212 lines
7.2 KiB
Rust
212 lines
7.2 KiB
Rust
|
|
use super::{
|
||
|
|
DurationPacedSourcePtsRebaser, SourcePtsRebaser, capture_pts_us, packet_age,
|
||
|
|
upstream_source_lag_cap, upstream_source_lead_cap, upstream_timing_trace_enabled,
|
||
|
|
};
|
||
|
|
use serial_test::serial;
|
||
|
|
use std::time::Duration;
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
#[serial]
|
||
|
|
fn capture_pts_us_monotonically_advances() {
|
||
|
|
let first = capture_pts_us();
|
||
|
|
std::thread::sleep(Duration::from_millis(2));
|
||
|
|
let second = capture_pts_us();
|
||
|
|
assert!(second >= first);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
#[serial]
|
||
|
|
fn packet_age_is_small_for_recent_packets() {
|
||
|
|
let pts = capture_pts_us();
|
||
|
|
std::thread::sleep(Duration::from_millis(2));
|
||
|
|
let age = packet_age(pts);
|
||
|
|
assert!(age >= Duration::from_millis(1));
|
||
|
|
assert!(age < Duration::from_secs(1));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
#[serial]
|
||
|
|
fn source_pts_rebaser_preserves_source_delta_on_shared_capture_clock() {
|
||
|
|
let rebased = SourcePtsRebaser::default();
|
||
|
|
let first = rebased.rebase_or_now(Some(1_000_000), 1);
|
||
|
|
let second = rebased.rebase_or_now(Some(1_033_333), 1);
|
||
|
|
|
||
|
|
assert!(first.used_source_pts);
|
||
|
|
assert_eq!(
|
||
|
|
second.packet_pts_us.saturating_sub(first.packet_pts_us),
|
||
|
|
33_333
|
||
|
|
);
|
||
|
|
assert_eq!(first.source_base_us, Some(1_000_000));
|
||
|
|
assert_eq!(second.source_base_us, Some(1_000_000));
|
||
|
|
assert_eq!(first.capture_base_us, second.capture_base_us);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
#[serial]
|
||
|
|
fn source_pts_rebaser_stays_monotonic_when_source_pts_repeat() {
|
||
|
|
let rebased = SourcePtsRebaser::default();
|
||
|
|
let first = rebased.rebase_or_now(Some(50_000), 1);
|
||
|
|
let second = rebased.rebase_or_now(Some(50_000), 1);
|
||
|
|
|
||
|
|
assert_eq!(second.packet_pts_us, first.packet_pts_us + 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
#[serial]
|
||
|
|
fn source_pts_rebaser_falls_back_to_capture_clock_without_source_pts() {
|
||
|
|
let rebased = SourcePtsRebaser::default();
|
||
|
|
let first = rebased.rebase_or_now(None, 1);
|
||
|
|
std::thread::sleep(Duration::from_millis(2));
|
||
|
|
let second = rebased.rebase_or_now(None, 1);
|
||
|
|
|
||
|
|
assert!(!first.used_source_pts);
|
||
|
|
assert!(!second.used_source_pts);
|
||
|
|
assert!(second.packet_pts_us > first.packet_pts_us);
|
||
|
|
assert!(!first.lag_clamped);
|
||
|
|
assert!(!second.lag_clamped);
|
||
|
|
assert!(!first.lead_clamped);
|
||
|
|
assert!(!second.lead_clamped);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
#[serial]
|
||
|
|
fn source_pts_rebaser_clamps_source_lag_when_it_falls_too_far_behind_now() {
|
||
|
|
let rebased = SourcePtsRebaser::default();
|
||
|
|
let _first = rebased.rebase_with_lag_cap(Some(1_000_000), 1, None);
|
||
|
|
std::thread::sleep(Duration::from_millis(8));
|
||
|
|
let second = rebased.rebase_with_lag_cap(Some(1_000_001), 1, Some(Duration::from_millis(2)));
|
||
|
|
|
||
|
|
assert!(second.used_source_pts);
|
||
|
|
assert!(second.lag_clamped);
|
||
|
|
assert!(second.capture_now_us >= second.packet_pts_us);
|
||
|
|
assert!(second.capture_now_us - second.packet_pts_us <= 2_500);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
#[serial]
|
||
|
|
fn source_pts_rebaser_clamps_source_lead_when_it_runs_too_far_ahead() {
|
||
|
|
temp_env::with_var("LESAVKA_UPSTREAM_SOURCE_LEAD_CAP_MS", Some("5"), || {
|
||
|
|
let rebased = SourcePtsRebaser::default();
|
||
|
|
let _first =
|
||
|
|
rebased.rebase_with_lag_cap(Some(1_000_000), 1, Some(Duration::from_millis(250)));
|
||
|
|
let second =
|
||
|
|
rebased.rebase_with_lag_cap(Some(2_000_000), 1, Some(Duration::from_millis(250)));
|
||
|
|
|
||
|
|
assert!(second.used_source_pts);
|
||
|
|
assert!(second.lead_clamped);
|
||
|
|
assert!(second.packet_pts_us >= second.capture_now_us);
|
||
|
|
assert!(second.packet_pts_us <= second.capture_now_us + 5_500);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
#[serial]
|
||
|
|
fn source_pts_rebasers_anchor_each_stream_to_its_own_first_packet_time() {
|
||
|
|
let microphone = SourcePtsRebaser::default();
|
||
|
|
let camera = SourcePtsRebaser::default();
|
||
|
|
|
||
|
|
let first_microphone = microphone.rebase_or_now(Some(80_000), 1);
|
||
|
|
std::thread::sleep(Duration::from_millis(5));
|
||
|
|
let first_camera = camera.rebase_or_now(Some(435_000), 1);
|
||
|
|
|
||
|
|
assert_ne!(
|
||
|
|
first_microphone.capture_base_us, first_camera.capture_base_us,
|
||
|
|
"independent camera/mic pipelines must not be forced onto the same first-packet timestamp"
|
||
|
|
);
|
||
|
|
assert!(
|
||
|
|
first_camera.packet_pts_us > first_microphone.packet_pts_us,
|
||
|
|
"a later-starting camera pipeline should keep that real wall-clock delay"
|
||
|
|
);
|
||
|
|
assert_eq!(first_microphone.source_base_us, Some(80_000));
|
||
|
|
assert_eq!(first_camera.source_base_us, Some(435_000));
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
#[serial]
|
||
|
|
fn upstream_timing_trace_flag_defaults_off_and_accepts_true_values() {
|
||
|
|
temp_env::with_var_unset("LESAVKA_UPSTREAM_TIMING_TRACE", || {
|
||
|
|
assert!(!upstream_timing_trace_enabled());
|
||
|
|
});
|
||
|
|
|
||
|
|
temp_env::with_var("LESAVKA_UPSTREAM_TIMING_TRACE", Some("1"), || {
|
||
|
|
assert!(upstream_timing_trace_enabled());
|
||
|
|
});
|
||
|
|
|
||
|
|
temp_env::with_var("LESAVKA_UPSTREAM_TIMING_TRACE", Some("false"), || {
|
||
|
|
assert!(!upstream_timing_trace_enabled());
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
#[serial]
|
||
|
|
fn upstream_source_lag_cap_defaults_and_accepts_override() {
|
||
|
|
temp_env::with_var_unset("LESAVKA_UPSTREAM_SOURCE_LAG_CAP_MS", || {
|
||
|
|
assert_eq!(upstream_source_lag_cap(), Duration::from_millis(250));
|
||
|
|
});
|
||
|
|
|
||
|
|
temp_env::with_var("LESAVKA_UPSTREAM_SOURCE_LAG_CAP_MS", Some("90"), || {
|
||
|
|
assert_eq!(upstream_source_lag_cap(), Duration::from_millis(90));
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
#[serial]
|
||
|
|
fn upstream_source_lead_cap_defaults_and_accepts_override() {
|
||
|
|
temp_env::with_var_unset("LESAVKA_UPSTREAM_SOURCE_LEAD_CAP_MS", || {
|
||
|
|
assert_eq!(upstream_source_lead_cap(), Duration::from_millis(80));
|
||
|
|
});
|
||
|
|
|
||
|
|
temp_env::with_var("LESAVKA_UPSTREAM_SOURCE_LEAD_CAP_MS", Some("35"), || {
|
||
|
|
assert_eq!(upstream_source_lead_cap(), Duration::from_millis(35));
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
#[serial]
|
||
|
|
fn duration_paced_rebaser_advances_by_packet_duration_when_source_pts_stretch() {
|
||
|
|
let rebased = DurationPacedSourcePtsRebaser::default();
|
||
|
|
let first = rebased.rebase_with_packet_duration(Some(0), 21_333, Duration::from_millis(250));
|
||
|
|
let second =
|
||
|
|
rebased.rebase_with_packet_duration(Some(52_666), 21_333, Duration::from_millis(250));
|
||
|
|
|
||
|
|
assert_eq!(
|
||
|
|
second.packet_pts_us.saturating_sub(first.packet_pts_us),
|
||
|
|
21_333
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
#[serial]
|
||
|
|
fn duration_paced_rebaser_clamps_when_duration_pacing_falls_stale() {
|
||
|
|
let rebased = DurationPacedSourcePtsRebaser::default();
|
||
|
|
let _first = rebased.rebase_with_packet_duration(Some(0), 10_000, Duration::from_millis(2));
|
||
|
|
std::thread::sleep(Duration::from_millis(8));
|
||
|
|
let second =
|
||
|
|
rebased.rebase_with_packet_duration(Some(10_000), 10_000, Duration::from_millis(2));
|
||
|
|
|
||
|
|
assert!(
|
||
|
|
second.packet_pts_us.saturating_add(2_500) >= second.capture_now_us,
|
||
|
|
"duration-paced packet pts should never trail live capture by more than the lag cap"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
#[test]
|
||
|
|
#[serial]
|
||
|
|
fn duration_paced_rebaser_clamps_when_duration_pacing_runs_future() {
|
||
|
|
temp_env::with_var("LESAVKA_UPSTREAM_SOURCE_LEAD_CAP_MS", Some("15"), || {
|
||
|
|
let rebased = DurationPacedSourcePtsRebaser::default();
|
||
|
|
let mut last =
|
||
|
|
rebased.rebase_with_packet_duration(Some(0), 50_000, Duration::from_millis(250));
|
||
|
|
for packet_index in 1..12 {
|
||
|
|
last = rebased.rebase_with_packet_duration(
|
||
|
|
Some(packet_index * 50_000),
|
||
|
|
50_000,
|
||
|
|
Duration::from_millis(250),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
assert!(last.lead_clamped);
|
||
|
|
assert!(last.packet_pts_us <= last.capture_now_us + 16_000);
|
||
|
|
});
|
||
|
|
}
|