diff --git a/Code/Light.GuardClauses.Tests/CollectionAssertions/MustContainTests.cs b/Code/Light.GuardClauses.Tests/CollectionAssertions/MustContainTests.cs index c2fe867..e450f76 100644 --- a/Code/Light.GuardClauses.Tests/CollectionAssertions/MustContainTests.cs +++ b/Code/Light.GuardClauses.Tests/CollectionAssertions/MustContainTests.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Collections.ObjectModel; using FluentAssertions; using Light.GuardClauses.Exceptions; @@ -69,4 +70,72 @@ public static void CallerArgumentExpression() act.Should().Throw() .WithParameterName(nameof(array)); } + + [Theory] + [InlineData(new[] { 1, 2, 3 }, 5)] + [InlineData(new[] { -5491, 6199 }, 42)] + public static void ImmutableArrayItemNotPartOf(int[] source, int item) + { + var immutableArray = source.ToImmutableArray(); + Action act = () => immutableArray.MustContain(item, nameof(immutableArray)); + + var assertion = act.Should().Throw().Which; + assertion.Message.Should().Contain($"{nameof(immutableArray)} must contain {item}, but it actually does not."); + } + + [Theory] + [InlineData(new[] { "Foo", "Bar" }, "Foo")] + [InlineData(new[] { "Foo", "Bar", "Foo" }, "Foo")] + [InlineData(new[] { "Qux" }, "Qux")] + [InlineData(new[] { "Qux", null }, null)] + public static void ImmutableArrayItemPartOf(string[] source, string item) + { + var immutableArray = source.ToImmutableArray(); + immutableArray.MustContain(item).Should().Equal(immutableArray); + } + + [Fact] + public static void ImmutableArrayEmptyDoesNotContainItem() + { + var immutableArray = ImmutableArray.Empty; + Action act = () => immutableArray.MustContain("Foo"); + + act.Should().Throw(); + } + + [Theory] + [InlineData(new[] { 42L, 100L }, 1337L)] + public static void ImmutableArrayCustomException(long[] source, long item) + { + var immutableArray = source.ToImmutableArray(); + Test.CustomException( + immutableArray, + item, + (array, i, exceptionFactory) => array.MustContain(i, exceptionFactory) + ); + } + + [Fact] + public static void ImmutableArrayCustomExceptionNotThrown() + { + var immutableArray = ImmutableArray.Create(1, 2, 3); + immutableArray.MustContain(2, (_, _) => new Exception()).Should().Equal(immutableArray); + } + + [Fact] + public static void ImmutableArrayCustomMessage() => + Test.CustomMessage( + message => ImmutableArray.Empty.MustContain("Foo", message: message) + ); + + [Fact] + public static void ImmutableArrayCallerArgumentExpression() + { + var immutableArray = ImmutableArray.Create("Foo", "Bar"); + + var act = () => immutableArray.MustContain("Baz"); + + act.Should().Throw() + .WithParameterName(nameof(immutableArray)); + } } \ No newline at end of file diff --git a/Code/Light.GuardClauses/Check.MustContain.cs b/Code/Light.GuardClauses/Check.MustContain.cs index 7158a37..be3ec1a 100644 --- a/Code/Light.GuardClauses/Check.MustContain.cs +++ b/Code/Light.GuardClauses/Check.MustContain.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Runtime.CompilerServices; using JetBrains.Annotations; @@ -197,4 +198,50 @@ public static string MustContain( return parameter; } + + /// + /// Ensures that the immutable array contains the specified item, or otherwise throws a . + /// + /// The immutable array to be checked. + /// The item that must be part of the immutable array. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not contain . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ImmutableArray MustContain( + this ImmutableArray parameter, + T item, + [CallerArgumentExpression("parameter")] string? parameterName = null, + string? message = null + ) + { + if (!parameter.Contains(item)) + { + Throw.MissingItem(parameter, item, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the immutable array contains the specified item, or otherwise throws your custom exception. + /// + /// The immutable array to be checked. + /// The item that must be part of the immutable array. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not contain . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ImmutableArray MustContain( + this ImmutableArray parameter, + T item, + Func, T, Exception> exceptionFactory + ) + { + if (!parameter.Contains(item)) + { + Throw.CustomException(exceptionFactory, parameter, item); + } + + return parameter; + } } diff --git a/Code/Plans/issue-119-must-contain-for-immutable-array.md b/Code/Plans/issue-119-must-contain-for-immutable-array.md new file mode 100644 index 0000000..b3c085f --- /dev/null +++ b/Code/Plans/issue-119-must-contain-for-immutable-array.md @@ -0,0 +1,20 @@ +# Issue 119 - MustContain for ImmutableArray + +## Context + +The .NET library Light.GuardClauses already has several assertions for collections. They often rely on `IEnumerable` or `IEnumerable`. However, these assertions would result in `ImmutableArray` being boxed - we want to avoid that by providing dedicated assertions for this type which avoids boxing. For this issue, we implement the `MustContain` assertion for `ImmutableArray`. + +## Tasks for this issue + +- [ ] The production code should be placed in the Light.GuardClauses project. Extend the existing `Check.MustContain.cs` in the root folder of the project. +- [ ] In this file, add several extension method overloads called `MustContain` for `ImmutableArray`. They should be placed in the existing class `Check` which is marked as `partial`. +- [ ] Each assertion in Light.GuardClauses has two overloads - the first one takes the optional `parameterName` and `message` arguments and throws the default exception (in this case the existing `MissingItemException`). The actual exception is thrown in the `Throw` class, you need to call the existing method for it in this class. +- [ ] The other overload takes a delegate which allows the caller to provide their own custom exceptions. Use the existing `Throw.CustomException` method and pass the delegate, the erroneous `ImmutableArray` instance and the missing item. +- [ ] Create unit tests for both overloads. The corresponding tests should be placed in Light.GuardClauses.Tests project, in the existing file 'CollectionAssertions/MustContainTests.cs'. Please follow conventions of the existing tests (e.g. use FluentAssertions' `Should()` for assertions). + +## Notes + +- There are already plenty of other assertions and tests in this library. All overloads are placed in the same file in the production code project. The test projects has top-level folders for different groups of assertions, like `CollectionAssertions`, `StringAssertions`, `DateTimeAssertions` and so on. Please take a look at them to follow a similar structure and code style. +- This assertion specifically targets `ImmutableArray` to avoid boxing that would occur with generic `IEnumerable` extensions. +- The assertion should verify that the `ImmutableArray` contains the specified item. +- If you have any questions or suggestions, please ask me about them.