Skip to content

Commit 85bcd29

Browse files
author
Vasileios Karakasis
committed
Refactor fixture instantiation
Fixtures are instantiated in the `instantiate_all()` method of the `TestRegistry`.
1 parent e4a7776 commit 85bcd29

File tree

2 files changed

+44
-31
lines changed

2 files changed

+44
-31
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 & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ def load_from_module(self, module):
171171
return []
172172

173173
self._set_defaults(registry)
174-
test_pool = registry.instantiate_all() if registry else []
174+
candidate_tests = registry.instantiate_all() if registry else []
175175
legacy_tests = legacy_registry() if legacy_registry else []
176176
if self._external_vars and legacy_tests:
177177
getlogger().warning(
@@ -180,32 +180,11 @@ def load_from_module(self, module):
180180
"please use the 'parameter' builtin in your tests"
181181
)
182182

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)
183+
# Merge tests
184+
candidate_tests += legacy_tests
206185

207186
# Post-instantiation validation of the candidate tests
208-
tests = []
187+
final_tests = []
209188
for c in candidate_tests:
210189
if not isinstance(c, RegressionTest):
211190
continue
@@ -218,15 +197,15 @@ def load_from_module(self, module):
218197
conflicted = self._loaded[c.unique_name]
219198
except KeyError:
220199
self._loaded[c.unique_name] = testfile
221-
tests.append(c)
200+
final_tests.append(c)
222201
else:
223202
raise NameConflictError(
224203
f'test {c.unique_name!r} from {testfile!r} '
225204
f'is already defined in {conflicted!r}'
226205
)
227206

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

231210
def load_from_file(self, filename, force=False):
232211
if not self._validate_source(filename):

0 commit comments

Comments
 (0)