feat(engine): clip move, clip effect_input, mandatory static FFmpeg
- oakengine_sequence_move_clip implemented for real (oaktimeline TrackMoveBlockCommand; fixes the graph-ownership/gap-anchor/ripple trim bugs the stub was hiding); same-track via the frozen C ABI, cross-track supported by the module command - oaknode clip blocks now declare a tex_in texture input and set effect_input to it, so timeline clips can host effect chains; facade test covers effect insert/remove on a real clip - oakffmpeg-link: FFMPEG_DIR is now mandatory with a clear panic (a Homebrew upgrade left the system ffmpeg .pc pointing at a deleted dav1d Cellar path, breaking links); reads a git-ignored workspace .env for IDEs that cannot inject env vars (RustRover); links the C++ stdlib for C++ codec libs (svt-av1) - oakengine re-exports oaknode so tests share one crate instance; it_node uses the direct instance's value type where it calls the module FFI (the --workspace dev-dependency feature split builds oaknode twice)
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#[test]
|
||||
fn dbg_dlsym() {
|
||||
let p = oaknode::bridge::dlsym::resolve("oaknode_xml_writer_init");
|
||||
eprintln!("writer init symbol: {:?}", p);
|
||||
let p2 = oaknode::bridge::dlsym::resolve("oakundo_command_init_multi");
|
||||
eprintln!("undo multi symbol: {:?}", p2);
|
||||
let p = oaknode::bridge::dlsym::resolve("oaknode_xml_writer_init");
|
||||
eprintln!("writer init symbol: {:?}", p);
|
||||
let p2 = oaknode::bridge::dlsym::resolve("oakundo_command_init_multi");
|
||||
eprintln!("undo multi symbol: {:?}", p2);
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@ use oaknode::input::{flags, Input, ValueHint};
|
||||
use oaknode::keyframe::{Interpolation, Keyframe};
|
||||
use oaknode::node::{Category, NodeBehavior, NodeCore};
|
||||
use oaknode::value::{
|
||||
oak, AudioParams, NodeValue, NodeValueTable, OakNodeValue, SampleBuffer, ValueType,
|
||||
VideoParams,
|
||||
oak, AudioParams, NodeValue, NodeValueTable, OakNodeValue, SampleBuffer, ValueType, VideoParams,
|
||||
};
|
||||
|
||||
fn float(v: f64) -> NodeValue {
|
||||
@@ -47,13 +46,22 @@ fn value_type_and_conversions() {
|
||||
assert_eq!(NodeValue::Color([0.0; 4]).value_type(), ValueType::Color);
|
||||
assert_eq!(NodeValue::Text("x".into()).value_type(), ValueType::Text);
|
||||
assert_eq!(NodeValue::Boolean(true).value_type(), ValueType::Boolean);
|
||||
assert_eq!(NodeValue::Samples(SampleBuffer::default()).value_type(), ValueType::Samples);
|
||||
assert_eq!(NodeValue::Rational(Rational::new(1, 2)).value_type(), ValueType::Rational);
|
||||
assert_eq!(
|
||||
NodeValue::Samples(SampleBuffer::default()).value_type(),
|
||||
ValueType::Samples
|
||||
);
|
||||
assert_eq!(
|
||||
NodeValue::Rational(Rational::new(1, 2)).value_type(),
|
||||
ValueType::Rational
|
||||
);
|
||||
assert_eq!(NodeValue::Vec2([0.0; 2]).value_type(), ValueType::Vec2);
|
||||
assert_eq!(NodeValue::Vec3([0.0; 3]).value_type(), ValueType::Vec3);
|
||||
assert_eq!(NodeValue::Vec4([0.0; 4]).value_type(), ValueType::Vec4);
|
||||
assert_eq!(NodeValue::Combo(0).value_type(), ValueType::Combo);
|
||||
assert_eq!(NodeValue::StrCombo("s".into()).value_type(), ValueType::StrCombo);
|
||||
assert_eq!(
|
||||
NodeValue::StrCombo("s".into()).value_type(),
|
||||
ValueType::StrCombo
|
||||
);
|
||||
assert_eq!(
|
||||
NodeValue::VideoParams(VideoParams::default()).value_type(),
|
||||
ValueType::VideoParams
|
||||
@@ -76,15 +84,16 @@ fn value_type_and_conversions() {
|
||||
assert_eq!(NodeValue::Color([7.0, 0.0, 0.0, 0.0]).to_double(), 7.0);
|
||||
assert_eq!(NodeValue::Boolean(true).to_double(), 1.0);
|
||||
assert_eq!(NodeValue::Boolean(false).to_double(), 0.0);
|
||||
assert_eq!(
|
||||
NodeValue::Rational(Rational::new(1, 4)).to_double(),
|
||||
0.25
|
||||
);
|
||||
assert_eq!(NodeValue::Rational(Rational::new(1, 4)).to_double(), 0.25);
|
||||
assert_eq!(NodeValue::Vec2([9.0, 0.0]).to_double(), 9.0);
|
||||
assert_eq!(NodeValue::Vec3([8.0, 0.0, 0.0]).to_double(), 8.0);
|
||||
assert_eq!(NodeValue::Vec4([6.0, 0.0, 0.0, 0.0]).to_double(), 6.0);
|
||||
assert_eq!(NodeValue::Combo(5).to_double(), 5.0);
|
||||
assert_eq!(NodeValue::Text("x".into()).to_double(), 0.0, "non-numeric -> 0");
|
||||
assert_eq!(
|
||||
NodeValue::Text("x".into()).to_double(),
|
||||
0.0,
|
||||
"non-numeric -> 0"
|
||||
);
|
||||
assert_eq!(NodeValue::None.to_double(), 0.0);
|
||||
|
||||
// can_interpolate.
|
||||
@@ -137,7 +146,10 @@ fn value_split_combine_lerp() {
|
||||
vec![float(1.0), float(2.0), float(3.0), float(4.0)]
|
||||
);
|
||||
// Scalar types hold the whole value in track 0.
|
||||
assert_eq!(float(9.0).split_into_tracks(ValueType::Float), vec![float(9.0)]);
|
||||
assert_eq!(
|
||||
float(9.0).split_into_tracks(ValueType::Float),
|
||||
vec![float(9.0)]
|
||||
);
|
||||
assert_eq!(
|
||||
NodeValue::Int(7).split_into_tracks(ValueType::Int),
|
||||
vec![NodeValue::Int(7)]
|
||||
@@ -168,14 +180,20 @@ fn value_split_combine_lerp() {
|
||||
NodeValue::combine_tracks(&[float(1.0)], ValueType::Vec2),
|
||||
NodeValue::Vec2([1.0, 0.0])
|
||||
);
|
||||
assert_eq!(NodeValue::combine_tracks(&[], ValueType::Float), NodeValue::None);
|
||||
assert_eq!(
|
||||
NodeValue::combine_tracks(&[], ValueType::Float),
|
||||
NodeValue::None
|
||||
);
|
||||
assert_eq!(
|
||||
NodeValue::combine_tracks(&[float(3.0)], ValueType::Float),
|
||||
float(3.0)
|
||||
);
|
||||
|
||||
// with_scalar for every declared type.
|
||||
assert_eq!(float(0.0).with_scalar(ValueType::Int, 4.0), NodeValue::Int(4));
|
||||
assert_eq!(
|
||||
float(0.0).with_scalar(ValueType::Int, 4.0),
|
||||
NodeValue::Int(4)
|
||||
);
|
||||
assert_eq!(float(0.0).with_scalar(ValueType::Float, 4.0), float(4.0));
|
||||
assert_eq!(
|
||||
float(0.0).with_scalar(ValueType::Boolean, 1.0),
|
||||
@@ -185,7 +203,10 @@ fn value_split_combine_lerp() {
|
||||
float(0.0).with_scalar(ValueType::Boolean, 0.0),
|
||||
NodeValue::Boolean(false)
|
||||
);
|
||||
assert_eq!(float(0.0).with_scalar(ValueType::Combo, 2.0), NodeValue::Combo(2));
|
||||
assert_eq!(
|
||||
float(0.0).with_scalar(ValueType::Combo, 2.0),
|
||||
NodeValue::Combo(2)
|
||||
);
|
||||
assert_eq!(
|
||||
float(0.0).with_scalar(ValueType::Color, 0.5),
|
||||
NodeValue::Color([0.5, 0.0, 0.0, 0.0])
|
||||
@@ -230,13 +251,21 @@ fn value_split_combine_lerp() {
|
||||
NodeValue::Color([0.0; 4]).lerp(&NodeValue::Color([2.0; 4]), 0.5),
|
||||
NodeValue::Color([1.0; 4])
|
||||
);
|
||||
match NodeValue::Rational(Rational::new(0, 1)).lerp(&NodeValue::Rational(Rational::new(1, 1)), 0.5) {
|
||||
match NodeValue::Rational(Rational::new(0, 1))
|
||||
.lerp(&NodeValue::Rational(Rational::new(1, 1)), 0.5)
|
||||
{
|
||||
NodeValue::Rational(r) => assert!((r.to_f64() - 0.5).abs() < eps),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
// Non-interpolable types snap to self.
|
||||
assert_eq!(NodeValue::Int(3).lerp(&NodeValue::Int(9), 0.5), NodeValue::Int(3));
|
||||
assert_eq!(NodeValue::Text("a".into()).lerp(&NodeValue::Text("b".into()), 0.5), NodeValue::Text("a".into()));
|
||||
assert_eq!(
|
||||
NodeValue::Int(3).lerp(&NodeValue::Int(9), 0.5),
|
||||
NodeValue::Int(3)
|
||||
);
|
||||
assert_eq!(
|
||||
NodeValue::Text("a".into()).lerp(&NodeValue::Text("b".into()), 0.5),
|
||||
NodeValue::Text("a".into())
|
||||
);
|
||||
}
|
||||
|
||||
/// value.rs: NodeValue equality + SampleBuffer default + Drop on
|
||||
@@ -259,7 +288,10 @@ fn value_equality_and_buffer() {
|
||||
assert_eq!(NodeValue::Vec3([0.0; 3]), NodeValue::Vec3([0.0; 3]));
|
||||
assert_eq!(NodeValue::Vec4([0.0; 4]), NodeValue::Vec4([0.0; 4]));
|
||||
assert_eq!(NodeValue::Combo(1), NodeValue::Combo(1));
|
||||
assert_eq!(NodeValue::StrCombo("s".into()), NodeValue::StrCombo("s".into()));
|
||||
assert_eq!(
|
||||
NodeValue::StrCombo("s".into()),
|
||||
NodeValue::StrCombo("s".into())
|
||||
);
|
||||
assert_eq!(
|
||||
NodeValue::VideoParams(VideoParams::default()),
|
||||
NodeValue::VideoParams(VideoParams::default())
|
||||
@@ -305,66 +337,116 @@ fn value_equality_and_buffer() {
|
||||
fn oaknode_value_pod_roundtrip() {
|
||||
// to_node_value for every POD kind.
|
||||
assert_eq!(
|
||||
OakNodeValue { kind: oak::INT, num: 42, den: 0, f: [0.0; 4] }
|
||||
.to_node_value(ValueType::Int)
|
||||
.unwrap(),
|
||||
OakNodeValue {
|
||||
kind: oak::INT,
|
||||
num: 42,
|
||||
den: 0,
|
||||
f: [0.0; 4]
|
||||
}
|
||||
.to_node_value(ValueType::Int)
|
||||
.unwrap(),
|
||||
NodeValue::Int(42)
|
||||
);
|
||||
assert_eq!(
|
||||
OakNodeValue { kind: oak::COMBO, num: 1, den: 0, f: [0.0; 4] }
|
||||
.to_node_value(ValueType::Combo)
|
||||
.unwrap(),
|
||||
OakNodeValue {
|
||||
kind: oak::COMBO,
|
||||
num: 1,
|
||||
den: 0,
|
||||
f: [0.0; 4]
|
||||
}
|
||||
.to_node_value(ValueType::Combo)
|
||||
.unwrap(),
|
||||
NodeValue::Int(1)
|
||||
);
|
||||
assert_eq!(
|
||||
OakNodeValue { kind: oak::FLOAT, num: 0, den: 0, f: [2.5, 0.0, 0.0, 0.0] }
|
||||
.to_node_value(ValueType::Float)
|
||||
.unwrap(),
|
||||
OakNodeValue {
|
||||
kind: oak::FLOAT,
|
||||
num: 0,
|
||||
den: 0,
|
||||
f: [2.5, 0.0, 0.0, 0.0]
|
||||
}
|
||||
.to_node_value(ValueType::Float)
|
||||
.unwrap(),
|
||||
float(2.5)
|
||||
);
|
||||
assert_eq!(
|
||||
OakNodeValue { kind: oak::BOOL, num: 1, den: 0, f: [0.0; 4] }
|
||||
.to_node_value(ValueType::Boolean)
|
||||
.unwrap(),
|
||||
OakNodeValue {
|
||||
kind: oak::BOOL,
|
||||
num: 1,
|
||||
den: 0,
|
||||
f: [0.0; 4]
|
||||
}
|
||||
.to_node_value(ValueType::Boolean)
|
||||
.unwrap(),
|
||||
NodeValue::Boolean(true)
|
||||
);
|
||||
assert_eq!(
|
||||
OakNodeValue { kind: oak::RATIONAL, num: 1, den: 2, f: [0.0; 4] }
|
||||
.to_node_value(ValueType::Rational)
|
||||
.unwrap(),
|
||||
OakNodeValue {
|
||||
kind: oak::RATIONAL,
|
||||
num: 1,
|
||||
den: 2,
|
||||
f: [0.0; 4]
|
||||
}
|
||||
.to_node_value(ValueType::Rational)
|
||||
.unwrap(),
|
||||
NodeValue::Rational(Rational::new(1, 2))
|
||||
);
|
||||
assert_eq!(
|
||||
OakNodeValue { kind: oak::COLOR, num: 0, den: 0, f: [1.0, 2.0, 3.0, 4.0] }
|
||||
.to_node_value(ValueType::Color)
|
||||
.unwrap(),
|
||||
OakNodeValue {
|
||||
kind: oak::COLOR,
|
||||
num: 0,
|
||||
den: 0,
|
||||
f: [1.0, 2.0, 3.0, 4.0]
|
||||
}
|
||||
.to_node_value(ValueType::Color)
|
||||
.unwrap(),
|
||||
NodeValue::Color([1.0, 2.0, 3.0, 4.0])
|
||||
);
|
||||
assert_eq!(
|
||||
OakNodeValue { kind: oak::VEC2, num: 0, den: 0, f: [1.0, 2.0, 0.0, 0.0] }
|
||||
.to_node_value(ValueType::Vec2)
|
||||
.unwrap(),
|
||||
OakNodeValue {
|
||||
kind: oak::VEC2,
|
||||
num: 0,
|
||||
den: 0,
|
||||
f: [1.0, 2.0, 0.0, 0.0]
|
||||
}
|
||||
.to_node_value(ValueType::Vec2)
|
||||
.unwrap(),
|
||||
NodeValue::Vec2([1.0, 2.0])
|
||||
);
|
||||
assert_eq!(
|
||||
OakNodeValue { kind: oak::VEC3, num: 0, den: 0, f: [1.0, 2.0, 3.0, 0.0] }
|
||||
.to_node_value(ValueType::Vec3)
|
||||
.unwrap(),
|
||||
OakNodeValue {
|
||||
kind: oak::VEC3,
|
||||
num: 0,
|
||||
den: 0,
|
||||
f: [1.0, 2.0, 3.0, 0.0]
|
||||
}
|
||||
.to_node_value(ValueType::Vec3)
|
||||
.unwrap(),
|
||||
NodeValue::Vec3([1.0, 2.0, 3.0])
|
||||
);
|
||||
assert_eq!(
|
||||
OakNodeValue { kind: oak::VEC4, num: 0, den: 0, f: [1.0, 2.0, 3.0, 4.0] }
|
||||
.to_node_value(ValueType::Vec4)
|
||||
.unwrap(),
|
||||
OakNodeValue {
|
||||
kind: oak::VEC4,
|
||||
num: 0,
|
||||
den: 0,
|
||||
f: [1.0, 2.0, 3.0, 4.0]
|
||||
}
|
||||
.to_node_value(ValueType::Vec4)
|
||||
.unwrap(),
|
||||
NodeValue::Vec4([1.0, 2.0, 3.0, 4.0])
|
||||
);
|
||||
// Invalid kinds are rejected.
|
||||
assert!(OakNodeValue::none().to_node_value(ValueType::Float).is_err());
|
||||
assert!(
|
||||
OakNodeValue { kind: oak::STRING, num: 0, den: 0, f: [0.0; 4] }
|
||||
.to_node_value(ValueType::Text)
|
||||
.is_err()
|
||||
);
|
||||
assert!(OakNodeValue::none()
|
||||
.to_node_value(ValueType::Float)
|
||||
.is_err());
|
||||
assert!(OakNodeValue {
|
||||
kind: oak::STRING,
|
||||
num: 0,
|
||||
den: 0,
|
||||
f: [0.0; 4]
|
||||
}
|
||||
.to_node_value(ValueType::Text)
|
||||
.is_err());
|
||||
|
||||
// from_node_value: round-trips and error arms.
|
||||
let pod = OakNodeValue::from_node_value(ValueType::Float, &float(3.0)).unwrap();
|
||||
@@ -389,25 +471,18 @@ fn oaknode_value_pod_roundtrip() {
|
||||
assert_eq!((pod.num, pod.den), (1, 1));
|
||||
let pod = OakNodeValue::from_node_value(ValueType::None, &NodeValue::None).unwrap();
|
||||
assert_eq!(pod.kind, oak::NONE);
|
||||
let pod = OakNodeValue::from_node_value(
|
||||
ValueType::Color,
|
||||
&NodeValue::Color([1.0, 2.0, 3.0, 4.0]),
|
||||
)
|
||||
.unwrap();
|
||||
let pod =
|
||||
OakNodeValue::from_node_value(ValueType::Color, &NodeValue::Color([1.0, 2.0, 3.0, 4.0]))
|
||||
.unwrap();
|
||||
assert_eq!(pod.f, [1.0, 2.0, 3.0, 4.0]);
|
||||
let pod = OakNodeValue::from_node_value(ValueType::Vec2, &NodeValue::Vec2([1.0, 2.0])).unwrap();
|
||||
assert_eq!((pod.f[0], pod.f[1]), (1.0, 2.0));
|
||||
let pod = OakNodeValue::from_node_value(
|
||||
ValueType::Vec3,
|
||||
&NodeValue::Vec3([1.0, 2.0, 3.0]),
|
||||
)
|
||||
.unwrap();
|
||||
let pod =
|
||||
OakNodeValue::from_node_value(ValueType::Vec3, &NodeValue::Vec3([1.0, 2.0, 3.0])).unwrap();
|
||||
assert_eq!(pod.f[2], 3.0);
|
||||
let pod = OakNodeValue::from_node_value(
|
||||
ValueType::Vec4,
|
||||
&NodeValue::Vec4([1.0, 2.0, 3.0, 4.0]),
|
||||
)
|
||||
.unwrap();
|
||||
let pod =
|
||||
OakNodeValue::from_node_value(ValueType::Vec4, &NodeValue::Vec4([1.0, 2.0, 3.0, 4.0]))
|
||||
.unwrap();
|
||||
assert_eq!(pod.f[3], 4.0);
|
||||
// Error arms: string declared -> Invalid; wrong payload -> Failed;
|
||||
// non-POD declared -> Failed.
|
||||
@@ -451,7 +526,11 @@ fn node_core_helpers() {
|
||||
assert_eq!(core.standard_value("val_in", -1), float(5.0));
|
||||
core.set_standard_value("val_in", 2, float(9.0));
|
||||
assert_eq!(core.standard_value("val_in", 2), float(9.0));
|
||||
assert_eq!(core.standard_value("val_in", 3), float(0.0), "unset element -> default");
|
||||
assert_eq!(
|
||||
core.standard_value("val_in", 3),
|
||||
float(0.0),
|
||||
"unset element -> default"
|
||||
);
|
||||
|
||||
// Array size / insert / remove.
|
||||
assert_eq!(core.input_array_size("val_in"), 0);
|
||||
@@ -460,18 +539,36 @@ fn node_core_helpers() {
|
||||
assert_eq!(core.input_array_size("val_in"), 2);
|
||||
core.set_standard_value("val_in", 1, float(7.0));
|
||||
core.input_array_insert("val_in", 0); // shifts element 1 -> 2
|
||||
assert_eq!(core.standard_value("val_in", 2), float(7.0), "values shift on insert");
|
||||
assert_eq!(core.standard_value("val_in", 1), float(0.0), "inserted slot cleared");
|
||||
assert_eq!(
|
||||
core.standard_value("val_in", 2),
|
||||
float(7.0),
|
||||
"values shift on insert"
|
||||
);
|
||||
assert_eq!(
|
||||
core.standard_value("val_in", 1),
|
||||
float(0.0),
|
||||
"inserted slot cleared"
|
||||
);
|
||||
core.input_array_remove("val_in", 0);
|
||||
assert_eq!(core.standard_value("val_in", 1), float(7.0), "values shift on remove");
|
||||
assert_eq!(
|
||||
core.standard_value("val_in", 1),
|
||||
float(7.0),
|
||||
"values shift on remove"
|
||||
);
|
||||
assert_eq!(core.input_array_size("val_in"), 2);
|
||||
core.input_array_remove("val_in", 99); // out of range: no-op
|
||||
assert_eq!(core.input_array_size("val_in"), 2);
|
||||
|
||||
// value_at_time uses the standard value when the track is empty
|
||||
// (element 1 holds 7.0 after the shifts above).
|
||||
assert_eq!(core.value_at_time("val_in", 1, Rational::new(0, 1)), float(7.0));
|
||||
assert_eq!(core.value_at_time("val_in", 9, Rational::new(0, 1)), float(0.0));
|
||||
assert_eq!(
|
||||
core.value_at_time("val_in", 1, Rational::new(0, 1)),
|
||||
float(7.0)
|
||||
);
|
||||
assert_eq!(
|
||||
core.value_at_time("val_in", 9, Rational::new(0, 1)),
|
||||
float(0.0)
|
||||
);
|
||||
|
||||
// Keyframe tracks.
|
||||
{
|
||||
@@ -502,11 +599,17 @@ fn node_core_helpers() {
|
||||
assert!(core.keyframe_track("val_in", 0).is_some());
|
||||
assert!(core.keyframe_track("val_in", 1).is_none());
|
||||
core.input_array_remove("val_in", 0);
|
||||
assert!(core.keyframe_track("val_in", 0).is_none(), "removed element drops its track");
|
||||
assert!(
|
||||
core.keyframe_track("val_in", 0).is_none(),
|
||||
"removed element drops its track"
|
||||
);
|
||||
assert!(core.keyframe_track("val_in", -1).is_some());
|
||||
assert!(core.keyframe_track("missing", -1).is_none());
|
||||
// value_at_time uses keyframes when the track is non-empty.
|
||||
assert_eq!(core.value_at_time("val_in", -1, Rational::new(0, 1)), float(1.0));
|
||||
assert_eq!(
|
||||
core.value_at_time("val_in", -1, Rational::new(0, 1)),
|
||||
float(1.0)
|
||||
);
|
||||
|
||||
// Value hints.
|
||||
assert!(core.value_hint("val_in", -1).is_none());
|
||||
@@ -532,7 +635,10 @@ fn node_core_helpers() {
|
||||
assert!(!core.context_contains(ctx));
|
||||
assert!(core.set_context_position(ctx, 1.0, 2.0, true));
|
||||
assert!(core.context_contains(ctx));
|
||||
assert!(!core.set_context_position(ctx, 3.0, 4.0, false), "replace returns false");
|
||||
assert!(
|
||||
!core.set_context_position(ctx, 3.0, 4.0, false),
|
||||
"replace returns false"
|
||||
);
|
||||
assert_eq!(core.context_positions.len(), 1);
|
||||
assert_eq!(core.context_positions[0].1, (3.0, 4.0));
|
||||
assert!(core.remove_from_context(ctx));
|
||||
@@ -574,7 +680,9 @@ fn node_behavior_defaults() {
|
||||
assert_eq!(b.input_name("x"), "x");
|
||||
assert_eq!(b.input_name("enabled_in"), "Enabled");
|
||||
assert_eq!(b.ignore_inputs_for_rendering(), &[] as &[String]);
|
||||
assert!(b.active_elements_at_time("in", Rational::new(0, 1)).is_empty());
|
||||
assert!(b
|
||||
.active_elements_at_time("in", Rational::new(0, 1))
|
||||
.is_empty());
|
||||
assert_eq!(b.video_cache_range(&core), TimeRange::default());
|
||||
assert_eq!(b.audio_cache_range(&core), TimeRange::default());
|
||||
assert!(b.value_hint_for_input("in").is_none());
|
||||
@@ -720,9 +828,15 @@ fn ops_category_and_copy_inputs() {
|
||||
g.connect(src, other, "other_in", -1).unwrap();
|
||||
|
||||
// Without connections: values copy, edges do not.
|
||||
g.get_mut(src).unwrap().core.set_standard_value("val_in", -1, float(42.0));
|
||||
g.get_mut(src)
|
||||
.unwrap()
|
||||
.core
|
||||
.set_standard_value("val_in", -1, float(42.0));
|
||||
ops::copy_inputs(&mut g, src, dst, false).unwrap();
|
||||
assert_eq!(g.get(dst).unwrap().core.standard_value("val_in", -1), float(42.0));
|
||||
assert_eq!(
|
||||
g.get(dst).unwrap().core.standard_value("val_in", -1),
|
||||
float(42.0)
|
||||
);
|
||||
assert!(!g.is_input_connected(dst, "val_in", -1));
|
||||
|
||||
// With connections: dst's matching input reconnects to src's sources.
|
||||
@@ -747,14 +861,8 @@ fn handle_helpers_and_guards() {
|
||||
assert!(unsafe { handle::get::<u32>(&null) }.is_none());
|
||||
|
||||
// guard: Ok -> OK; Err -> mapped code; panic -> E_FAILED.
|
||||
assert_eq!(
|
||||
handle::guard(|| Ok(())),
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(
|
||||
handle::guard(|| Err(Error::Invalid)),
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
assert_eq!(handle::guard(|| Ok(())), OAKNODE_OK);
|
||||
assert_eq!(handle::guard(|| Err(Error::Invalid)), OAKNODE_E_INVALID);
|
||||
assert_eq!(
|
||||
handle::guard(|| -> Result<(), Error> { panic!("boom") }),
|
||||
OAKNODE_E_FAILED
|
||||
@@ -763,8 +871,16 @@ fn handle_helpers_and_guards() {
|
||||
// guard_handle: Ok -> handle; Err/panic -> empty.
|
||||
let h = handle::guard_handle(|| Ok(handle::make_owned(5u32)));
|
||||
assert!(!h.ctx.is_null());
|
||||
assert!(handle::guard_handle(|| -> Result<CHandle, Error> { Err(Error::NotFound) }).ctx.is_null());
|
||||
assert!(handle::guard_handle(|| -> Result<CHandle, Error> { panic!("x") }).ctx.is_null());
|
||||
assert!(
|
||||
handle::guard_handle(|| -> Result<CHandle, Error> { Err(Error::NotFound) })
|
||||
.ctx
|
||||
.is_null()
|
||||
);
|
||||
assert!(
|
||||
handle::guard_handle(|| -> Result<CHandle, Error> { panic!("x") })
|
||||
.ctx
|
||||
.is_null()
|
||||
);
|
||||
|
||||
// guard_void swallows panics.
|
||||
handle::guard_void(|| panic!("swallowed"));
|
||||
@@ -791,7 +907,10 @@ fn handle_helpers_and_guards() {
|
||||
}
|
||||
}
|
||||
let custom = handle::make_owned_with("hello".to_string(), custom_release);
|
||||
assert_eq!(unsafe { handle::get::<String>(&custom) }, Some(&"hello".to_string()));
|
||||
assert_eq!(
|
||||
unsafe { handle::get::<String>(&custom) },
|
||||
Some(&"hello".to_string())
|
||||
);
|
||||
|
||||
// Release everything (single release each).
|
||||
for h in [owned, borrowed, custom] {
|
||||
@@ -804,7 +923,10 @@ fn handle_helpers_and_guards() {
|
||||
fn error_codes_map() {
|
||||
assert_eq!(Error::Invalid.code(), OAKNODE_E_INVALID);
|
||||
assert_eq!(Error::State.code(), oaknode::error::OAKNODE_E_STATE);
|
||||
assert_eq!(Error::Failed("x".to_string()).code(), oaknode::error::OAKNODE_E_FAILED);
|
||||
assert_eq!(
|
||||
Error::Failed("x".to_string()).code(),
|
||||
oaknode::error::OAKNODE_E_FAILED
|
||||
);
|
||||
assert_eq!(Error::NotFound.code(), oaknode::error::OAKNODE_E_NOT_FOUND);
|
||||
assert_eq!(Error::NoMem.code(), oaknode::error::OAKNODE_E_NOMEM);
|
||||
assert_eq!(oaknode::error::OAKNODE_OK, 0);
|
||||
@@ -823,7 +945,10 @@ fn node_id_identity_packing() {
|
||||
assert_eq!(gen.index(), 5);
|
||||
assert_eq!(NodeId::from_identity(gen.identity()), Some(gen));
|
||||
assert!(NodeId::from_identity(0xdead).is_some());
|
||||
assert!(NodeId::from_identity(u32::MAX as u64).is_none(), "invalid index rejected");
|
||||
assert!(
|
||||
NodeId::from_identity(u32::MAX as u64).is_none(),
|
||||
"invalid index rejected"
|
||||
);
|
||||
assert!(!NodeId::INVALID.valid());
|
||||
assert!(id.valid());
|
||||
}
|
||||
|
||||
@@ -90,9 +90,15 @@ fn registered_entries_match_cpp_order() {
|
||||
#[test]
|
||||
fn first_and_last_entries() {
|
||||
let entries = Factory::global().entries();
|
||||
assert_eq!(entries.first().unwrap().type_id, "org.olivevideoeditor.Olive.polygon");
|
||||
assert_eq!(
|
||||
entries.first().unwrap().type_id,
|
||||
"org.olivevideoeditor.Olive.polygon"
|
||||
);
|
||||
assert_eq!(entries.first().unwrap().name, "Polygon");
|
||||
assert_eq!(entries.last().unwrap().type_id, "org.olivevideoeditor.Olive.multicam");
|
||||
assert_eq!(
|
||||
entries.last().unwrap().type_id,
|
||||
"org.olivevideoeditor.Olive.multicam"
|
||||
);
|
||||
assert_eq!(entries.last().unwrap().name, "Multi-Cam");
|
||||
}
|
||||
|
||||
|
||||
@@ -41,16 +41,15 @@ use oaknode::ffi::node::{
|
||||
oaknode_node_get_input, oaknode_node_get_input_at_time, oaknode_node_get_input_name,
|
||||
oaknode_node_get_input_string, oaknode_node_get_label, oaknode_node_get_name,
|
||||
oaknode_node_get_override_color, oaknode_node_get_project, oaknode_node_identity,
|
||||
oaknode_node_input_array_insert, oaknode_node_input_array_remove,
|
||||
oaknode_node_input_count, oaknode_node_input_get_connected_node,
|
||||
oaknode_node_input_get_type, oaknode_node_input_id, oaknode_node_input_is_connectable,
|
||||
oaknode_node_input_is_connected, oaknode_node_is_enabled, oaknode_node_link,
|
||||
oaknode_node_link_at, oaknode_node_link_count, oaknode_node_output_connection_count,
|
||||
oaknode_node_output_connection_element_at, oaknode_node_output_connection_input_id_at,
|
||||
oaknode_node_output_connection_node_at, oaknode_node_remove_from_context,
|
||||
oaknode_node_set_context_position, oaknode_node_set_enabled, oaknode_node_set_input,
|
||||
oaknode_node_set_input_string, oaknode_node_set_label, oaknode_node_set_override_color,
|
||||
oaknode_node_set_value_hint_track, oaknode_node_unlink,
|
||||
oaknode_node_input_array_insert, oaknode_node_input_array_remove, oaknode_node_input_count,
|
||||
oaknode_node_input_get_connected_node, oaknode_node_input_get_type, oaknode_node_input_id,
|
||||
oaknode_node_input_is_connectable, oaknode_node_input_is_connected, oaknode_node_is_enabled,
|
||||
oaknode_node_link, oaknode_node_link_at, oaknode_node_link_count,
|
||||
oaknode_node_output_connection_count, oaknode_node_output_connection_element_at,
|
||||
oaknode_node_output_connection_input_id_at, oaknode_node_output_connection_node_at,
|
||||
oaknode_node_remove_from_context, oaknode_node_set_context_position, oaknode_node_set_enabled,
|
||||
oaknode_node_set_input, oaknode_node_set_input_string, oaknode_node_set_label,
|
||||
oaknode_node_set_override_color, oaknode_node_set_value_hint_track, oaknode_node_unlink,
|
||||
};
|
||||
use oaknode::ffi::project::{
|
||||
oaknode_project_add_node, oaknode_project_cache_path, oaknode_project_clear,
|
||||
@@ -127,11 +126,16 @@ fn handle_contract_all_exports() {
|
||||
let mut n0 = unsafe { oaknode_project_node_at(dup(&p), 0) };
|
||||
assert!(!n0.ctx.is_null());
|
||||
assert!(unsafe { oaknode_project_node_at(dup(&p), 1) }.ctx.is_null());
|
||||
assert!(unsafe { oaknode_project_node_at(dup(&p), -1) }.ctx.is_null());
|
||||
assert!(unsafe { oaknode_project_node_at(CHandle::null(), 0) }.ctx.is_null());
|
||||
assert!(unsafe { oaknode_project_node_at(dup(&p), -1) }
|
||||
.ctx
|
||||
.is_null());
|
||||
assert!(unsafe { oaknode_project_node_at(CHandle::null(), 0) }
|
||||
.ctx
|
||||
.is_null());
|
||||
|
||||
// Factory node handle: owned, valid.
|
||||
let mut math = unsafe { oaknode_factory_create_from_id(cs("org.olivevideoeditor.Olive.math").as_ptr()) };
|
||||
let mut math =
|
||||
unsafe { oaknode_factory_create_from_id(cs("org.olivevideoeditor.Olive.math").as_ptr()) };
|
||||
assert!(!math.ctx.is_null());
|
||||
assert_eq!(math.abi_version, 1);
|
||||
|
||||
@@ -188,8 +192,13 @@ fn two_stage_string_contract() {
|
||||
|
||||
// Short buffer truncates and NUL-terminates.
|
||||
let mut buf = [0u8; 4];
|
||||
let rc = unsafe { oaknode_project_name(dup(&p), buf.as_mut_ptr() as *mut c_char, buf.len() as i32) };
|
||||
assert_eq!(rc, "(untitled)".len() as i32 + 1, "required size regardless of buffer");
|
||||
let rc =
|
||||
unsafe { oaknode_project_name(dup(&p), buf.as_mut_ptr() as *mut c_char, buf.len() as i32) };
|
||||
assert_eq!(
|
||||
rc,
|
||||
"(untitled)".len() as i32 + 1,
|
||||
"required size regardless of buffer"
|
||||
);
|
||||
assert_eq!(&buf[..3], b"(un");
|
||||
assert_eq!(buf[3], 0);
|
||||
|
||||
@@ -205,33 +214,41 @@ fn two_stage_string_contract() {
|
||||
assert_eq!(String::from_utf8(buf).unwrap(), "(untitled)");
|
||||
|
||||
// filename: "" initially (size 1); after set_filename -> full path.
|
||||
let filename = two_stage(|buf, size| unsafe { oaknode_project_filename(dup(&p), buf, size) }).unwrap();
|
||||
let filename =
|
||||
two_stage(|buf, size| unsafe { oaknode_project_filename(dup(&p), buf, size) }).unwrap();
|
||||
assert_eq!(filename, "");
|
||||
unsafe { oaknode_project_set_filename(dup(&p), cs("/tmp/demo.ove").as_ptr()) };
|
||||
let filename = two_stage(|buf, size| unsafe { oaknode_project_filename(dup(&p), buf, size) }).unwrap();
|
||||
let filename =
|
||||
two_stage(|buf, size| unsafe { oaknode_project_filename(dup(&p), buf, size) }).unwrap();
|
||||
assert_eq!(filename, "/tmp/demo.ove");
|
||||
let pretty =
|
||||
two_stage(|buf, size| unsafe { oaknode_project_pretty_filename(dup(&p), buf, size) }).unwrap();
|
||||
two_stage(|buf, size| unsafe { oaknode_project_pretty_filename(dup(&p), buf, size) })
|
||||
.unwrap();
|
||||
assert_eq!(pretty, "/tmp/demo.ove");
|
||||
|
||||
// uuid is the 36-char braced format.
|
||||
let uuid = two_stage(|buf, size| unsafe { oaknode_project_get_uuid(dup(&p), buf, size) }).unwrap();
|
||||
let uuid =
|
||||
two_stage(|buf, size| unsafe { oaknode_project_get_uuid(dup(&p), buf, size) }).unwrap();
|
||||
assert_eq!(uuid.len(), 38);
|
||||
assert!(uuid.starts_with('{') && uuid.ends_with('}'));
|
||||
|
||||
// cache_path is a two-stage getter too.
|
||||
let _ = two_stage(|buf, size| unsafe { oaknode_project_cache_path(dup(&p), buf, size) }).unwrap();
|
||||
let _ =
|
||||
two_stage(|buf, size| unsafe { oaknode_project_cache_path(dup(&p), buf, size) }).unwrap();
|
||||
|
||||
// Node string getters.
|
||||
let mut math = unsafe { oaknode_factory_create_from_id(cs("org.olivevideoeditor.Olive.math").as_ptr()) };
|
||||
let mut math =
|
||||
unsafe { oaknode_factory_create_from_id(cs("org.olivevideoeditor.Olive.math").as_ptr()) };
|
||||
let id = two_stage(|buf, size| unsafe { oaknode_node_get_id(dup(&math), buf, size) }).unwrap();
|
||||
assert_eq!(id, "org.olivevideoeditor.Olive.math");
|
||||
let name = two_stage(|buf, size| unsafe { oaknode_node_get_name(dup(&math), buf, size) }).unwrap();
|
||||
let name =
|
||||
two_stage(|buf, size| unsafe { oaknode_node_get_name(dup(&math), buf, size) }).unwrap();
|
||||
assert_eq!(name, "Math");
|
||||
let label = two_stage(|buf, size| unsafe { oaknode_node_get_label(dup(&math), buf, size) }).unwrap();
|
||||
let label =
|
||||
two_stage(|buf, size| unsafe { oaknode_node_get_label(dup(&math), buf, size) }).unwrap();
|
||||
assert_eq!(label, "");
|
||||
let iname = two_stage(|buf, size| {
|
||||
unsafe { oaknode_node_get_input_name(dup(&math), cs("enabled_in").as_ptr(), buf, size) }
|
||||
let iname = two_stage(|buf, size| unsafe {
|
||||
oaknode_node_get_input_name(dup(&math), cs("enabled_in").as_ptr(), buf, size)
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(iname, "Enabled");
|
||||
@@ -245,7 +262,8 @@ fn two_stage_string_contract() {
|
||||
#[test]
|
||||
fn identity_registry_roundtrip() {
|
||||
let _g = ffi_lock();
|
||||
let mut math = unsafe { oaknode_factory_create_from_id(cs("org.olivevideoeditor.Olive.math").as_ptr()) };
|
||||
let mut math =
|
||||
unsafe { oaknode_factory_create_from_id(cs("org.olivevideoeditor.Olive.math").as_ptr()) };
|
||||
let id = unsafe { oaknode_node_identity(dup(&math)) };
|
||||
|
||||
// Round-trip: the looked-up node has the same type id.
|
||||
@@ -258,7 +276,10 @@ fn identity_registry_roundtrip() {
|
||||
// Move the node into a project: its identity is re-registered and
|
||||
// still resolvable while the project lives.
|
||||
let mut p = unsafe { oaknode_project_init() };
|
||||
assert_eq!(unsafe { oaknode_project_add_node(dup(&p), dup(&math)) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_add_node(dup(&p), dup(&math)) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
let id2 = unsafe { oaknode_node_identity(dup(&math)) };
|
||||
let mut back2 = unsafe { oaknode_node_from_identity(id2) };
|
||||
assert!(!back2.ctx.is_null());
|
||||
@@ -269,7 +290,10 @@ fn identity_registry_roundtrip() {
|
||||
unsafe { oaknode_node_free(&mut math) };
|
||||
unsafe { oaknode_project_free(&mut p) };
|
||||
let ghost = unsafe { oaknode_node_from_identity(id) };
|
||||
assert!(ghost.ctx.is_null(), "freed node must be rejected by from_identity");
|
||||
assert!(
|
||||
ghost.ctx.is_null(),
|
||||
"freed node must be rejected by from_identity"
|
||||
);
|
||||
let ghost2 = unsafe { oaknode_node_from_identity(id2) };
|
||||
assert!(ghost2.ctx.is_null());
|
||||
}
|
||||
@@ -326,13 +350,21 @@ fn project_family_contract() {
|
||||
let mut p = init_project();
|
||||
|
||||
// initialize: E_STATE on second call; E_INVALID on empty handle.
|
||||
assert_eq!(unsafe { oaknode_project_initialize(dup(&p)) }, oaknode::error::OAKNODE_E_STATE);
|
||||
assert_eq!(unsafe { oaknode_project_initialize(CHandle::null()) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_initialize(dup(&p)) },
|
||||
oaknode::error::OAKNODE_E_STATE
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_initialize(CHandle::null()) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
|
||||
// root: valid after initialize.
|
||||
let mut root = unsafe { oaknode_project_root(dup(&p)) };
|
||||
assert!(!root.ctx.is_null());
|
||||
assert!(unsafe { oaknode_project_root(CHandle::null()) }.ctx.is_null());
|
||||
assert!(unsafe { oaknode_project_root(CHandle::null()) }
|
||||
.ctx
|
||||
.is_null());
|
||||
unsafe { oaknode_node_free(&mut root) };
|
||||
|
||||
// name/filename/pretty/is_modified/is_new + empty-handle failures.
|
||||
@@ -344,28 +376,50 @@ fn project_family_contract() {
|
||||
unsafe { oaknode_project_filename(CHandle::null(), std::ptr::null_mut(), 0) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
assert_eq!(unsafe { oaknode_project_is_modified(CHandle::null()) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(unsafe { oaknode_project_is_new(CHandle::null()) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_is_modified(CHandle::null()) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_is_new(CHandle::null()) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
|
||||
// set_filename: success; NULL failure.
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_set_filename(dup(&p), cs("/tmp/x.ove").as_ptr()) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(unsafe { oaknode_project_set_filename(dup(&p), std::ptr::null()) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_set_filename(dup(&p), std::ptr::null()) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_set_filename(CHandle::null(), cs("x").as_ptr()) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
assert_eq!(unsafe { oaknode_project_is_new(dup(&p)) }, 0, "has a filename now");
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_is_new(dup(&p)) },
|
||||
0,
|
||||
"has a filename now"
|
||||
);
|
||||
|
||||
// modified flag.
|
||||
assert_eq!(unsafe { oaknode_project_set_modified(dup(&p), 1) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_set_modified(dup(&p), 1) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(unsafe { oaknode_project_is_modified(dup(&p)) }, 1);
|
||||
assert_eq!(unsafe { oaknode_project_set_modified(CHandle::null(), 0) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_set_modified(CHandle::null(), 0) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
|
||||
// cache settings.
|
||||
assert_eq!(unsafe { oaknode_project_get_cache_location_setting(dup(&p)) }, 0);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_get_cache_location_setting(dup(&p)) },
|
||||
0
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_set_cache_location_setting(dup(&p), 2) },
|
||||
OAKNODE_OK
|
||||
@@ -382,19 +436,27 @@ fn project_family_contract() {
|
||||
unsafe { oaknode_project_set_custom_cache_path(dup(&p), cs("/tmp/cache").as_ptr()) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
let custom = two_stage(|buf, size| unsafe { oaknode_project_get_custom_cache_path(dup(&p), buf, size) })
|
||||
.unwrap();
|
||||
let custom =
|
||||
two_stage(|buf, size| unsafe { oaknode_project_get_custom_cache_path(dup(&p), buf, size) })
|
||||
.unwrap();
|
||||
assert_eq!(custom, "/tmp/cache");
|
||||
unsafe { oaknode_project_set_custom_cache_path(dup(&p), std::ptr::null()) }; // NULL clears
|
||||
let custom = two_stage(|buf, size| unsafe { oaknode_project_get_custom_cache_path(dup(&p), buf, size) })
|
||||
.unwrap();
|
||||
let custom =
|
||||
two_stage(|buf, size| unsafe { oaknode_project_get_custom_cache_path(dup(&p), buf, size) })
|
||||
.unwrap();
|
||||
assert_eq!(custom, "");
|
||||
|
||||
// copy_settings: dst inherits src settings; empty handle fails.
|
||||
let mut p2 = unsafe { oaknode_project_init() };
|
||||
unsafe { oaknode_project_set_cache_location_setting(dup(&p2), 1) };
|
||||
assert_eq!(unsafe { oaknode_project_copy_settings(dup(&p), dup(&p2)) }, OAKNODE_OK);
|
||||
assert_eq!(unsafe { oaknode_project_get_cache_location_setting(dup(&p)) }, 1);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_copy_settings(dup(&p), dup(&p2)) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_get_cache_location_setting(dup(&p)) },
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_copy_settings(CHandle::null(), dup(&p2)) },
|
||||
OAKNODE_E_INVALID
|
||||
@@ -406,28 +468,60 @@ fn project_family_contract() {
|
||||
|
||||
// node add/remove/count/at.
|
||||
let base_count = unsafe { oaknode_project_node_count(dup(&p)) };
|
||||
let mut node =
|
||||
unsafe { oaknode_factory_create_from_id(cs("org.olivevideoeditor.Olive.solidgenerator").as_ptr()) };
|
||||
assert_eq!(unsafe { oaknode_project_add_node(dup(&p), dup(&node)) }, OAKNODE_OK);
|
||||
assert_eq!(unsafe { oaknode_project_node_count(dup(&p)) }, base_count + 1);
|
||||
assert_eq!(unsafe { oaknode_project_add_node(CHandle::null(), dup(&node)) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(unsafe { oaknode_project_add_node(dup(&p), CHandle::null()) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(unsafe { oaknode_project_remove_node(dup(&p), dup(&node)) }, OAKNODE_OK);
|
||||
let mut node = unsafe {
|
||||
oaknode_factory_create_from_id(cs("org.olivevideoeditor.Olive.solidgenerator").as_ptr())
|
||||
};
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_add_node(dup(&p), dup(&node)) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_node_count(dup(&p)) },
|
||||
base_count + 1
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_add_node(CHandle::null(), dup(&node)) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_add_node(dup(&p), CHandle::null()) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_remove_node(dup(&p), dup(&node)) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(unsafe { oaknode_project_node_count(dup(&p)) }, base_count);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_remove_node(dup(&p), dup(&node)) },
|
||||
OAKNODE_E_NOT_FOUND,
|
||||
"double remove fails"
|
||||
);
|
||||
assert_eq!(unsafe { oaknode_project_remove_node(CHandle::null(), dup(&node)) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(unsafe { oaknode_project_node_count(CHandle::null()) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_remove_node(CHandle::null(), dup(&node)) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_node_count(CHandle::null()) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
unsafe { oaknode_node_free(&mut node) };
|
||||
|
||||
// clear: resets; empty handle fails.
|
||||
assert_eq!(unsafe { oaknode_project_clear(CHandle::null()) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_clear(CHandle::null()) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
assert_eq!(unsafe { oaknode_project_clear(dup(&p)) }, OAKNODE_OK);
|
||||
assert!(unsafe { oaknode_project_root(dup(&p)) }.ctx.is_null(), "root gone after clear");
|
||||
assert_eq!(unsafe { oaknode_project_initialize(dup(&p)) }, OAKNODE_OK, "re-initializable");
|
||||
assert!(
|
||||
unsafe { oaknode_project_root(dup(&p)) }.ctx.is_null(),
|
||||
"root gone after clear"
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_project_initialize(dup(&p)) },
|
||||
OAKNODE_OK,
|
||||
"re-initializable"
|
||||
);
|
||||
|
||||
unsafe { oaknode_project_free(&mut p) };
|
||||
unsafe { oaknode_project_free(&mut p2) };
|
||||
@@ -449,26 +543,54 @@ fn node_family_contract() {
|
||||
unsafe { oaknode_project_add_node(dup(&p), dup(&b)) };
|
||||
|
||||
// Metadata.
|
||||
assert_eq!(unsafe { oaknode_node_get_id(CHandle::null(), std::ptr::null_mut(), 0) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(unsafe { oaknode_node_get_name(CHandle::null(), std::ptr::null_mut(), 0) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_get_id(CHandle::null(), std::ptr::null_mut(), 0) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_get_name(CHandle::null(), std::ptr::null_mut(), 0) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
let id = two_stage(|buf, size| unsafe { oaknode_node_get_id(dup(&a), buf, size) }).unwrap();
|
||||
assert_eq!(id, "org.olivevideoeditor.Olive.math");
|
||||
|
||||
// Label.
|
||||
assert_eq!(unsafe { oaknode_node_set_label(dup(&a), cs("my node").as_ptr()) }, OAKNODE_OK);
|
||||
let label = two_stage(|buf, size| unsafe { oaknode_node_get_label(dup(&a), buf, size) }).unwrap();
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_set_label(dup(&a), cs("my node").as_ptr()) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
let label =
|
||||
two_stage(|buf, size| unsafe { oaknode_node_get_label(dup(&a), buf, size) }).unwrap();
|
||||
assert_eq!(label, "my node");
|
||||
assert_eq!(unsafe { oaknode_node_set_label(CHandle::null(), cs("x").as_ptr()) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(unsafe { oaknode_node_set_label(dup(&a), std::ptr::null()) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_set_label(CHandle::null(), cs("x").as_ptr()) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_set_label(dup(&a), std::ptr::null()) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
|
||||
// Override color.
|
||||
let mut oc = 0;
|
||||
assert_eq!(unsafe { oaknode_node_get_override_color(dup(&a), &mut oc) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_get_override_color(dup(&a), &mut oc) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(oc, -1);
|
||||
assert_eq!(unsafe { oaknode_node_set_override_color(dup(&a), 3) }, OAKNODE_OK);
|
||||
assert_eq!(unsafe { oaknode_node_get_override_color(dup(&a), &mut oc) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_set_override_color(dup(&a), 3) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_get_override_color(dup(&a), &mut oc) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(oc, 3);
|
||||
assert_eq!(unsafe { oaknode_node_get_override_color(CHandle::null(), &mut oc) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_get_override_color(CHandle::null(), &mut oc) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_get_override_color(dup(&a), std::ptr::null_mut()) },
|
||||
OAKNODE_E_INVALID
|
||||
@@ -476,18 +598,31 @@ fn node_family_contract() {
|
||||
|
||||
// Enabled.
|
||||
let mut en = 0;
|
||||
assert_eq!(unsafe { oaknode_node_is_enabled(dup(&a), &mut en) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_is_enabled(dup(&a), &mut en) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(en, 1);
|
||||
assert_eq!(unsafe { oaknode_node_set_enabled(dup(&a), 0) }, OAKNODE_OK);
|
||||
assert_eq!(unsafe { oaknode_node_is_enabled(dup(&a), &mut en) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_is_enabled(dup(&a), &mut en) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(en, 0);
|
||||
assert_eq!(unsafe { oaknode_node_is_enabled(CHandle::null(), &mut en) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_is_enabled(CHandle::null(), &mut en) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
|
||||
// Input introspection.
|
||||
let mut count = 0;
|
||||
assert_eq!(unsafe { oaknode_node_input_count(dup(&a), &mut count) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_input_count(dup(&a), &mut count) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(count, 4, "enabled_in + method_in + param_a_in + param_b_in");
|
||||
let first = two_stage(|buf, size| unsafe { oaknode_node_input_id(dup(&a), 0, buf, size) }).unwrap();
|
||||
let first =
|
||||
two_stage(|buf, size| unsafe { oaknode_node_input_id(dup(&a), 0, buf, size) }).unwrap();
|
||||
assert_eq!(first, "enabled_in");
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_input_id(dup(&a), 99, std::ptr::null_mut(), 0) },
|
||||
@@ -509,7 +644,9 @@ fn node_family_contract() {
|
||||
OAKNODE_E_NOT_FOUND
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_input_get_type(dup(&a), cs("enabled_in").as_ptr(), std::ptr::null_mut()) },
|
||||
unsafe {
|
||||
oaknode_node_input_get_type(dup(&a), cs("enabled_in").as_ptr(), std::ptr::null_mut())
|
||||
},
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
|
||||
@@ -526,27 +663,35 @@ fn node_family_contract() {
|
||||
|
||||
let mut connectable = 0;
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_input_is_connectable(dup(&a), cs("param_a_in").as_ptr(), &mut connectable) },
|
||||
unsafe {
|
||||
oaknode_node_input_is_connectable(dup(&a), cs("param_a_in").as_ptr(), &mut connectable)
|
||||
},
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(connectable, 1);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_input_is_connectable(dup(&a), cs("method_in").as_ptr(), &mut connectable) },
|
||||
unsafe {
|
||||
oaknode_node_input_is_connectable(dup(&a), cs("method_in").as_ptr(), &mut connectable)
|
||||
},
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(connectable, 0, "method_in is not connectable");
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_input_is_connectable(dup(&a), cs("nope").as_ptr(), &mut connectable) },
|
||||
unsafe {
|
||||
oaknode_node_input_is_connectable(dup(&a), cs("nope").as_ptr(), &mut connectable)
|
||||
},
|
||||
OAKNODE_E_NOT_FOUND
|
||||
);
|
||||
|
||||
let iname = two_stage(|buf, size| {
|
||||
unsafe { oaknode_node_get_input_name(dup(&a), cs("enabled_in").as_ptr(), buf, size) }
|
||||
let iname = two_stage(|buf, size| unsafe {
|
||||
oaknode_node_get_input_name(dup(&a), cs("enabled_in").as_ptr(), buf, size)
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(iname, "Enabled");
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_get_input_name(dup(&a), cs("nope").as_ptr(), std::ptr::null_mut(), 0) },
|
||||
unsafe {
|
||||
oaknode_node_get_input_name(dup(&a), cs("nope").as_ptr(), std::ptr::null_mut(), 0)
|
||||
},
|
||||
OAKNODE_E_NOT_FOUND
|
||||
);
|
||||
|
||||
@@ -594,7 +739,9 @@ fn node_family_contract() {
|
||||
);
|
||||
|
||||
// String input: the timeformat node's format_in.
|
||||
let mut tf = unsafe { oaknode_factory_create_from_id(cs("org.olivevideoeditor.Olive.timeformat").as_ptr()) };
|
||||
let mut tf = unsafe {
|
||||
oaknode_factory_create_from_id(cs("org.olivevideoeditor.Olive.timeformat").as_ptr())
|
||||
};
|
||||
unsafe { oaknode_project_add_node(dup(&p), dup(&tf)) };
|
||||
let s = two_stage(|buf, size| unsafe {
|
||||
oaknode_node_get_input_string(dup(&tf), cs("format_in").as_ptr(), buf, size)
|
||||
@@ -602,7 +749,9 @@ fn node_family_contract() {
|
||||
.unwrap();
|
||||
assert_eq!(s, "hh:mm:ss");
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_set_input_string(dup(&tf), cs("format_in").as_ptr(), cs("yyyy").as_ptr()) },
|
||||
unsafe {
|
||||
oaknode_node_set_input_string(dup(&tf), cs("format_in").as_ptr(), cs("yyyy").as_ptr())
|
||||
},
|
||||
OAKNODE_OK
|
||||
);
|
||||
let s = two_stage(|buf, size| unsafe {
|
||||
@@ -612,15 +761,21 @@ fn node_family_contract() {
|
||||
assert_eq!(s, "yyyy");
|
||||
// Non-string input -> E_INVALID; unknown -> E_NOT_FOUND.
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_get_input_string(dup(&tf), cs("time_in").as_ptr(), std::ptr::null_mut(), 0) },
|
||||
unsafe {
|
||||
oaknode_node_get_input_string(dup(&tf), cs("time_in").as_ptr(), std::ptr::null_mut(), 0)
|
||||
},
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_set_input_string(dup(&tf), cs("time_in").as_ptr(), cs("x").as_ptr()) },
|
||||
unsafe {
|
||||
oaknode_node_set_input_string(dup(&tf), cs("time_in").as_ptr(), cs("x").as_ptr())
|
||||
},
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_get_input_string(dup(&tf), cs("nope").as_ptr(), std::ptr::null_mut(), 0) },
|
||||
unsafe {
|
||||
oaknode_node_get_input_string(dup(&tf), cs("nope").as_ptr(), std::ptr::null_mut(), 0)
|
||||
},
|
||||
OAKNODE_E_NOT_FOUND
|
||||
);
|
||||
|
||||
@@ -663,7 +818,8 @@ fn node_family_contract() {
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert!(!src.ctx.is_null());
|
||||
let src_id = two_stage(|buf, size| unsafe { oaknode_node_get_id(dup(&src), buf, size) }).unwrap();
|
||||
let src_id =
|
||||
two_stage(|buf, size| unsafe { oaknode_node_get_id(dup(&src), buf, size) }).unwrap();
|
||||
assert_eq!(src_id, "org.olivevideoeditor.Olive.math");
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_input_get_connected_node(dup(&b), cs("nope").as_ptr(), &mut src) },
|
||||
@@ -672,7 +828,10 @@ fn node_family_contract() {
|
||||
|
||||
// Output connections.
|
||||
let mut count = 0;
|
||||
assert_eq!(unsafe { oaknode_node_output_connection_count(dup(&a), &mut count) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_output_connection_count(dup(&a), &mut count) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(count, 1);
|
||||
let mut node_out = CHandle::null();
|
||||
assert_eq!(
|
||||
@@ -684,9 +843,10 @@ fn node_family_contract() {
|
||||
unsafe { oaknode_node_output_connection_node_at(dup(&a), 1, &mut node_out) },
|
||||
OAKNODE_E_NOT_FOUND
|
||||
);
|
||||
let input_id =
|
||||
two_stage(|buf, size| unsafe { oaknode_node_output_connection_input_id_at(dup(&a), 0, buf, size) })
|
||||
.unwrap();
|
||||
let input_id = two_stage(|buf, size| unsafe {
|
||||
oaknode_node_output_connection_input_id_at(dup(&a), 0, buf, size)
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(input_id, "rot_in");
|
||||
let mut element = 0;
|
||||
assert_eq!(
|
||||
@@ -712,22 +872,46 @@ fn node_family_contract() {
|
||||
|
||||
// Links.
|
||||
let mut linked = 0;
|
||||
assert_eq!(unsafe { oaknode_node_link(dup(&a), dup(&b), &mut linked) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_link(dup(&a), dup(&b), &mut linked) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(linked, 1);
|
||||
assert_eq!(unsafe { oaknode_node_link(dup(&a), dup(&b), &mut linked) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_link(dup(&a), dup(&b), &mut linked) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(linked, 0, "already linked");
|
||||
let mut linked_ok = 0;
|
||||
assert_eq!(unsafe { oaknode_node_are_linked(dup(&a), dup(&b), &mut linked_ok) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_are_linked(dup(&a), dup(&b), &mut linked_ok) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(linked_ok, 1);
|
||||
assert_eq!(unsafe { oaknode_node_link_count(dup(&a), &mut count) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_link_count(dup(&a), &mut count) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(count, 1);
|
||||
let mut link_node = CHandle::null();
|
||||
assert_eq!(unsafe { oaknode_node_link_at(dup(&a), 0, &mut link_node) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_link_at(dup(&a), 0, &mut link_node) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert!(!link_node.ctx.is_null());
|
||||
assert_eq!(unsafe { oaknode_node_link_at(dup(&a), 1, &mut link_node) }, OAKNODE_E_NOT_FOUND);
|
||||
assert_eq!(unsafe { oaknode_node_unlink(dup(&a), dup(&b), &mut linked) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_link_at(dup(&a), 1, &mut link_node) },
|
||||
OAKNODE_E_NOT_FOUND
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_unlink(dup(&a), dup(&b), &mut linked) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(linked, 1);
|
||||
assert_eq!(unsafe { oaknode_node_unlink(dup(&a), dup(&b), &mut linked) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_unlink(dup(&a), dup(&b), &mut linked) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(linked, 0, "already unlinked");
|
||||
|
||||
// Context positions.
|
||||
@@ -735,24 +919,37 @@ fn node_family_contract() {
|
||||
unsafe { oaknode_node_set_context_position(dup(&a), dup(&b), 10.0, 20.0, 1) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(unsafe { oaknode_node_context_count(dup(&a), &mut count) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_context_count(dup(&a), &mut count) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(count, 1);
|
||||
let mut x = 0.0;
|
||||
let mut y = 0.0;
|
||||
let mut expanded = 0;
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_get_context_position(dup(&a), dup(&b), &mut x, &mut y, &mut expanded) },
|
||||
unsafe {
|
||||
oaknode_node_get_context_position(dup(&a), dup(&b), &mut x, &mut y, &mut expanded)
|
||||
},
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!((x, y, expanded), (10.0, 20.0, 1));
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_get_context_position(dup(&a), dup(&a), &mut x, &mut y, &mut expanded) },
|
||||
unsafe {
|
||||
oaknode_node_get_context_position(dup(&a), dup(&a), &mut x, &mut y, &mut expanded)
|
||||
},
|
||||
OAKNODE_E_NOT_FOUND
|
||||
);
|
||||
let mut ctx_node = CHandle::null();
|
||||
assert_eq!(unsafe { oaknode_node_context_node_at(dup(&a), 0, &mut ctx_node) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_context_node_at(dup(&a), 0, &mut ctx_node) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert!(!ctx_node.ctx.is_null());
|
||||
assert_eq!(unsafe { oaknode_node_remove_from_context(dup(&a), dup(&b)) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_remove_from_context(dup(&a), dup(&b)) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_remove_from_context(dup(&a), dup(&b)) },
|
||||
OAKNODE_E_NOT_FOUND
|
||||
@@ -760,9 +957,15 @@ fn node_family_contract() {
|
||||
|
||||
// get_project.
|
||||
let mut proj = CHandle::null();
|
||||
assert_eq!(unsafe { oaknode_node_get_project(dup(&a), &mut proj) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_get_project(dup(&a), &mut proj) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert!(!proj.ctx.is_null());
|
||||
assert_eq!(unsafe { oaknode_node_get_project(CHandle::null(), &mut proj) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_get_project(CHandle::null(), &mut proj) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
|
||||
// Array input insert/remove on a non-array input fails E_INVALID;
|
||||
// on an unknown input E_NOT_FOUND.
|
||||
@@ -777,8 +980,14 @@ fn node_family_contract() {
|
||||
|
||||
// copy_inputs: dst (matrix) takes src (math)'s values where the ids
|
||||
// match (enabled_in only) and preserves its own otherwise.
|
||||
assert_eq!(unsafe { oaknode_node_copy_inputs(dup(&b), dup(&a), 0) }, OAKNODE_OK);
|
||||
assert_eq!(unsafe { oaknode_node_copy_inputs(CHandle::null(), dup(&a), 0) }, OAKNODE_E_INVALID);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_copy_inputs(dup(&b), dup(&a), 0) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_copy_inputs(CHandle::null(), dup(&a), 0) },
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
|
||||
// set_value_hint_track.
|
||||
assert_eq!(
|
||||
@@ -793,7 +1002,9 @@ fn node_family_contract() {
|
||||
// get_input_at_time: no keyframes -> standard value.
|
||||
let mut at = OakNodeValue::none();
|
||||
assert_eq!(
|
||||
unsafe { oaknode_node_get_input_at_time(dup(&a), cs("param_a_in").as_ptr(), 5, 1, &mut at) },
|
||||
unsafe {
|
||||
oaknode_node_get_input_at_time(dup(&a), cs("param_a_in").as_ptr(), 5, 1, &mut at)
|
||||
},
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert_eq!(at.kind, oak::FLOAT);
|
||||
@@ -804,7 +1015,13 @@ fn node_family_contract() {
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
oaknode_node_get_input_at_time(dup(&a), cs("param_a_in").as_ptr(), 5, 1, std::ptr::null_mut())
|
||||
oaknode_node_get_input_at_time(
|
||||
dup(&a),
|
||||
cs("param_a_in").as_ptr(),
|
||||
5,
|
||||
1,
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
},
|
||||
OAKNODE_E_INVALID
|
||||
);
|
||||
@@ -812,7 +1029,8 @@ fn node_family_contract() {
|
||||
// create_copy of a graph-owned node.
|
||||
let mut copy = unsafe { oaknode_node_create_copy(dup(&a)) };
|
||||
assert!(!copy.ctx.is_null());
|
||||
let copy_id = two_stage(|buf, size| unsafe { oaknode_node_get_id(dup(©), buf, size) }).unwrap();
|
||||
let copy_id =
|
||||
two_stage(|buf, size| unsafe { oaknode_node_get_id(dup(©), buf, size) }).unwrap();
|
||||
assert_eq!(copy_id, "org.olivevideoeditor.Olive.math");
|
||||
|
||||
// Cleanup (free every borrowed view and the owned nodes).
|
||||
@@ -833,7 +1051,11 @@ fn node_family_contract() {
|
||||
fn factory_and_keyframe_helpers() {
|
||||
let _g = ffi_lock();
|
||||
assert_eq!(unsafe { oaknode_factory_initialize() }, OAKNODE_OK);
|
||||
assert_eq!(unsafe { oaknode_factory_initialize() }, OAKNODE_OK, "idempotent");
|
||||
assert_eq!(
|
||||
unsafe { oaknode_factory_initialize() },
|
||||
OAKNODE_OK,
|
||||
"idempotent"
|
||||
);
|
||||
|
||||
let mut count = 0;
|
||||
assert_eq!(unsafe { oaknode_factory_id_count(&mut count) }, OAKNODE_OK);
|
||||
@@ -850,35 +1072,44 @@ fn factory_and_keyframe_helpers() {
|
||||
OAKNODE_E_NOT_FOUND
|
||||
);
|
||||
|
||||
let name = two_stage(|buf, size| {
|
||||
unsafe { oaknode_factory_name_from_id(cs("org.olivevideoeditor.Olive.pan").as_ptr(), buf, size) }
|
||||
let name = two_stage(|buf, size| unsafe {
|
||||
oaknode_factory_name_from_id(cs("org.olivevideoeditor.Olive.pan").as_ptr(), buf, size)
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(name, "Pan");
|
||||
let missing = two_stage(|buf, size| {
|
||||
unsafe { oaknode_factory_name_from_id(cs("org.example.missing").as_ptr(), buf, size) }
|
||||
let missing = two_stage(|buf, size| unsafe {
|
||||
oaknode_factory_name_from_id(cs("org.example.missing").as_ptr(), buf, size)
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(missing, "", "unknown id -> empty string");
|
||||
|
||||
// create_from_id: known -> valid, unknown -> empty.
|
||||
let mut node =
|
||||
unsafe { oaknode_factory_create_from_id(cs("org.olivevideoeditor.Olive.solidgenerator").as_ptr()) };
|
||||
let mut node = unsafe {
|
||||
oaknode_factory_create_from_id(cs("org.olivevideoeditor.Olive.solidgenerator").as_ptr())
|
||||
};
|
||||
assert!(!node.ctx.is_null());
|
||||
assert!(
|
||||
unsafe { oaknode_factory_create_from_id(cs("org.example.missing").as_ptr()) }
|
||||
.ctx
|
||||
.is_null()
|
||||
);
|
||||
assert!(unsafe { oaknode_factory_create_from_id(std::ptr::null()) }.ctx.is_null());
|
||||
assert!(unsafe { oaknode_factory_create_from_id(std::ptr::null()) }
|
||||
.ctx
|
||||
.is_null());
|
||||
|
||||
// node_at: borrowed prototype (index 5 = math, whose constructor is
|
||||
// implemented; prototypes at todo!()-constructor indices are only
|
||||
// reachable once Phase 3 lands); out-of-range -> E_NOT_FOUND.
|
||||
let mut proto = CHandle::null();
|
||||
assert_eq!(unsafe { oaknode_factory_node_at(5, &mut proto) }, OAKNODE_OK);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_factory_node_at(5, &mut proto) },
|
||||
OAKNODE_OK
|
||||
);
|
||||
assert!(!proto.ctx.is_null());
|
||||
assert_eq!(unsafe { oaknode_factory_node_at(count, &mut proto) }, OAKNODE_E_NOT_FOUND);
|
||||
assert_eq!(
|
||||
unsafe { oaknode_factory_node_at(count, &mut proto) },
|
||||
OAKNODE_E_NOT_FOUND
|
||||
);
|
||||
|
||||
// Pure keyframe helper: IN(0) <-> OUT(1), invalid -> E_INVALID.
|
||||
assert_eq!(unsafe { oaknode_keyframe_opposing_bezier_type(0) }, 1);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -89,7 +89,10 @@ fn block_behaviors_and_inputs() {
|
||||
let (core, behavior) = oaknode::block::transition_create();
|
||||
assert!(core.has_input("out_block_in"));
|
||||
assert!(core.has_input("in_block_in"));
|
||||
assert_eq!(behavior.type_id(), "org.olivevideoeditor.Olive.transitionblock");
|
||||
assert_eq!(
|
||||
behavior.type_id(),
|
||||
"org.olivevideoeditor.Olive.transitionblock"
|
||||
);
|
||||
let t = TransitionBlockBehavior::new();
|
||||
assert_eq!(t.in_offset, Rational::new(0, 1));
|
||||
assert_eq!(t.out_offset, Rational::new(0, 1));
|
||||
@@ -132,12 +135,21 @@ fn track_behavior_ops() {
|
||||
assert!(track.insert_block_after(b2, b0));
|
||||
assert_eq!(track.blocks, vec![b0, b2, b1]);
|
||||
let missing = NodeId::from_identity(99).unwrap();
|
||||
assert!(!track.insert_block_after(b2, missing), "missing ref rejected");
|
||||
assert!(!track.insert_block_before(b1, missing), "missing ref rejected");
|
||||
assert!(
|
||||
!track.insert_block_after(b2, missing),
|
||||
"missing ref rejected"
|
||||
);
|
||||
assert!(
|
||||
!track.insert_block_before(b1, missing),
|
||||
"missing ref rejected"
|
||||
);
|
||||
let mut track = TrackBehavior::new(TrackType::Audio);
|
||||
track.append_block(b0);
|
||||
track.append_block(b1);
|
||||
assert!(track.insert_block_after(b2, b0), "insert after a present ref");
|
||||
assert!(
|
||||
track.insert_block_after(b2, b0),
|
||||
"insert after a present ref"
|
||||
);
|
||||
assert_eq!(track.blocks, vec![b0, b2, b1]);
|
||||
track.insert_block_at_index(NodeId::from_identity(7).unwrap(), 99); // clamped
|
||||
|
||||
@@ -185,7 +197,11 @@ fn tracklist_and_height() {
|
||||
list.tracks.push(NodeId::from_identity(5).unwrap());
|
||||
assert_eq!(list.track_at(0), Some(NodeId::from_identity(2).unwrap()));
|
||||
assert_eq!(list.track_index(NodeId::from_identity(5).unwrap()), Some(1));
|
||||
assert_eq!(list.total_length(&Ranges), Rational::new(6, 1), "longest track");
|
||||
assert_eq!(
|
||||
list.total_length(&Ranges),
|
||||
Rational::new(6, 1),
|
||||
"longest track"
|
||||
);
|
||||
|
||||
// Height conversions (C++ Track::internal_height_to_pixel_height).
|
||||
assert_eq!(
|
||||
@@ -232,7 +248,11 @@ fn footage_queries() {
|
||||
assert_eq!(f.audio_stream_count(), 1);
|
||||
assert_eq!(f.subtitle_stream_count(), 0);
|
||||
assert_eq!(f.duration(), Rational::new(30, 1));
|
||||
assert_eq!(f.video_length(), Rational::new(20, 1), "longest video stream");
|
||||
assert_eq!(
|
||||
f.video_length(),
|
||||
Rational::new(20, 1),
|
||||
"longest video stream"
|
||||
);
|
||||
assert!(f.video_params(1).is_some());
|
||||
assert!(f.video_params(5).is_none());
|
||||
assert!(f.audio_params(0).is_some());
|
||||
@@ -279,9 +299,15 @@ fn serializer_value_codecs() {
|
||||
(ValueType::Int, NodeValue::Int(7)),
|
||||
(ValueType::Combo, NodeValue::Combo(2)),
|
||||
(ValueType::Boolean, NodeValue::Boolean(true)),
|
||||
(ValueType::Rational, NodeValue::Rational(Rational::new(3, 4))),
|
||||
(
|
||||
ValueType::Rational,
|
||||
NodeValue::Rational(Rational::new(3, 4)),
|
||||
),
|
||||
(ValueType::Text, NodeValue::Text("hello".to_string())),
|
||||
(ValueType::StrCombo, NodeValue::StrCombo("choice".to_string())),
|
||||
(
|
||||
ValueType::StrCombo,
|
||||
NodeValue::StrCombo("choice".to_string()),
|
||||
),
|
||||
];
|
||||
for (declared, value) in cases {
|
||||
let text = value_to_string(declared, &value, true);
|
||||
@@ -357,14 +383,34 @@ fn undo_command_roundtrip() {
|
||||
.expect("undo stub available");
|
||||
|
||||
// redo applies, undo reverts; redo_now is idempotent.
|
||||
assert_eq!(oaknode::bridge::undo::command_redo_now(cmd.clone()).unwrap(), 0);
|
||||
assert_eq!(
|
||||
oaknode::bridge::undo::command_redo_now(cmd.clone()).unwrap(),
|
||||
0
|
||||
);
|
||||
assert_eq!(value_check.load(Ordering::SeqCst), 1);
|
||||
assert_eq!(oaknode::bridge::undo::command_redo_now(cmd.clone()).unwrap(), 0);
|
||||
assert_eq!(value_check.load(Ordering::SeqCst), 1, "redo no-ops when done");
|
||||
assert_eq!(oaknode::bridge::undo::command_undo_now(cmd.clone()).unwrap(), 0);
|
||||
assert_eq!(
|
||||
oaknode::bridge::undo::command_redo_now(cmd.clone()).unwrap(),
|
||||
0
|
||||
);
|
||||
assert_eq!(
|
||||
value_check.load(Ordering::SeqCst),
|
||||
1,
|
||||
"redo no-ops when done"
|
||||
);
|
||||
assert_eq!(
|
||||
oaknode::bridge::undo::command_undo_now(cmd.clone()).unwrap(),
|
||||
0
|
||||
);
|
||||
assert_eq!(value_check.load(Ordering::SeqCst), 0);
|
||||
assert_eq!(oaknode::bridge::undo::command_undo_now(cmd.clone()).unwrap(), 0);
|
||||
assert_eq!(value_check.load(Ordering::SeqCst), 0, "undo no-ops when not done");
|
||||
assert_eq!(
|
||||
oaknode::bridge::undo::command_undo_now(cmd.clone()).unwrap(),
|
||||
0
|
||||
);
|
||||
assert_eq!(
|
||||
value_check.load(Ordering::SeqCst),
|
||||
0,
|
||||
"undo no-ops when not done"
|
||||
);
|
||||
|
||||
// Multi command batches children.
|
||||
let mut multi = oaknode::bridge::undo::command_init_multi().unwrap();
|
||||
@@ -387,9 +433,15 @@ fn undo_command_roundtrip() {
|
||||
oaknode::bridge::undo::command_multi_add_child(multi.clone(), child.clone()).unwrap(),
|
||||
0
|
||||
);
|
||||
assert_eq!(oaknode::bridge::undo::command_redo_now(multi.clone()).unwrap(), 0);
|
||||
assert_eq!(
|
||||
oaknode::bridge::undo::command_redo_now(multi.clone()).unwrap(),
|
||||
0
|
||||
);
|
||||
assert_eq!(value_check.load(Ordering::SeqCst), 1);
|
||||
assert_eq!(oaknode::bridge::undo::command_undo_now(multi.clone()).unwrap(), 0);
|
||||
assert_eq!(
|
||||
oaknode::bridge::undo::command_undo_now(multi.clone()).unwrap(),
|
||||
0
|
||||
);
|
||||
assert_eq!(value_check.load(Ordering::SeqCst), 0);
|
||||
|
||||
oaknode::bridge::undo::command_free(&mut cmd);
|
||||
@@ -434,7 +486,10 @@ fn track_edge_cases() {
|
||||
|
||||
let mut track = TrackBehavior::new(TrackType::Subtitle);
|
||||
assert_eq!(track.name(), "Subtitle Track");
|
||||
assert!(!track.insert_block_before(b1, b9), "absent reference rejected");
|
||||
assert!(
|
||||
!track.insert_block_before(b1, b9),
|
||||
"absent reference rejected"
|
||||
);
|
||||
track.append_block(b0);
|
||||
assert!(track.insert_block_before(b1, b0));
|
||||
assert_eq!(track.blocks, vec![b1, b0]);
|
||||
@@ -444,8 +499,14 @@ fn track_edge_cases() {
|
||||
// Empty-track range queries.
|
||||
let empty = TrackBehavior::new(TrackType::Video);
|
||||
assert_eq!(empty.length(&Ranges), Rational::new(0, 1));
|
||||
assert_eq!(empty.block_containing_time(Rational::new(1, 1), &Ranges), None);
|
||||
assert_eq!(empty.visible_block_at_time(Rational::new(1, 1), &Ranges), None);
|
||||
assert_eq!(
|
||||
empty.block_containing_time(Rational::new(1, 1), &Ranges),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
empty.visible_block_at_time(Rational::new(1, 1), &Ranges),
|
||||
None
|
||||
);
|
||||
assert!(empty.is_range_free(
|
||||
TimeRange::new(Rational::new(0, 1), Rational::new(1, 1)),
|
||||
&Ranges
|
||||
@@ -454,7 +515,11 @@ fn track_edge_cases() {
|
||||
|
||||
// duplicate preserves the block list and kind.
|
||||
let dup = track.duplicate(&oaknode::node::NodeCore::new()).unwrap();
|
||||
let d = dup.as_any().unwrap().downcast_ref::<TrackBehavior>().unwrap();
|
||||
let d = dup
|
||||
.as_any()
|
||||
.unwrap()
|
||||
.downcast_ref::<TrackBehavior>()
|
||||
.unwrap();
|
||||
assert_eq!(d.blocks, vec![b1, b0]);
|
||||
assert_eq!(d.kind, TrackType::Subtitle);
|
||||
assert_eq!(dup.type_id(), "org.olivevideoeditor.Olive.track");
|
||||
@@ -467,7 +532,11 @@ fn track_edge_cases() {
|
||||
assert_eq!(list.total_length(&Tr), Rational::new(0, 1));
|
||||
list.tracks.push(b0);
|
||||
let dup = list.duplicate(&oaknode::node::NodeCore::new()).unwrap();
|
||||
let d = dup.as_any().unwrap().downcast_ref::<TrackListBehavior>().unwrap();
|
||||
let d = dup
|
||||
.as_any()
|
||||
.unwrap()
|
||||
.downcast_ref::<TrackListBehavior>()
|
||||
.unwrap();
|
||||
assert_eq!(d.tracks, vec![b0]);
|
||||
assert_eq!(d.kind, TrackType::Video);
|
||||
assert_eq!(dup.type_id(), "org.olivevideoeditor.Olive.tracklist");
|
||||
@@ -501,7 +570,11 @@ fn footage_edge_cases() {
|
||||
// duplicate preserves the fields.
|
||||
f.set_proxy("/p2.mov", 1, 0, 0, true);
|
||||
let dup = f.duplicate(&oaknode::node::NodeCore::new()).unwrap();
|
||||
let d = dup.as_any().unwrap().downcast_ref::<FootageBehavior>().unwrap();
|
||||
let d = dup
|
||||
.as_any()
|
||||
.unwrap()
|
||||
.downcast_ref::<FootageBehavior>()
|
||||
.unwrap();
|
||||
assert_eq!(d.filename, "nonexistent.mov");
|
||||
assert_eq!(d.proxy, "/p2.mov");
|
||||
assert_eq!(d.proxy_state, 1);
|
||||
@@ -531,7 +604,11 @@ fn sequence_edge_cases() {
|
||||
assert_eq!(seq.audio_stream_count(), 1, "default audio params");
|
||||
|
||||
let dup = seq.duplicate(&oaknode::node::NodeCore::new()).unwrap();
|
||||
let d = dup.as_any().unwrap().downcast_ref::<SequenceBehavior>().unwrap();
|
||||
let d = dup
|
||||
.as_any()
|
||||
.unwrap()
|
||||
.downcast_ref::<SequenceBehavior>()
|
||||
.unwrap();
|
||||
// C++ `copy()` clones without values/params; the duplicate is empty.
|
||||
assert_eq!(d.video_stream_count(), 0);
|
||||
let def = SequenceBehavior::default();
|
||||
@@ -564,10 +641,18 @@ fn group_behavior_edges() {
|
||||
{
|
||||
let p = project.lock().unwrap();
|
||||
let entry = p.graph.get(group_id).unwrap();
|
||||
let gb = entry.behavior.as_any().unwrap().downcast_ref::<NodeGroup>().unwrap();
|
||||
let gb = entry
|
||||
.behavior
|
||||
.as_any()
|
||||
.unwrap()
|
||||
.downcast_ref::<NodeGroup>()
|
||||
.unwrap();
|
||||
assert_eq!(gb.name(), "Group");
|
||||
assert_eq!(gb.type_id(), "org.olivevideoeditor.Olive.group");
|
||||
assert_eq!(gb.description(), "A group of nodes that is represented as a single node.");
|
||||
assert_eq!(
|
||||
gb.description(),
|
||||
"A group of nodes that is represented as a single node."
|
||||
);
|
||||
assert!(gb.categories().is_empty());
|
||||
assert_eq!(gb.output_passthrough(), None);
|
||||
assert!(gb.passthroughs().is_empty());
|
||||
@@ -576,11 +661,14 @@ fn group_behavior_edges() {
|
||||
input: "param_a_in".to_string(),
|
||||
element: -1,
|
||||
}));
|
||||
assert_eq!(gb.id_of_passthrough(&InnerInput {
|
||||
node: inner_id,
|
||||
input: "param_a_in".to_string(),
|
||||
element: -1,
|
||||
}), "");
|
||||
assert_eq!(
|
||||
gb.id_of_passthrough(&InnerInput {
|
||||
node: inner_id,
|
||||
input: "param_a_in".to_string(),
|
||||
element: -1,
|
||||
}),
|
||||
""
|
||||
);
|
||||
assert_eq!(gb.input_name("param_a_in"), "param_a_in");
|
||||
assert_eq!(gb.input_from_id("param_a_in"), None);
|
||||
}
|
||||
@@ -611,22 +699,39 @@ fn group_behavior_edges() {
|
||||
.unwrap()
|
||||
.clone();
|
||||
let entry = p.graph.get_mut(group_id).unwrap();
|
||||
let gb = entry.behavior.as_any_mut().unwrap().downcast_mut::<NodeGroup>().unwrap();
|
||||
let gb = entry
|
||||
.behavior
|
||||
.as_any_mut()
|
||||
.unwrap()
|
||||
.downcast_mut::<NodeGroup>()
|
||||
.unwrap();
|
||||
let id1 = gb.add_input_passthrough(
|
||||
&mut entry.core,
|
||||
InnerInput { node: inner_id, input: "param_a_in".to_string(), element: -1 },
|
||||
InnerInput {
|
||||
node: inner_id,
|
||||
input: "param_a_in".to_string(),
|
||||
element: -1,
|
||||
},
|
||||
"",
|
||||
&descriptor,
|
||||
);
|
||||
let id2 = gb.add_input_passthrough(
|
||||
&mut entry.core,
|
||||
InnerInput { node: inner2, input: "param_a_in".to_string(), element: -1 },
|
||||
InnerInput {
|
||||
node: inner2,
|
||||
input: "param_a_in".to_string(),
|
||||
element: -1,
|
||||
},
|
||||
"",
|
||||
&descriptor2,
|
||||
);
|
||||
let id3 = gb.add_input_passthrough(
|
||||
&mut entry.core,
|
||||
InnerInput { node: inner2, input: "param_b_in".to_string(), element: -1 },
|
||||
InnerInput {
|
||||
node: inner2,
|
||||
input: "param_b_in".to_string(),
|
||||
element: -1,
|
||||
},
|
||||
"forced",
|
||||
&descriptor2,
|
||||
);
|
||||
@@ -640,8 +745,17 @@ fn group_behavior_edges() {
|
||||
{
|
||||
let p = project.lock().unwrap();
|
||||
let entry = p.graph.get(group_id).unwrap();
|
||||
let gb = entry.behavior.as_any().unwrap().downcast_ref::<NodeGroup>().unwrap();
|
||||
let target = InnerInput { node: inner_id, input: "param_a_in".to_string(), element: -1 };
|
||||
let gb = entry
|
||||
.behavior
|
||||
.as_any()
|
||||
.unwrap()
|
||||
.downcast_ref::<NodeGroup>()
|
||||
.unwrap();
|
||||
let target = InnerInput {
|
||||
node: inner_id,
|
||||
input: "param_a_in".to_string(),
|
||||
element: -1,
|
||||
};
|
||||
assert!(gb.contains_input_passthrough(&target));
|
||||
assert_eq!(gb.id_of_passthrough(&target), "param_a_in");
|
||||
assert!(gb.input_from_id("param_a_in").is_some());
|
||||
@@ -652,28 +766,51 @@ fn group_behavior_edges() {
|
||||
// resolve_input follows it to the end.
|
||||
{
|
||||
let p = project.lock().unwrap();
|
||||
let mut input = InnerInput { node: group_id, input: "param_a_in".to_string(), element: -1 };
|
||||
let mut input = InnerInput {
|
||||
node: group_id,
|
||||
input: "param_a_in".to_string(),
|
||||
element: -1,
|
||||
};
|
||||
assert!(NodeGroup::get_inner(&p.graph, &mut input));
|
||||
assert_eq!(input.node, inner_id);
|
||||
assert_eq!(input.input, "param_a_in");
|
||||
let resolved = NodeGroup::resolve_input(
|
||||
&p.graph,
|
||||
InnerInput { node: group_id, input: "param_a_in".to_string(), element: -1 },
|
||||
InnerInput {
|
||||
node: group_id,
|
||||
input: "param_a_in".to_string(),
|
||||
element: -1,
|
||||
},
|
||||
);
|
||||
assert_eq!(resolved.node, inner_id);
|
||||
// A non-group node does not resolve through.
|
||||
let mut noop = InnerInput { node: inner_id, input: "param_a_in".to_string(), element: -1 };
|
||||
let mut noop = InnerInput {
|
||||
node: inner_id,
|
||||
input: "param_a_in".to_string(),
|
||||
element: -1,
|
||||
};
|
||||
assert!(!NodeGroup::get_inner(&p.graph, &mut noop));
|
||||
}
|
||||
|
||||
// duplicate clones the passthrough table and output reference.
|
||||
{
|
||||
let mut p = project.lock().unwrap();
|
||||
p.graph.get_mut(group_id).unwrap().behavior.as_any_mut().unwrap()
|
||||
.downcast_mut::<NodeGroup>().unwrap()
|
||||
p.graph
|
||||
.get_mut(group_id)
|
||||
.unwrap()
|
||||
.behavior
|
||||
.as_any_mut()
|
||||
.unwrap()
|
||||
.downcast_mut::<NodeGroup>()
|
||||
.unwrap()
|
||||
.set_output_passthrough(Some(inner_id));
|
||||
let entry = p.graph.get(group_id).unwrap();
|
||||
let gb = entry.behavior.as_any().unwrap().downcast_ref::<NodeGroup>().unwrap();
|
||||
let gb = entry
|
||||
.behavior
|
||||
.as_any()
|
||||
.unwrap()
|
||||
.downcast_ref::<NodeGroup>()
|
||||
.unwrap();
|
||||
assert_eq!(gb.output_passthrough(), Some(inner_id));
|
||||
let dup = gb.duplicate(&entry.core).unwrap();
|
||||
let d = dup.as_any().unwrap().downcast_ref::<NodeGroup>().unwrap();
|
||||
@@ -685,10 +822,19 @@ fn group_behavior_edges() {
|
||||
{
|
||||
let mut p = project.lock().unwrap();
|
||||
let entry = p.graph.get_mut(group_id).unwrap();
|
||||
let gb = entry.behavior.as_any_mut().unwrap().downcast_mut::<NodeGroup>().unwrap();
|
||||
let gb = entry
|
||||
.behavior
|
||||
.as_any_mut()
|
||||
.unwrap()
|
||||
.downcast_mut::<NodeGroup>()
|
||||
.unwrap();
|
||||
gb.remove_input_passthrough(
|
||||
&mut entry.core,
|
||||
&InnerInput { node: inner_id, input: "param_a_in".to_string(), element: -1 },
|
||||
&InnerInput {
|
||||
node: inner_id,
|
||||
input: "param_a_in".to_string(),
|
||||
element: -1,
|
||||
},
|
||||
);
|
||||
assert_eq!(gb.passthroughs().len(), 2);
|
||||
}
|
||||
@@ -703,7 +849,11 @@ fn group_serialization() {
|
||||
use oaknode::nodes::group::NodeGroup;
|
||||
|
||||
let mut gb = oaknode::nodes::group::create().1;
|
||||
let gb = gb.as_any_mut().unwrap().downcast_mut::<NodeGroup>().unwrap();
|
||||
let gb = gb
|
||||
.as_any_mut()
|
||||
.unwrap()
|
||||
.downcast_mut::<NodeGroup>()
|
||||
.unwrap();
|
||||
|
||||
// save_custom writes the passthrough elements.
|
||||
let mut writer = oaknode::serializer::XmlWriterBridge::new().unwrap();
|
||||
@@ -717,7 +867,11 @@ fn group_serialization() {
|
||||
|
||||
// load_custom parses them back (node references deferred).
|
||||
let mut gb = oaknode::nodes::group::create().1;
|
||||
let gb = gb.as_any_mut().unwrap().downcast_mut::<NodeGroup>().unwrap();
|
||||
let gb = gb
|
||||
.as_any_mut()
|
||||
.unwrap()
|
||||
.downcast_mut::<NodeGroup>()
|
||||
.unwrap();
|
||||
let mut reader = oaknode::serializer::XmlReaderBridge::new(&xml).unwrap();
|
||||
{
|
||||
use oaknode::node::NodeBehavior;
|
||||
|
||||
@@ -42,10 +42,10 @@ fn save_matches_cpp_byte_exact() {
|
||||
#[cfg(feature = "test-stubs")]
|
||||
#[test]
|
||||
fn roundtrip_idempotent() {
|
||||
use oaknode::project::Project;
|
||||
use oaknode::node::NodeCore;
|
||||
use oaknode::value::{NodeValue, ValueType};
|
||||
use oaknode::input::Input;
|
||||
use oaknode::node::NodeCore;
|
||||
use oaknode::project::Project;
|
||||
use oaknode::value::{NodeValue, ValueType};
|
||||
|
||||
let project = Project::new();
|
||||
{
|
||||
@@ -57,11 +57,11 @@ fn roundtrip_idempotent() {
|
||||
.unwrap()
|
||||
.create)();
|
||||
let id = p.graph.add_node(core, behavior);
|
||||
p.graph
|
||||
.get_mut(id)
|
||||
.unwrap()
|
||||
.core
|
||||
.set_standard_value("param_a_in", -1, NodeValue::Float(2.5));
|
||||
p.graph.get_mut(id).unwrap().core.set_standard_value(
|
||||
"param_a_in",
|
||||
-1,
|
||||
NodeValue::Float(2.5),
|
||||
);
|
||||
p.graph
|
||||
.get_mut(id)
|
||||
.unwrap()
|
||||
@@ -116,7 +116,8 @@ fn roundtrip_idempotent() {
|
||||
#[test]
|
||||
fn historical_versions_upgrade() {
|
||||
// A current-format document round-trips.
|
||||
let xml = "<project version=\"1\"><uuid>{test}</uuid><nodes></nodes><settings></settings></project>";
|
||||
let xml =
|
||||
"<project version=\"1\"><uuid>{test}</uuid><nodes></nodes><settings></settings></project>";
|
||||
let project = oaknode::serializer::load(xml).unwrap();
|
||||
assert_eq!(project.lock().unwrap().uuid, "{test}");
|
||||
|
||||
|
||||
@@ -180,12 +180,11 @@ fn project_sync_copy_consistency() {
|
||||
// 1. add a new node c + edge c->b, 2. change a's value.
|
||||
let c = add_test_node(&mut original.graph);
|
||||
original.graph.connect(c, b, "val_in2", -1).unwrap();
|
||||
original
|
||||
.graph
|
||||
.get_mut(a)
|
||||
.unwrap()
|
||||
.core
|
||||
.set_standard_value("val_in", -1, NodeValue::Float(11.0));
|
||||
original.graph.get_mut(a).unwrap().core.set_standard_value(
|
||||
"val_in",
|
||||
-1,
|
||||
NodeValue::Float(11.0),
|
||||
);
|
||||
|
||||
let changes = [
|
||||
ChangeRecord::NodeAdded(c),
|
||||
@@ -228,20 +227,20 @@ fn project_sync_copy_consistency() {
|
||||
drop(copy_guard);
|
||||
let fresh = original.deep_copy().unwrap();
|
||||
let fresh_guard = fresh.lock().unwrap();
|
||||
assert_eq!(
|
||||
fresh_guard.graph.node_count(),
|
||||
original.graph.node_count()
|
||||
);
|
||||
assert_eq!(fresh_guard.graph.node_count(), original.graph.node_count());
|
||||
assert_eq!(
|
||||
fresh_guard.graph.output_connections_all().len(),
|
||||
original.graph.output_connections_all().len()
|
||||
);
|
||||
assert_eq!(fresh_guard.graph.connected_output(b, "val_in", -1), Some(a));
|
||||
assert_eq!(
|
||||
fresh_guard.graph.connected_output(b, "val_in", -1),
|
||||
Some(a)
|
||||
);
|
||||
assert_eq!(
|
||||
fresh_guard.graph.get(a).unwrap().core.standard_value("val_in", -1).to_double(),
|
||||
fresh_guard
|
||||
.graph
|
||||
.get(a)
|
||||
.unwrap()
|
||||
.core
|
||||
.standard_value("val_in", -1)
|
||||
.to_double(),
|
||||
11.0
|
||||
);
|
||||
}
|
||||
@@ -274,8 +273,8 @@ fn sequence_default_structure() {
|
||||
/// stay consistent (C++ Track semantics).
|
||||
#[test]
|
||||
fn track_block_ordering() {
|
||||
use oaknode::track::{BlockRange, TrackBehavior, TrackType};
|
||||
use oakcore_rs::{Rational, TimeRange};
|
||||
use oaknode::track::{BlockRange, TrackBehavior, TrackType};
|
||||
|
||||
struct Ranges;
|
||||
impl BlockRange for Ranges {
|
||||
@@ -328,13 +327,13 @@ fn track_block_ordering() {
|
||||
/// caches).
|
||||
#[test]
|
||||
fn clip_cache_passthrough() {
|
||||
use oaknode::ffi::block::oaknode_clip_add_cache_passthrough_from;
|
||||
use oaknode::error::OAKNODE_OK;
|
||||
use oaknode::ffi::block::oaknode_block_clip_create;
|
||||
use oaknode::ffi::block::oaknode_block_free;
|
||||
use oaknode::ffi::project::oaknode_project_init;
|
||||
use oaknode::ffi::block::oaknode_clip_add_cache_passthrough_from;
|
||||
use oaknode::ffi::project::oaknode_project_free;
|
||||
use oaknode::ffi::project::oaknode_project_init;
|
||||
use oaknode::handle::CHandle;
|
||||
use oaknode::error::OAKNODE_OK;
|
||||
|
||||
let mut p = unsafe { oaknode_project_init() };
|
||||
let mut clip = unsafe { oaknode_block_clip_create() };
|
||||
@@ -356,9 +355,9 @@ fn clip_cache_passthrough() {
|
||||
/// gracefully without partial state); proxy fields, counts, duration.
|
||||
#[test]
|
||||
fn footage_probe() {
|
||||
use oakcore_rs::Rational;
|
||||
use oaknode::footage::{FootageBehavior, StreamInfo};
|
||||
use oaknode::value::VideoParams;
|
||||
use oakcore_rs::Rational;
|
||||
|
||||
let mut f = FootageBehavior::new("/nonexistent/file.mov");
|
||||
assert!(!f.valid);
|
||||
|
||||
@@ -55,9 +55,22 @@ impl NodeBehavior for Inc {
|
||||
fn duplicate(&self, _c: &NodeCore) -> Option<Box<dyn NodeBehavior>> {
|
||||
Some(Box::new(Inc))
|
||||
}
|
||||
fn value(&self, _c: &NodeCore, inputs: &NodeValueRow, _t: Rational, table: &mut NodeValueTable) {
|
||||
let v = inputs.get("val_in").cloned().unwrap_or(NodeValue::Float(0.0));
|
||||
table.push(ValueType::Float, NodeValue::Float(v.to_double() + 1.0), None);
|
||||
fn value(
|
||||
&self,
|
||||
_c: &NodeCore,
|
||||
inputs: &NodeValueRow,
|
||||
_t: Rational,
|
||||
table: &mut NodeValueTable,
|
||||
) {
|
||||
let v = inputs
|
||||
.get("val_in")
|
||||
.cloned()
|
||||
.unwrap_or(NodeValue::Float(0.0));
|
||||
table.push(
|
||||
ValueType::Float,
|
||||
NodeValue::Float(v.to_double() + 1.0),
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,8 +97,16 @@ impl NodeBehavior for Count {
|
||||
|
||||
fn node_with_input(g: &mut Graph, behavior: Box<dyn NodeBehavior>) -> NodeId {
|
||||
let mut core = NodeCore::new();
|
||||
core.add_input(Input::new("val_in", ValueType::Float, NodeValue::Float(0.0)));
|
||||
core.add_input(Input::new("val_in2", ValueType::Float, NodeValue::Float(0.0)));
|
||||
core.add_input(Input::new(
|
||||
"val_in",
|
||||
ValueType::Float,
|
||||
NodeValue::Float(0.0),
|
||||
));
|
||||
core.add_input(Input::new(
|
||||
"val_in2",
|
||||
ValueType::Float,
|
||||
NodeValue::Float(0.0),
|
||||
));
|
||||
g.add_node(core, behavior)
|
||||
}
|
||||
|
||||
@@ -129,9 +150,12 @@ fn diamond_evaluates_shared_node_once() {
|
||||
let mut core = NodeCore::new();
|
||||
core.add_input(Input::new("a", ValueType::Int, NodeValue::Int(0)));
|
||||
core.add_input(Input::new("b", ValueType::Int, NodeValue::Int(0)));
|
||||
g.add_node(core, Box::new(Count(std::sync::Arc::new(
|
||||
std::sync::atomic::AtomicUsize::new(0),
|
||||
))))
|
||||
g.add_node(
|
||||
core,
|
||||
Box::new(Count(std::sync::Arc::new(
|
||||
std::sync::atomic::AtomicUsize::new(0),
|
||||
))),
|
||||
)
|
||||
};
|
||||
g.connect(shared, leaf, "a", -1).unwrap();
|
||||
g.connect(shared, leaf, "b", -1).unwrap();
|
||||
@@ -198,7 +222,11 @@ fn invalidation_fanout() {
|
||||
g.connect(c, d, "val_in2", -1).unwrap();
|
||||
|
||||
let mut t = Traverser::new();
|
||||
t.invalidate_downstream(&g, a, TimeRange::new(Rational::new(0, 1), Rational::new(1, 1)));
|
||||
t.invalidate_downstream(
|
||||
&g,
|
||||
a,
|
||||
TimeRange::new(Rational::new(0, 1), Rational::new(1, 1)),
|
||||
);
|
||||
let walked = t.last_invalidation();
|
||||
assert_eq!(walked.len(), 4, "a, b, c, d each exactly once");
|
||||
assert!(walked.contains(&a) && walked.contains(&d));
|
||||
|
||||
@@ -18,10 +18,10 @@
|
||||
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use oakcore_rs::Rational;
|
||||
use oaknode::handle::{self, CHandle, RefBox};
|
||||
use oaknode::keyframe::{Interpolation, Keyframe, KeyframeTrack};
|
||||
use oaknode::value::{NodeValue, NodeValueTable, ValueType};
|
||||
use oakcore_rs::Rational;
|
||||
|
||||
/// NodeValueTable: last-push-wins per type, tag preserved, `get` of an
|
||||
/// absent type returns None (C++ NodeValueTable semantics).
|
||||
@@ -61,7 +61,11 @@ fn texture_value_drop_releases() {
|
||||
// bitwise; addref is the caller's contract).
|
||||
unsafe { (h.addref.unwrap())(h.ctx) };
|
||||
let refs = |h: &CHandle| -> u32 {
|
||||
unsafe { (*(h.ctx as *const RefBox<u32>)).refs.load(Ordering::Relaxed) }
|
||||
unsafe {
|
||||
(*(h.ctx as *const RefBox<u32>))
|
||||
.refs
|
||||
.load(Ordering::Relaxed)
|
||||
}
|
||||
};
|
||||
assert_eq!(refs(&h), 2);
|
||||
|
||||
@@ -84,7 +88,11 @@ fn texture_value_drop_releases() {
|
||||
fn texture_value_clone_addrefs() {
|
||||
let h = handle::make_owned(7u32);
|
||||
let refs = |h: &CHandle| -> u32 {
|
||||
unsafe { (*(h.ctx as *const RefBox<u32>)).refs.load(Ordering::Relaxed) }
|
||||
unsafe {
|
||||
(*(h.ctx as *const RefBox<u32>))
|
||||
.refs
|
||||
.load(Ordering::Relaxed)
|
||||
}
|
||||
};
|
||||
assert_eq!(refs(&h), 1);
|
||||
|
||||
@@ -114,7 +122,14 @@ fn keyframe_track_ordering() {
|
||||
track.set_key(key(10, 1.0));
|
||||
track.set_key(key(0, 0.0));
|
||||
track.set_key(key(5, 0.5));
|
||||
assert_eq!(times(&track), vec![Rational::new(0, 1), Rational::new(5, 1), Rational::new(10, 1)]);
|
||||
assert_eq!(
|
||||
times(&track),
|
||||
vec![
|
||||
Rational::new(0, 1),
|
||||
Rational::new(5, 1),
|
||||
Rational::new(10, 1)
|
||||
]
|
||||
);
|
||||
|
||||
// Replace at an existing time overwrites in place (still sorted).
|
||||
track.set_key(key(5, 9.0));
|
||||
@@ -201,7 +216,11 @@ fn interpolation_matches_cpp() {
|
||||
});
|
||||
quad.set_key(key(4, 4.0));
|
||||
let v = quad.value_at(Rational::new(2, 1)).unwrap();
|
||||
assert!((v.to_double() - 2.0).abs() < eps, "quadratic midpoint: {}", v.to_double());
|
||||
assert!(
|
||||
(v.to_double() - 2.0).abs() < eps,
|
||||
"quadratic midpoint: {}",
|
||||
v.to_double()
|
||||
);
|
||||
|
||||
// Rational type re-quantizes through Rational::from_double.
|
||||
let mut rt = KeyframeTrack::default();
|
||||
@@ -258,16 +277,31 @@ fn keyframe_edge_cases() {
|
||||
|
||||
let mut one = KeyframeTrack::default();
|
||||
one.set_key(key(5, 3.0));
|
||||
assert_eq!(one.value_at(Rational::new(0, 1)), Some(NodeValue::Float(3.0)));
|
||||
assert_eq!(one.value_at(Rational::new(5, 1)), Some(NodeValue::Float(3.0)));
|
||||
assert_eq!(one.value_at(Rational::new(99, 1)), Some(NodeValue::Float(3.0)));
|
||||
assert_eq!(
|
||||
one.value_at(Rational::new(0, 1)),
|
||||
Some(NodeValue::Float(3.0))
|
||||
);
|
||||
assert_eq!(
|
||||
one.value_at(Rational::new(5, 1)),
|
||||
Some(NodeValue::Float(3.0))
|
||||
);
|
||||
assert_eq!(
|
||||
one.value_at(Rational::new(99, 1)),
|
||||
Some(NodeValue::Float(3.0))
|
||||
);
|
||||
|
||||
// Exact key time returns the exact key value (before-holds branch).
|
||||
let mut two = KeyframeTrack::default();
|
||||
two.set_key(key(0, 1.0));
|
||||
two.set_key(key(10, 2.0));
|
||||
assert_eq!(two.value_at(Rational::new(0, 1)), Some(NodeValue::Float(1.0)));
|
||||
assert_eq!(two.value_at(Rational::new(10, 1)), Some(NodeValue::Float(2.0)));
|
||||
assert_eq!(
|
||||
two.value_at(Rational::new(0, 1)),
|
||||
Some(NodeValue::Float(1.0))
|
||||
);
|
||||
assert_eq!(
|
||||
two.value_at(Rational::new(10, 1)),
|
||||
Some(NodeValue::Float(2.0))
|
||||
);
|
||||
}
|
||||
|
||||
fn key(time: i64, value: f64) -> Keyframe {
|
||||
|
||||
Reference in New Issue
Block a user