using System.Numerics; using Content.Server.Construction.Components; using Content.Shared.Construction.Prototypes; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Construction { [TestFixture] public sealed class ConstructionPrototypeTest { // discount linter for construction graphs // TODO: Create serialization validators for these? // Top test definitely can be but writing a serializer takes ages. /// /// Checks every entity prototype with a construction component has a valid start node. /// [Test] public async Task TestStartNodeValid() { await using var pair = await PoolManager.GetServerClient(); var server = pair.Server; var entMan = server.ResolveDependency(); var protoMan = server.ResolveDependency(); var map = await pair.CreateTestMap(); await server.WaitAssertion(() => { foreach (var proto in protoMan.EnumeratePrototypes()) { if (!proto.Components.ContainsKey("Construction")) continue; var ent = entMan.SpawnEntity(proto.ID, new MapCoordinates(Vector2.Zero, map.MapId)); var construction = entMan.GetComponent(ent); var graph = protoMan.Index(construction.Graph); entMan.DeleteEntity(ent); Assert.That(graph.Nodes.ContainsKey(construction.Node), $"Found no startNode \"{construction.Node}\" on graph \"{graph.ID}\" for entity \"{proto.ID}\"!"); } }); await pair.CleanReturnAsync(); } [Test] public async Task TestStartIsValid() { await using var pair = await PoolManager.GetServerClient(); var server = pair.Server; var protoMan = server.ResolveDependency(); await server.WaitAssertion(() => { foreach (var proto in protoMan.EnumeratePrototypes()) { var start = proto.StartNode; var graph = protoMan.Index(proto.Graph); Assert.That(graph.Nodes.ContainsKey(start), $"Found no startNode \"{start}\" on graph \"{graph.ID}\" for construction prototype \"{proto.ID}\"!"); } }); await pair.CleanReturnAsync(); } [Test] public async Task TestTargetIsValid() { await using var pair = await PoolManager.GetServerClient(); var server = pair.Server; var protoMan = server.ResolveDependency(); await server.WaitAssertion(() => { foreach (var proto in protoMan.EnumeratePrototypes()) { var target = proto.TargetNode; var graph = protoMan.Index(proto.Graph); Assert.That(graph.Nodes.ContainsKey(target), $"Found no targetNode \"{target}\" on graph \"{graph.ID}\" for construction prototype \"{proto.ID}\"!"); } }); await pair.CleanReturnAsync(); } [Test] public async Task DeconstructionIsValid() { await using var pair = await PoolManager.GetServerClient(); var server = pair.Server; var protoMan = server.ResolveDependency(); var compFact = server.ResolveDependency(); var name = compFact.GetComponentName(typeof(ConstructionComponent)); Assert.Multiple(() => { foreach (var proto in protoMan.EnumeratePrototypes()) { if (proto.Abstract || pair.IsTestPrototype(proto) || !proto.Components.TryGetValue(name, out var reg)) continue; var comp = (ConstructionComponent) reg.Component; var target = comp.DeconstructionNode; if (target == null) continue; var graph = protoMan.Index(comp.Graph); Assert.That(graph.Nodes.ContainsKey(target), $"Invalid deconstruction node \"{target}\" on graph \"{graph.ID}\" for construction entity \"{proto.ID}\"!"); } }); await pair.CleanReturnAsync(); } [Test] public async Task TestStartReachesValidTarget() { await using var pair = await PoolManager.GetServerClient(); var server = pair.Server; var protoMan = server.ResolveDependency(); var entMan = server.ResolveDependency(); await server.WaitAssertion(() => { foreach (var proto in protoMan.EnumeratePrototypes()) { var start = proto.StartNode; var target = proto.TargetNode; var graph = protoMan.Index(proto.Graph); #pragma warning disable NUnit2045 // Interdependent assertions. Assert.That(graph.TryPath(start, target, out var path), $"Unable to find path from \"{start}\" to \"{target}\" on graph \"{graph.ID}\""); Assert.That(path, Has.Length.GreaterThanOrEqualTo(1), $"Unable to find path from \"{start}\" to \"{target}\" on graph \"{graph.ID}\"."); var next = path[0]; var nextId = next.Entity.GetId(null, null, new(entMan)); Assert.That(nextId, Is.Not.Null, $"The next node ({next.Name}) in the path from the start node ({start}) to the target node ({target}) must specify an entity! Graph: {graph.ID}"); Assert.That(protoMan.TryIndex(nextId, out EntityPrototype entity), $"The next node ({next.Name}) in the path from the start node ({start}) to the target node ({target}) specified an invalid entity prototype ({nextId} [{next.Entity}])"); Assert.That(entity.Components.ContainsKey("Construction"), $"The next node ({next.Name}) in the path from the start node ({start}) to the target node ({target}) specified an entity prototype ({next.Entity}) without a ConstructionComponent."); #pragma warning restore NUnit2045 } }); await pair.CleanReturnAsync(); } } }