mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
Robust.UnitTesting was both ALL tests for RT, and also API surface for content tests. Tests are now split into separate projects as appropriate, and the API side has also been split off.
297 lines
12 KiB
C#
297 lines
12 KiB
C#
using System.Linq;
|
|
using System.Numerics;
|
|
using System.Threading.Tasks;
|
|
using NUnit.Framework;
|
|
using Robust.Shared;
|
|
using Robust.Shared.Analyzers;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.GameStates;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Network;
|
|
using Robust.Shared.Reflection;
|
|
using Robust.Shared.Serialization.Manager.Attributes;
|
|
|
|
namespace Robust.UnitTesting.Shared.GameState;
|
|
|
|
internal sealed partial class ComponentStateTests : RobustIntegrationTest
|
|
{
|
|
/// <summary>
|
|
/// This tests performs a basic check to ensure that there is no issue with entity states referencing other
|
|
/// entities that the client is not yet aware of. It does this by spawning two entities that reference each other,
|
|
/// and then ensuring that they get sent to the client one at a time.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task UnknownEntityTest()
|
|
{
|
|
// Setup auto-comp-states. I hate this. Someone please fix reflection in RobustIntegrationTest
|
|
var serverOpts = new ServerIntegrationOptions { Pool = false };
|
|
var clientOpts = new ClientIntegrationOptions { Pool = false };
|
|
var server = StartServer(serverOpts);
|
|
var client = StartClient(clientOpts);
|
|
|
|
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
|
var netMan = client.ResolveDependency<IClientNetManager>();
|
|
var xforms = server.System<SharedTransformSystem>();
|
|
|
|
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
|
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
|
server.Post(() => server.CfgMan.SetCVar(CVars.NetPVS, true));
|
|
|
|
// Set up map.
|
|
EntityUid map = default;
|
|
server.Post(() =>
|
|
{
|
|
map = server.System<SharedMapSystem>().CreateMap();
|
|
});
|
|
|
|
await RunTicks();
|
|
|
|
// Spawn entities
|
|
var coordsA = new EntityCoordinates(map, default);
|
|
var coordsB = new EntityCoordinates(map, new Vector2(100, 100));
|
|
EntityUid player = default;
|
|
EntityUid cPlayer = default;
|
|
EntityUid serverEntA = default;
|
|
EntityUid serverEntB = default;
|
|
NetEntity serverNetA = default;
|
|
NetEntity serverNetB = default;
|
|
|
|
await server.WaitPost(() =>
|
|
{
|
|
// Attach player.
|
|
player = server.EntMan.Spawn();
|
|
var session = server.PlayerMan.Sessions.First();
|
|
server.PlayerMan.SetAttachedEntity(session, player);
|
|
server.PlayerMan.JoinGame(session);
|
|
|
|
// Spawn test entities.
|
|
serverEntA = server.EntMan.SpawnAttachedTo(null, coordsA);
|
|
serverEntB = server.EntMan.SpawnAttachedTo(null, coordsB);
|
|
serverNetA = server.EntMan.GetNetEntity(serverEntA);
|
|
serverNetB = server.EntMan.GetNetEntity(serverEntB);
|
|
|
|
// Setup components
|
|
var cmp = server.EntMan.EnsureComponent<UnknownEntityTestComponent>(serverEntA);
|
|
cmp.Other = serverEntB;
|
|
server.EntMan.Dirty(serverEntA, cmp);
|
|
|
|
cmp = server.EntMan.EnsureComponent<UnknownEntityTestComponent>(serverEntB);
|
|
cmp.Other = serverEntA;
|
|
server.EntMan.Dirty(serverEntB, cmp);
|
|
});
|
|
|
|
await RunTicks();
|
|
|
|
// Check player got properly attached and only knows about the expected entities
|
|
await client.WaitPost(() =>
|
|
{
|
|
cPlayer = client.EntMan.GetEntity(server.EntMan.GetNetEntity(player));
|
|
Assert.That(client.AttachedEntity, Is.EqualTo(cPlayer));
|
|
Assert.That(client.EntMan.EntityExists(cPlayer));
|
|
Assert.That(client.EntMan.EntityExists(client.EntMan.GetEntity(serverNetA)), Is.False);
|
|
Assert.That(client.EntMan.EntityExists(client.EntMan.GetEntity(serverNetB)), Is.False);
|
|
});
|
|
|
|
// Move the player into PVS range of one of the entities.
|
|
await server.WaitPost(() => xforms.SetCoordinates(player, coordsB));
|
|
await RunTicks();
|
|
|
|
await client.WaitPost(() =>
|
|
{
|
|
var clientEntA = client.EntMan.GetEntity(serverNetA);
|
|
var clientEntB = client.EntMan.GetEntity(serverNetB);
|
|
Assert.That(client.EntMan.EntityExists(clientEntB), Is.True);
|
|
Assert.That(client.EntMan.EntityExists(client.EntMan.GetEntity(serverNetA)), Is.False);
|
|
|
|
Assert.That(client.EntMan.TryGetComponent(clientEntB, out UnknownEntityTestComponent? cmp));
|
|
Assert.That(cmp?.Other, Is.EqualTo(clientEntA));
|
|
});
|
|
|
|
// Move the player into PVS range of the other entity
|
|
await server.WaitPost(() => xforms.SetCoordinates(player, coordsA));
|
|
await RunTicks();
|
|
|
|
await client.WaitPost(() =>
|
|
{
|
|
var clientEntA = client.EntMan.GetEntity(serverNetA);
|
|
var clientEntB = client.EntMan.GetEntity(serverNetB);
|
|
Assert.That(client.EntMan.EntityExists(clientEntB), Is.True);
|
|
Assert.That(client.EntMan.EntityExists(clientEntA), Is.True);
|
|
|
|
Assert.That(client.EntMan.TryGetComponent(clientEntB, out UnknownEntityTestComponent? cmp));
|
|
Assert.That(cmp?.Other, Is.EqualTo(clientEntA));
|
|
|
|
Assert.That(client.EntMan.TryGetComponent(clientEntA, out cmp));
|
|
Assert.That(cmp?.Other, Is.EqualTo(clientEntB));
|
|
});
|
|
|
|
server.Post(() => server.CfgMan.SetCVar(CVars.NetPVS, false));
|
|
|
|
// wait for errors.
|
|
await RunTicks();
|
|
|
|
async Task RunTicks()
|
|
{
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
await server!.WaitRunTicks(1);
|
|
await client!.WaitRunTicks(1);
|
|
}
|
|
}
|
|
|
|
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
|
await server.WaitRunTicks(5);
|
|
await client.WaitRunTicks(5);
|
|
}
|
|
|
|
/// <summary>
|
|
/// This is a variant of <see cref="UnknownEntityTest"/> that deletes one of the entities before the other entity gets sent.
|
|
/// </summary>
|
|
[Test]
|
|
public async Task UnknownEntityDeleteTest()
|
|
{
|
|
// The first chunk of the test just follows UnknownEntityTest
|
|
var serverOpts = new ServerIntegrationOptions { Pool = false };
|
|
var clientOpts = new ClientIntegrationOptions { Pool = false };
|
|
var server = StartServer(serverOpts);
|
|
var client = StartClient(clientOpts);
|
|
|
|
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
|
|
var netMan = client.ResolveDependency<IClientNetManager>();
|
|
var xforms = server.System<SharedTransformSystem>();
|
|
|
|
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
|
|
client.Post(() => netMan.ClientConnect(null!, 0, null!));
|
|
server.Post(() => server.CfgMan.SetCVar(CVars.NetPVS, true));
|
|
|
|
// Set up map.
|
|
EntityUid map = default;
|
|
server.Post(() =>
|
|
{
|
|
map = server.System<SharedMapSystem>().CreateMap();
|
|
});
|
|
|
|
await RunTicks();
|
|
|
|
// Spawn entities
|
|
var coordsA = new EntityCoordinates(map, default);
|
|
var coordsB = new EntityCoordinates(map, new Vector2(100, 100));
|
|
EntityUid player = default;
|
|
EntityUid cPlayer = default;
|
|
EntityUid serverEntA = default;
|
|
EntityUid serverEntB = default;
|
|
NetEntity serverNetA = default;
|
|
NetEntity serverNetB = default;
|
|
|
|
await server.WaitPost(() =>
|
|
{
|
|
// Attach player.
|
|
player = server.EntMan.Spawn();
|
|
var session = server.PlayerMan.Sessions.First();
|
|
server.PlayerMan.SetAttachedEntity(session, player);
|
|
server.PlayerMan.JoinGame(session);
|
|
|
|
// Spawn test entities.
|
|
serverEntA = server.EntMan.SpawnAttachedTo(null, coordsA);
|
|
serverEntB = server.EntMan.SpawnAttachedTo(null, coordsB);
|
|
serverNetA = server.EntMan.GetNetEntity(serverEntA);
|
|
serverNetB = server.EntMan.GetNetEntity(serverEntB);
|
|
|
|
// Setup components
|
|
var cmp = server.EntMan.EnsureComponent<UnknownEntityTestComponent>(serverEntA);
|
|
cmp.Other = serverEntB;
|
|
server.EntMan.Dirty(serverEntA, cmp);
|
|
|
|
cmp = server.EntMan.EnsureComponent<UnknownEntityTestComponent>(serverEntB);
|
|
cmp.Other = serverEntA;
|
|
server.EntMan.Dirty(serverEntB, cmp);
|
|
});
|
|
|
|
await RunTicks();
|
|
|
|
// Check player got properly attached and only knows about the expected entities
|
|
await client.WaitPost(() =>
|
|
{
|
|
cPlayer = client.EntMan.GetEntity(server.EntMan.GetNetEntity(player));
|
|
Assert.That(client.AttachedEntity, Is.EqualTo(cPlayer));
|
|
Assert.That(client.EntMan.EntityExists(cPlayer));
|
|
Assert.That(client.EntMan.EntityExists(client.EntMan.GetEntity(serverNetA)), Is.False);
|
|
Assert.That(client.EntMan.EntityExists(client.EntMan.GetEntity(serverNetB)), Is.False);
|
|
});
|
|
|
|
// Move the player into PVS range of one of the entities.
|
|
await server.WaitPost(() => xforms.SetCoordinates(player, coordsB));
|
|
await RunTicks();
|
|
|
|
await client.WaitPost(() =>
|
|
{
|
|
var clientEntA = client.EntMan.GetEntity(serverNetA);
|
|
var clientEntB = client.EntMan.GetEntity(serverNetB);
|
|
Assert.That(client.EntMan.EntityExists(clientEntB), Is.True);
|
|
Assert.That(client.EntMan.EntityExists(clientEntA), Is.False);
|
|
|
|
Assert.That(client.EntMan.TryGetComponent(clientEntB, out UnknownEntityTestComponent? cmp));
|
|
Assert.That(cmp?.Other, Is.EqualTo(clientEntA));
|
|
});
|
|
|
|
// This is where the test difffers from UnknownEntityTest:
|
|
// We delete the entity that the player knows about before it receives the entity that it references.
|
|
await server.WaitPost(() =>
|
|
{
|
|
server.EntMan.DeleteEntity(serverEntB);
|
|
var comp = server.EntMan.GetComponent<UnknownEntityTestComponent>(serverEntA);
|
|
comp.Other = EntityUid.Invalid;
|
|
server.EntMan.Dirty(serverEntA, comp);
|
|
});
|
|
await RunTicks();
|
|
|
|
await client.WaitPost(() =>
|
|
{
|
|
var clientEntA = client.EntMan.GetEntity(serverNetA);
|
|
var clientEntB = client.EntMan.GetEntity(serverNetB);
|
|
Assert.That(clientEntA, Is.EqualTo(EntityUid.Invalid));
|
|
Assert.That(clientEntB, Is.EqualTo(EntityUid.Invalid));
|
|
});
|
|
|
|
// Move the player into PVS range of the other entity
|
|
await server.WaitPost(() => xforms.SetCoordinates(player, coordsA));
|
|
await RunTicks();
|
|
|
|
await client.WaitPost(() =>
|
|
{
|
|
var clientEntA = client.EntMan.GetEntity(serverNetA);
|
|
var clientEntB = client.EntMan.GetEntity(serverNetB);
|
|
Assert.That(clientEntB, Is.EqualTo(EntityUid.Invalid));
|
|
Assert.That(client.EntMan.EntityExists(clientEntA), Is.True);
|
|
Assert.That(client.EntMan.TryGetComponent(clientEntA, out UnknownEntityTestComponent? cmp));
|
|
Assert.That(cmp?.Other, Is.EqualTo(EntityUid.Invalid));
|
|
});
|
|
|
|
server.Post(() => server.CfgMan.SetCVar(CVars.NetPVS, false));
|
|
|
|
// wait for errors.
|
|
await RunTicks();
|
|
|
|
async Task RunTicks()
|
|
{
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
await server!.WaitRunTicks(1);
|
|
await client!.WaitRunTicks(1);
|
|
}
|
|
}
|
|
|
|
await client.WaitPost(() => netMan.ClientDisconnect(""));
|
|
await server.WaitRunTicks(5);
|
|
await client.WaitRunTicks(5);
|
|
}
|
|
}
|
|
|
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
|
public sealed partial class UnknownEntityTestComponent : Component
|
|
{
|
|
[DataField, AutoNetworkedField]
|
|
public EntityUid? Other;
|
|
}
|