1 /*
2     This file is part of the KDE project
3     SPDX-FileCopyrightText: 2021 Steffen Hartleib <steffenhartleib@t-online.de>
4 
5     SPDX-License-Identifier: LGPL-2.1-or-later
6 */
7 
8 #include <ktwofingertap.h>
9 
10 #include <QTest>
11 #include <QSignalSpy>
12 #include <QWidget>
13 #include <QMainWindow>
14 
15 class KTwoFingerSwipeTest : public QObject
16 {
17     Q_OBJECT
18 
19     QTouchDevice *mDev = nullptr;
20     QMainWindow mMainWindow;
21     QWidget mWidget;
22     KTwoFingerTapRecognizer *mTwoFingerRec = nullptr;
23     Qt::GestureType mKTwoFingerTapGesture;
24     QPoint mTouchPointPos = QPoint(100, 100);
25     QSignalSpy *mSpyTapFinished = nullptr;
26     QSignalSpy *mSpyTapStarted = nullptr;
27     QSignalSpy *mSpyTapUpdated = nullptr;
28     QSignalSpy *mSpyTapCanceled = nullptr;
29 
30 Q_SIGNALS:
31     void tapStarted(KTwoFingerTap *);
32     void tapFinished(KTwoFingerTap *);
33     void tapUpdated(KTwoFingerTap *);
34     void tapCanceled(KTwoFingerTap *);
35 
36 protected:
eventFilter(QObject * watched,QEvent * e)37     bool eventFilter(QObject *watched, QEvent *e) override
38     {
39         if (e->type() == QEvent::Gesture) {
40             QGestureEvent *gEvent = static_cast<QGestureEvent *>(e);
41             KTwoFingerTap *tap = static_cast<KTwoFingerTap *>(gEvent->gesture(mKTwoFingerTapGesture));
42             if (tap) {
43                 const Qt::GestureState state = tap->state();
44                 if (state == Qt::GestureFinished) {
45                     Q_EMIT tapFinished(tap);
46                 } else if (state == Qt::GestureStarted) {
47                     Q_EMIT tapStarted(tap);
48                 } else if (state == Qt::GestureUpdated) {
49                     Q_EMIT tapUpdated(tap);
50                 } else if (state == Qt::GestureCanceled) {
51                     Q_EMIT tapCanceled(tap);
52                 }
53             }
54             e->accept();
55             return true;
56         }
57         return QObject::eventFilter(watched, e);
58     }
59 
60 protected Q_SLOTS:
slotTapFinished(KTwoFingerTap * tap)61     void slotTapFinished (KTwoFingerTap *tap)
62     {
63         compareGesturePositions(tap);
64     }
slotTapStarted(KTwoFingerTap * tap)65     void slotTapStarted (KTwoFingerTap *tap)
66     {
67         compareGesturePositions(tap);
68     }
slotTapUpdated(KTwoFingerTap * tap)69     void slotTapUpdated (KTwoFingerTap *tap)
70     {
71         compareGesturePositions(tap);
72     }
slotTapCanceled(KTwoFingerTap * tap)73     void slotTapCanceled (KTwoFingerTap *tap)
74     {
75         compareGesturePositions(tap);
76     }
77 private:
compareGesturePositions(KTwoFingerTap * tap)78     void compareGesturePositions(KTwoFingerTap *tap)
79     {
80         QCOMPARE(tap->pos(), mTouchPointPos);
81         QCOMPARE(tap->screenPos(), mWidget.mapToGlobal(mTouchPointPos));
82         QCOMPARE(tap->hotSpot(), mWidget.mapToGlobal(mTouchPointPos));
83         QCOMPARE(tap->scenePos(), mWidget.mapToGlobal(mTouchPointPos));
84         QVERIFY(tap->hasHotSpot());
85     }
86 
clearSignalSpys()87     void clearSignalSpys()
88     {
89         mSpyTapFinished->clear();
90         mSpyTapCanceled->clear();
91         mSpyTapUpdated->clear();
92         mSpyTapStarted->clear();
93     }
94 
95 private Q_SLOTS:
initTestCase()96     void initTestCase()
97     {
98         mDev = QTest::createTouchDevice();
99 
100         mMainWindow.setGeometry(0, 0, 500, 500);
101         mMainWindow.setCentralWidget(&mWidget);
102         mMainWindow.show();
103 
104         mWidget.installEventFilter(this);
105         mWidget.setAttribute(Qt::WA_AcceptTouchEvents);
106 
107         QVERIFY(QTest::qWaitForWindowActive(&mMainWindow));
108         QVERIFY(QTest::qWaitForWindowActive(&mWidget));
109 
110         mTwoFingerRec = new KTwoFingerTapRecognizer();
111         QVERIFY(mTwoFingerRec);
112 
113         mKTwoFingerTapGesture = QGestureRecognizer::registerRecognizer(mTwoFingerRec);
114         QVERIFY(mKTwoFingerTapGesture & Qt::CustomGesture);
115 
116         mWidget.grabGesture(mKTwoFingerTapGesture);
117 
118         connect(this, &KTwoFingerSwipeTest::tapFinished, this, &KTwoFingerSwipeTest::slotTapFinished);
119         connect(this, &KTwoFingerSwipeTest::tapStarted, this, &KTwoFingerSwipeTest::slotTapStarted);
120         connect(this, &KTwoFingerSwipeTest::tapUpdated, this, &KTwoFingerSwipeTest::slotTapUpdated);
121         connect(this, &KTwoFingerSwipeTest::tapCanceled, this, &KTwoFingerSwipeTest::slotTapCanceled);
122 
123         mSpyTapFinished = new QSignalSpy(this, &KTwoFingerSwipeTest::tapFinished);
124         QVERIFY(mSpyTapFinished->isValid());
125 
126         mSpyTapStarted = new QSignalSpy(this, &KTwoFingerSwipeTest::tapStarted);
127         QVERIFY(mSpyTapStarted->isValid());
128 
129         mSpyTapUpdated = new QSignalSpy(this, &KTwoFingerSwipeTest::tapUpdated);
130         QVERIFY(mSpyTapUpdated->isValid());
131 
132         mSpyTapCanceled = new QSignalSpy(this, &KTwoFingerSwipeTest::tapCanceled);
133         QVERIFY(mSpyTapCanceled->isValid());
134 
135         QTest::qWait(1000);
136     }
137 
testChangeTapRadius()138     void testChangeTapRadius()
139     {
140         QVERIFY(mTwoFingerRec->tapRadius() >= 0);
141 
142         mTwoFingerRec->setTapRadius(10);
143         QCOMPARE(mTwoFingerRec->tapRadius(), 10);
144 
145         mTwoFingerRec->setTapRadius(-100);
146         QCOMPARE(mTwoFingerRec->tapRadius(), 0);
147 
148         mTwoFingerRec->setTapRadius(40);
149         QCOMPARE(mTwoFingerRec->tapRadius(), 40);
150     }
151 
testSuccessfulGesture()152     void testSuccessfulGesture()
153     {
154         // Test a successful gesture with two fingers, where the touch points wiggle only a little bit.
155 
156         clearSignalSpys();
157 
158         int tapRadius = mTwoFingerRec->tapRadius();
159         QPoint wiggleRoom = QPoint (tapRadius / 2, tapRadius / 2);
160 
161         QTest::touchEvent(&mWidget, mDev)
162             .press(0, mTouchPointPos, (QWidget *) nullptr);
163 
164         QTest::touchEvent(&mWidget, mDev)
165             .move(0, mTouchPointPos + wiggleRoom, (QWidget *) nullptr)
166             .press(1, mTouchPointPos, (QWidget *) nullptr);
167 
168         QTest::touchEvent(&mWidget, mDev)
169             .move(0, mTouchPointPos, (QWidget *) nullptr)
170             .move(1, mTouchPointPos + wiggleRoom, (QWidget *) nullptr);
171 
172         QTest::touchEvent(&mWidget, mDev)
173             .move(0, mTouchPointPos, (QWidget *) nullptr)
174             .release(1, mTouchPointPos, (QWidget *) nullptr);
175 
176         QTest::touchEvent(&mWidget, mDev)
177             .release(0, mTouchPointPos, (QWidget *) nullptr);
178 
179         if (mSpyTapStarted->count() == 0) {
180             QVERIFY(mSpyTapStarted->wait(1000));
181         }
182         if (mSpyTapFinished->count() == 0) {
183             QVERIFY(mSpyTapFinished->wait(1000));
184         }
185 
186         QCOMPARE(mSpyTapStarted->count(), 1);
187         QCOMPARE(mSpyTapFinished->count(), 1);
188         QCOMPARE(mSpyTapCanceled->count(), 0);
189         QVERIFY(mSpyTapUpdated->count() > 0);
190     }
191 
testFailingGesture()192     void testFailingGesture()
193     {
194         // Test a failing gesture with two fingers, where the touch points wiggle too much at beginning.
195 
196         clearSignalSpys();
197 
198         int tapRadius = mTwoFingerRec->tapRadius();
199         QPoint wiggleRoom = QPoint (tapRadius, tapRadius);
200 
201         QTest::touchEvent(&mWidget, mDev)
202             .press(0, mTouchPointPos, (QWidget *) nullptr);
203 
204         QTest::touchEvent(&mWidget, mDev)
205             .move(0, mTouchPointPos + wiggleRoom, (QWidget *) nullptr)
206             .press(1, mTouchPointPos, (QWidget *) nullptr);
207 
208         QTest::touchEvent(&mWidget, mDev)
209             .move(0, mTouchPointPos, (QWidget *) nullptr)
210             .move(1, mTouchPointPos + wiggleRoom, (QWidget *) nullptr);
211 
212         QTest::touchEvent(&mWidget, mDev)
213             .move(0, mTouchPointPos, (QWidget *) nullptr)
214             .release(1, mTouchPointPos, (QWidget *) nullptr);
215 
216         QTest::touchEvent(&mWidget, mDev)
217             .release(0, mTouchPointPos, (QWidget *) nullptr);
218 
219         QCOMPARE(mSpyTapStarted->count(), 0);
220         QCOMPARE(mSpyTapFinished->count(), 0);
221         QCOMPARE(mSpyTapCanceled->count(), 0);
222         QCOMPARE(mSpyTapUpdated->count(), 0);
223     }
224 
testFailingGesture2()225     void testFailingGesture2()
226     {
227         // Test a failing gesture with two fingers, where the touch points wiggle too much in the middle.
228         clearSignalSpys();
229         int tapRadius = mTwoFingerRec->tapRadius();
230         QPoint wiggleRoom = QPoint (tapRadius, tapRadius);
231 
232         QTest::touchEvent(&mWidget, mDev)
233             .press(0, mTouchPointPos, (QWidget *) nullptr);
234 
235         QTest::touchEvent(&mWidget, mDev)
236             .move(0, mTouchPointPos, (QWidget *) nullptr)
237             .press(1, mTouchPointPos, (QWidget *) nullptr);
238 
239         QTest::touchEvent(&mWidget, mDev)
240             .move(0, mTouchPointPos, (QWidget *) nullptr)
241             .move(1, mTouchPointPos + wiggleRoom, (QWidget *) nullptr);
242 
243         QTest::touchEvent(&mWidget, mDev)
244             .move(0, mTouchPointPos, (QWidget *) nullptr)
245             .release(1, mTouchPointPos, (QWidget *) nullptr);
246 
247         QTest::touchEvent(&mWidget, mDev)
248             .release(0, mTouchPointPos, (QWidget *) nullptr);
249 
250         if (mSpyTapStarted->count() == 0) {
251             QVERIFY(mSpyTapStarted->wait(1000));
252         }
253         QCOMPARE(mSpyTapStarted->count(), 1);
254         QCOMPARE(mSpyTapFinished->count(), 0);
255         QCOMPARE(mSpyTapCanceled->count(), 1);
256         QVERIFY(mSpyTapUpdated->count() >= 0);
257     }
258 
testFailingGesture3()259     void testFailingGesture3()
260     {
261         // Test a failing gesture with two fingers, where the touch points wiggle too much in the middle,
262         // and the second finger press and release successively.
263 
264         clearSignalSpys();
265         int tapRadius = mTwoFingerRec->tapRadius();
266         QPoint wiggleRoom = QPoint (tapRadius, tapRadius);
267 
268         QTest::touchEvent(&mWidget, mDev)
269             .press(0, mTouchPointPos, (QWidget *) nullptr);
270 
271         QTest::touchEvent(&mWidget, mDev)
272             .move(0, mTouchPointPos, (QWidget *) nullptr)
273             .press(1, mTouchPointPos, (QWidget *) nullptr);
274 
275         QTest::touchEvent(&mWidget, mDev)
276             .move(0, mTouchPointPos, (QWidget *) nullptr)
277             .move(1, mTouchPointPos + wiggleRoom, (QWidget *) nullptr);
278 
279         QTest::touchEvent(&mWidget, mDev)
280             .move(0, mTouchPointPos, (QWidget *) nullptr)
281             .release(1, mTouchPointPos, (QWidget *) nullptr);
282 
283         QTest::touchEvent(&mWidget, mDev)
284             .move(0, mTouchPointPos, (QWidget *) nullptr)
285             .press(1, mTouchPointPos, (QWidget *) nullptr);
286 
287         QTest::touchEvent(&mWidget, mDev)
288             .move(0, mTouchPointPos, (QWidget *) nullptr)
289             .release(1, mTouchPointPos, (QWidget *) nullptr);
290 
291         QTest::touchEvent(&mWidget, mDev)
292             .release(0, mTouchPointPos, (QWidget *) nullptr);
293 
294         if (mSpyTapStarted->count() == 0) {
295             QVERIFY(mSpyTapStarted->wait(1000));
296         }
297         QCOMPARE(mSpyTapStarted->count(), 1);
298         QCOMPARE(mSpyTapFinished->count(), 0);
299         QCOMPARE(mSpyTapCanceled->count(), 1);
300         QVERIFY(mSpyTapUpdated->count() >= 0);
301     }
302 
testFailingGesture_threeFingers()303     void testFailingGesture_threeFingers()
304     {
305         // the test a gesture with three fingers should be end with gesture canceled.
306         clearSignalSpys();
307         int tapRadius = mTwoFingerRec->tapRadius();
308         QPoint wiggleRoom = QPoint (tapRadius / 2, tapRadius / 2);
309 
310         QTest::touchEvent(&mWidget, mDev)
311             .press(0, mTouchPointPos, (QWidget *) nullptr);
312 
313         QTest::touchEvent(&mWidget, mDev)
314             .move(0, mTouchPointPos, (QWidget *) nullptr)
315             .press(1, mTouchPointPos, (QWidget *) nullptr);
316 
317         QTest::touchEvent(&mWidget, mDev)
318             .move(0, mTouchPointPos, (QWidget *) nullptr)
319             .move(1, mTouchPointPos + wiggleRoom, (QWidget *) nullptr)
320             .press(2, mTouchPointPos, (QWidget *) nullptr);
321 
322         QTest::touchEvent(&mWidget, mDev)
323             .move(0, mTouchPointPos, (QWidget *) nullptr)
324             .release(1, mTouchPointPos, (QWidget *) nullptr);
325 
326         QTest::touchEvent(&mWidget, mDev)
327             .release(0, mTouchPointPos, (QWidget *) nullptr);
328 
329         if (mSpyTapStarted->count() == 0) {
330             QVERIFY(mSpyTapStarted->wait(1000));
331         }
332         QCOMPARE(mSpyTapStarted->count(), 1);
333         QCOMPARE(mSpyTapFinished->count(), 0);
334         QCOMPARE(mSpyTapCanceled->count(), 1);
335         QVERIFY(mSpyTapUpdated->count() >= 0);
336     }
337 
testFailingGesture_threeFingers2()338     void testFailingGesture_threeFingers2()
339     {
340 
341         clearSignalSpys();
342 
343         QTest::touchEvent(&mWidget, mDev)
344             .press(0, mTouchPointPos, (QWidget *) nullptr);
345 
346         QTest::touchEvent(&mWidget, mDev)
347             .move(0, mTouchPointPos, (QWidget *) nullptr)
348             .press(1, mTouchPointPos, (QWidget *) nullptr);
349 
350         QTest::touchEvent(&mWidget, mDev)
351             .move(0, mTouchPointPos, (QWidget *) nullptr)
352             .move(1, mTouchPointPos, (QWidget *) nullptr)
353             .press(2, mTouchPointPos, (QWidget *) nullptr);
354 
355         QTest::touchEvent(&mWidget, mDev)
356             .move(0, mTouchPointPos, (QWidget *) nullptr)
357             .move(1, mTouchPointPos, (QWidget *) nullptr)
358             .release(3, mTouchPointPos, (QWidget *) nullptr);
359 
360         QTest::touchEvent(&mWidget, mDev)
361             .move(0, mTouchPointPos, (QWidget *) nullptr)
362             .release(1, mTouchPointPos, (QWidget *) nullptr);
363 
364         QTest::touchEvent(&mWidget, mDev)
365             .move(0, mTouchPointPos, (QWidget *) nullptr)
366             .press(1, mTouchPointPos, (QWidget *) nullptr);
367 
368         QTest::touchEvent(&mWidget, mDev)
369             .move(0, mTouchPointPos, (QWidget *) nullptr)
370             .release(1, mTouchPointPos, (QWidget *) nullptr);
371 
372         QTest::touchEvent(&mWidget, mDev)
373             .release(1, mTouchPointPos, (QWidget *) nullptr);
374 
375         if (mSpyTapStarted->count() == 0) {
376             QVERIFY(mSpyTapStarted->wait(1000));
377         }
378         QCOMPARE(mSpyTapStarted->count(), 1);
379         QCOMPARE(mSpyTapFinished->count(), 0);
380         QCOMPARE(mSpyTapCanceled->count(), 1);
381         QVERIFY(mSpyTapUpdated->count() >= 0);
382     }
383 
testFailingGesture_oneFinger()384     void testFailingGesture_oneFinger()
385     {
386         // Test a failing gesture where we use only one finger.
387         clearSignalSpys();
388         int tapRadius = mTwoFingerRec->tapRadius();
389         QPoint wiggleRoom = QPoint (tapRadius / 2, tapRadius / 2);
390 
391         QTest::touchEvent(&mWidget, mDev)
392             .press(0, mTouchPointPos, (QWidget *) nullptr);
393 
394         QTest::touchEvent(&mWidget, mDev)
395             .move(0, mTouchPointPos + wiggleRoom, (QWidget *) nullptr);
396 
397         QTest::touchEvent(&mWidget, mDev)
398             .move(0, mTouchPointPos, (QWidget *) nullptr);
399 
400         QTest::touchEvent(&mWidget, mDev)
401             .release(0, mTouchPointPos, (QWidget *) nullptr);
402 
403         QCOMPARE(mSpyTapStarted->count(), 0);
404         QCOMPARE(mSpyTapFinished->count(), 0);
405         QCOMPARE(mSpyTapCanceled->count(), 0);
406         QCOMPARE(mSpyTapUpdated->count(), 0);
407     }
testSomeRandomTaps()408     void testSomeRandomTaps()
409     {
410         for (int i = 0; i < 50; ++i) {
411             int v1 = rand() % 450;
412             int v2 = rand() % 450;
413             mTouchPointPos = QPoint(v1, v2);
414             testSuccessfulGesture();
415         }
416     }
417 };
418 
419 QTEST_MAIN(KTwoFingerSwipeTest)
420 
421 #include "ktwofingertaptest.moc"
422