s4:dsdb/drepl: update the source_dsa_obj/invocation_id in repsFrom
[Samba/gebeck_regimport.git] / lib / testtools / testtools / tests / test_testresult.py
blobc935b146bfc9d51c0856c778a1e7127e36977da6
1 # Copyright (c) 2008-2012 testtools developers. See LICENSE for details.
3 """Test TestResults and related things."""
5 __metaclass__ = type
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
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,
31 from testtools.compat import (
32 _b,
33 _get_exception_encoding,
34 _r,
35 _u,
36 advance_iterator,
37 str_is_unicode,
38 StringIO,
40 from testtools.content import (
41 Content,
42 content_from_stream,
43 text_content,
44 TracebackContent,
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,
56 from testtools.tests.helpers import (
57 an_exc_info,
58 FullStackRunTest,
59 LoggingResult,
60 run_with_stack_hidden,
62 from testtools.testresult.doubles import (
63 Python26TestResult,
64 Python27TestResult,
65 ExtendedTestResult,
67 from testtools.testresult.real import (
68 _details_to_str,
69 _merge_tags,
70 utc,
74 def make_erroring_test():
75 class Test(TestCase):
76 def error(self):
77 1/0
78 return Test("error")
81 def make_failing_test():
82 class Test(TestCase):
83 def failed(self):
84 self.fail("yo!")
85 return Test("failed")
88 def make_mismatching_test():
89 class Test(TestCase):
90 def mismatch(self):
91 self.assertEqual(1, 2)
92 return Test("mismatch")
95 def make_unexpectedly_successful_test():
96 class Test(TestCase):
97 def succeeded(self):
98 self.expectFailure("yo!", lambda: None)
99 return Test("succeeded")
102 def make_test():
103 class Test(TestCase):
104 def test(self):
105 pass
106 return Test("test")
109 def make_exception_info(exceptionFactory, *args, **kwargs):
110 try:
111 raise exceptionFactory(*args, **kwargs)
112 except:
113 return sys.exc_info()
116 class Python26Contract(object):
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())
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())
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())
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())
147 def test_stop_sets_shouldStop(self):
148 result = self.makeResult()
149 result.stop()
150 self.assertTrue(result.shouldStop)
153 class Python27Contract(Python26Contract):
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)
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())
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"))
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())
183 def test_addUnexpectedSuccess(self):
184 # Calling addUnexpectedSuccess(test) completes ok.
185 result = self.makeResult()
186 result.startTest(self)
187 result.addUnexpectedSuccess(self)
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())
197 def test_startStopTestRun(self):
198 # Calling startTestRun completes ok.
199 result = self.makeResult()
200 result.startTestRun()
201 result.stopTestRun()
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)
215 class TagsContract(Python27Contract):
216 """Tests to ensure correct tagging behaviour.
218 See the subunit docs for guidelines on how this is supposed to work.
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)
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)
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)
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)
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)
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)
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)
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)
289 class DetailsContract(TagsContract):
290 """Tests for the details API of TestResults."""
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={})
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={})
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={})
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={})
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={})
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={})
329 class FallbackContract(DetailsContract):
330 """When we fallback we take our policy choice to map calls.
332 For instance, we map unexpectedSuccess to an error code, not to success.
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())
344 class StartTestRunContract(FallbackContract):
345 """Defines the contract for testtools policy choices.
347 That is things which are not simply extensions to unittest but choices we
348 have made differently.
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())
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())
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())
376 class TestTestResultContract(TestCase, StartTestRunContract):
378 run_test_with = FullStackRunTest
380 def makeResult(self):
381 return TestResult()
384 class TestMultiTestResultContract(TestCase, StartTestRunContract):
386 run_test_with = FullStackRunTest
388 def makeResult(self):
389 return MultiTestResult(TestResult(), TestResult())
392 class TestTextTestResultContract(TestCase, StartTestRunContract):
394 run_test_with = FullStackRunTest
396 def makeResult(self):
397 return TextTestResult(StringIO())
400 class TestThreadSafeForwardingResultContract(TestCase, StartTestRunContract):
402 run_test_with = FullStackRunTest
404 def makeResult(self):
405 result_semaphore = threading.Semaphore(1)
406 target = TestResult()
407 return ThreadsafeForwardingResult(target, result_semaphore)
410 class TestExtendedTestResultContract(TestCase, StartTestRunContract):
412 def makeResult(self):
413 return ExtendedTestResult()
416 class TestPython26TestResultContract(TestCase, Python26Contract):
418 def makeResult(self):
419 return Python26TestResult()
422 class TestAdaptedPython26TestResultContract(TestCase, FallbackContract):
424 def makeResult(self):
425 return ExtendedToOriginalDecorator(Python26TestResult())
428 class TestPython27TestResultContract(TestCase, Python27Contract):
430 def makeResult(self):
431 return Python27TestResult()
434 class TestAdaptedPython27TestResultContract(TestCase, DetailsContract):
436 def makeResult(self):
437 return ExtendedToOriginalDecorator(Python27TestResult())
440 class TestTestResultDecoratorContract(TestCase, StartTestRunContract):
442 run_test_with = FullStackRunTest
444 def makeResult(self):
445 return TestResultDecorator(TestResult())
448 class TestTestResult(TestCase):
449 """Tests for 'TestResult'."""
451 run_tests_with = FullStackRunTest
453 def makeResult(self):
454 """Make an arbitrary result for testing."""
455 return TestResult()
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)
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())
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())
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))
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))
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))
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.
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)
561 class TestMultiTestResult(TestCase):
562 """Tests for 'MultiTestResult'."""
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)
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)
575 def test_repr(self):
576 self.assertEqual(
577 '<MultiTestResult (%r, %r)>' % (
578 ExtendedToOriginalDecorator(self.result1),
579 ExtendedToOriginalDecorator(self.result2)),
580 repr(self.multiResult))
582 def test_empty(self):
583 # Initializing a `MultiTestResult` doesn't do anything to its
584 # `TestResult`s.
585 self.assertResultLogsEqual([])
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)
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)
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)
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)])
612 def test_stop(self):
613 self.assertFalse(self.multiResult.shouldStop)
614 self.multiResult.stop()
615 self.assertResultLogsEqual(['stop'])
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)])
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)])
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)])
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')])
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)])
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)])
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')])
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')])
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)
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)])
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')])
693 class TestTextTestResult(TestCase):
694 """Tests for 'TextTestResult'."""
696 def setUp(self):
697 super(TestTextTestResult, self).setUp()
698 self.result = TextTestResult(StringIO())
700 def getvalue(self):
701 return self.result.stream.getvalue()
703 def test__init_sets_stream(self):
704 result = TextTestResult("fp")
705 self.assertEqual("fp", result.stream)
707 def reset_output(self):
708 self.result.stream = StringIO()
710 def test_startTestRun(self):
711 self.result.startTestRun()
712 self.assertEqual("Tests running...\n", self.getvalue())
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))
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))
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))
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))
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))
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))
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))
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))
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
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))
817 class TestThreadSafeForwardingResult(TestCase):
818 """Tests for `TestThreadSafeForwardingResult`."""
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
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
1063 class TestMergeTags(TestCase):
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))
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))
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))
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))
1100 class TestExtendedToOriginalResultDecoratorBase(TestCase):
1102 def make_26_result(self):
1103 self.result = Python26TestResult()
1104 self.make_converter()
1106 def make_27_result(self):
1107 self.result = Python27TestResult()
1108 self.make_converter()
1110 def make_converter(self):
1111 self.converter = ExtendedToOriginalDecorator(self.result)
1113 def make_extended_result(self):
1114 self.result = ExtendedTestResult()
1115 self.make_converter()
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)
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"))
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)
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)
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)
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)
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)
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)
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)
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)
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)
1218 class TestExtendedToOriginalResultDecorator(
1219 TestExtendedToOriginalResultDecoratorBase):
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'))
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)
1234 def test_progress_py26(self):
1235 self.make_26_result()
1236 self.converter.progress(1, 2)
1238 def test_progress_py27(self):
1239 self.make_27_result()
1240 self.converter.progress(1, 2)
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)
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)
1253 def test_startTest_py26(self):
1254 self.make_26_result()
1255 self.converter.startTest(self)
1256 self.assertEqual([('startTest', self)], self.result._events)
1258 def test_startTest_py27(self):
1259 self.make_27_result()
1260 self.converter.startTest(self)
1261 self.assertEqual([('startTest', self)], self.result._events)
1263 def test_startTest_pyextended(self):
1264 self.make_extended_result()
1265 self.converter.startTest(self)
1266 self.assertEqual([('startTest', self)], self.result._events)
1268 def test_startTestRun_py26(self):
1269 self.make_26_result()
1270 self.converter.startTestRun()
1271 self.assertEqual([], self.result._events)
1273 def test_startTestRun_py27(self):
1274 self.make_27_result()
1275 self.converter.startTestRun()
1276 self.assertEqual([('startTestRun',)], self.result._events)
1278 def test_startTestRun_pyextended(self):
1279 self.make_extended_result()
1280 self.converter.startTestRun()
1281 self.assertEqual([('startTestRun',)], self.result._events)
1283 def test_stopTest_py26(self):
1284 self.make_26_result()
1285 self.converter.stopTest(self)
1286 self.assertEqual([('stopTest', self)], self.result._events)
1288 def test_stopTest_py27(self):
1289 self.make_27_result()
1290 self.converter.stopTest(self)
1291 self.assertEqual([('stopTest', self)], self.result._events)
1293 def test_stopTest_pyextended(self):
1294 self.make_extended_result()
1295 self.converter.stopTest(self)
1296 self.assertEqual([('stopTest', self)], self.result._events)
1298 def test_stopTestRun_py26(self):
1299 self.make_26_result()
1300 self.converter.stopTestRun()
1301 self.assertEqual([], self.result._events)
1303 def test_stopTestRun_py27(self):
1304 self.make_27_result()
1305 self.converter.stopTestRun()
1306 self.assertEqual([('stopTestRun',)], self.result._events)
1308 def test_stopTestRun_pyextended(self):
1309 self.make_extended_result()
1310 self.converter.stopTestRun()
1311 self.assertEqual([('stopTestRun',)], self.result._events)
1313 def test_tags_py26(self):
1314 self.make_26_result()
1315 self.converter.tags(set([1]), set([2]))
1317 def test_tags_py27(self):
1318 self.make_27_result()
1319 self.converter.tags(set([1]), set([2]))
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)
1326 def test_time_py26(self):
1327 self.make_26_result()
1328 self.converter.time(1)
1330 def test_time_py27(self):
1331 self.make_27_result()
1332 self.converter.time(1)
1334 def test_time_pyextended(self):
1335 self.make_extended_result()
1336 self.converter.time(1)
1337 self.assertEqual([('time', 1)], self.result._events)
1340 class TestExtendedToOriginalAddError(TestExtendedToOriginalResultDecoratorBase):
1342 outcome = 'addError'
1344 def test_outcome_Original_py26(self):
1345 self.make_26_result()
1346 self.check_outcome_exc_info(self.outcome)
1348 def test_outcome_Original_py27(self):
1349 self.make_27_result()
1350 self.check_outcome_exc_info(self.outcome)
1352 def test_outcome_Original_pyextended(self):
1353 self.make_extended_result()
1354 self.check_outcome_exc_info(self.outcome)
1356 def test_outcome_Extended_py26(self):
1357 self.make_26_result()
1358 self.check_outcome_details_to_exec_info(self.outcome)
1360 def test_outcome_Extended_py27(self):
1361 self.make_27_result()
1362 self.check_outcome_details_to_exec_info(self.outcome)
1364 def test_outcome_Extended_pyextended(self):
1365 self.make_extended_result()
1366 self.check_outcome_details(self.outcome)
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)))
1375 class TestExtendedToOriginalAddFailure(
1376 TestExtendedToOriginalAddError):
1378 outcome = 'addFailure'
1381 class TestExtendedToOriginalAddExpectedFailure(
1382 TestExtendedToOriginalAddError):
1384 outcome = 'addExpectedFailure'
1386 def test_outcome_Original_py26(self):
1387 self.make_26_result()
1388 self.check_outcome_exc_info_to_nothing(self.outcome, 'addSuccess')
1390 def test_outcome_Extended_py26(self):
1391 self.make_26_result()
1392 self.check_outcome_details_to_nothing(self.outcome, 'addSuccess')
1396 class TestExtendedToOriginalAddSkip(
1397 TestExtendedToOriginalResultDecoratorBase):
1399 outcome = 'addSkip'
1401 def test_outcome_Original_py26(self):
1402 self.make_26_result()
1403 self.check_outcome_string_nothing(self.outcome, 'addSuccess')
1405 def test_outcome_Original_py27(self):
1406 self.make_27_result()
1407 self.check_outcome_string(self.outcome)
1409 def test_outcome_Original_pyextended(self):
1410 self.make_extended_result()
1411 self.check_outcome_string(self.outcome)
1413 def test_outcome_Extended_py26(self):
1414 self.make_26_result()
1415 self.check_outcome_string_nothing(self.outcome, 'addSuccess')
1417 def test_outcome_Extended_py27_no_reason(self):
1418 self.make_27_result()
1419 self.check_outcome_details_to_string(self.outcome)
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')])})
1426 def test_outcome_Extended_pyextended(self):
1427 self.make_extended_result()
1428 self.check_outcome_details(self.outcome)
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)))
1437 class TestExtendedToOriginalAddSuccess(
1438 TestExtendedToOriginalResultDecoratorBase):
1440 outcome = 'addSuccess'
1441 expected = 'addSuccess'
1443 def test_outcome_Original_py26(self):
1444 self.make_26_result()
1445 self.check_outcome_nothing(self.outcome, self.expected)
1447 def test_outcome_Original_py27(self):
1448 self.make_27_result()
1449 self.check_outcome_nothing(self.outcome)
1451 def test_outcome_Original_pyextended(self):
1452 self.make_extended_result()
1453 self.check_outcome_nothing(self.outcome)
1455 def test_outcome_Extended_py26(self):
1456 self.make_26_result()
1457 self.check_outcome_details_to_nothing(self.outcome, self.expected)
1459 def test_outcome_Extended_py27(self):
1460 self.make_27_result()
1461 self.check_outcome_details_to_nothing(self.outcome)
1463 def test_outcome_Extended_pyextended(self):
1464 self.make_extended_result()
1465 self.check_outcome_details(self.outcome)
1468 class TestExtendedToOriginalAddUnexpectedSuccess(
1469 TestExtendedToOriginalResultDecoratorBase):
1471 outcome = 'addUnexpectedSuccess'
1472 expected = 'addFailure'
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])
1480 def test_outcome_Original_py27(self):
1481 self.make_27_result()
1482 self.check_outcome_nothing(self.outcome)
1484 def test_outcome_Original_pyextended(self):
1485 self.make_extended_result()
1486 self.check_outcome_nothing(self.outcome)
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])
1494 def test_outcome_Extended_py27(self):
1495 self.make_27_result()
1496 self.check_outcome_details_to_nothing(self.outcome)
1498 def test_outcome_Extended_pyextended(self):
1499 self.make_extended_result()
1500 self.check_outcome_details(self.outcome)
1503 class TestExtendedToOriginalResultOtherAttributes(
1504 TestExtendedToOriginalResultDecoratorBase):
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())
1517 class TestNonAsciiResults(TestCase):
1518 """Test all kinds of tracebacks are cleanly interpreted as unicode
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.
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
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
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()
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()
1559 def _test_external_case(self, testline, coding="ascii", modulelevel="",
1560 suffix=""):
1561 """Create and run a test case in a seperate module"""
1562 self._setup_external_case(testline, coding, modulelevel, suffix)
1563 return self._run_external_case()
1565 def _setup_external_case(self, testline, coding="ascii", modulelevel="",
1566 suffix=""):
1567 """Create a test case in a seperate 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))
1582 def _run_external_case(self):
1583 """Run the prepared test case in a seperate 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()
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])
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)
1611 def _as_output(self, text):
1612 return text
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)
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)
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)
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: "))
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())
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
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)
1816 class TestNonAsciiResultsWithUnittest(TestNonAsciiResults):
1817 """Test that running under unittest produces clean ascii strings"""
1819 def _run(self, stream, test):
1820 from unittest import TextTestRunner as _Runner
1821 return _Runner(stream).run(test)
1823 def _as_output(self, text):
1824 if str_is_unicode:
1825 return text
1826 return text.encode("utf-8")
1829 class TestDetailsToStr(TestCase):
1831 def test_no_details(self):
1832 string = _details_to_str({})
1833 self.assertThat(string, Equals(''))
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 """))
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'))
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'))
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'))
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'))
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 """))
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'),
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
1903 attachment: {{{foo}}}
1904 attachment-3: {{{bar}}}
1906 traceback
1907 """))
1910 class TestByTestResultTests(TestCase):
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)
1919 def assertCalled(self, **kwargs):
1920 defaults = {
1921 'test': self,
1922 'tags': set(),
1923 'details': None,
1924 'start_time': 0,
1925 'stop_time': 1,
1927 defaults.update(kwargs)
1928 self.assertEqual([defaults], self.log)
1930 def on_test(self, **kwargs):
1931 self.log.append(kwargs)
1933 def test_no_tests_nothing_reported(self):
1934 self.result.startTestRun()
1935 self.result.stopTestRun()
1936 self.assertEqual([], self.log)
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')
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)
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']))
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']))
1966 def test_add_error(self):
1967 self.result.startTest(self)
1968 try:
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)})
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)
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)})
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)
2004 def test_add_xfail(self):
2005 self.result.startTest(self)
2006 try:
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)})
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)
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)
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)})
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)
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},
2066 self.log)
2069 class TestTagger(TestCase):
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)
2093 def test_suite():
2094 from unittest import TestLoader
2095 return TestLoader().loadTestsFromName(__name__)