use super::{
lane::{
MAX_CONSECUTIVE_REMOTE_FAILURES, REMOTE_RETRY_BACKOFF_INITIAL, REMOTE_RETRY_BACKOFF_MAX,
pause_remote_lane, remote_retry_backoff,
},
run_batch::{FIRST_EVENT_TIMEOUT, LIVENESS_TIMEOUT, liveness_deadline, transfer_allowance},
scene_state::scene_state_from_snapshot,
};
use crate::bridge::export_thread::{sample_cursor::SampleCursor, scene_snapshot::SceneSnapshot};
use indicatrix::{geometry::cuts::StandardGemCuts, optics::materials::GemMaterial};
use std::{
sync::atomic::AtomicBool,
time::{Duration, Instant},
};
#[test]
fn scene_state_from_snapshot_carries_the_snapshots_own_bounce_cap_not_a_viewport_default() {
let snapshot = SceneSnapshot {
yaw: 0.6,
pitch: 0.45,
distance: 2.4,
light_yaw: 0.85,
light_pitch: 0.95,
material: GemMaterial::diamond(),
lighting_preset: indicatrix::optics::raytracer::LightingPreset::RingLights,
max_bounces: 64, exposure: 1.0,
backdrop: 0.0,
active_planes: StandardGemCuts::standard_round_brilliant(),
facet_finishes: Vec::new(),
env_map: None,
};
let state = scene_state_from_snapshot(&snapshot, 1920, 1080, snapshot.yaw, snapshot.pitch);
assert_eq!(
state.max_bounces, 64,
"the remote RenderRequest's scene must carry the export's OWN bounce cap"
);
}
#[test]
fn scene_state_from_snapshot_uses_the_explicit_pose_override_not_the_snapshots_own_yaw_pitch() {
let snapshot = SceneSnapshot {
yaw: 0.6,
pitch: 0.45,
distance: 2.4,
light_yaw: 0.85,
light_pitch: 0.95,
material: GemMaterial::diamond(),
lighting_preset: indicatrix::optics::raytracer::LightingPreset::RingLights,
max_bounces: 12,
exposure: 1.0,
backdrop: 0.0,
active_planes: StandardGemCuts::standard_round_brilliant(),
facet_finishes: Vec::new(),
env_map: None,
};
let state = scene_state_from_snapshot(&snapshot, 1920, 1080, 1.23, -0.45);
assert_eq!(
state.yaw, 1.23,
"the override's yaw must win over the snapshot's own"
);
assert_eq!(
state.pitch, -0.45,
"the override's pitch must win over the snapshot's own"
);
assert_eq!(
state.distance, snapshot.distance,
"distance is not swept per frame, so it still comes from the snapshot"
);
}
#[test]
fn liveness_deadline_grants_the_first_event_grace_before_any_update_has_arrived() {
assert_eq!(
liveness_deadline(false, 0),
FIRST_EVENT_TIMEOUT,
"the wait for a dispatch's very first RemoteUpdate (even Connected) must \
use the longer grace, not the steady-state deadline"
);
}
#[test]
fn liveness_deadline_switches_to_the_tighter_steady_state_timeout_once_seen() {
assert_eq!(
liveness_deadline(true, 0),
LIVENESS_TIMEOUT,
"once a dispatch has produced at least one update, every wait after it must \
use the steady-state liveness timeout, not the first-event grace"
);
}
#[test]
fn first_event_timeout_is_strictly_longer_than_liveness_timeout() {
assert!(FIRST_EVENT_TIMEOUT > LIVENESS_TIMEOUT);
}
#[test]
fn liveness_deadline_budgets_a_whole_frame_transfer_on_a_slow_link() {
let bytes_4k = 3840 * 2160 * indicatrix_net::radiance::BYTES_PER_PIXEL as u64;
let bytes_1080p = 1920 * 1080 * indicatrix_net::radiance::BYTES_PER_PIXEL as u64;
let at_4k = liveness_deadline(true, bytes_4k);
let at_1080p = liveness_deadline(true, bytes_1080p);
assert!(at_4k >= LIVENESS_TIMEOUT + Duration::from_secs(20));
assert!(at_4k > at_1080p);
assert_eq!(transfer_allowance(0), Duration::ZERO);
}
#[test]
fn remote_retry_backoff_doubles_from_the_threshold_and_caps() {
assert_eq!(
remote_retry_backoff(MAX_CONSECUTIVE_REMOTE_FAILURES),
REMOTE_RETRY_BACKOFF_INITIAL
);
assert_eq!(
remote_retry_backoff(MAX_CONSECUTIVE_REMOTE_FAILURES + 1),
REMOTE_RETRY_BACKOFF_INITIAL * 2
);
assert_eq!(remote_retry_backoff(50), REMOTE_RETRY_BACKOFF_MAX);
}
#[test]
fn pause_remote_lane_returns_early_once_the_shared_pool_is_dry() {
let cursor = SampleCursor::new(0, 4);
assert_eq!(cursor.claim(4), Some((0, 4)));
let cancel = AtomicBool::new(false);
let started = Instant::now();
assert!(!pause_remote_lane(
&cursor,
&cancel,
Duration::from_secs(30)
));
assert!(started.elapsed() < Duration::from_secs(5));
}