using System; using System.Collections.Generic; using System.IO; using System.Linq; using Ariadne.UnitTests; using UnitTestSharp; using Vellum; namespace Ariadne.Vellum.UnitTests { /// /// Saves arcs paused inside passages that call themselves, directly or through others. /// public class RecursionSaveTests : TestFixture { public static ScriptType Reloaded(ScriptType script) where ScriptType : Arc { SaveSession session = ArcConverterTests.Session(); return (ScriptType)session.FromNode(session.ToNode(script, typeof(ScriptType)), typeof(ScriptType)); } public static Countdown PausedAtTheThirdLevel(ArcTests.Gates gates) { var script = new Countdown(); script.Tick(gates, TestTicks.Fresh()); foreach (string gate in new[] { "go1", "go2" }) { gates.Open.Add(gate); script.Tick(gates, TestTicks.Fresh()); } return script; } public class ToNodeTests : TestFixture { public void EachLevel_IsATableInsideTheLevelAbove() { SaveSession session = ArcConverterTests.Session(); Countdown paused = PausedAtTheThirdLevel(new ArcTests.Gates()); SaveNode locals = session.ToNode(paused, typeof(Countdown)).RequireEntry(ArcRecord.LocalsKey); SaveNode first = locals.RequireEntry("Run.start/"); SaveNode second = first.RequireEntry("Descend.deeper/"); SaveNode third = second.RequireEntry("Descend.deeper/"); CheckEqual(new[] { "Descend.deeper/", "Descend.level", "Descend.mine" }, first.Entries.Keys.OrderBy(key => key, StringComparer.Ordinal).ToArray()); CheckEqual("1", first.RequireEntry("Descend.level").Text); CheckEqual("20", second.RequireEntry("Descend.mine").Text); CheckEqual("30", third.RequireEntry("Descend.mine").Text); } public void RunInACycle_KeepsItsLocalsAtTheTopAndEachLapInsideTheOneAbove() { SaveSession session = ArcConverterTests.Session(); var gates = new ArcTests.Gates(); var script = new Relapsing(); script.Tick(gates, TestTicks.Fresh()); gates.Open.Add("lap0"); script.Tick(gates, TestTicks.Fresh()); SaveNode locals = session.ToNode(script, typeof(Relapsing)).RequireEntry(ArcRecord.LocalsKey); CheckEqual("0", locals.RequireEntry("Run.lap").Text); CheckEqual("1", locals.RequireEntry("Run.start/").RequireEntry("Again.back/") .RequireEntry("Run.lap").Text); } public void LuaSave_NestsTheLevels() { string save = ArcRecordTests.Saved(PausedAtTheThirdLevel(new ArcTests.Gates())); CheckTrue(save.Contains("[\"Run.start/\"] = {")); CheckEqual(2, save.Split(new[] { "[\"Descend.deeper/\"] = {" }, StringSplitOptions.None).Length - 1); } } public class FromNodeTests : TestFixture { public void CountdownReloadedAtEachPause_RunsToTheEnd() { var gates = new ArcTests.Gates(); Countdown script = PausedAtTheThirdLevel(gates); script = Reloaded(script); gates.Open.Add("go3"); script.Tick(gates, TestTicks.Fresh()); CheckEqual("30;20;10;", script.Log); CheckTrue(script.Finished); } public void CountdownReloadedFromALuaFile_RunsToTheEnd() { var gates = new ArcTests.Gates(); var script = ArcRecordTests.Load(ArcRecordTests.Saved(PausedAtTheThirdLevel(gates))); gates.Open.Add("go3"); script.Tick(gates, TestTicks.Fresh()); CheckEqual("30;20;10;", script.Log); } public void CountdownReloadedAndSavedAgainBeforeTicking_KeepsEveryLevel() { var gates = new ArcTests.Gates(); Countdown script = Reloaded(Reloaded(PausedAtTheThirdLevel(gates))); gates.Open.Add("go3"); script.Tick(gates, TestTicks.Fresh()); CheckEqual("30;20;10;", script.Log); } public void MutualPassages_ReloadedAtEachPause_RunToTheEnd() { var gates = new ArcTests.Gates(); var script = new Rally(); script.Tick(gates, TestTicks.Fresh()); foreach (string gate in new[] { "ping3", "pong2", "ping1", "pong0" }) { script = Reloaded(script); gates.Open.Add(gate); script.Tick(gates, TestTicks.Fresh()); } CheckEqual("ping3;pong2;ping1;pong0;", script.Log); CheckTrue(script.Finished); } public void GenericPassageWithItsOwnTypeArguments_ReloadedAtEachPause_RunsToTheEnd() { var gates = new ArcTests.Gates(); var script = new Layering(); script.Tick(gates, TestTicks.Fresh()); foreach (string gate in new[] { "layer2", "layer1", "layer0" }) { script = Reloaded(script); gates.Open.Add(gate); script.Tick(gates, TestTicks.Fresh()); } CheckEqual("box@0;box@1;box@2;", script.Log); } public void GenericPassageWithItsTypeArgumentsSwapped_ReloadedAtEachPause_RunsToTheEnd() { var gates = new ArcTests.Gates(); var script = new Trading(); script.Tick(gates, TestTicks.Fresh()); foreach (string gate in new[] { "trade2", "trade1", "trade0" }) { script = Reloaded(script); gates.Open.Add(gate); script.Tick(gates, TestTicks.Fresh()); } CheckEqual("1/one;one/1;1/one;", script.Log); } public void GenericPassageWrappingItsTypeArgument_ReloadedAtEachPause_RunsToTheEnd() { var gates = new ArcTests.Gates(); var script = new Widening(); script.Tick(gates, TestTicks.Fresh()); foreach (string gate in new[] { "wrap2", "wrap1", "wrap0" }) { script = Reloaded(script); gates.Open.Add(gate); script.Tick(gates, TestTicks.Fresh()); } CheckEqual("0@2;1@1;2@0;up2;up1;up0;", script.Log); CheckTrue(script.Finished); } public void GenericPassageWrappingItsTypeArgument_ReloadedFromALuaFile_RunsToTheEnd() { var gates = new ArcTests.Gates(); var script = new Widening(); script.Tick(gates, TestTicks.Fresh()); foreach (string gate in new[] { "wrap2", "wrap1" }) { gates.Open.Add(gate); script.Tick(gates, TestTicks.Fresh()); } script = ArcRecordTests.Load(ArcRecordTests.Saved(script)); gates.Open.Add("wrap0"); script.Tick(gates, TestTicks.Fresh()); CheckEqual("0@2;1@1;2@0;up2;up1;up0;", script.Log); } public void SaveNamingATypeNothingRegisteredBuilds_Throws() { var gates = new ArcTests.Gates(); var script = new Widening(); script.Tick(gates, TestTicks.Fresh()); gates.Open.Add("wrap2"); script.Tick(gates, TestTicks.Fresh()); SaveSession session = ArcConverterTests.Session(); SaveNode node = session.ToNode(script, typeof(Widening)); node.RequireEntry(ArcRecord.LocalsKey).RequireEntry("Run.start/").RequireEntry("Wrap.deeper/") .RequireEntry(ArcRecord.TypesKey).Items[0] = SaveNode.OfText("System.Diagnostics.Process"); CheckThrow(typeof(InvalidDataException)); ArcConverterTests.Session().FromNode(node, typeof(Widening)); } public void DerivedClassRunInACycleOverridingABaseClassRunInACycle_ReloadedAtEachPause_RunsToTheEnd() { var gates = new ArcTests.Gates(); var script = new CyclingChild(); script.Tick(gates, TestTicks.Fresh()); foreach (string gate in new[] { "push0", "push1" }) { script = Reloaded(script); gates.Open.Add(gate); script.Tick(gates, TestTicks.Fresh()); } CheckEqual("push0;push1;", script.Log); CheckTrue(script.Finished); } public void RunCalledFromAPassageItCalls_ReloadedAtEachPause_RunsToTheEnd() { var gates = new ArcTests.Gates(); var script = new Relapsing(); script.Tick(gates, TestTicks.Fresh()); foreach (string gate in new[] { "lap0", "lap1", "lap2" }) { script = Reloaded(script); gates.Open.Add(gate); script.Tick(gates, TestTicks.Fresh()); } CheckEqual("lap0;lap1;lap2;end2;end1;end0;", script.Log); CheckTrue(script.Finished); } public void RunCallingItself_ReloadedFromALuaFileAtEachPause_RunsToTheEnd() { var gates = new ArcTests.Gates(); var script = new Repeating(); script.Tick(gates, TestTicks.Fresh()); foreach (string gate in new[] { "round0", "round1", "round2" }) { script = ArcRecordTests.Load(ArcRecordTests.Saved(script)); gates.Open.Add(gate); script.Tick(gates, TestTicks.Fresh()); } CheckEqual("round0;round1;round2;done2;done1;done0;", script.Log); CheckTrue(script.Finished); } public void RunCallingItself_ReloadedBeforeItsFirstTick_RunsToTheEnd() { var gates = new ArcTests.Gates(); Repeating script = Reloaded(new Repeating()); foreach (string gate in new[] { "round0", "round1", "round2" }) { gates.Open.Add(gate); script.Tick(gates, TestTicks.Fresh()); } CheckEqual("round0;round1;round2;done2;done1;done0;", script.Log); } public void RecursivePassageOfAnotherArc_ReloadedAtEachPause_RunsOnThatArc() { var gates = new ArcTests.Gates(); var gardener = new Orchardist(); gardener.Tick(gates, TestTicks.Fresh()); foreach (string gate in new[] { "ring2", "ring1" }) { gardener = Reloaded(gardener); gates.Open.Add(gate); gardener.Tick(gates, TestTicks.Fresh()); } CheckEqual("1;2;", gardener.Trees.Log); CheckTrue(gardener.Finished); } public void PassageOfABaseClassCallingItself_ReloadedAtEachPause_RunsToTheEnd() { var gates = new ArcTests.Gates(); var script = new Shaft(); script.Tick(gates, TestTicks.Fresh()); foreach (string gate in new[] { "dig1", "dig2" }) { script = Reloaded(script); gates.Open.Add(gate); script.Tick(gates, TestTicks.Fresh()); } CheckEqual("2;1;", script.Log); } public void ServesHandedBetweenTwoArcs_ReloadedAtEachPause_RunOnTheArcsReloadedWithIt() { var gates = new ArcTests.Gates(); var first = new Volley(); first.Partner = new Volley { Partner = first }; first.Tick(gates, TestTicks.Fresh()); foreach (string gate in new[] { "hit3", "hit2", "hit1" }) { first = Reloaded(first); gates.Open.Add(gate); first.Tick(gates, TestTicks.Fresh()); } CheckTrue(ReferenceEquals(first, first.Partner.Partner)); CheckEqual("serve3;serve1;", first.Log); CheckEqual("serve2;", first.Partner.Log); CheckTrue(first.Finished); } public void SaveNestedFarDeeperThanTheStackAllows_IsRejectedAsInvalidData() { SaveNode locals = SaveNode.NewTable(); SaveNode level = locals; for (int depth = 0; depth < 50000; ++depth) { SaveNode next = SaveNode.NewTable(); level.Entries[depth == 0 ? "Run.start/" : "Descend.deeper/"] = next; level = next; } SaveNode node = SaveNode.NewTable(); node.Entries[ArcRecord.LocalsKey] = locals; CheckThrow(typeof(InvalidDataException)); ArcConverterTests.Session().FromNode(node, typeof(Countdown)); } } public class TypeArgumentTests : TestFixture { public void WrittenAtTheTopLevel_ReadBackAsTheSameTypes() { var record = new ArcRecord(ArcConverterTests.Session()); record.TypeArguments(typeof(int), typeof(List)); Type[] read; CheckTrue(record.TryTypeArguments(out read)); CheckEqual(new[] { typeof(int), typeof(List) }, read); } public void NoneWritten_ReadsAsAbsent() { Type[] read; CheckFalse(new ArcRecord(ArcConverterTests.Session()).TryTypeArguments(out read)); CheckNull(read); } public void WrittenInACall_AreNotAKeyOfThatCallsLocals() { var record = new ArcRecord(ArcConverterTests.Session()); record.Scope("Run.start/").TypeArguments(typeof(int)); IArcReader scope; CheckTrue(record.TryScope("Run.start/", out scope)); CheckEqual(0, scope.LocalKeys.Count()); Type[] read; CheckTrue(scope.TryTypeArguments(out read)); CheckEqual(new[] { typeof(int) }, read); } public void TypeASaveCantName_Throws() { var record = new ArcRecord(ArcConverterTests.Session()); CheckThrow(typeof(NotSupportedException)); record.TypeArguments(typeof(System.Diagnostics.Process)); } } public class PolymorphicListingTests : TestFixture { [IgnoreTest] private static SaveNode SaveAtTheThirdLevel(SaveSession session) { var gates = new ArcTests.Gates(); var script = new Widening(); script.Tick(gates, TestTicks.Fresh()); foreach (string gate in new[] { "wrap2", "wrap1" }) { gates.Open.Add(gate); script.Tick(gates, TestTicks.Fresh()); } return session.ToNode(script, typeof(Widening)); } public void ChildrenOfASaveWithNewFrameTypesPerLevel_AreListedWithTheTypeOfEachLevel() { SaveConverters converters = SaveConverters.For(typeof(ArcConverterTests).Assembly); SaveNode node = SaveAtTheThirdLevel(converters.CreateSession()); var children = new ChildList(); CheckTrue(new ArcConverter().TryListChildren(node, children, converters)); var types = new List(); for (int i = 0; i < children.Count; ++i) { types.Add(children.TypeAt(i)); } CheckTrue(types.Contains(typeof(List))); CheckTrue(types.Contains(typeof(List>))); } public void WithoutConverters_ReturnsFalse() { SaveNode node = SaveAtTheThirdLevel(ArcConverterTests.Session()); CheckFalse(new ArcConverter().TryListChildren(node, new ChildList())); } public void TypesThatDontFitTheFrame_ReturnFalse() { SaveConverters converters = SaveConverters.For(typeof(ArcConverterTests).Assembly); SaveNode node = SaveAtTheThirdLevel(converters.CreateSession()); node.RequireEntry(ArcRecord.LocalsKey).RequireEntry("Run.start/").RequireEntry("Wrap.deeper/") .RequireEntry(ArcRecord.TypesKey).Items.Add(SaveNode.OfText("int")); CheckFalse(new ArcConverter().TryListChildren(node, new ChildList(), converters)); } public void TypesThatNothingRegisteredBuilds_ReturnFalse() { SaveConverters converters = SaveConverters.For(typeof(ArcConverterTests).Assembly); SaveNode node = SaveAtTheThirdLevel(converters.CreateSession()); node.RequireEntry(ArcRecord.LocalsKey).RequireEntry("Run.start/").RequireEntry("Wrap.deeper/") .RequireEntry(ArcRecord.TypesKey).Items[0] = SaveNode.OfText("System.Diagnostics.Process"); CheckFalse(new ArcConverter().TryListChildren(node, new ChildList(), converters)); } public void CallEntryThatIsNotATable_ReturnsFalse() { SaveConverters converters = SaveConverters.For(typeof(ArcConverterTests).Assembly); SaveNode node = SaveAtTheThirdLevel(converters.CreateSession()); node.RequireEntry(ArcRecord.LocalsKey).RequireEntry("Run.start/").Entries["Wrap.deeper/"] = SaveNode.OfText("not a table"); CheckFalse(new ArcConverter().TryListChildren(node, new ChildList(), converters)); } public void TypesEntryThatIsNotAList_ReturnsFalse() { SaveConverters converters = SaveConverters.For(typeof(ArcConverterTests).Assembly); SaveNode node = SaveAtTheThirdLevel(converters.CreateSession()); node.RequireEntry(ArcRecord.LocalsKey).RequireEntry("Run.start/").RequireEntry("Wrap.deeper/") .Entries[ArcRecord.TypesKey] = SaveNode.OfText("int"); CheckFalse(new ArcConverter().TryListChildren(node, new ChildList(), converters)); } } public class ConverterTests : TestFixture { public void ArcsThatRunEachOthersPassages_ListTheirPartsWithoutEnd() { new ArcConverter(); new ArcConverter(); new ArcConverter(); } public void ChildrenOfARecursiveSave_AreListedForEveryLevel() { SaveSession session = ArcConverterTests.Session(); SaveNode node = session.ToNode(PausedAtTheThirdLevel(new ArcTests.Gates()), typeof(Countdown)); var children = new ChildList(); CheckTrue(new ArcConverter().TryListChildren(node, children)); CheckEqual(7, children.Count); } } } }