use std::time::Duration;
use calloop::EventLoop;
use calloop::channel::Event as ChannelEvent;
use tablero::networkmanager::network_from_nm;
use tablero::producer::{ProducerBridge, from_fn};
use tablero::render::Bounds;
use tablero::widget::{Dashboard, Msg, NetworkWidget};
type Reading = (u32, Option<&'static str>, Option<&'static str>);
struct Harness {
dashboard: Dashboard,
labels: Vec<String>,
redraws: usize,
}
impl Harness {
fn new() -> Self {
Self {
dashboard: Dashboard::new(vec![Box::new(NetworkWidget::new(Bounds::new(
0, 0, 320, 32,
)))]),
labels: Vec::new(),
redraws: 0,
}
}
}
fn pump_until(
event_loop: &mut EventLoop<Harness>,
harness: &mut Harness,
done: impl Fn(&Harness) -> bool,
) {
let mut waited = Duration::ZERO;
let step = Duration::from_millis(20);
while !done(harness) && waited < Duration::from_secs(5) {
event_loop
.dispatch(step, harness)
.expect("loop dispatch succeeds");
waited += step;
}
}
fn run_messages(messages: Vec<Msg>) -> (Vec<String>, usize) {
let mut harness = Harness::new();
let mut event_loop: EventLoop<Harness> = EventLoop::try_new().expect("event loop");
let handle = event_loop.handle();
let (bridge, channel) = ProducerBridge::new().expect("runtime starts");
let expected = messages.len();
handle
.insert_source(channel, |event, _, h: &mut Harness| {
if let ChannelEvent::Msg(msg) = event {
if h.dashboard.update(&msg) {
h.redraws += 1;
}
if let Msg::Network(snapshot) = &msg {
h.labels
.push(snapshot.as_ref().map(|n| n.label()).unwrap_or_default());
}
}
})
.expect("channel registers");
bridge.spawn(from_fn("fake-networkmanager", move |tx| async move {
for msg in messages {
tx.send(msg)?;
}
Ok(())
}));
pump_until(&mut event_loop, &mut harness, |h| {
h.labels.len() >= expected
});
drop(bridge);
(harness.labels, harness.redraws)
}
fn drive(readings: Vec<Reading>) -> (Vec<String>, usize) {
let messages = readings
.into_iter()
.map(|(state, conn_type, ssid)| Msg::Network(Some(network_from_nm(state, conn_type, ssid))))
.collect();
run_messages(messages)
}
#[test]
fn common_network_states_render_expected_labels() {
let (labels, redraws) = drive(vec![
(20, None, None),
(70, Some("802-3-ethernet"), None),
(70, Some("802-11-wireless"), Some("home-net")),
(0, None, None),
]);
assert_eq!(
labels,
vec![
"disconnected".to_string(),
"wired".to_string(),
"home-net".to_string(),
"unknown".to_string(),
]
);
assert_eq!(redraws, 4, "each distinct reading repaints once");
}
#[test]
fn wireless_without_ssid_renders_with_its_glyph_only() {
let (labels, redraws) = drive(vec![(70, Some("802-11-wireless"), None)]);
assert_eq!(labels, vec![String::new()]);
assert_eq!(redraws, 1);
}
#[test]
fn unchanged_connectivity_does_not_repaint() {
let (labels, redraws) = drive(vec![
(70, Some("802-11-wireless"), Some("home-net")),
(70, Some("802-11-wireless"), Some(" home-net ")),
]);
assert_eq!(labels, vec!["home-net".to_string(), "home-net".to_string()]);
assert_eq!(redraws, 1, "an identical snapshot must not repaint");
}
#[test]
fn networkmanager_unavailable_renders_blank() {
let (labels, redraws) = run_messages(vec![Msg::Network(None)]);
assert_eq!(
labels,
vec![String::new()],
"unavailable network shows nothing"
);
assert_eq!(
redraws, 0,
"an absent snapshot matches the empty initial state: no repaint"
);
}