source: mergebot/trunk/utils/test.py @ 67

Last change on this file since 67 was 67, checked in by retracile, 14 years ago

Mergebot: add test for merge task dependencies

File size: 34.0 KB
Line 
1#!/usr/bin/python
2"""Automated tests for MergeBot
3
4Run from a Trac source tree with mergebot installed system-wide.  (This needs
5to be reworked to be less cumbersome.)
6"""
7
8# TODO: The testcases need to be extended to cover the following:
9# - using a ticket #number as a version
10# - using a release version number as a version
11# - verify inter-ticket dependency checking
12# - verify failure cascades through inter-ticket dependencies
13# - change the version of a ticket, rebranch, and merge
14
15import os
16import unittest
17import time
18import shutil
19
20from subprocess import call, Popen #, PIPE, STDOUT
21from twill.errors import TwillAssertionError
22
23
24from trac.tests.functional import FunctionalTestSuite, FunctionalTester, FunctionalTwillTestCaseSetup, tc, b, logfile
25from trac.tests.functional.svntestenv import SvnFunctionalTestEnvironment
26from trac.tests.contentgen import random_page, random_sentence #, random_word
27
28
29#class MergeBotTestEnvironment(FunctionalTestEnvironment):
30#    """Slight change to FunctionalTestEnvironment to keep the PYTHONPATH from
31#    our environment.
32#    """
33#    def start(self):
34#        """Starts the webserver"""
35#        server = Popen(["python", "./trac/web/standalone.py",
36#                        "--port=%s" % self.port, "-s",
37#                        "--basic-auth=trac,%s," % self.htpasswd,
38#                        self.tracdir],
39#                       #env={'PYTHONPATH':'.'},
40#                       stdout=logfile, stderr=logfile,
41#                      )
42#        self.pid = server.pid
43#        time.sleep(1) # Give the server time to come up
44#
45#    def _tracadmin(self, *args):
46#        """Internal utility method for calling trac-admin"""
47#        if call(["python", "./trac/admin/console.py", self.tracdir] +
48#                list(args),
49#                #env={'PYTHONPATH':'.'},
50#                stdout=logfile, stderr=logfile):
51#            raise Exception('Failed running trac-admin with %r' % (args, ))
52#
53#
54#FunctionalTestEnvironment = MergeBotTestEnvironment
55
56
57class MergeBotFunctionalTester(FunctionalTester):
58    """Adds some MergeBot functionality to the functional tester."""
59    # FIXME: the tc.find( <various actions> ) checks are bogus: any ticket can
60    # satisfy them, not just the one we're working on.
61    def __init__(self, trac_url, repo_url):
62        FunctionalTester.__init__(self, trac_url)
63        self.repo_url = repo_url
64        self.mergeboturl = self.url + '/mergebot'
65
66    def wait_until_find(self, search, timeout=5):
67        start = time.time()
68        while time.time() - start < timeout:
69            try:
70                #tc.reload() # This appears to re-POST
71                tc.go(b.get_url())
72                tc.find(search)
73                return
74            except TwillAssertionError:
75                pass
76        raise TwillAssertionError("Unable to find %r within %s seconds" % (search, timeout))
77
78    def wait_until_notfind(self, search, timeout=5):
79        start = time.time()
80        while time.time() - start < timeout:
81            try:
82                #tc.reload() # This appears to re-POST
83                tc.go(b.get_url())
84                tc.notfind(search)
85                return
86            except TwillAssertionError:
87                pass
88        raise TwillAssertionError("Unable to notfind %r within %s seconds" % (search, timeout))
89
90    def go_to_mergebot(self):
91        tc.go(self.mergeboturl)
92        tc.url(self.mergeboturl)
93        tc.notfind('No handler matched request to /mergebot')
94
95    def queue_branch(self, ticket_id):
96        self.go_to_mergebot()
97        tc.formvalue('ops-%s' % ticket_id, 'ticket', ticket_id) # Essentially a noop to select the right form
98        tc.submit('Branch')
99
100    def branch(self, ticket_id, component, timeout=1):
101        """timeout is in seconds."""
102        self.queue_branch(ticket_id)
103        self.wait_until_find('Nothing in the queue', timeout)
104        tc.find('Rebranch')
105        tc.find('Merge')
106        tc.find('CheckMerge')
107        self.go_to_ticket(ticket_id)
108        tc.find('Created branch from .* for .*')
109        retval = call(['svn', 'ls', self.repo_url + '/' + component + '/branches/ticket-%s' % ticket_id],
110                    stdout=logfile, stderr=logfile)
111        if retval:
112            raise Exception('svn ls failed with exit code %s' % retval)
113
114    def queue_rebranch(self, ticket_id):
115        self.go_to_mergebot()
116        tc.formvalue('ops-%s' % ticket_id, 'ticket', ticket_id) # Essentially a noop to select the right form
117        tc.submit('Rebranch')
118
119    def _rebranch(self, ticket_id, component, search, timeout=15):
120        """timeout is in seconds."""
121        self.queue_rebranch(ticket_id)
122        self.wait_until_find('Nothing in the queue', timeout)
123        tc.find('Rebranch')
124        tc.find('Merge')
125        tc.find('CheckMerge')
126        self.go_to_ticket(ticket_id)
127        tc.find(search)
128        retval = call(['svn', 'ls', self.repo_url + '/' + component + '/branches/ticket-%s' % ticket_id],
129                    stdout=logfile, stderr=logfile)
130        if retval:
131            raise Exception('svn ls failed with exit code %s' % retval)
132
133    def rebranch(self, ticket_id, component, timeout=15):
134        self._rebranch(ticket_id, component, 'Rebranched from .* for .*', timeout)
135
136    def rebranch_conflict(self, ticket_id, component, timeout=15):
137        self._rebranch(ticket_id, component, 'There were conflicts on rebranching', timeout)
138
139    def queue_merge(self, ticket_id):
140        self.go_to_mergebot()
141        tc.formvalue('ops-%s' % ticket_id, 'ticket', ticket_id) # Essentially a noop to select the right form
142        tc.submit('Merge')
143
144    def merge(self, ticket_id, component, timeout=5):
145        self._merge(ticket_id, component, 'Merged .* to .* for', timeout)
146
147    def merge_conflict(self, ticket_id, component, timeout=5):
148        self._merge(ticket_id, component, 'Found [0-9]+ conflicts? in attempt to merge ', timeout)
149
150    def _merge(self, ticket_id, component, search, timeout=5):
151        """timeout is in seconds."""
152        self.queue_merge(ticket_id)
153        self.wait_until_find('Nothing in the queue', timeout)
154        tc.find('Branch')
155        self.go_to_ticket(ticket_id)
156        tc.find(search)
157        # TODO: We may want to change this to remove the "dead" branch
158        retval = call(['svn', 'ls', self.repo_url + '/' + component + '/branches/ticket-%s' % ticket_id],
159                    stdout=logfile, stderr=logfile)
160        if retval:
161            raise Exception('svn ls failed with exit code %s' % retval)
162
163    def queue_checkmerge(self, ticket_id):
164        self.go_to_mergebot()
165        tc.formvalue('ops-%s' % ticket_id, 'ticket', ticket_id) # Essentially a noop to select the right form
166        tc.submit('CheckMerge')
167
168    def checkmerge(self, ticket_id, component, timeout=5):
169        """timeout is in seconds."""
170        self.queue_checkmerge(ticket_id)
171        self.wait_until_find('Nothing in the queue', timeout)
172        tc.find('Rebranch')
173        tc.find('Merge')
174        tc.find('CheckMerge')
175        self.go_to_ticket(ticket_id)
176        tc.find('while checking merge of')
177        retval = call(['svn', 'ls', self.repo_url + '/' + component + '/branches/ticket-%s' % ticket_id],
178                    stdout=logfile, stderr=logfile)
179        if retval:
180            raise Exception('svn ls failed with exit code %s' % retval)
181
182    def wait_for_empty_queue(self, timeout=10):
183        self.go_to_mergebot()
184        self.wait_until_find('Nothing in the queue', timeout)
185
186
187class MergeBotTestSuite(FunctionalTestSuite):
188    def setUp(self):
189        port = 8889
190        baseurl = "http://localhost:%s" % port
191        self._testenv = SvnFunctionalTestEnvironment("testenv%s" % port, port, baseurl)
192
193        # Configure mergebot
194        env = self._testenv.get_trac_environment()
195        env.config.set('components', 'mergebot.web_ui.mergebotmodule', 'enabled')
196        env.config.save()
197        os.mkdir(os.path.join("testenv%s" % port, 'trac', 'mergebot'))
198        self._testenv._tracadmin('upgrade') # sets up the bulk of the mergebot config
199        env.config.parse_if_needed()
200        env.config.set('mergebot', 'repository_url', self._testenv.repo_url())
201        env.config.set('logging', 'log_type', 'file')
202        env.config.save()
203        env.config.parse_if_needed()
204
205        self._testenv.start()
206        self._tester = MergeBotFunctionalTester(baseurl, self._testenv.repo_url())
207        os.system('mergebotdaemon -f "%s" > %s/mergebotdaemon.log 2>&1 &' % (self._testenv.tracdir, self._testenv.tracdir))
208        self.fixture = (self._testenv, self._tester)
209
210        # Setup some common component stuff for MergeBot's use:
211        svnurl = self._testenv.repo_url()
212        for component in ['stuff', 'flagship', 'submarine']:
213            self._tester.create_component(component)
214            if call(['svn', '-m', 'Create tree for "%s".' % component, 'mkdir',
215                     svnurl + '/' + component,
216                     svnurl + '/' + component + '/trunk',
217                     svnurl + '/' + component + '/tags',
218                     svnurl + '/' + component + '/branches'],
219                    stdout=logfile, stderr=logfile):
220                raise Exception("svn mkdir failed")
221
222        self._tester.create_version('trunk')
223
224
225class FunctionalSvnTestCaseSetup(FunctionalTwillTestCaseSetup):
226    def get_workdir(self):
227        return os.path.join(self._testenv.dirname, self.__class__.__name__)
228
229    def checkout(self, ticket_id=None):
230        """checkout a working copy of the branch for the given ticket, or trunk if none given"""
231        if ticket_id is None:
232            svnurl = self._testenv.repo_url() + '/stuff/trunk'
233        else:
234            svnurl = self._testenv.repo_url() + '/stuff/branches/ticket-%s' % ticket_id
235        retval = call(['svn', 'checkout', svnurl, self.get_workdir()],
236            stdout=logfile, stderr=logfile)
237        self.assertEqual(retval, 0, "svn checkout failed with error %s" % (retval))
238
239    def cleanup(self):
240        shutil.rmtree(self.get_workdir())
241
242    def switch(self, ticket_id=None):
243        if ticket_id is None:
244            svnurl = self._testenv.repo_url() + '/stuff/trunk'
245        else:
246            svnurl = self._testenv.repo_url() + '/stuff/branches/ticket-%s' % ticket_id
247        retval = call(['svn', 'switch', svnurl, self.get_workdir()],
248            stdout=logfile, stderr=logfile)
249        self.assertEqual(retval, 0, "svn checkout failed with error %s" % (retval))
250
251    def add_new_file(self, filename=None, file_size=None):
252        workdir = self.get_workdir()
253        if filename is None:
254            newfile = os.path.join(workdir, self.__class__.__name__)
255        else:
256            newfile = os.path.join(workdir, filename)
257        if file_size is None:
258            data = random_page()
259        else:
260            data = ''
261            while len(data) < file_size:
262                data += random_page()
263            data = data[:file_size]
264        open(newfile, 'w').write(data)
265        retval = call(['svn', 'add', newfile],
266            cwd=workdir,
267            stdout=logfile, stderr=logfile)
268        self.assertEqual(retval, 0, "svn add failed with error %s" % (retval))
269
270    def commit(self, message, files=None):
271        if files is None:
272            files = ['.']
273        commit_message = self.__class__.__name__ + ": " + message
274        retval = call(['svn', 'commit', '-m', commit_message] + list(files),
275            cwd=self.get_workdir(),
276            stdout=logfile, stderr=logfile)
277        self.assertEqual(retval, 0, "svn commit failed with error %s" % (retval))
278
279    def mv(self, oldname, newname):
280        retval = call(['svn', 'mv', oldname, newname],
281            cwd=self.get_workdir(),
282            stdout=logfile, stderr=logfile)
283        self.assertEqual(retval, 0, "svn mv failed with error %s" % (retval))
284
285    def propset(self, propname, propvalue, filename):
286        retval = call(['svn', 'propset', propname, propvalue, filename],
287            cwd=self.get_workdir(),
288            stdout=logfile, stderr=logfile)
289        self.assertEqual(retval, 0, "svn prposet failed with error %s" % (retval))
290       
291    def propdel(self, propname, filename):
292        retval = call(['svn', 'propdel', propname, filename],
293            cwd=self.get_workdir(),
294            stdout=logfile, stderr=logfile)
295        self.assertEqual(retval, 0, "svn prposet failed with error %s" % (retval))
296
297
298class MergeBotTestEnabled(FunctionalTwillTestCaseSetup):
299    def runTest(self):
300        self._tester.logout()
301        tc.go(self._tester.url)
302        self._tester.login('admin')
303        tc.follow('MergeBot')
304        mergeboturl = self._tester.url + '/mergebot'
305        tc.url(mergeboturl)
306        tc.notfind('No handler matched request to /mergebot')
307
308
309class MergeBotTestNoVersion(FunctionalTwillTestCaseSetup):
310    """Verify that if a ticket does not have the version field set, it will not
311    appear in the MergeBot list.
312    """
313    def runTest(self):
314        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
315            info={'component':'stuff', 'version':''})
316        tc.follow('MergeBot')
317        tc.notfind(self.__class__.__name__)
318
319
320class MergeBotTestBranch(FunctionalTwillTestCaseSetup):
321    def runTest(self):
322        """Verify that the 'branch' button works"""
323        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
324            info={'component':'stuff', 'version':'trunk'})
325        self._tester.branch(ticket_id, 'stuff')
326
327
328class MergeBotTestRebranch(FunctionalTwillTestCaseSetup):
329    def runTest(self):
330        """Verify that the 'rebranch' button works"""
331        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
332            info={'component':'stuff', 'version':'trunk'})
333        self._tester.branch(ticket_id, 'stuff')
334        self._tester.rebranch(ticket_id, 'stuff')
335
336
337class MergeBotTestMerge(FunctionalTwillTestCaseSetup):
338    def runTest(self):
339        """Verify that the 'merge' button works"""
340        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
341            info={'component':'stuff', 'version':'trunk'})
342        self._tester.branch(ticket_id, 'stuff')
343        self._tester.merge(ticket_id, 'stuff')
344
345
346class MergeBotTestMergeWithChange(FunctionalSvnTestCaseSetup):
347    def runTest(self):
348        """Verify that the 'merge' button works with changes on the branch"""
349        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
350            info={'component':'stuff', 'version':'trunk'})
351        self._tester.branch(ticket_id, 'stuff')
352
353        # checkout a working copy & make a change
354        self.checkout(ticket_id)
355        # Create & add a new file
356        self.add_new_file()
357        self.commit('Add a new file')
358
359        self._tester.merge(ticket_id, 'stuff')
360
361        self.cleanup()
362
363
364class MergeBotTestMergeWithChangeAndTrunkChange(FunctionalSvnTestCaseSetup):
365    def runTest(self):
366        """Verify that the 'merge' button works with changes on the branch and trunk"""
367        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
368            info={'component':'stuff', 'version':'trunk'})
369        self._tester.branch(ticket_id, 'stuff')
370
371        # checkout a working copy & make a change
372        self.checkout(ticket_id)
373        # Create & add a new file
374        basename = self.__class__.__name__
375        self.add_new_file(basename + '-ticket')
376        self.commit('Add a new file on ticket')
377        self.switch()
378        self.add_new_file(basename + '-trunk')
379        self.commit('Add a new file on trunk')
380
381        self._tester.merge(ticket_id, 'stuff')
382        self.cleanup()
383
384
385class MergeBotTestMergeWithConflict(FunctionalSvnTestCaseSetup):
386    def runTest(self):
387        """Verify that the 'merge' button detects conflicts between the branch and trunk"""
388        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
389            info={'component':'stuff', 'version':'trunk'})
390        basename = self.__class__.__name__
391
392        # create a file in which to have conflicts
393        self.checkout()
394        self.add_new_file(basename)
395        self.commit('Add a new file on trunk')
396
397        # create the branch
398        self._tester.branch(ticket_id, 'stuff')
399
400        # modify the file on trunk
401        open(os.path.join(self.get_workdir(), basename), 'a').write(random_sentence())
402        self.commit('Modify the file on trunk')
403
404        # modify the file on the branch
405        self.switch(ticket_id)
406        open(os.path.join(self.get_workdir(), basename), 'a').write(random_sentence())
407        self.commit('Modify the file on branch')
408
409        # merge, make sure it shows a conflict
410        self._tester.merge_conflict(ticket_id, 'stuff')
411
412        self.cleanup()
413
414
415class MergeBotTestMergeWithPropertyConflict(FunctionalSvnTestCaseSetup):
416    def runTest(self):
417        """Verify that the 'merge' button detects property conflicts between the branch and trunk"""
418        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
419            info={'component':'stuff', 'version':'trunk'})
420        basename = self.__class__.__name__
421
422        self.checkout()
423        self.propset('svn:ignore', basename, '.')
424        self.commit('set property on trunk')
425
426        # create the branch
427        self._tester.branch(ticket_id, 'stuff')
428
429        # modify the property on trunk
430        self.propset('svn:ignore', basename + '\ntrunk', '.')
431        self.commit('Modify the property on trunk')
432
433        # modify the property on the branch
434        self.switch(ticket_id)
435        self.propset('svn:ignore', basename + '\nbranch', '.')
436        self.commit('Modify the property on branch')
437
438        # merge, make sure it shows a conflict
439        self._tester.merge_conflict(ticket_id, 'stuff')
440
441        self.cleanup()
442
443
444class MergeBotTestMergeWithPropertyBranchDeleteConflict(FunctionalSvnTestCaseSetup):
445    def runTest(self):
446        """Verify that the 'merge' button detects property deleted on branch and modified on trunk"""
447        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
448            info={'component':'stuff', 'version':'trunk'})
449        basename = self.__class__.__name__
450
451        self.checkout()
452        self.propset('svn:ignore', basename, '.')
453        self.commit('set property on trunk')
454
455        # create the branch
456        self._tester.branch(ticket_id, 'stuff')
457
458        # modify the property on trunk
459        self.propset('svn:ignore', basename + '\ntrunk', '.')
460        self.commit('Modify the property on trunk')
461
462        # delete the property on the branch
463        self.switch(ticket_id)
464        self.propdel('svn:ignore', '.')
465        self.commit('Delete the property on branch')
466
467        # merge, make sure it shows a conflict
468        self._tester.merge_conflict(ticket_id, 'stuff')
469
470        self.cleanup()
471
472
473class MergeBotTestMergeWithPropertyTrunkDeleteConflict(FunctionalSvnTestCaseSetup):
474    def runTest(self):
475        """Verify that the 'merge' button detects property deleted on trunk and modified on branch"""
476        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
477            info={'component':'stuff', 'version':'trunk'})
478        basename = self.__class__.__name__
479
480        self.checkout()
481        self.propset('svn:ignore', basename, '.')
482        self.commit('set property on trunk')
483
484        # create the branch
485        self._tester.branch(ticket_id, 'stuff')
486
487        # delete the property on trunk
488        self.propdel('svn:ignore', '.')
489        self.commit('Delete the property on trunk')
490
491        # delete the property on the branch
492        self.switch(ticket_id)
493        self.propset('svn:ignore', basename + '\nbranch', '.')
494        self.commit('Modify the property on branch')
495
496        # merge, make sure it shows a conflict
497        self._tester.merge_conflict(ticket_id, 'stuff')
498
499        self.cleanup()
500
501
502class MergeBotTestMergeWithBranchRenameConflict(FunctionalSvnTestCaseSetup):
503    def runTest(self):
504        """Verify that the 'merge' button detects a conflict when a file renamed on the branch was modified on trunk"""
505        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
506            info={'component':'stuff', 'version':'trunk'})
507        basename = self.__class__.__name__
508
509        # create a file in which to have conflicts
510        self.checkout()
511        self.add_new_file(basename)
512        self.commit('Add a new file on trunk')
513
514        # create the branch
515        self._tester.branch(ticket_id, 'stuff')
516
517        # modify the file on trunk
518        open(os.path.join(self.get_workdir(), basename), 'a').write(random_sentence())
519        self.commit('Modify the file on trunk')
520
521        # rename the file on the branch
522        self.switch(ticket_id)
523        self.mv(basename, basename + '-renamed')
524        self.commit('Rename the file on the branch')
525
526        self._tester.merge_conflict(ticket_id, 'stuff')
527
528        self.cleanup()
529
530
531class MergeBotTestMergeWithTrunkRenameConflict(FunctionalSvnTestCaseSetup):
532    def runTest(self):
533        """Verify that the 'merge' button detects conflicts when a file renamed on trunk was modified on the branch"""
534        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
535            info={'component':'stuff', 'version':'trunk'})
536        basename = self.__class__.__name__
537
538        # create a file in which to have conflicts
539        self.checkout()
540        self.add_new_file(basename)
541        self.commit('Add a new file on trunk')
542
543        # create the branch
544        self._tester.branch(ticket_id, 'stuff')
545
546        # rename the file on trunk
547        self.mv(basename, basename + '-renamed')
548        self.commit('Rename the file on trunk')
549
550        # rename the file on the branch
551        self.switch(ticket_id)
552        open(os.path.join(self.get_workdir(), basename), 'a').write(random_sentence())
553        self.commit('Modify the file on the branch')
554
555        # make sure it finds the conflict
556        self._tester.merge_conflict(ticket_id, 'stuff')
557
558        self.cleanup()
559
560
561class MergeBotTestCheckMerge(FunctionalTwillTestCaseSetup):
562    def runTest(self):
563        """Verify that the 'checkmerge' button works"""
564        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
565            info={'component':'stuff', 'version':'trunk'})
566        self._tester.branch(ticket_id, 'stuff')
567        self._tester.checkmerge(ticket_id, 'stuff')
568
569
570class MergeBotTestRebranchWithChange(FunctionalSvnTestCaseSetup):
571    def runTest(self):
572        """Verify that the 'rebranch' button works with changes on the branch"""
573        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
574            info={'component':'stuff', 'version':'trunk'})
575        self._tester.branch(ticket_id, 'stuff')
576
577        # checkout a working copy & make a change
578        self.checkout(ticket_id)
579        # Create & add a new file
580        self.add_new_file()
581        self.commit('Add a new file')
582
583        self._tester.rebranch(ticket_id, 'stuff')
584        self.cleanup()
585
586
587class MergeBotTestRebranchWithChangeAndTrunkChange(FunctionalSvnTestCaseSetup):
588    def runTest(self):
589        """Verify that the 'rebranch' button works with changes on the branch and trunk"""
590        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
591            info={'component':'stuff', 'version':'trunk'})
592        self._tester.branch(ticket_id, 'stuff')
593
594        # checkout a working copy & make a change
595        self.checkout(ticket_id)
596        # Create & add a new file
597        basename = self.__class__.__name__
598        self.add_new_file(basename + '-ticket')
599        self.commit('Add a new file on ticket')
600        self.switch()
601        self.add_new_file(basename + '-trunk')
602        self.commit('Add a new file on trunk')
603
604        self._tester.rebranch(ticket_id, 'stuff')
605        self.cleanup()
606
607
608class MergeBotTestRebranchWithConflict(FunctionalSvnTestCaseSetup):
609    def runTest(self):
610        """Verify that the 'rebranch' button detects conflicts between the branch and trunk"""
611        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
612            info={'component':'stuff', 'version':'trunk'})
613        basename = self.__class__.__name__
614
615        # create a file in which to have conflicts
616        self.checkout()
617        self.add_new_file(basename)
618        self.commit('Add a new file on trunk')
619
620        # create the branch
621        self._tester.branch(ticket_id, 'stuff')
622
623        # modify the file on trunk
624        open(os.path.join(self.get_workdir(), basename), 'a').write(random_sentence())
625        self.commit('Modify the file on trunk')
626
627        # modify the file on the branch
628        self.switch(ticket_id)
629        open(os.path.join(self.get_workdir(), basename), 'a').write(random_sentence())
630        self.commit('Modify the file on branch')
631
632        # rebranch, make sure it shows a conflict
633        self._tester.rebranch_conflict(ticket_id, 'stuff')
634
635        self.cleanup()
636
637
638class MergeBotTestRebranchWithPropertyConflict(FunctionalSvnTestCaseSetup):
639    def runTest(self):
640        """Verify that the 'rebranch' button detects property conflicts between the branch and trunk"""
641        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
642            info={'component':'stuff', 'version':'trunk'})
643        basename = self.__class__.__name__
644
645        self.checkout()
646        self.propset('svn:ignore', basename, '.')
647        self.commit('set property on trunk')
648
649        # create the branch
650        self._tester.branch(ticket_id, 'stuff')
651
652        # modify the property on trunk
653        self.propset('svn:ignore', basename + '\ntrunk', '.')
654        self.commit('Modify the property on trunk')
655
656        # modify the property on the branch
657        self.switch(ticket_id)
658        self.propset('svn:ignore', basename + '\nbranch', '.')
659        self.commit('Modify the property on branch')
660
661        # rebranch, make sure it shows a conflict
662        self._tester.rebranch_conflict(ticket_id, 'stuff')
663
664        self.cleanup()
665
666
667class MergeBotTestRebranchWithPropertyBranchDeleteConflict(FunctionalSvnTestCaseSetup):
668    def runTest(self):
669        """Verify that the 'rebranch' button detects property deleted on branch and modified on trunk"""
670        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
671            info={'component':'stuff', 'version':'trunk'})
672        basename = self.__class__.__name__
673
674        self.checkout()
675        self.propset('svn:ignore', basename, '.')
676        self.commit('set property on trunk')
677
678        # create the branch
679        self._tester.branch(ticket_id, 'stuff')
680
681        # modify the property on trunk
682        self.propset('svn:ignore', basename + '\ntrunk', '.')
683        self.commit('Modify the property on trunk')
684
685        # delete the property on the branch
686        self.switch(ticket_id)
687        self.propdel('svn:ignore', '.')
688        self.commit('Delete the property on branch')
689
690        # rebranch, make sure it shows a conflict
691        self._tester.rebranch_conflict(ticket_id, 'stuff')
692
693        self.cleanup()
694
695
696class MergeBotTestRebranchWithPropertyTrunkDeleteConflict(FunctionalSvnTestCaseSetup):
697    def runTest(self):
698        """Verify that the 'rebranch' button detects property deleted on trunk and modified on branch"""
699        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
700            info={'component':'stuff', 'version':'trunk'})
701        basename = self.__class__.__name__
702
703        self.checkout()
704        self.propset('svn:ignore', basename, '.')
705        self.commit('set property on trunk')
706
707        # create the branch
708        self._tester.branch(ticket_id, 'stuff')
709
710        # delete the property on trunk
711        self.propdel('svn:ignore', '.')
712        self.commit('Delete the property on trunk')
713
714        # delete the property on the branch
715        self.switch(ticket_id)
716        self.propset('svn:ignore', basename + '\nbranch', '.')
717        self.commit('Modify the property on branch')
718
719        # rebranch, make sure it shows a conflict
720        self._tester.rebranch_conflict(ticket_id, 'stuff')
721
722        self.cleanup()
723
724
725class MergeBotTestRebranchWithBranchRenameConflict(FunctionalSvnTestCaseSetup):
726    def runTest(self):
727        """Verify that the 'rebranch' button detects a conflict when a file renamed on the branch was modified on trunk"""
728        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
729            info={'component':'stuff', 'version':'trunk'})
730        basename = self.__class__.__name__
731
732        # create a file in which to have conflicts
733        self.checkout()
734        self.add_new_file(basename)
735        self.commit('Add a new file on trunk')
736
737        # create the branch
738        self._tester.branch(ticket_id, 'stuff')
739
740        # modify the file on trunk
741        open(os.path.join(self.get_workdir(), basename), 'a').write(random_sentence())
742        self.commit('Modify the file on trunk')
743
744        # rename the file on the branch
745        self.switch(ticket_id)
746        self.mv(basename, basename + '-renamed')
747        self.commit('Rename the file on the branch')
748
749        self._tester.rebranch_conflict(ticket_id, 'stuff')
750
751        self.cleanup()
752
753
754class MergeBotTestRebranchWithTrunkRenameConflict(FunctionalSvnTestCaseSetup):
755    def runTest(self):
756        """Verify that the 'rebranch' button detects conflicts when a file renamed on trunk was modified on the branch"""
757        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
758            info={'component':'stuff', 'version':'trunk'})
759        basename = self.__class__.__name__
760
761        # create a file in which to have conflicts
762        self.checkout()
763        self.add_new_file(basename)
764        self.commit('Add a new file on trunk')
765
766        # create the branch
767        self._tester.branch(ticket_id, 'stuff')
768
769        # rename the file on trunk
770        self.mv(basename, basename + '-renamed')
771        self.commit('Rename the file on trunk')
772
773        # rename the file on the branch
774        self.switch(ticket_id)
775        open(os.path.join(self.get_workdir(), basename), 'a').write(random_sentence())
776        self.commit('Modify the file on the branch')
777
778        # make sure it finds the conflict
779        self._tester.rebranch_conflict(ticket_id, 'stuff')
780
781        self.cleanup()
782
783
784class MergeBotTestSingleUseCase(FunctionalSvnTestCaseSetup):
785    def runTest(self):
786        """Create a branch, make a change, checkmerge, and merge it."""
787        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
788            info={'component':'stuff', 'version':'trunk'})
789        self._tester.branch(ticket_id, 'stuff')
790        # checkout a working copy & make a change
791        self.checkout(ticket_id)
792        # Create & add a new file
793        self.add_new_file()
794        self.commit('Add a new file')
795        self._tester.checkmerge(ticket_id, 'stuff')
796        self._tester.merge(ticket_id, 'stuff')
797        self.cleanup()
798
799
800class MergeBotTestBranchReuse(FunctionalSvnTestCaseSetup):
801    def runTest(self):
802        """Merge a branch, branch it again, and merge it a second time."""
803        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
804            info={'component':'stuff', 'version':'trunk'})
805        basename = self.__class__.__name__
806
807        self.checkout()
808        self.add_new_file(basename)
809        self.commit('Add a new file')
810
811        self._tester.branch(ticket_id, 'stuff')
812        self.switch(ticket_id)
813        open(os.path.join(self.get_workdir(), basename), 'a').write(random_sentence())
814        self.commit('Make a modification')
815        self._tester.merge(ticket_id, 'stuff')
816
817        self._tester.branch(ticket_id, 'stuff')
818        self.switch(ticket_id)
819        open(os.path.join(self.get_workdir(), basename), 'a').write(random_sentence())
820        self.commit('Make a second modification')
821        self._tester.merge(ticket_id, 'stuff')
822        self.cleanup()
823
824
825class MergeBotTestMergeDependency(FunctionalSvnTestCaseSetup):
826    def runTest(self):
827        """Merge two branches to trunk; make sure the second one shows as waiting"""
828        # This test is fundamentally racy.  The size of the files has been
829        # chosen to slow down the test long enough to get a view of the
830        # mergebot page with one of them waiting for the other to complete.
831        ticket_one = self._tester.create_ticket(summary=self.__class__.__name__ + " one",
832            info={'component':'stuff', 'version':'trunk'})
833        ticket_two = self._tester.create_ticket(summary=self.__class__.__name__ + " two",
834            info={'component':'stuff', 'version':'trunk'})
835        basename = self.__class__.__name__
836
837        self._tester.branch(ticket_one, 'stuff')
838        self._tester.branch(ticket_two, 'stuff')
839
840        self.checkout(ticket_one)
841        self.add_new_file(basename + '-one', 30*1024**2)
842        self.commit('Add a new file')
843
844        self.switch(ticket_two)
845        self.add_new_file(basename + '-two', 30*1024**2)
846        self.commit('Add a new file')
847
848        self._tester.queue_merge(ticket_two)
849        self._tester.queue_merge(ticket_one)
850
851        #self._tester.go_to_mergebot()
852        tc.find('Waiting')
853        self._tester.wait_for_empty_queue()
854       
855        self.cleanup()
856
857
858def suite():
859    suite = MergeBotTestSuite()
860    suite.addTest(MergeBotTestEnabled())
861    suite.addTest(MergeBotTestNoVersion())
862    suite.addTest(MergeBotTestBranch())
863    suite.addTest(MergeBotTestRebranch())
864    suite.addTest(MergeBotTestCheckMerge())
865    suite.addTest(MergeBotTestMerge())
866    suite.addTest(MergeBotTestRebranchWithChange())
867    suite.addTest(MergeBotTestRebranchWithChangeAndTrunkChange())
868    suite.addTest(MergeBotTestRebranchWithConflict())
869    suite.addTest(MergeBotTestRebranchWithPropertyConflict())
870    suite.addTest(MergeBotTestRebranchWithBranchRenameConflict())
871    suite.addTest(MergeBotTestRebranchWithTrunkRenameConflict())
872    suite.addTest(MergeBotTestRebranchWithPropertyBranchDeleteConflict())
873    suite.addTest(MergeBotTestRebranchWithPropertyTrunkDeleteConflict())
874    suite.addTest(MergeBotTestMergeWithChange())
875    suite.addTest(MergeBotTestMergeWithChangeAndTrunkChange())
876    suite.addTest(MergeBotTestMergeWithConflict())
877    suite.addTest(MergeBotTestMergeWithPropertyConflict())
878    suite.addTest(MergeBotTestMergeWithBranchRenameConflict())
879    suite.addTest(MergeBotTestMergeWithTrunkRenameConflict())
880    suite.addTest(MergeBotTestMergeWithPropertyBranchDeleteConflict())
881    suite.addTest(MergeBotTestMergeWithPropertyTrunkDeleteConflict())
882    suite.addTest(MergeBotTestSingleUseCase())
883    suite.addTest(MergeBotTestBranchReuse())
884    suite.addTest(MergeBotTestMergeDependency())
885    return suite
886
887if __name__ == '__main__':
888    unittest.main(defaultTest='suite')
Note: See TracBrowser for help on using the repository browser.