From 67ba8eab2e346dc13cce3d0813de6370cb3cfa48 Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 22 Nov 2025 18:26:43 +0700 Subject: [PATCH] focus on cdc test, write lots more data, each trunk is 64 or less since examples having minimum 64 bytes fifo (fs) --- AGENTS.md | 50 +++++++++++++++------------- CLAUDE.md | 78 -------------------------------------------- test/hil/hil_test.py | 57 +++++++++++++++++++++----------- 3 files changed, 66 insertions(+), 119 deletions(-) delete mode 100644 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md index a6163dd42..73bf1f599 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,34 +18,39 @@ information that does not match the info here. - Install ARM GCC toolchain: `sudo apt-get update && sudo apt-get install -y gcc-arm-none-eabi` - Fetch core dependencies: `python3 tools/get_deps.py` -- takes <1 second. NEVER CANCEL. -- For specific board families: `python3 tools/get_deps.py FAMILY_NAME` (e.g., rp2040, stm32f4) +- For specific board families: `python3 tools/get_deps.py FAMILY_NAME` (e.g., rp2040, stm32f4), or + `python3 tools/get_deps.py -b BOARD_NAME` - Dependencies are cached in `lib/` and `hw/mcu/` directories ## Build Examples Choose ONE of these approaches: -**Option 1: Individual Example with CMake (RECOMMENDED)** +**Option 1: Individual Example with CMake and Ninja (RECOMMENDED)** ```bash cd examples/device/cdc_msc mkdir -p build && cd build -cmake -DBOARD=raspberry_pi_pico -DCMAKE_BUILD_TYPE=MinSizeRel .. -cmake --build . -j4 +cmake -DBOARD=raspberry_pi_pico -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel .. +cmake --build . ``` -- takes 1-2 seconds. NEVER CANCEL. Set timeout to 5+ minutes. -**CMake with Ninja (Alternative)** +**Option 2: All Examples for a Board** + +different folder than Option 1 ```bash -cd examples/device/cdc_msc -mkdir build && cd build -cmake -G Ninja -DBOARD=raspberry_pi_pico .. -ninja +cd examples/ +mkdir -p build && cd build +cmake -DBOARD=raspberry_pi_pico -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel .. +cmake --build . ``` -**Option 2: Individual Example with Make** +-- takes 15-20 seconds, may have some objcopy failures that are non-critical. NEVER CANCEL. Set timeout to 30+ minutes. + +**Option 3: Individual Example with Make** ```bash cd examples/device/cdc_msc @@ -54,13 +59,6 @@ make BOARD=raspberry_pi_pico all -- takes 2-3 seconds. NEVER CANCEL. Set timeout to 5+ minutes. -**Option 3: All Examples for a Board** - -```bash -python3 tools/build.py -b BOARD_NAME -``` - --- takes 15-20 seconds, may have some objcopy failures that are non-critical. NEVER CANCEL. Set timeout to 30+ minutes. ## Build Options @@ -101,6 +99,17 @@ python3 tools/build.py -b BOARD_NAME - Run specific test: `cd test/unit-test && ceedling test:test_fifo` - Tests use Unity framework with CMock for mocking +## Hardware-in-the-Loop (HIL) Testing + +- Run tests on actual hardware, one of following ways: + - test a specific board `python test/hil/hil_test.py -b BOARD_NAME -B examples local.json` + - test all boards in config `python test/hil/hil_test.py -B examples local.json` +- In case of error, enabled verbose mode with `-v` flag for detailed logs. Also try to observe script output, and try to + modify hil_test.py (temporarily) to add more debug prints to pinpoint the issue. +- Requires pre-built (all) examples for target boards (see Build Examples section 2) + +take 2-5 minutes. NEVER CANCEL. Set timeout to 20+ minutes. + ## Documentation - Install requirements: `pip install -r docs/requirements.txt` @@ -145,11 +154,8 @@ python3 tools/build.py -b BOARD_NAME - Install pre-commit: `pip install pre-commit && pre-commit install` - Runs all quality checks, unit tests, spell checking, and formatting - Takes 10-15 seconds. NEVER CANCEL. Set timeout to 15+ minutes. -2. **Build validation**: Build at least one example that exercises your changes - ```bash - cd examples/device/cdc_msc - make BOARD=raspberry_pi_pico all - ``` +2. **Build validation**: Build at least one board with all example that exercises your changes, see Build Examples + section (option 2) 3. Run unit tests relevant to touched modules; add fuzz/HIL coverage when modifying parsers or protocol state machines. ### Manual Testing Scenarios diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 6c6baa246..000000000 --- a/CLAUDE.md +++ /dev/null @@ -1,78 +0,0 @@ -# TinyUSB Development Guide - -## Build Commands - -### CMake Build System (Preferred) -CMake with Ninja is the preferred build method for TinyUSB development. - -- Build example with Ninja: - ```bash - cd examples/device/cdc_msc - mkdir build && cd build - cmake -G Ninja -DBOARD=raspberry_pi_pico .. - ninja - ``` -- Debug build: `cmake -G Ninja -DBOARD=raspberry_pi_pico -DCMAKE_BUILD_TYPE=Debug ..` -- With logging: `cmake -G Ninja -DBOARD=raspberry_pi_pico -DLOG=2 ..` -- With RTT logger: `cmake -G Ninja -DBOARD=raspberry_pi_pico -DLOG=2 -DLOGGER=rtt ..` -- Flash with JLink: `ninja cdc_msc-jlink` -- Flash with OpenOCD: `ninja cdc_msc-openocd` -- Generate UF2: `ninja cdc_msc-uf2` -- List all targets: `ninja -t targets` - -### Make Build System (Alternative) -- Build example: `cd examples/device/cdc_msc && make BOARD=raspberry_pi_pico all` -- For specific example: `cd examples/{device|host|dual}/{example_name} && make BOARD=raspberry_pi_pico all` -- Flash with JLink: `make BOARD=raspberry_pi_pico flash-jlink` -- Flash with OpenOCD: `make BOARD=raspberry_pi_pico flash-openocd` -- Debug build: `make BOARD=raspberry_pi_pico DEBUG=1 all` -- With logging: `make BOARD=raspberry_pi_pico LOG=2 all` -- With RTT logger: `make BOARD=raspberry_pi_pico LOG=2 LOGGER=rtt all` -- Generate UF2: `make BOARD=raspberry_pi_pico all uf2` - -### Additional Options -- Select RootHub port: `RHPORT_DEVICE=1` (make) or `-DRHPORT_DEVICE=1` (cmake) -- Set port speed: `RHPORT_DEVICE_SPEED=OPT_MODE_FULL_SPEED` (make) or `-DRHPORT_DEVICE_SPEED=OPT_MODE_FULL_SPEED` (cmake) - -### Dependencies -- Get dependencies: `python tools/get_deps.py rp2040` -- Or from example: `cd examples/device/cdc_msc && make BOARD=raspberry_pi_pico get-deps` - -### Testing -- Run unit tests: `cd test/unit-test && ceedling test:all` -- Run specific test: `cd test/unit-test && ceedling test:test_fifo` - -### Pre-commit Hooks -Before building, it's recommended to run pre-commit to ensure code quality: -- Run pre-commit on all files: `pre-commit run --all-files` -- Run pre-commit on staged files: `pre-commit run` -- Install pre-commit hook: `pre-commit install` - -## Code Style Guidelines -- Use C99 standard -- Memory-safe: no dynamic allocation -- Thread-safe: defer all interrupt events to non-ISR task functions -- 2-space indentation, no tabs -- Use snake_case for variables/functions -- Use UPPER_CASE for macros and constants -- Follow existing variable naming patterns in files you're modifying -- Include proper header comments with MIT license -- Add descriptive comments for non-obvious functions -- When including headers, group in order: C stdlib, tusb common, drivers, classes -- Always check return values from functions that can fail -- Use TU_ASSERT() for error checking with return statements - -## Project Structure -- src/: Core TinyUSB stack code -- hw/: Board support packages and MCU drivers -- examples/: Reference examples for device/host/dual -- test/: Unit tests and hardware integration tests - -## Release Process -To prepare a new release: -1. Update the `version` variable in `tools/make_release.py` to the new version number -2. Run the release script: `python tools/make_release.py` - - This will update version numbers in `src/tusb_option.h`, `repository.yml`, and `library.json` - - It will also regenerate documentation -3. Update `docs/info/changelog.rst` with release notes -4. Commit changes and create release tag diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index 238d452e8..6b7a5ee12 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -32,6 +32,11 @@ import random import re import sys import time +import warnings + +# Suppress pkg_resources deprecation warning from fs module +warnings.filterwarnings("ignore", message="pkg_resources is deprecated") + import serial import subprocess import json @@ -51,6 +56,7 @@ STATUS_SKIPPED = "\033[33mSkipped\033[0m" verbose = False test_only = [] +build_dir = 'cmake-build' WCH_RISCV_CONTENT = """ adapter driver wlinke @@ -405,10 +411,17 @@ def test_device_cdc_dual_ports(board): size = len(payload) for s in ser: s.reset_input_buffer() - ser[writer].write(payload) - ser[writer].flush() - rd0 = ser[0].read(size) - rd1 = ser[1].read(size) + rd0 = b'' + rd1 = b'' + offset = 0 + # Write in chunks of random 1-64 bytes (device has 64-byte buffer) + while offset < size: + chunk_size = min(random.randint(1, 64), size - offset) + ser[writer].write(payload[offset:offset + chunk_size]) + ser[writer].flush() + rd0 += ser[0].read(chunk_size) + rd1 += ser[1].read(chunk_size) + offset += chunk_size assert rd0 == payload.lower(), f'Port0 wrong data ({size}): expected {payload.lower()[:16]}... was {rd0[:16]}' assert rd1 == payload.upper(), f'Port1 wrong data ({size}): expected {payload.upper()[:16]}... was {rd1[:16]}' @@ -434,21 +447,26 @@ def test_device_cdc_msc(board): sizes = [32, 64, 128, 256, 512, random.randint(2000, 5000)] for size in sizes: test_str = rand_ascii(size) - ser.write(test_str) - ser.flush() - rd_str = ser.read(len(test_str)) - assert rd_str == test_str, f'CDC wrong data ({size} bytes):\n expected: {test_str}\n was: {rd_str}' - + rd_str = b'' + offset = 0 + # Write in chunks of random 1-64 bytes (device has 64-byte buffer) + while offset < size: + chunk_size = min(random.randint(1, 64), size - offset) + ser.write(test_str[offset:offset + chunk_size]) + ser.flush() + rd_str += ser.read(chunk_size) + offset += chunk_size + assert rd_str == test_str, f'CDC wrong data ({size} bytes):\n expected: {test_str}\n received: {rd_str}' ser.close() # MSC Block test data = read_disk_file(uid, 0, 'README.TXT') readme = \ b"This is tinyusb's MassStorage Class demo.\r\n\r\n\ - If you find any bugs or get any questions, feel free to file an\r\n\ - issue at github.com/hathach/tinyusb" +If you find any bugs or get any questions, feel free to file an\r\n\ +issue at github.com/hathach/tinyusb" - assert data == readme, 'MSC wrong data' + assert data == readme, f'MSC wrong data in README.TXT\n expected: {readme.decode()}\n received: {data.decode()}' def test_device_cdc_msc_freertos(board): @@ -598,11 +616,11 @@ def test_device_mtp(board): # note don't test 2 examples with cdc or 2 msc next to each other device_tests = [ 'device/cdc_dual_ports', - 'device/dfu', + # 'device/dfu', 'device/cdc_msc', - 'device/dfu_runtime', + # 'device/dfu_runtime', 'device/cdc_msc_freertos', - 'device/hid_boot_interface', + # 'device/hid_boot_interface', # 'device/mtp' ] @@ -630,9 +648,7 @@ def test_example(board, f1, example): if f1 != "": f1_str = '-f1_' + f1.replace(' ', '_') - fw_dir = f'{TINYUSB_ROOT}/cmake-build/cmake-build-{name}{f1_str}/{example}' - if not os.path.exists(fw_dir): - fw_dir = f'{TINYUSB_ROOT}/examples/cmake-build-{name}{f1_str}/{example}' + fw_dir = f'{TINYUSB_ROOT}/{build_dir}/cmake-build-{name}{f1_str}/{example}' fw_name = f'{fw_dir}/{os.path.basename(example)}' print(f'{name+f1_str:40} {example:30} ...', end='') @@ -644,7 +660,7 @@ def test_example(board, f1, example): print(f'Flashing {fw_name}.elf') # flash firmware. It may fail randomly, retry a few times - max_rety = 3 + max_rety = 1 start_s = time.time() for i in range(max_rety): ret = globals()[f'flash_{board["flasher"]["name"].lower()}'](board, fw_name) @@ -720,6 +736,7 @@ def main(): """ global verbose global test_only + global build_dir duration = time.time() @@ -728,6 +745,7 @@ def main(): parser.add_argument('-b', '--board', action='append', default=[], help='Boards to test, all if not specified') parser.add_argument('-s', '--skip', action='append', default=[], help='Skip boards from test') parser.add_argument('-t', '--test-only', action='append', default=[], help='Tests to run, all if not specified') + parser.add_argument('-B', '--build', default='cmake-build', help='Build folder name (default: cmake-build)') parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output') args = parser.parse_args() @@ -736,6 +754,7 @@ def main(): skip_boards = args.skip verbose = args.verbose test_only = args.test_only + build_dir = args.build # if config file is not found, try to find it in the same directory as this script if not os.path.exists(config_file):