Skip to content

Commit 4d82c48

Browse files
nashifdkalowsk
authored andcommitted
twister: make test configuration handling a class
Split test configuration code into own class and do parsing in two steps to get the right context of platforms and scenarios. Fixes #89774 Signed-off-by: Anas Nashif <anas.nashif@intel.com>
1 parent 388f767 commit 4d82c48

File tree

1 file changed

+79
-59
lines changed

1 file changed

+79
-59
lines changed

scripts/pylib/twister/twisterlib/testplan.py

Lines changed: 79 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,78 @@ class Filters:
6969
MODULE = 'Module filter'
7070
# in case of missing env. variable required for a platform
7171
ENVIRONMENT = 'Environment filter'
72-
73-
7472
class TestLevel:
7573
name = None
7674
levels = []
7775
scenarios = []
76+
class TestConfiguration:
77+
tc_schema_path = os.path.join(
78+
ZEPHYR_BASE,
79+
"scripts",
80+
"schemas",
81+
"twister",
82+
"test-config-schema.yaml"
83+
)
84+
85+
def __init__(self, config_file):
86+
self.test_config = None
87+
self.override_default_platforms = False
88+
self.increased_platform_scope = True
89+
self.default_platforms = []
90+
self.parse(config_file)
91+
92+
def parse(self, config_file):
93+
if os.path.exists(config_file):
94+
tc_schema = scl.yaml_load(self.tc_schema_path)
95+
self.test_config = scl.yaml_load_verify(config_file, tc_schema)
96+
else:
97+
raise TwisterRuntimeError(f"File {config_file} not found.")
98+
99+
platform_config = self.test_config.get('platforms', {})
100+
101+
self.override_default_platforms = platform_config.get('override_default_platforms', False)
102+
self.increased_platform_scope = platform_config.get('increased_platform_scope', True)
103+
self.default_platforms = platform_config.get('default_platforms', [])
104+
105+
self.options = self.test_config.get('options', {})
78106

79107

108+
@staticmethod
109+
def get_level(levels, name):
110+
level = next((lvl for lvl in levels if lvl.name == name), None)
111+
return level
112+
113+
def get_levels(self, scenarios):
114+
levels = []
115+
configured_levels = self.test_config.get('levels', [])
116+
117+
# Do first pass on levels to get initial data.
118+
for level in configured_levels:
119+
adds = []
120+
for s in level.get('adds', []):
121+
r = re.compile(s)
122+
adds.extend(list(filter(r.fullmatch, scenarios)))
123+
124+
test_level = TestLevel()
125+
test_level.name = level['name']
126+
test_level.scenarios = adds
127+
test_level.levels = level.get('inherits', [])
128+
levels.append(test_level)
129+
130+
# Go over levels again to resolve inheritance.
131+
for level in configured_levels:
132+
inherit = level.get('inherits', [])
133+
_level = self.get_level(levels, level['name'])
134+
if inherit:
135+
for inherted_level in inherit:
136+
_inherited = self.get_level(levels, inherted_level)
137+
assert _inherited, "Unknown inherited level {inherted_level}"
138+
_inherited_scenarios = _inherited.scenarios
139+
level_scenarios = _level.scenarios if _level else []
140+
level_scenarios.extend(_inherited_scenarios)
141+
142+
return levels
143+
80144
class TestPlan:
81145
__test__ = False # for pytest to skip this class when collects tests
82146
config_re = re.compile('(CONFIG_[A-Za-z0-9_]+)[=]\"?([^\"]*)\"?$')
@@ -89,14 +153,6 @@ class TestPlan:
89153
os.path.join(ZEPHYR_BASE,
90154
"scripts", "schemas", "twister", "quarantine-schema.yaml"))
91155

92-
tc_schema_path = os.path.join(
93-
ZEPHYR_BASE,
94-
"scripts",
95-
"schemas",
96-
"twister",
97-
"test-config-schema.yaml"
98-
)
99-
100156
SAMPLE_FILENAME = 'sample.yaml'
101157
TESTSUITE_FILENAME = 'testcase.yaml'
102158

@@ -126,48 +182,10 @@ def __init__(self, env: Namespace):
126182

127183
self.run_individual_testsuite = []
128184
self.levels = []
129-
self.test_config = {}
185+
self.test_config = None
130186

131187
self.name = "unnamed"
132188

133-
def get_level(self, name):
134-
level = next((lvl for lvl in self.levels if lvl.name == name), None)
135-
return level
136-
137-
def parse_configuration(self, config_file):
138-
if os.path.exists(config_file):
139-
tc_schema = scl.yaml_load(self.tc_schema_path)
140-
self.test_config = scl.yaml_load_verify(config_file, tc_schema)
141-
else:
142-
raise TwisterRuntimeError(f"File {config_file} not found.")
143-
144-
levels = self.test_config.get('levels', [])
145-
146-
# Do first pass on levels to get initial data.
147-
for level in levels:
148-
adds = []
149-
for s in level.get('adds', []):
150-
r = re.compile(s)
151-
adds.extend(list(filter(r.fullmatch, self.scenarios)))
152-
153-
tl = TestLevel()
154-
tl.name = level['name']
155-
tl.scenarios = adds
156-
tl.levels = level.get('inherits', [])
157-
self.levels.append(tl)
158-
159-
# Go over levels again to resolve inheritance.
160-
for level in levels:
161-
inherit = level.get('inherits', [])
162-
_level = self.get_level(level['name'])
163-
if inherit:
164-
for inherted_level in inherit:
165-
_inherited = self.get_level(inherted_level)
166-
assert _inherited, "Unknown inherited level {inherted_level}"
167-
_inherited_scenarios = _inherited.scenarios
168-
level_scenarios = _level.scenarios if _level else []
169-
level_scenarios.extend(_inherited_scenarios)
170-
171189
def find_subtests(self):
172190
sub_tests = self.options.sub_test
173191
if sub_tests:
@@ -188,6 +206,8 @@ def discover(self):
188206
if self.options.test:
189207
self.run_individual_testsuite = self.options.test
190208

209+
self.test_config = TestConfiguration(self.env.test_config)
210+
191211
self.add_configurations()
192212
num = self.add_testsuites(testsuite_filter=self.run_individual_testsuite)
193213
if num == 0:
@@ -203,7 +223,7 @@ def discover(self):
203223
self.scenarios.append(ts.id)
204224

205225
self.report_duplicates()
206-
self.parse_configuration(config_file=self.env.test_config)
226+
self.levels = self.test_config.get_levels(self.scenarios)
207227

208228
# handle quarantine
209229
ql = self.options.quarantine_list
@@ -220,6 +240,10 @@ def discover(self):
220240
logger.debug(f'Quarantine file {quarantine_file} is empty')
221241
self.quarantine = Quarantine(ql)
222242

243+
def get_level(self, name):
244+
level = next((lvl for lvl in self.levels if lvl.name == name), None)
245+
return level
246+
223247
def load(self):
224248

225249
if self.options.report_suffix:
@@ -440,19 +464,16 @@ def add_configurations(self):
440464
soc_roots = self.env.soc_roots
441465
arch_roots = self.env.arch_roots
442466

443-
platform_config = self.test_config.get('platforms', {})
444-
445467
for platform in generate_platforms(board_roots, soc_roots, arch_roots):
446468
if not platform.twister:
447469
continue
448470
self.platforms.append(platform)
449471

450-
if not platform_config.get('override_default_platforms', False):
472+
if not self.test_config.override_default_platforms:
451473
if platform.default:
452474
self.default_platforms.append(platform.name)
453-
#logger.debug(f"adding {platform.name} to default platforms")
454475
continue
455-
for pp in platform_config.get('default_platforms', []):
476+
for pp in self.test_config.default_platforms:
456477
if pp in platform.aliases:
457478
logger.debug(f"adding {platform.name} to default platforms (override mode)")
458479
self.default_platforms.append(platform.name)
@@ -767,9 +788,8 @@ def apply_filters(self, **kwargs):
767788
else:
768789
platforms = self.platforms
769790

770-
platform_config = self.test_config.get('platforms', {})
771791
# test configuration options
772-
test_config_options = self.test_config.get('options', {})
792+
test_config_options = self.test_config.options
773793
integration_mode_list = test_config_options.get('integration_mode', [])
774794

775795
logger.info("Building initial testsuite list...")
@@ -784,7 +804,7 @@ def apply_filters(self, **kwargs):
784804
_integration_platforms = []
785805

786806
if (ts.build_on_all and not platform_filter and
787-
platform_config.get('increased_platform_scope', True)):
807+
self.test_config.increased_platform_scope):
788808
# if build_on_all is set, we build on all platforms
789809
platform_scope = self.platforms
790810
elif ts.integration_platforms and self.options.integration:
@@ -808,7 +828,7 @@ def apply_filters(self, **kwargs):
808828
ts.platform_allow
809829
and not platform_filter
810830
and not integration
811-
and platform_config.get('increased_platform_scope', True)
831+
and self.test_config.increased_platform_scope
812832
):
813833
a = set(platform_scope)
814834
b = set(filter(lambda item: item.name in ts.platform_allow, self.platforms))

0 commit comments

Comments
 (0)