247 lines
7.3 KiB
Rust
247 lines
7.3 KiB
Rust
//! Extra coverage for `lesavka-uvc` control/error branches.
|
|
//!
|
|
//! Scope: keep additive branch tests in a separate file so each testing module
|
|
//! remains under the 500 LOC contract.
|
|
//! Targets: `server/src/bin/lesavka-uvc.rs`.
|
|
//! Why: preserve expanded UVC branch coverage while satisfying test module contracts.
|
|
|
|
mod uvc_binary_extra {
|
|
#![allow(warnings)]
|
|
#![allow(clippy::all)]
|
|
#![allow(dead_code)]
|
|
#![allow(unused_imports)]
|
|
#![allow(unused_variables)]
|
|
|
|
include!(env!("LESAVKA_SERVER_UVC_BIN_SRC"));
|
|
|
|
use serial_test::serial;
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
use temp_env::with_var;
|
|
use tempfile::NamedTempFile;
|
|
|
|
fn sample_cfg() -> UvcConfig {
|
|
UvcConfig {
|
|
width: 1280,
|
|
height: 720,
|
|
fps: 25,
|
|
interval: 400_000,
|
|
max_packet: 1024,
|
|
frame_size: 1_843_200,
|
|
}
|
|
}
|
|
|
|
fn sample_interfaces() -> UvcInterfaces {
|
|
UvcInterfaces {
|
|
control: UVC_STRING_CONTROL_IDX,
|
|
streaming: UVC_STRING_STREAMING_IDX,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn handle_setup_stalls_non_streaming_set_cur_and_non_in_requests() {
|
|
let interfaces = sample_interfaces();
|
|
let mut state = UvcState::new(sample_cfg());
|
|
let mut pending = None;
|
|
|
|
let set_cur_other_iface = UsbCtrlRequest {
|
|
b_request_type: 0x00,
|
|
b_request: UVC_SET_CUR,
|
|
w_value: (0xFEu16) << 8,
|
|
w_index: 0x00FF,
|
|
w_length: 8,
|
|
};
|
|
handle_setup(
|
|
-1,
|
|
0,
|
|
&mut state,
|
|
&mut pending,
|
|
interfaces,
|
|
set_cur_other_iface,
|
|
true,
|
|
);
|
|
assert!(pending.is_none());
|
|
|
|
let non_in_non_set_cur = UsbCtrlRequest {
|
|
b_request_type: 0x00,
|
|
b_request: UVC_GET_CUR,
|
|
w_value: (UVC_VS_PROBE_CONTROL as u16) << 8,
|
|
w_index: interfaces.streaming as u16,
|
|
w_length: 8,
|
|
};
|
|
handle_setup(
|
|
-1,
|
|
0,
|
|
&mut state,
|
|
&mut pending,
|
|
interfaces,
|
|
non_in_non_set_cur,
|
|
true,
|
|
);
|
|
assert!(pending.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn handle_setup_rejects_oversized_set_cur_payload() {
|
|
let interfaces = sample_interfaces();
|
|
let mut state = UvcState::new(sample_cfg());
|
|
let mut pending = None;
|
|
let oversized = UsbCtrlRequest {
|
|
b_request_type: 0x00,
|
|
b_request: UVC_SET_CUR,
|
|
w_value: (UVC_VS_PROBE_CONTROL as u16) << 8,
|
|
w_index: interfaces.streaming as u16,
|
|
w_length: (UVC_DATA_SIZE as u16).saturating_add(1),
|
|
};
|
|
handle_setup(-1, 0, &mut state, &mut pending, interfaces, oversized, true);
|
|
assert!(pending.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn handle_setup_stalls_unknown_in_selector() {
|
|
let interfaces = sample_interfaces();
|
|
let mut state = UvcState::new(sample_cfg());
|
|
let mut pending = None;
|
|
let req = UsbCtrlRequest {
|
|
b_request_type: USB_DIR_IN,
|
|
b_request: UVC_GET_CUR,
|
|
w_value: (0xFEu16) << 8,
|
|
w_index: interfaces.streaming as u16,
|
|
w_length: 8,
|
|
};
|
|
handle_setup(-1, 0, &mut state, &mut pending, interfaces, req, true);
|
|
assert!(pending.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn handle_data_ignores_missing_pending_and_negative_lengths() {
|
|
let interfaces = sample_interfaces();
|
|
let mut state = UvcState::new(sample_cfg());
|
|
let mut pending = None;
|
|
handle_data(
|
|
-1,
|
|
0,
|
|
&mut state,
|
|
&mut pending,
|
|
interfaces,
|
|
UvcRequestData {
|
|
length: 8,
|
|
data: [0u8; UVC_DATA_SIZE],
|
|
},
|
|
true,
|
|
);
|
|
|
|
pending = Some(PendingRequest {
|
|
interface: interfaces.streaming,
|
|
selector: UVC_VS_PROBE_CONTROL,
|
|
expected_len: STREAM_CTRL_SIZE_11,
|
|
});
|
|
handle_data(
|
|
-1,
|
|
0,
|
|
&mut state,
|
|
&mut pending,
|
|
interfaces,
|
|
UvcRequestData {
|
|
length: -1,
|
|
data: [0u8; UVC_DATA_SIZE],
|
|
},
|
|
true,
|
|
);
|
|
assert!(pending.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn handle_data_ignores_non_streaming_pending_requests() {
|
|
let interfaces = sample_interfaces();
|
|
let mut state = UvcState::new(sample_cfg());
|
|
let mut pending = Some(PendingRequest {
|
|
interface: interfaces.control,
|
|
selector: UVC_VS_PROBE_CONTROL,
|
|
expected_len: STREAM_CTRL_SIZE_11,
|
|
});
|
|
let mut payload = [0u8; UVC_DATA_SIZE];
|
|
payload[2] = 1;
|
|
handle_data(
|
|
-1,
|
|
0,
|
|
&mut state,
|
|
&mut pending,
|
|
interfaces,
|
|
UvcRequestData {
|
|
length: STREAM_CTRL_SIZE_11 as i32,
|
|
data: payload,
|
|
},
|
|
true,
|
|
);
|
|
assert!(pending.is_none());
|
|
assert_eq!(state.probe, state.default);
|
|
}
|
|
|
|
#[test]
|
|
fn build_in_response_returns_none_for_unknown_selector() {
|
|
let state = UvcState::new(sample_cfg());
|
|
let interfaces = sample_interfaces();
|
|
let response = build_in_response(
|
|
&state,
|
|
interfaces,
|
|
interfaces.streaming,
|
|
0xFE,
|
|
UVC_GET_CUR,
|
|
8,
|
|
);
|
|
assert!(response.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn sanitize_streaming_control_keeps_defaults_for_short_payload() {
|
|
let state = UvcState::new(sample_cfg());
|
|
let short = [0u8; 8];
|
|
let out = sanitize_streaming_control(&short, &state);
|
|
assert_eq!(out, state.default);
|
|
}
|
|
|
|
#[test]
|
|
fn io_helpers_return_none_for_empty_or_missing_input() {
|
|
let empty = NamedTempFile::new().expect("tmp");
|
|
fs::write(empty.path(), "\n").expect("write empty");
|
|
assert_eq!(read_u32_first(empty.path().to_str().expect("path")), None);
|
|
|
|
let missing = PathBuf::from(format!(
|
|
"/tmp/lesavka-missing-fifo-{}-{}",
|
|
std::process::id(),
|
|
std::thread::current().name().unwrap_or("anon")
|
|
));
|
|
assert_eq!(read_fifo_min(missing.to_str().expect("missing")), None);
|
|
}
|
|
|
|
#[test]
|
|
fn compute_payload_cap_clamps_limit_pct_bounds() {
|
|
with_var("LESAVKA_UVC_MAXPAYLOAD_LIMIT", None::<&str>, || {
|
|
with_var("LESAVKA_UVC_LIMIT_PCT", Some("0"), || {
|
|
let cap = compute_payload_cap(false);
|
|
if let Some(cap) = cap {
|
|
assert!(cap.pct >= 1);
|
|
}
|
|
});
|
|
with_var("LESAVKA_UVC_LIMIT_PCT", Some("250"), || {
|
|
let cap = compute_payload_cap(true);
|
|
if let Some(cap) = cap {
|
|
assert!(cap.pct <= 100);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn main_returns_error_for_non_uvc_device_node() {
|
|
with_var("LESAVKA_UVC_DEV", Some("/dev/null"), || {
|
|
with_var("LESAVKA_UVC_BLOCKING", Some("1"), || {
|
|
let result = main();
|
|
assert!(result.is_err(), "non-UVC node should fail during event subscribe");
|
|
});
|
|
});
|
|
}
|
|
}
|