on c8b50e3 help sign in
unity8/tests/plugins/Ubuntu/Gestures/tst_TouchGate.cpp
406 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#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
23 #include <private/qquickwindow_p.h>
24#endif
26// C++ std lib
27#include <functional>
29#include <TouchGate.h>
30#include <TouchRegistry.h>
32class TestItem : public QQuickItem
34 Q_OBJECT
36public:
37 QList<QSharedPointer<QTouchEvent>> touchEventsReceived;
39protected:
40 void touchEvent(QTouchEvent *event) override;
41};
43class CandidateItem : public QQuickItem
45 Q_OBJECT
47public:
48 std::function<void(QTouchEvent*)> touchEventHandler;
50protected:
51 void touchEvent(QTouchEvent *event) override;
52};
54class tst_TouchGate : public QObject
56 Q_OBJECT
57public:
58 tst_TouchGate(QObject *parent = nullptr);
59private Q_SLOTS:
60 void initTestCase(); // will be called before the first test function is executed
61 void cleanupTestCase(); // will be called after the last test function was executed.
63 void init(); // called right before each and every test function is executed
64 void cleanup(); // called right after each and every test function is executed
66private:
67 void onlyCandidate_passThrough();
68 void holdsEventsUntilGainsOwnership();
69 void holdsEventsUntilGainsOwnership_data();
70 void holdsEventsUntilGainsOwnership_repeatingTouchId();
71 void dontDispacthToDisabledOrInvisibleTarget();
72 void dontDispacthToDisabledOrInvisibleTarget_data();
74private Q_SLOTS:
75 void disabledWhileHoldingTouch();
77private:
78 QQuickView *createView();
79 TouchRegistry *touchRegistry;
80 QQuickView *view;
81 QTouchDevice *device;
82};
84tst_TouchGate::tst_TouchGate(QObject *parent)
85 : QObject(parent)
86 , touchRegistry(nullptr)
87 , view(nullptr)
88 , device(nullptr)
92void tst_TouchGate::initTestCase()
94 if (!device) {
95 device = new QTouchDevice;
96 device->setType(QTouchDevice::TouchScreen);
97 QWindowSystemInterface::registerTouchDevice(device);
98 }
101void tst_TouchGate::cleanupTestCase()
105void tst_TouchGate::init()
107 touchRegistry = new TouchRegistry;
109 view = createView();
110 view->installEventFilter(touchRegistry);
111 view->setSource(QUrl::fromLocalFile("touchGateExample.qml"));
112 view->show();
113 QVERIFY(QTest::qWaitForWindowExposed(view));
114 QVERIFY(view->rootObject() != 0);
115 qApp->processEvents();
118void tst_TouchGate::cleanup()
120 delete view;
121 view = 0;
123 delete touchRegistry;
124 touchRegistry = nullptr;
127QQuickView *tst_TouchGate::createView()
129 QQuickView *window = new QQuickView(0);
130 window->setResizeMode(QQuickView::SizeRootObjectToView);
131 window->resize(720, 720);
133 return window;
136/*
137 Simplest case. TouchGate is the only candidate for the touch point, thus it
138 will instantly gain ownership causing the event to pass through it directly to
139 its child.
140 */
141void tst_TouchGate::onlyCandidate_passThrough()
143 TouchGate *touchGate = view->rootObject()->findChild<TouchGate*>("touchGate");
144 Q_ASSERT(touchGate);
146 TestItem *testItem = new TestItem;
147 testItem->setWidth(touchGate->width());
148 testItem->setHeight(touchGate->height());
149 testItem->setParentItem(view->rootObject());
150 testItem->setZ(0.0);
152 touchGate->setZ(1.0);
153 touchGate->setTargetItem(testItem);
155 QTest::touchEvent(view, device)
156 .press(0, QPoint(100,100));
158 QCOMPARE(testItem->touchEventsReceived.count(), 1);
159 QVERIFY(touchGate->m_storedEvents.isEmpty());
162void tst_TouchGate::holdsEventsUntilGainsOwnership()
164 TouchGate *touchGate = view->rootObject()->findChild<TouchGate*>("touchGate");
165 Q_ASSERT(touchGate);
166 QFETCH(bool, ownershipAfterTouchEnd);
168 TestItem *testItem = new TestItem;
169 testItem->setWidth(touchGate->width());
170 testItem->setHeight(touchGate->height());
171 testItem->setParentItem(view->rootObject());
172 testItem->setZ(0.0);
174 touchGate->setZ(1.0);
175 touchGate->setTargetItem(testItem);
177 // Put it in front of touchGate
178 CandidateItem *candidateItem = new CandidateItem;
179 candidateItem->setWidth(touchGate->width());
180 candidateItem->setHeight(touchGate->height());
181 candidateItem->setParentItem(view->rootObject());
182 candidateItem->setZ(2.0);
184 candidateItem->touchEventHandler = [&](QTouchEvent* event) {
185 touchRegistry->addCandidateOwnerForTouch(0, candidateItem);
186 event->ignore();
187 };
189 QTest::touchEvent(view, device)
190 .press(0, QPoint(100,100));
192 QCOMPARE(testItem->touchEventsReceived.count(), 0);
194 QTest::touchEvent(view, device)
195 .move(0, QPoint(101,101));
197 QCOMPARE(testItem->touchEventsReceived.count(), 0);
199 if (!ownershipAfterTouchEnd) {
200 touchRegistry->removeCandidateOwnerForTouch(0, candidateItem);
201#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
202 QQuickWindowPrivate *wp = QQuickWindowPrivate::get(testItem->window());
203 wp->flushDelayedTouchEvent();
204#endif
205 // TouchGate should now open its flood gates and let testItem get all
206 // events from touch 0 produced so far
207 QCOMPARE(testItem->touchEventsReceived.count(), 2);
208 }
210 QTest::touchEvent(view, device)
211 .release(0, QPoint(101,101));
213 if (ownershipAfterTouchEnd) {
214 QCOMPARE(testItem->touchEventsReceived.count(), 0);
215 touchRegistry->removeCandidateOwnerForTouch(0, candidateItem);
216 // TouchGate should now open its flood gates and let testItem get all
217 // events from touch 0 produced so far
218 }
220 QCOMPARE(testItem->touchEventsReceived.count(), 3);
222 QVERIFY(touchGate->m_storedEvents.isEmpty());
223 QVERIFY(touchGate->m_touchInfoMap.isEmpty());
226void tst_TouchGate::holdsEventsUntilGainsOwnership_data()
228 QTest::addColumn<bool>("ownershipAfterTouchEnd");
230 QTest::newRow("touch end after touch ownership") << false;
231 QTest::newRow("touch ownership after touch end") << true;
234/*
235 A touch point of id 0 starts and ends. Then the next touch point is also given
236 id 0. Check that TouchGate behaves correcly recognizing them as two distinct touch
237 points.
239 Note that this won't be a problem with Mir as it assigns unique ids for the touch
240 points it produces.
241 */
242void tst_TouchGate::holdsEventsUntilGainsOwnership_repeatingTouchId()
244 TouchGate *touchGate = view->rootObject()->findChild<TouchGate*>("touchGate");
245 Q_ASSERT(touchGate);
247 TestItem *testItem = new TestItem;
248 testItem->setWidth(touchGate->width());
249 testItem->setHeight(touchGate->height());
250 testItem->setParentItem(view->rootObject());
251 testItem->setZ(0.0);
253 touchGate->setZ(1.0);
254 touchGate->setTargetItem(testItem);
256 QTest::touchEvent(view, device)
257 .press(0, QPoint(100,100));
258 QTest::touchEvent(view, device)
259 .move(0, QPoint(101,101));
260 QTest::touchEvent(view, device)
261 .release(0, QPoint(101,101));
263 QCOMPARE(testItem->touchEventsReceived.count(), 3);
264 testItem->touchEventsReceived.clear();
266 // Put it in front of touchGate
267 CandidateItem *candidateItem = new CandidateItem;
268 candidateItem->setWidth(touchGate->width());
269 candidateItem->setHeight(touchGate->height());
270 candidateItem->setParentItem(view->rootObject());
271 candidateItem->setZ(2.0);
273 candidateItem->touchEventHandler = [&](QTouchEvent* event) {
274 touchRegistry->addCandidateOwnerForTouch(0, candidateItem);
275 event->ignore();
276 };
278 // A brand new touch, but with the same id as the previous one
279 QTest::touchEvent(view, device)
280 .press(0, QPoint(200,200));
281 QTest::touchEvent(view, device)
282 .move(0, QPoint(201,201));
283 QTest::touchEvent(view, device)
284 .release(0, QPoint(201,201));
286 QCOMPARE(testItem->touchEventsReceived.count(), 0);
288 touchRegistry->requestTouchOwnership(0, candidateItem);
290 QCOMPARE(testItem->touchEventsReceived.count(), 0);
292 QVERIFY(touchGate->m_storedEvents.isEmpty());
295void tst_TouchGate::dontDispacthToDisabledOrInvisibleTarget()
297 TouchGate *touchGate = view->rootObject()->findChild<TouchGate*>("touchGate");
298 Q_ASSERT(touchGate);
300 TestItem *testItem = new TestItem;
301 testItem->setWidth(touchGate->width());
302 testItem->setHeight(touchGate->height());
303 testItem->setParentItem(view->rootObject());
304 testItem->setZ(0.0);
306 QFETCH(bool, isEnabled);
307 testItem->setEnabled(isEnabled);
309 QFETCH(bool, isVisible);
310 testItem->setVisible(isVisible);
312 touchGate->setZ(1.0);
313 touchGate->setTargetItem(testItem);
315 QTest::touchEvent(view, device)
316 .press(0, QPoint(100,100));
318 QCOMPARE(testItem->touchEventsReceived.count(), 0);
319 QVERIFY(touchGate->m_storedEvents.isEmpty());
322void tst_TouchGate::dontDispacthToDisabledOrInvisibleTarget_data()
324 QTest::addColumn<bool>("isEnabled");
325 QTest::addColumn<bool>("isVisible");
327 QTest::newRow("disabled visible") << false << true;
328 QTest::newRow("enabled invisible") << true << false;
329 QTest::newRow("disabled invisible") << false << false;
332void tst_TouchGate::disabledWhileHoldingTouch()
334 TouchGate *touchGate = view->rootObject()->findChild<TouchGate*>("touchGate");
335 Q_ASSERT(touchGate);
337 TestItem *testItem = new TestItem;
338 testItem->setWidth(touchGate->width());
339 testItem->setHeight(touchGate->height());
340 testItem->setParentItem(view->rootObject());
341 testItem->setZ(0.0);
343 touchGate->setZ(1.0);
344 touchGate->setTargetItem(testItem);
346 QTest::touchEvent(view, device)
347 .press(0, QPoint(100,100));
349 QCOMPARE(testItem->touchEventsReceived.count(), 1);
350 testItem->touchEventsReceived.clear();
352 touchGate->setEnabled(false);
354 QTest::touchEvent(view, device)
355 .move(0, QPoint(101,101));
357 // Nothing new came as TouchGate didn't even get it because it's disabled
358 QCOMPARE(testItem->touchEventsReceived.count(), 0);
360 touchGate->setEnabled(true);
362 QTest::touchEvent(view, device)
363 .move(0, QPoint(102,102));
365 QTest::touchEvent(view, device)
366 .release(0, QPoint(102,102));
368 // Nothing new came because TouchGate has already discarded touch 0
369 QCOMPARE(testItem->touchEventsReceived.count(), 0);
371 QTest::touchEvent(view, device)
372 .press(1, QPoint(200,200));
374 QCOMPARE(testItem->touchEventsReceived.count(), 1);
375 {
376 // it got only the new touch point.
377 QSharedPointer<QTouchEvent> touchEvent = testItem->touchEventsReceived[0];
378 QCOMPARE(touchEvent->touchPoints().count(), 1);
379 QCOMPARE(touchEvent->touchPoints()[0].id(), 1);
380 }
383///////////// TestItem /////////////////////////////////////////////////////////////
385void TestItem::touchEvent(QTouchEvent *event)
387 QTouchEvent *clonedEvent = new QTouchEvent(event->type(),
388 event->device(),
389 event->modifiers(),
390 event->touchPointStates(),
391 event->touchPoints());
393 touchEventsReceived.append(QSharedPointer<QTouchEvent>(clonedEvent));
396///////////// CandidateItem /////////////////////////////////////////////////////////////
398void CandidateItem::touchEvent(QTouchEvent *event)
400 touchEventHandler(event);
404QTEST_MAIN(tst_TouchGate)
406#include "tst_TouchGate.moc"