using System;
using System.Collections.Generic;
using System.Linq;
using UnitTestSharp;
namespace Ariadne.UnitTests
{
///
/// Runs scripts next to a twin that is saved and reloaded before every tick, to show a save never changes what a
/// script does.
///
public class ReloadEquivalenceTests : TestFixture
{
public const string RestartStep = "restart";
///
/// Starts a step that sets the game time, in seconds, for the ticks after it, eg: "@2.5".
///
public const string ClockStep = "@";
public static void ApplyChange(string change, ArcTests.Gates gates)
{
if (change.StartsWith("+", StringComparison.Ordinal))
{
gates.Open.Add(change.Substring(1));
}
else if (change.StartsWith("-", StringComparison.Ordinal))
{
gates.Open.Remove(change.Substring(1));
}
}
public static string[] SavedValuesOf(ArcRecord record)
{
var returnMe = new List();
foreach (KeyValuePair field in
record.Fields.OrderBy(pair => pair.Key, StringComparer.Ordinal))
{
returnMe.Add("field " + field.Key + "=" + field.Value);
}
foreach (KeyValuePair local in
record.AllLocals().OrderBy(pair => pair.Key, StringComparer.Ordinal))
{
returnMe.Add("local " + local.Key + "=" + local.Value);
}
return returnMe.ToArray();
}
public class ReloadedBeforeEveryTickTests : TestFixture
{
///
/// Ticks a script once per entry of , which opens ("+gate") or closes ("-gate")
/// a gate or changes nothing (""), and returns what the script observed. Before each tick a twin is
/// loaded from the script's save and given the same tick, and the two must end it identically.
///
[IgnoreTest]
public string[] TickWithATwinReloadedEachTime(string[] schedule,
Func> observe)
where ScriptType : Arc, new()
{
var live = new ScriptType();
var liveGates = new ArcTests.Gates();
TimeSpan gameTime = TimeSpan.Zero;
for (int i = 0; i < schedule.Length; ++i)
{
// Not a tick, so the next step saves the arc just as the restart left it.
if (schedule[i] == RestartStep)
{
live.Restart();
continue;
}
if (schedule[i].StartsWith(ClockStep, StringComparison.Ordinal))
{
gameTime = TimeSpan.FromSeconds(double.Parse(schedule[i].Substring(ClockStep.Length),
System.Globalization.CultureInfo.InvariantCulture));
continue;
}
ArcRecord saved = ArcRecord.Of(live);
ScriptType twin = LoadedArc.From(saved);
twin.VerifyLoaded(saved);
var twinGates = new ArcTests.Gates();
twinGates.Open.UnionWith(liveGates.Open);
int observedBefore = observe(live).Count();
ApplyChange(schedule[i], liveGates);
ApplyChange(schedule[i], twinGates);
live.Tick(liveGates, TestTicks.At(TestTicks.NewSandbox(), TimeSpan.Zero, gameTime));
twin.Tick(twinGates, TestTicks.At(TestTicks.NewSandbox(), TimeSpan.Zero, gameTime));
CheckEqual(observe(live).Skip(observedBefore).ToArray(), observe(twin).ToArray());
CheckEqual(live.Cursor.ToArray(), twin.Cursor.ToArray());
CheckEqual(live.Finished, twin.Finished);
CheckTrue(liveGates.Open.SetEquals(twinGates.Open));
CheckEqual(SavedValuesOf(ArcRecord.Of(live)),
SavedValuesOf(ArcRecord.Of(twin)));
}
return observe(live).ToArray();
}
public void PassageCalledAgain_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new[] { "", "", "+A", "", "+B", "" }, script => script.Log);
CheckEqual(new[] { "step begins", "A seen", "step ends", "step begins", "A seen", "step ends" },
observed);
}
public void PassageCalledAgainAndReturningEarly_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new[] { "", "+skip", "+A", "" }, script => script.Log);
CheckEqual(new[] { "step begins", "step ends", "step begins" }, observed);
}
public void PassageCalledTwiceFromTwoSites_BehavesTheSame()
{
TickWithATwinReloadedEachTime(
new[] { "", "+read", "", "+read", "" }, script => new string[0]);
}
public void PassageKeepingALocal_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new[] { "", "", "+go", "" }, script => script.Log);
CheckEqual(new[] { "first held 2", "second held 0" }, observed);
}
public void Wait_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new[] { "@10", "", "@10.5", "", "@11", "", "@12", "", "@13", "" }, script => script.Log);
CheckEqual(new[] { "start", "after one second", "end" }, observed);
}
public void WaitForTheResultOfAPassage_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new[] { "@10", "", "@11", "", "@12", "+answer", "@13", "", "@14", "" }, script => script.Log);
CheckEqual(new[] { "done" }, observed);
}
public void WaitForTheSumOfTwoPassages_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new[] { "@10", "", "@11", "+a", "@12", "+b", "@13", "", "@14.5", "" }, script => script.Log);
CheckEqual(new[] { "done" }, observed);
}
public void WaitInOneBranch_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new[] { "@10", "+slow", "@10.5", "", "@11", "" }, script => script.Log);
CheckEqual(new[] { "start", "slow", "end" }, observed);
}
public void WaitInALoopAcrossARestart_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new[] { "@10", "", "@11", "", RestartStep, "@12", "", "@13", "", "@14", "", "@15", "" },
script => script.Log);
CheckEqual(new[] { "trip 0", "trip 0", "trip 1", "trip 2" }, observed);
}
public void PassageKeepingALocal_BehavesTheSameAfterARestart()
{
string[] observed = TickWithATwinReloadedEachTime(
new[] { "", "", RestartStep, "", "+go", "" }, script => script.Log);
CheckEqual(new[] { "first held 1", "second held 0" }, observed);
}
public void RestartOfAFinishedScript_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new[] { "", "+go", "+crate", "+crate", "", RestartStep, "", "+crate", "+crate", "" },
script => script.Log);
CheckEqual(new[] { "hello", "waited 1", "delivered", "hello", "waited 0", "delivered" }, observed);
}
public void LocalsAndAPassageArgument_BehaveTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new[] { "", "", "+go", "+crate", "", "+crate", "" }, script => script.Log);
CheckEqual(new[] { "hello", "waited 2", "delivered" }, observed);
}
public void LocalDeclaredInALoopBody_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new[] { "", "+a", "+b", "", "", "" }, script => script.Log);
CheckTrue(observed.Contains("end"));
}
public void ParamsPassage_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new[] { "", "", "+go", "" }, script => script.Log);
CheckEqual(new[] { "a,n1" }, observed);
}
public void ScriptWithSeveralPassages_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new[] { "", "+intro", "+build", "+connect", "+act3 connect", "" }, script => script.Log);
CheckEqual("end", observed.Last());
}
}
}
}