Files
rockblock9704-zephyr/docs/TESTING.md
T
scottp beb22fb70c Docs: the README, JSPR, Cloudloop, testing
README says what the module adds to Ground Control's library and what it
works around, and how to use it. JSPR.md is the protocol as the library
speaks it, from its source, with its surprises. CLOUDLOOP.md is account
to first message, from WII6's IRIDIUM.md. TESTING.md is the levels, the
rig, and what has passed: Python, native_sim, an ESP32-S3 and an STM32U5
against the fake, and free checks against a real 9704.
2026-10-09 12:48:16 +11:00

4.4 KiB

Testing

One fake modem, three places to run the code against it, and a few free checks against a real modem.

Level What runs Against Needs
Python Ground Control's Python binding, the rb9704 command line, the fake itself the fake on a pty Python 3.10 to 3.14
Host Zephyr this module in the shell sample on native_sim the fake on a pty, or a real modem on USB a Zephyr workspace
Board the same sample on an ESP32-S3, STM32U5 or any board the fake on a USB-serial port wired to the board's modem pins a board and a wire

The native_sim and board runs are the same tests (python/tests/test_zephyr.py) through a different console, so a behaviour seen on one is checked on the other.

The fake modem

python/rb9704tools/fakemodem.py, rb9704-fake on the command line. It implements JSPR as JSPR.md describes it: the three exchanges rbBegin needs, the identity and signal getters, provisioning, MO in segments of any size with the CRC checked, MT in segments the library can take, and on command a refusal, a failed MO, a signal change or a change of provisioning. It writes every whole MO it receives as an event line, so a test checks the bytes that reached "the network".

What it cannot tell you is what a real modem does that the library's source does not show: unsolicited lines at odd moments, timing, MT segment sizes. That is what test_real.py and the hardware are for.

Running them

python/install.sh

# Python level: the fake, the command line, Ground Control's binding
python/.venv/bin/pytest -q python/tests

# Host Zephyr: build the sample for native_sim, then
west build -b native_sim/native/64 samples/shell -d build/native
python/.venv/bin/pytest -q python/tests --zephyr-exe build/native/zephyr/zephyr.exe

# Board: flash the sample with its modem UART wired to a USB-serial
# adapter (3.3 V), then the board's shell port and the adapter's port
python/.venv/bin/pytest -q python/tests/test_zephyr.py \
    --board-console /dev/ttyACM0 --board-modem /dev/ttyUSB0

# A real modem, free checks only (identity and signal, never a message)
python/.venv/bin/pytest -q python/tests/test_real.py --real-modem /dev/ttyUSB0 \
    [--zephyr-exe build/native/zephyr/zephyr.exe]

Every option has an environment variable as well: RB9704_ZEPHYR_EXE, RB9704_BOARD_CONSOLE, RB9704_BOARD_MODEM, RB9704_REAL_MODEM.

On native_sim the modem UART is a zephyr,native-tty-uart node (samples/shell/boards/native_sim.overlay); the tests give it the fake's pty with -modem_uart_port=. The same option takes a real modem's /dev/ttyUSB0, which is how host Zephyr talks to real hardware.

The rig: two boards, one wire

No USB-serial adapter to hand? Two boards with a UART wired between them will do: one runs the sample, the other runs tests/rig/bridge, which copies bytes between that UART and its own USB serial port, where the fake answers.

host --USB-- bridge board --UART-- board under test --USB-- host
             (tests/rig/bridge)    (samples/shell)
  fake modem on this port                       pytest drives this shell

The overlays in tests/rig/ are for a NUCLEO-U575ZI-Q (USART2 on PD5 and PD6) and a Seeed XIAO ESP32S3 (D8 and D9) wired to each other, either way round:

Under test Bridge --board-console --board-modem
XIAO ESP32S3, -DDTC_OVERLAY_FILE=tests/rig/xiao_esp32s3_rig.overlay Nucleo, tests/rig/bridge the XIAO's USB the Nucleo's ST-LINK port
Nucleo, -DDTC_OVERLAY_FILE=tests/rig/nucleo_u575zi_q_rig.overlay XIAO, tests/rig/bridge the Nucleo's ST-LINK port the XIAO's USB

The ESP32-S3 sits in its ROM loader after west flash until it is reset; toggle RTS on its USB port, or press reset, before the tests.

What has passed

On 2026-10-09, Zephyr v4.4.0, Ground Control's library v1.0.1, the binding 1.0.2:

Level Tests Result
Python: fake, command line, binding 16 all pass
native_sim/native/64 against the fake 12 all pass
XIAO ESP32S3 through the rig 12 all pass
NUCLEO-U575ZI-Q through the rig 12 all pass
a real 9704 (firmware v1.5.14), binding and native_sim, free checks 2 all pass

Not yet: a message through a real modem and satellite, which costs airtime and needs the sky (CLOUDLOOP.md, "The order to test in"), and the power pins on a board that has them wired.