Refactor script handlers to return a result type
This moves the point of strictness from the handler to the driver, which makes more sense and keeps it in one place. Drivers choose to be strict when a script result is a failure but still gives a continuation handler. The CLI driver prints an error and continues, while the test driver fails if it wasn't expecting the failure.
This commit is contained in:
parent
569c9021e6
commit
4fee854c4c
|
@ -69,13 +69,21 @@ public class ReplOptions
|
|||
outputWriter?.Flush();
|
||||
|
||||
// Delegate all other command parsing to the handler.
|
||||
handler = handler.HandleInput(input);
|
||||
var result = handler.HandleInput(input);
|
||||
|
||||
// Quit if the handler ends processing, otherwise prompt for the next command.
|
||||
if (handler is null)
|
||||
// Report errors if they occured.
|
||||
if (!result.Success)
|
||||
{
|
||||
Console.WriteLine($"Error: {result.Message}");
|
||||
}
|
||||
|
||||
// Quit if the handler didn't continue processing.
|
||||
if (result.NextHandler is null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Otherwise prompt for the next command.
|
||||
Console.Write(handler.Prompt);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,7 @@ public class AdjudicationQueryScriptHandler(
|
|||
Action<string> WriteLine,
|
||||
List<OrderValidation> validations,
|
||||
World world,
|
||||
IPhaseAdjudicator adjudicator,
|
||||
bool strict = false)
|
||||
IPhaseAdjudicator adjudicator)
|
||||
: IScriptHandler
|
||||
{
|
||||
public string Prompt => "valid> ";
|
||||
|
@ -20,17 +19,12 @@ public class AdjudicationQueryScriptHandler(
|
|||
|
||||
public World World { get; private set; } = world;
|
||||
|
||||
/// <summary>
|
||||
/// Whether unsuccessful commands should terminate the script.
|
||||
/// </summary>
|
||||
public bool Strict { get; } = strict;
|
||||
|
||||
public IScriptHandler? HandleInput(string input)
|
||||
public ScriptResult HandleInput(string input)
|
||||
{
|
||||
var args = input.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (args.Length == 0 || input.StartsWith('#'))
|
||||
{
|
||||
return this;
|
||||
return ScriptResult.Succeed(this);
|
||||
}
|
||||
|
||||
var command = args[0];
|
||||
|
@ -38,55 +32,49 @@ public class AdjudicationQueryScriptHandler(
|
|||
{
|
||||
case "---":
|
||||
WriteLine("Ready for orders");
|
||||
return new GameScriptHandler(WriteLine, World, adjudicator, Strict);
|
||||
return ScriptResult.Succeed(new GameScriptHandler(WriteLine, World, adjudicator));
|
||||
|
||||
case "assert" when args.Length == 1:
|
||||
WriteLine("Usage:");
|
||||
break;
|
||||
|
||||
case "assert":
|
||||
if (!EvaluateAssertion(args[1])) return Strict ? null : this;
|
||||
break;
|
||||
return EvaluateAssertion(args[1]);
|
||||
|
||||
case "status":
|
||||
throw new NotImplementedException();
|
||||
|
||||
default:
|
||||
WriteLine($"Unrecognized command: \"{command}\"");
|
||||
if (Strict) return null;
|
||||
break;
|
||||
return ScriptResult.Fail($"Unrecognized command: \"{command}\"", this);
|
||||
}
|
||||
|
||||
return this;
|
||||
return ScriptResult.Succeed(this);
|
||||
}
|
||||
|
||||
private bool EvaluateAssertion(string assertion)
|
||||
private ScriptResult EvaluateAssertion(string assertion)
|
||||
{
|
||||
var args = assertion.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
switch (args[0])
|
||||
{
|
||||
case "true":
|
||||
return true;
|
||||
return ScriptResult.Succeed(this);
|
||||
|
||||
case "false":
|
||||
return false;
|
||||
return ScriptResult.Fail("assert false", this);
|
||||
|
||||
case "order-valid":
|
||||
case "order-invalid":
|
||||
OrderParser re = new(World);
|
||||
Regex prov = new($"^{re.FullLocation}$", RegexOptions.IgnoreCase);
|
||||
Match match = prov.Match(args[1]);
|
||||
if (!match.Success) {
|
||||
WriteLine($"Could not parse province from \"{args[1]}\"");
|
||||
return !Strict;
|
||||
}
|
||||
if (!match.Success) return ScriptResult.Fail($"Could not parse province from \"{args[1]}\"", this);
|
||||
|
||||
string timeline = match.Groups[1].Length > 0
|
||||
? match.Groups[1].Value
|
||||
: Season.First.Timeline;
|
||||
var seasonsInTimeline = World.Timelines.Seasons.Where(season => season.Timeline == timeline);
|
||||
if (!seasonsInTimeline.Any()) return false;
|
||||
if (!seasonsInTimeline.Any()) return ScriptResult.Fail($"No seasons in timeline {timeline}", this);
|
||||
|
||||
int turn = match.Groups[4].Length > 0
|
||||
? int.Parse(match.Groups[4].Value)
|
||||
|
@ -102,11 +90,15 @@ public class AdjudicationQueryScriptHandler(
|
|||
=> val.Order is UnitOrder order
|
||||
&& order.Unit.Season == season
|
||||
&& World.Map.GetLocation(order.Unit.Location).ProvinceName == province.Name);
|
||||
if (!matching.Any()) return false;
|
||||
if (!matching.Any()) return ScriptResult.Fail("No matching validations");
|
||||
|
||||
return args[0] == "order-valid"
|
||||
? matching.First().Valid
|
||||
: !matching.First().Valid;
|
||||
if (args[0] == "order-valid" && !matching.First().Valid) {
|
||||
return ScriptResult.Fail($"Order \"{matching.First().Order} is invalid");
|
||||
}
|
||||
if (args[0] == "order-invalid" && matching.First().Valid) {
|
||||
return ScriptResult.Fail($"Order \"{matching.First().Order} is valid");
|
||||
}
|
||||
return ScriptResult.Succeed(this);
|
||||
|
||||
case "has-past":
|
||||
// Assert a timeline's past
|
||||
|
@ -130,8 +122,7 @@ public class AdjudicationQueryScriptHandler(
|
|||
// Assert a unit's support was cut
|
||||
|
||||
default:
|
||||
WriteLine($"Unknown assertion \"{args[0]}\"");
|
||||
return !Strict;
|
||||
return ScriptResult.Fail($"Unknown assertion \"{args[0]}\"", this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,30 +9,24 @@ namespace MultiversalDiplomacy.Script;
|
|||
public class GameScriptHandler(
|
||||
Action<string> WriteLine,
|
||||
World world,
|
||||
IPhaseAdjudicator adjudicator,
|
||||
bool strict = false)
|
||||
IPhaseAdjudicator adjudicator)
|
||||
: IScriptHandler
|
||||
{
|
||||
public string Prompt => "orders> ";
|
||||
|
||||
public World World { get; private set; } = world;
|
||||
|
||||
/// <summary>
|
||||
/// Whether unsuccessful commands should terminate the script.
|
||||
/// </summary>
|
||||
public bool Strict { get; } = strict;
|
||||
|
||||
private string? CurrentPower { get; set; } = null;
|
||||
|
||||
public List<Order> Orders { get; } = [];
|
||||
|
||||
public IScriptHandler? HandleInput(string input)
|
||||
public ScriptResult HandleInput(string input)
|
||||
{
|
||||
if (input == "") {
|
||||
CurrentPower = null;
|
||||
return this;
|
||||
return ScriptResult.Succeed(this);
|
||||
}
|
||||
if (input.StartsWith('#')) return this;
|
||||
if (input.StartsWith('#')) return ScriptResult.Succeed(this);
|
||||
|
||||
// "---" submits the orders and allows queries about the outcome
|
||||
if (input == "---") {
|
||||
|
@ -44,7 +38,8 @@ public class GameScriptHandler(
|
|||
.ToList();
|
||||
var adjudication = adjudicator.AdjudicateOrders(World, validOrders);
|
||||
var newWorld = adjudicator.UpdateWorld(World, adjudication);
|
||||
return new AdjudicationQueryScriptHandler(WriteLine, validation, newWorld, adjudicator, Strict);
|
||||
return ScriptResult.Succeed(new AdjudicationQueryScriptHandler(
|
||||
WriteLine, validation, newWorld, adjudicator));
|
||||
}
|
||||
|
||||
// "===" submits the orders and moves immediately to taking the next set of orders
|
||||
|
@ -59,13 +54,13 @@ public class GameScriptHandler(
|
|||
var adjudication = adjudicator.AdjudicateOrders(World, validOrders);
|
||||
World = adjudicator.UpdateWorld(World, adjudication);
|
||||
WriteLine("Ready for orders");
|
||||
return this;
|
||||
return ScriptResult.Succeed(this);
|
||||
}
|
||||
|
||||
// A block of orders for a single power beginning with "{name}:"
|
||||
if (World.Powers.FirstOrDefault(p => input.EqualsAnyCase($"{p}:"), null) is string power) {
|
||||
CurrentPower = power;
|
||||
return this;
|
||||
return ScriptResult.Succeed(this);
|
||||
}
|
||||
|
||||
// If it's not a comment, submit, or order block, assume it's an order.
|
||||
|
@ -79,10 +74,7 @@ public class GameScriptHandler(
|
|||
// Outside a power block, the power is prefixed to each order.
|
||||
Regex re = new($"^{World.Map.PowerRegex}(?:[:])? (.*)$", RegexOptions.IgnoreCase);
|
||||
var match = re.Match(input);
|
||||
if (!match.Success) {
|
||||
WriteLine($"Could not determine ordering power in \"{input}\"");
|
||||
return Strict ? null : this;
|
||||
}
|
||||
if (!match.Success) return ScriptResult.Fail($"Could not determine ordering power in \"{input}\"", this);
|
||||
orderPower = match.Groups[1].Value;
|
||||
orderText = match.Groups[2].Value;
|
||||
}
|
||||
|
@ -90,10 +82,9 @@ public class GameScriptHandler(
|
|||
if (OrderParser.TryParseOrder(World, orderPower, orderText, out Order? order)) {
|
||||
WriteLine($"Parsed {orderPower} order: {order}");
|
||||
Orders.Add(order);
|
||||
return this;
|
||||
return ScriptResult.Succeed(this);
|
||||
}
|
||||
|
||||
WriteLine($"Failed to parse \"{orderText}\"");
|
||||
return Strict ? null : this;
|
||||
return ScriptResult.Fail($"Failed to parse \"{orderText}\"", this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,5 @@ public interface IScriptHandler
|
|||
/// <summary>
|
||||
/// Process a line of input.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The handler that should handle the next line of input, or null if script handling should end.
|
||||
/// </returns>
|
||||
public IScriptHandler? HandleInput(string input);
|
||||
public ScriptResult HandleInput(string input);
|
||||
}
|
||||
|
|
|
@ -10,12 +10,12 @@ public class ReplScriptHandler(Action<string> WriteLine) : IScriptHandler
|
|||
{
|
||||
public string Prompt => "5dp> ";
|
||||
|
||||
public IScriptHandler? HandleInput(string input)
|
||||
public ScriptResult HandleInput(string input)
|
||||
{
|
||||
var args = input.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (args.Length == 0 || input.StartsWith('#'))
|
||||
{
|
||||
return this;
|
||||
return ScriptResult.Succeed(this);
|
||||
}
|
||||
|
||||
var command = args[0];
|
||||
|
@ -50,13 +50,15 @@ public class ReplScriptHandler(Action<string> WriteLine) : IScriptHandler
|
|||
}
|
||||
World world = World.WithMap(Map.FromType(map));
|
||||
WriteLine($"Created a new {map} game");
|
||||
return new SetupScriptHandler(WriteLine, world, MovementPhaseAdjudicator.Instance);
|
||||
return ScriptResult.Succeed(new SetupScriptHandler(
|
||||
WriteLine,
|
||||
world,
|
||||
MovementPhaseAdjudicator.Instance));
|
||||
|
||||
default:
|
||||
WriteLine($"Unrecognized command: \"{command}\"");
|
||||
break;
|
||||
return ScriptResult.Fail($"Unrecognized command: \"{command}\"", this);
|
||||
}
|
||||
|
||||
return this;
|
||||
return ScriptResult.Succeed(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
namespace MultiversalDiplomacy.Script;
|
||||
|
||||
/// <summary>
|
||||
/// The result of an <see cref="IScriptHandler"/> processing a line of input.
|
||||
/// </summary>
|
||||
/// <param name="success">Whether processing was successful.</param>
|
||||
/// <param name="next">The handler to continue script processing with.</param>
|
||||
/// <param name="message">If processing failed, the error message.</param>
|
||||
public class ScriptResult(bool success, IScriptHandler? next, string message)
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether processing was successful.
|
||||
/// </summary>
|
||||
public bool Success { get; } = success;
|
||||
|
||||
/// <summary>
|
||||
/// The handler to continue script processing with.
|
||||
/// </summary>
|
||||
public IScriptHandler? NextHandler { get; } = next;
|
||||
|
||||
/// <summary>
|
||||
/// If processing failed, the error message.
|
||||
/// </summary>
|
||||
public string Message { get; } = message;
|
||||
|
||||
/// <summary>
|
||||
/// Mark the processing as successful and continue processing with the next handler.
|
||||
/// </summary>
|
||||
public static ScriptResult Succeed(IScriptHandler next) => new(true, next, "");
|
||||
|
||||
/// <summary>
|
||||
/// Mark the processing as a failure and optionally continue with the next handler.
|
||||
/// </summary>
|
||||
/// <param name="message">The reason for the processing failure.</param>
|
||||
public static ScriptResult Fail(string message, IScriptHandler? next = null) => new(false, next, message);
|
||||
}
|
|
@ -9,25 +9,19 @@ namespace MultiversalDiplomacy.Script;
|
|||
public class SetupScriptHandler(
|
||||
Action<string> WriteLine,
|
||||
World world,
|
||||
IPhaseAdjudicator adjudicator,
|
||||
bool strict = false)
|
||||
IPhaseAdjudicator adjudicator)
|
||||
: IScriptHandler
|
||||
{
|
||||
public string Prompt => "setup> ";
|
||||
|
||||
public World World { get; private set; } = world;
|
||||
|
||||
/// <summary>
|
||||
/// Whether unsuccessful commands should terminate the script.
|
||||
/// </summary>
|
||||
public bool Strict { get; } = strict;
|
||||
|
||||
public IScriptHandler? HandleInput(string input)
|
||||
public ScriptResult HandleInput(string input)
|
||||
{
|
||||
var args = input.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (args.Length == 0 || input.StartsWith('#'))
|
||||
{
|
||||
return this;
|
||||
return ScriptResult.Succeed(this);
|
||||
}
|
||||
|
||||
var command = args[0];
|
||||
|
@ -47,7 +41,7 @@ public class SetupScriptHandler(
|
|||
case "---":
|
||||
WriteLine("Starting game");
|
||||
WriteLine("Ready for orders");
|
||||
return new GameScriptHandler(WriteLine, World, adjudicator, Strict);
|
||||
return ScriptResult.Succeed(new GameScriptHandler(WriteLine, World, adjudicator));
|
||||
|
||||
case "list" when args.Length == 1:
|
||||
WriteLine("usage:");
|
||||
|
@ -83,18 +77,15 @@ public class SetupScriptHandler(
|
|||
if (OrderParser.TryParseUnit(World, unitSpec, out Unit? newUnit)) {
|
||||
World = World.Update(units: World.Units.Append(newUnit));
|
||||
WriteLine($"Created {newUnit}");
|
||||
return this;
|
||||
return ScriptResult.Succeed(this);
|
||||
}
|
||||
WriteLine($"Could not match unit spec \"{unitSpec}\"");
|
||||
if (Strict) return null;
|
||||
break;
|
||||
return ScriptResult.Fail($"Could not match unit spec \"{unitSpec}\"", this);
|
||||
|
||||
default:
|
||||
WriteLine($"Unrecognized command: \"{command}\"");
|
||||
if (Strict) return null;
|
||||
ScriptResult.Fail($"Unrecognized command: \"{command}\"", this);
|
||||
break;
|
||||
}
|
||||
|
||||
return this;
|
||||
return ScriptResult.Succeed(this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,12 +15,6 @@ public class ReplDriver(IScriptHandler initialHandler, bool echo = false)
|
|||
/// </summary>
|
||||
bool Echo { get; } = echo;
|
||||
|
||||
/// <summary>
|
||||
/// Input a multiline string into the repl. Call <see cref="AssertReady"/> or <see cref="AssertClosed"/> at the end so the
|
||||
/// statement is valid.
|
||||
/// </summary>
|
||||
public ReplDriver this[string input] => ExecuteAll(input);
|
||||
|
||||
public ReplDriver ExecuteAll(string multiline)
|
||||
{
|
||||
var lines = multiline.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
|
@ -33,19 +27,22 @@ public class ReplDriver(IScriptHandler initialHandler, bool echo = false)
|
|||
$"Cannot execute \"{inputLine}\", handler quit. Last input was \"{LastInput}\"");
|
||||
if (Echo) Console.WriteLine($"{Handler.Prompt}{inputLine}");
|
||||
|
||||
Handler = Handler.HandleInput(inputLine);
|
||||
var result = Handler.HandleInput(inputLine);
|
||||
if (!result.Success) Assert.Fail($"Script failed at \"{inputLine}\": {result.Message}");
|
||||
|
||||
Handler = result.NextHandler;
|
||||
LastInput = inputLine;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void AssertReady()
|
||||
public void AssertFails(string inputLine)
|
||||
{
|
||||
if (Handler is null) Assert.Fail($"Handler terminated after \"{LastInput}\"");
|
||||
}
|
||||
if (Handler is null) throw new AssertionException(
|
||||
$"Cannot execute \"{inputLine}\", handler quit. Last input was \"{LastInput}\"");
|
||||
if (Echo) Console.WriteLine($"{Handler.Prompt}{inputLine}");
|
||||
|
||||
public void AssertClosed()
|
||||
{
|
||||
if (Handler is not null) Assert.Fail($"Handler did not terminate after \"{LastInput}\"");
|
||||
var result = Handler.HandleInput(inputLine);
|
||||
if (result.Success) Assert.Fail($"Expected \"{inputLine}\" to fail, but it succeeded.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,21 +10,20 @@ public class ReplTest
|
|||
{
|
||||
private static ReplDriver StandardRepl() => new(
|
||||
new SetupScriptHandler(
|
||||
(msg) => {/* discard*/},
|
||||
(msg) => {/* discard */},
|
||||
World.WithStandardMap(),
|
||||
Adjudicator.MovementPhase,
|
||||
strict: true));
|
||||
Adjudicator.MovementPhase));
|
||||
|
||||
[Test]
|
||||
public void SetupHandler()
|
||||
{
|
||||
var repl = StandardRepl();
|
||||
|
||||
repl["""
|
||||
repl.ExecuteAll("""
|
||||
unit Germany A Munich
|
||||
unit Austria Army Tyrolia
|
||||
unit England F Lon
|
||||
"""].AssertReady();
|
||||
""");
|
||||
|
||||
Assert.That(repl.Handler, Is.TypeOf<SetupScriptHandler>());
|
||||
SetupScriptHandler handler = (SetupScriptHandler)repl.Handler!;
|
||||
|
@ -33,9 +32,7 @@ public class ReplTest
|
|||
Assert.That(handler.World.GetUnitAt("Tyr"), Is.Not.Null);
|
||||
Assert.That(handler.World.GetUnitAt("Lon"), Is.Not.Null);
|
||||
|
||||
repl["""
|
||||
---
|
||||
"""].AssertReady();
|
||||
repl.Execute("---");
|
||||
|
||||
Assert.That(repl.Handler, Is.TypeOf<GameScriptHandler>());
|
||||
}
|
||||
|
@ -45,7 +42,7 @@ public class ReplTest
|
|||
{
|
||||
var repl = StandardRepl();
|
||||
|
||||
repl["""
|
||||
repl.ExecuteAll("""
|
||||
unit Germany A Mun
|
||||
unit Austria A Tyr
|
||||
unit England F Lon
|
||||
|
@ -54,7 +51,7 @@ public class ReplTest
|
|||
Austria: Army Tyrolia - Vienna
|
||||
England:
|
||||
Lon h
|
||||
"""].AssertReady();
|
||||
""");
|
||||
|
||||
Assert.That(repl.Handler, Is.TypeOf<GameScriptHandler>());
|
||||
GameScriptHandler handler = (GameScriptHandler)repl.Handler!;
|
||||
|
@ -66,9 +63,7 @@ public class ReplTest
|
|||
|
||||
World before = handler.World;
|
||||
|
||||
repl["""
|
||||
---
|
||||
"""].AssertReady();
|
||||
repl.Execute("---");
|
||||
|
||||
Assert.That(repl.Handler, Is.TypeOf<AdjudicationQueryScriptHandler>());
|
||||
var newHandler = (AdjudicationQueryScriptHandler)repl.Handler!;
|
||||
|
@ -81,50 +76,41 @@ public class ReplTest
|
|||
{
|
||||
var repl = StandardRepl();
|
||||
|
||||
repl["""
|
||||
repl.ExecuteAll("""
|
||||
unit Germany A Munich
|
||||
---
|
||||
---
|
||||
assert true
|
||||
"""].AssertReady();
|
||||
""");
|
||||
|
||||
repl["assert false"].AssertClosed();
|
||||
repl.AssertFails("assert false");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AssertInvalidOrder()
|
||||
public void AssertOrderValidity()
|
||||
{
|
||||
var repl = StandardRepl();
|
||||
|
||||
repl["""
|
||||
repl.ExecuteAll("""
|
||||
unit Germany A Mun
|
||||
---
|
||||
Germany A Mun - Stp
|
||||
---
|
||||
"""].AssertReady();
|
||||
""");
|
||||
|
||||
// Assertion should pass for an invalid order
|
||||
repl["assert order-invalid Mun"].AssertReady();
|
||||
// Assertion should fail for an invalid order
|
||||
repl["assert order-valid Mun"].AssertClosed();
|
||||
}
|
||||
// Order should be invalid
|
||||
repl.Execute("assert order-invalid Mun");
|
||||
repl.AssertFails("assert order-valid Mun");
|
||||
|
||||
[Test]
|
||||
public void AssertValidOrder()
|
||||
{
|
||||
var repl = StandardRepl();
|
||||
|
||||
repl["""
|
||||
unit Germany A Mun
|
||||
repl.ExecuteAll("""
|
||||
---
|
||||
Germany A Mun - Tyr
|
||||
---
|
||||
"""].AssertReady();
|
||||
""");
|
||||
|
||||
// Assertion should pass for a valid order
|
||||
repl["assert order-valid Mun"].AssertReady();
|
||||
// Assertion should fail for a valid order
|
||||
repl["assert order-invalid Mun"].AssertClosed();
|
||||
// Order should be valid
|
||||
repl.Execute("assert order-valid Mun");
|
||||
repl.AssertFails("assert order-invalid Mun");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -133,16 +119,16 @@ public class ReplTest
|
|||
Assert.Ignore();
|
||||
var repl = StandardRepl();
|
||||
|
||||
repl["""
|
||||
repl.ExecuteAll("""
|
||||
unit England F London
|
||||
---
|
||||
---
|
||||
"""].AssertReady();
|
||||
""");
|
||||
|
||||
// Assertion should pass for a season's past
|
||||
repl["assert has-past a1>a0"].AssertReady();
|
||||
repl.Execute("assert has-past a1>a0");
|
||||
// Assertion should fail for an incorrect past
|
||||
repl["assert has-past a0>a1"].AssertClosed();
|
||||
repl.AssertFails("assert has-past a0>a1");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -151,18 +137,18 @@ public class ReplTest
|
|||
Assert.Ignore();
|
||||
var repl = StandardRepl();
|
||||
|
||||
repl["""
|
||||
repl.ExecuteAll("""
|
||||
unit Germany A Mun
|
||||
unit Austria A Tyr
|
||||
---
|
||||
Germany Mun - Tyr
|
||||
---
|
||||
"""].AssertReady();
|
||||
""");
|
||||
|
||||
// Assertion should pass for a repelled move
|
||||
repl["assert holds Tyr"].AssertReady();
|
||||
repl.Execute("assert holds Tyr");
|
||||
// Assertion should fail for a repelled move
|
||||
repl["assert dislodged Tyr"].AssertClosed();
|
||||
repl.Execute("assert dislodged Tyr");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -171,7 +157,7 @@ public class ReplTest
|
|||
Assert.Ignore();
|
||||
var repl = StandardRepl();
|
||||
|
||||
repl["""
|
||||
repl.ExecuteAll("""
|
||||
unit Germany A Mun
|
||||
unit Germany A Boh
|
||||
unit Austria A Tyr
|
||||
|
@ -179,12 +165,12 @@ public class ReplTest
|
|||
Germany Mun - Tyr
|
||||
Germany Boh s Mun - Tyr
|
||||
---
|
||||
"""].AssertReady();
|
||||
""");
|
||||
|
||||
// Assertion should pass for a dislodge
|
||||
repl["assert dislodged Tyr"].AssertReady();
|
||||
repl.Execute("assert dislodged Tyr");
|
||||
// Assertion should fail for a repelled move
|
||||
repl["assert holds Tyr"].AssertClosed();
|
||||
repl.AssertFails("assert holds Tyr");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -193,17 +179,17 @@ public class ReplTest
|
|||
Assert.Ignore();
|
||||
var repl = StandardRepl();
|
||||
|
||||
repl["""
|
||||
repl.ExecuteAll("""
|
||||
unit Germany A Mun
|
||||
---
|
||||
Germany Mun - Tyr
|
||||
---
|
||||
"""].AssertReady();
|
||||
""");
|
||||
|
||||
// Assertion should pass for a move
|
||||
repl["assert moves Mun"].AssertReady();
|
||||
repl.Execute("assert moves Mun");
|
||||
// Assertion should fail for a successful move
|
||||
repl["assert no-move Mun"].AssertClosed();
|
||||
repl.AssertFails("assert no-move Mun");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -212,18 +198,18 @@ public class ReplTest
|
|||
Assert.Ignore();
|
||||
var repl = StandardRepl();
|
||||
|
||||
repl["""
|
||||
repl.ExecuteAll("""
|
||||
unit Germany A Mun
|
||||
unit Austria A Tyr
|
||||
---
|
||||
Germany Mun - Tyr
|
||||
---
|
||||
"""].AssertReady();
|
||||
""");
|
||||
|
||||
// Assertion should pass for a repelled move
|
||||
repl["assert no-move Mun"].AssertReady();
|
||||
repl.Execute("assert no-move Mun");
|
||||
// Assertion should fail for no move
|
||||
repl["assert moves Mun"].AssertClosed();
|
||||
repl.AssertFails("assert moves Mun");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -232,7 +218,7 @@ public class ReplTest
|
|||
Assert.Ignore();
|
||||
var repl = StandardRepl();
|
||||
|
||||
repl["""
|
||||
repl.ExecuteAll("""
|
||||
unit Germany A Mun
|
||||
unit Germany A Boh
|
||||
unit Austria A Tyr
|
||||
|
@ -241,11 +227,11 @@ public class ReplTest
|
|||
Mun - Tyr
|
||||
Boh s Mun - Tyr
|
||||
---
|
||||
"""].AssertReady();
|
||||
""");
|
||||
|
||||
// `supports` and `cut` are opposites
|
||||
repl["assert supports Boh"].AssertReady();
|
||||
repl["assert cut Boh"].AssertClosed();
|
||||
repl.Execute("assert supports Boh");
|
||||
repl.AssertFails("assert cut Boh");
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -254,7 +240,7 @@ public class ReplTest
|
|||
Assert.Ignore();
|
||||
var repl = StandardRepl();
|
||||
|
||||
repl["""
|
||||
repl.ExecuteAll("""
|
||||
unit Germany A Mun
|
||||
unit Germany A Boh
|
||||
unit Austria A Tyr
|
||||
|
@ -266,10 +252,10 @@ public class ReplTest
|
|||
|
||||
Italy Vienna - Boh
|
||||
---
|
||||
"""].AssertReady();
|
||||
""");
|
||||
|
||||
// `supports` and `cut` are opposites
|
||||
repl["assert cut Boh"].AssertReady();
|
||||
repl["assert supports Boh"].AssertClosed();
|
||||
repl.Execute("assert cut Boh");
|
||||
repl.AssertFails("assert supports Boh");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,15 +22,18 @@ public class ScriptTests
|
|||
Assert.Ignore("Script tests postponed until parsing tests are done");
|
||||
string filename = Path.GetFileName(testScriptPath);
|
||||
int line = 0;
|
||||
IScriptHandler? handler = new SetupScriptHandler(
|
||||
(msg) => {/* discard*/},
|
||||
IScriptHandler handler = new SetupScriptHandler(
|
||||
(msg) => {/* discard */},
|
||||
World.WithStandardMap(),
|
||||
Adjudicator.MovementPhase,
|
||||
strict: true);
|
||||
Adjudicator.MovementPhase);
|
||||
foreach (string input in File.ReadAllLines(testScriptPath)) {
|
||||
line++;
|
||||
handler = handler?.HandleInput(input);
|
||||
if (handler is null) Assert.Fail($"Script {filename} quit unexpectedly at line {line}: \"{input}\"");
|
||||
var result = handler.HandleInput(input);
|
||||
if (!result.Success) throw new AssertionException(
|
||||
$"Script {filename} error at line {line}: {result.Message}");
|
||||
if (result.NextHandler is null) throw new AssertionException(
|
||||
$"Script {filename} quit unexpectedly at line {line}: \"{input}\"");
|
||||
handler = result.NextHandler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue