using System; using System.Threading; using Amphora; using UnitTestSharp; namespace Ariadne.UnitTests { public class ConditionWaitScriptTests : TestFixture { public class RunTests : TestFixture { private LatchWorld world; private ExceptionSandbox sandbox; private ArcScheduler scheduler; private int frame; [IgnoreTest] public void Tick() { scheduler.Tick(world, ArcSchedulerTests.At(sandbox, frame * 0.016)); ++frame; } public override void TestSetup() { world = new LatchWorld(); sandbox = TestTicks.NewSandbox(); scheduler = new ArcScheduler(); frame = 0; } public void Wait_ConditionAlreadySet_FlowsThrough() { world.Door.Set(); var script = new SingleLatchScript(); scheduler.Add(script); Tick(); CheckEqual(new[] { "start", "door" }, world.Log.ToArray()); CheckTrue(script.Finished); CheckEqual(0, scheduler.Count); } public void Wait_ConditionClear_RunsToTheWaitAndSuspends() { var script = new SingleLatchScript(); scheduler.Add(script); Tick(); CheckEqual(new[] { "start" }, world.Log.ToArray()); CheckEqual(new[] { "first" }, script.Cursor); CheckEqual(1, scheduler.SuspendedCount); CheckFalse(script.Finished); } public void Wait_KeepsWaitingUntilTheConditionIsSet() { scheduler.Add(new SingleLatchScript()); for (int i = 0; i < 20; ++i) { Tick(); } CheckEqual(new[] { "start" }, world.Log.ToArray()); CheckEqual(1, scheduler.SuspendedCount); } public void Wait_ResumesOnTheTickAfterTheSet() { var script = new SingleLatchScript(); scheduler.Add(script); Tick(); world.Door.Set(); CheckEqual(new[] { "start" }, world.Log.ToArray()); Tick(); CheckEqual(new[] { "start", "door" }, world.Log.ToArray()); CheckTrue(script.Finished); CheckEqual(0, scheduler.Count); } public void Wait_IgnoresAnotherConditionBeingSet() { scheduler.Add(new SingleLatchScript()); Tick(); world.Alarm.Set(); Tick(); CheckEqual(new[] { "start" }, world.Log.ToArray()); } public void Wait_ConditionClearedBeforeTheArcRuns_WaitsAgain() { scheduler.Add(new SingleLatchScript()); Tick(); world.Door.Set(); world.Door.Clear(); Tick(); CheckEqual(new[] { "start" }, world.Log.ToArray()); CheckEqual(1, scheduler.SuspendedCount); } public void Wait_ConditionSetAgainAfterBeingCleared_Resumes() { scheduler.Add(new SingleLatchScript()); Tick(); world.Door.Set(); world.Door.Clear(); Tick(); world.Door.Set(); Tick(); CheckEqual(new[] { "start", "door" }, world.Log.ToArray()); } public void Wait_ManyArcsOnOneLatch_AllResumeFromOneSet() { scheduler.Add(new SingleLatchScript()); scheduler.Add(new SingleLatchScript()); scheduler.Add(new SingleLatchScript()); Tick(); world.Door.Set(); Tick(); CheckEqual(new[] { "start", "start", "start", "door", "door", "door" }, world.Log.ToArray()); CheckEqual(0, scheduler.Count); } public void Wait_RemovedArc_DoesNotResume() { var script = new SingleLatchScript(); scheduler.Add(script); Tick(); scheduler.Remove(script); world.Door.Set(); Tick(); CheckEqual(new[] { "start" }, world.Log.ToArray()); } public void Wait_RemovedAndAddedAgain_StillResumes() { var script = new SingleLatchScript(); scheduler.Add(script); Tick(); scheduler.Remove(script); scheduler.Add(script); world.Door.Set(); Tick(); CheckEqual(new[] { "start", "door" }, world.Log.ToArray()); } public void Wait_InALoop_FlowsThroughWhileTheConditionStaysSet() { world.Door.Set(); var script = new LoopedLatchScript(); scheduler.Add(script); Tick(); CheckEqual(new[] { "door 1", "door 2", "door 3" }, world.Log.ToArray()); CheckTrue(script.Finished); } public void Wait_PassedAfterAWake_LeavesTheArcAloneWhenTheConditionChangesLater() { scheduler.Add(new LatchThenSleepScript()); Tick(); world.Door.Set(); Tick(); CheckEqual(1, scheduler.SleepingCount); world.Door.Clear(); world.Door.Set(); CheckEqual(1, scheduler.SleepingCount); } public void Wait_PassedWithoutPausing_LeavesTheArcAloneWhenTheConditionChangesLater() { world.Door.Set(); scheduler.Add(new LatchThenSleepScript()); Tick(); CheckEqual(1, scheduler.SleepingCount); world.Door.Clear(); world.Door.Set(); CheckEqual(1, scheduler.SleepingCount); } public void WaitForClear_ConditionAlreadyClear_FlowsThrough() { scheduler.Add(new ClearLatchScript()); Tick(); CheckEqual(new[] { "start", "clear" }, world.Log.ToArray()); } public void WaitForClear_ResumesWhenTheLatchIsCleared() { world.Door.Set(); scheduler.Add(new ClearLatchScript()); Tick(); CheckEqual(new[] { "start" }, world.Log.ToArray()); world.Door.Clear(); Tick(); CheckEqual(new[] { "start", "clear" }, world.Log.ToArray()); } public void WaitForClear_IgnoresTheLatchBeingSet() { world.Door.Set(); scheduler.Add(new ClearLatchScript()); Tick(); world.Door.Set(); Tick(); CheckEqual(new[] { "start" }, world.Log.ToArray()); CheckEqual(1, scheduler.SuspendedCount); } } public class RestartTests : TestFixture { private LatchWorld world; private ExceptionSandbox sandbox; private ArcScheduler scheduler; [IgnoreTest] public void TickAt(double gameSeconds) { scheduler.Tick(world, ArcSchedulerTests.At(sandbox, gameSeconds)); } public override void TestSetup() { world = new LatchWorld(); sandbox = TestTicks.NewSandbox(); scheduler = new ArcScheduler(); } public void Restart_WhileWaiting_StopsTheLatchWakingTheArcFromItsNextSleep() { var script = new RestartableLatchScript(); scheduler.Add(script); TickAt(0); script.Skip = true; scheduler.Restart(script); TickAt(0.1); CheckEqual(1, scheduler.SleepingCount); world.Door.Set(); CheckEqual(1, scheduler.SleepingCount); } public void Restart_WhileWaiting_WaitsAgainAtTheSameWait() { var script = new RestartableLatchScript(); scheduler.Add(script); TickAt(0); scheduler.Restart(script); TickAt(0.1); world.Door.Set(); TickAt(0.2); CheckEqual(new[] { "door" }, world.Log.ToArray()); } public void Restart_WhilePausedInAPassage_StopsTheLatchWakingTheArcFromItsNextSleep() { var script = new RestartableLatchPassageScript(); scheduler.Add(script); TickAt(0); script.Skip = true; scheduler.Restart(script); TickAt(0.1); CheckEqual(1, scheduler.SleepingCount); world.Door.Set(); CheckEqual(1, scheduler.SleepingCount); } public void Restart_WhilePausedInAPassageOfAnotherArc_StopsTheLatchWakingTheArcFromItsNextSleep() { var script = new LatchRelayScript { Lookout = new LatchLookoutScript() }; scheduler.Add(script); TickAt(0); script.Skip = true; scheduler.Restart(script); TickAt(0.1); CheckEqual(1, scheduler.SleepingCount); world.Door.Set(); CheckEqual(1, scheduler.SleepingCount); } public void Restart_WhileWaiting_MakesTheLatchsSubscriptionStale() { var script = new RestartableLatchScript(); scheduler.Add(script); TickAt(0); scheduler.Restart(script); CheckFalse(world.Door.IsLiveAt(0)); } } public class ListTests : TestFixture { private LatchWorld world; private ExceptionSandbox sandbox; private ArcScheduler scheduler; private int frame; [IgnoreTest] public void Tick() { scheduler.Tick(world, ArcSchedulerTests.At(sandbox, frame * 0.016)); ++frame; } public override void TestSetup() { world = new LatchWorld(); sandbox = TestTicks.NewSandbox(); scheduler = new ArcScheduler(); frame = 0; } public void WaitAny_ResumesWhenTheFirstIsSet() { scheduler.Add(new AnyLatchScript()); Tick(); world.Door.Set(); Tick(); CheckEqual(new[] { "door" }, world.Log.ToArray()); } public void WaitAny_ResumesWhenTheSecondIsSet() { scheduler.Add(new AnyLatchScript()); Tick(); world.Alarm.Set(); Tick(); CheckEqual(new[] { "alarm" }, world.Log.ToArray()); } public void WaitAny_OneAlreadySet_FlowsThrough() { world.Alarm.Set(); scheduler.Add(new AnyLatchScript()); Tick(); CheckEqual(new[] { "alarm" }, world.Log.ToArray()); CheckEqual(0, scheduler.Count); } public void WaitAny_BothSetBeforeTheArcRuns_NamesTheFirstListedAndNotTheFirstSet() { scheduler.Add(new AnyLatchScript()); Tick(); world.Alarm.Set(); world.Door.Set(); Tick(); CheckEqual(new[] { "door" }, world.Log.ToArray()); } public void WaitAny_SetThenClearedBeforeTheArcRuns_WaitsAgain() { scheduler.Add(new AnyLatchScript()); Tick(); world.Door.Set(); world.Door.Clear(); Tick(); CheckEqual(0, world.Log.Count); world.Alarm.Set(); Tick(); CheckEqual(new[] { "alarm" }, world.Log.ToArray()); } public void WaitAny_OfALatchAndItsInverse_FlowsThrough() { scheduler.Add(new AnyOrInverseScript()); Tick(); CheckEqual(new[] { "through" }, world.Log.ToArray()); } public void WaitAny_PassedWithEitherLatch_LeavesTheArcAloneWhenBothChangeLater() { scheduler.Add(new AnyLatchThenSleepScript()); Tick(); world.Door.Set(); Tick(); CheckEqual(1, scheduler.SleepingCount); world.Alarm.Set(); world.Door.Clear(); world.Door.Set(); CheckEqual(1, scheduler.SleepingCount); } public void WaitAll_StaysAsleepUntilEveryLatchIsSet() { scheduler.Add(new AllLatchesScript()); Tick(); world.Door.Set(); Tick(); CheckEqual(0, world.Log.Count); CheckEqual(1, scheduler.SuspendedCount); world.Alarm.Set(); Tick(); CheckEqual(new[] { "all set" }, world.Log.ToArray()); } public void WaitAll_AllAlreadySet_FlowsThrough() { world.Door.Set(); world.Alarm.Set(); scheduler.Add(new AllLatchesScript()); Tick(); CheckEqual(new[] { "all set" }, world.Log.ToArray()); CheckEqual(0, scheduler.Count); } public void WaitAll_ALatchClearedBeforeTheArcRuns_WaitsAgain() { scheduler.Add(new AllLatchesScript()); Tick(); world.Door.Set(); world.Alarm.Set(); world.Door.Clear(); Tick(); CheckEqual(0, world.Log.Count); world.Door.Set(); Tick(); CheckEqual(new[] { "all set" }, world.Log.ToArray()); } public void WaitAll_WithAnInverse_NeedsTheLatchSetAndTheOtherClear() { world.Alarm.Set(); scheduler.Add(new DoorOpenAlarmClearScript()); Tick(); world.Door.Set(); Tick(); CheckEqual(0, world.Log.Count); world.Alarm.Clear(); Tick(); CheckEqual(new[] { "ready" }, world.Log.ToArray()); } public void WaitAll_OfALatchAndItsInverse_QuarantinesTheArcBeforeItWaits() { var script = new ContradictoryAllScript(); scheduler.Add(script); Tick(); CheckEqual(0, world.Log.Count); CheckEqual(0, scheduler.SuspendedCount); CheckTrue(sandbox.IsQuarantined(AriadneSites.Tick(script))); } } public class TimeoutTests : TestFixture { private LatchWorld world; private ExceptionSandbox sandbox; private ArcScheduler scheduler; [IgnoreTest] public void TickAt(double gameSeconds) { scheduler.Tick(world, ArcSchedulerTests.At(sandbox, gameSeconds)); } public override void TestSetup() { world = new LatchWorld(); sandbox = TestTicks.NewSandbox(); scheduler = new ArcScheduler(); } public void WaitAny_ResumesWithTheLatchThatIsSetInTime() { scheduler.Add(new TimedAnyLatchScript()); TickAt(0); world.Alarm.Set(); TickAt(0.5); CheckEqual(new[] { "alarm" }, world.Log.ToArray()); } public void WaitAny_ResumesWithNullWhenTheLimitPasses() { scheduler.Add(new TimedAnyLatchScript()); TickAt(0); TickAt(0.9); CheckEqual(0, world.Log.Count); TickAt(1); CheckEqual(new[] { "timeout" }, world.Log.ToArray()); } public void WaitAll_ResumesWhenEveryLatchIsSetInTime() { scheduler.Add(new TimedAllLatchesScript()); TickAt(0); world.Door.Set(); TickAt(0.3); world.Alarm.Set(); TickAt(0.5); CheckEqual(new[] { "all set" }, world.Log.ToArray()); } public void WaitAll_WithOneLatchSet_ResumesWithNullWhenTheLimitPasses() { scheduler.Add(new TimedAllLatchesScript()); TickAt(0); world.Door.Set(); TickAt(0.9); CheckEqual(0, world.Log.Count); TickAt(1); CheckEqual(new[] { "timeout" }, world.Log.ToArray()); } public void WaitAll_TimedOut_LeavesNoSubscriptionOnEitherLatch() { scheduler.Add(new TimedAllLatchesScript()); TickAt(0); TickAt(1); world.Door.Set(); world.Alarm.Set(); CheckEqual(0, world.Door.SubscriptionCount); CheckEqual(0, world.Alarm.SubscriptionCount); } public void Wait_ResumesWithTheConditionWhenItIsSetInTime() { scheduler.Add(new TimedLatchScript()); TickAt(0); world.Door.Set(); TickAt(0.5); CheckEqual(new[] { "door" }, world.Log.ToArray()); } public void Wait_ResumesWithNullWhenTheLimitPasses() { scheduler.Add(new TimedLatchScript()); TickAt(0); TickAt(0.9); CheckEqual(0, world.Log.Count); TickAt(1); CheckEqual(new[] { "timeout" }, world.Log.ToArray()); CheckEqual(0, scheduler.Count); } public void Wait_ConditionSetBeforeALateTick_BeatsTheLimit() { scheduler.Add(new TimedLatchScript()); TickAt(0); world.Door.Set(); TickAt(3); CheckEqual(new[] { "door" }, world.Log.ToArray()); } public void Wait_ConditionAlreadySet_FlowsThroughAndNamesIt() { world.Door.Set(); scheduler.Add(new TimedLatchScript()); TickAt(0); CheckEqual(new[] { "door" }, world.Log.ToArray()); CheckEqual(0, scheduler.Count); } public void Wait_InfiniteLimit_WaitsForTheConditionHoweverLongItTakes() { world.Limit = Timeout.InfiniteTimeSpan; scheduler.Add(new TimedLatchScript()); TickAt(0); TickAt(1e9); CheckEqual(0, world.Log.Count); world.Door.Set(); TickAt(1e9 + 1); CheckEqual(new[] { "door" }, world.Log.ToArray()); CheckEqual(0, scheduler.Count); } public void Wait_LimitThatHasAlreadyPassed_DoesNotPause() { world.Limit = TimeSpan.Zero; scheduler.Add(new TimedLatchScript()); TickAt(0); CheckEqual(new[] { "timeout" }, world.Log.ToArray()); CheckEqual(0, scheduler.Count); } public void Wait_LimitThatHasAlreadyPassedWithTheConditionSet_NamesIt() { world.Limit = TimeSpan.Zero; world.Door.Set(); scheduler.Add(new TimedLatchScript()); TickAt(0); CheckEqual(new[] { "door" }, world.Log.ToArray()); } public void Wait_ShortLimit_KeepsTheArcAwake() { world.Limit = TimeSpan.FromSeconds(0.05); scheduler.Add(new TimedLatchScript()); TickAt(0); CheckEqual(0, scheduler.SleepingCount); CheckEqual(0, scheduler.SuspendedCount); } public void Wait_LongLimit_SleepsTheArcTillItsDeadline() { scheduler.Add(new TimedLatchScript()); TickAt(0); CheckEqual(1, scheduler.SleepingCount); } public void Wait_SetAfterTheLimit_DoesNotWakeTheArcFromItsNextSleep() { scheduler.Add(new TimedLatchThenSleepScript()); TickAt(0); TickAt(1); world.Door.Set(); CheckEqual(1, scheduler.SleepingCount); TickAt(1.1); CheckEqual(new[] { "through" }, world.Log.ToArray()); CheckEqual(1, scheduler.SleepingCount); } public void Wait_SetAfterTheLimit_LeavesNoSubscription() { scheduler.Add(new TimedLatchScript()); TickAt(0); TickAt(1); world.Door.Set(); CheckEqual(0, world.Door.SubscriptionCount); } public void WaitForClear_ResumesWithTheInverseWhenTheLatchIsClearedInTime() { world.Door.Set(); scheduler.Add(new TimedClearScript()); TickAt(0); world.Door.Clear(); TickAt(0.5); CheckEqual(new[] { "clear" }, world.Log.ToArray()); } public void WaitForClear_ResumesWithNullWhenTheLimitPasses() { world.Door.Set(); scheduler.Add(new TimedClearScript()); TickAt(0); TickAt(1); CheckEqual(new[] { "timeout" }, world.Log.ToArray()); } public void Wait_AssignedToAField_TakesTheConditionOrNull() { var first = new AssignedLatchScript(); scheduler.Add(first); TickAt(0); world.Door.Set(); TickAt(0.5); CheckTrue(ReferenceEquals(world.Door, first.Last)); world.Door.Clear(); var second = new AssignedLatchScript(); scheduler.Add(second); TickAt(1); TickAt(2); CheckNull(second.Last); } public void Wait_ReturnedFromAPassage_IsTheConditionOrNull() { scheduler.Add(new ReturnedLatchScript()); TickAt(0); world.Door.Set(); TickAt(0.5); CheckEqual(new[] { "door" }, world.Log.ToArray()); world.Door.Clear(); scheduler.Add(new ReturnedLatchScript()); TickAt(1); TickAt(2); CheckEqual(new[] { "door", "timeout" }, world.Log.ToArray()); } } } }