33#
44# SPDX-License-Identifier: BSD-3-Clause
55
6+ from enum import IntEnum
67
78import reframe .core .builtins as builtins
89import reframe .core .runtime as runtime
910import reframe .utility as util
1011
1112from reframe .core .decorators import TestRegistry
13+ from reframe .core .exceptions import ReframeError
1214from reframe .core .fields import make_convertible
1315from reframe .core .logging import getlogger , time_function
1416from reframe .core .meta import make_test
@@ -42,33 +44,76 @@ def getallnodes(state, jobs_cli_options=None):
4244 return nodes
4345
4446
45- def _generate_tests (testcases , gen_fn ):
47+ class _GenKind (IntEnum ):
48+ BY_CHECK = 1
49+ BY_PARTITION = 2
50+
51+
52+ def _generate_tests (testcases , gen_fn , kind : _GenKind ):
4653 tmp_registry = TestRegistry ()
4754
55+ def _testcase_key (tc ):
56+ check , partition , _ = tc
57+ if kind == _GenKind .BY_CHECK :
58+ return check .unique_name
59+ elif kind == _GenKind .BY_PARTITION :
60+ return (check .unique_name , partition .fullname )
61+
62+ assert False , '[BUG] unknown _GenKind'
63+
64+ def _variant_key (cls , partition ):
65+ if kind == _GenKind .BY_CHECK :
66+ return cls .__name__
67+ if kind == _GenKind .BY_PARTITION :
68+ return (cls .__name__ , partition )
69+
70+ assert False , '[BUG] unknown _GenKind'
71+
72+ def _remove_params (params , variant_info ):
73+ for p in params :
74+ variant_info ['params' ].pop (p , None )
75+
4876 # We don't want to register the same check for every environment
4977 # per partition
50- check_part_combs = set ()
78+ known_testcases = set ()
79+ registered_variants = {}
5180 for tc in testcases :
5281 check , partition , _ = tc
53- candidate_comb = ( check . unique_name , partition . fullname )
54- if check .is_fixture () or candidate_comb in check_part_combs :
82+ tc_key = _testcase_key ( tc )
83+ if check .is_fixture () or tc_key in known_testcases :
5584 continue
5685
57- check_part_combs .add (candidate_comb )
86+ known_testcases .add (tc_key )
87+
88+ # We want to instantiate only the variants of the original test cases.
89+ # For this reason, we store the original test case variant info and
90+ # compare it with the newly generated one after having removed its new
91+ # parameters. In case of reparameterizations, we remove the redefined
92+ # parameter also from the original test case info. We then instantiate
93+ # only the test cases that have a matching variant info. This
94+ # technique essentially re-applies any parameter filtering that has
95+ # happened previously in the CLI, e.g., with `-n MyTest%param=3`
5896 cls = type (check )
59- variant_info = cls .get_variant_info (
60- check .variant_num , recurse = True
61- )
97+ tc_info = cls .get_variant_info (check .variant_num , recurse = True )
6298 nc , params = gen_fn (tc )
99+
100+ # Remove any redefined parameters
101+ _remove_params (params , tc_info )
102+
103+ nc_key = _variant_key (nc , partition .fullname )
104+ registered_variants .setdefault (nc_key , set ())
63105 nc ._rfm_custom_prefix = check .prefix
64106 for i in range (nc .num_variants ):
65- # Check if this variant should be instantiated
66- vinfo = nc .get_variant_info (i , recurse = True )
67- for p in params :
68- vinfo ['params' ].pop (p )
69-
70- if vinfo == variant_info :
107+ nc_info = nc .get_variant_info (i , recurse = True )
108+
109+ # Remove all the parameters that we have (re)defined and compare
110+ # it with the original info; we will only instantiate test that
111+ # have a matching remaining info and that we have not already
112+ # registered.
113+ _remove_params (params , nc_info )
114+ if nc_info == tc_info and i not in registered_variants [nc_key ]:
71115 tmp_registry .add (nc , variant_num = i )
116+ registered_variants [nc_key ].add (i )
72117
73118 new_checks = tmp_registry .instantiate_all ()
74119 return generate_testcases (new_checks )
@@ -115,7 +160,7 @@ def _rfm_set_valid_systems(obj):
115160 ]
116161 ), ['.part' , '.nid' ]
117162
118- return _generate_tests (testcases , _make_dist_test )
163+ return _generate_tests (testcases , _make_dist_test , _GenKind . BY_PARTITION )
119164
120165
121166@time_function
@@ -132,7 +177,7 @@ def _make_repeat_test(testcase):
132177 }
133178 ), ['.repeat_no' ]
134179
135- return _generate_tests (testcases , _make_repeat_test )
180+ return _generate_tests (testcases , _make_repeat_test , _GenKind . BY_CHECK )
136181
137182
138183@time_function
@@ -145,6 +190,7 @@ def _make_parameterized_test(testcase):
145190
146191 # Check that all the requested variables exist
147192 body = {}
193+ methods = []
148194 for var , values in paramvars .items ():
149195 var_parts = var .split ('.' )
150196 if len (var_parts ) == 1 :
@@ -157,24 +203,37 @@ def _make_parameterized_test(testcase):
157203 getlogger ().warning ('cannot set a variable in a fixture' )
158204 continue
159205
160- if var not in cls .var_space :
206+ if var not in cls .var_space and var not in cls . param_space :
161207 getlogger ().warning (
162- f'variable { var !r} not defined for test '
208+ f'{ var !r} is neither a variable nor a parameter of test '
163209 f'{ check .display_name !r} ; ignoring parameterization'
164210 )
165211 continue
166212
167- body [f'.{ var } ' ] = builtins .parameter (values , loggable = False )
168-
169- def _set_vars (self ):
170- for var in body .keys ():
171- setattr (self , var [1 :],
172- make_convertible (getattr (self , f'{ var } ' )))
213+ if var in cls .var_space :
214+ body [f'.{ var } ' ] = builtins .parameter (values , loggable = False )
215+ def _set_vars (self ):
216+ for var in body .keys ():
217+ setattr (self , var [1 :],
218+ make_convertible (getattr (self , f'{ var } ' )))
219+
220+ methods = [builtins .run_after ('init' )(_set_vars )]
221+ elif var in cls .param_space :
222+ p = cls .param_space .params [var ]
223+ if p .type is None :
224+ raise ReframeError (
225+ f'cannot parameterize test { cls .__name__ !r} : '
226+ f'no type information associated with parameter { var !r} : ' # noqa: E501
227+ 'consider defining the parameter as follows:\n '
228+ f' { var } = parameter({ list (p .values )} , type=<type>, ' # noqa: E501
229+ f'loggable={ p .is_loggable ()} )'
230+ )
231+
232+ body [var ] = builtins .parameter ([p .type (v ) for v in values ], type = p .type )
233+ else :
234+ assert 0 , f'[BUG] { var } cannot be defined as both a variable and a parameter'
173235
174- return make_test (
175- cls .__name__ , (cls ,),
176- body = body ,
177- methods = [builtins .run_after ('init' )(_set_vars )]
178- ), body .keys ()
236+ return (make_test (cls .__name__ , (cls ,), body = body , methods = methods ),
237+ body .keys ())
179238
180- return _generate_tests (testcases , _make_parameterized_test )
239+ return _generate_tests (testcases , _make_parameterized_test , _GenKind . BY_CHECK )
0 commit comments