// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System; using System.Reflection; using System.Threading.Tasks; using Xunit; namespace Microsoft.TestCommon { /// <summary> /// MSTest assert class to make assertions about tests using <see cref="Task"/>. /// </summary> public class TaskAssert { private static int timeOutMs = System.Diagnostics.Debugger.IsAttached ? TimeoutConstant.DefaultTimeout : TimeoutConstant.DefaultTimeout * 10; private static TaskAssert singleton = new TaskAssert(); public static TaskAssert Singleton { get { return singleton; } } /// <summary> /// Asserts the given task has been started. TAP guidelines are that all /// <see cref="Task"/> objects returned from public API's have been started. /// </summary> /// <param name="task">The <see cref="Task"/> to test.</param> public void IsStarted(Task task) { Assert.NotNull(task); Assert.True(task.Status != TaskStatus.Created); } /// <summary> /// Asserts the given task completes successfully. This method will block the /// current thread waiting for the task, but will timeout if it does not complete. /// </summary> /// <param name="task">The <see cref="Task"/> to test.</param> public void Succeeds(Task task) { IsStarted(task); task.Wait(timeOutMs); AggregateException aggregateException = task.Exception; Exception innerException = aggregateException == null ? null : aggregateException.InnerException; Assert.Null(innerException); } /// <summary> /// Asserts the given task completes successfully and returns a result. /// Use this overload for a generic <see cref="Task"/> whose generic parameter is not known at compile time. /// This method will block the current thread waiting for the task, but will timeout if it does not complete. /// </summary> /// <param name="task">The <see cref="Task"/> to test.</param> /// <returns>The result from that task.</returns> public object SucceedsWithResult(Task task) { Succeeds(task); Assert.True(task.GetType().IsGenericType); Type[] genericArguments = task.GetType().GetGenericArguments(); Assert.Equal(1, genericArguments.Length); PropertyInfo resultProperty = task.GetType().GetProperty("Result"); Assert.NotNull(resultProperty); return resultProperty.GetValue(task, null); } /// <summary> /// Asserts the given task completes successfully and returns a <typeparamref name="T"/> result. /// This method will block the current thread waiting for the task, but will timeout if it does not complete. /// </summary> /// <typeparam name="T">The result of the <see cref="Task"/>.</typeparam> /// <param name="task">The <see cref="Task"/> to test.</param> /// <returns>The result from that task.</returns> public T SucceedsWithResult<T>(Task<T> task) { Succeeds(task); return task.Result; } /// <summary> /// Asserts the given <see cref="Task"/> completes successfully and yields /// the expected result. /// </summary> /// <param name="task">The <see cref="Task"/> to test.</param> /// <param name="expectedObj">The expected result.</param> public void ResultEquals(Task task, object expectedObj) { object actualObj = SucceedsWithResult(task); Assert.Equal(expectedObj, actualObj); } /// <summary> /// Asserts the given <see cref="Task"/> completes successfully and yields /// the expected result. /// </summary> /// <typeparam name="T">The type the task will return.</typeparam> /// <param name="task">The task to test.</param> /// <param name="expectedObj">The expected result.</param> public void ResultEquals<T>(Task<T> task, T expectedObj) { T actualObj = SucceedsWithResult<T>(task); Assert.Equal(expectedObj, actualObj); } } }