Files
oak-editor/crates/oak-otio/tests/semantic.rs
T
Mike-Solar 244d5e860f
CI / Build & test (Windows) (push) Failing after 7s
workspace: kebab-case crates, app under crates/oak-app, shared versions
All crates take the oak-* kebab-case naming (oak-audio, oak-codec,
oak-common, oak-core, oak-ffmpeg-link, oak-node, oak-otio, oak-plugin,
oak-render, oak-storage, oak-task, oak-timeline, oak-undo), with the
lib identifiers rewritten (oakrender:: -> oak_render::, oakcore_rs:: ->
oak_core::, ...) across all 226 referencing files.

The GUI application moves from the workspace root into
crates/oak-app/: src/, build.rs (paths fixed for the new location) and
tests/ travel with it, the root Cargo.toml becomes workspace-only
([workspace] + workspace.package + profiles), and the app package
inherits the workspace version. The screenshots example becomes a
standalone crate examples/simple_player/ with its own Cargo.toml.

Every crate now inherits the single workspace version
(version.workspace = true), and the workflows' crate paths and the
build docs follow the renames.

Validated with a clean cargo check --workspace.
2026-08-22 16:58:37 +08:00

167 lines
4.9 KiB
Rust

// Oak Video Editor - Non-Linear Video Editor
// Copyright (C) 2026 Oak Team
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//! Semantic tests over `golden_timeline.json`: walk the parsed object graph
//! and assert the values Oak's C++ load task depends on.
use oak_otio::{Clip, ExternalReference, MediaReference, Serializable, Timeline};
fn golden_timeline() -> Timeline {
let text = std::fs::read_to_string(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/data/golden_timeline.json"
))
.expect("read golden_timeline.json");
match oak_otio::from_json_string(&text).expect("parse golden_timeline.json") {
Serializable::Timeline(t) => t,
other => panic!("expected Timeline root, got {}", other.schema_name()),
}
}
#[test]
fn root_timeline_fields() {
let tl = golden_timeline();
assert_eq!(tl.name(), "My Sequence");
assert_eq!(tl.global_start_time(), None);
assert_eq!(tl.tracks().children().len(), 2);
}
#[test]
fn video_track_contents() {
let tl = golden_timeline();
let video = &tl.tracks().children()[0];
let track = video
.as_track()
.expect("first child of tracks stack is a Track");
assert_eq!(track.kind(), "Video");
assert_eq!(track.children().len(), 3);
// Clip -> Gap -> Transition, in order.
let clip = track.children()[0]
.as_clip()
.expect("video track child 0 is a Clip");
assert_eq!(clip.name(), "My Sequence Clip");
let range = clip.source_range().expect("clip has a source_range");
assert_eq!(
(range.duration().value(), range.duration().rate()),
(1152.0, 24.0)
);
assert_eq!(
(range.start_time().value(), range.start_time().rate()),
(0.0, 24.0)
);
let gap = track.children()[1]
.as_gap()
.expect("video track child 1 is a Gap");
assert_eq!(gap.name(), "My Sequence Gap");
let grange = gap.source_range().expect("gap has a source_range");
assert_eq!(
(grange.duration().value(), grange.duration().rate()),
(576.0, 24.0)
);
let trans = track.children()[2]
.as_transition()
.expect("video track child 2 is a Transition");
assert_eq!(trans.name(), "My Sequence Transition");
assert_eq!(
(trans.in_offset().value(), trans.in_offset().rate()),
(12.0, 24.0)
);
assert_eq!(
(trans.out_offset().value(), trans.out_offset().rate()),
(12.0, 24.0)
);
assert_eq!(trans.transition_type(), "");
}
#[test]
fn video_clip_media_reference() {
let tl = golden_timeline();
let clip = &tl.tracks().children()[0].as_track().unwrap().children()[0];
let clip = clip.as_clip().unwrap();
let reference = clip
.media_reference()
.expect("clip resolves a media reference");
assert_eq!(reference.schema_name(), "ExternalReference");
let external: &ExternalReference = match reference {
MediaReference::ExternalReference(e) => e,
other => panic!("expected ExternalReference, got {}", other.schema_name()),
};
assert_eq!(external.target_url(), "file:///tmp/My Sequence.mp4");
let available = external.available_range().expect("available_range is set");
assert_eq!(
(available.duration().value(), available.duration().rate()),
(100.0, 25.0)
);
assert_eq!(
(
available.start_time().value(),
available.start_time().rate()
),
(0.0, 25.0)
);
}
#[test]
fn audio_track_contents() {
let tl = golden_timeline();
let audio = &tl.tracks().children()[1];
let track = audio
.as_track()
.expect("second child of tracks stack is a Track");
assert_eq!(track.kind(), "Audio");
assert_eq!(track.children().len(), 2);
let clip: &Clip = track.children()[0]
.as_clip()
.expect("audio track child 0 is a Clip");
assert_eq!(clip.name(), "My Sequence Audio");
let external: &ExternalReference = match clip.media_reference().unwrap() {
MediaReference::ExternalReference(e) => e,
other => panic!("expected ExternalReference, got {}", other.schema_name()),
};
assert_eq!(external.target_url(), "file:///tmp/My Sequence.wav");
let available = external.available_range().unwrap();
assert_eq!(
(available.duration().value(), available.duration().rate()),
(0.0, 48000.0)
);
assert_eq!(
(
available.start_time().value(),
available.start_time().rate()
),
(0.0, 48000.0)
);
let gap = track.children()[1]
.as_gap()
.expect("audio track child 1 is a Gap");
assert_eq!(gap.name(), "");
let grange = gap.source_range().unwrap();
assert_eq!(
(grange.duration().value(), grange.duration().rate()),
(12.0, 1.0)
);
assert_eq!(
(grange.start_time().value(), grange.start_time().rate()),
(1152.0, 24.0)
);
}