Add integration test for drains (#41190)

* drain test

* fix linter fail
This commit is contained in:
slarticodefast 2026-01-15 18:53:30 +01:00 committed by BarryNorfolk
parent bf45a27ec7
commit 1bb91b2efa
4 changed files with 175 additions and 33 deletions

View File

@ -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);
}
}

View 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
}

View 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)

View 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));
}
}