From 55cf399ff8b9c15300f26dd1a46045dda7d49f98 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Thu, 13 Aug 2020 21:35:31 +0530 Subject: mtest: Allow filtering tests by subproject You could always specify a list of tests to run by passing the names as arguments to `meson test`. If there were multiple tests with that name (in the same project or different subprojects), all of them would be run. Now you can: 1. Run all tests with the specified name from a specific subproject: `meson test subprojname:testname` 1. Run all tests defined in a specific subproject: `meson test subprojectname:` Also forbid ':' in test names. We already forbid this elsewhere, so should not be a big deal. --- .../snippets/mtest_test_list_subprojects.md | 18 ++++++++++++++ mesonbuild/interpreter.py | 13 +++++++--- mesonbuild/mtest.py | 29 +++++++++++++++++++--- 3 files changed, 53 insertions(+), 7 deletions(-) create mode 100644 docs/markdown/snippets/mtest_test_list_subprojects.md diff --git a/docs/markdown/snippets/mtest_test_list_subprojects.md b/docs/markdown/snippets/mtest_test_list_subprojects.md new file mode 100644 index 0000000..a8dbf4c --- /dev/null +++ b/docs/markdown/snippets/mtest_test_list_subprojects.md @@ -0,0 +1,18 @@ +## `meson test` can now filter tests by subproject + +You could always specify a list of tests to run by passing the names as +arguments to `meson test`. If there were multiple tests with that name (in the +same project or different subprojects), all of them would be run. Now you can: + +1. Run all tests with the specified name from a specific subproject: `meson test subprojname:testname` +1. Run all tests defined in a specific subproject: `meson test subprojectname:` + +As before, these can all be specified multiple times and mixed: + +```sh +# Run: +# * All tests called 'name1' or 'name2' and +# * All tests called 'name3' in subproject 'bar' and +# * All tests in subproject 'foo' +$ meson test name1 name2 bar:name3 foo: +``` diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index ca1411e..be19af3 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -4074,8 +4074,13 @@ This will become a hard error in the future.''' % kwargs['input'], location=self def add_test(self, node, args, kwargs, is_base_test): if len(args) != 2: raise InterpreterException('test expects 2 arguments, {} given'.format(len(args))) - if not isinstance(args[0], str): + name = args[0] + if not isinstance(name, str): raise InterpreterException('First argument of test must be a string.') + if ':' in name: + mlog.deprecation('":" is not allowed in test name "{}", it has been replaced with "_"'.format(name), + location=node) + name = name.replace(':', '_') exe = args[1] if not isinstance(exe, (ExecutableHolder, JarHolder, ExternalProgramHolder)): if isinstance(exe, mesonlib.File): @@ -4120,14 +4125,14 @@ This will become a hard error in the future.''' % kwargs['input'], location=self priority = kwargs.get('priority', 0) if not isinstance(priority, int): raise InterpreterException('Keyword argument priority must be an integer.') - t = Test(args[0], prj, suite, exe.held_object, depends, par, cmd_args, + t = Test(name, prj, suite, exe.held_object, depends, par, cmd_args, env, should_fail, timeout, workdir, protocol, priority) if is_base_test: self.build.tests.append(t) - mlog.debug('Adding test', mlog.bold(args[0], True)) + mlog.debug('Adding test', mlog.bold(name, True)) else: self.build.benchmarks.append(t) - mlog.debug('Adding benchmark', mlog.bold(args[0], True)) + mlog.debug('Adding benchmark', mlog.bold(name, True)) @FeatureNewKwargs('install_headers', '0.47.0', ['install_mode']) @permittedKwargs(permitted_kwargs['install_headers']) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index caf4039..a5c2dda 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -124,7 +124,9 @@ def add_arguments(parser: argparse.ArgumentParser) -> None: parser.add_argument('--test-args', default=[], type=split_args, help='Arguments to pass to the specified test(s) or all tests') parser.add_argument('args', nargs='*', - help='Optional list of tests to run') + help='Optional list of test names to run. "testname" to run all tests with that name, ' + '"subprojname:testname" to specifically run "testname" from "subprojname", ' + '"subprojname:" to run all tests defined by "subprojname".') def returncode_to_status(retcode: int) -> str: @@ -1039,6 +1041,28 @@ class TestHarness: TestHarness.test_in_suites(test, self.options.include_suites)) and not TestHarness.test_in_suites(test, self.options.exclude_suites)) + def tests_from_args(self, tests: T.List[TestSerialisation]) -> T.Generator[TestSerialisation, None, None]: + ''' + Allow specifying test names like "meson test foo1 foo2", where test('foo1', ...) + + Also support specifying the subproject to run tests from like + "meson test subproj:" (all tests inside subproj) or "meson test subproj:foo1" + to run foo1 inside subproj. Coincidentally also "meson test :foo1" to + run all tests with that name across all subprojects, which is + identical to "meson test foo1" + ''' + for arg in self.options.args: + if ':' in arg: + subproj, name = arg.split(':', maxsplit=1) + else: + subproj, name = '', arg + for t in tests: + if subproj and t.project_name != subproj: + continue + if name and t.name != name: + continue + yield t + def get_tests(self) -> T.List[TestSerialisation]: if not self.tests: print('No tests defined.') @@ -1052,9 +1076,8 @@ class TestHarness: else: tests = self.tests - # allow specifying test names like "meson test foo1 foo2", where test('foo1', ...) if self.options.args: - tests = [t for t in tests if t.name in self.options.args] + tests = list(self.tests_from_args(tests)) if not tests: print('No suitable tests defined.') -- cgit v1.1