diff --git a/src/FSharp.Control.TaskSeq.Test/TaskSeq.Exists.Tests.fs b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Exists.Tests.fs index 04a652f..ee65f1e 100644 --- a/src/FSharp.Control.TaskSeq.Test/TaskSeq.Exists.Tests.fs +++ b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Exists.Tests.fs @@ -82,7 +82,7 @@ module Immutable = module SideEffects = [)>] - let ``TaskSeq-exists KeyNotFoundException only sometimes for mutated state`` variant = task { + let ``TaskSeq-exists success only sometimes for mutated state`` variant = task { let ts = Gen.getSeqWithSideEffect variant let finder = (=) 11 @@ -100,7 +100,7 @@ module SideEffects = } [)>] - let ``TaskSeq-existsAsync KeyNotFoundException only sometimes for mutated state`` variant = task { + let ``TaskSeq-existsAsync success only sometimes for mutated state`` variant = task { let ts = Gen.getSeqWithSideEffect variant let finder x = task { return x = 11 } @@ -201,7 +201,7 @@ module SideEffects = found |> should be True i |> should equal 0 // notice that it should be one higher if the statement after 'yield' is evaluated - // find some next item. We do get a new iterator, but mutable state is now starting at '1' + // find some next item. We do get a new iterator, but mutable state is now still starting at '0' let! found = ts |> TaskSeq.exists ((=) 4) found |> should be True i |> should equal 4 // only partial evaluation! @@ -221,7 +221,7 @@ module SideEffects = found |> should be True i |> should equal 0 // notice that it should be one higher if the statement after 'yield' is evaluated - // find some next item. We do get a new iterator, but mutable state is now starting at '1' + // find some next item. We do get a new iterator, but mutable state is now still starting at '0' let! found = ts |> TaskSeq.existsAsync (fun x -> task { return x = 4 }) found |> should be True i |> should equal 4 // only partial evaluation! diff --git a/src/FSharp.Control.TaskSeq.Test/TaskSeq.Forall.Tests.fs b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Forall.Tests.fs index 04a652f..8c47dea 100644 --- a/src/FSharp.Control.TaskSeq.Test/TaskSeq.Forall.Tests.fs +++ b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Forall.Tests.fs @@ -1,4 +1,4 @@ -module TaskSeq.Tests.Exists +module TaskSeq.Tests.Forall open Xunit open FsUnit.Xunit @@ -6,119 +6,117 @@ open FsUnit.Xunit open FSharp.Control // -// TaskSeq.exists -// TaskSeq.existsAsyncc +// TaskSeq.forall +// TaskSeq.forallAsyncc // module EmptySeq = [] let ``Null source is invalid`` () = assertNullArg - <| fun () -> TaskSeq.exists (fun _ -> false) null + <| fun () -> TaskSeq.forall (fun _ -> false) null assertNullArg - <| fun () -> TaskSeq.existsAsync (fun _ -> Task.fromResult false) null + <| fun () -> TaskSeq.forallAsync (fun _ -> Task.fromResult false) null [)>] - let ``TaskSeq-exists returns false`` variant = + let ``TaskSeq-forall always returns true`` variant = Gen.getEmptyVariant variant - |> TaskSeq.exists ((=) 12) - |> Task.map (should be False) + |> TaskSeq.forall ((=) 12) + |> Task.map (should be True) [)>] - let ``TaskSeq-existsAsync returns false`` variant = + let ``TaskSeq-forallAsync always returns true`` variant = Gen.getEmptyVariant variant - |> TaskSeq.existsAsync (fun x -> task { return x = 12 }) - |> Task.map (should be False) - -module Immutable = - [)>] - let ``TaskSeq-exists sad path returns false`` variant = - Gen.getSeqImmutable variant - |> TaskSeq.exists ((=) 0) - |> Task.map (should be False) - - [)>] - let ``TaskSeq-existsAsync sad path return false`` variant = - Gen.getSeqImmutable variant - |> TaskSeq.existsAsync (fun x -> task { return x = 0 }) - |> Task.map (should be False) - - [)>] - let ``TaskSeq-exists happy path middle of seq`` variant = - Gen.getSeqImmutable variant - |> TaskSeq.exists (fun x -> x < 6 && x > 4) - |> Task.map (should be True) - - [)>] - let ``TaskSeq-existsAsync happy path middle of seq`` variant = - Gen.getSeqImmutable variant - |> TaskSeq.existsAsync (fun x -> task { return x < 6 && x > 4 }) + |> TaskSeq.forallAsync (fun x -> task { return x = 12 }) |> Task.map (should be True) +module Immutable = [)>] - let ``TaskSeq-exists happy path first item of seq`` variant = - Gen.getSeqImmutable variant - |> TaskSeq.exists ((=) 1) - |> Task.map (should be True) + let ``TaskSeq-forall sad path returns false`` variant = task { + do! + Gen.getSeqImmutable variant + |> TaskSeq.forall ((=) 0) + |> Task.map (should be False) + + do! + Gen.getSeqImmutable variant + |> TaskSeq.forall ((>) 9) // lt + |> Task.map (should be False) + } [)>] - let ``TaskSeq-existsAsync happy path first item of seq`` variant = - Gen.getSeqImmutable variant - |> TaskSeq.existsAsync (fun x -> task { return x = 1 }) - |> Task.map (should be True) + let ``TaskSeq-forallAsync sad path returns false`` variant = task { + do! + Gen.getSeqImmutable variant + |> TaskSeq.forallAsync (fun x -> task { return x = 0 }) + |> Task.map (should be False) + + do! + Gen.getSeqImmutable variant + |> TaskSeq.forallAsync (fun x -> task { return x < 9 }) + |> Task.map (should be False) + } [)>] - let ``TaskSeq-exists happy path last item of seq`` variant = + let ``TaskSeq-forall happy path whole seq true`` variant = Gen.getSeqImmutable variant - |> TaskSeq.exists ((=) 10) + |> TaskSeq.forall (fun x -> x < 6 || x > 5) |> Task.map (should be True) [)>] - let ``TaskSeq-existsAsync happy path last item of seq`` variant = + let ``TaskSeq-forallAsync happy path whole seq true`` variant = Gen.getSeqImmutable variant - |> TaskSeq.existsAsync (fun x -> task { return x = 10 }) + |> TaskSeq.forallAsync (fun x -> task { return x <= 10 && x >= 0 }) |> Task.map (should be True) module SideEffects = [)>] - let ``TaskSeq-exists KeyNotFoundException only sometimes for mutated state`` variant = task { + let ``TaskSeq-forall mutated state can change result`` variant = task { let ts = Gen.getSeqWithSideEffect variant - let finder = (=) 11 + let predicate x = x > 10 // first: false - let! found = TaskSeq.exists finder ts - found |> should be False + let! found = TaskSeq.forall predicate ts + found |> should be False // fails on first item, not many side effects yet + + // ensure side effects executes + do! consumeTaskSeq ts // find again: found now, because of side effects - let! found = TaskSeq.exists finder ts + let! found = TaskSeq.forall predicate ts found |> should be True - // find once more: false - let! found = TaskSeq.exists finder ts - found |> should be False + // find once more, still true, as numbers increase + do! consumeTaskSeq ts // ensure side effects executes + let! found = TaskSeq.forall predicate ts + found |> should be True } [)>] - let ``TaskSeq-existsAsync KeyNotFoundException only sometimes for mutated state`` variant = task { + let ``TaskSeq-forallAsync mutated state can change result`` variant = task { let ts = Gen.getSeqWithSideEffect variant - let finder x = task { return x = 11 } + let predicate x = Task.fromResult (x > 10) // first: false - let! found = TaskSeq.existsAsync finder ts - found |> should be False + let! found = TaskSeq.forallAsync predicate ts + found |> should be False // fails on first item, not many side effects yet + + // ensure side effects executes + do! consumeTaskSeq ts // find again: found now, because of side effects - let! found = TaskSeq.existsAsync finder ts + let! found = TaskSeq.forallAsync predicate ts found |> should be True - // find once more: false - let! found = TaskSeq.existsAsync finder ts - found |> should be False + // find once more, still true, as numbers increase + do! consumeTaskSeq ts // ensure side effects executes + let! found = TaskSeq.forallAsync predicate ts + found |> should be True } [] - let ``TaskSeq-exists _specialcase_ prove we don't read past the found item`` () = task { + let ``TaskSeq-forall _specialcase_ prove we don't read past the first failing item`` () = task { let mutable i = 0 let ts = taskSeq { @@ -127,18 +125,18 @@ module SideEffects = yield i } - let! found = ts |> TaskSeq.exists ((=) 3) - found |> should be True + let! found = ts |> TaskSeq.forall ((>) 3) + found |> should be False i |> should equal 3 // only partial evaluation! // find next item. We do get a new iterator, but mutable state is now starting at '3', so first item now returned is '4'. - let! found = ts |> TaskSeq.exists ((=) 4) + let! found = ts |> TaskSeq.forall ((<=) 4) found |> should be True - i |> should equal 4 // only partial evaluation! + i |> should equal 13 // we evaluated to the end } [] - let ``TaskSeq-existsAsync _specialcase_ prove we don't read past the found item`` () = task { + let ``TaskSeq-forallAsync _specialcase_ prove we don't read past the first failing item`` () = task { let mutable i = 0 let ts = taskSeq { @@ -147,48 +145,22 @@ module SideEffects = yield i } - let! found = ts |> TaskSeq.existsAsync (fun x -> task { return x = 3 }) - found |> should be True + let! found = ts |> TaskSeq.forallAsync (fun x -> Task.fromResult (x < 3)) + found |> should be False i |> should equal 3 // only partial evaluation! // find next item. We do get a new iterator, but mutable state is now starting at '3', so first item now returned is '4'. - let! found = ts |> TaskSeq.existsAsync (fun x -> task { return x = 4 }) - found |> should be True - i |> should equal 4 - } + let! found = + ts + |> TaskSeq.forallAsync (fun x -> Task.fromResult (x >= 4)) - [] - let ``TaskSeq-exists _specialcase_ prove we don't read past the found item v2`` () = task { - let mutable i = 0 - - let ts = taskSeq { - yield 42 - i <- i + 1 - i <- i + 1 - } - - let! found = ts |> TaskSeq.exists ((=) 42) found |> should be True - i |> should equal 0 // because no MoveNext after found item, the last statements are not executed + i |> should equal 13 // we evaluated to the end } - [] - let ``TaskSeq-existsAsync _specialcase_ prove we don't read past the found item v2`` () = task { - let mutable i = 0 - - let ts = taskSeq { - yield 42 - i <- i + 1 - i <- i + 1 - } - - let! found = ts |> TaskSeq.existsAsync (fun x -> task { return x = 42 }) - found |> should be True - i |> should equal 0 // because no MoveNext after found item, the last statements are not executed - } [] - let ``TaskSeq-exists _specialcase_ prove statement after yield is not evaluated`` () = task { + let ``TaskSeq-forall _specialcase_ prove statement after first false result is not evaluated`` () = task { let mutable i = 0 let ts = taskSeq { @@ -197,18 +169,18 @@ module SideEffects = i <- i + 1 } - let! found = ts |> TaskSeq.exists ((=) 0) - found |> should be True - i |> should equal 0 // notice that it should be one higher if the statement after 'yield' is evaluated + let! found = ts |> TaskSeq.forall ((>) 0) + found |> should be False + i |> should equal 0 // notice that it should be one higher if the statement after 'yield' was evaluated - // find some next item. We do get a new iterator, but mutable state is now starting at '1' - let! found = ts |> TaskSeq.exists ((=) 4) - found |> should be True + // find some next item. We do get a new iterator, but mutable state is still starting at '0' + let! found = ts |> TaskSeq.forall ((>) 4) + found |> should be False i |> should equal 4 // only partial evaluation! } [] - let ``TaskSeq-existsAsync _specialcase_ prove statement after yield is not evaluated`` () = task { + let ``TaskSeq-forallAsync _specialcase_ prove statement after first false result is not evaluated`` () = task { let mutable i = 0 let ts = taskSeq { @@ -217,12 +189,12 @@ module SideEffects = i <- i + 1 } - let! found = ts |> TaskSeq.existsAsync (fun x -> task { return x = 0 }) - found |> should be True - i |> should equal 0 // notice that it should be one higher if the statement after 'yield' is evaluated + let! found = ts |> TaskSeq.forallAsync (fun x -> Task.fromResult (x < 0)) + found |> should be False + i |> should equal 0 // notice that it should be one higher if the statement after 'yield' was evaluated - // find some next item. We do get a new iterator, but mutable state is now starting at '1' - let! found = ts |> TaskSeq.existsAsync (fun x -> task { return x = 4 }) - found |> should be True + // find some next item. We do get a new iterator, but mutable state is still starting at '0' + let! found = ts |> TaskSeq.forallAsync (fun x -> Task.fromResult (x < 4)) + found |> should be False i |> should equal 4 // only partial evaluation! }