on dc3a20d help sign in
unity8/tests/plugins/Ubuntu/Gestures/tst_TouchGate.cpp
392 lines 12 kB history raw
1/*
2 * Copyright (C) 2014-2015 Canonical, Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
17#include <qpa/qwindowsysteminterface.h>
18#include <QtTest>
19#include <QQmlEngine>
20#include <QQuickView>
21#include <QSharedPointer>
22#include <private/qquickanimatorcontroller_p.h>
23#include <private/qquickwindow_p.h>
25// C++ std lib
26#include <functional>
28#include <TouchGate.h>
29#include <UbuntuGestures/private/touchregistry_p.h>
31#include "TestItem.h"
33#include <paths.h>
35UG_USE_NAMESPACE
37class CandidateItem : public QQuickItem
39 Q_OBJECT
41public:
42 std::function<void(QTouchEvent*)> touchEventHandler;
44protected:
45 void touchEvent(QTouchEvent *event) override;
46};
48class tst_TouchGate : public QObject
50 Q_OBJECT
51public:
52 tst_TouchGate(QObject *parent = nullptr);
53private Q_SLOTS:
54 void initTestCase(); // will be called before the first test function is executed
55 void cleanupTestCase(); // will be called after the last test function was executed.
57 void init(); // called right before each and every test function is executed
58 void cleanup(); // called right after each and every test function is executed
60private:
61 void onlyCandidate_passThrough();
62 void holdsEventsUntilGainsOwnership();
63 void holdsEventsUntilGainsOwnership_data();
64 void holdsEventsUntilGainsOwnership_repeatingTouchId();
65 void dontDispacthToDisabledOrInvisibleTarget();
66 void dontDispacthToDisabledOrInvisibleTarget_data();
68private Q_SLOTS:
69 void disabledWhileHoldingTouch();
71private:
72 QQuickView *createView();
73 TouchRegistry *touchRegistry;
74 QQuickView *view;
75 QTouchDevice *device;
76};
78tst_TouchGate::tst_TouchGate(QObject *parent)
79 : QObject(parent)
80 , touchRegistry(nullptr)
81 , view(nullptr)
82 , device(nullptr)
86void tst_TouchGate::initTestCase()
88 if (!device) {
89 device = new QTouchDevice;
90 device->setType(QTouchDevice::TouchScreen);
91 QWindowSystemInterface::registerTouchDevice(device);
92 }
95void tst_TouchGate::cleanupTestCase()
99void tst_TouchGate::init()
101 touchRegistry = TouchRegistry::instance();
103 view = createView();
104 view->setSource(QUrl::fromLocalFile(testDataDir() + "/plugins/Ubuntu/Gestures/touchGateExample.qml"));
105 view->show();
106 QVERIFY(QTest::qWaitForWindowExposed(view));
107 QVERIFY(view->rootObject() != 0);
108 qApp->processEvents();
111void tst_TouchGate::cleanup()
113 delete view;
114 view = 0;
116 delete touchRegistry;
117 touchRegistry = nullptr;
120QQuickView *tst_TouchGate::createView()
122 QQuickView *window = new QQuickView(0);
123 window->setResizeMode(QQuickView::SizeRootObjectToView);
124 window->resize(720, 720);
126 return window;
129/*
130 Simplest case. TouchGate is the only candidate for the touch point, thus it
131 will instantly gain ownership causing the event to pass through it directly to
132 its child.
133 */
134void tst_TouchGate::onlyCandidate_passThrough()
136 TouchGate *touchGate = view->rootObject()->findChild<TouchGate*>("touchGate");
137 Q_ASSERT(touchGate);
139 TestItem *testItem = new TestItem;
140 testItem->setWidth(touchGate->width());
141 testItem->setHeight(touchGate->height());
142 testItem->setParentItem(view->rootObject());
143 testItem->setZ(0.0);
145 touchGate->setZ(1.0);
146 touchGate->setTargetItem(testItem);
148 QTest::touchEvent(view, device)
149 .press(0, QPoint(100,100));
151 QCOMPARE(testItem->touchEventsReceived.count(), 1);
152 QVERIFY(touchGate->m_storedEvents.isEmpty());
155void tst_TouchGate::holdsEventsUntilGainsOwnership()
157 TouchGate *touchGate = view->rootObject()->findChild<TouchGate*>("touchGate");
158 Q_ASSERT(touchGate);
159 QFETCH(bool, ownershipAfterTouchEnd);
161 TestItem *testItem = new TestItem;
162 testItem->setWidth(touchGate->width());
163 testItem->setHeight(touchGate->height());
164 testItem->setParentItem(view->rootObject());
165 testItem->setZ(0.0);
167 touchGate->setZ(1.0);
168 touchGate->setTargetItem(testItem);
170 // Put it in front of touchGate
171 CandidateItem *candidateItem = new CandidateItem;
172 candidateItem->setWidth(touchGate->width());
173 candidateItem->setHeight(touchGate->height());
174 candidateItem->setParentItem(view->rootObject());
175 candidateItem->setZ(2.0);
177 candidateItem->touchEventHandler = [&](QTouchEvent* event) {
178 touchRegistry->addCandidateOwnerForTouch(0, candidateItem);
179 event->ignore();
180 };
182 QTest::touchEvent(view, device)
183 .press(0, QPoint(100,100));
185 QCOMPARE(testItem->touchEventsReceived.count(), 0);
187 QTest::touchEvent(view, device)
188 .move(0, QPoint(101,101));
190 QCOMPARE(testItem->touchEventsReceived.count(), 0);
192 if (!ownershipAfterTouchEnd) {
193 touchRegistry->removeCandidateOwnerForTouch(0, candidateItem);
194 QQuickWindowPrivate *wp = QQuickWindowPrivate::get(testItem->window());
195 if (wp->delayedTouch) {
196 wp->deliverDelayedTouchEvent();
198 // Touch events which constantly start animations (such as a behavior tracking
199 // the mouse point) need animations to start.
200 QQmlAnimationTimer *ut = QQmlAnimationTimer::instance();
201 if (ut && ut->hasStartAnimationPending())
202 ut->startAnimations();
203 }
204 // TouchGate should now open its flood gates and let testItem get all
205 // events from touch 0 produced so far
206 QCOMPARE(testItem->touchEventsReceived.count(), 2);
207 }
209 QTest::touchEvent(view, device)
210 .release(0, QPoint(101,101));
212 if (ownershipAfterTouchEnd) {
213 QCOMPARE(testItem->touchEventsReceived.count(), 0);
214 touchRegistry->removeCandidateOwnerForTouch(0, candidateItem);
215 // TouchGate should now open its flood gates and let testItem get all
216 // events from touch 0 produced so far
217 }
219 QCOMPARE(testItem->touchEventsReceived.count(), 3);
221 QVERIFY(touchGate->m_storedEvents.isEmpty());
222 QVERIFY(touchGate->m_touchInfoMap.isEmpty());
225void tst_TouchGate::holdsEventsUntilGainsOwnership_data()
227 QTest::addColumn<bool>("ownershipAfterTouchEnd");
229 QTest::newRow("touch end after touch ownership") << false;
230 QTest::newRow("touch ownership after touch end") << true;
233/*
234 A touch point of id 0 starts and ends. Then the next touch point is also given
235 id 0. Check that TouchGate behaves correcly recognizing them as two distinct touch
236 points.
238 Note that this won't be a problem with Mir as it assigns unique ids for the touch
239 points it produces.
240 */
241void tst_TouchGate::holdsEventsUntilGainsOwnership_repeatingTouchId()
243 TouchGate *touchGate = view->rootObject()->findChild<TouchGate*>("touchGate");
244 Q_ASSERT(touchGate);
246 TestItem *testItem = new TestItem;
247 testItem->setWidth(touchGate->width());
248 testItem->setHeight(touchGate->height());
249 testItem->setParentItem(view->rootObject());
250 testItem->setZ(0.0);
252 touchGate->setZ(1.0);
253 touchGate->setTargetItem(testItem);
255 QTest::touchEvent(view, device)
256 .press(0, QPoint(100,100));
257 QTest::touchEvent(view, device)
258 .move(0, QPoint(101,101));
259 QTest::touchEvent(view, device)
260 .release(0, QPoint(101,101));
262 QCOMPARE(testItem->touchEventsReceived.count(), 3);
263 testItem->touchEventsReceived.clear();
265 // Put it in front of touchGate
266 CandidateItem *candidateItem = new CandidateItem;
267 candidateItem->setWidth(touchGate->width());
268 candidateItem->setHeight(touchGate->height());
269 candidateItem->setParentItem(view->rootObject());
270 candidateItem->setZ(2.0);
272 candidateItem->touchEventHandler = [&](QTouchEvent* event) {
273 touchRegistry->addCandidateOwnerForTouch(0, candidateItem);
274 event->ignore();
275 };
277 // A brand new touch, but with the same id as the previous one
278 QTest::touchEvent(view, device)
279 .press(0, QPoint(200,200));
280 QTest::touchEvent(view, device)
281 .move(0, QPoint(201,201));
282 QTest::touchEvent(view, device)
283 .release(0, QPoint(201,201));
285 QCOMPARE(testItem->touchEventsReceived.count(), 0);
287 touchRegistry->requestTouchOwnership(0, candidateItem);
289 QCOMPARE(testItem->touchEventsReceived.count(), 0);
291 QVERIFY(touchGate->m_storedEvents.isEmpty());
294void tst_TouchGate::dontDispacthToDisabledOrInvisibleTarget()
296 TouchGate *touchGate = view->rootObject()->findChild<TouchGate*>("touchGate");
297 Q_ASSERT(touchGate);
299 TestItem *testItem = new TestItem;
300 testItem->setWidth(touchGate->width());
301 testItem->setHeight(touchGate->height());
302 testItem->setParentItem(view->rootObject());
303 testItem->setZ(0.0);
305 QFETCH(bool, isEnabled);
306 testItem->setEnabled(isEnabled);
308 QFETCH(bool, isVisible);
309 testItem->setVisible(isVisible);
311 touchGate->setZ(1.0);
312 touchGate->setTargetItem(testItem);
314 QTest::touchEvent(view, device)
315 .press(0, QPoint(100,100));
317 QCOMPARE(testItem->touchEventsReceived.count(), 0);
318 QVERIFY(touchGate->m_storedEvents.isEmpty());
321void tst_TouchGate::dontDispacthToDisabledOrInvisibleTarget_data()
323 QTest::addColumn<bool>("isEnabled");
324 QTest::addColumn<bool>("isVisible");
326 QTest::newRow("disabled visible") << false << true;
327 QTest::newRow("enabled invisible") << true << false;
328 QTest::newRow("disabled invisible") << false << false;
331void tst_TouchGate::disabledWhileHoldingTouch()
333 TouchGate *touchGate = view->rootObject()->findChild<TouchGate*>("touchGate");
334 Q_ASSERT(touchGate);
336 TestItem *testItem = new TestItem;
337 testItem->setWidth(touchGate->width());
338 testItem->setHeight(touchGate->height());
339 testItem->setParentItem(view->rootObject());
340 testItem->setZ(0.0);
342 touchGate->setZ(1.0);
343 touchGate->setTargetItem(testItem);
345 QTest::touchEvent(view, device)
346 .press(0, QPoint(100,100));
348 QCOMPARE(testItem->touchEventsReceived.count(), 1);
349 testItem->touchEventsReceived.clear();
351 touchGate->setEnabled(false);
353 QTest::touchEvent(view, device)
354 .move(0, QPoint(101,101));
356 // Nothing new came as TouchGate didn't even get it because it's disabled
357 QCOMPARE(testItem->touchEventsReceived.count(), 0);
359 touchGate->setEnabled(true);
361 QTest::touchEvent(view, device)
362 .move(0, QPoint(102,102));
364 QTest::touchEvent(view, device)
365 .release(0, QPoint(102,102));
367 // Nothing new came because TouchGate has already discarded touch 0
368 QCOMPARE(testItem->touchEventsReceived.count(), 0);
370 QTest::touchEvent(view, device)
371 .press(1, QPoint(200,200));
373 QCOMPARE(testItem->touchEventsReceived.count(), 1);
374 {
375 // it got only the new touch point.
376 QSharedPointer<QTouchEvent> touchEvent = testItem->touchEventsReceived[0];
377 QCOMPARE(touchEvent->touchPoints().count(), 1);
378 QCOMPARE(touchEvent->touchPoints()[0].pos(), QPointF(200, 200));
379 }
382///////////// CandidateItem /////////////////////////////////////////////////////////////
384void CandidateItem::touchEvent(QTouchEvent *event)
386 touchEventHandler(event);
390QTEST_MAIN(tst_TouchGate)
392#include "tst_TouchGate.moc"