@@ -22,6 +22,30 @@
2222
2323#include <DirectionalDragArea.h>
2424
25+class FakeTimer : public UbuntuGestures::AbstractTimer
26+{
27+ Q_OBJECT
28+public:
29+ FakeTimer(QObject *parent = 0)
30+ : UbuntuGestures::AbstractTimer(parent)
31+ {}
32+
33+ virtual int interval() const { return m_duration; }
34+ virtual void setInterval(int msecs) { m_duration = msecs; }
35+
36+ void emitTimeout() {Q_EMIT timeout();}
37+private:
38+ int m_duration;
39+};
40+
41+class FakeTimeSource : public AxisVelocityCalculator::TimeSource
42+{
43+public:
44+ FakeTimeSource() { m_msecsSinceReference = 0; }
45+ virtual qint64 msecsSinceReference() {return m_msecsSinceReference;}
46+ qint64 m_msecsSinceReference;
47+};
48+
2549class tst_DirectionalDragArea: public QObject
2650{
2751 Q_OBJECT
@@ -31,13 +55,24 @@ private Q_SLOTS:
3155 void initTestCase(); // will be called before the first test function is executed
3256 void cleanupTestCase(); // will be called after the last test function was executed.
3357
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
60+
3461 void edgeDrag();
3562 void edgeDrag_data();
3663 void dragWithShortDirectionChange();
64+ void minSpeed();
65+ void minSpeed_data();
66+ void recognitionTimerUsage();
67+ void maxSilenceTime();
3768
3869private:
3970 QQuickView *createView();
71+ QQuickView *view;
4072 QTouchDevice *device;
73+ FakeTimer *fakeTimer;
74+ FakeTimeSource *fakeTimeSource;
75+ AxisVelocityCalculator *velCalcWithFakeTiming;
4176};
4277
4378void tst_DirectionalDragArea::initTestCase()
@@ -47,12 +82,42 @@ void tst_DirectionalDragArea::initTestCase()
4782 device->setType(QTouchDevice::TouchScreen);
4883 QWindowSystemInterface::registerTouchDevice(device);
4984 }
85+
86+ view = 0;
5087}
5188
5289void tst_DirectionalDragArea::cleanupTestCase()
5390{
5491}
5592
93+void tst_DirectionalDragArea::init()
94+{
95+ view = createView();
96+ view->setSource(QUrl::fromLocalFile("edgeDragExample.qml"));
97+ view->show();
98+ QVERIFY(QTest::qWaitForWindowExposed(view));
99+ QVERIFY(view->rootObject() != 0);
100+ qApp->processEvents();
101+
102+ fakeTimer = new FakeTimer;
103+ fakeTimeSource = new FakeTimeSource;
104+ velCalcWithFakeTiming = new AxisVelocityCalculator(fakeTimeSource);
105+}
106+
107+void tst_DirectionalDragArea::cleanup()
108+{
109+ delete view;
110+ view = 0;
111+
112+ delete fakeTimer;
113+ fakeTimer = 0;
114+
115+ // will bring fakeTimeSource down with it
116+ delete velCalcWithFakeTiming;
117+ fakeTimeSource = 0;
118+ velCalcWithFakeTiming = 0;
119+}
120+
56121QQuickView *tst_DirectionalDragArea::createView()
57122{
58123 QQuickView *window = new QQuickView(0);
@@ -119,17 +184,11 @@ void tst_DirectionalDragArea::edgeDrag()
119184 QFETCH(qreal, dragDistanceFactor);
120185 QFETCH(int, expectedGestureRecognition);
121186
122− QQuickView *view = createView();
123− QScopedPointer<QQuickView> scope(view);
124− view->setSource(QUrl::fromLocalFile("edgeDragExample.qml"));
125− view->show();
126− QVERIFY(QTest::qWaitForWindowExposed(view));
127− QVERIFY(view->rootObject() != 0);
128− qApp->processEvents();
129−
130187 DirectionalDragArea *edgeDragArea =
131188 view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
132189 QVERIFY(edgeDragArea != 0);
190+ edgeDragArea->setRecognitionTimer(fakeTimer);
191+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
133192
134193 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
135194
@@ -230,17 +289,11 @@ void tst_DirectionalDragArea::edgeDrag_data()
230289 */
231290void tst_DirectionalDragArea::dragWithShortDirectionChange()
232291{
233− QQuickView *view = createView();
234− QScopedPointer<QQuickView> scope(view);
235− view->setSource(QUrl::fromLocalFile("edgeDragExample.qml"));
236− view->show();
237− QVERIFY(QTest::qWaitForWindowExposed(view));
238− QVERIFY(view->rootObject() != 0);
239− qApp->processEvents();
240−
241292 DirectionalDragArea *edgeDragArea =
242293 view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
243294 QVERIFY(edgeDragArea != 0);
295+ edgeDragArea->setRecognitionTimer(fakeTimer);
296+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
244297
245298 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
246299 QPointF touchPoint = initialTouchPos;
@@ -272,6 +325,140 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
272325 QTest::touchEvent(view, device).release(0, touchPoint.toPoint());
273326}
274327
328+/*
329+ Checks that a gesture will be rejected if it's slower than minSpeed while
330+ status is Undecided.
331+ */
332+void tst_DirectionalDragArea::minSpeed()
333+{
334+ QFETCH(int, minSpeedMsecsDeviation);
335+ QFETCH(int, expectedStatusAfterSpeedCheck);
336+
337+ DirectionalDragArea *edgeDragArea =
338+ view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
339+ QVERIFY(edgeDragArea != 0);
340+ edgeDragArea->setRecognitionTimer(fakeTimer);
341+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
342+
343+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
344+ QPointF touchPoint = initialTouchPos;
345+
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;
351+
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);
355+
356+ fakeTimeSource->m_msecsSinceReference = 0;
357+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
358+
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());
364+ }
365+
366+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
367+
368+ // Force the periodic speed check.
369+ fakeTimer->emitTimeout();
370+
371+ QCOMPARE((int)edgeDragArea->status(), expectedStatusAfterSpeedCheck);
372+}
373+
374+void tst_DirectionalDragArea::minSpeed_data()
375+{
376+ QTest::addColumn<int>("minSpeedMsecsDeviation");
377+ QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
378+
379+ QTest::newRow("slower than minSpeed") << 20 << (int)DirectionalDragArea::Rejected;
380+ QTest::newRow("faster than minSpeed") << -20 << (int)DirectionalDragArea::Undecided;
381+}
382+
383+/*
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)
387+ */
388+void tst_DirectionalDragArea::recognitionTimerUsage()
389+{
390+ DirectionalDragArea *edgeDragArea =
391+ view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
392+ QVERIFY(edgeDragArea != 0);
393+ edgeDragArea->setRecognitionTimer(fakeTimer);
394+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
395+
396+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
397+ QPointF touchPoint = initialTouchPos;
398+
399+ QPointF dragDirectionVector(1.0, 0.0);
400+ QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);
401+
402+ QVERIFY(!fakeTimer->isRunning());
403+
404+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
405+
406+ QVERIFY(fakeTimer->isRunning());
407+
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());
412+ }
413+
414+ QVERIFY(fakeTimer->isRunning());
415+
416+ // Move beyond distance threshold
417+ touchPoint += 3*touchMovement;
418+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
419+
420+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
421+
422+ QVERIFY(!fakeTimer->isRunning());
423+}
424+
425+/*
426+ A gesture should be rejected if too much time has passed without any new input
427+ events from it.
428+ */
429+void tst_DirectionalDragArea::maxSilenceTime()
430+{
431+ DirectionalDragArea *edgeDragArea =
432+ view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
433+ QVERIFY(edgeDragArea != 0);
434+ edgeDragArea->setRecognitionTimer(fakeTimer);
435+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
436+
437+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
438+ QPointF touchPoint = initialTouchPos;
439+
440+ fakeTimeSource->m_msecsSinceReference = 0;
441+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
442+
443+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
444+
445+ int neededTimeouts = qCeil((qreal)edgeDragArea->maxSilenceTime() / (qreal)fakeTimer->interval());
446+ if ((edgeDragArea->maxSilenceTime() % fakeTimer->interval()) == 0) {
447+ ++neededTimeouts;
448+ }
449+
450+ // Force the periodic speed check.
451+ for (int i = 0; i < neededTimeouts; ++i) {
452+ fakeTimer->emitTimeout();
453+
454+ if (i < neededTimeouts - 1) {
455+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
456+ }
457+ }
458+
459+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Rejected);
460+}
461+
275462QTEST_MAIN(tst_DirectionalDragArea)
276463
277464#include "tst_DirectionalDragArea.moc"