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; /// /// 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. /// [TestFixture] internal 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(); var loader = server.System(); var mapMan = server.ResolveDependency(); var tileMan = server.ResolveDependency(); 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(TestTileDefId); MapId mapId = default; Entity map = default; Entity ent = default; Entity 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(uid).EntityPaused, Is.EqualTo(expected)); } void AssertPreInit(EntityUid uid, bool expected = true) { Assert.That(entMan!.GetComponent(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(), Is.EqualTo(3)); await server.WaitPost(() => mapSys.DeleteMap(mapId)); Assert.That(entMan.Count(), 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(), Is.EqualTo(1)); grid = Find(nameof(grid), entMan); AssertPaused(grid); AssertPreInit(grid); await server.WaitPost(() => mapSys.DeleteMap(mapId)); Assert.That(entMan.Count(), 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(), 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(), 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(), Is.EqualTo(1)); grid = Find(nameof(grid), entMan); AssertPaused(grid, false); AssertPreInit(grid, false); await server.WaitPost(() => mapSys.DeleteMap(mapId)); Assert.That(entMan.Count(), 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(), 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(), 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(), Is.EqualTo(1)); grid = Find(nameof(grid), entMan); AssertPaused(grid); AssertPreInit(grid, false); await server.WaitPost(() => mapSys.DeleteMap(mapId)); Assert.That(entMan.Count(), 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(), 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(), 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(), Is.EqualTo(1)); grid = Find(nameof(grid), entMan); AssertPaused(grid); AssertPreInit(grid); await server.WaitPost(() => mapSys.DeleteMap(mapId)); Assert.That(entMan.Count(), 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(), 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(), 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(), Is.EqualTo(1)); grid = Find(nameof(grid), entMan); AssertPaused(grid, false); AssertPreInit(grid, false); await server.WaitPost(() => mapSys.DeleteMap(mapId)); Assert.That(entMan.Count(), 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(), 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(), Is.EqualTo(0)); } }