Files
oak-editor/crates/oakplugin/tests/ofx_roundtrip.rs
T
Mike-Solar 9003b78176 feat: built-in effect params, clip click-select, effect drag-and-drop, project load with plugins
- serializer resolves node types through the factory's dynamic
  (runtime-registered OpenFX) entries, so a project carrying plugin
  nodes loads again (was: "unknown node type"); covered by a new
  CI-gated round-trip test driving the real fixture plugin
- built-in effect nodes expose their inputs as inspector parameters
  like the C++ parameter editor: localized input names from the
  behavior, combo option tables via the new
  NodeBehavior::input_combo_strings (16 nodes, string-for-string from
  the C++ set_combo_box_strings), connection/data inputs excluded
- effect library: live drag-and-drop — onto the inspector's effect
  stack (lands at the indicator position) and onto the node editor
  canvas (creates the node at the drop point); double-click still
  appends to the selected clip
- inspector parameter controls are no longer recreated per render
  (gpui stack view caches them per effect), so sliders drag and
  checkboxes click; the view observes the engine and silently re-syncs
  values (undo/redo land on the widgets)
- timeline: left-press selects clips (plain/keep-multi/Ctrl-Cmd
  toggle); clip moves clamp the shared delta so no clip of a linked
  group lands before frame 0 instead of failing with "invalid move
  target"
- oakplugin: createInstance-rejected instances skip the destroyInstance
  notification (the plugin never owned them); vendor-suite fetchSuite
  misses moved behind OAK_OFX_TRACE; the worker logs the discovered/
  registered plugin counts
- CI: the OFX probe step also runs the serialization round-trip test
- gpui submodule: params view caching, clip click-select, library
  drag payload, graph_position_at
2026-08-20 23:46:40 +08:00

75 lines
3.0 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/>.
//! OFX plugin project serialization round-trip (CI-gated): a project that
//! carries a plugin node must save to XML and load back with the same
//! plugin type id — the serializer resolves plugin types through the
//! factory's dynamic (runtime-registered) entries.
//!
//! Gated on `OAK_OFX_FIXTURE_DIR` pointing at a directory with a built
//! `OakCiTest.ofx.bundle` (see `tests/fixtures/build_fixture.sh`); the
//! test skips silently when the variable is unset so plain `cargo test`
//! runs stay hermetic. The CI workflow builds the fixture and sets it.
use oakplugin::host::Host;
/// The fixture plugin's type id (tests/fixtures/ci_test_plugin.c).
const FIXTURE_TYPE_ID: &str = "rs.oak.CiTestPlugin";
#[test]
fn plugin_node_survives_save_load_roundtrip() {
let Ok(fixture_dir) = std::env::var("OAK_OFX_FIXTURE_DIR") else {
eprintln!("OAK_OFX_FIXTURE_DIR unset; skipping the OFX round-trip test");
return;
};
let host = Host::global();
host.cache
.scan_path(std::path::Path::new(&fixture_dir))
.expect("fixture dir scans");
let registered = oakplugin::node_factory::register_plugin_nodes();
assert!(
registered.iter().any(|id| id == FIXTURE_TYPE_ID),
"the fixture plugin registered (got {registered:?})"
);
// Build a project carrying one plugin node.
let project = oaknode::project::Project::new();
let node_id = {
let mut p = project.lock().unwrap_or_else(|e| e.into_inner());
let (core, behavior) = oaknode::factory::Factory::global()
.create_any(FIXTURE_TYPE_ID)
.expect("the fixture type resolves through the factory");
p.graph.add_node(core, behavior)
};
// Save, wipe, reload: the type id must resolve again (this is the path
// that used to fail with "unknown node type" for plugin nodes).
let xml = {
let p = project.lock().unwrap_or_else(|e| e.into_inner());
oaknode::serializer::save(&p).expect("project saves")
};
let loaded = oaknode::serializer::load(&xml).expect("project with a plugin node loads");
let p = loaded.lock().unwrap_or_else(|e| e.into_inner());
let mut found = false;
for id in p.graph.node_ids() {
let entry = p.graph.get(id).expect("listed node exists");
if entry.behavior.type_id() == FIXTURE_TYPE_ID {
found = true;
}
}
assert!(found, "the plugin node survived the round trip (node {node_id:?})");
}