Skip to content

Commit 11e656b

Browse files
nashifkartben
authored andcommitted
twister: support testing multiple toolchain variants
Added integration_toolchains to allow building/testing with multiple toolchains available in the environment. This changes the output structure and adds another level in the path under twister_out signifying the toolchain used. The toolchain used (variant) is also part of the json output now. Signed-off-by: Anas Nashif <[email protected]>
1 parent 81563c1 commit 11e656b

File tree

19 files changed

+167
-85
lines changed

19 files changed

+167
-85
lines changed

scripts/pylib/twister/twisterlib/config_parser.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class TwisterConfigParser:
6666
"vendor_exclude": {"type": "set"},
6767
"extra_sections": {"type": "list", "default": []},
6868
"integration_platforms": {"type": "list", "default": []},
69+
"integration_toolchains": {"type": "list", "default": []},
6970
"ignore_faults": {"type": "bool", "default": False },
7071
"ignore_qemu_crash": {"type": "bool", "default": False },
7172
"testcases": {"type": "list", "default": []},

scripts/pylib/twister/twisterlib/package.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@
1111

1212

1313
class Artifacts:
14-
14+
"""Package the test artifacts into a tarball."""
1515
def __init__(self, env):
1616
self.options = env.options
1717

1818
def make_tarfile(self, output_filename, source_dirs):
19+
"""Create a tarball from the test artifacts."""
1920
root = os.path.basename(self.options.outdir)
2021
with tarfile.open(output_filename, "w:bz2") as tar:
2122
tar.add(self.options.outdir, recursive=False)
@@ -24,14 +25,21 @@ def make_tarfile(self, output_filename, source_dirs):
2425
tar.add(d, arcname=os.path.join(root, f))
2526

2627
def package(self):
28+
"""Package the test artifacts into a tarball."""
2729
dirs = []
28-
with open(os.path.join(self.options.outdir, "twister.json")) as json_test_plan:
30+
with open(
31+
os.path.join(self.options.outdir, "twister.json"), encoding='utf-8'
32+
) as json_test_plan:
2933
jtp = json.load(json_test_plan)
3034
for t in jtp['testsuites']:
3135
if t['status'] != TwisterStatus.FILTER:
3236
p = t['platform']
3337
normalized = p.replace("/", "_")
34-
dirs.append(os.path.join(self.options.outdir, normalized, t['name']))
38+
dirs.append(
39+
os.path.join(
40+
self.options.outdir, normalized, t['toolchain'], t['name']
41+
)
42+
)
3543

3644
dirs.extend(
3745
[

scripts/pylib/twister/twisterlib/reports.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,8 @@ def json_report(self, filename, version="NA", platform=None, filters=None):
357357
suite["used_rom"] = used_rom
358358

359359
suite['retries'] = instance.retries
360+
if instance.toolchain:
361+
suite['toolchain'] = instance.toolchain
360362

361363
if instance.dut:
362364
suite["dut"] = instance.dut

scripts/pylib/twister/twisterlib/runner.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,9 @@ def run_cmake(self, args="", filter_stages=None):
658658
'-DCONFIG_COVERAGE=y'
659659
])
660660

661+
if self.instance.toolchain:
662+
cmake_args.append(f'-DZEPHYR_TOOLCHAIN_VARIANT={self.instance.toolchain}')
663+
661664
# If needed, run CMake using the package_helper script first, to only run
662665
# a subset of all cmake modules. This output will be used to filter
663666
# testcases, and the full CMake configuration will be run for
@@ -830,7 +833,13 @@ def parse_generated(self, filter_stages=None):
830833
and self.env.options.west_flash is None
831834
):
832835
logger.warning("Sysbuild test will be skipped. West must be used for flashing.")
833-
return {os.path.join(self.platform.name, self.testsuite.name): True}
836+
return {
837+
os.path.join(
838+
self.platform.name,
839+
self.instance.toolchain,
840+
self.testsuite.name
841+
): True
842+
}
834843

835844
if self.testsuite and self.testsuite.filter:
836845
try:
@@ -846,9 +855,21 @@ def parse_generated(self, filter_stages=None):
846855
raise se
847856

848857
if not ret:
849-
return {os.path.join(self.platform.name, self.testsuite.name): True}
858+
return {
859+
os.path.join(
860+
self.platform.name,
861+
self.instance.toolchain,
862+
self.testsuite.name
863+
): True
864+
}
850865
else:
851-
return {os.path.join(self.platform.name, self.testsuite.name): False}
866+
return {
867+
os.path.join(
868+
self.platform.name,
869+
self.instance.toolchain,
870+
self.testsuite.name
871+
): False
872+
}
852873
else:
853874
self.platform.filter_data = filter_data
854875
return filter_data
@@ -1548,6 +1569,8 @@ def report_out(self, results):
15481569
and hasattr(self.instance.handler, 'seed')
15491570
and self.instance.handler.seed is not None ):
15501571
more_info += "/seed: " + str(self.options.seed)
1572+
if instance.toolchain:
1573+
more_info += f" <{instance.toolchain}>"
15511574
logger.info(
15521575
f"{results.done - results.filtered_static:>{total_tests_width}}/{total_to_do}"
15531576
f" {instance.platform.name:<25} {instance.testsuite.name:<50}"

scripts/pylib/twister/twisterlib/testinstance.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class TestInstance:
4949

5050
__test__ = False
5151

52-
def __init__(self, testsuite, platform, outdir):
52+
def __init__(self, testsuite, platform, toolchain, outdir):
5353

5454
self.testsuite: TestSuite = testsuite
5555
self.platform: Platform = platform
@@ -63,19 +63,23 @@ def __init__(self, testsuite, platform, outdir):
6363
self.execution_time = 0
6464
self.build_time = 0
6565
self.retries = 0
66+
self.toolchain = toolchain
6667

67-
self.name = os.path.join(platform.name, testsuite.name)
68+
self.name = os.path.join(platform.name, toolchain, testsuite.name)
6869
self.dut = None
6970

7071
if testsuite.detailed_test_id:
71-
self.build_dir = os.path.join(outdir, platform.normalized_name, testsuite.name)
72+
self.build_dir = os.path.join(
73+
outdir, platform.normalized_name, self.toolchain, testsuite.name
74+
)
7275
else:
7376
# if suite is not in zephyr,
7477
# keep only the part after ".." in reconstructed dir structure
7578
source_dir_rel = testsuite.source_dir_rel.rsplit(os.pardir+os.path.sep, 1)[-1]
7679
self.build_dir = os.path.join(
7780
outdir,
7881
platform.normalized_name,
82+
self.toolchain,
7983
source_dir_rel,
8084
testsuite.name
8185
)

scripts/pylib/twister/twisterlib/testplan.py

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import collections
99
import copy
1010
import glob
11+
import itertools
1112
import json
1213
import logging
1314
import os
@@ -698,11 +699,14 @@ def load_from_file(self, file, filter_platform=None):
698699
for ts in jtp.get("testsuites", []):
699700
logger.debug(f"loading {ts['name']}...")
700701
testsuite = ts["name"]
702+
toolchain = ts["toolchain"]
701703

702704
platform = self.get_platform(ts["platform"])
703705
if filter_platform and platform.name not in filter_platform:
704706
continue
705-
instance = TestInstance(self.testsuites[testsuite], platform, self.env.outdir)
707+
instance = TestInstance(
708+
self.testsuites[testsuite], platform, toolchain, self.env.outdir
709+
)
706710
if ts.get("run_id"):
707711
instance.run_id = ts.get("run_id")
708712

@@ -777,7 +781,6 @@ def check_platform(self, platform, platform_list):
777781

778782
def apply_filters(self, **kwargs):
779783

780-
toolchain = self.env.toolchain
781784
platform_filter = self.options.platform
782785
vendor_filter = self.options.vendor
783786
exclude_platform = self.options.exclude_platform
@@ -890,8 +893,16 @@ def apply_filters(self, **kwargs):
890893
)
891894
# list of instances per testsuite, aka configurations.
892895
instance_list = []
893-
for plat in platform_scope:
894-
instance = TestInstance(ts, plat, self.env.outdir)
896+
for itoolchain, plat in itertools.product(
897+
ts.integration_toolchains or [None], platform_scope
898+
):
899+
if itoolchain:
900+
toolchain = itoolchain
901+
else:
902+
default_toolchain = "zephyr" if not self.env.toolchain else self.env.toolchain
903+
toolchain = default_toolchain if plat.arch not in ['posix', 'unit'] else "host"
904+
905+
instance = TestInstance(ts, plat, toolchain, self.env.outdir)
895906
instance.run = instance.check_runnable(
896907
self.options,
897908
self.hwm
@@ -999,9 +1010,7 @@ def apply_filters(self, **kwargs):
9991010
)
10001011

10011012
if not force_toolchain \
1002-
and toolchain and (toolchain not in plat.supported_toolchains) \
1003-
and "host" not in plat.supported_toolchains \
1004-
and ts.type != 'unit':
1013+
and toolchain and (toolchain not in plat.supported_toolchains):
10051014
instance.add_filter(
10061015
f"Not supported by the toolchain: {toolchain}",
10071016
Filters.PLATFORM

scripts/schemas/twister/testsuite-schema.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ schema;scenario-schema:
8989
required: false
9090
sequence:
9191
- type: str
92+
"integration_toolchains":
93+
type: seq
94+
required: false
95+
sequence:
96+
- type: str
9297
"ignore_faults":
9398
type: bool
9499
required: false

scripts/tests/twister/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def instances_fixture(class_testplan, platforms_list, all_testsuites_dict, tmpdi
9090
platform = class_testplan.get_platform("demo_board_2")
9191
instance_list = []
9292
for _, testcase in all_testsuites_dict.items():
93-
instance = TestInstance(testcase, platform, class_testplan.outdir)
93+
instance = TestInstance(testcase, platform, 'zephyr', class_testplan.outdir)
9494
instance_list.append(instance)
9595
class_testplan.add_instances(instance_list)
9696
return class_testplan.instances

scripts/tests/twister/pytest_integration/test_harness_pytest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def testinstance() -> TestInstance:
2222
testsuite.sysbuild = False
2323
platform = Platform()
2424

25-
testinstance = TestInstance(testsuite, platform, 'outdir')
25+
testinstance = TestInstance(testsuite, platform, 'zephyr', 'outdir')
2626
testinstance.handler = mock.Mock()
2727
testinstance.handler.options = mock.Mock()
2828
testinstance.handler.options.verbose = 1

scripts/tests/twister/test_harness.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ def test_robot_configure(tmp_path):
207207
outdir.mkdir()
208208

209209
instance = TestInstance(
210-
testsuite=mock_testsuite, platform=mock_platform, outdir=outdir
210+
testsuite=mock_testsuite, platform=mock_platform, toolchain='zephyr', outdir=outdir
211211
)
212212
instance.testsuite.harness_config = {
213213
"robot_testsuite": "/path/to/robot/test",
@@ -238,7 +238,7 @@ def test_robot_handle(tmp_path):
238238
outdir.mkdir()
239239

240240
instance = TestInstance(
241-
testsuite=mock_testsuite, platform=mock_platform, outdir=outdir
241+
testsuite=mock_testsuite, platform=mock_platform, toolchain='zephyr', outdir=outdir
242242
)
243243

244244
handler = Robot()
@@ -288,7 +288,7 @@ def test_robot_run_robot_test(tmp_path, caplog, exp_out, returncode, expected_st
288288
outdir.mkdir()
289289

290290
instance = TestInstance(
291-
testsuite=mock_testsuite, platform=mock_platform, outdir=outdir
291+
testsuite=mock_testsuite, platform=mock_platform, toolchain='zephyr', outdir=outdir
292292
)
293293
instance.build_dir = "build_dir"
294294

@@ -342,7 +342,7 @@ def test_console_configure(tmp_path, type, num_patterns):
342342
outdir.mkdir()
343343

344344
instance = TestInstance(
345-
testsuite=mock_testsuite, platform=mock_platform, outdir=outdir
345+
testsuite=mock_testsuite, platform=mock_platform, toolchain='zephyr', outdir=outdir
346346
)
347347
instance.testsuite.harness_config = {
348348
"type": type,
@@ -403,7 +403,7 @@ def test_console_handle(
403403
outdir.mkdir()
404404

405405
instance = TestInstance(
406-
testsuite=mock_testsuite, platform=mock_platform, outdir=outdir
406+
testsuite=mock_testsuite, platform=mock_platform, toolchain='zephyr', outdir=outdir
407407
)
408408

409409
console = Console()
@@ -465,7 +465,7 @@ def test_pytest__generate_parameters_for_hardware(tmp_path, pty_value, hardware_
465465
outdir.mkdir()
466466

467467
instance = TestInstance(
468-
testsuite=mock_testsuite, platform=mock_platform, outdir=outdir
468+
testsuite=mock_testsuite, platform=mock_platform, toolchain='zephyr', outdir=outdir
469469
)
470470

471471
handler = mock.Mock()
@@ -563,7 +563,7 @@ def test_pytest_run(tmp_path, caplog):
563563
outdir.mkdir()
564564

565565
instance = TestInstance(
566-
testsuite=mock_testsuite, platform=mock_platform, outdir=outdir
566+
testsuite=mock_testsuite, platform=mock_platform, toolchain='zephyr', outdir=outdir
567567
)
568568
instance.handler = handler
569569

@@ -712,7 +712,7 @@ def test_test_handle(
712712
outdir = tmp_path / "ztest_out"
713713
with mock.patch('twisterlib.testsuite.TestSuite.get_unique', return_value="dummy_suite"):
714714
instance = TestInstance(
715-
testsuite=mock_testsuite, platform=mock_platform, outdir=outdir
715+
testsuite=mock_testsuite, platform=mock_platform, toolchain='zephyr', outdir=outdir
716716
)
717717
instance.handler = mock.Mock(options=mock.Mock(verbose=0), type_str="handler_type")
718718

@@ -753,7 +753,7 @@ def gtest(tmp_path):
753753
outdir.mkdir()
754754

755755
instance = TestInstance(
756-
testsuite=mock_testsuite, platform=mock_platform, outdir=outdir
756+
testsuite=mock_testsuite, platform=mock_platform, toolchain='zephyr', outdir=outdir
757757
)
758758

759759
harness = Gtest()

0 commit comments

Comments
 (0)