// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Threading.Tasks; namespace Microsoft.AspNetCore.SignalR.Client.Tests { // Possibly useful as a general-purpose async testing helper? public class SyncPoint { private readonly TaskCompletionSource _atSyncPoint = new TaskCompletionSource(); private readonly TaskCompletionSource _continueFromSyncPoint = new TaskCompletionSource(); /// /// Waits for the code-under-test to reach . /// /// public Task WaitForSyncPoint() => _atSyncPoint.Task; /// /// Releases the code-under-test to continue past where it waited for . /// public void Continue() => _continueFromSyncPoint.TrySetResult(null); /// /// Used by the code-under-test to wait for the test code to sync up. /// /// /// This code will unblock and then block waiting for to be called. /// /// public Task WaitToContinue() { _atSyncPoint.TrySetResult(null); return _continueFromSyncPoint.Task; } public static Func Create(out SyncPoint syncPoint) { var handler = Create(1, out var syncPoints); syncPoint = syncPoints[0]; return handler; } /// /// Creates a re-entrant function that waits for sync points in sequence. /// /// The number of sync points to expect /// The objects that can be used to coordinate the sync point /// public static Func Create(int count, out SyncPoint[] syncPoints) { // Need to use a local so the closure can capture it. You can't use out vars in a closure. var localSyncPoints = new SyncPoint[count]; for (var i = 0; i < count; i += 1) { localSyncPoints[i] = new SyncPoint(); } syncPoints = localSyncPoints; var counter = 0; return () => { if (counter >= localSyncPoints.Length) { return Task.CompletedTask; } else { var syncPoint = localSyncPoints[counter]; counter += 1; return syncPoint.WaitToContinue(); } }; } } }