1f7d10902a7e603a47b9291664fcb9ab0b21f690
- Author
- Dasky <32983009+daskygit@users.noreply.github.com>
- Committer
- GitHub <noreply@github.com>
- Date
Message
Diff
This diff is truncated to protect this page.
1diff --git a/builddefs/build_full_test.mk b/builddefs/build_full_test.mk
2index 63f9fea915db61cbab3e638bc6febf7c441f66d3..5cae327c6c70a176e65594f397ea1feaa73bd199 100644
3--- a/builddefs/build_full_test.mk
4+++ b/builddefs/build_full_test.mk
5@@ -21,8 +21,10 @@ $(TEST_OUTPUT)_SRC := \
6 $(SRC) \
7 $(QUANTUM_PATH)/keymap_introspection.c \
8 tests/test_common/matrix.c \
9+ tests/test_common/pointing_device_driver.c \
10 tests/test_common/test_driver.cpp \
11 tests/test_common/keyboard_report_util.cpp \
12+ tests/test_common/mouse_report_util.cpp \
13 tests/test_common/keycode_util.cpp \
14 tests/test_common/keycode_table.cpp \
15 tests/test_common/test_fixture.cpp \
16diff --git a/tests/mousekeys/config.h b/tests/mousekeys/config.h
17new file mode 100644
18index 0000000000000000000000000000000000000000..76bbbe8daea89514bab9efe1103154f213d3779c
19--- /dev/null
20+++ b/tests/mousekeys/config.h
21@@ -0,0 +1,6 @@
22+// Copyright 2024 Dasky (@daskygit)
23+// SPDX-License-Identifier: GPL-2.0-or-later
24+
25+#pragma once
26+
27+#include "test_common.h"
28diff --git a/tests/mousekeys/test.mk b/tests/mousekeys/test.mk
29new file mode 100644
30index 0000000000000000000000000000000000000000..6c605daecf53f6a078e2fa1614efa013128f07de
31--- /dev/null
32+++ b/tests/mousekeys/test.mk
33@@ -0,0 +1 @@
34+MOUSEKEY_ENABLE = yes
35diff --git a/tests/mousekeys/test_mousekeys.cpp b/tests/mousekeys/test_mousekeys.cpp
36new file mode 100644
37index 0000000000000000000000000000000000000000..a1b8e06cf7a309e5a43aa6b08b230ec57b497688
38--- /dev/null
39+++ b/tests/mousekeys/test_mousekeys.cpp
40@@ -0,0 +1,109 @@
41+// Copyright 2024 Dasky (@daskygit)
42+// SPDX-License-Identifier: GPL-2.0-or-later
43+
44+#include "gtest/gtest.h"
45+#include "mouse_report_util.hpp"
46+#include "test_common.hpp"
47+
48+using testing::_;
49+
50+struct MouseKeyExpectations {
51+ int16_t x;
52+ int16_t y;
53+ int16_t h;
54+ int16_t v;
55+ uint16_t button_mask;
56+};
57+
58+class Mousekey : public TestFixture {};
59+class MousekeyParametrized : public ::testing::WithParamInterface<std::pair<KeymapKey, MouseKeyExpectations>>, public Mousekey {};
60+
61+TEST_F(Mousekey, SendMouseNotCalledWhenNoKeyIsPressed) {
62+ TestDriver driver;
63+ EXPECT_NO_MOUSE_REPORT(driver);
64+ run_one_scan_loop();
65+}
66+
67+TEST_F(Mousekey, PressAndHoldCursorUpIsCorrectlyReported) {
68+ TestDriver driver;
69+ KeymapKey mouse_key = KeymapKey{0, 0, 0, QK_MOUSE_CURSOR_UP};
70+
71+ set_keymap({mouse_key});
72+
73+ EXPECT_MOUSE_REPORT(driver, (0, -8, 0, 0, 0));
74+ mouse_key.press();
75+ run_one_scan_loop();
76+
77+ EXPECT_MOUSE_REPORT(driver, (0, -2, 0, 0, 0));
78+ idle_for(MOUSEKEY_INTERVAL);
79+
80+ EXPECT_EMPTY_MOUSE_REPORT(driver);
81+ mouse_key.release();
82+ run_one_scan_loop();
83+
84+ VERIFY_AND_CLEAR(driver);
85+}
86+
87+TEST_F(Mousekey, PressAndHoldButtonOneCorrectlyReported) {
88+ TestDriver driver;
89+ KeymapKey mouse_key = KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_1};
90+
91+ set_keymap({mouse_key});
92+
93+ EXPECT_MOUSE_REPORT(driver, (0, 0, 0, 0, 1));
94+ mouse_key.press();
95+ run_one_scan_loop();
96+
97+ idle_for(MOUSEKEY_INTERVAL);
98+
99+ EXPECT_EMPTY_MOUSE_REPORT(driver);
100+ mouse_key.release();
101+ run_one_scan_loop();
102+
103+ VERIFY_AND_CLEAR(driver);
104+}
105+
106+TEST_P(MousekeyParametrized, PressAndReleaseIsCorrectlyReported) {
107+ TestDriver driver;
108+ KeymapKey mouse_key = GetParam().first;
109+ MouseKeyExpectations expectations = GetParam().second;
110+
111+ set_keymap({mouse_key});
112+
113+ EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, expectations.button_mask));
114+ mouse_key.press();
115+ run_one_scan_loop();
116+
117+ EXPECT_EMPTY_MOUSE_REPORT(driver);
118+ mouse_key.release();
119+ run_one_scan_loop();
120+
121+ EXPECT_NO_MOUSE_REPORT(driver);
122+ run_one_scan_loop();
123+
124+ VERIFY_AND_CLEAR(driver);
125+}
126+// clang-format off
127+INSTANTIATE_TEST_CASE_P(
128+ Keys,
129+ MousekeyParametrized,
130+ ::testing::Values(
131+ // Key , X, Y, H, V, Buttons Mask
132+ std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_1}, MouseKeyExpectations{ 0, 0, 0, 0, 1}),
133+ std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_2}, MouseKeyExpectations{ 0, 0, 0, 0, 2}),
134+ std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_3}, MouseKeyExpectations{ 0, 0, 0, 0, 4}),
135+ std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_4}, MouseKeyExpectations{ 0, 0, 0, 0, 8}),
136+ std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_5}, MouseKeyExpectations{ 0, 0, 0, 0, 16}),
137+ std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_6}, MouseKeyExpectations{ 0, 0, 0, 0, 32}),
138+ std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_7}, MouseKeyExpectations{ 0, 0, 0, 0, 64}),
139+ std::make_pair(KeymapKey{0, 0, 0, QK_MOUSE_BUTTON_8}, MouseKeyExpectations{ 0, 0, 0, 0, 128}),
140diff --git a/tests/pointing/config.h b/tests/pointing/config.h
141new file mode 100644
142index 0000000000000000000000000000000000000000..76bbbe8daea89514bab9efe1103154f213d3779c
143--- /dev/null
144+++ b/tests/pointing/config.h
145@@ -0,0 +1,6 @@
146+// Copyright 2024 Dasky (@daskygit)
147+// SPDX-License-Identifier: GPL-2.0-or-later
148+
149+#pragma once
150+
151+#include "test_common.h"
152diff --git a/tests/pointing/invertandrotate/config.h b/tests/pointing/invertandrotate/config.h
153new file mode 100644
154index 0000000000000000000000000000000000000000..a3f3df7a54e8a56c1285003c32486d6dcd8da1f7
155--- /dev/null
156+++ b/tests/pointing/invertandrotate/config.h
157@@ -0,0 +1,9 @@
158+// Copyright 2024 Dasky (@daskygit)
159+// SPDX-License-Identifier: GPL-2.0-or-later
160+
161+#pragma once
162+
163+#include "test_common.h"
164+
165+#define POINTING_DEVICE_INVERT_X
166+#define POINTING_DEVICE_ROTATION_90
167diff --git a/tests/pointing/invertandrotate/test.mk b/tests/pointing/invertandrotate/test.mk
168new file mode 100644
169index 0000000000000000000000000000000000000000..ba224d74f0abce7522bef05de75ede9d3552de6b
170--- /dev/null
171+++ b/tests/pointing/invertandrotate/test.mk
172@@ -0,0 +1,2 @@
173+POINTING_DEVICE_ENABLE = yes
174+POINTING_DEVICE_DRIVER = custom
175diff --git a/tests/pointing/invertandrotate/test_invertandrotate.cpp b/tests/pointing/invertandrotate/test_invertandrotate.cpp
176new file mode 100644
177index 0000000000000000000000000000000000000000..b47739f415b7cdc3043ac6416b20981f8842ae95
178--- /dev/null
179+++ b/tests/pointing/invertandrotate/test_invertandrotate.cpp
180@@ -0,0 +1,59 @@
181+// Copyright 2024 Dasky (@daskygit)
182+// SPDX-License-Identifier: GPL-2.0-or-later
183+
184+#include "gtest/gtest.h"
185+#include "mouse_report_util.hpp"
186+#include "test_common.hpp"
187+#include "test_pointing_device_driver.h"
188+
189+using testing::_;
190+
191+struct SimpleReport {
192+ int16_t x;
193+ int16_t y;
194+ int16_t h;
195+ int16_t v;
196+};
197+
198+class Pointing : public TestFixture {};
199+class PointingInvertAndRotateParametrized : public ::testing::WithParamInterface<std::pair<SimpleReport, SimpleReport>>, public Pointing {};
200+
201+TEST_P(PointingInvertAndRotateParametrized, PointingInvertAndRotateOrder) {
202+ TestDriver driver;
203+ SimpleReport input = GetParam().first;
204+ SimpleReport expectations = GetParam().second;
205+
206+ pd_set_x(input.x);
207+ pd_set_y(input.y);
208+ pd_set_h(input.h);
209+ pd_set_v(input.v);
210+
211+ EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, 0));
212+ run_one_scan_loop();
213+
214+ // EXPECT_EMPTY_MOUSE_REPORT(driver);
215+ pd_clear_movement();
216+ run_one_scan_loop();
217+
218+ EXPECT_NO_MOUSE_REPORT(driver);
219+ run_one_scan_loop();
220+
221+ VERIFY_AND_CLEAR(driver);
222+}
223+// clang-format off
224+INSTANTIATE_TEST_CASE_P(
225+ InvertAndRotate,
226+ PointingInvertAndRotateParametrized,
227+ ::testing::Values(
228+ // Input Expected // Actual Result - Rotate 90 then Invert X
229+ std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 0, 1, 0, 0}), // LEFT - DOWN
230+ std::make_pair(SimpleReport{ 0, -1, 0, 0}, SimpleReport{ 1, 0, 0, 0}), // UP - RIGHT
231+ std::make_pair(SimpleReport{ 1, 0, 0, 0}, SimpleReport{ 0, -1, 0, 0}), // RIGHT - UP
232+ std::make_pair(SimpleReport{ 0, 1, 0, 0}, SimpleReport{ -1, 0, 0, 0}) // DOWN - LEFT
233+ // Input Expected // Invert X then Rotate 90
234+ // std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 0, -1, 0, 0}), // LEFT - UP
235+ // std::make_pair(SimpleReport{ 0, -1, 0, 0}, SimpleReport{ -1, 0, 0, 0}), // UP - LEFT
236+ // std::make_pair(SimpleReport{ 1, 0, 0, 0}, SimpleReport{ 0, 1, 0, 0}), // RIGHT - DOWN
237+ // std::make_pair(SimpleReport{ 0, 1, 0, 0}, SimpleReport{ 1, 0, 0, 0}) // DOWN - RIGHT
238+ ));
239+// clang-format on
240diff --git a/tests/pointing/invertxy/config.h b/tests/pointing/invertxy/config.h
241new file mode 100644
242index 0000000000000000000000000000000000000000..6b29185d371b32e236d36edaa607803a3738b8c2
243--- /dev/null
244+++ b/tests/pointing/invertxy/config.h
245@@ -0,0 +1,9 @@
246+// Copyright 2024 Dasky (@daskygit)
247+// SPDX-License-Identifier: GPL-2.0-or-later
248+
249+#pragma once
250+
251+#include "test_common.h"
252+
253+#define POINTING_DEVICE_INVERT_X
254+#define POINTING_DEVICE_INVERT_Y
255diff --git a/tests/pointing/invertxy/test.mk b/tests/pointing/invertxy/test.mk
256new file mode 100644
257index 0000000000000000000000000000000000000000..ba224d74f0abce7522bef05de75ede9d3552de6b
258--- /dev/null
259+++ b/tests/pointing/invertxy/test.mk
260@@ -0,0 +1,2 @@
261+POINTING_DEVICE_ENABLE = yes
262+POINTING_DEVICE_DRIVER = custom
263diff --git a/tests/pointing/invertxy/test_invertxy.cpp b/tests/pointing/invertxy/test_invertxy.cpp
264new file mode 100644
265index 0000000000000000000000000000000000000000..014f6f3cd03253df630eb56b687238fd82fe1fdd
266--- /dev/null
267+++ b/tests/pointing/invertxy/test_invertxy.cpp
268@@ -0,0 +1,53 @@
269+// Copyright 2024 Dasky (@daskygit)
270+// SPDX-License-Identifier: GPL-2.0-or-later
271+
272+#include "gtest/gtest.h"
273+#include "mouse_report_util.hpp"
274+#include "test_common.hpp"
275+#include "test_pointing_device_driver.h"
276+
277+using testing::_;
278+
279+struct SimpleReport {
280+ int16_t x;
281+ int16_t y;
282+ int16_t h;
283+ int16_t v;
284+};
285+
286+class Pointing : public TestFixture {};
287+class PointingInvertXYParametrized : public ::testing::WithParamInterface<std::pair<SimpleReport, SimpleReport>>, public Pointing {};
288+
289+TEST_P(PointingInvertXYParametrized, PointingInvertXY) {
290+ TestDriver driver;
291+ SimpleReport input = GetParam().first;
292+ SimpleReport expectations = GetParam().second;
293+
294+ pd_set_x(input.x);
295+ pd_set_y(input.y);
296+ pd_set_h(input.h);
297+ pd_set_v(input.v);
298+
299+ EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, 0));
300+ run_one_scan_loop();
301+
302+ // EXPECT_EMPTY_MOUSE_REPORT(driver);
303+ pd_clear_movement();
304+ run_one_scan_loop();
305+
306+ EXPECT_NO_MOUSE_REPORT(driver);
307+ run_one_scan_loop();
308+
309+ VERIFY_AND_CLEAR(driver);
310+}
311+// clang-format off
312+INSTANTIATE_TEST_CASE_P(
313+ X_Y_XY,
314+ PointingInvertXYParametrized,
315+ ::testing::Values(
316+ // Input Expected
317+ std::make_pair(SimpleReport{ 33, 0, 0, 0}, SimpleReport{ -33, 0, 0, 0}),
318+ std::make_pair(SimpleReport{ 0, -127, 0, 0}, SimpleReport{ 0, 127, 0, 0}),
319+ std::make_pair(SimpleReport{ 10, -20, 0, 0}, SimpleReport{ -10, 20, 0, 0})
320+ ));
321+// clang-format on
322diff --git a/tests/pointing/rotate180/config.h b/tests/pointing/rotate180/config.h
323new file mode 100644
324index 0000000000000000000000000000000000000000..a80f5482fd4d9a7eb9bf6aeca84f047a2d55389f
325--- /dev/null
326+++ b/tests/pointing/rotate180/config.h
327@@ -0,0 +1,8 @@
328+// Copyright 2024 Dasky (@daskygit)
329+// SPDX-License-Identifier: GPL-2.0-or-later
330+
331+#pragma once
332+
333+#include "test_common.h"
334+
335+#define POINTING_DEVICE_ROTATION_180
336diff --git a/tests/pointing/rotate180/test.mk b/tests/pointing/rotate180/test.mk
337new file mode 100644
338index 0000000000000000000000000000000000000000..ba224d74f0abce7522bef05de75ede9d3552de6b
339--- /dev/null
340+++ b/tests/pointing/rotate180/test.mk
341@@ -0,0 +1,2 @@
342+POINTING_DEVICE_ENABLE = yes
343+POINTING_DEVICE_DRIVER = custom
344diff --git a/tests/pointing/rotate180/test_rotate180.cpp b/tests/pointing/rotate180/test_rotate180.cpp
345new file mode 100644
346index 0000000000000000000000000000000000000000..736cf9ea25ccd85dc0e307be9c577e50e957cb6c
347--- /dev/null
348+++ b/tests/pointing/rotate180/test_rotate180.cpp
349@@ -0,0 +1,55 @@
350+// Copyright 2024 Dasky (@daskygit)
351+// SPDX-License-Identifier: GPL-2.0-or-later
352+
353+#include "gtest/gtest.h"
354+#include "mouse_report_util.hpp"
355+#include "test_common.hpp"
356+#include "test_pointing_device_driver.h"
357+
358+using testing::_;
359+
360+struct SimpleReport {
361+ int16_t x;
362+ int16_t y;
363+ int16_t h;
364+ int16_t v;
365+};
366+
367+class Pointing : public TestFixture {};
368+class PointingRotateParametrized : public ::testing::WithParamInterface<std::pair<SimpleReport, SimpleReport>>, public Pointing {};
369+
370+TEST_P(PointingRotateParametrized, PointingRotateXY) {
371+ TestDriver driver;
372+ SimpleReport input = GetParam().first;
373+ SimpleReport expectations = GetParam().second;
374+
375+ pd_set_x(input.x);
376+ pd_set_y(input.y);
377+ pd_set_h(input.h);
378+ pd_set_v(input.v);
379+
380+ EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, 0));
381+ run_one_scan_loop();
382+
383+ // EXPECT_EMPTY_MOUSE_REPORT(driver);
384+ pd_clear_movement();
385+ run_one_scan_loop();
386+
387+ EXPECT_NO_MOUSE_REPORT(driver);
388+ run_one_scan_loop();
389+
390+ VERIFY_AND_CLEAR(driver);
391+}
392+// clang-format off
393+INSTANTIATE_TEST_CASE_P(
394+ Rotate180,
395+ PointingRotateParametrized,
396+ ::testing::Values(
397+ // Input Expected
398+ // Rotate Clockwise
399+ std::make_pair(SimpleReport{ 0,-1, 0, 0}, SimpleReport{ 0, 1, 0, 0}), // UP - DOWN
400+ std::make_pair(SimpleReport{ 0, 1, 0, 0}, SimpleReport{ 0,-1, 0, 0}), // DOWN - UP
401+ std::make_pair(SimpleReport{ 1, 0, 0, 0}, SimpleReport{-1, 0, 0, 0}), // RIGHT - LEFT
402+ std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 1, 0, 0, 0}) // LEFT - RIGHT
403+ ));
404+// clang-format on
405diff --git a/tests/pointing/rotate270/config.h b/tests/pointing/rotate270/config.h
406new file mode 100644
407index 0000000000000000000000000000000000000000..3977dd95e6c36d7996049a6378957fd233252093
408--- /dev/null
409+++ b/tests/pointing/rotate270/config.h
410@@ -0,0 +1,8 @@
411+// Copyright 2024 Dasky (@daskygit)
412+// SPDX-License-Identifier: GPL-2.0-or-later
413+
414+#pragma once
415+
416+#include "test_common.h"
417+
418+#define POINTING_DEVICE_ROTATION_270
419diff --git a/tests/pointing/rotate270/test.mk b/tests/pointing/rotate270/test.mk
420new file mode 100644
421index 0000000000000000000000000000000000000000..ba224d74f0abce7522bef05de75ede9d3552de6b
422--- /dev/null
423+++ b/tests/pointing/rotate270/test.mk
424@@ -0,0 +1,2 @@
425+POINTING_DEVICE_ENABLE = yes
426+POINTING_DEVICE_DRIVER = custom
427diff --git a/tests/pointing/rotate270/test_rotate270.cpp b/tests/pointing/rotate270/test_rotate270.cpp
428new file mode 100644
429index 0000000000000000000000000000000000000000..6735f06248954a875155693c6d08fde73fd79e21
430--- /dev/null
431+++ b/tests/pointing/rotate270/test_rotate270.cpp
432@@ -0,0 +1,60 @@
433+// Copyright 2024 Dasky (@daskygit)
434+// SPDX-License-Identifier: GPL-2.0-or-later
435+
436+#include "gtest/gtest.h"
437+#include "mouse_report_util.hpp"
438+#include "test_common.hpp"
439+#include "test_pointing_device_driver.h"
440+
441+using testing::_;
442+
443+struct SimpleReport {
444+ int16_t x;
445+ int16_t y;
446+ int16_t h;
447+ int16_t v;
448+};
449+
450+class Pointing : public TestFixture {};
451+class PointingRotateParametrized : public ::testing::WithParamInterface<std::pair<SimpleReport, SimpleReport>>, public Pointing {};
452+
453+TEST_P(PointingRotateParametrized, PointingRotateXY) {
454+ TestDriver driver;
455+ SimpleReport input = GetParam().first;
456+ SimpleReport expectations = GetParam().second;
457+
458+ pd_set_x(input.x);
459+ pd_set_y(input.y);
460+ pd_set_h(input.h);
461+ pd_set_v(input.v);
462+
463+ EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, 0));
464+ run_one_scan_loop();
465+
466+ // EXPECT_EMPTY_MOUSE_REPORT(driver);
467+ pd_clear_movement();
468+ run_one_scan_loop();
469+
470+ EXPECT_NO_MOUSE_REPORT(driver);
471+ run_one_scan_loop();
472+
473+ VERIFY_AND_CLEAR(driver);
474+}
475+// clang-format off
476+INSTANTIATE_TEST_CASE_P(
477+ Rotate270,
478+ PointingRotateParametrized,
479+ ::testing::Values(
480+ // Input Expected
481+ // Actual Result - Rotate Anticlockwise
482+ std::make_pair(SimpleReport{ 0,-1, 0, 0}, SimpleReport{ 1, 0, 0, 0}), // UP - RIGHT
483+ std::make_pair(SimpleReport{ 0, 1, 0, 0}, SimpleReport{-1, 0, 0, 0}), // DOWN - LEFT
484+ std::make_pair(SimpleReport{ 1, 0, 0, 0}, SimpleReport{ 0, 1, 0, 0}), // RIGHT - DOWN
485+ std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 0,-1, 0, 0}) // LEFT - UP
486+ // Rotate Clockwise
487+ // std::make_pair(SimpleReport{ 0,-1, 0, 0}, SimpleReport{-1, 0, 0, 0}), // UP - LEFT
488+ // std::make_pair(SimpleReport{ 0, 1, 0, 0}, SimpleReport{ 1, 0, 0, 0}), // DOWN - RIGHT
489+ // std::make_pair(SimpleReport{ 1, 0, 0, 0}, SimpleReport{ 0,-1, 0, 0}), // RIGHT - UP
490+ // std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 0, 1, 0, 0}) // LEFT - DOWN
491+ ));
492+// clang-format on
493diff --git a/tests/pointing/rotate90/config.h b/tests/pointing/rotate90/config.h
494new file mode 100644
495index 0000000000000000000000000000000000000000..3a18ec92e947217a056f35741dc11db7fe578d8f
496--- /dev/null
497+++ b/tests/pointing/rotate90/config.h
498@@ -0,0 +1,8 @@
499+// Copyright 2024 Dasky (@daskygit)
500+// SPDX-License-Identifier: GPL-2.0-or-later
501+
502+#pragma once
503+
504+#include "test_common.h"
505+
506+#define POINTING_DEVICE_ROTATION_90
507diff --git a/tests/pointing/rotate90/test.mk b/tests/pointing/rotate90/test.mk
508new file mode 100644
509index 0000000000000000000000000000000000000000..ba224d74f0abce7522bef05de75ede9d3552de6b
510--- /dev/null
511+++ b/tests/pointing/rotate90/test.mk
512@@ -0,0 +1,2 @@
513+POINTING_DEVICE_ENABLE = yes
514+POINTING_DEVICE_DRIVER = custom
515diff --git a/tests/pointing/rotate90/test_rotate90.cpp b/tests/pointing/rotate90/test_rotate90.cpp
516new file mode 100644
517index 0000000000000000000000000000000000000000..5c44faad0ab4bdad6a70c64f8bbeb06040778f36
518--- /dev/null
519+++ b/tests/pointing/rotate90/test_rotate90.cpp
520@@ -0,0 +1,60 @@
521+// Copyright 2024 Dasky (@daskygit)
522+// SPDX-License-Identifier: GPL-2.0-or-later
523+
524+#include "gtest/gtest.h"
525+#include "mouse_report_util.hpp"
526+#include "test_common.hpp"
527+#include "test_pointing_device_driver.h"
528+
529+using testing::_;
530+
531+struct SimpleReport {
532+ int16_t x;
533+ int16_t y;
534+ int16_t h;
535+ int16_t v;
536+};
537+
538+class Pointing : public TestFixture {};
539+class PointingRotateParametrized : public ::testing::WithParamInterface<std::pair<SimpleReport, SimpleReport>>, public Pointing {};
540+
541+TEST_P(PointingRotateParametrized, PointingRotateXY) {
542+ TestDriver driver;
543+ SimpleReport input = GetParam().first;
544+ SimpleReport expectations = GetParam().second;
545+
546+ pd_set_x(input.x);
547+ pd_set_y(input.y);
548+ pd_set_h(input.h);
549+ pd_set_v(input.v);
550+
551+ EXPECT_MOUSE_REPORT(driver, (expectations.x, expectations.y, expectations.h, expectations.v, 0));
552+ run_one_scan_loop();
553+
554+ // EXPECT_EMPTY_MOUSE_REPORT(driver);
555+ pd_clear_movement();
556+ run_one_scan_loop();
557+
558+ EXPECT_NO_MOUSE_REPORT(driver);
559+ run_one_scan_loop();
560+
561+ VERIFY_AND_CLEAR(driver);
562+}
563+// clang-format off
564+INSTANTIATE_TEST_CASE_P(
565+ Rotate90,
566+ PointingRotateParametrized,
567+ ::testing::Values(
568+ // Input Expected
569+ // Actual Result - Rotate Anticlockwise
570+ std::make_pair(SimpleReport{ 0,-1, 0, 0}, SimpleReport{-1, 0, 0, 0}), // UP - LEFT
571+ std::make_pair(SimpleReport{ 0, 1, 0, 0}, SimpleReport{ 1, 0, 0, 0}), // DOWN - RIGHT
572+ std::make_pair(SimpleReport{ 1, 0, 0, 0}, SimpleReport{ 0,-1, 0, 0}), // RIGHT - UP
573+ std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 0, 1, 0, 0}) // LEFT - DOWN
574+ // Rotate Clockwise
575+ // std::make_pair(SimpleReport{ 0,-1, 0, 0}, SimpleReport{ 1, 0, 0, 0}), // UP - RIGHT
576+ // std::make_pair(SimpleReport{ 0, 1, 0, 0}, SimpleReport{-1, 0, 0, 0}), // DOWN - LEFT
577+ // std::make_pair(SimpleReport{ 1, 0, 0, 0}, SimpleReport{ 0,-1, 0, 0}), // RIGHT - DOWN
578+ // std::make_pair(SimpleReport{ -1, 0, 0, 0}, SimpleReport{ 0, 1, 0, 0}) // LEFT - UP
579+ ));
580+// clang-format on
581diff --git a/tests/pointing/test.mk b/tests/pointing/test.mk
582new file mode 100644
583index 0000000000000000000000000000000000000000..95fde21cb92ccb2d79ec3c03bf32e91009d33491
584--- /dev/null
585+++ b/tests/pointing/test.mk
586@@ -0,0 +1,4 @@
587+POINTING_DEVICE_ENABLE = yes
588+MOUSEKEY_ENABLE = no
589+POINTING_DEVICE_DRIVER = custom
590+
591diff --git a/tests/pointing/test_pointing.cpp b/tests/pointing/test_pointing.cpp
592new file mode 100644
593index 0000000000000000000000000000000000000000..d59d014925174b231e46f69733dac422305885cd
594--- /dev/null
595+++ b/tests/pointing/test_pointing.cpp
596@@ -0,0 +1,166 @@
597+// Copyright 2024 Dasky (@daskygit)
598+// SPDX-License-Identifier: GPL-2.0-or-later
599+
600+#include "gtest/gtest.h"
601+#include "mouse_report_util.hpp"
602+#include "test_common.hpp"
603+#include "test_pointing_device_driver.h"
604+#include "mousekey.h"
605+
606+using testing::_;
607+
608+class Pointing : public TestFixture {};
609+class PointingButtonsViaMousekeysParametrized : public ::testing::WithParamInterface<std::pair<KeymapKey, uint8_t>>, public Pointing {};
610+
611+TEST_F(Pointing, SendMouseIsNotCalledWithNoInput) {
612+ TestDriver driver;
613+ EXPECT_NO_MOUSE_REPORT(driver);
614+ run_one_scan_loop();
615+}
616+
617+TEST_F(Pointing, Xnegative) {
618+ TestDriver driver;
619+
620+ pd_set_x(-10);
621+ EXPECT_MOUSE_REPORT(driver, (-10, 0, 0, 0, 0));
622+ run_one_scan_loop();
623+
624+ pd_clear_movement();
625+ // EXPECT_EMPTY_MOUSE_REPORT(driver);
626+ run_one_scan_loop();
627+
628+ VERIFY_AND_CLEAR(driver);
629+}
630+
631+TEST_F(Pointing, Xpositive) {
632+ TestDriver driver;
633+
634+ pd_set_x(10);
635+ EXPECT_MOUSE_REPORT(driver, (10, 0, 0, 0, 0));
636+ run_one_scan_loop();
637+
638+ pd_clear_movement();
639+ // EXPECT_EMPTY_MOUSE_REPORT(driver);
640+ run_one_scan_loop();
641+
642+ VERIFY_AND_CLEAR(driver);
643+}
644+
645+TEST_F(Pointing, Ynegative) {
646+ TestDriver driver;
647+
648+ pd_set_y(-20);
649+ EXPECT_MOUSE_REPORT(driver, (0, -20, 0, 0, 0));
650+ run_one_scan_loop();
651+
652+ pd_clear_movement();
653+ // EXPECT_EMPTY_MOUSE_REPORT(driver);
654+ run_one_scan_loop();
655+
656+ VERIFY_AND_CLEAR(driver);
657+}
658+
659+TEST_F(Pointing, Ypositive) {
660+ TestDriver driver;
661+
662+ pd_set_y(20);
663+ EXPECT_MOUSE_REPORT(driver, (0, 20, 0, 0, 0));
664+ run_one_scan_loop();
665+
666+ pd_clear_movement();
667+ // EXPECT_EMPTY_MOUSE_REPORT(driver);
668+ run_one_scan_loop();
669+
670+ VERIFY_AND_CLEAR(driver);
671+}
672+
673+TEST_F(Pointing, XandY) {
674+ TestDriver driver;
675+
676+ pd_set_x(-50);
677+ pd_set_y(100);
678+ EXPECT_MOUSE_REPORT(driver, (-50, 100, 0, 0, 0));
679+ run_one_scan_loop();
680+
681+ pd_clear_movement();
682+ // EXPECT_EMPTY_MOUSE_REPORT(driver);
683+ run_one_scan_loop();
684+
685+ VERIFY_AND_CLEAR(driver);
686+}
687+
688+TEST_F(Pointing, CorrectButtonIsReportedWhenPressed) {
689+ TestDriver driver;
690+
691+ EXPECT_MOUSE_REPORT(driver, (0, 0, 0, 0, 1));
692+ pd_press_button(POINTING_DEVICE_BUTTON1);
693+ run_one_scan_loop();
694+
695+ EXPECT_EMPTY_MOUSE_REPORT(driver);
696diff --git a/tests/test_common/mouse_report_util.cpp b/tests/test_common/mouse_report_util.cpp
697new file mode 100644
698index 0000000000000000000000000000000000000000..60cdeced24f5a6fce3454713fc28f401bba2282a
699--- /dev/null
700+++ b/tests/test_common/mouse_report_util.cpp
701@@ -0,0 +1,58 @@
702+/* Copyright 2017 Fred Sundvik
703+ *
704+ * This program is free software: you can redistribute it and/or modify
705+ * it under the terms of the GNU General Public License as published by
706+ * the Free Software Foundation, either version 2 of the License, or
707+ * (at your option) any later version.
708+ *
709+ * This program is distributed in the hope that it will be useful,
710+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
711+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
712+ * GNU General Public License for more details.
713+ *
714+ * You should have received a copy of the GNU General Public License
715+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
716+ */
717+
718+#include "mouse_report_util.hpp"
719+#include <cstdint>
720+#include <vector>
721+#include <algorithm>
722+
723+using namespace testing;
724+
725+bool operator==(const report_mouse_t& lhs, const report_mouse_t& rhs) {
726+ return lhs.x == rhs.x && lhs.y == rhs.y && lhs.h == rhs.h && lhs.v == rhs.v && lhs.buttons == rhs.buttons;
727+}
728+
729+std::ostream& operator<<(std::ostream& os, const report_mouse_t& report) {
730+ os << std::setw(10) << std::left << "mouse report: ";
731+
732+ if (report.x == 0 && report.y == 0 && report.h == 0 && report.v == 0 && report.buttons == 0) {
733+ return os << "empty" << std::endl;
734+ }
735+
736+ os << "(X:" << (int)report.x << ", Y:" << (int)report.y << ", H:" << (int)report.h << ", V:" << (int)report.v << ", B:" << (int)report.buttons << ")";
737+ return os << std::endl;
738+}
739+
740+MouseReportMatcher::MouseReportMatcher(int16_t x, int16_t y, int8_t h, int8_t v, uint8_t button_mask) {
741+ memset(&m_report, 0, sizeof(report_mouse_t));
742+ m_report.x = x;
743+ m_report.y = y;
744+ m_report.h = h;
745+ m_report.v = v;
746+ m_report.buttons = button_mask;
747+}
748+
749+bool MouseReportMatcher::MatchAndExplain(report_mouse_t& report, MatchResultListener* listener) const {
750+ return m_report == report;
751+}
752+
753+void MouseReportMatcher::DescribeTo(::std::ostream* os) const {
754+ *os << "is equal to " << m_report;
755+}
756+
757+void MouseReportMatcher::DescribeNegationTo(::std::ostream* os) const {
758+ *os << "is not equal to " << m_report;
759+}
760diff --git a/tests/test_common/mouse_report_util.hpp b/tests/test_common/mouse_report_util.hpp
761new file mode 100644
762index 0000000000000000000000000000000000000000..645f6aa58c64f8f7833ddd6a6300ba5e9e246d59
763--- /dev/null
764+++ b/tests/test_common/mouse_report_util.hpp
765@@ -0,0 +1,38 @@
766+/* Copyright 2017 Fred Sundvik
767+ *
768+ * This program is free software: you can redistribute it and/or modify
769+ * it under the terms of the GNU General Public License as published by
770+ * the Free Software Foundation, either version 2 of the License, or
771+ * (at your option) any later version.
772+ *
773+ * This program is distributed in the hope that it will be useful,
774+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
775+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
776+ * GNU General Public License for more details.
777+ *
778+ * You should have received a copy of the GNU General Public License
779+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
780+ */
781+
782+#pragma once
783+#include "report.h"
784+#include <ostream>
785+#include "gmock/gmock.h"
786+
787+bool operator==(const report_mouse_t& lhs, const report_mouse_t& rhs);
788+std::ostream& operator<<(std::ostream& stream, const report_mouse_t& value);
789+
790+class MouseReportMatcher : public testing::MatcherInterface<report_mouse_t&> {
791+ public:
792+ MouseReportMatcher(int16_t x, int16_t y, int8_t h, int8_t v, uint8_t button_mask);
793+ virtual bool MatchAndExplain(report_mouse_t& report, testing::MatchResultListener* listener) const override;
794+ virtual void DescribeTo(::std::ostream* os) const override;
795+ virtual void DescribeNegationTo(::std::ostream* os) const override;
796+
797+ private:
798+ report_mouse_t m_report;
799+};
800+
801+inline testing::Matcher<report_mouse_t&> MouseReport(int16_t x, int16_t y, int8_t h, int8_t v, uint8_t button_mask) {
802+ return testing::MakeMatcher(new MouseReportMatcher(x, y, h, v, button_mask));
803+}
804diff --git a/tests/test_common/pointing_device_driver.c b/tests/test_common/pointing_device_driver.c
805new file mode 100644
806index 0000000000000000000000000000000000000000..b64dbad50635f3583120f1da0ff2a6bc4ceaaf82
807--- /dev/null
808+++ b/tests/test_common/pointing_device_driver.c
809@@ -0,0 +1,109 @@
810+// Copyright 2024 Dasky (@daskygit)
811+// SPDX-License-Identifier: GPL-2.0-or-later
812+
813+#include "report.h"
814+#include "test_pointing_device_driver.h"
815+#include <string.h>
816+
817+typedef struct {
818+ bool pressed;
819+ bool dirty;
820+} pd_button_state_t;
821+
822+typedef struct {
823+ int16_t x;
824+ int16_t y;
825+ int16_t h;
826+ int16_t v;
827+ pd_button_state_t button_state[8];
828+ uint16_t cpi;
829+ bool initiated;
830+} pd_config_t;
831+
832+static pd_config_t pd_config = {0};
833+
834+void pointing_device_driver_init(void) {
835+ pd_set_init(true);
836+}
837+
838+report_mouse_t pointing_device_driver_get_report(report_mouse_t mouse_report) {
839+ for (uint8_t i = 0; i < 8; i++) {
840+ if (pd_config.button_state[i].dirty) {
841+ pd_config.button_state[i].dirty = false;
842+ if (pd_config.button_state[i].pressed) {
843+ mouse_report.buttons |= 1 << (i);
844+ } else {
845+ mouse_report.buttons &= ~(1 << (i));
846+ }
847+ }
848+ }
849+ mouse_report.x = pd_config.x;
850+ mouse_report.y = pd_config.y;
851+ mouse_report.h = pd_config.h;
852+ mouse_report.v = pd_config.v;
853+ return mouse_report;
854+}
855+
856+__attribute__((weak)) uint16_t pointing_device_driver_get_cpi(void) {
857+ return pd_config.cpi;
858+}
859+
860+__attribute__((weak)) void pointing_device_driver_set_cpi(uint16_t cpi) {
861+ pd_config.cpi = cpi;
862+}
863+
864+void pd_press_button(uint8_t btn) {
865+ pd_config.button_state[btn].dirty = true;
866+ pd_config.button_state[btn].pressed = true;
867+}
868+void pd_release_button(uint8_t btn) {
869+ pd_config.button_state[btn].dirty = true;
870+ pd_config.button_state[btn].pressed = false;
871+}
872+
873+void pd_clear_all_buttons(void) {
874+ for (uint8_t i = 0; i < 8; i++) {
875+ pd_config.button_state[i].dirty = true;
876+ pd_config.button_state[i].pressed = false;
877+ }
878+}
879+
880+void pd_set_x(int16_t x) {
881+ pd_config.x = x;
882+}
883+
884+void pd_clear_x(void) {
885+ pd_set_x(0);
886+}
887+
888+void pd_set_y(int16_t y) {
889+ pd_config.y = y;
890+}
891+void pd_clear_y(void) {
892+ pd_set_y(0);
893+}
894+
895+void pd_set_h(int16_t h) {
896+ pd_config.h = h;
897+}
898+void pd_clear_h(void) {
899+ pd_set_h(0);
900+}
901+
902+void pd_set_v(int16_t v) {
903+ pd_config.v = v;
904+}
905+void pd_clear_v(void) {
906+ pd_set_v(0);
907+}
908+
909diff --git a/tests/test_common/test_driver.cpp b/tests/test_common/test_driver.cpp
910index d410b225f9d5a6a6bcb7e18d6fb6af37182d68da..70b920ac86aba699efa5c296f82b18752eb49e79 100644
911--- a/tests/test_common/test_driver.cpp
912+++ b/tests/test_common/test_driver.cpp
913@@ -54,6 +54,7 @@ void TestDriver::send_nkro(report_nkro_t* report) {
914 }
915
916 void TestDriver::send_mouse(report_mouse_t* report) {
917+ 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;
918 m_this->send_mouse_mock(*report);
919 }
920
921diff --git a/tests/test_common/test_driver.hpp b/tests/test_common/test_driver.hpp
922index ec75d3fff2268902d2876bca046f9b4e9e9eeb37..fea8225953a8e0f98e2cd471adeee6889ddb7369 100644
923--- a/tests/test_common/test_driver.hpp
924+++ b/tests/test_common/test_driver.hpp
925@@ -66,6 +66,25 @@ class TestDriver {
926 */
927 #define EXPECT_REPORT(driver, report) EXPECT_CALL((driver), send_keyboard_mock(KeyboardReport report))
928
929+/**
930+ * @brief Sets gmock expectation that a mouse report of `report` will be sent.
931+ * For this macro to parse correctly, the `report` arg must be surrounded by
932+ * parentheses ( ). For instance,
933+ *
934+ * // Expect that a report of "X:-10 Y:0 H:0 V:10 BTN:1 " is sent to the host.
935+ * EXPECT_REPORT(driver, (-10, 0, 0, 0, 1));
936+ *
937+ * is shorthand for
938+ *
939+ * EXPECT_CALL(driver, send_mouse_mock(MouseReport(-10, 0, 0, 0, 1)));
940+ *
941+ * It is possible to use .Times() and other gmock APIS with EXPECT_REPORT, for instance,
942+ * allow only single report to be sent:
943+ *
944+ * EXPECT_REPORT(driver, (-10, 0, 0, 0, 1)).Times(1);
945+ */
946+#define EXPECT_MOUSE_REPORT(driver, report) EXPECT_CALL((driver), send_mouse_mock(MouseReport report))
947+
948 /**
949 * @brief Sets gmock expectation that Unicode `code_point` is sent with UNICODE_MODE_LINUX input
950 * mode. For instance for U+2013,
951@@ -87,6 +106,15 @@ class TestDriver {
952 */
953 #define EXPECT_EMPTY_REPORT(driver) EXPECT_REPORT(driver, ())
954
955+/**
956+ * @brief Sets gmock expectation that a empty keyboard report will be sent.
957+ * It is possible to use .Times() and other gmock APIS with EXPECT_EMPTY_MOUSE_REPORT, for instance,
958+ * allow any number of empty reports with:
959+ *
960+ * EXPECT_EMPTY_MOUSE_REPORT(driver).Times(AnyNumber());
961+ */
962+#define EXPECT_EMPTY_MOUSE_REPORT(driver) EXPECT_MOUSE_REPORT(driver, (0, 0, 0, 0, 0))
963+
964 /**
965 * @brief Sets gmock expectation that a keyboard report will be sent, without matching its content.
966 * It is possible to use .Times() and other gmock APIS with EXPECT_ANY_REPORT, for instance,
967@@ -96,11 +124,25 @@ class TestDriver {
968 */
969 #define EXPECT_ANY_REPORT(driver) EXPECT_CALL((driver), send_keyboard_mock(_))
970
971+/**
972+ * @brief Sets gmock expectation that a mouse report will be sent, without matching its content.
973+ * It is possible to use .Times() and other gmock APIS with EXPECT_ANY_MOUSE_REPORT, for instance,
974+ * allow a single arbitrary report with:
975+ *
976+ * EXPECT_ANY_MOUSE_REPORT(driver).Times(1);
977+ */
978+#define EXPECT_ANY_MOUSE_REPORT(driver) EXPECT_CALL((driver), send_mouse_mock(_))
979+
980 /**
981 * @brief Sets gmock expectation that no keyboard report will be sent at all.
982 */
983 #define EXPECT_NO_REPORT(driver) EXPECT_ANY_REPORT(driver).Times(0)
984
985+/**
986+ * @brief Sets gmock expectation that no keyboard report will be sent at all.
987+ */
988+#define EXPECT_NO_MOUSE_REPORT(driver) EXPECT_ANY_MOUSE_REPORT(driver).Times(0)
989+
990 /** @brief Tests whether keycode `actual` is equal to `expected`. */
991 #define EXPECT_KEYCODE_EQ(actual, expected) EXPECT_THAT((actual), KeycodeEq((expected)))
992
993diff --git a/tests/test_common/test_fixture.cpp b/tests/test_common/test_fixture.cpp
994index 3cfb2cb4c29235c499bf2c18fbefb5d8406215bd..81806b0e6d274bc95c7f531f0a47db41ce35f04a 100644
995--- a/tests/test_common/test_fixture.cpp
996+++ b/tests/test_common/test_fixture.cpp
997@@ -7,6 +7,7 @@
998 #include "gmock/gmock.h"
999 #include "gtest/gtest.h"
1000 #include "keyboard_report_util.hpp"
1001+#include "mouse_report_util.hpp"
1002 #include "keycode.h"
1003 #include "test_driver.hpp"
1004 #include "test_logger.hpp"
1005@@ -69,6 +70,9 @@ TestFixture::~TestFixture() {
1006 /* Reset keyboard state. */
1007 clear_all_keys();
1008
1009+#ifdef MOUSEKEY_ENABLE
1010+ EXPECT_EMPTY_MOUSE_REPORT(driver);
1011+#endif
1012 clear_keyboard();
1013
1014 clear_oneshot_mods();
1015diff --git a/tests/test_common/test_pointing_device_driver.h b/tests/test_common/test_pointing_device_driver.h
1016new file mode 100644
1017index 0000000000000000000000000000000000000000..ae136b21cdf0eacb4ce12465d0d1dde04b5ab1c6
1018--- /dev/null
1019+++ b/tests/test_common/test_pointing_device_driver.h
1020@@ -0,0 +1,32 @@
1021+// Copyright 2024 Dasky (@daskygit)
1022+// SPDX-License-Identifier: GPL-2.0-or-later
1023+
1024+#pragma once
1025+
1026+#ifdef __cplusplus
1027+extern "C" {
1028+#endif
1029+
1030+void pd_press_button(uint8_t btn);
1031+void pd_release_button(uint8_t btn);
1032+void pd_clear_all_buttons(void);
1033+
1034+void pd_set_x(int16_t x);
1035+void clear_x(void);
1036+
1037+void pd_set_y(int16_t y);
1038+void pd_clear_y(void);
1039+
1040+void pd_set_h(int16_t h);
1041+void pd_clear_h(void);
1042+
1043+void pd_set_v(int16_t v);
1044+void pd_clear_v(void);
1045+
1046+void pd_clear_movement(void);
1047+
1048+void pd_set_init(bool success);
1049+
1050+#ifdef __cplusplus
1051+}
1052+#endif