Add interaction tests (#15251)

This commit is contained in:
Leon Friedrich
2023-04-15 07:41:25 +12:00
committed by GitHub
parent ffe946729f
commit 489660a6bb
36 changed files with 2354 additions and 32 deletions

View File

@@ -0,0 +1,99 @@
using System.Threading.Tasks;
using Content.IntegrationTests.Tests.Interaction;
using NUnit.Framework;
namespace Content.IntegrationTests.Tests.Construction.Interaction;
public sealed class ComputerConstruction : InteractionTest
{
private const string Computer = "Computer";
private const string ComputerId = "ComputerId";
private const string ComputerFrame = "ComputerFrame";
private const string IdBoard = "IDComputerCircuitboard";
[Test]
public async Task ConstructComputer()
{
// Place ghost
await StartConstruction(Computer);
// Initial interaction (ghost turns into real entity)
await Interact(Steel, 5);
AssertPrototype(ComputerFrame);
// Perform construction steps
await Interact(
Wrench,
IdBoard,
Screw,
(Cable, 5),
(Glass, 2),
Screw);
// Construction finished, target entity was replaced with a new one:
AssertPrototype(ComputerId);
}
[Test]
public async Task DeconstructComputer()
{
// Spawn initial entity
await StartDeconstruction(ComputerId);
// Initial interaction turns id computer into generic computer
await Interact(Screw);
AssertPrototype(ComputerFrame);
// Perform deconstruction steps
await Interact(
Pry,
Cut,
Screw,
Pry,
Wrench,
Weld);
// construction finished, entity no longer exists.
AssertDeleted();
// Check expected entities were dropped.
await AssertEntityLookup(
IdBoard,
(Cable, 5),
(Steel, 5),
(Glass, 2));
}
[Test]
public async Task ChangeComputer()
{
// Spawn initial entity
await SpawnTarget(ComputerId);
// Initial interaction turns id computer into generic computer
await Interact(Screw);
AssertPrototype(ComputerFrame);
// Perform partial deconstruction steps
await Interact(
Pry,
Cut,
Screw,
Pry);
// Entity should still exist
AssertPrototype(ComputerFrame);
// Begin re-constructing with a new circuit board
await Interact(
"CargoRequestComputerCircuitboard",
Screw,
(Cable, 5),
(Glass, 2),
Screw);
// Construction finished, target entity was replaced with a new one:
AssertPrototype("ComputerCargoOrders");
}
}

View File

@@ -0,0 +1,127 @@
using System.Linq;
using System.Threading.Tasks;
using Content.IntegrationTests.Tests.Interaction;
using Content.Shared.Stacks;
using NUnit.Framework;
using Robust.Shared.Containers;
namespace Content.IntegrationTests.Tests.Construction.Interaction;
public sealed class CraftingTests : InteractionTest
{
public const string ShardGlass = "ShardGlass";
public const string Spear = "Spear";
/// <summary>
/// Craft a simple instant recipe
/// </summary>
[Test]
public async Task CraftRods()
{
await PlaceInHands(Steel);
await CraftItem(Rod);
await FindEntity((Rod, 2));
}
/// <summary>
/// Craft a simple recipe with a DoAfter
/// </summary>
[Test]
public async Task CraftGrenade()
{
await PlaceInHands(Steel, 5);
await CraftItem("ModularGrenadeRecipe");
await FindEntity("ModularGrenade");
}
/// <summary>
/// Craft a complex recipe (more than one ingredient).
/// </summary>
[Test]
public async Task CraftSpear()
{
// Spawn a full tack of rods in the user's hands.
await PlaceInHands(Rod, 10);
await SpawnEntity((Cable, 10), PlayerCoords);
// Attempt (and fail) to craft without glass.
await CraftItem(Spear, shouldSucceed: false);
await FindEntity(Spear, shouldSucceed: false);
// Spawn three shards of glass and finish crafting (only one is needed).
await SpawnTarget(ShardGlass);
await SpawnTarget(ShardGlass);
await SpawnTarget(ShardGlass);
await CraftItem(Spear);
await FindEntity(Spear);
// Player's hands should be full of the remaining rods, except those dropped during the failed crafting attempt.
// Spear and left over stacks should be on the floor.
await AssertEntityLookup((Rod, 2), (Cable, 8), (ShardGlass, 2), (Spear, 1));
}
// The following is wrapped in an if DEBUG. This is because of cursed state handling bugs. Tests don't (de)serialize
// net messages and just copy objects by reference. This means that the server will directly modify cached server
// states on the client's end. Crude fix at the moment is to used modified state handling while in debug mode
// Otherwise, this test cannot work.
#if DEBUG
/// <summary>
/// Cancel crafting a complex recipe.
/// </summary>
[Test]
public async Task CancelCraft()
{
var rods = await SpawnEntity((Rod, 10), TargetCoords);
var wires = await SpawnEntity((Cable, 10), TargetCoords);
var shard = await SpawnEntity(ShardGlass, TargetCoords);
var rodStack = SEntMan.GetComponent<StackComponent>(rods);
var wireStack = SEntMan.GetComponent<StackComponent>(wires);
await RunTicks(5);
var sys = SEntMan.System<SharedContainerSystem>();
Assert.That(sys.IsEntityInContainer(rods), Is.False);
Assert.That(sys.IsEntityInContainer(wires), Is.False);
Assert.That(sys.IsEntityInContainer(shard), Is.False);
await Server.WaitPost(() => SConstruction.TryStartItemConstruction(Spear, Player));
await RunTicks(1);
// DoAfter is in progress. Entity not spawned, stacks have been split and someingredients are in a container.
Assert.That(ActiveDoAfters.Count(), Is.EqualTo(1));
Assert.That(sys.IsEntityInContainer(shard), Is.True);
Assert.That(sys.IsEntityInContainer(rods), Is.False);
Assert.That(sys.IsEntityInContainer(wires), Is.False);
Assert.That(rodStack.Count, Is.EqualTo(8));
Assert.That(wireStack.Count, Is.EqualTo(8));
await FindEntity(Spear, shouldSucceed: false);
// Cancel the DoAfter. Should drop ingredients to the floor.
await CancelDoAfters();
Assert.That(sys.IsEntityInContainer(rods), Is.False);
Assert.That(sys.IsEntityInContainer(wires), Is.False);
Assert.That(sys.IsEntityInContainer(shard), Is.False);
await FindEntity(Spear, shouldSucceed: false);
await AssertEntityLookup((Rod, 10), (Cable, 10), (ShardGlass, 1));
// Re-attempt the do-after
await Server.WaitPost(() => SConstruction.TryStartItemConstruction(Spear, Player));
await RunTicks(1);
// DoAfter is in progress. Entity not spawned, ingredients are in a container.
Assert.That(ActiveDoAfters.Count(), Is.EqualTo(1));
Assert.That(sys.IsEntityInContainer(shard), Is.True);
await FindEntity(Spear, shouldSucceed: false);
// Finish the DoAfter
await AwaitDoAfters();
// Spear has been crafted. Rods and wires are no longer contained. Glass has been consumed.
await FindEntity(Spear);
Assert.That(sys.IsEntityInContainer(rods), Is.False);
Assert.That(sys.IsEntityInContainer(wires), Is.False);
Assert.That(SEntMan.Deleted(shard));
}
#endif
}

View File

@@ -0,0 +1,59 @@
using System.Threading.Tasks;
using Content.IntegrationTests.Tests.Interaction;
using Content.Shared.Construction.Prototypes;
using NUnit.Framework;
using Robust.Shared.Maths;
namespace Content.IntegrationTests.Tests.Construction.Interaction;
/// <summary>
/// Check that we can build grilles on top of windows, but not the other way around.
/// </summary>
public sealed class GrilleWindowConstruction : InteractionTest
{
private const string Grille = "Grille";
private const string Window = "Window";
[Test]
public async Task WindowOnGrille()
{
// Construct Grille
await StartConstruction(Grille);
await Interact(Rod, 10);
AssertPrototype(Grille);
var grille = Target;
// Construct Window
await StartConstruction(Window);
await Interact(Glass, 10);
AssertPrototype(Window);
// Deconstruct Window
await Interact(Screw, Wrench);
AssertDeleted();
// Deconstruct Grille
Target = grille;
await Interact(Cut);
AssertDeleted();
}
[Test]
[TestCase(Grille, Grille)]
[TestCase(Window, Grille)]
[TestCase(Window, Window)]
public async Task ConstructionBlocker(string first, string second)
{
// Spawn blocking entity
await SpawnTarget(first);
// Further construction attempts fail - blocked by first entity interaction.
await Client.WaitPost(() =>
{
var proto = ProtoMan.Index<ConstructionPrototype>(second);
Assert.That(CConSys.TrySpawnGhost(proto, TargetCoords, Direction.South, out _), Is.False);
});
}
}

View File

@@ -0,0 +1,89 @@
using System.Threading.Tasks;
using Content.IntegrationTests.Tests.Interaction;
using NUnit.Framework;
namespace Content.IntegrationTests.Tests.Construction.Interaction;
public sealed class MachineConstruction : InteractionTest
{
private const string MachineFrame = "MachineFrame";
private const string Unfinished = "UnfinishedMachineFrame";
private const string ProtolatheBoard = "ProtolatheMachineCircuitboard";
private const string Protolathe = "Protolathe";
private const string Beaker = "Beaker";
[Test]
public async Task ConstructProtolathe()
{
await StartConstruction(MachineFrame);
await Interact(Steel, 5);
AssertPrototype(Unfinished);
await Interact(Wrench, Cable);
AssertPrototype(MachineFrame);
await Interact(ProtolatheBoard, Bin1, Bin1, Manipulator1, Manipulator1, Beaker, Beaker, Screw);
AssertPrototype(Protolathe);
}
[Test]
public async Task DeconstructProtolathe()
{
await StartDeconstruction(Protolathe);
await Interact(Screw, Pry);
AssertPrototype(MachineFrame);
await Interact(Pry, Cut);
AssertPrototype(Unfinished);
await Interact(Wrench, Screw);
AssertDeleted();
await AssertEntityLookup(
(Steel, 5),
(Cable, 1),
(Beaker, 2),
(Manipulator1, 2),
(Bin1, 2),
(ProtolatheBoard, 1));
}
[Test]
public async Task ChangeMachine()
{
// Partially deconstruct a protolathe.
await SpawnTarget(Protolathe);
await Interact(Screw, Pry, Pry);
AssertPrototype(MachineFrame);
// Change it into an autolathe
await Interact("AutolatheMachineCircuitboard");
AssertPrototype(MachineFrame);
await Interact(Bin1, Bin1, Bin1, Manipulator1, Glass, Screw);
AssertPrototype("Autolathe");
}
[Test]
public async Task UpgradeLathe()
{
// Partially deconstruct a protolathe.
await SpawnTarget(Protolathe);
// Initially has all quality-1 parts.
foreach (var part in SConstruction.GetAllParts(Target!.Value))
{
Assert.That(part.Rating, Is.EqualTo(1));
}
// Partially deconstruct lathe
await Interact(Screw, Pry, Pry);
AssertPrototype(MachineFrame);
// Reconstruct with better parts.
await Interact(ProtolatheBoard, Bin4, Bin4, Manipulator4, Manipulator4, Beaker, Beaker);
await Interact(Screw);
AssertPrototype(Protolathe);
// Query now returns higher quality parts.
foreach (var part in SConstruction.GetAllParts(Target!.Value))
{
Assert.That(part.Rating, Is.EqualTo(4));
}
}
}

View File

@@ -0,0 +1,69 @@
using System.Threading.Tasks;
using Content.IntegrationTests.Tests.Interaction;
using Content.Server.Power.Components;
using Content.Shared.Wires;
using NUnit.Framework;
using Robust.Shared.GameObjects;
namespace Content.IntegrationTests.Tests.Construction.Interaction;
public sealed class PanelScrewing : InteractionTest
{
[Test]
public async Task ApcPanel()
{
await SpawnTarget("APCBasic");
var comp = Comp<ApcComponent>();
// Open & close panel
Assert.That(comp.IsApcOpen, Is.False);
await Interact(Screw);
Assert.That(comp.IsApcOpen, Is.True);
await Interact(Screw);
Assert.That(comp.IsApcOpen, Is.False);
// Interrupted DoAfters
await Interact(Screw, awaitDoAfters: false);
await CancelDoAfters();
Assert.That(comp.IsApcOpen, Is.False);
await Interact(Screw);
Assert.That(comp.IsApcOpen, Is.True);
await Interact(Screw, awaitDoAfters: false);
await CancelDoAfters();
Assert.That(comp.IsApcOpen, Is.True);
await Interact(Screw);
Assert.That(comp.IsApcOpen, Is.False);
}
// Test wires panel on both airlocks & tcomms servers. These both use the same component, but comms may have
// conflicting interactions due to encryption key removal interactions.
[Test]
[TestCase("Airlock")]
[TestCase("TelecomServerFilled")]
public async Task WiresPanelScrewing(string prototype)
{
await SpawnTarget(prototype);
var comp = Comp<WiresPanelComponent>();
// Open & close panel
Assert.That(comp.Open, Is.False);
await Interact(Screw);
Assert.That(comp.Open, Is.True);
await Interact(Screw);
Assert.That(comp.Open, Is.False);
// Interrupted DoAfters
await Interact(Screw, awaitDoAfters: false);
await CancelDoAfters();
Assert.That(comp.Open, Is.False);
await Interact(Screw);
Assert.That(comp.Open, Is.True);
await Interact(Screw, awaitDoAfters: false);
await CancelDoAfters();
Assert.That(comp.Open, Is.True);
await Interact(Screw);
Assert.That(comp.Open, Is.False);
}
}

View File

@@ -0,0 +1,25 @@
using System.Threading.Tasks;
using Content.IntegrationTests.Tests.Interaction;
using Content.Shared.Placeable;
using NUnit.Framework;
namespace Content.IntegrationTests.Tests.Construction.Interaction;
public sealed class PlaceableDeconstruction : InteractionTest
{
/// <summary>
/// Checks that you can deconstruct placeable surfaces (i.e., placing a wrench on a table does not take priority).
/// </summary>
[Test]
public async Task DeconstructTable()
{
await StartDeconstruction("Table");
Assert.That(Comp<PlaceableSurfaceComponent>().IsPlaceable);
await Interact(Wrench);
AssertPrototype("TableFrame");
await Interact(Wrench);
AssertDeleted();
await AssertEntityLookup((Steel, 1), (Rod, 2));
}
}

View File

@@ -0,0 +1,36 @@
using System.Threading.Tasks;
using Content.IntegrationTests.Tests.Interaction;
using NUnit.Framework;
namespace Content.IntegrationTests.Tests.Construction.Interaction;
public sealed class WallConstruction : InteractionTest
{
public const string Girder = "Girder";
public const string WallSolid = "WallSolid";
public const string Wall = "Wall";
[Test]
public async Task ConstructWall()
{
await StartConstruction(Wall);
await Interact(Steel, 2);
Assert.IsNull(Hands.ActiveHandEntity);
AssertPrototype(Girder);
await Interact(Steel, 2);
Assert.IsNull(Hands.ActiveHandEntity);
AssertPrototype(WallSolid);
}
[Test]
public async Task DeconstructWall()
{
await StartDeconstruction(WallSolid);
await Interact(Weld);
AssertPrototype(Girder);
await Interact(Wrench, Screw);
AssertDeleted();
await AssertEntityLookup((Steel, 4));
}
}

View File

@@ -0,0 +1,52 @@
using System.Threading.Tasks;
using Content.IntegrationTests.Tests.Interaction;
using NUnit.Framework;
namespace Content.IntegrationTests.Tests.Construction.Interaction;
public sealed class WindowConstruction : InteractionTest
{
private const string Window = "Window";
private const string RWindow = "ReinforcedWindow";
[Test]
public async Task ConstructWindow()
{
await StartConstruction(Window);
await Interact(Glass, 5);
AssertPrototype(Window);
}
[Test]
public async Task DeconstructWindow()
{
await StartDeconstruction(Window);
await Interact(Screw, Wrench);
AssertDeleted();
await AssertEntityLookup((Glass, 2));
}
[Test]
public async Task ConstructReinforcedWindow()
{
await StartConstruction(RWindow);
await Interact(RGlass, 5);
AssertPrototype(RWindow);
}
[Test]
public async Task DeonstructReinforcedWindow()
{
await StartDeconstruction(RWindow);
await Interact(
Weld,
Screw,
Pry,
Weld,
Screw,
Wrench);
AssertDeleted();
await AssertEntityLookup((RGlass, 2));
}
}

View File

@@ -0,0 +1,44 @@
using System.Threading.Tasks;
using Content.IntegrationTests.Tests.Interaction;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using NUnit.Framework;
using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests.Construction.Interaction;
public sealed class WindowRepair : InteractionTest
{
[Test]
public async Task RepairReinforcedWindow()
{
await SpawnTarget("ReinforcedWindow");
// Damage the entity.
var sys = SEntMan.System<DamageableSystem>();
var comp = Comp<DamageableComponent>();
var damageType = Server.ResolveDependency<IPrototypeManager>().Index<DamageTypePrototype>("Blunt");
var damage = new DamageSpecifier(damageType, FixedPoint2.New(10));
Assert.That(comp.Damage.Total, Is.EqualTo(FixedPoint2.Zero));
await Server.WaitPost(() => sys.TryChangeDamage(Target, damage, ignoreResistances: true));
await RunTicks(5);
Assert.That(comp.Damage.Total, Is.GreaterThan(FixedPoint2.Zero));
// Repair the entity
await Interact(Weld);
Assert.That(comp.Damage.Total, Is.EqualTo(FixedPoint2.Zero));
// Validate that we can still deconstruct the entity (i.e., that welding deconstruction is not blocked).
await Interact(
Weld,
Screw,
Pry,
Weld,
Screw,
Wrench);
AssertDeleted();
await AssertEntityLookup((RGlass, 2));
}
}