diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..cd2bcb0 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,38 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build gtests (MSVC)", + "type": "shell", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "scripts/build-tests-vscode.ps1" + ], + "group": "build", + "problemMatcher": [ + "$msCompile" + ] + }, + { + "label": "Build + Run gtests (MSVC)", + "type": "shell", + "command": "powershell", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "scripts/build-tests-vscode.ps1", + "-RunTests" + ], + "group": "test", + "problemMatcher": [ + "$msCompile" + ] + } + ] +} diff --git a/CMakeLists.txt b/CMakeLists.txt index f9c5e77..f7e6f4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,8 @@ project(CutScene VERSION 0.1 LANGUAGES CXX) set(CMAKE_CXX_STANDARD_REQUIRED ON) +include(CTest) + find_package(Qt6 6.4 REQUIRED COMPONENTS Core Gui Widgets Multimedia MultimediaWidgets) find_package(FFMPEG REQUIRED) @@ -74,3 +76,19 @@ install(TARGETS appCutScene LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) + +if(BUILD_TESTING) + include(FetchContent) + + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip + ) + + # Prevent GoogleTest from overriding our compiler/linker options on Windows. + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + + FetchContent_MakeAvailable(googletest) + + add_subdirectory(tests) +endif() diff --git a/scripts/build-tests-vscode.ps1 b/scripts/build-tests-vscode.ps1 new file mode 100644 index 0000000..889cca2 --- /dev/null +++ b/scripts/build-tests-vscode.ps1 @@ -0,0 +1,35 @@ +param( + [string]$BuildDir = "build/vscode-msvc-debug", + [string]$QtDir = "C:/Qt/6.7.0/msvc2019_64", + [switch]$RunTests +) + +$ErrorActionPreference = "Stop" + +$vswhere = Join-Path ${env:ProgramFiles(x86)} "Microsoft Visual Studio\Installer\vswhere.exe" +if (-not (Test-Path $vswhere)) { + throw "vswhere not found: $vswhere" +} + +$vsInstall = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath +if (-not $vsInstall) { + throw "Visual Studio with C++ tools was not found." +} + +$devCmd = Join-Path $vsInstall "Common7\Tools\VsDevCmd.bat" +if (-not (Test-Path $devCmd)) { + throw "VsDevCmd.bat not found: $devCmd" +} + +$configureCmd = "cmake -S . -B `"$BuildDir`" -G `"Ninja`" -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON -DCMAKE_PREFIX_PATH=`"$QtDir`"" +$buildCmd = "cmake --build `"$BuildDir`" --target cutscene_tests" +$testCmd = "ctest --test-dir `"$BuildDir`" --output-on-failure" + +$qtBinDir = Join-Path $QtDir "bin" +$fullCmd = "`"$devCmd`" -arch=x64 && set `"PATH=$qtBinDir;%PATH%`" && $configureCmd && $buildCmd" +if ($RunTests) { + $fullCmd += " && $testCmd" +} + +cmd.exe /c $fullCmd +exit $LASTEXITCODE diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..fffbebc --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,27 @@ +add_executable(cutscene_tests + test_main.cpp + drop_widget_test.cpp + header_bar_widget_test.cpp + ../src/DropWidget.h + ../src/HeaderBarWidget.cpp + ../src/HeaderBarWidget.h +) + +set_target_properties(cutscene_tests PROPERTIES + AUTOMOC ON +) + +target_include_directories(cutscene_tests + PRIVATE + ../src +) + +target_link_libraries(cutscene_tests + PRIVATE + GTest::gtest + Qt6::Core + Qt6::Widgets +) + +include(GoogleTest) +gtest_add_tests(TARGET cutscene_tests) diff --git a/tests/drop_widget_test.cpp b/tests/drop_widget_test.cpp new file mode 100644 index 0000000..7bea7da --- /dev/null +++ b/tests/drop_widget_test.cpp @@ -0,0 +1,46 @@ +#include "../src/DropWidget.h" + +#include +#include + +class TestableDropWidget : public DropWidget { +public: + using DropWidget::DropWidget; + using DropWidget::dragEnterEvent; + using DropWidget::dropEvent; +}; + +TEST(DropWidgetTest, ConstructorSetsExpectedDefaults) +{ + TestableDropWidget widget; + + EXPECT_EQ(widget.text().toStdString(), "Drop Here"); + EXPECT_TRUE(widget.acceptDrops()); + EXPECT_EQ(widget.alignment(), Qt::AlignCenter); +} + +TEST(DropWidgetTest, DragEnterAcceptsTextMimeData) +{ + TestableDropWidget widget; + QMimeData mimeData; + mimeData.setText("clip.mp4"); + + QDragEnterEvent event(QPoint(5, 5), Qt::CopyAction, &mimeData, Qt::LeftButton, Qt::NoModifier); + + widget.dragEnterEvent(&event); + + EXPECT_TRUE(event.isAccepted()); +} + +TEST(DropWidgetTest, DropEventUpdatesDisplayedText) +{ + TestableDropWidget widget; + QMimeData mimeData; + mimeData.setText("new media"); + + QDropEvent event(QPointF(10, 10), Qt::CopyAction, &mimeData, Qt::LeftButton, Qt::NoModifier); + + widget.dropEvent(&event); + + EXPECT_EQ(widget.text().toStdString(), "new media"); +} diff --git a/tests/header_bar_widget_test.cpp b/tests/header_bar_widget_test.cpp new file mode 100644 index 0000000..b3cb616 --- /dev/null +++ b/tests/header_bar_widget_test.cpp @@ -0,0 +1,53 @@ +#include "../src/HeaderBarWidget.h" + +#include +#include +#include +#include +#include + +namespace { +QPushButton *findButtonByText(HeaderBarWidget &widget, const QString &text) +{ + const QList buttons = widget.findChildren(); + for (QPushButton *button : buttons) { + if (button && button->text() == text) { + return button; + } + } + return nullptr; +} +} + +TEST(HeaderBarWidgetTest, ConstructorSetsExpectedTitleAndHeight) +{ + HeaderBarWidget widget; + + EXPECT_EQ(widget.minimumHeight(), 50); + EXPECT_EQ(widget.maximumHeight(), 50); + + const QList labels = widget.findChildren(); + bool foundTitle = false; + for (QLabel *label : labels) { + if (label && label->text() == "CutScene") { + foundTitle = true; + break; + } + } + EXPECT_TRUE(foundTitle); +} + +TEST(HeaderBarWidgetTest, FileButtonClickEmitsFileClickedSignal) +{ + HeaderBarWidget widget; + int emissionCount = 0; + QObject::connect(&widget, &HeaderBarWidget::fileClicked, [&emissionCount]() { + ++emissionCount; + }); + + QPushButton *fileButton = findButtonByText(widget, "File"); + ASSERT_NE(fileButton, nullptr); + + fileButton->click(); + EXPECT_EQ(emissionCount, 1); +} diff --git a/tests/test_main.cpp b/tests/test_main.cpp new file mode 100644 index 0000000..96f1ec0 --- /dev/null +++ b/tests/test_main.cpp @@ -0,0 +1,12 @@ +#include +#include +#include + +int main(int argc, char **argv) +{ + qputenv("QT_QPA_PLATFORM", QByteArray("offscreen")); + QApplication app(argc, argv); + + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}