664 lines
27 KiB
Rust
664 lines
27 KiB
Rust
use super::*;
|
|
use tempfile::NamedTempFile;
|
|
|
|
const BLESSED_SERVER_TO_RCT_VIDEO_OFFSETS: &[(&str, i64)] = &[
|
|
("1280x720@20", 162_659),
|
|
("1280x720@30", 135_090),
|
|
("1920x1080@20", 160_045),
|
|
("1920x1080@30", 127_952),
|
|
];
|
|
|
|
/// Keeps `with_clean_offset_env` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn with_clean_offset_env(test: impl FnOnce()) {
|
|
temp_env::with_vars(
|
|
[
|
|
("LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US", None::<&str>),
|
|
("LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US", None::<&str>),
|
|
(
|
|
"LESAVKA_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US",
|
|
None::<&str>,
|
|
),
|
|
(
|
|
"LESAVKA_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US",
|
|
None::<&str>,
|
|
),
|
|
("UVC_MODE", None::<&str>),
|
|
("LESAVKA_UVC_MODE", None::<&str>),
|
|
("LESAVKA_UVC_WIDTH", None::<&str>),
|
|
("LESAVKA_UVC_HEIGHT", None::<&str>),
|
|
("LESAVKA_UVC_FPS", None::<&str>),
|
|
("LESAVKA_UVC_INTERVAL", None::<&str>),
|
|
("LESAVKA_CAM_WIDTH", None::<&str>),
|
|
("LESAVKA_CAM_HEIGHT", None::<&str>),
|
|
("LESAVKA_CAM_FPS", None::<&str>),
|
|
],
|
|
test,
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `blessed_server_to_rct_offsets_are_release_defaults` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn blessed_server_to_rct_offsets_are_release_defaults() {
|
|
assert_eq!(
|
|
FACTORY_MJPEG_VIDEO_OFFSET_US, FACTORY_MJPEG_VIDEO_OFFSET_1280X720_30_US,
|
|
"720p30 is the blessed default profile until a new lab matrix replaces it"
|
|
);
|
|
assert_eq!(FACTORY_MJPEG_VIDEO_OFFSET_1280X720_20_US, 162_659);
|
|
assert_eq!(FACTORY_MJPEG_VIDEO_OFFSET_1280X720_30_US, 135_090);
|
|
assert_eq!(FACTORY_MJPEG_VIDEO_OFFSET_1920X1080_20_US, 160_045);
|
|
assert_eq!(FACTORY_MJPEG_VIDEO_OFFSET_1920X1080_30_US, 127_952);
|
|
assert_eq!(
|
|
FACTORY_MJPEG_AUDIO_MODE_OFFSETS_US,
|
|
"1280x720@20=0,1280x720@30=0,1920x1080@20=0,1920x1080@30=0"
|
|
);
|
|
assert_eq!(
|
|
FACTORY_MJPEG_VIDEO_MODE_OFFSETS_US,
|
|
"1280x720@20=162659,1280x720@30=135090,1920x1080@20=160045,1920x1080@30=127952"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `every_supported_uvc_mode_loads_tailored_factory_offset` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn every_supported_uvc_mode_loads_tailored_factory_offset() {
|
|
for (mode, expected_offset_us) in BLESSED_SERVER_TO_RCT_VIDEO_OFFSETS {
|
|
with_clean_offset_env(|| {
|
|
temp_env::with_var("UVC_MODE", Some(*mode), || {
|
|
let state = snapshot_from_env();
|
|
assert_eq!(
|
|
state.factory_video_offset_us, *expected_offset_us,
|
|
"{mode} should use its baked server-to-RCT factory offset"
|
|
);
|
|
assert_eq!(
|
|
state.default_video_offset_us, *expected_offset_us,
|
|
"{mode} should default to its baked server-to-RCT offset"
|
|
);
|
|
assert_eq!(state.default_audio_offset_us, 0);
|
|
assert_eq!(state.source, "factory");
|
|
assert_eq!(state.confidence, FACTORY_CONFIDENCE);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn default_snapshot_uses_factory_mjpeg_calibration() {
|
|
with_clean_offset_env(|| {
|
|
let state = snapshot_from_env();
|
|
assert_eq!(state.default_audio_offset_us, 0);
|
|
assert_eq!(state.active_video_offset_us, FACTORY_MJPEG_VIDEO_OFFSET_US);
|
|
assert_eq!(state.source, "factory");
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `default_snapshot_uses_uvc_mode_factory_calibration` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn default_snapshot_uses_uvc_mode_factory_calibration() {
|
|
temp_env::with_vars(
|
|
[
|
|
("LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US", None::<&str>),
|
|
("LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US", None::<&str>),
|
|
(
|
|
"LESAVKA_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US",
|
|
None::<&str>,
|
|
),
|
|
(
|
|
"LESAVKA_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US",
|
|
None::<&str>,
|
|
),
|
|
("LESAVKA_UVC_WIDTH", Some("1920")),
|
|
("LESAVKA_UVC_HEIGHT", Some("1080")),
|
|
("LESAVKA_UVC_FPS", Some("30")),
|
|
("LESAVKA_UVC_INTERVAL", None::<&str>),
|
|
],
|
|
|| {
|
|
let state = snapshot_from_env();
|
|
assert_eq!(
|
|
state.default_video_offset_us,
|
|
FACTORY_MJPEG_VIDEO_OFFSET_1920X1080_30_US
|
|
);
|
|
assert_eq!(
|
|
state.factory_video_offset_us,
|
|
FACTORY_MJPEG_VIDEO_OFFSET_1920X1080_30_US
|
|
);
|
|
assert_eq!(state.source, "factory");
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `mode_offset_map_overrides_stale_scalar_offset` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn mode_offset_map_overrides_stale_scalar_offset() {
|
|
temp_env::with_vars(
|
|
[
|
|
("LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US", None::<&str>),
|
|
("LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US", Some("170000")),
|
|
(
|
|
"LESAVKA_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US",
|
|
None::<&str>,
|
|
),
|
|
(
|
|
"LESAVKA_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US",
|
|
Some("1280x720@20=123456"),
|
|
),
|
|
("LESAVKA_UVC_WIDTH", Some("1280")),
|
|
("LESAVKA_UVC_HEIGHT", Some("720")),
|
|
("LESAVKA_UVC_FPS", Some("20")),
|
|
("LESAVKA_UVC_INTERVAL", None::<&str>),
|
|
],
|
|
|| {
|
|
let state = snapshot_from_env();
|
|
assert_eq!(state.default_video_offset_us, 123_456);
|
|
assert_eq!(state.source, "env");
|
|
assert_eq!(state.confidence, "configured");
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `stale_scalar_video_offset_falls_back_to_mode_factory` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn stale_scalar_video_offset_falls_back_to_mode_factory() {
|
|
temp_env::with_vars(
|
|
[
|
|
("LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US", None::<&str>),
|
|
("LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US", Some("170000")),
|
|
(
|
|
"LESAVKA_UPSTREAM_AUDIO_PLAYOUT_MODE_OFFSETS_US",
|
|
None::<&str>,
|
|
),
|
|
(
|
|
"LESAVKA_UPSTREAM_VIDEO_PLAYOUT_MODE_OFFSETS_US",
|
|
None::<&str>,
|
|
),
|
|
("LESAVKA_UVC_WIDTH", Some("1920")),
|
|
("LESAVKA_UVC_HEIGHT", Some("1080")),
|
|
("LESAVKA_UVC_FPS", Some("20")),
|
|
("LESAVKA_UVC_INTERVAL", None::<&str>),
|
|
],
|
|
|| {
|
|
let state = snapshot_from_env();
|
|
assert_eq!(
|
|
state.default_video_offset_us,
|
|
FACTORY_MJPEG_VIDEO_OFFSET_1920X1080_20_US
|
|
);
|
|
assert_eq!(state.source, "factory");
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `store_persists_manual_adjustments_and_updates_runtime` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn store_persists_manual_adjustments_and_updates_runtime() {
|
|
let file = NamedTempFile::new().expect("temp calibration file");
|
|
let path = file.path().to_string_lossy().to_string();
|
|
temp_env::with_var("LESAVKA_CALIBRATION_PATH", Some(path.as_str()), || {
|
|
let runtime = Arc::new(UpstreamMediaRuntime::new());
|
|
let store = CalibrationStore::load(runtime.clone());
|
|
let state = store
|
|
.apply(CalibrationRequest {
|
|
action: CalibrationAction::AdjustActive as i32,
|
|
audio_delta_us: -5_000,
|
|
video_delta_us: 0,
|
|
observed_delivery_skew_ms: 0.0,
|
|
observed_enqueue_skew_ms: 0.0,
|
|
note: String::new(),
|
|
})
|
|
.expect("manual adjust applies");
|
|
assert_eq!(state.active_audio_offset_us, -5_000);
|
|
assert_eq!(
|
|
runtime.playout_offsets(),
|
|
(FACTORY_MJPEG_VIDEO_OFFSET_US, -5_000)
|
|
);
|
|
let raw = std::fs::read_to_string(file.path()).expect("persisted calibration");
|
|
assert!(raw.contains("active_audio_offset_us=-5000"));
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn calibration_path_uses_default_for_blank_override() {
|
|
temp_env::with_var("LESAVKA_CALIBRATION_PATH", Some(""), || {
|
|
assert_eq!(
|
|
calibration_path(),
|
|
PathBuf::from("/var/lib/lesavka/calibration.toml")
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `snapshot_from_env_uses_configured_offsets_and_clamps_extremes` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn snapshot_from_env_uses_configured_offsets_and_clamps_extremes() {
|
|
temp_env::with_vars(
|
|
[
|
|
("LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US", Some("-9999999")),
|
|
("LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US", Some("12345")),
|
|
],
|
|
|| {
|
|
let state = snapshot_from_env();
|
|
assert_eq!(state.default_audio_offset_us, -OFFSET_LIMIT_US);
|
|
assert_eq!(state.default_video_offset_us, 12_345);
|
|
assert_eq!(state.source, "env");
|
|
assert_eq!(state.confidence, "configured");
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `parse_snapshot_falls_back_for_missing_and_malformed_fields` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn parse_snapshot_falls_back_for_missing_and_malformed_fields() {
|
|
temp_env::with_vars(
|
|
[
|
|
("LESAVKA_UPSTREAM_AUDIO_PLAYOUT_OFFSET_US", None::<&str>),
|
|
("LESAVKA_UPSTREAM_VIDEO_PLAYOUT_OFFSET_US", None::<&str>),
|
|
],
|
|
|| {
|
|
let state = parse_snapshot(
|
|
r#"
|
|
profile="mjpeg"
|
|
default_audio_offset_us=bad
|
|
default_video_offset_us=2500
|
|
active_audio_offset_us=-1600000
|
|
source="saved"
|
|
detail="loaded \"quoted\" value"
|
|
"#,
|
|
);
|
|
assert_eq!(state.default_audio_offset_us, FACTORY_MJPEG_AUDIO_OFFSET_US);
|
|
assert_eq!(state.default_video_offset_us, 2_500);
|
|
assert_eq!(state.active_audio_offset_us, -OFFSET_LIMIT_US);
|
|
assert_eq!(state.active_video_offset_us, FACTORY_MJPEG_VIDEO_OFFSET_US);
|
|
assert_eq!(state.source, "saved");
|
|
assert_eq!(state.confidence, FACTORY_CONFIDENCE);
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `load_migrates_untouched_legacy_factory_mjpeg_baseline` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn load_migrates_untouched_legacy_factory_mjpeg_baseline() {
|
|
let file = NamedTempFile::new().expect("temp calibration file");
|
|
std::fs::write(
|
|
file.path(),
|
|
r#"
|
|
profile="mjpeg"
|
|
default_audio_offset_us=-45000
|
|
default_video_offset_us=0
|
|
active_audio_offset_us=-45000
|
|
active_video_offset_us=0
|
|
source="env"
|
|
confidence="configured"
|
|
detail="loaded upstream A/V calibration defaults"
|
|
"#,
|
|
)
|
|
.expect("legacy calibration seed");
|
|
let path = file.path().to_string_lossy().to_string();
|
|
temp_env::with_var("LESAVKA_CALIBRATION_PATH", Some(path.as_str()), || {
|
|
let runtime = Arc::new(UpstreamMediaRuntime::new());
|
|
let store = CalibrationStore::load(runtime.clone());
|
|
let state = store.current();
|
|
assert_eq!(state.active_audio_offset_us, 0);
|
|
assert_eq!(state.default_audio_offset_us, 0);
|
|
assert_eq!(state.active_video_offset_us, FACTORY_MJPEG_VIDEO_OFFSET_US);
|
|
assert_eq!(state.default_video_offset_us, FACTORY_MJPEG_VIDEO_OFFSET_US);
|
|
assert_eq!(state.source, "factory");
|
|
assert_eq!(
|
|
runtime.playout_offsets(),
|
|
(FACTORY_MJPEG_VIDEO_OFFSET_US, 0)
|
|
);
|
|
assert!(state.detail.contains("migrated legacy MJPEG"));
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `load_migrates_untouched_previous_factory_mjpeg_baseline` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn load_migrates_untouched_previous_factory_mjpeg_baseline() {
|
|
let file = NamedTempFile::new().expect("temp calibration file");
|
|
std::fs::write(
|
|
file.path(),
|
|
r#"
|
|
profile="mjpeg"
|
|
default_audio_offset_us=720000
|
|
default_video_offset_us=0
|
|
active_audio_offset_us=720000
|
|
active_video_offset_us=0
|
|
source="env"
|
|
confidence="configured"
|
|
detail="loaded upstream A/V calibration defaults"
|
|
"#,
|
|
)
|
|
.expect("previous calibration seed");
|
|
let path = file.path().to_string_lossy().to_string();
|
|
temp_env::with_var("LESAVKA_CALIBRATION_PATH", Some(path.as_str()), || {
|
|
let runtime = Arc::new(UpstreamMediaRuntime::new());
|
|
let store = CalibrationStore::load(runtime.clone());
|
|
let state = store.current();
|
|
assert_eq!(state.active_audio_offset_us, 0);
|
|
assert_eq!(state.default_audio_offset_us, 0);
|
|
assert_eq!(state.active_video_offset_us, FACTORY_MJPEG_VIDEO_OFFSET_US);
|
|
assert_eq!(state.default_video_offset_us, FACTORY_MJPEG_VIDEO_OFFSET_US);
|
|
assert_eq!(state.source, "factory");
|
|
assert_eq!(
|
|
runtime.playout_offsets(),
|
|
(FACTORY_MJPEG_VIDEO_OFFSET_US, 0)
|
|
);
|
|
assert!(state.detail.contains("to audio +0.0ms/video +135.1ms"));
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `load_migrates_overshot_video_factory_mjpeg_baseline` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn load_migrates_overshot_video_factory_mjpeg_baseline() {
|
|
let file = NamedTempFile::new().expect("temp calibration file");
|
|
std::fs::write(
|
|
file.path(),
|
|
r#"
|
|
profile="mjpeg"
|
|
default_audio_offset_us=0
|
|
default_video_offset_us=1090000
|
|
active_audio_offset_us=0
|
|
active_video_offset_us=1090000
|
|
source="factory"
|
|
confidence="factory"
|
|
detail="loaded upstream A/V calibration defaults"
|
|
"#,
|
|
)
|
|
.expect("overshot video calibration seed");
|
|
let path = file.path().to_string_lossy().to_string();
|
|
temp_env::with_var("LESAVKA_CALIBRATION_PATH", Some(path.as_str()), || {
|
|
let runtime = Arc::new(UpstreamMediaRuntime::new());
|
|
let store = CalibrationStore::load(runtime.clone());
|
|
let state = store.current();
|
|
assert_eq!(state.active_audio_offset_us, 0);
|
|
assert_eq!(state.default_audio_offset_us, 0);
|
|
assert_eq!(state.active_video_offset_us, FACTORY_MJPEG_VIDEO_OFFSET_US);
|
|
assert_eq!(state.default_video_offset_us, FACTORY_MJPEG_VIDEO_OFFSET_US);
|
|
assert_eq!(state.source, "factory");
|
|
assert_eq!(
|
|
runtime.playout_offsets(),
|
|
(FACTORY_MJPEG_VIDEO_OFFSET_US, 0)
|
|
);
|
|
assert!(state.detail.contains("from audio +0.0ms/video +1090.0ms"));
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `load_migrates_browser_video_factory_mjpeg_baseline` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn load_migrates_browser_video_factory_mjpeg_baseline() {
|
|
let file = NamedTempFile::new().expect("temp calibration file");
|
|
std::fs::write(
|
|
file.path(),
|
|
r#"
|
|
profile="mjpeg"
|
|
default_audio_offset_us=0
|
|
default_video_offset_us=130000
|
|
active_audio_offset_us=0
|
|
active_video_offset_us=130000
|
|
source="factory"
|
|
confidence="factory"
|
|
detail="loaded upstream A/V calibration defaults"
|
|
"#,
|
|
)
|
|
.expect("browser video calibration seed");
|
|
let path = file.path().to_string_lossy().to_string();
|
|
temp_env::with_var("LESAVKA_CALIBRATION_PATH", Some(path.as_str()), || {
|
|
let runtime = Arc::new(UpstreamMediaRuntime::new());
|
|
let store = CalibrationStore::load(runtime.clone());
|
|
let state = store.current();
|
|
assert_eq!(state.active_audio_offset_us, 0);
|
|
assert_eq!(state.default_audio_offset_us, 0);
|
|
assert_eq!(state.active_video_offset_us, FACTORY_MJPEG_VIDEO_OFFSET_US);
|
|
assert_eq!(state.default_video_offset_us, FACTORY_MJPEG_VIDEO_OFFSET_US);
|
|
assert_eq!(state.source, "factory");
|
|
assert_eq!(
|
|
runtime.playout_offsets(),
|
|
(FACTORY_MJPEG_VIDEO_OFFSET_US, 0)
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `load_migrates_delayed_video_factory_mjpeg_baseline` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn load_migrates_delayed_video_factory_mjpeg_baseline() {
|
|
let file = NamedTempFile::new().expect("temp calibration file");
|
|
std::fs::write(
|
|
file.path(),
|
|
r#"
|
|
profile="mjpeg"
|
|
default_audio_offset_us=0
|
|
default_video_offset_us=350000
|
|
active_audio_offset_us=0
|
|
active_video_offset_us=350000
|
|
source="factory"
|
|
confidence="factory"
|
|
detail="loaded upstream A/V calibration defaults"
|
|
"#,
|
|
)
|
|
.expect("delayed video calibration seed");
|
|
let path = file.path().to_string_lossy().to_string();
|
|
temp_env::with_var("LESAVKA_CALIBRATION_PATH", Some(path.as_str()), || {
|
|
let runtime = Arc::new(UpstreamMediaRuntime::new());
|
|
let store = CalibrationStore::load(runtime.clone());
|
|
let state = store.current();
|
|
assert_eq!(state.active_audio_offset_us, 0);
|
|
assert_eq!(state.default_audio_offset_us, 0);
|
|
assert_eq!(state.active_video_offset_us, FACTORY_MJPEG_VIDEO_OFFSET_US);
|
|
assert_eq!(state.default_video_offset_us, FACTORY_MJPEG_VIDEO_OFFSET_US);
|
|
assert_eq!(state.source, "factory");
|
|
assert_eq!(
|
|
runtime.playout_offsets(),
|
|
(FACTORY_MJPEG_VIDEO_OFFSET_US, 0)
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `load_migrates_early_zero_video_factory_mjpeg_baseline` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn load_migrates_early_zero_video_factory_mjpeg_baseline() {
|
|
let file = NamedTempFile::new().expect("temp calibration file");
|
|
std::fs::write(
|
|
file.path(),
|
|
r#"
|
|
profile="mjpeg"
|
|
default_audio_offset_us=0
|
|
default_video_offset_us=0
|
|
active_audio_offset_us=0
|
|
active_video_offset_us=0
|
|
source="factory"
|
|
confidence="factory"
|
|
detail="loaded upstream A/V calibration defaults"
|
|
"#,
|
|
)
|
|
.expect("early zero calibration seed");
|
|
let path = file.path().to_string_lossy().to_string();
|
|
temp_env::with_var("LESAVKA_CALIBRATION_PATH", Some(path.as_str()), || {
|
|
let runtime = Arc::new(UpstreamMediaRuntime::new());
|
|
let store = CalibrationStore::load(runtime.clone());
|
|
let state = store.current();
|
|
assert_eq!(state.active_audio_offset_us, 0);
|
|
assert_eq!(state.default_audio_offset_us, 0);
|
|
assert_eq!(state.active_video_offset_us, FACTORY_MJPEG_VIDEO_OFFSET_US);
|
|
assert_eq!(state.default_video_offset_us, FACTORY_MJPEG_VIDEO_OFFSET_US);
|
|
assert_eq!(state.source, "factory");
|
|
assert_eq!(
|
|
runtime.playout_offsets(),
|
|
(FACTORY_MJPEG_VIDEO_OFFSET_US, 0)
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `load_keeps_manual_legacy_sized_calibration` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn load_keeps_manual_legacy_sized_calibration() {
|
|
let file = NamedTempFile::new().expect("temp calibration file");
|
|
std::fs::write(
|
|
file.path(),
|
|
r#"
|
|
profile="mjpeg"
|
|
default_audio_offset_us=-45000
|
|
default_video_offset_us=0
|
|
active_audio_offset_us=-45000
|
|
active_video_offset_us=0
|
|
source="manual"
|
|
confidence="manual"
|
|
detail="operator-set"
|
|
"#,
|
|
)
|
|
.expect("manual calibration seed");
|
|
let path = file.path().to_string_lossy().to_string();
|
|
temp_env::with_var("LESAVKA_CALIBRATION_PATH", Some(path.as_str()), || {
|
|
let runtime = Arc::new(UpstreamMediaRuntime::new());
|
|
let store = CalibrationStore::load(runtime.clone());
|
|
let state = store.current();
|
|
assert_eq!(state.active_audio_offset_us, -45_000);
|
|
assert_eq!(state.active_video_offset_us, 0);
|
|
assert_eq!(state.source, "manual");
|
|
assert_eq!(runtime.playout_offsets(), (0, -45_000));
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `store_applies_all_calibration_actions_and_persists_defaults` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn store_applies_all_calibration_actions_and_persists_defaults() {
|
|
let dir = tempfile::tempdir().expect("calibration dir");
|
|
let path = dir.path().join("calibration.toml");
|
|
let path_string = path.to_string_lossy().to_string();
|
|
temp_env::with_var(
|
|
"LESAVKA_CALIBRATION_PATH",
|
|
Some(path_string.as_str()),
|
|
|| {
|
|
let runtime = Arc::new(UpstreamMediaRuntime::new());
|
|
let store = CalibrationStore::load(runtime.clone());
|
|
|
|
let blind = store
|
|
.apply(CalibrationRequest {
|
|
action: CalibrationAction::BlindEstimate as i32,
|
|
audio_delta_us: 5_000,
|
|
video_delta_us: -2_000,
|
|
observed_delivery_skew_ms: 44.0,
|
|
observed_enqueue_skew_ms: 3.5,
|
|
note: String::new(),
|
|
})
|
|
.expect("blind estimate");
|
|
assert_eq!(blind.source, "blind");
|
|
assert!(blind.detail.contains("delivery skew 44.0ms"));
|
|
assert_eq!(
|
|
runtime.playout_offsets(),
|
|
(FACTORY_MJPEG_VIDEO_OFFSET_US - 2_000, 5_000)
|
|
);
|
|
|
|
let manual = store
|
|
.apply(CalibrationRequest {
|
|
action: CalibrationAction::AdjustActive as i32,
|
|
audio_delta_us: 1_999_999,
|
|
video_delta_us: 0,
|
|
observed_delivery_skew_ms: 0.0,
|
|
observed_enqueue_skew_ms: 0.0,
|
|
note: String::new(),
|
|
})
|
|
.expect("manual clamp");
|
|
assert_eq!(manual.active_audio_offset_us, OFFSET_LIMIT_US);
|
|
|
|
let saved = store
|
|
.apply(CalibrationRequest {
|
|
action: CalibrationAction::SaveActiveAsDefault as i32,
|
|
..CalibrationRequest::default()
|
|
})
|
|
.expect("save default");
|
|
assert_eq!(saved.default_audio_offset_us, saved.active_audio_offset_us);
|
|
assert_eq!(saved.confidence, "measured");
|
|
|
|
let factory = store
|
|
.apply(CalibrationRequest {
|
|
action: CalibrationAction::RestoreFactory as i32,
|
|
..CalibrationRequest::default()
|
|
})
|
|
.expect("factory restore");
|
|
assert_eq!(
|
|
factory.active_audio_offset_us,
|
|
FACTORY_MJPEG_AUDIO_OFFSET_US
|
|
);
|
|
assert_eq!(
|
|
factory.active_video_offset_us,
|
|
FACTORY_MJPEG_VIDEO_OFFSET_US
|
|
);
|
|
assert_eq!(factory.source, "factory");
|
|
|
|
let restored = store
|
|
.apply(CalibrationRequest {
|
|
action: CalibrationAction::RestoreDefault as i32,
|
|
..CalibrationRequest::default()
|
|
})
|
|
.expect("default restore");
|
|
assert_eq!(
|
|
restored.active_audio_offset_us,
|
|
restored.default_audio_offset_us
|
|
);
|
|
assert_eq!(
|
|
store.current().active_audio_offset_us,
|
|
restored.active_audio_offset_us
|
|
);
|
|
|
|
let no_op = store
|
|
.apply(CalibrationRequest::default())
|
|
.expect("unspecified action is ok");
|
|
assert_eq!(
|
|
no_op.active_audio_offset_us,
|
|
restored.active_audio_offset_us
|
|
);
|
|
|
|
let raw = std::fs::read_to_string(&path).expect("persisted actions");
|
|
assert!(raw.contains("confidence="));
|
|
assert!(raw.contains("detail="));
|
|
assert_eq!(escape_value("a\\b\"c"), "a\\\\b\\\"c");
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
/// Keeps `transient_blind_estimate_updates_runtime_without_persisting_active_file_state` explicit because it sits on calibration state, where persisted and factory offsets must stay auditable.
|
|
/// Inputs are the typed parameters; output is the return value or side effect.
|
|
fn transient_blind_estimate_updates_runtime_without_persisting_active_file_state() {
|
|
let dir = tempfile::tempdir().expect("calibration dir");
|
|
let path = dir.path().join("calibration.toml");
|
|
let path_string = path.to_string_lossy().to_string();
|
|
temp_env::with_var(
|
|
"LESAVKA_CALIBRATION_PATH",
|
|
Some(path_string.as_str()),
|
|
|| {
|
|
let runtime = Arc::new(UpstreamMediaRuntime::new());
|
|
let store = CalibrationStore::load(runtime.clone());
|
|
let before_raw = std::fs::read_to_string(&path).ok();
|
|
|
|
let state = store.apply_transient_blind_estimate(
|
|
0,
|
|
-12_000,
|
|
48.0,
|
|
7.0,
|
|
"runtime blind healer nudge",
|
|
);
|
|
|
|
assert_eq!(state.source, "blind");
|
|
assert_eq!(state.confidence, "runtime-estimated");
|
|
assert_eq!(
|
|
runtime.playout_offsets(),
|
|
(FACTORY_MJPEG_VIDEO_OFFSET_US - 12_000, 0)
|
|
);
|
|
assert_eq!(std::fs::read_to_string(&path).ok(), before_raw);
|
|
},
|
|
);
|
|
}
|