using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Amphora;
using UnitTestSharp;
namespace Ariadne.UnitTests
{
public class ArcSchedulerTests : TestFixture
{
public static TickContext At(ExceptionSandbox sandbox, double gameSeconds)
{
return TestTicks.At(sandbox, TimeSpan.FromMilliseconds(16), TimeSpan.FromSeconds(gameSeconds));
}
public static NapScript Nap(string name, double seconds)
{
return new NapScript { Name = name, Nap = TimeSpan.FromSeconds(seconds) };
}
public static PollScript Poll(string name)
{
return new PollScript { Name = name };
}
public static ChainScript Chain(string name, double seconds)
{
return new ChainScript { Name = name, Nap = TimeSpan.FromSeconds(seconds) };
}
public static PassageNapScript PassageNap(string name, double seconds)
{
return new PassageNapScript { Name = name, Nap = TimeSpan.FromSeconds(seconds) };
}
public static SleeperArc Sleeper(double deadlineSeconds)
{
return new SleeperArc { Deadline = TimeSpan.FromSeconds(deadlineSeconds) };
}
public class MatchesTickingEveryArcTests : TestFixture
{
///
/// Plays , where "@2.5" sets the game time, "" ticks, and "+go" opens the gate "go"
/// and ticks. Every arc is ticked each step in order, or the scheduler does it.
///
[IgnoreTest]
public string[] Play(Func[]> makeArcs, string[] steps, bool scheduled)
{
var world = new SchedulerWorld();
ExceptionSandbox sandbox = TestTicks.NewSandbox();
Arc[] arcs = makeArcs();
var scheduler = new ArcScheduler();
foreach (Arc arc in arcs)
{
scheduler.Add(arc);
}
double seconds = 0;
foreach (string step in steps)
{
if (step.StartsWith("@", StringComparison.Ordinal))
{
seconds = double.Parse(step.Substring(1), System.Globalization.CultureInfo.InvariantCulture);
continue;
}
if (step.StartsWith("+", StringComparison.Ordinal))
{
world.Open.Add(step.Substring(1));
}
if (scheduled)
{
scheduler.Tick(world, At(sandbox, seconds));
}
else
{
foreach (Arc arc in arcs)
{
arc.Tick(world, At(sandbox, seconds));
}
}
}
return world.Events.ToArray();
}
[IgnoreTest]
public string[] PlayBothWays(Func[]> makeArcs, string[] steps)
{
string[] ticked = Play(makeArcs, steps, scheduled: false);
string[] scheduled = Play(makeArcs, steps, scheduled: true);
CheckEqual(ticked, scheduled);
return scheduled;
}
public void NapsOfDifferentLengths_RunInTheSameOrder()
{
string[] events = PlayBothWays(
() => new Arc[] { Nap("a", 3), Nap("b", 1), Nap("c", 2) },
new[] { "@0", "", "@1", "", "@2", "", "@3", "", "@4", "", "@5", "", "@6", "" });
CheckEqual(new[]
{
"a begins", "b begins", "c begins", "b wakes", "b wakes again", "c wakes", "a wakes",
"c wakes again", "a wakes again",
}, events);
}
public void SeveralWakingInOneFrame_RunInRegistrationOrder()
{
string[] events = PlayBothWays(
() => new Arc[] { Nap("a", 3), Nap("b", 1), Nap("c", 2) },
new[] { "@0", "", "@10", "" });
CheckEqual(new[] { "a begins", "b begins", "c begins", "a wakes", "b wakes", "c wakes" }, events);
}
[IgnoreTest]
public Arc[] NappersOfSeveralLengths(int count)
{
var returnMe = new Arc[count];
for (int i = 0; i < count; ++i)
{
returnMe[i] = Nap("a" + i, 1 + i % 7);
}
return returnMe;
}
[IgnoreTest]
public string[] EverySecondUpTo(int seconds)
{
var steps = new List { "@0", "" };
for (int second = 1; second <= seconds; ++second)
{
steps.Add("@" + second);
steps.Add("");
}
return steps.ToArray();
}
public void ManyArcsOfSeveralNapLengthsWakingTogether_RunInTheSameOrder()
{
string[] events = PlayBothWays(() => NappersOfSeveralLengths(300), EverySecondUpTo(20));
CheckEqual(900, events.Length);
}
public void ManyArcsOfOneNapLengthWakingTogether_RunInTheSameOrder()
{
string[] events = PlayBothWays(
() => Enumerable.Range(0, 300).Select(i => (Arc)Nap("a" + i, 2)).ToArray(),
EverySecondUpTo(6));
CheckEqual(900, events.Length);
}
public void ArcsThatPollAndArcsThatNap_RunInTheSameOrder()
{
string[] events = PlayBothWays(
() => new Arc[] { Poll("p"), Nap("n", 2), Poll("q"), Chain("c", 1) },
new[] { "@0", "", "@1", "", "@2", "", "@2.5", "+go", "@3", "", "@4", "" });
CheckTrue(events.Contains("n wakes again"));
CheckTrue(events.Contains("c done"));
CheckTrue(events.Contains("p goes"));
CheckTrue(events.Contains("q goes"));
}
public void WaitThenYieldThenWait_RunsTheSameAsTickingEveryArc()
{
string[] events = PlayBothWays(
() => new Arc[] { Poll("p"), Chain("c", 1) },
new[] { "@0", "", "@1", "", "@1", "", "@2", "" });
CheckEqual(new[] { "p begins", "p polls", "p polls", "c wakes", "p polls", "c yielded", "p polls",
"c done" }, events);
}
public void WaitInAPassageOfTheArc_RunsTheSameAsTickingEveryArc()
{
string[] events = PlayBothWays(
() => new Arc[] { PassageNap("a", 1), PassageNap("b", 2) },
new[] { "@0", "", "@1", "", "@2", "", "@3", "", "@4", "" });
CheckEqual(new[] { "a rested", "a rested", "b rested", "b rested" }, events);
}
public void WaitInAPassageOfAnotherArc_RunsTheSameAsTickingEveryArc()
{
string[] events = PlayBothWays(
() => new Arc[] { new NapClientScript(), Nap("n", 1) },
new[] { "@0", "", "@0.5", "", "@1", "", "@2", "" });
CheckEqual(new[] { "n begins", "worker rested", "client done", "n wakes", "n wakes again" }, events);
}
public void WaitsEitherSideOfTheMinimum_RunTheSameAsTickingEveryArc()
{
var steps = new System.Collections.Generic.List();
for (int frame = 0; frame < 60; ++frame)
{
steps.Add("@" + (frame * 0.016).ToString(System.Globalization.CultureInfo.InvariantCulture));
steps.Add("");
}
string[] events = PlayBothWays(
() => new Arc[]
{
Nap("under", 0.1), Nap("over", 0.2), Nap("edge", 0.128), Nap("short", 0.016),
},
steps.ToArray());
CheckTrue(events.Contains("under wakes again"));
CheckTrue(events.Contains("over wakes again"));
CheckTrue(events.Contains("edge wakes again"));
CheckTrue(events.Contains("short wakes again"));
}
public void GameTimeStandingStill_RunsTheSameAsTickingEveryArc()
{
string[] events = PlayBothWays(
() => new Arc[] { Nap("a", 1), Poll("p") },
new[] { "@5", "", "", "", "" });
CheckEqual(new[] { "a begins", "p begins", "p polls", "p polls", "p polls", "p polls" }, events);
}
public void GameTimeJumpingFarPastEveryDeadline_RunsTheSameAsTickingEveryArc()
{
string[] events = PlayBothWays(
() => new Arc[] { Nap("a", 1), Nap("b", 2) },
new[] { "@0", "", "@1000", "", "" });
CheckEqual(new[] { "a begins", "b begins", "a wakes", "b wakes" }, events);
}
}
public class SleepTests : TestFixture
{
private SchedulerWorld world;
private ExceptionSandbox sandbox;
private ArcScheduler scheduler;
public override void TestSetup()
{
world = new SchedulerWorld();
sandbox = TestTicks.NewSandbox();
scheduler = new ArcScheduler();
}
public void ArcPausedInAWait_Sleeps()
{
scheduler.Add(Nap("a", 3));
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(1, scheduler.SleepingCount);
CheckEqual(1, scheduler.Count);
}
public void ArcWaitingForever_SleepsAndIsNeverWoken()
{
scheduler.Add(new NapScript { Name = "a", Nap = Timeout.InfiniteTimeSpan });
scheduler.Tick(world, At(sandbox, 0));
scheduler.Tick(world, At(sandbox, 1e9));
scheduler.Tick(world, At(sandbox, 1e11));
CheckEqual(new[] { "a begins" }, world.Events.ToArray());
CheckEqual(1, scheduler.SleepingCount);
CheckEqual(0, sandbox.Entries.Count);
}
public void ArcWaitingForever_CanBeRemoved()
{
SleeperArc sleeper = new SleeperArc { Deadline = Timeout.InfiniteTimeSpan };
scheduler.Add(sleeper);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Remove(sleeper);
CheckEqual(0, scheduler.SleepingCount);
CheckEqual(0, scheduler.Count);
}
public void ArcWakedWhileWaitingForever_RunsOnTheNextTick()
{
SleeperArc sleeper = new SleeperArc { Deadline = Timeout.InfiniteTimeSpan };
scheduler.Add(sleeper);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Wake(sleeper);
scheduler.Tick(world, At(sandbox, 1));
CheckEqual(2, sleeper.Resumes);
}
public void ArcPausedInAYield_StaysAwake()
{
scheduler.Add(Poll("p"));
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(0, scheduler.SleepingCount);
}
public void SleepingArc_IsNotRunBeforeItsDeadline()
{
SleeperArc sleeper = Sleeper(10);
scheduler.Add(sleeper);
for (int second = 0; second < 10; ++second)
{
scheduler.Tick(world, At(sandbox, second));
}
scheduler.Tick(world, At(sandbox, 9.99));
CheckEqual(1, sleeper.Resumes);
}
public void SleepingArc_RunsOnTheFrameItsDeadlineIsReached()
{
SleeperArc sleeper = Sleeper(10);
scheduler.Add(sleeper);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Tick(world, At(sandbox, 10));
CheckEqual(2, sleeper.Resumes);
}
public void ThousandSleepers_AreNotRunWhileTheyWait()
{
var sleepers = new SleeperArc[1000];
for (int i = 0; i < sleepers.Length; ++i)
{
sleepers[i] = Sleeper(10);
scheduler.Add(sleepers[i]);
}
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(1000, scheduler.SleepingCount);
for (int second = 1; second < 10; ++second)
{
scheduler.Tick(world, At(sandbox, second));
}
CheckEqual(0, sleepers.Count(each => each.Resumes != 1));
scheduler.Tick(world, At(sandbox, 10));
CheckEqual(0, sleepers.Count(each => each.Resumes != 2));
CheckEqual(0, scheduler.Count);
}
public void WaitInAPassageOfAnotherArc_PutsTheRegisteredArcToSleep()
{
scheduler.Add(new NapClientScript());
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(1, scheduler.SleepingCount);
scheduler.Tick(world, At(sandbox, 1));
CheckEqual(new[] { "worker rested", "client done" }, world.Events.ToArray());
CheckEqual(0, scheduler.Count);
}
public void DefaultMinimumSleep_IsEightFrames()
{
CheckEqual(8, scheduler.MinimumSleepFrames);
CheckEqual(ArcScheduler.DefaultMinimumSleepFrames, scheduler.MinimumSleepFrames);
}
public void WaitShorterThanTheMinimum_StaysAwakeAndRunsEveryFrame()
{
SleeperArc sleeper = Sleeper(0.1);
scheduler.Add(sleeper);
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(0, scheduler.SleepingCount);
scheduler.Tick(world, At(sandbox, 0.016));
scheduler.Tick(world, At(sandbox, 0.032));
CheckEqual(3, sleeper.Resumes);
}
public void WaitLongerThanTheMinimum_Sleeps()
{
scheduler.Add(Sleeper(0.2));
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(1, scheduler.SleepingCount);
}
public void MinimumOfZero_SleepsEveryWait()
{
scheduler.MinimumSleepFrames = 0;
scheduler.Add(Sleeper(0.01));
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(1, scheduler.SleepingCount);
}
public void MinimumMeasuredInTheTicksDeltaTime_FollowsTheFrameLength()
{
scheduler.MinimumSleepFrames = 10;
scheduler.Add(Sleeper(5));
scheduler.Tick(world, TestTicks.At(sandbox, TimeSpan.FromSeconds(1), TimeSpan.Zero));
CheckEqual(0, scheduler.SleepingCount);
scheduler.Tick(world, TestTicks.At(sandbox, TimeSpan.FromSeconds(0.1), TimeSpan.FromSeconds(1)));
CheckEqual(1, scheduler.SleepingCount);
}
public void NegativeMinimum_Throws()
{
CheckThrow(typeof(ArgumentOutOfRangeException));
scheduler.MinimumSleepFrames = -1;
}
public void ArcThatOverridesTickSelf_NeverSleeps()
{
var parent = new ParentArc { Deadline = TimeSpan.FromSeconds(10) };
scheduler.Add(parent);
for (int second = 0; second < 4; ++second)
{
scheduler.Tick(world, At(sandbox, second));
}
CheckEqual(4, parent.SelfTicks);
CheckEqual(0, scheduler.SleepingCount);
}
}
public class SuspendTests : TestFixture
{
private SchedulerWorld world;
private ExceptionSandbox sandbox;
private ArcScheduler scheduler;
public override void TestSetup()
{
world = new SchedulerWorld();
sandbox = TestTicks.NewSandbox();
scheduler = new ArcScheduler();
}
public void ArcThatSuspends_IsCountedAsSuspendedAndNotAsSleeping()
{
scheduler.Add(new SuspenderArc());
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(1, scheduler.SuspendedCount);
CheckEqual(0, scheduler.SleepingCount);
CheckEqual(1, scheduler.Count);
}
public void SuspendedArc_IsNotRunHoweverMuchTimePasses()
{
var suspender = new SuspenderArc();
scheduler.Add(suspender);
foreach (double seconds in new double[] { 0, 1, 1000, 1000000 })
{
scheduler.Tick(world, At(sandbox, seconds));
}
CheckEqual(1, suspender.Resumes);
}
public void ThousandSuspendedArcs_AreNotRunUntilWoken()
{
var suspenders = new SuspenderArc[1000];
for (int i = 0; i < suspenders.Length; ++i)
{
suspenders[i] = new SuspenderArc();
scheduler.Add(suspenders[i]);
}
for (int second = 0; second < 10; ++second)
{
scheduler.Tick(world, At(sandbox, second));
}
CheckEqual(1000, scheduler.SuspendedCount);
CheckEqual(0, suspenders.Count(each => each.Resumes != 1));
foreach (SuspenderArc suspender in suspenders)
{
scheduler.Wake(suspender);
}
scheduler.Tick(world, At(sandbox, 10));
CheckEqual(0, suspenders.Count(each => each.Resumes != 2));
CheckEqual(1000, scheduler.SuspendedCount);
}
public void Wake_RunsTheArcOnTheNextTick()
{
var suspender = new SuspenderArc();
scheduler.Add(suspender);
scheduler.Tick(world, At(sandbox, 0));
CheckTrue(scheduler.Wake(suspender));
CheckEqual(0, scheduler.SuspendedCount);
CheckEqual(1, suspender.Resumes);
scheduler.Tick(world, At(sandbox, 0.016));
CheckEqual(2, suspender.Resumes);
CheckEqual(1, scheduler.SuspendedCount);
}
public void WakeTwice_RunsTheArcOnce()
{
var suspender = new SuspenderArc();
scheduler.Add(suspender);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Wake(suspender);
scheduler.Wake(suspender);
scheduler.Tick(world, At(sandbox, 0.016));
CheckEqual(2, suspender.Resumes);
CheckEqual(1, scheduler.SuspendedCount);
}
public void Wake_FromAnotherArcsTick_RunsTheArcOnTheNextTick()
{
var suspender = new SuspenderArc();
bool woke = false;
scheduler.Add(suspender);
scheduler.Add(new SchedulerUsingArc
{
OnResume = () =>
{
if (!woke)
{
woke = true;
scheduler.Wake(suspender);
}
},
});
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(1, suspender.Resumes);
scheduler.Tick(world, At(sandbox, 0.016));
CheckEqual(2, suspender.Resumes);
}
public void WokenArcs_RunInRegistrationOrderAmongTheRunningOnes()
{
scheduler.Add(new SuspenderArc { Name = "a" });
scheduler.Add(new RecordingArc { Name = "b" });
var last = new SuspenderArc { Name = "c" };
scheduler.Add(last);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Wake(last);
scheduler.Tick(world, At(sandbox, 0.016));
CheckEqual(new[] { "a", "b", "c", "b", "c" }, world.Events.ToArray());
}
public void ArcWokenAfterAnEarlierOneInTheOrder_RunsAfterIt()
{
var first = new SuspenderArc { Name = "a" };
var second = new SuspenderArc { Name = "b" };
scheduler.Add(first);
scheduler.Add(second);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Wake(second);
scheduler.Wake(first);
scheduler.Tick(world, At(sandbox, 0.016));
CheckEqual(new[] { "a", "b", "a", "b" }, world.Events.ToArray());
}
public void Remove_OfASuspendedArc_ForgetsIt()
{
var suspender = new SuspenderArc();
scheduler.Add(suspender);
scheduler.Tick(world, At(sandbox, 0));
CheckTrue(scheduler.Remove(suspender));
CheckFalse(scheduler.Wake(suspender));
scheduler.Tick(world, At(sandbox, 1));
CheckEqual(1, suspender.Resumes);
CheckEqual(0, scheduler.Count);
CheckEqual(0, scheduler.SuspendedCount);
}
public void RemovedSuspendedArc_CanBeAddedAgain()
{
var suspender = new SuspenderArc();
scheduler.Add(suspender);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Remove(suspender);
scheduler.Add(suspender);
scheduler.Tick(world, At(sandbox, 1));
CheckEqual(2, suspender.Resumes);
CheckEqual(1, scheduler.SuspendedCount);
}
public void Restart_OfASuspendedArc_WakesIt()
{
var suspender = new SuspenderArc();
scheduler.Add(suspender);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Restart(suspender);
scheduler.Tick(world, At(sandbox, 0.016));
CheckEqual(2, suspender.Resumes);
CheckEqual(1, scheduler.SuspendedCount);
}
public void ArcThatFinishesWhenWoken_IsDropped()
{
var suspender = new SuspenderArc { FinishAtResume = 2 };
scheduler.Add(suspender);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Wake(suspender);
scheduler.Tick(world, At(sandbox, 0.016));
CheckEqual(0, scheduler.Count);
CheckEqual(0, scheduler.SuspendedCount);
}
public void ArcThatThrowsWhenWoken_IsQuarantinedAndNotSuspended()
{
var suspender = new SuspenderArc { ThrowAtResume = 2 };
scheduler.Add(suspender);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Wake(suspender);
scheduler.Tick(world, At(sandbox, 0.016));
scheduler.Tick(world, At(sandbox, 0.032));
CheckTrue(sandbox.IsQuarantined(AriadneSites.Tick(suspender)));
CheckEqual(2, suspender.Resumes);
CheckEqual(0, scheduler.SuspendedCount);
CheckEqual(1, scheduler.Count);
}
public void ArcThatOverridesTickSelf_NeverSuspends()
{
var parent = new SuspenderParentArc();
scheduler.Add(parent);
for (int second = 0; second < 4; ++second)
{
scheduler.Tick(world, At(sandbox, second));
}
CheckEqual(4, parent.Resumes);
CheckEqual(0, scheduler.SuspendedCount);
}
public void TickRefusedByAnArc_LeavesASuspendedArcSuspended()
{
var suspender = new SuspenderArc();
var yielder = new YieldingArc();
scheduler.Add(suspender);
scheduler.Add(yielder);
scheduler.Tick(world, At(sandbox, 0));
try
{
scheduler.Tick(world, default(TickContext));
}
catch (ArgumentException)
{
}
scheduler.Tick(world, At(sandbox, 1));
CheckEqual(1, suspender.Resumes);
CheckEqual(1, scheduler.SuspendedCount);
CheckEqual(2, yielder.Resumes);
}
public void SuspendedWithADistantDeadline_SleepsInsteadAndRunsAtTheDeadline()
{
var suspender = new SuspenderArc { Deadline = TimeSpan.FromSeconds(10) };
scheduler.Add(suspender);
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(1, scheduler.SleepingCount);
CheckEqual(0, scheduler.SuspendedCount);
scheduler.Tick(world, At(sandbox, 9.99));
CheckEqual(1, suspender.Resumes);
scheduler.Tick(world, At(sandbox, 10));
CheckEqual(2, suspender.Resumes);
}
public void SuspendedWithADistantDeadline_CanBeWokenEarly()
{
var suspender = new SuspenderArc { Deadline = TimeSpan.FromSeconds(10) };
scheduler.Add(suspender);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Wake(suspender);
scheduler.Tick(world, At(sandbox, 1));
CheckEqual(2, suspender.Resumes);
}
public void SuspendedWithANearDeadline_StaysAwakeAndRunsEveryFrame()
{
var suspender = new SuspenderArc { Deadline = TimeSpan.FromSeconds(0.1) };
scheduler.Add(suspender);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Tick(world, At(sandbox, 0.016));
scheduler.Tick(world, At(sandbox, 0.032));
CheckEqual(3, suspender.Resumes);
CheckEqual(0, scheduler.SuspendedCount);
CheckEqual(0, scheduler.SleepingCount);
}
}
public class ManageTests : TestFixture
{
private SchedulerWorld world;
private ExceptionSandbox sandbox;
private ArcScheduler scheduler;
public override void TestSetup()
{
world = new SchedulerWorld();
sandbox = TestTicks.NewSandbox();
scheduler = new ArcScheduler();
}
public void Add_OfAnArcTheSchedulerHolds_Throws()
{
SleeperArc sleeper = Sleeper(1);
scheduler.Add(sleeper);
CheckThrow(typeof(ArgumentException));
scheduler.Add(sleeper);
}
public void Add_OfNull_Throws()
{
CheckThrow(typeof(ArgumentNullException));
scheduler.Add(null);
}
public void Remove_OfAnArcItDoesNotHold_ReturnsFalse()
{
CheckFalse(scheduler.Remove(Sleeper(1)));
}
public void Remove_OfASleepingArc_ForgetsIt()
{
SleeperArc sleeper = Sleeper(10);
scheduler.Add(sleeper);
scheduler.Tick(world, At(sandbox, 0));
CheckTrue(scheduler.Remove(sleeper));
scheduler.Tick(world, At(sandbox, 100));
CheckEqual(1, sleeper.Resumes);
CheckEqual(0, scheduler.Count);
CheckEqual(0, scheduler.SleepingCount);
}
public void Remove_OfAnArcThatHasNotRunYet_NeverRunsIt()
{
SleeperArc sleeper = Sleeper(10);
scheduler.Add(sleeper);
scheduler.Remove(sleeper);
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(0, sleeper.Resumes);
}
public void RemovedArc_CanBeAddedAgain()
{
SleeperArc sleeper = Sleeper(10);
scheduler.Add(sleeper);
scheduler.Remove(sleeper);
scheduler.Add(sleeper);
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(1, sleeper.Resumes);
}
public void Wake_OfASleepingArc_RunsItOnTheNextTick()
{
SleeperArc sleeper = Sleeper(100);
scheduler.Add(sleeper);
scheduler.Tick(world, At(sandbox, 0));
CheckTrue(scheduler.Wake(sleeper));
CheckEqual(0, scheduler.SleepingCount);
scheduler.Tick(world, At(sandbox, 1));
CheckEqual(2, sleeper.Resumes);
CheckEqual(1, scheduler.SleepingCount);
}
public void Wake_OfAnArcItDoesNotHold_ReturnsFalse()
{
CheckFalse(scheduler.Wake(Sleeper(1)));
}
public void Restart_OfASleepingScript_BeginsItAgain()
{
NapScript nap = Nap("a", 5);
scheduler.Add(nap);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Restart(nap);
scheduler.Tick(world, At(sandbox, 1));
CheckEqual(new[] { "a begins", "a begins" }, world.Events.ToArray());
CheckEqual(1, scheduler.SleepingCount);
}
public void Restart_OfAnArcItDoesNotHold_Throws()
{
CheckThrow(typeof(ArgumentException));
scheduler.Restart(Nap("a", 5));
}
public void FinishedArc_IsDropped()
{
scheduler.Add(Nap("a", 1));
scheduler.Tick(world, At(sandbox, 0));
scheduler.Tick(world, At(sandbox, 1));
CheckEqual(1, scheduler.Count);
scheduler.Tick(world, At(sandbox, 2));
CheckEqual(0, scheduler.Count);
CheckEqual(3, world.Events.Count);
}
}
public class DuringATickTests : TestFixture
{
private SchedulerWorld world;
private ExceptionSandbox sandbox;
private ArcScheduler scheduler;
public override void TestSetup()
{
world = new SchedulerWorld();
sandbox = TestTicks.NewSandbox();
scheduler = new ArcScheduler();
}
public void ArcAdded_FirstRunsOnTheNextTick()
{
SleeperArc late = Sleeper(0);
bool added = false;
scheduler.Add(new SchedulerUsingArc
{
OnResume = () =>
{
if (!added)
{
added = true;
scheduler.Add(late);
}
},
});
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(0, late.Resumes);
scheduler.Tick(world, At(sandbox, 1));
CheckEqual(1, late.Resumes);
}
public void ArcRemovedByAnEarlierArc_IsNotRun()
{
SleeperArc victim = Sleeper(0);
scheduler.Add(new SchedulerUsingArc { OnResume = () => scheduler.Remove(victim) });
scheduler.Add(victim);
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(0, victim.Resumes);
CheckEqual(1, scheduler.Count);
}
public void TickingTheSchedulerFromAnArc_IsRefused()
{
scheduler.Add(new SchedulerUsingArc { OnResume = () => scheduler.Tick(world, At(sandbox, 0)) });
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(1, sandbox.Entries.Count);
CheckEqual(typeof(InvalidOperationException), sandbox.Entries[0].Exception.GetType());
CheckEqual(1, scheduler.Count);
}
}
public class FaultTests : TestFixture
{
private SchedulerWorld world;
private ExceptionSandbox sandbox;
private ArcScheduler scheduler;
public override void TestSetup()
{
world = new SchedulerWorld();
sandbox = TestTicks.NewSandbox();
scheduler = new ArcScheduler();
}
public void ArcThatThrows_IsQuarantinedAndTheOthersCarryOn()
{
var thrower = new ThrowingSchedulerArc();
var other = new YieldingArc();
scheduler.Add(thrower);
scheduler.Add(other);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Tick(world, At(sandbox, 1));
CheckEqual(1, sandbox.Entries.Count);
CheckEqual(1, thrower.Resumes);
CheckEqual(2, other.Resumes);
}
public void QuarantinedArc_RunsAgainOnceReleased()
{
var thrower = new ThrowingSchedulerArc();
scheduler.Add(thrower);
scheduler.Tick(world, At(sandbox, 0));
sandbox.Release(AriadneSites.Tick(thrower));
scheduler.Tick(world, At(sandbox, 1));
CheckEqual(2, thrower.Resumes);
CheckEqual(0, scheduler.Count);
}
public void QuarantinedArc_IsHeldAndNotRun()
{
var thrower = new ThrowingSchedulerArc();
scheduler.Add(thrower);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Tick(world, At(sandbox, 1));
scheduler.Tick(world, At(sandbox, 2));
CheckEqual(1, thrower.Resumes);
CheckEqual(1, scheduler.Count);
CheckEqual(0, scheduler.SleepingCount);
}
public void ArcThatThrowsOnWakingFromAWait_IsHeldAwakeAndRunsAgainOnceReleased()
{
var napper = new NapThenThrowScript { Nap = TimeSpan.FromSeconds(20) };
scheduler.Add(napper);
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(1, scheduler.SleepingCount);
scheduler.Tick(world, At(sandbox, 20));
scheduler.Tick(world, At(sandbox, 21));
CheckTrue(sandbox.IsQuarantined(AriadneSites.Tick(napper)));
CheckEqual(0, scheduler.SleepingCount);
CheckEqual(1, scheduler.Count);
CheckEqual(1, napper.Wakes);
sandbox.Release(AriadneSites.Tick(napper));
scheduler.Tick(world, At(sandbox, 22));
CheckEqual(2, napper.Wakes);
CheckTrue(napper.Finished);
CheckEqual(0, scheduler.Count);
}
public void RestartOfAQuarantinedArc_DoesNotReleaseIt()
{
var thrower = new ThrowingSchedulerArc();
scheduler.Add(thrower);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Restart(thrower);
scheduler.Tick(world, At(sandbox, 1));
CheckEqual(1, thrower.Resumes);
CheckTrue(sandbox.IsQuarantined(AriadneSites.Tick(thrower)));
}
public void ArcWithAnOverloadOfTickSelf_CanBeAddedAndMaySleep()
{
scheduler.Add(new OverloadedTickSelfArc());
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(1, scheduler.Count);
}
public void TickRefusedByAnArc_KeepsRegistrationOrder()
{
var world = new SchedulerWorld();
foreach (string name in new[] { "a", "b", "c" })
{
scheduler.Add(new RecordingArc { Name = name });
}
try
{
scheduler.Tick(world, default(TickContext));
}
catch (ArgumentException)
{
}
scheduler.Tick(world, At(sandbox, 0));
scheduler.Tick(world, At(sandbox, 1));
CheckEqual(new[] { "a", "b", "c", "a", "b", "c" }, world.Events.ToArray());
}
public void TickRefusedByAnArc_LeavesEveryArcInTheScheduler()
{
var first = new YieldingArc();
var second = new YieldingArc();
scheduler.Add(first);
scheduler.Add(second);
try
{
scheduler.Tick(world, default(TickContext));
}
catch (ArgumentException)
{
}
scheduler.Tick(world, At(sandbox, 0));
CheckEqual(2, scheduler.Count);
CheckEqual(1, first.Resumes);
CheckEqual(1, second.Resumes);
}
}
public class AllocationTests : TestFixture
{
private SchedulerWorld world;
private ExceptionSandbox sandbox;
private ArcScheduler scheduler;
public override void TestSetup()
{
world = new SchedulerWorld();
sandbox = TestTicks.NewSandbox();
scheduler = new ArcScheduler();
for (int i = 0; i < 100; ++i)
{
scheduler.Add(Sleeper(1000));
}
for (int i = 0; i < 3; ++i)
{
scheduler.Add(new YieldingArc());
}
scheduler.Tick(world, At(sandbox, 0));
}
[AllocationTest]
public void FrameWithSleepersAndActiveArcs_AllocatesNothing()
{
scheduler.Tick(world, At(sandbox, 1));
CheckNoAllocations();
}
}
public class SuspendAllocationTests : TestFixture
{
private SchedulerWorld world;
private ExceptionSandbox sandbox;
private ArcScheduler scheduler;
private SuspenderArc[] suspenders;
[IgnoreTest]
public void WakeAll()
{
foreach (SuspenderArc suspender in suspenders)
{
scheduler.Wake(suspender);
}
}
// One round of waking first, so everything a herd's wake grows is ready for the next.
public override void TestSetup()
{
world = new SchedulerWorld();
sandbox = TestTicks.NewSandbox();
scheduler = new ArcScheduler();
suspenders = new SuspenderArc[100];
for (int i = 0; i < suspenders.Length; ++i)
{
suspenders[i] = new SuspenderArc();
scheduler.Add(suspenders[i]);
}
for (int i = 0; i < 3; ++i)
{
scheduler.Add(new YieldingArc());
}
scheduler.Tick(world, At(sandbox, 0));
WakeAll();
scheduler.Tick(world, At(sandbox, 1));
}
[AllocationTest]
public void FrameWhereSuspendedArcsAreWokenAndSuspendAgain_AllocatesNothing()
{
WakeAll();
scheduler.Tick(world, At(sandbox, 2));
CheckNoAllocations();
CheckEqual(100, scheduler.SuspendedCount);
}
}
public class HerdAllocationTests : TestFixture
{
private SchedulerWorld world;
private ExceptionSandbox sandbox;
private ArcScheduler scheduler;
[IgnoreTest]
public void AddHerd(double firstDeadlineSeconds)
{
for (int i = 0; i < 300; ++i)
{
scheduler.Add(Sleeper(firstDeadlineSeconds + (i % 7) * 0.001));
}
}
// One herd wakes and finishes first, so everything a herd's wake grows is ready for the next.
public override void TestSetup()
{
world = new SchedulerWorld();
sandbox = TestTicks.NewSandbox();
scheduler = new ArcScheduler();
AddHerd(1);
scheduler.Tick(world, At(sandbox, 0));
scheduler.Tick(world, At(sandbox, 2));
AddHerd(3);
scheduler.Tick(world, At(sandbox, 2.5));
}
[AllocationTest]
public void FrameWhereAHerdOfMixedDeadlinesWakes_AllocatesNothing()
{
scheduler.Tick(world, At(sandbox, 4));
CheckNoAllocations();
CheckEqual(0, scheduler.Count);
}
}
}
}