Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add Should().NotThrowAfter assertion for actions #942

Merged
merged 23 commits into from Jan 11, 2019
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
0e8d5f8
Add wait time to NotThrow
frederik-h Sep 18, 2018
b7a41f8
Rename NotThrow overload with wait time
frederik-h Oct 8, 2018
2da5e3b
Changes requested in review of NotThrowAfter
frederik-h Oct 12, 2018
d83dd87
Minor variable naming and exception message change
frederik-h Oct 13, 2018
b46d678
Use portable method for sleeping in NotThrowAfter
frederik-h Oct 15, 2018
b6a22e1
Fix test name
frederik-h Nov 9, 2018
5d80597
Fix exception message in NotThrowAfter
frederik-h Nov 9, 2018
cdd339e
Revert "Use portable method for sleeping in NotThrowAfter"
frederik-h Nov 19, 2018
fe90171
Disable NotThrowAfter for netstandard < 2.0
frederik-h Nov 19, 2018
a8bdaf9
Merge remote-tracking branch 'upstream/master' into NotThrow-Wait
frederik-h Nov 19, 2018
a9aa045
Minor coding style changes
frederik-h Nov 22, 2018
cc58f1d
Remove code literal names from test names
frederik-h Nov 22, 2018
670d278
Minor code formatting fixes
frederik-h Nov 22, 2018
f1525e5
Add NotThrowAfter to FunctionAssertions
frederik-h Dec 7, 2018
f22fcdb
Add NotThrowAfter for Func<Task>
frederik-h Dec 20, 2018
bb1e1a0
Wrap assertion code of NotThrowAfter in local function
frederik-h Dec 30, 2018
36a850c
Increase wait times in NotThrowAfter tests
frederik-h Jan 1, 2019
e3004c2
Change way NowThrowAfterAsync calls the assertion task
frederik-h Jan 1, 2019
cfc76ca
Add documentation for NotThrowAfter
frederik-h Jan 5, 2019
40258c0
Increase wait time in NotThrowAfter-tests
frederik-h Jan 6, 2019
7da866c
Fixup whitespace
jnyrup Jan 7, 2019
ccfeeca
Merge pull request #1 from jnyrup/NotBeInDescendingOrder
frederik-h Jan 8, 2019
49ec86f
Change NotThrowAfter for Func<T> to return result
frederik-h Jan 9, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
64 changes: 63 additions & 1 deletion Src/FluentAssertions/Specialized/ActionAssertions.cs
Expand Up @@ -4,7 +4,9 @@
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;

#if !NETSTANDARD1_3 && !NETSTANDARD1_6
using System.Threading;
#endif
using FluentAssertions.Common;
using FluentAssertions.Execution;
using FluentAssertions.Primitives;
Expand Down Expand Up @@ -114,6 +116,66 @@ public void NotThrow(string because = "", params object[] becauseArgs)
}
}

#if !NETSTANDARD1_3 && !NETSTANDARD1_6
/// <summary>
/// Asserts that the current <see cref="Action"/> stops throwing any exception
/// after a specified amount of time.
/// </summary>
/// <remarks>
/// The <see cref="Action"/> 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.
/// </remarks>
/// <param name="waitTime">
/// The time after which the <see cref="Action"/> should have stopped throwing any exception.
/// </param>
/// <param name="pollInterval">
/// The time between subsequent invocations of the <see cref="Action"/>.
/// </param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <see cref="because" />.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">Throws if waitTime or pollInterval are negative.</exception>
public void NotThrowAfter(TimeSpan waitTime, TimeSpan pollInterval, string because = "", params object[] becauseArgs)
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
{
FailIfSubjectIsAsyncVoid();
jnyrup marked this conversation as resolved.
Show resolved Hide resolved

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;
var watch = Stopwatch.StartNew();

while (invocationEndTime is null || invocationEndTime < waitTime)
{
exception = InvokeSubjectWithInterception();
if (exception is null)
frederik-h marked this conversation as resolved.
Show resolved Hide resolved
{
return;
}

Thread.Sleep(pollInterval);
invocationEndTime = watch.Elapsed;
}

Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith("Did not expect any exceptions after {0}{reason}, but found {1}.", waitTime, exception);
}
#endif

private Exception InvokeSubjectWithInterception()
{
Exception actualException = null;
Expand Down
116 changes: 116 additions & 0 deletions Src/FluentAssertions/Specialized/AsyncFunctionAssertions.cs
Expand Up @@ -174,6 +174,122 @@ private static void NotThrow(Exception exception, string because, object[] becau
}
}

/// <summary>
/// Asserts that the current <see cref="Func{T}"/> stops throwing any exception
/// after a specified amount of time.
/// </summary>
/// <remarks>
/// The <see cref="Func{T}"/> 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.
/// </remarks>
/// <param name="waitTime">
/// The time after which the <see cref="Func{T}"/> should have stopped throwing any exception.
/// </param>
/// <param name="pollInterval">
/// The time between subsequent invocations of the <see cref="Func{T}"/>.
/// </param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <see cref="because" />.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">Throws if waitTime or pollInterval are negative.</exception>
public void 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.");
}

TimeSpan? invocationEndTime = null;
Exception exception = null;
var watch = Stopwatch.StartNew();

while (invocationEndTime is null || invocationEndTime < waitTime)
{
exception = InvokeSubjectWithInterception();
if (exception is null)
{
return;
}
Task.Delay(pollInterval).Wait();
invocationEndTime = watch.Elapsed;
}
Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith("Did not expect any exceptions after {0}{reason}, but found {1}.", waitTime, exception);
}

/// <summary>
/// Asserts that the current <see cref="Func{T}"/> stops throwing any exception
/// after a specified amount of time.
/// </summary>
/// <remarks>
/// The <see cref="Func{T}"/> 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.
/// </remarks>
/// <param name="waitTime">
/// The time after which the <see cref="Func{T}"/> should have stopped throwing any exception.
/// </param>
/// <param name="pollInterval">
/// The time between subsequent invocations of the <see cref="Func{T}"/>.
/// </param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <see cref="because" />.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">Throws if waitTime or pollInterval are negative.</exception>
public
Task 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.");
}

return assertionTask();

async Task assertionTask()
{
TimeSpan? invocationEndTime = null;
Exception exception = null;
var watch = Stopwatch.StartNew();

while (invocationEndTime is null || invocationEndTime < waitTime)
{
exception = await InvokeSubjectWithInterceptionAsync();
if (exception is null)
{
return;
}

await Task.Delay(pollInterval);
invocationEndTime = watch.Elapsed;
}

Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith("Did not expect any exceptions after {0}{reason}, but found {1}.", waitTime, exception);
}
}

private static Exception GetFirstNonAggregateException(Exception exception)
{
Exception nonAggregateException = exception;
Expand Down
62 changes: 61 additions & 1 deletion Src/FluentAssertions/Specialized/FunctionAssertions.cs
@@ -1,7 +1,9 @@
using System;
using System.Diagnostics;
using System.Linq;

#if !NETSTANDARD1_3 && !NETSTANDARD1_6
using System.Threading;
#endif
using FluentAssertions.Execution;
using FluentAssertions.Primitives;

Expand Down Expand Up @@ -130,6 +132,64 @@ private static void NotThrow(Exception exception, string because, object[] becau
}
}

#if !NETSTANDARD1_3 && !NETSTANDARD1_6
/// <summary>
/// Asserts that the current <see cref="Func{T}"/> stops throwing any exception
/// after a specified amount of time.
/// </summary>
/// <remarks>
/// The <see cref="Func{T}"/> 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.
/// </remarks>
/// <param name="waitTime">
/// The time after which the <see cref="Func{T}"/> should have stopped throwing any exception.
/// </param>
/// <param name="pollInterval">
/// The time between subsequent invocations of the <see cref="Func{T}"/>.
/// </param>
/// <param name="because">
/// A formatted phrase as is supported by <see cref="string.Format(string,object[])" /> explaining why the assertion
/// is needed. If the phrase does not start with the word <i>because</i>, it is prepended automatically.
/// </param>
/// <param name="becauseArgs">
/// Zero or more objects to format using the placeholders in <see cref="because" />.
/// </param>
/// <exception cref="ArgumentOutOfRangeException">Throws if waitTime or pollInterval are negative.</exception>
public void 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.");
}

TimeSpan? invocationEndTime = null;
Exception exception = null;
var watch = Stopwatch.StartNew();

while (invocationEndTime is null || invocationEndTime < waitTime)
{
exception = InvokeSubjectWithInterception();
if (exception is null)
{
return;
}

Thread.Sleep(pollInterval);
invocationEndTime = watch.Elapsed;
}

Execute.Assertion
.BecauseOf(because, becauseArgs)
.FailWith("Did not expect any exceptions after {0}{reason}, but found {1}.", waitTime, exception);
}
#endif

private static Exception GetFirstNonAggregateException(Exception exception)
{
Exception nonAggregateException = exception;
Expand Down