Home | History | Annotate | Download | only in unittest
      1 """Test result object"""
      2 
      3 import os
      4 import sys
      5 import traceback
      6 
      7 from StringIO import StringIO
      8 
      9 from . import util
     10 from functools import wraps
     11 
     12 __unittest = True
     13 
     14 def failfast(method):
     15     @wraps(method)
     16     def inner(self, *args, **kw):
     17         if getattr(self, 'failfast', False):
     18             self.stop()
     19         return method(self, *args, **kw)
     20     return inner
     21 
     22 STDOUT_LINE = '\nStdout:\n%s'
     23 STDERR_LINE = '\nStderr:\n%s'
     24 
     25 
     26 class TestResult(object):
     27     """Holder for test result information.
     28 
     29     Test results are automatically managed by the TestCase and TestSuite
     30     classes, and do not need to be explicitly manipulated by writers of tests.
     31 
     32     Each instance holds the total number of tests run, and collections of
     33     failures and errors that occurred among those test runs. The collections
     34     contain tuples of (testcase, exceptioninfo), where exceptioninfo is the
     35     formatted traceback of the error that occurred.
     36     """
     37     _previousTestClass = None
     38     _testRunEntered = False
     39     _moduleSetUpFailed = False
     40     def __init__(self, stream=None, descriptions=None, verbosity=None):
     41         self.failfast = False
     42         self.failures = []
     43         self.errors = []
     44         self.testsRun = 0
     45         self.skipped = []
     46         self.expectedFailures = []
     47         self.unexpectedSuccesses = []
     48         self.shouldStop = False
     49         self.buffer = False
     50         self._stdout_buffer = None
     51         self._stderr_buffer = None
     52         self._original_stdout = sys.stdout
     53         self._original_stderr = sys.stderr
     54         self._mirrorOutput = False
     55 
     56     def printErrors(self):
     57         "Called by TestRunner after test run"
     58 
     59     def startTest(self, test):
     60         "Called when the given test is about to be run"
     61         self.testsRun += 1
     62         self._mirrorOutput = False
     63         self._setupStdout()
     64 
     65     def _setupStdout(self):
     66         if self.buffer:
     67             if self._stderr_buffer is None:
     68                 self._stderr_buffer = StringIO()
     69                 self._stdout_buffer = StringIO()
     70             sys.stdout = self._stdout_buffer
     71             sys.stderr = self._stderr_buffer
     72 
     73     def startTestRun(self):
     74         """Called once before any tests are executed.
     75 
     76         See startTest for a method called before each test.
     77         """
     78 
     79     def stopTest(self, test):
     80         """Called when the given test has been run"""
     81         self._restoreStdout()
     82         self._mirrorOutput = False
     83 
     84     def _restoreStdout(self):
     85         if self.buffer:
     86             if self._mirrorOutput:
     87                 output = sys.stdout.getvalue()
     88                 error = sys.stderr.getvalue()
     89                 if output:
     90                     if not output.endswith('\n'):
     91                         output += '\n'
     92                     self._original_stdout.write(STDOUT_LINE % output)
     93                 if error:
     94                     if not error.endswith('\n'):
     95                         error += '\n'
     96                     self._original_stderr.write(STDERR_LINE % error)
     97 
     98             sys.stdout = self._original_stdout
     99             sys.stderr = self._original_stderr
    100             self._stdout_buffer.seek(0)
    101             self._stdout_buffer.truncate()
    102             self._stderr_buffer.seek(0)
    103             self._stderr_buffer.truncate()
    104 
    105     def stopTestRun(self):
    106         """Called once after all tests are executed.
    107 
    108         See stopTest for a method called after each test.
    109         """
    110 
    111     @failfast
    112     def addError(self, test, err):
    113         """Called when an error has occurred. 'err' is a tuple of values as
    114         returned by sys.exc_info().
    115         """
    116         self.errors.append((test, self._exc_info_to_string(err, test)))
    117         self._mirrorOutput = True
    118 
    119     @failfast
    120     def addFailure(self, test, err):
    121         """Called when an error has occurred. 'err' is a tuple of values as
    122         returned by sys.exc_info()."""
    123         self.failures.append((test, self._exc_info_to_string(err, test)))
    124         self._mirrorOutput = True
    125 
    126     def addSuccess(self, test):
    127         "Called when a test has completed successfully"
    128         pass
    129 
    130     def addSkip(self, test, reason):
    131         """Called when a test is skipped."""
    132         self.skipped.append((test, reason))
    133 
    134     def addExpectedFailure(self, test, err):
    135         """Called when an expected failure/error occured."""
    136         self.expectedFailures.append(
    137             (test, self._exc_info_to_string(err, test)))
    138 
    139     @failfast
    140     def addUnexpectedSuccess(self, test):
    141         """Called when a test was expected to fail, but succeed."""
    142         self.unexpectedSuccesses.append(test)
    143 
    144     def wasSuccessful(self):
    145         "Tells whether or not this result was a success"
    146         return len(self.failures) == len(self.errors) == 0
    147 
    148     def stop(self):
    149         "Indicates that the tests should be aborted"
    150         self.shouldStop = True
    151 
    152     def _exc_info_to_string(self, err, test):
    153         """Converts a sys.exc_info()-style tuple of values into a string."""
    154         exctype, value, tb = err
    155         # Skip test runner traceback levels
    156         while tb and self._is_relevant_tb_level(tb):
    157             tb = tb.tb_next
    158 
    159         if exctype is test.failureException:
    160             # Skip assert*() traceback levels
    161             length = self._count_relevant_tb_levels(tb)
    162             msgLines = traceback.format_exception(exctype, value, tb, length)
    163         else:
    164             msgLines = traceback.format_exception(exctype, value, tb)
    165 
    166         if self.buffer:
    167             output = sys.stdout.getvalue()
    168             error = sys.stderr.getvalue()
    169             if output:
    170                 if not output.endswith('\n'):
    171                     output += '\n'
    172                 msgLines.append(STDOUT_LINE % output)
    173             if error:
    174                 if not error.endswith('\n'):
    175                     error += '\n'
    176                 msgLines.append(STDERR_LINE % error)
    177         return ''.join(msgLines)
    178 
    179 
    180     def _is_relevant_tb_level(self, tb):
    181         return '__unittest' in tb.tb_frame.f_globals
    182 
    183     def _count_relevant_tb_levels(self, tb):
    184         length = 0
    185         while tb and not self._is_relevant_tb_level(tb):
    186             length += 1
    187             tb = tb.tb_next
    188         return length
    189 
    190     def __repr__(self):
    191         return ("<%s run=%i errors=%i failures=%i>" %
    192                (util.strclass(self.__class__), self.testsRun, len(self.errors),
    193                 len(self.failures)))
    194