From 28a4a548b615bc717021082e54c32c1dfb6a6b2a Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Thu, 15 Jan 2026 18:53:30 +0100 Subject: [PATCH] Add integration test for drains (#41190) * drain test * fix linter fail --- .../Tests/Chemistry/DrainTest.cs | 124 ++++++++++++++++++ .../Interaction/InteractionTest.Helpers.cs | 44 ++++++- .../Tests/Interaction/InteractionTest.cs | 3 + .../Tests/Strip/StrippableTest.cs | 37 ++---- 4 files changed, 175 insertions(+), 33 deletions(-) create mode 100644 Content.IntegrationTests/Tests/Chemistry/DrainTest.cs diff --git a/Content.IntegrationTests/Tests/Chemistry/DrainTest.cs b/Content.IntegrationTests/Tests/Chemistry/DrainTest.cs new file mode 100644 index 0000000000..55f405f039 --- /dev/null +++ b/Content.IntegrationTests/Tests/Chemistry/DrainTest.cs @@ -0,0 +1,124 @@ +using Content.IntegrationTests.Tests.Interaction; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.FixedPoint; +using Content.Shared.Fluids.Components; +using Content.Shared.Nutrition.Components; +using Robust.Shared.Prototypes; + +namespace Content.IntegrationTests.Tests.Chemistry; + +public sealed class DrainTest : InteractionTest +{ + private static readonly EntProtoId PizzaPrototype = "FoodPizzaMargherita"; + private static readonly EntProtoId DrainPrototype = "FloorDrain"; + private static readonly EntProtoId BucketPrototype = "Bucket"; + private static readonly ProtoId BloodReagent = "Blood"; + private static readonly ProtoId WaterReagent = "Water"; + private static readonly FixedPoint2 WaterVolume = 50; // 50u + private static readonly FixedPoint2 PuddleVolume = 30; // 30u + + [TestPrototypes] + private static readonly string Prototypes = @$" +- type: entity + parent: Puddle + id: PuddleBloodTest + suffix: Blood (30u) + components: + - type: SolutionContainerManager + solutions: + puddle: + maxVol: 1000 + reagents: + - ReagentId: {BloodReagent} + Quantity: {PuddleVolume} +"; + + + /// + /// Tests that drag drop interactions with drains are working as intended. + /// + [Test] + public async Task DragDropOntoDrainTest() + { + var solutionContainerSys = SEntMan.System(); + + // Spawn a drain one tile away. + var drain = await Spawn(DrainPrototype); + + // Spawn a bucket at the player's coordinates. + var bucket = await Spawn(BucketPrototype, PlayerCoords); + + // Add water to the bucket. + Assert.That(solutionContainerSys.TryGetDrainableSolution(ToServer(bucket), out var solutionEnt, out var solution), "Bucket had no drainable solution."); + await Server.WaitAssertion(() => + { + Assert.That(solutionContainerSys.TryAddReagent(solutionEnt.Value, WaterReagent, WaterVolume), "Could not add water to the bucket."); + }); + + // Check that the bucket was filled. + Assert.That(solutionContainerSys.TryGetDrainableSolution(ToServer(bucket), out solutionEnt, out solution), "Bucket had no drainable solution after filling it."); + Assert.That(solution.Volume, Is.EqualTo(WaterVolume)); + + // Drag drop the bucket onto the drain. + await DragDrop(bucket, drain); + + // Check that the bucket is empty. + Assert.That(solutionContainerSys.TryGetDrainableSolution(ToServer(bucket), out solutionEnt, out solution), "Bucket had no drainable solution after draining it."); + Assert.That(solution.Volume, Is.EqualTo(FixedPoint2.Zero), "Bucket was not empty after draining it."); + + await Delete(bucket); + + // Spawn a pizza at the player's coordinates. + var pizza = await Spawn(PizzaPrototype, PlayerCoords); + + // Check that the pizza is not empty. + var edibleSolutionId = Comp(pizza).Solution; + Assert.That(solutionContainerSys.TryGetSolution(ToServer(pizza), edibleSolutionId, out solutionEnt, out solution), "Pizza had no edible solution."); + var pizzaVolume = solution.Volume; + Assert.That(pizzaVolume, Is.GreaterThan(FixedPoint2.Zero), "Pizza had no reagents inside its edible solution."); + + // Drag drop the pizza onto the drain. + // Yes, this was a bug that existed before. + await DragDrop(pizza, drain); + + // Check that the pizza did not get deleted or had its reagents drained. + AssertExists(pizza); + Assert.That(solutionContainerSys.TryGetSolution(ToServer(pizza), edibleSolutionId, out solutionEnt, out solution), "Pizza had no edible solution."); + Assert.That(solution.Volume, Is.EqualTo(pizzaVolume), "Pizza lost reagents when drag dropped onto a drain."); + } + + /// + /// Tests that drains make puddles next to them disappear. + /// + [Test] + public async Task DrainPuddleTest() + { + var solutionContainerSys = SEntMan.System(); + + // Spawn a puddle at the player coordinates; + var puddle = await Spawn("PuddleBloodTest", PlayerCoords); + + // Make sure the reagent chosen for this test does not evaporate on its own. + // If you are a fork that made more reagents evaporate, just change BloodReagent ProtoId above to something else. + Assert.That(HasComp(puddle), Is.False, "The chosen reagent is evaporating on its own and we cannot use it for the drain test."); + + var puddleSolutionId = Comp(puddle).SolutionName; + Assert.That(solutionContainerSys.TryGetSolution(ToServer(puddle), puddleSolutionId, out _, out var solution), "Puddle had no solution."); + Assert.That(solution.Volume, Is.EqualTo(PuddleVolume), "Puddle had the wrong amount of reagents after spawning."); + + // Wait a few seconds and check that the puddle did not disappear on its own. + await RunSeconds(10); + Assert.That(solutionContainerSys.TryGetSolution(ToServer(puddle), puddleSolutionId, out _, out solution), "Puddle had no solution."); + Assert.That(solution.Volume, Is.EqualTo(PuddleVolume), "Puddle had the wrong amount of reagents after spawning."); + + // Spawn a drain one tile away. + await Spawn(DrainPrototype); + + // Wait a few seconds. + await RunSeconds(10); + + // Make sure the puddle was deleted by the drain. + AssertDeleted(puddle); + } +} diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs index 6279449ba7..0f85397901 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs @@ -91,16 +91,18 @@ public abstract partial class InteractionTest } /// - /// Spawn an entity at the target coordinates and set it as the target. + /// Spawn an entity at the given coordinates and set it as the target. + /// If no coordinates are given it will default to /// [MemberNotNull(nameof(Target), nameof(STarget), nameof(CTarget))] #pragma warning disable CS8774 // Member must have a non-null value when exiting. - protected async Task SpawnTarget(string prototype) + protected async Task SpawnTarget(string prototype, NetCoordinates? coords = null) { + coords ??= TargetCoords; Target = NetEntity.Invalid; await Server.WaitPost(() => { - Target = SEntMan.GetNetEntity(SEntMan.SpawnAtPosition(prototype, SEntMan.GetCoordinates(TargetCoords))); + Target = SEntMan.GetNetEntity(SEntMan.SpawnAtPosition(prototype, SEntMan.GetCoordinates(coords.Value))); }); await RunTicks(5); @@ -110,14 +112,16 @@ public abstract partial class InteractionTest #pragma warning restore CS8774 // Member must have a non-null value when exiting. /// - /// Spawn an entity entity at the target coordinates without setting it as the target. + /// Spawn an entity entity at the given coordinates without setting it as the target. + /// If no coordinates are given it will default to /// - protected async Task Spawn(string prototype) + protected async Task Spawn(string prototype, NetCoordinates? coords = null) { + coords ??= TargetCoords; var entity = NetEntity.Invalid; await Server.WaitPost(() => { - entity = SEntMan.GetNetEntity(SEntMan.SpawnAtPosition(prototype, SEntMan.GetCoordinates(TargetCoords))); + entity = SEntMan.GetNetEntity(SEntMan.SpawnAtPosition(prototype, SEntMan.GetCoordinates(coords.Value))); }); await RunTicks(5); @@ -407,6 +411,33 @@ public abstract partial class InteractionTest } } + /// + /// Simulates a drag and drop mouse interaction from one entity to another. + /// + protected async Task DragDrop(NetEntity source, NetEntity target) + { + // ScreenCoordinates diff needs to be larger than DragDropSystem.Deadzone for the drag drop to initiate + var screenX = CDragDropSys.Deadzone + 1f; + + // Start drag + await SetKey(EngineKeyFunctions.Use, + BoundKeyState.Down, + NetPosition(source), + source, + screenCoordinates: new ScreenCoordinates(screenX, 0f, WindowId.Main)); + + await RunTicks(3); + + // End drag + await SetKey(EngineKeyFunctions.Use, + BoundKeyState.Up, + NetPosition(target), + target, + screenCoordinates: new ScreenCoordinates(0f, 0f, WindowId.Main)); + + await RunTicks(3); + } + /// /// Throw the currently held entity. Defaults to targeting the current /// @@ -1607,6 +1638,7 @@ public abstract partial class InteractionTest protected EntityCoordinates Position(NetEntity uid) => Position(ToServer(uid)); protected EntityCoordinates Position(EntityUid uid) => Xform(uid).Coordinates; + protected NetCoordinates NetPosition(NetEntity uid) => FromServer(Position(uid)); #endregion } diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs index 245aeab9ee..62a03b0abe 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs @@ -3,6 +3,7 @@ using System.Numerics; using Content.Client.Construction; using Content.Client.Examine; using Content.Client.Gameplay; +using Content.Client.Interaction; using Content.IntegrationTests.Pair; using Content.Server.Hands.Systems; using Content.Server.Stack; @@ -134,6 +135,7 @@ public abstract partial class InteractionTest protected InteractionTestSystem CTestSystem = default!; protected ISawmill CLogger = default!; protected SharedUserInterfaceSystem CUiSys = default!; + protected DragDropSystem CDragDropSys = default!; // player components protected HandsComponent? Hands; @@ -208,6 +210,7 @@ public abstract partial class InteractionTest CConSys = CEntMan.System(); ExamineSys = CEntMan.System(); CUiSys = CEntMan.System(); + CDragDropSys = CEntMan.System(); // Setup map. if (TestMapPath == null) diff --git a/Content.IntegrationTests/Tests/Strip/StrippableTest.cs b/Content.IntegrationTests/Tests/Strip/StrippableTest.cs index f65bab1f81..d2ae3bd7ec 100644 --- a/Content.IntegrationTests/Tests/Strip/StrippableTest.cs +++ b/Content.IntegrationTests/Tests/Strip/StrippableTest.cs @@ -1,8 +1,6 @@ -using Content.Client.Interaction; -using Content.IntegrationTests.Tests.Interaction; +using Content.IntegrationTests.Tests.Interaction; +using Content.Shared.Strip.Components; using Robust.Shared.GameObjects; -using Robust.Shared.Input; -using Robust.Shared.Map; namespace Content.IntegrationTests.Tests.Strip; @@ -10,37 +8,22 @@ public sealed class StrippableTest : InteractionTest { protected override string PlayerPrototype => "MobHuman"; + /// + /// Tests that the stripping UI is opened when drag dropping from another mob onto the player. + /// [Test] public async Task DragDropOpensStrip() { - // Spawn one tile away - TargetCoords = SEntMan.GetNetCoordinates(new EntityCoordinates(MapData.MapUid, 1, 0)); await SpawnTarget("MobHuman"); var userInterface = Comp(Target); - Assert.That(userInterface.Actors.Count == 0); + Assert.That(userInterface.Actors, Is.Empty); - // screenCoordinates diff needs to be larger than DragDropSystem._deadzone - var screenX = CEntMan.System().Deadzone + 1f; + await DragDrop(Target.Value, Player); - // Start drag - await SetKey(EngineKeyFunctions.Use, - BoundKeyState.Down, - TargetCoords, - Target, - screenCoordinates: new ScreenCoordinates(screenX, 0f, WindowId.Main)); + Assert.That(userInterface.Actors, Is.Not.Empty); - await RunTicks(5); - - // End drag - await SetKey(EngineKeyFunctions.Use, - BoundKeyState.Up, - PlayerCoords, - Player, - screenCoordinates: new ScreenCoordinates(0f, 0f, WindowId.Main)); - - await RunTicks(5); - - Assert.That(userInterface.Actors.Count > 0); + Assert.That(CUiSys.IsUiOpen(CTarget.Value, StrippingUiKey.Key)); + Assert.That(SUiSys.IsUiOpen(STarget.Value, StrippingUiKey.Key)); } } -- 2.52.0