// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Runtime.CompilerServices; using Xunit; namespace System.Threading.Tasks.Tests { public class AsyncValueTaskMethodBuilderTests { [Fact] public void Create_ReturnsDefaultInstance() { AsyncValueTaskMethodBuilder b = ValueTask.CreateAsyncMethodBuilder(); Assert.Equal(default(AsyncValueTaskMethodBuilder), b); // implementation detail being verified } [Fact] public void SetResult_BeforeAccessTask_ValueTaskContainsValue() { AsyncValueTaskMethodBuilder b = ValueTask.CreateAsyncMethodBuilder(); b.SetResult(42); ValueTask vt = b.Task; Assert.True(vt.IsCompletedSuccessfully); Assert.False(WrapsTask(vt)); Assert.Equal(42, vt.Result); } [Fact] public void SetResult_AfterAccessTask_ValueTaskContainsValue() { AsyncValueTaskMethodBuilder b = ValueTask.CreateAsyncMethodBuilder(); ValueTask vt = b.Task; b.SetResult(42); Assert.True(vt.IsCompletedSuccessfully); Assert.True(WrapsTask(vt)); Assert.Equal(42, vt.Result); } [Fact] public void SetException_BeforeAccessTask_FaultsTask() { AsyncValueTaskMethodBuilder b = ValueTask.CreateAsyncMethodBuilder(); var e = new FormatException(); b.SetException(e); ValueTask vt = b.Task; Assert.True(vt.IsFaulted); Assert.Same(e, Assert.Throws(() => vt.GetAwaiter().GetResult())); } [Fact] public void SetException_AfterAccessTask_FaultsTask() { AsyncValueTaskMethodBuilder b = ValueTask.CreateAsyncMethodBuilder(); var e = new FormatException(); ValueTask vt = b.Task; b.SetException(e); Assert.True(vt.IsFaulted); Assert.Same(e, Assert.Throws(() => vt.GetAwaiter().GetResult())); } [Fact] public void SetException_OperationCanceledException_CancelsTask() { AsyncValueTaskMethodBuilder b = ValueTask.CreateAsyncMethodBuilder(); var e = new OperationCanceledException(); ValueTask vt = b.Task; b.SetException(e); Assert.True(vt.IsCanceled); Assert.Same(e, Assert.Throws(() => vt.GetAwaiter().GetResult())); } [Fact] public void Start_InvokesMoveNext() { AsyncValueTaskMethodBuilder b = ValueTask.CreateAsyncMethodBuilder(); int invokes = 0; var dsm = new DelegateStateMachine { MoveNextDelegate = () => invokes++ }; b.Start(ref dsm); Assert.Equal(1, invokes); } [Theory] [InlineData(1, false)] [InlineData(2, false)] [InlineData(1, true)] [InlineData(2, true)] public void AwaitOnCompleted_ForcesTaskCreation(int numAwaits, bool awaitUnsafe) { AsyncValueTaskMethodBuilder b = ValueTask.CreateAsyncMethodBuilder(); var dsm = new DelegateStateMachine(); TaskAwaiter t = new TaskCompletionSource().Task.GetAwaiter(); Assert.InRange(numAwaits, 1, int.MaxValue); for (int i = 1; i <= numAwaits; i++) { if (awaitUnsafe) { b.AwaitUnsafeOnCompleted(ref t, ref dsm); } else { b.AwaitOnCompleted(ref t, ref dsm); } } b.SetResult(42); Assert.True(WrapsTask(b.Task)); Assert.Equal(42, b.Task.Result); } [Fact] [ActiveIssue("https://github.com/dotnet/corefx/issues/22506", TargetFrameworkMonikers.UapAot)] public void SetStateMachine_InvalidArgument_ThrowsException() { AsyncValueTaskMethodBuilder b = ValueTask.CreateAsyncMethodBuilder(); AssertExtensions.Throws("stateMachine", () => b.SetStateMachine(null)); } [Fact] public void Start_ExecutionContextChangesInMoveNextDontFlowOut() { var al = new AsyncLocal { Value = 0 }; int calls = 0; var dsm = new DelegateStateMachine { MoveNextDelegate = () => { al.Value++; calls++; } }; dsm.MoveNext(); Assert.Equal(1, al.Value); Assert.Equal(1, calls); dsm.MoveNext(); Assert.Equal(2, al.Value); Assert.Equal(2, calls); AsyncValueTaskMethodBuilder b = ValueTask.CreateAsyncMethodBuilder(); b.Start(ref dsm); Assert.Equal(2, al.Value); // change should not be visible Assert.Equal(3, calls); // Make sure we've not caused the Task to be allocated b.SetResult(42); ValueTask vt = b.Task; Assert.False(WrapsTask(vt)); } [Theory] [InlineData(0)] [InlineData(1)] [InlineData(2)] [InlineData(10)] public static async Task UsedWithAsyncMethod_CompletesSuccessfully(int yields) { Assert.Equal(42, await ValueTaskReturningAsyncMethod(42)); ValueTask vt = ValueTaskReturningAsyncMethod(84); Assert.Equal(yields > 0, WrapsTask(vt)); Assert.Equal(84, await vt); async ValueTask ValueTaskReturningAsyncMethod(int result) { for (int i = 0; i < yields; i++) await Task.Yield(); return result; } } /// Gets whether the ValueTask has a non-null Task. private static bool WrapsTask(ValueTask vt) => ReferenceEquals(vt.AsTask(), vt.AsTask()); private struct DelegateStateMachine : IAsyncStateMachine { internal Action MoveNextDelegate; public void MoveNext() => MoveNextDelegate?.Invoke(); public void SetStateMachine(IAsyncStateMachine stateMachine) { } } } }