mirror of
https://github.com/Klipper4FlashForge/firmware.git
synced 2026-10-22 21:32:16 -05:00
The split existed so a plain pull request had something to run. This repo has one maintainer who always has the firmware to hand, so it was a boundary kept in sync for a contributor who never arrives. One directory now. The tests moved rather than went away. test_chamber.py is the only check in the suite that can catch a printer heating something it has no element for, and the replica cannot cover it: case-install.sh touches .cfg files at exactly two lines, both a grep for a sentinel proving the user config survived, and it asserts klippy.py exists and c_helper.so has the right ABI without ever starting klippy. Declaring [heater_generic chamber_heater] on the plain Creator 5 fails four of these tests, including M191 waiting on a target the machine can never reach; that same package installs, boots, and passes every replica gate. make-stock-fixture.sh came too, since the packaging pipeline is built on it. Ordering changed, and that is the part to look at. With two lanes, pytest ran twice -- once per lane, each with its own -m expression or path. With one it runs once, so it has to run at the point where it can see the most: the stock package is now resolved and the rootfs extracted BEFORE pytest rather than after it. On a configured machine that is 28 tests in one pass; on a bare tree the five that read the rootfs skip. config.env is read in a subshell while doing that. It is the BUILD config, and it is now consulted upstream of the fixture build, where the entire point is that the fixture config wins -- sourcing it into this shell would put MOD_NAME and TARGET_MACHINE where that build could see them. Verified by pointing a config.env at MOD_NAME=LEAKED and confirming the fixture still builds Creator5Pro-anvil-ci.tgz. One thing gets worse, and it is inherent to having a single lane: on a tree with no rootfs, pytest reports 5 skips, so sub() marks the whole gate SKIP rather than ok, and the summary reads 6 passed instead of 7. Those 23 passes are real and the line above the SKIP says so. This is the existing policy working as designed -- a gate that did not fully run must not read as success -- but the summary is coarser than it was when a lane existed that never skipped. Verified: 28 passed with a real extracted rootfs, 23 passed / 5 skipped without; the full suite gives 7 passed / 1 failed / 1 skipped with a rootfs present and 6 / 1 / 2 without, the failure being shellcheck, which lives in the build image; no test/unit reference is left outside the passages that explain why it is gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
75 lines
3.1 KiB
Python
75 lines
3.1 KiB
Python
"""Absolute paths the payload uses must exist on the printer.
|
|
|
|
What is left of this check is the half that earns its keep: every literal
|
|
/bin, /sbin, /etc, /usr/bin, /usr/sbin path in an on-printer script has to
|
|
exist in the real extracted rootfs. Its value is the branches no simulation
|
|
ever reaches -- S50wifi bails at `[ -d /sys/class/net/wlan0 ]`, which the
|
|
replica has not got, so /sbin/udhcpc, /sbin/ifconfig, /usr/sbin/wpa_supplicant
|
|
and /usr/sbin/wpa_cli are verified here and nowhere else.
|
|
|
|
The bare-command-word scan that used to live here is gone, along with
|
|
test/applets.allow. It extracted only the first word of a simple, unprefixed
|
|
command, so `if timeout 5 foo; then` yielded ['if','echo','fi'] -- the exact
|
|
failure its own docstring claimed to guard was invisible to it, as were
|
|
`VAR=1 cmd`, `nohup cmd`, and everything reached through a variable or an
|
|
absolute path, which is how the payload invokes essentially everything. Its
|
|
allowlist had drifted to nine entries that excused nothing. What it could
|
|
really catch, case-install.sh catches properly by booting the machine and
|
|
grepping the boot log for "command not found".
|
|
|
|
/usr/prog and /usr/data are deliberately not checked: they live on partitions
|
|
the rootfs does not contain, and the install simulation covers them.
|
|
|
|
The two extractor guards below need no rootfs and so still run when there is
|
|
no firmware; they exist to stop the check above from passing vacuously.
|
|
"""
|
|
import os
|
|
import re
|
|
|
|
import pytest
|
|
|
|
PATHS = re.compile(
|
|
r"(?<![\w.])(/(?:bin|sbin|etc)/[A-Za-z0-9_./-]+|/usr/(?:bin|sbin)/[A-Za-z0-9_./-]+)")
|
|
|
|
|
|
def payload_scripts(root):
|
|
out = []
|
|
for dirpath, _, names in os.walk(os.path.join(root, "payload")):
|
|
for n in names:
|
|
if n.endswith(".sh") or n == "firmwareExe" or re.match(r"^S\d", n):
|
|
out.append(os.path.join(dirpath, n))
|
|
return sorted(out)
|
|
|
|
|
|
def absolute_paths(root):
|
|
"""(script, path) for every rootfs-absolute path the payload names."""
|
|
for f in payload_scripts(root):
|
|
src = open(f, encoding="utf-8", errors="replace").read()
|
|
for p in sorted(set(PATHS.findall(src))):
|
|
yield os.path.relpath(f, root), p
|
|
|
|
|
|
def test_payload_scripts_are_found(root):
|
|
assert payload_scripts(root), "no on-printer scripts found under payload/"
|
|
|
|
|
|
def test_some_absolute_paths_are_checked(root):
|
|
"""Guard against the regex silently matching nothing and the test passing."""
|
|
assert list(absolute_paths(root)), "no absolute paths extracted -- regex broken?"
|
|
|
|
|
|
def test_absolute_paths_exist_on_the_printer(root, rootfs):
|
|
missing = ["%s: %s" % (script, p)
|
|
for script, p in absolute_paths(root)
|
|
if not os.path.exists(rootfs + p)]
|
|
assert not missing, \
|
|
"paths that do not exist on the printer:\n " + "\n ".join(missing)
|
|
|
|
|
|
@pytest.mark.parametrize("binary", [
|
|
"/sbin/ifconfig", "/sbin/udhcpc", "/usr/sbin/wpa_supplicant", "/usr/sbin/wpa_cli",
|
|
])
|
|
def test_wifi_binaries_exist(rootfs, binary):
|
|
"""Named explicitly: S50wifi's connect() never runs in any simulation."""
|
|
assert os.path.exists(rootfs + binary), "%s missing from the printer rootfs" % binary
|