Skip to content

Commit 82558c2

Browse files
committed
Add TaskSeq.tail/tryTail tests
1 parent 8486e1b commit 82558c2

File tree

3 files changed

+203
-9
lines changed

3 files changed

+203
-9
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
@@ -35,6 +35,7 @@
3535
<Compile Include="TaskSeq.Map.Tests.fs" />
3636
<Compile Include="TaskSeq.OfXXX.Tests.fs" />
3737
<Compile Include="TaskSeq.Pick.Tests.fs" />
38+
<Compile Include="TaskSeq.Tail.Tests.fs" />
3839
<Compile Include="TaskSeq.ToXXX.Tests.fs" />
3940
<Compile Include="TaskSeq.Zip.Tests.fs" />
4041
<Compile Include="TaskSeq.Tests.CE.fs" />

src/FSharp.Control.TaskSeq.Test/TaskSeq.Last.Tests.fs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,19 @@ open FSharp.Control
1515
module EmptySeq =
1616

1717
[<Theory; ClassData(typeof<TestEmptyVariants>)>]
18-
let ``TaskSeq-last throws on empty sequences`` variant = task {
18+
let ``TaskSeq-last throws`` variant = task {
1919
fun () -> Gen.getEmptyVariant variant |> TaskSeq.last |> Task.ignore
2020
|> should throwAsyncExact typeof<ArgumentException>
2121
}
2222

2323
[<Theory; ClassData(typeof<TestEmptyVariants>)>]
24-
let ``TaskSeq-tryLast returns None on empty sequences`` variant = task {
24+
let ``TaskSeq-tryLast returns None`` variant = task {
2525
let! nothing = Gen.getEmptyVariant variant |> TaskSeq.tryLast
2626
nothing |> should be None'
2727
}
2828

2929
[<Fact>]
30-
let ``TaskSeq-last throws on empty sequences, but side effect is executed`` () = task {
30+
let ``TaskSeq-last executes side effect`` () = task {
3131
let mutable x = 0
3232

3333
fun () -> taskSeq { do x <- x + 1 } |> TaskSeq.last |> Task.ignore
@@ -37,10 +37,21 @@ module EmptySeq =
3737
x |> should equal 1
3838
}
3939

40+
[<Fact>]
41+
let ``TaskSeq-tryLast executes side effect`` () = task {
42+
let mutable x = 0
43+
44+
let! nothing = taskSeq { do x <- x + 1 } |> TaskSeq.tryLast
45+
nothing |> should be None'
46+
47+
// side effect must have run!
48+
x |> should equal 1
49+
}
50+
4051

4152
module Immutable =
4253
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
43-
let ``TaskSeq-last gets the last item in a longer sequence`` variant = task {
54+
let ``TaskSeq-last gets the last item`` variant = task {
4455
let ts = Gen.getSeqImmutable variant
4556

4657
let! last = TaskSeq.last ts
@@ -62,7 +73,7 @@ module Immutable =
6273
}
6374

6475
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
65-
let ``TaskSeq-tryLast gets the last item in a longer sequence`` variant = task {
76+
let ``TaskSeq-tryLast gets the last item`` variant = task {
6677
let ts = Gen.getSeqImmutable variant
6778

6879
let! last = TaskSeq.tryLast ts
@@ -86,7 +97,7 @@ module Immutable =
8697

8798
module SideEffects =
8899
[<Fact>]
89-
let ``TaskSeq-last gets the only item in a singleton sequence, with change`` () = task {
100+
let ``TaskSeq-last executes side effect after first item`` () = task {
90101
let mutable x = 42
91102

92103
let one = taskSeq {
@@ -102,7 +113,7 @@ module SideEffects =
102113
}
103114

104115
[<Fact>]
105-
let ``TaskSeq-tryLast gets the only item in a singleton sequence, with change`` () = task {
116+
let ``TaskSeq-tryLast executes side effect after first item`` () = task {
106117
let mutable x = 42
107118

108119
let one = taskSeq {
@@ -120,7 +131,7 @@ module SideEffects =
120131
}
121132

122133
[<Theory; ClassData(typeof<TestSideEffectTaskSeq>)>]
123-
let ``TaskSeq-last gets the last item in a longer sequence, with change`` variant = task {
134+
let ``TaskSeq-last gets the last item`` variant = task {
124135
let ts = Gen.getSeqWithSideEffect variant
125136

126137
let! ten = TaskSeq.last ts
@@ -132,7 +143,7 @@ module SideEffects =
132143
}
133144

134145
[<Theory; ClassData(typeof<TestSideEffectTaskSeq>)>]
135-
let ``TaskSeq-tryLast gets the last item in a longer sequence, with change`` variant = task {
146+
let ``TaskSeq-tryLast gets the last item`` variant = task {
136147
let ts = Gen.getSeqWithSideEffect variant
137148

138149
let! ten = TaskSeq.tryLast ts
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
module TaskSeq.Tests.Tail
2+
3+
open System
4+
open Xunit
5+
open FsUnit.Xunit
6+
open FsToolkit.ErrorHandling
7+
8+
open FSharp.Control
9+
10+
//
11+
// TaskSeq.tail
12+
// TaskSeq.tryTail
13+
//
14+
15+
module EmptySeq =
16+
17+
[<Theory; ClassData(typeof<TestEmptyVariants>)>]
18+
let ``TaskSeq-tail throws`` variant = task {
19+
fun () -> Gen.getEmptyVariant variant |> TaskSeq.tail |> Task.ignore
20+
|> should throwAsyncExact typeof<ArgumentException>
21+
}
22+
23+
[<Theory; ClassData(typeof<TestEmptyVariants>)>]
24+
let ``TaskSeq-tryTail returns None`` variant = task {
25+
let! nothing = Gen.getEmptyVariant variant |> TaskSeq.tryTail
26+
nothing |> should be None'
27+
}
28+
29+
[<Fact>]
30+
let ``TaskSeq-tail executes side effect`` () = task {
31+
let mutable x = 0
32+
33+
fun () -> taskSeq { do x <- x + 1 } |> TaskSeq.tail |> Task.ignore
34+
|> should throwAsyncExact typeof<ArgumentException>
35+
36+
// side effect must have run!
37+
x |> should equal 1
38+
}
39+
40+
[<Fact>]
41+
let ``TaskSeq-tryTail executes side effect`` () = task {
42+
let mutable x = 0
43+
44+
let! nothing = taskSeq { do x <- x + 1 } |> TaskSeq.tryTail
45+
nothing |> should be None'
46+
47+
// side effect must have run!
48+
x |> should equal 1
49+
}
50+
51+
52+
module Immutable =
53+
let verifyTail tail =
54+
tail
55+
|> TaskSeq.toArrayAsync
56+
|> Task.map (should equal [| 2..10 |])
57+
58+
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
59+
let ``TaskSeq-tail gets the tail items`` variant = task {
60+
let ts = Gen.getSeqImmutable variant
61+
62+
let! tail = TaskSeq.tail ts
63+
do! verifyTail tail
64+
65+
let! tail = TaskSeq.tail ts //immutable, so re-iteration does not change outcome
66+
do! verifyTail tail
67+
}
68+
69+
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
70+
let ``TaskSeq-tryTail gets the tail item`` variant = task {
71+
let ts = Gen.getSeqImmutable variant
72+
73+
match! TaskSeq.tryTail ts with
74+
| Some tail -> do! verifyTail tail
75+
| x -> do x |> should not' (be None')
76+
77+
}
78+
79+
[<Fact>]
80+
let ``TaskSeq-tail return empty from a singleton sequence`` () = task {
81+
let ts = taskSeq { yield 42 }
82+
83+
let! tail = TaskSeq.tail ts
84+
do! verifyEmpty tail
85+
}
86+
87+
[<Fact>]
88+
let ``TaskSeq-tryTail gets the only item in a singleton sequence`` () = task {
89+
let ts = taskSeq { yield 42 }
90+
91+
match! TaskSeq.tryTail ts with
92+
| Some tail -> do! verifyEmpty tail
93+
| x -> do x |> should not' (be None')
94+
}
95+
96+
97+
module SideEffects =
98+
[<Fact>]
99+
let ``TaskSeq-tail does not execute side effect after the first item in singleton`` () = task {
100+
let mutable x = 42
101+
102+
let one = taskSeq {
103+
yield x
104+
x <- x + 1 // <--- we should never get here
105+
}
106+
107+
let! _ = one |> TaskSeq.tail
108+
let! _ = one |> TaskSeq.tail // side effect, re-iterating!
109+
110+
x |> should equal 42
111+
}
112+
113+
[<Fact>]
114+
let ``TaskSeq-tryTail does not execute execute side effect after first item in singleton`` () = task {
115+
let mutable x = 42
116+
117+
let one = taskSeq {
118+
yield x
119+
x <- x + 1 // <--- we should never get here
120+
}
121+
122+
let! _ = one |> TaskSeq.tryTail
123+
let! _ = one |> TaskSeq.tryTail
124+
125+
// side effect, reiterating causes it to execute again!
126+
x |> should equal 42
127+
128+
}
129+
130+
[<Fact>]
131+
let ``TaskSeq-tail executes side effect partially`` () = task {
132+
let mutable x = 42
133+
134+
let ts = taskSeq {
135+
x <- x + 1 // <--- executed on tail, but not materializing rest
136+
yield 1
137+
x <- x + 1 // <--- not executed on tail, but on materializing rest
138+
yield 2
139+
x <- x + 1 // <--- id
140+
}
141+
142+
let! tail1 = ts |> TaskSeq.tail
143+
x |> should equal 43 // test side effect runs 1x
144+
145+
let! tail2 = ts |> TaskSeq.tail
146+
x |> should equal 44 // test side effect ran again only 1x
147+
148+
let! len = TaskSeq.length tail1
149+
x |> should equal 46 // now 2nd & 3rd side effect runs, but not the first
150+
len |> should equal 1
151+
152+
let! len = TaskSeq.length tail2
153+
x |> should equal 48 // now again 2nd & 3rd side effect runs, but not the first
154+
len |> should equal 1
155+
}
156+
157+
[<Fact>]
158+
let ``TaskSeq-tryTail executes side effect partially`` () = task {
159+
let mutable x = 42
160+
161+
let ts = taskSeq {
162+
x <- x + 1 // <--- executed on tail, but not materializing rest
163+
yield 1
164+
x <- x + 1 // <--- not executed on tail, but on materializing rest
165+
yield 2
166+
x <- x + 1 // <--- id
167+
}
168+
169+
let! tail1 = ts |> TaskSeq.tryTail
170+
x |> should equal 43 // test side effect runs 1x
171+
172+
let! tail2 = ts |> TaskSeq.tryTail
173+
x |> should equal 44 // test side effect ran again only 1x
174+
175+
let! len = TaskSeq.length tail1.Value
176+
x |> should equal 46 // now 2nd side effect runs, but not the first
177+
len |> should equal 1
178+
179+
let! len = TaskSeq.length tail2.Value
180+
x |> should equal 48 // now again 2nd side effect runs, but not the first
181+
len |> should equal 1
182+
}

0 commit comments

Comments
 (0)