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/update_pipeline.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/update_pipeline.rs')
| -rw-r--r-- | rust/crates/timeline/tests/update_pipeline.rs | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/rust/crates/timeline/tests/update_pipeline.rs b/rust/crates/timeline/tests/update_pipeline.rs new file mode 100644 index 00000000..a7f6f339 --- /dev/null +++ b/rust/crates/timeline/tests/update_pipeline.rs @@ -0,0 +1,145 @@ +use scene::{ + BaseTimelineElement, Element, ParamValue, ParamValues, RetimeConfig, SceneTracks, Track, + VideoElement, +}; +use time::MediaTime; +use timeline::update_pipeline::{ElementPatch, ElementUpdateContext, apply_element_update}; + +fn build_video_element() -> Element { + Element::Video(VideoElement { + base: BaseTimelineElement { + id: "video-1".to_string(), + name: "Video 1".to_string(), + start_time: MediaTime::ZERO, + duration: MediaTime::from_ticks(10), + trim_start: MediaTime::ZERO, + trim_end: MediaTime::ZERO, + source_duration: None, + animations: None, + params: ParamValues::from([ + ("transform.positionX".to_string(), ParamValue::Number(0.0)), + ("transform.positionY".to_string(), ParamValue::Number(0.0)), + ("transform.scaleX".to_string(), ParamValue::Number(1.0)), + ("transform.scaleY".to_string(), ParamValue::Number(1.0)), + ("transform.rotate".to_string(), ParamValue::Number(0.0)), + ("opacity".to_string(), ParamValue::Number(1.0)), + ]), + }, + media_id: "media-1".to_string(), + is_source_audio_enabled: None, + hidden: None, + retime: None, + effects: None, + masks: None, + }) +} + +fn build_tracks(element: Element) -> SceneTracks { + SceneTracks { + overlay: Vec::new(), + main: Track::Video { + id: "main-track".to_string(), + name: "Main".to_string(), + elements: vec![element], + muted: false, + hidden: false, + }, + audio: Vec::new(), + } +} + +#[test] +fn rounds_retimed_durations_back_to_integer_media_time() { + let element = build_video_element(); + let tracks = build_tracks(element.clone()); + + let updated = apply_element_update( + &element, + &ElementPatch { + retime: Some(Some(RetimeConfig { + rate: 1.5, + maintain_pitch: None, + })), + ..ElementPatch::default() + }, + &ElementUpdateContext { + tracks: &tracks, + track_id: tracks.main.id(), + }, + ); + + assert_eq!(updated.base().duration, MediaTime::from_ticks(7)); + assert_eq!(timeline::element_retime(&updated).unwrap().rate, 1.5); +} + +#[test] +fn start_time_on_main_track_snaps_to_zero_before_the_earliest_element() { + let earliest = { + let mut element = build_video_element(); + element.base_mut().id = "video-2".to_string(); + element.base_mut().start_time = MediaTime::from_ticks(100); + element + }; + let element = build_video_element(); + let tracks = build_tracks(earliest); + let tracks = SceneTracks { + main: Track::Video { + id: "main-track".to_string(), + name: "Main".to_string(), + elements: vec![tracks.main.elements()[0].clone(), element.clone()], + muted: false, + hidden: false, + }, + ..tracks + }; + + let updated = apply_element_update( + &element, + &ElementPatch { + start_time: Some(MediaTime::from_ticks(50)), + ..ElementPatch::default() + }, + &ElementUpdateContext { + tracks: &tracks, + track_id: "main-track", + }, + ); + + assert_eq!(updated.base().start_time, MediaTime::ZERO); +} + +#[test] +fn start_time_on_overlay_track_only_clamps_negative_values() { + let element = build_video_element(); + let tracks = SceneTracks { + overlay: vec![Track::Video { + id: "overlay-track".to_string(), + name: "Overlay".to_string(), + elements: vec![element.clone()], + muted: false, + hidden: false, + }], + main: Track::Video { + id: "main-track".to_string(), + name: "Main".to_string(), + elements: Vec::new(), + muted: false, + hidden: false, + }, + audio: Vec::new(), + }; + + let updated = apply_element_update( + &element, + &ElementPatch { + start_time: Some(MediaTime::from_ticks(-20)), + ..ElementPatch::default() + }, + &ElementUpdateContext { + tracks: &tracks, + track_id: "overlay-track", + }, + ); + + assert_eq!(updated.base().start_time, MediaTime::ZERO); +} |
