Home | History | Annotate | Download | only in unittest2
      1 """Test result object"""
      2 
      3 import sys
      4 import traceback
      5 import unittest
      6 
      7 from StringIO import StringIO
      8 
      9 from unittest2 import util
     10 from unittest2.compatibility 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 
     23 STDOUT_LINE = '\nStdout:\n%s'
     24 STDERR_LINE = '\nStderr:\n%s'
     25 
     26 class TestResult(unittest.TestResult):
     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     _moduleSetUpFailed = False
     39     
     40     def __init__(self):
     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 startTest(self, test):
     57         "Called when the given test is about to be run"
     58         self.testsRun += 1
     59         self._mirrorOutput = False
     60         if self.buffer:
     61             if self._stderr_buffer is None:
     62                 self._stderr_buffer = StringIO()
     63                 self._stdout_buffer = StringIO()
     64             sys.stdout = self._stdout_buffer
     65             sys.stderr = self._stderr_buffer
     66 
     67     def startTestRun(self):
     68         """Called once before any tests are executed.
     69 
     70         See startTest for a method called before each test.
     71         """
     72 
     73     def stopTest(self, test):
     74         """Called when the given test has been run"""
     75         if self.buffer:
     76             if self._mirrorOutput:
     77                 output = sys.stdout.getvalue()
     78                 error = sys.stderr.getvalue()
     79                 if output:
     80                     if not output.endswith('\n'):
     81                         output += '\n'
     82                     self._original_stdout.write(STDOUT_LINE % output)
     83                 if error:
     84                     if not error.endswith('\n'):
     85                         error += '\n'
     86                     self._original_stderr.write(STDERR_LINE % error)
     87                 
     88             sys.stdout = self._original_stdout
     89             sys.stderr = self._original_stderr
     90             self._stdout_buffer.seek(0)
     91             self._stdout_buffer.truncate()
     92             self._stderr_buffer.seek(0)
     93             self._stderr_buffer.truncate()
     94         self._mirrorOutput = False
     95         
     96 
     97     def stopTestRun(self):
     98         """Called once after all tests are executed.
     99 
    100         See stopTest for a method called after each test.
    101         """
    102 
    103     @failfast
    104     def addError(self, test, err):
    105         """Called when an error has occurred. 'err' is a tuple of values as
    106         returned by sys.exc_info().
    107         """
    108         self.errors.append((test, self._exc_info_to_string(err, test)))
    109         self._mirrorOutput = True
    110 
    111     @failfast
    112     def addFailure(self, test, err):
    113         """Called when an error has occurred. 'err' is a tuple of values as
    114         returned by sys.exc_info()."""
    115         self.failures.append((test, self._exc_info_to_string(err, test)))
    116         self._mirrorOutput = True
    117 
    118     def addSuccess(self, test):
    119         "Called when a test has completed successfully"
    120         pass
    121 
    122     def addSkip(self, test, reason):
    123         """Called when a test is skipped."""
    124         self.skipped.append((test, reason))
    125 
    126     def addExpectedFailure(self, test, err, bugnumber):
    127         """Called when an expected failure/error occured."""
    128         self.expectedFailures.append(
    129             (test, self._exc_info_to_string(err, test)))
    130 
    131     @failfast
    132     def addUnexpectedSuccess(self, test, bugnumber):
    133         """Called when a test was expected to fail, but succeed."""
    134         self.unexpectedSuccesses.append(test)
    135 
    136     def wasSuccessful(self):
    137         "Tells whether or not this result was a success"
    138         return (len(self.failures) + len(self.errors) == 0)
    139 
    140     def stop(self):
    141         "Indicates that the tests should be aborted"
    142         self.shouldStop = True
    143 
    144     def _exc_info_to_string(self, err, test):
    145         """Converts a sys.exc_info()-style tuple of values into a string."""
    146         exctype, value, tb = err
    147         # Skip test runner traceback levels
    148         while tb and self._is_relevant_tb_level(tb):
    149             tb = tb.tb_next
    150         if exctype is test.failureException:
    151             # Skip assert*() traceback levels
    152             length = self._count_relevant_tb_levels(tb)
    153             msgLines = traceback.format_exception(exctype, value, tb, length)
    154         else:
    155             msgLines = traceback.format_exception(exctype, value, tb)
    156         
    157         if self.buffer:
    158             output = sys.stdout.getvalue()
    159             error = sys.stderr.getvalue()            
    160             if output:
    161                 if not output.endswith('\n'):
    162                     output += '\n'
    163                 msgLines.append(STDOUT_LINE % output)
    164             if error:
    165                 if not error.endswith('\n'):
    166                     error += '\n'
    167                 msgLines.append(STDERR_LINE % error)
    168         return ''.join(msgLines)
    169 
    170     def _is_relevant_tb_level(self, tb):
    171         return '__unittest' in tb.tb_frame.f_globals
    172 
    173     def _count_relevant_tb_levels(self, tb):
    174         length = 0
    175         while tb and not self._is_relevant_tb_level(tb):
    176             length += 1
    177             tb = tb.tb_next
    178         return length
    179 
    180     def __repr__(self):
    181         return "<%s run=%i errors=%i failures=%i>" % \
    182                (util.strclass(self.__class__), self.testsRun, len(self.errors),
    183                 len(self.failures))
    184