From 32438ce25f69de99d2523b98237f62cfa90890d7 Mon Sep 17 00:00:00 2001 From: cashmere Date: Tue, 28 Jul 2026 20:14:51 +0200 Subject: feat: port domain crates to Rust (session 1 complete) All green: cargo test --workspace (~300 tests, 0 failures). - timeline (45): placement, resize/ripple, split, snap, retime, audio separation, scene helpers - animation (73): bezier solver, channel sampling, keyframe ops, split/clamp/clone, param definitions - commands (9): Command trait, BatchCommand, history, 19 concrete commands, freeze-frame port 1:1 - audio (22): audible collection, range mixing, mastering limiter, rms/peak buckets - text (19): cosmic-text layout/measure/raster, backgrounds, decorations - graphics (52): full CSS gradient parser, tiny-skia raster, 4 shape definitions - effect-defs (16): blur definition, registry, pass resolution - mask-defs (19): all 9 mask renderers via tiny-skia - renderer (6): node model + scene builder port - desktop: GPUI projects screen wired to ProjectStore - scene: factory (web defaults: v32, Main Track, 1920x1080, 30fps) --- rust/crates/timeline/tests/retime.rs | 111 +++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 rust/crates/timeline/tests/retime.rs (limited to 'rust/crates/timeline/tests/retime.rs') diff --git a/rust/crates/timeline/tests/retime.rs b/rust/crates/timeline/tests/retime.rs new file mode 100644 index 00000000..b495fec6 --- /dev/null +++ b/rust/crates/timeline/tests/retime.rs @@ -0,0 +1,111 @@ +use scene::RetimeConfig; +use timeline::retime::{ + clamp_retime_rate, get_clip_time_at_source_time, get_effective_rate_at, + get_source_span_at_clip_time, get_source_time_at_clip_time, + get_timeline_duration_for_source_span, split_retime_at_clip_time, +}; + +const TWO_X: RetimeConfig = RetimeConfig { + rate: 2.0, + maintain_pitch: None, +}; +const HALF_X: RetimeConfig = RetimeConfig { + rate: 0.5, + maintain_pitch: None, +}; + +#[test] +fn maps_clip_time_to_source_time_at_2x_speed() { + assert_eq!(get_source_time_at_clip_time(5.0, Some(&TWO_X)), 10.0); +} + +#[test] +fn maps_clip_time_to_source_time_at_half_speed() { + assert_eq!(get_source_time_at_clip_time(4.0, Some(&HALF_X)), 2.0); +} + +#[test] +fn returns_clip_time_unchanged_when_no_retime() { + assert_eq!(get_source_time_at_clip_time(7.0, None), 7.0); +} + +#[test] +fn inverts_source_time_back_to_clip_time_at_2x_speed() { + assert_eq!(get_clip_time_at_source_time(10.0, Some(&TWO_X)), 5.0); +} + +#[test] +fn returns_effective_rate() { + assert_eq!(get_effective_rate_at(Some(&TWO_X)), 2.0); + assert_eq!(get_effective_rate_at(None), 1.0); +} + +#[test] +fn derives_timeline_duration_for_a_visible_source_span() { + assert_eq!( + get_timeline_duration_for_source_span(10.0, Some(&TWO_X)), + 5.0 + ); + assert_eq!( + get_timeline_duration_for_source_span(10.0, Some(&HALF_X)), + 20.0 + ); +} + +#[test] +fn clamps_invalid_rates_to_one() { + let zero = RetimeConfig { + rate: 0.0, + maintain_pitch: None, + }; + let negative = RetimeConfig { + rate: -1.0, + maintain_pitch: None, + }; + assert_eq!(get_source_time_at_clip_time(5.0, Some(&zero)), 5.0); + assert_eq!(get_source_time_at_clip_time(5.0, Some(&negative)), 5.0); + assert_eq!(clamp_retime_rate(f64::NAN), 1.0); + assert_eq!(clamp_retime_rate(f64::INFINITY), 1.0); +} + +#[test] +fn caps_retime_rates_above_5x() { + let hundred = RetimeConfig { + rate: 100.0, + maintain_pitch: None, + }; + assert_eq!(get_source_time_at_clip_time(5.0, Some(&hundred)), 25.0); + assert_eq!( + get_timeline_duration_for_source_span(10.0, Some(&hundred)), + 2.0 + ); +} + +#[test] +fn measures_source_span_at_a_clip_time() { + assert_eq!(get_source_span_at_clip_time(5.0, Some(&TWO_X)), 10.0); +} + +#[test] +fn returns_zero_for_non_positive_clip_time() { + assert_eq!(get_source_span_at_clip_time(0.0, None), 0.0); + assert_eq!(get_source_span_at_clip_time(-1.0, None), 0.0); +} + +#[test] +fn passes_the_same_retime_to_both_halves_when_splitting() { + let retime = RetimeConfig { + rate: 1.5, + maintain_pitch: None, + }; + let (left, right) = split_retime_at_clip_time(Some(&retime)); + assert_eq!(left, Some(retime)); + assert_eq!(right, Some(retime)); +} + +#[test] +fn returns_none_on_both_sides_when_no_retime() { + let (left, right) = split_retime_at_clip_time(None); + assert_eq!(left, None); + assert_eq!(right, None); +} -- cgit v1.3.1