Skip to content

Commit 15b9649

Browse files
committed
Add unit tests for TaskSeq.forall and TaskSeq.forallAsync
1 parent e9c947b commit 15b9649

File tree

2 files changed

+229
-0
lines changed

2 files changed

+229
-0
lines changed

src/FSharp.Control.TaskSeq.Test/FSharp.Control.TaskSeq.Test.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<Compile Include="TaskSeq.FindIndex.Tests.fs" />
2525
<Compile Include="TaskSeq.Find.Tests.fs" />
2626
<Compile Include="TaskSeq.Fold.Tests.fs" />
27+
<Compile Include="TaskSeq.Forall.Tests.fs" />
2728
<Compile Include="TaskSeq.Head.Tests.fs" />
2829
<Compile Include="TaskSeq.Indexed.Tests.fs" />
2930
<Compile Include="TaskSeq.Init.Tests.fs" />
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
module TaskSeq.Tests.Exists
2+
3+
open Xunit
4+
open FsUnit.Xunit
5+
6+
open FSharp.Control
7+
8+
//
9+
// TaskSeq.exists
10+
// TaskSeq.existsAsyncc
11+
//
12+
13+
module EmptySeq =
14+
[<Fact>]
15+
let ``Null source is invalid`` () =
16+
assertNullArg
17+
<| fun () -> TaskSeq.exists (fun _ -> false) null
18+
19+
assertNullArg
20+
<| fun () -> TaskSeq.existsAsync (fun _ -> Task.fromResult false) null
21+
22+
[<Theory; ClassData(typeof<TestEmptyVariants>)>]
23+
let ``TaskSeq-exists returns false`` variant =
24+
Gen.getEmptyVariant variant
25+
|> TaskSeq.exists ((=) 12)
26+
|> Task.map (should be False)
27+
28+
[<Theory; ClassData(typeof<TestEmptyVariants>)>]
29+
let ``TaskSeq-existsAsync returns false`` variant =
30+
Gen.getEmptyVariant variant
31+
|> TaskSeq.existsAsync (fun x -> task { return x = 12 })
32+
|> Task.map (should be False)
33+
34+
module Immutable =
35+
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
36+
let ``TaskSeq-exists sad path returns false`` variant =
37+
Gen.getSeqImmutable variant
38+
|> TaskSeq.exists ((=) 0)
39+
|> Task.map (should be False)
40+
41+
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
42+
let ``TaskSeq-existsAsync sad path return false`` variant =
43+
Gen.getSeqImmutable variant
44+
|> TaskSeq.existsAsync (fun x -> task { return x = 0 })
45+
|> Task.map (should be False)
46+
47+
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
48+
let ``TaskSeq-exists happy path middle of seq`` variant =
49+
Gen.getSeqImmutable variant
50+
|> TaskSeq.exists (fun x -> x < 6 && x > 4)
51+
|> Task.map (should be True)
52+
53+
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
54+
let ``TaskSeq-existsAsync happy path middle of seq`` variant =
55+
Gen.getSeqImmutable variant
56+
|> TaskSeq.existsAsync (fun x -> task { return x < 6 && x > 4 })
57+
|> Task.map (should be True)
58+
59+
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
60+
let ``TaskSeq-exists happy path first item of seq`` variant =
61+
Gen.getSeqImmutable variant
62+
|> TaskSeq.exists ((=) 1)
63+
|> Task.map (should be True)
64+
65+
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
66+
let ``TaskSeq-existsAsync happy path first item of seq`` variant =
67+
Gen.getSeqImmutable variant
68+
|> TaskSeq.existsAsync (fun x -> task { return x = 1 })
69+
|> Task.map (should be True)
70+
71+
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
72+
let ``TaskSeq-exists happy path last item of seq`` variant =
73+
Gen.getSeqImmutable variant
74+
|> TaskSeq.exists ((=) 10)
75+
|> Task.map (should be True)
76+
77+
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
78+
let ``TaskSeq-existsAsync happy path last item of seq`` variant =
79+
Gen.getSeqImmutable variant
80+
|> TaskSeq.existsAsync (fun x -> task { return x = 10 })
81+
|> Task.map (should be True)
82+
83+
module SideEffects =
84+
[<Theory; ClassData(typeof<TestSideEffectTaskSeq>)>]
85+
let ``TaskSeq-exists KeyNotFoundException only sometimes for mutated state`` variant = task {
86+
let ts = Gen.getSeqWithSideEffect variant
87+
let finder = (=) 11
88+
89+
// first: false
90+
let! found = TaskSeq.exists finder ts
91+
found |> should be False
92+
93+
// find again: found now, because of side effects
94+
let! found = TaskSeq.exists finder ts
95+
found |> should be True
96+
97+
// find once more: false
98+
let! found = TaskSeq.exists finder ts
99+
found |> should be False
100+
}
101+
102+
[<Theory; ClassData(typeof<TestSideEffectTaskSeq>)>]
103+
let ``TaskSeq-existsAsync KeyNotFoundException only sometimes for mutated state`` variant = task {
104+
let ts = Gen.getSeqWithSideEffect variant
105+
let finder x = task { return x = 11 }
106+
107+
// first: false
108+
let! found = TaskSeq.existsAsync finder ts
109+
found |> should be False
110+
111+
// find again: found now, because of side effects
112+
let! found = TaskSeq.existsAsync finder ts
113+
found |> should be True
114+
115+
// find once more: false
116+
let! found = TaskSeq.existsAsync finder ts
117+
found |> should be False
118+
}
119+
120+
[<Fact>]
121+
let ``TaskSeq-exists _specialcase_ prove we don't read past the found item`` () = task {
122+
let mutable i = 0
123+
124+
let ts = taskSeq {
125+
for _ in 0..9 do
126+
i <- i + 1
127+
yield i
128+
}
129+
130+
let! found = ts |> TaskSeq.exists ((=) 3)
131+
found |> should be True
132+
i |> should equal 3 // only partial evaluation!
133+
134+
// find next item. We do get a new iterator, but mutable state is now starting at '3', so first item now returned is '4'.
135+
let! found = ts |> TaskSeq.exists ((=) 4)
136+
found |> should be True
137+
i |> should equal 4 // only partial evaluation!
138+
}
139+
140+
[<Fact>]
141+
let ``TaskSeq-existsAsync _specialcase_ prove we don't read past the found item`` () = task {
142+
let mutable i = 0
143+
144+
let ts = taskSeq {
145+
for _ in 0..9 do
146+
i <- i + 1
147+
yield i
148+
}
149+
150+
let! found = ts |> TaskSeq.existsAsync (fun x -> task { return x = 3 })
151+
found |> should be True
152+
i |> should equal 3 // only partial evaluation!
153+
154+
// find next item. We do get a new iterator, but mutable state is now starting at '3', so first item now returned is '4'.
155+
let! found = ts |> TaskSeq.existsAsync (fun x -> task { return x = 4 })
156+
found |> should be True
157+
i |> should equal 4
158+
}
159+
160+
[<Fact>]
161+
let ``TaskSeq-exists _specialcase_ prove we don't read past the found item v2`` () = task {
162+
let mutable i = 0
163+
164+
let ts = taskSeq {
165+
yield 42
166+
i <- i + 1
167+
i <- i + 1
168+
}
169+
170+
let! found = ts |> TaskSeq.exists ((=) 42)
171+
found |> should be True
172+
i |> should equal 0 // because no MoveNext after found item, the last statements are not executed
173+
}
174+
175+
[<Fact>]
176+
let ``TaskSeq-existsAsync _specialcase_ prove we don't read past the found item v2`` () = task {
177+
let mutable i = 0
178+
179+
let ts = taskSeq {
180+
yield 42
181+
i <- i + 1
182+
i <- i + 1
183+
}
184+
185+
let! found = ts |> TaskSeq.existsAsync (fun x -> task { return x = 42 })
186+
found |> should be True
187+
i |> should equal 0 // because no MoveNext after found item, the last statements are not executed
188+
}
189+
190+
[<Fact>]
191+
let ``TaskSeq-exists _specialcase_ prove statement after yield is not evaluated`` () = task {
192+
let mutable i = 0
193+
194+
let ts = taskSeq {
195+
for _ in 0..9 do
196+
yield i
197+
i <- i + 1
198+
}
199+
200+
let! found = ts |> TaskSeq.exists ((=) 0)
201+
found |> should be True
202+
i |> should equal 0 // notice that it should be one higher if the statement after 'yield' is evaluated
203+
204+
// find some next item. We do get a new iterator, but mutable state is now starting at '1'
205+
let! found = ts |> TaskSeq.exists ((=) 4)
206+
found |> should be True
207+
i |> should equal 4 // only partial evaluation!
208+
}
209+
210+
[<Fact>]
211+
let ``TaskSeq-existsAsync _specialcase_ prove statement after yield is not evaluated`` () = task {
212+
let mutable i = 0
213+
214+
let ts = taskSeq {
215+
for _ in 0..9 do
216+
yield i
217+
i <- i + 1
218+
}
219+
220+
let! found = ts |> TaskSeq.existsAsync (fun x -> task { return x = 0 })
221+
found |> should be True
222+
i |> should equal 0 // notice that it should be one higher if the statement after 'yield' is evaluated
223+
224+
// find some next item. We do get a new iterator, but mutable state is now starting at '1'
225+
let! found = ts |> TaskSeq.existsAsync (fun x -> task { return x = 4 })
226+
found |> should be True
227+
i |> should equal 4 // only partial evaluation!
228+
}

0 commit comments

Comments
 (0)