Compare commits
2 Commits
4096e4d517
...
5e2d495fa5
Author | SHA1 | Date |
---|---|---|
Tim Van Baak | 5e2d495fa5 | |
Tim Van Baak | 7773c571e3 |
|
@ -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) {
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,4 +11,4 @@ F North Sea - Picardy
|
||||||
---
|
---
|
||||||
|
|
||||||
# Order should fail.
|
# Order should fail.
|
||||||
assert North Sea holds
|
assert hold-order North Sea
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -13,4 +13,4 @@ F London - North Sea
|
||||||
---
|
---
|
||||||
|
|
||||||
# Order should fail.
|
# Order should fail.
|
||||||
assert London holds
|
assert hold-order London
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue