Skip to content

Commit 923df39

Browse files
author
Vasileios Karakasis
authored
Merge pull request #2474 from vkarak/refactor/fixture-instantiation
[refactor] Refactor fixture instantiation
2 parents f44e37c + 91c2cd3 commit 923df39

File tree

2 files changed

+44
-32
lines changed

2 files changed

+44
-32
lines changed

reframe/core/decorators.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,16 @@
2222
import reframe.core.warnings as warn
2323
import reframe.core.hooks as hooks
2424
from reframe.core.exceptions import ReframeSyntaxError, SkipTestError, what
25+
from reframe.core.fixtures import FixtureRegistry
2526
from reframe.core.logging import getlogger
2627
from reframe.core.pipeline import RegressionTest
2728
from reframe.utility.versioning import VersionValidator
2829

2930

31+
# NOTE: we should consider renaming this module in 4.0; it practically takes
32+
# care of the registration and instantiation of the tests.
33+
34+
3035
class TestRegistry:
3136
'''Regression test registry.
3237
@@ -61,14 +66,20 @@ def skip(self, test):
6166

6267
def instantiate_all(self):
6368
'''Instantiate all the registered tests.'''
64-
ret = []
69+
70+
# We first instantiate the leaf tests and then walk up their
71+
# dependencies to instantiate all the fixtures. Fixtures can only
72+
# establish their exact dependencies at instantiation time, so the
73+
# dependency graph grows dynamically.
74+
75+
leaf_tests = []
6576
for test, variants in self._tests.items():
6677
if test in self._skip_tests:
6778
continue
6879

6980
for args, kwargs in variants:
7081
try:
71-
ret.append(test(*args, **kwargs))
82+
leaf_tests.append(test(*args, **kwargs))
7283
except SkipTestError as e:
7384
getlogger().warning(
7485
f'skipping test {test.__qualname__!r}: {e}'
@@ -82,7 +93,30 @@ def instantiate_all(self):
8293
)
8394
getlogger().verbose(traceback.format_exc())
8495

85-
return ret
96+
# Instantiate fixtures
97+
98+
# Do a level-order traversal of the fixture registries of all leaf
99+
# tests, instantiate all fixtures and generate the final set of
100+
# candidate tests; the leaf tests are consumed at the end of the
101+
# traversal and all instantiated tests (including fixtures) are stored
102+
# in `final_tests`.
103+
final_tests = []
104+
fixture_registry = FixtureRegistry()
105+
while leaf_tests:
106+
tmp_registry = FixtureRegistry()
107+
while leaf_tests:
108+
c = leaf_tests.pop()
109+
reg = getattr(c, '_rfm_fixture_registry', None)
110+
final_tests.append(c)
111+
if reg:
112+
tmp_registry.update(reg)
113+
114+
# Instantiate the new fixtures and update the registry
115+
new_fixtures = tmp_registry.difference(fixture_registry)
116+
leaf_tests = new_fixtures.instantiate_all()
117+
fixture_registry.update(new_fixtures)
118+
119+
return final_tests
86120

87121
def __iter__(self):
88122
'''Iterate over the registered test classes.'''

reframe/frontend/loader.py

Lines changed: 7 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import reframe.utility.osext as osext
1919
from reframe.core.exceptions import NameConflictError, is_severe, what
2020
from reframe.core.logging import getlogger
21-
from reframe.core.fixtures import FixtureRegistry
2221

2322

2423
class RegressionCheckValidator(ast.NodeVisitor):
@@ -171,7 +170,7 @@ def load_from_module(self, module):
171170
return []
172171

173172
self._set_defaults(registry)
174-
test_pool = registry.instantiate_all() if registry else []
173+
candidate_tests = registry.instantiate_all() if registry else []
175174
legacy_tests = legacy_registry() if legacy_registry else []
176175
if self._external_vars and legacy_tests:
177176
getlogger().warning(
@@ -180,32 +179,11 @@ def load_from_module(self, module):
180179
"please use the 'parameter' builtin in your tests"
181180
)
182181

183-
# Merge registries
184-
test_pool += legacy_tests
185-
186-
# Do a level-order traversal of the fixture registries of all tests in
187-
# the test pool, instantiate all fixtures and generate the final set
188-
# of candidate tests to load; the test pool is consumed at the end of
189-
# the traversal and all instantiated tests (including fixtures) are
190-
# stored in `candidate_tests`.
191-
candidate_tests = []
192-
fixture_registry = FixtureRegistry()
193-
while test_pool:
194-
tmp_registry = FixtureRegistry()
195-
while test_pool:
196-
c = test_pool.pop()
197-
reg = getattr(c, '_rfm_fixture_registry', None)
198-
candidate_tests.append(c)
199-
if reg:
200-
tmp_registry.update(reg)
201-
202-
# Instantiate the new fixtures and update the registry
203-
new_fixtures = tmp_registry.difference(fixture_registry)
204-
test_pool = new_fixtures.instantiate_all()
205-
fixture_registry.update(new_fixtures)
182+
# Merge tests
183+
candidate_tests += legacy_tests
206184

207185
# Post-instantiation validation of the candidate tests
208-
tests = []
186+
final_tests = []
209187
for c in candidate_tests:
210188
if not isinstance(c, RegressionTest):
211189
continue
@@ -218,15 +196,15 @@ def load_from_module(self, module):
218196
conflicted = self._loaded[c.unique_name]
219197
except KeyError:
220198
self._loaded[c.unique_name] = testfile
221-
tests.append(c)
199+
final_tests.append(c)
222200
else:
223201
raise NameConflictError(
224202
f'test {c.unique_name!r} from {testfile!r} '
225203
f'is already defined in {conflicted!r}'
226204
)
227205

228-
getlogger().debug(f' > Loaded {len(tests)} test(s)')
229-
return tests
206+
getlogger().debug(f' > Loaded {len(final_tests)} test(s)')
207+
return final_tests
230208

231209
def load_from_file(self, filename, force=False):
232210
if not self._validate_source(filename):

0 commit comments

Comments
 (0)