testtools: Import new upstream snapshot.
[tridge/samba.git] / lib / testtools / testtools / tests / test_testresult.py
1 # Copyright (c) 2008-2011 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 import warnings
16
17 from testtools import (
18     ExtendedToOriginalDecorator,
19     MultiTestResult,
20     TestCase,
21     TestResult,
22     TextTestResult,
23     ThreadsafeForwardingResult,
24     testresult,
25     )
26 from testtools.compat import (
27     _b,
28     _get_exception_encoding,
29     _r,
30     _u,
31     str_is_unicode,
32     StringIO,
33     )
34 from testtools.content import (
35     Content,
36     content_from_stream,
37     text_content,
38     )
39 from testtools.content_type import ContentType, UTF8_TEXT
40 from testtools.matchers import (
41     DocTestMatches,
42     Equals,
43     MatchesException,
44     Raises,
45     )
46 from testtools.tests.helpers import (
47     an_exc_info,
48     FullStackRunTest,
49     LoggingResult,
50     run_with_stack_hidden,
51     )
52 from testtools.testresult.doubles import (
53     Python26TestResult,
54     Python27TestResult,
55     ExtendedTestResult,
56     )
57 from testtools.testresult.real import (
58     _details_to_str,
59     utc,
60     )
61
62
63 def make_erroring_test():
64     class Test(TestCase):
65         def error(self):
66             1/0
67     return Test("error")
68
69
70 def make_failing_test():
71     class Test(TestCase):
72         def failed(self):
73             self.fail("yo!")
74     return Test("failed")
75
76
77 def make_unexpectedly_successful_test():
78     class Test(TestCase):
79         def succeeded(self):
80             self.expectFailure("yo!", lambda: None)
81     return Test("succeeded")
82
83
84 def make_test():
85     class Test(TestCase):
86         def test(self):
87             pass
88     return Test("test")
89
90
91 def make_exception_info(exceptionFactory, *args, **kwargs):
92     try:
93         raise exceptionFactory(*args, **kwargs)
94     except:
95         return sys.exc_info()
96
97
98 class Python26Contract(object):
99
100     def test_fresh_result_is_successful(self):
101         # A result is considered successful before any tests are run.
102         result = self.makeResult()
103         self.assertTrue(result.wasSuccessful())
104
105     def test_addError_is_failure(self):
106         # addError fails the test run.
107         result = self.makeResult()
108         result.startTest(self)
109         result.addError(self, an_exc_info)
110         result.stopTest(self)
111         self.assertFalse(result.wasSuccessful())
112
113     def test_addFailure_is_failure(self):
114         # addFailure fails the test run.
115         result = self.makeResult()
116         result.startTest(self)
117         result.addFailure(self, an_exc_info)
118         result.stopTest(self)
119         self.assertFalse(result.wasSuccessful())
120
121     def test_addSuccess_is_success(self):
122         # addSuccess does not fail the test run.
123         result = self.makeResult()
124         result.startTest(self)
125         result.addSuccess(self)
126         result.stopTest(self)
127         self.assertTrue(result.wasSuccessful())
128
129
130 class Python27Contract(Python26Contract):
131
132     def test_addExpectedFailure(self):
133         # Calling addExpectedFailure(test, exc_info) completes ok.
134         result = self.makeResult()
135         result.startTest(self)
136         result.addExpectedFailure(self, an_exc_info)
137
138     def test_addExpectedFailure_is_success(self):
139         # addExpectedFailure does not fail the test run.
140         result = self.makeResult()
141         result.startTest(self)
142         result.addExpectedFailure(self, an_exc_info)
143         result.stopTest(self)
144         self.assertTrue(result.wasSuccessful())
145
146     def test_addSkipped(self):
147         # Calling addSkip(test, reason) completes ok.
148         result = self.makeResult()
149         result.startTest(self)
150         result.addSkip(self, _u("Skipped for some reason"))
151
152     def test_addSkip_is_success(self):
153         # addSkip does not fail the test run.
154         result = self.makeResult()
155         result.startTest(self)
156         result.addSkip(self, _u("Skipped for some reason"))
157         result.stopTest(self)
158         self.assertTrue(result.wasSuccessful())
159
160     def test_addUnexpectedSuccess(self):
161         # Calling addUnexpectedSuccess(test) completes ok.
162         result = self.makeResult()
163         result.startTest(self)
164         result.addUnexpectedSuccess(self)
165
166     def test_addUnexpectedSuccess_was_successful(self):
167         # addUnexpectedSuccess does not fail the test run in Python 2.7.
168         result = self.makeResult()
169         result.startTest(self)
170         result.addUnexpectedSuccess(self)
171         result.stopTest(self)
172         self.assertTrue(result.wasSuccessful())
173
174     def test_startStopTestRun(self):
175         # Calling startTestRun completes ok.
176         result = self.makeResult()
177         result.startTestRun()
178         result.stopTestRun()
179
180
181 class DetailsContract(Python27Contract):
182     """Tests for the contract of TestResults."""
183
184     def test_addExpectedFailure_details(self):
185         # Calling addExpectedFailure(test, details=xxx) completes ok.
186         result = self.makeResult()
187         result.startTest(self)
188         result.addExpectedFailure(self, details={})
189
190     def test_addError_details(self):
191         # Calling addError(test, details=xxx) completes ok.
192         result = self.makeResult()
193         result.startTest(self)
194         result.addError(self, details={})
195
196     def test_addFailure_details(self):
197         # Calling addFailure(test, details=xxx) completes ok.
198         result = self.makeResult()
199         result.startTest(self)
200         result.addFailure(self, details={})
201
202     def test_addSkipped_details(self):
203         # Calling addSkip(test, reason) completes ok.
204         result = self.makeResult()
205         result.startTest(self)
206         result.addSkip(self, details={})
207
208     def test_addUnexpectedSuccess_details(self):
209         # Calling addUnexpectedSuccess(test) completes ok.
210         result = self.makeResult()
211         result.startTest(self)
212         result.addUnexpectedSuccess(self, details={})
213
214     def test_addSuccess_details(self):
215         # Calling addSuccess(test) completes ok.
216         result = self.makeResult()
217         result.startTest(self)
218         result.addSuccess(self, details={})
219
220
221 class FallbackContract(DetailsContract):
222     """When we fallback we take our policy choice to map calls.
223
224     For instance, we map unexpectedSuccess to an error code, not to success.
225     """
226
227     def test_addUnexpectedSuccess_was_successful(self):
228         # addUnexpectedSuccess fails test run in testtools.
229         result = self.makeResult()
230         result.startTest(self)
231         result.addUnexpectedSuccess(self)
232         result.stopTest(self)
233         self.assertFalse(result.wasSuccessful())
234
235
236 class StartTestRunContract(FallbackContract):
237     """Defines the contract for testtools policy choices.
238
239     That is things which are not simply extensions to unittest but choices we
240     have made differently.
241     """
242
243     def test_startTestRun_resets_unexpected_success(self):
244         result = self.makeResult()
245         result.startTest(self)
246         result.addUnexpectedSuccess(self)
247         result.stopTest(self)
248         result.startTestRun()
249         self.assertTrue(result.wasSuccessful())
250
251     def test_startTestRun_resets_failure(self):
252         result = self.makeResult()
253         result.startTest(self)
254         result.addFailure(self, an_exc_info)
255         result.stopTest(self)
256         result.startTestRun()
257         self.assertTrue(result.wasSuccessful())
258
259     def test_startTestRun_resets_errors(self):
260         result = self.makeResult()
261         result.startTest(self)
262         result.addError(self, an_exc_info)
263         result.stopTest(self)
264         result.startTestRun()
265         self.assertTrue(result.wasSuccessful())
266
267
268 class TestTestResultContract(TestCase, StartTestRunContract):
269
270     run_test_with = FullStackRunTest
271
272     def makeResult(self):
273         return TestResult()
274
275
276 class TestMultiTestResultContract(TestCase, StartTestRunContract):
277
278     run_test_with = FullStackRunTest
279
280     def makeResult(self):
281         return MultiTestResult(TestResult(), TestResult())
282
283
284 class TestTextTestResultContract(TestCase, StartTestRunContract):
285
286     run_test_with = FullStackRunTest
287
288     def makeResult(self):
289         return TextTestResult(StringIO())
290
291
292 class TestThreadSafeForwardingResultContract(TestCase, StartTestRunContract):
293
294     run_test_with = FullStackRunTest
295
296     def makeResult(self):
297         result_semaphore = threading.Semaphore(1)
298         target = TestResult()
299         return ThreadsafeForwardingResult(target, result_semaphore)
300
301
302 class TestExtendedTestResultContract(TestCase, StartTestRunContract):
303
304     def makeResult(self):
305         return ExtendedTestResult()
306
307
308 class TestPython26TestResultContract(TestCase, Python26Contract):
309
310     def makeResult(self):
311         return Python26TestResult()
312
313
314 class TestAdaptedPython26TestResultContract(TestCase, FallbackContract):
315
316     def makeResult(self):
317         return ExtendedToOriginalDecorator(Python26TestResult())
318
319
320 class TestPython27TestResultContract(TestCase, Python27Contract):
321
322     def makeResult(self):
323         return Python27TestResult()
324
325
326 class TestAdaptedPython27TestResultContract(TestCase, DetailsContract):
327
328     def makeResult(self):
329         return ExtendedToOriginalDecorator(Python27TestResult())
330
331
332 class TestTestResult(TestCase):
333     """Tests for 'TestResult'."""
334
335     run_tests_with = FullStackRunTest
336
337     def makeResult(self):
338         """Make an arbitrary result for testing."""
339         return TestResult()
340
341     def test_addSkipped(self):
342         # Calling addSkip on a TestResult records the test that was skipped in
343         # its skip_reasons dict.
344         result = self.makeResult()
345         result.addSkip(self, _u("Skipped for some reason"))
346         self.assertEqual({_u("Skipped for some reason"):[self]},
347             result.skip_reasons)
348         result.addSkip(self, _u("Skipped for some reason"))
349         self.assertEqual({_u("Skipped for some reason"):[self, self]},
350             result.skip_reasons)
351         result.addSkip(self, _u("Skipped for another reason"))
352         self.assertEqual({_u("Skipped for some reason"):[self, self],
353             _u("Skipped for another reason"):[self]},
354             result.skip_reasons)
355
356     def test_now_datetime_now(self):
357         result = self.makeResult()
358         olddatetime = testresult.real.datetime
359         def restore():
360             testresult.real.datetime = olddatetime
361         self.addCleanup(restore)
362         class Module:
363             pass
364         now = datetime.datetime.now(utc)
365         stubdatetime = Module()
366         stubdatetime.datetime = Module()
367         stubdatetime.datetime.now = lambda tz: now
368         testresult.real.datetime = stubdatetime
369         # Calling _now() looks up the time.
370         self.assertEqual(now, result._now())
371         then = now + datetime.timedelta(0, 1)
372         # Set an explicit datetime, which gets returned from then on.
373         result.time(then)
374         self.assertNotEqual(now, result._now())
375         self.assertEqual(then, result._now())
376         # go back to looking it up.
377         result.time(None)
378         self.assertEqual(now, result._now())
379
380     def test_now_datetime_time(self):
381         result = self.makeResult()
382         now = datetime.datetime.now(utc)
383         result.time(now)
384         self.assertEqual(now, result._now())
385
386     def test_traceback_formatting_without_stack_hidden(self):
387         # During the testtools test run, we show our levels of the stack,
388         # because we want to be able to use our test suite to debug our own
389         # code.
390         result = self.makeResult()
391         test = make_erroring_test()
392         test.run(result)
393         self.assertThat(
394             result.errors[0][1],
395             DocTestMatches(
396                 'Traceback (most recent call last):\n'
397                 '  File "...testtools...runtest.py", line ..., in _run_user\n'
398                 '    return fn(*args, **kwargs)\n'
399                 '  File "...testtools...testcase.py", line ..., in _run_test_method\n'
400                 '    return self._get_test_method()()\n'
401                 '  File "...testtools...tests...test_testresult.py", line ..., in error\n'
402                 '    1/0\n'
403                 'ZeroDivisionError: ...\n',
404                 doctest.ELLIPSIS | doctest.REPORT_UDIFF))
405
406     def test_traceback_formatting_with_stack_hidden(self):
407         result = self.makeResult()
408         test = make_erroring_test()
409         run_with_stack_hidden(True, test.run, result)
410         self.assertThat(
411             result.errors[0][1],
412             DocTestMatches(
413                 'Traceback (most recent call last):\n'
414                 '  File "...testtools...tests...test_testresult.py", line ..., in error\n'
415                 '    1/0\n'
416                 'ZeroDivisionError: ...\n',
417                 doctest.ELLIPSIS))
418
419
420 class TestMultiTestResult(TestCase):
421     """Tests for 'MultiTestResult'."""
422
423     def setUp(self):
424         super(TestMultiTestResult, self).setUp()
425         self.result1 = LoggingResult([])
426         self.result2 = LoggingResult([])
427         self.multiResult = MultiTestResult(self.result1, self.result2)
428
429     def assertResultLogsEqual(self, expectedEvents):
430         """Assert that our test results have received the expected events."""
431         self.assertEqual(expectedEvents, self.result1._events)
432         self.assertEqual(expectedEvents, self.result2._events)
433
434     def test_repr(self):
435         self.assertEqual(
436             '<MultiTestResult (%r, %r)>' % (
437                 ExtendedToOriginalDecorator(self.result1),
438                 ExtendedToOriginalDecorator(self.result2)),
439             repr(self.multiResult))
440
441     def test_empty(self):
442         # Initializing a `MultiTestResult` doesn't do anything to its
443         # `TestResult`s.
444         self.assertResultLogsEqual([])
445
446     def test_startTest(self):
447         # Calling `startTest` on a `MultiTestResult` calls `startTest` on all
448         # its `TestResult`s.
449         self.multiResult.startTest(self)
450         self.assertResultLogsEqual([('startTest', self)])
451
452     def test_stopTest(self):
453         # Calling `stopTest` on a `MultiTestResult` calls `stopTest` on all
454         # its `TestResult`s.
455         self.multiResult.stopTest(self)
456         self.assertResultLogsEqual([('stopTest', self)])
457
458     def test_addSkipped(self):
459         # Calling `addSkip` on a `MultiTestResult` calls addSkip on its
460         # results.
461         reason = _u("Skipped for some reason")
462         self.multiResult.addSkip(self, reason)
463         self.assertResultLogsEqual([('addSkip', self, reason)])
464
465     def test_addSuccess(self):
466         # Calling `addSuccess` on a `MultiTestResult` calls `addSuccess` on
467         # all its `TestResult`s.
468         self.multiResult.addSuccess(self)
469         self.assertResultLogsEqual([('addSuccess', self)])
470
471     def test_done(self):
472         # Calling `done` on a `MultiTestResult` calls `done` on all its
473         # `TestResult`s.
474         self.multiResult.done()
475         self.assertResultLogsEqual([('done')])
476
477     def test_addFailure(self):
478         # Calling `addFailure` on a `MultiTestResult` calls `addFailure` on
479         # all its `TestResult`s.
480         exc_info = make_exception_info(AssertionError, 'failure')
481         self.multiResult.addFailure(self, exc_info)
482         self.assertResultLogsEqual([('addFailure', self, exc_info)])
483
484     def test_addError(self):
485         # Calling `addError` on a `MultiTestResult` calls `addError` on all
486         # its `TestResult`s.
487         exc_info = make_exception_info(RuntimeError, 'error')
488         self.multiResult.addError(self, exc_info)
489         self.assertResultLogsEqual([('addError', self, exc_info)])
490
491     def test_startTestRun(self):
492         # Calling `startTestRun` on a `MultiTestResult` forwards to all its
493         # `TestResult`s.
494         self.multiResult.startTestRun()
495         self.assertResultLogsEqual([('startTestRun')])
496
497     def test_stopTestRun(self):
498         # Calling `stopTestRun` on a `MultiTestResult` forwards to all its
499         # `TestResult`s.
500         self.multiResult.stopTestRun()
501         self.assertResultLogsEqual([('stopTestRun')])
502
503     def test_stopTestRun_returns_results(self):
504         # `MultiTestResult.stopTestRun` returns a tuple of all of the return
505         # values the `stopTestRun`s that it forwards to.
506         class Result(LoggingResult):
507             def stopTestRun(self):
508                 super(Result, self).stopTestRun()
509                 return 'foo'
510         multi_result = MultiTestResult(Result([]), Result([]))
511         result = multi_result.stopTestRun()
512         self.assertEqual(('foo', 'foo'), result)
513
514     def test_time(self):
515         # the time call is dispatched, not eaten by the base class
516         self.multiResult.time('foo')
517         self.assertResultLogsEqual([('time', 'foo')])
518
519
520 class TestTextTestResult(TestCase):
521     """Tests for 'TextTestResult'."""
522
523     def setUp(self):
524         super(TestTextTestResult, self).setUp()
525         self.result = TextTestResult(StringIO())
526
527     def getvalue(self):
528         return self.result.stream.getvalue()
529
530     def test__init_sets_stream(self):
531         result = TextTestResult("fp")
532         self.assertEqual("fp", result.stream)
533
534     def reset_output(self):
535         self.result.stream = StringIO()
536
537     def test_startTestRun(self):
538         self.result.startTestRun()
539         self.assertEqual("Tests running...\n", self.getvalue())
540
541     def test_stopTestRun_count_many(self):
542         test = make_test()
543         self.result.startTestRun()
544         self.result.startTest(test)
545         self.result.stopTest(test)
546         self.result.startTest(test)
547         self.result.stopTest(test)
548         self.result.stream = StringIO()
549         self.result.stopTestRun()
550         self.assertThat(self.getvalue(),
551             DocTestMatches("\nRan 2 tests in ...s\n...", doctest.ELLIPSIS))
552
553     def test_stopTestRun_count_single(self):
554         test = make_test()
555         self.result.startTestRun()
556         self.result.startTest(test)
557         self.result.stopTest(test)
558         self.reset_output()
559         self.result.stopTestRun()
560         self.assertThat(self.getvalue(),
561             DocTestMatches("\nRan 1 test in ...s\nOK\n", doctest.ELLIPSIS))
562
563     def test_stopTestRun_count_zero(self):
564         self.result.startTestRun()
565         self.reset_output()
566         self.result.stopTestRun()
567         self.assertThat(self.getvalue(),
568             DocTestMatches("\nRan 0 tests in ...s\nOK\n", doctest.ELLIPSIS))
569
570     def test_stopTestRun_current_time(self):
571         test = make_test()
572         now = datetime.datetime.now(utc)
573         self.result.time(now)
574         self.result.startTestRun()
575         self.result.startTest(test)
576         now = now + datetime.timedelta(0, 0, 0, 1)
577         self.result.time(now)
578         self.result.stopTest(test)
579         self.reset_output()
580         self.result.stopTestRun()
581         self.assertThat(self.getvalue(),
582             DocTestMatches("... in 0.001s\n...", doctest.ELLIPSIS))
583
584     def test_stopTestRun_successful(self):
585         self.result.startTestRun()
586         self.result.stopTestRun()
587         self.assertThat(self.getvalue(),
588             DocTestMatches("...\nOK\n", doctest.ELLIPSIS))
589
590     def test_stopTestRun_not_successful_failure(self):
591         test = make_failing_test()
592         self.result.startTestRun()
593         test.run(self.result)
594         self.result.stopTestRun()
595         self.assertThat(self.getvalue(),
596             DocTestMatches("...\nFAILED (failures=1)\n", doctest.ELLIPSIS))
597
598     def test_stopTestRun_not_successful_error(self):
599         test = make_erroring_test()
600         self.result.startTestRun()
601         test.run(self.result)
602         self.result.stopTestRun()
603         self.assertThat(self.getvalue(),
604             DocTestMatches("...\nFAILED (failures=1)\n", doctest.ELLIPSIS))
605
606     def test_stopTestRun_not_successful_unexpected_success(self):
607         test = make_unexpectedly_successful_test()
608         self.result.startTestRun()
609         test.run(self.result)
610         self.result.stopTestRun()
611         self.assertThat(self.getvalue(),
612             DocTestMatches("...\nFAILED (failures=1)\n", doctest.ELLIPSIS))
613
614     def test_stopTestRun_shows_details(self):
615         def run_tests():
616             self.result.startTestRun()
617             make_erroring_test().run(self.result)
618             make_unexpectedly_successful_test().run(self.result)
619             make_failing_test().run(self.result)
620             self.reset_output()
621             self.result.stopTestRun()
622         run_with_stack_hidden(True, run_tests)
623         self.assertThat(self.getvalue(),
624             DocTestMatches("""...======================================================================
625 ERROR: testtools.tests.test_testresult.Test.error
626 ----------------------------------------------------------------------
627 Traceback (most recent call last):
628   File "...testtools...tests...test_testresult.py", line ..., in error
629     1/0
630 ZeroDivisionError:... divi... by zero...
631 ======================================================================
632 FAIL: testtools.tests.test_testresult.Test.failed
633 ----------------------------------------------------------------------
634 Traceback (most recent call last):
635   File "...testtools...tests...test_testresult.py", line ..., in failed
636     self.fail("yo!")
637 AssertionError: yo!
638 ======================================================================
639 UNEXPECTED SUCCESS: testtools.tests.test_testresult.Test.succeeded
640 ----------------------------------------------------------------------
641 ...""", doctest.ELLIPSIS | doctest.REPORT_NDIFF))
642
643
644 class TestThreadSafeForwardingResult(TestCase):
645     """Tests for `TestThreadSafeForwardingResult`."""
646
647     def setUp(self):
648         super(TestThreadSafeForwardingResult, self).setUp()
649         self.result_semaphore = threading.Semaphore(1)
650         self.target = LoggingResult([])
651         self.result1 = ThreadsafeForwardingResult(self.target,
652             self.result_semaphore)
653
654     def test_nonforwarding_methods(self):
655         # startTest and stopTest are not forwarded because they need to be
656         # batched.
657         self.result1.startTest(self)
658         self.result1.stopTest(self)
659         self.assertEqual([], self.target._events)
660
661     def test_startTestRun(self):
662         self.result1.startTestRun()
663         self.result2 = ThreadsafeForwardingResult(self.target,
664             self.result_semaphore)
665         self.result2.startTestRun()
666         self.assertEqual(["startTestRun", "startTestRun"], self.target._events)
667
668     def test_stopTestRun(self):
669         self.result1.stopTestRun()
670         self.result2 = ThreadsafeForwardingResult(self.target,
671             self.result_semaphore)
672         self.result2.stopTestRun()
673         self.assertEqual(["stopTestRun", "stopTestRun"], self.target._events)
674
675     def test_forwarding_methods(self):
676         # error, failure, skip and success are forwarded in batches.
677         exc_info1 = make_exception_info(RuntimeError, 'error')
678         starttime1 = datetime.datetime.utcfromtimestamp(1.489)
679         endtime1 = datetime.datetime.utcfromtimestamp(51.476)
680         self.result1.time(starttime1)
681         self.result1.startTest(self)
682         self.result1.time(endtime1)
683         self.result1.addError(self, exc_info1)
684         exc_info2 = make_exception_info(AssertionError, 'failure')
685         starttime2 = datetime.datetime.utcfromtimestamp(2.489)
686         endtime2 = datetime.datetime.utcfromtimestamp(3.476)
687         self.result1.time(starttime2)
688         self.result1.startTest(self)
689         self.result1.time(endtime2)
690         self.result1.addFailure(self, exc_info2)
691         reason = _u("Skipped for some reason")
692         starttime3 = datetime.datetime.utcfromtimestamp(4.489)
693         endtime3 = datetime.datetime.utcfromtimestamp(5.476)
694         self.result1.time(starttime3)
695         self.result1.startTest(self)
696         self.result1.time(endtime3)
697         self.result1.addSkip(self, reason)
698         starttime4 = datetime.datetime.utcfromtimestamp(6.489)
699         endtime4 = datetime.datetime.utcfromtimestamp(7.476)
700         self.result1.time(starttime4)
701         self.result1.startTest(self)
702         self.result1.time(endtime4)
703         self.result1.addSuccess(self)
704         self.assertEqual([
705             ('time', starttime1),
706             ('startTest', self),
707             ('time', endtime1),
708             ('addError', self, exc_info1),
709             ('stopTest', self),
710             ('time', starttime2),
711             ('startTest', self),
712             ('time', endtime2),
713             ('addFailure', self, exc_info2),
714             ('stopTest', self),
715             ('time', starttime3),
716             ('startTest', self),
717             ('time', endtime3),
718             ('addSkip', self, reason),
719             ('stopTest', self),
720             ('time', starttime4),
721             ('startTest', self),
722             ('time', endtime4),
723             ('addSuccess', self),
724             ('stopTest', self),
725             ], self.target._events)
726
727
728 class TestExtendedToOriginalResultDecoratorBase(TestCase):
729
730     def make_26_result(self):
731         self.result = Python26TestResult()
732         self.make_converter()
733
734     def make_27_result(self):
735         self.result = Python27TestResult()
736         self.make_converter()
737
738     def make_converter(self):
739         self.converter = ExtendedToOriginalDecorator(self.result)
740
741     def make_extended_result(self):
742         self.result = ExtendedTestResult()
743         self.make_converter()
744
745     def check_outcome_details(self, outcome):
746         """Call an outcome with a details dict to be passed through."""
747         # This dict is /not/ convertible - thats deliberate, as it should
748         # not hit the conversion code path.
749         details = {'foo': 'bar'}
750         getattr(self.converter, outcome)(self, details=details)
751         self.assertEqual([(outcome, self, details)], self.result._events)
752
753     def get_details_and_string(self):
754         """Get a details dict and expected string."""
755         text1 = lambda: [_b("1\n2\n")]
756         text2 = lambda: [_b("3\n4\n")]
757         bin1 = lambda: [_b("5\n")]
758         details = {'text 1': Content(ContentType('text', 'plain'), text1),
759             'text 2': Content(ContentType('text', 'strange'), text2),
760             'bin 1': Content(ContentType('application', 'binary'), bin1)}
761         return (details,
762                 ("Binary content:\n"
763                  "  bin 1 (application/binary)\n"
764                  "\n"
765                  "text 1: {{{\n"
766                  "1\n"
767                  "2\n"
768                  "}}}\n"
769                  "\n"
770                  "text 2: {{{\n"
771                  "3\n"
772                  "4\n"
773                  "}}}\n"))
774
775     def check_outcome_details_to_exec_info(self, outcome, expected=None):
776         """Call an outcome with a details dict to be made into exc_info."""
777         # The conversion is a done using RemoteError and the string contents
778         # of the text types in the details dict.
779         if not expected:
780             expected = outcome
781         details, err_str = self.get_details_and_string()
782         getattr(self.converter, outcome)(self, details=details)
783         err = self.converter._details_to_exc_info(details)
784         self.assertEqual([(expected, self, err)], self.result._events)
785
786     def check_outcome_details_to_nothing(self, outcome, expected=None):
787         """Call an outcome with a details dict to be swallowed."""
788         if not expected:
789             expected = outcome
790         details = {'foo': 'bar'}
791         getattr(self.converter, outcome)(self, details=details)
792         self.assertEqual([(expected, self)], self.result._events)
793
794     def check_outcome_details_to_string(self, outcome):
795         """Call an outcome with a details dict to be stringified."""
796         details, err_str = self.get_details_and_string()
797         getattr(self.converter, outcome)(self, details=details)
798         self.assertEqual([(outcome, self, err_str)], self.result._events)
799
800     def check_outcome_details_to_arg(self, outcome, arg, extra_detail=None):
801         """Call an outcome with a details dict to have an arg extracted."""
802         details, _ = self.get_details_and_string()
803         if extra_detail:
804             details.update(extra_detail)
805         getattr(self.converter, outcome)(self, details=details)
806         self.assertEqual([(outcome, self, arg)], self.result._events)
807
808     def check_outcome_exc_info(self, outcome, expected=None):
809         """Check that calling a legacy outcome still works."""
810         # calling some outcome with the legacy exc_info style api (no keyword
811         # parameters) gets passed through.
812         if not expected:
813             expected = outcome
814         err = sys.exc_info()
815         getattr(self.converter, outcome)(self, err)
816         self.assertEqual([(expected, self, err)], self.result._events)
817
818     def check_outcome_exc_info_to_nothing(self, outcome, expected=None):
819         """Check that calling a legacy outcome on a fallback works."""
820         # calling some outcome with the legacy exc_info style api (no keyword
821         # parameters) gets passed through.
822         if not expected:
823             expected = outcome
824         err = sys.exc_info()
825         getattr(self.converter, outcome)(self, err)
826         self.assertEqual([(expected, self)], self.result._events)
827
828     def check_outcome_nothing(self, outcome, expected=None):
829         """Check that calling a legacy outcome still works."""
830         if not expected:
831             expected = outcome
832         getattr(self.converter, outcome)(self)
833         self.assertEqual([(expected, self)], self.result._events)
834
835     def check_outcome_string_nothing(self, outcome, expected):
836         """Check that calling outcome with a string calls expected."""
837         getattr(self.converter, outcome)(self, "foo")
838         self.assertEqual([(expected, self)], self.result._events)
839
840     def check_outcome_string(self, outcome):
841         """Check that calling outcome with a string works."""
842         getattr(self.converter, outcome)(self, "foo")
843         self.assertEqual([(outcome, self, "foo")], self.result._events)
844
845
846 class TestExtendedToOriginalResultDecorator(
847     TestExtendedToOriginalResultDecoratorBase):
848
849     def test_progress_py26(self):
850         self.make_26_result()
851         self.converter.progress(1, 2)
852
853     def test_progress_py27(self):
854         self.make_27_result()
855         self.converter.progress(1, 2)
856
857     def test_progress_pyextended(self):
858         self.make_extended_result()
859         self.converter.progress(1, 2)
860         self.assertEqual([('progress', 1, 2)], self.result._events)
861
862     def test_shouldStop(self):
863         self.make_26_result()
864         self.assertEqual(False, self.converter.shouldStop)
865         self.converter.decorated.stop()
866         self.assertEqual(True, self.converter.shouldStop)
867
868     def test_startTest_py26(self):
869         self.make_26_result()
870         self.converter.startTest(self)
871         self.assertEqual([('startTest', self)], self.result._events)
872
873     def test_startTest_py27(self):
874         self.make_27_result()
875         self.converter.startTest(self)
876         self.assertEqual([('startTest', self)], self.result._events)
877
878     def test_startTest_pyextended(self):
879         self.make_extended_result()
880         self.converter.startTest(self)
881         self.assertEqual([('startTest', self)], self.result._events)
882
883     def test_startTestRun_py26(self):
884         self.make_26_result()
885         self.converter.startTestRun()
886         self.assertEqual([], self.result._events)
887
888     def test_startTestRun_py27(self):
889         self.make_27_result()
890         self.converter.startTestRun()
891         self.assertEqual([('startTestRun',)], self.result._events)
892
893     def test_startTestRun_pyextended(self):
894         self.make_extended_result()
895         self.converter.startTestRun()
896         self.assertEqual([('startTestRun',)], self.result._events)
897
898     def test_stopTest_py26(self):
899         self.make_26_result()
900         self.converter.stopTest(self)
901         self.assertEqual([('stopTest', self)], self.result._events)
902
903     def test_stopTest_py27(self):
904         self.make_27_result()
905         self.converter.stopTest(self)
906         self.assertEqual([('stopTest', self)], self.result._events)
907
908     def test_stopTest_pyextended(self):
909         self.make_extended_result()
910         self.converter.stopTest(self)
911         self.assertEqual([('stopTest', self)], self.result._events)
912
913     def test_stopTestRun_py26(self):
914         self.make_26_result()
915         self.converter.stopTestRun()
916         self.assertEqual([], self.result._events)
917
918     def test_stopTestRun_py27(self):
919         self.make_27_result()
920         self.converter.stopTestRun()
921         self.assertEqual([('stopTestRun',)], self.result._events)
922
923     def test_stopTestRun_pyextended(self):
924         self.make_extended_result()
925         self.converter.stopTestRun()
926         self.assertEqual([('stopTestRun',)], self.result._events)
927
928     def test_tags_py26(self):
929         self.make_26_result()
930         self.converter.tags(1, 2)
931
932     def test_tags_py27(self):
933         self.make_27_result()
934         self.converter.tags(1, 2)
935
936     def test_tags_pyextended(self):
937         self.make_extended_result()
938         self.converter.tags(1, 2)
939         self.assertEqual([('tags', 1, 2)], self.result._events)
940
941     def test_time_py26(self):
942         self.make_26_result()
943         self.converter.time(1)
944
945     def test_time_py27(self):
946         self.make_27_result()
947         self.converter.time(1)
948
949     def test_time_pyextended(self):
950         self.make_extended_result()
951         self.converter.time(1)
952         self.assertEqual([('time', 1)], self.result._events)
953
954
955 class TestExtendedToOriginalAddError(TestExtendedToOriginalResultDecoratorBase):
956
957     outcome = 'addError'
958
959     def test_outcome_Original_py26(self):
960         self.make_26_result()
961         self.check_outcome_exc_info(self.outcome)
962
963     def test_outcome_Original_py27(self):
964         self.make_27_result()
965         self.check_outcome_exc_info(self.outcome)
966
967     def test_outcome_Original_pyextended(self):
968         self.make_extended_result()
969         self.check_outcome_exc_info(self.outcome)
970
971     def test_outcome_Extended_py26(self):
972         self.make_26_result()
973         self.check_outcome_details_to_exec_info(self.outcome)
974
975     def test_outcome_Extended_py27(self):
976         self.make_27_result()
977         self.check_outcome_details_to_exec_info(self.outcome)
978
979     def test_outcome_Extended_pyextended(self):
980         self.make_extended_result()
981         self.check_outcome_details(self.outcome)
982
983     def test_outcome__no_details(self):
984         self.make_extended_result()
985         self.assertThat(
986             lambda: getattr(self.converter, self.outcome)(self),
987             Raises(MatchesException(ValueError)))
988
989
990 class TestExtendedToOriginalAddFailure(
991     TestExtendedToOriginalAddError):
992
993     outcome = 'addFailure'
994
995
996 class TestExtendedToOriginalAddExpectedFailure(
997     TestExtendedToOriginalAddError):
998
999     outcome = 'addExpectedFailure'
1000
1001     def test_outcome_Original_py26(self):
1002         self.make_26_result()
1003         self.check_outcome_exc_info_to_nothing(self.outcome, 'addSuccess')
1004
1005     def test_outcome_Extended_py26(self):
1006         self.make_26_result()
1007         self.check_outcome_details_to_nothing(self.outcome, 'addSuccess')
1008
1009
1010
1011 class TestExtendedToOriginalAddSkip(
1012     TestExtendedToOriginalResultDecoratorBase):
1013
1014     outcome = 'addSkip'
1015
1016     def test_outcome_Original_py26(self):
1017         self.make_26_result()
1018         self.check_outcome_string_nothing(self.outcome, 'addSuccess')
1019
1020     def test_outcome_Original_py27(self):
1021         self.make_27_result()
1022         self.check_outcome_string(self.outcome)
1023
1024     def test_outcome_Original_pyextended(self):
1025         self.make_extended_result()
1026         self.check_outcome_string(self.outcome)
1027
1028     def test_outcome_Extended_py26(self):
1029         self.make_26_result()
1030         self.check_outcome_string_nothing(self.outcome, 'addSuccess')
1031
1032     def test_outcome_Extended_py27_no_reason(self):
1033         self.make_27_result()
1034         self.check_outcome_details_to_string(self.outcome)
1035
1036     def test_outcome_Extended_py27_reason(self):
1037         self.make_27_result()
1038         self.check_outcome_details_to_arg(self.outcome, 'foo',
1039             {'reason': Content(UTF8_TEXT, lambda:[_b('foo')])})
1040
1041     def test_outcome_Extended_pyextended(self):
1042         self.make_extended_result()
1043         self.check_outcome_details(self.outcome)
1044
1045     def test_outcome__no_details(self):
1046         self.make_extended_result()
1047         self.assertThat(
1048             lambda: getattr(self.converter, self.outcome)(self),
1049             Raises(MatchesException(ValueError)))
1050
1051
1052 class TestExtendedToOriginalAddSuccess(
1053     TestExtendedToOriginalResultDecoratorBase):
1054
1055     outcome = 'addSuccess'
1056     expected = 'addSuccess'
1057
1058     def test_outcome_Original_py26(self):
1059         self.make_26_result()
1060         self.check_outcome_nothing(self.outcome, self.expected)
1061
1062     def test_outcome_Original_py27(self):
1063         self.make_27_result()
1064         self.check_outcome_nothing(self.outcome)
1065
1066     def test_outcome_Original_pyextended(self):
1067         self.make_extended_result()
1068         self.check_outcome_nothing(self.outcome)
1069
1070     def test_outcome_Extended_py26(self):
1071         self.make_26_result()
1072         self.check_outcome_details_to_nothing(self.outcome, self.expected)
1073
1074     def test_outcome_Extended_py27(self):
1075         self.make_27_result()
1076         self.check_outcome_details_to_nothing(self.outcome)
1077
1078     def test_outcome_Extended_pyextended(self):
1079         self.make_extended_result()
1080         self.check_outcome_details(self.outcome)
1081
1082
1083 class TestExtendedToOriginalAddUnexpectedSuccess(
1084     TestExtendedToOriginalResultDecoratorBase):
1085
1086     outcome = 'addUnexpectedSuccess'
1087     expected = 'addFailure'
1088
1089     def test_outcome_Original_py26(self):
1090         self.make_26_result()
1091         getattr(self.converter, self.outcome)(self)
1092         [event] = self.result._events
1093         self.assertEqual((self.expected, self), event[:2])
1094
1095     def test_outcome_Original_py27(self):
1096         self.make_27_result()
1097         self.check_outcome_nothing(self.outcome)
1098
1099     def test_outcome_Original_pyextended(self):
1100         self.make_extended_result()
1101         self.check_outcome_nothing(self.outcome)
1102
1103     def test_outcome_Extended_py26(self):
1104         self.make_26_result()
1105         getattr(self.converter, self.outcome)(self)
1106         [event] = self.result._events
1107         self.assertEqual((self.expected, self), event[:2])
1108
1109     def test_outcome_Extended_py27(self):
1110         self.make_27_result()
1111         self.check_outcome_details_to_nothing(self.outcome)
1112
1113     def test_outcome_Extended_pyextended(self):
1114         self.make_extended_result()
1115         self.check_outcome_details(self.outcome)
1116
1117
1118 class TestExtendedToOriginalResultOtherAttributes(
1119     TestExtendedToOriginalResultDecoratorBase):
1120
1121     def test_other_attribute(self):
1122         class OtherExtendedResult:
1123             def foo(self):
1124                 return 2
1125             bar = 1
1126         self.result = OtherExtendedResult()
1127         self.make_converter()
1128         self.assertEqual(1, self.converter.bar)
1129         self.assertEqual(2, self.converter.foo())
1130
1131
1132 class TestNonAsciiResults(TestCase):
1133     """Test all kinds of tracebacks are cleanly interpreted as unicode
1134
1135     Currently only uses weak "contains" assertions, would be good to be much
1136     stricter about the expected output. This would add a few failures for the
1137     current release of IronPython for instance, which gets some traceback
1138     lines muddled.
1139     """
1140
1141     _sample_texts = (
1142         _u("pa\u026a\u03b8\u0259n"), # Unicode encodings only
1143         _u("\u5357\u7121"), # In ISO 2022 encodings
1144         _u("\xa7\xa7\xa7"), # In ISO 8859 encodings
1145         )
1146     # Everything but Jython shows syntax errors on the current character
1147     _error_on_character = os.name != "java"
1148
1149     def _run(self, stream, test):
1150         """Run the test, the same as in testtools.run but not to stdout"""
1151         result = TextTestResult(stream)
1152         result.startTestRun()
1153         try:
1154             return test.run(result)
1155         finally:
1156             result.stopTestRun()
1157
1158     def _write_module(self, name, encoding, contents):
1159         """Create Python module on disk with contents in given encoding"""
1160         try:
1161             # Need to pre-check that the coding is valid or codecs.open drops
1162             # the file without closing it which breaks non-refcounted pythons
1163             codecs.lookup(encoding)
1164         except LookupError:
1165             self.skip("Encoding unsupported by implementation: %r" % encoding)
1166         f = codecs.open(os.path.join(self.dir, name + ".py"), "w", encoding)
1167         try:
1168             f.write(contents)
1169         finally:
1170             f.close()
1171
1172     def _test_external_case(self, testline, coding="ascii", modulelevel="",
1173             suffix=""):
1174         """Create and run a test case in a seperate module"""
1175         self._setup_external_case(testline, coding, modulelevel, suffix)
1176         return self._run_external_case()
1177
1178     def _setup_external_case(self, testline, coding="ascii", modulelevel="",
1179             suffix=""):
1180         """Create a test case in a seperate module"""
1181         _, prefix, self.modname = self.id().rsplit(".", 2)
1182         self.dir = tempfile.mkdtemp(prefix=prefix, suffix=suffix)
1183         self.addCleanup(shutil.rmtree, self.dir)
1184         self._write_module(self.modname, coding,
1185             # Older Python 2 versions don't see a coding declaration in a
1186             # docstring so it has to be in a comment, but then we can't
1187             # workaround bug: <http://ironpython.codeplex.com/workitem/26940>
1188             "# coding: %s\n"
1189             "import testtools\n"
1190             "%s\n"
1191             "class Test(testtools.TestCase):\n"
1192             "    def runTest(self):\n"
1193             "        %s\n" % (coding, modulelevel, testline))
1194
1195     def _run_external_case(self):
1196         """Run the prepared test case in a seperate module"""
1197         sys.path.insert(0, self.dir)
1198         self.addCleanup(sys.path.remove, self.dir)
1199         module = __import__(self.modname)
1200         self.addCleanup(sys.modules.pop, self.modname)
1201         stream = StringIO()
1202         self._run(stream, module.Test())
1203         return stream.getvalue()
1204
1205     def _silence_deprecation_warnings(self):
1206         """Shut up DeprecationWarning for this test only"""
1207         warnings.simplefilter("ignore", DeprecationWarning)
1208         self.addCleanup(warnings.filters.remove, warnings.filters[0])
1209
1210     def _get_sample_text(self, encoding="unicode_internal"):
1211         if encoding is None and str_is_unicode:
1212            encoding = "unicode_internal"
1213         for u in self._sample_texts:
1214             try:
1215                 b = u.encode(encoding)
1216                 if u == b.decode(encoding):
1217                    if str_is_unicode:
1218                        return u, u
1219                    return u, b
1220             except (LookupError, UnicodeError):
1221                 pass
1222         self.skip("Could not find a sample text for encoding: %r" % encoding)
1223
1224     def _as_output(self, text):
1225         return text
1226
1227     def test_non_ascii_failure_string(self):
1228         """Assertion contents can be non-ascii and should get decoded"""
1229         text, raw = self._get_sample_text(_get_exception_encoding())
1230         textoutput = self._test_external_case("self.fail(%s)" % _r(raw))
1231         self.assertIn(self._as_output(text), textoutput)
1232
1233     def test_non_ascii_failure_string_via_exec(self):
1234         """Assertion via exec can be non-ascii and still gets decoded"""
1235         text, raw = self._get_sample_text(_get_exception_encoding())
1236         textoutput = self._test_external_case(
1237             testline='exec ("self.fail(%s)")' % _r(raw))
1238         self.assertIn(self._as_output(text), textoutput)
1239
1240     def test_control_characters_in_failure_string(self):
1241         """Control characters in assertions should be escaped"""
1242         textoutput = self._test_external_case("self.fail('\\a\\a\\a')")
1243         self.expectFailure("Defense against the beeping horror unimplemented",
1244             self.assertNotIn, self._as_output("\a\a\a"), textoutput)
1245         self.assertIn(self._as_output(_u("\uFFFD\uFFFD\uFFFD")), textoutput)
1246
1247     def test_os_error(self):
1248         """Locale error messages from the OS shouldn't break anything"""
1249         textoutput = self._test_external_case(
1250             modulelevel="import os",
1251             testline="os.mkdir('/')")
1252         if os.name != "nt" or sys.version_info < (2, 5):
1253             self.assertIn(self._as_output("OSError: "), textoutput)
1254         else:
1255             self.assertIn(self._as_output("WindowsError: "), textoutput)
1256
1257     def test_assertion_text_shift_jis(self):
1258         """A terminal raw backslash in an encoded string is weird but fine"""
1259         example_text = _u("\u5341")
1260         textoutput = self._test_external_case(
1261             coding="shift_jis",
1262             testline="self.fail('%s')" % example_text)
1263         if str_is_unicode:
1264             output_text = example_text
1265         else:
1266             output_text = example_text.encode("shift_jis").decode(
1267                 _get_exception_encoding(), "replace")
1268         self.assertIn(self._as_output("AssertionError: %s" % output_text),
1269             textoutput)
1270
1271     def test_file_comment_iso2022_jp(self):
1272         """Control character escapes must be preserved if valid encoding"""
1273         example_text, _ = self._get_sample_text("iso2022_jp")
1274         textoutput = self._test_external_case(
1275             coding="iso2022_jp",
1276             testline="self.fail('Simple') # %s" % example_text)
1277         self.assertIn(self._as_output(example_text), textoutput)
1278
1279     def test_unicode_exception(self):
1280         """Exceptions that can be formated losslessly as unicode should be"""
1281         example_text, _ = self._get_sample_text()
1282         exception_class = (
1283             "class FancyError(Exception):\n"
1284             # A __unicode__ method does nothing on py3k but the default works
1285             "    def __unicode__(self):\n"
1286             "        return self.args[0]\n")
1287         textoutput = self._test_external_case(
1288             modulelevel=exception_class,
1289             testline="raise FancyError(%s)" % _r(example_text))
1290         self.assertIn(self._as_output(example_text), textoutput)
1291
1292     def test_unprintable_exception(self):
1293         """A totally useless exception instance still prints something"""
1294         exception_class = (
1295             "class UnprintableError(Exception):\n"
1296             "    def __str__(self):\n"
1297             "        raise RuntimeError\n"
1298             "    def __unicode__(self):\n"
1299             "        raise RuntimeError\n"
1300             "    def __repr__(self):\n"
1301             "        raise RuntimeError\n")
1302         textoutput = self._test_external_case(
1303             modulelevel=exception_class,
1304             testline="raise UnprintableError")
1305         self.assertIn(self._as_output(
1306             "UnprintableError: <unprintable UnprintableError object>\n"),
1307             textoutput)
1308
1309     def test_string_exception(self):
1310         """Raise a string rather than an exception instance if supported"""
1311         if sys.version_info > (2, 6):
1312             self.skip("No string exceptions in Python 2.6 or later")
1313         elif sys.version_info > (2, 5):
1314             self._silence_deprecation_warnings()
1315         textoutput = self._test_external_case(testline="raise 'plain str'")
1316         self.assertIn(self._as_output("\nplain str\n"), textoutput)
1317
1318     def test_non_ascii_dirname(self):
1319         """Script paths in the traceback can be non-ascii"""
1320         text, raw = self._get_sample_text(sys.getfilesystemencoding())
1321         textoutput = self._test_external_case(
1322             # Avoid bug in Python 3 by giving a unicode source encoding rather
1323             # than just ascii which raises a SyntaxError with no other details
1324             coding="utf-8",
1325             testline="self.fail('Simple')",
1326             suffix=raw)
1327         self.assertIn(self._as_output(text), textoutput)
1328
1329     def test_syntax_error(self):
1330         """Syntax errors should still have fancy special-case formatting"""
1331         textoutput = self._test_external_case("exec ('f(a, b c)')")
1332         self.assertIn(self._as_output(
1333             '  File "<string>", line 1\n'
1334             '    f(a, b c)\n'
1335             + ' ' * self._error_on_character +
1336             '          ^\n'
1337             'SyntaxError: '
1338             ), textoutput)
1339
1340     def test_syntax_error_malformed(self):
1341         """Syntax errors with bogus parameters should break anything"""
1342         textoutput = self._test_external_case("raise SyntaxError(3, 2, 1)")
1343         self.assertIn(self._as_output("\nSyntaxError: "), textoutput)
1344
1345     def test_syntax_error_import_binary(self):
1346         """Importing a binary file shouldn't break SyntaxError formatting"""
1347         if sys.version_info < (2, 5):
1348             # Python 2.4 assumes the file is latin-1 and tells you off
1349             self._silence_deprecation_warnings()
1350         self._setup_external_case("import bad")
1351         f = open(os.path.join(self.dir, "bad.py"), "wb")
1352         try:
1353             f.write(_b("x\x9c\xcb*\xcd\xcb\x06\x00\x04R\x01\xb9"))
1354         finally:
1355             f.close()
1356         textoutput = self._run_external_case()
1357         self.assertIn(self._as_output("\nSyntaxError: "), textoutput)
1358
1359     def test_syntax_error_line_iso_8859_1(self):
1360         """Syntax error on a latin-1 line shows the line decoded"""
1361         text, raw = self._get_sample_text("iso-8859-1")
1362         textoutput = self._setup_external_case("import bad")
1363         self._write_module("bad", "iso-8859-1",
1364             "# coding: iso-8859-1\n! = 0 # %s\n" % text)
1365         textoutput = self._run_external_case()
1366         self.assertIn(self._as_output(_u(
1367             #'bad.py", line 2\n'
1368             '    ! = 0 # %s\n'
1369             '    ^\n'
1370             'SyntaxError: ') %
1371             (text,)), textoutput)
1372
1373     def test_syntax_error_line_iso_8859_5(self):
1374         """Syntax error on a iso-8859-5 line shows the line decoded"""
1375         text, raw = self._get_sample_text("iso-8859-5")
1376         textoutput = self._setup_external_case("import bad")
1377         self._write_module("bad", "iso-8859-5",
1378             "# coding: iso-8859-5\n%% = 0 # %s\n" % text)
1379         textoutput = self._run_external_case()
1380         self.assertIn(self._as_output(_u(
1381             #'bad.py", line 2\n'
1382             '    %% = 0 # %s\n'
1383             + ' ' * self._error_on_character +
1384             '   ^\n'
1385             'SyntaxError: ') %
1386             (text,)), textoutput)
1387
1388     def test_syntax_error_line_euc_jp(self):
1389         """Syntax error on a euc_jp line shows the line decoded"""
1390         text, raw = self._get_sample_text("euc_jp")
1391         textoutput = self._setup_external_case("import bad")
1392         self._write_module("bad", "euc_jp",
1393             "# coding: euc_jp\n$ = 0 # %s\n" % text)
1394         textoutput = self._run_external_case()
1395         self.assertIn(self._as_output(_u(
1396             #'bad.py", line 2\n'
1397             '    $ = 0 # %s\n'
1398             + ' ' * self._error_on_character +
1399             '   ^\n'
1400             'SyntaxError: ') %
1401             (text,)), textoutput)
1402
1403     def test_syntax_error_line_utf_8(self):
1404         """Syntax error on a utf-8 line shows the line decoded"""
1405         text, raw = self._get_sample_text("utf-8")
1406         textoutput = self._setup_external_case("import bad")
1407         self._write_module("bad", "utf-8", _u("\ufeff^ = 0 # %s\n") % text)
1408         textoutput = self._run_external_case()
1409         self.assertIn(self._as_output(_u(
1410             'bad.py", line 1\n'
1411             '    ^ = 0 # %s\n'
1412             + ' ' * self._error_on_character +
1413             '   ^\n'
1414             'SyntaxError: ') %
1415             text), textoutput)
1416
1417
1418 class TestNonAsciiResultsWithUnittest(TestNonAsciiResults):
1419     """Test that running under unittest produces clean ascii strings"""
1420
1421     def _run(self, stream, test):
1422         from unittest import TextTestRunner as _Runner
1423         return _Runner(stream).run(test)
1424
1425     def _as_output(self, text):
1426         if str_is_unicode:
1427             return text
1428         return text.encode("utf-8")
1429
1430
1431 class TestDetailsToStr(TestCase):
1432
1433     def test_no_details(self):
1434         string = _details_to_str({})
1435         self.assertThat(string, Equals(''))
1436
1437     def test_binary_content(self):
1438         content = content_from_stream(
1439             StringIO('foo'), content_type=ContentType('image', 'jpeg'))
1440         string = _details_to_str({'attachment': content})
1441         self.assertThat(
1442             string, Equals("""\
1443 Binary content:
1444   attachment (image/jpeg)
1445 """))
1446
1447     def test_single_line_content(self):
1448         content = text_content('foo')
1449         string = _details_to_str({'attachment': content})
1450         self.assertThat(string, Equals('attachment: {{{foo}}}\n'))
1451
1452     def test_multi_line_text_content(self):
1453         content = text_content('foo\nbar\nbaz')
1454         string = _details_to_str({'attachment': content})
1455         self.assertThat(string, Equals('attachment: {{{\nfoo\nbar\nbaz\n}}}\n'))
1456
1457     def test_special_text_content(self):
1458         content = text_content('foo')
1459         string = _details_to_str({'attachment': content}, special='attachment')
1460         self.assertThat(string, Equals('foo\n'))
1461
1462     def test_multiple_text_content(self):
1463         string = _details_to_str(
1464             {'attachment': text_content('foo\nfoo'),
1465              'attachment-1': text_content('bar\nbar')})
1466         self.assertThat(
1467             string, Equals('attachment: {{{\n'
1468                            'foo\n'
1469                            'foo\n'
1470                            '}}}\n'
1471                            '\n'
1472                            'attachment-1: {{{\n'
1473                            'bar\n'
1474                            'bar\n'
1475                            '}}}\n'))
1476
1477     def test_empty_attachment(self):
1478         string = _details_to_str({'attachment': text_content('')})
1479         self.assertThat(
1480             string, Equals("""\
1481 Empty attachments:
1482   attachment
1483 """))
1484
1485     def test_lots_of_different_attachments(self):
1486         jpg = lambda x: content_from_stream(
1487             StringIO(x), ContentType('image', 'jpeg'))
1488         attachments = {
1489             'attachment': text_content('foo'),
1490             'attachment-1': text_content('traceback'),
1491             'attachment-2': jpg('pic1'),
1492             'attachment-3': text_content('bar'),
1493             'attachment-4': text_content(''),
1494             'attachment-5': jpg('pic2'),
1495             }
1496         string = _details_to_str(attachments, special='attachment-1')
1497         self.assertThat(
1498             string, Equals("""\
1499 Binary content:
1500   attachment-2 (image/jpeg)
1501   attachment-5 (image/jpeg)
1502 Empty attachments:
1503   attachment-4
1504
1505 attachment: {{{foo}}}
1506 attachment-3: {{{bar}}}
1507
1508 traceback
1509 """))
1510
1511
1512 def test_suite():
1513     from unittest import TestLoader
1514     return TestLoader().loadTestsFromName(__name__)