Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,422 @@
|
||||
import context
|
||||
import time
|
||||
import x.async as xasync
|
||||
|
||||
fn test_every_runs_at_least_one_iteration() {
|
||||
parent_ctx, cancel := xasync.with_cancel()
|
||||
ran := chan bool{cap: 1}
|
||||
result := chan string{cap: 1}
|
||||
worker := spawn fn [parent_ctx, ran, result] () {
|
||||
xasync.every(parent_ctx, 5 * time.millisecond, fn [ran] (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
ran <- true
|
||||
}) or {
|
||||
result <- err.msg()
|
||||
return
|
||||
}
|
||||
result <- 'ok'
|
||||
}()
|
||||
|
||||
select {
|
||||
did_run := <-ran {
|
||||
assert did_run
|
||||
}
|
||||
1 * time.second {
|
||||
assert false, 'periodic job did not run'
|
||||
}
|
||||
}
|
||||
cancel()
|
||||
select {
|
||||
msg := <-result {
|
||||
assert msg == 'context canceled'
|
||||
}
|
||||
1 * time.second {
|
||||
assert false, 'every did not stop after cancellation'
|
||||
}
|
||||
}
|
||||
worker.wait()
|
||||
}
|
||||
|
||||
fn test_every_stops_on_cancellation() {
|
||||
parent_ctx, cancel := xasync.with_cancel()
|
||||
entered := chan bool{cap: 1}
|
||||
result := chan string{cap: 1}
|
||||
worker := spawn fn [parent_ctx, entered, result] () {
|
||||
entered <- true
|
||||
xasync.every(parent_ctx, 1 * time.second, fn (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
}) or {
|
||||
result <- err.msg()
|
||||
return
|
||||
}
|
||||
result <- 'ok'
|
||||
}()
|
||||
|
||||
select {
|
||||
did_enter := <-entered {
|
||||
assert did_enter
|
||||
}
|
||||
1 * time.second {
|
||||
assert false, 'every worker did not start'
|
||||
}
|
||||
}
|
||||
cancel()
|
||||
select {
|
||||
msg := <-result {
|
||||
assert msg == 'context canceled'
|
||||
}
|
||||
1 * time.second {
|
||||
assert false, 'every did not stop on context cancellation'
|
||||
}
|
||||
}
|
||||
worker.wait()
|
||||
}
|
||||
|
||||
fn test_every_returns_immediately_when_parent_is_already_canceled() {
|
||||
parent_ctx, cancel := xasync.with_cancel()
|
||||
cancel()
|
||||
xasync.every(parent_ctx, 1 * time.second, fn (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
}) or {
|
||||
assert err.msg() == 'context canceled'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_every_returns_iteration_error() {
|
||||
parent_ctx, cancel := xasync.with_cancel()
|
||||
result := chan string{cap: 1}
|
||||
worker := spawn fn [parent_ctx, result] () {
|
||||
xasync.every(parent_ctx, 5 * time.millisecond, fn (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
return error('periodic failed')
|
||||
}) or {
|
||||
result <- err.msg()
|
||||
return
|
||||
}
|
||||
result <- 'ok'
|
||||
}()
|
||||
|
||||
select {
|
||||
msg := <-result {
|
||||
assert msg == 'periodic failed'
|
||||
}
|
||||
1 * time.second {
|
||||
assert false, 'every did not return the periodic job error'
|
||||
}
|
||||
}
|
||||
cancel()
|
||||
worker.wait()
|
||||
}
|
||||
|
||||
fn test_every_rejects_zero_interval() {
|
||||
xasync.every(context.background(), 0 * time.millisecond, fn (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
}) or {
|
||||
assert err.msg() == 'async: interval must be positive'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_every_rejects_negative_interval() {
|
||||
xasync.every(context.background(), -1 * time.millisecond, fn (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
}) or {
|
||||
assert err.msg() == 'async: interval must be positive'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_every_rejects_nil_job() {
|
||||
nil_job := unsafe { xasync.JobFn(nil) }
|
||||
xasync.every(context.background(), 1 * time.second, nil_job) or {
|
||||
assert err.msg() == 'async: job function is nil'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_every_does_not_overlap_iterations() {
|
||||
parent_ctx, cancel := xasync.with_cancel()
|
||||
active := chan bool{cap: 1}
|
||||
active <- true
|
||||
entered := chan bool{cap: 2}
|
||||
release := chan bool{cap: 2}
|
||||
overlap := chan bool{cap: 1}
|
||||
result := chan string{cap: 1}
|
||||
worker := spawn fn [parent_ctx, active, entered, release, overlap, result] () {
|
||||
xasync.every(parent_ctx, 5 * time.millisecond, fn [active, entered, release, overlap] (mut ctx context.Context) ! {
|
||||
select {
|
||||
_ := <-active {}
|
||||
else {
|
||||
overlap <- true
|
||||
return error('periodic overlap')
|
||||
}
|
||||
}
|
||||
entered <- true
|
||||
done := ctx.done()
|
||||
select {
|
||||
_ := <-release {}
|
||||
_ := <-done {
|
||||
active <- true
|
||||
return ctx.err()
|
||||
}
|
||||
}
|
||||
active <- true
|
||||
}) or {
|
||||
result <- err.msg()
|
||||
return
|
||||
}
|
||||
result <- 'ok'
|
||||
}()
|
||||
|
||||
wait_for_periodic_entry(entered)
|
||||
select {
|
||||
_ := <-entered {
|
||||
assert false, 'periodic iterations overlapped while first job was still running'
|
||||
}
|
||||
50 * time.millisecond {}
|
||||
}
|
||||
select {
|
||||
did_overlap := <-overlap {
|
||||
assert !did_overlap
|
||||
}
|
||||
else {}
|
||||
}
|
||||
|
||||
release <- true
|
||||
wait_for_periodic_entry(entered)
|
||||
cancel()
|
||||
release <- true
|
||||
select {
|
||||
msg := <-result {
|
||||
assert msg == 'context canceled'
|
||||
}
|
||||
1 * time.second {
|
||||
assert false, 'every did not stop after non-overlap test cancellation'
|
||||
}
|
||||
}
|
||||
worker.wait()
|
||||
}
|
||||
|
||||
fn test_start_every_rejects_invalid_interval() {
|
||||
xasync.start_every(context.background(), 0 * time.millisecond, fn (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
}) or {
|
||||
assert err.msg() == 'async: interval must be positive'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_start_every_rejects_negative_interval() {
|
||||
xasync.start_every(context.background(), -1 * time.millisecond, fn (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
}) or {
|
||||
assert err.msg() == 'async: interval must be positive'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_start_every_rejects_nil_job() {
|
||||
nil_job := unsafe { xasync.JobFn(nil) }
|
||||
xasync.start_every(context.background(), 1 * time.second, nil_job) or {
|
||||
assert err.msg() == 'async: job function is nil'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_start_every_returns_immediately_when_parent_is_already_canceled() {
|
||||
parent_ctx, cancel := xasync.with_cancel()
|
||||
cancel()
|
||||
xasync.start_every(parent_ctx, 1 * time.second, fn (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
}) or {
|
||||
assert err.msg() == 'context canceled'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_periodic_handle_stop_before_first_iteration() {
|
||||
ran := chan bool{cap: 1}
|
||||
mut handle := xasync.start_every(context.background(), 1 * time.second, fn [ran] (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
ran <- true
|
||||
})!
|
||||
handle.stop()
|
||||
handle.wait()!
|
||||
assert_no_periodic_signal(ran, 'periodic handle ran before first interval after stop')
|
||||
}
|
||||
|
||||
fn test_periodic_handle_stop_between_ticks() {
|
||||
ticks := chan bool{cap: 2}
|
||||
mut handle := xasync.start_every(context.background(), 5 * time.millisecond, fn [ticks] (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
ticks <- true
|
||||
})!
|
||||
wait_for_periodic_entry(ticks)
|
||||
handle.stop()
|
||||
handle.wait()!
|
||||
assert_no_periodic_signal(ticks, 'periodic handle ticked again after stop and wait')
|
||||
}
|
||||
|
||||
fn test_periodic_handle_stop_during_long_job_waits_for_job_to_return() {
|
||||
entered := chan bool{cap: 1}
|
||||
release := chan bool{cap: 1}
|
||||
waited := chan bool{cap: 1}
|
||||
mut handle := xasync.start_every(context.background(), 5 * time.millisecond, fn [entered, release] (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
entered <- true
|
||||
_ := <-release
|
||||
})!
|
||||
wait_for_periodic_entry(entered)
|
||||
handle.stop()
|
||||
|
||||
wait_thread := spawn fn [mut handle, waited] () {
|
||||
handle.wait() or {
|
||||
waited <- false
|
||||
return
|
||||
}
|
||||
waited <- true
|
||||
}()
|
||||
assert_no_periodic_signal(waited, 'periodic handle wait returned before long job completed')
|
||||
release <- true
|
||||
wait_for_periodic_entry(waited)
|
||||
wait_thread.wait()
|
||||
}
|
||||
|
||||
fn test_periodic_handle_wait_returns_job_error() {
|
||||
mut handle := xasync.start_every(context.background(), 5 * time.millisecond, fn (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
return error('periodic handle failed')
|
||||
})!
|
||||
handle.wait() or {
|
||||
assert err.msg() == 'periodic handle failed'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_periodic_handle_wait_returns_job_context_canceled_error_after_stop() {
|
||||
entered := chan bool{cap: 1}
|
||||
release := chan bool{cap: 1}
|
||||
mut handle := xasync.start_every(context.background(), 5 * time.millisecond, fn [entered, release] (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
entered <- true
|
||||
_ := <-release
|
||||
return error('context canceled')
|
||||
})!
|
||||
wait_for_periodic_entry(entered)
|
||||
handle.stop()
|
||||
release <- true
|
||||
handle.wait() or {
|
||||
assert err.msg() == 'context canceled'
|
||||
return
|
||||
}
|
||||
assert false, 'job-returned context canceled error was treated as normal stop'
|
||||
}
|
||||
|
||||
fn test_periodic_handle_wait_returns_parent_cancellation() {
|
||||
parent_ctx, cancel := xasync.with_cancel()
|
||||
mut handle := xasync.start_every(parent_ctx, 1 * time.second, fn (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
})!
|
||||
cancel()
|
||||
handle.wait() or {
|
||||
assert err.msg() == 'context canceled'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_periodic_handle_stop_after_parent_cancel_keeps_parent_error() {
|
||||
parent_ctx, cancel := xasync.with_cancel()
|
||||
mut handle := xasync.start_every(parent_ctx, 1 * time.second, fn (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
})!
|
||||
cancel()
|
||||
handle.stop()
|
||||
handle.wait() or {
|
||||
assert err.msg() == 'context canceled'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn test_periodic_handle_does_not_overlap_iterations() {
|
||||
active := chan bool{cap: 1}
|
||||
active <- true
|
||||
entered := chan bool{cap: 2}
|
||||
release := chan bool{cap: 2}
|
||||
overlap := chan bool{cap: 1}
|
||||
mut handle := xasync.start_every(context.background(), 5 * time.millisecond, fn [active, entered, release, overlap] (mut ctx context.Context) ! {
|
||||
select {
|
||||
_ := <-active {}
|
||||
else {
|
||||
overlap <- true
|
||||
return error('periodic handle overlap')
|
||||
}
|
||||
}
|
||||
entered <- true
|
||||
_ = ctx
|
||||
_ := <-release
|
||||
active <- true
|
||||
})!
|
||||
|
||||
wait_for_periodic_entry(entered)
|
||||
assert_no_periodic_signal(entered,
|
||||
'periodic handle iterations overlapped while first job was still running')
|
||||
select {
|
||||
did_overlap := <-overlap {
|
||||
assert !did_overlap
|
||||
}
|
||||
else {}
|
||||
}
|
||||
|
||||
release <- true
|
||||
wait_for_periodic_entry(entered)
|
||||
handle.stop()
|
||||
release <- true
|
||||
handle.wait()!
|
||||
assert_no_periodic_signal(entered, 'periodic handle started another iteration after stop')
|
||||
}
|
||||
|
||||
fn test_periodic_handle_double_stop_and_wait() {
|
||||
mut handle := xasync.start_every(context.background(), 1 * time.second, fn (mut ctx context.Context) ! {
|
||||
_ = ctx
|
||||
})!
|
||||
handle.stop()
|
||||
handle.stop()
|
||||
handle.wait()!
|
||||
handle.wait() or {
|
||||
assert err.msg() == 'async: periodic wait was already called'
|
||||
return
|
||||
}
|
||||
assert false
|
||||
}
|
||||
|
||||
fn wait_for_periodic_entry(entered chan bool) {
|
||||
select {
|
||||
did_enter := <-entered {
|
||||
assert did_enter
|
||||
}
|
||||
1 * time.second {
|
||||
assert false, 'periodic job did not enter'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_no_periodic_signal(signal chan bool, message string) {
|
||||
select {
|
||||
_ := <-signal {
|
||||
assert false, message
|
||||
}
|
||||
50 * time.millisecond {}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user