@@ -22,6 +22,30 @@
23#include <DirectionalDragArea.h>
23#include <DirectionalDragArea.h>
25+class FakeTimer : public UbuntuGestures::AbstractTimer
29+ FakeTimer(QObject *parent = 0)
30+ : UbuntuGestures::AbstractTimer(parent)
33+ virtual int interval() const { return m_duration; }
34+ virtual void setInterval(int msecs) { m_duration = msecs; }
36+ void emitTimeout() {Q_EMIT timeout();}
41+class FakeTimeSource : public AxisVelocityCalculator::TimeSource
44+ FakeTimeSource() { m_msecsSinceReference = 0; }
45+ virtual qint64 msecsSinceReference() {return m_msecsSinceReference;}
46+ qint64 m_msecsSinceReference;
25class tst_DirectionalDragArea: public QObject
49class tst_DirectionalDragArea: public QObject
@@ -31,13 +55,24 @@ private Q_SLOTS:
31 void initTestCase(); // will be called before the first test function is executed
55 void initTestCase(); // will be called before the first test function is executed
32 void cleanupTestCase(); // will be called after the last test function was executed.
56 void cleanupTestCase(); // will be called after the last test function was executed.
58+ void init(); // called right before each and every test function is executed
59+ void cleanup(); // called right after each and every test function is executed
34 void edgeDrag();
61 void edgeDrag();
35 void edgeDrag_data();
62 void edgeDrag_data();
36 void dragWithShortDirectionChange();
63 void dragWithShortDirectionChange();
65+ void minSpeed_data();
66+ void recognitionTimerUsage();
67+ void maxSilenceTime();
39 QQuickView *createView();
70 QQuickView *createView();
40 QTouchDevice *device;
72 QTouchDevice *device;
73+ FakeTimer *fakeTimer;
74+ FakeTimeSource *fakeTimeSource;
75+ AxisVelocityCalculator *velCalcWithFakeTiming;
43void tst_DirectionalDragArea::initTestCase()
78void tst_DirectionalDragArea::initTestCase()
@@ -47,12 +82,42 @@ void tst_DirectionalDragArea::initTestCase()
47 device->setType(QTouchDevice::TouchScreen);
82 device->setType(QTouchDevice::TouchScreen);
48 QWindowSystemInterface::registerTouchDevice(device);
83 QWindowSystemInterface::registerTouchDevice(device);
52void tst_DirectionalDragArea::cleanupTestCase()
89void tst_DirectionalDragArea::cleanupTestCase()
93+void tst_DirectionalDragArea::init()
96+ view->setSource(QUrl::fromLocalFile("edgeDragExample.qml"));
98+ QVERIFY(QTest::qWaitForWindowExposed(view));
99+ QVERIFY(view->rootObject() != 0);
100+ qApp->processEvents();
102+ fakeTimer = new FakeTimer;
103+ fakeTimeSource = new FakeTimeSource;
104+ velCalcWithFakeTiming = new AxisVelocityCalculator(fakeTimeSource);
107+void tst_DirectionalDragArea::cleanup()
115+ // will bring fakeTimeSource down with it
116+ delete velCalcWithFakeTiming;
118+ velCalcWithFakeTiming = 0;
56QQuickView *tst_DirectionalDragArea::createView()
121QQuickView *tst_DirectionalDragArea::createView()
58 QQuickView *window = new QQuickView(0);
123 QQuickView *window = new QQuickView(0);
@@ -119,17 +184,11 @@ void tst_DirectionalDragArea::edgeDrag()
119 QFETCH(qreal, dragDistanceFactor);
184 QFETCH(qreal, dragDistanceFactor);
120 QFETCH(int, expectedGestureRecognition);
185 QFETCH(int, expectedGestureRecognition);
122− QQuickView *view = createView();
123− QScopedPointer<QQuickView> scope(view);
124− view->setSource(QUrl::fromLocalFile("edgeDragExample.qml"));
126− QVERIFY(QTest::qWaitForWindowExposed(view));
127− QVERIFY(view->rootObject() != 0);
128− qApp->processEvents();
130 DirectionalDragArea *edgeDragArea =
187 DirectionalDragArea *edgeDragArea =
131 view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
188 view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
132 QVERIFY(edgeDragArea != 0);
189 QVERIFY(edgeDragArea != 0);
190+ edgeDragArea->setRecognitionTimer(fakeTimer);
191+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
134 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
193 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
@@ -230,17 +289,11 @@ void tst_DirectionalDragArea::edgeDrag_data()
231void tst_DirectionalDragArea::dragWithShortDirectionChange()
290void tst_DirectionalDragArea::dragWithShortDirectionChange()
233− QQuickView *view = createView();
234− QScopedPointer<QQuickView> scope(view);
235− view->setSource(QUrl::fromLocalFile("edgeDragExample.qml"));
237− QVERIFY(QTest::qWaitForWindowExposed(view));
238− QVERIFY(view->rootObject() != 0);
239− qApp->processEvents();
241 DirectionalDragArea *edgeDragArea =
292 DirectionalDragArea *edgeDragArea =
242 view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
293 view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
243 QVERIFY(edgeDragArea != 0);
294 QVERIFY(edgeDragArea != 0);
295+ edgeDragArea->setRecognitionTimer(fakeTimer);
296+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
245 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
298 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
246 QPointF touchPoint = initialTouchPos;
299 QPointF touchPoint = initialTouchPos;
@@ -272,6 +325,140 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
272 QTest::touchEvent(view, device).release(0, touchPoint.toPoint());
325 QTest::touchEvent(view, device).release(0, touchPoint.toPoint());
329+ Checks that a gesture will be rejected if it's slower than minSpeed while
330+ status is Undecided.
332+void tst_DirectionalDragArea::minSpeed()
334+ QFETCH(int, minSpeedMsecsDeviation);
335+ QFETCH(int, expectedStatusAfterSpeedCheck);
337+ DirectionalDragArea *edgeDragArea =
338+ view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
339+ QVERIFY(edgeDragArea != 0);
340+ edgeDragArea->setRecognitionTimer(fakeTimer);
341+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
343+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
344+ QPointF touchPoint = initialTouchPos;
346+ QPointF dragDirectionVector(1.0, 0.0);
347+ qreal distanceStep = edgeDragArea->distanceThreshold() * 0.1f;
348+ QPointF touchMovement = dragDirectionVector * distanceStep;
349+ qreal minSpeedMsecs = edgeDragArea->minSpeed() / 1000.0;
350+ qint64 timeStepMsecs = qFloor(distanceStep / minSpeedMsecs) + minSpeedMsecsDeviation;
352+ // if it fails it means the params set in the QML file are not in harmony with the values
353+ // used in this test
354+ Q_ASSERT(timeStepMsecs > 0);
356+ fakeTimeSource->m_msecsSinceReference = 0;
357+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
359+ // Move a bit in the proper direction
360+ for (int i=0; i < 4; ++i) {
361+ touchPoint += touchMovement;
362+ fakeTimeSource->m_msecsSinceReference += timeStepMsecs;
363+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
366+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
368+ // Force the periodic speed check.
369+ fakeTimer->emitTimeout();
371+ QCOMPARE((int)edgeDragArea->status(), expectedStatusAfterSpeedCheck);
374+void tst_DirectionalDragArea::minSpeed_data()
376+ QTest::addColumn<int>("minSpeedMsecsDeviation");
377+ QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
379+ QTest::newRow("slower than minSpeed") << 20 << (int)DirectionalDragArea::Rejected;
380+ QTest::newRow("faster than minSpeed") << -20 << (int)DirectionalDragArea::Undecided;
384+ Checks that the recognition timer is started and stopped appropriately.
385+ I.e., check that it's running only while gesture recognition is taking place
386+ (status == Undecided)
388+void tst_DirectionalDragArea::recognitionTimerUsage()
390+ DirectionalDragArea *edgeDragArea =
391+ view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
392+ QVERIFY(edgeDragArea != 0);
393+ edgeDragArea->setRecognitionTimer(fakeTimer);
394+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
396+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
397+ QPointF touchPoint = initialTouchPos;
399+ QPointF dragDirectionVector(1.0, 0.0);
400+ QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);
402+ QVERIFY(!fakeTimer->isRunning());
404+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
406+ QVERIFY(fakeTimer->isRunning());
408+ // Move a bit in the proper direction
409+ for (int i=0; i < 3; ++i) {
410+ touchPoint += touchMovement;
411+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
414+ QVERIFY(fakeTimer->isRunning());
416+ // Move beyond distance threshold
417+ touchPoint += 3*touchMovement;
418+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
420+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
422+ QVERIFY(!fakeTimer->isRunning());
426+ A gesture should be rejected if too much time has passed without any new input
429+void tst_DirectionalDragArea::maxSilenceTime()
431+ DirectionalDragArea *edgeDragArea =
432+ view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
433+ QVERIFY(edgeDragArea != 0);
434+ edgeDragArea->setRecognitionTimer(fakeTimer);
435+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
437+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
438+ QPointF touchPoint = initialTouchPos;
440+ fakeTimeSource->m_msecsSinceReference = 0;
441+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
443+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
445+ int neededTimeouts = qCeil((qreal)edgeDragArea->maxSilenceTime() / (qreal)fakeTimer->interval());
446+ if ((edgeDragArea->maxSilenceTime() % fakeTimer->interval()) == 0) {
450+ // Force the periodic speed check.
451+ for (int i = 0; i < neededTimeouts; ++i) {
452+ fakeTimer->emitTimeout();
454+ if (i < neededTimeouts - 1) {
455+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
459+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Rejected);
275QTEST_MAIN(tst_DirectionalDragArea)
462QTEST_MAIN(tst_DirectionalDragArea)
277#include "tst_DirectionalDragArea.moc"
464#include "tst_DirectionalDragArea.moc"