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
34 changes: 28 additions & 6 deletions python/gvtest/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ def __init__(
self.tui: Any = None
self.stats: TestsetStats = TestsetStats()
self.nb_total_tests: int = 0
self._module_cache: dict[str, Any] = {}
if properties is not None:
for prop in properties:
name, value = prop.split('=')
Expand Down Expand Up @@ -493,12 +494,17 @@ def import_testset(
sys.path.insert(0, path)
logging.debug(f"Added to sys.path for testset: {path}")

# Use a unique module name per file to avoid collisions in sys.modules
# Cache modules: import once, call testset_build per target.
# This preserves global state across targets.
module_name: str = f"gvtest_testset_{hash(file)}"
spec = importlib.util.spec_from_loader(module_name, SourceFileLoader(module_name, file))
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
if module_name in self._module_cache:
module = self._module_cache[module_name]
else:
spec = importlib.util.spec_from_loader(module_name, SourceFileLoader(module_name, file))
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
self._module_cache[module_name] = module

# testset_build() must run while python_paths are still in sys.path,
# since it may import modules from configured paths
Expand Down Expand Up @@ -545,7 +551,23 @@ def check_pending_tests(self) -> None:
self.lock.release()
break

test: TestRun = self.pending_tests.pop()
# Find a test whose dependencies are met
test: TestRun | None = None
for i in range(
len(self.pending_tests) - 1, -1, -1
):
candidate = self.pending_tests[i]
if candidate.test.deps_satisfied():
test = self.pending_tests.pop(i)
break

if test is None:
# All pending tests have unmet deps;
# wait for running tests to finish
self.lock.release()
time.sleep(0.1)
continue

self.lock.release()

while not self.check_cpu_load():
Expand Down
24 changes: 24 additions & 0 deletions python/gvtest/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def __init__(self, test: TestCommon, target: Any | None) -> None:
self.envvars: dict[str, str] | None = None
self.skip_message: str = ""
self.status: str = "failed"
self.finished: bool = False
self.output: str = ""
self.timeout_reached: bool = False
self.current_proc: subprocess.Popen[bytes] | None = None
Expand All @@ -84,6 +85,7 @@ def run(self) -> None:
if self.runner._interrupted:
self.output = ''
self.status = "failed"
self.finished = True
self.duration = 0
self.runner.terminate(self)
return
Expand Down Expand Up @@ -220,6 +222,7 @@ def __print_start_message(self) -> None:

# Print end banner
def print_end_message(self) -> None:
self.finished = True
testname: str = (
self.test.get_full_name() or ''
).ljust(self.runner.get_max_testname_len() + 5)
Expand Down Expand Up @@ -391,6 +394,7 @@ def __init__(
self.runner.declare_name(self.full_name)
self.benchs: list[list[str]] = []
self.runs: list[TestRun] = []
self.dependencies: list[TestCommon] = []

def skip(self, msg: str) -> TestCommon:
self.skipped = msg
Expand All @@ -403,6 +407,26 @@ def get_target(self) -> Any | None:
def add_command(self, command: testsuite.Command) -> None:
self.commands.append(command)

def depends_on(self, *tests: testsuite.Test) -> None:
"""Declare that this test depends on other tests.

This test will not start until all dependencies
have completed (passed, failed, or skipped).
"""
for t in tests:
if isinstance(t, TestCommon):
self.dependencies.append(t)

def deps_satisfied(self) -> bool:
"""Check if all dependencies have completed."""
for dep in self.dependencies:
if not dep.runs:
return False
for run in dep.runs:
if not run.finished:
return False
return True


# Called by runner to enqueue this test to the list of tests ready to be executed
def enqueue(self) -> None:
Expand Down
3 changes: 3 additions & 0 deletions python/gvtest/testsuite.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ def get_path(self) -> str: pass
@abc.abstractmethod
def add_command(self, command: Command) -> None: pass

@abc.abstractmethod
def depends_on(self, *tests: Test) -> None: pass



class Testset(object, metaclass=abc.ABCMeta):
Expand Down
Loading