Merge lp:~jml/launchpad/use-testtools into lp:launchpad

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
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.

To post a comment you must log in.
Revision history for this message
Jonathan Lange (jml) wrote :

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 'assertRaisesWithContent'), 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.

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/testtools-0.9.2.tar.gz

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
=== removed file 'lib/canonical/launchpad/testing/tests/test_testcase.py'
--- lib/canonical/launchpad/testing/tests/test_testcase.py 2009-06-25 05:30:52 +0000
+++ lib/canonical/launchpad/testing/tests/test_testcase.py 1970-01-01 00:00:00 +0000
@@ -1,163 +0,0 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Tests for the base TestCase classes."""
5
6__metaclass__ = type
7
8import unittest
9from lp.testing import TestCase
10
11
12class LoggingResult(unittest.TestResult):
13 """TestResult that logs its event to a list."""
14
15 def __init__(self, log):
16 self._events = log
17 super(LoggingResult, self).__init__()
18
19 def startTest(self, test):
20 self._events.append(('startTest', test))
21 super(LoggingResult, self).startTest(test)
22
23 def stopTest(self, test):
24 self._events.append(('stopTest', test))
25 super(LoggingResult, self).stopTest(test)
26
27 def addFailure(self, test, error):
28 self._events.append(('addFailure', test, error))
29 super(LoggingResult, self).addFailure(test, error)
30
31 def addError(self, test, error):
32 self._events.append(('addError', test, error))
33 super(LoggingResult, self).addError(test, error)
34
35 def addSuccess(self, test):
36 self._events.append(('addSuccess', test))
37 super(LoggingResult, self).addSuccess(test)
38
39
40class TestAddCleanup(unittest.TestCase):
41 """Tests for TestCase.addCleanup."""
42
43 class LoggingTest(TestCase):
44 """A test that logs calls to setUp, runTest and tearDown."""
45
46 def setUp(self):
47 self._calls = ['setUp']
48
49 def brokenSetUp(self):
50 # A tearDown that deliberately fails.
51 self._calls = ['brokenSetUp']
52 raise RuntimeError('Deliberate Failure')
53
54 def runTest(self):
55 self._calls.append('runTest')
56
57 def tearDown(self):
58 self._calls.append('tearDown')
59
60 def setUp(self):
61 self._result_calls = []
62 self.test = TestAddCleanup.LoggingTest('runTest')
63 self.logging_result = LoggingResult(self._result_calls)
64
65 def assertErrorLogEqual(self, messages):
66 self.assertEqual(messages, [call[0] for call in self._result_calls])
67
68 def assertTestLogEqual(self, messages):
69 """Assert that the call log equals `messages`."""
70 self.assertEqual(messages, self.test._calls)
71
72 def logAppender(self, message):
73 """Return a cleanup that appends `message` to the tests log.
74
75 Cleanups are callables that are added to a test by addCleanup. To
76 verify that our cleanups run in the right order, we add strings to a
77 list that acts as a log. This method returns a cleanup that will add
78 the given message to that log when run.
79 """
80 self.test._calls.append(message)
81
82 def test_fixture(self):
83 # A normal run of self.test logs 'setUp', 'runTest' and 'tearDown'.
84 # This test doesn't test addCleanup itself, it just sanity checks the
85 # fixture.
86 self.test.run(self.logging_result)
87 self.assertTestLogEqual(['setUp', 'runTest', 'tearDown'])
88
89 def test_cleanup_run_before_tearDown(self):
90 # Cleanup functions added with 'addCleanup' are called before tearDown
91 # runs.
92 self.test.addCleanup(self.logAppender, 'cleanup')
93 self.test.run(self.logging_result)
94 self.assertTestLogEqual(['setUp', 'runTest', 'cleanup', 'tearDown'])
95
96 def test_add_cleanup_called_if_setUp_fails(self):
97 # Cleanup functions added with 'addCleanup' are called even if setUp
98 # fails. Note that tearDown has a different behavior: it is only
99 # called when setUp succeeds.
100 self.test.setUp = self.test.brokenSetUp
101 self.test.addCleanup(self.logAppender, 'cleanup')
102 self.test.run(self.logging_result)
103 self.assertTestLogEqual(['brokenSetUp', 'cleanup'])
104
105 def test_addCleanup_called_in_reverse_order(self):
106 # Cleanup functions added with 'addCleanup' are called in reverse
107 # order.
108 #
109 # One of the main uses of addCleanup is to dynamically create
110 # resources that need some sort of explicit tearDown. Often one
111 # resource will be created in terms of another, e.g.,
112 # self.first = self.makeFirst()
113 # self.second = self.makeSecond(self.first)
114 #
115 # When this happens, we generally want to clean up the second resource
116 # before the first one, since the second depends on the first.
117 self.test.addCleanup(self.logAppender, 'first')
118 self.test.addCleanup(self.logAppender, 'second')
119 self.test.run(self.logging_result)
120 self.assertTestLogEqual(
121 ['setUp', 'runTest', 'second', 'first', 'tearDown'])
122
123 def test_tearDown_runs_after_cleanup_failure(self):
124 # tearDown runs even if a cleanup function fails.
125 self.test.addCleanup(lambda: 1/0)
126 self.test.run(self.logging_result)
127 self.assertTestLogEqual(['setUp', 'runTest', 'tearDown'])
128
129 def test_cleanups_continue_running_after_error(self):
130 # All cleanups are always run, even if one or two of them fail.
131 self.test.addCleanup(self.logAppender, 'first')
132 self.test.addCleanup(lambda: 1/0)
133 self.test.addCleanup(self.logAppender, 'second')
134 self.test.run(self.logging_result)
135 self.assertTestLogEqual(
136 ['setUp', 'runTest', 'second', 'first', 'tearDown'])
137
138 def test_error_in_cleanups_are_captured(self):
139 # If a cleanup raises an error, we want to record it and fail the
140 # test, even though we go on to run other cleanups.
141 self.test.addCleanup(lambda: 1/0)
142 self.test.run(self.logging_result)
143 self.assertErrorLogEqual(['startTest', 'addError', 'stopTest'])
144
145 def test_keyboard_interrupt_not_caught(self):
146 # If a cleanup raises KeyboardInterrupt, it gets reraised.
147 def raiseKeyboardInterrupt():
148 raise KeyboardInterrupt()
149 self.test.addCleanup(raiseKeyboardInterrupt)
150 self.assertRaises(
151 KeyboardInterrupt, self.test.run, self.logging_result)
152
153 def test_multipleErrorsReported(self):
154 # Errors from all failing cleanups are reported.
155 self.test.addCleanup(lambda: 1/0)
156 self.test.addCleanup(lambda: 1/0)
157 self.test.run(self.logging_result)
158 self.assertErrorLogEqual(
159 ['startTest', 'addError', 'addError', 'stopTest'])
160
161
162def test_suite():
163 return unittest.TestLoader().loadTestsFromName(__name__)
1640
=== modified file 'lib/lp/codehosting/puller/tests/test_scheduler.py'
--- lib/lp/codehosting/puller/tests/test_scheduler.py 2009-12-22 23:35:26 +0000
+++ lib/lp/codehosting/puller/tests/test_scheduler.py 2010-01-14 04:34:14 +0000
@@ -640,6 +640,11 @@
640640
641 layer = TwistedAppServerLayer641 layer = TwistedAppServerLayer
642642
643 # XXX: Hacky workaround for Trial. It interprets the skip method as a
644 # reason to skip the tests, but the default test result object doesn't
645 # support skipping, hence errors.
646 skip = None
647
643 def setUp(self):648 def setUp(self):
644 TrialTestCase.setUp(self)649 TrialTestCase.setUp(self)
645 PullerBranchTestCase.setUp(self)650 PullerBranchTestCase.setUp(self)
646651
=== modified file 'lib/lp/registry/windmill/tests/test_add_milestone.py.disabled'
--- lib/lp/registry/windmill/tests/test_add_milestone.py.disabled 2010-01-12 22:43:49 +0000
+++ lib/lp/registry/windmill/tests/test_add_milestone.py.disabled 2010-01-14 04:34:14 +0000
@@ -87,6 +87,7 @@
87 layer = RegistryWindmillLayer87 layer = RegistryWindmillLayer
8888
89 def setUp(self):89 def setUp(self):
90 super(TestAddMilestone, self).setUp()
90 self.client = WindmillTestClient('AddMilestone')91 self.client = WindmillTestClient('AddMilestone')
9192
92 def test_adding_milestone_on_addrelease_page(self):93 def test_adding_milestone_on_addrelease_page(self):
9394
=== modified file 'lib/lp/registry/windmill/tests/test_datetime_picker.py'
--- lib/lp/registry/windmill/tests/test_datetime_picker.py 2009-12-10 17:52:56 +0000
+++ lib/lp/registry/windmill/tests/test_datetime_picker.py 2010-01-14 04:34:14 +0000
@@ -22,6 +22,7 @@
22 layer = RegistryWindmillLayer22 layer = RegistryWindmillLayer
2323
24 def setUp(self):24 def setUp(self):
25 super(TestDateTimeCalendarWidget, self).setUp()
25 self.client = WindmillTestClient('DateTimeCalendarWidget')26 self.client = WindmillTestClient('DateTimeCalendarWidget')
2627
27 def test_datetime_calendar_widget(self):28 def test_datetime_calendar_widget(self):
2829
=== modified file 'lib/lp/registry/windmill/tests/test_plusnew_step1.py'
--- lib/lp/registry/windmill/tests/test_plusnew_step1.py 2009-12-11 19:54:04 +0000
+++ lib/lp/registry/windmill/tests/test_plusnew_step1.py 2010-01-14 04:34:14 +0000
@@ -24,6 +24,7 @@
24 layer = RegistryWindmillLayer24 layer = RegistryWindmillLayer
2525
26 def setUp(self):26 def setUp(self):
27 super(TestNewProjectStep1, self).setUp()
27 self.client = WindmillTestClient('TestNewProjectStep1')28 self.client = WindmillTestClient('TestNewProjectStep1')
2829
29 def test_projects_plusnew_text_fields(self):30 def test_projects_plusnew_text_fields(self):
3031
=== modified file 'lib/lp/registry/windmill/tests/test_plusnew_step2.py'
--- lib/lp/registry/windmill/tests/test_plusnew_step2.py 2009-12-11 19:54:04 +0000
+++ lib/lp/registry/windmill/tests/test_plusnew_step2.py 2010-01-14 04:34:14 +0000
@@ -22,6 +22,7 @@
22 layer = RegistryWindmillLayer22 layer = RegistryWindmillLayer
2323
24 def setUp(self):24 def setUp(self):
25 super(TestNewProjectStep2, self).setUp()
25 self.client = WindmillTestClient('TestNewProjectStep2')26 self.client = WindmillTestClient('TestNewProjectStep2')
2627
27 def test_projects_plusnew_step_two(self):28 def test_projects_plusnew_step_two(self):
2829
=== modified file 'lib/lp/registry/windmill/tests/test_team_index.py'
--- lib/lp/registry/windmill/tests/test_team_index.py 2009-12-18 05:46:01 +0000
+++ lib/lp/registry/windmill/tests/test_team_index.py 2010-01-14 04:34:14 +0000
@@ -24,6 +24,7 @@
24 layer = RegistryWindmillLayer24 layer = RegistryWindmillLayer
2525
26 def setUp(self):26 def setUp(self):
27 super(TestTeamIndex, self).setUp()
27 self.client = WindmillTestClient(__name__)28 self.client = WindmillTestClient(__name__)
2829
29 def test_addmember(self):30 def test_addmember(self):
3031
=== modified file 'lib/lp/registry/windmill/tests/test_timeline_graph.py'
--- lib/lp/registry/windmill/tests/test_timeline_graph.py 2009-12-10 17:52:56 +0000
+++ lib/lp/registry/windmill/tests/test_timeline_graph.py 2010-01-14 04:34:14 +0000
@@ -13,12 +13,14 @@
13from lp.registry.windmill.testing import RegistryWindmillLayer13from lp.registry.windmill.testing import RegistryWindmillLayer
14from lp.testing import TestCaseWithFactory14from lp.testing import TestCaseWithFactory
1515
16
16class TestTimelineGraph(TestCaseWithFactory):17class TestTimelineGraph(TestCaseWithFactory):
17 """Test timeline graph widget."""18 """Test timeline graph widget."""
1819
19 layer = RegistryWindmillLayer20 layer = RegistryWindmillLayer
2021
21 def setUp(self):22 def setUp(self):
23 super(TestTimelineGraph, self).setUp()
22 self.client = WindmillTestClient('TimelineGraph')24 self.client = WindmillTestClient('TimelineGraph')
2325
24 def test_timeline_graph(self):26 def test_timeline_graph(self):
2527
=== modified file 'lib/lp/testing/__init__.py'
--- lib/lp/testing/__init__.py 2009-12-22 23:50:27 +0000
+++ lib/lp/testing/__init__.py 2010-01-14 04:34:14 +0000
@@ -1,24 +1,47 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the1# Copyright 2009, 2010 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4# pylint: disable-msg=W0401,C03014# pylint: disable-msg=W0401,C0301
55
6__metaclass__ = type6__metaclass__ = type
7__all__ = [
8 'ANONYMOUS',
9 'capture_events',
10 'FakeTime',
11 'get_lsb_information',
12 'is_logged_in',
13 'login',
14 'login_person',
15 'logout',
16 'map_branch_contents',
17 'normalize_whitespace',
18 'record_statements',
19 'run_with_login',
20 'run_with_storm_debug',
21 'run_script',
22 'TestCase',
23 'TestCaseWithFactory',
24 'test_tales',
25 'time_counter',
26 # XXX: This really shouldn't be exported from here. People should import
27 # it from Zope.
28 'verifyObject',
29 'validate_mock_class',
30 'with_anonymous_login',
31 ]
732
33import copy
8from datetime import datetime, timedelta34from datetime import datetime, timedelta
9from pprint import pformat
10import copy
11from inspect import getargspec, getmembers, getmro, isclass, ismethod35from inspect import getargspec, getmembers, getmro, isclass, ismethod
12import os36import os
37from pprint import pformat
13import shutil38import shutil
14import subprocess39import subprocess
15import tempfile40import tempfile
16import time41import time
17import unittest
1842
19from bzrlib.branch import Branch as BzrBranch43from bzrlib.branch import Branch as BzrBranch
20from bzrlib.bzrdir import BzrDir, format_registry44from bzrlib.bzrdir import BzrDir, format_registry
21from bzrlib.errors import InvalidURLJoin
22from bzrlib.transport import get_transport45from bzrlib.transport import get_transport
2346
24import pytz47import pytz
@@ -26,7 +49,11 @@
26from storm.store import Store49from storm.store import Store
27from storm.tracer import install_tracer, remove_tracer_type50from storm.tracer import install_tracer, remove_tracer_type
2851
52import testtools
29import transaction53import transaction
54
55from twisted.python.util import mergeFunctionMetadata
56
30from zope.component import getUtility57from zope.component import getUtility
31import zope.event58import zope.event
32from zope.interface.verify import verifyClass, verifyObject59from zope.interface.verify import verifyClass, verifyObject
@@ -34,18 +61,17 @@
34 isinstance as zope_isinstance, removeSecurityProxy)61 isinstance as zope_isinstance, removeSecurityProxy)
3562
36from canonical.launchpad.webapp import errorlog63from canonical.launchpad.webapp import errorlog
64from canonical.config import config
65from canonical.launchpad.webapp.interfaces import ILaunchBag
37from lp.codehosting.vfs import branch_id_to_path, get_multi_server66from lp.codehosting.vfs import branch_id_to_path, get_multi_server
38from canonical.config import config
39# Import the login and logout functions here as it is a much better67# Import the login and logout functions here as it is a much better
40# place to import them from in tests.68# place to import them from in tests.
41from canonical.launchpad.webapp.interfaces import ILaunchBag
42
43from lp.testing._login import (69from lp.testing._login import (
44 ANONYMOUS, is_logged_in, login, login_person, logout)70 ANONYMOUS, is_logged_in, login, login_person, logout)
71# canonical.launchpad.ftests expects test_tales to be imported from here.
72# XXX: JonathanLange 2010-01-01: Why?!
45from lp.testing._tales import test_tales73from lp.testing._tales import test_tales
4674
47from twisted.python.util import mergeFunctionMetadata
48
49# zope.exception demands more of frame objects than twisted.python.failure75# zope.exception demands more of frame objects than twisted.python.failure
50# provides in its fake frames. This is enough to make it work with them76# provides in its fake frames. This is enough to make it work with them
51# as of 2009-09-16. See https://bugs.edge.launchpad.net/bugs/425113.77# as of 2009-09-16. See https://bugs.edge.launchpad.net/bugs/425113.
@@ -164,72 +190,9 @@
164 debug(False)190 debug(False)
165191
166192
167class TestCase(unittest.TestCase):193class TestCase(testtools.TestCase):
168 """Provide Launchpad-specific test facilities."""194 """Provide Launchpad-specific test facilities."""
169195
170 # Python 2.4 monkeypatch:
171 if getattr(unittest.TestCase, '_exc_info', None) is None:
172 _exc_info = unittest.TestCase._TestCase__exc_info
173 # We would not expect to need to make this property writeable, but
174 # twisted.trial.unittest.TestCase.__init__ chooses to write to it in
175 # the same way that the __init__ of the standard library's
176 # unittest.TestCase.__init__ does, as part of its own method of
177 # arranging for pre-2.5 compatibility.
178 class MonkeyPatchDescriptor:
179 def __get__(self, obj, type):
180 return obj._TestCase__testMethodName
181 def __set__(self, obj, value):
182 obj._TestCase__testMethodName = value
183 _testMethodName = MonkeyPatchDescriptor()
184
185 def __init__(self, *args, **kwargs):
186 unittest.TestCase.__init__(self, *args, **kwargs)
187 self._cleanups = []
188
189 def __str__(self):
190 """Return the fully qualified Python name of the test.
191
192 Zope uses this method to determine how to print the test in the
193 runner. We use the test's id in order to make the test easier to find,
194 and also so that modifications to the id will show up. This is
195 particularly important with bzrlib-style test multiplication.
196 """
197 return self.id()
198
199 def _runCleanups(self, result):
200 """Run the cleanups that have been added with addCleanup.
201
202 See the docstring for addCleanup for more information.
203
204 Returns True if all cleanups ran without error, False otherwise.
205 """
206 ok = True
207 while self._cleanups:
208 function, arguments, keywordArguments = self._cleanups.pop()
209 try:
210 function(*arguments, **keywordArguments)
211 except KeyboardInterrupt:
212 raise
213 except:
214 result.addError(self, self._exc_info())
215 ok = False
216 return ok
217
218 def addCleanup(self, function, *arguments, **keywordArguments):
219 """Add a cleanup function to be called before tearDown.
220
221 Functions added with addCleanup will be called in reverse order of
222 adding after the test method and before tearDown.
223
224 If a function added with addCleanup raises an exception, the error
225 will be recorded as a test error, and the next cleanup will then be
226 run.
227
228 Cleanup functions are always called before a test finishes running,
229 even if setUp is aborted by an exception.
230 """
231 self._cleanups.append((function, arguments, keywordArguments))
232
233 def installFixture(self, fixture):196 def installFixture(self, fixture):
234 """Install 'fixture', an object that has a `setUp` and `tearDown`.197 """Install 'fixture', an object that has a `setUp` and `tearDown`.
235198
@@ -346,27 +309,12 @@
346 self.assertTrue(zope_isinstance(instance, assert_class),309 self.assertTrue(zope_isinstance(instance, assert_class),
347 '%r is not an instance of %r' % (instance, assert_class))310 '%r is not an instance of %r' % (instance, assert_class))
348311
349 def assertIs(self, expected, observed):
350 """Assert that `expected` is the same object as `observed`."""
351 self.assertTrue(expected is observed,
352 "%r is not %r" % (expected, observed))
353
354 def assertIsNot(self, expected, observed, msg=None):312 def assertIsNot(self, expected, observed, msg=None):
355 """Assert that `expected` is not the same object as `observed`."""313 """Assert that `expected` is not the same object as `observed`."""
356 if msg is None:314 if msg is None:
357 msg = "%r is %r" % (expected, observed)315 msg = "%r is %r" % (expected, observed)
358 self.assertTrue(expected is not observed, msg)316 self.assertTrue(expected is not observed, msg)
359317
360 def assertIn(self, needle, haystack):
361 """Assert that 'needle' is in 'haystack'."""
362 self.assertTrue(
363 needle in haystack, '%r not in %r' % (needle, haystack))
364
365 def assertNotIn(self, needle, haystack):
366 """Assert that 'needle' is not in 'haystack'."""
367 self.assertFalse(
368 needle in haystack, '%r in %r' % (needle, haystack))
369
370 def assertContentEqual(self, iter1, iter2):318 def assertContentEqual(self, iter1, iter2):
371 """Assert that 'iter1' has the same content as 'iter2'."""319 """Assert that 'iter1' has the same content as 'iter2'."""
372 list1 = sorted(iter1)320 list1 = sorted(iter1)
@@ -374,28 +322,6 @@
374 self.assertEqual(322 self.assertEqual(
375 list1, list2, '%s != %s' % (pformat(list1), pformat(list2)))323 list1, list2, '%s != %s' % (pformat(list1), pformat(list2)))
376324
377 def assertRaises(self, excClass, callableObj, *args, **kwargs):
378 """Assert that a callable raises a particular exception.
379
380 :param excClass: As for the except statement, this may be either an
381 exception class, or a tuple of classes.
382 :param callableObj: A callable, will be passed ``*args`` and
383 ``**kwargs``.
384
385 Returns the exception so that you can examine it.
386 """
387 try:
388 callableObj(*args, **kwargs)
389 except excClass, e:
390 return e
391 else:
392 if getattr(excClass, '__name__', None) is not None:
393 excName = excClass.__name__
394 else:
395 # probably a tuple
396 excName = str(excClass)
397 raise self.failureException, "%s not raised" % excName
398
399 def assertRaisesWithContent(self, exception, exception_content,325 def assertRaisesWithContent(self, exception, exception_content,
400 func, *args):326 func, *args):
401 """Check if the given exception is raised with given content.327 """Check if the given exception is raised with given content.
@@ -403,15 +329,8 @@
403 If the exception isn't raised or the exception_content doesn't329 If the exception isn't raised or the exception_content doesn't
404 match what was raised an AssertionError is raised.330 match what was raised an AssertionError is raised.
405 """331 """
406 exception_name = str(exception).split('.')[-1]332 err = self.assertRaises(exception, func, *args)
407333 self.assertEqual(exception_content, str(err))
408 try:
409 func(*args)
410 except exception, err:
411 self.assertEqual(str(err), exception_content)
412 else:
413 raise AssertionError(
414 "'%s' was not raised" % exception_name)
415334
416 def assertBetween(self, lower_bound, variable, upper_bound):335 def assertBetween(self, lower_bound, variable, upper_bound):
417 """Assert that 'variable' is strictly between two boundaries."""336 """Assert that 'variable' is strictly between two boundaries."""
@@ -425,51 +344,12 @@
425 The config values will be restored during test tearDown.344 The config values will be restored during test tearDown.
426 """345 """
427 name = self.factory.getUniqueString()346 name = self.factory.getUniqueString()
428 body = '\n'.join(["%s: %s"%(k, v) for k, v in kwargs.iteritems()])347 body = '\n'.join(["%s: %s" % (k, v) for k, v in kwargs.iteritems()])
429 config.push(name, "\n[%s]\n%s\n" % (section, body))348 config.push(name, "\n[%s]\n%s\n" % (section, body))
430 self.addCleanup(config.pop, name)349 self.addCleanup(config.pop, name)
431350
432 def run(self, result=None):
433 if result is None:
434 result = self.defaultTestResult()
435 result.startTest(self)
436 testMethod = getattr(self, self._testMethodName)
437 try:
438 try:
439 self.setUp()
440 except KeyboardInterrupt:
441 raise
442 except:
443 result.addError(self, self._exc_info())
444 self._runCleanups(result)
445 return
446
447 ok = False
448 try:
449 testMethod()
450 ok = True
451 except self.failureException:
452 result.addFailure(self, self._exc_info())
453 except KeyboardInterrupt:
454 raise
455 except:
456 result.addError(self, self._exc_info())
457
458 cleanupsOk = self._runCleanups(result)
459 try:
460 self.tearDown()
461 except KeyboardInterrupt:
462 raise
463 except:
464 result.addError(self, self._exc_info())
465 ok = False
466 if ok and cleanupsOk:
467 result.addSuccess(self)
468 finally:
469 result.stopTest(self)
470
471 def setUp(self):351 def setUp(self):
472 unittest.TestCase.setUp(self)352 testtools.TestCase.setUp(self)
473 from lp.testing.factory import ObjectFactory353 from lp.testing.factory import ObjectFactory
474 self.factory = ObjectFactory()354 self.factory = ObjectFactory()
475355
@@ -673,6 +553,8 @@
673 zope.event.subscribers[:] = old_subscribers553 zope.event.subscribers[:] = old_subscribers
674554
675555
556# XXX: This doesn't seem like a generically-useful testing function. Perhaps
557# it should go in a sub-module or something? -- jml
676def get_lsb_information():558def get_lsb_information():
677 """Returns a dictionary with the LSB host information.559 """Returns a dictionary with the LSB host information.
678560
@@ -770,6 +652,8 @@
770 return " ".join(string.split())652 return " ".join(string.split())
771653
772654
655# XXX: This doesn't seem to be a generically useful testing function. Perhaps
656# it should go into a sub-module? -- jml
773def map_branch_contents(branch_url):657def map_branch_contents(branch_url):
774 """Return all files in branch at `branch_url`.658 """Return all files in branch at `branch_url`.
775659
@@ -794,6 +678,7 @@
794678
795 return contents679 return contents
796680
681
797def validate_mock_class(mock_class):682def validate_mock_class(mock_class):
798 """Validate method signatures in mock classes derived from real classes.683 """Validate method signatures in mock classes derived from real classes.
799684
800685
=== removed symlink 'lib/testtools'
=== target was u'../sourcecode/testtools/testtools/'
=== modified file 'utilities/sourcedeps.conf'
--- utilities/sourcedeps.conf 2010-01-14 03:48:53 +0000
+++ utilities/sourcedeps.conf 2010-01-14 04:34:14 +0000
@@ -13,5 +13,5 @@
13subunit lp:~launchpad-pqm/subunit/trunk;revno=6113subunit lp:~launchpad-pqm/subunit/trunk;revno=61
14subvertpy lp:~launchpad-pqm/subvertpy/trunk;revno=204014subvertpy lp:~launchpad-pqm/subvertpy/trunk;revno=2040
15testresources lp:~launchpad-pqm/testresources/dev;revno=1615testresources lp:~launchpad-pqm/testresources/dev;revno=16
16canonical-identity-provider lp:~launchpad-pqm/canonical-identity-provider/trunk;revno=8900 optional16canonical-identity-provider lp:~launchpad-pqm/canonical-identity-provider/trunk;revno=8901 optional
17shipit lp:~launchpad-pqm/shipit/trunk;revno=8901 optional17shipit lp:~launchpad-pqm/shipit/trunk;revno=8901 optional