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