@@ -22,6 +22,35 @@
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+ m_isRunning(false)
32+ {}
33+
34+ virtual int interval() const { return m_duration; }
35+ virtual void setInterval(int msecs) { m_duration = msecs; }
36+ virtual void start() { m_isRunning = true; }
37+ virtual void stop() { m_isRunning = false; }
38+
39+ bool isRunning() const { return m_isRunning; }
40+ void emitTimeout() {Q_EMIT timeout();}
41+private:
42+ int m_duration;
43+ bool m_isRunning;
44+};
45+
46+class FakeTimeSource : public AxisVelocityCalculator::TimeSource
47+{
48+public:
49+ FakeTimeSource() { m_msecsSinceReference = 0; }
50+ virtual qint64 msecsSinceReference() {return m_msecsSinceReference;}
51+ qint64 m_msecsSinceReference;
52+};
53+
2554class tst_DirectionalDragArea: public QObject
2655{
2756 Q_OBJECT
@@ -31,13 +60,24 @@ private Q_SLOTS:
3160 void initTestCase(); // will be called before the first test function is executed
3261 void cleanupTestCase(); // will be called after the last test function was executed.
3362
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
65+
3466 void edgeDrag();
3567 void edgeDrag_data();
3668 void dragWithShortDirectionChange();
69+ void minSpeed();
70+ void minSpeed_data();
71+ void recognitionTimerUsage();
72+ void maxSilenceTime();
3773
3874private:
3975 QQuickView *createView();
76+ QQuickView *view;
4077 QTouchDevice *device;
78+ FakeTimer *fakeTimer;
79+ FakeTimeSource *fakeTimeSource;
80+ AxisVelocityCalculator *velCalcWithFakeTiming;
4181};
4282
4383void tst_DirectionalDragArea::initTestCase()
@@ -47,12 +87,42 @@ void tst_DirectionalDragArea::initTestCase()
4787 device->setType(QTouchDevice::TouchScreen);
4888 QWindowSystemInterface::registerTouchDevice(device);
4989 }
90+
91+ view = 0;
5092}
5193
5294void tst_DirectionalDragArea::cleanupTestCase()
5395{
5496}
5597
98+void tst_DirectionalDragArea::init()
99+{
100+ view = createView();
101+ view->setSource(QUrl::fromLocalFile("edgeDragExample.qml"));
102+ view->show();
103+ QVERIFY(QTest::qWaitForWindowExposed(view));
104+ QVERIFY(view->rootObject() != 0);
105+ qApp->processEvents();
106+
107+ fakeTimer = new FakeTimer;
108+ fakeTimeSource = new FakeTimeSource;
109+ velCalcWithFakeTiming = new AxisVelocityCalculator(fakeTimeSource);
110+}
111+
112+void tst_DirectionalDragArea::cleanup()
113+{
114+ delete view;
115+ view = 0;
116+
117+ delete fakeTimer;
118+ fakeTimer = 0;
119+
120+ // will bring fakeTimeSource down with it
121+ delete velCalcWithFakeTiming;
122+ fakeTimeSource = 0;
123+ velCalcWithFakeTiming = 0;
124+}
125+
56126QQuickView *tst_DirectionalDragArea::createView()
57127{
58128 QQuickView *window = new QQuickView(0);
@@ -119,17 +189,11 @@ void tst_DirectionalDragArea::edgeDrag()
119189 QFETCH(qreal, dragDistanceFactor);
120190 QFETCH(int, expectedGestureRecognition);
121191
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−
130192 DirectionalDragArea *edgeDragArea =
131193 view->rootObject()->findChild<DirectionalDragArea*>(dragAreaObjectName);
132194 QVERIFY(edgeDragArea != 0);
195+ edgeDragArea->setRecognitionTimer(fakeTimer);
196+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
133197
134198 QSignalSpy draggingSpy(edgeDragArea, SIGNAL(draggingChanged(bool)));
135199
@@ -230,17 +294,11 @@ void tst_DirectionalDragArea::edgeDrag_data()
230294 */
231295void tst_DirectionalDragArea::dragWithShortDirectionChange()
232296{
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−
241297 DirectionalDragArea *edgeDragArea =
242298 view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
243299 QVERIFY(edgeDragArea != 0);
300+ edgeDragArea->setRecognitionTimer(fakeTimer);
301+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
244302
245303 QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
246304 QPointF touchPoint = initialTouchPos;
@@ -272,6 +330,140 @@ void tst_DirectionalDragArea::dragWithShortDirectionChange()
272330 QTest::touchEvent(view, device).release(0, touchPoint.toPoint());
273331}
274332
333+/*
334+ Checks that a gesture will be rejected if it's slower than minSpeed while
335+ status is Undecided.
336+ */
337+void tst_DirectionalDragArea::minSpeed()
338+{
339+ QFETCH(int, minSpeedMsecsDeviation);
340+ QFETCH(int, expectedStatusAfterSpeedCheck);
341+
342+ DirectionalDragArea *edgeDragArea =
343+ view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
344+ QVERIFY(edgeDragArea != 0);
345+ edgeDragArea->setRecognitionTimer(fakeTimer);
346+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
347+
348+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
349+ QPointF touchPoint = initialTouchPos;
350+
351+ QPointF dragDirectionVector(1.0, 0.0);
352+ qreal distanceStep = edgeDragArea->distanceThreshold() * 0.1f;
353+ QPointF touchMovement = dragDirectionVector * distanceStep;
354+ qreal minSpeedMsecs = edgeDragArea->minSpeed() / 1000.0;
355+ qint64 timeStepMsecs = qFloor(distanceStep / minSpeedMsecs) + minSpeedMsecsDeviation;
356+
357+ // if it fails it means the params set in the QML file are not in harmony with the values
358+ // used in this test
359+ Q_ASSERT(timeStepMsecs > 0);
360+
361+ fakeTimeSource->m_msecsSinceReference = 0;
362+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
363+
364+ // Move a bit in the proper direction
365+ for (int i=0; i < 4; ++i) {
366+ touchPoint += touchMovement;
367+ fakeTimeSource->m_msecsSinceReference += timeStepMsecs;
368+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
369+ }
370+
371+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
372+
373+ // Force the periodic speed check.
374+ fakeTimer->emitTimeout();
375+
376+ QCOMPARE((int)edgeDragArea->status(), expectedStatusAfterSpeedCheck);
377+}
378+
379+void tst_DirectionalDragArea::minSpeed_data()
380+{
381+ QTest::addColumn<int>("minSpeedMsecsDeviation");
382+ QTest::addColumn<int>("expectedStatusAfterSpeedCheck");
383+
384+ QTest::newRow("slower than minSpeed") << 20 << (int)DirectionalDragArea::Rejected;
385+ QTest::newRow("faster than minSpeed") << -20 << (int)DirectionalDragArea::Undecided;
386+}
387+
388+/*
389+ Checks that the recognition timer is started and stopped appropriately.
390+ I.e., check that it's running only while gesture recognition is taking place
391+ (status == Undecided)
392+ */
393+void tst_DirectionalDragArea::recognitionTimerUsage()
394+{
395+ DirectionalDragArea *edgeDragArea =
396+ view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
397+ QVERIFY(edgeDragArea != 0);
398+ edgeDragArea->setRecognitionTimer(fakeTimer);
399+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
400+
401+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
402+ QPointF touchPoint = initialTouchPos;
403+
404+ QPointF dragDirectionVector(1.0, 0.0);
405+ QPointF touchMovement = dragDirectionVector * (edgeDragArea->distanceThreshold() * 0.2f);
406+
407+ QVERIFY(!fakeTimer->isRunning());
408+
409+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
410+
411+ QVERIFY(fakeTimer->isRunning());
412+
413+ // Move a bit in the proper direction
414+ for (int i=0; i < 3; ++i) {
415+ touchPoint += touchMovement;
416+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
417+ }
418+
419+ QVERIFY(fakeTimer->isRunning());
420+
421+ // Move beyond distance threshold
422+ touchPoint += 3*touchMovement;
423+ QTest::touchEvent(view, device).move(0, touchPoint.toPoint());
424+
425+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Recognized);
426+
427+ QVERIFY(!fakeTimer->isRunning());
428+}
429+
430+/*
431+ A gesture should be rejected if too much time has passed without any new input
432+ events from it.
433+ */
434+void tst_DirectionalDragArea::maxSilenceTime()
435+{
436+ DirectionalDragArea *edgeDragArea =
437+ view->rootObject()->findChild<DirectionalDragArea*>("hpDragArea");
438+ QVERIFY(edgeDragArea != 0);
439+ edgeDragArea->setRecognitionTimer(fakeTimer);
440+ edgeDragArea->setAxisVelocityCalculator(velCalcWithFakeTiming);
441+
442+ QPointF initialTouchPos = calculateInitialTouchPos(edgeDragArea, view);
443+ QPointF touchPoint = initialTouchPos;
444+
445+ fakeTimeSource->m_msecsSinceReference = 0;
446+ QTest::touchEvent(view, device).press(0, touchPoint.toPoint());
447+
448+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
449+
450+ int neededTimeouts = qCeil((qreal)edgeDragArea->maxSilenceTime() / (qreal)fakeTimer->interval());
451+ if ((edgeDragArea->maxSilenceTime() % fakeTimer->interval()) == 0) {
452+ ++neededTimeouts;
453+ }
454+
455+ // Force the periodic speed check.
456+ for (int i = 0; i < neededTimeouts; ++i) {
457+ fakeTimer->emitTimeout();
458+
459+ if (i < neededTimeouts - 1) {
460+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Undecided);
461+ }
462+ }
463+
464+ QCOMPARE((int)edgeDragArea->status(), (int)DirectionalDragArea::Rejected);
465+}
466+
275467QTEST_MAIN(tst_DirectionalDragArea)
276468
277469#include "tst_DirectionalDragArea.moc"