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