From d89ec98b4763cda13da0ae22515c27f4dfe5c1b9 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 16 Oct 2020 12:37:30 -0700 Subject: mtest: Add support for rust unit tests Rust has it's own built in unit test format, which is invoked by compiling a rust executable with the `--test` flag to rustc. The tests are then run by simply invoking that binary. They output a custom test format, which this patch adds parsing support for. This means that we can report each subtest in the junit we generate correctly, which should be helpful for orchestration systems like gitlab and jenkins which can parse junit XML. --- mesonbuild/mtest.py | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'mesonbuild/mtest.py') diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index c31ad1a..9db271e 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -666,6 +666,28 @@ class JunitBuilder(TestLogger): tree.write(f, encoding='utf-8', xml_declaration=True) +def parse_rust_test(stdout: str) -> T.Dict[str, TestResult]: + """Parse the output of rust tests.""" + res = {} # type; T.Dict[str, TestResult] + + def parse_res(res: str) -> TestResult: + if res == 'ok': + return TestResult.OK + elif res == 'ignored': + return TestResult.SKIP + elif res == 'FAILED': + return TestResult.FAIL + raise MesonException('Unsupported output from rust test: {}'.format(res)) + + for line in stdout.splitlines(): + if line.startswith('test ') and not line.startswith('test result'): + _, name, _, result = line.split(' ') + name = name.replace('::', '.') + res[name] = parse_res(result) + + return res + + class TestRun: TEST_NUM = 0 @@ -749,6 +771,21 @@ class TestRun: self.complete(res, results, returncode, stdo, stde, cmd) + def complete_rust(self, returncode: int, stdo: str, stde: str, cmd: T.List[str]) -> None: + results = parse_rust_test(stdo) + + failed = TestResult.FAIL in results.values() + # Now determine the overall result of the test based on the outcome of the subcases + if all(t is TestResult.SKIP for t in results.values()): + # This includes the case where num_tests is zero + res = TestResult.SKIP + elif self.should_fail: + res = TestResult.EXPECTEDFAIL if failed else TestResult.UNEXPECTEDPASS + else: + res = TestResult.FAIL if failed else TestResult.OK + + self.complete(res, results, returncode, stdo, stde, cmd) + @property def num(self) -> int: if self._num is None: @@ -1069,6 +1106,8 @@ class SingleTestRunner: self.runobj.complete_exitcode(returncode, stdo, stde, cmd) elif self.test.protocol is TestProtocol.GTEST: self.runobj.complete_gtest(returncode, stdo, stde, cmd) + elif self.test.protocol is TestProtocol.RUST: + return self.runobj.complete_rust(returncode, stdo, stde, cmd) else: if self.options.verbose: print(stdo, end='') -- cgit v1.1