Skip to content

Commit 74dc4ba

Browse files
committed
Added support for running testbenches depending or not depending on a set of user-provided source file patterns.
1 parent 9fa4f37 commit 74dc4ba

File tree

3 files changed

+288
-18
lines changed

3 files changed

+288
-18
lines changed

tests/unit/test_ui.py

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from string import Template
1515
from pathlib import Path
1616
from os import chdir, getcwd
17+
from os.path import relpath
1718
import json
1819
import re
1920
from re import MULTILINE
@@ -1265,6 +1266,187 @@ def test_get_testbench_files(self):
12651266
sorted(expected, key=lambda x: x.name),
12661267
)
12671268

1269+
@with_tempdir
1270+
def test_update_test_pattern(self, tempdir):
1271+
relative_tempdir = Path(relpath(str(tempdir.resolve()), str(Path().cwd())))
1272+
1273+
def setup(ui):
1274+
"Setup the project"
1275+
ui.add_vhdl_builtins()
1276+
lib1 = ui.add_library("lib1")
1277+
lib2 = ui.add_library("lib2")
1278+
1279+
rtl = []
1280+
for i in range(4):
1281+
vhdl_source = f"""\
1282+
entity rtl{i} is
1283+
end entity;
1284+
1285+
architecture a of rtl{i} is
1286+
begin
1287+
end architecture;
1288+
"""
1289+
file_name = str(Path(tempdir) / f"rtl{i}.vhd")
1290+
self.create_file(file_name, vhdl_source)
1291+
rtl.append(lib1.add_source_file(file_name))
1292+
1293+
verilog_source = """\
1294+
module rtl4;
1295+
endmodule
1296+
"""
1297+
file_name = str(Path(tempdir) / f"rtl4.v")
1298+
self.create_file(file_name, verilog_source)
1299+
rtl.append(lib2.add_source_file(file_name))
1300+
1301+
tb = []
1302+
for i in range(2):
1303+
file_name = str(Path(tempdir) / f"tb{i}.vhd")
1304+
create_vhdl_test_bench_file(
1305+
f"tb{i}",
1306+
file_name,
1307+
tests=["Test 1"] if i == 0 else [],
1308+
)
1309+
if i == 0:
1310+
tb.append(lib1.add_source_file(file_name))
1311+
else:
1312+
tb.append(lib2.add_source_file(file_name))
1313+
1314+
rtl[1].add_dependency_on(rtl[0])
1315+
rtl[2].add_dependency_on(rtl[0])
1316+
rtl[2].add_dependency_on(rtl[4])
1317+
tb[0].add_dependency_on(rtl[1])
1318+
tb[1].add_dependency_on(rtl[2])
1319+
1320+
return rtl, tb
1321+
1322+
def check_stdout(ui, expected):
1323+
"Check that stdout matches expected"
1324+
with mock.patch("sys.stdout", autospec=True) as stdout:
1325+
self._run_main(ui)
1326+
text = "".join([call[1][0] for call in stdout.write.mock_calls])
1327+
# @TODO not always in the same order in Python3 due to dependency graph
1328+
print(text)
1329+
self.assertEqual(set(text.splitlines()), set(expected.splitlines()))
1330+
1331+
def restore_test_pattern():
1332+
ui.update_test_pattern()
1333+
1334+
ui = self._create_ui("--list")
1335+
rtl, tb = setup(ui)
1336+
ui.update_test_pattern()
1337+
check_stdout(ui, "lib1.tb0.Test 1\nlib2.tb1.all\nListed 2 tests")
1338+
1339+
restore_test_pattern()
1340+
ui.update_test_pattern(["*"])
1341+
check_stdout(ui, "lib1.tb0.Test 1\nlib2.tb1.all\nListed 2 tests")
1342+
1343+
restore_test_pattern()
1344+
ui.update_test_pattern(exclude_dependent_on=["*"])
1345+
check_stdout(ui, "Listed 0 tests")
1346+
1347+
restore_test_pattern()
1348+
ui.update_test_pattern(["*"], ["*"])
1349+
check_stdout(ui, "Listed 0 tests")
1350+
1351+
restore_test_pattern()
1352+
ui.update_test_pattern([rtl[0]._source_file.name])
1353+
check_stdout(ui, "lib1.tb0.Test 1\nlib2.tb1.all\nListed 2 tests")
1354+
1355+
restore_test_pattern()
1356+
ui.update_test_pattern([rtl[1]._source_file.name])
1357+
check_stdout(ui, "lib1.tb0.Test 1\nListed 1 tests")
1358+
1359+
restore_test_pattern()
1360+
ui.update_test_pattern([Path(rtl[2]._source_file.name)])
1361+
check_stdout(ui, "lib2.tb1.all\nListed 1 tests")
1362+
1363+
restore_test_pattern()
1364+
ui.update_test_pattern([rtl[3]._source_file.name])
1365+
check_stdout(ui, "Listed 0 tests")
1366+
1367+
restore_test_pattern()
1368+
ui.update_test_pattern([Path(rtl[4]._source_file.name)])
1369+
check_stdout(ui, "lib2.tb1.all\nListed 1 tests")
1370+
1371+
restore_test_pattern()
1372+
ui.update_test_pattern([tb[0]._source_file.name])
1373+
check_stdout(ui, "lib1.tb0.Test 1\nListed 1 tests")
1374+
1375+
restore_test_pattern()
1376+
ui.update_test_pattern([tb[1]._source_file.name])
1377+
check_stdout(ui, "lib2.tb1.all\nListed 1 tests")
1378+
1379+
restore_test_pattern()
1380+
ui.update_test_pattern([tb[1]._source_file.name, rtl[1]._source_file.name])
1381+
check_stdout(ui, "lib1.tb0.Test 1\nlib2.tb1.all\nListed 2 tests")
1382+
1383+
restore_test_pattern()
1384+
ui.update_test_pattern([tb[1]._source_file.name, "Missing file"])
1385+
check_stdout(ui, "lib2.tb1.all\nListed 1 tests")
1386+
1387+
restore_test_pattern()
1388+
a_dir = Path(tempdir) / "a_dir"
1389+
a_dir.mkdir()
1390+
ui.update_test_pattern([tb[1]._source_file.name, a_dir])
1391+
check_stdout(ui, "lib2.tb1.all\nListed 1 tests")
1392+
1393+
restore_test_pattern()
1394+
ui.update_test_pattern([relative_tempdir / "rtl1*"])
1395+
check_stdout(ui, "lib1.tb0.Test 1\nListed 1 tests")
1396+
1397+
restore_test_pattern()
1398+
ui.update_test_pattern(["./*rtl1*"])
1399+
check_stdout(ui, "lib1.tb0.Test 1\nListed 1 tests")
1400+
1401+
# Create a path that starts with ..
1402+
path = Path(rtl[0]._source_file.name).resolve()
1403+
path_relative_drive = path.relative_to(path.anchor)
1404+
relative_path_to_drive = Path("../" * len(Path(".").resolve().parents))
1405+
test_path = relative_path_to_drive / path_relative_drive
1406+
ui.update_test_pattern([test_path])
1407+
check_stdout(ui, "lib1.tb0.Test 1\nlib2.tb1.all\nListed 2 tests")
1408+
1409+
restore_test_pattern()
1410+
ui.update_test_pattern([tempdir / "rtl?.vhd"])
1411+
check_stdout(ui, "lib1.tb0.Test 1\nlib2.tb1.all\nListed 2 tests")
1412+
1413+
restore_test_pattern()
1414+
ui.update_test_pattern([rtl[0]._source_file.name], [rtl[1]._source_file.name])
1415+
check_stdout(ui, "lib2.tb1.all\nListed 1 tests")
1416+
1417+
restore_test_pattern()
1418+
ui.update_test_pattern([rtl[0]._source_file.name], [rtl[3]._source_file.name])
1419+
check_stdout(ui, "lib1.tb0.Test 1\nlib2.tb1.all\nListed 2 tests")
1420+
1421+
restore_test_pattern()
1422+
ui.update_test_pattern([rtl[0]._source_file.name], [rtl[3]._source_file.name, rtl[4]._source_file.name])
1423+
check_stdout(ui, "lib1.tb0.Test 1\nListed 1 tests")
1424+
1425+
restore_test_pattern()
1426+
ui.update_test_pattern(exclude_dependent_on=[rtl[4]._source_file.name])
1427+
check_stdout(ui, "lib1.tb0.Test 1\nListed 1 tests")
1428+
1429+
restore_test_pattern()
1430+
ui.update_test_pattern(["*.v"])
1431+
check_stdout(ui, "lib2.tb1.all\nListed 1 tests")
1432+
1433+
restore_test_pattern()
1434+
ui.update_test_pattern(exclude_dependent_on=["*.v"])
1435+
check_stdout(ui, "lib1.tb0.Test 1\nListed 1 tests")
1436+
1437+
restore_test_pattern()
1438+
ui.update_test_pattern(["*.v"], ["*.v"])
1439+
check_stdout(ui, "Listed 0 tests")
1440+
1441+
restore_test_pattern()
1442+
ui.update_test_pattern(set([rtl[0]._source_file.name]), set([rtl[3]._source_file.name]))
1443+
check_stdout(ui, "lib1.tb0.Test 1\nlib2.tb1.all\nListed 2 tests")
1444+
1445+
ui = self._create_ui("--list", "*tb0*")
1446+
rtl, tb = setup(ui)
1447+
ui.update_test_pattern([tb[1]._source_file.name])
1448+
check_stdout(ui, "lib1.tb0.Test 1\nlib2.tb1.all\nListed 2 tests")
1449+
12681450
def test_get_simulator_name(self):
12691451
ui = self._create_ui()
12701452
self.assertEqual(ui.get_simulator_name(), "mock")

vunit/project.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ def get_files_in_compile_order(self, incremental=True, dependency_graph=None, fi
500500
files_to_recompile = self._get_files_to_recompile(
501501
files or self.get_source_files_in_order(), dependency_graph, incremental
502502
)
503-
return self._get_affected_files_in_compile_order(files_to_recompile, dependency_graph.get_dependent)
503+
return self.get_affected_files_in_compile_order(files_to_recompile, dependency_graph.get_dependent)
504504

505505
def _get_files_to_recompile(self, files, dependency_graph, incremental):
506506
"""
@@ -527,15 +527,15 @@ def get_dependencies_in_compile_order(self, target_files=None, implementation_de
527527
target_files = self._source_files_in_order
528528

529529
dependency_graph = self.create_dependency_graph(implementation_dependencies)
530-
return self._get_affected_files_in_compile_order(set(target_files), dependency_graph.get_dependencies)
530+
return self.get_affected_files_in_compile_order(set(target_files), dependency_graph.get_dependencies)
531531

532-
def _get_affected_files_in_compile_order(self, target_files, get_depend_func):
532+
def get_affected_files_in_compile_order(self, target_files, get_depend_func):
533533
"""
534534
Returns the affected files in compile order given a list of target files and a dependencie function
535535
:param target_files: The files to compile
536536
:param get_depend_func: one of DependencyGraph [get_dependencies, get_dependent, get_direct_dependencies]
537537
"""
538-
affected_files = self._get_affected_files(target_files, get_depend_func)
538+
affected_files = self.get_affected_files(target_files, get_depend_func)
539539
return self._get_compile_order(affected_files, get_depend_func.__self__)
540540

541541
def get_minimal_file_set_in_compile_order(self, target_files=None):
@@ -546,7 +546,7 @@ def get_minimal_file_set_in_compile_order(self, target_files=None):
546546
###
547547
# First get all files that are required to fullfill the dependencies for the target files
548548
dependency_graph = self.create_dependency_graph(True)
549-
dependency_files = self._get_affected_files(
549+
dependency_files = self.get_affected_files(
550550
target_files or self.get_source_files_in_order(),
551551
dependency_graph.get_dependencies,
552552
)
@@ -562,7 +562,7 @@ def get_minimal_file_set_in_compile_order(self, target_files=None):
562562
min_file_set_to_be_compiled = [f for f in max_file_set_to_be_compiled if f in dependency_files]
563563
return min_file_set_to_be_compiled
564564

565-
def _get_affected_files(self, target_files, get_depend_func):
565+
def get_affected_files(self, target_files, get_depend_func):
566566
"""
567567
Get affected files given a list of type SourceFile, if the list is None
568568
all files are taken into account

0 commit comments

Comments
 (0)