public void ReturnsCompletedTaskWhenBusy() { var longTask = new TaskCompletionSource <bool>(); bool operationWasRunWhenBusy = false; var asyncLoader = new AsyncLoaderTestImpl(); // Start an operation that will not finish until we say so asyncLoader.PerformAsyncOperation(token => longTask.Task); // Now try to start another operation // NOTE: It is important for the test that this task is guaranteed to not complete var task = asyncLoader.PerformAsyncOperation(token => { operationWasRunWhenBusy = true; return(TaskConstants <bool> .Never); }); Assert.That(task.IsCompleted); Assert.That(operationWasRunWhenBusy, Is.False); // TODO: Should the returned task actually be faulted or cancelled instead? Assert.That(task.Status, Is.EqualTo(TaskStatus.RanToCompletion)); // Cleanup - is this needed? asyncLoader.Cancel(); longTask.SetResult(true); }
public void NotifiesOfSpecialOperationCompletedOnThreadDecidedByEventContext() { // Need to marshal thread ids back to main thread, we can use TCS for that var schedulerThreadId = new TaskCompletionSource <int>(); var notificationThreadId = new TaskCompletionSource <int>(); // Create a task scheduler that uses a single thread that reports its id at start var scheduler = CreateSingleThreadTaskScheduler(threadInit: () => schedulerThreadId.SetResult(GetCurrentThreadId())); var asyncLoader = new AsyncLoaderTestImpl(new TaskSchedulerSynchronizationContext(scheduler)); asyncLoader.AsyncOperationCompletedTunnel += (s, e) => { notificationThreadId.SetResult(GetCurrentThreadId()); }; asyncLoader.NotifySpecialOperationCompletedTunnel(true); bool finished = Task.WaitAll(new[] { schedulerThreadId.Task, notificationThreadId.Task }, TimeSpan.FromSeconds(5)); Assert.That(finished, Is.True); // Sanity check Assert.That(schedulerThreadId.Task.Result, Is.Not.EqualTo(GetCurrentThreadId())); // Handlers must be notified on scheduler thread Assert.That(notificationThreadId.Task.Result, Is.EqualTo(schedulerThreadId.Task.Result)); }
public void RunsOperation() { var operation = Substitute.For <Func <CancellationToken, Task <bool> > >(); var asyncLoader = new AsyncLoaderTestImpl(); asyncLoader.PerformAsyncOperation(operation); operation.Received().Invoke(Arg.Any <CancellationToken>()); }
public void RunsPostProcessStepWithResultFromOperation() { var postProcess = Substitute.For <Func <bool, CancellationToken, bool> >(); var asyncLoader = new AsyncLoaderTestImpl(); // Now we check with a task that returns _true_ asyncLoader.PerformAsyncOperation(tok => Task.FromResult(true), postProcess); postProcess.Received().Invoke(true, Arg.Any <CancellationToken>()); }
public void DoesNotReportAsyncOperationCompletedWhenOperationCancelled() { var completedHandler = Substitute.For <EventHandler <bool> >(); var asyncLoader = new AsyncLoaderTestImpl(new RunInlineSynchronizationContext()); asyncLoader.AsyncOperationCompletedTunnel += completedHandler; asyncLoader.PerformAsyncOperation(token => TaskConstants <bool> .Canceled); completedHandler.DidNotReceive().Invoke(Arg.Any <object>(), Arg.Any <bool>()); }
public void ReturnsTaskThatFailsIfOperationFails() { var exception = new Exception(); var asyncLoader = new AsyncLoaderTestImpl(); var task = asyncLoader.PerformAsyncOperation(token => FromException(exception)); Assert.That(task.IsFaulted, Is.True); // Exception will always be wrapped in an AggregateException Assert.That(task.Exception.InnerException, Is.EqualTo(exception)); }
public void ReturnsTaskThatFailsIfPostProcessingFails() { var exception = new Exception(); var asyncLoader = new AsyncLoaderTestImpl(); var task = asyncLoader.PerformAsyncOperation(token => Task.FromResult(true), (res, tok) => { throw exception; }); Assert.That(task.IsFaulted, Is.True); // Exception will always be wrapped in an AggregateException Assert.That(task.Exception.InnerException, Is.EqualTo(exception)); }
public void DoesNotRunPostProcessingStepIfOperationFails() { var postProcess = Substitute.For <Func <bool, CancellationToken, bool> >(); postProcess.Invoke(Arg.Any <bool>(), Arg.Any <CancellationToken>()).Returns(true); var asyncLoader = new AsyncLoaderTestImpl(); asyncLoader.PerformAsyncOperation(token => FromException(new Exception()), postProcess); postProcess.DidNotReceive().Invoke(Arg.Any <bool>(), Arg.Any <CancellationToken>()); }
public void ReportsAsyncOperationCompletedWithResultFromPostProcess() { var completedHandler = Substitute.For <EventHandler <bool> >(); var asyncLoader = new AsyncLoaderTestImpl(new RunInlineSynchronizationContext()); asyncLoader.AsyncOperationCompletedTunnel += completedHandler; // Now we check with a post process step that returns _true_ asyncLoader.PerformAsyncOperation(token => Task.FromResult(false), (b, c) => true); completedHandler.Received().Invoke(asyncLoader, true); }
public void ReturnsTaskThatFailsIfEventNotificationFailsWithInlineEventContext() { var exception = new Exception(); var asyncLoader = new AsyncLoaderTestImpl(new RunInlineSynchronizationContext()); asyncLoader.AsyncOperationCompletedTunnel += (s, e) => { throw exception; }; var task = asyncLoader.PerformAsyncOperation(tok => Task.FromResult(true), (res, tok) => { return(true); }); Assert.That(task.IsFaulted, Is.True); Assert.That(task.Exception.InnerException, Is.EqualTo(exception)); }
public void DoesNotReportExceptionOnCancel() { var asyncLoader = new AsyncLoaderTestImpl(new RunInlineSynchronizationContext()); var handler = Substitute.For <EventHandler <Exception> >(); asyncLoader.AsyncOperationFailed += handler; asyncLoader.PerformAsyncOperation(token => TaskConstants <bool> .Canceled); Assert.That(asyncLoader.Exception, Is.Null); handler.DidNotReceive().Invoke(Arg.Any <object>(), Arg.Any <Exception>()); }
public void DoesNotRunPostProcessingIfOperationCancelled() { var postProcess = Substitute.For <Func <bool, CancellationToken, bool> >(); postProcess.Invoke(Arg.Any <bool>(), Arg.Any <CancellationToken>()).Returns(true); var asyncLoader = new AsyncLoaderTestImpl(); asyncLoader.PerformAsyncOperation(token => TaskConstants <bool> .Canceled, postProcess); postProcess.DidNotReceive().Invoke(Arg.Any <bool>(), Arg.Any <CancellationToken>()); }
public void TransitionsStatusToCancelledForCancelledOperation() { var statusHandler = Substitute.For <EventHandler <AsyncStatusTransition> >(); var asyncLoader = new AsyncLoaderTestImpl(new RunInlineSynchronizationContext()); asyncLoader.StatusChanged += statusHandler; asyncLoader.PerformAsyncOperation(token => TaskConstants <bool> .Canceled); Assert.That(asyncLoader.Status, Is.EqualTo(AsyncStatus.Cancelled)); statusHandler.Received().Invoke(asyncLoader, new AsyncStatusTransition(AsyncStatus.Ready, AsyncStatus.Loading)); statusHandler.Received().Invoke(asyncLoader, new AsyncStatusTransition(AsyncStatus.Loading, AsyncStatus.Cancelled)); }
public void ClearsPreviousExceptionWhenNewOperationIsStarted() { var longTask = new TaskCompletionSource <bool>(); var asyncLoader = new AsyncLoaderTestImpl(); // First perform an operation that fails asyncLoader.PerformAsyncOperation(token => FromException(new Exception())); Assert.That(asyncLoader.Exception, Is.Not.Null); // Then start a new one that will not complete until we say so asyncLoader.PerformAsyncOperation(token => longTask.Task); Assert.That(asyncLoader.Exception, Is.Null); }
public void DoesNotReportExceptionOnSuccess() { // Have event notifications be executed inline on current thread var asyncLoader = new AsyncLoaderTestImpl(new RunInlineSynchronizationContext()); var handler = Substitute.For <EventHandler <Exception> >(); asyncLoader.AsyncOperationFailed += handler; asyncLoader.PerformAsyncOperation(token => Task.FromResult(true)); Assert.That(asyncLoader.Exception, Is.Null); handler.DidNotReceive().Invoke(Arg.Any <object>(), Arg.Any <Exception>()); }
public void ReportsAsyncOperationCompletedForSpecialOperations() { var completedHandler = Substitute.For <EventHandler <bool> >(); var asyncLoader = new AsyncLoaderTestImpl(new RunInlineSynchronizationContext()); asyncLoader.AsyncOperationCompletedTunnel += completedHandler; asyncLoader.NotifySpecialOperationCompletedTunnel(false); completedHandler.Received().Invoke(asyncLoader, false); asyncLoader.NotifySpecialOperationCompletedTunnel(true); completedHandler.Received().Invoke(asyncLoader, true); }
public void TransitionsStatusToFailedForOperationThatFails() { var statusHandler = Substitute.For <EventHandler <AsyncStatusTransition> >(); var asyncLoader = new AsyncLoaderTestImpl(new RunInlineSynchronizationContext()); asyncLoader.StatusChanged += statusHandler; asyncLoader.PerformAsyncOperation(token => FromException(new Exception())); Assert.That(asyncLoader.Status, Is.EqualTo(AsyncStatus.Failed)); statusHandler.Received().Invoke(asyncLoader, new AsyncStatusTransition(AsyncStatus.Ready, AsyncStatus.Loading)); statusHandler.Received().Invoke(asyncLoader, new AsyncStatusTransition(AsyncStatus.Loading, AsyncStatus.Failed)); }
public void ReportsAllExceptionsFromFailedOperation() { var exc1 = new Exception(); var exc2 = new Exception(); var handler = Substitute.For <EventHandler <Exception> >(); var asyncLoader = new AsyncLoaderTestImpl(new RunInlineSynchronizationContext()); asyncLoader.AsyncOperationFailed += handler; asyncLoader.PerformAsyncOperation(token => FromExceptions(exc1, exc2)); handler.Received(1).Invoke(asyncLoader, exc1); handler.Received(1).Invoke(asyncLoader, exc2); }
public void ReportsExceptionIfOperationFails() { var exception = new Exception(); var handler = Substitute.For <EventHandler <Exception> >(); var asyncLoader = new AsyncLoaderTestImpl(new RunInlineSynchronizationContext()); asyncLoader.AsyncOperationFailed += handler; asyncLoader.PerformAsyncOperation(token => FromException(exception)); Assert.That(asyncLoader.Exception, Is.Not.Null); Assert.That(asyncLoader.InnerException, Is.EqualTo(exception)); handler.Received(1).Invoke(asyncLoader, exception); }
public void ReportsAsyncOperationCompletedWhenOperationComplete() { var longTask = new TaskCompletionSource <bool>(); var completedHandler = Substitute.For <EventHandler <bool> >(); // Have event notifications be executed inline on current thread var asyncLoader = new AsyncLoaderTestImpl(new RunInlineSynchronizationContext()); asyncLoader.AsyncOperationCompletedTunnel += completedHandler; // Here we check with a post process step that returns false asyncLoader.PerformAsyncOperation(token => longTask.Task, (b, c) => false); // Operation not complete yet completedHandler.DidNotReceive().Invoke(Arg.Any <object>(), Arg.Any <bool>()); longTask.SetResult(true); completedHandler.Received().Invoke(asyncLoader, false); }
public void NotifiesOfOperationStartOnThreadPoolIfEventContextIsNullAndNoAmbientContext() { // Need to marshal the notification thread id back to main thread, we can use TCS for that var notificationResult = new TaskCompletionSource <int>(); // Now create a loader that uses the thread pool for notifications var asyncLoader = new AsyncLoaderTestImpl(eventContext: null); asyncLoader.StatusChanged += (s, e) => { notificationResult.SetResult(GetCurrentThreadId()); }; asyncLoader.PerformAsyncOperation(token => TaskConstants <bool> .Never); bool finished = notificationResult.Task.Wait(TimeSpan.FromSeconds(5)); Assert.That(finished, Is.True); // Now the thread id must NOT equate the current thread id Assert.That(notificationResult.Task.Result, Is.Not.EqualTo(GetCurrentThreadId())); }
public void TransitionsStatusAsExpectedForOperationThatRunsToCompletion() { var longTask = new TaskCompletionSource <bool>(); var statusHandler = Substitute.For <EventHandler <AsyncStatusTransition> >(); // Have event notifications be executed inline on current thread var asyncLoader = new AsyncLoaderTestImpl(new RunInlineSynchronizationContext()); asyncLoader.StatusChanged += statusHandler; Assert.That(asyncLoader.Status, Is.EqualTo(AsyncStatus.Ready)); asyncLoader.PerformAsyncOperation(token => longTask.Task); Assert.That(asyncLoader.Status, Is.EqualTo(AsyncStatus.Loading)); statusHandler.Received().Invoke(asyncLoader, new AsyncStatusTransition(AsyncStatus.Ready, AsyncStatus.Loading)); longTask.SetResult(true); Assert.That(asyncLoader.Status, Is.EqualTo(AsyncStatus.Ready)); statusHandler.Received().Invoke(asyncLoader, new AsyncStatusTransition(AsyncStatus.Loading, AsyncStatus.Ready)); }
public void NotifiesOfOperationStartOnCurrentThreadWhenUsingCurrentThreadScheduler() { var longTask = new TaskCompletionSource <bool>(); // Since we use CurrentThreadTaskScheduler, event notifications should be run synchronously on current thread int expectedEventThreadId = GetCurrentThreadId(); int actualEventThreadId = 0; // Have event notifications be executed inline on current thread var asyncLoader = new AsyncLoaderTestImpl(new RunInlineSynchronizationContext()); asyncLoader.StatusChanged += (s, e) => { // NOTE: Cannot perform asserts here, since the exceptions are eaten by the task scheduling mechanics. actualEventThreadId = GetCurrentThreadId(); }; // Start an operation that will not finish until we say so asyncLoader.PerformAsyncOperation(token => longTask.Task); Assert.That(actualEventThreadId, Is.EqualTo(expectedEventThreadId)); }
public void ReturnsTaskThatOnlyCompletesWhenEntireOperationIsComplete() { using (var postProcessPause = new ManualResetEventSlim(false)) using (var postProcessExecuting = new ManualResetEventSlim(false)) { var longTask = new TaskCompletionSource <bool>(); // Need to run this on another thread, since we intend to later block that thread during a callback Func <CancellationToken, Task <bool> > longAsyncOp = token => Task.Run(async() => await longTask.Task); // This post process step will wait until handle is signalled Func <bool, CancellationToken, bool> processOp = (res, tok) => { postProcessExecuting.Set(); // signal that we have arrived at post process step postProcessPause.Wait(); return(true); }; var asyncLoader = new AsyncLoaderTestImpl(); var task = asyncLoader.PerformAsyncOperation(longAsyncOp, processOp); // Verify that returned task does not complete before async op is done Assert.That(task.IsCompleted, Is.False); // Complete async op, then wait for execution to reach post process step longTask.SetResult(true); postProcessExecuting.Wait(); // Verify that the task does not complete before the post-process step is done Assert.That(task.IsCompleted, Is.False); // Signal the operation to proceed and finish postProcessPause.Set(); bool finished = task.Wait(TimeSpan.FromSeconds(5)); Assert.That(finished, Is.True); } }