style: reformat C/C++ sources and headers with clang-format

Run clang-format over all project C/C++ source and header files
(.c, .cpp, .cc, .h, .hpp, .hh, .m, .mm) under app/ and tests/.
Non-source files (svg, qm, qrc, etc.) were not touched.
This commit is contained in:
2026-07-13 15:34:11 +08:00
parent 6a696bc234
commit f03b6b58ff
61 changed files with 2873 additions and 2740 deletions
+2 -1
View File
@@ -11,7 +11,8 @@ extern "C" {
#include <libavutil/channel_layout.h> #include <libavutil/channel_layout.h>
} }
namespace { namespace
{
olive::core::AudioParams MakeStereoParams() olive::core::AudioParams MakeStereoParams()
{ {
File diff suppressed because it is too large Load Diff
+6 -6
View File
@@ -13,8 +13,8 @@ TEST(AudioSynchronizer, PlacesCandidateBySourceStartTime)
candidate.has_source_start_time = true; candidate.has_source_start_time = true;
const olive::AudioSynchronizer::Placement placement = const olive::AudioSynchronizer::Placement placement =
olive::AudioSynchronizer::PlaceBySourceTime( olive::AudioSynchronizer::PlaceBySourceTime(reference, candidate,
reference, candidate, olive::core::rational(10)); olive::core::rational(10));
ASSERT_TRUE(placement.valid); ASSERT_TRUE(placement.valid);
EXPECT_EQ(placement.timeline_in, olive::core::rational(22)); EXPECT_EQ(placement.timeline_in, olive::core::rational(22));
@@ -33,8 +33,8 @@ TEST(AudioSynchronizer, AccountsForMediaInWhenPlacingBySourceTime)
candidate.has_source_start_time = true; candidate.has_source_start_time = true;
const olive::AudioSynchronizer::Placement placement = const olive::AudioSynchronizer::Placement placement =
olive::AudioSynchronizer::PlaceBySourceTime( olive::AudioSynchronizer::PlaceBySourceTime(reference, candidate,
reference, candidate, olive::core::rational(20)); olive::core::rational(20));
ASSERT_TRUE(placement.valid); ASSERT_TRUE(placement.valid);
EXPECT_EQ(placement.timeline_in, olive::core::rational(23)); EXPECT_EQ(placement.timeline_in, olive::core::rational(23));
@@ -49,8 +49,8 @@ TEST(AudioSynchronizer, RejectsMissingSourceStartTime)
olive::AudioSynchronizer::SourceClip candidate; olive::AudioSynchronizer::SourceClip candidate;
const olive::AudioSynchronizer::Placement placement = const olive::AudioSynchronizer::Placement placement =
olive::AudioSynchronizer::PlaceBySourceTime( olive::AudioSynchronizer::PlaceBySourceTime(reference, candidate,
reference, candidate, olive::core::rational(10)); olive::core::rational(10));
EXPECT_FALSE(placement.valid); EXPECT_FALSE(placement.valid);
} }
+12 -15
View File
@@ -9,7 +9,8 @@ extern "C" {
#include <libavutil/channel_layout.h> #include <libavutil/channel_layout.h>
} }
namespace { namespace
{
olive::core::AudioParams MakeMonoParams() olive::core::AudioParams MakeMonoParams()
{ {
@@ -46,13 +47,11 @@ TEST(AudioWaveformSync, ExtractsRmsEnvelope)
TEST(AudioWaveformSync, EstimatesCandidateLag) TEST(AudioWaveformSync, EstimatesCandidateLag)
{ {
const QVector<float> reference_values = { const QVector<float> reference_values = { 0.0f, 0.0f, 0.8f, 0.8f, 0.1f,
0.0f, 0.0f, 0.8f, 0.8f, 0.1f, 0.1f, 0.6f, 0.6f, 0.0f, 0.0f 0.1f, 0.6f, 0.6f, 0.0f, 0.0f };
}; const QVector<float> candidate_values = { 0.0f, 0.0f, 0.0f, 0.0f,
const QVector<float> candidate_values = { 0.8f, 0.8f, 0.1f, 0.1f,
0.0f, 0.0f, 0.0f, 0.0f, 0.8f, 0.8f, 0.1f, 0.6f, 0.6f, 0.0f, 0.0f };
0.1f, 0.6f, 0.6f, 0.0f, 0.0f
};
const olive::AudioWaveformSync::OffsetResult result = const olive::AudioWaveformSync::OffsetResult result =
olive::AudioWaveformSync::EstimateOffset( olive::AudioWaveformSync::EstimateOffset(
@@ -65,13 +64,11 @@ TEST(AudioWaveformSync, EstimatesCandidateLag)
TEST(AudioWaveformSync, EstimatesCandidateLead) TEST(AudioWaveformSync, EstimatesCandidateLead)
{ {
const QVector<float> reference_values = { const QVector<float> reference_values = { 0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, 0.9f, 0.9f, 0.3f, 0.9f, 0.9f, 0.3f, 0.3f,
0.3f, 0.7f, 0.7f, 0.0f, 0.0f 0.7f, 0.7f, 0.0f, 0.0f };
}; const QVector<float> candidate_values = { 0.9f, 0.9f, 0.3f, 0.3f,
const QVector<float> candidate_values = { 0.7f, 0.7f, 0.0f, 0.0f };
0.9f, 0.9f, 0.3f, 0.3f, 0.7f, 0.7f, 0.0f, 0.0f
};
const olive::AudioWaveformSync::OffsetResult result = const olive::AudioWaveformSync::OffsetResult result =
olive::AudioWaveformSync::EstimateOffset( olive::AudioWaveformSync::EstimateOffset(
+2 -1
View File
@@ -11,7 +11,8 @@ TEST(CodecDecoder, RetrieveVideoFrameFromDemoMp4)
.filePath(QStringLiteral("tests/demo.mp4")); .filePath(QStringLiteral("tests/demo.mp4"));
ASSERT_TRUE(QFileInfo::exists(path)); ASSERT_TRUE(QFileInfo::exists(path));
olive::DecoderPtr decoder = olive::Decoder::CreateFromID(QStringLiteral("ffmpeg")); olive::DecoderPtr decoder =
olive::Decoder::CreateFromID(QStringLiteral("ffmpeg"));
ASSERT_TRUE(decoder); ASSERT_TRUE(decoder);
ASSERT_TRUE(decoder->Open(olive::Decoder::CodecStream(path, 0, nullptr))); ASSERT_TRUE(decoder->Open(olive::Decoder::CodecStream(path, 0, nullptr)));
+10 -12
View File
@@ -2,7 +2,8 @@
#include "codec/encoder.h" #include "codec/encoder.h"
namespace { namespace
{
class TestEncoder final : public olive::Encoder { class TestEncoder final : public olive::Encoder {
public: public:
explicit TestEncoder(const olive::EncodingParams &params) explicit TestEncoder(const olive::EncodingParams &params)
@@ -57,8 +58,8 @@ TEST(CodecEncoder, ImageSequenceFilenames)
QStringLiteral("frame_[####].png")), QStringLiteral("frame_[####].png")),
QStringLiteral("frame.png")); QStringLiteral("frame.png"));
const QString filename = encoder.GetFilenameForFrame( const QString filename =
olive::core::rational(1, 24)); encoder.GetFilenameForFrame(olive::core::rational(1, 24));
EXPECT_EQ(filename, QStringLiteral("frame_0001.png")); EXPECT_EQ(filename, QStringLiteral("frame_0001.png"));
} }
@@ -66,21 +67,18 @@ TEST(CodecEncoder, MatrixGeneration)
{ {
using Method = olive::EncodingParams::VideoScalingMethod; using Method = olive::EncodingParams::VideoScalingMethod;
QMatrix4x4 stretch = QMatrix4x4 stretch = olive::EncodingParams::GenerateMatrix(
olive::EncodingParams::GenerateMatrix(Method::kStretch, 1920, 1080, Method::kStretch, 1920, 1080, 1280, 720);
1280, 720);
EXPECT_TRUE(qFuzzyCompare(stretch(0, 0), 1.0f)); EXPECT_TRUE(qFuzzyCompare(stretch(0, 0), 1.0f));
EXPECT_TRUE(qFuzzyCompare(stretch(1, 1), 1.0f)); EXPECT_TRUE(qFuzzyCompare(stretch(1, 1), 1.0f));
QMatrix4x4 fit = QMatrix4x4 fit = olive::EncodingParams::GenerateMatrix(Method::kFit, 1920,
olive::EncodingParams::GenerateMatrix(Method::kFit, 1920, 1080, 1080, 1024, 1024);
1024, 1024);
EXPECT_TRUE(qFuzzyCompare(fit(0, 0), 1.0f)); EXPECT_TRUE(qFuzzyCompare(fit(0, 0), 1.0f));
EXPECT_FALSE(qFuzzyCompare(fit(1, 1), 1.0f)); EXPECT_FALSE(qFuzzyCompare(fit(1, 1), 1.0f));
QMatrix4x4 crop = QMatrix4x4 crop = olive::EncodingParams::GenerateMatrix(Method::kCrop, 1920,
olive::EncodingParams::GenerateMatrix(Method::kCrop, 1920, 1080, 1080, 1024, 1024);
1024, 1024);
EXPECT_FALSE(qFuzzyCompare(crop(0, 0), 1.0f)); EXPECT_FALSE(qFuzzyCompare(crop(0, 0), 1.0f));
EXPECT_TRUE(qFuzzyCompare(crop(1, 1), 1.0f)); EXPECT_TRUE(qFuzzyCompare(crop(1, 1), 1.0f));
} }
+2 -1
View File
@@ -12,7 +12,8 @@ TEST(CodecExportFormat, NamesAndExtensions)
QStringLiteral("mxf")); QStringLiteral("mxf"));
EXPECT_EQ(ExportFormat::GetName(ExportFormat::kFormatCount), EXPECT_EQ(ExportFormat::GetName(ExportFormat::kFormatCount),
QStringLiteral("Unknown")); QStringLiteral("Unknown"));
EXPECT_TRUE(ExportFormat::GetExtension(ExportFormat::kFormatCount).isEmpty()); EXPECT_TRUE(
ExportFormat::GetExtension(ExportFormat::kFormatCount).isEmpty());
} }
TEST(CodecExportFormat, AllFormatsHaveNames) TEST(CodecExportFormat, AllFormatsHaveNames)
+10 -10
View File
@@ -44,9 +44,9 @@ TEST(CodecFrame, AllocateMatchesLineSize)
TEST(CodecFrame, DestroyDeallocatesData) TEST(CodecFrame, DestroyDeallocatesData)
{ {
olive::FramePtr frame = olive::Frame::Create(); olive::FramePtr frame = olive::Frame::Create();
frame->set_video_params(olive::VideoParams( frame->set_video_params(
8, 8, olive::core::PixelFormat::U8, olive::VideoParams(8, 8, olive::core::PixelFormat::U8,
olive::VideoParams::kRGBAChannelCount)); olive::VideoParams::kRGBAChannelCount));
frame->allocate(); frame->allocate();
EXPECT_TRUE(frame->is_allocated()); EXPECT_TRUE(frame->is_allocated());
@@ -55,7 +55,6 @@ TEST(CodecFrame, DestroyDeallocatesData)
EXPECT_EQ(frame->data(), nullptr); EXPECT_EQ(frame->data(), nullptr);
} }
TEST(CodecFrame, AllocateInvalidParamsFails) TEST(CodecFrame, AllocateInvalidParamsFails)
{ {
olive::Frame frame; olive::Frame frame;
@@ -157,13 +156,15 @@ TEST(CodecFrame, InterlaceFrames)
TEST(CodecFrame, InterlaceIncompatibleReturnsNull) TEST(CodecFrame, InterlaceIncompatibleReturnsNull)
{ {
olive::FramePtr top = olive::Frame::Create(); olive::FramePtr top = olive::Frame::Create();
top->set_video_params(olive::VideoParams( top->set_video_params(
4, 4, olive::core::PixelFormat::U8, olive::VideoParams::kRGBAChannelCount)); olive::VideoParams(4, 4, olive::core::PixelFormat::U8,
olive::VideoParams::kRGBAChannelCount));
top->allocate(); top->allocate();
olive::FramePtr bottom = olive::Frame::Create(); olive::FramePtr bottom = olive::Frame::Create();
bottom->set_video_params(olive::VideoParams( bottom->set_video_params(
8, 8, olive::core::PixelFormat::U8, olive::VideoParams::kRGBAChannelCount)); olive::VideoParams(8, 8, olive::core::PixelFormat::U8,
olive::VideoParams::kRGBAChannelCount));
bottom->allocate(); bottom->allocate();
EXPECT_EQ(olive::Frame::Interlace(top, bottom), nullptr); EXPECT_EQ(olive::Frame::Interlace(top, bottom), nullptr);
@@ -177,8 +178,7 @@ TEST(CodecFrame, ConvertU8ToU16)
frame->set_video_params(params); frame->set_video_params(params);
frame->allocate(); frame->allocate();
olive::FramePtr converted = olive::FramePtr converted = frame->convert(olive::core::PixelFormat::U16);
frame->convert(olive::core::PixelFormat::U16);
ASSERT_NE(converted, nullptr); ASSERT_NE(converted, nullptr);
EXPECT_EQ(converted->format(), olive::core::PixelFormat::U16); EXPECT_EQ(converted->format(), olive::core::PixelFormat::U16);
EXPECT_EQ(converted->width(), 4); EXPECT_EQ(converted->width(), 4);
+58 -68
View File
@@ -18,7 +18,8 @@
namespace OCIO = OCIO_NAMESPACE; namespace OCIO = OCIO_NAMESPACE;
namespace { namespace
{
bool IsOakSupportedLutExtension(QString suffix) bool IsOakSupportedLutExtension(QString suffix)
{ {
@@ -31,19 +32,19 @@ bool IsOakSupportedLutExtension(QString suffix)
QString WriteTestCube(QTemporaryDir *dir) QString WriteTestCube(QTemporaryDir *dir)
{ {
const QString path = QDir(dir->path()).filePath(QStringLiteral("invert.cube")); const QString path =
QDir(dir->path()).filePath(QStringLiteral("invert.cube"));
QFile file(path); QFile file(path);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return QString(); return QString();
} }
const QByteArray data = const QByteArray data = "TITLE \"Oak test invert\"\n"
"TITLE \"Oak test invert\"\n" "LUT_1D_SIZE 2\n"
"LUT_1D_SIZE 2\n" "DOMAIN_MIN 0.0 0.0 0.0\n"
"DOMAIN_MIN 0.0 0.0 0.0\n" "DOMAIN_MAX 1.0 1.0 1.0\n"
"DOMAIN_MAX 1.0 1.0 1.0\n" "1.0 1.0 1.0\n"
"1.0 1.0 1.0\n" "0.0 0.0 0.0\n";
"0.0 0.0 0.0\n";
file.write(data); file.write(data);
file.close(); file.close();
return path; return path;
@@ -87,9 +88,10 @@ protected:
} }
} }
virtual void ProcessColorTransform(olive::TexturePtr destination, virtual void
const olive::Node *node, ProcessColorTransform(olive::TexturePtr destination,
const olive::ColorTransformJob *job) override const olive::Node *node,
const olive::ColorTransformJob *job) override
{ {
Q_UNUSED(destination) Q_UNUSED(destination)
Q_UNUSED(node) Q_UNUSED(node)
@@ -110,11 +112,12 @@ protected:
} }
}; };
QString WriteTestCubeLut(QTemporaryDir *dir, const char *title, QString WriteTestCubeLut(QTemporaryDir *dir, const char *title, float low,
float low, float high) float high)
{ {
const QString path = QDir(dir->path()).filePath( const QString path =
QStringLiteral("%1.cube").arg(QString::fromUtf8(title))); QDir(dir->path())
.filePath(QStringLiteral("%1.cube").arg(QString::fromUtf8(title)));
QFile file(path); QFile file(path);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
return QString(); return QString();
@@ -147,14 +150,13 @@ QString WriteAsymmetricCube(QTemporaryDir *dir)
return QString(); return QString();
} }
const QByteArray data = const QByteArray data = "TITLE \"Oak test asymmetric\"\n"
"TITLE \"Oak test asymmetric\"\n" "LUT_1D_SIZE 3\n"
"LUT_1D_SIZE 3\n" "DOMAIN_MIN 0.0 0.0 0.0\n"
"DOMAIN_MIN 0.0 0.0 0.0\n" "DOMAIN_MAX 1.0 1.0 1.0\n"
"DOMAIN_MAX 1.0 1.0 1.0\n" "0.00 0.00 0.00\n"
"0.00 0.00 0.00\n" "0.75 0.75 0.75\n"
"0.75 0.75 0.75\n" "1.00 1.00 1.00\n";
"1.00 1.00 1.00\n";
file.write(data); file.write(data);
file.close(); file.close();
return path; return path;
@@ -231,7 +233,8 @@ TEST(ColorLut, CubeFileTransformConvertsColor)
olive::ColorProcessor::Create(config->getProcessor(transform)); olive::ColorProcessor::Create(config->getProcessor(transform));
ASSERT_TRUE(processor); ASSERT_TRUE(processor);
const olive::Color out = processor->ConvertColor(olive::Color(0.25f, 0.50f, 0.75f, 1.0f)); const olive::Color out =
processor->ConvertColor(olive::Color(0.25f, 0.50f, 0.75f, 1.0f));
EXPECT_NEAR(out.red(), 0.75f, 0.02f); EXPECT_NEAR(out.red(), 0.75f, 0.02f);
EXPECT_NEAR(out.green(), 0.50f, 0.02f); EXPECT_NEAR(out.green(), 0.50f, 0.02f);
EXPECT_NEAR(out.blue(), 0.25f, 0.02f); EXPECT_NEAR(out.blue(), 0.25f, 0.02f);
@@ -240,9 +243,8 @@ TEST(ColorLut, CubeFileTransformConvertsColor)
TEST(ColorV04, FactoryCreatesColorNodes) TEST(ColorV04, FactoryCreatesColorNodes)
{ {
std::unique_ptr<olive::Node> lut( std::unique_ptr<olive::Node> lut(olive::NodeFactory::CreateFromFactoryIndex(
olive::NodeFactory::CreateFromFactoryIndex( olive::NodeFactory::kOCIOLut));
olive::NodeFactory::kOCIOLut));
ASSERT_NE(lut, nullptr); ASSERT_NE(lut, nullptr);
EXPECT_EQ(lut->id(), QStringLiteral("org.olivevideoeditor.Olive.ociolut")); EXPECT_EQ(lut->id(), QStringLiteral("org.olivevideoeditor.Olive.ociolut"));
@@ -260,8 +262,8 @@ TEST(ColorV04, FactoryCreatesColorNodes)
olive::ThreeWayColorNode::kHighlightsColorInput)); olive::ThreeWayColorNode::kHighlightsColorInput));
const olive::Color neutral = const olive::Color neutral =
three_way->GetStandardValue( three_way
olive::ThreeWayColorNode::kMidtonesColorInput) ->GetStandardValue(olive::ThreeWayColorNode::kMidtonesColorInput)
.value<olive::Color>(); .value<olive::Color>();
EXPECT_FLOAT_EQ(neutral.red(), 0.5f); EXPECT_FLOAT_EQ(neutral.red(), 0.5f);
EXPECT_FLOAT_EQ(neutral.green(), 0.5f); EXPECT_FLOAT_EQ(neutral.green(), 0.5f);
@@ -306,9 +308,8 @@ TEST(ColorLutNode, ForwardDirectionInvertsPixels)
olive::Node::ConnectEdge( olive::Node::ConnectEdge(
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput)); solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
const olive::VideoParams params( const olive::VideoParams params(16, 16, olive::core::PixelFormat::F32,
16, 16, olive::core::PixelFormat::F32, olive::VideoParams::kRGBAChannelCount);
olive::VideoParams::kRGBAChannelCount);
PixelColorTransformTraverser traverser; PixelColorTransformTraverser traverser;
traverser.SetCacheVideoParams(params); traverser.SetCacheVideoParams(params);
@@ -354,9 +355,8 @@ TEST(ColorLutNode, InverseDirectionReversesForwardTransform)
olive::Node::ConnectEdge( olive::Node::ConnectEdge(
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput)); solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
const olive::VideoParams params( const olive::VideoParams params(16, 16, olive::core::PixelFormat::F32,
16, 16, olive::core::PixelFormat::F32, olive::VideoParams::kRGBAChannelCount);
olive::VideoParams::kRGBAChannelCount);
PixelColorTransformTraverser traverser; PixelColorTransformTraverser traverser;
traverser.SetCacheVideoParams(params); traverser.SetCacheVideoParams(params);
@@ -402,9 +402,8 @@ TEST(ColorLutNode, SwitchingDirectionUpdatesProcessorAndPixels)
olive::Node::ConnectEdge( olive::Node::ConnectEdge(
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput)); solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
const olive::VideoParams params( const olive::VideoParams params(16, 16, olive::core::PixelFormat::F32,
16, 16, olive::core::PixelFormat::F32, olive::VideoParams::kRGBAChannelCount);
olive::VideoParams::kRGBAChannelCount);
// First render: forward direction. // First render: forward direction.
PixelColorTransformTraverser forward_traverser; PixelColorTransformTraverser forward_traverser;
@@ -471,9 +470,8 @@ TEST(ColorLutNode, EmptyFilePathLeavesProcessorNull)
olive::Node::ConnectEdge( olive::Node::ConnectEdge(
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput)); solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
const olive::VideoParams params( const olive::VideoParams params(16, 16, olive::core::PixelFormat::F32,
16, 16, olive::core::PixelFormat::F32, olive::VideoParams::kRGBAChannelCount);
olive::VideoParams::kRGBAChannelCount);
PixelColorTransformTraverser traverser; PixelColorTransformTraverser traverser;
traverser.SetCacheVideoParams(params); traverser.SetCacheVideoParams(params);
@@ -503,14 +501,13 @@ TEST(ColorLutNode, MissingFilePathLeavesProcessorNull)
auto *lut = new olive::OCIOLutNode(); auto *lut = new olive::OCIOLutNode();
lut->setParent(&project); lut->setParent(&project);
lut->SetStandardValue(olive::OCIOLutNode::kFileInput, lut->SetStandardValue(olive::OCIOLutNode::kFileInput,
QStringLiteral("/nonexistent/path/lut.cube")); QStringLiteral("/nonexistent/path/lut.cube"));
olive::Node::ConnectEdge( olive::Node::ConnectEdge(
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput)); solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
const olive::VideoParams params( const olive::VideoParams params(16, 16, olive::core::PixelFormat::F32,
16, 16, olive::core::PixelFormat::F32, olive::VideoParams::kRGBAChannelCount);
olive::VideoParams::kRGBAChannelCount);
PixelColorTransformTraverser traverser; PixelColorTransformTraverser traverser;
traverser.SetCacheVideoParams(params); traverser.SetCacheVideoParams(params);
@@ -538,14 +535,13 @@ TEST(ColorLutNode, UnsupportedExtensionLeavesProcessorNull)
auto *lut = new olive::OCIOLutNode(); auto *lut = new olive::OCIOLutNode();
lut->setParent(&project); lut->setParent(&project);
lut->SetStandardValue(olive::OCIOLutNode::kFileInput, lut->SetStandardValue(olive::OCIOLutNode::kFileInput,
QStringLiteral("/tmp/lut.txt")); QStringLiteral("/tmp/lut.txt"));
olive::Node::ConnectEdge( olive::Node::ConnectEdge(
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput)); solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
const olive::VideoParams params( const olive::VideoParams params(16, 16, olive::core::PixelFormat::F32,
16, 16, olive::core::PixelFormat::F32, olive::VideoParams::kRGBAChannelCount);
olive::VideoParams::kRGBAChannelCount);
PixelColorTransformTraverser traverser; PixelColorTransformTraverser traverser;
traverser.SetCacheVideoParams(params); traverser.SetCacheVideoParams(params);
@@ -579,14 +575,13 @@ TEST(ColorLutNode, DirectionStringValuesAreAccepted)
lut->setParent(&project); lut->setParent(&project);
lut->SetStandardValue(olive::OCIOLutNode::kFileInput, path); lut->SetStandardValue(olive::OCIOLutNode::kFileInput, path);
lut->SetStandardValue(olive::OCIOLutNode::kDirectionInput, lut->SetStandardValue(olive::OCIOLutNode::kDirectionInput,
QStringLiteral("forward")); QStringLiteral("forward"));
olive::Node::ConnectEdge( olive::Node::ConnectEdge(
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput)); solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
const olive::VideoParams params( const olive::VideoParams params(16, 16, olive::core::PixelFormat::F32,
16, 16, olive::core::PixelFormat::F32, olive::VideoParams::kRGBAChannelCount);
olive::VideoParams::kRGBAChannelCount);
PixelColorTransformTraverser traverser; PixelColorTransformTraverser traverser;
traverser.SetCacheVideoParams(params); traverser.SetCacheVideoParams(params);
@@ -626,14 +621,13 @@ TEST(ColorLutNode, DirectionStringInverseIsAccepted)
lut->setParent(&project); lut->setParent(&project);
lut->SetStandardValue(olive::OCIOLutNode::kFileInput, path); lut->SetStandardValue(olive::OCIOLutNode::kFileInput, path);
lut->SetStandardValue(olive::OCIOLutNode::kDirectionInput, lut->SetStandardValue(olive::OCIOLutNode::kDirectionInput,
QStringLiteral("inverse")); QStringLiteral("inverse"));
olive::Node::ConnectEdge( olive::Node::ConnectEdge(
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput)); solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
const olive::VideoParams params( const olive::VideoParams params(16, 16, olive::core::PixelFormat::F32,
16, 16, olive::core::PixelFormat::F32, olive::VideoParams::kRGBAChannelCount);
olive::VideoParams::kRGBAChannelCount);
PixelColorTransformTraverser traverser; PixelColorTransformTraverser traverser;
traverser.SetCacheVideoParams(params); traverser.SetCacheVideoParams(params);
@@ -676,9 +670,8 @@ TEST(ColorLutNode, ReusingSameFileDoesNotCrash)
olive::Node::ConnectEdge( olive::Node::ConnectEdge(
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput)); solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
const olive::VideoParams params( const olive::VideoParams params(16, 16, olive::core::PixelFormat::F32,
16, 16, olive::core::PixelFormat::F32, olive::VideoParams::kRGBAChannelCount);
olive::VideoParams::kRGBAChannelCount);
// Render twice; the second render should reuse the cached processor. // Render twice; the second render should reuse the cached processor.
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
@@ -708,10 +701,8 @@ TEST(ColorLutNode, SwitchingBackToOriginalFileRestoresOriginalPixels)
QTemporaryDir dir; QTemporaryDir dir;
ASSERT_TRUE(dir.isValid()); ASSERT_TRUE(dir.isValid());
const QString invert_path = WriteTestCubeLut( const QString invert_path = WriteTestCubeLut(&dir, "invert", 0.0f, 1.0f);
&dir, "invert", 0.0f, 1.0f); const QString boost_path = WriteTestCubeLut(&dir, "boost", 0.5f, 1.0f);
const QString boost_path = WriteTestCubeLut(
&dir, "boost", 0.5f, 1.0f);
ASSERT_FALSE(invert_path.isEmpty()); ASSERT_FALSE(invert_path.isEmpty());
ASSERT_FALSE(boost_path.isEmpty()); ASSERT_FALSE(boost_path.isEmpty());
@@ -728,9 +719,8 @@ TEST(ColorLutNode, SwitchingBackToOriginalFileRestoresOriginalPixels)
olive::Node::ConnectEdge( olive::Node::ConnectEdge(
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput)); solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
const olive::VideoParams params( const olive::VideoParams params(16, 16, olive::core::PixelFormat::F32,
16, 16, olive::core::PixelFormat::F32, olive::VideoParams::kRGBAChannelCount);
olive::VideoParams::kRGBAChannelCount);
auto render = [&]() { auto render = [&]() {
PixelColorTransformTraverser traverser; PixelColorTransformTraverser traverser;
+41 -41
View File
@@ -4,80 +4,80 @@
TEST(CommonCommandLineParser, OptionWithoutArgument) TEST(CommonCommandLineParser, OptionWithoutArgument)
{ {
CommandLineParser parser; CommandLineParser parser;
const CommandLineParser::Option *opt = parser.AddOption( const CommandLineParser::Option *opt =
{QStringLiteral("help"), QStringLiteral("h")}, parser.AddOption({ QStringLiteral("help"), QStringLiteral("h") },
QStringLiteral("Show help"), false); QStringLiteral("Show help"), false);
parser.Process({QStringLiteral("app"), QStringLiteral("-help")}); parser.Process({ QStringLiteral("app"), QStringLiteral("-help") });
EXPECT_TRUE(opt->IsSet()); EXPECT_TRUE(opt->IsSet());
} }
TEST(CommonCommandLineParser, ShortOption) TEST(CommonCommandLineParser, ShortOption)
{ {
CommandLineParser parser; CommandLineParser parser;
const CommandLineParser::Option *opt = parser.AddOption( const CommandLineParser::Option *opt =
{QStringLiteral("help"), QStringLiteral("h")}, parser.AddOption({ QStringLiteral("help"), QStringLiteral("h") },
QStringLiteral("Show help"), false); QStringLiteral("Show help"), false);
parser.Process({QStringLiteral("app"), QStringLiteral("-h")}); parser.Process({ QStringLiteral("app"), QStringLiteral("-h") });
EXPECT_TRUE(opt->IsSet()); EXPECT_TRUE(opt->IsSet());
} }
TEST(CommonCommandLineParser, OptionWithArgument) TEST(CommonCommandLineParser, OptionWithArgument)
{ {
CommandLineParser parser; CommandLineParser parser;
const CommandLineParser::Option *opt = parser.AddOption( const CommandLineParser::Option *opt = parser.AddOption(
{QStringLiteral("project")}, QStringLiteral("Project file"), true, { QStringLiteral("project") }, QStringLiteral("Project file"), true,
QStringLiteral("file")); QStringLiteral("file"));
parser.Process({QStringLiteral("app"), QStringLiteral("-project"), parser.Process({ QStringLiteral("app"), QStringLiteral("-project"),
QStringLiteral("test.ove")}); QStringLiteral("test.ove") });
EXPECT_TRUE(opt->IsSet()); EXPECT_TRUE(opt->IsSet());
EXPECT_EQ(opt->GetSetting(), QStringLiteral("test.ove")); EXPECT_EQ(opt->GetSetting(), QStringLiteral("test.ove"));
} }
TEST(CommonCommandLineParser, PositionalArgument) TEST(CommonCommandLineParser, PositionalArgument)
{ {
CommandLineParser parser; CommandLineParser parser;
const CommandLineParser::PositionalArgument *arg = const CommandLineParser::PositionalArgument *arg =
parser.AddPositionalArgument(QStringLiteral("filename"), parser.AddPositionalArgument(QStringLiteral("filename"),
QStringLiteral("Project file"), true); QStringLiteral("Project file"), true);
parser.Process({QStringLiteral("app"), QStringLiteral("test.ove")}); parser.Process({ QStringLiteral("app"), QStringLiteral("test.ove") });
EXPECT_EQ(arg->GetSetting(), QStringLiteral("test.ove")); EXPECT_EQ(arg->GetSetting(), QStringLiteral("test.ove"));
} }
TEST(CommonCommandLineParser, UnknownOptionWarning) TEST(CommonCommandLineParser, UnknownOptionWarning)
{ {
CommandLineParser parser; CommandLineParser parser;
parser.AddOption({QStringLiteral("known")}, QStringLiteral("Known")); parser.AddOption({ QStringLiteral("known") }, QStringLiteral("Known"));
// Should not crash; unknown option is logged // Should not crash; unknown option is logged
parser.Process({QStringLiteral("app"), QStringLiteral("-unknown")}); parser.Process({ QStringLiteral("app"), QStringLiteral("-unknown") });
} }
TEST(CommonCommandLineParser, UnknownPositionalWarning) TEST(CommonCommandLineParser, UnknownPositionalWarning)
{ {
CommandLineParser parser; CommandLineParser parser;
// Should not crash; unknown positional is logged // Should not crash; unknown positional is logged
parser.Process({QStringLiteral("app"), QStringLiteral("extra")}); parser.Process({ QStringLiteral("app"), QStringLiteral("extra") });
} }
TEST(CommonCommandLineParser, HiddenOptionExcludedFromHelp) TEST(CommonCommandLineParser, HiddenOptionExcludedFromHelp)
{ {
CommandLineParser parser; CommandLineParser parser;
parser.AddOption({QStringLiteral("visible")}, QStringLiteral("Visible")); parser.AddOption({ QStringLiteral("visible") }, QStringLiteral("Visible"));
parser.AddOption({QStringLiteral("hidden")}, QStringLiteral("Hidden"), parser.AddOption({ QStringLiteral("hidden") }, QStringLiteral("Hidden"),
false, QString(), true); false, QString(), true);
parser.AddPositionalArgument(QStringLiteral("file"), parser.AddPositionalArgument(QStringLiteral("file"),
QStringLiteral("Input file")); QStringLiteral("Input file"));
// Should not crash; hidden option should be skipped during help output // Should not crash; hidden option should be skipped during help output
parser.PrintHelp("/usr/bin/app"); parser.PrintHelp("/usr/bin/app");
} }
+4 -2
View File
@@ -11,7 +11,8 @@ TEST(CommonCurrent, SetAndGetVideoParams)
params.set_height(1080); params.set_height(1080);
Current::getInstance().setCurrentVideoParams(params); Current::getInstance().setCurrentVideoParams(params);
const olive::VideoParams &stored = Current::getInstance().currentVideoParams(); const olive::VideoParams &stored =
Current::getInstance().currentVideoParams();
EXPECT_EQ(stored.width(), 1920); EXPECT_EQ(stored.width(), 1920);
EXPECT_EQ(stored.height(), 1080); EXPECT_EQ(stored.height(), 1080);
} }
@@ -22,6 +23,7 @@ TEST(CommonCurrent, SetAndGetAudioParams)
params.set_sample_rate(48000); params.set_sample_rate(48000);
Current::getInstance().setCurrentAudioParams(params); Current::getInstance().setCurrentAudioParams(params);
const olive::AudioParams &stored = Current::getInstance().currentAudioParams(); const olive::AudioParams &stored =
Current::getInstance().currentAudioParams();
EXPECT_EQ(stored.sample_rate(), 48000); EXPECT_EQ(stored.sample_rate(), 48000);
} }
+7 -7
View File
@@ -4,13 +4,13 @@
TEST(CommonDebug, DebugHandlerFormatsAllLevels) TEST(CommonDebug, DebugHandlerFormatsAllLevels)
{ {
// Install handler and restore after test // Install handler and restore after test
QtMessageHandler old = qInstallMessageHandler(olive::DebugHandler); QtMessageHandler old = qInstallMessageHandler(olive::DebugHandler);
qDebug() << "debug message"; qDebug() << "debug message";
qInfo() << "info message"; qInfo() << "info message";
qWarning() << "warning message"; qWarning() << "warning message";
qCritical() << "critical message"; qCritical() << "critical message";
qInstallMessageHandler(old); qInstallMessageHandler(old);
} }
+13 -13
View File
@@ -4,49 +4,49 @@
TEST(CommonDecibel, FromLinearZeroReturnsMinimum) TEST(CommonDecibel, FromLinearZeroReturnsMinimum)
{ {
EXPECT_DOUBLE_EQ(olive::Decibel::fromLinear(0.0), olive::Decibel::MINIMUM); EXPECT_DOUBLE_EQ(olive::Decibel::fromLinear(0.0), olive::Decibel::MINIMUM);
} }
TEST(CommonDecibel, FromLinearOneReturnsZero) TEST(CommonDecibel, FromLinearOneReturnsZero)
{ {
EXPECT_DOUBLE_EQ(olive::Decibel::fromLinear(1.0), 0.0); EXPECT_DOUBLE_EQ(olive::Decibel::fromLinear(1.0), 0.0);
} }
TEST(CommonDecibel, FromLinearTenReturnsTwenty) TEST(CommonDecibel, FromLinearTenReturnsTwenty)
{ {
EXPECT_DOUBLE_EQ(olive::Decibel::fromLinear(10.0), 20.0); EXPECT_DOUBLE_EQ(olive::Decibel::fromLinear(10.0), 20.0);
} }
TEST(CommonDecibel, ToLinearZeroReturnsOne) TEST(CommonDecibel, ToLinearZeroReturnsOne)
{ {
EXPECT_DOUBLE_EQ(olive::Decibel::toLinear(0.0), 1.0); EXPECT_DOUBLE_EQ(olive::Decibel::toLinear(0.0), 1.0);
} }
TEST(CommonDecibel, ToLinearMinimumReturnsZero) TEST(CommonDecibel, ToLinearMinimumReturnsZero)
{ {
EXPECT_DOUBLE_EQ(olive::Decibel::toLinear(olive::Decibel::MINIMUM), 0.0); EXPECT_DOUBLE_EQ(olive::Decibel::toLinear(olive::Decibel::MINIMUM), 0.0);
} }
TEST(CommonDecibel, ToLinearTwentyReturnsTen) TEST(CommonDecibel, ToLinearTwentyReturnsTen)
{ {
EXPECT_DOUBLE_EQ(olive::Decibel::toLinear(20.0), 10.0); EXPECT_DOUBLE_EQ(olive::Decibel::toLinear(20.0), 10.0);
} }
TEST(CommonDecibel, FromLogarithmicAtEdges) TEST(CommonDecibel, FromLogarithmicAtEdges)
{ {
EXPECT_DOUBLE_EQ(olive::Decibel::fromLogarithmic(0.0), EXPECT_DOUBLE_EQ(olive::Decibel::fromLogarithmic(0.0),
olive::Decibel::MINIMUM); olive::Decibel::MINIMUM);
EXPECT_DOUBLE_EQ(olive::Decibel::fromLogarithmic(1.0), 0.0); EXPECT_DOUBLE_EQ(olive::Decibel::fromLogarithmic(1.0), 0.0);
} }
TEST(CommonDecibel, ToLogarithmicAtEdges) TEST(CommonDecibel, ToLogarithmicAtEdges)
{ {
EXPECT_DOUBLE_EQ(olive::Decibel::toLogarithmic(0.0), 1.0); EXPECT_DOUBLE_EQ(olive::Decibel::toLogarithmic(0.0), 1.0);
} }
TEST(CommonDecibel, LinearLogarithmicRoundTrip) TEST(CommonDecibel, LinearLogarithmicRoundTrip)
{ {
EXPECT_NEAR(olive::Decibel::LogarithmicToLinear( EXPECT_NEAR(olive::Decibel::LogarithmicToLinear(
olive::Decibel::LinearToLogarithmic(0.5)), olive::Decibel::LinearToLogarithmic(0.5)),
0.5, 1e-6); 0.5, 1e-6);
} }
+8 -8
View File
@@ -4,20 +4,20 @@
TEST(CommonDigit, SingleDigit) TEST(CommonDigit, SingleDigit)
{ {
EXPECT_EQ(olive::GetDigitCount(0), 1); EXPECT_EQ(olive::GetDigitCount(0), 1);
EXPECT_EQ(olive::GetDigitCount(5), 1); EXPECT_EQ(olive::GetDigitCount(5), 1);
EXPECT_EQ(olive::GetDigitCount(-5), 1); EXPECT_EQ(olive::GetDigitCount(-5), 1);
} }
TEST(CommonDigit, MultipleDigits) TEST(CommonDigit, MultipleDigits)
{ {
EXPECT_EQ(olive::GetDigitCount(10), 2); EXPECT_EQ(olive::GetDigitCount(10), 2);
EXPECT_EQ(olive::GetDigitCount(999), 3); EXPECT_EQ(olive::GetDigitCount(999), 3);
EXPECT_EQ(olive::GetDigitCount(1000), 4); EXPECT_EQ(olive::GetDigitCount(1000), 4);
EXPECT_EQ(olive::GetDigitCount(-12345), 5); EXPECT_EQ(olive::GetDigitCount(-12345), 5);
} }
TEST(CommonDigit, LargeValue) TEST(CommonDigit, LargeValue)
{ {
EXPECT_EQ(olive::GetDigitCount(123456789012345LL), 15); EXPECT_EQ(olive::GetDigitCount(123456789012345LL), 15);
} }
+120 -115
View File
@@ -6,144 +6,149 @@ using namespace olive;
TEST(CommonFFmpegUtils, GetNativeSampleFormatMapsCorrectly) TEST(CommonFFmpegUtils, GetNativeSampleFormatMapsCorrectly)
{ {
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_U8), EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_U8),
SampleFormat::U8); SampleFormat::U8);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S16), EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S16),
SampleFormat::S16); SampleFormat::S16);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S32), EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S32),
SampleFormat::S32); SampleFormat::S32);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S64), EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S64),
SampleFormat::S64); SampleFormat::S64);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_FLT), EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_FLT),
SampleFormat::F32); SampleFormat::F32);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_DBL), EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_DBL),
SampleFormat::F64); SampleFormat::F64);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_U8P), EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_U8P),
SampleFormat::U8P); SampleFormat::U8P);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S16P), EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S16P),
SampleFormat::S16P); SampleFormat::S16P);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S32P), EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S32P),
SampleFormat::S32P); SampleFormat::S32P);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S64P), EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_S64P),
SampleFormat::S64P); SampleFormat::S64P);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_FLTP), EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_FLTP),
SampleFormat::F32P); SampleFormat::F32P);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_DBLP), EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_DBLP),
SampleFormat::F64P); SampleFormat::F64P);
EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_NONE), EXPECT_EQ(FFmpegUtils::GetNativeSampleFormat(AV_SAMPLE_FMT_NONE),
SampleFormat::INVALID); SampleFormat::INVALID);
} }
TEST(CommonFFmpegUtils, GetFFmpegSampleFormatMapsCorrectly) TEST(CommonFFmpegUtils, GetFFmpegSampleFormatMapsCorrectly)
{ {
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::U8), EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::U8),
AV_SAMPLE_FMT_U8); AV_SAMPLE_FMT_U8);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S16), EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S16),
AV_SAMPLE_FMT_S16); AV_SAMPLE_FMT_S16);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S32), EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S32),
AV_SAMPLE_FMT_S32); AV_SAMPLE_FMT_S32);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S64), EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S64),
AV_SAMPLE_FMT_S64); AV_SAMPLE_FMT_S64);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::F32), EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::F32),
AV_SAMPLE_FMT_FLT); AV_SAMPLE_FMT_FLT);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::F64), EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::F64),
AV_SAMPLE_FMT_DBL); AV_SAMPLE_FMT_DBL);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::U8P), EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::U8P),
AV_SAMPLE_FMT_U8P); AV_SAMPLE_FMT_U8P);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S16P), EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S16P),
AV_SAMPLE_FMT_S16P); AV_SAMPLE_FMT_S16P);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S32P), EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S32P),
AV_SAMPLE_FMT_S32P); AV_SAMPLE_FMT_S32P);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S64P), EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::S64P),
AV_SAMPLE_FMT_S64P); AV_SAMPLE_FMT_S64P);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::F32P), EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::F32P),
AV_SAMPLE_FMT_FLTP); AV_SAMPLE_FMT_FLTP);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::F64P), EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::F64P),
AV_SAMPLE_FMT_DBLP); AV_SAMPLE_FMT_DBLP);
EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::INVALID), EXPECT_EQ(FFmpegUtils::GetFFmpegSampleFormat(SampleFormat::INVALID),
AV_SAMPLE_FMT_NONE); AV_SAMPLE_FMT_NONE);
} }
TEST(CommonFFmpegUtils, GetSwsColorspaceFromAVColorSpace) TEST(CommonFFmpegUtils, GetSwsColorspaceFromAVColorSpace)
{ {
EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_BT709), EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_BT709),
SWS_CS_ITU709); SWS_CS_ITU709);
EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_FCC), EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_FCC),
SWS_CS_FCC); SWS_CS_FCC);
EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_BT470BG), EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_BT470BG),
SWS_CS_ITU624); SWS_CS_ITU624);
EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_SMPTE170M), EXPECT_EQ(
SWS_CS_SMPTE170M); FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_SMPTE170M),
EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_SMPTE240M), SWS_CS_SMPTE170M);
SWS_CS_SMPTE240M); EXPECT_EQ(
EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_BT2020_NCL), FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_SMPTE240M),
SWS_CS_BT2020); SWS_CS_SMPTE240M);
EXPECT_EQ(FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_UNSPECIFIED), EXPECT_EQ(
SWS_CS_DEFAULT); FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_BT2020_NCL),
SWS_CS_BT2020);
EXPECT_EQ(
FFmpegUtils::GetSwsColorspaceFromAVColorSpace(AVCOL_SPC_UNSPECIFIED),
SWS_CS_DEFAULT);
} }
TEST(CommonFFmpegUtils, ConvertJPEGSpaceToRegularSpace) TEST(CommonFFmpegUtils, ConvertJPEGSpaceToRegularSpace)
{ {
EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUVJ420P), EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUVJ420P),
AV_PIX_FMT_YUV420P); AV_PIX_FMT_YUV420P);
EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUVJ422P), EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUVJ422P),
AV_PIX_FMT_YUV422P); AV_PIX_FMT_YUV422P);
EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUVJ444P), EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUVJ444P),
AV_PIX_FMT_YUV444P); AV_PIX_FMT_YUV444P);
EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUVJ440P), EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUVJ440P),
AV_PIX_FMT_YUV440P); AV_PIX_FMT_YUV440P);
EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUVJ411P), EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUVJ411P),
AV_PIX_FMT_YUV411P); AV_PIX_FMT_YUV411P);
EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUV420P), EXPECT_EQ(FFmpegUtils::ConvertJPEGSpaceToRegularSpace(AV_PIX_FMT_YUV420P),
AV_PIX_FMT_YUV420P); AV_PIX_FMT_YUV420P);
} }
TEST(CommonFFmpegUtils, GetCompatiblePixelFormatNative) TEST(CommonFFmpegUtils, GetCompatiblePixelFormatNative)
{ {
EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::U8), EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::U8),
PixelFormat::U8); PixelFormat::U8);
EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::U10), EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::U10),
PixelFormat::U8); PixelFormat::U8);
EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::U16), EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::U16),
PixelFormat::U16); PixelFormat::U16);
EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::F16), EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::F16),
PixelFormat::U16); PixelFormat::U16);
EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::F32), EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::F32),
PixelFormat::U16); PixelFormat::U16);
EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::INVALID), EXPECT_EQ(FFmpegUtils::GetCompatiblePixelFormat(PixelFormat::INVALID),
PixelFormat::INVALID); PixelFormat::INVALID);
} }
TEST(CommonFFmpegUtils, GetFFmpegPixelFormat) TEST(CommonFFmpegUtils, GetFFmpegPixelFormat)
{ {
EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::U8, EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::U8,
VideoParams::kRGBChannelCount), VideoParams::kRGBChannelCount),
AV_PIX_FMT_RGB24); AV_PIX_FMT_RGB24);
EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::U16, EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::U16,
VideoParams::kRGBChannelCount), VideoParams::kRGBChannelCount),
AV_PIX_FMT_RGB48); AV_PIX_FMT_RGB48);
EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::F32, EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::F32,
VideoParams::kRGBChannelCount), VideoParams::kRGBChannelCount),
AV_PIX_FMT_RGBF32); AV_PIX_FMT_RGBF32);
EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::U8, EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::U8,
VideoParams::kRGBAChannelCount), VideoParams::kRGBAChannelCount),
AV_PIX_FMT_RGBA); AV_PIX_FMT_RGBA);
EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::U16, EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::U16,
VideoParams::kRGBAChannelCount), VideoParams::kRGBAChannelCount),
AV_PIX_FMT_RGBA64); AV_PIX_FMT_RGBA64);
EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::F32, EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::F32,
VideoParams::kRGBAChannelCount), VideoParams::kRGBAChannelCount),
AV_PIX_FMT_RGBAF32); AV_PIX_FMT_RGBAF32);
EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::INVALID, 0), EXPECT_EQ(FFmpegUtils::GetFFmpegPixelFormat(PixelFormat::INVALID, 0),
AV_PIX_FMT_NONE); AV_PIX_FMT_NONE);
} }
TEST(CommonFFmpegUtils, GetCompatiblePixelFormatAV) TEST(CommonFFmpegUtils, GetCompatiblePixelFormatAV)
{ {
AVPixelFormat fmt = FFmpegUtils::GetCompatiblePixelFormat(AV_PIX_FMT_YUV420P); AVPixelFormat fmt =
EXPECT_NE(fmt, AV_PIX_FMT_NONE); FFmpegUtils::GetCompatiblePixelFormat(AV_PIX_FMT_YUV420P);
EXPECT_NE(fmt, AV_PIX_FMT_NONE);
fmt = FFmpegUtils::GetCompatiblePixelFormat(AV_PIX_FMT_YUV420P, fmt = FFmpegUtils::GetCompatiblePixelFormat(AV_PIX_FMT_YUV420P,
PixelFormat::U8); PixelFormat::U8);
EXPECT_EQ(fmt, AV_PIX_FMT_RGBA); EXPECT_EQ(fmt, AV_PIX_FMT_RGBA);
} }
+12 -7
View File
@@ -16,7 +16,9 @@ TEST(CommonFileFunctions, EnsureFilenameExtension)
EXPECT_EQ(olive::FileFunctions::EnsureFilenameExtension( EXPECT_EQ(olive::FileFunctions::EnsureFilenameExtension(
QStringLiteral("PROJECT"), QStringLiteral("ove")), QStringLiteral("PROJECT"), QStringLiteral("ove")),
QStringLiteral("PROJECT.ove")); QStringLiteral("PROJECT.ove"));
EXPECT_TRUE(olive::FileFunctions::EnsureFilenameExtension(QString(), QStringLiteral("ove")).isEmpty()); EXPECT_TRUE(olive::FileFunctions::EnsureFilenameExtension(
QString(), QStringLiteral("ove"))
.isEmpty());
EXPECT_EQ(olive::FileFunctions::EnsureFilenameExtension( EXPECT_EQ(olive::FileFunctions::EnsureFilenameExtension(
QStringLiteral("project"), QString()), QStringLiteral("project"), QString()),
QStringLiteral("project")); QStringLiteral("project"));
@@ -46,7 +48,8 @@ TEST(CommonFileFunctions, DirectoryIsValid)
QTemporaryDir dir; QTemporaryDir dir;
ASSERT_TRUE(dir.isValid()); ASSERT_TRUE(dir.isValid());
EXPECT_TRUE(olive::FileFunctions::DirectoryIsValid(QDir(dir.path()), false)); EXPECT_TRUE(
olive::FileFunctions::DirectoryIsValid(QDir(dir.path()), false));
QDir nonexistent(dir.filePath(QStringLiteral("subdir/nested"))); QDir nonexistent(dir.filePath(QStringLiteral("subdir/nested")));
EXPECT_TRUE(olive::FileFunctions::DirectoryIsValid(nonexistent, true)); EXPECT_TRUE(olive::FileFunctions::DirectoryIsValid(nonexistent, true));
@@ -133,7 +136,8 @@ TEST(CommonFileFunctions, ReadFileAsString)
EXPECT_EQ(olive::FileFunctions::ReadFileAsString(f.fileName()), EXPECT_EQ(olive::FileFunctions::ReadFileAsString(f.fileName()),
QStringLiteral("hello world")); QStringLiteral("hello world"));
EXPECT_TRUE(olive::FileFunctions::ReadFileAsString( EXPECT_TRUE(olive::FileFunctions::ReadFileAsString(
QStringLiteral("/nonexistent/path")).isEmpty()); QStringLiteral("/nonexistent/path"))
.isEmpty());
} }
TEST(CommonFileFunctions, GetUniqueFileIdentifier) TEST(CommonFileFunctions, GetUniqueFileIdentifier)
@@ -148,10 +152,10 @@ TEST(CommonFileFunctions, GetUniqueFileIdentifier)
EXPECT_EQ(id1, id2); EXPECT_EQ(id1, id2);
EXPECT_TRUE(olive::FileFunctions::GetUniqueFileIdentifier( EXPECT_TRUE(olive::FileFunctions::GetUniqueFileIdentifier(
QStringLiteral("/nonexistent")).isEmpty()); QStringLiteral("/nonexistent"))
.isEmpty());
} }
TEST(CommonFileFunctions, GetConfigurationLocation) TEST(CommonFileFunctions, GetConfigurationLocation)
{ {
QString loc = olive::FileFunctions::GetConfigurationLocation(); QString loc = olive::FileFunctions::GetConfigurationLocation();
@@ -176,7 +180,8 @@ TEST(CommonFileFunctions, DirectoryIsValidExisting)
{ {
QTemporaryDir dir; QTemporaryDir dir;
ASSERT_TRUE(dir.isValid()); ASSERT_TRUE(dir.isValid());
EXPECT_TRUE(olive::FileFunctions::DirectoryIsValid(QDir(dir.path()), false)); EXPECT_TRUE(
olive::FileFunctions::DirectoryIsValid(QDir(dir.path()), false));
} }
TEST(CommonFileFunctions, CopyDirectoryWithOverwrite) TEST(CommonFileFunctions, CopyDirectoryWithOverwrite)
@@ -212,5 +217,5 @@ TEST(CommonFileFunctions, CopyDirectorySourceMissing)
// Should not crash even if source doesn't exist // Should not crash even if source doesn't exist
olive::FileFunctions::CopyDirectory(QStringLiteral("/nonexistent/path"), olive::FileFunctions::CopyDirectory(QStringLiteral("/nonexistent/path"),
dst.path(), false); dst.path(), false);
} }
+20 -20
View File
@@ -4,39 +4,39 @@
TEST(CommonJobTime, ConstructorAcquiresValue) TEST(CommonJobTime, ConstructorAcquiresValue)
{ {
olive::JobTime a; olive::JobTime a;
olive::JobTime b; olive::JobTime b;
EXPECT_NE(a.value(), b.value()); EXPECT_NE(a.value(), b.value());
EXPECT_LT(a.value(), b.value()); EXPECT_LT(a.value(), b.value());
} }
TEST(CommonJobTime, AcquireUpdatesValue) TEST(CommonJobTime, AcquireUpdatesValue)
{ {
olive::JobTime a; olive::JobTime a;
uint64_t first = a.value(); uint64_t first = a.value();
a.Acquire(); a.Acquire();
uint64_t second = a.value(); uint64_t second = a.value();
EXPECT_GT(second, first); EXPECT_GT(second, first);
} }
TEST(CommonJobTime, ComparisonOperators) TEST(CommonJobTime, ComparisonOperators)
{ {
olive::JobTime a; olive::JobTime a;
olive::JobTime b; olive::JobTime b;
EXPECT_LT(a, b); EXPECT_LT(a, b);
EXPECT_GT(b, a); EXPECT_GT(b, a);
EXPECT_LE(a, a); EXPECT_LE(a, a);
EXPECT_GE(b, b); EXPECT_GE(b, b);
EXPECT_EQ(a, a); EXPECT_EQ(a, a);
EXPECT_NE(a, b); EXPECT_NE(a, b);
} }
TEST(CommonJobTime, DebugStream) TEST(CommonJobTime, DebugStream)
{ {
olive::JobTime a; olive::JobTime a;
QDebug debug(QtDebugMsg); QDebug debug(QtDebugMsg);
debug << a; debug << a;
} }
+5 -5
View File
@@ -30,7 +30,8 @@ TEST(CommonQtUtils, FlipControlAndShiftModifiers)
// (Qt::ControlModifier & Qt::ShiftModifier is always zero), so the function // (Qt::ControlModifier & Qt::ShiftModifier is always zero), so the function
// always swaps Control and Shift. This test documents current behavior. // always swaps Control and Shift. This test documents current behavior.
Qt::KeyboardModifiers both = Qt::ControlModifier | Qt::ShiftModifier; Qt::KeyboardModifiers both = Qt::ControlModifier | Qt::ShiftModifier;
Qt::KeyboardModifiers flipped = olive::QtUtils::FlipControlAndShiftModifiers(both); Qt::KeyboardModifiers flipped =
olive::QtUtils::FlipControlAndShiftModifiers(both);
EXPECT_TRUE(flipped & Qt::ControlModifier); EXPECT_TRUE(flipped & Qt::ControlModifier);
EXPECT_FALSE(flipped & Qt::ShiftModifier); EXPECT_FALSE(flipped & Qt::ShiftModifier);
@@ -112,11 +113,10 @@ TEST(CommonQtUtils, ToQColor)
EXPECT_NEAR(qc.alphaF(), 0.4, 0.001); EXPECT_NEAR(qc.alphaF(), 0.4, 0.001);
} }
TEST(CommonQtUtils, GetFormattedDateTime) TEST(CommonQtUtils, GetFormattedDateTime)
{ {
QDateTime dt = QDateTime::fromString(QStringLiteral("2025-01-15T10:30:00"), QDateTime dt = QDateTime::fromString(QStringLiteral("2025-01-15T10:30:00"),
Qt::ISODate); Qt::ISODate);
QString s = olive::QtUtils::GetFormattedDateTime(dt); QString s = olive::QtUtils::GetFormattedDateTime(dt);
EXPECT_FALSE(s.isEmpty()); EXPECT_FALSE(s.isEmpty());
} }
@@ -132,8 +132,8 @@ TEST(CommonQtUtils, WordWrapString)
EXPECT_GE(wrapped.size(), 1u); EXPECT_GE(wrapped.size(), 1u);
// Should preserve manual newlines // Should preserve manual newlines
wrapped = olive::QtUtils::WordWrapString( wrapped = olive::QtUtils::WordWrapString(QStringLiteral("line1\nline2"), fm,
QStringLiteral("line1\nline2"), fm, 1000); 1000);
EXPECT_EQ(wrapped.size(), 2); EXPECT_EQ(wrapped.size(), 2);
} }
-1
View File
@@ -59,7 +59,6 @@ TEST(CommonXmlUtils, ReadNextStartElementSkipsUnknown)
EXPECT_EQ(reader.name().toString(), QStringLiteral("known")); EXPECT_EQ(reader.name().toString(), QStringLiteral("known"));
} }
TEST(CommonXmlUtils, ReadNextStartElementWithCancel) TEST(CommonXmlUtils, ReadNextStartElementWithCancel)
{ {
QByteArray xml = "<root><child/></root>"; QByteArray xml = "<root><child/></root>";
+12 -12
View File
@@ -46,18 +46,18 @@ TEST(Config, GraphicsBackendStringConversion)
olive::RenderManager::kVulkan); olive::RenderManager::kVulkan);
EXPECT_EQ(olive::RenderManager::BackendFromString(QStringLiteral("dummy")), EXPECT_EQ(olive::RenderManager::BackendFromString(QStringLiteral("dummy")),
olive::RenderManager::kDummy); olive::RenderManager::kDummy);
EXPECT_EQ(olive::RenderManager::BackendFromString( EXPECT_EQ(
QStringLiteral("multiprocess")), olive::RenderManager::BackendFromString(QStringLiteral("multiprocess")),
olive::RenderManager::kMultiProcess); olive::RenderManager::kMultiProcess);
EXPECT_EQ(olive::RenderManager::BackendToString( EXPECT_EQ(
olive::RenderManager::kOpenGL), olive::RenderManager::BackendToString(olive::RenderManager::kOpenGL),
QStringLiteral("opengl")); QStringLiteral("opengl"));
EXPECT_EQ(olive::RenderManager::BackendToString( EXPECT_EQ(
olive::RenderManager::kVulkan), olive::RenderManager::BackendToString(olive::RenderManager::kVulkan),
QStringLiteral("vulkan")); QStringLiteral("vulkan"));
EXPECT_EQ(olive::RenderManager::BackendToString( EXPECT_EQ(
olive::RenderManager::kDummy), olive::RenderManager::BackendToString(olive::RenderManager::kDummy),
QStringLiteral("dummy")); QStringLiteral("dummy"));
EXPECT_EQ(olive::RenderManager::BackendToString( EXPECT_EQ(olive::RenderManager::BackendToString(
olive::RenderManager::kMultiProcess), olive::RenderManager::kMultiProcess),
QStringLiteral("multiprocess")); QStringLiteral("multiprocess"));
+64 -64
View File
@@ -6,115 +6,115 @@ using namespace olive::core;
TEST(CoreBezier, DefaultConstruction) TEST(CoreBezier, DefaultConstruction)
{ {
Bezier b; Bezier b;
EXPECT_DOUBLE_EQ(b.x(), 0.0); EXPECT_DOUBLE_EQ(b.x(), 0.0);
EXPECT_DOUBLE_EQ(b.y(), 0.0); EXPECT_DOUBLE_EQ(b.y(), 0.0);
EXPECT_DOUBLE_EQ(b.cp1_x(), 0.0); EXPECT_DOUBLE_EQ(b.cp1_x(), 0.0);
EXPECT_DOUBLE_EQ(b.cp1_y(), 0.0); EXPECT_DOUBLE_EQ(b.cp1_y(), 0.0);
EXPECT_DOUBLE_EQ(b.cp2_x(), 0.0); EXPECT_DOUBLE_EQ(b.cp2_x(), 0.0);
EXPECT_DOUBLE_EQ(b.cp2_y(), 0.0); EXPECT_DOUBLE_EQ(b.cp2_y(), 0.0);
} }
TEST(CoreBezier, ValueConstruction) TEST(CoreBezier, ValueConstruction)
{ {
Bezier b(1.0, 2.0); Bezier b(1.0, 2.0);
EXPECT_DOUBLE_EQ(b.x(), 1.0); EXPECT_DOUBLE_EQ(b.x(), 1.0);
EXPECT_DOUBLE_EQ(b.y(), 2.0); EXPECT_DOUBLE_EQ(b.y(), 2.0);
} }
TEST(CoreBezier, FullConstruction) TEST(CoreBezier, FullConstruction)
{ {
Bezier b(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); 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.x(), 1.0);
EXPECT_DOUBLE_EQ(b.y(), 2.0); EXPECT_DOUBLE_EQ(b.y(), 2.0);
EXPECT_DOUBLE_EQ(b.cp1_x(), 3.0); EXPECT_DOUBLE_EQ(b.cp1_x(), 3.0);
EXPECT_DOUBLE_EQ(b.cp1_y(), 4.0); EXPECT_DOUBLE_EQ(b.cp1_y(), 4.0);
EXPECT_DOUBLE_EQ(b.cp2_x(), 5.0); EXPECT_DOUBLE_EQ(b.cp2_x(), 5.0);
EXPECT_DOUBLE_EQ(b.cp2_y(), 6.0); EXPECT_DOUBLE_EQ(b.cp2_y(), 6.0);
} }
TEST(CoreBezier, Setters) TEST(CoreBezier, Setters)
{ {
Bezier b; Bezier b;
b.set_x(10.0); b.set_x(10.0);
b.set_y(20.0); b.set_y(20.0);
b.set_cp1_x(30.0); b.set_cp1_x(30.0);
b.set_cp1_y(40.0); b.set_cp1_y(40.0);
b.set_cp2_x(50.0); b.set_cp2_x(50.0);
b.set_cp2_y(60.0); b.set_cp2_y(60.0);
EXPECT_DOUBLE_EQ(b.x(), 10.0); EXPECT_DOUBLE_EQ(b.x(), 10.0);
EXPECT_DOUBLE_EQ(b.y(), 20.0); EXPECT_DOUBLE_EQ(b.y(), 20.0);
EXPECT_DOUBLE_EQ(b.cp1_x(), 30.0); EXPECT_DOUBLE_EQ(b.cp1_x(), 30.0);
EXPECT_DOUBLE_EQ(b.cp1_y(), 40.0); EXPECT_DOUBLE_EQ(b.cp1_y(), 40.0);
EXPECT_DOUBLE_EQ(b.cp2_x(), 50.0); EXPECT_DOUBLE_EQ(b.cp2_x(), 50.0);
EXPECT_DOUBLE_EQ(b.cp2_y(), 60.0); EXPECT_DOUBLE_EQ(b.cp2_y(), 60.0);
} }
TEST(CoreBezier, QuadraticXtoT) TEST(CoreBezier, QuadraticXtoT)
{ {
double t = Bezier::QuadraticXtoT(0.5, 0.0, 0.5, 1.0); double t = Bezier::QuadraticXtoT(0.5, 0.0, 0.5, 1.0);
EXPECT_NEAR(t, 0.5, 0.00001); EXPECT_NEAR(t, 0.5, 0.00001);
t = Bezier::QuadraticXtoT(0.0, 0.0, 0.5, 1.0); t = Bezier::QuadraticXtoT(0.0, 0.0, 0.5, 1.0);
EXPECT_NEAR(t, 0.0, 0.00001); EXPECT_NEAR(t, 0.0, 0.00001);
t = Bezier::QuadraticXtoT(1.0, 0.0, 0.5, 1.0); t = Bezier::QuadraticXtoT(1.0, 0.0, 0.5, 1.0);
EXPECT_NEAR(t, 1.0, 0.00001); EXPECT_NEAR(t, 1.0, 0.00001);
} }
TEST(CoreBezier, QuadraticTtoY) 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.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, 0.5), 0.5, 0.00001);
EXPECT_NEAR(Bezier::QuadraticTtoY(0.0, 0.5, 1.0, 1.0), 1.0, 0.00001); EXPECT_NEAR(Bezier::QuadraticTtoY(0.0, 0.5, 1.0, 1.0), 1.0, 0.00001);
} }
TEST(CoreBezier, QuadraticXtoY) TEST(CoreBezier, QuadraticXtoY)
{ {
Imath::V2d a(0.0, 0.0); Imath::V2d a(0.0, 0.0);
Imath::V2d b(0.5, 0.5); Imath::V2d b(0.5, 0.5);
Imath::V2d c(1.0, 1.0); Imath::V2d c(1.0, 1.0);
EXPECT_NEAR(Bezier::QuadraticXtoY(0.5, a, b, c), 0.5, 0.00001); EXPECT_NEAR(Bezier::QuadraticXtoY(0.5, a, b, c), 0.5, 0.00001);
} }
TEST(CoreBezier, CubicXtoT) TEST(CoreBezier, CubicXtoT)
{ {
double t = Bezier::CubicXtoT(0.5, 0.0, 0.33, 0.66, 1.0); double t = Bezier::CubicXtoT(0.5, 0.0, 0.33, 0.66, 1.0);
EXPECT_NEAR(t, 0.5, 0.01); EXPECT_NEAR(t, 0.5, 0.01);
} }
TEST(CoreBezier, CubicTtoY) 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, 0.0), 0.0, 0.00001);
EXPECT_NEAR(Bezier::CubicTtoY(0.0, 0.33, 0.66, 1.0, 1.0), 1.0, 0.00001); EXPECT_NEAR(Bezier::CubicTtoY(0.0, 0.33, 0.66, 1.0, 1.0), 1.0, 0.00001);
} }
TEST(CoreBezier, CubicXtoY) TEST(CoreBezier, CubicXtoY)
{ {
Imath::V2d a(0.0, 0.0); Imath::V2d a(0.0, 0.0);
Imath::V2d b(0.33, 0.0); Imath::V2d b(0.33, 0.0);
Imath::V2d c(0.66, 1.0); Imath::V2d c(0.66, 1.0);
Imath::V2d d(1.0, 1.0); Imath::V2d d(1.0, 1.0);
double y = Bezier::CubicXtoY(0.5, a, b, c, d); double y = Bezier::CubicXtoY(0.5, a, b, c, d);
EXPECT_GE(y, 0.0); EXPECT_GE(y, 0.0);
EXPECT_LE(y, 1.0); EXPECT_LE(y, 1.0);
} }
TEST(CoreBezier, VectorConverters) TEST(CoreBezier, VectorConverters)
{ {
Bezier b(1.0, 2.0, 3.0, 4.0, 5.0, 6.0); Bezier b(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
Imath::V2d v = b.to_vec(); Imath::V2d v = b.to_vec();
EXPECT_DOUBLE_EQ(v.x, 1.0); EXPECT_DOUBLE_EQ(v.x, 1.0);
EXPECT_DOUBLE_EQ(v.y, 2.0); EXPECT_DOUBLE_EQ(v.y, 2.0);
Imath::V2d cp1 = b.control_point_1_to_vec(); Imath::V2d cp1 = b.control_point_1_to_vec();
EXPECT_DOUBLE_EQ(cp1.x, 3.0); EXPECT_DOUBLE_EQ(cp1.x, 3.0);
EXPECT_DOUBLE_EQ(cp1.y, 4.0); EXPECT_DOUBLE_EQ(cp1.y, 4.0);
Imath::V2d cp2 = b.control_point_2_to_vec(); Imath::V2d cp2 = b.control_point_2_to_vec();
EXPECT_DOUBLE_EQ(cp2.x, 5.0); EXPECT_DOUBLE_EQ(cp2.x, 5.0);
EXPECT_DOUBLE_EQ(cp2.y, 6.0); EXPECT_DOUBLE_EQ(cp2.y, 6.0);
} }
+93 -93
View File
@@ -6,164 +6,164 @@ using namespace olive::core;
TEST(CoreColor, DefaultConstruction) TEST(CoreColor, DefaultConstruction)
{ {
Color c; Color c;
EXPECT_FLOAT_EQ(c.red(), 0.0f); EXPECT_FLOAT_EQ(c.red(), 0.0f);
EXPECT_FLOAT_EQ(c.green(), 0.0f); EXPECT_FLOAT_EQ(c.green(), 0.0f);
EXPECT_FLOAT_EQ(c.blue(), 0.0f); EXPECT_FLOAT_EQ(c.blue(), 0.0f);
EXPECT_FLOAT_EQ(c.alpha(), 0.0f); EXPECT_FLOAT_EQ(c.alpha(), 0.0f);
} }
TEST(CoreColor, ValueConstruction) TEST(CoreColor, ValueConstruction)
{ {
Color c(0.1f, 0.2f, 0.3f, 0.4f); Color c(0.1f, 0.2f, 0.3f, 0.4f);
EXPECT_FLOAT_EQ(c.red(), 0.1f); EXPECT_FLOAT_EQ(c.red(), 0.1f);
EXPECT_FLOAT_EQ(c.green(), 0.2f); EXPECT_FLOAT_EQ(c.green(), 0.2f);
EXPECT_FLOAT_EQ(c.blue(), 0.3f); EXPECT_FLOAT_EQ(c.blue(), 0.3f);
EXPECT_FLOAT_EQ(c.alpha(), 0.4f); EXPECT_FLOAT_EQ(c.alpha(), 0.4f);
} }
TEST(CoreColor, SettersAndDataAccess) TEST(CoreColor, SettersAndDataAccess)
{ {
Color c; Color c;
c.set_red(0.5f); c.set_red(0.5f);
c.set_green(0.6f); c.set_green(0.6f);
c.set_blue(0.7f); c.set_blue(0.7f);
c.set_alpha(0.8f); c.set_alpha(0.8f);
EXPECT_FLOAT_EQ(c.data()[0], 0.5f); EXPECT_FLOAT_EQ(c.data()[0], 0.5f);
EXPECT_FLOAT_EQ(c.data()[1], 0.6f); EXPECT_FLOAT_EQ(c.data()[1], 0.6f);
EXPECT_FLOAT_EQ(c.data()[2], 0.7f); EXPECT_FLOAT_EQ(c.data()[2], 0.7f);
EXPECT_FLOAT_EQ(c.data()[3], 0.8f); EXPECT_FLOAT_EQ(c.data()[3], 0.8f);
} }
TEST(CoreColor, FromHsvRed) TEST(CoreColor, FromHsvRed)
{ {
Color c = Color::fromHsv(0.0f, 1.0f, 1.0f); Color c = Color::fromHsv(0.0f, 1.0f, 1.0f);
EXPECT_NEAR(c.red(), 1.0f, 0.001f); EXPECT_NEAR(c.red(), 1.0f, 0.001f);
EXPECT_NEAR(c.green(), 0.0f, 0.001f); EXPECT_NEAR(c.green(), 0.0f, 0.001f);
EXPECT_NEAR(c.blue(), 0.0f, 0.001f); EXPECT_NEAR(c.blue(), 0.0f, 0.001f);
} }
TEST(CoreColor, FromHsvGreen) TEST(CoreColor, FromHsvGreen)
{ {
Color c = Color::fromHsv(120.0f, 1.0f, 1.0f); Color c = Color::fromHsv(120.0f, 1.0f, 1.0f);
EXPECT_NEAR(c.red(), 0.0f, 0.001f); EXPECT_NEAR(c.red(), 0.0f, 0.001f);
EXPECT_NEAR(c.green(), 1.0f, 0.001f); EXPECT_NEAR(c.green(), 1.0f, 0.001f);
EXPECT_NEAR(c.blue(), 0.0f, 0.001f); EXPECT_NEAR(c.blue(), 0.0f, 0.001f);
} }
TEST(CoreColor, FromHsvBlue) TEST(CoreColor, FromHsvBlue)
{ {
Color c = Color::fromHsv(240.0f, 1.0f, 1.0f); Color c = Color::fromHsv(240.0f, 1.0f, 1.0f);
EXPECT_NEAR(c.red(), 0.0f, 0.001f); EXPECT_NEAR(c.red(), 0.0f, 0.001f);
EXPECT_NEAR(c.green(), 0.0f, 0.001f); EXPECT_NEAR(c.green(), 0.0f, 0.001f);
EXPECT_NEAR(c.blue(), 1.0f, 0.001f); EXPECT_NEAR(c.blue(), 1.0f, 0.001f);
} }
TEST(CoreColor, HsvRoundTrip) TEST(CoreColor, HsvRoundTrip)
{ {
Color original(0.8f, 0.4f, 0.2f); Color original(0.8f, 0.4f, 0.2f);
float h, s, v; float h, s, v;
original.toHsv(&h, &s, &v); original.toHsv(&h, &s, &v);
EXPECT_NEAR(original.hsv_hue(), h, 0.001f); EXPECT_NEAR(original.hsv_hue(), h, 0.001f);
EXPECT_NEAR(original.hsv_saturation(), s, 0.001f); EXPECT_NEAR(original.hsv_saturation(), s, 0.001f);
EXPECT_NEAR(original.value(), v, 0.001f); EXPECT_NEAR(original.value(), v, 0.001f);
} }
TEST(CoreColor, HslRoundTrip) TEST(CoreColor, HslRoundTrip)
{ {
Color original(0.2f, 0.5f, 0.8f); Color original(0.2f, 0.5f, 0.8f);
float h, s, l; float h, s, l;
original.toHsl(&h, &s, &l); original.toHsl(&h, &s, &l);
EXPECT_NEAR(original.hsl_hue(), h, 0.001f); EXPECT_NEAR(original.hsl_hue(), h, 0.001f);
EXPECT_NEAR(original.hsl_saturation(), s, 0.001f); EXPECT_NEAR(original.hsl_saturation(), s, 0.001f);
EXPECT_NEAR(original.lightness(), l, 0.001f); EXPECT_NEAR(original.lightness(), l, 0.001f);
} }
TEST(CoreColor, ArithmeticOperators) TEST(CoreColor, ArithmeticOperators)
{ {
Color a(1.0f, 2.0f, 3.0f, 4.0f); Color a(1.0f, 2.0f, 3.0f, 4.0f);
Color b(0.5f, 0.5f, 0.5f, 0.5f); Color b(0.5f, 0.5f, 0.5f, 0.5f);
Color sum = a + b; Color sum = a + b;
EXPECT_FLOAT_EQ(sum.red(), 1.5f); EXPECT_FLOAT_EQ(sum.red(), 1.5f);
Color diff = a - b; Color diff = a - b;
EXPECT_FLOAT_EQ(diff.red(), 0.5f); EXPECT_FLOAT_EQ(diff.red(), 0.5f);
Color scaled = a * 2.0f; Color scaled = a * 2.0f;
EXPECT_FLOAT_EQ(scaled.red(), 2.0f); EXPECT_FLOAT_EQ(scaled.red(), 2.0f);
Color divided = a / 2.0f; Color divided = a / 2.0f;
EXPECT_FLOAT_EQ(divided.red(), 0.5f); EXPECT_FLOAT_EQ(divided.red(), 0.5f);
Color added_scalar = a + 1.0f; Color added_scalar = a + 1.0f;
EXPECT_FLOAT_EQ(added_scalar.red(), 2.0f); EXPECT_FLOAT_EQ(added_scalar.red(), 2.0f);
} }
TEST(CoreColor, CompoundAssignment) TEST(CoreColor, CompoundAssignment)
{ {
Color c(1.0f, 2.0f, 3.0f, 4.0f); Color c(1.0f, 2.0f, 3.0f, 4.0f);
c += Color(0.5f, 0.5f, 0.5f, 0.5f); c += Color(0.5f, 0.5f, 0.5f, 0.5f);
EXPECT_FLOAT_EQ(c.red(), 1.5f); EXPECT_FLOAT_EQ(c.red(), 1.5f);
c *= 2.0f; c *= 2.0f;
EXPECT_FLOAT_EQ(c.red(), 3.0f); EXPECT_FLOAT_EQ(c.red(), 3.0f);
} }
TEST(CoreColor, GetRoughLuminance) TEST(CoreColor, GetRoughLuminance)
{ {
Color white(1.0f, 1.0f, 1.0f); Color white(1.0f, 1.0f, 1.0f);
EXPECT_FLOAT_EQ(white.GetRoughLuminance(), 1.0f); EXPECT_FLOAT_EQ(white.GetRoughLuminance(), 1.0f);
Color black(0.0f, 0.0f, 0.0f); Color black(0.0f, 0.0f, 0.0f);
EXPECT_FLOAT_EQ(black.GetRoughLuminance(), 0.0f); EXPECT_FLOAT_EQ(black.GetRoughLuminance(), 0.0f);
} }
TEST(CoreColor, ToDataAndFromDataU8) TEST(CoreColor, ToDataAndFromDataU8)
{ {
Color c(1.0f, 0.5f, 0.0f, 1.0f); Color c(1.0f, 0.5f, 0.0f, 1.0f);
uint8_t data[4]; uint8_t data[4];
c.toData(reinterpret_cast<char *>(data), PixelFormat::U8, 4); c.toData(reinterpret_cast<char *>(data), PixelFormat::U8, 4);
EXPECT_EQ(data[0], 255u); EXPECT_EQ(data[0], 255u);
EXPECT_EQ(data[1], 127u); EXPECT_EQ(data[1], 127u);
EXPECT_EQ(data[2], 0u); EXPECT_EQ(data[2], 0u);
EXPECT_EQ(data[3], 255u); EXPECT_EQ(data[3], 255u);
Color restored = Color::fromData(reinterpret_cast<const char *>(data), Color restored = Color::fromData(reinterpret_cast<const char *>(data),
PixelFormat::U8, 4); PixelFormat::U8, 4);
EXPECT_NEAR(restored.red(), 1.0f, 0.01f); EXPECT_NEAR(restored.red(), 1.0f, 0.01f);
EXPECT_NEAR(restored.green(), 0.5f, 0.01f); EXPECT_NEAR(restored.green(), 0.5f, 0.01f);
} }
TEST(CoreColor, ToDataAndFromDataF32) TEST(CoreColor, ToDataAndFromDataF32)
{ {
Color c(0.25f, 0.5f, 0.75f, 1.0f); Color c(0.25f, 0.5f, 0.75f, 1.0f);
float data[4]; float data[4];
c.toData(reinterpret_cast<char *>(data), PixelFormat::F32, 4); c.toData(reinterpret_cast<char *>(data), PixelFormat::F32, 4);
EXPECT_FLOAT_EQ(data[0], 0.25f); EXPECT_FLOAT_EQ(data[0], 0.25f);
EXPECT_FLOAT_EQ(data[1], 0.5f); EXPECT_FLOAT_EQ(data[1], 0.5f);
EXPECT_FLOAT_EQ(data[2], 0.75f); EXPECT_FLOAT_EQ(data[2], 0.75f);
EXPECT_FLOAT_EQ(data[3], 1.0f); EXPECT_FLOAT_EQ(data[3], 1.0f);
Color restored = Color::fromData(reinterpret_cast<const char *>(data), Color restored = Color::fromData(reinterpret_cast<const char *>(data),
PixelFormat::F32, 4); PixelFormat::F32, 4);
EXPECT_FLOAT_EQ(restored.red(), 0.25f); EXPECT_FLOAT_EQ(restored.red(), 0.25f);
} }
TEST(CoreColor, ToDataAndFromDataU10) TEST(CoreColor, ToDataAndFromDataU10)
{ {
Color c(1.0f, 0.5f, 0.0f, 1.0f); Color c(1.0f, 0.5f, 0.0f, 1.0f);
uint32_t data; uint32_t data;
c.toData(reinterpret_cast<char *>(&data), PixelFormat::U10, 4); c.toData(reinterpret_cast<char *>(&data), PixelFormat::U10, 4);
Color restored = Color::fromData(reinterpret_cast<const char *>(&data), Color restored = Color::fromData(reinterpret_cast<const char *>(&data),
PixelFormat::U10, 4); PixelFormat::U10, 4);
EXPECT_NEAR(restored.red(), 1.0f, 0.001f); EXPECT_NEAR(restored.red(), 1.0f, 0.001f);
EXPECT_NEAR(restored.green(), 0.5f, 0.001f); EXPECT_NEAR(restored.green(), 0.5f, 0.001f);
EXPECT_NEAR(restored.blue(), 0.0f, 0.001f); EXPECT_NEAR(restored.blue(), 0.0f, 0.001f);
} }
+1 -1
View File
@@ -208,7 +208,7 @@ TEST(CoreSampleBuffer, Set)
{ {
AudioParams params = MakeParams(); AudioParams params = MakeParams();
SampleBuffer b(params, 4); SampleBuffer b(params, 4);
float data[2] = {0.3f, 0.4f}; float data[2] = { 0.3f, 0.4f };
b.set(0, data, 1, 2); b.set(0, data, 1, 2);
EXPECT_FLOAT_EQ(b.data(0)[1], 0.3f); EXPECT_FLOAT_EQ(b.data(0)[1], 0.3f);
EXPECT_FLOAT_EQ(b.data(0)[2], 0.4f); EXPECT_FLOAT_EQ(b.data(0)[2], 0.4f);
+31 -30
View File
@@ -8,58 +8,59 @@ using namespace olive::core;
TEST(CoreStringUtils, Split) TEST(CoreStringUtils, Split)
{ {
auto result = StringUtils::split("a,b,c", ','); auto result = StringUtils::split("a,b,c", ',');
ASSERT_EQ(result.size(), 3u); ASSERT_EQ(result.size(), 3u);
EXPECT_EQ(result[0], "a"); EXPECT_EQ(result[0], "a");
EXPECT_EQ(result[1], "b"); EXPECT_EQ(result[1], "b");
EXPECT_EQ(result[2], "c"); EXPECT_EQ(result[2], "c");
} }
TEST(CoreStringUtils, SplitRegex) TEST(CoreStringUtils, SplitRegex)
{ {
auto result = StringUtils::split_regex("one:two;three", std::regex("(:)|(;)| ")); auto result =
ASSERT_EQ(result.size(), 3u); StringUtils::split_regex("one:two;three", std::regex("(:)|(;)| "));
EXPECT_EQ(result[0], "one"); ASSERT_EQ(result.size(), 3u);
EXPECT_EQ(result[1], "two"); EXPECT_EQ(result[0], "one");
EXPECT_EQ(result[2], "three"); EXPECT_EQ(result[1], "two");
EXPECT_EQ(result[2], "three");
} }
TEST(CoreStringUtils, ToInt) TEST(CoreStringUtils, ToInt)
{ {
bool ok = false; bool ok = false;
EXPECT_EQ(StringUtils::to_int("42", &ok), 42); EXPECT_EQ(StringUtils::to_int("42", &ok), 42);
EXPECT_TRUE(ok); EXPECT_TRUE(ok);
EXPECT_EQ(StringUtils::to_int("-7", 10, &ok), -7); EXPECT_EQ(StringUtils::to_int("-7", 10, &ok), -7);
EXPECT_TRUE(ok); EXPECT_TRUE(ok);
EXPECT_EQ(StringUtils::to_int("ff", 16, &ok), 255); EXPECT_EQ(StringUtils::to_int("ff", 16, &ok), 255);
EXPECT_TRUE(ok); EXPECT_TRUE(ok);
EXPECT_EQ(StringUtils::to_int("abc", &ok), 0); EXPECT_EQ(StringUtils::to_int("abc", &ok), 0);
EXPECT_FALSE(ok); EXPECT_FALSE(ok);
} }
TEST(CoreStringUtils, ToStringLeftpad) TEST(CoreStringUtils, ToStringLeftpad)
{ {
EXPECT_EQ(StringUtils::to_string_leftpad(5, 3), "005"); EXPECT_EQ(StringUtils::to_string_leftpad(5, 3), "005");
EXPECT_EQ(StringUtils::to_string_leftpad(123, 2), "123"); EXPECT_EQ(StringUtils::to_string_leftpad(123, 2), "123");
EXPECT_EQ(StringUtils::to_string_leftpad(7, 4, '*'), "***7"); EXPECT_EQ(StringUtils::to_string_leftpad(7, 4, '*'), "***7");
} }
TEST(CoreStringUtils, Format) TEST(CoreStringUtils, Format)
{ {
EXPECT_EQ(StringUtils::format("Hello %s %d", "world", 42), EXPECT_EQ(StringUtils::format("Hello %s %d", "world", 42),
"Hello world 42"); "Hello world 42");
} }
TEST(CoreStringUtils, Trim) TEST(CoreStringUtils, Trim)
{ {
std::string s = " hello world "; std::string s = " hello world ";
StringUtils::trim(s); StringUtils::trim(s);
EXPECT_EQ(s, "hello world"); EXPECT_EQ(s, "hello world");
EXPECT_EQ(StringUtils::trimmed("\t\nvalue\t\n"), "value"); EXPECT_EQ(StringUtils::trimmed("\t\nvalue\t\n"), "value");
EXPECT_EQ(StringUtils::ltrimmed(" left"), "left"); EXPECT_EQ(StringUtils::ltrimmed(" left"), "left");
EXPECT_EQ(StringUtils::rtrimmed("right "), "right"); EXPECT_EQ(StringUtils::rtrimmed("right "), "right");
} }
+55 -54
View File
@@ -6,110 +6,111 @@ using namespace olive::core;
TEST(CoreTimecode, TimeToTimecodeSeconds) TEST(CoreTimecode, TimeToTimecodeSeconds)
{ {
rational time(5, 1); rational time(5, 1);
rational tb(1, 25); rational tb(1, 25);
std::string tc = Timecode::time_to_timecode(time, tb, Timecode::kTimecodeSeconds); std::string tc =
EXPECT_EQ(tc, "00:00:05.000"); Timecode::time_to_timecode(time, tb, Timecode::kTimecodeSeconds);
EXPECT_EQ(tc, "00:00:05.000");
} }
TEST(CoreTimecode, TimeToTimecodeNonDropFrame) TEST(CoreTimecode, TimeToTimecodeNonDropFrame)
{ {
rational time(2, 1); rational time(2, 1);
rational tb(1, 25); rational tb(1, 25);
std::string tc = std::string tc =
Timecode::time_to_timecode(time, tb, Timecode::kTimecodeNonDropFrame); Timecode::time_to_timecode(time, tb, Timecode::kTimecodeNonDropFrame);
EXPECT_EQ(tc, "00:00:02:00"); EXPECT_EQ(tc, "00:00:02:00");
} }
TEST(CoreTimecode, TimeToTimecodePlusSign) TEST(CoreTimecode, TimeToTimecodePlusSign)
{ {
rational time(1, 1); rational time(1, 1);
rational tb(1, 25); rational tb(1, 25);
std::string tc = Timecode::time_to_timecode(time, tb, std::string tc =
Timecode::kTimecodeSeconds, true); Timecode::time_to_timecode(time, tb, Timecode::kTimecodeSeconds, true);
EXPECT_EQ(tc.substr(0, 1), "+"); EXPECT_EQ(tc.substr(0, 1), "+");
} }
TEST(CoreTimecode, TimeToTimecodeInvalidTimebase) TEST(CoreTimecode, TimeToTimecodeInvalidTimebase)
{ {
rational time(1, 1); rational time(1, 1);
EXPECT_EQ(Timecode::time_to_timecode(time, rational(), Timecode::kFrames), EXPECT_EQ(Timecode::time_to_timecode(time, rational(), Timecode::kFrames),
"INVALID TIMEBASE"); "INVALID TIMEBASE");
} }
TEST(CoreTimecode, TimecodeToTimeSeconds) TEST(CoreTimecode, TimecodeToTimeSeconds)
{ {
rational tb(1, 25); rational tb(1, 25);
bool ok = false; bool ok = false;
rational t = Timecode::timecode_to_time("00:00:05.500", tb, rational t = Timecode::timecode_to_time("00:00:05.500", tb,
Timecode::kTimecodeSeconds, &ok); Timecode::kTimecodeSeconds, &ok);
EXPECT_TRUE(ok); EXPECT_TRUE(ok);
EXPECT_EQ(t, rational(11, 2)); EXPECT_EQ(t, rational(11, 2));
} }
TEST(CoreTimecode, TimecodeToTimeNonDropFrame) TEST(CoreTimecode, TimecodeToTimeNonDropFrame)
{ {
rational tb(1, 25); rational tb(1, 25);
bool ok = false; bool ok = false;
rational t = Timecode::timecode_to_time("00:00:02:03", tb, rational t = Timecode::timecode_to_time(
Timecode::kTimecodeNonDropFrame, &ok); "00:00:02:03", tb, Timecode::kTimecodeNonDropFrame, &ok);
EXPECT_TRUE(ok); EXPECT_TRUE(ok);
EXPECT_EQ(t, rational(53, 25)); EXPECT_EQ(t, rational(53, 25));
} }
TEST(CoreTimecode, TimecodeToTimeInvalid) TEST(CoreTimecode, TimecodeToTimeInvalid)
{ {
rational tb(1, 25); rational tb(1, 25);
bool ok = true; bool ok = true;
Timecode::timecode_to_time("not a timecode", tb, Timecode::timecode_to_time("not a timecode", tb, Timecode::kTimecodeSeconds,
Timecode::kTimecodeSeconds, &ok); &ok);
EXPECT_FALSE(ok); EXPECT_FALSE(ok);
} }
TEST(CoreTimecode, TimeToString) TEST(CoreTimecode, TimeToString)
{ {
EXPECT_EQ(Timecode::time_to_string(3661000), "01:01:01"); EXPECT_EQ(Timecode::time_to_string(3661000), "01:01:01");
} }
TEST(CoreTimecode, SnapTimeToTimebase) TEST(CoreTimecode, SnapTimeToTimebase)
{ {
rational tb(1, 25); rational tb(1, 25);
rational snapped = Timecode::snap_time_to_timebase(rational(1, 10), tb); rational snapped = Timecode::snap_time_to_timebase(rational(1, 10), tb);
// 0.1s @ 25fps rounds to frame 3 (0.12s) // 0.1s @ 25fps rounds to frame 3 (0.12s)
EXPECT_EQ(snapped, rational(3, 25)); EXPECT_EQ(snapped, rational(3, 25));
} }
TEST(CoreTimecode, TimeToTimestamp) TEST(CoreTimecode, TimeToTimestamp)
{ {
rational tb(1, 25); rational tb(1, 25);
EXPECT_EQ(Timecode::time_to_timestamp(rational(2, 1), tb), 50); 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::kFloor), 2);
EXPECT_EQ(Timecode::time_to_timestamp(0.08, tb, Timecode::kCeil), 2); EXPECT_EQ(Timecode::time_to_timestamp(0.08, tb, Timecode::kCeil), 2);
} }
TEST(CoreTimecode, TimestampToTime) TEST(CoreTimecode, TimestampToTime)
{ {
rational tb(1, 25); rational tb(1, 25);
EXPECT_EQ(Timecode::timestamp_to_time(50, tb), rational(2, 1)); EXPECT_EQ(Timecode::timestamp_to_time(50, tb), rational(2, 1));
} }
TEST(CoreTimecode, RescaleTimestamp) TEST(CoreTimecode, RescaleTimestamp)
{ {
rational src(1, 25); rational src(1, 25);
rational dst(1, 30); rational dst(1, 30);
EXPECT_EQ(Timecode::rescale_timestamp(50, src, dst), 60); EXPECT_EQ(Timecode::rescale_timestamp(50, src, dst), 60);
EXPECT_EQ(Timecode::rescale_timestamp(50, src, src), 50); EXPECT_EQ(Timecode::rescale_timestamp(50, src, src), 50);
} }
TEST(CoreTimecode, RescaleTimestampCeil) TEST(CoreTimecode, RescaleTimestampCeil)
{ {
rational src(1, 25); rational src(1, 25);
rational dst(1, 30); rational dst(1, 30);
EXPECT_EQ(Timecode::rescale_timestamp_ceil(1, src, dst), 2); EXPECT_EQ(Timecode::rescale_timestamp_ceil(1, src, dst), 2);
} }
TEST(CoreTimecode, TimebaseIsDropFrame) TEST(CoreTimecode, TimebaseIsDropFrame)
{ {
EXPECT_FALSE(Timecode::timebase_is_drop_frame(rational(1, 25))); EXPECT_FALSE(Timecode::timebase_is_drop_frame(rational(1, 25)));
EXPECT_TRUE(Timecode::timebase_is_drop_frame(rational(1001, 30000))); EXPECT_TRUE(Timecode::timebase_is_drop_frame(rational(1001, 30000)));
} }
+102 -101
View File
@@ -6,179 +6,180 @@ using namespace olive::core;
TEST(CoreTimeRange, ConstructAndAccess) TEST(CoreTimeRange, ConstructAndAccess)
{ {
TimeRange r(rational(1), rational(5)); TimeRange r(rational(1), rational(5));
EXPECT_EQ(r.in(), rational(1)); EXPECT_EQ(r.in(), rational(1));
EXPECT_EQ(r.out(), rational(5)); EXPECT_EQ(r.out(), rational(5));
EXPECT_EQ(r.length(), rational(4)); EXPECT_EQ(r.length(), rational(4));
} }
TEST(CoreTimeRange, NormalizationSwapsReversedBounds) TEST(CoreTimeRange, NormalizationSwapsReversedBounds)
{ {
TimeRange r(rational(5), rational(1)); TimeRange r(rational(5), rational(1));
EXPECT_EQ(r.in(), rational(1)); EXPECT_EQ(r.in(), rational(1));
EXPECT_EQ(r.out(), rational(5)); EXPECT_EQ(r.out(), rational(5));
} }
TEST(CoreTimeRange, SettersNormalize) TEST(CoreTimeRange, SettersNormalize)
{ {
TimeRange r(rational(0), rational(10)); TimeRange r(rational(0), rational(10));
r.set_in(rational(15)); r.set_in(rational(15));
EXPECT_EQ(r.in(), rational(10)); EXPECT_EQ(r.in(), rational(10));
EXPECT_EQ(r.out(), rational(15)); EXPECT_EQ(r.out(), rational(15));
r.set_out(rational(2)); r.set_out(rational(2));
EXPECT_EQ(r.in(), rational(2)); EXPECT_EQ(r.in(), rational(2));
EXPECT_EQ(r.out(), rational(10)); EXPECT_EQ(r.out(), rational(10));
} }
TEST(CoreTimeRange, ContainsRational) TEST(CoreTimeRange, ContainsRational)
{ {
TimeRange r(rational(0), rational(10)); TimeRange r(rational(0), rational(10));
EXPECT_TRUE(r.Contains(rational(5))); EXPECT_TRUE(r.Contains(rational(5)));
EXPECT_FALSE(r.Contains(rational(10))); EXPECT_FALSE(r.Contains(rational(10)));
EXPECT_FALSE(r.Contains(rational(-1))); EXPECT_FALSE(r.Contains(rational(-1)));
} }
TEST(CoreTimeRange, ContainsRange) TEST(CoreTimeRange, ContainsRange)
{ {
TimeRange outer(rational(0), rational(10)); TimeRange outer(rational(0), rational(10));
TimeRange inner(rational(2), rational(8)); TimeRange inner(rational(2), rational(8));
TimeRange partial(rational(5), rational(15)); TimeRange partial(rational(5), rational(15));
EXPECT_TRUE(outer.Contains(inner)); EXPECT_TRUE(outer.Contains(inner));
EXPECT_FALSE(outer.Contains(partial)); EXPECT_FALSE(outer.Contains(partial));
} }
TEST(CoreTimeRange, OverlapsWith) TEST(CoreTimeRange, OverlapsWith)
{ {
TimeRange a(rational(0), rational(10)); TimeRange a(rational(0), rational(10));
TimeRange b(rational(5), rational(15)); TimeRange b(rational(5), rational(15));
TimeRange c(rational(10), rational(20)); TimeRange c(rational(10), rational(20));
EXPECT_TRUE(a.OverlapsWith(b)); EXPECT_TRUE(a.OverlapsWith(b));
// By default bounds are inclusive, so [0,10] and [10,20] touch and overlap // By default bounds are inclusive, so [0,10] and [10,20] touch and overlap
EXPECT_TRUE(a.OverlapsWith(c)); EXPECT_TRUE(a.OverlapsWith(c));
EXPECT_FALSE(a.OverlapsWith(c, false, false)); EXPECT_FALSE(a.OverlapsWith(c, false, false));
} }
TEST(CoreTimeRange, CombineAndIntersect) TEST(CoreTimeRange, CombineAndIntersect)
{ {
TimeRange a(rational(0), rational(10)); TimeRange a(rational(0), rational(10));
TimeRange b(rational(5), rational(15)); TimeRange b(rational(5), rational(15));
TimeRange combined = a.Combined(b); TimeRange combined = a.Combined(b);
EXPECT_EQ(combined.in(), rational(0)); EXPECT_EQ(combined.in(), rational(0));
EXPECT_EQ(combined.out(), rational(15)); EXPECT_EQ(combined.out(), rational(15));
TimeRange intersect = a.Intersected(b); TimeRange intersect = a.Intersected(b);
EXPECT_EQ(intersect.in(), rational(5)); EXPECT_EQ(intersect.in(), rational(5));
EXPECT_EQ(intersect.out(), rational(10)); EXPECT_EQ(intersect.out(), rational(10));
} }
TEST(CoreTimeRange, Arithmetic) TEST(CoreTimeRange, Arithmetic)
{ {
TimeRange r(rational(0), rational(10)); TimeRange r(rational(0), rational(10));
TimeRange shifted = r + rational(5); TimeRange shifted = r + rational(5);
EXPECT_EQ(shifted.in(), rational(5)); EXPECT_EQ(shifted.in(), rational(5));
EXPECT_EQ(shifted.out(), rational(15)); EXPECT_EQ(shifted.out(), rational(15));
shifted -= rational(3); shifted -= rational(3);
EXPECT_EQ(shifted.in(), rational(2)); EXPECT_EQ(shifted.in(), rational(2));
EXPECT_EQ(shifted.out(), rational(12)); EXPECT_EQ(shifted.out(), rational(12));
} }
TEST(CoreTimeRange, Split) TEST(CoreTimeRange, Split)
{ {
TimeRange r(rational(0), rational(10)); TimeRange r(rational(0), rational(10));
auto pieces = r.Split(3); auto pieces = r.Split(3);
ASSERT_EQ(pieces.size(), 4u); ASSERT_EQ(pieces.size(), 4u);
EXPECT_EQ(pieces.front().in(), rational(0)); EXPECT_EQ(pieces.front().in(), rational(0));
} }
TEST(CoreTimeRangeList, InsertMergesOverlapping) TEST(CoreTimeRangeList, InsertMergesOverlapping)
{ {
TimeRangeList list; TimeRangeList list;
list.insert(TimeRange(rational(0), rational(5))); list.insert(TimeRange(rational(0), rational(5)));
list.insert(TimeRange(rational(3), rational(8))); list.insert(TimeRange(rational(3), rational(8)));
list.insert(TimeRange(rational(10), rational(12))); list.insert(TimeRange(rational(10), rational(12)));
EXPECT_EQ(list.size(), 2); EXPECT_EQ(list.size(), 2);
EXPECT_EQ(list.first().in(), rational(0)); EXPECT_EQ(list.first().in(), rational(0));
EXPECT_EQ(list.first().out(), rational(8)); EXPECT_EQ(list.first().out(), rational(8));
} }
TEST(CoreTimeRangeList, RemoveSplitsRange) TEST(CoreTimeRangeList, RemoveSplitsRange)
{ {
TimeRangeList list; TimeRangeList list;
list.insert(TimeRange(rational(0), rational(10))); list.insert(TimeRange(rational(0), rational(10)));
list.remove(TimeRange(rational(3), rational(7))); list.remove(TimeRange(rational(3), rational(7)));
EXPECT_EQ(list.size(), 2); EXPECT_EQ(list.size(), 2);
EXPECT_EQ(list.first().out(), rational(3)); EXPECT_EQ(list.first().out(), rational(3));
EXPECT_EQ(list.last().in(), rational(7)); EXPECT_EQ(list.last().in(), rational(7));
} }
TEST(CoreTimeRangeList, Shift) TEST(CoreTimeRangeList, Shift)
{ {
TimeRangeList list; TimeRangeList list;
list.insert(TimeRange(rational(0), rational(5))); list.insert(TimeRange(rational(0), rational(5)));
list.shift(rational(10)); list.shift(rational(10));
EXPECT_EQ(list.first().in(), rational(10)); EXPECT_EQ(list.first().in(), rational(10));
EXPECT_EQ(list.first().out(), rational(15)); EXPECT_EQ(list.first().out(), rational(15));
} }
TEST(CoreTimeRangeList, TrimInAndOut) TEST(CoreTimeRangeList, TrimInAndOut)
{ {
TimeRangeList list; TimeRangeList list;
list.insert(TimeRange(rational(10), rational(20))); list.insert(TimeRange(rational(10), rational(20)));
list.trim_in(rational(5)); list.trim_in(rational(5));
EXPECT_EQ(list.first().in(), rational(15)); EXPECT_EQ(list.first().in(), rational(15));
EXPECT_EQ(list.first().out(), rational(20)); EXPECT_EQ(list.first().out(), rational(20));
list.trim_out(rational(-5)); list.trim_out(rational(-5));
// set_out(out + diff) = 20 + (-5) = 15 // set_out(out + diff) = 20 + (-5) = 15
EXPECT_EQ(list.first().out(), rational(15)); EXPECT_EQ(list.first().out(), rational(15));
} }
TEST(CoreTimeRangeList, Intersects) TEST(CoreTimeRangeList, Intersects)
{ {
TimeRangeList list; TimeRangeList list;
list.insert(TimeRange(rational(0), rational(10))); list.insert(TimeRange(rational(0), rational(10)));
list.insert(TimeRange(rational(20), rational(30))); list.insert(TimeRange(rational(20), rational(30)));
TimeRangeList result = list.Intersects(TimeRange(rational(5), rational(25))); TimeRangeList result =
EXPECT_EQ(result.size(), 2); list.Intersects(TimeRange(rational(5), rational(25)));
EXPECT_EQ(result.first().in(), rational(5)); EXPECT_EQ(result.size(), 2);
EXPECT_EQ(result.first().out(), rational(10)); EXPECT_EQ(result.first().in(), rational(5));
EXPECT_EQ(result.first().out(), rational(10));
} }
TEST(CoreTimeRangeListFrameIterator, IteratesFrames) TEST(CoreTimeRangeListFrameIterator, IteratesFrames)
{ {
TimeRangeList list; TimeRangeList list;
// 5 seconds at 25fps = 125 frames // 5 seconds at 25fps = 125 frames
list.insert(TimeRange(rational(0), rational(5))); list.insert(TimeRange(rational(0), rational(5)));
TimeRangeListFrameIterator it(list, rational(1, 25)); TimeRangeListFrameIterator it(list, rational(1, 25));
rational out; rational out;
int count = 0; int count = 0;
while (it.GetNext(&out)) { while (it.GetNext(&out)) {
count++; count++;
} }
EXPECT_EQ(count, 125); EXPECT_EQ(count, 125);
EXPECT_EQ(it.size(), 125); EXPECT_EQ(it.size(), 125);
} }
TEST(CoreTimeRangeListFrameIterator, HasNext) TEST(CoreTimeRangeListFrameIterator, HasNext)
{ {
TimeRangeList list; TimeRangeList list;
list.insert(TimeRange(rational(0), rational(1))); list.insert(TimeRange(rational(0), rational(1)));
TimeRangeListFrameIterator it(list, rational(1, 25)); TimeRangeListFrameIterator it(list, rational(1, 25));
EXPECT_TRUE(it.HasNext()); EXPECT_TRUE(it.HasNext());
rational out; rational out;
while (it.GetNext(&out)) { while (it.GetNext(&out)) {
} }
EXPECT_FALSE(it.HasNext()); EXPECT_FALSE(it.HasNext());
} }
+80 -67
View File
@@ -21,7 +21,8 @@ TEST(DynamicRenderBackend, LoadsExperimentalOpenGLBackend)
#else #else
olive::DynamicRenderer renderer(QStringLiteral("opengl")); olive::DynamicRenderer renderer(QStringLiteral("opengl"));
if (!renderer.Load()) { if (!renderer.Load()) {
GTEST_SKIP() << "opengl backend library could not be loaded in this environment"; GTEST_SKIP()
<< "opengl backend library could not be loaded in this environment";
} }
EXPECT_EQ(renderer.OpenGLContext(), nullptr); EXPECT_EQ(renderer.OpenGLContext(), nullptr);
@@ -48,11 +49,13 @@ TEST(DynamicRenderBackend, OpenGLBackendFollowsAdapterToRenderThread)
#else #else
olive::DynamicRenderer renderer(QStringLiteral("opengl")); olive::DynamicRenderer renderer(QStringLiteral("opengl"));
if (!renderer.Load()) { if (!renderer.Load()) {
GTEST_SKIP() << "opengl backend library could not be loaded in this environment"; GTEST_SKIP()
<< "opengl backend library could not be loaded in this environment";
} }
if (!renderer.Init()) { if (!renderer.Init()) {
GTEST_SKIP() << "OpenGL backend could not be initialized on this system"; GTEST_SKIP()
<< "OpenGL backend could not be initialized on this system";
} }
QThread render_thread; QThread render_thread;
@@ -71,9 +74,9 @@ TEST(DynamicRenderBackend, OpenGLBackendFollowsAdapterToRenderThread)
&renderer, &renderer,
[&]() { [&]() {
renderer.PostInit(); renderer.PostInit();
texture = renderer.CreateTexture(olive::VideoParams( texture = renderer.CreateTexture(
64, 64, olive::PixelFormat::U8, olive::VideoParams(64, 64, olive::PixelFormat::U8,
olive::VideoParams::kRGBAChannelCount)); olive::VideoParams::kRGBAChannelCount));
}, },
Qt::BlockingQueuedConnection); Qt::BlockingQueuedConnection);
@@ -94,7 +97,8 @@ TEST(DynamicRenderBackend, LoadsExperimentalVulkanBackendWhenAvailable)
#else #else
olive::DynamicRenderer renderer(QStringLiteral("vulkan")); olive::DynamicRenderer renderer(QStringLiteral("vulkan"));
if (!renderer.Load()) { if (!renderer.Load()) {
GTEST_SKIP() << "vulkan backend library could not be loaded in this environment"; GTEST_SKIP()
<< "vulkan backend library could not be loaded in this environment";
} }
OakRenderBackendInfo info = {}; OakRenderBackendInfo info = {};
@@ -121,13 +125,15 @@ TEST(DynamicRenderBackend, FallsBackWhenExperimentalVulkanUnavailable)
#else #else
olive::DynamicRenderer renderer(QStringLiteral("vulkan")); olive::DynamicRenderer renderer(QStringLiteral("vulkan"));
if (!renderer.Load()) { if (!renderer.Load()) {
GTEST_SKIP() << "vulkan backend library could not be loaded in this environment"; GTEST_SKIP()
<< "vulkan backend library could not be loaded in this environment";
} }
OakRenderBackendInfo info = {}; OakRenderBackendInfo info = {};
ASSERT_TRUE(renderer.GetBackendInfo(&info)); ASSERT_TRUE(renderer.GetBackendInfo(&info));
if (info.kind == OAK_RENDER_BACKEND_VULKAN) { if (info.kind == OAK_RENDER_BACKEND_VULKAN) {
GTEST_SKIP() << "Vulkan backend is available on this system; skip fallback test"; GTEST_SKIP()
<< "Vulkan backend is available on this system; skip fallback test";
} }
EXPECT_EQ(renderer.backend_name(), QStringLiteral("opengl")); EXPECT_EQ(renderer.backend_name(), QStringLiteral("opengl"));
EXPECT_EQ(renderer.OpenGLContext(), nullptr); EXPECT_EQ(renderer.OpenGLContext(), nullptr);
@@ -145,7 +151,8 @@ TEST(DynamicRenderBackend, VulkanUploadBlitDownload)
#else #else
olive::DynamicRenderer renderer(QStringLiteral("vulkan")); olive::DynamicRenderer renderer(QStringLiteral("vulkan"));
if (!renderer.Load()) { if (!renderer.Load()) {
GTEST_SKIP() << "vulkan backend library could not be loaded in this environment"; GTEST_SKIP()
<< "vulkan backend library could not be loaded in this environment";
} }
OakRenderBackendInfo info = {}; OakRenderBackendInfo info = {};
@@ -168,8 +175,8 @@ TEST(DynamicRenderBackend, VulkanUploadBlitDownload)
QByteArray src_data(kSize * kSize * 4, 0); QByteArray src_data(kSize * kSize * 4, 0);
for (int i = 0; i < kSize * kSize; ++i) { for (int i = 0; i < kSize * kSize; ++i) {
src_data[i * 4 + 0] = static_cast<char>(255); // R src_data[i * 4 + 0] = static_cast<char>(255); // R
src_data[i * 4 + 1] = static_cast<char>(0); // G src_data[i * 4 + 1] = static_cast<char>(0); // G
src_data[i * 4 + 2] = static_cast<char>(0); // B src_data[i * 4 + 2] = static_cast<char>(0); // B
src_data[i * 4 + 3] = static_cast<char>(255); // A src_data[i * 4 + 3] = static_cast<char>(255); // A
} }
src->Upload(src_data.data(), kSize); src->Upload(src_data.data(), kSize);
@@ -178,23 +185,24 @@ TEST(DynamicRenderBackend, VulkanUploadBlitDownload)
ASSERT_NE(dst, nullptr); ASSERT_NE(dst, nullptr);
ASSERT_FALSE(dst->IsDummy()); ASSERT_FALSE(dst->IsDummy());
const QString vert = QStringLiteral( const QString vert =
"uniform mat4 ove_mvpmat;\n" QStringLiteral("uniform mat4 ove_mvpmat;\n"
"in vec4 a_position;\n" "in vec4 a_position;\n"
"in vec2 a_texcoord;\n" "in vec2 a_texcoord;\n"
"out vec2 ove_texcoord;\n" "out vec2 ove_texcoord;\n"
"void main() {\n" "void main() {\n"
" gl_Position = ove_mvpmat * a_position;\n" " gl_Position = ove_mvpmat * a_position;\n"
" ove_texcoord = a_texcoord;\n" " ove_texcoord = a_texcoord;\n"
"}\n"); "}\n");
const QString frag = QStringLiteral( const QString frag =
"uniform sampler2D ove_maintex;\n" QStringLiteral("uniform sampler2D ove_maintex;\n"
"in vec2 ove_texcoord;\n" "in vec2 ove_texcoord;\n"
"out vec4 frag_color;\n" "out vec4 frag_color;\n"
"void main() {\n" "void main() {\n"
" frag_color = texture(ove_maintex, ove_texcoord);\n" " frag_color = texture(ove_maintex, ove_texcoord);\n"
"}\n"); "}\n");
QVariant shader = renderer.CreateNativeShader(olive::ShaderCode(frag, vert)); QVariant shader =
renderer.CreateNativeShader(olive::ShaderCode(frag, vert));
ASSERT_FALSE(shader.isNull()); ASSERT_FALSE(shader.isNull());
olive::ShaderJob job; olive::ShaderJob job;
@@ -226,7 +234,8 @@ TEST(DynamicRenderBackend, VulkanNullDestinationBlitDoesNotCrash)
#else #else
olive::DynamicRenderer renderer(QStringLiteral("vulkan")); olive::DynamicRenderer renderer(QStringLiteral("vulkan"));
if (!renderer.Load()) { if (!renderer.Load()) {
GTEST_SKIP() << "vulkan backend library could not be loaded in this environment"; GTEST_SKIP()
<< "vulkan backend library could not be loaded in this environment";
} }
OakRenderBackendInfo info = {}; OakRenderBackendInfo info = {};
@@ -253,23 +262,24 @@ TEST(DynamicRenderBackend, VulkanNullDestinationBlitDoesNotCrash)
} }
src->Upload(src_data.data(), kSize); src->Upload(src_data.data(), kSize);
const QString vert = QStringLiteral( const QString vert =
"uniform mat4 ove_mvpmat;\n" QStringLiteral("uniform mat4 ove_mvpmat;\n"
"in vec4 a_position;\n" "in vec4 a_position;\n"
"in vec2 a_texcoord;\n" "in vec2 a_texcoord;\n"
"out vec2 ove_texcoord;\n" "out vec2 ove_texcoord;\n"
"void main() {\n" "void main() {\n"
" gl_Position = ove_mvpmat * a_position;\n" " gl_Position = ove_mvpmat * a_position;\n"
" ove_texcoord = a_texcoord;\n" " ove_texcoord = a_texcoord;\n"
"}\n"); "}\n");
const QString frag = QStringLiteral( const QString frag =
"uniform sampler2D ove_maintex;\n" QStringLiteral("uniform sampler2D ove_maintex;\n"
"in vec2 ove_texcoord;\n" "in vec2 ove_texcoord;\n"
"out vec4 frag_color;\n" "out vec4 frag_color;\n"
"void main() {\n" "void main() {\n"
" frag_color = texture(ove_maintex, ove_texcoord);\n" " frag_color = texture(ove_maintex, ove_texcoord);\n"
"}\n"); "}\n");
QVariant shader = renderer.CreateNativeShader(olive::ShaderCode(frag, vert)); QVariant shader =
renderer.CreateNativeShader(olive::ShaderCode(frag, vert));
ASSERT_FALSE(shader.isNull()); ASSERT_FALSE(shader.isNull());
olive::ShaderJob job; olive::ShaderJob job;
@@ -294,7 +304,8 @@ TEST(DynamicRenderBackend, VulkanIterativeBlitPingPong)
#else #else
olive::DynamicRenderer renderer(QStringLiteral("vulkan")); olive::DynamicRenderer renderer(QStringLiteral("vulkan"));
if (!renderer.Load()) { if (!renderer.Load()) {
GTEST_SKIP() << "vulkan backend library could not be loaded in this environment"; GTEST_SKIP()
<< "vulkan backend library could not be loaded in this environment";
} }
OakRenderBackendInfo info = {}; OakRenderBackendInfo info = {};
@@ -327,24 +338,25 @@ TEST(DynamicRenderBackend, VulkanIterativeBlitPingPong)
ASSERT_FALSE(dst->IsDummy()); ASSERT_FALSE(dst->IsDummy());
// Shader that samples the iterative input and scales RGB by 0.5 each pass. // Shader that samples the iterative input and scales RGB by 0.5 each pass.
const QString vert = QStringLiteral( const QString vert =
"uniform mat4 ove_mvpmat;\n" QStringLiteral("uniform mat4 ove_mvpmat;\n"
"in vec4 a_position;\n" "in vec4 a_position;\n"
"in vec2 a_texcoord;\n" "in vec2 a_texcoord;\n"
"out vec2 ove_texcoord;\n" "out vec2 ove_texcoord;\n"
"void main() {\n" "void main() {\n"
" gl_Position = ove_mvpmat * a_position;\n" " gl_Position = ove_mvpmat * a_position;\n"
" ove_texcoord = a_texcoord;\n" " ove_texcoord = a_texcoord;\n"
"}\n"); "}\n");
const QString frag = QStringLiteral( const QString frag =
"uniform sampler2D ove_maintex;\n" QStringLiteral("uniform sampler2D ove_maintex;\n"
"in vec2 ove_texcoord;\n" "in vec2 ove_texcoord;\n"
"out vec4 frag_color;\n" "out vec4 frag_color;\n"
"void main() {\n" "void main() {\n"
" vec4 c = texture(ove_maintex, ove_texcoord);\n" " vec4 c = texture(ove_maintex, ove_texcoord);\n"
" frag_color = vec4(c.rgb * 0.5, c.a);\n" " frag_color = vec4(c.rgb * 0.5, c.a);\n"
"}\n"); "}\n");
QVariant shader = renderer.CreateNativeShader(olive::ShaderCode(frag, vert)); QVariant shader =
renderer.CreateNativeShader(olive::ShaderCode(frag, vert));
ASSERT_FALSE(shader.isNull()); ASSERT_FALSE(shader.isNull());
olive::ShaderJob job; olive::ShaderJob job;
@@ -378,7 +390,8 @@ TEST(DynamicRenderBackend, VulkanUploadDownloadThreeChannel)
#else #else
olive::DynamicRenderer renderer(QStringLiteral("vulkan")); olive::DynamicRenderer renderer(QStringLiteral("vulkan"));
if (!renderer.Load()) { if (!renderer.Load()) {
GTEST_SKIP() << "vulkan backend library could not be loaded in this environment"; GTEST_SKIP()
<< "vulkan backend library could not be loaded in this environment";
} }
OakRenderBackendInfo info = {}; OakRenderBackendInfo info = {};
+2 -1
View File
@@ -12,7 +12,8 @@ using namespace olive;
TEST(FFmpegDecoderHW, H264_422_10bit_CPUFrame_IsNotBlack) TEST(FFmpegDecoderHW, H264_422_10bit_CPUFrame_IsNotBlack)
{ {
const QString path = QStringLiteral("/home/mikesolar/Videos/dual_system_video.MOV"); const QString path =
QStringLiteral("/home/mikesolar/Videos/dual_system_video.MOV");
if (!QFileInfo::exists(path)) { if (!QFileInfo::exists(path)) {
GTEST_SKIP() << "Test footage not available: " << path.toStdString(); GTEST_SKIP() << "Test footage not available: " << path.toStdString();
+4 -4
View File
@@ -10,10 +10,10 @@ int main(int argc, char **argv)
{ {
Q_INIT_RESOURCE(ocioconf); Q_INIT_RESOURCE(ocioconf);
if (qEnvironmentVariableIsEmpty("OCIO")) { if (qEnvironmentVariableIsEmpty("OCIO")) {
qputenv("OCIO", QFile::encodeName( qputenv("OCIO",
QDir(QStringLiteral(OAK_TEST_SOURCE_DIR)) QFile::encodeName(QDir(QStringLiteral(OAK_TEST_SOURCE_DIR))
.filePath(QStringLiteral( .filePath(QStringLiteral(
"app/render/ocioconf/config.ocio")))); "app/render/ocioconf/config.ocio"))));
} }
if (qEnvironmentVariableIsEmpty("QT_QPA_PLATFORM")) { if (qEnvironmentVariableIsEmpty("QT_QPA_PLATFORM")) {
qputenv("QT_QPA_PLATFORM", "offscreen"); qputenv("QT_QPA_PLATFORM", "offscreen");
+37 -18
View File
@@ -5,24 +5,39 @@
TEST(ModuleSmoke, ToolAddableObjectNames) TEST(ModuleSmoke, ToolAddableObjectNames)
{ {
EXPECT_FALSE(olive::Tool::GetAddableObjectName(olive::Tool::kAddableEmpty).isEmpty()); EXPECT_FALSE(
EXPECT_FALSE(olive::Tool::GetAddableObjectName(olive::Tool::kAddableBars).isEmpty()); olive::Tool::GetAddableObjectName(olive::Tool::kAddableEmpty).isEmpty());
EXPECT_FALSE(olive::Tool::GetAddableObjectName(olive::Tool::kAddableShape).isEmpty()); EXPECT_FALSE(
EXPECT_FALSE(olive::Tool::GetAddableObjectName(olive::Tool::kAddableSolid).isEmpty()); olive::Tool::GetAddableObjectName(olive::Tool::kAddableBars).isEmpty());
EXPECT_FALSE(olive::Tool::GetAddableObjectName(olive::Tool::kAddableTitle).isEmpty()); EXPECT_FALSE(
EXPECT_FALSE(olive::Tool::GetAddableObjectName(olive::Tool::kAddableTone).isEmpty()); olive::Tool::GetAddableObjectName(olive::Tool::kAddableShape).isEmpty());
EXPECT_FALSE(olive::Tool::GetAddableObjectName(olive::Tool::kAddableSubtitle).isEmpty()); EXPECT_FALSE(
olive::Tool::GetAddableObjectName(olive::Tool::kAddableSolid).isEmpty());
EXPECT_FALSE(
olive::Tool::GetAddableObjectName(olive::Tool::kAddableTitle).isEmpty());
EXPECT_FALSE(
olive::Tool::GetAddableObjectName(olive::Tool::kAddableTone).isEmpty());
EXPECT_FALSE(
olive::Tool::GetAddableObjectName(olive::Tool::kAddableSubtitle)
.isEmpty());
} }
TEST(ModuleSmoke, ToolAddableObjectIds) TEST(ModuleSmoke, ToolAddableObjectIds)
{ {
EXPECT_EQ(olive::Tool::GetAddableObjectID(olive::Tool::kAddableEmpty), QStringLiteral("empty")); EXPECT_EQ(olive::Tool::GetAddableObjectID(olive::Tool::kAddableEmpty),
EXPECT_EQ(olive::Tool::GetAddableObjectID(olive::Tool::kAddableBars), QStringLiteral("bars")); QStringLiteral("empty"));
EXPECT_EQ(olive::Tool::GetAddableObjectID(olive::Tool::kAddableShape), QStringLiteral("shape")); EXPECT_EQ(olive::Tool::GetAddableObjectID(olive::Tool::kAddableBars),
EXPECT_EQ(olive::Tool::GetAddableObjectID(olive::Tool::kAddableSolid), QStringLiteral("solid")); QStringLiteral("bars"));
EXPECT_EQ(olive::Tool::GetAddableObjectID(olive::Tool::kAddableTitle), QStringLiteral("title")); EXPECT_EQ(olive::Tool::GetAddableObjectID(olive::Tool::kAddableShape),
EXPECT_EQ(olive::Tool::GetAddableObjectID(olive::Tool::kAddableTone), QStringLiteral("tone")); QStringLiteral("shape"));
EXPECT_EQ(olive::Tool::GetAddableObjectID(olive::Tool::kAddableSubtitle), QStringLiteral("subtitle")); EXPECT_EQ(olive::Tool::GetAddableObjectID(olive::Tool::kAddableSolid),
QStringLiteral("solid"));
EXPECT_EQ(olive::Tool::GetAddableObjectID(olive::Tool::kAddableTitle),
QStringLiteral("title"));
EXPECT_EQ(olive::Tool::GetAddableObjectID(olive::Tool::kAddableTone),
QStringLiteral("tone"));
EXPECT_EQ(olive::Tool::GetAddableObjectID(olive::Tool::kAddableSubtitle),
QStringLiteral("subtitle"));
} }
TEST(ModuleSmoke, HumanStringsSampleRate) TEST(ModuleSmoke, HumanStringsSampleRate)
@@ -33,12 +48,16 @@ TEST(ModuleSmoke, HumanStringsSampleRate)
TEST(ModuleSmoke, HumanStringsChannelLayout) TEST(ModuleSmoke, HumanStringsChannelLayout)
{ {
EXPECT_FALSE(olive::HumanStrings::ChannelLayoutToString(AV_CH_LAYOUT_MONO).isEmpty()); EXPECT_FALSE(
EXPECT_FALSE(olive::HumanStrings::ChannelLayoutToString(AV_CH_LAYOUT_STEREO).isEmpty()); olive::HumanStrings::ChannelLayoutToString(AV_CH_LAYOUT_MONO).isEmpty());
EXPECT_FALSE(olive::HumanStrings::ChannelLayoutToString(AV_CH_LAYOUT_STEREO)
.isEmpty());
} }
TEST(ModuleSmoke, HumanStringsFormat) TEST(ModuleSmoke, HumanStringsFormat)
{ {
EXPECT_FALSE(olive::HumanStrings::FormatToString(olive::SampleFormat::U8).isEmpty()); EXPECT_FALSE(
EXPECT_FALSE(olive::HumanStrings::FormatToString(olive::SampleFormat::F32).isEmpty()); olive::HumanStrings::FormatToString(olive::SampleFormat::U8).isEmpty());
EXPECT_FALSE(
olive::HumanStrings::FormatToString(olive::SampleFormat::F32).isEmpty());
} }
+4 -2
View File
@@ -19,8 +19,10 @@ TEST(NodeGlobals, ConstructedWithParams)
audio_params.set_sample_rate(48000); audio_params.set_sample_rate(48000);
audio_params.set_channel_layout(AV_CH_LAYOUT_STEREO); audio_params.set_channel_layout(AV_CH_LAYOUT_STEREO);
olive::TimeRange time(olive::core::rational(1, 24), olive::core::rational(2, 24)); olive::TimeRange time(olive::core::rational(1, 24),
olive::NodeGlobals globals(video_params, audio_params, time, olive::LoopMode::kLoopModeLoop); olive::core::rational(2, 24));
olive::NodeGlobals globals(video_params, audio_params, time,
olive::LoopMode::kLoopModeLoop);
EXPECT_EQ(globals.vparams().width(), 1920); EXPECT_EQ(globals.vparams().width(), 1920);
EXPECT_EQ(globals.vparams().height(), 1080); EXPECT_EQ(globals.vparams().height(), 1080);
+5 -3
View File
@@ -47,8 +47,9 @@ TEST(NodeProject, SettingsRoundTrip)
{ {
olive::Project project; olive::Project project;
project.SetSetting(olive::Project::kCacheLocationSettingKey, project.SetSetting(
QString::number(olive::Project::kCacheStoreAlongsideProject)); olive::Project::kCacheLocationSettingKey,
QString::number(olive::Project::kCacheStoreAlongsideProject));
EXPECT_EQ(project.GetCacheLocationSetting(), EXPECT_EQ(project.GetCacheLocationSetting(),
olive::Project::kCacheStoreAlongsideProject); olive::Project::kCacheStoreAlongsideProject);
@@ -62,7 +63,8 @@ TEST(NodeProject, SettingsRoundTrip)
EXPECT_EQ(project.GetDefaultInputColorSpace(), QStringLiteral("ACEScg")); EXPECT_EQ(project.GetDefaultInputColorSpace(), QStringLiteral("ACEScg"));
project.SetColorReferenceSpace(QStringLiteral("ACES - ACEScg")); project.SetColorReferenceSpace(QStringLiteral("ACES - ACEScg"));
EXPECT_EQ(project.GetColorReferenceSpace(), QStringLiteral("ACES - ACEScg")); EXPECT_EQ(project.GetColorReferenceSpace(),
QStringLiteral("ACES - ACEScg"));
} }
TEST(NodeProject, InitializeCreatesRoot) TEST(NodeProject, InitializeCreatesRoot)
+7 -3
View File
@@ -10,7 +10,8 @@
#include "node/value.h" #include "node/value.h"
#include "render/diskmanager.h" #include "render/diskmanager.h"
namespace { namespace
{
class TestNode final : public olive::Node { class TestNode final : public olive::Node {
public: public:
TestNode() TestNode()
@@ -55,7 +56,8 @@ public:
TEST(NodeSerialization, SaveAndLoadInput) TEST(NodeSerialization, SaveAndLoadInput)
{ {
const bool created_disk_manager = (olive::DiskManager::instance() == nullptr); const bool created_disk_manager =
(olive::DiskManager::instance() == nullptr);
if (created_disk_manager) { if (created_disk_manager) {
olive::DiskManager::CreateInstance(); olive::DiskManager::CreateInstance();
} }
@@ -87,7 +89,9 @@ TEST(NodeSerialization, SaveAndLoadInput)
EXPECT_EQ(loaded.GetLabel(), QStringLiteral("MyNode")); EXPECT_EQ(loaded.GetLabel(), QStringLiteral("MyNode"));
EXPECT_EQ(loaded.GetOverrideColor(), 2); EXPECT_EQ(loaded.GetOverrideColor(), 2);
EXPECT_DOUBLE_EQ(loaded.GetSplitStandardValue(QStringLiteral("Value"), -1) EXPECT_DOUBLE_EQ(loaded.GetSplitStandardValue(QStringLiteral("Value"), -1)
.first().toDouble(), 3.5); .first()
.toDouble(),
3.5);
if (created_disk_manager) { if (created_disk_manager) {
olive::DiskManager::DestroyInstance(); olive::DiskManager::DestroyInstance();
+26 -19
View File
@@ -11,27 +11,27 @@ TEST(NodeValue, VectorRoundTrip)
QVector2D v2(1.5f, -2.0f); QVector2D v2(1.5f, -2.0f);
QString encoded = olive::NodeValue::ValueToString( QString encoded = olive::NodeValue::ValueToString(
olive::NodeValue::kVec2, QVariant::fromValue(v2), false); olive::NodeValue::kVec2, QVariant::fromValue(v2), false);
QVariant decoded = olive::NodeValue::StringToValue( QVariant decoded = olive::NodeValue::StringToValue(olive::NodeValue::kVec2,
olive::NodeValue::kVec2, encoded, false); encoded, false);
QVector2D v2_out = decoded.value<QVector2D>(); QVector2D v2_out = decoded.value<QVector2D>();
EXPECT_FLOAT_EQ(v2_out.x(), v2.x()); EXPECT_FLOAT_EQ(v2_out.x(), v2.x());
EXPECT_FLOAT_EQ(v2_out.y(), v2.y()); EXPECT_FLOAT_EQ(v2_out.y(), v2.y());
QVector3D v3(1.0f, 2.0f, 3.0f); QVector3D v3(1.0f, 2.0f, 3.0f);
encoded = olive::NodeValue::ValueToString( encoded = olive::NodeValue::ValueToString(olive::NodeValue::kVec3,
olive::NodeValue::kVec3, QVariant::fromValue(v3), false); QVariant::fromValue(v3), false);
decoded = olive::NodeValue::StringToValue( decoded = olive::NodeValue::StringToValue(olive::NodeValue::kVec3, encoded,
olive::NodeValue::kVec3, encoded, false); false);
QVector3D v3_out = decoded.value<QVector3D>(); QVector3D v3_out = decoded.value<QVector3D>();
EXPECT_FLOAT_EQ(v3_out.x(), v3.x()); EXPECT_FLOAT_EQ(v3_out.x(), v3.x());
EXPECT_FLOAT_EQ(v3_out.y(), v3.y()); EXPECT_FLOAT_EQ(v3_out.y(), v3.y());
EXPECT_FLOAT_EQ(v3_out.z(), v3.z()); EXPECT_FLOAT_EQ(v3_out.z(), v3.z());
QVector4D v4(1.0f, 2.0f, 3.0f, 4.0f); QVector4D v4(1.0f, 2.0f, 3.0f, 4.0f);
encoded = olive::NodeValue::ValueToString( encoded = olive::NodeValue::ValueToString(olive::NodeValue::kVec4,
olive::NodeValue::kVec4, QVariant::fromValue(v4), false); QVariant::fromValue(v4), false);
decoded = olive::NodeValue::StringToValue( decoded = olive::NodeValue::StringToValue(olive::NodeValue::kVec4, encoded,
olive::NodeValue::kVec4, encoded, false); false);
QVector4D v4_out = decoded.value<QVector4D>(); QVector4D v4_out = decoded.value<QVector4D>();
EXPECT_FLOAT_EQ(v4_out.x(), v4.x()); EXPECT_FLOAT_EQ(v4_out.x(), v4.x());
EXPECT_FLOAT_EQ(v4_out.y(), v4.y()); EXPECT_FLOAT_EQ(v4_out.y(), v4.y());
@@ -42,8 +42,8 @@ TEST(NodeValue, VectorRoundTrip)
TEST(NodeValue, BinaryRoundTrip) TEST(NodeValue, BinaryRoundTrip)
{ {
QByteArray data("OliveTest"); QByteArray data("OliveTest");
QString encoded = olive::NodeValue::ValueToString( QString encoded =
olive::NodeValue::kBinary, data, false); olive::NodeValue::ValueToString(olive::NodeValue::kBinary, data, false);
QVariant decoded = olive::NodeValue::StringToValue( QVariant decoded = olive::NodeValue::StringToValue(
olive::NodeValue::kBinary, encoded, false); olive::NodeValue::kBinary, encoded, false);
EXPECT_EQ(decoded.toByteArray(), data); EXPECT_EQ(decoded.toByteArray(), data);
@@ -51,9 +51,12 @@ TEST(NodeValue, BinaryRoundTrip)
TEST(NodeValue, TypeClassification) TEST(NodeValue, TypeClassification)
{ {
EXPECT_TRUE(olive::NodeValue::type_can_be_interpolated(olive::NodeValue::kFloat)); EXPECT_TRUE(
EXPECT_TRUE(olive::NodeValue::type_can_be_interpolated(olive::NodeValue::kColor)); olive::NodeValue::type_can_be_interpolated(olive::NodeValue::kFloat));
EXPECT_FALSE(olive::NodeValue::type_can_be_interpolated(olive::NodeValue::kInt)); EXPECT_TRUE(
olive::NodeValue::type_can_be_interpolated(olive::NodeValue::kColor));
EXPECT_FALSE(
olive::NodeValue::type_can_be_interpolated(olive::NodeValue::kInt));
EXPECT_TRUE(olive::NodeValue::type_is_numeric(olive::NodeValue::kInt)); EXPECT_TRUE(olive::NodeValue::type_is_numeric(olive::NodeValue::kInt));
EXPECT_TRUE(olive::NodeValue::type_is_numeric(olive::NodeValue::kFloat)); EXPECT_TRUE(olive::NodeValue::type_is_numeric(olive::NodeValue::kFloat));
@@ -70,7 +73,8 @@ TEST(NodeValue, TypeClassification)
TEST(NodeValue, DataTypeNameRoundTrip) TEST(NodeValue, DataTypeNameRoundTrip)
{ {
for (int i = olive::NodeValue::kNone; i < olive::NodeValue::kDataTypeCount; ++i) { for (int i = olive::NodeValue::kNone; i < olive::NodeValue::kDataTypeCount;
++i) {
auto type = static_cast<olive::NodeValue::Type>(i); auto type = static_cast<olive::NodeValue::Type>(i);
QString name = olive::NodeValue::GetDataTypeName(type); QString name = olive::NodeValue::GetDataTypeName(type);
if (name.isEmpty()) { if (name.isEmpty()) {
@@ -109,8 +113,10 @@ TEST(NodeValueTable, PushAndGet)
TEST(NodeValueTable, TakeRemovesValue) TEST(NodeValueTable, TakeRemovesValue)
{ {
olive::NodeValueTable table; olive::NodeValueTable table;
table.Push(olive::NodeValue(olive::NodeValue::kInt, static_cast<int64_t>(1))); table.Push(
table.Push(olive::NodeValue(olive::NodeValue::kInt, static_cast<int64_t>(2))); olive::NodeValue(olive::NodeValue::kInt, static_cast<int64_t>(1)));
table.Push(
olive::NodeValue(olive::NodeValue::kInt, static_cast<int64_t>(2)));
olive::NodeValue taken = table.Take(olive::NodeValue::kInt); olive::NodeValue taken = table.Take(olive::NodeValue::kInt);
EXPECT_EQ(taken.toInt(), 2); EXPECT_EQ(taken.toInt(), 2);
@@ -120,7 +126,8 @@ TEST(NodeValueTable, TakeRemovesValue)
TEST(NodeValueTable, ClearEmptiesTable) TEST(NodeValueTable, ClearEmptiesTable)
{ {
olive::NodeValueTable table; olive::NodeValueTable table;
table.Push(olive::NodeValue(olive::NodeValue::kText, QStringLiteral("hello"))); table.Push(
olive::NodeValue(olive::NodeValue::kText, QStringLiteral("hello")));
table.Clear(); table.Clear();
EXPECT_TRUE(table.isEmpty()); EXPECT_TRUE(table.isEmpty());
EXPECT_EQ(table.Count(), 0); EXPECT_EQ(table.Count(), 0);
+4 -3
View File
@@ -14,7 +14,8 @@ TEST(OpenGLRenderer, DownloadFromTextureWithoutCurrentContext)
QOpenGLContext context; QOpenGLContext context;
if (!context.create()) { if (!context.create()) {
GTEST_SKIP() << "Skipping OpenGL test because no context can be created"; GTEST_SKIP()
<< "Skipping OpenGL test because no context can be created";
} }
ASSERT_EQ(QOpenGLContext::currentContext(), nullptr); ASSERT_EQ(QOpenGLContext::currentContext(), nullptr);
@@ -26,8 +27,8 @@ TEST(OpenGLRenderer, DownloadFromTextureWithoutCurrentContext)
olive::VideoParams::kInterlaceNone, 1); olive::VideoParams::kInterlaceNone, 1);
unsigned char buffer[4 * 4 * 4] = {}; unsigned char buffer[4 * 4 * 4] = {};
renderer.DownloadFromTexture(QVariant::fromValue<GLuint>(0), params, renderer.DownloadFromTexture(QVariant::fromValue<GLuint>(0), params, buffer,
buffer, 4 * 4); 4 * 4);
EXPECT_EQ(QOpenGLContext::currentContext(), nullptr); EXPECT_EQ(QOpenGLContext::currentContext(), nullptr);
} }
+212 -195
View File
@@ -20,219 +20,236 @@ using namespace olive;
using namespace olive::core; using namespace olive::core;
// Test helper to create AVFrame with specific format // Test helper to create AVFrame with specific format
static AVFramePtr CreateTestFrame(int width, int height, AVPixelFormat fmt, uint32_t fill_color = 0xFF804020) { static AVFramePtr CreateTestFrame(int width, int height, AVPixelFormat fmt,
AVFramePtr frame = CreateAVFramePtr(); uint32_t fill_color = 0xFF804020)
frame->width = width; {
frame->height = height; AVFramePtr frame = CreateAVFramePtr();
frame->format = fmt; frame->width = width;
frame->height = height;
if (av_frame_get_buffer(frame.get(), 0) < 0) { frame->format = fmt;
return nullptr;
} if (av_frame_get_buffer(frame.get(), 0) < 0) {
return nullptr;
if (av_frame_make_writable(frame.get()) < 0) { }
return nullptr;
} if (av_frame_make_writable(frame.get()) < 0) {
return nullptr;
// Fill with test pattern }
uint8_t r = (fill_color >> 24) & 0xFF;
uint8_t g = (fill_color >> 16) & 0xFF; // Fill with test pattern
uint8_t b = (fill_color >> 8) & 0xFF; uint8_t r = (fill_color >> 24) & 0xFF;
uint8_t a = fill_color & 0xFF; uint8_t g = (fill_color >> 16) & 0xFF;
uint8_t b = (fill_color >> 8) & 0xFF;
if (fmt == AV_PIX_FMT_RGBA) { uint8_t a = fill_color & 0xFF;
for (int y = 0; y < height; ++y) {
uint8_t *row = frame->data[0] + y * frame->linesize[0]; if (fmt == AV_PIX_FMT_RGBA) {
for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) {
row[x * 4 + 0] = r; uint8_t *row = frame->data[0] + y * frame->linesize[0];
row[x * 4 + 1] = g; for (int x = 0; x < width; ++x) {
row[x * 4 + 2] = b; row[x * 4 + 0] = r;
row[x * 4 + 3] = a; row[x * 4 + 1] = g;
} row[x * 4 + 2] = b;
} row[x * 4 + 3] = a;
} else if (fmt == AV_PIX_FMT_RGBA64) { }
uint16_t r16 = (r << 8) | r; }
uint16_t g16 = (g << 8) | g; } else if (fmt == AV_PIX_FMT_RGBA64) {
uint16_t b16 = (b << 8) | b; uint16_t r16 = (r << 8) | r;
uint16_t a16 = (a << 8) | a; uint16_t g16 = (g << 8) | g;
for (int y = 0; y < height; ++y) { uint16_t b16 = (b << 8) | b;
uint16_t *row = reinterpret_cast<uint16_t*>(frame->data[0] + y * frame->linesize[0]); uint16_t a16 = (a << 8) | a;
for (int x = 0; x < width; ++x) { for (int y = 0; y < height; ++y) {
row[x * 4 + 0] = r16; uint16_t *row = reinterpret_cast<uint16_t *>(
row[x * 4 + 1] = g16; frame->data[0] + y * frame->linesize[0]);
row[x * 4 + 2] = b16; for (int x = 0; x < width; ++x) {
row[x * 4 + 3] = a16; row[x * 4 + 0] = r16;
} row[x * 4 + 1] = g16;
} row[x * 4 + 2] = b16;
} row[x * 4 + 3] = a16;
}
return frame; }
}
return frame;
} }
// Test U8 to U16 conversion // Test U8 to U16 conversion
TEST(FormatConversion, U8ToU16) { TEST(FormatConversion, U8ToU16)
const int width = 10; {
const int height = 10; const int width = 10;
const uint32_t test_color = 0xFF804020; // ARGB: A=255, R=128, G=64, B=32 const int height = 10;
const uint32_t test_color = 0xFF804020; // ARGB: A=255, R=128, G=64, B=32
// Create U8 frame
AVFramePtr u8_frame = CreateTestFrame(width, height, AV_PIX_FMT_RGBA, test_color); // Create U8 frame
ASSERT_NE(u8_frame, nullptr); AVFramePtr u8_frame =
CreateTestFrame(width, height, AV_PIX_FMT_RGBA, test_color);
// Verify U8 values ASSERT_NE(u8_frame, nullptr);
uint8_t *first_pixel_u8 = u8_frame->data[0];
EXPECT_EQ(first_pixel_u8[0], 0xFF); // R // Verify U8 values
EXPECT_EQ(first_pixel_u8[1], 0x80); // G uint8_t *first_pixel_u8 = u8_frame->data[0];
EXPECT_EQ(first_pixel_u8[2], 0x40); // B EXPECT_EQ(first_pixel_u8[0], 0xFF); // R
EXPECT_EQ(first_pixel_u8[3], 0x20); // A EXPECT_EQ(first_pixel_u8[1], 0x80); // G
EXPECT_EQ(first_pixel_u8[2], 0x40); // B
// Create U16 frame EXPECT_EQ(first_pixel_u8[3], 0x20); // A
AVFramePtr u16_frame = CreateTestFrame(width, height, AV_PIX_FMT_RGBA64, test_color);
ASSERT_NE(u16_frame, nullptr); // Create U16 frame
AVFramePtr u16_frame =
// Verify U16 values (should be U8 value repeated: 0xFF -> 0xFFFF, 0x80 -> 0x8080) CreateTestFrame(width, height, AV_PIX_FMT_RGBA64, test_color);
uint16_t *first_pixel_u16 = reinterpret_cast<uint16_t*>(u16_frame->data[0]); ASSERT_NE(u16_frame, nullptr);
EXPECT_EQ(first_pixel_u16[0], 0xFFFF); // R
EXPECT_EQ(first_pixel_u16[1], 0x8080); // G // Verify U16 values (should be U8 value repeated: 0xFF -> 0xFFFF, 0x80 -> 0x8080)
EXPECT_EQ(first_pixel_u16[2], 0x4040); // B uint16_t *first_pixel_u16 =
EXPECT_EQ(first_pixel_u16[3], 0x2020); // A reinterpret_cast<uint16_t *>(u16_frame->data[0]);
EXPECT_EQ(first_pixel_u16[0], 0xFFFF); // R
EXPECT_EQ(first_pixel_u16[1], 0x8080); // G
EXPECT_EQ(first_pixel_u16[2], 0x4040); // B
EXPECT_EQ(first_pixel_u16[3], 0x2020); // A
} }
// Test FFmpeg sws_scale for U16 to U8 conversion // Test FFmpeg sws_scale for U16 to U8 conversion
TEST(FormatConversion, FFmpegU16ToU8) { TEST(FormatConversion, FFmpegU16ToU8)
const int width = 10; {
const int height = 10; const int width = 10;
const uint32_t test_color = 0xFF804020; const int height = 10;
const uint32_t test_color = 0xFF804020;
// Create U16 frame
AVFramePtr u16_frame = CreateTestFrame(width, height, AV_PIX_FMT_RGBA64, test_color); // Create U16 frame
ASSERT_NE(u16_frame, nullptr); AVFramePtr u16_frame =
CreateTestFrame(width, height, AV_PIX_FMT_RGBA64, test_color);
// Create destination U8 frame ASSERT_NE(u16_frame, nullptr);
AVFramePtr u8_frame = CreateTestFrame(width, height, AV_PIX_FMT_RGBA, 0);
ASSERT_NE(u8_frame, nullptr); // Create destination U8 frame
AVFramePtr u8_frame = CreateTestFrame(width, height, AV_PIX_FMT_RGBA, 0);
// Use sws_scale to convert ASSERT_NE(u8_frame, nullptr);
SwsContext *sws_ctx = sws_getContext(
width, height, AV_PIX_FMT_RGBA64, // Use sws_scale to convert
width, height, AV_PIX_FMT_RGBA, SwsContext *sws_ctx = sws_getContext(width, height, AV_PIX_FMT_RGBA64,
SWS_POINT, nullptr, nullptr, nullptr); width, height, AV_PIX_FMT_RGBA,
ASSERT_NE(sws_ctx, nullptr); SWS_POINT, nullptr, nullptr, nullptr);
ASSERT_NE(sws_ctx, nullptr);
sws_scale(sws_ctx, u16_frame->data, u16_frame->linesize, 0, height,
u8_frame->data, u8_frame->linesize); sws_scale(sws_ctx, u16_frame->data, u16_frame->linesize, 0, height,
sws_freeContext(sws_ctx); u8_frame->data, u8_frame->linesize);
sws_freeContext(sws_ctx);
// Verify conversion (U16 0xFFFF -> U8 0xFF, 0x8080 -> ~0x80, etc.)
// Note: FFmpeg sws_scale has rounding offset, so values may be off by 1 // Verify conversion (U16 0xFFFF -> U8 0xFF, 0x8080 -> ~0x80, etc.)
uint8_t *first_pixel = u8_frame->data[0]; // Note: FFmpeg sws_scale has rounding offset, so values may be off by 1
EXPECT_NEAR(first_pixel[0], 0xFF, 1); // R (255 vs 255) uint8_t *first_pixel = u8_frame->data[0];
EXPECT_NEAR(first_pixel[1], 0x80, 1); // G (128 vs 129) EXPECT_NEAR(first_pixel[0], 0xFF, 1); // R (255 vs 255)
EXPECT_NEAR(first_pixel[2], 0x40, 1); // B (64 vs 64) EXPECT_NEAR(first_pixel[1], 0x80, 1); // G (128 vs 129)
EXPECT_NEAR(first_pixel[3], 0x20, 1); // A (32 vs 32) EXPECT_NEAR(first_pixel[2], 0x40, 1); // B (64 vs 64)
EXPECT_NEAR(first_pixel[3], 0x20, 1); // A (32 vs 32)
} }
// Test VideoParams to AVPixelFormat mapping // Test VideoParams to AVPixelFormat mapping
TEST(FormatConversion, VideoParamsToAVFormat) { TEST(FormatConversion, VideoParamsToAVFormat)
// U8 RGBA {
VideoParams u8_rgba(320, 240, PixelFormat::U8, 4); // U8 RGBA
AVPixelFormat fmt_u8_rgba = FFmpegUtils::GetFFmpegPixelFormat(u8_rgba.format(), u8_rgba.channel_count()); VideoParams u8_rgba(320, 240, PixelFormat::U8, 4);
EXPECT_EQ(fmt_u8_rgba, AV_PIX_FMT_RGBA); AVPixelFormat fmt_u8_rgba = FFmpegUtils::GetFFmpegPixelFormat(
u8_rgba.format(), u8_rgba.channel_count());
// U16 RGBA EXPECT_EQ(fmt_u8_rgba, AV_PIX_FMT_RGBA);
VideoParams u16_rgba(320, 240, PixelFormat::U16, 4);
AVPixelFormat fmt_u16_rgba = FFmpegUtils::GetFFmpegPixelFormat(u16_rgba.format(), u16_rgba.channel_count()); // U16 RGBA
EXPECT_EQ(fmt_u16_rgba, AV_PIX_FMT_RGBA64); VideoParams u16_rgba(320, 240, PixelFormat::U16, 4);
AVPixelFormat fmt_u16_rgba = FFmpegUtils::GetFFmpegPixelFormat(
// U8 RGB u16_rgba.format(), u16_rgba.channel_count());
VideoParams u8_rgb(320, 240, PixelFormat::U8, 3); EXPECT_EQ(fmt_u16_rgba, AV_PIX_FMT_RGBA64);
AVPixelFormat fmt_u8_rgb = FFmpegUtils::GetFFmpegPixelFormat(u8_rgb.format(), u8_rgb.channel_count());
EXPECT_EQ(fmt_u8_rgb, AV_PIX_FMT_RGB24); // U8 RGB
VideoParams u8_rgb(320, 240, PixelFormat::U8, 3);
// U16 RGB AVPixelFormat fmt_u8_rgb = FFmpegUtils::GetFFmpegPixelFormat(
VideoParams u16_rgb(320, 240, PixelFormat::U16, 3); u8_rgb.format(), u8_rgb.channel_count());
AVPixelFormat fmt_u16_rgb = FFmpegUtils::GetFFmpegPixelFormat(u16_rgb.format(), u16_rgb.channel_count()); EXPECT_EQ(fmt_u8_rgb, AV_PIX_FMT_RGB24);
EXPECT_EQ(fmt_u16_rgb, AV_PIX_FMT_RGB48);
// U16 RGB
VideoParams u16_rgb(320, 240, PixelFormat::U16, 3);
AVPixelFormat fmt_u16_rgb = FFmpegUtils::GetFFmpegPixelFormat(
u16_rgb.format(), u16_rgb.channel_count());
EXPECT_EQ(fmt_u16_rgb, AV_PIX_FMT_RGB48);
} }
// Test row bytes calculation // Test row bytes calculation
TEST(FormatConversion, RowBytes) { TEST(FormatConversion, RowBytes)
const int width = 320; {
const int width = 320;
// U8 RGBA: 4 bytes per pixel
EXPECT_EQ(width * 4, 1280); // U8 RGBA: 4 bytes per pixel
EXPECT_EQ(width * 4, 1280);
// U16 RGBA: 8 bytes per pixel
EXPECT_EQ(width * 8, 2560); // U16 RGBA: 8 bytes per pixel
EXPECT_EQ(width * 8, 2560);
// U8 RGB: 3 bytes per pixel
EXPECT_EQ(width * 3, 960); // U8 RGB: 3 bytes per pixel
EXPECT_EQ(width * 3, 960);
// U16 RGB: 6 bytes per pixel
EXPECT_EQ(width * 6, 1920); // U16 RGB: 6 bytes per pixel
EXPECT_EQ(width * 6, 1920);
} }
// Test that linesize may differ from width * bpp due to alignment // Test that linesize may differ from width * bpp due to alignment
TEST(FormatConversion, LinesizeAlignment) { TEST(FormatConversion, LinesizeAlignment)
const int width = 10; {
const int height = 10; const int width = 10;
const int height = 10;
AVFramePtr frame = CreateAVFramePtr();
frame->width = width; AVFramePtr frame = CreateAVFramePtr();
frame->height = height; frame->width = width;
frame->format = AV_PIX_FMT_RGBA; frame->height = height;
frame->format = AV_PIX_FMT_RGBA;
ASSERT_EQ(av_frame_get_buffer(frame.get(), 0), 0);
ASSERT_EQ(av_frame_get_buffer(frame.get(), 0), 0);
// linesize[0] should be at least width * 4
EXPECT_GE(frame->linesize[0], width * 4); // linesize[0] should be at least width * 4
EXPECT_GE(frame->linesize[0], width * 4);
// linesize may be larger due to alignment (typically 32-byte aligned)
qDebug() << "Width:" << width << "Expected bytes:" << width * 4 // linesize may be larger due to alignment (typically 32-byte aligned)
<< "Actual linesize:" << frame->linesize[0]; qDebug() << "Width:" << width << "Expected bytes:" << width * 4
<< "Actual linesize:" << frame->linesize[0];
} }
// Test loading actual image file // Test loading actual image file
TEST(FormatConversion, LoadImageFile) { TEST(FormatConversion, LoadImageFile)
// Load the test image {
QString img_path = QStringLiteral("%1/../tests/img.png").arg(QDir::currentPath()); // Load the test image
QString img_path =
AVFramePtr frame = CreateAVFramePtr(); QStringLiteral("%1/../tests/img.png").arg(QDir::currentPath());
// Just create a simple test frame instead of loading an image
frame->width = 1920; AVFramePtr frame = CreateAVFramePtr();
frame->height = 1080; // Just create a simple test frame instead of loading an image
frame->format = AV_PIX_FMT_RGBA; frame->width = 1920;
if (av_frame_get_buffer(frame.get(), 0) < 0) { frame->height = 1080;
return; frame->format = AV_PIX_FMT_RGBA;
} if (av_frame_get_buffer(frame.get(), 0) < 0) {
// Fill with orange color (sunrise sky) return;
for (int y = 0; y < frame->height; ++y) { }
uint8_t *row = frame->data[0] + y * frame->linesize[0]; // Fill with orange color (sunrise sky)
uint8_t r = 255; for (int y = 0; y < frame->height; ++y) {
uint8_t g = 128 + (y * 127) / frame->height; // Gradient from 128 to 255 uint8_t *row = frame->data[0] + y * frame->linesize[0];
uint8_t b = 64; uint8_t r = 255;
uint8_t a = 255; uint8_t g = 128 + (y * 127) / frame->height; // Gradient from 128 to 255
for (int x = 0; x < frame->width; ++x) { uint8_t b = 64;
row[x * 4 + 0] = r; uint8_t a = 255;
row[x * 4 + 1] = g; for (int x = 0; x < frame->width; ++x) {
row[x * 4 + 2] = b; row[x * 4 + 0] = r;
row[x * 4 + 3] = a; row[x * 4 + 1] = g;
} row[x * 4 + 2] = b;
} row[x * 4 + 3] = a;
ASSERT_NE(frame->data[0], nullptr) << "Failed to create test frame"; }
}
EXPECT_EQ(frame->width, 1920); ASSERT_NE(frame->data[0], nullptr) << "Failed to create test frame";
EXPECT_EQ(frame->height, 1080);
EXPECT_EQ(frame->width, 1920);
// Check first pixel (top-left corner of the sunrise image) EXPECT_EQ(frame->height, 1080);
// Based on the image, it should have some orange/pink color in the sky area
uint8_t *first_pixel = frame->data[0]; // Check first pixel (top-left corner of the sunrise image)
qDebug() << "First pixel RGBA:" << first_pixel[0] << first_pixel[1] // Based on the image, it should have some orange/pink color in the sky area
<< first_pixel[2] << first_pixel[3]; uint8_t *first_pixel = frame->data[0];
qDebug() << "First pixel RGBA:" << first_pixel[0] << first_pixel[1]
// The image is RGB, so we expect 3 channels << first_pixel[2] << first_pixel[3];
// First pixel should be non-black (sky area)
EXPECT_GT(first_pixel[0] + first_pixel[1] + first_pixel[2], 0); // The image is RGB, so we expect 3 channels
// First pixel should be non-black (sky area)
EXPECT_GT(first_pixel[0] + first_pixel[1] + first_pixel[2], 0);
} }
// Tests are registered with gtest, no main needed // Tests are registered with gtest, no main needed
+2 -1
View File
@@ -15,7 +15,8 @@ extern "C" {
#include "render/texture.h" #include "render/texture.h"
#include "render/videoparams.h" #include "render/videoparams.h"
namespace { namespace
{
olive::TexturePtr CreateSolidTexture(const olive::VideoParams &params) olive::TexturePtr CreateSolidTexture(const olive::VideoParams &params)
{ {
+129 -109
View File
@@ -27,22 +27,26 @@ extern "C" {
#include "render/texture.h" #include "render/texture.h"
#include "render/videoparams.h" #include "render/videoparams.h"
namespace olive { namespace olive
namespace plugin { {
namespace test { namespace plugin
{
namespace test
{
namespace { namespace
{
// Helper to create a test texture with solid color // Helper to create a test texture with solid color
// For U8: fill_value is 0-255 // For U8: fill_value is 0-255
// For U16: fill_value is 0-65535 // For U16: fill_value is 0-65535
// For Float: fill_value is 0.0-1.0 mapped to bytes // For Float: fill_value is 0.0-1.0 mapped to bytes
template<typename T> template <typename T>
TexturePtr CreateSolidTextureT(const VideoParams &params, T fill_value) TexturePtr CreateSolidTextureT(const VideoParams &params, T fill_value)
{ {
AVFramePtr frame = CreateAVFramePtr(); AVFramePtr frame = CreateAVFramePtr();
frame->format = FFmpegUtils::GetFFmpegPixelFormat( frame->format = FFmpegUtils::GetFFmpegPixelFormat(params.format(),
params.format(), params.channel_count()); params.channel_count());
frame->width = params.width(); frame->width = params.width();
frame->height = params.height(); frame->height = params.height();
if (frame->format == AV_PIX_FMT_NONE) { if (frame->format == AV_PIX_FMT_NONE) {
@@ -57,7 +61,7 @@ TexturePtr CreateSolidTextureT(const VideoParams &params, T fill_value)
const int linesize = frame->linesize[0]; const int linesize = frame->linesize[0];
for (int y = 0; y < frame->height; ++y) { for (int y = 0; y < frame->height; ++y) {
T *row = reinterpret_cast<T*>(frame->data[0] + y * linesize); T *row = reinterpret_cast<T *>(frame->data[0] + y * linesize);
for (int x = 0; x < frame->width * params.channel_count(); ++x) { for (int x = 0; x < frame->width * params.channel_count(); ++x) {
row[x] = fill_value; row[x] = fill_value;
} }
@@ -68,17 +72,21 @@ TexturePtr CreateSolidTextureT(const VideoParams &params, T fill_value)
return texture; return texture;
} }
TexturePtr CreateSolidTexture(const VideoParams &params, uint32_t fill_value = 0x7f) TexturePtr CreateSolidTexture(const VideoParams &params,
uint32_t fill_value = 0x7f)
{ {
// Choose type based on pixel format // Choose type based on pixel format
switch (params.format()) { switch (params.format()) {
case core::PixelFormat::U8: case core::PixelFormat::U8:
return CreateSolidTextureT<uint8_t>(params, static_cast<uint8_t>(fill_value)); return CreateSolidTextureT<uint8_t>(params,
static_cast<uint8_t>(fill_value));
case core::PixelFormat::U16: case core::PixelFormat::U16:
return CreateSolidTextureT<uint16_t>(params, static_cast<uint16_t>(fill_value)); return CreateSolidTextureT<uint16_t>(params,
static_cast<uint16_t>(fill_value));
case core::PixelFormat::F16: case core::PixelFormat::F16:
case core::PixelFormat::F32: case core::PixelFormat::F32:
return CreateSolidTextureT<float>(params, static_cast<float>(fill_value) / 255.0f); return CreateSolidTextureT<float>(
params, static_cast<float>(fill_value) / 255.0f);
default: default:
return nullptr; return nullptr;
} }
@@ -87,12 +95,12 @@ TexturePtr CreateSolidTexture(const VideoParams &params, uint32_t fill_value = 0
// Helper to create a gradient texture // Helper to create a gradient texture
// For U8: gradient is 0-255 per byte // For U8: gradient is 0-255 per byte
// For Float: gradient is 0.0-1.0 per component // For Float: gradient is 0.0-1.0 per component
template<typename T> template <typename T>
TexturePtr CreateGradientTextureT(const VideoParams &params, float scale) TexturePtr CreateGradientTextureT(const VideoParams &params, float scale)
{ {
AVFramePtr frame = CreateAVFramePtr(); AVFramePtr frame = CreateAVFramePtr();
frame->format = FFmpegUtils::GetFFmpegPixelFormat( frame->format = FFmpegUtils::GetFFmpegPixelFormat(params.format(),
params.format(), params.channel_count()); params.channel_count());
frame->width = params.width(); frame->width = params.width();
frame->height = params.height(); frame->height = params.height();
if (frame->format == AV_PIX_FMT_NONE) { if (frame->format == AV_PIX_FMT_NONE) {
@@ -107,7 +115,7 @@ TexturePtr CreateGradientTextureT(const VideoParams &params, float scale)
const int linesize = frame->linesize[0]; const int linesize = frame->linesize[0];
for (int y = 0; y < frame->height; ++y) { for (int y = 0; y < frame->height; ++y) {
T *row = reinterpret_cast<T*>(frame->data[0] + y * linesize); T *row = reinterpret_cast<T *>(frame->data[0] + y * linesize);
T value = static_cast<T>((y * scale) / frame->height); T value = static_cast<T>((y * scale) / frame->height);
for (int x = 0; x < frame->width * params.channel_count(); ++x) { for (int x = 0; x < frame->width * params.channel_count(); ++x) {
row[x] = value; row[x] = value;
@@ -135,17 +143,16 @@ TexturePtr CreateGradientTexture(const VideoParams &params)
} }
// Helper function to find and render a plugin // Helper function to find and render a plugin
bool RenderPlugin(const std::string &plugin_id, bool RenderPlugin(const std::string &plugin_id, const VideoParams &params,
const VideoParams &params, const NodeValueRow &inputs, bool verbose = false)
const NodeValueRow &inputs,
bool verbose = false)
{ {
auto *cache = OFX::Host::PluginCache::getPluginCache(); auto *cache = OFX::Host::PluginCache::getPluginCache();
if (!cache) { if (!cache) {
if (verbose) std::cerr << "Plugin cache not available" << std::endl; if (verbose)
std::cerr << "Plugin cache not available" << std::endl;
return false; return false;
} }
OFX::Host::Plugin *found = nullptr; OFX::Host::Plugin *found = nullptr;
for (auto *plug : cache->getPlugins()) { for (auto *plug : cache->getPlugins()) {
if (plug && plug->getIdentifier() == plugin_id) { if (plug && plug->getIdentifier() == plugin_id) {
@@ -153,66 +160,72 @@ bool RenderPlugin(const std::string &plugin_id,
break; break;
} }
} }
if (!found) { if (!found) {
if (verbose) std::cerr << "Plugin not found: " << plugin_id << std::endl; if (verbose)
std::cerr << "Plugin not found: " << plugin_id << std::endl;
return false; return false;
} }
auto *image_effect = dynamic_cast<OFX::Host::ImageEffect::ImageEffectPlugin *>(found); auto *image_effect =
dynamic_cast<OFX::Host::ImageEffect::ImageEffectPlugin *>(found);
if (!image_effect) { if (!image_effect) {
if (verbose) std::cerr << "Not an image effect plugin" << std::endl; if (verbose)
std::cerr << "Not an image effect plugin" << std::endl;
return false; return false;
} }
const auto &contexts = image_effect->getContexts(); const auto &contexts = image_effect->getContexts();
std::string context = kOfxImageEffectContextFilter; std::string context = kOfxImageEffectContextFilter;
if (!contexts.empty() && if (!contexts.empty() &&
contexts.find(kOfxImageEffectContextFilter) == contexts.end()) { contexts.find(kOfxImageEffectContextFilter) == contexts.end()) {
context = *contexts.begin(); context = *contexts.begin();
} }
OFX::Host::ImageEffect::Instance *instance = OFX::Host::ImageEffect::Instance *instance =
image_effect->createInstance(context, nullptr); image_effect->createInstance(context, nullptr);
if (!instance) { if (!instance) {
if (verbose) std::cerr << "Failed to create instance" << std::endl; if (verbose)
std::cerr << "Failed to create instance" << std::endl;
return false; return false;
} }
auto *olive_instance = dynamic_cast<OlivePluginInstance *>(instance); auto *olive_instance = dynamic_cast<OlivePluginInstance *>(instance);
if (!olive_instance) { if (!olive_instance) {
if (verbose) std::cerr << "Not an OlivePluginInstance" << std::endl; if (verbose)
std::cerr << "Not an OlivePluginInstance" << std::endl;
return false; return false;
} }
olive_instance->setVideoParam(params); olive_instance->setVideoParam(params);
PluginJob job(instance, nullptr, inputs); PluginJob job(instance, nullptr, inputs);
TexturePtr output = std::make_shared<Texture>(params); TexturePtr output = std::make_shared<Texture>(params);
PluginRenderer renderer(nullptr); PluginRenderer renderer(nullptr);
renderer.RenderPlugin(nullptr, job, output, params, true, false); renderer.RenderPlugin(nullptr, job, output, params, true, false);
bool has_frame = output->frame() != nullptr; bool has_frame = output->frame() != nullptr;
if (!has_frame && verbose) { if (!has_frame && verbose) {
std::cerr << "Render produced no output frame" << std::endl; std::cerr << "Render produced no output frame" << std::endl;
} }
return has_frame; return has_frame;
} }
// Skip check function // Skip check function
bool ShouldSkipTest() { bool ShouldSkipTest()
{
const char *itest = std::getenv("OAK_OFX_ITEST"); const char *itest = std::getenv("OAK_OFX_ITEST");
if (!itest || std::string(itest) != "1") { if (!itest || std::string(itest) != "1") {
return true; return true;
} }
const char *path = std::getenv("OAK_OFX_PLUGIN_PATH"); const char *path = std::getenv("OAK_OFX_PLUGIN_PATH");
if (!path || std::string(path).empty()) { if (!path || std::string(path).empty()) {
return true; return true;
} }
static bool plugins_loaded = false; static bool plugins_loaded = false;
if (!plugins_loaded) { if (!plugins_loaded) {
QString raw = QString::fromUtf8(path); QString raw = QString::fromUtf8(path);
@@ -223,7 +236,7 @@ bool ShouldSkipTest() {
} }
plugins_loaded = true; plugins_loaded = true;
} }
return false; return false;
} }
@@ -238,16 +251,16 @@ TEST(PluginMisc, MirrorHorizontal)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
// Mirror plugin typically works with 8-bit // Mirror plugin typically works with 8-bit
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateGradientTexture(params); TexturePtr input = CreateGradientTexture(params);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
bool result = RenderPlugin("net.sf.openfx.Mirror", params, row, true); bool result = RenderPlugin("net.sf.openfx.Mirror", params, row, true);
EXPECT_TRUE(result) << "Mirror plugin should produce output"; EXPECT_TRUE(result) << "Mirror plugin should produce output";
} }
@@ -261,16 +274,17 @@ TEST(PluginMisc, TransformTranslate)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateSolidTexture(params, 0x80); TexturePtr input = CreateSolidTexture(params, 0x80);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
bool result = RenderPlugin("net.sf.openfx.TransformPlugin", params, row, true); bool result =
RenderPlugin("net.sf.openfx.TransformPlugin", params, row, true);
EXPECT_TRUE(result) << "Transform plugin should produce output"; EXPECT_TRUE(result) << "Transform plugin should produce output";
} }
@@ -283,16 +297,17 @@ TEST(PluginMisc, ColorCorrect)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateSolidTexture(params, 0x80); TexturePtr input = CreateSolidTexture(params, 0x80);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
bool result = RenderPlugin("net.sf.openfx.ColorCorrectPlugin", params, row, true); bool result =
RenderPlugin("net.sf.openfx.ColorCorrectPlugin", params, row, true);
EXPECT_TRUE(result) << "ColorCorrect plugin should produce output"; EXPECT_TRUE(result) << "ColorCorrect plugin should produce output";
} }
@@ -301,16 +316,17 @@ TEST(PluginMisc, Saturation)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateSolidTexture(params, 0x80); TexturePtr input = CreateSolidTexture(params, 0x80);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
bool result = RenderPlugin("net.sf.openfx.SaturationPlugin", params, row, true); bool result =
RenderPlugin("net.sf.openfx.SaturationPlugin", params, row, true);
EXPECT_TRUE(result) << "Saturation plugin should produce output"; EXPECT_TRUE(result) << "Saturation plugin should produce output";
} }
@@ -323,15 +339,15 @@ TEST(PluginMisc, GaussianBlur)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateSolidTexture(params, 0x80); TexturePtr input = CreateSolidTexture(params, 0x80);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
// Use CImgBlur from the available plugin list // Use CImgBlur from the available plugin list
bool result = RenderPlugin("net.sf.cimg.CImgBlur", params, row, true); bool result = RenderPlugin("net.sf.cimg.CImgBlur", params, row, true);
EXPECT_TRUE(result) << "GaussianBlur plugin should produce output"; EXPECT_TRUE(result) << "GaussianBlur plugin should produce output";
@@ -346,15 +362,15 @@ TEST(PluginMisc, Crop)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateSolidTexture(params, 0x80); TexturePtr input = CreateSolidTexture(params, 0x80);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
bool result = RenderPlugin("net.sf.openfx.CropPlugin", params, row, true); bool result = RenderPlugin("net.sf.openfx.CropPlugin", params, row, true);
EXPECT_TRUE(result) << "Crop plugin should produce output"; EXPECT_TRUE(result) << "Crop plugin should produce output";
} }
@@ -364,15 +380,15 @@ TEST(PluginMisc, Grade)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateSolidTexture(params, 0x80); TexturePtr input = CreateSolidTexture(params, 0x80);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
bool result = RenderPlugin("net.sf.openfx.GradePlugin", params, row, true); bool result = RenderPlugin("net.sf.openfx.GradePlugin", params, row, true);
EXPECT_TRUE(result) << "Grade plugin should produce output"; EXPECT_TRUE(result) << "Grade plugin should produce output";
} }
@@ -386,16 +402,17 @@ TEST(PluginMisc, NonExistentPlugin)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateSolidTexture(params, 0x80); TexturePtr input = CreateSolidTexture(params, 0x80);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
bool result = RenderPlugin("net.sf.openfx.NonExistentPlugin", params, row, true); bool result =
RenderPlugin("net.sf.openfx.NonExistentPlugin", params, row, true);
EXPECT_FALSE(result) << "Non-existent plugin should fail gracefully"; EXPECT_FALSE(result) << "Non-existent plugin should fail gracefully";
} }
@@ -408,15 +425,15 @@ TEST(PluginMisc, CImgSharpen)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateSolidTexture(params, 0x80); TexturePtr input = CreateSolidTexture(params, 0x80);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
bool result = RenderPlugin("net.sf.cimg.CImgSharpen", params, row, true); bool result = RenderPlugin("net.sf.cimg.CImgSharpen", params, row, true);
EXPECT_TRUE(result) << "CImgSharpen plugin should produce output"; EXPECT_TRUE(result) << "CImgSharpen plugin should produce output";
} }
@@ -426,15 +443,15 @@ TEST(PluginMisc, CImgDenoise)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateSolidTexture(params, 0x80); TexturePtr input = CreateSolidTexture(params, 0x80);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
bool result = RenderPlugin("net.sf.cimg.CImgDenoise", params, row, true); bool result = RenderPlugin("net.sf.cimg.CImgDenoise", params, row, true);
EXPECT_TRUE(result) << "CImgDenoise plugin should produce output"; EXPECT_TRUE(result) << "CImgDenoise plugin should produce output";
} }
@@ -444,15 +461,15 @@ TEST(PluginMisc, CImgBilateral)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateSolidTexture(params, 0x80); TexturePtr input = CreateSolidTexture(params, 0x80);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
bool result = RenderPlugin("net.sf.cimg.CImgBilateral", params, row, true); bool result = RenderPlugin("net.sf.cimg.CImgBilateral", params, row, true);
EXPECT_TRUE(result) << "CImgBilateral plugin should produce output"; EXPECT_TRUE(result) << "CImgBilateral plugin should produce output";
} }
@@ -466,17 +483,16 @@ TEST(PluginMisc, MergeOver)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateSolidTexture(params, 0x80); TexturePtr input = CreateSolidTexture(params, 0x80);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
row.insert(QStringLiteral("Bg"), row.insert(QStringLiteral("Bg"), NodeValue(NodeValue::kTexture, input));
NodeValue(NodeValue::kTexture, input));
bool result = RenderPlugin("net.sf.openfx.MergePlugin", params, row, true); bool result = RenderPlugin("net.sf.openfx.MergePlugin", params, row, true);
EXPECT_TRUE(result) << "Merge plugin should produce output"; EXPECT_TRUE(result) << "Merge plugin should produce output";
} }
@@ -490,15 +506,15 @@ TEST(PluginMisc, Keyer)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateSolidTexture(params, 0x80); TexturePtr input = CreateSolidTexture(params, 0x80);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
bool result = RenderPlugin("net.sf.openfx.KeyerPlugin", params, row, true); bool result = RenderPlugin("net.sf.openfx.KeyerPlugin", params, row, true);
EXPECT_TRUE(result) << "Keyer plugin should produce output"; EXPECT_TRUE(result) << "Keyer plugin should produce output";
} }
@@ -512,16 +528,17 @@ TEST(PluginMisc, CornerPin)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateSolidTexture(params, 0x80); TexturePtr input = CreateSolidTexture(params, 0x80);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
bool result = RenderPlugin("net.sf.openfx.CornerPinPlugin", params, row, true); bool result =
RenderPlugin("net.sf.openfx.CornerPinPlugin", params, row, true);
EXPECT_TRUE(result) << "CornerPin plugin should produce output"; EXPECT_TRUE(result) << "CornerPin plugin should produce output";
} }
@@ -530,16 +547,17 @@ TEST(PluginMisc, LensDistortion)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateSolidTexture(params, 0x80); TexturePtr input = CreateSolidTexture(params, 0x80);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
bool result = RenderPlugin("net.sf.openfx.LensDistortion", params, row, true); bool result =
RenderPlugin("net.sf.openfx.LensDistortion", params, row, true);
EXPECT_TRUE(result) << "LensDistortion plugin should produce output"; EXPECT_TRUE(result) << "LensDistortion plugin should produce output";
} }
@@ -552,15 +570,15 @@ TEST(PluginMisc, Invert)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateSolidTexture(params, 0x80); TexturePtr input = CreateSolidTexture(params, 0x80);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
bool result = RenderPlugin("net.sf.openfx.Invert", params, row, true); bool result = RenderPlugin("net.sf.openfx.Invert", params, row, true);
EXPECT_TRUE(result) << "Invert plugin should produce output"; EXPECT_TRUE(result) << "Invert plugin should produce output";
} }
@@ -570,15 +588,15 @@ TEST(PluginMisc, Gamma)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr input = CreateSolidTexture(params, 0x80); TexturePtr input = CreateSolidTexture(params, 0x80);
ASSERT_NE(input, nullptr); ASSERT_NE(input, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, input)); NodeValue(NodeValue::kTexture, input));
bool result = RenderPlugin("net.sf.openfx.GammaPlugin", params, row, true); bool result = RenderPlugin("net.sf.openfx.GammaPlugin", params, row, true);
EXPECT_TRUE(result) << "Gamma plugin should produce output"; EXPECT_TRUE(result) << "Gamma plugin should produce output";
} }
@@ -592,12 +610,12 @@ TEST(PluginMisc, ListAvailablePlugins)
if (ShouldSkipTest()) { if (ShouldSkipTest()) {
GTEST_SKIP() << "OFX integration test not enabled"; GTEST_SKIP() << "OFX integration test not enabled";
} }
auto *cache = OFX::Host::PluginCache::getPluginCache(); auto *cache = OFX::Host::PluginCache::getPluginCache();
if (!cache) { if (!cache) {
GTEST_SKIP() << "Plugin cache not available"; GTEST_SKIP() << "Plugin cache not available";
} }
std::cout << "\nAvailable OFX plugins:\n"; std::cout << "\nAvailable OFX plugins:\n";
for (auto *plug : cache->getPlugins()) { for (auto *plug : cache->getPlugins()) {
if (plug) { if (plug) {
@@ -605,29 +623,31 @@ TEST(PluginMisc, ListAvailablePlugins)
} }
} }
std::cout << std::endl; std::cout << std::endl;
SUCCEED(); SUCCEED();
} }
TEST(PluginMisc, CImgBilateralGuided_MultiInput) TEST(PluginMisc, CImgBilateralGuided_MultiInput)
{ {
if (ShouldSkipTest()) GTEST_SKIP() << "OFX integration test not enabled"; if (ShouldSkipTest())
GTEST_SKIP() << "OFX integration test not enabled";
// CImgBilateralGuided is a multi-input plugin (Source + Guide). // CImgBilateralGuided is a multi-input plugin (Source + Guide).
// This test verifies that connecting both inputs does not trigger // This test verifies that connecting both inputs does not trigger
// the frame-rate mismatch exception in setupClipPreferencesArgs. // the frame-rate mismatch exception in setupClipPreferencesArgs.
VideoParams params(320, 240, core::PixelFormat::F32, 4); VideoParams params(320, 240, core::PixelFormat::F32, 4);
TexturePtr source = CreateSolidTexture(params, 0x80); TexturePtr source = CreateSolidTexture(params, 0x80);
TexturePtr guide = CreateSolidTexture(params, 0x40); TexturePtr guide = CreateSolidTexture(params, 0x40);
ASSERT_NE(source, nullptr); ASSERT_NE(source, nullptr);
ASSERT_NE(guide, nullptr); ASSERT_NE(guide, nullptr);
NodeValueRow row; NodeValueRow row;
row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName), row.insert(QString::fromStdString(kOfxImageEffectSimpleSourceClipName),
NodeValue(NodeValue::kTexture, source)); NodeValue(NodeValue::kTexture, source));
row.insert(QStringLiteral("Guide"), row.insert(QStringLiteral("Guide"), NodeValue(NodeValue::kTexture, guide));
NodeValue(NodeValue::kTexture, guide)); bool result =
bool result = RenderPlugin("net.sf.cimg.CImgBilateralGuided", params, row, true); RenderPlugin("net.sf.cimg.CImgBilateralGuided", params, row, true);
EXPECT_TRUE(result) << "CImgBilateralGuided plugin should produce output with both Source and Guide connected"; EXPECT_TRUE(result)
<< "CImgBilateralGuided plugin should produce output with both Source and Guide connected";
} }
} // namespace test } // namespace test
+2 -1
View File
@@ -6,7 +6,8 @@
#include "render/texture.h" #include "render/texture.h"
#include "render/videoparams.h" #include "render/videoparams.h"
namespace { namespace
{
class PluginJobTraverser : public olive::NodeTraverser { class PluginJobTraverser : public olive::NodeTraverser {
public: public:
@@ -8,9 +8,8 @@ TEST(PluginRendererReadback, BytesToPixels)
olive::core::rational(1, 1), olive::core::rational(1, 1),
olive::VideoParams::kInterlaceNone, 1); olive::VideoParams::kInterlaceNone, 1);
const int bytes_per_pixel = const int bytes_per_pixel = olive::VideoParams::GetBytesPerPixel(
olive::VideoParams::GetBytesPerPixel(params.format(), params.format(), params.channel_count());
params.channel_count());
ASSERT_EQ(bytes_per_pixel, 4); ASSERT_EQ(bytes_per_pixel, 4);
EXPECT_EQ(olive::plugin::detail::BytesToPixels(64, params), 16); EXPECT_EQ(olive::plugin::detail::BytesToPixels(64, params), 16);
File diff suppressed because it is too large Load Diff
+5 -7
View File
@@ -4,10 +4,10 @@
#include "ofxhClip.h" #include "ofxhClip.h"
#include "pluginSupport/OliveClip.h" #include "pluginSupport/OliveClip.h"
namespace { namespace
{
olive::VideoParams MakeParams(int width, int height, olive::VideoParams MakeParams(int width, int height,
olive::core::PixelFormat format, olive::core::PixelFormat format, int channels,
int channels,
bool premultiplied) bool premultiplied)
{ {
olive::VideoParams params; olive::VideoParams params;
@@ -64,16 +64,14 @@ TEST(PluginSupportClip, GetImageClampsBoundsAndCachesOutput)
olive::plugin::OliveClipInstance clip(nullptr, desc, params); olive::plugin::OliveClipInstance clip(nullptr, desc, params);
OfxRectD optional_bounds = { -10.0, -10.0, 200.0, 200.0 }; OfxRectD optional_bounds = { -10.0, -10.0, 200.0, 200.0 };
OFX::Host::ImageEffect::Image *image = OFX::Host::ImageEffect::Image *image = clip.getImage(0.0, &optional_bounds);
clip.getImage(0.0, &optional_bounds);
ASSERT_NE(image, nullptr); ASSERT_NE(image, nullptr);
auto *olive_image = static_cast<olive::plugin::Image *>(image); auto *olive_image = static_cast<olive::plugin::Image *>(image);
EXPECT_EQ(olive_image->width(), 100); EXPECT_EQ(olive_image->width(), 100);
EXPECT_EQ(olive_image->height(), 80); EXPECT_EQ(olive_image->height(), 80);
OFX::Host::ImageEffect::Image *image_again = OFX::Host::ImageEffect::Image *image_again = clip.getImage(0.0, nullptr);
clip.getImage(0.0, nullptr);
EXPECT_EQ(image, image_again); EXPECT_EQ(image, image_again);
} }
+5 -5
View File
@@ -5,10 +5,10 @@
#include "pluginSupport/OliveClip.h" #include "pluginSupport/OliveClip.h"
#include "pluginSupport/image.h" #include "pluginSupport/image.h"
namespace { namespace
{
olive::VideoParams MakeParams(int width, int height, olive::VideoParams MakeParams(int width, int height,
olive::core::PixelFormat format, olive::core::PixelFormat format, int channels,
int channels,
bool premultiplied) bool premultiplied)
{ {
olive::VideoParams params; olive::VideoParams params;
@@ -106,14 +106,14 @@ TEST(PluginSupportImage, AllocateSetsOfxProperties)
EXPECT_NE(image.data(), nullptr); EXPECT_NE(image.data(), nullptr);
EXPECT_EQ(image.row_bytes(), 8 * 4 * 2); EXPECT_EQ(image.row_bytes(), 8 * 4 * 2);
int bounds_props[4] = {0}; int bounds_props[4] = { 0 };
image.getIntPropertyN(kOfxImagePropBounds, bounds_props, 4); image.getIntPropertyN(kOfxImagePropBounds, bounds_props, 4);
EXPECT_EQ(bounds_props[0], bounds.x1); EXPECT_EQ(bounds_props[0], bounds.x1);
EXPECT_EQ(bounds_props[1], bounds.y1); EXPECT_EQ(bounds_props[1], bounds.y1);
EXPECT_EQ(bounds_props[2], bounds.x2); EXPECT_EQ(bounds_props[2], bounds.x2);
EXPECT_EQ(bounds_props[3], bounds.y2); EXPECT_EQ(bounds_props[3], bounds.y2);
int rod_props[4] = {0}; int rod_props[4] = { 0 };
image.getIntPropertyN(kOfxImagePropRegionOfDefinition, rod_props, 4); image.getIntPropertyN(kOfxImagePropRegionOfDefinition, rod_props, 4);
EXPECT_EQ(rod_props[0], rod.x1); EXPECT_EQ(rod_props[0], rod.x1);
EXPECT_EQ(rod_props[1], rod.y1); EXPECT_EQ(rod_props[1], rod.y1);
+1 -3
View File
@@ -4,7 +4,5 @@
TEST(PluginSupport, LoadPluginsEmptyPath) TEST(PluginSupport, LoadPluginsEmptyPath)
{ {
EXPECT_NO_THROW({ EXPECT_NO_THROW({ olive::plugin::loadPlugins(QString()); });
olive::plugin::loadPlugins(QString());
});
} }
+10 -10
View File
@@ -45,16 +45,16 @@ TEST(PreferencesBehaviorTab, BehaviorPrefTrProvidesTranslations)
{ {
QStringList keys; QStringList keys;
keys << QStringLiteral("Enable hover focus") keys << QStringLiteral("Enable hover focus")
<< QStringLiteral("Select also selects all children in the graph") << QStringLiteral("Select also selects all children in the graph")
<< QStringLiteral("Double-clicking a node opens its properties") << QStringLiteral("Double-clicking a node opens its properties")
<< QStringLiteral("Auto-Seek to Beginning of Sequence") << QStringLiteral("Auto-Seek to Beginning of Sequence")
<< QStringLiteral("Scroll wheel zooms instead of scrolling") << QStringLiteral("Scroll wheel zooms instead of scrolling")
<< QStringLiteral("Enable audio scrubbing"); << QStringLiteral("Enable audio scrubbing");
foreach (const QString &key, keys) { foreach (const QString &key, keys) {
EXPECT_FALSE( EXPECT_FALSE(
PreferencesBehaviorTab::BehaviorPrefTr( PreferencesBehaviorTab::BehaviorPrefTr(key.toUtf8().constData())
key.toUtf8().constData()).isEmpty()) .isEmpty())
<< key.toStdString(); << key.toStdString();
} }
} }
@@ -76,8 +76,8 @@ TEST(PreferencesGeneralTab, ContainsHoverFocusOption)
bool found = false; bool found = false;
foreach (QCheckBox *box, boxes) { foreach (QCheckBox *box, boxes) {
if (box->text() == PreferencesBehaviorTab::BehaviorPrefTr( if (box->text() ==
"Enable hover focus")) { PreferencesBehaviorTab::BehaviorPrefTr("Enable hover focus")) {
found = true; found = true;
break; break;
} }
@@ -96,7 +96,7 @@ TEST(PreferencesAudioTab, AudioScrubbingCheckboxUsesBehaviorTranslation)
bool found = false; bool found = false;
foreach (QCheckBox *box, boxes) { foreach (QCheckBox *box, boxes) {
if (box->text() == PreferencesBehaviorTab::BehaviorPrefTr( if (box->text() == PreferencesBehaviorTab::BehaviorPrefTr(
"Enable audio scrubbing")) { "Enable audio scrubbing")) {
found = true; found = true;
break; break;
} }
+2 -1
View File
@@ -79,7 +79,8 @@ TEST_F(PreviewAutoCacherTest, ClearSingleFrameRendersDoesNotCrashWhenEmpty)
cacher.ClearSingleFrameRenders(); cacher.ClearSingleFrameRenders();
} }
TEST_F(PreviewAutoCacherTest, ClearSingleFrameRendersThatArentRunningDoesNotCrashWhenEmpty) TEST_F(PreviewAutoCacherTest,
ClearSingleFrameRendersThatArentRunningDoesNotCrashWhenEmpty)
{ {
PreviewAutoCacher cacher; PreviewAutoCacher cacher;
cacher.ClearSingleFrameRendersThatArentRunning(); cacher.ClearSingleFrameRendersThatArentRunning();
+7 -6
View File
@@ -13,7 +13,8 @@
TEST(ProjectSerializer, SaveLoadProjectRoundTrip) TEST(ProjectSerializer, SaveLoadProjectRoundTrip)
{ {
const bool created_disk_manager = (olive::DiskManager::instance() == nullptr); const bool created_disk_manager =
(olive::DiskManager::instance() == nullptr);
if (created_disk_manager) { if (created_disk_manager) {
olive::DiskManager::CreateInstance(); olive::DiskManager::CreateInstance();
} }
@@ -45,15 +46,15 @@ TEST(ProjectSerializer, SaveLoadProjectRoundTrip)
QBuffer read_buffer(&xml); QBuffer read_buffer(&xml);
read_buffer.open(QIODevice::ReadOnly); read_buffer.open(QIODevice::ReadOnly);
QXmlStreamReader reader(&read_buffer); QXmlStreamReader reader(&read_buffer);
olive::ProjectSerializer::Result result = olive::ProjectSerializer::Result result = olive::ProjectSerializer::Load(
olive::ProjectSerializer::Load(&loaded_project, &reader, &loaded_project, &reader, olive::ProjectSerializer::kProject);
olive::ProjectSerializer::kProject);
EXPECT_EQ(result.code(), olive::ProjectSerializer::kSuccess); EXPECT_EQ(result.code(), olive::ProjectSerializer::kSuccess);
EXPECT_FALSE(loaded_project.nodes().isEmpty()); EXPECT_FALSE(loaded_project.nodes().isEmpty());
ASSERT_TRUE(result.GetLoadData().node_ptrs.contains( ASSERT_TRUE(result.GetLoadData().node_ptrs.contains(
reinterpret_cast<quintptr>(node))); reinterpret_cast<quintptr>(node)));
EXPECT_TRUE(loaded_project.nodes().contains( EXPECT_TRUE(
result.GetLoadData().node_ptrs.value(reinterpret_cast<quintptr>(node)))); loaded_project.nodes().contains(result.GetLoadData().node_ptrs.value(
reinterpret_cast<quintptr>(node))));
olive::ProjectSerializer::Destroy(); olive::ProjectSerializer::Destroy();
if (created_disk_manager) { if (created_disk_manager) {
+31 -33
View File
@@ -18,14 +18,14 @@ TEST(ProxyManager, BuildsStableProxyFilename)
params.version = 1; params.version = 1;
const QString first = olive::ProxyManager::GetProxyFilename( const QString first = olive::ProxyManager::GetProxyFilename(
QStringLiteral("/tmp/oak-cache"), QStringLiteral("/tmp/oak-cache"), QStringLiteral("/media/source.mov"),
QStringLiteral("/media/source.mov"), 0, params); 0, params);
const QString second = olive::ProxyManager::GetProxyFilename( const QString second = olive::ProxyManager::GetProxyFilename(
QStringLiteral("/tmp/oak-cache"), QStringLiteral("/tmp/oak-cache"), QStringLiteral("/media/source.mov"),
QStringLiteral("/media/source.mov"), 0, params); 0, params);
const QString other_stream = olive::ProxyManager::GetProxyFilename( const QString other_stream = olive::ProxyManager::GetProxyFilename(
QStringLiteral("/tmp/oak-cache"), QStringLiteral("/tmp/oak-cache"), QStringLiteral("/media/source.mov"),
QStringLiteral("/media/source.mov"), 1, params); 1, params);
EXPECT_EQ(first, second); EXPECT_EQ(first, second);
EXPECT_NE(first, other_stream); EXPECT_NE(first, other_stream);
@@ -48,11 +48,11 @@ TEST(ProxyManager, ProxyFilenameIncludesPresetParameters)
mov_540p.extension = QStringLiteral("mov"); mov_540p.extension = QStringLiteral("mov");
const QString first = olive::ProxyManager::GetProxyFilename( const QString first = olive::ProxyManager::GetProxyFilename(
QStringLiteral("/tmp/oak-cache"), QStringLiteral("/tmp/oak-cache"), QStringLiteral("/media/source.mov"),
QStringLiteral("/media/source.mov"), 0, mp4_720p); 0, mp4_720p);
const QString second = olive::ProxyManager::GetProxyFilename( const QString second = olive::ProxyManager::GetProxyFilename(
QStringLiteral("/tmp/oak-cache"), QStringLiteral("/tmp/oak-cache"), QStringLiteral("/media/source.mov"),
QStringLiteral("/media/source.mov"), 0, mov_540p); 0, mov_540p);
EXPECT_NE(first, second); EXPECT_NE(first, second);
EXPECT_TRUE(first.contains(QStringLiteral(".1280x720.v1."))); EXPECT_TRUE(first.contains(QStringLiteral(".1280x720.v1.")));
@@ -117,21 +117,21 @@ TEST(ProxyManager, ConvertsProxyStateToAndFromStrings)
olive::ProxyManager::kProxyFailed), olive::ProxyManager::kProxyFailed),
QStringLiteral("failed")); QStringLiteral("failed"));
EXPECT_EQ(olive::ProxyManager::ProxyStateFromString( EXPECT_EQ(
QStringLiteral("missing")), olive::ProxyManager::ProxyStateFromString(QStringLiteral("missing")),
olive::ProxyManager::kProxyMissing); olive::ProxyManager::kProxyMissing);
EXPECT_EQ(olive::ProxyManager::ProxyStateFromString( EXPECT_EQ(
QStringLiteral("generating")), olive::ProxyManager::ProxyStateFromString(QStringLiteral("generating")),
olive::ProxyManager::kProxyGenerating); olive::ProxyManager::kProxyGenerating);
EXPECT_EQ(olive::ProxyManager::ProxyStateFromString( EXPECT_EQ(
QStringLiteral("ready")), olive::ProxyManager::ProxyStateFromString(QStringLiteral("ready")),
olive::ProxyManager::kProxyReady); olive::ProxyManager::kProxyReady);
EXPECT_EQ(olive::ProxyManager::ProxyStateFromString( EXPECT_EQ(
QStringLiteral("failed")), olive::ProxyManager::ProxyStateFromString(QStringLiteral("failed")),
olive::ProxyManager::kProxyFailed); olive::ProxyManager::kProxyFailed);
EXPECT_EQ(olive::ProxyManager::ProxyStateFromString( EXPECT_EQ(
QStringLiteral("unknown")), olive::ProxyManager::ProxyStateFromString(QStringLiteral("unknown")),
olive::ProxyManager::kProxyMissing); olive::ProxyManager::kProxyMissing);
} }
TEST(ProxyManager, FootagePersistsProxyMetadata) TEST(ProxyManager, FootagePersistsProxyMetadata)
@@ -225,10 +225,10 @@ TEST(ProxyManager, EmitsProxyFinishedState)
received_state = state; received_state = state;
}); });
emit olive::ProxyManager::instance()->ProxyFinished( emit olive::ProxyManager::instance()
QStringLiteral("/media/source.mov"), 0, -> ProxyFinished(QStringLiteral("/media/source.mov"), 0,
QStringLiteral("/cache/proxy/example.mp4"), QStringLiteral("/cache/proxy/example.mp4"),
olive::ProxyManager::kProxyFailed); olive::ProxyManager::kProxyFailed);
EXPECT_TRUE(received); EXPECT_TRUE(received);
EXPECT_EQ(received_source, QStringLiteral("/media/source.mov")); EXPECT_EQ(received_source, QStringLiteral("/media/source.mov"));
@@ -241,10 +241,8 @@ TEST(ProxyManager, EmitsProxyFinishedState)
TEST(ProxyManager, WorkingProxyFilenamePrependsExtension) TEST(ProxyManager, WorkingProxyFilenamePrependsExtension)
{ {
const QString proxy = const QString proxy = QStringLiteral("/cache/proxy/example.mp4");
QStringLiteral("/cache/proxy/example.mp4"); const QString working = olive::ProxyManager::GetWorkingProxyFilename(proxy);
const QString working =
olive::ProxyManager::GetWorkingProxyFilename(proxy);
EXPECT_EQ(working, QStringLiteral("/cache/proxy/example.mp4.working.mp4")); EXPECT_EQ(working, QStringLiteral("/cache/proxy/example.mp4.working.mp4"));
} }
+20 -20
View File
@@ -7,12 +7,12 @@ TEST(RenderAudioParams, ValidityAndEquality)
olive::core::AudioParams invalid; olive::core::AudioParams invalid;
EXPECT_FALSE(invalid.is_valid()); EXPECT_FALSE(invalid.is_valid());
olive::core::AudioParams params( olive::core::AudioParams params(48000, AV_CH_LAYOUT_STEREO,
48000, AV_CH_LAYOUT_STEREO, olive::core::SampleFormat::S16); olive::core::SampleFormat::S16);
EXPECT_TRUE(params.is_valid()); EXPECT_TRUE(params.is_valid());
olive::core::AudioParams other( olive::core::AudioParams other(48000, AV_CH_LAYOUT_STEREO,
48000, AV_CH_LAYOUT_STEREO, olive::core::SampleFormat::S16); olive::core::SampleFormat::S16);
EXPECT_TRUE(params == other); EXPECT_TRUE(params == other);
other.set_sample_rate(44100); other.set_sample_rate(44100);
@@ -21,8 +21,8 @@ TEST(RenderAudioParams, ValidityAndEquality)
TEST(RenderAudioParams, TimeAndSampleConversions) TEST(RenderAudioParams, TimeAndSampleConversions)
{ {
olive::core::AudioParams params( olive::core::AudioParams params(48000, AV_CH_LAYOUT_STEREO,
48000, AV_CH_LAYOUT_STEREO, olive::core::SampleFormat::S16); olive::core::SampleFormat::S16);
EXPECT_EQ(params.channel_count(), 2); EXPECT_EQ(params.channel_count(), 2);
EXPECT_EQ(params.bytes_per_sample_per_channel(), 2); EXPECT_EQ(params.bytes_per_sample_per_channel(), 2);
@@ -43,34 +43,34 @@ TEST(RenderAudioParams, TimeAndSampleConversions)
TEST(RenderAudioParams, ChannelLayoutCount) TEST(RenderAudioParams, ChannelLayoutCount)
{ {
olive::core::AudioParams mono( olive::core::AudioParams mono(48000, AV_CH_LAYOUT_MONO,
48000, AV_CH_LAYOUT_MONO, olive::core::SampleFormat::F32); olive::core::SampleFormat::F32);
EXPECT_EQ(mono.channel_count(), 1); EXPECT_EQ(mono.channel_count(), 1);
olive::core::AudioParams surround( olive::core::AudioParams surround(48000, AV_CH_LAYOUT_5POINT1,
48000, AV_CH_LAYOUT_5POINT1, olive::core::SampleFormat::F32); olive::core::SampleFormat::F32);
EXPECT_EQ(surround.channel_count(), 6); EXPECT_EQ(surround.channel_count(), 6);
} }
TEST(RenderAudioParams, SampleFormatSizes) TEST(RenderAudioParams, SampleFormatSizes)
{ {
olive::core::AudioParams u8( olive::core::AudioParams u8(48000, AV_CH_LAYOUT_MONO,
48000, AV_CH_LAYOUT_MONO, olive::core::SampleFormat::U8); olive::core::SampleFormat::U8);
EXPECT_EQ(u8.bytes_per_sample_per_channel(), 1); EXPECT_EQ(u8.bytes_per_sample_per_channel(), 1);
olive::core::AudioParams f32( olive::core::AudioParams f32(48000, AV_CH_LAYOUT_MONO,
48000, AV_CH_LAYOUT_MONO, olive::core::SampleFormat::F32); olive::core::SampleFormat::F32);
EXPECT_EQ(f32.bytes_per_sample_per_channel(), 4); EXPECT_EQ(f32.bytes_per_sample_per_channel(), 4);
olive::core::AudioParams f64( olive::core::AudioParams f64(48000, AV_CH_LAYOUT_MONO,
48000, AV_CH_LAYOUT_MONO, olive::core::SampleFormat::F64); olive::core::SampleFormat::F64);
EXPECT_EQ(f64.bytes_per_sample_per_channel(), 8); EXPECT_EQ(f64.bytes_per_sample_per_channel(), 8);
} }
TEST(RenderAudioParams, CopyAndAssignment) TEST(RenderAudioParams, CopyAndAssignment)
{ {
olive::core::AudioParams params( olive::core::AudioParams params(96000, AV_CH_LAYOUT_STEREO,
96000, AV_CH_LAYOUT_STEREO, olive::core::SampleFormat::F32); olive::core::SampleFormat::F32);
olive::core::AudioParams copy(params); olive::core::AudioParams copy(params);
EXPECT_EQ(copy.sample_rate(), 96000); EXPECT_EQ(copy.sample_rate(), 96000);
@@ -85,8 +85,8 @@ TEST(RenderAudioParams, CopyAndAssignment)
TEST(RenderAudioParams, SettersModifyState) TEST(RenderAudioParams, SettersModifyState)
{ {
olive::core::AudioParams params( olive::core::AudioParams params(44100, AV_CH_LAYOUT_MONO,
44100, AV_CH_LAYOUT_MONO, olive::core::SampleFormat::S16); olive::core::SampleFormat::S16);
EXPECT_TRUE(params.is_valid()); EXPECT_TRUE(params.is_valid());
params.set_sample_rate(48000); params.set_sample_rate(48000);
+22 -17
View File
@@ -40,13 +40,13 @@ TEST(SpscRingBuffer, BasicPushPopAndCapacity)
EXPECT_TRUE(ring->IsEmptyApprox()); EXPECT_TRUE(ring->IsEmptyApprox());
uint32_t v = 0; uint32_t v = 0;
EXPECT_FALSE(ring->Pop(&v)); // empty EXPECT_FALSE(ring->Pop(&v)); // empty
// Capacity 4 holds at most 3 entries (one slot reserved to disambiguate full/empty). // Capacity 4 holds at most 3 entries (one slot reserved to disambiguate full/empty).
EXPECT_TRUE(ring->Push(10)); EXPECT_TRUE(ring->Push(10));
EXPECT_TRUE(ring->Push(20)); EXPECT_TRUE(ring->Push(20));
EXPECT_TRUE(ring->Push(30)); EXPECT_TRUE(ring->Push(30));
EXPECT_FALSE(ring->Push(40)); // full EXPECT_FALSE(ring->Push(40)); // full
EXPECT_TRUE(ring->Pop(&v)); EXPECT_TRUE(ring->Pop(&v));
EXPECT_EQ(v, 10u); EXPECT_EQ(v, 10u);
@@ -54,7 +54,7 @@ TEST(SpscRingBuffer, BasicPushPopAndCapacity)
EXPECT_EQ(v, 20u); EXPECT_EQ(v, 20u);
EXPECT_TRUE(ring->Pop(&v)); EXPECT_TRUE(ring->Pop(&v));
EXPECT_EQ(v, 30u); EXPECT_EQ(v, 30u);
EXPECT_FALSE(ring->Pop(&v)); // empty again EXPECT_FALSE(ring->Pop(&v)); // empty again
} }
TEST(SpscRingBuffer, WrapAround) TEST(SpscRingBuffer, WrapAround)
@@ -75,17 +75,18 @@ TEST(SpscRingBuffer, WrapAround)
TEST(SpscRingBuffer, ConcurrentProducerConsumer) TEST(SpscRingBuffer, ConcurrentProducerConsumer)
{ {
constexpr uint32_t kCapacity = 1024; constexpr uint32_t kCapacity = 1024;
constexpr uint32_t kCount = 2'000'000; // values 0..kCount-1 streamed through the ring constexpr uint32_t kCount =
2'000'000; // values 0..kCount-1 streamed through the ring
std::vector<uint8_t> mem(SpscRingBuffer::BytesNeeded(kCapacity)); std::vector<uint8_t> mem(SpscRingBuffer::BytesNeeded(kCapacity));
SpscRingBuffer *ring = SpscRingBuffer::Create(mem.data(), kCapacity); SpscRingBuffer *ring = SpscRingBuffer::Create(mem.data(), kCapacity);
std::atomic<bool> order_ok{true}; std::atomic<bool> order_ok{ true };
std::thread producer([&] { std::thread producer([&] {
for (uint32_t i = 0; i < kCount; i++) { for (uint32_t i = 0; i < kCount; i++) {
while (!ring->Push(i)) { while (!ring->Push(i)) {
std::this_thread::yield(); // buffer full, spin until consumer drains std::this_thread::yield(); // buffer full, spin until consumer drains
} }
} }
}); });
@@ -124,7 +125,8 @@ TEST(FrameSlotPool, SingleThreadedHandoff)
constexpr size_t kSlotBytes = 256; constexpr size_t kSlotBytes = 256;
std::vector<uint8_t> mem(FrameSlotPool::BytesNeeded(kSlots, kSlotBytes)); std::vector<uint8_t> mem(FrameSlotPool::BytesNeeded(kSlots, kSlotBytes));
FrameSlotPool filler = FrameSlotPool::Create(mem.data(), kSlots, kSlotBytes); FrameSlotPool filler =
FrameSlotPool::Create(mem.data(), kSlots, kSlotBytes);
FrameSlotPool drainer = FrameSlotPool::Attach(mem.data()); FrameSlotPool drainer = FrameSlotPool::Attach(mem.data());
ASSERT_TRUE(filler.IsValid()); ASSERT_TRUE(filler.IsValid());
@@ -156,7 +158,8 @@ TEST(FrameSlotPool, SingleThreadedHandoff)
EXPECT_EQ(got_meta->id, 4242); EXPECT_EQ(got_meta->id, 4242);
EXPECT_EQ(got_meta->width, 16); EXPECT_EQ(got_meta->width, 16);
const auto *got_data = static_cast<const uint8_t *>(drainer.SlotData(got_idx)); const auto *got_data =
static_cast<const uint8_t *>(drainer.SlotData(got_idx));
for (size_t i = 0; i < kSlotBytes; i++) { for (size_t i = 0; i < kSlotBytes; i++) {
ASSERT_EQ(got_data[i], uint8_t(i & 0xFF)); ASSERT_EQ(got_data[i], uint8_t(i & 0xFF));
} }
@@ -180,7 +183,7 @@ TEST(FrameSlotPool, ExhaustionAndRefill)
held.push_back(a); held.push_back(a);
} }
uint32_t overflow = 0; uint32_t overflow = 0;
EXPECT_FALSE(pool.Acquire(&overflow)); // pool exhausted EXPECT_FALSE(pool.Acquire(&overflow)); // pool exhausted
// Publishing then consuming + releasing returns the slots to the free pool. // Publishing then consuming + releasing returns the slots to the free pool.
for (uint32_t idx : held) { for (uint32_t idx : held) {
@@ -192,7 +195,7 @@ TEST(FrameSlotPool, ExhaustionAndRefill)
ASSERT_TRUE(pool.Release(c)); ASSERT_TRUE(pool.Release(c));
} }
uint32_t again = 0; uint32_t again = 0;
EXPECT_TRUE(pool.Acquire(&again)); // free again EXPECT_TRUE(pool.Acquire(&again)); // free again
} }
TEST(FrameSlotPool, ConcurrentFillDrainIntegrity) TEST(FrameSlotPool, ConcurrentFillDrainIntegrity)
@@ -202,10 +205,11 @@ TEST(FrameSlotPool, ConcurrentFillDrainIntegrity)
constexpr int64_t kFrames = 200'000; constexpr int64_t kFrames = 200'000;
std::vector<uint8_t> mem(FrameSlotPool::BytesNeeded(kSlots, kSlotBytes)); std::vector<uint8_t> mem(FrameSlotPool::BytesNeeded(kSlots, kSlotBytes));
FrameSlotPool filler = FrameSlotPool::Create(mem.data(), kSlots, kSlotBytes); FrameSlotPool filler =
FrameSlotPool::Create(mem.data(), kSlots, kSlotBytes);
FrameSlotPool drainer = FrameSlotPool::Attach(mem.data()); FrameSlotPool drainer = FrameSlotPool::Attach(mem.data());
std::atomic<bool> integrity_ok{true}; std::atomic<bool> integrity_ok{ true };
// Filler: for each frame id, acquire a slot, stamp the id into meta and a pattern into the data, // Filler: for each frame id, acquire a slot, stamp the id into meta and a pattern into the data,
// publish. Spins when no slot is free (this is the natural backpressure path). // publish. Spins when no slot is free (this is the natural backpressure path).
@@ -220,7 +224,7 @@ TEST(FrameSlotPool, ConcurrentFillDrainIntegrity)
const uint8_t pat = uint8_t(id & 0xFF); const uint8_t pat = uint8_t(id & 0xFF);
memset(d, pat, kSlotBytes); memset(d, pat, kSlotBytes);
while (!filler.Publish(idx)) { while (!filler.Publish(idx)) {
std::this_thread::yield(); // ready ring transiently full std::this_thread::yield(); // ready ring transiently full
} }
} }
}); });
@@ -292,7 +296,7 @@ TEST(IpcMessage, TypedRoundTrip)
rf.channel_count = 4; rf.channel_count = 4;
rf.mode = 1; rf.mode = 1;
rf.input_slot = 2; rf.input_slot = 2;
rf.input_slots = {2, 3}; rf.input_slots = { 2, 3 };
ASSERT_TRUE(WriteMessage(&dev, rf.ToJson())); ASSERT_TRUE(WriteMessage(&dev, rf.ToJson()));
FrameReadyMsg fr; FrameReadyMsg fr;
@@ -349,7 +353,8 @@ TEST(IpcMessage, PartialFrameByteByByte)
CancelMsg c; CancelMsg c;
c.ticket_id = 7; c.ticket_id = 7;
const QByteArray full = const QByteArray full =
QByteArray(QJsonDocument(c.ToJson()).toJson(QJsonDocument::Compact)) + '\n'; QByteArray(QJsonDocument(c.ToJson()).toJson(QJsonDocument::Compact)) +
'\n';
// Feed the bytes one at a time; ReadMessage must return false until the terminating '\n'. // Feed the bytes one at a time; ReadMessage must return false until the terminating '\n'.
QByteArray reader; QByteArray reader;
@@ -357,9 +362,9 @@ TEST(IpcMessage, PartialFrameByteByByte)
bool ok = false; bool ok = false;
for (int i = 0; i < full.size() - 1; i++) { for (int i = 0; i < full.size() - 1; i++) {
reader.append(full.at(i)); reader.append(full.at(i));
ASSERT_FALSE(ReadMessage(&reader, &obj, &ok)); // no complete line yet ASSERT_FALSE(ReadMessage(&reader, &obj, &ok)); // no complete line yet
} }
reader.append(full.at(full.size() - 1)); // the trailing newline reader.append(full.at(full.size() - 1)); // the trailing newline
ASSERT_TRUE(ReadMessage(&reader, &obj, &ok)); ASSERT_TRUE(ReadMessage(&reader, &obj, &ok));
ASSERT_TRUE(ok); ASSERT_TRUE(ok);
+21 -24
View File
@@ -15,29 +15,27 @@ TEST(RenderVideoParams, BytesPerChannelAndPixel)
EXPECT_EQ(olive::VideoParams::GetBytesPerChannel( EXPECT_EQ(olive::VideoParams::GetBytesPerChannel(
olive::core::PixelFormat::INVALID), olive::core::PixelFormat::INVALID),
0); 0);
EXPECT_EQ(olive::VideoParams::GetBytesPerChannel( EXPECT_EQ(
olive::core::PixelFormat::U8), olive::VideoParams::GetBytesPerChannel(olive::core::PixelFormat::U8),
1); 1);
EXPECT_EQ(olive::VideoParams::GetBytesPerChannel( EXPECT_EQ(
olive::core::PixelFormat::U16), olive::VideoParams::GetBytesPerChannel(olive::core::PixelFormat::U16),
2); 2);
EXPECT_EQ(olive::VideoParams::GetBytesPerChannel( EXPECT_EQ(
olive::core::PixelFormat::F16), olive::VideoParams::GetBytesPerChannel(olive::core::PixelFormat::F16),
2); 2);
EXPECT_EQ(olive::VideoParams::GetBytesPerChannel( EXPECT_EQ(
olive::core::PixelFormat::F32), olive::VideoParams::GetBytesPerChannel(olive::core::PixelFormat::F32),
4); 4);
EXPECT_EQ(olive::VideoParams::GetBytesPerPixel( EXPECT_EQ(olive::VideoParams::GetBytesPerPixel(olive::core::PixelFormat::U8,
olive::core::PixelFormat::U8, 4), 4),
4); 4);
} }
TEST(RenderVideoParams, DividerAndFormatNames) TEST(RenderVideoParams, DividerAndFormatNames)
{ {
EXPECT_EQ(olive::VideoParams::GetNameForDivider(1), EXPECT_EQ(olive::VideoParams::GetNameForDivider(1), QStringLiteral("Full"));
QStringLiteral("Full")); EXPECT_EQ(olive::VideoParams::GetNameForDivider(3), QStringLiteral("1/3"));
EXPECT_EQ(olive::VideoParams::GetNameForDivider(3),
QStringLiteral("1/3"));
const QString unknown = const QString unknown =
olive::VideoParams::GetFormatName(olive::core::PixelFormat::INVALID); olive::VideoParams::GetFormatName(olive::core::PixelFormat::INVALID);
@@ -47,11 +45,11 @@ TEST(RenderVideoParams, DividerAndFormatNames)
TEST(RenderVideoParams, ScalingAndDividerForTarget) TEST(RenderVideoParams, ScalingAndDividerForTarget)
{ {
EXPECT_EQ(olive::VideoParams::GetScaledDimension(100, 3), 33); EXPECT_EQ(olive::VideoParams::GetScaledDimension(100, 3), 33);
EXPECT_EQ(olive::VideoParams::GetDividerForTargetResolution( EXPECT_EQ(olive::VideoParams::GetDividerForTargetResolution(1920, 1080, 960,
1920, 1080, 960, 540), 540),
2); 2);
EXPECT_EQ(olive::VideoParams::GetDividerForTargetResolution( EXPECT_EQ(olive::VideoParams::GetDividerForTargetResolution(1920, 1080, 480,
1920, 1080, 480, 270), 270),
4); 4);
} }
@@ -191,8 +189,7 @@ TEST(RenderVideoParams, SaveLoadRoundTripExtended)
EXPECT_FLOAT_EQ(loaded.x(), 1.5f); EXPECT_FLOAT_EQ(loaded.x(), 1.5f);
EXPECT_FLOAT_EQ(loaded.y(), -2.25f); EXPECT_FLOAT_EQ(loaded.y(), -2.25f);
EXPECT_EQ(loaded.stream_index(), 7); EXPECT_EQ(loaded.stream_index(), 7);
EXPECT_EQ(loaded.video_type(), EXPECT_EQ(loaded.video_type(), olive::VideoParams::kVideoTypeImageSequence);
olive::VideoParams::kVideoTypeImageSequence);
EXPECT_EQ(loaded.frame_rate(), olive::core::rational(30000, 1001)); EXPECT_EQ(loaded.frame_rate(), olive::core::rational(30000, 1001));
EXPECT_EQ(loaded.start_time(), 123); EXPECT_EQ(loaded.start_time(), 123);
EXPECT_EQ(loaded.duration(), 456); EXPECT_EQ(loaded.duration(), 456);
+52 -39
View File
@@ -49,7 +49,8 @@
using namespace olive; using namespace olive;
using namespace olive::core; using namespace olive::core;
namespace { namespace
{
#ifdef OAK_ENABLE_DYNAMIC_RENDER_BACKEND #ifdef OAK_ENABLE_DYNAMIC_RENDER_BACKEND
bool IsRenderBackendAvailable(const QString &backend) bool IsRenderBackendAvailable(const QString &backend)
@@ -96,8 +97,8 @@ QString WorkerBinaryPath()
// The test binary lives in cmake-build-debug/tests/gtest; the worker is in // The test binary lives in cmake-build-debug/tests/gtest; the worker is in
// cmake-build-debug/app. // cmake-build-debug/app.
QDir dir(QCoreApplication::applicationDirPath()); QDir dir(QCoreApplication::applicationDirPath());
dir.cdUp(); // tests/gtest -> tests dir.cdUp(); // tests/gtest -> tests
dir.cdUp(); // tests -> build dir dir.cdUp(); // tests -> build dir
dir.cd(QStringLiteral("app")); dir.cd(QStringLiteral("app"));
#if defined(_WIN32) #if defined(_WIN32)
return dir.filePath(QStringLiteral("oak-render-worker.exe")); return dir.filePath(QStringLiteral("oak-render-worker.exe"));
@@ -140,8 +141,10 @@ void SaveFrameAsPng(const void *data, int width, int height,
for (int x = 0; x < width; ++x) { for (int x = 0; x < width; ++x) {
for (int c = 0; c < 4; ++c) { for (int c = 0; c < 4; ++c) {
float v = src[(y * width + x) * 4 + c]; float v = src[(y * width + x) * 4 + c];
if (v < 0.0f) v = 0.0f; if (v < 0.0f)
if (v > 1.0f) v = 1.0f; v = 0.0f;
if (v > 1.0f)
v = 1.0f;
dst[(x * 4) + c] = static_cast<uchar>(v * 255.0f); dst[(x * 4) + c] = static_cast<uchar>(v * 255.0f);
} }
} }
@@ -153,7 +156,7 @@ void SaveFrameAsPng(const void *data, int width, int height,
} }
} }
} // namespace } // namespace
class RenderWorkerFootageTest : public ::testing::Test { class RenderWorkerFootageTest : public ::testing::Test {
protected: protected:
@@ -210,8 +213,8 @@ protected:
temp_dir_.filePath(QStringLiteral("worker_graph.ove"))); temp_dir_.filePath(QStringLiteral("worker_graph.ove")));
ProjectSerializer::Result r = ProjectSerializer::Save( ProjectSerializer::Result r = ProjectSerializer::Save(
ProjectSerializer::SaveData(ProjectSerializer::kProject, project_.get(), ProjectSerializer::SaveData(ProjectSerializer::kProject,
project_file_), project_.get(), project_file_),
false); false);
ASSERT_EQ(r.code(), ProjectSerializer::kSuccess) ASSERT_EQ(r.code(), ProjectSerializer::kSuccess)
<< "Failed to save project file: " << r.GetDetails().toStdString(); << "Failed to save project file: " << r.GetDetails().toStdString();
@@ -222,7 +225,8 @@ protected:
{ {
// ---- decode a frame so we know the dimensions and slot sizes ---- // ---- decode a frame so we know the dimensions and slot sizes ----
DecoderPtr decoder = Decoder::CreateFromID(QStringLiteral("ffmpeg")); DecoderPtr decoder = Decoder::CreateFromID(QStringLiteral("ffmpeg"));
if (!decoder || !decoder->Open(Decoder::CodecStream(demo_path_, 0, nullptr))) { if (!decoder ||
!decoder->Open(Decoder::CodecStream(demo_path_, 0, nullptr))) {
return false; return false;
} }
Decoder::RetrieveVideoParams retrieve; Decoder::RetrieveVideoParams retrieve;
@@ -251,10 +255,10 @@ protected:
output_shm_key_ = ipc::SharedMemoryRegion::MakeKey(owner_pid, 0); output_shm_key_ = ipc::SharedMemoryRegion::MakeKey(owner_pid, 0);
input_shm_key_ = ipc::SharedMemoryRegion::MakeKey(owner_pid, 1); input_shm_key_ = ipc::SharedMemoryRegion::MakeKey(owner_pid, 1);
const size_t output_bytes = ipc::FrameSlotPool::BytesNeeded( const size_t output_bytes =
kOutputSlots, output_data_bytes_); ipc::FrameSlotPool::BytesNeeded(kOutputSlots, output_data_bytes_);
const size_t input_bytes = ipc::FrameSlotPool::BytesNeeded( const size_t input_bytes =
kInputSlots, input_data_bytes_); ipc::FrameSlotPool::BytesNeeded(kInputSlots, input_data_bytes_);
if (!output_region_.Open(output_shm_key_, output_bytes, if (!output_region_.Open(output_shm_key_, output_bytes,
ipc::SharedMemoryRegion::kCreate)) { ipc::SharedMemoryRegion::kCreate)) {
@@ -278,7 +282,8 @@ protected:
// ---- spawn worker ---- // ---- spawn worker ----
worker_.setProcessChannelMode(QProcess::SeparateChannels); worker_.setProcessChannelMode(QProcess::SeparateChannels);
worker_.start(worker_path_, QStringList{QStringLiteral("--backend"), backend}); worker_.start(worker_path_,
QStringList{ QStringLiteral("--backend"), backend });
if (!worker_.waitForStarted(kTimeoutMs)) { if (!worker_.waitForStarted(kTimeoutMs)) {
return false; return false;
} }
@@ -333,8 +338,8 @@ protected:
if (!input_pool_->Acquire(&input_slot)) { if (!input_pool_->Acquire(&input_slot)) {
return false; return false;
} }
std::memcpy(input_pool_->SlotData(input_slot), decoded_frame_->const_data(), std::memcpy(input_pool_->SlotData(input_slot),
input_data_bytes_); decoded_frame_->const_data(), input_data_bytes_);
ipc::FrameSlotMeta *meta = input_pool_->Meta(input_slot); ipc::FrameSlotMeta *meta = input_pool_->Meta(input_slot);
meta->id = 0; meta->id = 0;
meta->time_num = 0; meta->time_num = 0;
@@ -345,9 +350,10 @@ protected:
meta->channel_count = decoded_frame_->channel_count(); meta->channel_count = decoded_frame_->channel_count();
meta->linesize = input_stride_; meta->linesize = input_stride_;
meta->data_size = int32_t(input_data_bytes_); meta->data_size = int32_t(input_data_bytes_);
std::strncpy(meta->colorspace, std::strncpy(
decoded_frame_->video_params().colorspace().toUtf8().constData(), meta->colorspace,
sizeof(meta->colorspace) - 1); decoded_frame_->video_params().colorspace().toUtf8().constData(),
sizeof(meta->colorspace) - 1);
meta->colorspace[sizeof(meta->colorspace) - 1] = '\0'; meta->colorspace[sizeof(meta->colorspace) - 1] = '\0';
input_pool_->Publish(input_slot); input_pool_->Publish(input_slot);
@@ -363,7 +369,8 @@ protected:
req.mode = int(RenderMode::kOnline); req.mode = int(RenderMode::kOnline);
req.input_slot = 0; req.input_slot = 0;
if (!ipc::WriteMessage(&worker_, req.ToJson())) { if (!ipc::WriteMessage(&worker_, req.ToJson())) {
std::cerr << "RenderFrameAndWait: failed to write request" << std::endl; std::cerr << "RenderFrameAndWait: failed to write request"
<< std::endl;
return false; return false;
} }
@@ -378,8 +385,9 @@ protected:
std::cerr << "RenderFrameAndWait: unexpected message type " std::cerr << "RenderFrameAndWait: unexpected message type "
<< ready[QStringLiteral("type")].toString().toStdString() << ready[QStringLiteral("type")].toString().toStdString()
<< " body=" << " body="
<< QJsonDocument(ready).toJson(QJsonDocument::Compact) << QJsonDocument(ready)
.toStdString() .toJson(QJsonDocument::Compact)
.toStdString()
<< std::endl; << std::endl;
return false; return false;
} }
@@ -407,8 +415,9 @@ protected:
if (worker_.state() == QProcess::NotRunning) { if (worker_.state() == QProcess::NotRunning) {
std::cerr << "WaitForMessage: worker exited with code " std::cerr << "WaitForMessage: worker exited with code "
<< worker_.exitCode() << std::endl; << worker_.exitCode() << std::endl;
std::cerr << "Worker stdout buffer: " std::cerr
<< read_buffer_.toStdString() << std::endl; << "Worker stdout buffer: " << read_buffer_.toStdString()
<< std::endl;
QByteArray err = worker_.readAllStandardError(); QByteArray err = worker_.readAllStandardError();
if (!err.isEmpty()) { if (!err.isEmpty()) {
std::cerr << "Worker stderr:\n" std::cerr << "Worker stderr:\n"
@@ -417,8 +426,9 @@ protected:
return false; return false;
} }
} }
std::cerr << "WaitForMessage: timeout, buffer=" std::cerr
<< read_buffer_.toStdString() << std::endl; << "WaitForMessage: timeout, buffer=" << read_buffer_.toStdString()
<< std::endl;
return false; return false;
} }
@@ -473,19 +483,21 @@ TEST_F(RenderWorkerFootageTest, VulkanFootageIsNotBlack)
ASSERT_EQ(int(consumed_slot), output_slot); ASSERT_EQ(int(consumed_slot), output_slot);
const void *output_data = output_pool_->SlotData(consumed_slot); const void *output_data = output_pool_->SlotData(consumed_slot);
const double brightness = SampleBrightnessF32( const double brightness =
output_data, output_width_, output_height_, SampleBrightnessF32(output_data, output_width_, output_height_,
output_width_ * 4 * int(sizeof(float))); output_width_ * 4 * int(sizeof(float)));
EXPECT_GT(brightness, 0.01) EXPECT_GT(brightness, 0.01)
<< "Worker output frame is black (brightness=" << brightness << ")"; << "Worker output frame is black (brightness=" << brightness << ")";
SaveFrameAsPng(output_data, output_width_, output_height_, SaveFrameAsPng(
temp_dir_.filePath(QStringLiteral("worker_output_vulkan.png"))); output_data, output_width_, output_height_,
temp_dir_.filePath(QStringLiteral("worker_output_vulkan.png")));
QFile::remove(QStringLiteral("/tmp/worker_output_vulkan.png")); QFile::remove(QStringLiteral("/tmp/worker_output_vulkan.png"));
QFile::copy(temp_dir_.filePath(QStringLiteral("worker_output_vulkan.png")), QFile::copy(temp_dir_.filePath(QStringLiteral("worker_output_vulkan.png")),
QStringLiteral("/tmp/worker_output_vulkan.png")); QStringLiteral("/tmp/worker_output_vulkan.png"));
std::cerr << "Vulkan output copied to /tmp/worker_output_vulkan.png" << std::endl; std::cerr << "Vulkan output copied to /tmp/worker_output_vulkan.png"
<< std::endl;
output_pool_->Release(consumed_slot); output_pool_->Release(consumed_slot);
} }
@@ -507,19 +519,20 @@ TEST_F(RenderWorkerFootageTest, OpenGLFootageIsNotBlack)
ASSERT_EQ(int(consumed_slot), output_slot); ASSERT_EQ(int(consumed_slot), output_slot);
const void *output_data = output_pool_->SlotData(consumed_slot); const void *output_data = output_pool_->SlotData(consumed_slot);
const double brightness = SampleBrightnessF32( const double brightness =
output_data, output_width_, output_height_, SampleBrightnessF32(output_data, output_width_, output_height_,
output_width_ * 4 * int(sizeof(float))); output_width_ * 4 * int(sizeof(float)));
EXPECT_GT(brightness, 0.01) EXPECT_GT(brightness, 0.01)
<< "Worker output frame is black (brightness=" << brightness << ")"; << "Worker output frame is black (brightness=" << brightness << ")";
SaveFrameAsPng(output_data, output_width_, output_height_, SaveFrameAsPng(
temp_dir_.filePath(QStringLiteral("worker_output_opengl.png"))); output_data, output_width_, output_height_,
temp_dir_.filePath(QStringLiteral("worker_output_opengl.png")));
QFile::remove(QStringLiteral("/tmp/worker_output_opengl.png")); QFile::remove(QStringLiteral("/tmp/worker_output_opengl.png"));
QFile::copy(temp_dir_.filePath(QStringLiteral("worker_output_opengl.png")), QFile::copy(temp_dir_.filePath(QStringLiteral("worker_output_opengl.png")),
QStringLiteral("/tmp/worker_output_opengl.png")); QStringLiteral("/tmp/worker_output_opengl.png"));
std::cerr << "OpenGL output copied to /tmp/worker_output_opengl.png" << std::endl; std::cerr << "OpenGL output copied to /tmp/worker_output_opengl.png"
<< std::endl;
output_pool_->Release(consumed_slot); output_pool_->Release(consumed_slot);
} }
+2 -2
View File
@@ -51,8 +51,8 @@ TEST(Shaders, ResourcesAvailable)
for (const QString &path : shader_paths) { for (const QString &path : shader_paths) {
QFile file(path); QFile file(path);
ASSERT_TRUE(file.exists()) << "Missing shader resource: " ASSERT_TRUE(file.exists())
<< path.toStdString(); << "Missing shader resource: " << path.toStdString();
ASSERT_TRUE(file.open(QIODevice::ReadOnly)) ASSERT_TRUE(file.open(QIODevice::ReadOnly))
<< "Failed to open shader resource: " << path.toStdString(); << "Failed to open shader resource: " << path.toStdString();
const QByteArray contents = file.readAll(); const QByteArray contents = file.readAll();
+4 -4
View File
@@ -5,7 +5,8 @@
#include "task/taskmanager.h" #include "task/taskmanager.h"
namespace { namespace
{
class DummyTask final : public olive::Task { class DummyTask final : public olive::Task {
public: public:
explicit DummyTask(bool *ran) explicit DummyTask(bool *ran)
@@ -74,9 +75,8 @@ TEST(TaskManager, AddAndRunTask)
DummyTask *task = new DummyTask(&ran); DummyTask *task = new DummyTask(&ran);
QEventLoop loop; QEventLoop loop;
QObject::connect(task, &olive::Task::Finished, &loop, [&loop](olive::Task *, bool) { QObject::connect(task, &olive::Task::Finished, &loop,
loop.quit(); [&loop](olive::Task *, bool) { loop.quit(); });
});
mgr->AddTask(task); mgr->AddTask(task);
+13 -14
View File
@@ -36,8 +36,8 @@ TEST(TimecodeMetadata, ParsesDropFrameTimecode)
TEST(TimecodeMetadata, ParsesBwfTimeReference) TEST(TimecodeMetadata, ParsesBwfTimeReference)
{ {
const olive::TimecodeMetadata::SourceTime parsed = const olive::TimecodeMetadata::SourceTime parsed =
olive::TimecodeMetadata::FromBwfTimeReference( olive::TimecodeMetadata::FromBwfTimeReference(QStringLiteral("96000"),
QStringLiteral("96000"), 48000); 48000);
ASSERT_TRUE(parsed.valid); ASSERT_TRUE(parsed.valid);
EXPECT_EQ(parsed.source, QStringLiteral("bwf_time_reference")); EXPECT_EQ(parsed.source, QStringLiteral("bwf_time_reference"));
@@ -63,20 +63,20 @@ TEST(TimecodeMetadata, RejectsInvalidMetadata)
EXPECT_FALSE(olive::TimecodeMetadata::FromBwfTimeReference( EXPECT_FALSE(olive::TimecodeMetadata::FromBwfTimeReference(
QStringLiteral("not-a-number"), 48000) QStringLiteral("not-a-number"), 48000)
.valid); .valid);
EXPECT_FALSE(olive::TimecodeMetadata::FromBwfTimeReference( EXPECT_FALSE(
QStringLiteral("123"), 0) olive::TimecodeMetadata::FromBwfTimeReference(QStringLiteral("123"), 0)
.valid); .valid);
EXPECT_FALSE(olive::TimecodeMetadata::FromTimecodeString( EXPECT_FALSE(
QStringLiteral("not-a-timecode"), olive::TimecodeMetadata::FromTimecodeString(
olive::core::rational(1, 24)) QStringLiteral("not-a-timecode"), olive::core::rational(1, 24))
.valid); .valid);
} }
TEST(TimecodeMetadata, FromBwfTimeReferenceZeroSampleRateIsInvalid) TEST(TimecodeMetadata, FromBwfTimeReferenceZeroSampleRateIsInvalid)
{ {
EXPECT_FALSE(olive::TimecodeMetadata::FromBwfTimeReference( EXPECT_FALSE(
QStringLiteral("0"), 0) olive::TimecodeMetadata::FromBwfTimeReference(QStringLiteral("0"), 0)
.valid); .valid);
} }
TEST(TimecodeMetadata, FootageDescriptionWithoutSourceStartTime) TEST(TimecodeMetadata, FootageDescriptionWithoutSourceStartTime)
@@ -113,8 +113,7 @@ TEST(TimecodeMetadata, FootagePersistsSourceStartTime)
writer.writeStartElement(QStringLiteral("custom")); writer.writeStartElement(QStringLiteral("custom"));
writer.writeTextElement(QStringLiteral("timestamp"), QStringLiteral("0")); writer.writeTextElement(QStringLiteral("timestamp"), QStringLiteral("0"));
writer.writeStartElement(QStringLiteral("sourcestarttime")); writer.writeStartElement(QStringLiteral("sourcestarttime"));
writer.writeAttribute(QStringLiteral("source"), writer.writeAttribute(QStringLiteral("source"), QStringLiteral("timecode"));
QStringLiteral("timecode"));
writer.writeCharacters(QStringLiteral("3600/1")); writer.writeCharacters(QStringLiteral("3600/1"));
writer.writeEndElement(); writer.writeEndElement();
writer.writeEndElement(); writer.writeEndElement();
+8 -4
View File
@@ -52,13 +52,17 @@ TEST(TimelineCoordinate, CopyAndAssignment)
TEST(TimelineCoordinate, Equality) TEST(TimelineCoordinate, Equality)
{ {
olive::TimelineCoordinate a(olive::core::rational(5, 1), olive::TimelineCoordinate a(olive::core::rational(5, 1),
olive::Track::Reference(olive::Track::kVideo, 1)); olive::Track::Reference(olive::Track::kVideo,
1));
olive::TimelineCoordinate b(olive::core::rational(5, 1), olive::TimelineCoordinate b(olive::core::rational(5, 1),
olive::Track::Reference(olive::Track::kVideo, 1)); olive::Track::Reference(olive::Track::kVideo,
1));
olive::TimelineCoordinate c(olive::core::rational(6, 1), olive::TimelineCoordinate c(olive::core::rational(6, 1),
olive::Track::Reference(olive::Track::kVideo, 1)); olive::Track::Reference(olive::Track::kVideo,
1));
olive::TimelineCoordinate d(olive::core::rational(5, 1), olive::TimelineCoordinate d(olive::core::rational(5, 1),
olive::Track::Reference(olive::Track::kAudio, 1)); olive::Track::Reference(olive::Track::kAudio,
1));
EXPECT_EQ(a.GetFrame(), b.GetFrame()); EXPECT_EQ(a.GetFrame(), b.GetFrame());
EXPECT_EQ(a.GetTrack(), b.GetTrack()); EXPECT_EQ(a.GetTrack(), b.GetTrack());
+11 -18
View File
@@ -51,20 +51,17 @@ TEST(TimelineMarkerList, OrderAndLookup)
1, 1,
olive::core::TimeRange(olive::core::rational(10, 1), olive::core::TimeRange(olive::core::rational(10, 1),
olive::core::rational(10, 1)), olive::core::rational(10, 1)),
QStringLiteral("A"), QStringLiteral("A"), &list);
&list);
olive::TimelineMarker marker_b( olive::TimelineMarker marker_b(
2, 2,
olive::core::TimeRange(olive::core::rational(5, 1), olive::core::TimeRange(olive::core::rational(5, 1),
olive::core::rational(5, 1)), olive::core::rational(5, 1)),
QStringLiteral("B"), QStringLiteral("B"), &list);
&list);
olive::TimelineMarker marker_c( olive::TimelineMarker marker_c(
3, 3,
olive::core::TimeRange(olive::core::rational(20, 1), olive::core::TimeRange(olive::core::rational(20, 1),
olive::core::rational(20, 1)), olive::core::rational(20, 1)),
QStringLiteral("C"), QStringLiteral("C"), &list);
&list);
ASSERT_EQ(list.size(), 3); ASSERT_EQ(list.size(), 3);
auto it = list.cbegin(); auto it = list.cbegin();
@@ -85,7 +82,8 @@ TEST(TimelineMarkerList, GetMarkerAtTimeReturnsNullWhenEmpty)
{ {
olive::TimelineMarkerList list; olive::TimelineMarkerList list;
EXPECT_EQ(list.GetMarkerAtTime(olive::core::rational(10, 1)), nullptr); EXPECT_EQ(list.GetMarkerAtTime(olive::core::rational(10, 1)), nullptr);
EXPECT_EQ(list.GetClosestMarkerToTime(olive::core::rational(10, 1)), nullptr); EXPECT_EQ(list.GetClosestMarkerToTime(olive::core::rational(10, 1)),
nullptr);
} }
TEST(TimelineMarkerList, SaveLoadWithUnknownElements) TEST(TimelineMarkerList, SaveLoadWithUnknownElements)
@@ -95,8 +93,7 @@ TEST(TimelineMarkerList, SaveLoadWithUnknownElements)
4, 4,
olive::core::TimeRange(olive::core::rational(12, 1), olive::core::TimeRange(olive::core::rational(12, 1),
olive::core::rational(15, 1)), olive::core::rational(15, 1)),
QStringLiteral("Span"), QStringLiteral("Span"), &list);
&list);
QByteArray xml; QByteArray xml;
QBuffer buffer(&xml); QBuffer buffer(&xml);
@@ -130,8 +127,7 @@ TEST(TimelineMarkerCommands, AddRemoveAndChange)
&list, &list,
olive::core::TimeRange(olive::core::rational(1, 1), olive::core::TimeRange(olive::core::rational(1, 1),
olive::core::rational(2, 1)), olive::core::rational(2, 1)),
QStringLiteral("One"), QStringLiteral("One"), 1);
1);
add.redo_now(); add.redo_now();
ASSERT_EQ(list.size(), 1); ASSERT_EQ(list.size(), 1);
auto *marker = list.front(); auto *marker = list.front();
@@ -159,14 +155,12 @@ TEST(TimelineMarkerCommands, AddRemoveAndChange)
2, 2,
olive::core::TimeRange(olive::core::rational(5, 1), olive::core::TimeRange(olive::core::rational(5, 1),
olive::core::rational(5, 1)), olive::core::rational(5, 1)),
QStringLiteral("Two"), QStringLiteral("Two"), &list);
&list);
EXPECT_EQ(list.front()->time().in(), olive::core::rational(1, 1)); EXPECT_EQ(list.front()->time().in(), olive::core::rational(1, 1));
olive::MarkerChangeTimeCommand move( olive::MarkerChangeTimeCommand move(
marker, marker, olive::core::TimeRange(olive::core::rational(0, 1),
olive::core::TimeRange(olive::core::rational(0, 1), olive::core::rational(0, 1)));
olive::core::rational(0, 1)));
move.redo_now(); move.redo_now();
EXPECT_EQ(list.front(), marker); EXPECT_EQ(list.front(), marker);
move.undo_now(); move.undo_now();
@@ -180,8 +174,7 @@ TEST(TimelineMarkerCommands, AddCommandUndo)
&list, &list,
olive::core::TimeRange(olive::core::rational(5, 1), olive::core::TimeRange(olive::core::rational(5, 1),
olive::core::rational(5, 1)), olive::core::rational(5, 1)),
QStringLiteral("UndoMe"), QStringLiteral("UndoMe"), 2);
2);
add.redo_now(); add.redo_now();
EXPECT_EQ(list.size(), 1); EXPECT_EQ(list.size(), 1);
add.undo_now(); add.undo_now();
+8 -9
View File
@@ -24,7 +24,8 @@ extern "C" {
using namespace olive; using namespace olive;
using namespace olive::core; using namespace olive::core;
namespace { namespace
{
AudioParams MakeMonoParams(int sample_rate) AudioParams MakeMonoParams(int sample_rate)
{ {
@@ -55,17 +56,16 @@ void WritePartialWaveform(AudioWaveformCache *cache, int sample_rate)
waveform.OverwriteSamples(buf, sample_rate, rational(1)); waveform.OverwriteSamples(buf, sample_rate, rational(1));
// Tell the cache that only the middle second is valid in a 3-second clip. // Tell the cache that only the middle second is valid in a 3-second clip.
cache->WriteWaveform(TimeRange(1, 2), cache->WriteWaveform(TimeRange(1, 2), TimeRangeList({ TimeRange(1, 2) }),
TimeRangeList({ TimeRange(1, 2) }),
&waveform); &waveform);
} }
} // namespace } // namespace
TEST(TimelineWaveformSync, ExtractEnvelopeUsesOnlyValidatedRanges) TEST(TimelineWaveformSync, ExtractEnvelopeUsesOnlyValidatedRanges)
{ {
constexpr int kSampleRate = 48000; constexpr int kSampleRate = 48000;
constexpr size_t kWindowSamples = kSampleRate / 20; // 50 ms windows constexpr size_t kWindowSamples = kSampleRate / 20; // 50 ms windows
AudioWaveformCache cache; AudioWaveformCache cache;
WritePartialWaveform(&cache, kSampleRate); WritePartialWaveform(&cache, kSampleRate);
@@ -76,8 +76,8 @@ TEST(TimelineWaveformSync, ExtractEnvelopeUsesOnlyValidatedRanges)
clip.sample_rate = kSampleRate; clip.sample_rate = kSampleRate;
const QVector<double> envelope = const QVector<double> envelope =
TimelineWaveformSync::ExtractWaveformCacheEnvelope( TimelineWaveformSync::ExtractWaveformCacheEnvelope(clip, kSampleRate,
clip, kSampleRate, kWindowSamples); kWindowSamples);
// 3 seconds at 20 windows per second == 60 windows. // 3 seconds at 20 windows per second == 60 windows.
EXPECT_EQ(envelope.size(), 60); EXPECT_EQ(envelope.size(), 60);
@@ -134,8 +134,7 @@ TEST(TimelineWaveformSync, EmptyCacheIsNotReady)
clip.set_length_and_media_out(rational(3)); clip.set_length_and_media_out(rational(3));
clip.set_media_in(rational(0)); clip.set_media_in(rational(0));
Node::ConnectEdge( Node::ConnectEdge(&footage, NodeInput(&clip, ClipBlock::kBufferIn));
&footage, NodeInput(&clip, ClipBlock::kBufferIn));
WaveformSyncClip out; WaveformSyncClip out;
EXPECT_FALSE(TimelineWaveformSync::GetWaveformSyncClip(&clip, &out)); EXPECT_FALSE(TimelineWaveformSync::GetWaveformSyncClip(&clip, &out));
+5 -5
View File
@@ -28,7 +28,7 @@ TEST(TimelineWorkArea, SaveLoadRoundTrip)
olive::TimelineWorkArea workarea; olive::TimelineWorkArea workarea;
workarea.set_enabled(true); workarea.set_enabled(true);
workarea.set_range(olive::core::TimeRange(olive::core::rational(2, 1), workarea.set_range(olive::core::TimeRange(olive::core::rational(2, 1),
olive::core::rational(6, 1))); olive::core::rational(6, 1)));
QByteArray xml; QByteArray xml;
QBuffer buffer(&xml); QBuffer buffer(&xml);
@@ -51,7 +51,7 @@ TEST(TimelineWorkArea, SaveLoadRoundTrip)
EXPECT_TRUE(loaded.enabled()); EXPECT_TRUE(loaded.enabled());
EXPECT_EQ(loaded.range(), EXPECT_EQ(loaded.range(),
olive::core::TimeRange(olive::core::rational(2, 1), olive::core::TimeRange(olive::core::rational(2, 1),
olive::core::rational(6, 1))); olive::core::rational(6, 1)));
} }
TEST(TimelineWorkArea, DisabledWorkAreaRoundTrip) TEST(TimelineWorkArea, DisabledWorkAreaRoundTrip)
@@ -59,7 +59,7 @@ TEST(TimelineWorkArea, DisabledWorkAreaRoundTrip)
olive::TimelineWorkArea workarea; olive::TimelineWorkArea workarea;
workarea.set_enabled(false); workarea.set_enabled(false);
workarea.set_range(olive::core::TimeRange(olive::core::rational(0, 1), workarea.set_range(olive::core::TimeRange(olive::core::rational(0, 1),
olive::core::rational(10, 1))); olive::core::rational(10, 1)));
QByteArray xml; QByteArray xml;
QBuffer buffer(&xml); QBuffer buffer(&xml);
@@ -82,14 +82,14 @@ TEST(TimelineWorkArea, DisabledWorkAreaRoundTrip)
EXPECT_FALSE(loaded.enabled()); EXPECT_FALSE(loaded.enabled());
EXPECT_EQ(loaded.range(), EXPECT_EQ(loaded.range(),
olive::core::TimeRange(olive::core::rational(0, 1), olive::core::TimeRange(olive::core::rational(0, 1),
olive::core::rational(10, 1))); olive::core::rational(10, 1)));
} }
TEST(TimelineWorkArea, SetRangeUpdatesInOut) TEST(TimelineWorkArea, SetRangeUpdatesInOut)
{ {
olive::TimelineWorkArea workarea; olive::TimelineWorkArea workarea;
workarea.set_range(olive::core::TimeRange(olive::core::rational(3, 1), workarea.set_range(olive::core::TimeRange(olive::core::rational(3, 1),
olive::core::rational(8, 1))); olive::core::rational(8, 1)));
EXPECT_EQ(workarea.in(), olive::core::rational(3, 1)); EXPECT_EQ(workarea.in(), olive::core::rational(3, 1));
EXPECT_EQ(workarea.out(), olive::core::rational(8, 1)); EXPECT_EQ(workarea.out(), olive::core::rational(8, 1));
+2 -1
View File
@@ -5,7 +5,8 @@
#include "undo/undostack.h" #include "undo/undostack.h"
#include "undo/undocommand.h" #include "undo/undocommand.h"
namespace { namespace
{
class TestCommand final : public olive::UndoCommand { class TestCommand final : public olive::UndoCommand {
public: public:
explicit TestCommand(int *value) explicit TestCommand(int *value)
+374 -368
View File
@@ -25,9 +25,12 @@
using namespace olive; using namespace olive;
using namespace olive::core; using namespace olive::core;
namespace olive { namespace olive
namespace viewer { {
namespace test { namespace viewer
{
namespace test
{
// ============================================================================ // ============================================================================
// Smoke Test: ViewerPlaybackTimer // Smoke Test: ViewerPlaybackTimer
@@ -35,92 +38,92 @@ namespace test {
TEST(ViewerSmokeTimer, DefaultConstruction) TEST(ViewerSmokeTimer, DefaultConstruction)
{ {
ViewerPlaybackTimer timer; ViewerPlaybackTimer timer;
// Timer should be in a valid but not-started state // Timer should be in a valid but not-started state
// After Start() is called, it should return valid timestamps // After Start() is called, it should return valid timestamps
} }
TEST(ViewerSmokeTimer, BasicTiming) TEST(ViewerSmokeTimer, BasicTiming)
{ {
ViewerPlaybackTimer timer; ViewerPlaybackTimer timer;
// Start at timestamp 0, 1x speed, 24fps (timebase = 1/24) // Start at timestamp 0, 1x speed, 24fps (timebase = 1/24)
timer.Start(0, 1, 1.0 / 24.0); timer.Start(0, 1, 1.0 / 24.0);
// Immediately get timestamp (should be close to 0) // Immediately get timestamp (should be close to 0)
int64_t ts = timer.GetTimestampNow(); int64_t ts = timer.GetTimestampNow();
EXPECT_GE(ts, 0); EXPECT_GE(ts, 0);
// Wait a bit and check timestamp has increased // Wait a bit and check timestamp has increased
QThread::msleep(50); // 50ms QThread::msleep(50); // 50ms
int64_t ts2 = timer.GetTimestampNow(); int64_t ts2 = timer.GetTimestampNow();
// At 24fps, 50ms should be approximately 1 frame (or slightly more) // At 24fps, 50ms should be approximately 1 frame (or slightly more)
// Allow for some timing variance // Allow for some timing variance
EXPECT_GE(ts2, ts); EXPECT_GE(ts2, ts);
} }
TEST(ViewerSmokeTimer, PlaybackSpeedForward) TEST(ViewerSmokeTimer, PlaybackSpeedForward)
{ {
ViewerPlaybackTimer timer; ViewerPlaybackTimer timer;
// Start at timestamp 100, 2x speed, 30fps // Start at timestamp 100, 2x speed, 30fps
timer.Start(100, 2, 1.0 / 30.0); timer.Start(100, 2, 1.0 / 30.0);
int64_t ts1 = timer.GetTimestampNow(); int64_t ts1 = timer.GetTimestampNow();
QThread::msleep(50); QThread::msleep(50);
int64_t ts2 = timer.GetTimestampNow(); int64_t ts2 = timer.GetTimestampNow();
// At 2x speed, time should advance twice as fast // At 2x speed, time should advance twice as fast
EXPECT_GT(ts2, ts1); EXPECT_GT(ts2, ts1);
} }
TEST(ViewerSmokeTimer, PlaybackSpeedReverse) TEST(ViewerSmokeTimer, PlaybackSpeedReverse)
{ {
ViewerPlaybackTimer timer; ViewerPlaybackTimer timer;
// Start at timestamp 1000, -1x speed (reverse), 24fps // Start at timestamp 1000, -1x speed (reverse), 24fps
timer.Start(1000, -1, 1.0 / 24.0); timer.Start(1000, -1, 1.0 / 24.0);
int64_t ts1 = timer.GetTimestampNow(); int64_t ts1 = timer.GetTimestampNow();
QThread::msleep(50); QThread::msleep(50);
int64_t ts2 = timer.GetTimestampNow(); int64_t ts2 = timer.GetTimestampNow();
// In reverse, timestamp should decrease // In reverse, timestamp should decrease
EXPECT_LT(ts2, ts1); EXPECT_LT(ts2, ts1);
} }
TEST(ViewerSmokeTimer, DifferentTimebases) TEST(ViewerSmokeTimer, DifferentTimebases)
{ {
ViewerPlaybackTimer timer; ViewerPlaybackTimer timer;
// Test with 24fps // Test with 24fps
timer.Start(0, 1, 1.0 / 24.0); timer.Start(0, 1, 1.0 / 24.0);
QThread::msleep(100); QThread::msleep(100);
int64_t ts24 = timer.GetTimestampNow(); int64_t ts24 = timer.GetTimestampNow();
// Test with 60fps // Test with 60fps
timer.Start(0, 1, 1.0 / 60.0); timer.Start(0, 1, 1.0 / 60.0);
QThread::msleep(100); QThread::msleep(100);
int64_t ts60 = timer.GetTimestampNow(); int64_t ts60 = timer.GetTimestampNow();
// At same real time, 60fps should have more frames than 24fps // At same real time, 60fps should have more frames than 24fps
EXPECT_GT(ts60, ts24); EXPECT_GT(ts60, ts24);
} }
TEST(ViewerSmokeTimer, ZeroSpeed) TEST(ViewerSmokeTimer, ZeroSpeed)
{ {
ViewerPlaybackTimer timer; ViewerPlaybackTimer timer;
// Start with 0 speed (paused) // Start with 0 speed (paused)
timer.Start(500, 0, 1.0 / 24.0); timer.Start(500, 0, 1.0 / 24.0);
int64_t ts1 = timer.GetTimestampNow(); int64_t ts1 = timer.GetTimestampNow();
QThread::msleep(50); QThread::msleep(50);
int64_t ts2 = timer.GetTimestampNow(); int64_t ts2 = timer.GetTimestampNow();
// With 0 speed, timestamp should not change // With 0 speed, timestamp should not change
EXPECT_EQ(ts1, ts2); EXPECT_EQ(ts1, ts2);
} }
// ============================================================================ // ============================================================================
@@ -129,121 +132,121 @@ TEST(ViewerSmokeTimer, ZeroSpeed)
TEST(ViewerSmokeQueue, DefaultConstruction) TEST(ViewerSmokeQueue, DefaultConstruction)
{ {
ViewerQueue queue; ViewerQueue queue;
EXPECT_TRUE(queue.empty()); EXPECT_TRUE(queue.empty());
} }
TEST(ViewerSmokeQueue, AppendForwardPlayback) TEST(ViewerSmokeQueue, AppendForwardPlayback)
{ {
ViewerQueue queue; ViewerQueue queue;
// Append frames for forward playback // Append frames for forward playback
ViewerPlaybackFrame frame1{rational(0), QVariant()}; ViewerPlaybackFrame frame1{ rational(0), QVariant() };
ViewerPlaybackFrame frame2{rational(1, 24), QVariant()}; ViewerPlaybackFrame frame2{ rational(1, 24), QVariant() };
ViewerPlaybackFrame frame3{rational(2, 24), QVariant()}; ViewerPlaybackFrame frame3{ rational(2, 24), QVariant() };
queue.AppendTimewise(frame1, 1); // speed = 1 (forward) queue.AppendTimewise(frame1, 1); // speed = 1 (forward)
queue.AppendTimewise(frame2, 1); queue.AppendTimewise(frame2, 1);
queue.AppendTimewise(frame3, 1); queue.AppendTimewise(frame3, 1);
EXPECT_EQ(queue.size(), 3); EXPECT_EQ(queue.size(), 3);
// Verify order (should be chronological for forward playback) // Verify order (should be chronological for forward playback)
auto it = queue.begin(); auto it = queue.begin();
EXPECT_EQ(it->timestamp, rational(0)); EXPECT_EQ(it->timestamp, rational(0));
++it; ++it;
EXPECT_EQ(it->timestamp, rational(1, 24)); EXPECT_EQ(it->timestamp, rational(1, 24));
++it; ++it;
EXPECT_EQ(it->timestamp, rational(2, 24)); EXPECT_EQ(it->timestamp, rational(2, 24));
} }
TEST(ViewerSmokeQueue, AppendReversePlayback) TEST(ViewerSmokeQueue, AppendReversePlayback)
{ {
ViewerQueue queue; ViewerQueue queue;
// Append frames for reverse playback // Append frames for reverse playback
ViewerPlaybackFrame frame1{rational(2, 24), QVariant()}; ViewerPlaybackFrame frame1{ rational(2, 24), QVariant() };
ViewerPlaybackFrame frame2{rational(1, 24), QVariant()}; ViewerPlaybackFrame frame2{ rational(1, 24), QVariant() };
ViewerPlaybackFrame frame3{rational(0), QVariant()}; ViewerPlaybackFrame frame3{ rational(0), QVariant() };
queue.AppendTimewise(frame1, -1); // speed = -1 (reverse) queue.AppendTimewise(frame1, -1); // speed = -1 (reverse)
queue.AppendTimewise(frame2, -1); queue.AppendTimewise(frame2, -1);
queue.AppendTimewise(frame3, -1); queue.AppendTimewise(frame3, -1);
EXPECT_EQ(queue.size(), 3); EXPECT_EQ(queue.size(), 3);
// Verify order (should be reverse chronological for reverse playback) // Verify order (should be reverse chronological for reverse playback)
auto it = queue.begin(); auto it = queue.begin();
EXPECT_EQ(it->timestamp, rational(2, 24)); EXPECT_EQ(it->timestamp, rational(2, 24));
++it; ++it;
EXPECT_EQ(it->timestamp, rational(1, 24)); EXPECT_EQ(it->timestamp, rational(1, 24));
++it; ++it;
EXPECT_EQ(it->timestamp, rational(0)); EXPECT_EQ(it->timestamp, rational(0));
} }
TEST(ViewerSmokeQueue, InsertOutOfOrder) TEST(ViewerSmokeQueue, InsertOutOfOrder)
{ {
ViewerQueue queue; ViewerQueue queue;
// Insert frames out of order for forward playback // Insert frames out of order for forward playback
ViewerPlaybackFrame frame1{rational(0), QVariant()}; ViewerPlaybackFrame frame1{ rational(0), QVariant() };
ViewerPlaybackFrame frame2{rational(2, 24), QVariant()}; ViewerPlaybackFrame frame2{ rational(2, 24), QVariant() };
ViewerPlaybackFrame frame3{rational(1, 24), QVariant()}; // Middle frame ViewerPlaybackFrame frame3{ rational(1, 24), QVariant() }; // Middle frame
queue.AppendTimewise(frame1, 1); queue.AppendTimewise(frame1, 1);
queue.AppendTimewise(frame2, 1); queue.AppendTimewise(frame2, 1);
queue.AppendTimewise(frame3, 1); // Should insert in middle queue.AppendTimewise(frame3, 1); // Should insert in middle
EXPECT_EQ(queue.size(), 3); EXPECT_EQ(queue.size(), 3);
// Verify correct order // Verify correct order
auto it = queue.begin(); auto it = queue.begin();
EXPECT_EQ(it->timestamp, rational(0)); EXPECT_EQ(it->timestamp, rational(0));
++it; ++it;
EXPECT_EQ(it->timestamp, rational(1, 24)); EXPECT_EQ(it->timestamp, rational(1, 24));
++it; ++it;
EXPECT_EQ(it->timestamp, rational(2, 24)); EXPECT_EQ(it->timestamp, rational(2, 24));
} }
TEST(ViewerSmokeQueue, PurgeBefore) TEST(ViewerSmokeQueue, PurgeBefore)
{ {
ViewerQueue queue; ViewerQueue queue;
// Add some frames // Add some frames
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
ViewerPlaybackFrame frame{rational(i, 24), QVariant()}; ViewerPlaybackFrame frame{ rational(i, 24), QVariant() };
queue.AppendTimewise(frame, 1); queue.AppendTimewise(frame, 1);
} }
EXPECT_EQ(queue.size(), 10); EXPECT_EQ(queue.size(), 10);
// Purge frames before 5/24 // Purge frames before 5/24
queue.PurgeBefore(rational(5, 24), 1); queue.PurgeBefore(rational(5, 24), 1);
// Should have 5 frames remaining (5, 6, 7, 8, 9) // Should have 5 frames remaining (5, 6, 7, 8, 9)
EXPECT_EQ(queue.size(), 5); EXPECT_EQ(queue.size(), 5);
EXPECT_EQ(queue.front().timestamp, rational(5, 24)); EXPECT_EQ(queue.front().timestamp, rational(5, 24));
} }
TEST(ViewerSmokeQueue, PurgeBeforeReverse) TEST(ViewerSmokeQueue, PurgeBeforeReverse)
{ {
ViewerQueue queue; ViewerQueue queue;
// Add frames for reverse playback (newest first) // Add frames for reverse playback (newest first)
for (int i = 9; i >= 0; i--) { for (int i = 9; i >= 0; i--) {
ViewerPlaybackFrame frame{rational(i, 24), QVariant()}; ViewerPlaybackFrame frame{ rational(i, 24), QVariant() };
queue.AppendTimewise(frame, -1); queue.AppendTimewise(frame, -1);
} }
EXPECT_EQ(queue.size(), 10); EXPECT_EQ(queue.size(), 10);
// In reverse playback, front() is the largest timestamp (9/24) // In reverse playback, front() is the largest timestamp (9/24)
// PurgeBefore with negative speed removes frames where front > time // PurgeBefore with negative speed removes frames where front > time
queue.PurgeBefore(rational(5, 24), -1); queue.PurgeBefore(rational(5, 24), -1);
// Should have frames 0-5 remaining (those <= 5/24) // Should have frames 0-5 remaining (those <= 5/24)
EXPECT_EQ(queue.size(), 6); EXPECT_EQ(queue.size(), 6);
EXPECT_EQ(queue.front().timestamp, rational(5, 24)); EXPECT_EQ(queue.front().timestamp, rational(5, 24));
} }
// ============================================================================ // ============================================================================
@@ -252,57 +255,57 @@ TEST(ViewerSmokeQueue, PurgeBeforeReverse)
TEST(ViewerSmokeSafeMargin, DefaultConstruction) TEST(ViewerSmokeSafeMargin, DefaultConstruction)
{ {
ViewerSafeMarginInfo info; ViewerSafeMarginInfo info;
EXPECT_FALSE(info.is_enabled()); EXPECT_FALSE(info.is_enabled());
EXPECT_FALSE(info.custom_ratio()); EXPECT_FALSE(info.custom_ratio());
EXPECT_DOUBLE_EQ(info.ratio(), 0.0); EXPECT_DOUBLE_EQ(info.ratio(), 0.0);
} }
TEST(ViewerSmokeSafeMargin, EnabledConstruction) TEST(ViewerSmokeSafeMargin, EnabledConstruction)
{ {
ViewerSafeMarginInfo info(true); ViewerSafeMarginInfo info(true);
EXPECT_TRUE(info.is_enabled()); EXPECT_TRUE(info.is_enabled());
EXPECT_FALSE(info.custom_ratio()); EXPECT_FALSE(info.custom_ratio());
} }
TEST(ViewerSmokeSafeMargin, CustomRatioConstruction) TEST(ViewerSmokeSafeMargin, CustomRatioConstruction)
{ {
ViewerSafeMarginInfo info(true, 0.9); ViewerSafeMarginInfo info(true, 0.9);
EXPECT_TRUE(info.is_enabled()); EXPECT_TRUE(info.is_enabled());
EXPECT_TRUE(info.custom_ratio()); EXPECT_TRUE(info.custom_ratio());
EXPECT_DOUBLE_EQ(info.ratio(), 0.9); EXPECT_DOUBLE_EQ(info.ratio(), 0.9);
} }
TEST(ViewerSmokeSafeMargin, EqualityOperators) TEST(ViewerSmokeSafeMargin, EqualityOperators)
{ {
ViewerSafeMarginInfo info1(true, 0.9); ViewerSafeMarginInfo info1(true, 0.9);
ViewerSafeMarginInfo info2(true, 0.9); ViewerSafeMarginInfo info2(true, 0.9);
ViewerSafeMarginInfo info3(false, 0.9); ViewerSafeMarginInfo info3(false, 0.9);
ViewerSafeMarginInfo info4(true, 0.8); ViewerSafeMarginInfo info4(true, 0.8);
EXPECT_TRUE(info1 == info2); EXPECT_TRUE(info1 == info2);
EXPECT_FALSE(info1 != info2); EXPECT_FALSE(info1 != info2);
EXPECT_FALSE(info1 == info3); // Different enabled state EXPECT_FALSE(info1 == info3); // Different enabled state
EXPECT_FALSE(info1 == info4); // Different ratio EXPECT_FALSE(info1 == info4); // Different ratio
EXPECT_TRUE(info1 != info3); EXPECT_TRUE(info1 != info3);
} }
TEST(ViewerSmokeSafeMargin, ZeroRatio) TEST(ViewerSmokeSafeMargin, ZeroRatio)
{ {
ViewerSafeMarginInfo info(true, 0.0); ViewerSafeMarginInfo info(true, 0.0);
EXPECT_TRUE(info.is_enabled()); EXPECT_TRUE(info.is_enabled());
EXPECT_FALSE(info.custom_ratio()); // 0 ratio means no custom ratio EXPECT_FALSE(info.custom_ratio()); // 0 ratio means no custom ratio
} }
TEST(ViewerSmokeSafeMargin, CopyConstruction) TEST(ViewerSmokeSafeMargin, CopyConstruction)
{ {
ViewerSafeMarginInfo original(true, 0.85); ViewerSafeMarginInfo original(true, 0.85);
ViewerSafeMarginInfo copy(original); ViewerSafeMarginInfo copy(original);
EXPECT_EQ(copy.is_enabled(), original.is_enabled()); EXPECT_EQ(copy.is_enabled(), original.is_enabled());
EXPECT_EQ(copy.custom_ratio(), original.custom_ratio()); EXPECT_EQ(copy.custom_ratio(), original.custom_ratio());
EXPECT_DOUBLE_EQ(copy.ratio(), original.ratio()); EXPECT_DOUBLE_EQ(copy.ratio(), original.ratio());
} }
// ============================================================================ // ============================================================================
@@ -311,30 +314,30 @@ TEST(ViewerSmokeSafeMargin, CopyConstruction)
TEST(ViewerSmokeAudioCache, DefaultConstruction) TEST(ViewerSmokeAudioCache, DefaultConstruction)
{ {
AudioPlaybackCache cache; AudioPlaybackCache cache;
// Should construct without crashing // Should construct without crashing
SUCCEED(); SUCCEED();
} }
TEST(ViewerSmokeAudioCache, ParameterSetters) TEST(ViewerSmokeAudioCache, ParameterSetters)
{ {
AudioPlaybackCache cache; AudioPlaybackCache cache;
AudioParams params(48000, AV_CH_LAYOUT_STEREO, SampleFormat::F32P); AudioParams params(48000, AV_CH_LAYOUT_STEREO, SampleFormat::F32P);
cache.SetParameters(params); cache.SetParameters(params);
// Parameters should be retrievable // Parameters should be retrievable
AudioParams retrieved = cache.GetParameters(); AudioParams retrieved = cache.GetParameters();
EXPECT_EQ(retrieved.sample_rate(), params.sample_rate()); EXPECT_EQ(retrieved.sample_rate(), params.sample_rate());
} }
TEST(ViewerSmokeAudioCache, ValidateWithRange) TEST(ViewerSmokeAudioCache, ValidateWithRange)
{ {
AudioPlaybackCache cache; AudioPlaybackCache cache;
// Initially no validated ranges // Initially no validated ranges
TimeRangeList validated = cache.GetValidatedRanges(); TimeRangeList validated = cache.GetValidatedRanges();
EXPECT_TRUE(validated.isEmpty()); EXPECT_TRUE(validated.isEmpty());
} }
// ============================================================================ // ============================================================================
@@ -346,32 +349,32 @@ TEST(ViewerSmokeAudioCache, ValidateWithRange)
TEST(ViewerSmokeAutoCacher, DISABLED_Construction) TEST(ViewerSmokeAutoCacher, DISABLED_Construction)
{ {
// PreviewAutoCacher requires full GUI environment // PreviewAutoCacher requires full GUI environment
SUCCEED(); SUCCEED();
} }
TEST(ViewerSmokeAutoCacher, DISABLED_SetPlayhead) TEST(ViewerSmokeAutoCacher, DISABLED_SetPlayhead)
{ {
// PreviewAutoCacher requires full GUI environment // PreviewAutoCacher requires full GUI environment
SUCCEED(); SUCCEED();
} }
TEST(ViewerSmokeAutoCacher, DISABLED_PauseControls) TEST(ViewerSmokeAutoCacher, DISABLED_PauseControls)
{ {
// PreviewAutoCacher requires full GUI environment // PreviewAutoCacher requires full GUI environment
SUCCEED(); SUCCEED();
} }
TEST(ViewerSmokeAutoCacher, DISABLED_SetIgnoreCacheRequests) TEST(ViewerSmokeAutoCacher, DISABLED_SetIgnoreCacheRequests)
{ {
// PreviewAutoCacher requires full GUI environment // PreviewAutoCacher requires full GUI environment
SUCCEED(); SUCCEED();
} }
TEST(ViewerSmokeAutoCacher, DISABLED_SetDisplayColorProcessor) TEST(ViewerSmokeAutoCacher, DISABLED_SetDisplayColorProcessor)
{ {
// PreviewAutoCacher requires full GUI environment // PreviewAutoCacher requires full GUI environment
SUCCEED(); SUCCEED();
} }
// ============================================================================ // ============================================================================
@@ -380,73 +383,73 @@ TEST(ViewerSmokeAutoCacher, DISABLED_SetDisplayColorProcessor)
TEST(ViewerSmokeRational, DefaultConstruction) TEST(ViewerSmokeRational, DefaultConstruction)
{ {
rational r; rational r;
EXPECT_EQ(r.numerator(), 0); EXPECT_EQ(r.numerator(), 0);
EXPECT_EQ(r.denominator(), 1); EXPECT_EQ(r.denominator(), 1);
} }
TEST(ViewerSmokeRational, ValueConstruction) TEST(ViewerSmokeRational, ValueConstruction)
{ {
rational r(24, 1); rational r(24, 1);
EXPECT_EQ(r.numerator(), 24); EXPECT_EQ(r.numerator(), 24);
EXPECT_EQ(r.denominator(), 1); EXPECT_EQ(r.denominator(), 1);
rational r2(1, 24); rational r2(1, 24);
EXPECT_EQ(r2.numerator(), 1); EXPECT_EQ(r2.numerator(), 1);
EXPECT_EQ(r2.denominator(), 24); EXPECT_EQ(r2.denominator(), 24);
} }
TEST(ViewerSmokeRational, ToDouble) TEST(ViewerSmokeRational, ToDouble)
{ {
rational r(1, 2); rational r(1, 2);
EXPECT_DOUBLE_EQ(r.toDouble(), 0.5); EXPECT_DOUBLE_EQ(r.toDouble(), 0.5);
rational r2(3, 4); rational r2(3, 4);
EXPECT_DOUBLE_EQ(r2.toDouble(), 0.75); EXPECT_DOUBLE_EQ(r2.toDouble(), 0.75);
} }
TEST(ViewerSmokeRational, Arithmetic) TEST(ViewerSmokeRational, Arithmetic)
{ {
rational r1(1, 2); rational r1(1, 2);
rational r2(1, 4); rational r2(1, 4);
rational sum = r1 + r2; rational sum = r1 + r2;
EXPECT_EQ(sum.numerator(), 3); EXPECT_EQ(sum.numerator(), 3);
EXPECT_EQ(sum.denominator(), 4); EXPECT_EQ(sum.denominator(), 4);
rational diff = r1 - r2; rational diff = r1 - r2;
EXPECT_EQ(diff.numerator(), 1); EXPECT_EQ(diff.numerator(), 1);
EXPECT_EQ(diff.denominator(), 4); EXPECT_EQ(diff.denominator(), 4);
} }
TEST(ViewerSmokeRational, Comparison) TEST(ViewerSmokeRational, Comparison)
{ {
rational r1(1, 2); rational r1(1, 2);
rational r2(2, 4); rational r2(2, 4);
rational r3(3, 4); rational r3(3, 4);
EXPECT_TRUE(r1 == r2); // Equivalent fractions EXPECT_TRUE(r1 == r2); // Equivalent fractions
EXPECT_FALSE(r1 == r3); EXPECT_FALSE(r1 == r3);
EXPECT_TRUE(r1 < r3); EXPECT_TRUE(r1 < r3);
EXPECT_TRUE(r3 > r1); EXPECT_TRUE(r3 > r1);
} }
TEST(ViewerSmokeRational, NullCheck) TEST(ViewerSmokeRational, NullCheck)
{ {
rational r; rational r;
EXPECT_TRUE(r.isNull()); // 0/1 is considered null EXPECT_TRUE(r.isNull()); // 0/1 is considered null
rational r2(1, 2); rational r2(1, 2);
EXPECT_FALSE(r2.isNull()); EXPECT_FALSE(r2.isNull());
} }
TEST(ViewerSmokeRational, Flipped) TEST(ViewerSmokeRational, Flipped)
{ {
rational r(24, 1); rational r(24, 1);
rational flipped = r.flipped(); rational flipped = r.flipped();
EXPECT_EQ(flipped.numerator(), 1); EXPECT_EQ(flipped.numerator(), 1);
EXPECT_EQ(flipped.denominator(), 24); EXPECT_EQ(flipped.denominator(), 24);
} }
// ============================================================================ // ============================================================================
@@ -455,58 +458,61 @@ TEST(ViewerSmokeRational, Flipped)
TEST(ViewerSmokeThread, ConcurrentTimerAccess) TEST(ViewerSmokeThread, ConcurrentTimerAccess)
{ {
const int num_threads = 4; const int num_threads = 4;
const int num_iterations = 100; const int num_iterations = 100;
ViewerPlaybackTimer timer; ViewerPlaybackTimer timer;
timer.Start(0, 1, 1.0 / 30.0); timer.Start(0, 1, 1.0 / 30.0);
std::vector<std::thread> threads; std::vector<std::thread> threads;
std::atomic<int> success_count{0}; std::atomic<int> success_count{ 0 };
for (int t = 0; t < num_threads; ++t) { for (int t = 0; t < num_threads; ++t) {
threads.emplace_back([&timer, &success_count, num_iterations]() { threads.emplace_back([&timer, &success_count, num_iterations]() {
for (int i = 0; i < num_iterations; ++i) { for (int i = 0; i < num_iterations; ++i) {
int64_t ts = timer.GetTimestampNow(); int64_t ts = timer.GetTimestampNow();
if (ts >= 0) { if (ts >= 0) {
success_count++; success_count++;
} }
} }
}); });
} }
for (auto &t : threads) { for (auto &t : threads) {
t.join(); t.join();
} }
EXPECT_EQ(success_count.load(), num_threads * num_iterations); EXPECT_EQ(success_count.load(), num_threads * num_iterations);
} }
TEST(ViewerSmokeThread, ConcurrentQueueAccess) TEST(ViewerSmokeThread, ConcurrentQueueAccess)
{ {
const int num_threads = 4; const int num_threads = 4;
const int num_frames_per_thread = 25; const int num_frames_per_thread = 25;
ViewerQueue queue; ViewerQueue queue;
std::vector<std::thread> threads; std::vector<std::thread> threads;
std::atomic<int> append_count{0}; std::atomic<int> append_count{ 0 };
for (int t = 0; t < num_threads; ++t) { for (int t = 0; t < num_threads; ++t) {
threads.emplace_back([&queue, &append_count, t, num_frames_per_thread]() { threads.emplace_back(
for (int i = 0; i < num_frames_per_thread; ++i) { [&queue, &append_count, t, num_frames_per_thread]() {
ViewerPlaybackFrame frame{rational(t * num_frames_per_thread + i, 24), QVariant()}; for (int i = 0; i < num_frames_per_thread; ++i) {
queue.AppendTimewise(frame, 1); ViewerPlaybackFrame frame{
append_count++; rational(t * num_frames_per_thread + i, 24), QVariant()
} };
}); queue.AppendTimewise(frame, 1);
} append_count++;
}
for (auto &t : threads) { });
t.join(); }
}
for (auto &t : threads) {
EXPECT_EQ(append_count.load(), num_threads * num_frames_per_thread); t.join();
EXPECT_EQ(queue.size(), num_threads * num_frames_per_thread); }
EXPECT_EQ(append_count.load(), num_threads * num_frames_per_thread);
EXPECT_EQ(queue.size(), num_threads * num_frames_per_thread);
} }
// ============================================================================ // ============================================================================
@@ -515,70 +521,70 @@ TEST(ViewerSmokeThread, ConcurrentQueueAccess)
TEST(ViewerSmokeIntegration, PlaybackSequenceSimulation) TEST(ViewerSmokeIntegration, PlaybackSequenceSimulation)
{ {
// Simulate a basic playback sequence // Simulate a basic playback sequence
ViewerPlaybackTimer timer; ViewerPlaybackTimer timer;
ViewerQueue queue; ViewerQueue queue;
// Start playback at frame 0, 24fps // Start playback at frame 0, 24fps
timer.Start(0, 1, 1.0 / 24.0); timer.Start(0, 1, 1.0 / 24.0);
// Queue some frames // Queue some frames
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
ViewerPlaybackFrame frame{rational(i, 24), QVariant(i)}; ViewerPlaybackFrame frame{ rational(i, 24), QVariant(i) };
queue.AppendTimewise(frame, 1); queue.AppendTimewise(frame, 1);
} }
// Get current timestamp // Get current timestamp
int64_t current_ts = timer.GetTimestampNow(); int64_t current_ts = timer.GetTimestampNow();
// Find frame closest to current time // Find frame closest to current time
rational current_time(current_ts, 1); rational current_time(current_ts, 1);
bool found = false; bool found = false;
for (const auto &frame : queue) { for (const auto &frame : queue) {
if (frame.timestamp >= current_time) { if (frame.timestamp >= current_time) {
found = true; found = true;
break; break;
} }
} }
// Should have frames available // Should have frames available
EXPECT_FALSE(queue.empty()); EXPECT_FALSE(queue.empty());
} }
TEST(ViewerSmokeIntegration, SafeMarginWithDifferentAspectRatios) TEST(ViewerSmokeIntegration, SafeMarginWithDifferentAspectRatios)
{ {
// Test safe margins for different aspect ratios // Test safe margins for different aspect ratios
std::vector<double> ratios = {0.9, 0.85, 0.8, 0.7}; std::vector<double> ratios = { 0.9, 0.85, 0.8, 0.7 };
for (double ratio : ratios) { for (double ratio : ratios) {
ViewerSafeMarginInfo info(true, ratio); ViewerSafeMarginInfo info(true, ratio);
EXPECT_TRUE(info.is_enabled()); EXPECT_TRUE(info.is_enabled());
EXPECT_TRUE(info.custom_ratio()); EXPECT_TRUE(info.custom_ratio());
EXPECT_DOUBLE_EQ(info.ratio(), ratio); EXPECT_DOUBLE_EQ(info.ratio(), ratio);
} }
} }
TEST(ViewerSmokeIntegration, ReversePlaybackScenario) TEST(ViewerSmokeIntegration, ReversePlaybackScenario)
{ {
ViewerPlaybackTimer timer; ViewerPlaybackTimer timer;
ViewerQueue queue; ViewerQueue queue;
// Start reverse playback from frame 100 // Start reverse playback from frame 100
timer.Start(100, -1, 1.0 / 24.0); timer.Start(100, -1, 1.0 / 24.0);
// Queue frames in reverse order // Queue frames in reverse order
for (int i = 100; i >= 90; i--) { for (int i = 100; i >= 90; i--) {
ViewerPlaybackFrame frame{rational(i, 24), QVariant(i)}; ViewerPlaybackFrame frame{ rational(i, 24), QVariant(i) };
queue.AppendTimewise(frame, -1); queue.AppendTimewise(frame, -1);
} }
// Get timestamps - should decrease // Get timestamps - should decrease
int64_t ts1 = timer.GetTimestampNow(); int64_t ts1 = timer.GetTimestampNow();
QThread::msleep(50); QThread::msleep(50);
int64_t ts2 = timer.GetTimestampNow(); int64_t ts2 = timer.GetTimestampNow();
EXPECT_LT(ts2, ts1); EXPECT_LT(ts2, ts1);
EXPECT_EQ(queue.front().timestamp, rational(100, 24)); EXPECT_EQ(queue.front().timestamp, rational(100, 24));
} }
} // namespace test } // namespace test