Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,184 @@
|
||||
import context
|
||||
import time
|
||||
import x.async as xasync
|
||||
|
||||
fn test_task_returns_value() {
|
||||
mut task := xasync.run[int](fn (mut ctx context.Context) !int {
|
||||
_ = ctx
|
||||
return 42
|
||||
})!
|
||||
assert task.wait()! == 42
|
||||
}
|
||||
|
||||
fn test_task_returns_error() {
|
||||
mut task := xasync.run[int](fn (mut ctx context.Context) !int {
|
||||
_ = ctx
|
||||
return error('task failed')
|
||||
})!
|
||||
task.wait() or {
|
||||
assert err.msg() == 'task failed'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_task_wait_is_one_shot() {
|
||||
mut task := xasync.run[string](fn (mut ctx context.Context) !string {
|
||||
_ = ctx
|
||||
return 'ok'
|
||||
})!
|
||||
assert task.wait()! == 'ok'
|
||||
task.wait() or {
|
||||
assert err.msg() == 'async: task wait was already called'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_task_concurrent_wait_is_one_shot_without_deadlock() {
|
||||
release := chan bool{cap: 1}
|
||||
results := chan string{cap: 2}
|
||||
mut task := xasync.run[int](fn [release] (mut ctx context.Context) !int {
|
||||
_ = ctx
|
||||
_ := <-release
|
||||
return 7
|
||||
})!
|
||||
|
||||
first_waiter := spawn fn [mut task, results] () {
|
||||
value := task.wait() or {
|
||||
results <- 'error:${err.msg()}'
|
||||
return
|
||||
}
|
||||
results <- 'value:${value}'
|
||||
}()
|
||||
second_waiter := spawn fn [mut task, results] () {
|
||||
value := task.wait() or {
|
||||
results <- 'error:${err.msg()}'
|
||||
return
|
||||
}
|
||||
results <- 'value:${value}'
|
||||
}()
|
||||
release <- true
|
||||
|
||||
mut saw_value := false
|
||||
mut saw_second_wait_error := false
|
||||
for _ in 0 .. 2 {
|
||||
select {
|
||||
result := <-results {
|
||||
match result {
|
||||
'value:7' {
|
||||
saw_value = true
|
||||
}
|
||||
'error:async: task wait was already called' {
|
||||
saw_second_wait_error = true
|
||||
}
|
||||
else {
|
||||
assert false, 'unexpected concurrent wait result: ${result}'
|
||||
}
|
||||
}
|
||||
}
|
||||
1 * time.second {
|
||||
assert false, 'concurrent task waiters did not both finish'
|
||||
}
|
||||
}
|
||||
}
|
||||
first_waiter.wait()
|
||||
second_waiter.wait()
|
||||
|
||||
assert saw_value
|
||||
assert saw_second_wait_error
|
||||
}
|
||||
|
||||
fn test_task_parent_cancellation_is_observed() {
|
||||
parent_ctx, cancel := xasync.with_cancel()
|
||||
mut task := xasync.run_with_context[int](parent_ctx, fn (mut ctx context.Context) !int {
|
||||
done := ctx.done()
|
||||
select {
|
||||
_ := <-done {
|
||||
return ctx.err()
|
||||
}
|
||||
1 * time.second {
|
||||
return error('task did not observe parent cancellation')
|
||||
}
|
||||
}
|
||||
return error('unreachable')
|
||||
})!
|
||||
cancel()
|
||||
task.wait() or {
|
||||
assert err.msg() == 'context canceled'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_task_parent_already_canceled_is_observed() {
|
||||
parent_ctx, cancel := xasync.with_cancel()
|
||||
cancel()
|
||||
mut task := xasync.run_with_context[int](parent_ctx, fn (mut ctx context.Context) !int {
|
||||
done := ctx.done()
|
||||
select {
|
||||
_ := <-done {
|
||||
return ctx.err()
|
||||
}
|
||||
1 * time.second {
|
||||
return error('task did not observe already canceled parent')
|
||||
}
|
||||
}
|
||||
return error('unreachable')
|
||||
})!
|
||||
task.wait() or {
|
||||
assert err.msg() == 'context canceled'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_task_observes_context_done() {
|
||||
parent_ctx, cancel := xasync.with_cancel()
|
||||
observed := chan bool{cap: 1}
|
||||
mut task := xasync.run_with_context[int](parent_ctx, fn [observed] (mut ctx context.Context) !int {
|
||||
done := ctx.done()
|
||||
select {
|
||||
_ := <-done {
|
||||
observed <- true
|
||||
return ctx.err()
|
||||
}
|
||||
1 * time.second {
|
||||
observed <- false
|
||||
return error('task did not observe ctx.done()')
|
||||
}
|
||||
}
|
||||
return error('unreachable')
|
||||
})!
|
||||
cancel()
|
||||
task.wait() or { assert err.msg() == 'context canceled' }
|
||||
select {
|
||||
did_observe := <-observed {
|
||||
assert did_observe
|
||||
}
|
||||
1 * time.second {
|
||||
assert false, 'task did not report cancellation observation'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn test_task_ignores_cancellation_but_result_publication_is_buffered() {
|
||||
parent_ctx, cancel := xasync.with_cancel()
|
||||
mut task := xasync.run_with_context[int](parent_ctx, fn (mut ctx context.Context) !int {
|
||||
_ = ctx
|
||||
time.sleep(20 * time.millisecond)
|
||||
return 9
|
||||
})!
|
||||
cancel()
|
||||
time.sleep(60 * time.millisecond)
|
||||
assert task.wait()! == 9
|
||||
}
|
||||
|
||||
fn test_task_refuses_nil_function() {
|
||||
nil_task_fn := unsafe { xasync.TaskFn[int](nil) }
|
||||
xasync.run[int](nil_task_fn) or {
|
||||
assert err.msg() == 'async: job function is nil'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
Reference in new issue
Block a user