1
1
# -*- test-case-name: twisted.trial.test.test_reporter -*-
3
# Copyright (c) 2001-2007 Twisted Matrix Laboratories.
3
# Copyright (c) 2001-2008 Twisted Matrix Laboratories.
4
4
# See LICENSE for details.
6
# Maintainer: Jonathan Lange <jml@twistedmatrix.com>
6
# Maintainer: Jonathan Lange
9
9
Defines classes that handle the results of tests.
89
91
self._testStarted = self._getTime()
91
93
def stopTest(self, test):
92
"""This must be called after the given test is completed.
95
This must be called after the given test is completed.
94
97
@type test: L{pyunit.TestCase}
97
100
self._lastTime = self._getTime() - self._testStarted
99
102
def addFailure(self, test, fail):
100
"""Report a failed assertion for the given test.
104
Report a failed assertion for the given test.
102
106
@type test: L{pyunit.TestCase}
103
107
@type fail: L{Failure} or L{tuple}
105
109
self.failures.append((test, self._getFailure(fail)))
107
111
def addError(self, test, error):
108
"""Report an error that occurred while running the given test.
113
Report an error that occurred while running the given test.
110
115
@type test: L{pyunit.TestCase}
111
@type fail: L{Failure} or L{tuple}
116
@type error: L{Failure} or L{tuple}
113
118
self.errors.append((test, self._getFailure(error)))
293
298
class Reporter(TestResult):
295
300
A basic L{TestResult} with support for writing to a stream.
302
@ivar _startTime: The time when the first test was started. It defaults to
303
C{None}, which means that no test was actually launched.
304
@type _startTime: C{float} or C{NoneType}
306
@ivar _warningCache: A C{set} of tuples of warning message (file, line,
307
text, category) which have already been written to the output stream
308
during the currently executing test. This is used to avoid writing
309
duplicates of the same warning to the output stream.
310
@type _warningCache: C{set}
312
@ivar _publisher: The log publisher which will be observed for warning
314
@type _publisher: L{LogPublisher} (or another type sufficiently similar)
298
317
implements(itrial.IReporter)
300
319
_separator = '-' * 79
301
320
_doubleSeparator = '=' * 79
303
def __init__(self, stream=sys.stdout, tbformat='default', realtime=False):
322
def __init__(self, stream=sys.stdout, tbformat='default', realtime=False,
304
324
super(Reporter, self).__init__()
305
325
self._stream = SafeStream(stream)
306
326
self.tbformat = tbformat
307
327
self.realtime = realtime
308
# The time when the first test was started.
309
328
self._startTime = None
329
self._warningCache = set()
331
# Start observing log events so as to be able to report warnings.
332
self._publisher = publisher
333
if publisher is not None:
334
publisher.addObserver(self._observeWarnings)
337
def _observeWarnings(self, event):
339
Observe warning events and write them to C{self._stream}.
341
This method is a log observer which will be registered with
342
C{self._publisher.addObserver}.
344
@param event: A C{dict} from the logging system. If it has a
345
C{'warning'} key, a logged warning will be extracted from it and
346
possibly written to C{self.stream}.
348
if 'warning' in event:
349
key = (event['filename'], event['lineno'],
350
event['category'].split('.')[-1],
351
str(event['warning']))
352
if key not in self._warningCache:
353
self._warningCache.add(key)
354
self._stream.write('%s:%s: %s: %s\n' % key)
312
357
def stream(self):
313
358
warnings.warn("stream is deprecated in Twisted 8.0.",
314
category=DeprecationWarning, stacklevel=4)
359
category=DeprecationWarning, stacklevel=2)
315
360
return self._stream
316
361
stream = property(stream)
319
364
def separator(self):
320
365
warnings.warn("separator is deprecated in Twisted 8.0.",
321
category=DeprecationWarning, stacklevel=4)
366
category=DeprecationWarning, stacklevel=2)
322
367
return self._separator
323
368
separator = property(separator)
326
371
def startTest(self, test):
328
373
Called when a test begins to run. Records the time when it was first
374
called and resets the warning cache.
331
376
@param test: L{ITestCase}
333
378
super(Reporter, self).startTest(test)
334
379
if self._startTime is None:
335
self._startTime = time.time()
380
self._startTime = self._getTime()
381
self._warningCache = set()
338
384
def addFailure(self, test, fail):
581
627
Expects that L{_printErrors}, L{_writeln}, L{_write}, L{_printSummary}
582
628
and L{_separator} are all implemented.
630
if self._publisher is not None:
631
self._publisher.removeObserver(self._observeWarnings)
584
632
self._printErrors()
585
633
self._writeln(self._separator)
586
634
if self._startTime is not None:
611
659
%(num_failures) %(num_skips)'
613
661
numTests = self.testsRun
614
t = (self._startTime - self._getTime(), numTests, numTests,
662
if self._startTime is not None:
663
timing = self._getTime() - self._startTime
666
t = (timing, numTests, numTests,
615
667
len(self.errors), len(self.failures), len(self.skips))
616
668
self._writeln(' '.join(map(str, t)))
734
786
def __init__(self, stream):
735
787
self.stream = stream
789
def supported(cls, stream=sys.stdout):
739
791
A class method that returns True if the current platform supports
740
792
coloring terminal output using this method. Returns False otherwise.
743
# isatty() returns False when SSHd into Win32 machine
744
if 'CYGWIN' in os.environ:
746
if not sys.stderr.isatty():
794
if not stream.isatty():
747
795
return False # auto color only on TTYs
751
return curses.tigetnum("colors") > 2
753
# guess false in case of error
803
return curses.tigetnum("colors") > 2
806
return curses.tigetnum("colors") > 2
808
# guess false in case of error
755
810
supported = classmethod(supported)
757
812
def write(self, text, color):
851
906
SUCCESS = 'green'
853
def __init__(self, stream=sys.stdout, tbformat='default', realtime=False):
854
super(TreeReporter, self).__init__(stream, tbformat, realtime)
908
def __init__(self, stream=sys.stdout, *args, **kwargs):
909
super(TreeReporter, self).__init__(stream, *args, **kwargs)
855
910
self._lastTest = []
856
911
for colorizer in [_Win32Colorizer, _AnsiColorizer, _NullColorizer]:
857
if colorizer.supported():
912
if colorizer.supported(stream):
858
913
self._colorizer = colorizer(stream)