test: expand gtest coverage for common/core utilities

Add unit tests for:
- CommandLineParser, Debug handler, Decibel, Digit, JobTime, FFmpegUtils
- Core StringUtils, Timecode, TimeRange, Bezier, Color
- Extend Frame tests to cover pixel access, interlace, conversion

Coverage improvements (app + ext/core):
- commandlineparser.cpp: 0% -> 93%
- debug.cpp: 0% -> 85%
- decibel.h: 25% -> 88%
- digit.h: 0% -> 100%
- ffmpegutils.cpp: 29% -> 90%
- frame.cpp: 53% -> 97%
- bezier.cpp: 16% -> 98%
- color.cpp: 13% -> 71%
- Overall line coverage: 17% -> 19%
This commit is contained in:
2026-07-13 10:19:30 +08:00
parent 748c53ac5c
commit c57608b8f8
13 changed files with 1159 additions and 0 deletions
+11
View File
@@ -5,6 +5,12 @@ add_executable(olive-gtest
common_range_test.cpp common_range_test.cpp
common_filefunctions_test.cpp common_filefunctions_test.cpp
common_qtutils_test.cpp common_qtutils_test.cpp
common_commandlineparser_test.cpp
common_debug_test.cpp
common_decibel_test.cpp
common_digit_test.cpp
common_jobtime_test.cpp
common_ffmpegutils_test.cpp
config_test.cpp config_test.cpp
audio_level_meter_test.cpp audio_level_meter_test.cpp
audio_synchronizer_test.cpp audio_synchronizer_test.cpp
@@ -16,6 +22,11 @@ add_executable(olive-gtest
node_globals_test.cpp node_globals_test.cpp
node_project_test.cpp node_project_test.cpp
render_videoparams_test.cpp render_videoparams_test.cpp
core_stringutils_test.cpp
core_timecode_test.cpp
core_timerange_test.cpp
core_bezier_test.cpp
core_color_test.cpp
render_videoparams_branch_test.cpp render_videoparams_branch_test.cpp
render_audioparams_test.cpp render_audioparams_test.cpp
render_audioparams_branch_test.cpp render_audioparams_branch_test.cpp
+130
View File
@@ -54,3 +54,133 @@ TEST(CodecFrame, DestroyDeallocatesData)
EXPECT_FALSE(frame->is_allocated()); EXPECT_FALSE(frame->is_allocated());
EXPECT_EQ(frame->data(), nullptr); EXPECT_EQ(frame->data(), nullptr);
} }
TEST(CodecFrame, AllocateInvalidParamsFails)
{
olive::Frame frame;
EXPECT_FALSE(frame.allocate());
}
TEST(CodecFrame, DoubleAllocateReturnsTrue)
{
olive::VideoParams params(8, 8, olive::core::PixelFormat::U8,
olive::VideoParams::kRGBAChannelCount);
olive::FramePtr frame = olive::Frame::Create();
frame->set_video_params(params);
EXPECT_TRUE(frame->allocate());
EXPECT_TRUE(frame->allocate());
}
TEST(CodecFrame, ContainsPixel)
{
olive::VideoParams params(8, 8, olive::core::PixelFormat::U8,
olive::VideoParams::kRGBAChannelCount);
olive::FramePtr frame = olive::Frame::Create();
frame->set_video_params(params);
EXPECT_FALSE(frame->contains_pixel(0, 0));
frame->allocate();
EXPECT_TRUE(frame->contains_pixel(0, 0));
EXPECT_TRUE(frame->contains_pixel(7, 7));
EXPECT_FALSE(frame->contains_pixel(8, 0));
EXPECT_FALSE(frame->contains_pixel(0, 8));
EXPECT_FALSE(frame->contains_pixel(-1, 0));
}
TEST(CodecFrame, GetPixelOutOfBoundsReturnsBlack)
{
olive::VideoParams params(8, 8, olive::core::PixelFormat::U8,
olive::VideoParams::kRGBAChannelCount);
olive::FramePtr frame = olive::Frame::Create();
frame->set_video_params(params);
frame->allocate();
olive::core::Color c = frame->get_pixel(-1, 0);
EXPECT_FLOAT_EQ(c.red(), 0.0f);
}
TEST(CodecFrame, SetAndGetPixel)
{
olive::VideoParams params(8, 8, olive::core::PixelFormat::U8,
olive::VideoParams::kRGBAChannelCount);
olive::FramePtr frame = olive::Frame::Create();
frame->set_video_params(params);
frame->allocate();
olive::core::Color red(1.0f, 0.0f, 0.0f, 1.0f);
frame->set_pixel(3, 3, red);
olive::core::Color got = frame->get_pixel(3, 3);
EXPECT_NEAR(got.red(), 1.0f, 0.01f);
EXPECT_NEAR(got.green(), 0.0f, 0.01f);
EXPECT_NEAR(got.blue(), 0.0f, 0.01f);
}
TEST(CodecFrame, TimestampRoundTrip)
{
olive::FramePtr frame = olive::Frame::Create();
frame->set_timestamp(olive::core::rational(5, 1));
EXPECT_EQ(frame->timestamp(), olive::core::rational(5, 1));
}
TEST(CodecFrame, GenerateLineSizeBytes)
{
EXPECT_EQ(olive::Frame::generate_linesize_bytes(
64, olive::core::PixelFormat::U8,
olive::VideoParams::kRGBAChannelCount),
64 * 4);
}
TEST(CodecFrame, InterlaceFrames)
{
olive::VideoParams params(4, 4, olive::core::PixelFormat::U8,
olive::VideoParams::kRGBAChannelCount);
olive::FramePtr top = olive::Frame::Create();
top->set_video_params(params);
top->allocate();
memset(top->data(), 0xFF, top->allocated_size());
olive::FramePtr bottom = olive::Frame::Create();
bottom->set_video_params(params);
bottom->allocate();
memset(bottom->data(), 0x00, bottom->allocated_size());
olive::FramePtr interlaced = olive::Frame::Interlace(top, bottom);
ASSERT_NE(interlaced, nullptr);
EXPECT_EQ(interlaced->width(), 4);
EXPECT_EQ(interlaced->height(), 4);
}
TEST(CodecFrame, InterlaceIncompatibleReturnsNull)
{
olive::FramePtr top = olive::Frame::Create();
top->set_video_params(olive::VideoParams(
4, 4, olive::core::PixelFormat::U8, olive::VideoParams::kRGBAChannelCount));
top->allocate();
olive::FramePtr bottom = olive::Frame::Create();
bottom->set_video_params(olive::VideoParams(
8, 8, olive::core::PixelFormat::U8, olive::VideoParams::kRGBAChannelCount));
bottom->allocate();
EXPECT_EQ(olive::Frame::Interlace(top, bottom), nullptr);
}
TEST(CodecFrame, ConvertU8ToU16)
{
olive::VideoParams params(4, 4, olive::core::PixelFormat::U8,
olive::VideoParams::kRGBAChannelCount);
olive::FramePtr frame = olive::Frame::Create();
frame->set_video_params(params);
frame->allocate();
olive::FramePtr converted =
frame->convert(olive::core::PixelFormat::U16);
ASSERT_NE(converted, nullptr);
EXPECT_EQ(converted->format(), olive::core::PixelFormat::U16);
EXPECT_EQ(converted->width(), 4);
EXPECT_EQ(converted->height(), 4);
}
@@ -0,0 +1,83 @@
#include <gtest/gtest.h>
#include "common/commandlineparser.h"
TEST(CommonCommandLineParser, OptionWithoutArgument)
{
CommandLineParser parser;
const CommandLineParser::Option *opt = parser.AddOption(
{QStringLiteral("help"), QStringLiteral("h")},
QStringLiteral("Show help"), false);
parser.Process({QStringLiteral("app"), QStringLiteral("-help")});
EXPECT_TRUE(opt->IsSet());
}
TEST(CommonCommandLineParser, ShortOption)
{
CommandLineParser parser;
const CommandLineParser::Option *opt = parser.AddOption(
{QStringLiteral("help"), QStringLiteral("h")},
QStringLiteral("Show help"), false);
parser.Process({QStringLiteral("app"), QStringLiteral("-h")});
EXPECT_TRUE(opt->IsSet());
}
TEST(CommonCommandLineParser, OptionWithArgument)
{
CommandLineParser parser;
const CommandLineParser::Option *opt = parser.AddOption(
{QStringLiteral("project")}, QStringLiteral("Project file"), true,
QStringLiteral("file"));
parser.Process({QStringLiteral("app"), QStringLiteral("-project"),
QStringLiteral("test.ove")});
EXPECT_TRUE(opt->IsSet());
EXPECT_EQ(opt->GetSetting(), QStringLiteral("test.ove"));
}
TEST(CommonCommandLineParser, PositionalArgument)
{
CommandLineParser parser;
const CommandLineParser::PositionalArgument *arg =
parser.AddPositionalArgument(QStringLiteral("filename"),
QStringLiteral("Project file"), true);
parser.Process({QStringLiteral("app"), QStringLiteral("test.ove")});
EXPECT_EQ(arg->GetSetting(), QStringLiteral("test.ove"));
}
TEST(CommonCommandLineParser, UnknownOptionWarning)
{
CommandLineParser parser;
parser.AddOption({QStringLiteral("known")}, QStringLiteral("Known"));
// Should not crash; unknown option is logged
parser.Process({QStringLiteral("app"), QStringLiteral("-unknown")});
}
TEST(CommonCommandLineParser, UnknownPositionalWarning)
{
CommandLineParser parser;
// Should not crash; unknown positional is logged
parser.Process({QStringLiteral("app"), QStringLiteral("extra")});
}
TEST(CommonCommandLineParser, HiddenOptionExcludedFromHelp)
{
CommandLineParser parser;
parser.AddOption({QStringLiteral("visible")}, QStringLiteral("Visible"));
parser.AddOption({QStringLiteral("hidden")}, QStringLiteral("Hidden"),
false, QString(), true);
parser.AddPositionalArgument(QStringLiteral("file"),
QStringLiteral("Input file"));
// Should not crash; hidden option should be skipped during help output
parser.PrintHelp("/usr/bin/app");
}
+16
View File
@@ -0,0 +1,16 @@
#include <gtest/gtest.h>
#include "common/debug.h"
TEST(CommonDebug, DebugHandlerFormatsAllLevels)
{
// Install handler and restore after test
QtMessageHandler old = qInstallMessageHandler(olive::DebugHandler);
qDebug() << "debug message";
qInfo() << "info message";
qWarning() << "warning message";
qCritical() << "critical message";
qInstallMessageHandler(old);
}
+52
View File
@@ -0,0 +1,52 @@
#include <gtest/gtest.h>
#include "common/decibel.h"
TEST(CommonDecibel, FromLinearZeroReturnsMinimum)
{
EXPECT_DOUBLE_EQ(olive::Decibel::fromLinear(0.0), olive::Decibel::MINIMUM);
}
TEST(CommonDecibel, FromLinearOneReturnsZero)
{
EXPECT_DOUBLE_EQ(olive::Decibel::fromLinear(1.0), 0.0);
}
TEST(CommonDecibel, FromLinearTenReturnsTwenty)
{
EXPECT_DOUBLE_EQ(olive::Decibel::fromLinear(10.0), 20.0);
}
TEST(CommonDecibel, ToLinearZeroReturnsOne)
{
EXPECT_DOUBLE_EQ(olive::Decibel::toLinear(0.0), 1.0);
}
TEST(CommonDecibel, ToLinearMinimumReturnsZero)
{
EXPECT_DOUBLE_EQ(olive::Decibel::toLinear(olive::Decibel::MINIMUM), 0.0);
}
TEST(CommonDecibel, ToLinearTwentyReturnsTen)
{
EXPECT_DOUBLE_EQ(olive::Decibel::toLinear(20.0), 10.0);
}
TEST(CommonDecibel, FromLogarithmicAtEdges)
{
EXPECT_DOUBLE_EQ(olive::Decibel::fromLogarithmic(0.0),
olive::Decibel::MINIMUM);
EXPECT_DOUBLE_EQ(olive::Decibel::fromLogarithmic(1.0), 0.0);
}
TEST(CommonDecibel, ToLogarithmicAtEdges)
{
EXPECT_DOUBLE_EQ(olive::Decibel::toLogarithmic(0.0), 1.0);
}
TEST(CommonDecibel, LinearLogarithmicRoundTrip)
{
EXPECT_NEAR(olive::Decibel::LogarithmicToLinear(
olive::Decibel::LinearToLogarithmic(0.5)),
0.5, 1e-6);
}
+23
View File
@@ -0,0 +1,23 @@
#include <gtest/gtest.h>
#include "common/digit.h"
TEST(CommonDigit, SingleDigit)
{
EXPECT_EQ(olive::GetDigitCount(0), 1);
EXPECT_EQ(olive::GetDigitCount(5), 1);
EXPECT_EQ(olive::GetDigitCount(-5), 1);
}
TEST(CommonDigit, MultipleDigits)
{
EXPECT_EQ(olive::GetDigitCount(10), 2);
EXPECT_EQ(olive::GetDigitCount(999), 3);
EXPECT_EQ(olive::GetDigitCount(1000), 4);
EXPECT_EQ(olive::GetDigitCount(-12345), 5);
}
TEST(CommonDigit, LargeValue)
{
EXPECT_EQ(olive::GetDigitCount(123456789012345LL), 15);
}
+149
View File
@@ -0,0 +1,149 @@
#include <gtest/gtest.h>
#include "common/ffmpegutils.h"
using namespace olive;
TEST(CommonFFmpegUtils, GetNativeSampleFormatMapsCorrectly)
{
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_U8),
SampleFormat::U8);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S16),
SampleFormat::S16);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S32),
SampleFormat::S32);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S64),
SampleFormat::S64);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_FLT),
SampleFormat::F32);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_DBL),
SampleFormat::F64);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_U8P),
SampleFormat::U8P);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S16P),
SampleFormat::S16P);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S32P),
SampleFormat::S32P);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S64P),
SampleFormat::S64P);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_FLTP),
SampleFormat::F32P);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_DBLP),
SampleFormat::F64P);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_NONE),
SampleFormat::INVALID);
}
TEST(CommonFFmpegUtils, GetFFmpegSampleFormatMapsCorrectly)
{
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::U8),
AV_SAMPLE_FMT_U8);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S16),
AV_SAMPLE_FMT_S16);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S32),
AV_SAMPLE_FMT_S32);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S64),
AV_SAMPLE_FMT_S64);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::F32),
AV_SAMPLE_FMT_FLT);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::F64),
AV_SAMPLE_FMT_DBL);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::U8P),
AV_SAMPLE_FMT_U8P);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S16P),
AV_SAMPLE_FMT_S16P);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S32P),
AV_SAMPLE_FMT_S32P);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S64P),
AV_SAMPLE_FMT_S64P);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::F32P),
AV_SAMPLE_FMT_FLTP);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::F64P),
AV_SAMPLE_FMT_DBLP);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::INVALID),
AV_SAMPLE_FMT_NONE);
}
TEST(CommonFFmpegUtils, GetSwsColorspaceFromAVColorSpace)
{
EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_BT709),
SWS_CS_ITU709);
EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_FCC),
SWS_CS_FCC);
EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_BT470BG),
SWS_CS_ITU624);
EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_SMPTE170M),
SWS_CS_SMPTE170M);
EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_SMPTE240M),
SWS_CS_SMPTE240M);
EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_BT2020_NCL),
SWS_CS_BT2020);
EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_UNSPECIFIED),
SWS_CS_DEFAULT);
}
TEST(CommonFFmpegUtils, ConvertJPEGSpaceToRegularSpace)
{
EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUVJ420P),
AV_PIX_FMT_YUV420P);
EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUVJ422P),
AV_PIX_FMT_YUV422P);
EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUVJ444P),
AV_PIX_FMT_YUV444P);
EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUVJ440P),
AV_PIX_FMT_YUV440P);
EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUVJ411P),
AV_PIX_FMT_YUV411P);
EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUV420P),
AV_PIX_FMT_YUV420P);
}
TEST(CommonFFmpegUtils, GetCompatiblePixelFormatNative)
{
EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::U8),
PixelFormat::U8);
EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::U10),
PixelFormat::U8);
EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::U16),
PixelFormat::U16);
EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::F16),
PixelFormat::U16);
EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::F32),
PixelFormat::U16);
EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::INVALID),
PixelFormat::INVALID);
}
TEST(CommonFFmpegUtils, GetFFmpegPixelFormat)
{
EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::U8,
VideoParams::kRGBChannelCount),
AV_PIX_FMT_RGB24);
EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::U16,
VideoParams::kRGBChannelCount),
AV_PIX_FMT_RGB48);
EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::F32,
VideoParams::kRGBChannelCount),
AV_PIX_FMT_RGBF32);
EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::U8,
VideoParams::kRGBAChannelCount),
AV_PIX_FMT_RGBA);
EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::U16,
VideoParams::kRGBAChannelCount),
AV_PIX_FMT_RGBA64);
EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::F32,
VideoParams::kRGBAChannelCount),
AV_PIX_FMT_RGBAF32);
EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::INVALID, 0),
AV_PIX_FMT_NONE);
}
TEST(CommonFFmpegUtils, GetCompatiblePixelFormatAV)
{
AVPixelFormat fmt = FFmpegUtils::GetCompatiblePixelFormat(AV_PIX_FMT_YUV420P);
EXPECT_NE(fmt, AV_PIX_FMT_NONE);
fmt = FFmpegUtils::GetCompatiblePixelFormat(AV_PIX_FMT_YUV420P,
PixelFormat::U8);
EXPECT_EQ(fmt, AV_PIX_FMT_RGBA);
}
+42
View File
@@ -0,0 +1,42 @@
#include <gtest/gtest.h>
#include "common/jobtime.h"
TEST(CommonJobTime, ConstructorAcquiresValue)
{
olive::JobTime a;
olive::JobTime b;
EXPECT_NE(a.value(), b.value());
EXPECT_LT(a.value(), b.value());
}
TEST(CommonJobTime, AcquireUpdatesValue)
{
olive::JobTime a;
uint64_t first = a.value();
a.Acquire();
uint64_t second = a.value();
EXPECT_GT(second, first);
}
TEST(CommonJobTime, ComparisonOperators)
{
olive::JobTime a;
olive::JobTime b;
EXPECT_LT(a, b);
EXPECT_GT(b, a);
EXPECT_LE(a, a);
EXPECT_GE(b, b);
EXPECT_EQ(a, a);
EXPECT_NE(a, b);
}
TEST(CommonJobTime, DebugStream)
{
olive::JobTime a;
QDebug debug(QtDebugMsg);
debug << a;
}
+120
View File
@@ -0,0 +1,120 @@
#include <gtest/gtest.h>
#include "olive/core/util/bezier.h"
using namespace olive::core;
TEST(CoreBezier, DefaultConstruction)
{
Bezier b;
EXPECT_DOUBLE_EQ(b.x(), 0.0);
EXPECT_DOUBLE_EQ(b.y(), 0.0);
EXPECT_DOUBLE_EQ(b.cp1_x(), 0.0);
EXPECT_DOUBLE_EQ(b.cp1_y(), 0.0);
EXPECT_DOUBLE_EQ(b.cp2_x(), 0.0);
EXPECT_DOUBLE_EQ(b.cp2_y(), 0.0);
}
TEST(CoreBezier, ValueConstruction)
{
Bezier b(1.0, 2.0);
EXPECT_DOUBLE_EQ(b.x(), 1.0);
EXPECT_DOUBLE_EQ(b.y(), 2.0);
}
TEST(CoreBezier, FullConstruction)
{
Bezier b(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
EXPECT_DOUBLE_EQ(b.x(), 1.0);
EXPECT_DOUBLE_EQ(b.y(), 2.0);
EXPECT_DOUBLE_EQ(b.cp1_x(), 3.0);
EXPECT_DOUBLE_EQ(b.cp1_y(), 4.0);
EXPECT_DOUBLE_EQ(b.cp2_x(), 5.0);
EXPECT_DOUBLE_EQ(b.cp2_y(), 6.0);
}
TEST(CoreBezier, Setters)
{
Bezier b;
b.set_x(10.0);
b.set_y(20.0);
b.set_cp1_x(30.0);
b.set_cp1_y(40.0);
b.set_cp2_x(50.0);
b.set_cp2_y(60.0);
EXPECT_DOUBLE_EQ(b.x(), 10.0);
EXPECT_DOUBLE_EQ(b.y(), 20.0);
EXPECT_DOUBLE_EQ(b.cp1_x(), 30.0);
EXPECT_DOUBLE_EQ(b.cp1_y(), 40.0);
EXPECT_DOUBLE_EQ(b.cp2_x(), 50.0);
EXPECT_DOUBLE_EQ(b.cp2_y(), 60.0);
}
TEST(CoreBezier, QuadraticXtoT)
{
double t = Bezier::QuadraticXtoT(0.5, 0.0, 0.5, 1.0);
EXPECT_NEAR(t, 0.5, 0.00001);
t = Bezier::QuadraticXtoT(0.0, 0.0, 0.5, 1.0);
EXPECT_NEAR(t, 0.0, 0.00001);
t = Bezier::QuadraticXtoT(1.0, 0.0, 0.5, 1.0);
EXPECT_NEAR(t, 1.0, 0.00001);
}
TEST(CoreBezier, QuadraticTtoY)
{
EXPECT_NEAR(Bezier::QuadraticTtoY(0.0, 0.5, 1.0, 0.0), 0.0, 0.00001);
EXPECT_NEAR(Bezier::QuadraticTtoY(0.0, 0.5, 1.0, 0.5), 0.5, 0.00001);
EXPECT_NEAR(Bezier::QuadraticTtoY(0.0, 0.5, 1.0, 1.0), 1.0, 0.00001);
}
TEST(CoreBezier, QuadraticXtoY)
{
Imath::V2d a(0.0, 0.0);
Imath::V2d b(0.5, 0.5);
Imath::V2d c(1.0, 1.0);
EXPECT_NEAR(Bezier::QuadraticXtoY(0.5, a, b, c), 0.5, 0.00001);
}
TEST(CoreBezier, CubicXtoT)
{
double t = Bezier::CubicXtoT(0.5, 0.0, 0.33, 0.66, 1.0);
EXPECT_NEAR(t, 0.5, 0.01);
}
TEST(CoreBezier, CubicTtoY)
{
EXPECT_NEAR(Bezier::CubicTtoY(0.0, 0.33, 0.66, 1.0, 0.0), 0.0, 0.00001);
EXPECT_NEAR(Bezier::CubicTtoY(0.0, 0.33, 0.66, 1.0, 1.0), 1.0, 0.00001);
}
TEST(CoreBezier, CubicXtoY)
{
Imath::V2d a(0.0, 0.0);
Imath::V2d b(0.33, 0.0);
Imath::V2d c(0.66, 1.0);
Imath::V2d d(1.0, 1.0);
double y = Bezier::CubicXtoY(0.5, a, b, c, d);
EXPECT_GE(y, 0.0);
EXPECT_LE(y, 1.0);
}
TEST(CoreBezier, VectorConverters)
{
Bezier b(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
Imath::V2d v = b.to_vec();
EXPECT_DOUBLE_EQ(v.x, 1.0);
EXPECT_DOUBLE_EQ(v.y, 2.0);
Imath::V2d cp1 = b.control_point_1_to_vec();
EXPECT_DOUBLE_EQ(cp1.x, 3.0);
EXPECT_DOUBLE_EQ(cp1.y, 4.0);
Imath::V2d cp2 = b.control_point_2_to_vec();
EXPECT_DOUBLE_EQ(cp2.x, 5.0);
EXPECT_DOUBLE_EQ(cp2.y, 6.0);
}
+169
View File
@@ -0,0 +1,169 @@
#include <gtest/gtest.h>
#include "olive/core/util/color.h"
using namespace olive::core;
TEST(CoreColor, DefaultConstruction)
{
Color c;
EXPECT_FLOAT_EQ(c.red(), 0.0f);
EXPECT_FLOAT_EQ(c.green(), 0.0f);
EXPECT_FLOAT_EQ(c.blue(), 0.0f);
EXPECT_FLOAT_EQ(c.alpha(), 0.0f);
}
TEST(CoreColor, ValueConstruction)
{
Color c(0.1f, 0.2f, 0.3f, 0.4f);
EXPECT_FLOAT_EQ(c.red(), 0.1f);
EXPECT_FLOAT_EQ(c.green(), 0.2f);
EXPECT_FLOAT_EQ(c.blue(), 0.3f);
EXPECT_FLOAT_EQ(c.alpha(), 0.4f);
}
TEST(CoreColor, SettersAndDataAccess)
{
Color c;
c.set_red(0.5f);
c.set_green(0.6f);
c.set_blue(0.7f);
c.set_alpha(0.8f);
EXPECT_FLOAT_EQ(c.data()[0], 0.5f);
EXPECT_FLOAT_EQ(c.data()[1], 0.6f);
EXPECT_FLOAT_EQ(c.data()[2], 0.7f);
EXPECT_FLOAT_EQ(c.data()[3], 0.8f);
}
TEST(CoreColor, FromHsvRed)
{
Color c = Color::fromHsv(0.0f, 1.0f, 1.0f);
EXPECT_NEAR(c.red(), 1.0f, 0.001f);
EXPECT_NEAR(c.green(), 0.0f, 0.001f);
EXPECT_NEAR(c.blue(), 0.0f, 0.001f);
}
TEST(CoreColor, FromHsvGreen)
{
Color c = Color::fromHsv(120.0f, 1.0f, 1.0f);
EXPECT_NEAR(c.red(), 0.0f, 0.001f);
EXPECT_NEAR(c.green(), 1.0f, 0.001f);
EXPECT_NEAR(c.blue(), 0.0f, 0.001f);
}
TEST(CoreColor, FromHsvBlue)
{
Color c = Color::fromHsv(240.0f, 1.0f, 1.0f);
EXPECT_NEAR(c.red(), 0.0f, 0.001f);
EXPECT_NEAR(c.green(), 0.0f, 0.001f);
EXPECT_NEAR(c.blue(), 1.0f, 0.001f);
}
TEST(CoreColor, HsvRoundTrip)
{
Color original(0.8f, 0.4f, 0.2f);
float h, s, v;
original.toHsv(&h, &s, &v);
EXPECT_NEAR(original.hsv_hue(), h, 0.001f);
EXPECT_NEAR(original.hsv_saturation(), s, 0.001f);
EXPECT_NEAR(original.value(), v, 0.001f);
}
TEST(CoreColor, HslRoundTrip)
{
Color original(0.2f, 0.5f, 0.8f);
float h, s, l;
original.toHsl(&h, &s, &l);
EXPECT_NEAR(original.hsl_hue(), h, 0.001f);
EXPECT_NEAR(original.hsl_saturation(), s, 0.001f);
EXPECT_NEAR(original.lightness(), l, 0.001f);
}
TEST(CoreColor, ArithmeticOperators)
{
Color a(1.0f, 2.0f, 3.0f, 4.0f);
Color b(0.5f, 0.5f, 0.5f, 0.5f);
Color sum = a + b;
EXPECT_FLOAT_EQ(sum.red(), 1.5f);
Color diff = a - b;
EXPECT_FLOAT_EQ(diff.red(), 0.5f);
Color scaled = a * 2.0f;
EXPECT_FLOAT_EQ(scaled.red(), 2.0f);
Color divided = a / 2.0f;
EXPECT_FLOAT_EQ(divided.red(), 0.5f);
Color added_scalar = a + 1.0f;
EXPECT_FLOAT_EQ(added_scalar.red(), 2.0f);
}
TEST(CoreColor, CompoundAssignment)
{
Color c(1.0f, 2.0f, 3.0f, 4.0f);
c += Color(0.5f, 0.5f, 0.5f, 0.5f);
EXPECT_FLOAT_EQ(c.red(), 1.5f);
c *= 2.0f;
EXPECT_FLOAT_EQ(c.red(), 3.0f);
}
TEST(CoreColor, GetRoughLuminance)
{
Color white(1.0f, 1.0f, 1.0f);
EXPECT_FLOAT_EQ(white.GetRoughLuminance(), 1.0f);
Color black(0.0f, 0.0f, 0.0f);
EXPECT_FLOAT_EQ(black.GetRoughLuminance(), 0.0f);
}
TEST(CoreColor, ToDataAndFromDataU8)
{
Color c(1.0f, 0.5f, 0.0f, 1.0f);
uint8_t data[4];
c.toData(reinterpret_cast<char *>(data), PixelFormat::U8, 4);
EXPECT_EQ(data[0], 255u);
EXPECT_EQ(data[1], 127u);
EXPECT_EQ(data[2], 0u);
EXPECT_EQ(data[3], 255u);
Color restored = Color::fromData(reinterpret_cast<const char *>(data),
PixelFormat::U8, 4);
EXPECT_NEAR(restored.red(), 1.0f, 0.01f);
EXPECT_NEAR(restored.green(), 0.5f, 0.01f);
}
TEST(CoreColor, ToDataAndFromDataF32)
{
Color c(0.25f, 0.5f, 0.75f, 1.0f);
float data[4];
c.toData(reinterpret_cast<char *>(data), PixelFormat::F32, 4);
EXPECT_FLOAT_EQ(data[0], 0.25f);
EXPECT_FLOAT_EQ(data[1], 0.5f);
EXPECT_FLOAT_EQ(data[2], 0.75f);
EXPECT_FLOAT_EQ(data[3], 1.0f);
Color restored = Color::fromData(reinterpret_cast<const char *>(data),
PixelFormat::F32, 4);
EXPECT_FLOAT_EQ(restored.red(), 0.25f);
}
TEST(CoreColor, ToDataAndFromDataU10)
{
Color c(1.0f, 0.5f, 0.0f, 1.0f);
uint32_t data;
c.toData(reinterpret_cast<char *>(&data), PixelFormat::U10, 4);
Color restored = Color::fromData(reinterpret_cast<const char *>(&data),
PixelFormat::U10, 4);
EXPECT_NEAR(restored.red(), 1.0f, 0.001f);
EXPECT_NEAR(restored.green(), 0.5f, 0.001f);
EXPECT_NEAR(restored.blue(), 0.0f, 0.001f);
}
+65
View File
@@ -0,0 +1,65 @@
#include <gtest/gtest.h>
#include <regex>
#include "olive/core/util/stringutils.h"
using namespace olive::core;
TEST(CoreStringUtils, Split)
{
auto result = StringUtils::split("a,b,c", ',');
ASSERT_EQ(result.size(), 3u);
EXPECT_EQ(result[0], "a");
EXPECT_EQ(result[1], "b");
EXPECT_EQ(result[2], "c");
}
TEST(CoreStringUtils, SplitRegex)
{
auto result = StringUtils::split_regex("one:two;three", std::regex("(:)|(;)| "));
ASSERT_EQ(result.size(), 3u);
EXPECT_EQ(result[0], "one");
EXPECT_EQ(result[1], "two");
EXPECT_EQ(result[2], "three");
}
TEST(CoreStringUtils, ToInt)
{
bool ok = false;
EXPECT_EQ(StringUtils::to_int("42", &ok), 42);
EXPECT_TRUE(ok);
EXPECT_EQ(StringUtils::to_int("-7", 10, &ok), -7);
EXPECT_TRUE(ok);
EXPECT_EQ(StringUtils::to_int("ff", 16, &ok), 255);
EXPECT_TRUE(ok);
EXPECT_EQ(StringUtils::to_int("abc", &ok), 0);
EXPECT_FALSE(ok);
}
TEST(CoreStringUtils, ToStringLeftpad)
{
EXPECT_EQ(StringUtils::to_string_leftpad(5, 3), "005");
EXPECT_EQ(StringUtils::to_string_leftpad(123, 2), "123");
EXPECT_EQ(StringUtils::to_string_leftpad(7, 4, '*'), "***7");
}
TEST(CoreStringUtils, Format)
{
EXPECT_EQ(StringUtils::format("Hello %s %d", "world", 42),
"Hello world 42");
}
TEST(CoreStringUtils, Trim)
{
std::string s = " hello world ";
StringUtils::trim(s);
EXPECT_EQ(s, "hello world");
EXPECT_EQ(StringUtils::trimmed("\t\nvalue\t\n"), "value");
EXPECT_EQ(StringUtils::ltrimmed(" left"), "left");
EXPECT_EQ(StringUtils::rtrimmed("right "), "right");
}
+115
View File
@@ -0,0 +1,115 @@
#include <gtest/gtest.h>
#include "olive/core/util/timecodefunctions.h"
using namespace olive::core;
TEST(CoreTimecode, TimeToTimecodeSeconds)
{
rational time(5, 1);
rational tb(1, 25);
std::string tc = Timecode::time_to_timecode(time, tb, Timecode::kTimecodeSeconds);
EXPECT_EQ(tc, "00:00:05.000");
}
TEST(CoreTimecode, TimeToTimecodeNonDropFrame)
{
rational time(2, 1);
rational tb(1, 25);
std::string tc =
Timecode::time_to_timecode(time, tb, Timecode::kTimecodeNonDropFrame);
EXPECT_EQ(tc, "00:00:02:00");
}
TEST(CoreTimecode, TimeToTimecodePlusSign)
{
rational time(1, 1);
rational tb(1, 25);
std::string tc = Timecode::time_to_timecode(time, tb,
Timecode::kTimecodeSeconds, true);
EXPECT_EQ(tc.substr(0, 1), "+");
}
TEST(CoreTimecode, TimeToTimecodeInvalidTimebase)
{
rational time(1, 1);
EXPECT_EQ(Timecode::time_to_timecode(time, rational(), Timecode::kFrames),
"INVALID TIMEBASE");
}
TEST(CoreTimecode, TimecodeToTimeSeconds)
{
rational tb(1, 25);
bool ok = false;
rational t = Timecode::timecode_to_time("00:00:05.500", tb,
Timecode::kTimecodeSeconds, &ok);
EXPECT_TRUE(ok);
EXPECT_EQ(t, rational(11, 2));
}
TEST(CoreTimecode, TimecodeToTimeNonDropFrame)
{
rational tb(1, 25);
bool ok = false;
rational t = Timecode::timecode_to_time("00:00:02:03", tb,
Timecode::kTimecodeNonDropFrame, &ok);
EXPECT_TRUE(ok);
EXPECT_EQ(t, rational(53, 25));
}
TEST(CoreTimecode, TimecodeToTimeInvalid)
{
rational tb(1, 25);
bool ok = true;
Timecode::timecode_to_time("not a timecode", tb,
Timecode::kTimecodeSeconds, &ok);
EXPECT_FALSE(ok);
}
TEST(CoreTimecode, TimeToString)
{
EXPECT_EQ(Timecode::time_to_string(3661000), "01:01:01");
}
TEST(CoreTimecode, SnapTimeToTimebase)
{
rational tb(1, 25);
rational snapped = Timecode::snap_time_to_timebase(rational(1, 10), tb);
// 0.1s @ 25fps rounds to frame 3 (0.12s)
EXPECT_EQ(snapped, rational(3, 25));
}
TEST(CoreTimecode, TimeToTimestamp)
{
rational tb(1, 25);
EXPECT_EQ(Timecode::time_to_timestamp(rational(2, 1), tb), 50);
EXPECT_EQ(Timecode::time_to_timestamp(0.08, tb, Timecode::kFloor), 2);
EXPECT_EQ(Timecode::time_to_timestamp(0.08, tb, Timecode::kCeil), 2);
}
TEST(CoreTimecode, TimestampToTime)
{
rational tb(1, 25);
EXPECT_EQ(Timecode::timestamp_to_time(50, tb), rational(2, 1));
}
TEST(CoreTimecode, RescaleTimestamp)
{
rational src(1, 25);
rational dst(1, 30);
EXPECT_EQ(Timecode::rescale_timestamp(50, src, dst), 60);
EXPECT_EQ(Timecode::rescale_timestamp(50, src, src), 50);
}
TEST(CoreTimecode, RescaleTimestampCeil)
{
rational src(1, 25);
rational dst(1, 30);
EXPECT_EQ(Timecode::rescale_timestamp_ceil(1, src, dst), 2);
}
TEST(CoreTimecode, TimebaseIsDropFrame)
{
EXPECT_FALSE(Timecode::timebase_is_drop_frame(rational(1, 25)));
EXPECT_TRUE(Timecode::timebase_is_drop_frame(rational(1001, 30000)));
}
+184
View File
@@ -0,0 +1,184 @@
#include <gtest/gtest.h>
#include "olive/core/util/timerange.h"
using namespace olive::core;
TEST(CoreTimeRange, ConstructAndAccess)
{
TimeRange r(rational(1), rational(5));
EXPECT_EQ(r.in(), rational(1));
EXPECT_EQ(r.out(), rational(5));
EXPECT_EQ(r.length(), rational(4));
}
TEST(CoreTimeRange, NormalizationSwapsReversedBounds)
{
TimeRange r(rational(5), rational(1));
EXPECT_EQ(r.in(), rational(1));
EXPECT_EQ(r.out(), rational(5));
}
TEST(CoreTimeRange, SettersNormalize)
{
TimeRange r(rational(0), rational(10));
r.set_in(rational(15));
EXPECT_EQ(r.in(), rational(10));
EXPECT_EQ(r.out(), rational(15));
r.set_out(rational(2));
EXPECT_EQ(r.in(), rational(2));
EXPECT_EQ(r.out(), rational(10));
}
TEST(CoreTimeRange, ContainsRational)
{
TimeRange r(rational(0), rational(10));
EXPECT_TRUE(r.Contains(rational(5)));
EXPECT_FALSE(r.Contains(rational(10)));
EXPECT_FALSE(r.Contains(rational(-1)));
}
TEST(CoreTimeRange, ContainsRange)
{
TimeRange outer(rational(0), rational(10));
TimeRange inner(rational(2), rational(8));
TimeRange partial(rational(5), rational(15));
EXPECT_TRUE(outer.Contains(inner));
EXPECT_FALSE(outer.Contains(partial));
}
TEST(CoreTimeRange, OverlapsWith)
{
TimeRange a(rational(0), rational(10));
TimeRange b(rational(5), rational(15));
TimeRange c(rational(10), rational(20));
EXPECT_TRUE(a.OverlapsWith(b));
// By default bounds are inclusive, so [0,10] and [10,20] touch and overlap
EXPECT_TRUE(a.OverlapsWith(c));
EXPECT_FALSE(a.OverlapsWith(c, false, false));
}
TEST(CoreTimeRange, CombineAndIntersect)
{
TimeRange a(rational(0), rational(10));
TimeRange b(rational(5), rational(15));
TimeRange combined = a.Combined(b);
EXPECT_EQ(combined.in(), rational(0));
EXPECT_EQ(combined.out(), rational(15));
TimeRange intersect = a.Intersected(b);
EXPECT_EQ(intersect.in(), rational(5));
EXPECT_EQ(intersect.out(), rational(10));
}
TEST(CoreTimeRange, Arithmetic)
{
TimeRange r(rational(0), rational(10));
TimeRange shifted = r + rational(5);
EXPECT_EQ(shifted.in(), rational(5));
EXPECT_EQ(shifted.out(), rational(15));
shifted -= rational(3);
EXPECT_EQ(shifted.in(), rational(2));
EXPECT_EQ(shifted.out(), rational(12));
}
TEST(CoreTimeRange, Split)
{
TimeRange r(rational(0), rational(10));
auto pieces = r.Split(3);
ASSERT_EQ(pieces.size(), 4u);
EXPECT_EQ(pieces.front().in(), rational(0));
}
TEST(CoreTimeRangeList, InsertMergesOverlapping)
{
TimeRangeList list;
list.insert(TimeRange(rational(0), rational(5)));
list.insert(TimeRange(rational(3), rational(8)));
list.insert(TimeRange(rational(10), rational(12)));
EXPECT_EQ(list.size(), 2);
EXPECT_EQ(list.first().in(), rational(0));
EXPECT_EQ(list.first().out(), rational(8));
}
TEST(CoreTimeRangeList, RemoveSplitsRange)
{
TimeRangeList list;
list.insert(TimeRange(rational(0), rational(10)));
list.remove(TimeRange(rational(3), rational(7)));
EXPECT_EQ(list.size(), 2);
EXPECT_EQ(list.first().out(), rational(3));
EXPECT_EQ(list.last().in(), rational(7));
}
TEST(CoreTimeRangeList, Shift)
{
TimeRangeList list;
list.insert(TimeRange(rational(0), rational(5)));
list.shift(rational(10));
EXPECT_EQ(list.first().in(), rational(10));
EXPECT_EQ(list.first().out(), rational(15));
}
TEST(CoreTimeRangeList, TrimInAndOut)
{
TimeRangeList list;
list.insert(TimeRange(rational(10), rational(20)));
list.trim_in(rational(5));
EXPECT_EQ(list.first().in(), rational(15));
EXPECT_EQ(list.first().out(), rational(20));
list.trim_out(rational(-5));
// set_out(out + diff) = 20 + (-5) = 15
EXPECT_EQ(list.first().out(), rational(15));
}
TEST(CoreTimeRangeList, Intersects)
{
TimeRangeList list;
list.insert(TimeRange(rational(0), rational(10)));
list.insert(TimeRange(rational(20), rational(30)));
TimeRangeList result = list.Intersects(TimeRange(rational(5), rational(25)));
EXPECT_EQ(result.size(), 2);
EXPECT_EQ(result.first().in(), rational(5));
EXPECT_EQ(result.first().out(), rational(10));
}
TEST(CoreTimeRangeListFrameIterator, IteratesFrames)
{
TimeRangeList list;
// 5 seconds at 25fps = 125 frames
list.insert(TimeRange(rational(0), rational(5)));
TimeRangeListFrameIterator it(list, rational(1, 25));
rational out;
int count = 0;
while (it.GetNext(&out)) {
count++;
}
EXPECT_EQ(count, 125);
EXPECT_EQ(it.size(), 125);
}
TEST(CoreTimeRangeListFrameIterator, HasNext)
{
TimeRangeList list;
list.insert(TimeRange(rational(0), rational(1)));
TimeRangeListFrameIterator it(list, rational(1, 25));
EXPECT_TRUE(it.HasNext());
rational out;
while (it.GetNext(&out)) {
}
EXPECT_FALSE(it.HasNext());
}