1# Copyright (c) 2023 Nordic Semiconductor ASA
2#
3# SPDX-License-Identifier: Apache-2.0
4
5from __future__ import annotations
6
7import logging
8import os
9
10import pytest
11
12from twister_harness.twister_harness_config import TwisterHarnessConfig
13
14logger = logging.getLogger(__name__)
15
16pytest_plugins = (
17    'twister_harness.fixtures'
18)
19
20
21def pytest_addoption(parser: pytest.Parser):
22    twister_harness_group = parser.getgroup('Twister harness')
23    twister_harness_group.addoption(
24        '--twister-harness',
25        action='store_true',
26        default=False,
27        help='Activate Twister harness plugin.'
28    )
29    parser.addini(
30        'twister_harness',
31        'Activate Twister harness plugin',
32        type='bool'
33    )
34    twister_harness_group.addoption(
35        '--base-timeout',
36        type=float,
37        default=60.0,
38        help='Set base timeout (in seconds) used during monitoring if some '
39             'operations are finished in a finite amount of time.'
40    )
41    twister_harness_group.addoption(
42        '--flash-timeout',
43        type=float,
44        default=60.0,
45        help='Set timeout for device flashing (in seconds).'
46    )
47    twister_harness_group.addoption(
48        '--build-dir',
49        metavar='PATH',
50        help='Directory with built application.'
51    )
52    twister_harness_group.addoption(
53        '--device-type',
54        choices=('native', 'qemu', 'hardware', 'unit', 'custom'),
55        help='Choose type of device (hardware, qemu, etc.).'
56    )
57    twister_harness_group.addoption(
58        '--platform',
59        help='Name of used platform (qemu_x86, nrf52840dk/nrf52840, etc.).'
60    )
61    twister_harness_group.addoption(
62        '--device-serial',
63        help='Serial device for accessing the board (e.g., /dev/ttyACM0).'
64    )
65    twister_harness_group.addoption(
66        '--device-serial-baud',
67        type=int,
68        default=115200,
69        help='Serial device baud rate (default 115200).'
70    )
71    twister_harness_group.addoption(
72        '--runner',
73        help='Use the specified west runner (pyocd, nrfjprog, etc.).'
74    )
75    twister_harness_group.addoption(
76        '--runner-params',
77        action='append',
78        help='Use the specified west runner params.'
79    )
80    twister_harness_group.addoption(
81        '--device-id',
82        help='ID of connected hardware device (for example 000682459367).'
83    )
84    twister_harness_group.addoption(
85        '--device-product',
86        help='Product name of connected hardware device (e.g. "STM32 STLink").'
87    )
88    twister_harness_group.addoption(
89        '--device-serial-pty',
90        help='Script for controlling pseudoterminal.'
91    )
92    twister_harness_group.addoption(
93        '--flash-before',
94        type=bool,
95        help='Flash device before attaching to serial port'
96             'This is useful for devices that share the same port for programming'
97             'and serial console, or use soft-USB, where flash must come first.'
98    )
99    twister_harness_group.addoption(
100        '--west-flash-extra-args',
101        help='Extend parameters for west flash. '
102             'E.g. --west-flash-extra-args="--board-id=foobar,--erase" '
103             'will translate to "west flash -- --board-id=foobar --erase".'
104    )
105    twister_harness_group.addoption(
106        '--pre-script',
107        metavar='PATH',
108        help='Script executed before flashing and connecting to serial.'
109    )
110    twister_harness_group.addoption(
111        '--post-flash-script',
112        metavar='PATH',
113        help='Script executed after flashing.'
114    )
115    twister_harness_group.addoption(
116        '--post-script',
117        metavar='PATH',
118        help='Script executed after closing serial connection.'
119    )
120    twister_harness_group.addoption(
121        '--dut-scope',
122        choices=('function', 'class', 'module', 'package', 'session'),
123        help='The scope for which `dut` and `shell` fixtures are shared.'
124    )
125    twister_harness_group.addoption(
126        '--twister-fixture', action='append', dest='fixtures', metavar='FIXTURE', default=[],
127        help='Twister fixture supported by this platform. May be given multiple times.'
128    )
129    twister_harness_group.addoption(
130        '--extra-test-args',
131        help='Additional args passed to the test binary'
132    )
133
134
135def pytest_configure(config: pytest.Config):
136    if config.getoption('help'):
137        return
138
139    if not (config.getoption('twister_harness') or config.getini('twister_harness')):
140        return
141
142    _normalize_paths(config)
143    _validate_options(config)
144
145    config.twister_harness_config = TwisterHarnessConfig.create(config)  # type: ignore
146
147
148def _validate_options(config: pytest.Config) -> None:
149    if not config.option.build_dir:
150        raise Exception('--build-dir has to be provided')
151    if not os.path.isdir(config.option.build_dir):
152        raise Exception(f'Provided --build-dir does not exist: {config.option.build_dir}')
153    if not config.option.device_type:
154        raise Exception('--device-type has to be provided')
155
156
157def _normalize_paths(config: pytest.Config) -> None:
158    """Normalize paths provided by user via CLI"""
159    config.option.build_dir = _normalize_path(config.option.build_dir)
160    config.option.pre_script = _normalize_path(config.option.pre_script)
161    config.option.post_script = _normalize_path(config.option.post_script)
162    config.option.post_flash_script = _normalize_path(config.option.post_flash_script)
163
164
165def _normalize_path(path: str | None) -> str:
166    if path is not None:
167        path = os.path.expanduser(os.path.expandvars(path))
168        path = os.path.normpath(os.path.abspath(path))
169    return path
170