1/****************************************************************************
2**
3** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QTest>
30#include <qbackendnodetester.h>
31#include "testdevice.h"
32
33#include <Qt3DInput/private/actioninput_p.h>
34#include <Qt3DInput/private/inputhandler_p.h>
35#include <Qt3DInput/private/inputmanagers_p.h>
36#include <Qt3DInput/private/inputsequence_p.h>
37#include <Qt3DInput/QActionInput>
38#include <Qt3DInput/QInputSequence>
39
40class tst_InputSequence : public Qt3DCore::QBackendNodeTester
41{
42 Q_OBJECT
43private Q_SLOTS:
44 void shouldMirrorPeerProperties()
45 {
46 // GIVEN
47 Qt3DInput::Input::InputSequence backendInputSequence;
48 Qt3DInput::QInputSequence inputSequence;
49 Qt3DInput::QActionInput actionInput;
50
51 inputSequence.setTimeout(250);
52 inputSequence.setButtonInterval(100);
53 inputSequence.addSequence(input: &actionInput);
54
55 // WHEN
56 simulateInitializationSync(frontend: &inputSequence, backend: &backendInputSequence);
57
58 // THEN
59 QCOMPARE(backendInputSequence.peerId(), inputSequence.id());
60 QCOMPARE(backendInputSequence.isEnabled(), inputSequence.isEnabled());
61 QCOMPARE(backendInputSequence.timeout(), inputSequence.timeout() * 1000000);
62 QCOMPARE(backendInputSequence.buttonInterval(), inputSequence.buttonInterval() * 1000000);
63 QCOMPARE(backendInputSequence.sequences().size(), inputSequence.sequences().size());
64
65 const int inputsCount = backendInputSequence.sequences().size();
66 if (inputsCount > 0) {
67 for (int i = 0; i < inputsCount; ++i)
68 QCOMPARE(backendInputSequence.sequences().at(i), inputSequence.sequences().at(i)->id());
69 }
70 }
71
72 void shouldHaveInitialAndCleanedUpStates()
73 {
74 // GIVEN
75 Qt3DInput::Input::InputSequence backendInputSequence;
76
77 // THEN
78 QVERIFY(backendInputSequence.peerId().isNull());
79 QCOMPARE(backendInputSequence.isEnabled(), false);
80 QCOMPARE(backendInputSequence.timeout(), 0);
81 QCOMPARE(backendInputSequence.buttonInterval(), 0);
82 QCOMPARE(backendInputSequence.sequences().size(), 0);
83
84 // GIVEN
85 Qt3DInput::QInputSequence inputSequence;
86 Qt3DInput::QActionInput actionInput;
87
88 inputSequence.setTimeout(250);
89 inputSequence.setButtonInterval(100);
90 inputSequence.addSequence(input: &actionInput);
91
92 // WHEN
93 simulateInitializationSync(frontend: &inputSequence, backend: &backendInputSequence);
94 backendInputSequence.cleanup();
95
96 // THEN
97 QCOMPARE(backendInputSequence.isEnabled(), false);
98 QCOMPARE(backendInputSequence.timeout(), 0);
99 QCOMPARE(backendInputSequence.buttonInterval(), 0);
100 QCOMPARE(backendInputSequence.sequences().size(), 0);
101 }
102
103 void shouldHandlePropertyChanges()
104 {
105 // GIVEN
106 Qt3DInput::QInputSequence inputSequence;
107 Qt3DInput::Input::InputSequence backendInputSequence;
108 simulateInitializationSync(frontend: &inputSequence, backend: &backendInputSequence);
109
110 // WHEN
111 inputSequence.setTimeout(250);
112 backendInputSequence.syncFromFrontEnd(frontEnd: &inputSequence, firstTime: false);
113
114 // THEN
115 QCOMPARE(backendInputSequence.timeout(), 250000000);
116
117 // WHEN
118 inputSequence.setButtonInterval(150);
119 backendInputSequence.syncFromFrontEnd(frontEnd: &inputSequence, firstTime: false);
120
121 // THEN
122 QCOMPARE(backendInputSequence.buttonInterval(), 150000000);
123
124 // WHEN
125 inputSequence.setEnabled(false);
126 backendInputSequence.syncFromFrontEnd(frontEnd: &inputSequence, firstTime: false);
127
128 // THEN
129 QCOMPARE(backendInputSequence.isEnabled(), false);
130
131 // WHEN
132 Qt3DInput::QActionInput input;
133 const Qt3DCore::QNodeId inputId = input.id();
134 inputSequence.addSequence(input: &input);
135 backendInputSequence.syncFromFrontEnd(frontEnd: &inputSequence, firstTime: false);
136
137 // THEN
138 QCOMPARE(backendInputSequence.sequences().size(), 1);
139 QCOMPARE(backendInputSequence.sequences().first(), inputId);
140
141 // WHEN
142 inputSequence.removeSequence(input: &input);
143 backendInputSequence.syncFromFrontEnd(frontEnd: &inputSequence, firstTime: false);
144
145 // THEN
146 QCOMPARE(backendInputSequence.sequences().size(), 0);
147 }
148
149 void shouldActivateWhenSequenceIsConsumedInOrderOnly()
150 {
151 // GIVEN
152 TestDeviceIntegration deviceIntegration;
153 TestDevice *device = deviceIntegration.createPhysicalDevice(name: "keyboard");
154 TestDeviceBackendNode *deviceBackend = deviceIntegration.physicalDevice(id: device->id());
155 Qt3DInput::Input::InputHandler handler;
156 handler.addInputDeviceIntegration(inputIntegration: &deviceIntegration);
157
158 auto firstInput = new Qt3DInput::QActionInput;
159 firstInput->setButtons(QVector<int>() << Qt::Key_Q << Qt::Key_A);
160 firstInput->setSourceDevice(device);
161 auto backendFirstInput = handler.actionInputManager()->getOrCreateResource(id: firstInput->id());
162 simulateInitializationSync(frontend: firstInput, backend: backendFirstInput);
163
164 auto secondInput = new Qt3DInput::QActionInput;
165 secondInput->setButtons(QVector<int>() << Qt::Key_S << Qt::Key_W);
166 secondInput->setSourceDevice(device);
167 auto backendSecondInput = handler.actionInputManager()->getOrCreateResource(id: secondInput->id());
168 simulateInitializationSync(frontend: secondInput, backend: backendSecondInput);
169
170 auto thirdInput = new Qt3DInput::QActionInput;
171 thirdInput->setButtons(QVector<int>() << Qt::Key_D << Qt::Key_E);
172 thirdInput->setSourceDevice(device);
173 auto backendThirdInput = handler.actionInputManager()->getOrCreateResource(id: thirdInput->id());
174 simulateInitializationSync(frontend: thirdInput, backend: backendThirdInput);
175
176 Qt3DInput::Input::InputSequence backendInputSequence;
177 Qt3DInput::QInputSequence inputSequence;
178 inputSequence.setEnabled(true);
179 inputSequence.setButtonInterval(150);
180 inputSequence.setTimeout(450);
181 inputSequence.addSequence(input: firstInput);
182 inputSequence.addSequence(input: secondInput);
183 inputSequence.addSequence(input: thirdInput);
184 simulateInitializationSync(frontend: &inputSequence, backend: &backendInputSequence);
185
186 // WHEN
187 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Up, pressed: true);
188
189 // THEN
190 QCOMPARE(backendInputSequence.process(&handler, 1000000000), false);
191
192 // WHEN
193 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Up, pressed: false);
194 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: true);
195
196 // THEN
197 QCOMPARE(backendInputSequence.process(&handler, 1100000000), false);
198
199 // WHEN
200 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: false);
201 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: true);
202
203 // THEN
204 QCOMPARE(backendInputSequence.process(&handler, 1200000000), false);
205
206 // WHEN
207 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: false);
208 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_E, pressed: true);
209
210 // THEN
211 QCOMPARE(backendInputSequence.process(&handler, 1300000000), true);
212
213 // WHEN
214 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_E, pressed: false);
215
216 // THEN
217 QCOMPARE(backendInputSequence.process(&handler, 1400000000), false);
218
219 // WHEN
220 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: true);
221
222 // THEN
223 QCOMPARE(backendInputSequence.process(&handler, 1500000000), false);
224
225 // WHEN
226 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: false);
227 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: true);
228
229 // THEN
230 QCOMPARE(backendInputSequence.process(&handler, 1600000000), false);
231
232 // WHEN
233 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: false);
234 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_E, pressed: true);
235
236 // THEN
237 QCOMPARE(backendInputSequence.process(&handler, 1700000000), true);
238
239 // WHEN
240 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_E, pressed: false);
241
242 // THEN
243 QCOMPARE(backendInputSequence.process(&handler, 1800000000), false);
244
245
246 // Now out of order
247
248 // WHEN
249 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: true);
250
251 // THEN
252 QCOMPARE(backendInputSequence.process(&handler, 1900000000), false);
253
254 // WHEN
255 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: false);
256 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: true);
257
258 // THEN
259 QCOMPARE(backendInputSequence.process(&handler, 2000000000), false);
260
261 // WHEN
262 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: false);
263 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_D, pressed: true);
264
265 // THEN
266 QCOMPARE(backendInputSequence.process(&handler, 2100000000), false);
267
268 // WHEN
269 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_D, pressed: false);
270
271 // THEN
272 QCOMPARE(backendInputSequence.process(&handler, 22000000000), false);
273 }
274
275 void shouldRespectSequenceTimeout()
276 {
277 // GIVEN
278 TestDeviceIntegration deviceIntegration;
279 TestDevice *device = deviceIntegration.createPhysicalDevice(name: "keyboard");
280 TestDeviceBackendNode *deviceBackend = deviceIntegration.physicalDevice(id: device->id());
281 Qt3DInput::Input::InputHandler handler;
282 handler.addInputDeviceIntegration(inputIntegration: &deviceIntegration);
283
284 auto firstInput = new Qt3DInput::QActionInput;
285 firstInput->setButtons(QVector<int>() << Qt::Key_Q << Qt::Key_A);
286 firstInput->setSourceDevice(device);
287 auto backendFirstInput = handler.actionInputManager()->getOrCreateResource(id: firstInput->id());
288 simulateInitializationSync(frontend: firstInput, backend: backendFirstInput);
289
290 auto secondInput = new Qt3DInput::QActionInput;
291 secondInput->setButtons(QVector<int>() << Qt::Key_S << Qt::Key_W);
292 secondInput->setSourceDevice(device);
293 auto backendSecondInput = handler.actionInputManager()->getOrCreateResource(id: secondInput->id());
294 simulateInitializationSync(frontend: secondInput, backend: backendSecondInput);
295
296 auto thirdInput = new Qt3DInput::QActionInput;
297 thirdInput->setButtons(QVector<int>() << Qt::Key_D << Qt::Key_E);
298 thirdInput->setSourceDevice(device);
299 auto backendThirdInput = handler.actionInputManager()->getOrCreateResource(id: thirdInput->id());
300 simulateInitializationSync(frontend: thirdInput, backend: backendThirdInput);
301
302 Qt3DInput::Input::InputSequence backendInputSequence;
303 Qt3DInput::QInputSequence inputSequence;
304 inputSequence.setEnabled(true);
305 inputSequence.setButtonInterval(250);
306 inputSequence.setTimeout(450);
307 inputSequence.addSequence(input: firstInput);
308 inputSequence.addSequence(input: secondInput);
309 inputSequence.addSequence(input: thirdInput);
310 simulateInitializationSync(frontend: &inputSequence, backend: &backendInputSequence);
311
312 // WHEN
313 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: true);
314
315 // THEN
316 QCOMPARE(backendInputSequence.process(&handler, 1100000000), false);
317
318 // WHEN
319 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: false);
320 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: true);
321
322 // THEN
323 QCOMPARE(backendInputSequence.process(&handler, 1300000000), false);
324
325 // WHEN
326 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: false);
327 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_E, pressed: true);
328
329 // THEN
330 QCOMPARE(backendInputSequence.process(&handler, 1600000000), false); // Too late
331 }
332
333 void shouldRespectSequenceButtonInterval()
334 {
335 // GIVEN
336 TestDeviceIntegration deviceIntegration;
337 TestDevice *device = deviceIntegration.createPhysicalDevice(name: "keyboard");
338 TestDeviceBackendNode *deviceBackend = deviceIntegration.physicalDevice(id: device->id());
339 Qt3DInput::Input::InputHandler handler;
340 handler.addInputDeviceIntegration(inputIntegration: &deviceIntegration);
341
342 auto firstInput = new Qt3DInput::QActionInput;
343 firstInput->setButtons(QVector<int>() << Qt::Key_Q << Qt::Key_A);
344 firstInput->setSourceDevice(device);
345 auto backendFirstInput = handler.actionInputManager()->getOrCreateResource(id: firstInput->id());
346 simulateInitializationSync(frontend: firstInput, backend: backendFirstInput);
347
348 auto secondInput = new Qt3DInput::QActionInput;
349 secondInput->setButtons(QVector<int>() << Qt::Key_S << Qt::Key_W);
350 secondInput->setSourceDevice(device);
351 auto backendSecondInput = handler.actionInputManager()->getOrCreateResource(id: secondInput->id());
352 simulateInitializationSync(frontend: secondInput, backend: backendSecondInput);
353
354 auto thirdInput = new Qt3DInput::QActionInput;
355 thirdInput->setButtons(QVector<int>() << Qt::Key_D << Qt::Key_E);
356 thirdInput->setSourceDevice(device);
357 auto backendThirdInput = handler.actionInputManager()->getOrCreateResource(id: thirdInput->id());
358 simulateInitializationSync(frontend: thirdInput, backend: backendThirdInput);
359
360 Qt3DInput::Input::InputSequence backendInputSequence;
361 Qt3DInput::QInputSequence inputSequence;
362 inputSequence.setEnabled(true);
363 inputSequence.setButtonInterval(100);
364 inputSequence.setTimeout(450);
365 inputSequence.addSequence(input: firstInput);
366 inputSequence.addSequence(input: secondInput);
367 inputSequence.addSequence(input: thirdInput);
368 simulateInitializationSync(frontend: &inputSequence, backend: &backendInputSequence);
369
370 // WHEN
371 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: true);
372
373 // THEN
374 QCOMPARE(backendInputSequence.process(&handler, 1100000000), false);
375
376 // WHEN
377 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: false);
378 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: true);
379
380 // THEN
381 QCOMPARE(backendInputSequence.process(&handler, 1250000000), false); // Too late
382
383 // WHEN
384 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: false);
385 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_E, pressed: true);
386
387 // THEN
388 QCOMPARE(backendInputSequence.process(&handler, 1300000000), false);
389 }
390
391 void shouldNotProcessWhenDisabled()
392 {
393 // GIVEN
394 TestDeviceIntegration deviceIntegration;
395 TestDevice *device = deviceIntegration.createPhysicalDevice(name: "keyboard");
396 TestDeviceBackendNode *deviceBackend = deviceIntegration.physicalDevice(id: device->id());
397 Qt3DInput::Input::InputHandler handler;
398 handler.addInputDeviceIntegration(inputIntegration: &deviceIntegration);
399
400 auto firstInput = new Qt3DInput::QActionInput;
401 firstInput->setButtons(QVector<int>() << Qt::Key_Q);
402 firstInput->setSourceDevice(device);
403 auto backendFirstInput = handler.actionInputManager()->getOrCreateResource(id: firstInput->id());
404 simulateInitializationSync(frontend: firstInput, backend: backendFirstInput);
405
406 auto secondInput = new Qt3DInput::QActionInput;
407 secondInput->setButtons(QVector<int>() << Qt::Key_S);
408 secondInput->setSourceDevice(device);
409 auto backendSecondInput = handler.actionInputManager()->getOrCreateResource(id: secondInput->id());
410 simulateInitializationSync(frontend: secondInput, backend: backendSecondInput);
411
412 Qt3DInput::Input::InputSequence backendInputSequence;
413 Qt3DInput::QInputSequence inputSequence;
414 inputSequence.setEnabled(false);
415 inputSequence.setButtonInterval(150);
416 inputSequence.setTimeout(450);
417 inputSequence.addSequence(input: firstInput);
418 inputSequence.addSequence(input: secondInput);
419 simulateInitializationSync(frontend: &inputSequence, backend: &backendInputSequence);
420
421 // WHEN
422 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: true);
423
424 // THEN
425 QCOMPARE(backendInputSequence.process(&handler, 1000000000), false);
426
427 // WHEN
428 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_Q, pressed: false);
429 deviceBackend->setButtonPressed(buttonIdentifier: Qt::Key_S, pressed: true);
430
431 // THEN
432 QCOMPARE(backendInputSequence.process(&handler, 1100000000), false);
433 }
434};
435
436QTEST_APPLESS_MAIN(tst_InputSequence)
437
438#include "tst_inputsequence.moc"
439

source code of qt3d/tests/auto/input/inputsequence/tst_inputsequence.cpp