OLD | NEW |
| (Empty) |
1 # Copyright (c) 2001-2007 Twisted Matrix Laboratories. | |
2 # See LICENSE for details. | |
3 # | |
4 # Maintainer: Jonathan Lange <jml@twistedmatrix.com> | |
5 | |
6 | |
7 import errno, sys, os, re, StringIO | |
8 from twisted.internet.utils import suppressWarnings | |
9 from twisted.python.failure import Failure | |
10 from twisted.trial import itrial, unittest, runner, reporter, util | |
11 from twisted.trial.reporter import UncleanWarningsReporterWrapper | |
12 from twisted.trial.test import erroneous | |
13 from twisted.trial.unittest import makeTodo, SkipTest, Todo | |
14 | |
15 | |
16 class BrokenStream(object): | |
17 """ | |
18 Stream-ish object that raises a signal interrupt error. We use this to make | |
19 sure that Trial still manages to write what it needs to write. | |
20 """ | |
21 written = False | |
22 flushed = False | |
23 | |
24 def __init__(self, fObj): | |
25 self.fObj = fObj | |
26 | |
27 def write(self, s): | |
28 if self.written: | |
29 return self.fObj.write(s) | |
30 self.written = True | |
31 raise IOError(errno.EINTR, "Interrupted write") | |
32 | |
33 def flush(self): | |
34 if self.flushed: | |
35 return self.fObj.flush() | |
36 self.flushed = True | |
37 raise IOError(errno.EINTR, "Interrupted flush") | |
38 | |
39 | |
40 class StringTest(unittest.TestCase): | |
41 def stringComparison(self, expect, output): | |
42 output = filter(None, output) | |
43 self.failUnless(len(expect) <= len(output), | |
44 "Must have more observed than expected" | |
45 "lines %d < %d" % (len(output), len(expect))) | |
46 REGEX_PATTERN_TYPE = type(re.compile('')) | |
47 for line_number, (exp, out) in enumerate(zip(expect, output)): | |
48 if exp is None: | |
49 continue | |
50 elif isinstance(exp, str): | |
51 self.assertSubstring(exp, out, "Line %d: %r not in %r" | |
52 % (line_number, exp, out)) | |
53 elif isinstance(exp, REGEX_PATTERN_TYPE): | |
54 self.failUnless(exp.match(out), | |
55 "Line %d: %r did not match string %r" | |
56 % (line_number, exp.pattern, out)) | |
57 else: | |
58 raise TypeError("don't know what to do with object %r" | |
59 % (exp,)) | |
60 | |
61 | |
62 class TestTestResult(unittest.TestCase): | |
63 def setUp(self): | |
64 self.result = reporter.TestResult() | |
65 | |
66 def test_pyunitAddError(self): | |
67 # pyunit passes an exc_info tuple directly to addError | |
68 try: | |
69 raise RuntimeError('foo') | |
70 except RuntimeError, excValue: | |
71 self.result.addError(self, sys.exc_info()) | |
72 failure = self.result.errors[0][1] | |
73 self.assertEqual(excValue, failure.value) | |
74 self.assertEqual(RuntimeError, failure.type) | |
75 | |
76 def test_pyunitAddFailure(self): | |
77 # pyunit passes an exc_info tuple directly to addFailure | |
78 try: | |
79 raise self.failureException('foo') | |
80 except self.failureException, excValue: | |
81 self.result.addFailure(self, sys.exc_info()) | |
82 failure = self.result.failures[0][1] | |
83 self.assertEqual(excValue, failure.value) | |
84 self.assertEqual(self.failureException, failure.type) | |
85 | |
86 | |
87 class TestReporterRealtime(TestTestResult): | |
88 def setUp(self): | |
89 output = StringIO.StringIO() | |
90 self.result = reporter.Reporter(output, realtime=True) | |
91 | |
92 | |
93 class TestErrorReporting(StringTest): | |
94 doubleSeparator = re.compile(r'^=+$') | |
95 | |
96 def setUp(self): | |
97 self.loader = runner.TestLoader() | |
98 self.output = StringIO.StringIO() | |
99 self.result = reporter.Reporter(self.output) | |
100 | |
101 def getOutput(self, suite): | |
102 result = self.getResult(suite) | |
103 result.done() | |
104 return self.output.getvalue() | |
105 | |
106 def getResult(self, suite): | |
107 suite.run(self.result) | |
108 return self.result | |
109 | |
110 def testFormatErroredMethod(self): | |
111 suite = self.loader.loadClass(erroneous.TestFailureInSetUp) | |
112 output = self.getOutput(suite).splitlines() | |
113 match = [self.doubleSeparator, | |
114 ('[ERROR]: twisted.trial.test.erroneous.' | |
115 'TestFailureInSetUp.test_noop'), | |
116 'Traceback (most recent call last):', | |
117 re.compile(r'^\s+File .*erroneous\.py., line \d+, in setUp$'), | |
118 re.compile(r'^\s+raise FoolishError, ' | |
119 r'.I am a broken setUp method.$'), | |
120 ('twisted.trial.test.erroneous.FoolishError: ' | |
121 'I am a broken setUp method')] | |
122 self.stringComparison(match, output) | |
123 | |
124 def testFormatFailedMethod(self): | |
125 suite = self.loader.loadMethod(erroneous.TestRegularFail.test_fail) | |
126 output = self.getOutput(suite).splitlines() | |
127 match = [ | |
128 self.doubleSeparator, | |
129 '[FAIL]: ' | |
130 'twisted.trial.test.erroneous.TestRegularFail.test_fail', | |
131 'Traceback (most recent call last):', | |
132 re.compile(r'^\s+File .*erroneous\.py., line \d+, in test_fail$'), | |
133 re.compile(r'^\s+self\.fail\("I fail"\)$'), | |
134 'twisted.trial.unittest.FailTest: I fail' | |
135 ] | |
136 self.stringComparison(match, output) | |
137 | |
138 def testDoctestError(self): | |
139 from twisted.trial.test import erroneous | |
140 suite = unittest.decorate( | |
141 self.loader.loadDoctests(erroneous), itrial.ITestCase) | |
142 output = self.getOutput(suite) | |
143 path = 'twisted.trial.test.erroneous.unexpectedException' | |
144 for substring in ['1/0', 'ZeroDivisionError', | |
145 'Exception raised:', path]: | |
146 self.assertSubstring(substring, output) | |
147 self.failUnless(re.search('Fail(ed|ure in) example:', output), | |
148 "Couldn't match 'Failure in example: ' " | |
149 "or 'Failed example: '") | |
150 expect = [self.doubleSeparator, | |
151 re.compile(r'\[(ERROR|FAIL)\]: .*' + re.escape(path))] | |
152 self.stringComparison(expect, output.splitlines()) | |
153 | |
154 def testHiddenException(self): | |
155 """ | |
156 Check that errors in C{DelayedCall}s get reported, even if the | |
157 test already has a failure. | |
158 | |
159 Only really necessary for testing the deprecated style of tests that | |
160 use iterate() directly. See | |
161 L{erroneous.DelayedCall.testHiddenException} for more details. | |
162 """ | |
163 test = erroneous.DelayedCall('testHiddenException') | |
164 output = self.getOutput(test).splitlines() | |
165 match = [ | |
166 self.doubleSeparator, | |
167 '[FAIL]: ' | |
168 'twisted.trial.test.erroneous.DelayedCall.testHiddenException', | |
169 'Traceback (most recent call last):', | |
170 re.compile(r'^\s+File .*erroneous\.py., line \d+, in ' | |
171 'testHiddenException$'), | |
172 re.compile(r'^\s+self\.fail\("Deliberate failure to mask the ' | |
173 'hidden exception"\)$'), | |
174 'twisted.trial.unittest.FailTest: ' | |
175 'Deliberate failure to mask the hidden exception', | |
176 self.doubleSeparator, | |
177 '[ERROR]: ' | |
178 'twisted.trial.test.erroneous.DelayedCall.testHiddenException', | |
179 'Traceback (most recent call last):', | |
180 re.compile(r'^\s+File .* in runUntilCurrent'), | |
181 re.compile(r'^\s+.*'), | |
182 re.compile('^\s+File .*erroneous\.py", line \d+, in go'), | |
183 re.compile('^\s+raise RuntimeError\(self.hiddenExceptionMsg\)'), | |
184 'exceptions.RuntimeError: something blew up', | |
185 ] | |
186 | |
187 self.stringComparison(match, output) | |
188 | |
189 | |
190 | |
191 class TestUncleanWarningWrapperErrorReporting(TestErrorReporting): | |
192 """ | |
193 Tests that the L{UncleanWarningsReporterWrapper} can sufficiently proxy | |
194 IReporter failure and error reporting methods to a L{reporter.Reporter}. | |
195 """ | |
196 def setUp(self): | |
197 self.loader = runner.TestLoader() | |
198 self.output = StringIO.StringIO() | |
199 self.result = UncleanWarningsReporterWrapper( | |
200 reporter.Reporter(self.output)) | |
201 | |
202 | |
203 | |
204 class TracebackHandling(unittest.TestCase): | |
205 def getErrorFrames(self, test): | |
206 stream = StringIO.StringIO() | |
207 result = reporter.Reporter(stream) | |
208 test.run(result) | |
209 bads = result.failures + result.errors | |
210 assert len(bads) == 1 | |
211 assert bads[0][0] == test | |
212 return result._trimFrames(bads[0][1].frames) | |
213 | |
214 def checkFrames(self, observedFrames, expectedFrames): | |
215 for observed, expected in zip(observedFrames, expectedFrames): | |
216 self.assertEqual(observed[0], expected[0]) | |
217 observedSegs = os.path.splitext(observed[1])[0].split(os.sep) | |
218 expectedSegs = expected[1].split('/') | |
219 self.assertEqual(observedSegs[-len(expectedSegs):], | |
220 expectedSegs) | |
221 self.assertEqual(len(observedFrames), len(expectedFrames)) | |
222 | |
223 def test_basic(self): | |
224 test = erroneous.TestRegularFail('test_fail') | |
225 frames = self.getErrorFrames(test) | |
226 self.checkFrames(frames, | |
227 [('test_fail', 'twisted/trial/test/erroneous')]) | |
228 | |
229 def test_subroutine(self): | |
230 test = erroneous.TestRegularFail('test_subfail') | |
231 frames = self.getErrorFrames(test) | |
232 self.checkFrames(frames, | |
233 [('test_subfail', 'twisted/trial/test/erroneous'), | |
234 ('subroutine', 'twisted/trial/test/erroneous')]) | |
235 | |
236 def test_deferred(self): | |
237 test = erroneous.TestFailureInDeferredChain('test_fail') | |
238 frames = self.getErrorFrames(test) | |
239 self.checkFrames(frames, | |
240 [('_later', 'twisted/trial/test/erroneous')]) | |
241 | |
242 def test_noFrames(self): | |
243 result = reporter.Reporter(None) | |
244 self.assertEqual([], result._trimFrames([])) | |
245 | |
246 def test_oneFrame(self): | |
247 result = reporter.Reporter(None) | |
248 self.assertEqual(['fake frame'], result._trimFrames(['fake frame'])) | |
249 | |
250 | |
251 class FormatFailures(StringTest): | |
252 def setUp(self): | |
253 try: | |
254 raise RuntimeError('foo') | |
255 except RuntimeError: | |
256 self.f = Failure() | |
257 self.f.frames = [ | |
258 ['foo', 'foo/bar.py', 5, [('x', 5)], [('y', 'orange')]], | |
259 ['qux', 'foo/bar.py', 10, [('a', 'two')], [('b', 'MCMXCIX')]] | |
260 ] | |
261 self.stream = StringIO.StringIO() | |
262 self.result = reporter.Reporter(self.stream) | |
263 | |
264 def test_formatDefault(self): | |
265 tb = self.result._formatFailureTraceback(self.f) | |
266 self.stringComparison([ | |
267 'Traceback (most recent call last):', | |
268 ' File "foo/bar.py", line 5, in foo', | |
269 re.compile(r'^\s*$'), | |
270 ' File "foo/bar.py", line 10, in qux', | |
271 re.compile(r'^\s*$'), | |
272 'RuntimeError: foo'], tb.splitlines()) | |
273 | |
274 def test_formatString(self): | |
275 tb = ''' | |
276 File "twisted/trial/unittest.py", line 256, in failUnlessSubstring | |
277 return self.failUnlessIn(substring, astring, msg) | |
278 exceptions.TypeError: iterable argument required | |
279 | |
280 ''' | |
281 expected = ''' | |
282 File "twisted/trial/unittest.py", line 256, in failUnlessSubstring | |
283 return self.failUnlessIn(substring, astring, msg) | |
284 exceptions.TypeError: iterable argument required | |
285 ''' | |
286 formatted = self.result._formatFailureTraceback(tb) | |
287 self.assertEqual(expected, formatted) | |
288 | |
289 def test_mutation(self): | |
290 frames = self.f.frames[:] | |
291 tb = self.result._formatFailureTraceback(self.f) | |
292 self.assertEqual(self.f.frames, frames) | |
293 | |
294 | |
295 class PyunitTestNames(unittest.TestCase): | |
296 def setUp(self): | |
297 from twisted.trial.test import sample | |
298 self.stream = StringIO.StringIO() | |
299 self.test = sample.PyunitTest('test_foo') | |
300 | |
301 def test_verboseReporter(self): | |
302 result = reporter.VerboseTextReporter(self.stream) | |
303 result.startTest(self.test) | |
304 output = self.stream.getvalue() | |
305 self.failUnlessEqual( | |
306 output, 'twisted.trial.test.sample.PyunitTest.test_foo ... ') | |
307 | |
308 def test_treeReporter(self): | |
309 result = reporter.TreeReporter(self.stream) | |
310 result.startTest(self.test) | |
311 output = self.stream.getvalue() | |
312 output = output.splitlines()[-1].strip() | |
313 self.failUnlessEqual(output, result.getDescription(self.test) + ' ...') | |
314 | |
315 def test_getDescription(self): | |
316 result = reporter.TreeReporter(self.stream) | |
317 output = result.getDescription(self.test) | |
318 self.failUnlessEqual(output, 'test_foo') | |
319 | |
320 | |
321 def test_minimalReporter(self): | |
322 """ | |
323 The summary of L{reporter.MinimalReporter} is a simple list of | |
324 numbers, indicating how many tests ran, how many failed etc. | |
325 """ | |
326 result = reporter.MinimalReporter(self.stream) | |
327 self.test.run(result) | |
328 result._printSummary() | |
329 output = self.stream.getvalue().strip().split(' ') | |
330 self.failUnlessEqual(output[1:], ['1', '1', '0', '0', '0']) | |
331 | |
332 | |
333 | |
334 class TestDirtyReactor(unittest.TestCase): | |
335 """ | |
336 The trial script has an option to treat L{DirtyReactorAggregateError}s as | |
337 warnings, as a migration tool for test authors. It causes a wrapper to be | |
338 placed around reporters that replaces L{DirtyReactorAggregatErrors} with | |
339 warnings. | |
340 """ | |
341 | |
342 def setUp(self): | |
343 self.dirtyError = Failure( | |
344 util.DirtyReactorAggregateError(['foo'], ['bar'])) | |
345 self.output = StringIO.StringIO() | |
346 self.test = TestDirtyReactor('test_errorByDefault') | |
347 | |
348 | |
349 def test_errorByDefault(self): | |
350 """ | |
351 C{DirtyReactorAggregateError}s are reported as errors with the default | |
352 Reporter. | |
353 """ | |
354 result = reporter.Reporter(stream=self.output) | |
355 result.addError(self.test, self.dirtyError) | |
356 self.assertEqual(len(result.errors), 1) | |
357 self.assertEqual(result.errors[0][1], self.dirtyError) | |
358 | |
359 | |
360 def test_warningsEnabled(self): | |
361 """ | |
362 C{DirtyReactorErrors}s are reported as warnings when using the | |
363 L{UncleanWarningsReporterWrapper}. | |
364 """ | |
365 result = UncleanWarningsReporterWrapper( | |
366 reporter.Reporter(stream=self.output)) | |
367 self.assertWarns(UserWarning, self.dirtyError.getErrorMessage(), | |
368 reporter.__file__, | |
369 result.addError, self.test, self.dirtyError) | |
370 | |
371 | |
372 def test_warningsMaskErrors(self): | |
373 """ | |
374 C{DirtyReactorErrors}s are I{not} reported as errors if the | |
375 L{UncleanWarningsReporterWrapper} is used. | |
376 """ | |
377 result = UncleanWarningsReporterWrapper( | |
378 reporter.Reporter(stream=self.output)) | |
379 self.assertWarns(UserWarning, self.dirtyError.getErrorMessage(), | |
380 reporter.__file__, | |
381 result.addError, self.test, self.dirtyError) | |
382 self.assertEquals(result._originalReporter.errors, []) | |
383 | |
384 | |
385 def test_dealsWithThreeTuples(self): | |
386 """ | |
387 Some annoying stuff can pass three-tuples to addError instead of | |
388 Failures (like PyUnit). The wrapper, of course, handles this case, | |
389 since it is a part of L{twisted.trial.itrial.IReporter}! But it does | |
390 not convert L{DirtyReactorError} to warnings in this case, because | |
391 nobody should be passing those in the form of three-tuples. | |
392 """ | |
393 result = UncleanWarningsReporterWrapper( | |
394 reporter.Reporter(stream=self.output)) | |
395 result.addError(self.test, | |
396 (self.dirtyError.type, self.dirtyError.value, None)) | |
397 self.assertEqual(len(result._originalReporter.errors), 1) | |
398 self.assertEquals(result._originalReporter.errors[0][1].type, | |
399 self.dirtyError.type) | |
400 self.assertEquals(result._originalReporter.errors[0][1].value, | |
401 self.dirtyError.value) | |
402 | |
403 | |
404 | |
405 class TrialTestNames(unittest.TestCase): | |
406 | |
407 def setUp(self): | |
408 from twisted.trial.test import sample | |
409 self.stream = StringIO.StringIO() | |
410 self.test = sample.FooTest('test_foo') | |
411 | |
412 def test_verboseReporter(self): | |
413 result = reporter.VerboseTextReporter(self.stream) | |
414 result.startTest(self.test) | |
415 output = self.stream.getvalue() | |
416 self.failUnlessEqual(output, self.test.id() + ' ... ') | |
417 | |
418 def test_treeReporter(self): | |
419 result = reporter.TreeReporter(self.stream) | |
420 result.startTest(self.test) | |
421 output = self.stream.getvalue() | |
422 output = output.splitlines()[-1].strip() | |
423 self.failUnlessEqual(output, result.getDescription(self.test) + ' ...') | |
424 | |
425 def test_treeReporterWithDocstrings(self): | |
426 """A docstring""" | |
427 result = reporter.TreeReporter(self.stream) | |
428 self.assertEqual(result.getDescription(self), | |
429 'test_treeReporterWithDocstrings') | |
430 | |
431 def test_getDescription(self): | |
432 result = reporter.TreeReporter(self.stream) | |
433 output = result.getDescription(self.test) | |
434 self.failUnlessEqual(output, "test_foo") | |
435 | |
436 | |
437 class TestSkip(unittest.TestCase): | |
438 """ | |
439 Tests for L{reporter.Reporter}'s handling of skips. | |
440 """ | |
441 def setUp(self): | |
442 from twisted.trial.test import sample | |
443 self.stream = StringIO.StringIO() | |
444 self.result = reporter.Reporter(self.stream) | |
445 self.test = sample.FooTest('test_foo') | |
446 | |
447 def _getSkips(self, result): | |
448 """ | |
449 Get the number of skips that happened to a reporter. | |
450 """ | |
451 return len(result.skips) | |
452 | |
453 def test_accumulation(self): | |
454 self.result.addSkip(self.test, 'some reason') | |
455 self.assertEqual(self._getSkips(self.result), 1) | |
456 | |
457 def test_success(self): | |
458 self.result.addSkip(self.test, 'some reason') | |
459 self.failUnlessEqual(True, self.result.wasSuccessful()) | |
460 | |
461 | |
462 def test_summary(self): | |
463 """ | |
464 The summary of a successful run with skips indicates that the test | |
465 suite passed and includes the number of skips. | |
466 """ | |
467 self.result.addSkip(self.test, 'some reason') | |
468 self.result.done() | |
469 output = self.stream.getvalue().splitlines()[-1] | |
470 prefix = 'PASSED ' | |
471 self.failUnless(output.startswith(prefix)) | |
472 self.failUnlessEqual(output[len(prefix):].strip(), '(skips=1)') | |
473 | |
474 | |
475 def test_basicErrors(self): | |
476 """ | |
477 The output at the end of a test run with skips includes the reasons | |
478 for skipping those tests. | |
479 """ | |
480 self.result.addSkip(self.test, 'some reason') | |
481 self.result.done() | |
482 output = self.stream.getvalue().splitlines()[4] | |
483 self.failUnlessEqual(output.strip(), 'some reason') | |
484 | |
485 | |
486 def test_booleanSkip(self): | |
487 """ | |
488 Tests can be skipped without specifying a reason by setting the 'skip' | |
489 attribute to True. When this happens, the test output includes 'True' | |
490 as the reason. | |
491 """ | |
492 self.result.addSkip(self.test, True) | |
493 self.result.done() | |
494 output = self.stream.getvalue().splitlines()[4] | |
495 self.failUnlessEqual(output, 'True') | |
496 | |
497 | |
498 def test_exceptionSkip(self): | |
499 """ | |
500 Skips can be raised as errors. When this happens, the error is | |
501 included in the summary at the end of the test suite. | |
502 """ | |
503 try: | |
504 1/0 | |
505 except Exception, e: | |
506 error = e | |
507 self.result.addSkip(self.test, error) | |
508 self.result.done() | |
509 output = '\n'.join(self.stream.getvalue().splitlines()[3:5]).strip() | |
510 self.failUnlessEqual(output, str(e)) | |
511 | |
512 | |
513 class UncleanWarningSkipTest(TestSkip): | |
514 """ | |
515 Tests for skips on a L{reporter.Reporter} wrapped by an | |
516 L{UncleanWarningsReporterWrapper}. | |
517 """ | |
518 def setUp(self): | |
519 TestSkip.setUp(self) | |
520 self.result = UncleanWarningsReporterWrapper(self.result) | |
521 | |
522 def _getSkips(self, result): | |
523 """ | |
524 Get the number of skips that happened to a reporter inside of an | |
525 unclean warnings reporter wrapper. | |
526 """ | |
527 return len(result._originalReporter.skips) | |
528 | |
529 | |
530 | |
531 class TodoTest(unittest.TestCase): | |
532 """ | |
533 Tests for L{reporter.Reporter}'s handling of todos. | |
534 """ | |
535 | |
536 def setUp(self): | |
537 from twisted.trial.test import sample | |
538 self.stream = StringIO.StringIO() | |
539 self.result = reporter.Reporter(self.stream) | |
540 self.test = sample.FooTest('test_foo') | |
541 | |
542 | |
543 def _getTodos(self, result): | |
544 """ | |
545 Get the number of todos that happened to a reporter. | |
546 """ | |
547 return len(result.expectedFailures) | |
548 | |
549 | |
550 def _getUnexpectedSuccesses(self, result): | |
551 """ | |
552 Get the number of unexpected successes that happened to a reporter. | |
553 """ | |
554 return len(result.unexpectedSuccesses) | |
555 | |
556 | |
557 def test_accumulation(self): | |
558 """ | |
559 L{reporter.Reporter} accumulates the expected failures that it | |
560 is notified of. | |
561 """ | |
562 self.result.addExpectedFailure(self.test, Failure(Exception()), | |
563 makeTodo('todo!')) | |
564 self.assertEqual(self._getTodos(self.result), 1) | |
565 | |
566 | |
567 def test_success(self): | |
568 """ | |
569 A test run is still successful even if there are expected failures. | |
570 """ | |
571 self.result.addExpectedFailure(self.test, Failure(Exception()), | |
572 makeTodo('todo!')) | |
573 self.assertEqual(True, self.result.wasSuccessful()) | |
574 | |
575 | |
576 def test_unexpectedSuccess(self): | |
577 """ | |
578 A test which is marked as todo but succeeds will have an unexpected | |
579 success reported to its result. A test run is still successful even | |
580 when this happens. | |
581 """ | |
582 self.result.addUnexpectedSuccess(self.test, makeTodo("Heya!")) | |
583 self.assertEqual(True, self.result.wasSuccessful()) | |
584 self.assertEqual(self._getUnexpectedSuccesses(self.result), 1) | |
585 | |
586 | |
587 def test_summary(self): | |
588 """ | |
589 The reporter's C{printSummary} method should print the number of | |
590 expected failures that occured. | |
591 """ | |
592 self.result.addExpectedFailure(self.test, Failure(Exception()), | |
593 makeTodo('some reason')) | |
594 self.result.done() | |
595 output = self.stream.getvalue().splitlines()[-1] | |
596 prefix = 'PASSED ' | |
597 self.failUnless(output.startswith(prefix)) | |
598 self.assertEqual(output[len(prefix):].strip(), | |
599 '(expectedFailures=1)') | |
600 | |
601 | |
602 def test_basicErrors(self): | |
603 """ | |
604 The reporter's L{printErrors} method should include the value of the | |
605 Todo. | |
606 """ | |
607 self.result.addExpectedFailure(self.test, Failure(Exception()), | |
608 makeTodo('some reason')) | |
609 self.result.done() | |
610 output = self.stream.getvalue().splitlines()[4].strip() | |
611 self.assertEqual(output, "Reason: 'some reason'") | |
612 | |
613 | |
614 def test_booleanTodo(self): | |
615 """ | |
616 Booleans CAN'T be used as the value of a todo. Maybe this sucks. This | |
617 is a test for current behavior, not a requirement. | |
618 """ | |
619 self.result.addExpectedFailure(self.test, Failure(Exception()), | |
620 makeTodo(True)) | |
621 self.assertRaises(Exception, self.result.done) | |
622 | |
623 | |
624 def test_exceptionTodo(self): | |
625 """ | |
626 The exception for expected failures should be shown in the | |
627 C{printErrors} output. | |
628 """ | |
629 try: | |
630 1/0 | |
631 except Exception, e: | |
632 error = e | |
633 self.result.addExpectedFailure(self.test, Failure(error), | |
634 makeTodo("todo!")) | |
635 self.result.done() | |
636 output = '\n'.join(self.stream.getvalue().splitlines()[3:]).strip() | |
637 self.assertTrue(str(e) in output) | |
638 | |
639 | |
640 | |
641 class UncleanWarningTodoTest(TodoTest): | |
642 """ | |
643 Tests for L{UncleanWarningsReporterWrapper}'s handling of todos. | |
644 """ | |
645 | |
646 def setUp(self): | |
647 TodoTest.setUp(self) | |
648 self.result = UncleanWarningsReporterWrapper(self.result) | |
649 | |
650 | |
651 def _getTodos(self, result): | |
652 """ | |
653 Get the number of todos that happened to a reporter inside of an | |
654 unclean warnings reporter wrapper. | |
655 """ | |
656 return len(result._originalReporter.expectedFailures) | |
657 | |
658 | |
659 def _getUnexpectedSuccesses(self, result): | |
660 """ | |
661 Get the number of unexpected successes that happened to a reporter | |
662 inside of an unclean warnings reporter wrapper. | |
663 """ | |
664 return len(result._originalReporter.unexpectedSuccesses) | |
665 | |
666 | |
667 | |
668 class MockColorizer: | |
669 """ | |
670 Used by TestTreeReporter to make sure that output is colored correctly. | |
671 """ | |
672 def __init__(self, stream): | |
673 self.log = [] | |
674 | |
675 def supported(self): | |
676 return True | |
677 supported = classmethod(supported) | |
678 | |
679 def write(self, text, color): | |
680 self.log.append((color, text)) | |
681 | |
682 | |
683 class TestTreeReporter(unittest.TestCase): | |
684 def setUp(self): | |
685 from twisted.trial.test import sample | |
686 self.test = sample.FooTest('test_foo') | |
687 self.stream = StringIO.StringIO() | |
688 self.result = reporter.TreeReporter(self.stream) | |
689 self.result._colorizer = MockColorizer(self.stream) | |
690 self.log = self.result._colorizer.log | |
691 | |
692 def makeError(self): | |
693 try: | |
694 1/0 | |
695 except ZeroDivisionError: | |
696 f = Failure() | |
697 return f | |
698 | |
699 def test_cleanupError(self): | |
700 """ | |
701 Run cleanupErrors and check that the output is correct, and colored | |
702 correctly. | |
703 """ | |
704 f = self.makeError() | |
705 self.result.cleanupErrors(f) | |
706 color, text = self.log[0] | |
707 self.assertEqual(color.strip(), self.result.ERROR) | |
708 self.assertEqual(text.strip(), 'cleanup errors') | |
709 color, text = self.log[1] | |
710 self.assertEqual(color.strip(), self.result.ERROR) | |
711 self.assertEqual(text.strip(), '[ERROR]') | |
712 test_cleanupError = suppressWarnings( | |
713 test_cleanupError, | |
714 util.suppress(category=reporter.BrokenTestCaseWarning), | |
715 util.suppress(category=DeprecationWarning)) | |
716 | |
717 | |
718 def test_upDownError(self): | |
719 """ | |
720 Run upDownError and check that the output is correct and colored | |
721 correctly. | |
722 """ | |
723 self.result.upDownError("method", None, None, False) | |
724 color, text = self.log[0] | |
725 self.assertEqual(color.strip(), self.result.ERROR) | |
726 self.assertEqual(text.strip(), 'method') | |
727 test_upDownError = suppressWarnings( | |
728 test_upDownError, | |
729 util.suppress(category=DeprecationWarning, | |
730 message="upDownError is deprecated in Twisted 8.0.")) | |
731 | |
732 | |
733 def test_summaryColoredSuccess(self): | |
734 """ | |
735 The summary in case of success should have a good count of successes | |
736 and be colored properly. | |
737 """ | |
738 self.result.addSuccess(self.test) | |
739 self.result.done() | |
740 self.assertEquals(self.log[1], (self.result.SUCCESS, 'PASSED')) | |
741 self.assertEquals( | |
742 self.stream.getvalue().splitlines()[-1].strip(), "(successes=1)") | |
743 | |
744 | |
745 def test_summaryColoredFailure(self): | |
746 """ | |
747 The summary in case of failure should have a good count of errors | |
748 and be colored properly. | |
749 """ | |
750 try: | |
751 raise RuntimeError('foo') | |
752 except RuntimeError, excValue: | |
753 self.result.addError(self, sys.exc_info()) | |
754 self.result.done() | |
755 self.assertEquals(self.log[1], (self.result.FAILURE, 'FAILED')) | |
756 self.assertEquals( | |
757 self.stream.getvalue().splitlines()[-1].strip(), "(errors=1)") | |
758 | |
759 | |
760 def test_getPrelude(self): | |
761 """ | |
762 The tree needs to get the segments of the test ID that correspond | |
763 to the module and class that it belongs to. | |
764 """ | |
765 self.assertEqual( | |
766 ['foo.bar', 'baz'], | |
767 self.result._getPreludeSegments('foo.bar.baz.qux')) | |
768 self.assertEqual( | |
769 ['foo', 'bar'], | |
770 self.result._getPreludeSegments('foo.bar.baz')) | |
771 self.assertEqual( | |
772 ['foo'], | |
773 self.result._getPreludeSegments('foo.bar')) | |
774 self.assertEqual([], self.result._getPreludeSegments('foo')) | |
775 | |
776 | |
777 class TestReporter(unittest.TestCase): | |
778 resultFactory = reporter.Reporter | |
779 | |
780 def setUp(self): | |
781 from twisted.trial.test import sample | |
782 self.test = sample.FooTest('test_foo') | |
783 self.stream = StringIO.StringIO() | |
784 self.result = self.resultFactory(self.stream) | |
785 self._timer = 0 | |
786 self.result._getTime = self._getTime | |
787 | |
788 def _getTime(self): | |
789 self._timer += 1 | |
790 return self._timer | |
791 | |
792 def test_startStop(self): | |
793 self.result.startTest(self.test) | |
794 self.result.stopTest(self.test) | |
795 self.assertTrue(self.result._lastTime > 0) | |
796 self.assertEqual(self.result.testsRun, 1) | |
797 self.assertEqual(self.result.wasSuccessful(), True) | |
798 | |
799 | |
800 def test_brokenStream(self): | |
801 """ | |
802 Test that the reporter safely writes to its stream. | |
803 """ | |
804 result = self.resultFactory(stream=BrokenStream(self.stream)) | |
805 result._writeln("Hello") | |
806 self.assertEqual(self.stream.getvalue(), 'Hello\n') | |
807 self.stream.truncate(0) | |
808 result._writeln("Hello %s!", 'World') | |
809 self.assertEqual(self.stream.getvalue(), 'Hello World!\n') | |
810 | |
811 | |
812 | |
813 def test_printErrorsDeprecated(self): | |
814 """ | |
815 L{IReporter.printErrors} was deprecated in Twisted 8.0. | |
816 """ | |
817 def f(): | |
818 self.result.printErrors() | |
819 self.assertWarns( | |
820 DeprecationWarning, "printErrors is deprecated in Twisted 8.0.", | |
821 __file__, f) | |
822 | |
823 | |
824 def test_printSummaryDeprecated(self): | |
825 """ | |
826 L{IReporter.printSummary} was deprecated in Twisted 8.0. | |
827 """ | |
828 def f(): | |
829 self.result.printSummary() | |
830 self.assertWarns( | |
831 DeprecationWarning, "printSummary is deprecated in Twisted 8.0.", | |
832 __file__, f) | |
833 | |
834 | |
835 def test_writeDeprecated(self): | |
836 """ | |
837 L{IReporter.write} was deprecated in Twisted 8.0. | |
838 """ | |
839 def f(): | |
840 self.result.write("") | |
841 self.assertWarns( | |
842 DeprecationWarning, "write is deprecated in Twisted 8.0.", | |
843 __file__, f) | |
844 | |
845 | |
846 def test_writelnDeprecated(self): | |
847 """ | |
848 L{IReporter.writeln} was deprecated in Twisted 8.0. | |
849 """ | |
850 def f(): | |
851 self.result.writeln("") | |
852 self.assertWarns( | |
853 DeprecationWarning, "writeln is deprecated in Twisted 8.0.", | |
854 __file__, f) | |
855 | |
856 | |
857 def test_separatorDeprecated(self): | |
858 """ | |
859 L{IReporter.separator} was deprecated in Twisted 8.0. | |
860 """ | |
861 def f(): | |
862 return self.result.separator | |
863 self.assertWarns( | |
864 DeprecationWarning, "separator is deprecated in Twisted 8.0.", | |
865 __file__, f) | |
866 | |
867 | |
868 def test_streamDeprecated(self): | |
869 """ | |
870 L{IReporter.stream} was deprecated in Twisted 8.0. | |
871 """ | |
872 def f(): | |
873 return self.result.stream | |
874 self.assertWarns( | |
875 DeprecationWarning, "stream is deprecated in Twisted 8.0.", | |
876 __file__, f) | |
877 | |
878 | |
879 def test_upDownErrorDeprecated(self): | |
880 """ | |
881 L{IReporter.upDownError} was deprecated in Twisted 8.0. | |
882 """ | |
883 def f(): | |
884 self.result.upDownError(None, None, None, None) | |
885 self.assertWarns( | |
886 DeprecationWarning, "upDownError is deprecated in Twisted 8.0.", | |
887 __file__, f) | |
888 | |
889 | |
890 | |
891 class TestSafeStream(unittest.TestCase): | |
892 def test_safe(self): | |
893 """ | |
894 Test that L{reporter.SafeStream} successfully write to its original | |
895 stream even if an interrupt happens during the write. | |
896 """ | |
897 stream = StringIO.StringIO() | |
898 broken = BrokenStream(stream) | |
899 safe = reporter.SafeStream(broken) | |
900 safe.write("Hello") | |
901 self.assertEqual(stream.getvalue(), "Hello") | |
902 | |
903 | |
904 class TestTimingReporter(TestReporter): | |
905 resultFactory = reporter.TimingTextReporter | |
906 | |
907 | |
908 | |
909 class LoggingReporter(reporter.Reporter): | |
910 """ | |
911 Simple reporter that stores the last test that was passed to it. | |
912 """ | |
913 | |
914 def __init__(self, *args, **kwargs): | |
915 reporter.Reporter.__init__(self, *args, **kwargs) | |
916 self.test = None | |
917 | |
918 def addError(self, test, error): | |
919 self.test = test | |
920 | |
921 def addExpectedFailure(self, test, failure, todo): | |
922 self.test = test | |
923 | |
924 def addFailure(self, test, failure): | |
925 self.test = test | |
926 | |
927 def addSkip(self, test, skip): | |
928 self.test = test | |
929 | |
930 def addUnexpectedSuccess(self, test, todo): | |
931 self.test = test | |
932 | |
933 def startTest(self, test): | |
934 self.test = test | |
935 | |
936 def stopTest(self, test): | |
937 self.test = test | |
938 | |
939 | |
940 | |
941 class TestAdaptedReporter(unittest.TestCase): | |
942 """ | |
943 L{reporter._AdaptedReporter} is a reporter wrapper that wraps all of the | |
944 tests it receives before passing them on to the original reporter. | |
945 """ | |
946 | |
947 def setUp(self): | |
948 self.wrappedResult = self.getWrappedResult() | |
949 | |
950 | |
951 def _testAdapter(self, test): | |
952 return test.id() | |
953 | |
954 | |
955 def assertWrapped(self, wrappedResult, test): | |
956 self.assertEqual(wrappedResult._originalReporter.test, self._testAdapter
(test)) | |
957 | |
958 | |
959 def getFailure(self, exceptionInstance): | |
960 """ | |
961 Return a L{Failure} from raising the given exception. | |
962 | |
963 @param exceptionInstance: The exception to raise. | |
964 @return: L{Failure} | |
965 """ | |
966 try: | |
967 raise exceptionInstance | |
968 except: | |
969 return Failure() | |
970 | |
971 | |
972 def getWrappedResult(self): | |
973 result = LoggingReporter() | |
974 return reporter._AdaptedReporter(result, self._testAdapter) | |
975 | |
976 | |
977 def test_addError(self): | |
978 """ | |
979 C{addError} wraps its test with the provided adapter. | |
980 """ | |
981 self.wrappedResult.addError(self, self.getFailure(RuntimeError())) | |
982 self.assertWrapped(self.wrappedResult, self) | |
983 | |
984 | |
985 def test_addFailure(self): | |
986 """ | |
987 C{addFailure} wraps its test with the provided adapter. | |
988 """ | |
989 self.wrappedResult.addFailure(self, self.getFailure(AssertionError())) | |
990 self.assertWrapped(self.wrappedResult, self) | |
991 | |
992 | |
993 def test_addSkip(self): | |
994 """ | |
995 C{addSkip} wraps its test with the provided adapter. | |
996 """ | |
997 self.wrappedResult.addSkip(self, self.getFailure(SkipTest('no reason'))) | |
998 self.assertWrapped(self.wrappedResult, self) | |
999 | |
1000 | |
1001 def test_startTest(self): | |
1002 """ | |
1003 C{startTest} wraps its test with the provided adapter. | |
1004 """ | |
1005 self.wrappedResult.startTest(self) | |
1006 self.assertWrapped(self.wrappedResult, self) | |
1007 | |
1008 | |
1009 def test_stopTest(self): | |
1010 """ | |
1011 C{stopTest} wraps its test with the provided adapter. | |
1012 """ | |
1013 self.wrappedResult.stopTest(self) | |
1014 self.assertWrapped(self.wrappedResult, self) | |
1015 | |
1016 | |
1017 def test_addExpectedFailure(self): | |
1018 """ | |
1019 C{addExpectedFailure} wraps its test with the provided adapter. | |
1020 """ | |
1021 self.wrappedResult.addExpectedFailure( | |
1022 self, self.getFailure(RuntimeError()), Todo("no reason")) | |
1023 self.assertWrapped(self.wrappedResult, self) | |
1024 | |
1025 | |
1026 def test_addUnexpectedSuccess(self): | |
1027 """ | |
1028 C{addUnexpectedSuccess} wraps its test with the provided adapter. | |
1029 """ | |
1030 self.wrappedResult.addUnexpectedSuccess(self, Todo("no reason")) | |
1031 self.assertWrapped(self.wrappedResult, self) | |
OLD | NEW |