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
47 changes: 47 additions & 0 deletions Source/Testably.Expectations/Options/NumberTolerance.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using Testably.Expectations.Formatting;

namespace Testably.Expectations.Options;

/// <summary>
/// Tolerance for number comparisons.
/// </summary>
public class NumberTolerance<TNumber>(
Func<TNumber, TNumber, TNumber?, bool> isWithinTolerance)
where TNumber : struct, IComparable<TNumber>
{
/// <summary>
/// The tolerance to apply on the number comparisons.
/// </summary>
public TNumber? Tolerance { get; private set; }

/// <summary>
/// Sets the tolerance to apply on the number comparisons.
/// </summary>
public void SetTolerance(TNumber tolerance)
{
if (tolerance.CompareTo(default) < 0)
{
throw new ArgumentOutOfRangeException(nameof(tolerance),
"Tolerance must be non-negative");
}

Tolerance = tolerance;
}

/// <inheritdoc />
public override string ToString()
{
if (Tolerance == null)
{
return "";
}

const char plusMinus = '\u00b1';
return $" {plusMinus} {Formatter.Format(Tolerance)}";
}

internal bool IsWithinTolerance(TNumber actual, TNumber? expected)
=> expected != null &&
isWithinTolerance(actual, expected.Value, Tolerance);
}
9 changes: 7 additions & 2 deletions Source/Testably.Expectations/Options/TimeTolerance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,15 @@ public class TimeTolerance
/// <summary>
/// Sets the tolerance to apply on the time comparisons.
/// </summary>
public TimeTolerance SetTolerance(TimeSpan tolerance)
public void SetTolerance(TimeSpan tolerance)
{
if (tolerance < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(tolerance),
"Tolerance must be non-negative");
}

Tolerance = tolerance;
return this;
}

/// <inheritdoc />
Expand Down
52 changes: 52 additions & 0 deletions Source/Testably.Expectations/Results/NumberToleranceResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Runtime.CompilerServices;
using Testably.Expectations.Core;
using Testably.Expectations.Options;

namespace Testably.Expectations.Results;

/// <summary>
/// The result of an expectation with an underlying value of type <typeparamref name="TNumber" />.
/// <para />
/// In addition to the combinations from <see cref="AndOrResult{TResult,TValue}" />, allows specifying a
/// tolerance.
/// </summary>
public class NumberToleranceResult<TNumber, TResult>(
ExpectationBuilder expectationBuilder,
TResult returnValue,
NumberTolerance<TNumber> options)
: NumberToleranceResult<TNumber, TResult,
NumberToleranceResult<TNumber, TResult>>(
expectationBuilder,
returnValue,
options)
where TNumber : struct, IComparable<TNumber>;

/// <summary>
/// The result of an expectation with an underlying value of type <typeparamref name="TResult" />.
/// <para />
/// In addition to the combinations from <see cref="AndOrResult{TResult,TValue}" />, allows specifying a
/// tolerance.
/// </summary>
public class NumberToleranceResult<TNumber, TResult, TSelf>(
ExpectationBuilder expectationBuilder,
TResult returnValue,
NumberTolerance<TNumber> options)
: AndOrResult<TNumber, TResult, TSelf>(expectationBuilder, returnValue)
where TSelf : NumberToleranceResult<TNumber, TResult, TSelf>
where TNumber : struct, IComparable<TNumber>
{
private readonly ExpectationBuilder _expectationBuilder = expectationBuilder;

/// <summary>
/// Specifies a tolerance to apply on the number comparison.
/// </summary>
public NumberToleranceResult<TNumber, TResult, TSelf> Within(TNumber tolerance,
[CallerArgumentExpression("tolerance")]
string doNotPopulateThisValue = "")
{
options.SetTolerance(tolerance);
_expectationBuilder.AppendMethodStatement(nameof(Within), doNotPopulateThisValue);
return this;
}
}
9 changes: 2 additions & 7 deletions Source/Testably.Expectations/Results/TimeToleranceResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class TimeToleranceResult<TResult, TValue, TSelf>(
: AndOrResult<TResult, TValue, TSelf>(expectationBuilder, returnValue)
where TSelf : TimeToleranceResult<TResult, TValue, TSelf>
{
private readonly ExpectationBuilder _expectationBuilder1 = expectationBuilder;
private readonly ExpectationBuilder _expectationBuilder = expectationBuilder;

/// <summary>
/// Specifies a tolerance to apply on the time comparison.
Expand All @@ -43,13 +43,8 @@ public TimeToleranceResult<TResult, TValue, TSelf> Within(TimeSpan tolerance,
[CallerArgumentExpression("tolerance")]
string doNotPopulateThisValue = "")
{
if (tolerance < TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(tolerance), "Tolerance must be non-negative");
}

options.SetTolerance(tolerance);
_expectationBuilder1.AppendMethodStatement(nameof(Within), doNotPopulateThisValue);
_expectationBuilder.AppendMethodStatement(nameof(Within), doNotPopulateThisValue);
return this;
}
}
4 changes: 2 additions & 2 deletions Source/Testably.Expectations/That/Guids/ThatGuidShould.Be.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static partial class ThatGuidShould
/// Verifies that the subject is equal to the <paramref name="expected" /> value.
/// </summary>
public static AndOrResult<Guid, IThat<Guid>> Be(this IThat<Guid> source,
Guid expected,
Guid? expected,
[CallerArgumentExpression("expected")] string doNotPopulateThisValue = "")
=> new(source.ExpectationBuilder
.AddConstraint(new ValueConstraint(
Expand All @@ -26,7 +26,7 @@ public static AndOrResult<Guid, IThat<Guid>> Be(this IThat<Guid> source,
/// Verifies that the subject is not equal to the <paramref name="unexpected" /> value.
/// </summary>
public static AndOrResult<Guid, IThat<Guid>> NotBe(this IThat<Guid> source,
Guid unexpected,
Guid? unexpected,
[CallerArgumentExpression("unexpected")]
string doNotPopulateThisValue = "")
=> new(source.ExpectationBuilder
Expand Down
Loading
Loading