#![cfg(feature = "simulate")]
use mendi::simulate::{MockDevice, SimConfig, SimulatedDevice};
use mendi::types::*;
#[tokio::test]
async fn mock_send_receive() {
let (mut rx, mock) = MockDevice::new(16);
mock.send(MendiEvent::Connected(DeviceInfo::default()));
mock.send(MendiEvent::Disconnected);
drop(mock);
let e1 = rx.recv().await.unwrap();
assert!(matches!(e1, MendiEvent::Connected(_)));
let e2 = rx.recv().await.unwrap();
assert_eq!(e2, MendiEvent::Disconnected);
assert!(rx.recv().await.is_none());
}
#[tokio::test]
async fn mock_connected_helper() {
let info = DeviceInfo {
name: "TestBand".into(),
id: "TEST-01".into(),
firmware_version: Some("1.0".into()),
..Default::default()
};
let (mut rx, mock) = MockDevice::connected(info.clone(), 16);
let ev = rx.recv().await.unwrap();
assert_eq!(ev, MendiEvent::Connected(info));
let ev = rx.recv().await.unwrap();
assert!(matches!(ev, MendiEvent::Diagnostics(_)));
if let MendiEvent::Diagnostics(d) = ev {
assert!(d.imu_ok);
assert!(d.sensor_ok);
assert!(d.adc.is_some());
}
drop(mock);
assert!(rx.recv().await.is_none());
}
#[tokio::test]
async fn mock_with_frames() {
let n = 10;
let (mut rx, _mock) = MockDevice::with_frames(n, 32);
let ev = rx.recv().await.unwrap();
assert!(matches!(ev, MendiEvent::Connected(_)));
let mut frames = vec![];
for _ in 0..n {
let ev = rx.recv().await.unwrap();
if let MendiEvent::Frame(f) = ev {
frames.push(f);
} else {
panic!("expected Frame, got {:?}", ev);
}
}
assert_eq!(frames.len(), n);
assert_eq!(frames[0].ir_left, 50_000);
assert_eq!(frames[1].ir_left, 50_010);
assert_eq!(frames[9].ir_left, 50_090);
assert!((frames[0].timestamp - 0.0).abs() < 0.001);
assert!((frames[1].timestamp - 40.0).abs() < 0.001);
assert_eq!(frames[0].acc_z, 16384);
assert!((frames[0].temperature - 36.5).abs() < 0.01);
let ev = rx.recv().await.unwrap();
assert_eq!(ev, MendiEvent::Disconnected);
}
#[tokio::test]
async fn mock_with_frames_zero() {
let (mut rx, _mock) = MockDevice::with_frames(0, 8);
let ev = rx.recv().await.unwrap();
assert!(matches!(ev, MendiEvent::Connected(_)));
let ev = rx.recv().await.unwrap();
assert_eq!(ev, MendiEvent::Disconnected);
}
#[tokio::test]
async fn mock_try_send_on_full_channel() {
let (mut rx, mock) = MockDevice::new(1);
mock.send(MendiEvent::Disconnected);
assert!(!mock.try_send(MendiEvent::Disconnected));
let _ = rx.recv().await;
}
#[tokio::test]
async fn mock_send_async() {
let (mut rx, mock) = MockDevice::new(4);
mock.send_async(MendiEvent::Disconnected).await;
let ev = rx.recv().await.unwrap();
assert_eq!(ev, MendiEvent::Disconnected);
}
#[tokio::test]
async fn sim_emits_connected_then_diagnostics() {
let config = SimConfig {
disconnect_after_frames: Some(1),
..Default::default()
};
let (mut rx, _handle) = SimulatedDevice::start(config);
let ev = rx.recv().await.unwrap();
if let MendiEvent::Connected(info) = ev {
assert_eq!(info.name, "Mendi-SIM");
assert_eq!(info.firmware_version, Some("SIM-1.0.0".into()));
} else {
panic!("expected Connected, got {:?}", ev);
}
let ev = rx.recv().await.unwrap();
assert!(matches!(ev, MendiEvent::Diagnostics(_)));
}
#[tokio::test]
async fn sim_disconnect_after_frames() {
let config = SimConfig {
disconnect_after_frames: Some(5),
frame_rate_hz: 1000.0, ..Default::default()
};
let (mut rx, handle) = SimulatedDevice::start(config);
let mut events = vec![];
while let Some(ev) = rx.recv().await {
events.push(ev);
}
assert!(events.len() >= 7, "got {} events", events.len());
assert!(matches!(events[0], MendiEvent::Connected(_)));
assert!(matches!(events[1], MendiEvent::Diagnostics(_)));
let frame_count = events.iter().filter(|e| matches!(e, MendiEvent::Frame(_))).count();
assert_eq!(frame_count, 5);
assert_eq!(*events.last().unwrap(), MendiEvent::Disconnected);
assert!(!handle.is_running());
}
#[tokio::test]
async fn sim_handle_disconnect() {
let config = SimConfig {
disconnect_after_frames: None, frame_rate_hz: 100.0,
..Default::default()
};
let (mut rx, handle) = SimulatedDevice::start(config);
let _ = rx.recv().await; let _ = rx.recv().await; let _ = rx.recv().await;
handle.disconnect();
handle.join().await;
let mut found_disconnect = false;
while let Some(ev) = rx.recv().await {
if ev == MendiEvent::Disconnected {
found_disconnect = true;
break;
}
}
assert!(found_disconnect);
}
#[tokio::test]
async fn sim_frame_data_is_realistic() {
let config = SimConfig {
disconnect_after_frames: Some(10),
frame_rate_hz: 1000.0,
base_ir: 50_000,
base_red: 40_000,
base_ambient: 1_000,
temperature: 37.0,
..Default::default()
};
let (mut rx, _handle) = SimulatedDevice::start(config);
let _ = rx.recv().await;
let _ = rx.recv().await;
let ev = rx.recv().await.unwrap();
if let MendiEvent::Frame(f) = ev {
assert!(f.ir_left > 40_000 && f.ir_left < 60_000, "ir_left={}", f.ir_left);
assert!(f.red_left > 30_000 && f.red_left < 50_000, "red_left={}", f.red_left);
assert!(f.amb_left > 500 && f.amb_left < 1_500, "amb_left={}", f.amb_left);
assert!((f.temperature - 37.0).abs() < 0.01);
assert!(f.acc_z > 16000 && f.acc_z < 16800, "acc_z={}", f.acc_z);
assert!(f.timestamp > 0.0);
} else {
panic!("expected Frame, got {:?}", ev);
}
}
#[tokio::test]
async fn sim_battery_events_periodic() {
let config = SimConfig {
disconnect_after_frames: Some(150),
frame_rate_hz: 10_000.0, battery_every_n_frames: 50,
battery_voltage_mv: 4000,
charging: true,
usb_connected: true,
..Default::default()
};
let (mut rx, _handle) = SimulatedDevice::start(config);
let mut battery_count = 0;
while let Some(ev) = rx.recv().await {
if let MendiEvent::Battery(b) = ev {
battery_count += 1;
assert_eq!(b.voltage_mv, 4000);
assert!(b.charging);
assert!(b.usb_connected);
assert_eq!(b.percentage(), 80);
}
}
assert_eq!(battery_count, 3);
}
#[tokio::test]
async fn sim_calibration_events_periodic() {
let config = SimConfig {
disconnect_after_frames: Some(200),
frame_rate_hz: 10_000.0,
calibration_every_n_frames: 100,
..Default::default()
};
let (mut rx, _handle) = SimulatedDevice::start(config);
let mut cal_count = 0;
while let Some(ev) = rx.recv().await {
if let MendiEvent::Calibration(c) = ev {
cal_count += 1;
assert!(c.auto_calibration);
assert!(!c.low_power_mode);
}
}
assert_eq!(cal_count, 2);
}
#[tokio::test]
async fn sim_custom_device_name() {
let config = SimConfig {
disconnect_after_frames: Some(0),
device_name: "MyBand-42".into(),
firmware_version: "2.5.0".into(),
..Default::default()
};
let (mut rx, _handle) = SimulatedDevice::start(config);
let ev = rx.recv().await.unwrap();
if let MendiEvent::Connected(info) = ev {
assert_eq!(info.name, "MyBand-42");
assert_eq!(info.firmware_version, Some("2.5.0".into()));
}
}