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

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

Mergebot: fixup the test suite

File size: 11.6 KB
Line 
1#!/usr/bin/python
2"""Automated tests for MergeBot
3"""
4
5import os
6import unittest
7import time
8import shutil
9
10from subprocess import call, Popen #, PIPE, STDOUT
11from twill.errors import TwillAssertionError
12
13
14from trac.tests.functional import FunctionalTestSuite, FunctionalTester, FunctionalTwillTestCaseSetup, tc, b, logfile
15from trac.tests.functional.svntestenv import SvnFunctionalTestEnvironment
16from trac.tests.contentgen import random_page #, random_sentence, random_word
17
18
19#class MergeBotTestEnvironment(FunctionalTestEnvironment):
20#    """Slight change to FunctionalTestEnvironment to keep the PYTHONPATH from
21#    our environment.
22#    """
23#    def start(self):
24#        """Starts the webserver"""
25#        server = Popen(["python", "./trac/web/standalone.py",
26#                        "--port=%s" % self.port, "-s",
27#                        "--basic-auth=trac,%s," % self.htpasswd,
28#                        self.tracdir],
29#                       #env={'PYTHONPATH':'.'},
30#                       stdout=logfile, stderr=logfile,
31#                      )
32#        self.pid = server.pid
33#        time.sleep(1) # Give the server time to come up
34#
35#    def _tracadmin(self, *args):
36#        """Internal utility method for calling trac-admin"""
37#        if call(["python", "./trac/admin/console.py", self.tracdir] +
38#                list(args),
39#                #env={'PYTHONPATH':'.'},
40#                stdout=logfile, stderr=logfile):
41#            raise Exception('Failed running trac-admin with %r' % (args, ))
42#
43#
44#FunctionalTestEnvironment = MergeBotTestEnvironment
45
46
47class MergeBotFunctionalTester(FunctionalTester):
48    """Adds some MergeBot functionality to the functional tester."""
49    # FIXME: the tc.find( <various actions> ) checks are bogus: any ticket can
50    # satisfy them, not just the one we're working on.
51    def __init__(self, *args, **kwargs):
52        FunctionalTester.__init__(self, *args, **kwargs)
53        self.mergeboturl = self.url + '/mergebot'
54
55    def wait_until_find(self, search, timeout=5):
56        start = time.time()
57        while time.time() - start < timeout:
58            try:
59                tc.reload()
60                tc.find(search)
61                return
62            except TwillAssertionError:
63                pass
64        raise TwillAssertionError("Unable to find %r within %s seconds" % (search, timeout))
65
66    def wait_until_notfind(self, search, timeout=5):
67        start = time.time()
68        while time.time() - start < timeout:
69            try:
70                tc.reload()
71                tc.notfind(search)
72                return
73            except TwillAssertionError:
74                pass
75        raise TwillAssertionError("Unable to notfind %r within %s seconds" % (search, timeout))
76
77    def go_to_mergebot(self):
78        tc.go(self.mergeboturl)
79        tc.url(self.mergeboturl)
80        tc.notfind('No handler matched request to /mergebot')
81
82    def branch(self, ticket_id, component, timeout=1):
83        """timeout is in seconds."""
84        self.go_to_mergebot()
85        tc.formvalue('ops-%s' % ticket_id, 'ticket', ticket_id) # Essentially a noop to select the right form
86        tc.submit('Branch')
87        self.wait_until_notfind('Doing branch', timeout)
88        tc.find('Rebranch')
89        tc.find('Merge')
90        tc.find('CheckMerge')
91        self.go_to_ticket(ticket_id)
92        tc.find('Created branch from .* for .*')
93        retval = call(['svn', 'ls', self.repo_url + '/' + component + '/branches/ticket-%s' % ticket_id],
94                    stdout=logfile, stderr=logfile)
95        if retval:
96            raise Exception('svn ls failed with exit code %s' % retval)
97
98    def rebranch(self, ticket_id, component, timeout=5):
99        """timeout is in seconds."""
100        self.go_to_mergebot()
101        tc.formvalue('ops-%s' % ticket_id, 'ticket', ticket_id) # Essentially a noop to select the right form
102        tc.submit('Rebranch')
103        self.wait_until_notfind('Doing rebranch', timeout)
104        tc.find('Rebranch')
105        tc.find('Merge')
106        tc.find('CheckMerge')
107        self.go_to_ticket(ticket_id)
108        tc.find('Rebranched 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 merge(self, ticket_id, component, timeout=5):
115        """timeout is in seconds."""
116        self.go_to_mergebot()
117        tc.formvalue('ops-%s' % ticket_id, 'ticket', ticket_id) # Essentially a noop to select the right form
118        tc.submit('Merge')
119        self.wait_until_notfind('Doing merge', timeout)
120        tc.find('Branch')
121        self.go_to_ticket(ticket_id)
122        tc.find('Merged .* to .* for')
123        # TODO: We may want to change this to remove the "dead" branch
124        retval = call(['svn', 'ls', self.repo_url + '/' + component + '/branches/ticket-%s' % ticket_id],
125                    stdout=logfile, stderr=logfile)
126        if retval:
127            raise Exception('svn ls failed with exit code %s' % retval)
128
129    def checkmerge(self, ticket_id, component, timeout=5):
130        """timeout is in seconds."""
131        self.go_to_mergebot()
132        tc.formvalue('ops-%s' % ticket_id, 'ticket', ticket_id) # Essentially a noop to select the right form
133        tc.submit('CheckMerge')
134        self.wait_until_notfind('Doing checkmerge', timeout)
135        tc.find('Rebranch')
136        tc.find('Merge')
137        tc.find('CheckMerge')
138        self.go_to_ticket(ticket_id)
139        tc.find('while checking merge of')
140        # TODO: We may want to change this to remove the "dead" branch
141        retval = call(['svn', 'ls', self.repo_url + '/' + component + '/branches/ticket-%s' % ticket_id],
142                    stdout=logfile, stderr=logfile)
143        if retval:
144            raise Exception('svn ls failed with exit code %s' % retval)
145
146
147class MergeBotTestSuite(FunctionalTestSuite):
148    def setUp(self):
149        port = 8889
150        baseurl = "http://localhost:%s" % port
151        self._testenv = SvnFunctionalTestEnvironment("testenv%s" % port, port, baseurl)
152
153        # Configure mergebot
154        env = self._testenv.get_trac_environment()
155        env.config.set('components', 'mergebot.web_ui.mergebotmodule', 'enabled')
156        env.config.save()
157        self._testenv._tracadmin('upgrade') # sets up the bulk of the mergebot config
158        env.config.parse_if_needed()
159        env.config.set('mergebot', 'repository_url', self._testenv.repo_url())
160        env.config.set('logging', 'log_type', 'file')
161        env.config.save()
162        env.config.parse_if_needed()
163
164        self._testenv.start()
165        self._tester = MergeBotFunctionalTester(baseurl)
166        self.fixture = (self._testenv, self._tester)
167
168        # Setup some common component stuff for MergeBot's use:
169        svnurl = self._testenv.repo_url()
170        for component in ['stuff', 'flagship', 'submarine']:
171            self._tester.create_component(component)
172            if call(['svn', '-m', 'Create tree for "%s".' % component, 'mkdir',
173                     svnurl + '/' + component,
174                     svnurl + '/' + component + '/trunk',
175                     svnurl + '/' + component + '/tags',
176                     svnurl + '/' + component + '/branches'],
177                    stdout=logfile, stderr=logfile):
178                raise Exception("svn mkdir failed")
179
180        self._tester.create_version('trunk')
181
182
183class MergeBotTestEnabled(FunctionalTwillTestCaseSetup):
184    def runTest(self):
185        self._tester.logout()
186        tc.go(self._tester.url)
187        self._tester.login('admin')
188        tc.follow('MergeBot')
189        mergeboturl = self._tester.url + '/mergebot'
190        tc.url(mergeboturl)
191        tc.notfind('No handler matched request to /mergebot')
192
193
194class MergeBotTestQueueList(FunctionalTwillTestCaseSetup):
195    def runTest(self):
196        tc.follow('MergeBot')
197        for queue in 'branch', 'rebranch', 'checkmerge', 'merge':
198            tc.find('%s Queue' % queue)
199
200
201class MergeBotTestNoVersion(FunctionalTwillTestCaseSetup):
202    """Verify that if a ticket does not have the version field set, it will not
203    appear in the MergeBot list.
204    """
205    def runTest(self):
206        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
207            info={'component':'stuff', 'version':''})
208        tc.follow('MergeBot')
209        tc.notfind(self.__class__.__name__)
210
211
212class MergeBotTestBranch(FunctionalTwillTestCaseSetup):
213    def runTest(self):
214        """Verify that the 'branch' button works"""
215        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
216            info={'component':'stuff', 'version':'trunk'})
217        self._tester.branch(ticket_id, 'stuff')
218
219
220class MergeBotTestRebranch(FunctionalTwillTestCaseSetup):
221    def runTest(self):
222        """Verify that the 'rebranch' button works"""
223        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
224            info={'component':'stuff', 'version':'trunk'})
225        self._tester.branch(ticket_id, 'stuff')
226        self._tester.rebranch(ticket_id, 'stuff')
227
228
229class MergeBotTestMerge(FunctionalTwillTestCaseSetup):
230    def runTest(self):
231        """Verify that the 'merge' button works"""
232        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
233            info={'component':'stuff', 'version':'trunk'})
234        self._tester.branch(ticket_id, 'stuff')
235        self._tester.merge(ticket_id, 'stuff')
236
237
238class MergeBotTestCheckMerge(FunctionalTwillTestCaseSetup):
239    def runTest(self):
240        """Verify that the 'checkmerge' button works"""
241        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
242            info={'component':'stuff', 'version':'trunk'})
243        self._tester.branch(ticket_id, 'stuff')
244        self._tester.checkmerge(ticket_id, 'stuff')
245
246
247class MergeBotTestSingleUseCase(FunctionalTwillTestCaseSetup):
248    def runTest(self):
249        """Create a branch, make a change, checkmerge, and merge it."""
250        ticket_id = self._tester.create_ticket(summary=self.__class__.__name__,
251            info={'component':'stuff', 'version':'trunk'})
252        self._tester.branch(ticket_id, 'stuff')
253        # checkout a working copy & make a change
254        svnurl = self._testenv.repo_url()
255        workdir = os.path.join(self._testenv.dirname, self.__class__.__name__)
256        retval = call(['svn', 'checkout', svnurl + '/stuff/branches/ticket-%s' % ticket_id, workdir],
257            stdout=logfile, stderr=logfile)
258        self.assertEqual(retval, 0, "svn checkout failed with error %s" % (retval))
259        # Create & add a new file
260        newfile = os.path.join(workdir, self.__class__.__name__)
261        open(newfile, 'w').write(random_page())
262        retval = call(['svn', 'add', self.__class__.__name__],
263            cwd=workdir,
264            stdout=logfile, stderr=logfile)
265        self.assertEqual(retval, 0, "svn add failed with error %s" % (retval))
266        retval = call(['svn', 'commit', '-m', 'Add a new file', self.__class__.__name__],
267            cwd=workdir,
268            stdout=logfile, stderr=logfile)
269        self.assertEqual(retval, 0, "svn commit failed with error %s" % (retval))
270
271        self._tester.checkmerge(ticket_id, 'stuff')
272        self._tester.merge(ticket_id, 'stuff')
273
274        shutil.rmtree(workdir) # cleanup working copy
275
276
277def suite():
278    suite = MergeBotTestSuite()
279    suite.addTest(MergeBotTestEnabled())
280    suite.addTest(MergeBotTestQueueList())
281    suite.addTest(MergeBotTestNoVersion())
282    suite.addTest(MergeBotTestBranch())
283    suite.addTest(MergeBotTestRebranch())
284    suite.addTest(MergeBotTestMerge())
285    suite.addTest(MergeBotTestCheckMerge())
286    suite.addTest(MergeBotTestSingleUseCase())
287    return suite
288
289if __name__ == '__main__':
290    unittest.main(defaultTest='suite')
Note: See TracBrowser for help on using the repository browser.