The host proofs of image.license-bundle's pack half and of
image.network-boot's interfaces check. No catalog test is removed here;
suite/image.py is unchanged.
pack-licenses.sh, next to forgefirm-image.bb, is the license step.
pack_licenses calls it with the common-licenses directory and the
output tar path (forgefirm-image-dev.bb requires the release recipe, so
both images run the one call), and do_rootfs's file-checksums name it.
Before it writes the tar or removes the tree, it exits non-zero with a
message on stderr when the directory is missing, when the manifest is
missing, or when the manifest lacks any of the newline-terminated lines
"RECIPE NAME: forgectrl", "RECIPE NAME: grblhal-glowforge" and "RECIPE
NAME: kernel-module-glowforge". On success it packs as before: tar
--sort=name --mtime=@0 --owner=0 --group=0 --numeric-owner, gzip -9 -n,
the member common-licenses/license.manifest, and the loose tree removed
once the bundle holds the manifest.
test_image.py's PackLicensesTests drives that script on a fixture: a
missing directory, a missing manifest, and a manifest missing each one
of the three lines are refused with a message on stderr and no tar, the
tree left; a manifest naming all three packs, the tar holds
common-licenses/license.manifest, and the tree is gone.
NetworkBootTests reads the BSP layer's interfaces file from the
meta-openglow checkout beside forgefirm's (forgetest-ci checks out its
scarthgap branch there) and finds no line that starts a DHCPv6 client.
A missing file fails the case.
meta-forgefirm's content hash moved (forgefirm-image.bb and the new
script are layer content, and the platform identity of
forgefirm-image-manifest.bbclass leaves out only *.md, *-pin.inc and
forgefirm-release.inc): the next image cannot inherit a campaign.
Proof: python3 -B -m unittest discover -s tests -p test_image.py on
Linux (Ubuntu 24.04, GNU tar 1.35, meta-openglow at f012d8e): 14 OK, the
five new cases among them; pyflakes names no undefined name.
MachineNameTests runs the layer's own forgefirm-hostname and
forgefirm-banner in private mount, UTS and network namespaces: /run a
fresh tmpfs, a scratch directory over /sys/class/net, /etc a read-only
overlay with the scratch hostname and issue over the host's (so each
script makes its copy under /run/forgefirm and writes through its own
bind mount, as at boot). unshare -Urmun where user namespaces are open,
sudo -n unshare -mun otherwise; skipped only where neither makes a
namespace.
- the name: wlan0's last four hex digits (forgefirm-12ab with eth0 beside
it), eth0's with no wlan0, eth0's with a zero wlan0 MAC; the live name
and /etc/hostname agree.
- the banner: the image's text, then the heading and one URL per global
address of wlan0 (a veth carrying 192.0.2.5, 2001:db8::5 and fe80::5):
https://192.0.2.5/ and https://[2001:db8::5]/, the link-local address
left out, no marker line, no .local name.
Proof: test_image.py under Linux by both namespace routes (9 tests OK),
and on Windows with the two skipped. Mutants of scratch copies of the
scripts, each failing: eth0 read before wlan0, a zero MAC taken, the file
written without the script's bind mount, a marker line in the banner, a
.local URL, the link-local address kept.
setup.machine-name stays: the DHCP client's -x hostname and no mDNS
responder on the image are image content. Nothing under meta-forgefirm
changed.
The cases that tested a mock, a timer, a wording or a table, or that
replayed a judgment a host test already holds, leave forgetest/tests.
What catches a false PASS, keeps the bench safe, or guards the gate
stays.
- test_baseline: the fake sysfs tree is a mixin with no test_ methods,
so ExtensionFreeTests and BaselineModeTests no longer re-run
BaselineTests' 25 cases (133 runs, now 45). CoolPublishTests, the
lamp-fade cases and TransientNotLeftoverTests go; PositionDeadbandTests
keeps a reading past the band and an unreadable reading, both never
excused. Both latch-relock cases stay.
- test_fixture: MdnsTests, the three timing cases, the probe's log
wording, and nine prompt-routing variants go. A live test never runs
unattended, the arm-press opt-in, the short-press edge, the presence
press handing over the arm press, and the arm notice dropping at the
press stay (35 cases, now 21).
- test_cloud_suite: three false-PASS guards stay: the latch open at a
dark print's button, a lid stop that is not edge-driven, the kernel
refusing the resume. The guards for the latch unlocking in the hold
and a paused print resuming left with their catalog tests in 6a434bb.
The rest go, with the harness pieces and the hunt-lid fixture only
they read (41 cases, now 3).
- test_cooling_suite: GateOffTests, CriticalTierTests, FanGateTests and
FansQuietTests go with their harness; the flow verdict's log reads
stay: only the text after the offset, never an older verdict before it
(the flow-under-load false-PASS guard), a rotated log read whole.
- test_queue: RunClockTests and three ordering cases go, and refusals
are judged by a reason given, not its words; both live
acknowledgment cases stay.
- test_takeover_position: the six cases that judge the head and move
nothing stay.
- test_camera_home_return: the five cases where the head must not move
stay, and so do the three that check the hand-back jog's direction and
distance (homed, unhomed with the counters not counted twice, after
cloud mode): a wrong sign or a double count drives the head into the
gantry stops (15 cases, now 8).
- test_server: the keep-alive and ETag cases, the takeover and scope
half of the bench case, and the page's self-contained asserts
(test_page.py's) go.
- test_operator: the wording cases, the fixture case (test_fixture.py's)
and JournalTests go.
- test_mode: the supervisor timeout table goes.
- test_takeover_client: the listener, the offline service back, the
online client without the hunt, and GRBL under no marker stay.
- test_responsiveness: PageTests and ImplementationCostTests go; the
log cache keeps appends, a partial line, a replaced file and a fresh
list, and the fingerprint cache a changed manifest and one with no
content hash.
- test_bench_registry: GfbenchTests and two description and argument
pins go.
- test_installer: the log-format and TLS-wording cases and
InstallerShapeTests go; the die case checks the reason is in the log,
not its format.
- test_image: KernelIdentTests and the registration pin go.
Each file's __main__ guard now ends the file, and the docstrings say what
each still proves.
Proof: each of the fifteen files passes under Linux as forgetest-ci
discovers it, and pyflakes reports nothing new in them.
Acceptance: none; test-only.
A client that waits in ifup's foreground on a server's answer holds the
whole machine, because init starts the rest of the boot - sshd, forgectrl,
the console login - only after S01networking returns. A field machine sat
there forever on a router that refused DHCPv6 (the record is in the
meta-openglow commit that drops the DHCPv6 client). Nothing in the catalog
looked at the network boot path; this test does.
It asserts: wlan0 is in ifupdown's state file and no ifup is running; the
console getty is up; udhcpc runs with -b (it leaves ifup after three
unanswered discovers) and has been reparented to init; no DHCPv6 client is
named in /etc/network/interfaces, running, or on the image, and its hook
script is gone; IPv6 on wlan0 is the kernel's own - enabled, router
advertisements accepted, a link-local address up. A global address is
evidence only: a network whose router advertisement offers no SLAAC prefix
gives none. What a hostile server does to a client is a bench drill, not a
test.
covers is empty by design, as with setup.machine-name: the interfaces file
is layer content, in the platform identity of every fingerprint, so a
change there already makes every test necessary again.
Proven: the host tests for the parsers and the registration
(tests/test_image.py), and the whole host suite, 398 tests, under Linux.
The test's logic, run read-only on the bench reference against an image
that carries the client, fails exactly the four DHCPv6 checks and passes
every other one. The test fails on any image built from a meta-openglow
that still carries the client, so the kas lock moves to the layer head
that drops it.
The manifest lists the modules directory without the kernel's
LOCALVERSION_AUTO hash (the hash does not reproduce across a re-patch of
the same source, so the image manifest strips it). image.health still
compared the full running release against that list and failed on the
first post-flash run of image 20260902144848 with the kernel
6.12.20-fslc-fslc-g72a0b1431a9d against the manifest's 6.12.20-fslc-fslc.
kernel_ident() strips the same suffix from both sides, so a manifest with
or without the hash matches the running kernel, and a different base
release still fails. tests/test_image.py covers both forms.
Catalog consequence: only image.health's own implementation hash moves;
it is an always-run test, so no inherited result is affected.