use crate::types::{NetworkDiagnostics, SpeedTestResult};
pub type EventSender = tokio::sync::mpsc::UnboundedSender<TestEvent>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Phase {
Locating,
BuildingServers,
SelectingServers,
Latency,
Download,
Upload,
Jitter,
}
impl Phase {
pub fn title(self) -> &'static str {
match self {
Phase::Locating => "Detecting Location",
Phase::BuildingServers => "Building Server Pool",
Phase::SelectingServers => "Selecting Best Servers",
Phase::Latency => "Testing Latency",
Phase::Download => "Testing Download Speed",
Phase::Upload => "Testing Upload Speed",
Phase::Jitter => "Measuring Jitter & Packet Loss",
}
}
}
#[derive(Debug, Clone)]
pub struct SelectedServer {
pub name: String,
pub location: String,
pub latency_ms: f64,
pub distance_km: f64,
}
#[derive(Debug, Clone)]
pub enum TestEvent {
Status(String),
LocationDetected {
city: String,
country: String,
isp: Option<String>,
source: String,
},
ServerPoolBuilt { count: usize },
NearbyServersFound { count: usize },
ServersSelected { servers: Vec<SelectedServer> },
PrimarySelected {
name: String,
location: String,
distance_km: f64,
},
PhaseStarted(Phase),
DownloadSample {
mbps: f64,
peak_mbps: f64,
elapsed_secs: f64,
},
UploadSample {
mbps: f64,
peak_mbps: f64,
elapsed_secs: f64,
},
DownloadComplete { mbps: f64 },
UploadComplete { mbps: f64 },
LatencyProgress { avg_ms: f64 },
LatencyComplete { avg_ms: f64 },
JitterComplete {
jitter_ms: f64,
packet_loss_percent: f64,
},
DiagnosticsComplete(Box<NetworkDiagnostics>),
Completed(Box<SpeedTestResult>),
}
#[inline]
pub fn emit(tx: &Option<EventSender>, event: TestEvent) {
if let Some(tx) = tx {
let _ = tx.send(event);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn phase_titles_are_present() {
assert_eq!(Phase::Download.title(), "Testing Download Speed");
assert_eq!(Phase::Upload.title(), "Testing Upload Speed");
assert_eq!(Phase::Latency.title(), "Testing Latency");
}
#[test]
fn emit_is_a_noop_without_a_sender() {
emit(&None, TestEvent::Status("hello".into()));
}
#[test]
fn emit_delivers_to_receiver() {
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
emit(&Some(tx), TestEvent::ServerPoolBuilt { count: 7 });
match rx.try_recv() {
Ok(TestEvent::ServerPoolBuilt { count }) => assert_eq!(count, 7),
other => panic!("unexpected event: {other:?}"),
}
}
}