Add order references to test case builder
This allows assertions to be made more easily against orders in a test case.
This commit is contained in:
parent
be3f6a527f
commit
00cac2cb89
|
@ -0,0 +1,69 @@
|
||||||
|
using MultiversalDiplomacy.Adjudicate;
|
||||||
|
using MultiversalDiplomacy.Orders;
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
|
||||||
|
namespace MultiversalDiplomacyTests;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An object that provides a view into an order's fate during a test case.
|
||||||
|
/// </summary>
|
||||||
|
public class OrderReference<OrderType> where OrderType : Order
|
||||||
|
{
|
||||||
|
private TestCaseBuilder Builder { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The order.
|
||||||
|
/// </summary>
|
||||||
|
public OrderType Order { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The validation result for the order. Throws if validation has not occurred.
|
||||||
|
/// </summary>
|
||||||
|
public OrderValidation Validation
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (this.Builder.ValidationResults == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Validation has not been done yet");
|
||||||
|
}
|
||||||
|
var orderValidation = this.Builder.ValidationResults.Where(v => this.Order == v.Order);
|
||||||
|
if (!orderValidation.Any())
|
||||||
|
{
|
||||||
|
throw new AssertionException($"Missing validation for {this.Order}");
|
||||||
|
}
|
||||||
|
return orderValidation.Single();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The order that replaced this order, if any. Throws if validation has not occurred.
|
||||||
|
/// </summary>
|
||||||
|
public OrderValidation? Replacement
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (this.Builder.ValidationResults == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Validation has not been done yet");
|
||||||
|
}
|
||||||
|
if (this.Order is UnitOrder unitOrder)
|
||||||
|
{
|
||||||
|
var replacementOrder = this.Builder.ValidationResults.Where(
|
||||||
|
v => v.Order is UnitOrder uo && uo != unitOrder && uo.Unit == unitOrder.Unit);
|
||||||
|
if (replacementOrder.Any())
|
||||||
|
{
|
||||||
|
return replacementOrder.Single();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderReference(TestCaseBuilder builder, OrderType order)
|
||||||
|
{
|
||||||
|
this.Builder = builder;
|
||||||
|
this.Order = order;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,9 @@ namespace MultiversalDiplomacyTests;
|
||||||
|
|
||||||
public class TestAdjudicator : IPhaseAdjudicator
|
public class TestAdjudicator : IPhaseAdjudicator
|
||||||
{
|
{
|
||||||
|
public static Func<World, List<Order>, List<OrderValidation>> RubberStamp =
|
||||||
|
(world, orders) => orders.Select(o => o.Validate(ValidationReason.Valid)).ToList();
|
||||||
|
|
||||||
private Func<World, List<Order>, List<OrderValidation>> ValidateOrdersCallback;
|
private Func<World, List<Order>, List<OrderValidation>> ValidateOrdersCallback;
|
||||||
|
|
||||||
public TestAdjudicator(
|
public TestAdjudicator(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
using MultiversalDiplomacy.Adjudicate;
|
||||||
using MultiversalDiplomacy.Model;
|
using MultiversalDiplomacy.Model;
|
||||||
using MultiversalDiplomacy.Orders;
|
using MultiversalDiplomacy.Orders;
|
||||||
|
|
||||||
|
@ -44,12 +45,12 @@ public class TestCaseBuilder
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Give the unit a hold order.
|
/// Give the unit a hold order.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPowerContext Holds();
|
public IOrderDefinedContext<HoldOrder> Holds();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Give the unit a move order.
|
/// Give the unit a move order.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPowerContext MovesTo(string provinceName, string? coast = null);
|
public IOrderDefinedContext<MoveOrder> MovesTo(string provinceName, string? coast = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Give the unit a convoy order.
|
/// Give the unit a convoy order.
|
||||||
|
@ -89,7 +90,7 @@ public class TestCaseBuilder
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Define the destination of the convoy order.
|
/// Define the destination of the convoy order.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPowerContext To(string provinceName);
|
public IOrderDefinedContext<ConvoyOrder> To(string provinceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -119,18 +120,47 @@ public class TestCaseBuilder
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Give the unit an order to support the target's hold order.
|
/// Give the unit an order to support the target's hold order.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPowerContext Hold();
|
public IOrderDefinedContext<SupportHoldOrder> Hold();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Give the unit an order to support the target's move order.
|
/// Give the unit an order to support the target's move order.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPowerContext MoveTo(string provinceName, string? coast = null);
|
public IOrderDefinedContext<SupportMoveOrder> MoveTo(string provinceName, string? coast = null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Context for additional operations on a defined order or defining another order. This
|
||||||
|
/// context mimics the <see cref="IPowerContext"/> with additional functionality related to
|
||||||
|
/// the order that was just defined.
|
||||||
|
/// </summary>
|
||||||
|
public interface IOrderDefinedContext<OrderType> where OrderType : Order
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Get the context for defining the orders for another power.
|
||||||
|
/// </summary>
|
||||||
|
public IPowerContext this[string powerName] { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Define an order for a new army in a province.
|
||||||
|
/// </summary>
|
||||||
|
public IUnitContext Army(string provinceName);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Define an order for a new fleet in a province, optionally on a specific coast.
|
||||||
|
/// </summary>
|
||||||
|
public IUnitContext Fleet(string provinceName, string? coast = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save a reference to the order just defined.
|
||||||
|
/// </summary>
|
||||||
|
public IOrderDefinedContext<OrderType> GetReference(out OrderReference<OrderType> order);
|
||||||
}
|
}
|
||||||
|
|
||||||
public World World { get; private set; }
|
public World World { get; private set; }
|
||||||
public ReadOnlyCollection<Order> Orders { get; }
|
public ReadOnlyCollection<Order> Orders { get; }
|
||||||
private List<Order> OrderList;
|
private List<Order> OrderList;
|
||||||
private Season Season;
|
private Season Season;
|
||||||
|
public List<OrderValidation>? ValidationResults { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a test case builder that will operate on a world.
|
/// Create a test case builder that will operate on a world.
|
||||||
|
@ -138,9 +168,10 @@ public class TestCaseBuilder
|
||||||
public TestCaseBuilder(World world, Season? season = null)
|
public TestCaseBuilder(World world, Season? season = null)
|
||||||
{
|
{
|
||||||
this.World = world;
|
this.World = world;
|
||||||
this.OrderList = new List<Order>();
|
this.OrderList = new();
|
||||||
this.Orders = new(this.OrderList);
|
this.Orders = new(this.OrderList);
|
||||||
this.Season = season ?? this.World.Seasons.First();
|
this.Season = season ?? this.World.Seasons.First();
|
||||||
|
this.ValidationResults = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -188,6 +219,12 @@ public class TestCaseBuilder
|
||||||
return newUnit;
|
return newUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<OrderValidation> ValidateOrders(IPhaseAdjudicator adjudicator)
|
||||||
|
{
|
||||||
|
this.ValidationResults = adjudicator.ValidateOrders(this.World, this.Orders.ToList());
|
||||||
|
return this.ValidationResults;
|
||||||
|
}
|
||||||
|
|
||||||
private class PowerContext : IPowerContext
|
private class PowerContext : IPowerContext
|
||||||
{
|
{
|
||||||
public TestCaseBuilder Builder;
|
public TestCaseBuilder Builder;
|
||||||
|
@ -241,17 +278,17 @@ public class TestCaseBuilder
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Order a unit to hold.
|
/// Order a unit to hold.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPowerContext Holds()
|
public IOrderDefinedContext<HoldOrder> Holds()
|
||||||
{
|
{
|
||||||
HoldOrder order = new HoldOrder(this.PowerContext.Power, this.Unit);
|
HoldOrder order = new HoldOrder(this.PowerContext.Power, this.Unit);
|
||||||
this.Builder.OrderList.Add(order);
|
this.Builder.OrderList.Add(order);
|
||||||
return this.PowerContext;
|
return new OrderDefinedContext<HoldOrder>(this, order);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Order a unit to move to a destination.
|
/// Order a unit to move to a destination.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IPowerContext MovesTo(string provinceName, string? coast = null)
|
public IOrderDefinedContext<MoveOrder> MovesTo(string provinceName, string? coast = null)
|
||||||
{
|
{
|
||||||
Location destination = this.Unit.Type == UnitType.Army
|
Location destination = this.Unit.Type == UnitType.Army
|
||||||
? this.Builder.World.GetLand(provinceName)
|
? this.Builder.World.GetLand(provinceName)
|
||||||
|
@ -262,7 +299,7 @@ public class TestCaseBuilder
|
||||||
this.Builder.Season,
|
this.Builder.Season,
|
||||||
destination);
|
destination);
|
||||||
this.Builder.OrderList.Add(moveOrder);
|
this.Builder.OrderList.Add(moveOrder);
|
||||||
return this.PowerContext;
|
return new OrderDefinedContext<MoveOrder>(this, moveOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IConvoyContext Convoys
|
public IConvoyContext Convoys
|
||||||
|
@ -326,7 +363,7 @@ public class TestCaseBuilder
|
||||||
this.Target = target;
|
this.Target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPowerContext To(string provinceName)
|
public IOrderDefinedContext<ConvoyOrder> To(string provinceName)
|
||||||
{
|
{
|
||||||
Location location = this.Builder.World.GetLand(provinceName);
|
Location location = this.Builder.World.GetLand(provinceName);
|
||||||
ConvoyOrder order = new ConvoyOrder(
|
ConvoyOrder order = new ConvoyOrder(
|
||||||
|
@ -336,7 +373,7 @@ public class TestCaseBuilder
|
||||||
this.Builder.Season,
|
this.Builder.Season,
|
||||||
location);
|
location);
|
||||||
this.Builder.OrderList.Add(order);
|
this.Builder.OrderList.Add(order);
|
||||||
return this.PowerContext;
|
return new OrderDefinedContext<ConvoyOrder>(this.UnitContext, order);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,17 +431,17 @@ public class TestCaseBuilder
|
||||||
this.Target = target;
|
this.Target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPowerContext Hold()
|
public IOrderDefinedContext<SupportHoldOrder> Hold()
|
||||||
{
|
{
|
||||||
SupportHoldOrder order = new SupportHoldOrder(
|
SupportHoldOrder order = new SupportHoldOrder(
|
||||||
this.PowerContext.Power,
|
this.PowerContext.Power,
|
||||||
this.UnitContext.Unit,
|
this.UnitContext.Unit,
|
||||||
this.Target);
|
this.Target);
|
||||||
this.Builder.OrderList.Add(order);
|
this.Builder.OrderList.Add(order);
|
||||||
return this.PowerContext;
|
return new OrderDefinedContext<SupportHoldOrder>(this.UnitContext, order);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPowerContext MoveTo(string provinceName, string? coast = null)
|
public IOrderDefinedContext<SupportMoveOrder> MoveTo(string provinceName, string? coast = null)
|
||||||
{
|
{
|
||||||
Location destination = this.Target.Type == UnitType.Army
|
Location destination = this.Target.Type == UnitType.Army
|
||||||
? this.Builder.World.GetLand(provinceName)
|
? this.Builder.World.GetLand(provinceName)
|
||||||
|
@ -416,7 +453,36 @@ public class TestCaseBuilder
|
||||||
this.Builder.Season,
|
this.Builder.Season,
|
||||||
destination);
|
destination);
|
||||||
this.Builder.OrderList.Add(order);
|
this.Builder.OrderList.Add(order);
|
||||||
return this.PowerContext;
|
return new OrderDefinedContext<SupportMoveOrder>(this.UnitContext, order);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OrderDefinedContext<OrderType> : IOrderDefinedContext<OrderType> where OrderType : Order
|
||||||
|
{
|
||||||
|
public TestCaseBuilder Builder;
|
||||||
|
public PowerContext PowerContext;
|
||||||
|
public UnitContext UnitContext;
|
||||||
|
public OrderType Order;
|
||||||
|
|
||||||
|
public OrderDefinedContext(UnitContext unitContext, OrderType order)
|
||||||
|
{
|
||||||
|
this.Builder = unitContext.Builder;
|
||||||
|
this.PowerContext = unitContext.PowerContext;
|
||||||
|
this.UnitContext = unitContext;
|
||||||
|
this.Order = order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPowerContext this[string powerName] => this.PowerContext[powerName];
|
||||||
|
|
||||||
|
public IUnitContext Army(string provinceName) => this.PowerContext.Army(provinceName);
|
||||||
|
|
||||||
|
public IUnitContext Fleet(string provinceName, string? coast = null)
|
||||||
|
=> this.PowerContext.Fleet(provinceName);
|
||||||
|
|
||||||
|
public IOrderDefinedContext<OrderType> GetReference(out OrderReference<OrderType> order)
|
||||||
|
{
|
||||||
|
order = new OrderReference<OrderType>(this.Builder, this.Order);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using MultiversalDiplomacy.Adjudicate;
|
||||||
using MultiversalDiplomacy.Model;
|
using MultiversalDiplomacy.Model;
|
||||||
using MultiversalDiplomacy.Orders;
|
using MultiversalDiplomacy.Orders;
|
||||||
|
|
||||||
|
@ -112,4 +113,47 @@ class TestCaseBuilderTest
|
||||||
|
|
||||||
Assert.That(orders.Where(OrderForProvince("London")), Is.Empty, "Unexpected order");
|
Assert.That(orders.Where(OrderForProvince("London")), Is.Empty, "Unexpected order");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void BuilderProvidesReferencesForValidation()
|
||||||
|
{
|
||||||
|
IPhaseAdjudicator rubberStamp = new TestAdjudicator(TestAdjudicator.RubberStamp);
|
||||||
|
|
||||||
|
TestCaseBuilder setup = new TestCaseBuilder(World.WithStandardMap().WithInitialSeason());
|
||||||
|
setup["Germany"]
|
||||||
|
.Army("Mun").Holds().GetReference(out var orderMun);
|
||||||
|
|
||||||
|
Assert.That(orderMun, Is.Not.Null, "Expected order reference");
|
||||||
|
Assert.That(
|
||||||
|
orderMun.Order.Power,
|
||||||
|
Is.EqualTo(setup.World.GetPower("Germany")),
|
||||||
|
"Wrong power");
|
||||||
|
Assert.That(
|
||||||
|
orderMun.Order.Unit.Location,
|
||||||
|
Is.EqualTo(setup.World.GetLand("Mun")),
|
||||||
|
"Wrong unit");
|
||||||
|
|
||||||
|
Assert.That<OrderValidation>(
|
||||||
|
() => orderMun.Validation,
|
||||||
|
Throws.Exception,
|
||||||
|
"Validation property should be inaccessible before validation actually happens");
|
||||||
|
setup.ValidateOrders(rubberStamp);
|
||||||
|
Assert.That<OrderValidation>(
|
||||||
|
() => orderMun.Validation,
|
||||||
|
Throws.Nothing,
|
||||||
|
"Validation property should be accessible after validation");
|
||||||
|
|
||||||
|
Assert.That(
|
||||||
|
orderMun.Validation.Order,
|
||||||
|
Is.EqualTo(orderMun.Order),
|
||||||
|
"Validation for wrong order");
|
||||||
|
Assert.That(
|
||||||
|
orderMun.Validation.Valid,
|
||||||
|
Is.True,
|
||||||
|
"Unexpected validation result");
|
||||||
|
Assert.That(
|
||||||
|
orderMun.Validation.Reason,
|
||||||
|
Is.EqualTo(ValidationReason.Valid),
|
||||||
|
"Unexpected validation reason");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue