on e4400a2 help sign in
unity8/tests/plugins/Utils/WindowInputMonitorTest.cpp
431 lines 12 kB history raw
1/*
2 * Copyright (C) 2015-2016 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 "WindowInputMonitor.h"
19#include <QTest>
20#include <QSignalSpy>
23namespace UnityUtil {
24class FakeElapsedTimer : public AbstractElapsedTimer {
25public:
26 static qint64 msecsSinceEpoch;
28 FakeElapsedTimer() {}
30 void start() override { m_msecsSinceReference = msecsSinceEpoch; m_valid = true; }
31 qint64 msecsSinceReference() const override { return m_msecsSinceReference; }
32 qint64 elapsed() const override { return m_valid ? msecsSinceEpoch - m_msecsSinceReference : qrand(); }
34private:
35 qint64 m_msecsSinceReference;
36 bool m_valid{false};
37};
38qint64 FakeElapsedTimer::msecsSinceEpoch = 0;
40class FakeTimer : public AbstractTimer
42 Q_OBJECT
43public:
44 FakeTimer(QObject *parent = nullptr);
46 void update();
47 qint64 nextTimeoutTime() const { return m_nextTimeoutTime; }
49 int interval() const override;
50 void setInterval(int msecs) override;
51 void start() override;
52 void stop() override;
53 bool isRunning() const override;
54 bool isSingleShot() const override;
55 void setSingleShot(bool value) override;
56private:
57 int m_interval;
58 bool m_singleShot;
59 bool m_isRunning;
60 qint64 m_nextTimeoutTime;
61};
63class FakeTimerFactory : public AbstractTimerFactory
65public:
66 FakeTimerFactory();
67 virtual ~FakeTimerFactory();
69 void updateTime(qint64 targetTime);
71 AbstractTimer *create(QObject *parent = nullptr) override;
72 QList<QPointer<FakeTimer>> timers;
73};
74} // namespace UnityUtil
76using namespace UnityUtil;
78Q_DECLARE_METATYPE(Qt::Key) // because of QTest::addColumn<Qt::Key>
80class WindowInputMonitorTest : public QObject {
81 Q_OBJECT
82private Q_SLOTS:
83 void init(); // called right before each and every test function is executed
84 void cleanup(); // called right after each and every test function is executed
86 void touchTapTouch_data();
87 void touchTapTouch();
89 void tapWhileTouching();
90 void multipleHomeKeys();
92 void repeatedSuperPress();
94private:
95 void passTime(qint64 timeSpanMs);
97 FakeTimerFactory *m_fakeTimerFactory;
98};
100void WindowInputMonitorTest::init()
102 m_fakeTimerFactory = new FakeTimerFactory;
105void WindowInputMonitorTest::cleanup()
107 delete m_fakeTimerFactory;
108 m_fakeTimerFactory = nullptr;
111void WindowInputMonitorTest::passTime(qint64 timeSpanMs)
113 qint64 finalTime = FakeElapsedTimer::msecsSinceEpoch + timeSpanMs;
114 m_fakeTimerFactory->updateTime(finalTime);
117void WindowInputMonitorTest::touchTapTouch_data()
119 QTest::addColumn<int>("silenceBeforeTap");
120 QTest::addColumn<int>("tapDuration");
121 QTest::addColumn<int>("silenceAfterTap");
122 QTest::addColumn<int>("expectedActivatedCount");
123 QTest::addColumn<Qt::Key>("key");
125 QTest::newRow("tap followed by touch") << 2000 << 50 << 10 << 0 << Qt::Key_Super_L;
126 QTest::newRow("touch, tap and touch") << 10 << 50 << 10 << 0 << Qt::Key_Super_L;
127 QTest::newRow("touch followed by tap") << 10 << 50 << 2000 << 0 << Qt::Key_Super_L;
128 QTest::newRow("touch followed by long tap") << 10 << 500 << 2000 << 0 << Qt::Key_Super_L;
129 QTest::newRow("isolated tap, Super_L") << 1000 << 50 << 1000 << 1 << Qt::Key_Super_L;
130 QTest::newRow("isolated tap, HomePage") << 1000 << 50 << 1000 << 1 << Qt::Key_HomePage;
131 QTest::newRow("isolated long press") << 1000 << 200 << 1000 << 0 << Qt::Key_Super_L;
134void WindowInputMonitorTest::touchTapTouch()
136 QFETCH(int, silenceBeforeTap);
137 QFETCH(int, tapDuration);
138 QFETCH(int, silenceAfterTap);
139 QFETCH(int, expectedActivatedCount);
140 QFETCH(Qt::Key, key);
141 WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
142 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
143 QVERIFY(activatedSpy.isValid());
145 {
146 QTouchEvent touchEvent(QEvent::TouchBegin);
147 homeKeyWatcher.update(&touchEvent);
148 }
149 passTime(100);
150 {
151 QTouchEvent touchEvent(QEvent::TouchUpdate);
152 homeKeyWatcher.update(&touchEvent);
153 }
154 passTime(100);
155 {
156 QTouchEvent touchEvent(QEvent::TouchEnd);
157 homeKeyWatcher.update(&touchEvent);
158 }
160 passTime(silenceBeforeTap);
162 {
163 QKeyEvent keyEvent(QEvent::KeyPress, key, Qt::NoModifier);
164 homeKeyWatcher.update(&keyEvent);
165 }
166 int tapTime = 0;
167 while (tapTime < tapDuration) {
168 passTime(10);
169 tapTime += 10;
170 if (tapTime + 10 >= tapDuration) {
171 QKeyEvent keyEvent(QEvent::KeyRelease, key, Qt::NoModifier);
172 homeKeyWatcher.update(&keyEvent);
173 } else {
174 QKeyEvent keyEvent(QEvent::KeyPress, key, Qt::NoModifier, QString(), true /*autorepeat*/);
175 homeKeyWatcher.update(&keyEvent);
176 }
177 }
179 passTime(silenceAfterTap);
181 {
182 QTouchEvent touchEvent(QEvent::TouchBegin);
183 homeKeyWatcher.update(&touchEvent);
184 }
185 passTime(100);
186 {
187 QTouchEvent touchEvent(QEvent::TouchUpdate);
188 homeKeyWatcher.update(&touchEvent);
189 }
190 passTime(100);
191 {
192 QTouchEvent touchEvent(QEvent::TouchEnd);
193 homeKeyWatcher.update(&touchEvent);
194 }
195 passTime(1000);
197 QCOMPARE(activatedSpy.count(), expectedActivatedCount);
200void WindowInputMonitorTest::tapWhileTouching()
202 WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
203 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
204 QVERIFY(activatedSpy.isValid());
206 {
207 QTouchEvent touchEvent(QEvent::TouchBegin);
208 homeKeyWatcher.update(&touchEvent);
209 }
210 passTime(1000);
211 {
212 QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier);
213 homeKeyWatcher.update(&keyEvent);
214 }
215 passTime(100);
216 {
217 QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_Super_L, Qt::NoModifier);
218 homeKeyWatcher.update(&keyEvent);
219 }
220 passTime(1000);
221 {
222 QTouchEvent touchEvent(QEvent::TouchEnd);
223 homeKeyWatcher.update(&touchEvent);
224 }
225 passTime(1000);
228 QCOMPARE(activatedSpy.count(), 0);
231/*
232 Press multiple of the mapped home keys roughly at the same time.
233 The first pressed will be recognized and the other ignored
234 */
235void WindowInputMonitorTest::multipleHomeKeys()
237 WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
238 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
239 QVERIFY(activatedSpy.isValid());
241 passTime(1000);
242 {
243 QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_HomePage, Qt::NoModifier);
244 homeKeyWatcher.update(&keyEvent);
245 }
246 passTime(50);
247 {
248 QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier);
249 homeKeyWatcher.update(&keyEvent);
250 }
251 passTime(50);
252 {
253 QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_HomePage, Qt::NoModifier);
254 homeKeyWatcher.update(&keyEvent);
255 }
256 passTime(10);
257 {
258 QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_Super_L, Qt::NoModifier);
259 homeKeyWatcher.update(&keyEvent);
260 }
261 passTime(1000);
263 QCOMPARE(activatedSpy.count(), 1);
266// regression test for lp:1607427
267void WindowInputMonitorTest::repeatedSuperPress()
269 WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
270 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
271 QVERIFY(activatedSpy.isValid());
273 // 1st try
274 passTime(1000);
275 {
276 QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier);
277 homeKeyWatcher.update(&keyEvent);
278 }
279 passTime(50);
280 {
281 QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_Super_L, Qt::NoModifier);
282 homeKeyWatcher.update(&keyEvent);
283 }
284 passTime(1000);
285 QCOMPARE(activatedSpy.count(), 1);
287 // a touch event in between
288 {
289 QTouchEvent touchEvent(QEvent::TouchBegin);
290 homeKeyWatcher.update(&touchEvent);
291 }
292 {
293 QTouchEvent touchEvent(QEvent::TouchUpdate);
294 homeKeyWatcher.update(&touchEvent);
295 }
296 {
297 QTouchEvent touchEvent(QEvent::TouchEnd);
298 homeKeyWatcher.update(&touchEvent);
299 }
301 passTime(1000);
302 // 2nd try
303 activatedSpy.clear();
304 {
305 QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier);
306 homeKeyWatcher.update(&keyEvent);
307 }
308 passTime(50);
309 {
310 QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_Super_L, Qt::NoModifier);
311 homeKeyWatcher.update(&keyEvent);
312 }
313 passTime(1000);
314 QCOMPARE(activatedSpy.count(), 1);
317/////////////////////////////////// FakeTimer //////////////////////////////////
319FakeTimer::FakeTimer(QObject *parent)
320 : UnityUtil::AbstractTimer(parent)
321 , m_interval(0)
322 , m_singleShot(true)
323 , m_isRunning(false)
327void FakeTimer::update()
329 if (!isRunning()) {
330 return;
331 }
333 if (m_nextTimeoutTime <= FakeElapsedTimer::msecsSinceEpoch) {
334 if (isSingleShot()) {
335 stop();
336 } else {
337 m_nextTimeoutTime += interval();
338 }
339 Q_EMIT timeout();
340 }
343void FakeTimer::start()
345 m_isRunning = true;
346 m_nextTimeoutTime = FakeElapsedTimer::msecsSinceEpoch + (qint64)interval();
349void FakeTimer::stop()
351 m_isRunning = false;
354bool FakeTimer::isRunning() const
356 return m_isRunning;
359int FakeTimer::interval() const
361 return m_interval;
364void FakeTimer::setInterval(int msecs)
366 m_interval = msecs;
369bool FakeTimer::isSingleShot() const
371 return m_singleShot;
374void FakeTimer::setSingleShot(bool value)
376 m_singleShot = value;
379/////////////////////////////////// FakeTimerFactory //////////////////////////////////
381FakeTimerFactory::FakeTimerFactory()
385FakeTimerFactory::~FakeTimerFactory()
387 for (int i = 0; i < timers.count(); ++i) {
388 FakeTimer *timer = timers[i].data();
389 if (timer) {
390 delete timer;
391 }
392 }
395void FakeTimerFactory::updateTime(qint64 targetTime)
397 qint64 minTimeoutTime = targetTime;
399 for (int i = 0; i < timers.count(); ++i) {
400 FakeTimer *timer = timers[i].data();
401 if (timer && timer->isRunning() && timer->nextTimeoutTime() < minTimeoutTime) {
402 minTimeoutTime = timer->nextTimeoutTime();
403 }
404 }
406 FakeElapsedTimer::msecsSinceEpoch = minTimeoutTime;
408 for (int i = 0; i < timers.count(); ++i) {
409 FakeTimer *timer = timers[i].data();
410 if (timer) {
411 timer->update();
412 }
413 }
415 if (FakeElapsedTimer::msecsSinceEpoch < targetTime) {
416 updateTime(targetTime);
417 }
420AbstractTimer *FakeTimerFactory::create(QObject *parent)
422 FakeTimer *fakeTimer = new FakeTimer(parent);
424 timers.append(fakeTimer);
426 return fakeTimer;
429QTEST_GUILESS_MAIN(WindowInputMonitorTest)
431#include "WindowInputMonitorTest.moc"