Skip to content

Commit

Permalink
Add NukeOps Test (space-wizards#27207)
Browse files Browse the repository at this point in the history
* Add NukeOps Test

* Update EvacShuttleTest to also check mapinit

* Update RuleMaxTimeRestartTest

* Fix cvar cleanup

* A

* Revert some changes

* comments

* Add MappingTests

* Finally fix the test

* A
  • Loading branch information
ElectroJr authored Apr 24, 2024
1 parent 49f1c8b commit 91aa16f
Show file tree
Hide file tree
Showing 16 changed files with 355 additions and 31 deletions.
19 changes: 17 additions & 2 deletions Content.IntegrationTests/Pair/TestPair.Timing.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#nullable enable
using Robust.Shared.Timing;

namespace Content.IntegrationTests.Pair;

Expand All @@ -19,6 +18,22 @@ public async Task RunTicksSync(int ticks)
}
}

/// <summary>
/// Convert a time interval to some number of ticks.
/// </summary>
public int SecondsToTicks(float seconds)
{
return (int) Math.Ceiling(seconds / Server.Timing.TickPeriod.TotalSeconds);
}

/// <summary>
/// Run the server & client in sync for some amount of time
/// </summary>
public async Task RunSeconds(float seconds)
{
await RunTicksSync(SecondsToTicks(seconds));
}

/// <summary>
/// Runs the server-client pair in sync, but also ensures they are both idle each tick.
/// </summary>
Expand Down Expand Up @@ -59,4 +74,4 @@ public async Task SyncTicks(int targetDelta = 1)
delta = cTick - sTick;
Assert.That(delta, Is.EqualTo(targetDelta));
}
}
}
1 change: 1 addition & 0 deletions Content.IntegrationTests/PoolManager.Cvars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ private static readonly (string cvar, string value)[] TestCvars =
(CCVars.GameLobbyEnabled.Name, "false"),
(CCVars.ConfigPresetDevelopment.Name, "false"),
(CCVars.AdminLogsEnabled.Name, "false"),
(CCVars.AutosaveEnabled.Name, "false"),
(CVars.NetBufferSize.Name, "0")
};

Expand Down
187 changes: 187 additions & 0 deletions Content.IntegrationTests/Tests/GameRules/NukeOpsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
#nullable enable
using System.Linq;
using Content.Server.Body.Components;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Presets;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.Mind;
using Content.Server.Pinpointer;
using Content.Server.Roles;
using Content.Server.Shuttles.Components;
using Content.Server.Station.Components;
using Content.Shared.CCVar;
using Content.Shared.Damage;
using Content.Shared.FixedPoint;
using Content.Shared.GameTicking;
using Content.Shared.Hands.Components;
using Content.Shared.Inventory;
using Content.Shared.NPC.Systems;
using Content.Shared.NukeOps;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Map.Components;

namespace Content.IntegrationTests.Tests.GameRules;

[TestFixture]
public sealed class NukeOpsTest
{
/// <summary>
/// Check that a nuke ops game mode can start without issue. I.e., that the nuke station and such all get loaded.
/// </summary>
[Test]
public async Task TryStopNukeOpsFromConstantlyFailing()
{
await using var pair = await PoolManager.GetServerClient(new PoolSettings
{
Dirty = true,
DummyTicker = false,
Connected = true,
InLobby = true
});

var server = pair.Server;
var client = pair.Client;
var entMan = server.EntMan;
var mapSys = server.System<MapSystem>();
var ticker = server.System<GameTicker>();
var mindSys = server.System<MindSystem>();
var roleSys = server.System<RoleSystem>();
var invSys = server.System<InventorySystem>();
var factionSys = server.System<NpcFactionSystem>();

Assert.That(server.CfgMan.GetCVar(CCVars.GridFill), Is.False);
server.CfgMan.SetCVar(CCVars.GridFill, true);

// Initially in the lobby
Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby));
Assert.That(client.AttachedEntity, Is.Null);
Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.NotReadyToPlay));

// There are no grids or maps
Assert.That(entMan.Count<MapComponent>(), Is.Zero);
Assert.That(entMan.Count<MapGridComponent>(), Is.Zero);
Assert.That(entMan.Count<StationMapComponent>(), Is.Zero);
Assert.That(entMan.Count<StationMemberComponent>(), Is.Zero);
Assert.That(entMan.Count<StationCentcommComponent>(), Is.Zero);

// And no nukie related components
Assert.That(entMan.Count<NukeopsRuleComponent>(), Is.Zero);
Assert.That(entMan.Count<NukeopsRoleComponent>(), Is.Zero);
Assert.That(entMan.Count<NukeOperativeComponent>(), Is.Zero);
Assert.That(entMan.Count<NukeOpsShuttleComponent>(), Is.Zero);
Assert.That(entMan.Count<NukeOperativeSpawnerComponent>(), Is.Zero);

// Ready up and start nukeops
await pair.WaitClientCommand("toggleready True");
Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.ReadyToPlay));
await pair.WaitCommand("forcepreset Nukeops");
await pair.RunTicksSync(10);

// Game should have started
Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.InRound));
Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.JoinedGame));
Assert.That(client.EntMan.EntityExists(client.AttachedEntity));
var player = pair.Player!.AttachedEntity!.Value;
Assert.That(entMan.EntityExists(player));

// Maps now exist
Assert.That(entMan.Count<MapComponent>(), Is.GreaterThan(0));
Assert.That(entMan.Count<MapGridComponent>(), Is.GreaterThan(0));
Assert.That(entMan.Count<StationDataComponent>(), Is.EqualTo(2)); // The main station & nukie station
Assert.That(entMan.Count<StationMemberComponent>(), Is.GreaterThan(3)); // Each station has at least 1 grid, plus some shuttles
Assert.That(entMan.Count<StationCentcommComponent>(), Is.EqualTo(1));

// And we now have nukie related components
Assert.That(entMan.Count<NukeopsRuleComponent>(), Is.EqualTo(1));
Assert.That(entMan.Count<NukeopsRoleComponent>(), Is.EqualTo(1));
Assert.That(entMan.Count<NukeOperativeComponent>(), Is.EqualTo(1));
Assert.That(entMan.Count<NukeOpsShuttleComponent>(), Is.EqualTo(1));

// The player entity should be the nukie commander
var mind = mindSys.GetMind(player)!.Value;
Assert.That(entMan.HasComponent<NukeOperativeComponent>(player));
Assert.That(roleSys.MindIsAntagonist(mind));
Assert.That(roleSys.MindHasRole<NukeopsRoleComponent>(mind));
Assert.That(factionSys.IsMember(player, "Syndicate"), Is.True);
Assert.That(factionSys.IsMember(player, "NanoTrasen"), Is.False);

var roles = roleSys.MindGetAllRoles(mind);
var cmdRoles = roles.Where(x => x.Prototype == "NukeopsCommander" && x.Component is NukeopsRoleComponent);
Assert.That(cmdRoles.Count(), Is.EqualTo(1));

// The game rule exists, and all the stations/shuttles/maps are properly initialized
var rule = entMan.AllComponents<NukeopsRuleComponent>().Single().Component;
Assert.That(entMan.EntityExists(rule.NukieOutpost));
Assert.That(entMan.EntityExists(rule.NukieShuttle));
Assert.That(entMan.EntityExists(rule.TargetStation));

Assert.That(entMan.HasComponent<MapGridComponent>(rule.NukieOutpost));
Assert.That(entMan.HasComponent<MapGridComponent>(rule.NukieShuttle));

Assert.That(entMan.HasComponent<StationMemberComponent>(rule.NukieOutpost));
Assert.That(entMan.HasComponent<StationDataComponent>(rule.TargetStation));

var nukieStation = entMan.GetComponent<StationMemberComponent>(rule.NukieOutpost!.Value);
Assert.That(entMan.EntityExists(nukieStation.Station));
Assert.That(nukieStation.Station, Is.Not.EqualTo(rule.TargetStation));

Assert.That(server.MapMan.MapExists(rule.NukiePlanet));
var nukieMap = mapSys.GetMap(rule.NukiePlanet!.Value);

var targetStation = entMan.GetComponent<StationDataComponent>(rule.TargetStation!.Value);
var targetGrid = targetStation.Grids.First();
var targetMap = entMan.GetComponent<TransformComponent>(targetGrid).MapUid!.Value;
Assert.That(targetMap, Is.Not.EqualTo(nukieMap));

Assert.That(entMan.GetComponent<TransformComponent>(player).MapUid, Is.EqualTo(nukieMap));
Assert.That(entMan.GetComponent<TransformComponent>(rule.NukieOutpost!.Value).MapUid, Is.EqualTo(nukieMap));
Assert.That(entMan.GetComponent<TransformComponent>(rule.NukieShuttle!.Value).MapUid, Is.EqualTo(nukieMap));

// The maps are all map-initialized, including the player
// Yes, this is necessary as this has repeatedly been broken somehow.
Assert.That(mapSys.IsInitialized(nukieMap));
Assert.That(mapSys.IsInitialized(targetMap));
Assert.That(mapSys.IsPaused(nukieMap), Is.False);
Assert.That(mapSys.IsPaused(targetMap), Is.False);

EntityLifeStage LifeStage(EntityUid? uid) => entMan.GetComponent<MetaDataComponent>(uid!.Value).EntityLifeStage;
Assert.That(LifeStage(player), Is.GreaterThan(EntityLifeStage.Initialized));
Assert.That(LifeStage(nukieMap), Is.GreaterThan(EntityLifeStage.Initialized));
Assert.That(LifeStage(targetMap), Is.GreaterThan(EntityLifeStage.Initialized));
Assert.That(LifeStage(rule.NukieOutpost), Is.GreaterThan(EntityLifeStage.Initialized));
Assert.That(LifeStage(rule.NukieShuttle), Is.GreaterThan(EntityLifeStage.Initialized));
Assert.That(LifeStage(rule.TargetStation), Is.GreaterThan(EntityLifeStage.Initialized));

// Make sure the player has hands. We've had fucking disarmed nukies before.
Assert.That(entMan.HasComponent<HandsComponent>(player));
Assert.That(entMan.GetComponent<HandsComponent>(player).Hands.Count, Is.GreaterThan(0));

// While we're at it, lets make sure they aren't naked. I don't know how many inventory slots all mobs will be
// likely to have in the future. But nukies should probably have at least 3 slots with something in them.
var enumerator = invSys.GetSlotEnumerator(player);
int total = 0;
while (enumerator.NextItem(out _))
{
total++;
}
Assert.That(total, Is.GreaterThan(3));

// Finally lets check the nukie commander passed basic training and figured out how to breathe.
var totalSeconds = 30;
var totalTicks = (int) Math.Ceiling(totalSeconds / server.Timing.TickPeriod.TotalSeconds);
int increment = 5;
var resp = entMan.GetComponent<RespiratorComponent>(player);
var damage = entMan.GetComponent<DamageableComponent>(player);
for (var tick = 0; tick < totalTicks; tick += increment)
{
await pair.RunTicksSync(increment);
Assert.That(resp.SuffocationCycles, Is.LessThanOrEqualTo(resp.SuffocationCycleThreshold));
Assert.That(damage.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
}

ticker.SetGamePreset((GamePresetPrototype?)null);
server.CfgMan.SetCVar(CCVars.GridFill, false);
await pair.CleanReturnAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,29 @@ public async Task RestartTest()
await using var pair = await PoolManager.GetServerClient(new PoolSettings { InLobby = true });
var server = pair.Server;

Assert.That(server.EntMan.Count<GameRuleComponent>(), Is.Zero);
Assert.That(server.EntMan.Count<ActiveGameRuleComponent>(), Is.Zero);

var entityManager = server.ResolveDependency<IEntityManager>();
var sGameTicker = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<GameTicker>();
var sGameTiming = server.ResolveDependency<IGameTiming>();

sGameTicker.StartGameRule("MaxTimeRestart", out var ruleEntity);
Assert.That(entityManager.TryGetComponent<MaxTimeRestartRuleComponent>(ruleEntity, out var maxTime));

Assert.That(server.EntMan.Count<GameRuleComponent>(), Is.EqualTo(1));
Assert.That(server.EntMan.Count<ActiveGameRuleComponent>(), Is.EqualTo(1));

await server.WaitAssertion(() =>
{
Assert.That(sGameTicker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby));
maxTime.RoundMaxTime = TimeSpan.FromSeconds(3);
sGameTicker.StartRound();
});

Assert.That(server.EntMan.Count<GameRuleComponent>(), Is.EqualTo(1));
Assert.That(server.EntMan.Count<ActiveGameRuleComponent>(), Is.EqualTo(1));

await server.WaitAssertion(() =>
{
Assert.That(sGameTicker.RunLevel, Is.EqualTo(GameRunLevel.InRound));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -767,14 +767,9 @@ protected async Task RunTicks(int ticks)
await Pair.RunTicksSync(ticks);
}

protected int SecondsToTicks(float seconds)
{
return (int) Math.Ceiling(seconds / TickPeriod);
}

protected async Task RunSeconds(float seconds)
{
await RunTicks(SecondsToTicks(seconds));
await Pair.RunSeconds(seconds);
}

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
using Content.Shared.DoAfter;
using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
using Content.Server.Item;
using Content.Shared.Mind;
using Content.Shared.Players;
using Robust.Client.Input;
Expand Down
102 changes: 102 additions & 0 deletions Content.IntegrationTests/Tests/Mapping/MappingTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;

namespace Content.IntegrationTests.Tests.Mapping;

[TestFixture]
public sealed class MappingTests
{
/// <summary>
/// Checks that the mapping command creates paused & uninitialized maps.
/// </summary>
[Test]
public async Task MappingTest()
{
await using var pair = await PoolManager.GetServerClient(new PoolSettings {Dirty = true, Connected = true, DummyTicker = false});

var server = pair.Server;
var entMan = server.EntMan;
var mapSys = server.System<MapSystem>();

await pair.RunTicksSync(5);
var mapId = 1;
while (mapSys.MapExists(new(mapId)))
{
mapId++;
}

await pair.WaitClientCommand($"mapping {mapId}");
var map = mapSys.GetMap(new MapId(mapId));

var mapXform = server.Transform(map);
Assert.That(mapXform.MapUid, Is.EqualTo(map));
Assert.That(mapXform.MapID, Is.EqualTo(new MapId(mapId)));

var xform = server.Transform(pair.Player!.AttachedEntity!.Value);

Assert.That(xform.MapUid, Is.EqualTo(map));
Assert.That(mapSys.IsInitialized(map), Is.False);
Assert.That(mapSys.IsPaused(map), Is.True);
Assert.That(server.MetaData(map).EntityLifeStage, Is.EqualTo(EntityLifeStage.Initialized));
Assert.That(server.MetaData(map).EntityPaused, Is.True);

// Spawn a new entity
EntityUid ent = default;
await server.WaitPost(() =>
{
ent = entMan.Spawn(null, new MapCoordinates(default, new(mapId)));
});
await pair.RunTicksSync(5);
Assert.That(server.MetaData(ent).EntityLifeStage, Is.EqualTo(EntityLifeStage.Initialized));
Assert.That(server.MetaData(ent).EntityPaused, Is.True);

// Save the map
var file = $"{nameof(MappingTest)}.yml";
await pair.WaitClientCommand($"savemap {mapId} {file}");

// Mapinitialize it
await pair.WaitClientCommand($"mapinit {mapId}");
Assert.That(mapSys.IsInitialized(map), Is.True);
Assert.That(mapSys.IsPaused(map), Is.False);
Assert.That(server.MetaData(map).EntityLifeStage, Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(server.MetaData(map).EntityPaused, Is.False);
Assert.That(server.MetaData(ent).EntityLifeStage, Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(server.MetaData(ent).EntityPaused, Is.False);

await server.WaitPost(() => entMan.DeleteEntity(map));

// Load the saved map
mapId++;
while (mapSys.MapExists(new(mapId)))
{
mapId++;
}

await pair.WaitClientCommand($"mapping {mapId} {file}");
map = mapSys.GetMap(new MapId(mapId));

// And it should all be paused and un-initialized
xform = server.Transform(pair.Player!.AttachedEntity!.Value);
Assert.That(xform.MapUid, Is.EqualTo(map));
Assert.That(mapSys.IsInitialized(map), Is.False);
Assert.That(mapSys.IsPaused(map), Is.True);
Assert.That(server.MetaData(map).EntityLifeStage, Is.EqualTo(EntityLifeStage.Initialized));
Assert.That(server.MetaData(map).EntityPaused, Is.True);

mapXform = server.Transform(map);
Assert.That(mapXform.MapUid, Is.EqualTo(map));
Assert.That(mapXform.MapID, Is.EqualTo(new MapId(mapId)));
Assert.That(mapXform.ChildCount, Is.EqualTo(2));

mapXform.ChildEnumerator.MoveNext(out ent);
if (ent == pair.Player.AttachedEntity)
mapXform.ChildEnumerator.MoveNext(out ent);

Assert.That(server.MetaData(ent).EntityLifeStage, Is.EqualTo(EntityLifeStage.Initialized));
Assert.That(server.MetaData(ent).EntityPaused, Is.True);

await server.WaitPost(() => entMan.DeleteEntity(map));
await pair.CleanReturnAsync();
}
}
Loading

0 comments on commit 91aa16f

Please sign in to comment.