tawasuyu/llimphi
Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.
chore: refresco desde el monorepo — vello 0.9 / wgpu 29, 120 miembros
1//! **Reproducir una escena que llegó por el cable, ¿da los mismos píxeles que
2//! componerla acá?**
3//!
4//! El IR de escena (W4) tiene round-trip **estructural** certificado:
5//! `llimphi-wire-escena/src/pruebas.rs` verifica que cada variante sobreviva la
6//! serialización, que el texto viaje sin shapear, que el degradé conserve sus
7//! paradas. Lo que **no** verificaba nadie es que reproducir esas órdenes
8//! **dibuje lo mismo**. Un reproductor que aplicara las transformaciones en
9//! otro orden, o que remapeara mal un estilo internado, pasaría todos los tests
10//! estructurales y pintaría otra cosa.
11//!
12//! Acá los dos caminos terminan en el **mismo** rasterizador (`vello_cpu`) y
13//! sobre el **mismo** árbol (`escena_patron.rs`), así que lo que se exige no es
14//! una tolerancia sino **igualdad bit a bit**: cualquier diferencia es un error
15//! del emisor o del reproductor, no del antialiasing.
16//!
17//! La escena de cable la produce `llimphi-jaula` (backend `wire`), que no puede
18//! vivir en este binario —los backends de `llimphi-text` son excluyentes—:
19//!
20//! ```bash
21//! ./scripts/check-llimphi-cpu-vs-gpu.sh
22//! ```
23//!
24//! Sin los `.wire` el test se saltea, con el comando para generarlos.
25
26use llimphi_cpu::lienzo::FormatoPixel;
27use llimphi_wawa::Cascara;
28
29include!("../../escena_patron.rs");
30
31fn volcado(c: &Cascara) -> Vec<u8> {
32 let mut px = vec![0u8; (ANCHO * ALTO * 4) as usize];
33 c.volcar(&mut px, (ANCHO * 4) as usize, FormatoPixel::Rgba8);
34 px
35}
36
37#[test]
38fn lo_que_llega_por_el_cable_se_pinta_igual_que_lo_compuesto_aca() {
39 let Ok(dir) = std::env::var("LLIMPHI_WIRE_DUMP") else {
40 eprintln!(
41 "salteado: faltan las escenas de cable. Generalas con\n \
42 ./scripts/check-llimphi-cpu-vs-gpu.sh"
43 );
44 return;
45 };
46 let dir = std::path::PathBuf::from(dir);
47
48 let mut cascara = Cascara::new(ANCHO as u16, ALTO as u16);
49 let mut probados = 0;
50 for (nombre, vista) in patrones::<()>() {
51 // El patrón de texto no cruza el cable en esta prueba: medir una cadena
52 // es una pregunta que contesta este lado, y el emisor no puede
53 // responderla solo. Ver el encabezado de `emitir_patron.rs`.
54 let Ok(bytes) = std::fs::read(dir.join(format!("{nombre}.wire"))) else {
55 continue;
56 };
57
58 cascara.fotograma(vista);
59 let directo = volcado(&cascara);
60
61 let wire: llimphi_wire_escena::EscenaWire =
62 postcard::from_bytes(&bytes).expect("deserializar la escena de cable");
63 let st = cascara.fotograma_wire(&wire);
64 let por_cable = volcado(&cascara);
65
66 let distintos = directo
67 .chunks_exact(4)
68 .zip(por_cable.chunks_exact(4))
69 .filter(|(a, b)| a != b)
70 .count();
71 // Evidencia por texto (Regla 8): el número queda impreso corra o falle.
72 println!(
73 "{nombre:8} · {} bytes de cable · {} órdenes ({} salteadas) · {distintos} px distintos",
74 bytes.len(),
75 st.ejecutadas,
76 st.salteadas
77 );
78 assert_eq!(
79 distintos, 0,
80 "{nombre}: reproducir la escena del cable pintó algo distinto de \
81 componerla acá — mismo rasterizador, así que es el cable"
82 );
83 // Guarda del arnés: una escena vacía se reproduce «igual» a la nada y
84 // el test pasaría sin haber comparado un dibujo.
85 assert!(
86 st.ejecutadas > 0,
87 "{nombre}: la escena del cable llegó sin una sola orden"
88 );
89 // Y el reproductor tiene un contador propio para lo que no supo
90 // ejecutar. Cero píxeles distintos con órdenes salteadas sería la
91 // combinación más engañosa que hay: dos escenas igual de incompletas.
92 assert_eq!(
93 st.salteadas, 0,
94 "{nombre}: el reproductor salteó {} órdenes",
95 st.salteadas
96 );
97 probados += 1;
98 }
99 assert!(
100 probados >= 4,
101 "sólo {probados} patrones cruzaron el cable; el arnés no está probando"
102 );
103}