use tellers_timeline_core::track_methods::track_item_insert::{InsertPolicy, OverlapPolicy};
use std::collections::HashMap;
use tellers_timeline_core::*;
fn make_clip_with_id(duration: Seconds, id: &str) -> Item {
let mut refs: HashMap<String, MediaReference> = HashMap::new();
refs.insert(
"DEFAULT_MEDIA".to_string(),
MediaReference::ExternalReference {
target_url: "mem://".to_string(),
available_range: Some(TimeRange {
otio_schema: "TimeRange.1".to_string(),
duration: RationalTime { otio_schema: "RationalTime.1".to_string(), rate: 1.0, value: duration },
start_time: RationalTime { otio_schema: "RationalTime.1".to_string(), rate: 1.0, value: 0.0 },
}),
name: None,
available_image_bounds: None,
metadata: serde_json::Value::Null,
},
);
let mut it = Item::Clip(Clip {
otio_schema: "Clip.2".to_string(),
enabled: true,
name: Some("c".to_string()),
source_range: TimeRange { otio_schema: "TimeRange.1".to_string(), duration: RationalTime { otio_schema: "RationalTime.1".to_string(), rate: 1.0, value: duration }, start_time: RationalTime { otio_schema: "RationalTime.1".to_string(), rate: 1.0, value: 0.0 } },
media_references: refs,
active_media_reference_key: Some("DEFAULT_MEDIA".to_string()),
metadata: serde_json::Value::Null,
effects: Vec::new(),
markers: Vec::new(),
});
it.set_id(Some(id.to_string()));
it
}
#[test]
fn stack_get_and_remove_item() {
let mut tl = Timeline::default();
let mut t1 = Track::default();
let mut t2 = Track::default();
let id1 = "id-1";
let id2 = "id-2";
t1.items.push(make_clip_with_id(2.0, id1));
t2.items.push(make_clip_with_id(3.0, id2));
let tid1 = "tid-1".to_string();
let tid2 = "tid-2".to_string();
t1.set_id(Some(tid1.clone()));
t2.set_id(Some(tid2.clone()));
tl.tracks.children.push(t1);
tl.tracks.children.push(t2);
let got = tl.tracks.get_item(id1).expect("should find");
assert_eq!(got.0, 0);
assert_eq!(got.1, 0);
let removed = tl.tracks.delete_item(id1, false, true);
assert_eq!(removed.len(), 1);
assert_eq!(removed[0].0, 0);
assert!(matches!(removed[0].1, Item::Clip(_)));
assert!(tl.tracks.children[0].items.is_empty());
}
#[test]
fn stack_move_item_between_tracks_at_time() {
let mut tl = Timeline::default();
let mut t1 = Track::default();
let mut t2 = Track::default();
let tid1 = "tid-1".to_string();
let tid2 = "tid-2".to_string();
t1.set_id(Some(tid1.clone()));
t2.set_id(Some(tid2.clone()));
let id_move = "id-move";
t1.items.push(make_clip_with_id(2.0, id_move));
t2.items.push(Item::Gap(Gap::make_gap(1.0)));
tl.tracks.children.push(t1);
tl.tracks.children.push(t2);
let ok = tl.tracks.move_item_at_time(
id_move,
&tid2,
1.0,
false,
InsertPolicy::InsertBeforeOrAfter,
OverlapPolicy::Override,
);
assert!(ok);
assert!(tl.tracks.children[0].items.is_empty());
let dest = &tl.tracks.children[1];
assert_eq!(dest.items.len(), 2);
let found = dest.items.iter().any(|it| it.get_id().as_deref() == Some(id_move));
assert!(found);
}
#[test]
fn moving_a_clip_preserves_an_adjacent_gap_id() {
let mut source = Track::new(TrackKind::Video, Some("source".to_string()));
source.items.push(make_clip_with_id(2.0, "moving"));
let mut trailing_gap = Item::Gap(Gap::make_gap(3.0));
trailing_gap.set_id(Some("trailing-gap".to_string()));
source.items.push(trailing_gap);
source.items.push(make_clip_with_id(1.0, "remaining"));
let destination = Track::new(TrackKind::Video, Some("destination".to_string()));
let mut stack = Stack::default();
stack.children = vec![source, destination];
assert!(stack.move_item_at_time(
"moving",
"destination",
0.0,
true,
InsertPolicy::SplitAndInsert,
OverlapPolicy::Override,
));
let (gap_track, gap_index, _) = stack.get_item("trailing-gap").expect("gap ID is stable");
assert_eq!(gap_track, 0);
assert_eq!(stack.children[gap_track].items[gap_index].duration(), 5.0);
assert!(stack.get_item("moving").is_some());
}