zephyr/scripts/tests/twister/test_hardwaremap.py
Lukasz Mrugala 670b917b4b scripts: tests: twister: Hardware map unit testing
This change creates unit tests for the hardwaremap.py module.
It achieves 98% coverage.

Signed-off-by: Lukasz Mrugala <lukaszx.mrugala@intel.com>
2023-11-06 10:08:44 +01:00

723 lines
19 KiB
Python

#!/usr/bin/env python3
# Copyright (c) 2023 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
"""
Tests for hardwaremap.py classes' methods
"""
import mock
import pytest
import sys
from pathlib import Path
from twisterlib.hardwaremap import(
DUT,
HardwareMap
)
@pytest.fixture
def mocked_hm():
duts = [
DUT(platform='p1', id=1, serial='s1', product='pr1', connected=True),
DUT(platform='p2', id=2, serial='s2', product='pr2', connected=False),
DUT(platform='p3', id=3, serial='s3', product='pr3', connected=True),
DUT(platform='p4', id=4, serial='s4', product='pr4', connected=False),
DUT(platform='p5', id=5, serial='s5', product='pr5', connected=True),
DUT(platform='p6', id=6, serial='s6', product='pr6', connected=False),
DUT(platform='p7', id=7, serial='s7', product='pr7', connected=True),
DUT(platform='p8', id=8, serial='s8', product='pr8', connected=False)
]
hm = HardwareMap(env=mock.Mock())
hm.duts = duts
hm.detected = duts[:5]
return hm
TESTDATA_1 = [
(
{},
{'baud': 115200, 'lock': mock.ANY, 'flash_timeout': 60},
'<None (None) on None>'
),
(
{
'id': 'dummy id',
'serial': 'dummy serial',
'serial_baud': 4400,
'platform': 'dummy platform',
'product': 'dummy product',
'serial_pty': 'dummy serial pty',
'connected': True,
'runner_params': ['dummy', 'runner', 'params'],
'pre_script': 'dummy pre script',
'post_script': 'dummy post script',
'post_flash_script': 'dummy post flash script',
'runner': 'dummy runner',
'flash_timeout': 30,
'flash_with_test': True
},
{
'lock': mock.ANY,
'id': 'dummy id',
'serial': 'dummy serial',
'baud': 4400,
'platform': 'dummy platform',
'product': 'dummy product',
'serial_pty': 'dummy serial pty',
'connected': True,
'runner_params': ['dummy', 'runner', 'params'],
'pre_script': 'dummy pre script',
'post_script': 'dummy post script',
'post_flash_script': 'dummy post flash script',
'runner': 'dummy runner',
'flash_timeout': 30,
'flash_with_test': True
},
'<dummy platform (dummy product) on dummy serial>'
),
]
@pytest.mark.parametrize(
'kwargs, expected_dict, expected_repr',
TESTDATA_1,
ids=['no information', 'full information']
)
def test_dut(kwargs, expected_dict, expected_repr):
d = DUT(**kwargs)
assert d.available
assert d.counter == 0
d.available = False
d.counter = 1
assert not d.available
assert d.counter == 1
assert d.to_dict() == expected_dict
assert d.__repr__() == expected_repr
TESTDATA_2 = [
('ghm.yaml', mock.ANY, mock.ANY, [], mock.ANY, mock.ANY, mock.ANY, 0,
True, True, False, False, False, False, []),
(None, False, 'hm.yaml', [], mock.ANY, mock.ANY, mock.ANY, 0,
False, False, True, True, False, False, []),
(None, True, 'hm.yaml', [], mock.ANY, mock.ANY, ['fix'], 1,
False, False, True, False, False, True, ['p1', 'p3', 'p5', 'p7']),
(None, True, 'hm.yaml', ['pX'], mock.ANY, mock.ANY, ['fix'], 1,
False, False, True, False, False, True, ['pX']),
(None, True, None, ['p'], 's', None, ['fix'], 1,
False, False, False, False, True, True, ['p']),
(None, True, None, ['p'], None, 'spty', ['fix'], 1,
False, False, False, False, True, True, ['p']),
]
@pytest.mark.parametrize(
'generate_hardware_map, device_testing, hardware_map, platform,' \
' device_serial, device_serial_pty, fixtures,' \
' return_code, expect_scan, expect_save, expect_load,' \
' expect_dump, expect_add_device, expect_fixtures, expected_platforms',
TESTDATA_2,
ids=['generate hardware map', 'existing hardware map',
'device testing with hardware map, no platform',
'device testing with hardware map with platform',
'device testing with device serial',
'device testing with device serial pty']
)
def test_hardwaremap_discover(
caplog,
mocked_hm,
generate_hardware_map,
device_testing,
hardware_map,
platform,
device_serial,
device_serial_pty,
fixtures,
return_code,
expect_scan,
expect_save,
expect_load,
expect_dump,
expect_add_device,
expect_fixtures,
expected_platforms
):
def mock_load(*args):
mocked_hm.platform = platform
mocked_hm.scan = mock.Mock()
mocked_hm.save = mock.Mock()
mocked_hm.load = mock.Mock(side_effect=mock_load)
mocked_hm.dump = mock.Mock()
mocked_hm.add_device = mock.Mock()
mocked_hm.options.device_flash_with_test = True
mocked_hm.options.device_flash_timeout = 15
mocked_hm.options.pre_script = 'dummy pre script'
mocked_hm.options.platform = platform
mocked_hm.options.device_serial = device_serial
mocked_hm.options.device_serial_pty = device_serial_pty
mocked_hm.options.device_testing = device_testing
mocked_hm.options.hardware_map = hardware_map
mocked_hm.options.persistent_hardware_map = mock.Mock()
mocked_hm.options.generate_hardware_map = generate_hardware_map
mocked_hm.options.fixture = fixtures
returncode = mocked_hm.discover()
assert returncode == return_code
if expect_scan:
mocked_hm.scan.assert_called_once_with(
persistent=mocked_hm.options.persistent_hardware_map
)
if expect_save:
mocked_hm.save.assert_called_once_with(
mocked_hm.options.generate_hardware_map
)
if expect_load:
mocked_hm.load.assert_called_once_with(
mocked_hm.options.hardware_map
)
if expect_dump:
mocked_hm.dump.assert_called_once_with(
connected_only=True
)
if expect_add_device:
mocked_hm.add_device.assert_called_once()
if expect_fixtures:
assert all(
[all(
[fixture in dut.fixtures for fixture in fixtures]
) for dut in mocked_hm.duts]
)
assert sorted(expected_platforms) == sorted(mocked_hm.options.platform)
def test_hardwaremap_summary(capfd, mocked_hm):
selected_platforms = ['p0', 'p1', 'p6', 'p7']
mocked_hm.summary(selected_platforms)
expected = """
Hardware distribution summary:
| Board | ID | Counter |
|---------|------|-----------|
| p1 | 1 | 0 |
| p7 | 7 | 0 |
"""
out, err = capfd.readouterr()
sys.stdout.write(out)
sys.stderr.write(err)
assert expected in out
TESTDATA_3 = [
(True),
(False)
]
@pytest.mark.parametrize(
'is_pty',
TESTDATA_3,
ids=['pty', 'not pty']
)
def test_hardwaremap_add_device(is_pty):
hm = HardwareMap(env=mock.Mock())
serial = 'dummy'
platform = 'p0'
pre_script = 'dummy pre script'
hm.add_device(serial, platform, pre_script, is_pty)
assert len(hm.duts) == 1
if is_pty:
assert hm.duts[0].serial_pty == 'dummy' if is_pty else None
assert hm.duts[0].serial is None
else:
assert hm.duts[0].serial_pty is None
assert hm.duts[0].serial == 'dummy'
def test_hardwaremap_load():
map_file = \
"""
- id: id0
platform: p0
product: pr0
runner: r0
flash_with_test: True
flash_timeout: 15
baud: 14400
fixtures:
- dummy fixture 1
- dummy fixture 2
connected: True
serial: 'dummy'
- id: id1
platform: p1
product: pr1
runner: r1
connected: True
serial_pty: 'dummy'
- id: id2
platform: p2
product: pr2
runner: r2
connected: True
"""
map_filename = 'map-file.yaml'
builtin_open = open
def mock_open(*args, **kwargs):
if args[0] == map_filename:
return mock.mock_open(read_data=map_file)(*args, **kwargs)
return builtin_open(*args, **kwargs)
hm = HardwareMap(env=mock.Mock())
hm.options.device_flash_timeout = 30
hm.options.device_flash_with_test = False
with mock.patch('builtins.open', mock_open):
hm.load(map_filename)
expected = {
'id0': {
'platform': 'p0',
'product': 'pr0',
'runner': 'r0',
'flash_timeout': 15,
'flash_with_test': True,
'baud': 14400,
'fixtures': ['dummy fixture 1', 'dummy fixture 2'],
'connected': True,
'serial': 'dummy',
'serial_pty': None,
},
'id1': {
'platform': 'p1',
'product': 'pr1',
'runner': 'r1',
'flash_timeout': 30,
'flash_with_test': False,
'baud': 115200,
'fixtures': [],
'connected': True,
'serial': None,
'serial_pty': 'dummy',
},
}
for dut in hm.duts:
assert dut.id in expected
assert all([getattr(dut, k) == v for k, v in expected[dut.id].items()])
TESTDATA_4 = [
(
True,
'Linux',
['<p1 (pr1) on s1>', '<p2 (pr2) on s2>', '<p3 (pr3) on s3>',
'<p4 (pr4) on s4>', '<p5 (pr5) on s5>',
'<unknown (TI product) on /dev/serial/by-id/basic-file1>',
'<unknown (product123) on dummy device>',
'<unknown (unknown) on /dev/serial/by-id/basic-file2-link>']
),
(
True,
'nt',
['<p1 (pr1) on s1>', '<p2 (pr2) on s2>', '<p3 (pr3) on s3>',
'<p4 (pr4) on s4>', '<p5 (pr5) on s5>',
'<unknown (TI product) on /dev/serial/by-id/basic-file1>',
'<unknown (product123) on dummy device>',
'<unknown (unknown) on /dev/serial/by-id/basic-file2>']
),
(
False,
'Linux',
['<p1 (pr1) on s1>', '<p2 (pr2) on s2>', '<p3 (pr3) on s3>',
'<p4 (pr4) on s4>', '<p5 (pr5) on s5>',
'<unknown (TI product) on /dev/serial/by-id/basic-file1>',
'<unknown (product123) on dummy device>',
'<unknown (unknown) on /dev/serial/by-id/basic-file2>']
)
]
@pytest.mark.parametrize(
'persistent, system, expected_reprs',
TESTDATA_4,
ids=['linux persistent map', 'no map (not linux)', 'no map (nonpersistent)']
)
def test_hardwaremap_scan(
caplog,
mocked_hm,
persistent,
system,
expected_reprs
):
def mock_resolve(path):
if str(path).endswith('-link'):
return Path(str(path)[:-5])
return path
def mock_iterdir(path):
return [
Path(path / 'basic-file1'),
Path(path / 'basic-file2-link')
]
mocked_hm.manufacturer = ['dummy manufacturer', 'Texas Instruments']
mocked_hm.runner_mapping = {
'dummy runner': ['product[0-9]+',],
'other runner': ['other TI product', 'TI product']
}
comports_mock = [
mock.Mock(
manufacturer='wrong manufacturer',
location='wrong location',
serial_number='wrong number',
product='wrong product',
device='wrong device'
),
mock.Mock(
manufacturer='dummy manufacturer',
location='dummy location',
serial_number='dummy number',
product=None,
device='/dev/serial/by-id/basic-file2'
),
mock.Mock(
manufacturer='dummy manufacturer',
location='dummy location',
serial_number='dummy number',
product='product123',
device='dummy device'
),
mock.Mock(
manufacturer='Texas Instruments',
location='serial1',
serial_number='TI1',
product='TI product',
device='TI device1'
),
mock.Mock(
manufacturer='Texas Instruments',
location='serial0',
serial_number='TI0',
product='TI product',
device='/dev/serial/by-id/basic-file1'
),
]
with mock.patch('platform.system', return_value=system), \
mock.patch('serial.tools.list_ports.comports',
return_value=comports_mock), \
mock.patch('twisterlib.hardwaremap.Path.resolve',
autospec=True, side_effect=mock_resolve), \
mock.patch('twisterlib.hardwaremap.Path.iterdir',
autospec=True, side_effect=mock_iterdir):
mocked_hm.scan(persistent)
assert sorted([d.__repr__() for d in mocked_hm.detected]) == \
sorted(expected_reprs)
assert 'Scanning connected hardware...' in caplog.text
assert 'Unsupported device (wrong manufacturer): %s' % comports_mock[0] \
in caplog.text
TESTDATA_5 = [
(
None,
[{
'platform': 'p1',
'id': 1,
'runner': mock.ANY,
'serial': 's1',
'product': 'pr1',
'connected': True
},
{
'platform': 'p2',
'id': 2,
'runner': mock.ANY,
'serial': 's2',
'product': 'pr2',
'connected': False
},
{
'platform': 'p3',
'id': 3,
'runner': mock.ANY,
'serial': 's3',
'product': 'pr3',
'connected': True
},
{
'platform': 'p4',
'id': 4,
'runner': mock.ANY,
'serial': 's4',
'product': 'pr4',
'connected': False
},
{
'platform': 'p5',
'id': 5,
'runner': mock.ANY,
'serial': 's5',
'product': 'pr5',
'connected': True
}]
),
(
'',
[{
'serial': 's1',
'baud': 115200,
'platform': 'p1',
'connected': True,
'id': 1,
'product': 'pr1',
'lock': mock.ANY,
'flash_timeout': 60
},
{
'serial': 's2',
'baud': 115200,
'platform': 'p2',
'id': 2,
'product': 'pr2',
'lock': mock.ANY,
'flash_timeout': 60
},
{
'serial': 's3',
'baud': 115200,
'platform': 'p3',
'connected': True,
'id': 3,
'product': 'pr3',
'lock': mock.ANY,
'flash_timeout': 60
},
{
'serial': 's4',
'baud': 115200,
'platform': 'p4',
'id': 4,
'product': 'pr4',
'lock': mock.ANY,
'flash_timeout': 60
},
{
'serial': 's5',
'baud': 115200,
'platform': 'p5',
'connected': True,
'id': 5,
'product': 'pr5',
'lock': mock.ANY,
'flash_timeout': 60
}]
),
(
"""
- id: 4
platform: p4
product: pr4
connected: True
serial: s4
- id: 0
platform: p0
product: pr0
connected: True
serial: s0
- id: 10
platform: p10
product: pr10
connected: False
serial: s10
- id: 5
platform: p5-5
product: pr5-5
connected: True
serial: s5-5
""",
[{
'id': 0,
'platform': 'p0',
'product': 'pr0',
'connected': False,
'serial': None
},
{
'id': 4,
'platform': 'p4',
'product': 'pr4',
'connected': True,
'serial': 's4'
},
{
'id': 5,
'platform': 'p5-5',
'product': 'pr5-5',
'connected': False,
'serial': None
},
{
'id': 10,
'platform': 'p10',
'product': 'pr10',
'connected': False,
'serial': None
},
{
'serial': 's1',
'baud': 115200,
'platform': 'p1',
'connected': True,
'id': 1,
'product': 'pr1',
'lock': mock.ANY,
'flash_timeout': 60
},
{
'serial': 's2',
'baud': 115200,
'platform': 'p2',
'id': 2,
'product': 'pr2',
'lock': mock.ANY,
'flash_timeout': 60
},
{
'serial': 's3',
'baud': 115200,
'platform': 'p3',
'connected': True,
'id': 3,
'product': 'pr3',
'lock': mock.ANY,
'flash_timeout': 60
},
{
'serial': 's5',
'baud': 115200,
'platform': 'p5',
'connected': True,
'id': 5,
'product': 'pr5',
'lock': mock.ANY,
'flash_timeout': 60
}]
),
]
@pytest.mark.parametrize(
'hwm, expected_dump',
TESTDATA_5,
ids=['no map', 'empty map', 'map exists']
)
def test_hardwaremap_save(mocked_hm, hwm, expected_dump):
read_mock = mock.mock_open(read_data=hwm)
write_mock = mock.mock_open()
def mock_open(filename, mode):
if mode == 'r':
return read_mock()
elif mode == 'w':
return write_mock()
mocked_hm.load = mock.Mock()
mocked_hm.dump = mock.Mock()
open_mock = mock.Mock(side_effect=mock_open)
dump_mock = mock.Mock()
with mock.patch('os.path.exists', return_value=hwm is not None), \
mock.patch('builtins.open', open_mock), \
mock.patch('twisterlib.hardwaremap.yaml.dump', dump_mock):
mocked_hm.save('hwm.yaml')
dump_mock.assert_called_once_with(expected_dump, mock.ANY, Dumper=mock.ANY,
default_flow_style=mock.ANY)
TESTDATA_6 = [
(
['p1', 'p3', 'p5', 'p7'],
[],
True,
True,
"""
| Platform | ID | Serial device |
|------------|------|-----------------|
| p1 | 1 | s1 |
| p3 | 3 | s3 |
| p5 | 5 | s5 |
"""
),
(
[],
['?', '??', '???'],
False,
False,
"""
| ? | ?? | ??? |
|-----|------|-------|
| p1 | 1 | s1 |
| p2 | 2 | s2 |
| p3 | 3 | s3 |
| p4 | 4 | s4 |
| p5 | 5 | s5 |
| p6 | 6 | s6 |
| p7 | 7 | s7 |
| p8 | 8 | s8 |
"""
),
]
@pytest.mark.parametrize(
'filtered, header, connected_only, detected, expected_out',
TESTDATA_6,
ids=['detected no header', 'all with header']
)
def test_hardwaremap_dump(
capfd,
mocked_hm,
filtered,
header,
connected_only,
detected,
expected_out
):
mocked_hm.dump(filtered, header, connected_only, detected)
out, err = capfd.readouterr()
sys.stdout.write(out)
sys.stderr.write(err)
assert out.strip() == expected_out.strip()