on bionic help sign in
unity8/tests/plugins/Utils/WindowInputMonitorTest.cpp
461 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{0};
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 keyComboInvolvingHome();
94 void repeatedSuperPress();
96private:
97 void passTime(qint64 timeSpanMs);
99 FakeTimerFactory *m_fakeTimerFactory;
100};
102void WindowInputMonitorTest::init()
104 m_fakeTimerFactory = new FakeTimerFactory;
107void WindowInputMonitorTest::cleanup()
109 delete m_fakeTimerFactory;
110 m_fakeTimerFactory = nullptr;
113void WindowInputMonitorTest::passTime(qint64 timeSpanMs)
115 qint64 finalTime = FakeElapsedTimer::msecsSinceEpoch + timeSpanMs;
116 m_fakeTimerFactory->updateTime(finalTime);
119void WindowInputMonitorTest::touchTapTouch_data()
121 QTest::addColumn<int>("silenceBeforeTap");
122 QTest::addColumn<int>("tapDuration");
123 QTest::addColumn<int>("silenceAfterTap");
124 QTest::addColumn<int>("expectedActivatedCount");
125 QTest::addColumn<Qt::Key>("key");
127 QTest::newRow("tap followed by touch") << 2000 << 50 << 10 << 0 << Qt::Key_Super_L;
128 QTest::newRow("touch, tap and touch") << 10 << 50 << 10 << 0 << Qt::Key_Super_L;
129 QTest::newRow("touch followed by tap") << 10 << 50 << 2000 << 0 << Qt::Key_Super_L;
130 QTest::newRow("touch followed by long tap") << 10 << 500 << 2000 << 0 << Qt::Key_Super_L;
131 QTest::newRow("isolated tap, Super_L") << 1000 << 50 << 1000 << 1 << Qt::Key_Super_L;
132 QTest::newRow("isolated tap, HomePage") << 1000 << 50 << 1000 << 1 << Qt::Key_HomePage;
133 QTest::newRow("isolated long press") << 1000 << 200 << 1000 << 0 << Qt::Key_Super_L;
136void WindowInputMonitorTest::touchTapTouch()
138 QFETCH(int, silenceBeforeTap);
139 QFETCH(int, tapDuration);
140 QFETCH(int, silenceAfterTap);
141 QFETCH(int, expectedActivatedCount);
142 QFETCH(Qt::Key, key);
143 WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
144 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
145 QVERIFY(activatedSpy.isValid());
147 {
148 QTouchEvent touchEvent(QEvent::TouchBegin);
149 homeKeyWatcher.update(&touchEvent);
150 }
151 passTime(100);
152 {
153 QTouchEvent touchEvent(QEvent::TouchUpdate);
154 homeKeyWatcher.update(&touchEvent);
155 }
156 passTime(100);
157 {
158 QTouchEvent touchEvent(QEvent::TouchEnd);
159 homeKeyWatcher.update(&touchEvent);
160 }
162 passTime(silenceBeforeTap);
164 {
165 QKeyEvent keyEvent(QEvent::KeyPress, key, Qt::NoModifier);
166 homeKeyWatcher.update(&keyEvent);
167 }
168 int tapTime = 0;
169 while (tapTime < tapDuration) {
170 passTime(10);
171 tapTime += 10;
172 if (tapTime + 10 >= tapDuration) {
173 QKeyEvent keyEvent(QEvent::KeyRelease, key, Qt::NoModifier);
174 homeKeyWatcher.update(&keyEvent);
175 } else {
176 QKeyEvent keyEvent(QEvent::KeyPress, key, Qt::NoModifier, QString(), true /*autorepeat*/);
177 homeKeyWatcher.update(&keyEvent);
178 }
179 }
181 passTime(silenceAfterTap);
183 {
184 QTouchEvent touchEvent(QEvent::TouchBegin);
185 homeKeyWatcher.update(&touchEvent);
186 }
187 passTime(100);
188 {
189 QTouchEvent touchEvent(QEvent::TouchUpdate);
190 homeKeyWatcher.update(&touchEvent);
191 }
192 passTime(100);
193 {
194 QTouchEvent touchEvent(QEvent::TouchEnd);
195 homeKeyWatcher.update(&touchEvent);
196 }
197 passTime(1000);
199 QCOMPARE(activatedSpy.count(), expectedActivatedCount);
202void WindowInputMonitorTest::keyComboInvolvingHome()
204 WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
205 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
206 QVERIFY(activatedSpy.isValid());
208 passTime(1000);
209 {
210 QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier);
211 homeKeyWatcher.update(&keyEvent);
212 }
213 {
214 QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier);
215 homeKeyWatcher.update(&keyEvent);
216 }
217 {
218 QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_A, Qt::NoModifier);
219 homeKeyWatcher.update(&keyEvent);
220 }
221 {
222 QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_Super_L, Qt::NoModifier);
223 homeKeyWatcher.update(&keyEvent);
224 }
225 passTime(1000);
227 QCOMPARE(activatedSpy.count(), 0);
230void WindowInputMonitorTest::tapWhileTouching()
232 WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
233 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
234 QVERIFY(activatedSpy.isValid());
236 {
237 QTouchEvent touchEvent(QEvent::TouchBegin);
238 homeKeyWatcher.update(&touchEvent);
239 }
240 passTime(1000);
241 {
242 QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier);
243 homeKeyWatcher.update(&keyEvent);
244 }
245 passTime(100);
246 {
247 QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_Super_L, Qt::NoModifier);
248 homeKeyWatcher.update(&keyEvent);
249 }
250 passTime(1000);
251 {
252 QTouchEvent touchEvent(QEvent::TouchEnd);
253 homeKeyWatcher.update(&touchEvent);
254 }
255 passTime(1000);
258 QCOMPARE(activatedSpy.count(), 0);
261/*
262 Press multiple of the mapped home keys roughly at the same time.
263 The first pressed will be recognized and the other ignored
264 */
265void WindowInputMonitorTest::multipleHomeKeys()
267 WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
268 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
269 QVERIFY(activatedSpy.isValid());
271 passTime(1000);
272 {
273 QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_HomePage, Qt::NoModifier);
274 homeKeyWatcher.update(&keyEvent);
275 }
276 passTime(50);
277 {
278 QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier);
279 homeKeyWatcher.update(&keyEvent);
280 }
281 passTime(50);
282 {
283 QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_HomePage, Qt::NoModifier);
284 homeKeyWatcher.update(&keyEvent);
285 }
286 passTime(10);
287 {
288 QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_Super_L, Qt::NoModifier);
289 homeKeyWatcher.update(&keyEvent);
290 }
291 passTime(1000);
293 QCOMPARE(activatedSpy.count(), 1);
296// regression test for lp:1607427
297void WindowInputMonitorTest::repeatedSuperPress()
299 WindowInputMonitor homeKeyWatcher(m_fakeTimerFactory->create(this), new FakeElapsedTimer);
300 QSignalSpy activatedSpy(&homeKeyWatcher, &WindowInputMonitor::homeKeyActivated);
301 QVERIFY(activatedSpy.isValid());
303 // 1st try
304 passTime(1000);
305 {
306 QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier);
307 homeKeyWatcher.update(&keyEvent);
308 }
309 passTime(50);
310 {
311 QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_Super_L, Qt::NoModifier);
312 homeKeyWatcher.update(&keyEvent);
313 }
314 passTime(1000);
315 QCOMPARE(activatedSpy.count(), 1);
317 // a touch event in between
318 {
319 QTouchEvent touchEvent(QEvent::TouchBegin);
320 homeKeyWatcher.update(&touchEvent);
321 }
322 {
323 QTouchEvent touchEvent(QEvent::TouchUpdate);
324 homeKeyWatcher.update(&touchEvent);
325 }
326 {
327 QTouchEvent touchEvent(QEvent::TouchEnd);
328 homeKeyWatcher.update(&touchEvent);
329 }
331 passTime(1000);
332 // 2nd try
333 activatedSpy.clear();
334 {
335 QKeyEvent keyEvent(QEvent::KeyPress, Qt::Key_Super_L, Qt::NoModifier);
336 homeKeyWatcher.update(&keyEvent);
337 }
338 passTime(50);
339 {
340 QKeyEvent keyEvent(QEvent::KeyRelease, Qt::Key_Super_L, Qt::NoModifier);
341 homeKeyWatcher.update(&keyEvent);
342 }
343 passTime(1000);
344 QCOMPARE(activatedSpy.count(), 1);
347/////////////////////////////////// FakeTimer //////////////////////////////////
349FakeTimer::FakeTimer(QObject *parent)
350 : UnityUtil::AbstractTimer(parent)
351 , m_interval(0)
352 , m_singleShot(true)
353 , m_isRunning(false)
357void FakeTimer::update()
359 if (!isRunning()) {
360 return;
361 }
363 if (m_nextTimeoutTime <= FakeElapsedTimer::msecsSinceEpoch) {
364 if (isSingleShot()) {
365 stop();
366 } else {
367 m_nextTimeoutTime += interval();
368 }
369 Q_EMIT timeout();
370 }
373void FakeTimer::start()
375 m_isRunning = true;
376 m_nextTimeoutTime = FakeElapsedTimer::msecsSinceEpoch + (qint64)interval();
379void FakeTimer::stop()
381 m_isRunning = false;
384bool FakeTimer::isRunning() const
386 return m_isRunning;
389int FakeTimer::interval() const
391 return m_interval;
394void FakeTimer::setInterval(int msecs)
396 m_interval = msecs;
399bool FakeTimer::isSingleShot() const
401 return m_singleShot;
404void FakeTimer::setSingleShot(bool value)
406 m_singleShot = value;
409/////////////////////////////////// FakeTimerFactory //////////////////////////////////
411FakeTimerFactory::FakeTimerFactory()
415FakeTimerFactory::~FakeTimerFactory()
417 for (int i = 0; i < timers.count(); ++i) {
418 FakeTimer *timer = timers[i].data();
419 if (timer) {
420 delete timer;
421 }
422 }
425void FakeTimerFactory::updateTime(qint64 targetTime)
427 qint64 minTimeoutTime = targetTime;
429 for (int i = 0; i < timers.count(); ++i) {
430 FakeTimer *timer = timers[i].data();
431 if (timer && timer->isRunning() && timer->nextTimeoutTime() < minTimeoutTime) {
432 minTimeoutTime = timer->nextTimeoutTime();
433 }
434 }
436 FakeElapsedTimer::msecsSinceEpoch = minTimeoutTime;
438 for (int i = 0; i < timers.count(); ++i) {
439 FakeTimer *timer = timers[i].data();
440 if (timer) {
441 timer->update();
442 }
443 }
445 if (FakeElapsedTimer::msecsSinceEpoch < targetTime) {
446 updateTime(targetTime);
447 }
450AbstractTimer *FakeTimerFactory::create(QObject *parent)
452 FakeTimer *fakeTimer = new FakeTimer(parent);
454 timers.append(fakeTimer);
456 return fakeTimer;
459QTEST_GUILESS_MAIN(WindowInputMonitorTest)
461#include "WindowInputMonitorTest.moc"