diff --git a/src/MrKWatkins.Assertions.Tests/EnumerableAssertionsTests.cs b/src/MrKWatkins.Assertions.Tests/EnumerableAssertionsTests.cs index 986bcd9..d8aa953 100644 --- a/src/MrKWatkins.Assertions.Tests/EnumerableAssertionsTests.cs +++ b/src/MrKWatkins.Assertions.Tests/EnumerableAssertionsTests.cs @@ -132,6 +132,44 @@ public async Task SequenceEqual_Chain() await Assert.That(chain.And.Value).IsEqualTo(value); } + [Test] + public async Task SequenceEqual_Comparer() + { + var value = new List { "a", "b", "c" }; + + await Assert.That(() => value.Should().SequenceEqual(new List { "A", "B", "D" }, StringComparer.OrdinalIgnoreCase)).Throws(); + await Assert.That(() => value.Should().SequenceEqual(new List { "A", "B", "C" }, StringComparer.OrdinalIgnoreCase)).ThrowsNothing(); + } + + [Test] + public async Task SequenceEqual_Comparer_Chain() + { + var value = new List { "a", "b", "c" }; + + var chain = value.Should().SequenceEqual(new List { "A", "B", "C" }, StringComparer.OrdinalIgnoreCase); + await Assert.That(chain.Value).IsEqualTo(value); + await Assert.That(chain.And.Value).IsEqualTo(value); + } + + [Test] + public async Task SequenceEqual_Predicate() + { + var value = new List { "a", "b", "c" }; + + await Assert.That(() => value.Should().SequenceEqual(new List { "A", "B", "D" }, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase))).Throws(); + await Assert.That(() => value.Should().SequenceEqual(new List { "A", "B", "C" }, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase))).ThrowsNothing(); + } + + [Test] + public async Task SequenceEqual_Predicate_Chain() + { + var value = new List { "a", "b", "c" }; + + var chain = value.Should().SequenceEqual(new List { "A", "B", "C" }, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); + await Assert.That(chain.Value).IsEqualTo(value); + await Assert.That(chain.And.Value).IsEqualTo(value); + } + [Test] public async Task NotSequenceEqual_Null() { @@ -169,6 +207,46 @@ public async Task NotSequenceEqual_Chain() await Assert.That(chain.And.Value).IsEqualTo(value); } + [Test] + public async Task NotSequenceEqual_Comparer() + { + var value = new List { "a", "b", "c" }; + + await Assert.That(() => value.Should().NotSequenceEqual(new List { "A", "B", "C" }, StringComparer.OrdinalIgnoreCase)).Throws() + .WithMessage("Value should not sequence equal [\"A\", \"B\", \"C\"]."); + await Assert.That(() => value.Should().NotSequenceEqual(new List { "A", "B", "D" }, StringComparer.OrdinalIgnoreCase)).ThrowsNothing(); + } + + [Test] + public async Task NotSequenceEqual_Comparer_Chain() + { + var value = new List { "a", "b", "c" }; + + var chain = value.Should().NotSequenceEqual(new List { "x", "y", "z" }, StringComparer.OrdinalIgnoreCase); + await Assert.That(chain.Value).IsEqualTo(value); + await Assert.That(chain.And.Value).IsEqualTo(value); + } + + [Test] + public async Task NotSequenceEqual_Predicate() + { + var value = new List { "a", "b", "c" }; + + await Assert.That(() => value.Should().NotSequenceEqual(new List { "A", "B", "C" }, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase))).Throws() + .WithMessage("Value should not sequence equal [\"A\", \"B\", \"C\"]."); + await Assert.That(() => value.Should().NotSequenceEqual(new List { "A", "B", "D" }, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase))).ThrowsNothing(); + } + + [Test] + public async Task NotSequenceEqual_Predicate_Chain() + { + var value = new List { "a", "b", "c" }; + + var chain = value.Should().NotSequenceEqual(new List { "x", "y", "z" }, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); + await Assert.That(chain.Value).IsEqualTo(value); + await Assert.That(chain.And.Value).IsEqualTo(value); + } + [Test] public async Task Contain_Null() { diff --git a/src/MrKWatkins.Assertions.Tests/ObjectAssertionsTests.cs b/src/MrKWatkins.Assertions.Tests/ObjectAssertionsTests.cs index 79d4442..c56f681 100644 --- a/src/MrKWatkins.Assertions.Tests/ObjectAssertionsTests.cs +++ b/src/MrKWatkins.Assertions.Tests/ObjectAssertionsTests.cs @@ -112,6 +112,72 @@ public async Task Equal_Chain() await Assert.That(and.Value).IsEqualTo(value); } + [Test] + public async Task Equal_Comparer_Null() + { + string? nullValue = null; + const string nonNullValue = "Not Null"; + + await Assert.That(() => nullValue.Should().Equal(nonNullValue, StringComparer.OrdinalIgnoreCase)).Throws().WithMessage("Value should equal \"Not Null\" but was null."); + await Assert.That(() => nullValue.Should().Equal(nullValue, StringComparer.OrdinalIgnoreCase)).ThrowsNothing(); + } + + [Test] + public async Task Equal_Comparer() + { + const string value = "Test"; + const string sameValueDifferentCase = "test"; + const string otherValue = "Not Test"; + + await Assert.That(() => value.Should().Equal(otherValue, StringComparer.OrdinalIgnoreCase)).Throws().WithMessage("Value should equal \"Not Test\" but was \"Test\"."); + await Assert.That(() => value.Should().Equal(sameValueDifferentCase, StringComparer.OrdinalIgnoreCase)).ThrowsNothing(); + } + + [Test] + public async Task Equal_Comparer_Chain() + { + const string value = "Test"; + + var chain = value.Should().Equal("test", StringComparer.OrdinalIgnoreCase); + await Assert.That(chain.Value).IsEqualTo(value); + + var and = chain.And; + await Assert.That(and.Value).IsEqualTo(value); + } + + [Test] + public async Task Equal_Predicate_Null() + { + string? nullValue = null; + const string nonNullValue = "Not Null"; + + await Assert.That(() => nullValue.Should().Equal(nonNullValue, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase))).Throws().WithMessage("Value should equal \"Not Null\" but was null."); + await Assert.That(() => nullValue.Should().Equal(nullValue, (a, b) => a is null && b is null)).ThrowsNothing(); + } + + [Test] + public async Task Equal_Predicate() + { + const string value = "Test"; + const string sameValueDifferentCase = "test"; + const string otherValue = "Not Test"; + + await Assert.That(() => value.Should().Equal(otherValue, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase))).Throws().WithMessage("Value should equal \"Not Test\" but was \"Test\"."); + await Assert.That(() => value.Should().Equal(sameValueDifferentCase, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase))).ThrowsNothing(); + } + + [Test] + public async Task Equal_Predicate_Chain() + { + const string value = "Test"; + + var chain = value.Should().Equal("test", (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); + await Assert.That(chain.Value).IsEqualTo(value); + + var and = chain.And; + await Assert.That(and.Value).IsEqualTo(value); + } + [Test] public async Task NotEqual_Null() { @@ -145,6 +211,74 @@ public async Task NotEqual_Chain() await Assert.That(and.Value).IsEqualTo(value); } + [Test] + public async Task NotEqual_Comparer_Null() + { + string? nullValue = null; + const string nonNullValue = "Not Null"; + + await Assert.That(() => nullValue.Should().NotEqual(nonNullValue, StringComparer.OrdinalIgnoreCase)).ThrowsNothing(); + await Assert.That(() => nullValue.Should().NotEqual(nullValue, StringComparer.OrdinalIgnoreCase)).Throws().WithMessage("Value should not equal null."); + } + + [Test] + public async Task NotEqual_Comparer() + { + const string value = "Test"; + const string sameValueDifferentCase = "test"; + const string otherValue = "Not Test"; + + await Assert.That(() => value.Should().NotEqual(otherValue, StringComparer.OrdinalIgnoreCase)).ThrowsNothing(); + await Assert.That(() => value.Should().NotEqual(sameValueDifferentCase, StringComparer.OrdinalIgnoreCase)).Throws().WithMessage("Value should not equal \"test\"."); + } + + [Test] + public async Task NotEqual_Comparer_Chain() + { + const string value = "Test"; + const string otherValue = "Not Test"; + + var chain = value.Should().NotEqual(otherValue, StringComparer.OrdinalIgnoreCase); + await Assert.That(chain.Value).IsEqualTo(value); + + var and = chain.And; + await Assert.That(and.Value).IsEqualTo(value); + } + + [Test] + public async Task NotEqual_Predicate_Null() + { + string? nullValue = null; + const string nonNullValue = "Not Null"; + + await Assert.That(() => nullValue.Should().NotEqual(nonNullValue, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase))).ThrowsNothing(); + await Assert.That(() => nullValue.Should().NotEqual(nullValue, (a, b) => a is null && b is null)).Throws().WithMessage("Value should not equal null."); + } + + [Test] + public async Task NotEqual_Predicate() + { + const string value = "Test"; + const string sameValueDifferentCase = "test"; + const string otherValue = "Not Test"; + + await Assert.That(() => value.Should().NotEqual(otherValue, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase))).ThrowsNothing(); + await Assert.That(() => value.Should().NotEqual(sameValueDifferentCase, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase))).Throws().WithMessage("Value should not equal \"test\"."); + } + + [Test] + public async Task NotEqual_Predicate_Chain() + { + const string value = "Test"; + const string otherValue = "Not Test"; + + var chain = value.Should().NotEqual(otherValue, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); + await Assert.That(chain.Value).IsEqualTo(value); + + var and = chain.And; + await Assert.That(and.Value).IsEqualTo(value); + } + [Test] public async Task BeTheSameInstanceAs() { diff --git a/src/MrKWatkins.Assertions.Tests/ReadOnlySpanAssertionsTests.cs b/src/MrKWatkins.Assertions.Tests/ReadOnlySpanAssertionsTests.cs index 5e13cbd..83ab968 100644 --- a/src/MrKWatkins.Assertions.Tests/ReadOnlySpanAssertionsTests.cs +++ b/src/MrKWatkins.Assertions.Tests/ReadOnlySpanAssertionsTests.cs @@ -128,6 +128,42 @@ public async Task SequenceEqual_Chain_And_Value() await Assert.That(and.Value == value).IsTrue(); } + [Test] + public async Task SequenceEqual_Comparer() + { + await Assert.That(() => + { + ReadOnlySpan value = ["a", "b", "c"]; + ReadOnlySpan different = ["A", "B", "D"]; + value.Should().SequenceEqual(different, StringComparer.OrdinalIgnoreCase); + }).Throws(); + + await Assert.That(() => + { + ReadOnlySpan value = ["a", "b", "c"]; + ReadOnlySpan same = ["A", "B", "C"]; + value.Should().SequenceEqual(same, StringComparer.OrdinalIgnoreCase); + }).ThrowsNothing(); + } + + [Test] + public async Task SequenceEqual_Predicate() + { + await Assert.That(() => + { + ReadOnlySpan value = ["a", "b", "c"]; + ReadOnlySpan different = ["A", "B", "D"]; + value.Should().SequenceEqual(different, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); + }).Throws(); + + await Assert.That(() => + { + ReadOnlySpan value = ["a", "b", "c"]; + ReadOnlySpan same = ["A", "B", "C"]; + value.Should().SequenceEqual(same, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); + }).ThrowsNothing(); + } + [Test] public async Task SequenceEqual_IEnumerable() { @@ -242,6 +278,42 @@ public async Task SequenceEqual_IEnumerable_Chain_And_Value() await Assert.That(and.Value == value).IsTrue(); } + [Test] + public async Task SequenceEqual_IEnumerable_Comparer() + { + await Assert.That(() => + { + ReadOnlySpan value = ["a", "b", "c"]; + IReadOnlyList different = ["A", "B", "D"]; + value.Should().SequenceEqual(different, StringComparer.OrdinalIgnoreCase); + }).Throws(); + + await Assert.That(() => + { + ReadOnlySpan value = ["a", "b", "c"]; + IReadOnlyList same = ["A", "B", "C"]; + value.Should().SequenceEqual(same, StringComparer.OrdinalIgnoreCase); + }).ThrowsNothing(); + } + + [Test] + public async Task SequenceEqual_IEnumerable_Predicate() + { + await Assert.That(() => + { + ReadOnlySpan value = ["a", "b", "c"]; + IReadOnlyList different = ["A", "B", "D"]; + value.Should().SequenceEqual(different, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); + }).Throws(); + + await Assert.That(() => + { + ReadOnlySpan value = ["a", "b", "c"]; + IReadOnlyList same = ["A", "B", "C"]; + value.Should().SequenceEqual(same, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); + }).ThrowsNothing(); + } + [Test] public async Task NotSequenceEqual() { @@ -308,6 +380,42 @@ public async Task NotSequenceEqual_Chain_And_Value() await Assert.That(and.Value == value).IsTrue(); } + [Test] + public async Task NotSequenceEqual_Comparer() + { + await Assert.That(() => + { + ReadOnlySpan value = ["a", "b", "c"]; + ReadOnlySpan same = ["A", "B", "C"]; + value.Should().NotSequenceEqual(same, StringComparer.OrdinalIgnoreCase); + }).Throws(); + + await Assert.That(() => + { + ReadOnlySpan value = ["a", "b", "c"]; + ReadOnlySpan different = ["A", "B", "D"]; + value.Should().NotSequenceEqual(different, StringComparer.OrdinalIgnoreCase); + }).ThrowsNothing(); + } + + [Test] + public async Task NotSequenceEqual_Predicate() + { + await Assert.That(() => + { + ReadOnlySpan value = ["a", "b", "c"]; + ReadOnlySpan same = ["A", "B", "C"]; + value.Should().NotSequenceEqual(same, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); + }).Throws(); + + await Assert.That(() => + { + ReadOnlySpan value = ["a", "b", "c"]; + ReadOnlySpan different = ["A", "B", "D"]; + value.Should().NotSequenceEqual(different, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); + }).ThrowsNothing(); + } + [Test] public async Task NotSequenceEqual_IEnumerable() { @@ -366,4 +474,40 @@ public async Task NotSequenceEqual_IEnumerable_Chain_And_Value() var and = value.Should().NotSequenceEqual(notSequenceEqual).And; await Assert.That(and.Value == value).IsTrue(); } + + [Test] + public async Task NotSequenceEqual_IEnumerable_Comparer() + { + await Assert.That(() => + { + ReadOnlySpan value = ["a", "b", "c"]; + IReadOnlyList same = ["A", "B", "C"]; + value.Should().NotSequenceEqual(same, StringComparer.OrdinalIgnoreCase); + }).Throws(); + + await Assert.That(() => + { + ReadOnlySpan value = ["a", "b", "c"]; + IReadOnlyList different = ["A", "B", "D"]; + value.Should().NotSequenceEqual(different, StringComparer.OrdinalIgnoreCase); + }).ThrowsNothing(); + } + + [Test] + public async Task NotSequenceEqual_IEnumerable_Predicate() + { + await Assert.That(() => + { + ReadOnlySpan value = ["a", "b", "c"]; + IReadOnlyList same = ["A", "B", "C"]; + value.Should().NotSequenceEqual(same, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); + }).Throws(); + + await Assert.That(() => + { + ReadOnlySpan value = ["a", "b", "c"]; + IReadOnlyList different = ["A", "B", "D"]; + value.Should().NotSequenceEqual(different, (a, b) => string.Equals(a, b, StringComparison.OrdinalIgnoreCase)); + }).ThrowsNothing(); + } } \ No newline at end of file diff --git a/src/MrKWatkins.Assertions/EnumerableAssertions.cs b/src/MrKWatkins.Assertions/EnumerableAssertions.cs index d609d8d..2c8948e 100644 --- a/src/MrKWatkins.Assertions/EnumerableAssertions.cs +++ b/src/MrKWatkins.Assertions/EnumerableAssertions.cs @@ -54,7 +54,31 @@ public ObjectAssertionsChain ContainSingle([InstantHandle] Func pred /// The expected elements. /// An for chaining further assertions. [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] - public EnumerableAssertionsChain SequenceEqual([InstantHandle] params IEnumerable expected) + public EnumerableAssertionsChain SequenceEqual([InstantHandle] params IEnumerable expected) => + SequenceEqualCore(expected, EqualityComparer.Default.Equals); + + /// + /// Asserts that the enumerable is sequence equal to the expected elements using the specified equality comparer. + /// + /// The expected elements. + /// The equality comparer to use for element comparison. + /// An for chaining further assertions. + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + public EnumerableAssertionsChain SequenceEqual([InstantHandle] IEnumerable expected, IEqualityComparer comparer) => + SequenceEqualCore(expected, comparer.Equals); + + /// + /// Asserts that the enumerable is sequence equal to the expected elements using the specified predicate. + /// + /// The expected elements. + /// The predicate to use for element comparison. + /// An for chaining further assertions. + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + public EnumerableAssertionsChain SequenceEqual([InstantHandle] IEnumerable expected, Func predicate) => + SequenceEqualCore(expected, predicate); + + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + private EnumerableAssertionsChain SequenceEqualCore(IEnumerable expected, Func equals) { NotBeNull(); @@ -68,7 +92,6 @@ public EnumerableAssertionsChain SequenceEqual([InstantHandle] p var actualList = new List(); var expectedList = new List(); - var equalityComparer = EqualityComparer.Default; using var actualEnumerator = Value.GetEnumerator(); using var expectedEnumerator = expected.GetEnumerator(); @@ -94,7 +117,7 @@ public EnumerableAssertionsChain SequenceEqual([InstantHandle] p $"Value {Format.Enumerable(Value)} should sequence equal {Format.Collection(expectedList)} but it has more elements than the expected {expectedList.Count}."); } - if (!equalityComparer.Equals(actualEnumerator.Current, expectedEnumerator.Current)) + if (!equals(actualEnumerator.Current, expectedEnumerator.Current)) { var index = actualList.Count - 1; throw Verify.CreateException( @@ -129,6 +152,63 @@ public EnumerableAssertionsChain NotSequenceEqual(params IEnumer return new EnumerableAssertionsChain(this); } + /// + /// Asserts that the enumerable is not sequence equal to the expected elements using the specified equality comparer. + /// + /// The elements the enumerable should not be sequence equal to. + /// The equality comparer to use for element comparison. + /// An for chaining further assertions. + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + public EnumerableAssertionsChain NotSequenceEqual(IEnumerable expected, IEqualityComparer comparer) + { + NotBeNull(); + + if (Value.SequenceEqual(expected, comparer)) + { + throw Verify.CreateException($"Value should not sequence equal {Format.Enumerable(expected)}."); + } + + return new EnumerableAssertionsChain(this); + } + + /// + /// Asserts that the enumerable is not sequence equal to the expected elements using the specified predicate. + /// + /// The elements the enumerable should not be sequence equal to. + /// The predicate to use for element comparison. + /// An for chaining further assertions. + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + public EnumerableAssertionsChain NotSequenceEqual(IEnumerable expected, Func predicate) + { + NotBeNull(); + + using var actualEnumerator = Value.GetEnumerator(); + using var expectedEnumerator = expected.GetEnumerator(); + + while (true) + { + var actualHasNext = actualEnumerator.MoveNext(); + var expectedHasNext = expectedEnumerator.MoveNext(); + + if (!actualHasNext && !expectedHasNext) + { + break; + } + + if (!actualHasNext || !expectedHasNext) + { + return new EnumerableAssertionsChain(this); + } + + if (!predicate(actualEnumerator.Current, expectedEnumerator.Current)) + { + return new EnumerableAssertionsChain(this); + } + } + + throw Verify.CreateException($"Value should not sequence equal {Format.Enumerable(expected)}."); + } + /// /// Asserts that the enumerable contains the specified item. /// diff --git a/src/MrKWatkins.Assertions/ObjectAssertions.cs b/src/MrKWatkins.Assertions/ObjectAssertions.cs index a9bb3f1..a19948f 100644 --- a/src/MrKWatkins.Assertions/ObjectAssertions.cs +++ b/src/MrKWatkins.Assertions/ObjectAssertions.cs @@ -53,6 +53,46 @@ public ObjectAssertionsChain Equal(T? expected) return new ObjectAssertionsChain(this); } + /// + /// Asserts that the value is equal to the expected value using the specified equality comparer. + /// + /// The expected value. + /// The equality comparer to use for the comparison. + /// An for chaining further assertions. + public ObjectAssertionsChain Equal(T? expected, IEqualityComparer comparer) + { + if (Value is null) + { + Verify.That(expected is null, $"Value should equal {expected} but was null."); + } + else + { + Verify.That(comparer.Equals(Value, expected), $"Value should equal {expected} but was {Value}."); + } + + return new ObjectAssertionsChain(this); + } + + /// + /// Asserts that the value is equal to the expected value using the specified predicate. + /// + /// The expected value. + /// The predicate to use for the comparison. + /// An for chaining further assertions. + public ObjectAssertionsChain Equal(T? expected, Func predicate) + { + if (Value is null) + { + Verify.That(predicate(Value, expected), $"Value should equal {expected} but was null."); + } + else + { + Verify.That(predicate(Value, expected), $"Value should equal {expected} but was {Value}."); + } + + return new ObjectAssertionsChain(this); + } + /// /// Asserts that the value is not equal to the expected value using the default equality comparer. /// @@ -72,6 +112,39 @@ public ObjectAssertionsChain NotEqual(T? expected) return new ObjectAssertionsChain(this); } + /// + /// Asserts that the value is not equal to the expected value using the specified equality comparer. + /// + /// The value that is not expected. + /// The equality comparer to use for the comparison. + /// An for chaining further assertions. + public ObjectAssertionsChain NotEqual(T? expected, IEqualityComparer comparer) + { + if (Value is null) + { + Verify.That(expected is not null, "Value should not equal null."); + } + else + { + Verify.That(!comparer.Equals(Value, expected), $"Value should not equal {expected}."); + } + + return new ObjectAssertionsChain(this); + } + + /// + /// Asserts that the value is not equal to the expected value using the specified predicate. + /// + /// The value that is not expected. + /// The predicate to use for the comparison. + /// An for chaining further assertions. + public ObjectAssertionsChain NotEqual(T? expected, Func predicate) + { + Verify.That(!predicate(Value, expected), $"Value should not equal {expected}."); + + return new ObjectAssertionsChain(this); + } + /// /// Asserts that the value is the same instance as the expected value. /// diff --git a/src/MrKWatkins.Assertions/ReadOnlySpanAssertions.cs b/src/MrKWatkins.Assertions/ReadOnlySpanAssertions.cs index 0562f99..ce33c33 100644 --- a/src/MrKWatkins.Assertions/ReadOnlySpanAssertions.cs +++ b/src/MrKWatkins.Assertions/ReadOnlySpanAssertions.cs @@ -63,6 +63,62 @@ public ReadOnlySpanAssertionsChain SequenceEqual(params ReadOnlySpan expec return new ReadOnlySpanAssertionsChain(this); } + /// + /// Asserts that the span is sequence equal to the expected elements using the specified equality comparer. + /// + /// The expected elements. + /// The equality comparer to use for element comparison. + /// A for chaining further assertions. + public ReadOnlySpanAssertionsChain SequenceEqual(ReadOnlySpan expected, IEqualityComparer comparer) + { + if (Value.Length != expected.Length) + { + throw Verify.CreateException( + $"Value {Format.Value(Value)} should sequence equal {Format.Value(expected)} but it has {Value.Length} element{(Value.Length == 1 ? "" : "s")} rather than the expected {expected.Length}."); + } + + for (var f = 0; f < Value.Length; f++) + { + var actualItem = Value[f]; + var expectedItem = expected[f]; + if (!comparer.Equals(actualItem, expectedItem)) + { + throw Verify.CreateException( + $"Value {Format.Value(Value, f)} should sequence equal {Format.Value(expected, f)} but it differs at index {f}."); + } + } + + return new ReadOnlySpanAssertionsChain(this); + } + + /// + /// Asserts that the span is sequence equal to the expected elements using the specified predicate. + /// + /// The expected elements. + /// The predicate to use for element comparison. + /// A for chaining further assertions. + public ReadOnlySpanAssertionsChain SequenceEqual(ReadOnlySpan expected, Func predicate) + { + if (Value.Length != expected.Length) + { + throw Verify.CreateException( + $"Value {Format.Value(Value)} should sequence equal {Format.Value(expected)} but it has {Value.Length} element{(Value.Length == 1 ? "" : "s")} rather than the expected {expected.Length}."); + } + + for (var f = 0; f < Value.Length; f++) + { + var actualItem = Value[f]; + var expectedItem = expected[f]; + if (!predicate(actualItem, expectedItem)) + { + throw Verify.CreateException( + $"Value {Format.Value(Value, f)} should sequence equal {Format.Value(expected, f)} but it differs at index {f}."); + } + } + + return new ReadOnlySpanAssertionsChain(this); + } + /// /// Asserts that the span is not sequence equal to the expected elements. /// @@ -78,14 +134,83 @@ public ReadOnlySpanAssertionsChain NotSequenceEqual(params ReadOnlySpan ex return new ReadOnlySpanAssertionsChain(this); } + /// + /// Asserts that the span is not sequence equal to the expected elements using the specified equality comparer. + /// + /// The elements the span should not be sequence equal to. + /// The equality comparer to use for element comparison. + /// A for chaining further assertions. + public ReadOnlySpanAssertionsChain NotSequenceEqual(ReadOnlySpan expected, IEqualityComparer comparer) + { + if (Value.Length != expected.Length) + { + return new ReadOnlySpanAssertionsChain(this); + } + + for (var f = 0; f < Value.Length; f++) + { + if (!comparer.Equals(Value[f], expected[f])) + { + return new ReadOnlySpanAssertionsChain(this); + } + } + + throw Verify.CreateException($"Value should not sequence equal {Format.Value(expected)}."); + } + + /// + /// Asserts that the span is not sequence equal to the expected elements using the specified predicate. + /// + /// The elements the span should not be sequence equal to. + /// The predicate to use for element comparison. + /// A for chaining further assertions. + public ReadOnlySpanAssertionsChain NotSequenceEqual(ReadOnlySpan expected, Func predicate) + { + if (Value.Length != expected.Length) + { + return new ReadOnlySpanAssertionsChain(this); + } + + for (var f = 0; f < Value.Length; f++) + { + if (!predicate(Value[f], expected[f])) + { + return new ReadOnlySpanAssertionsChain(this); + } + } + + throw Verify.CreateException($"Value should not sequence equal {Format.Value(expected)}."); + } + /// /// Asserts that the span is sequence equal to the expected enumerable elements. /// /// The expected elements. /// A for chaining further assertions. - public ReadOnlySpanAssertionsChain SequenceEqual([InstantHandle] IEnumerable expected) + public ReadOnlySpanAssertionsChain SequenceEqual([InstantHandle] IEnumerable expected) => + SequenceEqualEnumerableCore(expected, EqualityComparer.Default.Equals); + + /// + /// Asserts that the span is sequence equal to the expected enumerable elements using the specified equality comparer. + /// + /// The expected elements. + /// The equality comparer to use for element comparison. + /// A for chaining further assertions. + public ReadOnlySpanAssertionsChain SequenceEqual([InstantHandle] IEnumerable expected, IEqualityComparer comparer) => + SequenceEqualEnumerableCore(expected, comparer.Equals); + + /// + /// Asserts that the span is sequence equal to the expected enumerable elements using the specified predicate. + /// + /// The expected elements. + /// The predicate to use for element comparison. + /// A for chaining further assertions. + public ReadOnlySpanAssertionsChain SequenceEqual([InstantHandle] IEnumerable expected, Func predicate) => + SequenceEqualEnumerableCore(expected, predicate); + + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + private ReadOnlySpanAssertionsChain SequenceEqualEnumerableCore(IEnumerable expected, Func equals) { - var equalityComparer = EqualityComparer.Default; if (expected.TryGetCount(out var expectedCount) && Value.Length != expectedCount) { throw Verify.CreateException( @@ -115,7 +240,7 @@ public ReadOnlySpanAssertionsChain SequenceEqual([InstantHandle] IEnumerable< $"Value {Format.Value(Value)} should sequence equal {Format.Enumerable(expected)} but it has more elements than the expected {expectedList.Count}."); } - if (!equalityComparer.Equals(actualEnumerator.Current, expectedEnumerator.Current)) + if (!equals(actualEnumerator.Current, expectedEnumerator.Current)) { throw Verify.CreateException( $"Value {Format.Value(Value, index)} should sequence equal {Format.Collection(expectedList, index, true)} but it differs at index {index}."); @@ -138,9 +263,30 @@ public ReadOnlySpanAssertionsChain SequenceEqual([InstantHandle] IEnumerable< /// /// The elements the span should not be sequence equal to. /// A for chaining further assertions. - public ReadOnlySpanAssertionsChain NotSequenceEqual([InstantHandle] IEnumerable expected) + public ReadOnlySpanAssertionsChain NotSequenceEqual([InstantHandle] IEnumerable expected) => + NotSequenceEqualEnumerableCore(expected, EqualityComparer.Default.Equals); + + /// + /// Asserts that the span is not sequence equal to the expected enumerable elements using the specified equality comparer. + /// + /// The elements the span should not be sequence equal to. + /// The equality comparer to use for element comparison. + /// A for chaining further assertions. + public ReadOnlySpanAssertionsChain NotSequenceEqual([InstantHandle] IEnumerable expected, IEqualityComparer comparer) => + NotSequenceEqualEnumerableCore(expected, comparer.Equals); + + /// + /// Asserts that the span is not sequence equal to the expected enumerable elements using the specified predicate. + /// + /// The elements the span should not be sequence equal to. + /// The predicate to use for element comparison. + /// A for chaining further assertions. + public ReadOnlySpanAssertionsChain NotSequenceEqual([InstantHandle] IEnumerable expected, Func predicate) => + NotSequenceEqualEnumerableCore(expected, predicate); + + [SuppressMessage("ReSharper", "PossibleMultipleEnumeration")] + private ReadOnlySpanAssertionsChain NotSequenceEqualEnumerableCore(IEnumerable expected, Func equals) { - var equalityComparer = EqualityComparer.Default; if (expected.TryGetCount(out var expectedCount) && Value.Length != expectedCount) { return new ReadOnlySpanAssertionsChain(this); @@ -170,7 +316,7 @@ public ReadOnlySpanAssertionsChain NotSequenceEqual([InstantHandle] IEnumerab return new ReadOnlySpanAssertionsChain(this); } - if (!equalityComparer.Equals(actualEnumerator.Current, expectedEnumerator.Current)) + if (!equals(actualEnumerator.Current, expectedEnumerator.Current)) { // Items not equal, not sequence equal. return new ReadOnlySpanAssertionsChain(this);