// 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.Linq; using System.Reflection; using System.Runtime.CompilerServices; using Xunit; namespace System.Threading.Tasks.Tests { public class ValueTaskTests { [Fact] public void DefaultValueTask_ValueType_DefaultValue() { Assert.True(default(ValueTask).IsCompleted); Assert.True(default(ValueTask).IsCompletedSuccessfully); Assert.False(default(ValueTask).IsFaulted); Assert.False(default(ValueTask).IsCanceled); Assert.Equal(0, default(ValueTask).Result); Assert.True(default(ValueTask).IsCompleted); Assert.True(default(ValueTask).IsCompletedSuccessfully); Assert.False(default(ValueTask).IsFaulted); Assert.False(default(ValueTask).IsCanceled); Assert.Equal(null, default(ValueTask).Result); } [Fact] public void CreateFromValue_IsRanToCompletion() { ValueTask t = new ValueTask(42); Assert.True(t.IsCompleted); Assert.True(t.IsCompletedSuccessfully); Assert.False(t.IsFaulted); Assert.False(t.IsCanceled); Assert.Equal(42, t.Result); } [Fact] public void CreateFromCompletedTask_IsRanToCompletion() { ValueTask t = new ValueTask(Task.FromResult(42)); Assert.True(t.IsCompleted); Assert.True(t.IsCompletedSuccessfully); Assert.False(t.IsFaulted); Assert.False(t.IsCanceled); Assert.Equal(42, t.Result); } [Fact] public void CreateFromNotCompletedTask_IsNotRanToCompletion() { var tcs = new TaskCompletionSource(); ValueTask t = new ValueTask(tcs.Task); Assert.False(t.IsCompleted); Assert.False(t.IsCompletedSuccessfully); Assert.False(t.IsFaulted); Assert.False(t.IsCanceled); tcs.SetResult(42); Assert.Equal(42, t.Result); Assert.True(t.IsCompleted); Assert.True(t.IsCompletedSuccessfully); Assert.False(t.IsFaulted); Assert.False(t.IsCanceled); } [Fact] public void CreateFromNullTask_Throws() { Assert.Throws(() => new ValueTask((Task)null)); Assert.Throws(() => new ValueTask((Task)null)); } [Fact] public void CreateFromTask_AsTaskIdempotent() { Task source = Task.FromResult(42); ValueTask t = new ValueTask(source); Assert.Same(source, t.AsTask()); Assert.Same(t.AsTask(), t.AsTask()); } [Fact] public void CreateFromValue_AsTaskNotIdempotent() { ValueTask t = new ValueTask(42); Assert.NotSame(Task.FromResult(42), t.AsTask()); Assert.NotSame(t.AsTask(), t.AsTask()); } [Fact] public async Task CreateFromValue_Await() { ValueTask t = new ValueTask(42); Assert.Equal(42, await t); Assert.Equal(42, await t.ConfigureAwait(false)); Assert.Equal(42, await t.ConfigureAwait(true)); } [Fact] public async Task CreateFromTask_Await_Normal() { Task source = Task.Delay(1).ContinueWith(_ => 42); ValueTask t = new ValueTask(source); Assert.Equal(42, await t); } [Fact] public async Task CreateFromTask_Await_ConfigureAwaitFalse() { Task source = Task.Delay(1).ContinueWith(_ => 42); ValueTask t = new ValueTask(source); Assert.Equal(42, await t.ConfigureAwait(false)); } [Fact] public async Task CreateFromTask_Await_ConfigureAwaitTrue() { Task source = Task.Delay(1).ContinueWith(_ => 42); ValueTask t = new ValueTask(source); Assert.Equal(42, await t.ConfigureAwait(true)); } [Fact] public async Task Awaiter_OnCompleted() { // Since ValueTask implements both OnCompleted and UnsafeOnCompleted, // OnCompleted typically won't be used by await, so we add an explicit test // for it here. ValueTask t = new ValueTask(42); var tcs = new TaskCompletionSource(); t.GetAwaiter().OnCompleted(() => tcs.SetResult(true)); await tcs.Task; } [Theory] [InlineData(true)] [InlineData(false)] public async Task ConfiguredAwaiter_OnCompleted(bool continueOnCapturedContext) { // Since ValueTask implements both OnCompleted and UnsafeOnCompleted, // OnCompleted typically won't be used by await, so we add an explicit test // for it here. ValueTask t = new ValueTask(42); var tcs = new TaskCompletionSource(); t.ConfigureAwait(continueOnCapturedContext).GetAwaiter().OnCompleted(() => tcs.SetResult(true)); await tcs.Task; } [Fact] public async Task Awaiter_ContinuesOnCapturedContext() { await Task.Run(() => { var tsc = new TrackingSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(tsc); try { ValueTask t = new ValueTask(42); var mres = new ManualResetEventSlim(); t.GetAwaiter().OnCompleted(() => mres.Set()); Assert.True(mres.Wait(10000)); Assert.Equal(1, tsc.Posts); } finally { SynchronizationContext.SetSynchronizationContext(null); } }); } [Theory] [InlineData(true)] [InlineData(false)] public async Task ConfiguredAwaiter_ContinuesOnCapturedContext(bool continueOnCapturedContext) { await Task.Run(() => { var tsc = new TrackingSynchronizationContext(); SynchronizationContext.SetSynchronizationContext(tsc); try { ValueTask t = new ValueTask(42); var mres = new ManualResetEventSlim(); t.ConfigureAwait(continueOnCapturedContext).GetAwaiter().OnCompleted(() => mres.Set()); Assert.True(mres.Wait(10000)); Assert.Equal(continueOnCapturedContext ? 1 : 0, tsc.Posts); } finally { SynchronizationContext.SetSynchronizationContext(null); } }); } [Fact] public void GetHashCode_ContainsResult() { ValueTask t = new ValueTask(42); Assert.Equal(t.Result.GetHashCode(), t.GetHashCode()); } [Fact] public void GetHashCode_ContainsTask() { ValueTask t = new ValueTask(Task.FromResult("42")); Assert.Equal(t.AsTask().GetHashCode(), t.GetHashCode()); } [Fact] public void GetHashCode_ContainsNull() { ValueTask t = new ValueTask((string)null); Assert.Equal(0, t.GetHashCode()); } [Fact] public void OperatorEquals() { Assert.True(new ValueTask(42) == new ValueTask(42)); Assert.False(new ValueTask(42) == new ValueTask(43)); Assert.True(new ValueTask("42") == new ValueTask("42")); Assert.True(new ValueTask((string)null) == new ValueTask((string)null)); Assert.False(new ValueTask("42") == new ValueTask((string)null)); Assert.False(new ValueTask((string)null) == new ValueTask("42")); Assert.False(new ValueTask(42) == new ValueTask(Task.FromResult(42))); Assert.False(new ValueTask(Task.FromResult(42)) == new ValueTask(42)); } [Fact] public void OperatorNotEquals() { Assert.False(new ValueTask(42) != new ValueTask(42)); Assert.True(new ValueTask(42) != new ValueTask(43)); Assert.False(new ValueTask("42") != new ValueTask("42")); Assert.False(new ValueTask((string)null) != new ValueTask((string)null)); Assert.True(new ValueTask("42") != new ValueTask((string)null)); Assert.True(new ValueTask((string)null) != new ValueTask("42")); Assert.True(new ValueTask(42) != new ValueTask(Task.FromResult(42))); Assert.True(new ValueTask(Task.FromResult(42)) != new ValueTask(42)); } [Fact] public void Equals_ValueTask() { Assert.True(new ValueTask(42).Equals(new ValueTask(42))); Assert.False(new ValueTask(42).Equals(new ValueTask(43))); Assert.True(new ValueTask("42").Equals(new ValueTask("42"))); Assert.True(new ValueTask((string)null).Equals(new ValueTask((string)null))); Assert.False(new ValueTask("42").Equals(new ValueTask((string)null))); Assert.False(new ValueTask((string)null).Equals(new ValueTask("42"))); Assert.False(new ValueTask(42).Equals(new ValueTask(Task.FromResult(42)))); Assert.False(new ValueTask(Task.FromResult(42)).Equals(new ValueTask(42))); } [Fact] public void Equals_Object() { Assert.True(new ValueTask(42).Equals((object)new ValueTask(42))); Assert.False(new ValueTask(42).Equals((object)new ValueTask(43))); Assert.True(new ValueTask("42").Equals((object)new ValueTask("42"))); Assert.True(new ValueTask((string)null).Equals((object)new ValueTask((string)null))); Assert.False(new ValueTask("42").Equals((object)new ValueTask((string)null))); Assert.False(new ValueTask((string)null).Equals((object)new ValueTask("42"))); Assert.False(new ValueTask(42).Equals((object)new ValueTask(Task.FromResult(42)))); Assert.False(new ValueTask(Task.FromResult(42)).Equals((object)new ValueTask(42))); Assert.False(new ValueTask(42).Equals((object)null)); Assert.False(new ValueTask(42).Equals(new object())); Assert.False(new ValueTask(42).Equals((object)42)); } [Fact] public void ToString_Success() { Assert.Equal("Hello", new ValueTask("Hello").ToString()); Assert.Equal("Hello", new ValueTask(Task.FromResult("Hello")).ToString()); Assert.Equal("42", new ValueTask(42).ToString()); Assert.Equal("42", new ValueTask(Task.FromResult(42)).ToString()); Assert.Same(string.Empty, new ValueTask(string.Empty).ToString()); Assert.Same(string.Empty, new ValueTask(Task.FromResult(string.Empty)).ToString()); Assert.Same(string.Empty, new ValueTask(Task.FromException(new InvalidOperationException())).ToString()); Assert.Same(string.Empty, new ValueTask(Task.FromException(new OperationCanceledException())).ToString()); Assert.Same(string.Empty, new ValueTask(Task.FromCanceled(new CancellationToken(true))).ToString()); Assert.Equal("0", default(ValueTask).ToString()); Assert.Same(string.Empty, default(ValueTask).ToString()); Assert.Same(string.Empty, new ValueTask((string)null).ToString()); Assert.Same(string.Empty, new ValueTask(Task.FromResult(null)).ToString()); Assert.Same(string.Empty, new ValueTask(new TaskCompletionSource().Task).ToString()); } [Theory] [InlineData(typeof(ValueTask<>))] [InlineData(typeof(ValueTask))] [InlineData(typeof(ValueTask))] public void AsyncMethodBuilderAttribute_ValueTaskAttributed(Type valueTaskType) { CustomAttributeData cad = valueTaskType.GetTypeInfo().CustomAttributes.Single(attr => attr.AttributeType == typeof(AsyncMethodBuilderAttribute)); Type builderTypeCtorArg = (Type)cad.ConstructorArguments[0].Value; Assert.Equal(typeof(AsyncValueTaskMethodBuilder<>), builderTypeCtorArg); AsyncMethodBuilderAttribute amba = valueTaskType.GetTypeInfo().GetCustomAttribute(); Assert.Equal(builderTypeCtorArg, amba.BuilderType); } private sealed class TrackingSynchronizationContext : SynchronizationContext { internal int Posts { get; set; } public override void Post(SendOrPostCallback d, object state) { Posts++; base.Post(d, state); } } } }