Fix ShieldTakesHit coincidental pass and cut3 harness stale test name

ShieldTakesHit: damage raised from 10 to 50, past the Reactor/Shield gear's own Durability of 10. Under the dart-throw kernel, Build's default Precision groups unaimed shots tightly enough around this small hull's centre of mass that this fixture's deterministic single shot lands on one of the two equipped Tool items. At damage 10 that happened to exactly consume the item's own durability with zero remainder, so hull durability stayed unchanged with or without the shield-absorb branch -- the mutation this test is supposed to kill was surviving by coincidence, not because the rule held.

mutation_tests_fire_control_cut3.sh: its closing section still referenced FireAuthorityTests.OutOfArcConsumesNoDraw, a test Cut 7 renamed to CombatNeverDrawsFromTheSharedStream before this branch started (verified against the merge-base commit). Replaced the stale not-yet-reached note with a real mutation against the current test: Commit reads shot.Source.ItemManager.Random once, restoring the shared-stream touch Cut 6b removed.
This commit is contained in:
Meta Vens 2026-09-19 22:16:25 +02:00
commit aa850a3cbe
2 changed files with 36 additions and 25 deletions

View file

@ -522,10 +522,20 @@ public sealed class FireAuthorityTests : IDisposable
// The absorb rule now exists once, in FireControl: an active shield that CanTakeHit absorbs, and the
// schematic is untouched. Mutation: remove the shield branch (the hit always reaches the hull).
//
// Cut 6d (docs/fire-control-cut.md): damage raised from 10 to 50, well above the Reactor/Shield gear's own
// Durability of 10. Unaimed fire now lands via the dart-throw kernel instead of a uniform pick over the
// whole hull, and Build's own default Precision groups shots tightly enough around this fixture's small
// hull's centre of mass that the deterministic draw for this exact seed lands on one of the two equipped
// Tool items. At damage 10 that coincidentally consumed the item's own durability with zero remainder,
// which left the hull's durability unchanged with or without the shield mutation applied -- the shield
// branch could be deleted outright and this test would not have noticed. Damage well past what any single
// item on this hull could fully absorb makes the assertion mean what it says regardless of which cell the
// kernel happens to land on.
[Fact]
public void ShieldTakesHit()
{
var e = Build(TestSettings(), damage: 10, velocity: 0, accuracy: 1, resolution: 1, spread: 0, armor: 0,
var e = Build(TestSettings(), damage: 50, velocity: 0, accuracy: 1, resolution: 1, spread: 0, armor: 0,
beforeActivate: (items, shooter, target) =>
{
// The Reactor/Shield catalog entries are authored in Build's own upsert batch (see its
@ -543,7 +553,7 @@ public sealed class FireAuthorityTests : IDisposable
e.Target.Shield.Item.Enabled.Value = true;
for (var i = 0; i < 20; i++) e.Zone.Update(.1f); // charge the reserve
Assert.True(e.Target.Shield.CanTakeHit(DamageType.Kinetic, 10));
Assert.True(e.Target.Shield.CanTakeHit(DamageType.Kinetic, 50));
var beforeHull = e.Target.Hull.Durability;
FireControl.Fire(e.Weapon, e.WeaponItem, e.Shooter);

View file

@ -3,22 +3,20 @@
#
# Fourteen check_mutation calls covering the cut's own thirteen declared tests (one, ProbabilityFollowsInputs,
# carries three sub-mutations from its own declared list, so fifteen mutations run in total) against
# tests/Aetheria.Shared.Tests/FireAuthorityTests.cs -- plus one test the harness deliberately does NOT attempt
# a mutation for, documented below rather than silently skipped:
# tests/Aetheria.Shared.Tests/FireAuthorityTests.cs, plus one more against the test Cut 7 rewrote in its place:
#
# OutOfArcConsumesNoDraw -- NOT YET REACHED. The test asserts against ItemManager.Random (`e.Items.Random`),
# but Cut 6b (6.1) moved every shot's roll onto its own freshly-seeded, immediately-discarded
# OutOfArcConsumesNoDraw no longer exists. It asserted against ItemManager.Random (`e.Items.Random`), but
# Cut 6b (6.1) moved every shot's roll onto its own freshly-seeded, immediately-discarded
# `new Random((zone.CombatSeed * 2654435761u) ^ (uint) shot.ShotId | 1u)` local to Commit -- a deliberate,
# good change (reproducibility independent of unrelated draws elsewhere) that also means Commit never reads
# or writes ItemManager.Random at all, on any path, arc or no arc. Confirmed empirically: applying the
# declared mutation ("roll first and multiply by zero" -- `var hit = random.NextFloat() < p;` in place of
# `var hit = p > 0f && random.NextFloat() < p;`) leaves this test green, because the local generator that
# mutation actually touches is discarded before anything outside Commit could observe it, and nothing else
# in the Fire/Step path ever touches ItemManager.Random (Zone.NextShotId is a plain counter). This is not a
# fixable fixture defect -- there is no external observation point for "did Commit's own local generator
# draw" under the current per-shot-seeded design, short of instrumenting FireControl itself (an injectable
# call-counting random source, or reverting 6.1's shared-stream removal) -- a production-code seam change,
# not a test change, and out of this cut's scope. Reaching it takes that seam.
# good change (reproducibility independent of unrelated draws elsewhere) that also meant Commit stopped
# reading or writing ItemManager.Random on any path, arc or no arc, which made the old test's own declared
# mutation ("roll first and multiply by zero") pass for a reason that had nothing to do with arcs. Cut 7
# (fire-control-cut.md, 7.2's own header comment, and FireAuthorityTests.cs's comment on the replacement)
# rewrote it as CombatNeverDrawsFromTheSharedStream: no combat path draws from the shared stream, in arc or
# out. That rule IS externally observable (Commit has no reference to ItemManager to draw from at all under
# the current design, so the only way to make it "touch the shared stream" is to add a read that was not
# there before) -- this harness's own mutation below does exactly that, through the one handle Commit does
# hold (`shot.Source.ItemManager`, already read elsewhere in this same function for hull data).
#
# A no-op control (a byte-identical rewrite of one target file through this script's own read/write path)
# proves the mechanism itself is transparent before any real mutation is trusted. Modelled on
@ -343,14 +341,17 @@ check_mutation \
"FireAuthorityTests.ShieldTakesHit" \
"red"
echo ""
echo "=== OutOfArcConsumesNoDraw: NOT YET REACHED (see header comment) -- confirming the test itself is still green with no mutation applied ==="
if run_test "FireAuthorityTests.OutOfArcConsumesNoDraw"; then
echo "OK: FireAuthorityTests.OutOfArcConsumesNoDraw passes on the unmutated tree"
else
FAILURES+=("OutOfArcConsumesNoDraw: the unmutated test itself failed -- that is a real regression, not a not-yet-reached mutation")
echo "$LAST_OUTPUT" | tail -40
fi
# --- Cut 7's replacement for OutOfArcConsumesNoDraw: CombatNeverDrawsFromTheSharedStream. Mutation: Commit
# reads shot.Source.ItemManager.Random (the shared stream FireAuthorityTests.cs's own Build exposes as
# e.Items.Random) once per call, restoring exactly the kind of shared-stream touch Cut 6b (6.1) removed --
# unconditionally, so both the theory's in-arc and out-of-arc cases go red. ---
check_mutation \
"CombatNeverDrawsFromTheSharedStream: Commit must never read ItemManager's shared stream, on any path" \
"$FIRE_CONTROL_CS" \
'var random = new Random((zone.CombatSeed * 2654435761u) ^ (uint) shot.ShotId | 1u);' \
'var random = new Random((zone.CombatSeed * 2654435761u) ^ (uint) shot.ShotId | 1u); shot.Source.ItemManager.Random.NextFloat();' \
"FireAuthorityTests.CombatNeverDrawsFromTheSharedStream" \
"red"
echo ""
verify_tree_clean
@ -369,5 +370,5 @@ if [ "${#FAILURES[@]}" -gt 0 ] || [ "$TREE_CLEAN_OK" != "1" ]; then
exit 1
fi
echo "RESULT: All attempted mutations behaved as declared (OutOfArcConsumesNoDraw: not yet reached, see header). tree-clean: PASS"
echo "RESULT: All mutations behaved as declared. tree-clean: PASS"
exit 0