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
7 changes: 5 additions & 2 deletions Source/Testably.Expectations/Results/ExpectationResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,12 @@ public class ExpectationResult<TResult, TSelf>(ExpectationBuilder expectationBui
/// Provide a <paramref name="reason" /> explaining why the constraint is needed.<br />
/// If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </summary>
public TSelf Because(string reason)
public TSelf Because(string reason,
[CallerArgumentExpression("reason")] string doNotPopulateThisValue = "")
{
expectationBuilder.AddReason(reason);
expectationBuilder
.AppendMethodStatement(nameof(Because), doNotPopulateThisValue)
.AddReason(reason);
return (TSelf)this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public TimeToleranceExpectationResult<TResult, TValue, TSelf> Within(TimeSpan to
[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);
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,19 @@ public static partial class ThatDateTimeShould
/// </summary>
public static TimeToleranceExpectationResult<DateTime, IThat<DateTime>> Be(
this IThat<DateTime> source,
DateTime expected,
DateTime? expected,
[CallerArgumentExpression("expected")] string doNotPopulateThisValue = "")
{
TimeTolerance tolerance = new();
return new TimeToleranceExpectationResult<DateTime, IThat<DateTime>>(
source.ExpectationBuilder
.AddConstraint(new ConditionConstraint(
expected,
(a, e, t) => IsWithinTolerance(t, a - e),
$"be {Formatter.Format(expected)}{tolerance}",
(a, e, t) => a.Kind == e.Kind && IsWithinTolerance(t, a - e),
(a, e) => a.Kind == e?.Kind
? $"found {Formatter.Format(a)}"
: "it differed in the Kind property",
tolerance))
.AppendMethodStatement(nameof(Be), doNotPopulateThisValue),
source,
Expand All @@ -36,7 +39,7 @@ public static TimeToleranceExpectationResult<DateTime, IThat<DateTime>> Be(
/// </summary>
public static TimeToleranceExpectationResult<DateTime, IThat<DateTime>> NotBe(
this IThat<DateTime> source,
DateTime unexpected,
DateTime? unexpected,
[CallerArgumentExpression("unexpected")]
string doNotPopulateThisValue = "")
{
Expand All @@ -45,8 +48,9 @@ public static TimeToleranceExpectationResult<DateTime, IThat<DateTime>> NotBe(
source.ExpectationBuilder
.AddConstraint(new ConditionConstraint(
unexpected,
(a, e, t) => !IsWithinTolerance(t, a - e),
$"not be {Formatter.Format(unexpected)}{tolerance}",
(a, e, t) => a.Kind != e.Kind || !IsWithinTolerance(t, a - e),
(a, _) => $"found {Formatter.Format(a)}",
tolerance))
.AppendMethodStatement(nameof(NotBe), doNotPopulateThisValue),
source,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@ public static partial class ThatDateTimeShould
/// </summary>
public static TimeToleranceExpectationResult<DateTime, IThat<DateTime>> BeAfter(
this IThat<DateTime> source,
DateTime expected,
DateTime? expected,
[CallerArgumentExpression("expected")] string doNotPopulateThisValue = "")
{
TimeTolerance tolerance = new();
return new TimeToleranceExpectationResult<DateTime, IThat<DateTime>>(
source.ExpectationBuilder
.AddConstraint(new ConditionConstraint(
expected,
(a, e, t) => a + t > e,
$"be after {Formatter.Format(expected)}",
(a, e, t) => a + t > e,
(a, _) => $"found {Formatter.Format(a)}",
tolerance))
.AppendMethodStatement(nameof(BeAfter), doNotPopulateThisValue),
source,
Expand All @@ -36,16 +37,17 @@ public static TimeToleranceExpectationResult<DateTime, IThat<DateTime>> BeAfter(
/// </summary>
public static TimeToleranceExpectationResult<DateTime, IThat<DateTime>> NotBeAfter(
this IThat<DateTime> source,
DateTime expected,
DateTime? expected,
[CallerArgumentExpression("expected")] string doNotPopulateThisValue = "")
{
TimeTolerance tolerance = new();
return new TimeToleranceExpectationResult<DateTime, IThat<DateTime>>(
source.ExpectationBuilder
.AddConstraint(new ConditionConstraint(
expected,
(a, e, t) => a - t <= e,
$"not be after {Formatter.Format(expected)}",
(a, e, t) => a - t <= e,
(a, _) => $"found {Formatter.Format(a)}",
tolerance))
.AppendMethodStatement(nameof(NotBeAfter), doNotPopulateThisValue),
source,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@ public static partial class ThatDateTimeShould
/// </summary>
public static TimeToleranceExpectationResult<DateTime, IThat<DateTime>> BeBefore(
this IThat<DateTime> source,
DateTime expected,
DateTime? expected,
[CallerArgumentExpression("expected")] string doNotPopulateThisValue = "")
{
TimeTolerance tolerance = new();
return new TimeToleranceExpectationResult<DateTime, IThat<DateTime>>(
source.ExpectationBuilder
.AddConstraint(new ConditionConstraint(
expected,
(a, e, t) => a - t < e,
$"be before {Formatter.Format(expected)}",
(a, e, t) => a - t < e,
(a, _) => $"found {Formatter.Format(a)}",
tolerance))
.AppendMethodStatement(nameof(BeBefore), doNotPopulateThisValue),
source,
Expand All @@ -36,16 +37,17 @@ public static TimeToleranceExpectationResult<DateTime, IThat<DateTime>> BeBefore
/// </summary>
public static TimeToleranceExpectationResult<DateTime, IThat<DateTime>> NotBeBefore(
this IThat<DateTime> source,
DateTime expected,
DateTime? expected,
[CallerArgumentExpression("expected")] string doNotPopulateThisValue = "")
{
TimeTolerance tolerance = new();
return new TimeToleranceExpectationResult<DateTime, IThat<DateTime>>(
source.ExpectationBuilder
.AddConstraint(new ConditionConstraint(
expected,
(a, e, t) => a + t >= e,
$"not be before {Formatter.Format(expected)}",
(a, e, t) => a + t >= e,
(a, _) => $"found {Formatter.Format(a)}",
tolerance))
.AppendMethodStatement(nameof(NotBeBefore), doNotPopulateThisValue),
source,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@ public static partial class ThatDateTimeShould
/// </summary>
public static TimeToleranceExpectationResult<DateTime, IThat<DateTime>> BeOnOrAfter(
this IThat<DateTime> source,
DateTime expected,
DateTime? expected,
[CallerArgumentExpression("expected")] string doNotPopulateThisValue = "")
{
TimeTolerance tolerance = new();
return new TimeToleranceExpectationResult<DateTime, IThat<DateTime>>(
source.ExpectationBuilder
.AddConstraint(new ConditionConstraint(
expected,
(a, e, t) => a + t >= e,
$"be on or after {Formatter.Format(expected)}",
(a, e, t) => a + t >= e,
(a, _) => $"found {Formatter.Format(a)}",
tolerance))
.AppendMethodStatement(nameof(BeOnOrAfter), doNotPopulateThisValue),
source,
Expand All @@ -36,16 +37,17 @@ public static TimeToleranceExpectationResult<DateTime, IThat<DateTime>> BeOnOrAf
/// </summary>
public static TimeToleranceExpectationResult<DateTime, IThat<DateTime>> NotBeOnOrAfter(
this IThat<DateTime> source,
DateTime expected,
DateTime? expected,
[CallerArgumentExpression("expected")] string doNotPopulateThisValue = "")
{
TimeTolerance tolerance = new();
return new TimeToleranceExpectationResult<DateTime, IThat<DateTime>>(
source.ExpectationBuilder
.AddConstraint(new ConditionConstraint(
expected,
(a, e, t) => a - t < e,
$"not be on or after {Formatter.Format(expected)}",
(a, e, t) => a - t < e,
(a, _) => $"found {Formatter.Format(a)}",
tolerance))
.AppendMethodStatement(nameof(NotBeOnOrAfter), doNotPopulateThisValue),
source,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@ public static partial class ThatDateTimeShould
/// </summary>
public static TimeToleranceExpectationResult<DateTime, IThat<DateTime>> BeOnOrBefore(
this IThat<DateTime> source,
DateTime expected,
DateTime? expected,
[CallerArgumentExpression("expected")] string doNotPopulateThisValue = "")
{
TimeTolerance tolerance = new();
return new TimeToleranceExpectationResult<DateTime, IThat<DateTime>>(
source.ExpectationBuilder
.AddConstraint(new ConditionConstraint(
expected,
(a, e, t) => a - t <= e,
$"be on or before {Formatter.Format(expected)}",
(a, e, t) => a - t <= e,
(a, _) => $"found {Formatter.Format(a)}",
tolerance))
.AppendMethodStatement(nameof(BeOnOrBefore), doNotPopulateThisValue),
source,
Expand All @@ -36,16 +37,17 @@ public static TimeToleranceExpectationResult<DateTime, IThat<DateTime>> BeOnOrBe
/// </summary>
public static TimeToleranceExpectationResult<DateTime, IThat<DateTime>> NotBeOnOrBefore(
this IThat<DateTime> source,
DateTime expected,
DateTime? expected,
[CallerArgumentExpression("expected")] string doNotPopulateThisValue = "")
{
TimeTolerance tolerance = new();
return new TimeToleranceExpectationResult<DateTime, IThat<DateTime>>(
source.ExpectationBuilder
.AddConstraint(new ConditionConstraint(
expected,
(a, e, t) => a + t > e,
$"not be on or before {Formatter.Format(expected)}",
(a, e, t) => a + t > e,
(a, _) => $"found {Formatter.Format(a)}",
tolerance))
.AppendMethodStatement(nameof(NotBeOnOrBefore), doNotPopulateThisValue),
source,
Expand Down
35 changes: 15 additions & 20 deletions Source/Testably.Expectations/That/Chronology/ThatDateTimeShould.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using System;
using System.Runtime.CompilerServices;
using Testably.Expectations.Core;
using Testably.Expectations.Core.Constraints;
using Testably.Expectations.Formatting;
using Testably.Expectations.Options;

// ReSharper disable once CheckNamespace
Expand All @@ -16,16 +14,7 @@ public static partial class ThatDateTimeShould
/// <summary>
/// Start expectations for current <see cref="DateTime" /> <paramref name="subject" />.
/// </summary>
public static IThat<DateTime> Should(this IExpectSubject<DateTime> subject,
[CallerArgumentExpression("subject")] string doNotPopulateThisValue = "")
=> subject.Should(expectationBuilder => expectationBuilder
.AppendMethodStatement(nameof(Should)));

/// <summary>
/// Start expectations for the current <see cref="DateTime" />? <paramref name="subject" />.
/// </summary>
public static IThat<DateTime?> Should(this IExpectSubject<DateTime?> subject,
[CallerArgumentExpression("subject")] string doNotPopulateThisValue = "")
public static IThat<DateTime> Should(this IExpectSubject<DateTime> subject)
=> subject.Should(expectationBuilder => expectationBuilder
.AppendMethodStatement(nameof(Should)));

Expand All @@ -36,28 +25,34 @@ private static bool IsWithinTolerance(TimeSpan? tolerance, TimeSpan difference)
return difference == TimeSpan.Zero;
}

return difference <= tolerance.Value && difference >= tolerance.Value.Negate();
return difference <= tolerance.Value &&
difference >= tolerance.Value.Negate();
}

private readonly struct ConditionConstraint(
DateTime expected,
Func<DateTime, DateTime, TimeSpan, bool> condition,
DateTime? expected,
string expectation,
Func<DateTime, DateTime, TimeSpan, bool> condition,
Func<DateTime, DateTime?, string> failureMessageFactory,
TimeTolerance tolerance) : IValueConstraint<DateTime>
{
private readonly string _expectation = expectation;

public ConstraintResult IsMetBy(DateTime actual)
{
if (condition(actual, expected, tolerance.Tolerance ?? TimeSpan.Zero))
if (expected is null)
{
return new ConstraintResult.Failure(ToString(), failureMessageFactory(actual, expected));
}

if (condition(actual, expected.Value, tolerance.Tolerance ?? TimeSpan.Zero))
{
return new ConstraintResult.Success<DateTime>(actual, ToString());
}

return new ConstraintResult.Failure(ToString(), $"found {Formatter.Format(actual)}");
return new ConstraintResult.Failure(ToString(),
failureMessageFactory(actual, expected.Value));
}

public override string ToString()
=> _expectation + tolerance;
=> expectation + tolerance;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using System.Runtime.CompilerServices;
using Testably.Expectations.Core;
using Testably.Expectations.Formatting;
using Testably.Expectations.Options;
using Testably.Expectations.Results;

// ReSharper disable once CheckNamespace
namespace Testably.Expectations;

public static partial class ThatNullableDateTimeShould
{
/// <summary>
/// Verifies that the subject is equal to the <paramref name="expected" /> value.
/// </summary>
public static TimeToleranceExpectationResult<DateTime?, IThat<DateTime?>> Be(
this IThat<DateTime?> source,
DateTime? expected,
[CallerArgumentExpression("expected")] string doNotPopulateThisValue = "")
{
TimeTolerance tolerance = new();
return new TimeToleranceExpectationResult<DateTime?, IThat<DateTime?>>(
source.ExpectationBuilder
.AddConstraint(new ConditionConstraint(
expected,
$"be {Formatter.Format(expected)}{tolerance}",
(a, e, t) => a?.Kind == e?.Kind && IsWithinTolerance(t, a - e),
(a, e) => a?.Kind == e?.Kind
? $"found {Formatter.Format(a)}"
: "it differed in the Kind property",
tolerance))
.AppendMethodStatement(nameof(Be), doNotPopulateThisValue),
source,
tolerance);
}

/// <summary>
/// Verifies that the subject is not equal to the <paramref name="unexpected" /> value.
/// </summary>
public static TimeToleranceExpectationResult<DateTime?, IThat<DateTime?>> NotBe(
this IThat<DateTime?> source,
DateTime? unexpected,
[CallerArgumentExpression("unexpected")]
string doNotPopulateThisValue = "")
{
TimeTolerance tolerance = new();
return new TimeToleranceExpectationResult<DateTime?, IThat<DateTime?>>(
source.ExpectationBuilder
.AddConstraint(new ConditionConstraint(
unexpected,
$"not be {Formatter.Format(unexpected)}{tolerance}",
(a, e, t) => a?.Kind != e?.Kind || !IsWithinTolerance(t, a - e),
(a, _) => $"found {Formatter.Format(a)}",
tolerance))
.AppendMethodStatement(nameof(NotBe), doNotPopulateThisValue),
source,
tolerance);
}
}
Loading
Loading