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