using Microsoft.CodeAnalysis; using UnitTestSharp; namespace Ariadne.Generator.UnitTests { /// /// Calls from one arc to a passage of another, which run on the other and keep their state in the caller. /// public class ForeignCallTests : TestFixture { public const string Generators = @" public partial class Generators : Arc { public int Updates; public void UpdateGenerators(World world) { int waited = 0; wait: while (!world.Open.Contains(""go"")) { waited++; Yield(); } Updates += waited; } protected override void Run(World world) { } } "; public const string Mission = @" public partial class Mission : Arc { public Generators Plant = new Generators(); protected override void Run(World world) { int before = world.Open.Count; update: Plant.UpdateGenerators(world); world.Open.Add(before.ToString()); } } "; public class RunTests : TestFixture { public void CallOnAnotherArc_CompilesWithoutComplaint() { GeneratorRun run = GeneratorRun.Of(Generators + Mission); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); } public void CallOnAnotherArc_RunsTheCalleesCopyWithAFrameLentToIt() { GeneratorRun run = GeneratorRun.Of(Generators + Mission); CheckTrue(run.CodeBehind.Contains( "__frame.Run____call_update_receiver.UpdateGenerators__Passage(" + "__frame.Lend(__frame.Run____call_update_frame), __time, world)")); } public void CallOnAnotherArc_MakesTheReceiverAndItsFrameWhenTheCallBegins() { GeneratorRun run = GeneratorRun.Of(Generators + Mission); CheckTrue(run.CodeBehind.Contains("if (__frame.ResumeKey == null) { " + "__frame.Run____call_update_receiver = (Plant); __frame.Run____call_update_frame = null; }")); CheckTrue(run.CodeBehind.Contains("__frame.Run____call_update_frame = (global::Generators.__Frame)" + "__frame.Run____call_update_receiver.NewFrame();")); } public void CallOnAnotherArc_KeepsTheReceiverAndTheCalleesFrameInTheCallersFrame() { GeneratorRun run = GeneratorRun.Of(Generators + Mission); CheckTrue(run.CodeBehind.Contains("public global::Generators Run____call_update_receiver;")); CheckTrue(run.CodeBehind.Contains("public global::Generators.__Frame Run____call_update_frame;")); } public void CallOnAnotherArc_SavesTheReceiverAmongTheCallersLocals() { GeneratorRun run = GeneratorRun.Of(Generators + Mission); CheckTrue(run.CodeBehind.Contains("case \"Run.update/receiver\":")); CheckTrue(run.CodeBehind.Contains("writer.Local(\"receiver\", " + "this.Run____call_update_receiver);")); CheckTrue(run.CodeBehind.Contains("__frame.SaveLocal(\"Run.update/receiver\", scope);")); } public void CallOnAnotherArc_SavesTheCalleesLocalsInATableNamedForTheCall() { GeneratorRun run = GeneratorRun.Of(Generators + Mission); CheckTrue(run.CodeBehind.Contains( "global::Ariadne.IArcWriter scope = writer.Scope(\"Run.update/\");")); CheckTrue(run.CodeBehind.Contains("UpdateGenerators__LocalsToSave(" + "__frame.Lend(__frame.Run____call_update_frame), depth + 1, scope);")); } public void CallOnAnotherArc_NeedsNoRoutingOfTheCalleesKeys() { GeneratorRun run = GeneratorRun.Of(Generators + Mission); CheckFalse(run.CodeBehind.Contains("KnowsLocal")); CheckFalse(run.CodeBehind.Contains("Substring")); CheckFalse(run.CodeBehind.Contains("Prefixed")); } public void CallOnAnotherArc_LoadsTheCalleesLocalsIntoAFrameMadeFromTheSavedReceiver() { GeneratorRun run = GeneratorRun.Of(Generators + Mission); CheckTrue(run.CodeBehind.Contains( "if (reader.TryScope(\"Run.update/\", out global::Ariadne.IArcReader scope))")); CheckTrue(run.CodeBehind.Contains("scope.TryLocal(\"receiver\", out ")); CheckTrue(run.CodeBehind.Contains("if (this.Run____call_update_receiver != null)")); CheckTrue(run.CodeBehind.Contains("this.Run____call_update_frame.LoadLocals(scope);")); } public void CallOnAnotherArc_ListsTheCalleesFrameInATableNamedForTheCall() { GeneratorRun run = GeneratorRun.Of(Generators + Mission); CheckTrue(run.CodeBehind.Contains( "global::Ariadne.IArcPartLister scope = lister.Scope(\"Run.update/\", " + "typeof(global::Generators.__Frame));")); CheckTrue(run.CodeBehind.Contains("global::Generators.__Frame.ListParts(scope);")); CheckTrue(run.CodeBehind.Contains("scope.Local(\"receiver\");")); } public void CalleeWithNothingToSave_StillSavesTheReceiver() { GeneratorRun run = GeneratorRun.Of(@" public partial class Quiet : Arc { public void Hop(World world) { wait: while (!world.Open.Contains(""go"")) { Yield(); } } protected override void Run(World world) { } } public partial class Hopper : Arc { public Quiet Other = new Quiet(); protected override void Run(World world) { jump: Other.Hop(world); } }"); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains("__frame.SaveLocal(\"Run.jump/receiver\", scope);")); CheckFalse(run.CodeBehind.Contains("Hop__LocalsToSave")); } public void ArcsThatCallEachOthersPassages_AreAnalyzedOnceEach() { GeneratorRun run = GeneratorRun.Of(@" public partial class Ping : Arc { public Pong Partner = new Pong(); public void Serve(World world) { wait: while (!world.Open.Contains(""ping"")) { Yield(); } } public void Rally(World world) { Partner.Return(world); } protected override void Run(World world) { Rally(world); } } public partial class Pong : Arc { public Ping Partner; public void Return(World world) { wait: while (!world.Open.Contains(""pong"")) { Yield(); } } protected override void Run(World world) { Partner.Serve(world); } }"); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); } public void ArcFromAnotherAssembly_IsCalledLikeAnyObject() { MetadataReference missions = GeneratorRun.CompileLibrary("Missions", @" public abstract class Remote : Arc { public void Act(string context) { } }"); GeneratorRun run = GeneratorRun.Of(missions, @" public partial class Local : Arc { public Remote Other; protected override void Run(string context) { wait: while (context.Length == 0) { Yield(); } Other.Act(context); } }"); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); CheckFalse(run.CodeBehind.Contains("NewFrame")); } public void MethodThatDoesntPause_IsCalledLikeAnyObject() { GeneratorRun run = GeneratorRun.Of(@" public partial class Plain : Arc { public void Note(World world) { world.Open.Add(""noted""); } protected override void Run(World world) { } } public partial class Local : Arc { public Plain Other = new Plain(); protected override void Run(World world) { Other.Note(world); } }"); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); CheckFalse(run.CodeBehind.Contains("NewFrame")); } } public class MisplacementTests : TestFixture { public void CallInACondition_IsReported() { GeneratorRun run = GeneratorRun.Of(Generators + @" public partial class Mission : Arc { public Generators Plant = new Generators(); protected override void Run(World world) { if (world.Open.Count == 0 && Plant.UpdateGenerators(world)) { world.Open.Add(""x""); } } }"); CheckTrue(run.Reported.Contains("ARI0004:Plant.UpdateGenerators(world)")); } public void CallInsideAnExpression_IsLiftedOutOfIt() { GeneratorRun run = GeneratorRun.Of(@" public partial class Counter : Arc { public int Count() { wait: while (!Open()) { Yield(); } return 3; } private bool Open() { return true; } protected override void Run(World world) { } } public partial class Mission : Arc { public Counter Plant = new Counter(); protected override void Run(World world) { world.Open.Add((Plant.Count() + 1).ToString()); } }"); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); } public void CallOnTheArcsOwnPassageThroughThis_StaysAnOrdinaryCall() { GeneratorRun run = GeneratorRun.Of(@" public partial class Own : Arc { private void Act(World world) { wait: while (!world.Open.Contains(""wait"")) { Yield(); } } protected override void Run(World world) { this.Act(world); } }"); CheckEqual(0, run.Reported.Count); CheckFalse(run.CodeBehind.Contains("NewFrame")); } } public class AccessTests : TestFixture { private static GeneratorRun Run(string modifiers) { return GeneratorRun.Of(@" public partial class Worker : Arc { " + modifiers + @" void Act(World world) { wait: while (!world.Open.Contains(""wait"")) { Yield(); } } protected override void Run(World world) { Act(world); } }"); } public void PublicPassage_HasAPublicCopy() { GeneratorRun run = Run("public"); CheckTrue(run.CodeBehind.Contains( "public bool Act__Passage(__Frame __frame, global::Ariadne.TickTime __time, World world)")); } public void InternalPassage_HasAnInternalCopy() { CheckTrue(Run("internal").CodeBehind.Contains("internal bool Act__Passage(")); } public void ProtectedPassage_HasAProtectedCopy() { CheckTrue(Run("protected").CodeBehind.Contains("protected bool Act__Passage(")); } public void ProtectedInternalPassage_HasAProtectedInternalCopy() { CheckTrue(Run("protected internal").CodeBehind.Contains("protected internal bool Act__Passage(")); } public void PrivateProtectedPassage_HasAPrivateProtectedCopy() { CheckTrue(Run("private protected").CodeBehind.Contains("private protected bool Act__Passage(")); } public void PrivatePassage_HasAPrivateCopy() { CheckTrue(Run("private").CodeBehind.Contains("private bool Act__Passage(")); } public void PassageWithLocalsToSave_HasASavingMethodAsVisibleAsTheCopy() { GeneratorRun run = GeneratorRun.Of(Generators); CheckTrue(run.CodeBehind.Contains("public void UpdateGenerators__LocalsToSave(__Frame __frame, int " + "depth, global::Ariadne.IArcWriter writer)")); } } public class ListPartsTests : TestFixture { public void FrameOfAClassDerivedFromAGeneratedClass_ListsTheBaseFramesPartsFirst() { GeneratorRun run = GeneratorRun.Of(@" public abstract partial class Base : Arc { public int Reward; } public partial class Derived : Base { protected override void Run(World world) { int count = 0; wait: while (!world.Open.Contains(""wait"")) { count++; Yield(); } } }"); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains("global::Base.__Frame.ListParts(lister);\r\n")); CheckTrue(run.CodeBehind.Contains("lister.Local(\"Run.count\");")); } public void FrameOfAClassWithNoGeneratedBase_ListsOnlyItsOwnParts() { GeneratorRun run = GeneratorRun.Of(Generators); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains( "public static void ListParts(global::Ariadne.IArcPartLister lister)")); CheckFalse(run.CodeBehind.Contains("Frame.ListParts(lister);")); } } } }