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.
This commit is contained in:
parent
11521c6db7
commit
e7b9dfac98
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:").
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue