1# Copyright (c) 2023 Nordic Semiconductor ASA
2#
3# SPDX-License-Identifier: Apache-2.0
4
5import logging
6from pathlib import Path
7from typing import Generator, Type
8
9import pytest
10import time
11
12from twister_harness.device.device_adapter import DeviceAdapter
13from twister_harness.device.factory import DeviceFactory
14from twister_harness.twister_harness_config import DeviceConfig, TwisterHarnessConfig
15from twister_harness.helpers.shell import Shell
16from twister_harness.helpers.mcumgr import MCUmgr
17from twister_harness.helpers.utils import find_in_config
18
19logger = logging.getLogger(__name__)
20
21
22@pytest.fixture(scope='session')
23def twister_harness_config(request: pytest.FixtureRequest) -> TwisterHarnessConfig:
24    """Return twister_harness_config object."""
25    twister_harness_config: TwisterHarnessConfig = request.config.twister_harness_config  # type: ignore
26    return twister_harness_config
27
28
29@pytest.fixture(scope='session')
30def device_object(twister_harness_config: TwisterHarnessConfig) -> Generator[DeviceAdapter, None, None]:
31    """Return device object - without run application."""
32    device_config: DeviceConfig = twister_harness_config.devices[0]
33    device_type = device_config.type
34    device_class: Type[DeviceAdapter] = DeviceFactory.get_device(device_type)
35    device_object = device_class(device_config)
36    try:
37        yield device_object
38    finally:  # to make sure we close all running processes execution
39        device_object.close()
40
41
42def determine_scope(fixture_name, config):
43    if dut_scope := config.getoption("--dut-scope", None):
44        return dut_scope
45    return 'function'
46
47
48@pytest.fixture(scope=determine_scope)
49def unlaunched_dut(request: pytest.FixtureRequest, device_object: DeviceAdapter) -> Generator[DeviceAdapter, None, None]:
50    """Return device object - with logs connected, but not run"""
51    device_object.initialize_log_files(request.node.name)
52    try:
53        yield device_object
54    finally:  # to make sure we close all running processes execution
55        device_object.close()
56
57@pytest.fixture(scope=determine_scope)
58def dut(request: pytest.FixtureRequest, device_object: DeviceAdapter) -> Generator[DeviceAdapter, None, None]:
59    """Return launched device - with run application."""
60    device_object.initialize_log_files(request.node.name)
61    try:
62        device_object.launch()
63        yield device_object
64    finally:  # to make sure we close all running processes execution
65        device_object.close()
66
67
68@pytest.fixture(scope=determine_scope)
69def shell(dut: DeviceAdapter) -> Shell:
70    """Return ready to use shell interface"""
71    shell = Shell(dut, timeout=20.0)
72    if prompt := find_in_config(Path(dut.device_config.app_build_dir) / 'zephyr' / '.config',
73                                'CONFIG_SHELL_PROMPT_UART'):
74        shell.prompt = prompt
75    logger.info('Wait for prompt')
76    if not shell.wait_for_prompt():
77        pytest.fail('Prompt not found')
78    if dut.device_config.type == 'hardware':
79        # after booting up the device, there might appear additional logs
80        # after first prompt, so we need to wait and clear the buffer
81        time.sleep(0.5)
82        dut.clear_buffer()
83    return shell
84
85
86@pytest.fixture(scope='session')
87def is_mcumgr_available() -> None:
88    if not MCUmgr.is_available():
89        pytest.skip('mcumgr not available')
90
91
92@pytest.fixture()
93def mcumgr(is_mcumgr_available: None, dut: DeviceAdapter) -> Generator[MCUmgr, None, None]:
94    yield MCUmgr.create_for_serial(dut.device_config.serial)
95