diff --git a/.github/workflows/build_cmake.yml b/.github/workflows/build_cmake.yml index c24165d..2b0d62f 100644 --- a/.github/workflows/build_cmake.yml +++ b/.github/workflows/build_cmake.yml @@ -70,7 +70,7 @@ jobs: cmakeVersion: ${{ env.CMAKE_VERSION }} ninjaVersion: ${{ env.NINJA_VERSION }} - - name: Install system libs + - name: Install dependencies shell: cmake -P {0} run: | if ("${{ runner.os }}" STREQUAL "Linux") @@ -78,7 +78,13 @@ jobs: COMMAND sudo apt update ) execute_process( - COMMAND sudo apt install libgl1-mesa-dev + COMMAND sudo apt install + # build dependencies + libgl1-mesa-dev libgtest-dev + # runtime dependencies for tests (Qt is downloaded outside package manager, + # thus minimal dependencies must be installed explicitly) + libsecret-1-0 libxcb-cursor0 libxcb-icccm4 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-render0 + libxcb-shape0 libxcb-shm0 libxcb-sync1 libxcb-xfixes0 libxcb-xkb1 libxkbcommon-x11-0 xvfb RESULT_VARIABLE result ) if (NOT result EQUAL 0) @@ -258,6 +264,11 @@ jobs: name: ${{ env.PLUGIN_NAME }}-origin-json path: ./build/build/${{ env.PLUGIN_NAME }}.json + - name: Run unit tests + if: matrix.config.os == 'ubuntu-latest' + run: | + xvfb-run ./build/build/test/QodeAssistTest + update_json: if: contains(github.ref, 'tags/v') runs-on: ubuntu-latest diff --git a/CMakeLists.txt b/CMakeLists.txt index 7cc1f76..83ea3e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) find_package(QtCreator REQUIRED COMPONENTS Core) find_package(Qt6 COMPONENTS Core Gui Quick Widgets Network REQUIRED) +find_package(GTest) add_definitions( -DQODEASSIST_QT_CREATOR_VERSION_MAJOR=${QODEASSIST_QT_CREATOR_VERSION_MAJOR} @@ -24,6 +25,9 @@ add_subdirectory(settings) add_subdirectory(logger) add_subdirectory(ChatView) add_subdirectory(context) +if(GTest_FOUND) + add_subdirectory(test) +endif() add_qtc_plugin(QodeAssist PLUGIN_DEPENDS diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..6702761 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,14 @@ +add_executable(QodeAssistTest + DocumentContextReaderTest.cpp + unittest_main.cpp +) + +target_link_libraries(QodeAssistTest PRIVATE + Qt::Core + GTest::GTest + GTest::Main + QtCreator::LanguageClient + Context +) + +add_test(NAME QodeAssistTest COMMAND QodeAssistTest) diff --git a/test/DocumentContextReaderTest.cpp b/test/DocumentContextReaderTest.cpp new file mode 100644 index 0000000..d3b074a --- /dev/null +++ b/test/DocumentContextReaderTest.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2025 Povilas Kanapickas + * + * This file is part of QodeAssist. + * + * QodeAssist 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. + * + * QodeAssist 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 QodeAssist. If not, see . + */ + +#include "context/DocumentContextReader.hpp" + +#include +#include + +using namespace QodeAssist::Context; + +std::ostream &operator<<(std::ostream &out, const QString &value) +{ + out << value.toStdString(); + return out; +} + +class DocumentContextReaderTest : public QObject, public testing::Test +{ + Q_OBJECT + +protected: + QTextDocument *createTestDocument(const QString &text) + { + auto *doc = new QTextDocument(this); + doc->setPlainText(text); + return doc; + } + + DocumentContextReader createTestReader(const QString &text) + { + return DocumentContextReader(createTestDocument(text), "text/python", "/path/to/file"); + } +}; + +TEST_F(DocumentContextReaderTest, testGetLineText) +{ + auto reader = createTestReader("Line 1\nLine 2\nLine 3"); + + EXPECT_EQ(reader.getLineText(0), "Line 1"); + EXPECT_EQ(reader.getLineText(1), "Line 2"); + EXPECT_EQ(reader.getLineText(2), "Line 3"); + EXPECT_EQ(reader.getLineText(0, 4), "Line"); +} + +TEST_F(DocumentContextReaderTest, testGetContextBefore) +{ + auto reader = createTestReader("Line 1\nLine 2\nLine 3\nLine 4\nLine 5"); + + EXPECT_EQ(reader.getContextBefore(2, -1, 2), "Line 1\nLine 2\nLine 3"); + EXPECT_EQ(reader.getContextBefore(4, -1, 3), "Line 2\nLine 3\nLine 4\nLine 5"); +} + +TEST_F(DocumentContextReaderTest, testGetContextAfter) +{ + auto reader = createTestReader("Line 1\nLine 2\nLine 3\nLine 4\nLine 5"); + + EXPECT_EQ(reader.getContextAfter(0, -1, 2), "Line 2\nLine 3"); + EXPECT_EQ(reader.getContextAfter(2, -1, 2), "Line 4\nLine 5"); +} + +TEST_F(DocumentContextReaderTest, testGetContextBeforeWithCopyright) +{ + auto reader = createTestReader("/* Copyright (C) 2024 */\nLine 1\nLine 2\nLine 3\nLine 4"); + + // Test getting context before with copyright header + EXPECT_EQ(reader.getContextBefore(2, -1, 2), "Line 1\nLine 2"); + + // Test getting context before skipping copyright + EXPECT_EQ(reader.getContextBefore(3, 0, 2), "Line 1\nLine 2\n"); +} + +TEST_F(DocumentContextReaderTest, testGetContextAfterWithCopyright) +{ + auto reader = createTestReader("/* Copyright (C) 2024 */\nLine 1\nLine 2\nLine 3\nLine 4"); + + // Test getting context after copyright header + EXPECT_EQ(reader.getContextAfter(0, -1, 2), "Line 1\nLine 2"); + + // Test getting context after with copyright skipped + EXPECT_EQ(reader.getContextAfter(1, 0, 2), "Line 2\n"); +} + +TEST_F(DocumentContextReaderTest, testReadWholeFile) +{ + auto reader = createTestReader("Line 1\nLine 2\nLine 3\nLine 4\nLine 5"); + + EXPECT_EQ(reader.readWholeFileBefore(2, -1), "Line 1\nLine 2\nLine 3"); + EXPECT_EQ(reader.readWholeFileAfter(2, -1), "Line 3\nLine 4\nLine 5"); +} + +TEST_F(DocumentContextReaderTest, testReadWholeFileWithCopyright) +{ + auto reader = createTestReader("/* Copyright (C) 2024 */\nLine 1\nLine 2\nLine 3\nLine 4"); + + EXPECT_EQ(reader.readWholeFileBefore(2, -1), "Line 1\nLine 2"); + EXPECT_EQ(reader.readWholeFileAfter(2, -1), "Line 2\nLine 3\nLine 4"); + + EXPECT_EQ(reader.readWholeFileBefore(2, 0), "Line 1\n"); + EXPECT_EQ(reader.readWholeFileAfter(2, 0), "Line 2\nLine 3\n"); +} + +TEST_F(DocumentContextReaderTest, testReadWholeFileWithMultilineCopyright) +{ + auto reader = createTestReader( + "/*\n * Copyright (C) 2024\n * \n * This file is part of QodeAssist.\n */\n" + "Line 1\nLine 2"); + + EXPECT_EQ(reader.readWholeFileBefore(6, -1), "Line 1\nLine 2"); + EXPECT_EQ(reader.readWholeFileAfter(5, -1), "Line 1\nLine 2"); + + EXPECT_EQ(reader.readWholeFileBefore(6, 4), "Line 1\nLine"); + EXPECT_EQ(reader.readWholeFileAfter(5, 4), "Line 1\nLine"); +} + +TEST_F(DocumentContextReaderTest, testFindCopyrightSingleLine) +{ + auto reader = createTestReader("/* Copyright (C) 2024 */\nCode line 1\nCode line 2"); + + auto info = reader.findCopyright(); + ASSERT_TRUE(info.found); + EXPECT_EQ(info.startLine, 0); + EXPECT_EQ(info.endLine, 0); +} + +TEST_F(DocumentContextReaderTest, testFindCopyrightMultiLine) +{ + auto reader = createTestReader( + "/*\n * Copyright (C) 2024\n * \n * This file is part of QodeAssist.\n */\nCode line 1"); + + auto info = reader.findCopyright(); + ASSERT_TRUE(info.found); + EXPECT_EQ(info.startLine, 0); + EXPECT_EQ(info.endLine, 4); +} + +TEST_F(DocumentContextReaderTest, testFindCopyrightMultipleBlocks) +{ + auto reader = createTestReader("/* Copyright 2023 */\n\n/* Copyright 2024 */\nCode"); + + auto info = reader.findCopyright(); + ASSERT_TRUE(info.found); + EXPECT_EQ(info.startLine, 0); + EXPECT_EQ(info.endLine, 0); +} + +TEST_F(DocumentContextReaderTest, testFindCopyrightNoCopyright) +{ + auto reader = createTestReader("/* Just a comment */\nCode line 1"); + + auto info = reader.findCopyright(); + ASSERT_TRUE(!info.found); + EXPECT_EQ(info.startLine, -1); + EXPECT_EQ(info.endLine, -1); +} + +TEST_F(DocumentContextReaderTest, testGetContextBetween) +{ + auto reader = createTestReader("Line 1\nLine 2\nLine 3\nLine 4\nLine 5"); + + EXPECT_EQ(reader.getContextBetween(1, 3, -1), "Line 2\nLine 3\nLine 4"); + EXPECT_EQ(reader.getContextBetween(0, 2, 4), "Line 1\nLine 2\nLine"); +} + +#include "DocumentContextReaderTest.moc" diff --git a/test/unittest_main.cpp b/test/unittest_main.cpp new file mode 100644 index 0000000..2ed6164 --- /dev/null +++ b/test/unittest_main.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2025 Povilas Kanapickas + * + * This file is part of QodeAssist. + * + * QodeAssist 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. + * + * QodeAssist 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 QodeAssist. If not, see . + */ + +#include +#include + +int main(int argc, char *argv[]) +{ + QGuiApplication application(argc, argv); + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}