Skip to content

Commit d40f8b1

Browse files
author
Vasileios Karakasis
authored
Merge pull request #1934 from vkarak/feat/deprecate-parameterized_test
[feat] Deprecate the use of the `@parameterized_test` decorator
2 parents 15d37da + 0bc03dc commit d40f8b1

File tree

9 files changed

+87
-156
lines changed

9 files changed

+87
-156
lines changed

reframe/core/decorators.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import traceback
2121

2222
import reframe.utility.osext as osext
23+
import reframe.core.warnings as warn
2324
from reframe.core.exceptions import (ReframeSyntaxError,
2425
SkipTestError,
2526
user_frame)
@@ -60,10 +61,12 @@ def _instantiate_all():
6061
getlogger().warning(f'skipping test {cls.__name__!r}: {e}')
6162
except Exception:
6263
frame = user_frame(*sys.exc_info())
64+
filename = frame.filename if frame else 'n/a'
65+
lineno = frame.lineno if frame else 'n/a'
6366
getlogger().warning(
6467
f"skipping test {cls.__name__!r} due to errors: "
6568
f"use `-v' for more information\n"
66-
f" FILE: {frame.filename}:{frame.lineno}"
69+
f" FILE: {filename}:{lineno}"
6770
)
6871
getlogger().verbose(traceback.format_exc())
6972

@@ -121,7 +124,20 @@ def parameterized_test(*inst):
121124
.. note::
122125
This decorator does not instantiate any test. It only registers them.
123126
The actual instantiation happens during the loading phase of the test.
127+
128+
.. deprecated:: 3.6.0
129+
130+
Please use the :func:`~reframe.core.pipeline.RegressionTest.parameter`
131+
built-in instead.
132+
124133
'''
134+
135+
warn.user_deprecation_warning(
136+
'the @parameterized_test decorator is deprecated; '
137+
'please use the parameter() built-in instead',
138+
from_version='3.6.0'
139+
)
140+
125141
def _do_register(cls):
126142
_validate_test(cls)
127143
if not cls.param_space.is_empty():

reframe/frontend/loader.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,7 @@ def load_from_module(self, module):
126126
if hasattr(module, '_get_checks'):
127127
getlogger().warning(
128128
f'{module.__file__}: _get_checks() is no more supported '
129-
f'in test files: please use @reframe.simple_test or '
130-
f'@reframe.parameterized_test decorators'
129+
f'in test files: please use @reframe.simple_test decorator'
131130
)
132131

133132
if not hasattr(module, '_rfm_gettests'):
@@ -167,42 +166,48 @@ def load_from_module(self, module):
167166
getlogger().debug(f' > Loaded {len(ret)} test(s)')
168167
return ret
169168

170-
def load_from_file(self, filename, **check_args):
169+
def load_from_file(self, filename, force=False):
171170
if not self._validate_source(filename):
172171
return []
173172

174-
return self.load_from_module(util.import_module_from_file(filename))
173+
return self.load_from_module(
174+
util.import_module_from_file(filename, force)
175+
)
175176

176-
def load_from_dir(self, dirname, recurse=False):
177+
def load_from_dir(self, dirname, recurse=False, force=False):
177178
checks = []
178179
for entry in os.scandir(dirname):
179180
if recurse and entry.is_dir():
180181
checks.extend(
181-
self.load_from_dir(entry.path, recurse)
182+
self.load_from_dir(entry.path, recurse, force)
182183
)
183184

184185
if (entry.name.startswith('.') or
185186
not entry.name.endswith('.py') or
186187
not entry.is_file()):
187188
continue
188189

189-
checks.extend(self.load_from_file(entry.path))
190+
checks += self.load_from_file(entry.path, force)
190191

191192
return checks
192193

193-
def load_all(self):
194+
def load_all(self, force=False):
194195
'''Load all checks in self._load_path.
195196
196-
If a prefix exists, it will be prepended to each path.'''
197+
If a prefix exists, it will be prepended to each path.
198+
199+
:arg force: Force reloading of test files.
200+
:returns: The list of loaded tests.
201+
'''
197202
checks = []
198203
for d in self._load_path:
199204
getlogger().debug(f'Looking for tests in {d!r}')
200205
if not os.path.exists(d):
201206
continue
202207

203208
if os.path.isdir(d):
204-
checks.extend(self.load_from_dir(d, self._recurse))
209+
checks += self.load_from_dir(d, self._recurse, force)
205210
else:
206-
checks.extend(self.load_from_file(d))
211+
checks += self.load_from_file(d, force)
207212

208213
return checks

reframe/utility/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,11 @@ def _do_import_module_from_file(filename, module_name=None):
6767
return module
6868

6969

70-
def import_module_from_file(filename):
70+
def import_module_from_file(filename, force=False):
7171
'''Import module from file.
7272
7373
:arg filename: The path to the filename of a Python module.
74+
:arg force: Force reload of module in case it is already loaded.
7475
:returns: The loaded Python module.
7576
'''
7677

@@ -94,6 +95,9 @@ def import_module_from_file(filename):
9495
if match:
9596
module_name = _get_module_name(match['rel_filename'])
9697

98+
if force:
99+
sys.modules.pop(module_name, None)
100+
97101
return importlib.import_module(module_name)
98102

99103

unittests/resources/checks_unlisted/deps_simple.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ def __init__(self):
1818
self.sanity_patterns = sn.assert_found(self.name, self.stdout)
1919

2020

21-
@rfm.parameterized_test(*([kind] for kind in ['default', 'fully',
22-
'by_part', 'by_case',
23-
'custom', 'any', 'all',
24-
'nodeps']))
21+
@rfm.simple_test
2522
class Test1(rfm.RunOnlyRegressionTest):
26-
def __init__(self, kind):
23+
kind = parameter(['default', 'fully', 'by_part', 'by_case',
24+
'custom', 'any', 'all', 'nodeps'])
25+
26+
def __init__(self):
2727
def custom_deps(src, dst):
2828
return (
2929
src[0] == 'p0' and
@@ -48,7 +48,7 @@ def custom_deps(src, dst):
4848
self.executable = 'echo'
4949
self.executable_opts = [self.name]
5050
self.sanity_patterns = sn.assert_found(self.name, self.stdout)
51-
if kind == 'default':
51+
if self.kind == 'default':
5252
self.depends_on('Test0')
5353
else:
54-
self.depends_on('Test0', kindspec[kind])
54+
self.depends_on('Test0', kindspec[self.kind])

unittests/resources/checks_unlisted/good.py

Lines changed: 0 additions & 72 deletions
This file was deleted.

unittests/test_dependencies.py

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -327,53 +327,55 @@ def __init__(self):
327327
self.executable_opts = [self.name]
328328
self.sanity_patterns = sn.assert_found(self.name, self.stdout)
329329

330-
@rfm.parameterized_test(*([kind] for kind in ['fully', 'by_case',
331-
'exact']))
332330
class Test1_deprecated(rfm.RunOnlyRegressionTest):
333-
def __init__(self, kind):
334-
kindspec = {
335-
'fully': rfm.DEPEND_FULLY,
336-
'by_case': rfm.DEPEND_BY_ENV,
337-
'exact': rfm.DEPEND_EXACT,
338-
}
331+
kind = parameter([rfm.DEPEND_FULLY,
332+
rfm.DEPEND_BY_ENV,
333+
rfm.DEPEND_EXACT])
334+
335+
def __init__(self):
339336
self.valid_systems = ['sys0:p0', 'sys0:p1']
340337
self.valid_prog_environs = ['e0', 'e1']
341338
self.executable = 'echo'
342339
self.executable_opts = [self.name]
343-
if kindspec[kind] == rfm.DEPEND_EXACT:
344-
self.depends_on('Test0', kindspec[kind],
340+
if self.kind == rfm.DEPEND_EXACT:
341+
self.depends_on('Test0', self.kind,
345342
{'e0': ['e0', 'e1'], 'e1': ['e1']})
346343
else:
347-
self.depends_on('Test0', kindspec[kind])
348-
349-
with pytest.warns(ReframeDeprecationWarning):
350-
t1 = Test1_deprecated('fully')
351-
assert(t1._userdeps == [('Test0', udeps.by_part)])
352-
353-
with pytest.warns(ReframeDeprecationWarning):
354-
t1 = Test1_deprecated('by_case')
355-
assert(t1._userdeps == [('Test0', udeps.by_case)])
356-
357-
with pytest.warns(ReframeDeprecationWarning):
358-
t1 = Test1_deprecated('exact')
359-
how = t1._userdeps[0][1]
360-
t0_cases = [(p, e) for p in ['p0', 'p1']
361-
for e in ['e0', 'e1']]
362-
t1_cases = [(p, e) for p in ['p0', 'p1']
363-
for e in ['e0', 'e1']]
364-
deps = {(t0, t1) for t0 in t0_cases
365-
for t1 in t1_cases if how(t0, t1)}
366-
assert deps == {
367-
(t0, t1) for t0 in t0_cases
368-
for t1 in t1_cases
369-
if ((t0[0] == t1[0] and t0[1] == 'e0') or
370-
(t0[0] == t1[0] and t0[1] == 'e1' and t1[1] == 'e1'))
371-
}
372-
assert len(deps) == 6
344+
self.depends_on('Test0', self.kind)
345+
346+
# We will do our assertions in a post-init hook
347+
348+
@rfm.run_after('init')
349+
def assert_deps(self):
350+
if self.kind == rfm.DEPEND_FULLY:
351+
assert self._userdeps == [('Test0', udeps.by_part)]
352+
elif self.kind == rfm.DEPEND_BY_ENV:
353+
assert self._userdeps == [('Test0', udeps.by_case)]
354+
else:
355+
how = self._userdeps[0][1]
356+
t0_cases = [(p, e) for p in ['p0', 'p1']
357+
for e in ['e0', 'e1']]
358+
t1_cases = [(p, e) for p in ['p0', 'p1']
359+
for e in ['e0', 'e1']]
360+
deps = {(t0, t1) for t0 in t0_cases
361+
for t1 in t1_cases if how(t0, t1)}
362+
assert deps == {
363+
(t0, t1) for t0 in t0_cases
364+
for t1 in t1_cases
365+
if ((t0[0] == t1[0] and t0[1] == 'e0') or
366+
(t0[0] == t1[0] and t0[1] == 'e1' and t1[1] == 'e1'))
367+
}
368+
assert len(deps) == 6
369+
370+
with pytest.warns(ReframeDeprecationWarning) as warnings:
371+
for _ in Test1_deprecated.param_space:
372+
Test1_deprecated(_rfm_use_params=True)
373+
374+
assert len(warnings) == 3
373375

374376

375377
def test_build_deps(loader, default_exec_ctx):
376-
checks = loader.load_all()
378+
checks = loader.load_all(force=True)
377379
cases = executors.generate_testcases(checks)
378380

379381
# Test calling getdep() before having built the graph

unittests/test_loader.py

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,6 @@ def test_load_all(loader_with_path):
4848
assert 12 == len(checks)
4949

5050

51-
def test_load_new_syntax(loader):
52-
checks = loader.load_from_file(
53-
'unittests/resources/checks_unlisted/good.py'
54-
)
55-
assert 13 == len(checks)
56-
57-
5851
def test_conflicted_checks(loader_with_path):
5952
loader_with_path._ignore_conflicts = False
6053
with pytest.raises(NameConflictError):

unittests/test_parameters.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,9 @@ class MyTest(ExtendParams):
164164
assert test.P2 is not None
165165

166166

167+
@pytest.mark.filterwarnings(
168+
'ignore::reframe.core.warnings.ReframeDeprecationWarning'
169+
)
167170
def test_parameterized_test_is_incompatible():
168171
with pytest.raises(ValueError):
169172
@rfm.parameterized_test(['var'])

unittests/test_pipeline.py

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -927,26 +927,6 @@ def __init__(self, a, b):
927927
assert 'test_name_compileonly_test.<locals>.MyTest_1_2' == test.name
928928

929929

930-
def test_registration_of_tests():
931-
import unittests.resources.checks_unlisted.good as mod
932-
933-
checks = mod._rfm_gettests()
934-
assert 13 == len(checks)
935-
assert [mod.MyBaseTest(0, 0),
936-
mod.MyBaseTest(0, 1),
937-
mod.MyBaseTest(1, 0),
938-
mod.MyBaseTest(1, 1),
939-
mod.MyBaseTest(2, 0),
940-
mod.MyBaseTest(2, 1),
941-
mod.AnotherBaseTest(0, 0),
942-
mod.AnotherBaseTest(0, 1),
943-
mod.AnotherBaseTest(1, 0),
944-
mod.AnotherBaseTest(1, 1),
945-
mod.AnotherBaseTest(2, 0),
946-
mod.AnotherBaseTest(2, 1),
947-
mod.MyBaseTest(10, 20)] == checks
948-
949-
950930
def test_trap_job_errors_without_sanity_patterns(local_exec_ctx):
951931
rt.runtime().site_config.add_sticky_option('general/trap_job_errors', True)
952932

0 commit comments

Comments
 (0)