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);"));
}
}
}
}