From 87d753e91062512c9059179bfd17be2a5b1a6b55 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Mon, 22 Aug 2022 12:45:25 +1200 Subject: [PATCH] Add construction graph test (#10760) --- .../Construction/ConstructionPrototypeTest.cs | 46 +++++++++++++++++++ .../Steps/PrototypeConstructionGraphStep.cs | 7 ++- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/Content.IntegrationTests/Tests/Construction/ConstructionPrototypeTest.cs b/Content.IntegrationTests/Tests/Construction/ConstructionPrototypeTest.cs index e9e3e64229..60606c05e3 100644 --- a/Content.IntegrationTests/Tests/Construction/ConstructionPrototypeTest.cs +++ b/Content.IntegrationTests/Tests/Construction/ConstructionPrototypeTest.cs @@ -1,6 +1,9 @@ using System.Threading.Tasks; using Content.Server.Construction.Components; +using Content.Server.Stack; using Content.Shared.Construction.Prototypes; +using Content.Shared.Construction.Steps; +using Content.Shared.Prototypes; using NUnit.Framework; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -109,5 +112,48 @@ namespace Content.IntegrationTests.Tests.Construction } await pairTracker.CleanReturnAsync(); } + + [Test] + public async Task TestStackPrototypeSteps() + { + // People often mistakenly use the prototype-step rather than a material-step, resulting in a whole stack + // being consumed. This checks that if something uses a prototype step, that the relevant prototype does not + // have a stack component. + // + // If, for whatever reason, that is ever required, then this test should probably just support an ignore + // list. Though the test should then also checks that it accepts both the full-stack and single-sheet + // prototypes. + + await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings { NoClient = true }); + var server = pairTracker.Pair.Server; + var protoMan = server.ResolveDependency(); + var stackName = server.ResolveDependency().GetComponentName(typeof(StackComponent)); + + Assert.Multiple(() => + { + // quadruple for loop nesting, what fun. + foreach (var protoGraph in protoMan.EnumeratePrototypes()) + { + foreach (var node in protoGraph.Nodes.Values) + { + foreach (var edge in node.Edges) + { + foreach (var step in edge.Steps) + { + if (step is not PrototypeConstructionGraphStep protoStep) + continue; + + var protoEnt = protoMan.Index(protoStep.Prototype); + + Assert.False(protoEnt.Components.ContainsKey(stackName), + $"Construction graph {protoGraph.ID} uses a prototype-step that consumes a stackable entity {protoEnt.ID}"); + } + } + } + } + }); + + await pairTracker.CleanReturnAsync(); + } } } diff --git a/Content.Shared/Construction/Steps/PrototypeConstructionGraphStep.cs b/Content.Shared/Construction/Steps/PrototypeConstructionGraphStep.cs index cdf00a3c5f..d6d75f4054 100644 --- a/Content.Shared/Construction/Steps/PrototypeConstructionGraphStep.cs +++ b/Content.Shared/Construction/Steps/PrototypeConstructionGraphStep.cs @@ -1,11 +1,14 @@ -using Content.Shared.Examine; +using Content.Shared.Examine; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Construction.Steps { [DataDefinition] public sealed class PrototypeConstructionGraphStep : ArbitraryInsertConstructionGraphStep { - [DataField("prototype")] public string Prototype { get; } = string.Empty; + [DataField("prototype", customTypeSerializer:typeof(PrototypeIdSerializer), required:true)] + public string Prototype { get; } = string.Empty; public override bool EntityValid(EntityUid uid, IEntityManager entityManager) {