using System;
using System.Collections.Generic;
using System.Linq;
using UnitTestSharp;
namespace Ariadne.UnitTests
{
///
/// Scripts as users write them, run through the code Ariadne.Generator writes from them.
///
public class ScriptTests : TestFixture
{
public static readonly string[] EveryGate = { "intro", "build", "connect", "act3 connect" };
public class TickTests : TestFixture
{
private TickContext tick;
private TutorialScript script;
private LocalsScript locals;
private ArcTests.Gates gates;
public override void TestSetup()
{
tick = TestTicks.Fresh();
script = new TutorialScript();
locals = new LocalsScript();
gates = new ArcTests.Gates();
}
public void FreshScript_RunsItsOpeningCodeAndPausesAtTheFirstLoop()
{
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "start" }, script.Log);
CheckEqual(new[] { "intro" }, script.Cursor);
}
public void StillPaused_RerunsNothing()
{
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "start" }, script.Log);
}
public void FinishedLoop_RunsOnIntoTheNextPassage()
{
gates.Open.Add("intro");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "start", "after intro", "act2 start" }, script.Log);
CheckEqual(new[] { "Act2", "build" }, script.Cursor);
}
public void CodeAfterALoop_RunsOnce()
{
gates.Open.UnionWith(new[] { "intro", "build" });
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "start", "after intro", "act2 start", "built" }, script.Log);
CheckEqual(new[] { "Act2", "connect" }, script.Cursor);
}
public void LastLoopDone_FinishesTheArc()
{
gates.Open.UnionWith(EveryGate);
script.Tick(gates, TestTicks.Fresh());
CheckTrue(script.Finished);
CheckEqual(new[] { "start", "after intro", "act2 start", "built", "act3 start", "end" }, script.Log);
}
public void PassageAfterAPassage_StartsFreshDespiteASharedStepName()
{
script = LoadedArc.From(new ArcRecord { Cursor = { "Act2", "connect" } });
gates.Open.Add("connect");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "act3 start" }, script.Log);
CheckEqual(new[] { "Act3", "connect" }, script.Cursor);
}
public void UnknownKey_RestartsItsPassage()
{
script = LoadedArc.From(new ArcRecord { Cursor = { "Act2", "removed" } });
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "act2 start" }, script.Log);
CheckEqual(new[] { "Act2", "build" }, script.Cursor);
}
public void RepeatedPassage_IsKeyedByEachCallsLabel()
{
var repeated = new RepeatedPassageScript();
gates.Open.Add("read");
repeated.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "second", "read" }, repeated.Cursor);
}
public void SecondLoopInAStep_IsKeyedWithItsCount()
{
var twoPauses = new TwoPausesInOneStepScript();
gates.Open.Add("breaker");
twoPauses.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "connect#1" }, twoPauses.Cursor);
}
public void FinishedArc_LeavesNoPathBehind()
{
gates.Open.UnionWith(EveryGate);
script.Tick(gates, TestTicks.Fresh());
CheckEqual(0, script.Cursor.Count);
}
public void PassageCalledAgainInTheTickItEnds_StartsFromTheTop()
{
var looped = new LoopedPassageScript();
looped.Tick(gates, TestTicks.Fresh());
gates.Open.Add("A");
looped.Tick(gates, TestTicks.Fresh());
gates.Open.Add("B");
looped.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "step begins", "A seen", "step ends", "step begins", "A seen", "step ends" },
looped.Log);
CheckTrue(looped.Finished);
}
public void PassageEndedByAReturn_StartsFromTheTopWhenCalledAgain()
{
var looped = new LoopedEarlyReturnScript();
looped.Tick(gates, TestTicks.Fresh());
gates.Open.UnionWith(new[] { "A", "skip" });
looped.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "step begins", "step ends", "step begins" }, looped.Log);
CheckTrue(looped.Finished);
}
public void BareReturn_EndsThePassage()
{
var earlyReturn = new EarlyReturnScript();
gates.Open.Add("skip");
earlyReturn.Tick(gates, TestTicks.Fresh());
CheckTrue(earlyReturn.Finished);
CheckEqual(0, earlyReturn.Log.Count);
}
public void Exception_ReportsTheScriptFileNotTheCodeBehind()
{
gates.Open.Add("explode");
var sandbox = TestTicks.NewSandbox();
new ThrowingScript().Tick(gates, TestTicks.With(sandbox));
Exception thrown = sandbox.Entries[0].Exception;
string file = new System.Diagnostics.StackTrace(thrown, fNeedFileInfo: true).GetFrame(0).GetFileName();
CheckTrue(file.EndsWith("TestScripts.cs", StringComparison.OrdinalIgnoreCase));
}
public void LocalChangedWhilePaused_KeepsItsValue()
{
locals.Tick(gates, TestTicks.Fresh());
locals.Tick(gates, TestTicks.Fresh());
locals.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
locals.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "hello", "waited 3" }, locals.Log);
}
public void PassageParameter_KeepsItsValueWhilePaused()
{
gates.Open.Add("go");
locals.Tick(gates, TestTicks.Fresh());
gates.Open.Add("crate");
locals.Tick(gates, TestTicks.Fresh());
gates.Open.Add("crate");
locals.Tick(gates, TestTicks.Fresh());
locals.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "hello", "waited 0", "delivered" }, locals.Log);
CheckTrue(locals.Finished);
}
public void PassageArgument_IsEvaluatedOnlyOnTheWayIn()
{
gates.Open.Add("go");
locals.Tick(gates, TestTicks.Fresh());
locals.Tick(gates, TestTicks.Fresh());
locals.Tick(gates, TestTicks.Fresh());
CheckEqual(1, locals.ArgumentEvaluations);
}
public void LambdaReadingASavedLocal_SeesItsValue()
{
var captured = new CapturedLocalScript();
gates.Open.Add("go");
captured.Tick(gates, TestTicks.Fresh());
CheckTrue(captured.Finished);
}
[AllocationTest]
public void PausedInsideAPassage_AllocatesNothing()
{
gates.Open.Add("intro");
script.Tick(gates, tick);
CheckNoAllocations();
}
[AllocationTest]
public void PausedWithSavedLocals_AllocatesNothing()
{
gates.Open.Add("go");
locals.Tick(gates, tick);
CheckNoAllocations();
}
}
public class SaveTests : TestFixture
{
public void FreshScript_SavesNoLocals()
{
CheckEqual(0, ArcRecord.Of(new LocalsScript()).Locals.Count);
}
public void PausedScript_SavesOnlyTheLocalsStillNeeded()
{
var script = new LocalsScript();
var gates = new ArcTests.Gates();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(script);
CheckEqual(1, record.Locals.Count);
CheckEqual(2, record.Locals["Run.waits"]);
}
public void PausedInAPassage_SavesItsParameterButNotTheCallersFinishedLocals()
{
var script = new LocalsScript();
var gates = new ArcTests.Gates();
gates.Open.Add("go");
script.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(script);
CheckEqual(1, record.Locals.Count);
CheckEqual(2, record.Locals["Deliver.crates"]);
}
public void ContextParameter_IsNotSaved()
{
var script = new TutorialScript();
script.Tick(new ArcTests.Gates(), TestTicks.Fresh());
CheckEqual(0, ArcRecord.Of(script).Locals.Count);
}
public void ScratchAndReadonlyFields_AreNotSaved()
{
CheckEqual(0, ArcRecord.Of(new LocalsScript()).Fields.Count);
}
public void FieldsAndLocals_AreWrittenAsTheirDeclaredTypes()
{
var script = new UserTypesScript();
script.Tick(new ArcTests.Gates(), TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(script);
CheckEqual(typeof(Waypoint), record.Types["Destination"]);
CheckEqual(typeof(IList), record.Types["Visits"]);
CheckEqual(typeof(Point), record.Types["Run.start"]);
}
public void BaseClassFields_AreSavedAlongsideTheScripts()
{
var script = new MissionScript();
script.Reward = 9;
script.Title = "relief";
script.Tick(new ArcTests.Gates(), TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(script);
CheckEqualCommutative(new[] { "Reward", "Title", "Stage" }, record.Fields.Keys);
CheckEqual(9, record.Fields["Reward"]);
CheckEqual("relief", record.Fields["Title"]);
CheckEqual(1, record.Fields["Stage"]);
}
}
public class LoadTests : TestFixture
{
public static ArcRecord PausedAtWait(params object[] keysAndValues)
{
var returnMe = new ArcRecord { Cursor = { "wait" } };
for (int i = 0; i < keysAndValues.Length; i += 2)
{
returnMe.Locals[(string)keysAndValues[i]] = keysAndValues[i + 1];
}
return returnMe;
}
public void RestoresALocal()
{
var resumed = LoadedArc.From(PausedAtWait("Run.waits", 4));
var gates = new ArcTests.Gates();
gates.Open.Add("go");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "waited 4" }, resumed.Log);
}
public void SavedInsideAPassage_ResumesWithItsParameter()
{
var gates = new ArcTests.Gates();
gates.Open.UnionWith(new[] { "go", "crate" });
var original = new LocalsScript();
original.Tick(gates, TestTicks.Fresh());
var resumed = LoadedArc.From(ArcRecord.Of(original));
gates.Open.Add("crate");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(0, ArcRecord.Of(resumed).Locals["Deliver.crates"]);
CheckEqual(0, resumed.ArgumentEvaluations);
}
public void LambdaReadingASavedLocal_SeesTheRestoredValue()
{
var resumed = LoadedArc.From(PausedAtWait("Run.wanted", "other"));
var gates = new ArcTests.Gates();
gates.Open.Add("other");
resumed.Tick(gates, TestTicks.Fresh());
CheckTrue(resumed.Finished);
}
public void LocalTheScriptLacks_Throws()
{
ArcRecord record = PausedAtWait("Run.waits", 1, "Run.removed", 1);
var script = LoadedArc.From(record);
CheckThrow(typeof(NotImplementedException));
script.VerifyLoaded(record);
}
public void NeededLocalMissingFromTheSave_Throws()
{
ArcRecord record = PausedAtWait();
var script = LoadedArc.From(record);
CheckThrow(typeof(NotImplementedException));
script.VerifyLoaded(record);
}
public void LocalsTheScriptKeeps_PassVerification()
{
ArcRecord record = PausedAtWait("Run.waits", 4);
var script = LoadedArc.From(record);
script.VerifyLoaded(record);
}
public void LocalTheReaderCannotFind_KeepsItsValue()
{
var resumed = LoadedArc.From(
new UnreadableLocals { Cursor = { "wait" }, Keys = { "Run.waits" } });
var gates = new ArcTests.Gates();
gates.Open.Add("go");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "waited 0" }, resumed.Log);
}
public void UserTypes_SurviveASaveAndLoad()
{
var gates = new ArcTests.Gates();
var original = new UserTypesScript();
original.Tick(gates, TestTicks.Fresh());
original.Destination = new Waypoint { Name = "midway" };
var resumed = LoadedArc.From(ArcRecord.Of(original));
string loadedName = resumed.Destination.Name;
gates.Open.Add("go");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual("midway", loadedName);
CheckEqual(1.5, resumed.Destination.At.X);
CheckEqual(-2.0, resumed.Destination.At.Y);
}
public void BaseClassFields_AreRestoredAlongsideTheScripts()
{
var record = new ArcRecord { Cursor = { "wait" } };
record.Fields["Reward"] = 9;
record.Fields["Title"] = "relief";
record.Fields["Stage"] = 3;
var resumed = LoadedArc.From(record);
CheckEqual(9, resumed.Reward);
CheckEqual("relief", resumed.Title);
CheckEqual(3, resumed.Stage);
CheckEqual(1, resumed.Cache);
}
public void ResumedScript_DoesNotRerunEarlierCode()
{
var gates = new ArcTests.Gates();
gates.Open.UnionWith(new[] { "intro", "build" });
var original = new TutorialScript();
original.Tick(gates, TestTicks.Fresh());
var resumed = LoadedArc.From(ArcRecord.Of(original));
gates.Open.UnionWith(EveryGate);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "act3 start", "end" }, resumed.Log);
CheckTrue(resumed.Finished);
}
}
public class NestedPauseTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public static ScriptType Reloaded(ScriptType paused)
where ScriptType : Arc
{
return LoadedArc.From(ArcRecord.Of(paused));
}
public void TopLevelYield_PausesBeforeAnyBranch()
{
var script = new BranchingScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "start" }, script.Log);
CheckEqual(new[] { "first" }, script.Cursor);
}
public void ThenBranch_PausesInsideIt()
{
gates.Open.Add("left");
var script = new BranchingScript();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "start", "after first", "left" }, script.Log);
CheckEqual(new[] { "second" }, script.Cursor);
}
public void ElseIfBranch_PausesInsideIt()
{
gates.Open.Add("middle");
var script = new BranchingScript();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "start", "after first", "middle" }, script.Log);
CheckEqual(new[] { "third" }, script.Cursor);
}
public void ElseBranch_PausesInsideIt()
{
var script = new BranchingScript();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "start", "after first", "right" }, script.Log);
CheckEqual(new[] { "fourth" }, script.Cursor);
}
public void ResumedInsideABranch_FinishesThatBranchAndRunsOn()
{
gates.Open.Add("left");
var script = new BranchingScript();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "start", "after first", "left", "left done", "end" }, script.Log);
CheckTrue(script.Finished);
}
public void LoadedInsideAThenBranch_FinishesIt()
{
gates.Open.Add("left");
var original = new BranchingScript();
original.Tick(gates, TestTicks.Fresh());
original.Tick(gates, TestTicks.Fresh());
BranchingScript resumed = Reloaded(original);
gates.Open.Clear();
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "left done", "end" }, resumed.Log);
CheckTrue(resumed.Finished);
}
public void LoadedInsideAnElseIfBranch_FinishesIt()
{
gates.Open.Add("middle");
var original = new BranchingScript();
original.Tick(gates, TestTicks.Fresh());
original.Tick(gates, TestTicks.Fresh());
BranchingScript resumed = Reloaded(original);
gates.Open.Clear();
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "middle done", "end" }, resumed.Log);
CheckTrue(resumed.Finished);
}
public void LoadedInsideAnElseBranch_FinishesIt()
{
var original = new BranchingScript();
original.Tick(gates, TestTicks.Fresh());
original.Tick(gates, TestTicks.Fresh());
BranchingScript resumed = Reloaded(original);
gates.Open.Add("left");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "right done", "end" }, resumed.Log);
CheckTrue(resumed.Finished);
}
public void LoadedAtAPauseBeforeAnIf_ChoosesTheBranchWhenItGetsThere()
{
var original = new BranchingScript();
original.Tick(gates, TestTicks.Fresh());
BranchingScript resumed = Reloaded(original);
gates.Open.Add("middle");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "after first", "middle" }, resumed.Log);
CheckEqual(new[] { "third" }, resumed.Cursor);
}
public void BlockInsideAnIf_PausesAndResumesInsideIt()
{
gates.Open.Add("outer");
var original = new NestedBlocksScript();
original.Tick(gates, TestTicks.Fresh());
NestedBlocksScript resumed = Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "outer", "inner" }, original.Log);
CheckEqual(new[] { "deep" }, original.Cursor);
CheckEqual(new[] { "inner done", "outer done" }, resumed.Log);
CheckEqual(new[] { "after" }, resumed.Cursor);
}
public void IfSkipped_RunsOnToThePauseAfterIt()
{
var script = new NestedBlocksScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(0, script.Log.Count);
CheckEqual(new[] { "after" }, script.Cursor);
}
public void PauseAfterAnIfThatHeldPauses_ResumesPastTheIf()
{
gates.Open.Add("outer");
var original = new NestedBlocksScript();
original.Tick(gates, TestTicks.Fresh());
original.Tick(gates, TestTicks.Fresh());
NestedBlocksScript resumed = Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "end" }, resumed.Log);
CheckTrue(resumed.Finished);
}
public void ConditionOfTheIfResumedInside_IsNotRunAgain()
{
gates.Open.Add("go");
var script = new ConditionProbeScript();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
CheckEqual(1, script.Probes);
CheckEqual(new[] { "inside", "end" }, script.Log);
}
public void ConditionOfTheIfLoadedInside_IsNotRunAgain()
{
gates.Open.Add("go");
var original = new ConditionProbeScript();
original.Tick(gates, TestTicks.Fresh());
ConditionProbeScript resumed = Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(0, resumed.Probes);
CheckEqual(new[] { "inside", "end" }, resumed.Log);
}
public void LocalInAThenBranch_KeepsItsValueAcrossASaveAndLoad()
{
gates.Open.Add("big");
var original = new BranchLocalsScript();
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
BranchLocalsScript resumed = Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(10, record.Locals["Run.total"]);
CheckEqual(1, record.Locals.Count);
CheckEqual(new[] { "big 11" }, resumed.Log);
}
public void SameNamedLocalInAnElseBranch_IsSavedUnderItsOwnKey()
{
var original = new BranchLocalsScript();
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
BranchLocalsScript resumed = Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(1, record.Locals["Run.total#1"]);
CheckEqual(1, record.Locals.Count);
CheckEqual(new[] { "small 2" }, resumed.Log);
}
public void LocalOnlyAssignedAfterThePause_IsNotSaved()
{
gates.Open.Add("go");
var script = new AcrossScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(0, ArcRecord.Of(script).Locals.Count);
}
public void LocalOnlyAssignedAfterThePause_HasItsValueOnceResumed()
{
gates.Open.Add("go");
var script = new AcrossScript();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "doubled 2" }, script.Log);
}
public void TopLevelLocalChangedOnBothSidesOfAPause_KeepsItsValueAcrossASaveAndLoad()
{
gates.Open.Add("go");
var original = new SharedCounterScript();
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
SharedCounterScript resumed = Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(1, record.Locals["Run.visits"]);
CheckEqual(new[] { "visits 2" }, resumed.Log);
}
public void PassageCalledInAnIf_PausesAndResumesInsideIt()
{
gates.Open.Add("brief");
var original = new NestedKindsScript();
original.Tick(gates, TestTicks.Fresh());
NestedKindsScript resumed = Reloaded(original);
gates.Open.Add("read");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "Briefing", "read" }, original.Cursor);
CheckEqual(new[] { "briefed", "after brief" }, resumed.Log);
CheckTrue(resumed.Finished);
}
public void LoopInAnElse_PausesAndResumesInsideIt()
{
var original = new NestedKindsScript();
original.Tick(gates, TestTicks.Fresh());
NestedKindsScript resumed = Reloaded(original);
gates.Open.Add("go");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "wait" }, original.Cursor);
CheckEqual(new[] { "went" }, resumed.Log);
CheckTrue(resumed.Finished);
}
}
public class LoopPauseTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void WhileLoop_PausesInTheMiddleOfEachTrip()
{
var script = new WhileMiddleScript();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "before 1", "after 1", "before 2" }, script.Log);
CheckEqual(new[] { "wait" }, script.Cursor);
}
public void WhileLoop_RunsToItsEnd()
{
var script = new WhileMiddleScript();
for (int tick = 0; tick < 5; ++tick)
{
script.Tick(gates, TestTicks.Fresh());
}
CheckEqual(new[] { "before 1", "after 1", "before 2", "after 2", "before 3", "after 3", "before 4",
"after 4", "done 4" }, script.Log);
CheckTrue(script.Finished);
}
public void ContinueBeforeThePause_StartsTheNextTrip()
{
var script = new WhileMiddleScript();
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("skip2");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "before 1", "after 1", "before 3" }, script.Log);
}
public void BreakAfterAResume_LeavesTheLoop()
{
var script = new WhileMiddleScript();
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("stop2");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "before 1", "after 1", "done 2" }, script.Log);
CheckTrue(script.Finished);
}
public void WhileLoopLoadedMidTrip_KeepsItsCounter()
{
var original = new WhileMiddleScript();
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
WhileMiddleScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("stop2");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(1, record.Locals["Run.round"]);
CheckEqual(new[] { "after 1", "done 2" }, resumed.Log);
CheckTrue(resumed.Finished);
}
public void ForLoop_PausesOnEachTripAndThenEnds()
{
var script = new ForPauseScript();
for (int tick = 0; tick < 4; ++tick)
{
script.Tick(gates, TestTicks.Fresh());
}
CheckEqual(new[] { "i 0", "i 1", "i 2", "end" }, script.Log);
CheckTrue(script.Finished);
}
public void ForLoopLoadedMidLoop_RestoresItsCounter()
{
var original = new ForPauseScript();
original.Tick(gates, TestTicks.Fresh());
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
ForPauseScript resumed = NestedPauseTests.Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(1, record.Locals["Run.i"]);
CheckEqual(new[] { "i 2" }, resumed.Log);
}
public void DoLoop_TestsItsConditionAfterTheBody()
{
var script = new DoPauseScript();
for (int tick = 0; tick < 4; ++tick)
{
script.Tick(gates, TestTicks.Fresh());
}
CheckEqual(new[] { "do 0", "do 1", "do 2", "done" }, script.Log);
CheckTrue(script.Finished);
}
public void DoLoopLoadedMidLoop_RestoresItsCounter()
{
var original = new DoPauseScript();
original.Tick(gates, TestTicks.Fresh());
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
DoPauseScript resumed = NestedPauseTests.Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(2, record.Locals["Run.count"]);
CheckEqual(new[] { "do 2" }, resumed.Log);
}
public void LoopInsideAForLoop_PausesInTheInnerOne()
{
var script = new NestedLoopsScript();
for (int tick = 0; tick < 5; ++tick)
{
script.Tick(gates, TestTicks.Fresh());
}
CheckEqual(new[] { "j0k0", "j0k1", "row 0", "j1k0", "j1k1", "row 1" }, script.Log);
CheckTrue(script.Finished);
}
public void NestedLoopsLoadedInTheInnerOne_RestoresBothCounters()
{
var original = new NestedLoopsScript();
original.Tick(gates, TestTicks.Fresh());
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
NestedLoopsScript resumed = NestedPauseTests.Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(0, record.Locals["Run.j"]);
CheckEqual(2, record.Locals["Run.k"]);
CheckEqual(new[] { "row 0", "j1k0" }, resumed.Log);
}
public void ContinueInAForLoop_RunsTheStepBeforeTheNextTest()
{
var script = new ForContinueScript();
for (int tick = 0; tick < 4; ++tick)
{
script.Tick(gates, TestTicks.Fresh());
}
CheckEqual(new[] { "i 0", "i 2", "i 3", "end" }, script.Log);
CheckTrue(script.Finished);
}
public void ContinueInADoLoop_TestsTheConditionBeforeTheNextTrip()
{
var script = new DoContinueScript();
for (int tick = 0; tick < 3; ++tick)
{
script.Tick(gates, TestTicks.Fresh());
}
CheckEqual(new[] { "n 1", "n 3", "end" }, script.Log);
CheckTrue(script.Finished);
}
public void LocalDeclaredInALoopBody_KeepsItsValueAcrossASaveAndLoad()
{
var original = new LoopBodyLocalsScript();
original.Tick(gates, TestTicks.Fresh());
LoopBodyLocalsScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("changed");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "count 1 seen 0" }, resumed.Log);
}
public void ContinueInsideASwitch_StartsTheNextTrip()
{
var script = new LoopBodyLocalsScript();
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("changed");
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "count 1 seen 0", "count 3 seen 1", "end" }, script.Log);
CheckTrue(script.Finished);
}
}
public class SwitchPauseTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void SectionWithAPause_PausesInsideIt()
{
gates.Open.Add("a");
var script = new SwitchPauseScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "one" }, script.Log);
CheckEqual(new[] { "first" }, script.Cursor);
}
public void ResumedSection_FinishesAndRunsOnPastTheSwitch()
{
gates.Open.Add("a");
var script = new SwitchPauseScript();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "one", "one done", "end" }, script.Log);
CheckTrue(script.Finished);
}
public void SectionWithSeveralLabels_RunsForEachOfThem()
{
gates.Open.Add("b");
var script = new SwitchPauseScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "two or three" }, script.Log);
CheckEqual(new[] { "second" }, script.Cursor);
}
public void SectionWithoutAPause_RunsStraightThrough()
{
var script = new SwitchPauseScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "other", "end" }, script.Log);
CheckTrue(script.Finished);
}
public void LoadedInsideASection_FinishesItWithoutChoosingAgain()
{
gates.Open.Add("a");
var original = new SwitchPauseScript();
original.Tick(gates, TestTicks.Fresh());
SwitchPauseScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Clear();
gates.Open.Add("b");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "one done", "end" }, resumed.Log);
CheckTrue(resumed.Finished);
}
public void ContinueInASwitchInALoop_StartsTheNextTrip()
{
var script = new SwitchLoopScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "other 1", "after 1", "three" }, script.Log);
CheckEqual(new[] { "wait" }, script.Cursor);
}
public void LoadedInsideASwitchInALoop_RestoresTheCounter()
{
var original = new SwitchLoopScript();
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
SwitchLoopScript resumed = NestedPauseTests.Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(3, record.Locals["Run.n"]);
CheckEqual(new[] { "after 3", "end" }, resumed.Log);
CheckTrue(resumed.Finished);
}
public void GotoCase_RunsTheOtherSectionBeforeItPauses()
{
var script = new GotoCaseScript();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "one", "two", "after" }, script.Log);
CheckTrue(script.Finished);
}
public void LoadedAfterAGotoCase_ResumesInTheSectionItReached()
{
var original = new GotoCaseScript();
original.Tick(gates, TestTicks.Fresh());
GotoCaseScript resumed = NestedPauseTests.Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "after" }, resumed.Log);
CheckEqual(new[] { "step" }, original.Cursor);
}
}
public class ExpressionVariableTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void OutVariableInAGuardClause_KeepsItsValueAcrossASaveAndLoad()
{
var original = new GuardClauseScript();
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
GuardClauseScript resumed = NestedPauseTests.Reloaded(original);
resumed.Text = "unused";
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(5, record.Locals["Run.count"]);
CheckEqual(new[] { "count 5" }, resumed.Log);
}
public void GuardClauseThatFails_ReturnsBeforeAnyPause()
{
var script = new GuardClauseScript { Text = "x" };
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "bad" }, script.Log);
CheckTrue(script.Finished);
}
public void PatternVariableInAnIf_KeepsItsValueAcrossASaveAndLoad()
{
var original = new PatternIfScript { Thing = "hi" };
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
PatternIfScript resumed = NestedPauseTests.Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual("hi", record.Locals["Run.text"]);
CheckEqual(new[] { "text hi" }, resumed.Log);
}
public void PatternThatDoesNotMatch_TakesTheElseBranch()
{
var script = new PatternIfScript { Thing = 7 };
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "none" }, script.Log);
CheckTrue(script.Finished);
}
public void OutVariableInALoopCondition_KeepsTheItemAcrossASaveAndLoad()
{
var original = new OutLoopScript();
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
OutLoopScript resumed = NestedPauseTests.Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(2, record.Locals["Run.item"]);
CheckEqual(new[] { "item 2" }, resumed.Log);
}
public void OutLoop_RunsToItsEnd()
{
var script = new OutLoopScript();
for (int tick = 0; tick < 3; ++tick)
{
script.Tick(gates, TestTicks.Fresh());
}
CheckEqual(new[] { "item 2", "item 1", "end" }, script.Log);
CheckTrue(script.Finished);
}
public void PatternVariableInACaseLabel_KeepsItsValueAcrossASaveAndLoad()
{
var original = new PatternCaseScript { Thing = "hi" };
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
PatternCaseScript resumed = NestedPauseTests.Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual("hi", record.Locals["Run.text"]);
CheckEqual(new[] { "string hi" }, original.Log);
CheckEqual(new[] { "done hi" }, resumed.Log);
}
public void OtherCaseLabels_StillPickTheirSections()
{
var number = new PatternCaseScript { Thing = 7 };
number.Tick(gates, TestTicks.Fresh());
var other = new PatternCaseScript { Thing = 1.5 };
other.Tick(gates, TestTicks.Fresh());
var empty = new PatternCaseScript { Thing = "" };
empty.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "int 7" }, number.Log);
CheckEqual(new[] { "other" }, other.Log);
CheckEqual(new[] { "other" }, empty.Log);
}
public void TupleDeconstruction_KeepsEachVariableAcrossASaveAndLoad()
{
var original = new DeconstructionScript();
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
DeconstructionScript resumed = NestedPauseTests.Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(2, record.Locals["Run.low"]);
CheckEqual(9, record.Locals["Run.high"]);
CheckEqual(10, record.Locals["Run.inner"]);
CheckEqual(new[] { "2-9 10" }, resumed.Log);
}
}
public class ValuePassageTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void PassageThatPauses_KeepsTheCallerWaitingForItsValue()
{
var script = new AskScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(0, script.Log.Count);
CheckEqual(new[] { "first", "wait" }, script.Cursor);
}
public void PassageThatFinishes_HandsItsValueToTheCaller()
{
var script = new AskScript();
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("answer");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "choice 11" }, script.Log);
CheckTrue(script.Finished);
}
public void PassageThatNeverPauses_HandsItsValueOnTheFirstTick()
{
gates.Open.Add("answer");
var script = new AskScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "choice 10" }, script.Log);
}
public void LoadedInsideTheCalledPassage_StillHandsTheValueBack()
{
var original = new AskScript();
original.Tick(gates, TestTicks.Fresh());
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
AskScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("answer");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(2, record.Locals["Ask.waited"]);
CheckEqual(new[] { "choice 12" }, resumed.Log);
}
public void ValueHandedThroughTwoPassages_ArrivesAtTheCaller()
{
var original = new ValueChainScript();
original.Tick(gates, TestTicks.Fresh());
ValueChainScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("inner");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "Outer", "Inner", "wait" }, original.Cursor);
CheckEqual(new[] { "result 7" }, resumed.Log);
CheckTrue(resumed.Finished);
}
public void ValueKeptAcrossALaterPause_SurvivesASaveAndLoad()
{
var original = new KeptResultScript();
original.Tick(gates, TestTicks.Fresh());
gates.Open.Add("answer");
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
KeptResultScript resumed = NestedPauseTests.Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "second" }, original.Cursor);
CheckEqual(11, record.Locals["Run.first"]);
CheckEqual(new[] { "first 11" }, resumed.Log);
}
public void ValuesAssignedToAFieldAndDiscarded_EachKeyByTheirLabel()
{
var script = new AssignedValuesScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "one", "wait" }, script.Cursor);
gates.Open.Add("answer");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "total 11" }, script.Log);
CheckTrue(script.Finished);
}
public void PassagesThatNeverPause_RunStraightThrough()
{
gates.Open.Add("answer");
var script = new AssignedValuesScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "total 10" }, script.Log);
}
public void ReturnFromABranchThatPauses_HandsItsValueBack()
{
gates.Open.Add("left");
var original = new BranchingValueScript();
original.Tick(gates, TestTicks.Fresh());
BranchingValueScript resumed = NestedPauseTests.Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "Pick", "wait" }, original.Cursor);
CheckEqual(new[] { "left done" }, resumed.Log);
}
public void ReturnFromABranchThatDoesNotPause_HandsItsValueAtOnce()
{
var script = new BranchingValueScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "right" }, script.Log);
CheckTrue(script.Finished);
}
}
public class SpilledExpressionTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void FieldReadBeforeACall_KeepsItsOldValue()
{
var script = new SpillOrderScript();
script.Tick(gates, TestTicks.Fresh());
script.Offset = 500;
gates.Open.Add("answer");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "result 111" }, script.Log);
CheckTrue(script.Finished);
}
public void FieldReadBeforeACall_SurvivesASaveAndLoad()
{
var original = new SpillOrderScript();
original.Tick(gates, TestTicks.Fresh());
original.Offset = 500;
ArcRecord record = ArcRecord.Of(original);
SpillOrderScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("answer");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(100, record.Locals["Run.__spill1"]);
CheckEqual(new[] { "result 111" }, resumed.Log);
}
public void TwoCallsInOneExpression_KeepTheFirstValueAcrossTheSecondPause()
{
var original = new TwoCallsScript();
original.Tick(gates, TestTicks.Fresh());
gates.Open.Add("answer");
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
TwoCallsScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("later");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "AskLater", "later" }, original.Cursor);
CheckEqual(1100, record.Locals["Run.__spill2"]);
CheckEqual(new[] { "sum 1107" }, resumed.Log);
}
public void ReturnOfACallPlusSomethingElse_HandsTheSumBack()
{
var script = new SpillReturnScript();
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("answer");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "doubled 22" }, script.Log);
CheckTrue(script.Finished);
}
public void CallAsTheReceiverOfAnotherCall_UsesItsValue()
{
var original = new SpillReceiverScript();
original.Tick(gates, TestTicks.Fresh());
SpillReceiverScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("answer");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "got 11" }, resumed.Log);
}
public void ValueCallFeedingAPausingPassage_HandsItsResultOver()
{
var original = new RememberScript();
original.Tick(gates, TestTicks.Fresh());
gates.Open.Add("answer");
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
RememberScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("remember");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "Remember", "wait" }, original.Cursor);
CheckEqual(11, record.Locals["Remember.value"]);
CheckEqual(new[] { "remembered 11" }, resumed.Log);
CheckTrue(resumed.Finished);
}
}
public class ConditionalOperatorTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void AndWhoseLeftIsFalse_NeverCallsTheRight()
{
var script = new AndAskScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "ok False" }, script.Log);
CheckTrue(script.Finished);
}
public void AndWhoseLeftIsTrue_PausesInTheRightAndUsesItsValue()
{
gates.Open.Add("flag");
var script = new AndAskScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "Ask", "wait" }, script.Cursor);
gates.Open.Add("answer");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "ok True" }, script.Log);
}
public void AndPausedInTheRight_SurvivesASaveAndLoad()
{
gates.Open.Add("flag");
var original = new AndAskScript();
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
AndAskScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Remove("flag");
gates.Open.Add("answer");
gates.Open.Add("more");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(true, record.Locals["Run.__spill1"]);
CheckEqual(new[] { "ok True" }, resumed.Log);
}
public void OrWhoseLeftIsTrue_NeverCallsTheRight()
{
gates.Open.Add("flag");
var script = new OrAskScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "ok True" }, script.Log);
CheckTrue(script.Finished);
}
public void OrWhoseLeftIsFalse_PausesInTheRight()
{
var script = new OrAskScript();
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("answer");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "ok False" }, script.Log);
}
public void CoalesceWithAValue_NeverCallsTheRight()
{
var script = new CoalesceAskScript { Cached = 7 };
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "value 7" }, script.Log);
CheckTrue(script.Finished);
}
public void CoalesceWithoutAValue_PausesInTheRight()
{
var script = new CoalesceAskScript();
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("answer");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "value 11" }, script.Log);
}
public void ConditionalTakingTheLiteral_NeverCallsThePassage()
{
gates.Open.Add("fast");
var script = new TernaryAskScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "how quick" }, script.Log);
CheckTrue(script.Finished);
}
public void ConditionalTakingThePassage_PausesInIt()
{
var original = new TernaryAskScript();
original.Tick(gates, TestTicks.Fresh());
TernaryAskScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("describe");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "Describe", "describe" }, original.Cursor);
CheckEqual(new[] { "how slow" }, resumed.Log);
}
public void ConditionalAccessOnNothing_NeverEvaluatesItsArguments()
{
var script = new MaybeRecordScript();
script.Tick(gates, TestTicks.Fresh());
CheckTrue(script.Finished);
}
public void ConditionalAccessOnAnObject_PausesInItsArgument()
{
var script = new MaybeRecordScript { Helper = new Recorder() };
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "Ask", "wait" }, script.Cursor);
gates.Open.Add("answer");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { 11 }, script.Helper.Seen);
}
public void FieldReadBeforeAConditionalThatPauses_KeepsItsOldValue()
{
gates.Open.Add("flag");
var script = new OrderThroughBranchScript();
script.Tick(gates, TestTicks.Fresh());
script.Offset = 500;
gates.Open.Add("answer");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "total 121" }, script.Log);
}
public void FieldReadBeforeAConditionalThatDoesNotPause_IsStillRead()
{
var script = new OrderThroughBranchScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "total 100" }, script.Log);
CheckTrue(script.Finished);
}
}
public class SameFileNameTests : TestFixture
{
public void ScriptsInDifferentFolders_EachRunTheirOwnCode()
{
var gates = new ArcTests.Gates();
var first = new FirstBriefing();
var second = new SecondBriefing();
gates.Open.Add("second");
first.Tick(gates, TestTicks.Fresh());
second.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "first" }, first.Log);
CheckEqual(new[] { "second", "second done" }, second.Log);
}
}
public class NestedClassTests : TestFixture
{
public void ScriptInAContainer_PausesAndResumesAcrossAReload()
{
var gates = new ArcTests.Gates();
var original = new Campaign.Outpost();
original.Tick(gates, TestTicks.Fresh());
original.Tick(gates, TestTicks.Fresh());
Campaign.Outpost resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("clear");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "scouted after 2", "done" }, resumed.Log);
CheckTrue(resumed.Finished);
}
}
public class GenericScriptTests : TestFixture
{
public void LocalOfTheTypeParameter_SurvivesAReloadForEachInstantiation()
{
var gates = new ArcTests.Gates();
var numbers = new Collector { Source = 7 };
var words = new Collector { Source = "seven" };
numbers.Tick(gates, TestTicks.Fresh());
words.Tick(gates, TestTicks.Fresh());
Collector resumedNumbers = NestedPauseTests.Reloaded(numbers);
Collector resumedWords = NestedPauseTests.Reloaded(words);
gates.Open.Add("go");
resumedNumbers.Tick(gates, TestTicks.Fresh());
resumedWords.Tick(gates, TestTicks.Fresh());
CheckEqual(7, resumedNumbers.Result);
CheckEqual("seven", resumedWords.Result);
CheckEqual(2, resumedNumbers.Count);
}
public void PassageOfAConstructedGenericBase_KeepsItsLocalAcrossAReload()
{
var gates = new ArcTests.Gates();
var paused = new WordStepper();
paused.Tick(gates, TestTicks.Fresh());
WordStepper resumed = NestedPauseTests.Reloaded(paused);
gates.Open.Add("go");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual("word!", resumed.Last);
CheckTrue(resumed.Finished);
}
public void CallOnAConstructedGenericArc_RunsItsPassageOnThatArc()
{
var gates = new ArcTests.Gates();
var gardener = new Gardener { Plot = new Planter() };
gardener.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
gardener.Tick(gates, TestTicks.Fresh());
CheckEqual("tulip", gardener.Plot.Planted);
CheckEqual("planted tulip", gardener.Report);
}
}
public class GenericPassageTests : TestFixture
{
private TickContext tick;
private ArcTests.Gates gates;
private DualHolder holder;
private SteadyHolder steady;
public override void TestSetup()
{
tick = TestTicks.Fresh();
gates = new ArcTests.Gates();
holder = new DualHolder();
holder.Tick(gates, tick);
steady = new SteadyHolder();
steady.Tick(gates, tick);
}
public void EachInstantiation_KeepsItsOwnLocalsAcrossTheCall()
{
holder.Tick(gates, tick);
gates.Open.Add("go1");
holder.Tick(gates, tick);
holder.Tick(gates, tick);
gates.Open.Add("go2");
holder.Tick(gates, tick);
CheckEqual("7x2;sevenx2;", holder.Report);
CheckTrue(holder.Finished);
}
public void PausedInsideTheFirstInstantiation_ResumesAfterAReload()
{
holder.Tick(gates, tick);
DualHolder resumed = LoadedArc.From(ArcRecord.Of(holder));
gates.Open.Add("go1");
resumed.Tick(gates, tick);
CheckEqual("7x2;", resumed.Report);
CheckEqual(new[] { "words", "hold" }, resumed.Cursor);
}
public void PausedInsideTheSecondInstantiation_ResumesAfterAReload()
{
gates.Open.Add("go1");
holder.Tick(gates, tick);
holder.Tick(gates, tick);
DualHolder resumed = LoadedArc.From(ArcRecord.Of(holder));
gates.Open.Add("go2");
resumed.Tick(gates, tick);
CheckEqual("7x1;sevenx2;", resumed.Report);
CheckTrue(resumed.Finished);
}
public void LocalsOfEachInstantiation_AreSavedBehindTheirCallSite()
{
CheckEqual(new[] { "Run.numbers/Hold.gate", "Run.numbers/Hold.held", "Run.numbers/Hold.seen" },
ArcRecord.Of(holder).AllLocals().Keys.OrderBy(key => key, StringComparer.Ordinal));
}
public void ValueAndConstraints_ComeBackFromAPassageWithTwoTypeParameters()
{
var chooser = new Chooser();
chooser.Tick(gates, tick);
Chooser resumed = LoadedArc.From(ArcRecord.Of(chooser));
gates.Open.Add("go");
resumed.Tick(gates, tick);
CheckEqual(42, resumed.Chosen);
CheckEqual("left/41", resumed.Said);
}
public void GenericPassage_CallsOrdinaryAndGenericPassagesAndCarriesOn()
{
var relayer = new Relayer();
relayer.Tick(gates, tick);
CheckEqual(new[] { "start", "first", "step" }, relayer.Cursor);
gates.Open.Add("a");
relayer.Tick(gates, tick);
CheckEqual(new[] { "start", "second", "inner" }, relayer.Cursor);
gates.Open.Add("b");
relayer.Tick(gates, tick);
CheckEqual(new[] { "start", "third", "inner" }, relayer.Cursor);
gates.Open.Add("c");
relayer.Tick(gates, tick);
CheckEqual(new[] { "start", "fourth", "step" }, relayer.Cursor);
gates.Open.Add("d");
relayer.Tick(gates, tick);
CheckEqual(new[] { "end", "step" }, relayer.Cursor);
gates.Open.Add("e");
relayer.Tick(gates, tick);
CheckEqual("step1;inner5;innerword;step1;step1;", relayer.Log);
CheckTrue(relayer.Finished);
}
public void PausedAtEachCallOfAGenericPassage_ResumesAfterAReload()
{
var relayer = new Relayer();
relayer.Tick(gates, tick);
relayer.Tick(gates, tick);
foreach (string gate in new[] { "a", "b", "c", "d", "e" })
{
relayer = LoadedArc.From(ArcRecord.Of(relayer));
gates.Open.Add(gate);
relayer.Tick(gates, tick);
}
CheckEqual("step2;inner5;innerword;step1;step1;", relayer.Log);
CheckTrue(relayer.Finished);
}
public void LocalsOfCallsInsideAGenericPassage_AreSavedBehindBothCallSites()
{
var relayer = new Relayer();
relayer.Tick(gates, tick);
gates.Open.Add("a");
relayer.Tick(gates, tick);
CheckEqual(new[] { "Run.start/Outer.held", "Run.start/Outer.second/Inner.deep",
"Run.start/Outer.second/Inner.gate" },
ArcRecord.Of(relayer).AllLocals().Keys
.OrderBy(key => key, StringComparer.Ordinal));
}
public void GenericPassageOfAnotherArc_RunsOnThatArcWithAFrameForEachTypeArgument()
{
var farmer = new Farmer { House = new Greenhouse() };
farmer.Tick(gates, tick);
CheckEqual(new[] { "first", "grow", "step" }, farmer.Cursor);
gates.Open.Add("s");
farmer.Tick(gates, tick);
CheckEqual(new[] { "first", "wait" }, farmer.Cursor);
gates.Open.Add("g1");
farmer.Tick(gates, tick);
CheckEqual(new[] { "second", "wait" }, farmer.Cursor);
gates.Open.Add("g2");
farmer.Tick(gates, tick);
CheckEqual("step1;grew4;step0;grewx;", farmer.House.Note);
CheckEqual(2, farmer.House.Grown);
CheckTrue(farmer.Finished);
}
public void RefAndOutParametersOfTheTypeParameter_ComeBackAfterAReload()
{
var swapper = new Swapper();
swapper.Tick(gates, tick);
Swapper resumed = LoadedArc.From(ArcRecord.Of(swapper));
gates.Open.Add("go");
resumed.Tick(gates, tick);
CheckEqual(5, resumed.Left);
CheckEqual(5, resumed.Right);
CheckEqual(10, resumed.Total);
}
public void GenericPassageOfAGenericBaseClass_RunsWithBothTypeParametersAcrossAReload()
{
var steps = new IntSteps();
steps.Tick(gates, tick);
gates.Open.Add("go");
steps.Tick(gates, tick);
IntSteps resumed = LoadedArc.From(ArcRecord.Of(steps));
resumed.Tick(gates, tick);
CheckEqual("a;7;", resumed.Log);
CheckEqual(4, resumed.Last);
CheckTrue(resumed.Finished);
}
[AllocationTest]
public void PausedInsideAGenericPassage_AllocatesNothing()
{
steady.Tick(gates, tick);
CheckNoAllocations();
}
}
public class HeaderCallTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void IfCondition_PausesThenTakesTheBranch()
{
var script = new IfAskScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "Ask", "wait" }, script.Cursor);
gates.Open.Add("answer");
gates.Open.Add("more");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "big" }, script.Log);
CheckTrue(script.Finished);
}
public void IfConditionPaused_SurvivesASaveAndLoad()
{
var original = new IfAskScript();
original.Tick(gates, TestTicks.Fresh());
IfAskScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("answer");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "small" }, resumed.Log);
}
public void WhileCondition_AsksOnEveryTrip()
{
var script = new WhileAskScript();
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("answer");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "trips 2" }, script.Log);
CheckTrue(script.Finished);
}
public void WhileConditionPaused_KeepsItsTripCountAcrossASaveAndLoad()
{
var original = new WhileAskScript();
original.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(original);
WhileAskScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("answer");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(0, record.Locals["Run.trips"]);
CheckEqual(new[] { "trips 2" }, resumed.Log);
}
public void DoCondition_AsksAfterEachTrip()
{
var script = new DoAskScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "Ask", "wait" }, script.Cursor);
gates.Open.Add("answer");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "trips 2" }, script.Log);
CheckTrue(script.Finished);
}
public void ForInitializer_StartsTheLoopAtTheAnswer()
{
var original = new ForInitAskScript();
original.Tick(gates, TestTicks.Fresh());
ForInitAskScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("answer");
gates.Open.Add("more");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "i 21", "i 22", "i 23" }, resumed.Log);
}
public void ForCondition_AsksOnEveryTest()
{
var original = new ForConditionAskScript();
original.Tick(gates, TestTicks.Fresh());
ForConditionAskScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("answer");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "i 0" }, resumed.Log);
CheckTrue(resumed.Finished);
}
public void ForStep_AsksAfterEachTrip()
{
var original = new ForStepAskScript();
original.Tick(gates, TestTicks.Fresh());
ForStepAskScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("answer");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "note 11", "note 10" }, resumed.Log);
CheckTrue(resumed.Finished);
}
public void SwitchExpression_PausesThenTakesTheCase()
{
var original = new SwitchAskScript();
original.Tick(gates, TestTicks.Fresh());
SwitchAskScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("answer");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "eleven" }, resumed.Log);
}
}
public class SeveralDeclaratorTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void PausesInEachInitializerInTurn_AndKeepsTheFirstAnswer()
{
var script = new TwoDeclaratorsScript();
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("one");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "both#1", "wait" }, script.Cursor);
TwoDeclaratorsScript resumed = NestedPauseTests.Reloaded(script);
gates.Open.Add("two");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "1,3" }, resumed.Log);
}
public void InitializerBeforeTheCall_RunsOnceBeforeThePause()
{
var script = new DeclaratorOrderScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(1, script.Calls);
DeclaratorOrderScript resumed = NestedPauseTests.Reloaded(script);
gates.Open.Add("one");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "1,1,1" }, resumed.Log);
}
}
public class PatternCallTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void PatternVariableOfACall_SurvivesALaterPauseAndASaveAndLoad()
{
var original = new PatternAskScript();
original.Tick(gates, TestTicks.Fresh());
gates.Open.Add("answer");
gates.Open.Add("more");
original.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "show" }, original.Cursor);
PatternAskScript resumed = NestedPauseTests.Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "n 20", "done" }, resumed.Log);
}
public void PatternThatDoesNotMatch_SkipsTheBody()
{
var script = new PatternAskScript();
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("answer");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "done" }, script.Log);
CheckTrue(script.Finished);
}
public void SwitchExpressionOverACall_PicksTheArm()
{
var original = new SwitchExpressionAskScript();
original.Tick(gates, TestTicks.Fresh());
SwitchExpressionAskScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("answer");
gates.Open.Add("more");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "big" }, resumed.Log);
}
}
public class SwitchExpressionArmTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void ArmWithoutACall_NeverRunsTheOthersPasses()
{
gates.Open.Add("fast");
var script = new ArmAskScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "fast 1 1" }, script.Log);
CheckTrue(script.Finished);
}
public void ArmWithACall_PausesOnlyInTheTakenArm()
{
gates.Open.Add("slow");
var original = new ArmAskScript();
original.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "Ask", "wait" }, original.Cursor);
ArmAskScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("answer");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "slow 20 0" }, resumed.Log);
}
public void PatternVariableUsedByAnArmsCall_ReachesThePassage()
{
var original = new ArmAskScript();
original.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "Scale", "scale" }, original.Cursor);
ArmAskScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("scale");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "other 500 0" }, resumed.Log);
}
}
public class AssignmentCallTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void CompoundAssignment_AddsToTheValueItHadBeforeThePause()
{
gates.Open.Add("describe");
var original = new AssignAskScript();
original.Tick(gates, TestTicks.Fresh());
original.Total = 100;
AssignAskScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("answer");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "25 21 20 1 described" }, resumed.Log);
}
public void CompoundAssignmentOnALocal_SurvivesASaveAndLoad()
{
gates.Open.Add("describe");
gates.Open.Add("answer");
var original = new AssignAskScript();
original.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "25 21 20 1 described" }, original.Log);
CheckTrue(original.Finished);
}
public void ArraySlot_IsPickedBeforeThePauseAndFilledAfter()
{
gates.Open.Add("describe");
gates.Open.Add("answer");
var original = new AssignAskScript();
original.Tick(gates, TestTicks.Fresh());
CheckEqual(1, original.Calls);
CheckEqual(20, original.Numbers[1]);
}
public void CoalesceAssignmentOnNothing_PausesForTheValue()
{
gates.Open.Add("answer");
var original = new AssignAskScript();
original.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "name", "describe" }, original.Cursor);
AssignAskScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("describe");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual("described", resumed.Name);
}
public void CoalesceAssignmentOnSomething_NeverAsks()
{
gates.Open.Add("answer");
var script = new AssignAskScript { Name = "kept" };
script.Tick(gates, TestTicks.Fresh());
CheckEqual("kept", script.Name);
CheckTrue(script.Finished);
}
}
public class DeconstructionCallTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void DeconstructingIntoNewLocals_KeepsThemAcrossALaterPause()
{
var original = new PairAskScript();
original.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "fresh", "range" }, original.Cursor);
gates.Open.Add("answer");
original.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "pause" }, original.Cursor);
PairAskScript resumed = NestedPauseTests.Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "1-10", "1-10" }, resumed.Log);
CheckEqual(1, resumed.Total);
}
}
public class OutParameterTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void EveryCallForm_ReceivesTheValueWhenThePassageEnds()
{
gates.Open.Add("accept");
gates.Open.Add("fill");
gates.Open.Add("answer");
var script = new OutParameterScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(
new[] { "picked 30", "stored 30", "again 30 True", "6 filled", "relayed 6" }, script.Log);
CheckEqual(30, script.Stored);
CheckTrue(script.Finished);
}
public void ReturnBeforeAnyPause_StillHandsBackWhatWasAssigned()
{
gates.Open.Add("accept");
gates.Open.Add("skip");
gates.Open.Add("answer");
var script = new OutParameterScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "5 early", "relayed 5" }, script.Log.GetRange(3, 2).ToArray());
}
public void PausedInTheCallee_SurvivesASaveAndLoadWithTheValueAssignedBeforeThePause()
{
var original = new OutParameterScript();
original.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "header", "pick" }, original.Cursor);
OutParameterScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("answer");
gates.Open.Add("fill");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "declined 20", "stored 20" }, resumed.Log.GetRange(0, 2).ToArray());
}
public void OutParameterChangedAfterAPause_IsSavedAcrossIt()
{
gates.Open.Add("answer");
var original = new OutParameterScript();
original.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "both", "fill" }, original.Cursor);
ArcRecord record = ArcRecord.Of(original);
OutParameterScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("fill");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(5, record.Locals["Fill.number"]);
CheckTrue(resumed.Log.Contains("6 filled"));
}
}
public class RefParameterTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void RefToALocalAndAField_ChangesThemInPlaceAcrossPauses()
{
gates.Open.Add("go");
var script = new RefParameterScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "local 30", "total 80", "relayed 315" }, script.Log);
CheckEqual(80, script.Total);
CheckTrue(script.Finished);
}
public void WriteBeforeThePause_IsKeptInTheCallersVariableAcrossASaveAndLoad()
{
var original = new RefParameterScript();
original.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "call", "bump" }, original.Cursor);
ArcRecord record = ArcRecord.Of(original);
RefParameterScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("go");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(3, record.Locals["Run.local"]);
CheckEqual(new[] { "local 30", "total 80", "relayed 315" }, resumed.Log);
}
}
public class ArgumentEvaluationTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void ArgumentToAParameterNeverUsedAfterThePause_IsWorkedOutOnce()
{
var script = new ArgumentEffectScript();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
CheckEqual(1, script.Calls);
}
public void InArguments_AreCopiedOnceAndKeptAcrossASaveAndLoad()
{
var original = new ArgumentEffectScript();
original.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
original.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "shown", "show" }, original.Cursor);
ArcRecord record = ArcRecord.Of(original);
ArgumentEffectScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("show");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(2, record.Locals["Show.amount"]);
CheckEqual(new[] { "amount 2", "named 3", "amount 4" }, resumed.Log);
CheckEqual(4, resumed.Calls);
CheckTrue(resumed.Finished);
}
}
public class ReadonlyRefParameterTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void CallersVariableChangedDuringThePause_IsSeenAfterIt()
{
var script = new ReadonlyRefScript();
script.Tick(gates, TestTicks.Fresh());
script.Level = 9;
gates.Open.Add("go");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "level 9" }, script.Log);
CheckTrue(script.Finished);
}
public void PausedInTheCallee_ResumesReadingTheCallersVariableAfterALoad()
{
var original = new ReadonlyRefScript();
original.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "watching", "watch" }, original.Cursor);
ReadonlyRefScript resumed = NestedPauseTests.Reloaded(original);
resumed.Level = 6;
gates.Open.Add("go");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "level 6" }, resumed.Log);
}
}
public class StepJumpTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void GotoToAYieldStep_PausesThereAgain()
{
var script = new StepJumpScript();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "start", "woke" }, script.Log);
CheckEqual(new[] { "retry" }, script.Cursor);
CheckFalse(script.Finished);
}
public void GotoToAYieldStep_RunsOnOnceTheConditionHolds()
{
var script = new StepJumpScript();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "start", "woke", "woke", "end" }, script.Log);
CheckTrue(script.Finished);
}
public void LoadedAfterAGotoPause_ResumesAfterTheStep()
{
var original = new StepJumpScript();
original.Tick(gates, TestTicks.Fresh());
original.Tick(gates, TestTicks.Fresh());
StepJumpScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("go");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "woke", "end" }, resumed.Log);
CheckTrue(resumed.Finished);
}
public void SiblingBlocksSharingALabel_EachJumpsToItsOwn()
{
var script = new SiblingLabelScript();
for (int tick = 0; tick < 4; ++tick)
{
script.Tick(gates, TestTicks.Fresh());
}
CheckEqual(new[] { "right", "right", "right" }, script.Log);
CheckEqual(new[] { "again#1" }, script.Cursor);
}
public void SiblingBlocksSharingALabel_FirstBlockStillJumpsToItself()
{
gates.Open.Add("left");
var script = new SiblingLabelScript();
for (int tick = 0; tick < 4; ++tick)
{
script.Tick(gates, TestTicks.Fresh());
}
CheckEqual(new[] { "left", "left", "left" }, script.Log);
CheckEqual(new[] { "again" }, script.Cursor);
}
public void SiblingBlocksSharingALabel_RunOnOnceTheConditionHolds()
{
var script = new SiblingLabelScript();
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("stop");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "right", "end" }, script.Log);
CheckTrue(script.Finished);
}
public void SecondSiblingLoadedAtItsStep_ResumesInItsOwnBlock()
{
var original = new SiblingLabelScript();
original.Tick(gates, TestTicks.Fresh());
SiblingLabelScript resumed = NestedPauseTests.Reloaded(original);
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "right" }, resumed.Log);
CheckEqual(new[] { "again#1" }, resumed.Cursor);
}
public void CopiedBlockWithAStepsLabelName_JumpsWithinItself()
{
gates.Open.Add("left");
var script = new CopiedLabelScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "copied", "copied", "copied", "end" }, script.Log);
CheckTrue(script.Finished);
}
public void StepBesideACopiedBlockWithItsLabelName_StillPauses()
{
var script = new CopiedLabelScript();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "step", "end" }, script.Log);
CheckTrue(script.Finished);
}
public void GotoToALabelOnAnOrdinaryStatement_RunsFromThatStatement()
{
var script = new PlainLabelJumpScript();
for (int tick = 0; tick < 4; ++tick)
{
script.Tick(gates, TestTicks.Fresh());
}
CheckEqual(new[] { "body", "body", "body", "end" }, script.Log);
CheckTrue(script.Finished);
}
}
public class ParamsArgumentTests : TestFixture
{
private TickContext tick;
private ArcTests.Gates gates;
private ParamsScript script;
public override void TestSetup()
{
tick = TestTicks.Fresh();
gates = new ArcTests.Gates();
script = new ParamsScript();
script.Tick(gates, TestTicks.Fresh());
}
public void ExpandedArguments_AreWorkedOutOnceHoweverLongThePassagePauses()
{
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(1, script.Evaluations);
CheckEqual(new[] { "a,n1" }, script.Log);
}
public void LoadedWhilePaused_StillHasTheArray()
{
ParamsScript resumed = LoadedArc.From(ArcRecord.Of(script));
gates.Open.Add("go");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "a,n1" }, resumed.Log);
CheckEqual(1, resumed.Evaluations);
}
[AllocationTest]
public void PausedInsideAPassageWithParams_AllocatesNothing()
{
script.Tick(gates, tick);
CheckNoAllocations();
}
}
public class InheritedPassageTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void PassageOfTheBaseClass_RunsOnTheDerivedArc()
{
var script = new HoldingScript();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "first held 2", "second held 0" }, script.Log);
CheckTrue(script.Finished);
}
public void PausedInsideIt_KeepsOnePathThroughTheCallersKeysAndTheBases()
{
var script = new HoldingScript();
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "first", "wait" }, script.Cursor);
}
public void TwoScriptsOfOneBase_KeepSeparateLocals()
{
var first = new HoldingScript();
var other = new OtherHoldingScript();
first.Tick(gates, TestTicks.Fresh());
first.Tick(gates, TestTicks.Fresh());
other.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
first.Tick(gates, TestTicks.Fresh());
other.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "first held 2", "second held 0" }, first.Log);
CheckEqual(new[] { "only held 1" }, other.Log);
}
public void PassageOfABaseOfABase_RunsOnTheMostDerivedFrame()
{
var script = new DeepHoldingScript();
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "deep held 1" }, script.Log);
}
public void LoadedInsideIt_ResumesWithTheBasesLocals()
{
var original = new HoldingScript();
original.Tick(gates, TestTicks.Fresh());
original.Tick(gates, TestTicks.Fresh());
HoldingScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("go");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "first held 2", "second held 0" }, resumed.Log);
}
public void SaveInsideIt_HoldsTheBasesLocalsUnderTheirOwnKeys()
{
var script = new HoldingScript();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(script);
CheckEqual(new[] { "Hold.name", "Hold.seen" },
record.Locals.Keys.OrderBy(key => key, StringComparer.Ordinal).ToArray());
CheckEqual(2, record.Locals["Hold.seen"]);
}
public void CalledAgainInTheTickItEnds_StartsFromTheTop()
{
var script = new HoldingScript();
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "first held 1", "second held 0" }, script.Log);
}
}
public class RestartTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void PausedScript_KeepsNoLocalsFromBeforeTheRestart()
{
var script = new HoldingScript();
script.Tick(gates, TestTicks.Fresh());
script.Tick(gates, TestTicks.Fresh());
script.Restart();
script.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
script.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "first held 1", "second held 0" }, script.Log);
}
public void ScriptPausedInsideAnotherArcsPassage_StartsOverWithoutThatCall()
{
var client = new ClientScript();
client.Tick(gates, TestTicks.Fresh());
client.Restart();
client.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
client.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "added 5 after 1" }, client.First.Log);
CheckEqual(5, client.First.Total);
CheckTrue(client.Finished);
}
public void FinishedScript_LeavesTheFieldsOfArcsItCalledAsTheyWere()
{
var client = new ClientScript();
client.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
client.Tick(gates, TestTicks.Fresh());
client.Restart();
client.Tick(gates, TestTicks.Fresh());
CheckEqual(10, client.First.Total);
CheckEqual(14, client.Second.Total);
CheckEqual(new[] { "a 5 b 7 before 3", "a 10 b 14 before 3" }, client.Log);
}
}
public class ForeignCallTests : TestFixture
{
private ArcTests.Gates gates;
public override void TestSetup()
{
gates = new ArcTests.Gates();
}
public void NewFrame_IsAFreshFrameOfTheArcsOwnClass()
{
var worker = new WorkerScript();
Frame frame = worker.NewFrame();
CheckEqual(typeof(WorkerScript.__Frame), frame.GetType());
}
public void CalleesEffects_LandOnTheArcItRunsOn()
{
var client = new ClientScript();
client.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
client.Tick(gates, TestTicks.Fresh());
CheckEqual(5, client.First.Total);
CheckEqual(new[] { "added 5 after 1" }, client.First.Log);
CheckEqual(7, client.Second.Total);
CheckEqual(new[] { "added 7 after 0" }, client.Second.Log);
}
public void Value_ComesBackToTheCaller()
{
var client = new ClientScript();
client.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
client.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "a 5 b 7 before 3" }, client.Log);
CheckTrue(client.Finished);
}
public void PausedInsideTheCall_KeepsOneCursorPathThroughBothArcs()
{
var client = new ClientScript();
client.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "one", "wait" }, client.Cursor);
CheckEqual(0, client.First.Cursor.Count);
}
public void PassageOfAnotherArc_LeavesNoPathBehindWhenItFinishes()
{
var client = new ClientScript();
client.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
client.Tick(gates, TestTicks.Fresh());
CheckEqual(0, client.Cursor.Count);
}
public void CallsOnTwoArcsOfOneClass_KeepTheirOwnLocals()
{
var client = new ClientScript();
client.Tick(gates, TestTicks.Fresh());
client.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
client.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "added 5 after 2" }, client.First.Log);
CheckEqual(new[] { "added 7 after 0" }, client.Second.Log);
}
public void CallMadeAgainInTheTickItEnds_StartsFromTheTop()
{
var client = new RepeatingClientScript();
client.Tick(gates, TestTicks.Fresh());
gates.Open.Add("go");
client.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "added 10 after 1", "added 10 after 0" }, client.Worker.Log);
CheckEqual(20, client.Worker.Total);
}
public void CallMadeFromAPassageOfTheCaller_RunsInsideIt()
{
var relay = new RelayScript();
relay.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "Relay", "Add", "wait" }, relay.Cursor);
gates.Open.Add("go");
relay.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "added 1 after 1", "through 1" }, relay.Worker.Log);
CheckTrue(relay.Finished);
}
public void LoadedInsideTheCall_ResumesItWithItsLocals()
{
var original = new ClientScript();
original.Tick(gates, TestTicks.Fresh());
original.Tick(gates, TestTicks.Fresh());
ClientScript resumed = NestedPauseTests.Reloaded(original);
gates.Open.Add("go");
resumed.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "a 5 b 7 before 3" }, resumed.Log);
CheckEqual(new[] { "added 5 after 2" }, resumed.First.Log);
CheckTrue(resumed.Finished);
}
public void SavedAgainBeforeItsNextTick_KeepsTheCallsLocals()
{
var original = new ClientScript();
original.Tick(gates, TestTicks.Fresh());
original.Tick(gates, TestTicks.Fresh());
ClientScript twice = NestedPauseTests.Reloaded(NestedPauseTests.Reloaded(original));
gates.Open.Add("go");
twice.Tick(gates, TestTicks.Fresh());
CheckEqual(new[] { "added 5 after 2" }, twice.First.Log);
}
public void SaveHoldingTheCallsLocals_PutsThemInATableNamedForTheCall()
{
var client = new ClientScript();
client.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(client);
CheckEqual(new[] { "Run.before" }, record.Locals.Keys.ToArray());
CheckEqual(new[] { "Run.one/" }, record.Scopes.Keys.ToArray());
CheckEqual(new[] { "Add.amount", "Add.seen", "receiver" },
record.Scopes["Run.one/"].Locals.Keys.OrderBy(key => key, StringComparer.Ordinal).ToArray());
}
public void SaveMissingTheReceiver_LeavesTheCallsLocalsUnknown()
{
var client = new ClientScript();
client.Tick(gates, TestTicks.Fresh());
ArcRecord record = ArcRecord.Of(client);
record.Scopes["Run.one/"].Locals.Remove("receiver");
CheckThrow(typeof(NotImplementedException));
LoadedArc.From(record).VerifyLoaded(record);
}
}
///
/// Lists saved locals it then can't find, so the arc's own values stand.
///
public class UnreadableLocals : IArcReader
{
public readonly List Keys = new List();
public List Cursor { get; } = new List();
IReadOnlyList IArcReader.Cursor
{
get
{
return Cursor;
}
}
public bool Finished
{
get
{
return false;
}
}
public IEnumerable LocalKeys
{
get
{
return Keys;
}
}
public IEnumerable ScopeNames
{
get
{
return new string[0];
}
}
public bool TryField(string key, out T value)
{
value = default(T);
return false;
}
public bool HasField(string key)
{
return false;
}
public bool TryLocal(string key, out T value)
{
value = default(T);
return false;
}
public bool TryScope(string name, out IArcReader scope)
{
scope = null;
return false;
}
public bool TryTypeArguments(out Type[] types)
{
types = null;
return false;
}
public void Publish(object built)
{
}
}
}
}