Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using BenchmarkDotNet.Attributes;

namespace Light.GuardClauses.Performance.ComparableAssertions;

[MemoryDiagnoser]
// ReSharper disable once ClassCanBeSealed.Global -- Benchmark.NET derives from this class with dynamically created code
public class MustBeApproximatelyBenchmark
{
[Benchmark]
public double MustBeApproximately() => 5.100001.MustBeApproximately(5.100000, 0.0001);

[Benchmark(Baseline = true)]
public double Imperative() => Imperative(5.100001, 5.100000, 0.0001);

private static double Imperative(double parameter, double other, double tolerance)
{
if (Math.Abs(parameter - other) > tolerance)
{
throw new ArgumentOutOfRangeException(nameof(parameter), $"Value is not approximately {other}");
}

return parameter;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using BenchmarkDotNet.Attributes;

namespace Light.GuardClauses.Performance.ComparableAssertions;

[MemoryDiagnoser]
// ReSharper disable once ClassCanBeSealed.Global -- Benchmark.NET derives from this class with dynamically created code
public class MustNotBeApproximatelyBenchmark
{
[Benchmark]
public double MustNotBeApproximately() => 5.2.MustNotBeApproximately(5.1, 0.5);

[Benchmark(Baseline = true)]
public double Imperative() => Imperative(5.2, 5.1, 0.5);

private static double Imperative(double parameter, double other, double tolerance)
{
if (Math.Abs(parameter - other) < tolerance)
{
throw new ArgumentOutOfRangeException(nameof(parameter), $"Value is approximately {other}");
}

return parameter;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<ItemGroup>
<ProjectReference Include="..\Light.GuardClauses\Light.GuardClauses.csproj" />
<PackageReference Include="BenchmarkDotNet" Version="0.13.9" />
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
</ItemGroup>

</Project>
23 changes: 11 additions & 12 deletions Code/Light.GuardClauses.Performance/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,17 @@
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;

namespace Light.GuardClauses.Performance
namespace Light.GuardClauses.Performance;

public static class Program
{
public static class Program
{
private static IConfig DefaultConfiguration =>
DefaultConfig
.Instance
.AddJob(Job.Default.WithRuntime(CoreRuntime.Core70))
.AddJob(Job.Default.WithRuntime(ClrRuntime.Net48))
.AddDiagnoser(MemoryDiagnoser.Default, new DisassemblyDiagnoser(new DisassemblyDiagnoserConfig()));
private static IConfig DefaultConfiguration =>
DefaultConfig
.Instance
.AddJob(Job.Default.WithRuntime(CoreRuntime.Core80))
.AddJob(Job.Default.WithRuntime(ClrRuntime.Net48))
.AddDiagnoser(MemoryDiagnoser.Default, new DisassemblyDiagnoser(new DisassemblyDiagnoserConfig()));

public static void Main(string[] arguments) =>
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(arguments, DefaultConfiguration);
}
public static void Main(string[] arguments) =>
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(arguments, DefaultConfiguration);
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,60 @@ public static void FloatWithDefaultTolerance(float first, float second, bool exp
[InlineData(5.0f, 15.0001f, 10.0f, false)]
public static void FloatWithCustomTolerance(float first, float second, float tolerance, bool expected) =>
first.IsApproximately(second, tolerance).Should().Be(expected);
}

#if NET8_0
[Theory]
[InlineData(1.1, 1.3, 0.5, true)]
[InlineData(100.55, 100.555, 0.00001, false)]
[InlineData(5.0, 14.999999, 10.0, true)]
[InlineData(5.0, 15.0, 10.0, false)]
[InlineData(5.0, 15.0001, 10.0, false)]
public static void GenericDoubleWithCustomTolerance(double first, double second, double tolerance, bool expected) =>
first.IsApproximately<double>(second, tolerance).Should().Be(expected);

[Theory]
[InlineData(1.1f, 1.3f, 0.5f, true)]
[InlineData(100.55f, 100.555f, 0.00001f, false)]
[InlineData(5.0f, 14.999999f, 10.0f, true)]
[InlineData(5.0f, 15.0f, 10.0f, false)]
[InlineData(5.0f, 15.0001f, 10.0f, false)]
public static void GenericFloatWithCustomTolerance(float first, float second, float tolerance, bool expected) =>
first.IsApproximately<float>(second, tolerance).Should().Be(expected);

[Theory]
[InlineData(5, 10, 10, true)]
[InlineData(5, 15, 10, false)]
[InlineData(-5, 5, 12, true)]
[InlineData(-100, 100, 199, false)]
[InlineData(42, 42, 1, true)]
public static void GenericIntWithCustomTolerance(int first, int second, int tolerance, bool expected) =>
first.IsApproximately(second, tolerance).Should().Be(expected);

[Theory]
[InlineData(5L, 10L, 10L, true)]
[InlineData(5L, 15L, 10L, false)]
[InlineData(-5L, 5L, 12L, true)]
[InlineData(-100L, 100L, 199L, false)]
[InlineData(42L, 42L, 1L, true)]
public static void GenericLongWithCustomTolerance(long first, long second, long tolerance, bool expected) =>
first.IsApproximately(second, tolerance).Should().Be(expected);

[Theory]
[MemberData(nameof(DecimalTestData))]
public static void GenericDecimalWithCustomTolerance(
decimal first,
decimal second,
decimal tolerance,
bool expected
) =>
first.IsApproximately(second, tolerance).Should().Be(expected);

public static TheoryData<decimal, decimal, decimal, bool> DecimalTestData() => new ()
{
{ 1.1m, 1.3m, 0.5m, true },
{ 100.55m, 100.555m, 0.00001m, false },
{ 5.0m, 14.999999m, 10.0m, true },
{ 5.0m, 15.0m, 10.0m, false },
};
#endif
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
using System;
using FluentAssertions;
using Xunit;

namespace Light.GuardClauses.Tests.ComparableAssertions;

public static class MustBeApproximatelyTests
{
[Theory]
[InlineData(5.1, 5.0, 0.2)]
[InlineData(10.3, 10.3, 0.01)]
[InlineData(3.14159, 3.14, 0.002)]
[InlineData(-42.0, -42.0001, 0.001)]
public static void ValuesApproximatelyEqual_Double(double value, double other, double tolerance) =>
value.MustBeApproximately(other, tolerance).Should().Be(value);

[Theory]
[InlineData(5.1f, 5.0f, 0.2f)]
[InlineData(10.3f, 10.3f, 0.01f)]
[InlineData(3.14159f, 3.14f, 0.002f)]
[InlineData(-42.0f, -42.0001f, 0.001f)]
public static void ValuesApproximatelyEqual_Float(float value, float other, float tolerance) =>
value.MustBeApproximately(other, tolerance).Should().Be(value);

[Theory]
[InlineData(5.0, 5.3, 0.1)]
[InlineData(100.0, 99.8, 0.1)]
[InlineData(-20.0, -20.2, 0.1)]
[InlineData(0.0001, 0.0002, 0.00005)]
public static void ValuesNotApproximatelyEqual_Double(double value, double other, double tolerance)
{
var act = () => value.MustBeApproximately(other, tolerance, nameof(value));

var exceptionAssertion = act.Should().Throw<ArgumentOutOfRangeException>().Which;
exceptionAssertion.Message.Should().Contain(
$"{nameof(value)} must be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}."
);
exceptionAssertion.ParamName.Should().BeSameAs(nameof(value));
}

[Theory]
[InlineData(5.0f, 5.3f, 0.1f)]
[InlineData(100.0f, 99.8f, 0.1f)]
[InlineData(-20.0f, -20.2f, 0.1f)]
[InlineData(0.0001f, 0.0002f, 0.00005f)]
public static void ValuesNotApproximatelyEqual_Float(float value, float other, float tolerance)
{
var act = () => value.MustBeApproximately(other, tolerance, nameof(value));

var exceptionAssertion = act.Should().Throw<ArgumentOutOfRangeException>().Which;
exceptionAssertion.Message.Should().Contain(
$"{nameof(value)} must be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}."
);
exceptionAssertion.ParamName.Should().BeSameAs(nameof(value));
}

[Fact]
public static void DefaultTolerance_Double()
{
// Should pass - difference is 0.00005 which is less than default tolerance 0.0001
const double value = 1.00005;
value.MustBeApproximately(1.0).Should().Be(value);

// Should throw - difference is 0.0002 which is greater than default tolerance 0.0001
Action act = () => 1.0002.MustBeApproximately(1.0, "parameter");
act.Should().Throw<ArgumentOutOfRangeException>()
.WithParameterName("parameter");
}

[Fact]
public static void DefaultTolerance_Float()
{
// Should pass - difference is 0.00005f which is less than default tolerance 0.0001f
const float value = 1.00005f;
value.MustBeApproximately(1.0f).Should().Be(value);

// Should throw - difference is 0.0002f which is greater than default tolerance 0.0001f
Action act = () => 1.0002f.MustBeApproximately(1.0f, "parameter");
act.Should().Throw<ArgumentOutOfRangeException>()
.WithParameterName("parameter");
}

[Fact]
public static void CustomException_Double() =>
Test.CustomException(
5.0,
5.3,
(x, y, exceptionFactory) => x.MustBeApproximately(y, exceptionFactory)
);

[Fact]
public static void CustomExceptionWithTolerance_Double() =>
Test.CustomException(
5.0,
5.5,
0.1,
(x, y, z, exceptionFactory) => x.MustBeApproximately(y, z, exceptionFactory)
);

[Fact]
public static void CustomException_Float() =>
Test.CustomException(
5.0f,
5.3f,
(x, y, exceptionFactory) => x.MustBeApproximately(y, exceptionFactory)
);

[Fact]
public static void CustomExceptionWithTolerance_Float() =>
Test.CustomException(
5.0f,
5.5f,
0.1f,
(x, y, z, exceptionFactory) => x.MustBeApproximately(y, z, exceptionFactory)
);

[Fact]
public static void NoCustomExceptionThrown_Double() =>
5.0.MustBeApproximately(5.05, 0.1, (_, _, _) => null).Should().Be(5.0);

[Fact]
public static void NoCustomExceptionThrown_Float() =>
5.0f.MustBeApproximately(5.05f, 0.1f, (_, _, _) => null).Should().Be(5.0f);

[Fact]
public static void CustomMessage_Double() =>
Test.CustomMessage<ArgumentOutOfRangeException>(
message => 100.0.MustBeApproximately(101.0, 0.5, message: message)
);

[Fact]
public static void CustomMessage_Float() =>
Test.CustomMessage<ArgumentOutOfRangeException>(
message => 100.0f.MustBeApproximately(101.0f, 0.5f, message: message)
);

[Fact]
public static void CallerArgumentExpression_Double()
{
const double seventyEightO1 = 78.1;

var act = () => seventyEightO1.MustBeApproximately(3.0);

act.Should().Throw<ArgumentOutOfRangeException>()
.WithParameterName(nameof(seventyEightO1));
}

[Fact]
public static void CallerArgumentExpressionWithTolerance_Double()
{
const double pi = 3.14159;

var act = () => pi.MustBeApproximately(3.0, 0.1);

act.Should().Throw<ArgumentOutOfRangeException>()
.WithParameterName(nameof(pi));
}

[Fact]
public static void CallerArgumentExpression_Float()
{
const float seventyEightO1 = 78.1f;

var act = () => seventyEightO1.MustBeApproximately(3.0f);

act.Should().Throw<ArgumentOutOfRangeException>()
.WithParameterName(nameof(seventyEightO1));
}

[Fact]
public static void CallerArgumentExpressionWithTolerance_Float()
{
const float pi = 3.14159f;

var act = () => pi.MustBeApproximately(3.0f, 0.1f);

act.Should().Throw<ArgumentOutOfRangeException>()
.WithParameterName(nameof(pi));
}

#if NET8_0
[Theory]
[InlineData(5.1, 5.0, 0.2)]
[InlineData(10.3, 10.3, 0.01)]
[InlineData(3.14159, 3.14, 0.002)]
[InlineData(-42.0, -42.0001, 0.001)]
public static void ValuesApproximatelyEqual_Generic(double value, double other, double tolerance) =>
value.MustBeApproximately<double>(other, tolerance).Should().Be(value);

[Theory]
[InlineData(5.0, 5.3, 0.1)]
[InlineData(100.0, 99.8, 0.1)]
[InlineData(-20.0, -20.2, 0.1)]
[InlineData(0.0001, 0.0002, 0.00005)]
public static void ValuesNotApproximatelyEqual_Generic(double value, double other, double tolerance)
{
var act = () => value.MustBeApproximately<double>(other, tolerance, nameof(value));

var exceptionAssertion = act.Should().Throw<ArgumentOutOfRangeException>().Which;
exceptionAssertion.Message.Should().Contain(
$"{nameof(value)} must be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {value}."
);
exceptionAssertion.ParamName.Should().BeSameAs(nameof(value));
}

[Fact]
public static void CustomExceptionWithTolerance_Generic() =>
Test.CustomException(
5.0,
5.3,
0.2,
(x, y, t, exceptionFactory) => x.MustBeApproximately<double>(y, t, exceptionFactory)
);

[Fact]
public static void NoCustomExceptionThrown_Generic() =>
5.0.MustBeApproximately<double>(5.05, 0.1, (_, _, _) => null).Should().Be(5.0);

[Fact]
public static void CustomMessage_Generic() =>
Test.CustomMessage<ArgumentOutOfRangeException>(
message => 100.0.MustBeApproximately<double>(101.0, 0.5, message: message)
);

[Fact]
public static void CallerArgumentExpressionWithTolerance_Generic()
{
const double seventyEightO1 = 78.1;

var act = () => seventyEightO1.MustBeApproximately<double>(3.0, 10.0);

act.Should().Throw<ArgumentOutOfRangeException>()
.WithParameterName(nameof(seventyEightO1));
}
#endif
}
Loading