添加测试

This commit is contained in:
2026-01-05 02:55:21 +08:00
parent 9d0790370f
commit 1f679551f2
20 changed files with 784 additions and 0 deletions
+86
View File
@@ -0,0 +1,86 @@
name: CI
on:
push:
pull_request:
jobs:
build-test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, macos-13, windows-2022]
env:
CMAKE_BUILD_TYPE: Release
steps:
- uses: actions/checkout@v4
- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y \
ninja-build pkg-config \
qt6-base-dev qt6-base-dev-tools qt6-tools-dev qt6-tools-dev-tools \
libavcodec-dev libavformat-dev libavfilter-dev libavutil-dev libswscale-dev libswresample-dev \
libopencolorio-dev libopenimageio-dev libopenexr-dev libexpat1-dev \
portaudio19-dev libgl1-mesa-dev
- name: Install dependencies (macOS)
if: runner.os == 'macOS'
run: |
brew update
brew install ninja pkg-config qt@6 ffmpeg openimageio opencolorio openexr portaudio expat opentimelineio
echo "$(brew --prefix qt@6)/bin" >> "$GITHUB_PATH"
echo "CMAKE_PREFIX_PATH=$(brew --prefix qt@6)" >> "$GITHUB_ENV"
- name: Install Qt (Windows)
if: runner.os == 'Windows'
uses: jurplel/install-qt-action@v4
with:
version: 6.5.3
cache: true
tools: 'tools_ninja'
- name: Install dependencies (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
choco install -y ninja
$env:VCPKG_ROOT = "C:\vcpkg"
& "$env:VCPKG_ROOT\vcpkg.exe" install ffmpeg openimageio opencolorio openexr expat portaudio --triplet x64-windows
echo "VCPKG_ROOT=$env:VCPKG_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append
echo "CMAKE_TOOLCHAIN_FILE=$env:VCPKG_ROOT\scripts\buildsystems\vcpkg.cmake" | Out-File -FilePath $env:GITHUB_ENV -Append
- name: Configure (Linux/macOS)
if: runner.os != 'Windows'
run: |
cmake -S . -B build -G Ninja \
-DBUILD_TESTS=ON \
-DBUILD_QT6=ON \
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
- name: Configure (Windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
cmake -S . -B build -G Ninja `
-DBUILD_TESTS=ON `
-DBUILD_QT6=ON `
-DCMAKE_BUILD_TYPE=$env:CMAKE_BUILD_TYPE `
-DCMAKE_TOOLCHAIN_FILE=$env:CMAKE_TOOLCHAIN_FILE `
-DCMAKE_PREFIX_PATH=$env:Qt6_DIR
- name: Build
run: cmake --build build --config ${{ env.CMAKE_BUILD_TYPE }}
- name: Test (Linux)
if: runner.os == 'Linux'
env:
QT_QPA_PLATFORM: offscreen
run: ctest --test-dir build --output-on-failure -C ${{ env.CMAKE_BUILD_TYPE }}
- name: Test (macOS/Windows)
if: runner.os != 'Linux'
run: ctest --test-dir build --output-on-failure -C ${{ env.CMAKE_BUILD_TYPE }}
+86
View File
@@ -0,0 +1,86 @@
# Olive Testing Strategy and Plan
This document describes the automated testing strategy for Olive, including unit tests, integration tests, and CI execution.
## Goals
- Maximize automation and reduce manual testing.
- Cover all modules with at least one automated test.
- Keep integration tests headless (no GUI interaction).
- Make failures reproducible on Windows/macOS/Linux CI.
## Test Layers
### 1) Unit Tests (GoogleTest)
- Focus: small units, deterministic behavior, no GUI.
- Location: `tests/gtest/`.
- Execution: `ctest` target `olive-gtest`.
### 1.5) Module Smoke Tests (GoogleTest)
- Focus: compile-time and link-time coverage for GUI-heavy modules without instantiating widgets.
- Location: `tests/gtest/module_smoke_test.cpp`.
- Execution: `ctest` target `olive-gtest`.
### 2) Integration Tests (GoogleTest)
- Focus: cross-module flows without GUI (e.g., serialize → deserialize → resolve).
- Location: `tests/gtest/` (prefixed with `ProjectSerializer`, `TaskManager`, etc.).
### 3) Legacy Tests (Olive macro tests)
- Existing tests in `tests/general`, `tests/timeline`, `tests/compositing` remain.
## Module Coverage Map
Each top-level module has at least one test that exercises its core API or serialization path.
- `app/common`: `common_current_test.cpp`, `common_xmlutils_test.cpp`
- `app/config`: `config_test.cpp`
- `app/node`: `node_value_test.cpp`, `node_keyframe_test.cpp`, `node_serialization_test.cpp`
- `app/node/project/serializer`: `project_serializer_test.cpp`
- `app/render`: `render_videoparams_test.cpp`, `render_audioparams_test.cpp`
- `app/timeline`: `timeline_marker_test.cpp`
- `app/undo`: `undo_stack_test.cpp`
- `app/task`: `task_taskmanager_test.cpp`
- `app/codec`: `codec_frame_test.cpp`
- `app/pluginSupport`: `plugin_support_test.cpp`
- `app/audio`, `app/cli`, `app/dialog`, `app/panel`, `app/tool`, `app/ui`, `app/widget`, `app/window`: `module_smoke_test.cpp`
If a module has a GUI dependency (e.g., widgets), tests focus on non-visual data/model components.
## Integration Test Details
### Project Serializer Roundtrip
- Creates a minimal project with a built-in node.
- Saves to XML via `ProjectSerializer::Save`.
- Loads with `ProjectSerializer::Load`.
- Verifies that nodes are restored.
### Task Manager Execution
- Adds a dummy task to `TaskManager`.
- Waits for completion using an event loop.
- Verifies the task ran.
## Headless Execution
- Tests avoid QWidget usage.
- CI sets `QT_QPA_PLATFORM=offscreen` to prevent GUI initialization issues.
## Continuous Integration
CI runs on Windows, macOS, and Linux:
1. Install system dependencies (Qt, FFmpeg, OpenImageIO, OpenColorIO, OpenEXR, PortAudio, Expat).
2. Configure with `-DBUILD_TESTS=ON`.
3. Build with CMake + Ninja.
4. Run `ctest` with output on failure.
### Dependency Installation Notes
- Linux: use distro packages (`apt` on Ubuntu) for Qt6, FFmpeg, OpenImageIO, OpenColorIO, OpenEXR, PortAudio, Expat, OpenGL headers.
- macOS: use Homebrew for Qt6 and media/color/image libraries.
- Windows: use system installers where available (Qt via `install-qt-action`), and vcpkg for the remaining C/C++ libraries.
## Adding New Tests
- Place new unit tests in `tests/gtest`.
- Use GoogleTest conventions.
- Prefer deterministic fixtures and local-only resources.
- When adding a new module, add at least one unit test and one integration scenario if applicable.
+12
View File
@@ -69,6 +69,18 @@ function(olive_add_test GROUP NAME SOURCE)
endif()
endfunction()
include(FetchContent)
find_package(GTest QUIET)
if (NOT GTest_FOUND)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/refs/tags/v1.15.2.zip
)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)
endif()
add_subdirectory(compositing)
add_subdirectory(general)
add_subdirectory(timeline)
add_subdirectory(gtest)
+53
View File
@@ -0,0 +1,53 @@
add_executable(olive-gtest
main.cpp
common_current_test.cpp
common_xmlutils_test.cpp
config_test.cpp
node_value_test.cpp
node_keyframe_test.cpp
node_serialization_test.cpp
render_videoparams_test.cpp
render_audioparams_test.cpp
project_serializer_test.cpp
timeline_marker_test.cpp
undo_stack_test.cpp
plugin_support_test.cpp
codec_frame_test.cpp
task_taskmanager_test.cpp
module_smoke_test.cpp
)
target_sources(olive-gtest PRIVATE $<TARGET_OBJECTS:libolive-editor>)
target_include_directories(
olive-gtest
PRIVATE
${CMAKE_SOURCE_DIR}/app
${CMAKE_SOURCE_DIR}/tests
${OLIVE_INCLUDE_DIRS}
)
target_link_libraries(
olive-gtest
PRIVATE
${OLIVE_LIBRARIES}
GTest::gtest
)
target_compile_definitions(
olive-gtest
PRIVATE
${OLIVE_DEFINITIONS}
)
target_compile_options(
olive-gtest
PRIVATE
${OLIVE_COMPILE_OPTIONS}
)
if (MSVC)
add_test("Olive.gtest" olive-gtest)
else()
add_test(olive-gtest olive-gtest)
endif()
+11
View File
@@ -0,0 +1,11 @@
#include <gtest/gtest.h>
#include "codec/frame.h"
TEST(CodecFrame, DefaultState)
{
olive::Frame frame;
EXPECT_EQ(frame.width(), 0);
EXPECT_EQ(frame.height(), 0);
EXPECT_EQ(frame.pixel_format(), olive::core::PixelFormat::Format::kFormatNone);
}
+27
View File
@@ -0,0 +1,27 @@
#include <gtest/gtest.h>
#include "common/Current.h"
#include "render/videoparams.h"
#include "render/audioparams.h"
TEST(CommonCurrent, SetAndGetVideoParams)
{
olive::VideoParams params;
params.set_width(1920);
params.set_height(1080);
Current::getInstance().setCurrentVideoParams(params);
const olive::VideoParams &stored = Current::getInstance().currentVideoParams();
EXPECT_EQ(stored.width(), 1920);
EXPECT_EQ(stored.height(), 1080);
}
TEST(CommonCurrent, SetAndGetAudioParams)
{
olive::AudioParams params;
params.set_sample_rate(48000);
Current::getInstance().setCurrentAudioParams(params);
const olive::AudioParams &stored = Current::getInstance().currentAudioParams();
EXPECT_EQ(stored.sample_rate(), 48000);
}
+20
View File
@@ -0,0 +1,20 @@
#include <gtest/gtest.h>
#include <QBuffer>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include "common/xmlutils.h"
TEST(CommonXmlUtils, ReadNextStartElement)
{
QByteArray xml = "<root><child>value</child></root>";
QBuffer buffer(&xml);
buffer.open(QIODevice::ReadOnly);
QXmlStreamReader reader(&buffer);
EXPECT_TRUE(XMLReadNextStartElement(&reader));
EXPECT_EQ(reader.name().toString(), QStringLiteral("root"));
EXPECT_TRUE(XMLReadNextStartElement(&reader));
EXPECT_EQ(reader.name().toString(), QStringLiteral("child"));
}
+20
View File
@@ -0,0 +1,20 @@
#include <gtest/gtest.h>
#include "config/config.h"
TEST(Config, DefaultsPresent)
{
olive::Config &cfg = olive::Config::Current();
cfg.SetDefaults();
EXPECT_TRUE(cfg[QStringLiteral("Style")].isValid());
EXPECT_TRUE(cfg[QStringLiteral("TimecodeDisplay")].isValid());
EXPECT_TRUE(cfg[QStringLiteral("DefaultStillLength")].isValid());
}
TEST(Config, SetAndGetValues)
{
olive::Config &cfg = olive::Config::Current();
cfg[QStringLiteral("UnitTestValue")] = 42;
EXPECT_EQ(cfg[QStringLiteral("UnitTestValue")].toInt(), 42);
}
+9
View File
@@ -0,0 +1,9 @@
#include <QCoreApplication>
#include <gtest/gtest.h>
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
+15
View File
@@ -0,0 +1,15 @@
#include <gtest/gtest.h>
#include "audio/audiomanager.h"
#include "cli/cliexport/cliexportmanager.h"
#include "dialog/progress/progress.h"
#include "panel/panelmanager.h"
#include "tool/tool.h"
#include "ui/humanstrings.h"
#include "widget/nodeview/nodeview.h"
#include "window/mainwindow/mainwindow.h"
TEST(ModuleSmoke, HeadersBuild)
{
SUCCEED();
}
+47
View File
@@ -0,0 +1,47 @@
#include <gtest/gtest.h>
#include <QBuffer>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include "node/keyframe.h"
#include "node/value.h"
TEST(NodeKeyframe, SaveLoadRoundTrip)
{
olive::NodeKeyframe key;
key.set_input(QStringLiteral("Value"));
key.set_time(olive::core::rational(1, 24));
key.set_type(olive::NodeKeyframe::kLinear);
key.set_value(42.0);
key.set_bezier_control_in(QPointF(0.1, 0.2));
key.set_bezier_control_out(QPointF(0.3, 0.4));
QByteArray xml;
QBuffer buffer(&xml);
buffer.open(QIODevice::WriteOnly);
QXmlStreamWriter writer(&buffer);
writer.writeStartDocument();
writer.writeStartElement(QStringLiteral("key"));
key.save(&writer, olive::NodeValue::kFloat);
writer.writeEndElement();
writer.writeEndDocument();
buffer.close();
QBuffer read_buffer(&xml);
read_buffer.open(QIODevice::ReadOnly);
QXmlStreamReader reader(&read_buffer);
EXPECT_TRUE(reader.readNextStartElement());
EXPECT_EQ(reader.name().toString(), QStringLiteral("key"));
olive::NodeKeyframe loaded;
EXPECT_TRUE(loaded.load(&reader, olive::NodeValue::kFloat));
EXPECT_EQ(loaded.input(), QStringLiteral("Value"));
EXPECT_EQ(loaded.time(), olive::core::rational(1, 24));
EXPECT_EQ(loaded.type(), olive::NodeKeyframe::kLinear);
EXPECT_DOUBLE_EQ(loaded.value().toDouble(), 42.0);
EXPECT_DOUBLE_EQ(loaded.bezier_control_in().x(), 0.1);
EXPECT_DOUBLE_EQ(loaded.bezier_control_in().y(), 0.2);
EXPECT_DOUBLE_EQ(loaded.bezier_control_out().x(), 0.3);
EXPECT_DOUBLE_EQ(loaded.bezier_control_out().y(), 0.4);
}
+81
View File
@@ -0,0 +1,81 @@
#include <gtest/gtest.h>
#include <QBuffer>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include "node/node.h"
#include "node/serializeddata.h"
#include "node/value.h"
namespace {
class TestNode final : public olive::Node {
public:
TestNode()
{
AddInput(QStringLiteral("Value"), olive::NodeValue::kFloat);
SetSplitStandardValue(QStringLiteral("Value"), 3.5, -1);
}
TestNode *copy() const override
{
return new TestNode();
}
QString Name() const override
{
return QStringLiteral("TestNode");
}
QString id() const override
{
return QStringLiteral("org.olivevideoeditor.TestNode");
}
QVector<CategoryID> Category() const override
{
return { kCategoryUnknown };
}
QString Description() const override
{
return QStringLiteral("Test node for serialization");
}
void Value(const NodeValueRow &, const NodeGlobals &, NodeValueTable *) const override
{
}
};
}
TEST(NodeSerialization, SaveAndLoadInput)
{
TestNode node;
node.SetLabel(QStringLiteral("MyNode"));
node.SetOverrideColor(2);
QByteArray xml;
QBuffer buffer(&xml);
buffer.open(QIODevice::WriteOnly);
QXmlStreamWriter writer(&buffer);
writer.writeStartDocument();
writer.writeStartElement(QStringLiteral("node"));
node.Save(&writer);
writer.writeEndElement();
writer.writeEndDocument();
buffer.close();
TestNode loaded;
olive::SerializedData data;
QBuffer read_buffer(&xml);
read_buffer.open(QIODevice::ReadOnly);
QXmlStreamReader reader(&read_buffer);
EXPECT_TRUE(reader.readNextStartElement());
EXPECT_EQ(reader.name().toString(), QStringLiteral("node"));
EXPECT_TRUE(loaded.Load(&reader, &data));
EXPECT_EQ(loaded.GetLabel(), QStringLiteral("MyNode"));
EXPECT_EQ(loaded.GetOverrideColor(), 2);
EXPECT_DOUBLE_EQ(loaded.GetSplitStandardValue(QStringLiteral("Value"), -1)
.first().toDouble(), 3.5);
}
+50
View File
@@ -0,0 +1,50 @@
#include <gtest/gtest.h>
#include <QVector2D>
#include <QVector3D>
#include <QVector4D>
#include "node/value.h"
TEST(NodeValue, VectorRoundTrip)
{
QVector2D v2(1.5f, -2.0f);
QString encoded = olive::NodeValue::ValueToString(
olive::NodeValue::kVec2, QVariant::fromValue(v2), false);
QVariant decoded = olive::NodeValue::StringToValue(
olive::NodeValue::kVec2, encoded, false);
QVector2D v2_out = decoded.value<QVector2D>();
EXPECT_FLOAT_EQ(v2_out.x(), v2.x());
EXPECT_FLOAT_EQ(v2_out.y(), v2.y());
QVector3D v3(1.0f, 2.0f, 3.0f);
encoded = olive::NodeValue::ValueToString(
olive::NodeValue::kVec3, QVariant::fromValue(v3), false);
decoded = olive::NodeValue::StringToValue(
olive::NodeValue::kVec3, encoded, false);
QVector3D v3_out = decoded.value<QVector3D>();
EXPECT_FLOAT_EQ(v3_out.x(), v3.x());
EXPECT_FLOAT_EQ(v3_out.y(), v3.y());
EXPECT_FLOAT_EQ(v3_out.z(), v3.z());
QVector4D v4(1.0f, 2.0f, 3.0f, 4.0f);
encoded = olive::NodeValue::ValueToString(
olive::NodeValue::kVec4, QVariant::fromValue(v4), false);
decoded = olive::NodeValue::StringToValue(
olive::NodeValue::kVec4, encoded, false);
QVector4D v4_out = decoded.value<QVector4D>();
EXPECT_FLOAT_EQ(v4_out.x(), v4.x());
EXPECT_FLOAT_EQ(v4_out.y(), v4.y());
EXPECT_FLOAT_EQ(v4_out.z(), v4.z());
EXPECT_FLOAT_EQ(v4_out.w(), v4.w());
}
TEST(NodeValue, BinaryRoundTrip)
{
QByteArray data("OliveTest");
QString encoded = olive::NodeValue::ValueToString(
olive::NodeValue::kBinary, data, false);
QVariant decoded = olive::NodeValue::StringToValue(
olive::NodeValue::kBinary, encoded, false);
EXPECT_EQ(decoded.toByteArray(), data);
}
+10
View File
@@ -0,0 +1,10 @@
#include <gtest/gtest.h>
#include "pluginSupport/OliveHost.h"
TEST(PluginSupport, LoadPluginsEmptyPath)
{
EXPECT_NO_THROW({
olive::plugin::loadPlugins(QString());
});
}
+43
View File
@@ -0,0 +1,43 @@
#include <gtest/gtest.h>
#include <QBuffer>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include "node/factory.h"
#include "node/project.h"
#include "node/input/time/timeinput.h"
#include "node/project/serializer/serializer.h"
TEST(ProjectSerializer, SaveLoadProjectRoundTrip)
{
olive::NodeFactory::Initialize();
olive::Project project;
project.Initialize();
auto *node = new olive::TimeInput();
node->SetLabel(QStringLiteral("TimeInput"));
node->setParent(&project);
olive::ProjectSerializer::SaveData save_data(
olive::ProjectSerializer::kProject, &project, QString());
QByteArray xml;
QBuffer buffer(&xml);
buffer.open(QIODevice::WriteOnly);
QXmlStreamWriter writer(&buffer);
EXPECT_EQ(olive::ProjectSerializer::Save(&writer, save_data),
olive::ProjectSerializer::kSuccess);
buffer.close();
olive::Project loaded_project;
QBuffer read_buffer(&xml);
read_buffer.open(QIODevice::ReadOnly);
QXmlStreamReader reader(&read_buffer);
olive::ProjectSerializer::Result result =
olive::ProjectSerializer::Load(&loaded_project, &reader,
olive::ProjectSerializer::kProject);
EXPECT_EQ(result.code(), olive::ProjectSerializer::kSuccess);
EXPECT_FALSE(loaded_project.nodes().isEmpty());
}
+38
View File
@@ -0,0 +1,38 @@
#include <gtest/gtest.h>
#include <QBuffer>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include "node/project/serializer/typeserializer.h"
#include "render/audioparams.h"
TEST(RenderAudioParams, SaveLoadRoundTrip)
{
olive::AudioParams params;
params.set_sample_rate(48000);
params.set_enabled(true);
params.set_time_base(olive::core::rational(1, 48000));
QByteArray xml;
QBuffer buffer(&xml);
buffer.open(QIODevice::WriteOnly);
QXmlStreamWriter writer(&buffer);
writer.writeStartDocument();
writer.writeStartElement(QStringLiteral("audioparams"));
olive::TypeSerializer::SaveAudioParams(&writer, params);
writer.writeEndElement();
writer.writeEndDocument();
buffer.close();
QBuffer read_buffer(&xml);
read_buffer.open(QIODevice::ReadOnly);
QXmlStreamReader reader(&read_buffer);
EXPECT_TRUE(reader.readNextStartElement());
EXPECT_EQ(reader.name().toString(), QStringLiteral("audioparams"));
olive::AudioParams loaded = olive::TypeSerializer::LoadAudioParams(&reader);
EXPECT_EQ(loaded.sample_rate(), 48000);
EXPECT_TRUE(loaded.enabled());
EXPECT_EQ(loaded.time_base(), olive::core::rational(1, 48000));
}
+42
View File
@@ -0,0 +1,42 @@
#include <gtest/gtest.h>
#include <QBuffer>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include "render/videoparams.h"
TEST(RenderVideoParams, SaveLoadRoundTrip)
{
olive::VideoParams params;
params.set_width(1280);
params.set_height(720);
params.set_frame_rate(olive::core::rational(24, 1));
params.set_pixel_aspect_ratio(olive::core::rational(1, 1));
params.set_colorspace(QStringLiteral("test"));
QByteArray xml;
QBuffer buffer(&xml);
buffer.open(QIODevice::WriteOnly);
QXmlStreamWriter writer(&buffer);
writer.writeStartDocument();
writer.writeStartElement(QStringLiteral("videoparams"));
params.Save(&writer);
writer.writeEndElement();
writer.writeEndDocument();
buffer.close();
olive::VideoParams loaded;
QBuffer read_buffer(&xml);
read_buffer.open(QIODevice::ReadOnly);
QXmlStreamReader reader(&read_buffer);
EXPECT_TRUE(reader.readNextStartElement());
EXPECT_EQ(reader.name().toString(), QStringLiteral("videoparams"));
loaded.Load(&reader);
EXPECT_EQ(loaded.width(), 1280);
EXPECT_EQ(loaded.height(), 720);
EXPECT_EQ(loaded.frame_rate(), olive::core::rational(24, 1));
EXPECT_EQ(loaded.pixel_aspect_ratio(), olive::core::rational(1, 1));
EXPECT_EQ(loaded.colorspace(), QStringLiteral("test"));
}
+52
View File
@@ -0,0 +1,52 @@
#include <gtest/gtest.h>
#include <QEventLoop>
#include <QTimer>
#include "task/taskmanager.h"
namespace {
class DummyTask final : public olive::Task {
public:
explicit DummyTask(bool *ran)
: ran_(ran)
{
SetTitle(QStringLiteral("DummyTask"));
}
protected:
bool Run() override
{
if (ran_) {
*ran_ = true;
}
return true;
}
private:
bool *ran_ = nullptr;
};
}
TEST(TaskManager, AddAndRunTask)
{
olive::TaskManager::CreateInstance();
olive::TaskManager *mgr = olive::TaskManager::instance();
ASSERT_NE(mgr, nullptr);
bool ran = false;
DummyTask *task = new DummyTask(&ran);
QEventLoop loop;
QObject::connect(task, &olive::Task::Finished, &loop, [&loop](olive::Task *, bool) {
loop.quit();
});
mgr->AddTask(task);
QTimer::singleShot(5000, &loop, &QEventLoop::quit);
loop.exec();
EXPECT_TRUE(ran);
olive::TaskManager::DestroyInstance();
}
+38
View File
@@ -0,0 +1,38 @@
#include <gtest/gtest.h>
#include <QBuffer>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include "timeline/timelinemarker.h"
TEST(TimelineMarker, SaveLoadRoundTrip)
{
olive::TimelineMarker marker;
marker.set_time(olive::core::rational(10, 1));
marker.set_name(QStringLiteral("Marker"));
marker.set_color(5);
QByteArray xml;
QBuffer buffer(&xml);
buffer.open(QIODevice::WriteOnly);
QXmlStreamWriter writer(&buffer);
writer.writeStartDocument();
writer.writeStartElement(QStringLiteral("marker"));
marker.save(&writer);
writer.writeEndElement();
writer.writeEndDocument();
buffer.close();
olive::TimelineMarker loaded;
QBuffer read_buffer(&xml);
read_buffer.open(QIODevice::ReadOnly);
QXmlStreamReader reader(&read_buffer);
EXPECT_TRUE(reader.readNextStartElement());
EXPECT_EQ(reader.name().toString(), QStringLiteral("marker"));
loaded.load(&reader);
EXPECT_EQ(loaded.time(), olive::core::rational(10, 1));
EXPECT_EQ(loaded.name(), QStringLiteral("Marker"));
EXPECT_EQ(loaded.color(), 5);
}
+44
View File
@@ -0,0 +1,44 @@
#include <gtest/gtest.h>
#include "undo/undostack.h"
#include "undo/undocommand.h"
namespace {
class TestCommand final : public olive::UndoCommand {
public:
explicit TestCommand(int *value)
: value_(value)
{
}
protected:
void redo() override
{
if (value_) {
(*value_)++;
}
}
void undo() override
{
if (value_) {
(*value_)--;
}
}
private:
int *value_ = nullptr;
};
}
TEST(UndoStack, PushUndoRedo)
{
int counter = 0;
olive::UndoStack stack;
stack.push(new TestCommand(&counter), QStringLiteral("Test"));
EXPECT_EQ(counter, 1);
stack.undo();
EXPECT_EQ(counter, 0);
stack.redo();
EXPECT_EQ(counter, 1);
}