diff options
-rw-r--r-- | mesonbuild/mtest.py | 81 |
1 files changed, 48 insertions, 33 deletions
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 32b5e26..3214d82 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -883,8 +883,8 @@ class TestRun: self.returncode = 0 self.starttime = None # type: T.Optional[float] self.duration = None # type: T.Optional[float] - self.stdo = None # type: T.Optional[str] - self.stde = None # type: T.Optional[str] + self.stdo = '' + self.stde = '' self.cmd = None # type: T.Optional[T.List[str]] self.env = test_env # type: T.Dict[str, str] self.should_fail = test.should_fail @@ -930,17 +930,18 @@ class TestRun: return self.get_exit_status() return self.get_results() - def _complete(self, returncode: int, res: TestResult, - stdo: T.Optional[str], stde: T.Optional[str]) -> None: + def _complete(self, returncode: int, res: TestResult) -> None: assert isinstance(res, TestResult) if self.should_fail and res in (TestResult.OK, TestResult.FAIL): res = TestResult.UNEXPECTEDPASS if res.is_ok() else TestResult.EXPECTEDFAIL + if self.stdo and not self.stdo.endswith('\n'): + self.stdo += '\n' + if self.stde and not self.stde.endswith('\n'): + self.stde += '\n' self.res = res self.returncode = returncode self.duration = time.time() - self.starttime - self.stdo = stdo - self.stde = stde @property def cmdline(self) -> T.Optional[str]: @@ -950,13 +951,12 @@ class TestRun: return env_tuple_to_str(test_only_env) + \ ' '.join(sh_quote(x) for x in self.cmd) - def complete_skip(self, message: str) -> None: + def complete_skip(self) -> None: self.starttime = time.time() - self._complete(GNU_SKIP_RETURNCODE, TestResult.SKIP, message, None) + self._complete(GNU_SKIP_RETURNCODE, TestResult.SKIP) - def complete(self, returncode: int, res: TestResult, - stdo: T.Optional[str], stde: T.Optional[str]) -> None: - self._complete(returncode, res, stdo, stde) + def complete(self, returncode: int, res: TestResult) -> None: + self._complete(returncode, res) def get_log(self, colorize: bool = False, stderr_only: bool = False) -> str: stdo = '' if stderr_only else self.stdo @@ -987,8 +987,7 @@ class TestRun: class TestRunExitCode(TestRun): - def complete(self, returncode: int, res: TestResult, - stdo: T.Optional[str], stde: T.Optional[str]) -> None: + def complete(self, returncode: int, res: TestResult) -> None: if res: pass elif returncode == GNU_SKIP_RETURNCODE: @@ -997,14 +996,13 @@ class TestRunExitCode(TestRun): res = TestResult.ERROR else: res = TestResult.FAIL if bool(returncode) else TestResult.OK - super().complete(returncode, res, stdo, stde) + super().complete(returncode, res) TestRun.PROTOCOL_TO_CLASS[TestProtocol.EXITCODE] = TestRunExitCode class TestRunGTest(TestRunExitCode): - def complete(self, returncode: int, res: TestResult, - stdo: T.Optional[str], stde: T.Optional[str]) -> None: + def complete(self, returncode: int, res: TestResult) -> None: filename = f'{self.test.name}.xml' if self.test.workdir: filename = os.path.join(self.test.workdir, filename) @@ -1017,7 +1015,7 @@ class TestRunGTest(TestRunExitCode): # will handle the failure, don't generate a stacktrace. pass - super().complete(returncode, res, stdo, stde) + super().complete(returncode, res) TestRun.PROTOCOL_TO_CLASS[TestProtocol.GTEST] = TestRunGTest @@ -1027,14 +1025,13 @@ class TestRunTAP(TestRun): def needs_parsing(self) -> bool: return True - def complete(self, returncode: int, res: TestResult, - stdo: str, stde: str) -> None: + def complete(self, returncode: int, res: TestResult) -> None: if returncode != 0 and not res.was_killed(): res = TestResult.ERROR - stde = stde or '' - stde += f'\n(test program exited with status code {returncode})' + self.stde = self.stde or '' + self.stde += f'\n(test program exited with status code {returncode})' - super().complete(returncode, res, stdo, stde) + super().complete(returncode, res) async def parse(self, harness: 'TestHarness', lines: T.AsyncIterator[str]) -> T.Tuple[TestResult, str]: res = TestResult.OK @@ -1230,16 +1227,28 @@ class TestSubprocess: self.stdo_task = asyncio.ensure_future(decode_coro) return queue_iter(q) - def communicate(self, console_mode: ConsoleUser) -> T.Tuple[T.Optional[T.Awaitable[str]], - T.Optional[T.Awaitable[str]]]: + def communicate(self, + test: 'TestRun', + console_mode: ConsoleUser) -> T.Tuple[T.Optional[T.Awaitable[str]], + T.Optional[T.Awaitable[str]]]: + async def collect_stdo(test: 'TestRun', + reader: asyncio.StreamReader, + console_mode: ConsoleUser) -> None: + test.stdo = await read_decode(reader, console_mode) + + async def collect_stde(test: 'TestRun', + reader: asyncio.StreamReader, + console_mode: ConsoleUser) -> None: + test.stde = await read_decode(reader, console_mode) + # asyncio.ensure_future ensures that printing can # run in the background, even before it is awaited if self.stdo_task is None and self.stdout is not None: - decode_coro = read_decode(self._process.stdout, console_mode) + decode_coro = collect_stdo(test, self._process.stdout, console_mode) self.stdo_task = asyncio.ensure_future(decode_coro) self.all_futures.append(self.stdo_task) if self.stderr is not None and self.stderr != asyncio.subprocess.STDOUT: - decode_coro = read_decode(self._process.stderr, console_mode) + decode_coro = collect_stde(test, self._process.stderr, console_mode) self.stde_task = asyncio.ensure_future(decode_coro) self.all_futures.append(self.stde_task) @@ -1402,9 +1411,9 @@ class SingleTestRunner: async def run(self, harness: 'TestHarness') -> TestRun: if self.cmd is None: - skip_stdout = 'Not run because can not execute cross compiled binaries.' + self.stdo = 'Not run because can not execute cross compiled binaries.' harness.log_start_test(self.runobj) - self.runobj.complete_skip(skip_stdout) + self.runobj.complete_skip() else: cmd = self.cmd + self.test.cmd_args + self.options.test_args self.runobj.start(cmd) @@ -1473,8 +1482,11 @@ class SingleTestRunner: if self.runobj.needs_parsing: parse_coro = self.runobj.parse(harness, p.stdout_lines()) parse_task = asyncio.ensure_future(parse_coro) + stdo_task = stde_task = None + else: + stdo_task, stde_task = p.communicate(self.runobj, self.console_mode) + parse_task = None - stdo_task, stde_task = p.communicate(self.console_mode) returncode, result, additional_error = await p.wait(self.runobj.timeout) if parse_task is not None: @@ -1483,10 +1495,13 @@ class SingleTestRunner: additional_error = join_lines(additional_error, error) result = result or res - stdo = await stdo_task if stdo_task else '' - stde = await stde_task if stde_task else '' - stde = join_lines(stde, additional_error) - self.runobj.complete(returncode, result, stdo, stde) + if stdo_task: + await stdo_task + if stde_task: + await stde_task + + self.runobj.stde = join_lines(self.runobj.stde, additional_error) + self.runobj.complete(returncode, result) class TestHarness: |