Skip to content

Commit

Permalink
Fix some naming and test comments in TaskSeq.exists tests
Browse files Browse the repository at this point in the history
  • Loading branch information
abelbraaksma committed Mar 17, 2024
1 parent 15b9649 commit 219f89d
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 119 deletions.
8 changes: 4 additions & 4 deletions src/FSharp.Control.TaskSeq.Test/TaskSeq.Exists.Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ module Immutable =

module SideEffects =
[<Theory; ClassData(typeof<TestSideEffectTaskSeq>)>]
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

Expand All @@ -100,7 +100,7 @@ module SideEffects =
}

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

Expand Down Expand Up @@ -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!
Expand All @@ -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!
Expand Down
202 changes: 87 additions & 115 deletions src/FSharp.Control.TaskSeq.Test/TaskSeq.Forall.Tests.fs
Original file line number Diff line number Diff line change
@@ -1,124 +1,122 @@
module TaskSeq.Tests.Exists
module TaskSeq.Tests.Forall

open Xunit
open FsUnit.Xunit

open FSharp.Control

//
// TaskSeq.exists
// TaskSeq.existsAsyncc
// TaskSeq.forall
// TaskSeq.forallAsyncc
//

module EmptySeq =
[<Fact>]
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

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

[<Theory; ClassData(typeof<TestEmptyVariants>)>]
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 =
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
let ``TaskSeq-exists sad path returns false`` variant =
Gen.getSeqImmutable variant
|> TaskSeq.exists ((=) 0)
|> Task.map (should be False)

[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
let ``TaskSeq-existsAsync sad path return false`` variant =
Gen.getSeqImmutable variant
|> TaskSeq.existsAsync (fun x -> task { return x = 0 })
|> Task.map (should be False)

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

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

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

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

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

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

[<Fact>]
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 {
Expand All @@ -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
}

[<Fact>]
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 {
Expand All @@ -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))

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

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

[<Fact>]
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 {
Expand All @@ -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!
}

[<Fact>]
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 {
Expand All @@ -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!
}

0 comments on commit 219f89d

Please sign in to comment.