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

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

Mergebot: minimal docs for test suite

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