@@ -0,0 +1,323 @@
1+/*
2+ * Copyright (C) 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+ */
16+
17+#include "HomeKeyWatcher.h"
18+
19+#include <QTest>
20+#include <QSignalSpy>
21+
22+
23+namespace UnityUtil {
24+class FakeElapsedTimer : public AbstractElapsedTimer {
25+public:
26+ static qint64 msecsSinceEpoch;
27+
28+ FakeElapsedTimer() { start(); }
29+
30+ void start() override { m_msecsSinceReference = msecsSinceEpoch; }
31+ qint64 msecsSinceReference() const override { return m_msecsSinceReference; }
32+ qint64 elapsed() const override { return msecsSinceEpoch - m_msecsSinceReference; }
33+
34+private:
35+ qint64 m_msecsSinceReference;
36+};
37+qint64 FakeElapsedTimer::msecsSinceEpoch = 0;
38+
39+class FakeTimer : public AbstractTimer
40+{
41+ Q_OBJECT
42+public:
43+ FakeTimer(QObject *parent = nullptr);
44+
45+ void update();
46+ qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
47+
48+ int interval() const override;
49+ void setInterval(int msecs) override;
50+ void start() override;
51+ bool isSingleShot() const override;
52+ void setSingleShot(bool value) override;
53+private:
54+ int m_interval;
55+ bool m_singleShot;
56+ qint64 m_nextTimeoutTime;
57+};
58+
59+class FakeTimerFactory : public AbstractTimerFactory
60+{
61+public:
62+ FakeTimerFactory();
63+ virtual ~FakeTimerFactory();
64+
65+ void updateTime(qint64 targetTime);
66+
67+ AbstractTimer *create(QObject *parent = nullptr) override;
68+ QList<QPointer<FakeTimer>> timers;
69+};
70+} // namespace UnityUtil
71+
72+using namespace UnityUtil;
73+
74+class HomeKeyWatcherTest : public QObject {
75+ Q_OBJECT
76+private Q_SLOTS:
77+ void init(); // called right before each and every test function is executed
78+ void cleanup(); // called right after each and every test function is executed
79+
80+ void touchTapTouch_data();
81+ void touchTapTouch();
82+
83+ void tapWhileTouching();
84+
85+private:
86+ void passTime(qint64 timeSpanMs);
87+
88+ FakeTimerFactory *m_fakeTimerFactory;
89+};
90+
91+void HomeKeyWatcherTest::init()
92+{
93+ m_fakeTimerFactory = new FakeTimerFactory;
94+}
95+
96+void HomeKeyWatcherTest::cleanup()
97+{
98+ delete m_fakeTimerFactory;
99+ m_fakeTimerFactory = nullptr;
100+}
101+
102+
103+void HomeKeyWatcherTest::passTime(qint64 timeSpanMs)
104+{
105+ qint64 finalTime = FakeElapsedTimer::msecsSinceEpoch + timeSpanMs;
106+ m_fakeTimerFactory->updateTime(finalTime);
107+}
108+
109+void HomeKeyWatcherTest::touchTapTouch_data()
110+{
111+ QTest::addColumn<int>("silenceBeforeTap");
112+ QTest::addColumn<int>("tapDuration");
113+ QTest::addColumn<int>("silenceAfterTap");
114+ QTest::addColumn<int>("expectedActivatedCount");
115+
116+ QTest::newRow("tap followed by touch") << 2000 << 50 << 10 << 0;
117+ QTest::newRow("touch, tap and touch") << 10 << 50 << 10 << 0;
118+ QTest::newRow("touch followed by tap") << 10 << 50 << 2000 << 0;
119+ QTest::newRow("touch followed by long tap") << 10 << 500 << 2000 << 0;
120+ QTest::newRow("isolated tap") << 1000 << 50 << 1000 << 1;
121+ QTest::newRow("isolated long press") << 1000 << 200 << 1000 << 0;
122+}
123+
124+void HomeKeyWatcherTest::touchTapTouch()
125+{
126+ QFETCH(int, silenceBeforeTap);
127+ QFETCH(int, tapDuration);
128+ QFETCH(int, silenceAfterTap);
129+ QFETCH(int, expectedActivatedCount);
130+ HomeKeyWatcher homeKeyWatcher(m_fakeTimerFactory->create(), new FakeElapsedTimer);
131+ QSignalSpy activatedSpy(&homeKeyWatcher, SIGNAL(activated()));
132+ QVERIFY(activatedSpy.isValid());
133+
134+ {
135+ QTouchEvent touchEvent(QEvent::TouchBegin);
136+ homeKeyWatcher.update(&touchEvent);
137+ }
138+ passTime(100);
139+ {
140+ QTouchEvent touchEvent(QEvent::TouchUpdate);
141+ homeKeyWatcher.update(&touchEvent);
142+ }
143+ passTime(100);
144+ {
145+ QTouchEvent touchEvent(QEvent::TouchEnd);
146+ homeKeyWatcher.update(&touchEvent);
147+ }
148+
149+ passTime(silenceBeforeTap);
150+
151+ {
152+ QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier);
153+ homeKeyWatcher.update(&keyEvent);
154+ }
155+ int tapTime = 0;
156+ while (tapTime < tapDuration) {
157+ passTime(10);
158+ tapTime += 10;
159+ if (tapTime + 10 >= tapDuration) {
160+ QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_Super_L, Qt::NoModifier);
161+ homeKeyWatcher.update(&keyEvent);
162+ } else {
163+ QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier, QString(), true /*autorepeat*/);
164+ homeKeyWatcher.update(&keyEvent);
165+ }
166+ }
167+
168+ passTime(silenceAfterTap);
169+
170+ {
171+ QTouchEvent touchEvent(QEvent::TouchBegin);
172+ homeKeyWatcher.update(&touchEvent);
173+ }
174+ passTime(100);
175+ {
176+ QTouchEvent touchEvent(QEvent::TouchUpdate);
177+ homeKeyWatcher.update(&touchEvent);
178+ }
179+ passTime(100);
180+ {
181+ QTouchEvent touchEvent(QEvent::TouchEnd);
182+ homeKeyWatcher.update(&touchEvent);
183+ }
184+ passTime(1000);
185+
186+ QCOMPARE(activatedSpy.count(), expectedActivatedCount);
187+}
188+
189+void HomeKeyWatcherTest::tapWhileTouching()
190+{
191+ HomeKeyWatcher homeKeyWatcher(m_fakeTimerFactory->create(), new FakeElapsedTimer);
192+ QSignalSpy activatedSpy(&homeKeyWatcher, SIGNAL(activated()));
193+ QVERIFY(activatedSpy.isValid());
194+
195+ {
196+ QTouchEvent touchEvent(QEvent::TouchBegin);
197+ homeKeyWatcher.update(&touchEvent);
198+ }
199+ passTime(1000);
200+ {
201+ QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier);
202+ homeKeyWatcher.update(&keyEvent);
203+ }
204+ passTime(100);
205+ {
206+ QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_Super_L, Qt::NoModifier);
207+ homeKeyWatcher.update(&keyEvent);
208+ }
209+ passTime(1000);
210+ {
211+ QTouchEvent touchEvent(QEvent::TouchEnd);
212+ homeKeyWatcher.update(&touchEvent);
213+ }
214+ passTime(1000);
215+
216+
217+ QCOMPARE(activatedSpy.count(), 0);
218+}
219+
220+/////////////////////////////////// FakeTimer //////////////////////////////////
221+
222+FakeTimer::FakeTimer(QObject *parent)
223+ : UnityUtil::AbstractTimer(parent)
224+ , m_interval(0)
225+ , m_singleShot(true)
226+{
227+}
228+
229+void FakeTimer::update()
230+{
231+ if (!isRunning()) {
232+ return;
233+ }
234+
235+ if (m_nextTimeoutTime <= FakeElapsedTimer::msecsSinceEpoch) {
236+ if (isSingleShot()) {
237+ stop();
238+ } else {
239+ m_nextTimeoutTime += interval();
240+ }
241+ Q_EMIT timeout();
242+ }
243+}
244+
245+void FakeTimer::start()
246+{
247+ AbstractTimer::start();
248+ m_nextTimeoutTime = FakeElapsedTimer::msecsSinceEpoch + (qint64)interval();
249+}
250+
251+int FakeTimer::interval() const
252+{
253+ return m_interval;
254+}
255+
256+void FakeTimer::setInterval(int msecs)
257+{
258+ m_interval = msecs;
259+}
260+
261+bool FakeTimer::isSingleShot() const
262+{
263+ return m_singleShot;
264+}
265+
266+void FakeTimer::setSingleShot(bool value)
267+{
268+ m_singleShot = value;
269+}
270+
271+/////////////////////////////////// FakeTimerFactory //////////////////////////////////
272+
273+FakeTimerFactory::FakeTimerFactory()
274+{
275+}
276+
277+FakeTimerFactory::~FakeTimerFactory()
278+{
279+ for (int i = 0; i < timers.count(); ++i) {
280+ FakeTimer *timer = timers[i].data();
281+ if (timer) {
282+ delete timer;
283+ }
284+ }
285+}
286+
287+void FakeTimerFactory::updateTime(qint64 targetTime)
288+{
289+ qint64 minTimeoutTime = targetTime;
290+
291+ for (int i = 0; i < timers.count(); ++i) {
292+ FakeTimer *timer = timers[i].data();
293+ if (timer && timer->isRunning() && timer->nextTimeoutTime() < minTimeoutTime) {
294+ minTimeoutTime = timer->nextTimeoutTime();
295+ }
296+ }
297+
298+ FakeElapsedTimer::msecsSinceEpoch = minTimeoutTime;
299+
300+ for (int i = 0; i < timers.count(); ++i) {
301+ FakeTimer *timer = timers[i].data();
302+ if (timer) {
303+ timer->update();
304+ }
305+ }
306+
307+ if (FakeElapsedTimer::msecsSinceEpoch < targetTime) {
308+ updateTime(targetTime);
309+ }
310+}
311+
312+AbstractTimer *FakeTimerFactory::create(QObject *parent)
313+{
314+ FakeTimer *fakeTimer = new FakeTimer(parent);
315+
316+ timers.append(fakeTimer);
317+
318+ return fakeTimer;
319+}
320+
321+QTEST_GUILESS_MAIN(HomeKeyWatcherTest)
322+
323+#include "homekeywatchertest.moc"