// 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 .
//! Test-only `#[no_mangle]` stubs for every extern C symbol the crate
//! imports. Integration test binaries link the crate's rlib, which
//! references the real module DLLs; these stubs satisfy the linker and let
//! the real task logic run against controllable behavior. Each stub family
//! exposes `set_*` helpers used by the tests to drive success/failure paths.
#![allow(non_snake_case, dead_code, unused_variables, clippy::missing_safety_doc)]
use std::ffi::{c_char, c_int, c_void};
use std::sync::atomic::{AtomicI32, AtomicI64, Ordering};
// AtomicUsize/Condvar are only used by the simulated oakrender ticket
// arena, which the `real-oakrender` feature compiles out.
#[cfg(not(feature = "real-oakrender"))]
use std::sync::atomic::AtomicUsize;
use std::sync::Mutex;
#[cfg(not(feature = "real-oakrender"))]
use std::sync::Condvar;
// ---------------------------------------------------------------------------
// Fake handle helpers
// ---------------------------------------------------------------------------
/// A fake refcounted handle: non-null ctx with no-op addref/release.
pub fn fake_handle() -> oaktask::handle::CHandle {
unsafe extern "C" fn noop(_ctx: *mut c_void) {}
oaktask::handle::CHandle {
ctx: 1usize as *mut c_void,
addref: Some(noop),
release: Some(noop),
abi_version: 1,
}
}
pub fn empty_handle() -> oaktask::handle::CHandle {
oaktask::handle::CHandle::null()
}
/// A fake OakCancelAtom (ctx non-null so `is_null()` is false).
pub fn fake_atom() -> oaktask::bridge::render::OakCancelAtom {
unsafe extern "C" fn noop(_ctx: *mut c_void) {}
oaktask::bridge::render::OakCancelAtom {
ctx: 2usize as *mut c_void,
addref: Some(noop),
release: Some(noop),
abi_version: 1,
}
}
pub fn write_cstr(s: &str, buf: *mut u8, size: i32) -> i32 {
let bytes = s.as_bytes();
let needed = bytes.len() as i32 + 1;
if !buf.is_null() && size >= needed {
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), buf, bytes.len());
*buf.add(bytes.len()) = 0;
}
}
needed
}
pub fn cstr_of(s: &str) -> *const c_char {
let mut v = s.as_bytes().to_vec();
v.push(0);
v.leak().as_ptr() as *const c_char
}
// ---------------------------------------------------------------------------
// Control statics
// ---------------------------------------------------------------------------
pub static SERIALIZER_LOAD_CODE: AtomicI32 = AtomicI32::new(0); // OAKNODE_SERIALIZER_RESULT_*
pub static SERIALIZER_LOAD_RET: AtomicI32 = AtomicI32::new(0); // oaknode_serializer_load_from_file return
pub static SERIALIZER_SAVE_CODE: AtomicI32 = AtomicI32::new(0);
pub static SERIALIZER_DETAILS: Mutex<&'static str> = Mutex::new("");
pub static PROJECT_FILENAME: Mutex = Mutex::new(String::new());
pub static NODE_LABEL: Mutex = Mutex::new(String::new());
pub static FOOTAGE_FILENAME: Mutex = Mutex::new(String::new());
pub static FOOTAGE_VALID: AtomicI32 = AtomicI32::new(1);
pub static FOOTAGE_STREAM_COUNT: AtomicI32 = AtomicI32::new(1);
pub static VIDEO_TYPE: AtomicI32 = AtomicI32::new(1); // OAKCOMMON_VIDEO_TYPE_STILL
pub static VIDEO_IS_VALID: AtomicI32 = AtomicI32::new(1);
pub static VIDEO_WIDTH: AtomicI32 = AtomicI32::new(640);
pub static VIDEO_HEIGHT: AtomicI32 = AtomicI32::new(360);
pub static VIDEO_TIME_BASE_NUM: AtomicI32 = AtomicI32::new(0); // 0 => render falls back to 1/1
pub static VIDEO_TIME_BASE_DEN: AtomicI32 = AtomicI32::new(1);
pub static VIDEO_LENGTH_NUM: AtomicI64 = AtomicI64::new(0);
pub static VIDEO_LENGTH_DEN: AtomicI64 = AtomicI64::new(1);
pub static SEQUENCE_LENGTH_NUM: AtomicI32 = AtomicI32::new(0);
pub static SEQUENCE_LENGTH_DEN: AtomicI32 = AtomicI32::new(1);
pub static CONNECTED_NODE: AtomicI64 = AtomicI64::new(1); // non-null => connected
pub static FOLDER_CHILD_COUNT: AtomicI32 = AtomicI32::new(0);
pub static REDO_DELTA: AtomicI32 = AtomicI32::new(0);
pub static MULTI_CHILDREN: AtomicI32 = AtomicI32::new(0);
pub static DECODER_OPEN_RESULT: AtomicI32 = AtomicI32::new(0);
pub static DECODER_CONFORM_RESULT: AtomicI32 = AtomicI32::new(0);
pub static DECODER_LAST_ERROR: Mutex = Mutex::new(String::new());
pub static ENCODER_INIT_NULL: AtomicI32 = AtomicI32::new(0);
pub static ENCODER_OPEN_RESULT: AtomicI32 = AtomicI32::new(0);
pub static ENCODER_WRITE_VIDEO_RESULT: AtomicI32 = AtomicI32::new(0);
pub static ENCODER_WRITE_SUBTITLE_RESULT: AtomicI32 = AtomicI32::new(0);
pub static ENCODER_FLUSH_ERROR: AtomicI32 = AtomicI32::new(0); // non-zero => flush reports an error
pub static ENCODER_LAST_ERROR: Mutex = Mutex::new(String::new());
pub static DESIRED_PIXEL_FORMAT: AtomicI32 = AtomicI32::new(0);
pub static IMAGE_SEQUENCE_DIGIT_COUNT: AtomicI32 = AtomicI32::new(0);
pub static CONFIG_DEFAULT_SEQ_FRAME_RATE: AtomicI32 = AtomicI32::new(0); // 0 => config_get returns nothing
pub static PROXY_FFMPEG_PATH: Mutex = Mutex::new(String::new());
pub static SUBMIT_CB: Mutex