-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathTaskSeq.Head.Tests.fs
187 lines (141 loc) · 5.44 KB
/
TaskSeq.Head.Tests.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
module TaskSeq.Tests.Head
open System
open Xunit
open FsUnit.Xunit
open FSharp.Control
//
// TaskSeq.head
// TaskSeq.tryHead
//
module EmptySeq =
[<Fact>]
let ``Null source is invalid`` () =
assertNullArg <| fun () -> TaskSeq.head null
assertNullArg <| fun () -> TaskSeq.tryHead null
[<Theory; ClassData(typeof<TestEmptyVariants>)>]
let ``TaskSeq-head throws`` variant = task {
fun () -> Gen.getEmptyVariant variant |> TaskSeq.head |> Task.ignore
|> should throwAsyncExact typeof<ArgumentException>
}
[<Theory; ClassData(typeof<TestEmptyVariants>)>]
let ``TaskSeq-tryHead returns None`` variant = task {
let! nothing = Gen.getEmptyVariant variant |> TaskSeq.tryHead
nothing |> should be None'
}
[<Fact>]
let ``TaskSeq-head throws, but side effect is executed`` () = task {
let mutable x = 0
fun () -> taskSeq { do x <- x + 1 } |> TaskSeq.head |> Task.ignore
|> should throwAsyncExact typeof<ArgumentException>
// side effect must have run!
x |> should equal 1
}
module Immutable =
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
let ``TaskSeq-head gets the head item of longer sequence`` variant = task {
let ts = Gen.getSeqImmutable variant
let! head = TaskSeq.head ts
head |> should equal 1
let! head = TaskSeq.head ts //immutable, so re-iteration does not change outcome
head |> should equal 1
}
[<Theory; ClassData(typeof<TestImmTaskSeq>)>]
let ``TaskSeq-tryHead gets the head item of longer sequence`` variant = task {
let ts = Gen.getSeqImmutable variant
let! head = TaskSeq.tryHead ts
head |> should equal (Some 1)
let! head = TaskSeq.tryHead ts //immutable, so re-iteration does not change outcome
head |> should equal (Some 1)
}
[<Fact>]
let ``TaskSeq-head gets the only item in a singleton sequence`` () = task {
let ts = taskSeq { yield 42 }
let! head = TaskSeq.head ts
head |> should equal 42
let! head = TaskSeq.head ts // doing it twice is fine
head |> should equal 42
}
[<Fact>]
let ``TaskSeq-tryHead gets the only item in a singleton sequence`` () = task {
let ts = taskSeq { yield 42 }
let! head = TaskSeq.tryHead ts
head |> should equal (Some 42)
let! head = TaskSeq.tryHead ts // doing it twice is fine
head |> should equal (Some 42)
}
module SideEffects =
[<Fact>]
let ``TaskSeq-head __special-case__ prove it does not read beyond first yield`` () = task {
let mutable x = 42
let one = taskSeq {
yield x
x <- x + 1 // we never get here
}
let! fortyTwo = one |> TaskSeq.head
let! stillFortyTwo = one |> TaskSeq.head // the statement after 'yield' will never be reached
fortyTwo |> should equal 42
stillFortyTwo |> should equal 42
}
[<Fact>]
let ``TaskSeq-tryHead __special-case__ prove it does not read beyond first yield`` () = task {
let mutable x = 42
let one = taskSeq {
yield x
x <- x + 1 // we never get here
}
let! fortyTwo = one |> TaskSeq.tryHead
fortyTwo |> should equal (Some 42)
// the statement after 'yield' will never be reached, the mutable will not be updated
let! stillFortyTwo = one |> TaskSeq.tryHead
stillFortyTwo |> should equal (Some 42)
}
[<Fact>]
let ``TaskSeq-head __special-case__ prove early side effect is executed`` () = task {
let mutable x = 42
let one = taskSeq {
x <- x + 1
x <- x + 1
yield 42
x <- x + 200 // we won't get here!
}
let! fortyTwo = one |> TaskSeq.head
fortyTwo |> should equal 42
x |> should equal 44
let! fortyTwo = one |> TaskSeq.head
fortyTwo |> should equal 42
x |> should equal 46
}
[<Fact>]
let ``TaskSeq-tryHead __special-case__ prove early side effect is executed`` () = task {
let mutable x = 42
let one = taskSeq {
x <- x + 1
x <- x + 1
yield 42
x <- x + 200 // we won't get here!
}
let! fortyTwo = one |> TaskSeq.tryHead
fortyTwo |> should equal (Some 42)
x |> should equal 44
let! fortyTwo = one |> TaskSeq.tryHead
fortyTwo |> should equal (Some 42)
x |> should equal 46
}
[<Theory; ClassData(typeof<TestSideEffectTaskSeq>)>]
let ``TaskSeq-head gets the head item in a longer sequence, with mutation`` variant = task {
let ts = Gen.getSeqWithSideEffect variant
let! ten = TaskSeq.head ts
ten |> should equal 1
// side effect, reiterating causes it to execute again!
let! twenty = TaskSeq.head ts
twenty |> should not' (equal 1) // different test data changes first item counter differently
}
[<Theory; ClassData(typeof<TestSideEffectTaskSeq>)>]
let ``TaskSeq-tryHead gets the head item in a longer sequence, with mutation`` variant = task {
let ts = Gen.getSeqWithSideEffect variant
let! ten = TaskSeq.tryHead ts
ten |> should equal (Some 1)
// side effect, reiterating causes it to execute again!
let! twenty = TaskSeq.tryHead ts
twenty |> should not' (equal (Some 1)) // different test data changes first item counter differently
}