using System.Linq; using UnitTestSharp; namespace Ariadne.Generator.UnitTests { public class ConditionWaitLoweringTests : TestFixture { public const string Limit = "System.TimeSpan.FromSeconds(2)"; /// /// Returns a script class whose Run is , with two latches and a trigger it can wait on. /// public static string ScriptWith(string body, string members = "") { return @" public partial class Waiting : Arc { private static readonly Latch A = new Latch(); private static readonly Latch B = new Latch(); private static readonly Trigger T = new Trigger(); " + members + @" protected override void Run(World world) { " + body + @" } }"; } public class WriteTests : TestFixture { public void Wait_BuildsAWaitOnConditions() { GeneratorRun run = GeneratorRun.Of(ScriptWith(" first:\n Wait(A);")); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains( "public global::Ariadne.TriggerWait Run____triggers1 = " + "new global::Ariadne.TriggerWait(1, onConditions: true);")); } public void Wait_BeginsTheWaitThenTestsItAtTheResumePoint() { GeneratorRun run = GeneratorRun.Of(ScriptWith(" first:\n Wait(A);")); CheckTrue(run.CodeBehind.Contains( "__frame.Run____triggers1.Begin(__frame, requiresAll: false); __frame.Run____triggers1.Add(A);")); CheckTrue(run.CodeBehind.Contains( "if (!__frame.Run____triggers1.Complete) { __frame.Suspend(); __frame.Mark(\"first\"); " + "return false; }")); } public void Wait_OnTheInverse_AddsTheInverse() { GeneratorRun run = GeneratorRun.Of(ScriptWith(" first:\n Wait(!A);")); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains("__frame.Run____triggers1.Add(!A);")); } public void Wait_DiscardingTheValue_FinishesTheWaitAfterTheTest() { GeneratorRun run = GeneratorRun.Of(ScriptWith(" first:\n Wait(A);")); CheckTrue(run.CodeBehind.Contains("return false; } __frame.Run____triggers1.Finish();")); CheckFalse(run.CodeBehind.Contains(".Result")); } public void Wait_KeepsItsWaitAmongTheSavedLocals() { GeneratorRun run = GeneratorRun.Of(ScriptWith(" first:\n Wait(A);")); CheckTrue(run.CodeBehind.Contains("lister.Local(\"Run.__triggers1\");")); CheckTrue(run.CodeBehind.Contains("this.Run____triggers1.Attach(slot);")); } public void WaitOnALatchAndWaitOnATrigger_EachBuildTheirOwnKindOfWait() { GeneratorRun run = GeneratorRun.Of(ScriptWith( " first:\n Wait(A);\n second:\n Wait(T);")); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains( "Run____triggers1 = new global::Ariadne.TriggerWait(1, onConditions: true);")); CheckTrue(run.CodeBehind.Contains("Run____triggers2 = new global::Ariadne.TriggerWait(1);")); CheckTrue(run.CodeBehind.Contains("Run____triggers1.Finish();")); CheckFalse(run.CodeBehind.Contains("Run____triggers2.Finish();")); } public void Wait_DeclaringALocal_TakesTheResultFromFinishing() { GeneratorRun run = GeneratorRun.Of(ScriptWith( " first:\n Condition got = Wait(A, timeout: " + Limit + ");\n" + " world.Open.Add(got == null ? \"none\" : \"a\");")); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains("got = __frame.Run____triggers1.Finish();")); CheckTrue(run.CodeBehind.IndexOf("Run____triggers1.Expire()") < run.CodeBehind.IndexOf("got = __frame.Run____triggers1.Finish();")); } public void Wait_AssigningAVariable_TakesTheResultFromFinishing() { GeneratorRun run = GeneratorRun.Of(ScriptWith( " Condition got = null;\n first:\n got = Wait(!A, timeout: " + Limit + ");\n" + " world.Open.Add(got == null ? \"none\" : \"a\");")); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains("got = __frame.Run____triggers1.Finish();")); } public void WaitReturnedFromAPassage_IsThePassagesResult() { GeneratorRun run = GeneratorRun.Of(ScriptWith( " outer:\n Condition picked = Pick();", members: " private Condition Pick()\n {\n inner:\n return Wait(A, timeout: " + Limit + ");\n }\n")); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains("__result = __frame.Pick____triggers1.Finish();")); CheckTrue(run.CodeBehind.Contains("__frame.Pick____triggers1.Expire();")); } public void WaitAny_AddsEveryConditionButNotTheTimeoutAndDoesNotRequireAll() { GeneratorRun run = GeneratorRun.Of(ScriptWith( " first:\n WaitAny(timeout: " + Limit + ", A, !B);")); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains("new global::Ariadne.TriggerWait(2, onConditions: true);")); CheckTrue(run.CodeBehind.Contains( "__frame.Run____triggers1.Begin(__frame, requiresAll: false); " + "__frame.Run____triggers1.Add(A); __frame.Run____triggers1.Add(!B);")); CheckTrue(run.CodeBehind.Contains("Run____triggers1.Expire();")); } public void WaitAll_RequiresAllAndTakesTheResultFromFinishing() { GeneratorRun run = GeneratorRun.Of(ScriptWith( " first:\n Condition last = WaitAll(A, B);\n" + " world.Open.Add(last == B ? \"b last\" : \"a last\");")); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains("Run____triggers1.Begin(__frame, requiresAll: true);")); CheckTrue(run.CodeBehind.Contains("last = __frame.Run____triggers1.Finish();")); } public void WaitAllWithATimeout_AddsEveryConditionButNotTheTimeout() { GeneratorRun run = GeneratorRun.Of(ScriptWith( " first:\n WaitAll(" + Limit + ", A, B);")); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains( "__frame.Run____triggers1.Begin(__frame, requiresAll: true); " + "__frame.Run____triggers1.Add(A); __frame.Run____triggers1.Add(B);")); CheckFalse(run.CodeBehind.Contains(".Add(System.TimeSpan")); } public void TimedWait_KeepsItsDeadlineAndSleepsTheArcTillIt() { GeneratorRun run = GeneratorRun.Of(ScriptWith( " first:\n Wait(A, timeout: " + Limit + ");")); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); CheckTrue(run.CodeBehind.Contains("__frame.Run____wait1 = __time.DeadlineAfter(" + Limit + ");")); CheckTrue(run.CodeBehind.Contains("__frame.Sleep(__frame.Run____wait1); __frame.Suspend();")); } } public class DiagnosticTests : TestFixture { [IgnoreTest] public GeneratorRun RunOf(string body) { return GeneratorRun.Of(ScriptWith(body)); } public void WaitAnyOfATriggerAndALatch_IsReported() { GeneratorRun run = RunOf(" first:\n WaitAny(T, A);"); CheckEqual(new[] { "ARI0034:WaitAny(T, A)" }, run.Reported); CheckTrue(run.Messages.Single().Contains("not both")); } public void WaitAllOfALatchAndATriggerWithATimeout_IsReported() { GeneratorRun run = RunOf(" first:\n WaitAll(timeout: " + Limit + ", A, T);"); CheckEqual(new[] { "ARI0034:WaitAll(timeout: " + Limit + ", A, T)" }, run.Reported); CheckTrue(run.Messages.Single().Contains("not both")); } public void WaitAnyOfValuesTypedAsWaitable_IsReported() { GeneratorRun run = RunOf(" Waitable one = A;\n first:\n WaitAny(one);"); CheckEqual(new[] { "ARI0034:WaitAny(one)" }, run.Reported); CheckTrue(run.Messages.Single().Contains("not both")); } public void ConditionsPassedAsAnArray_AreReported() { GeneratorRun run = RunOf(" Latch[] both = { A, B };\n first:\n WaitAny(both);"); CheckEqual(new[] { "ARI0034:WaitAny(both)" }, run.Reported); CheckTrue(run.Messages.Single().Contains("Pass the conditions one by one")); } public void MoreConditionsThanAWaitHolds_AreReported() { string conditions = string.Join(", ", Enumerable.Repeat("A", 33)); GeneratorRun run = RunOf(" first:\n WaitAll(" + conditions + ");"); CheckEqual(1, run.Reported.Count); CheckTrue(run.Messages.Single().Contains("at most 32 conditions")); } public void WaitAnyOfNothing_IsAmbiguousToTheCompiler() { GeneratorRun run = RunOf(" first:\n WaitAny();"); CheckEqual(0, run.Reported.Count); CheckTrue(run.Compiled.Count > 0); CheckTrue(run.Compiled.All(error => error.StartsWith("CS0121:", System.StringComparison.Ordinal))); } } } }