Split up test project

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.
This commit is contained in:
PJB3005
2025-12-16 01:36:53 +01:00
parent 095c5f58d9
commit 788e9386fd
284 changed files with 849 additions and 595 deletions

View File

@@ -1,89 +0,0 @@
using System.Numerics;
using System.Runtime.Intrinsics;
using NUnit.Framework.Constraints;
using Robust.Shared.Maths;
namespace Robust.UnitTesting
{
public sealed class ApproxEqualityConstraint : Constraint
{
public object Expected { get; }
public double? Tolerance { get; }
public ApproxEqualityConstraint(object expected, double? tolerance = null)
{
Expected = expected;
Tolerance = tolerance;
}
public override ConstraintResult ApplyTo<TActual>(TActual actual)
{
if (Expected is not IApproxEquatable<TActual> equatable)
{
if (Expected is float f1 && actual is float f2)
{
if (Tolerance != null)
{
return new ConstraintResult(this, actual, MathHelper.CloseToPercent(f1, f2, Tolerance.Value));
}
return new ConstraintResult(this, actual, MathHelper.CloseToPercent(f1, f2));
}
if (Expected is double d1 && actual is float d2)
{
if (Tolerance != null)
{
return new ConstraintResult(this, actual, MathHelper.CloseToPercent(d1, d2, Tolerance.Value));
}
return new ConstraintResult(this, actual, MathHelper.CloseToPercent(d1, d2));
}
if (Expected is Vector2 exp && actual is Vector2 act)
{
if (Tolerance != null)
{
return new ConstraintResult(this, actual, exp.EqualsApprox(act, Tolerance.Value));
}
return new ConstraintResult(this, actual, exp.EqualsApprox(act));
}
if (Expected is Matrix3x2 m3x2Expected && actual is Matrix3x2 m3x2Actual)
{
if (Tolerance != null)
{
return new ConstraintResult(this, actual, m3x2Expected.EqualsApprox(m3x2Actual, Tolerance.Value));
}
return new ConstraintResult(this, actual, m3x2Expected.EqualsApprox(m3x2Actual));
}
if (Expected is Vector128<float> vecExpected && actual is Vector128<float> vecActual)
{
if (Tolerance != null)
{
return new ConstraintResult(this,
actual,
MathHelper.CloseToPercent(vecActual, vecExpected, (float)Tolerance.Value));
}
return new ConstraintResult(this, actual, MathHelper.CloseToPercent(vecActual, vecExpected));
}
return new ConstraintResult(this, actual, false);
}
if (Tolerance != null)
{
return new ConstraintResult(this, actual, equatable.EqualsApprox(actual, Tolerance.Value));
}
else
{
return new ConstraintResult(this, actual, equatable.EqualsApprox(actual));
}
}
public override string Description => $"approximately {Expected}";
}
}

View File

@@ -3,5 +3,7 @@ using NUnit.Framework;
// So it can use RobustServerSimulation.
[assembly: InternalsVisibleTo("Robust.Benchmarks")]
[assembly: InternalsVisibleTo("Robust.Server.IntegrationTests")]
[assembly: InternalsVisibleTo("Robust.Shared.IntegrationTests")]
[assembly: Parallelizable(ParallelScope.Fixtures)]

View File

@@ -1,122 +0,0 @@
using System.Numerics;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Timing;
using Robust.UnitTesting.Server;
namespace Robust.UnitTesting.Client.GameObjects.Components
{
[TestFixture]
[TestOf(typeof(TransformComponent))]
public sealed class TransformComponentTests
{
private static (ISimulation, EntityUid gridA, EntityUid gridB) SimulationFactory()
{
var sim = RobustServerSimulation
.NewSimulation()
.InitializeInstance();
var mapId = sim.Resolve<IEntityManager>().System<SharedMapSystem>().CreateMap();
var mapManager = sim.Resolve<IMapManager>();
// Adds two grids to use in tests.
var gridA = mapManager.CreateGridEntity(mapId);
var gridB = mapManager.CreateGridEntity(mapId);
return (sim, gridA, gridB);
}
/// <summary>
/// Make sure that component state locations are RELATIVE.
/// </summary>
[Test]
public void ComponentStatePositionTest()
{
var (sim, gridIdA, gridIdB) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var xformSystem = entMan.System<SharedTransformSystem>();
var gridSystem = entMan.System<SharedGridTraversalSystem>();
gridSystem.Enabled = false;
// Arrange
var initialPos = new EntityCoordinates(gridIdA, new Vector2(0, 0));
var parent = entMan.SpawnEntity(null, initialPos);
var child = entMan.SpawnEntity(null, initialPos);
var parentTrans = entMan.GetComponent<TransformComponent>(parent);
var childTrans = entMan.GetComponent<TransformComponent>(child);
ComponentHandleState handleState;
var compState = new TransformComponentState(new Vector2(5, 5), new Angle(0), entMan.GetNetEntity(gridIdB), false, false);
handleState = new ComponentHandleState(compState, null);
xformSystem.OnHandleState(parent, parentTrans, ref handleState);
compState = new TransformComponentState(new Vector2(6, 6), new Angle(0), entMan.GetNetEntity(gridIdB), false, false);
handleState = new ComponentHandleState(compState, null);
xformSystem.OnHandleState(child, childTrans, ref handleState);
// World pos should be 6, 6 now.
// Act
var oldWpos = xformSystem.GetWorldPosition(childTrans);
compState = new TransformComponentState(new Vector2(1, 1), new Angle(0), entMan.GetNetEntity(parent), false, false);
handleState = new ComponentHandleState(compState, null);
xformSystem.OnHandleState(child, childTrans, ref handleState);
var newWpos = xformSystem.GetWorldPosition(childTrans);
gridSystem.Enabled = true;
// Assert
Assert.That(newWpos, Is.EqualTo(oldWpos));
}
/// <summary>
/// Tests that world rotation is built properly
/// </summary>
[Test]
public void WorldRotationTest()
{
var (sim, gridIdA, gridIdB) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var xformSystem = entMan.System<SharedTransformSystem>();
var metaSystem = entMan.System<MetaDataSystem>();
var gridSystem = entMan.System<SharedGridTraversalSystem>();
gridSystem.Enabled = false;
// Arrange
var initalPos = new EntityCoordinates(gridIdA, new Vector2(0, 0));
var node1 = entMan.SpawnEntity(null, initalPos);
var node2 = entMan.SpawnEntity(null, initalPos);
var node3 = entMan.SpawnEntity(null, initalPos);
metaSystem.SetEntityName(node1, "node1_dummy");
metaSystem.SetEntityName(node2, "node2_dummy");
metaSystem.SetEntityName(node3, "node3_dummy");
var node1Trans = entMan.GetComponent<TransformComponent>(node1);
var node2Trans = entMan.GetComponent<TransformComponent>(node2);
var node3Trans = entMan.GetComponent<TransformComponent>(node3);
var compState = new TransformComponentState(new Vector2(6, 6), Angle.FromDegrees(135), entMan.GetNetEntity(gridIdB), false, false);
var handleState = new ComponentHandleState(compState, null);
xformSystem.OnHandleState(node1, node1Trans, ref handleState);
compState = new TransformComponentState(new Vector2(1, 1), Angle.FromDegrees(45), entMan.GetNetEntity(node1), false, false);
handleState = new ComponentHandleState(compState, null);
xformSystem.OnHandleState(node2, node2Trans, ref handleState);
compState = new TransformComponentState(new Vector2(0, 0), Angle.FromDegrees(45), entMan.GetNetEntity(node2), false, false);
handleState = new ComponentHandleState(compState, null);
xformSystem.OnHandleState(node3, node3Trans, ref handleState);
// Act
var result = xformSystem.GetWorldRotation(node3Trans);
gridSystem.Enabled = true;
// Assert (135 + 45 + 45 = 225)
Assert.That(result, new ApproxEqualityConstraint(Angle.FromDegrees(225)));
}
}
}

View File

@@ -1,201 +0,0 @@
using Moq;
using NUnit.Framework;
using Robust.Client.GameStates;
using Robust.Client.Timing;
using Robust.Shared.GameStates;
using Robust.Shared.Log;
using Robust.Shared.Timing;
namespace Robust.UnitTesting.Client.GameStates
{
[TestFixture, Parallelizable, TestOf(typeof(GameStateProcessor))]
sealed class GameStateProcessor_Tests
{
[Test]
public void FillBufferBlocksProcessing()
{
var timingMock = new Mock<IClientGameTiming>();
timingMock.SetupProperty(p => p.CurTick);
var timing = timingMock.Object;
var managerMock = new Mock<IClientGameStateManager>();
var logMock = new Mock<ISawmill>();
var processor = new GameStateProcessor(managerMock.Object, timing, logMock.Object);
processor.Interpolation = true;
processor.AddNewState(GameStateFactory(0, 1));
processor.AddNewState(GameStateFactory(1, 2)); // buffer is at 2/3, so processing should be blocked
// calculate states for first tick
timing.LastProcessedTick = new GameTick(0);
var result = processor.TryGetServerState(out _, out _);
Assert.That(result, Is.False);
}
[Test]
public void FillBufferAndCalculateFirstState()
{
var timingMock = new Mock<IClientGameTiming>();
timingMock.SetupProperty(p => p.CurTick);
var timing = timingMock.Object;
var managerMock = new Mock<IClientGameStateManager>();
var logMock = new Mock<ISawmill>();
var processor = new GameStateProcessor(managerMock.Object, timing, logMock.Object);
processor.AddNewState(GameStateFactory(0, 1));
processor.AddNewState(GameStateFactory(1, 2));
processor.AddNewState(GameStateFactory(2, 3)); // buffer is now full, otherwise cannot calculate states.
// calculate states for first tick
timing.LastProcessedTick = new GameTick(0);
var result = processor.TryGetServerState(out var curState, out var nextState);
Assert.That(result, Is.True);
Assert.That(curState, Is.Not.Null);
Assert.That(curState!.ToSequence.Value, Is.EqualTo(1));
Assert.That(nextState, Is.Null);
}
/// <summary>
/// When a full state is in the queue (fromSequence = 0), it will modify CurTick to the states' toSequence,
/// then return the state as curState.
/// </summary>
[Test]
public void FullStateResyncsCurTick()
{
var timingMock = new Mock<IClientGameTiming>();
timingMock.SetupProperty(p => p.CurTick);
var timing = timingMock.Object;
var managerMock = new Mock<IClientGameStateManager>();
var logMock = new Mock<ISawmill>();
var processor = new GameStateProcessor(managerMock.Object, timing, logMock.Object);
processor.AddNewState(GameStateFactory(0, 1));
processor.AddNewState(GameStateFactory(1, 2));
processor.AddNewState(GameStateFactory(2, 3)); // buffer is now full, otherwise cannot calculate states.
// calculate states for first tick
timing.LastProcessedTick = new GameTick(2);
processor.TryGetServerState(out var state, out _);
Assert.That(state, Is.Not.Null);
Assert.That(state!.ToSequence.Value, Is.EqualTo(1));
}
[Test]
public void StatesReceivedPastCurTickAreDropped()
{
var (timing, processor) = SetupProcessorFactory();
// a few moments later...
timing.LastProcessedTick = new GameTick(4); // current clock is ahead of server
processor.AddNewState(GameStateFactory(3, 4)); // received a late state
var result = processor.TryGetServerState(out _, out _);
Assert.That(result, Is.False);
}
/// <summary>
/// The server fell behind the client, so the client clock is now ahead of the incoming states.
/// Without extrapolation, processing blocks.
/// </summary>
[Test]
public void ServerLagsWithoutExtrapolation()
{
var (timing, processor) = SetupProcessorFactory();
// a few moments later...
timing.LastProcessedTick = new GameTick(4); // current clock is ahead of server
var result = processor.TryGetServerState(out _, out _);
Assert.That(result, Is.False);
}
/// <summary>
/// There is a hole in the state buffer, we have a future state but their FromSequence is too high!
/// In this case we stop and wait for the server to get us the missing link.
/// </summary>
[Test]
public void Hole()
{
var (timing, processor) = SetupProcessorFactory();
processor.AddNewState(GameStateFactory(4, 5));
timing.LastRealTick = new GameTick(3);
timing.LastProcessedTick = new GameTick(3);
var result = processor.TryGetServerState(out _, out _);
Assert.That(result, Is.False);
}
/// <summary>
/// Test that the game state manager goes into extrapolation mode *temporarily*,
/// if we are missing the curState, but we have a future state that we can apply to skip it.
/// </summary>
[Test]
public void ExtrapolateAdvanceWithFutureState()
{
var (timing, processor) = SetupProcessorFactory();
processor.Interpolation = true;
timing.LastProcessedTick = new GameTick(3);
timing.LastRealTick = new GameTick(3);
processor.AddNewState(GameStateFactory(3, 5));
// We're missing the state for this tick so go into extrap.
var result = processor.TryGetServerState(out var curState, out _);
Assert.That(result, Is.True);
Assert.That(curState, Is.Null);
timing.LastProcessedTick = new GameTick(4);
// But we DO have the state for the tick after so apply away!
result = processor.TryGetServerState(out curState, out _);
Assert.That(result, Is.True);
Assert.That(curState, Is.Not.Null);
}
/// <summary>
/// Creates a new empty GameState with the given to and from properties.
/// </summary>
private static GameState GameStateFactory(uint from, uint to)
{
return new(new GameTick(@from), new GameTick(to), 0, default, default, default);
}
/// <summary>
/// Creates a new GameTiming and GameStateProcessor, fills the processor with enough states, and calculate the first tick.
/// CurTick = 1, states 1 - 3 are in the buffer.
/// </summary>
private static (IClientGameTiming timing, GameStateProcessor processor) SetupProcessorFactory()
{
var timingMock = new Mock<IClientGameTiming>();
timingMock.SetupProperty(p => p.CurTick);
timingMock.SetupProperty(p => p.LastProcessedTick);
timingMock.SetupProperty(p => p.LastRealTick);
timingMock.SetupProperty(p => p.TickTimingAdjustment);
var timing = timingMock.Object;
var managerMock = new Mock<IClientGameStateManager>();
var logMock = new Mock<ISawmill>();
var processor = new GameStateProcessor(managerMock.Object, timing, logMock.Object);
processor.AddNewState(GameStateFactory(0, 1));
processor.AddNewState(GameStateFactory(1, 2));
processor.AddNewState(GameStateFactory(2, 3)); // buffer is now full, otherwise cannot calculate states.
processor.OnFullStateReceived();
timing.LastProcessedTick = timing.LastRealTick = new GameTick(1);
return (timing, processor);
}
}
}

View File

@@ -1,47 +0,0 @@
using System;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Client.Graphics;
using Robust.Shared.Maths;
namespace Robust.UnitTesting.Client.Graphics
{
[TestFixture]
[TestOf(typeof(IEyeManager))]
public sealed class EyeManagerTest : RobustIntegrationTest
{
[Test]
public async Task TestViewportRotation()
{
// At cardinal rotations with a square viewport these should all be the same.
var client = StartClient();
await client.WaitIdleAsync();
var eyeManager = client.ResolveDependency<IEyeManager>();
await client.WaitAssertion(() =>
{
// At this stage integration tests aren't pooled so no way I'm making each of these a new test for now.
foreach (var angle in new[]
{
Angle.Zero,
new Angle(Math.PI / 4),
new Angle(Math.PI / 2),
new Angle(Math.PI),
new Angle(-Math.PI / 4),
new Angle(-Math.PI / 2),
new Angle(-Math.PI)
})
{
eyeManager.CurrentEye.Rotation = angle;
var worldAABB = eyeManager.GetWorldViewport();
var worldPort = eyeManager.GetWorldViewbounds();
Assert.That(worldAABB.EqualsApprox(worldPort.CalcBoundingBox()),
$"Invalid EyeRotation bounds found for {angle}: Expected {worldAABB} and received {worldPort.CalcBoundingBox()}");
}
});
}
}
}

View File

@@ -1,37 +0,0 @@
using System.Numerics;
using NUnit.Framework;
using Robust.Client.Graphics;
using Robust.Shared.Graphics;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Robust.UnitTesting.Client.Graphics
{
[TestFixture, Parallelizable, TestOf(typeof(Eye))]
sealed class EyeTest
{
[Test]
public void Constructor_DefaultZoom_isTwo()
{
var eye = new Eye();
Assert.That(eye.Zoom, Is.Approximately(Vector2.One*2f));
}
[Test]
public void Constructor_DefaultPosition_isZero()
{
var eye = new Eye();
Assert.That(eye.Position, Is.EqualTo(MapCoordinates.Nullspace));
}
[Test]
public void Constructor_DefaultDrawFov_isTrue()
{
var eye = new Eye();
Assert.That(eye.DrawFov, Is.True);
}
}
}

View File

@@ -1,40 +0,0 @@
using System.Numerics;
using NUnit.Framework;
using Robust.Client.Graphics;
using Robust.Shared.Maths;
namespace Robust.UnitTesting.Client.Graphics
{
[TestFixture]
[Parallelizable(ParallelScope.All)]
[TestOf(typeof(StyleBox))]
public sealed class StyleBoxTest
{
[Test]
public void TestGetEnvelopBox()
{
var styleBox = new StyleBoxFlat();
Assert.That(
styleBox.GetEnvelopBox(Vector2.Zero, new Vector2(50, 50), 1),
Is.EqualTo(new UIBox2(0, 0, 50, 50)));
styleBox.ContentMarginLeftOverride = 3;
styleBox.ContentMarginTopOverride = 5;
styleBox.ContentMarginRightOverride = 7;
styleBox.ContentMarginBottomOverride = 11;
Assert.That(
styleBox.GetEnvelopBox(Vector2.Zero, new Vector2(50, 50), 1),
Is.EqualTo(new UIBox2(0, 0, 60, 66)));
Assert.That(
styleBox.GetEnvelopBox(new Vector2(10, 10), new Vector2(50, 50), 1),
Is.EqualTo(new UIBox2(10, 10, 70, 76)));
Assert.That(
styleBox.GetEnvelopBox(new Vector2(10, 10), new Vector2(50, 50), 2.0f),
Is.EqualTo(new UIBox2(10, 10, 80, 92)));
}
}
}

View File

@@ -1,87 +0,0 @@
using System.IO;
using NUnit.Framework;
using Robust.Client.Graphics;
using Robust.Shared.Graphics;
using YamlDotNet.RepresentationModel;
namespace Robust.UnitTesting.Client.Graphics
{
[TestFixture]
public sealed class TextureLoadParametersTest
{
[Test]
public void TestLoadEmptyYaml()
{
// Test whether it defaults for empty YAML.
var yaml = new YamlMappingNode();
var loaded = TextureLoadParameters.FromYaml(yaml);
var defaultParams = TextureLoadParameters.Default;
Assert.That(loaded.SampleParameters.Filter, Is.EqualTo(defaultParams.SampleParameters.Filter));
Assert.That(loaded.SampleParameters.WrapMode, Is.EqualTo(defaultParams.SampleParameters.WrapMode));
Assert.That(loaded.Srgb, Is.EqualTo(defaultParams.Srgb));
}
[Test]
public void TestLoadEmptySamplingYaml()
{
// Test whether it defaults for empty YAML.
var yaml = _getMapping("sample: {}\n");
var loaded = TextureLoadParameters.FromYaml(yaml);
var defaultParams = TextureLoadParameters.Default;
Assert.That(loaded.SampleParameters.Filter, Is.EqualTo(defaultParams.SampleParameters.Filter));
Assert.That(loaded.SampleParameters.WrapMode, Is.EqualTo(defaultParams.SampleParameters.WrapMode));
}
[Test]
public void TestLoadYamlOne()
{
var yaml = _getMapping(TestDataOne);
var loaded = TextureLoadParameters.FromYaml(yaml);
Assert.That(loaded.SampleParameters.Filter, Is.EqualTo(true));
Assert.That(loaded.SampleParameters.WrapMode, Is.EqualTo(TextureWrapMode.Repeat));
Assert.That(loaded.Srgb, Is.EqualTo(false));
}
[Test]
public void TestLoadYamlTwo()
{
var yaml = _getMapping(TestDataTwo);
var loaded = TextureLoadParameters.FromYaml(yaml);
Assert.That(loaded.SampleParameters.Filter, Is.EqualTo(false));
Assert.That(loaded.SampleParameters.WrapMode, Is.EqualTo(TextureWrapMode.MirroredRepeat));
}
[Test]
public void TestLoadYamlThree()
{
var yaml = _getMapping(TestDataThree);
var loaded = TextureLoadParameters.FromYaml(yaml);
Assert.That(loaded.SampleParameters.WrapMode, Is.EqualTo(TextureWrapMode.None));
}
private YamlMappingNode _getMapping(string data)
{
var yamlStream = new YamlStream();
yamlStream.Load(new StringReader(data));
return (YamlMappingNode) yamlStream.Documents[0].RootNode;
}
private const string TestDataOne = @"
sample:
filter: true
wrap: repeat
srgb: false
";
private const string TestDataTwo = @"
sample:
filter: false
wrap: mirrored_repeat
";
private const string TestDataThree = @"
sample:
wrap: none
";
}
}

View File

@@ -1,25 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Robust.Client.Input;
namespace Robust.UnitTesting.Client.Input
{
[Parallelizable(ParallelScope.All)]
public sealed class KeyboardTest
{
public static IEnumerable<object[]> TestCases =>
((Keyboard.Key[]) Enum.GetValues(typeof(Keyboard.Key)))
.Where(p => p.ToString().Contains("Mouse"))
.Select(p => new object[] {p, true});
[Test]
[TestCaseSource(nameof(TestCases))]
[TestCase(Keyboard.Key.A, false)]
public void TestKeyboardIsMouseKey(Keyboard.Key key, bool isMouse)
{
Assert.That(key.IsMouseKey(), Is.EqualTo(isMouse));
}
}
}

View File

@@ -1,108 +0,0 @@
using NUnit.Framework;
using Robust.Client.ResourceManagement;
namespace Robust.UnitTesting.Client.ResourceManagement
{
[TestOf(typeof(RSIResource))]
[Parallelizable(ParallelScope.All)]
[TestFixture]
public sealed class RsiResourceTest
{
/// <summary>
/// Simple test in which the delays are already identical so nothing should change much.
/// </summary>
[Test]
public void TestFoldDelaysIdentical()
{
var delays = new[]
{
new float[] {5, 5, 5, 5},
new float[] {5, 5, 5, 5},
new float[] {5, 5, 5, 5},
new float[] {5, 5, 5, 5},
};
var (newDelays, indices) = RSIResource.FoldDelays(delays);
Assert.That(newDelays, Is.EqualTo(new[] {5f, 5f, 5f, 5f}));
for (var i = 0; i < 4; i++)
{
var o = i * 4;
Assert.That(indices[i], Is.EqualTo(new[] {o, o + 1, o + 2, o + 3}));
}
}
/// <summary>
/// Test in which the delays are different but equal amount of frames.
/// </summary>
[Test]
public void TestFoldDelaysDifferent()
{
var delays = new[]
{
new float[] {5, 5, 5, 5},
new float[] {5, 7, 3, 5},
new float[] {5, 5, 5, 5},
new float[] {5, 5, 5, 5},
};
var (newDelays, indices) = RSIResource.FoldDelays(delays);
Assert.That(newDelays, Is.EqualTo(new[] {5f, 5f, 2f, 3f, 5f}));
Assert.That(indices[0], Is.EqualTo(new[] {0, 1, 2, 2, 3}));
Assert.That(indices[1], Is.EqualTo(new[] {4, 5, 5, 6, 7}));
Assert.That(indices[2], Is.EqualTo(new[] {8, 9, 10, 10, 11}));
Assert.That(indices[3], Is.EqualTo(new[] {12, 13, 14, 14, 15}));
}
/// <summary>
/// Test in which the delays are different but equal amount of frames.
/// </summary>
[Test]
public void TestFoldFramesDifferent()
{
var delays = new[]
{
new float[] {5, 5, 5, 5},
new float[] {5, 5, 5, 5},
new float[] {5, 5, 10},
new float[] {5, 5, 5, 5},
};
var (newDelays, indices) = RSIResource.FoldDelays(delays);
Assert.That(newDelays, Is.EqualTo(new[] {5f, 5f, 5f, 5f}));
Assert.That(indices[0], Is.EqualTo(new[] {0, 1, 2, 3}));
Assert.That(indices[1], Is.EqualTo(new[] {4, 5, 6, 7}));
Assert.That(indices[2], Is.EqualTo(new[] {8, 9, 10, 10}));
Assert.That(indices[3], Is.EqualTo(new[] {11, 12, 13, 14}));
}
/// <summary>
/// The very complicated test where a ton of stuff goes down.
/// </summary>
[Test]
public void TestFoldWild()
{
var delays = new[]
{
new float[] {1, 9},
new float[] {3, 3, 3, 1},
new float[] {7, 1, 2},
new float[] {5, 2, 2, 1},
};
var (newDelays, indices) = RSIResource.FoldDelays(delays);
Assert.That(newDelays, Is.EqualTo(new[] {1f, 2f, 2f, 1f, 1f, 1f, 1f, 1f}));
Assert.That(indices[0], Is.EqualTo(new[] {0, 1, 1, 1, 1, 1, 1, 1}));
Assert.That(indices[1], Is.EqualTo(new[] {2, 2, 3, 3, 4, 4, 4, 5}));
Assert.That(indices[2], Is.EqualTo(new[] {6, 6, 6, 6, 6, 7, 8, 8}));
Assert.That(indices[3], Is.EqualTo(new[] {9, 9, 9, 10, 10, 11, 11, 12}));
}
}
}

View File

@@ -1,132 +0,0 @@
using System.Numerics;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Client;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
namespace Robust.UnitTesting.Client.Sprite;
public sealed class SpriteBoundsTest : RobustIntegrationTest
{
private static readonly string Prototypes = @"
- type: entity
id: spriteBoundsTest1
components:
- type: Sprite
sprite: debugRotation.rsi
layers:
- state: direction1
- type: entity
id: spriteBoundsTest2
components:
- type: Sprite
sprite: debugRotation.rsi
layers:
- state: direction1
- state: direction1
rotation: 0.1
- type: entity
id: spriteBoundsTest3
components:
- type: Sprite
sprite: debugRotation.rsi
layers:
- state: direction1
- state: direction1
rotation: 0.1
visible: false
";
[Test]
public async Task TestSpriteBounds()
{
var client = StartClient(new ClientIntegrationOptions {ExtraPrototypes = Prototypes});
await client.WaitIdleAsync();
var baseClient = client.Resolve<IBaseClient>();
await client.WaitPost(() => baseClient.StartSinglePlayer());
await client.WaitIdleAsync();
var entMan = client.EntMan;
var sys = client.System<SpriteSystem>();
EntityUid uid1 = default;
EntityUid uid2 = default;
EntityUid uid3 = default;
await client.WaitPost(() =>
{
uid1 = entMan.Spawn("spriteBoundsTest1");
uid2 = entMan.Spawn("spriteBoundsTest2");
uid3 = entMan.Spawn("spriteBoundsTest3");
});
var ent1 = new Entity<SpriteComponent>(uid1, entMan.GetComponent<SpriteComponent>(uid1));
var ent2 = new Entity<SpriteComponent>(uid2, entMan.GetComponent<SpriteComponent>(uid2));
var ent3 = new Entity<SpriteComponent>(uid3, entMan.GetComponent<SpriteComponent>(uid3));
// None of the entities have empty bounding boxes
var box1 = sys.GetLocalBounds(ent1);
var box2 = sys.GetLocalBounds(ent2);
var box3 = sys.GetLocalBounds(ent3);
Assert.That(!box1.EqualsApprox(Box2.Empty));
Assert.That(!box2.EqualsApprox(Box2.Empty));
Assert.That(!box3.EqualsApprox(Box2.Empty));
// ent2 should have a larger bb than ent1 as it has a visible rotated layer
// ents 1 & 3 should have the same bounds, as the rotated layer is invisible in ent3
Assert.That(box1.EqualsApprox(box3));
Assert.That(!box1.EqualsApprox(box2));
Assert.That(box2.EqualsApprox(ent2.Comp.Layers[1].Bounds));
Assert.That(box2.Size.X, Is.GreaterThan(box1.Size.X));
Assert.That(box2.Size.Y, Is.GreaterThan(box1.Size.Y));
// Toggling layer visibility updates the bounds
sys.LayerSetVisible(ent2!, 1, false);
sys.LayerSetVisible(ent3!, 1, true);
var newBox2 = sys.GetLocalBounds(ent2);
var newBox3 = sys.GetLocalBounds(ent3);
Assert.That(!newBox2.EqualsApprox(Box2.Empty));
Assert.That(!newBox3.EqualsApprox(Box2.Empty));
Assert.That(box1.EqualsApprox(newBox2));
Assert.That(!box1.EqualsApprox(newBox3));
Assert.That(newBox3.EqualsApprox(ent3.Comp.Layers[1].Bounds));
Assert.That(newBox3.Size.X, Is.GreaterThan(box1.Size.X));
Assert.That(newBox3.Size.Y, Is.GreaterThan(box1.Size.Y));
// Changing the rotation, offset, scale, all trigger a bounds updatge
sys.LayerSetRotation(ent3!, 1, Angle.Zero);
var box = sys.GetLocalBounds(ent3);
Assert.That(box1.EqualsApprox(box));
// scale
sys.LayerSetScale(ent3!, 1, Vector2.One * 2);
box = sys.GetLocalBounds(ent3);
Assert.That(box1.EqualsApprox(box.Scale(0.5f)));
sys.LayerSetScale(ent3!, 1, Vector2.One);
box = sys.GetLocalBounds(ent3);
Assert.That(box1.EqualsApprox(box));
// offset
Assert.That(box.Center, Is.Approximately(Vector2.Zero));
sys.LayerSetOffset(ent3!, 1, Vector2.One);
box = sys.GetLocalBounds(ent3);
Assert.That(box.Size.X, Is.GreaterThan(box1.Size.X));
Assert.That(box.Size.Y, Is.GreaterThan(box1.Size.Y));
Assert.That(box.Center.X, Is.GreaterThan(0));
Assert.That(box.Center.Y, Is.GreaterThan(0));
await client.WaitPost(() =>
{
entMan.DeleteEntity(uid1);
entMan.DeleteEntity(uid2);
entMan.DeleteEntity(uid3);
});
}
}

View File

@@ -1,36 +0,0 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using NUnit.Framework;
using Robust.Client.Graphics.Clyde;
using Robust.Shared.Maths;
namespace Robust.UnitTesting.Client.Sprite;
[TestFixture]
[TestOf(typeof(Clyde))]
public sealed class TransformCenteredBoxTest
{
private static IEnumerable<(Box2 box, float angle, Vector2 offset, Vector2 scale)> _args =
[
(Box2.UnitCentered, 0f, new Vector2(0f,0f), new Vector2(1f,-1f)),
(Box2.UnitCentered, MathF.PI / 7, new Vector2(0f, 0f), new Vector2(1f, -1f)),
(Box2.UnitCentered, MathF.PI / 7, new Vector2(-1f, 2.3f), new Vector2(1f, -1f)),
(Box2.UnitCentered, MathF.PI / 7, new Vector2(-1f, 2.3f), new Vector2(1.25f, -0.32f)),
(new Box2(1,2,3,4), MathF.PI / 7, new Vector2(-1f, 2.3f), new Vector2(1.25f, -0.32f)),
];
[Test]
public void TestTransformCenteredBox([ValueSource(nameof(_args))]
(Box2 box, float angle, Vector2 offset, Vector2 scale) args)
{
var result = Clyde.TransformCenteredBox(args.box, args.angle, args.offset, args.scale);
var expected = Matrix3x2.CreateRotation(args.angle).TransformBox(args.box).Translated(args.offset);
expected = new(
expected.Left * args.scale.X,
expected.Top * args.scale.Y,
expected.Right * args.scale.X,
expected.Bottom * args.scale.Y);
Assert.That(result, Is.Approximately(expected));
}
}

View File

@@ -1,199 +0,0 @@
using System;
using NUnit.Framework;
using Robust.Client.Animations;
using Robust.Client.UserInterface;
using Robust.Shared.Animations;
using Robust.Shared.IoC;
using Robust.Shared.Timing;
namespace Robust.UnitTesting.Client.UserInterface
{
[TestFixture]
[TestOf(typeof(Control))]
public sealed class ControlTest : RobustUnitTest
{
private static readonly AttachedProperty _refTypeAttachedProperty
= AttachedProperty.Create("_refType", typeof(ControlTest), typeof(string), "foo", v => (string?) v != "bar");
private static readonly AttachedProperty _valueTypeAttachedProperty
= AttachedProperty.Create("_valueType", typeof(ControlTest), typeof(float));
private static readonly AttachedProperty _nullableAttachedProperty
= AttachedProperty.Create("_nullable", typeof(ControlTest), typeof(float?));
private static readonly AttachedProperty<int> _genericProperty =
AttachedProperty<int>.Create("generic", typeof(ControlTest), 5, i => i % 2 == 1);
public override UnitTestProject Project => UnitTestProject.Client;
[OneTimeSetUp]
public void Setup()
{
IoCManager.Resolve<IUserInterfaceManagerInternal>().InitializeTesting();
}
/// <summary>
/// Test that you can't parent a control to its (grand)child.
/// </summary>
[Test]
public void TestNoRecursion()
{
var control1 = new Control();
var control2 = new Control();
var control3 = new Control();
control1.AddChild(control2);
// Test direct parent/child.
Assert.That(() => control2.AddChild(control1), Throws.ArgumentException);
control2.AddChild(control3);
// Test grand child.
Assert.That(() => control3.AddChild(control1), Throws.ArgumentException);
}
[Test]
public void TestVisibleInTree()
{
var control1 = new Control();
// Not visible because not parented to root control.
Assert.That(control1.Visible, Is.True);
Assert.That(control1.VisibleInTree, Is.False);
control1.UserInterfaceManager.RootControl.AddChild(control1);
Assert.That(control1.Visible, Is.True);
Assert.That(control1.VisibleInTree, Is.True);
control1.Visible = false;
Assert.That(control1.Visible, Is.False);
Assert.That(control1.VisibleInTree, Is.False);
control1.Visible = true;
var control2 = new Control();
Assert.That(control2.VisibleInTree, Is.False);
control1.AddChild(control2);
Assert.That(control2.VisibleInTree, Is.True);
control1.Visible = false;
Assert.That(control2.VisibleInTree, Is.False);
control2.Visible = false;
Assert.That(control2.VisibleInTree, Is.False);
control1.Visible = true;
Assert.That(control2.VisibleInTree, Is.False);
control1.Orphan();
}
[Test]
public void TestAttachedPropertiesBasic()
{
var control = new Control();
control.SetValue(_refTypeAttachedProperty, "honk");
Assert.That(control.GetValue(_refTypeAttachedProperty), Is.EqualTo("honk"));
}
[Test]
public void TestAttachedPropertiesValidate()
{
var control = new Control();
Assert.Throws<ArgumentException>(() => control.SetValue(_refTypeAttachedProperty, "bar"));
}
[Test]
public void TestAttachedPropertiesInvalidType()
{
var control = new Control();
Assert.Throws<ArgumentException>(() => control.SetValue(_refTypeAttachedProperty, new object()));
Assert.Throws<ArgumentException>(() => control.SetValue(_valueTypeAttachedProperty, new object()));
}
[Test]
public void TestAttachedPropertiesInvalidNull()
{
var control = new Control();
Assert.Throws<ArgumentNullException>(() => control.SetValue(_valueTypeAttachedProperty, null));
}
[Test]
public void TestAttachedPropertiesValidNull()
{
var control = new Control();
control.SetValue(_nullableAttachedProperty, null);
}
[Test]
public void TestAttachedPropertiesGeneric()
{
var control = new Control();
Assert.That(control.GetValue(_genericProperty), Is.EqualTo(5));
control.SetValue(_genericProperty, 11);
Assert.That(control.GetValue(_genericProperty), Is.EqualTo(11));
Assert.That(() => control.SetValue(_genericProperty, 10), Throws.ArgumentException);
}
[Test]
public void TestAnimations()
{
var control = new TestControl();
var animation = new Animation
{
Length = TimeSpan.FromSeconds(3),
AnimationTracks =
{
new AnimationTrackControlProperty
{
Property = nameof(TestControl.Foo),
KeyFrames =
{
new AnimationTrackProperty.KeyFrame(1f, 1f),
new AnimationTrackProperty.KeyFrame(3f, 2f)
}
}
}
};
control.PlayAnimation(animation, "foo");
control.DoFrameUpdateRecursive(new FrameEventArgs(0.5f));
Assert.That(control.Foo, new ApproxEqualityConstraint(0f)); // Should still be 0.
control.DoFrameUpdateRecursive(new FrameEventArgs(0.5001f));
Assert.That(control.Foo, new ApproxEqualityConstraint(1f, 0.01)); // Should now be 1.
control.DoFrameUpdateRecursive(new FrameEventArgs(0.5f));
Assert.That(control.Foo, new ApproxEqualityConstraint(1.5f, 0.01)); // Should now be 1.5.
control.DoFrameUpdateRecursive(new FrameEventArgs(1.0f));
Assert.That(control.Foo, new ApproxEqualityConstraint(2.5f, 0.01)); // Should now be 2.5.
control.DoFrameUpdateRecursive(new FrameEventArgs(0.5f));
Assert.That(control.Foo, new ApproxEqualityConstraint(3f, 0.01)); // Should now be 3.
control.DoFrameUpdateRecursive(new FrameEventArgs(0.5f));
Assert.That(control.Foo, new ApproxEqualityConstraint(3f, 0.01)); // Should STILL be 3.
}
private sealed class TestControl : Control
{
[Animatable] public float Foo { get; set; }
}
}
}

View File

@@ -1,96 +0,0 @@
using System.Numerics;
using NUnit.Framework;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Robust.UnitTesting.Client.UserInterface.Controls;
[TestFixture]
public sealed class BaseButtonTest : RobustUnitTest
{
public override UnitTestProject Project => UnitTestProject.Client;
private string UIRightClick = "UIRightClick";
private string OpenContextMenu = "UseSecondary";
private sealed class TestButton : BaseButton { }
[OneTimeSetUp]
public void Setup()
{
IoCManager.Resolve<IUserInterfaceManagerInternal>().InitializeTesting();
}
[Test]
public void TestToggleButtonToggle()
{
var button = new TestButton
{
ToggleMode = true
};
var toggled = false;
button.OnToggled += _ =>
{
toggled = true;
};
var click = new GUIBoundKeyEventArgs(EngineKeyFunctions.UIClick, BoundKeyState.Down, new ScreenCoordinates(), true, Vector2.Zero, Vector2.Zero);
button.KeyBindDown(click);
button.KeyBindUp(click);
Assert.That(button.Pressed, Is.EqualTo(true));
Assert.That(toggled, Is.EqualTo(true));
toggled = false;
button.KeyBindDown(click);
button.KeyBindUp(click);
Assert.That(button.Pressed, Is.EqualTo(false));
Assert.That(toggled, Is.EqualTo(true));
}
[Test]
public void TestToggleButtonAllKeybinds()
{
var button = new TestButton
{
EnableAllKeybinds = true,
ToggleMode = true
};
var uiRightClickPressed = false;
var openContextMenuPressed = false;
var toggled = false;
button.OnPressed += args =>
{
if (args.Event.Function == UIRightClick)
uiRightClickPressed = true;
else if (args.Event.Function == OpenContextMenu)
openContextMenuPressed = true;
};
button.OnToggled += _ =>
{
toggled = true;
};
var uiRightClick = new GUIBoundKeyEventArgs(UIRightClick, BoundKeyState.Down, new ScreenCoordinates(), true, Vector2.Zero, Vector2.Zero);
var openContextMenu = new GUIBoundKeyEventArgs(OpenContextMenu, BoundKeyState.Down, new ScreenCoordinates(), true, Vector2.Zero, Vector2.Zero);
button.KeyBindDown(uiRightClick);
button.KeyBindDown(openContextMenu);
Assert.That(button.Pressed, Is.EqualTo(false));
Assert.That(toggled, Is.EqualTo(false));
button.KeyBindUp(uiRightClick);
Assert.That(button.Pressed, Is.EqualTo(false));
Assert.That(toggled, Is.EqualTo(false));
button.KeyBindUp(openContextMenu);
Assert.That(uiRightClickPressed, Is.EqualTo(true));
Assert.That(openContextMenuPressed, Is.EqualTo(true));
Assert.That(button.Pressed, Is.EqualTo(false));
Assert.That(toggled, Is.EqualTo(false));
}
}

View File

@@ -1,193 +0,0 @@
using System.Numerics;
using NUnit.Framework;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Maths;
using static Robust.Client.UserInterface.Controls.BoxContainer;
namespace Robust.UnitTesting.Client.UserInterface.Controls
{
[TestFixture]
[TestOf(typeof(BoxContainer))]
public sealed class BoxContainerTest : RobustUnitTest
{
public override UnitTestProject Project => UnitTestProject.Client;
[Test]
public void TestLayoutBasic()
{
var root = new LayoutContainer();
var boxContainer = new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
MinSize = new Vector2(50, 60)
};
var control1 = new Control {MinSize = new Vector2(20, 20)};
var control2 = new Control {MinSize = new Vector2(30, 30)};
root.AddChild(boxContainer);
boxContainer.AddChild(control1);
boxContainer.AddChild(control2);
root.Arrange(new UIBox2(0, 0, 50, 60));
Assert.That(control1.Position, Is.EqualTo(Vector2.Zero));
Assert.That(control1.Size, Is.EqualTo(new Vector2(50, 20)));
Assert.That(control2.Position, Is.EqualTo(new Vector2(0, 20)));
Assert.That(control2.Size, Is.EqualTo(new Vector2(50, 30)));
Assert.That(boxContainer.DesiredSize, Is.EqualTo(new Vector2(50, 60)));
}
[Test]
public void TestLayoutExpand()
{
var root = new LayoutContainer();
var boxContainer = new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
MinSize = new Vector2(50, 60)
};
var control1 = new Control
{
VerticalExpand = true
};
var control2 = new Control {MinSize = new Vector2(30, 30)};
boxContainer.AddChild(control1);
boxContainer.AddChild(control2);
root.AddChild(boxContainer);
root.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(control1.Position, Is.EqualTo(Vector2.Zero));
Assert.That(control1.Size, Is.EqualTo(new Vector2(50, 30)));
Assert.That(control2.Position, Is.EqualTo(new Vector2(0, 30)));
Assert.That(control2.Size, Is.EqualTo(new Vector2(50, 30)));
Assert.That(boxContainer.DesiredSize, Is.EqualTo(new Vector2(50, 60)));
}
[Test]
public void TestCalcMinSize()
{
var boxContainer = new BoxContainer
{
Orientation = LayoutOrientation.Vertical
};
var control1 = new Control
{
MinSize = new Vector2(50, 30)
};
var control2 = new Control {MinSize = new Vector2(30, 50)};
boxContainer.AddChild(control1);
boxContainer.AddChild(control2);
boxContainer.Measure(new Vector2(100, 100));
Assert.That(boxContainer.DesiredSize, Is.EqualTo(new Vector2(50, 80)));
}
[Test]
public void TestTwoExpand()
{
var root = new LayoutContainer();
var boxContainer = new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
MinSize = new Vector2(30, 80)
};
var control1 = new Control
{
VerticalExpand = true,
};
var control2 = new Control
{
VerticalExpand = true,
};
var control3 = new Control {MinSize = new Vector2(0, 50)};
root.AddChild(boxContainer);
boxContainer.AddChild(control1);
boxContainer.AddChild(control3);
boxContainer.AddChild(control2);
root.Arrange(new UIBox2(0, 0, 250, 250));
Assert.That(control1.Position, Is.EqualTo(Vector2.Zero));
Assert.That(control1.Size, Is.EqualTo(new Vector2(30, 15)));
Assert.That(control3.Position, Is.EqualTo(new Vector2(0, 15)));
Assert.That(control3.Size, Is.EqualTo(new Vector2(30, 50)));
Assert.That(control2.Position, Is.EqualTo(new Vector2(0, 65)));
Assert.That(control2.Size, Is.EqualTo(new Vector2(30, 15)));
}
[Test]
public void TestTwoExpandRatio()
{
var boxContainer = new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
SetSize = new Vector2(100, 10),
Children =
{
new Control
{
MinWidth = 10,
HorizontalExpand = true,
SizeFlagsStretchRatio = 20,
},
new Control
{
MinWidth = 10,
HorizontalExpand = true,
SizeFlagsStretchRatio = 80
}
}
};
boxContainer.Arrange(UIBox2.FromDimensions(Vector2.Zero, boxContainer.SetSize));
Assert.Multiple(() =>
{
Assert.That(boxContainer.GetChild(0).Width, Is.EqualTo(20));
Assert.That(boxContainer.GetChild(1).Width, Is.EqualTo(80));
});
}
[Test]
public void TestTwoExpandOneSmall()
{
var boxContainer = new BoxContainer
{
Orientation = LayoutOrientation.Horizontal,
SetSize = new Vector2(100, 10),
Children =
{
new Control
{
MinWidth = 30,
HorizontalExpand = true,
SizeFlagsStretchRatio = 20,
},
new Control
{
MinWidth = 30,
HorizontalExpand = true,
SizeFlagsStretchRatio = 80
}
}
};
boxContainer.Arrange(UIBox2.FromDimensions(Vector2.Zero, boxContainer.SetSize));
Assert.Multiple(() =>
{
Assert.That(boxContainer.GetChild(0).Width, Is.EqualTo(30));
Assert.That(boxContainer.GetChild(1).Width, Is.EqualTo(70));
});
}
}
}

View File

@@ -1,30 +0,0 @@
using System.Numerics;
using NUnit.Framework;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Maths;
namespace Robust.UnitTesting.Client.UserInterface.Controls
{
[TestFixture]
[TestOf(typeof(CenterContainer))]
public sealed class CenterContainerTest : RobustUnitTest
{
public override UnitTestProject Project => UnitTestProject.Client;
[Test]
public void Test()
{
var container = new CenterContainer();
var child = new Control {MinSize = new Vector2(50, 50)};
container.AddChild(child);
container.Arrange(UIBox2.FromDimensions(0, 0, 100, 100));
Assert.That(container.DesiredSize, Is.EqualTo(new Vector2(50, 50)));
Assert.That(child.Position, Is.EqualTo(new Vector2(25, 25)));
Assert.That(child.Size, Is.EqualTo(new Vector2(50, 50)));
}
}
}

View File

@@ -1,324 +0,0 @@
using System.Numerics;
using NUnit.Framework;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Maths;
namespace Robust.UnitTesting.Client.UserInterface.Controls
{
[TestFixture]
[TestOf(typeof(GridContainer))]
public sealed class GridContainerTest : RobustUnitTest
{
public override UnitTestProject Project => UnitTestProject.Client;
[TestCase(true)]
[TestCase(false)]
public void TestBasic(bool limitByCount)
{
var grid = limitByCount ? new GridContainer {Columns = 2} : new GridContainer {MaxGridWidth = 125};
var child1 = new Control {MinSize = new Vector2(50, 50)};
var child2 = new Control {MinSize = new Vector2(50, 50)};
var child3 = new Control {MinSize = new Vector2(50, 50)};
var child4 = new Control {MinSize = new Vector2(50, 50)};
var child5 = new Control {MinSize = new Vector2(50, 50)};
grid.AddChild(child1);
grid.AddChild(child2);
grid.AddChild(child3);
grid.AddChild(child4);
grid.AddChild(child5);
grid.Arrange(new UIBox2(0, 0, 250, 250));
Assert.That(grid.DesiredSize, Is.EqualTo(new Vector2(104, 158)));
Assert.That(child1.Position, Is.EqualTo(Vector2.Zero));
Assert.That(child2.Position, Is.EqualTo(new Vector2(54, 0)));
Assert.That(child3.Position, Is.EqualTo(new Vector2(0, 54)));
Assert.That(child4.Position, Is.EqualTo(new Vector2(54, 54)));
Assert.That(child5.Position, Is.EqualTo(new Vector2(0, 108)));
}
[TestCase(true)]
[TestCase(false)]
public void TestBasicRows(bool limitByCount)
{
var grid = limitByCount
? new GridContainer {Rows = 2}
: new GridContainer {MaxGridHeight = 125};
var child1 = new Control {MinSize = new Vector2(50, 50)};
var child2 = new Control {MinSize = new Vector2(50, 50)};
var child3 = new Control {MinSize = new Vector2(50, 50)};
var child4 = new Control {MinSize = new Vector2(50, 50)};
var child5 = new Control {MinSize = new Vector2(50, 50)};
grid.AddChild(child1);
grid.AddChild(child2);
grid.AddChild(child3);
grid.AddChild(child4);
grid.AddChild(child5);
grid.Arrange(new UIBox2(0, 0, 250, 250));
Assert.That(grid.DesiredSize, Is.EqualTo(new Vector2(158, 104)));
Assert.That(child1.Position, Is.EqualTo(Vector2.Zero));
Assert.That(child2.Position, Is.EqualTo(new Vector2(0, 54)));
Assert.That(child3.Position, Is.EqualTo(new Vector2(54, 0)));
Assert.That(child4.Position, Is.EqualTo(new Vector2(54, 54)));
Assert.That(child5.Position, Is.EqualTo(new Vector2(108, 0)));
}
[Test]
public void TestUnevenLimitSize()
{
// when uneven sizes are used and limiting by size, they should all be treated as equal size cells based on the
// max minwidth / minheight among them.
// Note that when limiting by count, the behavior is different - rows and columns are individually
// expanded based on the max size of their elements
var grid = new GridContainer {MaxGridWidth = 125};
var child1 = new Control {MinSize = new Vector2(12, 24)};
var child2 = new Control {MinSize = new Vector2(30, 50)};
var child3 = new Control {MinSize = new Vector2(40, 20)};
var child4 = new Control {MinSize = new Vector2(20, 12)};
var child5 = new Control {MinSize = new Vector2(50, 10)};
grid.AddChild(child1);
grid.AddChild(child2);
grid.AddChild(child3);
grid.AddChild(child4);
grid.AddChild(child5);
grid.Arrange(new UIBox2(0, 0, 250, 250));
Assert.That(grid.DesiredSize, Is.EqualTo(new Vector2(104, 158)));
Assert.That(child1.Position, Is.EqualTo(Vector2.Zero));
Assert.That(child2.Position, Is.EqualTo(new Vector2(54, 0)));
Assert.That(child3.Position, Is.EqualTo(new Vector2(0, 54)));
Assert.That(child4.Position, Is.EqualTo(new Vector2(54, 54)));
Assert.That(child5.Position, Is.EqualTo(new Vector2(0, 108)));
}
[Test]
public void TestUnevenLimitSizeRows()
{
var grid = new GridContainer {MaxGridHeight = 125};
var child1 = new Control {MinSize = new Vector2(12, 2)};
var child2 = new Control {MinSize = new Vector2(5, 23)};
var child3 = new Control {MinSize = new Vector2(42, 4)};
var child4 = new Control {MinSize = new Vector2(2, 50)};
var child5 = new Control {MinSize = new Vector2(50, 34)};
grid.AddChild(child1);
grid.AddChild(child2);
grid.AddChild(child3);
grid.AddChild(child4);
grid.AddChild(child5);
grid.Arrange(new UIBox2(0, 0, 250, 250));
Assert.That(grid.DesiredSize, Is.EqualTo(new Vector2(158, 104)));
Assert.That(child1.Position, Is.EqualTo(Vector2.Zero));
Assert.That(child2.Position, Is.EqualTo(new Vector2(0, 54)));
Assert.That(child3.Position, Is.EqualTo(new Vector2(54, 0)));
Assert.That(child4.Position, Is.EqualTo(new Vector2(54, 54)));
Assert.That(child5.Position, Is.EqualTo(new Vector2(108, 0)));
}
[TestCase(true)]
[TestCase(false)]
public void TestBasicBackwards(bool limitByCount)
{
var grid = limitByCount
? new GridContainer {Columns = 2, ExpandBackwards = true}
: new GridContainer {MaxGridWidth = 125, ExpandBackwards = true};
var child1 = new Control {MinSize = new Vector2(50, 50)};
var child2 = new Control {MinSize = new Vector2(50, 50)};
var child3 = new Control {MinSize = new Vector2(50, 50)};
var child4 = new Control {MinSize = new Vector2(50, 50)};
var child5 = new Control {MinSize = new Vector2(50, 50)};
grid.AddChild(child1);
grid.AddChild(child2);
grid.AddChild(child3);
grid.AddChild(child4);
grid.AddChild(child5);
grid.Arrange(new UIBox2(0, 0, 250, 250));
Assert.That(grid.DesiredSize, Is.EqualTo(new Vector2(104, 158)));
Assert.That(child1.Position, Is.EqualTo(new Vector2(0, 108)));
Assert.That(child2.Position, Is.EqualTo(new Vector2(54, 108)));
Assert.That(child3.Position, Is.EqualTo(new Vector2(0, 54)));
Assert.That(child4.Position, Is.EqualTo(new Vector2(54, 54)));
Assert.That(child5.Position, Is.EqualTo(Vector2.Zero));
}
[TestCase(true)]
[TestCase(false)]
public void TestBasicRowsBackwards(bool limitByCount)
{
var grid = limitByCount
? new GridContainer {Rows = 2, ExpandBackwards = true}
: new GridContainer {MaxGridHeight = 125, ExpandBackwards = true};
var child1 = new Control {MinSize = new Vector2(50, 50)};
var child2 = new Control {MinSize = new Vector2(50, 50)};
var child3 = new Control {MinSize = new Vector2(50, 50)};
var child4 = new Control {MinSize = new Vector2(50, 50)};
var child5 = new Control {MinSize = new Vector2(50, 50)};
grid.AddChild(child1);
grid.AddChild(child2);
grid.AddChild(child3);
grid.AddChild(child4);
grid.AddChild(child5);
grid.Arrange(new UIBox2(0, 0, 250, 250));
Assert.That(grid.DesiredSize, Is.EqualTo(new Vector2(158, 104)));
Assert.That(child1.Position, Is.EqualTo(new Vector2(108, 0)));
Assert.That(child2.Position, Is.EqualTo(new Vector2(108, 54)));
Assert.That(child3.Position, Is.EqualTo(new Vector2(54, 0)));
Assert.That(child4.Position, Is.EqualTo(new Vector2(54, 54)));
Assert.That(child5.Position, Is.EqualTo(Vector2.Zero));
}
[TestCase(true)]
[TestCase(false)]
public void TestExpand(bool limitByCount)
{
// in the presence of a MaxWidth with expanding elements, the
// pre-expanded size should be used to determine the size of each "cell", and then expansion
// happens within the defined control size
var grid = limitByCount
? new GridContainer {Columns = 2, SetSize = new Vector2(200, 200)}
: new GridContainer {MaxGridWidth = 125, SetSize = new Vector2(200, 200)};
var child1 = new Control {MinSize = new Vector2(50, 50), HorizontalExpand = true};
var child2 = new Control {MinSize = new Vector2(50, 50)};
var child3 = new Control {MinSize = new Vector2(50, 50)};
var child4 = new Control {MinSize = new Vector2(50, 50), VerticalExpand = true};
var child5 = new Control {MinSize = new Vector2(50, 50)};
grid.AddChild(child1);
grid.AddChild(child2);
grid.AddChild(child3);
grid.AddChild(child4);
grid.AddChild(child5);
grid.Arrange(new UIBox2(0, 0, 250, 250));
Assert.That(child1.Position, Is.EqualTo(Vector2.Zero));
Assert.That(child1.Size, Is.EqualTo(new Vector2(146, 50)));
Assert.That(child2.Position, Is.EqualTo(new Vector2(150, 0)));
Assert.That(child2.Size, Is.EqualTo(new Vector2(50, 50)));
Assert.That(child3.Position, Is.EqualTo(new Vector2(0, 54)));
Assert.That(child3.Size, Is.EqualTo(new Vector2(146, 92)));
Assert.That(child4.Position, Is.EqualTo(new Vector2(150, 54)));
Assert.That(child4.Size, Is.EqualTo(new Vector2(50, 92)));
Assert.That(child5.Position, Is.EqualTo(new Vector2(0, 150)));
Assert.That(child5.Size, Is.EqualTo(new Vector2(146, 50)));
}
[TestCase(true)]
[TestCase(false)]
public void TestExpandRows(bool limitByCount)
{
var grid = limitByCount
? new GridContainer {Rows = 2, SetSize = new Vector2(200, 200)}
: new GridContainer {MaxGridHeight = 125, SetSize = new Vector2(200, 200)};
var child1 = new Control {MinSize = new Vector2(50, 50), VerticalExpand = true};
var child2 = new Control {MinSize = new Vector2(50, 50)};
var child3 = new Control {MinSize = new Vector2(50, 50)};
var child4 = new Control {MinSize = new Vector2(50, 50), HorizontalExpand = true};
var child5 = new Control {MinSize = new Vector2(50, 50)};
grid.AddChild(child1);
grid.AddChild(child2);
grid.AddChild(child3);
grid.AddChild(child4);
grid.AddChild(child5);
grid.Arrange(new UIBox2(0, 0, 250, 250));
Assert.That(child1.Position, Is.EqualTo(Vector2.Zero));
Assert.That(child1.Size, Is.EqualTo(new Vector2(50, 146)));
Assert.That(child2.Position, Is.EqualTo(new Vector2(0, 150)));
Assert.That(child2.Size, Is.EqualTo(new Vector2(50, 50)));
Assert.That(child3.Position, Is.EqualTo(new Vector2(54, 0)));
Assert.That(child3.Size, Is.EqualTo(new Vector2(92, 146)));
Assert.That(child4.Position, Is.EqualTo(new Vector2(54, 150)));
Assert.That(child4.Size, Is.EqualTo(new Vector2(92, 50)));
Assert.That(child5.Position, Is.EqualTo(new Vector2(150, 0)));
Assert.That(child5.Size, Is.EqualTo(new Vector2(50, 146)));
}
[TestCase(true)]
[TestCase(false)]
public void TestRowCount(bool limitByCount)
{
var grid = limitByCount
? new GridContainer {Columns = 2}
: new GridContainer {MaxGridWidth = 125};
var child1 = new Control {MinSize = new Vector2(50, 50)};
var child2 = new Control {MinSize = new Vector2(50, 50)};
var child3 = new Control {MinSize = new Vector2(50, 50)};
var child4 = new Control {MinSize = new Vector2(50, 50)};
var child5 = new Control {MinSize = new Vector2(50, 50)};
grid.AddChild(child1);
grid.AddChild(child2);
grid.AddChild(child3);
grid.AddChild(child4);
grid.AddChild(child5);
grid.Measure(new Vector2(250, 250));
Assert.That(grid.Rows, Is.EqualTo(3));
grid.RemoveChild(child5);
Assert.That(grid.Rows, Is.EqualTo(2));
grid.DisposeAllChildren();
Assert.That(grid.Rows, Is.EqualTo(1));
}
[TestCase(true)]
[TestCase(false)]
public void TestColumnCountRows(bool limitByCount)
{
var grid = limitByCount
? new GridContainer {Rows = 2}
: new GridContainer {MaxGridHeight = 125};
var child1 = new Control {MinSize = new Vector2(50, 50)};
var child2 = new Control {MinSize = new Vector2(50, 50)};
var child3 = new Control {MinSize = new Vector2(50, 50)};
var child4 = new Control {MinSize = new Vector2(50, 50)};
var child5 = new Control {MinSize = new Vector2(50, 50)};
grid.AddChild(child1);
grid.AddChild(child2);
grid.AddChild(child3);
grid.AddChild(child4);
grid.AddChild(child5);
grid.Measure(new Vector2(250, 250));
Assert.That(grid.Columns, Is.EqualTo(3));
grid.RemoveChild(child5);
Assert.That(grid.Columns, Is.EqualTo(2));
grid.DisposeAllChildren();
Assert.That(grid.Columns, Is.EqualTo(1));
}
}
}

View File

@@ -1,206 +0,0 @@
using System.Numerics;
using NUnit.Framework;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Maths;
namespace Robust.UnitTesting.Client.UserInterface.Controls
{
[TestFixture]
[TestOf(typeof(LayoutContainer))]
public sealed class LayoutContainerTest : RobustUnitTest
{
public override UnitTestProject Project => UnitTestProject.Client;
[Test]
public void TestMarginLayoutBasic()
{
var control = new LayoutContainer {Size = new Vector2(100, 100)};
var child = new Control();
LayoutContainer.SetMarginRight(child, 5);
LayoutContainer.SetMarginBottom(child, 5);
control.AddChild(child);
control.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(child.Size, Is.EqualTo(new Vector2(5, 5)));
Assert.That(child.Position, Is.EqualTo(Vector2.Zero));
LayoutContainer.SetMarginTop(child, 3);
LayoutContainer.SetMarginLeft(child, 3);
control.InvalidateArrange();
control.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(child.Size, Is.EqualTo(new Vector2(2, 2)));
Assert.That(child.Position, Is.EqualTo(new Vector2(3, 3)));
}
[Test]
public void TestAnchorLayoutBasic()
{
var control = new LayoutContainer {Size = new Vector2(100, 100)};
var child = new Control();
LayoutContainer.SetAnchorRight(child, 1);
LayoutContainer.SetAnchorBottom(child, 1);
control.AddChild(child);
control.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(child.Size, Is.EqualTo(new Vector2(100, 100)));
Assert.That(child.Position, Is.EqualTo(Vector2.Zero));
LayoutContainer.SetAnchorLeft(child, 0.5f);
control.InvalidateArrange();
control.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(child.Position, Is.EqualTo(new Vector2(50, 0)));
Assert.That(child.Size, Is.EqualTo(new Vector2(50, 100)));
LayoutContainer.SetAnchorTop(child, 0.5f);
control.InvalidateArrange();
control.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(child.Position, Is.EqualTo(new Vector2(50, 50)));
Assert.That(child.Size, Is.EqualTo(new Vector2(50, 50)));
}
[Test]
public void TestMarginLayoutMinimumSize()
{
var control = new LayoutContainer {Size = new Vector2(100, 100)};
var child = new Control
{
MinSize = new Vector2(50, 50),
};
LayoutContainer.SetMarginRight(child, 20);
LayoutContainer.SetMarginBottom(child, 20);
control.AddChild(child);
control.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(child.Size, Is.EqualTo(new Vector2(50, 50)));
}
[Test]
public void TestMarginAnchorLayout()
{
var control = new LayoutContainer {Size = new Vector2(100, 100)};
var child = new Control();
LayoutContainer.SetMarginRight(child, -10);
LayoutContainer.SetMarginBottom(child, -10);
LayoutContainer.SetMarginTop(child, 10);
LayoutContainer.SetMarginLeft(child, 10);
LayoutContainer.SetAnchorRight(child, 1);
LayoutContainer.SetAnchorBottom(child, 1);
control.AddChild(child);
control.InvalidateArrange();
control.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(child.Position, Is.EqualTo(new Vector2(10, 10)));
Assert.That(child.Size, Is.EqualTo(new Vector2(80, 80)));
}
// Test that a control grows its size instead of position by default. (GrowDirection.End)
[Test]
public void TestGrowEnd()
{
var parent = new LayoutContainer();
var child = new Control {SetSize = new Vector2(100, 100)};
LayoutContainer.SetAnchorRight(child, 1);
parent.AddChild(child);
parent.Arrange(new UIBox2(0, 0, 100, 100));
// Child should be at 0,0.
Assert.That(child.Position, Is.EqualTo(Vector2.Zero));
// Right margin should make the child not have enough space and grow left.
LayoutContainer.SetMarginRight(child, -100);
parent.InvalidateArrange();
parent.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(child.Position, Is.EqualTo(Vector2.Zero));
Assert.That(child.Size, Is.EqualTo(new Vector2(100, 100)));
}
// Test GrowDirection.Begin
[Test]
public void TestGrowBegin()
{
var parent = new LayoutContainer();
var child = new Control {SetSize = new Vector2(100, 100)};
LayoutContainer.SetGrowHorizontal(child, LayoutContainer.GrowDirection.Begin);
LayoutContainer.SetAnchorRight(child, 1);
parent.AddChild(child);
parent.Arrange(new UIBox2(0, 0, 100, 100));
// Child should be at 0,0.
Assert.That(child.Position, Is.EqualTo(Vector2.Zero));
// Right margin should make the child not have enough space and grow left.
LayoutContainer.SetMarginRight(child, -100);
parent.InvalidateArrange();
parent.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(child.Position, Is.EqualTo(new Vector2(-100, 0)));
Assert.That(child.Size, Is.EqualTo(new Vector2(100, 100)));
}
// Test GrowDirection.Both
[Test]
public void TestGrowBoth()
{
var parent = new LayoutContainer {MinSize = new Vector2(100, 100)};
var child = new Control {SetSize = new Vector2(100, 100)};
LayoutContainer.SetGrowHorizontal(child, LayoutContainer.GrowDirection.Both);
LayoutContainer.SetAnchorRight(child, 1);
parent.AddChild(child);
parent.Arrange(new UIBox2(0, 0, 100, 100));
// Child should be at 0,0.
Assert.That(child.Position, Is.EqualTo(Vector2.Zero));
// Right margin should make the child not have enough space and grow left.
LayoutContainer.SetMarginRight(child, -100);
parent.InvalidateArrange();
parent.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(child.Position, Is.EqualTo(new Vector2(-50, 0)));
Assert.That(child.Size, Is.EqualTo(new Vector2(100, 100)));
}
// Test that changing a grow direction updates the position correctly.
[Test]
public void TestGrowDirectionChange()
{
var parent = new LayoutContainer {MinSize = new Vector2(100, 100)};
var child = new Control();
parent.AddChild(child);
parent.Arrange(new UIBox2(0, 0, 100, 100));
// Child should be at -100,0.
Assert.That(child.Position, Is.EqualTo(new Vector2(0, 0)));
child.MinSize = new Vector2(100, 100);
parent.InvalidateMeasure();
parent.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(child.Position, Is.EqualTo(new Vector2(0, 0)));
Assert.That(child.Size, Is.EqualTo(new Vector2(100, 100)));
LayoutContainer.SetGrowHorizontal(child, LayoutContainer.GrowDirection.Begin);
parent.InvalidateArrange();
parent.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(child.Position, Is.EqualTo(new Vector2(-100, 0)));
}
}
}

View File

@@ -1,174 +0,0 @@
using NUnit.Framework;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input;
using Robust.UnitTesting.Server;
namespace Robust.UnitTesting.Client.UserInterface.Controls
{
[TestFixture]
[TestOf(typeof(LineEdit))]
public sealed class LineEditTest : RobustUnitTest
{
public override UnitTestProject Project => UnitTestProject.Client;
[Test]
public void TestRuneBackspace()
{
var lineEdit = new TestLineEdit();
lineEdit.Text = "Foo👏";
lineEdit.CursorPosition = lineEdit.Text.Length;
var eventArgs = new GUIBoundKeyEventArgs(
EngineKeyFunctions.TextBackspace,
BoundKeyState.Down,
default, false, default, default);
lineEdit.KeyBindDown(eventArgs);
Assert.That(lineEdit.Text, Is.EqualTo("Foo"));
Assert.That(lineEdit.CursorPosition, Is.EqualTo(3));
}
[Test]
public void TestRuneDelete()
{
var lineEdit = new TestLineEdit();
lineEdit.Text = "Foo👏";
lineEdit.CursorPosition = 3;
var eventArgs = new GUIBoundKeyEventArgs(
EngineKeyFunctions.TextDelete,
BoundKeyState.Down,
default, false, default, default);
lineEdit.KeyBindDown(eventArgs);
Assert.That(lineEdit.Text, Is.EqualTo("Foo"));
Assert.That(lineEdit.CursorPosition, Is.EqualTo(3));
}
[Test]
public void TestMoveLeft()
{
var lineEdit = new TestLineEdit();
lineEdit.Text = "Foo👏";
lineEdit.CursorPosition = lineEdit.Text.Length;
var eventArgs = new GUIBoundKeyEventArgs(
EngineKeyFunctions.TextCursorLeft,
BoundKeyState.Down,
default, false, default, default);
lineEdit.KeyBindDown(eventArgs);
Assert.That(lineEdit.Text, Is.EqualTo("Foo👏"));
Assert.That(lineEdit.CursorPosition, Is.EqualTo(3));
}
[Test]
public void TestMoveRight()
{
var lineEdit = new TestLineEdit();
lineEdit.Text = "Foo👏";
lineEdit.CursorPosition = 3;
var eventArgs = new GUIBoundKeyEventArgs(
EngineKeyFunctions.TextCursorRight,
BoundKeyState.Down,
default, false, default, default);
lineEdit.KeyBindDown(eventArgs);
Assert.That(lineEdit.Text, Is.EqualTo("Foo👏"));
Assert.That(lineEdit.CursorPosition, Is.EqualTo(5));
}
[Test]
public void TestMoveSelectLeft()
{
var lineEdit = new TestLineEdit();
lineEdit.Text = "Foo👏";
lineEdit.CursorPosition = lineEdit.Text.Length;
var eventArgs = new GUIBoundKeyEventArgs(
EngineKeyFunctions.TextCursorSelectLeft,
BoundKeyState.Down,
default, false, default, default);
lineEdit.KeyBindDown(eventArgs);
Assert.That(lineEdit.Text, Is.EqualTo("Foo👏"));
Assert.That(lineEdit.SelectionStart, Is.EqualTo(5));
Assert.That(lineEdit.CursorPosition, Is.EqualTo(3));
}
[Test]
public void TestMoveSelectRight()
{
var lineEdit = new TestLineEdit();
lineEdit.Text = "Foo👏";
lineEdit.CursorPosition = 3;
var eventArgs = new GUIBoundKeyEventArgs(
EngineKeyFunctions.TextCursorSelectRight,
BoundKeyState.Down,
default, false, default, default);
lineEdit.KeyBindDown(eventArgs);
Assert.That(lineEdit.Text, Is.EqualTo("Foo👏"));
Assert.That(lineEdit.SelectionStart, Is.EqualTo(3));
Assert.That(lineEdit.CursorPosition, Is.EqualTo(5));
}
[Test]
// RIGHT
[TestCase("Foo Bar Baz", false, 0, ExpectedResult = 3)]
[TestCase("Foo Bar Baz", false, 8, ExpectedResult = 11)]
[TestCase("Foo[Bar[Baz", false, 0, ExpectedResult = 3)]
[TestCase("Foo[Bar[Baz", false, 3, ExpectedResult = 4)]
[TestCase("Foo^Bar^Baz", false, 0, ExpectedResult = 3)]
[TestCase("Foo^Bar^Baz", false, 3, ExpectedResult = 5)]
[TestCase("Foo^^^Bar^Baz", false, 3, ExpectedResult = 9)]
[TestCase("^^^ ^^^", false, 0, ExpectedResult = 6)]
[TestCase("^^^ ^^^", false, 7, ExpectedResult = 13)]
// LEFT
[TestCase("Foo Bar Baz", true, 4, ExpectedResult = 0)]
[TestCase("Foo Bar Baz", true, 11, ExpectedResult = 8)]
[TestCase("Foo[Bar[Baz", true, 3, ExpectedResult = 0)]
[TestCase("Foo[Bar[Baz", true, 4, ExpectedResult = 3)]
[TestCase("Foo^Bar^Baz", true, 3, ExpectedResult = 0)]
[TestCase("Foo^Bar^Baz", true, 5, ExpectedResult = 3)]
[TestCase("Foo^^^Bar^Baz", true, 9, ExpectedResult = 3)]
[TestCase("^^^ ^^^", true, 7, ExpectedResult = 0)]
[TestCase("^^^ ^^^", true, 13, ExpectedResult = 7)]
public int TestMoveWord(string text, bool left, int initCursorPos)
{
// ^ is replaced by 👏 because Rider refuses to run the tests otherwise.
text = text.Replace("^", "👏");
var lineEdit = new TestLineEdit();
lineEdit.Text = text;
lineEdit.CursorPosition = initCursorPos;
var eventArgs = new GUIBoundKeyEventArgs(
left ? EngineKeyFunctions.TextCursorWordLeft : EngineKeyFunctions.TextCursorWordRight,
BoundKeyState.Down,
default, false, default, default);
lineEdit.KeyBindDown(eventArgs);
return lineEdit.CursorPosition;
}
private sealed class TestLineEdit : LineEdit
{
public override bool HasKeyboardFocus()
{
return true;
}
}
}
}

View File

@@ -1,44 +0,0 @@
using NUnit.Framework;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.IoC;
namespace Robust.UnitTesting.Client.UserInterface.Controls
{
[TestFixture]
[TestOf(typeof(MultiselectOptionButton<>))]
public sealed class MultiselectOptionButtonTest : RobustUnitTest
{
public override UnitTestProject Project => UnitTestProject.Client;
[Test]
public void TestAddRemoveItem()
{
var uiManager = IoCManager.Resolve<IUserInterfaceManagerInternal>();
uiManager.InitializeTesting();
var optionButton = new MultiselectOptionButton<string>();
optionButton.AddItem("Label1", "Key1");
optionButton.AddItem("Label2", "Key2");
optionButton.AddItem("Label3", "Key3");
Assert.That(optionButton.GetIdx("Key1"), Is.EqualTo(0));
Assert.That(optionButton.GetIdx("Key2"), Is.EqualTo(1));
Assert.That(optionButton.GetIdx("Key3"), Is.EqualTo(2));
Assert.That(optionButton.GetItemKey(0), Is.EqualTo("Key1"));
Assert.That(optionButton.GetItemKey(1), Is.EqualTo("Key2"));
Assert.That(optionButton.GetItemKey(2), Is.EqualTo("Key3"));
optionButton.RemoveItem(1);
Assert.That(optionButton.GetIdx("Key1"), Is.EqualTo(0));
Assert.That(optionButton.GetIdx("Key3"), Is.EqualTo(1));
Assert.That(optionButton.GetItemKey(0), Is.EqualTo("Key1"));
Assert.That(optionButton.GetItemKey(1), Is.EqualTo("Key3"));
optionButton.RemoveItem(0);
Assert.That(optionButton.GetIdx("Key3"), Is.EqualTo(0));
Assert.That(optionButton.GetItemKey(0), Is.EqualTo("Key3"));
}
}
}

View File

@@ -1,74 +0,0 @@
using System.Numerics;
using NUnit.Framework;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Maths;
namespace Robust.UnitTesting.Client.UserInterface.Controls
{
[TestFixture]
[TestOf(typeof(PopupContainer))]
public sealed class PopupContainerTest : RobustUnitTest
{
public override UnitTestProject Project => UnitTestProject.Client;
[Test]
public void Test()
{
var container = new PopupContainer {MinSize = new Vector2(100, 100)};
container.Arrange(new UIBox2(0, 0, 100, 100));
var popup = new Control {MinSize = new Vector2(50, 50)};
container.AddChild(popup);
PopupContainer.SetPopupOrigin(popup, new Vector2(25, 25));
container.InvalidateArrange();
container.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(popup.Position, Is.EqualTo(new Vector2(25, 25)));
Assert.That(popup.Size, Is.EqualTo(new Vector2(50, 50)));
// Test that pos gets pushed back to the top left if the size + offset is too large to fit.
PopupContainer.SetPopupOrigin(popup, new Vector2(75, 75));
container.InvalidateArrange();
container.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(popup.Position, Is.EqualTo(new Vector2(50, 50)));
Assert.That(popup.Size, Is.EqualTo(new Vector2(50, 50)));
// Test that pos = 0 if the popup is too large to fit.
popup.MinSize = new Vector2(150, 150);
container.InvalidateArrange();
container.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(popup.Position, Is.EqualTo(new Vector2(0, 0)));
Assert.That(popup.Size, Is.EqualTo(new Vector2(150, 150)));
}
[Test]
public void TestAltPos()
{
var container = new PopupContainer {MinSize = new Vector2(100, 100)};
container.Arrange(new UIBox2(0, 0, 100, 100));
var popup = new Control {MinSize = new Vector2(50, 50)};
container.AddChild(popup);
PopupContainer.SetPopupOrigin(popup, new Vector2(75, 75));
PopupContainer.SetAltOrigin(popup, new Vector2(65, 25));
container.InvalidateArrange();
container.Arrange(new UIBox2(0, 0, 100, 100));
Assert.That(popup.Position, Is.EqualTo(new Vector2(15, 25)));
Assert.That(popup.Size, Is.EqualTo(new Vector2(50, 50)));
}
}
}

View File

@@ -1,63 +0,0 @@
using NUnit.Framework;
using Robust.Client.UserInterface.Controls;
namespace Robust.UnitTesting.Client.UserInterface.Controls
{
[TestFixture]
[TestOf(typeof(RadioOptions<int>))]
public sealed class RadioOptionsTest : RobustUnitTest
{
public override UnitTestProject Project => UnitTestProject.Client;
[Test]
public void TestDefaultInvoke()
{
//Arrange
RadioOptions<int> _optionButton = new RadioOptions<int>(RadioOptionsLayout.Horizontal);
int itemId = _optionButton.AddItem("High", 1);
int countSelected = 0;
_optionButton.OnItemSelected += args =>
{
countSelected++;
};
//Act
_optionButton.InvokeItemSelected(new RadioOptionItemSelectedEventArgs<int>(itemId, _optionButton));
//Assert
Assert.That(countSelected, Is.EqualTo(1));
}
[Test]
public void TestOverrideInvoke()
{
//Arrange
RadioOptions<int> _optionButton = new RadioOptions<int>(RadioOptionsLayout.Horizontal);
int countSelected = 0;
int itemId = _optionButton.AddItem("High", 1, args => { countSelected--; });
int itemId2 = _optionButton.AddItem("High", 2);
_optionButton.OnItemSelected += args =>
{
countSelected++;
};
//Act
_optionButton.InvokeItemSelected(new RadioOptionItemSelectedEventArgs<int>(itemId, _optionButton));
//Assert
Assert.That(countSelected, Is.EqualTo(-1));
//Act
_optionButton.InvokeItemSelected(new RadioOptionItemSelectedEventArgs<int>(itemId2, _optionButton));
_optionButton.InvokeItemSelected(new RadioOptionItemSelectedEventArgs<int>(itemId2, _optionButton));
//Assert
Assert.That(countSelected, Is.EqualTo(1));
}
}
}

View File

@@ -1,68 +0,0 @@
using NUnit.Framework;
using Robust.Client.UserInterface.Controls;
namespace Robust.UnitTesting.Client.UserInterface.Controls;
[TestFixture]
[TestOf(typeof(TextEditShared))]
[Parallelizable]
internal sealed class TextEditSharedTest
{
// @formatter:off
[Test]
[TestCase("foo bar baz", 0, ExpectedResult = 0)]
[TestCase("foo bar baz", 1, ExpectedResult = 0)]
[TestCase("foo bar baz", 4, ExpectedResult = 0)]
[TestCase("foo bar baz", 5, ExpectedResult = 4)]
[TestCase("foo bar baz", 8, ExpectedResult = 4)]
[TestCase("foo bar baz", 9, ExpectedResult = 8)]
[TestCase("foo +bar baz", 5, ExpectedResult = 4)]
[TestCase("foo +bar baz", 4, ExpectedResult = 0)]
[TestCase("foo +bar baz", 6, ExpectedResult = 5)]
[TestCase("foo +bar baz", 7, ExpectedResult = 5)]
[TestCase("Foo Bar Baz", 4, ExpectedResult = 0)]
[TestCase("Foo Bar Baz", 11, ExpectedResult = 8)]
[TestCase("Foo[Bar[Baz", 3, ExpectedResult = 0)]
[TestCase("Foo[Bar[Baz", 4, ExpectedResult = 3)]
[TestCase("Foo^Bar^Baz", 3, ExpectedResult = 0)]
[TestCase("Foo^Bar^Baz", 5, ExpectedResult = 3)]
[TestCase("Foo^^^Bar^Baz", 9, ExpectedResult = 3)]
[TestCase("^^^ ^^^", 7, ExpectedResult = 0)]
[TestCase("^^^ ^^^", 13, ExpectedResult = 7)]
// @formatter:on
public int TestPrevWordPosition(string str, int cursor)
{
// For my sanity.
str = str.Replace("^", "👏");
return TextEditShared.PrevWordPosition(str, cursor);
}
[Test]
// @formatter:off
[TestCase("foo bar baz", 11, ExpectedResult = 11)]
[TestCase("foo bar baz", 0, ExpectedResult = 3 )]
[TestCase("foo bar baz", 1, ExpectedResult = 3 )]
[TestCase("foo bar baz", 3, ExpectedResult = 7 )]
[TestCase("foo bar baz", 4, ExpectedResult = 7 )]
[TestCase("foo bar baz", 5, ExpectedResult = 7 )]
[TestCase("Foo Bar Baz", 0, ExpectedResult = 3 )]
[TestCase("Foo Bar Baz", 8, ExpectedResult = 11)]
[TestCase("foo +bar baz", 0, ExpectedResult = 3 )]
[TestCase("foo +bar baz", 4, ExpectedResult = 5 )]
[TestCase("Foo[Bar[Baz", 0, ExpectedResult = 3 )]
[TestCase("Foo[Bar[Baz", 3, ExpectedResult = 4 )]
[TestCase("Foo^Bar^Baz", 0, ExpectedResult = 3 )]
[TestCase("Foo^Bar^Baz", 3, ExpectedResult = 5 )]
[TestCase("Foo^^^Bar^Baz", 3, ExpectedResult = 9 )]
[TestCase("^^^ ^^^", 0, ExpectedResult = 6 )]
[TestCase("^^^ ^^^", 7, ExpectedResult = 13)]
// @formatter:on
public int TestEndWordPosition(string str, int cursor)
{
// For my sanity.
str = str.Replace("^", "👏");
return TextEditShared.EndWordPosition(str, cursor);
}
}

View File

@@ -1,52 +0,0 @@
using System.Numerics;
using NUnit.Framework;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
namespace Robust.UnitTesting.Client.UserInterface.Controls;
[TestFixture]
[TestOf(typeof(TextEdit))]
public sealed class TextEditTest : RobustUnitTest
{
public override UnitTestProject Project => UnitTestProject.Client;
[OneTimeSetUp]
public void Setup()
{
IoCManager.Resolve<IUserInterfaceManagerInternal>().InitializeTesting();
}
// Regression test for https://github.com/space-wizards/RobustToolbox/issues/4953
// It was possible to move the cursor up/down if there was multi-line placeholder text.
[Test]
public void TestInvalidMoveInPlaceholder()
{
var textEdit = new TextEdit { Placeholder = new Rope.Leaf("Foo\nBar") };
textEdit.Arrange(new UIBox2(0, 0, 200, 200));
var click = new GUIBoundKeyEventArgs(EngineKeyFunctions.TextCursorDown, BoundKeyState.Down, new ScreenCoordinates(), true, Vector2.Zero, Vector2.Zero);
textEdit.KeyBindDown(click);
textEdit.KeyBindUp(click);
Assert.That(textEdit.CursorPosition.Index, Is.Zero);
}
// Regression test for https://github.com/space-wizards/RobustToolbox/issues/4957
// Moving left (with the arrow keys) in an empty TextEdit would cause an exception.
[Test]
public void TestEmptyMoveLeft()
{
var textEdit = new TextEdit();
textEdit.Arrange(new UIBox2(0, 0, 200, 200));
var click = new GUIBoundKeyEventArgs(EngineKeyFunctions.TextCursorLeft, BoundKeyState.Down, new ScreenCoordinates(), true, Vector2.Zero, Vector2.Zero);
textEdit.KeyBindDown(click);
textEdit.KeyBindUp(click);
}
}

View File

@@ -1,137 +0,0 @@
using NUnit.Framework;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.IoC;
namespace Robust.UnitTesting.Client.UserInterface
{
[TestFixture]
public sealed class StylesheetTest : RobustUnitTest
{
public override UnitTestProject Project => UnitTestProject.Client;
[OneTimeSetUp]
public void Setup()
{
IoCManager.Resolve<IUserInterfaceManagerInternal>().InitializeTesting();
}
[Test]
public void TestSelectors()
{
var selectorElementLabel = new SelectorElement(typeof(Label), null, null, null);
var label = new Label();
var panel = new PanelContainer {StyleIdentifier = "bar"};
Assert.That(selectorElementLabel.Matches(label), Is.True);
Assert.That(selectorElementLabel.Matches(panel), Is.False);
selectorElementLabel = new SelectorElement(typeof(Label), new[] {"foo"}, null, null);
Assert.That(selectorElementLabel.Matches(label), Is.False);
Assert.That(selectorElementLabel.Matches(panel), Is.False);
Assert.That(label.HasStyleClass("foo"), Is.False);
label.AddStyleClass("foo");
Assert.That(selectorElementLabel.Matches(label), Is.True);
Assert.That(label.HasStyleClass("foo"));
// Make sure it doesn't throw.
label.AddStyleClass("foo");
label.RemoveStyleClass("foo");
Assert.That(selectorElementLabel.Matches(label), Is.False);
Assert.That(label.HasStyleClass("foo"), Is.False);
// Make sure it doesn't throw.
label.RemoveStyleClass("foo");
selectorElementLabel = new SelectorElement(null, null, "bar", null);
Assert.That(selectorElementLabel.Matches(label), Is.False);
Assert.That(selectorElementLabel.Matches(panel), Is.True);
}
[Test]
public void TestStyleProperties()
{
var sheet = new Stylesheet(new[]
{
new StyleRule(new SelectorElement(typeof(Label), null, "baz", null), new[]
{
new StyleProperty("foo", "honk"),
}),
new StyleRule(new SelectorElement(typeof(Label), null, null, null), new[]
{
new StyleProperty("foo", "heh"),
}),
new StyleRule(new SelectorElement(typeof(Label), null, null, null), new[]
{
new StyleProperty("foo", "bar"),
}),
});
var uiMgr = IoCManager.Resolve<IUserInterfaceManager>();
uiMgr.Stylesheet = sheet;
var control = new Label();
uiMgr.StateRoot.AddChild(control);
control.ForceRunStyleUpdate();
control.TryGetStyleProperty("foo", out string? value);
Assert.That(value, Is.EqualTo("bar"));
control.StyleIdentifier = "baz";
control.ForceRunStyleUpdate();
control.TryGetStyleProperty("foo", out value);
Assert.That(value, Is.EqualTo("honk"));
}
[Test]
public void TestStylesheetOverride()
{
var sheetA = new Stylesheet(new[]
{
new StyleRule(SelectorElement.Class("A"), new[] {new StyleProperty("foo", "bar")}),
});
var sheetB = new Stylesheet(new[]
{
new StyleRule(SelectorElement.Class("A"), new[] {new StyleProperty("foo", "honk!")})
});
// Set style sheet to null, property shouldn't exist.
var uiMgr = IoCManager.Resolve<IUserInterfaceManager>();
uiMgr.Stylesheet = null;
var baseControl = new Control();
baseControl.AddStyleClass("A");
var childA = new Control();
childA.AddStyleClass("A");
var childB = new Control();
childB.AddStyleClass("A");
uiMgr.StateRoot.AddChild(baseControl);
baseControl.AddChild(childA);
childA.AddChild(childB);
baseControl.ForceRunStyleUpdate();
Assert.That(baseControl.TryGetStyleProperty("foo", out object? _), Is.False);
uiMgr.RootControl.Stylesheet = sheetA;
childA.Stylesheet = sheetB;
// Assign sheets.
baseControl.ForceRunStyleUpdate();
baseControl.TryGetStyleProperty("foo", out object? value);
Assert.That(value, Is.EqualTo("bar"));
childA.TryGetStyleProperty("foo", out value);
Assert.That(value, Is.EqualTo("honk!"));
childB.TryGetStyleProperty("foo", out value);
Assert.That(value, Is.EqualTo("honk!"));
}
}
}

View File

@@ -1,246 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using NUnit.Framework;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Robust.UnitTesting.Client.UserInterface
{
[TestFixture]
public sealed class UserInterfaceManagerTest : RobustUnitTest
{
public override UnitTestProject Project => UnitTestProject.Client;
private IUserInterfaceManagerInternal _userInterfaceManager = default!;
[OneTimeSetUp]
public void Setup()
{
_userInterfaceManager = IoCManager.Resolve<IUserInterfaceManagerInternal>();
_userInterfaceManager.InitializeTesting();
}
[Test]
[SuppressMessage("ReSharper", "AccessToModifiedClosure")]
public void TestMouseDown()
{
// We create 4 controls.
// Control 2 is set to stop mouse events,
// Control 3 pass,
// Control 4 ignore.
// We check that 4 and 1 do not receive events, that 3 receives before 2, and that positions are correct.
var control1 = new LayoutContainer
{
MinSize = new Vector2(50, 50)
};
var control2 = new LayoutContainer
{
MinSize = new Vector2(50, 50),
MouseFilter = Control.MouseFilterMode.Stop
};
var control3 = new LayoutContainer
{
MinSize = new Vector2(50, 50),
MouseFilter = Control.MouseFilterMode.Pass
};
var control4 = new LayoutContainer
{
MinSize = new Vector2(50, 50),
MouseFilter = Control.MouseFilterMode.Ignore
};
_userInterfaceManager.RootControl.AddChild(control1);
control1.AddChild(control2);
// Offsets to test relative positioning on the events.
LayoutContainer.SetPosition(control2, new Vector2(5, 5));
control2.AddChild(control3);
LayoutContainer.SetPosition(control3, new Vector2(5, 5));
control3.AddChild(control4);
LayoutContainer.SetPosition(control4, new Vector2(5, 5));
control1.Arrange(new UIBox2(0, 0, 50, 50));
var mouseEvent = new BoundKeyEventArgs(EngineKeyFunctions.Use, BoundKeyState.Down,
new ScreenCoordinates(30, 30, WindowId.Main), true);
var control2Fired = false;
var control3Fired = false;
control1.OnKeyBindDown += _ => Assert.Fail("Control 1 should not get a mouse event.");
void Control2MouseDown(GUIBoundKeyEventArgs ev)
{
Assert.That(control2Fired, NUnit.Framework.Is.False);
Assert.That(control3Fired, NUnit.Framework.Is.True);
Assert.That(ev.RelativePosition, NUnit.Framework.Is.EqualTo(new Vector2(25, 25)));
control2Fired = true;
}
control2.OnKeyBindDown += Control2MouseDown;
control3.OnKeyBindDown += ev =>
{
Assert.That(control2Fired, NUnit.Framework.Is.False);
Assert.That(control3Fired, NUnit.Framework.Is.False);
Assert.That(ev.RelativePosition, NUnit.Framework.Is.EqualTo(new Vector2(20, 20)));
control3Fired = true;
};
control4.OnKeyBindDown += _ => Assert.Fail("Control 4 should not get a mouse event.");
_userInterfaceManager.KeyBindDown(mouseEvent);
_userInterfaceManager.KeyBindUp(mouseEvent);
Assert.Multiple(() =>
{
Assert.That(control2Fired, NUnit.Framework.Is.True);
Assert.That(control3Fired, NUnit.Framework.Is.True);
});
// Step two: instead of relying on stop for control2 to prevent the event reaching control1,
// handle the event in control2.
control2Fired = false;
control3Fired = false;
control2.OnKeyBindDown -= Control2MouseDown;
control2.OnKeyBindDown += ev =>
{
Assert.That(control2Fired, NUnit.Framework.Is.False);
Assert.That(control3Fired, NUnit.Framework.Is.True);
Assert.That(ev.RelativePosition, NUnit.Framework.Is.EqualTo(new Vector2(25, 25)));
control2Fired = true;
ev.Handle();
};
control2.MouseFilter = Control.MouseFilterMode.Pass;
_userInterfaceManager.KeyBindDown(mouseEvent);
_userInterfaceManager.KeyBindUp(mouseEvent);
Assert.Multiple(() =>
{
Assert.That(control2Fired, NUnit.Framework.Is.True);
Assert.That(control3Fired, NUnit.Framework.Is.True);
});
control1.Orphan();
}
[Test]
public void TestGrabKeyboardFocus()
{
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
var control1 = new Control {CanKeyboardFocus = true};
control1.GrabKeyboardFocus();
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.EqualTo(control1));
Assert.That(control1.HasKeyboardFocus(), NUnit.Framework.Is.EqualTo(true));
control1.ReleaseKeyboardFocus();
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
}
[Test]
public void TestGrabKeyboardFocusSteal()
{
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
var control1 = new Control {CanKeyboardFocus = true};
var control2 = new Control {CanKeyboardFocus = true};
control1.GrabKeyboardFocus();
control2.GrabKeyboardFocus();
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.EqualTo(control2));
control2.ReleaseKeyboardFocus();
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
}
[Test]
public void TestGrabKeyboardFocusOtherRelease()
{
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
var control1 = new Control {CanKeyboardFocus = true};
var control2 = new Control {CanKeyboardFocus = true};
control1.GrabKeyboardFocus();
control2.ReleaseKeyboardFocus();
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.EqualTo(control1));
_userInterfaceManager.ReleaseKeyboardFocus();
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
}
[Test]
public void TestGrabKeyboardFocusNull()
{
Assert.That(() => _userInterfaceManager.GrabKeyboardFocus(null!), Throws.ArgumentNullException);
Assert.That(() => _userInterfaceManager.ReleaseKeyboardFocus(null!), Throws.ArgumentNullException);
}
[Test]
public void TestGrabKeyboardFocusBlocked()
{
var control = new Control();
Assert.That(() => _userInterfaceManager.GrabKeyboardFocus(control), Throws.ArgumentException);
}
[Test]
public void TestGrabKeyboardFocusOnClick()
{
var control = new Control
{
CanKeyboardFocus = true,
KeyboardFocusOnClick = true,
MinSize = new Vector2(50, 50),
MouseFilter = Control.MouseFilterMode.Stop
};
_userInterfaceManager.RootControl.AddChild(control);
_userInterfaceManager.RootControl.Arrange(new UIBox2(0, 0, 50, 50));
_userInterfaceManager.HandleCanFocusDown(new ScreenCoordinates(30, 30, WindowId.Main), out _);
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.EqualTo(control));
_userInterfaceManager.ReleaseKeyboardFocus();
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
control.Orphan();
}
/// <summary>
/// Assert that indeed nothing happens when the control has focus modes off.
/// </summary>
[Test]
public void TestNotGrabKeyboardFocusOnClick()
{
var control = new Control
{
MinSize = new Vector2(50, 50),
MouseFilter = Control.MouseFilterMode.Stop
};
_userInterfaceManager.RootControl.AddChild(control);
var pos = new ScreenCoordinates(30, 30, WindowId.Main);
var mouseEvent = new GUIBoundKeyEventArgs(EngineKeyFunctions.Use, BoundKeyState.Down,
pos, true, pos.Position / 1 - control.GlobalPosition, pos.Position - control.GlobalPixelPosition);
_userInterfaceManager.KeyBindDown(mouseEvent);
_userInterfaceManager.KeyBindUp(mouseEvent);
Assert.That(_userInterfaceManager.KeyboardFocused, NUnit.Framework.Is.Null);
control.Orphan();
}
}
}

View File

@@ -2,7 +2,6 @@ using System;
using System.IO;
using System.Text;
using Robust.Shared.ContentPack;
using Robust.Shared.Utility;
namespace Robust.UnitTesting
{

View File

@@ -1,76 +0,0 @@
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Packaging.AssetProcessing.Passes;
namespace Robust.UnitTesting.Packaging;
[Parallelizable(ParallelScope.All)]
[TestFixture]
[TestOf(typeof(AssetPassMergeTextDirectories))]
internal sealed class AssetPassMergeTextDirectoriesTest
{
[Test]
public async Task Test()
{
var pass = new AssetPassMergeTextDirectories("/Prototypes", "yml", f => $"# BEGIN: {f}", f => $"# END: {f}");
var collectorPass = AssetPassTest.SetupTestPass(pass);
pass.InjectFileFromMemory("/Prototypes/LICENSE.txt", "Do whatever\n"u8);
pass.InjectFileFromMemory("/Prototypes/b.yml", "# file: B\n"u8);
pass.InjectFileFromMemory("/Prototypes/a.yml", "# file: A\n"u8);
pass.InjectFileFromMemory("/Prototypes/z/d.yml", "# file: D\n"u8);
pass.InjectFileFromMemory("/Prototypes/z/c.yml", "# file: C\n"u8);
pass.InjectFinished();
await collectorPass.FinishedTask;
collectorPass.AssertTextFiles(
("/Prototypes/__merged.yml", """
# BEGIN: /Prototypes/a.yml
# file: A
# END: /Prototypes/a.yml
# BEGIN: /Prototypes/b.yml
# file: B
# END: /Prototypes/b.yml
""".Replace("\r\n", "\n")),
("/Prototypes/z/__merged.yml", """
# BEGIN: /Prototypes/z/c.yml
# file: C
# END: /Prototypes/z/c.yml
# BEGIN: /Prototypes/z/d.yml
# file: D
# END: /Prototypes/z/d.yml
""".Replace("\r\n", "\n")));
}
[Test]
public async Task TestNormalizeEol()
{
var pass = new AssetPassMergeTextDirectories("/", "yml");
var collectorPass = AssetPassTest.SetupTestPass(pass);
pass.InjectFileFromMemory("/b.yml", "# file: B\r\n"u8);
pass.InjectFileFromMemory("/a.yml", "# file: A\n"u8);
pass.InjectFinished();
await collectorPass.FinishedTask;
collectorPass.AssertTextFiles(
("/__merged.yml", "# file: A\n# file: B\n"));
}
[Test]
public async Task TestFixBom()
{
var pass = new AssetPassMergeTextDirectories("/", "yml");
var collectorPass = AssetPassTest.SetupTestPass(pass);
pass.InjectFileFromMemory("/b.yml", "\uFEFF# file: B\n"u8);
pass.InjectFileFromMemory("/a.yml", "\uFEFF# file: A\n"u8);
pass.InjectFinished();
await collectorPass.FinishedTask;
collectorPass.AssertTextFiles(
("/__merged.yml", "# file: A\n# file: B\n"));
}
}

View File

@@ -1,28 +0,0 @@
using NUnit.Framework;
using Robust.Packaging.AssetProcessing;
namespace Robust.UnitTesting.Packaging;
/// <summary>
/// Helper class for testing <see cref="AssetPass"/>.
/// </summary>
public static class AssetPassTest
{
/// <summary>
/// Make an asset pass write into a <see cref="AssetPassTestCollector"/> and resolve the graph.
/// </summary>
/// <remarks>
/// The resolved graph logs to the NUnit test context.
/// </remarks>
public static AssetPassTestCollector SetupTestPass(AssetPass testedPass)
{
var logger = new PackageLoggerNUnit(TestContext.Out);
var collectorPass = new AssetPassTestCollector();
collectorPass.AddDependency(testedPass);
AssetGraph.CalculateGraph([testedPass, collectorPass], logger);
return collectorPass;
}
}

View File

@@ -1,55 +0,0 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NUnit.Framework;
using Robust.Packaging.AssetProcessing;
namespace Robust.UnitTesting.Packaging;
/// <summary>
/// A simple asset pass that stores all files it receives, for introspection by tests.
/// </summary>
public sealed class AssetPassTestCollector : AssetPass
{
public readonly List<AssetFile> Files = [];
protected override AssetFileAcceptResult AcceptFile(AssetFile file)
{
lock (Files)
{
Files.Add(file);
}
return AssetFileAcceptResult.Consumed;
}
/// <summary>
/// Assert that the only files collected are an exact set of test files.
/// </summary>
public void AssertTextFiles(params (string path, string data)[] files)
{
lock (Files)
{
Assert.That(Files, Has.Count.EqualTo(files.Length));
Assert.Multiple(() =>
{
foreach (var file in files)
{
var matchingFile = Files.SingleOrDefault(f => f.Path == file.path);
if (matchingFile == null)
{
Assert.Fail($"Unable to find file {file.path}");
continue;
}
using var fileData = matchingFile.Open();
using var reader = new StreamReader(fileData);
var fileText = reader.ReadToEnd();
Assert.That(fileText, Is.EqualTo(file.data));
}
});
}
}
}

View File

@@ -1,17 +0,0 @@
using System.IO;
using Robust.Packaging;
using Robust.Shared.Log;
namespace Robust.UnitTesting.Packaging;
/// <summary>
/// Package logger for writing to NUnit's test context.
/// </summary>
/// <param name="writer"></param>
public sealed class PackageLoggerNUnit(TextWriter writer) : IPackageLogger
{
public void Log(LogLevel level, string msg)
{
writer.WriteLine($"[{level}] {msg}");
}
}

View File

@@ -1,21 +0,0 @@
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.Log;
namespace Robust.UnitTesting
{
// ReSharper disable once InconsistentNaming
[CVarDefs]
public sealed class RTCVars : CVars
{
/*
* Logging
*/
/// <summary>
/// The log level which will cause a test failure.
/// </summary>
public static readonly CVarDef<LogLevel> FailureLogLevel =
CVarDef.Create("robust.tests.failure_log_level", Robust.Shared.Log.LogLevel.Error);
}
}

View File

@@ -2,15 +2,11 @@
<Import Project="..\MSBuild\Robust.Engine.props" />
<PropertyGroup>
<IsPackable>false</IsPackable>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<OutputPath>../bin/UnitTesting</OutputPath>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" />
<PackageReference Include="JetBrains.Annotations" />
<PackageReference Include="Microsoft.CodeCoverage" />
<PackageReference Include="Microsoft.DotNet.RemoteExecutor" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" />
<PackageReference Include="NUnit" />
@@ -19,16 +15,14 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Lidgren.Network\Lidgren.Network.csproj" />
<ProjectReference Include="..\Robust.Shared.Maths.Testing\Robust.Shared.Maths.Testing.csproj" />
<ProjectReference Include="..\Robust.Shared.Maths.Tests\Robust.Shared.Maths.Tests.csproj" />
<ProjectReference Include="..\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
<ProjectReference Include="..\Robust.Shared.Testing\Robust.Shared.Testing.csproj" />
<ProjectReference Include="..\Robust.Shared\Robust.Shared.csproj" />
<ProjectReference Include="..\Robust.Client\Robust.Client.csproj" />
<ProjectReference Include="..\Robust.Server\Robust.Server.csproj" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Shared\ContentPack\ZipTest.zip">
<LogicalName>Robust.UnitTesting.Shared.ContentPack.ZipTest.zip</LogicalName>
</EmbeddedResource>
</ItemGroup>
<Import Project="..\MSBuild\Robust.Properties.targets" />
<Import Project="..\MSBuild\Robust.CompNetworkGenerator.targets" />
</Project>

View File

@@ -10,7 +10,6 @@ using Lidgren.Network;
using Robust.Shared.Asynchronous;
using Robust.Shared.IoC;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Player;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;

View File

@@ -1,6 +1,4 @@
using System.Threading.Tasks;
using Robust.Client;
using Robust.Server;
using Robust.Shared.Log;
using Robust.UnitTesting.Pool;

View File

@@ -36,8 +36,8 @@ using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Reflection;
using Robust.Shared.Serialization;
using Robust.Shared.Testing;
using Robust.Shared.Timing;
using Robust.UnitTesting.Pool;
using ServerProgram = Robust.Server.Program;
namespace Robust.UnitTesting
@@ -83,6 +83,9 @@ namespace Robust.UnitTesting
/// </summary>
protected virtual ServerIntegrationInstance StartServer(ServerIntegrationOptions? options = null)
{
options ??= new ServerIntegrationOptions();
options.TestAssembly = GetType().Assembly;
ServerIntegrationInstance instance;
if (ShouldPool(options))
@@ -132,6 +135,9 @@ namespace Robust.UnitTesting
/// </summary>
protected virtual ClientIntegrationInstance StartClient(ClientIntegrationOptions? options = null)
{
options ??= new ClientIntegrationOptions();
options.TestAssembly = GetType().Assembly;
ClientIntegrationInstance instance;
if (ShouldPool(options))
@@ -717,8 +723,8 @@ namespace Robust.UnitTesting
//ServerProgram.SetupLogging();
ServerProgram.InitReflectionManager(deps);
if (Options?.LoadTestAssembly != false)
deps.Resolve<IReflectionManager>().LoadAssemblies(typeof(RobustIntegrationTest).Assembly);
if (Options?.LoadTestAssembly != false && Options?.TestAssembly != null)
deps.Resolve<IReflectionManager>().LoadAssemblies(Options.TestAssembly);
var server = DependencyCollection.Resolve<BaseServer>();
@@ -747,6 +753,7 @@ namespace Robust.UnitTesting
var cfg = deps.Resolve<IConfigurationManagerInternal>();
cfg.LoadCVarsFromAssembly(typeof(RobustIntegrationTest).Assembly);
cfg.LoadCVarsFromAssembly(typeof(RTCVars).Assembly);
if (Options != null)
{
@@ -977,8 +984,8 @@ namespace Robust.UnitTesting
GameController.RegisterReflection(deps);
if (Options?.LoadTestAssembly != false)
deps.Resolve<IReflectionManager>().LoadAssemblies(typeof(RobustIntegrationTest).Assembly);
if (Options?.LoadTestAssembly != false && Options?.TestAssembly != null)
deps.Resolve<IReflectionManager>().LoadAssemblies(Options.TestAssembly);
var client = DependencyCollection.Resolve<GameController>();
@@ -1007,6 +1014,7 @@ namespace Robust.UnitTesting
var cfg = deps.Resolve<IConfigurationManagerInternal>();
cfg.LoadCVarsFromAssembly(typeof(RobustIntegrationTest).Assembly);
cfg.LoadCVarsFromAssembly(typeof(RTCVars).Assembly);
if (Options != null)
{
@@ -1214,6 +1222,7 @@ namespace Robust.UnitTesting
public Assembly[]? ContentAssemblies { get; set; }
public bool LoadTestAssembly { get; set; } = true;
public Assembly? TestAssembly { get; set; }
/// <summary>
/// String containing extra prototypes to load. Contents of the string are treated like a yaml file in the

View File

@@ -0,0 +1,13 @@
using NUnit.Framework;
namespace Robust.UnitTesting;
[TestFixture]
internal sealed class RobustIntegrationTestDummy : RobustIntegrationTest
{
[Test]
public void Foo()
{
// Nada.
}
}

View File

@@ -3,6 +3,7 @@ using Robust.Client;
using Robust.Server;
using Robust.Shared.ContentPack;
using Robust.Shared.IoC;
using Robust.Shared.Testing;
namespace Robust.UnitTesting
{

View File

@@ -14,18 +14,16 @@ using Robust.Shared.Console;
using Robust.Shared.Containers;
using Robust.Shared.ContentPack;
using Robust.Shared.EntitySerialization.Components;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Controllers;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Player;
using Robust.Shared.Reflection;
using Robust.Shared.Testing;
using Robust.Shared.Threading;
using Robust.Shared.Utility;
using AppearanceSystem = Robust.Client.GameObjects.AppearanceSystem;
@@ -112,7 +110,7 @@ namespace Robust.UnitTesting
configurationManager.LoadCVarsFromAssembly(assembly);
}
configurationManager.LoadCVarsFromAssembly(typeof(RobustUnitTest).Assembly);
configurationManager.LoadCVarsFromAssembly(typeof(RTCVars).Assembly);
var systems = deps.Resolve<IEntitySystemManager>();
// Required systems

View File

@@ -1,62 +0,0 @@
using System.Numerics;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Reflection;
namespace Robust.UnitTesting.Server.GameObjects;
[TestFixture]
public sealed partial class ComponentMapInitTest
{
/// <summary>
/// Asserts whether a component added after an entity has fully initialized has MapInit called.
/// </summary>
[Test]
public void ComponentMapInit()
{
var simFactory = RobustServerSimulation.NewSimulation();
simFactory.RegisterComponents(fac =>
{
fac.RegisterClass<MapInitTestComponent>();
}).RegisterEntitySystems(fac =>
{
fac.LoadExtraSystemType<MapInitTestSystem>();
});
var sim = simFactory.InitializeInstance();
var entManager = sim.Resolve<IEntityManager>();
var mapSystem = entManager.System<SharedMapSystem>();
mapSystem.CreateMap(out var mapId);
var ent = entManager.SpawnEntity(null, new MapCoordinates(Vector2.Zero, mapId));
Assert.That(entManager.GetComponent<MetaDataComponent>(ent).EntityLifeStage, Is.EqualTo(EntityLifeStage.MapInitialized));
var comp = entManager.AddComponent<MapInitTestComponent>(ent);
Assert.That(comp.Count, Is.EqualTo(1));
mapSystem.DeleteMap(mapId);
}
[Reflect(false)]
private sealed class MapInitTestSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<MapInitTestComponent, MapInitEvent>(OnMapInitTestMapInit);
}
private void OnMapInitTestMapInit(EntityUid uid, MapInitTestComponent component, MapInitEvent args)
{
component.Count += 1;
}
}
[Reflect(false)]
private sealed partial class MapInitTestComponent : Component
{
public int Count = 0;
}
}

View File

@@ -1,337 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Robust.Server.Containers;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.UnitTesting.Server.GameObjects.Components
{
[TestFixture, Parallelizable]
public sealed partial class ContainerTest
{
private static EntityCoordinates _coords;
private static ISimulation SimulationFactory()
{
var sim = RobustServerSimulation
.NewSimulation()
.InitializeInstance();
var map = sim.CreateMap();
_coords = new EntityCoordinates(map.Item1, default);
return sim;
}
[Test]
public void TestCreation()
{
var sim = SimulationFactory();
var entManager = sim.Resolve<IEntityManager>();
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
var entity = sim.SpawnEntity(null,_coords);
var container = containerSys.MakeContainer<Container>(entity, "dummy");
Assert.That(container.ID, Is.EqualTo("dummy"));
Assert.That(container.Owner, Is.EqualTo(entity));
var manager = entManager.GetComponent<ContainerManagerComponent>(entity);
Assert.That(container.Manager, Is.EqualTo(manager));
Assert.That(() => containerSys.MakeContainer<Container>(entity, "dummy"), Throws.ArgumentException);
Assert.That(containerSys.HasContainer(entity, "dummy2", manager), Is.False);
var container2 = containerSys.MakeContainer<Container>(entity, "dummy2");
Assert.That(container2.Manager, Is.EqualTo(manager));
Assert.That(container2.Owner, Is.EqualTo(entity));
Assert.That(container2.ID, Is.EqualTo("dummy2"));
Assert.That(containerSys.HasContainer(entity, "dummy", manager), Is.True);
Assert.That(containerSys.HasContainer(entity, "dummy2",manager), Is.True);
Assert.That(containerSys.HasContainer(entity, "dummy3", manager), Is.False);
Assert.That(containerSys.GetContainer(entity, "dummy", manager), Is.EqualTo(container));
Assert.That(containerSys.GetContainer(entity, "dummy2", manager), Is.EqualTo(container2));
Assert.That(() => containerSys.GetContainer(entity, "dummy3", manager), Throws.TypeOf<KeyNotFoundException>());
entManager.DeleteEntity(entity);
}
[Test]
public void TestInsertion()
{
var sim = SimulationFactory();
var entManager = sim.Resolve<IEntityManager>();
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
var owner = sim.SpawnEntity(null,_coords);
var inserted = sim.SpawnEntity(null,_coords);
var transform = entManager.GetComponent<TransformComponent>(inserted);
var container = containerSys.MakeContainer<Container>(owner, "dummy");
Assert.That(containerSys.Insert(inserted, container), Is.True);
Assert.That(transform.ParentUid, Is.EqualTo(owner));
var container2 = containerSys.MakeContainer<Container>(inserted, "dummy");
Assert.That(containerSys.Insert(owner, container2), Is.False);
var success = containerSys.Remove(inserted, container);
Assert.That(success, Is.True);
success = containerSys.Remove(inserted, container);
Assert.That(success, Is.False);
containerSys.Insert(inserted, container);
entManager.DeleteEntity(owner);
// Make sure inserted was detached.
Assert.That(transform.Deleted, Is.True);
}
[Test]
public void TestNestedRemoval()
{
var sim = SimulationFactory();
var entManager = sim.Resolve<IEntityManager>();
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
var owner = sim.SpawnEntity(null,_coords);
var inserted = sim.SpawnEntity(null,_coords);
var transform = entManager.GetComponent<TransformComponent>(inserted);
var entity = sim.SpawnEntity(null,_coords);
var container = containerSys.MakeContainer<Container>(owner, "dummy");
Assert.That(containerSys.Insert(inserted, container), Is.True);
Assert.That(transform.ParentUid, Is.EqualTo(owner));
var container2 = containerSys.MakeContainer<Container>(inserted, "dummy");
Assert.That(containerSys.Insert(entity, container2), Is.True);
Assert.That(entManager.GetComponent<TransformComponent>(entity).ParentUid, Is.EqualTo(inserted));
Assert.That(containerSys.Remove(entity, container2), Is.True);
Assert.That(container.Contains(entity), Is.True);
Assert.That(entManager.GetComponent<TransformComponent>(entity).ParentUid, Is.EqualTo(owner));
entManager.DeleteEntity(owner);
Assert.That(transform.Deleted, Is.True);
}
[Test]
public void TestNestedRemovalWithDenial()
{
var sim = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
var coordinates =_coords;
var entityOne = sim.SpawnEntity(null, coordinates);
var entityTwo = sim.SpawnEntity(null, coordinates);
var entityThree = sim.SpawnEntity(null, coordinates);
var entityItem = sim.SpawnEntity(null, coordinates);
var container = containerSys.MakeContainer<Container>(entityOne, "dummy");
var container2 = containerSys.MakeContainer<ContainerOnlyContainer>(entityTwo, "dummy");
var container3 = containerSys.MakeContainer<Container>(entityThree, "dummy");
Assert.That(containerSys.Insert(entityTwo, container), Is.True);
Assert.That(entMan.GetComponent<TransformComponent>(entityTwo).ParentUid, Is.EqualTo(entityOne));
Assert.That(containerSys.Insert(entityThree, container2), Is.True);
Assert.That(entMan.GetComponent<TransformComponent>(entityThree).ParentUid, Is.EqualTo(entityTwo));
Assert.That(containerSys.Insert(entityItem, container3), Is.True);
Assert.That(entMan.GetComponent<TransformComponent>(entityItem).ParentUid, Is.EqualTo(entityThree));
Assert.That(containerSys.Remove(entityItem, container3), Is.True);
Assert.That(container.Contains(entityItem), Is.True);
Assert.That(entMan.GetComponent<TransformComponent>(entityItem).ParentUid, Is.EqualTo(entityOne));
entMan.DeleteEntity(entityOne);
Assert.That(entMan.Deleted(entityOne), Is.True);
}
[Test]
public void BaseContainer_SelfInsert_False()
{
var sim = SimulationFactory();
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
var entity = sim.SpawnEntity(null,_coords);
var container = containerSys.MakeContainer<Container>(entity, "dummy");
Assert.That(containerSys.Insert(entity, container), Is.False);
Assert.That(containerSys.CanInsert(entity, container), Is.False);
}
[Test]
public void BaseContainer_InsertMap_False()
{
var sim = SimulationFactory();
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
var mapEnt = new EntityUid(1);
var entity = sim.SpawnEntity(null,_coords);
var container = containerSys.MakeContainer<Container>(entity, "dummy");
Assert.That(containerSys.Insert(mapEnt, container), Is.False);
Assert.That(containerSys.CanInsert(mapEnt, container), Is.False);
}
[Test]
public void BaseContainer_InsertGrid_False()
{
var sim = SimulationFactory();
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
var grid = sim.Resolve<IMapManager>().CreateGridEntity(new MapId(1)).Owner;
var entity = sim.SpawnEntity(null,_coords);
var container = containerSys.MakeContainer<Container>(entity, "dummy");
Assert.That(containerSys.Insert(grid, container), Is.False);
Assert.That(containerSys.CanInsert(grid, container), Is.False);
}
[Test]
public void BaseContainer_Insert_True()
{
var sim = SimulationFactory();
var entManager = sim.Resolve<IEntityManager>();
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
var containerEntity = sim.SpawnEntity(null,_coords);
var container = containerSys.MakeContainer<Container>(containerEntity, "dummy");
var insertEntity = sim.SpawnEntity(null,_coords);
var result = containerSys.Insert(insertEntity, container);
Assert.That(result, Is.True);
Assert.That(container.ContainedEntities.Count, Is.EqualTo(1));
Assert.That(entManager.GetComponent<TransformComponent>(containerEntity).ChildCount, Is.EqualTo(1));
Assert.That(entManager.GetComponent<TransformComponent>(containerEntity)._children.First(), Is.EqualTo(insertEntity));
result = containerSys.TryGetContainingContainer(insertEntity, out var resultContainerMan);
Assert.That(result, Is.True);
Assert.That(resultContainerMan?.Manager, Is.EqualTo(container.Manager));
}
[Test]
public void BaseContainer_RemoveNotAdded_False()
{
var sim = SimulationFactory();
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
var containerEntity = sim.SpawnEntity(null,_coords);
var container = containerSys.MakeContainer<Container>(containerEntity, "dummy");
var insertEntity = sim.SpawnEntity(null,_coords);
var result = containerSys.Remove(insertEntity, container);
Assert.That(result, Is.False);
}
[Test]
public void BaseContainer_Transfer_True()
{
var sim = SimulationFactory();
var containerSys = sim.Resolve<IEntitySystemManager>().GetEntitySystem<ContainerSystem>();
var entity1 = sim.SpawnEntity(null,_coords);
var container1 = containerSys.MakeContainer<Container>(entity1, "dummy");
var entity2 = sim.SpawnEntity(null,_coords);
var container2 = containerSys.MakeContainer<Container>(entity2, "dummy");
var transferEntity = sim.SpawnEntity(null,_coords);
containerSys.Insert(transferEntity, container1);
var result = containerSys.Insert(transferEntity, container2);
Assert.That(result, Is.True);
Assert.That(container1.ContainedEntities.Count, Is.EqualTo(0));
Assert.That(container2.ContainedEntities.Count, Is.EqualTo(1));
}
[Test]
public void Container_Serialize()
{
var sim = SimulationFactory();
var entManager = sim.Resolve<IEntityManager>();
var containerSys = entManager.System<ContainerSystem>();
var entity = sim.SpawnEntity(null,_coords);
var container = containerSys.MakeContainer<Container>(entity, "dummy");
var childEnt = sim.SpawnEntity(null,_coords);
container.OccludesLight = true;
container.ShowContents = true;
containerSys.Insert(childEnt, container);
var containerMan = entManager.GetComponent<ContainerManagerComponent>(entity);
var getState = new ComponentGetState();
entManager.EventBus.RaiseComponentEvent(entity, containerMan, ref getState);
var state = (ContainerManagerComponent.ContainerManagerComponentState)getState.State!;
Assert.That(state.Containers, Has.Count.EqualTo(1));
var cont = state.Containers.Values.First();
Assert.That(state.Containers.Keys.First(), Is.EqualTo("dummy"));
Assert.That(cont.OccludesLight, Is.True);
Assert.That(cont.ShowContents, Is.True);
Assert.That(cont.ContainedEntities.Count, Is.EqualTo(1));
Assert.That(cont.ContainedEntities[0], Is.EqualTo(entManager.GetNetEntity(childEnt)));
}
[SerializedType(nameof(ContainerOnlyContainer))]
private sealed partial class ContainerOnlyContainer : BaseContainer
{
/// <summary>
/// The generic container class uses a list of entities
/// </summary>
private readonly List<EntityUid> _containerList = new();
public override int Count => _containerList.Count;
/// <inheritdoc />
public override IReadOnlyList<EntityUid> ContainedEntities => _containerList;
/// <inheritdoc />
protected internal override void InternalInsert(EntityUid toInsert, IEntityManager entMan)
{
_containerList.Add(toInsert);
}
/// <inheritdoc />
protected internal override void InternalRemove(EntityUid toRemove, IEntityManager entMan)
{
_containerList.Remove(toRemove);
}
/// <inheritdoc />
public override bool Contains(EntityUid contained)
{
if (!_containerList.Contains(contained))
return false;
if (IoCManager.Resolve<IGameTiming>().ApplyingState)
return true;
var flags = IoCManager.Resolve<IEntityManager>().GetComponent<MetaDataComponent>(contained).Flags;
DebugTools.Assert((flags & MetaDataFlags.InContainer) != 0);
return true;
}
/// <inheritdoc />
protected internal override void InternalShutdown(IEntityManager entMan, SharedContainerSystem system, bool isClient)
{
foreach (var entity in _containerList.ToArray())
{
if (!isClient)
entMan.DeleteEntity(entity);
else if (entMan.EntityExists(entity))
system.Remove(entity, this, reparent: false, force: true);
}
}
protected internal override bool CanInsert(EntityUid toinsert, bool assumeEmpty, IEntityManager entMan)
{
return entMan.HasComponent<ContainerManagerComponent>(toinsert);
}
}
}
}

View File

@@ -1,57 +0,0 @@
using System.Numerics;
using NUnit.Framework;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
namespace Robust.UnitTesting.Server.GameObjects.Components;
[TestFixture]
public sealed class TransformIntegration_Test
{
/// <summary>
/// Asserts that calling SetWorldPosition while in a container correctly removes the entity from its container.
/// </summary>
[Test]
public void WorldPositionContainerSet()
{
var factory = RobustServerSimulation.NewSimulation();
var sim = factory.InitializeInstance();
var entManager = sim.Resolve<IEntityManager>();
var containerSystem = entManager.System<SharedContainerSystem>();
var xformSystem = entManager.System<SharedTransformSystem>();
var map1 = sim.CreateMap().Uid;
var ent1 = entManager.SpawnEntity(null, new EntityCoordinates(map1, Vector2.Zero));
var ent2 = entManager.SpawnEntity(null, new EntityCoordinates(map1, Vector2.Zero));
var ent3 = entManager.SpawnEntity(null, new EntityCoordinates(map1, Vector2.Zero));
var container = containerSystem.EnsureContainer<ContainerSlot>(ent1, "a");
// Assert that setting worldpos updates parent correctly.
containerSystem.Insert(ent2, container, force: true);
Assert.That(containerSystem.IsEntityInContainer(ent2));
xformSystem.SetWorldPosition(ent2, Vector2.One);
Assert.That(!containerSystem.IsEntityInContainer(ent2));
Assert.That(xformSystem.GetWorldPosition(ent2), Is.EqualTo(Vector2.One));
// Assert that you can set recursively contained (but not directly contained) entities correctly.
containerSystem.Insert(ent2, container);
xformSystem.SetParent(ent3, ent2);
Assert.That(xformSystem.GetParentUid(ent3), Is.EqualTo(ent2));
xformSystem.SetWorldPosition(ent3, Vector2.One);
Assert.That(xformSystem.GetParentUid(ent3), Is.EqualTo(map1));
Assert.That(xformSystem.GetWorldPosition(ent3).Equals(Vector2.One));
// Cleanup
entManager.DeleteEntity(map1);
}
}

View File

@@ -1,473 +0,0 @@
using System.IO;
using System.Numerics;
using NUnit.Framework;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Timing;
namespace Robust.UnitTesting.Server.GameObjects.Components
{
[TestFixture]
[TestOf(typeof(TransformComponent))]
sealed class Transform_Test : RobustUnitTest
{
public override UnitTestProject Project => UnitTestProject.Server;
private IEntityManager EntityManager = default!;
private IMapManager MapManager = default!;
private SharedTransformSystem XformSystem => EntityManager.System<SharedTransformSystem>();
const string Prototypes = @"
- type: entity
name: dummy
id: mapDummy
components:
- type: Transform
- type: Map
index: 123
# Due to the map getting initialised last this seemed easiest to fix the test while removing the mocks.
- type: Broadphase
";
private MapId MapA;
private Entity<MapGridComponent> GridA;
private MapId MapB;
private Entity<MapGridComponent> GridB;
private static readonly EntityCoordinates InitialPos = new(EntityUid.FirstUid, new Vector2(0, 0));
[OneTimeSetUp]
public void Setup()
{
IoCManager.Resolve<IComponentFactory>().GenerateNetIds();
EntityManager = IoCManager.Resolve<IEntityManager>();
MapManager = IoCManager.Resolve<IMapManager>();
IoCManager.Resolve<ISerializationManager>().Initialize();
var manager = IoCManager.Resolve<IPrototypeManager>();
manager.RegisterKind(typeof(EntityPrototype), typeof(EntityCategoryPrototype));
manager.LoadFromStream(new StringReader(Prototypes));
manager.ResolveResults();
var mapSys = EntityManager.System<SharedMapSystem>();
// build the net dream
mapSys.CreateMap(out MapA);
mapSys.CreateMap(out MapB);
GridA = MapManager.CreateGridEntity(MapA);
GridB = MapManager.CreateGridEntity(MapB);
//NOTE: The grids have not moved, so we can assert worldpos == localpos for the test
}
[SetUp]
public void ClearSimulation()
{
// One of the tests changes this so we use this to ensure it doesn't get passed to other tests.
IoCManager.Resolve<IGameTiming>().InSimulation = false;
}
[Test]
public void ParentMapSwitchTest()
{
// two entities
var parent = EntityManager.SpawnEntity(null, InitialPos);
var child = EntityManager.SpawnEntity(null, InitialPos);
var parentTrans = EntityManager.GetComponent<TransformComponent>(parent);
var childTrans = EntityManager.GetComponent<TransformComponent>(child);
// that are not on the same map
XformSystem.SetCoordinates(parent, parentTrans, new EntityCoordinates(GridA, new Vector2(5, 5)));
XformSystem.SetCoordinates(child, childTrans, new EntityCoordinates(GridB, new Vector2(4, 4)));
// if they are parented, the child keeps its world position, but moves to the parents map
XformSystem.SetParent(child, childTrans, parent, parentXform: parentTrans);
Assert.Multiple(() =>
{
Assert.That(childTrans.MapID, NUnit.Framework.Is.EqualTo(parentTrans.MapID));
Assert.That(childTrans.GridUid, NUnit.Framework.Is.EqualTo(parentTrans.GridUid));
Assert.That(childTrans.Coordinates, NUnit.Framework.Is.EqualTo(new EntityCoordinates(parent, new Vector2(-1, -1))));
Assert.That(XformSystem.GetWorldPosition(childTrans), NUnit.Framework.Is.EqualTo(new Vector2(4, 4)));
});
// move the parent, and the child should move with it
XformSystem.SetLocalPosition(child, new Vector2(6, 6), childTrans);
XformSystem.SetWorldPosition(parent, new Vector2(-8, -8));
Assert.That(XformSystem.GetWorldPosition(childTrans), NUnit.Framework.Is.EqualTo(new Vector2(-2, -2)));
// if we detach parent, the child should be left where it was, still relative to parents grid
var oldLpos = new Vector2(-2, -2);
var oldWpos = XformSystem.GetWorldPosition(childTrans);
XformSystem.AttachToGridOrMap(child, childTrans);
// the gridId won't match, because we just detached from the grid entity
Assert.Multiple(() =>
{
Assert.That(childTrans.Coordinates.Position, NUnit.Framework.Is.EqualTo(oldLpos));
Assert.That(XformSystem.GetWorldPosition(childTrans), NUnit.Framework.Is.EqualTo(oldWpos));
});
}
/// <summary>
/// Tests that a child entity does not move when attaching to a parent.
/// </summary>
[Test]
public void ParentAttachMoveTest()
{
// Arrange
var parent = EntityManager.SpawnEntity(null, InitialPos);
var child = EntityManager.SpawnEntity(null, InitialPos);
var parentTrans = EntityManager.GetComponent<TransformComponent>(parent);
var childTrans = EntityManager.GetComponent<TransformComponent>(child);
XformSystem.SetWorldPosition(parent, new Vector2(5, 5));
XformSystem.SetWorldPosition(child, new Vector2(6, 6));
// Act
var oldWpos = XformSystem.GetWorldPosition(childTrans);
XformSystem.SetParent(child, childTrans, parent, parentXform: parentTrans);
var newWpos = XformSystem.GetWorldPosition(childTrans);
// Assert
Assert.That(oldWpos, NUnit.Framework.Is.EqualTo(newWpos));
}
/// <summary>
/// Tests that a child entity does not move when attaching to a parent.
/// </summary>
[Test]
public void ParentDoubleAttachMoveTest()
{
// Arrange
var parent = EntityManager.SpawnEntity(null, InitialPos);
var childOne = EntityManager.SpawnEntity(null, InitialPos);
var childTwo = EntityManager.SpawnEntity(null, InitialPos);
var parentTrans = EntityManager.GetComponent<TransformComponent>(parent);
var childOneTrans = EntityManager.GetComponent<TransformComponent>(childOne);
var childTwoTrans = EntityManager.GetComponent<TransformComponent>(childTwo);
XformSystem.SetWorldPosition(parent, new Vector2(1, 1));
XformSystem.SetWorldPosition(childOne, new Vector2(2, 2));
XformSystem.SetWorldPosition(childTwo, new Vector2(3, 3));
// Act
var oldWpos = XformSystem.GetWorldPosition(childOneTrans);
XformSystem.SetParent(childOne, childOneTrans, parent, parentXform: parentTrans);
var newWpos = XformSystem.GetWorldPosition(childOneTrans);
Assert.That(oldWpos, NUnit.Framework.Is.EqualTo(newWpos));
oldWpos = XformSystem.GetWorldPosition(childTwoTrans);
XformSystem.SetParent(childOne, childOneTrans, parent, parentXform: parentTrans);
newWpos = XformSystem.GetWorldPosition(childTwoTrans);
Assert.That(oldWpos, NUnit.Framework.Is.EqualTo(newWpos));
oldWpos = XformSystem.GetWorldPosition(childTwoTrans);
XformSystem.SetParent(childTwo, childTwoTrans, childOne, parentXform: childOneTrans);
newWpos = XformSystem.GetWorldPosition(childTwoTrans);
Assert.That(oldWpos, NUnit.Framework.Is.EqualTo(newWpos));
}
/// <summary>
/// Tests that the entity orbits properly when the parent rotates.
/// </summary>
[Test]
public void ParentRotateTest()
{
// Arrange
var parent = EntityManager.SpawnEntity(null, InitialPos);
var child = EntityManager.SpawnEntity(null, InitialPos);
var parentTrans = EntityManager.GetComponent<TransformComponent>(parent);
var childTrans = EntityManager.GetComponent<TransformComponent>(child);
XformSystem.SetWorldPosition(parent, new Vector2(0, 0));
XformSystem.SetWorldPosition(child, new Vector2(2, 0));
XformSystem.SetParent(child, childTrans, parent, parentXform: parentTrans);
//Act
parentTrans.LocalRotation = new Angle(MathHelper.Pi / 2);
//Assert
var result = XformSystem.GetWorldPosition(childTrans);
Assert.Multiple(() =>
{
Assert.That(MathHelper.CloseToPercent(result.X, 0));
Assert.That(MathHelper.CloseToPercent(result.Y, 2));
});
}
/// <summary>
/// Tests that the entity orbits properly when the parent rotates and is not at the origin.
/// </summary>
[Test]
public void ParentTransRotateTest()
{
// Arrange
var parent = EntityManager.SpawnEntity(null, InitialPos);
var child = EntityManager.SpawnEntity(null, InitialPos);
var parentTrans = EntityManager.GetComponent<TransformComponent>(parent);
var childTrans = EntityManager.GetComponent<TransformComponent>(child);
XformSystem.SetWorldPosition(parent, new Vector2(1, 1));
XformSystem.SetWorldPosition(child, new Vector2(2, 1));
XformSystem.SetParent(child, childTrans, parent, parentXform: parentTrans);
//Act
parentTrans.LocalRotation = new Angle(MathHelper.Pi / 2);
//Assert
var result = XformSystem.GetWorldPosition(childTrans);
Assert.Multiple(() =>
{
Assert.That(MathHelper.CloseToPercent(result.X, 1));
Assert.That(MathHelper.CloseToPercent(result.Y, 2));
});
}
/// <summary>
/// Tests to see if parenting multiple entities with WorldPosition places the leaf properly.
/// </summary>
[Test]
public void PositionCompositionTest()
{
// Arrange
var node1 = EntityManager.SpawnEntity(null, InitialPos);
var node2 = EntityManager.SpawnEntity(null, InitialPos);
var node3 = EntityManager.SpawnEntity(null, InitialPos);
var node4 = EntityManager.SpawnEntity(null, InitialPos);
var node1Trans = EntityManager.GetComponent<TransformComponent>(node1);
var node2Trans = EntityManager.GetComponent<TransformComponent>(node2);
var node3Trans = EntityManager.GetComponent<TransformComponent>(node3);
var node4Trans = EntityManager.GetComponent<TransformComponent>(node4);
XformSystem.SetWorldPosition(node1, new Vector2(0, 0));
XformSystem.SetWorldPosition(node2, new Vector2(1, 1));
XformSystem.SetWorldPosition(node3, new Vector2(2, 2));
XformSystem.SetWorldPosition(node4, new Vector2(0, 2));
XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans);
XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans);
XformSystem.SetParent(node4, node4Trans, node3, parentXform: node3Trans);
//Act
node1Trans.LocalRotation = new Angle(MathHelper.Pi / 2);
//Assert
var result = XformSystem.GetWorldPosition(node4Trans);
Assert.Multiple(() =>
{
Assert.That(result.X, new ApproxEqualityConstraint(-2f));
Assert.That(result.Y, new ApproxEqualityConstraint(0f));
});
}
/// <summary>
/// Tests to see if setting the world position of a child causes position rounding errors.
/// </summary>
[Test]
public void ParentLocalPositionRoundingErrorTest()
{
// Arrange
var node1 = EntityManager.SpawnEntity(null, InitialPos);
var node2 = EntityManager.SpawnEntity(null, InitialPos);
var node3 = EntityManager.SpawnEntity(null, InitialPos);
var node1Trans = EntityManager.GetComponent<TransformComponent>(node1);
var node2Trans = EntityManager.GetComponent<TransformComponent>(node2);
var node3Trans = EntityManager.GetComponent<TransformComponent>(node3);
XformSystem.SetWorldPosition(node1, new Vector2(0, 0));
XformSystem.SetWorldPosition(node2, new Vector2(1, 1));
XformSystem.SetWorldPosition(node3, new Vector2(2, 2));
XformSystem.SetParent(node1, node1Trans, node2, parentXform: node2Trans);
XformSystem.SetParent(node2, node2Trans, node3, parentXform: node3Trans);
// Act
var oldWpos = XformSystem.GetWorldPosition(node3Trans);
for (var i = 0; i < 10000; i++)
{
var dx = i % 2 == 0 ? 5 : -5;
XformSystem.SetLocalPosition(node1, node1Trans.LocalPosition + new Vector2(dx, dx), node1Trans);
XformSystem.SetLocalPosition(node2, node2Trans.LocalPosition + new Vector2(dx, dx), node2Trans);
XformSystem.SetLocalPosition(node3, node3Trans.LocalPosition + new Vector2(dx, dx), node3Trans);
}
var newWpos = XformSystem.GetWorldPosition(node3Trans);
// Assert
Assert.Multiple(() =>
{
Assert.That(MathHelper.CloseToPercent(oldWpos.X, newWpos.Y), $"{oldWpos.X} should be {newWpos.Y}");
Assert.That(MathHelper.CloseToPercent(oldWpos.Y, newWpos.Y), newWpos.ToString);
});
}
/// <summary>
/// Tests to see if rotating a parent causes major child position rounding errors.
/// </summary>
[Test]
public void ParentRotationRoundingErrorTest()
{
IoCManager.Resolve<IGameTiming>().InSimulation = true;
// Arrange
var node1 = EntityManager.SpawnEntity(null, InitialPos);
var node2 = EntityManager.SpawnEntity(null, InitialPos);
var node3 = EntityManager.SpawnEntity(null, InitialPos);
var node1Trans = EntityManager.GetComponent<TransformComponent>(node1);
var node2Trans = EntityManager.GetComponent<TransformComponent>(node2);
var node3Trans = EntityManager.GetComponent<TransformComponent>(node3);
XformSystem.SetWorldPosition(node1, new Vector2(0, 0));
XformSystem.SetWorldPosition(node2, new Vector2(1, 1));
XformSystem.SetWorldPosition(node3, new Vector2(2, 2));
XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans);
XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans);
// Act
var oldWpos = XformSystem.GetWorldPosition(node3Trans);
for (var i = 0; i < 100; i++)
{
node1Trans.LocalRotation += new Angle(MathHelper.Pi);
node2Trans.LocalRotation += new Angle(MathHelper.Pi);
node3Trans.LocalRotation += new Angle(MathHelper.Pi);
}
var newWpos = XformSystem.GetWorldPosition(node3Trans);
//NOTE: Yes, this does cause a non-zero error
// Assert
Assert.Multiple(() =>
{
Assert.That(MathHelper.CloseToPercent(oldWpos.X, newWpos.Y, 0.0001f));
Assert.That(MathHelper.CloseToPercent(oldWpos.Y, newWpos.Y, 0.0001f));
});
}
/// <summary>
/// Tests that the world and inverse world transforms are built properly.
/// </summary>
[Test]
public void TreeComposeWorldMatricesTest()
{
// Arrange
var control = Matrix3x2.Identity;
var node1 = EntityManager.SpawnEntity(null, InitialPos);
var node2 = EntityManager.SpawnEntity(null, InitialPos);
var node3 = EntityManager.SpawnEntity(null, InitialPos);
var node4 = EntityManager.SpawnEntity(null, InitialPos);
var node1Trans = EntityManager.GetComponent<TransformComponent>(node1);
var node2Trans = EntityManager.GetComponent<TransformComponent>(node2);
var node3Trans = EntityManager.GetComponent<TransformComponent>(node3);
var node4Trans = EntityManager.GetComponent<TransformComponent>(node4);
XformSystem.SetWorldPosition(node1, new Vector2(0, 0));
XformSystem.SetWorldPosition(node2, new Vector2(1, 1));
XformSystem.SetWorldPosition(node3, new Vector2(2, 2));
XformSystem.SetWorldPosition(node4, new Vector2(0, 2));
XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans);
XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans);
XformSystem.SetParent(node4, node4Trans, node3, parentXform: node3Trans);
//Act
node1Trans.LocalRotation = new Angle(MathHelper.Pi / 6.37);
XformSystem.SetWorldPosition(node1, new Vector2(1, 1));
var worldMat = XformSystem.GetWorldMatrix(node4Trans);
var invWorldMat = XformSystem.GetInvWorldMatrix(node4Trans);
var leftVerifyMatrix = Matrix3x2.Multiply(worldMat, invWorldMat);
var rightVerifyMatrix = Matrix3x2.Multiply(invWorldMat, worldMat);
//Assert
Assert.Multiple(() =>
{
// these should be the same (A × A-1 = A-1 × A = I)
Assert.That(leftVerifyMatrix, new ApproxEqualityConstraint(rightVerifyMatrix));
// verify matrix == identity matrix (or very close to because float precision)
Assert.That(leftVerifyMatrix, new ApproxEqualityConstraint(control));
});
}
/// <summary>
/// Tests that world rotation is built properly
/// </summary>
[Test]
public void WorldRotationTest()
{
// Arrange
var node1 = EntityManager.SpawnEntity(null, InitialPos);
var node2 = EntityManager.SpawnEntity(null, InitialPos);
var node3 = EntityManager.SpawnEntity(null, InitialPos);
var node1Trans = EntityManager.GetComponent<TransformComponent>(node1);
var node2Trans = EntityManager.GetComponent<TransformComponent>(node2);
var node3Trans = EntityManager.GetComponent<TransformComponent>(node3);
XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans);
XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans);
node1Trans.LocalRotation = Angle.FromDegrees(0);
node2Trans.LocalRotation = Angle.FromDegrees(45);
node3Trans.LocalRotation = Angle.FromDegrees(45);
// Act
node1Trans.LocalRotation = Angle.FromDegrees(135);
// Assert (135 + 45 + 45 = 225)
var result = XformSystem.GetWorldRotation(node3Trans);
Assert.That(result, new ApproxEqualityConstraint(Angle.FromDegrees(225)));
}
/// <summary>
/// Test that, in a chain A -> B -> C, if A is moved C's world position correctly updates.
/// </summary>
[Test]
public void MatrixUpdateTest()
{
var node1 = EntityManager.SpawnEntity(null, InitialPos);
var node2 = EntityManager.SpawnEntity(null, InitialPos);
var node3 = EntityManager.SpawnEntity(null, InitialPos);
var node1Trans = EntityManager.GetComponent<TransformComponent>(node1);
var node2Trans = EntityManager.GetComponent<TransformComponent>(node2);
var node3Trans = EntityManager.GetComponent<TransformComponent>(node3);
XformSystem.SetParent(node2, node2Trans, node1, parentXform: node1Trans);
XformSystem.SetParent(node3, node3Trans, node2, parentXform: node2Trans);
XformSystem.SetLocalPosition(node3, new Vector2(5, 5), node3Trans);
XformSystem.SetLocalPosition(node2, new Vector2(5, 5), node2Trans);
XformSystem.SetLocalPosition(node1, new Vector2(5, 5), node1Trans);
Assert.That(XformSystem.GetWorldPosition(node3Trans), new ApproxEqualityConstraint(new Vector2(15, 15)));
}
/*
* There used to be a TestMapInitOrder test here. The problem is that the actual game will probably explode if
* you start initialising children before parents and the test only worked because of specific setup being done
* to prevent this in its use case.
*/
}
}

View File

@@ -1,46 +0,0 @@
using Moq;
using NUnit.Framework;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Network;
using Robust.Shared.Network.Messages;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.UnitTesting.Server.GameObjects
{
public sealed class ServerEntityNetworkManagerTest
{
[Test]
public void TestMessageSort()
{
var tickA = new GameTick(5);
var tickB = new GameTick(3);
var channel = new Mock<INetChannel>().Object;
var msgA = new MsgEntity() {MsgChannel = channel, Type = EntityMessageType.SystemMessage, SourceTick = tickA, Sequence = 10};
var msgB = new MsgEntity() {MsgChannel = channel, Type = EntityMessageType.SystemMessage, SourceTick = tickA, Sequence = 13};
var msgC = new MsgEntity() {MsgChannel = channel, Type = EntityMessageType.SystemMessage, SourceTick = tickA, Sequence = 12};
var msgD = new MsgEntity() {MsgChannel = channel, Type = EntityMessageType.SystemMessage, SourceTick = tickA, Sequence = 14};
var msgE = new MsgEntity() {MsgChannel = channel, Type = EntityMessageType.SystemMessage, SourceTick = tickB, Sequence = 7};
var msgF = new MsgEntity() {MsgChannel = channel, Type = EntityMessageType.SystemMessage, SourceTick = tickB, Sequence = 4};
var pq = new PriorityQueue<MsgEntity>(new ServerEntityManager.MessageSequenceComparer())
{
msgA,
msgB,
msgC,
msgD,
msgE,
msgF
};
Assert.That(pq.Take(), Is.EqualTo(msgF));
Assert.That(pq.Take(), Is.EqualTo(msgE));
Assert.That(pq.Take(), Is.EqualTo(msgA));
Assert.That(pq.Take(), Is.EqualTo(msgC));
Assert.That(pq.Take(), Is.EqualTo(msgB));
Assert.That(pq.Take(), Is.EqualTo(msgD));
}
}
}

View File

@@ -1,56 +0,0 @@
using System.Linq;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
namespace Robust.UnitTesting.Server.GameObjects
{
[TestFixture]
public sealed class ThrowingEntityDeletion_Test
{
private ISimulation _sim = default!;
const string PROTOTYPES = @"
- type: entity
id: throwInAdd
components:
- type: DebugExceptionOnAdd
- type: entity
id: throwsInInitialize
components:
- type: DebugExceptionInitialize
- type: entity
id: throwsInStartup
components:
- type: DebugExceptionStartup
";
[OneTimeSetUp]
public void Setup()
{
_sim = RobustServerSimulation.NewSimulation()
.RegisterComponents(f =>
{
f.RegisterClass<DebugExceptionOnAddComponent>();
f.RegisterClass<DebugExceptionInitializeComponent>();
f.RegisterClass<DebugExceptionStartupComponent>();
})
.RegisterEntitySystems(f => f.LoadExtraSystemType<DebugExceptionSystem>())
.RegisterPrototypes(protoMan => protoMan.LoadString(PROTOTYPES))
.InitializeInstance();
}
[TestCase("throwInAdd")]
[TestCase("throwsInInitialize")]
[TestCase("throwsInStartup")]
public void Test(string prototypeName)
{
var entMan = _sim.Resolve<IEntityManager>();
_sim.Resolve<IEntityManager>().System<SharedMapSystem>().CreateMap(out var map);
Assert.That(() => entMan.SpawnEntity(prototypeName, new MapCoordinates(0, 0, map)),
Throws.TypeOf<EntityCreationException>());
Assert.That(entMan.GetEntities().Where(p => entMan.GetComponent<MetaDataComponent>(p).EntityPrototype?.ID == prototypeName), Is.Empty);
}
}
}

View File

@@ -1,112 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Server.Player;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Network;
namespace Robust.UnitTesting.Server.GameStates;
public sealed class DefaultEntityTest : RobustIntegrationTest
{
/// <summary>
/// Simple test that just spawns a default entity without any components or modifications and checks that the
/// client receives the entity.
/// </summary>
[Test]
public async Task TestSpawnDefaultEntity()
{
var server = StartServer();
var client = StartClient();
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
var sEntMan = server.ResolveDependency<IEntityManager>();
var cEntMan = client.ResolveDependency<IEntityManager>();
var netMan = client.ResolveDependency<IClientNetManager>();
var playerMan = server.ResolveDependency<IPlayerManager>();
var confMan = server.ResolveDependency<IConfigurationManager>();
client.SetConnectTarget(server);
client.Post(() => netMan.ClientConnect(null!, 0, null!));
server.Post(() => confMan.SetCVar(CVars.NetPVS, false));
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
var session = playerMan.Sessions.First();
await server.WaitPost(() => playerMan.JoinGame(session));
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Spawn a default unmodified entity.
NetEntity ent = default;
await server.WaitPost(() =>
{
ent = sEntMan.GetNetEntity(sEntMan.Spawn());
});
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Check that server & client both think the entity exists.
Assert.That(sEntMan.EntityExists(sEntMan.GetEntity(ent)));
Assert.That(cEntMan.EntityExists(cEntMan.GetEntity(ent)));
// Enable PVS and repeat the test.
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
// Set up map and spawn player entity
NetEntity player = default;
EntityCoordinates coords = default!;
await server.WaitPost(() =>
{
var map = server.System<SharedMapSystem>().CreateMap();
coords = new(map, default);
var playerUid = sEntMan.SpawnEntity(null, coords);
player = sEntMan.GetNetEntity(playerUid);
server.PlayerMan.SetAttachedEntity(session, playerUid);
});
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
Assert.That(sEntMan.EntityExists(sEntMan.GetEntity(player)));
Assert.That(cEntMan.EntityExists(cEntMan.GetEntity(player)));
await server.WaitPost(() =>
{
ent = sEntMan.GetNetEntity(sEntMan.SpawnAtPosition(null, coords));
});
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
Assert.That(sEntMan.EntityExists(sEntMan.GetEntity(ent)));
Assert.That(cEntMan.EntityExists(cEntMan.GetEntity(ent)));
await client.WaitPost(() => netMan.ClientDisconnect(""));
await server.WaitRunTicks(5);
await client.WaitRunTicks(5);
}
}

View File

@@ -1,450 +0,0 @@
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Player;
namespace Robust.UnitTesting.Server.GameStates;
public sealed class DetachedParentTest : RobustIntegrationTest
{
/// <summary>
/// Check that the client can handle an entity getting attached to an entity that is outside of their PVS range, or
/// that they have never seen. Previously this could result in entities with improperly assigned GridUids due to
/// an existing/initialized entity being attached to an un-initialized entity on an already initialized grid.
/// </summary>
[Test]
public async Task TestDetachedParent()
{
var server = StartServer(new() {Pool = false});
var client = StartClient(new() {Pool = false});
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
var mapSys = server.System<SharedMapSystem>();
var xformSys = server.System<SharedTransformSystem>();
var mapMan = server.ResolveDependency<IMapManager>();
var sEntMan = server.ResolveDependency<IEntityManager>();
var confMan = server.ResolveDependency<IConfigurationManager>();
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
var netMan = client.ResolveDependency<IClientNetManager>();
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
client.Post(() => netMan.ClientConnect(null!, 0, null!));
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
for (var i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Ensure client & server ticks are synced.
// Client runs 1 tick ahead
{
var sTick = (int)server.Timing.CurTick.Value;
var cTick = (int)client.Timing.CurTick.Value;
var delta = cTick - sTick;
if (delta > 1)
await server.WaitRunTicks(delta - 1);
else if (delta < 1)
await client.WaitRunTicks(1 - delta);
sTick = (int)server.Timing.CurTick.Value;
cTick = (int)client.Timing.CurTick.Value;
delta = cTick - sTick;
Assert.That(delta, Is.EqualTo(1));
}
// Set up map and spawn player
MapId mapId = default;
EntityUid map = default;
EntityUid grid = default;
EntityUid parent = default;
EntityUid player = default;
EntityUid child = default;
EntityCoordinates gridCoords = default;
EntityCoordinates mapCoords = default;
await server.WaitPost(() =>
{
// Cycle through some EntityUids to avoid server-side and client-side uids accidentally matching up.
// I made a mistake earlier in this test where I used a server-side uid on the client
for (var i = 0; i < 10; i++)
{
server.EntMan.DeleteEntity(server.EntMan.SpawnEntity(null, MapCoordinates.Nullspace));
}
map = mapSys.CreateMap(out mapId);
var gridEnt = mapMan.CreateGridEntity(mapId);
mapSys.SetTile(gridEnt.Owner, gridEnt.Comp, Vector2i.Zero, new Tile(1));
gridCoords = new EntityCoordinates(gridEnt, .5f, .5f);
mapCoords = new EntityCoordinates(map, 200, 200);
grid = gridEnt.Owner;
parent = sEntMan.SpawnEntity(null, gridCoords);
player = sEntMan.SpawnEntity(null, gridCoords);
child = sEntMan.SpawnEntity(null, mapCoords);
// Attach player.
var session = sPlayerMan.Sessions.First();
server.PlayerMan.SetAttachedEntity(session, player);
sPlayerMan.JoinGame(session);
});
for (var i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Check that transforms are as expected.
var childX = server.Transform(child);
var parentX = server.Transform(parent);
var playerX = server.Transform(player);
var gridX = server.Transform(grid);
Assert.That(childX.MapID, Is.EqualTo(mapId));
Assert.That(parentX.MapID, Is.EqualTo(mapId));
Assert.That(playerX.MapID, Is.EqualTo(mapId));
Assert.That(gridX.MapID, Is.EqualTo(mapId));
Assert.That(childX.ParentUid, Is.EqualTo(map));
Assert.That(parentX.ParentUid, Is.EqualTo(grid));
Assert.That(playerX.ParentUid, Is.EqualTo(grid));
Assert.That(gridX.ParentUid, Is.EqualTo(map));
Assert.That(childX.GridUid, Is.Null);
Assert.That(parentX.GridUid, Is.EqualTo(grid));
Assert.That(playerX.GridUid, Is.EqualTo(grid));
Assert.That(gridX.GridUid, Is.EqualTo(grid));
// Check that the player received the entities, and that their transforms are as expected.
// Note that the child entity should be outside of PVS range.
var cMap = client.EntMan.GetEntity(server.EntMan.GetNetEntity(map));
var cGrid = client.EntMan.GetEntity(server.EntMan.GetNetEntity(grid));
var cPlayer = client.EntMan.GetEntity(server.EntMan.GetNetEntity(player));
var cParent = client.EntMan.GetEntity(server.EntMan.GetNetEntity(parent));
var cChild = client.EntMan.GetEntity(server.EntMan.GetNetEntity(child));
Assert.That(cMap, Is.Not.EqualTo(EntityUid.Invalid));
Assert.That(cGrid, Is.Not.EqualTo(EntityUid.Invalid));
Assert.That(cPlayer, Is.Not.EqualTo(EntityUid.Invalid));
Assert.That(cParent, Is.Not.EqualTo(EntityUid.Invalid));
Assert.That(cChild, Is.EqualTo(EntityUid.Invalid));
var cParentX = client.Transform(cParent);
var cPlayerX = client.Transform(cPlayer);
var cGridX = client.Transform(cGrid);
Assert.That(cParentX.MapID, Is.EqualTo(mapId));
Assert.That(cPlayerX.MapID, Is.EqualTo(mapId));
Assert.That(cGridX.MapID, Is.EqualTo(mapId));
Assert.That(cParentX.ParentUid, Is.EqualTo(cGrid));
Assert.That(cPlayerX.ParentUid, Is.EqualTo(cGrid));
Assert.That(cGridX.ParentUid, Is.EqualTo(cMap));
Assert.That(cParentX.GridUid, Is.EqualTo(cGrid));
Assert.That(cPlayerX.GridUid, Is.EqualTo(cGrid));
Assert.That(cGridX.GridUid, Is.EqualTo(cGrid));
// Move the player into pvs range of the child, which will move them outside of the grid & parent's PVS range.
await server.WaitPost(() => xformSys.SetCoordinates(player, mapCoords));
for (var i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// the client now knows about the child.
cChild = client.EntMan.GetEntity(server.EntMan.GetNetEntity(child));
Assert.That(cChild, Is.Not.EqualTo(EntityUid.Invalid));
var cChildX = client.Transform(cChild);
Assert.That(childX.MapID, Is.EqualTo(mapId));
Assert.That(cChildX.ParentUid, Is.EqualTo(cMap));
Assert.That(cChildX.GridUid, Is.Null);
// Player transform has updated
Assert.That(cPlayerX.GridUid, Is.Null);
Assert.That(cPlayerX.MapID, Is.EqualTo(mapId));
Assert.That(cPlayerX.ParentUid, Is.EqualTo(cMap));
// But the other entities have left PVS range
Assert.That(cParentX.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(cParentX.MapID, Is.EqualTo(MapId.Nullspace));
Assert.That(cParentX.GridUid, Is.Null);
Assert.That((client.MetaData(cParent).Flags & MetaDataFlags.Detached) != 0);
// Attach the child & player entities to the parent
// This is the main step that the test is actually checking
var parentCoords = new EntityCoordinates(parent, Vector2.Zero);
await server.WaitPost(() => xformSys.SetCoordinates(player, parentCoords));
await server.WaitPost(() => xformSys.SetCoordinates(child, parentCoords));
for (var i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Check that server-side transforms are as expected
Assert.That(childX.ParentUid, Is.EqualTo(parent));
Assert.That(parentX.ParentUid, Is.EqualTo(grid));
Assert.That(playerX.ParentUid, Is.EqualTo(parent));
Assert.That(gridX.ParentUid, Is.EqualTo(map));
Assert.That(childX.GridUid, Is.EqualTo(grid));
Assert.That(parentX.GridUid, Is.EqualTo(grid));
Assert.That(playerX.GridUid, Is.EqualTo(grid));
Assert.That(gridX.GridUid, Is.EqualTo(grid));
// Next check the client-side transforms
Assert.That((client.MetaData(cParent).Flags & MetaDataFlags.Detached) == 0);
Assert.That(cChildX.ParentUid, Is.EqualTo(cParent));
Assert.That(cParentX.ParentUid, Is.EqualTo(cGrid));
Assert.That(cPlayerX.ParentUid, Is.EqualTo(cParent));
Assert.That(cGridX.ParentUid, Is.EqualTo(cMap));
Assert.That(cChildX.GridUid, Is.EqualTo(cGrid));
Assert.That(cParentX.GridUid, Is.EqualTo(cGrid));
Assert.That(cPlayerX.GridUid, Is.EqualTo(cGrid));
Assert.That(cGridX.GridUid, Is.EqualTo(cGrid));
// Repeat the previous test, but this time attaching to an entity that gets spawned outside of PVS range, that
// the client never new about previously.
await server.WaitPost(() => xformSys.SetCoordinates(player, mapCoords));
await server.WaitPost(() => xformSys.SetCoordinates(child, mapCoords));
for (var i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Child transform has updated.
Assert.That(childX.MapID, Is.EqualTo(mapId));
Assert.That(cChildX.ParentUid, Is.EqualTo(cMap));
Assert.That(cChildX.GridUid, Is.Null);
// Player transform has updated
Assert.That(cPlayerX.GridUid, Is.Null);
Assert.That(cPlayerX.MapID, Is.EqualTo(mapId));
Assert.That(cPlayerX.ParentUid, Is.EqualTo(cMap));
// The other entities have left PVS range
Assert.That(cParentX.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(cParentX.MapID, Is.EqualTo(MapId.Nullspace));
Assert.That(cParentX.GridUid, Is.Null);
Assert.That((client.MetaData(cParent).Flags & MetaDataFlags.Detached) != 0);
// Create a new parent entity
EntityUid parent2 = default;
await server.WaitPost(() => parent2 = sEntMan.SpawnEntity(null, gridCoords));
for (var i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
var parent2X = server.Transform(parent2);
Assert.That(parent2X.MapID, Is.EqualTo(mapId));
Assert.That(parent2X.ParentUid, Is.EqualTo(grid));
Assert.That(parent2X.GridUid, Is.EqualTo(grid));
// Client does not know that parent2 exists yet.
var cParent2 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(parent2));
Assert.That(cParent2, Is.EqualTo(EntityUid.Invalid));
// Attach player & child to the new parent.
var parent2Coords = new EntityCoordinates(parent2, Vector2.Zero);
await server.WaitPost(() => xformSys.SetCoordinates(player, parent2Coords));
await server.WaitPost(() => xformSys.SetCoordinates(child, parent2Coords));
for (var i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Check all the transforms
cParent2 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(parent2));
Assert.That(cParent2, Is.Not.EqualTo(EntityUid.Invalid));
var cParent2X = client.Transform(cParent2);
Assert.That(cChildX.ParentUid, Is.EqualTo(cParent2));
Assert.That(cParent2X.ParentUid, Is.EqualTo(cGrid));
Assert.That(cPlayerX.ParentUid, Is.EqualTo(cParent2));
Assert.That(cGridX.ParentUid, Is.EqualTo(cMap));
Assert.That(cParent2X.GridUid, Is.EqualTo(cGrid));
Assert.That(cChildX.GridUid, Is.EqualTo(cGrid));
Assert.That(cPlayerX.GridUid, Is.EqualTo(cGrid));
Assert.That(cGridX.GridUid, Is.EqualTo(cGrid));
// Repeat again, but with a new map.
// Set up map and spawn player
MapId mapId2 = default;
EntityUid map2 = default;
EntityUid grid2 = default;
EntityUid parent3 = default;
await server.WaitPost(() =>
{
map2 = mapSys.CreateMap(out mapId2);
var gridEnt = mapMan.CreateGridEntity(mapId2);
mapSys.SetTile(gridEnt.Owner, gridEnt.Comp, Vector2i.Zero, new Tile(1));
var grid2Coords = new EntityCoordinates(gridEnt, .5f, .5f);
grid2 = gridEnt.Owner;
parent3 = sEntMan.SpawnEntity(null, grid2Coords);
});
for (var i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Check server-side transforms
var grid2X = server.Transform(grid2);
var parent3X = server.Transform(parent3);
Assert.That(parent3X.MapID, Is.EqualTo(mapId2));
Assert.That(grid2X.MapID, Is.EqualTo(mapId2));
Assert.That(parent3X.ParentUid, Is.EqualTo(grid2));
Assert.That(grid2X.ParentUid, Is.EqualTo(map2));
Assert.That(parent3X.GridUid, Is.EqualTo(grid2));
Assert.That(grid2X.GridUid, Is.EqualTo(grid2));
// Client does not know that parent3 exists, but (at least for now) clients always know about all maps and grids.
var cParent3 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(parent3));
var cGrid2 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(grid2));
var cMap2 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(map2));
Assert.That(cMap2, Is.Not.EqualTo(EntityUid.Invalid));
Assert.That(cGrid2, Is.Not.EqualTo(EntityUid.Invalid));
Assert.That(cParent3, Is.EqualTo(EntityUid.Invalid));
// Attach the entities to the parent on the new map.
var parent3Coords = new EntityCoordinates(parent3, Vector2.Zero);
await server.WaitPost(() => xformSys.SetCoordinates(player, parent3Coords));
await server.WaitPost(() => xformSys.SetCoordinates(child, parent3Coords));
for (var i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Check all the transforms
cParent3 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(parent3));
Assert.That(cParent3, Is.Not.EqualTo(EntityUid.Invalid));
var cParent3X = client.Transform(cParent3);
var cGrid2X = client.Transform(cGrid2);
Assert.That(cChildX.ParentUid, Is.EqualTo(cParent3));
Assert.That(cParent3X.ParentUid, Is.EqualTo(cGrid2));
Assert.That(cPlayerX.ParentUid, Is.EqualTo(cParent3));
Assert.That(cGrid2X.ParentUid, Is.EqualTo(cMap2));
Assert.That(cParent3X.GridUid, Is.EqualTo(cGrid2));
Assert.That(cChildX.GridUid, Is.EqualTo(cGrid2));
Assert.That(cPlayerX.GridUid, Is.EqualTo(cGrid2));
Assert.That(cGrid2X.GridUid, Is.EqualTo(cGrid2));
Assert.That(cParent3X.MapID, Is.EqualTo(mapId2));
Assert.That(cChildX.MapID, Is.EqualTo(mapId2));
Assert.That(cPlayerX.MapID, Is.EqualTo(mapId2));
Assert.That(cGrid2X.MapID, Is.EqualTo(mapId2));
Assert.That(cParent3X.MapUid, Is.EqualTo(cMap2));
Assert.That(cChildX.MapUid, Is.EqualTo(cMap2));
Assert.That(cPlayerX.MapUid, Is.EqualTo(cMap2));
Assert.That(cGrid2X.MapUid, Is.EqualTo(cMap2));
// Create a new map & grid and move entities in the same tick
MapId mapId3 = default;
EntityUid map3 = default;
EntityUid grid3 = default;
EntityUid parent4 = default;
await server.WaitPost(() =>
{
map3 = mapSys.CreateMap(out mapId3);
var gridEnt = mapMan.CreateGridEntity(mapId3);
mapSys.SetTile(gridEnt.Owner, gridEnt.Comp, Vector2i.Zero, new Tile(1));
var grid3Coords = new EntityCoordinates(gridEnt, .5f, .5f);
grid3 = gridEnt.Owner;
parent4 = sEntMan.SpawnEntity(null, grid3Coords);
var parent4Coords = new EntityCoordinates(parent4, Vector2.Zero);
// Move existing entity to new parent
xformSys.SetCoordinates(player, parent4Coords);
// Move existing parent & child combination to new grid
xformSys.SetCoordinates(parent3, grid3Coords);
});
for (var i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Check all the transforms
var cParent4 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(parent4));
var cMap3 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(map3));
var cGrid3 = client.EntMan.GetEntity(server.EntMan.GetNetEntity(grid3));
Assert.That(cParent4, Is.Not.EqualTo(EntityUid.Invalid));
Assert.That(cMap3, Is.Not.EqualTo(EntityUid.Invalid));
Assert.That(cGrid3, Is.Not.EqualTo(EntityUid.Invalid));
var cParent4X = client.Transform(cParent4);
var cGrid3X = client.Transform(cGrid3);
Assert.That(cChildX.ParentUid, Is.EqualTo(cParent3));
Assert.That(cPlayerX.ParentUid, Is.EqualTo(cParent4));
Assert.That(cParent3X.ParentUid, Is.EqualTo(cGrid3));
Assert.That(cParent4X.ParentUid, Is.EqualTo(cGrid3));
Assert.That(cGrid3X.ParentUid, Is.EqualTo(cMap3));
Assert.That(cChildX.GridUid, Is.EqualTo(cGrid3));
Assert.That(cPlayerX.GridUid, Is.EqualTo(cGrid3));
Assert.That(cParent3X.GridUid, Is.EqualTo(cGrid3));
Assert.That(cParent4X.GridUid, Is.EqualTo(cGrid3));
Assert.That(cGrid3X.GridUid, Is.EqualTo(cGrid3));
Assert.That(cChildX.MapID, Is.EqualTo(mapId3));
Assert.That(cPlayerX.MapID, Is.EqualTo(mapId3));
Assert.That(cParent3X.MapID, Is.EqualTo(mapId3));
Assert.That(cParent4X.MapID, Is.EqualTo(mapId3));
Assert.That(cGrid3X.MapID, Is.EqualTo(mapId3));
Assert.That(cChildX.MapUid, Is.EqualTo(cMap3));
Assert.That(cPlayerX.MapUid, Is.EqualTo(cMap3));
Assert.That(cParent3X.MapUid, Is.EqualTo(cMap3));
Assert.That(cParent4X.MapUid, Is.EqualTo(cMap3));
Assert.That(cGrid3X.MapUid, Is.EqualTo(cMap3));
await client.WaitPost(() => netMan.ClientDisconnect(""));
await server.WaitRunTicks(5);
await client.WaitRunTicks(5);
}
}

View File

@@ -1,173 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Player;
namespace Robust.UnitTesting.Server.GameStates;
public sealed class MissingParentTest : RobustIntegrationTest
{
/// <summary>
/// Check that PVS & clients can handle entities being sent before their parents are.
/// </summary>
[Test]
public async Task TestMissingParent()
{
var server = StartServer();
var client = StartClient();
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
var mapMan = server.ResolveDependency<IMapManager>();
var sEntMan = server.ResolveDependency<IEntityManager>();
var confMan = server.ResolveDependency<IConfigurationManager>();
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
var cEntMan = client.ResolveDependency<IEntityManager>();
var netMan = client.ResolveDependency<IClientNetManager>();
var cPlayerMan = client.ResolveDependency<ISharedPlayerManager>();
var cConfMan = client.ResolveDependency<IConfigurationManager>();
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
client.Post(() => netMan.ClientConnect(null!, 0, null!));
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Limit client to receiving at most 1 entity per tick.
cConfMan.SetCVar(CVars.NetPVSEntityBudget, 1);
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Ensure client & server ticks are synced.
// Client runs 1 tick ahead
{
var sTick = (int)server.Timing.CurTick.Value;
var cTick = (int)client.Timing.CurTick.Value;
var delta = cTick - sTick;
if (delta > 1)
await server.WaitRunTicks(delta - 1);
else if (delta < 1)
await client.WaitRunTicks(1 - delta);
sTick = (int)server.Timing.CurTick.Value;
cTick = (int)client.Timing.CurTick.Value;
delta = cTick - sTick;
Assert.That(delta, Is.EqualTo(1));
}
// Set up map and spawn player
NetEntity player = default;
NetEntity entity = default;
EntityCoordinates coords = default;
NetCoordinates nCoords = default;
await server.WaitPost(() =>
{
var map = server.System<SharedMapSystem>().CreateMap();
coords = new(map, default);
var playerUid = sEntMan.SpawnEntity(null, coords);
var entUid = sEntMan.SpawnEntity(null, coords);
entity = sEntMan.GetNetEntity(entUid);
player = sEntMan.GetNetEntity(playerUid);
nCoords = sEntMan.GetNetCoordinates(coords);
// Attach player.
var session = sPlayerMan.Sessions.First();
server.PlayerMan.SetAttachedEntity(session, playerUid);
sPlayerMan.JoinGame(session);
});
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
Assert.That(player, Is.Not.EqualTo(NetEntity.Invalid));
Assert.That(entity, Is.Not.EqualTo(NetEntity.Invalid));
// Check player got properly attached, and has received the other entity.
Assert.That(cEntMan.TryGetEntityData(entity, out _, out var meta));
Assert.That(cEntMan.TryGetEntity(player, out var cPlayerUid));
Assert.That(cPlayerMan.LocalEntity, Is.EqualTo(cPlayerUid));
Assert.That(server.Transform(player).Coordinates, Is.EqualTo(coords));
Assert.That(client.Transform(player).Coordinates, Is.EqualTo(client.EntMan.GetCoordinates(nCoords)));
Assert.That(client.Transform(entity).ParentUid.IsValid(), Is.True);
Assert.That(client.MetaData(entity).Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
// Spawn 20 new entities
NetEntity first = default;
NetEntity last = default;
await server.WaitPost(() =>
{
first = sEntMan.GetNetEntity(sEntMan.SpawnEntity(null, coords));
for (var i = 0; i < 18; i++)
{
sEntMan.SpawnEntity(null, coords);
}
last = sEntMan.GetNetEntity(sEntMan.SpawnEntity(null, coords));
});
// Wait for the client to receive some, but not all, of the entities
for (int i = 0; i < 8; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
Assert.That(cEntMan.TryGetEntity(first, out _), Is.True);
Assert.That(cEntMan.TryGetEntity(last, out _), Is.False);
// Re-parent the known entity to an entity that the client has not received yet.
await server.WaitPost(() =>
{
var newCoords = new EntityCoordinates(sEntMan.GetEntity(last), default);
server.System<SharedTransformSystem>().SetCoordinates(sEntMan.GetEntity(entity), newCoords);
});
// Wait a few more ticks
for (int i = 0; i < 8; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Client should still not have received the new parent, however this shouldn't cause any issues.
// The already known entity should just have been moved to nullspace.
Assert.That(cEntMan.TryGetEntity(last, out _), Is.False);
Assert.That(client.Transform(entity).ParentUid.IsValid(), Is.False);
Assert.That(client.MetaData(entity).Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
// Wait untill the client receives the parent entity
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// now that the parent was received the entity should no longer be in nullspace.
Assert.That(cEntMan.TryGetEntity(last, out var newParent), Is.True);
Assert.That(client.Transform(entity).ParentUid.IsValid(), Is.True);
Assert.That(client.Transform(entity).ParentUid, Is.EqualTo(newParent));
Assert.That(client.MetaData(entity).Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
await client.WaitPost(() => netMan.ClientDisconnect(""));
await server.WaitRunTicks(5);
await client.WaitRunTicks(5);
}
}

View File

@@ -1,150 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Server.GameObjects;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Player;
namespace Robust.UnitTesting.Server.GameStates;
public sealed class PvsChunkTest : RobustIntegrationTest
{
[Test]
public async Task TestGridMapChange()
{
var server = StartServer();
var client = StartClient();
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
var mapMan = server.ResolveDependency<IMapManager>();
var sEntMan = server.ResolveDependency<IEntityManager>();
var confMan = server.ResolveDependency<IConfigurationManager>();
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
var xforms = sEntMan.System<SharedTransformSystem>();
var mapSys = sEntMan.System<MapSystem>();
var cEntMan = client.ResolveDependency<IEntityManager>();
var netMan = client.ResolveDependency<IClientNetManager>();
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
client.Post(() => netMan.ClientConnect(null!, 0, null!));
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Ensure client & server ticks are synced.
// Client runs 1 tick ahead
{
var sTick = (int)server.Timing.CurTick.Value;
var cTick = (int)client.Timing.CurTick.Value;
var delta = cTick - sTick;
if (delta > 1)
await server.WaitRunTicks(delta - 1);
else if (delta < 1)
await client.WaitRunTicks(1 - delta);
sTick = (int)server.Timing.CurTick.Value;
cTick = (int)client.Timing.CurTick.Value;
delta = cTick - sTick;
Assert.That(delta, Is.EqualTo(1));
}
// Set up entities
EntityUid map1 = default;
EntityUid map2 = default;
EntityUid grid = default;
EntityUid player = default;
EntityUid entity = default;
EntityCoordinates mapCoords = default;
await server.WaitPost(() =>
{
map1 = server.System<SharedMapSystem>().CreateMap();
mapCoords = new(map1, default);
map2 = server.System<SharedMapSystem>().CreateMap();
var gridComp = mapMan.CreateGridEntity(map2);
grid = gridComp.Owner;
mapSys.SetTile(grid, gridComp, Vector2i.Zero, new Tile(1));
var gridCoords = new EntityCoordinates(grid, .5f, .5f);
player = sEntMan.SpawnEntity(null, mapCoords);
entity = sEntMan.SpawnEntity(null, gridCoords);
// Attach player.
var session = sPlayerMan.Sessions.First();
server.PlayerMan.SetAttachedEntity(session, player);
sPlayerMan.JoinGame(session);
});
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
var nEntity = sEntMan.GetNetEntity(entity);
var nGrid = sEntMan.GetNetEntity(grid);
var nMap1 = sEntMan.GetNetEntity(map1);
var nMap2 = sEntMan.GetNetEntity(map2);
var xform = sEntMan.GetComponent<TransformComponent>(entity);
Assert.That(xform.ParentUid, Is.EqualTo(grid));
Assert.That(xform.GridUid, Is.EqualTo(grid));
Assert.That(xform.MapUid, Is.EqualTo(map2));
Assert.That(!cEntMan.TryGetEntity(nEntity, out _));
Assert.That(cEntMan.TryGetEntity(nMap1, out _));
Assert.That(cEntMan.TryGetEntity(nMap2, out _));
Assert.That(cEntMan.TryGetEntity(nGrid, out _));
// Teleport grid to new map
await server.WaitPost(() => xforms.SetCoordinates(grid, mapCoords));
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
Assert.That(xform.ParentUid, Is.EqualTo(grid));
Assert.That(xform.GridUid, Is.EqualTo(grid));
Assert.That(xform.MapUid, Is.EqualTo(map1));
Assert.That(cEntMan.TryGetEntity(nEntity, out _));
Assert.That(cEntMan.TryGetEntity(nMap1, out _));
Assert.That(cEntMan.TryGetEntity(nMap2, out _));
Assert.That(cEntMan.TryGetEntity(nGrid, out _));
// Delete the original map.
await server.WaitPost(() => sEntMan.DeleteEntity(map2));
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
Assert.That(xform.ParentUid, Is.EqualTo(grid));
Assert.That(xform.GridUid, Is.EqualTo(grid));
Assert.That(xform.MapUid, Is.EqualTo(map1));
Assert.That(cEntMan.TryGetEntity(nEntity, out _));
Assert.That(cEntMan.TryGetEntity(nMap1, out _));
Assert.That(!cEntMan.TryGetEntity(nMap2, out _));
Assert.That(cEntMan.TryGetEntity(nGrid, out _));
await client.WaitPost(() => netMan.ClientDisconnect(""));
await server.WaitRunTicks(5);
await client.WaitRunTicks(5);
}
}

View File

@@ -1,173 +0,0 @@
using System;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Client.GameStates;
using Robust.Shared;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Network;
namespace Robust.UnitTesting.Server.GameStates;
public sealed class PvsPauseTest : RobustIntegrationTest
{
/// <summary>
/// Check that the client "pauses" entities that have left their PVS range.
/// </summary>
[Test]
public async Task PauseTest()
{
var server = StartServer();
var client = StartClient();
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
var sEntMan = server.EntMan;
var confMan = server.CfgMan;
var sPlayerMan = server.PlayerMan;
var xforms = sEntMan.System<SharedTransformSystem>();
var metaSys = sEntMan.System<MetaDataSystem>();
var stateMan = (ClientGameStateManager) client.ResolveDependency<IClientGameStateManager>();
var cEntMan = client.EntMan;
var cPlayerMan = client.PlayerMan;
var netMan = client.ResolveDependency<IClientNetManager>();
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
client.Post(() => netMan.ClientConnect(null!, 0, null!));
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
async Task RunTicks()
{
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
}
await RunTicks();
// Set up map and spawn player
EntityUid map = default;
EntityUid playerUid = default;
EntityUid sEnt = default;
EntityCoordinates coords = default;
await server.WaitPost(() =>
{
map = server.System<SharedMapSystem>().CreateMap();
coords = new(map, default);
playerUid = sEntMan.SpawnEntity(null, coords);
sEnt = sEntMan.SpawnEntity(null, coords);
// Attach player.
var session = sPlayerMan.Sessions.First();
server.PlayerMan.SetAttachedEntity(session, playerUid);
sPlayerMan.JoinGame(session);
});
await RunTicks();
var farAway = new EntityCoordinates(map, new Vector2(100, 100));
var netEnt = sEntMan.GetNetEntity(sEnt);
var player = sEntMan.GetNetEntity(playerUid);
Assert.That(player, Is.Not.EqualTo(NetEntity.Invalid));
Assert.That(netEnt, Is.Not.EqualTo(NetEntity.Invalid));
// Check player got properly attached, and has received the other entity.
Assert.That(cEntMan.TryGetEntity(netEnt, out var uid));
Assert.That(cEntMan.TryGetEntity(player, out var cPlayerUid));
var cEnt = uid!.Value;
Assert.That(cPlayerMan.LocalEntity, Is.EqualTo(cPlayerUid));
void AssertEnt(bool paused, bool detached, bool clientPaused)
{
var cMeta = client.MetaData(cEnt);
var sMeta = server.MetaData(sEnt);
Assert.That(cMeta.Flags.HasFlag(MetaDataFlags.Detached), Is.EqualTo(detached));
Assert.That(sMeta.Flags.HasFlag(MetaDataFlags.Detached), Is.False);
Assert.That(stateMan.IsQueuedForDetach(netEnt), Is.False);
Assert.That(sMeta.EntityPaused, Is.EqualTo(paused));
Assert.That(cMeta.EntityPaused, Is.EqualTo(clientPaused));
if (detached)
Assert.That(cMeta.PauseTime, Is.EqualTo(TimeSpan.Zero));
if (clientPaused)
Assert.That(cMeta.PauseTime, Is.GreaterThanOrEqualTo(TimeSpan.Zero));
else
Assert.That(cMeta.PauseTime, Is.Null);
if (paused)
Assert.That(sMeta.PauseTime, Is.GreaterThan(TimeSpan.Zero));
else
Assert.That(sMeta.PauseTime, Is.Null);
}
// Entity is initially in view and not paused.
AssertEnt(paused: false, detached: false, clientPaused: false);
// Move the player out of the entity's PVS range
await server.WaitPost(() => xforms.SetCoordinates(sEntMan.GetEntity(player), farAway));
await RunTicks();
// Client should now have detached & locally paused the entity.
AssertEnt(paused: false, detached: true, clientPaused: true);
// Move the player back into range
await server.WaitPost( () => xforms.SetCoordinates(sEntMan.GetEntity(player), coords));
await RunTicks();
AssertEnt(paused: false, detached: false, clientPaused: false);
// Actually pause the entity.
await server.WaitPost(() => metaSys.SetEntityPaused(sEnt, true));
await RunTicks();
AssertEnt(paused: true, detached: false, clientPaused: true);
// Out of range
await server.WaitPost(() => xforms.SetCoordinates(sEntMan.GetEntity(player), farAway));
await RunTicks();
AssertEnt(paused: true, detached: true, clientPaused: true);
// Back in range
await server.WaitPost(() => xforms.SetCoordinates(sEntMan.GetEntity(player), coords));
await RunTicks();
AssertEnt(paused: true, detached: false, clientPaused: true);
// Unpause the entity while out of range
{
await server.WaitPost(() => xforms.SetCoordinates(sEntMan.GetEntity(player), farAway));
await RunTicks();
AssertEnt(paused: true, detached: true, clientPaused: true);
await server.WaitPost(() => metaSys.SetEntityPaused(sEnt, false));
await RunTicks();
AssertEnt(paused: false, detached: true, clientPaused: true);
await server.WaitPost(() => xforms.SetCoordinates(sEntMan.GetEntity(player), coords));
await RunTicks();
AssertEnt(paused: false, detached: false, clientPaused: false);
}
// Pause the entity while out of range
{
await server.WaitPost(() => xforms.SetCoordinates(sEntMan.GetEntity(player), farAway));
await RunTicks();
AssertEnt(paused: false, detached: true, clientPaused: true);
await server.WaitPost(() => metaSys.SetEntityPaused(sEnt, true));
await RunTicks();
AssertEnt(paused: true, detached: true, clientPaused: true);
await server.WaitPost(() => xforms.SetCoordinates(sEntMan.GetEntity(player), coords));
await RunTicks();
AssertEnt(paused: true, detached: false, clientPaused: true);
}
client.Post(() => netMan.ClientDisconnect(""));
await RunTicks();
}
}

View File

@@ -1,213 +0,0 @@
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Client.GameStates;
using Robust.Client.Timing;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Timing;
namespace Robust.UnitTesting.Server.GameStates;
public sealed class PvsReEntryTest : RobustIntegrationTest
{
#if DEBUG
/// <summary>
/// Checks that there are no issues when an entity enters, leaves, then enters pvs while the client drops packets.
/// </summary>
[Test]
public async Task TestLossyReEntry()
{
var server = StartServer();
var client = StartClient();
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
var mapMan = server.ResolveDependency<IMapManager>();
var sEntMan = server.ResolveDependency<IEntityManager>();
var confMan = server.ResolveDependency<IConfigurationManager>();
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
var xforms = sEntMan.System<SharedTransformSystem>();
var stateMan = (ClientGameStateManager) client.ResolveDependency<IClientGameStateManager>();
var cEntMan = client.ResolveDependency<IEntityManager>();
var netMan = client.ResolveDependency<IClientNetManager>();
var cPlayerMan = client.ResolveDependency<ISharedPlayerManager>();
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
client.Post(() => netMan.ClientConnect(null!, 0, null!));
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Ensure client & server ticks are synced.
// Client runs 1 tick ahead
{
var sTick = (int)server.Timing.CurTick.Value;
var cTick = (int)client.Timing.CurTick.Value;
var delta = cTick - sTick;
if (delta > 1)
await server.WaitRunTicks(delta - 1);
else if (delta < 1)
await client.WaitRunTicks(1 - delta);
sTick = (int)server.Timing.CurTick.Value;
cTick = (int)client.Timing.CurTick.Value;
delta = cTick - sTick;
Assert.That(delta, Is.EqualTo(1));
}
// Set up map and spawn player
EntityUid map = default;
NetEntity player = default;
NetEntity entity = default;
EntityCoordinates coords = default;
await server.WaitPost(() =>
{
map = server.System<SharedMapSystem>().CreateMap();
coords = new(map, default);
var playerUid = sEntMan.SpawnEntity(null, coords);
var entUid = sEntMan.SpawnEntity(null, coords);
entity = sEntMan.GetNetEntity(entUid);
player = sEntMan.GetNetEntity(playerUid);
// Attach player.
var session = sPlayerMan.Sessions.First();
server.PlayerMan.SetAttachedEntity(session, playerUid);
sPlayerMan.JoinGame(session);
});
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
Assert.That(player, Is.Not.EqualTo(NetEntity.Invalid));
Assert.That(entity, Is.Not.EqualTo(NetEntity.Invalid));
// Check player got properly attached, and has received the other entity.
MetaDataComponent? meta = default!;
await client.WaitPost(() =>
{
Assert.That(cEntMan.TryGetEntityData(entity, out _, out meta));
Assert.That(cEntMan.TryGetEntity(player, out var cPlayerUid));
Assert.That(cPlayerMan.LocalEntity, Is.EqualTo(cPlayerUid));
Assert.That(meta!.Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
Assert.That(stateMan.IsQueuedForDetach(entity), Is.False);
});
var lastDirty = meta.LastModifiedTick;
Assert.That(lastDirty, Is.GreaterThan(GameTick.Zero));
// Move the player outside of the entity's PVS range
// note that we move the PLAYER not the entity, as we don't want to dirty the entity.
var farAway = new EntityCoordinates(map, new Vector2(100, 100));
await server.WaitPost( () => xforms.SetCoordinates(sEntMan.GetEntity(player), farAway));
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Client should have detached the entity to null space.
Assert.That(meta!.Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.Detached));
Assert.That(stateMan.IsQueuedForDetach(entity), Is.False);
// Move the player back into range
await server.WaitPost( () => xforms.SetCoordinates(sEntMan.GetEntity(player), coords));
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Entity is back in pvs range
Assert.That(meta!.Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
Assert.That(stateMan.IsQueuedForDetach(entity), Is.False);
// Oh no, the client is going through a tunnel!
stateMan.DropStates = true;
var timing = client.ResolveDependency<IClientGameTiming>();
var lastRealTick = timing.LastRealTick;
for (int i = 0; i < 5; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Even though the client is receiving no new states, it will still have applied some from the state buffer.
Assert.That(timing.LastRealTick, Is.GreaterThan(lastRealTick));
lastRealTick = timing.LastRealTick;
// Move the player outside of pvs range.
Assert.That(meta!.Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
await server.WaitPost(() => xforms.SetCoordinates(sEntMan.GetEntity(player), farAway));
for (int i = 0; i < 5; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Client should have exhausted the buffer -- client has not been applying any states.
Assert.That(timing.LastRealTick, Is.EqualTo(lastRealTick));
Assert.That(meta!.Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
// However pvs-leave messages are sent separately, and are sent reliably. So they are still being received.
// Though in a realistic scenario they should probably be delayed somewhat.
Assert.That(stateMan.IsQueuedForDetach(entity), Is.True);
// Move the entity back into range
await server.WaitPost( () => xforms.SetCoordinates(sEntMan.GetEntity(player), coords));
for (int i = 0; i < 5; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Still hasn't been applying states.
Assert.That(timing.LastRealTick, Is.EqualTo(lastRealTick));
Assert.That(meta!.Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
// The pvs-leave message has not yet been processed, detaching is still queued.
Assert.That(stateMan.IsQueuedForDetach(entity), Is.True);
// Client clears the tunnel, starts receiving states again.
stateMan.DropStates = false;
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Entity should be in PVS range, client should know about it:
Assert.That(timing.LastRealTick, Is.GreaterThan(lastRealTick));
Assert.That(meta!.Flags & MetaDataFlags.Detached, Is.EqualTo(MetaDataFlags.None));
Assert.That(stateMan.IsQueuedForDetach(entity), Is.False);
// The entity itself should not have been dirtied since creation -- only the player has been moving.
// If the test moves the entity instead of the player, then the test doesn't actually work.
Assert.That(meta.LastModifiedTick, Is.EqualTo(lastDirty));
await client.WaitPost(() => netMan.ClientDisconnect(""));
await server.WaitRunTicks(5);
await client.WaitRunTicks(5);
}
#endif
}

View File

@@ -1,134 +0,0 @@
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Network;
namespace Robust.UnitTesting.Server.GameStates;
public sealed class PvsResetTest : RobustIntegrationTest
{
/// <summary>
/// Check that the client doesn't reset dirty detached entities. They should remain in nullspace.
/// </summary>
[Test]
public async Task ResetTest()
{
var server = StartServer();
var client = StartClient();
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
var sEntMan = server.EntMan;
var confMan = server.CfgMan;
var sPlayerMan = server.PlayerMan;
var xforms = sEntMan.System<SharedTransformSystem>();
var cEntMan = client.EntMan;
var cPlayerMan = client.PlayerMan;
var netMan = client.ResolveDependency<IClientNetManager>();
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
client.Post(() => netMan.ClientConnect(null!, 0, null!));
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
async Task RunTicks()
{
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
}
await RunTicks();
// Set up map and spawn player
EntityUid sMap = default;
EntityUid playerUid = default;
EntityUid sEnt = default;
EntityCoordinates coords = default;
await server.WaitPost(() =>
{
sMap = server.System<SharedMapSystem>().CreateMap();
coords = new(sMap, default);
playerUid = sEntMan.SpawnEntity(null, coords);
sEnt = sEntMan.SpawnEntity(null, coords);
// Attach player.
var session = sPlayerMan.Sessions.First();
server.PlayerMan.SetAttachedEntity(session, playerUid);
sPlayerMan.JoinGame(session);
});
await RunTicks();
var farAway = new EntityCoordinates(sMap, new Vector2(100, 100));
var netEnt = sEntMan.GetNetEntity(sEnt);
var player = sEntMan.GetNetEntity(playerUid);
Assert.That(player, Is.Not.EqualTo(NetEntity.Invalid));
Assert.That(netEnt, Is.Not.EqualTo(NetEntity.Invalid));
// Check player got properly attached, and has received the other entity.
Assert.That(cEntMan.TryGetEntity(netEnt, out var uid));
Assert.That(cEntMan.TryGetEntity(player, out var cPlayerUid));
var cEnt = uid!.Value;
Assert.That(cPlayerMan.LocalEntity, Is.EqualTo(cPlayerUid));
var cMap = cEntMan.GetEntity(sEntMan.GetNetEntity(sMap));
void AssertDetached(bool detached)
{
var cXform = client.Transform(cEnt);
var sXform = server.Transform(sEnt);
var meta = client.MetaData(cEnt);
Assert.That(sXform.MapUid, Is.EqualTo(sMap));
Assert.That(sXform.ParentUid, Is.EqualTo(sMap));
if (detached)
{
Assert.That(meta.Flags.HasFlag(MetaDataFlags.Detached));
Assert.That(cXform.MapUid, Is.Null);
Assert.That(cXform.ParentUid, Is.EqualTo(EntityUid.Invalid));
}
else
{
Assert.That(!meta.Flags.HasFlag(MetaDataFlags.Detached));
Assert.That(cXform.MapUid, Is.EqualTo(cMap));
Assert.That(cXform.ParentUid, Is.EqualTo(cMap));
}
}
// Entity is initially in view
AssertDetached(false);
// Move the player out of the entity's PVS range
await server.WaitPost(() => xforms.SetCoordinates(playerUid, farAway));
await RunTicks();
// Client should now have detached the entity, moving it into nullspace
AssertDetached(true);
// Marking the entity as dirty due to client-side prediction should have effect
await client.WaitPost(() => client.EntMan.Dirty(cEnt, client.Transform(cEnt)));
await RunTicks();
AssertDetached(true);
// Move the player back into range
await server.WaitPost( () => xforms.SetCoordinates(playerUid, coords));
await RunTicks();
AssertDetached(false);
// Marking the entity as dirty due to client-side prediction should have no real effect
await client.WaitPost(() => client.EntMan.Dirty(cEnt, client.Transform(cEnt)));
await RunTicks();
AssertDetached(false);
await client.WaitPost(() => netMan.ClientDisconnect(""));
await server.WaitRunTicks(5);
await client.WaitRunTicks(5);
}
}

View File

@@ -1,130 +0,0 @@
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Player;
namespace Robust.UnitTesting.Server.GameStates;
public sealed class PvsSystemTests : RobustIntegrationTest
{
/// <summary>
/// Checks that there are no issues when an entity changes PVS chunk location multiple times in a single tick.
/// </summary>
[Test]
public async Task TestMultipleIndexChange()
{
var server = StartServer();
var client = StartClient();
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
var mapMan = server.ResolveDependency<IMapManager>();
var sEntMan = server.ResolveDependency<IEntityManager>();
var confMan = server.ResolveDependency<IConfigurationManager>();
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
var xforms = sEntMan.System<SharedTransformSystem>();
var maps = sEntMan.System<SharedMapSystem>();
var cEntMan = client.ResolveDependency<IEntityManager>();
var netMan = client.ResolveDependency<IClientNetManager>();
var cPlayerMan = client.ResolveDependency<ISharedPlayerManager>();
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
client.Post(() => netMan.ClientConnect(null!, 0, null!));
server.Post(() => confMan.SetCVar(CVars.NetPVS, true));
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Set up map and grid
EntityUid grid = default;
EntityUid map = default;
await server.WaitPost(() =>
{
map = server.System<SharedMapSystem>().CreateMap(out var mapId);
var gridComp = mapMan.CreateGridEntity(mapId);
maps.SetTile(gridComp, Vector2i.Zero, new Tile(1));
grid = gridComp.Owner;
});
// Spawn player entity on grid 1
EntityUid player = default;
EntityUid other = default;
TransformComponent otherXform = default!;
var gridCoords = new EntityCoordinates(grid, new Vector2(0.5f, 0.5f));
var mapCoords = new EntityCoordinates(map, new Vector2(2, 2));
await server.WaitPost(() =>
{
player = sEntMan.SpawnEntity(null, gridCoords);
other = sEntMan.SpawnEntity(null, gridCoords);
otherXform = sEntMan.GetComponent<TransformComponent>(other);
// Ensure map PVS chunk is not empty
sEntMan.SpawnEntity(null, mapCoords);
// Attach player.
var session = sPlayerMan.Sessions.First();
server.PlayerMan.SetAttachedEntity(session, player);
sPlayerMan.JoinGame(session);
});
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Check player got properly attached
await client.WaitPost(() =>
{
var ent = cEntMan.GetNetEntity(cPlayerMan.LocalEntity);
Assert.That(ent, Is.EqualTo(sEntMan.GetNetEntity(player)));
});
// Move the player off-grid and back onto the grid in the same tick
xforms.SetCoordinates(other, otherXform, mapCoords);
xforms.SetCoordinates(other, otherXform, gridCoords);
// Run for a few ticks. The test just checks that no PVS asserts/errors happen.
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Repeat but in the opposite direction ( map -> grid -> map )
// first move to map and wait a bit.
xforms.SetCoordinates(other, otherXform, mapCoords);
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Move to and off grid in the same tick
xforms.SetCoordinates(other, otherXform, gridCoords);
xforms.SetCoordinates(other, otherXform, mapCoords);
// wait for errors.
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);
}
}

View File

@@ -1,112 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared.ContentPack;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.Map.Components;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
namespace Robust.UnitTesting.Server.Maps
{
[TestFixture]
public sealed partial class MapLoaderTest : RobustIntegrationTest
{
private const string MapData = @"
meta:
format: 7
category: Grid
engineVersion: 238.0.0
forkId: """"
forkVersion: """"
time: 12/22/2024 04:08:12
entityCount: 3
maps: []
grids:
- 1
orphans:
- 1
nullspace: []
tilemap: {}
entities:
- proto: """"
entities:
- uid: 1
mapInit: true
components:
- type: MetaData
- type: Transform
- type: MapGrid
chunks: {}
- type: Broadphase
- type: Physics
canCollide: False
- type: Fixtures
fixtures: {}
- type: MapSaveTileMap
- proto: MapDeserializeTest
entities:
- uid: 2
mapInit: true
components:
- type: Transform
parent: 1
- type: MapDeserializeTest
foo: 3
";
private const string Prototype = @"
- type: entity
id: MapDeserializeTest
components:
- type: MapDeserializeTest
foo: 1
bar: 2
";
[Test]
public async Task TestDataLoadPriority()
{
var opts = new ServerIntegrationOptions()
{
ExtraPrototypes = Prototype
};
var server = StartServer(opts);
await server.WaitIdleAsync();
var resourceManager = server.ResolveDependency<IResourceManagerInternal>();
resourceManager.MountString("/TestMap.yml", MapData);
var traversal = server.System<SharedGridTraversalSystem>();
traversal.Enabled = false;
var mapLoad = server.System<MapLoaderSystem>();
Entity<MapGridComponent>? grid = default;
await server.WaitPost(() =>
{
server.System<SharedMapSystem>().CreateMap(out var mapId);
Assert.That(mapLoad.TryLoadGrid(mapId, new ResPath("/TestMap.yml"), out grid));
});
var geid = grid!.Value.Owner;
var entity = server.EntMan.GetComponent<TransformComponent>(geid)._children.Single();
var c = server.EntMan.GetComponent<MapDeserializeTestComponent>(entity);
traversal.Enabled = true;
Assert.That(c.Bar, Is.EqualTo(2));
Assert.That(c.Foo, Is.EqualTo(3));
Assert.That(c.Baz, Is.EqualTo(-1));
}
[RegisterComponent]
private sealed partial class MapDeserializeTestComponent : Component
{
[DataField("foo")] public int Foo { get; set; } = -1;
[DataField("bar")] public int Bar { get; set; } = -1;
[DataField("baz")] public int Baz { get; set; } = -1;
}
}
}

View File

@@ -1,369 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using JetBrains.Annotations;
using Moq;
using Robust.Client.HWId;
using Robust.Server;
using Robust.Server.Configuration;
using Robust.Server.Console;
using Robust.Server.Containers;
using Robust.Server.Debugging;
using Robust.Server.GameObjects;
using Robust.Server.GameStates;
using Robust.Server.Localization;
using Robust.Server.Physics;
using Robust.Server.Player;
using Robust.Server.Prototypes;
using Robust.Server.Reflection;
using Robust.Server.Replays;
using Robust.Shared;
using Robust.Shared.Asynchronous;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.Containers;
using Robust.Shared.ContentPack;
using Robust.Shared.Exceptions;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Network;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Controllers;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Player;
using Robust.Shared.Profiling;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Reflection;
using Robust.Shared.Replays;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Threading;
using Robust.Shared.Timing;
namespace Robust.UnitTesting.Server
{
[PublicAPI]
public interface ISimulationFactory
{
ISimulationFactory RegisterComponents(CompRegistrationDelegate factory);
ISimulationFactory RegisterDependencies(DiContainerDelegate factory);
ISimulationFactory RegisterEntitySystems(EntitySystemRegistrationDelegate factory);
ISimulationFactory RegisterPrototypes(PrototypeRegistrationDelegate factory);
ISimulation InitializeInstance();
}
[PublicAPI]
public interface ISimulation
{
IDependencyCollection Collection { get; }
/// <summary>
/// Resolves a dependency directly out of IoC collection.
/// </summary>
T Resolve<T>();
/// <summary>
/// Adds a new map directly to the map manager.
/// </summary>
(EntityUid Uid, MapId MapId) CreateMap();
EntityUid SpawnEntity(string? protoId, EntityCoordinates coordinates);
EntityUid SpawnEntity(string? protoId, MapCoordinates coordinates);
}
/// <summary>
/// Helper methods for working with <see cref="ISimulation"/>.
/// </summary>
internal static class SimulationExtensions
{
public static T System<T>(this ISimulation simulation) where T : IEntitySystem
{
return simulation.Resolve<IEntitySystemManager>().GetEntitySystem<T>();
}
public static bool HasComp<T>(this ISimulation simulation, EntityUid entity) where T : IComponent
{
return simulation.Resolve<IEntityManager>().HasComponent<T>(entity);
}
public static T Comp<T>(this ISimulation simulation, EntityUid entity) where T : IComponent
{
return simulation.Resolve<IEntityManager>().GetComponent<T>(entity);
}
public static TransformComponent Transform(this ISimulation simulation, EntityUid entity)
{
return simulation.Comp<TransformComponent>(entity);
}
}
public delegate void DiContainerDelegate(IDependencyCollection diContainer);
public delegate void CompRegistrationDelegate(IComponentFactory factory);
public delegate void EntitySystemRegistrationDelegate(IEntitySystemManager systemMan);
public delegate void PrototypeRegistrationDelegate(IPrototypeManager protoMan);
public sealed class RobustServerSimulation : ISimulation, ISimulationFactory
{
private DiContainerDelegate? _diFactory;
private CompRegistrationDelegate? _regDelegate;
private EntitySystemRegistrationDelegate? _systemDelegate;
private PrototypeRegistrationDelegate? _protoDelegate;
public IDependencyCollection Collection { get; private set; } = default!;
public T Resolve<T>()
{
return Collection.Resolve<T>();
}
public (EntityUid Uid, MapId MapId) CreateMap()
{
var uid = Collection.Resolve<IEntityManager>().System<SharedMapSystem>().CreateMap(out var mapId);
return (uid, mapId);
}
public EntityUid SpawnEntity(string? protoId, EntityCoordinates coordinates)
{
var entMan = Collection.Resolve<IEntityManager>();
return entMan.SpawnEntity(protoId, coordinates);
}
public EntityUid SpawnEntity(string? protoId, MapCoordinates coordinates)
{
var entMan = Collection.Resolve<IEntityManager>();
return entMan.SpawnEntity(protoId, coordinates);
}
private RobustServerSimulation() { }
public ISimulationFactory RegisterDependencies(DiContainerDelegate factory)
{
_diFactory += factory;
return this;
}
public ISimulationFactory RegisterComponents(CompRegistrationDelegate factory)
{
_regDelegate += factory;
return this;
}
public ISimulationFactory RegisterEntitySystems(EntitySystemRegistrationDelegate factory)
{
_systemDelegate += factory;
return this;
}
public ISimulationFactory RegisterPrototypes(PrototypeRegistrationDelegate factory)
{
_protoDelegate += factory;
return this;
}
public ISimulation InitializeInstance()
{
var container = new DependencyCollection();
Collection = container;
IoCManager.InitThread(container, true);
//TODO: This is a long term project that should eventually have parity with the actual server/client/SP IoC registration.
// The goal is to be able to pull out all networking and frontend dependencies, and only have a core simulation running.
// This does NOT replace the full RobustIntegrationTest, or regular unit testing. This simulation sits in the middle
// and allows you to run integration testing only on the simulation.
//Tier 1: System
container.Register<ILogManager, LogManager>();
container.Register<IRuntimeLog, RuntimeLog>();
container.Register<IConfigurationManager, ServerNetConfigurationManager>();
container.Register<INetConfigurationManager, ServerNetConfigurationManager>();
container.Register<IConfigurationManagerInternal, ServerNetConfigurationManager>();
container.Register<IServerNetConfigurationManager, ServerNetConfigurationManager>();
container.Register<INetConfigurationManagerInternal, ServerNetConfigurationManager>();
container.Register<IDynamicTypeFactory, DynamicTypeFactory>();
container.Register<IDynamicTypeFactoryInternal, DynamicTypeFactory>();
container.Register<ILocalizationManager, ServerLocalizationManager>();
container.Register<IModLoader, TestingModLoader>();
container.Register<IModLoaderInternal, TestingModLoader>();
container.Register<ProfManager, ProfManager>();
container.RegisterInstance<ITaskManager>(new Mock<ITaskManager>().Object);
container.Register<HttpClientHolder>();
container.Register<IHttpClientHolder, HttpClientHolder>();
container.Register<IHWId, DummyHWId>();
var realReflection = new ServerReflectionManager();
realReflection.LoadAssemblies(new List<Assembly>(2)
{
AppDomain.CurrentDomain.GetAssemblyByName("Robust.Shared"),
AppDomain.CurrentDomain.GetAssemblyByName("Robust.Server"),
});
var reflectionManager = new Mock<IReflectionManager>();
reflectionManager
.Setup(x => x.FindTypesWithAttribute<MeansDataDefinitionAttribute>())
.Returns(() => new[]
{
typeof(DataDefinitionAttribute)
});
reflectionManager
.Setup(x => x.FindTypesWithAttribute(typeof(DataDefinitionAttribute)))
.Returns(() => new[]
{
typeof(EntityPrototype),
typeof(TransformComponent),
typeof(MetaDataComponent)
});
reflectionManager
.Setup(x => x.FindTypesWithAttribute<TypeSerializerAttribute>())
.Returns(() => realReflection.FindTypesWithAttribute<TypeSerializerAttribute>());
reflectionManager
.Setup(x => x.FindAllTypes())
.Returns(() => realReflection.FindAllTypes());
container.RegisterInstance<IBaseServerInternal>(new Mock<IBaseServerInternal>().Object);
container.RegisterInstance<IReflectionManager>(reflectionManager.Object); // tests should not be searching for types
container.RegisterInstance<IRobustSerializer>(new Mock<IRobustSerializer>().Object);
container.RegisterInstance<IResourceManager>(new Mock<IResourceManager>().Object); // no disk access for tests
container.RegisterInstance<IGameTiming>(new Mock<IGameTiming>().Object); // TODO: get timing working similar to RobustIntegrationTest
//Tier 2: Simulation
container.RegisterInstance<IConsoleHost>(new Mock<IConsoleHost>().Object); //Console is technically a frontend, we want to run headless
container.Register<IEntityManager, ServerEntityManager>();
container.Register<IServerEntityNetworkManager, ServerEntityManager>();
container.Register<EntityManager, ServerEntityManager>();
container.Register<IMapManager, NetworkedMapManager>();
container.Register<INetworkedMapManager, NetworkedMapManager>();
container.Register<IMapManagerInternal, NetworkedMapManager>();
container.Register<ISerializationManager, SerializationManager>();
container.Register<IRobustRandom, RobustRandom>();
container.Register<IPrototypeManager, ServerPrototypeManager>();
container.Register<IComponentFactory, ComponentFactory>();
container.Register<IEntitySystemManager, EntitySystemManager>();
container.Register<IManifoldManager, CollisionManager>();
container.Register<INetManager, NetManager>();
container.Register<IAuthManager, AuthManager>();
container.Register<ITileDefinitionManager, TileDefinitionManager>();
container.Register<IParallelManager, TestingParallelManager>();
container.Register<IParallelManagerInternal, TestingParallelManager>();
// Needed for grid fixture debugging.
container.Register<IConGroupController, ConGroupController>();
container.Register<EntityConsoleHost>();
// I just wanted to load pvs system
container.Register<IServerEntityManager, ServerEntityManager>();
container.Register<IServerNetManager, NetManager>();
// god help you if you actually need to test pvs functions
container.RegisterInstance<IPlayerManager>(new Mock<IPlayerManager>().Object);
container.RegisterInstance<ISharedPlayerManager>(new Mock<ISharedPlayerManager>().Object);
container.RegisterInstance<IServerGameStateManager>(new Mock<IServerGameStateManager>().Object);
container.RegisterInstance<IReplayRecordingManager>(new Mock<IReplayRecordingManager>().Object);
container.RegisterInstance<IServerReplayRecordingManager>(new Mock<IServerReplayRecordingManager>().Object);
_diFactory?.Invoke(container);
container.BuildGraph();
// Because of CVarDef, we have to load every one through reflection
// just in case a system needs one of them.
var configMan = container.Resolve<IConfigurationManagerInternal>();
configMan.Initialize(true);
configMan.LoadCVarsFromAssembly(typeof(Program).Assembly); // Server
configMan.LoadCVarsFromAssembly(typeof(ProgramShared).Assembly); // Shared
configMan.LoadCVarsFromAssembly(typeof(RobustServerSimulation).Assembly); // Tests
var logMan = container.Resolve<ILogManager>();
logMan.RootSawmill.AddHandler(new TestLogHandler(configMan, "SIM"));
var compFactory = container.Resolve<IComponentFactory>();
// if only we had some sort of attribute for automatically registering components.
compFactory.RegisterClass<MetaDataComponent>();
compFactory.RegisterClass<TransformComponent>();
compFactory.RegisterClass<MapGridComponent>();
compFactory.RegisterClass<MapComponent>();
compFactory.RegisterClass<MapLightComponent>();
compFactory.RegisterClass<PhysicsComponent>();
compFactory.RegisterClass<JointComponent>();
compFactory.RegisterClass<EyeComponent>();
compFactory.RegisterClass<GridTreeComponent>();
compFactory.RegisterClass<JointRelayTargetComponent>();
compFactory.RegisterClass<BroadphaseComponent>();
compFactory.RegisterClass<ContainerManagerComponent>();
compFactory.RegisterClass<FixturesComponent>();
compFactory.RegisterClass<CollisionWakeComponent>();
compFactory.RegisterClass<OccluderComponent>();
compFactory.RegisterClass<OccluderTreeComponent>();
compFactory.RegisterClass<CollideOnAnchorComponent>();
compFactory.RegisterClass<ActorComponent>();
_regDelegate?.Invoke(compFactory);
compFactory.GenerateNetIds();
var entityMan = container.Resolve<IEntityManager>();
entityMan.Initialize();
var entitySystemMan = container.Resolve<IEntitySystemManager>();
entitySystemMan.LoadExtraSystemType<PhysicsSystem>();
entitySystemMan.LoadExtraSystemType<SharedGridTraversalSystem>();
entitySystemMan.LoadExtraSystemType<ContainerSystem>();
entitySystemMan.LoadExtraSystemType<JointSystem>();
entitySystemMan.LoadExtraSystemType<MapSystem>();
entitySystemMan.LoadExtraSystemType<DebugPhysicsSystem>();
entitySystemMan.LoadExtraSystemType<DebugRayDrawingSystem>();
entitySystemMan.LoadExtraSystemType<BroadPhaseSystem>();
entitySystemMan.LoadExtraSystemType<CollisionWakeSystem>();
entitySystemMan.LoadExtraSystemType<FixtureSystem>();
entitySystemMan.LoadExtraSystemType<GridFixtureSystem>();
entitySystemMan.LoadExtraSystemType<TransformSystem>();
entitySystemMan.LoadExtraSystemType<EntityLookupSystem>();
entitySystemMan.LoadExtraSystemType<ServerMetaDataSystem>();
entitySystemMan.LoadExtraSystemType<PvsSystem>();
entitySystemMan.LoadExtraSystemType<InputSystem>();
entitySystemMan.LoadExtraSystemType<PvsOverrideSystem>();
_systemDelegate?.Invoke(entitySystemMan);
var mapManager = container.Resolve<IMapManager>();
mapManager.Initialize();
entityMan.Startup();
mapManager.Startup();
container.Resolve<INetManager>().Initialize(true);
container.Resolve<ISerializationManager>().Initialize();
var protoMan = container.Resolve<IPrototypeManager>();
protoMan.Initialize();
protoMan.RegisterKind(typeof(EntityPrototype), typeof(EntityCategoryPrototype));
_protoDelegate?.Invoke(protoMan);
// This just exists to set protoMan._hasEverBeenReloaded to True
// The code is perfect.
protoMan.LoadString("");
protoMan.ResolveResults();
return this;
}
public static ISimulationFactory NewSimulation()
{
return new RobustServerSimulation();
}
}
}

View File

@@ -1,70 +0,0 @@
using System;
using Moq;
using NUnit.Framework;
using Robust.Server.ViewVariables;
using Robust.Server.ViewVariables.Traits;
using Robust.Shared.Analyzers;
using Robust.Shared.Log;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Robust.UnitTesting.Server.ViewVariables
{
[Parallelizable]
[TestFixture]
internal sealed class ViewVariablesTraitMembersTest
{
[Test]
public void Test()
{
var ser = new Mock<IRobustSerializer>();
ser.Setup(p => p.CanSerialize(It.IsAny<Type>())).Returns(true);
var logger = new Mock<ISawmill>();
var session = new Mock<IViewVariablesSession>();
session.SetupGet(p => p.Object).Returns(new C());
session.SetupGet(p => p.ObjectType).Returns(typeof(C));
session.SetupGet(p => p.RobustSerializer).Returns(ser.Object);
var blob = new ViewVariablesTraitMembers(session.Object, logger.Object).DataRequest(new ViewVariablesRequestMembers());
Assert.That(blob, Is.TypeOf<ViewVariablesBlobMembers>());
var blobM = (ViewVariablesBlobMembers) blob!;
Assert.That(blobM.MemberGroups, Has.Count.EqualTo(2));
var group0 = blobM.MemberGroups[0];
var group1 = blobM.MemberGroups[1];
Assert.That(group0.groupName, Does.EndWith("+C"));
Assert.That(group1.groupName, Does.EndWith("+A"));
Assert.That(group0.groupMembers, Has.Count.EqualTo(2));
Assert.That(group1.groupMembers, Has.Count.EqualTo(1));
Assert.That(group0.groupMembers[0].Name, Is.EqualTo("Y"));
Assert.That(group0.groupMembers[1].Name, Is.EqualTo("Z"));
Assert.That(group1.groupMembers[0].Name, Is.EqualTo("X"));
}
#pragma warning disable 649
[Virtual]
private class A
{
[ViewVariables] public int X;
}
[Virtual]
private class B : A
{
public int Hidden { get; set; }
}
private sealed class C : B
{
[ViewVariables] public int Y { get; set; }
[ViewVariables] public string? Z { get; set; }
}
#pragma warning restore 649
}
}

View File

@@ -1,43 +0,0 @@
using NUnit.Framework;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Maths;
namespace Robust.UnitTesting.Shared.Audio;
[TestFixture]
public sealed class AudioGainVolume_Test
{
private static float[] _gainValues = new[]
{
1f,
0.5f,
0f,
};
private static float[] _volumeValues = new[]
{
-100f,
-3f,
0f,
1f,
100f,
};
[Test, TestCaseSource(nameof(_gainValues))]
public void GainCalculationTest(float value)
{
var volume = SharedAudioSystem.GainToVolume(value);
var gain = SharedAudioSystem.VolumeToGain(volume);
Assert.That(MathHelper.CloseTo(value, gain, 0.01f), $"Expected {value} and found {volume}");
}
[Test, TestCaseSource(nameof(_volumeValues))]
public void VolumeCalculationTest(float value)
{
var gain = SharedAudioSystem.VolumeToGain(value);
var volume = SharedAudioSystem.GainToVolume(gain);
Assert.That(MathHelper.CloseTo(value, volume, 0.01f), $"Expected {value} and found {volume}");
}
}

View File

@@ -1,78 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Robust.Shared.Collections;
namespace Robust.UnitTesting.Shared.Collections;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture, TestOf(typeof(OverflowQueue<>))]
public sealed class OverflowDictionary_Test
{
private static IEnumerable<(int size, int iterations)> TestParams => new[]
{
(10, 17),
(10, 4)
};
[Test]
public void ValueDisposerTest()
{
var disposedCalled = 0;
var dict = new OverflowDictionary<int, int>(1, (_) => disposedCalled++);
dict.Add(0,0);
dict.Add(1,0);
Assert.That(dict.ContainsKey(0), Is.False);
Assert.That(dict.ContainsKey(1));
Assert.That(disposedCalled, Is.EqualTo(1));
Assert.That(dict.Count, Is.EqualTo(1));
}
[Test]
public void TestQueue([ValueSource(nameof(TestParams))] (int size, int iterations) test)
{
Assert.Throws<InvalidOperationException>(() =>
{
var _ = new OverflowDictionary<int, int>(0);
});
var dict = new OverflowDictionary<int, int>(test.size);
for (int i = 0; i < test.iterations; i++)
{
dict.Add(i, i+1);
}
Assert.That(dict.ContainsKey(test.iterations-1));
Assert.Throws<InvalidOperationException>(() => dict.Add(test.iterations - 1, 0));
var overlap = Math.Max(test.iterations - test.size, 0);
for (int i = overlap; i < test.iterations; i++)
{
Assert.That(dict.TryGetValue(i, out var val));
Assert.That(val, Is.EqualTo(i+1));
}
if(overlap > 0)
{
Assert.That(dict.ContainsKey(0), Is.False);
Assert.That(dict.Count, Is.EqualTo(test.size));
}
else
{
Assert.That(dict.Count, Is.EqualTo(test.iterations));
}
dict.Clear();
Assert.That(dict.Count, Is.EqualTo(0));
//assert that the clear didnt mess up our internal ordering & array. just to make sure we didnt forget to reset something
dict.Add(0, 1);
Assert.That(dict.TryGetValue(0, out var value));
Assert.That(value, Is.EqualTo(1));
Assert.Throws<NotImplementedException>(() => dict.Remove(0));
}
}

View File

@@ -1,56 +0,0 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using Robust.Shared.Collections;
namespace Robust.UnitTesting.Shared.Collections;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture, TestOf(typeof(OverflowQueue<>))]
public sealed class OverflowQueue_Test
{
private static IEnumerable<(int size, int iterations)> TestParams => new[]
{
(10, 17),
(10, 4)
};
[Test]
public void TestQueue([ValueSource(nameof(TestParams))] (int size, int iterations) test)
{
var queue = new OverflowQueue<int>(test.size);
Assert.That(queue.Contains(0), Is.False);
for (int i = 0; i < test.iterations; i++)
{
queue.Enqueue(i);
}
var overlap = Math.Max(test.iterations - test.size, 0);
for (int i = overlap; i < test.iterations; i++)
{
Assert.That(queue.Contains(i));
}
Assert.That(queue.Contains(test.iterations-1));
Assert.That(queue.Contains(-1), Is.False);
Assert.That(queue.Peek(), Is.EqualTo(overlap));
var array = queue.ToArray();
Assert.That(array.Length, Is.EqualTo(Math.Min(test.size, test.iterations)));
for (var i = 0; i < array.Length; i++)
{
Assert.That(array[i], Is.EqualTo(i + overlap));
}
for (int i = overlap; i < test.iterations; i++)
{
Assert.That(queue.TryDequeue(out var item));
Assert.That(item, Is.EqualTo(i));
}
Assert.Throws<InvalidOperationException>(() => queue.Dequeue());
Assert.That(queue.TryDequeue(out _), Is.False);
}
}

View File

@@ -1,95 +0,0 @@
using NUnit.Framework;
using Robust.Shared.Collections;
namespace Robust.UnitTesting.Shared.Collections;
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture, TestOf(typeof(RingBufferList<>))]
public sealed class RingBufferListTest
{
[Test]
public void TestBasicAdd()
{
var list = new RingBufferList<int>();
list.Add(1);
list.Add(2);
list.Add(3);
Assert.That(list, NUnit.Framework.Is.EquivalentTo(new[] {1, 2, 3}));
}
[Test]
public void TestBasicAddAfterWrap()
{
var list = new RingBufferList<int>(6);
list.Add(1);
list.Add(2);
list.Add(3);
list.RemoveAt(0);
list.Add(4);
list.Add(5);
list.Add(6);
Assert.Multiple(() =>
{
// Ensure wrapping properly happened and we didn't expand.
// (one slot is wasted by nature of implementation)
Assert.That(list.Capacity, NUnit.Framework.Is.EqualTo(6));
Assert.That(list, NUnit.Framework.Is.EquivalentTo(new[] { 2, 3, 4, 5, 6 }));
});
}
[Test]
public void TestMiddleRemoveAtScenario1()
{
var list = new RingBufferList<int>(6);
list.Add(-1);
list.Add(-1);
list.Add(-1);
list.Add(-1);
list.Add(1);
list.RemoveAt(0);
list.RemoveAt(0);
list.RemoveAt(0);
list.RemoveAt(0);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(5);
list.Remove(4);
Assert.That(list, NUnit.Framework.Is.EquivalentTo(new[] {1, 2, 3, 5}));
}
[Test]
public void TestMiddleRemoveAtScenario2()
{
var list = new RingBufferList<int>(6);
list.Add(-1);
list.Add(-1);
list.Add(1);
list.RemoveAt(0);
list.RemoveAt(0);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(5);
list.Remove(3);
Assert.That(list, NUnit.Framework.Is.EquivalentTo(new[] {1, 2, 4, 5}));
}
[Test]
public void TestMiddleRemoveAtScenario3()
{
var list = new RingBufferList<int>(6);
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(5);
list.Remove(4);
Assert.That(list, NUnit.Framework.Is.EquivalentTo(new[] {1, 2, 3, 5}));
}
}

View File

@@ -1,59 +0,0 @@
using Robust.Shared.Maths;
using NUnit.Framework;
namespace Robust.UnitTesting.Shared
{
[Parallelizable(ParallelScope.All | ParallelScope.Fixtures)]
[TestFixture]
public sealed class ColorUtils_Test
{
[Test]
public void TestInterpolateBetween()
{
var black = Color.Black;
var white = Color.White;
Assert.That(Color.InterpolateBetween(black, white, 1), Is.EqualTo(white));
Assert.That(Color.InterpolateBetween(black, white, 0), Is.EqualTo(black));
// Should be grey but floating points hate us so...
// The byte conversion shouldn't have issues because the error marging should be small enough.
var grey = Color.InterpolateBetween(black, white, 0.5f);
Assert.That(grey.RByte, Is.EqualTo(127));
Assert.That(grey.GByte, Is.EqualTo(127));
Assert.That(grey.BByte, Is.EqualTo(127));
}
[Test]
public void TestWithAlpha()
{
Assert.That(Color.White.WithAlpha(128), Is.EqualTo(new Color(255, 255, 255, 128)));
Assert.That(Color.White.WithAlpha(0.5f), Is.EqualTo(new Color(1, 1, 1, 0.5f)));
}
[Test]
public void TestFromHex()
{
// Test fallback.
Assert.That(Color.FromHex("honk", Color.AliceBlue), Is.EqualTo(Color.AliceBlue));
Assert.That(() => Color.FromHex("honk"), Throws.ArgumentException);
Assert.That(() => Color.FromHex("#FFFFFFFF00"), Throws.ArgumentException);
Assert.That(Color.FromHex("#FFFFFF"), Is.EqualTo(Color.White));
Assert.That(Color.FromHex("#FFFFFFFF"), Is.EqualTo(Color.White));
Assert.That(Color.FromHex("#FFFFFF69"), Is.EqualTo(Color.White.WithAlpha(0x69)));
Assert.That(Color.FromHex("#FFF"), Is.EqualTo(Color.White));
Assert.That(Color.FromHex("#FFF8"), Is.EqualTo(Color.White.WithAlpha(0x88)));
Assert.That(Color.FromHex("#ABCDEF"), Is.EqualTo(new Color(0xAB, 0xCD, 0xEF)));
}
[Test]
public void TestByteParts()
{
var color = new Color(23, 38, 18, 20);
Assert.That(color.RByte, Is.EqualTo(23));
Assert.That(color.GByte, Is.EqualTo(38));
Assert.That(color.BByte, Is.EqualTo(18));
Assert.That(color.AByte, Is.EqualTo(20));
}
}
}

View File

@@ -1,42 +0,0 @@
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared.Configuration;
using Robust.Shared.Log;
namespace Robust.UnitTesting.Shared.Configuration;
[Parallelizable(ParallelScope.All)]
[TestFixture]
[TestOf(typeof(ConfigurationManagerTest))]
internal sealed class ConfigurationIntegrationTest : RobustIntegrationTest
{
[Test]
public async Task TestSaveNoWarningServer()
{
using var server = StartServer(new ServerIntegrationOptions
{
FailureLogLevel = LogLevel.Warning
});
await server.WaitPost(() =>
{
// ReSharper disable once AccessToDisposedClosure
var cfg = server.Resolve<IConfigurationManager>();
cfg.SaveToFile();
});
}
[Test]
public async Task TestSaveNoWarningClient()
{
using var server = StartClient(new ClientIntegrationOptions
{
FailureLogLevel = LogLevel.Warning
});
await server.WaitPost(() =>
{
// ReSharper disable once AccessToDisposedClosure
var cfg = server.Resolve<IConfigurationManager>();
cfg.SaveToFile();
});
}
}

View File

@@ -1,154 +0,0 @@
using Moq;
using NUnit.Framework;
using Robust.Server.Configuration;
using Robust.Shared.Configuration;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Network;
using Robust.Shared.Replays;
using Robust.Shared.Timing;
namespace Robust.UnitTesting.Shared.Configuration
{
[TestFixture]
[Parallelizable(ParallelScope.All)]
[TestOf(typeof(ConfigurationManager))]
public sealed class ConfigurationManagerTest
{
[Test]
public void TestSubscribeUnsubscribe()
{
var mgr = MakeCfg();
mgr.RegisterCVar("foo.bar", 5);
var lastValue = 0;
var timesRan = 0;
void ValueChanged(int value)
{
timesRan += 1;
lastValue = value;
}
mgr.OnValueChanged<int>("foo.bar", ValueChanged);
mgr.SetCVar("foo.bar", 2);
Assert.That(timesRan, Is.EqualTo(1), "OnValueChanged did not run!");
Assert.That(lastValue, Is.EqualTo(2), "OnValueChanged value was wrong!");
mgr.UnsubValueChanged<int>("foo.bar", ValueChanged);
Assert.That(timesRan, Is.EqualTo(1), "UnsubValueChanged did not unsubscribe!");
}
[Test]
public void TestSubscribe_SubscribeMultipleThenUnsubscribe()
{
var mgr = MakeCfg();
mgr.RegisterCVar("foo.bar", 5);
var lastValueBar1 = 0;
var lastValueBar2 = 0;
var lastValueBar3 = 0;
var lastValueBar4 = 0;
var subscription = mgr.SubscribeMultiple()
.OnValueChanged<int>("foo.bar", value => lastValueBar1 = value)
.OnValueChanged<int>("foo.bar", value => lastValueBar2 = value)
.OnValueChanged<int>("foo.bar", value => lastValueBar3 = value)
.OnValueChanged<int>("foo.bar", value => lastValueBar4 = value);
mgr.SetCVar("foo.bar", 1);
Assert.That(lastValueBar1, Is.EqualTo(1), "OnValueChanged value was wrong!");
Assert.That(lastValueBar2, Is.EqualTo(1), "OnValueChanged value was wrong!");
Assert.That(lastValueBar3, Is.EqualTo(1), "OnValueChanged value was wrong!");
Assert.That(lastValueBar4, Is.EqualTo(1), "OnValueChanged value was wrong!");
subscription.Dispose();
mgr.SetCVar("foo.bar", 10);
Assert.That(lastValueBar1, Is.EqualTo(1), "OnValueChanged value was wrong!");
Assert.That(lastValueBar2, Is.EqualTo(1), "OnValueChanged value was wrong!");
Assert.That(lastValueBar3, Is.EqualTo(1), "OnValueChanged value was wrong!");
Assert.That(lastValueBar4, Is.EqualTo(1), "OnValueChanged value was wrong!");
}
[Test]
public void TestSubscribe_Unsubscribe()
{
var mgr = MakeCfg();
mgr.RegisterCVar("foo.bar", 5);
mgr.RegisterCVar("foo.foo", 2);
var lastValueBar = 0;
var lastValueFoo = 0;
var subscription = mgr.SubscribeMultiple()
.OnValueChanged<int>("foo.bar", value => lastValueBar = value)
.OnValueChanged<int>("foo.foo", value => lastValueFoo = value);
mgr.SetCVar("foo.bar", 1);
mgr.SetCVar("foo.foo", 3);
Assert.That(lastValueBar, Is.EqualTo(1), "OnValueChanged value was wrong!");
Assert.That(lastValueFoo, Is.EqualTo(3), "OnValueChanged value was wrong!");
subscription.Dispose();
mgr.SetCVar("foo.bar", 10);
mgr.SetCVar("foo.foo", 30);
Assert.That(lastValueBar, Is.EqualTo(1), "OnValueChanged value was wrong!");
Assert.That(lastValueFoo, Is.EqualTo(3), "OnValueChanged value was wrong!");
}
[Test]
public void TestOverrideDefaultValue()
{
var mgr = MakeCfg();
mgr.RegisterCVar("foo.bar", 5);
var value = 0;
mgr.OnValueChanged<int>("foo.bar", v => value = v);
// Change default value, this fires the value changed callback.
mgr.OverrideDefault("foo.bar", 10);
Assert.That(value, Is.EqualTo(10));
Assert.That(mgr.GetCVar<int>("foo.bar"), Is.EqualTo(10));
// Modify the cvar programmatically, also fires the callback.
mgr.SetCVar("foo.bar", 7);
Assert.That(value, Is.EqualTo(7));
Assert.That(mgr.GetCVar<int>("foo.bar"), Is.EqualTo(7));
// We have a value set now, so changing the default won't do anything.
mgr.OverrideDefault("foo.bar", 15);
Assert.That(value, Is.EqualTo(7));
Assert.That(mgr.GetCVar<int>("foo.bar"), Is.EqualTo(7));
}
private IConfigurationManager MakeCfg()
{
var collection = new DependencyCollection();
collection.RegisterInstance<IReplayRecordingManager>(new Mock<IReplayRecordingManager>().Object);
collection.RegisterInstance<INetManager>(new Mock<INetManager>().Object);
collection.Register<ConfigurationManager, ServerNetConfigurationManager>();
collection.Register<IServerNetConfigurationManager, ServerNetConfigurationManager>();
collection.Register<IGameTiming, GameTiming>();
collection.Register<ILogManager, LogManager>();
collection.BuildGraph();
return collection.Resolve<ConfigurationManager>();
}
}
}

View File

@@ -1,74 +0,0 @@
using System.Collections.Immutable;
using System.Reflection.Metadata;
using NUnit.Framework;
using Pidgin;
using static Robust.Shared.ContentPack.AssemblyTypeChecker;
namespace Robust.UnitTesting.Shared.ContentPack
{
[Parallelizable(ParallelScope.All)]
[TestFixture]
public sealed class AssemblyTypeCheckerParsingTest
{
[Test]
public void TestMethod()
{
var res = MethodParser.ParseOrThrow(" void foo ( int , string )");
Assert.That(res.Name, Is.EqualTo("foo"));
Assert.That(res.ReturnType, Is.EqualTo(new MTypePrimitive(PrimitiveTypeCode.Void)));
Assert.That(res.ParameterTypes,
Is.EquivalentTo(new[]
{
new MTypePrimitive(PrimitiveTypeCode.Int32),
new MTypePrimitive(PrimitiveTypeCode.String)
}));
}
[Test]
public void TestParseConstructor()
{
var res = MethodParser.ParseOrThrow(" void .ctor ( int , string )");
Assert.That(res.Name, Is.EqualTo(".ctor"));
Assert.That(res.ReturnType, Is.EqualTo(new MTypePrimitive(PrimitiveTypeCode.Void)));
Assert.That(res.ParameterTypes,
Is.EquivalentTo(new[]
{
new MTypePrimitive(PrimitiveTypeCode.Int32),
new MTypePrimitive(PrimitiveTypeCode.String)
}));
}
[Test]
public void TestMethodInvalid()
{
Assert.That(() => MethodParser.ParseOrThrow("foo ( int , string)"), Throws.InstanceOf<ParseException>());
}
[Test]
public void TestField()
{
var res = FieldParser.ParseOrThrow("string bar");
Assert.That(res.Name, Is.EqualTo("bar"));
Assert.That(res.FieldType, Is.EqualTo(new MTypePrimitive(PrimitiveTypeCode.String)));
}
[Test]
public void TestGenericMethod()
{
var res = MethodParser.ParseOrThrow("string Concat(System.Collections.Generic.IEnumerable`1<!!0>)");
Assert.That(res.Name, Is.EqualTo("Concat"));
Assert.That(res.ReturnType, Is.EqualTo(new MTypePrimitive(PrimitiveTypeCode.String)));
Assert.That(res.ParameterTypes,
Is.EquivalentTo(new MType[]
{
new MTypeGeneric(
new MTypeParsed("System.Collections.Generic.IEnumerable`1"),
new MType[] {new MTypeGenericMethodPlaceHolder(0)}.ToImmutableArray())
}));
}
}
}

View File

@@ -1,126 +0,0 @@
using System.IO;
using NUnit.Framework;
using Robust.Shared.ContentPack;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
namespace Robust.UnitTesting.Shared.ContentPack
{
[TestFixture]
public sealed class ResourceManagerTest : RobustUnitTest
{
private static Stream ZipStream => typeof(ResourceManagerTest).Assembly
.GetManifestResourceStream("Robust.UnitTesting.Shared.ContentPack.ZipTest.zip")!;
private static readonly byte[] Data =
{
0x56, 0x75, 0x6c, 0x6b, 0x61, 0x6e, 0x20, 0x57, 0x68, 0x65, 0x6e
};
private static readonly string[] InvalidPaths =
{
// Reserved filenames like COM ports.
"/foo/COM1",
"COM2",
"/COM3/foo",
"COM4",
"COM5",
"COM6",
"COM7",
"COM8",
"COM9",
"LPT1",
"LPT2",
"LPT3",
"LPT4",
"LPT5",
"LPT6",
"LPT7",
"LPT8",
"LPT9",
"NUL",
"AUX",
"CON",
"PRN",
// ? is banned.
"/foo/bar?",
// * is banned.
"/foo*/baz",
// | is banned.
"/yay|bugs",
// : is banned.
"/yay: bugs",
// " is banned.
"/yay... \"bugs\"",
// \0 is banned.
"/\0",
// \x01-\x1f are banned.
"/\n",
};
[OneTimeSetUp]
public void Setup()
{
var componentFactory = IoCManager.Resolve<IComponentFactory>();
componentFactory.GenerateNetIds();
var stream = new MemoryStream(Data);
var resourceManager = IoCManager.Resolve<IResourceManagerInternal>();
resourceManager.MountStreamAt(stream, new ("/a/b/c.dat"));
}
[Test]
public void TestMountedStreamRead()
{
var resourceManager = IoCManager.Resolve<IResourceManagerInternal>();
using (var stream = resourceManager.ContentFileRead("/a/b/c.dat"))
{
Assert.That(stream.CopyToArray(), Is.EqualTo(Data));
}
}
[Test]
public void TestInvalidPaths([ValueSource(nameof(InvalidPaths))] string path)
{
Assert.That(ResourceManager.IsPathValid(new (path)), Is.False);
}
[Test]
public void TestInvalidPathsLowerCase([ValueSource(nameof(InvalidPaths))] string path)
{
Assert.That(ResourceManager.IsPathValid(new (path.ToLowerInvariant())), Is.False);
}
[Test]
public void TestZipRead()
{
var resourceManager = IoCManager.Resolve<IResourceManagerInternal>();
resourceManager.MountContentPack(ZipStream);
var stream = resourceManager.ContentFileRead("/foo.txt");
Assert.That(ReadString(stream), Is.EqualTo("Honk!! \n"));
}
[Test]
public void TestZipFind()
{
var resourceManager = IoCManager.Resolve<IResourceManagerInternal>();
resourceManager.MountContentPack(ZipStream);
var found = resourceManager.ContentFindFiles("/bar/");
Assert.That(found, Is.EquivalentTo(new[]
{
new ResPath("/bar/a.txt"),
new ResPath("/bar/b.txt"),
}));
}
private static string ReadString(Stream stream)
{
using var streamReader = new StreamReader(stream);
return streamReader.ReadToEnd();
}
}
}

View File

@@ -1,112 +0,0 @@
using System;
using NUnit.Framework;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.UnitTesting.Shared.ContentPack;
[TestFixture]
[TestOf(typeof(ResourceManager))]
[Parallelizable(ParallelScope.All)]
public sealed class ResourceManagerTest2
{
[Test]
public void TestMemoryRootReadFiles()
{
var (_, res) = CreateRes();
var testRoot = new MemoryContentRoot();
testRoot.AddOrUpdateFile(new ResPath("/a.txt"), "a"u8.ToArray());
testRoot.AddOrUpdateFile(new ResPath("/b.txt"), "b"u8.ToArray());
testRoot.AddOrUpdateFile(new ResPath("/c.txt"), "c"u8.ToArray());
testRoot.AddOrUpdateFile(new ResPath("/d/foo.txt"), "foo"u8.ToArray());
res.AddRoot(new ResPath("/"), testRoot);
Assert.Multiple(() =>
{
Assert.That(res.ContentFileReadAllText("/a.txt"), Is.EqualTo("a"));
Assert.That(res.ContentFileReadAllText("/b.txt"), Is.EqualTo("b"));
Assert.That(res.ContentFileReadAllText("/c.txt"), Is.EqualTo("c"));
Assert.That(res.ContentFileReadAllText("/d/foo.txt"), Is.EqualTo("foo"));
});
}
[Test]
public void TestMemoryRootGetDirectoryEntries()
{
var (_, res) = CreateRes();
var testRoot = new MemoryContentRoot();
testRoot.AddOrUpdateFile(new ResPath("/a.txt"), "a"u8.ToArray());
testRoot.AddOrUpdateFile(new ResPath("/b.txt"), "b"u8.ToArray());
testRoot.AddOrUpdateFile(new ResPath("/c.txt"), "c"u8.ToArray());
testRoot.AddOrUpdateFile(new ResPath("/d/foo.txt"), "foo"u8.ToArray());
res.AddRoot(new ResPath("/"), testRoot);
Assert.Multiple(() =>
{
Assert.That(res.ContentGetDirectoryEntries(new ResPath("/")), Is.EquivalentTo(new[]
{
"a.txt",
"b.txt",
"c.txt",
"d/"
}));
// Listing should work both with and without a trailing /
Assert.That(res.ContentGetDirectoryEntries(new ResPath("/d")), Is.EquivalentTo(new[]
{
"foo.txt"
}));
Assert.That(res.ContentGetDirectoryEntries(new ResPath("/d/")), Is.EquivalentTo(new[]
{
"foo.txt"
}));
});
}
/// <summary>
/// Test that a mount path is properly shown as a directory entry.
/// </summary>
[Test]
public void TestGetDirectoryEntriesTwoMounts()
{
var (_, res) = CreateRes();
var testRoot = new MemoryContentRoot();
testRoot.AddOrUpdateFile(new ResPath("foo.txt"), Array.Empty<byte>());
testRoot.AddOrUpdateFile(new ResPath("bar/foo.txt"), Array.Empty<byte>());
res.AddRoot(new ResPath("/"), testRoot);
res.AddRoot(new ResPath("/second"), testRoot);
res.AddRoot(new ResPath("/third/fourth"), testRoot);
Assert.That(res.ContentGetDirectoryEntries(new ResPath("/")), Is.EquivalentTo(new []
{
"foo.txt",
"bar/",
"second/",
"third/"
}));
}
private static (IDependencyCollection, IResourceManagerInternal) CreateRes()
{
var deps = new DependencyCollection();
deps.Register<IResourceManager, ResourceManager>();
deps.Register<IResourceManagerInternal, ResourceManager>();
deps.Register<IConfigurationManager, ConfigurationManager>();
deps.Register<ILogManager, LogManager>();
deps.Register<IGameTiming, GameTiming>();
deps.BuildGraph();
return (deps, deps.Resolve<IResourceManagerInternal>());
}
}

View File

@@ -1,99 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Server.Player;
using Robust.Shared.Enums;
using Robust.Shared.IoC;
using Robust.Shared.Network;
namespace Robust.UnitTesting.Shared
{
[TestFixture]
public sealed class EngineIntegrationTest_Test : RobustIntegrationTest
{
[Test]
public void ServerStartsCorrectlyTest()
{
ServerIntegrationInstance? server = null;
Assert.DoesNotThrow(() => server = StartServer());
Assert.That(server, Is.Not.Null);
}
[Test]
public void ClientStartsCorrectlyTest()
{
ClientIntegrationInstance? client = null;
Assert.DoesNotThrow(() => client = StartClient());
Assert.That(client, Is.Not.Null);
}
[Test]
public async Task ConsoleErrorsFailTest()
{
var server = StartServer();
var client = StartClient();
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
// test missing commands
await client.WaitPost(() => Assert.Throws<AssertionException>(() => client.ConsoleHost.ExecuteCommand("aaaaaaaa")));
// test invalid commands / missing arguments
await client.WaitPost(() => Assert.Throws<AssertionException>(() => client.ConsoleHost.ExecuteCommand("cvar")));
// and repeat for the server
await server.WaitPost(() => Assert.Throws<AssertionException>(() => server.ConsoleHost.ExecuteCommand("aaaaaaaa")));
await server.WaitPost(() => Assert.Throws<AssertionException>(() => server.ConsoleHost.ExecuteCommand("cvar")));
}
[Test]
public async Task ServerClientPairConnectCorrectlyTest()
{
var server = StartServer();
var client = StartClient();
Assert.That(server, Is.Not.Null);
Assert.That(client, Is.Not.Null);
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
// Connect client to the server...
var netMan = client.ResolveDependency<IClientNetManager>();
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
client.Post(() => netMan.ClientConnect(null!, 0, null!));
// Run 10 synced ticks...
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
await server.WaitAssertion(() =>
{
var playerManager = IoCManager.Resolve<IPlayerManager>();
// There must be a player connected.
Assert.That(playerManager.PlayerCount, Is.EqualTo(1));
// Get the only player...
var player = playerManager.Sessions.Single();
Assert.That(player.Status, Is.EqualTo(SessionStatus.Connected));
Assert.That(player.Channel.IsConnected, Is.True);
});
await client.WaitAssertion(() =>
{
var netManager = IoCManager.Resolve<IClientNetManager>();
Assert.That(netManager.IsConnected, Is.True);
Assert.That(netManager.ServerChannel, Is.Not.Null);
Assert.That(netManager.ServerChannel!.IsConnected, Is.True);
});
await client.WaitPost(() => netMan.ClientDisconnect(""));
await server.WaitRunTicks(5);
await client.WaitRunTicks(5);
}
}
}

View File

@@ -1,389 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Maths;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Systems;
using Robust.UnitTesting.Server;
namespace Robust.UnitTesting.Shared
{
[TestFixture, TestOf(typeof(EntityLookupSystem))]
public sealed class EntityLookupTest
{
private static readonly MapId MapId = new MapId(1);
private static readonly TestCaseData[] IntersectingCases = new[]
{
// Big offset
new TestCaseData(true, new MapCoordinates(new Vector2(10.5f, 10.5f), MapId), new MapCoordinates(new Vector2(10.5f, 10.5f), MapId), 0.25f, true),
};
private static readonly TestCaseData[] InRangeCases = new[]
{
new TestCaseData(true, new MapCoordinates(Vector2.One, MapId), new MapCoordinates(Vector2.Zero, MapId), 0.5f, false),
new TestCaseData(true, new MapCoordinates(new Vector2(10f, 10f), MapId), new MapCoordinates(new Vector2(9.5f, 9.5f), MapId), 0.5f, true),
// Close but no cigar
new TestCaseData(true, new MapCoordinates(new Vector2(10f, 10f), MapId), new MapCoordinates(new Vector2(9f, 9f), MapId), 0.5f, false),
// Large area so useboundsquery
new TestCaseData(true, new MapCoordinates(new Vector2(0f, 0f), MapId), new MapCoordinates(new Vector2(0f, 1000f), MapId), 999f, false),
new TestCaseData(true, new MapCoordinates(new Vector2(0f, 0f), MapId), new MapCoordinates(new Vector2(0f, 999f), MapId), 999f, true),
// NoFixturecases
new TestCaseData(false, new MapCoordinates(Vector2.One, MapId), new MapCoordinates(Vector2.Zero, MapId), 0.5f, false),
new TestCaseData(false, new MapCoordinates(new Vector2(10f, 10f), MapId), new MapCoordinates(new Vector2(9.5f, 9.5f), MapId), 0.5f, false),
// Close but no cigar
new TestCaseData(false, new MapCoordinates(new Vector2(10f, 10f), MapId), new MapCoordinates(new Vector2(9f, 9f), MapId), 0.5f, false),
};
// Remember this test data is relative.
private static readonly TestCaseData[] Box2Cases = new[]
{
new TestCaseData(true, new MapCoordinates(Vector2.One, MapId), Box2.UnitCentered, false),
new TestCaseData(true, new MapCoordinates(new Vector2(10f, 10f), MapId), Box2.UnitCentered, true),
};
private static readonly TestCaseData[] TileCases = new[]
{
new TestCaseData(true, new MapCoordinates(Vector2.One, MapId), Vector2i.Zero, false),
new TestCaseData(true, new MapCoordinates(new Vector2(10f, 10f), MapId), Vector2i.Zero, true),
// Need to make sure we don't pull out neighbor fixtures even if they barely touch our tile
new TestCaseData(true, new MapCoordinates(new Vector2(11f + 0.35f, 10f), MapId), Vector2i.Zero, false),
};
private EntityUid GetPhysicsEntity(IEntityManager entManager, MapCoordinates spawnPos)
{
var ent = entManager.SpawnEntity(null, spawnPos);
var physics = entManager.AddComponent<PhysicsComponent>(ent);
entManager.System<FixtureSystem>().TryCreateFixture(ent, new PhysShapeCircle(0.35f, Vector2.Zero), "fix1");
entManager.System<SharedPhysicsSystem>().SetCanCollide(ent, true, body: physics);
return ent;
}
private Entity<MapGridComponent> SetupGrid(MapId mapId, SharedMapSystem mapSystem, IEntityManager entManager, IMapManager mapManager)
{
var grid = mapManager.CreateGridEntity(mapId);
entManager.System<SharedTransformSystem>().SetLocalPosition(grid.Owner, new Vector2(10f, 10f));
mapSystem.SetTile(grid, Vector2i.Zero, new Tile(1));
return grid;
}
#region Entity
/*
* We double these tests just because these have slightly different codepaths at the moment.
*
*/
[Test, TestCaseSource(nameof(Box2Cases))]
public void TestEntityAnyIntersecting(bool physics, MapCoordinates spawnPos, Box2 queryBounds, bool result)
{
var sim = RobustServerSimulation.NewSimulation();
var server = sim.InitializeInstance();
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
var entManager = server.Resolve<IEntityManager>();
var mapManager = server.Resolve<IMapManager>();
var mapSystem = entManager.System<SharedMapSystem>();
mapSystem.CreateMap(spawnPos.MapId);
var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager);
if (physics)
GetPhysicsEntity(entManager, spawnPos);
else
entManager.Spawn(null, spawnPos);
var outcome = lookup.AnyLocalEntitiesIntersecting(grid.Owner, queryBounds, LookupFlags.All);
Assert.That(outcome, Is.EqualTo(result));
mapSystem.DeleteMap(spawnPos.MapId);
}
[Test, TestCaseSource(nameof(Box2Cases))]
public void TestEntityAnyLocalIntersecting(bool physics, MapCoordinates spawnPos, Box2 queryBounds, bool result)
{
var sim = RobustServerSimulation.NewSimulation();
var server = sim.InitializeInstance();
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
var entManager = server.Resolve<IEntityManager>();
var mapManager = server.Resolve<IMapManager>();
var mapSystem = entManager.System<SharedMapSystem>();
mapSystem.CreateMap(spawnPos.MapId);
var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager);
if (physics)
GetPhysicsEntity(entManager, spawnPos);
else
entManager.Spawn(null, spawnPos);
var outcome = lookup.AnyLocalEntitiesIntersecting(grid.Owner, queryBounds, LookupFlags.All);
Assert.That(outcome, Is.EqualTo(result));
mapSystem.DeleteMap(spawnPos.MapId);
}
/// <summary>
/// Tests Box2 local queries for a particular lookup ID.
/// </summary>
[Test, TestCaseSource(nameof(Box2Cases))]
public void TestEntityGridLocalIntersecting(bool physics, MapCoordinates spawnPos, Box2 queryBounds, bool result)
{
var sim = RobustServerSimulation.NewSimulation();
var server = sim.InitializeInstance();
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
var entManager = server.Resolve<IEntityManager>();
var mapManager = server.Resolve<IMapManager>();
var mapSystem = entManager.System<SharedMapSystem>();
mapSystem.CreateMap(spawnPos.MapId);
var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager);
if (physics)
GetPhysicsEntity(entManager, spawnPos);
else
entManager.Spawn(null, spawnPos);
var entities = new HashSet<Entity<TransformComponent>>();
lookup.GetLocalEntitiesIntersecting(grid.Owner, queryBounds, entities);
Assert.That(entities.Count > 0, Is.EqualTo(result));
mapSystem.DeleteMap(spawnPos.MapId);
}
/// <summary>
/// Tests Box2 local queries for a particular lookup ID.
/// </summary>
[Test, TestCaseSource(nameof(TileCases))]
public void TestEntityGridTileIntersecting(bool physics, MapCoordinates spawnPos, Vector2i queryTile, bool result)
{
var sim = RobustServerSimulation.NewSimulation();
var server = sim.InitializeInstance();
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
var entManager = server.Resolve<IEntityManager>();
var mapManager = server.Resolve<IMapManager>();
var mapSystem = entManager.System<SharedMapSystem>();
mapSystem.CreateMap(spawnPos.MapId);
var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager);
if (physics)
GetPhysicsEntity(entManager, spawnPos);
else
entManager.Spawn(null, spawnPos);
var entities = new HashSet<Entity<TransformComponent>>();
lookup.GetLocalEntitiesIntersecting(grid.Owner, queryTile, entities);
Assert.That(entities.Count > 0, Is.EqualTo(result));
mapSystem.DeleteMap(spawnPos.MapId);
}
#endregion
#region EntityUid
[Test, TestCaseSource(nameof(InRangeCases))]
public void TestMapInRange(bool physics, MapCoordinates spawnPos, MapCoordinates queryPos, float range, bool result)
{
var sim = RobustServerSimulation.NewSimulation();
var server = sim.InitializeInstance();
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
var entManager = server.Resolve<IEntityManager>();
var mapSystem = entManager.System<SharedMapSystem>();
entManager.System<SharedMapSystem>().CreateMap(spawnPos.MapId);
if (physics)
GetPhysicsEntity(entManager, spawnPos);
else
entManager.Spawn(null, spawnPos);
Assert.That(lookup.GetEntitiesInRange(queryPos.MapId, queryPos.Position, range).Count > 0, Is.EqualTo(result));
mapSystem.DeleteMap(spawnPos.MapId);
}
[Test, TestCaseSource(nameof(IntersectingCases))]
public void TestGridIntersecting(bool physics, MapCoordinates spawnPos, MapCoordinates queryPos, float range, bool result)
{
var sim = RobustServerSimulation.NewSimulation();
var server = sim.InitializeInstance();
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
var entManager = server.Resolve<IEntityManager>();
var mapManager = server.Resolve<IMapManager>();
var mapSystem = entManager.System<SharedMapSystem>();
mapSystem.CreateMap(spawnPos.MapId);
var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager);
if (physics)
GetPhysicsEntity(entManager, spawnPos);
else
entManager.Spawn(null, spawnPos);
_ = entManager.SpawnEntity(null, spawnPos);
var bounds = new Box2Rotated(Box2.CenteredAround(queryPos.Position, new Vector2(range, range)));
Assert.That(lookup.GetEntitiesIntersecting(queryPos.MapId, bounds).Count > 0, Is.EqualTo(result));
mapSystem.DeleteMap(spawnPos.MapId);
}
[Test, TestCaseSource(nameof(InRangeCases))]
public void TestGridInRange(bool physics, MapCoordinates spawnPos, MapCoordinates queryPos, float range, bool result)
{
var sim = RobustServerSimulation.NewSimulation();
var server = sim.InitializeInstance();
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
var entManager = server.Resolve<IEntityManager>();
var mapManager = server.Resolve<IMapManager>();
var mapSystem = entManager.System<SharedMapSystem>();
mapSystem.CreateMap(spawnPos.MapId);
var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager);
if (physics)
GetPhysicsEntity(entManager, spawnPos);
else
entManager.Spawn(null, spawnPos);
_ = entManager.SpawnEntity(null, spawnPos);
Assert.That(lookup.GetEntitiesInRange(queryPos.MapId, queryPos.Position, range).Count > 0, Is.EqualTo(result));
mapSystem.DeleteMap(spawnPos.MapId);
}
[Test, TestCaseSource(nameof(InRangeCases))]
public void TestMapNoFixtureInRange(bool physics, MapCoordinates spawnPos, MapCoordinates queryPos, float range, bool result)
{
var sim = RobustServerSimulation.NewSimulation();
var server = sim.InitializeInstance();
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
var entManager = server.Resolve<IEntityManager>();
var mapSystem = entManager.System<SharedMapSystem>();
entManager.System<SharedMapSystem>().CreateMap(spawnPos.MapId);
if (physics)
GetPhysicsEntity(entManager, spawnPos);
else
entManager.Spawn(null, spawnPos);
Assert.That(lookup.GetEntitiesInRange(queryPos.MapId, queryPos.Position, range).Count > 0, Is.EqualTo(result));
mapSystem.DeleteMap(spawnPos.MapId);
}
/// <summary>
/// Tests Box2 local queries for a particular lookup ID.
/// </summary>
[Test, TestCaseSource(nameof(Box2Cases))]
public void TestGridAnyIntersecting(bool physics, MapCoordinates spawnPos, Box2 queryBounds, bool result)
{
var sim = RobustServerSimulation.NewSimulation();
var server = sim.InitializeInstance();
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
var entManager = server.Resolve<IEntityManager>();
var mapManager = server.Resolve<IMapManager>();
var mapSystem = entManager.System<SharedMapSystem>();
mapSystem.CreateMap(spawnPos.MapId);
var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager);
if (physics)
GetPhysicsEntity(entManager, spawnPos);
else
entManager.Spawn(null, spawnPos);
var outcome = lookup.AnyLocalEntitiesIntersecting(grid.Owner, queryBounds, LookupFlags.All);
Assert.That(outcome, Is.EqualTo(result));
mapSystem.DeleteMap(spawnPos.MapId);
}
/// <summary>
/// Tests Box2 local queries for a particular lookup ID.
/// </summary>
[Test, TestCaseSource(nameof(Box2Cases))]
public void TestGridLocalIntersecting(bool physics, MapCoordinates spawnPos, Box2 queryBounds, bool result)
{
var sim = RobustServerSimulation.NewSimulation();
var server = sim.InitializeInstance();
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
var entManager = server.Resolve<IEntityManager>();
var mapManager = server.Resolve<IMapManager>();
var mapSystem = entManager.System<SharedMapSystem>();
mapSystem.CreateMap(spawnPos.MapId);
var grid = SetupGrid(spawnPos.MapId, mapSystem, entManager, mapManager);
if (physics)
GetPhysicsEntity(entManager, spawnPos);
else
entManager.Spawn(null, spawnPos);
var entities = new HashSet<EntityUid>();
lookup.GetLocalEntitiesIntersecting(grid.Owner, queryBounds, entities);
Assert.That(entities.Count > 0, Is.EqualTo(result));
mapSystem.DeleteMap(spawnPos.MapId);
}
#endregion
/// <summary>
/// Is the entity correctly removed / added to EntityLookup when anchored
/// </summary>
[Test]
public void TestAnchoring()
{
var sim = RobustServerSimulation.NewSimulation();
var server = sim.InitializeInstance();
var lookup = server.Resolve<IEntitySystemManager>().GetEntitySystem<EntityLookupSystem>();
var entManager = server.Resolve<IEntityManager>();
var mapManager = server.Resolve<IMapManager>();
var mapSystem = entManager.System<SharedMapSystem>();
var transformSystem = entManager.System<SharedTransformSystem>();
var mapId = server.CreateMap().MapId;
var grid = mapManager.CreateGridEntity(mapId);
var theMapSpotBeingUsed = new Box2(Vector2.Zero, Vector2.One);
mapSystem.SetTile(grid, new Vector2i(), new Tile(1));
Assert.That(lookup.GetEntitiesIntersecting(mapId, theMapSpotBeingUsed).ToList(), Is.Empty);
// Setup and check it actually worked
var dummy = entManager.SpawnEntity(null, new MapCoordinates(Vector2.Zero, mapId));
Assert.That(lookup.GetEntitiesIntersecting(mapId, theMapSpotBeingUsed).ToList(), Has.Count.EqualTo(1));
var xform = entManager.GetComponent<TransformComponent>(dummy);
// When anchoring it should still get returned.
transformSystem.AnchorEntity(dummy, xform);
Assert.That(xform.Anchored, Is.True);
Assert.That(lookup.GetEntitiesIntersecting(mapId, theMapSpotBeingUsed).ToList(), Has.Count.EqualTo(1));
transformSystem.Unanchor(dummy, xform);
Assert.That(xform.Anchored, Is.False);
Assert.That(lookup.GetEntitiesIntersecting(mapId, theMapSpotBeingUsed).ToList().Count, Is.EqualTo(1));
entManager.DeleteEntity(dummy);
entManager.DeleteEntity(grid);
mapSystem.DeleteMap(mapId);
}
}
}

View File

@@ -1,143 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
namespace Robust.UnitTesting.Shared.EntitySerialization;
[TestFixture]
public sealed class AlwaysPushSerializationTest : RobustIntegrationTest
{
private const string Prototype = @"
- type: entity
id: TestEntityCompositionParent
components:
- type: EntitySaveTest
list: [ 1, 2 ]
- type: entity
id: TestEntityCompositionChild
parent: TestEntityCompositionParent
components:
- type: EntitySaveTest
list: [ 3 , 4 ]
";
/// <summary>
/// This test checks that deserializing an entity with some component that has the
/// <see cref="AlwaysPushInheritanceAttribute"/> works as intended. Previously the attribute would cause the entity
/// prototype to **always** append it's contents to the loaded entity, effectively causing
/// the <see cref="TestAlwaysPushComponent.List"/> data-field to grow each time a map was loaded and saved.
/// </summary>
[Test]
[TestOf(typeof(AlwaysPushInheritanceAttribute))]
public async Task TestAlwaysPushSerialization()
{
var opts = new ServerIntegrationOptions
{
ExtraPrototypes = Prototype
};
var server = StartServer(opts);
await server.WaitIdleAsync();
var entMan = server.EntMan;
// Create a new map and spawn in some entities.
MapId mapId = default;
Entity<TransformComponent, EntitySaveTestComponent> parent1 = default;
Entity<TransformComponent, EntitySaveTestComponent> parent2 = default;
Entity<TransformComponent, EntitySaveTestComponent> parent3 = default;
Entity<TransformComponent, EntitySaveTestComponent> child1 = default;
Entity<TransformComponent, EntitySaveTestComponent> child2 = default;
Entity<TransformComponent, EntitySaveTestComponent> child3 = default;
var path = new ResPath($"{nameof(TestAlwaysPushSerialization)}.yml");
await server.WaitPost(() =>
{
server.System<SharedMapSystem>().CreateMap(out mapId);
var coords = new MapCoordinates(0, 0, mapId);
var parent1Uid = entMan.Spawn("TestEntityCompositionParent", coords);
var parent2Uid = entMan.Spawn("TestEntityCompositionParent", coords);
var parent3Uid = entMan.Spawn("TestEntityCompositionParent", coords);
var child1Uid = entMan.Spawn("TestEntityCompositionChild", coords);
var child2Uid = entMan.Spawn("TestEntityCompositionChild", coords);
var child3Uid = entMan.Spawn("TestEntityCompositionChild", coords);
parent1 = Get(parent1Uid, entMan);
parent2 = Get(parent2Uid, entMan);
parent3 = Get(parent3Uid, entMan);
child1 = Get(child1Uid, entMan);
child2 = Get(child2Uid, entMan);
child3 = Get(child3Uid, entMan);
});
// Assign a unique id to each entity (so they can be identified after saving & loading a map)
parent1.Comp2!.Id = nameof(parent1);
parent2.Comp2!.Id = nameof(parent2);
parent3.Comp2!.Id = nameof(parent3);
child1.Comp2!.Id = nameof(child1);
child2.Comp2!.Id = nameof(child2);
child3.Comp2!.Id = nameof(child3);
// The inheritance pushing for the prototypes should ensure that the parent & child prototype's lists were merged.
Assert.That(parent1.Comp2.List.SequenceEqual(new[] {1, 2}));
Assert.That(parent2.Comp2.List.SequenceEqual(new[] {1, 2}));
Assert.That(parent3.Comp2.List.SequenceEqual(new[] {1, 2}));
Assert.That(child1.Comp2.List.SequenceEqual(new[] {3, 4, 1, 2}));
Assert.That(child2.Comp2.List.SequenceEqual(new[] {3, 4, 1, 2}));
Assert.That(child3.Comp2.List.SequenceEqual(new[] {3, 4, 1, 2}));
// Modify data on some components.
parent2.Comp2.List.Add(-1);
child2.Comp2.List.Add(-1);
parent3.Comp2.List.RemoveAt(1);
child3.Comp2.List.RemoveAt(1);
Assert.That(parent1.Comp2.List.SequenceEqual(new[] {1, 2}));
Assert.That(parent2.Comp2.List.SequenceEqual(new[] {1, 2, -1}));
Assert.That(parent3.Comp2.List.SequenceEqual(new[] {1}));
Assert.That(child1.Comp2.List.SequenceEqual(new[] {3, 4, 1, 2}));
Assert.That(child2.Comp2.List.SequenceEqual(new[] {3, 4, 1, 2, -1}));
Assert.That(child3.Comp2.List.SequenceEqual(new[] {3, 1, 2}));
// Save map to yaml
var loader = server.System<MapLoaderSystem>();
var map = server.System<SharedMapSystem>();
Assert.That(loader.TrySaveMap(mapId, path));
// Delete the entities
await server.WaitPost(() => map.DeleteMap(mapId));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Load the map
await server.WaitPost(() =>
{
Assert.That(loader.TryLoadMap(path, out var ent, out _));
mapId = ent!.Value.Comp.MapId;
});
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(6));
// Find the deserialized entities
parent1 = Find(nameof(parent1), entMan);
parent2 = Find(nameof(parent2), entMan);
parent3 = Find(nameof(parent3), entMan);
child1 = Find(nameof(child1), entMan);
child2 = Find(nameof(child2), entMan);
child3 = Find(nameof(child3), entMan);
// Verify that the entity data has not changed.
Assert.That(parent1.Comp2.List.SequenceEqual(new[] {1, 2}));
Assert.That(parent2.Comp2.List.SequenceEqual(new[] {1, 2, -1}));
Assert.That(parent3.Comp2.List.SequenceEqual(new[] {1}));
Assert.That(child1.Comp2.List.SequenceEqual(new[] {3, 4, 1, 2}));
Assert.That(child2.Comp2.List.SequenceEqual(new[] {3, 4, 1, 2, -1}));
Assert.That(child3.Comp2.List.SequenceEqual(new[] {3, 1, 2}));
}
}

View File

@@ -1,318 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Components;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
namespace Robust.UnitTesting.Shared.EntitySerialization;
[TestFixture]
public sealed partial class AutoIncludeSerializationTest : RobustIntegrationTest
{
private const string TestTileDefId = "a";
private const string TestPrototypes = $@"
- type: testTileDef
id: space
- type: testTileDef
id: {TestTileDefId}
";
[Test]
public async Task TestAutoIncludeSerialization()
{
var server = StartServer(new() { Pool = false, ExtraPrototypes = TestPrototypes }); // Pool=false due to TileDef registration
await server.WaitIdleAsync();
var entMan = server.EntMan;
var mapSys = server.System<SharedMapSystem>();
var loader = server.System<MapLoaderSystem>();
var mapMan = server.ResolveDependency<IMapManager>();
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
var mapPath = new ResPath($"{nameof(AutoIncludeSerializationTest)}_map.yml");
var gridPath = new ResPath($"{nameof(AutoIncludeSerializationTest)}_grid.yml");
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan, "space");
var tDef = server.ProtoMan.Index<TileDef>(TestTileDefId);
// Create a map that contains an entity that references a nullspace entity.
MapId mapId = default;
Entity<TransformComponent, EntitySaveTestComponent> map = default;
Entity<TransformComponent, EntitySaveTestComponent> grid = default;
Entity<TransformComponent, EntitySaveTestComponent> onGrid = default;
Entity<TransformComponent, EntitySaveTestComponent> offGrid = default;
Entity<TransformComponent, EntitySaveTestComponent> nullSpace = default;
void AssertCount(int expected) => Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(expected));
await server.WaitPost(() =>
{
var mapUid = mapSys.CreateMap(out mapId);
var gridUid = mapMan.CreateGridEntity(mapId);
mapSys.SetTile(gridUid, Vector2i.Zero, new Tile(tDef.TileId));
var onGridUid = entMan.SpawnEntity(null, new EntityCoordinates(gridUid, 0.5f, 0.5f));
var offGridUid = entMan.SpawnEntity(null, new MapCoordinates(10f, 10f, mapId));
var nullSpaceUid = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
map = Get(mapUid, entMan);
grid = Get(gridUid, entMan);
onGrid = Get(onGridUid, entMan);
offGrid = Get(offGridUid, entMan);
nullSpace = Get(nullSpaceUid, entMan);
});
await server.WaitRunTicks(5);
Assert.That(map.Comp1!.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(grid.Comp1!.ParentUid, Is.EqualTo(map.Owner));
Assert.That(onGrid.Comp1!.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(offGrid.Comp1!.ParentUid, Is.EqualTo(map.Owner));
Assert.That(nullSpace.Comp1!.ParentUid, Is.EqualTo(EntityUid.Invalid));
// Assign unique ids.
map.Comp2!.Id = nameof(map);
grid.Comp2!.Id = nameof(grid);
onGrid.Comp2!.Id = nameof(onGrid);
offGrid.Comp2!.Id = nameof(offGrid);
nullSpace.Comp2!.Id = nameof(nullSpace);
// First simple map loading without any references to other entities.
// This will cause the null-space entity to be lost.
// Save the map, then delete all the entities.
AssertCount(5);
Assert.That(loader.TrySaveMap(mapId, mapPath));
Assert.That(loader.TrySaveGrid(grid, gridPath));
await server.WaitPost(() => mapSys.DeleteMap(mapId));
AssertCount(1);
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
AssertCount(0);
// Load up the file that only saved the grid and check that the expected entities exist.
await server.WaitPost(() => mapSys.CreateMap(out mapId));
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
AssertCount(2);
grid = Find(nameof(grid), entMan);
onGrid = Find(nameof(onGrid), entMan);
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
await server.WaitPost(() => mapSys.DeleteMap(mapId));
AssertCount(0);
// Load up the map, and check that the expected entities exist.
Entity<MapComponent>? loadedMap = default;
HashSet<Entity<MapGridComponent>>? loadedGrids = default!;
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out loadedMap, out loadedGrids)));
mapId = loadedMap!.Value.Comp.MapId;
Assert.That(loadedGrids, Has.Count.EqualTo(1));
AssertCount(4);
map = Find(nameof(map), entMan);
grid = Find(nameof(grid), entMan);
onGrid = Find(nameof(onGrid), entMan);
offGrid = Find(nameof(offGrid), entMan);
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(offGrid.Comp1.ParentUid, Is.EqualTo(map.Owner));
// Re-spawn the nullspace entity
await server.WaitPost(() =>
{
var nullSpaceUid = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
nullSpace = Get(nullSpaceUid, entMan);
nullSpace.Comp2.Id = nameof(nullSpace);
});
// Repeat the previous saves, but with an entity that references the null-space entity.
onGrid.Comp2.Entity = nullSpace.Owner;
AssertCount(5);
Assert.That(loader.TrySaveMap(mapId, mapPath));
Assert.That(loader.TrySaveGrid(grid, gridPath));
await server.WaitPost(() => mapSys.DeleteMap(mapId));
AssertCount(1);
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
AssertCount(0);
// Load up the file that only saved the grid and check that the expected entities exist.
await server.WaitPost(() => mapSys.CreateMap(out mapId));
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
AssertCount(3);
grid = Find(nameof(grid), entMan);
onGrid = Find(nameof(onGrid), entMan);
nullSpace = Find(nameof(nullSpace), entMan);
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(onGrid.Comp2.Entity, Is.EqualTo(nullSpace.Owner));
Assert.That(nullSpace.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
await server.WaitPost(() => mapSys.DeleteMap(mapId));
AssertCount(1);
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
AssertCount(0);
// Load up the map, and check that the expected entities exist.
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out loadedMap, out loadedGrids)));
mapId = loadedMap!.Value.Comp.MapId;
Assert.That(loadedGrids, Has.Count.EqualTo(1));
AssertCount(5);
map = Find(nameof(map), entMan);
grid = Find(nameof(grid), entMan);
onGrid = Find(nameof(onGrid), entMan);
offGrid = Find(nameof(offGrid), entMan);
nullSpace = Find(nameof(nullSpace), entMan);
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(offGrid.Comp1.ParentUid, Is.EqualTo(map.Owner));
Assert.That(onGrid.Comp2.Entity, Is.EqualTo(nullSpace.Owner));
Assert.That(nullSpace.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
// Check that attempting to save a reference to a non-null-space entity does not auto-include it.
Entity<TransformComponent, EntitySaveTestComponent> otherMap = default;
await server.WaitPost(() =>
{
var otherMapUid = mapSys.CreateMap();
otherMap = Get(otherMapUid, entMan);
otherMap.Comp2.Id = nameof(otherMap);
});
onGrid.Comp2.Entity = otherMap.Owner;
// By default it should log an error, but tests don't have a nice way to validate that an error was logged, so we'll just suppress it.
var opts = SerializationOptions.Default with {MissingEntityBehaviour = MissingEntityBehaviour.Ignore};
AssertCount(6);
Assert.That(loader.TrySaveMap(mapId, mapPath, opts));
Assert.That(loader.TrySaveGrid(grid, gridPath, opts));
await server.WaitPost(() => mapSys.DeleteMap(mapId));
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
await server.WaitPost(() => entMan.DeleteEntity(otherMap));
AssertCount(0);
// Check the grid file
await server.WaitPost(() => mapSys.CreateMap(out mapId));
var dOpts = DeserializationOptions.Default with {LogInvalidEntities = false};
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _, dOpts)));
AssertCount(2);
grid = Find(nameof(grid), entMan);
onGrid = Find(nameof(onGrid), entMan);
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
await server.WaitPost(() => mapSys.DeleteMap(mapId));
AssertCount(0);
// Check the map file
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out loadedMap, out loadedGrids, dOpts)));
mapId = loadedMap!.Value.Comp.MapId;
Assert.That(loadedGrids, Has.Count.EqualTo(1));
AssertCount(4);
map = Find(nameof(map), entMan);
grid = Find(nameof(grid), entMan);
onGrid = Find(nameof(onGrid), entMan);
offGrid = Find(nameof(offGrid), entMan);
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(offGrid.Comp1.ParentUid, Is.EqualTo(map.Owner));
// repeat the check, but this time with auto inclusion fully enabled.
Entity<TransformComponent, EntitySaveTestComponent> otherEnt = default;
await server.WaitPost(() =>
{
var otherMapUid = mapSys.CreateMap(out var otherMapId);
otherMap = Get(otherMapUid, entMan);
otherMap.Comp2.Id = nameof(otherMap);
var otherEntUid = entMan.SpawnEntity(null, new MapCoordinates(0, 0, otherMapId));
otherEnt = Get(otherEntUid, entMan);
otherEnt.Comp2.Id = nameof(otherEnt);
var nullSpaceUid = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
nullSpace = Get(nullSpaceUid, entMan);
nullSpace.Comp2.Id = nameof(nullSpace);
});
onGrid.Comp2.Entity = otherMap.Owner;
otherEnt.Comp2!.Entity = nullSpace;
AssertCount(7);
opts = opts with {MissingEntityBehaviour = MissingEntityBehaviour.AutoInclude};
Assert.That(loader.TrySaveGeneric(map.Owner, mapPath, out var cat, opts));
Assert.That(cat, Is.EqualTo(FileCategory.Unknown));
Assert.That(loader.TrySaveGeneric(grid.Owner, gridPath, out cat, opts));
Assert.That(cat, Is.EqualTo(FileCategory.Unknown));
await server.WaitPost(() => mapSys.DeleteMap(mapId));
await server.WaitPost(() => entMan.DeleteEntity(otherMap));
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
AssertCount(0);
// Check the grid file
await server.WaitPost(() => mapSys.CreateMap(out mapId));
var mapLoadOpts = MapLoadOptions.Default with
{
DeserializationOptions = DeserializationOptions.Default with {LogOrphanedGrids = false}
};
LoadResult? result = default;
await server.WaitAssertion(() => Assert.That(loader.TryLoadGeneric(gridPath, out result, mapLoadOpts)));
Assert.That(result!.Grids, Has.Count.EqualTo(1));
Assert.That(result.Orphans, Is.Empty); // Grid was orphaned, but was adopted after a new map was created
Assert.That(result.Maps, Has.Count.EqualTo(2));
Assert.That(result.NullspaceEntities, Has.Count.EqualTo(1));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1)); // auto-generated map isn't marked as "loaded"
AssertCount(5);
grid = Find(nameof(grid), entMan);
onGrid = Find(nameof(onGrid), entMan);
otherMap = Find(nameof(otherMap), entMan);
otherEnt = Find(nameof(otherEnt), entMan);
nullSpace = Find(nameof(nullSpace), entMan);
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(otherEnt.Comp1.ParentUid, Is.EqualTo(otherMap.Owner));
Assert.That(otherMap.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(nullSpace.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
await server.WaitPost(() => entMan.DeleteEntity(otherMap));
await server.WaitPost(() => entMan.DeleteEntity(grid.Comp1.ParentUid));
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
await server.WaitPost(() => mapSys.DeleteMap(mapId));
AssertCount(0);
// Check the map file
await server.WaitAssertion(() => Assert.That(loader.TryLoadGeneric(mapPath, out result)));
Assert.That(result.Orphans, Is.Empty);
Assert.That(result.NullspaceEntities, Has.Count.EqualTo(1));
Assert.That(result.Grids, Has.Count.EqualTo(1));
Assert.That(result.Maps, Has.Count.EqualTo(2));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(2));
AssertCount(7);
map = Find(nameof(map), entMan);
grid = Find(nameof(grid), entMan);
onGrid = Find(nameof(onGrid), entMan);
offGrid = Find(nameof(offGrid), entMan);
otherMap = Find(nameof(otherMap), entMan);
otherEnt = Find(nameof(otherEnt), entMan);
nullSpace = Find(nameof(nullSpace), entMan);
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(offGrid.Comp1.ParentUid, Is.EqualTo(map.Owner));
Assert.That(otherEnt.Comp1.ParentUid, Is.EqualTo(otherMap.Owner));
Assert.That(otherMap.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(nullSpace.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
await server.WaitPost(() => entMan.DeleteEntity(map));
await server.WaitPost(() => entMan.DeleteEntity(otherMap));
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
AssertCount(0);
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
}
}

View File

@@ -1,450 +0,0 @@
using System.Text;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared.ContentPack;
using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Components;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Utility;
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
namespace Robust.UnitTesting.Shared.EntitySerialization;
[TestFixture]
public sealed partial class BackwardsCompatibilityTest
{
/// <summary>
/// Check that v3 maps can be loaded. This simply tries to load a file and doesn't do a lot of extra validation.
/// </summary>
/// <remarks>
/// The file was pilfered from content integration tests ("floor3x3.yml") and modified slightly.
/// See also the comments around <see cref="EntityDeserializer.OldestSupportedVersion"/> that point out that v3
/// isn't even really loadable anymore.
/// </remarks>
[Test]
public async Task TestLoadV3()
{
var server = StartServer(new ServerIntegrationOptions {ExtraPrototypes = PrototypeV3});
await server.WaitIdleAsync();
var entMan = server.EntMan;
var mapSys = server.System<SharedMapSystem>();
var loader = server.System<MapLoaderSystem>();
var meta = server.System<MetaDataSystem>();
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
var resourceManager = server.ResolveDependency<IResourceManagerInternal>();
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan);
var gridPath = new ResPath($"{nameof(MapDataV3Grid)}.yml");
resourceManager.MountString(gridPath.ToString(), MapDataV3Grid);
MapId mapId = default;
EntityUid mapUid = default;
Entity<TransformComponent, EntitySaveTestComponent> map;
Entity<TransformComponent, EntitySaveTestComponent> ent;
Entity<TransformComponent, EntitySaveTestComponent> grid;
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
await server.WaitPost(() => mapUid = mapSys.CreateMap(out mapId));
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(mapUid));
Assert.That(meta.EntityPaused(ent), Is.False);
Assert.That(meta.EntityPaused(grid), Is.False);
Assert.That(meta.EntityPaused(mapUid), Is.False);
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(mapUid).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
await server.WaitPost(() => entMan.DeleteEntity(mapUid));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
var mapPath = new ResPath($"{nameof(MapDataV3Map)}.yml");
resourceManager.MountString(mapPath.ToString(), MapDataV3Map);
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out _, out _)));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
map = Find(nameof(map), entMan);
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(meta.EntityPaused(ent), Is.True);
Assert.That(meta.EntityPaused(grid), Is.True);
Assert.That(meta.EntityPaused(map), Is.True);
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
Is.EqualTo(EntityLifeStage.Initialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
Is.EqualTo(EntityLifeStage.Initialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(map).EntityLifeStage,
Is.EqualTo(EntityLifeStage.Initialized));
await server.WaitPost(() => entMan.DeleteEntity(map));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Repeat test, but with the initialize maps option enabled.
// Apparently mounted strings can only be read a single time.
// So have to re-mount them.
var mapPath2 = new ResPath($"{nameof(MapDataV3Map)}2.yml");
resourceManager.MountString(mapPath2.ToString(), MapDataV3Map);
var opts = DeserializationOptions.Default with {InitializeMaps = true};
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath2, out _, out _, opts)));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
map = Find(nameof(map), entMan);
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(meta.EntityPaused(ent), Is.False);
Assert.That(meta.EntityPaused(grid), Is.False);
Assert.That(meta.EntityPaused(map), Is.False);
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(map).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
await server.WaitPost(() => entMan.DeleteEntity(map));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
}
private const string MapDataV3Grid = @"
meta:
format: 3
name: DemoStation
author: Space-Wizards
postmapinit: false
tilemap:
0: Space
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
10: 10
11: 11
12: 12
13: 13
14: 14
15: 15
16: 16
17: 17
18: 18
19: 19
20: 20
21: 21
22: 22
23: 23
24: 24
25: 25
26: 26
27: 27
28: 28
29: 29
30: 30
31: 31
32: 32
33: 33
34: 34
35: 35
36: 36
37: 37
38: 38
39: 39
40: 40
41: 41
42: 42
43: 43
44: 44
45: 45
46: 46
47: 47
48: 48
49: 49
50: 50
51: 51
52: 52
53: 53
54: 54
55: 55
56: 56
57: 57
58: 58
59: 59
60: 60
61: 61
62: 62
63: 63
64: 64
65: 65
66: 66
67: 67
68: 68
69: 69
70: 70
71: 71
72: 72
73: 73
74: 74
75: 75
76: 76
77: 77
78: 78
79: 79
80: 80
81: 81
82: 82
83: 83
84: 84
85: 85
86: 86
87: 87
88: 88
entities:
- uid: 0
components:
- type: MetaData
- parent: null
type: Transform
- chunks:
-1,-1:
ind: -1,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAPgAAAA==
-1,0:
ind: -1,0
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
0,0:
ind: 0,0
tiles: CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
0,-1:
ind: 0,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
type: MapGrid
- type: Broadphase
- angularDamping: 0.05
linearDamping: 0.05
fixedRotation: False
bodyType: Dynamic
type: Physics
- fixtures: {}
type: Fixtures
- type: OccluderTree
- id: grid
type: EntitySaveTest
- uid: 1
type: V3TestProto
components:
- pos: 0.5,0.5
parent: 0
type: Transform
- id: ent
type: EntitySaveTest
...
";
private const string MapDataV3Map = @"
meta:
format: 3
name: DemoStation
author: Space-Wizards
postmapinit: false
tilemap:
0: Space
1: 1
2: 2
3: 3
4: 4
5: 5
6: 6
7: 7
8: 8
9: 9
10: 10
11: 11
12: 12
13: 13
14: 14
15: 15
16: 16
17: 17
18: 18
19: 19
20: 20
21: 21
22: 22
23: 23
24: 24
25: 25
26: 26
27: 27
28: 28
29: 29
30: 30
31: 31
32: 32
33: 33
34: 34
35: 35
36: 36
37: 37
38: 38
39: 39
40: 40
41: 41
42: 42
43: 43
44: 44
45: 45
46: 46
47: 47
48: 48
49: 49
50: 50
51: 51
52: 52
53: 53
54: 54
55: 55
56: 56
57: 57
58: 58
59: 59
60: 60
61: 61
62: 62
63: 63
64: 64
65: 65
66: 66
67: 67
68: 68
69: 69
70: 70
71: 71
72: 72
73: 73
74: 74
75: 75
76: 76
77: 77
78: 78
79: 79
80: 80
81: 81
82: 82
83: 83
84: 84
85: 85
86: 86
87: 87
88: 88
entities:
- uid: 123
components:
- type: MetaData
- type: Transform
- type: Map
- type: EntitySaveTest
id: map
- uid: 0
components:
- type: MetaData
- parent: 123
type: Transform
- chunks:
-1,-1:
ind: -1,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAPgAAAA==
-1,0:
ind: -1,0
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
0,0:
ind: 0,0
tiles: CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
0,-1:
ind: 0,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
type: MapGrid
- type: Broadphase
- angularDamping: 0.05
linearDamping: 0.05
fixedRotation: False
bodyType: Dynamic
type: Physics
- fixtures: {}
type: Fixtures
- type: OccluderTree
- id: grid
type: EntitySaveTest
- uid: 1
type: V3TestProto
components:
- pos: 0.5,0.5
parent: 0
type: Transform
- id: ent
type: EntitySaveTest
...
";
private static string GenerateTileDefs(int count)
{
var sb = new StringBuilder();
for (var i = 0; i < count; i++)
{
sb.Append($@"
- type: testTileDef
id: {i}"
);
}
return sb.ToString();
}
private static readonly string PrototypeV3 = $@"
- type: entity
id: V3TestProto
components:
- type: EntitySaveTest
list: [ 1, 2 ]
- type: testTileDef
id: Space
{GenerateTileDefs(88)}
";
}

View File

@@ -1,267 +0,0 @@
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared.ContentPack;
using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Components;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Utility;
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
namespace Robust.UnitTesting.Shared.EntitySerialization;
[TestFixture]
public sealed partial class BackwardsCompatibilityTest
{
/// <summary>
/// Check that v4 maps can be loaded. This simply tries to load a file and doesn't do a lot of extra validation.
/// </summary>
/// <remarks>
/// The file was pilfered from content integration tests ("floor3x3.yml") and modified slightly.
/// </remarks>
[Test]
public async Task TestLoadV4()
{
var server = StartServer(new ServerIntegrationOptions {ExtraPrototypes = PrototypeV4});
await server.WaitIdleAsync();
var entMan = server.EntMan;
var mapSys = server.System<SharedMapSystem>();
var loader = server.System<MapLoaderSystem>();
var meta = server.System<MetaDataSystem>();
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
var resourceManager = server.ResolveDependency<IResourceManagerInternal>();
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan, "Space");
var gridPath = new ResPath($"{nameof(MapDataV4Grid)}.yml");
resourceManager.MountString(gridPath.ToString(), MapDataV4Grid);
MapId mapId = default;
EntityUid mapUid = default;
Entity<TransformComponent, EntitySaveTestComponent> map;
Entity<TransformComponent, EntitySaveTestComponent> ent;
Entity<TransformComponent, EntitySaveTestComponent> grid;
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
await server.WaitPost(() => mapUid = mapSys.CreateMap(out mapId));
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(mapUid));
Assert.That(meta.EntityPaused(ent), Is.False);
Assert.That(meta.EntityPaused(grid), Is.False);
Assert.That(meta.EntityPaused(mapUid), Is.False);
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(mapUid).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
await server.WaitPost(() => entMan.DeleteEntity(mapUid));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
var mapPath = new ResPath($"{nameof(MapDataV4Map)}.yml");
resourceManager.MountString(mapPath.ToString(), MapDataV4Map);
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out _, out _)));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
map = Find(nameof(map), entMan);
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(meta.EntityPaused(ent), Is.True);
Assert.That(meta.EntityPaused(grid), Is.True);
Assert.That(meta.EntityPaused(map), Is.True);
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
Is.EqualTo(EntityLifeStage.Initialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
Is.EqualTo(EntityLifeStage.Initialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(map).EntityLifeStage,
Is.EqualTo(EntityLifeStage.Initialized));
await server.WaitPost(() => entMan.DeleteEntity(map));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Repeat test, but with the initialize maps option enabled.
// Apparently mounted strings can only be read a single time.
// So have to re-mount them.
var mapPath2 = new ResPath($"{nameof(MapDataV4Map)}2.yml");
resourceManager.MountString(mapPath2.ToString(), MapDataV4Map);
var opts = DeserializationOptions.Default with {InitializeMaps = true};
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath2, out _, out _, opts)));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
map = Find(nameof(map), entMan);
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(meta.EntityPaused(ent), Is.False);
Assert.That(meta.EntityPaused(grid), Is.False);
Assert.That(meta.EntityPaused(map), Is.False);
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(map).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
await server.WaitPost(() => entMan.DeleteEntity(map));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
}
private const string MapDataV4Grid = @"
meta:
format: 4
name: DemoStation
author: Space-Wizards
postmapinit: false
tilemap:
0: Space
11: A
68: B
entities:
- proto: """"
entities:
- uid: 2
components:
- type: MetaData
- parent: invalid
type: Transform
- chunks:
-1,-1:
ind: -1,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAARAAAAA==
-1,0:
ind: -1,0
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
0,0:
ind: 0,0
tiles: CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
0,-1:
ind: 0,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
type: MapGrid
- type: Broadphase
- angularDamping: 0.05
linearDamping: 0.05
fixedRotation: False
bodyType: Dynamic
type: Physics
- type: OccluderTree
- id: grid
type: EntitySaveTest
- proto: V4TestProto
entities:
- uid: 1
components:
- pos: 0.5,0.5
parent: 2
type: Transform
- id: ent
type: EntitySaveTest
...
";
private const string MapDataV4Map = @"
meta:
format: 4
name: DemoStation
author: Space-Wizards
postmapinit: false
tilemap:
0: Space
11: A
68: B
entities:
- proto: """"
entities:
- uid: 123
components:
- type: MetaData
- type: Transform
- type: Map
- type: EntitySaveTest
id: map
- uid: 2
components:
- type: MetaData
- parent: 123
type: Transform
- chunks:
-1,-1:
ind: -1,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAARAAAAA==
-1,0:
ind: -1,0
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
0,0:
ind: 0,0
tiles: CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
0,-1:
ind: 0,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
type: MapGrid
- type: Broadphase
- angularDamping: 0.05
linearDamping: 0.05
fixedRotation: False
bodyType: Dynamic
type: Physics
- type: OccluderTree
- id: grid
type: EntitySaveTest
- proto: V4TestProto
entities:
- uid: 1
components:
- pos: 0.5,0.5
parent: 2
type: Transform
- id: ent
type: EntitySaveTest
";
private const string PrototypeV4 = @"
- type: entity
id: V4TestProto
components:
- type: EntitySaveTest
list: [ 1, 2 ]
- type: testTileDef
id: Space
- type: testTileDef
id: A
- type: testTileDef
id: B
";
}

View File

@@ -1,266 +0,0 @@
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared.ContentPack;
using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Components;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Utility;
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
namespace Robust.UnitTesting.Shared.EntitySerialization;
[TestFixture]
public sealed partial class BackwardsCompatibilityTest
{
/// <summary>
/// Check that v5 maps can be loaded. This simply tries to load a file and doesn't do a lot of extra validation.
/// </summary>
/// <remarks>
/// The file was pilfered from content integration tests ("floor3x3.yml") and modified slightly.
/// </remarks>
[Test]
public async Task TestLoadV5()
{
var server = StartServer(new ServerIntegrationOptions {ExtraPrototypes = PrototypeV5});
await server.WaitIdleAsync();
var entMan = server.EntMan;
var mapSys = server.System<SharedMapSystem>();
var loader = server.System<MapLoaderSystem>();
var meta = server.System<MetaDataSystem>();
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
var resourceManager = server.ResolveDependency<IResourceManagerInternal>();
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan, "Space");
var gridPath = new ResPath($"{nameof(MapDataV5Grid)}.yml");
resourceManager.MountString(gridPath.ToString(), MapDataV5Grid);
MapId mapId = default;
EntityUid mapUid = default;
Entity<TransformComponent, EntitySaveTestComponent> map;
Entity<TransformComponent, EntitySaveTestComponent> ent;
Entity<TransformComponent, EntitySaveTestComponent> grid;
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
await server.WaitPost(() => mapUid = mapSys.CreateMap(out mapId));
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(mapUid));
Assert.That(meta.EntityPaused(ent), Is.False);
Assert.That(meta.EntityPaused(grid), Is.False);
Assert.That(meta.EntityPaused(mapUid), Is.False);
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(mapUid).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
await server.WaitPost(() => entMan.DeleteEntity(mapUid));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
var mapPath = new ResPath($"{nameof(MapDataV5Map)}.yml");
resourceManager.MountString(mapPath.ToString(), MapDataV5Map);
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out _, out _)));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
map = Find(nameof(map), entMan);
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(meta.EntityPaused(ent), Is.True);
Assert.That(meta.EntityPaused(grid), Is.True);
Assert.That(meta.EntityPaused(map), Is.True);
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
Is.EqualTo(EntityLifeStage.Initialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
Is.EqualTo(EntityLifeStage.Initialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(map).EntityLifeStage,
Is.EqualTo(EntityLifeStage.Initialized));
await server.WaitPost(() => entMan.DeleteEntity(map));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Repeat test, but with the initialize maps option enabled.
// Apparently mounted strings can only be read a single time.
// So have to re-mount them.
var mapPath2 = new ResPath($"{nameof(MapDataV5Map)}2.yml");
resourceManager.MountString(mapPath2.ToString(), MapDataV5Map);
var opts = DeserializationOptions.Default with {InitializeMaps = true};
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath2, out _, out _, opts)));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
map = Find(nameof(map), entMan);
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(meta.EntityPaused(ent), Is.False);
Assert.That(meta.EntityPaused(grid), Is.False);
Assert.That(meta.EntityPaused(map), Is.False);
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(map).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
await server.WaitPost(() => entMan.DeleteEntity(map));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
}
private const string MapDataV5Grid = @"
meta:
format: 5
postmapinit: false
tilemap:
0: Space
11: A
69: B
entities:
- proto: """"
entities:
- uid: 2
components:
- type: MetaData
- parent: invalid
type: Transform
- chunks:
-1,-1:
ind: -1,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAARQAAAA==
-1,0:
ind: -1,0
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
0,0:
ind: 0,0
tiles: CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
0,-1:
ind: 0,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
type: MapGrid
- type: Broadphase
- angularDamping: 0.05
linearDamping: 0.05
fixedRotation: False
bodyType: Dynamic
type: Physics
- fixtures: {}
type: Fixtures
- type: OccluderTree
- id: grid
type: EntitySaveTest
- proto: V5TestProto
entities:
- uid: 1
components:
- pos: 0.5,0.5
parent: 2
type: Transform
- id: ent
type: EntitySaveTest
";
private const string MapDataV5Map = @"
meta:
format: 5
postmapinit: false
tilemap:
0: Space
11: A
69: B
entities:
- proto: """"
entities:
- uid: 123
components:
- type: MetaData
- type: Transform
- type: Map
- type: EntitySaveTest
id: map
- uid: 2
components:
- type: MetaData
- parent: 123
type: Transform
- chunks:
-1,-1:
ind: -1,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAARQAAAA==
-1,0:
ind: -1,0
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
0,0:
ind: 0,0
tiles: CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
0,-1:
ind: 0,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
type: MapGrid
- type: Broadphase
- angularDamping: 0.05
linearDamping: 0.05
fixedRotation: False
bodyType: Dynamic
type: Physics
- fixtures: {}
type: Fixtures
- type: OccluderTree
- id: grid
type: EntitySaveTest
- proto: V5TestProto
entities:
- uid: 1
components:
- pos: 0.5,0.5
parent: 2
type: Transform
- id: ent
type: EntitySaveTest
";
private const string PrototypeV5 = @"
- type: entity
id: V5TestProto
components:
- type: EntitySaveTest
list: [ 1, 2 ]
- type: testTileDef
id: Space
- type: testTileDef
id: A
- type: testTileDef
id: B
";
}

View File

@@ -1,273 +0,0 @@
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared.ContentPack;
using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Components;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Utility;
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
namespace Robust.UnitTesting.Shared.EntitySerialization;
[TestFixture]
public sealed partial class BackwardsCompatibilityTest
{
/// <summary>
/// Check that v6 maps can be loaded. This simply tries to load a file and doesn't do a lot of extra validation.
/// </summary>
/// <remarks>
/// The file was pilfered from content integration tests ("floor3x3.yml") and modified slightly.
/// </remarks>
[Test]
public async Task TestLoadV6()
{
var server = StartServer(new ServerIntegrationOptions {ExtraPrototypes = PrototypeV6});
await server.WaitIdleAsync();
var entMan = server.EntMan;
var mapSys = server.System<SharedMapSystem>();
var loader = server.System<MapLoaderSystem>();
var meta = server.System<MetaDataSystem>();
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
var resourceManager = server.ResolveDependency<IResourceManagerInternal>();
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan, "Space");
var gridPath = new ResPath($"{nameof(MapDataV6Grid)}.yml");
resourceManager.MountString(gridPath.ToString(), MapDataV6Grid);
MapId mapId = default;
EntityUid mapUid = default;
Entity<TransformComponent, EntitySaveTestComponent> map;
Entity<TransformComponent, EntitySaveTestComponent> ent;
Entity<TransformComponent, EntitySaveTestComponent> grid;
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
await server.WaitPost(() => mapUid = mapSys.CreateMap(out mapId));
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(mapUid));
Assert.That(meta.EntityPaused(ent), Is.False);
Assert.That(meta.EntityPaused(grid), Is.False);
Assert.That(meta.EntityPaused(mapUid), Is.False);
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(mapUid).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
await server.WaitPost(() => entMan.DeleteEntity(mapUid));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
var mapPath = new ResPath($"{nameof(MapDataV6Map)}.yml");
resourceManager.MountString(mapPath.ToString(), MapDataV6Map);
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath, out _, out _)));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
map = Find(nameof(map), entMan);
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(meta.EntityPaused(ent), Is.True);
Assert.That(meta.EntityPaused(grid), Is.True);
Assert.That(meta.EntityPaused(map), Is.True);
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
Is.EqualTo(EntityLifeStage.Initialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
Is.EqualTo(EntityLifeStage.Initialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(map).EntityLifeStage,
Is.EqualTo(EntityLifeStage.Initialized));
await server.WaitPost(() => entMan.DeleteEntity(map));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Repeat test, but with the initialize maps option enabled.
// Apparently mounted strings can only be read a single time.
// So have to re-mount them.
var mapPath2 = new ResPath($"{nameof(MapDataV6Map)}2.yml");
resourceManager.MountString(mapPath2.ToString(), MapDataV6Map);
var opts = DeserializationOptions.Default with {InitializeMaps = true};
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(mapPath2, out _, out _, opts)));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
map = Find(nameof(map), entMan);
Assert.That(ent.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(grid.Comp1.ParentUid, Is.EqualTo(map.Owner));
Assert.That(map.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(meta.EntityPaused(ent), Is.False);
Assert.That(meta.EntityPaused(grid), Is.False);
Assert.That(meta.EntityPaused(map), Is.False);
Assert.That(entMan.GetComponent<MetaDataComponent>(ent).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(grid).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
Assert.That(entMan.GetComponent<MetaDataComponent>(map).EntityLifeStage,
Is.EqualTo(EntityLifeStage.MapInitialized));
await server.WaitPost(() => entMan.DeleteEntity(map));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
}
private const string MapDataV6Grid = @"
meta:
format: 6
postmapinit: false
tilemap:
0: Space
11: A
89: B
entities:
- proto: """"
entities:
- uid: 2
components:
- type: MetaData
- type: Transform
parent: invalid
- type: MapGrid
chunks:
-1,-1:
ind: -1,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAWQAAAAAA
version: 6
-1,0:
ind: -1,0
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
version: 6
0,0:
ind: 0,0
tiles: CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
version: 6
0,-1:
ind: 0,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
version: 6
- type: Broadphase
- type: Physics
bodyStatus: InAir
angularDamping: 0.05
linearDamping: 0.05
fixedRotation: False
bodyType: Dynamic
- type: OccluderTree
- type: EntitySaveTest
id: grid
- proto: V6TestProto
entities:
- uid: 1
components:
- type: Transform
pos: 0.5,0.5
parent: 2
- type: EntitySaveTest
id: ent
";
private const string MapDataV6Map = @"
meta:
format: 6
postmapinit: false
tilemap:
0: Space
11: A
89: B
entities:
- proto: """"
entities:
- uid: 123
components:
- type: MetaData
- type: Transform
- type: Map
mapPaused: True
- type: EntitySaveTest
id: map
- uid: 2
components:
- type: MetaData
- type: Transform
parent: 123
- type: MapGrid
chunks:
-1,-1:
ind: -1,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAWQAAAAAA
version: 6
-1,0:
ind: -1,0
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
version: 6
0,0:
ind: 0,0
tiles: CwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
version: 6
0,-1:
ind: 0,-1
tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
version: 6
- type: Broadphase
- type: Physics
bodyStatus: InAir
angularDamping: 0.05
linearDamping: 0.05
fixedRotation: False
bodyType: Dynamic
- type: OccluderTree
- type: EntitySaveTest
id: grid
- proto: V6TestProto
entities:
- uid: 1
components:
- type: Transform
pos: 0.5,0.5
parent: 2
- type: EntitySaveTest
id: ent
";
private const string PrototypeV6 = @"
- type: entity
id: V6TestProto
components:
- type: EntitySaveTest
list: [ 1, 2 ]
- type: testTileDef
id: Space
- type: testTileDef
id: A
- type: testTileDef
id: B
";
}

View File

@@ -1,352 +0,0 @@
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared.ContentPack;
using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Components;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Utility;
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
namespace Robust.UnitTesting.Shared.EntitySerialization;
/// <summary>
/// Test that older file formats can still be loaded.
/// </summary>
[TestFixture]
public sealed partial class BackwardsCompatibilityTest : RobustIntegrationTest
{
/// <summary>
/// Check that v7 maps can be loaded. This just re-uses some map files that are generated by other tests, and then
/// checks that the post-load debug asserts still pass. specifically, it uses the second to last file from
/// <see cref="AutoIncludeSerializationTest"/> and the initial file from
/// <see cref="LifestageSerializationTest.TestMixedLifestageSerialization"/>.
/// </summary>
/// <remarks>
/// At the time of writing, v7 is the current version, but this is here for when the version increases in the future.
/// </remarks>
[Test]
public async Task TestLoadV7()
{
var server = StartServer(new ServerIntegrationOptions { ExtraPrototypes = PrototypeV7 });
await server.WaitIdleAsync();
var entMan = server.EntMan;
var mapSys = server.System<SharedMapSystem>();
var loader = server.System<MapLoaderSystem>();
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
var resourceManager = server.ResolveDependency<IResourceManagerInternal>();
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan, "space");
void AssertCount(int expected) => Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(expected));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
var mapLoadOpts = MapLoadOptions.Default with
{
DeserializationOptions = DeserializationOptions.Default with {LogOrphanedGrids = false}
};
// Test the file from AutoIncludeSerializationTest
{
var path = new ResPath($"{nameof(MapDataV7)}.yml");
resourceManager.MountString(path.ToString(), MapDataV7);
Entity<TransformComponent, EntitySaveTestComponent> grid;
Entity<TransformComponent, EntitySaveTestComponent> onGrid;
Entity<TransformComponent, EntitySaveTestComponent> otherMap;
Entity<TransformComponent, EntitySaveTestComponent> otherEnt;
Entity<TransformComponent, EntitySaveTestComponent> nullSpace;
LoadResult? result = default;
await server.WaitAssertion(() => Assert.That(loader.TryLoadGeneric(path, out result, mapLoadOpts)));
Assert.That(result!.Version, Is.EqualTo(7));
Assert.That(result.Grids, Has.Count.EqualTo(1));
Assert.That(result.Orphans, Is.Empty); // Grid was orphaned, but was adopted after a new map was created
Assert.That(result.Maps, Has.Count.EqualTo(2));
Assert.That(result.NullspaceEntities, Has.Count.EqualTo(1));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(1)); // auto-generated map isn't marked as "loaded"
AssertCount(5);
grid = Find(nameof(grid), entMan);
onGrid = Find(nameof(onGrid), entMan);
otherMap = Find(nameof(otherMap), entMan);
otherEnt = Find(nameof(otherEnt), entMan);
nullSpace = Find(nameof(nullSpace), entMan);
Assert.That(onGrid.Comp1.ParentUid, Is.EqualTo(grid.Owner));
Assert.That(otherEnt.Comp1.ParentUid, Is.EqualTo(otherMap.Owner));
Assert.That(otherMap.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(nullSpace.Comp1.ParentUid, Is.EqualTo(EntityUid.Invalid));
await server.WaitPost(() => entMan.DeleteEntity(otherMap));
await server.WaitPost(() => entMan.DeleteEntity(grid.Comp1.ParentUid));
await server.WaitPost(() => entMan.DeleteEntity(nullSpace));
AssertCount(0);
await server.WaitPost(() => loader.Delete(result));
}
// Test the file from LifestageSerializationTest.TestMixedLifestageSerialization
{
var pathLifestage = new ResPath($"{nameof(MapDataV7Lifestage)}.yml");
resourceManager.MountString(pathLifestage.ToString(), MapDataV7Lifestage);
Entity<TransformComponent, EntitySaveTestComponent> mapA; // preinit Map
Entity<TransformComponent, EntitySaveTestComponent> mapB; // postinit unpaused Map
Entity<TransformComponent, EntitySaveTestComponent> entA; // postinit entity on preinit map
Entity<TransformComponent, EntitySaveTestComponent> entB; // paused entity on postinit unpaused map
Entity<TransformComponent, EntitySaveTestComponent> entC; // preinit entity on postinit map
Entity<TransformComponent, EntitySaveTestComponent> nullA; // postinit nullspace entity
Entity<TransformComponent, EntitySaveTestComponent> nullB; // preinit nullspace entity
Entity<TransformComponent, EntitySaveTestComponent> nullC; // paused postinit nullspace entity
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
LoadResult? result = default;
await server.WaitAssertion(() => Assert.That(loader.TryLoadGeneric(pathLifestage, out result)));
Assert.That(result!.Version, Is.EqualTo(7));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(8));
mapA = Find(nameof(mapA), entMan);
mapB = Find(nameof(mapB), entMan);
entA = Find(nameof(entA), entMan);
entB = Find(nameof(entB), entMan);
entC = Find(nameof(entC), entMan);
nullA = Find(nameof(nullA), entMan);
nullB = Find(nameof(nullB), entMan);
nullC = Find(nameof(nullC), entMan);
AssertPaused(true, mapA, entB, nullC);
AssertPaused(false, mapB, entA, entC, nullA, nullB);
AssertPreInit(true, mapA, entC, nullB);
AssertPreInit(false, mapB, entA, entB, nullA, nullC);
void AssertPaused(bool expected, params EntityUid[] uids)
{
foreach (var uid in uids)
{
Assert.That(entMan.GetComponent<MetaDataComponent>(uid).EntityPaused, Is.EqualTo(expected));
}
}
void AssertPreInit(bool expected, params EntityUid[] uids)
{
foreach (var uid in uids)
{
Assert.That(entMan!.GetComponent<MetaDataComponent>(uid).EntityLifeStage,
expected
? Is.LessThan(EntityLifeStage.MapInitialized)
: Is.EqualTo(EntityLifeStage.MapInitialized));
}
}
await server.WaitPost(() => loader.Delete(result));
}
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
Assert.That(entMan.Count<MapComponent>(), Is.EqualTo(0));
}
private const string MapDataV7 = @"
meta:
format: 7
category: Unknown
engineVersion: 238.0.0
forkId: """"
forkVersion: """"
time: 12/25/2024 00:40:09
entityCount: 5
maps:
- 3
grids:
- 1
orphans:
- 1
nullspace:
- 5
tilemap:
1: space
0: a
entities:
- proto: """"
entities:
- uid: 1
paused: false
components:
- type: MetaData
name: grid
- type: Transform
parent: invalid
- type: MapGrid
chunks:
0,0:
ind: 0,0
tiles: AAAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAAAQAAAAAA
version: 6
- type: Broadphase
- type: Physics
- type: Fixtures
fixtures: {}
- type: OccluderTree
- type: EntitySaveTest
list: []
id: grid
- uid: 2
mapInit: true
components:
- type: MetaData
- type: Transform
pos: 0.5,0.5
parent: 1
- type: EntitySaveTest
list: []
entity: 3
id: onGrid
- uid: 3
mapInit: true
components:
- type: MetaData
name: Map Entity
- type: Transform
- type: Map
mapInitialized: True
- type: GridTree
- type: Broadphase
- type: OccluderTree
- type: EntitySaveTest
list: []
id: otherMap
- uid: 4
mapInit: true
components:
- type: MetaData
- type: Transform
parent: 3
- type: EntitySaveTest
list: []
entity: 5
id: otherEnt
- uid: 5
mapInit: true
components:
- type: MetaData
- type: Transform
- type: EntitySaveTest
list: []
id: nullSpace
";
private const string MapDataV7Lifestage = @"
meta:
format: 7
category: Unknown
engineVersion: 238.0.0
forkId: """"
forkVersion: """"
time: 12/25/2024 00:50:59
entityCount: 8
maps:
- 1
- 3
grids: []
orphans: []
nullspace:
- 6
- 7
- 8
tilemap: {}
entities:
- proto: """"
entities:
- uid: 1
components:
- type: MetaData
name: Map Entity
- type: Transform
- type: Map
mapPaused: True
- type: GridTree
- type: Broadphase
- type: OccluderTree
- type: EntitySaveTest
list: []
id: mapA
- uid: 2
mapInit: true
components:
- type: MetaData
- type: Transform
parent: 1
- type: EntitySaveTest
list: []
id: entA
- uid: 3
mapInit: true
components:
- type: MetaData
name: Map Entity
- type: Transform
- type: Map
mapInitialized: True
- type: GridTree
- type: Broadphase
- type: OccluderTree
- type: EntitySaveTest
list: []
id: mapB
- uid: 4
mapInit: true
paused: true
components:
- type: MetaData
- type: Transform
parent: 3
- type: EntitySaveTest
list: []
id: entB
- uid: 5
paused: false
components:
- type: MetaData
- type: Transform
parent: 3
- type: EntitySaveTest
list: []
id: entC
- uid: 6
mapInit: true
components:
- type: MetaData
- type: Transform
- type: EntitySaveTest
list: []
id: nullA
- uid: 7
paused: false
components:
- type: MetaData
- type: Transform
- type: EntitySaveTest
list: []
id: nullB
- uid: 8
mapInit: true
paused: true
components:
- type: MetaData
- type: Transform
- type: EntitySaveTest
list: []
id: nullC
";
private const string PrototypeV7 = @"
- type: testTileDef
id: space
- type: testTileDef
id: a
";
}

View File

@@ -1,137 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
namespace Robust.UnitTesting.Shared.EntitySerialization;
[TestFixture]
public sealed partial class CategorizationTest : RobustIntegrationTest
{
private const string TestTileDefId = "a";
private const string TestPrototypes = $@"
- type: testTileDef
id: space
- type: testTileDef
id: {TestTileDefId}
";
/// <summary>
/// Check that file categories are correctly assigned when saving & loading different combinations of entites.
/// </summary>
[Test]
[TestOf(typeof(FileCategory))]
public async Task TestCategorization()
{
var server = StartServer(new() { Pool = false, ExtraPrototypes = TestPrototypes }); // Pool=false due to TileDef registration
await server.WaitIdleAsync();
var entMan = server.EntMan;
var meta = server.System<MetaDataSystem>();
var mapSys = server.System<SharedMapSystem>();
var loader = server.System<MapLoaderSystem>();
var mapMan = server.ResolveDependency<IMapManager>();
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
var path = new ResPath($"{nameof(TestCategorization)}.yml");
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan, "space");
var tDef = server.ProtoMan.Index<TileDef>(TestTileDefId);
EntityUid mapA = default;
EntityUid mapB = default;
EntityUid gridA = default; // grid on map A
EntityUid gridB = default; // grid on map B
EntityUid entA = default; // ent on grid A
EntityUid entB = default; // ent on grid B
EntityUid entC = default; // a separate entity on grid B
EntityUid child = default; // child of entB
EntityUid @null = default; // nullspace entity
await server.WaitPost(() =>
{
mapA = mapSys.CreateMap(out var mapIdA);
mapB = mapSys.CreateMap(out var mapIdB);
var gridEntA = mapMan.CreateGridEntity(mapIdA);
var gridEntB = mapMan.CreateGridEntity(mapIdB);
mapSys.SetTile(gridEntA, Vector2i.Zero, new Tile(tDef.TileId));
mapSys.SetTile(gridEntB, Vector2i.Zero, new Tile(tDef.TileId));
gridA = gridEntA.Owner;
gridB = gridEntB.Owner;
entA = entMan.SpawnEntity(null, new EntityCoordinates(gridA, 0.5f, 0.5f));
entB = entMan.SpawnEntity(null, new EntityCoordinates(gridB, 0.5f, 0.5f));
entC = entMan.SpawnEntity(null, new EntityCoordinates(gridB, 0.5f, 0.5f));
child = entMan.SpawnEntity(null, new EntityCoordinates(entB, 0.5f, 0.5f));
@null = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
});
FileCategory Save(params EntityUid[] ents)
{
FileCategory cat = FileCategory.Unknown;
Assert.That(loader.TrySaveGeneric(ents.ToHashSet(), path, out cat));
return cat;
}
async Task<LoadResult> Load(FileCategory expected, int count)
{
var opts = MapLoadOptions.Default with
{
ExpectedCategory = expected,
DeserializationOptions = DeserializationOptions.Default with { LogOrphanedGrids = false}
};
LoadResult? result = null;
await server.WaitAssertion(() => Assert.That(loader.TryLoadGeneric(path, out result, opts)));
Assert.That(result!.Category, Is.EqualTo(expected));
Assert.That(result.Entities, Has.Count.EqualTo(count));
return result;
}
async Task SaveAndLoad(FileCategory expected, int count, params EntityUid[] ents)
{
var cat = Save(ents);
Assert.That(cat, Is.EqualTo(expected));
var result = await Load(expected, count);
await server.WaitPost(() => loader.Delete(result));
}
// Saving a single entity works as expected, even if it also serializes their children
await SaveAndLoad(FileCategory.Entity, 1, entA);
await SaveAndLoad(FileCategory.Entity, 2, entB);
await SaveAndLoad(FileCategory.Entity, 1, child);
// Including nullspace entities doesn't change the category, though a file containing only null-space entities
// is "unkown". Maybe in future they will get their own category
await SaveAndLoad(FileCategory.Entity, 2, entA, @null);
await SaveAndLoad(FileCategory.Entity, 3, entB, @null);
await SaveAndLoad(FileCategory.Entity, 2, child, @null);
await SaveAndLoad(FileCategory.Unknown, 1, @null);
// More than one entity is unknown
await SaveAndLoad(FileCategory.Unknown, 3, entA, entB);
await SaveAndLoad(FileCategory.Unknown, 4, entA, entB, @null);
// Saving grids works as expected. All counts are 1 higher than expected due to a map being automatically created.
await SaveAndLoad(FileCategory.Grid, 3, gridA);
await SaveAndLoad(FileCategory.Grid, 5, gridB);
await SaveAndLoad(FileCategory.Grid, 4, gridA, @null);
await SaveAndLoad(FileCategory.Grid, 6, gridB, @null);
// And saving maps also works
await SaveAndLoad(FileCategory.Map, 3, mapA);
await SaveAndLoad(FileCategory.Map, 5, mapB);
await SaveAndLoad(FileCategory.Map, 4, mapA, @null);
await SaveAndLoad(FileCategory.Map, 6, mapB, @null);
// Combinations of grids, entities, and maps, are unknown
await SaveAndLoad(FileCategory.Unknown, 4, mapA, child);
await SaveAndLoad(FileCategory.Unknown, 4, gridA, child);
await SaveAndLoad(FileCategory.Unknown, 8, gridA, mapB);
await SaveAndLoad(FileCategory.Unknown, 5, mapA, child, @null);
await SaveAndLoad(FileCategory.Unknown, 5, gridA, child, @null);
await SaveAndLoad(FileCategory.Unknown, 9, gridA, mapB, @null);
}
}

View File

@@ -1,374 +0,0 @@
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Utility;
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
namespace Robust.UnitTesting.Shared.EntitySerialization;
[TestFixture]
public sealed partial class LifestageSerializationTest : RobustIntegrationTest
{
/// <summary>
/// Check that whether or not an entity has been paused or map-initialized is preserved across saves & loads.
/// </summary>
[Test]
public async Task TestLifestageSerialization()
{
var server = StartServer();
await server.WaitIdleAsync();
var entMan = server.EntMan;
var mapSys = server.System<SharedMapSystem>();
var loader = server.System<MapLoaderSystem>();
var preInitPath = new ResPath($"{nameof(TestLifestageSerialization)}_preInit.yml");
var postInitPath = new ResPath($"{nameof(TestLifestageSerialization)}_postInit.yml");
var pausedPostInitPath = new ResPath($"{nameof(TestLifestageSerialization)}_paused.yml");
// Create a pre-init map, and spawn multiple entities on it
Entity<TransformComponent, EntitySaveTestComponent> map = default;
Entity<TransformComponent, EntitySaveTestComponent> entA = default;
Entity<TransformComponent, EntitySaveTestComponent> entB = default;
Entity<TransformComponent, EntitySaveTestComponent> childA = default;
Entity<TransformComponent, EntitySaveTestComponent> childB = default;
await server.WaitPost(() =>
{
var mapUid = mapSys.CreateMap(out var mapId, runMapInit: false);
var entAUid = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapId));
var entBUid = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapId));
var childAUid = entMan.SpawnEntity(null, new EntityCoordinates(entAUid, 0, 0));
var childBUid = entMan.SpawnEntity(null, new EntityCoordinates(entBUid, 0, 0));
map = Get(mapUid, entMan);
entA = Get(entAUid, entMan);
entB = Get(entBUid, entMan);
childA = Get(childAUid, entMan);
childB = Get(childBUid, entMan);
map.Comp2.Id = nameof(map);
entA.Comp2.Id = nameof(entA);
entB.Comp2.Id = nameof(entB);
childA.Comp2.Id = nameof(childA);
childB.Comp2.Id = nameof(childB);
});
void AssertPaused(bool expected, params EntityUid[] uids)
{
foreach (var uid in uids)
{
Assert.That(entMan.GetComponent<MetaDataComponent>(uid).EntityPaused, Is.EqualTo(expected));
}
}
void AssertPreInit(bool expected, params EntityUid[] uids)
{
foreach (var uid in uids)
{
Assert.That(entMan!.GetComponent<MetaDataComponent>(uid).EntityLifeStage,
expected
? Is.LessThan(EntityLifeStage.MapInitialized)
: Is.EqualTo(EntityLifeStage.MapInitialized));
}
}
// All entities should initially be un-initialized and paused.
AssertPaused(true, map, entA, entB, childA, childB);
AssertPreInit(true, map, entA, entB, childA, childB);
Assert.That(loader.TrySaveMap(map, preInitPath));
async Task Delete()
{
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(5));
await server.WaitPost(() => entMan.DeleteEntity(map));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
}
async Task Load(ResPath f, DeserializationOptions? o)
{
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
await server.WaitAssertion(() => Assert.That(loader.TryLoadMap(f, out _, out _, o)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(5));
}
void FindAll()
{
map = Find(nameof(map), entMan);
entA = Find(nameof(entA), entMan);
entB = Find(nameof(entB), entMan);
childA = Find(nameof(childA), entMan);
childB = Find(nameof(childB), entMan);
}
async Task Reload(ResPath f, DeserializationOptions? o = null)
{
await Delete();
await Load(f, o);
FindAll();
}
// Saving and loading the pre-init map should have no effect.
await Reload(preInitPath);
AssertPaused(true, map, entA, entB, childA, childB);
AssertPreInit(true, map, entA, entB, childA, childB);
// Saving and loading with the map-init option set to true should initialize & unpause all entities
var opts = DeserializationOptions.Default with {InitializeMaps = true};
await Reload(preInitPath, opts);
AssertPaused(false, map, entA, entB, childA, childB);
AssertPreInit(false, map, entA, entB, childA, childB);
Assert.That(loader.TrySaveMap(map, postInitPath));
// re-loading the post-init map should keep everything initialized, even without explicitly asking to initialize maps.
await Reload(postInitPath);
AssertPaused(false, map, entA, entB, childA, childB);
AssertPreInit(false, map, entA, entB, childA, childB);
// Load & initialize a pre-init map, but with the pause maps option enabled.
opts = DeserializationOptions.Default with {InitializeMaps = true, PauseMaps = true};
await Reload(preInitPath, opts);
AssertPaused(true, map, entA, entB, childA, childB);
AssertPreInit(false, map, entA, entB, childA, childB);
Assert.That(loader.TrySaveMap(map, pausedPostInitPath));
// The pause map option also works when loading un-paused post-init maps
opts = DeserializationOptions.Default with {PauseMaps = true};
await Reload(postInitPath, opts);
AssertPaused(true, map, entA, entB, childA, childB);
AssertPreInit(false, map, entA, entB, childA, childB);
// loading & initializing a post-init map should cause no issues.
opts = DeserializationOptions.Default with {InitializeMaps = true};
await Reload(postInitPath, opts);
AssertPaused(false, map, entA, entB, childA, childB);
AssertPreInit(false, map, entA, entB, childA, childB);
// Loading a paused post init map does NOT automatically un-pause entities
await Reload(pausedPostInitPath);
AssertPaused(true, map, entA, entB, childA, childB);
AssertPreInit(false, map, entA, entB, childA, childB);
// The above holds even if we are explicitly initialising maps.
opts = DeserializationOptions.Default with {InitializeMaps = true};
await Reload(pausedPostInitPath, opts);
AssertPaused(true, map, entA, entB, childA, childB);
AssertPreInit(false, map, entA, entB, childA, childB);
// And re-paused an already paused map should have no impact.
opts = DeserializationOptions.Default with {InitializeMaps = true, PauseMaps = true};
await Reload(pausedPostInitPath, opts);
AssertPaused(true, map, entA, entB, childA, childB);
AssertPreInit(false, map, entA, entB, childA, childB);
}
/// <summary>
/// Variant of <see cref="TestLifestageSerialization"/> that has multiple maps and combinations. E.g., a single
/// paused entity on an un-paused map.
/// </summary>
[Test]
public async Task TestMixedLifestageSerialization()
{
var server = StartServer();
await server.WaitIdleAsync();
var entMan = server.EntMan;
var meta = server.System<MetaDataSystem>();
var mapSys = server.System<SharedMapSystem>();
var loader = server.System<MapLoaderSystem>();
var path = new ResPath($"{nameof(TestMixedLifestageSerialization)}.yml");
var altPath = new ResPath($"{nameof(TestMixedLifestageSerialization)}_alt.yml");
Entity<TransformComponent, EntitySaveTestComponent> mapA = default; // preinit Map
Entity<TransformComponent, EntitySaveTestComponent> mapB = default; // postinit unpaused Map
Entity<TransformComponent, EntitySaveTestComponent> entA = default; // postinit entity on preinit map
Entity<TransformComponent, EntitySaveTestComponent> entB = default; // paused entity on postinit unpaused map
Entity<TransformComponent, EntitySaveTestComponent> entC = default; // preinit entity on postinit map
Entity<TransformComponent, EntitySaveTestComponent> nullA = default; // postinit nullspace entity
Entity<TransformComponent, EntitySaveTestComponent> nullB = default; // preinit nullspace entity
Entity<TransformComponent, EntitySaveTestComponent> nullC = default; // paused postinit nullspace entity
await server.WaitPost(() =>
{
var mapAUid = mapSys.CreateMap(out var mapIdA, runMapInit: false);
var mapBUid = mapSys.CreateMap(out var mapIdB, runMapInit: true);
var entAUid = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapIdA));
entMan.RunMapInit(entAUid, entMan.GetComponent<MetaDataComponent>(entAUid));
meta.SetEntityPaused(entAUid, false);
var entBUid = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapIdB));
meta.SetEntityPaused(entBUid, true);
var entCUid = entMan.CreateEntityUninitialized(null, new MapCoordinates(0, 0, mapIdB));
entMan.InitializeAndStartEntity(entCUid, doMapInit: false);
var nullAUid = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
var nullBUid = entMan.CreateEntityUninitialized(null, MapCoordinates.Nullspace);
entMan.InitializeAndStartEntity(nullBUid, doMapInit: false);
var nullCUid = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
meta.SetEntityPaused(nullCUid, true);
mapA = Get(mapAUid, entMan);
mapB = Get(mapBUid, entMan);
entA = Get(entAUid, entMan);
entB = Get(entBUid, entMan);
entC = Get(entCUid, entMan);
nullA = Get(nullAUid, entMan);
nullB = Get(nullBUid, entMan);
nullC = Get(nullCUid, entMan);
mapA.Comp2.Id = nameof(mapA);
mapB.Comp2.Id = nameof(mapB);
entA.Comp2.Id = nameof(entA);
entB.Comp2.Id = nameof(entB);
entC.Comp2.Id = nameof(entC);
nullA.Comp2.Id = nameof(nullA);
nullB.Comp2.Id = nameof(nullB);
nullC.Comp2.Id = nameof(nullC);
});
string? Name(EntityUid uid)
{
return entMan.GetComponentOrNull<EntitySaveTestComponent>(uid)?.Id;
}
void AssertPaused(bool expected, params EntityUid[] uids)
{
foreach (var uid in uids)
{
Assert.That(entMan.GetComponent<MetaDataComponent>(uid).EntityPaused, Is.EqualTo(expected), Name(uid));
}
}
void AssertPreInit(bool expected, params EntityUid[] uids)
{
foreach (var uid in uids)
{
Assert.That(entMan!.GetComponent<MetaDataComponent>(uid).EntityLifeStage,
expected
? Is.LessThan(EntityLifeStage.MapInitialized)
: Is.EqualTo(EntityLifeStage.MapInitialized));
}
}
void Save(ResPath f)
{
Assert.That(loader.TrySaveGeneric([mapA, mapB, nullA, nullB, nullC], f, out _));
}
async Task Delete()
{
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(8));
await server.WaitPost(() => entMan.DeleteEntity(mapA));
await server.WaitPost(() => entMan.DeleteEntity(mapB));
await server.WaitPost(() => entMan.DeleteEntity(nullA));
await server.WaitPost(() => entMan.DeleteEntity(nullB));
await server.WaitPost(() => entMan.DeleteEntity(nullC));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
}
async Task Load(ResPath f, DeserializationOptions? o)
{
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
var oo = MapLoadOptions.Default with
{
DeserializationOptions = o ?? DeserializationOptions.Default
};
await server.WaitAssertion(() => Assert.That(loader.TryLoadGeneric(f, out _, oo)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(8));
}
void FindAll()
{
mapA = Find(nameof(mapA), entMan);
mapB = Find(nameof(mapB), entMan);
entA = Find(nameof(entA), entMan);
entB = Find(nameof(entB), entMan);
entC = Find(nameof(entC), entMan);
nullA = Find(nameof(nullA), entMan);
nullB = Find(nameof(nullB), entMan);
nullC = Find(nameof(nullC), entMan);
}
async Task Reload(ResPath f, DeserializationOptions? o = null)
{
await Delete();
await Load(f, o);
FindAll();
}
// All entities should initially be in their respective expected states.
// entC (pre-mapinit entity on a post-mapinit map) is a bit fucky, and I don't know if that should even be allowed.
// Note that its just pre-init, not paused, as pre-mapinit entities get paused due to the maps state, not as a general result of being pre-mapinit.
// If this ever changes, these assers need fixing.
AssertPaused(true, mapA, entB, nullC);
AssertPaused(false, mapB, entA, entC, nullA, nullB);
AssertPreInit(true, mapA, entC, nullB);
AssertPreInit(false, mapB, entA, entB, nullA, nullC);
// Saving and re-loading entities should leave their metadata unchanged.
Save(path);
await Reload(path);
AssertPaused(true, mapA, entB, nullC);
AssertPaused(false, mapB, entA, entC, nullA, nullB);
AssertPreInit(true, mapA, entC, nullB);
AssertPreInit(false, mapB, entA, entB, nullA, nullC);
// reload maps with the mapinit option. This should only affect mapA, as entA is the only one on the map and it
// is already initialized,
var opts = DeserializationOptions.Default with {InitializeMaps = true};
await Reload(path, opts);
AssertPaused(true, entB, nullC);
AssertPaused(false, mapA, mapB, entA, entC, nullA, nullB);
AssertPreInit(true, entC, nullB);
AssertPreInit(false, mapA, mapB, entA, entB, nullA, nullC);
// Reloading the new configuration changes nothing
Save(altPath);
await Reload(altPath, opts);
AssertPaused(true, entB, nullC);
AssertPaused(false, mapA, mapB, entA, entC, nullA, nullB);
AssertPreInit(true, entC, nullB);
AssertPreInit(false, mapA, mapB, entA, entB, nullA, nullC);
// Pause all maps. This will not actually pause entityA, as mapA is already paused (due to being pre-init), so
// it will not iterate through its children. Maybe this will change in future, but I don't think we should even
// be trying to actively support having post-init entities on a pre-init map. This is subject to maybe change
// one day, though if it does the option should be changed to PauseEntities to clarify that it will pause ALL
// entities, not just maps.
opts = DeserializationOptions.Default with {PauseMaps = true};
await Reload(path, opts);
AssertPaused(true, mapA, mapB, entC, entB, nullC);
AssertPaused(false, entA, nullA, nullB);
AssertPreInit(true, mapA, entC, nullB);
AssertPreInit(false, mapB, entA, entB, nullA, nullC);
// Reloading the new configuration changes nothing
Save(altPath);
await Reload(altPath, opts);
AssertPaused(true, mapA, mapB, entC, entB, nullC);
AssertPaused(false, entA, nullA, nullB);
AssertPreInit(true, mapA, entC, nullB);
AssertPreInit(false, mapB, entA, entB, nullA, nullC);
// Initialise and pause all maps. Similar to the previous test with entA, this will not affect entC even
// though it is pre-init, because it is on a post-init map. Again, this is subject to maybe change one day.
// Though if it does, the option should be changed to MapInitializeEntities to clarify that it will mapinit ALL
// entities, not just maps.
opts = DeserializationOptions.Default with {InitializeMaps = true, PauseMaps = true};
await Reload(path, opts);
AssertPaused(true, mapA, mapB, entB, entC, nullC);
AssertPaused(false, entA, nullA, nullB);
AssertPreInit(true, entC, nullB);
AssertPreInit(false, mapA, mapB, entA, entB, nullA, nullC);
// Reloading the new configuration changes nothing
Save(altPath);
await Reload(altPath, opts);
AssertPaused(true, mapA, mapB, entB, entC, nullC);
AssertPaused(false, entA, nullA, nullB);
AssertPreInit(true, entC, nullB);
AssertPreInit(false, mapA, mapB, entA, entB, nullA, nullC);
}
}

View File

@@ -1,239 +0,0 @@
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
namespace Robust.UnitTesting.Shared.EntitySerialization;
/// <summary>
/// Test that loading a pre-init map/grid onto a post-init map should initialize, while loading a post-init map/grid
/// onto a paused map should pause it.
/// </summary>
[TestFixture]
public sealed partial class MapMergeTest : RobustIntegrationTest
{
private const string TestTileDefId = "a";
private const string TestPrototypes = $@"
- type: testTileDef
id: space
- type: testTileDef
id: {TestTileDefId}
";
[Test]
public async Task TestMapMerge()
{
var server = StartServer(new() { Pool = false, ExtraPrototypes = TestPrototypes }); // Pool=false due to TileDef registration
await server.WaitIdleAsync();
var entMan = server.EntMan;
var mapSys = server.System<SharedMapSystem>();
var loader = server.System<MapLoaderSystem>();
var mapMan = server.ResolveDependency<IMapManager>();
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
var mapPath = new ResPath($"{nameof(TestMapMerge)}_map.yml");
var gridPath = new ResPath($"{nameof(TestMapMerge)}_grid.yml");
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan, "space");
var tDef = server.ProtoMan.Index<TileDef>(TestTileDefId);
MapId mapId = default;
Entity<TransformComponent, EntitySaveTestComponent> map = default;
Entity<TransformComponent, EntitySaveTestComponent> ent = default;
Entity<TransformComponent, EntitySaveTestComponent> grid = default;
await server.WaitPost(() =>
{
var mapUid = mapSys.CreateMap(out mapId, runMapInit: false);
var gridEnt = mapMan.CreateGridEntity(mapId);
mapSys.SetTile(gridEnt, Vector2i.Zero, new Tile(tDef.TileId));
var entUid = entMan.SpawnEntity(null, new MapCoordinates(10, 10, mapId));
map = Get(mapUid, entMan);
ent = Get(entUid, entMan);
grid = Get(gridEnt.Owner, entMan);
});
void AssertPaused(EntityUid uid, bool expected = true)
{
Assert.That(entMan.GetComponent<MetaDataComponent>(uid).EntityPaused, Is.EqualTo(expected));
}
void AssertPreInit(EntityUid uid, bool expected = true)
{
Assert.That(entMan!.GetComponent<MetaDataComponent>(uid).EntityLifeStage,
expected
? Is.LessThan(EntityLifeStage.MapInitialized)
: Is.EqualTo(EntityLifeStage.MapInitialized));
}
map.Comp2!.Id = nameof(map);
ent.Comp2!.Id = nameof(ent);
grid.Comp2!.Id = nameof(grid);
AssertPaused(map);
AssertPreInit(map);
AssertPaused(ent);
AssertPreInit(ent);
AssertPaused(grid);
AssertPreInit(grid);
// Save then delete everything
await server.WaitAssertion(() => Assert.That(loader.TrySaveMap(map, mapPath)));
await server.WaitAssertion(() => Assert.That(loader.TrySaveGrid(grid, gridPath)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
await server.WaitPost(() => mapSys.DeleteMap(mapId));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Load a grid onto a pre-init map.
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: false));
Assert.That(mapSys.IsInitialized(mapId), Is.False);
Assert.That(mapSys.IsPaused(mapId), Is.True);
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(1));
grid = Find(nameof(grid), entMan);
AssertPaused(grid);
AssertPreInit(grid);
await server.WaitPost(() => mapSys.DeleteMap(mapId));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Merge a map onto a pre-init map.
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: false));
Assert.That(mapSys.IsInitialized(mapId), Is.False);
Assert.That(mapSys.IsPaused(mapId), Is.True);
await server.WaitAssertion(() => Assert.That(loader.TryMergeMap(mapId, mapPath, out _)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2)); // The loaded map entity gets deleted after merging
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
AssertPaused(grid);
AssertPreInit(grid);
AssertPaused(ent);
AssertPreInit(ent);
await server.WaitPost(() => mapSys.DeleteMap(mapId));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Load a grid onto a post-init map.
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: true));
Assert.That(mapSys.IsInitialized(mapId), Is.True);
Assert.That(mapSys.IsPaused(mapId), Is.False);
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(1));
grid = Find(nameof(grid), entMan);
AssertPaused(grid, false);
AssertPreInit(grid, false);
await server.WaitPost(() => mapSys.DeleteMap(mapId));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Merge a map onto a post-init map.
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: true));
Assert.That(mapSys.IsInitialized(mapId), Is.True);
Assert.That(mapSys.IsPaused(mapId), Is.False);
await server.WaitAssertion(() => Assert.That(loader.TryMergeMap(mapId, mapPath, out _)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
AssertPaused(grid, false);
AssertPreInit(grid, false);
AssertPaused(ent, false);
AssertPreInit(ent, false);
await server.WaitPost(() => mapSys.DeleteMap(mapId));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Load a grid onto a paused post-init map.
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: true));
await server.WaitPost(() => mapSys.SetPaused(mapId, true));
Assert.That(mapSys.IsInitialized(mapId), Is.True);
Assert.That(mapSys.IsPaused(mapId), Is.True);
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(1));
grid = Find(nameof(grid), entMan);
AssertPaused(grid);
AssertPreInit(grid, false);
await server.WaitPost(() => mapSys.DeleteMap(mapId));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Merge a map onto a paused post-init map.
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: true));
await server.WaitPost(() => mapSys.SetPaused(mapId, true));
Assert.That(mapSys.IsInitialized(mapId), Is.True);
Assert.That(mapSys.IsPaused(mapId), Is.True);
await server.WaitAssertion(() => Assert.That(loader.TryMergeMap(mapId, mapPath, out _)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
AssertPaused(grid);
AssertPreInit(grid, false);
AssertPaused(ent);
AssertPreInit(ent, false);
await server.WaitPost(() => mapSys.DeleteMap(mapId));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Check that the map initialization deserialziation options have no effect.
// We are loading onto an existing map, deserialization shouldn't modify it directly.
// Load a grid onto a pre-init map, with InitializeMaps = true
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: false));
Assert.That(mapSys.IsInitialized(mapId), Is.False);
Assert.That(mapSys.IsPaused(mapId), Is.True);
var opts = DeserializationOptions.Default with {InitializeMaps = true};
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _, opts)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(1));
grid = Find(nameof(grid), entMan);
AssertPaused(grid);
AssertPreInit(grid);
await server.WaitPost(() => mapSys.DeleteMap(mapId));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Merge a map onto a pre-init map, with InitializeMaps = true
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: false));
Assert.That(mapSys.IsInitialized(mapId), Is.False);
Assert.That(mapSys.IsPaused(mapId), Is.True);
opts = DeserializationOptions.Default with {InitializeMaps = true};
await server.WaitAssertion(() => Assert.That(loader.TryMergeMap(mapId, mapPath, out _, opts)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2)); // The loaded map entity gets deleted after merging
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
AssertPaused(grid);
AssertPreInit(grid);
AssertPaused(ent);
AssertPreInit(ent);
await server.WaitPost(() => mapSys.DeleteMap(mapId));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Load a grid onto a post-init map, with PauseMaps = true
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: true));
Assert.That(mapSys.IsInitialized(mapId), Is.True);
Assert.That(mapSys.IsPaused(mapId), Is.False);
opts = DeserializationOptions.Default with {PauseMaps = true};
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, gridPath, out _, opts)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(1));
grid = Find(nameof(grid), entMan);
AssertPaused(grid, false);
AssertPreInit(grid, false);
await server.WaitPost(() => mapSys.DeleteMap(mapId));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Load a grid onto a post-init map, with PauseMaps = true
await server.WaitPost(() => mapSys.CreateMap(out mapId, runMapInit: true));
Assert.That(mapSys.IsInitialized(mapId), Is.True);
Assert.That(mapSys.IsPaused(mapId), Is.False);
opts = DeserializationOptions.Default with {PauseMaps = true};
await server.WaitAssertion(() => Assert.That(loader.TryMergeMap(mapId, mapPath, out _, opts)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
ent = Find(nameof(ent), entMan);
grid = Find(nameof(grid), entMan);
AssertPaused(grid, false);
AssertPreInit(grid, false);
AssertPaused(ent, false);
AssertPreInit(ent, false);
await server.WaitPost(() => mapSys.DeleteMap(mapId));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
}
}

View File

@@ -1,242 +0,0 @@
using System.Numerics;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared.EntitySerialization;
using Robust.Shared.EntitySerialization.Components;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using static Robust.UnitTesting.Shared.EntitySerialization.EntitySaveTestComponent;
namespace Robust.UnitTesting.Shared.EntitySerialization;
[TestFixture]
public sealed partial class OrphanSerializationTest : RobustIntegrationTest
{
private const string TestTileDefId = "a";
private const string TestPrototypes = $@"
- type: testTileDef
id: space
- type: testTileDef
id: {TestTileDefId}
";
/// <summary>
/// Check that we can save & load a file containing multiple orphaned (non-grid) entities.
/// </summary>
[Test]
public async Task TestMultipleOrphanSerialization()
{
var server = StartServer();
await server.WaitIdleAsync();
var entMan = server.EntMan;
var mapSys = server.System<SharedMapSystem>();
var loader = server.System<MapLoaderSystem>();
var xform = server.System<SharedTransformSystem>();
var pathA = new ResPath($"{nameof(TestMultipleOrphanSerialization)}_A.yml");
var pathB = new ResPath($"{nameof(TestMultipleOrphanSerialization)}_B.yml");
var pathCombined = new ResPath($"{nameof(TestMultipleOrphanSerialization)}_C.yml");
// Spawn multiple entities on a map
MapId mapId = default;
Entity<TransformComponent, EntitySaveTestComponent> entA = default;
Entity<TransformComponent, EntitySaveTestComponent> entB = default;
Entity<TransformComponent, EntitySaveTestComponent> child = default;
await server.WaitPost(() =>
{
mapSys.CreateMap(out mapId);
var entAUid = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapId));
var entBUid = entMan.SpawnEntity(null, new MapCoordinates(0, 0, mapId));
var childUid = entMan.SpawnEntity(null, new EntityCoordinates(entBUid, 0, 0));
entA = Get(entAUid, entMan);
entB = Get(entBUid, entMan);
child = Get(childUid, entMan);
entA.Comp2.Id = nameof(entA);
entB.Comp2.Id = nameof(entB);
child.Comp2.Id = nameof(child);
xform.SetLocalPosition(entB.Owner, new (100,100));
});
// Entities are not in null-space
Assert.That(entA.Comp1!.ParentUid, Is.Not.EqualTo(EntityUid.Invalid));
Assert.That(entB.Comp1!.ParentUid, Is.Not.EqualTo(EntityUid.Invalid));
Assert.That(child.Comp1!.ParentUid, Is.EqualTo(entB.Owner));
// Save the entities without their map
Assert.That(loader.TrySaveEntity(entA, pathA));
Assert.That(loader.TrySaveEntity(entB, pathB));
Assert.That(loader.TrySaveGeneric([entA.Owner, entB.Owner], pathCombined, out var cat));
Assert.That(cat, Is.EqualTo(FileCategory.Unknown));
// Delete all the entities.
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
await server.WaitPost(() => mapSys.DeleteMap(mapId));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Load in the file containing only entA.
await server.WaitAssertion(() => Assert.That(loader.TryLoadEntity(pathA, out _)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(1));
entA = Find(nameof(entA), entMan);
Assert.That(entA.Comp1!.ParentUid, Is.EqualTo(EntityUid.Invalid));
await server.WaitPost(() => entMan.DeleteEntity(entA));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Load in the file containing entB and its child
await server.WaitAssertion(() => Assert.That(loader.TryLoadEntity(pathB, out _)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
entB = Find(nameof(entB), entMan);
child = Find(nameof(child), entMan);
// Even though the entities are in null-space their local position is preserved.
// This is so that you can save multiple entities on a map, without saving the map, while still preserving
// relative positions for loading them onto some other map.
Assert.That(entB.Comp1.LocalPosition, Is.Approximately(new Vector2(100, 100)));
Assert.That(entB.Comp1!.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(child.Comp1!.ParentUid, Is.EqualTo(entB.Owner));
await server.WaitPost(() => entMan.DeleteEntity(entB));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Load the file that contains both of them
LoadResult? result = null;
await server.WaitAssertion(() => Assert.That(loader.TryLoadGeneric(pathCombined, out result)));
Assert.That(result!.Category, Is.EqualTo(FileCategory.Unknown));
Assert.That(result.Orphans, Has.Count.EqualTo(2));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
entA = Find(nameof(entA), entMan);
entB = Find(nameof(entB), entMan);
child = Find(nameof(child), entMan);
Assert.That(entA.Comp1!.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(entB.Comp1!.ParentUid, Is.EqualTo(EntityUid.Invalid));
Assert.That(entB.Comp1.LocalPosition, Is.Approximately(new Vector2(100, 100)));
Assert.That(child.Comp1!.ParentUid, Is.EqualTo(entB.Owner));
await server.WaitPost(() => entMan.DeleteEntity(entA));
await server.WaitPost(() => entMan.DeleteEntity(entB));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
}
/// <summary>
/// Check that we can save & load a file containing multiple orphaned grid entities.
/// </summary>
[Test]
public async Task TestOrphanedGridSerialization()
{
var server = StartServer(new() { Pool = false, ExtraPrototypes = TestPrototypes }); // Pool=false due to TileDef registration
await server.WaitIdleAsync();
var entMan = server.EntMan;
var mapSys = server.System<SharedMapSystem>();
var loader = server.System<MapLoaderSystem>();
var xform = server.System<SharedTransformSystem>();
var mapMan = server.ResolveDependency<IMapManager>();
var tileMan = server.ResolveDependency<ITileDefinitionManager>();
var pathA = new ResPath($"{nameof(TestOrphanedGridSerialization)}_A.yml");
var pathB = new ResPath($"{nameof(TestOrphanedGridSerialization)}_B.yml");
var pathCombined = new ResPath($"{nameof(TestOrphanedGridSerialization)}_C.yml");
SerializationTestHelper.LoadTileDefs(server.ProtoMan, tileMan, "space");
var tDef = server.ProtoMan.Index<TileDef>(TestTileDefId);
// Spawn multiple entities on a map
MapId mapId = default;
Entity<TransformComponent, EntitySaveTestComponent> map = default;
Entity<TransformComponent, EntitySaveTestComponent> gridA = default;
Entity<TransformComponent, EntitySaveTestComponent> gridB = default;
Entity<TransformComponent, EntitySaveTestComponent> child = default;
await server.WaitPost(() =>
{
var mapUid = mapSys.CreateMap(out mapId);
map = Get(mapUid, entMan);
var gridAUid = mapMan.CreateGridEntity(mapId);
mapSys.SetTile(gridAUid, Vector2i.Zero, new Tile(tDef.TileId));
gridA = Get(gridAUid, entMan);
xform.SetLocalPosition(gridA.Owner, new(100, 100));
var gridBUid = mapMan.CreateGridEntity(mapId);
mapSys.SetTile(gridBUid, Vector2i.Zero, new Tile(tDef.TileId));
gridB = Get(gridBUid, entMan);
var childUid = entMan.SpawnEntity(null, new EntityCoordinates(gridBUid, 0.5f, 0.5f));
child = Get(childUid, entMan);
map.Comp2.Id = nameof(map);
gridA.Comp2.Id = nameof(gridA);
gridB.Comp2.Id = nameof(gridB);
child.Comp2.Id = nameof(child);
});
await server.WaitRunTicks(5);
// grids are not in null-space
Assert.That(gridA.Comp1!.ParentUid, Is.EqualTo(map.Owner));
Assert.That(gridB.Comp1!.ParentUid, Is.EqualTo(map.Owner));
Assert.That(child.Comp1!.ParentUid, Is.EqualTo(gridB.Owner));
Assert.That(map.Comp1!.ParentUid, Is.EqualTo(EntityUid.Invalid));
// Save the grids without their map
await server.WaitAssertion(() => Assert.That(loader.TrySaveGrid(gridA, pathA)));
await server.WaitAssertion(() => Assert.That(loader.TrySaveGrid(gridB, pathB)));
FileCategory cat = default;
await server.WaitAssertion(() => Assert.That(loader.TrySaveGeneric([gridA.Owner, gridB.Owner], pathCombined, out cat)));
Assert.That(cat, Is.EqualTo(FileCategory.Unknown));
// Delete all the entities.
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(4));
await server.WaitPost(() => mapSys.DeleteMap(mapId));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Load in the file containing only gridA.
EntityUid newMap = default;
await server.WaitPost(() => newMap = mapSys.CreateMap(out mapId));
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, pathA, out _)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(1));
gridA = Find(nameof(gridA), entMan);
Assert.That(gridA.Comp1.LocalPosition, Is.Approximately(new Vector2(100, 100)));
Assert.That(gridA.Comp1!.ParentUid, Is.EqualTo(newMap));
await server.WaitPost(() => mapSys.DeleteMap(mapId));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Load in the file containing gridB and its child
await server.WaitPost(() => newMap = mapSys.CreateMap(out mapId));
await server.WaitAssertion(() => Assert.That(loader.TryLoadGrid(mapId, pathB, out _)));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(2));
gridB = Find(nameof(gridB), entMan);
child = Find(nameof(child), entMan);
Assert.That(gridB.Comp1!.ParentUid, Is.EqualTo(newMap));
Assert.That(child.Comp1!.ParentUid, Is.EqualTo(gridB.Owner));
await server.WaitPost(() => mapSys.DeleteMap(mapId));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
// Load the file that contains both of them.
// This uses the generic loader, and should automatically create maps for both grids.
LoadResult? result = null;
var opts = MapLoadOptions.Default with
{
DeserializationOptions = DeserializationOptions.Default with {LogOrphanedGrids = false}
};
await server.WaitAssertion(() => Assert.That(loader.TryLoadGeneric(pathCombined, out result, opts)));
Assert.That(result!.Category, Is.EqualTo(FileCategory.Unknown));
Assert.That(result.Grids, Has.Count.EqualTo(2));
Assert.That(result.Maps, Has.Count.EqualTo(2));
Assert.That(entMan.Count<LoadedMapComponent>(), Is.EqualTo(0));
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(3));
gridA = Find(nameof(gridA), entMan);
gridB = Find(nameof(gridB), entMan);
child = Find(nameof(child), entMan);
Assert.That(gridA.Comp1.LocalPosition, Is.Approximately(new Vector2(100, 100)));
Assert.That(gridA.Comp1!.ParentUid, Is.Not.EqualTo(EntityUid.Invalid));
Assert.That(gridB.Comp1!.ParentUid, Is.Not.EqualTo(EntityUid.Invalid));
Assert.That(child.Comp1!.ParentUid, Is.EqualTo(gridB.Owner));
await server.WaitPost(() =>
{
foreach (var ent in result.Maps)
{
entMan.DeleteEntity(ent.Owner);
}
});
Assert.That(entMan.Count<EntitySaveTestComponent>(), Is.EqualTo(0));
}
}

View File

@@ -1,132 +0,0 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Robust.UnitTesting.Shared.EntitySerialization;
[Serializable, NetSerializable]
[DataDefinition]
public sealed partial class RobustCloneableTestClass : IRobustCloneable<RobustCloneableTestClass>
{
[DataField]
public int IntValue;
public RobustCloneableTestClass Clone()
{
return new RobustCloneableTestClass
{
IntValue = IntValue
};
}
}
[Serializable, NetSerializable]
[DataDefinition]
public partial struct RobustCloneableTestStruct : IRobustCloneable<RobustCloneableTestStruct>
{
[DataField]
public int IntValue;
public RobustCloneableTestStruct Clone()
{
return new RobustCloneableTestStruct
{
IntValue = IntValue
};
}
}
[RegisterComponent]
[NetworkedComponent, AutoGenerateComponentState]
public sealed partial class RobustCloneableTestComponent : Component
{
[DataField, AutoNetworkedField]
public RobustCloneableTestClass TestClass = new();
[DataField, AutoNetworkedField]
public RobustCloneableTestStruct TestStruct = new();
[DataField, AutoNetworkedField]
public RobustCloneableTestStruct? NullableTestStruct;
}
public sealed class RobustCloneableTest() : RobustIntegrationTest
{
[Test]
public async Task TestClone()
{
var server = StartServer();
var client = StartClient();
await Task.WhenAll(server.WaitIdleAsync(), client.WaitIdleAsync());
var sEntMan = server.EntMan;
var sPlayerMan = server.ResolveDependency<ISharedPlayerManager>();
var cEntMan = client.EntMan;
var cNetMan = client.ResolveDependency<IClientNetManager>();
MapId mapId = default;
await server.WaitPost(() =>
{
server.System<SharedMapSystem>().CreateMap(out mapId);
var coords = new MapCoordinates(0, 0, mapId);
var uid = sEntMan.SpawnEntity(null, coords);
var comp = sEntMan.EnsureComponent<RobustCloneableTestComponent>(uid);
comp.TestClass.IntValue = 50;
comp.TestStruct.IntValue = 60;
comp.NullableTestStruct = new() { IntValue = 70 };
});
// Connect client.
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
await client.WaitPost(() => cNetMan.ClientConnect(null!, 0, null!));
async Task RunTicks()
{
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
}
await RunTicks();
EntityUid player = default;
await server.WaitPost(() =>
{
var coords = new MapCoordinates(0, 0, mapId);
player = sEntMan.SpawnEntity(null, coords);
var session = sPlayerMan.Sessions.First();
server.PlayerMan.SetAttachedEntity(session, player);
sPlayerMan.JoinGame(session);
});
await RunTicks();
await server.WaitAssertion(() =>
{
Assert.That(cNetMan.IsConnected, Is.True);
var ents = cEntMan.AllEntities<RobustCloneableTestComponent>().ToList();
Assert.That(ents, Has.Count.EqualTo(1));
var testEnt = ents[0];
Assert.That(testEnt.Comp.TestClass.IntValue, Is.EqualTo(50));
Assert.That(testEnt.Comp.TestStruct.IntValue, Is.EqualTo(60));
Assert.That(testEnt.Comp.NullableTestStruct, Is.Not.Null);
Assert.That(testEnt.Comp.NullableTestStruct!.Value.IntValue, Is.EqualTo(70));
});
// Disconnect client
await client.WaitPost(() => cNetMan.ClientDisconnect(""));
await server.WaitRunTicks(5);
await client.WaitRunTicks(5);
}
}

View File

@@ -1,33 +0,0 @@
using System;
using System.Collections.Generic;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
namespace Robust.UnitTesting.Shared.EntitySerialization;
public static class SerializationTestHelper
{
public static void LoadTileDefs(IPrototypeManager protoMan, ITileDefinitionManager tileMan, string? spaceId = "Space")
{
var prototypeList = new List<TileDef>();
foreach (var tileDef in protoMan.EnumeratePrototypes<TileDef>())
{
if (tileDef.ID == spaceId)
{
// Filter out the space tile def and register it first
tileMan.Register(tileDef);
continue;
}
prototypeList.Add(tileDef);
}
prototypeList.Sort((a, b) => string.Compare(a.ID, b.ID, StringComparison.Ordinal));
// Register the rest
foreach (var tileDef in prototypeList)
{
tileMan.Register(tileDef);
}
}
}

View File

@@ -1,62 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
namespace Robust.UnitTesting.Shared.EntitySerialization;
[RegisterComponent]
public sealed partial class EntitySaveTestComponent : Component
{
/// <summary>
/// Give each entity a unique id to identify them across map saves & loads.
/// </summary>
[DataField] public string? Id;
[DataField] public EntityUid? Entity;
[DataField, AlwaysPushInheritance] public List<int> List = [];
/// <summary>
/// Find an entity with a <see cref="EntitySaveTestComponent"/> with the matching id.
/// </summary>
public static Entity<TransformComponent, EntitySaveTestComponent> Find(string id, IEntityManager entMan)
{
var ents = entMan.AllEntities<EntitySaveTestComponent>();
var matching = ents.Where(x => x.Comp.Id == id).ToArray();
Assert.That(matching, Has.Length.EqualTo(1));
return (matching[0].Owner, entMan.GetComponent<TransformComponent>(matching[0].Owner), matching[0].Comp);
}
public static Entity<TransformComponent, EntitySaveTestComponent> Get(EntityUid uid, IEntityManager entMan)
{
return new Entity<TransformComponent, EntitySaveTestComponent>(
uid,
entMan.GetComponent<TransformComponent>(uid),
entMan.EnsureComponent<EntitySaveTestComponent>(uid));
}
}
/// <summary>
/// Dummy tile definition for serializing grids.
/// </summary>
[Prototype("testTileDef")]
public sealed partial class TileDef : ITileDefinition
{
public ushort TileId { get; set; }
public string Name => ID;
[IdDataField]
public string ID { get; private set; } = default!;
public ResPath? Sprite => null;
public Dictionary<Direction, ResPath> EdgeSprites => new();
public int EdgeSpritePriority => 0;
public float Friction => 0;
public byte Variants => 0;
public void AssignTileId(ushort id) => TileId = id;
}

View File

@@ -1,110 +0,0 @@
using System;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Robust.UnitTesting.Shared.GameObjects
{
[TestFixture]
[TestOf(typeof(ComponentFactory))]
public sealed partial class ComponentFactory_Tests : RobustUnitTest
{
private const string TestComponentName = "A";
private const string LowercaseTestComponentName = "a";
private const string NonexistentComponentName = "B";
protected override Type[]? ExtraComponents => new[] {typeof(TestComponent)};
[Test]
public void GetComponentAvailabilityTest()
{
var componentFactory = IoCManager.Resolve<IComponentFactory>();
// Should not exist
Assert.That(componentFactory.GetComponentAvailability(NonexistentComponentName), Is.EqualTo(ComponentAvailability.Unknown));
Assert.That(componentFactory.GetComponentAvailability(NonexistentComponentName, true), Is.EqualTo(ComponentAvailability.Unknown));
// Normal casing, do not ignore case, should exist
Assert.That(componentFactory.GetComponentAvailability(TestComponentName), Is.EqualTo(ComponentAvailability.Available));
// Normal casing, ignore case, should exist
Assert.That(componentFactory.GetComponentAvailability(TestComponentName, true), Is.EqualTo(ComponentAvailability.Available));
// Lower casing, do not ignore case, should not exist
Assert.That(componentFactory.GetComponentAvailability(LowercaseTestComponentName), Is.EqualTo(ComponentAvailability.Unknown));
// Lower casing, ignore case, should exist
Assert.That(componentFactory.GetComponentAvailability(LowercaseTestComponentName, true), Is.EqualTo(ComponentAvailability.Available));
}
[Test]
public void GetComponentTest()
{
var componentFactory = IoCManager.Resolve<IComponentFactory>();
// Should not exist
Assert.Throws<UnknownComponentException>(() => componentFactory.GetComponent(NonexistentComponentName));
Assert.Throws<UnknownComponentException>(() => componentFactory.GetComponent(NonexistentComponentName, true));
// Normal casing, do not ignore case, should exist
Assert.That(componentFactory.GetComponent(TestComponentName), Is.InstanceOf<TestComponent>());
// Normal casing, ignore case, should exist
Assert.That(componentFactory.GetComponent(TestComponentName, true), Is.InstanceOf<TestComponent>());
// Lower casing, do not ignore case, should not exist
Assert.Throws<UnknownComponentException>(() => componentFactory.GetComponent(LowercaseTestComponentName));
// Lower casing, ignore case, should exist
Assert.That(componentFactory.GetComponent(LowercaseTestComponentName, true), Is.InstanceOf<TestComponent>());
}
[Test]
public void GetRegistrationTest()
{
var componentFactory = IoCManager.Resolve<IComponentFactory>();
// Should not exist
Assert.Throws<UnknownComponentException>(() => componentFactory.GetRegistration(NonexistentComponentName));
Assert.Throws<UnknownComponentException>(() => componentFactory.GetRegistration(NonexistentComponentName, true));
// Normal casing, do not ignore case, should exist
Assert.DoesNotThrow(() => componentFactory.GetRegistration(TestComponentName));
// Normal casing, ignore case, should exist
Assert.DoesNotThrow(() => componentFactory.GetRegistration(TestComponentName, true));
// Lower casing, do not ignore case, should not exist
Assert.Throws<UnknownComponentException>(() => componentFactory.GetRegistration(LowercaseTestComponentName));
// Lower casing, ignore case, should exist
Assert.DoesNotThrow(() => componentFactory.GetRegistration(LowercaseTestComponentName, true));
}
[Test]
public void TryGetRegistrationTest()
{
var componentFactory = IoCManager.Resolve<IComponentFactory>();
// Should not exist
Assert.That(componentFactory.TryGetRegistration(NonexistentComponentName, out _), Is.False);
Assert.That(componentFactory.TryGetRegistration(NonexistentComponentName, out _, true), Is.False);
// Normal casing, do not ignore case, should exist
Assert.That(componentFactory.TryGetRegistration(TestComponentName, out _));
// Normal casing, ignore case, should exist
Assert.That(componentFactory.TryGetRegistration(TestComponentName, out _, true));
// Lower casing, do not ignore case, should not exist
Assert.That(componentFactory.TryGetRegistration(LowercaseTestComponentName, out _), Is.False);
// Lower casing, ignore case, should exist
Assert.That(componentFactory.TryGetRegistration(LowercaseTestComponentName, out _, true));
}
[ComponentProtoName(TestComponentName)]
private sealed partial class TestComponent : Component
{
}
}
}

View File

@@ -1,370 +0,0 @@
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Client.GameObjects;
using Robust.Client.Timing;
using Robust.Server.Player;
using Robust.Shared.Containers;
using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Network;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Robust.UnitTesting.Shared.GameObjects
{
public sealed class ContainerTests : RobustIntegrationTest
{
/// <summary>
/// Tests container states with children that do not exist on the client
/// and tests that said children are added to the container when they do arrive on the client.
/// </summary>
/// <returns></returns>
[Test]
public async Task TestContainerNonexistantItems()
{
var server = StartServer();
var client = StartClient();
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
var cEntManager = client.ResolveDependency<IEntityManager>();
var clientNetManager = client.ResolveDependency<IClientNetManager>();
var sEntManager = server.ResolveDependency<IEntityManager>();
var sPlayerManager = server.ResolveDependency<IPlayerManager>();
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
client.Post(() =>
{
clientNetManager.ClientConnect(null!, 0, null!);
});
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Setup
var mapId = MapId.Nullspace;
var mapPos = MapCoordinates.Nullspace;
EntityUid entityUid = default!;
var cContainerSys = cEntManager.System<ContainerSystem>();
var sContainerSys = sEntManager.System<SharedContainerSystem>();
var sMetadataSys = sEntManager.System<MetaDataSystem>();
await server.WaitAssertion(() =>
{
sEntManager.System<SharedMapSystem>().CreateMap(out mapId);
mapPos = new MapCoordinates(new Vector2(0, 0), mapId);
entityUid = sEntManager.SpawnEntity(null, mapPos);
sMetadataSys.SetEntityName(entityUid, "Container");
sContainerSys.EnsureContainer<Container>(entityUid, "dummy");
// Setup PVS
sEntManager.AddComponent<EyeComponent>(entityUid);
var player = sPlayerManager.Sessions.First();
server.PlayerMan.SetAttachedEntity(player, entityUid);
sPlayerManager.JoinGame(player);
});
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
EntityUid itemUid = default!;
await server.WaitAssertion(() =>
{
itemUid = sEntManager.SpawnEntity(null, mapPos);
sMetadataSys.SetEntityName(itemUid, "Item");
var container = sContainerSys.EnsureContainer<Container>(entityUid, "dummy");
Assert.That(sContainerSys.Insert(itemUid, container));
// Modify visibility layer so that the item does not get sent ot the player
sEntManager.System<SharedVisibilitySystem>().AddLayer(itemUid, 10 );
});
// Needs minimum 4 to sync to client because buffer size is 3
await server.WaitRunTicks(4);
await client.WaitRunTicks(10);
EntityUid cEntityUid = default!;
await client.WaitAssertion(() =>
{
cEntityUid = client.EntMan.GetEntity(server.EntMan.GetNetEntity(entityUid));
if (!cEntManager.TryGetComponent<ContainerManagerComponent>(cEntityUid, out var containerManagerComp))
{
Assert.Fail();
return;
}
var container = cContainerSys.GetContainer(cEntityUid, "dummy", containerManagerComp);
Assert.That(container.ContainedEntities.Count, Is.EqualTo(0));
Assert.That(container.ExpectedEntities.Count, Is.EqualTo(1));
Assert.That(cContainerSys.ExpectedEntities.ContainsKey(sEntManager.GetNetEntity(itemUid)));
Assert.That(cContainerSys.ExpectedEntities.Count, Is.EqualTo(1));
});
await server.WaitAssertion(() =>
{
// Modify visibility layer so it now gets sent to the client
sEntManager.System<SharedVisibilitySystem>().RemoveLayer(itemUid, 10 );
});
await server.WaitRunTicks(1);
await client.WaitRunTicks(4);
await client.WaitAssertion(() =>
{
if (!cEntManager.TryGetComponent<ContainerManagerComponent>(cEntityUid, out var containerManagerComp))
{
Assert.Fail();
return;
}
var container = cContainerSys.GetContainer(cEntityUid, "dummy", containerManagerComp);
Assert.That(container.ContainedEntities.Count, Is.EqualTo(1));
Assert.That(container.ExpectedEntities.Count, Is.EqualTo(0));
Assert.That(!cContainerSys.ExpectedEntities.ContainsKey(sEntManager.GetNetEntity(itemUid)));
Assert.That(cContainerSys.ExpectedEntities, Is.Empty);
});
await client.WaitPost(() => clientNetManager.ClientDisconnect(""));
await server.WaitRunTicks(5);
await client.WaitRunTicks(5);
}
/// <summary>
/// Tests container states with children that do not exist on the client
/// and that if those children are deleted that they get properly removed from the expected entities list.
/// </summary>
/// <returns></returns>
[Test]
public async Task TestContainerExpectedEntityDeleted()
{
var server = StartServer();
var client = StartClient();
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
var cEntManager = client.ResolveDependency<IEntityManager>();
var clientTime = client.ResolveDependency<IClientGameTiming>();
var clientNetManager = client.ResolveDependency<IClientNetManager>();
var sMapManager = server.ResolveDependency<IMapManager>();
var sEntManager = server.ResolveDependency<IEntityManager>();
var sPlayerManager = server.ResolveDependency<IPlayerManager>();
var serverTime = server.ResolveDependency<IGameTiming>();
Assert.DoesNotThrow(() => client.SetConnectTarget(server));
await client.WaitPost(() =>
{
clientNetManager.ClientConnect(null!, 0, null!);
});
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
// Setup
MapId mapId;
var mapPos = MapCoordinates.Nullspace;
EntityUid sEntityUid = default!;
EntityUid sItemUid = default!;
NetEntity netEnt = default;
var cContainerSys = cEntManager.System<ContainerSystem>();
var sContainerSys = sEntManager.System<SharedContainerSystem>();
var sMetadataSys = sEntManager.System<MetaDataSystem>();
await server.WaitAssertion(() =>
{
sEntManager.System<SharedMapSystem>().CreateMap(out mapId);
mapPos = new MapCoordinates(new Vector2(0, 0), mapId);
sEntityUid = sEntManager.SpawnEntity(null, mapPos);
sMetadataSys.SetEntityName(sEntityUid, "Container");
sContainerSys.EnsureContainer<Container>(sEntityUid, "dummy");
// Setup PVS
sEntManager.AddComponent<EyeComponent>(sEntityUid);
var player = sPlayerManager.Sessions.First();
server.PlayerMan.SetAttachedEntity(player, sEntityUid);
sPlayerManager.JoinGame(player);
});
for (int i = 0; i < 10; i++)
{
await server.WaitRunTicks(1);
await client.WaitRunTicks(1);
}
await server.WaitAssertion(() =>
{
sItemUid = sEntManager.SpawnEntity(null, mapPos);
netEnt = sEntManager.GetNetEntity(sItemUid);
sMetadataSys.SetEntityName(sItemUid, "Item");
var container = sContainerSys.GetContainer(sEntityUid, "dummy");
sContainerSys.Insert(sItemUid, container);
// Modify visibility layer so that the item does not get sent ot the player
sEntManager.System<SharedVisibilitySystem>().AddLayer(sItemUid, 10 );
});
await server.WaitRunTicks(1);
while (clientTime.LastRealTick < serverTime.CurTick - 1)
{
await client.WaitRunTicks(1);
}
var cUid = cEntManager.GetEntity(sEntManager.GetNetEntity(sEntityUid));
await client.WaitAssertion(() =>
{
if (!cEntManager.TryGetComponent<ContainerManagerComponent>(cUid, out var containerManagerComp))
{
Assert.Fail();
return;
}
var container = cContainerSys.GetContainer(cUid, "dummy", containerManagerComp);
Assert.That(container.ContainedEntities.Count, Is.EqualTo(0));
Assert.That(container.ExpectedEntities.Count, Is.EqualTo(1));
Assert.That(cContainerSys.ExpectedEntities.ContainsKey(netEnt));
Assert.That(cContainerSys.ExpectedEntities.Count, Is.EqualTo(1));
});
await server.WaitAssertion(() =>
{
// If possible it'd be best to only have the DeleteEntity, but right now
// the entity deleted event is not played on the client if the entity does not exist on the client.
if (sEntManager.EntityExists(sItemUid)
// && itemUid.TryGetContainer(out var container))
&& sContainerSys.TryGetContainingContainer(sItemUid, out var container))
{
sContainerSys.Remove(sItemUid, container, force: true);
}
sEntManager.DeleteEntity(sItemUid);
});
await server.WaitRunTicks(1);
await client.WaitRunTicks(4);
await client.WaitAssertion(() =>
{
if (!cEntManager.TryGetComponent<ContainerManagerComponent>(cUid, out var containerManagerComp))
{
Assert.Fail();
return;
}
var container = cContainerSys.GetContainer(cUid, "dummy", containerManagerComp);
Assert.That(container.ContainedEntities.Count, Is.EqualTo(0));
Assert.That(container.ExpectedEntities.Count, Is.EqualTo(0));
Assert.That(!cContainerSys.ExpectedEntities.ContainsKey(netEnt));
Assert.That(cContainerSys.ExpectedEntities.Count, Is.EqualTo(0));
});
await client.WaitPost(() => clientNetManager.ClientDisconnect(""));
await server.WaitRunTicks(5);
await client.WaitRunTicks(5);
}
/// <summary>
/// Sets up a new container, initializes map, saves the map, then loads it again on another map. The contained entity should still
/// be inside the container.
/// </summary>
[Test]
public async Task Container_DeserializeGrid_IsStillContained()
{
var server = StartServer();
await Task.WhenAll(server.WaitIdleAsync());
var sEntManager = server.ResolveDependency<IEntityManager>();
var mapSys = sEntManager.System<SharedMapSystem>();
var sContainerSys = sEntManager.System<SharedContainerSystem>();
var sMetadataSys = sEntManager.System<MetaDataSystem>();
var path = new ResPath("container_test.yml");
await server.WaitAssertion(() =>
{
// build the map
sEntManager.System<SharedMapSystem>().CreateMap(out var mapIdOne);
Assert.That(mapSys.IsInitialized(mapIdOne), Is.True);
var containerEnt = sEntManager.SpawnEntity(null, new MapCoordinates(1, 1, mapIdOne));
sMetadataSys.SetEntityName(containerEnt, "ContainerEnt");
var containeeEnt = sEntManager.SpawnEntity(null, new MapCoordinates(2, 2, mapIdOne));
sMetadataSys.SetEntityName(containeeEnt, "ContaineeEnt");
var container = sContainerSys.MakeContainer<Container>(containerEnt, "testContainer");
container.OccludesLight = true;
container.ShowContents = true;
sContainerSys.Insert(containeeEnt, container);
// save the map
var mapLoader = sEntManager.EntitySysManager.GetEntitySystem<MapLoaderSystem>();
Assert.That(mapLoader.TrySaveMap(mapIdOne, path));
mapSys.DeleteMap(mapIdOne);
});
// A few moments later...
await server.WaitRunTicks(10);
await server.WaitAssertion(() =>
{
var mapLoader = sEntManager.System<MapLoaderSystem>();
// load the map
Assert.That(mapLoader.TryLoadMap(path, out var map, out _));
Assert.That(mapSys.IsInitialized(map), Is.True); // Map Initialize-ness is saved in the map file.
});
await server.WaitRunTicks(1);
await server.WaitAssertion(() =>
{
// verify container
Entity<ContainerManagerComponent> container = default;
var query = sEntManager.EntityQueryEnumerator<ContainerManagerComponent>();
while (query.MoveNext(out var uid, out var containerComp))
{
container = (uid, containerComp);
}
var containerEnt = container.Owner;
Assert.That(container.Comp, Is.Not.Null);
Assert.That(sEntManager.GetComponent<MetaDataComponent>(containerEnt).EntityName, Is.EqualTo("ContainerEnt"));
Assert.That(container.Comp!.Containers.ContainsKey("testContainer"));
var baseContainer = sContainerSys.GetContainer(containerEnt, "testContainer", container.Comp);
Assert.That(baseContainer.ContainedEntities, Has.Count.EqualTo(1));
var containeeEnt = baseContainer.ContainedEntities[0];
Assert.That(sEntManager.GetComponent<MetaDataComponent>(containeeEnt).EntityName, Is.EqualTo("ContaineeEnt"));
});
}
}
}

View File

@@ -1,145 +0,0 @@
using System.Threading.Tasks;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Reflection;
namespace Robust.UnitTesting.Shared.GameObjects;
public sealed partial class DeferredEntityDeletionTest : RobustIntegrationTest
{
// This test ensures that deferred deletion can be used while handling events without issue, and that deleting an
// entity after deferring component removal doesn't cause any issues.
[Test]
public async Task TestDeferredEntityDeletion()
{
var options = new ServerIntegrationOptions();
options.Pool = false;
options.BeforeRegisterComponents += () =>
{
var fact = IoCManager.Resolve<IComponentFactory>();
fact.RegisterClass<DeferredDeletionTestComponent>();
fact.RegisterClass<OtherDeferredDeletionTestComponent>();
};
options.BeforeStart += () =>
{
var sysMan = IoCManager.Resolve<IEntitySystemManager>();
sysMan.LoadExtraSystemType<DeferredDeletionTestSystem>();
sysMan.LoadExtraSystemType<OtherDeferredDeletionTestSystem>();
};
var server = StartServer(options);
await server.WaitIdleAsync();
EntityUid uid1 = default, uid2 = default, uid3 = default, uid4 = default;
DeferredDeletionTestComponent comp1 = default!, comp2 = default!, comp3 = default!, comp4 = default!;
IEntityManager entMan = default!;
await server.WaitAssertion(() =>
{
var mapMan = IoCManager.Resolve<IMapManager>();
entMan = IoCManager.Resolve<IEntityManager>();
var sys = entMan.EntitySysManager.GetEntitySystem<DeferredDeletionTestSystem>();
uid1 = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
uid2 = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
uid3 = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
uid4 = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
comp1 = entMan.AddComponent<DeferredDeletionTestComponent>(uid1);
comp2 = entMan.AddComponent<DeferredDeletionTestComponent>(uid2);
comp3 = entMan.AddComponent<DeferredDeletionTestComponent>(uid3);
comp4 = entMan.AddComponent<DeferredDeletionTestComponent>(uid4);
entMan.AddComponent<OtherDeferredDeletionTestComponent>(uid1);
entMan.AddComponent<OtherDeferredDeletionTestComponent>(uid2);
entMan.AddComponent<OtherDeferredDeletionTestComponent>(uid3);
entMan.AddComponent<OtherDeferredDeletionTestComponent>(uid4);
});
await server.WaitRunTicks(1);
// first: test that deferring deletion while handling events doesn't cause issues
await server.WaitAssertion(() =>
{
Assert.That(comp1.Running);
var ev = new DeferredDeletionTestEvent();
entMan.EventBus.RaiseLocalEvent(uid1, ev);
Assert.That(comp1.LifeStage == ComponentLifeStage.Stopped);
});
await server.WaitRunTicks(1);
Assert.That(comp1.LifeStage == ComponentLifeStage.Deleted);
// next check that entity deletion doesn't cause issues:
await server.WaitAssertion(() =>
{
var ev = new DeferredDeletionTestEvent();
entMan.EventBus.RaiseLocalEvent(uid2, ev);
entMan.EventBus.RaiseLocalEvent(uid3, ev);
entMan.EventBus.RaiseLocalEvent(uid4, ev);
entMan.DeleteEntity(uid2);
entMan.QueueDeleteEntity(uid3);
entMan.TryQueueDeleteEntity(uid4);
Assert.That(entMan.Deleted(uid2));
Assert.That(!entMan.Deleted(uid3));
Assert.That(!entMan.Deleted(uid4));
Assert.That(comp2.LifeStage == ComponentLifeStage.Deleted);
Assert.That(comp3.LifeStage == ComponentLifeStage.Stopped);
Assert.That(comp4.LifeStage == ComponentLifeStage.Stopped);
});
await server.WaitRunTicks(1);
Assert.That(comp3.LifeStage == ComponentLifeStage.Deleted);
Assert.That(comp4.LifeStage == ComponentLifeStage.Deleted);
Assert.That(entMan.Deleted(uid3));
Assert.That(entMan.Deleted(uid4));
await server.WaitIdleAsync();
}
[Reflect(false)]
private sealed class DeferredDeletionTestSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<DeferredDeletionTestComponent, DeferredDeletionTestEvent>(OnTestEvent);
}
private void OnTestEvent(EntityUid uid, DeferredDeletionTestComponent component, DeferredDeletionTestEvent args)
{
// remove both this component, and some other component that this entity has that also subscribes to this event.
RemCompDeferred<DeferredDeletionTestComponent>(uid);
RemCompDeferred<OtherDeferredDeletionTestComponent>(uid);
}
}
[Reflect(false)]
private sealed class OtherDeferredDeletionTestSystem : EntitySystem
{
public override void Initialize() => SubscribeLocalEvent<OtherDeferredDeletionTestComponent, DeferredDeletionTestEvent>(OnTestEvent);
private void OnTestEvent(EntityUid uid, OtherDeferredDeletionTestComponent component, DeferredDeletionTestEvent args)
{
// remove both this component, and some other component that this entity has that also subscribes to this event.
RemCompDeferred<DeferredDeletionTestComponent>(uid);
RemCompDeferred<OtherDeferredDeletionTestComponent>(uid);
}
}
[RegisterComponent]
[Reflect(false)]
private sealed partial class DeferredDeletionTestComponent : Component
{
}
[Reflect(false)]
private sealed partial class OtherDeferredDeletionTestComponent : Component
{
}
private sealed class DeferredDeletionTestEvent
{
}
}

View File

@@ -1,270 +0,0 @@
using System.Collections.Generic;
using Moq;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Reflection;
using Robust.UnitTesting.Server;
namespace Robust.UnitTesting.Shared.GameObjects
{
public sealed partial class EntityEventBusTests
{
[Test]
public void SubscribeCompEvent()
{
var (bus, sim, entUid, compInstance) = EntFactory();
var compFactory = sim.Resolve<IComponentFactory>();
// Subscribe
int calledCount = 0;
bus.SubscribeLocalEvent<MetaDataComponent, TestEvent>(HandleTestEvent);
bus.LockSubscriptions();
// add a component to the system
bus.OnEntityAdded(entUid);
var reg = compFactory.GetRegistration(CompIdx.Index<MetaDataComponent>());
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid), reg));
// Raise
var evntArgs = new TestEvent(5);
bus.RaiseLocalEvent(entUid, evntArgs, true);
// Assert
Assert.That(calledCount, Is.EqualTo(1));
void HandleTestEvent(EntityUid uid, MetaDataComponent component, TestEvent args)
{
calledCount++;
Assert.That(uid, Is.EqualTo(entUid));
Assert.That(component, Is.EqualTo(compInstance));
Assert.That(args.TestNumber, Is.EqualTo(5));
}
}
[Test]
public void UnsubscribeCompEvent()
{
var (bus, sim, entUid, compInstance) = EntFactory();
var compFactory = sim.Resolve<IComponentFactory>();
// Subscribe
int calledCount = 0;
bus.SubscribeLocalEvent<MetaDataComponent, TestEvent>(HandleTestEvent);
bus.UnsubscribeLocalEvent<MetaDataComponent, TestEvent>();
bus.LockSubscriptions();
// add a component to the system
bus.OnEntityAdded(entUid);
var reg = compFactory.GetRegistration(CompIdx.Index<MetaDataComponent>());
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(compInstance, entUid), reg));
// Raise
var evntArgs = new TestEvent(5);
bus.RaiseLocalEvent(entUid, evntArgs, true);
// Assert
Assert.That(calledCount, Is.EqualTo(0));
void HandleTestEvent(EntityUid uid, MetaDataComponent component, TestEvent args)
{
calledCount++;
}
}
[Test]
public void SubscribeCompLifeEvent()
{
var (bus, sim, entUid, compInstance) = EntFactory();
var entMan = sim.Resolve<EntityManager>();
var fact = sim.Resolve<IComponentFactory>();
// Subscribe
int calledCount = 0;
bus.SubscribeLocalEvent<MetaDataComponent, ComponentInit>(HandleTestEvent);
bus.LockSubscriptions();
// Raise
bus.RaiseComponentEvent(entUid, compInstance, new ComponentInit());
// Assert
Assert.That(calledCount, Is.EqualTo(1));
void HandleTestEvent(EntityUid uid, MetaDataComponent component, ComponentInit args)
{
calledCount++;
Assert.That(uid, Is.EqualTo(entUid));
Assert.That(component, Is.EqualTo(compInstance));
}
}
[Test]
public void CompEventOrdered()
{
var sim = RobustServerSimulation
.NewSimulation()
.RegisterComponents(f =>
{
f.RegisterClass<OrderAComponent>();
f.RegisterClass<OrderBComponent>();
f.RegisterClass<OrderCComponent>();
})
.InitializeInstance();
var entMan = sim.Resolve<EntityManager>();
var entUid = entMan.Spawn();
var instA = entMan.AddComponent<OrderAComponent>(entUid);
var instB = entMan.AddComponent<OrderBComponent>(entUid);
var instC = entMan.AddComponent<OrderCComponent>(entUid);
var bus = entMan.EventBusInternal;
bus.ClearSubscriptions();
var fact = sim.Resolve<IComponentFactory>();
// Subscribe
var a = false;
var b = false;
var c = false;
void HandlerA(EntityUid uid, Component comp, TestEvent ev)
{
Assert.That(b, Is.False, "A should run before B");
Assert.That(c, Is.False, "A should run before C");
a = true;
}
void HandlerB(EntityUid uid, Component comp, TestEvent ev)
{
Assert.That(c, Is.True, "B should run after C");
b = true;
}
void HandlerC(EntityUid uid, Component comp, TestEvent ev) => c = true;
bus.SubscribeLocalEvent<OrderAComponent, TestEvent>(HandlerA, typeof(OrderAComponent), before: new []{typeof(OrderBComponent), typeof(OrderCComponent)});
bus.SubscribeLocalEvent<OrderBComponent, TestEvent>(HandlerB, typeof(OrderBComponent), after: new []{typeof(OrderCComponent)});
bus.SubscribeLocalEvent<OrderCComponent, TestEvent>(HandlerC, typeof(OrderCComponent));
bus.LockSubscriptions();
// add a component to the system
bus.OnEntityAdded(entUid);
var regA = fact.GetRegistration(CompIdx.Index<OrderAComponent>());
var regB = fact.GetRegistration(CompIdx.Index<OrderBComponent>());
var regC = fact.GetRegistration(CompIdx.Index<OrderCComponent>());
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instA, entUid), regA));
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instB, entUid), regB));
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instC, entUid), regC));
// Raise
var evntArgs = new TestEvent(5);
bus.RaiseLocalEvent(entUid, evntArgs, true);
// Assert
Assert.That(a, Is.True, "A did not fire");
Assert.That(b, Is.True, "B did not fire");
Assert.That(c, Is.True, "C did not fire");
}
[Test]
public void CompEventLoop()
{
var sim = RobustServerSimulation
.NewSimulation()
.RegisterComponents(f =>
{
f.RegisterClass<OrderAComponent>();
f.RegisterClass<OrderBComponent>();
})
.InitializeInstance();
var entMan = sim.Resolve<EntityManager>();
var entUid = entMan.Spawn();
var instA = entMan.AddComponent<OrderAComponent>(entUid);
var instB = entMan.AddComponent<OrderBComponent>(entUid);
var bus = entMan.EventBusInternal;
bus.ClearSubscriptions();
var fact = sim.Resolve<IComponentFactory>();
var regA = fact.GetRegistration(CompIdx.Index<OrderAComponent>());
var regB = fact.GetRegistration(CompIdx.Index<OrderBComponent>());
var handlerACount = 0;
void HandlerA(EntityUid uid, Component comp, TestEvent ev)
{
Assert.That(handlerACount, Is.EqualTo(0));
handlerACount++;
// add and then remove component B
bus.OnComponentRemoved(new RemovedComponentEventArgs(new ComponentEventArgs(instB, entUid), false, default!, CompIdx.Index<OrderBComponent>()));
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instB, entUid), regB));
}
var handlerBCount = 0;
void HandlerB(EntityUid uid, Component comp, TestEvent ev)
{
Assert.That(handlerBCount, Is.EqualTo(0));
handlerBCount++;
// add and then remove component A
bus.OnComponentRemoved(new RemovedComponentEventArgs(new ComponentEventArgs(instA, entUid), false, default!, CompIdx.Index<OrderAComponent>()));
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instA, entUid), regA));
}
bus.SubscribeLocalEvent<OrderAComponent, TestEvent>(HandlerA, typeof(OrderAComponent));
bus.SubscribeLocalEvent<OrderBComponent, TestEvent>(HandlerB, typeof(OrderBComponent));
bus.LockSubscriptions();
// add a component to the system
bus.OnEntityAdded(entUid);
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instA, entUid), regA));
bus.OnComponentAdded(new AddedComponentEventArgs(new ComponentEventArgs(instB, entUid), regB));
// Event subscriptions currently use a linked list.
// Currently expect event subscriptions to be raised in order: handlerB -> handlerA
// If a component gets removed and added again, it gets moved back to the front of the linked list.
// I.e., adding and then removing compA changes the linked list order: handlerA -> handlerB
//
// This could in principle cause the event raising code to enter an infinite loop.
// Adding and removing a comp in an event handler may seem silly but:
// - it doesn't have to be the same component if you had a chain of three or more components
// - some event handlers raise other events and can lead to convoluted chains of interactions that might inadvertently trigger something like this.
// Raise
bus.RaiseLocalEvent(entUid, new TestEvent(0));
// Assert
Assert.That(handlerACount, Is.LessThanOrEqualTo(1));
Assert.That(handlerBCount, Is.LessThanOrEqualTo(1));
Assert.That(handlerACount+handlerBCount, Is.GreaterThan(0));
}
private sealed partial class DummyComponent : Component
{
}
private sealed partial class OrderAComponent : Component
{
}
private sealed partial class OrderBComponent : Component
{
}
private sealed partial class OrderCComponent : Component
{
}
private sealed class TestEvent : EntityEventArgs
{
public int TestNumber { get; }
public TestEvent(int testNumber)
{
TestNumber = testNumber;
}
}
}
}

View File

@@ -1,75 +0,0 @@
using System.Collections.Generic;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Reflection;
using Robust.UnitTesting.Server;
namespace Robust.UnitTesting.Shared.GameObjects;
public sealed partial class EntityEventBusTests
{
// Explanation of what bug this is testing:
// Because event ordering is keyed on system type, we have a problem.
// If you register to a directed event like FooEvent twice for different components,
// you now have different subscriptions with the same key.
//
// To trigger this, at least one subscription to this event (possibly another system entirely)
// needs to demand some ordering calculation to happen.
[Test]
public void TestDifferentComponentsOrderedSameKeySub()
{
var simulation = RobustServerSimulation
.NewSimulation()
.RegisterEntitySystems(factory =>
{
factory.LoadExtraSystemType<DifferentComponentsSameKeySubSystem>();
factory.LoadExtraSystemType<DifferentComponentsSameKeySubSystem2>();
})
.RegisterComponents(factory => factory.RegisterClass<FooComponent>())
.InitializeInstance();
var map = simulation.CreateMap().MapId;
var entity = simulation.SpawnEntity(null, new MapCoordinates(0, 0, map));
simulation.Resolve<IEntityManager>().AddComponent<FooComponent>(entity);
var foo = new FooEvent();
simulation.Resolve<IEntityManager>().EventBus.RaiseLocalEvent(entity, foo, true);
Assert.That(foo.EventOrder, Is.EquivalentTo(new[]{"Foo", "Transform", "Metadata"}).Or.EquivalentTo(new[]{"Foo", "Metadata", "Transform"}));
}
[Reflect(false)]
private sealed class DifferentComponentsSameKeySubSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<TransformComponent, FooEvent>((_, _, e) => { e.EventOrder.Add("Transform"); });
SubscribeLocalEvent<MetaDataComponent, FooEvent>((_, _, e) => { e.EventOrder.Add("Metadata"); });
}
}
[Reflect(false)]
private sealed class DifferentComponentsSameKeySubSystem2 : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<FooComponent, FooEvent>(
(_, _, e) => e.EventOrder.Add("Foo"),
before: new[] {typeof(DifferentComponentsSameKeySubSystem)});
}
}
[Reflect(false)]
private sealed partial class FooComponent : Component
{
}
private sealed class FooEvent
{
public List<string> EventOrder = new();
}
}

View File

@@ -1,149 +0,0 @@
using System;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Reflection;
using Robust.UnitTesting.Server;
namespace Robust.UnitTesting.Shared.GameObjects
{
[TestFixture]
public partial class EntityEventBusTests
{
[Test]
public void SubscribeCompRefBroadcastEvent()
{
// Arrange.
var simulation = RobustServerSimulation
.NewSimulation()
.RegisterEntitySystems(factory => factory.LoadExtraSystemType<SubscribeCompRefBroadcastSystem>())
.InitializeInstance();
var ev = new TestStructEvent() {TestNumber = 5};
simulation.Resolve<IEntityManager>().EventBus.RaiseEvent(EventSource.Local, ref ev);
Assert.That(ev.TestNumber, Is.EqualTo(15));
}
[Reflect(false)]
public sealed class SubscribeCompRefBroadcastSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<TestStructEvent>(OnTestEvent);
}
private void OnTestEvent(ref TestStructEvent ev)
{
Assert.That(ev.TestNumber, Is.EqualTo(5));
ev.TestNumber += 10;
}
}
[Test]
public void SubscriptionNoMixedRefValueBroadcastEvent()
{
// Arrange.
var simulation = RobustServerSimulation
.NewSimulation()
.RegisterEntitySystems(factory =>
factory.LoadExtraSystemType<SubscriptionNoMixedRefValueBroadcastEventSystem>());
// Act. No mixed ref and value subscriptions are allowed.
Assert.Throws(typeof(InvalidOperationException), () => simulation.InitializeInstance());
}
[Reflect(false)]
private sealed class SubscriptionNoMixedRefValueBroadcastEventSystem : EntitySystem
{
public override void Initialize()
{
// The below is not allowed, as you're subscribing by-ref and by-value to the same event...
#pragma warning disable RA0013
SubscribeLocalEvent<TestStructEvent>(MyRefHandler);
SubscribeLocalEvent<TestStructEvent>(MyValueHandler);
#pragma warning restore RA0013
}
private void MyValueHandler(TestStructEvent args) { }
private void MyRefHandler(ref TestStructEvent args) { }
}
[Test]
public void SortedBroadcastRefEvents()
{
// Arrange.
var simulation = RobustServerSimulation
.NewSimulation()
.RegisterEntitySystems(factory =>
{
factory.LoadExtraSystemType<BroadcastOrderASystem>();
factory.LoadExtraSystemType<BroadcastOrderBSystem>();
factory.LoadExtraSystemType<BroadcastOrderCSystem>();
})
.InitializeInstance();
// Act.
var testEvent = new TestStructEvent {TestNumber = 5};
var eventBus = simulation.Resolve<IEntityManager>().EventBus;
eventBus.RaiseEvent(EventSource.Local, ref testEvent);
// Check that the entity systems changed the value correctly
Assert.That(testEvent.TestNumber, Is.EqualTo(15));
}
[Reflect(false)]
private sealed class BroadcastOrderASystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<TestStructEvent>(OnA, new[]{typeof(BroadcastOrderBSystem)}, new[]{typeof(BroadcastOrderCSystem)});
}
private void OnA(ref TestStructEvent args)
{
// Second handler being ran.
Assert.That(args.TestNumber, Is.EqualTo(0));
args.TestNumber = 10;
}
}
[Reflect(false)]
private sealed class BroadcastOrderBSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<TestStructEvent>(OnB, null, new []{typeof(BroadcastOrderASystem)});
}
private void OnB(ref TestStructEvent args)
{
// Last handler being ran.
Assert.That(args.TestNumber, Is.EqualTo(10));
args.TestNumber = 15;
}
}
[Reflect(false)]
private sealed class BroadcastOrderCSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<TestStructEvent>(OnC);
}
private void OnC(ref TestStructEvent args)
{
// First handler being ran.
Assert.That(args.TestNumber, Is.EqualTo(5));
args.TestNumber = 0;
}
}
}
}

View File

@@ -1,166 +0,0 @@
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Reflection;
using Robust.UnitTesting.Server;
namespace Robust.UnitTesting.Shared.GameObjects
{
public partial class EntityEventBusTests
{
[Test]
public void SubscribeCompRefDirectedEvent()
{
// Arrange.
var simulation = RobustServerSimulation
.NewSimulation()
.RegisterComponents(factory => factory.RegisterClass<DummyComponent>())
.RegisterEntitySystems(factory => factory.LoadExtraSystemType<SubscribeCompRefDirectedEventSystem>())
.InitializeInstance();
var map = simulation.CreateMap().MapId;
var entity = simulation.SpawnEntity(null, new MapCoordinates(0, 0, map));
IoCManager.Resolve<IEntityManager>().AddComponent<DummyComponent>(entity);
// Act.
var testEvent = new TestStructEvent {TestNumber = 5};
var eventBus = simulation.Resolve<IEntityManager>().EventBus;
eventBus.RaiseLocalEvent(entity, ref testEvent, true);
// Check that the entity system changed the value correctly
Assert.That(testEvent.TestNumber, Is.EqualTo(10));
}
[Reflect(false)]
private sealed class SubscribeCompRefDirectedEventSystem : EntitySystem
{
public override void Initialize()
{
SubscribeLocalEvent<DummyComponent, TestStructEvent>(MyRefHandler);
}
private void MyRefHandler(EntityUid uid, DummyComponent component, ref TestStructEvent args)
{
Assert.That(args.TestNumber, Is.EqualTo(5));
args.TestNumber = 10;
}
}
[Reflect(false)]
private sealed class SubscriptionNoMixedRefValueDirectedEventSystem : EntitySystem
{
public override void Initialize()
{
// The below is not allowed, as you're subscribing by-ref and by-value to the same event...
SubscribeLocalEvent<DummyComponent, TestStructEvent>(MyRefHandler);
#pragma warning disable RA0013
SubscribeLocalEvent<DummyTwoComponent, TestStructEvent>(MyValueHandler);
#pragma warning restore RA0013
}
private void MyValueHandler(EntityUid uid, DummyTwoComponent component, TestStructEvent args) { }
private void MyRefHandler(EntityUid uid, DummyComponent component, ref TestStructEvent args) { }
}
[Test]
public void SortedDirectedRefEvents()
{
// Arrange.
var simulation = RobustServerSimulation
.NewSimulation()
.RegisterComponents(factory =>
{
factory.RegisterClass<OrderAComponent>();
factory.RegisterClass<OrderBComponent>();
factory.RegisterClass<OrderCComponent>();
})
.RegisterEntitySystems(factory =>
{
factory.LoadExtraSystemType<OrderASystem>();
factory.LoadExtraSystemType<OrderBSystem>();
factory.LoadExtraSystemType<OrderCSystem>();
})
.InitializeInstance();
var map = simulation.CreateMap().MapId;
var entity = simulation.SpawnEntity(null, new MapCoordinates(0, 0, map));
IoCManager.Resolve<IEntityManager>().AddComponent<OrderAComponent>(entity);
IoCManager.Resolve<IEntityManager>().AddComponent<OrderBComponent>(entity);
IoCManager.Resolve<IEntityManager>().AddComponent<OrderCComponent>(entity);
// Act.
var testEvent = new TestStructEvent {TestNumber = 5};
var eventBus = simulation.Resolve<IEntityManager>().EventBus;
eventBus.RaiseLocalEvent(entity, ref testEvent, true);
// Check that the entity systems changed the value correctly
Assert.That(testEvent.TestNumber, Is.EqualTo(15));
}
[Reflect(false)]
private sealed class OrderASystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<OrderAComponent, TestStructEvent>(OnA, new[]{typeof(OrderBSystem)}, new[]{typeof(OrderCSystem)});
}
private void OnA(EntityUid uid, OrderAComponent component, ref TestStructEvent args)
{
// Second handler being ran.
Assert.That(args.TestNumber, Is.EqualTo(0));
args.TestNumber = 10;
}
}
[Reflect(false)]
private sealed class OrderBSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<OrderBComponent, TestStructEvent>(OnB, null, new []{typeof(OrderASystem)});
}
private void OnB(EntityUid uid, OrderBComponent component, ref TestStructEvent args)
{
// Last handler being ran.
Assert.That(args.TestNumber, Is.EqualTo(10));
args.TestNumber = 15;
}
}
[Reflect(false)]
private sealed class OrderCSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<OrderCComponent, TestStructEvent>(OnC);
}
private void OnC(EntityUid uid, OrderCComponent component, ref TestStructEvent args)
{
// First handler being ran.
Assert.That(args.TestNumber, Is.EqualTo(5));
args.TestNumber = 0;
}
}
private sealed partial class DummyTwoComponent : Component
{
}
[ByRefEvent]
private struct TestStructEvent
{
public int TestNumber;
}
}
}

View File

@@ -1,14 +0,0 @@
using System;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Reflection;
using Robust.UnitTesting.Server;
namespace Robust.UnitTesting.Shared.GameObjects
{
public partial class EntityEventBusTests
{
}
}

View File

@@ -1,508 +0,0 @@
using System;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.UnitTesting.Server;
namespace Robust.UnitTesting.Shared.GameObjects
{
[TestFixture, Parallelizable, TestOf(typeof(EntityEventBus))]
public partial class EntityEventBusTests
{
private static (EntityEventBus Bus, ISimulation Sim, EntityUid Uid, MetaDataComponent Comp) EntFactory()
{
var sim = RobustServerSimulation.NewSimulation().InitializeInstance();
var entMan = sim.Resolve<EntityManager>();
var uid = entMan.Spawn();
var comp = entMan.MetaQuery.Comp(uid);
var bus = entMan.EventBusInternal;
bus.ClearSubscriptions();
return (bus, sim, uid, comp);
}
private static EntityEventBus BusFactory()
{
return EntFactory().Bus;
}
/// <summary>
/// Trying to subscribe a null handler causes a <see cref="ArgumentNullException"/> to be thrown.
/// </summary>
[Test]
public void SubscribeEvent_NullHandler_NullArgumentException()
{
// Arrange
var bus = BusFactory();
var subscriber = new TestEventSubscriber();
// Act
void Code() => bus.SubscribeEvent(EventSource.Local, subscriber, (EntityEventHandler<TestEventArgs>) null!);
//Assert
Assert.Throws<ArgumentNullException>(Code);
}
/// <summary>
/// Trying to subscribe with a null subscriber causes a <see cref="ArgumentNullException"/> to be thrown.
/// </summary>
[Test]
public void SubscribeEvent_NullSubscriber_NullArgumentException()
{
// Arrange
var bus = BusFactory();
// Act
void Code() => bus.SubscribeEvent<TestEventArgs>(EventSource.Local, null!, ev => {});
//Assert: this should do nothing
Assert.Throws<ArgumentNullException>(Code);
}
/// <summary>
/// Duplicate Event subscriptions are not allowed.
/// </summary>
[Test]
public void SubscribeEvent_DuplicateSubscription_Invalid()
{
// Arrange
var bus = BusFactory();
var subscriber = new TestEventSubscriber();
int delegateCallCount = 0;
void Handler(TestEventArgs ev) => delegateCallCount++;
// 2 subscriptions 1 handler
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler);
Assert.Throws<InvalidOperationException>(() => bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler));
}
// TODO if ever duplicate events are allowed, re-enable these tests.
/*
/// <summary>
/// Unlike C# events, the set of event handler delegates is unique.
/// Subscribing the same delegate multiple times will only call the handler once.
/// </summary>
[Test]
public void SubscribeEvent_DuplicateSubscription_RaisedOnce()
{
// Arrange
var bus = BusFactory();
var subscriber = new TestEventSubscriber();
int delegateCallCount = 0;
void Handler(TestEventArgs ev) => delegateCallCount++;
// 2 subscriptions 1 handler
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler);
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler);
// Act
bus.RaiseEvent(EventSource.Local, new TestEventArgs());
//Assert
Assert.That(delegateCallCount, Is.EqualTo(1));
}
/// <summary>
/// Subscribing two different delegates to a single event type causes both events
/// to be raised in an indeterminate order.
/// </summary>
[Test]
public void SubscribeEvent_MultipleDelegates_BothRaised()
{
// Arrange
var bus = BusFactory();
var subscriber = new TestEventSubscriber();
int delFooCount = 0;
int delBarCount = 0;
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, ev => delFooCount++);
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, ev => delBarCount++);
// Act
bus.RaiseEvent(EventSource.Local, new TestEventArgs());
// Assert
Assert.That(delFooCount, Is.EqualTo(1));
Assert.That(delBarCount, Is.EqualTo(1));
}
*/
/// <summary>
/// A subscriber's handlers are properly called only when the specified event type is raised.
/// </summary>
[Test]
public void SubscribeEvent_MultipleSubscriptions_IndividuallyCalled()
{
// Arrange
var bus = BusFactory();
var subscriber = new TestEventSubscriber();
int delFooCount = 0;
int delBarCount = 0;
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, ev => delFooCount++);
bus.SubscribeEvent<TestEventTwoArgs>(EventSource.Local, subscriber, ev => delBarCount++);
bus.LockSubscriptions();
// Act & Assert
bus.RaiseEvent(EventSource.Local, new TestEventArgs());
Assert.That(delFooCount, Is.EqualTo(1));
Assert.That(delBarCount, Is.EqualTo(0));
delFooCount = delBarCount = 0;
bus.RaiseEvent(EventSource.Local, new TestEventTwoArgs());
Assert.That(delFooCount, Is.EqualTo(0));
Assert.That(delBarCount, Is.EqualTo(1));
}
/// <summary>
/// Trying to subscribe with <see cref="EventSource.None"/> makes no sense and causes
/// a <see cref="ArgumentOutOfRangeException"/> to be thrown.
/// </summary>
[Test]
public void SubscribeEvent_SourceNone_ArgOutOfRange()
{
// Arrange
var bus = BusFactory();
var subscriber = new TestEventSubscriber();
void TestEventHandler(TestEventArgs args) { }
// Act
void Code() => bus.SubscribeEvent(EventSource.None, subscriber, (EntityEventHandler<TestEventArgs>)TestEventHandler);
//Assert
Assert.Throws<ArgumentOutOfRangeException>(Code);
}
/// <summary>
/// Unsubscribing a handler twice does nothing.
/// </summary>
[Test]
public void UnsubscribeEvent_DoubleUnsubscribe_Nop()
{
// Arrange
var bus = BusFactory();
var subscriber = new TestEventSubscriber();
void Handler(TestEventArgs ev) { }
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler);
bus.UnsubscribeEvent<TestEventArgs>(EventSource.Local, subscriber);
bus.LockSubscriptions();
// Act
bus.UnsubscribeEvent<TestEventArgs>(EventSource.Local, subscriber);
// Assert: Does not throw
}
/// <summary>
/// Unsubscribing a handler that was never subscribed in the first place does nothing.
/// </summary>
[Test]
public void UnsubscribeEvent_NoSubscription_Nop()
{
// Arrange
var bus = BusFactory();
var subscriber = new TestEventSubscriber();
// Act
bus.UnsubscribeEvent<TestEventArgs>(EventSource.Local, subscriber);
// Assert: Does not throw
}
/// <summary>
/// Trying to unsubscribe with a null subscriber causes a <see cref="ArgumentNullException"/> to be thrown.
/// </summary>
[Test]
public void UnsubscribeEvent_NullSubscriber_NullArgumentException()
{
// Arrange
var bus = BusFactory();
// Act
void Code() => bus.UnsubscribeEvent<TestEventArgs>(EventSource.Local, null!);
// Assert
Assert.Throws<ArgumentNullException>(Code);
}
/// <summary>
/// An event cannot be subscribed to with <see cref="EventSource.None"/>, so trying to unsubscribe
/// with an <see cref="EventSource.None"/> causes a <see cref="ArgumentOutOfRangeException"/> to be thrown.
/// </summary>
[Test]
public void UnsubscribeEvent_SourceNone_ArgOutOfRange()
{
// Arrange
var bus = BusFactory();
var subscriber = new TestEventSubscriber();
// Act
void Code() => bus.UnsubscribeEvent<TestEventArgs>(EventSource.None, subscriber);
// Assert
Assert.Throws<ArgumentOutOfRangeException>(Code);
}
/// <summary>
/// Raising an event with no handlers subscribed to it does nothing.
/// </summary>
[Test]
public void RaiseEvent_NoSubscriptions_Nop()
{
// Arrange
var bus = BusFactory();
var subscriber = new TestEventSubscriber();
int delCalledCount = 0;
bus.SubscribeEvent<TestEventTwoArgs>(EventSource.Local, subscriber, ev => delCalledCount++);
bus.LockSubscriptions();
// Act
bus.RaiseEvent(EventSource.Local, new TestEventArgs());
// Assert
Assert.That(delCalledCount, Is.EqualTo(0));
}
/// <summary>
/// Raising an event when a handler has been unsubscribed no longer calls the handler.
/// </summary>
[Test]
public void RaiseEvent_Unsubscribed_Nop()
{
// Arrange
var bus = BusFactory();
var subscriber = new TestEventSubscriber();
int delCallCount = 0;
void Handler(TestEventArgs ev) => delCallCount++;
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler);
bus.UnsubscribeEvent<TestEventArgs>(EventSource.Local, subscriber);
bus.LockSubscriptions();
// Act
bus.RaiseEvent(EventSource.Local, new TestEventArgs());
// Assert
Assert.That(delCallCount, Is.EqualTo(0));
}
/// <summary>
/// Trying to raise an event with <see cref="EventSource.None"/> makes no sense and causes
/// a <see cref="ArgumentOutOfRangeException"/> to be thrown.
/// </summary>
[Test]
public void RaiseEvent_SourceNone_ArgOutOfRange()
{
// Arrange
var bus = BusFactory();
// Act
void Code() => bus.RaiseEvent(EventSource.None, new TestEventArgs());
// Assert
Assert.Throws<ArgumentOutOfRangeException>(Code);
}
/// <summary>
/// Trying to unsubscribe all of a null subscriber's events causes a <see cref="ArgumentNullException"/> to be thrown.
/// </summary>
[Test]
public void UnsubscribeEvents_NullSubscriber_NullArgumentException()
{
// Arrange
var bus = BusFactory();
// Act
void Code() => bus.UnsubscribeEvents(null!);
// Assert
Assert.Throws<ArgumentNullException>(Code);
}
/// <summary>
/// Unsubscribing a subscriber with no subscriptions does nothing.
/// </summary>
[Test]
public void UnsubscribeEvents_NoSubscriptions_Nop()
{
// Arrange
var bus = BusFactory();
var subscriber = new TestEventSubscriber();
// Act
bus.UnsubscribeEvents(subscriber);
// Assert: no exception
}
/// <summary>
/// The subscriber's handlers are not raised after they are unsubscribed.
/// </summary>
[Test]
public void UnsubscribeEvents_UnsubscribedHandler_Nop()
{
// Arrange
var bus = BusFactory();
var subscriber = new TestEventSubscriber();
int delCallCount = 0;
void Handler(TestEventArgs ev) => delCallCount++;
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler);
bus.UnsubscribeEvents(subscriber);
bus.LockSubscriptions();
// Act
bus.RaiseEvent(EventSource.Local, new TestEventArgs());
// Assert
Assert.That(delCallCount, Is.EqualTo(0));
}
/// <summary>
/// Trying to queue a null event causes a <see cref="ArgumentNullException"/> to be thrown.
/// </summary>
[Test]
public void QueueEvent_NullEvent_ArgumentNullException()
{
// Arrange
var bus = BusFactory();
// Act
void Code() => bus.QueueEvent(EventSource.Local, null!);
// Assert
Assert.Throws<ArgumentNullException>(Code);
}
/// <summary>
/// Queuing an event does not immediately raise the event unless the queue is processed.
/// </summary>
[Test]
public void QueueEvent_EventQueued_DoesNotImmediatelyRaise()
{
// Arrange
var bus = BusFactory();
var subscriber = new TestEventSubscriber();
int delCallCount = 0;
void Handler(TestEventArgs ev) => delCallCount++;
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler);
// Act
bus.QueueEvent(EventSource.Local, new TestEventArgs());
// Assert
Assert.That(delCallCount, Is.EqualTo(0));
}
/// <summary>
/// Trying to queue an event with <see cref="EventSource.None"/> makes no sense and causes
/// a <see cref="ArgumentOutOfRangeException"/> to be thrown.
/// </summary>
[Test]
public void QueueEvent_SourceNone_ArgOutOfRange()
{
// Arrange
var bus = BusFactory();
// Act
void Code() => bus.QueueEvent(EventSource.None, new TestEventArgs());
// Assert
Assert.Throws<ArgumentOutOfRangeException>(Code);
}
/// <summary>
/// Queued events are raised when the queue is processed.
/// </summary>
[Test]
public void ProcessQueue_EventQueued_HandlerRaised()
{
// Arrange
var bus = BusFactory();
var subscriber = new TestEventSubscriber();
int delCallCount = 0;
void Handler(TestEventArgs ev) => delCallCount++;
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, subscriber, Handler);
bus.LockSubscriptions();
bus.QueueEvent(EventSource.Local, new TestEventArgs());
// Act
bus.ProcessEventQueue();
// Assert
Assert.That(delCallCount, Is.EqualTo(1));
}
[Test]
public void RaiseEvent_Ordered()
{
// Arrange
var bus = BusFactory();
// Expected order is A -> C -> B
var a = false;
var b = false;
var c = false;
void HandlerA(TestEventArgs ev)
{
Assert.That(b, Is.False, "A should run before B");
Assert.That(c, Is.False, "A should run before C");
a = true;
}
void HandlerB(TestEventArgs ev)
{
Assert.That(c, Is.True, "B should run after C");
b = true;
}
void HandlerC(TestEventArgs ev) => c = true;
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, new SubA(), HandlerA, typeof(SubA), before: new []{typeof(SubB), typeof(SubC)});
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, new SubB(), HandlerB, typeof(SubB), after: new []{typeof(SubC)});
bus.SubscribeEvent<TestEventArgs>(EventSource.Local, new SubC(), HandlerC, typeof(SubC));
bus.LockSubscriptions();
// Act
bus.RaiseEvent(EventSource.Local, new TestEventArgs());
// Assert
Assert.That(a, Is.True, "A did not fire");
Assert.That(b, Is.True, "B did not fire");
Assert.That(c, Is.True, "C did not fire");
}
public sealed class SubA : IEntityEventSubscriber
{
}
public sealed class SubB : IEntityEventSubscriber
{
}
public sealed class SubC : IEntityEventSubscriber
{
}
}
internal sealed class TestEventSubscriber : IEntityEventSubscriber { }
internal sealed class TestEventArgs : EntityEventArgs { }
internal sealed class TestEventTwoArgs : EntityEventArgs { }
}

View File

@@ -1,168 +0,0 @@
using System.Numerics;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.UnitTesting.Server;
namespace Robust.UnitTesting.Shared.GameObjects;
[TestFixture]
public sealed partial class EntityManagerCopyTests
{
[Test]
public void CopyComponentGeneric()
{
var instant = RobustServerSimulation.NewSimulation();
instant.RegisterComponents(fac =>
{
fac.RegisterClass<AComponent>();
});
var sim = instant.InitializeInstance();
var entManager = sim.Resolve<IEntityManager>();
var mapSystem = entManager.System<SharedMapSystem>();
mapSystem.CreateMap(out var mapId);
var original = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
var comp = entManager.AddComponent<AComponent>(original);
Assert.That(comp.Value, Is.EqualTo(false));
comp.Value = true;
var target = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
Assert.That(!entManager.HasComponent<AComponent>(target));
var targetComp = entManager.CopyComponent(original, target, comp);
Assert.That(entManager.GetComponent<AComponent>(target), Is.EqualTo(targetComp));
Assert.That(targetComp.Value, Is.EqualTo(comp.Value));
Assert.That(!ReferenceEquals(comp, targetComp));
}
[Test]
public void CopyComponentNonGeneric()
{
var instant = RobustServerSimulation.NewSimulation();
instant.RegisterComponents(fac =>
{
fac.RegisterClass<AComponent>();
});
var sim = instant.InitializeInstance();
var entManager = sim.Resolve<IEntityManager>();
var mapSystem = entManager.System<SharedMapSystem>();
mapSystem.CreateMap(out var mapId);
var original = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
var comp = entManager.AddComponent<AComponent>(original);
Assert.That(comp.Value, Is.EqualTo(false));
comp.Value = true;
var target = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
Assert.That(!entManager.HasComponent<AComponent>(target));
var targetComp = entManager.CopyComponent(original, target, (IComponent) comp);
Assert.That(entManager.GetComponent<AComponent>(target), Is.EqualTo(targetComp));
Assert.That(((AComponent) targetComp).Value, Is.EqualTo(comp.Value));
Assert.That(!ReferenceEquals(comp, targetComp));
}
[Test]
public void CopyComponentMultiple()
{
var instant = RobustServerSimulation.NewSimulation();
instant.RegisterComponents(fac =>
{
fac.RegisterClass<AComponent>();
fac.RegisterClass<BComponent>();
});
var sim = instant.InitializeInstance();
var entManager = sim.Resolve<IEntityManager>();
var mapSystem = entManager.System<SharedMapSystem>();
mapSystem.CreateMap(out var mapId);
var original = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
var comp = entManager.AddComponent<AComponent>(original);
var comp2 = entManager.AddComponent<BComponent>(original);
Assert.That(comp.Value, Is.EqualTo(false));
comp.Value = true;
var target = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
Assert.That(!entManager.HasComponent<AComponent>(target));
entManager.CopyComponents(original, target, null, comp, comp2);
var targetComp = entManager.GetComponent<AComponent>(target);
var targetComp2 = entManager.GetComponent<BComponent>(target);
Assert.That(entManager.GetComponent<AComponent>(target), Is.EqualTo(targetComp));
Assert.That(targetComp.Value, Is.EqualTo(comp.Value));
Assert.That(entManager.GetComponent<BComponent>(target), Is.EqualTo(targetComp2));
Assert.That(targetComp2.Value, Is.EqualTo(comp2.Value));
Assert.That(!ReferenceEquals(comp, targetComp));
Assert.That(!ReferenceEquals(comp2, targetComp2));
}
[Test]
public void CopyComponentMultipleViaTry()
{
var instant = RobustServerSimulation.NewSimulation();
instant.RegisterComponents(fac =>
{
fac.RegisterClass<AComponent>();
fac.RegisterClass<BComponent>();
});
var sim = instant.InitializeInstance();
var entManager = sim.Resolve<IEntityManager>();
var mapSystem = entManager.System<SharedMapSystem>();
mapSystem.CreateMap(out var mapId);
var original = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
var comp = entManager.AddComponent<AComponent>(original);
var comp2 = entManager.AddComponent<BComponent>(original);
Assert.That(comp.Value, Is.EqualTo(false));
comp.Value = true;
var target = entManager.Spawn(null, new MapCoordinates(Vector2.Zero, mapId));
Assert.That(!entManager.HasComponent<AComponent>(target));
entManager.TryCopyComponents(original, target, null, comp.GetType(), comp2.GetType());
var targetComp = entManager.GetComponent<AComponent>(target);
var targetComp2 = entManager.GetComponent<BComponent>(target);
Assert.That(entManager.GetComponent<AComponent>(target), Is.EqualTo(targetComp));
Assert.That(targetComp.Value, Is.EqualTo(comp.Value));
Assert.That(entManager.GetComponent<BComponent>(target), Is.EqualTo(targetComp2));
Assert.That(targetComp2.Value, Is.EqualTo(comp2.Value));
Assert.That(!ReferenceEquals(comp, targetComp));
Assert.That(!ReferenceEquals(comp2, targetComp2));
}
[DataDefinition]
private sealed partial class AComponent : Component
{
[DataField]
public bool Value = false;
}
[DataDefinition]
private sealed partial class BComponent : Component
{
[DataField]
public bool Value = false;
}
}

View File

@@ -1,373 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager;
using Robust.UnitTesting.Server;
namespace Robust.UnitTesting.Shared.GameObjects
{
[TestFixture, Parallelizable ,TestOf(typeof(EntityManager))]
public sealed partial class EntityManager_Components_Tests
{
private const string DummyLoadId = "DummyLoad";
private const string DummyLoad = $@"
- type: entity
id: {DummyLoadId}
name: weh
components:
- type: Joint
- type: Physics
";
[Test]
public void AddRegistryComponentTest()
{
var sim = RobustServerSimulation
.NewSimulation()
.RegisterPrototypes(fac => fac.LoadString(DummyLoad))
.InitializeInstance();
var entMan = sim.Resolve<IEntityManager>();
var protoManager = sim.Resolve<IPrototypeManager>();
var map = sim.CreateMap().Uid;
var coords = new EntityCoordinates(map, default);
var entity = entMan.SpawnEntity(null, coords);
Assert.That(!entMan.HasComponent<PhysicsComponent>(entity));
var proto = protoManager.Index<EntityPrototype>(DummyLoadId);
entMan.AddComponents(entity, proto);
Assert.Multiple(() =>
{
Assert.That(entMan.HasComponent<JointComponent>(entity));
Assert.That(entMan.HasComponent<PhysicsComponent>(entity));
});
}
[Test]
public void RemoveRegistryComponentTest()
{
var sim = RobustServerSimulation
.NewSimulation()
.RegisterPrototypes(fac => fac.LoadString(DummyLoad))
.InitializeInstance();
var entMan = sim.Resolve<IEntityManager>();
var protoManager = sim.Resolve<IPrototypeManager>();
var map = sim.CreateMap().Uid;
var coords = new EntityCoordinates(map, default);
var entity = entMan.SpawnEntity(DummyLoadId, coords);
var proto = protoManager.Index<EntityPrototype>(DummyLoadId);
entMan.RemoveComponents(entity, proto);
Assert.Multiple(() =>
{
Assert.That(!entMan.HasComponent<JointComponent>(entity));
Assert.That(!entMan.HasComponent<PhysicsComponent>(entity));
});
}
[Test]
public void AddComponentTest()
{
// Arrange
var (sim, coords) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var entity = entMan.SpawnEntity(null, coords);
var component = new DummyComponent();
// Act
entMan.AddComponent(entity, component);
// Assert
var result = entMan.GetComponent<DummyComponent>(entity);
Assert.That(result, Is.EqualTo(component));
}
[Test]
public void AddComponentOverwriteTest()
{
// Arrange
var (sim, coords) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var entity = entMan.SpawnEntity(null, coords);
var component = new DummyComponent();
// Act
entMan.AddComponent(entity, component, true);
// Assert
var result = entMan.GetComponent<DummyComponent>(entity);
Assert.That(result, Is.EqualTo(component));
}
[Test]
public void AddComponent_ExistingDeleted()
{
// Arrange
var (sim, coords) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var entity = entMan.SpawnEntity(null, coords);
var firstComp = new DummyComponent();
entMan.AddComponent(entity, firstComp);
entMan.RemoveComponent<DummyComponent>(entity);
var secondComp = new DummyComponent();
// Act
entMan.AddComponent(entity, secondComp);
// Assert
var result = entMan.GetComponent<DummyComponent>(entity);
Assert.That(result, Is.EqualTo(secondComp));
}
[Test]
public void HasComponentTest()
{
// Arrange
var (sim, coords) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var entity = entMan.SpawnEntity(null, coords);
entMan.AddComponent<DummyComponent>(entity);
// Act
var result = entMan.HasComponent<DummyComponent>(entity);
// Assert
Assert.That(result, Is.True);
}
[Test]
public void HasComponentNoGenericTest()
{
// Arrange
var (sim, coords) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var entity = entMan.SpawnEntity(null, coords);
entMan.AddComponent<DummyComponent>(entity);
// Act
var result = entMan.HasComponent(entity, typeof(DummyComponent));
// Assert
Assert.That(result, Is.True);
}
[Test]
public void HasNetComponentTest()
{
// Arrange
var (sim, coords) = SimulationFactory();
var factory = sim.Resolve<IComponentFactory>();
var netId = factory.GetRegistration<DummyComponent>().NetID!;
var entMan = sim.Resolve<IEntityManager>();
var entity = entMan.SpawnEntity(null, coords);
entMan.AddComponent<DummyComponent>(entity);
// Act
var result = entMan.HasComponent(entity, netId.Value);
// Assert
Assert.That(result, Is.True);
}
[Test]
public void GetNetComponentTest()
{
// Arrange
var (sim, coords) = SimulationFactory();
var factory = sim.Resolve<IComponentFactory>();
var netId = factory.GetRegistration<DummyComponent>().NetID!;
var entMan = sim.Resolve<IEntityManager>();
var entity = entMan.SpawnEntity(null, coords);
var component = entMan.AddComponent<DummyComponent>(entity);
// Act
var result = entMan.GetComponent(entity, netId.Value);
// Assert
Assert.That(result, Is.EqualTo(component));
}
[Test]
public void TryGetComponentTest()
{
// Arrange
var (sim, coords) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var entity = entMan.SpawnEntity(null, coords);
var component = entMan.AddComponent<DummyComponent>(entity);
// Act
var result = entMan.TryGetComponent<DummyComponent>(entity, out var comp);
// Assert
Assert.That(result, Is.True);
Assert.That(comp, Is.EqualTo(component));
}
[Test]
public void TryGetNetComponentTest()
{
// Arrange
var (sim, coords) = SimulationFactory();
var factory = sim.Resolve<IComponentFactory>();
var netId = factory.GetRegistration<DummyComponent>().NetID!;
var entMan = sim.Resolve<IEntityManager>();
var entity = entMan.SpawnEntity(null, coords);
var component = entMan.AddComponent<DummyComponent>(entity);
// Act
var result = entMan.TryGetComponent(entity, netId.Value, out var comp);
// Assert
Assert.That(result, Is.True);
Assert.That(comp, Is.EqualTo(component));
}
[Test]
public void RemoveComponentTest()
{
// Arrange
var (sim, coords) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var entity = entMan.SpawnEntity(null, coords);
var component = entMan.AddComponent<DummyComponent>(entity);
// Act
entMan.RemoveComponent<DummyComponent>(entity);
entMan.CullRemovedComponents();
// Assert
Assert.That(entMan.HasComponent(entity, component.GetType()), Is.False);
}
[Test]
public void EnsureQueuedComponentDeletion()
{
var (sim, coords) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var entity = entMan.SpawnEntity(null, coords);
var component = entMan.AddComponent<DummyComponent>(entity);
Assert.That(component.LifeStage, Is.LessThanOrEqualTo(ComponentLifeStage.Running));
entMan.RemoveComponentDeferred(entity, component);
Assert.That(component.LifeStage, Is.EqualTo(ComponentLifeStage.Stopped));
Assert.That(entMan.EnsureComponent<DummyComponent>(entity, out var comp2), Is.False);
Assert.That(comp2.LifeStage, Is.LessThanOrEqualTo(ComponentLifeStage.Running));
Assert.That(component.LifeStage, Is.EqualTo(ComponentLifeStage.Deleted));
}
[Test]
public void RemoveNetComponentTest()
{
// Arrange
var (sim, coords) = SimulationFactory();
var factory = sim.Resolve<IComponentFactory>();
var netId = factory.GetRegistration<DummyComponent>().NetID!;
var entMan = sim.Resolve<IEntityManager>();
var entity = entMan.SpawnEntity(null, coords);
var component = entMan.AddComponent<DummyComponent>(entity);
// Act
entMan.RemoveComponent(entity, netId.Value);
entMan.CullRemovedComponents();
// Assert
Assert.That(entMan.HasComponent(entity, component.GetType()), Is.False);
}
[Test]
public void GetComponentsTest()
{
// Arrange
var (sim, coords) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var entity = entMan.SpawnEntity(null, coords);
var component = entMan.AddComponent<DummyComponent>(entity);
// Act
var result = entMan.GetComponents<DummyComponent>(entity);
// Assert
var list = result.ToList();
Assert.That(list.Count, Is.EqualTo(1));
Assert.That(list[0], Is.EqualTo(component));
}
[Test]
public void GetAllComponentsTest()
{
// Arrange
var (sim, coords) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var entity = entMan.SpawnEntity(null, coords);
var component = entMan.AddComponent<DummyComponent>(entity);
// Act
var result = entMan.EntityQuery<DummyComponent>(true);
// Assert
var list = result.ToList();
Assert.That(list.Count, Is.EqualTo(1));
Assert.That(list[0], Is.EqualTo(component));
}
[Test]
public void GetAllComponentInstances()
{
// Arrange
var (sim, coords) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var fac = sim.Resolve<IComponentFactory>();
var entity = entMan.SpawnEntity(null, coords);
var component = entMan.AddComponent<DummyComponent>(entity);
// Act
var result = entMan.GetComponents(entity);
// Assert
var list = result.Where(c=>fac.GetComponentName(c.GetType()) == "Dummy").ToList();
Assert.That(list.Count, Is.EqualTo(1));
Assert.That(list[0], Is.EqualTo(component));
}
private static (ISimulation, EntityCoordinates) SimulationFactory()
{
var sim = RobustServerSimulation
.NewSimulation()
.RegisterComponents(factory => factory.RegisterClass<DummyComponent>())
.InitializeInstance();
var map = sim.CreateMap().Uid;
var coords = new EntityCoordinates(map, default);
return (sim, coords);
}
[NetworkedComponent()]
private sealed partial class DummyComponent : Component, ICompType1, ICompType2
{
}
private interface ICompType1 { }
private interface ICompType2 { }
}
}

View File

@@ -1,85 +0,0 @@
using System;
using System.IO;
using Moq;
using NUnit.Framework;
using Robust.Server.Configuration;
using Robust.Server.Reflection;
using Robust.Server.Serialization;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Network;
using Robust.Shared.Profiling;
using Robust.Shared.Reflection;
using Robust.Shared.Replays;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
namespace Robust.UnitTesting.Shared.GameObjects
{
[TestFixture, Serializable]
sealed class EntityState_Tests
{
/// <summary>
/// Used to measure the size of <see cref="object"/>s in bytes. This is not actually a test,
/// but a useful benchmark tool, so i'm leaving it here.
/// </summary>
[Test]
public void ComponentChangedSerialized()
{
var container = new DependencyCollection();
container.Register<ILogManager, LogManager>();
container.Register<IConfigurationManager, ServerNetConfigurationManager>();
container.Register<IConfigurationManagerInternal, ServerNetConfigurationManager>();
container.Register<INetManager, NetManager>();
container.Register<IHWId, DummyHWId>();
container.Register<IReflectionManager, ServerReflectionManager>();
container.Register<IRobustSerializer, ServerRobustSerializer>();
container.Register<IRobustMappedStringSerializer, RobustMappedStringSerializer>();
container.Register<IAuthManager, AuthManager>();
container.Register<IGameTiming, GameTiming>();
container.Register<ProfManager, ProfManager>();
container.Register<HttpClientHolder>();
container.RegisterInstance<IReplayRecordingManager>(new Mock<IReplayRecordingManager>().Object);
container.BuildGraph();
var cfg = container.Resolve<IConfigurationManagerInternal>();
cfg.Initialize(true);
cfg.LoadCVarsFromAssembly(typeof(IConfigurationManager).Assembly);
container.Resolve<IReflectionManager>().LoadAssemblies(AppDomain.CurrentDomain.GetAssemblyByName("Robust.Shared"));
IoCManager.InitThread(container, replaceExisting: true);
cfg.LoadCVarsFromAssembly(typeof(IConfigurationManager).Assembly); // Robust.Shared
container.Resolve<INetManager>().Initialize(true);
var serializer = container.Resolve<IRobustSerializer>();
serializer.Initialize();
IoCManager.Resolve<IRobustMappedStringSerializer>().LockStrings();
byte[] array;
using(var stream = new MemoryStream())
{
var payload = new EntityState(
new NetEntity(64),
new []
{
new ComponentChange(0, new MapGridComponentDeltaState(16, chunkData: null, default), default)
}, default);
serializer.Serialize(stream, payload);
array = stream.ToArray();
}
IoCManager.Clear();
Assert.Pass($"Size in Bytes: {array.Length.ToString()}");
}
}
}

View File

@@ -1,137 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Moq;
using NUnit.Framework;
using Robust.Server.Configuration;
using Robust.Shared.Configuration;
using Robust.Shared.ContentPack;
using Robust.Shared.Exceptions;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Network;
using Robust.Shared.Profiling;
using Robust.Shared.Reflection;
using Robust.Shared.Replays;
using Robust.Shared.Timing;
namespace Robust.UnitTesting.Shared.GameObjects
{
[TestFixture]
[TestOf(typeof(EntitySystemManager))]
public sealed class EntitySystemManagerOrderTest
{
private sealed class Counter
{
public int X;
}
[Reflect(false)]
private abstract class TestSystemBase : IEntitySystem
{
public Counter? Counter;
public int LastUpdate;
public virtual IEnumerable<Type> UpdatesAfter => Enumerable.Empty<Type>();
public virtual IEnumerable<Type> UpdatesBefore => Enumerable.Empty<Type>();
public bool UpdatesOutsidePrediction => true;
public void Initialize() { }
public void Shutdown() { }
public void Update(float frameTime)
{
LastUpdate = Counter!.X++;
}
public void FrameUpdate(float frameTime) { }
}
// Expected update order is is A -> D -> C -> B
[Reflect(false)]
private sealed class TestSystemA : TestSystemBase
{
}
[Reflect(false)]
private sealed class TestSystemB : TestSystemBase
{
public override IEnumerable<Type> UpdatesAfter => new[] {typeof(TestSystemA)};
}
[Reflect(false)]
private sealed class TestSystemC : TestSystemBase
{
public override IEnumerable<Type> UpdatesBefore => new[] {typeof(TestSystemB)};
}
[Reflect(false)]
private sealed class TestSystemD : TestSystemBase
{
public override IEnumerable<Type> UpdatesAfter => new[] {typeof(TestSystemA)};
public override IEnumerable<Type> UpdatesBefore => new[] {typeof(TestSystemC)};
}
[Test]
public void Test()
{
var deps = new DependencyCollection();
deps.Register<IRuntimeLog, RuntimeLog>();
deps.Register<ILogManager, LogManager>();
deps.Register<IGameTiming, GameTiming>();
deps.RegisterInstance<INetManager>(new Mock<INetManager>().Object);
deps.Register<IConfigurationManager, ServerNetConfigurationManager>();
deps.Register<IServerNetConfigurationManager, ServerNetConfigurationManager>();
deps.Register<ProfManager, ProfManager>();
deps.Register<IDynamicTypeFactory, DynamicTypeFactory>();
deps.Register<IDynamicTypeFactoryInternal, DynamicTypeFactory>();
deps.RegisterInstance<IModLoader>(new Mock<IModLoader>().Object);
deps.Register<IEntitySystemManager, EntitySystemManager>();
deps.RegisterInstance<IEntityManager>(new Mock<IEntityManager>().Object);
// WHEN WILL THE SUFFERING END
deps.RegisterInstance<IReplayRecordingManager>(new Mock<IReplayRecordingManager>().Object);
var reflectionMock = new Mock<IReflectionManager>();
reflectionMock.Setup(p => p.GetAllChildren<IEntitySystem>(false))
.Returns(new[]
{
typeof(TestSystemA),
typeof(TestSystemB),
typeof(TestSystemC),
typeof(TestSystemD),
});
deps.RegisterInstance<IReflectionManager>(reflectionMock.Object);
deps.BuildGraph();
IoCManager.InitThread(deps, true);
var systems = deps.Resolve<IEntitySystemManager>();
systems.Initialize();
var counter = new Counter();
systems.GetEntitySystem<TestSystemA>().Counter = counter;
systems.GetEntitySystem<TestSystemB>().Counter = counter;
systems.GetEntitySystem<TestSystemC>().Counter = counter;
systems.GetEntitySystem<TestSystemD>().Counter = counter;
systems.TickUpdate(1, noPredictions: false);
Assert.That(counter.X, Is.EqualTo(4));
Assert.That(systems.GetEntitySystem<TestSystemA>().LastUpdate, Is.EqualTo(0));
Assert.That(systems.GetEntitySystem<TestSystemB>().LastUpdate, Is.EqualTo(3));
Assert.That(systems.GetEntitySystem<TestSystemC>().LastUpdate, Is.EqualTo(2));
Assert.That(systems.GetEntitySystem<TestSystemD>().LastUpdate, Is.EqualTo(1));
}
[TearDown]
public void TearDown()
{
IoCManager.Clear();
}
}
}

View File

@@ -1,107 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.IoC.Exceptions;
namespace Robust.UnitTesting.Shared.GameObjects
{
[TestFixture, TestOf(typeof(EntitySystemManager))]
public sealed class EntitySystemManager_Tests: RobustUnitTest
{
public abstract class ESystemBase : IEntitySystem
{
public virtual IEnumerable<Type> UpdatesAfter => Enumerable.Empty<Type>();
public virtual IEnumerable<Type> UpdatesBefore => Enumerable.Empty<Type>();
public bool UpdatesOutsidePrediction => true;
public void Initialize() { }
public void Shutdown() { }
public void Update(float frameTime) { }
public void FrameUpdate(float frameTime) { }
}
[Virtual]
public class ESystemA : ESystemBase { }
public sealed class ESystemC : ESystemA { }
public abstract class ESystemBase2 : ESystemBase { }
public sealed class ESystemB : ESystemBase2 { }
public sealed class ESystemDepA : ESystemBase
{
[Dependency] public readonly ESystemDepB ESystemDepB = default!;
}
public sealed class ESystemDepB : ESystemBase
{
[Dependency] public readonly ESystemDepA ESystemDepA = default!;
}
/*
ESystemBase (Abstract)
- ESystemA
- ESystemC
- EsystemBase2 (Abstract)
- ESystemB
*/
[OneTimeSetUp]
public void Setup()
{
var syssy = IoCManager.Resolve<IEntitySystemManager>();
syssy.Clear();
syssy.LoadExtraSystemType<ESystemA>();
syssy.LoadExtraSystemType<ESystemB>();
syssy.LoadExtraSystemType<ESystemC>();
syssy.LoadExtraSystemType<ESystemDepA>();
syssy.LoadExtraSystemType<ESystemDepB>();
syssy.Initialize(false);
}
[Test]
public void GetsByTypeOrSupertype()
{
var esm = IoCManager.Resolve<IEntitySystemManager>();
// getting type by the exact type should work fine
Assert.That(esm.GetEntitySystem<ESystemB>(), Is.TypeOf<ESystemB>());
// getting type by an abstract supertype should work fine
// because there are no other subtypes of that supertype it would conflict with
// it should return the only concrete subtype
Assert.That(esm.GetEntitySystem<ESystemBase2>(), Is.TypeOf<ESystemB>());
// getting ESystemA type by its exact type should work fine,
// even though EsystemC is a subtype - it should return an instance of ESystemA
var esysA = esm.GetEntitySystem<ESystemA>();
Assert.That(esysA, Is.TypeOf<ESystemA>());
Assert.That(esysA, Is.Not.TypeOf<ESystemC>());
var esysC = esm.GetEntitySystem<ESystemC>();
Assert.That(esysC, Is.TypeOf<ESystemC>());
// this should not work - it's abstract and there are multiple
// concrete subtypes
Assert.Throws<UnregisteredTypeException>(() =>
{
esm.GetEntitySystem<ESystemBase>();
});
}
[Test]
public void DependencyTest()
{
var esm = IoCManager.Resolve<IEntitySystemManager>();
var sysA = esm.GetEntitySystem<ESystemDepA>();
var sysB = esm.GetEntitySystem<ESystemDepB>();
Assert.That(sysA.ESystemDepB, Is.EqualTo(sysB));
Assert.That(sysB.ESystemDepA, Is.EqualTo(sysA));
}
}
}

View File

@@ -1,226 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Robust.UnitTesting.Shared.GameObjects;
public sealed class GenericEntityPrint
{
//[Test]
public void Print()
{
// Using the test framework for things it was not meant for is my passion
//
// Its pretty fucked that just occasionally running this manually is so much easier than setting up a porper
// source generator.
var i = 8;
IEnumerable<string> Generics(int n, bool nullable, bool forceIncludeNumber = false)
{
for (var j = 1; j <= n; j++)
{
var jStr = n == 1 && !forceIncludeNumber ? string.Empty : j.ToString();
yield return $"T{jStr}{(nullable ? "?" : string.Empty)}";
}
}
IEnumerable<string> PartiallyNullableGenerics(int n, int notNullCount)
{
bool nullable;
for (var j = 1; j <= n; j++)
{
nullable = j > notNullCount;
var jStr = n == 1 ? string.Empty : j.ToString();
yield return $"T{jStr}{(nullable ? "?" : string.Empty)}";
}
}
var structs = new StringBuilder();
var constraints = new StringBuilder();
var fields = new StringBuilder();
var parameters = new StringBuilder();
var asserts = new StringBuilder();
var assignments = new StringBuilder();
var tupleParameters = new StringBuilder();
var tupleAccess = new StringBuilder();
var entityAccess = new StringBuilder();
var selfAccess = new StringBuilder();
var entityNumberedAccess = new StringBuilder();
var defaults = new StringBuilder();
var compOperators = new StringBuilder();
var deConstructorParameters = new StringBuilder();
var deConstructorAccess = new StringBuilder();
var partialTupleCasts = new StringBuilder();
var partialEntityCasts = new StringBuilder();
var entitySubCast = new StringBuilder();
var castRegion = new StringBuilder();
for (var j = 1; j <= i; j++)
{
constraints.Clear();
fields.Clear();
parameters.Clear();
asserts.Clear();
assignments.Clear();
tupleParameters.Clear();
tupleAccess.Clear();
entityAccess.Clear();
selfAccess.Clear();
entityNumberedAccess.Clear();
defaults.Clear();
compOperators.Clear();
deConstructorParameters.Clear();
deConstructorAccess.Clear();
partialTupleCasts.Clear();
partialEntityCasts.Clear();
entitySubCast.Clear();
castRegion.Clear();
var generics = string.Join(", ", Generics(j, false));
var nullableGenerics = string.Join(", ", Generics(j, true));
for (var k = 1; k <= j; k++)
{
var kStr = j == 1 ? string.Empty : k.ToString();
fields.AppendLine($" public T{kStr} Comp{kStr};");
constraints.Append($"where T{kStr} : IComponent? ");
parameters.Append($", T{kStr} comp{kStr}");
asserts.AppendLine($" DebugTools.AssertOwner(owner, comp{kStr});");
assignments.AppendLine($" Comp{kStr} = comp{kStr};");
tupleParameters.Append($", T{kStr} Comp{kStr}");
tupleAccess.Append($", tuple.Comp{kStr}");
var suffix = (j >= 2 && k == 1) ? string.Empty : kStr;
var prefix = (j >= 2 && k == 2) ? "1" : string.Empty;
entityAccess.Append($"{prefix}, ent.Comp{suffix}");
selfAccess.Append($"{prefix}, Comp{suffix}");
entityNumberedAccess.Append($", ent.Comp{kStr}");
defaults.Append(", default");
compOperators.AppendLine($$"""
public static implicit operator T{{kStr}}(Entity<{{generics}}> ent)
{
return ent.Comp{{kStr}};
}
""");
deConstructorParameters.Append($", out T{kStr} comp{kStr}");
deConstructorAccess.AppendLine($" comp{kStr} = Comp{kStr};");
if (k == j)
continue;
// Cast a (EntityUid, T1) tuple to an Entity<T1, T2?>
// We could also casts for going from a (Uid, T2) tuple to a Entity<T1?, T2> but once we get to 4 or
// more components there are just too many combinations and I CBF writing the code to generate all those.
var partiallyNullableGenerics = string.Join(", ", PartiallyNullableGenerics(j, k));
var defaultArgs = string.Concat(Enumerable.Repeat(", default", j-k));
partialTupleCasts.Append($$"""
public static implicit operator Entity<{{partiallyNullableGenerics}}>((EntityUid Owner{{tupleParameters}}) tuple)
{
return new Entity<{{partiallyNullableGenerics}}>(tuple.Owner{{tupleAccess}}{{defaultArgs}});
}
""");
// Cast an Entity<T1> to an Entity<T1, T2?>
// As with the tuple casts, we could in principle generate more here.
var subGenerics = string.Join(", ", Generics(k, false, true));
partialEntityCasts.Append($$"""
public static implicit operator Entity<{{partiallyNullableGenerics}}>(Entity<{{subGenerics}}> ent)
{
return new Entity<{{partiallyNullableGenerics}}>(ent.Owner{{entityAccess}}{{defaultArgs}});
}
""");
// Cast an Entity<T1, T2> to an Entity<T1/2>
entitySubCast.Append($$"""
public static implicit operator Entity<{{subGenerics}}>(Entity<{{generics}}> ent)
{
return new Entity<{{subGenerics}}>(ent.Owner{{entityNumberedAccess}});
}
""");
}
if (j == 2)
{
castRegion.Append($$"""
{{partialTupleCasts.ToString().TrimEnd()}}
{{partialEntityCasts.ToString().TrimEnd()}}
{{entitySubCast.ToString().TrimEnd()}}
""");
}
else if (j > 2)
{
castRegion.Append($$"""
#region Partial Tuple Casts
{{partialTupleCasts}}
#endregion
#region Partial Entity Casts
{{partialEntityCasts}}
#endregion
#region Entity Sub casts
{{entitySubCast}}
#endregion
""");
}
structs.Append($$"""
[NotYamlSerializable]
public record struct Entity<{{generics}}> : IFluentEntityUid, IAsType<EntityUid>
{{constraints.ToString().TrimEnd()}}
{
public EntityUid Owner;
{{fields.ToString().TrimEnd()}}
readonly EntityUid IFluentEntityUid.FluentOwner => Owner;
public Entity(EntityUid owner{{parameters}})
{
{{asserts}}
Owner = owner;
{{assignments.ToString().TrimEnd()}}
}
public static implicit operator Entity<{{generics}}>((EntityUid Owner{{tupleParameters}}) tuple)
{
return new Entity<{{generics}}>(tuple.Owner{{tupleAccess}});
}
public static implicit operator Entity<{{nullableGenerics}}>(EntityUid owner)
{
return new Entity<{{nullableGenerics}}>(owner{{defaults}});
}
public static implicit operator EntityUid(Entity<{{generics}}> ent)
{
return ent.Owner;
}
{{compOperators.ToString().TrimEnd()}}
public readonly void Deconstruct(out EntityUid owner{{deConstructorParameters}})
{
owner = Owner;
{{deConstructorAccess.ToString().TrimEnd()}}
}
{{castRegion}}
public override readonly int GetHashCode() => Owner.GetHashCode();
public readonly Entity<{{nullableGenerics}}> AsNullable() => new(Owner{{selfAccess}});
public readonly EntityUid AsType() => Owner;
}
""");
}
Console.WriteLine(structs);
}
}

View File

@@ -1,52 +0,0 @@
using System.Numerics;
using NUnit.Framework;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.UnitTesting.Server;
namespace Robust.UnitTesting.Shared.GameObjects
{
[TestFixture, Parallelizable]
sealed partial class EntityManagerTests
{
private static ISimulation SimulationFactory()
{
var sim = RobustServerSimulation
.NewSimulation()
.InitializeInstance();
return sim;
}
/// <summary>
/// The entity prototype can define field on the TransformComponent, just like any other component.
/// </summary>
[Test]
public void SpawnEntity_PrototypeTransform_Works()
{
var sim = SimulationFactory();
var map = sim.CreateMap().MapId;
var entMan = sim.Resolve<IEntityManager>();
var newEnt = entMan.SpawnEntity(null, new MapCoordinates(0, 0, map));
Assert.That(newEnt, Is.Not.EqualTo(EntityUid.Invalid));
}
[Test]
public void ComponentCount_Works()
{
var sim = RobustServerSimulation.NewSimulation().InitializeInstance();
var entManager = sim.Resolve<IEntityManager>();
var mapSystem = entManager.System<SharedMapSystem>();
Assert.That(entManager.Count<TransformComponent>(), Is.EqualTo(0));
var mapId = sim.CreateMap().MapId;
Assert.That(entManager.Count<TransformComponent>(), Is.EqualTo(1));
mapSystem.DeleteMap(mapId);
Assert.That(entManager.Count<TransformComponent>(), Is.EqualTo(0));
}
}
}

View File

@@ -1,521 +0,0 @@
using System.Linq;
using System.Numerics;
using NUnit.Framework;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Reflection;
using Robust.UnitTesting.Server;
namespace Robust.UnitTesting.Shared.GameObjects.Systems
{
[TestFixture, Parallelizable]
public sealed partial class AnchoredSystemTests
{
private const string Prototypes = @"
- type: entity
name: anchoredEnt
id: anchoredEnt
components:
- type: Transform
anchored: true";
private static (ISimulation, Entity<MapGridComponent> grid, MapCoordinates, SharedTransformSystem xformSys, SharedMapSystem mapSys) SimulationFactory()
{
var sim = RobustServerSimulation
.NewSimulation()
.RegisterEntitySystems(f => f.LoadExtraSystemType<MoveEventTestSystem>())
.RegisterPrototypes(f=>
{
f.LoadString(Prototypes);
})
.InitializeInstance();
var mapManager = sim.Resolve<IMapManager>();
var testMapId = sim.CreateMap().MapId;
var coords = new MapCoordinates(new Vector2(7, 7), testMapId);
// Add grid 1, as the default grid to anchor things to.
var grid = mapManager.CreateGridEntity(testMapId);
return (sim, grid, coords, sim.System<SharedTransformSystem>(), sim.System<SharedMapSystem>());
}
// An entity is anchored to the tile it is over on the target grid.
// An entity is anchored by setting the flag on the transform.
// An anchored entity is defined as an entity with the TransformComponent.Anchored flag set.
// The Anchored field is used for serialization of anchored state.
// TODO: The grid SnapGrid functions are internal, expose the query functions to content.
// PhysicsComponent.BodyType is not able to be changed by content.
/// <summary>
/// When an entity is anchored to a grid tile, it's world position is centered on the tile.
/// Otherwise you can anchor an entity to a tile without the entity actually being on top of the tile.
/// This movement will trigger a MoveEvent.
/// </summary>
[Test]
public void OnAnchored_WorldPosition_TileCenter()
{
var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory();
// can only be anchored to a tile
mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1));
var ent1 = sim.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after
// Act
sim.System<MoveEventTestSystem>().ResetCounters();
xformSys.AnchorEntity(ent1);
Assert.That(xformSys.GetWorldPosition(ent1), Is.EqualTo(new Vector2(7.5f, 7.5f))); // centered on tile
sim.System<MoveEventTestSystem>().AssertMoved(false);
}
[ComponentProtoName("AnchorOnInit")]
[Reflect(false)]
private sealed partial class AnchorOnInitComponent : Component;
[Reflect(false)]
private sealed class AnchorOnInitTestSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AnchorOnInitComponent, ComponentInit>((e, _, _) => Transform(e).Anchored = true);
}
}
[Reflect(false)]
internal sealed class MoveEventTestSystem : EntitySystem
{
[Dependency] private readonly SharedTransformSystem _transform = default!;
public override void Initialize()
{
base.Initialize();
_transform.OnGlobalMoveEvent += OnMove;
SubscribeLocalEvent<EntParentChangedMessage>(OnReparent);
}
public override void Shutdown()
{
base.Shutdown();
_transform.OnGlobalMoveEvent -= OnMove;
}
public bool FailOnMove;
public int MoveCounter;
public int ParentCounter;
private void OnMove(ref MoveEvent ev)
{
MoveCounter++;
if (FailOnMove)
Assert.Fail($"Move event was raised");
}
private void OnReparent(ref EntParentChangedMessage ev)
{
ParentCounter++;
if (FailOnMove)
Assert.Fail($"Move event was raised");
}
public void ResetCounters()
{
ParentCounter = 0;
MoveCounter = 0;
}
public void AssertMoved(bool parentChanged = true)
{
if (parentChanged)
Assert.That(ParentCounter, Is.EqualTo(1));
Assert.That(MoveCounter, Is.EqualTo(1));
}
}
/// <summary>
/// Ensures that if an entity gets added to lookups when anchored during init by some system.
/// </summary>
/// <remarks>
/// See space-wizards/RobustToolbox/issues/3444
/// </remarks>
[Test]
public void OnInitAnchored_AddedToLookup()
{
var sim = RobustServerSimulation
.NewSimulation()
.RegisterEntitySystems(f => f.LoadExtraSystemType<AnchorOnInitTestSystem>())
.RegisterComponents(f => f.RegisterClass<AnchorOnInitComponent>())
.InitializeInstance();
var mapSys = sim.System<SharedMapSystem>();
var entMan = sim.Resolve<IEntityManager>();
var mapMan = sim.Resolve<IMapManager>();
var mapId = sim.CreateMap().MapId;
var grid = mapMan.CreateGridEntity(mapId);
var coordinates = new MapCoordinates(new Vector2(7, 7), mapId);
var pos = mapSys.TileIndicesFor(grid, coordinates);
mapSys.SetTile(grid, pos, new Tile(1));
var ent1 = entMan.SpawnEntity(null, coordinates);
Assert.That(sim.Transform(ent1).Anchored, Is.False);
Assert.That(!mapSys.GetAnchoredEntities(grid, pos).Any());
entMan.DeleteEntity(ent1);
var ent2 = entMan.CreateEntityUninitialized(null, coordinates);
entMan.AddComponent<AnchorOnInitComponent>(ent2);
entMan.InitializeAndStartEntity(ent2);
Assert.That(sim.Transform(ent2).Anchored);
Assert.That(mapSys.GetAnchoredEntities(grid, pos).Count(), Is.EqualTo(1));
Assert.That(mapSys.GetAnchoredEntities(grid, pos).Contains(ent2));
}
/// <summary>
/// When an entity is anchored to a grid tile, it's parent is set to the grid.
/// </summary>
[Test]
public void OnAnchored_Parent_SetToGrid()
{
var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory();
// can only be anchored to a tile
mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1));
var traversal = sim.System<SharedGridTraversalSystem>();
traversal.Enabled = false;
var ent1 = sim.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after
// Act
xformSys.AnchorEntity(ent1);
Assert.That(sim.Transform(ent1).ParentUid, Is.EqualTo(grid.Owner));
traversal.Enabled = true;
}
/// <summary>
/// Entities cannot be anchored to empty tiles. Attempting this is a no-op, and silently fails.
/// </summary>
[Test]
public void OnAnchored_EmptyTile_Nop()
{
var (sim, grid, coords, xformSys, mapSys) = SimulationFactory();
var ent1 = sim.SpawnEntity(null, coords);
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
mapSys.SetTile(grid, tileIndices, Tile.Empty);
// Act
xformSys.AnchorEntity(ent1);
Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0));
Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(Tile.Empty));
}
/// <summary>
/// Entities can be anchored to any non-empty grid tile. A physics component is not required on either
/// the grid or the entity to anchor it.
/// </summary>
[Test]
public void OnAnchored_NonEmptyTile_Anchors()
{
var (sim, grid, coords, xformSys, mapSys) = SimulationFactory();
var ent1 = sim.SpawnEntity(null, coords);
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
mapSys.SetTile(grid, tileIndices, new Tile(1));
// Act
sim.Transform(ent1).Anchored = true;
Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).First(), Is.EqualTo(ent1));
Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.Not.EqualTo(Tile.Empty));
Assert.That(sim.HasComp<PhysicsComponent>(ent1), Is.False);
var tempQualifier = grid.Owner;
Assert.That(sim.HasComp<PhysicsComponent>(tempQualifier), Is.True);
}
/// <summary>
/// Local position of an anchored entity cannot be changed (can still change world position via parent).
/// Writing to the property is a no-op and is silently ignored.
/// Because the position cannot be changed, MoveEvents are not raised when setting the property.
/// </summary>
[Test]
public void Anchored_SetPosition_Nop()
{
var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory();
// coordinates are already tile centered to prevent snapping and MoveEvent
coordinates = coordinates.Offset(new Vector2(0.5f, 0.5f));
// can only be anchored to a tile
mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1));
var ent1 = sim.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after
sim.Transform(ent1).Anchored = true;
sim.System<MoveEventTestSystem>().FailOnMove = true;
// Act
sim.Transform(ent1).WorldPosition = new Vector2(99, 99);
sim.Transform(ent1).LocalPosition = new Vector2(99, 99);
Assert.That(xformSys.GetMapCoordinates(ent1), Is.EqualTo(coordinates));
sim.System<MoveEventTestSystem>().FailOnMove = false;
}
/// <summary>
/// Changing the parent of the entity un-anchors it.
/// </summary>
[Test]
public void Anchored_ChangeParent_Unanchors()
{
var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory();
var ent1 = sim.SpawnEntity(null, coordinates);
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
mapSys.SetTile(grid, tileIndices, new Tile(1));
xformSys.AnchorEntity(ent1);
// Act
xformSys.SetParent(ent1, mapSys.GetMap(coordinates.MapId));
Assert.That(sim.Transform(ent1).Anchored, Is.False);
Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0));
Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(new Tile(1)));
}
/// <summary>
/// Setting the parent of an anchored entity to the same parent is a no-op (it will not be un-anchored).
/// This is an specific case to the base functionality of TransformComponent, where in general setting the same
/// parent is a no-op.
/// </summary>
[Test]
public void Anchored_SetParentSame_Nop()
{
var (sim, grid, coords, xformSys, mapSys) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var ent1 = entMan.SpawnEntity(null, coords);
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
mapSys.SetTile(grid, tileIndices, new Tile(1));
sim.Transform(ent1).Anchored = true;
// Act
xformSys.SetParent(ent1, grid.Owner);
Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).First(), Is.EqualTo(ent1));
Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.Not.EqualTo(Tile.Empty));
}
/// <summary>
/// If a tile is changed to a space tile, all entities anchored to that tile are unanchored.
/// </summary>
[Test]
public void Anchored_TileToSpace_Unanchors()
{
var (sim, grid, coords, xformSys, mapSys) = SimulationFactory();
var ent1 = sim.SpawnEntity(null, coords);
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
mapSys.SetTile(grid, tileIndices, new Tile(1));
mapSys.SetTile(grid, new Vector2i(100, 100), new Tile(1)); // Prevents the grid from being deleted when the Act happens
xformSys.AnchorEntity(ent1);
// Act
mapSys.SetTile(grid, tileIndices, Tile.Empty);
Assert.That(sim.Transform(ent1).Anchored, Is.False);
Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0));
Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(Tile.Empty));
}
/// <summary>
/// Adding an anchored entity to a container un-anchors an entity. There should be no way to have an anchored entity
/// inside a container.
/// </summary>
/// <remarks>
/// The only way you can do this without changing the parent is to make the parent grid a ContainerManager, then add the anchored entity to it.
/// </remarks>
[Test]
public void Anchored_AddToContainer_Unanchors()
{
var (sim, grid, coords, xformSys, mapSys) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var ent1 = sim.SpawnEntity(null, coords);
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
mapSys.SetTile(grid, tileIndices, new Tile(1));
xformSys.AnchorEntity(ent1);
// Act
// We purposefully use the grid as container so parent stays the same, reparent will unanchor
var containerSys = entMan.System<SharedContainerSystem>();
var containerMan = entMan.AddComponent<ContainerManagerComponent>(grid);
var container = containerSys.MakeContainer<Container>(grid, "TestContainer", containerMan);
containerSys.Insert(ent1, container);
Assert.That(sim.Transform(ent1).Anchored, Is.False);
Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0));
Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(new Tile(1)));
Assert.That(container.ContainedEntities.Count, Is.EqualTo(1));
}
/// <summary>
/// Adding a physics component should poll TransformComponent.Anchored for the correct body type.
/// </summary>
[Test]
public void Anchored_AddPhysComp_IsStaticBody()
{
var (sim, grid, coords, xformSys, mapSys) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var ent1 = sim.SpawnEntity(null, coords);
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
mapSys.SetTile(grid, tileIndices, new Tile(1));
xformSys.AnchorEntity(ent1);
// Act
// assumed default body is Dynamic
var physComp = entMan.AddComponent<PhysicsComponent>(ent1);
Assert.That(physComp.BodyType, Is.EqualTo(BodyType.Static));
}
/// <summary>
/// When an entity is anchored, it's physics body type is set to <see cref="BodyType.Static"/>.
/// </summary>
[Test]
public void OnAnchored_HasPhysicsComp_IsStaticBody()
{
var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var physSystem = sim.System<SharedPhysicsSystem>();
// can only be anchored to a tile
mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1));
var ent1 = entMan.SpawnEntity(null, coordinates);
var physComp = entMan.AddComponent<PhysicsComponent>(ent1);
physSystem.SetBodyType(ent1, BodyType.Dynamic, body: physComp);
// Act
xformSys.AnchorEntity(ent1);
Assert.That(physComp.BodyType, Is.EqualTo(BodyType.Static));
}
/// <summary>
/// When an entity is unanchored, it's physics body type is set to <see cref="BodyType.Dynamic"/>.
/// </summary>
[Test]
public void OnUnanchored_HasPhysicsComp_IsDynamicBody()
{
var (sim, grid, coords, xformSys, mapSys) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var ent1 = sim.SpawnEntity(null, coords);
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
mapSys.SetTile(grid, tileIndices, new Tile(1));
var physComp = entMan.AddComponent<PhysicsComponent>(ent1);
sim.Transform(ent1).Anchored = true;
// Act
xformSys.Unanchor(ent1);
Assert.That(physComp.BodyType, Is.EqualTo(BodyType.Dynamic));
}
/// <summary>
/// If an entity with an anchored prototype is spawned in an invalid location, the entity is unanchored.
/// </summary>
[Test]
public void SpawnAnchored_EmptyTile_Unanchors()
{
var (sim, grid, coords, _, mapSys) = SimulationFactory();
// Act
var ent1 = sim.SpawnEntity("anchoredEnt", coords);
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0));
Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(Tile.Empty));
Assert.That(sim.Transform(ent1).Anchored, Is.False);
}
/// <summary>
/// If an entity is inside a container, setting Anchored silently fails.
/// </summary>
[Test]
public void OnAnchored_InContainer_Nop()
{
var (sim, grid, coords, xformSys, mapSys) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
var ent1 = sim.SpawnEntity(null, coords);
var tileIndices = mapSys.TileIndicesFor(grid, sim.Transform(ent1).Coordinates);
mapSys.SetTile(grid, tileIndices, new Tile(1));
var containerSys = entMan.System<SharedContainerSystem>();
var containerMan = entMan.AddComponent<ContainerManagerComponent>(grid);
var container = containerSys.MakeContainer<Container>(grid, "TestContainer", containerMan);
containerSys.Insert(ent1, container);
// Act
xformSys.AnchorEntity(ent1);
Assert.That(sim.Transform(ent1).Anchored, Is.False);
Assert.That(mapSys.GetAnchoredEntities(grid, tileIndices).Count(), Is.EqualTo(0));
Assert.That(mapSys.GetTileRef(grid, tileIndices).Tile, Is.EqualTo(new Tile(1)));
Assert.That(container.ContainedEntities.Count, Is.EqualTo(1));
}
/// <summary>
/// Unanchoring an unanchored entity is a no-op.
/// </summary>
[Test]
public void Unanchored_Unanchor_Nop()
{
var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory();
// can only be anchored to a tile
mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1));
var traversal = sim.System<SharedGridTraversalSystem>();
traversal.Enabled = false;
var ent1 = sim.SpawnEntity(null, coordinates); // this raises MoveEvent, subscribe after
// Act
sim.System<MoveEventTestSystem>().FailOnMove = true;
xformSys.Unanchor(ent1);
Assert.That(sim.Transform(ent1).ParentUid, Is.EqualTo(grid.Owner));
sim.System<MoveEventTestSystem>().FailOnMove = false;
traversal.Enabled = true;
}
/// <summary>
/// Unanchoring an entity should leave it parented to the grid it was anchored to.
/// </summary>
[Test]
public void Anchored_Unanchored_ParentUnchanged()
{
var (sim, grid, coordinates, xformSys, mapSys) = SimulationFactory();
var entMan = sim.Resolve<IEntityManager>();
// can only be anchored to a tile
mapSys.SetTile(grid, mapSys.TileIndicesFor(grid, coordinates), new Tile(1));
var ent1 = entMan.SpawnEntity("anchoredEnt", mapSys.MapToGrid(grid, coordinates));
xformSys.Unanchor(ent1);
Assert.That(sim.Transform(ent1).ParentUid, Is.EqualTo(grid.Owner));
}
}
}

Some files were not shown because too many files have changed in this diff Show More