Skip to content

Commit e1484b4

Browse files
authored
Merge pull request #115 from feO2x/issue-114-must-not-be-default-or-empty-for-immutable-array
Issue 114 must not be default or empty for immutable array
2 parents 62a273b + f404273 commit e1484b4

File tree

5 files changed

+174
-4
lines changed

5 files changed

+174
-4
lines changed

Code/Light.GuardClauses.AllProjects.sln

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Versioning", "Versioning",
3030
EndProject
3131
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Light.GuardClauses.SourceCodeTransformation.Tests", "Light.GuardClauses.SourceCodeTransformation.Tests\Light.GuardClauses.SourceCodeTransformation.Tests.csproj", "{A2067796-F167-47BE-B367-191152DCE230}"
3232
EndProject
33+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plans", "Plans", "{664661A0-3861-486B-87B5-A35A5CC5F7B7}"
34+
ProjectSection(SolutionItems) = preProject
35+
Plans\issue-114-must-not-be-default-or-empty-for-immutable-array.md = Plans\issue-114-must-not-be-default-or-empty-for-immutable-array.md
36+
EndProjectSection
37+
EndProject
3338
Global
3439
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3540
Debug|Any CPU = Debug|Any CPU
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using System;
2+
using System.Collections.Immutable;
3+
using FluentAssertions;
4+
using Light.GuardClauses.Exceptions;
5+
using Xunit;
6+
7+
namespace Light.GuardClauses.Tests.CollectionAssertions;
8+
9+
public static class MustNotBeDefaultOrEmptyTests
10+
{
11+
[Fact]
12+
public static void ImmutableArrayDefault()
13+
{
14+
var defaultArray = default(ImmutableArray<int>);
15+
16+
Action act = () => defaultArray.MustNotBeDefaultOrEmpty(nameof(defaultArray));
17+
18+
var assertion = act.Should().Throw<EmptyCollectionException>().Which;
19+
assertion.Message.Should()
20+
.Contain($"{nameof(defaultArray)} must not be an empty collection, but it actually is.");
21+
assertion.ParamName.Should().BeSameAs(nameof(defaultArray));
22+
}
23+
24+
[Fact]
25+
public static void ImmutableArrayEmpty()
26+
{
27+
var emptyArray = ImmutableArray<string>.Empty;
28+
29+
Action act = () => emptyArray.MustNotBeDefaultOrEmpty(nameof(emptyArray));
30+
31+
var assertion = act.Should().Throw<EmptyCollectionException>().Which;
32+
assertion.Message.Should()
33+
.Contain($"{nameof(emptyArray)} must not be an empty collection, but it actually is.");
34+
assertion.ParamName.Should().BeSameAs(nameof(emptyArray));
35+
}
36+
37+
[Fact]
38+
public static void ImmutableArrayNotEmpty()
39+
{
40+
var array = ImmutableArray.Create("Foo", "Bar", "Baz");
41+
42+
array.MustNotBeDefaultOrEmpty().Should().Equal(array);
43+
}
44+
45+
[Theory]
46+
[MemberData(nameof(DefaultOrEmptyArrays))]
47+
public static void CustomException(ImmutableArray<int> array) =>
48+
Test.CustomException(
49+
array,
50+
(invalidArray, exceptionFactory) => invalidArray.MustNotBeDefaultOrEmpty(exceptionFactory)
51+
);
52+
53+
[Fact]
54+
public static void NoCustomExceptionThrown()
55+
{
56+
var array = ImmutableArray.Create(42, 84);
57+
array.MustNotBeDefaultOrEmpty(_ => new ()).Should().Equal(array);
58+
}
59+
60+
[Fact]
61+
public static void CustomMessage() =>
62+
Test.CustomMessage<EmptyCollectionException>(
63+
message => ImmutableArray<string>.Empty.MustNotBeDefaultOrEmpty(message: message)
64+
);
65+
66+
[Fact]
67+
public static void CallerArgumentExpressionForEmptyArray()
68+
{
69+
var emptyArray = ImmutableArray<int>.Empty;
70+
71+
Action act = () => emptyArray.MustNotBeDefaultOrEmpty();
72+
73+
act.Should().Throw<EmptyCollectionException>()
74+
.WithParameterName(nameof(emptyArray));
75+
}
76+
77+
[Fact]
78+
public static void CallerArgumentExpressionForDefaultArray()
79+
{
80+
var defaultArray = default(ImmutableArray<int>);
81+
82+
Action act = () => defaultArray.MustNotBeDefaultOrEmpty();
83+
84+
act.Should().Throw<EmptyCollectionException>()
85+
.WithParameterName(nameof(defaultArray));
86+
}
87+
88+
public static TheoryData<ImmutableArray<int>> DefaultOrEmptyArrays() => new ()
89+
{
90+
default,
91+
ImmutableArray<int>.Empty,
92+
};
93+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System;
2+
using System.Collections.Immutable;
3+
using System.Runtime.CompilerServices;
4+
using JetBrains.Annotations;
5+
using Light.GuardClauses.ExceptionFactory;
6+
7+
namespace Light.GuardClauses;
8+
9+
public static partial class Check
10+
{
11+
/// <summary>
12+
/// Ensures that the specified <see cref="ImmutableArray{T}" /> is not default or empty, or otherwise throws an <see cref="Exceptions.EmptyCollectionException" />.
13+
/// </summary>
14+
/// <param name="parameter">The <see cref="ImmutableArray{T}" /> to be checked.</param>
15+
/// <param name="parameterName">The name of the parameter (optional).</param>
16+
/// <param name="message">The message that will be passed to the resulting exception (optional).</param>
17+
/// <exception cref="Exceptions.EmptyCollectionException">Thrown when <paramref name="parameter" /> is default or empty.</exception>
18+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
19+
public static ImmutableArray<T> MustNotBeDefaultOrEmpty<T>(
20+
this ImmutableArray<T> parameter,
21+
[CallerArgumentExpression("parameter")] string? parameterName = null,
22+
string? message = null
23+
)
24+
{
25+
if (parameter.IsDefaultOrEmpty)
26+
{
27+
Throw.EmptyCollection(parameterName, message);
28+
}
29+
30+
return parameter;
31+
}
32+
33+
/// <summary>
34+
/// Ensures that the specified <see cref="ImmutableArray{T}" /> is not default or empty, or otherwise throws your custom exception.
35+
/// </summary>
36+
/// <param name="parameter">The <see cref="ImmutableArray{T}" /> to be checked.</param>
37+
/// <param name="exceptionFactory">The delegate that creates your custom exception. The <see cref="ImmutableArray{T}" /> is passed to this delegate.</param>
38+
/// <exception cref="Exception">Your custom exception thrown when <paramref name="parameter" /> is default or empty.</exception>
39+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
40+
[ContractAnnotation("exceptionFactory:null => halt")]
41+
public static ImmutableArray<T> MustNotBeDefaultOrEmpty<T>(
42+
this ImmutableArray<T> parameter,
43+
Func<ImmutableArray<T>, Exception> exceptionFactory
44+
)
45+
{
46+
if (parameter.IsDefaultOrEmpty)
47+
{
48+
Throw.CustomException(exceptionFactory, parameter);
49+
}
50+
51+
return parameter;
52+
}
53+
}

Code/Light.GuardClauses/Light.GuardClauses.csproj

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<Import Project="..\Version.props" />
44

@@ -39,10 +39,11 @@ Light.GuardClauses 13.0.0
3939
</PropertyGroup>
4040

4141
<ItemGroup>
42-
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.0" Condition="'$(TargetFramework)' != 'net8.0'" />
43-
<PackageReference Include="System.Memory" Version="4.6.0" Condition="'$(TargetFramework)' == 'netstandard2.0'" />
44-
<PackageReference Include="Nullable" Version="1.3.1" PrivateAssets="all" />
4542
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="all" />
43+
<PackageReference Include="Nullable" Version="1.3.1" PrivateAssets="all" />
44+
<PackageReference Include="System.Collections.Immutable" Version="8.0.0" />
45+
<PackageReference Include="System.Memory" Version="4.6.0" Condition="'$(TargetFramework)' == 'netstandard2.0'" />
46+
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.1.0" Condition="'$(TargetFramework)' != 'net8.0'" />
4647
</ItemGroup>
4748

4849
<ItemGroup>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Issue 114 - MustNotBeDefaultOrEmpty for ImmutableArray
2+
3+
## Context
4+
5+
The .NET library Light.GuardClauses already has several assertions for collections. They often rely on `IEnumerable<T>` or `IEnumerable`. However, these assertions would result in `ImmutableArray<T>` being boxed - we want to avoid that by providing dedicated assertions for this type which avoids boxing. For this issue, we start out with a new assertion called `MustNotBeDefaultOrEmpty`.
6+
7+
## Tasks for this issue
8+
9+
- [ ] The production code should be placed in the Light.GuardClauses project. Create a file called `Check.MustNotBeDefaultOrEmpty.cs` in the root folder of the project.
10+
- [ ] In this file, create several extension method overloads called `MustNotBeDefaultOrEmpty` for `ImmutableArray<T>`. It should be placed in the class `Check` which is marked as `partial`.
11+
- [ ] Each assertion in Light.GuardClauses has two overloads - the first one takes the optional `parameterName` and `message` arguments and throw the default exception (in this case the existing `EmptyCollectionException`). The actual exception in thrown in the `Throw` class, you need to create a corresponding method for it in this class.
12+
- [ ] The other overload takes a delegate which allows the caller to provide their own custom exceptions. Pass the erroneous `ImmutableArray<T>` instance to the delegate and throw the returned exception.
13+
- [ ] Create unit tests for both overloads. The corresponding file should be placed in Light.GuardClauses.Tests project, in the existing subfolder 'CollectionAssertions'. Please follow conventions of the existing tests (e.g. use FluentAssertions' `Should()` for assertions).
14+
15+
## Notes
16+
17+
- 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.
18+
- If you have any questions or suggestions, please ask me about them.

0 commit comments

Comments
 (0)