diff --git a/src/FSharp.Control.TaskSeq.Test/TaskSeq.Choose.Tests.fs b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Choose.Tests.fs index 6258a760..06d67bc4 100644 --- a/src/FSharp.Control.TaskSeq.Test/TaskSeq.Choose.Tests.fs +++ b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Choose.Tests.fs @@ -66,6 +66,84 @@ module Immutable = String letters2 |> should equal "ABCDE" } +module Immutable2 = + [)>] + let ``TaskSeq-choose returns all when chooser always returns Some`` variant = task { + let ts = Gen.getSeqImmutable variant + let! xs = ts |> TaskSeq.choose Some |> TaskSeq.toArrayAsync + xs |> should equal [| 1..10 |] + } + + [)>] + let ``TaskSeq-chooseAsync returns all when chooser always returns Some`` variant = task { + let ts = Gen.getSeqImmutable variant + + let! xs = + ts + |> TaskSeq.chooseAsync (fun x -> task { return Some x }) + |> TaskSeq.toArrayAsync + + xs |> should equal [| 1..10 |] + } + + [)>] + let ``TaskSeq-choose returns empty when chooser always returns None`` variant = task { + let ts = Gen.getSeqImmutable variant + + do! ts |> TaskSeq.choose (fun _ -> None) |> verifyEmpty + } + + [)>] + let ``TaskSeq-chooseAsync returns empty when chooser always returns None`` variant = task { + let ts = Gen.getSeqImmutable variant + + do! + ts + |> TaskSeq.chooseAsync (fun _ -> task { return None }) + |> verifyEmpty + } + + [] + let ``TaskSeq-choose with singleton sequence and Some chooser returns singleton`` () = task { + let! xs = + taskSeq { yield 42 } + |> TaskSeq.choose (fun x -> Some(x * 2)) + |> TaskSeq.toListAsync + + xs |> should equal [ 84 ] + } + + [] + let ``TaskSeq-choose with singleton sequence and None chooser returns empty`` () = + taskSeq { yield 42 } + |> TaskSeq.choose (fun _ -> None) + |> verifyEmpty + + [] + let ``TaskSeq-choose can change the element type`` () = task { + // choose maps int -> string option, verifying type-changing behavior + let chooser n = if n % 2 = 0 then Some(sprintf "even-%d" n) else None + + let! xs = + taskSeq { yield! [ 1..6 ] } + |> TaskSeq.choose chooser + |> TaskSeq.toListAsync + + xs |> should equal [ "even-2"; "even-4"; "even-6" ] + } + + [] + let ``TaskSeq-chooseAsync can change the element type`` () = task { + let chooser n = task { return if n % 2 = 0 then Some(sprintf "even-%d" n) else None } + + let! xs = + taskSeq { yield! [ 1..6 ] } + |> TaskSeq.chooseAsync chooser + |> TaskSeq.toListAsync + + xs |> should equal [ "even-2"; "even-4"; "even-6" ] + } + module SideEffects = [)>] let ``TaskSeq-choose applied multiple times`` variant = task { @@ -94,3 +172,41 @@ module SideEffects = String lettersK |> should equal "KLMNO" String lettersU |> should equal "UVWXY" } + + [] + let ``TaskSeq-choose evaluates each source element exactly once`` () = task { + let mutable count = 0 + + let ts = taskSeq { + for i in 1..5 do + count <- count + 1 + yield i + } + + let! xs = + ts + |> TaskSeq.choose (fun x -> if x < 3 then Some x else None) + |> TaskSeq.toListAsync + + count |> should equal 5 // all 5 elements were visited + xs |> should equal [ 1; 2 ] + } + + [] + let ``TaskSeq-chooseAsync evaluates each source element exactly once`` () = task { + let mutable count = 0 + + let ts = taskSeq { + for i in 1..5 do + count <- count + 1 + yield i + } + + let! xs = + ts + |> TaskSeq.chooseAsync (fun x -> task { return if x < 3 then Some x else None }) + |> TaskSeq.toListAsync + + count |> should equal 5 + xs |> should equal [ 1; 2 ] + } diff --git a/src/FSharp.Control.TaskSeq.Test/TaskSeq.Filter.Tests.fs b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Filter.Tests.fs index bc0fe54d..c43b5ec8 100644 --- a/src/FSharp.Control.TaskSeq.Test/TaskSeq.Filter.Tests.fs +++ b/src/FSharp.Control.TaskSeq.Test/TaskSeq.Filter.Tests.fs @@ -87,6 +87,50 @@ module Immutable = } +module Immutable2 = + [)>] + let ``TaskSeq-filter keeps all when predicate is always true`` variant = + Gen.getSeqImmutable variant + |> TaskSeq.filter (fun _ -> true) + |> verify1To10 + + [)>] + let ``TaskSeq-filterAsync keeps all when predicate is always true`` variant = + Gen.getSeqImmutable variant + |> TaskSeq.filterAsync (fun _ -> Task.fromResult true) + |> verify1To10 + + [)>] + let ``TaskSeq-filter returns empty when predicate is always false`` variant = + Gen.getSeqImmutable variant + |> TaskSeq.filter (fun _ -> false) + |> verifyEmpty + + [)>] + let ``TaskSeq-filterAsync returns empty when predicate is always false`` variant = + Gen.getSeqImmutable variant + |> TaskSeq.filterAsync (fun _ -> Task.fromResult false) + |> verifyEmpty + + [] + let ``TaskSeq-filter evaluates each element exactly once`` () = task { + let mutable count = 0 + + let ts = taskSeq { + for i in 1..5 do + count <- count + 1 + yield i + } + + let! xs = + ts + |> TaskSeq.filter (fun x -> x % 2 = 0) + |> TaskSeq.toListAsync + + count |> should equal 5 + xs |> should equal [ 2; 4 ] + } + module SideEffects = [)>] let ``TaskSeq-filter filters correctly`` variant = task {