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(&registry, &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