@@ -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