1#!/usr/bin/env python3
2# Copyright (c) 2024 Intel Corporation
3#
4# SPDX-License-Identifier: Apache-2.0
5"""
6Blackbox tests for twister's command line functions related to Twister's tooling.
7"""
8# pylint: disable=duplicate-code
9
10import importlib
11from unittest import mock
12import os
13import pytest
14import sys
15import json
16
17# pylint: disable=no-name-in-module
18from conftest import ZEPHYR_BASE, TEST_DATA, sample_filename_mock, testsuite_filename_mock
19from twisterlib.statuses import TwisterStatus
20from twisterlib.testplan import TestPlan
21
22
23class TestTooling:
24    @classmethod
25    def setup_class(cls):
26        apath = os.path.join(ZEPHYR_BASE, 'scripts', 'twister')
27        cls.loader = importlib.machinery.SourceFileLoader('__main__', apath)
28        cls.spec = importlib.util.spec_from_loader(cls.loader.name, cls.loader)
29        cls.twister_module = importlib.util.module_from_spec(cls.spec)
30
31    @classmethod
32    def teardown_class(cls):
33        pass
34
35    @pytest.mark.parametrize(
36        'jobs',
37        ['1', '2'],
38        ids=['single job', 'two jobs']
39    )
40    @mock.patch.object(TestPlan, 'TESTSUITE_FILENAME', testsuite_filename_mock)
41    def test_jobs(self, out_path, jobs):
42        test_platforms = ['qemu_x86', 'intel_adl_crb']
43        path = os.path.join(TEST_DATA, 'tests', 'dummy', 'agnostic', 'group2')
44        args = ['-i', '--outdir', out_path, '-T', path] + \
45               ['--jobs', jobs] + \
46               [val for pair in zip(
47                   ['-p'] * len(test_platforms), test_platforms
48               ) for val in pair]
49
50        with mock.patch.object(sys, 'argv', [sys.argv[0]] + args), \
51                pytest.raises(SystemExit) as sys_exit:
52            self.loader.exec_module(self.twister_module)
53
54        with open(os.path.join(out_path, 'twister.log')) as f:
55            log = f.read()
56            assert f'JOBS: {jobs}' in log
57
58        assert str(sys_exit.value) == '0'
59
60    @mock.patch.object(TestPlan, 'SAMPLE_FILENAME', sample_filename_mock)
61    def test_force_toolchain(self, out_path):
62        # nsim_vpx5 is one of the rare platforms that do not support the zephyr toolchain
63        test_platforms = ['nsim/nsim_vpx5']
64        path = os.path.join(TEST_DATA, 'samples', 'hello_world')
65        args = ['-i', '--outdir', out_path, '-T', path, '-y'] + \
66               ['--force-toolchain'] + \
67               [] + \
68               [val for pair in zip(
69                   ['-p'] * len(test_platforms), test_platforms
70               ) for val in pair]
71
72        with mock.patch.object(sys, 'argv', [sys.argv[0]] + args), \
73                pytest.raises(SystemExit) as sys_exit:
74            self.loader.exec_module(self.twister_module)
75
76        assert str(sys_exit.value) == '0'
77
78        with open(os.path.join(out_path, 'testplan.json')) as f:
79            j = json.load(f)
80        filtered_j = [
81            (ts['platform'], ts['name'], tc['identifier'], tc['status']) \
82                for ts in j['testsuites'] \
83                for tc in ts['testcases']
84        ]
85
86        # Normally, board not supporting our toolchain would be filtered, so we check against that
87        assert len(filtered_j) == 1
88        assert filtered_j[0][3] != TwisterStatus.FILTER
89
90    @pytest.mark.parametrize(
91        'test_path, test_platforms',
92        [
93            (
94                os.path.join(TEST_DATA, 'tests', 'dummy', 'agnostic'),
95                ['qemu_x86'],
96            )
97        ],
98        ids=[
99            'ninja',
100        ]
101    )
102    @pytest.mark.parametrize(
103        'flag',
104        ['--ninja', '-N']
105    )
106    @mock.patch.object(TestPlan, 'TESTSUITE_FILENAME', testsuite_filename_mock)
107    def test_ninja(self, capfd, out_path, test_path, test_platforms, flag):
108        args = ['--outdir', out_path, '-T', test_path, flag] + \
109               [val for pair in zip(
110                   ['-p'] * len(test_platforms), test_platforms
111               ) for val in pair]
112
113        with mock.patch.object(sys, 'argv', [sys.argv[0]] + args), \
114            pytest.raises(SystemExit) as sys_exit:
115            self.loader.exec_module(self.twister_module)
116
117        out, err = capfd.readouterr()
118        sys.stdout.write(out)
119        sys.stderr.write(err)
120
121        assert str(sys_exit.value) == '0'
122