Skip to content

test: overhaul firmware test infrastructure (parallel runner, tautology removal, xs unification, device smoke) - #543

Merged
meganetaaan merged 7 commits into
developfrom
improve-firmware-test-infra
Jul 12, 2026
Merged

test: overhaul firmware test infrastructure (parallel runner, tautology removal, xs unification, device smoke)#543
meganetaaan merged 7 commits into
developfrom
improve-firmware-test-infra

Conversation

@meganetaaan

@meganetaaan meganetaaan commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

概要

firmwareテストのレビュー結果に基づく4領域の改善です。

1. Moddableテストランナーの高速化(インクリメンタル+並列)

run-module-tests.js は毎回クリーンビルド×完全直列でした。mcconfigはmakefileを毎回再生成し mc.xs.c がmanifest include チェーンに依存するためインクリメンタルビルドは安全と確認し、クリーン削除を STACKCHAN_MODULE_TEST_CLEAN=1 に退避。ワーカープール(STACKCHAN_MODULE_TEST_JOBS、既定 min(4, cores))で並列化しました。

mcsimの並列実行には2つの隔離が必要でした:

  • DBusセッション分離dbus-run-session): mcsimは固定application-idのGtkApplicationで、同一バス上の2個目は1個目に委譲して即終了する
  • XDG_CONFIG_HOME分離: mcsimは mc.so を config dir にコピーして実行するため、共有すると相互にSIGBUS
フルスイート 変更前 変更後
コールド 約10分 3分39秒
ウォーム 約10分 1分未満

2. 追認型テストの削減

実装行・manifest値をそのまま再掲するarchitectureテストを削除・変換しました。純ミラーの5ファイル(app-capabilities / splash-view / camera / consts.test / input-event.test)を削除、7ファイルをglob型不変条件・レイヤ境界・tombstoneのみにトリム。実質的な制約は関係チェックに変換:サブプラットフォームを動的走査してベースターゲットとのcreation一致を検証、defines.camera を持つsubplatformの camera/conversation manifest ゲート必須チェック(過去に実際に発生した追加漏れを検出可能)など。非自明なwhyはソースコメントへ移設。

3. Node/xs重複テストのxs一本化

motion-controller(ほぼ逐語重複)と camera-preview-utils(xs版が部分集合)を、Node版のみのケースを移植した上でxs版へ一本化。

4. スモーク統合+実機スモークランナー

コンストラクタスモーク級のテスト9件を module-smoke / dialogue-smoke の複合manifestへ統合(29→22 manifest)。npm run test:device を新設し、mcrun -dn+xsbugログサーバで実機スモークMODの完走を自動判定(CoreS3のxsbugブリッジ不安定対策のリトライ、--channel serial フォールバック付き)。

リリース影響

none — テスト・CI・開発スクリプトのみの変更のため、リリースノート/changesetは不要です。プロダクションソースへの変更はwhyコメント2箇所(main.ts の起動順序、ft6206_async_m5stackchan.js のポーリング理由)のみで動作変更はありません。CI変更はaptパッケージ dbus の明示追加のみ。

検証

origin/develop ベースで以下すべて通過:

  • npm run lint / npm run format
  • npm run test:unit(141件)
  • npm run check:architecture(53件)
  • npm run check:legacy-names / npm run check:manifest(6ターゲット)
  • npm run test:moddable(22 manifest、5回連続の並列安定性確認済み)
  • npm run smoke:lin

補足:ローカルで mcp-server-service テストが失敗しますが、開発機の live-server が8080を占有しているためで、JOBS=1(従来同等の直列動作)でも再現する既存の環境衝突です。本PRとは無関係で、CIでは発生しません。

未検証: test:device の実デバイス経路はハードウェア未接続のため未実施です(引数処理・エラー経路のみ確認済み)。実機での動作確認をお願いします。

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added an automated on-device smoke testing flow with configurable target, MOD, upload port, channel, timeout, and retry behavior.
    • Added a new npm command to run the device smoke test.
  • Tests

    • Refreshed/expanded smoke coverage for modules including audio, connectivity, and provider dialogues.
    • Updated module test execution to run in parallel with improved isolation and clearer failure reporting.
  • Documentation

    • Expanded testing guidance, including detailed instructions for the automated device smoke runner.
  • Chores

    • Updated the Linux test environment to include an additional package.

meganetaaan and others added 5 commits July 12, 2026 10:09
Stop deleting build output before every run (mcconfig regenerates the
makefile each invocation and mc.xs.c depends on the manifest include
chain, so incremental builds are safe; STACKCHAN_MODULE_TEST_CLEAN=1
restores clean builds) and run tests through a worker pool
(STACKCHAN_MODULE_TEST_JOBS, default min(4, cores)).

Parallel mcsim instances need two isolations: a private DBus session per
test (mcsim is a fixed-application-id GtkApplication, so a second
instance on the same bus hands off to the first and exits 0) and a
per-test XDG_CONFIG_HOME (mcsim copies mc.so into its config directory
and concurrent instances SIGBUS each other). One warm-up build per
platform segment avoids racing on the shared XS core lib directory.

Full suite: ~10min serial -> 3m39s cold / under 1min warm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nvariants

Architecture tests that re-asserted implementation lines, manifest
values, or constants broke on every legitimate edit while catching no
regressions. Delete the pure mirrors (app-capabilities, splash-view,
camera, consts.test, input-event.test) and trim the mixed files down to
glob-based invariants, layer boundaries, and tombstones.

Where a deleted assertion encoded a real constraint, keep it as a
relational check instead of a literal mirror: subplatforms are now
discovered dynamically and compared against their base target's
creation block, camera-defining subplatforms must be gated into the
camera/conversation manifests (catches the historical
new-subplatform-forgotten case the old hardcoded list could not), and
config.serial must align with config.driver.serial. Non-derivable
whys move into source comments (boot ordering in main.ts, touch
polling mode in the FT6206 driver).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ts into xs

The Node and xs variants were near-verbatim duplicates (motion-controller)
or the xs variant was a strict subset (camera-preview-utils). Port the
Node-only cases (gaze release via lookAt(undefined), close cancelling
polling and torque-release timers, duration helpers, big-endian
conversion, frame copy byte order) into the xs tests, which run on the
real XS engine, and delete the Node copies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Each manifest.test.json pays a full mcconfig build, which is not worth
it for 10-line construct-and-trace-ok smokes. Merge the TTS engines,
stt-whisper, network-service, and the Claude/Gemini dialogue smokes into
host/modules/__tests__/module-smoke and
mods/examples/provider-dialogues/__tests__/dialogue-smoke (29 -> 22
manifests). The led smoke stays separate because it is lighting's only
test and check:architecture requires each runtime module to own one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
npm run test:device installs the m5stackchan_smoke MOD through
mcrun -dn (no xsbug GUI), bridges device traces via serial2xsbug into
the shared xsbug log server, and judges pass/fail from the smoke MOD's
completion line with automatic retries for the flaky CoreS3 xsbug
serial bridge. --channel serial falls back to watching the raw console
for crash markers (boot-stability only; trace() is not visible on raw
serial). The device table moves to scripts/lib/devices.mjs, shared with
firmware.mjs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f12b3dd9-ef56-40a2-8e82-9c4d5deeb2e4

📥 Commits

Reviewing files that changed from the base of the PR and between 157d1a8 and dbf39fb.

📒 Files selected for processing (7)
  • firmware/host/modules/motion/__tests__/motion-controller/motion-controller.test.ts
  • firmware/host/modules/ui/state/face-state.architecture.ts
  • firmware/scripts/firmware.mjs
  • firmware/scripts/lib/devices.mjs
  • firmware/scripts/lib/xsbug-log-server.js
  • firmware/scripts/run-device-smoke.js
  • firmware/scripts/run-module-tests.js
🚧 Files skipped from review as they are similar to previous changes (6)
  • firmware/scripts/lib/xsbug-log-server.js
  • firmware/host/modules/motion/tests/motion-controller/motion-controller.test.ts
  • firmware/scripts/run-device-smoke.js
  • firmware/host/modules/ui/state/face-state.architecture.ts
  • firmware/scripts/firmware.mjs
  • firmware/scripts/run-module-tests.js

📝 Walkthrough

Walkthrough

Changes

Testing and smoke execution

Layer / File(s) Summary
Device smoke runner and execution support
firmware/scripts/run-device-smoke.js, firmware/scripts/lib/*, firmware/package.json, firmware/docs/*, .github/actions/setup/action.yml
Adds device smoke execution over xsbug or serial, shared device configuration, log-server handling, retries, CLI options, documentation, and the test:device script.
Consolidated module smoke manifests
firmware/host/modules/__tests__/module-smoke/*, firmware/mods/examples/provider-dialogues/__tests__/dialogue-smoke/*, CLAUDE.md
Consolidates constructor smoke checks for audio, STT, network, and dialogue modules and documents the resulting test organization.
Concurrent Moddable test execution
firmware/scripts/run-module-tests.js, firmware/scripts/lib/xsbug-log-server.js
Runs module tests through isolated concurrent workers with asynchronous process handling, DBus support, warm-up builds, and structured failure reporting.
Architecture and manifest contract coverage
firmware/host/modules/**/__tests__/*, firmware/host/platforms/**, firmware/mods/examples/**
Reworks architecture checks around module boundaries, resource ownership, namespaced capabilities, platform manifest gating, legacy-module removal, and WASM facade mappings.
Behavior and conversion test updates
firmware/host/modules/motion/**, firmware/host/modules/ui/**, firmware/host/platforms/wasm/wasm-stubs.test.ts
Adds motion lifecycle and conversion assertions, strengthens RGB565 and mosaic tests, makes cache expectations implementation-independent, and narrows synthetic-camera checks.
Platform discovery and launch wiring
firmware/host/platforms/platform-manifest.architecture.ts, firmware/host/app/main.ts, firmware/host/platforms/m5stackchan_cores3/host/ft6206_async_m5stackchan.js
Generalizes subplatform validation and documents splash-screen ordering and CoreS3 timer-based touch polling.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant DeviceSmokeRunner
  participant XsbugLogServer
  participant Mcrun
  participant Device
  Operator->>DeviceSmokeRunner: run test:device
  DeviceSmokeRunner->>XsbugLogServer: start temporary server
  DeviceSmokeRunner->>Mcrun: launch smoke manifest
  Mcrun->>Device: install and execute MOD
  Device->>XsbugLogServer: send execution logs
  XsbugLogServer->>Mcrun: respond with <go/>
  DeviceSmokeRunner->>Operator: report result or retry
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.90% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the PR’s main themes: test infrastructure overhaul, test simplification, XS unification, and device smoke support.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch improve-firmware-test-infra

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 157d1a895d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread firmware/scripts/run-device-smoke.js

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
firmware/scripts/lib/devices.mjs (1)

1-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider exporting resolveDevice alongside devices and aliases.

Both firmware.mjs (lines 134-140) and run-device-smoke.js (lines 49-55) define near-identical resolveDevice functions that differ only in the log prefix ([stack-chan] vs [device-smoke]). Since this module was created to centralize device configuration, exporting a shared resolveDevice here would eliminate the duplication.

♻️ Suggested addition to `devices.mjs`
 export const aliases = {
   default: 'm5stackchan_cores3',
   m5stackchan: 'm5stackchan_cores3',
   rt: 'stackchan_rt',
   takao: 'takao_core2_sg90',
 }
+
+export function resolveDevice(value, prefix = '[stack-chan]') {
+  const name = aliases[value] ?? value
+  if (devices[name]) return name
+  console.error(`${prefix} Unknown device: ${value}`)
+  console.error(`${prefix} Supported devices: ${Object.keys(devices).join(', ')}`)
+  process.exit(1)
+}

Then in firmware.mjs and run-device-smoke.js, replace the local resolveDevice with the imported one (passing the appropriate prefix).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@firmware/scripts/lib/devices.mjs` around lines 1 - 24, Export a shared
resolveDevice function from the devices module alongside devices and aliases,
accepting the caller’s log prefix so it preserves the existing firmware and
smoke-test messages. Remove the duplicate local resolveDevice implementations in
firmware.mjs and run-device-smoke.js, import the shared function, and update
their calls to pass the appropriate prefix.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@firmware/host/modules/ui/state/face-state.architecture.ts`:
- Around line 62-91: Add a non-empty result assertion immediately after each
extractMethodBlocks(source, 'onFaceState') call in the two tests, before
iterating, so both tests fail when no onFaceState block is detected. Keep the
existing per-block allocation assertions unchanged.

In `@firmware/scripts/lib/xsbug-log-server.js`:
- Around line 42-49: Update the server setup and returned close method in the
log-server factory to track each connected socket, remove sockets when they
close, and destroy all remaining sockets before or while invoking
server.close(). Ensure the close promise resolves deterministically even when
clients keep connections open, while preserving the existing ready and getLog
behavior.

---

Nitpick comments:
In `@firmware/scripts/lib/devices.mjs`:
- Around line 1-24: Export a shared resolveDevice function from the devices
module alongside devices and aliases, accepting the caller’s log prefix so it
preserves the existing firmware and smoke-test messages. Remove the duplicate
local resolveDevice implementations in firmware.mjs and run-device-smoke.js,
import the shared function, and update their calls to pass the appropriate
prefix.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 420df1d6-7f56-430c-9a01-82205ce773bb

📥 Commits

Reviewing files that changed from the base of the PR and between 459a347 and 157d1a8.

📒 Files selected for processing (53)
  • .github/actions/setup/action.yml
  • CLAUDE.md
  • firmware/docs/m5stackchan-cores3-smoke.md
  • firmware/host/app/app-capabilities.architecture.ts
  • firmware/host/app/main.ts
  • firmware/host/modules/__tests__/module-smoke/manifest.test.json
  • firmware/host/modules/__tests__/module-smoke/module-smoke.test.ts
  • firmware/host/modules/audio/__tests__/audio-buffer-ownership.architecture.ts
  • firmware/host/modules/audio/__tests__/stt-whisper/stt-whisper.test.ts
  • firmware/host/modules/audio/__tests__/tts-elevenlabs/manifest.test.json
  • firmware/host/modules/audio/__tests__/tts-elevenlabs/tts-elevenlabs.test.ts
  • firmware/host/modules/audio/__tests__/tts-local/manifest.test.json
  • firmware/host/modules/audio/__tests__/tts-local/tts-local.test.ts
  • firmware/host/modules/audio/__tests__/tts-openai/manifest.test.json
  • firmware/host/modules/audio/__tests__/tts-openai/tts-openai.test.ts
  • firmware/host/modules/audio/__tests__/tts-voicevox-web/manifest.test.json
  • firmware/host/modules/audio/__tests__/tts-voicevox-web/tts-voicevox-web.test.ts
  • firmware/host/modules/audio/__tests__/tts-voicevox/manifest.test.json
  • firmware/host/modules/audio/__tests__/tts-voicevox/tts-voicevox.test.ts
  • firmware/host/modules/camera/camera.architecture.ts
  • firmware/host/modules/connectivity/__tests__/network-service/manifest.test.json
  • firmware/host/modules/connectivity/__tests__/network-service/network-service.test.ts
  • firmware/host/modules/input/input-event.test.ts
  • firmware/host/modules/motion/__tests__/m5stackchan-servo.test.ts
  • firmware/host/modules/motion/__tests__/motion-controller.test.ts
  • firmware/host/modules/motion/__tests__/motion-controller/motion-controller.test.ts
  • firmware/host/modules/preferences/consts.architecture.ts
  • firmware/host/modules/preferences/consts.test.ts
  • firmware/host/modules/testing/module-structure.architecture.ts
  • firmware/host/modules/ui/application/app-controller.architecture.ts
  • firmware/host/modules/ui/components/face/parts/__tests__/shape-cache.test.ts
  • firmware/host/modules/ui/components/face/parts/image/__tests__/image-avatar-pack.architecture.ts
  • firmware/host/modules/ui/components/face/parts/image/__tests__/image-avatar-pack.test.ts
  • firmware/host/modules/ui/state/face-state.architecture.ts
  • firmware/host/modules/ui/views/camera-preview/__tests__/camera-preview-utils/camera-preview-utils.test.ts
  • firmware/host/modules/ui/views/camera-preview/camera-preview-utils.test.ts
  • firmware/host/modules/ui/views/settings/settings-view.architecture.ts
  • firmware/host/modules/ui/views/splash/splash-view.architecture.ts
  • firmware/host/platforms/m5stackchan_cores3/host/ft6206_async_m5stackchan.js
  • firmware/host/platforms/platform-manifest.architecture.ts
  • firmware/host/platforms/wasm/wasm-stubs.architecture.ts
  • firmware/host/platforms/wasm/wasm-stubs.test.ts
  • firmware/mods/examples/provider-dialogues/__tests__/dialogue-claude/dialogue-claude.test.ts
  • firmware/mods/examples/provider-dialogues/__tests__/dialogue-claude/manifest.test.json
  • firmware/mods/examples/provider-dialogues/__tests__/dialogue-gemini/dialogue-gemini.test.ts
  • firmware/mods/examples/provider-dialogues/__tests__/dialogue-smoke/dialogue-smoke.test.ts
  • firmware/mods/examples/provider-dialogues/__tests__/dialogue-smoke/manifest.test.json
  • firmware/package.json
  • firmware/scripts/firmware.mjs
  • firmware/scripts/lib/devices.mjs
  • firmware/scripts/lib/xsbug-log-server.js
  • firmware/scripts/run-device-smoke.js
  • firmware/scripts/run-module-tests.js
💤 Files with no reviewable changes (26)
  • firmware/host/modules/connectivity/tests/network-service/manifest.test.json
  • firmware/host/modules/ui/views/splash/splash-view.architecture.ts
  • firmware/mods/examples/provider-dialogues/tests/dialogue-claude/manifest.test.json
  • firmware/host/modules/audio/tests/tts-voicevox/tts-voicevox.test.ts
  • firmware/host/modules/audio/tests/tts-elevenlabs/tts-elevenlabs.test.ts
  • firmware/host/app/app-capabilities.architecture.ts
  • firmware/mods/examples/provider-dialogues/tests/dialogue-claude/dialogue-claude.test.ts
  • firmware/host/modules/preferences/consts.test.ts
  • firmware/host/modules/audio/tests/tts-openai/manifest.test.json
  • firmware/host/modules/audio/tests/tts-local/tts-local.test.ts
  • firmware/host/modules/audio/tests/tts-openai/tts-openai.test.ts
  • firmware/host/modules/audio/tests/tts-voicevox-web/manifest.test.json
  • firmware/host/modules/motion/tests/motion-controller.test.ts
  • firmware/host/modules/audio/tests/stt-whisper/stt-whisper.test.ts
  • firmware/host/modules/ui/views/camera-preview/camera-preview-utils.test.ts
  • firmware/host/modules/input/input-event.test.ts
  • firmware/mods/examples/provider-dialogues/tests/dialogue-gemini/dialogue-gemini.test.ts
  • firmware/host/modules/audio/tests/tts-elevenlabs/manifest.test.json
  • firmware/host/modules/audio/tests/tts-voicevox/manifest.test.json
  • firmware/host/modules/audio/tests/tts-voicevox-web/tts-voicevox-web.test.ts
  • firmware/host/modules/audio/tests/tts-local/manifest.test.json
  • firmware/host/modules/connectivity/tests/network-service/network-service.test.ts
  • firmware/host/modules/camera/camera.architecture.ts
  • firmware/host/modules/preferences/consts.architecture.ts
  • firmware/host/modules/ui/components/face/parts/image/tests/image-avatar-pack.test.ts
  • firmware/host/modules/motion/tests/m5stackchan-servo.test.ts

Comment thread firmware/host/modules/ui/state/face-state.architecture.ts
Comment thread firmware/scripts/lib/xsbug-log-server.js
meganetaaan and others added 2 commits July 12, 2026 11:35
biome ci enforces organizeImports, which lint/format do not. The piu
test builds timed out on 4-core CI runners: each mcconfig already
compiles with make --jobs 8 internally, so four concurrent builds
oversubscribed the runner. Default the worker pool to
min(4, cores - 2) and double the build timeout for cold caches.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- assert extractMethodBlocks found an onFaceState block before iterating,
  so a regex miss cannot silently pass the allocation checks
- destroy lingering sockets in the xsbug log server's close() so shutdown
  cannot hang on a surviving simulator or serial2xsbug connection
- share resolveDevice from scripts/lib/devices.mjs instead of duplicating
  it in firmware.mjs and run-device-smoke.js

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant