Check the progress a print reports, where a print already runs

Two tests already run a print end to end, and progress is a property of a
running print, so the checks go there rather than into a test of their own
that would cost the operator another job.

cloud.pause-resume takes the job that fits the ring: the client names the
length it is reporting against, and the operator is asked the question only a
person can answer, whether the bar actually moved.

cloud.oversize-stream takes the job that does not fit, which is where a moving
denominator would show: the kernel's program total grows all run long under a
live feed, and the test already samples it growing, so the check is that the
figure progress divides by is larger than that - the job, not the count the
ring had swallowed when the run started.

The forgetest replay plays a captured log from a build that predates the line,
so it carries the line where the current build emits it, as it already does
for the warm-up and the rest.

BRINGUP's cloud item now says a print reports itself again, and what is left
on it is a print watched from the app.
This commit is contained in:
ScottW514
2026-08-20 14:16:27 -04:00
parent 0ef2047625
commit aaabfdf9b9
3 changed files with 62 additions and 13 deletions
+49 -5
View File
@@ -347,6 +347,24 @@ def message(line):
return line.split(" ", 2)[-1] if line else None
PROGRESS_MARK = "print:progress: reporting against "
def progress_denominator(lines):
"""The length the print reported its progress against, or None.
The client names it once per job, which is the number worth having: it
is what every progress frame of that job divides by.
"""
for ln in lines:
if PROGRESS_MARK in ln:
try:
return int(ln.split(PROGRESS_MARK, 1)[1].split()[0])
except (IndexError, ValueError):
return None
return None
def session_live(pid):
"""(live, detail): the running cloud client (gfcloud[pid]) has a live
service session when its last websocket state line is 'ready' - a
@@ -731,7 +749,8 @@ def hunt_lid_open(ctx):
"latch stays unlocked and the armed window open through the pause. The same "
"print is where the job's lifecycle shows: a print warms up before its first "
"fire and rests after its park, both non-zero, while the connect-time hunt in "
"the same session does neither.")
"the same session does neither. It is also where the app's progress bar shows: "
"the run reports how far it has gotten, against the job's own length.")
def pause_resume(ctx):
ev = ctx.evidence
offset = enter_cloud(ctx)
@@ -791,9 +810,20 @@ def pause_resume(ctx):
ev["holds_before_the_print"] = early_holds
ctx.check(not early_holds, "a hunt or motion held for a print's periods: %s", early_holds[:2])
# The app's progress bar, from the same print. The machine reports where
# it has gotten to every 30 s and at every phase change, against the
# job's own length, which it names once when the run starts.
declared = progress_denominator(lines)
ev["progress_total"] = declared
ctx.log("progress reported against %s bytes", declared)
ctx.check(declared is not None, "the print reported no progress at all")
ctx.check(declared and declared > 0, "the print reported progress against %s bytes", declared)
ctx.confirm("Did the app show the print's progress advancing while it cut, rather than "
"standing still or jumping straight to nearly finished?")
settle_cloud(ctx, offset)
ctx.log("PASS: button pause/resume mid-print, warm-up and rest observed, job completed "
"and parked")
ctx.log("PASS: button pause/resume mid-print, warm-up and rest observed, progress reported, "
"job completed and parked")
@test("cloud.oversize-stream", title="A print longer than the ring is fed while it plays",
@@ -810,8 +840,9 @@ def pause_resume(ctx):
"is several times the ring: the machine holds the job in memory, fills the ring, "
"starts, and tops the ring up as it drains. This checks the signature of that - "
"the device in live-feed mode, the kernel's program total growing during the run, "
"and no underrun - that a live-fed print pauses and resumes the factory way, the "
"ring having kept the history to back into, and that the job still cancels "
"and no underrun - that progress is reported against the job's length rather "
"than that growing total, that a live-fed print pauses and resumes the factory "
"way, the ring having kept the history to back into, and that the job cancels "
"cleanly.")
def oversize_stream(ctx):
ev = ctx.evidence
@@ -846,6 +877,19 @@ def oversize_stream(ctx):
ev["underruns_during"] = after
ctx.check(after == before, "the ring ran dry during the run (underruns %s -> %s)", before, after)
# Progress on a live feed is where a moving denominator would show. The
# kernel's program total is what just grew, and a bar divided by it would
# sit near full from the first frame to the last; the job's own length is
# what the report divides by, and on a job this long it is the larger
# number by a wide margin.
declared = progress_denominator(log_lines_since(GFCLOUD_LOG, offset))
ev["progress_total"] = declared
ctx.log("progress reported against %s bytes, kernel program total %s", declared, grown)
ctx.check(declared is not None, "the print reported no progress at all")
ctx.check(declared and first is not None and declared > first,
"progress is being reported against %s bytes, which is the ring's count (%s), not "
"the job", declared, first)
# The pause on a live feed: the ring keeps the history to back into, so a
# streamed print retraces and leads back on exactly like a preloaded one.
ev["max_backtrack"] = hw.sysfs_int("cnc/max_backtrack", 0)
+6 -3
View File
@@ -37,6 +37,8 @@ WARM_UP_LINE = ("2026-08-17T09:44:02.100000+00:00 gfcloud[1522] INFO "
"machine:_dwell warm up: holding 3.0 s")
COOL_DOWN_LINE = ("2026-08-17T09:45:31.700000+00:00 gfcloud[1522] INFO "
"machine:_dwell cool down: holding 10.0 s")
PROGRESS_LINE = ("2026-08-17T09:44:05.200000+00:00 gfcloud[1522] INFO "
"machine:__init__ print:progress: reporting against 47848 bytes every 30 s")
def cut(lines, marker, count=1):
@@ -289,9 +291,10 @@ class CloudSuiteTests(unittest.TestCase):
pre, rest = cut(lines, "waiting for button")
run_pre, rest = cut(rest, "current state: MachineState.RUNNING") # the PRINT's run
# The excerpt was captured before the machine held for a warm-up and
# a rest; the replay carries those two lines where it emits them now,
# rather than editing what the machine actually said that day.
run_pre = run_pre + [WARM_UP_LINE]
# a rest, and before it reported a print's progress; the replay
# carries those lines where it emits them now, rather than editing
# what the machine actually said that day.
run_pre = run_pre + [WARM_UP_LINE, PROGRESS_LINE]
pre, rest = pre + run_pre + [rest[0]], rest[1:]
mid, tail = cut(rest, at_end)
tail = tail + [COOL_DOWN_LINE]