Skip to content

Commit b69a8d1

Browse files
golowanowkartben
authored andcommitted
twister: ztest: short test case names on --no-detailed-test-id
Extend `--no-detailed-test-id` command line option: in addition to its current behavior to exclude from a test Suite name its configuration path prefix, also don't prefix each Ztest Case name with its Scenario name. For example: 'kernel.common.timing' Scenario name, the same Suite name, and 'sleep.usleep' test Case (where 'sleep' is its Ztest suite name and 'usleep' is Ztest test name. This way both TestSuite and TestCase names follow the same principle having no parent object name prefix. There is no information loss in Twister reports with this naming: TestSuite is a container object for its TestCases, whereas TestSuite has its configuration path as a property. Signed-off-by: Dmitrii Golovanov <[email protected]>
1 parent a9bdaf4 commit b69a8d1

File tree

15 files changed

+396
-85
lines changed

15 files changed

+396
-85
lines changed

doc/develop/test/twister.rst

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,8 +261,7 @@ A Test Suite is a collection of Test Cases which are intended to be used to test
261261
a software program to ensure it meets certain requirements. The Test Cases in a
262262
Test Suite are either related or meant to be executed together.
263263

264-
The name of each Test Scenario needs to be unique in the context of the overall
265-
test application and has to follow basic rules:
264+
Test Scenario, Test Suite, and Test Case names must follow to these basic rules:
266265

267266
#. The format of the Test Scenario identifier shall be a string without any spaces or
268267
special characters (allowed characters: alphanumeric and [\_=]) consisting
@@ -272,7 +271,8 @@ test application and has to follow basic rules:
272271
subsection names delimited with a dot (``.``). For example, a test scenario
273272
that covers semaphores in the kernel shall start with ``kernel.semaphore``.
274273

275-
#. All Test Scenario identifiers within a ``testcase.yaml`` file need to be unique.
274+
#. All Test Scenario identifiers within a Test Configuration (``testcase.yaml`` file)
275+
need to be unique.
276276
For example a ``testcase.yaml`` file covering semaphores in the kernel can have:
277277

278278
* ``kernel.semaphore``: For general semaphore tests
@@ -295,6 +295,18 @@ test application and has to follow basic rules:
295295
Test Case name, for example: ``debug.coredump.logging_backend``.
296296

297297

298+
The ``--no-detailed-test-id`` command line option modifies the above rules in this way:
299+
300+
#. A Test Suite name has only ``<Test Scenario identifier>`` component.
301+
Its Application Project path can be found in ``twister.json`` report as ``path:`` property.
302+
303+
#. With short Test Suite names in this mode, all corresponding Test Scenario names
304+
must be unique for the Twister execution scope.
305+
306+
#. **Ztest** Test Case names have only Ztest components ``<Ztest suite name>.<Ztest test name>``.
307+
Its parent Test Suite name equals to the corresponding Test Scenario identifier.
308+
309+
298310
The following is an example test configuration with a few options that are
299311
explained in this document.
300312

doc/releases/release-notes-4.1.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,15 @@ Build system and Infrastructure
118118
them can use the :zephyr_file:`scripts/utils/twister_to_list.py` script to
119119
automatically migrate Twister configuration files.
120120

121+
* Twister
122+
123+
* Test Case names for Ztest now include Ztest suite name, so the resulting identifier has
124+
three sections and looks like: ``<test_scenario_name>.<ztest_suite_name>.<ztest_name>``.
125+
These extended identifiers are used in log output, twister.json and testplan.json,
126+
as well as for ``--sub-test`` command line parameters (:github:`80088`).
127+
* The ``--no-detailed-test-id`` command line option also shortens Ztest Test Case names excluding
128+
its Test Scenario name prefix which is the same as the parent Test Suite id (:github:`82302`).
129+
121130
Drivers and Sensors
122131
*******************
123132

scripts/pylib/twister/twisterlib/environment.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,10 @@ def add_parse_arguments(parser = None) -> argparse.ArgumentParser:
148148

149149
test_plan_report_xor.add_argument("--list-tests", action="store_true",
150150
help="""List of all sub-test functions recursively found in
151-
all --testsuite-root arguments. Note different sub-tests can share
152-
the same test scenario identifier (section.subsection)
153-
and come from different directories.
154-
The output is flattened and reports --sub-test names only,
155-
not their directories. For instance net.socket.getaddrinfo_ok
156-
and net.socket.fd_set belong to different directories.
151+
all --testsuite-root arguments. The output is flattened and reports detailed
152+
sub-test names without their directories.
153+
Note: sub-test names can share the same test scenario identifier prefix
154+
(section.subsection) even if they are from different test projects.
157155
""")
158156

159157
test_plan_report_xor.add_argument("--test-tree", action="store_true",
@@ -264,9 +262,11 @@ def add_parse_arguments(parser = None) -> argparse.ArgumentParser:
264262
functions. Sub-tests are named by:
265263
'section.subsection_in_testcase_yaml.ztest_suite.ztest_without_test_prefix'.
266264
Example_1: 'kernel.fifo.fifo_api_1cpu.fifo_loop' where 'kernel.fifo' is a test scenario
267-
name (section.subsection) and 'fifo_api_1cpu.fifo_loop' is
268-
a Ztest suite_name.test_name identificator.
265+
name (section.subsection) and 'fifo_api_1cpu.fifo_loop' is a Ztest 'suite_name.test_name'.
269266
Example_2: 'debug.coredump.logging_backend' is a standalone test scenario name.
267+
Note: This selection mechanism works only for Ztest suite and test function names in
268+
the source files which are not generated by macro-substitutions.
269+
Note: With --no-detailed-test-id use only Ztest names without scenario name.
270270
""")
271271

272272
parser.add_argument(
@@ -578,15 +578,21 @@ def add_parse_arguments(parser = None) -> argparse.ArgumentParser:
578578

579579
parser.add_argument(
580580
'--detailed-test-id', action='store_true',
581-
help="Include paths to tests' locations in tests' names. Names will follow "
582-
"PATH_TO_TEST/SCENARIO_NAME schema "
583-
"e.g. samples/hello_world/sample.basic.helloworld")
581+
help="Compose each test Suite name from its configuration path (relative to root) and "
582+
"the appropriate Scenario name using PATH_TO_TEST_CONFIG/SCENARIO_NAME schema. "
583+
"Also (for Ztest only), prefix each test Case name with its Scenario name. "
584+
"For example: 'kernel.common.timing' Scenario with test Suite name "
585+
"'tests/kernel/sleep/kernel.common.timing' and 'kernel.common.timing.sleep.usleep' "
586+
"test Case (where 'sleep' is its Ztest suite name and 'usleep' is Ztest test name.")
584587

585588
parser.add_argument(
586589
"--no-detailed-test-id", dest='detailed_test_id', action="store_false",
587-
help="Don't put paths into tests' names. "
588-
"With this arg a test name will be a scenario name "
589-
"e.g. sample.basic.helloworld.")
590+
help="Don't prefix each test Suite name with its configuration path, "
591+
"so it is the same as the appropriate Scenario name. "
592+
"Also (for Ztest only), don't prefix each Ztest Case name with its Scenario name. "
593+
"For example: 'kernel.common.timing' Scenario name, the same Suite name, "
594+
"and 'sleep.usleep' test Case (where 'sleep' is its Ztest suite name "
595+
"and 'usleep' is Ztest test name.")
590596

591597
# Include paths in names by default.
592598
parser.set_defaults(detailed_test_id=True)

scripts/pylib/twister/twisterlib/harness.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ def get_testcase(self, tc_name, phase, ts_name=None):
770770
for ts_name_ in ts_names:
771771
if self.started_suites[ts_name_]['count'] < (0 if phase == 'TS_SUM' else 1):
772772
continue
773-
tc_fq_id = f"{self.id}.{ts_name_}.{tc_name}"
773+
tc_fq_id = self.instance.compose_case_name(f"{ts_name_}.{tc_name}")
774774
if tc := self.instance.get_case_by_name(tc_fq_id):
775775
if self.trace:
776776
logger.debug(f"On {phase}: Ztest case '{tc_name}' matched to '{tc_fq_id}")
@@ -779,7 +779,7 @@ def get_testcase(self, tc_name, phase, ts_name=None):
779779
f"On {phase}: Ztest case '{tc_name}' is not known"
780780
f" in {self.started_suites} running suite(s)."
781781
)
782-
tc_id = f"{self.id}.{tc_name}"
782+
tc_id = self.instance.compose_case_name(tc_name)
783783
return self.instance.get_case_or_create(tc_id)
784784

785785
def start_suite(self, suite_name):

scripts/pylib/twister/twisterlib/runner.py

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,12 +1186,8 @@ def demangle(self, symbol_name):
11861186
return symbol_name
11871187

11881188
def determine_testcases(self, results):
1189-
yaml_testsuite_name = self.instance.testsuite.id
1190-
logger.debug(f"Determine test cases for test suite: {yaml_testsuite_name}")
1189+
logger.debug(f"Determine test cases for test suite: {self.instance.testsuite.id}")
11911190

1192-
logger.debug(
1193-
f"Test instance {self.instance.name} already has {len(self.instance.testcases)} cases."
1194-
)
11951191
new_ztest_unit_test_regex = re.compile(r"z_ztest_unit_test__([^\s]+?)__([^\s]*)")
11961192
detected_cases = []
11971193

@@ -1220,9 +1216,14 @@ def determine_testcases(self, results):
12201216
f"not present in: {self.instance.testsuite.ztest_suite_names}"
12211217
)
12221218
test_func_name = m_[2].replace("test_", "", 1)
1223-
testcase_id = f"{yaml_testsuite_name}.{new_ztest_suite}.{test_func_name}"
1219+
testcase_id = self.instance.compose_case_name(
1220+
f"{new_ztest_suite}.{test_func_name}"
1221+
)
12241222
detected_cases.append(testcase_id)
12251223

1224+
logger.debug(
1225+
f"Test instance {self.instance.name} already has {len(self.instance.testcases)} cases."
1226+
)
12261227
if detected_cases:
12271228
logger.debug(f"Detected Ztest cases: [{', '.join(detected_cases)}] in {elf_file}")
12281229
tc_keeper = {
@@ -1232,16 +1233,17 @@ def determine_testcases(self, results):
12321233
self.instance.testcases.clear()
12331234
self.instance.testsuite.testcases.clear()
12341235

1235-
# When the old regex-based test case collection is fully deprecated,
1236-
# this will be the sole place where test cases get added to the test instance.
1237-
# Then we can further include the new_ztest_suite info in the testcase_id.
1238-
12391236
for testcase_id in detected_cases:
12401237
testcase = self.instance.add_testcase(name=testcase_id)
12411238
self.instance.testsuite.add_testcase(name=testcase_id)
12421239

12431240
# Keep previous statuses and reasons
12441241
tc_info = tc_keeper.get(testcase_id, {})
1242+
if not tc_info and self.trace:
1243+
# Also happens when Ztest uses macroses, eg. DEFINE_TEST_VARIANT
1244+
logger.debug(f"Ztest case '{testcase_id}' discovered for "
1245+
f"'{self.instance.testsuite.source_dir_rel}' "
1246+
f"with {list(tc_keeper)}")
12451247
testcase.status = tc_info.get('status', TwisterStatus.NONE)
12461248
testcase.reason = tc_info.get('reason')
12471249

scripts/pylib/twister/twisterlib/testinstance.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# vim: set syntax=python ts=4 :
22
#
3-
# Copyright (c) 2018-2022 Intel Corporation
3+
# Copyright (c) 2018-2024 Intel Corporation
44
# Copyright 2022 NXP
55
# Copyright (c) 2024 Arm Limited (or its affiliates). All rights reserved.
66
#
@@ -173,6 +173,9 @@ def __setstate__(self, d):
173173
def __lt__(self, other):
174174
return self.name < other.name
175175

176+
def compose_case_name(self, tc_name) -> str:
177+
return self.testsuite.compose_case_name(tc_name)
178+
176179
def set_case_status_by_name(self, name, status, reason=None):
177180
tc = self.get_case_or_create(name)
178181
tc.status = status

scripts/pylib/twister/twisterlib/testplan.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env python3
22
# vim: set syntax=python ts=4 :
33
#
4-
# Copyright (c) 2018 Intel Corporation
4+
# Copyright (c) 2018-2024 Intel Corporation
55
# Copyright (c) 2024 Arm Limited (or its affiliates). All rights reserved.
66
#
77
# SPDX-License-Identifier: Apache-2.0
@@ -346,9 +346,13 @@ def handle_modules(self):
346346

347347
def report(self):
348348
if self.options.test_tree:
349+
if not self.options.detailed_test_id:
350+
logger.info("Test tree is always shown with detailed test-id.")
349351
self.report_test_tree()
350352
return 0
351353
elif self.options.list_tests:
354+
if not self.options.detailed_test_id:
355+
logger.info("Test list is always shown with detailed test-id.")
352356
self.report_test_list()
353357
return 0
354358
elif self.options.list_tags:
@@ -551,18 +555,18 @@ def get_tests_list(self):
551555
for _, ts in self.testsuites.items():
552556
if ts.tags.intersection(tag_filter):
553557
for case in ts.testcases:
554-
testcases.append(case.name)
558+
testcases.append(case.detailed_name)
555559
else:
556560
for _, ts in self.testsuites.items():
557561
for case in ts.testcases:
558-
testcases.append(case.name)
562+
testcases.append(case.detailed_name)
559563

560564
if exclude_tag := self.options.exclude_tag:
561565
for _, ts in self.testsuites.items():
562566
if ts.tags.intersection(exclude_tag):
563567
for case in ts.testcases:
564-
if case.name in testcases:
565-
testcases.remove(case.name)
568+
if case.detailed_name in testcases:
569+
testcases.remove(case.detailed_name)
566570
return testcases
567571

568572
def add_testsuites(self, testsuite_filter=None):

scripts/pylib/twister/twisterlib/testsuite.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,10 @@ def __init__(self, name=None, testsuite=None):
386386
self.output = ""
387387
self.freeform = False
388388

389+
@property
390+
def detailed_name(self) -> str:
391+
return TestSuite.get_case_name_(self.testsuite, self.name, detailed=True)
392+
389393
@property
390394
def status(self) -> TwisterStatus:
391395
return self._status
@@ -477,20 +481,31 @@ def load(self, data):
477481
'Harness config error: console harness defined without a configuration.'
478482
)
479483

484+
@staticmethod
485+
def get_case_name_(test_suite, tc_name, detailed=True) -> str:
486+
return f"{test_suite.id}.{tc_name}" \
487+
if test_suite and detailed and not test_suite.detailed_test_id else f"{tc_name}"
488+
489+
@staticmethod
490+
def compose_case_name_(test_suite, tc_name) -> str:
491+
return f"{test_suite.id}.{tc_name}" \
492+
if test_suite and test_suite.detailed_test_id else f"{tc_name}"
493+
494+
def compose_case_name(self, tc_name) -> str:
495+
return self.compose_case_name_(self, tc_name)
496+
480497
def add_subcases(self, data, parsed_subcases=None, suite_names=None):
481498
testcases = data.get("testcases", [])
482499
if testcases:
483500
for tc in testcases:
484-
self.add_testcase(name=f"{self.id}.{tc}")
501+
self.add_testcase(name=self.compose_case_name(tc))
485502
else:
486503
if not parsed_subcases:
487504
self.add_testcase(self.id, freeform=True)
488505
else:
489506
# only add each testcase once
490-
for sub in set(parsed_subcases):
491-
name = f"{self.id}.{sub}"
492-
self.add_testcase(name)
493-
507+
for tc in set(parsed_subcases):
508+
self.add_testcase(name=self.compose_case_name(tc))
494509
if suite_names:
495510
self.ztest_suite_names = suite_names
496511

0 commit comments

Comments
 (0)