]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Add integration test for drains (#41190)
authorslarticodefast <161409025+slarticodefast@users.noreply.github.com>
Thu, 15 Jan 2026 17:53:30 +0000 (18:53 +0100)
committerGitHub <noreply@github.com>
Thu, 15 Jan 2026 17:53:30 +0000 (17:53 +0000)
* drain test

* fix linter fail

Content.IntegrationTests/Tests/Chemistry/DrainTest.cs [new file with mode: 0644]
Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs
Content.IntegrationTests/Tests/Interaction/InteractionTest.cs
Content.IntegrationTests/Tests/Strip/StrippableTest.cs

diff --git a/Content.IntegrationTests/Tests/Chemistry/DrainTest.cs b/Content.IntegrationTests/Tests/Chemistry/DrainTest.cs
new file mode 100644 (file)
index 0000000..55f405f
--- /dev/null
@@ -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<ReagentPrototype> BloodReagent = "Blood";
+    private static readonly ProtoId<ReagentPrototype> 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}
+";
+
+
+    /// <summary>
+    /// Tests that drag drop interactions with drains are working as intended.
+    /// </summary>
+    [Test]
+    public async Task DragDropOntoDrainTest()
+    {
+        var solutionContainerSys = SEntMan.System<SharedSolutionContainerSystem>();
+
+        // 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<EdibleComponent>(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.");
+    }
+
+    /// <summary>
+    /// Tests that drains make puddles next to them disappear.
+    /// </summary>
+    [Test]
+    public async Task DrainPuddleTest()
+    {
+        var solutionContainerSys = SEntMan.System<SharedSolutionContainerSystem>();
+
+        // 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<EvaporationComponent>(puddle), Is.False, "The chosen reagent is evaporating on its own and we cannot use it for the drain test.");
+
+        var puddleSolutionId = Comp<PuddleComponent>(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);
+    }
+}
index 6279449ba7906dda578bbd5958e45328e54987a1..0f853979017a75e82a888fef60e3dd9e9949f16d 100644 (file)
@@ -91,16 +91,18 @@ public abstract partial class InteractionTest
     }
 
     /// <summary>
-    /// 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 <see cref="TargetCoords"/>
     /// </summary>
     [MemberNotNull(nameof(Target), nameof(STarget), nameof(CTarget))]
 #pragma warning disable CS8774 // Member must have a non-null value when exiting.
-    protected async Task<NetEntity> SpawnTarget(string prototype)
+    protected async Task<NetEntity> 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.
 
     /// <summary>
-    /// 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 <see cref="TargetCoords"/>
     /// </summary>
-    protected async Task<NetEntity> Spawn(string prototype)
+    protected async Task<NetEntity> 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
         }
     }
 
+    /// <summary>
+    /// Simulates a drag and drop mouse interaction from one entity to another.
+    /// </summary>
+    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);
+    }
+
     /// <summary>
     /// Throw the currently held entity. Defaults to targeting the current <see cref="TargetCoords"/>
     /// </summary>
@@ -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
 }
index 245aeab9ee3db662d2ef1dd922afff9275ef9387..62a03b0abeee9c2d666285c704f82a727fe23844 100644 (file)
@@ -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<ConstructionSystem>();
         ExamineSys = CEntMan.System<ExamineSystem>();
         CUiSys = CEntMan.System<SharedUserInterfaceSystem>();
+        CDragDropSys = CEntMan.System<DragDropSystem>();
 
         // Setup map.
         if (TestMapPath == null)
index f65bab1f81077e1453377180f1179ceb9958f175..d2ae3bd7ec464b2edfb3d51bef88be7f1151bc2d 100644 (file)
@@ -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";
 
+    /// <summary>
+    /// Tests that the stripping UI is opened when drag dropping from another mob onto the player.
+    /// </summary>
     [Test]
     public async Task DragDropOpensStrip()
     {
-        // Spawn one tile away
-        TargetCoords = SEntMan.GetNetCoordinates(new EntityCoordinates(MapData.MapUid, 1, 0));
         await SpawnTarget("MobHuman");
 
         var userInterface = Comp<UserInterfaceComponent>(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<DragDropSystem>().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));
     }
 }