ci: refresh uvc coverage hygiene baseline
This commit is contained in:
parent
6cbe78e576
commit
4988956f9c
@ -903,12 +903,12 @@
|
||||
"server/src/bin/lesavka_uvc/coverage_model.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 141
|
||||
"loc": 286
|
||||
},
|
||||
"server/src/bin/lesavka_uvc/coverage_startup.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 5,
|
||||
"loc": 229
|
||||
"doc_debt": 0,
|
||||
"loc": 447
|
||||
},
|
||||
"server/src/bin/lesavka_uvc/payload_limits.rs": {
|
||||
"clippy_warnings": 0,
|
||||
@ -1078,7 +1078,7 @@
|
||||
"server/src/main/relay_service_coverage/relay_trait_impl.rs": {
|
||||
"clippy_warnings": 0,
|
||||
"doc_debt": 0,
|
||||
"loc": 372
|
||||
"loc": 373
|
||||
},
|
||||
"server/src/main/relay_service_tests.rs": {
|
||||
"clippy_warnings": 0,
|
||||
|
||||
@ -193,6 +193,11 @@ struct UvcVideoStats {
|
||||
|
||||
#[cfg(coverage)]
|
||||
impl UvcVideoStream {
|
||||
/// Builds an inert coverage-mode stream seeded with the idle JPEG frame.
|
||||
///
|
||||
/// Inputs: the already-open UVC file descriptor, currently unused in this
|
||||
/// harness. Output: stream state that mirrors production defaults. Why:
|
||||
/// coverage tests need the stream bookkeeping without touching hardware.
|
||||
fn new(_fd: i32) -> Self {
|
||||
Self {
|
||||
buffers: Vec::new(),
|
||||
@ -207,6 +212,11 @@ impl UvcVideoStream {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reloads the latest spooled MJPEG frame when it is fresh and safe to use.
|
||||
///
|
||||
/// Inputs: the configured frame path and current buffer limits. Output:
|
||||
/// updated cached frame plus rejection counters. Why: stale or oversized
|
||||
/// frames should degrade to known-safe idle content instead of freezing RCT.
|
||||
fn refresh_latest_frame(&mut self) {
|
||||
let stale = frame_spool_is_stale(&self.frame_path, frame_spool_max_age());
|
||||
if stale && looks_like_mjpeg_frame(&self.latest_frame) {
|
||||
@ -246,6 +256,10 @@ impl UvcVideoStream {
|
||||
.min(self.frame_max_bytes)
|
||||
}
|
||||
|
||||
/// Chooses the safest frame payload for the active UVC buffer.
|
||||
///
|
||||
/// Inputs: one buffer length. Output: current, idle, or minimal JPEG bytes.
|
||||
/// Why: the gadget must always queue valid MJPEG, even with tiny buffers.
|
||||
fn frame_for_buffer(&self, buffer_len: usize) -> &[u8] {
|
||||
if self.latest_frame.len() <= buffer_len && looks_like_mjpeg_frame(&self.latest_frame) {
|
||||
&self.latest_frame
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
/// Runs only the coverage-mode startup probes and refuses the live control loop.
|
||||
///
|
||||
/// Inputs: CLI/env device settings. Output: successful validation until the
|
||||
/// disabled loop guard. Why: contracts need startup coverage without driving USB.
|
||||
fn main() -> Result<()> {
|
||||
let (dev, _cfg) = parse_args()?;
|
||||
let _ = load_interfaces();
|
||||
@ -18,6 +22,10 @@ fn parse_args() -> Result<(String, UvcConfig)> {
|
||||
}
|
||||
|
||||
#[cfg(coverage)]
|
||||
/// Finds the UVC device from `--device`, `-d`, or a positional path argument.
|
||||
///
|
||||
/// Inputs: raw CLI arguments. Output: the selected device path when present.
|
||||
/// Why: manual probes and contract tests both exercise the same device syntax.
|
||||
fn parse_device_arg(args: &[String]) -> Option<String> {
|
||||
args
|
||||
.windows(2)
|
||||
@ -34,6 +42,11 @@ fn parse_device_arg(args: &[String]) -> Option<String> {
|
||||
|
||||
#[cfg(coverage)]
|
||||
impl UvcConfig {
|
||||
/// Builds a bounded UVC mode from environment overrides.
|
||||
///
|
||||
/// Inputs: `LESAVKA_UVC_*` mode variables. Output: a packet-safe mode
|
||||
/// snapshot. Why: coverage contracts must pin descriptor math without
|
||||
/// requiring the real kernel gadget.
|
||||
fn from_env() -> Self {
|
||||
let width = env_u32("LESAVKA_UVC_WIDTH", 1280);
|
||||
let height = env_u32("LESAVKA_UVC_HEIGHT", 720);
|
||||
@ -72,6 +85,10 @@ impl UvcConfig {
|
||||
|
||||
#[cfg(coverage)]
|
||||
impl UvcState {
|
||||
/// Creates the control-state mirrors used by UVC request handling tests.
|
||||
///
|
||||
/// Inputs: one validated UVC mode. Output: default/probe/commit buffers.
|
||||
/// Why: tests need deterministic state transitions for host negotiation.
|
||||
fn new(cfg: UvcConfig) -> Self {
|
||||
let ctrl_len = stream_ctrl_len();
|
||||
let default = build_streaming_control(&cfg, ctrl_len);
|
||||
@ -101,6 +118,10 @@ fn read_interface(path: &str) -> Option<u8> {
|
||||
}
|
||||
|
||||
#[cfg(coverage)]
|
||||
/// Opens the UVC control device with safe coverage defaults.
|
||||
///
|
||||
/// Inputs: device path and read/write env knobs. Output: opened file handle.
|
||||
/// Why: tests should default to non-blocking read-only access unless opted in.
|
||||
fn open_with_retry(path: &str) -> Result<std::fs::File> {
|
||||
let read_only = uvc_control_read_only();
|
||||
let mut opts = OpenOptions::new();
|
||||
@ -202,6 +223,10 @@ fn derived_uvc_frame_max_bytes_for_transport(fps: u32, max_packet: u32, bulk: bo
|
||||
}
|
||||
|
||||
#[cfg(coverage)]
|
||||
/// Returns the effective MJPEG byte budget for the selected USB transport.
|
||||
///
|
||||
/// Inputs: negotiated max packet size and bulk/isochronous mode. Output: bytes
|
||||
/// per second. Why: frame-size checks should mirror the transport budget.
|
||||
fn effective_uvc_mjpeg_budget_bytes_per_sec(max_packet: u32, bulk: bool) -> u32 {
|
||||
let configured = env_u32(
|
||||
"LESAVKA_UVC_MJPEG_BUDGET_BYTES_PER_SEC",
|
||||
@ -218,6 +243,10 @@ fn effective_uvc_mjpeg_budget_bytes_per_sec(max_packet: u32, bulk: bool) -> u32
|
||||
}
|
||||
|
||||
#[cfg(coverage)]
|
||||
/// Computes the high-speed isochronous payload budget.
|
||||
///
|
||||
/// Inputs: endpoint packet size and `LESAVKA_UVC_ISOCHRONOUS_LIMIT_PCT`.
|
||||
/// Output: capped bytes per second. Why: coverage locks the safety margin.
|
||||
fn uvc_isochronous_budget_bytes_per_sec(max_packet: u32) -> u32 {
|
||||
let pct = env_u32(
|
||||
"LESAVKA_UVC_ISOCHRONOUS_LIMIT_PCT",
|
||||
@ -232,6 +261,10 @@ fn uvc_isochronous_budget_bytes_per_sec(max_packet: u32) -> u32 {
|
||||
}
|
||||
|
||||
#[cfg(coverage)]
|
||||
/// Parses a boolean environment flag with a default fallback.
|
||||
///
|
||||
/// Inputs: variable name and default value. Output: parsed boolean. Why:
|
||||
/// UVC safety switches must handle common true/false spellings consistently.
|
||||
fn env_flag_enabled(name: &str, default: bool) -> bool {
|
||||
env::var(name)
|
||||
.ok()
|
||||
@ -262,6 +295,10 @@ fn uvc_frame_size_guard_enabled() -> bool {
|
||||
}
|
||||
|
||||
#[cfg(coverage)]
|
||||
/// Decides whether the coverage helper may use bulk transport.
|
||||
///
|
||||
/// Inputs: env override plus configfs capabilities. Output: effective mode.
|
||||
/// Why: tests should not advertise bulk if the gadget tree lacks support.
|
||||
fn uvc_bulk_transfer_enabled() -> bool {
|
||||
if !env_flag_enabled("LESAVKA_UVC_BULK", true) {
|
||||
return false;
|
||||
@ -290,6 +327,10 @@ fn uvc_frame_size_for_active_mode(
|
||||
}
|
||||
|
||||
#[cfg(coverage)]
|
||||
/// Returns the optional JSON stats path for the UVC coverage helper.
|
||||
///
|
||||
/// Inputs: `LESAVKA_UVC_STATS_PATH`. Output: path or disabled state. Why:
|
||||
/// tests and probes need a cheap health artifact without forcing writes.
|
||||
fn uvc_stats_path() -> Option<std::path::PathBuf> {
|
||||
match std::env::var("LESAVKA_UVC_STATS_PATH") {
|
||||
Ok(value) => {
|
||||
@ -305,6 +346,10 @@ fn uvc_stats_path() -> Option<std::path::PathBuf> {
|
||||
}
|
||||
|
||||
#[cfg(coverage)]
|
||||
/// Writes a text artifact through a temporary file and atomic rename.
|
||||
///
|
||||
/// Inputs: target path and payload. Output: persisted file. Why: readers should
|
||||
/// never observe partially-written UVC stats JSON.
|
||||
fn write_atomic_text(path: &std::path::Path, text: &str) -> Result<()> {
|
||||
if let Some(parent) = path.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
@ -316,6 +361,10 @@ fn write_atomic_text(path: &std::path::Path, text: &str) -> Result<()> {
|
||||
}
|
||||
|
||||
#[cfg(coverage)]
|
||||
/// Serializes the compact UVC video-stat snapshot consumed by probes.
|
||||
///
|
||||
/// Inputs: current counters and frame cap. Output: one JSON object string. Why:
|
||||
/// shell probes need stable fields without depending on Rust serialization.
|
||||
fn uvc_stats_snapshot_json(stats: &UvcVideoStats, frame_cap: usize) -> String {
|
||||
format!(
|
||||
"{{\"queued\":{},\"reloaded\":{},\"stale_replay\":{},\"rejected_oversize\":{},\"rejected_invalid\":{},\"fallback_idle\":{},\"latest_bytes\":{},\"frame_cap\":{},\"last_rejected_oversize_bytes\":{},\"last_rejected_oversize_cap\":{},\"paced_sleeps\":{},\"paced_sleep_ms\":{}}}\n",
|
||||
@ -335,6 +384,10 @@ fn uvc_stats_snapshot_json(stats: &UvcVideoStats, frame_cap: usize) -> String {
|
||||
}
|
||||
|
||||
#[cfg(coverage)]
|
||||
/// Returns the optional periodic stats write interval.
|
||||
///
|
||||
/// Inputs: `LESAVKA_UVC_STATS_INTERVAL_MS`. Output: duration or disabled state.
|
||||
/// Why: probes can ask for telemetry without slowing every coverage test.
|
||||
fn uvc_stats_interval() -> Option<std::time::Duration> {
|
||||
match env_u64("LESAVKA_UVC_STATS_INTERVAL_MS", DEFAULT_UVC_STATS_INTERVAL_MS) {
|
||||
0 => None,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user