Skip to content

test: Verify each driver is frozen in the upstream firmware manifest.#393

Merged
nedseb merged 2 commits intomainfrom
test/frozen-manifest-check
Apr 14, 2026
Merged

test: Verify each driver is frozen in the upstream firmware manifest.#393
nedseb merged 2 commits intomainfrom
test/frozen-manifest-check

Conversation

@nedseb
Copy link
Copy Markdown
Contributor

@nedseb nedseb commented Apr 14, 2026

Summary

  • Add tests/test_frozen_manifest.py: one parametrized case per lib/*/, asserts the driver name is required in the upstream STEAM32_WB55RG manifest.
  • Mark gc9a01 and im34dt05 as .not-frozen (currently not shipped in the firmware). The marker file contains a one-line reason shown in the skip message.
  • Document the check and the .not-frozen opt-out in CONTRIBUTING.md.

Why

Two regressions slipped through in the past because no test watched the frozen manifest:

How it works

The test fetches the raw manifest from steamicc/micropython-steami@stm32-steami-rev1d-final once per session, extracts every require("<name>", library="micropython-steami-lib"), and asserts each lib/<dir>/ appears in that set. Network failures skip cleanly, so offline dev and CI flakes don't produce false negatives.

The URL is overridable via STEAMI_FIRMWARE_MANIFEST_URL for forks or local manifest testing.

Test plan

  • make test-mock → 15 passed + 2 skipped (gc9a01, im34dt05) on the new test.
  • Regression simulation (STEAMI_FIRMWARE_MANIFEST_URL=file:///tmp/fake_manifest.py) with only hts221 in the file → 14 failed with actionable messages, 1 passed (hts221), 2 skipped.
  • Offline simulation (unreachable URL) → all 17 cases cleanly skipped.
  • make lint passes.

Closes #392.

Adds a parametrized pytest test that generates one case per `lib/*/` and
asserts the driver name is declared in the STEAM32_WB55RG manifest hosted
in steamicc/micropython-steami. Catches regressions like the accidental
removal of mcp23009e and the forgotten steami_screen.

The upstream manifest is fetched at test time (no dependency on a local
clone), with an env override for forks. A `lib/<driver>/.not-frozen` marker
opts a driver out with a one-line reason — used here for gc9a01 (pending
freeze, #368) and im34dt05 (not yet integrated). Network failures skip
cleanly so offline dev is unaffected.

Closes #392.
Copilot AI review requested due to automatic review settings April 14, 2026 08:16
@nedseb nedseb added bug Something isn't working test labels Apr 14, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a CI-facing safeguard to ensure every driver under lib/ is represented in the upstream MicroPython firmware frozen-manifest, with an explicit per-driver opt-out mechanism and documentation.

Changes:

  • Add tests/test_frozen_manifest.py to fetch and validate the upstream STEAM32_WB55RG manifest against lib/*/ drivers.
  • Add .not-frozen marker files for gc9a01 and im34dt05 to intentionally skip checks for drivers not shipped in firmware yet.
  • Document the frozen-manifest check and .not-frozen opt-out in CONTRIBUTING.md.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
tests/test_frozen_manifest.py New test that fetches the upstream board manifest and asserts each lib/<driver>/ is required (or skipped via .not-frozen).
lib/gc9a01/.not-frozen Marks gc9a01 as intentionally not frozen (skipped by the new test).
lib/im34dt05/.not-frozen Marks im34dt05 as intentionally not frozen (skipped by the new test).
CONTRIBUTING.md Documents the new frozen-manifest verification and the .not-frozen opt-out.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +36 to +37
except (URLError, TimeoutError, OSError) as exc:
pytest.skip(f"cannot fetch upstream manifest: {exc}")
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

The exception handling treats all URLError as an offline/network condition and skips the entire check. urllib.error.HTTPError is a URLError, so a 404/403 (e.g., upstream path moved, branch renamed, rate-limited) will silently skip and permanently disable this protection in CI. Consider handling HTTPError separately (fail with a clear message for non-2xx responses, or only skip on genuine connectivity/timeouts).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 34281f7: HTTPError is caught separately and fails the test loudly with a pointer to MANIFEST_URL, so a renamed branch or moved path cannot silently disable the check. Only URLError / TimeoutError / OSError still skip (genuine offline cases).

Comment thread tests/test_frozen_manifest.py Outdated
Comment on lines +24 to +26
REQUIRE_RE = re.compile(
r'require\(\s*"([^"]+)"\s*,\s*library\s*=\s*"micropython-steami-lib"\s*\)'
)
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

REQUIRE_RE is very formatting-specific: it only matches double-quoted require("name", library="micropython-steami-lib") with no extra kwargs and with library in that exact position. A semantically equivalent manifest using single quotes, additional keyword args, or different argument ordering will produce an empty/partial frozen_drivers set and cause false failures. To make the test resilient, parse the manifest as Python (e.g., via ast) and extract require() calls by inspecting call nodes/keywords, or broaden the pattern to accept both quote styles and additional args.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 34281f7: replaced the regex with an ast-based walker that inspects require() call nodes and matches on the library= keyword. Verified locally against a manifest using single quotes, extra keyword arguments, and both — both parse correctly.

Comment thread CONTRIBUTING.md Outdated
* The directory name must match the driver name (e.g. `mcp23009e`, `wsen-hids`)
* The main class must be exposed in `__init__.py`
* Drivers must be self-contained (no cross-driver dependencies)
* Every driver is automatically checked against the upstream firmware manifest by `tests/test_frozen_manifest.py`. If a driver is intentionally **not** frozen (experimental, not yet integrated, etc.), add an empty `lib/<driver>/.not-frozen` file containing a one-line reason — the test will skip it with that reason displayed.
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

This bullet is internally inconsistent: it says to add an empty .not-frozen file while also saying it should contain a one-line reason. Consider rewording to “add a lib/<driver>/.not-frozen marker file (may be empty, optionally containing a one-line reason)” to match the behavior in tests/test_frozen_manifest.py.

Suggested change
* Every driver is automatically checked against the upstream firmware manifest by `tests/test_frozen_manifest.py`. If a driver is intentionally **not** frozen (experimental, not yet integrated, etc.), add an empty `lib/<driver>/.not-frozen` file containing a one-line reason — the test will skip it with that reason displayed.
* Every driver is automatically checked against the upstream firmware manifest by `tests/test_frozen_manifest.py`. If a driver is intentionally **not** frozen (experimental, not yet integrated, etc.), add a `lib/<driver>/.not-frozen` marker file (it may be empty; optionally include a one-line reason) — the test will skip it and display the reason if present.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 34281f7: reworded using your suggestion — "it may be empty; optionally include a one-line reason".

Three issues raised on #393:

1. URLError catches HTTPError too, so a 404 (branch/path renamed, path
   moved) would silently skip the test and permanently disable the
   protection. Handle HTTPError separately and fail loudly with a message
   pointing to MANIFEST_URL.

2. The regex-based extraction was formatting-sensitive: single quotes,
   extra keyword arguments, or a different argument order would produce
   false failures. Replace with an ast-based walker that inspects
   require() calls and matches on the library= keyword — resilient to
   any legal Python formatting.

3. CONTRIBUTING.md contradicted itself ("empty file containing a one-line
   reason"). Reword per Copilot's suggestion: empty by default, optionally
   with a one-line reason.
@nedseb nedseb merged commit a9df44c into main Apr 14, 2026
9 checks passed
@nedseb nedseb deleted the test/frozen-manifest-check branch April 14, 2026 08:23
@semantic-release-updater
Copy link
Copy Markdown

🎉 This PR is included in version 0.16.8 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working released test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

test: Verify each driver is frozen in the upstream firmware manifest.

2 participants