Exemple #1
0
        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);
        }
Exemple #2
0
        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));
        }
Exemple #3
0
        public void RunsOperation()
        {
            var operation = Substitute.For <Func <CancellationToken, Task <bool> > >();

            var asyncLoader = new AsyncLoaderTestImpl();

            asyncLoader.PerformAsyncOperation(operation);

            operation.Received().Invoke(Arg.Any <CancellationToken>());
        }
Exemple #4
0
        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>());
        }
Exemple #5
0
        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>());
        }
Exemple #6
0
        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));
        }
Exemple #7
0
        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));
        }
Exemple #8
0
        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>());
        }
Exemple #9
0
        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);
        }
Exemple #10
0
        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));
        }
Exemple #11
0
        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>());
        }
Exemple #12
0
        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>());
        }
Exemple #13
0
        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));
        }
Exemple #14
0
        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);
        }
Exemple #15
0
        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>());
        }
Exemple #16
0
        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);
        }
Exemple #17
0
        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));
        }
Exemple #18
0
        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);
        }
Exemple #19
0
        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);
        }
Exemple #20
0
        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);
        }
Exemple #21
0
        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()));
        }
Exemple #22
0
        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));
        }
Exemple #23
0
        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));
        }
Exemple #24
0
        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);
                }
        }