/test/System.Web.Mvc.Test/Async/Test/TaskAsyncActionDescriptorTest.cs
C# | 557 lines | 394 code | 100 blank | 63 comment | 1 complexity | 461114e2f045f88bc85eee6c6cede88b MD5 | raw file
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using System.Threading;
- using System.Threading.Tasks;
- using Moq;
- using Xunit;
- using Assert = Microsoft.TestCommon.AssertEx;
- namespace System.Web.Mvc.Async.Test
- {
- public class TaskAsyncActionDescriptorTest
- {
- private readonly MethodInfo _taskMethod = typeof(ExecuteController).GetMethod("SimpleTask");
- [Fact]
- public void Constructor_SetsProperties()
- {
- // Arrange
- string actionName = "SomeAction";
- ControllerDescriptor cd = new Mock<ControllerDescriptor>().Object;
- // Act
- TaskAsyncActionDescriptor ad = new TaskAsyncActionDescriptor(_taskMethod, actionName, cd);
- // Assert
- Assert.Equal(_taskMethod, ad.TaskMethodInfo);
- Assert.Equal(actionName, ad.ActionName);
- Assert.Equal(cd, ad.ControllerDescriptor);
- }
- [Fact]
- public void Constructor_ThrowsIfActionNameIsEmpty()
- {
- // Arrange
- ControllerDescriptor cd = new Mock<ControllerDescriptor>().Object;
- // Act & assert
- Assert.ThrowsArgumentNullOrEmpty(
- delegate { new TaskAsyncActionDescriptor(_taskMethod, "", cd); }, "actionName");
- }
- [Fact]
- public void Constructor_ThrowsIfActionNameIsNull()
- {
- // Arrange
- ControllerDescriptor cd = new Mock<ControllerDescriptor>().Object;
- // Act & assert
- Assert.ThrowsArgumentNullOrEmpty(
- delegate { new TaskAsyncActionDescriptor(_taskMethod, null, cd); }, "actionName");
- }
- [Fact]
- public void Constructor_ThrowsIfTaskMethodInfoIsInvalid()
- {
- // Arrange
- ControllerDescriptor cd = new Mock<ControllerDescriptor>().Object;
- MethodInfo getHashCodeMethod = typeof(object).GetMethod("GetHashCode");
- // Act & assert
- Assert.Throws<ArgumentException>(
- delegate { new TaskAsyncActionDescriptor(getHashCodeMethod, "SomeAction", cd); },
- @"Cannot create a descriptor for instance method 'Int32 GetHashCode()' on type 'System.Object' because the type does not derive from ControllerBase.
- Parameter name: taskMethodInfo");
- }
- [Fact]
- public void Constructor_ThrowsIfTaskMethodInfoIsNull()
- {
- // Arrange
- ControllerDescriptor cd = new Mock<ControllerDescriptor>().Object;
- // Act & assert
- Assert.ThrowsArgumentNull(
- delegate { new TaskAsyncActionDescriptor(null, "SomeAction", cd); }, "taskMethodInfo");
- }
- [Fact]
- public void Constructor_ThrowsIfControllerDescriptorIsNull()
- {
- // Act & assert
- Assert.ThrowsArgumentNull(
- delegate { new TaskAsyncActionDescriptor(_taskMethod, "SomeAction", null); }, "controllerDescriptor");
- }
- [Fact]
- public void ExecuteTask()
- {
- // Arrange
- TaskAsyncActionDescriptor actionDescriptor = GetActionDescriptor(GetExecuteControllerMethodInfo("SimpleTask"));
- Dictionary<string, object> parameters = new Dictionary<string, object>()
- {
- { "doWork", true }
- };
- ControllerContext controllerContext = GetControllerContext();
- // Act
- object retVal = ExecuteHelper(actionDescriptor, parameters, controllerContext);
- // Assert
- Assert.Null(retVal);
- Assert.True((controllerContext.Controller as ExecuteController).WorkDone);
- }
- [Fact]
- public void ExecuteTaskGeneric()
- {
- // Arrange
- TaskAsyncActionDescriptor actionDescriptor = GetActionDescriptor(GetExecuteControllerMethodInfo("GenericTask"));
- Dictionary<string, object> parameters = new Dictionary<string, object>()
- {
- { "taskId", "foo" }
- };
- // Act
- object retVal = ExecuteHelper(actionDescriptor, parameters);
- // Assert
- Assert.Equal("foo", retVal);
- }
- [Fact]
- public void ExecuteTaskPreservesStackTraceOnException()
- {
- // Arrange
- TaskAsyncActionDescriptor actionDescriptor = GetActionDescriptor(GetExecuteControllerMethodInfo("SimpleTaskException"));
- Dictionary<string, object> parameters = new Dictionary<string, object>()
- {
- { "doWork", true }
- };
- // Act
- IAsyncResult result = actionDescriptor.BeginExecute(GetControllerContext(), parameters, null, null);
- // Assert
- InvalidOperationException ex = Assert.Throws<InvalidOperationException>(
- () => actionDescriptor.EndExecute(result),
- "Test exception from action"
- );
- Assert.True(ex.StackTrace.Contains("System.Web.Mvc.Async.Test.TaskAsyncActionDescriptorTest.ExecuteController."));
- }
- [Fact]
- public void ExecuteTaskGenericPreservesStackTraceOnException()
- {
- // Arrange
- TaskAsyncActionDescriptor actionDescriptor = GetActionDescriptor(GetExecuteControllerMethodInfo("GenericTaskException"));
- Dictionary<string, object> parameters = new Dictionary<string, object>()
- {
- { "taskId", "foo" },
- { "throwException", true }
- };
- // Act
- IAsyncResult result = actionDescriptor.BeginExecute(GetControllerContext(), parameters, null, null);
- // Assert
- InvalidOperationException ex = Assert.Throws<InvalidOperationException>(
- () => actionDescriptor.EndExecute(result),
- "Test exception from action"
- );
- Assert.True(ex.StackTrace.Contains("System.Web.Mvc.Async.Test.TaskAsyncActionDescriptorTest.ExecuteController."));
- }
- [Fact]
- public void ExecuteTaskOfPrivateT()
- {
- // Arrange
- TaskAsyncActionDescriptor actionDescriptor = GetActionDescriptor(GetExecuteControllerMethodInfo("TaskOfPrivateT"));
- ControllerContext controllerContext = GetControllerContext();
- Dictionary<string, object> parameters = new Dictionary<string, object>();
- // Act
- object retVal = ExecuteHelper(actionDescriptor, parameters, controllerContext);
- // Assert
- Assert.Null(retVal);
- Assert.True((controllerContext.Controller as ExecuteController).WorkDone);
- }
- [Fact]
- public void ExecuteTaskPreservesState()
- {
- // Arrange
- TaskAsyncActionDescriptor actionDescriptor = GetActionDescriptor(GetExecuteControllerMethodInfo("SimpleTask"));
- Dictionary<string, object> parameters = new Dictionary<string, object>()
- {
- { "doWork", true }
- };
- ControllerContext controllerContext = GetControllerContext();
- // Act
- TaskWrapperAsyncResult result = (TaskWrapperAsyncResult)actionDescriptor.BeginExecute(GetControllerContext(), parameters, callback: null, state: "state");
- // Assert
- Assert.Equal("state", result.AsyncState);
- }
- [Fact]
- public void ExecuteTaskWithNullParameterAndTimeout()
- {
- // Arrange
- TaskAsyncActionDescriptor actionDescriptor = GetActionDescriptor(GetExecuteControllerMethodInfo("TaskTimeoutWithNullParam"));
- Dictionary<string, object> token = new Dictionary<string, object>()
- {
- { "nullParam", null },
- { "cancellationToken", new CancellationToken() }
- };
- // Act & assert
- Assert.Throws<TimeoutException>(
- () => actionDescriptor.EndExecute(actionDescriptor.BeginExecute(GetControllerContext(0), parameters: token, callback: null, state: null)),
- "The operation has timed out."
- );
- }
- [Fact]
- public void ExecuteWithInfiniteTimeout()
- {
- // Arrange
- TaskAsyncActionDescriptor actionDescriptor = GetActionDescriptor(GetExecuteControllerMethodInfo("TaskWithInfiniteTimeout"));
- ControllerContext controllerContext = GetControllerContext(Timeout.Infinite);
- Dictionary<string, object> parameters = new Dictionary<string, object>()
- {
- { "cancellationToken", new CancellationToken() }
- };
- // Act
- object retVal = ExecuteHelper(actionDescriptor, parameters);
- // Assert
- Assert.Equal("Task Completed", retVal);
- }
- [Fact]
- public void ExecuteTaskWithImmediateTimeout()
- {
- // Arrange
- TaskAsyncActionDescriptor actionDescriptor = GetActionDescriptor(GetExecuteControllerMethodInfo("TaskTimeout"));
- Dictionary<string, object> token = new Dictionary<string, object>()
- {
- { "cancellationToken", new CancellationToken() }
- };
- // Act & assert
- Assert.Throws<TimeoutException>(
- () => actionDescriptor.EndExecute(actionDescriptor.BeginExecute(GetControllerContext(0), parameters: token, callback: null, state: null)),
- "The operation has timed out."
- );
- }
- [Fact]
- public void ExecuteTaskWithTimeout()
- {
- // Arrange
- TaskAsyncActionDescriptor actionDescriptor = GetActionDescriptor(GetExecuteControllerMethodInfo("TaskTimeout"));
- Dictionary<string, object> token = new Dictionary<string, object>()
- {
- { "cancellationToken", new CancellationToken() }
- };
- // Act & assert
- Assert.Throws<TimeoutException>(
- () => actionDescriptor.EndExecute(actionDescriptor.BeginExecute(GetControllerContext(2000), parameters: token, callback: null, state: null)),
- "The operation has timed out."
- );
- }
- [Fact]
- public void SynchronousExecuteThrows()
- {
- // Arrange
- TaskAsyncActionDescriptor actionDescriptor = GetActionDescriptor(GetExecuteControllerMethodInfo("SimpleTask"));
- // Act & assert
- Assert.Throws<InvalidOperationException>(
- delegate { actionDescriptor.Execute(new ControllerContext(), new Dictionary<string, object>()); }, "The asynchronous action method 'someName' returns a Task, which cannot be executed synchronously.");
- }
- [Fact]
- public void Execute_ThrowsIfControllerContextIsNull()
- {
- // Arrange
- TaskAsyncActionDescriptor ad = GetActionDescriptor(_taskMethod);
- // Act & assert
- Assert.ThrowsArgumentNull(
- delegate { ad.BeginExecute(null, new Dictionary<string, object>(), null, null); }, "controllerContext");
- }
- [Fact]
- public void Execute_ThrowsIfControllerIsNotAsyncManagerContainer()
- {
- // Arrange
- TaskAsyncActionDescriptor ad = GetActionDescriptor(_taskMethod);
- ControllerContext controllerContext = new ControllerContext()
- {
- Controller = new RegularSyncController()
- };
- Dictionary<string, object> parameters = new Dictionary<string, object>()
- {
- { "doWork", true }
- };
- // Act & assert
- Assert.Throws<InvalidOperationException>(
- delegate { ad.BeginExecute(controllerContext, parameters, null, null); },
- @"The controller of type 'System.Web.Mvc.Async.Test.TaskAsyncActionDescriptorTest+RegularSyncController' must subclass AsyncController or implement the IAsyncManagerContainer interface.");
- }
- [Fact]
- public void Execute_ThrowsIfParametersIsNull()
- {
- // Arrange
- TaskAsyncActionDescriptor ad = GetActionDescriptor(_taskMethod);
- // Act & assert
- Assert.ThrowsArgumentNull(
- delegate { ad.BeginExecute(new ControllerContext(), null, null, null); }, "parameters");
- }
- [Fact]
- public void GetCustomAttributesCallsMethodInfoGetCustomAttributes()
- {
- // Arrange
- object[] expected = new object[0];
- Mock<MethodInfo> mockMethod = new Mock<MethodInfo>();
- mockMethod.Setup(mi => mi.GetCustomAttributes(true)).Returns(expected);
- TaskAsyncActionDescriptor ad = new TaskAsyncActionDescriptor(mockMethod.Object, "someName", new Mock<ControllerDescriptor>().Object, validateMethod: false)
- {
- DispatcherCache = new ActionMethodDispatcherCache()
- };
- // Act
- object[] returned = ad.GetCustomAttributes(true);
- // Assert
- Assert.Same(expected, returned);
- }
- [Fact]
- public void GetCustomAttributesWithAttributeTypeCallsMethodInfoGetCustomAttributes()
- {
- // Arrange
- object[] expected = new object[0];
- Mock<MethodInfo> mockMethod = new Mock<MethodInfo>();
- mockMethod.Setup(mi => mi.GetCustomAttributes(typeof(ObsoleteAttribute), true)).Returns(expected);
- TaskAsyncActionDescriptor ad = new TaskAsyncActionDescriptor(mockMethod.Object, "someName", new Mock<ControllerDescriptor>().Object, validateMethod: false)
- {
- DispatcherCache = new ActionMethodDispatcherCache()
- };
- // Act
- object[] returned = ad.GetCustomAttributes(typeof(ObsoleteAttribute), true);
- // Assert
- Assert.Same(expected, returned);
- }
- [Fact]
- public void GetParameters()
- {
- // Arrange
- ParameterInfo pInfo = _taskMethod.GetParameters()[0];
- TaskAsyncActionDescriptor ad = GetActionDescriptor(_taskMethod);
- // Act
- ParameterDescriptor[] pDescsFirstCall = ad.GetParameters();
- ParameterDescriptor[] pDescsSecondCall = ad.GetParameters();
- // Assert
- Assert.NotSame(pDescsFirstCall, pDescsSecondCall); // Should get a new array every time
- Assert.Equal(pDescsFirstCall, pDescsSecondCall);
- Assert.Single(pDescsFirstCall);
- ReflectedParameterDescriptor pDesc = pDescsFirstCall[0] as ReflectedParameterDescriptor;
- Assert.NotNull(pDesc);
- Assert.Same(ad, pDesc.ActionDescriptor);
- Assert.Same(pInfo, pDesc.ParameterInfo);
- }
- [Fact]
- public void GetSelectors()
- {
- // Arrange
- ControllerContext controllerContext = new Mock<ControllerContext>().Object;
- Mock<MethodInfo> mockMethod = new Mock<MethodInfo>();
- Mock<ActionMethodSelectorAttribute> mockAttr = new Mock<ActionMethodSelectorAttribute>();
- mockAttr.Setup(attr => attr.IsValidForRequest(controllerContext, mockMethod.Object)).Returns(true).Verifiable();
- mockMethod.Setup(m => m.GetCustomAttributes(typeof(ActionMethodSelectorAttribute), true)).Returns(new ActionMethodSelectorAttribute[] { mockAttr.Object });
- TaskAsyncActionDescriptor ad = new TaskAsyncActionDescriptor(mockMethod.Object, "someName", new Mock<ControllerDescriptor>().Object, validateMethod: false)
- {
- DispatcherCache = new ActionMethodDispatcherCache()
- };
- // Act
- ICollection<ActionSelector> selectors = ad.GetSelectors();
- bool executedSuccessfully = selectors.All(s => s(controllerContext));
- // Assert
- Assert.Single(selectors);
- Assert.True(executedSuccessfully);
- mockAttr.Verify();
- }
- [Fact]
- public void IsDefined()
- {
- // Arrange
- TaskAsyncActionDescriptor ad = GetActionDescriptor(_taskMethod);
- // Act
- bool isDefined = ad.IsDefined(typeof(AuthorizeAttribute), inherit: true);
- // Assert
- Assert.True(isDefined);
- }
- public static object ExecuteHelper(TaskAsyncActionDescriptor actionDescriptor, Dictionary<string, object> parameters, ControllerContext controllerContext = null)
- {
- SignalContainer<object> resultContainer = new SignalContainer<object>();
- AsyncCallback callback = ar =>
- {
- object o = actionDescriptor.EndExecute(ar);
- resultContainer.Signal(o);
- };
- actionDescriptor.BeginExecute(controllerContext ?? GetControllerContext(), parameters, callback, state: null);
- return resultContainer.Wait();
- }
- private static TaskAsyncActionDescriptor GetActionDescriptor(MethodInfo taskMethod)
- {
- return new TaskAsyncActionDescriptor(taskMethod, "someName", new Mock<ControllerDescriptor>().Object)
- {
- DispatcherCache = new ActionMethodDispatcherCache()
- };
- }
- private static ControllerContext GetControllerContext(int timeout = 45 * 1000)
- {
- Mock<ControllerContext> mockControllerContext = new Mock<ControllerContext>();
- ExecuteController controller = new ExecuteController();
- controller.AsyncManager.Timeout = timeout;
- mockControllerContext.Setup(c => c.Controller).Returns(controller);
- return mockControllerContext.Object;
- }
- private static MethodInfo GetExecuteControllerMethodInfo(string methodName)
- {
- return typeof(ExecuteController).GetMethod(methodName);
- }
- private class ExecuteController : AsyncController
- {
- public bool WorkDone { get; set; }
- public Task<ActionResult> ReturnedTask { get; set; }
- public Task<string> GenericTask(string taskId)
- {
- return Task.Factory.StartNew(() => taskId);
- }
- public Task<string> GenericTaskException(string taskId, bool throwException)
- {
- return Task.Factory.StartNew(() =>
- {
- if (throwException)
- {
- ThrowException();
- }
- ;
- return taskId;
- });
- }
- private void ThrowException()
- {
- throw new InvalidOperationException("Test exception from action");
- }
- [Authorize]
- public Task SimpleTask(bool doWork)
- {
- return Task.Factory.StartNew(() => { WorkDone = doWork; });
- }
- public Task SimpleTaskException(bool doWork)
- {
- return Task.Factory.StartNew(() => { ThrowException(); });
- }
- public Task<ActionResult> TaskTimeoutWithNullParam(Object nullParam, CancellationToken cancellationToken)
- {
- return TaskTimeout(cancellationToken);
- }
- public Task<string> TaskWithInfiniteTimeout(CancellationToken cancellationToken)
- {
- return Task.Factory.StartNew(() => "Task Completed");
- }
- public Task<ActionResult> TaskTimeout(CancellationToken cancellationToken)
- {
- TaskCompletionSource<ActionResult> completionSource = new TaskCompletionSource<ActionResult>();
- cancellationToken.Register(() => completionSource.TrySetCanceled());
- ReturnedTask = completionSource.Task;
- return ReturnedTask;
- }
- public Task TaskOfPrivateT()
- {
- var completionSource = new TaskCompletionSource<PrivateObject>();
- completionSource.SetResult(new PrivateObject());
- WorkDone = true;
- return completionSource.Task;
- }
- private class PrivateObject
- {
- public override string ToString()
- {
- return "Private Object";
- }
- }
- }
- // Controller is async, so derive from ControllerBase to get sync behavior.
- private class RegularSyncController : ControllerBase
- {
- protected override void ExecuteCore()
- {
- throw new NotImplementedException();
- }
- }
- }
- }