Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: third_party/twisted_8_1/twisted/trial/reporter.py

Issue 12261012: Remove third_party/twisted_8_1 (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- test-case-name: twisted.trial.test.test_reporter -*-
2 #
3 # Copyright (c) 2001-2007 Twisted Matrix Laboratories.
4 # See LICENSE for details.
5 #
6 # Maintainer: Jonathan Lange <jml@twistedmatrix.com>
7
8 """
9 Defines classes that handle the results of tests.
10 """
11
12 import sys, os
13 import time
14 import warnings
15
16 from twisted.python import reflect, log
17 from twisted.python.components import proxyForInterface
18 from twisted.python.failure import Failure
19 from twisted.python.util import untilConcludes
20 from twisted.trial import itrial, util
21
22 from zope.interface import implements
23
24 pyunit = __import__('unittest')
25
26
27 class BrokenTestCaseWarning(Warning):
28 """emitted as a warning when an exception occurs in one of
29 setUp, tearDown, setUpClass, or tearDownClass"""
30
31
32 class SafeStream(object):
33 """
34 Wraps a stream object so that all C{write} calls are wrapped in
35 L{untilConcludes}.
36 """
37
38 def __init__(self, original):
39 self.original = original
40
41 def __getattr__(self, name):
42 return getattr(self.original, name)
43
44 def write(self, *a, **kw):
45 return untilConcludes(self.original.write, *a, **kw)
46
47
48 class TestResult(pyunit.TestResult, object):
49 """
50 Accumulates the results of several L{twisted.trial.unittest.TestCase}s.
51
52 @ivar successes: count the number of successes achieved by the test run.
53 @type successes: C{int}
54 """
55 implements(itrial.IReporter)
56
57 def __init__(self):
58 super(TestResult, self).__init__()
59 self.skips = []
60 self.expectedFailures = []
61 self.unexpectedSuccesses = []
62 self.successes = 0
63 self._timings = []
64
65 def __repr__(self):
66 return ('<%s run=%d errors=%d failures=%d todos=%d dones=%d skips=%d>'
67 % (reflect.qual(self.__class__), self.testsRun,
68 len(self.errors), len(self.failures),
69 len(self.expectedFailures), len(self.skips),
70 len(self.unexpectedSuccesses)))
71
72 def _getTime(self):
73 return time.time()
74
75 def _getFailure(self, error):
76 """
77 Convert a C{sys.exc_info()}-style tuple to a L{Failure}, if necessary.
78 """
79 if isinstance(error, tuple):
80 return Failure(error[1], error[0], error[2])
81 return error
82
83 def startTest(self, test):
84 """This must be called before the given test is commenced.
85
86 @type test: L{pyunit.TestCase}
87 """
88 super(TestResult, self).startTest(test)
89 self._testStarted = self._getTime()
90
91 def stopTest(self, test):
92 """This must be called after the given test is completed.
93
94 @type test: L{pyunit.TestCase}
95 """
96 super(TestResult, self).stopTest(test)
97 self._lastTime = self._getTime() - self._testStarted
98
99 def addFailure(self, test, fail):
100 """Report a failed assertion for the given test.
101
102 @type test: L{pyunit.TestCase}
103 @type fail: L{Failure} or L{tuple}
104 """
105 self.failures.append((test, self._getFailure(fail)))
106
107 def addError(self, test, error):
108 """Report an error that occurred while running the given test.
109
110 @type test: L{pyunit.TestCase}
111 @type fail: L{Failure} or L{tuple}
112 """
113 self.errors.append((test, self._getFailure(error)))
114
115 def addSkip(self, test, reason):
116 """
117 Report that the given test was skipped.
118
119 In Trial, tests can be 'skipped'. Tests are skipped mostly because there
120 is some platform or configuration issue that prevents them from being
121 run correctly.
122
123 @type test: L{pyunit.TestCase}
124 @type reason: L{str}
125 """
126 self.skips.append((test, reason))
127
128 def addUnexpectedSuccess(self, test, todo):
129 """Report that the given test succeeded against expectations.
130
131 In Trial, tests can be marked 'todo'. That is, they are expected to fail .
132 When a test that is expected to fail instead succeeds, it should call
133 this method to report the unexpected success.
134
135 @type test: L{pyunit.TestCase}
136 @type todo: L{unittest.Todo}
137 """
138 # XXX - 'todo' should just be a string
139 self.unexpectedSuccesses.append((test, todo))
140
141 def addExpectedFailure(self, test, error, todo):
142 """Report that the given test failed, and was expected to do so.
143
144 In Trial, tests can be marked 'todo'. That is, they are expected to fail .
145
146 @type test: L{pyunit.TestCase}
147 @type error: L{Failure}
148 @type todo: L{unittest.Todo}
149 """
150 # XXX - 'todo' should just be a string
151 self.expectedFailures.append((test, error, todo))
152
153 def addSuccess(self, test):
154 """Report that the given test succeeded.
155
156 @type test: L{pyunit.TestCase}
157 """
158 self.successes += 1
159
160 def upDownError(self, method, error, warn, printStatus):
161 warnings.warn("upDownError is deprecated in Twisted 8.0.",
162 category=DeprecationWarning, stacklevel=3)
163
164 def cleanupErrors(self, errs):
165 """Report an error that occurred during the cleanup between tests.
166 """
167 warnings.warn("Cleanup errors are actual errors. Use addError. "
168 "Deprecated in Twisted 8.0",
169 category=DeprecationWarning, stacklevel=2)
170
171 def startSuite(self, name):
172 warnings.warn("startSuite deprecated in Twisted 8.0",
173 category=DeprecationWarning, stacklevel=2)
174
175 def endSuite(self, name):
176 warnings.warn("endSuite deprecated in Twisted 8.0",
177 category=DeprecationWarning, stacklevel=2)
178
179
180 def done(self):
181 """
182 The test suite has finished running.
183 """
184
185
186
187 class TestResultDecorator(proxyForInterface(itrial.IReporter,
188 "_originalReporter")):
189 """
190 Base class for TestResult decorators.
191
192 @ivar _originalReporter: The wrapped instance of reporter.
193 @type _originalReporter: A provider of L{itrial.IReporter}
194 """
195
196 implements(itrial.IReporter)
197
198
199
200 class UncleanWarningsReporterWrapper(TestResultDecorator):
201 """
202 A wrapper for a reporter that converts L{util.DirtyReactorError}s
203 to warnings.
204 """
205 implements(itrial.IReporter)
206
207 def addError(self, test, error):
208 """
209 If the error is a L{util.DirtyReactorError}, instead of
210 reporting it as a normal error, throw a warning.
211 """
212
213 if (isinstance(error, Failure)
214 and error.check(util.DirtyReactorAggregateError)):
215 warnings.warn(error.getErrorMessage())
216 else:
217 self._originalReporter.addError(test, error)
218
219
220
221 class _AdaptedReporter(TestResultDecorator):
222 """
223 TestResult decorator that makes sure that addError only gets tests that
224 have been adapted with a particular test adapter.
225 """
226
227 def __init__(self, original, testAdapter):
228 """
229 Construct an L{_AdaptedReporter}.
230
231 @param original: An {itrial.IReporter}.
232 @param testAdapter: A callable that returns an L{itrial.ITestCase}.
233 """
234 TestResultDecorator.__init__(self, original)
235 self.testAdapter = testAdapter
236
237
238 def addError(self, test, error):
239 """
240 See L{itrial.IReporter}.
241 """
242 test = self.testAdapter(test)
243 return self._originalReporter.addError(test, error)
244
245
246 def addExpectedFailure(self, test, failure, todo):
247 """
248 See L{itrial.IReporter}.
249 """
250 return self._originalReporter.addExpectedFailure(
251 self.testAdapter(test), failure, todo)
252
253
254 def addFailure(self, test, failure):
255 """
256 See L{itrial.IReporter}.
257 """
258 test = self.testAdapter(test)
259 return self._originalReporter.addFailure(test, failure)
260
261
262 def addSkip(self, test, skip):
263 """
264 See L{itrial.IReporter}.
265 """
266 test = self.testAdapter(test)
267 return self._originalReporter.addSkip(test, skip)
268
269
270 def addUnexpectedSuccess(self, test, todo):
271 """
272 See L{itrial.IReporter}.
273 """
274 test = self.testAdapter(test)
275 return self._originalReporter.addUnexpectedSuccess(test, todo)
276
277
278 def startTest(self, test):
279 """
280 See L{itrial.IReporter}.
281 """
282 return self._originalReporter.startTest(self.testAdapter(test))
283
284
285 def stopTest(self, test):
286 """
287 See L{itrial.IReporter}.
288 """
289 return self._originalReporter.stopTest(self.testAdapter(test))
290
291
292
293 class Reporter(TestResult):
294 """
295 A basic L{TestResult} with support for writing to a stream.
296 """
297
298 implements(itrial.IReporter)
299
300 _separator = '-' * 79
301 _doubleSeparator = '=' * 79
302
303 def __init__(self, stream=sys.stdout, tbformat='default', realtime=False):
304 super(Reporter, self).__init__()
305 self._stream = SafeStream(stream)
306 self.tbformat = tbformat
307 self.realtime = realtime
308 # The time when the first test was started.
309 self._startTime = None
310
311
312 def stream(self):
313 warnings.warn("stream is deprecated in Twisted 8.0.",
314 category=DeprecationWarning, stacklevel=4)
315 return self._stream
316 stream = property(stream)
317
318
319 def separator(self):
320 warnings.warn("separator is deprecated in Twisted 8.0.",
321 category=DeprecationWarning, stacklevel=4)
322 return self._separator
323 separator = property(separator)
324
325
326 def startTest(self, test):
327 """
328 Called when a test begins to run. Records the time when it was first
329 called.
330
331 @param test: L{ITestCase}
332 """
333 super(Reporter, self).startTest(test)
334 if self._startTime is None:
335 self._startTime = time.time()
336
337
338 def addFailure(self, test, fail):
339 """
340 Called when a test fails. If L{realtime} is set, then it prints the
341 error to the stream.
342
343 @param test: L{ITestCase} that failed.
344 @param fail: L{failure.Failure} containing the error.
345 """
346 super(Reporter, self).addFailure(test, fail)
347 if self.realtime:
348 fail = self.failures[-1][1] # guarantee it's a Failure
349 self._write(self._formatFailureTraceback(fail))
350
351
352 def addError(self, test, error):
353 """
354 Called when a test raises an error. If L{realtime} is set, then it
355 prints the error to the stream.
356
357 @param test: L{ITestCase} that raised the error.
358 @param error: L{failure.Failure} containing the error.
359 """
360 error = self._getFailure(error)
361 super(Reporter, self).addError(test, error)
362 if self.realtime:
363 error = self.errors[-1][1] # guarantee it's a Failure
364 self._write(self._formatFailureTraceback(error))
365
366
367 def write(self, format, *args):
368 warnings.warn("write is deprecated in Twisted 8.0.",
369 category=DeprecationWarning, stacklevel=2)
370 self._write(format, *args)
371
372
373 def _write(self, format, *args):
374 """
375 Safely write to the reporter's stream.
376
377 @param format: A format string to write.
378 @param *args: The arguments for the format string.
379 """
380 s = str(format)
381 assert isinstance(s, type(''))
382 if args:
383 self._stream.write(s % args)
384 else:
385 self._stream.write(s)
386 untilConcludes(self._stream.flush)
387
388
389 def writeln(self, format, *args):
390 warnings.warn("writeln is deprecated in Twisted 8.0.",
391 category=DeprecationWarning, stacklevel=2)
392 self._writeln(format, *args)
393
394
395 def _writeln(self, format, *args):
396 """
397 Safely write a line to the reporter's stream. Newline is appended to
398 the format string.
399
400 @param format: A format string to write.
401 @param *args: The arguments for the format string.
402 """
403 self._write(format, *args)
404 self._write('\n')
405
406
407 def upDownError(self, method, error, warn, printStatus):
408 super(Reporter, self).upDownError(method, error, warn, printStatus)
409 if warn:
410 tbStr = self._formatFailureTraceback(error)
411 log.msg(tbStr)
412 msg = ("caught exception in %s, your TestCase is broken\n\n%s"
413 % (method, tbStr))
414 warnings.warn(msg, BrokenTestCaseWarning, stacklevel=2)
415
416
417 def cleanupErrors(self, errs):
418 super(Reporter, self).cleanupErrors(errs)
419 warnings.warn("%s\n%s" % ("REACTOR UNCLEAN! traceback(s) follow: ",
420 self._formatFailureTraceback(errs)),
421 BrokenTestCaseWarning)
422
423
424 def _trimFrames(self, frames):
425 # when a method fails synchronously, the stack looks like this:
426 # [0]: defer.maybeDeferred()
427 # [1]: utils.runWithWarningsSuppressed()
428 # [2:-2]: code in the test method which failed
429 # [-1]: unittest.fail
430
431 # when a method fails inside a Deferred (i.e., when the test method
432 # returns a Deferred, and that Deferred's errback fires), the stack
433 # captured inside the resulting Failure looks like this:
434 # [0]: defer.Deferred._runCallbacks
435 # [1:-2]: code in the testmethod which failed
436 # [-1]: unittest.fail
437
438 # as a result, we want to trim either [maybeDeferred,runWWS] or
439 # [Deferred._runCallbacks] from the front, and trim the
440 # [unittest.fail] from the end.
441
442 # There is also another case, when the test method is badly defined and
443 # contains extra arguments.
444
445 newFrames = list(frames)
446
447 if len(frames) < 2:
448 return newFrames
449
450 first = newFrames[0]
451 second = newFrames[1]
452 if (first[0] == "maybeDeferred"
453 and os.path.splitext(os.path.basename(first[1]))[0] == 'defer'
454 and second[0] == "runWithWarningsSuppressed"
455 and os.path.splitext(os.path.basename(second[1]))[0] == 'utils'):
456 newFrames = newFrames[2:]
457 elif (first[0] == "_runCallbacks"
458 and os.path.splitext(os.path.basename(first[1]))[0] == 'defer'):
459 newFrames = newFrames[1:]
460
461 if not newFrames:
462 # The method fails before getting called, probably an argument probl em
463 return newFrames
464
465 last = newFrames[-1]
466 if (last[0].startswith('fail')
467 and os.path.splitext(os.path.basename(last[1]))[0] == 'unittest'):
468 newFrames = newFrames[:-1]
469
470 return newFrames
471
472
473 def _formatFailureTraceback(self, fail):
474 if isinstance(fail, str):
475 return fail.rstrip() + '\n'
476 fail.frames, frames = self._trimFrames(fail.frames), fail.frames
477 result = fail.getTraceback(detail=self.tbformat, elideFrameworkCode=True )
478 fail.frames = frames
479 return result
480
481
482 def _printResults(self, flavour, errors, formatter):
483 """
484 Print a group of errors to the stream.
485
486 @param flavour: A string indicating the kind of error (e.g. 'TODO').
487 @param errors: A list of errors, often L{failure.Failure}s, but
488 sometimes 'todo' errors.
489 @param formatter: A callable that knows how to format the errors.
490 """
491 for content in errors:
492 self._writeln(self._doubleSeparator)
493 self._writeln('%s: %s' % (flavour, content[0].id()))
494 self._writeln('')
495 self._write(formatter(*(content[1:])))
496
497
498 def _printExpectedFailure(self, error, todo):
499 return 'Reason: %r\n%s' % (todo.reason,
500 self._formatFailureTraceback(error))
501
502
503 def _printUnexpectedSuccess(self, todo):
504 ret = 'Reason: %r\n' % (todo.reason,)
505 if todo.errors:
506 ret += 'Expected errors: %s\n' % (', '.join(todo.errors),)
507 return ret
508
509
510 def printErrors(self):
511 """
512 Print all of the non-success results in full to the stream.
513 """
514 warnings.warn("printErrors is deprecated in Twisted 8.0.",
515 category=DeprecationWarning, stacklevel=2)
516 self._printErrors()
517
518
519 def _printErrors(self):
520 """
521 Print all of the non-success results to the stream in full.
522 """
523 self._write('\n')
524 self._printResults('[SKIPPED]', self.skips, lambda x : '%s\n' % x)
525 self._printResults('[TODO]', self.expectedFailures,
526 self._printExpectedFailure)
527 self._printResults('[FAIL]', self.failures,
528 self._formatFailureTraceback)
529 self._printResults('[ERROR]', self.errors,
530 self._formatFailureTraceback)
531 self._printResults('[SUCCESS!?!]', self.unexpectedSuccesses,
532 self._printUnexpectedSuccess)
533
534
535 def _getSummary(self):
536 """
537 Return a formatted count of tests status results.
538 """
539 summaries = []
540 for stat in ("skips", "expectedFailures", "failures", "errors",
541 "unexpectedSuccesses"):
542 num = len(getattr(self, stat))
543 if num:
544 summaries.append('%s=%d' % (stat, num))
545 if self.successes:
546 summaries.append('successes=%d' % (self.successes,))
547 summary = (summaries and ' ('+', '.join(summaries)+')') or ''
548 return summary
549
550
551 def printSummary(self):
552 """
553 Print a line summarising the test results to the stream.
554 """
555 warnings.warn("printSummary is deprecated in Twisted 8.0.",
556 category=DeprecationWarning, stacklevel=2)
557 self._printSummary()
558
559
560 def _printSummary(self):
561 """
562 Print a line summarising the test results to the stream.
563 """
564 summary = self._getSummary()
565 if self.wasSuccessful():
566 status = "PASSED"
567 else:
568 status = "FAILED"
569 self._write("%s%s\n", status, summary)
570
571
572 def done(self):
573 """
574 Summarize the result of the test run.
575
576 The summary includes a report of all of the errors, todos, skips and
577 so forth that occurred during the run. It also includes the number of
578 tests that were run and how long it took to run them (not including
579 load time).
580
581 Expects that L{_printErrors}, L{_writeln}, L{_write}, L{_printSummary}
582 and L{_separator} are all implemented.
583 """
584 self._printErrors()
585 self._writeln(self._separator)
586 if self._startTime is not None:
587 self._writeln('Ran %d tests in %.3fs', self.testsRun,
588 time.time() - self._startTime)
589 self._write('\n')
590 self._printSummary()
591
592
593
594 class MinimalReporter(Reporter):
595 """
596 A minimalist reporter that prints only a summary of the test result, in
597 the form of (timeTaken, #tests, #tests, #errors, #failures, #skips).
598 """
599
600 def _printErrors(self):
601 """
602 Don't print a detailed summary of errors. We only care about the
603 counts.
604 """
605
606
607 def _printSummary(self):
608 """
609 Print out a one-line summary of the form:
610 '%(runtime) %(number_of_tests) %(number_of_tests) %(num_errors)
611 %(num_failures) %(num_skips)'
612 """
613 numTests = self.testsRun
614 t = (self._startTime - self._getTime(), numTests, numTests,
615 len(self.errors), len(self.failures), len(self.skips))
616 self._writeln(' '.join(map(str, t)))
617
618
619
620 class TextReporter(Reporter):
621 """
622 Simple reporter that prints a single character for each test as it runs,
623 along with the standard Trial summary text.
624 """
625
626 def addSuccess(self, test):
627 super(TextReporter, self).addSuccess(test)
628 self._write('.')
629
630
631 def addError(self, *args):
632 super(TextReporter, self).addError(*args)
633 self._write('E')
634
635
636 def addFailure(self, *args):
637 super(TextReporter, self).addFailure(*args)
638 self._write('F')
639
640
641 def addSkip(self, *args):
642 super(TextReporter, self).addSkip(*args)
643 self._write('S')
644
645
646 def addExpectedFailure(self, *args):
647 super(TextReporter, self).addExpectedFailure(*args)
648 self._write('T')
649
650
651 def addUnexpectedSuccess(self, *args):
652 super(TextReporter, self).addUnexpectedSuccess(*args)
653 self._write('!')
654
655
656
657 class VerboseTextReporter(Reporter):
658 """
659 A verbose reporter that prints the name of each test as it is running.
660
661 Each line is printed with the name of the test, followed by the result of
662 that test.
663 """
664
665 # This is actually the bwverbose option
666
667 def startTest(self, tm):
668 self._write('%s ... ', tm.id())
669 super(VerboseTextReporter, self).startTest(tm)
670
671
672 def addSuccess(self, test):
673 super(VerboseTextReporter, self).addSuccess(test)
674 self._write('[OK]')
675
676
677 def addError(self, *args):
678 super(VerboseTextReporter, self).addError(*args)
679 self._write('[ERROR]')
680
681
682 def addFailure(self, *args):
683 super(VerboseTextReporter, self).addFailure(*args)
684 self._write('[FAILURE]')
685
686
687 def addSkip(self, *args):
688 super(VerboseTextReporter, self).addSkip(*args)
689 self._write('[SKIPPED]')
690
691
692 def addExpectedFailure(self, *args):
693 super(VerboseTextReporter, self).addExpectedFailure(*args)
694 self._write('[TODO]')
695
696
697 def addUnexpectedSuccess(self, *args):
698 super(VerboseTextReporter, self).addUnexpectedSuccess(*args)
699 self._write('[SUCCESS!?!]')
700
701
702 def stopTest(self, test):
703 super(VerboseTextReporter, self).stopTest(test)
704 self._write('\n')
705
706
707
708 class TimingTextReporter(VerboseTextReporter):
709 """
710 Prints out each test as it is running, followed by the time taken for each
711 test to run.
712 """
713
714 def stopTest(self, method):
715 """
716 Mark the test as stopped, and write the time it took to run the test
717 to the stream.
718 """
719 super(TimingTextReporter, self).stopTest(method)
720 self._write("(%.03f secs)\n" % self._lastTime)
721
722
723
724 class _AnsiColorizer(object):
725 """
726 A colorizer is an object that loosely wraps around a stream, allowing
727 callers to write text to the stream in a particular color.
728
729 Colorizer classes must implement C{supported()} and C{write(text, color)}.
730 """
731 _colors = dict(black=30, red=31, green=32, yellow=33,
732 blue=34, magenta=35, cyan=36, white=37)
733
734 def __init__(self, stream):
735 self.stream = stream
736
737 def supported(self):
738 """
739 A class method that returns True if the current platform supports
740 coloring terminal output using this method. Returns False otherwise.
741 """
742 # assuming stderr
743 # isatty() returns False when SSHd into Win32 machine
744 if 'CYGWIN' in os.environ:
745 return True
746 if not sys.stderr.isatty():
747 return False # auto color only on TTYs
748 try:
749 import curses
750 curses.setupterm()
751 return curses.tigetnum("colors") > 2
752 except:
753 # guess false in case of error
754 return False
755 supported = classmethod(supported)
756
757 def write(self, text, color):
758 """
759 Write the given text to the stream in the given color.
760
761 @param text: Text to be written to the stream.
762
763 @param color: A string label for a color. e.g. 'red', 'white'.
764 """
765 color = self._colors[color]
766 self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text))
767
768
769 class _Win32Colorizer(object):
770 """
771 See _AnsiColorizer docstring.
772 """
773 def __init__(self, stream):
774 from win32console import GetStdHandle, STD_OUTPUT_HANDLE, \
775 FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \
776 FOREGROUND_INTENSITY
777 red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN,
778 FOREGROUND_BLUE, FOREGROUND_INTENSITY)
779 self.stream = stream
780 self.screenBuffer = GetStdHandle(STD_OUTPUT_HANDLE)
781 self._colors = {
782 'normal': red | green | blue,
783 'red': red | bold,
784 'green': green | bold,
785 'blue': blue | bold,
786 'yellow': red | green | bold,
787 'magenta': red | blue | bold,
788 'cyan': green | blue | bold,
789 'white': red | green | blue | bold
790 }
791
792 def supported(self):
793 try:
794 import win32console
795 screenBuffer = win32console.GetStdHandle(
796 win32console.STD_OUTPUT_HANDLE)
797 except ImportError:
798 return False
799 import pywintypes
800 try:
801 screenBuffer.SetConsoleTextAttribute(
802 win32console.FOREGROUND_RED |
803 win32console.FOREGROUND_GREEN |
804 win32console.FOREGROUND_BLUE)
805 except pywintypes.error:
806 return False
807 else:
808 return True
809 supported = classmethod(supported)
810
811 def write(self, text, color):
812 color = self._colors[color]
813 self.screenBuffer.SetConsoleTextAttribute(color)
814 self.stream.write(text)
815 self.screenBuffer.SetConsoleTextAttribute(self._colors['normal'])
816
817
818 class _NullColorizer(object):
819 """
820 See _AnsiColorizer docstring.
821 """
822 def __init__(self, stream):
823 self.stream = stream
824
825 def supported(self):
826 return True
827 supported = classmethod(supported)
828
829 def write(self, text, color):
830 self.stream.write(text)
831
832
833
834 class TreeReporter(Reporter):
835 """
836 Print out the tests in the form a tree.
837
838 Tests are indented according to which class and module they belong.
839 Results are printed in ANSI color.
840 """
841
842 currentLine = ''
843 indent = ' '
844 columns = 79
845
846 FAILURE = 'red'
847 ERROR = 'red'
848 TODO = 'blue'
849 SKIP = 'blue'
850 TODONE = 'red'
851 SUCCESS = 'green'
852
853 def __init__(self, stream=sys.stdout, tbformat='default', realtime=False):
854 super(TreeReporter, self).__init__(stream, tbformat, realtime)
855 self._lastTest = []
856 for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]:
857 if colorizer.supported():
858 self._colorizer = colorizer(stream)
859 break
860
861 def getDescription(self, test):
862 """
863 Return the name of the method which 'test' represents. This is
864 what gets displayed in the leaves of the tree.
865
866 e.g. getDescription(TestCase('test_foo')) ==> test_foo
867 """
868 return test.id().split('.')[-1]
869
870 def addSuccess(self, test):
871 super(TreeReporter, self).addSuccess(test)
872 self.endLine('[OK]', self.SUCCESS)
873
874 def addError(self, *args):
875 super(TreeReporter, self).addError(*args)
876 self.endLine('[ERROR]', self.ERROR)
877
878 def addFailure(self, *args):
879 super(TreeReporter, self).addFailure(*args)
880 self.endLine('[FAIL]', self.FAILURE)
881
882 def addSkip(self, *args):
883 super(TreeReporter, self).addSkip(*args)
884 self.endLine('[SKIPPED]', self.SKIP)
885
886 def addExpectedFailure(self, *args):
887 super(TreeReporter, self).addExpectedFailure(*args)
888 self.endLine('[TODO]', self.TODO)
889
890 def addUnexpectedSuccess(self, *args):
891 super(TreeReporter, self).addUnexpectedSuccess(*args)
892 self.endLine('[SUCCESS!?!]', self.TODONE)
893
894 def _write(self, format, *args):
895 if args:
896 format = format % args
897 self.currentLine = format
898 super(TreeReporter, self)._write(self.currentLine)
899
900
901 def _getPreludeSegments(self, testID):
902 """
903 Return a list of all non-leaf segments to display in the tree.
904
905 Normally this is the module and class name.
906 """
907 segments = testID.split('.')[:-1]
908 if len(segments) == 0:
909 return segments
910 segments = [
911 seg for seg in '.'.join(segments[:-1]), segments[-1]
912 if len(seg) > 0]
913 return segments
914
915
916 def _testPrelude(self, testID):
917 """
918 Write the name of the test to the stream, indenting it appropriately.
919
920 If the test is the first test in a new 'branch' of the tree, also
921 write all of the parents in that branch.
922 """
923 segments = self._getPreludeSegments(testID)
924 indentLevel = 0
925 for seg in segments:
926 if indentLevel < len(self._lastTest):
927 if seg != self._lastTest[indentLevel]:
928 self._write('%s%s\n' % (self.indent * indentLevel, seg))
929 else:
930 self._write('%s%s\n' % (self.indent * indentLevel, seg))
931 indentLevel += 1
932 self._lastTest = segments
933
934
935 def cleanupErrors(self, errs):
936 self._colorizer.write(' cleanup errors', self.ERROR)
937 self.endLine('[ERROR]', self.ERROR)
938 super(TreeReporter, self).cleanupErrors(errs)
939
940 def upDownError(self, method, error, warn, printStatus):
941 self._colorizer.write(" %s" % method, self.ERROR)
942 if printStatus:
943 self.endLine('[ERROR]', self.ERROR)
944 super(TreeReporter, self).upDownError(method, error, warn, printStatus)
945
946 def startTest(self, test):
947 """
948 Called when C{test} starts. Writes the tests name to the stream using
949 a tree format.
950 """
951 self._testPrelude(test.id())
952 self._write('%s%s ... ' % (self.indent * (len(self._lastTest)),
953 self.getDescription(test)))
954 super(TreeReporter, self).startTest(test)
955
956
957 def endLine(self, message, color):
958 """
959 Print 'message' in the given color.
960
961 @param message: A string message, usually '[OK]' or something similar.
962 @param color: A string color, 'red', 'green' and so forth.
963 """
964 spaces = ' ' * (self.columns - len(self.currentLine) - len(message))
965 super(TreeReporter, self)._write(spaces)
966 self._colorizer.write(message, color)
967 super(TreeReporter, self)._write("\n")
968
969
970 def _printSummary(self):
971 """
972 Print a line summarising the test results to the stream, and color the
973 status result.
974 """
975 summary = self._getSummary()
976 if self.wasSuccessful():
977 status = "PASSED"
978 color = self.SUCCESS
979 else:
980 status = "FAILED"
981 color = self.FAILURE
982 self._colorizer.write(status, color)
983 self._write("%s\n", summary)
OLDNEW
« no previous file with comments | « third_party/twisted_8_1/twisted/trial/itrial.py ('k') | third_party/twisted_8_1/twisted/trial/runner.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698