171 lines
11 KiB
Rust
171 lines
11 KiB
Rust
//! Contract tests for the server-to-RCT mode matrix harness.
|
|
//!
|
|
//! Scope: statically guard the hardware-in-the-loop matrix script defaults and
|
|
//! summary artifacts.
|
|
//! Targets: `scripts/manual/run_server_to_rc_mode_matrix.sh`.
|
|
//! Why: server-to-RCT calibration is now considered complete, so the matrix
|
|
//! script must keep the blessed offsets and evidence outputs stable.
|
|
|
|
const SERVER_RC_MODE_MATRIX_SCRIPT: &str =
|
|
include_str!("../../scripts/manual/run_server_to_rc_mode_matrix.sh");
|
|
|
|
#[test]
|
|
fn server_rc_mode_matrix_validates_advertised_uvc_profiles() {
|
|
for expected in [
|
|
"LESAVKA_SERVER_RC_CORE_WEBCAM_MODES=${LESAVKA_SERVER_RC_CORE_WEBCAM_MODES:-1280x720@20,1280x720@30,1920x1080@20,1920x1080@30}",
|
|
"LESAVKA_SERVER_RC_MODES=${LESAVKA_SERVER_RC_MODES:-${LESAVKA_SERVER_RC_CORE_WEBCAM_MODES}}",
|
|
"LESAVKA_SERVER_REPO=${LESAVKA_SERVER_REPO:-auto}",
|
|
"LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US:-${LESAVKA_OUTPUT_DELAY_PROBE_AUDIO_DELAY_US:-0}}",
|
|
"LESAVKA_SERVER_RC_DEFAULT_VIDEO_DELAY_US=${LESAVKA_SERVER_RC_DEFAULT_VIDEO_DELAY_US:-135090}",
|
|
"LESAVKA_SERVER_RC_MODE_AUDIO_DELAYS_US=${LESAVKA_SERVER_RC_MODE_AUDIO_DELAYS_US:-1280x720@20=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US},1280x720@30=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US},1920x1080@20=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US},1920x1080@30=${LESAVKA_SERVER_RC_DEFAULT_AUDIO_DELAY_US}}",
|
|
"LESAVKA_SERVER_RC_MODE_DELAYS_US=${LESAVKA_SERVER_RC_MODE_DELAYS_US:-1280x720@20=162659,1280x720@30=135090,1920x1080@20=160045,1920x1080@30=127952}",
|
|
"LESAVKA_SERVER_RC_MODE_DISCOVERY_SIZES=${LESAVKA_SERVER_RC_MODE_DISCOVERY_SIZES:-1280x720,1920x1080}",
|
|
"LESAVKA_SERVER_RC_MODE_DISCOVERY_FPS=${LESAVKA_SERVER_RC_MODE_DISCOVERY_FPS:-20,30}",
|
|
"LESAVKA_SERVER_RC_MODE_DISCOVERY_INCLUDE_REGEX=${LESAVKA_SERVER_RC_MODE_DISCOVERY_INCLUDE_REGEX:-Logitech|BRIO|C9[0-9]+|HD UVC WebCam|USB2[.]0 HD|Integrated Camera|Webcam|Camera}",
|
|
"LESAVKA_SERVER_RC_MODE_DISCOVERY_EXCLUDE_REGEX=${LESAVKA_SERVER_RC_MODE_DISCOVERY_EXCLUDE_REGEX:-Lesavka|UGREEN|MACROSILICON|Composite|Capture}",
|
|
"LESAVKA_SERVER_RC_MODE_SOURCE=${LESAVKA_SERVER_RC_MODE_SOURCE:-configured}",
|
|
"LESAVKA_SERVER_RC_RECONFIGURE=${LESAVKA_SERVER_RC_RECONFIGURE:-0}",
|
|
"LESAVKA_SERVER_RC_RECONFIGURE_UPDATE=${LESAVKA_SERVER_RC_RECONFIGURE_UPDATE:-0}",
|
|
"LESAVKA_SERVER_RC_RECONFIGURE_STRATEGY=${LESAVKA_SERVER_RC_RECONFIGURE_STRATEGY:-runtime}",
|
|
"LESAVKA_SERVER_RC_ALLOW_GADGET_RESET=${LESAVKA_SERVER_RC_ALLOW_GADGET_RESET:-1}",
|
|
"LESAVKA_SERVER_RC_RECONFIGURE_VERBOSE=${LESAVKA_SERVER_RC_RECONFIGURE_VERBOSE:-0}",
|
|
"LESAVKA_SERVER_RC_PROMPT_SUDO_EARLY=${LESAVKA_SERVER_RC_PROMPT_SUDO_EARLY:-1}",
|
|
"LESAVKA_SERVER_RC_START_DELAY_SECONDS=${LESAVKA_SERVER_RC_START_DELAY_SECONDS:-0}",
|
|
"LESAVKA_SERVER_RC_WAIT_TETHYS_READY=${LESAVKA_SERVER_RC_WAIT_TETHYS_READY:-1}",
|
|
"LESAVKA_SERVER_RC_TETHYS_READY_TIMEOUT_SECONDS=${LESAVKA_SERVER_RC_TETHYS_READY_TIMEOUT_SECONDS:-60}",
|
|
"LESAVKA_SERVER_RC_TETHYS_SETTLE_SECONDS=${LESAVKA_SERVER_RC_TETHYS_SETTLE_SECONDS:-6}",
|
|
"LESAVKA_SERVER_RC_PREROLL_DISCARD_SECONDS=${LESAVKA_SERVER_RC_PREROLL_DISCARD_SECONDS:-3}",
|
|
"LESAVKA_SERVER_RC_PROBE_PREBUILD=${LESAVKA_SERVER_RC_PROBE_PREBUILD:-1}",
|
|
"LESAVKA_SERVER_RC_TUNE_DELAYS=${LESAVKA_SERVER_RC_TUNE_DELAYS:-1}",
|
|
"LESAVKA_SERVER_RC_TUNE_CONFIRM=${LESAVKA_SERVER_RC_TUNE_CONFIRM:-1}",
|
|
"LESAVKA_SERVER_RC_TUNE_MIN_PAIRS=${LESAVKA_SERVER_RC_TUNE_MIN_PAIRS:-13}",
|
|
"LESAVKA_SERVER_RC_TUNE_MAX_ABS_SKEW_MS=${LESAVKA_SERVER_RC_TUNE_MAX_ABS_SKEW_MS:-1000}",
|
|
"LESAVKA_SERVER_RC_TUNE_MAX_STEP_US=${LESAVKA_SERVER_RC_TUNE_MAX_STEP_US:-500000}",
|
|
"LESAVKA_SERVER_RC_TUNE_MIN_CHANGE_US=${LESAVKA_SERVER_RC_TUNE_MIN_CHANGE_US:-5000}",
|
|
"Theia sudo password for %s",
|
|
"==> priming remote sudo on ${LESAVKA_SERVER_HOST}",
|
|
"sleep_start_delay",
|
|
"==> delaying server-to-RC matrix start for ${LESAVKA_SERVER_RC_START_DELAY_SECONDS}s",
|
|
"remote sudo has already been primed; sleeping before prebuild/reconfigure/capture",
|
|
"LESAVKA_SERVER_RC_START_DELAY_SECONDS must be a non-negative number",
|
|
"start_delay=${LESAVKA_SERVER_RC_START_DELAY_SECONDS}s",
|
|
"==> prebuilding relay control/analyzer once for the mode matrix",
|
|
"LESAVKA_SERVER_RC_MODES=auto",
|
|
"discover_local_webcam_modes",
|
|
"lookup_audio_delay_us",
|
|
"local webcam",
|
|
"mode_source=${LESAVKA_SERVER_RC_MODE_SOURCE}",
|
|
"video_delays=${LESAVKA_SERVER_RC_MODE_DELAYS_US}",
|
|
"audio_delays=${LESAVKA_SERVER_RC_MODE_AUDIO_DELAYS_US}",
|
|
"pulse_tool=${REMOTE_PULSE_CAPTURE_TOOL}",
|
|
"fast runtime env updated: CAM_OUTPUT=uvc",
|
|
"cycling UVC gadget descriptors",
|
|
"lesavka-core reconfigure log:",
|
|
"missing /usr/local/bin/lesavka-core.sh",
|
|
"wait_tethys_media_ready",
|
|
"==> waiting for Tethys media endpoints for ${mode}",
|
|
"Tethys media ready: video=%s mode=%s audio_stack=%s",
|
|
"timed out waiting for Tethys Lesavka media endpoints",
|
|
"LESAVKA_SERVER_RC_FRESHNESS_MAX_AGE_MS=${LESAVKA_SERVER_RC_FRESHNESS_MAX_AGE_MS:-350}",
|
|
"LESAVKA_SERVER_RC_FRESHNESS_MIN_PAIRS=${LESAVKA_SERVER_RC_FRESHNESS_MIN_PAIRS:-${LESAVKA_SERVER_RC_TUNE_MIN_PAIRS}}",
|
|
"LESAVKA_SERVER_RC_MIN_CODED_PAIRS=${LESAVKA_SERVER_RC_MIN_CODED_PAIRS:-${LESAVKA_SERVER_RC_FRESHNESS_MIN_PAIRS}}",
|
|
"LESAVKA_SERVER_RC_REQUIRE_ALL_CODED_PAIRS=${LESAVKA_SERVER_RC_REQUIRE_ALL_CODED_PAIRS:-0}",
|
|
"LESAVKA_SERVER_RC_REQUIRE_SMOOTHNESS_PASS=${LESAVKA_SERVER_RC_REQUIRE_SMOOTHNESS_PASS:-0}",
|
|
"LESAVKA_SERVER_RC_SIGNAL_READY=${LESAVKA_SERVER_RC_SIGNAL_READY:-1}",
|
|
"LESAVKA_SERVER_RC_SIGNAL_READY_MODE=${LESAVKA_SERVER_RC_SIGNAL_READY_MODE:-conditioned_capture}",
|
|
"LESAVKA_SERVER_RC_SIGNAL_READY_MIN_PAIRS=${LESAVKA_SERVER_RC_SIGNAL_READY_MIN_PAIRS:-3}",
|
|
"LESAVKA_SERVER_RC_SIGNAL_READY_DURATION_SECONDS=${LESAVKA_SERVER_RC_SIGNAL_READY_DURATION_SECONDS:-12}",
|
|
"LESAVKA_SERVER_RC_SIGNAL_READY_ATTEMPTS=${LESAVKA_SERVER_RC_SIGNAL_READY_ATTEMPTS:-4}",
|
|
"LESAVKA_SERVER_RC_SIGNAL_READY_RETRY_DELAY_SECONDS=${LESAVKA_SERVER_RC_SIGNAL_READY_RETRY_DELAY_SECONDS:-5}",
|
|
"LESAVKA_SERVER_RC_SIGNAL_CONDITION_SECONDS=${LESAVKA_SERVER_RC_SIGNAL_CONDITION_SECONDS:-12}",
|
|
"LESAVKA_SERVER_RC_SIGNAL_CONDITION_WARMUP_SECONDS=${LESAVKA_SERVER_RC_SIGNAL_CONDITION_WARMUP_SECONDS:-1}",
|
|
"LESAVKA_SERVER_RC_SIGNAL_CONDITION_GAP_SECONDS=${LESAVKA_SERVER_RC_SIGNAL_CONDITION_GAP_SECONDS:-1}",
|
|
"LESAVKA_SERVER_RC_ANALYSIS_TIMELINE_WINDOW=${LESAVKA_SERVER_RC_ANALYSIS_TIMELINE_WINDOW:-auto}",
|
|
"signal_readiness_passed",
|
|
"write_signal_readiness_attempt_result",
|
|
"write_signal_readiness_attempts_summary",
|
|
"schema\": \"lesavka.server-rc-signal-readiness-attempt.v1\"",
|
|
"schema\": \"lesavka.server-rc-signal-readiness-summary.v1\"",
|
|
"using same-capture signal conditioning before measured probe",
|
|
"proving Tethys signal readiness before measured probe",
|
|
"readiness attempt ${readiness_attempt}/${LESAVKA_SERVER_RC_SIGNAL_READY_ATTEMPTS}",
|
|
"waiting ${LESAVKA_SERVER_RC_SIGNAL_READY_RETRY_DELAY_SECONDS}s before retrying signal readiness",
|
|
"signal readiness did not pass",
|
|
"signal attempt ",
|
|
"smoothness_required",
|
|
"smoothness_warnings",
|
|
"smoothness warning:",
|
|
"coded visibility:",
|
|
"LESAVKA_SERVER_RC_MAX_VIDEO_HICCUPS=${LESAVKA_SERVER_RC_MAX_VIDEO_HICCUPS:-0}",
|
|
"LESAVKA_SERVER_RC_MAX_AUDIO_HICCUPS=${LESAVKA_SERVER_RC_MAX_AUDIO_HICCUPS:-0}",
|
|
"LESAVKA_SERVER_RC_MAX_VIDEO_MISSING_FRAMES=${LESAVKA_SERVER_RC_MAX_VIDEO_MISSING_FRAMES:-12}",
|
|
"mode-matrix-summary.json",
|
|
"mode-matrix-summary.csv",
|
|
"mode-matrix-summary.txt",
|
|
"mode-delay-recommendations.json",
|
|
"mode-delay-recommendations.env",
|
|
"schema\": \"lesavka.server-rc-mode-result.v1\"",
|
|
"schema\": \"lesavka.server-rc-mode-matrix-summary.v1\"",
|
|
"schema\": \"lesavka.server-rc-mode-delay-recommendations.v1\"",
|
|
"output_delay_calibration",
|
|
"write_tune_candidate_env",
|
|
"annotate_mode_result",
|
|
"LESAVKA_SERVER_RC_REPEAT_COUNT=${LESAVKA_SERVER_RC_REPEAT_COUNT:-1}",
|
|
"mode-static-calibration.json",
|
|
"mode-matrix-run.log",
|
|
"mode-result-seed.json",
|
|
"mode-result-tuned.json",
|
|
"==> mode ${mode} run ${mode_run_index}: confirming tuned delays",
|
|
"calibration_ready",
|
|
"calibration_video_target_offset_us",
|
|
"calibration_audio_target_offset_us",
|
|
"calibration:",
|
|
"capture_timebase_status",
|
|
"capture timebase invalid",
|
|
"capture timing:",
|
|
"signature_coverage",
|
|
"paired coded signatures",
|
|
"signature_missing_codes",
|
|
"probe_env=(",
|
|
"\"REMOTE_PULSE_CAPTURE_TOOL=${REMOTE_PULSE_CAPTURE_TOOL}\"",
|
|
"\"REMOTE_PULSE_VIDEO_MODE=${REMOTE_PULSE_VIDEO_MODE}\"",
|
|
"\"REMOTE_CAPTURE_STACK=${REMOTE_CAPTURE_STACK}\"",
|
|
"\"REMOTE_CAPTURE_ALLOW_ALSA_FALLBACK=${REMOTE_CAPTURE_ALLOW_ALSA_FALLBACK}\"",
|
|
"\"REMOTE_CAPTURE_PREROLL_DISCARD_SECONDS=${LESAVKA_SERVER_RC_PREROLL_DISCARD_SECONDS}\"",
|
|
"\"REMOTE_CAPTURE_READY_SETTLE_SECONDS=${REMOTE_CAPTURE_READY_SETTLE_SECONDS}\"",
|
|
"\"PROBE_PREBUILD=0\"",
|
|
"\"VIDEO_SIZE=${width}x${height}\"",
|
|
"\"VIDEO_FPS=${fps}\"",
|
|
"\"REMOTE_EXPECT_UVC_WIDTH=${width}\"",
|
|
"\"REMOTE_EXPECT_UVC_HEIGHT=${height}\"",
|
|
"\"REMOTE_EXPECT_UVC_FPS=${fps}\"",
|
|
"\"LESAVKA_OUTPUT_DELAY_PROBE_AUDIO_DELAY_US=${audio_delay_us}\"",
|
|
"\"LESAVKA_OUTPUT_DELAY_PROBE_VIDEO_DELAY_US=${video_delay_us}\"",
|
|
"\"LESAVKA_OUTPUT_DELAY_APPLY=0\"",
|
|
"\"LESAVKA_OUTPUT_DELAY_SAVE=0\"",
|
|
"\"LESAVKA_OUTPUT_FRESHNESS_MAX_AGE_MS=${LESAVKA_SERVER_RC_FRESHNESS_MAX_AGE_MS}\"",
|
|
"\"LESAVKA_OUTPUT_FRESHNESS_MAX_CLOCK_UNCERTAINTY_MS=${LESAVKA_SERVER_RC_FRESHNESS_MAX_CLOCK_UNCERTAINTY_MS}\"",
|
|
"\"LESAVKA_OUTPUT_FRESHNESS_MIN_PAIRS=${min_pairs}\"",
|
|
"sync did not pass",
|
|
"freshness did not pass",
|
|
"video hiccups",
|
|
"estimated missing video frames",
|
|
"audio hiccups",
|
|
] {
|
|
assert!(
|
|
SERVER_RC_MODE_MATRIX_SCRIPT.contains(expected),
|
|
"server-to-RC mode matrix script should contain {expected}"
|
|
);
|
|
}
|
|
let prime = SERVER_RC_MODE_MATRIX_SCRIPT
|
|
.find("prime_remote_sudo\nsleep_start_delay\nprebuild_probe_tools")
|
|
.expect("matrix should prime remote sudo before delayed start and prebuild");
|
|
let prompt = SERVER_RC_MODE_MATRIX_SCRIPT
|
|
.find("Theia sudo password for %s")
|
|
.expect("matrix should contain the immediate Theia password prompt");
|
|
assert!(
|
|
prompt < prime,
|
|
"password prompt machinery should be defined before the matrix startup sequence"
|
|
);
|
|
}
|