153 lines
No EOL
5.9 KiB
C#
153 lines
No EOL
5.9 KiB
C#
using FluentAssertions;
|
|
using Xunit;
|
|
|
|
namespace BucketChain.Test;
|
|
|
|
public sealed class ChainTests
|
|
{
|
|
private const double InitialFireSize = 10D;
|
|
private const double FireGrowthRate = 0.25D;
|
|
private const double WellSize = 20D;
|
|
private const double WellRefillRate = 0.5D;
|
|
private const double BucketSize = 1.5D;
|
|
|
|
[Fact]
|
|
public void Construction()
|
|
{
|
|
new Chain(10, 5, BucketSize, new Well(WellSize, WellRefillRate),
|
|
new Fire(InitialFireSize, FireGrowthRate), new Person())
|
|
.Should().NotBeNull("only sets private fields, not much to check here");
|
|
}
|
|
|
|
[Fact]
|
|
public void Operate_Minimal()
|
|
{
|
|
const double FireSize = 3D;
|
|
var (chain, fire, well, first) = CreateMinimalChain(2, 2,
|
|
2, FireSize);
|
|
|
|
// step 1
|
|
// new bucket handed to first person who filled it, then moved to second person
|
|
chain.Operate(1, out var error)
|
|
.Should().BeFalse("fire still burning");
|
|
error.Should().BeFalse("no problem so far");
|
|
first.HasBucket.Should().BeFalse("moved to second person already");
|
|
first.RightNeighbor?.HasBucket.Should().BeTrue("has the new bucket");
|
|
well.LitersRemaining.Should().BeApproximately(WellSize - BucketSize,
|
|
double.Epsilon, "bucket has been filled");
|
|
fire.Extinguished.Should().BeFalse("still burning");
|
|
fire.FireSize.Should().BeApproximately(FireSize + FireGrowthRate - BucketSize,
|
|
double.Epsilon, "fire grew, then got hit by the water in the bucket");
|
|
|
|
var wellSize = well.LitersRemaining;
|
|
var fireSize = fire.FireSize;
|
|
// step 2
|
|
// new bucket handed to first person who filled it, then moved to the second person
|
|
// empty bucket from previous step handed from second person to first
|
|
chain.Operate(2, out error)
|
|
.Should().BeFalse("fire still burning");
|
|
error.Should().BeFalse("no problem so far");
|
|
first.HasBucket.Should().BeTrue("there are now two buckets, has the one from step 1");
|
|
first.RightNeighbor?.HasBucket.Should().BeTrue("has the new bucket");
|
|
well.LitersRemaining.Should().BeApproximately(wellSize + WellRefillRate - BucketSize,
|
|
double.Epsilon, "well refilled, then bucket has been filled");
|
|
fire.Extinguished.Should().BeFalse("still burning");
|
|
fire.FireSize.Should().BeApproximately(fireSize + FireGrowthRate - BucketSize,
|
|
double.Epsilon, "fire grew, then got hit by the water in the bucket");
|
|
|
|
wellSize = well.LitersRemaining;
|
|
// step 3
|
|
// no new buckets added, existing two continue circulating
|
|
chain.Operate(3, out error)
|
|
.Should().BeTrue("fire extinguished");
|
|
error.Should().BeFalse("no problem so far");
|
|
well.LitersRemaining.Should().BeApproximately(wellSize + WellRefillRate - BucketSize,
|
|
double.Epsilon, "well refilled, then bucket has been filled");
|
|
fire.Extinguished.Should().BeTrue();
|
|
fire.FireSize.Should().BeApproximately(0D,
|
|
double.Epsilon, "fire extinguished, but size cannot become negative");
|
|
}
|
|
|
|
[Fact]
|
|
public void Operate_NotLongEnough()
|
|
{
|
|
const int Steps = 100;
|
|
var (chain, fire, _, _) = CreateMinimalChain(4, 3, 2);
|
|
|
|
for (var i = 0; i < Steps; i++)
|
|
{
|
|
chain.Operate(i + 1, out var error)
|
|
.Should().BeFalse("fire keeps burning");
|
|
error.Should().BeFalse("no error, chain is simply not long enough");
|
|
}
|
|
|
|
fire.Extinguished.Should().BeFalse();
|
|
fire.FireSize.Should().BeApproximately(InitialFireSize + FireGrowthRate * Steps,
|
|
2E-14, "fire keeps growing");
|
|
}
|
|
|
|
[Fact]
|
|
public void Operate_Initially_NotLongEnough()
|
|
{
|
|
const int FirstSteps = 5;
|
|
var (chain, fire, _, firstPerson) = CreateMinimalChain(4, 2, 2);
|
|
|
|
var initialSize = fire.FireSize;
|
|
for (var i = 0; i < FirstSteps; i++)
|
|
{
|
|
chain.Operate(i + 1, out var error)
|
|
.Should().BeFalse("fire keeps burning");
|
|
error.Should().BeFalse("no error, chain is simply not long enough");
|
|
}
|
|
fire.FireSize.Should().BeGreaterThan(initialSize, "fire grew");
|
|
|
|
// two additional people, but no more buckets, stays at 2
|
|
new Person().JoinChain(firstPerson, true);
|
|
new Person().JoinChain(firstPerson, true);
|
|
|
|
var s = FirstSteps;
|
|
while (!chain.Operate(++s, out var error))
|
|
{
|
|
fire.Extinguished.Should().BeFalse();
|
|
error.Should().BeFalse();
|
|
}
|
|
|
|
fire.FireSize.Should().BeApproximately(0D, double.Epsilon, "fire extinguished");
|
|
s.Should().Be(49);
|
|
}
|
|
|
|
[Fact]
|
|
public void StringRepresentation()
|
|
{
|
|
var (chain, fire, well, firstPerson) = CreateMinimalChain(4, 2, 1);
|
|
chain.Operate(1, out _);
|
|
|
|
chain.ToString()
|
|
.Should().Be($"{well} | {firstPerson} | {firstPerson.RightNeighbor} | ❔ | ❔ | {fire}");
|
|
}
|
|
|
|
private static (Chain Chain, Fire Fire, Well Well, Person FirstPerson) CreateMinimalChain(int requiredPeople,
|
|
int peopleCount, int buckets, double? initialFireSize = null)
|
|
{
|
|
var fire = new Fire(initialFireSize ?? InitialFireSize, FireGrowthRate);
|
|
var well = new Well(WellSize, WellRefillRate);
|
|
var firstPerson = CreatePeople();
|
|
var chain = new Chain(requiredPeople, buckets, BucketSize, well, fire, firstPerson);
|
|
|
|
return (chain, fire, well, firstPerson);
|
|
|
|
Person CreatePeople()
|
|
{
|
|
var first = new Person();
|
|
var prev = first;
|
|
for (var i = 1; i < peopleCount; i++)
|
|
{
|
|
var newPerson = new Person();
|
|
newPerson.JoinChain(prev, true);
|
|
prev = newPerson;
|
|
}
|
|
|
|
return first;
|
|
}
|
|
}
|
|
} |