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