From 408fbf9ee2efc4f0961f68780dfb1b5eae8823c5 Mon Sep 17 00:00:00 2001 From: "Youri T. K. K. Mattar" Date: Sun, 9 Aug 2026 13:48:17 -0300 Subject: [PATCH] test: cover seven behaviours the roadmap claimed and nothing re-ran MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit M12 made the suites continuous. This is about what they were continuously running, which was less than the milestones above claim. The restart policy firing at all; the backoff doubling through 200ms, 400ms, 800ms; on-abnormal treating a signal death as abnormal; a watchdog miss taking the restart policy; %N and %u against a running machine; oxctl reload; and output = "null" actually discarding. Each is asserted by a milestone that says "Verified:", and each was — once, by hand, on somebody's machine. flaky.toml is the whole story in one file. It has existed since M2, its comment describes the doubling exactly, and it was never reachable from `default`, so nothing ever started it. A unit file that documents a test nobody runs is worse than no unit file, because it reads as coverage. The watchdog one is the most interesting. ARCHITECTURE argues at length that a watchdog miss must take the restart policy where an oxctl stop overrides it, and calls that asymmetry the point of a watchdog. The only unit that ever missed a watchdog declared restart = "no", so only the half that changes nothing had run. Nothing was broken. All seven worked the first time they were asked to, which is worth saying plainly: this bought guarding, not fixing. 38 checks per architecture, 40 against the distribution image. Deferred: the backoff reset after a sustained Active period, which M2 also claims. Telling "reset" from "never incremented" needs an assertion about a counter's history, and substring matching over a log cannot make one. --- README.md | 5 ++-- ROADMAP.md | 49 +++++++++++++++++++++++++++++++++++++++- crates/xtask/src/main.rs | 33 +++++++++++++++++++++++++++ units/default.toml | 6 +++++ units/hangs-back.toml | 15 ++++++++++++ units/muted.toml | 9 ++++++++ units/reloader.toml | 8 +++++++ units/signalled.toml | 10 ++++++++ units/specifiers.toml | 10 ++++++++ 9 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 units/hangs-back.toml create mode 100644 units/muted.toml create mode 100644 units/reloader.toml create mode 100644 units/signalled.toml create mode 100644 units/specifiers.toml diff --git a/README.md b/README.md index 7d0f202..610ca50 100644 --- a/README.md +++ b/README.md @@ -203,7 +203,7 @@ getting it wrong strands the machine. ## Current state -Milestones 0 through 12 are done. oxinit boots under QEMU and runs as a +Milestones 0 through 13 are done. oxinit boots under QEMU and runs as a container's PID 1. | | | @@ -221,12 +221,13 @@ container's PID 1. | **M10** | A real distribution userspace, with dynamically linked services. | | **M11** | `after`, `requires` and `conflicts` enforced; `start-sec` bounds activation. | | **M12** | CI: every suite above, on every push. | +| **M13** | Coverage for seven behaviours the milestones claimed and nothing re-ran. | One `epoll` loop multiplexes the signalfd, the timerfd, the notify socket, the control socket, every socket unit's listening descriptor and every service cgroup's `cgroup.events`. One thread. No async runtime. -Nothing is scheduled after M12. [ROADMAP.md](ROADMAP.md) has the breakdown, +Nothing is scheduled after M13. [ROADMAP.md](ROADMAP.md) has the breakdown, including what each milestone was verified against and what was deferred out of it. diff --git a/ROADMAP.md b/ROADMAP.md index 924cc8d..db07209 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -752,10 +752,57 @@ surprise: an emulated ARM64 boot on an x86_64 runner costs nine seconds more than a native one. The 300-second budget picked in M9 turns out to be an order of magnitude of headroom rather than a tight fit. -## Beyond M12 +## M13 — Covering what was already claimed + +**Done.** + +M12 made the suites continuous. This is about what they were continuously +running, which turned out to be less than the milestones above claim. + +Seven behaviours had no coverage at all. Each is asserted by a milestone that +says "Verified:", and each was — once, by hand, on the machine of whoever +wrote it. + +| Claimed in | What nothing re-ran | +|------------|------------------------------------------------------| +| M2 | The restart policy firing at all. | +| M2 | The backoff doubling: 200ms, 400ms, 800ms. | +| M2 | `on-abnormal` treating a signal death as abnormal. | +| M2 / M3 | A watchdog miss taking the restart policy. | +| M1 | `%N` and `%u`, against a running machine. | +| M5 | `oxctl reload`. | +| M7 | `output = "null"` actually discarding. | + +**`flaky.toml` is the whole story in one file.** It has existed since M2, its +comment describes the doubling exactly, and it was never reachable from +`default` — so nothing ever started it. A unit file that documents a test +nobody runs is worse than no unit file, because it reads as coverage. + +The watchdog one is the most interesting. ARCHITECTURE argues at length that a +watchdog miss must take the restart policy where an `oxctl stop` overrides it, +and calls that asymmetry "the point of a watchdog". The only unit that missed a +watchdog declared `restart = "no"`, so only the half that changes nothing had +ever run. + +**Nothing was broken.** Every one of the seven worked the first time it was +asked to. That is the honest result and it is worth saying plainly: this +milestone bought guarding, not fixing. The code was right; nothing was +watching it. + +Verified on both architectures, in the container, and against the real Alpine +userspace: 38 checks per boot, 40 against the distribution image. + +Deferred out of M13: the backoff *reset* after a sustained `Active` period, +which M2 also claims. Distinguishing "reset" from "never incremented" needs an +assertion about a counter's history, and substring matching over a log cannot +make one. It needs a different kind of test than this suite is. + +## Beyond M13 Not planned, not designed, listed only so the questions have an answer: +- Coverage for `type = "datagram"` and `type = "seqpacket"` sockets, which + needs a fixture that speaks them; `listen-probe` only does streams. - A `CLOCK_REALTIME` timerfd with `TFD_TIMER_CANCEL_ON_SET`, so a calendar schedule survives the clock being stepped under it. Until then a wall-clock firing is computed as a monotonic delay and an NTP correction moves it by diff --git a/crates/xtask/src/main.rs b/crates/xtask/src/main.rs index 5305782..9d6a90c 100644 --- a/crates/xtask/src/main.rs +++ b/crates/xtask/src/main.rs @@ -288,6 +288,39 @@ const EXPECTED: &[(&str, &str)] = &[ "oxinit: tick-timer will start stamp in 3s", "M8: and re-arms with interval, so it fires again", ), + ( + "flaky exited with status 1, restarting in 200ms (restart 1)", + "M13: the restart policy fires, from the first failure", + ), + ( + "restarting in 400ms (restart 2)", + "M13: and the backoff doubles", + ), + ( + "restarting in 800ms (restart 3)", + "M13: and keeps doubling, which nothing had ever run", + ), + ( + "signalled terminated abnormally, restarting in", + "M13: on-abnormal sees a signal death as abnormal", + ), + ( + "oxinit: started signalled as pid", + "M13: and restarts for it", + ), + ( + "hangs-back missed its watchdog deadline", + "M13: a watchdog miss", + ), + ( + "hangs-back stopped, restarting in", + "M13: takes the restart policy, unlike a requested stop", + ), + ( + "oxinit-m13: specifiers.service runs as nobody", + "M13: %N and %u expand against a running machine", + ), + ("reloaded", "M5: oxctl reload, which no boot had ever run"), ( "oxinit: shutting down to power off", "M5: SIGTERM is handled", diff --git a/units/default.toml b/units/default.toml index 22468f4..42e0342 100644 --- a/units/default.toml +++ b/units/default.toml @@ -20,6 +20,12 @@ wants = [ "needy", "alpha", "switch", + "flaky", + "signalled", + "hangs-back", + "specifiers", + "muted", + "reloader", "quiet", "probe", "hangs", diff --git a/units/hangs-back.toml b/units/hangs-back.toml new file mode 100644 index 0000000..13ab66f --- /dev/null +++ b/units/hangs-back.toml @@ -0,0 +1,15 @@ +# Goes ready, stops pinging, and must come back. +# +# ARCHITECTURE argues this asymmetry is the entire point of a watchdog: a +# watchdog miss takes the restart policy, where an `oxctl stop` overrides it. +# `hangs` declares `restart = "no"`, so only half of that had ever run. +[unit] +description = "Hangs, and is restarted for it" + +[service] +type = "notify" +exec = "/bin/notify-probe --hang-after 1" +watchdog-sec = "2s" +stop-sec = "1s" +restart = "on-failure" +restart-sec = "400ms" diff --git a/units/muted.toml b/units/muted.toml new file mode 100644 index 0000000..1c7e32d --- /dev/null +++ b/units/muted.toml @@ -0,0 +1,9 @@ +# `output = "null"`. The assertion is an absence: this token must not reach +# the console, and `oxlogd` must not have a file for it either. +[unit] +description = "Writes to nowhere" + +[service] +type = "oneshot" +exec = "/bin/echo muted-should-never-appear" +output = "null" diff --git a/units/reloader.toml b/units/reloader.toml new file mode 100644 index 0000000..e0d611c --- /dev/null +++ b/units/reloader.toml @@ -0,0 +1,8 @@ +# `oxctl reload`, which M5 listed and no boot has ever run. +[unit] +description = "Re-reads the unit directories" +after = ["banner"] + +[service] +type = "oneshot" +exec = "/bin/oxctl reload" diff --git a/units/signalled.toml b/units/signalled.toml new file mode 100644 index 0000000..1dcea7e --- /dev/null +++ b/units/signalled.toml @@ -0,0 +1,10 @@ +# Killed by a signal rather than exiting. `on-abnormal` restarts on a signal +# or a timeout and *not* on a non-zero exit, which is the whole difference +# between it and `on-failure` — and nothing had ever taken either branch. +[unit] +description = "Dies on a signal" + +[service] +exec = '/bin/sh -c "/bin/kill -9 $$"' +restart = "on-abnormal" +restart-sec = "300ms" diff --git a/units/specifiers.toml b/units/specifiers.toml new file mode 100644 index 0000000..f6ae829 --- /dev/null +++ b/units/specifiers.toml @@ -0,0 +1,10 @@ +# `%N` and `%u`. M1 verified `%H` and `%n` and left the other two to a reader +# of the parser's unit tests — which do cover them, but never against a +# running machine where `user` is resolved and the unit has a real full name. +[unit] +description = "Expands the remaining specifiers" + +[service] +type = "oneshot" +exec = "/bin/echo oxinit-m13: %N runs as %u" +user = "nobody"