aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharles Brunet <charles.brunet@optelgroup.com>2023-04-03 09:46:26 -0400
committerEli Schwartz <eschwartz93@gmail.com>2023-05-25 13:44:13 -0400
commite7b9dfac98d30eb4ea5489c10b8dfef3bb8331f4 (patch)
treec1cba20c81e41f60d564a50ffa13bde7012ace54
parent11521c6db7facf0703a6037948fdcc1f69cd6246 (diff)
downloadmeson-e7b9dfac98d30eb4ea5489c10b8dfef3bb8331f4.zip
meson-e7b9dfac98d30eb4ea5489c10b8dfef3bb8331f4.tar.gz
meson-e7b9dfac98d30eb4ea5489c10b8dfef3bb8331f4.tar.bz2
mtest: wildcard selection
Allow the use of wildcards (e.g. *) to match test names in `meson test`. Raise an error is given test name does not match any test. Optimize the search by looping through the list of tests only once.
-rw-r--r--docs/markdown/Commands.md6
-rw-r--r--docs/markdown/Unit-tests.md21
-rw-r--r--docs/markdown/snippets/test_name_filters.md9
-rw-r--r--mesonbuild/mtest.py46
-rw-r--r--unittests/allplatformstests.py24
5 files changed, 99 insertions, 7 deletions
diff --git a/docs/markdown/Commands.md b/docs/markdown/Commands.md
index 52eb76c..75b2281 100644
--- a/docs/markdown/Commands.md
+++ b/docs/markdown/Commands.md
@@ -281,6 +281,12 @@ Run tests for the configure Meson project.
See [the unit test documentation](Unit-tests.md) for more info.
+Since *1.2.0* you can use wildcards in *args* for test names.
+For example, "bas*" will match all test with names beginning with "bas".
+
+Since *1.2.0* it is an error to provide a test name or wildcard that
+does not match any test.
+
#### Examples:
Run tests for the project:
diff --git a/docs/markdown/Unit-tests.md b/docs/markdown/Unit-tests.md
index 36fbdcf..18ab111 100644
--- a/docs/markdown/Unit-tests.md
+++ b/docs/markdown/Unit-tests.md
@@ -153,6 +153,27 @@ Specify test(s) by name like:
$ meson test A D
```
+You can run tests from specific (sub)project:
+
+```console
+$ meson test (sub)project_name:
+```
+
+or a specific test in a specific project:
+
+```console
+$ meson test (sub)project_name:test_name
+```
+
+Since version *1.2.0*, you can use wildcards in project
+and test names. For instance, to run all tests beginning with
+"foo" and all tests from projects beginning with "bar":
+
+```console
+$ meson test "foo*" "bar*:"
+```
+
+
Tests belonging to a suite `suite` can be run as follows
```console
diff --git a/docs/markdown/snippets/test_name_filters.md b/docs/markdown/snippets/test_name_filters.md
new file mode 100644
index 0000000..14e62a9
--- /dev/null
+++ b/docs/markdown/snippets/test_name_filters.md
@@ -0,0 +1,9 @@
+## Wildcards in list of tests to run
+
+The `meson test` command now accepts wildcards in the list of test names.
+For example `meson test basic*` will run all tests whose name begins
+with "basic".
+
+meson will report an error if the given test name does not match any
+existing test. meson will log a warning if two redundant test names
+are given (for example if you give both "proj:basic" and "proj:").
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py
index cbf1c19..63041bd 100644
--- a/mesonbuild/mtest.py
+++ b/mesonbuild/mtest.py
@@ -19,6 +19,7 @@ from pathlib import Path
from collections import deque
from contextlib import suppress
from copy import deepcopy
+from fnmatch import fnmatch
import argparse
import asyncio
import datetime
@@ -1859,17 +1860,48 @@ class TestHarness:
run all tests with that name across all subprojects, which is
identical to "meson test foo1"
'''
+ patterns: T.Dict[T.Tuple[str, str], bool] = {}
for arg in self.options.args:
+ # Replace empty components by wildcards:
+ # '' -> '*:*'
+ # 'name' -> '*:name'
+ # ':name' -> '*:name'
+ # 'proj:' -> 'proj:*'
if ':' in arg:
subproj, name = arg.split(':', maxsplit=1)
+ if name == '':
+ name = '*'
+ if subproj == '': # in case arg was ':'
+ subproj = '*'
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
+ subproj, name = '*', arg
+ patterns[(subproj, name)] = False
+
+ for t in tests:
+ # For each test, find the first matching pattern
+ # and mark it as used. yield the matching tests.
+ for subproj, name in list(patterns):
+ if fnmatch(t.project_name, subproj) and fnmatch(t.name, name):
+ patterns[(subproj, name)] = True
+ yield t
+ break
+
+ for (subproj, name), was_used in patterns.items():
+ if not was_used:
+ # For each unused pattern...
+ arg = f'{subproj}:{name}'
+ for t in tests:
+ # ... if it matches a test, then it wasn't used because another
+ # pattern matched the same test before.
+ # Report it as a warning.
+ if fnmatch(t.project_name, subproj) and fnmatch(t.name, name):
+ mlog.warning(f'{arg} test name is redundant and was not used')
+ break
+ else:
+ # If the pattern doesn't match any test,
+ # report it as an error. We don't want the `test` command to
+ # succeed on an invalid pattern.
+ raise MesonException(f'{arg} test name does not match any test')
def get_tests(self) -> T.List[TestSerialisation]:
if not self.tests:
diff --git a/unittests/allplatformstests.py b/unittests/allplatformstests.py
index b04ee2f..6f91857 100644
--- a/unittests/allplatformstests.py
+++ b/unittests/allplatformstests.py
@@ -818,6 +818,30 @@ class AllPlatformTests(BasePlatformTests):
o = self._run(self.mtest_command + ['--list', '--no-rebuild'])
self.assertNotIn('Regenerating build files.', o)
+ def test_unexisting_test_name(self):
+ testdir = os.path.join(self.unit_test_dir, '4 suite selection')
+ self.init(testdir)
+ self.build()
+
+ self.assertRaises(subprocess.CalledProcessError, self._run, self.mtest_command + ['notatest'])
+
+ def test_select_test_using_wildcards(self):
+ testdir = os.path.join(self.unit_test_dir, '4 suite selection')
+ self.init(testdir)
+ self.build()
+
+ o = self._run(self.mtest_command + ['--list', 'mainprj*'])
+ self.assertIn('mainprj-failing_test', o)
+ self.assertIn('mainprj-successful_test_no_suite', o)
+ self.assertNotIn('subprj', o)
+
+ o = self._run(self.mtest_command + ['--list', '*succ*', 'subprjm*:'])
+ self.assertIn('mainprj-successful_test_no_suite', o)
+ self.assertIn('subprjmix-failing_test', o)
+ self.assertIn('subprjmix-successful_test', o)
+ self.assertIn('subprjsucc-successful_test_no_suite', o)
+ self.assertNotIn('subprjfail-failing_test', o)
+
def test_build_by_default(self):
testdir = os.path.join(self.common_test_dir, '129 build by default')
self.init(testdir)