Compare commits

...

2 Commits

Author SHA1 Message Date
Tim Van Baak 5e2d495fa5 Update script tests 2024-09-06 16:04:25 +00:00
Tim Van Baak 7773c571e3 Add hold-order assertion for unparseable orders 2024-09-06 16:03:43 +00:00
10 changed files with 114 additions and 36 deletions

View File

@ -58,6 +58,14 @@ public class AdjudicationQueryScriptHandler(
private ScriptResult EvaluateAssertion(string assertion) private ScriptResult EvaluateAssertion(string assertion)
{ {
var args = assertion.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries); var args = assertion.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries);
OrderParser re = new(World);
Regex prov = new($"^{re.FullLocation}$", RegexOptions.IgnoreCase);
Match match;
string timeline;
IEnumerable<Season> seasonsInTimeline;
int turn;
Season season;
Province province;
switch (args[0]) switch (args[0])
{ {
@ -67,28 +75,56 @@ public class AdjudicationQueryScriptHandler(
case "false": case "false":
return ScriptResult.Fail("assert false", this); return ScriptResult.Fail("assert false", this);
case "order-valid": case "hold-order":
case "order-invalid": // The hold-order assertion primarily serves to verify that a unit's order was illegal in cases where
OrderParser re = new(World); // a written non-hold order was rejected before order validation and replaced with a hold order.
Regex prov = new($"^{re.FullLocation}$", RegexOptions.IgnoreCase); match = prov.Match(args[1]);
Match match = prov.Match(args[1]);
if (!match.Success) return ScriptResult.Fail($"Could not parse province from \"{args[1]}\"", this);
string timeline = match.Groups[1].Length > 0 timeline = match.Groups[1].Length > 0
? match.Groups[1].Value ? match.Groups[1].Value
: Season.First.Timeline; : Season.First.Timeline;
var seasonsInTimeline = World.Timelines.Seasons.Where(season => season.Timeline == timeline); seasonsInTimeline = World.Timelines.Seasons.Where(season => season.Timeline == timeline);
if (!seasonsInTimeline.Any()) return ScriptResult.Fail($"No seasons in timeline {timeline}", this); if (!seasonsInTimeline.Any()) return ScriptResult.Fail($"No seasons in timeline {timeline}", this);
int turn = match.Groups[4].Length > 0 turn = match.Groups[4].Length > 0
? int.Parse(match.Groups[4].Value) ? int.Parse(match.Groups[4].Value)
// If turn is unspecified, use the second-latest turn in the timeline, // If turn is unspecified, use the second-latest turn in the timeline,
// since we want to assert against the subjects of the orders just adjudicated, // since we want to assert against the subjects of the orders just adjudicated,
// and adjudication created a new set of seasons. // and adjudication created a new set of seasons.
: seasonsInTimeline.Max(season => season.Turn) - 1; : seasonsInTimeline.Max(season => season.Turn) - 1;
Season season = new(timeline, turn); season = new(timeline, turn);
Province province = World.Map.Provinces.Single(province => province.Is(match.Groups[2].Value)); province = World.Map.Provinces.Single(province => province.Is(match.Groups[2].Value));
var matchingHolds = Validations.Where(val
=> val.Valid
&& val.Order is HoldOrder hold
&& hold.Unit.Season == season
&& World.Map.GetLocation(hold.Unit.Location).ProvinceName == province.Name);
if (!matchingHolds.Any()) return ScriptResult.Fail("No matching holds");
return ScriptResult.Succeed(this);
case "order-valid":
case "order-invalid":
match = prov.Match(args[1]);
if (!match.Success) return ScriptResult.Fail($"Could not parse province from \"{args[1]}\"", this);
timeline = match.Groups[1].Length > 0
? match.Groups[1].Value
: Season.First.Timeline;
seasonsInTimeline = World.Timelines.Seasons.Where(season => season.Timeline == timeline);
if (!seasonsInTimeline.Any()) return ScriptResult.Fail($"No seasons in timeline {timeline}", this);
turn = match.Groups[4].Length > 0
? int.Parse(match.Groups[4].Value)
// If turn is unspecified, use the second-latest turn in the timeline,
// since we want to assert against the subjects of the orders just adjudicated,
// and adjudication created a new set of seasons.
: seasonsInTimeline.Max(season => season.Turn) - 1;
season = new(timeline, turn);
province = World.Map.Provinces.Single(province => province.Is(match.Groups[2].Value));
var matching = Validations.Where(val var matching = Validations.Where(val
=> val.Order is UnitOrder order => val.Order is UnitOrder order
@ -106,20 +142,20 @@ public class AdjudicationQueryScriptHandler(
case "has-past": case "has-past":
Regex hasPast = new($"^([a-z]+[0-9]+)>([a-z]+[0-9]+)$"); Regex hasPast = new($"^([a-z]+[0-9]+)>([a-z]+[0-9]+)$");
Match hpMatch = hasPast.Match(args[1]); match = hasPast.Match(args[1]);
if (!hpMatch.Success) return ScriptResult.Fail("Expected format s1>s2", this); if (!match.Success) return ScriptResult.Fail("Expected format s1>s2", this);
Season future = new(hpMatch.Groups[1].Value); Season future = new(match.Groups[1].Value);
if (!World.Timelines.Pasts.TryGetValue(future.Key, out Season? actual)) { if (!World.Timelines.Pasts.TryGetValue(future.Key, out Season? actual)) {
return ScriptResult.Fail($"No such season \"{future}\""); return ScriptResult.Fail($"No such season \"{future}\"");
} }
Season expected = new(hpMatch.Groups[2].Value); Season expected = new(match.Groups[2].Value);
if (actual != expected) return ScriptResult.Fail( if (actual != expected) return ScriptResult.Fail(
$"Expected past of {future} to be {expected}, but it was {actual}"); $"Expected past of {future} to be {expected}, but it was {actual}");
return ScriptResult.Succeed(this); return ScriptResult.Succeed(this);
case "holds": case "not-dislodged":
case "dislodged": case "dislodged":
re = new(World); re = new(World);
prov = new($"^{re.FullLocation}$", RegexOptions.IgnoreCase); prov = new($"^{re.FullLocation}$", RegexOptions.IgnoreCase);
@ -149,7 +185,7 @@ public class AdjudicationQueryScriptHandler(
if (!matchingDislodges.Any()) return ScriptResult.Fail("No matching dislodge decisions"); if (!matchingDislodges.Any()) return ScriptResult.Fail("No matching dislodge decisions");
var isDislodged = matchingDislodges.Cast<IsDislodged>().First(); var isDislodged = matchingDislodges.Cast<IsDislodged>().First();
if (args[0] == "holds" && isDislodged.Outcome != false) { if (args[0] == "not-dislodged" && isDislodged.Outcome != false) {
return ScriptResult.Fail($"Adjudication {isDislodged} is true"); return ScriptResult.Fail($"Adjudication {isDislodged} is true");
} }
if (args[0] == "dislodged" && isDislodged.Outcome != true) { if (args[0] == "dislodged" && isDislodged.Outcome != true) {

View File

@ -133,6 +133,24 @@ public class ReplTest
repl.AssertFails("assert has-past a2>a1"); repl.AssertFails("assert has-past a2>a1");
} }
[Test]
public void AssertHoldOrder()
{
var repl = StandardRepl();
repl.ExecuteAll("""
unit Germany A Mun
---
""");
repl.AssertFails("Germany A Mun - The Sun");
repl.Execute("---");
// Order is invalid
repl.Execute("assert hold-order Mun");
// order-invalid requires the order be parsable, which this isn't
repl.AssertFails("assert order-invalid Mun");
}
[Test] [Test]
public void AssertMovement() public void AssertMovement()
{ {
@ -241,7 +259,7 @@ public class ReplTest
"""); """);
// Move repelled // Move repelled
repl.Execute("assert holds Tyr"); repl.Execute("assert not-dislodged Tyr");
repl.AssertFails("assert dislodged Tyr"); repl.AssertFails("assert dislodged Tyr");
repl.ExecuteAll(""" repl.ExecuteAll("""
@ -253,6 +271,6 @@ public class ReplTest
// Move succeeds // Move succeeds
repl.Execute("assert dislodged Tyr"); repl.Execute("assert dislodged Tyr");
repl.AssertFails("assert holds Tyr"); repl.AssertFails("assert not-dislodged Tyr");
} }
} }

View File

@ -19,21 +19,37 @@ public class ScriptTests
[TestCaseSource(nameof(DatcTestCases))] [TestCaseSource(nameof(DatcTestCases))]
public void Test_DATC(string testScriptPath) public void Test_DATC(string testScriptPath)
{ {
Assert.Ignore("Script tests postponed until parsing tests are done");
string filename = Path.GetFileName(testScriptPath); string filename = Path.GetFileName(testScriptPath);
int line = 0; int line = 0;
bool expectFailure = false;
IScriptHandler handler = new SetupScriptHandler( IScriptHandler handler = new SetupScriptHandler(
(msg) => {/* discard */}, (msg) => {/* discard */},
World.WithStandardMap(), World.WithStandardMap(),
Adjudicator.MovementPhase); Adjudicator.MovementPhase);
foreach (string input in File.ReadAllLines(testScriptPath)) { foreach (string input in File.ReadAllLines(testScriptPath)) {
line++; line++;
// Handle test directives
if (input == "#test:skip") {
Assert.Ignore($"Script {filename} skipped at line {line}");
}
if (input == "#test:fails") {
expectFailure = true;
continue;
}
var result = handler.HandleInput(input); var result = handler.HandleInput(input);
if (!result.Success) throw new AssertionException( if (expectFailure && result.Success) throw new AssertionException(
$"Script {filename} expected line {line} to fail, but it succeeded");
if (!expectFailure && !result.Success) throw new AssertionException(
$"Script {filename} error at line {line}: {result.Message}"); $"Script {filename} error at line {line}: {result.Message}");
if (result.NextHandler is null) throw new AssertionException( if (result.NextHandler is null) throw new AssertionException(
$"Script {filename} quit unexpectedly at line {line}: \"{input}\""); $"Script {filename} quit unexpectedly at line {line}: \"{input}\"");
handler = result.NextHandler; handler = result.NextHandler;
expectFailure = false;
} }
} }
} }

View File

@ -11,4 +11,4 @@ F North Sea - Picardy
--- ---
# Order should fail. # Order should fail.
assert North Sea holds assert hold-order North Sea

View File

@ -6,9 +6,10 @@ unit England A Liverpool
--- ---
England: England:
#test:fails
A Liverpool - Irish Sea A Liverpool - Irish Sea
--- ---
# Order should fail. # Order should fail.
assert Liverpool holds assert hold-order Liverpool

View File

@ -1,14 +1,15 @@
# 6.A.3. TEST CASE, MOVE FLEET TO LAND # 6.A.3. TEST CASE, MOVE FLEET TO LAND
# Check whether a fleet cannot move to land. # Check whether a fleet cannot move to land.
unit Germany Army Kiel unit Germany F Kiel
--- ---
Germany: Germany:
#test:fails
F Kiel - Munich F Kiel - Munich
--- ---
# Order should fail. # Order should fail.
assert Kiel holds assert hold-order Kiel

View File

@ -1,7 +1,7 @@
# 6.A.4. TEST CASE, MOVE TO OWN SECTOR # 6.A.4. TEST CASE, MOVE TO OWN SECTOR
# Moving to the same sector is an illegal move (2023 rulebook, page 7, "An Army can be ordered to move into an adjacent inland or coastal province."). # Moving to the same sector is an illegal move (2023 rulebook, page 7, "An Army can be ordered to move into an adjacent inland or coastal province.").
unit Germany Army Kiel unit Germany F Kiel
--- ---
@ -11,4 +11,4 @@ F Kiel - Kiel
--- ---
# Program should not crash. # Program should not crash.
assert Kiel holds assert hold-order Kiel

View File

@ -1,6 +1,9 @@
# 6.A.5. TEST CASE, MOVE TO OWN SECTOR WITH CONVOY # 6.A.5. TEST CASE, MOVE TO OWN SECTOR WITH CONVOY
# Moving to the same sector is still illegal with convoy (2023 rulebook, page 7, "Note: An Army can move across water provinces from one coastal province to another..."). # Moving to the same sector is still illegal with convoy (2023 rulebook, page 7, "Note: An Army can move across water provinces from one coastal province to another...").
# TODO convoy order parsing
#test:skip
unit England F North Sea unit England F North Sea
unit England A Yorkshire unit England A Yorkshire
unit England A Liverpool unit England A Liverpool
@ -21,11 +24,11 @@ A Wales Supports F London - Yorkshire
--- ---
# The move of the army in Yorkshire is illegal. # The move of the army in Yorkshire is illegal.
assert Yorkshire holds assert hold-order Yorkshire
# This makes the support of Liverpool also illegal and without the support, the Germans have a stronger force. # This makes the support of Liverpool also illegal and without the support, the Germans have a stronger force.
assert North Sea holds assert hold-order North Sea
assert Liverpool holds assert hold-order Liverpool
assert London moves assert moves London
# The army in London dislodges the army in Yorkshire. # The army in London dislodges the army in Yorkshire.
assert Wales supports assert support-given Wales
assert Yorkshire dislodged assert dislodged Yorkshire

View File

@ -13,4 +13,4 @@ F London - North Sea
--- ---
# Order should fail. # Order should fail.
assert London holds assert hold-order London

View File

@ -1,8 +1,11 @@
# 6.A.7. TEST CASE, ONLY ARMIES CAN BE CONVOYED # 6.A.7. TEST CASE, ONLY ARMIES CAN BE CONVOYED
# A fleet cannot be convoyed. # A fleet cannot be convoyed.
# TODO convoy order parsing
#test:skip
unit England F London unit England F London
unit England North Sea unit England F North Sea
--- ---
@ -13,4 +16,4 @@ F North Sea Convoys A London - Belgium
--- ---
# Move from London to Belgium should fail. # Move from London to Belgium should fail.
assert London holds assert hold-order London