diff --git a/Source/aweXpect.Core/Core/Nodes/AndNode.cs b/Source/aweXpect.Core/Core/Nodes/AndNode.cs
index 37a5a0f90..93d06cf30 100644
--- a/Source/aweXpect.Core/Core/Nodes/AndNode.cs
+++ b/Source/aweXpect.Core/Core/Nodes/AndNode.cs
@@ -108,7 +108,20 @@ public override void AppendExpectation(StringBuilder stringBuilder, string? inde
private bool Equals(AndNode other) => Current.Equals(other.Current) && _nodes.SequenceEqual(other._nodes);
///
- public override int GetHashCode() => Current.GetHashCode() ^ _nodes.GetHashCode();
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ // ReSharper disable once NonReadonlyMemberInGetHashCode
+ int hash = 19 * Current.GetHashCode();
+ foreach (Node node in _nodes.Select(x => x.Item2))
+ {
+ hash = (hash * 31) + node.GetHashCode();
+ }
+
+ return hash;
+ }
+ }
private static ConstraintResult CombineResults(
ConstraintResult? combinedResult,
diff --git a/Source/aweXpect.Core/Core/Nodes/ExpectationNode.cs b/Source/aweXpect.Core/Core/Nodes/ExpectationNode.cs
index bebc0467e..55efe34ae 100644
--- a/Source/aweXpect.Core/Core/Nodes/ExpectationNode.cs
+++ b/Source/aweXpect.Core/Core/Nodes/ExpectationNode.cs
@@ -155,5 +155,9 @@ private bool Equals(ExpectationNode other)
}
///
- public override int GetHashCode() => GetType().GetHashCode();
+ // ReSharper disable NonReadonlyMemberInGetHashCode
+ public override int GetHashCode()
+ => _constraint?.GetType().GetHashCode() ?? 17
+ + _inner?.GetHashCode() ?? 0;
+ // ReSharper restore NonReadonlyMemberInGetHashCode
}
diff --git a/Source/aweXpect.Core/Core/Nodes/OrNode.cs b/Source/aweXpect.Core/Core/Nodes/OrNode.cs
index b43f6307d..a082dcce5 100644
--- a/Source/aweXpect.Core/Core/Nodes/OrNode.cs
+++ b/Source/aweXpect.Core/Core/Nodes/OrNode.cs
@@ -102,7 +102,20 @@ public override void AppendExpectation(StringBuilder stringBuilder, string? inde
private bool Equals(OrNode other) => Current.Equals(other.Current) && _nodes.SequenceEqual(other._nodes);
///
- public override int GetHashCode() => Current.GetHashCode() ^ _nodes.GetHashCode();
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ // ReSharper disable once NonReadonlyMemberInGetHashCode
+ int hash = 19 * Current.GetHashCode();
+ foreach (Node node in _nodes.Select(x => x.Item2))
+ {
+ hash = (hash * 31) + node.GetHashCode();
+ }
+
+ return hash;
+ }
+ }
private static ConstraintResult CombineResults(
ConstraintResult? combinedResult,
@@ -143,9 +156,9 @@ private static Outcome Or(Outcome left, Outcome right)
=> (left, right) switch
{
(Outcome.Failure, Outcome.Failure) => Outcome.Failure,
- (_, Outcome.Undecided) => Outcome.Undecided,
- (Outcome.Undecided, _) => Outcome.Undecided,
- (_, _) => Outcome.Success,
+ (_, Outcome.Success) => Outcome.Success,
+ (Outcome.Success, _) => Outcome.Success,
+ (_, _) => Outcome.Undecided,
};
public override void AppendExpectation(StringBuilder stringBuilder, string? indentation = null)
diff --git a/Tests/aweXpect.Core.Tests/Core/Adapters/TestFrameworkAdapterTests.cs b/Tests/aweXpect.Core.Tests/Core/Adapters/TestFrameworkAdapterTests.cs
index d2a218bd5..8a15a0cb3 100644
--- a/Tests/aweXpect.Core.Tests/Core/Adapters/TestFrameworkAdapterTests.cs
+++ b/Tests/aweXpect.Core.Tests/Core/Adapters/TestFrameworkAdapterTests.cs
@@ -27,6 +27,16 @@ public async Task FromType_WhenNameDoesNotExist_ShouldReturnNull()
await That(exception).IsNull();
}
+ [Fact]
+ public async Task Inconclusive_MissingAssemblyName_ShouldThrowNotSupportedException()
+ {
+ MyTestFrameworkAdapter adapter = new(MissingAssembly, skipException: new MyException());
+ _ = adapter.IsAvailable;
+
+ await That(() => adapter.Inconclusive("foo")).Throws()
+ .WithMessage("Failed to create the inconclusive assertion type");
+ }
+
[Fact]
public async Task Inconclusive_ValidAssemblyName_ShouldThrowNotSupportedException()
{
diff --git a/Tests/aweXpect.Core.Tests/Core/Constraints/ConstraintResultTests.FromExceptionTests.cs b/Tests/aweXpect.Core.Tests/Core/Constraints/ConstraintResultTests.FromExceptionTests.cs
index b7cc0182e..585672bc9 100644
--- a/Tests/aweXpect.Core.Tests/Core/Constraints/ConstraintResultTests.FromExceptionTests.cs
+++ b/Tests/aweXpect.Core.Tests/Core/Constraints/ConstraintResultTests.FromExceptionTests.cs
@@ -50,6 +50,23 @@ public async Task AppendResult_SpecificException_ShouldAppendExpectedValue()
await That(sb.ToString()).IsEqualTo("it did throw an ArgumentException");
}
+ [Theory]
+ [InlineData(Outcome.Failure, Outcome.Success)]
+ [InlineData(Outcome.Success, Outcome.Failure)]
+ [InlineData(Outcome.Undecided, Outcome.Undecided)]
+ public async Task Negate_ShouldNegateInnerOutcome(Outcome innerOutcome, Outcome expectedAfterNegation)
+ {
+ DummyConstraintResult inner = new(innerOutcome, "foo");
+ Exception exception = new("bar");
+ DummyExpectationBuilder expectationBuilder = new();
+ MyFromExceptionConstraintResult sut = new(inner, exception, expectationBuilder);
+
+ sut.Negate();
+
+ await That(sut.Outcome).IsEqualTo(Outcome.Failure);
+ await That(inner.Outcome).IsEqualTo(expectedAfterNegation);
+ }
+
[Fact]
public async Task Outcome_ShouldBeFailure()
{
@@ -61,7 +78,6 @@ public async Task Outcome_ShouldBeFailure()
await That(sut.Outcome).IsEqualTo(Outcome.Failure);
}
-
[Fact]
public async Task SetOutcome_ShouldBeForwardedToInner()
{
diff --git a/Tests/aweXpect.Core.Tests/Core/ManualExpectationBuilderTests.cs b/Tests/aweXpect.Core.Tests/Core/ManualExpectationBuilderTests.cs
index f8609559b..181f72eaa 100644
--- a/Tests/aweXpect.Core.Tests/Core/ManualExpectationBuilderTests.cs
+++ b/Tests/aweXpect.Core.Tests/Core/ManualExpectationBuilderTests.cs
@@ -93,6 +93,16 @@ public async Task Equals_FirstNull_ShouldBeFalse()
await That(result).IsFalse();
}
+ [Fact]
+ public async Task Equals_ObjectNull_ShouldBeFalse()
+ {
+ ManualExpectationBuilder sut = new(null);
+
+ bool result = sut.Equals(null);
+
+ await That(result).IsFalse();
+ }
+
[Fact]
public async Task Equals_SecondNull_ShouldBeFalse()
{
@@ -136,6 +146,18 @@ public async Task GetHashCode_SameConstraint_ShouldBeEqual()
await That(sut1.GetHashCode()).IsEqualTo(sut2.GetHashCode());
}
+ [Fact]
+ public async Task GetHashCode_WithParameter_ShouldUseHashCodeFromParameter()
+ {
+ ManualExpectationBuilder sut1 = new(null);
+ sut1.AddConstraint((_, _, _) => new DummyConstraint("foo"));
+ ManualExpectationBuilder sut2 = new(null);
+ sut2.ForWhich(x => x)
+ .AddConstraint((_, _, _) => new DummyConstraint("foo"));
+
+ await That(sut1.GetHashCode()).IsEqualTo(sut2.GetHashCode(sut1));
+ }
+
[Fact]
public async Task IsMet_ShouldThrowNotSupportedException()
{
diff --git a/Tests/aweXpect.Core.Tests/Core/Nodes/AndNodeTests.cs b/Tests/aweXpect.Core.Tests/Core/Nodes/AndNodeTests.cs
index 2ccb62442..846a3e3d2 100644
--- a/Tests/aweXpect.Core.Tests/Core/Nodes/AndNodeTests.cs
+++ b/Tests/aweXpect.Core.Tests/Core/Nodes/AndNodeTests.cs
@@ -1,6 +1,7 @@
using System.Text;
using System.Threading;
using aweXpect.Core.Constraints;
+using aweXpect.Core.Helpers;
using aweXpect.Core.Nodes;
using aweXpect.Core.Tests.TestHelpers;
@@ -8,6 +9,81 @@ namespace aweXpect.Core.Tests.Core.Nodes;
public sealed class AndNodeTests
{
+ [Fact]
+ public async Task AddAsyncMapping_ShouldUseCurrentNode()
+ {
+ MemberAccessor> memberAccessor =
+ MemberAccessor>.FromExpression(x => Task.FromResult(x.Length));
+ DummyNode first = new("foo");
+ DummyNode second = new("bar");
+ AndNode node = new(first);
+
+ node.AddAsyncMapping(memberAccessor);
+ node.AddNode(second);
+
+ await That(first.MappingMemberAccessor).IsSameAs(memberAccessor);
+ await That(second.MappingMemberAccessor).IsNull();
+ }
+
+ [Fact]
+ public async Task AddAsyncMapping_ShouldUseSecondNode()
+ {
+ MemberAccessor> memberAccessor =
+ MemberAccessor>.FromExpression(x => Task.FromResult(x.Length));
+ DummyNode first = new("foo");
+ DummyNode second = new("bar");
+ AndNode node = new(first);
+ node.AddNode(second);
+
+ node.AddAsyncMapping(memberAccessor);
+
+ await That(first.MappingMemberAccessor).IsNull();
+ await That(second.MappingMemberAccessor).IsSameAs(memberAccessor);
+ }
+
+ [Fact]
+ public async Task AddMapping_ShouldUseCurrentNode()
+ {
+ MemberAccessor memberAccessor = MemberAccessor.FromExpression(x => x.Length);
+ DummyNode first = new("foo");
+ DummyNode second = new("bar");
+ AndNode node = new(first);
+
+ node.AddMapping(memberAccessor);
+ node.AddNode(second);
+
+ await That(first.MappingMemberAccessor).IsSameAs(memberAccessor);
+ await That(second.MappingMemberAccessor).IsNull();
+ }
+
+ [Fact]
+ public async Task AddMapping_ShouldUseSecondNode()
+ {
+ MemberAccessor memberAccessor = MemberAccessor.FromExpression(x => x.Length);
+ DummyNode first = new("foo");
+ DummyNode second = new("bar");
+ AndNode node = new(first);
+ node.AddNode(second);
+
+ node.AddMapping(memberAccessor);
+
+ await That(first.MappingMemberAccessor).IsNull();
+ await That(second.MappingMemberAccessor).IsSameAs(memberAccessor);
+ }
+
+ [Fact]
+ public async Task AppendExpectation_WithAdditionalNodes_ShouldUseAllNodes()
+ {
+ AndNode node = new(new DummyNode("foo"));
+ node.AddNode(new DummyNode("bar"));
+ node.AddNode(new DummyNode("baz"));
+ StringBuilder sb = new();
+
+ node.AppendExpectation(sb);
+
+ await That(sb.ToString()).IsEqualTo("foo and bar and baz");
+ }
+
[Fact]
public async Task AppendExpectation_WithoutAdditionalNodes_ShouldUseFirstNode()
{
@@ -19,6 +95,134 @@ public async Task AppendExpectation_WithoutAdditionalNodes_ShouldUseFirstNode()
await That(sb.ToString()).IsEqualTo("foo");
}
+ [Fact]
+ public async Task Equals_IfCurrentNodeIsDifferent_ShouldBeFalse()
+ {
+ DummyNode innerNode1 = new("1", () => new DummyConstraintResult(Outcome.Success, "1", ""));
+ DummyNode innerNode2 = new("2", () => new DummyConstraintResult(Outcome.Success, "2", ""));
+ AndNode node1 = new(innerNode1);
+ AndNode node2 = new(innerNode2);
+
+ bool result = node1.Equals(node2);
+
+ await That(result).IsFalse();
+ await That(node1.GetHashCode()).IsNotEqualTo(node2.GetHashCode());
+ }
+
+ [Fact]
+ public async Task Equals_IfCurrentNodeIsTheSame_ShouldBeTrue()
+ {
+ DummyNode innerNode1 = new("1", () => new DummyConstraintResult(Outcome.Success, "1", ""));
+ DummyNode innerNode2 = new("1", () => new DummyConstraintResult(Outcome.Success, "1", ""));
+ AndNode node1 = new(innerNode1);
+ AndNode node2 = new(innerNode2);
+
+ bool result = node1.Equals(node2);
+
+ await That(result).IsTrue();
+ await That(node1.GetHashCode()).IsEqualTo(node2.GetHashCode());
+ }
+
+ [Fact]
+ public async Task Equals_IfInnerNodesAreDifferent_ShouldBeFalse()
+ {
+ DummyNode innerNode0 = new("0", () => new DummyConstraintResult(Outcome.Success, "0", ""));
+ DummyNode innerNode1 = new("1", () => new DummyConstraintResult(Outcome.Success, "1", ""));
+ DummyNode innerNode2 = new("2", () => new DummyConstraintResult(Outcome.Success, "2", ""));
+ DummyNode currentNode = new("3", () => new DummyConstraintResult(Outcome.Success, "3", ""));
+ AndNode node1 = new(innerNode0);
+ node1.AddNode(innerNode1);
+ node1.AddNode(currentNode);
+ AndNode node2 = new(innerNode0);
+ node2.AddNode(innerNode2);
+ node2.AddNode(currentNode);
+
+ bool result = node1.Equals(node2);
+
+ await That(result).IsFalse();
+ await That(node1.GetHashCode()).IsNotEqualTo(node2.GetHashCode());
+ }
+
+ [Fact]
+ public async Task Equals_IfInnerNodesAreSame_ShouldBeTrue()
+ {
+ DummyNode innerNode1 = new("1", () => new DummyConstraintResult(Outcome.Success, "1", ""));
+ DummyNode innerNode2 = new("1", () => new DummyConstraintResult(Outcome.Success, "1", ""));
+ DummyNode innerNode3 = new("2", () => new DummyConstraintResult(Outcome.Success, "2", ""));
+ DummyNode innerNode4 = new("2", () => new DummyConstraintResult(Outcome.Success, "2", ""));
+ DummyNode currentNode = new("3", () => new DummyConstraintResult(Outcome.Success, "3", ""));
+ AndNode node1 = new(innerNode1);
+ node1.AddNode(innerNode3);
+ node1.AddNode(currentNode);
+ AndNode node2 = new(innerNode2);
+ node2.AddNode(innerNode4);
+ node2.AddNode(currentNode);
+
+ bool result = node1.Equals(node2);
+
+ await That(result).IsTrue();
+ await That(node1.GetHashCode()).IsEqualTo(node2.GetHashCode());
+ }
+
+ [Fact]
+ public async Task Equals_WhenOtherIsDifferentNode_ShouldBeFalse()
+ {
+ DummyNode inner = new("foo");
+ AndNode node = new(inner);
+ object other = new OrNode(inner);
+
+ bool result = node.Equals(other);
+
+ await That(result).IsFalse();
+ }
+
+ [Fact]
+ public async Task Equals_WhenOtherIsNull_ShouldBeFalse()
+ {
+ AndNode node = new(new DummyNode("foo"));
+
+ bool result = node.Equals(null);
+
+ await That(result).IsFalse();
+ }
+
+ [Theory]
+ [InlineData(Outcome.Success, Outcome.Success, Outcome.Failure)]
+ [InlineData(Outcome.Failure, Outcome.Success, Outcome.Success)]
+ [InlineData(Outcome.Success, Outcome.Failure, Outcome.Success)]
+ [InlineData(Outcome.Failure, Outcome.Failure, Outcome.Success)]
+ [InlineData(Outcome.Failure, Outcome.Undecided, Outcome.Success)]
+ [InlineData(Outcome.Undecided, Outcome.Failure, Outcome.Success)]
+ [InlineData(Outcome.Undecided, Outcome.Undecided, Outcome.Undecided)]
+ public async Task NegatedOutcome_ShouldBeExpected(Outcome node1, Outcome node2, Outcome expectedOutcome)
+ {
+ AndNode node = new(new DummyNode("", () => new DummyConstraintResult(node1)));
+ node.AddNode(new DummyNode("", () => new DummyConstraintResult(node2)));
+
+ ConstraintResult result = await node.IsMetBy(0, null!, CancellationToken.None);
+ result.Negate();
+
+ await That(result.Outcome).IsEqualTo(expectedOutcome);
+ }
+
+ [Fact]
+ public async Task NegatedResult_ShouldUseOrAsSeparator()
+ {
+ AndNode node = new(new DummyNode("", () => new DummyConstraintResult(Outcome.Success, "foo")));
+ node.AddNode(new DummyNode("", () => new DummyConstraintResult(Outcome.Success, "bar")));
+ StringBuilder sb1 = new();
+ StringBuilder sb2 = new();
+
+ ConstraintResult result = await node.IsMetBy(0, null!, CancellationToken.None);
+ result.AppendExpectation(sb1);
+
+ result.Negate();
+
+ result.AppendExpectation(sb2);
+ await That(sb1.ToString()).IsEqualTo("foo and bar");
+ await That(sb2.ToString()).IsEqualTo("foo or bar");
+ }
+
[Theory]
[InlineData(Outcome.Success, Outcome.Success, Outcome.Success)]
[InlineData(Outcome.Failure, Outcome.Success, Outcome.Failure)]
@@ -37,6 +241,50 @@ public async Task Outcome_ShouldBeExpected(Outcome node1, Outcome node2, Outcome
await That(result.Outcome).IsEqualTo(expectedOutcome);
}
+ [Fact]
+ public async Task SetReason_WithAdditionalNodes_ShouldUseCurrentNode()
+ {
+ DummyNode node1 = new("node1");
+ DummyNode node2 = new("node2");
+ DummyNode current = new("current");
+ AndNode node = new(node1);
+ node.AddNode(node2);
+ node.AddNode(current);
+
+ node.SetReason(new BecauseReason("bar"));
+
+ await That(current.ReceivedReason).IsEqualTo(", because bar");
+ await That(node1.ReceivedReason).IsNull();
+ await That(node2.ReceivedReason).IsNull();
+ }
+
+ [Fact]
+ public async Task SetReason_WithAdditionalNodes_WhenCurrentNodeIsEmptyExpectationNode_ShouldUseLastNode()
+ {
+ DummyNode node1 = new("node1");
+ DummyNode node2 = new("node2");
+ ExpectationNode current = new();
+ AndNode node = new(node1);
+ node.AddNode(node2);
+ node.AddNode(current);
+
+ node.SetReason(new BecauseReason("bar"));
+
+ await That(node1.ReceivedReason).IsNull();
+ await That(node2.ReceivedReason).IsEqualTo(", because bar");
+ }
+
+ [Fact]
+ public async Task SetReason_WithoutAdditionalNodes_ShouldSetReasonForCurrentNode()
+ {
+ DummyNode current = new("current");
+ AndNode node = new(current);
+
+ node.SetReason(new BecauseReason("bar"));
+
+ await That(current.ReceivedReason).IsEqualTo(", because bar");
+ }
+
[Fact]
public async Task ShouldConsiderFurtherProcessingStrategy()
{
diff --git a/Tests/aweXpect.Core.Tests/Core/Nodes/AsyncMappingNodeTests.cs b/Tests/aweXpect.Core.Tests/Core/Nodes/AsyncMappingNodeTests.cs
index a802a9747..6f890af80 100644
--- a/Tests/aweXpect.Core.Tests/Core/Nodes/AsyncMappingNodeTests.cs
+++ b/Tests/aweXpect.Core.Tests/Core/Nodes/AsyncMappingNodeTests.cs
@@ -10,6 +10,58 @@ namespace aweXpect.Core.Tests.Core.Nodes;
public class AsyncMappingNodeTests
{
+ [Fact]
+ public async Task Equals_IfMemberAccessorsAreDifferent_ShouldBeFalse()
+ {
+ AsyncMappingNode node1 = new(
+ MemberAccessor>.FromFunc(s => Task.FromResult(s.Length), " length1 "));
+ AsyncMappingNode node2 = new(
+ MemberAccessor>.FromFunc(s => Task.FromResult(s.Length), " length2 "));
+
+ bool result = node1.Equals(node2);
+
+ await That(result).IsFalse();
+ await That(node1.GetHashCode()).IsNotEqualTo(node2.GetHashCode());
+ }
+
+ [Fact]
+ public async Task Equals_IfMemberAccessorsAreSame_ShouldBeTrue()
+ {
+ AsyncMappingNode node1 = new(
+ MemberAccessor>.FromFunc(s => Task.FromResult(s.Length), " length "));
+ AsyncMappingNode node2 = new(
+ MemberAccessor>.FromFunc(s => Task.FromResult(s.Length), " length "));
+
+ bool result = node1.Equals(node2);
+
+ await That(result).IsTrue();
+ await That(node1.GetHashCode()).IsEqualTo(node2.GetHashCode());
+ }
+
+ [Fact]
+ public async Task Equals_WhenOtherIsDifferentNode_ShouldBeFalse()
+ {
+ AsyncMappingNode node = new(
+ MemberAccessor>.FromFunc(s => Task.FromResult(s.Length), " length "));
+ object other = new AsyncMappingNode(
+ MemberAccessor>.FromFunc(s => Task.FromResult(s * 2), " duplicate "));
+
+ bool result = node.Equals(other);
+
+ await That(result).IsFalse();
+ }
+
+ [Fact]
+ public async Task Equals_WhenOtherIsNull_ShouldBeFalse()
+ {
+ AsyncMappingNode node = new(
+ MemberAccessor>.FromFunc(s => Task.FromResult(s.Length), " length "));
+
+ bool result = node.Equals(null);
+
+ await That(result).IsFalse();
+ }
+
[Fact]
public async Task IsMetBy_ShouldUseInnerConstraintWithOuterValue()
{
diff --git a/Tests/aweXpect.Core.Tests/Core/Nodes/ExpectationNodeTests.cs b/Tests/aweXpect.Core.Tests/Core/Nodes/ExpectationNodeTests.cs
index e5e333e7b..3403d679d 100644
--- a/Tests/aweXpect.Core.Tests/Core/Nodes/ExpectationNodeTests.cs
+++ b/Tests/aweXpect.Core.Tests/Core/Nodes/ExpectationNodeTests.cs
@@ -9,6 +9,128 @@ namespace aweXpect.Core.Tests.Core.Nodes;
public class ExpectationNodeTests
{
+ [Theory]
+ [InlineData(Outcome.Success, Outcome.Success, Outcome.Failure)]
+ [InlineData(Outcome.Failure, Outcome.Success, Outcome.Success)]
+ [InlineData(Outcome.Success, Outcome.Failure, Outcome.Success)]
+ [InlineData(Outcome.Failure, Outcome.Failure, Outcome.Success)]
+ [InlineData(Outcome.Failure, Outcome.Undecided, Outcome.Success)]
+ [InlineData(Outcome.Undecided, Outcome.Failure, Outcome.Success)]
+ [InlineData(Outcome.Undecided, Outcome.Undecided, Outcome.Undecided)]
+ public async Task AddAsyncMapping_NegatedResult_ShouldHaveExpectedOutcome(
+ Outcome node1, Outcome node2, Outcome expectedOutcome)
+ {
+ ExpectationNode node = new();
+ node.AddConstraint(new DummyValueConstraint(_ => new DummyConstraintResult(node1, "foo1", "bar1")));
+ node.AddAsyncMapping(MemberAccessor>.FromFunc(s => Task.FromResult(s.Length), " length: "))
+ .AddConstraint(new DummyValueConstraint(_ => new DummyConstraintResult(node2, "foo2", "bar2")));
+
+ ConstraintResult result = await node.IsMetBy("foobar", null!, CancellationToken.None);
+ result.Negate();
+
+ await That(result.Outcome).IsEqualTo(expectedOutcome);
+ }
+
+ [Theory]
+ [InlineData(Outcome.Success, Outcome.Success, Outcome.Success)]
+ [InlineData(Outcome.Failure, Outcome.Success, Outcome.Failure)]
+ [InlineData(Outcome.Success, Outcome.Failure, Outcome.Failure)]
+ [InlineData(Outcome.Failure, Outcome.Failure, Outcome.Failure)]
+ [InlineData(Outcome.Failure, Outcome.Undecided, Outcome.Failure)]
+ [InlineData(Outcome.Undecided, Outcome.Failure, Outcome.Failure)]
+ [InlineData(Outcome.Undecided, Outcome.Undecided, Outcome.Undecided)]
+ public async Task AddAsyncMapping_ShouldUseAndCombination(Outcome node1, Outcome node2, Outcome expectedOutcome)
+ {
+ ExpectationNode node = new();
+ node.AddConstraint(new DummyValueConstraint(_ => new DummyConstraintResult(node1, "foo1", "bar1")));
+ node.AddAsyncMapping(MemberAccessor>.FromFunc(s => Task.FromResult(s.Length), " length: "))
+ .AddConstraint(new DummyValueConstraint(_ => new DummyConstraintResult(node2, "foo2", "bar2")));
+ StringBuilder expectationSb = new();
+ StringBuilder resultSb = new();
+ string expectedResult = (node1 == Outcome.Failure, node2 == Outcome.Failure) switch
+ {
+ (true, true) => "bar1 and bar2",
+ (true, _) => "bar1",
+ (_, true) => "bar2",
+ (_, _) => "",
+ };
+
+ ConstraintResult result = await node.IsMetBy("foobar", null!, CancellationToken.None);
+ result.AppendExpectation(expectationSb);
+ result.AppendResult(resultSb);
+
+ await That(result.Outcome).IsEqualTo(expectedOutcome);
+ await That(expectationSb.ToString()).IsEqualTo("foo1 length: foo2");
+ await That(resultSb.ToString()).IsEqualTo(expectedResult);
+ }
+
+ [Fact]
+ public async Task AddAsyncMapping_TryGetValue_ShouldGetValueFromLeftNode()
+ {
+ ExpectationNode node = new();
+ node.AddConstraint(new DummyValueConstraint(_
+ => new DummyConstraintResult("foo", Outcome.Undecided, "foo1", "bar1")));
+ node.AddAsyncMapping(MemberAccessor>.FromFunc(s => Task.FromResult(s.Length), " length: "))
+ .AddConstraint(new DummyValueConstraint(_
+ => new DummyConstraintResult(1, Outcome.Undecided, "foo2", "bar2")));
+
+ ConstraintResult constraintResult = await node.IsMetBy("foobar", null!, CancellationToken.None);
+ bool result = constraintResult.TryGetValue(out string? value);
+
+ await That(result).IsTrue();
+ await That(value).IsEqualTo("foo");
+ }
+
+ [Fact]
+ public async Task AddAsyncMapping_TryGetValue_ShouldGetValueFromRightNode()
+ {
+ ExpectationNode node = new();
+ node.AddConstraint(new DummyValueConstraint(_
+ => new DummyConstraintResult(Outcome.Undecided, "foo1", "bar1")));
+ node.AddAsyncMapping(
+ MemberAccessor>.FromFunc(s => Task.FromResult(s.Substring(1)), " substring: "))
+ .AddConstraint(new DummyValueConstraint(_
+ => new DummyConstraintResult(Outcome.Undecided, "foo2", "bar2")));
+
+ ConstraintResult constraintResult = await node.IsMetBy("foobar", null!, CancellationToken.None);
+ bool result = constraintResult.TryGetValue(out string? value);
+
+ await That(result).IsTrue();
+ await That(value).IsEqualTo("foobar");
+ }
+
+ [Fact]
+ public async Task AddAsyncMapping_TryGetValue_WhenTypeDoesNotMatchAnyNode_ShouldReturnFalse()
+ {
+ ExpectationNode node = new();
+ node.AddConstraint(new DummyValueConstraint(_
+ => new DummyConstraintResult("foo", Outcome.Undecided, "foo1", "bar1")));
+ node.AddAsyncMapping(MemberAccessor>.FromFunc(s => Task.FromResult(s.Length), " length: "))
+ .AddConstraint(new DummyValueConstraint(_
+ => new DummyConstraintResult(42, Outcome.Undecided, "foo2", "bar2")));
+
+ ConstraintResult constraintResult = await node.IsMetBy("foobar", null!, CancellationToken.None);
+ bool result = constraintResult.TryGetValue(out DateTime? value);
+
+ await That(result).IsFalse();
+ await That(value).IsNull();
+ }
+
+ [Fact]
+ public async Task AddAsyncMapping_WithCustomExpectationTextGenerator_ShouldUseIt()
+ {
+ ExpectationNode node = new();
+ node.AddAsyncMapping(MemberAccessor>.FromFunc(s => Task.FromResult(s.Length), " length: "),
+ (m, sb) => sb.Append("my custom generator:").Append(m))
+ .AddConstraint(new DummyConstraint(_ => true, "yeah"));
+ StringBuilder sb = new();
+
+ ConstraintResult result = await node.IsMetBy("foobar", null!, CancellationToken.None);
+
+ result.AppendExpectation(sb);
+ await That(sb.ToString()).IsEqualTo("my custom generator: length: yeah");
+ }
+
[Fact]
public async Task AddConstraint_Twice_ShouldThrowInvalidOperationException()
{
@@ -52,6 +174,127 @@ public async Task AddConstraint_WithMapping_ShouldForwardToInnerNode()
await That(sb.ToString()).IsEqualTo("foobar");
}
+ [Theory]
+ [InlineData(Outcome.Success, Outcome.Success, Outcome.Failure)]
+ [InlineData(Outcome.Failure, Outcome.Success, Outcome.Success)]
+ [InlineData(Outcome.Success, Outcome.Failure, Outcome.Success)]
+ [InlineData(Outcome.Failure, Outcome.Failure, Outcome.Success)]
+ [InlineData(Outcome.Failure, Outcome.Undecided, Outcome.Success)]
+ [InlineData(Outcome.Undecided, Outcome.Failure, Outcome.Success)]
+ [InlineData(Outcome.Undecided, Outcome.Undecided, Outcome.Undecided)]
+ public async Task AddMapping_NegatedResult_ShouldHaveExpectedOutcome(
+ Outcome node1, Outcome node2, Outcome expectedOutcome)
+ {
+ ExpectationNode node = new();
+ node.AddConstraint(new DummyValueConstraint(_ => new DummyConstraintResult(node1, "foo1", "bar1")));
+ node.AddMapping(MemberAccessor.FromFunc(s => s.Length, " length: "))
+ .AddConstraint(new DummyValueConstraint(_ => new DummyConstraintResult(node2, "foo2", "bar2")));
+
+ ConstraintResult result = await node.IsMetBy("foobar", null!, CancellationToken.None);
+ result.Negate();
+
+ await That(result.Outcome).IsEqualTo(expectedOutcome);
+ }
+
+ [Theory]
+ [InlineData(Outcome.Success, Outcome.Success, Outcome.Success)]
+ [InlineData(Outcome.Failure, Outcome.Success, Outcome.Failure)]
+ [InlineData(Outcome.Success, Outcome.Failure, Outcome.Failure)]
+ [InlineData(Outcome.Failure, Outcome.Failure, Outcome.Failure)]
+ [InlineData(Outcome.Failure, Outcome.Undecided, Outcome.Failure)]
+ [InlineData(Outcome.Undecided, Outcome.Failure, Outcome.Failure)]
+ [InlineData(Outcome.Undecided, Outcome.Undecided, Outcome.Undecided)]
+ public async Task AddMapping_ShouldUseAndCombination(Outcome node1, Outcome node2, Outcome expectedOutcome)
+ {
+ ExpectationNode node = new();
+ node.AddConstraint(new DummyValueConstraint(_ => new DummyConstraintResult(node1, "foo1", "bar1")));
+ node.AddMapping(MemberAccessor.FromFunc(s => s.Length, " length: "))
+ .AddConstraint(new DummyValueConstraint(_ => new DummyConstraintResult(node2, "foo2", "bar2")));
+ StringBuilder expectationSb = new();
+ StringBuilder resultSb = new();
+ string expectedResult = (node1 == Outcome.Failure, node2 == Outcome.Failure) switch
+ {
+ (true, true) => "bar1 and bar2",
+ (true, _) => "bar1",
+ (_, true) => "bar2",
+ (_, _) => "",
+ };
+
+ ConstraintResult result = await node.IsMetBy("foobar", null!, CancellationToken.None);
+ result.AppendExpectation(expectationSb);
+ result.AppendResult(resultSb);
+
+ await That(result.Outcome).IsEqualTo(expectedOutcome);
+ await That(expectationSb.ToString()).IsEqualTo("foo1 length: foo2");
+ await That(resultSb.ToString()).IsEqualTo(expectedResult);
+ }
+
+ [Fact]
+ public async Task AddMapping_TryGetValue_ShouldGetValueFromLeftNode()
+ {
+ ExpectationNode node = new();
+ node.AddConstraint(new DummyValueConstraint(_
+ => new DummyConstraintResult("foo", Outcome.Undecided, "foo1", "bar1")));
+ node.AddMapping(MemberAccessor.FromFunc(s => s.Length, " length: "))
+ .AddConstraint(new DummyValueConstraint(_
+ => new DummyConstraintResult(1, Outcome.Undecided, "foo2", "bar2")));
+
+ ConstraintResult constraintResult = await node.IsMetBy("foobar", null!, CancellationToken.None);
+ bool result = constraintResult.TryGetValue(out string? value);
+
+ await That(result).IsTrue();
+ await That(value).IsEqualTo("foo");
+ }
+
+ [Fact]
+ public async Task AddMapping_TryGetValue_ShouldGetValueFromRightNode()
+ {
+ ExpectationNode node = new();
+ node.AddConstraint(new DummyValueConstraint(_
+ => new DummyConstraintResult(Outcome.Undecided, "foo1", "bar1")));
+ node.AddMapping(MemberAccessor.FromFunc(s => s.Substring(1), " substring: "))
+ .AddConstraint(new DummyValueConstraint(_
+ => new DummyConstraintResult(Outcome.Undecided, "foo2", "bar2")));
+
+ ConstraintResult constraintResult = await node.IsMetBy("foobar", null!, CancellationToken.None);
+ bool result = constraintResult.TryGetValue(out string? value);
+
+ await That(result).IsTrue();
+ await That(value).IsEqualTo("foobar");
+ }
+
+ [Fact]
+ public async Task AddMapping_TryGetValue_WhenTypeDoesNotMatchAnyNode_ShouldReturnFalse()
+ {
+ ExpectationNode node = new();
+ node.AddConstraint(new DummyValueConstraint(_
+ => new DummyConstraintResult("foo", Outcome.Undecided, "foo1", "bar1")));
+ node.AddMapping(MemberAccessor.FromFunc(s => s.Length, " length: "))
+ .AddConstraint(new DummyValueConstraint(_
+ => new DummyConstraintResult(42, Outcome.Undecided, "foo2", "bar2")));
+
+ ConstraintResult constraintResult = await node.IsMetBy("foobar", null!, CancellationToken.None);
+ bool result = constraintResult.TryGetValue(out DateTime? value);
+
+ await That(result).IsFalse();
+ await That(value).IsNull();
+ }
+
+ [Fact]
+ public async Task AddMapping_WithCustomExpectationTextGenerator_ShouldUseIt()
+ {
+ ExpectationNode node = new();
+ node.AddMapping(MemberAccessor.FromFunc(s => s.Length, " length: "),
+ (m, sb) => sb.Append("my custom generator:").Append(m))
+ .AddConstraint(new DummyConstraint(_ => true, "yeah"));
+ StringBuilder sb = new();
+
+ ConstraintResult result = await node.IsMetBy("foobar", null!, CancellationToken.None);
+
+ result.AppendExpectation(sb);
+ await That(sb.ToString()).IsEqualTo("my custom generator: length: yeah");
+ }
+
[Fact]
public async Task AddNode_ShouldThrowNotSupportedException()
{
@@ -143,6 +386,86 @@ public async Task AppendExpectation_WithMapping_ShouldReturnMapping()
await That(sb.ToString()).IsEqualTo("");
}
+ [Fact]
+ public async Task Equals_IfConstraintIsDifferent_ShouldBeFalse()
+ {
+ ExpectationNode node1 = new();
+ node1.AddConstraint(new DummyConstraint("foo"));
+ ExpectationNode node2 = new();
+ node2.AddConstraint(new DummyConstraint("bar"));
+
+ bool result = node1.Equals(node2);
+
+ await That(result).IsFalse();
+ }
+
+ [Fact]
+ public async Task Equals_IfConstraintIsTheSame_ShouldBeTrue()
+ {
+ ExpectationNode node1 = new();
+ node1.AddConstraint(new DummyConstraint("foo"));
+ ExpectationNode node2 = new();
+ node2.AddConstraint(new DummyConstraint("foo"));
+
+ bool result = node1.Equals(node2);
+
+ await That(result).IsTrue();
+ await That(node1.GetHashCode()).IsEqualTo(node2.GetHashCode());
+ }
+
+ [Fact]
+ public async Task Equals_IfInnerNodesAreDifferent_ShouldBeFalse()
+ {
+ ExpectationNode node1 = new();
+ node1
+ .AddMapping(MemberAccessor.FromFunc(s => s.Length, " with length1 "));
+ ExpectationNode node2 = new();
+ node2
+ .AddMapping(MemberAccessor.FromFunc(s => s.Length, " with length2 "));
+
+ bool result = node1.Equals(node2);
+
+ await That(result).IsFalse();
+ await That(node1.GetHashCode()).IsNotEqualTo(node2.GetHashCode());
+ }
+
+ [Fact]
+ public async Task Equals_IfInnerNodesAreSame_ShouldBeTrue()
+ {
+ ExpectationNode node1 = new();
+ node1
+ .AddMapping(MemberAccessor.FromFunc(s => s.Length, " with length "));
+ ExpectationNode node2 = new();
+ node2
+ .AddMapping(MemberAccessor.FromFunc(s => s.Length, " with length "));
+
+ bool result = node1.Equals(node2);
+
+ await That(result).IsTrue();
+ await That(node1.GetHashCode()).IsEqualTo(node2.GetHashCode());
+ }
+
+ [Fact]
+ public async Task Equals_WhenOtherIsDifferentNode_ShouldBeFalse()
+ {
+ ExpectationNode node = new();
+ object other = new DummyNode("");
+
+ bool result = node.Equals(other);
+
+ await That(result).IsFalse();
+ }
+
+ [Fact]
+ public async Task Equals_WhenOtherIsNull_ShouldBeFalse()
+ {
+ ExpectationNode node = new();
+
+ bool result = node.Equals(null);
+
+ await That(result).IsFalse();
+ }
+
[Fact]
public async Task IsMetBy_WhenAsyncConstraintReturns_ShouldApplyBecauseReason()
{
@@ -296,8 +619,8 @@ public async Task
{
ExpectationNode node = new();
node.AddConstraint(
- new DummyValueConstraint(
- v => new DummyConstraintResult(Outcome.Failure, v, "foo", "same failure")));
+ new DummyValueConstraint(v
+ => new DummyConstraintResult(Outcome.Failure, v, "foo", "same failure")));
node.AddAsyncMapping(MemberAccessor>.FromFunc(s => Task.FromResult(s), " with mapping "));
node.AddConstraint(new DummyValueConstraint(v
=> new DummyConstraintResult(Outcome.Failure, 2 * v, "bar", "same failure")));
@@ -339,8 +662,8 @@ public async Task
{
ExpectationNode node = new();
node.AddConstraint(
- new DummyValueConstraint(
- v => new DummyConstraintResult(Outcome.Failure, v, "foo", "same failure")));
+ new DummyValueConstraint(v
+ => new DummyConstraintResult(Outcome.Failure, v, "foo", "same failure")));
node.AddMapping(MemberAccessor.FromFunc(s => s, " with mapping "));
node.AddConstraint(new DummyValueConstraint(v
=> new DummyConstraintResult(Outcome.Failure, 2 * v, "bar", "same failure")));
diff --git a/Tests/aweXpect.Core.Tests/Core/Nodes/MappingNodeTests.cs b/Tests/aweXpect.Core.Tests/Core/Nodes/MappingNodeTests.cs
index 0024bd955..0cea92e9a 100644
--- a/Tests/aweXpect.Core.Tests/Core/Nodes/MappingNodeTests.cs
+++ b/Tests/aweXpect.Core.Tests/Core/Nodes/MappingNodeTests.cs
@@ -10,6 +10,55 @@ namespace aweXpect.Core.Tests.Core.Nodes;
public class MappingNodeTests
{
+ [Fact]
+ public async Task Equals_IfMemberAccessorsAreDifferent_ShouldBeFalse()
+ {
+ MappingNode node1 = new(
+ MemberAccessor.FromFunc(s => s.Length, " length1 "));
+ MappingNode node2 = new(
+ MemberAccessor.FromFunc(s => s.Length, " length2 "));
+
+ bool result = node1.Equals(node2);
+
+ await That(result).IsFalse();
+ await That(node1.GetHashCode()).IsNotEqualTo(node2.GetHashCode());
+ }
+
+ [Fact]
+ public async Task Equals_IfMemberAccessorsAreSame_ShouldBeTrue()
+ {
+ MappingNode node1 = new(
+ MemberAccessor.FromFunc(s => s.Length, " length "));
+ MappingNode node2 = new(
+ MemberAccessor.FromFunc(s => s.Length, " length "));
+
+ bool result = node1.Equals(node2);
+
+ await That(result).IsTrue();
+ await That(node1.GetHashCode()).IsEqualTo(node2.GetHashCode());
+ }
+
+ [Fact]
+ public async Task Equals_WhenOtherIsDifferentNode_ShouldBeFalse()
+ {
+ MappingNode node = new(MemberAccessor.FromFunc(s => s.Length, " length "));
+ object other = new MappingNode(MemberAccessor.FromFunc(s => s * 2, " duplicate "));
+
+ bool result = node.Equals(other);
+
+ await That(result).IsFalse();
+ }
+
+ [Fact]
+ public async Task Equals_WhenOtherIsNull_ShouldBeFalse()
+ {
+ MappingNode node = new(MemberAccessor.FromFunc(s => s.Length, " length "));
+
+ bool result = node.Equals(null);
+
+ await That(result).IsFalse();
+ }
+
[Fact]
public async Task IsMetBy_ShouldUseInnerConstraintWithOuterValue()
{
diff --git a/Tests/aweXpect.Core.Tests/Core/Nodes/OrNodeTests.cs b/Tests/aweXpect.Core.Tests/Core/Nodes/OrNodeTests.cs
index 87dd84931..0052e3dcb 100644
--- a/Tests/aweXpect.Core.Tests/Core/Nodes/OrNodeTests.cs
+++ b/Tests/aweXpect.Core.Tests/Core/Nodes/OrNodeTests.cs
@@ -1,6 +1,7 @@
using System.Text;
using System.Threading;
using aweXpect.Core.Constraints;
+using aweXpect.Core.Helpers;
using aweXpect.Core.Nodes;
using aweXpect.Core.Tests.TestHelpers;
@@ -8,10 +9,85 @@ namespace aweXpect.Core.Tests.Core.Nodes;
public sealed class OrNodeTests
{
+ [Fact]
+ public async Task AddAsyncMapping_ShouldUseCurrentNode()
+ {
+ MemberAccessor> memberAccessor =
+ MemberAccessor>.FromExpression(x => Task.FromResult(x.Length));
+ DummyNode first = new("foo");
+ DummyNode second = new("bar");
+ OrNode node = new(first);
+
+ node.AddAsyncMapping(memberAccessor);
+ node.AddNode(second);
+
+ await That(first.MappingMemberAccessor).IsSameAs(memberAccessor);
+ await That(second.MappingMemberAccessor).IsNull();
+ }
+
+ [Fact]
+ public async Task AddAsyncMapping_ShouldUseSecondNode()
+ {
+ MemberAccessor> memberAccessor =
+ MemberAccessor>.FromExpression(x => Task.FromResult(x.Length));
+ DummyNode first = new("foo");
+ DummyNode second = new("bar");
+ OrNode node = new(first);
+ node.AddNode(second);
+
+ node.AddAsyncMapping(memberAccessor);
+
+ await That(first.MappingMemberAccessor).IsNull();
+ await That(second.MappingMemberAccessor).IsSameAs(memberAccessor);
+ }
+
+ [Fact]
+ public async Task AddMapping_ShouldUseCurrentNode()
+ {
+ MemberAccessor memberAccessor = MemberAccessor.FromExpression(x => x.Length);
+ DummyNode first = new("foo");
+ DummyNode second = new("bar");
+ OrNode node = new(first);
+
+ node.AddMapping(memberAccessor);
+ node.AddNode(second);
+
+ await That(first.MappingMemberAccessor).IsSameAs(memberAccessor);
+ await That(second.MappingMemberAccessor).IsNull();
+ }
+
+ [Fact]
+ public async Task AddMapping_ShouldUseSecondNode()
+ {
+ MemberAccessor memberAccessor = MemberAccessor.FromExpression(x => x.Length);
+ DummyNode first = new("foo");
+ DummyNode second = new("bar");
+ OrNode node = new(first);
+ node.AddNode(second);
+
+ node.AddMapping(memberAccessor);
+
+ await That(first.MappingMemberAccessor).IsNull();
+ await That(second.MappingMemberAccessor).IsSameAs(memberAccessor);
+ }
+
+ [Fact]
+ public async Task AppendExpectation_WithAdditionalNodes_ShouldUseAllNodes()
+ {
+ OrNode node = new(new DummyNode("foo"));
+ node.AddNode(new DummyNode("bar"));
+ node.AddNode(new DummyNode("baz"));
+ StringBuilder sb = new();
+
+ node.AppendExpectation(sb);
+
+ await That(sb.ToString()).IsEqualTo("foo or bar or baz");
+ }
+
[Fact]
public async Task AppendExpectation_WithoutAdditionalNodes_ShouldUseFirstNode()
{
- AndNode node = new(new DummyNode("foo"));
+ OrNode node = new(new DummyNode("foo"));
StringBuilder sb = new();
node.AppendExpectation(sb);
@@ -19,11 +95,143 @@ public async Task AppendExpectation_WithoutAdditionalNodes_ShouldUseFirstNode()
await That(sb.ToString()).IsEqualTo("foo");
}
+ [Fact]
+ public async Task Equals_IfCurrentNodeIsDifferent_ShouldBeFalse()
+ {
+ DummyNode innerNode1 = new("1", () => new DummyConstraintResult(Outcome.Success, "1", ""));
+ DummyNode innerNode2 = new("2", () => new DummyConstraintResult(Outcome.Success, "2", ""));
+ OrNode node1 = new(innerNode1);
+ OrNode node2 = new(innerNode2);
+
+ bool result = node1.Equals(node2);
+
+ await That(result).IsFalse();
+ await That(node1.GetHashCode()).IsNotEqualTo(node2.GetHashCode());
+ }
+
+ [Fact]
+ public async Task Equals_IfCurrentNodeIsTheSame_ShouldBeTrue()
+ {
+ DummyNode innerNode1 = new("1", () => new DummyConstraintResult(Outcome.Success, "1", ""));
+ DummyNode innerNode2 = new("1", () => new DummyConstraintResult(Outcome.Success, "1", ""));
+ OrNode node1 = new(innerNode1);
+ OrNode node2 = new(innerNode2);
+
+ bool result = node1.Equals(node2);
+
+ await That(result).IsTrue();
+ await That(node1.GetHashCode()).IsEqualTo(node2.GetHashCode());
+ }
+
+ [Fact]
+ public async Task Equals_IfInnerNodesAreDifferent_ShouldBeFalse()
+ {
+ DummyNode innerNode0 = new("0", () => new DummyConstraintResult(Outcome.Success, "0", ""));
+ DummyNode innerNode1 = new("1", () => new DummyConstraintResult(Outcome.Success, "1", ""));
+ DummyNode innerNode2 = new("2", () => new DummyConstraintResult(Outcome.Success, "2", ""));
+ DummyNode currentNode = new("3", () => new DummyConstraintResult(Outcome.Success, "3", ""));
+ OrNode node1 = new(innerNode0);
+ node1.AddNode(innerNode1);
+ node1.AddNode(currentNode);
+ OrNode node2 = new(innerNode0);
+ node2.AddNode(innerNode2);
+ node2.AddNode(currentNode);
+
+ bool result = node1.Equals(node2);
+
+ await That(result).IsFalse();
+ await That(node1.GetHashCode()).IsNotEqualTo(node2.GetHashCode());
+ }
+
+ [Fact]
+ public async Task Equals_IfInnerNodesAreSame_ShouldBeTrue()
+ {
+ DummyNode innerNode1 = new("1", () => new DummyConstraintResult(Outcome.Success, "1", ""));
+ DummyNode innerNode2 = new("1", () => new DummyConstraintResult(Outcome.Success, "1", ""));
+ DummyNode innerNode3 = new("2", () => new DummyConstraintResult(Outcome.Success, "2", ""));
+ DummyNode innerNode4 = new("2", () => new DummyConstraintResult(Outcome.Success, "2", ""));
+ DummyNode currentNode = new("3", () => new DummyConstraintResult(Outcome.Success, "3", ""));
+ OrNode node1 = new(innerNode1);
+ node1.AddNode(innerNode3);
+ node1.AddNode(currentNode);
+ OrNode node2 = new(innerNode2);
+ node2.AddNode(innerNode4);
+ node2.AddNode(currentNode);
+
+ bool result = node1.Equals(node2);
+
+ await That(result).IsTrue();
+ await That(node1.GetHashCode()).IsEqualTo(node2.GetHashCode());
+ }
+
+ [Fact]
+ public async Task Equals_WhenOtherIsDifferentNode_ShouldBeFalse()
+ {
+ DummyNode inner = new("foo");
+ OrNode node = new(inner);
+ object other = new AndNode(inner);
+
+ bool result = node.Equals(other);
+
+ await That(result).IsFalse();
+ }
+
+ [Fact]
+ public async Task Equals_WhenOtherIsNull_ShouldBeFalse()
+ {
+ OrNode node = new(new DummyNode("foo"));
+
+ bool result = node.Equals(null);
+
+ await That(result).IsFalse();
+ }
+
+ [Theory]
+ [InlineData(Outcome.Success, Outcome.Success, Outcome.Failure)]
+ [InlineData(Outcome.Failure, Outcome.Success, Outcome.Failure)]
+ [InlineData(Outcome.Success, Outcome.Failure, Outcome.Failure)]
+ [InlineData(Outcome.Failure, Outcome.Failure, Outcome.Success)]
+ [InlineData(Outcome.Success, Outcome.Undecided, Outcome.Failure)]
+ [InlineData(Outcome.Undecided, Outcome.Success, Outcome.Failure)]
+ [InlineData(Outcome.Failure, Outcome.Undecided, Outcome.Undecided)]
+ [InlineData(Outcome.Undecided, Outcome.Failure, Outcome.Undecided)]
+ [InlineData(Outcome.Undecided, Outcome.Undecided, Outcome.Undecided)]
+ public async Task NegatedOutcome_ShouldBeExpected(Outcome node1, Outcome node2, Outcome expectedOutcome)
+ {
+ OrNode node = new(new DummyNode("", () => new DummyConstraintResult(node1)));
+ node.AddNode(new DummyNode("", () => new DummyConstraintResult(node2)));
+
+ ConstraintResult result = await node.IsMetBy(0, null!, CancellationToken.None);
+ result.Negate();
+
+ await That(result.Outcome).IsEqualTo(expectedOutcome);
+ }
+
+ [Fact]
+ public async Task NegatedResult_ShouldUseAndAsSeparator()
+ {
+ OrNode node = new(new DummyNode("", () => new DummyConstraintResult(Outcome.Success, "foo")));
+ node.AddNode(new DummyNode("", () => new DummyConstraintResult(Outcome.Success, "bar")));
+ StringBuilder sb1 = new();
+ StringBuilder sb2 = new();
+
+ ConstraintResult result = await node.IsMetBy(0, null!, CancellationToken.None);
+ result.AppendExpectation(sb1);
+
+ result.Negate();
+
+ result.AppendExpectation(sb2);
+ await That(sb1.ToString()).IsEqualTo("foo or bar");
+ await That(sb2.ToString()).IsEqualTo("foo and bar");
+ }
+
[Theory]
[InlineData(Outcome.Success, Outcome.Success, Outcome.Success)]
[InlineData(Outcome.Failure, Outcome.Success, Outcome.Success)]
[InlineData(Outcome.Success, Outcome.Failure, Outcome.Success)]
[InlineData(Outcome.Failure, Outcome.Failure, Outcome.Failure)]
+ [InlineData(Outcome.Success, Outcome.Undecided, Outcome.Success)]
+ [InlineData(Outcome.Undecided, Outcome.Success, Outcome.Success)]
[InlineData(Outcome.Failure, Outcome.Undecided, Outcome.Undecided)]
[InlineData(Outcome.Undecided, Outcome.Failure, Outcome.Undecided)]
[InlineData(Outcome.Undecided, Outcome.Undecided, Outcome.Undecided)]
@@ -37,6 +245,50 @@ public async Task Outcome_ShouldBeExpected(Outcome node1, Outcome node2, Outcome
await That(result.Outcome).IsEqualTo(expectedOutcome);
}
+ [Fact]
+ public async Task SetReason_WithAdditionalNodes_ShouldUseCurrentNode()
+ {
+ DummyNode node1 = new("node1");
+ DummyNode node2 = new("node2");
+ DummyNode current = new("current");
+ OrNode node = new(node1);
+ node.AddNode(node2);
+ node.AddNode(current);
+
+ node.SetReason(new BecauseReason("bar"));
+
+ await That(current.ReceivedReason).IsEqualTo(", because bar");
+ await That(node1.ReceivedReason).IsNull();
+ await That(node2.ReceivedReason).IsNull();
+ }
+
+ [Fact]
+ public async Task SetReason_WithAdditionalNodes_WhenCurrentNodeIsEmptyExpectationNode_ShouldUseLastNode()
+ {
+ DummyNode node1 = new("node1");
+ DummyNode node2 = new("node2");
+ ExpectationNode current = new();
+ OrNode node = new(node1);
+ node.AddNode(node2);
+ node.AddNode(current);
+
+ node.SetReason(new BecauseReason("bar"));
+
+ await That(node1.ReceivedReason).IsNull();
+ await That(node2.ReceivedReason).IsEqualTo(", because bar");
+ }
+
+ [Fact]
+ public async Task SetReason_WithoutAdditionalNodes_ShouldSetReasonForCurrentNode()
+ {
+ DummyNode current = new("current");
+ OrNode node = new(current);
+
+ node.SetReason(new BecauseReason("bar"));
+
+ await That(current.ReceivedReason).IsEqualTo(", because bar");
+ }
+
[Fact]
public async Task ShouldConsiderFurtherProcessingStrategy()
{
diff --git a/Tests/aweXpect.Core.Tests/Core/Nodes/WhichNodeTests.cs b/Tests/aweXpect.Core.Tests/Core/Nodes/WhichNodeTests.cs
index 1ba95c341..6266e447c 100644
--- a/Tests/aweXpect.Core.Tests/Core/Nodes/WhichNodeTests.cs
+++ b/Tests/aweXpect.Core.Tests/Core/Nodes/WhichNodeTests.cs
@@ -136,8 +136,8 @@ public async Task CombinedResult_ShouldBeNegatable(Outcome node1, Outcome node2,
[Fact]
public async Task Equals_IfInnerAreDifferent_ShouldBeFalse()
{
- DummyNode node1 = new("", () => new DummyConstraintResult(Outcome.Success, "1", ""));
- DummyNode node2 = new("", () => new DummyConstraintResult(Outcome.Success, "2", ""));
+ DummyNode node1 = new("1", () => new DummyConstraintResult(Outcome.Success, "1", ""));
+ DummyNode node2 = new("2", () => new DummyConstraintResult(Outcome.Success, "2", ""));
WhichNode whichNode1 = new(null, s => s.Length);
whichNode1.AddNode(node1);
WhichNode whichNode2 = new(null, s => s.Length);
@@ -151,7 +151,7 @@ public async Task Equals_IfInnerAreDifferent_ShouldBeFalse()
[Fact]
public async Task Equals_IfInnerAreSame_ShouldBeTrue()
{
- DummyNode node1 = new("", () => new DummyConstraintResult(Outcome.Success, "1", ""));
+ DummyNode node1 = new("1", () => new DummyConstraintResult(Outcome.Success, "1", ""));
WhichNode whichNode1 = new(null, s => s.Length);
whichNode1.AddNode(node1);
WhichNode whichNode2 = new(null, s => s.Length);
@@ -177,8 +177,8 @@ public async Task Equals_IfParentsAreBothNull_ShouldBeTrue()
[Fact]
public async Task Equals_IfParentsAreDifferent_ShouldBeFalse()
{
- DummyNode node1 = new("", () => new DummyConstraintResult(Outcome.Success, "1", ""));
- DummyNode node2 = new("", () => new DummyConstraintResult(Outcome.Success, "2", ""));
+ DummyNode node1 = new("1", () => new DummyConstraintResult(Outcome.Success, "1", ""));
+ DummyNode node2 = new("2", () => new DummyConstraintResult(Outcome.Success, "2", ""));
WhichNode whichNode1 = new(node1, s => s.Length);
WhichNode whichNode2 = new(node2, s => s.Length);
diff --git a/Tests/aweXpect.Core.Tests/TestHelpers/DummyConstraintResult.cs b/Tests/aweXpect.Core.Tests/TestHelpers/DummyConstraintResult.cs
index 45c7c7a91..1c9ecc19f 100644
--- a/Tests/aweXpect.Core.Tests/TestHelpers/DummyConstraintResult.cs
+++ b/Tests/aweXpect.Core.Tests/TestHelpers/DummyConstraintResult.cs
@@ -8,6 +8,7 @@ public sealed class DummyConstraintResult : ConstraintResult
{
private readonly string? _expectationText;
private readonly string? _failureText;
+ private readonly object? _value;
public DummyConstraintResult(Outcome outcome,
string? expectationText = null,
@@ -20,6 +21,16 @@ public DummyConstraintResult(Outcome outcome,
_failureText = failureText;
}
+ public DummyConstraintResult(object value,
+ Outcome outcome,
+ string? expectationText = null,
+ string? failureText = null,
+ FurtherProcessingStrategy furtherProcessingStrategy = FurtherProcessingStrategy.Continue)
+ : this(outcome, expectationText, failureText, furtherProcessingStrategy)
+ {
+ _value = value;
+ }
+
public override void AppendExpectation(StringBuilder stringBuilder, string? indentation = null)
{
if (_expectationText != null)
@@ -38,6 +49,12 @@ public override void AppendResult(StringBuilder stringBuilder, string? indentati
public override bool TryGetValue([NotNullWhen(true)] out TValue? value) where TValue : default
{
+ if (_value is TValue typedValue)
+ {
+ value = typedValue;
+ return true;
+ }
+
value = default;
return false;
}
diff --git a/Tests/aweXpect.Core.Tests/TestHelpers/DummyNode.cs b/Tests/aweXpect.Core.Tests/TestHelpers/DummyNode.cs
index 78a5b9443..89702eecc 100644
--- a/Tests/aweXpect.Core.Tests/TestHelpers/DummyNode.cs
+++ b/Tests/aweXpect.Core.Tests/TestHelpers/DummyNode.cs
@@ -9,7 +9,9 @@ namespace aweXpect.Core.Tests.TestHelpers;
internal class DummyNode(string name, Func? result = null) : Node
{
+ private readonly string _name = name;
public MemberAccessor? MappingMemberAccessor { get; private set; }
+ public string? ReceivedReason { get; private set; }
public override void AddConstraint(IConstraint constraint)
=> throw new NotSupportedException();
@@ -45,8 +47,16 @@ public override Task IsMetBy(
=> result == null ? throw new NotSupportedException() : Task.FromResult(result());
public override void SetReason(BecauseReason becauseReason)
- => throw new NotSupportedException();
+ => ReceivedReason = becauseReason.ToString();
public override void AppendExpectation(StringBuilder stringBuilder, string? indentation = null)
- => stringBuilder.Append(name);
+ => stringBuilder.Append(_name);
+
+ ///
+ public override bool Equals(object? obj) => obj is DummyNode other && Equals(other);
+
+ private bool Equals(DummyNode other) => _name == other._name;
+
+ ///
+ public override int GetHashCode() => _name.GetHashCode();
}