lib/testtools/testtools/tests/test_testresult.py: Fix typos.
[nivanova/samba-autobuild/.git] / lib / testtools / testtools / tests / test_testresult.py
1 # Copyright (c) 2008-2012 testtools developers. See LICENSE for details.
2
3 """Test TestResults and related things."""
4
5 __metaclass__ = type
6
7 import codecs
8 import datetime
9 import doctest
10 import os
11 import shutil
12 import sys
13 import tempfile
14 import threading
15 from unittest import TestSuite
16 import warnings
17
18 from testtools import (
19     ExtendedToOriginalDecorator,
20     MultiTestResult,
21     PlaceHolder,
22     Tagger,
23     TestCase,
24     TestResult,
25     TestResultDecorator,
26     TestByTestResult,
27     TextTestResult,
28     ThreadsafeForwardingResult,
29     testresult,
30     )
31 from testtools.compat import (
32     _b,
33     _get_exception_encoding,
34     _r,
35     _u,
36     advance_iterator,
37     str_is_unicode,
38     StringIO,
39     )
40 from testtools.content import (
41     Content,
42     content_from_stream,
43     text_content,
44     TracebackContent,
45     )
46 from testtools.content_type import ContentType, UTF8_TEXT
47 from testtools.helpers import safe_hasattr
48 from testtools.matchers import (
49     Contains,
50     DocTestMatches,
51     Equals,
52     MatchesAny,
53     MatchesException,
54     Raises,
55     )
56 from testtools.tests.helpers import (
57     an_exc_info,
58     FullStackRunTest,
59     LoggingResult,
60     run_with_stack_hidden,
61     )
62 from testtools.testresult.doubles import (
63     Python26TestResult,
64     Python27TestResult,
65     ExtendedTestResult,
66     )
67 from testtools.testresult.real import (
68     _details_to_str,
69     _merge_tags,
70     utc,
71     )
72
73
74 def make_erroring_test():
75     class Test(TestCase):
76         def error(self):
77             1/0
78     return Test("error")
79
80
81 def make_failing_test():
82     class Test(TestCase):
83         def failed(self):
84             self.fail("yo!")
85     return Test("failed")
86
87
88 def make_mismatching_test():
89     class Test(TestCase):
90         def mismatch(self):
91             self.assertEqual(1, 2)
92     return Test("mismatch")
93
94
95 def make_unexpectedly_successful_test():
96     class Test(TestCase):
97         def succeeded(self):
98             self.expectFailure("yo!", lambda: None)
99     return Test("succeeded")
100
101
102 def make_test():
103     class Test(TestCase):
104         def test(self):
105             pass
106     return Test("test")
107
108
109 def make_exception_info(exceptionFactory, *args, **kwargs):
110     try:
111         raise exceptionFactory(*args, **kwargs)
112     except:
113         return sys.exc_info()
114
115
116 class Python26Contract(object):
117
118     def test_fresh_result_is_successful(self):
119         # A result is considered successful before any tests are run.
120         result = self.makeResult()
121         self.assertTrue(result.wasSuccessful())
122
123     def test_addError_is_failure(self):
124         # addError fails the test run.
125         result = self.makeResult()
126         result.startTest(self)
127         result.addError(self, an_exc_info)
128         result.stopTest(self)
129         self.assertFalse(result.wasSuccessful())
130
131     def test_addFailure_is_failure(self):
132         # addFailure fails the test run.
133         result = self.makeResult()
134         result.startTest(self)
135         result.addFailure(self, an_exc_info)
136         result.stopTest(self)
137         self.assertFalse(result.wasSuccessful())
138
139     def test_addSuccess_is_success(self):
140         # addSuccess does not fail the test run.
141         result = self.makeResult()
142         result.startTest(self)
143         result.addSuccess(self)
144         result.stopTest(self)
145         self.assertTrue(result.wasSuccessful())
146
147     def test_stop_sets_shouldStop(self):
148         result = self.makeResult()
149         result.stop()
150         self.assertTrue(result.shouldStop)
151
152
153 class Python27Contract(Python26Contract):
154
155     def test_addExpectedFailure(self):
156         # Calling addExpectedFailure(test, exc_info) completes ok.
157         result = self.makeResult()
158         result.startTest(self)
159         result.addExpectedFailure(self, an_exc_info)
160
161     def test_addExpectedFailure_is_success(self):
162         # addExpectedFailure does not fail the test run.
163         result = self.makeResult()
164         result.startTest(self)
165         result.addExpectedFailure(self, an_exc_info)
166         result.stopTest(self)
167         self.assertTrue(result.wasSuccessful())
168
169     def test_addSkipped(self):
170         # Calling addSkip(test, reason) completes ok.
171         result = self.makeResult()
172         result.startTest(self)
173         result.addSkip(self, _u("Skipped for some reason"))
174
175     def test_addSkip_is_success(self):
176         # addSkip does not fail the test run.
177         result = self.makeResult()
178         result.startTest(self)
179         result.addSkip(self, _u("Skipped for some reason"))
180         result.stopTest(self)
181         self.assertTrue(result.wasSuccessful())
182
183     def test_addUnexpectedSuccess(self):
184         # Calling addUnexpectedSuccess(test) completes ok.
185         result = self.makeResult()
186         result.startTest(self)
187         result.addUnexpectedSuccess(self)
188
189     def test_addUnexpectedSuccess_was_successful(self):
190         # addUnexpectedSuccess does not fail the test run in Python 2.7.
191         result = self.makeResult()
192         result.startTest(self)
193         result.addUnexpectedSuccess(self)
194         result.stopTest(self)
195         self.assertTrue(result.wasSuccessful())
196
197     def test_startStopTestRun(self):
198         # Calling startTestRun completes ok.
199         result = self.makeResult()
200         result.startTestRun()
201         result.stopTestRun()
202
203     def test_failfast(self):
204         result = self.makeResult()
205         result.failfast = True
206         class Failing(TestCase):
207             def test_a(self):
208                 self.fail('a')
209             def test_b(self):
210                 self.fail('b')
211         TestSuite([Failing('test_a'), Failing('test_b')]).run(result)
212         self.assertEqual(1, result.testsRun)
213
214
215 class TagsContract(Python27Contract):
216     """Tests to ensure correct tagging behaviour.
217
218     See the subunit docs for guidelines on how this is supposed to work.
219     """
220
221     def test_no_tags_by_default(self):
222         # Results initially have no tags.
223         result = self.makeResult()
224         self.assertEqual(frozenset(), result.current_tags)
225
226     def test_adding_tags(self):
227         # Tags are added using 'tags' and thus become visible in
228         # 'current_tags'.
229         result = self.makeResult()
230         result.tags(set(['foo']), set())
231         self.assertEqual(set(['foo']), result.current_tags)
232
233     def test_removing_tags(self):
234         # Tags are removed using 'tags'.
235         result = self.makeResult()
236         result.tags(set(['foo']), set())
237         result.tags(set(), set(['foo']))
238         self.assertEqual(set(), result.current_tags)
239
240     def test_startTestRun_resets_tags(self):
241         # startTestRun makes a new test run, and thus clears all the tags.
242         result = self.makeResult()
243         result.tags(set(['foo']), set())
244         result.startTestRun()
245         self.assertEqual(set(), result.current_tags)
246
247     def test_add_tags_within_test(self):
248         # Tags can be added after a test has run.
249         result = self.makeResult()
250         result.startTestRun()
251         result.tags(set(['foo']), set())
252         result.startTest(self)
253         result.tags(set(['bar']), set())
254         self.assertEqual(set(['foo', 'bar']), result.current_tags)
255
256     def test_tags_added_in_test_are_reverted(self):
257         # Tags added during a test run are then reverted once that test has
258         # finished.
259         result = self.makeResult()
260         result.startTestRun()
261         result.tags(set(['foo']), set())
262         result.startTest(self)
263         result.tags(set(['bar']), set())
264         result.addSuccess(self)
265         result.stopTest(self)
266         self.assertEqual(set(['foo']), result.current_tags)
267
268     def test_tags_removed_in_test(self):
269         # Tags can be removed during tests.
270         result = self.makeResult()
271         result.startTestRun()
272         result.tags(set(['foo']), set())
273         result.startTest(self)
274         result.tags(set(), set(['foo']))
275         self.assertEqual(set(), result.current_tags)
276
277     def test_tags_removed_in_test_are_restored(self):
278         # Tags removed during tests are restored once that test has finished.
279         result = self.makeResult()
280         result.startTestRun()
281         result.tags(set(['foo']), set())
282         result.startTest(self)
283         result.tags(set(), set(['foo']))
284         result.addSuccess(self)
285         result.stopTest(self)
286         self.assertEqual(set(['foo']), result.current_tags)
287
288
289 class DetailsContract(TagsContract):
290     """Tests for the details API of TestResults."""
291
292     def test_addExpectedFailure_details(self):
293         # Calling addExpectedFailure(test, details=xxx) completes ok.
294         result = self.makeResult()
295         result.startTest(self)
296         result.addExpectedFailure(self, details={})
297
298     def test_addError_details(self):
299         # Calling addError(test, details=xxx) completes ok.
300         result = self.makeResult()
301         result.startTest(self)
302         result.addError(self, details={})
303
304     def test_addFailure_details(self):
305         # Calling addFailure(test, details=xxx) completes ok.
306         result = self.makeResult()
307         result.startTest(self)
308         result.addFailure(self, details={})
309
310     def test_addSkipped_details(self):
311         # Calling addSkip(test, reason) completes ok.
312         result = self.makeResult()
313         result.startTest(self)
314         result.addSkip(self, details={})
315
316     def test_addUnexpectedSuccess_details(self):
317         # Calling addUnexpectedSuccess(test) completes ok.
318         result = self.makeResult()
319         result.startTest(self)
320         result.addUnexpectedSuccess(self, details={})
321
322     def test_addSuccess_details(self):
323         # Calling addSuccess(test) completes ok.
324         result = self.makeResult()
325         result.startTest(self)
326         result.addSuccess(self, details={})
327
328
329 class FallbackContract(DetailsContract):
330     """When we fallback we take our policy choice to map calls.
331
332     For instance, we map unexpectedSuccess to an error code, not to success.
333     """
334
335     def test_addUnexpectedSuccess_was_successful(self):
336         # addUnexpectedSuccess fails test run in testtools.
337         result = self.makeResult()
338         result.startTest(self)
339         result.addUnexpectedSuccess(self)
340         result.stopTest(self)
341         self.assertFalse(result.wasSuccessful())
342
343
344 class StartTestRunContract(FallbackContract):
345     """Defines the contract for testtools policy choices.
346
347     That is things which are not simply extensions to unittest but choices we
348     have made differently.
349     """
350
351     def test_startTestRun_resets_unexpected_success(self):
352         result = self.makeResult()
353         result.startTest(self)
354         result.addUnexpectedSuccess(self)
355         result.stopTest(self)
356         result.startTestRun()
357         self.assertTrue(result.wasSuccessful())
358
359     def test_startTestRun_resets_failure(self):
360         result = self.makeResult()
361         result.startTest(self)
362         result.addFailure(self, an_exc_info)
363         result.stopTest(self)
364         result.startTestRun()
365         self.assertTrue(result.wasSuccessful())
366
367     def test_startTestRun_resets_errors(self):
368         result = self.makeResult()
369         result.startTest(self)
370         result.addError(self, an_exc_info)
371         result.stopTest(self)
372         result.startTestRun()
373         self.assertTrue(result.wasSuccessful())
374
375
376 class TestTestResultContract(TestCase, StartTestRunContract):
377
378     run_test_with = FullStackRunTest
379
380     def makeResult(self):
381         return TestResult()
382
383
384 class TestMultiTestResultContract(TestCase, StartTestRunContract):
385
386     run_test_with = FullStackRunTest
387
388     def makeResult(self):
389         return MultiTestResult(TestResult(), TestResult())
390
391
392 class TestTextTestResultContract(TestCase, StartTestRunContract):
393
394     run_test_with = FullStackRunTest
395
396     def makeResult(self):
397         return TextTestResult(StringIO())
398
399
400 class TestThreadSafeForwardingResultContract(TestCase, StartTestRunContract):
401
402     run_test_with = FullStackRunTest
403
404     def makeResult(self):
405         result_semaphore = threading.Semaphore(1)
406         target = TestResult()
407         return ThreadsafeForwardingResult(target, result_semaphore)
408
409
410 class TestExtendedTestResultContract(TestCase, StartTestRunContract):
411
412     def makeResult(self):
413         return ExtendedTestResult()
414
415
416 class TestPython26TestResultContract(TestCase, Python26Contract):
417
418     def makeResult(self):
419         return Python26TestResult()
420
421
422 class TestAdaptedPython26TestResultContract(TestCase, FallbackContract):
423
424     def makeResult(self):
425         return ExtendedToOriginalDecorator(Python26TestResult())
426
427
428 class TestPython27TestResultContract(TestCase, Python27Contract):
429
430     def makeResult(self):
431         return Python27TestResult()
432
433
434 class TestAdaptedPython27TestResultContract(TestCase, DetailsContract):
435
436     def makeResult(self):
437         return ExtendedToOriginalDecorator(Python27TestResult())
438
439
440 class TestTestResultDecoratorContract(TestCase, StartTestRunContract):
441
442     run_test_with = FullStackRunTest
443
444     def makeResult(self):
445         return TestResultDecorator(TestResult())
446
447
448 class TestTestResult(TestCase):
449     """Tests for 'TestResult'."""
450
451     run_tests_with = FullStackRunTest
452
453     def makeResult(self):
454         """Make an arbitrary result for testing."""
455         return TestResult()
456
457     def test_addSkipped(self):
458         # Calling addSkip on a TestResult records the test that was skipped in
459         # its skip_reasons dict.
460         result = self.makeResult()
461         result.addSkip(self, _u("Skipped for some reason"))
462         self.assertEqual({_u("Skipped for some reason"):[self]},
463             result.skip_reasons)
464         result.addSkip(self, _u("Skipped for some reason"))
465         self.assertEqual({_u("Skipped for some reason"):[self, self]},
466             result.skip_reasons)
467         result.addSkip(self, _u("Skipped for another reason"))
468         self.assertEqual({_u("Skipped for some reason"):[self, self],
469             _u("Skipped for another reason"):[self]},
470             result.skip_reasons)
471
472     def test_now_datetime_now(self):
473         result = self.makeResult()
474         olddatetime = testresult.real.datetime
475         def restore():
476             testresult.real.datetime = olddatetime
477         self.addCleanup(restore)
478         class Module:
479             pass
480         now = datetime.datetime.now(utc)
481         stubdatetime = Module()
482         stubdatetime.datetime = Module()
483         stubdatetime.datetime.now = lambda tz: now
484         testresult.real.datetime = stubdatetime
485         # Calling _now() looks up the time.
486         self.assertEqual(now, result._now())
487         then = now + datetime.timedelta(0, 1)
488         # Set an explicit datetime, which gets returned from then on.
489         result.time(then)
490         self.assertNotEqual(now, result._now())
491         self.assertEqual(then, result._now())
492         # go back to looking it up.
493         result.time(None)
494         self.assertEqual(now, result._now())
495
496     def test_now_datetime_time(self):
497         result = self.makeResult()
498         now = datetime.datetime.now(utc)
499         result.time(now)
500         self.assertEqual(now, result._now())
501
502     def test_traceback_formatting_without_stack_hidden(self):
503         # During the testtools test run, we show our levels of the stack,
504         # because we want to be able to use our test suite to debug our own
505         # code.
506         result = self.makeResult()
507         test = make_erroring_test()
508         test.run(result)
509         self.assertThat(
510             result.errors[0][1],
511             DocTestMatches(
512                 'Traceback (most recent call last):\n'
513                 '  File "...testtools...runtest.py", line ..., in _run_user\n'
514                 '    return fn(*args, **kwargs)\n'
515                 '  File "...testtools...testcase.py", line ..., in _run_test_method\n'
516                 '    return self._get_test_method()()\n'
517                 '  File "...testtools...tests...test_testresult.py", line ..., in error\n'
518                 '    1/0\n'
519                 'ZeroDivisionError: ...\n',
520                 doctest.ELLIPSIS | doctest.REPORT_UDIFF))
521
522     def test_traceback_formatting_with_stack_hidden(self):
523         result = self.makeResult()
524         test = make_erroring_test()
525         run_with_stack_hidden(True, test.run, result)
526         self.assertThat(
527             result.errors[0][1],
528             DocTestMatches(
529                 'Traceback (most recent call last):\n'
530                 '  File "...testtools...tests...test_testresult.py", line ..., in error\n'
531                 '    1/0\n'
532                 'ZeroDivisionError: ...\n',
533                 doctest.ELLIPSIS))
534
535     def test_traceback_formatting_with_stack_hidden_mismatch(self):
536         result = self.makeResult()
537         test = make_mismatching_test()
538         run_with_stack_hidden(True, test.run, result)
539         self.assertThat(
540             result.failures[0][1],
541             DocTestMatches(
542                 'Traceback (most recent call last):\n'
543                 '  File "...testtools...tests...test_testresult.py", line ..., in mismatch\n'
544                 '    self.assertEqual(1, 2)\n'
545                 '...MismatchError: 1 != 2\n',
546                 doctest.ELLIPSIS))
547
548     def test_exc_info_to_unicode(self):
549         # subunit upcalls to TestResult._exc_info_to_unicode, so we need to
550         # make sure that it's there.
551         #
552         # See <https://bugs.launchpad.net/testtools/+bug/929063>.
553         test = make_erroring_test()
554         exc_info = make_exception_info(RuntimeError, "foo")
555         result = self.makeResult()
556         text_traceback = result._exc_info_to_unicode(exc_info, test)
557         self.assertEqual(
558             TracebackContent(exc_info, test).as_text(), text_traceback)
559
560
561 class TestMultiTestResult(TestCase):
562     """Tests for 'MultiTestResult'."""
563
564     def setUp(self):
565         super(TestMultiTestResult, self).setUp()
566         self.result1 = LoggingResult([])
567         self.result2 = LoggingResult([])
568         self.multiResult = MultiTestResult(self.result1, self.result2)
569
570     def assertResultLogsEqual(self, expectedEvents):
571         """Assert that our test results have received the expected events."""
572         self.assertEqual(expectedEvents, self.result1._events)
573         self.assertEqual(expectedEvents, self.result2._events)
574
575     def test_repr(self):
576         self.assertEqual(
577             '<MultiTestResult (%r, %r)>' % (
578                 ExtendedToOriginalDecorator(self.result1),
579                 ExtendedToOriginalDecorator(self.result2)),
580             repr(self.multiResult))
581
582     def test_empty(self):
583         # Initializing a `MultiTestResult` doesn't do anything to its
584         # `TestResult`s.
585         self.assertResultLogsEqual([])
586
587     def test_failfast_get(self):
588         # Reading reads from the first one - arbitrary choice.
589         self.assertEqual(False, self.multiResult.failfast)
590         self.result1.failfast = True
591         self.assertEqual(True, self.multiResult.failfast)
592
593     def test_failfast_set(self):
594         # Writing writes to all.
595         self.multiResult.failfast = True
596         self.assertEqual(True, self.result1.failfast)
597         self.assertEqual(True, self.result2.failfast)
598
599     def test_shouldStop(self):
600         self.assertFalse(self.multiResult.shouldStop)
601         self.result2.stop()
602         # NB: result1 is not stopped: MultiTestResult has to combine the
603         # values.
604         self.assertTrue(self.multiResult.shouldStop)
605
606     def test_startTest(self):
607         # Calling `startTest` on a `MultiTestResult` calls `startTest` on all
608         # its `TestResult`s.
609         self.multiResult.startTest(self)
610         self.assertResultLogsEqual([('startTest', self)])
611
612     def test_stop(self):
613         self.assertFalse(self.multiResult.shouldStop)
614         self.multiResult.stop()
615         self.assertResultLogsEqual(['stop'])
616
617     def test_stopTest(self):
618         # Calling `stopTest` on a `MultiTestResult` calls `stopTest` on all
619         # its `TestResult`s.
620         self.multiResult.stopTest(self)
621         self.assertResultLogsEqual([('stopTest', self)])
622
623     def test_addSkipped(self):
624         # Calling `addSkip` on a `MultiTestResult` calls addSkip on its
625         # results.
626         reason = _u("Skipped for some reason")
627         self.multiResult.addSkip(self, reason)
628         self.assertResultLogsEqual([('addSkip', self, reason)])
629
630     def test_addSuccess(self):
631         # Calling `addSuccess` on a `MultiTestResult` calls `addSuccess` on
632         # all its `TestResult`s.
633         self.multiResult.addSuccess(self)
634         self.assertResultLogsEqual([('addSuccess', self)])
635
636     def test_done(self):
637         # Calling `done` on a `MultiTestResult` calls `done` on all its
638         # `TestResult`s.
639         self.multiResult.done()
640         self.assertResultLogsEqual([('done')])
641
642     def test_addFailure(self):
643         # Calling `addFailure` on a `MultiTestResult` calls `addFailure` on
644         # all its `TestResult`s.
645         exc_info = make_exception_info(AssertionError, 'failure')
646         self.multiResult.addFailure(self, exc_info)
647         self.assertResultLogsEqual([('addFailure', self, exc_info)])
648
649     def test_addError(self):
650         # Calling `addError` on a `MultiTestResult` calls `addError` on all
651         # its `TestResult`s.
652         exc_info = make_exception_info(RuntimeError, 'error')
653         self.multiResult.addError(self, exc_info)
654         self.assertResultLogsEqual([('addError', self, exc_info)])
655
656     def test_startTestRun(self):
657         # Calling `startTestRun` on a `MultiTestResult` forwards to all its
658         # `TestResult`s.
659         self.multiResult.startTestRun()
660         self.assertResultLogsEqual([('startTestRun')])
661
662     def test_stopTestRun(self):
663         # Calling `stopTestRun` on a `MultiTestResult` forwards to all its
664         # `TestResult`s.
665         self.multiResult.stopTestRun()
666         self.assertResultLogsEqual([('stopTestRun')])
667
668     def test_stopTestRun_returns_results(self):
669         # `MultiTestResult.stopTestRun` returns a tuple of all of the return
670         # values the `stopTestRun`s that it forwards to.
671         class Result(LoggingResult):
672             def stopTestRun(self):
673                 super(Result, self).stopTestRun()
674                 return 'foo'
675         multi_result = MultiTestResult(Result([]), Result([]))
676         result = multi_result.stopTestRun()
677         self.assertEqual(('foo', 'foo'), result)
678
679     def test_tags(self):
680         # Calling `tags` on a `MultiTestResult` calls `tags` on all its
681         # `TestResult`s.
682         added_tags = set(['foo', 'bar'])
683         removed_tags = set(['eggs'])
684         self.multiResult.tags(added_tags, removed_tags)
685         self.assertResultLogsEqual([('tags', added_tags, removed_tags)])
686
687     def test_time(self):
688         # the time call is dispatched, not eaten by the base class
689         self.multiResult.time('foo')
690         self.assertResultLogsEqual([('time', 'foo')])
691
692
693 class TestTextTestResult(TestCase):
694     """Tests for 'TextTestResult'."""
695
696     def setUp(self):
697         super(TestTextTestResult, self).setUp()
698         self.result = TextTestResult(StringIO())
699
700     def getvalue(self):
701         return self.result.stream.getvalue()
702
703     def test__init_sets_stream(self):
704         result = TextTestResult("fp")
705         self.assertEqual("fp", result.stream)
706
707     def reset_output(self):
708         self.result.stream = StringIO()
709
710     def test_startTestRun(self):
711         self.result.startTestRun()
712         self.assertEqual("Tests running...\n", self.getvalue())
713
714     def test_stopTestRun_count_many(self):
715         test = make_test()
716         self.result.startTestRun()
717         self.result.startTest(test)
718         self.result.stopTest(test)
719         self.result.startTest(test)
720         self.result.stopTest(test)
721         self.result.stream = StringIO()
722         self.result.stopTestRun()
723         self.assertThat(self.getvalue(),
724             DocTestMatches("\nRan 2 tests in ...s\n...", doctest.ELLIPSIS))
725
726     def test_stopTestRun_count_single(self):
727         test = make_test()
728         self.result.startTestRun()
729         self.result.startTest(test)
730         self.result.stopTest(test)
731         self.reset_output()
732         self.result.stopTestRun()
733         self.assertThat(self.getvalue(),
734             DocTestMatches("\nRan 1 test in ...s\nOK\n", doctest.ELLIPSIS))
735
736     def test_stopTestRun_count_zero(self):
737         self.result.startTestRun()
738         self.reset_output()
739         self.result.stopTestRun()
740         self.assertThat(self.getvalue(),
741             DocTestMatches("\nRan 0 tests in ...s\nOK\n", doctest.ELLIPSIS))
742
743     def test_stopTestRun_current_time(self):
744         test = make_test()
745         now = datetime.datetime.now(utc)
746         self.result.time(now)
747         self.result.startTestRun()
748         self.result.startTest(test)
749         now = now + datetime.timedelta(0, 0, 0, 1)
750         self.result.time(now)
751         self.result.stopTest(test)
752         self.reset_output()
753         self.result.stopTestRun()
754         self.assertThat(self.getvalue(),
755             DocTestMatches("... in 0.001s\n...", doctest.ELLIPSIS))
756
757     def test_stopTestRun_successful(self):
758         self.result.startTestRun()
759         self.result.stopTestRun()
760         self.assertThat(self.getvalue(),
761             DocTestMatches("...\nOK\n", doctest.ELLIPSIS))
762
763     def test_stopTestRun_not_successful_failure(self):
764         test = make_failing_test()
765         self.result.startTestRun()
766         test.run(self.result)
767         self.result.stopTestRun()
768         self.assertThat(self.getvalue(),
769             DocTestMatches("...\nFAILED (failures=1)\n", doctest.ELLIPSIS))
770
771     def test_stopTestRun_not_successful_error(self):
772         test = make_erroring_test()
773         self.result.startTestRun()
774         test.run(self.result)
775         self.result.stopTestRun()
776         self.assertThat(self.getvalue(),
777             DocTestMatches("...\nFAILED (failures=1)\n", doctest.ELLIPSIS))
778
779     def test_stopTestRun_not_successful_unexpected_success(self):
780         test = make_unexpectedly_successful_test()
781         self.result.startTestRun()
782         test.run(self.result)
783         self.result.stopTestRun()
784         self.assertThat(self.getvalue(),
785             DocTestMatches("...\nFAILED (failures=1)\n", doctest.ELLIPSIS))
786
787     def test_stopTestRun_shows_details(self):
788         def run_tests():
789             self.result.startTestRun()
790             make_erroring_test().run(self.result)
791             make_unexpectedly_successful_test().run(self.result)
792             make_failing_test().run(self.result)
793             self.reset_output()
794             self.result.stopTestRun()
795         run_with_stack_hidden(True, run_tests)
796         self.assertThat(self.getvalue(),
797             DocTestMatches("""...======================================================================
798 ERROR: testtools.tests.test_testresult.Test.error
799 ----------------------------------------------------------------------
800 Traceback (most recent call last):
801   File "...testtools...tests...test_testresult.py", line ..., in error
802     1/0
803 ZeroDivisionError:... divi... by zero...
804 ======================================================================
805 FAIL: testtools.tests.test_testresult.Test.failed
806 ----------------------------------------------------------------------
807 Traceback (most recent call last):
808   File "...testtools...tests...test_testresult.py", line ..., in failed
809     self.fail("yo!")
810 AssertionError: yo!
811 ======================================================================
812 UNEXPECTED SUCCESS: testtools.tests.test_testresult.Test.succeeded
813 ----------------------------------------------------------------------
814 ...""", doctest.ELLIPSIS | doctest.REPORT_NDIFF))
815
816
817 class TestThreadSafeForwardingResult(TestCase):
818     """Tests for `TestThreadSafeForwardingResult`."""
819
820     def make_results(self, n):
821         events = []
822         target = LoggingResult(events)
823         semaphore = threading.Semaphore(1)
824         return [
825             ThreadsafeForwardingResult(target, semaphore)
826             for i in range(n)], events
827
828     def test_nonforwarding_methods(self):
829         # startTest and stopTest are not forwarded because they need to be
830         # batched.
831         [result], events = self.make_results(1)
832         result.startTest(self)
833         result.stopTest(self)
834         self.assertEqual([], events)
835
836     def test_tags_not_forwarded(self):
837         # Tags need to be batched for each test, so they aren't forwarded
838         # until a test runs.
839         [result], events = self.make_results(1)
840         result.tags(set(['foo']), set(['bar']))
841         self.assertEqual([], events)
842
843     def test_global_tags_simple(self):
844         # Tags specified outside of a test result are global. When a test's
845         # results are finally forwarded, we send through these global tags
846         # *as* test specific tags, because as a multiplexer there should be no
847         # way for a global tag on an input stream to affect tests from other
848         # streams - we can just always issue test local tags.
849         [result], events = self.make_results(1)
850         result.tags(set(['foo']), set())
851         result.time(1)
852         result.startTest(self)
853         result.time(2)
854         result.addSuccess(self)
855         self.assertEqual(
856             [('time', 1),
857              ('startTest', self),
858              ('time', 2),
859              ('tags', set(['foo']), set()),
860              ('addSuccess', self),
861              ('stopTest', self),
862              ], events)
863
864     def test_global_tags_complex(self):
865         # Multiple calls to tags() in a global context are buffered until the
866         # next test completes and are issued as part of of the test context,
867         # because they cannot be issued until the output result is locked.
868         # The sample data shows them being merged together, this is, strictly
869         # speaking incidental - they could be issued separately (in-order) and
870         # still be legitimate.
871         [result], events = self.make_results(1)
872         result.tags(set(['foo', 'bar']), set(['baz', 'qux']))
873         result.tags(set(['cat', 'qux']), set(['bar', 'dog']))
874         result.time(1)
875         result.startTest(self)
876         result.time(2)
877         result.addSuccess(self)
878         self.assertEqual(
879             [('time', 1),
880              ('startTest', self),
881              ('time', 2),
882              ('tags', set(['cat', 'foo', 'qux']), set(['dog', 'bar', 'baz'])),
883              ('addSuccess', self),
884              ('stopTest', self),
885              ], events)
886
887     def test_local_tags(self):
888         # Any tags set within a test context are forwarded in that test
889         # context when the result is finally forwarded.  This means that the
890         # tags for the test are part of the atomic message communicating
891         # everything about that test.
892         [result], events = self.make_results(1)
893         result.time(1)
894         result.startTest(self)
895         result.tags(set(['foo']), set([]))
896         result.tags(set(), set(['bar']))
897         result.time(2)
898         result.addSuccess(self)
899         self.assertEqual(
900             [('time', 1),
901              ('startTest', self),
902              ('time', 2),
903              ('tags', set(['foo']), set(['bar'])),
904              ('addSuccess', self),
905              ('stopTest', self),
906              ], events)
907
908     def test_local_tags_dont_leak(self):
909         # A tag set during a test is local to that test and is not set during
910         # the tests that follow.
911         [result], events = self.make_results(1)
912         a, b = PlaceHolder('a'), PlaceHolder('b')
913         result.time(1)
914         result.startTest(a)
915         result.tags(set(['foo']), set([]))
916         result.time(2)
917         result.addSuccess(a)
918         result.stopTest(a)
919         result.time(3)
920         result.startTest(b)
921         result.time(4)
922         result.addSuccess(b)
923         result.stopTest(b)
924         self.assertEqual(
925             [('time', 1),
926              ('startTest', a),
927              ('time', 2),
928              ('tags', set(['foo']), set()),
929              ('addSuccess', a),
930              ('stopTest', a),
931              ('time', 3),
932              ('startTest', b),
933              ('time', 4),
934              ('addSuccess', b),
935              ('stopTest', b),
936              ], events)
937
938     def test_startTestRun(self):
939         # Calls to startTestRun are not batched, because we are only
940         # interested in sending tests atomically, not the whole run.
941         [result1, result2], events = self.make_results(2)
942         result1.startTestRun()
943         result2.startTestRun()
944         self.assertEqual(["startTestRun", "startTestRun"], events)
945
946     def test_stopTestRun(self):
947         # Calls to stopTestRun are not batched, because we are only
948         # interested in sending tests atomically, not the whole run.
949         [result1, result2], events = self.make_results(2)
950         result1.stopTestRun()
951         result2.stopTestRun()
952         self.assertEqual(["stopTestRun", "stopTestRun"], events)
953
954     def test_forward_addError(self):
955         # Once we receive an addError event, we forward all of the events for
956         # that test, as we now know that test is complete.
957         [result], events = self.make_results(1)
958         exc_info = make_exception_info(RuntimeError, 'error')
959         start_time = datetime.datetime.utcfromtimestamp(1.489)
960         end_time = datetime.datetime.utcfromtimestamp(51.476)
961         result.time(start_time)
962         result.startTest(self)
963         result.time(end_time)
964         result.addError(self, exc_info)
965         self.assertEqual([
966             ('time', start_time),
967             ('startTest', self),
968             ('time', end_time),
969             ('addError', self, exc_info),
970             ('stopTest', self),
971             ], events)
972
973     def test_forward_addFailure(self):
974         # Once we receive an addFailure event, we forward all of the events
975         # for that test, as we now know that test is complete.
976         [result], events = self.make_results(1)
977         exc_info = make_exception_info(AssertionError, 'failure')
978         start_time = datetime.datetime.utcfromtimestamp(2.489)
979         end_time = datetime.datetime.utcfromtimestamp(3.476)
980         result.time(start_time)
981         result.startTest(self)
982         result.time(end_time)
983         result.addFailure(self, exc_info)
984         self.assertEqual([
985             ('time', start_time),
986             ('startTest', self),
987             ('time', end_time),
988             ('addFailure', self, exc_info),
989             ('stopTest', self),
990             ], events)
991
992     def test_forward_addSkip(self):
993         # Once we receive an addSkip event, we forward all of the events for
994         # that test, as we now know that test is complete.
995         [result], events = self.make_results(1)
996         reason = _u("Skipped for some reason")
997         start_time = datetime.datetime.utcfromtimestamp(4.489)
998         end_time = datetime.datetime.utcfromtimestamp(5.476)
999         result.time(start_time)
1000         result.startTest(self)
1001         result.time(end_time)
1002         result.addSkip(self, reason)
1003         self.assertEqual([
1004             ('time', start_time),
1005             ('startTest', self),
1006             ('time', end_time),
1007             ('addSkip', self, reason),
1008             ('stopTest', self),
1009             ], events)
1010
1011     def test_forward_addSuccess(self):
1012         # Once we receive an addSuccess event, we forward all of the events
1013         # for that test, as we now know that test is complete.
1014         [result], events = self.make_results(1)
1015         start_time = datetime.datetime.utcfromtimestamp(6.489)
1016         end_time = datetime.datetime.utcfromtimestamp(7.476)
1017         result.time(start_time)
1018         result.startTest(self)
1019         result.time(end_time)
1020         result.addSuccess(self)
1021         self.assertEqual([
1022             ('time', start_time),
1023             ('startTest', self),
1024             ('time', end_time),
1025             ('addSuccess', self),
1026             ('stopTest', self),
1027             ], events)
1028
1029     def test_only_one_test_at_a_time(self):
1030         # Even if there are multiple ThreadsafeForwardingResults forwarding to
1031         # the same target result, the target result only receives the complete
1032         # events for one test at a time.
1033         [result1, result2], events = self.make_results(2)
1034         test1, test2 = self, make_test()
1035         start_time1 = datetime.datetime.utcfromtimestamp(1.489)
1036         end_time1 = datetime.datetime.utcfromtimestamp(2.476)
1037         start_time2 = datetime.datetime.utcfromtimestamp(3.489)
1038         end_time2 = datetime.datetime.utcfromtimestamp(4.489)
1039         result1.time(start_time1)
1040         result2.time(start_time2)
1041         result1.startTest(test1)
1042         result2.startTest(test2)
1043         result1.time(end_time1)
1044         result2.time(end_time2)
1045         result2.addSuccess(test2)
1046         result1.addSuccess(test1)
1047         self.assertEqual([
1048             # test2 finishes first, and so is flushed first.
1049             ('time', start_time2),
1050             ('startTest', test2),
1051             ('time', end_time2),
1052             ('addSuccess', test2),
1053             ('stopTest', test2),
1054             # test1 finishes next, and thus follows.
1055             ('time', start_time1),
1056             ('startTest', test1),
1057             ('time', end_time1),
1058             ('addSuccess', test1),
1059             ('stopTest', test1),
1060             ], events)
1061
1062
1063 class TestMergeTags(TestCase):
1064
1065     def test_merge_unseen_gone_tag(self):
1066         # If an incoming "gone" tag isn't currently tagged one way or the
1067         # other, add it to the "gone" tags.
1068         current_tags = set(['present']), set(['missing'])
1069         changing_tags = set(), set(['going'])
1070         expected = set(['present']), set(['missing', 'going'])
1071         self.assertEqual(
1072             expected, _merge_tags(current_tags, changing_tags))
1073
1074     def test_merge_incoming_gone_tag_with_current_new_tag(self):
1075         # If one of the incoming "gone" tags is one of the existing "new"
1076         # tags, then it overrides the "new" tag, leaving it marked as "gone".
1077         current_tags = set(['present', 'going']), set(['missing'])
1078         changing_tags = set(), set(['going'])
1079         expected = set(['present']), set(['missing', 'going'])
1080         self.assertEqual(
1081             expected, _merge_tags(current_tags, changing_tags))
1082
1083     def test_merge_unseen_new_tag(self):
1084         current_tags = set(['present']), set(['missing'])
1085         changing_tags = set(['coming']), set()
1086         expected = set(['coming', 'present']), set(['missing'])
1087         self.assertEqual(
1088             expected, _merge_tags(current_tags, changing_tags))
1089
1090     def test_merge_incoming_new_tag_with_current_gone_tag(self):
1091         # If one of the incoming "new" tags is currently marked as "gone",
1092         # then it overrides the "gone" tag, leaving it marked as "new".
1093         current_tags = set(['present']), set(['coming', 'missing'])
1094         changing_tags = set(['coming']), set()
1095         expected = set(['coming', 'present']), set(['missing'])
1096         self.assertEqual(
1097             expected, _merge_tags(current_tags, changing_tags))
1098
1099
1100 class TestExtendedToOriginalResultDecoratorBase(TestCase):
1101
1102     def make_26_result(self):
1103         self.result = Python26TestResult()
1104         self.make_converter()
1105
1106     def make_27_result(self):
1107         self.result = Python27TestResult()
1108         self.make_converter()
1109
1110     def make_converter(self):
1111         self.converter = ExtendedToOriginalDecorator(self.result)
1112
1113     def make_extended_result(self):
1114         self.result = ExtendedTestResult()
1115         self.make_converter()
1116
1117     def check_outcome_details(self, outcome):
1118         """Call an outcome with a details dict to be passed through."""
1119         # This dict is /not/ convertible - thats deliberate, as it should
1120         # not hit the conversion code path.
1121         details = {'foo': 'bar'}
1122         getattr(self.converter, outcome)(self, details=details)
1123         self.assertEqual([(outcome, self, details)], self.result._events)
1124
1125     def get_details_and_string(self):
1126         """Get a details dict and expected string."""
1127         text1 = lambda: [_b("1\n2\n")]
1128         text2 = lambda: [_b("3\n4\n")]
1129         bin1 = lambda: [_b("5\n")]
1130         details = {'text 1': Content(ContentType('text', 'plain'), text1),
1131             'text 2': Content(ContentType('text', 'strange'), text2),
1132             'bin 1': Content(ContentType('application', 'binary'), bin1)}
1133         return (details,
1134                 ("Binary content:\n"
1135                  "  bin 1 (application/binary)\n"
1136                  "\n"
1137                  "text 1: {{{\n"
1138                  "1\n"
1139                  "2\n"
1140                  "}}}\n"
1141                  "\n"
1142                  "text 2: {{{\n"
1143                  "3\n"
1144                  "4\n"
1145                  "}}}\n"))
1146
1147     def check_outcome_details_to_exec_info(self, outcome, expected=None):
1148         """Call an outcome with a details dict to be made into exc_info."""
1149         # The conversion is a done using RemoteError and the string contents
1150         # of the text types in the details dict.
1151         if not expected:
1152             expected = outcome
1153         details, err_str = self.get_details_and_string()
1154         getattr(self.converter, outcome)(self, details=details)
1155         err = self.converter._details_to_exc_info(details)
1156         self.assertEqual([(expected, self, err)], self.result._events)
1157
1158     def check_outcome_details_to_nothing(self, outcome, expected=None):
1159         """Call an outcome with a details dict to be swallowed."""
1160         if not expected:
1161             expected = outcome
1162         details = {'foo': 'bar'}
1163         getattr(self.converter, outcome)(self, details=details)
1164         self.assertEqual([(expected, self)], self.result._events)
1165
1166     def check_outcome_details_to_string(self, outcome):
1167         """Call an outcome with a details dict to be stringified."""
1168         details, err_str = self.get_details_and_string()
1169         getattr(self.converter, outcome)(self, details=details)
1170         self.assertEqual([(outcome, self, err_str)], self.result._events)
1171
1172     def check_outcome_details_to_arg(self, outcome, arg, extra_detail=None):
1173         """Call an outcome with a details dict to have an arg extracted."""
1174         details, _ = self.get_details_and_string()
1175         if extra_detail:
1176             details.update(extra_detail)
1177         getattr(self.converter, outcome)(self, details=details)
1178         self.assertEqual([(outcome, self, arg)], self.result._events)
1179
1180     def check_outcome_exc_info(self, outcome, expected=None):
1181         """Check that calling a legacy outcome still works."""
1182         # calling some outcome with the legacy exc_info style api (no keyword
1183         # parameters) gets passed through.
1184         if not expected:
1185             expected = outcome
1186         err = sys.exc_info()
1187         getattr(self.converter, outcome)(self, err)
1188         self.assertEqual([(expected, self, err)], self.result._events)
1189
1190     def check_outcome_exc_info_to_nothing(self, outcome, expected=None):
1191         """Check that calling a legacy outcome on a fallback works."""
1192         # calling some outcome with the legacy exc_info style api (no keyword
1193         # parameters) gets passed through.
1194         if not expected:
1195             expected = outcome
1196         err = sys.exc_info()
1197         getattr(self.converter, outcome)(self, err)
1198         self.assertEqual([(expected, self)], self.result._events)
1199
1200     def check_outcome_nothing(self, outcome, expected=None):
1201         """Check that calling a legacy outcome still works."""
1202         if not expected:
1203             expected = outcome
1204         getattr(self.converter, outcome)(self)
1205         self.assertEqual([(expected, self)], self.result._events)
1206
1207     def check_outcome_string_nothing(self, outcome, expected):
1208         """Check that calling outcome with a string calls expected."""
1209         getattr(self.converter, outcome)(self, "foo")
1210         self.assertEqual([(expected, self)], self.result._events)
1211
1212     def check_outcome_string(self, outcome):
1213         """Check that calling outcome with a string works."""
1214         getattr(self.converter, outcome)(self, "foo")
1215         self.assertEqual([(outcome, self, "foo")], self.result._events)
1216
1217
1218 class TestExtendedToOriginalResultDecorator(
1219     TestExtendedToOriginalResultDecoratorBase):
1220
1221     def test_failfast_py26(self):
1222         self.make_26_result()
1223         self.assertEqual(False, self.converter.failfast)
1224         self.converter.failfast = True
1225         self.assertFalse(safe_hasattr(self.converter.decorated, 'failfast'))
1226
1227     def test_failfast_py27(self):
1228         self.make_27_result()
1229         self.assertEqual(False, self.converter.failfast)
1230         # setting it should write it to the backing result
1231         self.converter.failfast = True
1232         self.assertEqual(True, self.converter.decorated.failfast)
1233
1234     def test_progress_py26(self):
1235         self.make_26_result()
1236         self.converter.progress(1, 2)
1237
1238     def test_progress_py27(self):
1239         self.make_27_result()
1240         self.converter.progress(1, 2)
1241
1242     def test_progress_pyextended(self):
1243         self.make_extended_result()
1244         self.converter.progress(1, 2)
1245         self.assertEqual([('progress', 1, 2)], self.result._events)
1246
1247     def test_shouldStop(self):
1248         self.make_26_result()
1249         self.assertEqual(False, self.converter.shouldStop)
1250         self.converter.decorated.stop()
1251         self.assertEqual(True, self.converter.shouldStop)
1252
1253     def test_startTest_py26(self):
1254         self.make_26_result()
1255         self.converter.startTest(self)
1256         self.assertEqual([('startTest', self)], self.result._events)
1257
1258     def test_startTest_py27(self):
1259         self.make_27_result()
1260         self.converter.startTest(self)
1261         self.assertEqual([('startTest', self)], self.result._events)
1262
1263     def test_startTest_pyextended(self):
1264         self.make_extended_result()
1265         self.converter.startTest(self)
1266         self.assertEqual([('startTest', self)], self.result._events)
1267
1268     def test_startTestRun_py26(self):
1269         self.make_26_result()
1270         self.converter.startTestRun()
1271         self.assertEqual([], self.result._events)
1272
1273     def test_startTestRun_py27(self):
1274         self.make_27_result()
1275         self.converter.startTestRun()
1276         self.assertEqual([('startTestRun',)], self.result._events)
1277
1278     def test_startTestRun_pyextended(self):
1279         self.make_extended_result()
1280         self.converter.startTestRun()
1281         self.assertEqual([('startTestRun',)], self.result._events)
1282
1283     def test_stopTest_py26(self):
1284         self.make_26_result()
1285         self.converter.stopTest(self)
1286         self.assertEqual([('stopTest', self)], self.result._events)
1287
1288     def test_stopTest_py27(self):
1289         self.make_27_result()
1290         self.converter.stopTest(self)
1291         self.assertEqual([('stopTest', self)], self.result._events)
1292
1293     def test_stopTest_pyextended(self):
1294         self.make_extended_result()
1295         self.converter.stopTest(self)
1296         self.assertEqual([('stopTest', self)], self.result._events)
1297
1298     def test_stopTestRun_py26(self):
1299         self.make_26_result()
1300         self.converter.stopTestRun()
1301         self.assertEqual([], self.result._events)
1302
1303     def test_stopTestRun_py27(self):
1304         self.make_27_result()
1305         self.converter.stopTestRun()
1306         self.assertEqual([('stopTestRun',)], self.result._events)
1307
1308     def test_stopTestRun_pyextended(self):
1309         self.make_extended_result()
1310         self.converter.stopTestRun()
1311         self.assertEqual([('stopTestRun',)], self.result._events)
1312
1313     def test_tags_py26(self):
1314         self.make_26_result()
1315         self.converter.tags(set([1]), set([2]))
1316
1317     def test_tags_py27(self):
1318         self.make_27_result()
1319         self.converter.tags(set([1]), set([2]))
1320
1321     def test_tags_pyextended(self):
1322         self.make_extended_result()
1323         self.converter.tags(set([1]), set([2]))
1324         self.assertEqual([('tags', set([1]), set([2]))], self.result._events)
1325
1326     def test_time_py26(self):
1327         self.make_26_result()
1328         self.converter.time(1)
1329
1330     def test_time_py27(self):
1331         self.make_27_result()
1332         self.converter.time(1)
1333
1334     def test_time_pyextended(self):
1335         self.make_extended_result()
1336         self.converter.time(1)
1337         self.assertEqual([('time', 1)], self.result._events)
1338
1339
1340 class TestExtendedToOriginalAddError(TestExtendedToOriginalResultDecoratorBase):
1341
1342     outcome = 'addError'
1343
1344     def test_outcome_Original_py26(self):
1345         self.make_26_result()
1346         self.check_outcome_exc_info(self.outcome)
1347
1348     def test_outcome_Original_py27(self):
1349         self.make_27_result()
1350         self.check_outcome_exc_info(self.outcome)
1351
1352     def test_outcome_Original_pyextended(self):
1353         self.make_extended_result()
1354         self.check_outcome_exc_info(self.outcome)
1355
1356     def test_outcome_Extended_py26(self):
1357         self.make_26_result()
1358         self.check_outcome_details_to_exec_info(self.outcome)
1359
1360     def test_outcome_Extended_py27(self):
1361         self.make_27_result()
1362         self.check_outcome_details_to_exec_info(self.outcome)
1363
1364     def test_outcome_Extended_pyextended(self):
1365         self.make_extended_result()
1366         self.check_outcome_details(self.outcome)
1367
1368     def test_outcome__no_details(self):
1369         self.make_extended_result()
1370         self.assertThat(
1371             lambda: getattr(self.converter, self.outcome)(self),
1372             Raises(MatchesException(ValueError)))
1373
1374
1375 class TestExtendedToOriginalAddFailure(
1376     TestExtendedToOriginalAddError):
1377
1378     outcome = 'addFailure'
1379
1380
1381 class TestExtendedToOriginalAddExpectedFailure(
1382     TestExtendedToOriginalAddError):
1383
1384     outcome = 'addExpectedFailure'
1385
1386     def test_outcome_Original_py26(self):
1387         self.make_26_result()
1388         self.check_outcome_exc_info_to_nothing(self.outcome, 'addSuccess')
1389
1390     def test_outcome_Extended_py26(self):
1391         self.make_26_result()
1392         self.check_outcome_details_to_nothing(self.outcome, 'addSuccess')
1393
1394
1395
1396 class TestExtendedToOriginalAddSkip(
1397     TestExtendedToOriginalResultDecoratorBase):
1398
1399     outcome = 'addSkip'
1400
1401     def test_outcome_Original_py26(self):
1402         self.make_26_result()
1403         self.check_outcome_string_nothing(self.outcome, 'addSuccess')
1404
1405     def test_outcome_Original_py27(self):
1406         self.make_27_result()
1407         self.check_outcome_string(self.outcome)
1408
1409     def test_outcome_Original_pyextended(self):
1410         self.make_extended_result()
1411         self.check_outcome_string(self.outcome)
1412
1413     def test_outcome_Extended_py26(self):
1414         self.make_26_result()
1415         self.check_outcome_string_nothing(self.outcome, 'addSuccess')
1416
1417     def test_outcome_Extended_py27_no_reason(self):
1418         self.make_27_result()
1419         self.check_outcome_details_to_string(self.outcome)
1420
1421     def test_outcome_Extended_py27_reason(self):
1422         self.make_27_result()
1423         self.check_outcome_details_to_arg(self.outcome, 'foo',
1424             {'reason': Content(UTF8_TEXT, lambda:[_b('foo')])})
1425
1426     def test_outcome_Extended_pyextended(self):
1427         self.make_extended_result()
1428         self.check_outcome_details(self.outcome)
1429
1430     def test_outcome__no_details(self):
1431         self.make_extended_result()
1432         self.assertThat(
1433             lambda: getattr(self.converter, self.outcome)(self),
1434             Raises(MatchesException(ValueError)))
1435
1436
1437 class TestExtendedToOriginalAddSuccess(
1438     TestExtendedToOriginalResultDecoratorBase):
1439
1440     outcome = 'addSuccess'
1441     expected = 'addSuccess'
1442
1443     def test_outcome_Original_py26(self):
1444         self.make_26_result()
1445         self.check_outcome_nothing(self.outcome, self.expected)
1446
1447     def test_outcome_Original_py27(self):
1448         self.make_27_result()
1449         self.check_outcome_nothing(self.outcome)
1450
1451     def test_outcome_Original_pyextended(self):
1452         self.make_extended_result()
1453         self.check_outcome_nothing(self.outcome)
1454
1455     def test_outcome_Extended_py26(self):
1456         self.make_26_result()
1457         self.check_outcome_details_to_nothing(self.outcome, self.expected)
1458
1459     def test_outcome_Extended_py27(self):
1460         self.make_27_result()
1461         self.check_outcome_details_to_nothing(self.outcome)
1462
1463     def test_outcome_Extended_pyextended(self):
1464         self.make_extended_result()
1465         self.check_outcome_details(self.outcome)
1466
1467
1468 class TestExtendedToOriginalAddUnexpectedSuccess(
1469     TestExtendedToOriginalResultDecoratorBase):
1470
1471     outcome = 'addUnexpectedSuccess'
1472     expected = 'addFailure'
1473
1474     def test_outcome_Original_py26(self):
1475         self.make_26_result()
1476         getattr(self.converter, self.outcome)(self)
1477         [event] = self.result._events
1478         self.assertEqual((self.expected, self), event[:2])
1479
1480     def test_outcome_Original_py27(self):
1481         self.make_27_result()
1482         self.check_outcome_nothing(self.outcome)
1483
1484     def test_outcome_Original_pyextended(self):
1485         self.make_extended_result()
1486         self.check_outcome_nothing(self.outcome)
1487
1488     def test_outcome_Extended_py26(self):
1489         self.make_26_result()
1490         getattr(self.converter, self.outcome)(self)
1491         [event] = self.result._events
1492         self.assertEqual((self.expected, self), event[:2])
1493
1494     def test_outcome_Extended_py27(self):
1495         self.make_27_result()
1496         self.check_outcome_details_to_nothing(self.outcome)
1497
1498     def test_outcome_Extended_pyextended(self):
1499         self.make_extended_result()
1500         self.check_outcome_details(self.outcome)
1501
1502
1503 class TestExtendedToOriginalResultOtherAttributes(
1504     TestExtendedToOriginalResultDecoratorBase):
1505
1506     def test_other_attribute(self):
1507         class OtherExtendedResult:
1508             def foo(self):
1509                 return 2
1510             bar = 1
1511         self.result = OtherExtendedResult()
1512         self.make_converter()
1513         self.assertEqual(1, self.converter.bar)
1514         self.assertEqual(2, self.converter.foo())
1515
1516
1517 class TestNonAsciiResults(TestCase):
1518     """Test all kinds of tracebacks are cleanly interpreted as unicode
1519
1520     Currently only uses weak "contains" assertions, would be good to be much
1521     stricter about the expected output. This would add a few failures for the
1522     current release of IronPython for instance, which gets some traceback
1523     lines muddled.
1524     """
1525
1526     _sample_texts = (
1527         _u("pa\u026a\u03b8\u0259n"), # Unicode encodings only
1528         _u("\u5357\u7121"), # In ISO 2022 encodings
1529         _u("\xa7\xa7\xa7"), # In ISO 8859 encodings
1530         )
1531
1532     _is_pypy = "__pypy__" in sys.builtin_module_names
1533     # Everything but Jython shows syntax errors on the current character
1534     _error_on_character = os.name != "java" and not _is_pypy
1535
1536     def _run(self, stream, test):
1537         """Run the test, the same as in testtools.run but not to stdout"""
1538         result = TextTestResult(stream)
1539         result.startTestRun()
1540         try:
1541             return test.run(result)
1542         finally:
1543             result.stopTestRun()
1544
1545     def _write_module(self, name, encoding, contents):
1546         """Create Python module on disk with contents in given encoding"""
1547         try:
1548             # Need to pre-check that the coding is valid or codecs.open drops
1549             # the file without closing it which breaks non-refcounted pythons
1550             codecs.lookup(encoding)
1551         except LookupError:
1552             self.skip("Encoding unsupported by implementation: %r" % encoding)
1553         f = codecs.open(os.path.join(self.dir, name + ".py"), "w", encoding)
1554         try:
1555             f.write(contents)
1556         finally:
1557             f.close()
1558
1559     def _test_external_case(self, testline, coding="ascii", modulelevel="",
1560             suffix=""):
1561         """Create and run a test case in a separate module"""
1562         self._setup_external_case(testline, coding, modulelevel, suffix)
1563         return self._run_external_case()
1564
1565     def _setup_external_case(self, testline, coding="ascii", modulelevel="",
1566             suffix=""):
1567         """Create a test case in a separate module"""
1568         _, prefix, self.modname = self.id().rsplit(".", 2)
1569         self.dir = tempfile.mkdtemp(prefix=prefix, suffix=suffix)
1570         self.addCleanup(shutil.rmtree, self.dir)
1571         self._write_module(self.modname, coding,
1572             # Older Python 2 versions don't see a coding declaration in a
1573             # docstring so it has to be in a comment, but then we can't
1574             # workaround bug: <http://ironpython.codeplex.com/workitem/26940>
1575             "# coding: %s\n"
1576             "import testtools\n"
1577             "%s\n"
1578             "class Test(testtools.TestCase):\n"
1579             "    def runTest(self):\n"
1580             "        %s\n" % (coding, modulelevel, testline))
1581
1582     def _run_external_case(self):
1583         """Run the prepared test case in a separate module"""
1584         sys.path.insert(0, self.dir)
1585         self.addCleanup(sys.path.remove, self.dir)
1586         module = __import__(self.modname)
1587         self.addCleanup(sys.modules.pop, self.modname)
1588         stream = StringIO()
1589         self._run(stream, module.Test())
1590         return stream.getvalue()
1591
1592     def _silence_deprecation_warnings(self):
1593         """Shut up DeprecationWarning for this test only"""
1594         warnings.simplefilter("ignore", DeprecationWarning)
1595         self.addCleanup(warnings.filters.remove, warnings.filters[0])
1596
1597     def _get_sample_text(self, encoding="unicode_internal"):
1598         if encoding is None and str_is_unicode:
1599            encoding = "unicode_internal"
1600         for u in self._sample_texts:
1601             try:
1602                 b = u.encode(encoding)
1603                 if u == b.decode(encoding):
1604                    if str_is_unicode:
1605                        return u, u
1606                    return u, b
1607             except (LookupError, UnicodeError):
1608                 pass
1609         self.skip("Could not find a sample text for encoding: %r" % encoding)
1610
1611     def _as_output(self, text):
1612         return text
1613
1614     def test_non_ascii_failure_string(self):
1615         """Assertion contents can be non-ascii and should get decoded"""
1616         text, raw = self._get_sample_text(_get_exception_encoding())
1617         textoutput = self._test_external_case("self.fail(%s)" % _r(raw))
1618         self.assertIn(self._as_output(text), textoutput)
1619
1620     def test_non_ascii_failure_string_via_exec(self):
1621         """Assertion via exec can be non-ascii and still gets decoded"""
1622         text, raw = self._get_sample_text(_get_exception_encoding())
1623         textoutput = self._test_external_case(
1624             testline='exec ("self.fail(%s)")' % _r(raw))
1625         self.assertIn(self._as_output(text), textoutput)
1626
1627     def test_control_characters_in_failure_string(self):
1628         """Control characters in assertions should be escaped"""
1629         textoutput = self._test_external_case("self.fail('\\a\\a\\a')")
1630         self.expectFailure("Defense against the beeping horror unimplemented",
1631             self.assertNotIn, self._as_output("\a\a\a"), textoutput)
1632         self.assertIn(self._as_output(_u("\uFFFD\uFFFD\uFFFD")), textoutput)
1633
1634     def _local_os_error_matcher(self):
1635         if sys.version_info > (3, 3):
1636             return MatchesAny(Contains("FileExistsError: "),
1637                               Contains("PermissionError: "))
1638         elif os.name != "nt" or sys.version_info < (2, 5):
1639             return Contains(self._as_output("OSError: "))
1640         else:
1641             return Contains(self._as_output("WindowsError: "))
1642
1643     def test_os_error(self):
1644         """Locale error messages from the OS shouldn't break anything"""
1645         textoutput = self._test_external_case(
1646             modulelevel="import os",
1647             testline="os.mkdir('/')")
1648         self.assertThat(textoutput, self._local_os_error_matcher())
1649
1650     def test_assertion_text_shift_jis(self):
1651         """A terminal raw backslash in an encoded string is weird but fine"""
1652         example_text = _u("\u5341")
1653         textoutput = self._test_external_case(
1654             coding="shift_jis",
1655             testline="self.fail('%s')" % example_text)
1656         if str_is_unicode:
1657             output_text = example_text
1658         else:
1659             output_text = example_text.encode("shift_jis").decode(
1660                 _get_exception_encoding(), "replace")
1661         self.assertIn(self._as_output("AssertionError: %s" % output_text),
1662             textoutput)
1663
1664     def test_file_comment_iso2022_jp(self):
1665         """Control character escapes must be preserved if valid encoding"""
1666         example_text, _ = self._get_sample_text("iso2022_jp")
1667         textoutput = self._test_external_case(
1668             coding="iso2022_jp",
1669             testline="self.fail('Simple') # %s" % example_text)
1670         self.assertIn(self._as_output(example_text), textoutput)
1671
1672     def test_unicode_exception(self):
1673         """Exceptions that can be formated losslessly as unicode should be"""
1674         example_text, _ = self._get_sample_text()
1675         exception_class = (
1676             "class FancyError(Exception):\n"
1677             # A __unicode__ method does nothing on py3k but the default works
1678             "    def __unicode__(self):\n"
1679             "        return self.args[0]\n")
1680         textoutput = self._test_external_case(
1681             modulelevel=exception_class,
1682             testline="raise FancyError(%s)" % _r(example_text))
1683         self.assertIn(self._as_output(example_text), textoutput)
1684
1685     def test_unprintable_exception(self):
1686         """A totally useless exception instance still prints something"""
1687         exception_class = (
1688             "class UnprintableError(Exception):\n"
1689             "    def __str__(self):\n"
1690             "        raise RuntimeError\n"
1691             "    def __unicode__(self):\n"
1692             "        raise RuntimeError\n"
1693             "    def __repr__(self):\n"
1694             "        raise RuntimeError\n")
1695         textoutput = self._test_external_case(
1696             modulelevel=exception_class,
1697             testline="raise UnprintableError")
1698         self.assertIn(self._as_output(
1699             "UnprintableError: <unprintable UnprintableError object>\n"),
1700             textoutput)
1701
1702     def test_string_exception(self):
1703         """Raise a string rather than an exception instance if supported"""
1704         if sys.version_info > (2, 6):
1705             self.skip("No string exceptions in Python 2.6 or later")
1706         elif sys.version_info > (2, 5):
1707             self._silence_deprecation_warnings()
1708         textoutput = self._test_external_case(testline="raise 'plain str'")
1709         self.assertIn(self._as_output("\nplain str\n"), textoutput)
1710
1711     def test_non_ascii_dirname(self):
1712         """Script paths in the traceback can be non-ascii"""
1713         text, raw = self._get_sample_text(sys.getfilesystemencoding())
1714         textoutput = self._test_external_case(
1715             # Avoid bug in Python 3 by giving a unicode source encoding rather
1716             # than just ascii which raises a SyntaxError with no other details
1717             coding="utf-8",
1718             testline="self.fail('Simple')",
1719             suffix=raw)
1720         self.assertIn(self._as_output(text), textoutput)
1721
1722     def test_syntax_error(self):
1723         """Syntax errors should still have fancy special-case formatting"""
1724         textoutput = self._test_external_case("exec ('f(a, b c)')")
1725         self.assertIn(self._as_output(
1726             '  File "<string>", line 1\n'
1727             '    f(a, b c)\n'
1728             + ' ' * self._error_on_character +
1729             '          ^\n'
1730             'SyntaxError: '
1731             ), textoutput)
1732
1733     def test_syntax_error_malformed(self):
1734         """Syntax errors with bogus parameters should break anything"""
1735         textoutput = self._test_external_case("raise SyntaxError(3, 2, 1)")
1736         self.assertIn(self._as_output("\nSyntaxError: "), textoutput)
1737
1738     def test_syntax_error_import_binary(self):
1739         """Importing a binary file shouldn't break SyntaxError formatting"""
1740         if sys.version_info < (2, 5):
1741             # Python 2.4 assumes the file is latin-1 and tells you off
1742             self._silence_deprecation_warnings()
1743         self._setup_external_case("import bad")
1744         f = open(os.path.join(self.dir, "bad.py"), "wb")
1745         try:
1746             f.write(_b("x\x9c\xcb*\xcd\xcb\x06\x00\x04R\x01\xb9"))
1747         finally:
1748             f.close()
1749         textoutput = self._run_external_case()
1750         matches_error = MatchesAny(
1751             Contains('\nTypeError: '), Contains('\nSyntaxError: '))
1752         self.assertThat(textoutput, matches_error)
1753
1754     def test_syntax_error_line_iso_8859_1(self):
1755         """Syntax error on a latin-1 line shows the line decoded"""
1756         text, raw = self._get_sample_text("iso-8859-1")
1757         textoutput = self._setup_external_case("import bad")
1758         self._write_module("bad", "iso-8859-1",
1759             "# coding: iso-8859-1\n! = 0 # %s\n" % text)
1760         textoutput = self._run_external_case()
1761         self.assertIn(self._as_output(_u(
1762             #'bad.py", line 2\n'
1763             '    ! = 0 # %s\n'
1764             '    ^\n'
1765             'SyntaxError: ') %
1766             (text,)), textoutput)
1767
1768     def test_syntax_error_line_iso_8859_5(self):
1769         """Syntax error on a iso-8859-5 line shows the line decoded"""
1770         text, raw = self._get_sample_text("iso-8859-5")
1771         textoutput = self._setup_external_case("import bad")
1772         self._write_module("bad", "iso-8859-5",
1773             "# coding: iso-8859-5\n%% = 0 # %s\n" % text)
1774         textoutput = self._run_external_case()
1775         self.assertIn(self._as_output(_u(
1776             #'bad.py", line 2\n'
1777             '    %% = 0 # %s\n'
1778             + ' ' * self._error_on_character +
1779             '   ^\n'
1780             'SyntaxError: ') %
1781             (text,)), textoutput)
1782
1783     def test_syntax_error_line_euc_jp(self):
1784         """Syntax error on a euc_jp line shows the line decoded"""
1785         text, raw = self._get_sample_text("euc_jp")
1786         textoutput = self._setup_external_case("import bad")
1787         self._write_module("bad", "euc_jp",
1788             "# coding: euc_jp\n$ = 0 # %s\n" % text)
1789         textoutput = self._run_external_case()
1790         # pypy uses cpython's multibyte codecs so has their behavior here
1791         if self._is_pypy:
1792             self._error_on_character = True
1793         self.assertIn(self._as_output(_u(
1794             #'bad.py", line 2\n'
1795             '    $ = 0 # %s\n'
1796             + ' ' * self._error_on_character +
1797             '   ^\n'
1798             'SyntaxError: ') %
1799             (text,)), textoutput)
1800
1801     def test_syntax_error_line_utf_8(self):
1802         """Syntax error on a utf-8 line shows the line decoded"""
1803         text, raw = self._get_sample_text("utf-8")
1804         textoutput = self._setup_external_case("import bad")
1805         self._write_module("bad", "utf-8", _u("\ufeff^ = 0 # %s\n") % text)
1806         textoutput = self._run_external_case()
1807         self.assertIn(self._as_output(_u(
1808             'bad.py", line 1\n'
1809             '    ^ = 0 # %s\n'
1810             + ' ' * self._error_on_character +
1811             '   ^\n'
1812             'SyntaxError: ') %
1813             text), textoutput)
1814
1815
1816 class TestNonAsciiResultsWithUnittest(TestNonAsciiResults):
1817     """Test that running under unittest produces clean ascii strings"""
1818
1819     def _run(self, stream, test):
1820         from unittest import TextTestRunner as _Runner
1821         return _Runner(stream).run(test)
1822
1823     def _as_output(self, text):
1824         if str_is_unicode:
1825             return text
1826         return text.encode("utf-8")
1827
1828
1829 class TestDetailsToStr(TestCase):
1830
1831     def test_no_details(self):
1832         string = _details_to_str({})
1833         self.assertThat(string, Equals(''))
1834
1835     def test_binary_content(self):
1836         content = content_from_stream(
1837             StringIO('foo'), content_type=ContentType('image', 'jpeg'))
1838         string = _details_to_str({'attachment': content})
1839         self.assertThat(
1840             string, Equals("""\
1841 Binary content:
1842   attachment (image/jpeg)
1843 """))
1844
1845     def test_single_line_content(self):
1846         content = text_content('foo')
1847         string = _details_to_str({'attachment': content})
1848         self.assertThat(string, Equals('attachment: {{{foo}}}\n'))
1849
1850     def test_multi_line_text_content(self):
1851         content = text_content('foo\nbar\nbaz')
1852         string = _details_to_str({'attachment': content})
1853         self.assertThat(string, Equals('attachment: {{{\nfoo\nbar\nbaz\n}}}\n'))
1854
1855     def test_special_text_content(self):
1856         content = text_content('foo')
1857         string = _details_to_str({'attachment': content}, special='attachment')
1858         self.assertThat(string, Equals('foo\n'))
1859
1860     def test_multiple_text_content(self):
1861         string = _details_to_str(
1862             {'attachment': text_content('foo\nfoo'),
1863              'attachment-1': text_content('bar\nbar')})
1864         self.assertThat(
1865             string, Equals('attachment: {{{\n'
1866                            'foo\n'
1867                            'foo\n'
1868                            '}}}\n'
1869                            '\n'
1870                            'attachment-1: {{{\n'
1871                            'bar\n'
1872                            'bar\n'
1873                            '}}}\n'))
1874
1875     def test_empty_attachment(self):
1876         string = _details_to_str({'attachment': text_content('')})
1877         self.assertThat(
1878             string, Equals("""\
1879 Empty attachments:
1880   attachment
1881 """))
1882
1883     def test_lots_of_different_attachments(self):
1884         jpg = lambda x: content_from_stream(
1885             StringIO(x), ContentType('image', 'jpeg'))
1886         attachments = {
1887             'attachment': text_content('foo'),
1888             'attachment-1': text_content('traceback'),
1889             'attachment-2': jpg('pic1'),
1890             'attachment-3': text_content('bar'),
1891             'attachment-4': text_content(''),
1892             'attachment-5': jpg('pic2'),
1893             }
1894         string = _details_to_str(attachments, special='attachment-1')
1895         self.assertThat(
1896             string, Equals("""\
1897 Binary content:
1898   attachment-2 (image/jpeg)
1899   attachment-5 (image/jpeg)
1900 Empty attachments:
1901   attachment-4
1902
1903 attachment: {{{foo}}}
1904 attachment-3: {{{bar}}}
1905
1906 traceback
1907 """))
1908
1909
1910 class TestByTestResultTests(TestCase):
1911
1912     def setUp(self):
1913         super(TestByTestResultTests, self).setUp()
1914         self.log = []
1915         self.result = TestByTestResult(self.on_test)
1916         now = iter(range(5))
1917         self.result._now = lambda: advance_iterator(now)
1918
1919     def assertCalled(self, **kwargs):
1920         defaults = {
1921             'test': self,
1922             'tags': set(),
1923             'details': None,
1924             'start_time': 0,
1925             'stop_time': 1,
1926             }
1927         defaults.update(kwargs)
1928         self.assertEqual([defaults], self.log)
1929
1930     def on_test(self, **kwargs):
1931         self.log.append(kwargs)
1932
1933     def test_no_tests_nothing_reported(self):
1934         self.result.startTestRun()
1935         self.result.stopTestRun()
1936         self.assertEqual([], self.log)
1937
1938     def test_add_success(self):
1939         self.result.startTest(self)
1940         self.result.addSuccess(self)
1941         self.result.stopTest(self)
1942         self.assertCalled(status='success')
1943
1944     def test_add_success_details(self):
1945         self.result.startTest(self)
1946         details = {'foo': 'bar'}
1947         self.result.addSuccess(self, details=details)
1948         self.result.stopTest(self)
1949         self.assertCalled(status='success', details=details)
1950
1951     def test_global_tags(self):
1952         self.result.tags(['foo'], [])
1953         self.result.startTest(self)
1954         self.result.addSuccess(self)
1955         self.result.stopTest(self)
1956         self.assertCalled(status='success', tags=set(['foo']))
1957
1958     def test_local_tags(self):
1959         self.result.tags(['foo'], [])
1960         self.result.startTest(self)
1961         self.result.tags(['bar'], [])
1962         self.result.addSuccess(self)
1963         self.result.stopTest(self)
1964         self.assertCalled(status='success', tags=set(['foo', 'bar']))
1965
1966     def test_add_error(self):
1967         self.result.startTest(self)
1968         try:
1969             1/0
1970         except ZeroDivisionError:
1971             error = sys.exc_info()
1972         self.result.addError(self, error)
1973         self.result.stopTest(self)
1974         self.assertCalled(
1975             status='error',
1976             details={'traceback': TracebackContent(error, self)})
1977
1978     def test_add_error_details(self):
1979         self.result.startTest(self)
1980         details = {"foo": text_content("bar")}
1981         self.result.addError(self, details=details)
1982         self.result.stopTest(self)
1983         self.assertCalled(status='error', details=details)
1984
1985     def test_add_failure(self):
1986         self.result.startTest(self)
1987         try:
1988             self.fail("intentional failure")
1989         except self.failureException:
1990             failure = sys.exc_info()
1991         self.result.addFailure(self, failure)
1992         self.result.stopTest(self)
1993         self.assertCalled(
1994             status='failure',
1995             details={'traceback': TracebackContent(failure, self)})
1996
1997     def test_add_failure_details(self):
1998         self.result.startTest(self)
1999         details = {"foo": text_content("bar")}
2000         self.result.addFailure(self, details=details)
2001         self.result.stopTest(self)
2002         self.assertCalled(status='failure', details=details)
2003
2004     def test_add_xfail(self):
2005         self.result.startTest(self)
2006         try:
2007             1/0
2008         except ZeroDivisionError:
2009             error = sys.exc_info()
2010         self.result.addExpectedFailure(self, error)
2011         self.result.stopTest(self)
2012         self.assertCalled(
2013             status='xfail',
2014             details={'traceback': TracebackContent(error, self)})
2015
2016     def test_add_xfail_details(self):
2017         self.result.startTest(self)
2018         details = {"foo": text_content("bar")}
2019         self.result.addExpectedFailure(self, details=details)
2020         self.result.stopTest(self)
2021         self.assertCalled(status='xfail', details=details)
2022
2023     def test_add_unexpected_success(self):
2024         self.result.startTest(self)
2025         details = {'foo': 'bar'}
2026         self.result.addUnexpectedSuccess(self, details=details)
2027         self.result.stopTest(self)
2028         self.assertCalled(status='success', details=details)
2029
2030     def test_add_skip_reason(self):
2031         self.result.startTest(self)
2032         reason = self.getUniqueString()
2033         self.result.addSkip(self, reason)
2034         self.result.stopTest(self)
2035         self.assertCalled(
2036             status='skip', details={'reason': text_content(reason)})
2037
2038     def test_add_skip_details(self):
2039         self.result.startTest(self)
2040         details = {'foo': 'bar'}
2041         self.result.addSkip(self, details=details)
2042         self.result.stopTest(self)
2043         self.assertCalled(status='skip', details=details)
2044
2045     def test_twice(self):
2046         self.result.startTest(self)
2047         self.result.addSuccess(self, details={'foo': 'bar'})
2048         self.result.stopTest(self)
2049         self.result.startTest(self)
2050         self.result.addSuccess(self)
2051         self.result.stopTest(self)
2052         self.assertEqual(
2053             [{'test': self,
2054               'status': 'success',
2055               'start_time': 0,
2056               'stop_time': 1,
2057               'tags': set(),
2058               'details': {'foo': 'bar'}},
2059              {'test': self,
2060               'status': 'success',
2061               'start_time': 2,
2062               'stop_time': 3,
2063               'tags': set(),
2064               'details': None},
2065              ],
2066             self.log)
2067
2068
2069 class TestTagger(TestCase):
2070
2071     def test_tags_tests(self):
2072         result = ExtendedTestResult()
2073         tagger = Tagger(result, set(['foo']), set(['bar']))
2074         test1, test2 = self, make_test()
2075         tagger.startTest(test1)
2076         tagger.addSuccess(test1)
2077         tagger.stopTest(test1)
2078         tagger.startTest(test2)
2079         tagger.addSuccess(test2)
2080         tagger.stopTest(test2)
2081         self.assertEqual(
2082             [('startTest', test1),
2083              ('tags', set(['foo']), set(['bar'])),
2084              ('addSuccess', test1),
2085              ('stopTest', test1),
2086              ('startTest', test2),
2087              ('tags', set(['foo']), set(['bar'])),
2088              ('addSuccess', test2),
2089              ('stopTest', test2),
2090              ], result._events)
2091
2092
2093 def test_suite():
2094     from unittest import TestLoader
2095     return TestLoader().loadTestsFromName(__name__)