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