test(seed): force scenario 8's interleaving instead of racing for it #4
@@ -370,26 +370,43 @@ Every suite runs the actual script, not a reimplementation of its logic, and
|
||||
every fix scenario is paired with a control that reproduces the bug — a
|
||||
scenario that passes either way proves nothing.
|
||||
|
||||
The concurrency scenarios run the real scripts as real concurrent processes,
|
||||
but they never race for the interleaving. The interfering step is placed
|
||||
inside the window by stubbing, on `PATH`, a command the code under test calls
|
||||
at a known point: `prune-cache-selftest.sh` scenario 12 stubs `du`, so the
|
||||
pass's own measurement publishes a reader marker strictly between its check
|
||||
and its unlink; `seed-target-dir-selftest.sh` scenarios 8a and 8b stub `cp`,
|
||||
so the consumer's own clone is what rotates the snapshot underneath it, or
|
||||
what loses a subtree of its own source, strictly inside the identity window.
|
||||
Placement is then a fact rather than a scheduling outcome — and each stub
|
||||
asserts that it fired, because a scenario whose interference silently did not
|
||||
happen passes for the wrong reason.
|
||||
The concurrency scenarios take one of three shapes, and none of them races
|
||||
for the interleaving its assertion depends on.
|
||||
|
||||
**A genuine race whose asserted invariant holds under any interleaving.**
|
||||
`seed-target-dir-selftest.sh` scenario 7 starts two real seeds on one cache
|
||||
key and asserts only what must be true whichever of them wins the rename.
|
||||
|
||||
**A `PATH` stub on a command the code under test calls at a known point**,
|
||||
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
|
||||
measurement publishes a reader marker strictly between its check and its
|
||||
unlink; `seed-target-dir-selftest.sh` scenarios 8a and 8b stub `cp`, so the
|
||||
consumer's own clone is what rotates the snapshot underneath it, or what
|
||||
loses a subtree of its own source, strictly inside the identity window. 8a
|
||||
does start a second real process — the actual `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 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
|
||||
contract.** `publish-snapshot-selftest.sh` scenarios 6 to 8 hold a
|
||||
`.reading-*` marker instead of running a slow consumer: the marker is the
|
||||
whole agreement between reader and publisher, so holding one is being a
|
||||
reader, and racing a real one would make the suite's runtime the thing under
|
||||
test.
|
||||
|
||||
Gating the interfering step on *observed progress* of the step it interferes
|
||||
with was the earlier answer here, and it is not one: seeing that a walk has
|
||||
with was an earlier answer here, and it is not one: seeing that a walk has
|
||||
started says nothing about where it will be when the interference lands, so
|
||||
the assertion downstream held only some of the time (issue #3).
|
||||
the assertion downstream held only some of the time (issue #3). No scenario
|
||||
does it any more.
|
||||
|
||||
Where more than one guard could catch a fault, the scenario asserts *which*
|
||||
one did — otherwise deleting the guard under test leaves the suite green
|
||||
because a sibling fires in its place.
|
||||
Where more than one guard could catch a fault, a scenario should assert
|
||||
*which* one did — otherwise deleting the guard under test leaves the suite
|
||||
green because a sibling fires in its place. Scenarios 8a and 8b of the seed
|
||||
suite do; scenario 9 of the same suite does not yet, which is why a mutation
|
||||
survives it (issue #5).
|
||||
|
||||
The action YAML holds no logic beyond wiring; everything testable lives in
|
||||
`scripts/`. A composite action needs `shell: bash` on every `run:` step, and
|
||||
|
||||
Reference in New Issue
Block a user