diff --git a/docs/manpage.rst b/docs/manpage.rst index 66b3e07a82..6eb2e012d0 100644 --- a/docs/manpage.rst +++ b/docs/manpage.rst @@ -92,6 +92,17 @@ This happens recursively so that if test ``T1`` depends on ``T2`` and ``T2`` dep These are all tests with :attr:`num_gpus_per_node` greater than zero. This option and :option:`--cpu-only` are mutually exclusive. +.. option:: --maintainer=MAINTAINER + + Filter tests by maintainer. + ``MAINTAINER`` is interpreted as a `Python Regular Expression `__; all tests that have at least a matching maintainer will be selected. + ``MAINTAINER`` being a regular expression has the implication that ``--maintainer 'foo'`` will select also tests that define ``'foobar'`` as a maintainer. + To restrict the selection to tests defining only ``'foo'``, you should use ``--maintainer 'foo$'``. + + This option may be specified multiple times, in which case only tests defining or matching *all* maintainers will be selected. + + .. versionadded:: 3.9.1 + .. option:: -n, --name=NAME Filter tests by name. diff --git a/reframe/frontend/cli.py b/reframe/frontend/cli.py index 9cf350fd61..4a9fd734f2 100644 --- a/reframe/frontend/cli.py +++ b/reframe/frontend/cli.py @@ -259,6 +259,11 @@ def main(): '--gpu-only', action='store_true', help='Select only GPU checks' ) + select_options.add_argument( + '--maintainer', action='append', dest='maintainers', default=[], + metavar='PATTERN', + help='Select checks with at least one maintainer matching PATTERN' + ) select_options.add_argument( '-n', '--name', action='append', dest='names', default=[], metavar='PATTERN', help='Select checks whose name matches PATTERN' @@ -842,6 +847,10 @@ def print_infoline(param, value): f'Filtering test cases(s) by tags: {len(testcases)} remaining' ) + # Filter test cases by maintainers + for maint in options.maintainers: + testcases = filter(filters.have_maintainer(maint), testcases) + # Filter test cases further if options.gpu_only and options.cpu_only: printer.error("options `--gpu-only' and `--cpu-only' " diff --git a/reframe/frontend/filters.py b/reframe/frontend/filters.py index f8b6e5990d..0319251f56 100644 --- a/reframe/frontend/filters.py +++ b/reframe/frontend/filters.py @@ -47,6 +47,15 @@ def _fn(case): return _fn +def have_maintainer(patt): + regex = re_compile(patt) + + def _fn(case): + return any(regex.match(p) for p in case.check.maintainers) + + return _fn + + def have_gpu_only(): def _fn(case): return case.check.num_gpus_per_node > 0 diff --git a/unittests/test_filters.py b/unittests/test_filters.py index be1f2fba1c..1036f5dc34 100644 --- a/unittests/test_filters.py +++ b/unittests/test_filters.py @@ -34,17 +34,20 @@ def sample_cases(): make_case({ 'name': 'check1', 'tags': {'a', 'b', 'c', 'd'}, - 'num_gpus_per_node': 1 + 'num_gpus_per_node': 1, + 'maintainers': {'A', 'B', 'C', 'D'} }), make_case({ 'name': 'check2', 'tags': {'x', 'y', 'z'}, - 'num_gpus_per_node': 0 + 'num_gpus_per_node': 0, + 'maintainers': {'X', 'Y', 'Z'} }), make_case({ 'name': 'check3', 'tags': {'a', 'z'}, - 'num_gpus_per_node': 1 + 'num_gpus_per_node': 1, + 'maintainers': {'A', 'Z'} }) ] @@ -82,6 +85,12 @@ def test_have_not_tags(sample_cases): assert 1 == count_checks(filters.have_not_tag('z'), sample_cases) +def test_have_maintainers(sample_cases): + assert 2 == count_checks(filters.have_maintainer('A|C'), sample_cases) + assert 0 == count_checks(filters.have_maintainer('P|Q'), sample_cases) + assert 2 == count_checks(filters.have_maintainer('Z'), sample_cases) + + def test_have_gpu_only(sample_cases): assert 2 == count_checks(filters.have_gpu_only(), sample_cases)