Skip to content

Commit 6df7afc

Browse files
committed
✨ add enum flag matchers
Expect(value)
1 parent 334ddb6 commit 6df7afc

File tree

4 files changed

+236
-28
lines changed

4 files changed

+236
-28
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System;
2+
using NUnit.Framework;
3+
using NExpect;
4+
using NExpect.Exceptions;
5+
using static NExpect.Expectations;
6+
using static PeanutButter.RandomGenerators.RandomValueGen;
7+
8+
namespace NExpect.Tests
9+
{
10+
[TestFixture]
11+
public class TestEnumMatchers
12+
{
13+
[Test]
14+
public void ShouldBeAbleToAssertEnumValueHasFlag()
15+
{
16+
// Arrange
17+
var value = Numbers.One | Numbers.Three;
18+
// Act
19+
Assert.That(
20+
() =>
21+
{
22+
Expect(value)
23+
.To.Have.Flag(Numbers.One)
24+
.And
25+
.To.Have.Flag(Numbers.Three);
26+
Expect(value)
27+
.Not.To.Have.Flag(Numbers.Two)
28+
.And
29+
.Not.To.Have.Flag(Numbers.Four);
30+
},
31+
Throws.Nothing
32+
);
33+
34+
Assert.That(
35+
() =>
36+
{
37+
Expect(value)
38+
.To.Have.Flag(Numbers.Two);
39+
},
40+
Throws.Exception.InstanceOf<UnmetExpectationException>()
41+
.With.Message.Contains(
42+
$"Expected ({value}) to have flag ({Numbers.Two})"
43+
)
44+
);
45+
46+
Assert.That(
47+
() =>
48+
{
49+
Expect(value)
50+
.Not.To.Have.Flag(Numbers.One);
51+
},
52+
Throws.Exception.InstanceOf<UnmetExpectationException>()
53+
.With.Message.Contains(
54+
$"Expected ({value}) not to have flag ({Numbers.One})"
55+
)
56+
);
57+
58+
// Assert
59+
}
60+
61+
[Test]
62+
public void ShouldAlwaysFailForNonFlagsEnums()
63+
{
64+
// Arrange
65+
var value = NotFlags.Strange;
66+
// Act
67+
Assert.That(
68+
() =>
69+
{
70+
Expect(value)
71+
.To.Have.Flag(NotFlags.Strange);
72+
},
73+
Throws.Exception.InstanceOf<UnmetExpectationException>()
74+
.With.Message.Contains("not decorated with [Flags]")
75+
);
76+
Assert.That(
77+
() =>
78+
{
79+
Expect(value)
80+
.Not.To.Have.Flag(NotFlags.Strange);
81+
},
82+
Throws.Exception.InstanceOf<UnmetExpectationException>()
83+
.With.Message.Contains("not decorated with [Flags]")
84+
);
85+
// Assert
86+
}
87+
88+
public enum NotFlags
89+
{
90+
Unknown,
91+
Up,
92+
Down,
93+
Strange,
94+
Charm,
95+
Bottom,
96+
Top
97+
}
98+
99+
[Flags]
100+
public enum Numbers
101+
{
102+
Zero = 0,
103+
One = 1,
104+
Two = 2,
105+
Three = 4,
106+
Four = 8
107+
}
108+
}
109+
}

src/NExpect/EnumMatchers.cs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
using System;
2+
using Imported.PeanutButter.Utils;
3+
using NExpect.Interfaces;
4+
using NExpect.MatcherLogic;
5+
using static NExpect.Implementations.MessageHelpers;
6+
7+
namespace NExpect;
8+
9+
/// <summary>
10+
/// Provides matchers for enum values
11+
/// </summary>
12+
public static class EnumMatchers
13+
{
14+
/// <summary>
15+
/// Verifies that the [Flag]-decorated enum
16+
/// has the provided flag
17+
/// </summary>
18+
/// <param name="have"></param>
19+
/// <param name="flag">The flag being tested for</param>
20+
/// <typeparam name="T"></typeparam>
21+
/// <returns></returns>
22+
public static IMore<T> Flag<T>(
23+
this IHave<T> have,
24+
T flag
25+
) where T : struct
26+
{
27+
return have.Flag(
28+
flag,
29+
NULL_STRING
30+
);
31+
}
32+
33+
/// <summary>
34+
/// Verifies that the [Flag]-decorated enum
35+
/// has the provided flag
36+
/// </summary>
37+
/// <param name="have"></param>
38+
/// <param name="flag">The flag being tested for</param>
39+
/// <param name="customMessage">A custom error message</param>
40+
/// <typeparam name="T"></typeparam>
41+
/// <returns></returns>
42+
public static IMore<T> Flag<T>(
43+
this IHave<T> have,
44+
T flag,
45+
string customMessage
46+
) where T : struct
47+
{
48+
return have.Flag(
49+
flag,
50+
() => customMessage
51+
);
52+
}
53+
54+
/// <summary>
55+
/// Verifies that the [Flag]-decorated enum
56+
/// has the provided flag
57+
/// </summary>
58+
/// <param name="have"></param>
59+
/// <param name="flag">The flag being tested for</param>
60+
/// <param name="customMessageGenerator">Generates a custom error message</param>
61+
/// <typeparam name="T"></typeparam>
62+
/// <returns></returns>
63+
public static IMore<T> Flag<T>(
64+
this IHave<T> have,
65+
T flag,
66+
Func<string> customMessageGenerator
67+
) where T : struct
68+
{
69+
return have.AddMatcher(
70+
actual =>
71+
{
72+
if (!typeof(T).HasAttribute<FlagsAttribute>())
73+
{
74+
return new EnforcedMatcherResult(
75+
false,
76+
FinalMessageFor(
77+
() =>
78+
$"{typeof(T)} is not decorated with [Flags]. Are you sure you should be using it like a Flags type?",
79+
customMessageGenerator
80+
)
81+
);
82+
}
83+
84+
var passed = actual.HasFlag(flag);
85+
86+
return new MatcherResult(
87+
passed,
88+
FinalMessageFor(
89+
() => $"Expected ({actual}) {passed.AsNot()}to have flag ({flag})",
90+
customMessageGenerator
91+
)
92+
);
93+
}
94+
);
95+
}
96+
}

src/NExpect/NExpect.csproj

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,43 +36,46 @@
3636
<DefineConstants>BUILD_PEANUTBUTTER_INTERNAL</DefineConstants>
3737
</PropertyGroup>
3838
<ItemGroup>
39-
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/>
39+
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
4040
</ItemGroup>
4141
<ItemGroup>
42-
<None Include="..\..\README.md" Pack="true" PackagePath="\"/>
42+
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
4343
</ItemGroup>
4444
<ItemGroup>
45-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\ArrayExtensions.cs" Link="Imported\ArrayExtensions.cs"/>
46-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\AutoLocker.cs" Link="Imported\AutoLocker.cs"/>
47-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\AutoResetter.cs" Link="Imported\AutoResetter.cs"/>
45+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\ArrayExtensions.cs" Link="Imported\ArrayExtensions.cs" />
46+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\AutoLocker.cs" Link="Imported\AutoLocker.cs" />
47+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\AutoResetter.cs" Link="Imported\AutoResetter.cs" />
4848
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\AutoResetterOfT.cs">
4949
<Link>Imported\AutoResetterOfT.cs</Link>
5050
</Compile>
51-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\ByteArrayExtensions.cs" Link="Imported\ByteArrayExtensions.cs"/>
52-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\DeepEqualityTester.cs" Link="Imported\DeepEqualityTester.cs"/>
53-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\DictionaryExtensions.cs" Link="Imported\DictionaryExtensions.cs"/>
54-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\EnumerableWrapper.cs" Link="Imported\EnumerableWrapper.cs"/>
55-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\ObjectExtensions.cs" Link="Imported\ObjectExtensions.cs"/>
56-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\ExtensionsForIEnumerables.cs" Link="Imported\ExtensionsForIEnumerables.cs"/>
57-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\MetadataExtensions.cs" Link="Imported\MetadataExtensions.cs"/>
58-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\MemberNotFoundException.cs" Link="Imported\MemberNotFoundException.cs"/>
59-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\Platform.cs" Link="Imported\Platform.cs"/>
60-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\PathType.cs" Link="Imported\PathType.cs"/>
61-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\PropertyOrField.cs" Link="Imported\PropertyOrField.cs"/>
62-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\PyLike.cs" Link="Imported\PyLike.cs"/>
63-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\StringExtensions.cs" Link="Imported\StringExtensions.cs"/>
64-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\RandomNumber.cs" Link="Imported\RandomNumber.cs"/>
65-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\Stringifier.cs" Link="Imported\Stringifier.cs"/>
66-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\TypeExtensions.cs" Link="Imported\TypeExtensions.cs"/>
67-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\CannotZipNullException.cs" Link="Imported\CannotZipNullException.cs"/>
68-
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\UnevenZipException.cs" Link="Imported\UnevenZipException.cs"/>
51+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\ByteArrayExtensions.cs" Link="Imported\ByteArrayExtensions.cs" />
52+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\DeepEqualityTester.cs" Link="Imported\DeepEqualityTester.cs" />
53+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\DictionaryExtensions.cs" Link="Imported\DictionaryExtensions.cs" />
54+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\EnumerableWrapper.cs" Link="Imported\EnumerableWrapper.cs" />
55+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\FlagExtensions.cs">
56+
<Link>Imported\FlagExtensions.cs</Link>
57+
</Compile>
58+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\ObjectExtensions.cs" Link="Imported\ObjectExtensions.cs" />
59+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\ExtensionsForIEnumerables.cs" Link="Imported\ExtensionsForIEnumerables.cs" />
60+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\MetadataExtensions.cs" Link="Imported\MetadataExtensions.cs" />
61+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\MemberNotFoundException.cs" Link="Imported\MemberNotFoundException.cs" />
62+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\Platform.cs" Link="Imported\Platform.cs" />
63+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\PathType.cs" Link="Imported\PathType.cs" />
64+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\PropertyOrField.cs" Link="Imported\PropertyOrField.cs" />
65+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\PyLike.cs" Link="Imported\PyLike.cs" />
66+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\StringExtensions.cs" Link="Imported\StringExtensions.cs" />
67+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\RandomNumber.cs" Link="Imported\RandomNumber.cs" />
68+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\Stringifier.cs" Link="Imported\Stringifier.cs" />
69+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\TypeExtensions.cs" Link="Imported\TypeExtensions.cs" />
70+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\CannotZipNullException.cs" Link="Imported\CannotZipNullException.cs" />
71+
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\UnevenZipException.cs" Link="Imported\UnevenZipException.cs" />
6972
</ItemGroup>
7073
<ItemGroup Condition="'$(TargetFramework)'=='netstandard1.6'">
71-
<Reference Include="System.Diagnostics.StackTrace"/>
72-
<Reference Include="System.Runtime.Serialization"/>
74+
<Reference Include="System.Diagnostics.StackTrace" />
75+
<Reference Include="System.Runtime.Serialization" />
7376
</ItemGroup>
7477
<ItemGroup>
75-
<None Include="icon.png" Pack="true" PackagePath=""/>
78+
<None Include="icon.png" Pack="true" PackagePath="" />
7679
</ItemGroup>
77-
<Import Project="..\MonoForFramework.targets"/>
80+
<Import Project="..\MonoForFramework.targets" />
7881
</Project>

src/PeanutButter

Submodule PeanutButter updated 46 files

0 commit comments

Comments
 (0)