-
-
Notifications
You must be signed in to change notification settings - Fork 13
feat: support async wait handlers #942
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
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
fa8fc34
feat: support async wait handlers
vbreuss ef00753
Fix review issues
vbreuss 0064039
Add `ToAsyncEnumerable` extension methods for .NET6 or higher
vbreuss 0290806
Remove obsolete attribute
vbreuss be6e3d8
Fix sonar issues about missing use of CancellationToken in tests
vbreuss ffca77d
Reduce change surface
vbreuss 955bbb7
Add missing tests
vbreuss File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
121 changes: 121 additions & 0 deletions
121
Source/Testably.Abstractions.Testing/AwaitableCallbackExtensions.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| using System; | ||
| using System.Threading; | ||
| using System.Threading.Tasks; | ||
| #if NET6_0_OR_GREATER | ||
| using System.Collections.Generic; | ||
| using System.Runtime.CompilerServices; | ||
| #endif | ||
|
|
||
| namespace Testably.Abstractions.Testing; | ||
|
|
||
| /// <summary> | ||
| /// Extension methods on <see cref="IAwaitableCallback{TValue}" />. | ||
| /// </summary> | ||
| public static class AwaitableCallbackExtensions | ||
| { | ||
| /// <summary> | ||
| /// Blocks the current thread until the callback is executed. | ||
| /// <para /> | ||
| /// Throws a <see cref="TimeoutException" /> if the <paramref name="timeout" /> expired before the callback was | ||
| /// executed. | ||
| /// </summary> | ||
| /// <param name="callback">The callback.</param> | ||
| /// <param name="count"> | ||
| /// (optional) The number of callbacks to wait.<br /> | ||
| /// Defaults to <c>1</c> | ||
| /// </param> | ||
| /// <param name="timeout"> | ||
| /// (optional) The timeout in milliseconds to wait on the callback.<br /> | ||
| /// Defaults to <c>30000</c>ms (30 seconds). | ||
| /// </param> | ||
| public static TValue[] Wait<TValue>(this IAwaitableCallback<TValue> callback, | ||
| int count = 1, | ||
| int timeout = 30000) | ||
| => callback.Wait(count, TimeSpan.FromMilliseconds(timeout)); | ||
|
|
||
| /// <summary> | ||
| /// Waits asynchronously until the callback is executed. | ||
| /// </summary> | ||
| /// <param name="callback">The callback.</param> | ||
| /// <param name="count"> | ||
| /// (optional) The number of callbacks to wait.<br /> | ||
| /// Defaults to <c>1</c> | ||
| /// </param> | ||
| /// <param name="timeout"> | ||
| /// (optional) The timeout in milliseconds to wait on the callback.<br /> | ||
| /// Defaults to <c>30000</c>ms (30 seconds). | ||
| /// </param> | ||
| /// <param name="cancellationToken"> | ||
| /// (optional) A <see cref="CancellationToken" /> to cancel waiting.<br /> | ||
| /// Throws a <see cref="OperationCanceledException" /> if the token was canceled before the callback was executed. | ||
| /// </param> | ||
| public static Task<TValue[]> WaitAsync<TValue>(this IAwaitableCallback<TValue> callback, | ||
| int count = 1, | ||
| int timeout = 30000, | ||
| CancellationToken? cancellationToken = null) | ||
| => callback.WaitAsync(count, TimeSpan.FromMilliseconds(timeout), cancellationToken); | ||
|
|
||
| #if NET6_0_OR_GREATER | ||
| /// <summary> | ||
| /// Converts the <see cref="IAwaitableCallback{TValue}" /> to an <see cref="IAsyncEnumerable{TValue}" /> that yields a | ||
| /// value each time the callback is executed. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// Uses a default timeout of 30 seconds to prevent infinite waiting if the callback is never executed. | ||
| /// </remarks> | ||
| public static IAsyncEnumerable<TValue> ToAsyncEnumerable<TValue>( | ||
| this IAwaitableCallback<TValue> source, | ||
| CancellationToken cancellationToken = default) | ||
| => ToAsyncEnumerable(source, null, cancellationToken); | ||
|
|
||
| /// <summary> | ||
| /// Converts the <see cref="IAwaitableCallback{TValue}" /> to an <see cref="IAsyncEnumerable{TValue}" /> that yields a | ||
| /// value each time the callback is executed. | ||
| /// </summary> | ||
| /// <remarks> | ||
| /// If no <paramref name="timeout" /> is specified (<see langword="null" />), a default timeout of 30 seconds is used | ||
| /// to prevent infinite waiting if the callback is never executed. | ||
| /// </remarks> | ||
| public static async IAsyncEnumerable<TValue> ToAsyncEnumerable<TValue>( | ||
| this IAwaitableCallback<TValue> source, | ||
| TimeSpan? timeout, | ||
| [EnumeratorCancellation] CancellationToken cancellationToken = default) | ||
| { | ||
| using CancellationTokenSource cts = | ||
| CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); | ||
| cts.CancelAfter(timeout ?? TimeSpan.FromSeconds(30)); | ||
| CancellationToken token = cts.Token; | ||
|
|
||
| while (!token.IsCancellationRequested) | ||
| { | ||
| TValue item; | ||
| try | ||
| { | ||
| TValue[] items = await source.WaitAsync(cancellationToken: token); | ||
| if (items.Length == 0) | ||
| { | ||
| continue; | ||
| } | ||
|
|
||
| item = items[0]; | ||
| } | ||
| catch (OperationCanceledException) | ||
| { | ||
| yield break; | ||
| } | ||
|
|
||
| yield return item; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Converts the <see cref="IAwaitableCallback{TValue}" /> to an <see cref="IAsyncEnumerable{TValue}" /> that yields a | ||
| /// value each time the callback is executed. | ||
| /// </summary> | ||
| public static IAsyncEnumerable<TValue> ToAsyncEnumerable<TValue>( | ||
| this IAwaitableCallback<TValue> source, | ||
| int timeout, | ||
| CancellationToken cancellationToken = default) | ||
| => ToAsyncEnumerable(source, TimeSpan.FromMilliseconds(timeout), cancellationToken); | ||
| #endif | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.