using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using UnitTestSharp; using Vellum; namespace Ariadne.Vellum.UnitTests { public class ArcConverterTests : TestFixture { /// /// Returns the converters the test assembly names, which include the arc family from Ariadne.Vellum. /// public static SaveSession Session() { return SaveConverters.For(typeof(ArcConverterTests).Assembly).CreateSession(); } public static OwnedScript Owned(int progress) { return new OwnedScript { Progress = progress }; } /// /// Returns a table holding , since a save is one table. /// public static SaveNode SavedList(SaveSession session, List entities) { SaveNode returnMe = SaveNode.NewTable(); returnMe.Entries["entities"] = session.ToNode(entities, typeof(List)); return returnMe; } public class FamilyTests : TestFixture { public void TestAssembly_NamesTheFamilyWithoutAnyArcInIt() { CheckTrue(typeof(ArcConverterTests).Assembly.GetCustomAttributes() .Any(attribute => attribute.Converter == typeof(ArcConverter<,>))); } public void ArcClassWithNoConverterOfItsOwn_IsSavedAndLoaded() { SaveSession session = Session(); SaveNode node = session.ToNode(Owned(4), typeof(OwnedScript)); CheckEqual(4, ((OwnedScript)session.FromNode(node, typeof(OwnedScript))).Progress); } public void ArcClassWithoutALoader_Throws() { SaveSession session = Session(); CheckThrow(typeof(InvalidOperationException)); session.ToNode(new ArcLoaderTests.LoaderlessArc(), typeof(ArcLoaderTests.LoaderlessArc)); } } public class ConstructorTests : TestFixture { public void ClassWithoutALoader_Throws() { CheckThrow(typeof(InvalidOperationException)); new ArcConverter(); } } public class ValueTypeTests : TestFixture { public void IsTheArcClass() { CheckEqual(typeof(CountingScript), new ArcConverter().ValueType); } } public class ToNodeTests : TestFixture { public void WritesWhatTheArcRecordWrites() { SaveSession session = Session(); CountingScript script = ArcRecordTests.PausedTwice(); SaveNode node = session.ToNode(script, typeof(CountingScript)); CheckEqual(new[] { "wait" }, node.RequireEntry(ArcRecord.CursorKey).RequireItems().Select(label => label.Text).ToArray()); CheckEqual("2", node.RequireEntry(ArcRecord.FieldsKey).RequireEntry("Count").Text); CheckEqual("2", node.RequireEntry(ArcRecord.LocalsKey).RequireEntry("Run.waits").Text); } } public class TryListChildrenTests : TestFixture { public static Type[] TypesOf(ChildList children) { var returnMe = new Type[children.Count]; for (int i = 0; i < returnMe.Length; ++i) { returnMe[i] = children.TypeAt(i); } return returnMe; } public void ListsEachSavedFieldAndLocalWithItsType() { SaveSession session = Session(); SaveNode node = session.ToNode(ArcRecordTests.PausedTwice(), typeof(CountingScript)); var children = new ChildList(); CheckTrue(new ArcConverter().TryListChildren(node, children)); CheckEqual(new[] { typeof(int), typeof(string), typeof(int) }, TypesOf(children)); CheckTrue(ReferenceEquals(node.RequireEntry(ArcRecord.FieldsKey).RequireEntry("Count"), children.NodeAt(0))); CheckTrue(ReferenceEquals(node.RequireEntry(ArcRecord.LocalsKey).RequireEntry("Run.waits"), children.NodeAt(2))); } public void DerivedClass_ListsItsBaseClassPartsFirst() { SaveSession session = Session(); SaveNode node = session.ToNode(new ReliefScript { Stage = 1, Crates = 2 }, typeof(ReliefScript)); var children = new ChildList(); CheckTrue(new ArcConverter().TryListChildren(node, children)); CheckEqual(2, children.Count); CheckEqual("1", children.NodeAt(0).Text); CheckEqual("2", children.NodeAt(1).Text); } public void PartTheSaveLacks_IsLeftOut() { SaveNode node = SaveNode.NewTable(); SaveNode fields = SaveNode.NewTable(); fields.Entries["Count"] = SaveNode.OfText("4"); node.Entries[ArcRecord.FieldsKey] = fields; var children = new ChildList(); CheckTrue(new ArcConverter().TryListChildren(node, children)); CheckEqual(new[] { typeof(int) }, TypesOf(children)); } public void SaveWithNoTables_ListsNothing() { var children = new ChildList(); CheckTrue(new ArcConverter().TryListChildren(SaveNode.NewTable(), children)); CheckEqual(0, children.Count); } public void ClassWritingItsOwnSaveFields_ReturnsFalse() { SaveNode node = Session().ToNode(new HandBuiltScript { Held = 3 }, typeof(HandBuiltScript)); CheckFalse(new ArcConverter().TryListChildren(node, new ChildList())); } public void NodeThatIsNotATable_ReturnsFalse() { CheckFalse(new ArcConverter().TryListChildren(SaveNode.OfText("x"), new ChildList())); } public void FieldsThatAreNotATable_ReturnsFalse() { SaveNode node = SaveNode.NewTable(); node.Entries[ArcRecord.FieldsKey] = SaveNode.OfText("x"); CheckFalse(new ArcConverter().TryListChildren(node, new ChildList())); } public void LocalsThatAreNotATable_ReturnsFalse() { SaveNode node = SaveNode.NewTable(); node.Entries[ArcRecord.LocalsKey] = SaveNode.OfText("x"); CheckFalse(new ArcConverter().TryListChildren(node, new ChildList())); } public void LocalsOfACall_AreListedFromTheCallsTableWithTheirTypes() { SaveNode node = Session().ToNode(ForeignCallSaveTests.PausedInside(new Gates(), ticks: 2), typeof(OperatorScript)); SaveNode call = node.RequireEntry(ArcRecord.LocalsKey).RequireEntry("Run.update/"); var children = new ChildList(); CheckTrue(new ArcConverter().TryListChildren(node, children)); int waited = IndexOfNode(children, call.RequireEntry("Update.waited")); int receiver = IndexOfNode(children, call.RequireEntry("receiver")); CheckTrue(waited >= 0); CheckTrue(receiver >= 0); CheckEqual(typeof(int), children.TypeAt(waited)); CheckEqual(typeof(PlantScript), children.TypeAt(receiver)); } public void CallTheSaveLacks_IsLeftOut() { SaveNode node = SaveNode.NewTable(); node.Entries[ArcRecord.LocalsKey] = SaveNode.NewTable(); var children = new ChildList(); CheckTrue(new ArcConverter().TryListChildren(node, children)); CheckEqual(0, children.Count); } public void CallThatIsNotATable_ReturnsFalse() { SaveNode locals = SaveNode.NewTable(); locals.Entries["Run.update/"] = SaveNode.OfText("x"); SaveNode node = SaveNode.NewTable(); node.Entries[ArcRecord.LocalsKey] = locals; CheckFalse(new ArcConverter().TryListChildren(node, new ChildList())); } } public class DeepChainTests : TestFixture { public static ChainScript Chain(int length) { var head = new ChainScript { Depth = length }; ChainScript tail = head; for (int i = length - 1; i > 0; --i) { tail.Next = new ChainScript { Depth = i }; tail = tail.Next; } return head; } public void ArcsHoldingArcs_LoadPastTheNestingLimit() { SaveSession session = Session(); SaveNode node = LuaSaveFormat.Parse(LuaSaveFormat.Format( session.ToNode(Chain(200), typeof(ChainScript)))); var loaded = (ChainScript)Session().FromNode(node, typeof(ChainScript)); int length = 0; for (ChainScript link = loaded; link != null; link = link.Next) { CheckEqual(200 - length, link.Depth); length++; } CheckEqual(200, length); } public void ClassWritingItsOwnSaveFields_StillLoads() { SaveSession session = Session(); SaveNode node = session.ToNode(new HandBuiltScript { Held = 7 }, typeof(HandBuiltScript)); CheckEqual(7, ((HandBuiltScript)session.FromNode(node, typeof(HandBuiltScript))).Held); } } public static int IndexOfNode(ChildList children, SaveNode node) { for (int i = 0; i < children.Count; ++i) { if (ReferenceEquals(children.NodeAt(i), node)) { return i; } } return -1; } public class FromNodeTests : TestFixture { public void BuildsAnArcAtWhereTheSaveLeftOff() { SaveSession session = Session(); SaveNode node = session.ToNode(ArcRecordTests.PausedTwice(), typeof(CountingScript)); var resumed = (CountingScript)session.FromNode(node, typeof(CountingScript)); var gates = new Gates(); gates.Open.Add("go"); resumed.Tick(gates, TestTicks.Fresh()); CheckEqual(2, resumed.Count); CheckEqual("waited 2", resumed.Note); CheckTrue(resumed.Finished); } public void PausedScript_ReportsNoUnreadKeys() { SaveSession session = Session(); session.ReportsUnreadKeys = true; SaveNode node = session.ToNode(ArcRecordTests.PausedTwice(), typeof(CountingScript)); session.FromNode(node, typeof(CountingScript)); CheckEqual(0, session.Sandbox.Issues.Count); } public void FieldTheScriptLacks_IsReportedAsUnread() { SaveSession session = Session(); session.ReportsUnreadKeys = true; SaveNode node = session.ToNode(ArcRecordTests.PausedTwice(), typeof(CountingScript)); node.Entries[ArcRecord.FieldsKey].Entries["Removed"] = SaveNode.OfText("1"); session.FromNode(node, typeof(CountingScript)); CheckEqual(new[] { "fields: Removed" }, session.Sandbox.Issues.Select(issue => issue.Path).ToArray()); } public void LocalTheScriptLacks_Throws() { SaveNode node = SaveNode.NewTable(); SaveNode locals = SaveNode.NewTable(); locals.Entries["Run.removed"] = SaveNode.OfText("1"); node.Entries[ArcRecord.LocalsKey] = locals; CheckThrow(typeof(NotImplementedException)); Session().FromNode(node, typeof(CountingScript)); } public void ValueOfTheWrongShape_KeepsTheFieldsInitialValueAndRecordsIt() { SaveNode node = SaveNode.NewTable(); SaveNode fields = SaveNode.NewTable(); fields.Entries["Count"] = SaveNode.OfText("many"); node.Entries[ArcRecord.FieldsKey] = fields; SaveSession session = Session(); var loaded = (CountingScript)session.FromNode(node, typeof(CountingScript)); CheckEqual(0, loaded.Count); CheckEqual(1, session.Sandbox.Issues.Count); } } public class FieldOfAnotherObjectTests : TestFixture { public void ArcHeldByAnEntity_SurvivesALuaRoundTrip() { SaveSession session = Session(); var original = new Entity { Name = "Ada", Mission = Owned(5) }; SaveNode node = LuaSaveFormat.Parse(LuaSaveFormat.Format(session.ToNode(original, typeof(Entity)))); var loaded = (Entity)session.FromNode(node, typeof(Entity)); CheckEqual("Ada", loaded.Name); CheckEqual(5, loaded.Mission.Progress); } public void EntityWithNoArc_LoadsWithNone() { SaveSession session = Session(); SaveNode node = session.ToNode(new Entity { Name = "Ada" }, typeof(Entity)); var loaded = (Entity)session.FromNode(node, typeof(Entity)); CheckNull(loaded.Mission); } public void ArcSharedByTwoEntities_IsOneArcAgain() { SaveSession session = Session(); OwnedScript shared = Owned(3); var entities = new List { new Entity { Name = "Ada", Mission = shared }, new Entity { Name = "Bob", Mission = shared }, }; SaveNode node = LuaSaveFormat.Parse(LuaSaveFormat.Format(SavedList(session, entities))); var loaded = (List)session.FromNode(node.RequireEntry("entities"), typeof(List)); CheckTrue(ReferenceEquals(loaded[0].Mission, loaded[1].Mission)); CheckEqual(3, loaded[0].Mission.Progress); } public void ArcSharedByTwoEntities_IsWrittenOnce() { SaveSession session = Session(); OwnedScript shared = Owned(3); var entities = new List { new Entity { Name = "Ada", Mission = shared }, new Entity { Name = "Bob", Mission = shared }, }; string text = LuaSaveFormat.Format(SavedList(session, entities)); CheckEqual(1, text.Split(new[] { "Progress" }, StringSplitOptions.None).Length - 1); } public void EntityAndArcHoldingEachOther_AreEachOtherAgain() { SaveSession session = Session(); var original = new Entity { Name = "Ada", Mission = Owned(1) }; original.Mission.Owner = original; SaveNode node = LuaSaveFormat.Parse(LuaSaveFormat.Format(session.ToNode(original, typeof(Entity)))); var loaded = (Entity)session.FromNode(node, typeof(Entity)); CheckTrue(ReferenceEquals(loaded, loaded.Mission.Owner)); CheckEqual("Ada", loaded.Mission.Owner.Name); } public void ArcLoadedFirst_StillFindsItsEntity() { SaveSession session = Session(); var original = new Entity { Name = "Ada", Mission = Owned(1) }; original.Mission.Owner = original; SaveNode node = LuaSaveFormat.Parse(LuaSaveFormat.Format(session.ToNode(original.Mission, typeof(OwnedScript)))); var loaded = (OwnedScript)session.FromNode(node, typeof(OwnedScript)); CheckTrue(ReferenceEquals(loaded, loaded.Owner.Mission)); } } public class OneSaveFileTests : TestFixture { public void ArcsAndTheWorld_ShareTheirObjectsAfterOneSaveAndOneLoad() { var ada = new Entity { Name = "Ada", Mission = Owned(1) }; ada.Mission.Owner = ada; OwnedScript helper = Owned(2); helper.Owner = ada; var arcs = new List> { ada.Mission, helper, ArcRecordTests.PausedTwice() }; SaveNode save = SaveNode.NewTable(); SaveSession saving = Session(); save.Entries["world"] = saving.ToNode(new List { ada }, typeof(List)); save.Entries["arcs"] = saving.ToNode(arcs, typeof(List>)); SaveNode parsed = LuaSaveFormat.Parse(LuaSaveFormat.Format(save)); SaveSession loading = Session(); var loadedWorld = (List)loading.FromNode(parsed.RequireEntry("world"), typeof(List)); var loadedArcs = (List>)loading.FromNode(parsed.RequireEntry("arcs"), typeof(List>)); CheckTrue(ReferenceEquals(loadedWorld[0].Mission, loadedArcs[0])); CheckTrue(ReferenceEquals(loadedWorld[0], ((OwnedScript)loadedArcs[1]).Owner)); CheckEqual(2, ((CountingScript)loadedArcs[2]).Count); } } public class SeparateSaveTests : TestFixture { public void ArcSavedAlone_CarriesEverythingItReaches() { var cy = new Entity { Name = "Cy", Mission = Owned(3) }; var bob = new Entity { Name = "Bob", Mission = Owned(2) }; var ada = new Entity { Name = "Ada", Mission = Owned(1) }; ada.Mission.Owner = bob; bob.Mission.Owner = cy; string text = LuaSaveFormat.Format(Session().ToNode(ada.Mission, typeof(OwnedScript))); CheckTrue(text.Contains("\"Bob\"")); CheckTrue(text.Contains("\"Cy\"")); CheckFalse(text.Contains("\"Ada\"")); } public void EntitySavedInTwoSessions_LoadsAsTwoCopies() { var ada = new Entity { Name = "Ada", Mission = Owned(1) }; ada.Mission.Owner = ada; SaveNode worldSave = Session().ToNode(ada, typeof(Entity)); SaveNode arcSave = Session().ToNode(ada.Mission, typeof(OwnedScript)); var loadedWorld = (Entity)Session().FromNode(worldSave, typeof(Entity)); var loadedArc = (OwnedScript)Session().FromNode(arcSave, typeof(OwnedScript)); CheckFalse(ReferenceEquals(loadedWorld, loadedArc.Owner)); CheckFalse(ReferenceEquals(loadedWorld.Mission, loadedArc)); CheckEqual("Ada", loadedArc.Owner.Name); } } public class DeclaredAsItsBaseClassTests : TestFixture { public void ArcHeldAsItsBaseClass_IsLoadedAsItsOwnClass() { SaveSession session = Session(); CountingScript original = ArcRecordTests.PausedTwice(); SaveNode node = session.ToNode(original, typeof(Arc)); var loaded = session.FromNode(node, typeof(Arc)); CheckEqual(typeof(CountingScript), loaded.GetType()); CheckEqual(2, ((CountingScript)loaded).Count); } public void NullHeldAsItsBaseClass_StaysNull() { SaveSession session = Session(); SaveNode node = session.ToNode(null, typeof(Arc)); CheckNull(session.FromNode(node, typeof(Arc))); } public void ArcClassNoConverterNamesInASave_CantBeLoaded() { SaveSession session = Session(); SaveNode node = session.ToNode(ArcRecordTests.PausedTwice(), typeof(Arc)); node.Entries[SaveConverters.TypeKey] = SaveNode.OfText("Ariadne.Vellum.UnitTests.NoSuchScript"); CheckThrow(typeof(InvalidDataException)); session.FromNode(node, typeof(Arc)); } } } }