Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better support for NaN in numeric assertions on floats and doubles #1822

Merged
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions Src/FluentAssertions/AssertionExtensions.cs
Expand Up @@ -631,7 +631,7 @@ public static NullableNumericAssertions<ulong> Should(this ulong? actualValue)
[Pure]
public static NumericAssertions<float> Should(this float actualValue)
{
return new NumericAssertions<float>(actualValue);
return new FloatAssertions(actualValue);
}

/// <summary>
Expand All @@ -651,7 +651,7 @@ public static NullableNumericAssertions<float> Should(this float? actualValue)
[Pure]
public static NumericAssertions<double> Should(this double actualValue)
{
return new NumericAssertions<double>(actualValue);
return new DoubleAssertions(actualValue);
}

/// <summary>
Expand Down
12 changes: 12 additions & 0 deletions Src/FluentAssertions/Numeric/DoubleAssertions.cs
@@ -0,0 +1,12 @@
namespace FluentAssertions.Numeric
{
internal class DoubleAssertions : NumericAssertions<double>
{
public DoubleAssertions(double value)
: base(value)
{
}

private protected override bool IsNaN(double value) => double.IsNaN(value);
}
}
12 changes: 12 additions & 0 deletions Src/FluentAssertions/Numeric/FloatAssertions.cs
@@ -0,0 +1,12 @@
namespace FluentAssertions.Numeric
{
internal class FloatAssertions : NumericAssertions<float>
{
public FloatAssertions(float value)
: base(value)
{
}

private protected override bool IsNaN(float value) => float.IsNaN(value);
}
}
40 changes: 36 additions & 4 deletions Src/FluentAssertions/Numeric/NumericAssertions.cs
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
Expand Down Expand Up @@ -165,7 +165,7 @@ public AndConstraint<TAssertions> BePositive(string because = "", params object[
public AndConstraint<TAssertions> BeNegative(string because = "", params object[] becauseArgs)
{
Execute.Assertion
.ForCondition(Subject.HasValue && Subject.Value.CompareTo(default(T)) < 0)
.ForCondition(Subject.HasValue && !IsNaN(Subject.Value) && Subject.Value.CompareTo(default(T)) < 0)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:value} to be negative{reason}, but found {0}.", Subject);

Expand All @@ -185,8 +185,13 @@ public AndConstraint<TAssertions> BeNegative(string because = "", params object[
/// </param>
public AndConstraint<TAssertions> BeLessThan(T expected, string because = "", params object[] becauseArgs)
{
if (IsNaN(expected))
{
throw new ArgumentException("A value can never be less than NaN", nameof(expected));
}

Execute.Assertion
.ForCondition(Subject.HasValue && Subject.Value.CompareTo(expected) < 0)
.ForCondition(Subject.HasValue && !IsNaN(Subject.Value) && Subject.Value.CompareTo(expected) < 0)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:value} to be less than {0}{reason}, but found {1}.", expected, Subject);

Expand All @@ -207,8 +212,13 @@ public AndConstraint<TAssertions> BeLessThan(T expected, string because = "", pa
public AndConstraint<TAssertions> BeLessThanOrEqualTo(T expected, string because = "",
params object[] becauseArgs)
{
dennisdoomen marked this conversation as resolved.
Show resolved Hide resolved
if (IsNaN(expected))
{
throw new ArgumentException("A value can never be less than or equal to NaN", nameof(expected));
}

Execute.Assertion
.ForCondition(Subject.HasValue && Subject.Value.CompareTo(expected) <= 0)
.ForCondition(Subject.HasValue && !IsNaN(Subject.Value) && Subject.Value.CompareTo(expected) <= 0)
.BecauseOf(because, becauseArgs)
.FailWith("Expected {context:value} to be less than or equal to {0}{reason}, but found {1}.", expected, Subject);

Expand All @@ -232,6 +242,11 @@ public AndConstraint<TAssertions> BeLessThan(T expected, string because = "", pa
public AndConstraint<TAssertions> BeGreaterThan(T expected, string because = "",
params object[] becauseArgs)
{
if (IsNaN(expected))
{
throw new ArgumentException("A value can never be greater than NaN", nameof(expected));
}

Execute.Assertion
.ForCondition(Subject.HasValue && Subject.Value.CompareTo(expected) > 0)
.BecauseOf(because, becauseArgs)
Expand All @@ -254,6 +269,11 @@ public AndConstraint<TAssertions> BeLessThan(T expected, string because = "", pa
public AndConstraint<TAssertions> BeGreaterThanOrEqualTo(T expected, string because = "",
params object[] becauseArgs)
{
if (IsNaN(expected))
{
throw new ArgumentException("A value can never be greater than or equal to a NaN", nameof(expected));
}

Execute.Assertion
.ForCondition(Subject.HasValue && Subject.Value.CompareTo(expected) >= 0)
.BecauseOf(because, becauseArgs)
Expand Down Expand Up @@ -287,6 +307,11 @@ public AndConstraint<TAssertions> BeLessThan(T expected, string because = "", pa
public AndConstraint<TAssertions> BeInRange(T minimumValue, T maximumValue, string because = "",
params object[] becauseArgs)
{
if (IsNaN(minimumValue) || IsNaN(maximumValue))
{
throw new ArgumentException("A range cannot begin or end with NaN");
}

Execute.Assertion
.ForCondition(Subject.HasValue && (Subject.Value.CompareTo(minimumValue) >= 0) && (Subject.Value.CompareTo(maximumValue) <= 0))
.BecauseOf(because, becauseArgs)
Expand Down Expand Up @@ -318,6 +343,11 @@ public AndConstraint<TAssertions> BeLessThan(T expected, string because = "", pa
public AndConstraint<TAssertions> NotBeInRange(T minimumValue, T maximumValue, string because = "",
params object[] becauseArgs)
{
if (IsNaN(minimumValue) || IsNaN(maximumValue))
{
throw new ArgumentException("A range cannot begin or end with NaN");
}

Execute.Assertion
.ForCondition(Subject.HasValue && !((Subject.Value.CompareTo(minimumValue) >= 0) && (Subject.Value.CompareTo(maximumValue) <= 0)))
.BecauseOf(because, becauseArgs)
Expand Down Expand Up @@ -449,5 +479,7 @@ public AndConstraint<TAssertions> NotBeOfType(Type unexpectedType, string becaus
/// <inheritdoc/>
public override bool Equals(object obj) =>
throw new NotSupportedException("Calling Equals on Assertion classes is not supported.");

private protected virtual bool IsNaN(T value) => false;
}
}
20 changes: 20 additions & 0 deletions Src/FluentAssertions/NumericAssertionsExtensions.cs
Expand Up @@ -752,6 +752,11 @@ public static class NumericAssertionsExtensions
float expectedValue, float precision, string because = "",
params object[] becauseArgs)
{
if (float.IsNaN(expectedValue))
{
throw new ArgumentException("Cannot determine approximation of a float to NaN", nameof(expectedValue));
}

if (precision < 0)
{
throw new ArgumentOutOfRangeException(nameof(precision), $"The value of {nameof(precision)} must be non-negative.");
Expand Down Expand Up @@ -879,6 +884,11 @@ public static class NumericAssertionsExtensions
double expectedValue, double precision, string because = "",
params object[] becauseArgs)
{
if (double.IsNaN(expectedValue))
{
throw new ArgumentException("Cannot determine approximation of a double to NaN", nameof(expectedValue));
}

if (precision < 0)
{
throw new ArgumentOutOfRangeException(nameof(precision), $"The value of {nameof(precision)} must be non-negative.");
Expand Down Expand Up @@ -1137,6 +1147,11 @@ public static class NumericAssertionsExtensions
float unexpectedValue, float precision, string because = "",
params object[] becauseArgs)
{
if (float.IsNaN(unexpectedValue))
{
throw new ArgumentException("Cannot determine approximation of a float to NaN", nameof(unexpectedValue));
}

if (precision < 0)
{
throw new ArgumentOutOfRangeException(nameof(precision), $"The value of {nameof(precision)} must be non-negative.");
Expand Down Expand Up @@ -1262,6 +1277,11 @@ public static class NumericAssertionsExtensions
double unexpectedValue, double precision, string because = "",
params object[] becauseArgs)
{
if (double.IsNaN(unexpectedValue))
{
throw new ArgumentException("Cannot determine approximation of a double to NaN", nameof(unexpectedValue));
}

if (precision < 0)
{
throw new ArgumentOutOfRangeException(nameof(precision), $"The value of {nameof(precision)} must be non-negative.");
Expand Down