using System;
using System.Collections.Generic;
namespace Ariadne.UnitTests
{
///
/// The context the scheduler tests run arcs in, where every arc logs what it does into one list.
///
public class SchedulerWorld
{
public readonly List Events = new List();
public readonly HashSet Open = new HashSet();
}
///
/// Naps twice, logging around each.
///
public partial class NapScript : Arc
{
public string Name;
public TimeSpan Nap;
protected override void Run(SchedulerWorld world)
{
world.Events.Add(Name + " begins");
first:
Wait(Nap);
world.Events.Add(Name + " wakes");
second:
Wait(Nap);
world.Events.Add(Name + " wakes again");
}
}
///
/// Checks a gate every frame until it opens.
///
public partial class PollScript : Arc
{
public string Name;
protected override void Run(SchedulerWorld world)
{
world.Events.Add(Name + " begins");
poll:
while (!world.Open.Contains("go"))
{
world.Events.Add(Name + " polls");
Yield();
}
world.Events.Add(Name + " goes");
}
}
///
/// Naps, then pauses for one frame, then naps again.
///
public partial class ChainScript : Arc
{
public string Name;
public TimeSpan Nap;
protected override void Run(SchedulerWorld world)
{
first:
Wait(Nap);
world.Events.Add(Name + " wakes");
second:
Yield();
world.Events.Add(Name + " yielded");
third:
Wait(Nap);
world.Events.Add(Name + " done");
}
}
///
/// Naps in a passage of its own, twice.
///
public partial class PassageNapScript : Arc
{
public string Name;
public TimeSpan Nap;
private void Rest(SchedulerWorld world)
{
rest:
Wait(Nap);
world.Events.Add(Name + " rested");
}
protected override void Run(SchedulerWorld world)
{
one:
Rest(world);
two:
Rest(world);
}
}
///
/// Offers a passage for other arcs to run, which naps a second.
///
public partial class NapWorkerScript : Arc
{
public void Pace(SchedulerWorld world)
{
rest:
Wait(TimeSpan.FromSeconds(1));
world.Events.Add("worker rested");
}
protected override void Run(SchedulerWorld world)
{
}
}
///
/// Runs the passage of another arc that naps, which is never ticked itself.
///
public partial class NapClientScript : Arc
{
public NapWorkerScript Worker = new NapWorkerScript();
protected override void Run(SchedulerWorld world)
{
pace:
Worker.Pace(world);
world.Events.Add("client done");
}
}
///
/// Counts how often it runs, and sleeps until a game time.
///
public class SleeperArc : Arc
{
public int Resumes;
public TimeSpan Deadline;
protected override bool __Resume(SchedulerWorld world, TickTime time)
{
++Resumes;
if (time.GameTime < Deadline)
{
__CurrentFrame.Sleep(Deadline);
return false;
}
return true;
}
protected override void Run(SchedulerWorld world)
{
}
}
///
/// Suspends each time it runs, and also sleeps until a game time if it has a deadline.
///
public class SuspenderArc : Arc
{
public int Resumes;
public string Name;
public TimeSpan? Deadline;
public readonly TriggerWait Waiting = new TriggerWait(capacity: 8);
// The triggers it waits on again each time it runs.
public Trigger[] Triggers;
public bool RequireAll;
// The resumes on which the arc finishes and on which it throws. Zero is never.
public int FinishAtResume;
public int ThrowAtResume;
public void WaitOn(params Trigger[] triggers)
{
Waiting.Begin(__CurrentFrame, RequireAll);
foreach (Trigger trigger in triggers)
{
Waiting.Add(trigger);
}
}
protected override bool __Resume(SchedulerWorld world, TickTime time)
{
++Resumes;
if (Triggers != null)
{
WaitOn(Triggers);
}
if (Name != null)
{
world.Events.Add(Name);
}
if (Resumes == ThrowAtResume)
{
throw new InvalidOperationException("boom");
}
if (Resumes == FinishAtResume)
{
return true;
}
__CurrentFrame.Suspend();
if (Deadline.HasValue)
{
__CurrentFrame.Sleep(Deadline.Value);
}
return false;
}
protected override void Run(SchedulerWorld world)
{
}
}
///
/// Suspends like its base, and overrides TickSelf as an arc that ticks other arcs does.
///
public class SuspenderParentArc : SuspenderArc
{
protected override bool TickSelf(SchedulerWorld world, TickContext tick)
{
return base.TickSelf(world, tick);
}
protected override bool __Resume(SchedulerWorld world, TickTime time)
{
return base.__Resume(world, time);
}
}
///
/// Writes its name into the world's events each time it runs, and pauses for a frame.
///
public class RecordingArc : Arc
{
public string Name;
protected override bool __Resume(SchedulerWorld world, TickTime time)
{
world.Events.Add(Name);
return false;
}
protected override void Run(SchedulerWorld world)
{
}
}
///
/// Counts how often it runs, and pauses for a frame each time.
///
public class YieldingArc : Arc
{
public int Resumes;
protected override bool __Resume(SchedulerWorld world, TickTime time)
{
++Resumes;
return false;
}
protected override void Run(SchedulerWorld world)
{
}
}
///
/// Sleeps until a game time, and overrides TickSelf as an arc that ticks other arcs does.
///
public class ParentArc : Arc
{
public int SelfTicks;
public TimeSpan Deadline;
protected override bool TickSelf(SchedulerWorld world, TickContext tick)
{
++SelfTicks;
return base.TickSelf(world, tick);
}
protected override bool __Resume(SchedulerWorld world, TickTime time)
{
if (time.GameTime < Deadline)
{
__CurrentFrame.Sleep(Deadline);
return false;
}
return true;
}
protected override void Run(SchedulerWorld world)
{
}
}
///
/// Throws on its first run, and counts how often it runs.
///
public class ThrowingSchedulerArc : Arc
{
public int Resumes;
protected override bool __Resume(SchedulerWorld world, TickTime time)
{
++Resumes;
if (Resumes == 1)
{
throw new InvalidOperationException("boom");
}
return true;
}
protected override void Run(SchedulerWorld world)
{
}
}
///
/// Calls into the scheduler that is ticking it, as game code reacting to a script might.
///
public class SchedulerUsingArc : Arc
{
public Action OnResume;
public int Resumes;
protected override bool __Resume(SchedulerWorld world, TickTime time)
{
++Resumes;
if (OnResume != null)
{
OnResume();
}
return Resumes >= 2;
}
protected override void Run(SchedulerWorld world)
{
}
}
///
/// Naps, then throws the first time it wakes.
///
public partial class NapThenThrowScript : Arc
{
public TimeSpan Nap;
public int Wakes;
protected override void Run(SchedulerWorld world)
{
nap:
Wait(Nap);
Wakes++;
if (Wakes == 1)
{
throw new InvalidOperationException("boom");
}
}
}
///
/// Has a method named like the one the scheduler looks for, which it doesn't override.
///
public class OverloadedTickSelfArc : Arc
{
protected int TickSelf(int times)
{
return times;
}
protected override bool __Resume(SchedulerWorld world, TickTime time)
{
return false;
}
protected override void Run(SchedulerWorld world)
{
}
}
}