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

Last change on this file since 64 was 64, checked in by retracile, 15 years ago

Mergebot: add another testcase, do some refactoring, and add some cleanup of working copies

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