Compiler hygiene (all platforms): - Silence warnings across the tree: missing override, -Wreorder ctor init, -Wshadow, -Wsign-compare, missing switch cases, unused functions/captures, Qt 6.11 deprecations (QMouseEvent/QDropEvent accessors, qAsConst, Q_FOREACH over non-shared containers, AA_UseHighDpiPixmaps) and the .bak/ backup tree removal. - Fix regressions from the cleanup: missing clip decls in capi/timeline.cpp, plugin.cpp rename fallout, panel setFocus ambiguity, QGraphicsItem::pos vs event->position(), duplicate k_push_button case, boolean test variable. Windows: - Qt portability: CommandLineParser is_set/add_option, QTimeZone systemTimeZone (QTimeZone::LocalTime is 6.11-only), k_progress_* enum. - Linking: stop adding oakengine to OLIVE_LIBRARIES (import lib plus oakengine-obj caused multiple definitions); add OAKENGINE_STATIC so internal consumers no longer reference __imp_* stubs. - oakengine.ver: export olive::Renderer typeinfo so liboakgl.so can be dlopened (Linux), DynamicRenderer no longer dlcloses backend libraries (crash in RenderManager's dtor calling into unmapped memory). - OTIO runtime: copy DLLs next to every binary on Windows instead of relying on PATH (0xc0000135 in gtest discovery). - Headless GL: the runner only has GDI OpenGL 1.1, killing every render worker. Deploy Mesa llvmpipe as opengl32sw.dll (Qt's software-GL channel) with QT_OPENGL=software, and let QT_OPENGL override the AA_UseDesktopOpenGL default. ExportTask fails fast after 8 consecutive undelivered frames instead of segfaulting or grinding forever; FFmpegEncoder::write_frame tolerates null frames. - Tests: GetTempPathA+PID temp dirs, GetLongPathNameA for 8.3 names, forward-slash normalization when comparing project filenames. Linux: - Install libshaderc-dev so oakvulkan compiles GLSL (Vulkan tests). - Accept UNORM floor-or-round (63/64) in the blit ping-pong test. - Skip MainWindow construction test on the offscreen QPA (cannot paint QOpenGLWidget). Also: oak_cli_transcode gets a 300s ctest timeout, worker logs GL context version and LoadGraph/render_frame stages, and docs/plans/eliminate-event-bridge-issues.md (English translation).
204 lines
5.4 KiB
C++
204 lines
5.4 KiB
C++
/*
|
|
* Oak Video Editor - Plugin Subsystem Smoke Tests
|
|
* Copyright (C) 2025 Olive CE Team
|
|
*
|
|
* Smoke tests for the OFX plugin subsystem covering:
|
|
* - Plugin host initialization
|
|
* - Plugin job execution
|
|
* - Concurrent image allocation
|
|
*
|
|
* Parameter instance, clip, image and renderer coverage lives in the
|
|
* dedicated plugin_paraminstance_test.cpp, plugin_support_param_test.cpp,
|
|
* plugin_support_clip_test.cpp, plugin_support_image_test.cpp and
|
|
* plugin_renderer_readback_test.cpp files.
|
|
*/
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include <atomic>
|
|
#include <thread>
|
|
|
|
#include <QCoreApplication>
|
|
#include <QThread>
|
|
|
|
// OFX headers
|
|
#include "ofxCore.h"
|
|
#include "ofxImageEffect.h"
|
|
#include "ofxhClip.h"
|
|
#include "ofxhImageEffect.h"
|
|
|
|
// Plugin support headers
|
|
#include "pluginSupport/olivehost.h"
|
|
#include "pluginSupport/oliveclip.h"
|
|
#include "pluginSupport/oliveplugininstance.h"
|
|
#include "pluginSupport/image.h"
|
|
|
|
// Node and render headers
|
|
#include "render/job/pluginjob.h"
|
|
#include "render/videoparams.h"
|
|
#include "render/texture.h"
|
|
|
|
#include "node/value.h"
|
|
#include "common/ffmpegutils.h"
|
|
|
|
namespace olive
|
|
{
|
|
namespace plugin
|
|
{
|
|
namespace test
|
|
{
|
|
|
|
// ============================================================================
|
|
// Helper Functions
|
|
// ============================================================================
|
|
|
|
static VideoParams make_video_params(int width, int height,
|
|
core::PixelFormat format, int channels,
|
|
bool premultiplied = false)
|
|
{
|
|
VideoParams params;
|
|
params.set_width(width);
|
|
params.set_height(height);
|
|
params.set_format(format);
|
|
params.set_channel_count(channels);
|
|
params.set_premultiplied_alpha(premultiplied);
|
|
params.set_pixel_aspect_ratio(core::Rational(1, 1));
|
|
params.set_frame_rate(core::Rational(30, 1));
|
|
return params;
|
|
}
|
|
|
|
static TexturePtr create_test_texture(const VideoParams ¶ms,
|
|
uint8_t fill_value = 0x7f)
|
|
{
|
|
AVFramePtr frame = create_av_frame_ptr();
|
|
frame->set_format(FFmpegUtils::get_f_fmpeg_pixel_format(params.format(),
|
|
params.channel_count()));
|
|
frame->set_width(params.width());
|
|
frame->set_height(params.height());
|
|
if (frame->format() == fb_pix_fmt_none) {
|
|
return nullptr;
|
|
}
|
|
if (frame->get_buffer(0) < 0) {
|
|
return nullptr;
|
|
}
|
|
if (frame->make_writable() < 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
const int linesize = frame->linesize(0);
|
|
for (int y = 0; y < frame->height(); ++y) {
|
|
std::memset(frame->data(0) + y * linesize, fill_value, linesize);
|
|
}
|
|
|
|
TexturePtr texture = std::make_shared<Texture>(params);
|
|
texture->handle_frame(frame);
|
|
return texture;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Smoke Test: Plugin Host
|
|
// ============================================================================
|
|
|
|
TEST(PluginSmoke, HostSingletonExists)
|
|
{
|
|
// Verify that the plugin cache can be accessed
|
|
auto *cache = OFX::Host::PluginCache::getPluginCache();
|
|
EXPECT_NE(cache, nullptr);
|
|
}
|
|
|
|
TEST(PluginSmoke, LoadPluginsEmptyPathNoCrash)
|
|
{
|
|
// Loading plugins from empty path should not crash
|
|
EXPECT_NO_THROW({ load_plugins(QString()); });
|
|
}
|
|
|
|
TEST(PluginSmoke, LoadPluginsNonExistentPathNoCrash)
|
|
{
|
|
// Loading plugins from non-existent path should not crash
|
|
EXPECT_NO_THROW(
|
|
{ load_plugins(QStringLiteral("/nonexistent/path/to/plugins")); });
|
|
}
|
|
|
|
// ============================================================================
|
|
// Smoke Test: Plugin Job
|
|
// ============================================================================
|
|
|
|
TEST(PluginSmokeJob, JobConstruction)
|
|
{
|
|
NodeValueRow row;
|
|
PluginJob job(nullptr, nullptr, row);
|
|
|
|
EXPECT_EQ(job.plugin_instance(), nullptr);
|
|
EXPECT_EQ(job.node(), nullptr);
|
|
EXPECT_DOUBLE_EQ(job.time_seconds(), 0.0);
|
|
}
|
|
|
|
TEST(PluginSmokeJob, JobWithTime)
|
|
{
|
|
NodeValueRow row;
|
|
core::Rational time(5, 1); // 5 seconds
|
|
PluginJob job(nullptr, nullptr, row, time);
|
|
|
|
EXPECT_DOUBLE_EQ(job.time_seconds(), 5.0);
|
|
}
|
|
|
|
TEST(PluginSmokeJob, JobWithTextureValue)
|
|
{
|
|
VideoParams params(64, 64, core::PixelFormat::u8, 4);
|
|
TexturePtr tex = create_test_texture(params, 0x80);
|
|
ASSERT_NE(tex, nullptr);
|
|
|
|
NodeValueRow row;
|
|
row.insert(QStringLiteral("source"), NodeValue(NodeValue::k_texture, tex));
|
|
|
|
PluginJob job(nullptr, nullptr, row);
|
|
|
|
// Job should have the values inserted
|
|
EXPECT_FALSE(job.get_values().isEmpty());
|
|
}
|
|
|
|
// ============================================================================
|
|
// Smoke Test: Thread Safety
|
|
// ============================================================================
|
|
|
|
TEST(PluginSmokeThread, ConcurrentImageAllocation)
|
|
{
|
|
const int num_threads = 4;
|
|
const int num_allocs_per_thread = 10;
|
|
|
|
std::vector<std::thread> threads;
|
|
std::atomic<int> success_count{ 0 };
|
|
|
|
for (int t = 0; t < num_threads; ++t) {
|
|
threads.emplace_back([&success_count, t]() {
|
|
for (int i = 0; i < num_allocs_per_thread; ++i) {
|
|
OFX::Host::ImageEffect::ClipDescriptor desc(
|
|
kOfxImageEffectOutputClipName);
|
|
VideoParams params = make_video_params(
|
|
32 + t, 32 + i, core::PixelFormat::u8, 4, false);
|
|
OliveClipInstance clip(nullptr, desc, params);
|
|
|
|
Image image(clip);
|
|
OfxRectI bounds = { 0, 0, 32 + t, 32 + i };
|
|
OfxRectI rod = bounds;
|
|
image.allocate_from_params(params, bounds, rod, true);
|
|
|
|
if (image.data() != nullptr && image.width() == 32 + t &&
|
|
image.height() == 32 + i) {
|
|
success_count++;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
for (auto &t : threads) {
|
|
t.join();
|
|
}
|
|
|
|
EXPECT_EQ(success_count.load(), num_threads * num_allocs_per_thread);
|
|
}
|
|
|
|
} // namespace test
|
|
} // namespace plugin
|
|
} // namespace olive
|