using System.Linq; using UnitTestSharp; namespace Ariadne.Generator.UnitTests { public class WaitLoweringTests : TestFixture { public static string[] IdsOf(GeneratorRun run) { return run.Reported.Select(each => each.Substring(0, each.IndexOf(':'))).ToArray(); } public class WriteTests : TestFixture { public void Wait_StoresADeadlineThenTestsItAtTheResumePoint() { GeneratorRun run = GeneratorRun.Of(@" public partial class Waiting : Arc { protected override void Run(World world) { first: Wait(System.TimeSpan.FromSeconds(1)); } }"); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains( "__frame.Run____wait1 = __time.DeadlineAfter(System.TimeSpan.FromSeconds(1));")); CheckTrue(run.CodeBehind.Contains("case \"first\": goto first__resume;")); CheckTrue(run.CodeBehind.Contains( "if (__time.GameTime < __frame.Run____wait1) { __frame.Sleep(__frame.Run____wait1); " + "__frame.Mark(\"first\"); return false; }")); } public void Wait_PutsTheResumeLabelBetweenTheDeadlineAndTheTest() { GeneratorRun run = GeneratorRun.Of(@" public partial class Waiting : Arc { protected override void Run(World world) { first: Wait(System.TimeSpan.FromSeconds(1)); } }"); int deadline = run.CodeBehind.IndexOf("__frame.Run____wait1 = "); int resume = run.CodeBehind.IndexOf("first__resume:"); int test = run.CodeBehind.IndexOf("if (__time.GameTime < "); CheckTrue(deadline < resume); CheckTrue(resume < test); } public void Wait_KeepsItsDeadlineAmongTheSavedLocals() { GeneratorRun run = GeneratorRun.Of(@" public partial class Waiting : Arc { protected override void Run(World world) { first: Wait(System.TimeSpan.FromSeconds(1)); } }"); CheckTrue(run.CodeBehind.Contains("lister.Local(\"Run.__wait1\");")); } public void TwoWaits_KeepTheirOwnDeadlines() { GeneratorRun run = GeneratorRun.Of(@" public partial class Waiting : Arc { protected override void Run(World world) { first: Wait(System.TimeSpan.FromSeconds(1)); second: Wait(System.TimeSpan.FromSeconds(2)); } }"); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains("lister.Local(\"Run.__wait1\");")); CheckTrue(run.CodeBehind.Contains("lister.Local(\"Run.__wait2\");")); } public void SpanHeldInALocal_IsNotKeptAcrossTheWait() { GeneratorRun run = GeneratorRun.Of(@" public partial class Waiting : Arc { protected override void Run(World world) { System.TimeSpan span = System.TimeSpan.FromSeconds(1); first: Wait(span); } }"); CheckEqual(0, run.Compiled.Count); CheckFalse(run.CodeBehind.Contains("\"Run.span\"")); } public void SpanGivenByName_IsTheDeadlinesSpan() { GeneratorRun run = GeneratorRun.Of(@" public partial class Waiting : Arc { protected override void Run(World world) { first: Wait(span: System.TimeSpan.FromSeconds(1)); } }"); CheckEqual(0, run.Reported.Count); CheckTrue(run.CodeBehind.Contains( "__frame.Run____wait1 = __time.DeadlineAfter(System.TimeSpan.FromSeconds(1));")); } public void PassageCallInTheSpan_RunsBeforeTheDeadlineIsSet() { GeneratorRun run = GeneratorRun.Of(@" public partial class Waiting : Arc { private System.TimeSpan Delay(World world) { ask: Yield(); return System.TimeSpan.FromSeconds(1); } protected override void Run(World world) { first: Wait(Delay(world)); } }"); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); int call = run.CodeBehind.IndexOf("Delay__Passage(__frame, __time, world, __result: out "); int deadline = run.CodeBehind.IndexOf("__frame.Run____wait1 = __time.DeadlineAfter(__frame."); int test = run.CodeBehind.IndexOf("if (__time.GameTime < __frame.Run____wait1)"); CheckTrue(call >= 0); CheckTrue(call < deadline); CheckTrue(deadline < test); } public void PassageCallInTheSpan_KeysTheWaitAfterTheCall() { GeneratorRun run = GeneratorRun.Of(@" public partial class Waiting : Arc { private System.TimeSpan Delay(World world) { ask: Yield(); return System.TimeSpan.FromSeconds(1); } protected override void Run(World world) { first: Wait(Delay(world)); } }"); CheckTrue(run.CodeBehind.Contains("case \"first\":")); CheckTrue(run.CodeBehind.Contains("case \"first#1\": goto first__1__resume;")); } public void LocalReadInASpanWithACall_IsKeptAcrossTheCall() { GeneratorRun run = GeneratorRun.Of(@" public partial class Waiting : Arc { private System.TimeSpan Delay(World world) { ask: Yield(); return System.TimeSpan.FromSeconds(1); } protected override void Run(World world) { System.TimeSpan extra = System.TimeSpan.FromSeconds(1); first: Wait(Delay(world) + extra); } }"); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains("lister.Local(\"Run.extra\");")); } public void PassageCallToAnotherArcInTheSpan_RunsBeforeTheDeadlineIsSet() { GeneratorRun run = GeneratorRun.Of(@" public partial class Counter : Arc { public System.TimeSpan Delay(World world) { ask: Yield(); return System.TimeSpan.FromSeconds(1); } protected override void Run(World world) { } } public partial class Waiting : Arc { public Counter Other = new Counter(); protected override void Run(World world) { first: Wait(Other.Delay(world)); } }"); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); int call = run.CodeBehind.IndexOf("Delay__Passage("); int deadline = run.CodeBehind.IndexOf("__frame.Run____wait1 = __time.DeadlineAfter(__frame."); int test = run.CodeBehind.IndexOf("if (__time.GameTime < __frame.Run____wait1)"); CheckTrue(call >= 0); CheckTrue(call < deadline); CheckTrue(deadline < test); } public void MethodThatWaits_IsAPassage() { GeneratorRun run = GeneratorRun.Of(@" public partial class Waiting : Arc { private void Rest(World world) { rest: Wait(System.TimeSpan.FromSeconds(1)); } protected override void Run(World world) { first: Rest(world); } }"); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains("Rest__Passage(__Frame __frame")); } } public class ReportTests : TestFixture { public void WaitWithoutAStepLabel_IsReported() { GeneratorRun run = GeneratorRun.Of(@" public partial class Waiting : Arc { protected override void Run(World world) { Wait(System.TimeSpan.FromSeconds(1)); } }"); CheckEqual(new[] { "ARI0002" }, IdsOf(run)); } public void WaitInAForeach_IsReported() { GeneratorRun run = GeneratorRun.Of(@" public partial class Waiting : Arc { protected override void Run(World world) { first: foreach (string each in world.Open) { Wait(System.TimeSpan.FromSeconds(1)); } } }"); CheckEqual(new[] { "ARI0031" }, IdsOf(run)); CheckTrue(run.Messages[0].Contains("a foreach loop")); } public void WaitInALambda_IsReported() { GeneratorRun run = GeneratorRun.Of(@" public partial class Waiting : Arc { protected override void Run(World world) { first: System.Action later = () => Wait(System.TimeSpan.FromSeconds(1)); later(); } }"); CheckEqual(new[] { "ARI0031" }, IdsOf(run)); CheckTrue(run.Messages[0].Contains("a lambda or local function")); } public void WaitInATry_IsReported() { GeneratorRun run = GeneratorRun.Of(@" public partial class Waiting : Arc { protected override void Run(World world) { first: try { Wait(System.TimeSpan.FromSeconds(1)); } finally { } } }"); CheckEqual(new[] { "ARI0031" }, IdsOf(run)); } public void WaitInAMemberThatIsNotAPassage_IsReported() { GeneratorRun run = GeneratorRun.Of(@" public partial class Waiting : Arc { public int Count { get { Wait(System.TimeSpan.FromSeconds(1)); return 1; } } protected override void Run(World world) { first: Yield(); } }"); CheckEqual(new[] { "ARI0031" }, IdsOf(run)); } public void PauseInsideALambdaInTheSpan_IsReported() { GeneratorRun run = GeneratorRun.Of(@" public partial class Waiting : Arc { private System.TimeSpan Pick(System.Func choose) { return choose(); } protected override void Run(World world) { first: Wait(Pick(() => { Yield(); return System.TimeSpan.Zero; })); } }"); CheckEqual(new[] { "ARI0003" }, IdsOf(run)); } } } }