docs(readme): catalogue scenario 11 and close its scratch self-check
The suite-coverage table and the concurrency-scenario prose enumerate every scenario by number and by the property it pins, so adding one without listing it leaves the catalogue claiming complete coverage it no longer has. PR #9 updated the same table for the same reason when it added 8b/8c/8d/10; this follows it. Also adds the closing no-scratch-left-behind check that 8a and ALIAS end with. Scenario 11 is last in the file, so nothing downstream would have caught a leaked staging path or reader marker.
This commit is contained in:
@@ -422,7 +422,7 @@ bash scripts/selftest.sh --fast # fixture-only suites, no compiler
|
|||||||
| suite | covers |
|
| suite | covers |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `hardlink-clone-selftest.sh` | that a build in a clone cannot mutate its source — with a control proving a raw `cp -al` does. Needs a real compiler. |
|
| `hardlink-clone-selftest.sh` | that a build in a clone cannot mutate its source — with a control proving a raw `cp -al` does. Needs a real compiler. |
|
||||||
| `seed-target-dir-selftest.sh` | seed-source preference, lock-file stripping, two jobs racing on one cache key, **and one scenario per check a hardlink clone is validated against**: a source rotated wholesale, a subtree silently lost from the walk, a copy that reports failure over a tree both other checks read as whole, and a source identity that resolved at neither end — plus a staging tree that could not be privately owned being discarded rather than published |
|
| `seed-target-dir-selftest.sh` | seed-source preference, lock-file stripping, two jobs racing on one cache key, **and one scenario per check a hardlink clone is validated against**: a source rotated wholesale, a subtree silently lost from the walk, a copy that reports failure over a tree both other checks read as whole, and a source identity that resolved at neither end — plus a staging tree that could not be privately owned being discarded rather than published, and the publisher's log showing it waited on the consumer's own reader-lock marker before reclaiming a rotated snapshot |
|
||||||
| `publish-snapshot-selftest.sh` | the atomic swap, that a live consumer survives a republish, and the publisher's side of the rotation race: deferred reclamation under a live reader, and its sweep once the reader is gone |
|
| `publish-snapshot-selftest.sh` | the atomic swap, that a live consumer survives a republish, and the publisher's side of the rotation race: deferred reclamation under a live reader, and its sweep once the reader is gone |
|
||||||
| `prune-cache-selftest.sh` | liveness, protection, locking, eviction order, self-clear, **and that a cache a job claims *inside* the check-to-unlink window survives it** — against a real scratch `origin` |
|
| `prune-cache-selftest.sh` | liveness, protection, locking, eviction order, self-clear, **and that a cache a job claims *inside* the check-to-unlink window survives it** — against a real scratch `origin` |
|
||||||
| `restore-mtimes-selftest.sh` | the merge hazard and the watermark that closes it, including the two-jobs-one-namespace case. Needs a real compiler. |
|
| `restore-mtimes-selftest.sh` | the merge hazard and the watermark that closes it, including the two-jobs-one-namespace case. Needs a real compiler. |
|
||||||
@@ -442,7 +442,7 @@ key and asserts only what must be true whichever of them wins the rename.
|
|||||||
which places the interference inside the window rather than hoping it lands
|
which places the interference inside the window rather than hoping it lands
|
||||||
there. `prune-cache-selftest.sh` scenario 12 stubs `du`, so the pass's own
|
there. `prune-cache-selftest.sh` scenario 12 stubs `du`, so the pass's own
|
||||||
measurement publishes a reader marker strictly between its check and its
|
measurement publishes a reader marker strictly between its check and its
|
||||||
unlink. `seed-target-dir-selftest.sh` uses the shape four times over, on two
|
unlink. `seed-target-dir-selftest.sh` uses the shape five times over, on two
|
||||||
different commands. Scenarios 8a, 8b and 8c stub `cp`, so the consumer's own
|
different commands. Scenarios 8a, 8b and 8c stub `cp`, so the consumer's own
|
||||||
clone is what rotates the snapshot underneath it, loses a subtree of its own
|
clone is what rotates the snapshot underneath it, loses a subtree of its own
|
||||||
source, or reports a failure over a tree that is in fact whole — each strictly
|
source, or reports a failure over a tree that is in fact whole — each strictly
|
||||||
@@ -453,9 +453,14 @@ could not be READ rather than on one that changed, and the only way to make
|
|||||||
that the sole witness is to fail the identity reads while the copy between
|
that the sole witness is to fail the identity reads while the copy between
|
||||||
them succeeds. 8a also starts a second real process — the actual
|
them succeeds. 8a also starts a second real process — the actual
|
||||||
`publish-snapshot.sh` — but the stub is what fixes where its swap lands; the
|
`publish-snapshot.sh` — but the stub is what fixes where its swap lands; the
|
||||||
concurrency is incidental to the determinism. Every stub asserts that it
|
concurrency is incidental to the determinism. Scenario 11 reuses 8a's exact
|
||||||
fired, because a scenario whose interference silently did not happen passes
|
stub and the same forced rotation, but reads a different witness: not the
|
||||||
for the wrong reason.
|
consumer's own checks, but a line in the *publisher's* log reporting that it
|
||||||
|
waited on the reader marker this consumer's clone wrote — `reader_lock_acquire`
|
||||||
|
is exercised by 8a already, but nothing asserts it actually fired until this
|
||||||
|
scenario reads that line back. Every stub asserts that it fired, because a
|
||||||
|
scenario whose interference silently did not happen passes for the wrong
|
||||||
|
reason.
|
||||||
|
|
||||||
**A synthetic stand-in for the other side, where that artefact *is* the
|
**A synthetic stand-in for the other side, where that artefact *is* the
|
||||||
contract.** `publish-snapshot-selftest.sh` scenarios 6 to 8 hold a
|
contract.** `publish-snapshot-selftest.sh` scenarios 6 to 8 hold a
|
||||||
@@ -487,6 +492,15 @@ how both `[ "$cp_rc" -eq 0 ]` and `[ "$i_before" != missing ]` sat unpinned
|
|||||||
(issue #5) while looking well covered. Isolating a check means constructing
|
(issue #5) while looking well covered. Isolating a check means constructing
|
||||||
the state only it can see, not the state that trips several at once.
|
the state only it can see, not the state that trips several at once.
|
||||||
|
|
||||||
|
Scenario 11 applies the same discipline to a witness outside the clone
|
||||||
|
entirely: not which of several checks inside `hardlink_clone_into` caught a
|
||||||
|
fault, but whether `reader_lock_acquire`'s marker was observed by anything
|
||||||
|
outside it at all. The consumer's own log and exit status are silent either
|
||||||
|
way — a run with the marker deleted still succeeds — so what is asserted is
|
||||||
|
one line in the *publisher's* log reporting that it waited. Deleting
|
||||||
|
`reader_lock_acquire` (issue #10) leaves that line unwritten without failing
|
||||||
|
anything else in the suite.
|
||||||
|
|
||||||
The action YAML holds no logic beyond wiring; everything testable lives in
|
The action YAML holds no logic beyond wiring; everything testable lives in
|
||||||
`scripts/`. A composite action needs `shell: bash` on every `run:` step, and
|
`scripts/`. A composite action needs `shell: bash` on every `run:` step, and
|
||||||
the actions reach their shared scripts through
|
the actions reach their shared scripts through
|
||||||
|
|||||||
@@ -720,6 +720,9 @@ ok "both sides of the race completed"
|
|||||||
grep -q "readers: waiting for 1 in-flight clone(s) of snapshot-${INTLK_BASE}" "$scratch/logPubIntlk" \
|
grep -q "readers: waiting for 1 in-flight clone(s) of snapshot-${INTLK_BASE}" "$scratch/logPubIntlk" \
|
||||||
|| { tail -40 "$scratch/logPubIntlk"; fail "the publisher never reported waiting on the consumer's reader marker — the interlock did not observably engage"; }
|
|| { tail -40 "$scratch/logPubIntlk"; fail "the publisher never reported waiting on the consumer's reader marker — the interlock did not observably engage"; }
|
||||||
ok "the publisher observably waited on the consumer's own reader marker before reclaiming the rotated snapshot generation"
|
ok "the publisher observably waited on the consumer's own reader marker before reclaiming the rotated snapshot generation"
|
||||||
|
leftovers=$(find "$root" -maxdepth 1 \( -name '.stage-*' -o -name '.reading-*' -o -name '.publish-*' \) -print)
|
||||||
|
[ -z "$leftovers" ] || fail "scratch left behind: ${leftovers}"
|
||||||
|
ok "no staging, reader-marker or deferred-generation scratch left behind"
|
||||||
rm -f "$scratch/bin/cp"
|
rm -f "$scratch/bin/cp"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
|
|||||||
Reference in New Issue
Block a user