Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions python/gvtest/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ def __init__(
self.properties: dict[str, str] = {}
self.test_list: list[str] | None = test_list
self.target_names: list[str] = targets if targets is not None else ['default']
self._cli_targets_explicit: bool = targets is not None
self.platform: str = platform
if targets is None:
self.default_target: Target = Target('default')
Expand Down Expand Up @@ -417,7 +418,7 @@ def stop(self) -> None:
@property
def _cli_targets_specified(self) -> bool:
"""True when the user explicitly passed --target."""
return self.target_names != ['default']
return self._cli_targets_explicit

def add_testset(self, file: str) -> None:
if not os.path.isabs(file):
Expand Down Expand Up @@ -480,10 +481,18 @@ def _resolve_targets_for_dir(
loader, '_targets_config_dir', None
)
targets: list[Target] = []
# Filter by --target names, but only filter out
# YAML targets that aren't requested. 'default'
# in target_names means "untargeted tests" and
# doesn't affect YAML target resolution.
cli_real_targets = (
[n for n in self.target_names if n != 'default']
if self._cli_targets_specified
else []
)
for name, cfg in yaml_targets.items():
# If CLI specifies targets, filter
if (self.target_names != ['default']
and name not in self.target_names):
if (cli_real_targets
and name not in cli_real_targets):
continue
t = Target.from_dict(name, cfg)
t.config_dir = config_dir
Expand Down
35 changes: 24 additions & 11 deletions python/gvtest/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,17 +427,30 @@ def enqueue(self) -> None:
else self.runner.get_config()
)

# When --target is specified, skip tests whose target
# is just the fallback (not from a gvtest.yaml
# targets section). This ensures only tests
# belonging to a real target are executed.
if (self.runner._cli_targets_specified
and self.target is not None
and getattr(
self.target, '_is_fallback', False
)):
# Don't count or show — silently excluded
return
# Target filtering when --target is specified:
is_fallback = (
self.target is not None
and getattr(self.target, '_is_fallback', False)
)
if self.runner._cli_targets_specified:
want_default = 'default' in self.runner.target_names
if is_fallback:
# Untargeted test — only run if 'default'
# is in the requested targets
if not want_default:
return
else:
# Targeted test — only run if its target
# name is in the requested targets
target_name = (
self.target.name
if self.target is not None
else None
)
if (target_name is not None
and target_name
not in self.runner.target_names):
return

self.runner.count_test()
if self.runner.tui is not None:
Expand Down
80 changes: 80 additions & 0 deletions tests/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,86 @@ def testset_build(testset):
# Both sub-testsets should run (1 test each)
assert r.stats.stats['passed'] == 2

def test_target_default_runs_only_untargeted(
self, tmp_path
):
"""--target default should run only tests without
a target definition, skipping targeted tests."""
# Sub with real targets
sub = tmp_path / 'sub'
sub.mkdir()
(sub / 'gvtest.yaml').write_text(
'targets:\n target_a: {}\n'
)
(sub / 'testset.cfg').write_text('''
from gvtest.testsuite import *

def testset_build(testset):
testset.set_name('sub')
test = testset.new_test('targeted')
test.add_command(Shell('run', 'echo targeted'))
''')
# Root with untargeted test + import of sub
root = tmp_path / 'testset.cfg'
root.write_text('''
from gvtest.testsuite import *

def testset_build(testset):
testset.set_name('root')
test = testset.new_test('untargeted')
test.add_command(Shell('run', 'echo untargeted'))
testset.import_testset('sub/testset.cfg')
''')
r = Runner(
properties=[], flags=[], nb_threads=1,
targets=['default']
)
r.add_testset(str(root))
r.start()
r.run()
r.stop()
# Only the untargeted test should run
assert r.stats.stats['passed'] == 1

def test_target_default_and_named_together(
self, tmp_path
):
"""--target default --target X should run both
untargeted tests and tests for target X."""
sub = tmp_path / 'sub'
sub.mkdir()
(sub / 'gvtest.yaml').write_text(
'targets:\n target_a: {}\n'
)
(sub / 'testset.cfg').write_text('''
from gvtest.testsuite import *

def testset_build(testset):
testset.set_name('sub')
test = testset.new_test('targeted')
test.add_command(Shell('run', 'echo targeted'))
''')
root = tmp_path / 'testset.cfg'
root.write_text('''
from gvtest.testsuite import *

def testset_build(testset):
testset.set_name('root')
test = testset.new_test('untargeted')
test.add_command(Shell('run', 'echo untargeted'))
testset.import_testset('sub/testset.cfg')
''')
r = Runner(
properties=[], flags=[], nb_threads=1,
targets=['default', 'target_a']
)
r.add_testset(str(root))
r.start()
r.run()
r.stop()
# Both should run
assert r.stats.stats['passed'] == 2

def test_no_cli_target_runs_all_yaml_targets(
self, tmp_path
):
Expand Down
Loading