From a1f103d1e73c56aa2c3f7f2f7e2ffcfe56b33da7 Mon Sep 17 00:00:00 2001
From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com>
Date: Mon, 17 Apr 2023 04:34:36 +1200
Subject: [PATCH] Add reagent dispenser BUI test (#15443)
---
.../Tests/Chemistry/DispenserTest.cs | 35 ++++
.../InteractionTest.EntitySpecifier.cs | 6 +-
.../Interaction/InteractionTest.Helpers.cs | 153 ++++++++++++++++--
.../Tests/Interaction/InteractionTest.cs | 7 +-
4 files changed, 181 insertions(+), 20 deletions(-)
create mode 100644 Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs
diff --git a/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs b/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs
new file mode 100644
index 0000000000..cbe3ee4dce
--- /dev/null
+++ b/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs
@@ -0,0 +1,35 @@
+using System.Threading.Tasks;
+using Content.IntegrationTests.Tests.Interaction;
+using Content.Shared.Chemistry;
+using Content.Shared.Containers.ItemSlots;
+using NUnit.Framework;
+
+namespace Content.IntegrationTests.Tests.Chemistry;
+
+public sealed class DispenserTest : InteractionTest
+{
+ ///
+ /// Basic test that checks that a beaker can be inserted and ejected from a dispenser.
+ ///
+ [Test]
+ public async Task InsertEjectBuiTest()
+ {
+ await SpawnTarget("chem_dispenser");
+ ToggleNeedPower();
+
+ // Insert beaker
+ await Interact("Beaker");
+ Assert.IsNull(Hands.ActiveHandEntity);
+
+ // Open BUI
+ await Interact("");
+
+ // Eject beaker via BUI.
+ var ev = new ItemSlotButtonPressedEvent(SharedChemMaster.InputSlotName);
+ await SendBui(ReagentDispenserUiKey.Key, ev);
+
+ // Beaker is back in the player's hands
+ Assert.IsNotNull(Hands.ActiveHandEntity);
+ AssertPrototype("Beaker", Hands.ActiveHandEntity);
+ }
+}
diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs
index 3949389919..2e8314ec97 100644
--- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs
+++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs
@@ -62,6 +62,10 @@ public abstract partial class InteractionTest
return;
Converted = true;
+
+ if (string.IsNullOrWhiteSpace(Prototype))
+ return;
+
if (protoMan.HasIndex(Prototype))
return;
@@ -121,6 +125,6 @@ public abstract partial class InteractionTest
var meta = SEntMan.GetComponent(uid);
Assert.NotNull(meta.EntityPrototype);
- return new (meta.EntityPrototype.ID, 1) { Converted = true };
+ return new (meta.EntityPrototype!.ID, 1) { Converted = true };
}
}
diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs
index d07af1dd52..634755062b 100644
--- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs
+++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs
@@ -1,14 +1,17 @@
#nullable enable
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
using Content.Client.Construction;
using Content.Server.Construction.Components;
+using Content.Server.Power.Components;
using Content.Server.Tools.Components;
using Content.Shared.Construction.Prototypes;
using Content.Shared.Item;
using NUnit.Framework;
+using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Log;
using Robust.Shared.Map;
@@ -139,7 +142,7 @@ public abstract partial class InteractionTest
await DeleteHeldEntity();
- if (entity == null)
+ if (entity == null || string.IsNullOrWhiteSpace(entity.Prototype))
{
await RunTicks(1);
Assert.That(Hands.ActiveHandEntity == null);
@@ -238,13 +241,19 @@ public abstract partial class InteractionTest
///
/// Place an entity prototype into the players hand and interact with the given entity (or target position)
///
- protected async Task Interact(string? id, int quantity = 1, bool shouldSucceed = true, bool awaitDoAfters = true)
- => await Interact(id == null ? null : (id, quantity), shouldSucceed, awaitDoAfters);
+ ///
+ /// Empty strings imply empty hands.
+ ///
+ protected async Task Interact(string id, int quantity = 1, bool shouldSucceed = true, bool awaitDoAfters = true)
+ => await Interact((id, quantity), shouldSucceed, awaitDoAfters);
///
/// Place an entity prototype into the players hand and interact with the given entity (or target position)
///
- protected async Task Interact(EntitySpecifier? entity, bool shouldSucceed = true, bool awaitDoAfters = true)
+ ///
+ /// Empty strings imply empty hands.
+ ///
+ protected async Task Interact(EntitySpecifier entity, bool shouldSucceed = true, bool awaitDoAfters = true)
{
// For every interaction, we will also examine the entity, just in case this breaks something, somehow.
// (e.g., servers attempt to assemble construction examine hints).
@@ -376,7 +385,10 @@ public abstract partial class InteractionTest
///
/// Variant of that performs several interactions using different entities.
///
- protected async Task Interact(params EntitySpecifier?[] specifiers)
+ ///
+ /// Empty strings imply empty hands.
+ ///
+ protected async Task Interact(params EntitySpecifier[] specifiers)
{
foreach (var spec in specifiers)
{
@@ -386,32 +398,60 @@ public abstract partial class InteractionTest
#region Asserts
- protected void AssertPrototype(string? prototype)
+ protected void AssertPrototype(string? prototype, EntityUid? target = null)
{
- var meta = Comp();
+ target ??= Target;
+ if (target == null)
+ {
+ Assert.Fail("No target specified");
+ return;
+ }
+
+ var meta = SEntMan.GetComponent(target.Value);
Assert.That(meta.EntityPrototype?.ID, Is.EqualTo(prototype));
}
- protected void AssertAnchored(bool anchored = true)
+ protected void AssertAnchored(bool anchored = true, EntityUid? target = null)
{
- var sXform = SEntMan.GetComponent(Target!.Value);
- var cXform = CEntMan.GetComponent(Target.Value);
+ target ??= Target;
+ if (target == null)
+ {
+ Assert.Fail("No target specified");
+ return;
+ }
+
+ var sXform = SEntMan.GetComponent(target.Value);
+ var cXform = CEntMan.GetComponent(target.Value);
Assert.That(sXform.Anchored, Is.EqualTo(anchored));
Assert.That(cXform.Anchored, Is.EqualTo(anchored));
}
- protected void AssertDeleted(bool deleted = true)
+ protected void AssertDeleted(bool deleted = true, EntityUid? target = null)
{
- Assert.That(SEntMan.Deleted(Target), Is.EqualTo(deleted));
- Assert.That(CEntMan.Deleted(Target), Is.EqualTo(deleted));
+ target ??= Target;
+ if (target == null)
+ {
+ Assert.Fail("No target specified");
+ return;
+ }
+
+ Assert.That(SEntMan.Deleted(target), Is.EqualTo(deleted));
+ Assert.That(CEntMan.Deleted(target), Is.EqualTo(deleted));
}
///
/// Assert whether or not the target has the given component.
///
- protected void AssertComp(bool hasComp = true)
+ protected void AssertComp(bool hasComp = true, EntityUid? target = null)
{
- Assert.That(SEntMan.HasComponent(Target), Is.EqualTo(hasComp));
+ target ??= Target;
+ if (target == null)
+ {
+ Assert.Fail("No target specified");
+ return;
+ }
+
+ Assert.That(SEntMan.HasComponent(target), Is.EqualTo(hasComp));
}
///
@@ -553,7 +593,6 @@ public abstract partial class InteractionTest
#endregion
-
///
/// List of currently active DoAfters on the player.
///
@@ -563,7 +602,14 @@ public abstract partial class InteractionTest
///
/// Convenience method to get components on the target. Returns SERVER-SIDE components.
///
- protected T Comp() => SEntMan.GetComponent(Target!.Value);
+ protected T Comp(EntityUid? target = null)
+ {
+ target ??= Target;
+ if (target == null)
+ Assert.Fail("No target specified");
+
+ return SEntMan.GetComponent(target!.Value);
+ }
///
/// Set the tile at the target position to some prototype.
@@ -611,4 +657,77 @@ public abstract partial class InteractionTest
protected async Task RunSeconds(float seconds)
=> await RunTicks((int) Math.Ceiling(seconds / TickPeriod));
+
+ #region BUI
+ ///
+ /// Sends a bui message using the given bui key.
+ ///
+ protected async Task SendBui(Enum key, BoundUserInterfaceMessage msg, EntityUid? target = null)
+ {
+ if (!TryGetBui(key, out var bui))
+ return;
+
+ await Client.WaitPost(() => bui.SendMessage(msg));
+
+ // allow for client -> server and server -> client messages to be sent.
+ await RunTicks(15);
+ }
+
+ ///
+ /// Sends a bui message using the given bui key.
+ ///
+ protected async Task CloseBui(Enum key, EntityUid? target = null)
+ {
+ if (!TryGetBui(key, out var bui))
+ return;
+
+ await Client.WaitPost(() => bui.Close());
+
+ // allow for client -> server and server -> client messages to be sent.
+ await RunTicks(15);
+ }
+
+ protected bool TryGetBui(Enum key, [NotNullWhen(true)] out BoundUserInterface? bui, EntityUid? target = null, bool shouldSucceed = true)
+ {
+ bui = null;
+ target ??= Target;
+ if (target == null)
+ {
+ Assert.Fail("No target specified");
+ return false;
+ }
+
+ if (!CEntMan.TryGetComponent(target, out ClientUserInterfaceComponent? ui))
+ {
+ if (shouldSucceed)
+ Assert.Fail($"Entity {SEntMan.ToPrettyString(target.Value)} does not have a bui component");
+ return false;
+ }
+
+ var first = ui.Interfaces.First();
+
+
+ bui = ui.Interfaces.FirstOrDefault(x => x.UiKey.Equals(key));
+ if (bui == null)
+ {
+ if (shouldSucceed)
+ Assert.Fail($"Entity {SEntMan.ToPrettyString(target.Value)} does not have an open bui with key {key.GetType()}.{key}.");
+ return false;
+ }
+
+ Assert.That(shouldSucceed, Is.True);
+ return true;
+ }
+
+ #endregion
+
+ #region Power
+
+ protected void ToggleNeedPower(EntityUid? target = null)
+ {
+ var comp = Comp(target);
+ comp.NeedsPower = !comp.NeedsPower;
+ }
+
+ #endregion
}
diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs
index 582e68aac7..16f6c71ca0 100644
--- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs
+++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs
@@ -13,6 +13,7 @@ using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using NUnit.Framework;
+using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
@@ -97,11 +98,12 @@ public abstract partial class InteractionTest
// player components
protected HandsComponent Hands = default!;
protected DoAfterComponent DoAfters = default!;
+ protected UserInterfaceSystem CUISystem = default!;
public float TickPeriod => (float)Timing.TickPeriod.TotalSeconds;
[SetUp]
- public async Task Setup()
+ public virtual async Task Setup()
{
PairTracker = await PoolManager.GetServerClient(new PoolSettings());
@@ -126,6 +128,7 @@ public abstract partial class InteractionTest
CTestSystem = CEntMan.System();
CConSys = CEntMan.System();
ExamineSys = CEntMan.System();
+ CUISystem = CEntMan.System();
// Setup map.
MapData = await PoolManager.CreateTestMap(PairTracker);
@@ -189,7 +192,7 @@ public abstract partial class InteractionTest
}
[TearDown]
- public async Task Cleanup()
+ public virtual async Task Cleanup()
{
await Server.WaitPost(() => MapMan.DeleteMap(MapId));
await PairTracker.CleanReturnAsync();