#![allow(non_snake_case)]
use goish::prelude::*;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
test!{ fn TestBackground(t) {
let ctx = context::Background();
if ctx.Err() != nil {
t.Errorf(Sprintf!("Background.Err() = %s, want nil", ctx.Err()));
}
}}
test!{ fn TestWithCancel(t) {
let (ctx, cancel) = context::WithCancel(context::Background());
if ctx.Err() != nil {
t.Errorf(Sprintf!("fresh WithCancel.Err() = %s", ctx.Err()));
}
cancel.call();
for _ in 0..100 {
if ctx.Err() != nil { break; }
std::thread::sleep(Duration::from_millis(1));
}
let es = format!("{}", ctx.Err());
if !strings::Contains(&es, "canceled") && !strings::Contains(&es, "Canceled") {
t.Errorf(Sprintf!("WithCancel.Err() = %s, want 'canceled'", es));
}
}}
test!{ fn TestWithCancelDone(t) {
let (ctx, cancel) = context::WithCancel(context::Background());
let done = ctx.Done();
let flag = Arc::new(AtomicBool::new(false));
let f = flag.clone();
std::thread::spawn(move || {
let _ = done.Recv();
f.store(true, Ordering::SeqCst);
});
std::thread::sleep(Duration::from_millis(10));
cancel.call();
std::thread::sleep(Duration::from_millis(50));
if !flag.load(Ordering::SeqCst) {
t.Errorf(Sprintf!("Done channel did not fire after cancel"));
}
}}
test!{ fn TestWithTimeout(t) {
let start = time::Now();
let (ctx, _cancel) = context::WithTimeout(context::Background(), time::Millisecond * 50i64);
ctx.Wait();
let elapsed = time::Since(start);
if elapsed.Milliseconds() < 40 {
t.Errorf(Sprintf!("WithTimeout fired too early: %d ms", elapsed.Milliseconds()));
}
if elapsed.Milliseconds() > 1000 {
t.Errorf(Sprintf!("WithTimeout fired too late: %d ms", elapsed.Milliseconds()));
}
if ctx.Err() == nil {
t.Errorf(Sprintf!("WithTimeout Err() = nil after deadline"));
}
}}
test!{ fn TestWithDeadline(t) {
let deadline = time::Now().Add(time::Millisecond * 40i64);
let (ctx, _cancel) = context::WithDeadline(context::Background(), deadline);
ctx.Wait();
if ctx.Err() == nil {
t.Errorf(Sprintf!("WithDeadline Err() = nil after deadline"));
}
}}
test!{ fn TestWithValue(t) {
let ctx = context::Background();
let ctx = context::WithValue(ctx, "user", "alice".to_string());
let got: Option<String> = ctx.Value("user");
if got.as_deref() != Some("alice") {
t.Errorf(Sprintf!("Value(user) = %v", got.is_some()));
}
let missing: Option<String> = ctx.Value("missing");
if missing.is_some() {
t.Errorf(Sprintf!("missing key got Some"));
}
}}
test!{ fn TestWithValueChained(t) {
let ctx = context::Background();
let ctx = context::WithValue(ctx, "a", 1i64);
let ctx = context::WithValue(ctx, "b", 2i64);
let a: Option<i64> = ctx.Value("a");
let b: Option<i64> = ctx.Value("b");
if a != Some(1) || b != Some(2) {
t.Errorf(Sprintf!("Chained values: a=%v b=%v", a.is_some(), b.is_some()));
}
}}
test!{ fn TestCancelPropagatesToChild(t) {
let (parent, pcancel) = context::WithCancel(context::Background());
let (child, _ccancel) = context::WithCancel(parent);
pcancel.call();
for _ in 0..100 {
if child.Err() != nil { break; }
std::thread::sleep(Duration::from_millis(1));
}
if child.Err() == nil {
t.Errorf(Sprintf!("child did not propagate parent cancellation"));
}
}}
test!{ fn TestMultipleCancelCalls(t) {
let (ctx, cancel) = context::WithCancel(context::Background());
cancel.call();
cancel.call();
let _ = ctx;
}}
test!{ fn TestBackgroundNeverDone(t) {
let ctx = context::Background();
if ctx.Err() != nil { t.Errorf(Sprintf!("Background Err non-nil")); }
std::thread::sleep(Duration::from_millis(20));
if ctx.Err() != nil { t.Errorf(Sprintf!("Background Err changed")); }
}}