Merge lp:~jml/testtools/less-stack into lp:~testtools-committers/testtools/trunk
- less-stack
- Merge into trunk
Status: | Superseded | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~jml/testtools/less-stack | ||||
Merge into: | lp:~testtools-committers/testtools/trunk | ||||
Diff against target: |
846 lines (+358/-58) 19 files modified
NEWS (+23/-1) doc/for-framework-folk.rst (+8/-0) doc/for-test-authors.rst (+25/-2) testtools/__init__.py (+3/-0) testtools/helpers.py (+13/-1) testtools/matchers.py (+33/-4) testtools/runtest.py (+5/-0) testtools/testcase.py (+5/-0) testtools/tests/__init__.py (+3/-2) testtools/tests/helpers.py (+43/-7) testtools/tests/test_deferredruntest.py (+2/-2) testtools/tests/test_fixturesupport.py (+2/-2) testtools/tests/test_helpers.py (+86/-0) testtools/tests/test_matchers.py (+34/-6) testtools/tests/test_runtest.py (+2/-2) testtools/tests/test_testcase.py (+5/-3) testtools/tests/test_testresult.py (+29/-16) testtools/tests/test_testsuite.py (+21/-5) testtools/testsuite.py (+16/-5) |
||||
To merge this branch: | bzr merge lp:~jml/testtools/less-stack | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
testtools developers | Pending | ||
Review via email: mp+68436@code.launchpad.net |
Commit message
Description of the change
Start of hiding irrelevant levels of stack.
Still need to re-enable them for testtools own development, and probably a way to make it easy to re-enable them for projects like fixtures and subunit that care a lot about testtools.
Have to say that the improvement is already obvious.
- 206. By Jonathan Lange
-
Don't re-export from helpers.
- 207. By Jonathan Lange
-
Helper method to show and hide test results.
- 208. By Jonathan Lange
-
Cleanups
- 209. By Jonathan Lange
-
Add FixtureSuite and a fixture for hiding/showing the debug levels of stack.
- 210. By Jonathan Lange
-
Merge all-match helper in.
- 211. By Jonathan Lange
-
Merge AfterPreprocessing fix
- 212. By Jonathan Lange
-
Use matchers for better tests.
- 213. By Jonathan Lange
-
Make sure we show testtools own stack levels during run.
- 214. By Jonathan Lange
-
Be explicit in our higher level tests about what levels of the stack we
want to see.Add a test that makes sure that the testtools stack levels are being shown
during a testtools run.Fix the stack hiding control code to delete __unittest rather than set it
to False. This correctly hides stack levels. - 215. By Jonathan Lange
-
Move safe_hasattr
- 216. By Jonathan Lange
-
Tests for safe_hasattr.
- 217. By Jonathan Lange
-
Document FixtureSuite
- 218. By Jonathan Lange
-
Docs and export.
- 219. By Jonathan Lange
-
More NEWS
- 220. By Jonathan Lange
-
Micro-optimize.
- 221. By Jonathan Lange
-
Switch to using a FunctionFixture, per Rob's suggestion.
- 222. By Jonathan Lange
-
Merge trunk
Unmerged revisions
Preview Diff
1 | === modified file 'NEWS' |
2 | --- NEWS 2011-07-20 08:48:46 +0000 |
3 | +++ NEWS 2011-07-20 12:59:27 +0000 |
4 | @@ -4,6 +4,15 @@ |
5 | NEXT |
6 | ~~~~ |
7 | |
8 | +Changes |
9 | +------- |
10 | + |
11 | +* ``AfterPreproccessing`` renamed to ``AfterPreproccesing``, which is a more |
12 | + correct spelling. Old name preserved for backwards compatibility, but is |
13 | + now deprecated. Please stop using it. |
14 | + (Jonathan Lange, #813460) |
15 | + |
16 | + |
17 | Improvements |
18 | ------------ |
19 | |
20 | @@ -16,6 +25,12 @@ |
21 | * Correctly display non-ASCII unicode output on terminals that claim to have a |
22 | unicode encoding. (Martin [gz], #804122) |
23 | |
24 | +* ``FixtureSuite`` added, allows test suites to run with a given fixture. |
25 | + (Jonathan Lange) |
26 | + |
27 | +* Hide testtools's own stack frames when displaying tracebacks, making it |
28 | + easier for test authors to focus on their errors. (Jonathan Lange, #788974) |
29 | + |
30 | * Less boilerplate displayed in test failures and errors. |
31 | (Jonathan Lange, #660852) |
32 | |
33 | @@ -27,7 +42,14 @@ |
34 | * New convenience assertions, ``assertIsNone`` and ``assertIsNotNone``. |
35 | (Christian Kampka) |
36 | |
37 | -* New matcher, ``GreaterThan``. (Christian Kampka) |
38 | +* New matchers: |
39 | + |
40 | + * ``AllMatch`` matches many values against a single matcher. |
41 | + (Jonathan Lange, #615108) |
42 | + |
43 | + * ``GreaterThan``. (Christian Kampka) |
44 | + |
45 | +* New helper, ``safe_hasattr`` added. (Jonathan Lange) |
46 | |
47 | |
48 | 0.9.11 |
49 | |
50 | === modified file 'doc/for-framework-folk.rst' |
51 | --- doc/for-framework-folk.rst 2010-12-22 20:36:40 +0000 |
52 | +++ doc/for-framework-folk.rst 2011-07-20 12:59:27 +0000 |
53 | @@ -182,5 +182,13 @@ |
54 | objects with a run(result), runs them all in threads using the |
55 | ThreadsafeForwardingResult to coalesce their activity. |
56 | |
57 | +FixtureSuite |
58 | +------------ |
59 | + |
60 | +A test suite that sets up a fixture_ before running any tests, and then tears |
61 | +it down after all of the tests are run. The fixture is *not* made available to |
62 | +any of the tests. |
63 | + |
64 | .. _`testtools API docs`: http://mumak.net/testtools/apidocs/ |
65 | .. _unittest: http://docs.python.org/library/unittest.html |
66 | +.. _fixture: http://pypi.python.org/pypi/fixtures |
67 | |
68 | === modified file 'doc/for-test-authors.rst' |
69 | --- doc/for-test-authors.rst 2011-07-19 15:37:07 +0000 |
70 | +++ doc/for-test-authors.rst 2011-07-20 12:59:27 +0000 |
71 | @@ -485,7 +485,7 @@ |
72 | def HasFileContent(content): |
73 | def _read(path): |
74 | return open(path).read() |
75 | - return AfterPreproccessing(_read, Equals(content)) |
76 | + return AfterPreprocessing(_read, Equals(content)) |
77 | self.assertThat('/tmp/foo.txt', PathHasFileContent("Hello world!")) |
78 | |
79 | |
80 | @@ -539,6 +539,21 @@ |
81 | self.assertThat(42, MatchesAny(Equals(5), Not(Equals(6)))) |
82 | |
83 | |
84 | +AllMatch |
85 | +~~~~~~~~ |
86 | + |
87 | +Matches many values against a single matcher. Can be used to make sure that |
88 | +many things all meet the same condition:: |
89 | + |
90 | + def test_all_match_example(self): |
91 | + self.assertThat([2, 3, 5, 7], AllMatch(LessThan(10))) |
92 | + |
93 | +If the match fails, then all of the values that fail to match will be included |
94 | +in the error message. |
95 | + |
96 | +In some ways, this is the converse of MatchesAll_. |
97 | + |
98 | + |
99 | MatchesListwise |
100 | ~~~~~~~~~~~~~~~ |
101 | |
102 | @@ -741,7 +756,6 @@ |
103 | arbitrary-color-name: {{{blue}}} |
104 | |
105 | Traceback (most recent call last): |
106 | - ... |
107 | File "exampletest.py", line 8, in test_thingy |
108 | 1 / 0 # Gratuitous error! |
109 | ZeroDivisionError: integer division or modulo by zero |
110 | @@ -1130,6 +1144,14 @@ |
111 | StringIO = try_imports(['StringIO.StringIO', 'io.StringIO']) |
112 | |
113 | |
114 | +Safe attribute testing |
115 | +---------------------- |
116 | + |
117 | +``hasattr`` is broken_ on many versions of Python. testtools provides |
118 | +``safe_hasattr``, which can be used to safely test whether an object has a |
119 | +particular attribute. |
120 | + |
121 | + |
122 | .. _testrepository: https://launchpad.net/testrepository |
123 | .. _Trial: http://twistedmatrix.com/documents/current/core/howto/testing.html |
124 | .. _nose: http://somethingaboutorange.com/mrl/projects/nose/ |
125 | @@ -1144,3 +1166,4 @@ |
126 | .. _`testtools API docs`: http://mumak.net/testtools/apidocs/ |
127 | .. _Distutils: http://docs.python.org/library/distutils.html |
128 | .. _`setup configuration`: http://docs.python.org/distutils/configfile.html |
129 | +.. _broken: http://chipaca.com/post/3210673069/hasattr-17-less-harmful |
130 | |
131 | === modified file 'testtools/__init__.py' |
132 | --- testtools/__init__.py 2011-06-12 00:58:39 +0000 |
133 | +++ testtools/__init__.py 2011-07-20 12:59:27 +0000 |
134 | @@ -8,12 +8,14 @@ |
135 | 'ErrorHolder', |
136 | 'ExpectedException', |
137 | 'ExtendedToOriginalDecorator', |
138 | + 'FixtureSuite', |
139 | 'iterate_tests', |
140 | 'MultipleExceptions', |
141 | 'MultiTestResult', |
142 | 'PlaceHolder', |
143 | 'run_test_with', |
144 | 'TestCase', |
145 | + 'TestCommand', |
146 | 'TestResult', |
147 | 'TextTestResult', |
148 | 'RunTest', |
149 | @@ -59,6 +61,7 @@ |
150 | ) |
151 | from testtools.testsuite import ( |
152 | ConcurrentTestSuite, |
153 | + FixtureSuite, |
154 | iterate_tests, |
155 | ) |
156 | from testtools.distutilscmd import ( |
157 | |
158 | === modified file 'testtools/helpers.py' |
159 | --- testtools/helpers.py 2011-06-30 15:51:58 +0000 |
160 | +++ testtools/helpers.py 2011-07-20 12:59:27 +0000 |
161 | @@ -1,6 +1,7 @@ |
162 | # Copyright (c) 2010-2011 testtools developers. See LICENSE for details. |
163 | |
164 | __all__ = [ |
165 | + 'safe_hasattr', |
166 | 'try_import', |
167 | 'try_imports', |
168 | ] |
169 | @@ -60,7 +61,7 @@ |
170 | :param module_names: A sequence of module names to try to import. |
171 | :param alternative: The value to return if no module can be imported. |
172 | If unspecified, we raise an ImportError. |
173 | - :param error_callback: If None, called with the ImportError for *each* |
174 | + :param error_callback: If None, called with the ImportError for *each* |
175 | module that fails to load. |
176 | :raises ImportError: If none of the modules can be imported and no |
177 | alternative value was specified. |
178 | @@ -74,3 +75,14 @@ |
179 | raise ImportError( |
180 | "Could not import any of: %s" % ', '.join(module_names)) |
181 | return alternative |
182 | + |
183 | + |
184 | +def safe_hasattr(obj, attr): |
185 | + """Does 'obj' have an attribute 'attr'? |
186 | + |
187 | + Use this rather than built-in hasattr, as the built-in swallows exceptions |
188 | + in some versions of Python and behaves unpredictably with respect to |
189 | + properties. |
190 | + """ |
191 | + marker = object() |
192 | + return getattr(obj, attr, marker) is not marker |
193 | |
194 | === modified file 'testtools/matchers.py' |
195 | --- testtools/matchers.py 2011-07-20 08:48:46 +0000 |
196 | +++ testtools/matchers.py 2011-07-20 12:59:27 +0000 |
197 | @@ -12,7 +12,8 @@ |
198 | |
199 | __metaclass__ = type |
200 | __all__ = [ |
201 | - 'AfterPreproccessing', |
202 | + 'AfterPreprocessing', |
203 | + 'AllMatch', |
204 | 'Annotate', |
205 | 'DocTestMatches', |
206 | 'EndsWith', |
207 | @@ -811,7 +812,7 @@ |
208 | ).match(not_matched[:common_length]) |
209 | |
210 | |
211 | -class AfterPreproccessing(object): |
212 | +class AfterPreprocessing(object): |
213 | """Matches if the value matches after passing through a function. |
214 | |
215 | This can be used to aid in creating trivial matchers as functions, for |
216 | @@ -820,7 +821,7 @@ |
217 | def PathHasFileContent(content): |
218 | def _read(path): |
219 | return open(path).read() |
220 | - return AfterPreproccessing(_read, Equals(content)) |
221 | + return AfterPreprocessing(_read, Equals(content)) |
222 | """ |
223 | |
224 | def __init__(self, preprocessor, matcher): |
225 | @@ -833,7 +834,7 @@ |
226 | return str(self.preprocessor) |
227 | |
228 | def __str__(self): |
229 | - return "AfterPreproccessing(%s, %s)" % ( |
230 | + return "AfterPreprocessing(%s, %s)" % ( |
231 | self._str_preprocessor(), self.matcher) |
232 | |
233 | def match(self, value): |
234 | @@ -841,3 +842,31 @@ |
235 | return Annotate( |
236 | "after %s" % self._str_preprocessor(), |
237 | self.matcher).match(value) |
238 | + |
239 | +# This is the old, deprecated. spelling of the name, kept for backwards |
240 | +# compatibility. |
241 | +AfterPreproccessing = AfterPreprocessing |
242 | + |
243 | + |
244 | +class AllMatch(object): |
245 | + """Matches if all provided values match the given matcher.""" |
246 | + |
247 | + def __init__(self, matcher): |
248 | + self.matcher = matcher |
249 | + |
250 | + def __str__(self): |
251 | + return 'AllMatch(%s)' % (self.matcher,) |
252 | + |
253 | + def match(self, values): |
254 | + mismatches = [] |
255 | + for value in values: |
256 | + mismatch = self.matcher.match(value) |
257 | + if mismatch: |
258 | + mismatches.append(mismatch) |
259 | + if mismatches: |
260 | + return MismatchesAll(mismatches) |
261 | + |
262 | + |
263 | +# Signal that this is part of the testing framework, and that code from this |
264 | +# should not normally appear in tracebacks. |
265 | +__unittest = True |
266 | |
267 | === modified file 'testtools/runtest.py' |
268 | --- testtools/runtest.py 2011-03-22 15:17:10 +0000 |
269 | +++ testtools/runtest.py 2011-07-20 12:59:27 +0000 |
270 | @@ -198,3 +198,8 @@ |
271 | self._exceptions.append(e) |
272 | return self.exception_caught |
273 | raise e |
274 | + |
275 | + |
276 | +# Signal that this is part of the testing framework, and that code from this |
277 | +# should not normally appear in tracebacks. |
278 | +__unittest = True |
279 | |
280 | === modified file 'testtools/testcase.py' |
281 | --- testtools/testcase.py 2011-06-20 11:57:32 +0000 |
282 | +++ testtools/testcase.py 2011-07-20 12:59:27 +0000 |
283 | @@ -772,3 +772,8 @@ |
284 | raise AssertionError('"%s" does not match "%s".' % |
285 | (str(exc_value), self.value_re)) |
286 | return True |
287 | + |
288 | + |
289 | +# Signal that this is part of the testing framework, and that code from this |
290 | +# should not normally appear in tracebacks. |
291 | +__unittest = True |
292 | |
293 | === modified file 'testtools/tests/__init__.py' |
294 | --- testtools/tests/__init__.py 2011-07-11 10:30:08 +0000 |
295 | +++ testtools/tests/__init__.py 2011-07-20 12:59:27 +0000 |
296 | @@ -2,7 +2,8 @@ |
297 | |
298 | # See README for copyright and licensing details. |
299 | |
300 | -import unittest |
301 | +from testtools.tests.helpers import StackHidingFixture |
302 | +from testtools.testsuite import FixtureSuite |
303 | |
304 | |
305 | def test_suite(): |
306 | @@ -41,4 +42,4 @@ |
307 | test_testsuite, |
308 | ] |
309 | suites = map(lambda x: x.test_suite(), modules) |
310 | - return unittest.TestSuite(suites) |
311 | + return FixtureSuite(StackHidingFixture(False), suites) |
312 | |
313 | === modified file 'testtools/tests/helpers.py' |
314 | --- testtools/tests/helpers.py 2011-01-22 17:56:00 +0000 |
315 | +++ testtools/tests/helpers.py 2011-07-20 12:59:27 +0000 |
316 | @@ -1,15 +1,21 @@ |
317 | -# Copyright (c) 2008 testtools developers. See LICENSE for details. |
318 | +# Copyright (c) 2008-2011 testtools developers. See LICENSE for details. |
319 | |
320 | """Helpers for tests.""" |
321 | |
322 | -import sys |
323 | - |
324 | -__metaclass__ = type |
325 | __all__ = [ |
326 | 'LoggingResult', |
327 | ] |
328 | |
329 | +import sys |
330 | + |
331 | +from fixtures import Fixture |
332 | + |
333 | from testtools import TestResult |
334 | +from testtools.helpers import ( |
335 | + safe_hasattr, |
336 | + try_import, |
337 | + ) |
338 | +from testtools import runtest |
339 | |
340 | |
341 | # GZ 2010-08-12: Don't do this, pointlessly creates an exc_info cycle |
342 | @@ -67,6 +73,36 @@ |
343 | self._events.append(('time', a_datetime)) |
344 | super(LoggingResult, self).time(a_datetime) |
345 | |
346 | -# Note, the following three classes are different to LoggingResult by |
347 | -# being fully defined exact matches rather than supersets. |
348 | -from testtools.testresult.doubles import * |
349 | + |
350 | +def is_stack_hidden(): |
351 | + return safe_hasattr(runtest, '__unittest') |
352 | + |
353 | + |
354 | +def hide_testtools_stack(should_hide=True): |
355 | + modules = [ |
356 | + 'testtools.matchers', |
357 | + 'testtools.runtest', |
358 | + 'testtools.testcase', |
359 | + ] |
360 | + for module_name in modules: |
361 | + module = try_import(module_name) |
362 | + if should_hide: |
363 | + setattr(module, '__unittest', True) |
364 | + else: |
365 | + try: |
366 | + delattr(module, '__unittest') |
367 | + except AttributeError: |
368 | + # Attribute already doesn't exist. Our work here is done. |
369 | + pass |
370 | + |
371 | + |
372 | +class StackHidingFixture(Fixture): |
373 | + |
374 | + def __init__(self, should_hide): |
375 | + super(StackHidingFixture, self).__init__() |
376 | + self._should_hide = should_hide |
377 | + |
378 | + def setUp(self): |
379 | + super(StackHidingFixture, self).setUp() |
380 | + self.addCleanup(hide_testtools_stack, is_stack_hidden()) |
381 | + hide_testtools_stack(self._should_hide) |
382 | |
383 | === modified file 'testtools/tests/test_deferredruntest.py' |
384 | --- testtools/tests/test_deferredruntest.py 2011-01-22 17:56:00 +0000 |
385 | +++ testtools/tests/test_deferredruntest.py 2011-07-20 12:59:27 +0000 |
386 | @@ -1,4 +1,4 @@ |
387 | -# Copyright (c) 2010 testtools developers. See LICENSE for details. |
388 | +# Copyright (c) 2010-2011 testtools developers. See LICENSE for details. |
389 | |
390 | """Tests for the DeferredRunTest single test execution logic.""" |
391 | |
392 | @@ -13,7 +13,6 @@ |
393 | text_content, |
394 | ) |
395 | from testtools.helpers import try_import |
396 | -from testtools.tests.helpers import ExtendedTestResult |
397 | from testtools.matchers import ( |
398 | Equals, |
399 | KeysEqual, |
400 | @@ -21,6 +20,7 @@ |
401 | Raises, |
402 | ) |
403 | from testtools.runtest import RunTest |
404 | +from testtools.testresult.doubles import ExtendedTestResult |
405 | from testtools.tests.test_spinner import NeedsTwistedTestCase |
406 | |
407 | assert_fails_with = try_import('testtools.deferredruntest.assert_fails_with') |
408 | |
409 | === modified file 'testtools/tests/test_fixturesupport.py' |
410 | --- testtools/tests/test_fixturesupport.py 2011-05-25 15:03:35 +0000 |
411 | +++ testtools/tests/test_fixturesupport.py 2011-07-20 12:59:27 +0000 |
412 | @@ -1,4 +1,4 @@ |
413 | -# Copyright (c) 2010 testtools developers. See LICENSE for details. |
414 | +# Copyright (c) 2010-2011 testtools developers. See LICENSE for details. |
415 | |
416 | import unittest |
417 | |
418 | @@ -8,7 +8,7 @@ |
419 | content_type, |
420 | ) |
421 | from testtools.helpers import try_import |
422 | -from testtools.tests.helpers import ( |
423 | +from testtools.testresult.doubles import ( |
424 | ExtendedTestResult, |
425 | ) |
426 | |
427 | |
428 | === modified file 'testtools/tests/test_helpers.py' |
429 | --- testtools/tests/test_helpers.py 2011-06-30 15:51:58 +0000 |
430 | +++ testtools/tests/test_helpers.py 2011-07-20 12:59:27 +0000 |
431 | @@ -6,10 +6,18 @@ |
432 | try_imports, |
433 | ) |
434 | from testtools.matchers import ( |
435 | + AllMatch, |
436 | + AfterPreprocessing, |
437 | Equals, |
438 | Is, |
439 | Not, |
440 | ) |
441 | +from testtools.tests.helpers import ( |
442 | + hide_testtools_stack, |
443 | + is_stack_hidden, |
444 | + safe_hasattr, |
445 | + StackHidingFixture, |
446 | + ) |
447 | |
448 | |
449 | def check_error_callback(test, function, arg, expected_error_count, |
450 | @@ -39,6 +47,37 @@ |
451 | test.assertEquals(len(cb_calls), expected_error_count) |
452 | |
453 | |
454 | +class TestSafeHasattr(TestCase): |
455 | + |
456 | + def test_attribute_not_there(self): |
457 | + class Foo(object): |
458 | + pass |
459 | + self.assertEqual(False, safe_hasattr(Foo(), 'anything')) |
460 | + |
461 | + def test_attribute_there(self): |
462 | + class Foo(object): |
463 | + pass |
464 | + foo = Foo() |
465 | + foo.attribute = None |
466 | + self.assertEqual(True, safe_hasattr(foo, 'attribute')) |
467 | + |
468 | + def test_property_there(self): |
469 | + class Foo(object): |
470 | + @property |
471 | + def attribute(self): |
472 | + return None |
473 | + foo = Foo() |
474 | + self.assertEqual(True, safe_hasattr(foo, 'attribute')) |
475 | + |
476 | + def test_property_raises(self): |
477 | + class Foo(object): |
478 | + @property |
479 | + def attribute(self): |
480 | + 1/0 |
481 | + foo = Foo() |
482 | + self.assertRaises(ZeroDivisionError, safe_hasattr, foo, 'attribute') |
483 | + |
484 | + |
485 | class TestTryImport(TestCase): |
486 | |
487 | def test_doesnt_exist(self): |
488 | @@ -154,6 +193,53 @@ |
489 | 0, True) |
490 | |
491 | |
492 | +import testtools.matchers |
493 | +import testtools.runtest |
494 | +import testtools.testcase |
495 | + |
496 | + |
497 | +def StackHidden(is_hidden): |
498 | + return AllMatch( |
499 | + AfterPreprocessing( |
500 | + lambda module: safe_hasattr(module, '__unittest'), |
501 | + Equals(is_hidden))) |
502 | + |
503 | + |
504 | +class TestStackHiding(TestCase): |
505 | + |
506 | + modules = [ |
507 | + testtools.matchers, |
508 | + testtools.runtest, |
509 | + testtools.testcase, |
510 | + ] |
511 | + |
512 | + def setUp(self): |
513 | + super(TestStackHiding, self).setUp() |
514 | + self.addCleanup(hide_testtools_stack, is_stack_hidden()) |
515 | + |
516 | + def test_shown_during_testtools_testsuite(self): |
517 | + self.assertThat(self.modules, StackHidden(False)) |
518 | + |
519 | + def test_is_stack_hidden_consistent_true(self): |
520 | + hide_testtools_stack(True) |
521 | + self.assertEqual(True, is_stack_hidden()) |
522 | + |
523 | + def test_is_stack_hidden_consistent_false(self): |
524 | + hide_testtools_stack(False) |
525 | + self.assertEqual(False, is_stack_hidden()) |
526 | + |
527 | + def test_show_stack(self): |
528 | + hide_testtools_stack(False) |
529 | + self.assertThat(self.modules, StackHidden(False)) |
530 | + |
531 | + def test_fixture(self): |
532 | + current_state = is_stack_hidden() |
533 | + fixture = StackHidingFixture(not current_state) |
534 | + with fixture: |
535 | + self.assertThat(self.modules, StackHidden(not current_state)) |
536 | + self.assertThat(self.modules, StackHidden(current_state)) |
537 | + |
538 | + |
539 | def test_suite(): |
540 | from unittest import TestLoader |
541 | return TestLoader().loadTestsFromName(__name__) |
542 | |
543 | === modified file 'testtools/tests/test_matchers.py' |
544 | --- testtools/tests/test_matchers.py 2011-07-20 08:46:46 +0000 |
545 | +++ testtools/tests/test_matchers.py 2011-07-20 12:59:27 +0000 |
546 | @@ -14,7 +14,8 @@ |
547 | StringIO, |
548 | ) |
549 | from testtools.matchers import ( |
550 | - AfterPreproccessing, |
551 | + AfterPreprocessing, |
552 | + AllMatch, |
553 | Annotate, |
554 | AnnotatedMismatch, |
555 | Equals, |
556 | @@ -698,24 +699,24 @@ |
557 | re.S)) |
558 | |
559 | |
560 | -class TestAfterPreproccessing(TestCase, TestMatchersInterface): |
561 | +class TestAfterPreprocessing(TestCase, TestMatchersInterface): |
562 | |
563 | def parity(x): |
564 | return x % 2 |
565 | |
566 | - matches_matcher = AfterPreproccessing(parity, Equals(1)) |
567 | + matches_matcher = AfterPreprocessing(parity, Equals(1)) |
568 | matches_matches = [3, 5] |
569 | matches_mismatches = [2] |
570 | |
571 | str_examples = [ |
572 | - ("AfterPreproccessing(<function parity>, Equals(1))", |
573 | - AfterPreproccessing(parity, Equals(1))), |
574 | + ("AfterPreprocessing(<function parity>, Equals(1))", |
575 | + AfterPreprocessing(parity, Equals(1))), |
576 | ] |
577 | |
578 | describe_examples = [ |
579 | ("1 != 0: after <function parity>", |
580 | 2, |
581 | - AfterPreproccessing(parity, Equals(1))), |
582 | + AfterPreprocessing(parity, Equals(1))), |
583 | ] |
584 | |
585 | |
586 | @@ -739,6 +740,33 @@ |
587 | repr(decorated)) |
588 | |
589 | |
590 | +class TestAllMatch(TestCase, TestMatchersInterface): |
591 | + |
592 | + matches_matcher = AllMatch(LessThan(10)) |
593 | + matches_matches = [ |
594 | + [9, 9, 9], |
595 | + (9, 9), |
596 | + iter([9, 9, 9, 9, 9]), |
597 | + ] |
598 | + matches_mismatches = [ |
599 | + [11, 9, 9], |
600 | + iter([9, 12, 9, 11]), |
601 | + ] |
602 | + |
603 | + str_examples = [ |
604 | + ("AllMatch(LessThan(12))", AllMatch(LessThan(12))), |
605 | + ] |
606 | + |
607 | + describe_examples = [ |
608 | + ('Differences: [\n' |
609 | + '10 is not > 11\n' |
610 | + '10 is not > 10\n' |
611 | + ']', |
612 | + [11, 9, 10], |
613 | + AllMatch(LessThan(10))), |
614 | + ] |
615 | + |
616 | + |
617 | def test_suite(): |
618 | from unittest import TestLoader |
619 | return TestLoader().loadTestsFromName(__name__) |
620 | |
621 | === modified file 'testtools/tests/test_runtest.py' |
622 | --- testtools/tests/test_runtest.py 2011-01-22 17:56:00 +0000 |
623 | +++ testtools/tests/test_runtest.py 2011-07-20 12:59:27 +0000 |
624 | @@ -1,4 +1,4 @@ |
625 | -# Copyright (c) 2009-2010 testtools developers. See LICENSE for details. |
626 | +# Copyright (c) 2009-2011 testtools developers. See LICENSE for details. |
627 | |
628 | """Tests for the RunTest single test execution logic.""" |
629 | |
630 | @@ -10,7 +10,7 @@ |
631 | TestResult, |
632 | ) |
633 | from testtools.matchers import MatchesException, Is, Raises |
634 | -from testtools.tests.helpers import ExtendedTestResult |
635 | +from testtools.testresult.doubles import ExtendedTestResult |
636 | |
637 | |
638 | class TestRunTest(TestCase): |
639 | |
640 | === modified file 'testtools/tests/test_testcase.py' |
641 | --- testtools/tests/test_testcase.py 2011-07-11 10:30:08 +0000 |
642 | +++ testtools/tests/test_testcase.py 2011-07-20 12:59:27 +0000 |
643 | @@ -23,12 +23,14 @@ |
644 | MatchesException, |
645 | Raises, |
646 | ) |
647 | +from testtools.testresult.doubles import ( |
648 | + Python26TestResult, |
649 | + Python27TestResult, |
650 | + ExtendedTestResult, |
651 | + ) |
652 | from testtools.tests.helpers import ( |
653 | an_exc_info, |
654 | LoggingResult, |
655 | - Python26TestResult, |
656 | - Python27TestResult, |
657 | - ExtendedTestResult, |
658 | ) |
659 | try: |
660 | exec('from __future__ import with_statement') |
661 | |
662 | === modified file 'testtools/tests/test_testresult.py' |
663 | --- testtools/tests/test_testresult.py 2011-07-01 10:50:54 +0000 |
664 | +++ testtools/tests/test_testresult.py 2011-07-20 12:59:27 +0000 |
665 | @@ -44,11 +44,14 @@ |
666 | Raises, |
667 | ) |
668 | from testtools.tests.helpers import ( |
669 | + an_exc_info, |
670 | LoggingResult, |
671 | + StackHidingFixture, |
672 | + ) |
673 | +from testtools.testresult.doubles import ( |
674 | Python26TestResult, |
675 | Python27TestResult, |
676 | ExtendedTestResult, |
677 | - an_exc_info |
678 | ) |
679 | from testtools.testresult.real import ( |
680 | _details_to_str, |
681 | @@ -369,7 +372,10 @@ |
682 | result.time(now) |
683 | self.assertEqual(now, result._now()) |
684 | |
685 | - def test_traceback_formatting(self): |
686 | + def test_traceback_formatting_without_stack_hidden(self): |
687 | + # During the testtools test run, we show our levels of the stack, |
688 | + # because we want to be able to use our test suite to debug our own |
689 | + # code. |
690 | result = self.makeResult() |
691 | test = make_erroring_test() |
692 | test.run(result) |
693 | @@ -386,6 +392,20 @@ |
694 | 'ZeroDivisionError: ...\n', |
695 | doctest.ELLIPSIS)) |
696 | |
697 | + def test_traceback_formatting_with_stack_hidden(self): |
698 | + result = self.makeResult() |
699 | + test = make_erroring_test() |
700 | + with StackHidingFixture(True): |
701 | + test.run(result) |
702 | + self.assertThat( |
703 | + result.errors[0][1], |
704 | + DocTestMatches( |
705 | + 'Traceback (most recent call last):\n' |
706 | + ' File "testtools/tests/test_testresult.py", line ..., in error\n' |
707 | + ' 1/0\n' |
708 | + 'ZeroDivisionError: ...\n', |
709 | + doctest.ELLIPSIS)) |
710 | + |
711 | |
712 | class TestMultiTestResult(TestCase): |
713 | """Tests for 'MultiTestResult'.""" |
714 | @@ -575,21 +595,18 @@ |
715 | DocTestMatches("...\nFAILED (failures=1)\n", doctest.ELLIPSIS)) |
716 | |
717 | def test_stopTestRun_shows_details(self): |
718 | - self.result.startTestRun() |
719 | - make_erroring_test().run(self.result) |
720 | - make_unexpectedly_successful_test().run(self.result) |
721 | - make_failing_test().run(self.result) |
722 | - self.reset_output() |
723 | - self.result.stopTestRun() |
724 | + with StackHidingFixture(True): |
725 | + self.result.startTestRun() |
726 | + make_erroring_test().run(self.result) |
727 | + make_unexpectedly_successful_test().run(self.result) |
728 | + make_failing_test().run(self.result) |
729 | + self.reset_output() |
730 | + self.result.stopTestRun() |
731 | self.assertThat(self.getvalue(), |
732 | DocTestMatches("""...====================================================================== |
733 | ERROR: testtools.tests.test_testresult.Test.error |
734 | ---------------------------------------------------------------------- |
735 | Traceback (most recent call last): |
736 | - File "...testtools...runtest.py", line ..., in _run_user... |
737 | - return fn(*args, **kwargs) |
738 | - File "...testtools...testcase.py", line ..., in _run_test_method |
739 | - return self._get_test_method()() |
740 | File "...testtools...tests...test_testresult.py", line ..., in error |
741 | 1/0 |
742 | ZeroDivisionError:... divi... by zero... |
743 | @@ -597,10 +614,6 @@ |
744 | FAIL: testtools.tests.test_testresult.Test.failed |
745 | ---------------------------------------------------------------------- |
746 | Traceback (most recent call last): |
747 | - File "...testtools...runtest.py", line ..., in _run_user... |
748 | - return fn(*args, **kwargs) |
749 | - File "...testtools...testcase.py", line ..., in _run_test_method |
750 | - return self._get_test_method()() |
751 | File "...testtools...tests...test_testresult.py", line ..., in failed |
752 | self.fail("yo!") |
753 | AssertionError: yo! |
754 | |
755 | === modified file 'testtools/tests/test_testsuite.py' |
756 | --- testtools/tests/test_testsuite.py 2011-01-22 17:56:00 +0000 |
757 | +++ testtools/tests/test_testsuite.py 2011-07-20 12:59:27 +0000 |
758 | @@ -1,20 +1,19 @@ |
759 | -# Copyright (c) 2009 testtools developers. See LICENSE for details. |
760 | +# Copyright (c) 2009-2011 testtools developers. See LICENSE for details. |
761 | |
762 | """Test ConcurrentTestSuite and related things.""" |
763 | |
764 | __metaclass__ = type |
765 | |
766 | -import datetime |
767 | import unittest |
768 | |
769 | +from fixtures import FunctionFixture |
770 | + |
771 | from testtools import ( |
772 | ConcurrentTestSuite, |
773 | iterate_tests, |
774 | TestCase, |
775 | ) |
776 | -from testtools.matchers import ( |
777 | - Equals, |
778 | - ) |
779 | +from testtools.testsuite import FixtureSuite |
780 | from testtools.tests.helpers import LoggingResult |
781 | |
782 | |
783 | @@ -48,6 +47,23 @@ |
784 | return tests[0], tests[1] |
785 | |
786 | |
787 | +class TestFixtureSuite(TestCase): |
788 | + |
789 | + def test_fixture_suite(self): |
790 | + log = [] |
791 | + class Sample(TestCase): |
792 | + def test_one(self): |
793 | + log.append(1) |
794 | + def test_two(self): |
795 | + log.append(2) |
796 | + fixture = FunctionFixture( |
797 | + lambda: log.append('setUp'), |
798 | + lambda fixture: log.append('tearDown')) |
799 | + suite = FixtureSuite(fixture, [Sample('test_one'), Sample('test_two')]) |
800 | + suite.run(LoggingResult([])) |
801 | + self.assertEqual(['setUp', 1, 2, 'tearDown'], log) |
802 | + |
803 | + |
804 | def test_suite(): |
805 | from unittest import TestLoader |
806 | return TestLoader().loadTestsFromName(__name__) |
807 | |
808 | === modified file 'testtools/testsuite.py' |
809 | --- testtools/testsuite.py 2011-01-22 17:56:00 +0000 |
810 | +++ testtools/testsuite.py 2011-07-20 12:59:27 +0000 |
811 | @@ -1,4 +1,4 @@ |
812 | -# Copyright (c) 2009 testtools developers. See LICENSE for details. |
813 | +# Copyright (c) 2009-2011 testtools developers. See LICENSE for details. |
814 | |
815 | """Test suites and related things.""" |
816 | |
817 | @@ -8,10 +8,10 @@ |
818 | 'iterate_tests', |
819 | ] |
820 | |
821 | -try: |
822 | - from Queue import Queue |
823 | -except ImportError: |
824 | - from queue import Queue |
825 | +from testtools.helpers import try_imports |
826 | + |
827 | +Queue = try_imports(['Queue.Queue', 'queue.Queue']) |
828 | + |
829 | import threading |
830 | import unittest |
831 | |
832 | @@ -85,3 +85,14 @@ |
833 | test.run(process_result) |
834 | finally: |
835 | queue.put(test) |
836 | + |
837 | + |
838 | +class FixtureSuite(unittest.TestSuite): |
839 | + |
840 | + def __init__(self, fixture, tests): |
841 | + super(FixtureSuite, self).__init__(tests) |
842 | + self._fixture = fixture |
843 | + |
844 | + def run(self, result): |
845 | + with self._fixture: |
846 | + super(FixtureSuite, self).run(result) |