Files
Mike-Solar 66d761b4b7 R8: finish app/ pure C ABI migration (P3-P9) and make OTIO required
- app/ no longer includes engine C++ headers nor holds engine C++ types:
  engine access goes through the oakengine C ABI plus C++ wrappers
  (oakutil/oaknode.h, oakutil/oakvideo.h) and app-local mirror types
  (tooltypes, trackreferencehandle, timelinecommonapp, keyframetypes,
  subtitleapp, serializedlayoutinfoapp, nodevaluehandle, sliderdisplaytypeapp)
- engine: new C ABI functions for block/track/clip/transition navigation
  and predicates, links, caches, waveform/playback, disk folder,
  sequence_track_list, node_free, footage_is_valid, block_get_track,
  get_brush; loadotio/saveotio ported to the current engine API
- OTIO is now a required dependency: CI and CD build it on every
  platform, FindOpenTimelineIO fixed for OTIO 0.16/0.19 (the old deps
  include requirement silently disabled OTIO everywhere), runtime
  libraries are bundled into packages and copied next to macOS binaries
  (oak_copy_otio_runtime)
- fix ProjectViewModel drag&drop mime read/write size mismatch (segfault)
- unify color label naming (k_olive -> "Oak") in the app-side mirror
- docs: OTIO required, FFmpeg minimum corrected to 6.0 (en/zh)
- gtest suite: 1925 passed, 0 failed
2026-07-31 22:46:52 +08:00

91 lines
2.1 KiB
C++

#include <gtest/gtest.h>
#include <QSignalSpy>
#include "node/generator/solid/solid.h"
#include "node/project.h"
#include "widget/nodeview/nodeview.h"
using namespace olive;
namespace
{
// Helper: wrap an engine Node* as oak::Node for the C ABI widget interface
inline oak::Node to_oak(olive::Node *n)
{
return oak::Node(reinterpret_cast<OakEngineNode *>(n));
}
} // namespace
class NodeViewTest : public ::testing::Test {
protected:
void SetUp() override
{
ColorManager::set_up_default_config();
project_ = std::make_unique<Project>();
project_->initialize();
}
std::unique_ptr<Project> project_;
};
TEST_F(NodeViewTest, ConstructionCreatesEmptyView)
{
NodeView view;
EXPECT_TRUE(view.get_contexts().isEmpty());
EXPECT_FALSE(view.is_group_overlay());
}
TEST_F(NodeViewTest, SetContextsUpdatesContextList)
{
auto *solid = new SolidGenerator();
solid->setParent(project_.get());
NodeView view;
view.set_contexts({ to_oak(solid) });
EXPECT_EQ(view.get_contexts().size(), 1);
EXPECT_EQ(view.get_contexts().first(), to_oak(solid));
}
TEST_F(NodeViewTest, ShowSelectedNodeInParamEditorNoSelectionIsNoOp)
{
NodeView view;
QSignalSpy changed_with_ctx_spy(
&view, &NodeView::node_selection_changed_with_contexts);
// The action must exist; silently skipping the trigger would make this
// test pass without exercising anything
QAction *show_params_action = nullptr;
foreach (QAction *action, view.actions()) {
if (action->property("id").toString() ==
QStringLiteral("shownodeparams")) {
show_params_action = action;
break;
}
}
ASSERT_NE(show_params_action, nullptr)
<< "NodeView has no action with id 'shownodeparams'";
// With nothing selected, triggering it must not emit a selection change
show_params_action->trigger();
EXPECT_EQ(changed_with_ctx_spy.count(), 0);
}
TEST_F(NodeViewTest, ClearGraphRemovesContexts)
{
auto *solid = new SolidGenerator();
solid->setParent(project_.get());
NodeView view;
view.set_contexts({ to_oak(solid) });
EXPECT_FALSE(view.get_contexts().isEmpty());
view.clear_graph();
EXPECT_TRUE(view.get_contexts().isEmpty());
}