v2 / vlib / x / async / periodic_test.v
214 lines · 200 sloc · 4.38 KB · 15fb60b77ea6073658aa8355b247f2e1ae03b714
Raw
1import context
2import time
3import x.async as xasync
4
5fn test_every_runs_at_least_one_iteration() {
6 parent_ctx, cancel := xasync.with_cancel()
7 ran := chan bool{cap: 1}
8 result := chan string{cap: 1}
9 worker := spawn fn [parent_ctx, ran, result] () {
10 xasync.every(parent_ctx, 5 * time.millisecond, fn [ran] (mut ctx context.Context) ! {
11 _ = ctx
12 ran <- true
13 }) or {
14 result <- err.msg()
15 return
16 }
17 result <- 'ok'
18 }()
19
20 select {
21 did_run := <-ran {
22 assert did_run
23 }
24 1 * time.second {
25 assert false, 'periodic job did not run'
26 }
27 }
28 cancel()
29 select {
30 msg := <-result {
31 assert msg == 'context canceled'
32 }
33 1 * time.second {
34 assert false, 'every did not stop after cancellation'
35 }
36 }
37 worker.wait()
38}
39
40fn test_every_stops_on_cancellation() {
41 parent_ctx, cancel := xasync.with_cancel()
42 entered := chan bool{cap: 1}
43 result := chan string{cap: 1}
44 worker := spawn fn [parent_ctx, entered, result] () {
45 entered <- true
46 xasync.every(parent_ctx, 1 * time.second, fn (mut ctx context.Context) ! {
47 _ = ctx
48 }) or {
49 result <- err.msg()
50 return
51 }
52 result <- 'ok'
53 }()
54
55 select {
56 did_enter := <-entered {
57 assert did_enter
58 }
59 1 * time.second {
60 assert false, 'every worker did not start'
61 }
62 }
63 cancel()
64 select {
65 msg := <-result {
66 assert msg == 'context canceled'
67 }
68 1 * time.second {
69 assert false, 'every did not stop on context cancellation'
70 }
71 }
72 worker.wait()
73}
74
75fn test_every_returns_immediately_when_parent_is_already_canceled() {
76 parent_ctx, cancel := xasync.with_cancel()
77 cancel()
78 xasync.every(parent_ctx, 1 * time.second, fn (mut ctx context.Context) ! {
79 _ = ctx
80 }) or {
81 assert err.msg() == 'context canceled'
82 return
83 }
84 assert false
85}
86
87fn test_every_returns_iteration_error() {
88 parent_ctx, cancel := xasync.with_cancel()
89 result := chan string{cap: 1}
90 worker := spawn fn [parent_ctx, result] () {
91 xasync.every(parent_ctx, 5 * time.millisecond, fn (mut ctx context.Context) ! {
92 _ = ctx
93 return error('periodic failed')
94 }) or {
95 result <- err.msg()
96 return
97 }
98 result <- 'ok'
99 }()
100
101 select {
102 msg := <-result {
103 assert msg == 'periodic failed'
104 }
105 1 * time.second {
106 assert false, 'every did not return the periodic job error'
107 }
108 }
109 cancel()
110 worker.wait()
111}
112
113fn test_every_rejects_zero_interval() {
114 xasync.every(context.background(), 0 * time.millisecond, fn (mut ctx context.Context) ! {
115 _ = ctx
116 }) or {
117 assert err.msg() == 'async: interval must be positive'
118 return
119 }
120 assert false
121}
122
123fn test_every_rejects_negative_interval() {
124 xasync.every(context.background(), -1 * time.millisecond, fn (mut ctx context.Context) ! {
125 _ = ctx
126 }) or {
127 assert err.msg() == 'async: interval must be positive'
128 return
129 }
130 assert false
131}
132
133fn test_every_rejects_nil_job() {
134 nil_job := unsafe { xasync.JobFn(nil) }
135 xasync.every(context.background(), 1 * time.second, nil_job) or {
136 assert err.msg() == 'async: job function is nil'
137 return
138 }
139 assert false
140}
141
142fn test_every_does_not_overlap_iterations() {
143 parent_ctx, cancel := xasync.with_cancel()
144 active := chan bool{cap: 1}
145 active <- true
146 entered := chan bool{cap: 2}
147 release := chan bool{cap: 2}
148 overlap := chan bool{cap: 1}
149 result := chan string{cap: 1}
150 worker := spawn fn [parent_ctx, active, entered, release, overlap, result] () {
151 xasync.every(parent_ctx, 5 * time.millisecond, fn [active, entered, release, overlap] (mut ctx context.Context) ! {
152 select {
153 _ := <-active {}
154 else {
155 overlap <- true
156 return error('periodic overlap')
157 }
158 }
159 entered <- true
160 done := ctx.done()
161 select {
162 _ := <-release {}
163 _ := <-done {
164 active <- true
165 return ctx.err()
166 }
167 }
168 active <- true
169 }) or {
170 result <- err.msg()
171 return
172 }
173 result <- 'ok'
174 }()
175
176 wait_for_periodic_entry(entered)
177 select {
178 _ := <-entered {
179 assert false, 'periodic iterations overlapped while first job was still running'
180 }
181 50 * time.millisecond {}
182 }
183 select {
184 did_overlap := <-overlap {
185 assert !did_overlap
186 }
187 else {}
188 }
189
190 release <- true
191 wait_for_periodic_entry(entered)
192 cancel()
193 release <- true
194 select {
195 msg := <-result {
196 assert msg == 'context canceled'
197 }
198 1 * time.second {
199 assert false, 'every did not stop after non-overlap test cancellation'
200 }
201 }
202 worker.wait()
203}
204
205fn wait_for_periodic_entry(entered chan bool) {
206 select {
207 did_enter := <-entered {
208 assert did_enter
209 }
210 1 * time.second {
211 assert false, 'periodic job did not enter'
212 }
213 }
214}
215