//! Tests del fork tawasuyu: los Lotties que en velato 0.9 upstream //! **paniqueaban** (`todo!()`/`unimplemented!()` en el importador) ahora se //! importan con degradación graciosa, sin panic. Cada caso construye una //! `Composition` y exige que vuelva `Ok`. use foreign_lottie::Composition; /// Lottie con un layer cuyo transform NO trae campo de rotación (`r`). /// Upstream: `None => todo!("split rotation")`. Fork: rotación 0. #[test] fn transform_sin_rotacion_no_paniquea() { let json = r#"{"v":"5.5.2","fr":30,"ip":0,"op":60,"w":100,"h":100, "layers":[{"ty":4,"ip":0,"op":60,"st":0,"sr":1, "ks":{"p":{"a":0,"k":[0,0]}},"shapes":[]}]}"#; assert!(Composition::from_slice(json).is_ok()); } /// Transform con rotación **splitteada** (`r` = {x,y,z,or}). Upstream: /// `SplitRotation => todo!()`. Fork: usa la componente z. #[test] fn split_rotation_no_paniquea() { let json = r#"{"v":"5.5.2","fr":30,"ip":0,"op":60,"w":100,"h":100, "layers":[{"ty":4,"ip":0,"op":60,"st":0,"sr":1, "ks":{"p":{"a":0,"k":[0,0]}, "r":{"x":{"a":0,"k":0},"y":{"a":0,"k":0},"z":{"a":0,"k":45}, "or":{"a":0,"k":[0,0,0]}}}, "shapes":[]}]}"#; assert!(Composition::from_slice(json).is_ok()); } /// Transform con posición **splitteada** (`p` = {s:true,x,y}). Upstream del /// shape-transform: `SplitPosition => todo!("split position")`. Fork: la arma /// desde x/y. (Lo metemos dentro de un group-shape con su transform para /// ejercitar `conv_shape_transform`.) #[test] fn split_position_en_shape_no_paniquea() { let json = r#"{"v":"5.5.2","fr":30,"ip":0,"op":60,"w":100,"h":100, "layers":[{"ty":4,"ip":0,"op":60,"st":0,"sr":1, "ks":{"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}}, "shapes":[{"ty":"gr","it":[ {"ty":"tr", "p":{"s":true,"x":{"a":0,"k":10},"y":{"a":0,"k":20}}, "a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]}, "r":{"a":0,"k":0},"o":{"a":0,"k":100}} ]}]}]}"#; assert!(Composition::from_slice(json).is_ok()); } /// Blend mode `Add` (be=10) en un layer. Upstream: `Add => unimplemented!()`. /// Fork: compositing aditivo (Compose::Plus). #[test] fn blend_add_no_paniquea() { let json = r#"{"v":"5.5.2","fr":30,"ip":0,"op":60,"w":100,"h":100, "layers":[{"ty":4,"ip":0,"op":60,"st":0,"sr":1,"bm":10, "ks":{"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0}},"shapes":[]}]}"#; assert!(Composition::from_slice(json).is_ok()); } /// Un Lottie sano sigue parseando (no rompimos el camino feliz). #[test] fn lottie_sano_sigue_ok() { let json = r#"{"v":"5.5.2","fr":30,"ip":0,"op":60,"w":100,"h":100, "layers":[{"ty":4,"ip":0,"op":60,"st":0,"sr":1, "ks":{"p":{"a":0,"k":[50,50]},"r":{"a":0,"k":0}}, "shapes":[ {"ty":"rc","p":{"a":0,"k":[50,50]},"s":{"a":0,"k":[80,80]},"r":{"a":0,"k":0}}, {"ty":"fl","c":{"a":0,"k":[0.8,0,0,1]},"o":{"a":0,"k":100}} ]}]}"#; let comp = Composition::from_slice(json).expect("lottie sano parsea"); assert_eq!((comp.width, comp.height), (100, 100)); } // ── El deserializador: una capa que no entendemos no se lleva el archivo ── /// Un `ty` fuera de 0–4 (texto=5, cámara, luz, ajuste…) devolvía /// `Err("unknown layer type")`, y serde aborta el documento al primer error: /// **un solo layer de texto tumbaba el `.json` entero**. En un export de After /// Effects una cámara o una luz vienen de fábrica, así que el caso es el /// común, no el raro. Ahora la capa se reconoce como `Unsupported` y el resto /// del archivo se importa. /// /// El diente del test es la SEGUNDA aserción: que no paniquee ni devuelva /// `Err` no alcanza si el documento vuelve sin capas — sería la misma pérdida /// con otro nombre. #[test] fn una_capa_de_tipo_desconocido_no_rechaza_el_documento() { let json = r#"{"v":"5.5.2","fr":30,"ip":0,"op":60,"w":100,"h":100, "layers":[ {"ty":5,"ip":0,"op":60,"st":0,"sr":1, "ks":{"p":{"a":0,"k":[50,50]},"r":{"a":0,"k":0}}}, {"ty":4,"ip":0,"op":60,"st":0,"sr":1, "ks":{"p":{"a":0,"k":[50,50]},"r":{"a":0,"k":0}}, "shapes":[ {"ty":"rc","p":{"a":0,"k":[50,50]},"s":{"a":0,"k":[80,80]},"r":{"a":0,"k":0}}, {"ty":"fl","c":{"a":0,"k":[0.8,0,0,1]},"o":{"a":0,"k":100}} ]} ]}"#; let comp = Composition::from_slice(json).expect("una capa de texto no puede tumbar el archivo"); assert!( !comp.layers.is_empty(), "importó con Ok pero sin capas: la capa de forma se fue con la de texto" ); } /// Y el otro lado del mismo agujero: **omitir el campo `s` (escala) de un /// transform** es legal en Lottie y su default es 100. El default del /// importador era `[1,1,1]`, y la escala se divide por 100 al armar la /// `Affine`, así que la capa entraba al **1 %** — sin error, sin panic, sin /// nada que mirar salvo el resultado. Verificado a nivel píxel en /// `02_ruway/llimphi/llimphi-test/tests/lottie_degrada_pero_pinta.rs` /// (4 px sin `s`, 8.100 con `s:[100,100]`); acá se fija el número que lo /// causa, que es más barato y no necesita GPU. #[test] fn un_transform_sin_escala_entra_al_cien_por_ciento() { let json = r#"{"v":"5.5.2","fr":30,"ip":0,"op":60,"w":100,"h":100, "layers":[{"ty":4,"ip":0,"op":60,"st":0,"sr":1, "ks":{"p":{"a":0,"k":[50,50]},"r":{"a":0,"k":0}},"shapes":[]}]}"#; let comp = Composition::from_slice(json).expect("parsea"); let capa = comp.layers.first().expect("hay una capa"); // `Transform::evaluate` devuelve un `ValueRef`. let escala = capa.transform.evaluate(0.0).as_ref().as_coeffs(); // Los coeficientes 0 y 3 de la Affine son el escalado en x e y. Con el // default viejo daban 0,01. assert!( (escala[0] - 1.0).abs() < 1e-6 && (escala[3] - 1.0).abs() < 1e-6, "un transform sin `s` entró con escala {:?}, no 1,0 (= 100 %)", (escala[0], escala[3]) ); }