diff --git a/Source/aweXpect/That/ThatGeneric.IsEquatableTo.cs b/Source/aweXpect/That/ThatGeneric.IsEquatableTo.cs new file mode 100644 index 000000000..bea9730af --- /dev/null +++ b/Source/aweXpect/That/ThatGeneric.IsEquatableTo.cs @@ -0,0 +1,74 @@ +using System; +using aweXpect.Core; +using aweXpect.Core.Constraints; +using aweXpect.Helpers; +using aweXpect.Results; + +namespace aweXpect; + +public static partial class ThatGeneric +{ + /// + /// Verifies that the subject is equal to the value + /// using the method. + /// + public static AndOrResult> IsEquatableTo( + this IThat source, + T expected) + where TEquatable : IEquatable + => new(source.Get().ExpectationBuilder.AddConstraint((it, grammars) + => new IsEquatableToConstraint(it, grammars, expected)), + source); + + /// + /// Verifies that the subject is not equal to the value + /// using the method. + /// + public static AndOrResult> IsNotEquatableTo( + this IThat source, + T unexpected) + where TEquatable : IEquatable + => new(source.Get().ExpectationBuilder.AddConstraint((it, grammars) + => new IsEquatableToConstraint(it, grammars, unexpected).Invert()), + source); + + private sealed class IsEquatableToConstraint( + string it, + ExpectationGrammars grammars, + T expected) + : ConstraintResult.WithValue>(grammars), + IValueConstraint + where TEquatable : IEquatable + { + public ConstraintResult IsMetBy(TEquatable actual) + { + Actual = actual; + Outcome = actual.Equals(expected) ? Outcome.Success : Outcome.Failure; + return this; + } + + protected override void AppendNormalExpectation(StringBuilder stringBuilder, string? indentation = null) + { + stringBuilder.Append("is equatable to "); + Formatter.Format(stringBuilder, expected); + } + + protected override void AppendNormalResult(StringBuilder stringBuilder, string? indentation = null) + { + stringBuilder.Append(it).Append(" was "); + Formatter.Format(stringBuilder, Actual); + } + + protected override void AppendNegatedExpectation(StringBuilder stringBuilder, string? indentation = null) + { + stringBuilder.Append("is not equatable to "); + Formatter.Format(stringBuilder, expected); + } + + protected override void AppendNegatedResult(StringBuilder stringBuilder, string? indentation = null) + { + stringBuilder.Append(it).Append(" was in "); + Formatter.Format(stringBuilder, Actual); + } + } +} diff --git a/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt b/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt index 008ede787..43c593949 100644 --- a/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt +++ b/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt @@ -724,6 +724,10 @@ namespace aweXpect public static aweXpect.Results.RepeatedCheckResult> DoesNotComplyWith(this aweXpect.Core.IThat source, System.Action> expectations) { } public static aweXpect.Results.AndOrResult> DoesNotSatisfy(this aweXpect.Core.IThat source, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { } public static aweXpect.Results.AndOrResult> For(this aweXpect.Core.IThat source, System.Func memberSelector, System.Action> expectations, [System.Runtime.CompilerServices.CallerArgumentExpression("memberSelector")] string doNotPopulateThisValue = "") { } + public static aweXpect.Results.AndOrResult> IsEquatableTo(this aweXpect.Core.IThat source, T expected) + where TEquatable : System.IEquatable { } + public static aweXpect.Results.AndOrResult> IsNotEquatableTo(this aweXpect.Core.IThat source, T unexpected) + where TEquatable : System.IEquatable { } public static aweXpect.Results.RepeatedCheckResult> Satisfies(this aweXpect.Core.IThat source, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { } } public static class ThatGuid diff --git a/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt b/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt index c33f3ae16..f319217a3 100644 --- a/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt +++ b/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt @@ -487,6 +487,10 @@ namespace aweXpect public static aweXpect.Results.RepeatedCheckResult> DoesNotComplyWith(this aweXpect.Core.IThat source, System.Action> expectations) { } public static aweXpect.Results.AndOrResult> DoesNotSatisfy(this aweXpect.Core.IThat source, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { } public static aweXpect.Results.AndOrResult> For(this aweXpect.Core.IThat source, System.Func memberSelector, System.Action> expectations, [System.Runtime.CompilerServices.CallerArgumentExpression("memberSelector")] string doNotPopulateThisValue = "") { } + public static aweXpect.Results.AndOrResult> IsEquatableTo(this aweXpect.Core.IThat source, T expected) + where TEquatable : System.IEquatable { } + public static aweXpect.Results.AndOrResult> IsNotEquatableTo(this aweXpect.Core.IThat source, T unexpected) + where TEquatable : System.IEquatable { } public static aweXpect.Results.RepeatedCheckResult> Satisfies(this aweXpect.Core.IThat source, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "") { } } public static class ThatGuid diff --git a/Tests/aweXpect.Tests/ThatGeneric.IsEquatableTo.Tests.cs b/Tests/aweXpect.Tests/ThatGeneric.IsEquatableTo.Tests.cs new file mode 100644 index 000000000..f5b839f23 --- /dev/null +++ b/Tests/aweXpect.Tests/ThatGeneric.IsEquatableTo.Tests.cs @@ -0,0 +1,150 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatGeneric +{ + public sealed class IsEquatableTo + { + public sealed class Tests + { + [Fact] + public async Task WhenComparingToMatchingLong_ShouldSucceed() + { + Wrapper subject = new(1); + + async Task Act() + => await That(subject).IsEquatableTo(1L); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenComparingToMatchingWrapper_ShouldSucceed() + { + Wrapper subject = new(1); + Wrapper expected = new(1); + + async Task Act() + => await That(subject).IsEquatableTo(expected); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenComparingToNotMatchingLong_ShouldFail() + { + Wrapper subject = new(1); + + async Task Act() + => await That(subject).IsEquatableTo(2L); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is equatable to 2, + but it was ThatGeneric.IsEquatableTo.Wrapper { + Value = 1 + } + """); + } + + [Fact] + public async Task WhenComparingToNotMatchingWrapper_ShouldFail() + { + Wrapper subject = new(1); + Wrapper expected = new(3); + + async Task Act() + => await That(subject).IsEquatableTo(expected); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is equatable to ThatGeneric.IsEquatableTo.Wrapper { + Value = 3 + }, + but it was ThatGeneric.IsEquatableTo.Wrapper { + Value = 1 + } + """); + } + } + + public sealed class NegatedTests + { + [Fact] + public async Task WhenComparingToMatchingLong_ShouldFail() + { + Wrapper subject = new(1); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsEquatableTo(1L)); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is not equatable to 1, + but it was in ThatGeneric.IsEquatableTo.Wrapper { + Value = 1 + } + """); + } + + [Fact] + public async Task WhenComparingToMatchingWrapper_ShouldFail() + { + Wrapper subject = new(1); + Wrapper expected = new(1); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsEquatableTo(expected)); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is not equatable to ThatGeneric.IsEquatableTo.Wrapper { + Value = 1 + }, + but it was in ThatGeneric.IsEquatableTo.Wrapper { + Value = 1 + } + """); + } + + [Fact] + public async Task WhenComparingToNotMatchingLong_ShouldSucceed() + { + Wrapper subject = new(1); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsEquatableTo(2L)); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenComparingToNotMatchingWrapper_ShouldSucceed() + { + Wrapper subject = new(1); + Wrapper expected = new(3); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsEquatableTo(expected)); + + await That(Act).DoesNotThrow(); + } + } + + private readonly struct Wrapper(long value) + : IEquatable, + IEquatable + { + public long Value { get; } = value; + + public bool Equals(Wrapper other) + => Value == other.Value; + + public bool Equals(long other) + => Value == other; + } + } +} diff --git a/Tests/aweXpect.Tests/ThatGeneric.IsNotEquatableTo.Tests.cs b/Tests/aweXpect.Tests/ThatGeneric.IsNotEquatableTo.Tests.cs new file mode 100644 index 000000000..6def14d0e --- /dev/null +++ b/Tests/aweXpect.Tests/ThatGeneric.IsNotEquatableTo.Tests.cs @@ -0,0 +1,150 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatGeneric +{ + public sealed class IsNotEquatableTo + { + public sealed class Tests + { + [Fact] + public async Task WhenComparingToMatchingLong_ShouldFail() + { + Wrapper subject = new(1); + + async Task Act() + => await That(subject).IsNotEquatableTo(1L); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is not equatable to 1, + but it was in ThatGeneric.IsNotEquatableTo.Wrapper { + Value = 1 + } + """); + } + + [Fact] + public async Task WhenComparingToMatchingWrapper_ShouldFail() + { + Wrapper subject = new(1); + Wrapper expected = new(1); + + async Task Act() + => await That(subject).IsNotEquatableTo(expected); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is not equatable to ThatGeneric.IsNotEquatableTo.Wrapper { + Value = 1 + }, + but it was in ThatGeneric.IsNotEquatableTo.Wrapper { + Value = 1 + } + """); + } + + [Fact] + public async Task WhenComparingToNotMatchingLong_ShouldSucceed() + { + Wrapper subject = new(1); + + async Task Act() + => await That(subject).IsNotEquatableTo(2L); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenComparingToNotMatchingWrapper_ShouldSucceed() + { + Wrapper subject = new(1); + Wrapper expected = new(3); + + async Task Act() + => await That(subject).IsNotEquatableTo(expected); + + await That(Act).DoesNotThrow(); + } + } + + public sealed class NegatedTests + { + [Fact] + public async Task WhenComparingToMatchingLong_ShouldSucceed() + { + Wrapper subject = new(1); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsNotEquatableTo(1L)); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenComparingToMatchingWrapper_ShouldSucceed() + { + Wrapper subject = new(1); + Wrapper expected = new(1); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsNotEquatableTo(expected)); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenComparingToNotMatchingLong_ShouldFail() + { + Wrapper subject = new(1); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsNotEquatableTo(2L)); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is equatable to 2, + but it was ThatGeneric.IsNotEquatableTo.Wrapper { + Value = 1 + } + """); + } + + [Fact] + public async Task WhenComparingToNotMatchingWrapper_ShouldFail() + { + Wrapper subject = new(1); + Wrapper expected = new(3); + + async Task Act() + => await That(subject).DoesNotComplyWith(it => it.IsNotEquatableTo(expected)); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is equatable to ThatGeneric.IsNotEquatableTo.Wrapper { + Value = 3 + }, + but it was ThatGeneric.IsNotEquatableTo.Wrapper { + Value = 1 + } + """); + } + } + + private readonly struct Wrapper(long value) + : IEquatable, + IEquatable + { + public long Value { get; } = value; + + public bool Equals(Wrapper other) + => Value == other.Value; + + public bool Equals(long other) + => Value == other; + } + } +}