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]
internal 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 ]
";
///
/// This test checks that deserializing an entity with some component that has the
/// works as intended. Previously the attribute would cause the entity
/// prototype to **always** append it's contents to the loaded entity, effectively causing
/// the data-field to grow each time a map was loaded and saved.
///
[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 parent1 = default;
Entity parent2 = default;
Entity parent3 = default;
Entity child1 = default;
Entity child2 = default;
Entity child3 = default;
var path = new ResPath($"{nameof(TestAlwaysPushSerialization)}.yml");
await server.WaitPost(() =>
{
server.System().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();
var map = server.System();
Assert.That(loader.TrySaveMap(mapId, path));
// Delete the entities
await server.WaitPost(() => map.DeleteMap(mapId));
Assert.That(entMan.Count(), 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(), 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}));
}
}