// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System; using System.Threading; using System.Web.Mvc.Async; using Xunit; using Assert = Microsoft.TestCommon.AssertEx; namespace Microsoft.Web.Mvc.Test { public class AsyncManagerExtensionsTest { [Fact] public void RegisterTask_AsynchronousCompletion() { // Arrange SimpleSynchronizationContext syncContext = new SimpleSynchronizationContext(); AsyncManager asyncManager = new AsyncManager(syncContext); bool endDelegateWasCalled = false; ManualResetEvent waitHandle = new ManualResetEvent(false /* initialState */); Func beginDelegate = callback => { Assert.Equal(1, asyncManager.OutstandingOperations.Count); MockAsyncResult asyncResult = new MockAsyncResult(false /* completedSynchronously */); ThreadPool.QueueUserWorkItem(_ => { Assert.Equal(1, asyncManager.OutstandingOperations.Count); callback(asyncResult); waitHandle.Set(); }); return asyncResult; }; Action endDelegate = delegate { endDelegateWasCalled = true; }; // Act asyncManager.RegisterTask(beginDelegate, endDelegate); waitHandle.WaitOne(); // Assert Assert.True(endDelegateWasCalled); Assert.True(syncContext.SendWasCalled); Assert.Equal(0, asyncManager.OutstandingOperations.Count); } [Fact] public void RegisterTask_AsynchronousCompletion_SwallowsExceptionsThrownByEndDelegate() { // Arrange SimpleSynchronizationContext syncContext = new SimpleSynchronizationContext(); AsyncManager asyncManager = new AsyncManager(syncContext); bool endDelegateWasCalled = false; ManualResetEvent waitHandle = new ManualResetEvent(false /* initialState */); Func beginDelegate = callback => { MockAsyncResult asyncResult = new MockAsyncResult(false /* completedSynchronously */); ThreadPool.QueueUserWorkItem(_ => { callback(asyncResult); waitHandle.Set(); }); return asyncResult; }; Action endDelegate = delegate { endDelegateWasCalled = true; throw new Exception("This is a sample exception."); }; // Act asyncManager.RegisterTask(beginDelegate, endDelegate); waitHandle.WaitOne(); // Assert Assert.True(endDelegateWasCalled); Assert.Equal(0, asyncManager.OutstandingOperations.Count); } [Fact] public void RegisterTask_ResetsOutstandingOperationCountIfBeginMethodThrows() { // Arrange SimpleSynchronizationContext syncContext = new SimpleSynchronizationContext(); AsyncManager asyncManager = new AsyncManager(syncContext); Func beginDelegate = cb => { throw new InvalidOperationException("BeginDelegate throws."); }; Action endDelegate = ar => { Assert.True(false, "This should never be called."); }; // Act & assert Assert.Throws( delegate { asyncManager.RegisterTask(beginDelegate, endDelegate); }, "BeginDelegate throws."); Assert.Equal(0, asyncManager.OutstandingOperations.Count); } [Fact] public void RegisterTask_SynchronousCompletion() { // Arrange SimpleSynchronizationContext syncContext = new SimpleSynchronizationContext(); AsyncManager asyncManager = new AsyncManager(syncContext); bool endDelegateWasCalled = false; Func beginDelegate = callback => { Assert.Equal(1, asyncManager.OutstandingOperations.Count); MockAsyncResult asyncResult = new MockAsyncResult(true /* completedSynchronously */); callback(asyncResult); return asyncResult; }; Action endDelegate = delegate { endDelegateWasCalled = true; }; // Act asyncManager.RegisterTask(beginDelegate, endDelegate); // Assert Assert.True(endDelegateWasCalled); Assert.False(syncContext.SendWasCalled); Assert.Equal(0, asyncManager.OutstandingOperations.Count); } [Fact] public void RegisterTask_ThrowsIfAsyncManagerIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { AsyncManagerExtensions.RegisterTask(null, _ => null, _ => { }); }, "asyncManager"); } [Fact] public void RegisterTask_ThrowsIfBeginDelegateIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new AsyncManager().RegisterTask(null, _ => { }); }, "beginDelegate"); } [Fact] public void RegisterTask_ThrowsIfEndDelegateIsNull() { // Act & assert Assert.ThrowsArgumentNull( delegate { new AsyncManager().RegisterTask(_ => null, null); }, "endDelegate"); } private class SimpleSynchronizationContext : SynchronizationContext { public bool SendWasCalled; public override void Send(SendOrPostCallback d, object state) { SendWasCalled = true; d(state); } } private class MockAsyncResult : IAsyncResult { private readonly bool _completedSynchronously; public MockAsyncResult(bool completedSynchronously) { _completedSynchronously = completedSynchronously; } public object AsyncState { get { throw new NotImplementedException(); } } public WaitHandle AsyncWaitHandle { get { throw new NotImplementedException(); } } public bool CompletedSynchronously { get { return _completedSynchronously; } } public bool IsCompleted { get { throw new NotImplementedException(); } } } } }