Merge lp:~jml/launchpad/use-testtools into lp:launchpad
- use-testtools
- Merge into devel
Proposed by
Jonathan Lange
Status: | Merged |
---|---|
Approved by: | Jonathan Lange |
Approved revision: | not available |
Merged at revision: | not available |
Proposed branch: | lp:~jml/launchpad/use-testtools |
Merge into: | lp:launchpad |
Diff against target: |
596 lines (+59/-325) 10 files modified
lib/canonical/launchpad/testing/tests/test_testcase.py (+0/-163) lib/lp/codehosting/puller/tests/test_scheduler.py (+5/-0) lib/lp/registry/windmill/tests/test_add_milestone.py.disabled (+1/-0) lib/lp/registry/windmill/tests/test_datetime_picker.py (+1/-0) lib/lp/registry/windmill/tests/test_plusnew_step1.py (+1/-0) lib/lp/registry/windmill/tests/test_plusnew_step2.py (+1/-0) lib/lp/registry/windmill/tests/test_team_index.py (+1/-0) lib/lp/registry/windmill/tests/test_timeline_graph.py (+2/-0) lib/lp/testing/__init__.py (+46/-161) utilities/sourcedeps.conf (+1/-1) |
To merge this branch: | bzr merge lp:~jml/launchpad/use-testtools |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gary Poster (community) | Approve | ||
Aaron Bentley (community) | Approve | ||
Review via email: mp+16711@code.launchpad.net |
Commit message
Use testtools, really. Make it an egg, and delete all of the Launchpad code that duplicates testtools.
Description of the change
To post a comment you must log in.
Revision history for this message
Jonathan Lange (jml) wrote : | # |
Revision history for this message
Jonathan Lange (jml) wrote : | # |
I got over my fear & committed the tarball for testtools to download-cache, like this:
$ bzr st
added:
dist/
Testing now.
Revision history for this message
Aaron Bentley (abentley) wrote : | # |
This looks totally reasonable to me.
review:
Approve
Revision history for this message
Gary Poster (gary) wrote : | # |
You have some diff cruft problems visible in lines 193 through 201 of the diff as I see it right now. Other than that it looks good to me.
Gary
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === removed file 'lib/canonical/launchpad/testing/tests/test_testcase.py' |
2 | --- lib/canonical/launchpad/testing/tests/test_testcase.py 2009-06-25 05:30:52 +0000 |
3 | +++ lib/canonical/launchpad/testing/tests/test_testcase.py 1970-01-01 00:00:00 +0000 |
4 | @@ -1,163 +0,0 @@ |
5 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
6 | -# GNU Affero General Public License version 3 (see the file LICENSE). |
7 | - |
8 | -"""Tests for the base TestCase classes.""" |
9 | - |
10 | -__metaclass__ = type |
11 | - |
12 | -import unittest |
13 | -from lp.testing import TestCase |
14 | - |
15 | - |
16 | -class LoggingResult(unittest.TestResult): |
17 | - """TestResult that logs its event to a list.""" |
18 | - |
19 | - def __init__(self, log): |
20 | - self._events = log |
21 | - super(LoggingResult, self).__init__() |
22 | - |
23 | - def startTest(self, test): |
24 | - self._events.append(('startTest', test)) |
25 | - super(LoggingResult, self).startTest(test) |
26 | - |
27 | - def stopTest(self, test): |
28 | - self._events.append(('stopTest', test)) |
29 | - super(LoggingResult, self).stopTest(test) |
30 | - |
31 | - def addFailure(self, test, error): |
32 | - self._events.append(('addFailure', test, error)) |
33 | - super(LoggingResult, self).addFailure(test, error) |
34 | - |
35 | - def addError(self, test, error): |
36 | - self._events.append(('addError', test, error)) |
37 | - super(LoggingResult, self).addError(test, error) |
38 | - |
39 | - def addSuccess(self, test): |
40 | - self._events.append(('addSuccess', test)) |
41 | - super(LoggingResult, self).addSuccess(test) |
42 | - |
43 | - |
44 | -class TestAddCleanup(unittest.TestCase): |
45 | - """Tests for TestCase.addCleanup.""" |
46 | - |
47 | - class LoggingTest(TestCase): |
48 | - """A test that logs calls to setUp, runTest and tearDown.""" |
49 | - |
50 | - def setUp(self): |
51 | - self._calls = ['setUp'] |
52 | - |
53 | - def brokenSetUp(self): |
54 | - # A tearDown that deliberately fails. |
55 | - self._calls = ['brokenSetUp'] |
56 | - raise RuntimeError('Deliberate Failure') |
57 | - |
58 | - def runTest(self): |
59 | - self._calls.append('runTest') |
60 | - |
61 | - def tearDown(self): |
62 | - self._calls.append('tearDown') |
63 | - |
64 | - def setUp(self): |
65 | - self._result_calls = [] |
66 | - self.test = TestAddCleanup.LoggingTest('runTest') |
67 | - self.logging_result = LoggingResult(self._result_calls) |
68 | - |
69 | - def assertErrorLogEqual(self, messages): |
70 | - self.assertEqual(messages, [call[0] for call in self._result_calls]) |
71 | - |
72 | - def assertTestLogEqual(self, messages): |
73 | - """Assert that the call log equals `messages`.""" |
74 | - self.assertEqual(messages, self.test._calls) |
75 | - |
76 | - def logAppender(self, message): |
77 | - """Return a cleanup that appends `message` to the tests log. |
78 | - |
79 | - Cleanups are callables that are added to a test by addCleanup. To |
80 | - verify that our cleanups run in the right order, we add strings to a |
81 | - list that acts as a log. This method returns a cleanup that will add |
82 | - the given message to that log when run. |
83 | - """ |
84 | - self.test._calls.append(message) |
85 | - |
86 | - def test_fixture(self): |
87 | - # A normal run of self.test logs 'setUp', 'runTest' and 'tearDown'. |
88 | - # This test doesn't test addCleanup itself, it just sanity checks the |
89 | - # fixture. |
90 | - self.test.run(self.logging_result) |
91 | - self.assertTestLogEqual(['setUp', 'runTest', 'tearDown']) |
92 | - |
93 | - def test_cleanup_run_before_tearDown(self): |
94 | - # Cleanup functions added with 'addCleanup' are called before tearDown |
95 | - # runs. |
96 | - self.test.addCleanup(self.logAppender, 'cleanup') |
97 | - self.test.run(self.logging_result) |
98 | - self.assertTestLogEqual(['setUp', 'runTest', 'cleanup', 'tearDown']) |
99 | - |
100 | - def test_add_cleanup_called_if_setUp_fails(self): |
101 | - # Cleanup functions added with 'addCleanup' are called even if setUp |
102 | - # fails. Note that tearDown has a different behavior: it is only |
103 | - # called when setUp succeeds. |
104 | - self.test.setUp = self.test.brokenSetUp |
105 | - self.test.addCleanup(self.logAppender, 'cleanup') |
106 | - self.test.run(self.logging_result) |
107 | - self.assertTestLogEqual(['brokenSetUp', 'cleanup']) |
108 | - |
109 | - def test_addCleanup_called_in_reverse_order(self): |
110 | - # Cleanup functions added with 'addCleanup' are called in reverse |
111 | - # order. |
112 | - # |
113 | - # One of the main uses of addCleanup is to dynamically create |
114 | - # resources that need some sort of explicit tearDown. Often one |
115 | - # resource will be created in terms of another, e.g., |
116 | - # self.first = self.makeFirst() |
117 | - # self.second = self.makeSecond(self.first) |
118 | - # |
119 | - # When this happens, we generally want to clean up the second resource |
120 | - # before the first one, since the second depends on the first. |
121 | - self.test.addCleanup(self.logAppender, 'first') |
122 | - self.test.addCleanup(self.logAppender, 'second') |
123 | - self.test.run(self.logging_result) |
124 | - self.assertTestLogEqual( |
125 | - ['setUp', 'runTest', 'second', 'first', 'tearDown']) |
126 | - |
127 | - def test_tearDown_runs_after_cleanup_failure(self): |
128 | - # tearDown runs even if a cleanup function fails. |
129 | - self.test.addCleanup(lambda: 1/0) |
130 | - self.test.run(self.logging_result) |
131 | - self.assertTestLogEqual(['setUp', 'runTest', 'tearDown']) |
132 | - |
133 | - def test_cleanups_continue_running_after_error(self): |
134 | - # All cleanups are always run, even if one or two of them fail. |
135 | - self.test.addCleanup(self.logAppender, 'first') |
136 | - self.test.addCleanup(lambda: 1/0) |
137 | - self.test.addCleanup(self.logAppender, 'second') |
138 | - self.test.run(self.logging_result) |
139 | - self.assertTestLogEqual( |
140 | - ['setUp', 'runTest', 'second', 'first', 'tearDown']) |
141 | - |
142 | - def test_error_in_cleanups_are_captured(self): |
143 | - # If a cleanup raises an error, we want to record it and fail the |
144 | - # test, even though we go on to run other cleanups. |
145 | - self.test.addCleanup(lambda: 1/0) |
146 | - self.test.run(self.logging_result) |
147 | - self.assertErrorLogEqual(['startTest', 'addError', 'stopTest']) |
148 | - |
149 | - def test_keyboard_interrupt_not_caught(self): |
150 | - # If a cleanup raises KeyboardInterrupt, it gets reraised. |
151 | - def raiseKeyboardInterrupt(): |
152 | - raise KeyboardInterrupt() |
153 | - self.test.addCleanup(raiseKeyboardInterrupt) |
154 | - self.assertRaises( |
155 | - KeyboardInterrupt, self.test.run, self.logging_result) |
156 | - |
157 | - def test_multipleErrorsReported(self): |
158 | - # Errors from all failing cleanups are reported. |
159 | - self.test.addCleanup(lambda: 1/0) |
160 | - self.test.addCleanup(lambda: 1/0) |
161 | - self.test.run(self.logging_result) |
162 | - self.assertErrorLogEqual( |
163 | - ['startTest', 'addError', 'addError', 'stopTest']) |
164 | - |
165 | - |
166 | -def test_suite(): |
167 | - return unittest.TestLoader().loadTestsFromName(__name__) |
168 | |
169 | === modified file 'lib/lp/codehosting/puller/tests/test_scheduler.py' |
170 | --- lib/lp/codehosting/puller/tests/test_scheduler.py 2009-12-22 23:35:26 +0000 |
171 | +++ lib/lp/codehosting/puller/tests/test_scheduler.py 2010-01-14 04:34:14 +0000 |
172 | @@ -640,6 +640,11 @@ |
173 | |
174 | layer = TwistedAppServerLayer |
175 | |
176 | + # XXX: Hacky workaround for Trial. It interprets the skip method as a |
177 | + # reason to skip the tests, but the default test result object doesn't |
178 | + # support skipping, hence errors. |
179 | + skip = None |
180 | + |
181 | def setUp(self): |
182 | TrialTestCase.setUp(self) |
183 | PullerBranchTestCase.setUp(self) |
184 | |
185 | === modified file 'lib/lp/registry/windmill/tests/test_add_milestone.py.disabled' |
186 | --- lib/lp/registry/windmill/tests/test_add_milestone.py.disabled 2010-01-12 22:43:49 +0000 |
187 | +++ lib/lp/registry/windmill/tests/test_add_milestone.py.disabled 2010-01-14 04:34:14 +0000 |
188 | @@ -87,6 +87,7 @@ |
189 | layer = RegistryWindmillLayer |
190 | |
191 | def setUp(self): |
192 | + super(TestAddMilestone, self).setUp() |
193 | self.client = WindmillTestClient('AddMilestone') |
194 | |
195 | def test_adding_milestone_on_addrelease_page(self): |
196 | |
197 | === modified file 'lib/lp/registry/windmill/tests/test_datetime_picker.py' |
198 | --- lib/lp/registry/windmill/tests/test_datetime_picker.py 2009-12-10 17:52:56 +0000 |
199 | +++ lib/lp/registry/windmill/tests/test_datetime_picker.py 2010-01-14 04:34:14 +0000 |
200 | @@ -22,6 +22,7 @@ |
201 | layer = RegistryWindmillLayer |
202 | |
203 | def setUp(self): |
204 | + super(TestDateTimeCalendarWidget, self).setUp() |
205 | self.client = WindmillTestClient('DateTimeCalendarWidget') |
206 | |
207 | def test_datetime_calendar_widget(self): |
208 | |
209 | === modified file 'lib/lp/registry/windmill/tests/test_plusnew_step1.py' |
210 | --- lib/lp/registry/windmill/tests/test_plusnew_step1.py 2009-12-11 19:54:04 +0000 |
211 | +++ lib/lp/registry/windmill/tests/test_plusnew_step1.py 2010-01-14 04:34:14 +0000 |
212 | @@ -24,6 +24,7 @@ |
213 | layer = RegistryWindmillLayer |
214 | |
215 | def setUp(self): |
216 | + super(TestNewProjectStep1, self).setUp() |
217 | self.client = WindmillTestClient('TestNewProjectStep1') |
218 | |
219 | def test_projects_plusnew_text_fields(self): |
220 | |
221 | === modified file 'lib/lp/registry/windmill/tests/test_plusnew_step2.py' |
222 | --- lib/lp/registry/windmill/tests/test_plusnew_step2.py 2009-12-11 19:54:04 +0000 |
223 | +++ lib/lp/registry/windmill/tests/test_plusnew_step2.py 2010-01-14 04:34:14 +0000 |
224 | @@ -22,6 +22,7 @@ |
225 | layer = RegistryWindmillLayer |
226 | |
227 | def setUp(self): |
228 | + super(TestNewProjectStep2, self).setUp() |
229 | self.client = WindmillTestClient('TestNewProjectStep2') |
230 | |
231 | def test_projects_plusnew_step_two(self): |
232 | |
233 | === modified file 'lib/lp/registry/windmill/tests/test_team_index.py' |
234 | --- lib/lp/registry/windmill/tests/test_team_index.py 2009-12-18 05:46:01 +0000 |
235 | +++ lib/lp/registry/windmill/tests/test_team_index.py 2010-01-14 04:34:14 +0000 |
236 | @@ -24,6 +24,7 @@ |
237 | layer = RegistryWindmillLayer |
238 | |
239 | def setUp(self): |
240 | + super(TestTeamIndex, self).setUp() |
241 | self.client = WindmillTestClient(__name__) |
242 | |
243 | def test_addmember(self): |
244 | |
245 | === modified file 'lib/lp/registry/windmill/tests/test_timeline_graph.py' |
246 | --- lib/lp/registry/windmill/tests/test_timeline_graph.py 2009-12-10 17:52:56 +0000 |
247 | +++ lib/lp/registry/windmill/tests/test_timeline_graph.py 2010-01-14 04:34:14 +0000 |
248 | @@ -13,12 +13,14 @@ |
249 | from lp.registry.windmill.testing import RegistryWindmillLayer |
250 | from lp.testing import TestCaseWithFactory |
251 | |
252 | + |
253 | class TestTimelineGraph(TestCaseWithFactory): |
254 | """Test timeline graph widget.""" |
255 | |
256 | layer = RegistryWindmillLayer |
257 | |
258 | def setUp(self): |
259 | + super(TestTimelineGraph, self).setUp() |
260 | self.client = WindmillTestClient('TimelineGraph') |
261 | |
262 | def test_timeline_graph(self): |
263 | |
264 | === modified file 'lib/lp/testing/__init__.py' |
265 | --- lib/lp/testing/__init__.py 2009-12-22 23:50:27 +0000 |
266 | +++ lib/lp/testing/__init__.py 2010-01-14 04:34:14 +0000 |
267 | @@ -1,24 +1,47 @@ |
268 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
269 | +# Copyright 2009, 2010 Canonical Ltd. This software is licensed under the |
270 | # GNU Affero General Public License version 3 (see the file LICENSE). |
271 | |
272 | # pylint: disable-msg=W0401,C0301 |
273 | |
274 | __metaclass__ = type |
275 | +__all__ = [ |
276 | + 'ANONYMOUS', |
277 | + 'capture_events', |
278 | + 'FakeTime', |
279 | + 'get_lsb_information', |
280 | + 'is_logged_in', |
281 | + 'login', |
282 | + 'login_person', |
283 | + 'logout', |
284 | + 'map_branch_contents', |
285 | + 'normalize_whitespace', |
286 | + 'record_statements', |
287 | + 'run_with_login', |
288 | + 'run_with_storm_debug', |
289 | + 'run_script', |
290 | + 'TestCase', |
291 | + 'TestCaseWithFactory', |
292 | + 'test_tales', |
293 | + 'time_counter', |
294 | + # XXX: This really shouldn't be exported from here. People should import |
295 | + # it from Zope. |
296 | + 'verifyObject', |
297 | + 'validate_mock_class', |
298 | + 'with_anonymous_login', |
299 | + ] |
300 | |
301 | +import copy |
302 | from datetime import datetime, timedelta |
303 | -from pprint import pformat |
304 | -import copy |
305 | from inspect import getargspec, getmembers, getmro, isclass, ismethod |
306 | import os |
307 | +from pprint import pformat |
308 | import shutil |
309 | import subprocess |
310 | import tempfile |
311 | import time |
312 | -import unittest |
313 | |
314 | from bzrlib.branch import Branch as BzrBranch |
315 | from bzrlib.bzrdir import BzrDir, format_registry |
316 | -from bzrlib.errors import InvalidURLJoin |
317 | from bzrlib.transport import get_transport |
318 | |
319 | import pytz |
320 | @@ -26,7 +49,11 @@ |
321 | from storm.store import Store |
322 | from storm.tracer import install_tracer, remove_tracer_type |
323 | |
324 | +import testtools |
325 | import transaction |
326 | + |
327 | +from twisted.python.util import mergeFunctionMetadata |
328 | + |
329 | from zope.component import getUtility |
330 | import zope.event |
331 | from zope.interface.verify import verifyClass, verifyObject |
332 | @@ -34,18 +61,17 @@ |
333 | isinstance as zope_isinstance, removeSecurityProxy) |
334 | |
335 | from canonical.launchpad.webapp import errorlog |
336 | +from canonical.config import config |
337 | +from canonical.launchpad.webapp.interfaces import ILaunchBag |
338 | from lp.codehosting.vfs import branch_id_to_path, get_multi_server |
339 | -from canonical.config import config |
340 | # Import the login and logout functions here as it is a much better |
341 | # place to import them from in tests. |
342 | -from canonical.launchpad.webapp.interfaces import ILaunchBag |
343 | - |
344 | from lp.testing._login import ( |
345 | ANONYMOUS, is_logged_in, login, login_person, logout) |
346 | +# canonical.launchpad.ftests expects test_tales to be imported from here. |
347 | +# XXX: JonathanLange 2010-01-01: Why?! |
348 | from lp.testing._tales import test_tales |
349 | |
350 | -from twisted.python.util import mergeFunctionMetadata |
351 | - |
352 | # zope.exception demands more of frame objects than twisted.python.failure |
353 | # provides in its fake frames. This is enough to make it work with them |
354 | # as of 2009-09-16. See https://bugs.edge.launchpad.net/bugs/425113. |
355 | @@ -164,72 +190,9 @@ |
356 | debug(False) |
357 | |
358 | |
359 | -class TestCase(unittest.TestCase): |
360 | +class TestCase(testtools.TestCase): |
361 | """Provide Launchpad-specific test facilities.""" |
362 | |
363 | - # Python 2.4 monkeypatch: |
364 | - if getattr(unittest.TestCase, '_exc_info', None) is None: |
365 | - _exc_info = unittest.TestCase._TestCase__exc_info |
366 | - # We would not expect to need to make this property writeable, but |
367 | - # twisted.trial.unittest.TestCase.__init__ chooses to write to it in |
368 | - # the same way that the __init__ of the standard library's |
369 | - # unittest.TestCase.__init__ does, as part of its own method of |
370 | - # arranging for pre-2.5 compatibility. |
371 | - class MonkeyPatchDescriptor: |
372 | - def __get__(self, obj, type): |
373 | - return obj._TestCase__testMethodName |
374 | - def __set__(self, obj, value): |
375 | - obj._TestCase__testMethodName = value |
376 | - _testMethodName = MonkeyPatchDescriptor() |
377 | - |
378 | - def __init__(self, *args, **kwargs): |
379 | - unittest.TestCase.__init__(self, *args, **kwargs) |
380 | - self._cleanups = [] |
381 | - |
382 | - def __str__(self): |
383 | - """Return the fully qualified Python name of the test. |
384 | - |
385 | - Zope uses this method to determine how to print the test in the |
386 | - runner. We use the test's id in order to make the test easier to find, |
387 | - and also so that modifications to the id will show up. This is |
388 | - particularly important with bzrlib-style test multiplication. |
389 | - """ |
390 | - return self.id() |
391 | - |
392 | - def _runCleanups(self, result): |
393 | - """Run the cleanups that have been added with addCleanup. |
394 | - |
395 | - See the docstring for addCleanup for more information. |
396 | - |
397 | - Returns True if all cleanups ran without error, False otherwise. |
398 | - """ |
399 | - ok = True |
400 | - while self._cleanups: |
401 | - function, arguments, keywordArguments = self._cleanups.pop() |
402 | - try: |
403 | - function(*arguments, **keywordArguments) |
404 | - except KeyboardInterrupt: |
405 | - raise |
406 | - except: |
407 | - result.addError(self, self._exc_info()) |
408 | - ok = False |
409 | - return ok |
410 | - |
411 | - def addCleanup(self, function, *arguments, **keywordArguments): |
412 | - """Add a cleanup function to be called before tearDown. |
413 | - |
414 | - Functions added with addCleanup will be called in reverse order of |
415 | - adding after the test method and before tearDown. |
416 | - |
417 | - If a function added with addCleanup raises an exception, the error |
418 | - will be recorded as a test error, and the next cleanup will then be |
419 | - run. |
420 | - |
421 | - Cleanup functions are always called before a test finishes running, |
422 | - even if setUp is aborted by an exception. |
423 | - """ |
424 | - self._cleanups.append((function, arguments, keywordArguments)) |
425 | - |
426 | def installFixture(self, fixture): |
427 | """Install 'fixture', an object that has a `setUp` and `tearDown`. |
428 | |
429 | @@ -346,27 +309,12 @@ |
430 | self.assertTrue(zope_isinstance(instance, assert_class), |
431 | '%r is not an instance of %r' % (instance, assert_class)) |
432 | |
433 | - def assertIs(self, expected, observed): |
434 | - """Assert that `expected` is the same object as `observed`.""" |
435 | - self.assertTrue(expected is observed, |
436 | - "%r is not %r" % (expected, observed)) |
437 | - |
438 | def assertIsNot(self, expected, observed, msg=None): |
439 | """Assert that `expected` is not the same object as `observed`.""" |
440 | if msg is None: |
441 | msg = "%r is %r" % (expected, observed) |
442 | self.assertTrue(expected is not observed, msg) |
443 | |
444 | - def assertIn(self, needle, haystack): |
445 | - """Assert that 'needle' is in 'haystack'.""" |
446 | - self.assertTrue( |
447 | - needle in haystack, '%r not in %r' % (needle, haystack)) |
448 | - |
449 | - def assertNotIn(self, needle, haystack): |
450 | - """Assert that 'needle' is not in 'haystack'.""" |
451 | - self.assertFalse( |
452 | - needle in haystack, '%r in %r' % (needle, haystack)) |
453 | - |
454 | def assertContentEqual(self, iter1, iter2): |
455 | """Assert that 'iter1' has the same content as 'iter2'.""" |
456 | list1 = sorted(iter1) |
457 | @@ -374,28 +322,6 @@ |
458 | self.assertEqual( |
459 | list1, list2, '%s != %s' % (pformat(list1), pformat(list2))) |
460 | |
461 | - def assertRaises(self, excClass, callableObj, *args, **kwargs): |
462 | - """Assert that a callable raises a particular exception. |
463 | - |
464 | - :param excClass: As for the except statement, this may be either an |
465 | - exception class, or a tuple of classes. |
466 | - :param callableObj: A callable, will be passed ``*args`` and |
467 | - ``**kwargs``. |
468 | - |
469 | - Returns the exception so that you can examine it. |
470 | - """ |
471 | - try: |
472 | - callableObj(*args, **kwargs) |
473 | - except excClass, e: |
474 | - return e |
475 | - else: |
476 | - if getattr(excClass, '__name__', None) is not None: |
477 | - excName = excClass.__name__ |
478 | - else: |
479 | - # probably a tuple |
480 | - excName = str(excClass) |
481 | - raise self.failureException, "%s not raised" % excName |
482 | - |
483 | def assertRaisesWithContent(self, exception, exception_content, |
484 | func, *args): |
485 | """Check if the given exception is raised with given content. |
486 | @@ -403,15 +329,8 @@ |
487 | If the exception isn't raised or the exception_content doesn't |
488 | match what was raised an AssertionError is raised. |
489 | """ |
490 | - exception_name = str(exception).split('.')[-1] |
491 | - |
492 | - try: |
493 | - func(*args) |
494 | - except exception, err: |
495 | - self.assertEqual(str(err), exception_content) |
496 | - else: |
497 | - raise AssertionError( |
498 | - "'%s' was not raised" % exception_name) |
499 | + err = self.assertRaises(exception, func, *args) |
500 | + self.assertEqual(exception_content, str(err)) |
501 | |
502 | def assertBetween(self, lower_bound, variable, upper_bound): |
503 | """Assert that 'variable' is strictly between two boundaries.""" |
504 | @@ -425,51 +344,12 @@ |
505 | The config values will be restored during test tearDown. |
506 | """ |
507 | name = self.factory.getUniqueString() |
508 | - body = '\n'.join(["%s: %s"%(k, v) for k, v in kwargs.iteritems()]) |
509 | + body = '\n'.join(["%s: %s" % (k, v) for k, v in kwargs.iteritems()]) |
510 | config.push(name, "\n[%s]\n%s\n" % (section, body)) |
511 | self.addCleanup(config.pop, name) |
512 | |
513 | - def run(self, result=None): |
514 | - if result is None: |
515 | - result = self.defaultTestResult() |
516 | - result.startTest(self) |
517 | - testMethod = getattr(self, self._testMethodName) |
518 | - try: |
519 | - try: |
520 | - self.setUp() |
521 | - except KeyboardInterrupt: |
522 | - raise |
523 | - except: |
524 | - result.addError(self, self._exc_info()) |
525 | - self._runCleanups(result) |
526 | - return |
527 | - |
528 | - ok = False |
529 | - try: |
530 | - testMethod() |
531 | - ok = True |
532 | - except self.failureException: |
533 | - result.addFailure(self, self._exc_info()) |
534 | - except KeyboardInterrupt: |
535 | - raise |
536 | - except: |
537 | - result.addError(self, self._exc_info()) |
538 | - |
539 | - cleanupsOk = self._runCleanups(result) |
540 | - try: |
541 | - self.tearDown() |
542 | - except KeyboardInterrupt: |
543 | - raise |
544 | - except: |
545 | - result.addError(self, self._exc_info()) |
546 | - ok = False |
547 | - if ok and cleanupsOk: |
548 | - result.addSuccess(self) |
549 | - finally: |
550 | - result.stopTest(self) |
551 | - |
552 | def setUp(self): |
553 | - unittest.TestCase.setUp(self) |
554 | + testtools.TestCase.setUp(self) |
555 | from lp.testing.factory import ObjectFactory |
556 | self.factory = ObjectFactory() |
557 | |
558 | @@ -673,6 +553,8 @@ |
559 | zope.event.subscribers[:] = old_subscribers |
560 | |
561 | |
562 | +# XXX: This doesn't seem like a generically-useful testing function. Perhaps |
563 | +# it should go in a sub-module or something? -- jml |
564 | def get_lsb_information(): |
565 | """Returns a dictionary with the LSB host information. |
566 | |
567 | @@ -770,6 +652,8 @@ |
568 | return " ".join(string.split()) |
569 | |
570 | |
571 | +# XXX: This doesn't seem to be a generically useful testing function. Perhaps |
572 | +# it should go into a sub-module? -- jml |
573 | def map_branch_contents(branch_url): |
574 | """Return all files in branch at `branch_url`. |
575 | |
576 | @@ -794,6 +678,7 @@ |
577 | |
578 | return contents |
579 | |
580 | + |
581 | def validate_mock_class(mock_class): |
582 | """Validate method signatures in mock classes derived from real classes. |
583 | |
584 | |
585 | === removed symlink 'lib/testtools' |
586 | === target was u'../sourcecode/testtools/testtools/' |
587 | === modified file 'utilities/sourcedeps.conf' |
588 | --- utilities/sourcedeps.conf 2010-01-14 03:48:53 +0000 |
589 | +++ utilities/sourcedeps.conf 2010-01-14 04:34:14 +0000 |
590 | @@ -13,5 +13,5 @@ |
591 | subunit lp:~launchpad-pqm/subunit/trunk;revno=61 |
592 | subvertpy lp:~launchpad-pqm/subvertpy/trunk;revno=2040 |
593 | testresources lp:~launchpad-pqm/testresources/dev;revno=16 |
594 | -canonical-identity-provider lp:~launchpad-pqm/canonical-identity-provider/trunk;revno=8900 optional |
595 | +canonical-identity-provider lp:~launchpad-pqm/canonical-identity-provider/trunk;revno=8901 optional |
596 | shipit lp:~launchpad-pqm/shipit/trunk;revno=8901 optional |
This is a bit preliminary, because there are still XXX comments and I don't really know how to do this sort of thing, but it's worth getting feedback now.
This branch changes Launchpad to use a testtools release, rather than the branch. It then changes our base test case to inherit from testtools.TestCase rather than unittest.TestCase directly. This lets us delete a whole bunch of code.
While looking at lp/testing/ __init_ _.py, I noticed a few things that could be improved. The ones I could easily fix (like the duplicated code in 'assertRaisesWi thContent' ), I fixed. Others, I've marked with XXXs.
I haven't run the tests yet, partly since I'm afraid of committing the tarball for testtools to the download-cache.