diff --git a/Src/FluentAssertions/AssertionExtensions.cs b/Src/FluentAssertions/AssertionExtensions.cs index 7f156b9b96..1e4b5a1607 100644 --- a/Src/FluentAssertions/AssertionExtensions.cs +++ b/Src/FluentAssertions/AssertionExtensions.cs @@ -71,7 +71,7 @@ public static Func Awaiting(this T subject, Func action) /// /// Invokes the specified action on a subject so that you can chain it - /// with any of the assertions from + /// with any of the assertions from /// [Pure] public static Func Awaiting(this T subject, Func action) @@ -81,7 +81,7 @@ public static Func Awaiting(this T subject, Func action) /// /// Invokes the specified action on a subject so that you can chain it - /// with any of the assertions from + /// with any of the assertions from /// [Pure] public static Func> Awaiting(this T subject, Func> action) diff --git a/Src/FluentAssertions/Specialized/ActionAssertions.cs b/Src/FluentAssertions/Specialized/ActionAssertions.cs index 5bf42ac934..d3f367e5cd 100644 --- a/Src/FluentAssertions/Specialized/ActionAssertions.cs +++ b/Src/FluentAssertions/Specialized/ActionAssertions.cs @@ -8,7 +8,7 @@ namespace FluentAssertions.Specialized /// Contains a number of methods to assert that an yields the expected result. /// [DebuggerNonUserCode] - public class ActionAssertions : DelegateAssertions + public class ActionAssertions : DelegateAssertions { public ActionAssertions(Action subject, IExtractExceptions extractor) : this(subject, extractor, new Clock()) { @@ -16,14 +16,8 @@ public ActionAssertions(Action subject, IExtractExceptions extractor) : this(sub public ActionAssertions(Action subject, IExtractExceptions extractor, IClock clock) : base(subject, extractor, clock) { - Subject = subject; } - /// - /// Gets the that is being asserted. - /// - public new Action Subject { get; } - protected override void InvokeSubject() { Subject(); diff --git a/Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs b/Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs index 36500864e1..aa350ebec3 100644 --- a/Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs +++ b/Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs @@ -11,22 +11,18 @@ namespace FluentAssertions.Specialized /// Contains a number of methods to assert that an asynchronous method yields the expected result. /// [DebuggerNonUserCode] - public class AsyncFunctionAssertions : DelegateAssertions> + public class AsyncFunctionAssertions : DelegateAssertions, TAssertions> + where TTask : Task + where TAssertions : AsyncFunctionAssertions { - public AsyncFunctionAssertions(Func subject, IExtractExceptions extractor) : this(subject, extractor, new Clock()) + public AsyncFunctionAssertions(Func subject, IExtractExceptions extractor) : this(subject, extractor, new Clock()) { } - public AsyncFunctionAssertions(Func subject, IExtractExceptions extractor, IClock clock) : base(subject, extractor, clock) + public AsyncFunctionAssertions(Func subject, IExtractExceptions extractor, IClock clock) : base(subject, extractor, clock) { - Subject = subject; } - /// - /// Gets the that is being asserted. - /// - public new Func Subject { get; } - protected override string Identifier => "async function"; private protected override bool CanHandleAsync => true; @@ -36,6 +32,77 @@ protected override void InvokeSubject() Subject.ExecuteInDefaultSynchronizationContext().GetAwaiter().GetResult(); } + /// + /// Asserts that the current will complete within specified time. + /// + /// The allowed time span for the operation. + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + public AndConstraint CompleteWithin( + TimeSpan timeSpan, string because = "", params object[] becauseArgs) + { + Execute.Assertion + .ForCondition(Subject is object) + .BecauseOf(because, becauseArgs) + .FailWith("Expected {context:task} to complete within {0}{reason}, but found .", timeSpan); + + TTask task = Subject.ExecuteInDefaultSynchronizationContext(); + bool completed = Clock.Wait(task, timeSpan); + + Execute.Assertion + .ForCondition(completed) + .BecauseOf(because, becauseArgs) + .FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan); + + return new AndConstraint((TAssertions)this); + } + + /// + /// Asserts that the current will complete within the specified time. + /// + /// The allowed time span for the operation. + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + public async Task> CompleteWithinAsync( + TimeSpan timeSpan, string because = "", params object[] becauseArgs) + { + Execute.Assertion + .ForCondition(Subject is object) + .BecauseOf(because, becauseArgs) + .FailWith("Expected {context:task} to complete within {0}{reason}, but found .", timeSpan); + + using (var timeoutCancellationTokenSource = new CancellationTokenSource()) + { + TTask task = Subject.ExecuteInDefaultSynchronizationContext(); + + Task completedTask = + await Task.WhenAny(task, Clock.DelayAsync(timeSpan, timeoutCancellationTokenSource.Token)); + + if (completedTask == task) + { + timeoutCancellationTokenSource.Cancel(); + await completedTask; + } + + Execute.Assertion + .ForCondition(completedTask == task) + .BecauseOf(because, becauseArgs) + .FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan); + + return new AndConstraint((TAssertions)this); + } + } + /// /// Asserts that the current throws an exception of the exact type (and not a derived exception type). /// @@ -108,7 +175,7 @@ protected override void InvokeSubject() /// /// Zero or more objects to format using the placeholders in . /// - public async Task NotThrowAsync(string because = "", params object[] becauseArgs) + public async Task> NotThrowAsync(string because = "", params object[] becauseArgs) { Execute.Assertion .ForCondition(Subject is object) @@ -123,6 +190,8 @@ public async Task NotThrowAsync(string because = "", params object[] becauseArgs { NotThrow(exception, because, becauseArgs); } + + return new AndConstraint((TAssertions)this); } /// @@ -135,7 +204,7 @@ public async Task NotThrowAsync(string because = "", params object[] becauseArgs /// /// Zero or more objects to format using the placeholders in . /// - public async Task NotThrowAsync(string because = "", params object[] becauseArgs) + public async Task> NotThrowAsync(string because = "", params object[] becauseArgs) where TException : Exception { Execute.Assertion @@ -151,6 +220,8 @@ public async Task NotThrowAsync(string because = "", params object[] { NotThrow(exception, because, becauseArgs); } + + return new AndConstraint((TAssertions)this); } /// @@ -176,7 +247,7 @@ public async Task NotThrowAsync(string because = "", params object[] /// Zero or more objects to format using the placeholders in . /// /// Throws if waitTime or pollInterval are negative. - public Task NotThrowAfterAsync(TimeSpan waitTime, TimeSpan pollInterval, string because = "", params object[] becauseArgs) + public Task> NotThrowAfterAsync(TimeSpan waitTime, TimeSpan pollInterval, string because = "", params object[] becauseArgs) { if (waitTime < TimeSpan.Zero) { @@ -198,7 +269,7 @@ public Task NotThrowAfterAsync(TimeSpan waitTime, TimeSpan pollInterval, string return assertionTask(); - async Task assertionTask() + async Task> assertionTask() { TimeSpan? invocationEndTime = null; Exception exception = null; @@ -209,7 +280,7 @@ async Task assertionTask() exception = await InvokeWithInterceptionAsync(wrappedSubject); if (exception is null) { - return; + return new AndConstraint((TAssertions)this); } await Clock.DelayAsync(pollInterval, CancellationToken.None); @@ -219,6 +290,8 @@ async Task assertionTask() Execute.Assertion .BecauseOf(because, becauseArgs) .FailWith("Did not expect any exceptions after {0}{reason}, but found {1}.", waitTime, exception); + + return new AndConstraint((TAssertions)this); } } diff --git a/Src/FluentAssertions/Specialized/DelegateAssertions.cs b/Src/FluentAssertions/Specialized/DelegateAssertions.cs index 936d69b3c4..0042ca3608 100644 --- a/Src/FluentAssertions/Specialized/DelegateAssertions.cs +++ b/Src/FluentAssertions/Specialized/DelegateAssertions.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; -using System.Threading.Tasks; using FluentAssertions.Common; using FluentAssertions.Execution; using FluentAssertions.Primitives; @@ -12,7 +11,9 @@ namespace FluentAssertions.Specialized { [DebuggerNonUserCode] - public abstract class DelegateAssertions : ReferenceTypeAssertions> where TDelegate : Delegate + public abstract class DelegateAssertions : ReferenceTypeAssertions> + where TDelegate : Delegate + where TAssertions : DelegateAssertions { private readonly IExtractExceptions extractor; @@ -24,14 +25,8 @@ private protected DelegateAssertions(TDelegate @delegate, IExtractExceptions ext { this.extractor = extractor ?? throw new ArgumentNullException(nameof(extractor)); Clock = clock ?? throw new ArgumentNullException(nameof(clock)); - Subject = @delegate; } - /// - /// Gets the that is being asserted. - /// - public new TDelegate Subject { get; } - private protected IClock Clock { get; } private protected virtual bool CanHandleAsync => false; @@ -69,7 +64,7 @@ public ExceptionAssertions Throw(string because = "", pa /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint> NotThrow(string because = "", params object[] becauseArgs) + public AndConstraint NotThrow(string because = "", params object[] becauseArgs) where TException : Exception { Execute.Assertion @@ -92,7 +87,7 @@ public AndConstraint> NotThrow(string /// /// Zero or more objects to format using the placeholders in . /// - public AndConstraint> NotThrow(string because = "", params object[] becauseArgs) + public AndConstraint NotThrow(string because = "", params object[] becauseArgs) { Execute.Assertion .ForCondition(Subject is object) @@ -167,7 +162,7 @@ public AndConstraint> NotThrow(string because = "" /// Zero or more objects to format using the placeholders in . /// /// Throws if waitTime or pollInterval are negative. - public AndConstraint> NotThrowAfter(TimeSpan waitTime, TimeSpan pollInterval, string because = "", params object[] becauseArgs) + public AndConstraint NotThrowAfter(TimeSpan waitTime, TimeSpan pollInterval, string because = "", params object[] becauseArgs) { Execute.Assertion .ForCondition(Subject is object) @@ -206,7 +201,7 @@ public AndConstraint> NotThrowAfter(TimeSpan waitT .ForCondition(exception is null) .FailWith("Did not expect any exceptions after {0}{reason}, but found {1}.", waitTime, exception); - return new AndConstraint>(this); + return new AndConstraint((TAssertions)this); } protected ExceptionAssertions Throw(Exception exception, string because, object[] becauseArgs) @@ -231,17 +226,17 @@ protected ExceptionAssertions Throw(Exception exception, return new ExceptionAssertions(expectedExceptions); } - protected AndConstraint> NotThrow(Exception exception, string because, object[] becauseArgs) + protected AndConstraint NotThrow(Exception exception, string because, object[] becauseArgs) { Execute.Assertion .ForCondition(exception is null) .BecauseOf(because, becauseArgs) .FailWith("Did not expect any exception{reason}, but found {0}.", exception); - return new AndConstraint>(this); + return new AndConstraint((TAssertions)this); } - protected AndConstraint> NotThrow(Exception exception, string because, object[] becauseArgs) where TException : Exception + protected AndConstraint NotThrow(Exception exception, string because, object[] becauseArgs) where TException : Exception { IEnumerable exceptions = extractor.OfType(exception); Execute.Assertion @@ -249,7 +244,7 @@ protected AndConstraint> NotThrow(Exception except .BecauseOf(because, becauseArgs) .FailWith("Did not expect {0}{reason}, but found {1}.", typeof(TException), exception); - return new AndConstraint>(this); + return new AndConstraint((TAssertions)this); } protected abstract void InvokeSubject(); diff --git a/Src/FluentAssertions/Specialized/FunctionAssertionHelpers.cs b/Src/FluentAssertions/Specialized/FunctionAssertionHelpers.cs new file mode 100644 index 0000000000..5dc6da999a --- /dev/null +++ b/Src/FluentAssertions/Specialized/FunctionAssertionHelpers.cs @@ -0,0 +1,64 @@ +using System; +using FluentAssertions.Common; +using FluentAssertions.Execution; + +namespace FluentAssertions.Specialized +{ + internal class FunctionAssertionHelpers + { + internal static T NotThrow(Func subject, string because, object[] becauseArgs) + { + try + { + return subject(); + } + catch (Exception exception) + { + Execute.Assertion + .ForCondition(exception is null) + .BecauseOf(because, becauseArgs) + .FailWith("Did not expect any exception{reason}, but found {0}.", exception); + + return default; + } + } + + internal static TResult NotThrowAfter(Func subject, IClock clock, TimeSpan waitTime, TimeSpan pollInterval, string because, object[] becauseArgs) + { + if (waitTime < TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException(nameof(waitTime), $"The value of {nameof(waitTime)} must be non-negative."); + } + + if (pollInterval < TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException(nameof(pollInterval), $"The value of {nameof(pollInterval)} must be non-negative."); + } + + TimeSpan? invocationEndTime = null; + Exception exception = null; + ITimer timer = clock.StartTimer(); + + while (invocationEndTime is null || invocationEndTime < waitTime) + { + try + { + return subject(); + } + catch (Exception ex) + { + exception = ex; + } + + clock.Delay(pollInterval); + invocationEndTime = timer.Elapsed; + } + + Execute.Assertion + .BecauseOf(because, becauseArgs) + .FailWith("Did not expect any exceptions after {0}{reason}, but found {1}.", waitTime, exception); + + return default; + } + } +} diff --git a/Src/FluentAssertions/Specialized/FunctionAssertions.cs b/Src/FluentAssertions/Specialized/FunctionAssertions.cs index 45330b4e38..67bc499371 100644 --- a/Src/FluentAssertions/Specialized/FunctionAssertions.cs +++ b/Src/FluentAssertions/Specialized/FunctionAssertions.cs @@ -1,6 +1,5 @@ using System; using System.Diagnostics; -using System.Threading.Tasks; using FluentAssertions.Common; using FluentAssertions.Execution; @@ -10,7 +9,7 @@ namespace FluentAssertions.Specialized /// Contains a number of methods to assert that a synchronous function yields the expected result. /// [DebuggerNonUserCode] - public class FunctionAssertions : DelegateAssertions> + public class FunctionAssertions : DelegateAssertions, FunctionAssertions> { public FunctionAssertions(Func subject, IExtractExceptions extractor) : this(subject, extractor, new Clock()) { @@ -18,14 +17,8 @@ public FunctionAssertions(Func subject, IExtractExceptions extractor) : this( public FunctionAssertions(Func subject, IExtractExceptions extractor, IClock clock) : base(subject, extractor, clock) { - Subject = subject; } - /// - /// Gets the that is being asserted. - /// - public new Func Subject { get; } - protected override void InvokeSubject() { Subject(); @@ -46,20 +39,12 @@ protected override void InvokeSubject() public new AndWhichConstraint, T> NotThrow(string because = "", params object[] becauseArgs) { Execute.Assertion - .ForCondition(Subject is object) - .BecauseOf(because, becauseArgs) - .FailWith("Expected {context} not to throw{reason}, but found ."); + .ForCondition(Subject is object) + .BecauseOf(because, becauseArgs) + .FailWith("Expected {context} not to throw{reason}, but found ."); - try - { - T result = Subject(); - return new AndWhichConstraint, T>(this, result); - } - catch (Exception exception) - { - NotThrow(exception, because, becauseArgs); - return new AndWhichConstraint, T>(this, default(T)); - } + T result = FunctionAssertionHelpers.NotThrow(Subject, because, becauseArgs); + return new AndWhichConstraint, T>(this, result); } /// @@ -87,46 +72,13 @@ protected override void InvokeSubject() /// Throws if waitTime or pollInterval are negative. public new AndWhichConstraint, T> NotThrowAfter(TimeSpan waitTime, TimeSpan pollInterval, string because = "", params object[] becauseArgs) { - if (waitTime < TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException(nameof(waitTime), $"The value of {nameof(waitTime)} must be non-negative."); - } - - if (pollInterval < TimeSpan.Zero) - { - throw new ArgumentOutOfRangeException(nameof(pollInterval), $"The value of {nameof(pollInterval)} must be non-negative."); - } - Execute.Assertion .ForCondition(Subject is object) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} not to throw any exceptions after {0}{reason}, but found .", waitTime); - TimeSpan? invocationEndTime = null; - Exception exception = null; - ITimer timer = Clock.StartTimer(); - - while (invocationEndTime is null || invocationEndTime < waitTime) - { - try - { - T result = Subject(); - return new AndWhichConstraint, T>(this, result); - } - catch (Exception e) - { - exception = e; - } - - Clock.Delay(pollInterval); - invocationEndTime = timer.Elapsed; - } - - Execute.Assertion - .BecauseOf(because, becauseArgs) - .FailWith("Did not expect any exceptions after {0}{reason}, but found {1}.", waitTime, exception); - - return new AndWhichConstraint, T>(this, default(T)); + T result = FunctionAssertionHelpers.NotThrowAfter(Subject, Clock, waitTime, pollInterval, because, becauseArgs); + return new AndWhichConstraint, T>(this, result); } } } diff --git a/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs b/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs index 254e739a12..eb19f6f5fe 100644 --- a/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs +++ b/Src/FluentAssertions/Specialized/GenericAsyncFunctionAssertions.cs @@ -6,18 +6,19 @@ namespace FluentAssertions.Specialized { - public class GenericAsyncFunctionAssertions : AsyncFunctionAssertions + public class GenericAsyncFunctionAssertions : AsyncFunctionAssertions, GenericAsyncFunctionAssertions> { - private readonly Func> subject; - public GenericAsyncFunctionAssertions(Func> subject, IExtractExceptions extractor) : this(subject, extractor, new Clock()) { } - public GenericAsyncFunctionAssertions(Func> subject, IExtractExceptions extractor, IClock clock) : base( - subject, extractor, clock) + public GenericAsyncFunctionAssertions(Func> subject, IExtractExceptions extractor, IClock clock) : base(subject, extractor, clock) + { + } + + private new TResult InvokeSubject() { - this.subject = subject; + return Subject.ExecuteInDefaultSynchronizationContext().GetAwaiter().GetResult(); } /// @@ -31,15 +32,15 @@ public GenericAsyncFunctionAssertions(Func> subject, IExtractExcep /// /// Zero or more objects to format using the placeholders in . /// - public AndWhichConstraint, TResult> CompleteWithin( + public new AndWhichConstraint, TResult> CompleteWithin( TimeSpan timeSpan, string because = "", params object[] becauseArgs) { Execute.Assertion - .ForCondition(subject is object) + .ForCondition(Subject is object) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} to complete within {0}{reason}, but found .", timeSpan); - Task task = subject.ExecuteInDefaultSynchronizationContext(); + Task task = Subject.ExecuteInDefaultSynchronizationContext(); bool completed = Clock.Wait(task, timeSpan); Execute.Assertion @@ -61,17 +62,17 @@ public GenericAsyncFunctionAssertions(Func> subject, IExtractExcep /// /// Zero or more objects to format using the placeholders in . /// - public async Task, TResult>> CompleteWithinAsync( + public new async Task, TResult>> CompleteWithinAsync( TimeSpan timeSpan, string because = "", params object[] becauseArgs) { Execute.Assertion - .ForCondition(subject is object) + .ForCondition(Subject is object) .BecauseOf(because, becauseArgs) .FailWith("Expected {context} to complete within {0}{reason}, but found .", timeSpan); using (var timeoutCancellationTokenSource = new CancellationTokenSource()) { - Task task = subject.ExecuteInDefaultSynchronizationContext(); + Task task = Subject.ExecuteInDefaultSynchronizationContext(); Task completedTask = await Task.WhenAny(task, Clock.DelayAsync(timeSpan, timeoutCancellationTokenSource.Token)); @@ -90,5 +91,163 @@ public GenericAsyncFunctionAssertions(Func> subject, IExtractExcep return new AndWhichConstraint, TResult>(this, task.Result); } } + + /// + /// Asserts that the current does not throw any exception. + /// + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + public new AndWhichConstraint, TResult> NotThrow(string because = "", params object[] becauseArgs) + { + Execute.Assertion + .ForCondition(Subject is object) + .BecauseOf(because, becauseArgs) + .FailWith("Expected {context} not to throw{reason}, but found ."); + + TResult result = FunctionAssertionHelpers.NotThrow(InvokeSubject, because, becauseArgs); + return new AndWhichConstraint, TResult>(this, result); + } + + /// + /// Asserts that the current stops throwing any exception + /// after a specified amount of time. + /// + /// + /// The is invoked. If it raises an exception, + /// the invocation is repeated until it either stops raising any exceptions + /// or the specified wait time is exceeded. + /// + /// + /// The time after which the should have stopped throwing any exception. + /// + /// + /// The time between subsequent invocations of the . + /// + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + /// Throws if waitTime or pollInterval are negative. + public new AndWhichConstraint, TResult> NotThrowAfter(TimeSpan waitTime, TimeSpan pollInterval, string because = "", params object[] becauseArgs) + { + Execute.Assertion + .ForCondition(Subject is object) + .BecauseOf(because, becauseArgs) + .FailWith("Expected {context} not to throw any exceptions after {0}{reason}, but found .", waitTime); + + TResult result = FunctionAssertionHelpers.NotThrowAfter(InvokeSubject, Clock, waitTime, pollInterval, because, becauseArgs); + return new AndWhichConstraint, TResult>(this, result); + } + + /// + /// Asserts that the current does not throw any exception. + /// + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + public new async Task, TResult>> NotThrowAsync(string because = "", params object[] becauseArgs) + { + Execute.Assertion + .ForCondition(Subject is object) + .BecauseOf(because, becauseArgs) + .FailWith("Expected {context} not to throw{reason}, but found ."); + + try + { + TResult result = await Subject.ExecuteInDefaultSynchronizationContext(); + return new AndWhichConstraint, TResult>(this, result); + } + catch (Exception exception) + { + NotThrow(exception, because, becauseArgs); + return new AndWhichConstraint, TResult>(this, default(TResult)); + } + } + + /// + /// Asserts that the current stops throwing any exception + /// after a specified amount of time. + /// + /// + /// The is invoked. If it raises an exception, + /// the invocation is repeated until it either stops raising any exceptions + /// or the specified wait time is exceeded. + /// + /// + /// The time after which the should have stopped throwing any exception. + /// + /// + /// The time between subsequent invocations of the . + /// + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + /// Throws if waitTime or pollInterval are negative. + public new Task, TResult>> NotThrowAfterAsync(TimeSpan waitTime, TimeSpan pollInterval, string because = "", params object[] becauseArgs) + { + if (waitTime < TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException(nameof(waitTime), $"The value of {nameof(waitTime)} must be non-negative."); + } + + if (pollInterval < TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException(nameof(pollInterval), + $"The value of {nameof(pollInterval)} must be non-negative."); + } + + Execute.Assertion + .ForCondition(Subject is object) + .BecauseOf(because, becauseArgs) + .FailWith("Expected {context} not to throw any exceptions after {0}{reason}, but found .", waitTime); + + Func> wrappedSubject = Subject.ExecuteInDefaultSynchronizationContext; + + return assertionTask(); + + async Task, TResult>> assertionTask() + { + TimeSpan? invocationEndTime = null; + Exception exception = null; + ITimer timer = Clock.StartTimer(); + + while (invocationEndTime is null || invocationEndTime < waitTime) + { + try + { + TResult result = await wrappedSubject(); + return new AndWhichConstraint, TResult>(this, result); + } + catch (Exception ex) + { + exception = ex; + await Clock.DelayAsync(pollInterval, CancellationToken.None); + invocationEndTime = timer.Elapsed; + } + } + + Execute.Assertion + .BecauseOf(because, becauseArgs) + .FailWith("Did not expect any exceptions after {0}{reason}, but found {1}.", waitTime, exception); + + return new AndWhichConstraint, TResult>(this, default(TResult)); + } + } } } diff --git a/Src/FluentAssertions/Specialized/NonGenericAsyncFunctionAssertions.cs b/Src/FluentAssertions/Specialized/NonGenericAsyncFunctionAssertions.cs index 9d04d2f3fe..60ec8f5cb9 100644 --- a/Src/FluentAssertions/Specialized/NonGenericAsyncFunctionAssertions.cs +++ b/Src/FluentAssertions/Specialized/NonGenericAsyncFunctionAssertions.cs @@ -1,92 +1,17 @@ using System; -using System.Threading; using System.Threading.Tasks; using FluentAssertions.Common; -using FluentAssertions.Execution; namespace FluentAssertions.Specialized { - public class NonGenericAsyncFunctionAssertions : AsyncFunctionAssertions + public class NonGenericAsyncFunctionAssertions : AsyncFunctionAssertions { - public NonGenericAsyncFunctionAssertions(Func subject, IExtractExceptions extractor) : this(subject, - extractor, new Clock()) + public NonGenericAsyncFunctionAssertions(Func subject, IExtractExceptions extractor) : this(subject, extractor, new Clock()) { } - public NonGenericAsyncFunctionAssertions(Func subject, IExtractExceptions extractor, IClock clock) : base(subject, - extractor, clock) + public NonGenericAsyncFunctionAssertions(Func subject, IExtractExceptions extractor, IClock clock) : base(subject, extractor, clock) { } - - /// - /// Asserts that the current will complete within specified time. - /// - /// The allowed time span for the operation. - /// - /// A formatted phrase as is supported by explaining why the assertion - /// is needed. If the phrase does not start with the word because, it is prepended automatically. - /// - /// - /// Zero or more objects to format using the placeholders in . - /// - public AndConstraint CompleteWithin( - TimeSpan timeSpan, string because = "", params object[] becauseArgs) - { - Execute.Assertion - .ForCondition(Subject is object) - .BecauseOf(because, becauseArgs) - .FailWith("Expected {context:task} to complete within {0}{reason}, but found .", timeSpan); - - Task task = Subject.ExecuteInDefaultSynchronizationContext(); - bool completed = Clock.Wait(task, timeSpan); - - Execute.Assertion - .ForCondition(completed) - .BecauseOf(because, becauseArgs) - .FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan); - - return new AndConstraint(this); - } - - /// - /// Asserts that the current will complete within the specified time. - /// - /// The allowed time span for the operation. - /// - /// A formatted phrase as is supported by explaining why the assertion - /// is needed. If the phrase does not start with the word because, it is prepended automatically. - /// - /// - /// Zero or more objects to format using the placeholders in . - /// - public async Task> CompleteWithinAsync( - TimeSpan timeSpan, string because = "", params object[] becauseArgs) - { - Execute.Assertion - .ForCondition(Subject is object) - .BecauseOf(because, becauseArgs) - .FailWith("Expected {context:task} to complete within {0}{reason}, but found .", timeSpan); - - using (var timeoutCancellationTokenSource = new CancellationTokenSource()) - { - Task task = Subject.ExecuteInDefaultSynchronizationContext(); - - Task completedTask = - await Task.WhenAny(task, Clock.DelayAsync(timeSpan, timeoutCancellationTokenSource.Token)); - - if (completedTask == task) - { - timeoutCancellationTokenSource.Cancel(); - await completedTask; - } - - Execute.Assertion - .ForCondition(completedTask == task) - .BecauseOf(because, becauseArgs) - .FailWith("Expected {context:task} to complete within {0}{reason}.", timeSpan); - - return new AndConstraint(this); - } - } } } diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.approved.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.approved.txt index 5f28d7f5c4..6bf6c8fe00 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.approved.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.approved.txt @@ -1754,43 +1754,45 @@ namespace FluentAssertions.Reflection } namespace FluentAssertions.Specialized { - public class ActionAssertions : FluentAssertions.Specialized.DelegateAssertions + public class ActionAssertions : FluentAssertions.Specialized.DelegateAssertions { public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } - public System.Action Subject { get; } protected override void InvokeSubject() { } } - public class AsyncFunctionAssertions : FluentAssertions.Specialized.DelegateAssertions> + public class AsyncFunctionAssertions : FluentAssertions.Specialized.DelegateAssertions, TAssertions> + where TTask : System.Threading.Tasks.Task + where TAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { - public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } + public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } - public System.Func Subject { get; } + public FluentAssertions.AndConstraint CompleteWithin(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } protected override void InvokeSubject() { } - public System.Threading.Tasks.Task NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } - public System.Threading.Tasks.Task NotThrowAsync(string because = "", params object[] becauseArgs) { } - public System.Threading.Tasks.Task NotThrowAsync(string because = "", params object[] becauseArgs) + public System.Threading.Tasks.Task> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) where TException : System.Exception { } public System.Threading.Tasks.Task> ThrowAsync(string because = "", params object[] becauseArgs) where TException : System.Exception { } public System.Threading.Tasks.Task> ThrowExactlyAsync(string because = "", params object[] becauseArgs) where TException : System.Exception { } } - public abstract class DelegateAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions> + public abstract class DelegateAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions> where TDelegate : System.Delegate + where TAssertions : FluentAssertions.Specialized.DelegateAssertions { protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public new TDelegate Subject { get; } protected abstract void InvokeSubject(); - public FluentAssertions.AndConstraint> NotThrow(string because = "", params object[] becauseArgs) { } - protected FluentAssertions.AndConstraint> NotThrow(System.Exception exception, string because, object[] becauseArgs) { } - public FluentAssertions.AndConstraint> NotThrow(string because = "", params object[] becauseArgs) + public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) { } + protected FluentAssertions.AndConstraint NotThrow(System.Exception exception, string because, object[] becauseArgs) { } + public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) where TException : System.Exception { } - protected FluentAssertions.AndConstraint> NotThrow(System.Exception exception, string because, object[] becauseArgs) + protected FluentAssertions.AndConstraint NotThrow(System.Exception exception, string because, object[] becauseArgs) where TException : System.Exception { } - public FluentAssertions.AndConstraint> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndConstraint NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } public FluentAssertions.Specialized.ExceptionAssertions Throw(string because = "", params object[] becauseArgs) where TException : System.Exception { } protected FluentAssertions.Specialized.ExceptionAssertions Throw(System.Exception exception, string because, object[] becauseArgs) @@ -1828,22 +1830,25 @@ namespace FluentAssertions.Specialized public FluentAssertions.AndConstraint BeLessOrEqualTo(System.TimeSpan maxDuration, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeLessThan(System.TimeSpan maxDuration, string because = "", params object[] becauseArgs) { } } - public class FunctionAssertions : FluentAssertions.Specialized.DelegateAssertions> + public class FunctionAssertions : FluentAssertions.Specialized.DelegateAssertions, FluentAssertions.Specialized.FunctionAssertions> { public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } - public System.Func Subject { get; } protected override void InvokeSubject() { } public FluentAssertions.AndWhichConstraint, T> NotThrow(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint, T> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } } - public class GenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions + public class GenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions, FluentAssertions.Specialized.GenericAsyncFunctionAssertions> { public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } public FluentAssertions.AndWhichConstraint, TResult> CompleteWithin(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task, TResult>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndWhichConstraint, TResult> NotThrow(string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndWhichConstraint, TResult> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task, TResult>> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task, TResult>> NotThrowAsync(string because = "", params object[] becauseArgs) { } } public interface IExtractExceptions { @@ -1854,12 +1859,10 @@ namespace FluentAssertions.Specialized { public MemberExecutionTime(T subject, System.Linq.Expressions.Expression> action) { } } - public class NonGenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions + public class NonGenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } - public FluentAssertions.AndConstraint CompleteWithin(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } - public System.Threading.Tasks.Task> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } } public class TaskCompletionSourceAssertions { diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.approved.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.approved.txt index de2d6bdec6..0a6c928255 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.approved.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.approved.txt @@ -1754,43 +1754,45 @@ namespace FluentAssertions.Reflection } namespace FluentAssertions.Specialized { - public class ActionAssertions : FluentAssertions.Specialized.DelegateAssertions + public class ActionAssertions : FluentAssertions.Specialized.DelegateAssertions { public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } - public System.Action Subject { get; } protected override void InvokeSubject() { } } - public class AsyncFunctionAssertions : FluentAssertions.Specialized.DelegateAssertions> + public class AsyncFunctionAssertions : FluentAssertions.Specialized.DelegateAssertions, TAssertions> + where TTask : System.Threading.Tasks.Task + where TAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { - public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } + public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } - public System.Func Subject { get; } + public FluentAssertions.AndConstraint CompleteWithin(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } protected override void InvokeSubject() { } - public System.Threading.Tasks.Task NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } - public System.Threading.Tasks.Task NotThrowAsync(string because = "", params object[] becauseArgs) { } - public System.Threading.Tasks.Task NotThrowAsync(string because = "", params object[] becauseArgs) + public System.Threading.Tasks.Task> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) where TException : System.Exception { } public System.Threading.Tasks.Task> ThrowAsync(string because = "", params object[] becauseArgs) where TException : System.Exception { } public System.Threading.Tasks.Task> ThrowExactlyAsync(string because = "", params object[] becauseArgs) where TException : System.Exception { } } - public abstract class DelegateAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions> + public abstract class DelegateAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions> where TDelegate : System.Delegate + where TAssertions : FluentAssertions.Specialized.DelegateAssertions { protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public new TDelegate Subject { get; } protected abstract void InvokeSubject(); - public FluentAssertions.AndConstraint> NotThrow(string because = "", params object[] becauseArgs) { } - protected FluentAssertions.AndConstraint> NotThrow(System.Exception exception, string because, object[] becauseArgs) { } - public FluentAssertions.AndConstraint> NotThrow(string because = "", params object[] becauseArgs) + public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) { } + protected FluentAssertions.AndConstraint NotThrow(System.Exception exception, string because, object[] becauseArgs) { } + public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) where TException : System.Exception { } - protected FluentAssertions.AndConstraint> NotThrow(System.Exception exception, string because, object[] becauseArgs) + protected FluentAssertions.AndConstraint NotThrow(System.Exception exception, string because, object[] becauseArgs) where TException : System.Exception { } - public FluentAssertions.AndConstraint> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndConstraint NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } public FluentAssertions.Specialized.ExceptionAssertions Throw(string because = "", params object[] becauseArgs) where TException : System.Exception { } protected FluentAssertions.Specialized.ExceptionAssertions Throw(System.Exception exception, string because, object[] becauseArgs) @@ -1828,22 +1830,25 @@ namespace FluentAssertions.Specialized public FluentAssertions.AndConstraint BeLessOrEqualTo(System.TimeSpan maxDuration, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeLessThan(System.TimeSpan maxDuration, string because = "", params object[] becauseArgs) { } } - public class FunctionAssertions : FluentAssertions.Specialized.DelegateAssertions> + public class FunctionAssertions : FluentAssertions.Specialized.DelegateAssertions, FluentAssertions.Specialized.FunctionAssertions> { public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } - public System.Func Subject { get; } protected override void InvokeSubject() { } public FluentAssertions.AndWhichConstraint, T> NotThrow(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint, T> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } } - public class GenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions + public class GenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions, FluentAssertions.Specialized.GenericAsyncFunctionAssertions> { public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } public FluentAssertions.AndWhichConstraint, TResult> CompleteWithin(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task, TResult>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndWhichConstraint, TResult> NotThrow(string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndWhichConstraint, TResult> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task, TResult>> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task, TResult>> NotThrowAsync(string because = "", params object[] becauseArgs) { } } public interface IExtractExceptions { @@ -1854,12 +1859,10 @@ namespace FluentAssertions.Specialized { public MemberExecutionTime(T subject, System.Linq.Expressions.Expression> action) { } } - public class NonGenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions + public class NonGenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } - public FluentAssertions.AndConstraint CompleteWithin(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } - public System.Threading.Tasks.Task> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } } public class TaskCompletionSourceAssertions { diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.approved.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.approved.txt index 929f596d9e..d58ae06b33 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.approved.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.approved.txt @@ -1754,43 +1754,45 @@ namespace FluentAssertions.Reflection } namespace FluentAssertions.Specialized { - public class ActionAssertions : FluentAssertions.Specialized.DelegateAssertions + public class ActionAssertions : FluentAssertions.Specialized.DelegateAssertions { public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } - public System.Action Subject { get; } protected override void InvokeSubject() { } } - public class AsyncFunctionAssertions : FluentAssertions.Specialized.DelegateAssertions> + public class AsyncFunctionAssertions : FluentAssertions.Specialized.DelegateAssertions, TAssertions> + where TTask : System.Threading.Tasks.Task + where TAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { - public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } + public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } - public System.Func Subject { get; } + public FluentAssertions.AndConstraint CompleteWithin(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } protected override void InvokeSubject() { } - public System.Threading.Tasks.Task NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } - public System.Threading.Tasks.Task NotThrowAsync(string because = "", params object[] becauseArgs) { } - public System.Threading.Tasks.Task NotThrowAsync(string because = "", params object[] becauseArgs) + public System.Threading.Tasks.Task> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) where TException : System.Exception { } public System.Threading.Tasks.Task> ThrowAsync(string because = "", params object[] becauseArgs) where TException : System.Exception { } public System.Threading.Tasks.Task> ThrowExactlyAsync(string because = "", params object[] becauseArgs) where TException : System.Exception { } } - public abstract class DelegateAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions> + public abstract class DelegateAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions> where TDelegate : System.Delegate + where TAssertions : FluentAssertions.Specialized.DelegateAssertions { protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public new TDelegate Subject { get; } protected abstract void InvokeSubject(); - public FluentAssertions.AndConstraint> NotThrow(string because = "", params object[] becauseArgs) { } - protected FluentAssertions.AndConstraint> NotThrow(System.Exception exception, string because, object[] becauseArgs) { } - public FluentAssertions.AndConstraint> NotThrow(string because = "", params object[] becauseArgs) + public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) { } + protected FluentAssertions.AndConstraint NotThrow(System.Exception exception, string because, object[] becauseArgs) { } + public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) where TException : System.Exception { } - protected FluentAssertions.AndConstraint> NotThrow(System.Exception exception, string because, object[] becauseArgs) + protected FluentAssertions.AndConstraint NotThrow(System.Exception exception, string because, object[] becauseArgs) where TException : System.Exception { } - public FluentAssertions.AndConstraint> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndConstraint NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } public FluentAssertions.Specialized.ExceptionAssertions Throw(string because = "", params object[] becauseArgs) where TException : System.Exception { } protected FluentAssertions.Specialized.ExceptionAssertions Throw(System.Exception exception, string because, object[] becauseArgs) @@ -1828,22 +1830,25 @@ namespace FluentAssertions.Specialized public FluentAssertions.AndConstraint BeLessOrEqualTo(System.TimeSpan maxDuration, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeLessThan(System.TimeSpan maxDuration, string because = "", params object[] becauseArgs) { } } - public class FunctionAssertions : FluentAssertions.Specialized.DelegateAssertions> + public class FunctionAssertions : FluentAssertions.Specialized.DelegateAssertions, FluentAssertions.Specialized.FunctionAssertions> { public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } - public System.Func Subject { get; } protected override void InvokeSubject() { } public FluentAssertions.AndWhichConstraint, T> NotThrow(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint, T> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } } - public class GenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions + public class GenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions, FluentAssertions.Specialized.GenericAsyncFunctionAssertions> { public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } public FluentAssertions.AndWhichConstraint, TResult> CompleteWithin(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task, TResult>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndWhichConstraint, TResult> NotThrow(string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndWhichConstraint, TResult> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task, TResult>> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task, TResult>> NotThrowAsync(string because = "", params object[] becauseArgs) { } } public interface IExtractExceptions { @@ -1854,12 +1859,10 @@ namespace FluentAssertions.Specialized { public MemberExecutionTime(T subject, System.Linq.Expressions.Expression> action) { } } - public class NonGenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions + public class NonGenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } - public FluentAssertions.AndConstraint CompleteWithin(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } - public System.Threading.Tasks.Task> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } } public class TaskCompletionSourceAssertions { diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.approved.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.approved.txt index 083b6d3d40..313f6d9568 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.approved.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.approved.txt @@ -1710,43 +1710,45 @@ namespace FluentAssertions.Reflection } namespace FluentAssertions.Specialized { - public class ActionAssertions : FluentAssertions.Specialized.DelegateAssertions + public class ActionAssertions : FluentAssertions.Specialized.DelegateAssertions { public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } - public System.Action Subject { get; } protected override void InvokeSubject() { } } - public class AsyncFunctionAssertions : FluentAssertions.Specialized.DelegateAssertions> + public class AsyncFunctionAssertions : FluentAssertions.Specialized.DelegateAssertions, TAssertions> + where TTask : System.Threading.Tasks.Task + where TAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { - public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } + public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } - public System.Func Subject { get; } + public FluentAssertions.AndConstraint CompleteWithin(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } protected override void InvokeSubject() { } - public System.Threading.Tasks.Task NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } - public System.Threading.Tasks.Task NotThrowAsync(string because = "", params object[] becauseArgs) { } - public System.Threading.Tasks.Task NotThrowAsync(string because = "", params object[] becauseArgs) + public System.Threading.Tasks.Task> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) where TException : System.Exception { } public System.Threading.Tasks.Task> ThrowAsync(string because = "", params object[] becauseArgs) where TException : System.Exception { } public System.Threading.Tasks.Task> ThrowExactlyAsync(string because = "", params object[] becauseArgs) where TException : System.Exception { } } - public abstract class DelegateAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions> + public abstract class DelegateAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions> where TDelegate : System.Delegate + where TAssertions : FluentAssertions.Specialized.DelegateAssertions { protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public new TDelegate Subject { get; } protected abstract void InvokeSubject(); - public FluentAssertions.AndConstraint> NotThrow(string because = "", params object[] becauseArgs) { } - protected FluentAssertions.AndConstraint> NotThrow(System.Exception exception, string because, object[] becauseArgs) { } - public FluentAssertions.AndConstraint> NotThrow(string because = "", params object[] becauseArgs) + public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) { } + protected FluentAssertions.AndConstraint NotThrow(System.Exception exception, string because, object[] becauseArgs) { } + public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) where TException : System.Exception { } - protected FluentAssertions.AndConstraint> NotThrow(System.Exception exception, string because, object[] becauseArgs) + protected FluentAssertions.AndConstraint NotThrow(System.Exception exception, string because, object[] becauseArgs) where TException : System.Exception { } - public FluentAssertions.AndConstraint> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndConstraint NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } public FluentAssertions.Specialized.ExceptionAssertions Throw(string because = "", params object[] becauseArgs) where TException : System.Exception { } protected FluentAssertions.Specialized.ExceptionAssertions Throw(System.Exception exception, string because, object[] becauseArgs) @@ -1784,22 +1786,25 @@ namespace FluentAssertions.Specialized public FluentAssertions.AndConstraint BeLessOrEqualTo(System.TimeSpan maxDuration, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeLessThan(System.TimeSpan maxDuration, string because = "", params object[] becauseArgs) { } } - public class FunctionAssertions : FluentAssertions.Specialized.DelegateAssertions> + public class FunctionAssertions : FluentAssertions.Specialized.DelegateAssertions, FluentAssertions.Specialized.FunctionAssertions> { public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } - public System.Func Subject { get; } protected override void InvokeSubject() { } public FluentAssertions.AndWhichConstraint, T> NotThrow(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint, T> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } } - public class GenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions + public class GenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions, FluentAssertions.Specialized.GenericAsyncFunctionAssertions> { public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } public FluentAssertions.AndWhichConstraint, TResult> CompleteWithin(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task, TResult>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndWhichConstraint, TResult> NotThrow(string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndWhichConstraint, TResult> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task, TResult>> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task, TResult>> NotThrowAsync(string because = "", params object[] becauseArgs) { } } public interface IExtractExceptions { @@ -1810,12 +1815,10 @@ namespace FluentAssertions.Specialized { public MemberExecutionTime(T subject, System.Linq.Expressions.Expression> action) { } } - public class NonGenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions + public class NonGenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } - public FluentAssertions.AndConstraint CompleteWithin(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } - public System.Threading.Tasks.Task> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } } public class TaskCompletionSourceAssertions { diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.approved.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.approved.txt index 2323250471..5497f97ab4 100644 --- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.approved.txt +++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.approved.txt @@ -1754,43 +1754,45 @@ namespace FluentAssertions.Reflection } namespace FluentAssertions.Specialized { - public class ActionAssertions : FluentAssertions.Specialized.DelegateAssertions + public class ActionAssertions : FluentAssertions.Specialized.DelegateAssertions { public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public ActionAssertions(System.Action subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } - public System.Action Subject { get; } protected override void InvokeSubject() { } } - public class AsyncFunctionAssertions : FluentAssertions.Specialized.DelegateAssertions> + public class AsyncFunctionAssertions : FluentAssertions.Specialized.DelegateAssertions, TAssertions> + where TTask : System.Threading.Tasks.Task + where TAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { - public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } + public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } + public AsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } - public System.Func Subject { get; } + public FluentAssertions.AndConstraint CompleteWithin(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } protected override void InvokeSubject() { } - public System.Threading.Tasks.Task NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } - public System.Threading.Tasks.Task NotThrowAsync(string because = "", params object[] becauseArgs) { } - public System.Threading.Tasks.Task NotThrowAsync(string because = "", params object[] becauseArgs) + public System.Threading.Tasks.Task> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task> NotThrowAsync(string because = "", params object[] becauseArgs) where TException : System.Exception { } public System.Threading.Tasks.Task> ThrowAsync(string because = "", params object[] becauseArgs) where TException : System.Exception { } public System.Threading.Tasks.Task> ThrowExactlyAsync(string because = "", params object[] becauseArgs) where TException : System.Exception { } } - public abstract class DelegateAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions> + public abstract class DelegateAssertions : FluentAssertions.Primitives.ReferenceTypeAssertions> where TDelegate : System.Delegate + where TAssertions : FluentAssertions.Specialized.DelegateAssertions { protected DelegateAssertions(TDelegate @delegate, FluentAssertions.Specialized.IExtractExceptions extractor) { } - public new TDelegate Subject { get; } protected abstract void InvokeSubject(); - public FluentAssertions.AndConstraint> NotThrow(string because = "", params object[] becauseArgs) { } - protected FluentAssertions.AndConstraint> NotThrow(System.Exception exception, string because, object[] becauseArgs) { } - public FluentAssertions.AndConstraint> NotThrow(string because = "", params object[] becauseArgs) + public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) { } + protected FluentAssertions.AndConstraint NotThrow(System.Exception exception, string because, object[] becauseArgs) { } + public FluentAssertions.AndConstraint NotThrow(string because = "", params object[] becauseArgs) where TException : System.Exception { } - protected FluentAssertions.AndConstraint> NotThrow(System.Exception exception, string because, object[] becauseArgs) + protected FluentAssertions.AndConstraint NotThrow(System.Exception exception, string because, object[] becauseArgs) where TException : System.Exception { } - public FluentAssertions.AndConstraint> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndConstraint NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } public FluentAssertions.Specialized.ExceptionAssertions Throw(string because = "", params object[] becauseArgs) where TException : System.Exception { } protected FluentAssertions.Specialized.ExceptionAssertions Throw(System.Exception exception, string because, object[] becauseArgs) @@ -1828,22 +1830,25 @@ namespace FluentAssertions.Specialized public FluentAssertions.AndConstraint BeLessOrEqualTo(System.TimeSpan maxDuration, string because = "", params object[] becauseArgs) { } public FluentAssertions.AndConstraint BeLessThan(System.TimeSpan maxDuration, string because = "", params object[] becauseArgs) { } } - public class FunctionAssertions : FluentAssertions.Specialized.DelegateAssertions> + public class FunctionAssertions : FluentAssertions.Specialized.DelegateAssertions, FluentAssertions.Specialized.FunctionAssertions> { public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public FunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } protected override string Identifier { get; } - public System.Func Subject { get; } protected override void InvokeSubject() { } public FluentAssertions.AndWhichConstraint, T> NotThrow(string because = "", params object[] becauseArgs) { } public FluentAssertions.AndWhichConstraint, T> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } } - public class GenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions + public class GenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions, FluentAssertions.Specialized.GenericAsyncFunctionAssertions> { public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public GenericAsyncFunctionAssertions(System.Func> subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } public FluentAssertions.AndWhichConstraint, TResult> CompleteWithin(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } public System.Threading.Tasks.Task, TResult>> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndWhichConstraint, TResult> NotThrow(string because = "", params object[] becauseArgs) { } + public FluentAssertions.AndWhichConstraint, TResult> NotThrowAfter(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task, TResult>> NotThrowAfterAsync(System.TimeSpan waitTime, System.TimeSpan pollInterval, string because = "", params object[] becauseArgs) { } + public System.Threading.Tasks.Task, TResult>> NotThrowAsync(string because = "", params object[] becauseArgs) { } } public interface IExtractExceptions { @@ -1854,12 +1859,10 @@ namespace FluentAssertions.Specialized { public MemberExecutionTime(T subject, System.Linq.Expressions.Expression> action) { } } - public class NonGenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions + public class NonGenericAsyncFunctionAssertions : FluentAssertions.Specialized.AsyncFunctionAssertions { public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor) { } public NonGenericAsyncFunctionAssertions(System.Func subject, FluentAssertions.Specialized.IExtractExceptions extractor, FluentAssertions.Common.IClock clock) { } - public FluentAssertions.AndConstraint CompleteWithin(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } - public System.Threading.Tasks.Task> CompleteWithinAsync(System.TimeSpan timeSpan, string because = "", params object[] becauseArgs) { } } public class TaskCompletionSourceAssertions { diff --git a/Tests/FluentAssertions.Specs/Exceptions/AsyncFunctionExceptionAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Exceptions/AsyncFunctionExceptionAssertionSpecs.cs index 4d283e0f98..7cb1fc10b3 100644 --- a/Tests/FluentAssertions.Specs/Exceptions/AsyncFunctionExceptionAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Exceptions/AsyncFunctionExceptionAssertionSpecs.cs @@ -1127,7 +1127,7 @@ public void throw new ArgumentException("An exception was forced"); } - await Task.Delay(0); + await Task.Yield(); }; // Act @@ -1156,7 +1156,7 @@ public void When_no_exception_should_be_thrown_for_async_func_executed_with_wait { throw new ArgumentException("An exception was forced"); } - await Task.Delay(0); + await Task.Yield(); }; // Act @@ -1258,7 +1258,7 @@ public void When_no_exception_should_be_thrown_for_async_func_after_wait_time_bu throw new ArgumentException("An exception was forced"); } - await Task.Delay(0); + await Task.Yield(); }; // Act @@ -1288,7 +1288,7 @@ public void When_no_exception_should_be_thrown_for_async_func_after_wait_time_an throw new ArgumentException("An exception was forced"); } - await Task.Delay(0); + await Task.Yield(); }; // Act diff --git a/Tests/FluentAssertions.Specs/Exceptions/FunctionExceptionAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Exceptions/FunctionExceptionAssertionSpecs.cs index b2ea561be7..bb57cbe4ce 100644 --- a/Tests/FluentAssertions.Specs/Exceptions/FunctionExceptionAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Exceptions/FunctionExceptionAssertionSpecs.cs @@ -438,7 +438,7 @@ public void When_an_assertion_fails_on_NotThrow_succeeding_message_should_be_inc #region NotThrowAfter [Fact] - public void When_subject_it_null_it_should_throw() + public void When_subject_is_null_it_should_throw() { // Arrange var waitTime = 0.Milliseconds(); @@ -562,11 +562,11 @@ public void When_no_exception_should_be_thrown_after_wait_time_the_func_result_s }; // Act - AndWhichConstraint, int> act = - throwShorterThanWaitTime.Should(clock).NotThrowAfter(waitTime, pollInterval); + Action act = () => throwShorterThanWaitTime.Should(clock).NotThrowAfter(waitTime, pollInterval) + .Which.Should().Be(42); // Assert - act.Subject.Should().Be(42); + act.Should().NotThrow(); } [Fact] diff --git a/Tests/FluentAssertions.Specs/Specialized/TaskAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Specialized/TaskAssertionSpecs.cs index e6c0153532..ffdfe3c330 100644 --- a/Tests/FluentAssertions.Specs/Specialized/TaskAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Specialized/TaskAssertionSpecs.cs @@ -22,7 +22,7 @@ public void When_getting_the_subject_it_should_remain_unchanged() } [Fact] - public void When_subject_it_null_when_expecting_to_complete_it_should_throw() + public void When_subject_is_null_when_expecting_to_complete_it_should_throw() { // Arrange var timer = new FakeClock(); @@ -70,7 +70,7 @@ public void When_task_completes_slow_it_should_fail() } [Fact] - public void When_subject_it_null_when_expecting_to_complete_async_it_should_throw() + public void When_subject_is_null_when_expecting_to_complete_async_it_should_throw() { // Arrange var timer = new FakeClock(); diff --git a/Tests/FluentAssertions.Specs/Specialized/TaskOfTAssertionSpecs.cs b/Tests/FluentAssertions.Specs/Specialized/TaskOfTAssertionSpecs.cs index f091442d3f..b490149670 100644 --- a/Tests/FluentAssertions.Specs/Specialized/TaskOfTAssertionSpecs.cs +++ b/Tests/FluentAssertions.Specs/Specialized/TaskOfTAssertionSpecs.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using FluentAssertions.Execution; using FluentAssertions.Extensions; using Xunit; using Xunit.Sdk; @@ -8,8 +9,9 @@ namespace FluentAssertions.Specs { public class TaskOfTAssertionSpecs { + #region CompleteWithin [Fact] - public void When_subject_it_null_when_expecting_to_complete_it_should_throw() + public void When_subject_is_null_when_expecting_to_complete_it_should_throw() { // Arrange var timer = new FakeClock(); @@ -68,9 +70,11 @@ public void When_task_completes_slow_it_should_fail() // Assert action.Should().Throw(); } + #endregion + #region CompleteWithinAsync [Fact] - public void When_subject_it_null_when_expecting_to_complete_async_it_should_throw() + public void When_subject_is_null_when_expecting_to_complete_async_it_should_throw() { // Arrange var timer = new FakeClock(); @@ -129,5 +133,406 @@ public void When_task_completes_slow_async_it_should_fail() // Assert action.Should().Throw(); } + #endregion + + #region NotThrow + [Fact] + public void When_subject_is_null_when_not_expecting_an_exception_it_should_throw() + { + // Arrange + Func> action = null; + + // Act + Action testAction = () => action.Should().NotThrow("because we want to test the failure {0}", "message"); + + // Assert + testAction.Should().Throw() + .WithMessage("*because we want to test the failure message*found *"); + } + + [Fact] + public void When_method_throws_an_empty_AggregateException_it_should_fail() + { + // Arrange + Func> act = () => throw new AggregateException(); + + // Act + Action act2 = () => act.Should().NotThrow("because we want to test the failure {0}", "message"); + + // Assert + act2.Should().Throw() + .WithMessage("*because we want to test the failure message*"); + } + + [Fact] + public void When_task_does_not_throw_when_not_expecting_an_exception_it_should_succeed() + { + // Arrange + Func> action = () => Task.FromResult(42); + + // Act + Action testAction = () => action.Should().NotThrow() + .Which.Should().Be(42); + + // Assert + testAction.Should().NotThrow(); + } + #endregion + + #region NotThrowAsync + [Fact] + public void When_subject_is_null_when_expecting_to_not_throw_async_it_should_throw() + { + // Arrange + Func> action = null; + + // Act + Func testAction = () => action.Should().NotThrowAsync( + "because we want to test the failure {0}", "message"); + + // Assert + testAction.Should().Throw() + .WithMessage("*because we want to test the failure message*found *"); + } + + [Fact] + public void When_task_does_not_throw_async_it_should_succeed() + { + // Arrange + var timer = new FakeClock(); + var taskFactory = new TaskCompletionSource(); + + // Act + Func action = async () => + { + Func> func = () => taskFactory.Task; + + (await func.Should(timer).NotThrowAsync()) + .Which.Should().Be(42); + }; + + taskFactory.SetResult(42); + timer.Complete(); + + // Assert + action.Should().NotThrow(); + } + + [Fact] + public void When_task_throws_async_it_should_fail() + { + // Arrange + var timer = new FakeClock(); + var taskFactory = new TaskCompletionSource(); + + // Act + Func action = () => + { + Func> func = () => throw new AggregateException(); + + return func.Should(timer).NotThrowAsync(); + }; + + // Assert + action.Should().Throw(); + } + #endregion + + #region NotThrowAfter + [Fact] + public void When_subject_is_null_it_should_throw() + { + // Arrange + var waitTime = 0.Milliseconds(); + var pollInterval = 0.Milliseconds(); + Func> action = null; + + // Act + Action testAction = () => action.Should().NotThrowAfter( + waitTime, pollInterval, "because we want to test the failure {0}", "message"); + + // Assert + testAction.Should().Throw() + .WithMessage("*because we want to test the failure message*found *"); + } + + [Fact] + public void When_wait_time_is_negative_it_should_throw() + { + // Arrange + var waitTime = -1.Milliseconds(); + var pollInterval = 10.Milliseconds(); + Func> someFunc = () => Task.FromResult(42); + + // Act + Action action = () => + someFunc.Should().NotThrowAfter(waitTime, pollInterval); + + // Assert + action.Should().Throw() + .WithMessage("* value of waitTime must be non-negative*"); + } + + [Fact] + public void When_poll_interval_is_negative_it_should_throw() + { + // Arrange + var waitTime = 10.Milliseconds(); + var pollInterval = -1.Milliseconds(); + Func> someAction = () => Task.FromResult(42); + + // Act + Action action = () => + someAction.Should().NotThrowAfter(waitTime, pollInterval); + + // Assert + action.Should().Throw() + .WithMessage("* value of pollInterval must be non-negative*"); + } + + [Fact] + public void When_no_exception_should_be_thrown_after_wait_time_but_it_was_it_should_throw() + { + // Arrange + var clock = new FakeClock(); + var timer = clock.StartTimer(); + var waitTime = 100.Milliseconds(); + var pollInterval = 10.Milliseconds(); + + Func> throwLongerThanWaitTime = () => + { + if (timer.Elapsed <= waitTime.Multiply(1.5)) + { + throw new ArgumentException("An exception was forced"); + } + + return Task.FromResult(42); + }; + + // Act + Action action = () => + throwLongerThanWaitTime.Should(clock).NotThrowAfter(waitTime, pollInterval, "we passed valid arguments"); + + // Assert + action.Should().Throw() + .WithMessage("Did not expect any exceptions after 0.100s because we passed valid arguments*"); + } + + [Fact] + public void When_no_exception_should_be_thrown_after_wait_time_and_none_was_it_should_not_throw() + { + // Arrange + var clock = new FakeClock(); + var timer = clock.StartTimer(); + var waitTime = 100.Milliseconds(); + var pollInterval = 10.Milliseconds(); + + Func> throwShorterThanWaitTime = () => + { + if (timer.Elapsed <= waitTime.Divide(2)) + { + throw new ArgumentException("An exception was forced"); + } + + return Task.FromResult(42); + }; + + // Act + Action act = () => throwShorterThanWaitTime.Should(clock).NotThrowAfter(waitTime, pollInterval); + + // Assert + act.Should().NotThrow(); + } + + [Fact] + public void When_no_exception_should_be_thrown_after_wait_time_the_func_result_should_be_returned() + { + // Arrange + var clock = new FakeClock(); + var timer = clock.StartTimer(); + var waitTime = 100.Milliseconds(); + var pollInterval = 10.Milliseconds(); + + Func> throwShorterThanWaitTime = () => + { + if (timer.Elapsed <= waitTime.Divide(2)) + { + throw new ArgumentException("An exception was forced"); + } + + return Task.FromResult(42); + }; + + // Act + Action act = () => throwShorterThanWaitTime.Should(clock).NotThrowAfter(waitTime, pollInterval) + .Which.Should().Be(42); + + // Assert + act.Should().NotThrow(); + } + + [Fact] + public void When_an_assertion_fails_on_NotThrowAfter_succeeding_message_should_be_included() + { + // Arrange + var waitTime = TimeSpan.Zero; + var pollInterval = TimeSpan.Zero; + Func> throwingFunction = () => throw new Exception(); + + // Act + Action act = () => + { + using (new AssertionScope()) + { + throwingFunction.Should().NotThrowAfter(waitTime, pollInterval) + .And.BeNull(); + } + }; + + // Assert + act.Should().Throw() + .WithMessage( + "*Did not expect any exceptions after*" + + "*to be *" + ); + } + #endregion + + #region NotThrowAfterAsync + [Fact] + public void When_subject_is_null_and_expecting_to_not_throw_async_it_should_throw() + { + // Arrange + var waitTime = 0.Milliseconds(); + var pollInterval = 0.Milliseconds(); + Func> action = null; + + // Act + Func testAction = () => action.Should().NotThrowAfterAsync( + waitTime, pollInterval, "because we want to test the failure {0}", "message"); + + // Assert + testAction.Should().Throw() + .WithMessage("*because we want to test the failure message*found *"); + } + + [Fact] + public void When_wait_time_is_negative_and_expecting_to_not_throw_async_it_should_throw() + { + // Arrange + var waitTime = -1.Milliseconds(); + var pollInterval = 10.Milliseconds(); + + var asyncObject = new AsyncClass(); + Func> someFunc = () => asyncObject.ReturnTaskInt(); + + // Act + Func act = () => someFunc.Should().NotThrowAfterAsync(waitTime, pollInterval); + + // Assert + act.Should().Throw() + .WithMessage("* value of waitTime must be non-negative*"); + } + + [Fact] + public void When_poll_interval_is_negative_and_expecting_to_not_throw_async_it_should_throw() + { + // Arrange + var waitTime = 10.Milliseconds(); + var pollInterval = -1.Milliseconds(); + + var asyncObject = new AsyncClass(); + Func> someFunc = () => asyncObject.ReturnTaskInt(); + + // Act + Func act = () => someFunc.Should().NotThrowAfterAsync(waitTime, pollInterval); + + // Assert + act.Should().Throw() + .WithMessage("* value of pollInterval must be non-negative*"); + } + + [Fact] + public void When_no_exception_should_be_thrown_async_for_null_after_wait_time_it_should_throw() + { + // Arrange + var waitTime = 2.Seconds(); + var pollInterval = 10.Milliseconds(); + + Func> func = null; + + // Act + Func action = () => func.Should() + .NotThrowAfterAsync(waitTime, pollInterval, "we passed valid arguments"); + + // Assert + action.Should().Throw() + .WithMessage("*but found *"); + } + + [Fact] + public void When_no_exception_should_be_thrown_async_after_wait_time_but_it_was_it_should_throw() + { + // Arrange + var waitTime = 2.Seconds(); + var pollInterval = 10.Milliseconds(); + + var clock = new FakeClock(); + var timer = clock.StartTimer(); + clock.CompleteAfter(waitTime); + + Func> throwLongerThanWaitTime = async () => + { + if (timer.Elapsed <= waitTime.Multiply(1.5)) + { + throw new ArgumentException("An exception was forced"); + } + + await Task.Yield(); + return 42; + }; + + // Act + Func action = () => throwLongerThanWaitTime.Should(clock) + .NotThrowAfterAsync(waitTime, pollInterval, "we passed valid arguments"); + + // Assert + action.Should().Throw() + .WithMessage("Did not expect any exceptions after 2s because we passed valid arguments*"); + } + + [Fact] + public void When_no_exception_should_be_thrown_async_after_wait_time_and_none_was_it_should_not_throw() + { + // Arrange + var waitTime = 6.Seconds(); + var pollInterval = 10.Milliseconds(); + + var clock = new FakeClock(); + var timer = clock.StartTimer(); + clock.Delay(waitTime); + + Func> throwShorterThanWaitTime = async () => + { + if (timer.Elapsed <= waitTime.Divide(12)) + { + throw new ArgumentException("An exception was forced"); + } + + await Task.Yield(); + return 42; + }; + + // Act + Func act = async () => + { + (await throwShorterThanWaitTime.Should(clock).NotThrowAfterAsync(waitTime, pollInterval)) + .Which.Should().Be(42); + }; + + // Assert + act.Should().NotThrow(); + } + #endregion } } diff --git a/docs/_pages/releases.md b/docs/_pages/releases.md index eacdc814e8..8c767014b7 100644 --- a/docs/_pages/releases.md +++ b/docs/_pages/releases.md @@ -50,6 +50,7 @@ sidebar: * Aligned strings to be compared using `Ordinal[Ignorecase]` - [#1283](https://github.com/fluentassertions/fluentassertions/pull/1283). * Changed `AutoConversion` to convert using `CultureInfo.InvariantCulture` instead of `CultureInfo.CurrentCulture` - [#1283](https://github.com/fluentassertions/fluentassertions/pull/1283). * Renamed `StartWithEquivalent` and `EndWithEquivalent` to `StartWithEquivalentOf` and `EndWithEquivalentOf` to make the API for Equivalent methods consistent - [#1292](https://github.com/fluentassertions/fluentassertions/pull/1292). +* Changed return types of assertions on `Func` and `Func>` to support chaining - [#1289](https://github.com/fluentassertions/fluentassertions/pull/1289). ## 5.10.3 **Fixes**