diff --git a/Docs/pages/docs/expectations/10-timespan.md b/Docs/pages/docs/expectations/10-timespan.md index f3bc93aa7..80a2e77cc 100644 --- a/Docs/pages/docs/expectations/10-timespan.md +++ b/Docs/pages/docs/expectations/10-timespan.md @@ -82,6 +82,28 @@ await Expect.That(subject).IsLessThan(42).Within(TimeSpan.FromSeconds(2)) .Because("we accept values less than 0:44 (0:42 ± 2s)"); ``` +## Between + +You can verify that the `TimeSpan` is between two values: + +```csharp +TimeSpan subject = TimeSpan.FromSeconds(42); + +await Expect.That(subject).IsBetween(TimeSpan.FromSeconds(40)).And(TimeSpan.FromSeconds(50)); +``` + +You can also specify a tolerance: + +```csharp +TimeSpan subject = TimeSpan.FromSeconds(42); + +await Expect.That(subject) + .IsBetween(43.Seconds()) + .And(45.Seconds()) + .Within(1.Seconds()) + .Because("it should expand the interval by 1 second"); +``` + ## Positive / negative You can verify that the `TimeSpan` is positive or negative: diff --git a/Source/aweXpect/That/TimeSpans/ThatNullableTimeSpan.IsBetween.cs b/Source/aweXpect/That/TimeSpans/ThatNullableTimeSpan.IsBetween.cs new file mode 100644 index 000000000..49d7805bb --- /dev/null +++ b/Source/aweXpect/That/TimeSpans/ThatNullableTimeSpan.IsBetween.cs @@ -0,0 +1,109 @@ +using System; +using aweXpect.Core; +using aweXpect.Core.Constraints; +using aweXpect.Customization; +using aweXpect.Helpers; +using aweXpect.Options; +using aweXpect.Results; + +namespace aweXpect; + +public static partial class ThatNullableTimeSpan +{ + /// + /// Verifies that the subject is between the … + /// + public static BetweenResult>, TimeSpan?> IsBetween( + this IThat source, + TimeSpan? minimum) + { + TimeTolerance tolerance = new(); + return new BetweenResult>, TimeSpan?>(maximum + => new TimeToleranceResult>( + source.Get().ExpectationBuilder.AddConstraint((it, grammars) => + new IsBetweenConstraint(it, grammars, minimum, maximum, tolerance)), + source, + tolerance)); + } + + /// + /// Verifies that the subject is not between the … + /// + public static BetweenResult>, TimeSpan?> IsNotBetween( + this IThat source, + TimeSpan? minimum) + { + TimeTolerance tolerance = new(); + return new BetweenResult>, TimeSpan?>(maximum + => new TimeToleranceResult>( + source.Get().ExpectationBuilder.AddConstraint((it, grammars) => + new IsBetweenConstraint(it, grammars, minimum, maximum, tolerance).Invert()), + source, + tolerance)); + } + + private sealed class IsBetweenConstraint( + string it, + ExpectationGrammars grammars, + TimeSpan? minimum, + TimeSpan? maximum, + TimeTolerance tolerance) + : ConstraintResult.WithNotNullValue(it, grammars), + IValueConstraint + { + public ConstraintResult IsMetBy(TimeSpan? actual) + { + Actual = actual; + if (actual is null && minimum is null && maximum is null) + { + Outcome = Outcome.Success; + } + else if (actual is null || minimum is null || maximum is null) + { + Outcome = IsNegated ? Outcome.Success : Outcome.Failure; + } + else + { + TimeSpan timeTolerance = tolerance.Tolerance + ?? Customize.aweXpect.Settings().DefaultTimeComparisonTolerance.Get(); + if (IsNegated) + { + timeTolerance = timeTolerance.Negate(); + } + + Outcome = actual.Value.Add(timeTolerance) >= minimum && actual.Value.Add(timeTolerance.Negate()) <= maximum + ? Outcome.Success + : Outcome.Failure; + } + + return this; + } + + protected override void AppendNormalExpectation(StringBuilder stringBuilder, string? indentation = null) + { + stringBuilder.Append("is between "); + Formatter.Format(stringBuilder, minimum); + stringBuilder.Append(" and "); + Formatter.Format(stringBuilder, maximum); + stringBuilder.Append(tolerance); + } + + protected override void AppendNormalResult(StringBuilder stringBuilder, string? indentation = null) + { + stringBuilder.Append(It).Append(" was "); + Formatter.Format(stringBuilder, Actual); + } + + protected override void AppendNegatedExpectation(StringBuilder stringBuilder, string? indentation = null) + { + stringBuilder.Append("is not between "); + Formatter.Format(stringBuilder, minimum); + stringBuilder.Append(" and "); + Formatter.Format(stringBuilder, maximum); + stringBuilder.Append(tolerance); + } + + protected override void AppendNegatedResult(StringBuilder stringBuilder, string? indentation = null) + => AppendNormalResult(stringBuilder, indentation); + } +} diff --git a/Source/aweXpect/That/TimeSpans/ThatTimeSpan.IsBetween.cs b/Source/aweXpect/That/TimeSpans/ThatTimeSpan.IsBetween.cs new file mode 100644 index 000000000..0eecb7c9b --- /dev/null +++ b/Source/aweXpect/That/TimeSpans/ThatTimeSpan.IsBetween.cs @@ -0,0 +1,105 @@ +using System; +using aweXpect.Core; +using aweXpect.Core.Constraints; +using aweXpect.Customization; +using aweXpect.Helpers; +using aweXpect.Options; +using aweXpect.Results; + +namespace aweXpect; + +public static partial class ThatTimeSpan +{ + /// + /// Verifies that the subject is between the … + /// + public static BetweenResult>, TimeSpan?> IsBetween( + this IThat source, + TimeSpan? minimum) + { + TimeTolerance tolerance = new(); + return new BetweenResult>, TimeSpan?>(maximum + => new TimeToleranceResult>( + source.Get().ExpectationBuilder.AddConstraint((it, grammars) => + new IsBetweenConstraint(it, grammars, minimum, maximum, tolerance)), + source, + tolerance)); + } + + /// + /// Verifies that the subject is not between the … + /// + public static BetweenResult>, TimeSpan?> IsNotBetween( + this IThat source, + TimeSpan? minimum) + { + TimeTolerance tolerance = new(); + return new BetweenResult>, TimeSpan?>(maximum + => new TimeToleranceResult>( + source.Get().ExpectationBuilder.AddConstraint((it, grammars) => + new IsBetweenConstraint(it, grammars, minimum, maximum, tolerance).Invert()), + source, + tolerance)); + } + + private sealed class IsBetweenConstraint( + string it, + ExpectationGrammars grammars, + TimeSpan? minimum, + TimeSpan? maximum, + TimeTolerance tolerance) + : ConstraintResult.WithNotNullValue(it, grammars), + IValueConstraint + { + public ConstraintResult IsMetBy(TimeSpan actual) + { + Actual = actual; + if (minimum is null || maximum is null) + { + Outcome = IsNegated ? Outcome.Success : Outcome.Failure; + } + else + { + TimeSpan timeTolerance = tolerance.Tolerance + ?? Customize.aweXpect.Settings().DefaultTimeComparisonTolerance.Get(); + if (IsNegated) + { + timeTolerance = timeTolerance.Negate(); + } + + Outcome = actual.Add(timeTolerance) >= minimum && actual.Add(timeTolerance.Negate()) <= maximum + ? Outcome.Success + : Outcome.Failure; + } + + return this; + } + + protected override void AppendNormalExpectation(StringBuilder stringBuilder, string? indentation = null) + { + stringBuilder.Append("is between "); + Formatter.Format(stringBuilder, minimum); + stringBuilder.Append(" and "); + Formatter.Format(stringBuilder, maximum); + stringBuilder.Append(tolerance); + } + + protected override void AppendNormalResult(StringBuilder stringBuilder, string? indentation = null) + { + stringBuilder.Append(It).Append(" was "); + Formatter.Format(stringBuilder, Actual); + } + + protected override void AppendNegatedExpectation(StringBuilder stringBuilder, string? indentation = null) + { + stringBuilder.Append("is not between "); + Formatter.Format(stringBuilder, minimum); + stringBuilder.Append(" and "); + Formatter.Format(stringBuilder, maximum); + stringBuilder.Append(tolerance); + } + + protected override void AppendNegatedResult(StringBuilder stringBuilder, string? indentation = null) + => AppendNormalResult(stringBuilder, indentation); + } +} diff --git a/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt b/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt index e456260c7..92f975821 100644 --- a/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt +++ b/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt @@ -653,12 +653,14 @@ namespace aweXpect } public static class ThatNullableTimeSpan { + public static aweXpect.Results.BetweenResult>, System.TimeSpan?> IsBetween(this aweXpect.Core.IThat source, System.TimeSpan? minimum) { } public static aweXpect.Results.TimeToleranceResult> IsEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.TimeToleranceResult> IsGreaterThan(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.TimeToleranceResult> IsGreaterThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.TimeToleranceResult> IsLessThan(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.TimeToleranceResult> IsLessThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.AndOrResult> IsNegative(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.BetweenResult>, System.TimeSpan?> IsNotBetween(this aweXpect.Core.IThat source, System.TimeSpan? minimum) { } public static aweXpect.Results.TimeToleranceResult> IsNotEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { } public static aweXpect.Results.TimeToleranceResult> IsNotGreaterThan(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { } public static aweXpect.Results.TimeToleranceResult> IsNotGreaterThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { } @@ -881,12 +883,14 @@ namespace aweXpect } public static class ThatTimeSpan { + public static aweXpect.Results.BetweenResult>, System.TimeSpan?> IsBetween(this aweXpect.Core.IThat source, System.TimeSpan? minimum) { } public static aweXpect.Results.TimeToleranceResult> IsEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.TimeToleranceResult> IsGreaterThan(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.TimeToleranceResult> IsGreaterThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.TimeToleranceResult> IsLessThan(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.TimeToleranceResult> IsLessThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.AndOrResult> IsNegative(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.BetweenResult>, System.TimeSpan?> IsNotBetween(this aweXpect.Core.IThat source, System.TimeSpan? minimum) { } public static aweXpect.Results.TimeToleranceResult> IsNotEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { } public static aweXpect.Results.TimeToleranceResult> IsNotGreaterThan(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { } public static aweXpect.Results.TimeToleranceResult> IsNotGreaterThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { } diff --git a/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt b/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt index f6bf8cc88..938dd8f57 100644 --- a/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt +++ b/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt @@ -469,12 +469,14 @@ namespace aweXpect } public static class ThatNullableTimeSpan { + public static aweXpect.Results.BetweenResult>, System.TimeSpan?> IsBetween(this aweXpect.Core.IThat source, System.TimeSpan? minimum) { } public static aweXpect.Results.TimeToleranceResult> IsEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.TimeToleranceResult> IsGreaterThan(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.TimeToleranceResult> IsGreaterThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.TimeToleranceResult> IsLessThan(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.TimeToleranceResult> IsLessThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.AndOrResult> IsNegative(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.BetweenResult>, System.TimeSpan?> IsNotBetween(this aweXpect.Core.IThat source, System.TimeSpan? minimum) { } public static aweXpect.Results.TimeToleranceResult> IsNotEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { } public static aweXpect.Results.TimeToleranceResult> IsNotGreaterThan(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { } public static aweXpect.Results.TimeToleranceResult> IsNotGreaterThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { } @@ -928,12 +930,14 @@ namespace aweXpect } public static class ThatTimeSpan { + public static aweXpect.Results.BetweenResult>, System.TimeSpan?> IsBetween(this aweXpect.Core.IThat source, System.TimeSpan? minimum) { } public static aweXpect.Results.TimeToleranceResult> IsEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.TimeToleranceResult> IsGreaterThan(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.TimeToleranceResult> IsGreaterThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.TimeToleranceResult> IsLessThan(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.TimeToleranceResult> IsLessThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? expected) { } public static aweXpect.Results.AndOrResult> IsNegative(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.BetweenResult>, System.TimeSpan?> IsNotBetween(this aweXpect.Core.IThat source, System.TimeSpan? minimum) { } public static aweXpect.Results.TimeToleranceResult> IsNotEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { } public static aweXpect.Results.TimeToleranceResult> IsNotGreaterThan(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { } public static aweXpect.Results.TimeToleranceResult> IsNotGreaterThanOrEqualTo(this aweXpect.Core.IThat source, System.TimeSpan? unexpected) { } diff --git a/Tests/aweXpect.Core.Tests/Customization/CustomizeSettingsTests.cs b/Tests/aweXpect.Core.Tests/Customization/CustomizeSettingsTests.cs index 039c07e1d..a02e70e94 100644 --- a/Tests/aweXpect.Core.Tests/Customization/CustomizeSettingsTests.cs +++ b/Tests/aweXpect.Core.Tests/Customization/CustomizeSettingsTests.cs @@ -11,7 +11,12 @@ public sealed class CustomizeSettingsTests [Fact] public async Task DefaultCheckInterval_ShouldBeUsedInTimeComparisons() { +#if DEBUG TimeSpan timeout = 2.Seconds(); +#else + TimeSpan timeout = 4.Seconds(); +#endif + ChangingClass sut1 = new(); ChangingClass sut2 = new(); diff --git a/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.IsBetween.Tests.cs b/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.IsBetween.Tests.cs new file mode 100644 index 000000000..69c8fd5a5 --- /dev/null +++ b/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.IsBetween.Tests.cs @@ -0,0 +1,254 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatTimeSpan +{ + public sealed class IsBetween + { + public sealed class Tests + { + [Fact] + public async Task WhenMaximumIsNull_ShouldFail() + { + TimeSpan subject = CurrentTime(); + TimeSpan? minimum = subject; + TimeSpan? maximum = null; + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is between {Formatter.Format(minimum)} and , + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenMinimumIsNull_ShouldFail() + { + TimeSpan subject = CurrentTime(); + TimeSpan? minimum = null; + TimeSpan? maximum = subject; + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is between and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectAndMaximumAreMaxValue_ShouldSucceed() + { + TimeSpan subject = TimeSpan.MaxValue; + TimeSpan minimum = CurrentTime(); + TimeSpan maximum = TimeSpan.MaxValue; + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectAndMinimumAreMinValue_ShouldSucceed() + { + TimeSpan subject = TimeSpan.MinValue; + TimeSpan minimum = TimeSpan.MinValue; + TimeSpan maximum = CurrentTime(); + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsBetweenMinimumAndMaximum_ShouldSucceed() + { + TimeSpan subject = CurrentTime(); + TimeSpan minimum = EarlierTime(); + TimeSpan maximum = LaterTime(); + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsEarlierThanMinimum_ShouldFail() + { + TimeSpan subject = EarlierTime(); + TimeSpan minimum = CurrentTime(); + TimeSpan maximum = LaterTime(); + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectIsLaterThanMaximum_ShouldFail() + { + TimeSpan subject = LaterTime(); + TimeSpan minimum = EarlierTime(); + TimeSpan maximum = CurrentTime(); + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectIsSameAsMaximum_ShouldSucceed() + { + TimeSpan subject = CurrentTime(); + TimeSpan minimum = EarlierTime(); + TimeSpan maximum = subject; + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsSameAsMinimum_ShouldSucceed() + { + TimeSpan subject = CurrentTime(); + TimeSpan minimum = subject; + TimeSpan maximum = LaterTime(); + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).DoesNotThrow(); + } + } + + public sealed class WithinTests + { + [Fact] + public async Task WhenMaximumValueIsOutsideTheTolerance_ShouldFail() + { + TimeSpan subject = LaterTime(4); + TimeSpan minimum = TimeSpan.MinValue; + TimeSpan maximum = CurrentTime(); + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenMinimumValueIsOutsideTheTolerance_ShouldFail() + { + TimeSpan subject = EarlierTime(4); + TimeSpan minimum = CurrentTime(); + TimeSpan maximum = TimeSpan.MaxValue; + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenNullableMaximumValueIsOutsideTheTolerance_ShouldFail() + { + TimeSpan subject = CurrentTime(); + TimeSpan minimum = TimeSpan.MinValue; + TimeSpan? maximum = LaterTime(-4); + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenNullableMinimumValueIsOutsideTheTolerance_ShouldFail() + { + TimeSpan subject = CurrentTime(); + TimeSpan? minimum = EarlierTime(-4); + TimeSpan maximum = TimeSpan.MaxValue; + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenValueIsWithinTheMaximumTolerance_ShouldSucceed() + { + TimeSpan subject = LaterTime(3); + TimeSpan minimum = TimeSpan.MinValue; + TimeSpan maximum = CurrentTime(); + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenValueIsWithinTheMinimumTolerance_ShouldSucceed() + { + TimeSpan subject = EarlierTime(3); + TimeSpan minimum = CurrentTime(); + TimeSpan maximum = TimeSpan.MaxValue; + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).DoesNotThrow(); + } + } + } +} diff --git a/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.IsNotBetween.Tests.cs b/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.IsNotBetween.Tests.cs new file mode 100644 index 000000000..8b8aece4a --- /dev/null +++ b/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.IsNotBetween.Tests.cs @@ -0,0 +1,259 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatTimeSpan +{ + public sealed class IsNotBetween + { + public sealed class Tests + { + [Fact] + public async Task WhenMaximumIsNull_ShouldFail() + { + TimeSpan subject = CurrentTime(); + TimeSpan? minimum = subject; + TimeSpan? maximum = null; + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between {Formatter.Format(minimum)} and , + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenMinimumIsNull_ShouldFail() + { + TimeSpan subject = CurrentTime(); + TimeSpan? minimum = null; + TimeSpan? maximum = subject; + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectAndMaximumAreMaxValue_ShouldFail() + { + TimeSpan subject = TimeSpan.MaxValue; + TimeSpan minimum = CurrentTime(); + TimeSpan maximum = TimeSpan.MaxValue; + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectAndMinimumAreMinValue_ShouldFail() + { + TimeSpan subject = TimeSpan.MinValue; + TimeSpan minimum = TimeSpan.MinValue; + TimeSpan maximum = CurrentTime(); + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectIsNotBetweenMinimumAndMaximum_ShouldFail() + { + TimeSpan subject = CurrentTime(); + TimeSpan minimum = EarlierTime(); + TimeSpan maximum = LaterTime(); + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectIsEarlierThanMinimum_ShouldSucceed() + { + TimeSpan subject = EarlierTime(); + TimeSpan minimum = CurrentTime(); + TimeSpan maximum = LaterTime(); + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsLaterThanMaximum_ShouldSucceed() + { + TimeSpan subject = LaterTime(); + TimeSpan minimum = EarlierTime(); + TimeSpan maximum = CurrentTime(); + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsSameAsMaximum_ShouldFail() + { + TimeSpan subject = CurrentTime(); + TimeSpan minimum = EarlierTime(); + TimeSpan maximum = subject; + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectIsSameAsMinimum_ShouldFail() + { + TimeSpan subject = CurrentTime(); + TimeSpan minimum = subject; + TimeSpan maximum = LaterTime(); + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + } + + public sealed class WithinTests + { + [Fact] + public async Task WhenMaximumValueIsOutsideTheTolerance_ShouldSucceed() + { + TimeSpan subject = CurrentTime(); + TimeSpan minimum = TimeSpan.MinValue; + TimeSpan maximum = LaterTime(2); + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenMinimumValueIsOutsideTheTolerance_ShouldSucceed() + { + TimeSpan subject = CurrentTime(); + TimeSpan minimum = EarlierTime(2); + TimeSpan maximum = TimeSpan.MaxValue; + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenNullableMaximumValueIsOutsideTheTolerance_ShouldSucceed() + { + TimeSpan subject = CurrentTime(); + TimeSpan minimum = TimeSpan.MinValue; + TimeSpan? maximum = LaterTime(2); + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenNullableMinimumValueIsOutsideTheTolerance_ShouldSucceed() + { + TimeSpan subject = CurrentTime(); + TimeSpan? minimum = EarlierTime(2); + TimeSpan maximum = TimeSpan.MaxValue; + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenValueIsWithinTheMaximumTolerance_ShouldFail() + { + TimeSpan subject = EarlierTime(3); + TimeSpan minimum = TimeSpan.MinValue; + TimeSpan maximum = CurrentTime(); + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenValueIsWithinTheMinimumTolerance_ShouldFail() + { + TimeSpan subject = LaterTime(3); + TimeSpan minimum = CurrentTime(); + TimeSpan maximum = TimeSpan.MaxValue; + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03, + but it was {Formatter.Format(subject)} + """); + } + } + } +} diff --git a/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.Nullable.IsBetween.Tests.cs b/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.Nullable.IsBetween.Tests.cs new file mode 100644 index 000000000..0c8df20ad --- /dev/null +++ b/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.Nullable.IsBetween.Tests.cs @@ -0,0 +1,257 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatTimeSpan +{ + public sealed partial class Nullable + { + public sealed class IsBetween + { + public sealed class Tests + { + [Fact] + public async Task WhenMaximumIsNull_ShouldFail() + { + TimeSpan? subject = CurrentTime(); + TimeSpan? minimum = subject; + TimeSpan? maximum = null; + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is between {Formatter.Format(minimum)} and , + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenMinimumIsNull_ShouldFail() + { + TimeSpan? subject = CurrentTime(); + TimeSpan? minimum = null; + TimeSpan? maximum = subject; + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is between and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectAndMaximumAreMaxValue_ShouldSucceed() + { + TimeSpan? subject = TimeSpan.MaxValue; + TimeSpan? minimum = CurrentTime(); + TimeSpan? maximum = TimeSpan.MaxValue; + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectAndMinimumAreMinValue_ShouldSucceed() + { + TimeSpan? subject = TimeSpan.MinValue; + TimeSpan? minimum = TimeSpan.MinValue; + TimeSpan? maximum = CurrentTime(); + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsBetweenMinimumAndMaximum_ShouldSucceed() + { + TimeSpan? subject = CurrentTime(); + TimeSpan? minimum = EarlierTime(); + TimeSpan? maximum = LaterTime(); + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsEarlierThanMinimum_ShouldFail() + { + TimeSpan? subject = EarlierTime(); + TimeSpan? minimum = CurrentTime(); + TimeSpan? maximum = LaterTime(); + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectIsLaterThanMaximum_ShouldFail() + { + TimeSpan? subject = LaterTime(); + TimeSpan? minimum = EarlierTime(); + TimeSpan? maximum = CurrentTime(); + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectIsSameAsMaximum_ShouldSucceed() + { + TimeSpan? subject = CurrentTime(); + TimeSpan? minimum = EarlierTime(); + TimeSpan? maximum = subject; + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsSameAsMinimum_ShouldSucceed() + { + TimeSpan? subject = CurrentTime(); + TimeSpan? minimum = subject; + TimeSpan? maximum = LaterTime(); + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum); + + await That(Act).DoesNotThrow(); + } + } + + public sealed class WithinTests + { + [Fact] + public async Task WhenMaximumValueIsOutsideTheTolerance_ShouldFail() + { + TimeSpan? subject = LaterTime(4); + TimeSpan minimum = TimeSpan.MinValue; + TimeSpan? maximum = CurrentTime(); + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenMinimumValueIsOutsideTheTolerance_ShouldFail() + { + TimeSpan? subject = EarlierTime(4); + TimeSpan? minimum = CurrentTime(); + TimeSpan maximum = TimeSpan.MaxValue; + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenNullableMaximumValueIsOutsideTheTolerance_ShouldFail() + { + TimeSpan? subject = CurrentTime(); + TimeSpan minimum = TimeSpan.MinValue; + TimeSpan? maximum = LaterTime(-4); + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenNullableMinimumValueIsOutsideTheTolerance_ShouldFail() + { + TimeSpan? subject = CurrentTime(); + TimeSpan? minimum = EarlierTime(-4); + TimeSpan maximum = TimeSpan.MaxValue; + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenValueIsWithinTheMaximumTolerance_ShouldSucceed() + { + TimeSpan? subject = LaterTime(3); + TimeSpan minimum = TimeSpan.MinValue; + TimeSpan? maximum = CurrentTime(); + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenValueIsWithinTheMinimumTolerance_ShouldSucceed() + { + TimeSpan? subject = EarlierTime(3); + TimeSpan? minimum = CurrentTime(); + TimeSpan maximum = TimeSpan.MaxValue; + + async Task Act() + => await That(subject).IsBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).DoesNotThrow(); + } + } + } + } +} diff --git a/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.Nullable.IsNotBetween.Tests.cs b/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.Nullable.IsNotBetween.Tests.cs new file mode 100644 index 000000000..f516fd884 --- /dev/null +++ b/Tests/aweXpect.Tests/TimeSpans/ThatTimeSpan.Nullable.IsNotBetween.Tests.cs @@ -0,0 +1,262 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatTimeSpan +{ + public sealed partial class Nullable + { + public sealed class IsNotBetween + { + public sealed class Tests + { + [Fact] + public async Task WhenMaximumIsNull_ShouldFail() + { + TimeSpan? subject = CurrentTime(); + TimeSpan? minimum = subject; + TimeSpan? maximum = null; + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between {Formatter.Format(minimum)} and , + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenMinimumIsNull_ShouldFail() + { + TimeSpan? subject = CurrentTime(); + TimeSpan? minimum = null; + TimeSpan? maximum = subject; + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectAndMaximumAreMaxValue_ShouldFail() + { + TimeSpan? subject = TimeSpan.MaxValue; + TimeSpan? minimum = CurrentTime(); + TimeSpan maximum = TimeSpan.MaxValue; + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectAndMinimumAreMinValue_ShouldFail() + { + TimeSpan? subject = TimeSpan.MinValue; + TimeSpan minimum = TimeSpan.MinValue; + TimeSpan? maximum = CurrentTime(); + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectIsEarlierThanMinimum_ShouldSucceed() + { + TimeSpan? subject = EarlierTime(); + TimeSpan? minimum = CurrentTime(); + TimeSpan? maximum = LaterTime(); + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsLaterThanMaximum_ShouldSucceed() + { + TimeSpan? subject = LaterTime(); + TimeSpan? minimum = EarlierTime(); + TimeSpan? maximum = CurrentTime(); + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsNotBetweenMinimumAndMaximum_ShouldFail() + { + TimeSpan? subject = CurrentTime(); + TimeSpan? minimum = EarlierTime(); + TimeSpan? maximum = LaterTime(); + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectIsSameAsMaximum_ShouldFail() + { + TimeSpan? subject = CurrentTime(); + TimeSpan? minimum = EarlierTime(); + TimeSpan? maximum = subject; + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectIsSameAsMinimum_ShouldFail() + { + TimeSpan? subject = CurrentTime(); + TimeSpan? minimum = subject; + TimeSpan? maximum = LaterTime(); + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)}, + but it was {Formatter.Format(subject)} + """); + } + } + + public sealed class WithinTests + { + [Fact] + public async Task WhenMaximumValueIsOutsideTheTolerance_ShouldSucceed() + { + TimeSpan? subject = CurrentTime(); + TimeSpan minimum = TimeSpan.MinValue; + TimeSpan? maximum = LaterTime(2); + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenMinimumValueIsOutsideTheTolerance_ShouldSucceed() + { + TimeSpan? subject = CurrentTime(); + TimeSpan? minimum = EarlierTime(2); + TimeSpan maximum = TimeSpan.MaxValue; + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenNullableMaximumValueIsOutsideTheTolerance_ShouldSucceed() + { + TimeSpan? subject = CurrentTime(); + TimeSpan minimum = TimeSpan.MinValue; + TimeSpan? maximum = LaterTime(2); + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenNullableMinimumValueIsOutsideTheTolerance_ShouldSucceed() + { + TimeSpan? subject = CurrentTime(); + TimeSpan? minimum = EarlierTime(2); + TimeSpan maximum = TimeSpan.MaxValue; + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenValueIsWithinTheMaximumTolerance_ShouldFail() + { + TimeSpan? subject = EarlierTime(3); + TimeSpan minimum = TimeSpan.MinValue; + TimeSpan? maximum = CurrentTime(); + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenValueIsWithinTheMinimumTolerance_ShouldFail() + { + TimeSpan? subject = LaterTime(3); + TimeSpan? minimum = CurrentTime(); + TimeSpan maximum = TimeSpan.MaxValue; + + async Task Act() + => await That(subject).IsNotBetween(minimum).And(maximum) + .Within(3.Seconds()); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not between {Formatter.Format(minimum)} and {Formatter.Format(maximum)} ± 0:03, + but it was {Formatter.Format(subject)} + """); + } + } + } + } +}