Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtestsupport_gui.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include <private/qguiapplication_p.h>
6#include <private/qeventpoint_p.h>
7
8#include <qpa/qplatformintegration.h>
9#include <qpa/qwindowsysteminterface.h>
10
12#include "qwindow.h"
13
14#include <QtCore/qtestsupport_core.h>
15#include <QtCore/qthread.h>
16#include <QtCore/QDebug>
17
18#if QT_CONFIG(test_gui)
19#include <QtCore/qloggingcategory.h>
20#include <private/qinputdevicemanager_p.h>
21#include <private/qeventpoint_p.h>
22#include <private/qhighdpiscaling_p.h>
23#endif // #if QT_CONFIG(test_gui)
24
26
27/*!
28 \since 5.0
29 \overload
30
31 The \a timeout is in milliseconds.
32*/
33bool QTest::qWaitForWindowActive(QWindow *window, int timeout)
34{
35 return qWaitForWindowActive(window, QDeadlineTimer{timeout, Qt::TimerType::PreciseTimer});
36}
37
38/*!
39 \since 6.10
40
41 Returns \c true, if \a window is active within \a timeout. Otherwise returns \c false.
42
43 The method is useful in tests that call QWindow::show() and rely on the window actually being
44 active (i.e. being visible and having focus) before proceeding.
45
46 \note The method will time out and return \c false if another window prevents \a window from
47 becoming active.
48
49 \note Since focus is an exclusive property, \a window may loose its focus to another window at
50 any time - even after the method has returned \c true.
51
52 \sa qWaitForWindowExposed(), qWaitForWindowFocused(), QWindow::isActive()
53*/
54bool QTest::qWaitForWindowActive(QWindow *window, QDeadlineTimer timeout)
55{
56 using Internal::WaitForResult;
57 if (Q_UNLIKELY(!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation))) {
58 qWarning() << "qWaitForWindowActive was called on a platform that doesn't support window"
59 << "activation. This means there is an error in the test and it should either"
60 << "check for the WindowActivation platform capability before calling"
61 << "qWaitForWindowActivate, use qWaitForWindowExposed instead, or skip the test."
62 << "Falling back to qWaitForWindowExposed.";
63 return qWaitForWindowExposed(window, timeout);
64 }
65 return QTest::qWaitFor([wp = QPointer(window)]() {
66 if (QWindow *w = wp.data(); !w)
67 return WaitForResult::Failed;
68 else
69 return w->isActive() ? WaitForResult::Done : WaitForResult::NotYet;
70 }, timeout);
71}
72
73/*!
74 \since 6.10
75 \overload
76
77 This function uses the default timeout of 5 seconds.
78*/
79bool QTest::qWaitForWindowActive(QWindow *window)
80{
81 return qWaitForWindowActive(window, defaultTryTimeout.load(std::memory_order_relaxed));
82}
83
84/*!
85 \since 6.7
86
87 Returns \c true, if \a window is the focus window within \a timeout. Otherwise returns \c false.
88
89 The method is useful in tests that call QWindow::show() and rely on the window
90 having focus (for receiving keyboard events e.g.) before proceeding.
91
92 \note The method will time out and return \c false if another window prevents \a window from
93 becoming focused.
94
95 \note Since focus is an exclusive property, \a window may loose its focus to another window at
96 any time - even after the method has returned \c true.
97
98 \sa qWaitForWindowExposed(), qWaitForWindowActive(), QGuiApplication::focusWindow()
99*/
100Q_GUI_EXPORT bool QTest::qWaitForWindowFocused(QWindow *window, QDeadlineTimer timeout)
101{
102 using Internal::WaitForResult;
103 return QTest::qWaitFor([wp = QPointer(window)]() {
104 if (QWindow *w = wp.data(); !w)
105 return WaitForResult::Failed;
106 else
107 return qGuiApp->focusWindow() == w ? WaitForResult::Done : WaitForResult::NotYet;
108 }, timeout);
109}
110
111/*!
112 \since 6.10
113 \overload
114
115 This function uses the default timeout of 5 seconds.
116*/
117bool QTest::qWaitForWindowFocused(QWindow *window)
118{
119 return qWaitForWindowFocused(window, defaultTryTimeout.load(std::memory_order_relaxed));
120}
121
122/*!
123 \since 5.0
124 \overload
125
126 The \a timeout is in milliseconds.
127*/
128bool QTest::qWaitForWindowExposed(QWindow *window, int timeout)
129{
130 return qWaitForWindowExposed(window, std::chrono::milliseconds(timeout));
131}
132
133/*!
134 \since 6.10
135
136 Returns \c true, if \a window is exposed within \a timeout. Otherwise returns \c false.
137
138 The method is useful in tests that call QWindow::show() and rely on the window actually being
139 being visible before proceeding.
140
141 \note A window mapped to screen may still not be considered exposed, if the window client area is
142 not visible, e.g. because it is completely covered by other windows.
143 In such cases, the method will time out and return \c false.
144
145 \sa qWaitForWindowActive(), QWindow::isExposed()
146*/
147bool QTest::qWaitForWindowExposed(QWindow *window, QDeadlineTimer timeout)
148{
149 using Internal::WaitForResult;
150 return QTest::qWaitFor([wp = QPointer(window)]() {
151 if (QWindow *w = wp.data(); !w)
152 return WaitForResult::Failed;
153 else
154 return w->isExposed() ? WaitForResult::Done : WaitForResult::NotYet;
155 }, timeout);
156}
157
158/*!
159 \since 6.10
160 \overload
161
162 This function uses the default timeout of 5 seconds.
163*/
164bool QTest::qWaitForWindowExposed(QWindow *window)
165{
166 return qWaitForWindowExposed(window, defaultTryTimeout.load(std::memory_order_relaxed));
167}
168
169namespace QTest {
170
203
205{
206 bool ret = false;
207 if (targetWindow) {
211 if (processEvents)
213 }
214 // A TouchCancel can only be followed by a TouchBegin, so start over.
216 points.clear();
217 return ret;
218}
219
221{
222 if (points.isEmpty())
223 return false;
225 bool ret = false;
226 if (targetWindow)
228 if (processEvents)
231 points.clear();
232 return ret;
233}
234
239
246
253
264
265} // namespace QTest
266
267//
268// W A R N I N G
269// -------------
270//
271// The QtGuiTest namespace is not part of the Qt API. It exists purely as an
272// implementation detail. It may change from version to version without notice,
273// or even be removed.
274//
275// We mean it.
276//
277#if QT_CONFIG(test_gui)
278Q_STATIC_LOGGING_CATEGORY(lcQtGuiTest, "qt.gui.test");
279#define deb qCDebug(lcQtGuiTest)
280
281/*!
282 \internal
283 \return the application's input device manager.
284 \return nullptr and log error, if the application hasn't been initialized.
285 */
286static QInputDeviceManager *inputDeviceManager()
287{
288 if (auto *idm = QGuiApplicationPrivate::inputDeviceManager())
289 return idm;
290
291 deb << "No input device manager present.";
292 return nullptr;
293}
294
295/*!
296 \internal
297 Synthesize keyboard modifier action by passing \a modifiers
298 to the application's input device manager.
299 */
300void QtGuiTest::setKeyboardModifiers(Qt::KeyboardModifiers modifiers)
301{
302 auto *idm = inputDeviceManager();
303 if (Q_UNLIKELY(!idm))
304 return;
305
306 idm->setKeyboardModifiers(modifiers);
307 deb << "Keyboard modifiers synthesized:" << modifiers;
308}
309
310/*!
311 \internal
312 Synthesize user-initiated mouse positioning by passing \a position
313 to the application's input device manager.
314 */
315void QtGuiTest::setCursorPosition(const QPoint &position)
316{
317 auto *idm = inputDeviceManager();
318 if (Q_UNLIKELY(!idm))
319 return;
320
321 idm->setCursorPos(position);
322 deb << "Mouse curser set to" << position;
323}
324
325/*!
326 \internal
327 Synthesize an extended \a key event of \a type, with \a modifiers, \a nativeScanCode,
328 \a nativeVirtualKey and \a text on application level.
329 Log whether the synthesizing has been successful.
330
331 \note
332 The application is expected to propagate the extended key event to its focus window,
333 if one exists.
334 */
335void QtGuiTest::synthesizeExtendedKeyEvent(QEvent::Type type, int key, Qt::KeyboardModifiers modifiers,
336 quint32 nativeScanCode, quint32 nativeVirtualKey,
337 const QString &text)
338{
339 Q_ASSERT_X((type == QEvent::KeyPress
340 || type == QEvent::KeyRelease),
341 Q_FUNC_INFO,
342 "called with invalid QEvent type");
343
344 deb << "Synthesizing key event:" << type << Qt::Key(key) << modifiers << text;
345
346 if (QWindowSystemInterface::handleExtendedKeyEvent(nullptr, type, key, modifiers,
347 nativeScanCode, nativeVirtualKey,
348 modifiers, text, /* autorep = */ false,
349 /* count = */ 0)) {
350
351 // If the key event is a shortcut, it may cause other events to be posted.
352 // => process those.
353 QCoreApplication::sendPostedEvents();
354 deb << "(success)";
355 } else {
356 deb << "(failure)";
357 }
358}
359
360/*!
361 \internal
362 Synthesize a key event \a k of type \a t, with modifiers \a mods, \a text,
363 \a autorep and \a count on application level.
364 Log whether the synthesizing has been successful.
365
366 \note
367 The application is expected to propagate the key event to its focus window,
368 if one exists.
369 */
370bool QtGuiTest::synthesizeKeyEvent(QWindow *window, QEvent::Type t, int k, Qt::KeyboardModifiers mods,
371 const QString & text, bool autorep,
372 ushort count)
373{
374 Q_ASSERT_X((t == QEvent::KeyPress
375 || t == QEvent::KeyRelease),
376 Q_FUNC_INFO,
377 "called with invalid QEvent type");
378
379 deb << "Synthesizing key event:" << t << Qt::Key(k) << mods << text;
380
381 bool result = QWindowSystemInterface::handleKeyEvent(window, t, k, mods, text, autorep, count);
382 if (result) {
383 // If the key event is a shortcut, it may cause other events to be posted.
384 // => process those.
385 QCoreApplication::sendPostedEvents();
386 deb << "(success)";
387 } else {
388 deb << "(failure)";
389 }
390
391 return result;
392}
393
394/*!
395 \internal
396 Synthesize a mouse event of \a type, with \a button at \a position at application level.
397 Respect \a state and \a modifiers.
398
399 The application is expected to
400 \list
401 \li propagate the mouse event to its focus window,
402 if one exists.
403 \li convert a click/release squence into a double click.
404 \endlist
405
406 \note
407 QEvent::MouseButtonDoubleClick can't be explicitly synthesized.
408 */
409void QtGuiTest::synthesizeMouseEvent(const QPointF &position, Qt::MouseButtons state,
410 Qt::MouseButton button, QEvent::Type type,
411 Qt::KeyboardModifiers modifiers)
412{
413 Q_ASSERT_X((type == QEvent::MouseButtonPress
414 || type == QEvent::MouseButtonRelease
415 || type == QEvent::MouseMove),
416 Q_FUNC_INFO,
417 "called with invalid QEvent type");
418
419 deb << "Synthesizing mouse event:" << type << position << button << modifiers;
420
421 if (QWindowSystemInterface::handleMouseEvent(nullptr, position, position, state, button,
422 type, modifiers, Qt::MouseEventNotSynthesized)) {
423 // If the mouse event reacts to a shortcut, it may cause other events to be posted.
424 // => process those.
425 QCoreApplication::processEvents();
426 QCoreApplication::sendPostedEvents();
427
428 deb << "(success)";
429 } else {
430 deb << "(failure)";
431 }
432}
433
434/*!
435 \internal
436 Synthesize a wheel event with \a modifiers and \a rollCount representing the number of
437 roll unit on application level.
438
439 \note
440 The application is expected to handle the wheel event, or propagate it
441 to its focus window, if one exists.
442 */
443void QtGuiTest::synthesizeWheelEvent(int rollCount, Qt::KeyboardModifiers modifiers)
444{
445 deb << "Synthesizing wheel event:" << rollCount << modifiers;
446
447 QPoint position = QCursor::pos();
448 if (QWindowSystemInterface::handleWheelEvent(nullptr, position, position,
449 QPoint(), QPoint(0, -rollCount), modifiers)) {
450
451 // It's unlikely that a shortcut relates to a subsequent wheel event.
452 // But it's not harmful, to send posted events here.
453 QCoreApplication::sendPostedEvents();
454 deb << "(success)";
455 } else {
456 deb << "(failure)";
457 }
458}
459
460/*!
461 \internal
462 \return the number of milliseconds since the QElapsedTimer
463 eventTime was last started.
464*/
465qint64 QtGuiTest::eventTimeElapsed()
466{
467 return QWindowSystemInterfacePrivate::eventTime.elapsed();
468}
469
470/*!
471 \internal
472 Post fake window activation with \a window representing the
473 fake window being activated.
474*/
475void QtGuiTest::postFakeWindowActivation(QWindow *window)
476{
477 Q_ASSERT_X(window,
478 Q_FUNC_INFO,
479 "called with nullptr");
480
481 deb << "Posting fake window activation:" << window;
482
483 QWindowSystemInterfacePrivate::FocusWindowEvent e(window, Qt::OtherFocusReason);
484 QGuiApplicationPrivate::processWindowSystemEvent(&e);
485 QWindowSystemInterface::handleFocusWindowChanged(window);
486}
487
488/*!
489 \internal
490 \return native \a window position from \a value.
491*/
492QPoint QtGuiTest::toNativePixels(const QPoint &value, const QWindow *window)
493{
494 Q_ASSERT_X(window,
495 Q_FUNC_INFO,
496 "called with nullptr");
497
498 deb << "Calculating native pixels: " << value << window;
499 return QHighDpi::toNativePixels<QPoint, QWindow>(value, window);
500}
501
502/*!
503 \internal
504 \return native \a window rectangle from \a value.
505*/
506QRect QtGuiTest::toNativePixels(const QRect &value, const QWindow *window)
507{
508 Q_ASSERT_X(window,
509 Q_FUNC_INFO,
510 "called with nullptr");
511
512 deb << "Calculating native pixels: " << value << window;
513 return QHighDpi::toNativePixels<QRect, QWindow>(value, window);
514}
515
516/*!
517 \internal
518 \return scaling factor of \a window relative to Qt.
519*/
520qreal QtGuiTest::scaleFactor(const QWindow *window)
521{
522 Q_ASSERT_X(window,
523 Q_FUNC_INFO,
524 "called with nullptr");
525
526 deb << "Calculating scaling factor: " << window;
527 return QHighDpiScaling::factor(window);
528}
529
530/*!
531 \internal
532 Set the id of \a p to \a arg.
533*/
534void QtGuiTest::setEventPointId(QEventPoint &p, int arg)
535{
536 QMutableEventPoint::setId(p, arg);
537}
538
539/*!
540 \internal
541 Set the pressure of \a p to \a arg.
542*/
543void QtGuiTest::setEventPointPressure(QEventPoint &p, qreal arg)
544{
545 QMutableEventPoint::setPressure(p, arg);
546}
547
548/*!
549 \internal
550 Set the state of \a p to \a arg.
551*/
552void QtGuiTest::setEventPointState(QEventPoint &p, QEventPoint::State arg)
553{
554 QMutableEventPoint::setState(p, arg);
555}
556
557/*!
558 \internal
559 Set the position of \a p to \a arg.
560*/
561void QtGuiTest::setEventPointPosition(QEventPoint &p, QPointF arg)
562{
563 QMutableEventPoint::setPosition(p, arg);
564}
565
566/*!
567 \internal
568 Set the global position of \a p to \a arg.
569*/
570void QtGuiTest::setEventPointGlobalPosition(QEventPoint &p, QPointF arg)
571{
572 QMutableEventPoint::setGlobalPosition(p, arg);
573}
574
575/*!
576 \internal
577 Set the scene position of \a p to \a arg.
578*/
579void QtGuiTest::setEventPointScenePosition(QEventPoint &p, QPointF arg)
580{
581 QMutableEventPoint::setScenePosition(p, arg);
582}
583
584/*!
585 \internal
586 Set the ellipse diameters of \a p to \a arg.
587*/
588void QtGuiTest::setEventPointEllipseDiameters(QEventPoint &p, QSizeF arg)
589{
590 QMutableEventPoint::setEllipseDiameters(p, arg);
591}
592
593/*!
594 \internal
595 Returns \c true, if the platform supports multiple windows,
596 otherwise \c false;
597*/
598bool QtGuiTest::platformSupportsMultipleWindows()
599{
600 const auto *platformIntegration = QGuiApplicationPrivate::platformIntegration();
601 return platformIntegration->hasCapability(QPlatformIntegration::Capability::MultipleWindows);
602}
603
604#undef deb
605#endif // #if QT_CONFIG(test_gui)
606QT_END_NAMESPACE
Combined button and popup list for selecting options.
Q_GUI_EXPORT bool qWaitForWindowExposed(QWindow *window)
Q_GUI_EXPORT bool qWaitForWindowFocused(QWindow *window)
Q_GUI_EXPORT bool qWaitForWindowActive(QWindow *window, int timeout)
Q_GUI_EXPORT bool qWaitForWindowFocused(QWindow *window, QDeadlineTimer timeout)
Q_GUI_EXPORT bool qWaitForWindowExposed(QWindow *window, int timeout)
Q_GUI_EXPORT bool qWaitForWindowActive(QWindow *window)