mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-09-28 09:11:11 -07:00
forgetest: the setup suite, and the sensors check asks nothing
The commission.* tests are setup.* (subsystem "setup"): suite/setup.py, setup_dark.py, setup_sheet.py, and the host tests test_setup_*.py. The coverage maps name src/setup.* in place of src/commission.*, the record is setup.json, and the bench seed in forgetest.init creates /run/forgefirm/setup-override. setup.check-sensors follows the check as it is now: no question is asked and no setting is written. The test reads the settings before and after and fails on any change, and fails at once if the check opens a prompt. The host test drives the fake daemon with no prompt and proves both outcomes. Proof: test_setup_dark.py, test_setup_sheet.py, and test_setup_suite.py pass (46 tests). The coverage lint names src/commission.c and .h as uncovered until the forgectrl pin moves to the revision that carries the rename.
This commit is contained in:
@@ -321,7 +321,7 @@ class Baseline:
|
||||
def wait_settled(self, timeout=SETTLE_S, unreachable_s=10):
|
||||
"""Block until forgectrl reports a settled supervisor: motion
|
||||
verified (the probe passed), motion-fault (the ladder exhausted),
|
||||
standby (the manual stop lever), or gated (the commissioning gate
|
||||
standby (the manual stop lever), or gated (the setup gate
|
||||
is closed: no controller spawns until it opens). Gives up after
|
||||
unreachable_s without an answer. Returns the last /mode body (None
|
||||
if unreachable)."""
|
||||
|
||||
@@ -4,9 +4,9 @@ order. Each module registers its tests with @catalog.test."""
|
||||
from . import image # noqa: F401,E402
|
||||
from . import kernel # noqa: F401,E402
|
||||
from . import forgectrl # noqa: F401,E402
|
||||
from . import commission # noqa: F401,E402
|
||||
from . import commission_dark # noqa: F401,E402
|
||||
from . import commission_sheet # noqa: F401,E402
|
||||
from . import setup # noqa: F401,E402
|
||||
from . import setup_dark # noqa: F401,E402
|
||||
from . import setup_sheet # noqa: F401,E402
|
||||
from . import logs # noqa: F401,E402
|
||||
from . import motion # noqa: F401,E402
|
||||
from . import cooling # noqa: F401,E402
|
||||
|
||||
@@ -396,7 +396,7 @@ def lid_privacy(ctx):
|
||||
"authorizes a write; a rotation from the panel route stops the old key and "
|
||||
"the new one reads. The setting is put back after.")
|
||||
def key_read(ctx):
|
||||
from .commission import request
|
||||
from .setup import request
|
||||
from .forgectrl import lan_ip
|
||||
fc = ctx.forgectrl
|
||||
ev = ctx.evidence
|
||||
|
||||
@@ -891,7 +891,7 @@ CRASH_GATES = ["crash_abort", "crash_x_alert", "crash_y_alert"]
|
||||
"read as the three crash gates off by value (gates_off names them) with the "
|
||||
"session OK; a threshold past the register range is refused; restored, the "
|
||||
"gates read ok. The tiers themselves (BUMP, CRASH) trip only on a physical "
|
||||
"knock inside an armed window and are commissioned at the bench, not here.")
|
||||
"knock inside an armed window and are proven at the bench, not here.")
|
||||
def crash_watch_plumbing(ctx):
|
||||
fc = ctx.forgectrl
|
||||
ev = ctx.evidence
|
||||
|
||||
@@ -152,7 +152,7 @@ def auth(ctx):
|
||||
# HTTPS the route itself refuses the peer (403 loopback only). Neither
|
||||
# request follows the redirect, and the self-signed certificate is
|
||||
# not verified.
|
||||
from .commission import request, decode
|
||||
from .setup import request, decode
|
||||
ip = lan_ip()
|
||||
ev["lan_ip"] = ip
|
||||
ctx.check(ip, "cannot determine the board's LAN address")
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
"""commission.* - the first-run commissioning: the record and the
|
||||
"""setup.* - the first-run setup: the record and the
|
||||
controller gate, the advisories, the account and its login, the HTTPS
|
||||
boundary, SSH, the cloud switch, the machine's name, and the factory
|
||||
return.
|
||||
|
||||
The daemon reads the commissioning record (commissioning.json in the
|
||||
The daemon reads the setup record (setup.json in the
|
||||
data directory) and the account record (users) once, at its start, and
|
||||
keeps them in memory; only its own routes write them. A test that needs
|
||||
a different record installs the file under a forgectrl restart (a
|
||||
@@ -34,13 +34,13 @@ from ..catalog import test
|
||||
from .. import hw
|
||||
from .forgectrl import lan_ip
|
||||
|
||||
RECORD = "commissioning.json"
|
||||
RECORD = "setup.json"
|
||||
USERS = "users"
|
||||
OVERRIDE = "commissioning-override"
|
||||
OVERRIDE = "setup-override"
|
||||
SSH_FLAG = "ssh-enabled"
|
||||
TLS_BASE = "https://127.0.0.1" # FORGECTRL_TLS_URL overrides (host tests)
|
||||
|
||||
# The image's required wizards, all at version 1 (commission.c).
|
||||
# The image's required wizards, all at version 1 (setup.c).
|
||||
REQUIRED_WIZARDS = ("advisories", "account", "preferences", "machine", "cloud")
|
||||
ADVISORY_DOCS = ("safety-and-risk", "licenses", "privacy", "cloud-service")
|
||||
SAFETY_PHRASE = "I UNDERSTAND"
|
||||
@@ -176,8 +176,8 @@ def record_without_wizards(record):
|
||||
|
||||
|
||||
def complete_record(status, base=None, account_name="bench"):
|
||||
"""A complete commissioning record for a machine that must count as
|
||||
commissioned: every advisory at the hash GET /wiz reports, the
|
||||
"""A complete setup record for a machine that must count as
|
||||
set up: every advisory at the hash GET /wiz reports, the
|
||||
press recorded, an account (the one of `base` when it has one),
|
||||
every wizard of the catalog at its version, and the run complete.
|
||||
`status` is a GET /wiz body; `base` is an existing record to keep
|
||||
@@ -352,12 +352,12 @@ def gate_is_open():
|
||||
|
||||
# -------------------------------------------------------------- the gate
|
||||
|
||||
_GATE_COVERS = [("forgectrl", "src/commission.*"), ("forgectrl", "src/super.c"),
|
||||
_GATE_COVERS = [("forgectrl", "src/setup.*"), ("forgectrl", "src/super.c"),
|
||||
("forgectrl", "src/main.c"), ("forgectrl", "src/paths.h")]
|
||||
|
||||
|
||||
@test("commission.gate-blocks-controllers", title="The commissioning gate spawns no controller",
|
||||
subsystem="commission", kind="auto", hardware="takeover", mode="grbl", est_min=5,
|
||||
@test("setup.gate-blocks-controllers", title="The setup gate spawns no controller",
|
||||
subsystem="setup", kind="auto", hardware="takeover", mode="grbl", est_min=5,
|
||||
covers=_GATE_COVERS, requires=["forgectrl.auth"], precheck=gate_is_open,
|
||||
description="With the override removed and a record that lacks every required wizard "
|
||||
"(installed under a forgectrl restart, the consent and the account kept), "
|
||||
@@ -374,11 +374,11 @@ def gate_blocks_controllers(ctx):
|
||||
ctx.log("before: gate %s, override %s, required %s", before.get("gate"), before.get("override"),
|
||||
before.get("required"))
|
||||
raw = read_file(record_path())
|
||||
ctx.check(raw, "no commissioning record at %s", record_path())
|
||||
ctx.check(raw, "no setup record at %s", record_path())
|
||||
try:
|
||||
record = json.loads(raw.decode("utf-8"))
|
||||
except ValueError as e:
|
||||
ctx.fail("the commissioning record is not JSON: %s", e)
|
||||
ctx.fail("the setup record is not JSON: %s", e)
|
||||
lacking = json.dumps(record_without_wizards(record), indent=1, sort_keys=True).encode() + b"\n"
|
||||
had_override = override_present()
|
||||
ev["override_before"] = had_override
|
||||
@@ -391,7 +391,7 @@ def gate_blocks_controllers(ctx):
|
||||
ctx.log("/mode after the restart: %s (after %s s)", m, took)
|
||||
ctx.check(m.get("controller") == "gated" and m.get("gated") is True,
|
||||
"the supervisor did not report the gate: %s", m)
|
||||
ctx.check("commissioning required" in (m.get("why") or ""),
|
||||
ctx.check("setup required" in (m.get("why") or ""),
|
||||
"the reason does not name the missing wizards: %r", m.get("why"))
|
||||
w = wiz(fc)
|
||||
ev["gated_wiz"] = wiz_summary(w)
|
||||
@@ -439,11 +439,11 @@ def gate_blocks_controllers(ctx):
|
||||
ctx.log("restored: gate %s, controller %s", after.get("gate"), m.get("controller"))
|
||||
|
||||
|
||||
@test("commission.override-until-reboot", title="The override stands until the next reboot",
|
||||
subsystem="commission", kind="auto", est_min=1,
|
||||
@test("setup.override-until-reboot", title="The override stands until the next reboot",
|
||||
subsystem="setup", kind="auto", est_min=1,
|
||||
covers=_GATE_COVERS, requires=["forgectrl.auth"],
|
||||
description="The bench seed: the dev image's forgetest init script writes "
|
||||
"/run/forgefirm/commissioning-override at boot. With it present, GET /wiz "
|
||||
"/run/forgefirm/setup-override at boot. With it present, GET /wiz "
|
||||
"reports the override and an open gate, the reason names the wizards it lifts "
|
||||
"when any is required, and GET /mode reports the controller running and not "
|
||||
"gated. The file lives on tmpfs (/proc/mounts), so a reboot removes it by "
|
||||
@@ -486,9 +486,9 @@ def override_until_reboot(ctx):
|
||||
|
||||
# ------------------------------------------------------------ advisories
|
||||
|
||||
@test("commission.advisories-rehash", title="An advisory is accepted at its current hash only",
|
||||
subsystem="commission", kind="auto", hardware="takeover", est_min=3,
|
||||
covers=[("forgectrl", "src/advisories.*"), ("forgectrl", "src/commission.*"),
|
||||
@test("setup.advisories-rehash", title="An advisory is accepted at its current hash only",
|
||||
subsystem="setup", kind="auto", hardware="takeover", est_min=3,
|
||||
covers=[("forgectrl", "src/advisories.*"), ("forgectrl", "src/setup.*"),
|
||||
("forgectrl", "src/wiz.*"), ("forgectrl", "src/sha256.*"), ("forgectrl", "src/main.c"),
|
||||
("forgectrl", "src/ui/md.js"), ("forgectrl", "src/ui/embed_docs.cmake")],
|
||||
requires=["forgectrl.auth"],
|
||||
@@ -504,7 +504,7 @@ def advisories_rehash(ctx):
|
||||
fc = ctx.forgectrl
|
||||
ev = ctx.evidence
|
||||
raw = read_file(record_path())
|
||||
ctx.check(raw, "no commissioning record at %s", record_path())
|
||||
ctx.check(raw, "no setup record at %s", record_path())
|
||||
before = wiz(fc)
|
||||
ev["before"] = wiz_summary(before)
|
||||
docs = {d.get("id"): d for d in before.get("documents") or []}
|
||||
@@ -579,7 +579,7 @@ def advisories_rehash(ctx):
|
||||
|
||||
# -------------------------------------------------------------- the login
|
||||
|
||||
@test("commission.account-login", title="The panel login over HTTPS", subsystem="commission",
|
||||
@test("setup.account-login", title="The panel login over HTTPS", subsystem="setup",
|
||||
kind="auto", est_min=2,
|
||||
covers=[("forgectrl", "src/auth.*"), ("forgectrl", "src/session.*"), ("forgectrl", "src/users.*"),
|
||||
("forgectrl", "src/tls.*"), ("forgectrl", "src/peer.*"), ("forgectrl", "src/main.c"),
|
||||
@@ -702,7 +702,7 @@ def login_checks(ctx, name, pw):
|
||||
|
||||
# ------------------------------------------------------- HTTPS boundary
|
||||
|
||||
@test("commission.https-only-writes", title="Writes from the LAN go to HTTPS", subsystem="commission",
|
||||
@test("setup.https-only-writes", title="Writes from the LAN go to HTTPS", subsystem="setup",
|
||||
kind="auto", est_min=1,
|
||||
covers=[("forgectrl", "src/auth.*"), ("forgectrl", "src/tls.*"), ("forgectrl", "src/peer.*"),
|
||||
("forgectrl", "src/main.c")],
|
||||
@@ -802,8 +802,8 @@ def restore_ssh(fc, before):
|
||||
write_file(flag, b"" if before.get("enabled") else None)
|
||||
|
||||
|
||||
@test("commission.ssh-until-reboot", title="SSH is switched from the panel until the next reboot",
|
||||
subsystem="commission", kind="auto", est_min=1,
|
||||
@test("setup.ssh-until-reboot", title="SSH is switched from the panel until the next reboot",
|
||||
subsystem="setup", kind="auto", est_min=1,
|
||||
covers=[("forgectrl", "src/main.c"), ("forgectrl", "src/paths.h")],
|
||||
requires=["forgectrl.auth"],
|
||||
description="GET /system/ssh reports enabled, running, and dev_image, and agrees with the "
|
||||
@@ -888,8 +888,8 @@ def ssh_until_reboot(ctx):
|
||||
|
||||
# ---------------------------------------------------------------- cloud
|
||||
|
||||
@test("commission.cloud-disabled-surface", title="Nothing points at the cloud while it is off",
|
||||
subsystem="commission", kind="auto", mode="grbl", est_min=1,
|
||||
@test("setup.cloud-disabled-surface", title="Nothing points at the cloud while it is off",
|
||||
subsystem="setup", kind="auto", mode="grbl", est_min=1,
|
||||
covers=[("forgectrl", "src/main.c"), ("forgectrl", "src/super.c"), ("forgectrl", "src/settings.*"),
|
||||
("forgectrl", "src/wiz.c"), ("forgectrl", "src/hooks.h")],
|
||||
requires=["forgectrl.settings-bounds"],
|
||||
@@ -971,8 +971,8 @@ def cloud_disabled_surface(ctx):
|
||||
|
||||
# ---------------------------------------------------------- the operator
|
||||
|
||||
@test("commission.factory-return", title="The return to the factory firmware is guarded",
|
||||
subsystem="commission", kind="auto", est_min=1,
|
||||
@test("setup.factory-return", title="The return to the factory firmware is guarded",
|
||||
subsystem="setup", kind="auto", est_min=1,
|
||||
covers=[("forgectrl", "src/update.*"), ("forgectrl", "src/main.c"), ("forgectrl", "src/ui/wizard.js")],
|
||||
requires=["update.slots-and-signature"],
|
||||
description="The return itself reboots the machine as a Glowforge, so it never runs from the "
|
||||
@@ -1034,8 +1034,8 @@ def cmdlines():
|
||||
return out
|
||||
|
||||
|
||||
@test("commission.machine-name", title="The machine names itself from its MAC address",
|
||||
subsystem="commission", kind="auto", est_min=1,
|
||||
@test("setup.machine-name", title="The machine names itself from its MAC address",
|
||||
subsystem="setup", kind="auto", est_min=1,
|
||||
requires=["forgectrl.auth"],
|
||||
description="The machine calls itself forgefirm-<xxxx>, where xxxx is the last four hex "
|
||||
"digits of its WiFi MAC address, so two machines on one network answer to "
|
||||
@@ -1213,12 +1213,12 @@ def finished_first_run(ctx):
|
||||
"the controller did not come back after the setup: %s", m)
|
||||
|
||||
|
||||
_FIRST_RUN_REQUIRES = ["commission.gate-blocks-controllers", "commission.advisories-rehash"]
|
||||
_FIRST_RUN_REQUIRES = ["setup.gate-blocks-controllers", "setup.advisories-rehash"]
|
||||
|
||||
|
||||
@test("commission.first-run-flow", title="The first run, driven as the page drives it", subsystem="commission",
|
||||
@test("setup.first-run-flow", title="The first run, driven as the page drives it", subsystem="setup",
|
||||
kind="operator", hardware="takeover", est_min=3,
|
||||
covers=[("forgectrl", "src/wiz.*"), ("forgectrl", "src/commission.*"), ("forgectrl", "src/users.*"),
|
||||
covers=[("forgectrl", "src/wiz.*"), ("forgectrl", "src/setup.*"), ("forgectrl", "src/users.*"),
|
||||
("forgectrl", "src/session.*"), ("forgectrl", "src/button.*"), ("forgectrl", "src/led.*"),
|
||||
("forgectrl", "src/sheetid.*"), ("forgectrl", "src/advisories.*"), ("forgectrl", "src/hooks.h"),
|
||||
("forgectrl", "src/main.c")],
|
||||
@@ -1309,11 +1309,11 @@ def first_run_flow(ctx):
|
||||
ctx.log("the run completed: gate open, the controller back")
|
||||
|
||||
|
||||
@test("commission.first-run-page", title="The first-run page, walked once", subsystem="commission",
|
||||
@test("setup.first-run-page", title="The first-run page, walked once", subsystem="setup",
|
||||
kind="operator", hardware="takeover", est_min=6,
|
||||
covers=[("forgectrl", "src/ui/wizard.*"), ("forgectrl", "src/ui/forms.js"), ("forgectrl", "src/ui/md.js"),
|
||||
("forgectrl", "src/ui/login.*"), ("forgectrl", "src/ui/embed_docs.cmake"), ("forgectrl", "src/tls.*")],
|
||||
requires=_FIRST_RUN_REQUIRES + ["commission.first-run-flow"],
|
||||
requires=_FIRST_RUN_REQUIRES + ["setup.first-run-flow"],
|
||||
actions=["button"], hands=["workstation"],
|
||||
steps=["From the workstation open https://<address>/ and accept the self-signed certificate.",
|
||||
"Click through the setup: scroll each document to its end and accept it (type I "
|
||||
@@ -1328,7 +1328,7 @@ def first_run_flow(ctx):
|
||||
"clicks through (the documents accepted, the press step with the LEDs "
|
||||
"breathing teal, the account created, the run complete, the gate open, the "
|
||||
"controller back). The operator confirms the panel opened. Everything the run "
|
||||
"wrote is restored as commission.first-run-flow restores it.")
|
||||
"wrote is restored as setup.first-run-flow restores it.")
|
||||
def first_run_page(ctx):
|
||||
fc = ctx.forgectrl
|
||||
ev = ctx.evidence
|
||||
@@ -1364,8 +1364,8 @@ def first_run_page(ctx):
|
||||
ctx.confirm("Did the control panel open after Finish?")
|
||||
|
||||
|
||||
@test("commission.cert-page", title="The certificate is checkable before the warning",
|
||||
subsystem="commission", kind="auto", est_min=1,
|
||||
@test("setup.cert-page", title="The certificate is checkable before the warning",
|
||||
subsystem="setup", kind="auto", est_min=1,
|
||||
covers=[("forgectrl", "src/tls.*"), ("forgectrl", "src/main.c"),
|
||||
("forgectrl", "src/ui/wizard.html"), ("forgectrl", "src/ui/wizard.js")],
|
||||
requires=["forgectrl.auth"],
|
||||
@@ -1423,9 +1423,9 @@ def flag_of(w, level, wid):
|
||||
return None
|
||||
|
||||
|
||||
@test("commission.what-changed", title="A replaced part asks for its checks again",
|
||||
subsystem="commission", kind="auto", est_min=2, hardware="takeover",
|
||||
covers=[("forgectrl", "src/commission.*"), ("forgectrl", "src/wiz.*"), ("forgectrl", "src/main.c"),
|
||||
@test("setup.what-changed", title="A replaced part asks for its checks again",
|
||||
subsystem="setup", kind="auto", est_min=2, hardware="takeover",
|
||||
covers=[("forgectrl", "src/setup.*"), ("forgectrl", "src/wiz.*"), ("forgectrl", "src/main.c"),
|
||||
("forgectrl", "src/ui/panel.js"), ("forgectrl", "src/ui/index.html"),
|
||||
("forgectrl", "src/ui/help.js")],
|
||||
requires=["forgectrl.auth"],
|
||||
@@ -1493,9 +1493,9 @@ def what_changed(ctx):
|
||||
ctx.check(bool(mode(fc).get("gated")) == gated_before, "the gate does not read as before")
|
||||
|
||||
|
||||
@test("commission.record-export", title="The record: JSON, the printable page, and the log bundle",
|
||||
subsystem="commission", kind="auto", est_min=2,
|
||||
covers=[("forgectrl", "src/commission.*"), ("forgectrl", "src/recordhtml.*"), ("forgectrl", "src/wiz.*"),
|
||||
@test("setup.record-export", title="The record: JSON, the printable page, and the log bundle",
|
||||
subsystem="setup", kind="auto", est_min=2,
|
||||
covers=[("forgectrl", "src/setup.*"), ("forgectrl", "src/recordhtml.*"), ("forgectrl", "src/wiz.*"),
|
||||
("forgectrl", "src/logs.*"), ("forgectrl", "src/main.c"), ("forgectrl", "src/ui/index.html"),
|
||||
("forgectrl", "src/ui/panel.js"), ("forgectrl", "src/ui/wizard.html")],
|
||||
requires=["forgectrl.auth", "logs.tree-tail-export"],
|
||||
@@ -1504,7 +1504,7 @@ def what_changed(ctx):
|
||||
"named after the sheet id; GET /wiz/record.html is a page with no script that "
|
||||
"carries the sheet id and every completed wizard; without the token and without "
|
||||
"a login both are refused (403); and the sanitized log export carries "
|
||||
"system/commissioning.json, parseable, with the same sheet id and no panel token.")
|
||||
"system/setup.json, parseable, with the same sheet id and no panel token.")
|
||||
def record_export(ctx):
|
||||
import gzip
|
||||
import io
|
||||
@@ -1544,7 +1544,7 @@ def record_export(ctx):
|
||||
ctx.check(st == 200 and "text/html" in (hdrs.get("content-type") or ""), "GET /wiz/record.html -> %s %s",
|
||||
st, hdrs.get("content-type"))
|
||||
ctx.check("<script" not in page.lower(), "the printable page carries a script")
|
||||
ctx.check(sid in page and "commissioning record" in page, "the page lacks the sheet id or its title")
|
||||
ctx.check(sid in page and "setup record" in page, "the page lacks the sheet id or its title")
|
||||
titles = {z["id"]: z["title"] for z in w.get("wizards") or []}
|
||||
missing = [d for d in done if titles.get(d) and ("<h3>" + titles[d]) not in page]
|
||||
ev["missing_on_page"] = missing
|
||||
@@ -1565,11 +1565,11 @@ def record_export(ctx):
|
||||
try:
|
||||
tf = tarfile.open(fileobj=io.BytesIO(gzip.decompress(data)))
|
||||
for m in tf.getmembers():
|
||||
if m.isfile() and m.name.endswith("system/commissioning.json"):
|
||||
if m.isfile() and m.name.endswith("system/setup.json"):
|
||||
member = tf.extractfile(m).read()
|
||||
except (OSError, tarfile.TarError, EOFError) as e:
|
||||
ctx.fail("export is not a readable tar.gz: %s", e)
|
||||
ctx.check(member is not None, "the bundle lacks system/commissioning.json")
|
||||
ctx.check(member is not None, "the bundle lacks system/setup.json")
|
||||
if member is not None:
|
||||
try:
|
||||
inside = json.loads(member.decode("utf-8"))
|
||||
@@ -1587,12 +1587,12 @@ def record_export(ctx):
|
||||
ctx.log("record %d wizards; page %d bytes; bundle carries the record", len(done), ev["page_bytes"])
|
||||
|
||||
|
||||
@test("commission.mirror", title="One browser drives a check; a second one follows",
|
||||
subsystem="commission", kind="auto", est_min=2, hardware="takeover",
|
||||
@test("setup.mirror", title="One browser drives a check; a second one follows",
|
||||
subsystem="setup", kind="auto", est_min=2, hardware="takeover",
|
||||
covers=[("forgectrl", "src/wizdark.*"), ("forgectrl", "src/wizcalc.*"), ("forgectrl", "src/wiz.*"),
|
||||
("forgectrl", "src/session.*"), ("forgectrl", "src/ui/wizard.js"),
|
||||
("forgectrl", "src/ui/wizard.html"), ("forgectrl", "src/ui/wizard.css")],
|
||||
requires=["forgectrl.auth", "commission.account-login", "commission.check-sensors"],
|
||||
requires=["forgectrl.auth", "setup.account-login", "setup.check-sensors"],
|
||||
description="With a temporary account (made and removed as the login test does) and two "
|
||||
"login sessions: the first session starts the sensors check; GET /wiz/dark "
|
||||
"reports the run as owned and its own to the first session, not to the second, "
|
||||
+41
-31
@@ -1,4 +1,4 @@
|
||||
"""commission.check-* - the setup's checks (the dark wizards), driven the
|
||||
"""setup.check-* - the setup's checks (the dark wizards), driven the
|
||||
way the page drives them: POST /wiz/<id>/start, GET /wiz/dark polled,
|
||||
the prompts answered from here (the bench fixture works the lid and the
|
||||
button; a confirmation is answered yes once the snapshot exists), the
|
||||
@@ -12,7 +12,7 @@ import time
|
||||
from ..catalog import test
|
||||
from .. import hw
|
||||
from ..baseline import read_position
|
||||
from .commission import wiz, Restore # noqa: F401 - Restore is re-exported for the sheet
|
||||
from .setup import wiz, Restore # noqa: F401 - Restore is re-exported for the sheet
|
||||
|
||||
POLL_S = 1.0
|
||||
# What the machine's own press prompt says, used when it sends no text.
|
||||
@@ -22,7 +22,7 @@ PRESS_TEXT = "The button is lit white. Press it now: the laser fires after your
|
||||
# (74) for the first seconds and settles near 630.
|
||||
PURGE_SPINUP_S = 30
|
||||
DARK_COVERS = [("forgectrl", "src/wizdark.*"), ("forgectrl", "src/wizcalc.*"),
|
||||
("forgectrl", "src/wiz.*"), ("forgectrl", "src/commission.*"),
|
||||
("forgectrl", "src/wiz.*"), ("forgectrl", "src/setup.*"),
|
||||
("forgectrl", "src/main.c"), ("forgectrl", "src/ui/wizard.*")]
|
||||
|
||||
|
||||
@@ -113,8 +113,8 @@ def run_check(ctx, wid, on_prompt, timeout_s):
|
||||
return last
|
||||
|
||||
|
||||
@test("commission.check-switches", title="The switches check follows the lid and the button",
|
||||
subsystem="commission", kind="operator", est_min=3,
|
||||
@test("setup.check-switches", title="The switches check follows the lid and the button",
|
||||
subsystem="setup", kind="operator", est_min=3,
|
||||
covers=DARK_COVERS + [("forgectrl", "src/status.c")],
|
||||
requires=["forgectrl.auth"], actions=["lid", "button"],
|
||||
description="POST /wiz/switches/start; the check asks for the lid to open and close and for "
|
||||
@@ -148,30 +148,40 @@ def check_switches(ctx):
|
||||
ctx.log("switches: %s", json.dumps(r))
|
||||
|
||||
|
||||
@test("commission.check-sensors", title="The sensors check reads a plausible machine at rest",
|
||||
subsystem="commission", kind="auto", est_min=2,
|
||||
@test("setup.check-sensors", title="The sensors check reads a plausible machine at rest",
|
||||
subsystem="setup", kind="auto", est_min=2,
|
||||
covers=DARK_COVERS + [("forgectrl", "src/status.c"), ("forgectrl", "src/accel.c"),
|
||||
("forgectrl", "src/cool.c")],
|
||||
requires=["forgectrl.auth"],
|
||||
description="POST /wiz/sensors/start; ten seconds of readings, then the room-temperature "
|
||||
"prompt, answered Skip so no offset is written. The result carries both coolant "
|
||||
"temperatures, the chassis and SoC, the lid IR maxima, the accelerometer event "
|
||||
"count, the supply power-good, the HV current, and the idle fan speeds; the "
|
||||
"record carries sensors at version 1 and cool_temp_offset_c reads as before.")
|
||||
description="POST /wiz/sensors/start; ten seconds of readings and no question asked. The "
|
||||
"result carries both coolant temperatures, the chassis and SoC, the lid IR "
|
||||
"maxima, the accelerometer event count, the supply power-good, the HV current, "
|
||||
"and the idle fan speeds; the record carries sensors at version 1 and the "
|
||||
"settings read as before (the check writes none).")
|
||||
def check_sensors(ctx):
|
||||
with Restore(ctx, ["cool_temp_offset_c"]):
|
||||
last = run_check(ctx, "sensors", lambda p: "Skip" if p.get("id") == "room-temp" else None, 120)
|
||||
r = last.get("result") or {}
|
||||
for k in ("coolant_down_c", "coolant_up_c", "lid_ir_max", "laser_pgood", "hv_current_max",
|
||||
"exhaust_rpm_idle", "intake_rpm_idle"):
|
||||
ctx.check(k in r, "the result lacks %s: %s", k, r)
|
||||
ctx.check(r.get("laser_pgood") == 1, "power-good read %s", r.get("laser_pgood"))
|
||||
ctx.log("sensors: coolant %s/%s C, IR %s, HV %s", r.get("coolant_down_c"), r.get("coolant_up_c"),
|
||||
r.get("lid_ir_max"), r.get("hv_current_max"))
|
||||
fc = ctx.forgectrl
|
||||
ev = ctx.evidence
|
||||
before = fc.settings() or {}
|
||||
ev["settings_before"] = dict(before)
|
||||
|
||||
def on_prompt(p):
|
||||
ctx.fail("the sensors check asked a question: %s", p)
|
||||
last = run_check(ctx, "sensors", on_prompt, 120)
|
||||
r = last.get("result") or {}
|
||||
for k in ("coolant_down_c", "coolant_up_c", "lid_ir_max", "laser_pgood", "hv_current_max",
|
||||
"exhaust_rpm_idle", "intake_rpm_idle"):
|
||||
ctx.check(k in r, "the result lacks %s: %s", k, r)
|
||||
ctx.check(r.get("laser_pgood") == 1, "power-good read %s", r.get("laser_pgood"))
|
||||
after = fc.settings() or {}
|
||||
changed = {k: (before.get(k), after.get(k)) for k in set(before) | set(after)
|
||||
if before.get(k) != after.get(k)}
|
||||
ctx.check(not changed, "the sensors check wrote a setting: %s", changed)
|
||||
ctx.log("sensors: coolant %s/%s C, IR %s, HV %s", r.get("coolant_down_c"), r.get("coolant_up_c"),
|
||||
r.get("lid_ir_max"), r.get("hv_current_max"))
|
||||
|
||||
|
||||
@test("commission.check-airflow", title="The airflow check measures the fans and sets the floors",
|
||||
subsystem="commission", kind="auto", hardware="takeover", est_min=3,
|
||||
@test("setup.check-airflow", title="The airflow check measures the fans and sets the floors",
|
||||
subsystem="setup", kind="auto", hardware="takeover", est_min=3,
|
||||
covers=DARK_COVERS + [("forgectrl", "src/cool.c"), ("forgectrl", "src/airflow.*"),
|
||||
("forgectrl", "src/gates.c"), ("forgectrl", "src/super.c")],
|
||||
requires=["forgectrl.auth", "cooling.fan-gate-trips"],
|
||||
@@ -229,8 +239,8 @@ def check_airflow(ctx):
|
||||
ctx.check(ok is not None, "the controller did not come back after the check")
|
||||
|
||||
|
||||
@test("commission.check-cameras", title="The cameras check captures both cameras",
|
||||
subsystem="commission", kind="operator", est_min=2,
|
||||
@test("setup.check-cameras", title="The cameras check captures both cameras",
|
||||
subsystem="setup", kind="operator", est_min=2,
|
||||
covers=DARK_COVERS + [("forgectrl", "src/cam.c")],
|
||||
requires=["forgectrl.auth", "camera.snapshot"], actions=["lid"],
|
||||
description="POST /wiz/cameras/start with the lid closed: a lid snapshot, the question, a "
|
||||
@@ -264,8 +274,8 @@ def check_cameras(ctx):
|
||||
shots.get("lid", {}).get("bytes", 0), shots.get("head", {}).get("bytes", 0))
|
||||
|
||||
|
||||
@test("commission.check-motion", title="The motion check proves the rail, the lens reference, and the jogs",
|
||||
subsystem="commission", kind="auto", hardware="takeover", est_min=5,
|
||||
@test("setup.check-motion", title="The motion check proves the rail, the lens reference, and the jogs",
|
||||
subsystem="setup", kind="auto", hardware="takeover", est_min=5,
|
||||
covers=DARK_COVERS + [("forgectrl", "src/super.c"), ("forgectrl", "src/liveness.c"),
|
||||
("forgectrl", "src/lenshome.*"),
|
||||
("forgectrl", "src/accel.c"), ("forgectrl", "src/cool.c")],
|
||||
@@ -305,8 +315,8 @@ def check_motion(ctx):
|
||||
r.get("rest"), {k: (v.get("p2p_lp_x"), v.get("p2p_lp_y")) for k, v in moves.items()})
|
||||
|
||||
|
||||
@test("commission.check-flow-verify", title="The flow check runs as a setup check",
|
||||
subsystem="commission", kind="auto", hardware="takeover", est_min=5,
|
||||
@test("setup.check-flow-verify", title="The flow check runs as a setup check",
|
||||
subsystem="setup", kind="auto", hardware="takeover", est_min=5,
|
||||
covers=DARK_COVERS + [("forgectrl", "src/diag.c")],
|
||||
requires=["forgectrl.auth", "cooling.aa-offset-calibrate", "cooling.flow-verify"],
|
||||
description="POST /wiz/cooling.flow-verify/start drives the flow-verify diagnostic through the "
|
||||
@@ -323,8 +333,8 @@ def check_flow_verify(ctx):
|
||||
r.get("flow_rise"), r.get("noflow_rise"), r.get("thin_margin"))
|
||||
|
||||
|
||||
@test("commission.cloud-header-capture", title="The cloud header check takes one print's envelope",
|
||||
subsystem="commission", kind="operator", hardware="takeover", mode="grbl", est_min=8,
|
||||
@test("setup.cloud-header-capture", title="The cloud header check takes one print's envelope",
|
||||
subsystem="setup", kind="operator", hardware="takeover", mode="grbl", est_min=8,
|
||||
covers=DARK_COVERS + [("forgectrl", "src/super.c"),
|
||||
("python3-gfhardware", "gfhardware/machine.py"),
|
||||
("python3-gfhardware", "forgefirm-app/gfcloud.py"),
|
||||
+6
-6
@@ -1,4 +1,4 @@
|
||||
"""commission.sheet - the setup's sheet (the live wizards) in one run,
|
||||
"""setup.sheet - the setup's sheet (the live wizards) in one run,
|
||||
driven the way the page drives them: POST /wiz/<id>/start, GET /wiz/dark
|
||||
polled, the prompts answered from here, each burn armed by a press, the
|
||||
result judged, and every setting a wizard wrote put back as found. One
|
||||
@@ -18,13 +18,13 @@ restored. The page is where the real numbers go in.
|
||||
import re
|
||||
|
||||
from ..catalog import test
|
||||
from .commission_dark import run_check, Restore
|
||||
from .setup_dark import run_check, Restore
|
||||
|
||||
SHEET_COVERS = [("forgectrl", "src/wizlive.*"), ("forgectrl", "src/wizrun.h"),
|
||||
("forgectrl", "src/lens.*"), ("forgectrl", "src/sheet.*"),
|
||||
("forgectrl", "src/font_hershey.*"), ("forgectrl", "src/jobstream.*"),
|
||||
("forgectrl", "src/curverec.*"), ("forgectrl", "src/wizdark.*"),
|
||||
("forgectrl", "src/wiz.*"), ("forgectrl", "src/commission.*"),
|
||||
("forgectrl", "src/wiz.*"), ("forgectrl", "src/setup.*"),
|
||||
("forgectrl", "src/main.c"), ("forgectrl", "src/ui/wizard.*"),
|
||||
("forgectrl", "src/super.c"),
|
||||
("forgectrl", "src/status.c"), ("forgectrl", "src/cool.c"),
|
||||
@@ -222,10 +222,10 @@ def burn(ctx, wid, answers, want):
|
||||
file_card(ctx, wid)
|
||||
|
||||
|
||||
@test("commission.sheet", title="The commissioning sheet: the placement, the frame, and the five cards",
|
||||
subsystem="commission", kind="live", hardware="takeover", mode="grbl", est_min=35,
|
||||
@test("setup.sheet", title="The setup sheet: the placement, the frame, and the five cards",
|
||||
subsystem="setup", kind="live", hardware="takeover", mode="grbl", est_min=35,
|
||||
covers=SHEET_COVERS,
|
||||
requires=["forgectrl.auth", "commission.check-motion", "laser.emission-witness", "cooling.flow-verify"],
|
||||
requires=["forgectrl.auth", "setup.check-motion", "laser.emission-witness", "cooling.flow-verify"],
|
||||
actions=["button"], hands=["scrap"],
|
||||
steps=["A piece of wood at least 200 x 150 mm (8 x 6 in) on the bed, pushed as far left as "
|
||||
"it goes with its top edge at the top of the cut area (the head's home corner); "
|
||||
Reference in New Issue
Block a user