use std::sync::atomic::Ordering;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tokio::time::Instant;
use super::test_lab::{LAB, Lab, RP, RPM, SUBSCRIBE_COV, ai, nc, object, settle};
use super::*;
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn the_first_notification_after_subscribing_is_kept() {
let lab = Lab::start(WatchOptions::default()).await;
let lease = lab.hub.watch(object(ai(1))).unwrap();
tokio::time::sleep(Duration::from_millis(300)).await;
let snapshot = lease.snapshot();
assert_eq!(snapshot.source, Some(Source::Cov), "{snapshot:?}");
assert!(snapshot.notifications >= 1, "{snapshot:?}");
drop(lease);
lab.stop().await;
}
#[tokio::test(start_paused = true)]
async fn shutdown_keeps_to_its_wait_with_a_poll_in_flight() {
let lab = Lab::start(WatchOptions::default()).await;
let lease = lab.hub.watch(object(nc(1))).unwrap();
settle().await;
tokio::time::sleep(Duration::from_millis(9_800)).await;
lab.quirks.muted.store(true, Ordering::Relaxed);
let from = Instant::now();
tokio::time::sleep(Duration::from_millis(500)).await;
assert_eq!(lab.sent(RPM, from), 1, "a poll is waiting for its answer");
let start = Instant::now();
assert!(lab.hub.shutdown(Duration::from_millis(200)).await);
let took = start.elapsed();
assert!(took <= Duration::from_millis(250), "shutdown took {took:?}");
assert_eq!(lab.hub.stats().cancelling, 0);
drop(lease);
lab.quirks.muted.store(false, Ordering::Relaxed);
lab.stop().await;
}
#[tokio::test(start_paused = true)]
async fn an_arrival_as_the_scheduler_ends_is_served() {
let lab = Lab::start(WatchOptions::default()).await;
let first = lab.hub.watch(object(nc(1))).unwrap();
settle().await;
let late: Arc<Mutex<Option<Lease>>> = Arc::new(Mutex::new(None));
let hub = lab.hub.clone();
let slot = Arc::clone(&late);
*lab.hub.inner.registry.before_end.lock().unwrap() = Some(Box::new(move || {
*slot.lock().unwrap() = Some(hub.watch(object(ai(1))).unwrap());
}));
drop(first);
settle().await;
let late = late.lock().unwrap().take().expect("the hook ran");
let snapshot = late.snapshot();
assert_eq!(snapshot.source, Some(Source::Cov), "{snapshot:?}");
assert_eq!(snapshot.state, State::Live);
assert_eq!(lab.table().await.len(), 1);
drop(late);
lab.stop().await;
}
#[tokio::test(start_paused = true)]
async fn a_row_too_large_alone_is_read_property_by_property() {
let lab = Lab::start(WatchOptions::default()).await;
let lease = lab.hub.watch(object(nc(1))).unwrap();
settle().await;
lab.quirks.abort_rpm.store(2, Ordering::Relaxed);
let from = Instant::now();
tokio::time::sleep(Duration::from_secs(11)).await;
assert_eq!(lab.sent(RPM, from), 1);
assert_eq!(lab.sent(RP, from), POINT_PROPERTIES.len());
let snapshot = lease.snapshot();
assert_eq!((snapshot.polls, snapshot.state), (1, State::Live));
lab.stop().await;
}
#[tokio::test(start_paused = true)]
async fn shutdown_waits_for_the_cancel_of_a_point_given_up() {
let options = WatchOptions {
missing: MissingDevice::Fail,
poll_floor: Duration::from_secs(1),
retry_cov: Backoff::fixed(Duration::from_secs(2)),
..WatchOptions::default()
};
let lab = Lab::start(options).await;
lab.quirks.swallow_cov.store(1, Ordering::Relaxed);
let lease = lab.hub.watch(object(ai(1))).unwrap();
tokio::time::sleep(Duration::from_millis(1_100)).await;
assert_eq!(lab.services(), [RPM, SUBSCRIBE_COV], "unanswered");
lab.engine.client().clear_devices().await;
let held = lab.engine.limits().turn(LAB).await;
tokio::time::sleep(Duration::from_secs(2)).await;
assert_eq!(lease.snapshot().state, State::Gone);
assert_eq!(lease.snapshot().why, Some(Why::Missing));
assert_eq!(lab.hub.stats().cancelling, 1);
let hub = lab.hub.clone();
let shutdown = tokio::spawn(async move { hub.shutdown(Duration::from_secs(5)).await });
tokio::time::sleep(Duration::from_millis(100)).await;
drop(held);
assert!(shutdown.await.unwrap());
assert_eq!(
lab.services(),
[RPM, SUBSCRIBE_COV, SUBSCRIBE_COV],
"the cancel went"
);
assert_eq!(lab.hub.stats().cancelling, 0);
drop(lease);
lab.stop().await;
}
#[tokio::test(start_paused = true)]
async fn a_renewal_behind_a_full_budget_request_lands_before_expiry() {
let budget = Duration::from_secs(24);
let lifetime = Duration::from_secs(120);
let margin = renewal_margin(120, budget);
let options = WatchOptions {
lifetime: Lifetime::new(120).unwrap(),
renewal_margin: margin,
..WatchOptions::default()
};
let lab = Lab::start(options).await;
let lease = lab.hub.watch(object(ai(1))).unwrap();
settle().await;
let start = lab.sent_at(SUBSCRIBE_COV)[0];
let due = start + lifetime - margin;
tokio::time::sleep_until(due - Duration::from_secs(1)).await;
let held = lab.engine.limits().turn(LAB).await;
tokio::time::sleep(budget).await;
drop(held);
tokio::time::sleep(Duration::from_secs(1)).await;
let renewed = *lab.sent_at(SUBSCRIBE_COV).get(1).expect("renewed");
assert!(
renewed + budget <= start + lifetime,
"renewal at {:?} leaves less than a budget before expiry",
renewed - start
);
assert_eq!(lease.snapshot().renewals, 1);
lab.stop().await;
}