diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 34e49439a..c754309c9 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -353,12 +353,12 @@ class JunitBuilder: 'testsuites', tests='0', errors='0', failures='0') self.suites = {} # type: T.Dict[str, et.Element] - def log(self, name: str, test: 'TestRun') -> None: + def log(self, test: 'TestRun') -> None: """Log a single test case.""" if test.junit is not None: for suite in test.junit.findall('.//testsuite'): # Assume that we don't need to merge anything here... - suite.attrib['name'] = '{}.{}.{}'.format(test.project, name, suite.attrib['name']) + suite.attrib['name'] = '{}.{}.{}'.format(test.project, test.name, suite.attrib['name']) # GTest can inject invalid attributes for case in suite.findall('.//testcase[@result]'): @@ -372,7 +372,7 @@ class JunitBuilder: # We want to record this so that each result is recorded # separately if test.results: - suitename = '{}.{}'.format(test.project, name) + suitename = '{}.{}'.format(test.project, test.name) assert suitename not in self.suites, 'duplicate suite' suite = self.suites[suitename] = et.Element( @@ -420,7 +420,7 @@ class JunitBuilder: suite = self.suites[test.project] suite.attrib['tests'] = str(int(suite.attrib['tests']) + 1) - testcase = et.SubElement(suite, 'testcase', name=name, classname=name) + testcase = et.SubElement(suite, 'testcase', name=test.name, classname=test.name) if test.res is TestResult.SKIP: et.SubElement(testcase, 'skipped') suite.attrib['skipped'] = str(int(suite.attrib['skipped']) + 1) @@ -451,10 +451,14 @@ class JunitBuilder: class TestRun: + TEST_NUM = 0 - def __init__(self, test: TestSerialisation, test_env: T.Dict[str, str]): + def __init__(self, test: TestSerialisation, test_env: T.Dict[str, str], + name: str): self.res = TestResult.PENDING self.test = test + self._num = None # type: T.Optional[int] + self.name = name self.results = list() # type: T.List[TestResult] self.returncode = 0 self.starttime = None # type: T.Optional[float] @@ -529,6 +533,13 @@ class TestRun: self.complete(res, results, returncode, stdo, stde, cmd) + @property + def num(self) -> int: + if self._num is None: + TestRun.TEST_NUM += 1 + self._num = TestRun.TEST_NUM + return self._num + def complete(self, res: TestResult, results: T.List[TestResult], returncode: int, stdo: T.Optional[str], stde: T.Optional[str], @@ -574,8 +585,8 @@ def decode(stream: T.Union[None, bytes]) -> str: except UnicodeDecodeError: return stream.decode('iso-8859-1', errors='ignore') -def write_json_log(jsonlogfile: T.TextIO, test_name: str, result: TestRun) -> None: - jresult = {'name': test_name, +def write_json_log(jsonlogfile: T.TextIO, result: TestRun) -> None: + jresult = {'name': result.name, 'stdout': result.stdo, 'result': result.res.value, 'starttime': result.starttime, @@ -647,12 +658,13 @@ async def complete_all(futures: T.Iterable[asyncio.Future]) -> None: class SingleTestRunner: def __init__(self, test: TestSerialisation, test_env: T.Dict[str, str], - env: T.Dict[str, str], options: argparse.Namespace): + env: T.Dict[str, str], name: str, + options: argparse.Namespace): self.test = test self.test_env = test_env self.env = env self.options = options - self.runobj = TestRun(test, test_env) + self.runobj = TestRun(test, test_env, name) def _get_cmd(self) -> T.Optional[T.List[str]]: if self.test.fname[0].endswith('.jar'): @@ -915,7 +927,7 @@ class TestHarness: options.wrapper = current.exe_wrapper return current.env.get_env(os.environ.copy()) - def get_test_runner(self, test: TestSerialisation) -> SingleTestRunner: + def get_test_runner(self, test: TestSerialisation, name: str) -> SingleTestRunner: options = deepcopy(self.options) if not options.setup: options.setup = self.build_data.test_setup_default_name @@ -928,7 +940,7 @@ class TestHarness: if (test.is_cross_built and test.needs_exe_wrapper and test.exe_runner and test.exe_runner.found()): env['MESON_EXE_WRAPPER'] = join_args(test.exe_runner.get_command()) - return SingleTestRunner(test, test_env, env, options) + return SingleTestRunner(test, test_env, env, name, options) def process_test_result(self, result: TestRun) -> None: if result.res is TestResult.TIMEOUT: @@ -947,17 +959,16 @@ class TestHarness: sys.exit('Unknown test result encountered: {}'.format(result.res)) def print_stats(self, test_count: int, name_max_len: int, - tests: T.List[TestSerialisation], - name: str, result: TestRun, i: int) -> None: + result: TestRun) -> None: ok_statuses = (TestResult.OK, TestResult.EXPECTEDFAIL) bad_statuses = (TestResult.FAIL, TestResult.TIMEOUT, TestResult.INTERRUPT, TestResult.UNEXPECTEDPASS, TestResult.ERROR) result_str = '{num:{numlen}}/{testcount} {name:{name_max_len}} {res:{reslen}} {dur:.2f}s'.format( numlen=len(str(test_count)), - num=i, + num=result.num, testcount=test_count, name_max_len=name_max_len, - name=name, + name=result.name, reslen=TestResult.maxlen(), res=result.res.value, dur=result.duration) @@ -979,9 +990,9 @@ class TestHarness: if self.logfile: self.logfile.write(result_str) if self.jsonlogfile: - write_json_log(self.jsonlogfile, name, result) + write_json_log(self.jsonlogfile, result) if self.junit: - self.junit.log(name, result) + self.junit.log(result) def print_summary(self) -> None: # Prepend a list of failures @@ -1201,14 +1212,13 @@ class TestHarness: self.build_data = build.load(os.getcwd()) interrupted = False - async def run_test(test: SingleTestRunner, - name: str, index: int) -> None: + async def run_test(test: SingleTestRunner) -> None: async with semaphore: if interrupted or (self.options.repeat > 1 and self.fail_count): return res = await test.run() self.process_test_result(res) - self.print_stats(test_count, name_max_len, tests, name, res, index) + self.print_stats(test_count, name_max_len, res) def test_done(f: asyncio.Future) -> None: if not f.cancelled(): @@ -1253,13 +1263,13 @@ class TestHarness: asyncio.get_event_loop().add_signal_handler(signal.SIGTERM, sigterm_handler) try: for _ in range(self.options.repeat): - for i, test in enumerate(tests, 1): + for test in tests: visible_name = self.get_pretty_suite(test) - single_test = self.get_test_runner(test) + single_test = self.get_test_runner(test, visible_name) if not test.is_parallel or single_test.options.gdb: await complete_all(futures) - future = asyncio.ensure_future(run_test(single_test, visible_name, i)) + future = asyncio.ensure_future(run_test(single_test)) futures.append(future) running_tests[future] = visible_name future.add_done_callback(test_done)