Add basic test of station initial power supply (#29698)

* Add basic test of station initial power supply

* Add info about stored power vs needed amount

* Update map list

* Get it compiling again

* Get it working again

* Only run if explicitly requested

* Fix merge

* We call 'em batteries

* Mark the test as dirty
This commit is contained in:
Tayrtahn
2025-06-01 11:51:37 -04:00
committed by GitHub
parent 5a30cddf42
commit 6fc2aa4a91

View File

@@ -0,0 +1,105 @@
using System.Collections.Generic;
using System.Linq;
using Content.Server.GameTicking;
using Content.Server.Maps;
using Content.Server.Power.Components;
using Content.Server.Power.NodeGroups;
using Content.Server.Power.Pow3r;
using Content.Shared.NodeContainer;
using Robust.Shared.EntitySerialization;
namespace Content.IntegrationTests.Tests.Power;
[Explicit]
public sealed class StationPowerTests
{
/// <summary>
/// How long the station should be able to survive on stored power if nothing is changed from round start.
/// </summary>
private const float MinimumPowerDurationSeconds = 10 * 60;
private static readonly string[] GameMaps =
[
"Fland",
"Meta",
"Packed",
"Omega",
"Bagel",
"Box",
"Core",
"Marathon",
"Saltern",
"Reach",
"Train",
"Oasis",
"Gate",
"Amber",
"Loop",
"Plasma",
"Elkridge",
"Convex",
"Relic",
];
[Test, TestCaseSource(nameof(GameMaps))]
public async Task TestStationStartingPowerWindow(string mapProtoId)
{
await using var pair = await PoolManager.GetServerClient(new PoolSettings
{
Dirty = true,
});
var server = pair.Server;
var entMan = server.EntMan;
var protoMan = server.ProtoMan;
var ticker = entMan.System<GameTicker>();
// Load the map
await server.WaitAssertion(() =>
{
Assert.That(protoMan.TryIndex<GameMapPrototype>(mapProtoId, out var mapProto));
var opts = DeserializationOptions.Default with { InitializeMaps = true };
ticker.LoadGameMap(mapProto, out var mapId, opts);
});
// Let powernet set up
await server.WaitRunTicks(1);
// Find the power network with the greatest stored charge in its batteries.
// This keeps backup SMESes out of the calculation.
var networks = new Dictionary<PowerState.Network, float>();
var batteryQuery = entMan.EntityQueryEnumerator<PowerNetworkBatteryComponent, BatteryComponent, NodeContainerComponent>();
while (batteryQuery.MoveNext(out var uid, out _, out var battery, out var nodeContainer))
{
if (!nodeContainer.Nodes.TryGetValue("output", out var node))
continue;
if (node.NodeGroup is not IBasePowerNet group)
continue;
networks.TryGetValue(group.NetworkNode, out var charge);
networks[group.NetworkNode] = charge + battery.CurrentCharge;
}
var totalStartingCharge = networks.MaxBy(n => n.Value).Value;
// Find how much charge all the APC-connected devices would like to use per second.
var totalAPCLoad = 0f;
var receiverQuery = entMan.EntityQueryEnumerator<ApcPowerReceiverComponent>();
while (receiverQuery.MoveNext(out _, out var receiver))
{
totalAPCLoad += receiver.Load;
}
var estimatedDuration = totalStartingCharge / totalAPCLoad;
var requiredStoredPower = totalAPCLoad * MinimumPowerDurationSeconds;
Assert.Multiple(() =>
{
Assert.That(estimatedDuration, Is.GreaterThanOrEqualTo(MinimumPowerDurationSeconds),
$"Initial power for {mapProtoId} does not last long enough! Needs at least {MinimumPowerDurationSeconds}s " +
$"but estimated to last only {estimatedDuration}s!");
Assert.That(totalStartingCharge, Is.GreaterThanOrEqualTo(requiredStoredPower),
$"Needs at least {requiredStoredPower - totalStartingCharge} more stored power!");
});
await pair.CleanReturnAsync();
}
}