use std::time::Duration;
use eye_declare::driver_tokio::{ActiveSubscriptions, SyncReport};
use eye_declare::{App, Ctx, Element, Fluent, Runtime, Subscriptions, text};
#[derive(Clone)]
enum Msg {
Tick,
}
struct Poller {
count: usize,
}
impl App for Poller {
type Msg = Msg;
type Output = ();
fn update(&mut self, _msg: Msg, _ctx: &mut Ctx<'_, Self>) {
self.count += 1;
}
fn tail(&self) -> impl Element + '_ {
text(format!("polled {}", self.count))
}
fn subscriptions(&self) -> Subscriptions<Msg> {
Subscriptions::new().when(self.count < 3, |s| {
s.every("poll", Duration::from_millis(5), || Msg::Tick)
})
}
}
#[tokio::test]
async fn every_fires_until_condition_drops() {
let mut rt = Runtime::new(Poller { count: 0 }, 20, 24);
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
let mut subs = ActiveSubscriptions::new(tx.clone());
subs.sync(rt.app().subscriptions());
while rt.app().count < 3 {
let msg = rx.recv().await.expect("subscription should tick");
let _ = rt.process(msg);
subs.sync(rt.app().subscriptions());
}
let extra = tokio::time::timeout(Duration::from_millis(50), rx.recv()).await;
assert!(extra.is_err(), "no ticks after the condition dropped");
assert_eq!(rt.app().count, 3);
}
#[tokio::test]
async fn sync_reports_lifecycle_and_interval_restarts() {
let (tx, _rx) = tokio::sync::mpsc::unbounded_channel::<Msg>();
let mut subs = ActiveSubscriptions::new(tx);
let declare =
|ms: u64| Subscriptions::new().every("poll", Duration::from_millis(ms), || Msg::Tick);
assert_eq!(
subs.sync(declare(50)),
SyncReport {
started: vec!["poll".into()],
stopped: vec![],
}
);
assert_eq!(subs.sync(declare(50)), SyncReport::default());
assert_eq!(
subs.sync(declare(10)),
SyncReport {
started: vec!["poll".into()],
stopped: vec!["poll".into()],
}
);
assert_eq!(
subs.sync(Subscriptions::new()),
SyncReport {
started: vec![],
stopped: vec!["poll".into()],
}
);
}
#[tokio::test]
async fn stream_subscription_cancels_on_removal() {
#[derive(Clone)]
struct Item(u8);
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<Item>();
let mut subs = ActiveSubscriptions::new(tx.clone());
let declared = Subscriptions::new().stream("events", || {
futures::stream::unfold(0u8, |n| async move {
match n {
0 => Some((Item(0), 1)),
1 => {
tokio::time::sleep(Duration::from_millis(100)).await;
Some((Item(1), 2))
}
_ => None,
}
})
});
subs.sync(declared);
let first = rx.recv().await.expect("first stream item");
assert_eq!(first.0, 0);
subs.sync(Subscriptions::new());
drop(tx);
drop(subs);
let next = tokio::time::timeout(Duration::from_secs(1), rx.recv())
.await
.expect("cancelled stream should end promptly");
assert!(next.is_none(), "no items after removal");
}