Skip to content

Commit 9201484

Browse files
committed
WIP: --changed
1 parent fb2d939 commit 9201484

File tree

4 files changed

+63
-15
lines changed

4 files changed

+63
-15
lines changed

tests/unit/test_project.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1942,7 +1942,7 @@ def hash_file_name_of(self, source_file):
19421942
"""
19431943
Get the hash file name of a source_file
19441944
"""
1945-
return self.project._hash_file_name_of(source_file) # pylint: disable=protected-access
1945+
return self.project.hash_file_name_of(source_file)
19461946

19471947
def update(self, source_file):
19481948
"""

vunit/project.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ def get_compile_timestamps(self, files):
481481
# Cache timestamps to avoid duplicate file operations
482482
timestamps = {}
483483
for source_file in files:
484-
hash_file_name = self._hash_file_name_of(source_file)
484+
hash_file_name = self.hash_file_name_of(source_file)
485485
if not ostools.file_exists(hash_file_name):
486486
timestamps[source_file] = None
487487
else:
@@ -614,7 +614,7 @@ def _needs_recompile(self, dependency_graph, source_file, timestamps):
614614
"""
615615
timestamp = timestamps[source_file]
616616

617-
content_hash_file_name = self._hash_file_name_of(source_file)
617+
content_hash_file_name = self.hash_file_name_of(source_file)
618618
if timestamp is None:
619619
LOGGER.debug(
620620
"%s has no vunit_hash file at %s and must be recompiled",
@@ -649,7 +649,7 @@ def _needs_recompile(self, dependency_graph, source_file, timestamps):
649649

650650
return False
651651

652-
def _hash_file_name_of(self, source_file):
652+
def hash_file_name_of(self, source_file):
653653
"""
654654
Returns the name of the hash file associated with the source_file
655655
"""
@@ -663,5 +663,5 @@ def update(self, source_file):
663663
to update the timestamp
664664
"""
665665
new_content_hash = source_file.content_hash
666-
ostools.write_file(self._hash_file_name_of(source_file), new_content_hash)
666+
ostools.write_file(self.hash_file_name_of(source_file), new_content_hash)
667667
LOGGER.debug("Wrote %s content_hash=%s", source_file.name, new_content_hash)

vunit/ui/__init__.py

Lines changed: 51 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
from ..test.bench_list import TestBenchList
4141
from ..test.report import TestReport
4242
from ..test.runner import TestRunner
43+
from ..test.list import TestList
4344
from ..dependency_graph import CircularDependencyException
4445

4546
from .common import LOGGER, TEST_OUTPUT_PATH, select_vhdl_standard, check_not_empty
@@ -176,8 +177,10 @@ def __init__(
176177
self._builtins = Builtins(self, self._vhdl_standard, simulator_class)
177178

178179
self._dependency_graph = None
179-
self._include_in_test_pattern: Optional[List[Union[str, Path]]] = []
180-
self._exclude_from_test_pattern: Optional[List[Union[str, Path]]] = []
180+
self._include_in_test_pattern: Optional[List[Union[str, Path]]] = None
181+
self._exclude_from_test_pattern: Optional[List[Union[str, Path]]] = None
182+
self._latest_dependency_updates = None
183+
self._test_history = None
181184

182185
def _create_database(self):
183186
"""
@@ -806,14 +809,14 @@ def get_dependent_files(dependencies):
806809

807810
dependent_files = get_dependent_files(include_dependencies) - get_dependent_files(exclude_dependencies)
808811

809-
# Extract testbenches from dependent files and create corresponding test patterns:
810-
# lib_name.tb_name*
811-
test_patterns = []
812-
for dependent_file in dependent_files:
813-
library_name = dependent_file.library.name
814-
for testbench in self._test_bench_list.get_test_benches_in_library(library_name):
815-
if testbench.design_unit.source_file == dependent_file:
816-
test_patterns.append(f"{library_name}.{testbench.name}*")
812+
# Extract testbenches from dependent files
813+
dependent_testbenches = []
814+
for testbench in self._test_bench_list.get_test_benches():
815+
if testbench.design_unit.source_file in dependent_files:
816+
dependent_testbenches.append(testbench)
817+
818+
# Create test patterns for remaining testbenches: lib_name.tb_name*
819+
test_patterns = [f"{testbench.library_name}.{testbench.name}*" for testbench in dependent_testbenches]
817820

818821
# Update test filter to match test patterns
819822
if isinstance(self._args.test_patterns, list):
@@ -877,6 +880,12 @@ def _get_latest_dependency_updates(self):
877880

878881
source_files_in_order = self._dependency_graph.toposort()
879882
source_file_timestamps = self._project.get_compile_timestamps(source_files_in_order)
883+
for source_file in source_files_in_order:
884+
hash_file_name = self._project.hash_file_name_of(source_file)
885+
if not ostools.file_exists(hash_file_name) or (
886+
ostools.read_file(hash_file_name) != source_file.content_hash
887+
):
888+
source_file_timestamps[source_file] = 10e9
880889

881890
for source_file, timestamp in source_file_timestamps.items():
882891
dependency_time_stamps = [
@@ -982,12 +991,41 @@ def _update_test_history(self, report, simulator_if):
982991

983992
self._database[b"test_history"] = test_history
984993

994+
def _get_test_list_depending_on_change(self, test_list):
995+
if self._latest_dependency_updates is None:
996+
self._latest_dependency_updates = self._get_latest_dependency_updates()
997+
998+
if self._test_history is None:
999+
self._test_history = self._get_test_history(simulator_if=None)
1000+
1001+
def depending_on_change(test_suite):
1002+
latest_dependency_update = self._latest_dependency_updates[test_suite.file_name]
1003+
test_suite_history = self._test_history.get(test_suite.name, None)
1004+
if test_suite_history:
1005+
test_start_times = [test_data["start_time"] for test_data in test_suite_history.values()]
1006+
if None in test_start_times:
1007+
return True
1008+
1009+
return min(test_start_times) < latest_dependency_update
1010+
else:
1011+
return True
1012+
1013+
new_test_list = TestList()
1014+
for test_suite in test_list:
1015+
if depending_on_change(test_suite):
1016+
new_test_list.add_suite(test_suite)
1017+
1018+
return new_test_list
1019+
9851020
def _main_run(self, post_run):
9861021
"""
9871022
Main with running tests
9881023
"""
9891024
simulator_if = self._create_simulator_if()
9901025
test_list = self._create_tests(simulator_if)
1026+
if self._args.changed:
1027+
test_list = self._get_test_list_depending_on_change(test_list)
1028+
9911029
self._compile(simulator_if)
9921030
print()
9931031

@@ -1022,6 +1060,9 @@ def _main_list_only(self):
10221060
Main function when only listing test cases
10231061
"""
10241062
test_list = self._create_tests(simulator_if=None)
1063+
if self._args.changed:
1064+
test_list = self._get_test_list_depending_on_change(test_list)
1065+
10251066
for test_name in test_list.test_names:
10261067
print(test_name)
10271068
print(f"Listed {test_list.num_tests} tests")

vunit/vunit_cli.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,13 @@ def _create_argument_parser(description=None, for_documentation=False):
166166
help="Output path for compilation and simulation artifacts",
167167
)
168168

169+
parser.add_argument(
170+
"--changed",
171+
action="store_true",
172+
default=False,
173+
help="Include only tests that depend on file changes since the last recorded test run",
174+
)
175+
169176
parser.add_argument(
170177
"--test-prio",
171178
choices=["opt", "ordered"],

0 commit comments

Comments
 (0)