mirror of
https://github.com/Klipper4FlashForge/firmware.git
synced 2026-10-21 12:52:16 -05:00
The software component existed to give the mod one exec hook. It is not needed for that any more, so a release now carries our installer and our payload and nothing else: 26MB smaller, 21MB of which was FlashForge's own firmwareExe travelling back to the partition it was already on, along with their klippy tree, their wifi module and their configs. WHAT IT GIVES BACK. /usr/prog/PROGRAM/software is no longer rewritten, so the version directory there stays FlashForge's with their genuine firmwareExe inside it -- and app_startup.sh's five-second watchdog, which copies one out of that directory when nothing by that name is running, has something to copy again. A printer whose payload fails to install now comes up on the stock UI instead of a blank screen. That recovery has been broken since we started deleting their binary out of a component we shipped; docs/how-it-works.md claimed it worked throughout. THE ROOT PASSWORD moves with it, because it used to ride in the component's shadow file. bin/pack.sh bakes ROOT_PW_HASH into the installer, single-quoted -- an unquoted $6$... would have the printer's shell expand $6 as a positional parameter and ship a truncated hash -- and the installer writes it to /usr/prog/etc/shadow. The random-password path asks one question instead of four: FlashForge ship the same root hash on every machine, so a live shadow still carrying it means nobody has set a password and it is ours to set. The .prev-root-hash recording, the shipped-vs-live comparison and the restore-it- afterwards step were all machinery for surviving a file that nothing overwrites any more, and are gone. pack.sh checks the hardcoded constant against the stock package it built from, because getting it wrong is silent and total: every printer would read as "already set" and none would ever get a password. bin/patch.sh is bin/payload.sh. It patched nothing any more -- no splice, no shadow edit, no firmwareExe deletion -- and what is left is the payload: the feed installed by the printer's own opkg, the seeded config, the manifest. Its sections are renumbered 1-5 with no gaps, and the five tombstone comments marking where removed sections used to be are gone with them. test_recipe_layout.py loses six of its nine tests. Each compared the tree against a description of the tree -- no fourth directory kind, each kind still used, 3rdparty carries no files of ours, installer/ holds exactly two named files -- and none could go red for a reason a printer would notice, while all six went red the moment the layout legitimately changed. The two that bite are kept: a payload/ path escaping $MODDIR, and a recipe name at both levels, which makes pkg_dir silently build the wrong package. SW_VER is deleted from config.env.example and both workflows: it stamped a component that is not built. Gates: 112 static tests pass. The generated installer is verified after substitution -- all four lines present, and `sh -n` over the result, because a hash with a stray character in it would otherwise reach a printer as a syntax error whose only symptom is a package that installs nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CH7Yb4wuPhnppHAB25UX7e
108 lines
4.9 KiB
Python
108 lines
4.9 KiB
Python
"""The qa suite: pytest is the only framework.
|
|
|
|
This is the only suite. It decides whether a package bricks a printer. The
|
|
harness that used to stand beside it, and the host-side test/ tree it wrapped,
|
|
are both gone -- see docs/qa-migration.md.
|
|
|
|
WHAT IS DIFFERENT HERE
|
|
|
|
The suite this replaced ran one `docker run --privileged` per case script and
|
|
got one boolean back, because the assertions lived in POSIX sh inside the qemu
|
|
chroot. 6,280 lines of case-*.sh reported 13 bits between them. The rule here
|
|
is:
|
|
|
|
shell inside the chroot performs actions; python on the host asserts.
|
|
|
|
Shell in qemu is irreplaceable for DOING things -- the printer's busybox, its
|
|
tar, its OpenSSL, and the fact that our scripts survive its ash is a large part
|
|
of what the suite proves. It is a bad place to JUDGE things. Where a fact is
|
|
only observable inside the replica, qa/lib/replica.py exposes a typed probe for
|
|
it; a case script never branches on it.
|
|
|
|
LANES
|
|
|
|
static needs nothing but the checkout and the lint tools. milliseconds.
|
|
replica needs docker + qemu + the proprietary firmware. seconds to
|
|
minutes. The gates that decide whether a package bricks.
|
|
|
|
Select with `-m static` / `-m replica`, or by path.
|
|
|
|
SKIPS, AND WHY THERE IS NO FLAG CONTROLLING THEM
|
|
|
|
A missing tool, a missing daemon or a missing firmware image is a FAILURE, at
|
|
the point that needs it. Not a skip, and not a skip that some flag can be asked
|
|
to promote later.
|
|
|
|
This suite had a `--strict-skip=<lane|all>` for exactly one commit, and it was
|
|
the wrong shape. It was ALLOW_SKIP inverted -- and ALLOW_SKIP's defect was
|
|
never which direction it named things in, it was that THE DECISION LIVED IN THE
|
|
CI INVOCATION. An accept-list in ci.yml has to be edited whenever a gate moves;
|
|
a strict-list in ci.yml has to be remembered whenever a job is added. Both
|
|
degrade silently when someone gets the command line wrong, which is the same
|
|
disease as the printer-sim job that was gated on an unset secret and therefore
|
|
never ran once in its entire existence.
|
|
|
|
So the verdict lives with the knowledge. `shellcheck` missing means the machine
|
|
cannot run the check, and no configuration exists in which that is a complete
|
|
run, so the test fails and says how to fix it. Same for pyflakes, for the docker
|
|
CLI, for the daemon, and for the replica image.
|
|
|
|
A GATE THAT DID NOT RUN MUST NEVER LOOK GREEN -- and the way to guarantee
|
|
that is to not have a mode in which it can.
|
|
|
|
`pytest.skip` stays available for the case it is actually for: a question that
|
|
does not APPLY to this configuration, as opposed to one this machine happens to
|
|
be unequipped to ask. A test that only means something on a Creator5Pro is the
|
|
shape that earns it. "I could not find the tool" is not.
|
|
|
|
Three skips do not meet that bar: test_ipk.py's payload questions read
|
|
work/modpayload-root, which only bin/payload.sh produces. Noted rather than
|
|
hidden -- and note that `make test` DELETES that directory when it clears up
|
|
after its fixture build, so running the old suite quietly costs the static
|
|
lane three tests. See docs/qa-migration.md.
|
|
"""
|
|
import os
|
|
import sys
|
|
|
|
import pytest
|
|
|
|
# qa/ on the path so `from lib.replica import Printer` works from any subdir.
|
|
# Not a package install: this tree is not shipped and never will be, and a
|
|
# setup.py for it would be one more thing that can disagree with reality.
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
LANES = ("static", "replica")
|
|
|
|
|
|
def pytest_configure(config):
|
|
for lane in LANES:
|
|
config.addinivalue_line(
|
|
"markers", "%s: the %s lane -- see qa/conftest.py" % (lane, lane))
|
|
_check_distribution(config)
|
|
|
|
|
|
def _check_distribution(config):
|
|
"""Refuse the xdist mode that would start a container per worker.
|
|
|
|
The `printer` fixture is module-scoped, and xdist's DEFAULT distribution
|
|
(`--dist load`) hands out individual tests. Four workers pulling tests
|
|
from one module each instantiate that module's fixture, so `-n 4` would
|
|
assemble four replicas of the same machine instead of one -- four binfmt
|
|
registrations, four mount layouts, and on a locally built replica four
|
|
stock installs under qemu. It would still pass, slowly, which is why it
|
|
needs catching here rather than in review: the symptom is a suite that
|
|
got mysteriously slower when it was parallelised.
|
|
|
|
`loadscope` groups by module (`loadfile` by file, `loadgroup` by an
|
|
explicit mark) -- any of those keeps one container per module, which is
|
|
the whole isolation model. Enforced rather than defaulted, because
|
|
silently rewriting somebody's -n flag is its own kind of surprise.
|
|
"""
|
|
dist = getattr(config.option, "dist", "no")
|
|
if dist in ("no", "loadscope", "loadfile", "loadgroup"):
|
|
return
|
|
raise pytest.UsageError(
|
|
"--dist=%s would start one replica per worker per module. Use "
|
|
"--dist=loadscope (or loadfile), which keeps each module on one "
|
|
"worker -- see _check_distribution in qa/conftest.py." % dist)
|