Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

twister: pytest: Bluetooth: multi harness boards test against DuT #83641

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions samples/bluetooth/central_ht/pytest/test_cases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Copyright (c) 2025 NXP
#
# SPDX-License-Identifier: Apache-2.0

import logging

logger = logging.getLogger(__name__)


def test_os_boot(dut, harness_devices):
dut.readlines_until('Booting Zephyr OS build', timeout=5)
harness_devices[0].readlines_until('Booting Zephyr OS build', timeout=5)


def test_bluetooth_boot(dut, harness_devices):
dut.readlines_until('Scanning successfully started', timeout=5)
harness_devices[0].readlines_until('Advertising successfully started', timeout=5)


def test_bluetooth_connection(dut, harness_devices):
dut.readlines_until('Connected', timeout=5)


def test_match_temp_value_over_ble(dut, harness_devices):
dut.readlines_until(r'Temperature \d{1,2}C', timeout=5)
7 changes: 5 additions & 2 deletions samples/bluetooth/central_ht/sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ tests:
- qemu_cortex_m3
sample.bluetooth.central_ht.nxp:
# Disabling monolithic since CI environment doesn't use blobs
build_only: true
harness: bluetooth
harness: pytest
platform_allow:
- rd_rw612_bga
- frdm_rw612
- frdm_mcxw71/mcxw716c
extra_configs:
- CONFIG_NXP_MONOLITHIC_NBU=n
harness_config:
pytest_dut_scope: session
pytest_args: ['--harness_apps=zephyr/samples/bluetooth/peripheral_ht']
26 changes: 26 additions & 0 deletions samples/bluetooth/peripheral_ht/pytest/test_cases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Copyright (c) 2025 NXP
#
# SPDX-License-Identifier: Apache-2.0

import logging

logger = logging.getLogger(__name__)


def test_os_boot(dut, harness_devices):
dut.readlines_until('Booting Zephyr OS build', timeout=5)
harness_devices[0].readlines_until('Booting Zephyr OS build', timeout=5)


def test_bluetooth_boot(dut, harness_devices):
dut.readlines_until('Advertising successfully started', timeout=5)
harness_devices[0].readlines_until('Scanning successfully started', timeout=5)


def test_bluetooth_connection(dut, harness_devices):
harness_devices[0].readlines_until('Connected', timeout=5)


def test_match_temp_value_over_ble(dut, harness_devices):
dut.readlines_until('Indication success', timeout=5)
harness_devices[0].readlines_until(r'Temperature \d{1,2}C', timeout=5)
Comment on lines +1 to +26
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will the test case be running as part of PR CI, nightly or weekly on the upstream main branch? (if not, these test vectors can get stale when someone change the strings).

7 changes: 5 additions & 2 deletions samples/bluetooth/peripheral_ht/sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@ tests:
- mimxrt1020_evk
sample.bluetooth.peripheral_ht.nxp:
# Disabling monolithic since CI environment doesn't use blobs
build_only: true
harness: bluetooth
harness: pytest
platform_allow:
- rd_rw612_bga
- frdm_rw612
- frdm_mcxw71/mcxw716c
extra_configs:
- CONFIG_NXP_MONOLITHIC_NBU=n
harness_config:
pytest_dut_scope: session
pytest_args: ['--harness_apps=zephyr/samples/bluetooth/central_ht']
Copy link
Collaborator

@hakehuang hakehuang Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seeing from below code, the harness_apps is a list, so need define list like

pytest_args:
   harness_apps:
    - "zephyr/samples/bluetooth/central_ht"

by the way, why start from zephyr?

Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
#
# SPDX-License-Identifier: Apache-2.0

import os
import sys
import copy
import logging
import time
import subprocess
from pathlib import Path
from typing import Generator, Type

import pytest
import time

from twister_harness.device.device_adapter import DeviceAdapter
from twister_harness.device.factory import DeviceFactory
Expand All @@ -16,6 +20,11 @@
from twister_harness.helpers.mcumgr import MCUmgr
from twister_harness.helpers.utils import find_in_config

from twister_harness.helpers.domains_helper import ZEPHYR_BASE
sys.path.insert(0, os.path.join(ZEPHYR_BASE, 'scripts')) # import zephyr_module in environment.py
sys.path.insert(0, os.path.join(ZEPHYR_BASE, 'scripts', 'pylib', 'twister'))
from twisterlib.hardwaremap import HardwareMap
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not good, why fixture mixed with hardware map which supposed to be platform agnostic

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi hake, thanks for these review and comments,

I put the harness devices configuration info in another yaml, and want to import and reuse twister HardwareMap class to load and parse this new yaml,

for the harness boards info, do you have any suggestion, where/which file to store/manage those info ?

all the boards in the current dut_map.yaml will be recognized as duts and scheduled for tests in parallels.


logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -92,3 +101,70 @@ def is_mcumgr_available() -> None:
@pytest.fixture()
def mcumgr(is_mcumgr_available: None, dut: DeviceAdapter) -> Generator[MCUmgr, None, None]:
yield MCUmgr.create_for_serial(dut.device_config.serial)


@pytest.fixture(scope='session')
def harness_devices(request, twister_harness_config):
"""Return harness_device list object."""

class TwisterOptionsWrapper:

device_flash_timeout: float = 60.0 # [s]
device_flash_with_test: bool = True
flash_before: bool = False

harness_device_yml = request.config.getoption('--harness_device_map')
harness_apps = request.config.getoption('--harness_apps')
logger.info(f'harness_device_yml:{harness_device_yml}')
logger.info(f'harness_apps:{harness_apps}')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please update the twister doc to explain the usage of this harness


harness_app_list = harness_apps.split(' ')
logger.info(f'harness_app:{harness_app_list}')

# reuse twister HardwareMap class to load harness device config yaml
options = TwisterOptionsWrapper()
harness_device_hwm = HardwareMap(options)
harness_device_hwm.load(harness_device_yml)
logger.info(f'harness_device_hwm[0]:{harness_device_hwm.duts[0]}')

if not harness_device_hwm.duts or (len(harness_app_list) != len(harness_device_hwm.duts)):
pytest.skip("Skipping all tests due to wrong harness setting.")

# reuse most dut config for harness_device, only build and flash different app into them
dut_device_config: DeviceConfig = twister_harness_config.devices[0]
logger.info(f'dut_device_config:{dut_device_config}')

harness_devices = []
for index, harness_hw in enumerate(harness_device_hwm.duts):
harness_app = harness_app_list[index]
# split harness_app into appname, extra_config
harness_app = harness_app.split('-')
appname, extra_config = harness_app[0], harness_app[1:]
extra_config = ['-' + config for config in extra_config]
# build harness_app image for harness device
build_dir = f'./harness_{harness_hw.platform}_{os.path.basename(appname)}'
cmd = ['west', 'build', appname, '-b', harness_hw.platform, '--build-dir', build_dir] + extra_config
Copy link
Collaborator

@hakehuang hakehuang Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be update in CMakefile as dependency. refer to samples/subsys/ipc/openamp/remote

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks hake, will check how to do this with CMakefile system for the dependency board settings

logger.info(' '.join(cmd))
logger.info(os.getcwd())
subprocess.call(cmd)

# update the specific configuration for harness_hw
harness_device_config = copy.deepcopy(dut_device_config)
harness_device_config.id = harness_hw.id
harness_device_config.serial = harness_hw.serial
harness_device_config.build_dir = Path(build_dir)
logger.info(f'harness_device_config:{harness_device_config}')

# init harness device as DuT
device_class: Type[DeviceAdapter] = DeviceFactory.get_device(harness_device_config.type)
device_object = device_class(harness_device_config)
device_object.initialize_log_files(request.node.name)
harness_devices.append(device_object)

try:
for device_object in harness_devices:
device_object.launch()
yield harness_devices
finally: # to make sure we close all running processes execution
for device_object in harness_devices:
device_object.close()
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ def pytest_addoption(parser: pytest.Parser):
'--extra-test-args',
help='Additional args passed to the test binary'
)
twister_harness_group.addoption(
'--harness_apps', default=None,
help='harness zephyr sample app for interaction with DuT.')
twister_harness_group.addoption(
'--harness_device_map', default=None,
help='harness devices info for interaction with DuT.')


def pytest_configure(config: pytest.Config):
Expand Down
4 changes: 2 additions & 2 deletions scripts/pylib/twister/twisterlib/hardwaremap.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,10 @@ class HardwareMap:
]
}

def __init__(self, env=None):
def __init__(self, options=None):
self.detected = []
self.duts = []
self.options = env.options
self.options = options

def discover(self):

Expand Down
2 changes: 1 addition & 1 deletion scripts/pylib/twister/twisterlib/twister_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def main(options: argparse.Namespace, default_options: argparse.Namespace):
env = TwisterEnv(options, default_options)
env.discover()

hwm = HardwareMap(env)
hwm = HardwareMap(env.options)
ret = hwm.discover()
if ret == 0:
return 0
Expand Down
Loading