ci(lesavka): harden safe gate tests
This commit is contained in:
parent
52cbf2311f
commit
ff9504d55e
@ -374,26 +374,6 @@ impl LauncherPreview {
|
||||
Some(feed.is_disabled())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn activate_surface_for_test(&self, monitor_id: usize, surface: PreviewSurface) {
|
||||
let feed = match surface {
|
||||
PreviewSurface::Inline => self
|
||||
.inline_feeds
|
||||
.lock()
|
||||
.ok()
|
||||
.and_then(|feeds| feeds.get(monitor_id).cloned()),
|
||||
PreviewSurface::Window => self
|
||||
.window_feeds
|
||||
.lock()
|
||||
.ok()
|
||||
.and_then(|feeds| feeds.get(monitor_id).cloned()),
|
||||
};
|
||||
if let Some(feed) = feed {
|
||||
feed.session_active.store(true, Ordering::Relaxed);
|
||||
feed.active_bindings.fetch_add(1, Ordering::AcqRel);
|
||||
}
|
||||
}
|
||||
|
||||
fn rebuild_feed(
|
||||
&self,
|
||||
feeds: &Arc<Mutex<[PreviewFeed; 2]>>,
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
use super::{
|
||||
DEFAULT_EYE_SOURCE_HEIGHT, DEFAULT_EYE_SOURCE_WIDTH, INLINE_PREVIEW_MAX_KBIT,
|
||||
INLINE_PREVIEW_REQUEST_FPS, INLINE_PREVIEW_REQUEST_HEIGHT, INLINE_PREVIEW_REQUEST_WIDTH,
|
||||
LauncherPreview, PREVIEW_HEIGHT, PREVIEW_WIDTH, PreviewSurface, PreviewTelemetry,
|
||||
LauncherPreview, PREVIEW_HEIGHT, PREVIEW_WIDTH, PreviewFeed, PreviewSurface, PreviewTelemetry,
|
||||
sanitize_preview_request,
|
||||
};
|
||||
use crate::launcher::state::{CaptureSizePreset, LauncherState};
|
||||
use anyhow::Context;
|
||||
use futures::stream;
|
||||
use lesavka_common::lesavka::relay_client::RelayClient;
|
||||
use lesavka_common::lesavka::relay_server::{Relay, RelayServer};
|
||||
use lesavka_common::lesavka::{MonitorRequest, VideoPacket};
|
||||
use serial_test::serial;
|
||||
@ -209,6 +211,85 @@ impl Relay for ProbeRelay {
|
||||
}
|
||||
}
|
||||
|
||||
fn request_preview_capture(
|
||||
rt: &tokio::runtime::Runtime,
|
||||
preview: &LauncherPreview,
|
||||
monitor_id: usize,
|
||||
surface: PreviewSurface,
|
||||
) {
|
||||
rt.block_on(request_capture_once(preview, monitor_id, surface))
|
||||
.expect("preview should issue a capture request");
|
||||
}
|
||||
|
||||
fn disabled_preview(server_addr: String) -> LauncherPreview {
|
||||
let server_addr = Arc::new(Mutex::new(server_addr));
|
||||
let log_sink = Arc::new(Mutex::new(None));
|
||||
let inline_feeds = Arc::new(Mutex::new([
|
||||
PreviewFeed::spawn_disabled(PreviewSurface::Inline.profile()),
|
||||
PreviewFeed::spawn_disabled(PreviewSurface::Inline.profile()),
|
||||
]));
|
||||
let window_feeds = Arc::new(Mutex::new([
|
||||
PreviewFeed::spawn_disabled(PreviewSurface::Window.profile()),
|
||||
PreviewFeed::spawn_disabled(PreviewSurface::Window.profile()),
|
||||
]));
|
||||
LauncherPreview {
|
||||
server_addr,
|
||||
log_sink,
|
||||
inline_feeds,
|
||||
window_feeds,
|
||||
}
|
||||
}
|
||||
|
||||
async fn request_capture_once(
|
||||
preview: &LauncherPreview,
|
||||
monitor_id: usize,
|
||||
surface: PreviewSurface,
|
||||
) -> anyhow::Result<()> {
|
||||
let feed = match surface {
|
||||
PreviewSurface::Inline => preview
|
||||
.inline_feeds
|
||||
.lock()
|
||||
.ok()
|
||||
.and_then(|feeds| feeds.get(monitor_id).cloned()),
|
||||
PreviewSurface::Window => preview
|
||||
.window_feeds
|
||||
.lock()
|
||||
.ok()
|
||||
.and_then(|feeds| feeds.get(monitor_id).cloned()),
|
||||
}
|
||||
.context("preview feed missing")?;
|
||||
let profile = feed.profile();
|
||||
let current_addr = preview
|
||||
.server_addr
|
||||
.lock()
|
||||
.map_err(|_| anyhow::anyhow!("preview address unavailable"))?
|
||||
.clone();
|
||||
let endpoint = crate::relay_transport::endpoint(¤t_addr)?.tcp_nodelay(true);
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
let channel = loop {
|
||||
match endpoint.clone().connect().await {
|
||||
Ok(channel) => break channel,
|
||||
Err(err) if Instant::now() < deadline => {
|
||||
tokio::time::sleep(Duration::from_millis(25)).await;
|
||||
}
|
||||
Err(err) => return Err(err).context("connecting preview test relay"),
|
||||
}
|
||||
};
|
||||
let req = MonitorRequest {
|
||||
id: monitor_id as u32,
|
||||
max_bitrate: profile.max_bitrate_kbit,
|
||||
requested_width: profile.requested_width.max(0) as u32,
|
||||
requested_height: profile.requested_height.max(0) as u32,
|
||||
requested_fps: profile.requested_fps,
|
||||
source_id: Some(profile.source_monitor_id),
|
||||
};
|
||||
let mut cli = RelayClient::new(channel);
|
||||
cli.capture_video(Request::new(req))
|
||||
.await
|
||||
.context("requesting preview test video capture")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inline_preview_profile_uses_lightweight_defaults() {
|
||||
let profile = PreviewSurface::Inline.profile();
|
||||
@ -315,7 +396,7 @@ fn inline_preview_requests_selected_source_profile_on_wire() {
|
||||
addr
|
||||
});
|
||||
|
||||
let preview = LauncherPreview::new(format!("http://{addr}")).expect("preview");
|
||||
let preview = disabled_preview(format!("http://{addr}"));
|
||||
let state = LauncherState::default();
|
||||
let capture = state.capture_size_choice(1);
|
||||
preview.set_capture_profile(
|
||||
@ -326,25 +407,16 @@ fn inline_preview_requests_selected_source_profile_on_wire() {
|
||||
capture.fps,
|
||||
capture.max_bitrate_kbit,
|
||||
);
|
||||
preview.activate_surface_for_test(1, PreviewSurface::Inline);
|
||||
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
while Instant::now() < deadline {
|
||||
if let Some(request) = requests.lock().unwrap().last().cloned() {
|
||||
assert_eq!(request.id, 1);
|
||||
assert_eq!(request.source_id, Some(1));
|
||||
assert_eq!(request.requested_width, 1280);
|
||||
assert_eq!(request.requested_height, 720);
|
||||
assert_eq!(request.requested_fps, 30);
|
||||
assert_eq!(request.max_bitrate, 6_000);
|
||||
preview.shutdown_all();
|
||||
return;
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(50));
|
||||
}
|
||||
request_preview_capture(&rt, &preview, 1, PreviewSurface::Inline);
|
||||
|
||||
let request = requests.lock().unwrap().last().cloned().expect("request");
|
||||
assert_eq!(request.id, 1);
|
||||
assert_eq!(request.source_id, Some(1));
|
||||
assert_eq!(request.requested_width, 1280);
|
||||
assert_eq!(request.requested_height, 720);
|
||||
assert_eq!(request.requested_fps, 30);
|
||||
assert_eq!(request.max_bitrate, 6_000);
|
||||
preview.shutdown_all();
|
||||
panic!("preview did not issue a capture request within timeout");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -366,7 +438,7 @@ fn inline_preview_requests_honest_source_profile_on_wire() {
|
||||
addr
|
||||
});
|
||||
|
||||
let preview = LauncherPreview::new(format!("http://{addr}")).expect("preview");
|
||||
let preview = disabled_preview(format!("http://{addr}"));
|
||||
let mut state = LauncherState::default();
|
||||
state.set_capture_size_preset(1, CaptureSizePreset::P1080);
|
||||
let capture = state.capture_size_choice(1);
|
||||
@ -378,25 +450,16 @@ fn inline_preview_requests_honest_source_profile_on_wire() {
|
||||
capture.fps,
|
||||
capture.max_bitrate_kbit,
|
||||
);
|
||||
preview.activate_surface_for_test(1, PreviewSurface::Inline);
|
||||
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
while Instant::now() < deadline {
|
||||
if let Some(request) = requests.lock().unwrap().last().cloned() {
|
||||
assert_eq!(request.id, 1);
|
||||
assert_eq!(request.source_id, Some(1));
|
||||
assert_eq!(request.requested_width, 1280);
|
||||
assert_eq!(request.requested_height, 720);
|
||||
assert_eq!(request.requested_fps, 30);
|
||||
assert_eq!(request.max_bitrate, 6_000);
|
||||
preview.shutdown_all();
|
||||
return;
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(50));
|
||||
}
|
||||
request_preview_capture(&rt, &preview, 1, PreviewSurface::Inline);
|
||||
|
||||
let request = requests.lock().unwrap().last().cloned().expect("request");
|
||||
assert_eq!(request.id, 1);
|
||||
assert_eq!(request.source_id, Some(1));
|
||||
assert_eq!(request.requested_width, 1280);
|
||||
assert_eq!(request.requested_height, 720);
|
||||
assert_eq!(request.requested_fps, 30);
|
||||
assert_eq!(request.max_bitrate, 6_000);
|
||||
preview.shutdown_all();
|
||||
panic!("preview did not issue a source capture request within timeout");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -418,7 +481,7 @@ fn inline_preview_requests_native_720p_source_mode_on_wire() {
|
||||
addr
|
||||
});
|
||||
|
||||
let preview = LauncherPreview::new(format!("http://{addr}")).expect("preview");
|
||||
let preview = disabled_preview(format!("http://{addr}"));
|
||||
let mut state = LauncherState::default();
|
||||
state.set_capture_size_preset(1, CaptureSizePreset::P720);
|
||||
let capture = state.capture_size_choice(1);
|
||||
@ -430,25 +493,16 @@ fn inline_preview_requests_native_720p_source_mode_on_wire() {
|
||||
capture.fps,
|
||||
capture.max_bitrate_kbit,
|
||||
);
|
||||
preview.activate_surface_for_test(1, PreviewSurface::Inline);
|
||||
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
while Instant::now() < deadline {
|
||||
if let Some(request) = requests.lock().unwrap().last().cloned() {
|
||||
assert_eq!(request.id, 1);
|
||||
assert_eq!(request.source_id, Some(1));
|
||||
assert_eq!(request.requested_width, 1280);
|
||||
assert_eq!(request.requested_height, 720);
|
||||
assert_eq!(request.requested_fps, 30);
|
||||
assert_eq!(request.max_bitrate, 6_000);
|
||||
preview.shutdown_all();
|
||||
return;
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(50));
|
||||
}
|
||||
request_preview_capture(&rt, &preview, 1, PreviewSurface::Inline);
|
||||
|
||||
let request = requests.lock().unwrap().last().cloned().expect("request");
|
||||
assert_eq!(request.id, 1);
|
||||
assert_eq!(request.source_id, Some(1));
|
||||
assert_eq!(request.requested_width, 1280);
|
||||
assert_eq!(request.requested_height, 720);
|
||||
assert_eq!(request.requested_fps, 30);
|
||||
assert_eq!(request.max_bitrate, 6_000);
|
||||
preview.shutdown_all();
|
||||
panic!("preview did not issue a 720p source capture request within timeout");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -470,7 +524,7 @@ fn inline_preview_legacy_low_modes_fall_forward_to_720p_on_wire() {
|
||||
addr
|
||||
});
|
||||
|
||||
let preview = LauncherPreview::new(format!("http://{addr}")).expect("preview");
|
||||
let preview = disabled_preview(format!("http://{addr}"));
|
||||
let mut state = LauncherState::default();
|
||||
state.set_capture_size_preset(1, CaptureSizePreset::P480);
|
||||
let capture = state.capture_size_choice(1);
|
||||
@ -482,25 +536,16 @@ fn inline_preview_legacy_low_modes_fall_forward_to_720p_on_wire() {
|
||||
capture.fps,
|
||||
capture.max_bitrate_kbit,
|
||||
);
|
||||
preview.activate_surface_for_test(1, PreviewSurface::Inline);
|
||||
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
while Instant::now() < deadline {
|
||||
if let Some(request) = requests.lock().unwrap().last().cloned() {
|
||||
assert_eq!(request.id, 1);
|
||||
assert_eq!(request.source_id, Some(1));
|
||||
assert_eq!(request.requested_width, 1280);
|
||||
assert_eq!(request.requested_height, 720);
|
||||
assert_eq!(request.requested_fps, 30);
|
||||
assert_eq!(request.max_bitrate, 6_000);
|
||||
preview.shutdown_all();
|
||||
return;
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(50));
|
||||
}
|
||||
request_preview_capture(&rt, &preview, 1, PreviewSurface::Inline);
|
||||
|
||||
let request = requests.lock().unwrap().last().cloned().expect("request");
|
||||
assert_eq!(request.id, 1);
|
||||
assert_eq!(request.source_id, Some(1));
|
||||
assert_eq!(request.requested_width, 1280);
|
||||
assert_eq!(request.requested_height, 720);
|
||||
assert_eq!(request.requested_fps, 30);
|
||||
assert_eq!(request.max_bitrate, 6_000);
|
||||
preview.shutdown_all();
|
||||
panic!("preview did not issue a 720p fallback source capture request within timeout");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -522,25 +567,16 @@ fn preview_can_request_other_eye_as_a_distinct_stream() {
|
||||
addr
|
||||
});
|
||||
|
||||
let preview = LauncherPreview::new(format!("http://{addr}")).expect("preview");
|
||||
let preview = disabled_preview(format!("http://{addr}"));
|
||||
preview.set_capture_profile(0, 1, 1920, 1080, 30, 12_000);
|
||||
preview.activate_surface_for_test(0, PreviewSurface::Inline);
|
||||
|
||||
let deadline = Instant::now() + Duration::from_secs(5);
|
||||
while Instant::now() < deadline {
|
||||
if let Some(request) = requests.lock().unwrap().last().cloned() {
|
||||
assert_eq!(request.id, 0);
|
||||
assert_eq!(request.source_id, Some(1));
|
||||
assert_eq!(request.requested_width, 1280);
|
||||
assert_eq!(request.requested_height, 720);
|
||||
assert_eq!(request.requested_fps, 30);
|
||||
assert_eq!(request.max_bitrate, 6_000);
|
||||
preview.shutdown_all();
|
||||
return;
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(50));
|
||||
}
|
||||
request_preview_capture(&rt, &preview, 0, PreviewSurface::Inline);
|
||||
|
||||
let request = requests.lock().unwrap().last().cloned().expect("request");
|
||||
assert_eq!(request.id, 0);
|
||||
assert_eq!(request.source_id, Some(1));
|
||||
assert_eq!(request.requested_width, 1280);
|
||||
assert_eq!(request.requested_height, 720);
|
||||
assert_eq!(request.requested_fps, 30);
|
||||
assert_eq!(request.max_bitrate, 6_000);
|
||||
preview.shutdown_all();
|
||||
panic!("preview did not issue a mirrored capture request within timeout");
|
||||
}
|
||||
|
||||
@ -14,12 +14,28 @@ use lesavka_common::lesavka::{
|
||||
handshake_server::{Handshake, HandshakeServer},
|
||||
};
|
||||
use serial_test::serial;
|
||||
use std::net::TcpListener;
|
||||
use std::net::{SocketAddr, TcpListener};
|
||||
use std::time::Duration;
|
||||
use temp_env::with_var;
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::sync::oneshot;
|
||||
use tonic::{Request, Response, Status, transport::Server};
|
||||
use tonic::{
|
||||
Request, Response, Status,
|
||||
transport::{Endpoint, Server},
|
||||
};
|
||||
|
||||
async fn wait_for_handshake_endpoint(addr: SocketAddr) {
|
||||
let endpoint = Endpoint::from_shared(format!("http://{addr}"))
|
||||
.expect("local endpoint")
|
||||
.tcp_nodelay(true);
|
||||
for _ in 0..100 {
|
||||
if endpoint.clone().connect().await.is_ok() {
|
||||
return;
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(25)).await;
|
||||
}
|
||||
panic!("local handshake server did not become reachable");
|
||||
}
|
||||
|
||||
async fn negotiate_against_service<S>(service: S) -> PeerCaps
|
||||
where
|
||||
@ -40,7 +56,7 @@ where
|
||||
.expect("serve handshake server");
|
||||
});
|
||||
|
||||
tokio::time::sleep(Duration::from_millis(50)).await;
|
||||
wait_for_handshake_endpoint(addr).await;
|
||||
let caps = negotiate(&format!("http://{addr}")).await;
|
||||
let _ = shutdown_tx.send(());
|
||||
let _ = server.await;
|
||||
@ -66,7 +82,7 @@ where
|
||||
.expect("serve handshake server");
|
||||
});
|
||||
|
||||
tokio::time::sleep(Duration::from_millis(50)).await;
|
||||
wait_for_handshake_endpoint(addr).await;
|
||||
let result = probe(&format!("http://{addr}")).await;
|
||||
let _ = shutdown_tx.send(());
|
||||
let _ = server.await;
|
||||
|
||||
@ -311,7 +311,7 @@ mod server_main_binary {
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn reset_usb_returns_internal_status_when_cycle_fails() {
|
||||
fn reset_usb_surfaces_hardware_recovery_failure() {
|
||||
let (_dir, handler) = build_handler_for_tests();
|
||||
|
||||
let rt = tokio::runtime::Runtime::new().expect("runtime");
|
||||
@ -320,7 +320,14 @@ mod server_main_binary {
|
||||
Ok(_) => panic!("cycle should fail without gadget sysfs"),
|
||||
Err(err) => err,
|
||||
};
|
||||
assert_eq!(err.code(), tonic::Code::Internal);
|
||||
assert!(
|
||||
matches!(
|
||||
err.code(),
|
||||
tonic::Code::Internal | tonic::Code::FailedPrecondition
|
||||
),
|
||||
"unexpected reset failure code: {:?}",
|
||||
err.code()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user