1
# -*- test-case-name: twisted.trial.test.test_reporter -*-
2
# Copyright (c) 2001-2009 Twisted Matrix Laboratories.
3
# See LICENSE for details.
5
# Maintainer: Jonathan Lange
8
Defines classes that handle the results of tests.
15
from twisted.python.compat import set
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
23
from subunit import TestProtocolClient
25
TestProtocolClient = None
26
from zope.interface import implements
28
pyunit = __import__('unittest')
31
class BrokenTestCaseWarning(Warning):
33
Emitted as a warning when an exception occurs in one of setUp or tearDown.
37
class SafeStream(object):
39
Wraps a stream object so that all C{write} calls are wrapped in
43
def __init__(self, original):
44
self.original = original
46
def __getattr__(self, name):
47
return getattr(self.original, name)
49
def write(self, *a, **kw):
50
return untilConcludes(self.original.write, *a, **kw)
53
class TestResult(pyunit.TestResult, object):
55
Accumulates the results of several L{twisted.trial.unittest.TestCase}s.
57
@ivar successes: count the number of successes achieved by the test run.
58
@type successes: C{int}
60
implements(itrial.IReporter)
63
super(TestResult, self).__init__()
65
self.expectedFailures = []
66
self.unexpectedSuccesses = []
71
return ('<%s run=%d errors=%d failures=%d todos=%d dones=%d skips=%d>'
72
% (reflect.qual(self.__class__), self.testsRun,
73
len(self.errors), len(self.failures),
74
len(self.expectedFailures), len(self.skips),
75
len(self.unexpectedSuccesses)))
80
def _getFailure(self, error):
82
Convert a C{sys.exc_info()}-style tuple to a L{Failure}, if necessary.
84
if isinstance(error, tuple):
85
return Failure(error[1], error[0], error[2])
88
def startTest(self, test):
90
This must be called before the given test is commenced.
92
@type test: L{pyunit.TestCase}
94
super(TestResult, self).startTest(test)
95
self._testStarted = self._getTime()
97
def stopTest(self, test):
99
This must be called after the given test is completed.
101
@type test: L{pyunit.TestCase}
103
super(TestResult, self).stopTest(test)
104
self._lastTime = self._getTime() - self._testStarted
106
def addFailure(self, test, fail):
108
Report a failed assertion for the given test.
110
@type test: L{pyunit.TestCase}
111
@type fail: L{Failure} or L{tuple}
113
self.failures.append((test, self._getFailure(fail)))
115
def addError(self, test, error):
117
Report an error that occurred while running the given test.
119
@type test: L{pyunit.TestCase}
120
@type error: L{Failure} or L{tuple}
122
self.errors.append((test, self._getFailure(error)))
124
def addSkip(self, test, reason):
126
Report that the given test was skipped.
128
In Trial, tests can be 'skipped'. Tests are skipped mostly because there
129
is some platform or configuration issue that prevents them from being
132
@type test: L{pyunit.TestCase}
135
self.skips.append((test, reason))
137
def addUnexpectedSuccess(self, test, todo):
138
"""Report that the given test succeeded against expectations.
140
In Trial, tests can be marked 'todo'. That is, they are expected to fail.
141
When a test that is expected to fail instead succeeds, it should call
142
this method to report the unexpected success.
144
@type test: L{pyunit.TestCase}
145
@type todo: L{unittest.Todo}
147
# XXX - 'todo' should just be a string
148
self.unexpectedSuccesses.append((test, todo))
150
def addExpectedFailure(self, test, error, todo):
151
"""Report that the given test failed, and was expected to do so.
153
In Trial, tests can be marked 'todo'. That is, they are expected to fail.
155
@type test: L{pyunit.TestCase}
156
@type error: L{Failure}
157
@type todo: L{unittest.Todo}
159
# XXX - 'todo' should just be a string
160
self.expectedFailures.append((test, error, todo))
162
def addSuccess(self, test):
163
"""Report that the given test succeeded.
165
@type test: L{pyunit.TestCase}
169
def upDownError(self, method, error, warn, printStatus):
170
warnings.warn("upDownError is deprecated in Twisted 8.0.",
171
category=DeprecationWarning, stacklevel=3)
173
def cleanupErrors(self, errs):
174
"""Report an error that occurred during the cleanup between tests.
176
warnings.warn("Cleanup errors are actual errors. Use addError. "
177
"Deprecated in Twisted 8.0",
178
category=DeprecationWarning, stacklevel=2)
180
def startSuite(self, name):
181
warnings.warn("startSuite deprecated in Twisted 8.0",
182
category=DeprecationWarning, stacklevel=2)
184
def endSuite(self, name):
185
warnings.warn("endSuite deprecated in Twisted 8.0",
186
category=DeprecationWarning, stacklevel=2)
191
The test suite has finished running.
196
class TestResultDecorator(proxyForInterface(itrial.IReporter,
197
"_originalReporter")):
199
Base class for TestResult decorators.
201
@ivar _originalReporter: The wrapped instance of reporter.
202
@type _originalReporter: A provider of L{itrial.IReporter}
205
implements(itrial.IReporter)
209
class UncleanWarningsReporterWrapper(TestResultDecorator):
211
A wrapper for a reporter that converts L{util.DirtyReactorError}s
214
implements(itrial.IReporter)
216
def addError(self, test, error):
218
If the error is a L{util.DirtyReactorError}, instead of
219
reporting it as a normal error, throw a warning.
222
if (isinstance(error, Failure)
223
and error.check(util.DirtyReactorAggregateError)):
224
warnings.warn(error.getErrorMessage())
226
self._originalReporter.addError(test, error)
230
class _AdaptedReporter(TestResultDecorator):
232
TestResult decorator that makes sure that addError only gets tests that
233
have been adapted with a particular test adapter.
236
def __init__(self, original, testAdapter):
238
Construct an L{_AdaptedReporter}.
240
@param original: An {itrial.IReporter}.
241
@param testAdapter: A callable that returns an L{itrial.ITestCase}.
243
TestResultDecorator.__init__(self, original)
244
self.testAdapter = testAdapter
247
def addError(self, test, error):
249
See L{itrial.IReporter}.
251
test = self.testAdapter(test)
252
return self._originalReporter.addError(test, error)
255
def addExpectedFailure(self, test, failure, todo):
257
See L{itrial.IReporter}.
259
return self._originalReporter.addExpectedFailure(
260
self.testAdapter(test), failure, todo)
263
def addFailure(self, test, failure):
265
See L{itrial.IReporter}.
267
test = self.testAdapter(test)
268
return self._originalReporter.addFailure(test, failure)
271
def addSkip(self, test, skip):
273
See L{itrial.IReporter}.
275
test = self.testAdapter(test)
276
return self._originalReporter.addSkip(test, skip)
279
def addUnexpectedSuccess(self, test, todo):
281
See L{itrial.IReporter}.
283
test = self.testAdapter(test)
284
return self._originalReporter.addUnexpectedSuccess(test, todo)
287
def startTest(self, test):
289
See L{itrial.IReporter}.
291
return self._originalReporter.startTest(self.testAdapter(test))
294
def stopTest(self, test):
296
See L{itrial.IReporter}.
298
return self._originalReporter.stopTest(self.testAdapter(test))
302
class Reporter(TestResult):
304
A basic L{TestResult} with support for writing to a stream.
306
@ivar _startTime: The time when the first test was started. It defaults to
307
C{None}, which means that no test was actually launched.
308
@type _startTime: C{float} or C{NoneType}
310
@ivar _warningCache: A C{set} of tuples of warning message (file, line,
311
text, category) which have already been written to the output stream
312
during the currently executing test. This is used to avoid writing
313
duplicates of the same warning to the output stream.
314
@type _warningCache: C{set}
316
@ivar _publisher: The log publisher which will be observed for warning
318
@type _publisher: L{LogPublisher} (or another type sufficiently similar)
321
implements(itrial.IReporter)
323
_separator = '-' * 79
324
_doubleSeparator = '=' * 79
326
def __init__(self, stream=sys.stdout, tbformat='default', realtime=False,
328
super(Reporter, self).__init__()
329
self._stream = SafeStream(stream)
330
self.tbformat = tbformat
331
self.realtime = realtime
332
self._startTime = None
333
self._warningCache = set()
335
# Start observing log events so as to be able to report warnings.
336
self._publisher = publisher
337
if publisher is not None:
338
publisher.addObserver(self._observeWarnings)
341
def _observeWarnings(self, event):
343
Observe warning events and write them to C{self._stream}.
345
This method is a log observer which will be registered with
346
C{self._publisher.addObserver}.
348
@param event: A C{dict} from the logging system. If it has a
349
C{'warning'} key, a logged warning will be extracted from it and
350
possibly written to C{self.stream}.
352
if 'warning' in event:
353
key = (event['filename'], event['lineno'],
354
event['category'].split('.')[-1],
355
str(event['warning']))
356
if key not in self._warningCache:
357
self._warningCache.add(key)
358
self._stream.write('%s:%s: %s: %s\n' % key)
362
warnings.warn("stream is deprecated in Twisted 8.0.",
363
category=DeprecationWarning, stacklevel=2)
365
stream = property(stream)
369
warnings.warn("separator is deprecated in Twisted 8.0.",
370
category=DeprecationWarning, stacklevel=2)
371
return self._separator
372
separator = property(separator)
375
def startTest(self, test):
377
Called when a test begins to run. Records the time when it was first
378
called and resets the warning cache.
380
@param test: L{ITestCase}
382
super(Reporter, self).startTest(test)
383
if self._startTime is None:
384
self._startTime = self._getTime()
385
self._warningCache = set()
388
def addFailure(self, test, fail):
390
Called when a test fails. If L{realtime} is set, then it prints the
393
@param test: L{ITestCase} that failed.
394
@param fail: L{failure.Failure} containing the error.
396
super(Reporter, self).addFailure(test, fail)
398
fail = self.failures[-1][1] # guarantee it's a Failure
399
self._write(self._formatFailureTraceback(fail))
402
def addError(self, test, error):
404
Called when a test raises an error. If L{realtime} is set, then it
405
prints the error to the stream.
407
@param test: L{ITestCase} that raised the error.
408
@param error: L{failure.Failure} containing the error.
410
error = self._getFailure(error)
411
super(Reporter, self).addError(test, error)
413
error = self.errors[-1][1] # guarantee it's a Failure
414
self._write(self._formatFailureTraceback(error))
417
def write(self, format, *args):
418
warnings.warn("write is deprecated in Twisted 8.0.",
419
category=DeprecationWarning, stacklevel=2)
420
self._write(format, *args)
423
def _write(self, format, *args):
425
Safely write to the reporter's stream.
427
@param format: A format string to write.
428
@param *args: The arguments for the format string.
431
assert isinstance(s, type(''))
433
self._stream.write(s % args)
435
self._stream.write(s)
436
untilConcludes(self._stream.flush)
439
def writeln(self, format, *args):
440
warnings.warn("writeln is deprecated in Twisted 8.0.",
441
category=DeprecationWarning, stacklevel=2)
442
self._writeln(format, *args)
445
def _writeln(self, format, *args):
447
Safely write a line to the reporter's stream. Newline is appended to
450
@param format: A format string to write.
451
@param *args: The arguments for the format string.
453
self._write(format, *args)
457
def upDownError(self, method, error, warn, printStatus):
458
super(Reporter, self).upDownError(method, error, warn, printStatus)
460
tbStr = self._formatFailureTraceback(error)
462
msg = ("caught exception in %s, your TestCase is broken\n\n%s"
464
warnings.warn(msg, BrokenTestCaseWarning, stacklevel=2)
467
def cleanupErrors(self, errs):
468
super(Reporter, self).cleanupErrors(errs)
469
warnings.warn("%s\n%s" % ("REACTOR UNCLEAN! traceback(s) follow: ",
470
self._formatFailureTraceback(errs)),
471
BrokenTestCaseWarning)
474
def _trimFrames(self, frames):
475
# when a method fails synchronously, the stack looks like this:
476
# [0]: defer.maybeDeferred()
477
# [1]: utils.runWithWarningsSuppressed()
478
# [2:-2]: code in the test method which failed
479
# [-1]: unittest.fail
481
# when a method fails inside a Deferred (i.e., when the test method
482
# returns a Deferred, and that Deferred's errback fires), the stack
483
# captured inside the resulting Failure looks like this:
484
# [0]: defer.Deferred._runCallbacks
485
# [1:-2]: code in the testmethod which failed
486
# [-1]: unittest.fail
488
# as a result, we want to trim either [maybeDeferred,runWWS] or
489
# [Deferred._runCallbacks] from the front, and trim the
490
# [unittest.fail] from the end.
492
# There is also another case, when the test method is badly defined and
493
# contains extra arguments.
495
newFrames = list(frames)
501
second = newFrames[1]
502
if (first[0] == "maybeDeferred"
503
and os.path.splitext(os.path.basename(first[1]))[0] == 'defer'
504
and second[0] == "runWithWarningsSuppressed"
505
and os.path.splitext(os.path.basename(second[1]))[0] == 'utils'):
506
newFrames = newFrames[2:]
507
elif (first[0] == "_runCallbacks"
508
and os.path.splitext(os.path.basename(first[1]))[0] == 'defer'):
509
newFrames = newFrames[1:]
512
# The method fails before getting called, probably an argument problem
516
if (last[0].startswith('fail')
517
and os.path.splitext(os.path.basename(last[1]))[0] == 'unittest'):
518
newFrames = newFrames[:-1]
523
def _formatFailureTraceback(self, fail):
524
if isinstance(fail, str):
525
return fail.rstrip() + '\n'
526
fail.frames, frames = self._trimFrames(fail.frames), fail.frames
527
result = fail.getTraceback(detail=self.tbformat, elideFrameworkCode=True)
532
def _printResults(self, flavour, errors, formatter):
534
Print a group of errors to the stream.
536
@param flavour: A string indicating the kind of error (e.g. 'TODO').
537
@param errors: A list of errors, often L{failure.Failure}s, but
538
sometimes 'todo' errors.
539
@param formatter: A callable that knows how to format the errors.
541
for content in errors:
542
self._writeln(self._doubleSeparator)
543
self._writeln('%s: %s' % (flavour, content[0].id()))
545
self._write(formatter(*(content[1:])))
548
def _printExpectedFailure(self, error, todo):
549
return 'Reason: %r\n%s' % (todo.reason,
550
self._formatFailureTraceback(error))
553
def _printUnexpectedSuccess(self, todo):
554
ret = 'Reason: %r\n' % (todo.reason,)
556
ret += 'Expected errors: %s\n' % (', '.join(todo.errors),)
560
def printErrors(self):
562
Print all of the non-success results in full to the stream.
564
warnings.warn("printErrors is deprecated in Twisted 8.0.",
565
category=DeprecationWarning, stacklevel=2)
569
def _printErrors(self):
571
Print all of the non-success results to the stream in full.
574
self._printResults('[SKIPPED]', self.skips, lambda x : '%s\n' % x)
575
self._printResults('[TODO]', self.expectedFailures,
576
self._printExpectedFailure)
577
self._printResults('[FAIL]', self.failures,
578
self._formatFailureTraceback)
579
self._printResults('[ERROR]', self.errors,
580
self._formatFailureTraceback)
581
self._printResults('[SUCCESS!?!]', self.unexpectedSuccesses,
582
self._printUnexpectedSuccess)
585
def _getSummary(self):
587
Return a formatted count of tests status results.
590
for stat in ("skips", "expectedFailures", "failures", "errors",
591
"unexpectedSuccesses"):
592
num = len(getattr(self, stat))
594
summaries.append('%s=%d' % (stat, num))
596
summaries.append('successes=%d' % (self.successes,))
597
summary = (summaries and ' ('+', '.join(summaries)+')') or ''
601
def printSummary(self):
603
Print a line summarising the test results to the stream.
605
warnings.warn("printSummary is deprecated in Twisted 8.0.",
606
category=DeprecationWarning, stacklevel=2)
610
def _printSummary(self):
612
Print a line summarising the test results to the stream.
614
summary = self._getSummary()
615
if self.wasSuccessful():
619
self._write("%s%s\n", status, summary)
624
Summarize the result of the test run.
626
The summary includes a report of all of the errors, todos, skips and
627
so forth that occurred during the run. It also includes the number of
628
tests that were run and how long it took to run them (not including
631
Expects that L{_printErrors}, L{_writeln}, L{_write}, L{_printSummary}
632
and L{_separator} are all implemented.
634
if self._publisher is not None:
635
self._publisher.removeObserver(self._observeWarnings)
637
self._writeln(self._separator)
638
if self._startTime is not None:
639
self._writeln('Ran %d tests in %.3fs', self.testsRun,
640
time.time() - self._startTime)
646
class MinimalReporter(Reporter):
648
A minimalist reporter that prints only a summary of the test result, in
649
the form of (timeTaken, #tests, #tests, #errors, #failures, #skips).
652
def _printErrors(self):
654
Don't print a detailed summary of errors. We only care about the
659
def _printSummary(self):
661
Print out a one-line summary of the form:
662
'%(runtime) %(number_of_tests) %(number_of_tests) %(num_errors)
663
%(num_failures) %(num_skips)'
665
numTests = self.testsRun
666
if self._startTime is not None:
667
timing = self._getTime() - self._startTime
670
t = (timing, numTests, numTests,
671
len(self.errors), len(self.failures), len(self.skips))
672
self._writeln(' '.join(map(str, t)))
676
class TextReporter(Reporter):
678
Simple reporter that prints a single character for each test as it runs,
679
along with the standard Trial summary text.
682
def addSuccess(self, test):
683
super(TextReporter, self).addSuccess(test)
687
def addError(self, *args):
688
super(TextReporter, self).addError(*args)
692
def addFailure(self, *args):
693
super(TextReporter, self).addFailure(*args)
697
def addSkip(self, *args):
698
super(TextReporter, self).addSkip(*args)
702
def addExpectedFailure(self, *args):
703
super(TextReporter, self).addExpectedFailure(*args)
707
def addUnexpectedSuccess(self, *args):
708
super(TextReporter, self).addUnexpectedSuccess(*args)
713
class VerboseTextReporter(Reporter):
715
A verbose reporter that prints the name of each test as it is running.
717
Each line is printed with the name of the test, followed by the result of
721
# This is actually the bwverbose option
723
def startTest(self, tm):
724
self._write('%s ... ', tm.id())
725
super(VerboseTextReporter, self).startTest(tm)
728
def addSuccess(self, test):
729
super(VerboseTextReporter, self).addSuccess(test)
733
def addError(self, *args):
734
super(VerboseTextReporter, self).addError(*args)
735
self._write('[ERROR]')
738
def addFailure(self, *args):
739
super(VerboseTextReporter, self).addFailure(*args)
740
self._write('[FAILURE]')
743
def addSkip(self, *args):
744
super(VerboseTextReporter, self).addSkip(*args)
745
self._write('[SKIPPED]')
748
def addExpectedFailure(self, *args):
749
super(VerboseTextReporter, self).addExpectedFailure(*args)
750
self._write('[TODO]')
753
def addUnexpectedSuccess(self, *args):
754
super(VerboseTextReporter, self).addUnexpectedSuccess(*args)
755
self._write('[SUCCESS!?!]')
758
def stopTest(self, test):
759
super(VerboseTextReporter, self).stopTest(test)
764
class TimingTextReporter(VerboseTextReporter):
766
Prints out each test as it is running, followed by the time taken for each
770
def stopTest(self, method):
772
Mark the test as stopped, and write the time it took to run the test
775
super(TimingTextReporter, self).stopTest(method)
776
self._write("(%.03f secs)\n" % self._lastTime)
780
class _AnsiColorizer(object):
782
A colorizer is an object that loosely wraps around a stream, allowing
783
callers to write text to the stream in a particular color.
785
Colorizer classes must implement C{supported()} and C{write(text, color)}.
787
_colors = dict(black=30, red=31, green=32, yellow=33,
788
blue=34, magenta=35, cyan=36, white=37)
790
def __init__(self, stream):
793
def supported(cls, stream=sys.stdout):
795
A class method that returns True if the current platform supports
796
coloring terminal output using this method. Returns False otherwise.
798
if not stream.isatty():
799
return False # auto color only on TTYs
807
return curses.tigetnum("colors") > 2
810
return curses.tigetnum("colors") > 2
812
# guess false in case of error
814
supported = classmethod(supported)
816
def write(self, text, color):
818
Write the given text to the stream in the given color.
820
@param text: Text to be written to the stream.
822
@param color: A string label for a color. e.g. 'red', 'white'.
824
color = self._colors[color]
825
self.stream.write('\x1b[%s;1m%s\x1b[0m' % (color, text))
828
class _Win32Colorizer(object):
830
See _AnsiColorizer docstring.
832
def __init__(self, stream):
833
from win32console import GetStdHandle, STD_OUTPUT_HANDLE, \
834
FOREGROUND_RED, FOREGROUND_BLUE, FOREGROUND_GREEN, \
836
red, green, blue, bold = (FOREGROUND_RED, FOREGROUND_GREEN,
837
FOREGROUND_BLUE, FOREGROUND_INTENSITY)
839
self.screenBuffer = GetStdHandle(STD_OUTPUT_HANDLE)
841
'normal': red | green | blue,
843
'green': green | bold,
845
'yellow': red | green | bold,
846
'magenta': red | blue | bold,
847
'cyan': green | blue | bold,
848
'white': red | green | blue | bold
851
def supported(cls, stream=sys.stdout):
854
screenBuffer = win32console.GetStdHandle(
855
win32console.STD_OUTPUT_HANDLE)
860
screenBuffer.SetConsoleTextAttribute(
861
win32console.FOREGROUND_RED |
862
win32console.FOREGROUND_GREEN |
863
win32console.FOREGROUND_BLUE)
864
except pywintypes.error:
868
supported = classmethod(supported)
870
def write(self, text, color):
871
color = self._colors[color]
872
self.screenBuffer.SetConsoleTextAttribute(color)
873
self.stream.write(text)
874
self.screenBuffer.SetConsoleTextAttribute(self._colors['normal'])
877
class _NullColorizer(object):
879
See _AnsiColorizer docstring.
881
def __init__(self, stream):
884
def supported(cls, stream=sys.stdout):
886
supported = classmethod(supported)
888
def write(self, text, color):
889
self.stream.write(text)
893
class SubunitReporter(object):
895
Reports test output via Subunit.
897
@ivar _subunit: The subunit protocol client that we are wrapping.
899
@ivar _successful: An internal variable, used to track whether we have
900
received only successful results.
904
implements(itrial.IReporter)
907
def __init__(self, stream=sys.stdout, tbformat='default',
908
realtime=False, publisher=None):
910
Construct a L{SubunitReporter}.
912
@param stream: A file-like object representing the stream to print
913
output to. Defaults to stdout.
914
@param tbformat: The format for tracebacks. Ignored, since subunit
915
always uses Python's standard format.
916
@param realtime: Whether or not to print exceptions in the middle
917
of the test results. Ignored, since subunit always does this.
918
@param publisher: The log publisher which will be preserved for
919
reporting events. Ignored, as it's not relevant to subunit.
921
if TestProtocolClient is None:
922
raise Exception("Subunit not available")
923
self._subunit = TestProtocolClient(stream)
924
self._successful = True
929
Record that the entire test suite run is finished.
931
We do nothing, since a summary clause is irrelevant to the subunit
937
def shouldStop(self):
939
Whether or not the test runner should stop running tests.
941
return self._subunit.shouldStop
942
shouldStop = property(shouldStop)
947
Signal that the test runner should stop running tests.
949
return self._subunit.stop()
952
def wasSuccessful(self):
954
Has the test run been successful so far?
956
@return: C{True} if we have received no reports of errors or failures,
959
# Subunit has a bug in its implementation of wasSuccessful, see
960
# https://bugs.edge.launchpad.net/subunit/+bug/491090, so we can't
961
# simply forward it on.
962
return self._successful
965
def startTest(self, test):
967
Record that C{test} has started.
969
return self._subunit.startTest(test)
972
def stopTest(self, test):
974
Record that C{test} has completed.
976
return self._subunit.stopTest(test)
979
def addSuccess(self, test):
981
Record that C{test} was successful.
983
return self._subunit.addSuccess(test)
986
def addSkip(self, test, reason):
988
Record that C{test} was skipped for C{reason}.
990
Some versions of subunit don't have support for addSkip. In those
991
cases, the skip is reported as a success.
993
@param test: A unittest-compatible C{TestCase}.
994
@param reason: The reason for it being skipped. The C{str()} of this
995
object will be included in the subunit output stream.
997
addSkip = getattr(self._subunit, 'addSkip', None)
999
self.addSuccess(test)
1001
self._subunit.addSkip(test, reason)
1004
def addError(self, test, err):
1006
Record that C{test} failed with an unexpected error C{err}.
1008
Also marks the run as being unsuccessful, causing
1009
L{SubunitReporter.wasSuccessful} to return C{False}.
1011
self._successful = False
1012
return self._subunit.addError(
1013
test, util.excInfoOrFailureToExcInfo(err))
1016
def addFailure(self, test, err):
1018
Record that C{test} failed an assertion with the error C{err}.
1020
Also marks the run as being unsuccessful, causing
1021
L{SubunitReporter.wasSuccessful} to return C{False}.
1023
self._successful = False
1024
return self._subunit.addFailure(
1025
test, util.excInfoOrFailureToExcInfo(err))
1028
def addExpectedFailure(self, test, failure, todo):
1030
Record an expected failure from a test.
1032
Some versions of subunit do not implement this. For those versions, we
1035
failure = util.excInfoOrFailureToExcInfo(failure)
1036
addExpectedFailure = getattr(self._subunit, 'addExpectedFailure', None)
1037
if addExpectedFailure is None:
1038
self.addSuccess(test)
1040
addExpectedFailure(test, failure)
1043
def addUnexpectedSuccess(self, test, todo):
1045
Record an unexpected success.
1047
Since subunit has no way of expressing this concept, we record a
1048
success on the subunit stream.
1050
# Not represented in pyunit/subunit.
1051
self.addSuccess(test)
1055
class TreeReporter(Reporter):
1057
Print out the tests in the form a tree.
1059
Tests are indented according to which class and module they belong.
1060
Results are printed in ANSI color.
1074
def __init__(self, stream=sys.stdout, *args, **kwargs):
1075
super(TreeReporter, self).__init__(stream, *args, **kwargs)
1077
for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]:
1078
if colorizer.supported(stream):
1079
self._colorizer = colorizer(stream)
1082
def getDescription(self, test):
1084
Return the name of the method which 'test' represents. This is
1085
what gets displayed in the leaves of the tree.
1087
e.g. getDescription(TestCase('test_foo')) ==> test_foo
1089
return test.id().split('.')[-1]
1091
def addSuccess(self, test):
1092
super(TreeReporter, self).addSuccess(test)
1093
self.endLine('[OK]', self.SUCCESS)
1095
def addError(self, *args):
1096
super(TreeReporter, self).addError(*args)
1097
self.endLine('[ERROR]', self.ERROR)
1099
def addFailure(self, *args):
1100
super(TreeReporter, self).addFailure(*args)
1101
self.endLine('[FAIL]', self.FAILURE)
1103
def addSkip(self, *args):
1104
super(TreeReporter, self).addSkip(*args)
1105
self.endLine('[SKIPPED]', self.SKIP)
1107
def addExpectedFailure(self, *args):
1108
super(TreeReporter, self).addExpectedFailure(*args)
1109
self.endLine('[TODO]', self.TODO)
1111
def addUnexpectedSuccess(self, *args):
1112
super(TreeReporter, self).addUnexpectedSuccess(*args)
1113
self.endLine('[SUCCESS!?!]', self.TODONE)
1115
def _write(self, format, *args):
1117
format = format % args
1118
self.currentLine = format
1119
super(TreeReporter, self)._write(self.currentLine)
1122
def _getPreludeSegments(self, testID):
1124
Return a list of all non-leaf segments to display in the tree.
1126
Normally this is the module and class name.
1128
segments = testID.split('.')[:-1]
1129
if len(segments) == 0:
1132
seg for seg in '.'.join(segments[:-1]), segments[-1]
1137
def _testPrelude(self, testID):
1139
Write the name of the test to the stream, indenting it appropriately.
1141
If the test is the first test in a new 'branch' of the tree, also
1142
write all of the parents in that branch.
1144
segments = self._getPreludeSegments(testID)
1146
for seg in segments:
1147
if indentLevel < len(self._lastTest):
1148
if seg != self._lastTest[indentLevel]:
1149
self._write('%s%s\n' % (self.indent * indentLevel, seg))
1151
self._write('%s%s\n' % (self.indent * indentLevel, seg))
1153
self._lastTest = segments
1156
def cleanupErrors(self, errs):
1157
self._colorizer.write(' cleanup errors', self.ERROR)
1158
self.endLine('[ERROR]', self.ERROR)
1159
super(TreeReporter, self).cleanupErrors(errs)
1161
def upDownError(self, method, error, warn, printStatus):
1162
self._colorizer.write(" %s" % method, self.ERROR)
1164
self.endLine('[ERROR]', self.ERROR)
1165
super(TreeReporter, self).upDownError(method, error, warn, printStatus)
1167
def startTest(self, test):
1169
Called when C{test} starts. Writes the tests name to the stream using
1172
self._testPrelude(test.id())
1173
self._write('%s%s ... ' % (self.indent * (len(self._lastTest)),
1174
self.getDescription(test)))
1175
super(TreeReporter, self).startTest(test)
1178
def endLine(self, message, color):
1180
Print 'message' in the given color.
1182
@param message: A string message, usually '[OK]' or something similar.
1183
@param color: A string color, 'red', 'green' and so forth.
1185
spaces = ' ' * (self.columns - len(self.currentLine) - len(message))
1186
super(TreeReporter, self)._write(spaces)
1187
self._colorizer.write(message, color)
1188
super(TreeReporter, self)._write("\n")
1191
def _printSummary(self):
1193
Print a line summarising the test results to the stream, and color the
1196
summary = self._getSummary()
1197
if self.wasSuccessful():
1199
color = self.SUCCESS
1202
color = self.FAILURE
1203
self._colorizer.write(status, color)
1204
self._write("%s\n", summary)