diff --git a/tests/Aetheria.Shared.Tests/BrownoutTests.cs b/tests/Aetheria.Shared.Tests/BrownoutTests.cs index b7a13fdc2..432b42d51 100644 --- a/tests/Aetheria.Shared.Tests/BrownoutTests.cs +++ b/tests/Aetheria.Shared.Tests/BrownoutTests.cs @@ -111,8 +111,7 @@ public sealed class BrownoutTests : IDisposable Assert.True(ship.TryEquip(Mint(cache, items, reactorData))); // The Skiff's centre column: a thruster there has no torque, so the allocator can fire it alone. These tests pin power, // not flight, and a lone off-centre thruster could not fire without turning. - Assert.True(ship.TryEquip(Mint(cache, items, cache.GetByName("Consumer")), new int2(2, 0))); - Assert.True(ship.GetBehavior().Torque == 0f, "the lone thruster turns nothing"); + Assert.True(ship.TryEquip(Mint(cache, items, cache.GetByName("Consumer")), new int2(2, 1))); zone.Entities.Add(ship); // Ship.Turn stays 0, so Ship.Update commands no rotation: no incidental torque thrust from the @@ -132,6 +131,8 @@ public sealed class BrownoutTests : IDisposable Thrust = Curved(100, exponent), Visibility = Constant(0), Heat = Constant(0), EnergyUsage = Constant(100) }); var ship = BuildShip(cache, reactorCharge); + Assert.True(ship.GetBehavior().Torque == 0f, "the lone thruster turns nothing"); + ship.Equipment.Single(e => e.Data.Name == "Consumer").UpdatePerformance(); // live from its first performance update ship.MovementDirection = float2(0, -1); // reverse-thruster axis += -MovementDirection.y == 1 ship.Update(1f); return (-ship.Velocity.y, ship.GetBehavior()); @@ -339,9 +340,11 @@ public sealed class BrownoutTests : IDisposable var energyUsage = Curved(100, exponent: 1); using var cache = OpenCatalog(new ThrusterData { - Thrust = Constant(0), Visibility = Constant(0), Heat = Constant(0), EnergyUsage = energyUsage + Thrust = Constant(100), Visibility = Constant(0), Heat = Constant(0), EnergyUsage = energyUsage }); var ship = BuildShip(cache, reactorCharge: 25); // demand 100 (nominal), generation 25 -> ratio .25 + // The allocator fires a thruster that can push; it is live from its first performance update. + ship.Equipment.Single(e => e.Data.Name == "Consumer").UpdatePerformance(); ship.MovementDirection = float2(0, -1); ship.Update(1f); var thruster = ship.GetBehavior(); diff --git a/tests/Aetheria.Shared.Tests/ConditionRatioTests.cs b/tests/Aetheria.Shared.Tests/ConditionRatioTests.cs index 77b51156a..aed8a6a28 100644 --- a/tests/Aetheria.Shared.Tests/ConditionRatioTests.cs +++ b/tests/Aetheria.Shared.Tests/ConditionRatioTests.cs @@ -94,13 +94,13 @@ public sealed class ConditionRatioTests : IDisposable Assert.True(ship.TryEquip(Mint(cache, items, reactorData))); // The Skiff's centre column: a thruster there has no torque, so the allocator can fire it alone. These tests pin power, // not flight, and a lone off-centre thruster could not fire without turning. - Assert.True(ship.TryEquip(Mint(cache, items, cache.GetByName("Consumer")), new int2(2, 0))); - Assert.True(ship.GetBehavior().Torque == 0f, "the lone thruster turns nothing"); + Assert.True(ship.TryEquip(Mint(cache, items, cache.GetByName("Consumer")), new int2(2, 1))); zone.Entities.Add(ship); ship.Aim = float3(0, 0, 1); // Turn stays 0, as in BrownoutTests ship.Activate(); consumer = ship.Equipment.Single(e => e.Data.Name == "Consumer"); + Assert.True(ship.GetBehavior().Torque == 0f, "the lone thruster turns nothing"); // Settle at the plateau's center: two ticks so UpdatePerformance's deltaTemp term reads 0 before a test // drives its own change, matching HeatResponseTests' own settle pattern. diff --git a/tests/Aetheria.Shared.Tests/EvasionTermTests.cs b/tests/Aetheria.Shared.Tests/EvasionTermTests.cs index a0c823e6b..a399f6ce9 100644 --- a/tests/Aetheria.Shared.Tests/EvasionTermTests.cs +++ b/tests/Aetheria.Shared.Tests/EvasionTermTests.cs @@ -273,7 +273,11 @@ public sealed partial class RunStartTests // The Djinni's measured motion is further below its reach (+53% nose-on, +27% broadside at the last measurement): its // strafers are off-axis and Ship.Update cancels their torque (M24), which the envelope, summed from what each thruster // can push, does not model. Stated deviation; the bound still pins that the reach covers what the ship does. - var ceiling = name == "djinni" ? 1.6f : 1.15f; + // The Longinus's reach sits 19.5% (nose-on) and 24.9% (broadside) above what the allocator's flight measures + // (ruling allocator-divergence-accepted): the allocator holds the heading and pays for the drive torque out of + // the flank thrusters, so the envelope's box support (cut envelope-from-columns reads the allocator's columns) + // promises more than a controlled flight spends. + var ceiling = name == "djinni" ? 1.6f : 1.3f; Assert.InRange(reach.noseOn, measured.noseOn * .9f, measured.noseOn * ceiling); Assert.InRange(reach.broadside, measured.broadside * .9f, measured.broadside * ceiling); } diff --git a/tests/Aetheria.Shared.Tests/RestoredHullsTests.cs b/tests/Aetheria.Shared.Tests/RestoredHullsTests.cs index b5622b268..0dba1a567 100644 --- a/tests/Aetheria.Shared.Tests/RestoredHullsTests.cs +++ b/tests/Aetheria.Shared.Tests/RestoredHullsTests.cs @@ -268,6 +268,16 @@ public sealed class RestoredHullsTests } } + // For a fixture that pins flight, not brownout: the thrusters draw no power. A ship with every thruster firing asks + // for more than its reactor gives, and a brownout scales the thrusters' thrust unevenly (follow-up + // allocator-brownout-columns), so the turn or heading a test reads would be the brownout's. + internal static void FreeThrusterPower(CultCache cache) + { + foreach (var gear in cache.GetAll()) + foreach (var thruster in gear.Behaviors.OfType()) + thruster.EnergyUsage = new PerformanceStat { Min = 0, Max = 0 }; + } + // A restored hull with every thruster hardpoint (and its reactor) filled, activated and warmed up: the // rotation thrusters the helm tests need. The caller owns the cache. internal static Ship BuildThrustedShip(CultCache cache, string name) diff --git a/tests/Aetheria.Shared.Tests/SteeringTests.cs b/tests/Aetheria.Shared.Tests/SteeringTests.cs index 60861ecdb..d2abbbbaa 100644 --- a/tests/Aetheria.Shared.Tests/SteeringTests.cs +++ b/tests/Aetheria.Shared.Tests/SteeringTests.cs @@ -290,6 +290,7 @@ public sealed class SteeringTests public void ATurnIsThatFractionOfTheHullsTurn(float turn) { using var cache = RestoredHullsTests.OpenCatalog(); + RestoredHullsTests.FreeThrusterPower(cache); var ship = RestoredHullsTests.BuildThrustedShip(cache, Hull); var settings = RestoredHullsTests.Settings(); const float dt = 1f / 60f; diff --git a/tests/Aetheria.Shared.Tests/ThrottleLockTests.cs b/tests/Aetheria.Shared.Tests/ThrottleLockTests.cs index b51a9086a..f7d012f6a 100644 --- a/tests/Aetheria.Shared.Tests/ThrottleLockTests.cs +++ b/tests/Aetheria.Shared.Tests/ThrottleLockTests.cs @@ -89,6 +89,8 @@ public sealed class ThrottleLockTests : IDisposable zone.Entities.Add(ship); ship.Aim = float3(0, 0, 1); ship.Activate(); + // A thruster is live from its first performance update; the allocator reads that every tick. + foreach (var item in ship.Equipment) item.UpdatePerformance(); var thrusters = ship.GetBehaviors().ToArray(); return new Fixture diff --git a/tests/Aetheria.Shared.Tests/ThrustAllocationTests.cs b/tests/Aetheria.Shared.Tests/ThrustAllocationTests.cs index 0e96ed191..bba7d64c7 100644 --- a/tests/Aetheria.Shared.Tests/ThrustAllocationTests.cs +++ b/tests/Aetheria.Shared.Tests/ThrustAllocationTests.cs @@ -43,10 +43,11 @@ public sealed partial class RunStartTests { var (_, player) = AllocDuel(); var drives = AllocDrives(player); - Assert.True(drives.Length >= 2 && drives[^1].NominalThrust > drives[0].NominalThrust * 1.05f, "fixture: the drives are mismatched"); player.MovementDirection = float2(0, 1); player.Turn = 0; Assert.True(abs(AllocFly(player, 120)) < .1f); + // The drives start alike and heat apart as they fire: by now they are mismatched and both pushing. + Assert.True(drives.Length >= 2 && drives[^1].NominalThrust > drives[0].NominalThrust * 1.05f, "fixture: the drives are mismatched"); Assert.All(drives, d => Assert.True(d.Axis > .5f, "fixture: the drives are pushing")); } @@ -88,11 +89,12 @@ public sealed partial class RunStartTests player.Turn = 0; Assert.True(abs(AllocFly(player, 30)) < .1f); + // The drive is heated a little off its plateau: its live thrust moves, and a column cached before would not. var weakened = drives[^1]; var before = weakened.Thrust; - weakened.Item.EquippableItem.Durability *= .5f; + foreach (var cell in weakened.Item.InsetShape.Coordinates) player.Temperature[cell.x, cell.y] += 5f; weakened.Item.UpdatePerformance(); - Assert.True(weakened.Thrust < before * .95f, "fixture: halving durability weakens the drive"); + Assert.True(weakened.Thrust < before * .95f && weakened.Item.Active.Value, "fixture: the heated drive pushes measurably less"); Assert.True(abs(AllocFly(player, 60)) < .1f); } @@ -100,6 +102,7 @@ public sealed partial class RunStartTests public void ATinyImbalanceStillHolds() { using var cache = RestoredHullsTests.OpenCatalog(); + RestoredHullsTests.FreeThrusterPower(cache); var ship = RestoredHullsTests.BuildThrustedShip(cache, "Longinus"); var drives = AllocDrives(ship); Assert.True(drives.Length >= 2);