Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions src/FSharp.Control.TaskSeq.Test/TaskSeq.Choose.Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,84 @@ module Immutable =
String letters2 |> should equal "ABCDE"
}

module Immutable2 =
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
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 |]
}

[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
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 |]
}

[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
let ``TaskSeq-choose returns empty when chooser always returns None`` variant = task {
let ts = Gen.getSeqImmutable variant

do! ts |> TaskSeq.choose (fun _ -> None) |> verifyEmpty
}

[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
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
}

[<Fact>]
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 ]
}

[<Fact>]
let ``TaskSeq-choose with singleton sequence and None chooser returns empty`` () =
taskSeq { yield 42 }
|> TaskSeq.choose (fun _ -> None)
|> verifyEmpty

[<Fact>]
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" ]
}

[<Fact>]
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 =
[<Theory; ClassData(typeof<TestSideEffectTaskSeq>)>]
let ``TaskSeq-choose applied multiple times`` variant = task {
Expand Down Expand Up @@ -94,3 +172,41 @@ module SideEffects =
String lettersK |> should equal "KLMNO"
String lettersU |> should equal "UVWXY"
}

[<Fact>]
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 ]
}

[<Fact>]
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 ]
}
44 changes: 44 additions & 0 deletions src/FSharp.Control.TaskSeq.Test/TaskSeq.Filter.Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,50 @@ module Immutable =

}

module Immutable2 =
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
let ``TaskSeq-filter keeps all when predicate is always true`` variant =
Gen.getSeqImmutable variant
|> TaskSeq.filter (fun _ -> true)
|> verify1To10

[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
let ``TaskSeq-filterAsync keeps all when predicate is always true`` variant =
Gen.getSeqImmutable variant
|> TaskSeq.filterAsync (fun _ -> Task.fromResult true)
|> verify1To10

[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
let ``TaskSeq-filter returns empty when predicate is always false`` variant =
Gen.getSeqImmutable variant
|> TaskSeq.filter (fun _ -> false)
|> verifyEmpty

[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
let ``TaskSeq-filterAsync returns empty when predicate is always false`` variant =
Gen.getSeqImmutable variant
|> TaskSeq.filterAsync (fun _ -> Task.fromResult false)
|> verifyEmpty

[<Fact>]
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 =
[<Theory; ClassData(typeof<TestSideEffectTaskSeq>)>]
let ``TaskSeq-filter filters correctly`` variant = task {
Expand Down