17db96d56Sopenharmony_ci"""Test result object""" 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ciimport io 47db96d56Sopenharmony_ciimport sys 57db96d56Sopenharmony_ciimport traceback 67db96d56Sopenharmony_ci 77db96d56Sopenharmony_cifrom . import util 87db96d56Sopenharmony_cifrom functools import wraps 97db96d56Sopenharmony_ci 107db96d56Sopenharmony_ci__unittest = True 117db96d56Sopenharmony_ci 127db96d56Sopenharmony_cidef failfast(method): 137db96d56Sopenharmony_ci @wraps(method) 147db96d56Sopenharmony_ci def inner(self, *args, **kw): 157db96d56Sopenharmony_ci if getattr(self, 'failfast', False): 167db96d56Sopenharmony_ci self.stop() 177db96d56Sopenharmony_ci return method(self, *args, **kw) 187db96d56Sopenharmony_ci return inner 197db96d56Sopenharmony_ci 207db96d56Sopenharmony_ciSTDOUT_LINE = '\nStdout:\n%s' 217db96d56Sopenharmony_ciSTDERR_LINE = '\nStderr:\n%s' 227db96d56Sopenharmony_ci 237db96d56Sopenharmony_ci 247db96d56Sopenharmony_ciclass TestResult(object): 257db96d56Sopenharmony_ci """Holder for test result information. 267db96d56Sopenharmony_ci 277db96d56Sopenharmony_ci Test results are automatically managed by the TestCase and TestSuite 287db96d56Sopenharmony_ci classes, and do not need to be explicitly manipulated by writers of tests. 297db96d56Sopenharmony_ci 307db96d56Sopenharmony_ci Each instance holds the total number of tests run, and collections of 317db96d56Sopenharmony_ci failures and errors that occurred among those test runs. The collections 327db96d56Sopenharmony_ci contain tuples of (testcase, exceptioninfo), where exceptioninfo is the 337db96d56Sopenharmony_ci formatted traceback of the error that occurred. 347db96d56Sopenharmony_ci """ 357db96d56Sopenharmony_ci _previousTestClass = None 367db96d56Sopenharmony_ci _testRunEntered = False 377db96d56Sopenharmony_ci _moduleSetUpFailed = False 387db96d56Sopenharmony_ci def __init__(self, stream=None, descriptions=None, verbosity=None): 397db96d56Sopenharmony_ci self.failfast = False 407db96d56Sopenharmony_ci self.failures = [] 417db96d56Sopenharmony_ci self.errors = [] 427db96d56Sopenharmony_ci self.testsRun = 0 437db96d56Sopenharmony_ci self.skipped = [] 447db96d56Sopenharmony_ci self.expectedFailures = [] 457db96d56Sopenharmony_ci self.unexpectedSuccesses = [] 467db96d56Sopenharmony_ci self.shouldStop = False 477db96d56Sopenharmony_ci self.buffer = False 487db96d56Sopenharmony_ci self.tb_locals = False 497db96d56Sopenharmony_ci self._stdout_buffer = None 507db96d56Sopenharmony_ci self._stderr_buffer = None 517db96d56Sopenharmony_ci self._original_stdout = sys.stdout 527db96d56Sopenharmony_ci self._original_stderr = sys.stderr 537db96d56Sopenharmony_ci self._mirrorOutput = False 547db96d56Sopenharmony_ci 557db96d56Sopenharmony_ci def printErrors(self): 567db96d56Sopenharmony_ci "Called by TestRunner after test run" 577db96d56Sopenharmony_ci 587db96d56Sopenharmony_ci def startTest(self, test): 597db96d56Sopenharmony_ci "Called when the given test is about to be run" 607db96d56Sopenharmony_ci self.testsRun += 1 617db96d56Sopenharmony_ci self._mirrorOutput = False 627db96d56Sopenharmony_ci self._setupStdout() 637db96d56Sopenharmony_ci 647db96d56Sopenharmony_ci def _setupStdout(self): 657db96d56Sopenharmony_ci if self.buffer: 667db96d56Sopenharmony_ci if self._stderr_buffer is None: 677db96d56Sopenharmony_ci self._stderr_buffer = io.StringIO() 687db96d56Sopenharmony_ci self._stdout_buffer = io.StringIO() 697db96d56Sopenharmony_ci sys.stdout = self._stdout_buffer 707db96d56Sopenharmony_ci sys.stderr = self._stderr_buffer 717db96d56Sopenharmony_ci 727db96d56Sopenharmony_ci def startTestRun(self): 737db96d56Sopenharmony_ci """Called once before any tests are executed. 747db96d56Sopenharmony_ci 757db96d56Sopenharmony_ci See startTest for a method called before each test. 767db96d56Sopenharmony_ci """ 777db96d56Sopenharmony_ci 787db96d56Sopenharmony_ci def stopTest(self, test): 797db96d56Sopenharmony_ci """Called when the given test has been run""" 807db96d56Sopenharmony_ci self._restoreStdout() 817db96d56Sopenharmony_ci self._mirrorOutput = False 827db96d56Sopenharmony_ci 837db96d56Sopenharmony_ci def _restoreStdout(self): 847db96d56Sopenharmony_ci if self.buffer: 857db96d56Sopenharmony_ci if self._mirrorOutput: 867db96d56Sopenharmony_ci output = sys.stdout.getvalue() 877db96d56Sopenharmony_ci error = sys.stderr.getvalue() 887db96d56Sopenharmony_ci if output: 897db96d56Sopenharmony_ci if not output.endswith('\n'): 907db96d56Sopenharmony_ci output += '\n' 917db96d56Sopenharmony_ci self._original_stdout.write(STDOUT_LINE % output) 927db96d56Sopenharmony_ci if error: 937db96d56Sopenharmony_ci if not error.endswith('\n'): 947db96d56Sopenharmony_ci error += '\n' 957db96d56Sopenharmony_ci self._original_stderr.write(STDERR_LINE % error) 967db96d56Sopenharmony_ci 977db96d56Sopenharmony_ci sys.stdout = self._original_stdout 987db96d56Sopenharmony_ci sys.stderr = self._original_stderr 997db96d56Sopenharmony_ci self._stdout_buffer.seek(0) 1007db96d56Sopenharmony_ci self._stdout_buffer.truncate() 1017db96d56Sopenharmony_ci self._stderr_buffer.seek(0) 1027db96d56Sopenharmony_ci self._stderr_buffer.truncate() 1037db96d56Sopenharmony_ci 1047db96d56Sopenharmony_ci def stopTestRun(self): 1057db96d56Sopenharmony_ci """Called once after all tests are executed. 1067db96d56Sopenharmony_ci 1077db96d56Sopenharmony_ci See stopTest for a method called after each test. 1087db96d56Sopenharmony_ci """ 1097db96d56Sopenharmony_ci 1107db96d56Sopenharmony_ci @failfast 1117db96d56Sopenharmony_ci def addError(self, test, err): 1127db96d56Sopenharmony_ci """Called when an error has occurred. 'err' is a tuple of values as 1137db96d56Sopenharmony_ci returned by sys.exc_info(). 1147db96d56Sopenharmony_ci """ 1157db96d56Sopenharmony_ci self.errors.append((test, self._exc_info_to_string(err, test))) 1167db96d56Sopenharmony_ci self._mirrorOutput = True 1177db96d56Sopenharmony_ci 1187db96d56Sopenharmony_ci @failfast 1197db96d56Sopenharmony_ci def addFailure(self, test, err): 1207db96d56Sopenharmony_ci """Called when an error has occurred. 'err' is a tuple of values as 1217db96d56Sopenharmony_ci returned by sys.exc_info().""" 1227db96d56Sopenharmony_ci self.failures.append((test, self._exc_info_to_string(err, test))) 1237db96d56Sopenharmony_ci self._mirrorOutput = True 1247db96d56Sopenharmony_ci 1257db96d56Sopenharmony_ci def addSubTest(self, test, subtest, err): 1267db96d56Sopenharmony_ci """Called at the end of a subtest. 1277db96d56Sopenharmony_ci 'err' is None if the subtest ended successfully, otherwise it's a 1287db96d56Sopenharmony_ci tuple of values as returned by sys.exc_info(). 1297db96d56Sopenharmony_ci """ 1307db96d56Sopenharmony_ci # By default, we don't do anything with successful subtests, but 1317db96d56Sopenharmony_ci # more sophisticated test results might want to record them. 1327db96d56Sopenharmony_ci if err is not None: 1337db96d56Sopenharmony_ci if getattr(self, 'failfast', False): 1347db96d56Sopenharmony_ci self.stop() 1357db96d56Sopenharmony_ci if issubclass(err[0], test.failureException): 1367db96d56Sopenharmony_ci errors = self.failures 1377db96d56Sopenharmony_ci else: 1387db96d56Sopenharmony_ci errors = self.errors 1397db96d56Sopenharmony_ci errors.append((subtest, self._exc_info_to_string(err, test))) 1407db96d56Sopenharmony_ci self._mirrorOutput = True 1417db96d56Sopenharmony_ci 1427db96d56Sopenharmony_ci def addSuccess(self, test): 1437db96d56Sopenharmony_ci "Called when a test has completed successfully" 1447db96d56Sopenharmony_ci pass 1457db96d56Sopenharmony_ci 1467db96d56Sopenharmony_ci def addSkip(self, test, reason): 1477db96d56Sopenharmony_ci """Called when a test is skipped.""" 1487db96d56Sopenharmony_ci self.skipped.append((test, reason)) 1497db96d56Sopenharmony_ci 1507db96d56Sopenharmony_ci def addExpectedFailure(self, test, err): 1517db96d56Sopenharmony_ci """Called when an expected failure/error occurred.""" 1527db96d56Sopenharmony_ci self.expectedFailures.append( 1537db96d56Sopenharmony_ci (test, self._exc_info_to_string(err, test))) 1547db96d56Sopenharmony_ci 1557db96d56Sopenharmony_ci @failfast 1567db96d56Sopenharmony_ci def addUnexpectedSuccess(self, test): 1577db96d56Sopenharmony_ci """Called when a test was expected to fail, but succeed.""" 1587db96d56Sopenharmony_ci self.unexpectedSuccesses.append(test) 1597db96d56Sopenharmony_ci 1607db96d56Sopenharmony_ci def wasSuccessful(self): 1617db96d56Sopenharmony_ci """Tells whether or not this result was a success.""" 1627db96d56Sopenharmony_ci # The hasattr check is for test_result's OldResult test. That 1637db96d56Sopenharmony_ci # way this method works on objects that lack the attribute. 1647db96d56Sopenharmony_ci # (where would such result instances come from? old stored pickles?) 1657db96d56Sopenharmony_ci return ((len(self.failures) == len(self.errors) == 0) and 1667db96d56Sopenharmony_ci (not hasattr(self, 'unexpectedSuccesses') or 1677db96d56Sopenharmony_ci len(self.unexpectedSuccesses) == 0)) 1687db96d56Sopenharmony_ci 1697db96d56Sopenharmony_ci def stop(self): 1707db96d56Sopenharmony_ci """Indicates that the tests should be aborted.""" 1717db96d56Sopenharmony_ci self.shouldStop = True 1727db96d56Sopenharmony_ci 1737db96d56Sopenharmony_ci def _exc_info_to_string(self, err, test): 1747db96d56Sopenharmony_ci """Converts a sys.exc_info()-style tuple of values into a string.""" 1757db96d56Sopenharmony_ci exctype, value, tb = err 1767db96d56Sopenharmony_ci tb = self._clean_tracebacks(exctype, value, tb, test) 1777db96d56Sopenharmony_ci tb_e = traceback.TracebackException( 1787db96d56Sopenharmony_ci exctype, value, tb, 1797db96d56Sopenharmony_ci capture_locals=self.tb_locals, compact=True) 1807db96d56Sopenharmony_ci msgLines = list(tb_e.format()) 1817db96d56Sopenharmony_ci 1827db96d56Sopenharmony_ci if self.buffer: 1837db96d56Sopenharmony_ci output = sys.stdout.getvalue() 1847db96d56Sopenharmony_ci error = sys.stderr.getvalue() 1857db96d56Sopenharmony_ci if output: 1867db96d56Sopenharmony_ci if not output.endswith('\n'): 1877db96d56Sopenharmony_ci output += '\n' 1887db96d56Sopenharmony_ci msgLines.append(STDOUT_LINE % output) 1897db96d56Sopenharmony_ci if error: 1907db96d56Sopenharmony_ci if not error.endswith('\n'): 1917db96d56Sopenharmony_ci error += '\n' 1927db96d56Sopenharmony_ci msgLines.append(STDERR_LINE % error) 1937db96d56Sopenharmony_ci return ''.join(msgLines) 1947db96d56Sopenharmony_ci 1957db96d56Sopenharmony_ci def _clean_tracebacks(self, exctype, value, tb, test): 1967db96d56Sopenharmony_ci ret = None 1977db96d56Sopenharmony_ci first = True 1987db96d56Sopenharmony_ci excs = [(exctype, value, tb)] 1997db96d56Sopenharmony_ci seen = {id(value)} # Detect loops in chained exceptions. 2007db96d56Sopenharmony_ci while excs: 2017db96d56Sopenharmony_ci (exctype, value, tb) = excs.pop() 2027db96d56Sopenharmony_ci # Skip test runner traceback levels 2037db96d56Sopenharmony_ci while tb and self._is_relevant_tb_level(tb): 2047db96d56Sopenharmony_ci tb = tb.tb_next 2057db96d56Sopenharmony_ci 2067db96d56Sopenharmony_ci # Skip assert*() traceback levels 2077db96d56Sopenharmony_ci if exctype is test.failureException: 2087db96d56Sopenharmony_ci self._remove_unittest_tb_frames(tb) 2097db96d56Sopenharmony_ci 2107db96d56Sopenharmony_ci if first: 2117db96d56Sopenharmony_ci ret = tb 2127db96d56Sopenharmony_ci first = False 2137db96d56Sopenharmony_ci else: 2147db96d56Sopenharmony_ci value.__traceback__ = tb 2157db96d56Sopenharmony_ci 2167db96d56Sopenharmony_ci if value is not None: 2177db96d56Sopenharmony_ci for c in (value.__cause__, value.__context__): 2187db96d56Sopenharmony_ci if c is not None and id(c) not in seen: 2197db96d56Sopenharmony_ci excs.append((type(c), c, c.__traceback__)) 2207db96d56Sopenharmony_ci seen.add(id(c)) 2217db96d56Sopenharmony_ci return ret 2227db96d56Sopenharmony_ci 2237db96d56Sopenharmony_ci def _is_relevant_tb_level(self, tb): 2247db96d56Sopenharmony_ci return '__unittest' in tb.tb_frame.f_globals 2257db96d56Sopenharmony_ci 2267db96d56Sopenharmony_ci def _remove_unittest_tb_frames(self, tb): 2277db96d56Sopenharmony_ci '''Truncates usercode tb at the first unittest frame. 2287db96d56Sopenharmony_ci 2297db96d56Sopenharmony_ci If the first frame of the traceback is in user code, 2307db96d56Sopenharmony_ci the prefix up to the first unittest frame is returned. 2317db96d56Sopenharmony_ci If the first frame is already in the unittest module, 2327db96d56Sopenharmony_ci the traceback is not modified. 2337db96d56Sopenharmony_ci ''' 2347db96d56Sopenharmony_ci prev = None 2357db96d56Sopenharmony_ci while tb and not self._is_relevant_tb_level(tb): 2367db96d56Sopenharmony_ci prev = tb 2377db96d56Sopenharmony_ci tb = tb.tb_next 2387db96d56Sopenharmony_ci if prev is not None: 2397db96d56Sopenharmony_ci prev.tb_next = None 2407db96d56Sopenharmony_ci 2417db96d56Sopenharmony_ci def __repr__(self): 2427db96d56Sopenharmony_ci return ("<%s run=%i errors=%i failures=%i>" % 2437db96d56Sopenharmony_ci (util.strclass(self.__class__), self.testsRun, len(self.errors), 2447db96d56Sopenharmony_ci len(self.failures))) 245