diff options
| author | cashmere <cashmere@autistici.org> | 2026-07-28 20:14:51 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-07-28 20:14:51 +0200 |
| commit | 32438ce25f69de99d2523b98237f62cfa90890d7 (patch) | |
| tree | 3a712c347dec0889bdea3328b4c817c2d9392b27 /rust/crates/timeline/tests/retime.rs | |
| parent | f5a82152394fde4d8f740f664ab7737b82861618 (diff) | |
| download | opencut-32438ce25f69de99d2523b98237f62cfa90890d7.tar.gz | |
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)
Diffstat (limited to 'rust/crates/timeline/tests/retime.rs')
| -rw-r--r-- | rust/crates/timeline/tests/retime.rs | 111 |
1 files changed, 111 insertions, 0 deletions
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); +} |
