ci(lesavka): keep video support source under loc gate
This commit is contained in:
parent
d73d86f0da
commit
10d390d353
@ -263,6 +263,7 @@ from `LESAVKA_CLIENT_PKI_SSH_SOURCE` over SSH. Runtime clients require the insta
|
|||||||
| `LESAVKA_TLS_KEY` | server TLS private-key path override |
|
| `LESAVKA_TLS_KEY` | server TLS private-key path override |
|
||||||
| `LESAVKA_TLS_SAN` | server installer extra certificate SAN list for additional relay hostnames/IPs |
|
| `LESAVKA_TLS_SAN` | server installer extra certificate SAN list for additional relay hostnames/IPs |
|
||||||
| `LESAVKA_SYNTHETIC_PKI_DIR` | server installer destination for the SSH user's local synthetic-probe mTLS identity; defaults to `~/.config/lesavka/pki` |
|
| `LESAVKA_SYNTHETIC_PKI_DIR` | server installer destination for the SSH user's local synthetic-probe mTLS identity; defaults to `~/.config/lesavka/pki` |
|
||||||
|
| `LESAVKA_SYNTHETIC_TEST_PATH` | test-only synthetic-uplink path override used by coverage contracts; not runtime operator config |
|
||||||
| `LESAVKA_UAC_BUFFER_TIME_US` | server audio sink latency override |
|
| `LESAVKA_UAC_BUFFER_TIME_US` | server audio sink latency override |
|
||||||
| `LESAVKA_UAC_COMPENSATION_US` | server audio sink latency override |
|
| `LESAVKA_UAC_COMPENSATION_US` | server audio sink latency override |
|
||||||
| `LESAVKA_UAC_DEV` | server hardware/device override |
|
| `LESAVKA_UAC_DEV` | server hardware/device override |
|
||||||
|
|||||||
@ -338,171 +338,5 @@ pub fn reserve_local_pts(counter: &AtomicU64, preferred_pts_us: u64, frame_step_
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
#[path = "video_support/tests.rs"]
|
||||||
use super::{
|
mod tests;
|
||||||
SOFTWARE_VIDEO_FALLBACK_ENV, adjust_effective_fps, contains_hevc_irap, contains_idr,
|
|
||||||
default_eye_fps, env_u32, env_usize, is_hardware_h264_decoder, is_hardware_hevc_decoder,
|
|
||||||
next_local_pts, pick_h264_decoder, pick_hevc_decoder, require_h264_decoder,
|
|
||||||
require_hevc_decoder, reserve_local_pts, should_send_frame,
|
|
||||||
software_video_fallback_allowed,
|
|
||||||
};
|
|
||||||
use serial_test::serial;
|
|
||||||
use std::sync::atomic::AtomicU64;
|
|
||||||
use temp_env::with_var;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn default_eye_fps_tracks_bitrate_tiers() {
|
|
||||||
assert_eq!(default_eye_fps(0), 25);
|
|
||||||
assert_eq!(default_eye_fps(2_000), 15);
|
|
||||||
assert_eq!(default_eye_fps(3_000), 20);
|
|
||||||
assert_eq!(default_eye_fps(8_000), 25);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn contains_idr_finds_annex_b_keyframes() {
|
|
||||||
let sample = [0, 0, 0, 1, 0x65, 0x88, 0x99];
|
|
||||||
assert!(contains_idr(&sample));
|
|
||||||
assert!(contains_idr(&[0, 0, 1, 0x65, 0x88]));
|
|
||||||
assert!(!contains_idr(&[0, 0, 0, 1, 0x41, 0x99]));
|
|
||||||
assert!(!contains_idr(&[0, 0, 2, 0x65, 0x88]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn contains_hevc_irap_finds_annex_b_recovery_frames() {
|
|
||||||
assert!(contains_hevc_irap(&[0, 0, 0, 1, 0x26, 0x01, 0xaa]));
|
|
||||||
assert!(contains_hevc_irap(&[0, 0, 1, 0x28, 0x01, 0xaa]));
|
|
||||||
assert!(!contains_hevc_irap(&[0, 0, 0, 1, 0x02, 0x01, 0xaa]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn adjust_effective_fps_reacts_to_drop_windows() {
|
|
||||||
assert_eq!(adjust_effective_fps(20, 12, 25, 5, 10), 17);
|
|
||||||
assert_eq!(adjust_effective_fps(20, 12, 25, 0, 20), 21);
|
|
||||||
assert_eq!(adjust_effective_fps(12, 12, 25, 10, 10), 12);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_send_frame_enforces_interval() {
|
|
||||||
assert!(should_send_frame(0, 10, 25));
|
|
||||||
assert!(!should_send_frame(40_000, 50_000, 25));
|
|
||||||
assert!(should_send_frame(40_000, 90_000, 25));
|
|
||||||
assert!(!should_send_frame(40_000, 40_001, 0));
|
|
||||||
assert!(should_send_frame(40_000, 1_040_000, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn next_local_pts_monotonically_advances() {
|
|
||||||
let counter = AtomicU64::new(0);
|
|
||||||
assert_eq!(next_local_pts(&counter, 40_000), 0);
|
|
||||||
assert_eq!(next_local_pts(&counter, 40_000), 40_000);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn reserve_local_pts_prefers_preferred_value_but_stays_monotonic() {
|
|
||||||
let counter = AtomicU64::new(0);
|
|
||||||
assert_eq!(reserve_local_pts(&counter, 0, 40_000), 0);
|
|
||||||
assert_eq!(reserve_local_pts(&counter, 10_000, 40_000), 40_000);
|
|
||||||
assert_eq!(reserve_local_pts(&counter, 120_000, 40_000), 120_000);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn env_helpers_parse_values_and_fallbacks() {
|
|
||||||
with_var("LESAVKA_TEST_U32", Some("42"), || {
|
|
||||||
assert_eq!(env_u32("LESAVKA_TEST_U32", 7), 42);
|
|
||||||
});
|
|
||||||
with_var("LESAVKA_TEST_U32", Some("oops"), || {
|
|
||||||
assert_eq!(env_u32("LESAVKA_TEST_U32", 7), 7);
|
|
||||||
});
|
|
||||||
with_var("LESAVKA_TEST_USIZE", Some("128"), || {
|
|
||||||
assert_eq!(env_usize("LESAVKA_TEST_USIZE", 64), 128);
|
|
||||||
});
|
|
||||||
with_var("LESAVKA_TEST_USIZE", None::<&str>, || {
|
|
||||||
assert_eq!(env_usize("LESAVKA_TEST_USIZE", 64), 64);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn software_video_fallback_requires_clear_operator_opt_in() {
|
|
||||||
for disabled in [
|
|
||||||
None,
|
|
||||||
Some(""),
|
|
||||||
Some("0"),
|
|
||||||
Some("false"),
|
|
||||||
Some("no"),
|
|
||||||
Some("off"),
|
|
||||||
] {
|
|
||||||
with_var(SOFTWARE_VIDEO_FALLBACK_ENV, disabled, || {
|
|
||||||
assert!(!software_video_fallback_allowed());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for enabled in ["1", "true", "yes", "on", "lab"] {
|
|
||||||
with_var(SOFTWARE_VIDEO_FALLBACK_ENV, Some(enabled), || {
|
|
||||||
assert!(software_video_fallback_allowed());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn hardware_decoder_classification_keeps_software_fallbacks_out() {
|
|
||||||
for name in [
|
|
||||||
"v4l2h264dec",
|
|
||||||
"v4l2slh264dec",
|
|
||||||
"omxh264dec",
|
|
||||||
"vulkanh264dec",
|
|
||||||
] {
|
|
||||||
assert!(is_hardware_h264_decoder(name));
|
|
||||||
}
|
|
||||||
assert!(!is_hardware_h264_decoder("avdec_h264"));
|
|
||||||
|
|
||||||
for name in [
|
|
||||||
"v4l2slh265dec",
|
|
||||||
"v4l2h265dec",
|
|
||||||
"vulkanh265dec",
|
|
||||||
"nvh265dec",
|
|
||||||
"nvh265sldec",
|
|
||||||
] {
|
|
||||||
assert!(is_hardware_hevc_decoder(name));
|
|
||||||
}
|
|
||||||
assert!(!is_hardware_hevc_decoder("libde265dec"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[serial]
|
|
||||||
fn explicit_bad_decoder_overrides_fail_loudly_without_registry_assumptions() {
|
|
||||||
gstreamer::init().expect("initialize gstreamer registry for decoder lookup");
|
|
||||||
|
|
||||||
with_var(
|
|
||||||
"LESAVKA_H264_DECODER",
|
|
||||||
Some("definitely-not-a-decoder"),
|
|
||||||
|| {
|
|
||||||
let err = require_h264_decoder().expect_err("bogus H.264 decoder should fail");
|
|
||||||
assert!(
|
|
||||||
err.to_string().contains("not buildable"),
|
|
||||||
"unexpected H.264 override error: {err:#}"
|
|
||||||
);
|
|
||||||
assert_eq!(pick_h264_decoder(), "missing-hardware-h264dec");
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
with_var(
|
|
||||||
"LESAVKA_HEVC_DECODER",
|
|
||||||
Some("definitely-not-a-decoder"),
|
|
||||||
|| {
|
|
||||||
let err = require_hevc_decoder().expect_err("bogus HEVC decoder should fail");
|
|
||||||
assert!(
|
|
||||||
err.to_string().contains("not buildable"),
|
|
||||||
"unexpected HEVC override error: {err:#}"
|
|
||||||
);
|
|
||||||
assert_eq!(pick_hevc_decoder(), "missing-hardware-hevcdec");
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn adjust_effective_fps_keeps_current_rate_when_no_samples() {
|
|
||||||
assert_eq!(adjust_effective_fps(18, 12, 25, 0, 0), 18);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
165
server/src/video_support/tests.rs
Normal file
165
server/src/video_support/tests.rs
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
use super::{
|
||||||
|
SOFTWARE_VIDEO_FALLBACK_ENV, adjust_effective_fps, contains_hevc_irap, contains_idr,
|
||||||
|
default_eye_fps, env_u32, env_usize, is_hardware_h264_decoder, is_hardware_hevc_decoder,
|
||||||
|
next_local_pts, pick_h264_decoder, pick_hevc_decoder, require_h264_decoder,
|
||||||
|
require_hevc_decoder, reserve_local_pts, should_send_frame, software_video_fallback_allowed,
|
||||||
|
};
|
||||||
|
use serial_test::serial;
|
||||||
|
use std::sync::atomic::AtomicU64;
|
||||||
|
use temp_env::with_var;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn default_eye_fps_tracks_bitrate_tiers() {
|
||||||
|
assert_eq!(default_eye_fps(0), 25);
|
||||||
|
assert_eq!(default_eye_fps(2_000), 15);
|
||||||
|
assert_eq!(default_eye_fps(3_000), 20);
|
||||||
|
assert_eq!(default_eye_fps(8_000), 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn contains_idr_finds_annex_b_keyframes() {
|
||||||
|
let sample = [0, 0, 0, 1, 0x65, 0x88, 0x99];
|
||||||
|
assert!(contains_idr(&sample));
|
||||||
|
assert!(contains_idr(&[0, 0, 1, 0x65, 0x88]));
|
||||||
|
assert!(!contains_idr(&[0, 0, 0, 1, 0x41, 0x99]));
|
||||||
|
assert!(!contains_idr(&[0, 0, 2, 0x65, 0x88]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn contains_hevc_irap_finds_annex_b_recovery_frames() {
|
||||||
|
assert!(contains_hevc_irap(&[0, 0, 0, 1, 0x26, 0x01, 0xaa]));
|
||||||
|
assert!(contains_hevc_irap(&[0, 0, 1, 0x28, 0x01, 0xaa]));
|
||||||
|
assert!(!contains_hevc_irap(&[0, 0, 0, 1, 0x02, 0x01, 0xaa]));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn adjust_effective_fps_reacts_to_drop_windows() {
|
||||||
|
assert_eq!(adjust_effective_fps(20, 12, 25, 5, 10), 17);
|
||||||
|
assert_eq!(adjust_effective_fps(20, 12, 25, 0, 20), 21);
|
||||||
|
assert_eq!(adjust_effective_fps(12, 12, 25, 10, 10), 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_send_frame_enforces_interval() {
|
||||||
|
assert!(should_send_frame(0, 10, 25));
|
||||||
|
assert!(!should_send_frame(40_000, 50_000, 25));
|
||||||
|
assert!(should_send_frame(40_000, 90_000, 25));
|
||||||
|
assert!(!should_send_frame(40_000, 40_001, 0));
|
||||||
|
assert!(should_send_frame(40_000, 1_040_000, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn next_local_pts_monotonically_advances() {
|
||||||
|
let counter = AtomicU64::new(0);
|
||||||
|
assert_eq!(next_local_pts(&counter, 40_000), 0);
|
||||||
|
assert_eq!(next_local_pts(&counter, 40_000), 40_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reserve_local_pts_prefers_preferred_value_but_stays_monotonic() {
|
||||||
|
let counter = AtomicU64::new(0);
|
||||||
|
assert_eq!(reserve_local_pts(&counter, 0, 40_000), 0);
|
||||||
|
assert_eq!(reserve_local_pts(&counter, 10_000, 40_000), 40_000);
|
||||||
|
assert_eq!(reserve_local_pts(&counter, 120_000, 40_000), 120_000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn env_helpers_parse_values_and_fallbacks() {
|
||||||
|
with_var("LESAVKA_TEST_U32", Some("42"), || {
|
||||||
|
assert_eq!(env_u32("LESAVKA_TEST_U32", 7), 42);
|
||||||
|
});
|
||||||
|
with_var("LESAVKA_TEST_U32", Some("oops"), || {
|
||||||
|
assert_eq!(env_u32("LESAVKA_TEST_U32", 7), 7);
|
||||||
|
});
|
||||||
|
with_var("LESAVKA_TEST_USIZE", Some("128"), || {
|
||||||
|
assert_eq!(env_usize("LESAVKA_TEST_USIZE", 64), 128);
|
||||||
|
});
|
||||||
|
with_var("LESAVKA_TEST_USIZE", None::<&str>, || {
|
||||||
|
assert_eq!(env_usize("LESAVKA_TEST_USIZE", 64), 64);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn software_video_fallback_requires_clear_operator_opt_in() {
|
||||||
|
for disabled in [
|
||||||
|
None,
|
||||||
|
Some(""),
|
||||||
|
Some("0"),
|
||||||
|
Some("false"),
|
||||||
|
Some("no"),
|
||||||
|
Some("off"),
|
||||||
|
] {
|
||||||
|
with_var(SOFTWARE_VIDEO_FALLBACK_ENV, disabled, || {
|
||||||
|
assert!(!software_video_fallback_allowed());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for enabled in ["1", "true", "yes", "on", "lab"] {
|
||||||
|
with_var(SOFTWARE_VIDEO_FALLBACK_ENV, Some(enabled), || {
|
||||||
|
assert!(software_video_fallback_allowed());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hardware_decoder_classification_keeps_software_fallbacks_out() {
|
||||||
|
for name in [
|
||||||
|
"v4l2h264dec",
|
||||||
|
"v4l2slh264dec",
|
||||||
|
"omxh264dec",
|
||||||
|
"vulkanh264dec",
|
||||||
|
] {
|
||||||
|
assert!(is_hardware_h264_decoder(name));
|
||||||
|
}
|
||||||
|
assert!(!is_hardware_h264_decoder("avdec_h264"));
|
||||||
|
|
||||||
|
for name in [
|
||||||
|
"v4l2slh265dec",
|
||||||
|
"v4l2h265dec",
|
||||||
|
"vulkanh265dec",
|
||||||
|
"nvh265dec",
|
||||||
|
"nvh265sldec",
|
||||||
|
] {
|
||||||
|
assert!(is_hardware_hevc_decoder(name));
|
||||||
|
}
|
||||||
|
assert!(!is_hardware_hevc_decoder("libde265dec"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[serial]
|
||||||
|
fn explicit_bad_decoder_overrides_fail_loudly_without_registry_assumptions() {
|
||||||
|
gstreamer::init().expect("initialize gstreamer registry for decoder lookup");
|
||||||
|
|
||||||
|
with_var(
|
||||||
|
"LESAVKA_H264_DECODER",
|
||||||
|
Some("definitely-not-a-decoder"),
|
||||||
|
|| {
|
||||||
|
let err = require_h264_decoder().expect_err("bogus H.264 decoder should fail");
|
||||||
|
assert!(
|
||||||
|
err.to_string().contains("not buildable"),
|
||||||
|
"unexpected H.264 override error: {err:#}"
|
||||||
|
);
|
||||||
|
assert_eq!(pick_h264_decoder(), "missing-hardware-h264dec");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
with_var(
|
||||||
|
"LESAVKA_HEVC_DECODER",
|
||||||
|
Some("definitely-not-a-decoder"),
|
||||||
|
|| {
|
||||||
|
let err = require_hevc_decoder().expect_err("bogus HEVC decoder should fail");
|
||||||
|
assert!(
|
||||||
|
err.to_string().contains("not buildable"),
|
||||||
|
"unexpected HEVC override error: {err:#}"
|
||||||
|
);
|
||||||
|
assert_eq!(pick_hevc_decoder(), "missing-hardware-hevcdec");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn adjust_effective_fps_keeps_current_rate_when_no_samples() {
|
||||||
|
assert_eq!(adjust_effective_fps(18, 12, 25, 0, 0), 18);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user