Skip to content

Commit

Permalink
✨ add enum flag matchers
Browse files Browse the repository at this point in the history
Expect(value)
  • Loading branch information
fluffynuts committed May 18, 2023
1 parent 334ddb6 commit 6df7afc
Show file tree
Hide file tree
Showing 4 changed files with 236 additions and 28 deletions.
109 changes: 109 additions & 0 deletions src/NExpect.Tests/TestEnumMatchers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
using System;
using NUnit.Framework;
using NExpect;
using NExpect.Exceptions;
using static NExpect.Expectations;
using static PeanutButter.RandomGenerators.RandomValueGen;

namespace NExpect.Tests
{
[TestFixture]
public class TestEnumMatchers
{
[Test]
public void ShouldBeAbleToAssertEnumValueHasFlag()
{
// Arrange
var value = Numbers.One | Numbers.Three;
// Act
Assert.That(
() =>
{
Expect(value)
.To.Have.Flag(Numbers.One)
.And
.To.Have.Flag(Numbers.Three);
Expect(value)
.Not.To.Have.Flag(Numbers.Two)
.And
.Not.To.Have.Flag(Numbers.Four);
},
Throws.Nothing
);

Assert.That(
() =>
{
Expect(value)
.To.Have.Flag(Numbers.Two);
},
Throws.Exception.InstanceOf<UnmetExpectationException>()
.With.Message.Contains(
$"Expected ({value}) to have flag ({Numbers.Two})"
)
);

Assert.That(
() =>
{
Expect(value)
.Not.To.Have.Flag(Numbers.One);
},
Throws.Exception.InstanceOf<UnmetExpectationException>()
.With.Message.Contains(
$"Expected ({value}) not to have flag ({Numbers.One})"
)
);

// Assert
}

[Test]
public void ShouldAlwaysFailForNonFlagsEnums()
{
// Arrange
var value = NotFlags.Strange;
// Act
Assert.That(
() =>
{
Expect(value)
.To.Have.Flag(NotFlags.Strange);
},
Throws.Exception.InstanceOf<UnmetExpectationException>()
.With.Message.Contains("not decorated with [Flags]")
);
Assert.That(
() =>
{
Expect(value)
.Not.To.Have.Flag(NotFlags.Strange);
},
Throws.Exception.InstanceOf<UnmetExpectationException>()
.With.Message.Contains("not decorated with [Flags]")
);
// Assert
}

public enum NotFlags
{
Unknown,
Up,
Down,
Strange,
Charm,
Bottom,
Top
}

[Flags]
public enum Numbers
{
Zero = 0,
One = 1,
Two = 2,
Three = 4,
Four = 8
}
}
}
96 changes: 96 additions & 0 deletions src/NExpect/EnumMatchers.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using System;
using Imported.PeanutButter.Utils;
using NExpect.Interfaces;
using NExpect.MatcherLogic;
using static NExpect.Implementations.MessageHelpers;

namespace NExpect;

/// <summary>
/// Provides matchers for enum values
/// </summary>
public static class EnumMatchers
{
/// <summary>
/// Verifies that the [Flag]-decorated enum
/// has the provided flag
/// </summary>
/// <param name="have"></param>
/// <param name="flag">The flag being tested for</param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IMore<T> Flag<T>(
this IHave<T> have,
T flag
) where T : struct
{
return have.Flag(
flag,
NULL_STRING
);
}

/// <summary>
/// Verifies that the [Flag]-decorated enum
/// has the provided flag
/// </summary>
/// <param name="have"></param>
/// <param name="flag">The flag being tested for</param>
/// <param name="customMessage">A custom error message</param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IMore<T> Flag<T>(
this IHave<T> have,
T flag,
string customMessage
) where T : struct
{
return have.Flag(
flag,
() => customMessage
);
}

/// <summary>
/// Verifies that the [Flag]-decorated enum
/// has the provided flag
/// </summary>
/// <param name="have"></param>
/// <param name="flag">The flag being tested for</param>
/// <param name="customMessageGenerator">Generates a custom error message</param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IMore<T> Flag<T>(
this IHave<T> have,
T flag,
Func<string> customMessageGenerator
) where T : struct
{
return have.AddMatcher(
actual =>
{
if (!typeof(T).HasAttribute<FlagsAttribute>())
{
return new EnforcedMatcherResult(
false,
FinalMessageFor(
() =>
$"{typeof(T)} is not decorated with [Flags]. Are you sure you should be using it like a Flags type?",
customMessageGenerator
)
);
}

var passed = actual.HasFlag(flag);

return new MatcherResult(
passed,
FinalMessageFor(
() => $"Expected ({actual}) {passed.AsNot()}to have flag ({flag})",
customMessageGenerator
)
);
}
);
}
}
57 changes: 30 additions & 27 deletions src/NExpect/NExpect.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -36,43 +36,46 @@
<DefineConstants>BUILD_PEANUTBUTTER_INTERNAL</DefineConstants>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\README.md" Pack="true" PackagePath="\"/>
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\ArrayExtensions.cs" Link="Imported\ArrayExtensions.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\AutoLocker.cs" Link="Imported\AutoLocker.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\AutoResetter.cs" Link="Imported\AutoResetter.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\ArrayExtensions.cs" Link="Imported\ArrayExtensions.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\AutoLocker.cs" Link="Imported\AutoLocker.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\AutoResetter.cs" Link="Imported\AutoResetter.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\AutoResetterOfT.cs">
<Link>Imported\AutoResetterOfT.cs</Link>
</Compile>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\ByteArrayExtensions.cs" Link="Imported\ByteArrayExtensions.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\DeepEqualityTester.cs" Link="Imported\DeepEqualityTester.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\DictionaryExtensions.cs" Link="Imported\DictionaryExtensions.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\EnumerableWrapper.cs" Link="Imported\EnumerableWrapper.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\ObjectExtensions.cs" Link="Imported\ObjectExtensions.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\ExtensionsForIEnumerables.cs" Link="Imported\ExtensionsForIEnumerables.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\MetadataExtensions.cs" Link="Imported\MetadataExtensions.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\MemberNotFoundException.cs" Link="Imported\MemberNotFoundException.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\Platform.cs" Link="Imported\Platform.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\PathType.cs" Link="Imported\PathType.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\PropertyOrField.cs" Link="Imported\PropertyOrField.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\PyLike.cs" Link="Imported\PyLike.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\StringExtensions.cs" Link="Imported\StringExtensions.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\RandomNumber.cs" Link="Imported\RandomNumber.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\Stringifier.cs" Link="Imported\Stringifier.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\TypeExtensions.cs" Link="Imported\TypeExtensions.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\CannotZipNullException.cs" Link="Imported\CannotZipNullException.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\UnevenZipException.cs" Link="Imported\UnevenZipException.cs"/>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\ByteArrayExtensions.cs" Link="Imported\ByteArrayExtensions.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\DeepEqualityTester.cs" Link="Imported\DeepEqualityTester.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\DictionaryExtensions.cs" Link="Imported\DictionaryExtensions.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\EnumerableWrapper.cs" Link="Imported\EnumerableWrapper.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\FlagExtensions.cs">
<Link>Imported\FlagExtensions.cs</Link>
</Compile>
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\ObjectExtensions.cs" Link="Imported\ObjectExtensions.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\ExtensionsForIEnumerables.cs" Link="Imported\ExtensionsForIEnumerables.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\MetadataExtensions.cs" Link="Imported\MetadataExtensions.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\MemberNotFoundException.cs" Link="Imported\MemberNotFoundException.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\Platform.cs" Link="Imported\Platform.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\PathType.cs" Link="Imported\PathType.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\PropertyOrField.cs" Link="Imported\PropertyOrField.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\PyLike.cs" Link="Imported\PyLike.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\StringExtensions.cs" Link="Imported\StringExtensions.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\RandomNumber.cs" Link="Imported\RandomNumber.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\Stringifier.cs" Link="Imported\Stringifier.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\TypeExtensions.cs" Link="Imported\TypeExtensions.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\CannotZipNullException.cs" Link="Imported\CannotZipNullException.cs" />
<Compile Include="..\PeanutButter\source\Utils\PeanutButter.Utils\UnevenZipException.cs" Link="Imported\UnevenZipException.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='netstandard1.6'">
<Reference Include="System.Diagnostics.StackTrace"/>
<Reference Include="System.Runtime.Serialization"/>
<Reference Include="System.Diagnostics.StackTrace" />
<Reference Include="System.Runtime.Serialization" />
</ItemGroup>
<ItemGroup>
<None Include="icon.png" Pack="true" PackagePath=""/>
<None Include="icon.png" Pack="true" PackagePath="" />
</ItemGroup>
<Import Project="..\MonoForFramework.targets"/>
<Import Project="..\MonoForFramework.targets" />
</Project>
2 changes: 1 addition & 1 deletion src/PeanutButter
Submodule PeanutButter updated 46 files
+1 −1 source/Async/PeanutButter.Async/Package.nuspec
+7 −7 source/Async/PeanutButter.TestUtils.Async/Package.nuspec
+3 −3 source/DatabaseHelpers/PeanutButter.DatabaseHelpers.Testability/Package.nuspec
+2 −2 source/DatabaseHelpers/PeanutButter.DatabaseHelpers/Package.nuspec
+1 −1 source/INI/PeanutButter.INI/Package.nuspec
+7 −7 source/SimpleServers/PeanutButter.SimpleHTTPServer.Testability/Package.nuspec
+5 −5 source/SimpleServers/PeanutButter.SimpleHTTPServer/Package.nuspec
+3 −3 source/SimpleServers/PeanutButter.SimpleTcpServer/Package.nuspec
+5 −5 source/TempDb/PeanutButter.TempDb.LocalDb/Package.nuspec
+7 −7 source/TempDb/PeanutButter.TempDb.MySql.Connector/Package.nuspec
+1 −1 source/TempDb/PeanutButter.TempDb.MySql.Data.Tests/TestTempDbMySqlData.cs
+7 −7 source/TempDb/PeanutButter.TempDb.MySql.Data/Package.nuspec
+1 −1 source/TempDb/PeanutButter.TempDb.Runner/Package.nuspec
+5 −5 source/TempDb/PeanutButter.TempDb.SqlCe/Package.nuspec
+5 −5 source/TempDb/PeanutButter.TempDb.Sqlite/Package.nuspec
+3 −3 source/TempDb/PeanutButter.TempDb/Package.nuspec
+5 −5 source/TempDb/PeanutButter.TempRedis/Package.nuspec
+1 −1 source/TestUtils/PeanutButter.RandomGenerators/GenericBuilder.cs
+3 −3 source/TestUtils/PeanutButter.RandomGenerators/Package.nuspec
+5 −5 source/TestUtils/PeanutButter.TestUtils.AspNetCore/Package.nuspec
+3 −3 source/TestUtils/PeanutButter.TestUtils.Generic/Package.nuspec
+1 −1 source/TestUtils/PeanutButter.TestUtils.MVC/Package.nuspec
+1 −1 source/TinyEventAggregator/PeanutButter.TinyEventAggregator/Package.nuspec
+2 −2 source/Utils/PeanutButter.DuckTyping.Tests/AutoConversion/Converters/TestGenericStringConverter.cs
+1 −1 source/Utils/PeanutButter.DuckTyping/Package.nuspec
+5 −5 source/Utils/PeanutButter.EasyArgs/Package.nuspec
+1 −1 source/Utils/PeanutButter.JObjectExtensions/Package.nuspec
+56 −0 source/Utils/PeanutButter.Utils.Tests/TestAutoBarrier.cs
+182 −179 source/Utils/PeanutButter.Utils.Tests/TestAutoLocker.cs
+1 −1 source/Utils/PeanutButter.Utils.Tests/TestDateTimeExtensions.cs
+265 −98 source/Utils/PeanutButter.Utils.Tests/TestFlagExtensions.cs
+1 −1 source/Utils/PeanutButter.Utils.Tests/TestObjectExtensions.cs
+35 −0 source/Utils/PeanutButter.Utils/AutoBarrier.cs
+64 −64 source/Utils/PeanutButter.Utils/AutoLocker.cs
+186 −3 source/Utils/PeanutButter.Utils/FlagExtensions.cs
+1 −1 source/Utils/PeanutButter.Utils/Package.nuspec
+36 −11 source/Utils/PeanutButter.Utils/PortFinder.cs
+1 −1 source/Utils/PeanutButter.XmlUtils/Package.nuspec
+2 −2 source/Win32Service/PeanutButter.ServiceShell/Package.nuspec
+1 −1 source/Win32Service/PeanutButter.WindowsServiceManagement/Package.nuspec
+1 −1 source/_deprecated_/EmailSpooler/EmailSpooler.Win32Service.DB/Package.nuspec
+3 −3 source/_deprecated_/EmailSpooler/EmailSpooler.Win32Service.Entity/Package.nuspec
+3 −3 source/_deprecated_/PeanutButter.FluentMigrator/Package.nuspec
+8 −8 source/_deprecated_/PeanutButter.TestUtils.Entity/Package.nuspec
+2 −2 source/_deprecated_/PeanutButter.Utils.Entity/Package.nuspec
+3 −3 source/_deprecated_/PeanutButter.Utils.Windsor/Package.nuspec

0 comments on commit 6df7afc

Please sign in to comment.