use std::ffi::{c_char, CStr, CString};
use std::ptr;
use mx_remote_ffi::*;
fn uid_n(n: u8) -> mxr_uid_t {
let mut bytes = [0u8; 16];
bytes[0] = n;
bytes[15] = 0xa5;
mxr_uid_t { bytes }
}
fn client(name: &CStr, uid: &CStr) -> *mut mxr_remote_t {
let config = mxr_config_t {
target_ip: ptr::null(),
port: 0,
broadcast: false,
local_ip: ptr::null(),
interface: ptr::null(),
name: name.as_ptr(),
uid: uid.as_ptr(),
uid_path: ptr::null(),
};
let handle = unsafe { mxr_remote_new(&config, ptr::null(), ptr::null_mut()) };
assert!(
!handle.is_null(),
"the client was not created: {}",
last_error()
);
handle
}
fn last_error() -> String {
unsafe { CStr::from_ptr(mxr_last_error()) }
.to_string_lossy()
.into_owned()
}
fn field(bytes: &[c_char]) -> String {
let text: Vec<u8> = bytes
.iter()
.take_while(|b| **b != 0)
.map(|b| *b as u8)
.collect();
String::from_utf8_lossy(&text).into_owned()
}
#[test]
fn an_identifier_survives_the_trip_out_to_text_and_back() {
let uid = uid_n(7);
let mut text = [0 as c_char; MXR_UID_STRING_LEN];
let rc = unsafe { mxr_uid_to_string(uid, text.as_mut_ptr(), text.len()) };
assert_eq!(rc, mxr_result_t::MXR_OK);
let mut back = mxr_uid_t::default();
let rc = unsafe { mxr_uid_from_string(text.as_ptr(), &mut back) };
assert_eq!(rc, mxr_result_t::MXR_OK);
assert_eq!(
back,
uid,
"{} did not read back as it was written",
field(&text)
);
}
#[test]
fn a_buffer_too_short_for_an_identifier_is_refused_rather_than_filled() {
let mut text = [0 as c_char; MXR_UID_STRING_LEN - 1];
let rc = unsafe { mxr_uid_to_string(uid_n(1), text.as_mut_ptr(), text.len()) };
assert_eq!(rc, mxr_result_t::MXR_ERR_INVALID_ARGUMENT);
assert_eq!(text[0], 0, "a refused call still wrote to the buffer");
}
#[test]
fn text_that_is_not_an_identifier_is_refused_rather_than_read_as_zero() {
let text = CString::new("not a uid").expect("no NUL");
let mut out = uid_n(3);
let rc = unsafe { mxr_uid_from_string(text.as_ptr(), &mut out) };
assert_eq!(rc, mxr_result_t::MXR_ERR_INVALID_ARGUMENT);
assert_eq!(out, uid_n(3), "a refused parse still wrote an identifier");
assert!(
last_error().contains("not a uid"),
"the failure did not say what could not be read: {}",
last_error()
);
}
#[test]
fn the_empty_identifier_is_the_one_the_protocol_spells_absence_with() {
assert!(mxr_uid_is_zero(mxr_uid_t::default()));
assert!(!mxr_uid_is_zero(uid_n(1)));
}
#[test]
fn a_null_handle_is_an_argument_error_rather_than_a_crash() {
let mut info = std::mem::MaybeUninit::<mxr_device_info_t>::zeroed();
let rc = unsafe { mxr_device(ptr::null(), uid_n(1), info.as_mut_ptr()) };
assert_eq!(rc, mxr_result_t::MXR_ERR_INVALID_ARGUMENT);
assert!(!last_error().is_empty(), "the failure said nothing");
let count = unsafe { mxr_devices(ptr::null(), ptr::null_mut(), 0) };
assert_eq!(count, 0);
let rc = unsafe { mxr_power_on(ptr::null(), mxr_bay_uid_t::default()) };
assert_eq!(rc, mxr_result_t::MXR_ERR_INVALID_ARGUMENT);
unsafe { mxr_remote_close(ptr::null()) };
unsafe { mxr_remote_free(ptr::null_mut()) };
}
#[test]
fn a_client_reports_the_identifier_and_name_it_was_given() {
let name = CString::new("test client").expect("no NUL");
let uid_text = CString::new("00000007.00000000.00000000.a5000000").expect("no NUL");
let handle = client(&name, &uid_text);
let mut uid = mxr_uid_t::default();
let rc = unsafe { mxr_remote_uid(handle, &mut uid) };
assert_eq!(rc, mxr_result_t::MXR_OK);
assert_eq!(uid, uid_n(7), "the configured identifier was not kept");
let mut buf = [0 as c_char; 32];
let rc = unsafe { mxr_remote_name(handle, buf.as_mut_ptr(), buf.len()) };
assert_eq!(rc, mxr_result_t::MXR_OK);
assert_eq!(field(&buf), "test client");
unsafe { mxr_remote_free(handle) };
}
#[test]
fn a_client_that_was_never_started_has_no_target() {
let name = CString::new("unstarted").expect("no NUL");
let uid_text = CString::new("00000001.00000000.00000000.00000000").expect("no NUL");
let handle = client(&name, &uid_text);
let mut port = 0u16;
let rc = unsafe { mxr_remote_target(handle, ptr::null_mut(), 0, &mut port) };
assert_eq!(rc, mxr_result_t::MXR_ERR_NOT_CONNECTED);
unsafe { mxr_remote_free(handle) };
}
#[test]
fn a_command_for_a_device_never_heard_from_is_not_found() {
let name = CString::new("no devices").expect("no NUL");
let uid_text = CString::new("00000002.00000000.00000000.00000000").expect("no NUL");
let handle = client(&name, &uid_text);
let rc = unsafe { mxr_reboot(handle, uid_n(9)) };
assert_eq!(rc, mxr_result_t::MXR_ERR_NOT_FOUND);
assert!(
last_error().contains("no device"),
"the failure did not say what was missing: {}",
last_error()
);
unsafe { mxr_remote_free(handle) };
}
#[test]
fn a_configuration_that_cannot_be_read_yields_no_client() {
let bad = CString::new("300.1.1.1").expect("no NUL");
let config = mxr_config_t {
target_ip: ptr::null(),
port: 0,
broadcast: false,
local_ip: bad.as_ptr(),
interface: ptr::null(),
name: ptr::null(),
uid: ptr::null(),
uid_path: ptr::null(),
};
let handle = unsafe { mxr_remote_new(&config, ptr::null(), ptr::null_mut()) };
assert!(
handle.is_null(),
"a client was built from an unreadable address"
);
assert!(
last_error().contains("local_ip"),
"the failure did not name the field: {}",
last_error()
);
}
#[test]
fn a_list_call_reports_its_length_when_given_nowhere_to_write() {
let name = CString::new("sizing").expect("no NUL");
let uid_text = CString::new("00000003.00000000.00000000.00000000").expect("no NUL");
let handle = client(&name, &uid_text);
let count = unsafe { mxr_devices(handle, ptr::null_mut(), 0) };
assert_eq!(count, 0, "a client that has heard nothing listed a device");
unsafe { mxr_remote_free(handle) };
}
#[test]
fn the_version_is_the_crate_version() {
let version = unsafe { CStr::from_ptr(mxr_version()) };
assert_eq!(version.to_str().expect("ASCII"), env!("CARGO_PKG_VERSION"));
}
#[test]
fn a_subsystem_of_a_device_never_heard_from_is_not_found() {
let name = CString::new("subsystems").expect("no NUL");
let uid_text = CString::new("00000004.00000000.00000000.00000000").expect("no NUL");
let handle = client(&name, &uid_text);
let mut stats = std::mem::MaybeUninit::<mxr_v2ip_stats_t>::zeroed();
let rc = unsafe { mxr_v2ip_stats(handle, uid_n(4), stats.as_mut_ptr()) };
assert_eq!(rc, mxr_result_t::MXR_ERR_NOT_FOUND);
assert!(
last_error().contains("no device"),
"the failure did not say the device was missing: {}",
last_error()
);
let count = unsafe { mxr_audio_endpoints(handle, uid_n(4), ptr::null_mut(), 0) };
assert_eq!(count, 0);
unsafe { mxr_remote_free(handle) };
}
#[test]
fn a_subsystem_read_without_somewhere_to_write_is_an_argument_error() {
let name = CString::new("no output").expect("no NUL");
let uid_text = CString::new("00000005.00000000.00000000.00000000").expect("no NUL");
let handle = client(&name, &uid_text);
let rc = unsafe { mxr_multiviewer_status(handle, uid_n(5), ptr::null_mut()) };
assert_eq!(rc, mxr_result_t::MXR_ERR_INVALID_ARGUMENT);
unsafe { mxr_remote_free(handle) };
}