using System;
using System.Linq;
using UnitTestSharp;
using Vellum;
namespace Ariadne.Vellum.UnitTests
{
///
/// Runs scripts next to a twin that is saved and reloaded through Vellum before every tick, to show a save never
/// changes what a script does when the arcs it calls are saved with it.
///
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, 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 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(ScriptType script, string[] schedule,
Func observe)
where ScriptType : Arc
{
var liveGates = new 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)
{
script.Restart();
continue;
}
if (schedule[i].StartsWith(ClockStep, StringComparison.Ordinal))
{
gameTime = TimeSpan.FromSeconds(double.Parse(schedule[i].Substring(ClockStep.Length),
System.Globalization.CultureInfo.InvariantCulture));
continue;
}
// A session keeps the node it first made for an object, and loads one object per node.
SaveSession session = ArcConverterTests.Session();
var twin = (ScriptType)session.FromNode(session.ToNode(script, typeof(ScriptType)),
typeof(ScriptType));
var twinGates = new Gates();
twinGates.Open.UnionWith(liveGates.Open);
ApplyChange(schedule[i], liveGates);
ApplyChange(schedule[i], twinGates);
script.Tick(liveGates, TestTicks.At(TimeSpan.Zero, gameTime));
twin.Tick(twinGates, TestTicks.At(TimeSpan.Zero, gameTime));
CheckEqual(observe(script), observe(twin));
CheckEqual(script.Cursor.ToArray(), twin.Cursor.ToArray());
CheckEqual(script.Finished, twin.Finished);
CheckTrue(liveGates.Open.SetEquals(twinGates.Open));
}
return observe(script);
}
public void CallToAnotherArc_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new OperatorScript { Plant = new PlantScript() },
new[] { "", "", "+go", "" },
script => new[] { script.Result, script.Plant.Note, script.Plant.Updates.ToString() });
CheckEqual(new[] { "done 7 2", "updated after 2", "2" }, observed);
}
public void CallToAnotherArcMadeAgainWhileItsFirstLocalsAreStale_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new RepeatingOperatorScript { Plant = new PlantScript() },
new[] { "", "", "+go", "", "+go", "" },
script => new[] { script.Result, script.Plant.Note, script.Plant.Updates.ToString() });
CheckEqual(new[] { "done 4", "updated after 2", "4" }, observed);
}
public void WaitInAPassageOfAnotherArc_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new WaitOperatorScript { Plant = new WaitPlantScript() },
new[] { "@10", "", "@10.5", "", "@11", "" },
script => new[] { script.Result, script.Plant.Note });
CheckEqual(new[] { "done 7", "rested" }, observed);
}
public void TwoPassagesOfTwoArcsInOneStatement_BehaveTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new GatedSumOperatorScript { First = new GatedPlantScript(), Second = new GatedPlantScript() },
new[] { "@0", "", "", "+a", "", "+b", "" },
script => new[] { script.Result, script.First.Total.ToString(), script.Second.Total.ToString() });
CheckEqual(new[] { "sum 116", "7", "9" }, observed);
}
public void WaitForAPassageOfAnotherArc_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new AskWaitOperatorScript { Delay = new DelayPlantScript() },
new[] { "@10", "", "@11", "", "@12", "+answer", "@13", "", "@14", "" },
script => new[] { script.Result });
CheckEqual(new[] { "done" }, observed);
}
public void RestartInAWaitInAPassageOfAnotherArc_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new WaitOperatorScript { Plant = new WaitPlantScript() },
new[] { "@10", "", "@10.5", "", RestartStep, "@20", "", "@20.5", "", "@21", "" },
script => new[] { script.Result, script.Plant.Note });
CheckEqual(new[] { "done 7", "rested" }, observed);
}
public void RestartInsideACallToAnotherArc_BehavesTheSame()
{
string[] observed = TickWithATwinReloadedEachTime(
new RepeatingOperatorScript { Plant = new PlantScript() },
new[] { "", "", "+go", "", RestartStep, "", "+go", "" },
script => new[] { script.Result, script.Plant.Note, script.Plant.Updates.ToString() });
CheckEqual(new[] { null, "updated after 1", "3" }, observed);
}
}
}
}