using System; using System.Threading; using Amphora; using UnitTestSharp; namespace Ariadne.UnitTests { public class TriggerWaitScriptTests : TestFixture { public class RunTests : TestFixture { private TriggerWorld 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 TriggerWorld(); sandbox = TestTicks.NewSandbox(); scheduler = new ArcScheduler(); frame = 0; } public void Wait_RunsToTheWaitAndSuspends() { var script = new SingleTriggerScript(); 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_KeepsWaitingUntilTheTriggerIsRaised() { scheduler.Add(new SingleTriggerScript()); for (int i = 0; i < 20; ++i) { Tick(); } CheckEqual(new[] { "start" }, world.Log.ToArray()); CheckEqual(1, scheduler.SuspendedCount); } public void Wait_ResumesOnTheTickAfterTheRaise() { var script = new SingleTriggerScript(); scheduler.Add(script); Tick(); world.Door.Raise(); CheckEqual(new[] { "start" }, world.Log.ToArray()); Tick(); CheckEqual(new[] { "start", "door" }, world.Log.ToArray()); CheckTrue(script.Finished); CheckEqual(0, scheduler.Count); } public void Wait_IgnoresAnotherTriggerBeingRaised() { scheduler.Add(new SingleTriggerScript()); Tick(); world.Alarm.Raise(); Tick(); CheckEqual(new[] { "start" }, world.Log.ToArray()); } public void RaiseBeforeTheWaitBegins_IsMissed() { scheduler.Add(new SingleTriggerScript()); world.Door.Raise(); Tick(); Tick(); CheckEqual(new[] { "start" }, world.Log.ToArray()); CheckEqual(1, scheduler.SuspendedCount); } public void ManyArcsOnOneTrigger_AllResumeFromOneRaise() { scheduler.Add(new SingleTriggerScript()); scheduler.Add(new SingleTriggerScript()); scheduler.Add(new SingleTriggerScript()); Tick(); world.Door.Raise(); Tick(); CheckEqual(new[] { "start", "start", "start", "door", "door", "door" }, world.Log.ToArray()); CheckEqual(0, scheduler.Count); } public void RemovedArc_DoesNotResume() { var script = new SingleTriggerScript(); scheduler.Add(script); Tick(); scheduler.Remove(script); world.Door.Raise(); Tick(); CheckEqual(new[] { "start" }, world.Log.ToArray()); } public void SecondWait_NeedsARaiseOfItsOwn() { scheduler.Add(new TwoWaitsScript()); Tick(); world.Door.Raise(); Tick(); world.Door.Raise(); Tick(); CheckEqual(new[] { "door" }, world.Log.ToArray()); world.Alarm.Raise(); Tick(); CheckEqual(new[] { "door", "alarm" }, world.Log.ToArray()); } public void WaitAny_ResumesOnTheDoorAndNamesIt() { scheduler.Add(new AnyTriggerScript()); Tick(); world.Door.Raise(); Tick(); CheckEqual(new[] { "door" }, world.Log.ToArray()); } public void WaitAny_ResumesOnTheAlarmAndNamesIt() { scheduler.Add(new AnyTriggerScript()); Tick(); world.Alarm.Raise(); Tick(); CheckEqual(new[] { "alarm" }, world.Log.ToArray()); } public void WaitAny_NamesTheFirstTriggerRaisedWhenBothAreBeforeTheResume() { scheduler.Add(new AnyTriggerScript()); Tick(); world.Alarm.Raise(); world.Door.Raise(); Tick(); CheckEqual(new[] { "alarm" }, world.Log.ToArray()); } public void WaitAll_NeedsEveryTrigger() { scheduler.Add(new AllTriggersScript()); Tick(); world.Door.Raise(); Tick(); CheckEqual(0, world.Log.Count); world.Alarm.Raise(); Tick(); CheckEqual(new[] { "alarm last" }, world.Log.ToArray()); } public void WaitAll_NamesTheTriggerRaisedLastWhateverTheOrder() { scheduler.Add(new AllTriggersScript()); Tick(); world.Alarm.Raise(); Tick(); world.Door.Raise(); Tick(); CheckEqual(new[] { "door last" }, world.Log.ToArray()); } public void WaitAll_ResumesFromOneTickWhenBothAreRaisedBeforeIt() { scheduler.Add(new AllTriggersScript()); Tick(); world.Door.Raise(); world.Alarm.Raise(); Tick(); CheckEqual(new[] { "alarm last" }, world.Log.ToArray()); } public void WaitAll_RaiseOfOneTriggerTwice_DoesNotStandInForTheOther() { scheduler.Add(new AllTriggersScript()); Tick(); world.Door.Raise(); world.Door.Raise(); Tick(); CheckEqual(0, world.Log.Count); CheckEqual(1, scheduler.SuspendedCount); } public void WaitAllOfOneTriggerListedTwice_ResumesOnOneRaise() { scheduler.Add(new RepeatedTriggerScript()); Tick(); world.Door.Raise(); Tick(); CheckEqual(new[] { "through" }, world.Log.ToArray()); } public void WaitInALoop_WaitsAgainEachTrip() { scheduler.Add(new LoopedTriggerScript()); Tick(); for (int trip = 0; trip < 3; ++trip) { world.Door.Raise(); Tick(); } CheckEqual(new[] { "door 1", "door 2", "door 3" }, world.Log.ToArray()); CheckEqual(0, scheduler.Count); } public void WaitInALoop_TwoRaisesBeforeAResumeCountOnce() { scheduler.Add(new LoopedTriggerScript()); Tick(); world.Door.Raise(); world.Door.Raise(); Tick(); Tick(); CheckEqual(new[] { "door 1" }, world.Log.ToArray()); } public void WaitAnyAssigned_TakesTheTriggerIntoALocalAndAField() { var script = new AssignedTriggerScript(); scheduler.Add(script); Tick(); world.Door.Raise(); Tick(); world.Alarm.Raise(); Tick(); CheckEqual(new[] { "first door", "then alarm" }, world.Log.ToArray()); CheckTrue(ReferenceEquals(world.Alarm, script.Last)); } public void ResultKeptAcrossALaterPause_IsStillThere() { scheduler.Add(new KeptTriggerScript()); Tick(); world.Alarm.Raise(); Tick(); CheckEqual(0, world.Log.Count); Tick(); CheckEqual(new[] { "alarm" }, world.Log.ToArray()); } public void WaitInAPassage_HandsItsTriggerBack() { scheduler.Add(new PassageTriggerScript()); Tick(); world.Alarm.Raise(); Tick(); CheckEqual(new[] { "alarm" }, world.Log.ToArray()); } public void WaitInABranchThatIsTaken_Waits() { scheduler.Add(new BranchingTriggerScript()); Tick(); CheckEqual(0, world.Log.Count); world.Door.Raise(); Tick(); CheckEqual(new[] { "waited", "done" }, world.Log.ToArray()); } public void WaitInABranchThatIsSkipped_NeverSuspends() { scheduler.Add(new BranchingTriggerScript { Skip = true }); Tick(); CheckEqual(new[] { "done" }, world.Log.ToArray()); CheckEqual(0, scheduler.SuspendedCount); } public void RemovedAndAddedAgain_ResumesOnARaise() { var script = new SingleTriggerScript(); scheduler.Add(script); Tick(); scheduler.Remove(script); scheduler.Add(script); world.Door.Raise(); Tick(); CheckEqual(new[] { "start", "door" }, world.Log.ToArray()); } public void RemovedAndAddedAgain_IsSubscribedOnce() { var script = new SingleTriggerScript(); scheduler.Add(script); Tick(); scheduler.Remove(script); scheduler.Add(script); CheckEqual(1, world.Door.SubscriptionCount); } public void RemovedThenAddedAgainAfterARaiseSweptIt_ResumesOnTheNextRaise() { var script = new SingleTriggerScript(); scheduler.Add(script); Tick(); scheduler.Remove(script); world.Door.Raise(); scheduler.Add(script); Tick(); CheckEqual(new[] { "start" }, world.Log.ToArray()); world.Door.Raise(); Tick(); CheckEqual(new[] { "start", "door" }, world.Log.ToArray()); } public void WaitInAnotherArcsPassage_ResumesAfterTheArcIsRemovedAndAddedAgain() { var script = new TriggerRelayScript { Lookout = new TriggerLookoutScript() }; scheduler.Add(script); Tick(); scheduler.Remove(script); world.Door.Raise(); scheduler.Add(script); world.Door.Raise(); Tick(); CheckEqual(new[] { "seen", "relayed" }, world.Log.ToArray()); } public void WaitInAGenericPassage_ResumesAfterTheArcIsRemovedAndAddedAgain() { var script = new GenericTriggerScript(); scheduler.Add(script); Tick(); scheduler.Remove(script); world.Alarm.Raise(); scheduler.Add(script); world.Alarm.Raise(); Tick(); CheckEqual(new[] { "alarm" }, world.Log.ToArray()); } public void ArcOutsideAScheduler_CannotWaitOnATrigger() { var script = new SingleTriggerScript(); script.Tick(world, ArcSchedulerTests.At(sandbox, 0)); CheckEqual(1, sandbox.Entries.Count); CheckEqual(typeof(InvalidOperationException), sandbox.Entries[0].Exception.GetType()); } } public class RestartTests : TestFixture { private TriggerWorld 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 TriggerWorld(); sandbox = TestTicks.NewSandbox(); scheduler = new ArcScheduler(); } public void Restart_WhileWaiting_StopsTheTriggerWakingTheArcFromItsNextSleep() { var script = new RestartableTriggerScript(); scheduler.Add(script); TickAt(0); script.Skip = true; scheduler.Restart(script); TickAt(0.1); CheckEqual(1, scheduler.SleepingCount); world.Door.Raise(); CheckEqual(1, scheduler.SleepingCount); } public void Restart_WhileWaiting_WaitsAgainAtTheSameWait() { var script = new RestartableTriggerScript(); scheduler.Add(script); TickAt(0); scheduler.Restart(script); TickAt(0.1); world.Door.Raise(); TickAt(0.2); CheckEqual(new[] { "door" }, world.Log.ToArray()); } } public class TimeoutTests : TestFixture { private TriggerWorld 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 TriggerWorld(); sandbox = TestTicks.NewSandbox(); scheduler = new ArcScheduler(); } public void Wait_ResumesWithTheTriggerWhenItIsRaisedInTime() { scheduler.Add(new TimedDoorScript()); TickAt(0); world.Door.Raise(); TickAt(0.5); CheckEqual(new[] { "door" }, world.Log.ToArray()); } public void Wait_ResumesWithNullWhenTheLimitPasses() { scheduler.Add(new TimedDoorScript()); TickAt(0); TickAt(1.9); CheckEqual(0, world.Log.Count); TickAt(2); CheckEqual(new[] { "timed out" }, world.Log.ToArray()); CheckEqual(0, scheduler.Count); } public void Wait_TriggerRaisedBeforeALateTick_BeatsTheLimit() { scheduler.Add(new TimedDoorScript()); TickAt(0); world.Door.Raise(); TickAt(3); CheckEqual(new[] { "door" }, world.Log.ToArray()); } public void Wait_InfiniteLimit_WaitsForTheTriggerHoweverLongItTakes() { scheduler.Add(new TimedDoorScript { Limit = Timeout.InfiniteTimeSpan }); TickAt(0); TickAt(1e9); CheckEqual(0, world.Log.Count); world.Door.Raise(); TickAt(1e9 + 1); CheckEqual(new[] { "door" }, world.Log.ToArray()); CheckEqual(0, scheduler.Count); } public void Wait_LimitThatHasAlreadyPassed_DoesNotPause() { scheduler.Add(new TimedDoorScript { Limit = TimeSpan.Zero }); TickAt(0); CheckEqual(new[] { "timed out" }, world.Log.ToArray()); CheckEqual(0, scheduler.Count); } public void Wait_ShortLimit_KeepsTheArcAwake() { scheduler.Add(new TimedDoorScript { Limit = TimeSpan.FromSeconds(0.05) }); TickAt(0); CheckEqual(0, scheduler.SleepingCount); CheckEqual(0, scheduler.SuspendedCount); } public void Wait_ShortLimit_ResumesWithNullWhenItPasses() { scheduler.Add(new TimedDoorScript { Limit = TimeSpan.FromSeconds(0.05) }); TickAt(0); TickAt(0.04); CheckEqual(0, world.Log.Count); TickAt(0.05); CheckEqual(new[] { "timed out" }, world.Log.ToArray()); } public void Wait_LongLimit_SleepsInTheDeadlineQueue() { scheduler.Add(new TimedDoorScript { Limit = TimeSpan.FromSeconds(10) }); TickAt(0); CheckEqual(1, scheduler.SleepingCount); CheckEqual(0, scheduler.SuspendedCount); } public void Wait_LongLimit_ResumesWithNullAtTheDeadline() { scheduler.Add(new TimedDoorScript { Limit = TimeSpan.FromSeconds(10) }); TickAt(0); TickAt(9.9); CheckEqual(0, world.Log.Count); TickAt(10); CheckEqual(new[] { "timed out" }, world.Log.ToArray()); } public void Wait_LongLimit_IsWokenByARaise() { scheduler.Add(new TimedDoorScript { Limit = TimeSpan.FromSeconds(10) }); TickAt(0); world.Door.Raise(); TickAt(0.016); CheckEqual(new[] { "door" }, world.Log.ToArray()); CheckEqual(0, scheduler.SleepingCount); } public void Wait_RaisedAfterTheLimit_DoesNotWakeTheArcFromItsNextSleep() { scheduler.Add(new TimedThenNapScript()); TickAt(0); TickAt(2); world.Door.Raise(); CheckEqual(1, scheduler.SleepingCount); TickAt(2.1); CheckEqual(new[] { "waited" }, world.Log.ToArray()); CheckEqual(1, scheduler.SleepingCount); } public void Wait_RaisedAfterTheLimit_LeavesNoSubscription() { scheduler.Add(new TimedDoorScript()); TickAt(0); TickAt(2); world.Door.Raise(); CheckEqual(0, world.Door.SubscriptionCount); } public void Wait_DiscardingTheResult_ResumesOnARaise() { scheduler.Add(new TimedDiscardScript()); TickAt(0); world.Door.Raise(); TickAt(0.5); CheckEqual(new[] { "through" }, world.Log.ToArray()); } public void Wait_DiscardingTheResult_ResumesWhenTheLimitPasses() { scheduler.Add(new TimedDiscardScript()); TickAt(0); TickAt(2); CheckEqual(new[] { "through" }, world.Log.ToArray()); } public void Wait_InALoop_TimesOutOrIsRaisedEachTrip() { scheduler.Add(new TimedLoopScript()); TickAt(0); world.Door.Raise(); TickAt(0.1); TickAt(2.2); world.Door.Raise(); TickAt(2.3); CheckEqual(new[] { "door 1", "timed out 2", "door 3" }, world.Log.ToArray()); CheckEqual(0, scheduler.Count); } public void Wait_RemovedAndAddedAgain_KeepsItsDeadline() { var script = new TimedDoorScript(); scheduler.Add(script); TickAt(0); scheduler.Remove(script); scheduler.Add(script); TickAt(1); CheckEqual(0, world.Log.Count); TickAt(2); CheckEqual(new[] { "timed out" }, world.Log.ToArray()); } public void WaitAny_ResumesWithTheTriggerRaisedFirst() { scheduler.Add(new TimedAnyScript()); TickAt(0); world.Alarm.Raise(); world.Door.Raise(); TickAt(0.5); CheckEqual(new[] { "alarm" }, world.Log.ToArray()); } public void WaitAny_ResumesWithNullWhenNeitherIsRaisedInTime() { scheduler.Add(new TimedAnyScript()); TickAt(0); TickAt(2); CheckEqual(new[] { "timed out" }, world.Log.ToArray()); } public void WaitAll_ResumesWithTheTriggerRaisedLastWhenBothAreRaisedInTime() { scheduler.Add(new TimedAllScript()); TickAt(0); world.Door.Raise(); TickAt(0.5); world.Alarm.Raise(); TickAt(1); CheckEqual(new[] { "alarm last" }, world.Log.ToArray()); } public void WaitAll_ResumesWithNullWhenOnlyOneIsRaisedInTime() { scheduler.Add(new TimedAllScript()); TickAt(0); world.Door.Raise(); TickAt(0.5); TickAt(2); CheckEqual(new[] { "timed out" }, world.Log.ToArray()); } public void WaitAll_TriggerRaisedAfterTheLimit_ChangesNothing() { scheduler.Add(new TimedAllScript()); TickAt(0); world.Door.Raise(); TickAt(2); world.Alarm.Raise(); TickAt(2.1); CheckEqual(new[] { "timed out" }, world.Log.ToArray()); } public void WaitInAPassage_HandsBackNullWhenTheLimitPasses() { scheduler.Add(new TimedPassageScript()); TickAt(0); TickAt(2); CheckEqual(new[] { "timed out" }, world.Log.ToArray()); } public void WaitInAPassage_HandsBackTheTriggerRaisedInTime() { scheduler.Add(new TimedPassageScript()); TickAt(0); world.Alarm.Raise(); TickAt(0.5); CheckEqual(new[] { "alarm" }, world.Log.ToArray()); } } } }