v2 / vlib / x / async / task_test.v
184 lines · 172 sloc · 3.95 KB · 15fb60b77ea6073658aa8355b247f2e1ae03b714
Raw
1import context
2import time
3import x.async as xasync
4
5fn test_task_returns_value() {
6 mut task := xasync.run[int](fn (mut ctx context.Context) !int {
7 _ = ctx
8 return 42
9 })!
10 assert task.wait()! == 42
11}
12
13fn test_task_returns_error() {
14 mut task := xasync.run[int](fn (mut ctx context.Context) !int {
15 _ = ctx
16 return error('task failed')
17 })!
18 task.wait() or {
19 assert err.msg() == 'task failed'
20 return
21 }
22 assert false
23}
24
25fn test_task_wait_is_one_shot() {
26 mut task := xasync.run[string](fn (mut ctx context.Context) !string {
27 _ = ctx
28 return 'ok'
29 })!
30 assert task.wait()! == 'ok'
31 task.wait() or {
32 assert err.msg() == 'async: task wait was already called'
33 return
34 }
35 assert false
36}
37
38fn test_task_concurrent_wait_is_one_shot_without_deadlock() {
39 release := chan bool{cap: 1}
40 results := chan string{cap: 2}
41 mut task := xasync.run[int](fn [release] (mut ctx context.Context) !int {
42 _ = ctx
43 _ := <-release
44 return 7
45 })!
46
47 first_waiter := spawn fn [mut task, results] () {
48 value := task.wait() or {
49 results <- 'error:${err.msg()}'
50 return
51 }
52 results <- 'value:${value}'
53 }()
54 second_waiter := spawn fn [mut task, results] () {
55 value := task.wait() or {
56 results <- 'error:${err.msg()}'
57 return
58 }
59 results <- 'value:${value}'
60 }()
61 release <- true
62
63 mut saw_value := false
64 mut saw_second_wait_error := false
65 for _ in 0 .. 2 {
66 select {
67 result := <-results {
68 match result {
69 'value:7' {
70 saw_value = true
71 }
72 'error:async: task wait was already called' {
73 saw_second_wait_error = true
74 }
75 else {
76 assert false, 'unexpected concurrent wait result: ${result}'
77 }
78 }
79 }
80 1 * time.second {
81 assert false, 'concurrent task waiters did not both finish'
82 }
83 }
84 }
85 first_waiter.wait()
86 second_waiter.wait()
87
88 assert saw_value
89 assert saw_second_wait_error
90}
91
92fn test_task_parent_cancellation_is_observed() {
93 parent_ctx, cancel := xasync.with_cancel()
94 mut task := xasync.run_with_context[int](parent_ctx, fn (mut ctx context.Context) !int {
95 done := ctx.done()
96 select {
97 _ := <-done {
98 return ctx.err()
99 }
100 1 * time.second {
101 return error('task did not observe parent cancellation')
102 }
103 }
104 return error('unreachable')
105 })!
106 cancel()
107 task.wait() or {
108 assert err.msg() == 'context canceled'
109 return
110 }
111 assert false
112}
113
114fn test_task_parent_already_canceled_is_observed() {
115 parent_ctx, cancel := xasync.with_cancel()
116 cancel()
117 mut task := xasync.run_with_context[int](parent_ctx, fn (mut ctx context.Context) !int {
118 done := ctx.done()
119 select {
120 _ := <-done {
121 return ctx.err()
122 }
123 1 * time.second {
124 return error('task did not observe already canceled parent')
125 }
126 }
127 return error('unreachable')
128 })!
129 task.wait() or {
130 assert err.msg() == 'context canceled'
131 return
132 }
133 assert false
134}
135
136fn test_task_observes_context_done() {
137 parent_ctx, cancel := xasync.with_cancel()
138 observed := chan bool{cap: 1}
139 mut task := xasync.run_with_context[int](parent_ctx, fn [observed] (mut ctx context.Context) !int {
140 done := ctx.done()
141 select {
142 _ := <-done {
143 observed <- true
144 return ctx.err()
145 }
146 1 * time.second {
147 observed <- false
148 return error('task did not observe ctx.done()')
149 }
150 }
151 return error('unreachable')
152 })!
153 cancel()
154 task.wait() or { assert err.msg() == 'context canceled' }
155 select {
156 did_observe := <-observed {
157 assert did_observe
158 }
159 1 * time.second {
160 assert false, 'task did not report cancellation observation'
161 }
162 }
163}
164
165fn test_task_ignores_cancellation_but_result_publication_is_buffered() {
166 parent_ctx, cancel := xasync.with_cancel()
167 mut task := xasync.run_with_context[int](parent_ctx, fn (mut ctx context.Context) !int {
168 _ = ctx
169 time.sleep(20 * time.millisecond)
170 return 9
171 })!
172 cancel()
173 time.sleep(60 * time.millisecond)
174 assert task.wait()! == 9
175}
176
177fn test_task_refuses_nil_function() {
178 nil_task_fn := unsafe { xasync.TaskFn[int](nil) }
179 xasync.run[int](nil_task_fn) or {
180 assert err.msg() == 'async: job function is nil'
181 return
182 }
183 assert false
184}
185