diff options
| author | hathach <[email protected]> | 2026-05-25 15:13:30 +0700 |
|---|---|---|
| committer | hathach <[email protected]> | 2026-05-27 17:43:22 +0700 |
| commit | 4a131e1562d8e388bb86592278324ab6bc9f215d (patch) | |
| tree | 10303a041b9b8bc790a7ba5a7ce349afc6c6c0ee | |
| parent | f4d0d09c8ee06a3531c39032cbe102976aeca349 (diff) | |
hil: replace pyfatfs with mtools, update host setup instructions
- Removed `pyfatfs` dependency in favor of `mtools` for reading FAT volumes, simplifying the block device read logic.
- Updated `requirements.txt` and added detailed host setup instructions for system packages.
- Switched to `cython-hidapi` for HID tests, replacing deprecated APIs with updated usage.
- Removed unnecessary warnings suppression and `fs` module.
| -rw-r--r-- | test/hil/hil_ci.sh | 5 | ||||
| -rwxr-xr-x | test/hil/hil_test.py | 82 | ||||
| -rw-r--r-- | test/hil/requirements.txt | 10 |
3 files changed, 59 insertions, 38 deletions
diff --git a/test/hil/hil_ci.sh b/test/hil/hil_ci.sh index 35e71f1ba..4c7ba2936 100644 --- a/test/hil/hil_ci.sh +++ b/test/hil/hil_ci.sh @@ -76,8 +76,11 @@ if [ -n "$BOARD" ]; then copy_board_binaries "$BUILD_DIR" else echo "==> Copying all built binaries" + # Use `%/` parameter expansion to strip the trailing slash from the glob — + # rsync needs the bare dir name so the per-board cmake-build-<BOARD>/ subdir + # is preserved on the remote (hil_test.py looks up binaries by that path). for dir in "$ROOT_DIR"/examples/cmake-build-*/; do - [ -d "$dir" ] && copy_board_binaries "$dir" + [ -d "$dir" ] && copy_board_binaries "${dir%/}" done fi diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index d921a910a..b0b3fc17e 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -22,6 +22,14 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. +# Host setup: +# - System packages: sudo apt install mtools libmtp9 alsa-utils iperf +# mtools - read_disk_file (device/cdc_msc, device/msc_dual_lun) +# libmtp9 - pymtp ctypes load (device/mtp); Debian 13 uses libmtp9t64 +# alsa-utils - arecord (device/audio_test_freertos) +# iperf - throughput tests (device/net_lwip_*) +# - Python packages: pip install -r requirements.txt +# # udev rules : # ACTION=="add", SUBSYSTEM=="tty", SUBSYSTEMS=="usb", MODE="0666", PROGRAM="/bin/sh -c 'echo $$ID_SERIAL_SHORT | rev | cut -c -8 | rev'", SYMLINK+="ttyUSB_%c.%s{bInterfaceNumber}" # ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", ENV{ID_FS_USAGE}=="filesystem", MODE="0666", PROGRAM="/bin/sh -c 'echo $$ID_SERIAL_SHORT | rev | cut -c -8 | rev'", RUN{program}+="/usr/bin/systemd-mount --no-block --automount=yes --collect $devnode /media/blkUSB_%c.%s{bInterfaceNumber}" @@ -34,17 +42,11 @@ import re import select import sys import time -import warnings import signal from contextlib import redirect_stdout from pathlib import Path from typing import Any, TypedDict, NotRequired, cast -# Suppress pkg_resources deprecation warning from fs module -warnings.filterwarnings("ignore", message="pkg_resources is deprecated") -# Suppress pyfatfs unclean unmount warning -warnings.filterwarnings("ignore", message="Filesystem was not cleanly unmounted") - import serial import subprocess import json @@ -52,7 +54,6 @@ import glob import shutil from multiprocessing import Pool, Lock from multiprocessing import TimeoutError as MpTimeoutError -import fs import hashlib import ctypes from pymtp import MTP @@ -232,23 +233,24 @@ def open_serial_dev(port: str): def read_disk_file(uid: str, lun: int, fname: str) -> bytes: - # open_fs("fat://{dev}) require 'pip install pyfatfs' + # Reads a file from a FAT volume on a block device without mounting it. + # Requires mtools: `apt install mtools` (no pip dependency). dev = get_disk_dev(uid, 'TinyUSB', lun) timeout = ENUM_TIMEOUT + last_err = None while timeout > 0: if os.path.exists(dev): - fat = fs.open_fs(f'fat://{dev}?read_only=true') try: - with fat.open(fname, 'rb') as f: - data = f.read() - finally: - fat.close() - assert data, f'Cannot read file {fname} from {dev}' - return data + data = subprocess.check_output( + ['mtype', '-i', dev, f'::/{fname}'], stderr=subprocess.PIPE) + assert data, f'Cannot read file {fname} from {dev}' + return data + except subprocess.CalledProcessError as e: + last_err = e.stderr.decode(errors='replace').strip() time.sleep(1) timeout -= 1 - raise AssertionError(f'Storage {dev} not existed') + raise AssertionError(f'mtype failed on {dev}: {last_err}' if last_err else f'Storage {dev} not existed') def open_mtp_dev(uid): @@ -1406,7 +1408,7 @@ def test_device_audio_test_freertos(board): def test_device_hid_generic_inout(board): uid = board['uid'] - import hid + import hid # cython-hidapi (pip: hidapi, apt: python3-hid) # Find HID device by UID (VID=0xCafe) timeout = ENUM_TIMEOUT @@ -1422,22 +1424,23 @@ def test_device_hid_generic_inout(board): timeout -= 1 assert dev is not None, f'HID device not found for {uid}' - h = hid.Device(vid=dev['vendor_id'], pid=dev['product_id'], serial=uid) - - # Echo test: send random data and verify echo - for size in [8, 32, 63]: - # Report ID (0) + payload, padded to 64 bytes - payload = bytes([random.randint(1, 255) for _ in range(size)]) - report = bytes([0]) + payload + bytes(64 - size) - h.write(report) - echo = h.read(64, timeout=2000) - assert echo is not None and len(echo) >= size, ( - f'HID echo timeout or short read ({size} bytes)') - assert bytes(echo[:size]) == payload, ( - f'HID echo wrong data ({size} bytes):\n' - f' expected: {payload.hex()}\n received: {bytes(echo[:size]).hex()}') - - h.close() + h = hid.device() + h.open(dev['vendor_id'], dev['product_id'], uid) + try: + # Echo test: send random data and verify echo + for size in [8, 32, 63]: + # Report ID (0) + payload, padded to 64 bytes + payload = bytes([random.randint(1, 255) for _ in range(size)]) + report = bytes([0]) + payload + bytes(64 - size) + h.write(report) + echo = h.read(64, 2000) + assert echo and len(echo) >= size, ( + f'HID echo timeout or short read ({size} bytes)') + assert bytes(echo[:size]) == payload, ( + f'HID echo wrong data ({size} bytes):\n' + f' expected: {payload.hex()}\n received: {bytes(echo[:size]).hex()}') + finally: + h.close() # ------------------------------------------------------------- @@ -1601,7 +1604,18 @@ def test_board(board: Board) -> tuple[str, int, list[str]]: if name in board_test: test_list = board_test[name] elif len(test_only) > 0: - test_list = test_only + # Explicit -t: filter against the board's capabilities so a device-only + # board doesn't try to run host/dual tests (the test functions need a + # `dev_attached` entry in the board config that won't exist). + board_tests = board.get('tests', {}) + if 'only' in board_tests: + allowed = set(board_tests['only']) + test_list = [t for t in test_only if t in allowed] + else: + for t in test_only: + category = t.split('/', 1)[0] + if board_tests.get(category) is True: + test_list.append(t) else: if 'tests' in board: board_tests = board['tests'] diff --git a/test/hil/requirements.txt b/test/hil/requirements.txt index 127f6a8ec..ef1cf575b 100644 --- a/test/hil/requirements.txt +++ b/test/hil/requirements.txt @@ -1,5 +1,9 @@ -fs -hid -pyfatfs +# System packages (install separately): +# sudo apt install mtools libmtp9 alsa-utils iperf +# mtools - read_disk_file (device/cdc_msc, device/msc_dual_lun) +# libmtp9 - pymtp ctypes load (device/mtp); Debian 13 uses libmtp9t64 +# alsa-utils - arecord (device/audio_test_freertos) +# iperf - throughput tests (device/net_lwip_*) +hidapi pyserial esptool |
