Files
oak-editor/crates/oakundo/tests/edge_coverage_test.rs
T
Mike-Solar 194d761ade refactor(oakundo): replace the CHandle vtable layer with owned trait objects
With the C ABI facade (oakengine) retired, the frozen-ABI rationale is
gone. UndoCommand now boxes a Send Command trait (new/from_closures/
multi), dropping OakUndoCommandVtable, the userdata trampolines, the
refcount shell, the handle module, and all undostack_* handle exports.
The global facade loses its raw-pointer out-params (can_undo/can_redo
return bool, command_name returns String). oaktimeline/oaknode/
oakplugin/oaktask construct commands directly via UndoCommand::new.
oakundo src is now free of unsafe; behavior (ordering, idempotence,
done flags, groups, observers, 200-row cap) is unchanged and pinned by
the rewritten tests.
2026-08-19 00:41:49 +08:00

103 lines
3.7 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/>.
//! Edge-path coverage: error-code mapping, `Default` impls, the
//! prepared flag, and the `push_pre_executed` redo-tail/cap paths.
//! Everything here goes through the crate's public value-typed API
//! (the former handle-level/refcount tests were removed together with
//! the `CHandle` layer they exercised).
use oakundo::error::{Error, OAKUNDO_E_FAILED, OAKUNDO_E_INVALID, OAKUNDO_E_NOMEM, OAKUNDO_E_NOT_FOUND, OAKUNDO_E_STATE};
use oakundo::undocommand::{MultiUndoCommand, UndoCommand};
use oakundo::undostack::{EmptyCommand, UndoStack, K_MAX_UNDO_COMMANDS};
/// Every `Error` variant maps to its documented public code.
#[test]
fn error_code_mapping_is_complete() {
assert_eq!(Error::Invalid.code(), OAKUNDO_E_INVALID);
assert_eq!(Error::State.code(), OAKUNDO_E_STATE);
assert_eq!(Error::Failed("ctx".to_string()).code(), OAKUNDO_E_FAILED);
assert_eq!(Error::NotFound.code(), OAKUNDO_E_NOT_FOUND);
assert_eq!(Error::NoMem.code(), OAKUNDO_E_NOMEM);
}
/// `Default` impls mirror `new()`.
#[test]
fn default_impls_match_new() {
let _empty = EmptyCommand::default();
let stack = UndoStack::default();
assert_eq!(
stack.done_count(),
1,
"fresh stack holds the bottom command"
);
assert!(!stack.can_undo());
let multi = MultiUndoCommand::default();
assert_eq!(multi.child_count(), 0);
}
/// `set_prepared` is idempotent and `has_prepared` reflects it.
#[test]
fn prepared_flag_roundtrip() {
let mut cmd = UndoCommand::from_closures(|| {}, || {});
assert!(cmd.has_prepared());
cmd.set_prepared();
assert!(cmd.has_prepared());
}
/// `push_pre_executed` drops the redoable tail and evicts the oldest row
/// past the cap (mirrors `push`).
#[test]
fn push_pre_executed_clears_redo_tail_and_caps() {
let mut stack = UndoStack::new();
let name = "row";
// Push two, undo one, then push_pre_executed: redo tail is dropped.
for _ in 0..2 {
stack.push(UndoCommand::from_closures(|| {}, || {}), name);
}
stack.undo().unwrap();
stack.push_pre_executed(UndoCommand::from_closures(|| {}, || {}), name);
assert!(!stack.can_redo(), "push_pre_executed drops the redoable tail");
// Fill past the cap with pre-executed commands: the oldest rows are
// evicted and the count stays at K_MAX_UNDO_COMMANDS.
for _ in 0..(K_MAX_UNDO_COMMANDS + 10) {
stack.push_pre_executed(UndoCommand::from_closures(|| {}, || {}), name);
}
assert_eq!(
stack.done_count(),
K_MAX_UNDO_COMMANDS as i64,
"pre-executed rows evict at the cap"
);
}
/// A command value is moved, not copied: moving it into a stack leaves no
/// usable alias behind (the compiler enforces this; the assertion pins the
/// ownership semantics the old handle shells emulated).
#[test]
fn command_value_moves_into_stack() {
let mut stack = UndoStack::new();
stack.push(UndoCommand::from_closures(|| {}, || {}), "A");
stack.push(UndoCommand::from_closures(|| {}, || {}), "B");
assert_eq!(stack.command_count(), 3);
assert!(stack.can_undo());
stack.undo().unwrap();
assert!(stack.can_redo());
assert_eq!(stack.done_count(), 2);
assert_eq!(stack.command_name(2).unwrap(), "B"); // undone row still labeled
}