Skip to content

Commit 58aaa04

Browse files
committed
WIP: Added support for repeating seeds from previous test run.
1 parent 6d416fb commit 58aaa04

7 files changed

Lines changed: 120 additions & 29 deletions

File tree

vunit/test/bench.py

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,28 +84,57 @@ def create_tests(self, simulator_if, seed, elaborate_only, test_list=None):
8484
if test_list is None:
8585
test_list = TestList()
8686

87+
key = b"test_history"
88+
if key not in self._database:
89+
test_history = {}
90+
else:
91+
test_history = self._database[key]
92+
8793
if self._individual_tests:
8894
for test_case in self._test_cases:
89-
test_case.create_tests(simulator_if, seed, elaborate_only, test_list)
95+
test_case.create_tests(simulator_if, seed, elaborate_only, test_list, test_history)
9096
elif self._implicit_test:
9197
for config in self._get_configurations_to_run():
98+
actual_seed = seed
99+
if seed == "repeat":
100+
# Test suite name and test name are the same for implicit tests
101+
test_suite_name = test_name = IndependentSimTestCase.get_name(self._implicit_test, config)
102+
103+
if test_suite_name not in test_history:
104+
actual_seed = None
105+
elif test_name not in test_history[test_suite_name]:
106+
actual_seed = None
107+
else:
108+
actual_seed = test_history[test_name][test_name]["seed"]
109+
92110
test_list.add_test(
93111
IndependentSimTestCase(
94112
test=self._implicit_test,
95113
config=config,
96114
simulator_if=simulator_if,
97-
seed=seed,
115+
seed=actual_seed,
98116
elaborate_only=elaborate_only,
99117
)
100118
)
101119
else:
102120
for config in self._get_configurations_to_run():
121+
actual_seed = seed
122+
if seed == "repeat":
123+
test_suite_name = SameSimTestSuite.get_name(config)
124+
125+
if test_suite_name not in test_history:
126+
actual_seed = None
127+
else:
128+
# All tests in the test suite will have the same seed so take the first test
129+
test_name = next(iter(test_history[test_suite_name]))
130+
actual_seed = test_history[test_suite_name][test_name]["seed"]
131+
103132
test_list.add_suite(
104133
SameSimTestSuite(
105134
tests=[test.test for test in self._test_cases],
106135
config=config,
107136
simulator_if=simulator_if,
108-
seed=seed,
137+
seed=actual_seed,
109138
elaborate_only=elaborate_only,
110139
)
111140
)
@@ -414,17 +443,31 @@ def _get_configurations_to_run(self):
414443
del configs[DEFAULT_NAME]
415444
return configs.values()
416445

417-
def create_tests(self, simulator_if, seed, elaborate_only, test_list=None):
446+
def create_tests(self, simulator_if, seed, elaborate_only, test_list=None, test_history=None):
418447
"""
419448
Create all tests from this test case which may be several depending on the number of configurations
420449
"""
450+
test_history = test_history if test_history else {}
451+
421452
for config in self._get_configurations_to_run():
453+
actual_seed = seed
454+
if seed == "repeat":
455+
# Test suite name and test name are the same for individual tests
456+
test_suite_name = test_name = IndependentSimTestCase.get_name(self._test, config)
457+
458+
if test_suite_name not in test_history:
459+
actual_seed = None
460+
elif test_name not in test_history[test_suite_name]:
461+
actual_seed = None
462+
else:
463+
actual_seed = test_history[test_suite_name][test_name]["seed"]
464+
422465
test_list.add_test(
423466
IndependentSimTestCase(
424467
test=self._test,
425468
config=config,
426469
simulator_if=simulator_if,
427-
seed=seed,
470+
seed=actual_seed,
428471
elaborate_only=elaborate_only,
429472
)
430473
)

vunit/test/list.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,6 @@ def run(self, *args, **kwargs):
104104
"""
105105
test_ok = self._test_case.run(*args, **kwargs)
106106
return {self._test_case.name: PASSED if test_ok else FAILED}
107+
108+
def get_seed(self):
109+
return self._test_case.get_seed()

vunit/test/report.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ class TestResult(object):
275275
"""
276276

277277
def __init__(
278-
self, name, status, time, output_file_name, *, test_suite_name, start_time
278+
self, name, status, time, output_file_name, *, test_suite_name, start_time, seed
279279
): # pylint: disable=too-many-arguments
280280
assert status in (PASSED, FAILED, SKIPPED)
281281
self.name = name
@@ -284,6 +284,7 @@ def __init__(
284284
self._output_file_name = output_file_name
285285
self.test_suite_name = test_suite_name
286286
self.start_time = start_time
287+
self.seed = seed
287288

288289
@property
289290
def output(self):

vunit/test/runner.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ def _add_results(
328328
"""
329329
runtime = ostools.get_time() - start_time
330330
time_per_test = runtime / len(results)
331+
seed = test_suite.get_seed()
331332

332333
for test_name in test_suite.test_names:
333334
status = results[test_name]
@@ -338,6 +339,7 @@ def _add_results(
338339
output_file_name,
339340
test_suite_name=test_suite.name,
340341
start_time=start_time,
342+
seed=seed
341343
)
342344
self._report.print_latest_status(total_tests=num_tests)
343345
print()

vunit/test/suites.py

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,23 @@ class IndependentSimTestCase(object):
2121
A test case to be run in an independent simulation
2222
"""
2323

24-
def __init__(self, test, config, simulator_if, seed, *, elaborate_only=False):
25-
self._name = f"{config.library_name!s}.{config.design_unit_name!s}"
24+
@staticmethod
25+
def get_name(test, config):
26+
name = f"{config.library_name!s}.{config.design_unit_name!s}"
2627

2728
if not config.is_default:
28-
self._name += "." + config.name
29+
name += "." + config.name
2930

3031
if test.is_explicit:
31-
self._name += "." + test.name
32+
name += "." + test.name
3233
elif config.is_default:
3334
# JUnit XML test reports wants three dotted name hierarchies
34-
self._name += ".all"
35+
name += ".all"
36+
37+
return name
38+
39+
def __init__(self, test, config, simulator_if, seed, *, elaborate_only=False):
40+
self._name = self.get_name(test, config)
3541

3642
self._configuration = config
3743

@@ -74,19 +80,28 @@ def run(self, *args, **kwargs):
7480
Run the test case using the output_path
7581
"""
7682
results = self._run.run(*args, **kwargs)
83+
7784
return results[self._test.name] == PASSED
7885

86+
def get_seed(self):
87+
return self._run.get_seed()
88+
7989

8090
class SameSimTestSuite(object):
8191
"""
8292
A test suite where multiple test cases are run within the same simulation
8393
"""
8494

85-
def __init__(self, tests, config, simulator_if, seed, *, elaborate_only=False):
86-
self._name = f"{config.library_name!s}.{config.design_unit_name!s}"
87-
95+
@staticmethod
96+
def get_name(config):
97+
name = f"{config.library_name!s}.{config.design_unit_name!s}"
8898
if not config.is_default:
89-
self._name += "." + config.name
99+
name += "." + config.name
100+
101+
return name
102+
103+
def __init__(self, tests, config, simulator_if, seed, *, elaborate_only=False):
104+
self._name = self.get_name(config)
90105

91106
self._configuration = config
92107

@@ -98,7 +113,7 @@ def __init__(self, tests, config, simulator_if, seed, *, elaborate_only=False):
98113
test_suite_name=self._name,
99114
test_cases=[test.name for test in tests],
100115
seed=seed,
101-
)
116+
)
102117

103118
@property
104119
def file_name(self):
@@ -146,8 +161,11 @@ def run(self, *args, **kwargs):
146161
"""
147162
results = self._run.run(*args, **kwargs)
148163
results = {_full_name(self._name, test_name): result for test_name, result in results.items()}
164+
149165
return results
150166

167+
def get_seed(self):
168+
return self._run.get_seed()
151169

152170
class TestRun(object):
153171
"""
@@ -160,7 +178,19 @@ def __init__(self, *, simulator_if, config, elaborate_only, test_suite_name, tes
160178
self._elaborate_only = elaborate_only
161179
self._test_suite_name = test_suite_name
162180
self._test_cases = test_cases
163-
self._args_seed = seed
181+
self._seed = seed
182+
183+
def get_seed(self):
184+
if self._seed:
185+
pass
186+
elif "seed" in self._config.sim_options:
187+
self._seed = self._config.sim_options["seed"]
188+
else:
189+
now_us = str(int(time() * 1e6)).encode()
190+
thread_id = get_ident().to_bytes(8, byteorder="little")
191+
self._seed = blake2b(now_us, digest_size=8, salt=thread_id).hexdigest()
192+
193+
return self._seed
164194

165195
def set_test_cases(self, test_cases):
166196
self._test_cases = test_cases
@@ -223,15 +253,7 @@ def _simulate(self, output_path):
223253
"""
224254

225255
config = self._config.copy()
226-
227-
if self._args_seed:
228-
seed = self._args_seed
229-
elif "seed" in config.sim_options:
230-
seed = config.sim_options["seed"]
231-
else:
232-
now_us = str(int(time() * 1e6)).encode()
233-
thread_id = get_ident().to_bytes(8, byteorder="little")
234-
seed = blake2b(now_us, digest_size=8, salt=thread_id).hexdigest()
256+
seed = self.get_seed()
235257

236258
print(f"Seed for {self._test_suite_name}: {seed}")
237259

vunit/ui/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ def __init__(
163163

164164
self._create_output_path(args.clean)
165165

166-
self._database_version = (10, sys.version)
166+
self._database_version = (11, sys.version)
167167
self._pickled_database_version = (self._database_version[0], pickle.HIGHEST_PROTOCOL)
168168

169169
self._database = self._create_database()
@@ -989,6 +989,7 @@ def _update_test_history(self, report, simulator_if):
989989
test_suite_data[test_result.test_suite_name][test_result.name]["skipped"] = test_result.skipped
990990
test_suite_data[test_result.test_suite_name][test_result.name]["failed"] = test_result.failed
991991
test_suite_data[test_result.test_suite_name][test_result.name]["start_time"] = test_result.start_time
992+
test_suite_data[test_result.test_suite_name][test_result.name]["seed"] = test_result.seed
992993

993994
test_history = self._get_test_history(simulator_if)
994995

vunit/vunit_cli.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,9 @@
3535
"""
3636

3737
import argparse
38-
from pathlib import Path
38+
import re
3939
import os
40+
from pathlib import Path
4041
from vunit.sim_if.factory import SIMULATOR_FACTORY
4142
from vunit.about import version
4243

@@ -265,10 +266,28 @@ def _create_argument_parser(description=None, for_documentation=False):
265266

266267
parser.add_argument("--version", action="version", version=version())
267268

269+
def valid_seed(value):
270+
value = value.lower()
271+
272+
if value == "repeat":
273+
return value
274+
275+
if not re.fullmatch(r'[0-9a-fA-F]+', value):
276+
raise argparse.ArgumentTypeError(f"""Expecting a valid 16-digit hex number or "repeat".""")
277+
278+
if len(value) != 16:
279+
raise argparse.ArgumentTypeError(f"Expecting 16 digits. {value} is {len(value)} digits.")
280+
281+
return value.lower()
282+
268283
parser.add_argument(
269284
"--seed",
270285
default=None,
271-
help="Base seed provided to the simulation. Must be 16 hex digits. Default seed is generated from system time.",
286+
type=valid_seed,
287+
metavar="<seed>|repeat",
288+
help="16 hex digits base seed provided to the simulation or repeat the previous base seed(s)."
289+
""" If no previous base seed has been recorded, "repeat" will use the default base seed which is"""
290+
" generated from system time.",
272291
)
273292

274293
SIMULATOR_FACTORY.add_arguments(parser)

0 commit comments

Comments
 (0)