Expand gtest coverage across recently changed modules
Adds ~60 new unit tests covering the modules touched in recent bug-fix passes and UI refactors: - RenderTicket: result/finish-count semantics, empty watcher defaults, ticket-reuse rejection - Preferences tabs: behavior options migrated into General/Audio tabs, translation context helper, audio-tab initialization - NodeView: context management, 'Show in Parameter Editor' action wiring - PreviewAutoCacher: construction, pause toggles, single-frame render cancellation, force-cache range - SeekableWidget/TimeBasedWidget: scroll, marker editing, context reset - OCIOLutNode: empty/missing/unsupported LUT paths, string direction values, file-switching regression coverage - ProxyManager: working filename, disabled state, empty proxy fields - Frame/VideoParams/AudioParams/Timecode/ExportFormat/ExportCodec: additional edge-case and enumeration coverage Also fixes the PreferencesAudioTab test crash by initializing AudioManager in the offscreen test environment. All tests pass: ctest --output-on-failure
This commit is contained in:
@@ -17,6 +17,7 @@ add_executable(olive-gtest
|
||||
render_sampleformat_test.cpp
|
||||
render_pixelformat_test.cpp
|
||||
render_ipc_test.cpp
|
||||
render_ticket_test.cpp
|
||||
render_worker_footage_test.cpp
|
||||
project_serializer_test.cpp
|
||||
proxy_manager_test.cpp
|
||||
@@ -36,6 +37,9 @@ add_executable(olive-gtest
|
||||
plugin_render_pipeline_test.cpp
|
||||
plugin_renderer_readback_test.cpp
|
||||
plugin_ofx_integration_test.cpp
|
||||
preferences_behavior_tab_test.cpp
|
||||
node_view_test.cpp
|
||||
preview_autocacher_test.cpp
|
||||
codec_frame_test.cpp
|
||||
codec_decoder_test.cpp
|
||||
ffmpeg_decoder_hw_test.cpp
|
||||
@@ -51,6 +55,8 @@ add_executable(olive-gtest
|
||||
timeline_workarea_test.cpp
|
||||
)
|
||||
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Test)
|
||||
|
||||
target_sources(olive-gtest PRIVATE $<TARGET_OBJECTS:libolive-editor>)
|
||||
|
||||
target_include_directories(
|
||||
@@ -68,6 +74,7 @@ target_link_libraries(
|
||||
PRIVATE
|
||||
${OLIVE_LIBRARIES}
|
||||
GTest::gtest
|
||||
Qt${QT_VERSION_MAJOR}::Test
|
||||
)
|
||||
|
||||
target_compile_definitions(
|
||||
|
||||
@@ -18,3 +18,34 @@ TEST(CodecExportCodec, NamesAndFlags)
|
||||
EXPECT_TRUE(ExportCodec::IsCodecLossless(ExportCodec::kCodecFLAC));
|
||||
EXPECT_FALSE(ExportCodec::IsCodecLossless(ExportCodec::kCodecH265));
|
||||
}
|
||||
|
||||
TEST(CodecExportCodec, VideoCodecNamesAreNonEmpty)
|
||||
{
|
||||
using olive::ExportCodec;
|
||||
|
||||
for (int i = 0; i < ExportCodec::kCodecCount; ++i) {
|
||||
const auto codec = static_cast<ExportCodec::Codec>(i);
|
||||
const QString name = ExportCodec::GetCodecName(codec);
|
||||
EXPECT_FALSE(name.isEmpty()) << "Codec " << i;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CodecExportCodec, AudioCodecsAreNotStillImages)
|
||||
{
|
||||
using olive::ExportCodec;
|
||||
|
||||
EXPECT_FALSE(ExportCodec::IsCodecAStillImage(ExportCodec::kCodecPCM));
|
||||
EXPECT_FALSE(ExportCodec::IsCodecAStillImage(ExportCodec::kCodecAAC));
|
||||
EXPECT_FALSE(ExportCodec::IsCodecAStillImage(ExportCodec::kCodecFLAC));
|
||||
EXPECT_FALSE(ExportCodec::IsCodecAStillImage(ExportCodec::kCodecOpus));
|
||||
}
|
||||
|
||||
TEST(CodecExportCodec, LossyCodecsAreNotLossless)
|
||||
{
|
||||
using olive::ExportCodec;
|
||||
|
||||
EXPECT_FALSE(ExportCodec::IsCodecLossless(ExportCodec::kCodecH264));
|
||||
EXPECT_FALSE(ExportCodec::IsCodecLossless(ExportCodec::kCodecH265));
|
||||
EXPECT_FALSE(ExportCodec::IsCodecLossless(ExportCodec::kCodecVP9));
|
||||
EXPECT_FALSE(ExportCodec::IsCodecLossless(ExportCodec::kCodecAAC));
|
||||
}
|
||||
|
||||
@@ -15,6 +15,29 @@ TEST(CodecExportFormat, NamesAndExtensions)
|
||||
EXPECT_TRUE(ExportFormat::GetExtension(ExportFormat::kFormatCount).isEmpty());
|
||||
}
|
||||
|
||||
TEST(CodecExportFormat, AllFormatsHaveNames)
|
||||
{
|
||||
using olive::ExportFormat;
|
||||
|
||||
for (int i = 0; i < ExportFormat::kFormatCount; ++i) {
|
||||
const auto fmt = static_cast<ExportFormat::Format>(i);
|
||||
EXPECT_FALSE(ExportFormat::GetName(fmt).isEmpty()) << "Format " << i;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CodecExportFormat, AllFormatsHaveAtLeastOneCodecList)
|
||||
{
|
||||
using olive::ExportFormat;
|
||||
|
||||
for (int i = 0; i < ExportFormat::kFormatCount; ++i) {
|
||||
const auto fmt = static_cast<ExportFormat::Format>(i);
|
||||
EXPECT_FALSE(ExportFormat::GetVideoCodecs(fmt).isEmpty() &&
|
||||
ExportFormat::GetAudioCodecs(fmt).isEmpty() &&
|
||||
ExportFormat::GetSubtitleCodecs(fmt).isEmpty())
|
||||
<< "Format " << i;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(CodecExportFormat, CodecLists)
|
||||
{
|
||||
using olive::ExportCodec;
|
||||
@@ -42,3 +65,14 @@ TEST(CodecExportFormat, CodecLists)
|
||||
ExportFormat::GetAudioCodecs(ExportFormat::kFormatWAV);
|
||||
EXPECT_EQ(wav_audio, QList<ExportCodec::Codec>{ ExportCodec::kCodecPCM });
|
||||
}
|
||||
|
||||
TEST(CodecExportFormat, MPEG4ContainsH264AndAAC)
|
||||
{
|
||||
using olive::ExportCodec;
|
||||
using olive::ExportFormat;
|
||||
|
||||
EXPECT_TRUE(ExportFormat::GetVideoCodecs(ExportFormat::kFormatMPEG4Video)
|
||||
.contains(ExportCodec::kCodecH264));
|
||||
EXPECT_TRUE(ExportFormat::GetAudioCodecs(ExportFormat::kFormatMPEG4Audio)
|
||||
.contains(ExportCodec::kCodecAAC));
|
||||
}
|
||||
|
||||
@@ -8,4 +8,49 @@ TEST(CodecFrame, DefaultState)
|
||||
EXPECT_EQ(frame.width(), 0);
|
||||
EXPECT_EQ(frame.height(), 0);
|
||||
EXPECT_EQ(frame.format(), olive::core::PixelFormat::INVALID);
|
||||
EXPECT_FALSE(frame.is_allocated());
|
||||
EXPECT_EQ(frame.data(), nullptr);
|
||||
}
|
||||
|
||||
TEST(CodecFrame, CreateAllocatesForParams)
|
||||
{
|
||||
olive::VideoParams params(64, 32, olive::core::PixelFormat::U8,
|
||||
olive::VideoParams::kRGBAChannelCount);
|
||||
|
||||
olive::FramePtr frame = olive::Frame::Create();
|
||||
frame->set_video_params(params);
|
||||
frame->allocate();
|
||||
|
||||
EXPECT_TRUE(frame->is_allocated());
|
||||
EXPECT_NE(frame->data(), nullptr);
|
||||
EXPECT_EQ(frame->width(), 64);
|
||||
EXPECT_EQ(frame->height(), 32);
|
||||
EXPECT_EQ(frame->format(), olive::core::PixelFormat::U8);
|
||||
}
|
||||
|
||||
TEST(CodecFrame, AllocateMatchesLineSize)
|
||||
{
|
||||
olive::VideoParams params(64, 32, olive::core::PixelFormat::U8,
|
||||
olive::VideoParams::kRGBAChannelCount);
|
||||
|
||||
olive::FramePtr frame = olive::Frame::Create();
|
||||
frame->set_video_params(params);
|
||||
frame->allocate();
|
||||
|
||||
EXPECT_EQ(frame->linesize_bytes(), 64 * 4);
|
||||
EXPECT_EQ(frame->allocated_size(), 64 * 4 * 32);
|
||||
}
|
||||
|
||||
TEST(CodecFrame, DestroyDeallocatesData)
|
||||
{
|
||||
olive::FramePtr frame = olive::Frame::Create();
|
||||
frame->set_video_params(olive::VideoParams(
|
||||
8, 8, olive::core::PixelFormat::U8,
|
||||
olive::VideoParams::kRGBAChannelCount));
|
||||
frame->allocate();
|
||||
EXPECT_TRUE(frame->is_allocated());
|
||||
|
||||
frame->destroy();
|
||||
EXPECT_FALSE(frame->is_allocated());
|
||||
EXPECT_EQ(frame->data(), nullptr);
|
||||
}
|
||||
|
||||
+268
-38
@@ -408,7 +408,254 @@ TEST(ColorLutNode, SwitchingDirectionUpdatesProcessorAndPixels)
|
||||
EXPECT_GT(std::abs(forward_out.blue() - inverse_out.blue()), 0.1f);
|
||||
}
|
||||
|
||||
TEST(ColorLutNode, SwitchingFileUpdatesProcessorAndPixels)
|
||||
TEST(ColorLutNode, EmptyFilePathLeavesProcessorNull)
|
||||
{
|
||||
olive::ColorManager::SetUpDefaultConfig();
|
||||
|
||||
olive::Project project;
|
||||
project.Initialize();
|
||||
|
||||
auto *solid = new olive::SolidGenerator();
|
||||
solid->setParent(&project);
|
||||
solid->SetStandardValue(
|
||||
olive::SolidGenerator::kColorInput,
|
||||
QVariant::fromValue(olive::Color(0.25f, 0.50f, 0.75f, 1.0f)));
|
||||
|
||||
auto *lut = new olive::OCIOLutNode();
|
||||
lut->setParent(&project);
|
||||
lut->SetStandardValue(olive::OCIOLutNode::kFileInput, QString());
|
||||
|
||||
olive::Node::ConnectEdge(
|
||||
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
|
||||
|
||||
const olive::VideoParams params(
|
||||
16, 16, olive::core::PixelFormat::F32,
|
||||
olive::VideoParams::kRGBAChannelCount);
|
||||
|
||||
PixelColorTransformTraverser traverser;
|
||||
traverser.SetCacheVideoParams(params);
|
||||
olive::NodeValueTable table = traverser.GenerateTable(
|
||||
lut, olive::TimeRange(olive::core::rational(0),
|
||||
olive::core::rational(1, 30)));
|
||||
|
||||
// With an empty LUT path, the node should pass the input texture through
|
||||
// without producing a color-transform job.
|
||||
olive::NodeValue tex_val = table.Get(olive::NodeValue::kTexture);
|
||||
EXPECT_EQ(tex_val.type(), olive::NodeValue::kTexture);
|
||||
}
|
||||
|
||||
TEST(ColorLutNode, MissingFilePathLeavesProcessorNull)
|
||||
{
|
||||
olive::ColorManager::SetUpDefaultConfig();
|
||||
|
||||
olive::Project project;
|
||||
project.Initialize();
|
||||
|
||||
auto *solid = new olive::SolidGenerator();
|
||||
solid->setParent(&project);
|
||||
solid->SetStandardValue(
|
||||
olive::SolidGenerator::kColorInput,
|
||||
QVariant::fromValue(olive::Color(0.25f, 0.50f, 0.75f, 1.0f)));
|
||||
|
||||
auto *lut = new olive::OCIOLutNode();
|
||||
lut->setParent(&project);
|
||||
lut->SetStandardValue(olive::OCIOLutNode::kFileInput,
|
||||
QStringLiteral("/nonexistent/path/lut.cube"));
|
||||
|
||||
olive::Node::ConnectEdge(
|
||||
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
|
||||
|
||||
const olive::VideoParams params(
|
||||
16, 16, olive::core::PixelFormat::F32,
|
||||
olive::VideoParams::kRGBAChannelCount);
|
||||
|
||||
PixelColorTransformTraverser traverser;
|
||||
traverser.SetCacheVideoParams(params);
|
||||
olive::NodeValueTable table = traverser.GenerateTable(
|
||||
lut, olive::TimeRange(olive::core::rational(0),
|
||||
olive::core::rational(1, 30)));
|
||||
|
||||
olive::NodeValue tex_val = table.Get(olive::NodeValue::kTexture);
|
||||
EXPECT_EQ(tex_val.type(), olive::NodeValue::kTexture);
|
||||
}
|
||||
|
||||
TEST(ColorLutNode, UnsupportedExtensionLeavesProcessorNull)
|
||||
{
|
||||
olive::ColorManager::SetUpDefaultConfig();
|
||||
|
||||
olive::Project project;
|
||||
project.Initialize();
|
||||
|
||||
auto *solid = new olive::SolidGenerator();
|
||||
solid->setParent(&project);
|
||||
solid->SetStandardValue(
|
||||
olive::SolidGenerator::kColorInput,
|
||||
QVariant::fromValue(olive::Color(0.25f, 0.50f, 0.75f, 1.0f)));
|
||||
|
||||
auto *lut = new olive::OCIOLutNode();
|
||||
lut->setParent(&project);
|
||||
lut->SetStandardValue(olive::OCIOLutNode::kFileInput,
|
||||
QStringLiteral("/tmp/lut.txt"));
|
||||
|
||||
olive::Node::ConnectEdge(
|
||||
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
|
||||
|
||||
const olive::VideoParams params(
|
||||
16, 16, olive::core::PixelFormat::F32,
|
||||
olive::VideoParams::kRGBAChannelCount);
|
||||
|
||||
PixelColorTransformTraverser traverser;
|
||||
traverser.SetCacheVideoParams(params);
|
||||
olive::NodeValueTable table = traverser.GenerateTable(
|
||||
lut, olive::TimeRange(olive::core::rational(0),
|
||||
olive::core::rational(1, 30)));
|
||||
|
||||
olive::NodeValue tex_val = table.Get(olive::NodeValue::kTexture);
|
||||
EXPECT_EQ(tex_val.type(), olive::NodeValue::kTexture);
|
||||
}
|
||||
|
||||
TEST(ColorLutNode, DirectionStringValuesAreAccepted)
|
||||
{
|
||||
olive::ColorManager::SetUpDefaultConfig();
|
||||
|
||||
olive::Project project;
|
||||
project.Initialize();
|
||||
|
||||
QTemporaryDir dir;
|
||||
ASSERT_TRUE(dir.isValid());
|
||||
const QString path = WriteTestCubeLut(&dir, "invert", 0.0f, 1.0f);
|
||||
ASSERT_FALSE(path.isEmpty());
|
||||
|
||||
auto *solid = new olive::SolidGenerator();
|
||||
solid->setParent(&project);
|
||||
solid->SetStandardValue(
|
||||
olive::SolidGenerator::kColorInput,
|
||||
QVariant::fromValue(olive::Color(0.25f, 0.50f, 0.75f, 1.0f)));
|
||||
|
||||
auto *lut = new olive::OCIOLutNode();
|
||||
lut->setParent(&project);
|
||||
lut->SetStandardValue(olive::OCIOLutNode::kFileInput, path);
|
||||
lut->SetStandardValue(olive::OCIOLutNode::kDirectionInput,
|
||||
QStringLiteral("forward"));
|
||||
|
||||
olive::Node::ConnectEdge(
|
||||
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
|
||||
|
||||
const olive::VideoParams params(
|
||||
16, 16, olive::core::PixelFormat::F32,
|
||||
olive::VideoParams::kRGBAChannelCount);
|
||||
|
||||
PixelColorTransformTraverser traverser;
|
||||
traverser.SetCacheVideoParams(params);
|
||||
olive::NodeValueTable table = traverser.GenerateTable(
|
||||
lut, olive::TimeRange(olive::core::rational(0),
|
||||
olive::core::rational(1, 30)));
|
||||
olive::NodeValue tex_val = table.Get(olive::NodeValue::kTexture);
|
||||
traverser.Resolve(tex_val);
|
||||
|
||||
ASSERT_TRUE(traverser.output_frame);
|
||||
const olive::Color out = traverser.output_frame->get_pixel(0, 0);
|
||||
|
||||
EXPECT_NEAR(out.red(), 0.75f, 0.02f);
|
||||
EXPECT_NEAR(out.green(), 0.50f, 0.02f);
|
||||
EXPECT_NEAR(out.blue(), 0.25f, 0.02f);
|
||||
}
|
||||
|
||||
TEST(ColorLutNode, DirectionStringInverseIsAccepted)
|
||||
{
|
||||
olive::ColorManager::SetUpDefaultConfig();
|
||||
|
||||
olive::Project project;
|
||||
project.Initialize();
|
||||
|
||||
QTemporaryDir dir;
|
||||
ASSERT_TRUE(dir.isValid());
|
||||
const QString path = WriteTestCubeLut(&dir, "invert", 0.0f, 1.0f);
|
||||
ASSERT_FALSE(path.isEmpty());
|
||||
|
||||
auto *solid = new olive::SolidGenerator();
|
||||
solid->setParent(&project);
|
||||
solid->SetStandardValue(
|
||||
olive::SolidGenerator::kColorInput,
|
||||
QVariant::fromValue(olive::Color(0.75f, 0.50f, 0.25f, 1.0f)));
|
||||
|
||||
auto *lut = new olive::OCIOLutNode();
|
||||
lut->setParent(&project);
|
||||
lut->SetStandardValue(olive::OCIOLutNode::kFileInput, path);
|
||||
lut->SetStandardValue(olive::OCIOLutNode::kDirectionInput,
|
||||
QStringLiteral("inverse"));
|
||||
|
||||
olive::Node::ConnectEdge(
|
||||
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
|
||||
|
||||
const olive::VideoParams params(
|
||||
16, 16, olive::core::PixelFormat::F32,
|
||||
olive::VideoParams::kRGBAChannelCount);
|
||||
|
||||
PixelColorTransformTraverser traverser;
|
||||
traverser.SetCacheVideoParams(params);
|
||||
olive::NodeValueTable table = traverser.GenerateTable(
|
||||
lut, olive::TimeRange(olive::core::rational(0),
|
||||
olive::core::rational(1, 30)));
|
||||
olive::NodeValue tex_val = table.Get(olive::NodeValue::kTexture);
|
||||
traverser.Resolve(tex_val);
|
||||
|
||||
ASSERT_TRUE(traverser.output_frame);
|
||||
const olive::Color out = traverser.output_frame->get_pixel(0, 0);
|
||||
|
||||
EXPECT_NEAR(out.red(), 0.25f, 0.02f);
|
||||
EXPECT_NEAR(out.green(), 0.50f, 0.02f);
|
||||
EXPECT_NEAR(out.blue(), 0.75f, 0.02f);
|
||||
}
|
||||
|
||||
TEST(ColorLutNode, ReusingSameFileDoesNotCrash)
|
||||
{
|
||||
olive::ColorManager::SetUpDefaultConfig();
|
||||
|
||||
olive::Project project;
|
||||
project.Initialize();
|
||||
|
||||
QTemporaryDir dir;
|
||||
ASSERT_TRUE(dir.isValid());
|
||||
const QString path = WriteTestCubeLut(&dir, "invert", 0.0f, 1.0f);
|
||||
ASSERT_FALSE(path.isEmpty());
|
||||
|
||||
auto *solid = new olive::SolidGenerator();
|
||||
solid->setParent(&project);
|
||||
solid->SetStandardValue(
|
||||
olive::SolidGenerator::kColorInput,
|
||||
QVariant::fromValue(olive::Color(0.25f, 0.50f, 0.75f, 1.0f)));
|
||||
|
||||
auto *lut = new olive::OCIOLutNode();
|
||||
lut->setParent(&project);
|
||||
lut->SetStandardValue(olive::OCIOLutNode::kFileInput, path);
|
||||
|
||||
olive::Node::ConnectEdge(
|
||||
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
|
||||
|
||||
const olive::VideoParams params(
|
||||
16, 16, olive::core::PixelFormat::F32,
|
||||
olive::VideoParams::kRGBAChannelCount);
|
||||
|
||||
// Render twice; the second render should reuse the cached processor.
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
PixelColorTransformTraverser traverser;
|
||||
traverser.SetCacheVideoParams(params);
|
||||
olive::NodeValueTable table = traverser.GenerateTable(
|
||||
lut, olive::TimeRange(olive::core::rational(0),
|
||||
olive::core::rational(1, 30)));
|
||||
olive::NodeValue tex_val = table.Get(olive::NodeValue::kTexture);
|
||||
traverser.Resolve(tex_val);
|
||||
|
||||
ASSERT_TRUE(traverser.output_frame);
|
||||
const olive::Color out = traverser.output_frame->get_pixel(0, 0);
|
||||
EXPECT_NEAR(out.red(), 0.75f, 0.02f);
|
||||
EXPECT_NEAR(out.green(), 0.50f, 0.02f);
|
||||
EXPECT_NEAR(out.blue(), 0.25f, 0.02f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ColorLutNode, SwitchingBackToOriginalFileRestoresOriginalPixels)
|
||||
{
|
||||
olive::ColorManager::SetUpDefaultConfig();
|
||||
|
||||
@@ -434,7 +681,6 @@ TEST(ColorLutNode, SwitchingFileUpdatesProcessorAndPixels)
|
||||
auto *lut = new olive::OCIOLutNode();
|
||||
lut->setParent(&project);
|
||||
lut->SetStandardValue(olive::OCIOLutNode::kFileInput, invert_path);
|
||||
lut->SetStandardValue(olive::OCIOLutNode::kDirectionInput, 0); // Forward
|
||||
|
||||
olive::Node::ConnectEdge(
|
||||
solid, olive::NodeInput(lut, olive::OCIOLutNode::kTextureInput));
|
||||
@@ -443,45 +689,29 @@ TEST(ColorLutNode, SwitchingFileUpdatesProcessorAndPixels)
|
||||
16, 16, olive::core::PixelFormat::F32,
|
||||
olive::VideoParams::kRGBAChannelCount);
|
||||
|
||||
// Render with invert LUT.
|
||||
PixelColorTransformTraverser invert_traverser;
|
||||
invert_traverser.SetCacheVideoParams(params);
|
||||
olive::NodeValueTable invert_table = invert_traverser.GenerateTable(
|
||||
lut, olive::TimeRange(olive::core::rational(0),
|
||||
olive::core::rational(1, 30)));
|
||||
olive::NodeValue invert_tex = invert_table.Get(olive::NodeValue::kTexture);
|
||||
invert_traverser.Resolve(invert_tex);
|
||||
auto render = [&]() {
|
||||
PixelColorTransformTraverser traverser;
|
||||
traverser.SetCacheVideoParams(params);
|
||||
olive::NodeValueTable table = traverser.GenerateTable(
|
||||
lut, olive::TimeRange(olive::core::rational(0),
|
||||
olive::core::rational(1, 30)));
|
||||
olive::NodeValue tex_val = table.Get(olive::NodeValue::kTexture);
|
||||
traverser.Resolve(tex_val);
|
||||
return traverser.output_frame->get_pixel(0, 0);
|
||||
};
|
||||
|
||||
ASSERT_TRUE(invert_traverser.output_frame);
|
||||
const olive::Color invert_out =
|
||||
invert_traverser.output_frame->get_pixel(0, 0);
|
||||
// First render with invert.
|
||||
const olive::Color invert_out = render();
|
||||
EXPECT_NEAR(invert_out.red(), 0.75f, 0.02f);
|
||||
|
||||
// Switch to a different LUT file. Before the fix, the node could keep the
|
||||
// old invert processor cached and the output would not change.
|
||||
// Switch to boost, then back to invert.
|
||||
lut->SetStandardValue(olive::OCIOLutNode::kFileInput, boost_path);
|
||||
|
||||
PixelColorTransformTraverser boost_traverser;
|
||||
boost_traverser.SetCacheVideoParams(params);
|
||||
olive::NodeValueTable boost_table = boost_traverser.GenerateTable(
|
||||
lut, olive::TimeRange(olive::core::rational(0),
|
||||
olive::core::rational(1, 30)));
|
||||
olive::NodeValue boost_tex = boost_table.Get(olive::NodeValue::kTexture);
|
||||
boost_traverser.Resolve(boost_tex);
|
||||
|
||||
ASSERT_TRUE(boost_traverser.output_frame);
|
||||
const olive::Color boost_out =
|
||||
boost_traverser.output_frame->get_pixel(0, 0);
|
||||
|
||||
// boost LUT maps:
|
||||
// 0.0 -> 1.0, 1.0 -> 0.5 (linear: f(x) = 1 - 0.5*x)
|
||||
// so (0.25, 0.50, 0.75) -> (0.875, 0.750, 0.625).
|
||||
EXPECT_NEAR(boost_out.red(), 0.875f, 0.02f);
|
||||
EXPECT_NEAR(boost_out.green(), 0.750f, 0.02f);
|
||||
EXPECT_NEAR(boost_out.blue(), 0.625f, 0.02f);
|
||||
|
||||
// The two outputs must differ (boost raises all channels vs invert).
|
||||
const olive::Color boost_out = render();
|
||||
EXPECT_GT(std::abs(boost_out.red() - invert_out.red()), 0.1f);
|
||||
EXPECT_GT(std::abs(boost_out.green() - invert_out.green()), 0.1f);
|
||||
EXPECT_GT(std::abs(boost_out.blue() - invert_out.blue()), 0.1f);
|
||||
|
||||
lut->SetStandardValue(olive::OCIOLutNode::kFileInput, invert_path);
|
||||
const olive::Color restored_out = render();
|
||||
EXPECT_NEAR(restored_out.red(), invert_out.red(), 0.02f);
|
||||
EXPECT_NEAR(restored_out.green(), invert_out.green(), 0.02f);
|
||||
EXPECT_NEAR(restored_out.blue(), invert_out.blue(), 0.02f);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <QSignalSpy>
|
||||
|
||||
#include "node/generator/solid/solid.h"
|
||||
#include "node/project.h"
|
||||
#include "widget/nodeview/nodeview.h"
|
||||
|
||||
using namespace olive;
|
||||
|
||||
class NodeViewTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
ColorManager::SetUpDefaultConfig();
|
||||
|
||||
project_ = std::make_unique<Project>();
|
||||
project_->Initialize();
|
||||
}
|
||||
|
||||
std::unique_ptr<Project> project_;
|
||||
};
|
||||
|
||||
TEST_F(NodeViewTest, ConstructionCreatesEmptyView)
|
||||
{
|
||||
NodeView view;
|
||||
EXPECT_TRUE(view.GetContexts().isEmpty());
|
||||
EXPECT_FALSE(view.IsGroupOverlay());
|
||||
}
|
||||
|
||||
TEST_F(NodeViewTest, SetContextsUpdatesContextList)
|
||||
{
|
||||
auto *solid = new SolidGenerator();
|
||||
solid->setParent(project_.get());
|
||||
|
||||
NodeView view;
|
||||
view.SetContexts({ solid });
|
||||
|
||||
EXPECT_EQ(view.GetContexts().size(), 1);
|
||||
EXPECT_EQ(view.GetContexts().first(), solid);
|
||||
}
|
||||
|
||||
TEST_F(NodeViewTest, ShowSelectedNodeInParamEditorNoSelectionIsNoOp)
|
||||
{
|
||||
NodeView view;
|
||||
|
||||
QSignalSpy changed_with_ctx_spy(
|
||||
&view, &NodeView::NodeSelectionChangedWithContexts);
|
||||
|
||||
foreach (QAction *action, view.actions()) {
|
||||
if (action->property("id").toString() ==
|
||||
QStringLiteral("shownodeparams")) {
|
||||
action->trigger();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_EQ(changed_with_ctx_spy.count(), 0);
|
||||
}
|
||||
|
||||
TEST_F(NodeViewTest, ClearGraphRemovesContexts)
|
||||
{
|
||||
auto *solid = new SolidGenerator();
|
||||
solid->setParent(project_.get());
|
||||
|
||||
NodeView view;
|
||||
view.SetContexts({ solid });
|
||||
EXPECT_FALSE(view.GetContexts().isEmpty());
|
||||
|
||||
view.ClearGraph();
|
||||
EXPECT_TRUE(view.GetContexts().isEmpty());
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
#include "audio/audiomanager.h"
|
||||
#include "dialog/preferences/tabs/preferencesbehaviortab.h"
|
||||
#include "dialog/preferences/tabs/preferencesgeneraltab.h"
|
||||
#include "dialog/preferences/tabs/preferencesaudiotab.h"
|
||||
|
||||
using namespace olive;
|
||||
|
||||
TEST(PreferencesBehaviorTab, TimelineCategoryHasExpectedCheckboxes)
|
||||
{
|
||||
PreferencesBehaviorTab tab(PreferencesBehaviorTab::kCategoryTimeline);
|
||||
// 8 timeline behavior options
|
||||
EXPECT_EQ(tab.findChildren<QCheckBox *>().size(), 8);
|
||||
}
|
||||
|
||||
TEST(PreferencesBehaviorTab, PlaybackCategoryHasExpectedCheckboxes)
|
||||
{
|
||||
PreferencesBehaviorTab tab(PreferencesBehaviorTab::kCategoryPlayback);
|
||||
EXPECT_EQ(tab.findChildren<QCheckBox *>().size(), 2);
|
||||
}
|
||||
|
||||
TEST(PreferencesBehaviorTab, ProjectCategoryHasExpectedCheckboxes)
|
||||
{
|
||||
PreferencesBehaviorTab tab(PreferencesBehaviorTab::kCategoryProject);
|
||||
EXPECT_EQ(tab.findChildren<QCheckBox *>().size(), 1);
|
||||
}
|
||||
|
||||
TEST(PreferencesBehaviorTab, NodesCategoryHasExpectedCheckboxes)
|
||||
{
|
||||
PreferencesBehaviorTab tab(PreferencesBehaviorTab::kCategoryNodes);
|
||||
EXPECT_EQ(tab.findChildren<QCheckBox *>().size(), 3);
|
||||
}
|
||||
|
||||
TEST(PreferencesBehaviorTab, RenderingCategoryHasGraphicsBackendCombobox)
|
||||
{
|
||||
PreferencesBehaviorTab tab(PreferencesBehaviorTab::kCategoryRendering);
|
||||
EXPECT_FALSE(tab.findChildren<QComboBox *>().isEmpty());
|
||||
EXPECT_FALSE(tab.findChildren<QCheckBox *>().isEmpty());
|
||||
}
|
||||
|
||||
TEST(PreferencesBehaviorTab, BehaviorPrefTrProvidesTranslations)
|
||||
{
|
||||
QStringList keys;
|
||||
keys << QStringLiteral("Enable hover focus")
|
||||
<< QStringLiteral("Select also selects all children in the graph")
|
||||
<< QStringLiteral("Double-clicking a node opens its properties")
|
||||
<< QStringLiteral("Auto-Seek to Beginning of Sequence")
|
||||
<< QStringLiteral("Scroll wheel zooms instead of scrolling")
|
||||
<< QStringLiteral("Enable audio scrubbing");
|
||||
|
||||
foreach (const QString &key, keys) {
|
||||
EXPECT_FALSE(
|
||||
PreferencesBehaviorTab::BehaviorPrefTr(
|
||||
key.toUtf8().constData()).isEmpty())
|
||||
<< key.toStdString();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(PreferencesBehaviorTab, RenderingCategoryContainsDefaultBackend)
|
||||
{
|
||||
PreferencesBehaviorTab tab(PreferencesBehaviorTab::kCategoryRendering);
|
||||
QList<QComboBox *> boxes = tab.findChildren<QComboBox *>();
|
||||
ASSERT_FALSE(boxes.isEmpty());
|
||||
|
||||
QComboBox *backend_box = boxes.first();
|
||||
EXPECT_GT(backend_box->count(), 0);
|
||||
}
|
||||
|
||||
TEST(PreferencesGeneralTab, ContainsHoverFocusOption)
|
||||
{
|
||||
PreferencesGeneralTab tab;
|
||||
QList<QCheckBox *> boxes = tab.findChildren<QCheckBox *>();
|
||||
|
||||
bool found = false;
|
||||
foreach (QCheckBox *box, boxes) {
|
||||
if (box->text() == PreferencesBehaviorTab::BehaviorPrefTr(
|
||||
"Enable hover focus")) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(found);
|
||||
}
|
||||
|
||||
TEST(PreferencesAudioTab, AudioScrubbingCheckboxUsesBehaviorTranslation)
|
||||
{
|
||||
AudioManager::CreateInstance();
|
||||
|
||||
{
|
||||
PreferencesAudioTab tab;
|
||||
QList<QCheckBox *> boxes = tab.findChildren<QCheckBox *>();
|
||||
|
||||
bool found = false;
|
||||
foreach (QCheckBox *box, boxes) {
|
||||
if (box->text() == PreferencesBehaviorTab::BehaviorPrefTr(
|
||||
"Enable audio scrubbing")) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(found);
|
||||
}
|
||||
|
||||
AudioManager::DestroyInstance();
|
||||
}
|
||||
|
||||
TEST(PreferencesGeneralTab, IncludesBehaviorOptions)
|
||||
{
|
||||
PreferencesGeneralTab tab;
|
||||
QList<QCheckBox *> boxes = tab.findChildren<QCheckBox *>();
|
||||
EXPECT_GE(boxes.size(), 3);
|
||||
}
|
||||
|
||||
TEST(PreferencesAudioTab, IncludesAudioScrubbingOption)
|
||||
{
|
||||
AudioManager::CreateInstance();
|
||||
|
||||
{
|
||||
PreferencesAudioTab tab;
|
||||
QList<QCheckBox *> boxes = tab.findChildren<QCheckBox *>();
|
||||
bool found = false;
|
||||
foreach (QCheckBox *box, boxes) {
|
||||
if (!box->text().isEmpty()) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(found);
|
||||
}
|
||||
|
||||
AudioManager::DestroyInstance();
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <QSignalSpy>
|
||||
|
||||
#include "codec/conformmanager.h"
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "node/project.h"
|
||||
#include "render/diskmanager.h"
|
||||
#include "render/previewautocacher.h"
|
||||
#include "render/rendermanager.h"
|
||||
|
||||
using namespace olive;
|
||||
|
||||
class PreviewAutoCacherTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
ColorManager::SetUpDefaultConfig();
|
||||
|
||||
// Use the dummy render backend so PreviewAutoCacher can be exercised
|
||||
// without initializing OpenGL/Vulkan in the unit-test process.
|
||||
OLIVE_CONFIG("GraphicsBackend") = QStringLiteral("dummy");
|
||||
|
||||
DiskManager::CreateInstance();
|
||||
ConformManager::CreateInstance();
|
||||
RenderManager::CreateInstance();
|
||||
|
||||
project_ = std::make_unique<Project>();
|
||||
project_->Initialize();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
RenderManager::DestroyInstance();
|
||||
ConformManager::DestroyInstance();
|
||||
DiskManager::DestroyInstance();
|
||||
}
|
||||
|
||||
std::unique_ptr<Project> project_;
|
||||
};
|
||||
|
||||
TEST_F(PreviewAutoCacherTest, ConstructionInitializesDefaultState)
|
||||
{
|
||||
PreviewAutoCacher cacher;
|
||||
EXPECT_FALSE(cacher.IsRenderingCustomRange());
|
||||
}
|
||||
|
||||
TEST_F(PreviewAutoCacherTest, SetProjectToNullDoesNotCrash)
|
||||
{
|
||||
PreviewAutoCacher cacher;
|
||||
cacher.SetProject(project_.get());
|
||||
cacher.SetProject(nullptr);
|
||||
EXPECT_FALSE(cacher.IsRenderingCustomRange());
|
||||
}
|
||||
|
||||
TEST_F(PreviewAutoCacherTest, SetRendersPausedTogglesState)
|
||||
{
|
||||
PreviewAutoCacher cacher;
|
||||
cacher.SetRendersPaused(true);
|
||||
cacher.SetRendersPaused(false);
|
||||
}
|
||||
|
||||
TEST_F(PreviewAutoCacherTest, SetThumbnailsPausedTogglesState)
|
||||
{
|
||||
PreviewAutoCacher cacher;
|
||||
cacher.SetThumbnailsPaused(true);
|
||||
cacher.SetThumbnailsPaused(false);
|
||||
}
|
||||
|
||||
TEST_F(PreviewAutoCacherTest, SetPlayheadStoresPlayhead)
|
||||
{
|
||||
PreviewAutoCacher cacher;
|
||||
cacher.SetPlayhead(rational(42));
|
||||
}
|
||||
|
||||
TEST_F(PreviewAutoCacherTest, ClearSingleFrameRendersDoesNotCrashWhenEmpty)
|
||||
{
|
||||
PreviewAutoCacher cacher;
|
||||
cacher.ClearSingleFrameRenders();
|
||||
}
|
||||
|
||||
TEST_F(PreviewAutoCacherTest, ClearSingleFrameRendersThatArentRunningDoesNotCrashWhenEmpty)
|
||||
{
|
||||
PreviewAutoCacher cacher;
|
||||
cacher.ClearSingleFrameRendersThatArentRunning();
|
||||
}
|
||||
|
||||
TEST_F(PreviewAutoCacherTest, GetSingleFrameWithoutProjectReturnsTicket)
|
||||
{
|
||||
auto *viewer = new ViewerOutput();
|
||||
viewer->setParent(project_.get());
|
||||
|
||||
PreviewAutoCacher cacher;
|
||||
RenderTicketPtr ticket = cacher.GetSingleFrame(viewer, rational(0));
|
||||
EXPECT_NE(ticket, nullptr);
|
||||
}
|
||||
|
||||
TEST_F(PreviewAutoCacherTest, ForceCacheRangeDoesNotCrash)
|
||||
{
|
||||
auto *viewer = new ViewerOutput();
|
||||
viewer->setParent(project_.get());
|
||||
|
||||
PreviewAutoCacher cacher;
|
||||
cacher.ForceCacheRange(viewer, TimeRange(rational(0), rational(1)));
|
||||
cacher.SetProject(nullptr);
|
||||
}
|
||||
|
||||
TEST_F(PreviewAutoCacherTest, CancelVideoTasksDoesNotCrashWhenIdle)
|
||||
{
|
||||
PreviewAutoCacher cacher;
|
||||
cacher.CancelVideoTasks(false);
|
||||
cacher.CancelVideoTasks(true);
|
||||
}
|
||||
|
||||
TEST_F(PreviewAutoCacherTest, CancelAudioTasksDoesNotCrashWhenIdle)
|
||||
{
|
||||
PreviewAutoCacher cacher;
|
||||
cacher.CancelAudioTasks(false);
|
||||
cacher.CancelAudioTasks(true);
|
||||
}
|
||||
@@ -239,20 +239,41 @@ TEST(ProxyManager, EmitsProxyFinishedState)
|
||||
olive::ProxyManager::DestroyInstance();
|
||||
}
|
||||
|
||||
TEST(ProxyManager, FootageJobCarriesProxyMetadata)
|
||||
TEST(ProxyManager, WorkingProxyFilenamePrependsExtension)
|
||||
{
|
||||
const QString proxy =
|
||||
QStringLiteral("/cache/proxy/example.mp4");
|
||||
const QString working =
|
||||
olive::ProxyManager::GetWorkingProxyFilename(proxy);
|
||||
|
||||
EXPECT_EQ(working, QStringLiteral("/cache/proxy/example.mp4.working.mp4"));
|
||||
}
|
||||
|
||||
TEST(ProxyManager, ProxyStateFromStringDefaultsForEmpty)
|
||||
{
|
||||
EXPECT_EQ(olive::ProxyManager::ProxyStateFromString(QString()),
|
||||
olive::ProxyManager::kProxyMissing);
|
||||
}
|
||||
|
||||
TEST(ProxyManager, FootageProxyCanBeDisabled)
|
||||
{
|
||||
olive::Footage footage;
|
||||
footage.SetProxy(QStringLiteral("/cache/proxy/example.mp4"),
|
||||
olive::ProxyManager::kProxyReady, 0, 1, false);
|
||||
|
||||
EXPECT_FALSE(footage.proxy_enabled());
|
||||
EXPECT_FALSE(footage.proxy_path().isEmpty());
|
||||
}
|
||||
|
||||
TEST(ProxyManager, FootageJobWithoutProxyHasEmptyProxyFields)
|
||||
{
|
||||
olive::FootageJob job(olive::TimeRange(), QStringLiteral("source-decoder"),
|
||||
QStringLiteral("/media/source.mov"),
|
||||
olive::Track::kVideo, olive::rational(10),
|
||||
olive::LoopMode::kLoopModeOff);
|
||||
|
||||
EXPECT_FALSE(job.has_proxy());
|
||||
|
||||
job.set_proxy(QStringLiteral("/cache/proxy/source.mp4"),
|
||||
QStringLiteral("ffmpeg"), 0);
|
||||
|
||||
EXPECT_TRUE(job.has_proxy());
|
||||
EXPECT_EQ(job.filename(), QStringLiteral("/media/source.mov"));
|
||||
EXPECT_EQ(job.proxy_filename(), QStringLiteral("/cache/proxy/source.mp4"));
|
||||
EXPECT_EQ(job.proxy_decoder(), QStringLiteral("ffmpeg"));
|
||||
EXPECT_EQ(job.proxy_stream_index(), 0);
|
||||
EXPECT_TRUE(job.proxy_filename().isEmpty());
|
||||
EXPECT_TRUE(job.proxy_decoder().isEmpty());
|
||||
EXPECT_EQ(job.proxy_stream_index(), -1);
|
||||
}
|
||||
|
||||
@@ -40,3 +40,58 @@ TEST(RenderAudioParams, TimeAndSampleConversions)
|
||||
EXPECT_EQ(params.bytes_per_channel_to_time(96000),
|
||||
olive::core::rational(1, 1));
|
||||
}
|
||||
|
||||
TEST(RenderAudioParams, ChannelLayoutCount)
|
||||
{
|
||||
olive::core::AudioParams mono(
|
||||
48000, AV_CH_LAYOUT_MONO, olive::core::SampleFormat::F32);
|
||||
EXPECT_EQ(mono.channel_count(), 1);
|
||||
|
||||
olive::core::AudioParams surround(
|
||||
48000, AV_CH_LAYOUT_5POINT1, olive::core::SampleFormat::F32);
|
||||
EXPECT_EQ(surround.channel_count(), 6);
|
||||
}
|
||||
|
||||
TEST(RenderAudioParams, SampleFormatSizes)
|
||||
{
|
||||
olive::core::AudioParams u8(
|
||||
48000, AV_CH_LAYOUT_MONO, olive::core::SampleFormat::U8);
|
||||
EXPECT_EQ(u8.bytes_per_sample_per_channel(), 1);
|
||||
|
||||
olive::core::AudioParams f32(
|
||||
48000, AV_CH_LAYOUT_MONO, olive::core::SampleFormat::F32);
|
||||
EXPECT_EQ(f32.bytes_per_sample_per_channel(), 4);
|
||||
|
||||
olive::core::AudioParams f64(
|
||||
48000, AV_CH_LAYOUT_MONO, olive::core::SampleFormat::F64);
|
||||
EXPECT_EQ(f64.bytes_per_sample_per_channel(), 8);
|
||||
}
|
||||
|
||||
TEST(RenderAudioParams, CopyAndAssignment)
|
||||
{
|
||||
olive::core::AudioParams params(
|
||||
96000, AV_CH_LAYOUT_STEREO, olive::core::SampleFormat::F32);
|
||||
|
||||
olive::core::AudioParams copy(params);
|
||||
EXPECT_EQ(copy.sample_rate(), 96000);
|
||||
EXPECT_EQ(copy.channel_count(), 2);
|
||||
EXPECT_EQ(copy.format(), olive::core::SampleFormat::F32);
|
||||
|
||||
olive::core::AudioParams assigned;
|
||||
assigned = params;
|
||||
EXPECT_EQ(assigned.sample_rate(), 96000);
|
||||
EXPECT_TRUE(assigned == params);
|
||||
}
|
||||
|
||||
TEST(RenderAudioParams, SettersModifyState)
|
||||
{
|
||||
olive::core::AudioParams params(
|
||||
44100, AV_CH_LAYOUT_MONO, olive::core::SampleFormat::S16);
|
||||
EXPECT_TRUE(params.is_valid());
|
||||
|
||||
params.set_sample_rate(48000);
|
||||
params.set_format(olive::core::SampleFormat::F32);
|
||||
|
||||
EXPECT_EQ(params.sample_rate(), 48000);
|
||||
EXPECT_EQ(params.format(), olive::core::SampleFormat::F32);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QtTest/QSignalSpy>
|
||||
|
||||
#include "render/renderticket.h"
|
||||
|
||||
using namespace olive;
|
||||
|
||||
TEST(RenderTicketWatcher, DoesNotEmitFinishedForRunningTicketSynchronously)
|
||||
{
|
||||
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
|
||||
ticket->Start();
|
||||
|
||||
RenderTicketWatcher watcher;
|
||||
QSignalSpy spy(&watcher, &RenderTicketWatcher::Finished);
|
||||
|
||||
watcher.SetTicket(ticket);
|
||||
|
||||
// The ticket is still running, so the watcher must not emit Finished
|
||||
// synchronously when SetTicket is called.
|
||||
EXPECT_EQ(spy.count(), 0);
|
||||
|
||||
ticket->Finish();
|
||||
|
||||
// Once the ticket finishes, the watcher should emit Finished.
|
||||
spy.wait(100);
|
||||
EXPECT_EQ(spy.count(), 1);
|
||||
}
|
||||
|
||||
TEST(RenderTicketWatcher, EmitsFinishedForAlreadyFinishedTicketAsynchronously)
|
||||
{
|
||||
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
|
||||
ticket->Start();
|
||||
ticket->Finish();
|
||||
|
||||
RenderTicketWatcher watcher;
|
||||
QSignalSpy spy(&watcher, &RenderTicketWatcher::Finished);
|
||||
|
||||
watcher.SetTicket(ticket);
|
||||
|
||||
// The ticket has already finished. The watcher must not delete itself or
|
||||
// emit Finished synchronously inside SetTicket, because the caller may still
|
||||
// need the returned pointer. Instead it should defer the signal.
|
||||
EXPECT_EQ(spy.count(), 0);
|
||||
EXPECT_FALSE(watcher.GetTicket() == nullptr);
|
||||
|
||||
// Process the queued Finished emission.
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
EXPECT_EQ(spy.count(), 1);
|
||||
}
|
||||
|
||||
TEST(RenderTicketWatcher, CancelMarksTicketAsCancelled)
|
||||
{
|
||||
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
|
||||
ticket->Start();
|
||||
|
||||
RenderTicketWatcher watcher;
|
||||
watcher.SetTicket(ticket);
|
||||
|
||||
EXPECT_TRUE(watcher.IsRunning());
|
||||
EXPECT_FALSE(ticket->IsCancelled());
|
||||
|
||||
watcher.Cancel();
|
||||
|
||||
EXPECT_TRUE(ticket->IsCancelled());
|
||||
}
|
||||
|
||||
TEST(RenderTicket, HasResultIsFalseWhileRunning)
|
||||
{
|
||||
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
|
||||
ticket->Start();
|
||||
|
||||
EXPECT_TRUE(ticket->IsRunning());
|
||||
EXPECT_FALSE(ticket->HasResult());
|
||||
}
|
||||
|
||||
TEST(RenderTicket, FinishWithValueProvidesResult)
|
||||
{
|
||||
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
|
||||
ticket->Start();
|
||||
ticket->Finish(QVariant(42));
|
||||
|
||||
EXPECT_FALSE(ticket->IsRunning());
|
||||
EXPECT_TRUE(ticket->HasResult());
|
||||
EXPECT_EQ(ticket->Get().toInt(), 42);
|
||||
}
|
||||
|
||||
TEST(RenderTicket, FinishCountIncrementsOnEachFinish)
|
||||
{
|
||||
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
|
||||
EXPECT_EQ(ticket->GetFinishCount(), 0);
|
||||
|
||||
ticket->Start();
|
||||
ticket->Finish();
|
||||
EXPECT_EQ(ticket->GetFinishCount(), 1);
|
||||
|
||||
ticket->Start();
|
||||
ticket->Finish();
|
||||
EXPECT_EQ(ticket->GetFinishCount(), 2);
|
||||
}
|
||||
|
||||
TEST(RenderTicket, FinishWithoutStartIsIgnored)
|
||||
{
|
||||
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
|
||||
ticket->Finish();
|
||||
EXPECT_EQ(ticket->GetFinishCount(), 0);
|
||||
}
|
||||
|
||||
TEST(RenderTicketWatcher, DelegatesGetAndHasResultToTicket)
|
||||
{
|
||||
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
|
||||
ticket->Start();
|
||||
ticket->Finish(QVariant(QStringLiteral("result")));
|
||||
|
||||
RenderTicketWatcher watcher;
|
||||
watcher.SetTicket(ticket);
|
||||
|
||||
EXPECT_FALSE(watcher.IsRunning());
|
||||
EXPECT_TRUE(watcher.HasResult());
|
||||
EXPECT_EQ(watcher.Get().toString(), QStringLiteral("result"));
|
||||
}
|
||||
|
||||
TEST(RenderTicketWatcher, EmptyWatcherReturnsDefaults)
|
||||
{
|
||||
RenderTicketWatcher watcher;
|
||||
EXPECT_FALSE(watcher.IsRunning());
|
||||
EXPECT_FALSE(watcher.HasResult());
|
||||
EXPECT_TRUE(watcher.Get().isNull());
|
||||
EXPECT_EQ(watcher.GetTicket(), nullptr);
|
||||
}
|
||||
|
||||
TEST(RenderTicketWatcher, SettingTicketTwiceIsRejected)
|
||||
{
|
||||
RenderTicketPtr first = std::make_shared<RenderTicket>();
|
||||
RenderTicketPtr second = std::make_shared<RenderTicket>();
|
||||
|
||||
RenderTicketWatcher watcher;
|
||||
watcher.SetTicket(first);
|
||||
watcher.SetTicket(second);
|
||||
|
||||
EXPECT_EQ(watcher.GetTicket(), first);
|
||||
}
|
||||
@@ -102,6 +102,43 @@ TEST(RenderVideoParams, ValidityAndTimebase)
|
||||
12);
|
||||
}
|
||||
|
||||
TEST(RenderVideoParams, CopyConstructorPreservesValues)
|
||||
{
|
||||
olive::VideoParams params(1920, 1080, olive::core::rational(24, 1),
|
||||
olive::core::PixelFormat::F32, 4);
|
||||
params.set_colorspace(QStringLiteral("ACEScg"));
|
||||
|
||||
olive::VideoParams copy(params);
|
||||
EXPECT_EQ(copy.width(), 1920);
|
||||
EXPECT_EQ(copy.height(), 1080);
|
||||
EXPECT_EQ(copy.format(), olive::core::PixelFormat::F32);
|
||||
EXPECT_EQ(copy.channel_count(), 4);
|
||||
EXPECT_EQ(copy.colorspace(), QStringLiteral("ACEScg"));
|
||||
}
|
||||
|
||||
TEST(RenderVideoParams, AssignmentPreservesValues)
|
||||
{
|
||||
olive::VideoParams params(1280, 720, olive::core::rational(30, 1),
|
||||
olive::core::PixelFormat::U16, 4);
|
||||
olive::VideoParams copy;
|
||||
copy = params;
|
||||
EXPECT_EQ(copy.width(), 1280);
|
||||
EXPECT_EQ(copy.height(), 720);
|
||||
EXPECT_EQ(copy.format(), olive::core::PixelFormat::U16);
|
||||
}
|
||||
|
||||
TEST(RenderVideoParams, EqualityComparesDimensionsAndFormat)
|
||||
{
|
||||
olive::VideoParams a(1920, 1080, olive::core::PixelFormat::U8, 4);
|
||||
olive::VideoParams b(1920, 1080, olive::core::PixelFormat::U8, 4);
|
||||
olive::VideoParams c(1280, 720, olive::core::PixelFormat::U8, 4);
|
||||
olive::VideoParams d(1920, 1080, olive::core::PixelFormat::F32, 4);
|
||||
|
||||
EXPECT_EQ(a, b);
|
||||
EXPECT_NE(a, c);
|
||||
EXPECT_NE(a, d);
|
||||
}
|
||||
|
||||
TEST(RenderVideoParams, SaveLoadRoundTripExtended)
|
||||
{
|
||||
olive::VideoParams params(1920, 1080, olive::core::rational(1, 24),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "widget/timebased/timebasedwidget.h"
|
||||
#include "widget/timeruler/seekablewidget.h"
|
||||
#include "node/output/viewer/viewer.h"
|
||||
|
||||
TEST(TimeBasedWidget, ConnectViewerNodeNullSafe)
|
||||
@@ -19,3 +20,53 @@ TEST(TimeBasedWidget, ConnectedNodeClearsOnDelete)
|
||||
delete viewer;
|
||||
EXPECT_EQ(widget.GetConnectedNode(), nullptr);
|
||||
}
|
||||
|
||||
TEST(SeekableWidget, ConstructionInitializesDefaults)
|
||||
{
|
||||
olive::SeekableWidget widget;
|
||||
EXPECT_FALSE(widget.IsDraggingPlayhead());
|
||||
EXPECT_FALSE(widget.HasItemsSelected());
|
||||
EXPECT_EQ(widget.GetMarkers(), nullptr);
|
||||
EXPECT_EQ(widget.GetWorkArea(), nullptr);
|
||||
}
|
||||
|
||||
TEST(SeekableWidget, SetScrollAdjustsScrollBar)
|
||||
{
|
||||
olive::SeekableWidget widget;
|
||||
widget.resize(400, 100);
|
||||
|
||||
widget.SetScroll(0);
|
||||
EXPECT_EQ(widget.GetScroll(), 0);
|
||||
|
||||
int max_scroll = widget.horizontalScrollBar()->maximum();
|
||||
if (max_scroll > 0) {
|
||||
widget.SetScroll(max_scroll);
|
||||
EXPECT_EQ(widget.GetScroll(), max_scroll);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SeekableWidget, SetMarkersAndWorkAreaAreReflected)
|
||||
{
|
||||
olive::SeekableWidget widget;
|
||||
|
||||
olive::TimelineMarkerList markers;
|
||||
olive::TimelineWorkArea workarea;
|
||||
|
||||
widget.SetMarkers(&markers);
|
||||
widget.SetWorkArea(&workarea);
|
||||
|
||||
EXPECT_EQ(widget.GetMarkers(), &markers);
|
||||
EXPECT_EQ(widget.GetWorkArea(), &workarea);
|
||||
}
|
||||
|
||||
TEST(SeekableWidget, MarkerEditingEnabledToggles)
|
||||
{
|
||||
olive::SeekableWidget widget;
|
||||
EXPECT_TRUE(widget.IsMarkerEditingEnabled());
|
||||
|
||||
widget.SetMarkerEditingEnabled(false);
|
||||
EXPECT_FALSE(widget.IsMarkerEditingEnabled());
|
||||
|
||||
widget.SetMarkerEditingEnabled(true);
|
||||
EXPECT_TRUE(widget.IsMarkerEditingEnabled());
|
||||
}
|
||||
|
||||
@@ -66,6 +66,23 @@ TEST(TimecodeMetadata, RejectsInvalidMetadata)
|
||||
EXPECT_FALSE(olive::TimecodeMetadata::FromBwfTimeReference(
|
||||
QStringLiteral("123"), 0)
|
||||
.valid);
|
||||
EXPECT_FALSE(olive::TimecodeMetadata::FromTimecodeString(
|
||||
QStringLiteral("not-a-timecode"),
|
||||
olive::core::rational(1, 24))
|
||||
.valid);
|
||||
}
|
||||
|
||||
TEST(TimecodeMetadata, FromBwfTimeReferenceZeroSampleRateIsInvalid)
|
||||
{
|
||||
EXPECT_FALSE(olive::TimecodeMetadata::FromBwfTimeReference(
|
||||
QStringLiteral("0"), 0)
|
||||
.valid);
|
||||
}
|
||||
|
||||
TEST(TimecodeMetadata, FootageDescriptionWithoutSourceStartTime)
|
||||
{
|
||||
olive::FootageDescription desc(QStringLiteral("ffmpeg"));
|
||||
EXPECT_FALSE(desc.HasSourceStartTime());
|
||||
}
|
||||
|
||||
TEST(TimecodeMetadata, FootageDescriptionCachesSourceStartTime)
|
||||
|
||||
Reference in New Issue
Block a user