diff options
Diffstat (limited to 'crates/asist-app/tests/integracion.rs')
| -rw-r--r-- | crates/asist-app/tests/integracion.rs | 129 |
1 files changed, 108 insertions, 21 deletions
diff --git a/crates/asist-app/tests/integracion.rs b/crates/asist-app/tests/integracion.rs index a7f7e15..d0fca38 100644 --- a/crates/asist-app/tests/integracion.rs +++ b/crates/asist-app/tests/integracion.rs @@ -17,6 +17,7 @@ use asist_core::http::Cancel; use asist_core::tools::{Tool, ToolRegistry}; use asist_llm::chat::Conversation; use asist_llm::{Delta, LlmClient, Message}; +use asist_tools::FrameSource; use asist_tts::TtsClient; /// Las pruebas se turnan la GPU. @@ -346,24 +347,62 @@ fn una_consulta_inventada_no_revienta_el_turno() { ); } -#[test] -fn la_camara_captura_un_jpeg() { - let config = config(); - let capture = asist_tools::camera::CaptureConfig { +fn camera_source(config: &Config) -> Option<asist_tools::Camera> { + if !config.camera.enabled || !config.camera.device.exists() { + eprintln!("sin cámara; prueba omitida"); + return None; + } + Some(asist_tools::Camera::new(asist_tools::CameraConfig { device: config.camera.device.clone(), width: config.camera.width, height: config.camera.height, warmup_frames: config.camera.warmup_frames, timeout: Duration::from_secs(config.camera.timeout_secs), save_dir: None, - }; - if !config.camera.enabled || !capture.device.exists() { - eprintln!("sin cámara; prueba omitida"); - return; + })) +} + +fn screen_source(config: &Config) -> Option<asist_tools::Screen> { + let screen = asist_tools::Screen::new(asist_tools::ScreenConfig { + command: config.screen.command.clone(), + width: config.screen.width, + output: config.screen.output.clone(), + timeout: Duration::from_secs(config.screen.timeout_secs), + save_dir: None, + }); + if !config.screen.enabled || screen.available().is_err() { + eprintln!("sin pantalla capturable; prueba omitida"); + return None; + } + Some(screen) +} + +/// Ancho de un JPEG, leído de su cabecera SOF. +fn jpeg_width(bytes: &[u8]) -> Option<u32> { + let mut i = 2; + while i + 9 < bytes.len() { + if bytes[i] != 0xFF { + return None; + } + let marker = bytes[i + 1]; + let len = u16::from_be_bytes([bytes[i + 2], bytes[i + 3]]) as usize; + if (0xC0..=0xCF).contains(&marker) && marker != 0xC4 && marker != 0xC8 && marker != 0xCC { + return Some(u16::from_be_bytes([bytes[i + 7], bytes[i + 8]]) as u32); + } + i += 2 + len; } + None +} + +#[test] +fn la_camara_captura_un_jpeg() { + let config = config(); + let Some(source) = camera_source(&config) else { + return; + }; let started = Instant::now(); - let frame = asist_tools::camera::capture(&capture).expect("la captura falló"); + let frame = source.capture().expect("la captura falló"); eprintln!( "fotograma de {} KB en {:?}", frame.len() / 1024, @@ -380,26 +419,43 @@ fn la_camara_captura_un_jpeg() { } #[test] +fn la_pantalla_se_captura_al_ancho_configurado() { + let config = config(); + let Some(source) = screen_source(&config) else { + return; + }; + + let started = Instant::now(); + let frame = source.capture().expect("la captura falló"); + eprintln!( + "captura de {} KB en {:?}", + frame.len() / 1024, + started.elapsed() + ); + assert_eq!(&frame[..2], &[0xFF, 0xD8], "no parece un JPEG"); + + // El ancho es lo que separa leer de inventar, así que se comprueba que el + // guion de verdad redujo la imagen y no la mandó a tamaño completo. + let width = jpeg_width(&frame).expect("no se pudo leer el ancho del JPEG"); + assert_eq!( + width, config.screen.width, + "la captura salió a {width} px y se pedían {}", + config.screen.width + ); +} + +#[test] #[ignore = "enciende la cámara y hace una pasada de visión; lento"] fn el_modelo_describe_lo_que_ve_la_camara() { let _gpu = en_exclusiva(); let Some((config, llm, _)) = clientes() else { return; }; - let capture = asist_tools::camera::CaptureConfig { - device: config.camera.device.clone(), - width: config.camera.width, - height: config.camera.height, - warmup_frames: config.camera.warmup_frames, - timeout: Duration::from_secs(config.camera.timeout_secs), - save_dir: None, - }; - if !config.camera.enabled || !capture.device.exists() { - eprintln!("sin cámara; prueba omitida"); + let Some(source) = camera_source(&config) else { return; - } + }; - let tool = asist_tools::Camera::new(capture, std::sync::Arc::new(llm)); + let tool = asist_tools::VisionTool::camera(Box::new(source), std::sync::Arc::new(llm)); let started = Instant::now(); let out = tool .call(&serde_json::json!({ "pregunta": "¿Qué se ve en la imagen?" })) @@ -418,6 +474,37 @@ fn el_modelo_describe_lo_que_ve_la_camara() { } #[test] +#[ignore = "captura la pantalla y hace una pasada de visión; lento"] +fn el_modelo_describe_la_pantalla() { + let _gpu = en_exclusiva(); + let Some((config, llm, _)) = clientes() else { + return; + }; + let Some(source) = screen_source(&config) else { + return; + }; + + let tool = asist_tools::VisionTool::screen(Box::new(source), std::sync::Arc::new(llm)); + let started = Instant::now(); + let out = tool + .call(&serde_json::json!({ "pregunta": "¿Qué hay en la pantalla?" })) + .expect("la descripción falló"); + + eprintln!("pantalla en {:?}: {out}", started.elapsed()); + assert!( + out.split_whitespace().count() >= 3, + "descripción vacía o mínima: {out}" + ); + // A 1280 px se midieron 7,6 s. Si esto se dispara, o la GPU está ocupada o + // alguien subió screen.width sin mirar el coste. + assert!( + started.elapsed() < Duration::from_secs(30), + "tardó {:?}; revisa screen.width", + started.elapsed() + ); +} + +#[test] #[ignore = "sale a internet con los dos buscadores; lento"] fn los_dos_buscadores_responden_a_lo_mismo() { // Compara lo que devuelve cada uno para las mismas preguntas. No afirma |