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());
}
}
}
}