using System; using System.Collections.Generic; using System.Linq; using Amphora; using UnitTestSharp; using Vellum; namespace Ariadne.Vellum.UnitTests { /// /// Saves scripts paused on triggers, and reloads them into a scheduler of their own. /// public class TriggerWaitSaveTests : TestFixture { /// /// A hallway, a scheduler over it, and the ticks that drive them. /// public class Hall { public readonly Hallway Hallway; public readonly ArcScheduler Scheduler = new ArcScheduler(); private readonly ExceptionSandbox sandbox = new ExceptionSandbox(typeof(AriadneSites).Assembly); private int frame; public Hall(Hallway hallway) { Hallway = hallway; } public Hall() : this(new Hallway()) { } public void Tick() { var tick = new TickContext(sandbox, TimeSpan.FromMilliseconds(16), TimeSpan.FromMilliseconds(16 * frame)); Scheduler.Tick(Hallway, tick); ++frame; } public void TickAt(double gameSeconds) { Scheduler.Tick(Hallway, new TickContext(sandbox, TimeSpan.FromMilliseconds(16), TimeSpan.FromSeconds(gameSeconds))); } public string[] Log() { return Hallway.Log.ToArray(); } } /// /// Saves and as one scene, so they share the triggers, /// and loads it back. /// public static Scene Reload(Hallway hallway, ContentType content) { SaveSession session = ArcConverterTests.Session(); var scene = new Scene { Hallway = hallway, Content = content }; return (Scene)session.FromNode(session.ToNode(scene, typeof(Scene)), typeof(Scene)); } /// /// Does what does with a converter that gives the load no way to plan ahead. /// public static UnplannedScene ReloadUnplanned(Hallway hallway, ContentType content) { SaveSession session = ArcConverterTests.Session(); var scene = new UnplannedScene { Hallway = hallway, Content = content }; return (UnplannedScene)session.FromNode( session.ToNode(scene, typeof(UnplannedScene)), typeof(UnplannedScene)); } public class TimeoutTests : TestFixture { public void WaitReloadedMidWait_ResumesWhenTheLoadedTriggerIsRaisedInTime() { var original = new Hall(); var script = new HallTimedScript(); original.Scheduler.Add(script); original.TickAt(0); Scene loaded = Reload(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); hall.Hallway.Alarm.Raise(); hall.TickAt(1); CheckEqual(new[] { "start", "alarm" }, hall.Log()); } public void WaitReloadedMidWait_StillTimesOutAtTheSavedDeadline() { var original = new Hall(); var script = new HallTimedScript(); original.Scheduler.Add(script); original.TickAt(0); Scene loaded = Reload(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); hall.TickAt(1.9); CheckEqual(new[] { "start" }, hall.Log()); hall.TickAt(2); CheckEqual(new[] { "start", "timed out" }, hall.Log()); } public void WaitReloadedWithoutPlanningAhead_StillTimesOutAtTheSavedDeadline() { var original = new Hall(); var script = new HallTimedScript(); original.Scheduler.Add(script); original.TickAt(0); UnplannedScene loaded = ReloadUnplanned(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); hall.TickAt(2); CheckEqual(new[] { "start", "timed out" }, hall.Log()); } public void WaitReloadedAfterItTimedOut_IsSubscribedToNothing() { var original = new Hall(); var script = new HallTimedThenDoorScript(); original.Scheduler.Add(script); original.TickAt(0); original.TickAt(2); Scene loaded = Reload(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); CheckEqual(0, hall.Hallway.Alarm.SubscriptionCount); CheckEqual(1, hall.Hallway.Door.SubscriptionCount); } public void WaitReloadedAfterItTimedOut_ResumesOnTheNextWaitsTrigger() { var original = new Hall(); var script = new HallTimedThenDoorScript(); original.Scheduler.Add(script); original.TickAt(0); original.TickAt(2); Scene loaded = Reload(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); hall.Hallway.Alarm.Raise(); hall.TickAt(3); CheckEqual(new[] { "alarm window over" }, hall.Log()); hall.Hallway.Door.Raise(); hall.TickAt(4); CheckEqual(new[] { "alarm window over", "door" }, hall.Log()); } } public class FromNodeTests : TestFixture { public void Wait_ReloadedMidWait_ResumesWhenTheLoadedDoorIsRaised() { var original = new Hall(); var script = new HallDoorScript(); original.Scheduler.Add(script); original.Tick(); Scene loaded = Reload(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); hall.Hallway.Door.Raise(); hall.Tick(); CheckEqual(new[] { "start", "door" }, hall.Log()); } public void Wait_ReloadedMidWaitWithoutPlanningAhead_ResumesWhenTheLoadedDoorIsRaised() { var original = new Hall(); var script = new HallDoorScript(); original.Scheduler.Add(script); original.Tick(); UnplannedScene loaded = ReloadUnplanned(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); hall.Hallway.Door.Raise(); hall.Tick(); CheckEqual(new[] { "start", "door" }, hall.Log()); } public void WaitAny_ReloadedMidWaitWithoutPlanningAhead_NamesTheLoadedAlarm() { var original = new Hall(); var script = new HallEitherScript(); original.Scheduler.Add(script); original.Tick(); UnplannedScene loaded = ReloadUnplanned(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); hall.Hallway.Alarm.Raise(); hall.Tick(); CheckEqual(new[] { "alarm" }, hall.Log()); } public void Wait_ReloadedMidWait_KeepsWaitingUntilTheDoorIsRaised() { var original = new Hall(); var script = new HallDoorScript(); original.Scheduler.Add(script); original.Tick(); Scene loaded = Reload(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); hall.Tick(); hall.Tick(); CheckEqual(new[] { "start" }, hall.Log()); CheckEqual(1, hall.Scheduler.SuspendedCount); } public void Wait_ReloadedMidWait_IsNotWokenByTheOriginalDoor() { var original = new Hall(); var script = new HallDoorScript(); original.Scheduler.Add(script); original.Tick(); Scene loaded = Reload(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); original.Hallway.Door.Raise(); hall.Tick(); CheckEqual(new[] { "start" }, hall.Log()); } public void Wait_ReloadedMidWait_SubscribesOnlyTheLoadedDoor() { var original = new Hall(); var script = new HallDoorScript(); original.Scheduler.Add(script); original.Tick(); Scene loaded = Reload(original.Hallway, script); CheckEqual(1, loaded.Hallway.Door.SubscriptionCount); CheckEqual(0, loaded.Hallway.Alarm.SubscriptionCount); } public void Wait_ReloadedMidWaitAndAdded_StillHoldsOneSubscription() { var original = new Hall(); var script = new HallDoorScript(); original.Scheduler.Add(script); original.Tick(); Scene loaded = Reload(original.Hallway, script); new Hall(loaded.Hallway).Scheduler.Add(loaded.Content); CheckEqual(1, loaded.Hallway.Door.SubscriptionCount); } public void Wait_ReloadedTwiceMidWait_ResumesWhenTheDoorIsRaised() { var original = new Hall(); var script = new HallDoorScript(); original.Scheduler.Add(script); original.Tick(); Scene first = Reload(original.Hallway, script); Scene second = Reload(first.Hallway, first.Content); var hall = new Hall(second.Hallway); hall.Scheduler.Add(second.Content); hall.Hallway.Door.Raise(); hall.Tick(); CheckEqual(new[] { "start", "door" }, hall.Log()); } public void Raise_BeforeTheLoadedArcIsAdded_Throws() { var original = new Hall(); var script = new HallDoorScript(); original.Scheduler.Add(script); original.Tick(); Scene loaded = Reload(original.Hallway, script); CheckThrow(typeof(InvalidOperationException)); loaded.Hallway.Door.Raise(); } public void WaitAny_ReloadedMidWait_NamesTheLoadedAlarm() { var original = new Hall(); var script = new HallEitherScript(); original.Scheduler.Add(script); original.Tick(); Scene loaded = Reload(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); hall.Hallway.Alarm.Raise(); hall.Tick(); CheckEqual(new[] { "alarm" }, hall.Log()); } public void WaitAny_RaisedBeforeTheSaveButNotResumed_ResumesOnTheFirstTickAfterTheLoad() { var original = new Hall(); var script = new HallEitherScript(); original.Scheduler.Add(script); original.Tick(); original.Hallway.Alarm.Raise(); Scene loaded = Reload(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); hall.Tick(); CheckEqual(new[] { "alarm" }, hall.Log()); } public void WaitAny_RaisedBeforeTheSave_LeavesTheLoadedDoorWithNoSubscription() { var original = new Hall(); var script = new HallEitherScript(); original.Scheduler.Add(script); original.Tick(); original.Hallway.Alarm.Raise(); Scene loaded = Reload(original.Hallway, script); CheckEqual(0, loaded.Hallway.Door.SubscriptionCount); } public void WaitAll_ReloadedWithOneTriggerRaised_NeedsOnlyTheOther() { var original = new Hall(); var script = new HallBothScript(); original.Scheduler.Add(script); original.Tick(); original.Hallway.Door.Raise(); original.Tick(); Scene loaded = Reload(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); hall.Tick(); CheckEqual(0, hall.Log().Length); hall.Hallway.Alarm.Raise(); hall.Tick(); CheckEqual(new[] { "alarm last" }, hall.Log()); } public void WaitAll_ReloadedWithNoTriggerRaised_NeedsBoth() { var original = new Hall(); var script = new HallBothScript(); original.Scheduler.Add(script); original.Tick(); Scene loaded = Reload(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); hall.Hallway.Alarm.Raise(); hall.Tick(); CheckEqual(0, hall.Log().Length); hall.Hallway.Door.Raise(); hall.Tick(); CheckEqual(new[] { "door last" }, hall.Log()); } public void WaitInALoop_ReloadedBetweenTrips_WaitsForTheNextRaise() { var original = new Hall(); var script = new HallTwiceScript(); original.Scheduler.Add(script); original.Tick(); original.Hallway.Door.Raise(); original.Tick(); Scene loaded = Reload(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); hall.Tick(); CheckEqual(new[] { "door 1" }, hall.Log()); hall.Hallway.Door.Raise(); hall.Tick(); CheckEqual(new[] { "door 1", "door 2" }, hall.Log()); } public void ManyArcsOnOneTrigger_ReloadedTogether_ResumeInTheOrderTheyWereAdded() { var original = new Hall(); var scripts = new List(); for (int i = 0; i < 6; ++i) { scripts.Add(new HallNamedScript { Name = "arc " + i }); original.Scheduler.Add(scripts[i]); } original.Tick(); Scene> loaded = Reload(original.Hallway, scripts); var hall = new Hall(loaded.Hallway); CheckEqual(6, hall.Hallway.Door.SubscriptionCount); foreach (HallNamedScript script in loaded.Content) { hall.Scheduler.Add(script); } hall.Hallway.Door.Raise(); hall.Tick(); CheckEqual(Enumerable.Range(0, 6).Select(i => "arc " + i).ToArray(), hall.Log()); } public void ArcRemovedFromItsScheduler_ReloadedAndAdded_ResumesWhenTheDoorIsRaised() { var original = new Hall(); var script = new HallDoorScript(); original.Scheduler.Add(script); original.Tick(); original.Scheduler.Remove(script); Scene loaded = Reload(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); hall.Hallway.Door.Raise(); hall.Tick(); CheckEqual(new[] { "start", "door" }, hall.Log()); } public void WaitInAnotherArcsPassage_Reloaded_ResumesWhenTheDoorIsRaised() { var original = new Hall(); var script = new HallRelayScript { Lookout = new HallLookoutScript() }; original.Scheduler.Add(script); original.Tick(); Scene loaded = Reload(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); hall.Hallway.Door.Raise(); hall.Tick(); CheckEqual(new[] { "seen", "relayed" }, hall.Log()); } public void WaitInAGenericPassage_Reloaded_ResumesWhenTheAlarmIsRaised() { var original = new Hall(); var script = new HallGenericScript(); original.Scheduler.Add(script); original.Tick(); Scene loaded = Reload(original.Hallway, script); var hall = new Hall(loaded.Hallway); hall.Scheduler.Add(loaded.Content); hall.Hallway.Alarm.Raise(); hall.Tick(); CheckEqual(new[] { "alarm" }, hall.Log()); } } } }