From 247bca3faf81bd2e6915c4e39ef30fffda78a421 Mon Sep 17 00:00:00 2001 From: Spencer Farley <2847259+farlee2121@users.noreply.github.com> Date: Fri, 15 Mar 2024 16:56:51 -0500 Subject: [PATCH] Fix immediate task execution when defining test cases I.e. testTask and testCaseTask would start the task as soon as the test definition is created instead of when the test suite is run. This fixes a breaking change from how testTask used to work and a flaw of the new testCaseTask api. It is a breaking change to the testCaseTask api. --- Expecto.Tests/Tests.fs | 26 +++++++++++++++----------- Expecto/Expecto.fs | 23 ++++++++++++++--------- README.md | 2 +- 3 files changed, 30 insertions(+), 21 deletions(-) diff --git a/Expecto.Tests/Tests.fs b/Expecto.Tests/Tests.fs index 5246fce5..16447188 100644 --- a/Expecto.Tests/Tests.fs +++ b/Expecto.Tests/Tests.fs @@ -1397,19 +1397,23 @@ let taskTests = ] testList "testCaseTask" [ - testCaseTask "simple" <| task { - Expect.equal 1 1 "1=1" - } + testCaseTask "simple" <| fun () -> + task { + Expect.equal 1 1 "1=1" + } - testCaseTask "let" <| task { - let! n = async { return 1 } - Expect.equal n 1 "n=1" - } + testCaseTask "let" <| fun () -> + task { + let! n = async { return 1 } + Expect.equal n 1 "n=1" + } - testCaseTask "can fail" <| task { - let! n = async { return 2 } - Expect.equal n 1 "n=1" - } |> assertTestFails + testCaseTask "can fail" <| (fun () -> + task { + let! n = async { return 2 } + Expect.equal n 1 "n=1" + }) + |> assertTestFails ] testList "testTask" [ diff --git a/Expecto/Expecto.fs b/Expecto/Expecto.fs index e200691f..685c49df 100644 --- a/Expecto/Expecto.fs +++ b/Expecto/Expecto.fs @@ -87,8 +87,15 @@ module Tests = let inline testCaseWithCancel name test = TestLabel(name, TestCase (SyncWithCancel test,Normal), Normal) /// Builds an async test case let inline testCaseAsync name test = TestLabel(name, TestCase (Async test,Normal), Normal) + + let inline private deferTaskAsAsync (taskFactory: unit -> Task) = + // Tasks are hot, they are start right away, so we need to defer the task creation + async { + do! taskFactory() |> Async.AwaitTask + } + /// Builds an async test case from a task - let inline testCaseTask name test = TestLabel(name, TestCase (Async (Async.AwaitTask test),Normal), Normal) + let inline testCaseTask name test = TestLabel(name, TestCase (Async (deferTaskAsAsync test),Normal), Normal) /// Builds a test case that will make Expecto to ignore other unfocused tests let inline ftestCase name test = TestLabel(name, TestCase (Sync test, Focused), Focused) /// Builds a test case with cancel that will make Expecto to ignore other unfocused tests @@ -96,7 +103,7 @@ module Tests = /// Builds an async test case that will make Expecto to ignore other unfocused tests let inline ftestCaseAsync name test = TestLabel(name, TestCase (Async test, Focused), Focused) /// Builds an async test case from a task, that will make Expecto to ignore other unfocused tests - let inline ftestCaseTask name test = TestLabel(name, TestCase (Async (Async.AwaitTask test), Focused), Focused) + let inline ftestCaseTask name test = TestLabel(name, TestCase (Async (deferTaskAsAsync test), Focused), Focused) /// Builds a test case that will be ignored by Expecto let inline ptestCase name test = TestLabel(name, TestCase (Sync test, Pending), Pending) /// Builds a test case with cancel that will be ignored by Expecto @@ -104,7 +111,7 @@ module Tests = /// Builds an async test case that will be ignored by Expecto let inline ptestCaseAsync name test = TestLabel(name, TestCase (Async test, Pending), Pending) /// Builds an async test case from a task, that will be ignored by Expecto - let inline ptestCaseTask name test = TestLabel(name, TestCase (Async (Async.AwaitTask test), Pending), Pending) + let inline ptestCaseTask name test = TestLabel(name, TestCase (Async (deferTaskAsAsync test), Pending), Pending) /// Test case or list needs to run sequenced. Use for any benchmark code or /// for tests using `Expect.isFasterThan` let inline testSequenced test = Sequenced (Synchronous,test) @@ -235,13 +242,11 @@ module Tests = member inline __.TryFinally(p, cf) = task.TryFinally(p, cf) member inline __.TryWith(p, cf) = task.TryWith(p, cf) member __.Run f = - let a = task { - do! task.Run f - } + let deferred () = task.Run f match focusState with - | Normal -> testCaseTask name a - | Focused -> ftestCaseTask name a - | Pending -> ptestCaseTask name a + | Normal -> testCaseTask name deferred + | Focused -> ftestCaseTask name deferred + | Pending -> ptestCaseTask name deferred [] module TestTaskExtensions = diff --git a/README.md b/README.md index 36139bd0..3280bc79 100644 --- a/README.md +++ b/README.md @@ -325,7 +325,7 @@ test project.** - `testTask : string -> TestTaskBuilder` - Builds a task test case in a computation expression. - `testCase : string -> (unit -> unit) -> Test` - Builds a test case from a test function. - `testCaseAsync : string -> Async -> Test` - Builds an async test case from an async expression. -- `testCaseTask : string -> Task -> Test` - Builds an async test case from a task expression. +- `testCaseTask : string -> (unit -> Task) -> Test` - Builds an async test case from a function returning a task. Unlike async, tasks start right away and thus must be wrapped in a function so the task doesn't start until the test is run. ### `testList` for grouping