From 247256d4a48b1f8bb892ffd9aca02fe1c77439ae Mon Sep 17 00:00:00 2001 From: Povilas Kanapickas Date: Wed, 5 Mar 2025 16:01:52 +0200 Subject: [PATCH] chore: Add unit tests for DocumentContextReader (#90) chore: Add unit tests for DocumentContextReader The tests are based on GTest like some tests in Qt Creator itself, which makes it easy to run as full Qt Creator does not need to be started. --- .github/workflows/build_cmake.yml | 15 ++- CMakeLists.txt | 4 + test/CMakeLists.txt | 14 +++ test/DocumentContextReaderTest.cpp | 180 +++++++++++++++++++++++++++++ test/unittest_main.cpp | 28 +++++ 5 files changed, 239 insertions(+), 2 deletions(-) create mode 100644 test/CMakeLists.txt create mode 100644 test/DocumentContextReaderTest.cpp create mode 100644 test/unittest_main.cpp 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(); +}