Skip to content

Phase 2 integration testing: self-hosted runner + camera + OCR on real hardware #6

Description

@bryanwintermute

Summary

Issue #1 originally bundled two phases of integration testing:

  • Phase 1MANUAL_SMOKE_TEST.md (done — landed in commit bbf7f9c)
  • Phase 2 — self-hosted runner + OCR (still aspirational)

Splitting Phase 2 out into its own ticket so #1 can close on Phase 1
acceptance and Phase 2 can stay open as the long-tail "dreamy"
follow-on without blocking Phase 1's closure.

Why this matters

The byte-equality suite (tests/test_dry_run_bytes.py, 109 cases as
of v0.3.2) locks the wire protocol — what bytes the CLI emits
for given input. That's the highest-value regression guard we have.

What it doesn't cover:

  1. Does writing the bytes actually do what we documented? (/dev/rongta-receipt
    open + write path, plugdev perms, udev rule, firmware behavior on receipt.)
  2. Did firmware quietly change semantics? (Already happened once — the
    2017 Linux SDK numbered WCP1252 as 12, the 2025 firmware as 16. Future
    firmware revisions could move the goalposts again.)
  3. Does the cutter actually cut? "Cutter=Yes" in the self-test report
    is necessary but not sufficient.

Phase 1's manual checklist makes "did the CLI work end-to-end on real
hardware" a documented, repeatable thing — but it relies on a human
running it pre-release. Phase 2 automates that.

Architecture sketch

┌──────────────┐
│ GitHub       │
│ Actions      │ ──── workflow_dispatch / release tag trigger
│              │
└──────┬───────┘
       │
       ▼ (self-hosted runner registration)
┌──────────────────────────────────────────────────────────┐
│ tickerbox-ci (separate Pi from production tickerbox)     │
│                                                          │
│  ├── unspooled checkout (per-job clone)                  │
│  ├── Rongta RP332 over USB → /dev/rongta-receipt         │
│  ├── Pi Camera v3 + ring light → /dev/video0             │
│  └── Tesseract / EasyOCR + Python harness                │
└──────────────────────────────────────────────────────────┘

Per-test loop:

  1. CLI writes a known config (e.g., base --cutter on).
  2. CLI triggers a self-test print (printf '\x12T' > /dev/rongta-receipt).
  3. Camera captures the receipt.
  4. OCR parses the relevant fields (Cutter: Yes/No, Page: N, etc.).
  5. Assertion: parsed value matches expected.
  6. (Optional) Save raw image as a workflow artifact for human review.

What needs to be solved

Hardware

  • Dedicated Pi for CI (don't share with tickerbox production —
    printing self-tests during a CI run would interrupt real prints).
    Likely a Pi 4 (more USB bandwidth than the 3B+, camera headroom).
  • Camera mount — fixed geometry pointed at the print head /
    capture area. Distance, angle, and lighting need to be consistent
    across runs or OCR accuracy will drift.
  • Ring light for consistent illumination. The receipt's white
    paper is high-contrast against thermal-black, but ambient light
    drift (sun through a window) will wreck things.
  • Mounting frame so the printer + camera geometry stays fixed.
  • Paper feed monitoring — out-of-paper kills the test run silently
    on the wire. The printer's paper-out sensor IS readable over USB
    (DLE EOT 4) — wire it into the harness so the runner reports
    "halted: out of paper" instead of stalling.

Software harness

  • OCR pipeline. Tesseract is the obvious starting point but
    may struggle with the small font in the code-page table.
    Alternatives to try in order: Tesseract → EasyOCR → a fine-tuned
    Tesseract model trained on captures of THIS printer's output.
    Document accuracy numbers per pipeline.
  • Capture-then-OCR helper — Python module that takes a snapshot,
    auto-crops to receipt bounds (template-matching against printer
    housing edges or QR-code anchor), runs OCR, returns structured
    fields.
  • Field schema — define what's extracted per self-test type.
    Start with base's self-test (cutter / beeper / drawer / density /
    code-page / chars-per-line), expand from there.
  • Test harness wrapping byte-emit + capture + OCR + assert into
    a pytest-style API. Probably a new tests/hardware/ subdir,
    pytest-marked with @pytest.mark.hardware and skipped by default.
  • workflow_dispatch workflow (manual trigger first; on every
    release tag once stable).

Ops / cost

  • Self-hosted runner registration with read-only repo scope.
    Standard GitHub Actions runner setup; well-trodden path.
  • Paper budget. Estimate: ~40 self-test prints per full pass
    ≈ 16" of paper. At ~$30/roll for 80mm thermal × 200ft of paper,
    that's ~$0.05 per CI run. Negligible.
  • Power-cycle / USB-flake recovery. Already documented at
    docs/wine-cups-backend-recovers-nv-bytes.md;
    the harness should detect missing /dev/rongta-receipt and try
    a udevadm trigger once before failing.
  • Job concurrency. Only one job at a time can use the printer.
    concurrency: hardware-ci in the workflow.

What's deliberately out of scope

  • Color-fidelity testing (thermal printer is monochrome).
  • Cutter-cuts-cleanly testing (binary: did paper separate, yes/no —
    can be done via paper-end-sensor delta, but OCR can't see it).
  • Multi-SKU testing on the same rig (would need a multi-printer carousel;
    out of reasonable scope).
  • Stress / endurance testing (this is a release-gate suite, not a
    load test).

Done definition (for the issue, not the feature)

  • At least one workflow runs the smoke procedure on every release tag.
  • OCR pipeline accurate enough on base self-test to fail loud on
    regression (target: 100% on bold-text fields, ≥95% on code-page
    table small-font fields).
  • Failure modes produce useful artifacts (raw image, OCR output,
    diff against expected) attached to the workflow run for human triage.
  • Documented setup recipe in docs/ for someone else to replicate
    (camera position, lighting, OCR model choice, runner registration).

Related

Why this is "dreamy"

It needs hardware investment (~$80 Pi 4 + camera + mount + ring light)
and ongoing maintenance (paper, runner uptime, OCR drift). Not a
prerequisite for any release; would be nice for v1.0 confidence.

Open to deferring indefinitely unless someone (maintainer or
volunteer) volunteers to build it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedExtra attention is neededintegration-testingPhysical / hardware-in-the-loop testing

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions