17db96d56Sopenharmony_ci'''Test runner and result class for the regression test suite.
27db96d56Sopenharmony_ci
37db96d56Sopenharmony_ci'''
47db96d56Sopenharmony_ci
57db96d56Sopenharmony_ciimport functools
67db96d56Sopenharmony_ciimport io
77db96d56Sopenharmony_ciimport sys
87db96d56Sopenharmony_ciimport time
97db96d56Sopenharmony_ciimport traceback
107db96d56Sopenharmony_ciimport unittest
117db96d56Sopenharmony_ci
127db96d56Sopenharmony_ciclass RegressionTestResult(unittest.TextTestResult):
137db96d56Sopenharmony_ci    USE_XML = False
147db96d56Sopenharmony_ci
157db96d56Sopenharmony_ci    def __init__(self, stream, descriptions, verbosity):
167db96d56Sopenharmony_ci        super().__init__(stream=stream, descriptions=descriptions,
177db96d56Sopenharmony_ci                         verbosity=2 if verbosity else 0)
187db96d56Sopenharmony_ci        self.buffer = True
197db96d56Sopenharmony_ci        if self.USE_XML:
207db96d56Sopenharmony_ci            from xml.etree import ElementTree as ET
217db96d56Sopenharmony_ci            from datetime import datetime
227db96d56Sopenharmony_ci            self.__ET = ET
237db96d56Sopenharmony_ci            self.__suite = ET.Element('testsuite')
247db96d56Sopenharmony_ci            self.__suite.set('start', datetime.utcnow().isoformat(' '))
257db96d56Sopenharmony_ci            self.__e = None
267db96d56Sopenharmony_ci        self.__start_time = None
277db96d56Sopenharmony_ci
287db96d56Sopenharmony_ci    @classmethod
297db96d56Sopenharmony_ci    def __getId(cls, test):
307db96d56Sopenharmony_ci        try:
317db96d56Sopenharmony_ci            test_id = test.id
327db96d56Sopenharmony_ci        except AttributeError:
337db96d56Sopenharmony_ci            return str(test)
347db96d56Sopenharmony_ci        try:
357db96d56Sopenharmony_ci            return test_id()
367db96d56Sopenharmony_ci        except TypeError:
377db96d56Sopenharmony_ci            return str(test_id)
387db96d56Sopenharmony_ci        return repr(test)
397db96d56Sopenharmony_ci
407db96d56Sopenharmony_ci    def startTest(self, test):
417db96d56Sopenharmony_ci        super().startTest(test)
427db96d56Sopenharmony_ci        if self.USE_XML:
437db96d56Sopenharmony_ci            self.__e = e = self.__ET.SubElement(self.__suite, 'testcase')
447db96d56Sopenharmony_ci        self.__start_time = time.perf_counter()
457db96d56Sopenharmony_ci
467db96d56Sopenharmony_ci    def _add_result(self, test, capture=False, **args):
477db96d56Sopenharmony_ci        if not self.USE_XML:
487db96d56Sopenharmony_ci            return
497db96d56Sopenharmony_ci        e = self.__e
507db96d56Sopenharmony_ci        self.__e = None
517db96d56Sopenharmony_ci        if e is None:
527db96d56Sopenharmony_ci            return
537db96d56Sopenharmony_ci        ET = self.__ET
547db96d56Sopenharmony_ci
557db96d56Sopenharmony_ci        e.set('name', args.pop('name', self.__getId(test)))
567db96d56Sopenharmony_ci        e.set('status', args.pop('status', 'run'))
577db96d56Sopenharmony_ci        e.set('result', args.pop('result', 'completed'))
587db96d56Sopenharmony_ci        if self.__start_time:
597db96d56Sopenharmony_ci            e.set('time', f'{time.perf_counter() - self.__start_time:0.6f}')
607db96d56Sopenharmony_ci
617db96d56Sopenharmony_ci        if capture:
627db96d56Sopenharmony_ci            if self._stdout_buffer is not None:
637db96d56Sopenharmony_ci                stdout = self._stdout_buffer.getvalue().rstrip()
647db96d56Sopenharmony_ci                ET.SubElement(e, 'system-out').text = stdout
657db96d56Sopenharmony_ci            if self._stderr_buffer is not None:
667db96d56Sopenharmony_ci                stderr = self._stderr_buffer.getvalue().rstrip()
677db96d56Sopenharmony_ci                ET.SubElement(e, 'system-err').text = stderr
687db96d56Sopenharmony_ci
697db96d56Sopenharmony_ci        for k, v in args.items():
707db96d56Sopenharmony_ci            if not k or not v:
717db96d56Sopenharmony_ci                continue
727db96d56Sopenharmony_ci            e2 = ET.SubElement(e, k)
737db96d56Sopenharmony_ci            if hasattr(v, 'items'):
747db96d56Sopenharmony_ci                for k2, v2 in v.items():
757db96d56Sopenharmony_ci                    if k2:
767db96d56Sopenharmony_ci                        e2.set(k2, str(v2))
777db96d56Sopenharmony_ci                    else:
787db96d56Sopenharmony_ci                        e2.text = str(v2)
797db96d56Sopenharmony_ci            else:
807db96d56Sopenharmony_ci                e2.text = str(v)
817db96d56Sopenharmony_ci
827db96d56Sopenharmony_ci    @classmethod
837db96d56Sopenharmony_ci    def __makeErrorDict(cls, err_type, err_value, err_tb):
847db96d56Sopenharmony_ci        if isinstance(err_type, type):
857db96d56Sopenharmony_ci            if err_type.__module__ == 'builtins':
867db96d56Sopenharmony_ci                typename = err_type.__name__
877db96d56Sopenharmony_ci            else:
887db96d56Sopenharmony_ci                typename = f'{err_type.__module__}.{err_type.__name__}'
897db96d56Sopenharmony_ci        else:
907db96d56Sopenharmony_ci            typename = repr(err_type)
917db96d56Sopenharmony_ci
927db96d56Sopenharmony_ci        msg = traceback.format_exception(err_type, err_value, None)
937db96d56Sopenharmony_ci        tb = traceback.format_exception(err_type, err_value, err_tb)
947db96d56Sopenharmony_ci
957db96d56Sopenharmony_ci        return {
967db96d56Sopenharmony_ci            'type': typename,
977db96d56Sopenharmony_ci            'message': ''.join(msg),
987db96d56Sopenharmony_ci            '': ''.join(tb),
997db96d56Sopenharmony_ci        }
1007db96d56Sopenharmony_ci
1017db96d56Sopenharmony_ci    def addError(self, test, err):
1027db96d56Sopenharmony_ci        self._add_result(test, True, error=self.__makeErrorDict(*err))
1037db96d56Sopenharmony_ci        super().addError(test, err)
1047db96d56Sopenharmony_ci
1057db96d56Sopenharmony_ci    def addExpectedFailure(self, test, err):
1067db96d56Sopenharmony_ci        self._add_result(test, True, output=self.__makeErrorDict(*err))
1077db96d56Sopenharmony_ci        super().addExpectedFailure(test, err)
1087db96d56Sopenharmony_ci
1097db96d56Sopenharmony_ci    def addFailure(self, test, err):
1107db96d56Sopenharmony_ci        self._add_result(test, True, failure=self.__makeErrorDict(*err))
1117db96d56Sopenharmony_ci        super().addFailure(test, err)
1127db96d56Sopenharmony_ci
1137db96d56Sopenharmony_ci    def addSkip(self, test, reason):
1147db96d56Sopenharmony_ci        self._add_result(test, skipped=reason)
1157db96d56Sopenharmony_ci        super().addSkip(test, reason)
1167db96d56Sopenharmony_ci
1177db96d56Sopenharmony_ci    def addSuccess(self, test):
1187db96d56Sopenharmony_ci        self._add_result(test)
1197db96d56Sopenharmony_ci        super().addSuccess(test)
1207db96d56Sopenharmony_ci
1217db96d56Sopenharmony_ci    def addUnexpectedSuccess(self, test):
1227db96d56Sopenharmony_ci        self._add_result(test, outcome='UNEXPECTED_SUCCESS')
1237db96d56Sopenharmony_ci        super().addUnexpectedSuccess(test)
1247db96d56Sopenharmony_ci
1257db96d56Sopenharmony_ci    def get_xml_element(self):
1267db96d56Sopenharmony_ci        if not self.USE_XML:
1277db96d56Sopenharmony_ci            raise ValueError("USE_XML is false")
1287db96d56Sopenharmony_ci        e = self.__suite
1297db96d56Sopenharmony_ci        e.set('tests', str(self.testsRun))
1307db96d56Sopenharmony_ci        e.set('errors', str(len(self.errors)))
1317db96d56Sopenharmony_ci        e.set('failures', str(len(self.failures)))
1327db96d56Sopenharmony_ci        return e
1337db96d56Sopenharmony_ci
1347db96d56Sopenharmony_ciclass QuietRegressionTestRunner:
1357db96d56Sopenharmony_ci    def __init__(self, stream, buffer=False):
1367db96d56Sopenharmony_ci        self.result = RegressionTestResult(stream, None, 0)
1377db96d56Sopenharmony_ci        self.result.buffer = buffer
1387db96d56Sopenharmony_ci
1397db96d56Sopenharmony_ci    def run(self, test):
1407db96d56Sopenharmony_ci        test(self.result)
1417db96d56Sopenharmony_ci        return self.result
1427db96d56Sopenharmony_ci
1437db96d56Sopenharmony_cidef get_test_runner_class(verbosity, buffer=False):
1447db96d56Sopenharmony_ci    if verbosity:
1457db96d56Sopenharmony_ci        return functools.partial(unittest.TextTestRunner,
1467db96d56Sopenharmony_ci                                 resultclass=RegressionTestResult,
1477db96d56Sopenharmony_ci                                 buffer=buffer,
1487db96d56Sopenharmony_ci                                 verbosity=verbosity)
1497db96d56Sopenharmony_ci    return functools.partial(QuietRegressionTestRunner, buffer=buffer)
1507db96d56Sopenharmony_ci
1517db96d56Sopenharmony_cidef get_test_runner(stream, verbosity, capture_output=False):
1527db96d56Sopenharmony_ci    return get_test_runner_class(verbosity, capture_output)(stream)
1537db96d56Sopenharmony_ci
1547db96d56Sopenharmony_ciif __name__ == '__main__':
1557db96d56Sopenharmony_ci    import xml.etree.ElementTree as ET
1567db96d56Sopenharmony_ci    RegressionTestResult.USE_XML = True
1577db96d56Sopenharmony_ci
1587db96d56Sopenharmony_ci    class TestTests(unittest.TestCase):
1597db96d56Sopenharmony_ci        def test_pass(self):
1607db96d56Sopenharmony_ci            pass
1617db96d56Sopenharmony_ci
1627db96d56Sopenharmony_ci        def test_pass_slow(self):
1637db96d56Sopenharmony_ci            time.sleep(1.0)
1647db96d56Sopenharmony_ci
1657db96d56Sopenharmony_ci        def test_fail(self):
1667db96d56Sopenharmony_ci            print('stdout', file=sys.stdout)
1677db96d56Sopenharmony_ci            print('stderr', file=sys.stderr)
1687db96d56Sopenharmony_ci            self.fail('failure message')
1697db96d56Sopenharmony_ci
1707db96d56Sopenharmony_ci        def test_error(self):
1717db96d56Sopenharmony_ci            print('stdout', file=sys.stdout)
1727db96d56Sopenharmony_ci            print('stderr', file=sys.stderr)
1737db96d56Sopenharmony_ci            raise RuntimeError('error message')
1747db96d56Sopenharmony_ci
1757db96d56Sopenharmony_ci    suite = unittest.TestSuite()
1767db96d56Sopenharmony_ci    suite.addTest(unittest.TestLoader().loadTestsFromTestCase(TestTests))
1777db96d56Sopenharmony_ci    stream = io.StringIO()
1787db96d56Sopenharmony_ci    runner_cls = get_test_runner_class(sum(a == '-v' for a in sys.argv))
1797db96d56Sopenharmony_ci    runner = runner_cls(sys.stdout)
1807db96d56Sopenharmony_ci    result = runner.run(suite)
1817db96d56Sopenharmony_ci    print('Output:', stream.getvalue())
1827db96d56Sopenharmony_ci    print('XML: ', end='')
1837db96d56Sopenharmony_ci    for s in ET.tostringlist(result.get_xml_element()):
1847db96d56Sopenharmony_ci        print(s.decode(), end='')
1857db96d56Sopenharmony_ci    print()
186