/* * Copyright (C) 2014 Canonical, Ltd. * * 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; version 3. * * 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 #include #include #include #include #include #include using namespace UbuntuGestures; class TouchMemento { public: TouchMemento(const QTouchEvent *touchEvent); Qt::TouchPointStates touchPointStates; QList touchPoints; bool containsTouchWithId(int touchId) const; }; class DummyCandidate : public QQuickItem { Q_OBJECT public: bool event(QEvent *e) override; QSet ownedTouches; QSet lostTouches; QList unownedTouchEvents; }; class tst_TouchRegistry : public QObject { Q_OBJECT public: tst_TouchRegistry() : QObject(0) { } private Q_SLOTS: void initTestCase() {} // will be called before the first test function is executed void cleanupTestCase() {} // will be called after the last test function was executed. void init() {} // called right before each and every test function is executed void cleanup() {} // called right after each and every test function is executed void requestWithNoCandidates(); void lateCandidateRequestGetsNothing(); void lateCandidateGestOwnershipOnceEarlyCandidateQuits(); void dispatchesTouchEventsToCandidates(); void dispatchesTouchEventsToWatchers(); void keepDispatchingToWatchersAfterLastCandidateGivesUp(); void candidatesAndWatchers_1(); void candidatesAndWatchers_2(); void rejectingTouchfterItsEnd(); void removeOldUndecidedCandidates(); }; void tst_TouchRegistry::requestWithNoCandidates() { TouchRegistry touchRegistry; DummyCandidate candidate; { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointPressed); QTouchEvent touchEvent(QEvent::TouchBegin, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); touchRegistry.update(&touchEvent); } touchRegistry.requestTouchOwnership(0, &candidate); QVERIFY(candidate.ownedTouches.contains(0)); } void tst_TouchRegistry::lateCandidateRequestGetsNothing() { TouchRegistry touchRegistry; DummyCandidate earlyCandidate; earlyCandidate.setObjectName("early"); DummyCandidate lateCandidate; lateCandidate.setObjectName("late"); { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointPressed); QTouchEvent touchEvent(QEvent::TouchBegin, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); touchRegistry.update(&touchEvent); } touchRegistry.addCandidateOwnerForTouch(0, &earlyCandidate); touchRegistry.addCandidateOwnerForTouch(0, &lateCandidate); QVERIFY(earlyCandidate.ownedTouches.isEmpty()); QVERIFY(lateCandidate.ownedTouches.isEmpty()); touchRegistry.requestTouchOwnership(0, &lateCandidate); QVERIFY(earlyCandidate.ownedTouches.isEmpty()); QVERIFY(lateCandidate.ownedTouches.isEmpty()); touchRegistry.requestTouchOwnership(0, &earlyCandidate); QVERIFY(earlyCandidate.ownedTouches.contains(0)); QVERIFY(lateCandidate.ownedTouches.isEmpty()); } void tst_TouchRegistry::lateCandidateGestOwnershipOnceEarlyCandidateQuits() { TouchRegistry touchRegistry; DummyCandidate earlyCandidate; DummyCandidate lateCandidate; { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointPressed); QTouchEvent touchEvent(QEvent::TouchBegin, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); touchRegistry.update(&touchEvent); } touchRegistry.addCandidateOwnerForTouch(0, &earlyCandidate); touchRegistry.addCandidateOwnerForTouch(0, &lateCandidate); QVERIFY(earlyCandidate.ownedTouches.isEmpty()); QVERIFY(lateCandidate.ownedTouches.isEmpty()); touchRegistry.requestTouchOwnership(0, &lateCandidate); QVERIFY(earlyCandidate.ownedTouches.isEmpty()); QVERIFY(lateCandidate.ownedTouches.isEmpty()); touchRegistry.removeCandidateOwnerForTouch(0, &earlyCandidate); QVERIFY(earlyCandidate.ownedTouches.isEmpty()); QVERIFY(lateCandidate.ownedTouches.contains(0)); } void tst_TouchRegistry::dispatchesTouchEventsToCandidates() { TouchRegistry touchRegistry; QQuickItem rootItem; DummyCandidate candidate0; candidate0.setObjectName("0"); candidate0.setParentItem(&rootItem); candidate0.setX(1); candidate0.setY(2); DummyCandidate candidate1; candidate1.setObjectName("1"); candidate0.setParentItem(&rootItem); candidate1.setX(3); candidate1.setY(4); { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointPressed); touchPoints[0].setRect(QRect(10, 10, 0, 0)); QTouchEvent touchEvent(QEvent::TouchBegin, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); touchRegistry.update(&touchEvent); } touchRegistry.addCandidateOwnerForTouch(0, &candidate0); { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointMoved); touchPoints[0].setRect(QRect(11, 11, 0, 0)); touchPoints.append(QTouchEvent::TouchPoint(1)); touchPoints[1].setState(Qt::TouchPointPressed); touchPoints[1].setRect(QRect(20, 20, 0, 0)); QTouchEvent touchEvent(QEvent::TouchUpdate, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed | Qt::TouchPointMoved, touchPoints); touchRegistry.update(&touchEvent); } // candidate0 should have received an update on the touch he's interested on. QCOMPARE(candidate0.unownedTouchEvents.count(), 1); // make sure only touch 0 is there (i.e. no mention of touch 1) QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints.count(), 1); QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints[0].id(), 0); // Check that the points local coordinates have been mapped to candidate0's coordinate system. QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints[0].rect().x(), 11 - candidate0.x()); QCOMPARE(candidate0.unownedTouchEvents[0].touchPoints[0].rect().y(), 11 - candidate0.y()); touchRegistry.addCandidateOwnerForTouch(1, &candidate1); { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointMoved); touchPoints[0].setRect(QRect(12, 12, 0, 0)); touchPoints.append(QTouchEvent::TouchPoint(1)); touchPoints[1].setState(Qt::TouchPointMoved); touchPoints[1].setRect(QRect(21, 21, 0, 0)); QTouchEvent touchEvent(QEvent::TouchUpdate, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed | Qt::TouchPointMoved, touchPoints); touchRegistry.update(&touchEvent); } // candidate0 gets updates only for touch 0 and // candidate1 gets updates only for touch 1 QCOMPARE(candidate0.unownedTouchEvents.count(), 2); QCOMPARE(candidate0.unownedTouchEvents[1].touchPoints.count(), 1); QCOMPARE(candidate0.unownedTouchEvents[1].touchPoints[0].id(), 0); QCOMPARE(candidate1.unownedTouchEvents.count(), 1); QCOMPARE(candidate1.unownedTouchEvents[0].touchPoints.count(), 1); QCOMPARE(candidate1.unownedTouchEvents[0].touchPoints[0].id(), 1); } void tst_TouchRegistry::dispatchesTouchEventsToWatchers() { TouchRegistry touchRegistry; QQuickItem rootItem; DummyCandidate watcher; watcher.setObjectName("watcher"); watcher.setParentItem(&rootItem); watcher.setX(1); watcher.setY(2); { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointPressed); touchPoints[0].setRect(QRect(10, 10, 0, 0)); QTouchEvent touchEvent(QEvent::TouchBegin, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); touchRegistry.update(&touchEvent); } touchRegistry.addTouchWatcher(0, &watcher); { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointMoved); touchPoints[0].setRect(QRect(11, 11, 0, 0)); touchPoints.append(QTouchEvent::TouchPoint(1)); touchPoints[1].setState(Qt::TouchPointPressed); touchPoints[1].setRect(QRect(20, 20, 0, 0)); QTouchEvent touchEvent(QEvent::TouchUpdate, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed | Qt::TouchPointMoved, touchPoints); touchRegistry.update(&touchEvent); } // watcher should have received an update on the touch he's interested on. QCOMPARE(watcher.unownedTouchEvents.count(), 1); // make sure only touch 0 is there (i.e. no mention of touch 1) QCOMPARE(watcher.unownedTouchEvents[0].touchPoints.count(), 1); QCOMPARE(watcher.unownedTouchEvents[0].touchPoints[0].id(), 0); // Check that the points local coordinates have been mapped to watcher's coordinate system. QCOMPARE(watcher.unownedTouchEvents[0].touchPoints[0].rect().x(), 11 - watcher.x()); QCOMPARE(watcher.unownedTouchEvents[0].touchPoints[0].rect().y(), 11 - watcher.y()); { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointMoved); touchPoints[0].setRect(QRect(12, 12, 0, 0)); touchPoints.append(QTouchEvent::TouchPoint(1)); touchPoints[1].setState(Qt::TouchPointMoved); touchPoints[1].setRect(QRect(21, 21, 0, 0)); QTouchEvent touchEvent(QEvent::TouchUpdate, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed | Qt::TouchPointMoved, touchPoints); touchRegistry.update(&touchEvent); } // watcher gets updates only for touch 0 QCOMPARE(watcher.unownedTouchEvents.count(), 2); QCOMPARE(watcher.unownedTouchEvents[1].touchPoints.count(), 1); QCOMPARE(watcher.unownedTouchEvents[1].touchPoints[0].id(), 0); } void tst_TouchRegistry::keepDispatchingToWatchersAfterLastCandidateGivesUp() { TouchRegistry touchRegistry; DummyCandidate item; { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointPressed); QTouchEvent touchEvent(QEvent::TouchBegin, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); touchRegistry.update(&touchEvent); } touchRegistry.addCandidateOwnerForTouch(0, &item); { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointMoved); touchPoints.append(QTouchEvent::TouchPoint(1)); touchPoints[1].setState(Qt::TouchPointPressed); QTouchEvent touchEvent(QEvent::TouchUpdate, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed | Qt::TouchPointMoved, touchPoints); touchRegistry.update(&touchEvent); } QCOMPARE(item.unownedTouchEvents.count(), 1); QCOMPARE(item.unownedTouchEvents[0].touchPoints.count(), 1); QCOMPARE(item.unownedTouchEvents[0].touchPoints[0].id(), 0); item.unownedTouchEvents.clear(); touchRegistry.addTouchWatcher(1, &item); { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointMoved); touchPoints.append(QTouchEvent::TouchPoint(1)); touchPoints[1].setState(Qt::TouchPointMoved); QTouchEvent touchEvent(QEvent::TouchUpdate, 0 /* device */, Qt::NoModifier, Qt::TouchPointMoved, touchPoints); touchRegistry.update(&touchEvent); } QCOMPARE(item.unownedTouchEvents.count(), 1); QCOMPARE(item.unownedTouchEvents[0].touchPoints.count(), 2); QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(0)); QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(1)); item.unownedTouchEvents.clear(); { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointReleased); touchPoints.append(QTouchEvent::TouchPoint(1)); touchPoints[1].setState(Qt::TouchPointMoved); QTouchEvent touchEvent(QEvent::TouchUpdate, 0 /* device */, Qt::NoModifier, Qt::TouchPointReleased | Qt::TouchPointMoved, touchPoints); touchRegistry.update(&touchEvent); } QCOMPARE(item.unownedTouchEvents.count(), 1); QCOMPARE(item.unownedTouchEvents[0].touchPoints.count(), 2); QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(0)); QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(1)); item.unownedTouchEvents.clear(); touchRegistry.removeCandidateOwnerForTouch(0, &item); { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(1)); touchPoints[0].setState(Qt::TouchPointReleased); QTouchEvent touchEvent(QEvent::TouchEnd, 0 /* device */, Qt::NoModifier, Qt::TouchPointReleased, touchPoints); touchRegistry.update(&touchEvent); } QCOMPARE(item.unownedTouchEvents.count(), 1); QCOMPARE(item.unownedTouchEvents[0].touchPoints.count(), 1); QVERIFY(item.unownedTouchEvents[0].containsTouchWithId(1)); item.unownedTouchEvents.clear(); QVERIFY(touchRegistry.m_touchInfoPool.isEmpty()); } /* Regression test that reproduces a problematic scenario that came up during manual testing. It reproduces the interaction between TouchRegistry and a DirectionalDragArea */ void tst_TouchRegistry::candidatesAndWatchers_1() { TouchRegistry touchRegistry; DummyCandidate item; { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointPressed); QTouchEvent touchEvent(QEvent::TouchBegin, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); touchRegistry.update(&touchEvent); } touchRegistry.addCandidateOwnerForTouch(0, &item); { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointMoved); touchPoints.append(QTouchEvent::TouchPoint(1)); touchPoints[1].setState(Qt::TouchPointPressed); QTouchEvent touchEvent(QEvent::TouchUpdate, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed | Qt::TouchPointMoved, touchPoints); touchRegistry.update(&touchEvent); } touchRegistry.addTouchWatcher(1, &item); touchRegistry.removeCandidateOwnerForTouch(0, &item); touchRegistry.addTouchWatcher(0, &item); { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointReleased); touchPoints.append(QTouchEvent::TouchPoint(1)); touchPoints[1].setState(Qt::TouchPointMoved); QTouchEvent touchEvent(QEvent::TouchUpdate, 0 /* device */, Qt::NoModifier, Qt::TouchPointReleased | Qt::TouchPointMoved, touchPoints); touchRegistry.update(&touchEvent); } { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(1)); touchPoints[0].setState(Qt::TouchPointReleased); QTouchEvent touchEvent(QEvent::TouchEnd, 0 /* device */, Qt::NoModifier, Qt::TouchPointReleased, touchPoints); touchRegistry.update(&touchEvent); } QVERIFY(touchRegistry.m_touchInfoPool.isEmpty()); item.unownedTouchEvents.clear(); { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointPressed); QTouchEvent touchEvent(QEvent::TouchBegin, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); touchRegistry.update(&touchEvent); } // Haven't made any subscription for that new touch 0. QCOMPARE(item.unownedTouchEvents.count(), 0); } /* Regression test that reproduces a problematic scenario that came up during manual testing. It reproduces the interaction between TouchRegistry, DirectionalDragArea and a TouchGate. */ void tst_TouchRegistry::candidatesAndWatchers_2() { TouchRegistry touchRegistry; DummyCandidate directionalDragArea; directionalDragArea.setObjectName("DirectionalDragArea"); DummyCandidate touchGate; touchGate.setObjectName("TouchGate"); // [DDA] 1298 TouchBegin (id:0, state:pressed, scenePos:(147,1023)) { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointPressed); QTouchEvent touchEvent(QEvent::TouchBegin, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); touchRegistry.update(&touchEvent); } // [TouchRegistry] addCandidateOwnerForTouch id 0 candidate DirectionalDragArea touchRegistry.addCandidateOwnerForTouch(0, &directionalDragArea); // [TouchRegistry] requestTouchOwnership id 0 candidate TouchGate touchRegistry.requestTouchOwnership(0, &touchGate); //[TouchRegistry] got TouchUpdate (id:0, state:moved, scenePos:(147,1023)) (id:1, state:pressed, scenePos:(327,792)) { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointMoved); touchPoints.append(QTouchEvent::TouchPoint(1)); touchPoints[1].setState(Qt::TouchPointPressed); QTouchEvent touchEvent(QEvent::TouchUpdate, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed | Qt::TouchPointMoved, touchPoints); touchRegistry.update(&touchEvent); } // [TouchRegistry] addTouchWatcher id 1 watcher DirectionalDragArea touchRegistry.addTouchWatcher(1, &directionalDragArea); // [TouchRegistry] removeCandidateOwnerForTouch id 0 candidate DirectionalDragArea touchRegistry.removeCandidateOwnerForTouch(0, &directionalDragArea); //[TouchRegistry] sending TouchWonershipEvent(id = 0 gained) to candidate TouchGate QCOMPARE(touchGate.ownedTouches.count(), 1); QVERIFY(touchGate.ownedTouches.contains(0)); // [TouchRegistry] addTouchWatcher id 0 watcher DirectionalDragArea touchRegistry.addTouchWatcher(0, &directionalDragArea); // [TouchRegistry] requestTouchOwnership id 1 candidate TouchGate touchRegistry.requestTouchOwnership(1, &touchGate); //[TouchRegistry] sending TouchWonershipEvent(id = 1 gained) to candidate TouchGate QCOMPARE(touchGate.ownedTouches.count(), 2); QVERIFY(touchGate.ownedTouches.contains(1)); directionalDragArea.unownedTouchEvents.clear(); touchGate.unownedTouchEvents.clear(); //[TouchRegistry] got TouchUpdate (id:0, state:moved, scenePos:(148,1025)) (id:1, state:moved, scenePos:(329,795)) { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointMoved); touchPoints.append(QTouchEvent::TouchPoint(1)); touchPoints[1].setState(Qt::TouchPointMoved); QTouchEvent touchEvent(QEvent::TouchUpdate, 0 /* device */, Qt::NoModifier, Qt::TouchPointMoved, touchPoints); touchRegistry.update(&touchEvent); } //vvvvv DDA Watchers are being ignored from now on vvvvvvv QCOMPARE(directionalDragArea.unownedTouchEvents.count(), 1); QCOMPARE(directionalDragArea.unownedTouchEvents.first().touchPoints.count(), 2); QVERIFY(directionalDragArea.unownedTouchEvents.first().containsTouchWithId(0)); QVERIFY(directionalDragArea.unownedTouchEvents.first().containsTouchWithId(1)); QVERIFY(touchGate.unownedTouchEvents.isEmpty()); directionalDragArea.unownedTouchEvents.clear(); //[TouchRegistry] got TouchUpdate (id:0, state:moved, scenePos:(151,1029)) (id:1, state:released, scenePos:(329,795)) { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointMoved); touchPoints.append(QTouchEvent::TouchPoint(1)); touchPoints[1].setState(Qt::TouchPointReleased); QTouchEvent touchEvent(QEvent::TouchUpdate, 0 /* device */, Qt::NoModifier, Qt::TouchPointMoved | Qt::TouchPointReleased, touchPoints); touchRegistry.update(&touchEvent); } QCOMPARE(directionalDragArea.unownedTouchEvents.count(), 1); QCOMPARE(directionalDragArea.unownedTouchEvents.first().touchPoints.count(), 2); QVERIFY(directionalDragArea.unownedTouchEvents.first().containsTouchWithId(0)); QVERIFY(directionalDragArea.unownedTouchEvents.first().containsTouchWithId(1)); QVERIFY(touchGate.unownedTouchEvents.isEmpty()); directionalDragArea.unownedTouchEvents.clear(); //[TouchRegistry] got TouchEnd (id:0, state:released, scenePos:(157,1034)) { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointReleased); QTouchEvent touchEvent(QEvent::TouchEnd, 0 /* device */, Qt::NoModifier, Qt::TouchPointReleased, touchPoints); touchRegistry.update(&touchEvent); } QCOMPARE(directionalDragArea.unownedTouchEvents.count(), 1); QCOMPARE(directionalDragArea.unownedTouchEvents.first().touchPoints.count(), 1); QVERIFY(directionalDragArea.unownedTouchEvents.first().containsTouchWithId(0)); QVERIFY(touchGate.unownedTouchEvents.isEmpty()); } void tst_TouchRegistry::rejectingTouchfterItsEnd() { TouchRegistry touchRegistry; DummyCandidate earlyCandidate; DummyCandidate lateCandidate; { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointPressed); QTouchEvent touchEvent(QEvent::TouchBegin, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); touchRegistry.update(&touchEvent); } touchRegistry.addCandidateOwnerForTouch(0, &earlyCandidate); touchRegistry.addCandidateOwnerForTouch(0, &lateCandidate); QVERIFY(earlyCandidate.ownedTouches.isEmpty()); QVERIFY(lateCandidate.ownedTouches.isEmpty()); touchRegistry.requestTouchOwnership(0, &lateCandidate); QVERIFY(earlyCandidate.ownedTouches.isEmpty()); QVERIFY(lateCandidate.ownedTouches.isEmpty()); { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointReleased); QTouchEvent touchEvent(QEvent::TouchEnd, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); touchRegistry.update(&touchEvent); } QVERIFY(earlyCandidate.ownedTouches.isEmpty()); QVERIFY(lateCandidate.ownedTouches.isEmpty()); touchRegistry.removeCandidateOwnerForTouch(0, &earlyCandidate); QCOMPARE(lateCandidate.ownedTouches.count(), 1); QCOMPARE(lateCandidate.ownedTouches.contains(0), 1); // Check that there's no trace left of touch 0 as we no longer need to keep tabs on it. QVERIFY(!touchRegistry.findTouchInfo(0)); } void tst_TouchRegistry::removeOldUndecidedCandidates() { FakeTimerFactory *fakeTimerFactory = new FakeTimerFactory; TouchRegistry *touchRegistry = new TouchRegistry(nullptr, fakeTimerFactory); DummyCandidate undecidedCandidate; undecidedCandidate.setObjectName("undecided"); DummyCandidate candidateThatWantsTouch; candidateThatWantsTouch.setObjectName("wantsTouch"); { QList touchPoints; touchPoints.append(QTouchEvent::TouchPoint(0)); touchPoints[0].setState(Qt::TouchPointPressed); QTouchEvent touchEvent(QEvent::TouchBegin, 0 /* device */, Qt::NoModifier, Qt::TouchPointPressed, touchPoints); touchRegistry->update(&touchEvent); } touchRegistry->addCandidateOwnerForTouch(0, &undecidedCandidate); touchRegistry->requestTouchOwnership(0, &candidateThatWantsTouch); QVERIFY(undecidedCandidate.ownedTouches.isEmpty()); QVERIFY(undecidedCandidate.lostTouches.isEmpty()); QVERIFY(candidateThatWantsTouch.ownedTouches.isEmpty()); QVERIFY(candidateThatWantsTouch.lostTouches.isEmpty()); // Simulate that enough time has passed to cause the CandidateInactivityTimer to timeout, // making TouchRegistry consider that undecidedCantidate defaulted. fakeTimerFactory->makeRunningTimersTimeout(); QVERIFY(undecidedCandidate.ownedTouches.isEmpty()); QVERIFY(undecidedCandidate.lostTouches.contains(0)); QVERIFY(candidateThatWantsTouch.ownedTouches.contains(0)); QVERIFY(candidateThatWantsTouch.lostTouches.isEmpty()); delete touchRegistry; } ////////////// TouchMemento ////////// TouchMemento::TouchMemento(const QTouchEvent *touchEvent) : touchPointStates(touchEvent->touchPointStates()), touchPoints(touchEvent->touchPoints()) { } bool TouchMemento::containsTouchWithId(int touchId) const { for (int i = 0; i < touchPoints.count(); ++i) { if (touchPoints.at(i).id() == touchId) { return true; } } return false; } ////////////// DummyCandidate ////////// bool DummyCandidate::event(QEvent *e) { if (e->type() == TouchOwnershipEvent::touchOwnershipEventType()) { TouchOwnershipEvent *touchOwnershipEvent = static_cast(e); // NB: Cannot use QVERIFY here because the macro doesn't return a boolean and is // meant for use only directly in the body of a test function if (ownedTouches.contains(touchOwnershipEvent->touchId())) qFatal("Sent ownership event for a touch that is already owned."); if (lostTouches.contains(touchOwnershipEvent->touchId())) qFatal("Sent ownership event for a touch that has already been lost."); if (touchOwnershipEvent->gained()) { ownedTouches.insert(touchOwnershipEvent->touchId()); } else { lostTouches.insert(touchOwnershipEvent->touchId()); } return true; } else if (e->type() == UnownedTouchEvent::unownedTouchEventType()) { UnownedTouchEvent *unownedTouchEvent = static_cast(e); unownedTouchEvents.append(TouchMemento(unownedTouchEvent->touchEvent())); return true; } else { return QObject::event(e); } } QTEST_GUILESS_MAIN(tst_TouchRegistry) #include "tst_TouchRegistry.moc"