1 /*
2 SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6 // Qt
7 #include <QSignalSpy>
8 #include <QTest>
9 // client
10 #include "../../src/client/compositor.h"
11 #include "../../src/client/connection_thread.h"
12 #include "../../src/client/event_queue.h"
13 #include "../../src/client/keyboard.h"
14 #include "../../src/client/registry.h"
15 #include "../../src/client/seat.h"
16 #include "../../src/client/surface.h"
17 #include "../../src/client/textinput.h"
18 // server
19 #include "../../src/server/compositor_interface.h"
20 #include "../../src/server/display.h"
21 #include "../../src/server/seat_interface.h"
22 #include "../../src/server/textinput_interface.h"
23
24 using namespace KWayland::Client;
25 using namespace KWayland::Server;
26
27 class TextInputTest : public QObject
28 {
29 Q_OBJECT
30 private Q_SLOTS:
31 void init();
32 void cleanup();
33
34 void testEnterLeave_data();
35 void testEnterLeave();
36 void testShowHidePanel_data();
37 void testShowHidePanel();
38 void testCursorRectangle_data();
39 void testCursorRectangle();
40 void testPreferredLanguage_data();
41 void testPreferredLanguage();
42 void testReset_data();
43 void testReset();
44 void testSurroundingText_data();
45 void testSurroundingText();
46 void testContentHints_data();
47 void testContentHints();
48 void testContentPurpose_data();
49 void testContentPurpose();
50 void testTextDirection_data();
51 void testTextDirection();
52 void testLanguage_data();
53 void testLanguage();
54 void testKeyEvent_data();
55 void testKeyEvent();
56 void testPreEdit_data();
57 void testPreEdit();
58 void testCommit_data();
59 void testCommit();
60
61 private:
62 SurfaceInterface *waitForSurface();
63 TextInput *createTextInput(TextInputInterfaceVersion version);
64 Display *m_display = nullptr;
65 SeatInterface *m_seatInterface = nullptr;
66 CompositorInterface *m_compositorInterface = nullptr;
67 TextInputManagerInterface *m_textInputManagerV0Interface = nullptr;
68 TextInputManagerInterface *m_textInputManagerV2Interface = nullptr;
69 ConnectionThread *m_connection = nullptr;
70 QThread *m_thread = nullptr;
71 EventQueue *m_queue = nullptr;
72 Seat *m_seat = nullptr;
73 Keyboard *m_keyboard = nullptr;
74 Compositor *m_compositor = nullptr;
75 TextInputManager *m_textInputManagerV0 = nullptr;
76 TextInputManager *m_textInputManagerV2 = nullptr;
77 };
78
79 static const QString s_socketName = QStringLiteral("kwayland-test-text-input-0");
80
init()81 void TextInputTest::init()
82 {
83 delete m_display;
84 m_display = new Display(this);
85 m_display->setSocketName(s_socketName);
86 m_display->start();
87 QVERIFY(m_display->isRunning());
88 m_display->createShm();
89 m_seatInterface = m_display->createSeat();
90 m_seatInterface->setHasKeyboard(true);
91 m_seatInterface->setHasTouch(true);
92 m_seatInterface->create();
93 m_compositorInterface = m_display->createCompositor();
94 m_compositorInterface->create();
95 m_textInputManagerV0Interface = m_display->createTextInputManager(TextInputInterfaceVersion::UnstableV0);
96 m_textInputManagerV0Interface->create();
97 m_textInputManagerV2Interface = m_display->createTextInputManager(TextInputInterfaceVersion::UnstableV2);
98 m_textInputManagerV2Interface->create();
99
100 // setup connection
101 m_connection = new KWayland::Client::ConnectionThread;
102 QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected);
103 QVERIFY(connectedSpy.isValid());
104 m_connection->setSocketName(s_socketName);
105
106 m_thread = new QThread(this);
107 m_connection->moveToThread(m_thread);
108 m_thread->start();
109
110 m_connection->initConnection();
111 QVERIFY(connectedSpy.wait());
112
113 m_queue = new EventQueue(this);
114 m_queue->setup(m_connection);
115
116 Registry registry;
117 QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced);
118 QVERIFY(interfacesAnnouncedSpy.isValid());
119 registry.setEventQueue(m_queue);
120 registry.create(m_connection);
121 QVERIFY(registry.isValid());
122 registry.setup();
123 QVERIFY(interfacesAnnouncedSpy.wait());
124
125 m_seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version, this);
126 QVERIFY(m_seat->isValid());
127 QSignalSpy hasKeyboardSpy(m_seat, &Seat::hasKeyboardChanged);
128 QVERIFY(hasKeyboardSpy.isValid());
129 QVERIFY(hasKeyboardSpy.wait());
130 m_keyboard = m_seat->createKeyboard(this);
131 QVERIFY(m_keyboard->isValid());
132
133 auto compositorInterface = registry.interface(Registry::Interface::Compositor);
134 m_compositor = registry.createCompositor(compositorInterface.name, compositorInterface.version, this);
135 QVERIFY(m_compositor->isValid());
136
137 m_textInputManagerV0 = registry.createTextInputManager(registry.interface(Registry::Interface::TextInputManagerUnstableV0).name,
138 registry.interface(Registry::Interface::TextInputManagerUnstableV0).version,
139 this);
140 QVERIFY(m_textInputManagerV0->isValid());
141
142 m_textInputManagerV2 = registry.createTextInputManager(registry.interface(Registry::Interface::TextInputManagerUnstableV2).name,
143 registry.interface(Registry::Interface::TextInputManagerUnstableV2).version,
144 this);
145 QVERIFY(m_textInputManagerV2->isValid());
146 }
147
cleanup()148 void TextInputTest::cleanup()
149 {
150 #define CLEANUP(variable) \
151 if (variable) { \
152 delete variable; \
153 variable = nullptr; \
154 }
155 CLEANUP(m_textInputManagerV0)
156 CLEANUP(m_textInputManagerV2)
157 CLEANUP(m_keyboard)
158 CLEANUP(m_seat)
159 CLEANUP(m_compositor)
160 CLEANUP(m_queue)
161 if (m_connection) {
162 m_connection->deleteLater();
163 m_connection = nullptr;
164 }
165 if (m_thread) {
166 m_thread->quit();
167 m_thread->wait();
168 delete m_thread;
169 m_thread = nullptr;
170 }
171
172 CLEANUP(m_textInputManagerV0Interface)
173 CLEANUP(m_textInputManagerV2Interface)
174 CLEANUP(m_compositorInterface)
175 CLEANUP(m_seatInterface)
176 CLEANUP(m_display)
177 #undef CLEANUP
178 }
179
waitForSurface()180 SurfaceInterface *TextInputTest::waitForSurface()
181 {
182 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
183 if (!surfaceCreatedSpy.isValid()) {
184 return nullptr;
185 }
186 if (!surfaceCreatedSpy.wait(500)) {
187 return nullptr;
188 }
189 if (surfaceCreatedSpy.count() != 1) {
190 return nullptr;
191 }
192 return surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
193 }
194
createTextInput(TextInputInterfaceVersion version)195 TextInput *TextInputTest::createTextInput(TextInputInterfaceVersion version)
196 {
197 switch (version) {
198 case TextInputInterfaceVersion::UnstableV0:
199 return m_textInputManagerV0->createTextInput(m_seat);
200 case TextInputInterfaceVersion::UnstableV2:
201 return m_textInputManagerV2->createTextInput(m_seat);
202 default:
203 Q_UNREACHABLE();
204 return nullptr;
205 }
206 }
207
testEnterLeave_data()208 void TextInputTest::testEnterLeave_data()
209 {
210 QTest::addColumn<TextInputInterfaceVersion>("version");
211 QTest::addColumn<bool>("updatesDirectly");
212
213 QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0 << false;
214 QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2 << true;
215 }
216
testEnterLeave()217 void TextInputTest::testEnterLeave()
218 {
219 // this test verifies that enter leave are sent correctly
220 QScopedPointer<Surface> surface(m_compositor->createSurface());
221 QFETCH(TextInputInterfaceVersion, version);
222 QScopedPointer<TextInput> textInput(createTextInput(version));
223 auto serverSurface = waitForSurface();
224 QVERIFY(serverSurface);
225 QVERIFY(!textInput.isNull());
226 QSignalSpy enteredSpy(textInput.data(), &TextInput::entered);
227 QVERIFY(enteredSpy.isValid());
228 QSignalSpy leftSpy(textInput.data(), &TextInput::left);
229 QVERIFY(leftSpy.isValid());
230 QSignalSpy textInputChangedSpy(m_seatInterface, &SeatInterface::focusedTextInputChanged);
231 QVERIFY(textInputChangedSpy.isValid());
232
233 // now let's try to enter it
234 QVERIFY(!m_seatInterface->focusedTextInput());
235 QVERIFY(!m_seatInterface->focusedTextInputSurface());
236 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
237 QCOMPARE(m_seatInterface->focusedTextInputSurface(), serverSurface);
238 // text input not yet set for the surface
239 QFETCH(bool, updatesDirectly);
240 QCOMPARE(bool(m_seatInterface->focusedTextInput()), updatesDirectly);
241 QCOMPARE(textInputChangedSpy.isEmpty(), !updatesDirectly);
242 textInput->enable(surface.data());
243 // this should trigger on server side
244 if (!updatesDirectly) {
245 QVERIFY(textInputChangedSpy.wait());
246 }
247 QCOMPARE(textInputChangedSpy.count(), 1);
248 auto serverTextInput = m_seatInterface->focusedTextInput();
249 QVERIFY(serverTextInput);
250 QCOMPARE(serverTextInput->interfaceVersion(), version);
251 QSignalSpy enabledChangedSpy(serverTextInput, &TextInputInterface::enabledChanged);
252 QVERIFY(enabledChangedSpy.isValid());
253 if (updatesDirectly) {
254 QVERIFY(enabledChangedSpy.wait());
255 enabledChangedSpy.clear();
256 }
257 QCOMPARE(serverTextInput->surface().data(), serverSurface);
258 QVERIFY(serverTextInput->isEnabled());
259
260 // and trigger an enter
261 if (enteredSpy.isEmpty()) {
262 QVERIFY(enteredSpy.wait());
263 }
264 QCOMPARE(enteredSpy.count(), 1);
265 QCOMPARE(textInput->enteredSurface(), surface.data());
266
267 // now trigger a leave
268 m_seatInterface->setFocusedKeyboardSurface(nullptr);
269 QCOMPARE(textInputChangedSpy.count(), 2);
270 QVERIFY(!m_seatInterface->focusedTextInput());
271 QVERIFY(leftSpy.wait());
272 QVERIFY(!textInput->enteredSurface());
273 QVERIFY(serverTextInput->isEnabled());
274
275 // if we enter again we should directly get the text input as it's still activated
276 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
277 QCOMPARE(textInputChangedSpy.count(), 3);
278 QVERIFY(m_seatInterface->focusedTextInput());
279 QVERIFY(enteredSpy.wait());
280 QCOMPARE(enteredSpy.count(), 2);
281 QCOMPARE(textInput->enteredSurface(), surface.data());
282 QVERIFY(serverTextInput->isEnabled());
283
284 // let's deactivate on client side
285 textInput->disable(surface.data());
286 QVERIFY(enabledChangedSpy.wait());
287 QCOMPARE(enabledChangedSpy.count(), 1);
288 QVERIFY(!serverTextInput->isEnabled());
289 // does not trigger a leave
290 QCOMPARE(textInputChangedSpy.count(), 3);
291 // should still be the same text input
292 QCOMPARE(m_seatInterface->focusedTextInput(), serverTextInput);
293 // reset
294 textInput->enable(surface.data());
295 QVERIFY(enabledChangedSpy.wait());
296
297 // trigger an enter again and leave, but this
298 // time we try sending an event after the surface is unbound
299 // but not yet destroyed. It should work without errors
300 QCOMPARE(textInput->enteredSurface(), surface.data());
301 connect(serverSurface, &Resource::unbound, [=]() {
302 m_seatInterface->setFocusedKeyboardSurface(nullptr);
303 });
304 // delete the client and wait for the server to catch up
305 QSignalSpy unboundSpy(serverSurface, &QObject::destroyed);
306 surface.reset();
307 QVERIFY(unboundSpy.wait());
308 QVERIFY(leftSpy.wait());
309 QVERIFY(!textInput->enteredSurface());
310 }
311
testShowHidePanel_data()312 void TextInputTest::testShowHidePanel_data()
313 {
314 QTest::addColumn<TextInputInterfaceVersion>("version");
315
316 QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0;
317 QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2;
318 }
319
testShowHidePanel()320 void TextInputTest::testShowHidePanel()
321 {
322 // this test verifies that the requests for show/hide panel work
323 // and that status is properly sent to the client
324 QScopedPointer<Surface> surface(m_compositor->createSurface());
325 auto serverSurface = waitForSurface();
326 QVERIFY(serverSurface);
327 QFETCH(TextInputInterfaceVersion, version);
328 QScopedPointer<TextInput> textInput(createTextInput(version));
329 QVERIFY(!textInput.isNull());
330 textInput->enable(surface.data());
331 m_connection->flush();
332 m_display->dispatchEvents();
333
334 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
335 auto ti = m_seatInterface->focusedTextInput();
336 QVERIFY(ti);
337
338 QSignalSpy showPanelRequestedSpy(ti, &TextInputInterface::requestShowInputPanel);
339 QVERIFY(showPanelRequestedSpy.isValid());
340 QSignalSpy hidePanelRequestedSpy(ti, &TextInputInterface::requestHideInputPanel);
341 QVERIFY(hidePanelRequestedSpy.isValid());
342 QSignalSpy inputPanelStateChangedSpy(textInput.data(), &TextInput::inputPanelStateChanged);
343 QVERIFY(inputPanelStateChangedSpy.isValid());
344
345 QCOMPARE(textInput->isInputPanelVisible(), false);
346 textInput->showInputPanel();
347 QVERIFY(showPanelRequestedSpy.wait());
348 ti->setInputPanelState(true, QRect(0, 0, 0, 0));
349 QVERIFY(inputPanelStateChangedSpy.wait());
350 QCOMPARE(textInput->isInputPanelVisible(), true);
351
352 textInput->hideInputPanel();
353 QVERIFY(hidePanelRequestedSpy.wait());
354 ti->setInputPanelState(false, QRect(0, 0, 0, 0));
355 QVERIFY(inputPanelStateChangedSpy.wait());
356 QCOMPARE(textInput->isInputPanelVisible(), false);
357 }
358
testCursorRectangle_data()359 void TextInputTest::testCursorRectangle_data()
360 {
361 QTest::addColumn<TextInputInterfaceVersion>("version");
362
363 QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0;
364 QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2;
365 }
366
testCursorRectangle()367 void TextInputTest::testCursorRectangle()
368 {
369 // this test verifies that passing the cursor rectangle from client to server works
370 // and that setting visibility state from server to client works
371 QScopedPointer<Surface> surface(m_compositor->createSurface());
372 auto serverSurface = waitForSurface();
373 QVERIFY(serverSurface);
374 QFETCH(TextInputInterfaceVersion, version);
375 QScopedPointer<TextInput> textInput(createTextInput(version));
376 QVERIFY(!textInput.isNull());
377 textInput->enable(surface.data());
378 m_connection->flush();
379 m_display->dispatchEvents();
380
381 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
382 auto ti = m_seatInterface->focusedTextInput();
383 QVERIFY(ti);
384 QCOMPARE(ti->cursorRectangle(), QRect());
385 QSignalSpy cursorRectangleChangedSpy(ti, &TextInputInterface::cursorRectangleChanged);
386 QVERIFY(cursorRectangleChangedSpy.isValid());
387
388 textInput->setCursorRectangle(QRect(10, 20, 30, 40));
389 QVERIFY(cursorRectangleChangedSpy.wait());
390 QCOMPARE(ti->cursorRectangle(), QRect(10, 20, 30, 40));
391 }
392
testPreferredLanguage_data()393 void TextInputTest::testPreferredLanguage_data()
394 {
395 QTest::addColumn<TextInputInterfaceVersion>("version");
396
397 QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0;
398 QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2;
399 }
400
testPreferredLanguage()401 void TextInputTest::testPreferredLanguage()
402 {
403 // this test verifies that passing the preferred language from client to server works
404 QScopedPointer<Surface> surface(m_compositor->createSurface());
405 auto serverSurface = waitForSurface();
406 QVERIFY(serverSurface);
407 QFETCH(TextInputInterfaceVersion, version);
408 QScopedPointer<TextInput> textInput(createTextInput(version));
409 QVERIFY(!textInput.isNull());
410 textInput->enable(surface.data());
411 m_connection->flush();
412 m_display->dispatchEvents();
413
414 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
415 auto ti = m_seatInterface->focusedTextInput();
416 QVERIFY(ti);
417 QVERIFY(ti->preferredLanguage().isEmpty());
418
419 QSignalSpy preferredLanguageChangedSpy(ti, &TextInputInterface::preferredLanguageChanged);
420 QVERIFY(preferredLanguageChangedSpy.isValid());
421 textInput->setPreferredLanguage(QStringLiteral("foo"));
422 QVERIFY(preferredLanguageChangedSpy.wait());
423 QCOMPARE(ti->preferredLanguage(), QStringLiteral("foo").toUtf8());
424 }
425
testReset_data()426 void TextInputTest::testReset_data()
427 {
428 QTest::addColumn<TextInputInterfaceVersion>("version");
429
430 QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0;
431 QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2;
432 }
433
testReset()434 void TextInputTest::testReset()
435 {
436 // this test verifies that the reset request is properly passed from client to server
437 QScopedPointer<Surface> surface(m_compositor->createSurface());
438 auto serverSurface = waitForSurface();
439 QVERIFY(serverSurface);
440 QFETCH(TextInputInterfaceVersion, version);
441 QScopedPointer<TextInput> textInput(createTextInput(version));
442 QVERIFY(!textInput.isNull());
443 textInput->enable(surface.data());
444 m_connection->flush();
445 m_display->dispatchEvents();
446
447 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
448 auto ti = m_seatInterface->focusedTextInput();
449 QVERIFY(ti);
450
451 QSignalSpy resetRequestedSpy(ti, &TextInputInterface::requestReset);
452 QVERIFY(resetRequestedSpy.isValid());
453
454 textInput->reset();
455 QVERIFY(resetRequestedSpy.wait());
456 }
457
testSurroundingText_data()458 void TextInputTest::testSurroundingText_data()
459 {
460 QTest::addColumn<TextInputInterfaceVersion>("version");
461
462 QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0;
463 QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2;
464 }
465
testSurroundingText()466 void TextInputTest::testSurroundingText()
467 {
468 // this test verifies that surrounding text is properly passed around
469 QScopedPointer<Surface> surface(m_compositor->createSurface());
470 auto serverSurface = waitForSurface();
471 QVERIFY(serverSurface);
472 QFETCH(TextInputInterfaceVersion, version);
473 QScopedPointer<TextInput> textInput(createTextInput(version));
474 QVERIFY(!textInput.isNull());
475 textInput->enable(surface.data());
476 m_connection->flush();
477 m_display->dispatchEvents();
478
479 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
480 auto ti = m_seatInterface->focusedTextInput();
481 QVERIFY(ti);
482 QVERIFY(ti->surroundingText().isEmpty());
483 QCOMPARE(ti->surroundingTextCursorPosition(), 0);
484 QCOMPARE(ti->surroundingTextSelectionAnchor(), 0);
485
486 QSignalSpy surroundingTextChangedSpy(ti, &TextInputInterface::surroundingTextChanged);
487 QVERIFY(surroundingTextChangedSpy.isValid());
488
489 textInput->setSurroundingText(QStringLiteral("100 €, 100 $"), 5, 6);
490 QVERIFY(surroundingTextChangedSpy.wait());
491 QCOMPARE(ti->surroundingText(), QStringLiteral("100 €, 100 $").toUtf8());
492 QCOMPARE(ti->surroundingTextCursorPosition(), QStringLiteral("100 €, 100 $").toUtf8().indexOf(','));
493 QCOMPARE(ti->surroundingTextSelectionAnchor(), QStringLiteral("100 €, 100 $").toUtf8().indexOf(' ', ti->surroundingTextCursorPosition()));
494 }
495
testContentHints_data()496 void TextInputTest::testContentHints_data()
497 {
498 QTest::addColumn<TextInputInterfaceVersion>("version");
499 QTest::addColumn<TextInput::ContentHints>("clientHints");
500 QTest::addColumn<TextInputInterface::ContentHints>("serverHints");
501
502 QTest::newRow("completion/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::AutoCompletion)
503 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCompletion);
504 QTest::newRow("Correction/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::AutoCorrection)
505 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCorrection);
506 QTest::newRow("Capitalization/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::AutoCapitalization)
507 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCapitalization);
508 QTest::newRow("Lowercase/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::LowerCase)
509 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::LowerCase);
510 QTest::newRow("Uppercase/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::UpperCase)
511 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::UpperCase);
512 QTest::newRow("Titlecase/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::TitleCase)
513 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::TitleCase);
514 QTest::newRow("HiddenText/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::HiddenText)
515 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::HiddenText);
516 QTest::newRow("SensitiveData/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::SensitiveData)
517 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::SensitiveData);
518 QTest::newRow("Latin/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::Latin)
519 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::Latin);
520 QTest::newRow("Multiline/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentHints(TextInput::ContentHint::MultiLine)
521 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::MultiLine);
522
523 QTest::newRow("autos/v0") << TextInputInterfaceVersion::UnstableV0
524 << (TextInput::ContentHint::AutoCompletion | TextInput::ContentHint::AutoCorrection | TextInput::ContentHint::AutoCapitalization)
525 << (TextInputInterface::ContentHint::AutoCompletion | TextInputInterface::ContentHint::AutoCorrection
526 | TextInputInterface::ContentHint::AutoCapitalization);
527
528 // all has combinations which don't make sense - what's both lowercase and uppercase?
529 QTest::newRow("all/v0") << TextInputInterfaceVersion::UnstableV0
530 << (TextInput::ContentHint::AutoCompletion | TextInput::ContentHint::AutoCorrection | TextInput::ContentHint::AutoCapitalization
531 | TextInput::ContentHint::LowerCase | TextInput::ContentHint::UpperCase | TextInput::ContentHint::TitleCase
532 | TextInput::ContentHint::HiddenText | TextInput::ContentHint::SensitiveData | TextInput::ContentHint::Latin
533 | TextInput::ContentHint::MultiLine)
534 << (TextInputInterface::ContentHint::AutoCompletion | TextInputInterface::ContentHint::AutoCorrection
535 | TextInputInterface::ContentHint::AutoCapitalization | TextInputInterface::ContentHint::LowerCase
536 | TextInputInterface::ContentHint::UpperCase | TextInputInterface::ContentHint::TitleCase
537 | TextInputInterface::ContentHint::HiddenText | TextInputInterface::ContentHint::SensitiveData
538 | TextInputInterface::ContentHint::Latin | TextInputInterface::ContentHint::MultiLine);
539
540 // same for version 2
541
542 QTest::newRow("completion/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::AutoCompletion)
543 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCompletion);
544 QTest::newRow("Correction/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::AutoCorrection)
545 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCorrection);
546 QTest::newRow("Capitalization/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::AutoCapitalization)
547 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::AutoCapitalization);
548 QTest::newRow("Lowercase/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::LowerCase)
549 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::LowerCase);
550 QTest::newRow("Uppercase/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::UpperCase)
551 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::UpperCase);
552 QTest::newRow("Titlecase/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::TitleCase)
553 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::TitleCase);
554 QTest::newRow("HiddenText/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::HiddenText)
555 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::HiddenText);
556 QTest::newRow("SensitiveData/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::SensitiveData)
557 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::SensitiveData);
558 QTest::newRow("Latin/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::Latin)
559 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::Latin);
560 QTest::newRow("Multiline/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentHints(TextInput::ContentHint::MultiLine)
561 << TextInputInterface::ContentHints(TextInputInterface::ContentHint::MultiLine);
562
563 QTest::newRow("autos/v2") << TextInputInterfaceVersion::UnstableV2
564 << (TextInput::ContentHint::AutoCompletion | TextInput::ContentHint::AutoCorrection | TextInput::ContentHint::AutoCapitalization)
565 << (TextInputInterface::ContentHint::AutoCompletion | TextInputInterface::ContentHint::AutoCorrection
566 | TextInputInterface::ContentHint::AutoCapitalization);
567
568 // all has combinations which don't make sense - what's both lowercase and uppercase?
569 QTest::newRow("all/v2") << TextInputInterfaceVersion::UnstableV2
570 << (TextInput::ContentHint::AutoCompletion | TextInput::ContentHint::AutoCorrection | TextInput::ContentHint::AutoCapitalization
571 | TextInput::ContentHint::LowerCase | TextInput::ContentHint::UpperCase | TextInput::ContentHint::TitleCase
572 | TextInput::ContentHint::HiddenText | TextInput::ContentHint::SensitiveData | TextInput::ContentHint::Latin
573 | TextInput::ContentHint::MultiLine)
574 << (TextInputInterface::ContentHint::AutoCompletion | TextInputInterface::ContentHint::AutoCorrection
575 | TextInputInterface::ContentHint::AutoCapitalization | TextInputInterface::ContentHint::LowerCase
576 | TextInputInterface::ContentHint::UpperCase | TextInputInterface::ContentHint::TitleCase
577 | TextInputInterface::ContentHint::HiddenText | TextInputInterface::ContentHint::SensitiveData
578 | TextInputInterface::ContentHint::Latin | TextInputInterface::ContentHint::MultiLine);
579 }
580
testContentHints()581 void TextInputTest::testContentHints()
582 {
583 // this test verifies that content hints are properly passed from client to server
584 QScopedPointer<Surface> surface(m_compositor->createSurface());
585 auto serverSurface = waitForSurface();
586 QVERIFY(serverSurface);
587 QFETCH(TextInputInterfaceVersion, version);
588 QScopedPointer<TextInput> textInput(createTextInput(version));
589 QVERIFY(!textInput.isNull());
590 textInput->enable(surface.data());
591 m_connection->flush();
592 m_display->dispatchEvents();
593
594 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
595 auto ti = m_seatInterface->focusedTextInput();
596 QVERIFY(ti);
597 QCOMPARE(ti->contentHints(), TextInputInterface::ContentHints());
598
599 QSignalSpy contentTypeChangedSpy(ti, &TextInputInterface::contentTypeChanged);
600 QVERIFY(contentTypeChangedSpy.isValid());
601 QFETCH(TextInput::ContentHints, clientHints);
602 textInput->setContentType(clientHints, TextInput::ContentPurpose::Normal);
603 QVERIFY(contentTypeChangedSpy.wait());
604 QTEST(ti->contentHints(), "serverHints");
605
606 // setting to same should not trigger an update
607 textInput->setContentType(clientHints, TextInput::ContentPurpose::Normal);
608 QVERIFY(!contentTypeChangedSpy.wait(100));
609
610 // unsetting should work
611 textInput->setContentType(TextInput::ContentHints(), TextInput::ContentPurpose::Normal);
612 QVERIFY(contentTypeChangedSpy.wait());
613 QCOMPARE(ti->contentHints(), TextInputInterface::ContentHints());
614 }
615
testContentPurpose_data()616 void TextInputTest::testContentPurpose_data()
617 {
618 QTest::addColumn<TextInputInterfaceVersion>("version");
619 QTest::addColumn<TextInput::ContentPurpose>("clientPurpose");
620 QTest::addColumn<TextInputInterface::ContentPurpose>("serverPurpose");
621
622 QTest::newRow("Alpha/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Alpha << TextInputInterface::ContentPurpose::Alpha;
623 QTest::newRow("Digits/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Digits << TextInputInterface::ContentPurpose::Digits;
624 QTest::newRow("Number/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Number << TextInputInterface::ContentPurpose::Number;
625 QTest::newRow("Phone/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Phone << TextInputInterface::ContentPurpose::Phone;
626 QTest::newRow("Url/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Url << TextInputInterface::ContentPurpose::Url;
627 QTest::newRow("Email/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Email << TextInputInterface::ContentPurpose::Email;
628 QTest::newRow("Name/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Name << TextInputInterface::ContentPurpose::Name;
629 QTest::newRow("Password/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Password
630 << TextInputInterface::ContentPurpose::Password;
631 QTest::newRow("Date/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Date << TextInputInterface::ContentPurpose::Date;
632 QTest::newRow("Time/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Time << TextInputInterface::ContentPurpose::Time;
633 QTest::newRow("Datetime/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::DateTime
634 << TextInputInterface::ContentPurpose::DateTime;
635 QTest::newRow("Terminal/v0") << TextInputInterfaceVersion::UnstableV0 << TextInput::ContentPurpose::Terminal
636 << TextInputInterface::ContentPurpose::Terminal;
637
638 QTest::newRow("Alpha/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Alpha << TextInputInterface::ContentPurpose::Alpha;
639 QTest::newRow("Digits/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Digits << TextInputInterface::ContentPurpose::Digits;
640 QTest::newRow("Number/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Number << TextInputInterface::ContentPurpose::Number;
641 QTest::newRow("Phone/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Phone << TextInputInterface::ContentPurpose::Phone;
642 QTest::newRow("Url/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Url << TextInputInterface::ContentPurpose::Url;
643 QTest::newRow("Email/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Email << TextInputInterface::ContentPurpose::Email;
644 QTest::newRow("Name/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Name << TextInputInterface::ContentPurpose::Name;
645 QTest::newRow("Password/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Password
646 << TextInputInterface::ContentPurpose::Password;
647 QTest::newRow("Date/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Date << TextInputInterface::ContentPurpose::Date;
648 QTest::newRow("Time/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Time << TextInputInterface::ContentPurpose::Time;
649 QTest::newRow("Datetime/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::DateTime
650 << TextInputInterface::ContentPurpose::DateTime;
651 QTest::newRow("Terminal/v2") << TextInputInterfaceVersion::UnstableV2 << TextInput::ContentPurpose::Terminal
652 << TextInputInterface::ContentPurpose::Terminal;
653 }
654
testContentPurpose()655 void TextInputTest::testContentPurpose()
656 {
657 // this test verifies that content purpose are properly passed from client to server
658 QScopedPointer<Surface> surface(m_compositor->createSurface());
659 auto serverSurface = waitForSurface();
660 QVERIFY(serverSurface);
661 QFETCH(TextInputInterfaceVersion, version);
662 QScopedPointer<TextInput> textInput(createTextInput(version));
663 QVERIFY(!textInput.isNull());
664 textInput->enable(surface.data());
665 m_connection->flush();
666 m_display->dispatchEvents();
667
668 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
669 auto ti = m_seatInterface->focusedTextInput();
670 QVERIFY(ti);
671 QCOMPARE(ti->contentPurpose(), TextInputInterface::ContentPurpose::Normal);
672
673 QSignalSpy contentTypeChangedSpy(ti, &TextInputInterface::contentTypeChanged);
674 QVERIFY(contentTypeChangedSpy.isValid());
675 QFETCH(TextInput::ContentPurpose, clientPurpose);
676 textInput->setContentType(TextInput::ContentHints(), clientPurpose);
677 QVERIFY(contentTypeChangedSpy.wait());
678 QTEST(ti->contentPurpose(), "serverPurpose");
679
680 // setting to same should not trigger an update
681 textInput->setContentType(TextInput::ContentHints(), clientPurpose);
682 QVERIFY(!contentTypeChangedSpy.wait(100));
683
684 // unsetting should work
685 textInput->setContentType(TextInput::ContentHints(), TextInput::ContentPurpose::Normal);
686 QVERIFY(contentTypeChangedSpy.wait());
687 QCOMPARE(ti->contentPurpose(), TextInputInterface::ContentPurpose::Normal);
688 }
689
testTextDirection_data()690 void TextInputTest::testTextDirection_data()
691 {
692 QTest::addColumn<TextInputInterfaceVersion>("version");
693 QTest::addColumn<Qt::LayoutDirection>("textDirection");
694
695 QTest::newRow("ltr/v0") << TextInputInterfaceVersion::UnstableV0 << Qt::LeftToRight;
696 QTest::newRow("rtl/v0") << TextInputInterfaceVersion::UnstableV0 << Qt::RightToLeft;
697
698 QTest::newRow("ltr/v2") << TextInputInterfaceVersion::UnstableV2 << Qt::LeftToRight;
699 QTest::newRow("rtl/v2") << TextInputInterfaceVersion::UnstableV2 << Qt::RightToLeft;
700 }
701
testTextDirection()702 void TextInputTest::testTextDirection()
703 {
704 // this test verifies that the text direction is sent from server to client
705 QScopedPointer<Surface> surface(m_compositor->createSurface());
706 auto serverSurface = waitForSurface();
707 QVERIFY(serverSurface);
708 QFETCH(TextInputInterfaceVersion, version);
709 QScopedPointer<TextInput> textInput(createTextInput(version));
710 QVERIFY(!textInput.isNull());
711 // default should be auto
712 QCOMPARE(textInput->textDirection(), Qt::LayoutDirectionAuto);
713 textInput->enable(surface.data());
714 m_connection->flush();
715 m_display->dispatchEvents();
716
717 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
718 auto ti = m_seatInterface->focusedTextInput();
719 QVERIFY(ti);
720
721 // let's send the new text direction
722 QSignalSpy textDirectionChangedSpy(textInput.data(), &TextInput::textDirectionChanged);
723 QVERIFY(textDirectionChangedSpy.isValid());
724 QFETCH(Qt::LayoutDirection, textDirection);
725 ti->setTextDirection(textDirection);
726 QVERIFY(textDirectionChangedSpy.wait());
727 QCOMPARE(textInput->textDirection(), textDirection);
728 // setting again should not change
729 ti->setTextDirection(textDirection);
730 QVERIFY(!textDirectionChangedSpy.wait(100));
731
732 // setting back to auto
733 ti->setTextDirection(Qt::LayoutDirectionAuto);
734 QVERIFY(textDirectionChangedSpy.wait());
735 QCOMPARE(textInput->textDirection(), Qt::LayoutDirectionAuto);
736 }
737
testLanguage_data()738 void TextInputTest::testLanguage_data()
739 {
740 QTest::addColumn<TextInputInterfaceVersion>("version");
741
742 QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0;
743 QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2;
744 }
745
testLanguage()746 void TextInputTest::testLanguage()
747 {
748 // this test verifies that language is sent from server to client
749 QScopedPointer<Surface> surface(m_compositor->createSurface());
750 auto serverSurface = waitForSurface();
751 QVERIFY(serverSurface);
752 QFETCH(TextInputInterfaceVersion, version);
753 QScopedPointer<TextInput> textInput(createTextInput(version));
754 QVERIFY(!textInput.isNull());
755 // default should be empty
756 QVERIFY(textInput->language().isEmpty());
757 textInput->enable(surface.data());
758 m_connection->flush();
759 m_display->dispatchEvents();
760
761 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
762 auto ti = m_seatInterface->focusedTextInput();
763 QVERIFY(ti);
764
765 // let's send the new language
766 QSignalSpy langugageChangedSpy(textInput.data(), &TextInput::languageChanged);
767 QVERIFY(langugageChangedSpy.isValid());
768 ti->setLanguage(QByteArrayLiteral("foo"));
769 QVERIFY(langugageChangedSpy.wait());
770 QCOMPARE(textInput->language(), QByteArrayLiteral("foo"));
771 // setting to same should not trigger
772 ti->setLanguage(QByteArrayLiteral("foo"));
773 QVERIFY(!langugageChangedSpy.wait(100));
774 // but to something else should trigger again
775 ti->setLanguage(QByteArrayLiteral("bar"));
776 QVERIFY(langugageChangedSpy.wait());
777 QCOMPARE(textInput->language(), QByteArrayLiteral("bar"));
778 }
779
testKeyEvent_data()780 void TextInputTest::testKeyEvent_data()
781 {
782 QTest::addColumn<TextInputInterfaceVersion>("version");
783
784 QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0;
785 QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2;
786 }
787
testKeyEvent()788 void TextInputTest::testKeyEvent()
789 {
790 qRegisterMetaType<Qt::KeyboardModifiers>();
791 qRegisterMetaType<TextInput::KeyState>();
792 // this test verifies that key events are properly sent to the client
793 QScopedPointer<Surface> surface(m_compositor->createSurface());
794 auto serverSurface = waitForSurface();
795 QVERIFY(serverSurface);
796 QFETCH(TextInputInterfaceVersion, version);
797 QScopedPointer<TextInput> textInput(createTextInput(version));
798 QVERIFY(!textInput.isNull());
799 textInput->enable(surface.data());
800 m_connection->flush();
801 m_display->dispatchEvents();
802
803 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
804 auto ti = m_seatInterface->focusedTextInput();
805 QVERIFY(ti);
806
807 // TODO: test modifiers
808 QSignalSpy keyEventSpy(textInput.data(), &TextInput::keyEvent);
809 QVERIFY(keyEventSpy.isValid());
810 m_seatInterface->setTimestamp(100);
811 ti->keysymPressed(2);
812 QVERIFY(keyEventSpy.wait());
813 QCOMPARE(keyEventSpy.count(), 1);
814 QCOMPARE(keyEventSpy.last().at(0).value<quint32>(), 2u);
815 QCOMPARE(keyEventSpy.last().at(1).value<TextInput::KeyState>(), TextInput::KeyState::Pressed);
816 QCOMPARE(keyEventSpy.last().at(2).value<Qt::KeyboardModifiers>(), Qt::KeyboardModifiers());
817 QCOMPARE(keyEventSpy.last().at(3).value<quint32>(), 100u);
818 m_seatInterface->setTimestamp(101);
819 ti->keysymReleased(2);
820 QVERIFY(keyEventSpy.wait());
821 QCOMPARE(keyEventSpy.count(), 2);
822 QCOMPARE(keyEventSpy.last().at(0).value<quint32>(), 2u);
823 QCOMPARE(keyEventSpy.last().at(1).value<TextInput::KeyState>(), TextInput::KeyState::Released);
824 QCOMPARE(keyEventSpy.last().at(2).value<Qt::KeyboardModifiers>(), Qt::KeyboardModifiers());
825 QCOMPARE(keyEventSpy.last().at(3).value<quint32>(), 101u);
826 }
827
testPreEdit_data()828 void TextInputTest::testPreEdit_data()
829 {
830 QTest::addColumn<TextInputInterfaceVersion>("version");
831
832 QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0;
833 QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2;
834 }
835
testPreEdit()836 void TextInputTest::testPreEdit()
837 {
838 // this test verifies that pre-edit is correctly passed to the client
839 QScopedPointer<Surface> surface(m_compositor->createSurface());
840 auto serverSurface = waitForSurface();
841 QVERIFY(serverSurface);
842 QFETCH(TextInputInterfaceVersion, version);
843 QScopedPointer<TextInput> textInput(createTextInput(version));
844 QVERIFY(!textInput.isNull());
845 // verify default values
846 QVERIFY(textInput->composingText().isEmpty());
847 QVERIFY(textInput->composingFallbackText().isEmpty());
848 QCOMPARE(textInput->composingTextCursorPosition(), 0);
849
850 textInput->enable(surface.data());
851 m_connection->flush();
852 m_display->dispatchEvents();
853
854 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
855 auto ti = m_seatInterface->focusedTextInput();
856 QVERIFY(ti);
857
858 // now let's pass through some pre-edit events
859 QSignalSpy composingTextChangedSpy(textInput.data(), &TextInput::composingTextChanged);
860 QVERIFY(composingTextChangedSpy.isValid());
861 ti->setPreEditCursor(1);
862 ti->preEdit(QByteArrayLiteral("foo"), QByteArrayLiteral("bar"));
863 QVERIFY(composingTextChangedSpy.wait());
864 QCOMPARE(composingTextChangedSpy.count(), 1);
865 QCOMPARE(textInput->composingText(), QByteArrayLiteral("foo"));
866 QCOMPARE(textInput->composingFallbackText(), QByteArrayLiteral("bar"));
867 QCOMPARE(textInput->composingTextCursorPosition(), 1);
868
869 // when no pre edit cursor is sent, it's at end of text
870 ti->preEdit(QByteArrayLiteral("foobar"), QByteArray());
871 QVERIFY(composingTextChangedSpy.wait());
872 QCOMPARE(composingTextChangedSpy.count(), 2);
873 QCOMPARE(textInput->composingText(), QByteArrayLiteral("foobar"));
874 QCOMPARE(textInput->composingFallbackText(), QByteArray());
875 QCOMPARE(textInput->composingTextCursorPosition(), 6);
876 }
877
testCommit_data()878 void TextInputTest::testCommit_data()
879 {
880 QTest::addColumn<TextInputInterfaceVersion>("version");
881
882 QTest::newRow("UnstableV0") << TextInputInterfaceVersion::UnstableV0;
883 QTest::newRow("UnstableV2") << TextInputInterfaceVersion::UnstableV2;
884 }
885
testCommit()886 void TextInputTest::testCommit()
887 {
888 // this test verifies that the commit is handled correctly by the client
889 QScopedPointer<Surface> surface(m_compositor->createSurface());
890 auto serverSurface = waitForSurface();
891 QVERIFY(serverSurface);
892 QFETCH(TextInputInterfaceVersion, version);
893 QScopedPointer<TextInput> textInput(createTextInput(version));
894 QVERIFY(!textInput.isNull());
895 // verify default values
896 QCOMPARE(textInput->commitText(), QByteArray());
897 QCOMPARE(textInput->cursorPosition(), 0);
898 QCOMPARE(textInput->anchorPosition(), 0);
899 QCOMPARE(textInput->deleteSurroundingText().beforeLength, 0u);
900 QCOMPARE(textInput->deleteSurroundingText().afterLength, 0u);
901
902 textInput->enable(surface.data());
903 m_connection->flush();
904 m_display->dispatchEvents();
905
906 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
907 auto ti = m_seatInterface->focusedTextInput();
908 QVERIFY(ti);
909
910 // now let's commit
911 QSignalSpy committedSpy(textInput.data(), &TextInput::committed);
912 QVERIFY(committedSpy.isValid());
913 ti->setCursorPosition(3, 4);
914 ti->deleteSurroundingText(2, 1);
915 ti->commit(QByteArrayLiteral("foo"));
916
917 QVERIFY(committedSpy.wait());
918 QCOMPARE(textInput->commitText(), QByteArrayLiteral("foo"));
919 QCOMPARE(textInput->cursorPosition(), 3);
920 QCOMPARE(textInput->anchorPosition(), 4);
921 QCOMPARE(textInput->deleteSurroundingText().beforeLength, 2u);
922 QCOMPARE(textInput->deleteSurroundingText().afterLength, 1u);
923 }
924
925 QTEST_GUILESS_MAIN(TextInputTest)
926 #include "test_text_input.moc"
927