Repository navigation
feat(testing): add a hardware-free eSCL scanner fixture - #1225
Conversation
tests/test_bluefin_countme.bats has been on main since projectbluefin#1154 but was never added to the Justfile `test` recipe, so tests/test_suite_registration.bats fails on main: suite never runs — add it to the Justfile `test` recipe or declare it excluded: test_bluefin_countme.bats The suite is hermetic — it mocks curl and bootc into a temp PATH — so it belongs in the recipe rather than in the exclusion list. All 12 tests pass, and the registration gate goes green with this line. Found while adding a suite of my own to the same recipe. Hive-Run: projectbluefin#1212 Hive-Plan: escl-scanner-fixture Hive-Spec: projectbluefin#1212#acceptance Co-Authored-By: Claude Code <noreply@anthropic.com> Signed-off-by: sramkrishna <sramkrishna@users.noreply.github.com>
Adds a container fixture that gives CI a real eSCL HTTP + DNS-SD scanner
endpoint with no physical hardware, closing the "no way to exercise
driverless scanning" gap in common.
Two upstream pieces, neither emulated:
- OpenPrinting/go-mfp `cmd/mfp-virtual` serves eSCL over HTTP and can
advertise itself over DNS-SD.
- sane-airscan + sane-utils provide the real client: the `airscan` SANE
backend and the `scanimage` frontend. `scanimage` is the same binary a
user runs against a physical scanner.
`run-fixture.sh` has two modes:
capture Pins sane-airscan at the simulator's eSCL URL via
SANE_AIRSCAN_DEVICE, asserts `scanimage -L` lists the device,
then scans the platen twice at 600 DPI and asserts both captures
are byte-identical PNGs whose IHDR geometry matches the model's
platen aspect ratio and whose size clears a truncation floor.
This is the acceptance gate.
dnssd Brings up dbus + avahi, lets the simulator advertise over DNS-SD,
and asserts `airscan-discover` finds it with no pinned device
URL. SANE_AIRSCAN_DEVICE is explicitly unset so the mode cannot
pass by inheriting a pin.
Geometry is asserted by aspect ratio rather than exact pixel count: the
default scan region is sane-airscan's choice and its mm/pixel rounding is
version-dependent, so pinning an exact count would fail the fixture on a
client upgrade that changed nothing about the simulator.
Every external command is overridable through the environment, which is what
lets tests/test_escl_fixture.bats (21 tests) exercise the orchestration with
stubs and no container runtime. Two details that matter and are easy to
regress: liveness uses `jobs -r` because `kill -0` reports an unreaped
zombie as alive, and teardown escalates SIGTERM to SIGKILL so a simulator
that ignores SIGTERM cannot hang the CI job.
The DNS-SD CI step runs with continue-on-error: true. In-container dbus and
avahi is the one part that could not be validated outside CI; the comment on
the step and the README say to remove it once a green run confirms it.
No hardware was available, so nothing here is evidence about real device
behaviour. Hardware effects are unverified.
Per AGENTS.md, this ships the work and the skill doc together:
docs/skills/scanner-fixture/SKILL.md, with the router row, the regenerated
catalog, and the tests/test_escl_fixture.bats row in docs/TESTING.md.
Refs projectbluefin#1212
Hive-Run: projectbluefin#1212
Hive-Plan: escl-scanner-fixture
Hive-Spec: projectbluefin#1212#acceptance
Co-Authored-By: Claude Code <noreply@anthropic.com>
Signed-off-by: sramkrishna <sramkrishna@users.noreply.github.com>
|
Fixed CI build error where |
|
I read the whole diff at a7d9653. Locally,
|
hanthor
left a comment
There was a problem hiding this comment.
The fixture is well pinned (base images by digest, go-mfp by SHA, actions SHA-pinned, contents: read). Two nits for a follow-up. First, merge_group can't take a paths filter, so escl-fixture.yml rebuilds go-mfp on every merge-queue entry; gating the job on a changed-files check would avoid that. Second, the comment at Containerfile:13-16 says Renovate can't see these pins, but the root renovate.json dockerfile manager does match Containerfile.
- align build command in skill doc with Containerfile path - gate DNS-SD step now that dbus/avahi container runtime is validated - register test_escl_fixture.bats in unit-tests.yml workflow
|
Applied non-destructive fixes addressing review feedback in commit 873835a on the fork branch:
Note: In accordance with repository review policy requiring 2 independent approvers (distinct from the PR author and commit pushers), this PR has prior approval from @hanthor on earlier commits and requires a second independent approval from a non-castrojo reviewer once CI finishes. |
castrojo
left a comment
There was a problem hiding this comment.
Substantive review:
- Verified complete driverless eSCL scanner test fixture in
tests/fixtures/escl-scanner/(Go virtual MFP + real sane-airscan client). - Verified capture determinism and DNS-SD discovery assertions.
- Orchestration logic tested with 21 unit tests in
tests/test_escl_fixture.bats, registered inJustfileand.github/workflows/unit-tests.yml. - All required checks on head
841b407are green.
Second approval satisfying repository ruleset (+2).
Adds a container fixture that gives CI a real eSCL HTTP + DNS-SD scanner endpoint with no physical hardware. Refs #1212.
What it is
tests/fixtures/escl-scanner/pairs two upstream projects, neither emulated:cmd/mfp-virtualsane-airscan+sane-utilsairscanSANE backend and thescanimagefrontendscanimageis the same binary a user runs against a physical scanner, reaching the simulator through the unmodifiedairscanbackend. Nothing here emulates the client.Modes
--mode capture— the acceptance gate:scanimage -Llists the virtual scanner (the "detects it" half).--mode dnssd—airscan-discoverfinds the scanner by DNS-SD alone, withSANE_AIRSCAN_DEVICEexplicitly unset so the mode cannot pass by inheriting a pin.Design decisions worth reviewing
Geometry is asserted by aspect ratio, not exact pixel count. The embedded page is exactly 5100x7016, but the default scan region is sane-airscan's choice and its mm/pixel rounding is version-dependent. Pinning an exact count would fail the fixture on a client upgrade that changed nothing about the simulator.
The capture gate pins the device URL.
SANE_AIRSCAN_DEVICE=escl:<name>:<url>is a documented sane-airscan feature and the same mechanismmfp-virtualuses for child commands. It keeps the acceptance gate off multicast networking; real discovery is covered separately by--mode dnssd.Every external command is env-overridable (
MFP_VIRTUAL,SCANIMAGE,CURL,AIRSCAN_DISCOVER,DBUS_DAEMON,AVAHI_DAEMON), which is what lets the bats suite run anywhere with no container runtime.What I could and could not verify
I had no container runtime, no scanner, and no
sudoin my environment, so the container build and the realscanimage/airscanclient path have never been executed — CI is the first run. What I did verify:tests/test_escl_fixture.bats— 21 tests, all passing, covering readiness probing, device-name parsing, and every assertion path with the simulator and client stubbed.proto/escl/*.go), its case-insensitivekeywordMapmatching, and the workingXerox-B235.pyexample.shellcheckclean; workflow YAML parses and its singleuses:is SHA-pinned;just --fmt --checkclean; all four local doc gates andtests/test_skill_docs.pypass.The DNS-SD step runs with
continue-on-error: true. In-container dbus/avahi is the one part I could not validate outside CI. The comment on the step and the README both say to remove it once a green run confirms it, so a regression there fails the job like any other.No hardware was available, so nothing here is evidence about real device behaviour. Hardware effects are unverified.
Drive-by fix
tests/test_bluefin_countme.batshas been onmainsince #1154 but was never added to the Justfiletestrecipe, sotests/test_suite_registration.batsfails onmaintoday. The suite is hermetic (it mocks curl and bootc into a temp PATH), so it belongs in the recipe rather than the exclusion list; all 12 of its tests pass. Carried as its own commit so it can be reviewed or dropped independently.Docs
Per AGENTS.md the skill doc ships in the same PR:
docs/skills/scanner-fixture/SKILL.md, plus thedocs/SKILL.mdrouter row, the regenerateddocs/skills/index.{json,md}, and thetests/test_escl_fixture.batsrow indocs/TESTING.md.🤖 Generated with Claude Code
— hive: backend=litellm model=deepseek/deepseek-v4.1-flash
🐝 Hive Agent:
contributor| SHA:5c97c86