diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index af9c1680e..f6ed22f52 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2024 Chris Rizzitello +# SPDX-FileCopyrightText: (C) 2024 - 2026 Chris Rizzitello # SPDX-License-Identifier: MIT name: "Install dependencies for deskflow" @@ -30,29 +30,27 @@ runs: if: ${{ runner.os != 'Windows' }} run: | if [ "$RUNNER_OS" == "macOS" ]; then - brew install googletest openssl doxygen --quiet + brew install openssl doxygen --quiet elif [ "$RUNNER_OS" == "Linux" ]; then if [ ${{inputs.like}} == "debian" ]; then apt update -qqq > /dev/null apt install -qqq cmake build-essential ninja-build \ xorg-dev libx11-dev libxtst-dev libssl-dev \ libglib2.0-dev libxkbfile-dev qt6-base-dev qt6-tools-dev \ - libgtk-3-dev libgtest-dev libgmock-dev \ - libei-dev libportal-dev help2man doxygen -y >/dev/null + libgtk-3-dev libei-dev libportal-dev help2man doxygen -y >/dev/null elif [ ${{inputs.like}} == "fedora" ]; then dnf install -y cmake make ninja-build gcc-c++ rpm-build openssl-devel \ glib2-devel libXtst-devel libxkbfile-devel qt6-qtbase-devel qt6-qttools-devel \ - gtk3-devel gtest-devel gmock-devel libei-devel libportal-devel help2man doxygen + gtk3-devel libei-devel libportal-devel help2man doxygen elif [ ${{inputs.like}} == "suse" ]; then zypper refresh zypper install -y --force-resolution \ cmake make ninja gcc-c++ rpm-build libopenssl-devel \ glib2-devel libXtst-devel libxkbfile-devel qt6-base-devel qt6-tools-devel \ - qt6-linguist-devel gtk3-devel doxygen \ - googletest-devel googlemock-devel libei-devel libportal-devel help2man + qt6-linguist-devel gtk3-devel doxygen libei-devel libportal-devel help2man elif [ ${{ inputs.like }} == "arch" ]; then pacman -Syu --noconfirm base-devel cmake ninja \ - gcc openssl glib2 libxtst libxkbfile gtest libei libportal \ + gcc openssl glib2 libxtst libxkbfile libei libportal \ qt6-base qt6-tools qt6-svg qt6-translations qt6-declarative gtk3 help2man doxygen graphviz rsync else echo "Unknown like" @@ -77,7 +75,7 @@ runs: id: vcpkg uses: johnwason/vcpkg-action@v8 with: - pkgs: gtest openssl + pkgs: openssl extra-args: --classic --host-triplet=${{inputs.vcpkg-triplet}} triplet: ${{inputs.vcpkg-triplet}} token: ${{ github.token }} diff --git a/.github/actions/run-tests/action.yml b/.github/actions/run-tests/action.yml index 52b234708..00aea8255 100644 --- a/.github/actions/run-tests/action.yml +++ b/.github/actions/run-tests/action.yml @@ -29,31 +29,14 @@ runs: shell: bash continue-on-error: true - - name: Legacy Tests - id: legacy-tests - env: - QT_QPA_PLATFORM: offscreen - run: | - ./${{ inputs.bin-dir }}/legacytests - result=$? - - if [ $result -ne 0 ]; then - echo "Legacy tests failed with code: $result" >> $GITHUB_STEP_SUMMARY - fi - shell: bash - continue-on-error: true - - name: Get test results id: results run: | pass="✅ Pass" fail="❌ Fail" unit_tests_outcome="${{ steps.unit-tests.outcome }}" - legacy_tests_outcome="${{ steps.legacy-tests.outcome }}" unit_tests=$( [ "$unit_tests_outcome" = "success" ] && echo $pass || echo $fail ) - legacy_tests=$( [ "$legacy_tests_outcome" = "success" ] && echo $pass || echo $fail ) echo "unit-tests=$unit_tests" >> $GITHUB_OUTPUT - echo "legacy-tests=$legacy_tests" >> $GITHUB_OUTPUT shell: bash - name: Summary row @@ -64,7 +47,6 @@ runs: row="" row+="| ${{ inputs.job }} " row+="| ${{ steps.results.outputs.unit-tests }} " - row+="| ${{ steps.results.outputs.legacy-tests }} " echo "$row" > $file echo "file=$file" > $GITHUB_OUTPUT @@ -77,7 +59,7 @@ runs: path: ${{ steps.row.outputs.file }} - name: Check test outcome - if: (steps.unit-tests.outcome != 'success' || steps.legacy-tests.outcome != 'success') + if: (steps.unit-tests.outcome != 'success') run: | echo "Tests failed" exit 1 diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 6371a5286..d95ef450d 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -359,9 +359,9 @@ jobs: release: "14.4" run: | - pkg install -y cmake ninja gmake gcc12 openssl glib \ - libX11 libXtst libxkbfile qt6-base qt6-tools gtk3 \ - googletest pkgconf libei libportal doxygen + pkg install -y cmake ninja gmake gcc12 openssl \ + glib libX11 libXtst libxkbfile qt6-base qt6-tools \ + gtk3 pkgconf libei libportal doxygen ${{env.CMAKE_CONFIGURE}} -G Ninja cmake --build build -j16 flatpak: diff --git a/cmake/vcpkg.json.in b/cmake/vcpkg.json.in index d2615de26..465012d53 100644 --- a/cmake/vcpkg.json.in +++ b/cmake/vcpkg.json.in @@ -5,7 +5,6 @@ "version": "@DESKFLOW_VERSION_MAJOR@.@DESKFLOW_VERSION_MINOR@.@DESKFLOW_VERSION_PATCH@.@DESKFLOW_VERSION_TWEAK@", "builtin-baseline": "d5ec528843d29e3a52d745a64b469f810b2cedbf", "dependencies": [ - "gtest", "openssl" @QT_LIBS@ ] diff --git a/deploy/linux/flatpak/org.deskflow.deskflow.yml b/deploy/linux/flatpak/org.deskflow.deskflow.yml index ffc6c8a8e..dad2339e4 100644 --- a/deploy/linux/flatpak/org.deskflow.deskflow.yml +++ b/deploy/linux/flatpak/org.deskflow.deskflow.yml @@ -67,18 +67,6 @@ modules: url: https://github.com/flatpak/libportal.git tag: 0.10.0 commit: c23024018c8eb076549a1517fcb2d7f80d3e2ed5 - - name: gtest - buildsystem: cmake-ninja - sources: - - type: git - url: https://github.com/google/googletest.git - tag: v1.17.0 - commit: 52eb8108c5bdec04579160ae17225d66034bd723 - x-checker-data: - type: git - tag-pattern: ^([\\d.]+)$ - cleanup: - - '*' - name: deskflow buildsystem: cmake-ninja config-opts: diff --git a/docs/dev/build.md b/docs/dev/build.md index af0eb1a7c..71fc88576 100644 --- a/docs/dev/build.md +++ b/docs/dev/build.md @@ -6,7 +6,6 @@ To build Deskflow you will a minimum of: - [openssl] 3.0+ - [libportal] 0.9.1+ (linux, bsd) - [libei] 1.3+ (linux, bsd) - - [google_test] ^ > ^ Will be fetched if not found on the host system. @@ -25,7 +24,7 @@ Deskflow supports the following CMake options: | BUILD_USER_DOCS | Build user documentation | DOXYGEN_FOUND | `Doxygen` | | BUILD_DEV_DOCS | Build development documentation | OFF | `Doxygen` | | BUILD_INSTALLER | Build installers/packages | ON | | -| BUILD_TESTS | Build unit tests and legacy tests | ON | `gtest`| +| BUILD_TESTS | Build unit tests and legacy tests | ON | | | BUILD_X11_SUPPORT | Build X11 backend (Linux and BSD only) | ON | `x11 libs`| | BUILD_OSX_BUNDLE | Build an app bundle (macOS only) | ON | | | ENABLE_COVERAGE | Enable test coverage | OFF | `gcov` | @@ -105,6 +104,5 @@ After configuring you should be able to run make to build all targets. [doxygen]:http://www.stack.nl/~dimitri/doxygen/ [cmake]:https://cmake.org/ [openssl]:https://www.openssl.org/ -[google_test]:https://github.com/google/googletest [libei]:https://gitlab.freedesktop.org/libinput/libei [libportal]:https://github.com/flatpak/libportal diff --git a/src/unittests/CMakeLists.txt b/src/unittests/CMakeLists.txt index de0118d2c..974aa1812 100644 --- a/src/unittests/CMakeLists.txt +++ b/src/unittests/CMakeLists.txt @@ -61,7 +61,6 @@ add_subdirectory(client) add_subdirectory(common) add_subdirectory(deskflow) add_subdirectory(gui) -add_subdirectory(legacytests) add_subdirectory(net) add_subdirectory(platform) add_subdirectory(server) diff --git a/src/unittests/legacytests/CMakeLists.txt b/src/unittests/legacytests/CMakeLists.txt deleted file mode 100644 index e765c2cfb..000000000 --- a/src/unittests/legacytests/CMakeLists.txt +++ /dev/null @@ -1,145 +0,0 @@ -# SPDX-FileCopyrightText: (C) 2024 Deskflow Developers -# SPDX-FileCopyrightText: (C) 2012 - 2024 Symless Ltd -# SPDX-FileCopyrightText: (C) 2009 - 2012 Nick Bolton -# SPDX-License-Identifier: MIT - -include(FetchContent) -FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG b514bdc898e2951020cbdca1304b75f5950d1f59 # release-1.15.2 - GIT_SHALLOW 1 - FIND_PACKAGE_ARGS NAMES GTest -) -FetchContent_MakeAvailable(googletest) - -macro(config_all_tests) - - set(base_dir ${PROJECT_SOURCE_DIR}) - set(src_dir ${base_dir}/src) - set(test_base_dir ${src_dir}/unittests/legacytests) - set(gui_dir ${src_dir}/gui/src) - - config_test_deps() - - add_subdirectory(legacytests) - -endmacro() - -macro(config_test) - - include_directories( - ${test_base_dir} - ${src_dir} - ${src_dir}/lib - ${gui_dir} - ${ext_dir}) - - set_sources() - -endmacro() - -macro(set_sources) - - file(GLOB_RECURSE headers ${CMAKE_CURRENT_SOURCE_DIR}/*.h) - file(GLOB_RECURSE sources ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) - - file(GLOB_RECURSE shared_headers ${test_base_dir}/shared/*.h) - file(GLOB_RECURSE shared_sources ${test_base_dir}/shared/*.cpp) - - list(APPEND headers ${shared_headers}) - list(APPEND sources ${shared_sources}) - - file(GLOB_RECURSE mock_headers ${test_base_dir}/mock/*.h) - file(GLOB_RECURSE mock_sources ${test_base_dir}/mock/*.cpp) - - list(APPEND headers ${mock_headers}) - list(APPEND sources ${mock_sources}) - - if(ADD_HEADERS_TO_SOURCES) - list(APPEND sources ${headers}) - endif() - - list(APPEND sources ${PROJECT_SOURCE_DIR}/src/apps/res/deskflow.qrc) - - replace_platform_sources() - replace_arch_sources() - -endmacro() - -macro(replace_platform_sources) - - set(platform_dir ${CMAKE_CURRENT_SOURCE_DIR}/platform) - - # Remove platform files so that specific platform files can be added later. - # This is a bit weird, but it's simpler to include everything, remove all - # platform files, then only include the platforms we need. - file(GLOB_RECURSE all_platform_files ${platform_dir}/*) - list(REMOVE_ITEM headers ${all_platform_files}) - list(REMOVE_ITEM sources ${all_platform_files}) - - if(WIN32) - file(GLOB platform_sources ${platform_dir}/MSWindows*.cpp) - file(GLOB platform_headers ${platform_dir}/MSWindows*.h) - elseif(APPLE) - file(GLOB platform_sources ${platform_dir}/OSX*.cpp) - file(GLOB platform_headers ${platform_dir}/OSX*.h) - elseif(UNIX) - file(GLOB platform_sources ${platform_dir}/XWindows*.cpp) - file(GLOB platform_headers ${platform_dir}/XWindows*.h) - endif() - - list(APPEND sources ${platform_sources}) - list(APPEND headers ${platform_headers}) - -endmacro() - -macro(replace_arch_sources) - - set(arch_dir ${CMAKE_CURRENT_SOURCE_DIR}/arch) - - # Remove arch files so that specific arch files can be added later. - # This is a bit weird, but it's simpler to include everything, remove all - # arch files, then only include the archs we need. - file(GLOB_RECURSE all_arch_files ${arch_dir}/*) - list(REMOVE_ITEM headers ${all_arch_files}) - list(REMOVE_ITEM sources ${all_arch_files}) - - if(WIN32) - file(GLOB arch_sources ${arch_dir}/win32/*.cpp) - file(GLOB arch_headers ${arch_dir}/win32/*.h) - elseif(UNIX) - file(GLOB arch_sources ${arch_dir}/unix/*.cpp) - file(GLOB arch_headers ${arch_dir}/unix/*.h) - endif() - - list(APPEND sources ${arch_sources}) - list(APPEND headers ${arch_headers}) - -endmacro() - -macro(config_test_deps) - - # gui library autogen headers: - # qt doesn't seem to auto include the autogen headers for libraries. - include_directories(${PROJECT_BINARY_DIR}/src/lib/gui/gui_autogen/include) - - set(test_libs - arch - base - client - server - io - net - platform - server - app - mt - gui - GTest::gtest - GTest::gmock - ${libs}) - -endmacro() - -config_all_tests() diff --git a/src/unittests/legacytests/legacytests/CMakeLists.txt b/src/unittests/legacytests/legacytests/CMakeLists.txt deleted file mode 100644 index 966943c08..000000000 --- a/src/unittests/legacytests/legacytests/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-FileCopyrightText: (C) 2012 - 2024 Symless Ltd -# SPDX-FileCopyrightText: (C) 2009 - 2012 Nick Bolton -# SPDX-License-Identifier: MIT - -config_test() -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) -set(target legacytests) -add_executable(${target} ${sources} ${headers}) -target_link_libraries(${target} ${test_libs} common) - -if(ENABLE_COVERAGE) - set(test_exclude subprojects/* build/* src/unittests/*) - set(test_src ${PROJECT_SOURCE_DIR}/src) - setup_target_for_coverage_gcovr_xml( - NAME coverage-${target} - EXECUTABLE ${target} - BASE_DIRECTORY ${test_src} - EXCLUDE ${test_exclude} - ) -endif() - diff --git a/src/unittests/legacytests/legacytests/arch/unix/ArchNetworkBSDTests.cpp b/src/unittests/legacytests/legacytests/arch/unix/ArchNetworkBSDTests.cpp deleted file mode 100644 index 17c754f7b..000000000 --- a/src/unittests/legacytests/legacytests/arch/unix/ArchNetworkBSDTests.cpp +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * SPDX-FileCopyrightText: (C) 2014 - 2016 Synergy App Ltd - * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception - */ - -#include "arch/IArchNetwork.h" -#include "lib/arch/unix/ArchNetworkBSD.h" - -#include "lib/arch/ArchException.h" - -#include -#include -#include -#include -#include - -using ::testing::_; -using ::testing::NiceMock; -using PollEntries = std::vector; -using PollFD = struct pollfd[]; - -namespace { -struct MockDeps : public ArchNetworkBSD::Deps -{ - std::shared_ptr m_pollFD; - - MockDeps() - { - ON_CALL(*this, makePollFD(_)).WillByDefault([this](nfds_t n) { - m_pollFD = ArchNetworkBSD::Deps::makePollFD(n); - return m_pollFD; - }); - } - - static std::shared_ptr> makeNice() - { - return std::make_shared>(); - } - - MOCK_METHOD(void, sleep, (double), (override)); - MOCK_METHOD(int, poll, (struct pollfd *, nfds_t, int), (override)); - MOCK_METHOD(std::shared_ptr, makePollFD, (nfds_t), (override)); - MOCK_METHOD(ssize_t, read, (int, void *, size_t), (override)); - MOCK_METHOD(void, testCancelThread, (), (override)); -}; -} // namespace - -TEST(ArchNetworkBSDTests, pollSocket_zeroEntries_callsSleep) -{ - auto deps = MockDeps::makeNice(); - ArchNetworkBSD networkBSD(deps); - - EXPECT_CALL(*deps, sleep(1)).Times(1); - auto result = networkBSD.pollSocket(nullptr, 0, 1); - - EXPECT_EQ(result, 0); -} - -TEST(ArchNetworkBSDTests, pollSocket_mockAccessError_throws) -{ - auto deps = MockDeps::makeNice(); - ON_CALL(*deps, poll(_, _, _)).WillByDefault([]() { - errno = EACCES; - return -1; - }); - ArchNetworkBSD networkBSD(deps); - PollEntries entries{{nullptr, 0, 0}}; - - const auto f = [&] { networkBSD.pollSocket(entries.data(), static_cast(entries.size()), 1); }; - - EXPECT_THROW({ f(); }, ArchNetworkAccessException); -} - -TEST(ArchNetworkBSDTests, pollSocket_pfdHasRevents_copiedToEntries) -{ - auto deps = MockDeps::makeNice(); - ON_CALL(*deps, poll(_, _, _)).WillByDefault([](auto pfd, auto, auto) { - pfd[0].revents = POLLIN | POLLOUT | POLLERR | POLLNVAL; - return 0; - }); - ArchNetworkBSD networkBSD(deps); - PollEntries entries{{nullptr, 0, 0}}; - - networkBSD.pollSocket(entries.data(), static_cast(entries.size()), 1); - - const auto expect = IArchNetwork::PollEventMask::In | IArchNetwork::PollEventMask::Out | - IArchNetwork::PollEventMask::Error | IArchNetwork::PollEventMask::Invalid; - EXPECT_EQ(entries[0].m_revents, expect); -} - -TEST(ArchNetworkBSDTests, pollSocket_nullSocket_fdIsNegativeOne) -{ - auto deps = MockDeps::makeNice(); - ArchNetworkBSD networkBSD(deps); - PollEntries entries{{nullptr, 0, 0}}; - - networkBSD.pollSocket(entries.data(), static_cast(entries.size()), 1); - - EXPECT_EQ(deps->m_pollFD[0].fd, -1); -} - -TEST(ArchNetworkBSDTests, pollSocket_socketSet_fdWasSet) -{ - auto deps = MockDeps::makeNice(); - ArchNetworkBSD networkBSD(deps); - ArchSocketImpl socket{1, 0}; - PollEntries entries{{&socket, 0, 0}}; - - networkBSD.pollSocket(entries.data(), static_cast(entries.size()), 1); - - EXPECT_EQ(deps->m_pollFD[0].fd, 1); -} - -TEST(ArchNetworkBSDTests, pollSocket_eventHasPollInBit_bitWasSet) -{ - auto deps = MockDeps::makeNice(); - ArchNetworkBSD networkBSD(deps); - ArchSocketImpl socket{1, 0}; - PollEntries entries{{&socket, IArchNetwork::PollEventMask::In, 0}}; - - networkBSD.pollSocket(entries.data(), static_cast(entries.size()), 1); - - EXPECT_EQ(deps->m_pollFD[0].events, POLLIN); -} - -TEST(ArchNetworkBSDTests, pollSocket_eventHasPollOutBit_bitWasSet) -{ - auto deps = MockDeps::makeNice(); - ArchNetworkBSD networkBSD(deps); - ArchSocketImpl socket{1, 0}; - PollEntries entries{{&socket, IArchNetwork::PollEventMask::Out, 0}}; - - networkBSD.pollSocket(entries.data(), static_cast(entries.size()), 1); - - EXPECT_EQ(deps->m_pollFD[0].events, POLLOUT); -} - -TEST(ArchNetworkBSDTests, pollSocket_nullSocket_unblockPipeAppended) -{ - auto deps = MockDeps::makeNice(); - ArchNetworkBSD networkBSD(deps); - PollEntries entries{{nullptr, 0, 0}}; - - networkBSD.pollSocket(entries.data(), static_cast(entries.size()), 1); - - // interesting: unblock pipe fd comes from `getNetworkDataForThread` which - // seems to differ depending on linux distro. - EXPECT_GT(deps->m_pollFD[1].fd, -1); -} - -TEST(ArchNetworkBSDTests, pollSocket_unblockPipeReventsError_readCalled) -{ - const auto unblockPipeIndex = 1; - auto deps = MockDeps::makeNice(); - ON_CALL(*deps, poll(_, _, _)).WillByDefault([](auto pfd, auto, auto) { - pfd[unblockPipeIndex].revents = POLLIN; - return 1; - }); - ON_CALL(*deps, read(_, _, _)).WillByDefault([]() { - errno = EAGAIN; - return 0; - }); - ArchNetworkBSD networkBSD(deps); - PollEntries entries{{nullptr, 0, 0}}; - - EXPECT_CALL(*deps, read(_, _, _)).Times(1); - networkBSD.pollSocket(entries.data(), static_cast(entries.size()), 1); -} - -TEST(ArchNetworkBSDTests, pollSocket_interruptSystemCall_testCancelThread) -{ - auto deps = MockDeps::makeNice(); - ON_CALL(*deps, poll(_, _, _)).WillByDefault([]() { - errno = EINTR; - return -1; - }); - ArchNetworkBSD networkBSD(deps); - PollEntries entries{{nullptr, 0, 0}}; - - EXPECT_CALL(*deps, testCancelThread()).Times(1); - networkBSD.pollSocket(entries.data(), static_cast(entries.size()), 1); -} - -TEST(ArchNetworkBSDTests, isAnyAddr_goodAddress_returnsTrue) -{ - auto deps = MockDeps::makeNice(); - ArchNetworkBSD networkBSD(deps); - std::unique_ptr addr; - addr.reset(networkBSD.newAnyAddr(IArchNetwork::AddressFamily::INet6)); - - auto result = networkBSD.isAnyAddr(addr.get()); - - EXPECT_TRUE(result); -} - -TEST(ArchNetworkBSDTests, isAnyAddr_badAddress_returnsFalse) -{ - auto deps = MockDeps::makeNice(); - ArchNetworkBSD networkBSD(deps); - std::unique_ptr addr; - addr.reset(networkBSD.newAnyAddr(IArchNetwork::AddressFamily::INet6)); - auto scratch = (char *)&addr->m_addr; - std::string badAddr = "badaddr"; - std::ranges::copy(badAddr, scratch + 2); - - auto result = networkBSD.isAnyAddr(addr.get()); - - EXPECT_FALSE(result); -} diff --git a/src/unittests/legacytests/legacytests/deskflow/KeyStateTests.cpp b/src/unittests/legacytests/legacytests/deskflow/KeyStateTests.cpp deleted file mode 100644 index 0b9d1e569..000000000 --- a/src/unittests/legacytests/legacytests/deskflow/KeyStateTests.cpp +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * SPDX-FileCopyrightText: (C) 2012 - 2016 Synergy App Ltd - * SPDX-FileCopyrightText: (C) 2011 Nick Bolton - * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception - */ - -#include "unittests/legacytests/mock/deskflow/MockEventQueue.h" -#include "unittests/legacytests/mock/deskflow/MockKeyMap.h" -#include "unittests/legacytests/mock/deskflow/MockKeyState.h" - -#include -#include - -using ::testing::_; -using ::testing::Invoke; -using ::testing::NiceMock; -using ::testing::Return; - -void stubPollPressedKeys(IKeyState::KeyButtonSet &pressedKeys); - -void assertMaskIsOne(ForeachKeyCallback, void *userData); - -const deskflow::KeyMap::KeyItem *stubMapKey( - deskflow::KeyMap::Keystrokes &keys, KeyID id, int32_t group, deskflow::KeyMap::ModifierToKeys &activeModifiers, - KeyModifierMask ¤tState, KeyModifierMask desiredMask, bool isAutoRepeat, const std::string &lang -); - -deskflow::KeyMap::Keystroke s_stubKeystroke(1, false, false); -deskflow::KeyMap::KeyItem s_stubKeyItem; - -TEST(KeyStateTests, sendKeyEvent_halfDuplexAndRepeat_addEventNotCalled) -{ - NiceMock keyMap; - NiceMock eventQueue; - KeyStateImpl keyState(eventQueue, keyMap); - - ON_CALL(keyMap, isHalfDuplex(_, _)).WillByDefault(Return(true)); - - EXPECT_CALL(eventQueue, addEvent(_)).Times(0); - - keyState.sendKeyEvent(nullptr, false, true, kKeyCapsLock, 0, 0, 0); -} - -TEST(KeyStateTests, updateKeyMap_mockKeyMap_keyMapGotMock) -{ - NiceMock keyMap; - MockEventQueue eventQueue; - KeyStateImpl keyState(eventQueue, keyMap); - - // key map member gets a new key map via swap() - EXPECT_CALL(keyMap, swap(_)); - - keyState.updateKeyMap(); -} - -TEST(KeyStateTests, updateKeyState_activeModifiers_maskSet) -{ - NiceMock keyMap; - MockEventQueue eventQueue; - KeyStateImpl keyState(eventQueue, keyMap); - ON_CALL(keyState, pollActiveModifiers()).WillByDefault(Return(KeyModifierAlt)); - - keyState.updateKeyState(); - - KeyModifierMask actual = keyState.getActiveModifiers(); - ASSERT_EQ(KeyModifierAlt, actual); -} - -TEST(KeyStateTests, updateKeyState_activeModifiers_keyMapGotModifers) -{ - MockKeyMap keyMap; - MockEventQueue eventQueue; - KeyStateImpl keyState(eventQueue, keyMap); - ON_CALL(keyState, pollActiveModifiers()).WillByDefault(Return(1)); - ON_CALL(keyMap, foreachKey(_, _)).WillByDefault(Invoke(assertMaskIsOne)); - - // key map gets new modifiers via foreachKey() - EXPECT_CALL(keyMap, foreachKey(_, _)); - - keyState.updateKeyState(); -} - -TEST(KeyStateTests, setHalfDuplexMask_capsLock_halfDuplexCapsLockAdded) -{ - MockKeyMap keyMap; - MockEventQueue eventQueue; - KeyStateImpl keyState(eventQueue, keyMap); - - EXPECT_CALL(keyMap, addHalfDuplexModifier(kKeyCapsLock)); - - keyState.setHalfDuplexMask(KeyModifierCapsLock); -} - -TEST(KeyStateTests, setHalfDuplexMask_numLock_halfDuplexNumLockAdded) -{ - MockKeyMap keyMap; - MockEventQueue eventQueue; - KeyStateImpl keyState(eventQueue, keyMap); - - EXPECT_CALL(keyMap, addHalfDuplexModifier(kKeyNumLock)); - - keyState.setHalfDuplexMask(KeyModifierNumLock); -} - -TEST(KeyStateTests, setHalfDuplexMask_scrollLock_halfDuplexScollLockAdded) -{ - MockKeyMap keyMap; - MockEventQueue eventQueue; - KeyStateImpl keyState(eventQueue, keyMap); - - EXPECT_CALL(keyMap, addHalfDuplexModifier(kKeyScrollLock)); - - keyState.setHalfDuplexMask(KeyModifierScrollLock); -} - -TEST(KeyStateTests, fakeKeyDown_serverKeyAlreadyDown_fakeKeyCalledTwice) -{ - NiceMock keyMap; - MockEventQueue eventQueue; - KeyStateImpl keyState(eventQueue, keyMap); - s_stubKeyItem.m_client = 0; - s_stubKeyItem.m_button = 1; - ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey)); - - // 2 calls to fakeKeyDown should still call fakeKey, even though - // repeated keys are handled differently. - EXPECT_CALL(keyState, fakeKey(_)).Times(2); - - // call twice to simulate server key already down (a misreported autorepeat). - keyState.fakeKeyDown(1, 0, 0, "en"); - keyState.fakeKeyDown(1, 0, 0, "en"); -} - -TEST(KeyStateTests, fakeKeyDown_isIgnoredKey_fakeKeyNotCalled) -{ - MockKeyMap keyMap; - MockEventQueue eventQueue; - KeyStateImpl keyState(eventQueue, keyMap); - - EXPECT_CALL(keyState, fakeKey(_)).Times(0); - - keyState.fakeKeyDown(kKeyCapsLock, 0, 0, "en"); -} - -TEST(KeyStateTests, fakeKeyDown_mapReturnsKeystrokes_fakeKeyCalled) -{ - NiceMock keyMap; - MockEventQueue eventQueue; - KeyStateImpl keyState(eventQueue, keyMap); - s_stubKeyItem.m_button = 0; - s_stubKeyItem.m_client = 0; - ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey)); - - EXPECT_CALL(keyState, fakeKey(_)).Times(1); - - keyState.fakeKeyDown(1, 0, 0, "en"); -} - -TEST(KeyStateTests, fakeKeyRepeat_nullKey_returnsFalse) -{ - NiceMock keyMap; - MockEventQueue eventQueue; - KeyStateImpl keyState(eventQueue, keyMap); - - // set the key to down (we need to make mapKey return a valid key to do this). - deskflow::KeyMap::KeyItem keyItem; - keyItem.m_client = 0; - keyItem.m_button = 1; - ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Return(&keyItem)); - keyState.fakeKeyDown(1, 0, 0, "en"); - - // change mapKey to return nullptr so that fakeKeyRepeat exits early. - deskflow::KeyMap::KeyItem *nullKeyItem = nullptr; - ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Return(nullKeyItem)); - - bool actual = keyState.fakeKeyRepeat(1, 0, 0, 0, "en"); - - ASSERT_FALSE(actual); -} - -TEST(KeyStateTests, fakeKeyRepeat_invalidButton_returnsFalse) -{ - NiceMock keyMap; - MockEventQueue eventQueue; - KeyStateImpl keyState(eventQueue, keyMap); - - // set the key to down (we need to make mapKey return a valid key to do this). - deskflow::KeyMap::KeyItem keyItem; - keyItem.m_client = 0; - keyItem.m_button = 1; // set to 1 to make fakeKeyDown work. - ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Return(&keyItem)); - keyState.fakeKeyDown(1, 0, 0, "en"); - - // change button to 0 so that fakeKeyRepeat will return early. - keyItem.m_button = 0; - ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Return(&keyItem)); - - bool actual = keyState.fakeKeyRepeat(1, 0, 0, 0, "en"); - - ASSERT_FALSE(actual); -} - -TEST(KeyStateTests, fakeKeyRepeat_validKey_returnsTrue) -{ - NiceMock keyMap; - MockEventQueue eventQueue; - KeyStateImpl keyState(eventQueue, keyMap); - s_stubKeyItem.m_client = 0; - s_stubKeystroke.m_type = deskflow::KeyMap::Keystroke::KeyType::Button; - s_stubKeystroke.m_data.m_button.m_button = 2; - - // set the button to 1 for fakeKeyDown call - s_stubKeyItem.m_button = 1; - ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey)); - keyState.fakeKeyDown(1, 0, 0, "en"); - - // change the button to 2 - s_stubKeyItem.m_button = 2; - ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey)); - - bool actual = keyState.fakeKeyRepeat(1, 0, 0, 0, "en"); - - ASSERT_TRUE(actual); -} - -TEST(KeyStateTests, fakeKeyUp_buttonAlreadyDown_returnsTrue) -{ - NiceMock keyMap; - MockEventQueue eventQueue; - KeyStateImpl keyState(eventQueue, keyMap); - - // press alt down so we get full coverage. - ON_CALL(keyState, pollActiveModifiers()).WillByDefault(Return(KeyModifierAlt)); - keyState.updateKeyState(); - - // press button 1 down. - s_stubKeyItem.m_button = 1; - ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey)); - keyState.fakeKeyDown(1, 0, 1, "en"); - - // this takes the button id, which is the 3rd arg of fakeKeyDown - bool actual = keyState.fakeKeyUp(1); - - ASSERT_TRUE(actual); -} - -TEST(KeyStateTests, fakeAllKeysUp_keysWereDown_keysAreUp) -{ - NiceMock keyMap; - MockEventQueue eventQueue; - KeyStateImpl keyState(eventQueue, keyMap); - - // press button 1 down. - s_stubKeyItem.m_button = 1; - ON_CALL(keyMap, mapKey(_, _, _, _, _, _, _, _)).WillByDefault(Invoke(stubMapKey)); - keyState.fakeKeyDown(1, 0, 1, "en"); - - // method under test - keyState.fakeAllKeysUp(); - - bool actual = keyState.isKeyDown(1); - ASSERT_FALSE(actual); -} - -void stubPollPressedKeys(IKeyState::KeyButtonSet &pressedKeys) -{ - pressedKeys.insert(1); -} - -void assertMaskIsOne(ForeachKeyCallback, void *userData) -{ - ASSERT_EQ(1, ((KeyState::AddActiveModifierContext *)userData)->m_mask); -} - -const deskflow::KeyMap::KeyItem *stubMapKey( - deskflow::KeyMap::Keystrokes &keys, KeyID, int32_t, deskflow::KeyMap::ModifierToKeys &, KeyModifierMask &, - KeyModifierMask, bool, const std::string & -) -{ - keys.push_back(s_stubKeystroke); - return &s_stubKeyItem; -} diff --git a/src/unittests/legacytests/legacytests/main.cpp b/src/unittests/legacytests/legacytests/main.cpp deleted file mode 100644 index 96d84153a..000000000 --- a/src/unittests/legacytests/legacytests/main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * SPDX-FileCopyrightText: (C) 2012 Synergy App Ltd - * SPDX-FileCopyrightText: (C) 2011 Nick Bolton - * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception - */ - -#include "arch/Arch.h" -#include "base/Log.h" -#include "unittests/legacytests/shared/ExitTimeout.h" - -#if defined(Q_OS_WIN) -#include "arch/win32/ArchMiscWindows.h" -#endif - -#include - -using deskflow::test::ExitTimeout; - -const auto testDir = "tmp/test"; - -int main(int argc, char **argv) -{ - ExitTimeout exitTimeout(1, "Unit tests"); - -#if defined(Q_OS_WIN) - // HACK: shouldn't be needed, but logging fails without this. - ArchMiscWindows::setInstanceWin32(GetModuleHandle(nullptr)); -#endif - - Arch arch; - arch.init(); - - Log log; - log.setFilter(LogLevel::Level::Verbose); - - ::testing::GTEST_FLAG(throw_on_failure) = true; - testing::InitGoogleTest(&argc, argv); - - // return code 1 means the test failed. - // any other non-zero code is probably a memory error. - return RUN_ALL_TESTS(); -} diff --git a/src/unittests/legacytests/mock/deskflow/MockEventQueue.h b/src/unittests/legacytests/mock/deskflow/MockEventQueue.h deleted file mode 100644 index e85d4b2e0..000000000 --- a/src/unittests/legacytests/mock/deskflow/MockEventQueue.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * SPDX-FileCopyrightText: (C) 2012 - 2016 Synergy App Ltd - * SPDX-FileCopyrightText: (C) 2011 Nick Bolton - * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception - */ - -#pragma once - -#include "base/IEventQueue.h" - -#include - -class MockEventQueue : public IEventQueue -{ -public: - MOCK_METHOD(int, loop, (), (override)); - MOCK_METHOD(EventQueueTimer *, newOneShotTimer, (double, void *), (override)); - MOCK_METHOD(EventQueueTimer *, newTimer, (double, void *), (override)); - MOCK_METHOD(bool, getEvent, (Event &, double), (override)); - MOCK_METHOD(void, adoptBuffer, (IEventQueueBuffer *), (override)); - MOCK_METHOD(void, removeHandlers, (void *), (override)); - MOCK_METHOD(EventTypes, registerType, (const char *)); - MOCK_METHOD(void, addHandler, (EventTypes, void *, const EventHandler &), (override)); - MOCK_METHOD(void, addEvent, (Event &&), (override)); - MOCK_METHOD(void, removeHandler, (EventTypes, void *), (override)); - MOCK_METHOD(bool, dispatchEvent, (const Event &), (override)); - MOCK_METHOD(void, deleteTimer, (EventQueueTimer *), (override)); - MOCK_METHOD(void *, getSystemTarget, (), (override)); - MOCK_METHOD(void, waitForReady, (), (const, override)); -}; diff --git a/src/unittests/legacytests/mock/deskflow/MockKeyMap.h b/src/unittests/legacytests/mock/deskflow/MockKeyMap.h deleted file mode 100644 index e5fc95155..000000000 --- a/src/unittests/legacytests/mock/deskflow/MockKeyMap.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * SPDX-FileCopyrightText: (C) 2012 - 2016 Synergy App Ltd - * SPDX-FileCopyrightText: (C) 2011 Nick Bolton - * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception - */ - -#pragma once -#include "deskflow/KeyMap.h" - -#include - -class MockKeyMap : public deskflow::KeyMap -{ -public: - MOCK_METHOD(void, swap, (KeyMap &), (noexcept)); - MOCK_METHOD(void, finish, (), (override)); - MOCK_METHOD(void, foreachKey, (ForeachKeyCallback, void *), (override)); - MOCK_METHOD(void, addHalfDuplexModifier, (KeyID), (override)); - MOCK_METHOD(bool, isHalfDuplex, (KeyID, KeyButton), (const, override)); - MOCK_METHOD( - const KeyMap::KeyItem *, mapKey, - (Keystrokes &, KeyID, int32_t, ModifierToKeys &, KeyModifierMask &, KeyModifierMask, bool, const std::string &), - (const, override) - ); -}; diff --git a/src/unittests/legacytests/mock/deskflow/MockKeyState.h b/src/unittests/legacytests/mock/deskflow/MockKeyState.h deleted file mode 100644 index 868174cfc..000000000 --- a/src/unittests/legacytests/mock/deskflow/MockKeyState.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * SPDX-FileCopyrightText: (C) 2012 - 2016 Synergy App Ltd - * SPDX-FileCopyrightText: (C) 2011 Nick Bolton - * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception - */ - -#pragma once - -#include "deskflow/KeyState.h" - -#include - -class MockKeyMap; -class MockEventQueue; - -// NOTE: do not mock methods that are not pure virtual. this mock exists only -// to provide an implementation of the KeyState abstract class. -class MockKeyState : public KeyState -{ -public: - MockKeyState(const MockEventQueue &eventQueue) : KeyState((IEventQueue *)&eventQueue, {"en"}, true) - { - } - - MockKeyState(const MockEventQueue &eventQueue, const deskflow::KeyMap &keyMap) - : KeyState((IEventQueue *)&eventQueue, (deskflow::KeyMap &)keyMap, {"en"}, true) - { - } - - MOCK_METHOD(int32_t, pollActiveGroup, (), (const, override)); - MOCK_METHOD(KeyModifierMask, pollActiveModifiers, (), (const, override)); - MOCK_METHOD(bool, fakeCtrlAltDel, (), (override)); - MOCK_METHOD(void, getKeyMap, (deskflow::KeyMap &), (override)); - MOCK_METHOD(void, fakeKey, (const Keystroke &), (override)); - MOCK_METHOD(bool, fakeMediaKey, (KeyID), (override)); - MOCK_METHOD(void, pollPressedKeys, (KeyButtonSet &), (const, override)); -}; - -typedef ::testing::NiceMock KeyStateImpl; - -using KeyID = uint32_t; - -typedef void (*ForeachKeyCallback)(KeyID, int32_t group, deskflow::KeyMap::KeyItem &, void *userData); diff --git a/src/unittests/legacytests/shared/ExitTimeout.cpp b/src/unittests/legacytests/shared/ExitTimeout.cpp deleted file mode 100644 index f8959aa82..000000000 --- a/src/unittests/legacytests/shared/ExitTimeout.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * SPDX-FileCopyrightText: (C) 2024 Synergy App Ltd - * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception - */ - -#include "ExitTimeout.h" - -#include -#include -#include -#include -#include - -namespace deskflow::test { - -const auto checkMilliseconds = std::chrono::milliseconds(100); - -using std::chrono::duration_cast; -using std::chrono::steady_clock; - -bool timeoutReached(const steady_clock::time_point &start, const int minutes) -{ - auto now = steady_clock::now(); - auto duration = duration_cast(now - start); - return duration.count() >= minutes; -} - -ExitTimeout::ExitTimeout(const int minutes, const std::string_view &name) - : m_minutes(minutes), - m_name(name), - m_thread(std::make_unique([this]() { run(); })) -{ -} - -ExitTimeout::~ExitTimeout() -{ - m_running = false; - m_thread->join(); -} - -void ExitTimeout::run() const -{ - auto start = steady_clock::now(); - while (m_running) { - std::this_thread::sleep_for(checkMilliseconds); - if (timeoutReached(start, m_minutes)) { - std::cerr << m_name << " timed out after " << m_minutes << " minute(s)" << std::endl; - std::exit(EXIT_FAILURE); - } - } -} - -} // namespace deskflow::test diff --git a/src/unittests/legacytests/shared/ExitTimeout.h b/src/unittests/legacytests/shared/ExitTimeout.h deleted file mode 100644 index 166b8b3c1..000000000 --- a/src/unittests/legacytests/shared/ExitTimeout.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Deskflow -- mouse and keyboard sharing utility - * SPDX-FileCopyrightText: (C) 2024 Synergy App Ltd - * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception - */ - -#pragma once - -#include -#include - -namespace deskflow::test { - -/** - * @brief Exits the program after a specified timeout, unless destroyed. - * - * The `std::thread` class is used instead of `std::jthread` as Apple Clang has - * no `std::jthread` support: https://en.cppreference.com/w/cpp/compiler_support - * - * TODO: Switch to regular Clang instead of Apple Clang: - * https://symless.atlassian.net/browse/S1-1754 - */ -class ExitTimeout -{ -public: - ExitTimeout(const int minutes, const std::string_view &name); - ~ExitTimeout(); - void run() const; - -private: - bool m_running = true; - int m_minutes = 0; - std::string_view m_name; - std::unique_ptr m_thread; -}; - -} // namespace deskflow::test