midi-harbor/crates/platform/tests/bluetooth.rs
2026-09-28 13:59:10 -05:00

89 lines
3.2 KiB
Rust

//! The native Bluetooth backend against a real radio.
//!
//! Ignored by default because a build machine has no Bluetooth adapter. The rules the seam
//! promises are proved through the daemon, against the in-memory backend, in
//! `crates/daemon/tests/bluetooth.rs`; these check the native backend's answers on the air.
#![allow(clippy::expect_used)]
use midi_harbor_platform::bluetooth::{BluetoothPlatform, BluetoothRole};
/// Locks that the native backend reports the central role usable on a machine with a radio, and
/// scans; what it hears is printed for a person to judge.
///
/// Ignored by default because a build machine has no Bluetooth, and a test that fails for want of
/// hardware teaches nothing. Run it where there is a radio with:
///
/// ```text
/// cargo test -p midi-harbor-platform --test bluetooth -- --ignored --nocapture
/// ```
#[test]
#[ignore = "needs a Bluetooth adapter"]
fn the_native_backend_reports_what_the_radio_can_actually_do() {
use midi_harbor_platform::bluetooth::native::NativeBluetooth;
use std::time::Duration;
let adapter = NativeBluetooth::start().expect("the backend starts even with no radio");
// Opening the adapter is asynchronous, so the first answer may precede it.
std::thread::sleep(Duration::from_secs(2));
let central = adapter.unavailable(BluetoothRole::Central);
println!("central: {central:?}");
println!(
"peripheral: {:?}",
adapter.unavailable(BluetoothRole::Peripheral)
);
assert_eq!(
central, None,
"the central role must be usable on a machine with a radio"
);
adapter.start_scan().expect("a scan starts");
std::thread::sleep(Duration::from_secs(10));
adapter.stop_scan().expect("a scan stops");
for found in adapter.in_range() {
println!(
"found {} name={:?} rssi={:?}",
found.id, found.name, found.rssi
);
}
for event in adapter.drain_events() {
println!("event {event:?}");
}
}
/// Locks that the native backend reports the peripheral role usable and starts advertising; what
/// reaches the air is checked by a person.
///
/// Ignored for the same reason as the test above. Watch what goes out with `btmon` while it runs:
/// the advertising data must carry the BLE MIDI service UUID, or no central will ever find us.
#[test]
#[ignore = "needs a Bluetooth adapter"]
fn the_advertised_port_reaches_the_air() {
use midi_harbor_platform::bluetooth::native::NativeBluetooth;
use std::time::Duration;
let adapter = NativeBluetooth::start().expect("the backend starts even with no radio");
std::thread::sleep(Duration::from_secs(2));
let peripheral = adapter.unavailable(BluetoothRole::Peripheral);
println!("peripheral: {peripheral:?}");
assert_eq!(
peripheral, None,
"the peripheral role must be usable for this build to advertise"
);
adapter
.advertise("Midi Harbor Test", None)
.expect("advertising starts");
std::thread::sleep(Duration::from_secs(12));
for event in adapter.drain_events() {
println!("event {event:?}");
}
adapter.stop_advertising().expect("advertising stops");
std::thread::sleep(Duration::from_secs(2));
}