1#!/usr/bin/env python3
2# Copyright (c) 2023 Intel Corporation
3#
4# SPDX-License-Identifier: Apache-2.0
5"""
6Blackbox tests for twister's command line functions
7"""
8import importlib
9import mock
10import os
11import pytest
12import sys
13
14from conftest import ZEPHYR_BASE, testsuite_filename_mock, clear_log_in_test
15from twisterlib.testplan import TestPlan
16
17sys.path.insert(0, os.path.join(ZEPHYR_BASE, "scripts/pylib/twister/twisterlib"))
18
19@mock.patch.object(TestPlan, 'TESTSUITE_FILENAME', testsuite_filename_mock)
20class TestHardwaremap:
21    TESTDATA_1 = [
22        (
23            [
24                'ARM',
25                'SEGGER',
26                'MBED'
27            ],
28            [
29                'DAPLink CMSIS-DAP',
30                'MBED CMSIS-DAP'
31            ],
32            [1234, 'abcd'],
33            'pyocd'
34        ),
35        (
36            [
37                'STMicroelectronics',
38                'Atmel Corp.'
39            ],
40            [
41                'J-Link',
42                'J-Link OB'
43            ],
44            [1234, 'abcd'],
45            'jlink'
46        ),
47        (
48            [
49                'Silicon Labs',
50                'NXP Semiconductors',
51                'Microchip Technology Inc.'
52            ],
53            [
54                'STM32 STLink',
55                '^XDS110.*',
56                'STLINK-V3'
57            ],
58            [1234, 'abcd'],
59            'openocd'
60        ),
61        (
62            [
63                'FTDI',
64                'Digilent',
65                'Microsoft'
66            ],
67            [
68                'TTL232R-3V3',
69                'MCP2200 USB Serial Port Emulator'
70            ],
71            [1234, 'abcd'],
72            'dediprog'
73        )
74    ]
75    TESTDATA_2 = [
76        (
77            'FTDI',
78            'DAPLink CMSIS-DAP',
79            1234,
80            'pyocd'
81        )
82    ]
83    TESTDATA_3 = [
84        (
85            'Texas Instruments',
86            'DAPLink CMSIS-DAP',
87            'abcd', 'las'
88        ),
89        (
90            'Texas Instruments',
91            'DAPLink CMSIS-DAP',
92            'abcd', 'dse0'
93        )
94    ]
95
96    @classmethod
97    def setup_class(cls):
98        apath = os.path.join(ZEPHYR_BASE, 'scripts', 'twister')
99        cls.loader = importlib.machinery.SourceFileLoader('__main__', apath)
100        cls.spec = importlib.util.spec_from_loader(cls.loader.name, cls.loader)
101        cls.twister_module = importlib.util.module_from_spec(cls.spec)
102
103    @classmethod
104    def teardown_class(cls):
105        pass
106
107    @pytest.mark.parametrize(
108        ('manufacturer', 'product', 'serial', 'runner'),
109        TESTDATA_1,
110    )
111    def test_generate(self, capfd, out_path, manufacturer, product, serial, runner):
112        file_name = "test-map.yaml"
113        path = os.path.join(ZEPHYR_BASE, file_name)
114        args = ['--outdir', out_path, '--generate-hardware-map', file_name]
115
116        if os.path.exists(path):
117            os.remove(path)
118
119        def mocked_comports():
120            return [
121                mock.Mock(device='/dev/ttyUSB23',
122                          manufacturer=id_man,
123                          product=id_pro,
124                          serial_number=id_serial
125                          )
126            ]
127
128        for id_man in manufacturer:
129            for id_pro in product:
130                for id_serial in serial:
131                    with mock.patch.object(sys, 'argv', [sys.argv[0]] + args), \
132                            mock.patch('serial.tools.list_ports.comports',
133                                       side_effect=mocked_comports), \
134                            pytest.raises(SystemExit) as sys_exit:
135                        self.loader.exec_module(self.twister_module)
136
137                    out, err = capfd.readouterr()
138                    sys.stdout.write(out)
139                    sys.stderr.write(err)
140
141                    assert os.path.exists(path)
142
143                    expected_data = '- connected: true\n' \
144                                    f'  id: {id_serial}\n' \
145                                    '  platform: unknown\n' \
146                                    f'  product: {id_pro}\n' \
147                                    f'  runner: {runner}\n' \
148                                    '  serial: /dev/ttyUSB23\n'
149
150                    load_data = open(path).read()
151                    assert load_data == expected_data
152
153                    if os.path.exists(path):
154                        os.remove(path)
155
156                    assert str(sys_exit.value) == '0'
157                    clear_log_in_test()
158
159    @pytest.mark.parametrize(
160        ('manufacturer', 'product', 'serial', 'runner'),
161        TESTDATA_2,
162    )
163    def test_few_generate(self, capfd, out_path, manufacturer, product, serial, runner):
164        file_name = "test-map.yaml"
165        path = os.path.join(ZEPHYR_BASE, file_name)
166        args = ['--outdir', out_path, '--generate-hardware-map', file_name]
167
168        if os.path.exists(path):
169            os.remove(path)
170
171        def mocked_comports():
172            return [
173                mock.Mock(device='/dev/ttyUSB23',
174                          manufacturer=manufacturer,
175                          product=product,
176                          serial_number=serial
177                          ),
178                mock.Mock(device='/dev/ttyUSB24',
179                          manufacturer=manufacturer,
180                          product=product,
181                          serial_number=serial + 1
182                          ),
183                mock.Mock(device='/dev/ttyUSB24',
184                          manufacturer=manufacturer,
185                          product=product,
186                          serial_number=serial + 2
187                          ),
188                mock.Mock(device='/dev/ttyUSB25',
189                          manufacturer=manufacturer,
190                          product=product,
191                          serial_number=serial + 3
192                          )
193            ]
194
195        with mock.patch.object(sys, 'argv', [sys.argv[0]] + args), \
196                mock.patch('serial.tools.list_ports.comports',
197                           side_effect=mocked_comports), \
198                pytest.raises(SystemExit) as sys_exit:
199            self.loader.exec_module(self.twister_module)
200
201        out, err = capfd.readouterr()
202        sys.stdout.write(out)
203        sys.stderr.write(err)
204
205        assert os.path.exists(path)
206
207        expected_data = '- connected: true\n' \
208                        f'  id: {serial}\n' \
209                        '  platform: unknown\n' \
210                        f'  product: {product}\n' \
211                        f'  runner: {runner}\n' \
212                        '  serial: /dev/ttyUSB23\n' \
213                        '- connected: true\n' \
214                        f'  id: {serial + 1}\n' \
215                        '  platform: unknown\n' \
216                        f'  product: {product}\n' \
217                        f'  runner: {runner}\n' \
218                        '  serial: /dev/ttyUSB24\n' \
219                        '- connected: true\n' \
220                        f'  id: {serial + 2}\n' \
221                        '  platform: unknown\n' \
222                        f'  product: {product}\n' \
223                        f'  runner: {runner}\n' \
224                        '  serial: /dev/ttyUSB24\n' \
225                        '- connected: true\n' \
226                        f'  id: {serial + 3}\n' \
227                        '  platform: unknown\n' \
228                        f'  product: {product}\n' \
229                        f'  runner: {runner}\n' \
230                        '  serial: /dev/ttyUSB25\n'
231
232        load_data = open(path).read()
233        assert load_data == expected_data
234
235        if os.path.exists(path):
236            os.remove(path)
237
238        assert str(sys_exit.value) == '0'
239
240    @pytest.mark.parametrize(
241        ('manufacturer', 'product', 'serial', 'location'),
242        TESTDATA_3,
243    )
244    def test_texas_exeption(self, capfd, out_path, manufacturer, product, serial, location):
245        file_name = "test-map.yaml"
246        path = os.path.join(ZEPHYR_BASE, file_name)
247        args = ['--outdir', out_path, '--generate-hardware-map', file_name]
248
249        if os.path.exists(path):
250            os.remove(path)
251
252        def mocked_comports():
253            return [
254                mock.Mock(device='/dev/ttyUSB23',
255                          manufacturer=manufacturer,
256                          product=product,
257                          serial_number=serial,
258                          location=location
259                          )
260            ]
261
262        with mock.patch.object(sys, 'argv', [sys.argv[0]] + args), \
263                mock.patch('serial.tools.list_ports.comports',
264                           side_effect=mocked_comports), \
265                pytest.raises(SystemExit) as sys_exit:
266            self.loader.exec_module(self.twister_module)
267
268        out, err = capfd.readouterr()
269        sys.stdout.write(out)
270        sys.stderr.write(err)
271
272        assert os.path.exists(path)
273
274        expected_data = '- connected: true\n' \
275                        f'  id: {serial}\n' \
276                        '  platform: unknown\n' \
277                        f'  product: {product}\n' \
278                        '  runner: pyocd\n' \
279                        '  serial: /dev/ttyUSB23\n'
280        expected_data2 = '[]\n'
281
282        load_data = open(path).read()
283        if location.endswith('0'):
284            assert load_data == expected_data
285        else:
286            assert load_data == expected_data2
287        if os.path.exists(path):
288            os.remove(path)
289
290        assert str(sys_exit.value) == '0'
291