using System.Linq; using UnitTestSharp; namespace Ariadne.Generator.UnitTests { public class GenericPassageTests : TestFixture { public static string ProblemsOf(GeneratorRun run) { return string.Join(" | ", run.Reported.Concat(run.CompiledAt)); } public class FrameTests : TestFixture { public void GenericPassage_GetsAFrameGenericOverItsTypeParameters() { GeneratorRun run = GeneratorRun.Of(@" public partial class Mission : Arc { private void Wait(World world, X first, Y second) { X held = first; List seen = new List(); wait: while (!world.Open.Contains(""go"")) { seen.Add(second); Yield(); } held.ToString(); } protected override void Run(World world) { first: Wait(world, 1, ""a""); } }"); CheckEqual("", ProblemsOf(run)); CheckTrue(run.CodeBehind.Contains("public partial class Wait__Frame : global::Ariadne.Frame")); CheckTrue(run.CodeBehind.Contains("public X Wait__held;")); CheckTrue(run.CodeBehind.Contains("public global::System.Collections.Generic.List Wait__seen;")); } public void GenericPassage_KeepsItsLocalsOutOfTheArcsFrame() { GeneratorRun run = GeneratorRun.Of(@" public partial class Mission : Arc { private void Wait(World world, X item) { X held = item; wait: while (!world.Open.Contains(""go"")) { held.ToString(); Yield(); } } protected override void Run(World world) { first: Wait(world, 1); } }"); CheckEqual("", ProblemsOf(run)); CheckEqual(1, run.CodeBehind.Split(new[] { "public X Wait__held;" }, System.StringSplitOptions.None).Length - 1); } public void ConstraintsOfThePassage_AreCopiedToItsFrameAndCopy() { GeneratorRun run = GeneratorRun.Of(@" public partial class Mission : Arc { private void Wait(World world, X first, Y second) where X : class, new() where Y : struct { X held = first; Y other = second; wait: while (!world.Open.Contains(""go"")) { held.ToString(); other.ToString(); Yield(); } } protected override void Run(World world) { first: Wait(world, new object(), 3); } }"); CheckEqual("", ProblemsOf(run)); CheckTrue(run.CodeBehind.Contains("public partial class Wait__Frame : global::Ariadne.Frame " + "where X : class, new() where Y : struct")); CheckTrue(run.CodeBehind.Contains("Wait__Passage(Wait__Frame __frame, ")); CheckTrue(run.CodeBehind.Contains("where X : class, new() where Y : struct")); } public void GenericPassageWithNothingToSave_NeedsNoSavingOfItsFrame() { GeneratorRun run = GeneratorRun.Of(@" public partial class Mission : Arc { private void Wait(World world) { wait: while (!world.Open.Contains(""go"")) { Yield(); } } protected override void Run(World world) { first: Wait(world); } }"); CheckEqual("", ProblemsOf(run)); CheckTrue(run.CodeBehind.Contains("public partial class Wait__Frame : global::Ariadne.Frame")); } } public class CallTests : TestFixture { public void EachCallSite_KeepsAFrameTypedWithItsOwnTypeArguments() { GeneratorRun run = GeneratorRun.Of(@" public partial class Mission : Arc { private void Wait(World world, X item) { X held = item; wait: while (!world.Open.Contains(""go"")) { held.ToString(); Yield(); } } protected override void Run(World world) { numbers: Wait(world, 1); words: Wait(world, ""a""); } }"); CheckEqual("", ProblemsOf(run)); CheckTrue(run.CodeBehind.Contains( "public global::Mission.Wait__Frame Run____call_numbers_frame;")); CheckTrue(run.CodeBehind.Contains( "public global::Mission.Wait__Frame Run____call_words_frame;")); CheckTrue(run.CodeBehind.Contains( "Wait__Passage(__frame.Lend(__frame.Run____call_numbers_frame)")); CheckTrue(run.CodeBehind.Contains( "Wait__Passage(__frame.Lend(__frame.Run____call_words_frame)")); } public void ResumedArgument_IsDefaultOfTheArgumentAsConstructed() { GeneratorRun run = GeneratorRun.Of(@" public partial class Mission : Arc { private void Wait(World world, X item) { wait: while (!world.Open.Contains(""go"")) { Yield(); } } protected override void Run(World world) { first: Wait(world, 1.5); } }"); CheckEqual("", ProblemsOf(run)); CheckTrue(run.CodeBehind.Contains("default(double)")); } public void ExplicitTypeArguments_AreUsedAsGiven() { GeneratorRun run = GeneratorRun.Of(@" public partial class Mission : Arc { private void Wait(World world, X item) { wait: while (!world.Open.Contains(""go"")) { Yield(); } } protected override void Run(World world) { first: Wait(world, 1); } }"); CheckEqual("", ProblemsOf(run)); CheckTrue(run.CodeBehind.Contains("Wait__Passage(")); } public void CallFromAGenericScript_UsesTheScriptsOwnTypeParameter() { GeneratorRun run = GeneratorRun.Of(@" public partial class Mission : Arc { public U Source; private void Wait(World world, X item) { X held = item; wait: while (!world.Open.Contains(""go"")) { held.ToString(); Yield(); } } protected override void Run(World world) { first: Wait(world, Source); } }"); CheckEqual("", ProblemsOf(run)); CheckTrue(run.CodeBehind.Contains("public global::Mission.Wait__Frame Run____call_first_frame;")); } public void ValueOfTheTypeParameter_IsHandedBackAsConstructedAtTheCall() { GeneratorRun run = GeneratorRun.Of(@" public partial class Mission : Arc { public int Got; private X Ask(World world, X fallback) { X kept = fallback; ask: while (!world.Open.Contains(""go"")) { kept.ToString(); Yield(); } return kept; } protected override void Run(World world) { first: Got = Ask(world, 3) + 1; } }"); CheckEqual("", ProblemsOf(run)); } public void GenericPassageInAnInheritedClass_IsCalledLikeAnyOther() { GeneratorRun run = GeneratorRun.Of(@" public abstract partial class Steps : Arc { protected void Wait(World world, X item) { X held = item; wait: while (!world.Open.Contains(""go"")) { held.ToString(); Yield(); } } } public partial class Mission : Steps { protected override void Run(World world) { first: Wait(world, 1); } }"); CheckEqual("", ProblemsOf(run)); CheckTrue(run.CodeBehind.Contains("global::Steps.Wait__Frame")); } } public class NestedCallTests : TestFixture { public void GenericPassageCallingAnOrdinaryPassage_Compiles() { GeneratorRun run = GeneratorRun.Of(@" public partial class Mission : Arc { private void Inner(World world) { int count = 0; wait: while (!world.Open.Contains(""go"")) { ++count; Yield(); } } private void Outer(World world, X item) { X held = item; first: Inner(world); held.ToString(); } protected override void Run(World world) { start: Outer(world, 1); } }"); CheckEqual("", ProblemsOf(run)); CheckTrue(run.CodeBehind.Contains("private bool Outer__Passage(Outer__Frame __frame, " + "__Frame __arcFrame, ")); CheckTrue(run.CodeBehind.Contains("int __depth = __arcFrame.Depth; bool __done = " + "Inner__Passage(__frame.Lend(__arcFrame), __time, world); __arcFrame.Depth = __depth;")); } public void GenericPassageCallingAnotherGenericPassage_KeepsTheInnerFrameInItsOwn() { GeneratorRun run = GeneratorRun.Of(@" public partial class Mission : Arc { private void Inner(World world, Y item) { Y deep = item; wait: while (!world.Open.Contains(""go"")) { deep.ToString(); Yield(); } } private void Outer(World world, X item) { X held = item; first: Inner(world, new List { held }); } protected override void Run(World world) { start: Outer(world, 1); } }"); CheckEqual("", ProblemsOf(run)); CheckTrue(run.CodeBehind.Contains("public global::Mission.Inner__Frame<" + "global::System.Collections.Generic.List> Outer____call_first_frame;")); CheckTrue(run.CodeBehind.Contains("Inner__Passage>(" + "__frame.Lend(__frame.Outer____call_first_frame), __arcFrame, ")); CheckFalse(run.CodeBehind.Contains("public global::Mission.Inner__Frame<" + "global::System.Collections.Generic.List> Run")); } public void LocalsToSave_KeepTheArcsOwnKeysApartFromTheGenericPassagesBehindItsCallSite() { GeneratorRun run = GeneratorRun.Of(@" public partial class Mission : Arc { private void Inner(World world) { int count = 0; wait: while (!world.Open.Contains(""go"")) { ++count; Yield(); } } private void Outer(World world, X item) { X held = item; first: Inner(world); held.ToString(); } protected override void Run(World world) { start: Outer(world, 1); } }"); CheckEqual("", ProblemsOf(run)); CheckTrue(run.CodeBehind.Contains("Inner__LocalsToSave(__arcFrame, depth + 1, arcWriter);")); CheckTrue(run.CodeBehind.Contains("Outer__LocalsToSave(" + "__frame.Lend(__frame.Run____call_start_frame), __frame, depth + 1, scope, writer);")); } public void GenericPassageCallingAPassageOfAnotherArc_KeepsTheReceiverInItsOwnFrame() { GeneratorRun run = GeneratorRun.Of(@" public partial class Plant : Arc { public void Grow(World world) { wait: while (!world.Open.Contains(""go"")) { Yield(); } } protected override void Run(World world) { } } public partial class Mission : Arc { public Plant Plant; private void Tend(World world, X item) { X held = item; grow: Plant.Grow(world); held.ToString(); } protected override void Run(World world) { start: Tend(world, 1); } }"); CheckEqual("", ProblemsOf(run)); CheckTrue(run.CodeBehind.Contains("public global::Plant Tend____call_grow_receiver;")); CheckFalse(run.CodeBehind.Contains("Run____call_start_receiver")); } } public class OtherArcTests : TestFixture { public void GenericPassageCalledOnAnotherArc_KeepsAGenericFrameBesideTheArcFrame() { GeneratorRun run = GeneratorRun.Of(@" public partial class Plant : Arc { public void Grow(World world, X seed) { X kept = seed; wait: while (!world.Open.Contains(""go"")) { kept.ToString(); Yield(); } } protected override void Run(World world) { } } public partial class Mission : Arc { public Plant Plant; protected override void Run(World world) { first: Plant.Grow(world, 1); } }"); CheckEqual("", ProblemsOf(run)); CheckTrue(run.CodeBehind.Contains("public global::Plant.__Frame Run____call_first_frame;")); CheckTrue(run.CodeBehind.Contains( "public global::Plant.Grow__Frame Run____call_first_ownedFrame;")); CheckTrue(run.CodeBehind.Contains("Run____call_first_receiver.Grow__Passage(" + "__frame.Lend(__frame.Run____call_first_ownedFrame), " + "__frame.Lend(__frame.Run____call_first_frame), __time, ")); CheckTrue(run.CodeBehind.Contains("Grow__LocalsToSave(__frame.Lend(" + "__frame.Run____call_first_ownedFrame), __frame.Lend(__frame.Run____call_first_frame), " + "depth + 1, scope, scope);")); } public void GenericPassageOfAConstructedGenericArc_IsTypedAsConstructed() { GeneratorRun run = GeneratorRun.Of(@" public partial class Plant : Arc { public T Last; public void Grow(World world, T seed, X extra) { T kept = seed; X more = extra; wait: while (!world.Open.Contains(""go"")) { kept.ToString(); more.ToString(); Yield(); } } protected override void Run(World world) { } } public partial class Mission : Arc { public Plant Plant; protected override void Run(World world) { first: Plant.Grow(world, ""a"", 2); } }"); CheckEqual("", ProblemsOf(run)); CheckTrue(run.CodeBehind.Contains( "public global::Plant.Grow__Frame Run____call_first_ownedFrame;")); } public void CallOnAnotherArcInsideAGenericPassage_ToAGenericPassage_Compiles() { GeneratorRun run = GeneratorRun.Of(@" public partial class Plant : Arc { public void Grow(World world, X seed) { X kept = seed; wait: while (!world.Open.Contains(""go"")) { kept.ToString(); Yield(); } } protected override void Run(World world) { } } public partial class Mission : Arc { public Plant Plant; private void Tend(World world, Y item) { Y held = item; grow: Plant.Grow(world, new List { held }); } protected override void Run(World world) { start: Tend(world, 1); } }"); CheckEqual("", ProblemsOf(run)); CheckTrue(run.CodeBehind.Contains( "public global::Plant.Grow__Frame> " + "Tend____call_grow_ownedFrame;")); } } public class ParameterTests : TestFixture { public void OutAndRefParametersOfTheTypeParameter_Compile() { GeneratorRun run = GeneratorRun.Of(@" public partial class Mission : Arc { public int Total; private void Swap(World world, ref X left, out X right, X start) { X first = start; right = left; wait: while (!world.Open.Contains(""go"")) { Yield(); } right = first; left = first; } protected override void Run(World world) { int left = 1; int right; first: Swap(world, ref left, out right, 5); Total = left + right; } }"); CheckEqual("", ProblemsOf(run)); } public void OutVariableOfTheTypeParameter_Compiles() { GeneratorRun run = GeneratorRun.Of(@" public partial class Mission : Arc { public string Got; private void Make(World world, X start, out X made) { X first = start; wait: while (!world.Open.Contains(""go"")) { Yield(); } made = first; } protected override void Run(World world) { first: Make(world, ""s"", out string made); Got = made; } }"); CheckEqual("", ProblemsOf(run)); } } } }