@@ -133,3 +133,87 @@ module SideEffects =
133133 |> TaskSeq.map ((+) '@')
134134 |> TaskSeq.toArrayAsync
135135 |> Task.map (String >> should equal "ABCDE")
136+
137+ [<Theory; InlineData(false, false); InlineData(true, false); InlineData(false, true); InlineData(true, true)>]
138+ let ``TaskSeq-takeWhile(Inclusive)?(Async)? __special-case__ prove it does not read beyond the failing yield`` (inclusive, async) = task {
139+ let mutable x = 42 // for this test, the potential mutation should not actually occur
140+
141+ let items = taskSeq {
142+ yield x // Always passes the test; always returned
143+ yield x * 2 // the failing item (which will also be yielded in the result when using *Inclusive)
144+ x <- x + 1 // we are proving we never get here
145+ }
146+
147+ let f =
148+ match inclusive, async with
149+ | false, false -> TaskSeq.takeWhile (fun x -> x = 42)
150+ | true, false -> TaskSeq.takeWhileInclusive (fun x -> x = 42)
151+ | false, true -> TaskSeq.takeWhileAsync (fun x -> task { return x = 42 })
152+ | true, true -> TaskSeq.takeWhileInclusiveAsync (fun x -> task { return x = 42 })
153+
154+ let expected = if inclusive then [| 42; 84 |] else [| 42 |]
155+
156+ let! first = items |> f |> TaskSeq.toArrayAsync
157+ let! repeat = items |> f |> TaskSeq.toArrayAsync
158+
159+ first |> should equal expected
160+ repeat |> should equal expected
161+ x |> should equal 42
162+ }
163+
164+ [<Theory; InlineData(false, false); InlineData(true, false); InlineData(false, true); InlineData(true, true)>]
165+ let ``TaskSeq-takeWhile(Inclusive)?(Async)? __special-case__ prove side effects are executed`` (inclusive, async) = task {
166+ let mutable x = 41
167+
168+ let items = taskSeq {
169+ x <- x + 1
170+ yield x
171+ x <- x + 2
172+ yield x * 2
173+ x <- x + 200 // as previously proven, we should not trigger this
174+ }
175+
176+ let f =
177+ match inclusive, async with
178+ | false, false -> TaskSeq.takeWhile (fun x -> x < 50)
179+ | true, false -> TaskSeq.takeWhileInclusive (fun x -> x < 50)
180+ | false, true -> TaskSeq.takeWhileAsync (fun x -> task { return x < 50 })
181+ | true, true -> TaskSeq.takeWhileInclusiveAsync (fun x -> task { return x < 50 })
182+
183+ let expectedFirst = if inclusive then [| 42; 44*2 |] else [| 42 |]
184+ let expectedRepeat = if inclusive then [| 45; 47*2 |] else [| 45 |]
185+
186+ let! first = items |> f |> TaskSeq.toArrayAsync
187+ x |> should equal 44
188+ let! repeat = items |> f |> TaskSeq.toArrayAsync
189+ x |> should equal 47
190+
191+ first |> should equal expectedFirst
192+ repeat |> should equal expectedRepeat
193+ }
194+
195+ [<Theory; ClassData(typeof<TestSideEffectTaskSeq>)>]
196+ let ``TaskSeq-takeWhile consumes the prefix of a longer sequence, with mutation`` variant = task {
197+ let ts = Gen.getSeqWithSideEffect variant
198+
199+ let! first = TaskSeq.takeWhile (fun x -> x < 5) ts |> TaskSeq.toArrayAsync
200+ let expected = [| 1..4 |]
201+ first |> should equal expected
202+
203+ // side effect, reiterating causes it to resume from where we left it (minus the failing item)
204+ let! repeat = TaskSeq.takeWhile (fun x -> x < 5) ts |> TaskSeq.toArrayAsync
205+ repeat |> should not' (equal expected)
206+ }
207+
208+ [<Theory; ClassData(typeof<TestSideEffectTaskSeq>)>]
209+ let ``TaskSeq-takeWhileInclusiveAsync consumes the prefix for a longer sequence, with mutation`` variant = task {
210+ let ts = Gen.getSeqWithSideEffect variant
211+
212+ let! first = TaskSeq.takeWhileInclusiveAsync (fun x -> task { return x < 5 }) ts |> TaskSeq.toArrayAsync
213+ let expected = [| 1..5 |]
214+ first |> should equal expected
215+
216+ // side effect, reiterating causes it to resume from where we left it (minus the failing item)
217+ let! repeat = TaskSeq.takeWhileInclusiveAsync (fun x -> task { return x < 5 }) ts |> TaskSeq.toArrayAsync
218+ repeat |> should not' (equal expected)
219+ }
0 commit comments