Two strands, both landing here because they had grown into each other in the
working tree.
The printer replica: the printer's real rootfs.squashfs chrooted under
qemu-mipsel, with /usr/prog and /usr/data from the factory image, so a package
is installed by FlashForge's own updater running on the printer's own busybox,
tar, md5sum and unTar. There are no stubs -- if the real prog partition is
missing the harness refuses to run rather than test against mocks. A release
workflow is blocked on it.
The lanes: a file's directory now says whether it can ever reach a printer.
payload/, assets/ ship
bin/, profiles/ build the package
test/, docs/ neither, ever
* the replica's own infrastructure moved out of bin/ and docker/ and in
beside the harness it belongs to (test/extract-rootfs.sh,
test/build-printer-image.sh, test/printer/Dockerfile{,.full})
* the Makefile has two runners: only RUNSIM gets the docker socket and the
replica's variables, so a build cannot reach the docker daemon at all
* replica settings moved to test.env, which nothing in a package reads
* build-time flags renamed BUILD_* (APPLY, REPORT, KLIPPER, TOOLCHANGE,
MAINSAIL, HELIX). MOD_* now means only the runtime switches written into
anvil.conf and re-read by the printer at every boot
* verify.sh fails if any file in a package is byte-identical to one in
bin/, test/, docker/ or profiles/ -- by content, because a name blacklist
would trip over whatever Mainsail happens to call common.sh
Fixed while mapping it:
* BUILD_REPORT was read only inside the BUILD_APPLY=0 branch, so four
profiles declared it and got nothing. It is honoured on both paths now,
and off outside probe: report.sh copies /etc, passwd and shadow onto the
USB stick, which is a diagnostic, not something to hand out.
* three checks in the brick lint had been quietly dead. Two were guarded on
a payload/boot.sh replaced by the firmwareExe wrapper long ago; the third
was the file glob itself, which matched neither firmwareExe nor the
init.d/S* scripts -- the UI wrapper and every service went unscanned. A
missing file is now a failure rather than a silent pass.
* run-tests.sh overwrote config.env and copied it back, leaving the config
you edited one crashed run away from being a fixture. It uses CONFIG_ENV.
* bin/make-uninstall.sh does not exist and never did; README, run-pre.sh and
full.env all pointed at it. Recovery is the stock package, which is what
test-recovery actually proves.
README is now for someone who wants to flash their printer: what it is, which
file, how to flash, what to do when it goes wrong. The rest moved to
docs/how-it-works.md, docs/building.md and docs/testing.md.
make test: 89 passed, 0 failed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
4.3 KiB
Testing: how we know it does not brick
The suite has two halves, and only one of them can answer the question.
Half 1 — static and packaging. Needs no printer and no proprietary
firmware: a synthetic fixture (test/fixtures/make-stock-fixture.sh)
reproduces the package structure, so shell syntax, bashism and brick-pattern
lint, the packaging pipeline and the model gates all run in CI on a clean
machine.
Half 2 — the printer replica. The real rootfs.squashfs, extracted from
the stock package, chrooted under qemu-mipsel, with /usr/prog installed by
FlashForge's own updater. The installer under test runs on the printer's
busybox, tar, md5sum and unTar — not on Debian stand-ins — with a read-only
root and writable prog/data partitions, exactly like the machine. This is the
half that can catch a brick, it needs the stock package, and
.github/workflows/release.yml refuses to publish without it.
With PROG_DUMP (in test.env) pointed at a factory image, the replica has
essentially nothing invented left: the klipper daemons, nginx, python3,
moonraker and the printer's own OpenSSL 1.0.2d are all genuine, so
package decryption is verified against the real implementation. What remains
substituted is insmod/reboot/cmd_mcu, neutered because they would act on
the host kernel or real hardware, and the partition sizes.
See printer-replica.md for what is authentic, what is stubbed, and what it still cannot tell you.
The tests
make test # everything below
| Test | What it does | Replica |
|---|---|---|
test-lint |
Scans on-printer scripts for brick patterns: raw block-device writes, rm -rf of top-level paths, unguarded rm -rf $VAR/, missing backups, a boot chain that could end with no UI |
|
test-model |
Each package's filename prefix matches the gate inside, the PID matches the model, and the two models ship different firmwareExe binaries |
|
test-install |
Replays what app_startup.sh does with a USB stick — its own glob, its own unTar, its own runFirmwareExe.sh — then asserts the printer would still boot: UI present and executable, boot scripts unmodified and still parsing, Klipper owned by a service, c_helper.so still nan2008 MIPS, user printer.cfg preserved, re-install idempotent |
✓ |
test-recovery |
Installs the mod, then flashes the stock package, and asserts the machine is genuinely back to stock byte-for-byte and the leftover payload is inert | ✓ |
test-ui |
Drives the UI selection logic on the printer's shell: helix missing, crash-loop, SAFE-MODE latch and release, and the no-UI-at-all case | ✓ |
test-applets |
Every command the payload runs exists on the printer. Its busybox has no timeout, no bash, no systemctl; reaching for one is a blank screen at boot |
✓ |
test-ash |
Parses every on-printer script with the printer's own busybox 1.31.1 ash | ✓ |
test-abi |
Every shipped MIPS binary is nan2008/mips32r2/o32, and executes under qemu-mipsel |
✓ |
The replica tests need make rootfs first, which extracts the printer's
genuine root filesystem from the stock package's kernel-*.tar.xz. It is
never committed — it is FlashForge's firmware. make test skips the replica
half with a loud message when no stock package is configured;
REQUIRE_PRINTER_SIM=1 turns that skip into a failure.
make printer-image bakes the whole thing — rootfs, /usr/prog, /usr/data
— into a Docker image, which takes a replica run from ~1m35s to ~15s. That
image contains proprietary FlashForge firmware.
Bugs these caught
Not theoretical. Writing them caught real bugs before any hardware was involved:
- backups were taken after the stock
run.shhad already overwritten the files, so a restore would have restored the modified versions rm -rf $MODDIR/binwith an unsetMODDIRexpands torm -rf /bin- a crashed UI is a zombie, and
kill -0succeeds on a zombie — the crash detector called a dead UI healthy - the packer emitted both models' filenames from one build, which would have handed a Creator 5 the Pro's firmware
- three checks in the brick lint had been quietly dead for months: two were
guarded on a
payload/boot.shthat no longer existed, and the file glob never matchedfirmwareExeor theinit.d/S*scripts at all