diff --git a/builddefs/build_full_test.mk b/builddefs/build_full_test.mk index 63f9fea915d..5cae327c6c7 100644 --- a/builddefs/build_full_test.mk +++ b/builddefs/build_full_test.mk @@ -21,8 +21,10 @@ $(TEST_OUTPUT)_SRC := \ $(SRC) \ $(QUANTUM_PATH)/keymap_introspection.c \ tests/test_common/matrix.c \ + tests/test_common/pointing_device_driver.c \ tests/test_common/test_driver.cpp \ tests/test_common/keyboard_report_util.cpp \ + tests/test_common/mouse_report_util.cpp \ tests/test_common/keycode_util.cpp \ tests/test_common/keycode_table.cpp \ tests/test_common/test_fixture.cpp \ diff --git a/tests/mousekeys/config.h b/tests/mousekeys/config.h new file mode 100644 index 00000000000..76bbbe8daea --- /dev/null +++ b/tests/mousekeys/config.h @@ -0,0 +1,6 @@ +// Copyright 2024 Dasky (@daskygit) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "test_common.h" diff --git a/tests/mousekeys/test.mk b/tests/mousekeys/test.mk new file mode 100644 index 00000000000..6c605daecf5 --- /dev/null +++ b/tests/mousekeys/test.mk @@ -0,0 +1 @@ +MOUSEKEY_ENABLE = yes diff --git a/tests/mousekeys/test_mousekeys.cpp b/tests/mousekeys/test_mousekeys.cpp new file mode 100644 index 00000000000..a1b8e06cf7a --- /dev/null +++ b/tests/mousekeys/test_mousekeys.cpp @@ -0,0 +1,109 @@ +// Copyright 2024 Dasky (@daskygit) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "gtest/gtest.h" +#include "mouse_report_util.hpp" +#include "test_common.hpp" + +using testing::_; + +struct MouseKeyExpectations { + int16_t x; + int16_t y; + int16_t h; + int16_t v; + uint16_t button_mask; +}; + +class Mousekey : public TestFixture {}; +class MousekeyParametrized : public ::testing::WithParamInterface>, public Mousekey {}; + +TEST_F(Mousekey, SendMouseNotCalledWhenNoKeyIsPressed) { + TestDriver driver; + EXPECT_NO_MOUSE_REPORT(driver); + run_one_scan_loop(); +} + +TEST_F(Mousekey, PressAndHoldCursorUpIsCorrectlyReported) { + TestDriver driver; + KeymapKey mouse_key = KeymapKey{0, 0, 0, QK_MOUSE_CURSOR_UP}; + + set_keymap({mouse_key}); + + EXPECT_MOUSE_REPORT(driver, (0, -8, 0, 0, 0)); + mouse_key.press(); + run_one_scan_loop(); + + EXPECT_MOUSE_REPORT(driver, (0, -2, 0, 0, 0)); + idle_for(MOUSEKEY_INTERVAL); + + EXPECT_EMPTY_MOUSE_REPORT(driver); + mouse_key.release(); + run_one_scan_loop(); + + VERIFY_AND_CLEAR(driver); +} + +TEST_F(Mousekey, PressAndHoldButtonOneCorrectlyReported) { + TestDriver driver; + KeymapKey mouse_key = KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_1}; + + set_keymap({mouse_key}); + + EXPECT_MOUSE_REPORT(driver, (0, 0, 0, 0, 1)); + mouse_key.press(); + run_one_scan_loop(); + + idle_for(MOUSEKEY_INTERVAL); + + EXPECT_EMPTY_MOUSE_REPORT(driver); + mouse_key.release(); + run_one_scan_loop(); + + VERIFY_AND_CLEAR(driver); +} + +TEST_P(MousekeyParametrized, PressAndReleaseIsCorrectlyReported) { + TestDriver driver; + KeymapKey mouse_key = GetParam().first; + MouseKeyExpectations expectations = GetParam().second; + + set_keymap({mouse_key}); + + EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, expectations.button_mask)); + mouse_key.press(); + run_one_scan_loop(); + + EXPECT_EMPTY_MOUSE_REPORT(driver); + mouse_key.release(); + run_one_scan_loop(); + + EXPECT_NO_MOUSE_REPORT(driver); + run_one_scan_loop(); + + VERIFY_AND_CLEAR(driver); +} +// clang-format off +INSTANTIATE_TEST_CASE_P( + Keys, + MousekeyParametrized, + ::testing::Values( + // Key , X, Y, H, V, Buttons Mask + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_1}, MouseKeyExpectations{ 0, 0, 0, 0, 1}), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_2}, MouseKeyExpectations{ 0, 0, 0, 0, 2}), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_3}, MouseKeyExpectations{ 0, 0, 0, 0, 4}), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_4}, MouseKeyExpectations{ 0, 0, 0, 0, 8}), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_5}, MouseKeyExpectations{ 0, 0, 0, 0, 16}), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_6}, MouseKeyExpectations{ 0, 0, 0, 0, 32}), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_7}, MouseKeyExpectations{ 0, 0, 0, 0, 64}), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_8}, MouseKeyExpectations{ 0, 0, 0, 0, 128}), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_CURSOR_UP}, MouseKeyExpectations{ 0,-8, 0, 0, 0}), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_CURSOR_DOWN}, MouseKeyExpectations{ 0, 8, 0, 0, 0}), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_CURSOR_LEFT}, MouseKeyExpectations{-8, 0, 0, 0, 0}), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_CURSOR_RIGHT}, MouseKeyExpectations{ 8, 0, 0, 0, 0}), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_WHEEL_UP}, MouseKeyExpectations{ 0, 0, 0, 1, 0}), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_WHEEL_DOWN}, MouseKeyExpectations{ 0, 0, 0,-1, 0}), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_WHEEL_LEFT}, MouseKeyExpectations{ 0, 0,-1, 0, 0}), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_WHEEL_RIGHT}, MouseKeyExpectations{ 0, 0, 1, 0, 0}) + )); +// clang-format on diff --git a/tests/pointing/config.h b/tests/pointing/config.h new file mode 100644 index 00000000000..76bbbe8daea --- /dev/null +++ b/tests/pointing/config.h @@ -0,0 +1,6 @@ +// Copyright 2024 Dasky (@daskygit) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "test_common.h" diff --git a/tests/pointing/invertandrotate/config.h b/tests/pointing/invertandrotate/config.h new file mode 100644 index 00000000000..a3f3df7a54e --- /dev/null +++ b/tests/pointing/invertandrotate/config.h @@ -0,0 +1,9 @@ +// Copyright 2024 Dasky (@daskygit) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "test_common.h" + +#define POINTING_DEVICE_INVERT_X +#define POINTING_DEVICE_ROTATION_90 diff --git a/tests/pointing/invertandrotate/test.mk b/tests/pointing/invertandrotate/test.mk new file mode 100644 index 00000000000..ba224d74f0a --- /dev/null +++ b/tests/pointing/invertandrotate/test.mk @@ -0,0 +1,2 @@ +POINTING_DEVICE_ENABLE = yes +POINTING_DEVICE_DRIVER = custom diff --git a/tests/pointing/invertandrotate/test_invertandrotate.cpp b/tests/pointing/invertandrotate/test_invertandrotate.cpp new file mode 100644 index 00000000000..b47739f415b --- /dev/null +++ b/tests/pointing/invertandrotate/test_invertandrotate.cpp @@ -0,0 +1,59 @@ +// Copyright 2024 Dasky (@daskygit) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "gtest/gtest.h" +#include "mouse_report_util.hpp" +#include "test_common.hpp" +#include "test_pointing_device_driver.h" + +using testing::_; + +struct SimpleReport { + int16_t x; + int16_t y; + int16_t h; + int16_t v; +}; + +class Pointing : public TestFixture {}; +class PointingInvertAndRotateParametrized : public ::testing::WithParamInterface>, public Pointing {}; + +TEST_P(PointingInvertAndRotateParametrized, PointingInvertAndRotateOrder) { + TestDriver driver; + SimpleReport input = GetParam().first; + SimpleReport expectations = GetParam().second; + + pd_set_x(input.x); + pd_set_y(input.y); + pd_set_h(input.h); + pd_set_v(input.v); + + EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, 0)); + run_one_scan_loop(); + + // EXPECT_EMPTY_MOUSE_REPORT(driver); + pd_clear_movement(); + run_one_scan_loop(); + + EXPECT_NO_MOUSE_REPORT(driver); + run_one_scan_loop(); + + VERIFY_AND_CLEAR(driver); +} +// clang-format off +INSTANTIATE_TEST_CASE_P( + InvertAndRotate, + PointingInvertAndRotateParametrized, + ::testing::Values( + // Input Expected // Actual Result - Rotate 90 then Invert X + std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 0, 1, 0, 0}), // LEFT - DOWN + std::make_pair(SimpleReport{ 0, -1, 0, 0}, SimpleReport{ 1, 0, 0, 0}), // UP - RIGHT + std::make_pair(SimpleReport{ 1, 0, 0, 0}, SimpleReport{ 0, -1, 0, 0}), // RIGHT - UP + std::make_pair(SimpleReport{ 0, 1, 0, 0}, SimpleReport{ -1, 0, 0, 0}) // DOWN - LEFT + // Input Expected // Invert X then Rotate 90 + // std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 0, -1, 0, 0}), // LEFT - UP + // std::make_pair(SimpleReport{ 0, -1, 0, 0}, SimpleReport{ -1, 0, 0, 0}), // UP - LEFT + // std::make_pair(SimpleReport{ 1, 0, 0, 0}, SimpleReport{ 0, 1, 0, 0}), // RIGHT - DOWN + // std::make_pair(SimpleReport{ 0, 1, 0, 0}, SimpleReport{ 1, 0, 0, 0}) // DOWN - RIGHT + )); +// clang-format on diff --git a/tests/pointing/invertxy/config.h b/tests/pointing/invertxy/config.h new file mode 100644 index 00000000000..6b29185d371 --- /dev/null +++ b/tests/pointing/invertxy/config.h @@ -0,0 +1,9 @@ +// Copyright 2024 Dasky (@daskygit) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "test_common.h" + +#define POINTING_DEVICE_INVERT_X +#define POINTING_DEVICE_INVERT_Y diff --git a/tests/pointing/invertxy/test.mk b/tests/pointing/invertxy/test.mk new file mode 100644 index 00000000000..ba224d74f0a --- /dev/null +++ b/tests/pointing/invertxy/test.mk @@ -0,0 +1,2 @@ +POINTING_DEVICE_ENABLE = yes +POINTING_DEVICE_DRIVER = custom diff --git a/tests/pointing/invertxy/test_invertxy.cpp b/tests/pointing/invertxy/test_invertxy.cpp new file mode 100644 index 00000000000..014f6f3cd03 --- /dev/null +++ b/tests/pointing/invertxy/test_invertxy.cpp @@ -0,0 +1,53 @@ +// Copyright 2024 Dasky (@daskygit) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "gtest/gtest.h" +#include "mouse_report_util.hpp" +#include "test_common.hpp" +#include "test_pointing_device_driver.h" + +using testing::_; + +struct SimpleReport { + int16_t x; + int16_t y; + int16_t h; + int16_t v; +}; + +class Pointing : public TestFixture {}; +class PointingInvertXYParametrized : public ::testing::WithParamInterface>, public Pointing {}; + +TEST_P(PointingInvertXYParametrized, PointingInvertXY) { + TestDriver driver; + SimpleReport input = GetParam().first; + SimpleReport expectations = GetParam().second; + + pd_set_x(input.x); + pd_set_y(input.y); + pd_set_h(input.h); + pd_set_v(input.v); + + EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, 0)); + run_one_scan_loop(); + + // EXPECT_EMPTY_MOUSE_REPORT(driver); + pd_clear_movement(); + run_one_scan_loop(); + + EXPECT_NO_MOUSE_REPORT(driver); + run_one_scan_loop(); + + VERIFY_AND_CLEAR(driver); +} +// clang-format off +INSTANTIATE_TEST_CASE_P( + X_Y_XY, + PointingInvertXYParametrized, + ::testing::Values( + // Input Expected + std::make_pair(SimpleReport{ 33, 0, 0, 0}, SimpleReport{ -33, 0, 0, 0}), + std::make_pair(SimpleReport{ 0, -127, 0, 0}, SimpleReport{ 0, 127, 0, 0}), + std::make_pair(SimpleReport{ 10, -20, 0, 0}, SimpleReport{ -10, 20, 0, 0}) + )); +// clang-format on diff --git a/tests/pointing/rotate180/config.h b/tests/pointing/rotate180/config.h new file mode 100644 index 00000000000..a80f5482fd4 --- /dev/null +++ b/tests/pointing/rotate180/config.h @@ -0,0 +1,8 @@ +// Copyright 2024 Dasky (@daskygit) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "test_common.h" + +#define POINTING_DEVICE_ROTATION_180 diff --git a/tests/pointing/rotate180/test.mk b/tests/pointing/rotate180/test.mk new file mode 100644 index 00000000000..ba224d74f0a --- /dev/null +++ b/tests/pointing/rotate180/test.mk @@ -0,0 +1,2 @@ +POINTING_DEVICE_ENABLE = yes +POINTING_DEVICE_DRIVER = custom diff --git a/tests/pointing/rotate180/test_rotate180.cpp b/tests/pointing/rotate180/test_rotate180.cpp new file mode 100644 index 00000000000..736cf9ea25c --- /dev/null +++ b/tests/pointing/rotate180/test_rotate180.cpp @@ -0,0 +1,55 @@ +// Copyright 2024 Dasky (@daskygit) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "gtest/gtest.h" +#include "mouse_report_util.hpp" +#include "test_common.hpp" +#include "test_pointing_device_driver.h" + +using testing::_; + +struct SimpleReport { + int16_t x; + int16_t y; + int16_t h; + int16_t v; +}; + +class Pointing : public TestFixture {}; +class PointingRotateParametrized : public ::testing::WithParamInterface>, public Pointing {}; + +TEST_P(PointingRotateParametrized, PointingRotateXY) { + TestDriver driver; + SimpleReport input = GetParam().first; + SimpleReport expectations = GetParam().second; + + pd_set_x(input.x); + pd_set_y(input.y); + pd_set_h(input.h); + pd_set_v(input.v); + + EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, 0)); + run_one_scan_loop(); + + // EXPECT_EMPTY_MOUSE_REPORT(driver); + pd_clear_movement(); + run_one_scan_loop(); + + EXPECT_NO_MOUSE_REPORT(driver); + run_one_scan_loop(); + + VERIFY_AND_CLEAR(driver); +} +// clang-format off +INSTANTIATE_TEST_CASE_P( + Rotate180, + PointingRotateParametrized, + ::testing::Values( + // Input Expected + // Rotate Clockwise + std::make_pair(SimpleReport{ 0,-1, 0, 0}, SimpleReport{ 0, 1, 0, 0}), // UP - DOWN + std::make_pair(SimpleReport{ 0, 1, 0, 0}, SimpleReport{ 0,-1, 0, 0}), // DOWN - UP + std::make_pair(SimpleReport{ 1, 0, 0, 0}, SimpleReport{-1, 0, 0, 0}), // RIGHT - LEFT + std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 1, 0, 0, 0}) // LEFT - RIGHT + )); +// clang-format on diff --git a/tests/pointing/rotate270/config.h b/tests/pointing/rotate270/config.h new file mode 100644 index 00000000000..3977dd95e6c --- /dev/null +++ b/tests/pointing/rotate270/config.h @@ -0,0 +1,8 @@ +// Copyright 2024 Dasky (@daskygit) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "test_common.h" + +#define POINTING_DEVICE_ROTATION_270 diff --git a/tests/pointing/rotate270/test.mk b/tests/pointing/rotate270/test.mk new file mode 100644 index 00000000000..ba224d74f0a --- /dev/null +++ b/tests/pointing/rotate270/test.mk @@ -0,0 +1,2 @@ +POINTING_DEVICE_ENABLE = yes +POINTING_DEVICE_DRIVER = custom diff --git a/tests/pointing/rotate270/test_rotate270.cpp b/tests/pointing/rotate270/test_rotate270.cpp new file mode 100644 index 00000000000..6735f062489 --- /dev/null +++ b/tests/pointing/rotate270/test_rotate270.cpp @@ -0,0 +1,60 @@ +// Copyright 2024 Dasky (@daskygit) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "gtest/gtest.h" +#include "mouse_report_util.hpp" +#include "test_common.hpp" +#include "test_pointing_device_driver.h" + +using testing::_; + +struct SimpleReport { + int16_t x; + int16_t y; + int16_t h; + int16_t v; +}; + +class Pointing : public TestFixture {}; +class PointingRotateParametrized : public ::testing::WithParamInterface>, public Pointing {}; + +TEST_P(PointingRotateParametrized, PointingRotateXY) { + TestDriver driver; + SimpleReport input = GetParam().first; + SimpleReport expectations = GetParam().second; + + pd_set_x(input.x); + pd_set_y(input.y); + pd_set_h(input.h); + pd_set_v(input.v); + + EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, 0)); + run_one_scan_loop(); + + // EXPECT_EMPTY_MOUSE_REPORT(driver); + pd_clear_movement(); + run_one_scan_loop(); + + EXPECT_NO_MOUSE_REPORT(driver); + run_one_scan_loop(); + + VERIFY_AND_CLEAR(driver); +} +// clang-format off +INSTANTIATE_TEST_CASE_P( + Rotate270, + PointingRotateParametrized, + ::testing::Values( + // Input Expected + // Actual Result - Rotate Anticlockwise + std::make_pair(SimpleReport{ 0,-1, 0, 0}, SimpleReport{ 1, 0, 0, 0}), // UP - RIGHT + std::make_pair(SimpleReport{ 0, 1, 0, 0}, SimpleReport{-1, 0, 0, 0}), // DOWN - LEFT + std::make_pair(SimpleReport{ 1, 0, 0, 0}, SimpleReport{ 0, 1, 0, 0}), // RIGHT - DOWN + std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 0,-1, 0, 0}) // LEFT - UP + // Rotate Clockwise + // std::make_pair(SimpleReport{ 0,-1, 0, 0}, SimpleReport{-1, 0, 0, 0}), // UP - LEFT + // std::make_pair(SimpleReport{ 0, 1, 0, 0}, SimpleReport{ 1, 0, 0, 0}), // DOWN - RIGHT + // std::make_pair(SimpleReport{ 1, 0, 0, 0}, SimpleReport{ 0,-1, 0, 0}), // RIGHT - UP + // std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 0, 1, 0, 0}) // LEFT - DOWN + )); +// clang-format on diff --git a/tests/pointing/rotate90/config.h b/tests/pointing/rotate90/config.h new file mode 100644 index 00000000000..3a18ec92e94 --- /dev/null +++ b/tests/pointing/rotate90/config.h @@ -0,0 +1,8 @@ +// Copyright 2024 Dasky (@daskygit) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "test_common.h" + +#define POINTING_DEVICE_ROTATION_90 diff --git a/tests/pointing/rotate90/test.mk b/tests/pointing/rotate90/test.mk new file mode 100644 index 00000000000..ba224d74f0a --- /dev/null +++ b/tests/pointing/rotate90/test.mk @@ -0,0 +1,2 @@ +POINTING_DEVICE_ENABLE = yes +POINTING_DEVICE_DRIVER = custom diff --git a/tests/pointing/rotate90/test_rotate90.cpp b/tests/pointing/rotate90/test_rotate90.cpp new file mode 100644 index 00000000000..5c44faad0ab --- /dev/null +++ b/tests/pointing/rotate90/test_rotate90.cpp @@ -0,0 +1,60 @@ +// Copyright 2024 Dasky (@daskygit) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "gtest/gtest.h" +#include "mouse_report_util.hpp" +#include "test_common.hpp" +#include "test_pointing_device_driver.h" + +using testing::_; + +struct SimpleReport { + int16_t x; + int16_t y; + int16_t h; + int16_t v; +}; + +class Pointing : public TestFixture {}; +class PointingRotateParametrized : public ::testing::WithParamInterface>, public Pointing {}; + +TEST_P(PointingRotateParametrized, PointingRotateXY) { + TestDriver driver; + SimpleReport input = GetParam().first; + SimpleReport expectations = GetParam().second; + + pd_set_x(input.x); + pd_set_y(input.y); + pd_set_h(input.h); + pd_set_v(input.v); + + EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, 0)); + run_one_scan_loop(); + + // EXPECT_EMPTY_MOUSE_REPORT(driver); + pd_clear_movement(); + run_one_scan_loop(); + + EXPECT_NO_MOUSE_REPORT(driver); + run_one_scan_loop(); + + VERIFY_AND_CLEAR(driver); +} +// clang-format off +INSTANTIATE_TEST_CASE_P( + Rotate90, + PointingRotateParametrized, + ::testing::Values( + // Input Expected + // Actual Result - Rotate Anticlockwise + std::make_pair(SimpleReport{ 0,-1, 0, 0}, SimpleReport{-1, 0, 0, 0}), // UP - LEFT + std::make_pair(SimpleReport{ 0, 1, 0, 0}, SimpleReport{ 1, 0, 0, 0}), // DOWN - RIGHT + std::make_pair(SimpleReport{ 1, 0, 0, 0}, SimpleReport{ 0,-1, 0, 0}), // RIGHT - UP + std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 0, 1, 0, 0}) // LEFT - DOWN + // Rotate Clockwise + // std::make_pair(SimpleReport{ 0,-1, 0, 0}, SimpleReport{ 1, 0, 0, 0}), // UP - RIGHT + // std::make_pair(SimpleReport{ 0, 1, 0, 0}, SimpleReport{-1, 0, 0, 0}), // DOWN - LEFT + // std::make_pair(SimpleReport{ 1, 0, 0, 0}, SimpleReport{ 0,-1, 0, 0}), // RIGHT - DOWN + // std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 0, 1, 0, 0}) // LEFT - UP + )); +// clang-format on diff --git a/tests/pointing/test.mk b/tests/pointing/test.mk new file mode 100644 index 00000000000..95fde21cb92 --- /dev/null +++ b/tests/pointing/test.mk @@ -0,0 +1,4 @@ +POINTING_DEVICE_ENABLE = yes +MOUSEKEY_ENABLE = no +POINTING_DEVICE_DRIVER = custom + diff --git a/tests/pointing/test_pointing.cpp b/tests/pointing/test_pointing.cpp new file mode 100644 index 00000000000..d59d0149251 --- /dev/null +++ b/tests/pointing/test_pointing.cpp @@ -0,0 +1,166 @@ +// Copyright 2024 Dasky (@daskygit) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "gtest/gtest.h" +#include "mouse_report_util.hpp" +#include "test_common.hpp" +#include "test_pointing_device_driver.h" +#include "mousekey.h" + +using testing::_; + +class Pointing : public TestFixture {}; +class PointingButtonsViaMousekeysParametrized : public ::testing::WithParamInterface>, public Pointing {}; + +TEST_F(Pointing, SendMouseIsNotCalledWithNoInput) { + TestDriver driver; + EXPECT_NO_MOUSE_REPORT(driver); + run_one_scan_loop(); +} + +TEST_F(Pointing, Xnegative) { + TestDriver driver; + + pd_set_x(-10); + EXPECT_MOUSE_REPORT(driver, (-10, 0, 0, 0, 0)); + run_one_scan_loop(); + + pd_clear_movement(); + // EXPECT_EMPTY_MOUSE_REPORT(driver); + run_one_scan_loop(); + + VERIFY_AND_CLEAR(driver); +} + +TEST_F(Pointing, Xpositive) { + TestDriver driver; + + pd_set_x(10); + EXPECT_MOUSE_REPORT(driver, (10, 0, 0, 0, 0)); + run_one_scan_loop(); + + pd_clear_movement(); + // EXPECT_EMPTY_MOUSE_REPORT(driver); + run_one_scan_loop(); + + VERIFY_AND_CLEAR(driver); +} + +TEST_F(Pointing, Ynegative) { + TestDriver driver; + + pd_set_y(-20); + EXPECT_MOUSE_REPORT(driver, (0, -20, 0, 0, 0)); + run_one_scan_loop(); + + pd_clear_movement(); + // EXPECT_EMPTY_MOUSE_REPORT(driver); + run_one_scan_loop(); + + VERIFY_AND_CLEAR(driver); +} + +TEST_F(Pointing, Ypositive) { + TestDriver driver; + + pd_set_y(20); + EXPECT_MOUSE_REPORT(driver, (0, 20, 0, 0, 0)); + run_one_scan_loop(); + + pd_clear_movement(); + // EXPECT_EMPTY_MOUSE_REPORT(driver); + run_one_scan_loop(); + + VERIFY_AND_CLEAR(driver); +} + +TEST_F(Pointing, XandY) { + TestDriver driver; + + pd_set_x(-50); + pd_set_y(100); + EXPECT_MOUSE_REPORT(driver, (-50, 100, 0, 0, 0)); + run_one_scan_loop(); + + pd_clear_movement(); + // EXPECT_EMPTY_MOUSE_REPORT(driver); + run_one_scan_loop(); + + VERIFY_AND_CLEAR(driver); +} + +TEST_F(Pointing, CorrectButtonIsReportedWhenPressed) { + TestDriver driver; + + EXPECT_MOUSE_REPORT(driver, (0, 0, 0, 0, 1)); + pd_press_button(POINTING_DEVICE_BUTTON1); + run_one_scan_loop(); + + EXPECT_EMPTY_MOUSE_REPORT(driver); + pd_release_button(POINTING_DEVICE_BUTTON1); + run_one_scan_loop(); + + EXPECT_NO_MOUSE_REPORT(driver); + run_one_scan_loop(); + + pd_clear_all_buttons(); + run_one_scan_loop(); + + VERIFY_AND_CLEAR(driver); +} + +TEST_F(Pointing, CorrectButtonIsReportedWhenKeyPressed) { + TestDriver driver; + auto key = KeymapKey(0, 0, 0, KC_MS_BTN1); + set_keymap({key}); + + EXPECT_MOUSE_REPORT(driver, (0, 0, 0, 0, 1)); + key.press(); + run_one_scan_loop(); + + EXPECT_EMPTY_MOUSE_REPORT(driver); + key.release(); + run_one_scan_loop(); + + EXPECT_NO_MOUSE_REPORT(driver); + run_one_scan_loop(); + + VERIFY_AND_CLEAR(driver); +} + +TEST_P(PointingButtonsViaMousekeysParametrized, MouseKeysViaPointingDriver) { + TestDriver driver; + KeymapKey mouse_key = GetParam().first; + uint8_t button_mask = GetParam().second; + + set_keymap({mouse_key}); + + EXPECT_MOUSE_REPORT(driver, (0, 0, 0, 0, button_mask)); + mouse_key.press(); + run_one_scan_loop(); + + EXPECT_EMPTY_MOUSE_REPORT(driver); + mouse_key.release(); + run_one_scan_loop(); + + EXPECT_NO_MOUSE_REPORT(driver); + run_one_scan_loop(); + + VERIFY_AND_CLEAR(driver); +} +// clang-format off +INSTANTIATE_TEST_CASE_P( + ButtonsOneToEight, + PointingButtonsViaMousekeysParametrized, + ::testing::Values( + // Key , Buttons Mask + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_1}, 1), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_2}, 2), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_3}, 4), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_4}, 8), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_5}, 16), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_6}, 32), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_7}, 64), + std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_8}, 128) + )); +// clang-format on diff --git a/tests/test_common/mouse_report_util.cpp b/tests/test_common/mouse_report_util.cpp new file mode 100644 index 00000000000..60cdeced24f --- /dev/null +++ b/tests/test_common/mouse_report_util.cpp @@ -0,0 +1,58 @@ +/* Copyright 2017 Fred Sundvik + * + * 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 2 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 "mouse_report_util.hpp" +#include +#include +#include + +using namespace testing; + +bool operator==(const report_mouse_t& lhs, const report_mouse_t& rhs) { + return lhs.x == rhs.x && lhs.y == rhs.y && lhs.h == rhs.h && lhs.v == rhs.v && lhs.buttons == rhs.buttons; +} + +std::ostream& operator<<(std::ostream& os, const report_mouse_t& report) { + os << std::setw(10) << std::left << "mouse report: "; + + if (report.x == 0 && report.y == 0 && report.h == 0 && report.v == 0 && report.buttons == 0) { + return os << "empty" << std::endl; + } + + os << "(X:" << (int)report.x << ", Y:" << (int)report.y << ", H:" << (int)report.h << ", V:" << (int)report.v << ", B:" << (int)report.buttons << ")"; + return os << std::endl; +} + +MouseReportMatcher::MouseReportMatcher(int16_t x, int16_t y, int8_t h, int8_t v, uint8_t button_mask) { + memset(&m_report, 0, sizeof(report_mouse_t)); + m_report.x = x; + m_report.y = y; + m_report.h = h; + m_report.v = v; + m_report.buttons = button_mask; +} + +bool MouseReportMatcher::MatchAndExplain(report_mouse_t& report, MatchResultListener* listener) const { + return m_report == report; +} + +void MouseReportMatcher::DescribeTo(::std::ostream* os) const { + *os << "is equal to " << m_report; +} + +void MouseReportMatcher::DescribeNegationTo(::std::ostream* os) const { + *os << "is not equal to " << m_report; +} diff --git a/tests/test_common/mouse_report_util.hpp b/tests/test_common/mouse_report_util.hpp new file mode 100644 index 00000000000..645f6aa58c6 --- /dev/null +++ b/tests/test_common/mouse_report_util.hpp @@ -0,0 +1,38 @@ +/* Copyright 2017 Fred Sundvik + * + * 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 2 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 . + */ + +#pragma once +#include "report.h" +#include +#include "gmock/gmock.h" + +bool operator==(const report_mouse_t& lhs, const report_mouse_t& rhs); +std::ostream& operator<<(std::ostream& stream, const report_mouse_t& value); + +class MouseReportMatcher : public testing::MatcherInterface { + public: + MouseReportMatcher(int16_t x, int16_t y, int8_t h, int8_t v, uint8_t button_mask); + virtual bool MatchAndExplain(report_mouse_t& report, testing::MatchResultListener* listener) const override; + virtual void DescribeTo(::std::ostream* os) const override; + virtual void DescribeNegationTo(::std::ostream* os) const override; + + private: + report_mouse_t m_report; +}; + +inline testing::Matcher MouseReport(int16_t x, int16_t y, int8_t h, int8_t v, uint8_t button_mask) { + return testing::MakeMatcher(new MouseReportMatcher(x, y, h, v, button_mask)); +} diff --git a/tests/test_common/pointing_device_driver.c b/tests/test_common/pointing_device_driver.c new file mode 100644 index 00000000000..b64dbad5063 --- /dev/null +++ b/tests/test_common/pointing_device_driver.c @@ -0,0 +1,109 @@ +// Copyright 2024 Dasky (@daskygit) +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "report.h" +#include "test_pointing_device_driver.h" +#include + +typedef struct { + bool pressed; + bool dirty; +} pd_button_state_t; + +typedef struct { + int16_t x; + int16_t y; + int16_t h; + int16_t v; + pd_button_state_t button_state[8]; + uint16_t cpi; + bool initiated; +} pd_config_t; + +static pd_config_t pd_config = {0}; + +void pointing_device_driver_init(void) { + pd_set_init(true); +} + +report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) { + for (uint8_t i = 0; i < 8; i++) { + if (pd_config.button_state[i].dirty) { + pd_config.button_state[i].dirty = false; + if (pd_config.button_state[i].pressed) { + mouse_report.buttons |= 1 << (i); + } else { + mouse_report.buttons &= ~(1 << (i)); + } + } + } + mouse_report.x = pd_config.x; + mouse_report.y = pd_config.y; + mouse_report.h = pd_config.h; + mouse_report.v = pd_config.v; + return mouse_report; +} + +__attribute__((weak)) uint16_t pointing_device_driver_get_cpi(void) { + return pd_config.cpi; +} + +__attribute__((weak)) void pointing_device_driver_set_cpi(uint16_t cpi) { + pd_config.cpi = cpi; +} + +void pd_press_button(uint8_t btn) { + pd_config.button_state[btn].dirty = true; + pd_config.button_state[btn].pressed = true; +} +void pd_release_button(uint8_t btn) { + pd_config.button_state[btn].dirty = true; + pd_config.button_state[btn].pressed = false; +} + +void pd_clear_all_buttons(void) { + for (uint8_t i = 0; i < 8; i++) { + pd_config.button_state[i].dirty = true; + pd_config.button_state[i].pressed = false; + } +} + +void pd_set_x(int16_t x) { + pd_config.x = x; +} + +void pd_clear_x(void) { + pd_set_x(0); +} + +void pd_set_y(int16_t y) { + pd_config.y = y; +} +void pd_clear_y(void) { + pd_set_y(0); +} + +void pd_set_h(int16_t h) { + pd_config.h = h; +} +void pd_clear_h(void) { + pd_set_h(0); +} + +void pd_set_v(int16_t v) { + pd_config.v = v; +} +void pd_clear_v(void) { + pd_set_v(0); +} + +void pd_clear_movement(void) { + pd_set_x(0); + pd_set_y(0); + pd_set_h(0); + pd_set_v(0); +} + +void pd_set_init(bool success) { + pd_config.initiated = success; +} diff --git a/tests/test_common/test_driver.cpp b/tests/test_common/test_driver.cpp index d410b225f9d..70b920ac86a 100644 --- a/tests/test_common/test_driver.cpp +++ b/tests/test_common/test_driver.cpp @@ -54,6 +54,7 @@ void TestDriver::send_nkro(report_nkro_t* report) { } void TestDriver::send_mouse(report_mouse_t* report) { + test_logger.trace() << std::setw(10) << std::left << "send_mouse: (X:" << (int)report->x << ", Y:" << (int)report->y << ", H:" << (int)report->h << ", V:" << (int)report->v << ", B:" << (int)report->buttons << ")" << std::endl; m_this->send_mouse_mock(*report); } diff --git a/tests/test_common/test_driver.hpp b/tests/test_common/test_driver.hpp index ec75d3fff22..fea8225953a 100644 --- a/tests/test_common/test_driver.hpp +++ b/tests/test_common/test_driver.hpp @@ -66,6 +66,25 @@ class TestDriver { */ #define EXPECT_REPORT(driver, report) EXPECT_CALL((driver), send_keyboard_mock(KeyboardReport report)) +/** + * @brief Sets gmock expectation that a mouse report of `report` will be sent. + * For this macro to parse correctly, the `report` arg must be surrounded by + * parentheses ( ). For instance, + * + * // Expect that a report of "X:-10 Y:0 H:0 V:10 BTN:1 " is sent to the host. + * EXPECT_REPORT(driver, (-10, 0, 0, 0, 1)); + * + * is shorthand for + * + * EXPECT_CALL(driver, send_mouse_mock(MouseReport(-10, 0, 0, 0, 1))); + * + * It is possible to use .Times() and other gmock APIS with EXPECT_REPORT, for instance, + * allow only single report to be sent: + * + * EXPECT_REPORT(driver, (-10, 0, 0, 0, 1)).Times(1); + */ +#define EXPECT_MOUSE_REPORT(driver, report) EXPECT_CALL((driver), send_mouse_mock(MouseReport report)) + /** * @brief Sets gmock expectation that Unicode `code_point` is sent with UNICODE_MODE_LINUX input * mode. For instance for U+2013, @@ -87,6 +106,15 @@ class TestDriver { */ #define EXPECT_EMPTY_REPORT(driver) EXPECT_REPORT(driver, ()) +/** + * @brief Sets gmock expectation that a empty keyboard report will be sent. + * It is possible to use .Times() and other gmock APIS with EXPECT_EMPTY_MOUSE_REPORT, for instance, + * allow any number of empty reports with: + * + * EXPECT_EMPTY_MOUSE_REPORT(driver).Times(AnyNumber()); + */ +#define EXPECT_EMPTY_MOUSE_REPORT(driver) EXPECT_MOUSE_REPORT(driver, (0, 0, 0, 0, 0)) + /** * @brief Sets gmock expectation that a keyboard report will be sent, without matching its content. * It is possible to use .Times() and other gmock APIS with EXPECT_ANY_REPORT, for instance, @@ -96,11 +124,25 @@ class TestDriver { */ #define EXPECT_ANY_REPORT(driver) EXPECT_CALL((driver), send_keyboard_mock(_)) +/** + * @brief Sets gmock expectation that a mouse report will be sent, without matching its content. + * It is possible to use .Times() and other gmock APIS with EXPECT_ANY_MOUSE_REPORT, for instance, + * allow a single arbitrary report with: + * + * EXPECT_ANY_MOUSE_REPORT(driver).Times(1); + */ +#define EXPECT_ANY_MOUSE_REPORT(driver) EXPECT_CALL((driver), send_mouse_mock(_)) + /** * @brief Sets gmock expectation that no keyboard report will be sent at all. */ #define EXPECT_NO_REPORT(driver) EXPECT_ANY_REPORT(driver).Times(0) +/** + * @brief Sets gmock expectation that no keyboard report will be sent at all. + */ +#define EXPECT_NO_MOUSE_REPORT(driver) EXPECT_ANY_MOUSE_REPORT(driver).Times(0) + /** @brief Tests whether keycode `actual` is equal to `expected`. */ #define EXPECT_KEYCODE_EQ(actual, expected) EXPECT_THAT((actual), KeycodeEq((expected))) diff --git a/tests/test_common/test_fixture.cpp b/tests/test_common/test_fixture.cpp index 3cfb2cb4c29..81806b0e6d2 100644 --- a/tests/test_common/test_fixture.cpp +++ b/tests/test_common/test_fixture.cpp @@ -7,6 +7,7 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include "keyboard_report_util.hpp" +#include "mouse_report_util.hpp" #include "keycode.h" #include "test_driver.hpp" #include "test_logger.hpp" @@ -69,6 +70,9 @@ TestFixture::~TestFixture() { /* Reset keyboard state. */ clear_all_keys(); +#ifdef MOUSEKEY_ENABLE + EXPECT_EMPTY_MOUSE_REPORT(driver); +#endif clear_keyboard(); clear_oneshot_mods(); diff --git a/tests/test_common/test_pointing_device_driver.h b/tests/test_common/test_pointing_device_driver.h new file mode 100644 index 00000000000..ae136b21cdf --- /dev/null +++ b/tests/test_common/test_pointing_device_driver.h @@ -0,0 +1,32 @@ +// Copyright 2024 Dasky (@daskygit) +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void pd_press_button(uint8_t btn); +void pd_release_button(uint8_t btn); +void pd_clear_all_buttons(void); + +void pd_set_x(int16_t x); +void clear_x(void); + +void pd_set_y(int16_t y); +void pd_clear_y(void); + +void pd_set_h(int16_t h); +void pd_clear_h(void); + +void pd_set_v(int16_t v); +void pd_clear_v(void); + +void pd_clear_movement(void); + +void pd_set_init(bool success); + +#ifdef __cplusplus +} +#endif