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
69
MultiversalDiplomacyTests/OrderReference.cs
Normal file
69
MultiversalDiplomacyTests/OrderReference.cs
Normal file
@ -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 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;
|
||||
|
||||
public TestAdjudicator(
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
using MultiversalDiplomacy.Adjudicate;
|
||||
using MultiversalDiplomacy.Model;
|
||||
using MultiversalDiplomacy.Orders;
|
||||
|
||||
@ -44,12 +45,12 @@ public class TestCaseBuilder
|
||||
/// <summary>
|
||||
/// Give the unit a hold order.
|
||||
/// </summary>
|
||||
public IPowerContext Holds();
|
||||
public IOrderDefinedContext<HoldOrder> Holds();
|
||||
|
||||
/// <summary>
|
||||
/// Give the unit a move order.
|
||||
/// </summary>
|
||||
public IPowerContext MovesTo(string provinceName, string? coast = null);
|
||||
public IOrderDefinedContext<MoveOrder> MovesTo(string provinceName, string? coast = null);
|
||||
|
||||
/// <summary>
|
||||
/// Give the unit a convoy order.
|
||||
@ -89,7 +90,7 @@ public class TestCaseBuilder
|
||||
/// <summary>
|
||||
/// Define the destination of the convoy order.
|
||||
/// </summary>
|
||||
public IPowerContext To(string provinceName);
|
||||
public IOrderDefinedContext<ConvoyOrder> To(string provinceName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -119,18 +120,47 @@ public class TestCaseBuilder
|
||||
/// <summary>
|
||||
/// Give the unit an order to support the target's hold order.
|
||||
/// </summary>
|
||||
public IPowerContext Hold();
|
||||
public IOrderDefinedContext<SupportHoldOrder> Hold();
|
||||
|
||||
/// <summary>
|
||||
/// Give the unit an order to support the target's move order.
|
||||
/// </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 ReadOnlyCollection<Order> Orders { get; }
|
||||
private List<Order> OrderList;
|
||||
private Season Season;
|
||||
public List<OrderValidation>? ValidationResults { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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)
|
||||
{
|
||||
this.World = world;
|
||||
this.OrderList = new List<Order>();
|
||||
this.OrderList = new();
|
||||
this.Orders = new(this.OrderList);
|
||||
this.Season = season ?? this.World.Seasons.First();
|
||||
this.ValidationResults = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -188,6 +219,12 @@ public class TestCaseBuilder
|
||||
return newUnit;
|
||||
}
|
||||
|
||||
public List<OrderValidation> ValidateOrders(IPhaseAdjudicator adjudicator)
|
||||
{
|
||||
this.ValidationResults = adjudicator.ValidateOrders(this.World, this.Orders.ToList());
|
||||
return this.ValidationResults;
|
||||
}
|
||||
|
||||
private class PowerContext : IPowerContext
|
||||
{
|
||||
public TestCaseBuilder Builder;
|
||||
@ -241,17 +278,17 @@ public class TestCaseBuilder
|
||||
/// <summary>
|
||||
/// Order a unit to hold.
|
||||
/// </summary>
|
||||
public IPowerContext Holds()
|
||||
public IOrderDefinedContext<HoldOrder> Holds()
|
||||
{
|
||||
HoldOrder order = new HoldOrder(this.PowerContext.Power, this.Unit);
|
||||
this.Builder.OrderList.Add(order);
|
||||
return this.PowerContext;
|
||||
return new OrderDefinedContext<HoldOrder>(this, order);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Order a unit to move to a destination.
|
||||
/// </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
|
||||
? this.Builder.World.GetLand(provinceName)
|
||||
@ -262,7 +299,7 @@ public class TestCaseBuilder
|
||||
this.Builder.Season,
|
||||
destination);
|
||||
this.Builder.OrderList.Add(moveOrder);
|
||||
return this.PowerContext;
|
||||
return new OrderDefinedContext<MoveOrder>(this, moveOrder);
|
||||
}
|
||||
|
||||
public IConvoyContext Convoys
|
||||
@ -326,7 +363,7 @@ public class TestCaseBuilder
|
||||
this.Target = target;
|
||||
}
|
||||
|
||||
public IPowerContext To(string provinceName)
|
||||
public IOrderDefinedContext<ConvoyOrder> To(string provinceName)
|
||||
{
|
||||
Location location = this.Builder.World.GetLand(provinceName);
|
||||
ConvoyOrder order = new ConvoyOrder(
|
||||
@ -336,7 +373,7 @@ public class TestCaseBuilder
|
||||
this.Builder.Season,
|
||||
location);
|
||||
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;
|
||||
}
|
||||
|
||||
public IPowerContext Hold()
|
||||
public IOrderDefinedContext<SupportHoldOrder> Hold()
|
||||
{
|
||||
SupportHoldOrder order = new SupportHoldOrder(
|
||||
this.PowerContext.Power,
|
||||
this.UnitContext.Unit,
|
||||
this.Target);
|
||||
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
|
||||
? this.Builder.World.GetLand(provinceName)
|
||||
@ -416,7 +453,36 @@ public class TestCaseBuilder
|
||||
this.Builder.Season,
|
||||
destination);
|
||||
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.Orders;
|
||||
|
||||
@ -112,4 +113,47 @@ class TestCaseBuilderTest
|
||||
|
||||
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
Block a user