diff --git a/CMakeLists.txt b/CMakeLists.txt index 861f07b4d..a937ae93c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,7 @@ cmake_minimum_required(VERSION 3.13 FATAL_ERROR) project(olive-editor VERSION 0.2.0 LANGUAGES CXX) option(BUILD_DOXYGEN "Build Doxygen documentation" OFF) +option(BUILD_TESTS "Build unit tests" ON) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -28,22 +29,64 @@ set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) +# Set compiler options +if(MSVC) + set(OLIVE_COMPILE_OPTIONS + /WX + /wd4267 + /wd4244 + /experimental:external + /external:anglebrackets + /external:W0 + "$<$:/O2>" + "$<$:/MP>" + ) +else() + set(OLIVE_COMPILE_OPTIONS + "$<$:-O2>" + -Werror + -Wuninitialized + -pedantic-errors + -Wall + -Wextra + -Wno-unused-parameter + -Wshadow + ) +endif() + set(OLIVE_DEFINITIONS -DAPPVERSION="${PROJECT_VERSION}" -DQT_DEPRECATED_WARNINGS) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") +# Link OpenGL if(UNIX AND NOT APPLE AND NOT DEFINED OpenGL_GL_PREFERENCE) set(OpenGL_GL_PREFERENCE LEGACY) endif() - find_package(OpenGL REQUIRED) +list(APPEND OLIVE_LIBRARIES OpenGL::GL) +# Link OpenColorIO find_package(OpenColorIO 2.0.0 REQUIRED) +list(APPEND OLIVE_LIBRARIES ${OCIO_LIBRARIES}) +list(APPEND OLIVE_INCLUDE_DIRS ${OCIO_INCLUDE_DIRS}) +# Link OpenImageIO find_package(OpenImageIO 2.1.12 REQUIRED) +list(APPEND OLIVE_LIBRARIES ${OIIO_LIBRARIES}) +list(APPEND OLIVE_INCLUDE_DIRS ${OIIO_INCLUDE_DIRS}) +# Link OpenEXR find_package(OpenEXR REQUIRED) +list(APPEND OLIVE_LIBRARIES ${OPENEXR_LIBRARIES}) +list(APPEND OLIVE_INCLUDE_DIRS ${OPENEXR_INCLUDES}) +if (APPLE) + # HACK: Brew on macOS separates OpenEXR and IlmBase into two folders even though they seem to + # expect to be in one. This includes the IlmBase files as if they were in the same folders + # as the OpenEXR headers. This would be better rolled into FindOpenEXR.cmake. + list(APPEND OLIVE_INCLUDE_DIRS ${ILMBASE_INCLUDES}/OpenEXR) +endif() +# Link Qt 5 find_package(Qt5 5.6 REQUIRED COMPONENTS Core @@ -55,7 +98,17 @@ find_package(Qt5 5.6 REQUIRED LinguistTools Concurrent ) +list(APPEND OLIVE_LIBRARIES + Qt5::Core + Qt5::Gui + Qt5::Widgets + Qt5::Multimedia + Qt5::OpenGL + Qt5::Svg + Qt5::Concurrent +) +# Link FFmpeg find_package(FFMPEG 3.0 REQUIRED COMPONENTS avutil @@ -65,19 +118,43 @@ find_package(FFMPEG 3.0 REQUIRED swscale swresample ) +list(APPEND OLIVE_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS}) +list(APPEND OLIVE_LIBRARIES + FFMPEG::avutil + FFMPEG::avcodec + FFMPEG::avformat + FFMPEG::avfilter + FFMPEG::swscale + FFMPEG::swresample +) +# Optional: Link OpenTimelineIO find_package(OpenTimelineIO) - -if (NOT OpenTimelineIO_FOUND) +if (OpenTimelineIO_FOUND) + list(APPEND OLIVE_DEFINITIONS USE_OTIO) + list(APPEND OLIVE_INCLUDE_DIRS ${OTIO_INCLUDE_DIRS}) + list(APPEND OLIVE_LIBRARIES ${OTIO_LIBRARIES}) +else() message(" OpenTimelineIO interchange will be disabled.") endif() +# Optional: Link Google Crashpad find_package(GoogleCrashpad) - -if (NOT GoogleCrashpad_FOUND) +if (GoogleCrashpad_FOUND) + list(APPEND OLIVE_DEFINITIONS USE_CRASHPAD) + list(APPEND OLIVE_INCLUDE_DIRS ${CRASHPAD_INCLUDE_DIRS}) + list(APPEND OLIVE_LIBRARIES ${CRASHPAD_LIBRARIES}) +else() message(" Automatic crash reporting will be disabled.") + + if (APPLE) + # Enables use of special functions for slider dragging, only linked if Crashpad isn't found + # because Crashpad links it itself and will cause duplicate references if we also link it + list(APPEND OLIVE_LIBRARIES "-framework ApplicationServices") + endif() endif() +# Generate Git hash if(EXISTS "${CMAKE_SOURCE_DIR}/.git") find_package(Git) if(GIT_FOUND) @@ -95,6 +172,7 @@ else() message("Olive: No Git hash defined!") endif() +# Optional: Find Doxygen if requested if(BUILD_DOXYGEN) find_package(Doxygen) endif() @@ -102,3 +180,8 @@ endif() set(CMAKE_INCLUDE_CURRENT_DIR ON) add_subdirectory(app) + +if (BUILD_TESTS) +enable_testing() +add_subdirectory(tests) +endif() diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 56b665cda..1f0b02aa0 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -16,26 +16,11 @@ # Set Olive sources and resources set(OLIVE_SOURCES - ${OLIVE_SOURCES} core.h core.cpp - main.cpp ) -# Include resource files for specific platforms -if (WIN32) - set(OLIVE_RESOURCES - ${OLIVE_RESOURCES} - packaging/windows/resources.rc - ) -elseif(APPLE) - set(OLIVE_ICON packaging/macos/olive.icns) - - set(OLIVE_RESOURCES - ${OLIVE_RESOURCES} - ${OLIVE_ICON} - ) -endif() +#set(OLIVE_RESOURCES) # Add subdirectories, which will populate the above variables add_subdirectory(audio) @@ -59,220 +44,21 @@ add_subdirectory(undo) add_subdirectory(widget) add_subdirectory(window) -# Add translations -qt5_add_translation(OLIVE_QM_FILES ${OLIVE_TS_FILES}) - -set(QRC_BODY "") -foreach(QM_FILE ${OLIVE_QM_FILES}) - get_filename_component(QM_FILENAME_COMPONENT ${QM_FILE} NAME_WE) - string(APPEND QRC_BODY "${QM_FILE}\n") -endforeach() -configure_file(ts/translations.qrc.in ts/translations.qrc @ONLY) - -set(OLIVE_RESOURCES - ${OLIVE_RESOURCES} - ${CMAKE_CURRENT_BINARY_DIR}/ts/translations.qrc -) - -# Add executable -add_executable(olive-editor +# Add main library +add_library(libolive-editor + SHARED ${OLIVE_SOURCES} ${OLIVE_RESOURCES} ) -# Set global definitions -target_compile_definitions(olive-editor PRIVATE ${OLIVE_DEFINITIONS}) +# Remove prefix - prevents CMake calling it "liblibolive-editor" +set_target_properties(libolive-editor PROPERTIES PREFIX "") -# Set include directories -target_include_directories( - olive-editor - PRIVATE - ${FFMPEG_INCLUDE_DIRS} - ${OCIO_INCLUDE_DIRS} - ${OIIO_INCLUDE_DIRS} - ${OPENEXR_INCLUDES} +# Add applicaton +add_executable(olive-editor + main.cpp ) -# Set compiler options -if(MSVC) - target_compile_options( - olive-editor - PRIVATE - /WX - /wd4267 - /wd4244 - /experimental:external - /external:anglebrackets - /external:W0 - "$<$:/O2>" - "$<$:/MP>" - ) -else() - target_compile_options( - olive-editor - PRIVATE - "$<$:-O2>" - -Werror - -Wuninitialized - -pedantic-errors - -Wall - -Wextra - -Wno-unused-parameter - -Wshadow - ) -endif() - -# Set link libraries -target_link_libraries( - olive-editor - PRIVATE - Qt5::Core - Qt5::Gui - Qt5::Widgets - Qt5::Multimedia - Qt5::OpenGL - Qt5::Svg - Qt5::Concurrent - OpenGL::GL - FFMPEG::avutil - FFMPEG::avcodec - FFMPEG::avformat - FFMPEG::avfilter - FFMPEG::swscale - FFMPEG::swresample - ${OCIO_LIBRARIES} - ${OIIO_LIBRARIES} - ${OPENEXR_LIBRARIES} -) - -# Set Mac-specific properties for application -if (APPLE) - set_target_properties(olive-editor PROPERTIES - MACOSX_BUNDLE TRUE - MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/packaging/macos/MacOSXBundleInfo.plist.in - MACOSX_BUNDLE_GUI_IDENTIFIER org.olivevideoeditor.Olive - MACOSX_BUNDLE_ICON_FILE olive.icns - RESOURCE "${OLIVE_ICON}" - OUTPUT_NAME "Olive" - ) - - # Enables use of special functions for slider dragging - if (NOT GoogleCrashpad_FOUND) - target_link_libraries( - olive-editor - PRIVATE - "-framework ApplicationServices" - ) - endif() - - # HACK: Brew on macOS separates OpenEXR and IlmBase into two folders even though they seem to - # expect to be in one. This includes the IlmBase files as if they were in the same folders - # as the OpenEXR headers. This would be better rolled into FindOpenEXR.cmake. - target_include_directories( - olive-editor - PRIVATE - ${ILMBASE_INCLUDES}/OpenEXR - ) -endif() - -# Set Linux-specific properties for application -if(UNIX AND NOT APPLE) - install(TARGETS olive-editor RUNTIME DESTINATION bin) -endif() - -# Enable OTIO if found -if (OpenTimelineIO_FOUND) - target_compile_definitions(olive-editor PRIVATE USE_OTIO) - - target_include_directories( - olive-editor - PRIVATE - ${OTIO_INCLUDE_DIRS} - ) - - target_link_libraries( - olive-editor - PRIVATE - ${OTIO_LIBRARIES} - ) -endif() - -# Enable Crashpad if found -if (GoogleCrashpad_FOUND) - # Signal code to use Crashpad - target_compile_definitions( - olive-editor - PRIVATE - USE_CRASHPAD - ) - - # Include Crashpad headers - target_include_directories( - olive-editor - PRIVATE - ${CRASHPAD_INCLUDE_DIRS} - ) - - # Include Crashpad libs - target_link_libraries( - olive-editor - PRIVATE - ${CRASHPAD_LIBRARIES} - ) - - # Create crash handler executable - add_executable( - olive-crashhandler - dialog/crashhandler/crashhandler.h - dialog/crashhandler/crashhandler.cpp - dialog/crashhandler/crashhandlermain.cpp - ) - - # Disable console appearing on crash handler dialog - set_target_properties(olive-crashhandler PROPERTIES - WIN32_EXECUTABLE TRUE - ) - - # Set crash handler includes - target_include_directories( - olive-crashhandler - PRIVATE - ${CRASHPAD_INCLUDE_DIRS} - ) - - # Set crash handler libs - target_link_libraries( - olive-crashhandler - PRIVATE - Qt5::Core - Qt5::Gui - Qt5::Widgets - Qt5::Network - ${CRASHPAD_LIBRARIES} - ) - - # Set global Olive compiler definitions - target_compile_definitions(olive-crashhandler PRIVATE ${OLIVE_DEFINITIONS}) - - set(CRASHPAD_HANDLER "crashpad_handler${CMAKE_EXECUTABLE_SUFFIX}") - set(MINIDUMP_STACKWALK "minidump_stackwalk${CMAKE_EXECUTABLE_SUFFIX}") - - if(UNIX AND NOT APPLE) - install(TARGETS olive-crashhandler RUNTIME DESTINATION bin) - install(PROGRAMS ${CRASHPAD_LIBRARY_DIRS}/${CRASHPAD_HANDLER} DESTINATION bin) - install(PROGRAMS ${BREAKPAD_BIN_DIR}/${MINIDUMP_STACKWALK} DESTINATION bin) - endif() - - if(APPLE) - # Move crash handler executables inside Mac app bundle - add_custom_command(TARGET olive-crashhandler POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different olive-crashhandler $ - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CRASHPAD_LIBRARY_DIRS}/${CRASHPAD_HANDLER} $ - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${BREAKPAD_BIN_DIR}/${MINIDUMP_STACKWALK} $ - ) - endif() -endif() - # Create docs if doxygen was found if(DOXYGEN_FOUND) set(DOXYGEN_PROJECT_NAME "Olive") @@ -281,3 +67,48 @@ if(DOXYGEN_FOUND) set(DOXYGEN_EXTRACT_PRIVATE "YES") doxygen_add_docs(docs ALL ${OLIVE_SOURCES}) endif() + +# Platform-specific deployment preferences +if (WIN32) + # Set Windows application icon + target_sources(olive-editor PRIVATE packaging/windows/resources.rc) +elseif(APPLE) + # Set Mac application icon + set(OLIVE_ICON packaging/macos/olive.icns) + target_sources(olive-editor PRIVATE ${OLIVE_ICON}) + + # Set Mac bundle properties + set_target_properties(olive-editor PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/packaging/macos/MacOSXBundleInfo.plist.in + MACOSX_BUNDLE_GUI_IDENTIFIER org.olivevideoeditor.Olive + MACOSX_BUNDLE_ICON_FILE olive.icns + RESOURCE "${OLIVE_ICON}" + OUTPUT_NAME "Olive" + ) +elseif(UNIX) + # Set Linux-specific properties for application + #install(TARGETS olive-editor RUNTIME DESTINATION bin) + #install(TARGETS libolive-editor RUNTIME DESTINATION lib) +endif() + +# Set link libraries +target_link_libraries(olive-editor PRIVATE libolive-editor ${OLIVE_LIBRARIES}) +target_link_libraries(libolive-editor PRIVATE ${OLIVE_LIBRARIES}) + +# Set compile options +target_compile_options(olive-editor PRIVATE ${OLIVE_COMPILE_OPTIONS}) +target_compile_options(libolive-editor PRIVATE ${OLIVE_COMPILE_OPTIONS}) + +# Set global definitions +target_compile_definitions(olive-editor PRIVATE ${OLIVE_DEFINITIONS}) +target_compile_definitions(libolive-editor PRIVATE ${OLIVE_DEFINITIONS}) + +# Set include dirs +target_include_directories(olive-editor PRIVATE ${OLIVE_INCLUDE_DIRS}) +target_include_directories(libolive-editor PRIVATE ${OLIVE_INCLUDE_DIRS}) + +# Add crash handler +if (GoogleCrashpad_FOUND) + add_subdirectory(crashhandler) +endif() diff --git a/app/crashhandler/CMakeLists.txt b/app/crashhandler/CMakeLists.txt new file mode 100644 index 000000000..0c6682814 --- /dev/null +++ b/app/crashhandler/CMakeLists.txt @@ -0,0 +1,65 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2021 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Create crash handler executable +add_executable( + olive-crashhandler + dialog/crashhandler/crashhandler.h + dialog/crashhandler/crashhandler.cpp + dialog/crashhandler/crashhandlermain.cpp +) + +# Disable console appearing on crash handler dialog +set_target_properties(olive-crashhandler PROPERTIES + WIN32_EXECUTABLE TRUE +) + +# Set crash handler includes +target_include_directories( + olive-crashhandler + PRIVATE + ${CRASHPAD_INCLUDE_DIRS} +) + +# Set crash handler libs +target_link_libraries( + olive-crashhandler + PRIVATE + Qt5::Core + Qt5::Gui + Qt5::Widgets + Qt5::Network + ${CRASHPAD_LIBRARIES} +) + +set(CRASHPAD_HANDLER "crashpad_handler${CMAKE_EXECUTABLE_SUFFIX}") +set(MINIDUMP_STACKWALK "minidump_stackwalk${CMAKE_EXECUTABLE_SUFFIX}") + +if(APPLE) + # Move crash handler executables inside Mac app bundle + add_custom_command(TARGET olive-crashhandler POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different olive-crashhandler $ + COMMAND ${CMAKE_COMMAND} -E copy_if_different libolive-editor $ + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CRASHPAD_LIBRARY_DIRS}/${CRASHPAD_HANDLER} $ + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${BREAKPAD_BIN_DIR}/${MINIDUMP_STACKWALK} $ + ) +elseif(UNIX) + install(TARGETS olive-crashhandler RUNTIME DESTINATION bin) + install(PROGRAMS ${CRASHPAD_LIBRARY_DIRS}/${CRASHPAD_HANDLER} DESTINATION bin) + install(PROGRAMS ${BREAKPAD_BIN_DIR}/${MINIDUMP_STACKWALK} DESTINATION bin) +endif() + +target_compile_definitions(olive-crashhandler PRIVATE ${OLIVE_DEFINITIONS}) diff --git a/app/dialog/crashhandler/crashhandler.cpp b/app/crashhandler/crashhandler.cpp similarity index 100% rename from app/dialog/crashhandler/crashhandler.cpp rename to app/crashhandler/crashhandler.cpp diff --git a/app/dialog/crashhandler/crashhandler.h b/app/crashhandler/crashhandler.h similarity index 100% rename from app/dialog/crashhandler/crashhandler.h rename to app/crashhandler/crashhandler.h diff --git a/app/dialog/crashhandler/crashhandlermain.cpp b/app/crashhandler/crashhandlermain.cpp similarity index 100% rename from app/dialog/crashhandler/crashhandlermain.cpp rename to app/crashhandler/crashhandlermain.cpp diff --git a/app/ts/CMakeLists.txt b/app/ts/CMakeLists.txt index 346600656..06de3d0d0 100644 --- a/app/ts/CMakeLists.txt +++ b/app/ts/CMakeLists.txt @@ -33,3 +33,19 @@ set(OLIVE_TS_FILES ts/zh_TW.ts PARENT_SCOPE ) + +# Add translations +qt5_add_translation(OLIVE_QM_FILES ${OLIVE_TS_FILES}) + +set(QRC_BODY "") +foreach(QM_FILE ${OLIVE_QM_FILES}) + get_filename_component(QM_FILENAME_COMPONENT ${QM_FILE} NAME_WE) + string(APPEND QRC_BODY "${QM_FILE}\n") +endforeach() +configure_file(translations.qrc.in translations.qrc @ONLY) + +set(OLIVE_RESOURCES + ${OLIVE_RESOURCES} + ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc + PARENT_SCOPE +) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 000000000..59a0f8441 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,68 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2021 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +function(olive_add_test GROUP NAME SOURCE) + file(READ "${SOURCE}" TEST_FILE_CONTENT) + string(REGEX MATCHALL "OLIVE_ADD_TEST\(.[A-Za-z0-9]+\)" TEST_FUNCTIONS ${TEST_FILE_CONTENT}) + set(TEST_BODY "int main(int argc, char** argv)\n{\n") + set(TEST_INDEX 1) + list(LENGTH TEST_FUNCTIONS TEST_COUNT) + foreach (TEST_FUNC ${TEST_FUNCTIONS}) + string(REPLACE "OLIVE_ADD_TEST(" "" TEST_FUNC "${TEST_FUNC}") + string(APPEND TEST_BODY " std::cout << \"[${TEST_INDEX}/${TEST_COUNT}] ${GROUP} - ${TEST_FUNC}\" << std::endl;\n") + string(APPEND TEST_BODY " if (!olive::Test${TEST_FUNC}()) return 1;\n") + MATH(EXPR TEST_INDEX "${TEST_INDEX}+1") + endforeach() + string(APPEND TEST_BODY " return 0;\n}") + + set_property( + DIRECTORY + APPEND + PROPERTY CMAKE_CONFIGURE_DEPENDS ${SOURCE} + ) + + set(OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/${SOURCE}") + + string(APPEND TEST_FILE_CONTENT "\n${TEST_BODY}") + file(WRITE "${OUTPUT_FILE}" "${TEST_FILE_CONTENT}") + + add_executable(${NAME} ${OUTPUT_FILE}) + target_include_directories( + ${NAME} + PRIVATE + ${CMAKE_SOURCE_DIR}/app + ${OLIVE_INCLUDE_DIRS} + ) + target_link_libraries( + ${NAME} + PRIVATE + libolive-editor + ${OLIVE_LIBRARIES} + ) + target_compile_definitions( + ${NAME} + PRIVATE + ${OLIVE_DEFINITIONS} + ) + target_compile_options( + ${NAME} + PRIVATE + ${OLIVE_COMPILE_OPTIONS} + ) + add_test(${NAME} ${NAME}) +endfunction() + +add_subdirectory(timeline) diff --git a/tests/testutil.h b/tests/testutil.h new file mode 100644 index 000000000..e9c1013f2 --- /dev/null +++ b/tests/testutil.h @@ -0,0 +1,24 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#define OLIVE_ASSERT(x) if (!(x)) return false +#define OLIVE_TEST_END return true + +#define OLIVE_ADD_TEST(x) bool Test##x() diff --git a/tests/timeline/CMakeLists.txt b/tests/timeline/CMakeLists.txt new file mode 100644 index 000000000..7892dcd54 --- /dev/null +++ b/tests/timeline/CMakeLists.txt @@ -0,0 +1,17 @@ +# Olive - Non-Linear Video Editor +# Copyright (C) 2021 Olive Team +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +olive_add_test(Timeline timeline-tests timeline-tests.cpp) diff --git a/tests/timeline/timeline-tests.cpp b/tests/timeline/timeline-tests.cpp new file mode 100644 index 000000000..c0f0d4a88 --- /dev/null +++ b/tests/timeline/timeline-tests.cpp @@ -0,0 +1,219 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2021 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "core.h" +#include "node/block/clip/clip.h" +#include "node/math/math/math.h" +#include "node/math/merge/merge.h" +#include "node/project/project.h" +#include "node/project/sequence/sequence.h" +#include "undo/undocommand.h" +#include "widget/timelinewidget/timelineundo.h" +#include "../testutil.h" + +namespace olive { + +#define TIMELINE_TEST_START \ + Project project; \ + Sequence sequence; \ + sequence.setParent(&project) + +OLIVE_ADD_TEST(AddTrack) +{ + TIMELINE_TEST_START; + + Track *first_video_track, *first_audio_track; + + { + // Test creating initial video track + first_video_track = TimelineAddTrackCommand::RunImmediately(sequence.track_list(Track::kVideo)); + + OLIVE_ASSERT(sequence.GetConnectedOutput(Sequence::kTextureInput).node() == first_video_track); + OLIVE_ASSERT(sequence.track_list(Track::kVideo)->GetTrackCount() == 1); + OLIVE_ASSERT(sequence.track_list(Track::kVideo)->GetTrackAt(0) == first_video_track); + } + + { + // Test creating initial audio track + first_audio_track = TimelineAddTrackCommand::RunImmediately(sequence.track_list(Track::kAudio)); + + OLIVE_ASSERT(sequence.GetConnectedOutput(Sequence::kSamplesInput).node() == first_audio_track); + OLIVE_ASSERT(sequence.track_list(Track::kAudio)->GetTrackCount() == 1); + OLIVE_ASSERT(sequence.track_list(Track::kAudio)->GetTrackAt(0) == first_audio_track); + } + + { + // Test creating second video track with merge + Track* second_video_track = TimelineAddTrackCommand::RunImmediately(sequence.track_list(Track::kVideo), true); + OLIVE_ASSERT(sequence.GetConnectedOutput(Sequence::kTextureInput).node() != first_video_track); + OLIVE_ASSERT(sequence.GetConnectedOutput(Sequence::kTextureInput).node() != second_video_track); + OLIVE_ASSERT(sequence.track_list(Track::kVideo)->GetTrackCount() == 2); + OLIVE_ASSERT(sequence.track_list(Track::kVideo)->GetTrackAt(1) == second_video_track); + + MergeNode* merge = dynamic_cast(sequence.GetConnectedOutput(Sequence::kTextureInput).node()); + OLIVE_ASSERT(merge); + OLIVE_ASSERT(merge->GetConnectedOutput(MergeNode::kBaseIn).node() == first_video_track); + OLIVE_ASSERT(merge->GetConnectedOutput(MergeNode::kBlendIn).node() == second_video_track); + } + + { + // Test creating second audio track with merge + Track* second_audio_track = TimelineAddTrackCommand::RunImmediately(sequence.track_list(Track::kAudio), true); + OLIVE_ASSERT(sequence.GetConnectedOutput(Sequence::kSamplesInput).node() != first_audio_track); + OLIVE_ASSERT(sequence.GetConnectedOutput(Sequence::kSamplesInput).node() != second_audio_track); + OLIVE_ASSERT(sequence.track_list(Track::kAudio)->GetTrackCount() == 2); + OLIVE_ASSERT(sequence.track_list(Track::kAudio)->GetTrackAt(1) == second_audio_track); + + MathNode* merge = dynamic_cast(sequence.GetConnectedOutput(Sequence::kSamplesInput).node()); + OLIVE_ASSERT(merge); + OLIVE_ASSERT(merge->GetConnectedOutput(MathNode::kParamAIn).node() == first_audio_track); + OLIVE_ASSERT(merge->GetConnectedOutput(MathNode::kParamBIn).node() == second_audio_track); + } + + OLIVE_TEST_END; +} + +OLIVE_ADD_TEST(SequenceDefaults) +{ + TIMELINE_TEST_START; + + sequence.add_default_nodes(); + + OLIVE_ASSERT(sequence.GetTracks().size() == 2); + + Track* tex_connect = dynamic_cast(sequence.GetConnectedTextureOutput().node()); + OLIVE_ASSERT(tex_connect); + Track* smp_connect = dynamic_cast(sequence.GetConnectedSampleOutput().node()); + OLIVE_ASSERT(smp_connect); + OLIVE_ASSERT(tex_connect != smp_connect); + OLIVE_ASSERT(sequence.GetTracks().contains(tex_connect)); + OLIVE_ASSERT(sequence.GetTracks().contains(smp_connect)); + + OLIVE_TEST_END; +} + +OLIVE_ADD_TEST(Trim) +{ + TIMELINE_TEST_START; + + sequence.add_default_nodes(); + + Track* track = sequence.GetTracks().first(); + + ClipBlock* block1 = new ClipBlock(); + block1->set_length_and_media_out(2); + project.setParent(block1); + track->AppendBlock(block1); + + ClipBlock* block2 = new ClipBlock(); + block2->set_length_and_media_out(2); + project.setParent(block2); + track->AppendBlock(block2); + + // There should be two blocks right now + OLIVE_ASSERT(track->Blocks().size() == 2); + + { + // Trim out point of second block + BlockTrimCommand command(track, block2, 1, Timeline::kTrimOut); + command.redo(); + + // No block should have been added + OLIVE_ASSERT(track->Blocks().size() == 2); + OLIVE_ASSERT(block2->length() == 1); + OLIVE_ASSERT(block1->length() == 2); + + command.undo(); + + OLIVE_ASSERT(track->Blocks().size() == 2); + OLIVE_ASSERT(block2->length() == 2); + OLIVE_ASSERT(block1->length() == 2); + } + + { + // Trim in point of second block + BlockTrimCommand command(track, block2, 1, Timeline::kTrimIn); + command.redo(); + + // Gap should be inserted in between + OLIVE_ASSERT(track->Blocks().size() == 3); + GapBlock* gap = dynamic_cast(track->Blocks().at(1)); + OLIVE_ASSERT(gap); + OLIVE_ASSERT(gap->length() == 1); + OLIVE_ASSERT(block2->length() == 1); + OLIVE_ASSERT(block1->length() == 2); + OLIVE_ASSERT(block1->next() == gap); + OLIVE_ASSERT(block2->previous() == gap); + + command.undo(); + + OLIVE_ASSERT(track->Blocks().size() == 2); + OLIVE_ASSERT(block2->length() == 2); + OLIVE_ASSERT(block1->length() == 2); + } + + { + // Trim out point of first block + BlockTrimCommand command(track, block1, 1, Timeline::kTrimOut); + command.redo(); + + // Gap should be inserted in between + OLIVE_ASSERT(track->Blocks().size() == 3); + GapBlock* gap = dynamic_cast(track->Blocks().at(1)); + OLIVE_ASSERT(gap); + OLIVE_ASSERT(gap->length() == 1); + OLIVE_ASSERT(block1->length() == 1); + OLIVE_ASSERT(block2->length() == 2); + OLIVE_ASSERT(block1->next() == gap); + OLIVE_ASSERT(block2->previous() == gap); + + command.undo(); + + OLIVE_ASSERT(track->Blocks().size() == 2); + OLIVE_ASSERT(block2->length() == 2); + OLIVE_ASSERT(block1->length() == 2); + } + + { + // Trim in point of first block + BlockTrimCommand command(track, block1, 1, Timeline::kTrimIn); + command.redo(); + + // Gap should be prepended to the start + OLIVE_ASSERT(track->Blocks().size() == 3); + GapBlock* gap = dynamic_cast(track->Blocks().at(0)); + OLIVE_ASSERT(gap); + OLIVE_ASSERT(gap->length() == 1); + OLIVE_ASSERT(block1->length() == 1); + OLIVE_ASSERT(block2->length() == 2); + OLIVE_ASSERT(block1->next() == block2); + OLIVE_ASSERT(block1->previous() == gap); + + command.undo(); + + OLIVE_ASSERT(track->Blocks().size() == 2); + OLIVE_ASSERT(block2->length() == 2); + OLIVE_ASSERT(block1->length() == 2); + } + + OLIVE_TEST_END; +} + +}