mendi 0.0.2

Rust client for the Mendi neurofeedback headband over BLE using btleplug
Documentation
use mendi::types::{BatteryReading, DeviceInfo, FrameReading, MendiEvent, ACCEL_SCALE, GYRO_SCALE};

#[test]
fn battery_percentage_at_min() {
    let b = BatteryReading {
        timestamp: 0.0,
        voltage_mv: 3600,
        charging: false,
        usb_connected: false,
    };
    assert_eq!(b.percentage(), 0);
}

#[test]
fn battery_percentage_at_max() {
    let b = BatteryReading {
        timestamp: 0.0,
        voltage_mv: 4100,
        charging: false,
        usb_connected: false,
    };
    assert_eq!(b.percentage(), 100);
}

#[test]
fn battery_percentage_below_min() {
    let b = BatteryReading {
        timestamp: 0.0,
        voltage_mv: 3000,
        charging: false,
        usb_connected: false,
    };
    assert_eq!(b.percentage(), 0);
}

#[test]
fn battery_percentage_above_max() {
    let b = BatteryReading {
        timestamp: 0.0,
        voltage_mv: 4500,
        charging: true,
        usb_connected: true,
    };
    assert_eq!(b.percentage(), 100);
}

#[test]
fn battery_percentage_midpoint() {
    let b = BatteryReading {
        timestamp: 0.0,
        voltage_mv: 3850, // midpoint of 3600–4100
        charging: false,
        usb_connected: false,
    };
    assert_eq!(b.percentage(), 50);
}

#[test]
fn battery_voltage_conversion() {
    let b = BatteryReading {
        timestamp: 0.0,
        voltage_mv: 3750,
        charging: false,
        usb_connected: false,
    };
    assert!((b.voltage() - 3.75).abs() < 0.001);
}

#[test]
fn frame_accel_scaling() {
    let f = FrameReading {
        timestamp: 0.0,
        acc_x: 16384, // half of 32768 → should be ~1.0G
        acc_y: -16384,
        acc_z: 0,
        ang_x: 0, ang_y: 0, ang_z: 0,
        temperature: 0.0,
        ir_left: 0, red_left: 0, amb_left: 0,
        ir_right: 0, red_right: 0, amb_right: 0,
        ir_pulse: 0, red_pulse: 0, amb_pulse: 0,
    };
    assert!((f.accel_x_g() - 1.0).abs() < 0.001);
    assert!((f.accel_y_g() - (-1.0)).abs() < 0.001);
    assert!((f.accel_z_g() - 0.0).abs() < 0.001);
}

#[test]
fn frame_gyro_scaling() {
    let f = FrameReading {
        timestamp: 0.0,
        acc_x: 0, acc_y: 0, acc_z: 0,
        ang_x: 32768, // full scale → 125°/s
        ang_y: -32768,
        ang_z: 0,
        temperature: 0.0,
        ir_left: 0, red_left: 0, amb_left: 0,
        ir_right: 0, red_right: 0, amb_right: 0,
        ir_pulse: 0, red_pulse: 0, amb_pulse: 0,
    };
    assert!((f.gyro_x_dps() - 125.0).abs() < 0.01);
    assert!((f.gyro_y_dps() - (-125.0)).abs() < 0.01);
    assert!((f.gyro_z_dps() - 0.0).abs() < 0.001);
}

#[test]
fn accel_scale_constant() {
    // ±2G / 32768 ≈ 6.1e-5
    assert!((ACCEL_SCALE - 6.103_515_6e-5).abs() < 1e-10);
}

#[test]
fn gyro_scale_constant() {
    // ±125°/s / 32768 ≈ 3.8e-3
    assert!((GYRO_SCALE - 0.003_814_697_3).abs() < 1e-10);
}

#[test]
fn device_info_display_full() {
    let d = DeviceInfo {
        name: "Mendi".into(),
        id: "AA:BB:CC:DD:EE:FF".into(),
        firmware_version: Some("1.2.3".into()),
        hardware_version: Some("4.5".into()),
        fcc_id: "RYYEYSHJN".into(),
    };
    let s = d.to_string();
    assert!(s.contains("Mendi"));
    assert!(s.contains("AA:BB:CC:DD:EE:FF"));
    assert!(s.contains("fw=1.2.3"));
    assert!(s.contains("hw=4.5"));
    assert!(s.contains("fcc=RYYEYSHJN"));
}

#[test]
fn device_info_display_minimal() {
    let d = DeviceInfo {
        name: "Mendi".into(),
        id: "some-uuid".into(),
        firmware_version: None,
        hardware_version: None,
        fcc_id: String::new(),
    };
    let s = d.to_string();
    assert_eq!(s, "Mendi [some-uuid]");
    assert!(!s.contains("fw="));
    assert!(!s.contains("hw="));
    assert!(!s.contains("fcc="));
}

#[test]
fn event_partial_eq_disconnected() {
    assert_eq!(MendiEvent::Disconnected, MendiEvent::Disconnected);
}

#[test]
fn event_partial_eq_connected() {
    let info = DeviceInfo {
        name: "Mendi".into(),
        id: "test".into(),
        ..Default::default()
    };
    assert_eq!(
        MendiEvent::Connected(info.clone()),
        MendiEvent::Connected(info),
    );
}

#[test]
fn event_partial_eq_different_variants() {
    let info = DeviceInfo::default();
    assert_ne!(MendiEvent::Connected(info), MendiEvent::Disconnected);
}

#[test]
fn battery_reading_partial_eq() {
    let a = BatteryReading {
        timestamp: 1000.0,
        voltage_mv: 3800,
        charging: true,
        usb_connected: false,
    };
    let b = a.clone();
    assert_eq!(a, b);
}