diff --git a/tests/Aetheria.Shared.Tests/FireAuthorityTests.cs b/tests/Aetheria.Shared.Tests/FireAuthorityTests.cs index b313f8541..ad9aaa2d1 100644 --- a/tests/Aetheria.Shared.Tests/FireAuthorityTests.cs +++ b/tests/Aetheria.Shared.Tests/FireAuthorityTests.cs @@ -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); diff --git a/tests/mutation_tests_fire_control_cut3.sh b/tests/mutation_tests_fire_control_cut3.sh index 679956741..edd97eb24 100644 --- a/tests/mutation_tests_fire_control_cut3.sh +++ b/tests/mutation_tests_fire_control_cut3.sh @@ -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