5dplomacy/MultiversalDiplomacyTests/OrderParserTest.cs

390 lines
18 KiB
C#
Raw Normal View History

2024-08-20 14:39:49 +00:00
using NUnit.Framework;
using MultiversalDiplomacy.Model;
2024-08-27 02:43:12 +00:00
using MultiversalDiplomacy.Orders;
2024-08-20 14:39:49 +00:00
namespace MultiversalDiplomacyTests;
public class OrderParserTest
2024-08-20 14:39:49 +00:00
{
2024-08-26 15:39:42 +00:00
private static TestCaseData Test(string order, params string[] expected)
=> new TestCaseData(order, expected).SetName($"{{m}}(\"{order}\")");
static IEnumerable<TestCaseData> HoldRegexMatchesTestCases()
{
// Full specification
yield return Test(
"Army a-Munich/l@0 holds",
"Army", "a", "Munich", "l", "0", "holds");
// Case insensitivity
yield return Test(
"fleet B-lon/C@0 H",
"fleet", "B", "lon", "C", "0", "H");
// All optionals missing
yield return Test(
"ROM h",
"", "", "ROM", "", "", "h");
// No confusion of unit type and timeline
yield return Test(
"A F-STP hold",
"A", "F", "STP", "", "", "hold");
// Province with space in name
yield return Test(
"Fleet North Sea Hold",
"Fleet", "", "North Sea", "", "", "Hold");
// Parenthesis location
yield return Test(
"F Spain(nc) holds",
"F", "", "Spain", "nc", "", "holds");
}
[TestCaseSource(nameof(HoldRegexMatchesTestCases))]
public void HoldRegexMatches(string order, string[] expected)
{
2024-08-27 03:23:28 +00:00
OrderParser re = new(World.WithStandardMap());
var match = re.Hold.Match(order);
Assert.True(match.Success, "Match failed");
2024-08-27 03:23:28 +00:00
var (type, timeline, province, location, turn, holdVerb) = OrderParser.ParseHold(match);
2024-08-26 15:39:42 +00:00
string[] actual = [type, timeline, province, location, turn, holdVerb];
// Use EquivalentTo for more detailed error message
Assert.That(actual, Is.EquivalentTo(expected), "Unexpected parse results");
Assert.That(actual, Is.EqualTo(expected), "Unexpected parse results");
}
2024-08-26 15:39:42 +00:00
static IEnumerable<TestCaseData> MoveRegexMatchesTestCases()
2024-08-20 14:39:49 +00:00
{
2024-08-26 15:39:42 +00:00
// Full specification
yield return Test(
"Army a-Munich/l@0 - a-Tyrolia/l@0",
"Army", "a", "Munich", "l", "0", "-", "a", "Tyrolia", "l", "0", "");
// Case insensitivity
yield return Test(
"fleet B-lon/C@0 - B-enc/W@0",
"fleet", "B", "lon", "C", "0", "-", "B", "enc", "W", "0", "");
// All optionals missing
yield return Test(
"ROM - VIE",
"", "", "ROM", "", "", "-", "", "VIE", "", "", "");
// No confusion of unit type and timeline
yield return Test(
"A F-STP - MOS",
"A", "F", "STP", "", "", "-", "", "MOS", "", "", "");
2024-08-26 16:32:33 +00:00
// No confusion of timeline and hold verb
yield return Test(
"A Mun - h-Tyr",
"A", "", "Mun", "", "", "-", "h", "Tyr", "", "", "");
// No confusion of timeline and support verb
yield return Test(
"A Mun - s-Tyr",
"A", "", "Mun", "", "", "-", "s", "Tyr", "", "", "");
2024-08-26 15:39:42 +00:00
// Elements with spaces
yield return Test(
2024-08-26 16:32:33 +00:00
"Western Mediterranean Sea moves to Gulf of Lyons via convoy",
"", "", "Western Mediterranean Sea", "", "", "moves to", "", "Gulf of Lyons", "", "", "via convoy");
2024-08-26 15:39:42 +00:00
// Parenthesis location
yield return Test(
"F Spain(nc) - Spain(sc)",
"F", "", "Spain", "nc", "", "-", "", "Spain", "sc", "", "");
// Timeline designation spells out a province
yield return Test(
"A tyr-MUN(vie) - mun-TYR/vie",
"A", "tyr", "MUN", "vie", "", "-", "mun", "TYR", "vie", "", "");
}
2024-08-26 15:39:42 +00:00
[TestCaseSource(nameof(MoveRegexMatchesTestCases))]
public void MoveRegexMatches(string order, string[] expected)
{
2024-08-27 03:23:28 +00:00
OrderParser re = new(World.WithStandardMap());
2024-08-26 15:39:42 +00:00
var match = re.Move.Match(order);
Assert.True(match.Success, "Match failed");
var (type, timeline, province, location, turn, moveVerb,
2024-08-27 03:23:28 +00:00
destTimeline, destProvince, destLocation, destTurn, viaConvoy) = OrderParser.ParseMove(match);
2024-08-26 15:39:42 +00:00
string[] actual = [type, timeline, province, location, turn, moveVerb,
destTimeline, destProvince, destLocation, destTurn, viaConvoy];
// Use EquivalentTo for more detailed error message
Assert.That(actual, Is.EquivalentTo(expected), "Unexpected parse results");
Assert.That(actual, Is.EqualTo(expected), "Unexpected parse results");
2024-08-20 14:39:49 +00:00
}
2024-08-26 16:32:33 +00:00
static IEnumerable<TestCaseData> SupportHoldRegexMatchesTestCases()
{
// Full specification
yield return Test(
"Army a-Munich/l@0 s A a-Tyrolia/l@0",
"Army", "a", "Munich", "l", "0", "s", "A", "a", "Tyrolia", "l", "0");
// Case insensitivity
yield return Test(
"fleet B-lon/C@0 SUPPORTS B-enc/W@0",
"fleet", "B", "lon", "C", "0", "SUPPORTS", "", "B", "enc", "W", "0");
// All optionals missing
yield return Test(
"ROM s VIE",
"", "", "ROM", "", "", "s", "", "", "VIE", "", "");
// No confusion of unit type and timeline
yield return Test(
"A F-STP s MOS",
"A", "F", "STP", "", "", "s", "", "", "MOS", "", "");
// No confusion of timeline and support verb
yield return Test(
"A Mun s Tyr",
"A", "", "Mun", "", "", "s", "", "", "Tyr", "", "");
// Elements with spaces
yield return Test(
"Western Mediterranean Sea supports Gulf of Lyons",
"", "", "Western Mediterranean Sea", "", "", "supports", "", "", "Gulf of Lyons", "", "");
// Parenthesis location
yield return Test(
"F Spain(nc) s Spain(sc)",
"F", "", "Spain", "nc", "", "s", "", "", "Spain", "sc", "");
// Timeline designation spells out a province
yield return Test(
"A tyr-MUN(vie) s mun-TYR/vie",
"A", "tyr", "MUN", "vie", "", "s", "", "mun", "TYR", "vie", "");
}
[TestCaseSource(nameof(SupportHoldRegexMatchesTestCases))]
public void SupportHoldRegexMatches(string order, string[] expected)
{
2024-08-27 03:23:28 +00:00
OrderParser re = new(World.WithStandardMap());
2024-08-26 16:32:33 +00:00
var match = re.SupportHold.Match(order);
Assert.True(match.Success, "Match failed");
var (type, timeline, province, location, turn, supportVerb,
2024-08-27 03:23:28 +00:00
targetType, targetTimeline, targetProvince, targetLocation, targetTurn) = OrderParser.ParseSupportHold(match);
2024-08-26 16:32:33 +00:00
string[] actual = [type, timeline, province, location, turn, supportVerb,
targetType, targetTimeline, targetProvince, targetLocation, targetTurn];
// Use EquivalentTo for more detailed error message
Assert.That(actual, Is.EquivalentTo(expected), "Unexpected parse results");
Assert.That(actual, Is.EqualTo(expected), "Unexpected parse results");
}
2024-08-26 17:47:52 +00:00
static IEnumerable<TestCaseData> SupportMoveRegexMatchesTestCases()
{
// Full specification
yield return Test(
"Army a-Munich/l@0 s A a-Tyrolia/l@0 - a-Vienna/l@0",
"Army", "a", "Munich", "l", "0", "s", "A", "a", "Tyrolia", "l", "0", "-", "a", "Vienna", "l", "0");
// Case insensitivity
yield return Test(
"fleet B-lon/C@0 SUPPORTS B-enc/W@0 MOVE TO B-nts/W@0",
"fleet", "B", "lon", "C", "0", "SUPPORTS", "", "B", "enc", "W", "0", "MOVE TO", "B", "nts", "W", "0");
// All optionals missing
yield return Test(
"ROM s VIE - TYR",
"", "", "ROM", "", "", "s", "", "", "VIE", "", "", "-", "", "TYR", "", "");
// No confusion of unit type and timeline
yield return Test(
"A F-STP S MOS - A-UKR",
"A", "F", "STP", "", "", "S", "", "", "MOS", "", "", "-", "A", "UKR", "", "");
// Elements with spaces
yield return Test(
"Western Mediterranean Sea supports Gulf of Lyons move to North Sea",
"", "", "Western Mediterranean Sea", "", "", "supports", "", "", "Gulf of Lyons", "", "", "move to", "", "North Sea", "", "");
}
[TestCaseSource(nameof(SupportMoveRegexMatchesTestCases))]
public void SupportMoveRegexMatches(string order, string[] expected)
{
2024-08-27 03:23:28 +00:00
OrderParser re = new(World.WithStandardMap());
2024-08-26 17:47:52 +00:00
var match = re.SupportMove.Match(order);
Assert.True(match.Success, "Match failed");
var (type, timeline, province, location, turn, supportVerb,
targetType, targetTimeline, targetProvince, targetLocation, targetTurn, moveVerb,
2024-08-27 03:23:28 +00:00
destTimeline, destProvince, destLocation, destTurn) = OrderParser.ParseSupportMove(match);
2024-08-26 17:47:52 +00:00
string[] actual = [type, timeline, province, location, turn, supportVerb,
targetType, targetTimeline, targetProvince, targetLocation, targetTurn, moveVerb,
destTimeline, destProvince, destLocation, destTurn];
// Use EquivalentTo for more detailed error message
Assert.That(actual, Is.EquivalentTo(expected), "Unexpected parse results");
Assert.That(actual, Is.EqualTo(expected), "Unexpected parse results");
2024-09-07 02:45:13 +00:00
}
static IEnumerable<TestCaseData> ConvoyRegexMatchesTestCases()
{
// Full specification
yield return Test(
"Fleet a-Nth/w@0 c A a-London/l@0 - a-Belgium/l@0",
"Fleet", "a", "Nth", "w", "0", "c", "A", "a", "London", "l", "0", "-", "a", "Belgium", "l", "0");
// Case insensitivity
yield return Test(
"fleet B-nth/W@0 CONVOYS a B-lon/L@0 MOVE TO B-bel/L@0",
"fleet", "B", "nth", "W", "0", "CONVOYS", "a", "B", "lon", "L", "0", "MOVE TO", "B", "bel", "L", "0");
// All optionals missing
yield return Test(
"TYN c ROM - TUN",
"", "", "TYN", "", "", "c", "", "", "ROM", "", "", "-", "", "TUN", "", "");
// No confusion of unit type and timeline
yield return Test(
"F A-BOT C FIN - A-LVN",
"F", "A", "BOT", "", "", "C", "", "", "FIN", "", "", "-", "A", "LVN", "", "");
// Elements with spaces
yield return Test(
"Western Mediterranean Sea convoys Spain move to North Africa",
"", "", "Western Mediterranean Sea", "", "", "convoys", "", "", "Spain", "", "", "move to", "", "North Africa", "", "");
}
[TestCaseSource(nameof(ConvoyRegexMatchesTestCases))]
public void ConvoyRegexMatches(string order, string[] expected)
{
OrderParser re = new(World.WithStandardMap());
var match = re.Convoy.Match(order);
Assert.True(match.Success, "Match failed");
var (type, timeline, province, location, turn, convoyVerb,
targetType, targetTimeline, targetProvince, targetLocation, targetTurn, moveVerb,
destTimeline, destProvince, destLocation, destTurn) = OrderParser.ParseConvoy(match);
string[] actual = [type, timeline, province, location, turn, convoyVerb,
targetType, targetTimeline, targetProvince, targetLocation, targetTurn, moveVerb,
destTimeline, destProvince, destLocation, destTurn];
// Use EquivalentTo for more detailed error message
Assert.That(actual, Is.EquivalentTo(expected), "Unexpected parse results");
Assert.That(actual, Is.EqualTo(expected), "Unexpected parse results");
2024-08-26 17:47:52 +00:00
}
2024-08-27 02:43:12 +00:00
[Test]
public void OrderParsingTest()
{
World world = World.WithStandardMap().AddUnits("Germany A Mun");
2024-08-27 03:23:28 +00:00
OrderParser re = new(world);
2024-08-27 02:43:12 +00:00
var match = re.Move.Match("A Mun - Tyr");
2024-08-27 03:23:28 +00:00
var success = OrderParser.TryParseMoveOrder(world, "Germany", match, out Order? order);
2024-08-27 02:43:12 +00:00
Assert.That(success, Is.True);
Assert.That(order, Is.TypeOf<MoveOrder>());
MoveOrder move = (MoveOrder)order!;
Assert.That(move.Power, Is.EqualTo("Germany"));
Assert.That(move.Unit.Key, Is.EqualTo("A a-Munich/l@0"));
Assert.That(move.Location, Is.EqualTo("Tyrolia/l"));
Assert.That(move.Season.Key, Is.EqualTo("a0"));
}
[Test]
public void OrderDisambiguation()
{
World world = World.WithStandardMap().AddUnits("Germany A Mun");
OrderParser.TryParseOrder(world, "Germany", "Mun h", out Order? parsed);
Assert.That(parsed?.ToString(), Is.EqualTo("G A a-Munich/l@0 holds"));
}
[Test]
public void UnitTypeDisambiguatesCoastalLocation()
{
World world = World.WithStandardMap().AddUnits("England F Nth", "Germany A Ruhr");
Assert.That(
OrderParser.TryParseOrder(world, "England", "North Sea - Holland", out Order? fleetOrder),
"Failed to parse fleet order");
Assert.That(
OrderParser.TryParseOrder(world, "Germany", "Ruhr - Holland", out Order? armyOrder),
"Failed to parse army order");
Assert.That(fleetOrder, Is.TypeOf<MoveOrder>(), "Unexpected fleet order");
Assert.That(armyOrder, Is.TypeOf<MoveOrder>(), "Unexpected army order");
Location fleetDest = world.Map.GetLocation(((MoveOrder)fleetOrder!).Location);
Location armyDest = world.Map.GetLocation(((MoveOrder)armyOrder!).Location);
Assert.That(fleetDest.ProvinceName, Is.EqualTo(armyDest.ProvinceName));
Assert.That(fleetDest.Type, Is.EqualTo(LocationType.Water), "Unexpected fleet movement location");
Assert.That(armyDest.Type, Is.EqualTo(LocationType.Land), "Unexpected army movement location");
}
[Test]
public void UnitTypeOverrulesNonsenseLocation()
{
World world = World.WithStandardMap().AddUnits("England F Nth", "Germany A Ruhr");
Assert.That(
OrderParser.TryParseOrder(world, "England", "F North Sea - Holland/l", out Order? fleetOrder),
"Failed to parse fleet order");
Assert.That(
OrderParser.TryParseOrder(world, "Germany", "A Ruhr - Holland/w", out Order? armyOrder),
"Failed to parse army order");
Assert.That(fleetOrder, Is.TypeOf<MoveOrder>(), "Unexpected fleet order");
Assert.That(armyOrder, Is.TypeOf<MoveOrder>(), "Unexpected army order");
Location fleetDest = world.Map.GetLocation(((MoveOrder)fleetOrder!).Location);
Location armyDest = world.Map.GetLocation(((MoveOrder)armyOrder!).Location);
Assert.That(fleetDest.ProvinceName, Is.EqualTo(armyDest.ProvinceName));
Assert.That(fleetDest.Type, Is.EqualTo(LocationType.Water), "Unexpected fleet movement location");
Assert.That(armyDest.Type, Is.EqualTo(LocationType.Land), "Unexpected army movement location");
}
[Test]
public void DisambiguateSingleAccessibleCoast()
{
World world = World.WithStandardMap().AddUnits("France F Gascony", "France F Marseilles");
Assert.That(
OrderParser.TryParseOrder(world, "France", "Gascony - Spain", out Order? northOrder),
"Failed to parse north coast order");
Assert.That(
OrderParser.TryParseOrder(world, "France", "Marseilles - Spain", out Order? southOrder),
"Failed to parse south coast order");
Assert.That(northOrder, Is.TypeOf<MoveOrder>(), "Unexpected north coast order");
Assert.That(southOrder, Is.TypeOf<MoveOrder>(), "Unexpected south coast order");
Location north = world.Map.GetLocation(((MoveOrder)northOrder!).Location);
Location south = world.Map.GetLocation(((MoveOrder)southOrder!).Location);
Assert.That(north.Name, Is.EqualTo("north coast"), "Unexpected disambiguation");
Assert.That(south.Name, Is.EqualTo("south coast"), "Unexpected disambiguation");
}
[Test]
public void DisambiguateMultipleAccessibleCoasts()
{
World world = World.WithStandardMap().AddUnits("France F Portugal");
Assert.That(
OrderParser.TryParseOrder(world, "France", "Portugal - Spain", out Order? _),
Is.False,
"Should not parse ambiguous coastal move");
}
[Test]
public void DisambiguateSupportToSingleAccessibleCoast()
{
World world = World.WithStandardMap().AddUnits("France F Gascony", "France F Marseilles");
Assert.That(
OrderParser.TryParseOrder(world, "France", "Gascony S Marseilles - Spain", out Order? northOrder),
"Failed to parse north coast order");
Assert.That(
OrderParser.TryParseOrder(world, "France", "Marseilles S Gascony - Spain", out Order? southOrder),
"Failed to parse south coast order");
Assert.That(northOrder, Is.TypeOf<SupportMoveOrder>(), "Unexpected north coast order");
Assert.That(southOrder, Is.TypeOf<SupportMoveOrder>(), "Unexpected south coast order");
Location northTarget = ((SupportMoveOrder)northOrder!).Location;
Location southTarget = ((SupportMoveOrder)southOrder!).Location;
Assert.That(northTarget.Name, Is.EqualTo("south coast"), "Unexpected disambiguation");
Assert.That(southTarget.Name, Is.EqualTo("north coast"), "Unexpected disambiguation");
}
[Test]
public void DisambiguateSupportToMultipleAccessibleCoasts()
{
World world = World.WithStandardMap().AddUnits("France F Portugal", "France F Marseilles");
Assert.That(
OrderParser.TryParseOrder(world, "France", "Marseilles S Portugal - Spain", out Order? _),
Is.False,
"Should not parse ambiguous coastal support");
}
2024-09-07 03:01:44 +00:00
[Test]
public void DisambiguateConvoyDestination()
{
World world = World.WithStandardMap().AddUnits("France F MID", "France A Brest");
Assert.That(
OrderParser.TryParseOrder(world, "France", "MID C Brest - Spain", out Order? order),
"Failed to parse convoy order");
Assert.That(order, Is.TypeOf<ConvoyOrder>(), "Unexpected order type");
Location dest = ((ConvoyOrder)order!).Location;
Assert.That(dest.Name, Is.EqualTo("land"), "Unexpected destination location");
}
}