diff --git a/docs/release-notes/.FSharp.Core/11.0.100.md b/docs/release-notes/.FSharp.Core/11.0.100.md index 97c667e0eb3..6904710cc82 100644 --- a/docs/release-notes/.FSharp.Core/11.0.100.md +++ b/docs/release-notes/.FSharp.Core/11.0.100.md @@ -8,3 +8,5 @@ ### Added * `Async.RunSynchronouslyImmediate`: runs work on the calling thread until the first asynchronous suspension (as opposed to `RunSynchronously`, which immediately offloads if not on a background and/or threadpool thread). ([Issue #1042](https://github.com/fsharp/fslang-suggestions/issues/1042), [PR #19804](https://github.com/dotnet/fsharp/pull/19804)) +* Added modules for `Async`, `Task` and `ValueTask` with consistent `result`, `map`, `bind`, `ignore`, `catchWith`, `catch`, and `empty` functions ([LanguageSuggestion #1466](https://github.com/fsharp/fslang-suggestions/issues/1466), [PR #19844](https://github.com/dotnet/fsharp/pull/19844)) +* Added conversion functions `Task.ofValueTask` and `ValueTask.ofTask`. ([LanguageSuggestion #1466](https://github.com/fsharp/fslang-suggestions/issues/1466), [PR #19844](https://github.com/dotnet/fsharp/pull/19844)) diff --git a/src/FSharp.Core/async.fs b/src/FSharp.Core/async.fs index 02994a3a886..73c004b4260 100644 --- a/src/FSharp.Core/async.fs +++ b/src/FSharp.Core/async.fs @@ -2354,3 +2354,45 @@ module WebExtensions = start = (fun userToken -> this.DownloadFileAsync(address, fileName, userToken)), result = (fun _ -> ()) ) + +[] +module Async = + + [] + let inline result (value: 'T) : Async<'T> = + async.Return value + + [] + let inline map ([] mapping: 'T -> 'U) (computation: Async<'T>) : Async<'U> = + async.Bind(computation, mapping >> async.Return) + + [] + let inline bind ([] binder: 'T -> Async<'U>) (computation: Async<'T>) : Async<'U> = + async.Bind(computation, binder) + + [] + [] + let inline ignore<'T> (computation: Async<'T>) : Async = + Async.Ignore computation + + [] + let catchWith (handler: exn -> 'T) (computation: Async<'T>) : Async<'T> = + async { + try + return! computation + with e -> + return handler e + } + + [] + let catch (computation: Async<'T>) : Async> = + async { + try + let! v = computation + return Result.Ok v + with e -> + return Result.Error e + } + + [] + let empty: Async = async.Zero() diff --git a/src/FSharp.Core/async.fsi b/src/FSharp.Core/async.fsi index a629ab50fc5..1af0fab84db 100644 --- a/src/FSharp.Core/async.fsi +++ b/src/FSharp.Core/async.fsi @@ -1006,7 +1006,7 @@ namespace Microsoft.FSharp.Control /// use file = System.IO.File.OpenRead(filename) /// printfn "Reading from file %s." filename /// // Throw away the data being read. - /// do! file.AsyncRead(numBytes) |> Async.Ignore + /// do! file.AsyncRead(numBytes) |> Async.ignore<byte[]> /// } /// readFile "example.txt" 42 |> Async.Start /// @@ -1584,3 +1584,131 @@ namespace Microsoft.FSharp.Control module internal AsyncBuilderImpl = val async : AsyncBuilder + /// Contains camelCase module-level functions for computations. + /// + /// Async Programming + [] + module Async = + + /// Creates an asynchronous computation that returns the given value. + /// + /// The value to return. + /// + /// An asynchronous computation that returns value when executed. + /// + /// + /// + /// let computation = Async.result 42 + /// computation |> Async.RunSynchronouslyImmediate // evaluates to 42 + /// + /// + [] + val inline result: value: 'T -> Async<'T> + + /// Creates an asynchronous computation that applies the mapping function to the result of the given computation. + /// + /// The function to apply to the result. + /// The input computation. + /// + /// An asynchronous computation that applies mapping to the result of computation. + /// + /// + /// + /// let computation = Async.result 21 |> Async.map (fun x -> x * 2) + /// computation |> Async.RunSynchronouslyImmediate // evaluates to 42 + /// + /// + [] + val inline map: mapping: ('T -> 'U) -> computation: Async<'T> -> Async<'U> + + /// Creates an asynchronous computation that passes the result of the given computation to the binder function. + /// + /// A function that takes the result of the computation and returns a new asynchronous computation. + /// The input computation. + /// + /// An asynchronous computation that performs a monadic bind on the result of computation. + /// + /// + /// + /// let computation = Async.result 21 |> Async.bind (fun x -> Async.result (x * 2)) + /// computation |> Async.RunSynchronouslyImmediate // evaluates to 42 + /// + /// + [] + val inline bind: binder: ('T -> Async<'U>) -> computation: Async<'T> -> Async<'U> + + /// Creates an asynchronous computation that runs the given computation and ignores its result. + /// + /// The input computation. + /// + /// A computation that is equivalent to the input computation, but disregards the result. + /// + /// + /// + /// let readFile filename numBytes: Async<unit> = + /// async { + /// use file = System.IO.File.OpenRead(filename) + /// do! file.AsyncRead(numBytes) |> Async.ignore<byte[]> + /// } + /// + /// + /// + /// + /// let computation : Async<unit> = Async.result 42 |> Async.ignore<int> + /// computation |> Async.RunSynchronously // evaluates to () + /// + /// + [] + [] + val inline ignore<'T> : computation: Async<'T> -> Async + + /// Creates an asynchronous computation that yields the original result on success, or the result of + /// handler exn for non-cancellation exceptions. + /// OperationCanceledException and derived types such as TaskCanceledException propagate unchanged, + /// and therefore are never passed to handler. + /// + /// A function to handle (non-cancellation) exceptions, yielding a recovery value based on the exception. + /// Any exception thrown by handler will propagate. + /// The input computation. + /// An asynchronous computation that yields the result of computation on success, + /// or handler exn on failure. + /// Propagates the underlying cancellation exception where cancellation occurs. + /// + /// + /// let safeDiv x y = + /// async { return x / y } + /// |> Async.catchWith (fun _ -> 0) + /// safeDiv 10 0 |> Async.RunSynchronouslyImmediate // evaluates to 0 + /// + /// + [] + val catchWith: handler: (exn -> 'T) -> computation: Async<'T> -> Async<'T> + + /// Creates an asynchronous computation that reifies the outcome of the given computation as a Result: + /// Ok on success, Error on failure, so exceptions become values. Cancellation still propagates. + /// OperationCanceledException and derived types such as TaskCanceledException propagate unchanged. + /// The input computation. + /// An asynchronous computation that yields a Result: Ok with the outcome on success, + /// or Error with the exception on failure. + /// Propagates the underlying cancellation exception when cancellation occurs. + /// + /// + /// let safeDiv x y = + /// async { return x / y } |> Async.catch + /// safeDiv 10 2 |> Async.RunSynchronouslyImmediate // evaluates to Ok 5 + /// safeDiv 10 0 |> Async.RunSynchronouslyImmediate // evaluates to Error (DivideByZeroException ...) + /// + /// + [] + val catch: computation: Async<'T> -> Async> + + /// An asynchronous computation that returns unit. This is equivalent to async.Zero(). + /// + /// + /// + /// Async.empty |> Async.RunSynchronouslyImmediate // evaluates to () + /// + /// + [] + val empty: Async + diff --git a/src/FSharp.Core/tasks.fs b/src/FSharp.Core/tasks.fs index eec12a86c63..eda1d005c83 100644 --- a/src/FSharp.Core/tasks.fs +++ b/src/FSharp.Core/tasks.fs @@ -716,3 +716,154 @@ module LowPlusPriority = this.Bind(computation, fun (result2: ^TResult2) -> this.Return struct (result1, result2)) ) ) + +namespace Microsoft.FSharp.Control + +open System.Threading.Tasks +open Microsoft.FSharp.Core +open TaskBuilder +open Microsoft.FSharp.Control.TaskBuilderExtensions +open Microsoft.FSharp.Control.TaskBuilderExtensions.LowPriority +open Microsoft.FSharp.Control.TaskBuilderExtensions.HighPriority + +[] +[] +module Task = + + [] + let inline result (value: 'T) : Task<'T> = + Task.FromResult value + + [] + let empty: Task = result () + + [] + let inline bind ([] binder: 'T -> Task<'U>) (task: Task<'T>) : Task<'U> = + if task.Status = TaskStatus.RanToCompletion then + try + binder task.Result + with e -> + Task.FromException<'U>(e) + else + TaskBuilder.task { + let! v = task + return! binder v + } + + [] + let inline map ([] mapping: 'T -> 'U) (task: Task<'T>) : Task<'U> = + if task.Status = TaskStatus.RanToCompletion then + try + mapping task.Result |> result + with e -> + Task.FromException<'U>(e) + else + TaskBuilder.task { + let! v = task + return mapping v + } + + [] + [] + let inline ignore<'T> (task: Task<'T>) : Task = + if task.Status = TaskStatus.RanToCompletion then + empty + else + map ignore task + + [] + let inline catchWith ([] handler: exn -> 'T) (task: Task<'T>) : Task<'T> = + if task.Status = TaskStatus.RanToCompletion then + task + else + TaskBuilder.task { + try + return! task + with + | :? System.OperationCanceledException as e -> return! raise e + | e -> return handler e + } + + [] + let catch (task: Task<'T>) : Task> = + task |> map Ok |> catchWith Error + +#if NETSTANDARD2_1 + [] + let inline ofValueTask (valueTask: ValueTask<'T>) : Task<'T> = + valueTask.AsTask() +#endif + +#if NETSTANDARD2_1 +[] +[] +module ValueTask = + + [] + let inline result (value: 'T) : ValueTask<'T> = + ValueTask<'T>(value) + + [] + let empty: ValueTask = result () + + [] + let inline ofTask (task: Task<'T>) : ValueTask<'T> = + ValueTask<'T>(task) + + [] + let inline bind ([] binder: 'T -> ValueTask<'U>) (task: ValueTask<'T>) : ValueTask<'U> = + if task.IsCompletedSuccessfully then + try + binder task.Result + with e -> + Task.FromException<'U>(e) |> ofTask + else + let t: Task<'U> = + TaskBuilder.task { + let! v = task + return! binder v + } + + ValueTask<'U>(t) + + [] + let inline map ([] mapping: 'T -> 'U) (task: ValueTask<'T>) : ValueTask<'U> = + if task.IsCompletedSuccessfully then + try + mapping task.Result |> result + with e -> + Task.FromException<'U>(e) |> ofTask + else + let t: Task<'U> = + TaskBuilder.task { + let! v = task + return mapping v + } + + ValueTask<'U>(t) + + [] + [] + let inline ignore<'T> (task: ValueTask<'T>) : ValueTask = + map ignore task + + [] + let inline catchWith ([] handler: exn -> 'T) (task: ValueTask<'T>) : ValueTask<'T> = + if task.IsCompletedSuccessfully then + task + else + let t: Task<'T> = + TaskBuilder.task { + try + return! task + with + | :? System.OperationCanceledException as e -> return! raise e + | e -> return handler e + } + + ValueTask<'T>(t) + + [] + let catch (task: ValueTask<'T>) : ValueTask> = + task |> map Ok |> catchWith Error +#endif diff --git a/src/FSharp.Core/tasks.fsi b/src/FSharp.Core/tasks.fsi index 76d84bcfd28..a4e6806d824 100644 --- a/src/FSharp.Core/tasks.fsi +++ b/src/FSharp.Core/tasks.fsi @@ -457,3 +457,288 @@ module HighPriority = /// member inline MergeSources< ^TResult1, ^TResult2> : task1: Task< ^TResult1 > * task2: Task< ^TResult2 > -> Task + +namespace Microsoft.FSharp.Control + +open System.Threading.Tasks +open Microsoft.FSharp.Core + +/// Contains camelCase module-level functions for computations. +/// +/// Async Programming +[] +[] +module Task = + + /// Creates a task that returns the given value. + /// + /// The value to return. + /// + /// A completed task that returns value. + /// + /// + /// + /// let t = Task.result 42 + /// t.Result // evaluates to 42 + /// + /// + [] + val inline result: value: 'T -> Task<'T> + + /// Creates a task that applies the mapping function to the result of the given task. + /// + /// The function to apply to the result. + /// The input task. + /// + /// A task that applies mapping to the result of task. + /// + /// + /// + /// let t = Task.result 21 |> Task.map (fun x -> x * 2) + /// t.Result // evaluates to 42 + /// + /// + [] + val inline map: mapping: ('T -> 'U) -> task: Task<'T> -> Task<'U> + + /// Creates a task that passes the result of the given task to the binder function. + /// + /// A function that takes the result of the task and returns a new task. + /// The input task. + /// + /// A task that performs a monadic bind on the result of task. + /// + /// + /// + /// let t = Task.result 21 |> Task.bind (fun x -> Task.result (x * 2)) + /// t.Result // evaluates to 42 + /// + /// + [] + val inline bind: binder: ('T -> Task<'U>) -> task: Task<'T> -> Task<'U> + + /// Creates a task that runs the given task and ignores its result. + /// + /// The input task. + /// + /// A task that is equivalent to the input task, but disregards the result. + /// + /// + /// + /// let t : Task<unit> = Task.result 42 |> Task.ignore<int> + /// t.Result // evaluates to () + /// + /// + [] + [] + val inline ignore<'T> : task: Task<'T> -> Task + + /// Creates a Task that yields the original result on success, or the result of + /// handler exn for non-cancellation exceptions. + /// OperationCanceledException and derived types such as TaskCanceledException propagate unchanged + /// (and the task remains Canceled) in order to maintain cancellation semantics, and therefore are never passed to handler. + /// + /// A function to handle (non-cancellation) exceptions, yielding a recovery value based on the exception. + /// Any exception thrown by handler will propagate. + /// The input Task. + /// A Task that yields the result of task on success, or handler exn on failure. + /// Propagates the underlying cancellation exception when task is canceled. + /// + /// + /// let safeDiv x y = + /// task { return x / y } + /// |> Task.catchWith (fun _ -> 0) + /// (safeDiv 10 0).Result // evaluates to 0 + /// + /// + [] + val inline catchWith: handler: (exn -> 'T) -> task: Task<'T> -> Task<'T> + + /// Creates a Task that reifies the outcome of the given Task as a Result: + /// Ok on success, Error on failure, so faults become values. Cancellation still propagates. + /// OperationCanceledException and derived types such as TaskCanceledException propagate unchanged + /// (and the task remains Canceled) in order to maintain cancellation semantics. + /// The input Task. + /// A Task that yields a Result: Ok with the outcome on success, + /// or Error with the exception on failure. + /// Propagates the underlying cancellation exception when task is canceled. + /// + /// + /// let safeDiv x y = task { return x / y } |> Task.catch + /// (safeDiv 10 2).Result // evaluates to Ok 5 + /// (safeDiv 10 0).Result // evaluates to Error (DivideByZeroException ...) + /// + /// + [] + val catch: task: Task<'T> -> Task> + + /// A completed task that returns unit. This is a Task<unit> (not the non-generic Task.CompletedTask). + /// + /// + /// + /// Task.empty.Result // evaluates to () + /// + /// + [] + val empty: Task + +#if NETSTANDARD2_1 + /// Converts a to a . + /// + /// The input value task. + /// + /// A task equivalent to the given value task. + /// + /// + /// + /// let vt = ValueTask<int>(42) + /// let t = Task.ofValueTask vt + /// t.Result // evaluates to 42 + /// + /// + [] + val inline ofValueTask: valueTask: ValueTask<'T> -> Task<'T> +#endif + +#if NETSTANDARD2_1 +/// Contains camelCase module-level functions for computations. +/// +/// Async Programming +[] +[] +module ValueTask = + + /// Creates a value task that returns the given value. + /// + /// The value to return. + /// + /// A completed value task that returns value. + /// + /// + /// + /// let vt = ValueTask.result 42 + /// vt.Result // evaluates to 42 + /// + /// + [] + val inline result: value: 'T -> ValueTask<'T> + + /// Creates a value task that applies the mapping function to the result of the given value task. + /// + /// The function to apply to the result. + /// The input value task. + /// + /// A value task that applies mapping to the result of task. + /// + /// + /// + /// let vt = ValueTask.result 21 |> ValueTask.map (fun x -> x * 2) + /// vt.Result // evaluates to 42 + /// + /// + [] + val inline map: mapping: ('T -> 'U) -> task: ValueTask<'T> -> ValueTask<'U> + + /// Creates a value task that passes the result of the given value task to the binder function. + /// + /// A function that takes the result of the value task and returns a new value task. + /// The input value task. + /// + /// A value task that performs a monadic bind on the result of task. + /// + /// + /// + /// let vt = ValueTask.result 21 |> ValueTask.bind (fun x -> ValueTask.result (x * 2)) + /// vt.Result // evaluates to 42 + /// + /// + [] + val inline bind: binder: ('T -> ValueTask<'U>) -> task: ValueTask<'T> -> ValueTask<'U> + + /// Creates a value task that runs the given value task and ignores its result. + /// + /// When the value task is already synchronously complete, this avoids allocating a Task. + /// + /// The input value task. + /// + /// A value task that is equivalent to the input value task, but disregards the result. + /// + /// + /// + /// let vt : ValueTask<unit> = ValueTask.result 42 |> ValueTask.ignore<int> + /// vt.Result // evaluates to () + /// + /// + [] + [] + val inline ignore<'T> : task: ValueTask<'T> -> ValueTask + + /// Creates a ValueTask that yields the original result on success, or the result of + /// handler exn for non-cancellation exceptions. + /// OperationCanceledException and derived types such as TaskCanceledException propagate unchanged + /// (and the task remains Canceled) in order to maintain cancellation semantics, + /// and therefore are never passed to handler. + /// + /// A function to handle (non-cancellation) exceptions, yielding a recovery value based on the exception. + /// Any exception thrown by handler will propagate. + /// The input ValueTask. + /// A ValueTask that yields the result of task on success, + /// or handler exn on failure. + /// Propagates the underlying cancellation exception when task is canceled. + /// + /// + /// + /// let safeDiv x y = + /// task { return x / y } + /// |> ValueTask.ofTask + /// |> ValueTask.catchWith (fun _ -> 0) + /// (safeDiv 10 0).Result // evaluates to 0 + /// + /// + [] + val inline catchWith: handler: (exn -> 'T) -> task: ValueTask<'T> -> ValueTask<'T> + + /// Creates a ValueTask that reifies the outcome of the given ValueTask as a Result: + /// Ok on success, Error on failure, so faults become values. Cancellation still propagates. + /// OperationCanceledException and derived types such as TaskCanceledException propagate unchanged + /// (and the task remains Canceled) in order to maintain cancellation semantics. + /// The input ValueTask. + /// A ValueTask that yields a Result: Ok with the outcome on success, + /// or Error with the exception on failure. + /// Propagates the underlying cancellation exception when task is canceled. + /// + /// + /// let safeDiv x y = task { return x / y } |> ValueTask.ofTask |> ValueTask.catch + /// (safeDiv 10 2).Result // evaluates to Ok 5 + /// (safeDiv 10 0).Result // evaluates to Error (DivideByZeroException ...) + /// + /// + [] + val catch: task: ValueTask<'T> -> ValueTask> + + /// A completed value task that returns unit. + /// + /// + /// + /// ValueTask.empty.Result // evaluates to () + /// + /// + [] + val empty: ValueTask + + /// Converts a to a . + /// + /// The input task. + /// + /// A value task equivalent to the given task. + /// + /// + /// + /// let t = Task.FromResult 42 + /// let vt = ValueTask.ofTask t + /// vt.Result // evaluates to 42 + /// + /// + [] + val inline ofTask: task: Task<'T> -> ValueTask<'T> +#endif diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl index 89fb1bb6146..175102ee368 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.debug.bsl @@ -603,8 +603,8 @@ Microsoft.FSharp.Collections.SetModule: Microsoft.FSharp.Collections.FSharpSet`1 Microsoft.FSharp.Collections.SetModule: Microsoft.FSharp.Collections.FSharpSet`1[T] UnionMany[T](System.Collections.Generic.IEnumerable`1[Microsoft.FSharp.Collections.FSharpSet`1[T]]) Microsoft.FSharp.Collections.SetModule: Microsoft.FSharp.Collections.FSharpSet`1[T] Union[T](Microsoft.FSharp.Collections.FSharpSet`1[T], Microsoft.FSharp.Collections.FSharpSet`1[T]) Microsoft.FSharp.Collections.SetModule: System.Collections.Generic.IEnumerable`1[T] ToSeq[T](Microsoft.FSharp.Collections.FSharpSet`1[T]) -Microsoft.FSharp.Collections.SetModule: System.Tuple`2[Microsoft.FSharp.Collections.FSharpSet`1[T],Microsoft.FSharp.Collections.FSharpSet`1[T]] Partition[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], Microsoft.FSharp.Collections.FSharpSet`1[T]) Microsoft.FSharp.Collections.SetModule: System.Tuple`2[Microsoft.FSharp.Collections.FSharpSet`1[T1],Microsoft.FSharp.Collections.FSharpSet`1[T2]] PartitionWith[T,T1,T2](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpChoice`2[T1,T2]], Microsoft.FSharp.Collections.FSharpSet`1[T]) +Microsoft.FSharp.Collections.SetModule: System.Tuple`2[Microsoft.FSharp.Collections.FSharpSet`1[T],Microsoft.FSharp.Collections.FSharpSet`1[T]] Partition[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], Microsoft.FSharp.Collections.FSharpSet`1[T]) Microsoft.FSharp.Collections.SetModule: T MaxElement[T](Microsoft.FSharp.Collections.FSharpSet`1[T]) Microsoft.FSharp.Collections.SetModule: T MinElement[T](Microsoft.FSharp.Collections.FSharpSet`1[T]) Microsoft.FSharp.Collections.SetModule: TState FoldBack[T,TState](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[TState,TState]], Microsoft.FSharp.Collections.FSharpSet`1[T], TState) @@ -617,6 +617,14 @@ Microsoft.FSharp.Control.AsyncActivation`1[T]: Microsoft.FSharp.Control.AsyncRet Microsoft.FSharp.Control.AsyncActivation`1[T]: Microsoft.FSharp.Control.AsyncReturn OnSuccess(T) Microsoft.FSharp.Control.AsyncActivation`1[T]: Microsoft.FSharp.Control.AsyncReturn Success(Microsoft.FSharp.Control.AsyncActivation`1[T], T) Microsoft.FSharp.Control.AsyncActivation`1[T]: Void OnExceptionRaised() +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpResult`2[T,System.Exception]] Catch[T](Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Empty +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Ignore[T](Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] get_Empty() +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[TResult] Bind[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Control.FSharpAsync`1[TResult]], Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[TResult] Map[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[T] CatchWith[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,T], Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[T] Result[T](T) Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn Bind[T,TResult](Microsoft.FSharp.Control.AsyncActivation`1[T], Microsoft.FSharp.Control.FSharpAsync`1[TResult], Microsoft.FSharp.Core.FSharpFunc`2[TResult,Microsoft.FSharp.Control.FSharpAsync`1[T]]) Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn CallThenInvoke[T,TResult](Microsoft.FSharp.Control.AsyncActivation`1[T], TResult, Microsoft.FSharp.Core.FSharpFunc`2[TResult,Microsoft.FSharp.Control.FSharpAsync`1[T]]) Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn Invoke[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Control.AsyncActivation`1[T]) @@ -801,6 +809,14 @@ Microsoft.FSharp.Control.TaskBuilderModule: Microsoft.FSharp.Control.BackgroundT Microsoft.FSharp.Control.TaskBuilderModule: Microsoft.FSharp.Control.TaskBuilder get_task() Microsoft.FSharp.Control.TaskBuilderModule: Microsoft.FSharp.Control.TaskBuilder task Microsoft.FSharp.Control.TaskStateMachineData`1[T]: System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[T] MethodBuilder +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.FSharpResult`2[T,System.Exception]] Catch[T](System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.Unit] Empty +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.Unit] Ignore[T](System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.Unit] get_Empty() +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[TResult] Bind[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Threading.Tasks.Task`1[TResult]], System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[TResult] Map[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[T] CatchWith[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,T], System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[T] Result[T](T) Microsoft.FSharp.Control.TaskStateMachineData`1[T]: T Result Microsoft.FSharp.Control.WebExtensions: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] AsyncDownloadFile(System.Net.WebClient, System.Uri, System.String) Microsoft.FSharp.Control.WebExtensions: Microsoft.FSharp.Control.FSharpAsync`1[System.Byte[]] AsyncDownloadData(System.Net.WebClient, System.Uri) @@ -2668,4 +2684,4 @@ Microsoft.FSharp.Reflection.UnionCaseInfo: System.String Name Microsoft.FSharp.Reflection.UnionCaseInfo: System.String ToString() Microsoft.FSharp.Reflection.UnionCaseInfo: System.String get_Name() Microsoft.FSharp.Reflection.UnionCaseInfo: System.Type DeclaringType -Microsoft.FSharp.Reflection.UnionCaseInfo: System.Type get_DeclaringType() \ No newline at end of file +Microsoft.FSharp.Reflection.UnionCaseInfo: System.Type get_DeclaringType() diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl index 6d29205d290..cdf68c2d1ef 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard20.release.bsl @@ -603,8 +603,8 @@ Microsoft.FSharp.Collections.SetModule: Microsoft.FSharp.Collections.FSharpSet`1 Microsoft.FSharp.Collections.SetModule: Microsoft.FSharp.Collections.FSharpSet`1[T] UnionMany[T](System.Collections.Generic.IEnumerable`1[Microsoft.FSharp.Collections.FSharpSet`1[T]]) Microsoft.FSharp.Collections.SetModule: Microsoft.FSharp.Collections.FSharpSet`1[T] Union[T](Microsoft.FSharp.Collections.FSharpSet`1[T], Microsoft.FSharp.Collections.FSharpSet`1[T]) Microsoft.FSharp.Collections.SetModule: System.Collections.Generic.IEnumerable`1[T] ToSeq[T](Microsoft.FSharp.Collections.FSharpSet`1[T]) -Microsoft.FSharp.Collections.SetModule: System.Tuple`2[Microsoft.FSharp.Collections.FSharpSet`1[T],Microsoft.FSharp.Collections.FSharpSet`1[T]] Partition[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], Microsoft.FSharp.Collections.FSharpSet`1[T]) Microsoft.FSharp.Collections.SetModule: System.Tuple`2[Microsoft.FSharp.Collections.FSharpSet`1[T1],Microsoft.FSharp.Collections.FSharpSet`1[T2]] PartitionWith[T,T1,T2](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpChoice`2[T1,T2]], Microsoft.FSharp.Collections.FSharpSet`1[T]) +Microsoft.FSharp.Collections.SetModule: System.Tuple`2[Microsoft.FSharp.Collections.FSharpSet`1[T],Microsoft.FSharp.Collections.FSharpSet`1[T]] Partition[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], Microsoft.FSharp.Collections.FSharpSet`1[T]) Microsoft.FSharp.Collections.SetModule: T MaxElement[T](Microsoft.FSharp.Collections.FSharpSet`1[T]) Microsoft.FSharp.Collections.SetModule: T MinElement[T](Microsoft.FSharp.Collections.FSharpSet`1[T]) Microsoft.FSharp.Collections.SetModule: TState FoldBack[T,TState](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[TState,TState]], Microsoft.FSharp.Collections.FSharpSet`1[T], TState) @@ -617,6 +617,14 @@ Microsoft.FSharp.Control.AsyncActivation`1[T]: Microsoft.FSharp.Control.AsyncRet Microsoft.FSharp.Control.AsyncActivation`1[T]: Microsoft.FSharp.Control.AsyncReturn OnSuccess(T) Microsoft.FSharp.Control.AsyncActivation`1[T]: Microsoft.FSharp.Control.AsyncReturn Success(Microsoft.FSharp.Control.AsyncActivation`1[T], T) Microsoft.FSharp.Control.AsyncActivation`1[T]: Void OnExceptionRaised() +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpResult`2[T,System.Exception]] Catch[T](Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Empty +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Ignore[T](Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] get_Empty() +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[TResult] Bind[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Control.FSharpAsync`1[TResult]], Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[TResult] Map[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[T] CatchWith[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,T], Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[T] Result[T](T) Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn Bind[T,TResult](Microsoft.FSharp.Control.AsyncActivation`1[T], Microsoft.FSharp.Control.FSharpAsync`1[TResult], Microsoft.FSharp.Core.FSharpFunc`2[TResult,Microsoft.FSharp.Control.FSharpAsync`1[T]]) Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn CallThenInvoke[T,TResult](Microsoft.FSharp.Control.AsyncActivation`1[T], TResult, Microsoft.FSharp.Core.FSharpFunc`2[TResult,Microsoft.FSharp.Control.FSharpAsync`1[T]]) Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn Invoke[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Control.AsyncActivation`1[T]) @@ -800,6 +808,14 @@ Microsoft.FSharp.Control.TaskBuilderModule: Microsoft.FSharp.Control.BackgroundT Microsoft.FSharp.Control.TaskBuilderModule: Microsoft.FSharp.Control.BackgroundTaskBuilder get_backgroundTask() Microsoft.FSharp.Control.TaskBuilderModule: Microsoft.FSharp.Control.TaskBuilder get_task() Microsoft.FSharp.Control.TaskBuilderModule: Microsoft.FSharp.Control.TaskBuilder task +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.FSharpResult`2[T,System.Exception]] Catch[T](System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.Unit] Empty +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.Unit] Ignore[T](System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.Unit] get_Empty() +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[TResult] Bind[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Threading.Tasks.Task`1[TResult]], System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[TResult] Map[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[T] CatchWith[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,T], System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[T] Result[T](T) Microsoft.FSharp.Control.TaskStateMachineData`1[T]: System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[T] MethodBuilder Microsoft.FSharp.Control.TaskStateMachineData`1[T]: T Result Microsoft.FSharp.Control.WebExtensions: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] AsyncDownloadFile(System.Net.WebClient, System.Uri, System.String) @@ -2667,4 +2683,4 @@ Microsoft.FSharp.Reflection.UnionCaseInfo: System.String Name Microsoft.FSharp.Reflection.UnionCaseInfo: System.String ToString() Microsoft.FSharp.Reflection.UnionCaseInfo: System.String get_Name() Microsoft.FSharp.Reflection.UnionCaseInfo: System.Type DeclaringType -Microsoft.FSharp.Reflection.UnionCaseInfo: System.Type get_DeclaringType() \ No newline at end of file +Microsoft.FSharp.Reflection.UnionCaseInfo: System.Type get_DeclaringType() diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl index d8d7ff44b21..cac5fe9d0ef 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.debug.bsl @@ -605,8 +605,8 @@ Microsoft.FSharp.Collections.SetModule: Microsoft.FSharp.Collections.FSharpSet`1 Microsoft.FSharp.Collections.SetModule: Microsoft.FSharp.Collections.FSharpSet`1[T] UnionMany[T](System.Collections.Generic.IEnumerable`1[Microsoft.FSharp.Collections.FSharpSet`1[T]]) Microsoft.FSharp.Collections.SetModule: Microsoft.FSharp.Collections.FSharpSet`1[T] Union[T](Microsoft.FSharp.Collections.FSharpSet`1[T], Microsoft.FSharp.Collections.FSharpSet`1[T]) Microsoft.FSharp.Collections.SetModule: System.Collections.Generic.IEnumerable`1[T] ToSeq[T](Microsoft.FSharp.Collections.FSharpSet`1[T]) -Microsoft.FSharp.Collections.SetModule: System.Tuple`2[Microsoft.FSharp.Collections.FSharpSet`1[T],Microsoft.FSharp.Collections.FSharpSet`1[T]] Partition[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], Microsoft.FSharp.Collections.FSharpSet`1[T]) Microsoft.FSharp.Collections.SetModule: System.Tuple`2[Microsoft.FSharp.Collections.FSharpSet`1[T1],Microsoft.FSharp.Collections.FSharpSet`1[T2]] PartitionWith[T,T1,T2](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpChoice`2[T1,T2]], Microsoft.FSharp.Collections.FSharpSet`1[T]) +Microsoft.FSharp.Collections.SetModule: System.Tuple`2[Microsoft.FSharp.Collections.FSharpSet`1[T],Microsoft.FSharp.Collections.FSharpSet`1[T]] Partition[T](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Boolean], Microsoft.FSharp.Collections.FSharpSet`1[T]) Microsoft.FSharp.Collections.SetModule: T MaxElement[T](Microsoft.FSharp.Collections.FSharpSet`1[T]) Microsoft.FSharp.Collections.SetModule: T MinElement[T](Microsoft.FSharp.Collections.FSharpSet`1[T]) Microsoft.FSharp.Collections.SetModule: TState FoldBack[T,TState](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Core.FSharpFunc`2[TState,TState]], Microsoft.FSharp.Collections.FSharpSet`1[T], TState) @@ -619,6 +619,14 @@ Microsoft.FSharp.Control.AsyncActivation`1[T]: Microsoft.FSharp.Control.AsyncRet Microsoft.FSharp.Control.AsyncActivation`1[T]: Microsoft.FSharp.Control.AsyncReturn OnSuccess(T) Microsoft.FSharp.Control.AsyncActivation`1[T]: Microsoft.FSharp.Control.AsyncReturn Success(Microsoft.FSharp.Control.AsyncActivation`1[T], T) Microsoft.FSharp.Control.AsyncActivation`1[T]: Void OnExceptionRaised() +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpResult`2[T,System.Exception]] Catch[T](Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Empty +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Ignore[T](Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] get_Empty() +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[TResult] Bind[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Control.FSharpAsync`1[TResult]], Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[TResult] Map[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[T] CatchWith[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,T], Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[T] Result[T](T) Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn Bind[T,TResult](Microsoft.FSharp.Control.AsyncActivation`1[T], Microsoft.FSharp.Control.FSharpAsync`1[TResult], Microsoft.FSharp.Core.FSharpFunc`2[TResult,Microsoft.FSharp.Control.FSharpAsync`1[T]]) Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn CallThenInvoke[T,TResult](Microsoft.FSharp.Control.AsyncActivation`1[T], TResult, Microsoft.FSharp.Core.FSharpFunc`2[TResult,Microsoft.FSharp.Control.FSharpAsync`1[T]]) Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn Invoke[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Control.AsyncActivation`1[T]) @@ -673,8 +681,8 @@ Microsoft.FSharp.Control.FSharpAsync: System.Threading.CancellationToken get_Def Microsoft.FSharp.Control.FSharpAsync: System.Threading.Tasks.Task`1[T] StartAsTask[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.Tasks.TaskCreationOptions], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) Microsoft.FSharp.Control.FSharpAsync: System.Threading.Tasks.Task`1[T] StartImmediateAsTask[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) Microsoft.FSharp.Control.FSharpAsync: System.Tuple`3[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`3[TArg,System.AsyncCallback,System.Object],System.IAsyncResult],Microsoft.FSharp.Core.FSharpFunc`2[System.IAsyncResult,T],Microsoft.FSharp.Core.FSharpFunc`2[System.IAsyncResult,Microsoft.FSharp.Core.Unit]] AsBeginEnd[TArg,T](Microsoft.FSharp.Core.FSharpFunc`2[TArg,Microsoft.FSharp.Control.FSharpAsync`1[T]]) -Microsoft.FSharp.Control.FSharpAsync: T RunSynchronously[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) Microsoft.FSharp.Control.FSharpAsync: T RunSynchronouslyImmediate[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) +Microsoft.FSharp.Control.FSharpAsync: T RunSynchronously[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) Microsoft.FSharp.Control.FSharpAsync: Void CancelDefaultToken() Microsoft.FSharp.Control.FSharpAsync: Void Start(Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) Microsoft.FSharp.Control.FSharpAsync: Void StartImmediate(Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) @@ -803,8 +811,26 @@ Microsoft.FSharp.Control.TaskBuilderModule: Microsoft.FSharp.Control.BackgroundT Microsoft.FSharp.Control.TaskBuilderModule: Microsoft.FSharp.Control.BackgroundTaskBuilder get_backgroundTask() Microsoft.FSharp.Control.TaskBuilderModule: Microsoft.FSharp.Control.TaskBuilder get_task() Microsoft.FSharp.Control.TaskBuilderModule: Microsoft.FSharp.Control.TaskBuilder task +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.FSharpResult`2[T,System.Exception]] Catch[T](System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.Unit] Empty +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.Unit] Ignore[T](System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.Unit] get_Empty() +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[TResult] Bind[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Threading.Tasks.Task`1[TResult]], System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[TResult] Map[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[T] CatchWith[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,T], System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[T] OfValueTask[T](System.Threading.Tasks.ValueTask`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[T] Result[T](T) Microsoft.FSharp.Control.TaskStateMachineData`1[T]: System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[T] MethodBuilder Microsoft.FSharp.Control.TaskStateMachineData`1[T]: T Result +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[Microsoft.FSharp.Core.FSharpResult`2[T,System.Exception]] Catch[T](System.Threading.Tasks.ValueTask`1[T]) +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[Microsoft.FSharp.Core.Unit] Empty +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[Microsoft.FSharp.Core.Unit] Ignore[T](System.Threading.Tasks.ValueTask`1[T]) +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[Microsoft.FSharp.Core.Unit] get_Empty() +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[TResult] Bind[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Threading.Tasks.ValueTask`1[TResult]], System.Threading.Tasks.ValueTask`1[T]) +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[TResult] Map[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], System.Threading.Tasks.ValueTask`1[T]) +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[T] CatchWith[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,T], System.Threading.Tasks.ValueTask`1[T]) +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[T] OfTask[T](System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[T] Result[T](T) Microsoft.FSharp.Control.WebExtensions: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] AsyncDownloadFile(System.Net.WebClient, System.Uri, System.String) Microsoft.FSharp.Control.WebExtensions: Microsoft.FSharp.Control.FSharpAsync`1[System.Byte[]] AsyncDownloadData(System.Net.WebClient, System.Uri) Microsoft.FSharp.Control.WebExtensions: Microsoft.FSharp.Control.FSharpAsync`1[System.Net.WebResponse] AsyncGetResponse(System.Net.WebRequest) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl index db9f41d97a8..537095d9e10 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.SurfaceArea.netstandard21.release.bsl @@ -619,6 +619,14 @@ Microsoft.FSharp.Control.AsyncActivation`1[T]: Microsoft.FSharp.Control.AsyncRet Microsoft.FSharp.Control.AsyncActivation`1[T]: Microsoft.FSharp.Control.AsyncReturn OnSuccess(T) Microsoft.FSharp.Control.AsyncActivation`1[T]: Microsoft.FSharp.Control.AsyncReturn Success(Microsoft.FSharp.Control.AsyncActivation`1[T], T) Microsoft.FSharp.Control.AsyncActivation`1[T]: Void OnExceptionRaised() +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.FSharpResult`2[T,System.Exception]] Catch[T](Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Empty +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] Ignore[T](Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] get_Empty() +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[TResult] Bind[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,Microsoft.FSharp.Control.FSharpAsync`1[TResult]], Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[TResult] Map[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[T] CatchWith[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,T], Microsoft.FSharp.Control.FSharpAsync`1[T]) +Microsoft.FSharp.Control.AsyncModule: Microsoft.FSharp.Control.FSharpAsync`1[T] Result[T](T) Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn Bind[T,TResult](Microsoft.FSharp.Control.AsyncActivation`1[T], Microsoft.FSharp.Control.FSharpAsync`1[TResult], Microsoft.FSharp.Core.FSharpFunc`2[TResult,Microsoft.FSharp.Control.FSharpAsync`1[T]]) Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn CallThenInvoke[T,TResult](Microsoft.FSharp.Control.AsyncActivation`1[T], TResult, Microsoft.FSharp.Core.FSharpFunc`2[TResult,Microsoft.FSharp.Control.FSharpAsync`1[T]]) Microsoft.FSharp.Control.AsyncPrimitives: Microsoft.FSharp.Control.AsyncReturn Invoke[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Control.AsyncActivation`1[T]) @@ -673,8 +681,8 @@ Microsoft.FSharp.Control.FSharpAsync: System.Threading.CancellationToken get_Def Microsoft.FSharp.Control.FSharpAsync: System.Threading.Tasks.Task`1[T] StartAsTask[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.Tasks.TaskCreationOptions], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) Microsoft.FSharp.Control.FSharpAsync: System.Threading.Tasks.Task`1[T] StartImmediateAsTask[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) Microsoft.FSharp.Control.FSharpAsync: System.Tuple`3[Microsoft.FSharp.Core.FSharpFunc`2[System.Tuple`3[TArg,System.AsyncCallback,System.Object],System.IAsyncResult],Microsoft.FSharp.Core.FSharpFunc`2[System.IAsyncResult,T],Microsoft.FSharp.Core.FSharpFunc`2[System.IAsyncResult,Microsoft.FSharp.Core.Unit]] AsBeginEnd[TArg,T](Microsoft.FSharp.Core.FSharpFunc`2[TArg,Microsoft.FSharp.Control.FSharpAsync`1[T]]) -Microsoft.FSharp.Control.FSharpAsync: T RunSynchronously[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) Microsoft.FSharp.Control.FSharpAsync: T RunSynchronouslyImmediate[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) +Microsoft.FSharp.Control.FSharpAsync: T RunSynchronously[T](Microsoft.FSharp.Control.FSharpAsync`1[T], Microsoft.FSharp.Core.FSharpOption`1[System.Int32], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) Microsoft.FSharp.Control.FSharpAsync: Void CancelDefaultToken() Microsoft.FSharp.Control.FSharpAsync: Void Start(Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) Microsoft.FSharp.Control.FSharpAsync: Void StartImmediate(Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit], Microsoft.FSharp.Core.FSharpOption`1[System.Threading.CancellationToken]) @@ -803,8 +811,26 @@ Microsoft.FSharp.Control.TaskBuilderModule: Microsoft.FSharp.Control.BackgroundT Microsoft.FSharp.Control.TaskBuilderModule: Microsoft.FSharp.Control.BackgroundTaskBuilder get_backgroundTask() Microsoft.FSharp.Control.TaskBuilderModule: Microsoft.FSharp.Control.TaskBuilder get_task() Microsoft.FSharp.Control.TaskBuilderModule: Microsoft.FSharp.Control.TaskBuilder task +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.FSharpResult`2[T,System.Exception]] Catch[T](System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.Unit] Empty +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.Unit] Ignore[T](System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[Microsoft.FSharp.Core.Unit] get_Empty() +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[TResult] Bind[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Threading.Tasks.Task`1[TResult]], System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[TResult] Map[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[T] CatchWith[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,T], System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[T] OfValueTask[T](System.Threading.Tasks.ValueTask`1[T]) +Microsoft.FSharp.Control.TaskModule: System.Threading.Tasks.Task`1[T] Result[T](T) Microsoft.FSharp.Control.TaskStateMachineData`1[T]: System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1[T] MethodBuilder Microsoft.FSharp.Control.TaskStateMachineData`1[T]: T Result +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[Microsoft.FSharp.Core.FSharpResult`2[T,System.Exception]] Catch[T](System.Threading.Tasks.ValueTask`1[T]) +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[Microsoft.FSharp.Core.Unit] Empty +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[Microsoft.FSharp.Core.Unit] Ignore[T](System.Threading.Tasks.ValueTask`1[T]) +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[Microsoft.FSharp.Core.Unit] get_Empty() +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[TResult] Bind[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,System.Threading.Tasks.ValueTask`1[TResult]], System.Threading.Tasks.ValueTask`1[T]) +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[TResult] Map[T,TResult](Microsoft.FSharp.Core.FSharpFunc`2[T,TResult], System.Threading.Tasks.ValueTask`1[T]) +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[T] CatchWith[T](Microsoft.FSharp.Core.FSharpFunc`2[System.Exception,T], System.Threading.Tasks.ValueTask`1[T]) +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[T] OfTask[T](System.Threading.Tasks.Task`1[T]) +Microsoft.FSharp.Control.ValueTaskModule: System.Threading.Tasks.ValueTask`1[T] Result[T](T) Microsoft.FSharp.Control.WebExtensions: Microsoft.FSharp.Control.FSharpAsync`1[Microsoft.FSharp.Core.Unit] AsyncDownloadFile(System.Net.WebClient, System.Uri, System.String) Microsoft.FSharp.Control.WebExtensions: Microsoft.FSharp.Control.FSharpAsync`1[System.Byte[]] AsyncDownloadData(System.Net.WebClient, System.Uri) Microsoft.FSharp.Control.WebExtensions: Microsoft.FSharp.Control.FSharpAsync`1[System.Net.WebResponse] AsyncGetResponse(System.Net.WebRequest) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj b/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj index 1694e8eb4ca..faf41f04322 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj @@ -83,6 +83,8 @@ + + diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModuleFunctions.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModuleFunctions.fs new file mode 100644 index 00000000000..3335e6910ac --- /dev/null +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/AsyncModuleFunctions.fs @@ -0,0 +1,262 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +// Tests for camelCase functions in module Async +module FSharp.Core.UnitTests.Controa.AsyncModuleFunctionsTestsl + +open System +open System.Threading +open System.Threading.Tasks +open Xunit + +#if NETFRAMEWORK // Polyfill for netstandard2.0 +let cancelWithToken (tcs: TaskCompletionSource<'T>) = + tcs.SetCanceled() // No CT overload available + CancellationToken.None // so exception won't reference one +#else +let cancelWithToken (tcs: TaskCompletionSource<'T>) = + let ct = CancellationToken true + tcs.SetCanceled ct + ct +#endif + +let asyncWait (a: Async<'T>): 'T = Async.RunSynchronouslyImmediate a +let asyncWaitWithCt (ct: CancellationToken) (a: Async<'T>): 'T = Async.RunSynchronously(a, cancellationToken = ct) + +[] +let ``Async.result wraps value`` () = + let actual = Async.result 42 |> asyncWait + Assert.Equal(42, actual) + + +[] +let ``Async.map transforms value`` () = + let actual = Async.result 21 |> Async.map (fun x -> x * 2) |> asyncWait + Assert.Equal(42, actual) + +[] +let ``Async.map propagates incoming exception`` () = + let a = async { return failwith "boom" : int } |> Async.map (fun x -> x * 2) + let e = Assert.Throws(fun () -> a |> asyncWait |> ignore) + Assert.Equal("boom", e.Message) + +[] +let ``Async.map propagates mapper exception as Fault`` () = + let a = Async.result () |> Async.map (fun () -> failwith "boom") + let e = Assert.Throws(fun () -> a |> asyncWait |> ignore) + Assert.Equal("boom", e.Message) + +[] +let ``Async.map propagates Cancellation (sync)`` () = + let ct = CancellationToken true + let a = Async.result 2 |> Async.map (fun x -> x * 2) + let e = Assert.Throws(fun () -> a |> asyncWaitWithCt ct |> ignore) + Assert.Equal(ct, e.CancellationToken) + +[] +let ``Async.map propagates Cancellation (async)`` () = + let mutable mapperWasCalled = false + let cts = new CancellationTokenSource() + let a = + async { do! Async.Sleep 5000 } + |> Async.map (fun () -> async { mapperWasCalled <- true }) + let t = Async.StartAsTask(a, cancellationToken = cts.Token) + cts.Cancel() + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.NotEqual(cts.Token, e.CancellationToken) + Assert.False mapperWasCalled + + +[] +let ``Async.bind threads value`` () = + let actual = + Async.result 21 + |> Async.bind (fun x -> Async.result (x * 2)) + |> asyncWait + Assert.Equal(42, actual) + +[] +let ``Async.bind propagates incoming exception (sync)`` () = + let a = async { return failwith "boom" } |> Async.bind Async.result + let e = Assert.Throws(fun () -> a |> asyncWait |> ignore) + Assert.Equal("boom", e.Message) + +[] +let ``Async.bind propagates binder exception as Fault (async)`` () = + let a = Async.result 5 |> Async.bind (fun x -> async { failwith $"boom {x}"}) + let e = Assert.Throws(fun () -> asyncWait a) + Assert.Equal("boom 5", e.Message) + +[] +let ``Async.bind propagates Cancellation (sync)`` () = + let ct = CancellationToken true + let a = Async.result 2 |> Async.bind Async.result + let e = Assert.Throws(fun () -> a |> asyncWaitWithCt ct |> ignore) + Assert.Equal(ct, e.CancellationToken) + +[] +let ``Async.bind propagates Cancellation (async)`` () = + let cts = new CancellationTokenSource() + let mutable binderWasCalled = false + let a = + async { do! Async.Sleep 5000 } + |> Async.bind (fun () -> async { binderWasCalled <- true }) + let t = Async.StartAsTask(a, cancellationToken = cts.Token) + cts.Cancel() + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.NotEqual(cts.Token, e.CancellationToken) + Assert.False binderWasCalled + + +[] +let ``Async.ignore discards result (sync)`` () = + let actual = Async.result 42 |> Async.ignore |> asyncWait + Assert.Equal((), actual) + +[] +let ``Async.ignore discards result (async)`` () = + let tcs = TaskCompletionSource() + let t = async { return! tcs.Task |> Async.AwaitTask } |> Async.ignore |> Async.StartAsTask + tcs.SetResult 42 + Assert.Equal((), t.Result) + +[] +let ``Async.ignore propagates incoming exception (sync)`` () = + let a = async { return failwith "boom" : int } |> Async.ignore + let e = Assert.Throws(fun () -> a |> asyncWait) + Assert.Equal("boom", e.Message) + +[] +let ``Async.ignore propagates incoming exception (async)`` () = + let tcs = TaskCompletionSource() + let t = async { return! tcs.Task |> Async.AwaitTask } |> Async.ignore |> Async.StartAsTask + tcs.SetException(Exception "boom") + let e = Assert.ThrowsAsync(fun () -> t).Result.InnerException + Assert.Equal("boom", e.Message) + +[] +let ``Async.ignore propagates Cancellation (sync)`` () = + let ct = CancellationToken true + let a = Async.result 2 |> Async.ignore + let e = Assert.Throws(fun () -> a |> asyncWaitWithCt ct) + Assert.Equal(ct, e.CancellationToken) + +[] +let ``Async.ignore propagates Cancellation (async)`` () = + let mutable cancellationFailed = false + let cts = new CancellationTokenSource() + let a = + async { do! Async.Sleep 5000 + cancellationFailed <- true + return 42 } + |> Async.ignore + let t = Async.StartAsTask(a, cancellationToken = cts.Token) + cts.Cancel() + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.NotEqual(cts.Token, e.CancellationToken) + Assert.False cancellationFailed + + +[] +let ``Async.catchWith passes through success (sync)`` () = + let source = Async.result 42 + let a = source |> Async.catchWith (fun _ -> -1) + Assert.Equal(42, asyncWait a) + +[] +let ``Async.catchWith passes through success (async)`` () = async { + let tcs = TaskCompletionSource() + let! a = async { return! tcs.Task |> Async.AwaitTask } |> Async.catchWith (fun _ -> -1) |> Async.StartChild + tcs.SetResult 42 + let! res = a + Assert.Equal(42, res) } + +[] +let ``Async.catchWith recovers from exception (sync)`` () = async { + let! actual = + async { return failwith "boom" : int } + |> Async.catchWith (fun e -> Assert.Equal("boom", e.Message); -1) + Assert.Equal(-1, actual) } + +[] +let ``Async.catchWith recovers from exception (async)`` () = async { + let tcs = TaskCompletionSource() + let! a = async { return! tcs.Task |> Async.AwaitTask } |> Async.catchWith (fun _ -> -1) |> Async.StartChild + tcs.SetException(Exception "boom") + let! result = a + Assert.Equal(-1, result) } + +[] +let ``Async.catchWith propagates Cancellation (sync)`` () = + let mutable cancellationFailed = false + let ct = CancellationToken true + let a = async { do! Async.Sleep 5000 + cancellationFailed <- true + return 42 } + |> Async.catchWith (fun _ -> -1) + let e = Assert.Throws(fun () -> a |> asyncWaitWithCt ct |> ignore) + Assert.Equal(ct, e.CancellationToken) + Assert.False cancellationFailed + +[] +let ``Async.catchWith propagates Cancellation (async)`` () = + let mutable cancellationFailed = false + let cts = new CancellationTokenSource() + let a = + async { do! Async.Sleep 5000 + cancellationFailed <- true + return 42 } + |> Async.catchWith (fun _ -> -1) + let t = Async.StartAsTask(a, cancellationToken = cts.Token) + cts.Cancel() + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.NotEqual(cts.Token, e.CancellationToken) + + +[] +let ``Async.catch returns Ok on success (sync)`` () = + let actual = Async.result 42 |> Async.catch |> asyncWait + Assert.Equal(Ok 42, actual) + +[] +let ``Async.catch returns Ok on success (async)`` () : unit = + let tcs = TaskCompletionSource() + let t = async { return! tcs.Task |> Async.AwaitTask } |> Async.catch |> Async.StartAsTask + tcs.SetResult 42 + Assert.Equal(Ok 42, t.Result) + +[] +let ``Async.catch returns Error on exception`` () = + let a = async { return failwith "boom" : int } |> Async.catch + match a |> asyncWait with + | Error ex -> Assert.Equal("boom", ex.Message) + | Ok _ -> failwith "unexpected success" + +[] +let ``Async.catch returns Error on exception (async)`` () : unit = + let a = async { do! Async.Sleep 1 + return failwith "boom" } |> Async.catch + match a |> asyncWait with + | Error ex -> Assert.Equal("boom", ex.Message) + | Ok _ -> failwith "unexpected success" + +[] +let ``Async.catch propagates Cancellation (sync)`` () = + let ct = CancellationToken true + let a = async { do! Async.Sleep 5000 } |> Async.catch + let e = Assert.Throws(fun () -> a |> asyncWaitWithCt ct |> ignore) + Assert.Equal(ct, e.CancellationToken) + +[] +let ``Async.catch propagates Cancellation (async)`` () = + let cts = new CancellationTokenSource() + let a = async { do! Async.Sleep 5000 } |> Async.catch + let t = Async.StartAsTask(a, cancellationToken = cts.Token) + cts.Cancel() + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.NotEqual(cts.Token, e.CancellationToken) + + +[] +let ``Async.empty returns unit`` () = + let actual = Async.empty |> asyncWait + Assert.Equal((), actual) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/TaskModuleFunctions.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/TaskModuleFunctions.fs new file mode 100644 index 00000000000..b3b40724a81 --- /dev/null +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Control/TaskModuleFunctions.fs @@ -0,0 +1,620 @@ + +// Tests for camelCase functions in module Task and module ValueTask + +namespace FSharp.Core.UnitTests.Control + +open System +open System.Threading +open System.Threading.Tasks +open Xunit + +module TaskModuleFunctionsTests = + +#if NETFRAMEWORK // Polyfill for netstandard2.0 + type Task<'T> with member x.IsCompletedSuccessfully = x.Status = TaskStatus.RanToCompletion + let cancelWithToken (tcs: TaskCompletionSource<'T>) = + tcs.SetCanceled() // No CT overload available + CancellationToken.None // so exception won't reference one +#else + let cancelWithToken (tcs: TaskCompletionSource<'T>) = + let ct = CancellationToken true + tcs.SetCanceled ct + ct +#endif + + [] + let ``Task.result wraps value`` () = + let t = Task.result 42 + Assert.Equal(42, t.Result) + + + [] + let ``Task.map transforms value (sync)`` () = + let t = Task.result 21 |> Task.map (fun x -> x * 2) + Assert.True t.IsCompleted + Assert.Equal(42, t.Result) + + [] + let ``Task.map transforms value (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> Task.map (fun x -> x * 2) + Assert.False t.IsCompleted + tcs.SetResult 21 + Assert.Equal(42, t.Result) + + [] + let ``Task.map propagates incoming exception (sync)`` () = + let t = Task.FromException(Exception "boom") |> Task.map (fun x -> x * 2) + let! e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal("boom", e.Message) + + [] + let ``Task.map propagates incoming exception (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> Task.map (fun x -> x * 2) + tcs.SetException(Exception "boom") + let! e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal("boom", e.Message) + + [] + let ``Task.map propagates mapper exception as Fault (sync)`` () = + let t = Task.result () |> Task.map (fun () -> failwith "boom") + let! e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal("boom", e.Message) + + [] + let ``Task.map propagates mapper exception as Fault (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> Task.map (fun () -> failwith "boom") + tcs.SetResult () + let! e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal("boom", e.Message) + + [] + let ``Task.map propagates Cancellation (sync)`` () = + let ct = CancellationToken true + let t = Task.FromCanceled(ct) |> Task.map (fun x -> x * 2) + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + [] + let ``Task.map propagates Cancellation (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> Task.map (fun x -> x * 2) + let ct = cancelWithToken tcs + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + + [] + let ``Task.bind threads value (sync)`` () = + let t = Task.result 21 |> Task.bind (fun x -> Task.result (x * 2)) + Assert.True t.IsCompleted + Assert.Equal(42, t.Result) + + [] + let ``Task.bind threads value (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> Task.bind (fun x -> Task.result (x * 2)) + Assert.False t.IsCompleted + tcs.SetResult 21 + Assert.Equal(42, t.Result) + + [] + let ``Task.bind propagates incoming exception (sync)`` () = + let t = Task.FromException(Exception "boom") |> Task.bind (fun x -> Task.result (x * 2)) + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal("boom", e.Message) + + [] + let ``Task.bind propagates incoming exception (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> Task.bind (fun x -> Task.result (x * 2)) + tcs.SetException(Exception "boom") + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal("boom", e.Message) + + [] + let ``Task.bind propagates binder exception as Fault (sync)`` () = + let t = Task.result () |> Task.bind (fun () -> failwith "boom") + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal("boom", e.Message) + + [] + let ``Task.bind propagates binder exception as Fault (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> Task.bind (fun () -> failwith "boom") + tcs.SetResult () + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal("boom", e.Message) + + [] + let ``Task.bind propagates Cancellation (sync)`` () = + let ct = CancellationToken true + let t = Task.FromCanceled(ct) |> Task.bind (fun x -> Task.result (x * 2)) + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + [] + let ``Task.bind propagates Cancellation (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> Task.bind (fun x -> Task.result (x * 2)) + let ct = cancelWithToken tcs + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + + [] + let ``Task.ignore discards result (sync)`` () : unit = + let t = Task.result 42 |> Task.ignore + Assert.True t.IsCompletedSuccessfully + t.Result : unit + + [] + let ``Task.ignore discards result (async)`` () : unit = + let tcs = TaskCompletionSource() + let t = tcs.Task |> Task.ignore + Assert.False t.IsCompleted + tcs.SetResult 42 + Assert.True t.IsCompletedSuccessfully + t.Result : unit + + [] + let ``Task.ignore propagates incoming exception (sync)`` () = + let t = Task.FromException(Exception "boom") |> Task.ignore + Assert.True t.IsCompleted + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal("boom", e.Message) + + [] + let ``Task.ignore propagates incoming exception (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> Task.ignore + Assert.False t.IsCompleted + tcs.SetException(Exception "boom") + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal("boom", e.Message) + + [] + let ``Task.ignore propagates Cancellation (sync)`` () = + let ct = CancellationToken true + let t = Task.FromCanceled(ct) |> Task.ignore + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + [] + let ``Task.ignore propagates Cancellation (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> Task.ignore + let ct = cancelWithToken tcs + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + + [] + let ``Task.catchWith recovers from exception (sync)`` () = + let source = Task.FromException(Exception "boom") + let t = source |> Task.catchWith (fun _ -> -1) + Assert.Equal(-1, t.Result) + + [] + let ``Task.catchWith recovers from exception (async)`` () : Task = + let tcs = TaskCompletionSource() + let t = tcs.Task |> Task.catchWith (fun _ -> -1) + tcs.SetException(Exception "boom") + task { + let! result = t + Assert.Equal(-1, result) + } + + [] + let ``Task.catchWith passes through success (sync)`` () = + let source = Task.result 42 + let t = source |> Task.catchWith (fun _ -> -1) + Assert.Equal(42, t.Result) + + [] + let ``Task.catchWith passes through success (async)`` () : Task = + let tcs = TaskCompletionSource() + let t = tcs.Task |> Task.catchWith (fun _ -> -1) + Assert.False t.IsCompleted + tcs.SetResult 42 + task { + let! result = t + Assert.Equal(42, result) + } + + [] + let ``Task.catchWith propagates Cancellation (sync)`` () = + let ct = CancellationToken true + let t = Task.FromCanceled(ct) |> Task.catchWith (fun _ -> -1) + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + [] + let ``Task.catchWith propagates Cancellation (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> Task.catchWith (fun _ -> -1) + let ct = cancelWithToken tcs + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + + [] + let ``Task.catch returns Ok on success (sync)`` () : unit= + let t = Task.result 42 |> Task.catch + Assert.Equal(Ok 42, t.Result) + + [] + let ``Task.catch returns Ok on success (async)`` () : unit = + let tcs = TaskCompletionSource() + let t = Task.catch tcs.Task + tcs.SetResult 42 + Assert.Equal(Ok 42, t.Result) + + [] + let ``Task.catch returns Error on exception (sync)`` () = + let t = Task.FromException(Exception "boom") |> Task.catch + match t.Result with + | Error ex -> Assert.Equal("boom", ex.Message) + | Ok _ -> failwith "unexpected success" + + [] + let ``Task.catch returns Error on exception (async)`` () : unit = + let tcs = TaskCompletionSource() + let t = tcs.Task |> Task.catch + tcs.SetException(Exception "boom") + match t.Result with + | Error ex -> Assert.Equal("boom", ex.Message) + | Ok _ -> failwith "unexpected success" + + [] + let ``Task.catch propagates cancellation (sync)`` () = + let ct = CancellationToken true + let t = Task.FromCanceled(ct) |> Task.catch + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + [] + let ``Task.catch propagates cancellation (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> Task.catch + let ct = CancellationToken true + let ct = cancelWithToken tcs + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + + [] + let ``Task.empty returns completed unit task`` () = + let t = Task.empty + Assert.True t.IsCompletedSuccessfully + Assert.Equal((), t.Result) + + +#if NETSTANDARD2_1 + [] + let ``Task.ofValueTask converts ValueTask`` () = + let vt = ValueTask(42) + let t = Task.ofValueTask vt + Assert.Equal(42, t.Result) + + let ``Task.ofValueTask converts faulted ValueTask`` () = + let vt = ValueTask(Task.FromException(Exception "boom")) + let t = Task.ofValueTask vt + let e = Assert.ThrowsAsync(fun () -> t).Result + Assert.Equal("boom", e.Message) + +module ValueTaskModuleFunctionsTests = + + let cancelWithToken (tcs: TaskCompletionSource<'T>) = + let ct = CancellationToken true + tcs.SetCanceled ct + ct + + [] + let ``ValueTask.result wraps value`` () = + let vt = ValueTask.result 42 + Assert.Equal(42, vt.Result) + + [] + let ``ValueTask.map transforms value (sync)`` () = + let t = ValueTask.result 21 |> ValueTask.map (fun x -> x * 2) + Assert.True t.IsCompleted + Assert.Equal(42, t.Result) + + [] + let ``ValueTask.map transforms value (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.map (fun x -> x * 2) + Assert.False t.IsCompleted + tcs.SetResult 21 + Assert.Equal(42, t.Result) + + [] + let ``ValueTask.map propagates incoming exception (sync)`` () = + let t = ValueTask.FromException(Exception "boom") |> ValueTask.map (fun x -> x * 2) + task { + let! e = Assert.ThrowsAnyAsync(fun () -> t.AsTask()) + Assert.Equal("boom", e.Message) + } + + [] + let ``ValueTask.map propagates incoming exception (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.map (fun x -> x * 2) + tcs.SetException(Exception "boom") + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal("boom", e.Message) + + [] + let ``ValueTask.map propagates mapper exception as Fault (sync)`` () = + let t = ValueTask.result () |> ValueTask.map (fun () -> failwith "boom") + task { + let! e = Assert.ThrowsAnyAsync(fun () -> t.AsTask()) + Assert.Equal("boom", e.Message) + } + + [] + let ``ValueTask.map propagates mapper exception as Fault (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.map (fun () -> failwith "boom") + tcs.SetResult () + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal("boom", e.Message) + + [] + let ``ValueTask.map propagates Cancellation (sync)`` () = + let ct = CancellationToken true + let t = Task.FromCanceled(ct) |> ValueTask.ofTask |> ValueTask.map (fun x -> x * 2) + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + [] + let ``ValueTask.map propagates Cancellation (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.map (fun x -> x * 2) + let ct = cancelWithToken tcs + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + + [] + let ``ValueTask.bind threads value (sync)`` () = + let t = ValueTask.result 21 |> ValueTask.bind (fun x -> ValueTask.result (x * 2)) + Assert.True t.IsCompleted + Assert.Equal(42, t.Result) + + [] + let ``ValueTask.bind threads value (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.bind (fun x -> ValueTask.result (x * 2)) + Assert.False t.IsCompleted + tcs.SetResult 21 + Assert.Equal(42, t.Result) + + [] + let ``ValueTask.bind propagates incoming exception (sync)`` () = + let t = Task.FromException(Exception "boom") |> ValueTask.ofTask |> ValueTask.bind (fun x -> ValueTask.result (x * 2)) + task { + let! e = Assert.ThrowsAnyAsync(fun () -> t.AsTask()) + Assert.Equal("boom", e.Message) + } + + [] + let ``ValueTask.bind propagates incoming exception (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.bind (fun x -> ValueTask.result (x * 2)) + tcs.SetException(Exception "boom") + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal("boom", e.Message) + + [] + let ``ValueTask.bind propagates binder exception as Fault (sync)`` () = + let t = ValueTask.result () |> ValueTask.bind (fun () -> failwith "boom") + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal("boom", e.Message) + + [] + let ``ValueTask.bind propagates binder exception as Fault (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.bind (fun () -> failwith "boom") + tcs.SetResult () + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal("boom", e.Message) + + [] + let ``ValueTask.bind propagates Cancellation (sync)`` () = + let ct = CancellationToken true + let t = Task.FromCanceled(ct) |> ValueTask.ofTask |> ValueTask.bind (fun x -> ValueTask.result (x * 2)) + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + [] + let ``ValueTask.bind propagates Cancellation (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.bind (fun x -> ValueTask.result (x * 2)) + let ct = cancelWithToken tcs + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + + [] + let ``ValueTask.ignore discards result (sync)`` () : unit = + let t = ValueTask.result 42 |> ValueTask.ignore + Assert.True t.IsCompletedSuccessfully + t.Result : unit + + [] + let ``ValueTask.ignore discards result (async)`` () : unit = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.ignore + Assert.False t.IsCompleted + tcs.SetResult 42 + Assert.True t.IsCompletedSuccessfully + t.Result : unit + + [] + let ``ValueTask.ignore propagates incoming exception (sync)`` () = + let t = Task.FromException(Exception "boom") |> ValueTask.ofTask |> ValueTask.ignore + Assert.True t.IsCompleted + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal("boom", e.Message) + + [] + let ``ValueTask.ignore propagates incoming exception (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.ignore + Assert.False t.IsCompleted + tcs.SetException(Exception "boom") + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal("boom", e.Message) + + [] + let ``ValueTask.ignore propagates Cancellation (sync)`` () = + let ct = CancellationToken true + let t = Task.FromCanceled(ct) |> ValueTask.ofTask |> ValueTask.ignore + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + [] + let ``ValueTask.ignore propagates Cancellation (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.ignore + let ct = cancelWithToken tcs + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + + [] + let ``ValueTask.catchWith recovers from exception (sync)`` () = + let source = Task.FromException(Exception "boom") + let t = source |> ValueTask.ofTask |> ValueTask.catchWith (fun _ -> -1) + Assert.Equal(-1, t.Result) + + [] + let ``ValueTask.catchWith recovers from exception (async)`` () : Task = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.catchWith (fun _ -> -1) + tcs.SetException(Exception "boom") + task { + let! result = t + Assert.Equal(-1, result) + } + + [] + let ``ValueTask.catchWith passes through success (sync)`` () = + let source = ValueTask.result 42 + let t = source |> ValueTask.catchWith (fun _ -> -1) + Assert.Equal(42, t.Result) + + [] + let ``ValueTask.catchWith passes through success (async)`` () : Task = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.catchWith (fun _ -> -1) + Assert.False t.IsCompleted + tcs.SetResult 42 + task { + let! result = t + Assert.Equal(42, result) + } + + [] + let ``ValueTask.catchWith propagates Cancellation (sync)`` () = + let ct = CancellationToken true + let t = Task.FromCanceled(ct) |> ValueTask.ofTask |> ValueTask.catchWith (fun _ -> -1) + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + [] + let ``ValueTask.catchWith propagates Cancellation (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.catchWith (fun _ -> -1) + let ct = cancelWithToken tcs + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + [] + let ``ValueTask.catch returns Ok on success (sync)`` () : unit= + let t = ValueTask.result 42 |> ValueTask.catch + Assert.Equal(Ok 42, t.Result) + + [] + let ``ValueTask.catch returns Ok on success (async)`` () : unit = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.catch + tcs.SetResult 42 + Assert.Equal(Ok 42, t.Result) + + [] + let ``ValueTask.catch returns Error on exception (sync)`` () = + let t = ValueTask.FromException(Exception "boom") |> ValueTask.catch + match t.Result with + | Error ex -> Assert.Equal("boom", ex.Message) + | Ok _ -> failwith "unexpected success" + + [] + let ``ValueTask.catch returns Error on exception (async)`` () : unit = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.catch + tcs.SetException(Exception "boom") + match t.Result with + | Error ex -> Assert.Equal("boom", ex.Message) + | Ok _ -> failwith "unexpected success" + + [] + let ``ValueTask.catch propagates cancellation (sync)`` () = + let ct = CancellationToken true + let t = Task.FromCanceled(ct) |> ValueTask.ofTask |> ValueTask.catch + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + [] + let ``ValueTask.catch propagates cancellation (async)`` () = + let tcs = TaskCompletionSource() + let t = tcs.Task |> ValueTask.ofTask |> ValueTask.catch + let ct = CancellationToken true + let ct = cancelWithToken tcs + let e = Assert.ThrowsAsync(fun () -> t.AsTask()).Result + Assert.Equal(ct, e.CancellationToken) + Assert.True t.IsCanceled + + + [] + let ``ValueTask.empty returns completed unit value task`` () : unit = + let vt = ValueTask.empty + Assert.True vt.IsCompletedSuccessfully + vt.Result + + [] + let ``ValueTask.ofTask wraps Task`` () = + let t = Task.FromResult 42 + let vt = ValueTask.ofTask t + Assert.Equal(42, vt.Result) + + let ``ValueTask.ofTask converts faulted Task`` () = + let t = Task.FromException(Exception "boom") + let vt = ValueTask.ofTask t + let e = Assert.ThrowsAsync(fun () -> vt.AsTask()).Result + Assert.Equal("boom", e.Message) + +#endif