-
Notifications
You must be signed in to change notification settings - Fork 4
hardware in the loop testing #376
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
Merged
Merged
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
fd88449
docs: add hardware-in-the-loop testing design proposal
claude 41f53e3
ci: pin all actions and Python dependencies; update HIL design
claude 9653c32
docs: add HIL runner setup runbook for the Raspberry Pi
claude 984c4a9
docs: cut runner setup down to the minimum
claude c06c794
docs: simplify runner setup for the actual bench
claude f6c433b
docs: package runner setup as a Raspberry Pi Connect artefact
claude d7e15a5
build: commit prebuilt Connect update artefact and checksum
claude 02da188
docs: add pre-merge artefact URL for testing
claude 7567cdb
docs: replace OTA artefact with a plain setup script
claude 962e256
ci: add HIL bench smoke workflow
claude f08ac74
ci: put the venv on PATH for HIL smoke
claude cad0383
fix: pin the bench clone to a known ref, guard .env values
claude 9761bf0
ci: flash every bench board and health-check its API
claude 25ae1ff
feat: add board identify mode to the HIL harness [skip ci]
claude e6ef1e3
fix: make the HIL harness find its own tools [skip ci]
claude 3f18e6e
ci: record per-stage timings in the HIL harness
claude f8962aa
fix: decompress gzip and retry in the HIL HTTP checks
claude a389523
docs: correct the bare-board fault predictions with measured results …
claude bbdbb31
docs: replace HIL throughput estimates with measured numbers
claude 17316c5
feat: wait on the boot console and broaden the HTTP API checks
claude be59fc6
fix: reset each board before watching for its ready marker
claude 42f91e5
fix: allow plain-text routes and prime the USB link before use
claude 8967ccb
Merge branch 'main' into claude/hil-testing-design-s564ln
mullinmax 74b829c
Merge branch 'main' into claude/hil-testing-design-s564ln
mullinmax File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,5 @@ | ||
| # Auto detect text files and perform LF normalization | ||
| * text=auto | ||
|
|
||
| # Bench setup scripts run as POSIX sh on the Pi; CRLF would break the shebang. | ||
| dev/hil/*.sh text eol=lf |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| name: HIL flash and health check | ||
|
|
||
| # Builds firmware for each attached board, flashes it, and health-checks the | ||
| # API over both USB and HTTP once the board boots. | ||
| # | ||
| # Deliberately no `pull_request` trigger — this targets a self-hosted runner on | ||
| # a private network and physically reflashes hardware. Fork gating is designed | ||
| # in dev/hil/DESIGN.md §4 and is not built yet. | ||
| # | ||
| # The `push` trigger exists so this can be validated before merging, since a | ||
| # workflow_dispatch workflow is not dispatchable until it reaches the default | ||
| # branch. Drop the push trigger once this is on main. | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| inputs: | ||
| skip_http: | ||
| description: "USB checks only (skip the network stack)" | ||
| type: boolean | ||
| default: false | ||
| skip_flash: | ||
| description: "Health-check what is already flashed, do not reflash" | ||
| type: boolean | ||
| default: false | ||
| inventory_only: | ||
| description: "Just print each board's chip id, for building VECTOR_HIL_BOARD_MAP" | ||
| type: boolean | ||
| default: false | ||
| identify: | ||
| description: "Blink each board in turn so you can see which physical board is which" | ||
| type: boolean | ||
| default: false | ||
| push: | ||
| branches: | ||
| - claude/hil-testing-design-s564ln | ||
| paths: | ||
| - dev/hil/flash_and_check.py | ||
| - .github/workflows/hil-flash-check.yml | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| # Flashing is destructive and the bench is one set of boards. Never interleave. | ||
| concurrency: | ||
| group: hil-bench | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| flash-and-check: | ||
| runs-on: [self-hosted, vector-hil] | ||
| timeout-minutes: 45 | ||
|
|
||
| steps: | ||
| # actions/checkout is safe here ONLY because every trigger above is | ||
| # repo-internal. If a fork-reachable trigger is ever added, this must go | ||
| # back to running from the runner's pinned clone ($VECTOR_HIL_REPO) — | ||
| # otherwise a fork PR would execute its own harness code on the bench. | ||
| - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 | ||
|
|
||
| - name: Prepare environment | ||
| run: | | ||
| test -x "$VECTOR_HIL_VENV/bin/python" || { echo "bench venv missing"; exit 1; } | ||
| echo "$VECTOR_HIL_VENV/bin" >> "$GITHUB_PATH" | ||
|
|
||
| # VECTOR_HIL_BOARD_MAP, VECTOR_HIL_WIFI_* and friends arrive from the | ||
| # runner's .env, which the runner process exports into every job. They | ||
| # are deliberately NOT restated in an `env:` block here: the `env` | ||
| # context only covers workflow/job/step-level vars, so `${{ env.X }}` | ||
| # would evaluate to empty and shadow the real value. | ||
| - name: Flash and health-check every board | ||
| run: | | ||
| args="" | ||
| if [ "${{ inputs.skip_http }}" = "true" ]; then args="$args --skip-http"; fi | ||
| if [ "${{ inputs.skip_flash }}" = "true" ]; then args="$args --skip-flash"; fi | ||
| if [ "${{ inputs.inventory_only }}" = "true" ]; then args="$args --inventory-only"; fi | ||
| if [ "${{ inputs.identify }}" = "true" ]; then args="$args --identify"; fi | ||
| python dev/hil/flash_and_check.py $args | ||
|
|
||
| - name: Board serial logs on failure | ||
| if: failure() | ||
| run: | | ||
| # A board that failed its health check may still be printing something | ||
| # useful; grab a few seconds of console from each. | ||
| for dev in /dev/ttyACM*; do | ||
| echo "--- $dev" | ||
| timeout 8 cat "$dev" || true | ||
| done |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| name: HIL smoke | ||
|
|
||
| # Proves the bench runner is wired up correctly: the runner picks up jobs, the | ||
| # bench environment reaches them, the boards enumerate, and each one answers | ||
| # over serial. It does not test firmware — see dev/hil/DESIGN.md for that. | ||
| # | ||
| # Deliberately no `pull_request` trigger. This targets a self-hosted runner on | ||
| # a private network, and a pull_request trigger would let any fork PR run code | ||
| # against it. Fork gating is designed in DESIGN.md §4 and is not built yet. | ||
| # | ||
| # The `push` trigger exists so this can be validated before merging, since a | ||
| # workflow_dispatch workflow is not dispatchable until it reaches the default | ||
| # branch. Drop the push trigger once this is on main. | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| push: | ||
| branches: | ||
| - claude/hil-testing-design-s564ln | ||
|
|
||
| permissions: {} | ||
|
|
||
| # The bench is a single set of physical boards. Queue, never interleave. | ||
| concurrency: | ||
| group: hil-bench | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| smoke: | ||
| runs-on: [self-hosted, vector-hil] | ||
| timeout-minutes: 15 | ||
|
|
||
| # No actions/checkout on purpose: under the design's trust model the bench | ||
| # runs harness code from a trusted checkout the runner already has, not | ||
| # from the PR. $VECTOR_HIL_REPO points at it. | ||
|
|
||
| steps: | ||
| - name: Runner host | ||
| run: | | ||
| echo "user: $(whoami)" | ||
| echo "kernel: $(uname -srm)" | ||
| echo "arch: $(dpkg --print-architecture)" | ||
| free -h | awk '/Mem:/ {print "memory: " $2 " total, " $7 " available"}' | ||
| echo "uptime:$(uptime -p | sed 's/^up//')" | ||
|
|
||
| - name: Bench environment | ||
| run: | | ||
| fail=0 | ||
| for var in VECTOR_HIL_REPO VECTOR_HIL_VENV VECTOR_HIL_WIFI_SSID VECTOR_HIL_WIFI_PASSWORD; do | ||
| eval "val=\${$var:-}" | ||
| if [ -z "$val" ]; then | ||
| echo "MISSING: $var" | ||
| fail=1 | ||
| else | ||
| echo "set: $var" | ||
| fi | ||
| done | ||
| [ "$fail" -eq 0 ] || { | ||
| echo "The runner's .env is incomplete. It is read at service start:" | ||
| echo " cd ~/actions-runner && sudo ./svc.sh stop && sudo ./svc.sh start" | ||
| exit 1 | ||
| } | ||
| test -x "$VECTOR_HIL_VENV/bin/python" || { echo "venv python missing at $VECTOR_HIL_VENV"; exit 1; } | ||
| test -d "$VECTOR_HIL_REPO/.git" || { echo "no clone at $VECTOR_HIL_REPO"; exit 1; } | ||
|
|
||
| # dev/detect_boards.py shells out to a bare `mpremote` from PATH and | ||
| # silently returns {} when it is missing, so the venv must be ON PATH | ||
| # for later steps - invoking its python by absolute path is not enough. | ||
| echo "$VECTOR_HIL_VENV/bin" >> "$GITHUB_PATH" | ||
|
|
||
| - name: Serial access | ||
| run: | | ||
| if ! ls /dev/ttyACM* >/dev/null 2>&1; then | ||
| echo "no /dev/ttyACM* devices - check the USB hub and power" | ||
| exit 1 | ||
| fi | ||
| ls -l /dev/ttyACM* | ||
| id -nG | grep -qw dialout || { | ||
| echo "runner user is not in the dialout group; reboot or restart the service" | ||
| exit 1 | ||
| } | ||
|
|
||
| - name: mpremote sees the boards | ||
| run: | | ||
| # Checked separately because detect_boards.py collapses "mpremote is | ||
| # missing", "mpremote failed", and "no boards attached" into the same | ||
| # empty result, which makes a real failure indistinguishable from an | ||
| # empty bench. | ||
| command -v mpremote || { echo "mpremote not on PATH"; exit 1; } | ||
| mpremote version | ||
|
|
||
| echo "--- mpremote devs ---" | ||
| mpremote devs | tee /tmp/devs.txt | ||
| test -s /tmp/devs.txt || { | ||
| echo "mpremote sees no devices, though /dev/ttyACM* exist." | ||
| echo "Boards may be busy or in an odd state; power-cycle the hub and retry." | ||
| exit 1 | ||
| } | ||
|
|
||
| - name: Detect and query boards | ||
| run: | | ||
| # detect_boards.py prints progress text before the JSON, so the | ||
| # machine-readable part is the last line only. | ||
| raw=$(python "$VECTOR_HIL_REPO/dev/detect_boards.py") | ||
| echo "$raw" | ||
| json=$(printf '%s\n' "$raw" | tail -1) | ||
|
|
||
| python - "$json" <<'PY' | ||
| import json, subprocess, sys | ||
|
|
||
| boards = json.loads(sys.argv[1]) | ||
|
|
||
| if not boards: | ||
| sys.exit("no boards detected") | ||
|
|
||
| rc = 0 | ||
| for kind, ports in sorted(boards.items()): | ||
| for port in ports: | ||
| probe = subprocess.run( | ||
| ["mpremote", "connect", port, "exec", | ||
| "import systemConfig; print(systemConfig.vectorSystem, systemConfig.SystemVersion)"], | ||
| capture_output=True, text=True, timeout=60) | ||
| if probe.returncode: | ||
| print(f"FAIL {kind:12} {port:16} {probe.stderr.strip()}") | ||
| rc = 1 | ||
| else: | ||
| print(f"ok {kind:12} {port:16} {probe.stdout.strip()}") | ||
|
|
||
| print(f"\n{sum(len(p) for p in boards.values())} board(s) responding") | ||
| sys.exit(rc) | ||
| PY | ||
|
|
||
| - name: Summary | ||
| if: always() | ||
| run: echo "Bench smoke ${{ job.status }}" | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.