use super::*;
fn ms(value: u64) -> Duration {
Duration::from_millis(value)
}
#[test]
fn progress_reaches_one_and_stops() {
let mut clock = AnimationClock::default();
let start = Instant::now();
let animation = Animation::new(ms(100)).easing(Easing::Linear);
assert_eq!(clock.progress("fade", animation, start), (0.0, true));
let (halfway, running) = clock.progress("fade", animation, start + ms(50));
assert!((halfway - 0.5).abs() < 0.01 && running);
assert_eq!(clock.progress("fade", animation, start + ms(200)), (1.0, false));
}
#[test]
fn every_curve_starts_at_zero_and_ends_at_one() {
let curves = [
Easing::Linear,
Easing::EaseIn,
Easing::EaseOut,
Easing::EaseInOut,
Easing::STANDARD,
Easing::SPRING,
Easing::Spring { bounce: 0.0 },
Easing::Spring { bounce: 0.6 },
];
for curve in curves {
assert!(curve.apply(0.0).abs() < 1.0e-4, "{curve:?} empieza en 0");
assert!((curve.apply(1.0) - 1.0).abs() < 1.0e-4, "{curve:?} termina en 1");
}
}
#[test]
fn cubic_bezier_matches_css_ease() {
let ease = Easing::CubicBezier(0.25, 0.1, 0.25, 1.0);
assert!((ease.apply(0.5) - 0.8024).abs() < 0.005);
assert!((Easing::CubicBezier(0.0, 0.0, 1.0, 1.0).apply(0.3) - 0.3).abs() < 1.0e-3);
}
#[test]
fn a_bouncy_spring_overshoots_and_settles() {
let bouncy = Easing::Spring { bounce: 0.5 };
let peak = (1..100).map(|step| bouncy.apply(step as f32 / 100.0)).fold(0.0, f32::max);
assert!(peak > 1.05, "rebota por encima del destino");
assert!((bouncy.apply(0.99) - 1.0).abs() < 0.02, "casi quieto al final");
let firm = Easing::Spring { bounce: 0.0 };
assert!((1..100).all(|step| firm.apply(step as f32 / 100.0) <= 1.0));
}
#[test]
fn delay_waits_before_moving() {
let animation = Animation::new(ms(100)).easing(Easing::Linear).delay(ms(50));
assert_eq!(animation.sample(ms(30)), (0.0, true));
let (value, _) = animation.sample(ms(100));
assert!((value - 0.5).abs() < 0.01);
}
#[test]
fn repeats_and_ping_pong() {
let twice = Animation::new(ms(100)).easing(Easing::Linear).repeat(Repeat::Times(2));
let (value, running) = twice.sample(ms(150));
assert!((value - 0.5).abs() < 0.01 && running);
assert_eq!(twice.sample(ms(250)), (1.0, false));
let pulse = Animation::new(ms(100)).easing(Easing::Linear).ping_pong();
let (back, running) = pulse.sample(ms(175));
assert!((back - 0.25).abs() < 0.01 && running, "vuelve en el segundo ciclo");
assert!(pulse.sample(Duration::from_secs(3600)).1, "nunca termina");
}
#[test]
fn transitions_glide_from_where_they_were() {
let mut clock = AnimationClock::default();
let start = Instant::now();
let animation = Animation::new(ms(100)).easing(Easing::Linear);
assert_eq!(clock.transition("x", 0.0, animation, start), (0.0, false), "la primera vez no anima");
assert_eq!(clock.transition("x", 10.0, animation, start), (0.0, true));
let (halfway, running) = clock.transition("x", 10.0, animation, start + ms(50));
assert!((halfway - 5.0).abs() < 0.01 && running);
assert_eq!(clock.transition("x", 0.0, animation, start + ms(50)).0, halfway);
assert_eq!(clock.transition("x", 0.0, animation, start + ms(200)), (0.0, false));
}
#[test]
fn colors_and_geometry_interpolate() {
let black = Rgba { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
let white = Rgba { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
assert_eq!(black.lerp(white, 0.5).r, 0.5);
let from = Point::new(Pixels(0.0), Pixels(10.0));
assert_eq!(from.lerp(Point::new(Pixels(10.0), Pixels(30.0)), 0.5), Point::new(Pixels(5.0), Pixels(20.0)));
}
#[test]
fn the_wave_stays_between_zero_and_one() {
let samples: Vec<f32> = (0..100).map(|i| wave(i as f32 * 0.03, 2.0, 0.0)).collect();
assert!(samples.iter().all(|v| (0.0..=1.0).contains(v)));
assert!(samples.iter().any(|v| *v > 0.95) && samples.iter().any(|v| *v < 0.05));
assert!((wave(0.0, 1.0, 0.0) - 0.5).abs() < 1e-6);
}