ex-col-02-bucket-chain/BucketChain.Test/PersonTests.cs
github-classroom[bot] 593d8ebfea
Initial commit
2024-11-17 08:52:38 +00:00

466 lines
No EOL
15 KiB
C#

using System.Reflection;
using FluentAssertions;
using Xunit;
namespace BucketChain.Test;
public sealed class PersonTests
{
[Fact]
public void Construction()
{
var person = new Person();
person.LeftNeighbor.Should().BeNull("not set yet");
person.RightNeighbor.Should().BeNull("not set yet");
person.HasBucket.Should().BeFalse("no bucket received yet");
person.LastStepPerformed.Should().Be(0, "performed in no step yet");
}
[Fact]
public void JoinChain_OneOther_Right()
{
var firstPerson = new Person();
var newPerson = new Person();
newPerson.JoinChain(firstPerson, true);
firstPerson.LeftNeighbor.Should().BeNull();
firstPerson.RightNeighbor.Should().BeSameAs(newPerson, "joined on right side");
newPerson.LeftNeighbor.Should().BeSameAs(firstPerson);
newPerson.RightNeighbor.Should().BeNull();
}
[Fact]
public void JoinChain_OneOther_Left()
{
var firstPerson = new Person();
var newPerson = new Person();
newPerson.JoinChain(firstPerson, false);
firstPerson.LeftNeighbor.Should().BeSameAs(newPerson, "joined on left side");
firstPerson.RightNeighbor.Should().BeNull();
newPerson.LeftNeighbor.Should().BeNull();
newPerson.RightNeighbor.Should().BeSameAs(firstPerson);
}
[Fact]
public void JoinChain_TwoOthers_Left()
{
var (firstPerson, secondPerson) = CreateMinimalChain();
var newPerson = new Person();
newPerson.JoinChain(firstPerson, false);
firstPerson.LeftNeighbor.Should().BeSameAs(newPerson, "joined on left side of first person");
firstPerson.RightNeighbor.Should().BeSameAs(secondPerson, "unchanged");
secondPerson.LeftNeighbor.Should().BeSameAs(firstPerson, "unchanged");
secondPerson.RightNeighbor.Should().BeNull();
newPerson.LeftNeighbor.Should().BeNull();
newPerson.RightNeighbor.Should().BeSameAs(firstPerson);
}
[Fact]
public void JoinChain_TwoOthers_Right()
{
var (firstPerson, secondPerson) = CreateMinimalChain();
var newPerson = new Person();
newPerson.JoinChain(secondPerson, true);
firstPerson.LeftNeighbor.Should().BeNull();
firstPerson.RightNeighbor.Should().BeSameAs(secondPerson, "unchanged");
secondPerson.LeftNeighbor.Should().BeSameAs(firstPerson, "unchanged");
secondPerson.RightNeighbor.Should().BeSameAs(newPerson, "joined on right side of second person");
newPerson.LeftNeighbor.Should().BeSameAs(secondPerson);
newPerson.RightNeighbor.Should().BeNull();
}
[Fact]
public void JoinChain_TwoOthers_Center_Option1()
{
var (firstPerson, secondPerson) = CreateMinimalChain();
var newPerson = new Person();
newPerson.JoinChain(firstPerson, true);
firstPerson.LeftNeighbor.Should().BeNull();
firstPerson.RightNeighbor.Should().BeSameAs(newPerson, "joined on right side of first person");
secondPerson.LeftNeighbor.Should().BeSameAs(newPerson, "joined on left side of second person");
secondPerson.RightNeighbor.Should().BeNull();
newPerson.LeftNeighbor.Should().BeSameAs(firstPerson);
newPerson.RightNeighbor.Should().BeSameAs(secondPerson);
}
[Fact]
public void JoinChain_TwoOthers_Center_Option2()
{
var (firstPerson, secondPerson) = CreateMinimalChain();
var newPerson = new Person();
newPerson.JoinChain(secondPerson, false);
firstPerson.LeftNeighbor.Should().BeNull();
firstPerson.RightNeighbor.Should().BeSameAs(newPerson, "joined on right side of first person");
secondPerson.LeftNeighbor.Should().BeSameAs(newPerson, "joined on left side of second person");
secondPerson.RightNeighbor.Should().BeNull();
newPerson.LeftNeighbor.Should().BeSameAs(firstPerson);
newPerson.RightNeighbor.Should().BeSameAs(secondPerson);
}
[Fact]
public void FightFire_Simple()
{
const double FireInitialSize = 45.67D;
const double BucketSize = 2.4D;
var fire = new Fire(FireInitialSize, default(double));
var bucket = CreateFilledBucket(BucketSize);
var person = new Person();
SetBucket(person, bucket, true);
person.FightFire(fire);
fire.FireSize.Should().BeApproximately(FireInitialSize - BucketSize,
double.Epsilon, "fire shrank by amount of water in the bucket");
bucket.IsEmpty.Should().BeTrue("bucket content has been used");
}
[Fact]
public void FightFire_NoBucket()
{
const double FireInitialSize = 45.67D;
var fire = new Fire(FireInitialSize, default(double));
var person = new Person();
person.FightFire(fire);
fire.FireSize.Should().BeApproximately(FireInitialSize,
double.Epsilon, "fire size did not change");
}
[Fact]
public void UseWell_ExistingBucket_Forward()
{
const double WellInitialFillLevel = 20D;
const double BucketSize = 1.86D;
var well = new Well(WellInitialFillLevel, default(double));
var bucket = new Bucket(BucketSize);
var person = new Person();
SetBucket(person, bucket, true);
person.UseWell(well, null);
bucket.IsEmpty.Should().BeFalse("has been filled");
well.LitersRemaining.Should().BeApproximately(WellInitialFillLevel - BucketSize,
double.Epsilon, "well level has been reduced by bucket amount");
}
[Fact]
public void UseWell_ExistingBucket_Backward()
{
const double WellInitialFillLevel = 20D;
const double BucketSize = 1.86D;
var well = new Well(WellInitialFillLevel, default(double));
var bucket = new Bucket(BucketSize);
var person = new Person();
SetBucket(person, bucket, false);
person.UseWell(well, null);
bucket.IsEmpty.Should().BeFalse("has been filled");
well.LitersRemaining.Should().BeApproximately(WellInitialFillLevel - BucketSize,
double.Epsilon, "well level has been reduced by bucket amount");
}
[Fact]
public void UseWell_NoBucket()
{
const double WellInitialFillLevel = 20D;
var well = new Well(WellInitialFillLevel, default(double));
var person = new Person();
person.UseWell(well, null);
well.LitersRemaining.Should().Be(WellInitialFillLevel, "unchanged");
}
[Fact]
public void UseWell_AdditionalBucket()
{
const double WellInitialFillLevel = 20D;
const double BucketSize = 1.86D;
var well = new Well(WellInitialFillLevel, default(double));
var bucket = new Bucket(BucketSize);
var person = new Person();
person.UseWell(well, bucket);
bucket.IsEmpty.Should().BeFalse("has been filled");
well.LitersRemaining.Should().BeApproximately(WellInitialFillLevel - BucketSize,
double.Epsilon, "well level has been reduced by bucket amount");
}
[Fact]
public void MoveBucket_Forward_Simple()
{
var (first, second) = CreateMinimalChain();
var bucket = CreateFilledBucket(1.2D);
SetBucket(first, bucket, true);
HasBucket(first, true).Should().BeTrue();
HasBucket(second, true).Should().BeFalse();
first.MoveBucket(1)
.Should().BeTrue("successfully moved forward to next person");
HasBucket(first, true).Should().BeFalse("sent forward");
HasBucket(second, true).Should().BeTrue("received");
HasBucket(first, false).Should().BeFalse();
HasBucket(second, false).Should().BeFalse();
}
[Fact]
public void MoveBucket_Backward_Simple()
{
var (first, second) = CreateMinimalChain();
var bucket = new Bucket(1.2D);
SetBucket(second, bucket, false);
HasBucket(first, true).Should().BeFalse();
HasBucket(second, true).Should().BeFalse();
HasBucket(second, false).Should().BeTrue();
first.MoveBucket(1)
.Should().BeTrue("successfully moved backward from next person");
HasBucket(first, true).Should().BeFalse();
HasBucket(second, true).Should().BeFalse();
HasBucket(first, false).Should().BeTrue("received");
HasBucket(second, false).Should().BeFalse("sent backward");
}
[Fact]
public void MoveBucket_Forward_And_Backward()
{
var (first, second) = CreateMinimalChain();
var bucket = CreateFilledBucket(1.2D);
var backwardBucket = new Bucket(1.2D);
SetBucket(first, bucket, true);
SetBucket(second, backwardBucket, false);
HasBucket(first, true).Should().BeTrue();
HasBucket(second, true).Should().BeFalse();
HasBucket(first, false).Should().BeFalse();
HasBucket(second, false).Should().BeTrue();
first.MoveBucket(1)
.Should().BeTrue("successfully moved forward to and backward from next person");
HasBucket(first, true).Should().BeFalse("sent forward");
HasBucket(second, true).Should().BeTrue("received");
HasBucket(first, false).Should().BeTrue("received");
HasBucket(second, false).Should().BeFalse("sent backward");
}
[Fact]
public void MoveBucket_NoBuckets()
{
var (first, second) = CreateMinimalChain();
HasBucket(first, true).Should().BeFalse();
HasBucket(second, true).Should().BeFalse();
HasBucket(first, false).Should().BeFalse();
HasBucket(second, false).Should().BeFalse();
first.MoveBucket(1)
.Should().BeTrue("nothing was moved, but there was no error either");
HasBucket(first, true).Should().BeFalse();
HasBucket(second, true).Should().BeFalse();
HasBucket(first, false).Should().BeFalse();
HasBucket(second, false).Should().BeFalse();
}
[Fact]
public void MoveBucket_ForwardBlocked()
{
var (first, second) = CreateMinimalChain();
var bucket1 = CreateFilledBucket(1.2D);
var bucket2 = CreateFilledBucket(1.2D);
SetBucket(first, bucket1, true);
SetBucket(second, bucket2, true);
HasBucket(first, true).Should().BeTrue();
HasBucket(second, true).Should().BeTrue();
first.MoveBucket(1)
.Should().BeTrue("nothing was moved, but there was no error either");
HasBucket(first, true).Should().BeTrue();
HasBucket(second, true).Should().BeTrue();
}
[Fact]
public void MoveBucket_BackwardBlocked()
{
var (first, second) = CreateMinimalChain();
var bucket1 = CreateFilledBucket(1.2D);
var bucket2 = CreateFilledBucket(1.2D);
SetBucket(first, bucket1, false);
SetBucket(second, bucket2, false);
HasBucket(first, false).Should().BeTrue();
HasBucket(second, false).Should().BeTrue();
first.MoveBucket(1)
.Should().BeTrue("nothing was moved, but there was no error either");
HasBucket(first, false).Should().BeTrue();
HasBucket(second, false).Should().BeTrue();
}
[Fact]
public void MoveBucket_AlreadyPerformedInStep_Simple()
{
const int Step = 12;
var person = new Person();
SetLastStepPerformed(person, Step);
person.MoveBucket(Step)
.Should().BeFalse("already performed in this step");
}
[Fact]
public void MoveBucket_AlreadyPerformedInStep_Complex()
{
const int Step = 12;
var (first, second) = CreateMinimalChain();
var third = new Person();
third.JoinChain(second, true);
SetBucket(first, CreateFilledBucket(1.2D), true);
first.MoveBucket(Step)
.Should().BeTrue("can move");
second.MoveBucket(Step)
.Should().BeFalse("already performed in this step");
}
[Fact]
public void MoveBucket_NoNeighbor()
{
var person = new Person();
SetBucket(person, CreateFilledBucket(1.2D), true);
person.MoveBucket(1)
.Should().BeFalse("no right neighbor to move to/from");
}
[Theory]
[MemberData(nameof(StringRepresentationData))]
public void StringRepresentation(Person person, string expected, string reason)
{
person.ToString()
.Should().Be(expected, reason);
}
public static TheoryData<Person, string, string> StringRepresentationData()
{
return new()
{
{
CreatePerson(null),
"🫲",
"has no bucket"
},
{
CreatePerson(false),
"🔘",
"has empty forward bucket only"
},
{
CreatePerson(false, false),
"🔘",
"has empty backward bucket only"
},
{
CreatePerson(true),
"🔵",
"has full forward bucket only"
},
{
CreatePerson(true, false),
"🔵",
"has full backward bucket only"
},
{
CreatePerson(false,
then: p => SetBucket(p, CreateFilledBucket(1), false)),
"🔘",
"has empty forward bucket and full backward bucket"
},
{
CreatePerson(true,
then: p => SetBucket(p, new Bucket(1D), false)),
"🔵",
"has full forward bucket and empty backward bucket"
}
};
static Person CreatePerson(bool? bucketFull, bool forwardBucket = true, Action<Person>? then = null)
{
var person = new Person();
if (bucketFull == null)
{
return person;
}
var bucket = bucketFull.Value ? CreateFilledBucket(1D) : new Bucket(1D);
SetBucket(person, bucket, forwardBucket);
then?.Invoke(person);
return person;
}
}
private static (Person FirstPerson, Person SecondPerson) CreateMinimalChain()
{
var firstPerson = new Person();
var secondPerson = new Person();
secondPerson.JoinChain(firstPerson, true);
return (firstPerson, secondPerson);
}
private static Bucket CreateFilledBucket(double size)
{
var bucket = new Bucket(size);
bucket.Fill(size);
return bucket;
}
#region Test Helper - ignore
private static void SetBucket(Person person, Bucket? bucket, bool forwardBucket)
{
var field = GetBucketField(forwardBucket);
field.SetValue(person, bucket);
}
private static bool HasBucket(Person person, bool forwardBucket)
{
var field = GetBucketField(forwardBucket);
return field.GetValue(person) != null;
}
private static FieldInfo GetBucketField(bool forwardBucket) =>
typeof(Person).GetField(forwardBucket ? "_forwardBucket" : "_backwardBucket",
BindingFlags.Instance | BindingFlags.NonPublic)!;
private static void SetLastStepPerformed(Person person, int step)
{
var prop = typeof(Person).GetProperty("LastStepPerformed",
BindingFlags.Instance | BindingFlags.Public);
prop!.SetValue(person, step);
}
#endregion
}