Skip to content

Commit ac4aabc

Browse files
committed
twister: support filtering tests using regex
Use --test-pattern to filter scenarios using regular expressions, for example: ./scripts/twister --test-pattern '^kernel.semaphore.*' -v will run all those test using this identifier pattern, and nothing else. Multiple patterns are possible. Signed-off-by: Anas Nashif <[email protected]>
1 parent 5e4688f commit ac4aabc

File tree

3 files changed

+45
-16
lines changed

3 files changed

+45
-16
lines changed

scripts/pylib/twister/twisterlib/environment.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,12 @@ def add_parse_arguments(parser = None) -> argparse.ArgumentParser:
253253
timeout would be multiplication of test timeout value, board-level timeout multiplier
254254
and global timeout multiplier (this parameter)""")
255255

256+
parser.add_argument(
257+
"--test-pattern", action="append",
258+
help="""Run only the tests matching the specified pattern. The pattern
259+
can include regular expressions.
260+
""")
261+
256262
test_xor_subtest.add_argument(
257263
"-s", "--test", "--scenario", action="append", type = norm_path,
258264
help="""Run only the specified test suite scenario. These are named by

scripts/pylib/twister/twisterlib/testplan.py

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -202,13 +202,12 @@ def find_subtests(self):
202202

203203
def discover(self):
204204
self.handle_modules()
205-
if self.options.test:
206-
self.run_individual_testsuite = self.options.test
207-
208205
self.test_config = TestConfiguration(self.env.test_config)
209206

210207
self.add_configurations()
211-
num = self.add_testsuites(testsuite_filter=self.run_individual_testsuite)
208+
num = self.add_testsuites(testsuite_filter=self.options.test,
209+
testsuite_pattern=self.options.test_pattern)
210+
212211
if num == 0:
213212
raise TwisterRuntimeError("No testsuites found at the specified location...")
214213
if self.load_errors:
@@ -507,9 +506,35 @@ def get_tests_list(self):
507506
testcases.remove(case.detailed_name)
508507
return testcases
509508

510-
def add_testsuites(self, testsuite_filter=None):
509+
def _is_testsuite_selected(self, suite: TestSuite, testsuite_filter, testsuite_patterns_r):
510+
"""Check if the testsuite is selected by the user."""
511+
if not testsuite_filter and not testsuite_patterns_r:
512+
# no matching requested, include all testsuites
513+
return True
514+
if testsuite_filter:
515+
scenario = os.path.basename(suite.name)
516+
if (
517+
suite.name
518+
and (suite.name in testsuite_filter or scenario in testsuite_filter)
519+
):
520+
return True
521+
if testsuite_patterns_r:
522+
for r in testsuite_patterns_r:
523+
if r.search(suite.id):
524+
return True
525+
return False
526+
527+
def add_testsuites(self, testsuite_filter=None, testsuite_pattern=None):
511528
if testsuite_filter is None:
512529
testsuite_filter = []
530+
531+
testsuite_patterns_r = []
532+
if testsuite_pattern is None:
533+
testsuite_pattern = []
534+
else:
535+
for pattern in testsuite_pattern:
536+
testsuite_patterns_r.append(re.compile(pattern))
537+
513538
for root in self.env.test_roots:
514539
root = os.path.abspath(root)
515540

@@ -574,14 +599,11 @@ def add_testsuites(self, testsuite_filter=None):
574599
else:
575600
suite.add_subcases(suite_dict)
576601

577-
if testsuite_filter:
578-
scenario = os.path.basename(suite.name)
579-
if (
580-
suite.name
581-
and (suite.name in testsuite_filter or scenario in testsuite_filter)
582-
):
583-
self.testsuites[suite.name] = suite
584-
elif suite.name in self.testsuites:
602+
if not self._is_testsuite_selected(suite, testsuite_filter,
603+
testsuite_patterns_r):
604+
# skip testsuite if they were not selected directly by the user
605+
continue
606+
if suite.name in self.testsuites:
585607
msg = (
586608
f"test suite '{suite.name}' in '{suite.yamlfile}' is already added"
587609
)

scripts/tests/twister/test_testplan.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,7 @@ def test_testplan_discover(
570570
env.test_config = tmp_tc
571571
testplan = TestPlan(env=env)
572572
testplan.options = mock.Mock(
573+
test_pattern=[],
573574
test='ts1',
574575
quarantine_list=[tmp_path / qf for qf in ql],
575576
quarantine_verify=qv
@@ -589,7 +590,7 @@ def test_testplan_discover(
589590
with pytest.raises(exception) if exception else nullcontext():
590591
testplan.discover()
591592

592-
testplan.add_testsuites.assert_called_once_with(testsuite_filter='ts1')
593+
testplan.add_testsuites.assert_called_once_with(testsuite_filter='ts1', testsuite_pattern=[])
593594
assert all([log in caplog.text for log in expected_logs])
594595

595596

@@ -1186,7 +1187,7 @@ def test_testplan_get_all_tests():
11861187
(['good_test/dummy.common.1', 'good_test/dummy.common.2', 'good_test/dummy.common.3'], False, True, 3, 1),
11871188
(['good_test/dummy.common.1', 'good_test/dummy.common.2',
11881189
'duplicate_test/dummy.common.1', 'duplicate_test/dummy.common.2'], False, True, 4, 1),
1189-
(['dummy.common.1', 'dummy.common.2'], False, False, 2, 1),
1190+
(['dummy.common.1', 'dummy.common.2'], False, False, 2, 2),
11901191
(['good_test/dummy.common.1', 'good_test/dummy.common.2', 'good_test/dummy.common.3'], True, True, 0, 1),
11911192
]
11921193

@@ -1314,7 +1315,7 @@ def test_testplan_add_testsuites(tmp_path, testsuite_filter, use_alt_root, detai
13141315

13151316
testplan = TestPlan(env=env)
13161317

1317-
res = testplan.add_testsuites(testsuite_filter)
1318+
res = testplan.add_testsuites(testsuite_filter, testsuite_pattern=[])
13181319

13191320
assert res == expected_suite_count
13201321
assert testplan.load_errors == expected_errors

0 commit comments

Comments
 (0)