Skip to content

Commit

Permalink
tests: on_target: refactor dfu test
Browse files Browse the repository at this point in the history
Fix and refactor dfu test and utils.

Signed-off-by: Giacomo Dematteis <[email protected]>
  • Loading branch information
DematteisGiacomo committed Oct 11, 2024
1 parent 8f7d61e commit 5976e4f
Show file tree
Hide file tree
Showing 5 changed files with 420 additions and 449 deletions.
33 changes: 17 additions & 16 deletions .github/workflows/on_target.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
runs-on: self-hosted
environment: production
container:
image: ghcr.io/hello-nrfcloud/firmware:v2.0.0-preview42
image: ghcr.io/hello-nrfcloud/firmware:v2.0.0-preview55
options: --privileged
volumes:
- /dev:/dev:rw
Expand Down Expand Up @@ -158,21 +158,22 @@ jobs:
IMEI: ${{ secrets.IMEI_DUT_1 }}
FINGERPRINT: ${{ secrets.FINGERPRINT_DUT_1 }}

# - name: Run DFU tests
# if: ${{ inputs.run_dfu_tests }}
# working-directory: thingy91x-oob/tests/on_target
# run: |
# pytest -s -v -m dut2 --junit-xml=results/test-results-dfu.xml tests
# env:
# SEGGER_NRF53: ${{ secrets.SEGGER_DUT_2_EXT_DBG }}
# SEGGER_NRF91: ${{ secrets.SEGGER_DUT_2_NRF91 }}
# UART_ID: ${{ secrets.UART_DUT_2 }}
# NRF53_HEX_FILE: artifacts/connectivity-bridge-${{ inputs.artifact_fw_version }}-thingy91x-nrf53-merged.hex
# NRF53_APP_UPDATE_ZIP: artifacts/connectivity-bridge-${{ inputs.artifact_fw_version }}-thingy91x-nrf53-verbose.zip
# NRF53_BL_UPDATE_ZIP: artifacts/connectivity-bridge-${{ inputs.artifact_fw_version }}-thingy91x-nrf53-bootloader.zip
# NRF91_HEX_FILE: artifacts/hello.nrfcloud.com-${{ inputs.artifact_fw_version }}-thingy91x-nrf91-bootloader.hex
# NRF91_APP_UPDATE_ZIP: artifacts/hello.nrfcloud.com-${{ inputs.artifact_fw_version }}-thingy91x-nrf91-dfu.zip
# NRF91_BL_UPDATE_ZIP: artifacts/hello.nrfcloud.com-${{ inputs.artifact_fw_version }}-thingy91x-nrf91-bootloader.zip
- name: Run DFU tests
if: ${{ inputs.run_dfu_tests }}
working-directory: thingy91x-oob/tests/on_target
run: |
pytest -s -v -m dut2 --junit-xml=results/test-results-dfu.xml tests
env:
SEGGER_NRF53: ${{ secrets.SEGGER_DUT_2_EXT_DBG }}
SEGGER_NRF91: ${{ secrets.SEGGER_DUT_2_NRF91 }}
UART_ID: ${{ secrets.UART_DUT_2 }}
NRF53_NET_HEX_FILE: artifacts/connectivity-bridge-${{ inputs.artifact_fw_version }}-thingy91x-nrf53-net.hex
NRF53_APP_HEX_FILE: artifacts/connectivity-bridge-${{ inputs.artifact_fw_version }}-thingy91x-nrf53-app.hex
NRF53_APP_UPDATE_ZIP: artifacts/connectivity-bridge-${{ inputs.artifact_fw_version }}-thingy91x-nrf53-verbose.zip
NRF53_BL_UPDATE_ZIP: artifacts/connectivity-bridge-${{ inputs.artifact_fw_version }}-thingy91x-nrf53-bootloader.zip
NRF91_HEX_FILE: artifacts/hello.nrfcloud.com-${{ inputs.artifact_fw_version }}-thingy91x-nrf91-bootloader.hex
NRF91_APP_UPDATE_ZIP: artifacts/hello.nrfcloud.com-${{ inputs.artifact_fw_version }}-thingy91x-nrf91-dfu.zip
NRF91_BL_UPDATE_ZIP: artifacts/hello.nrfcloud.com-${{ inputs.artifact_fw_version }}-thingy91x-nrf91-bootloader.zip

# - name: Check nRF53 connectivity bridge version
# if: ${{ inputs.run_connectivity_bridge_tests }}
Expand Down
4 changes: 2 additions & 2 deletions tests/on_target/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Setup docker
```shell
docker pull ghcr.io/hello-nrfcloud/firmware:v2.0.0-preview42
docker pull ghcr.io/hello-nrfcloud/firmware:v2.0.0-preview55
cd <path_to_oob_dir>
west build -p -b thingy91x/nrf9151/ns app
cp build/merged.hex tests/on_target/artifacts/hello.nrfcloud.com-aaa000-thingy91x-nrf91.hex
Expand All @@ -13,7 +13,7 @@ docker run --rm -it \
-v /dev:/dev:rw \
-v /run/udev:/run/udev \
-v .:/work/thingy91x-oob \
ghcr.io/hello-nrfcloud/firmware:v2.0.0-preview42 \
ghcr.io/hello-nrfcloud/firmware:v2.0.0-preview55 \
/bin/bash
```

Expand Down
51 changes: 35 additions & 16 deletions tests/on_target/tests/test_serial_dfu.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,36 +23,57 @@ def wait_until_uart_available(name, timeout_seconds=60):
serial_paths = [os.path.join(base_path, entry) for entry in os.listdir(base_path)]
for path in sorted(serial_paths):
if name in path:
break
logger.info(f"UART found: {path}")
return path
except (FileNotFoundError, PermissionError) as e:
logger.error(e)
logger.info(f"Uart not available yet (error: {e}), retrying...")
time.sleep(1)
timeout_seconds -= 1
logger.error(f"UART '{name}' not found within {timeout_seconds} seconds")
return None

@pytest.fixture(scope="function")
def t91x_dfu():
'''
This fixture initializes the nRF53 and nRF91 for dfu test.
First nRF91 is flashed with oob bootloader fw through segger fw on nRF53.
Then nRF53 is flashed with connectivity bridge fw.
'''
SEGGER_NRF53 = os.getenv('SEGGER_NRF53')
SEGGER_NRF91 = os.getenv('SEGGER_NRF91')
CONNECTIVITY_BRIDGE_UART = os.getenv('UART_ID')
NRF53_HEX_FILE = os.getenv('NRF53_HEX_FILE')
NRF53_NET_HEX_FILE = os.getenv('NRF53_NET_HEX_FILE')
NRF53_APP_HEX_FILE = os.getenv('NRF53_APP_HEX_FILE')
NRF91_HEX_FILE = os.getenv('NRF91_HEX_FILE')

logger.info(("Flashing nRF53 with Segger firmware"))
setup_jlink(SEGGER_NRF53)

logger.info("Waiting for segger UART to be available")
wait_until_uart_available(SEGGER_NRF91)

logger.info("Flashing nRF91 bootloader firmware")
flash_device(hexfile=NRF91_HEX_FILE, serial=SEGGER_NRF91)
logger.info("nRF91 initialized successfully")

logger.info("Recovering nRF53 network core")
recover_device(serial=SEGGER_NRF53, core='Network')
logger.info("Recovering nRF53 application core")
recover_device(serial=SEGGER_NRF53, core='Application')
flash_device(hexfile=NRF53_HEX_FILE, serial=SEGGER_NRF53, extra_args=['--core', 'Network', '--options', 'reset=RESET_NONE,chip_erase_mode=ERASE_ALL,verify=VERIFY_NONE'])
flash_device(hexfile=NRF53_HEX_FILE, serial=SEGGER_NRF53, extra_args=['--options', 'reset=RESET_SYSTEM,chip_erase_mode=ERASE_ALL,verify=VERIFY_NONE'])
logger.info("Flashing nRF53 with connectivity bridge firmware")
logger.info("Flashing nRF53 network core")
flash_device(hexfile=NRF53_NET_HEX_FILE, serial=SEGGER_NRF53, extra_args=['--core', 'Network', '--options', 'reset=RESET_NONE,chip_erase_mode=ERASE_ALL,verify=VERIFY_NONE'])
logger.info("Flashing nRF53 application core")
flash_device(hexfile=NRF53_APP_HEX_FILE, serial=SEGGER_NRF53, extra_args=['--options', 'reset=RESET_SYSTEM,chip_erase_mode=ERASE_ALL,verify=VERIFY_NONE'])
logger.info("Waiting for connectivity bridge UART to be available")
wait_until_uart_available(CONNECTIVITY_BRIDGE_UART)

logger.info("nRF53 initialized successfully")

all_uarts = get_uarts()
logger.info(f"All uarts discovered: {all_uarts}")
if not all_uarts:
pytest.fail("No UARTs found")
logger.info(f"All uarts discovered: {all_uarts}")
log_uart_string = all_uarts[0]
logger.info(f"Log UART: {log_uart_string}")

Expand All @@ -73,23 +94,21 @@ def test_dfu(t91x_dfu):
NRF53_APP_UPDATE_ZIP = os.getenv('NRF53_APP_UPDATE_ZIP')
NRF53_BL_UPDATE_ZIP = os.getenv('NRF53_BL_UPDATE_ZIP')

logger.info("Starting DFU, stopping UART")
t91x_dfu.uart.stop()


logger.info("Starting nRF91 APP DFU")
dfu_device(NRF91_APP_UPDATE_ZIP, serial=CONNECTIVITY_BRIDGE_UART)
logger.info("Starting nRF91 BL DFU")
dfu_device(NRF91_BL_UPDATE_ZIP, serial=CONNECTIVITY_BRIDGE_UART)

t91x_dfu.uart.start()

expected_lines = ["Firmware version 3", "Zephyr OS"]
t91x_dfu.uart.wait_for_str(expected_lines, timeout=60)
logger.info("nRF91 DFU test passed successfully")

t91x_dfu.uart.stop()

dfu_device(NRF53_APP_UPDATE_ZIP, serial=CONNECTIVITY_BRIDGE_UART)
wait_until_uart_available(CONNECTIVITY_BRIDGE_UART)
# TODO: To be fixed, reset fails after DFU
# dfu_device(NRF53_APP_UPDATE_ZIP, serial=CONNECTIVITY_BRIDGE_UART)
# wait_until_uart_available(CONNECTIVITY_BRIDGE_UART)
# TODO: Fix the CI issue with nRF53 BL DFU
# dfu_device(NRF53_BL_UPDATE_ZIP, serial=CONNECTIVITY_BRIDGE_UART)
# wait_until_uart_available(CONNECTIVITY_BRIDGE_UART)

logger.info("nRF53 DFU successful, checking version")
# logger.info("nRF53 DFU successful, checking version")
86 changes: 60 additions & 26 deletions tests/on_target/utils/flash_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,21 @@
import sys
sys.path.append(os.getcwd())
from utils.logger import get_logger
from utils.thingy91x_dfu import Thingy91XDFU, detect_family_from_zip

logger = get_logger()

SEGGER = os.getenv('SEGGER')

def reset_device(serial=SEGGER):
def reset_device(serial=SEGGER, reset_kind="RESET_SYSTEM"):
logger.info(f"Resetting device, segger: {serial}")
try:
result = subprocess.run(['nrfutil', 'device', 'reset', '--serial-number', serial], check=True, text=True, capture_output=True)
logger.info("Command output:")
logger.info(result.stdout)
result = subprocess.run(
['nrfutil', 'device', 'reset', '--serial-number', serial, '--reset-kind', reset_kind],
check=True,
text=True,
capture_output=True
)
logger.info("Command completed successfully.")
except subprocess.CalledProcessError as e:
# Handle errors in the command execution
Expand Down Expand Up @@ -54,31 +58,61 @@ def recover_device(serial=SEGGER, core="Application"):
logger.info(e.stderr)
raise

def dfu_device(zipfile, serial):
logger.info(f"Performing MCUBoot DFU, firmware: {zipfile}")
thingy91x_dfu = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'thingy91x_dfu.py')
# call thingy91x-dfu
def dfu_device(zipfile, serial, reset_only=False):
chip, is_mcuboot = detect_family_from_zip(zipfile)
if chip is None:
logger.error("Could not determine chip family from image")
raise ValueError("Invalid image file")

dfu = Thingy91XDFU(0x1915, 0x910A, chip, serial)

if reset_only:
dfu.reset_device()
return

try:
result = subprocess.run(['python3', thingy91x_dfu, '--serial', serial, '--debug', '--image', zipfile], check=True, text=True, capture_output=True)
logger.info("Command output:")
logger.info(result.stdout)
logger.info(result.stderr)
logger.info("Command completed successfully.")
except subprocess.CalledProcessError as e:
# Handle errors in the command execution
logger.info("An error occurred while flashing the device.")
logger.info("Error output:")
logger.info(e.stderr)
serial_number = dfu.enter_bootloader_mode()
if serial_number:
logger.info(f"{chip} on {serial_number} is in bootloader mode")
else:
raise RuntimeError("Failed to enter bootloader mode")

if is_mcuboot:
dfu.perform_bootloader_dfu(zipfile)
else:
dfu.perform_dfu(zipfile)

logger.info("DFU completed successfully.")
except Exception as e:
logger.error(f"An error occurred during DFU: {str(e)}")
raise

def setup_jlink(serial):
# run command and check if it was successful
def setup_jlink(serial_number):
"""
Flash Segger firmware to nRF53 on Thingy91x through external debugger.
Args:
serial_number (str): Serial number of the external debugger.
Raises:
subprocess.CalledProcessError: If the setup-jlink command fails.
"""
logger.info("Flashing Segger firmware to nRF53 on Thingy91x through external debugger")

setup_jlink_command = ['/opt/setup-jlink/setup-jlink.bash', serial_number]

try:
result = subprocess.run(['/opt/setup-jlink/setup-jlink.bash', serial], check=True, text=True, capture_output=False)
logger.info("Command completed successfully.")
subprocess.run(
setup_jlink_command,
check=True,
text=True,
capture_output=True
)
logger.info("Segger firmware flashed successfully.")
except subprocess.CalledProcessError as e:
# Handle errors in the command execution
logger.info("An error occurred while setting up JLink.")
logger.info("Error output:")
logger.info(e.stderr)
logger.error("Failed to flash Segger firmware.")
logger.error(f"Command: {' '.join(setup_jlink_command)}")
logger.error(f"Exit code: {e.returncode}")
logger.error(f"Standard output:\n{e.stdout}")
logger.error(f"Standard error:\n{e.stderr}")
raise
Loading

0 comments on commit 5976e4f

Please sign in to comment.