KDDockWidgets API Documentation 2.1
Loading...
Searching...
No Matches
TestHelpers_qt.cpp
Go to the documentation of this file.
1/*
2 This file is part of KDDockWidgets.
3
4 SPDX-FileCopyrightText: 2019 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
5 Author: SĂ©rgio Martins <sergio.martins@kdab.com>
6
7 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only
8
9 Contact KDAB at <info@kdab.com> for commercial licensing options.
10*/
11
12#include "Platform.h"
13#include "qtcommon/Window_p.h"
14#include "qtcommon/View.h"
15#include "core/ViewGuard.h"
16#include "core/Utils_p.h"
17
18#include <QString>
19#include <QTimer>
20#include <QDebug>
21#include <QEventLoop>
22#include <QWindow>
23#include <QGuiApplication>
24#include <QElapsedTimer>
25
26using namespace KDDockWidgets;
27using namespace KDDockWidgets::QtCommon;
28
30
31static QtMessageHandler s_original = nullptr;
32
33static bool shouldBlacklistWarning(const QString &msg, const QString &category)
34{
35 if (category == QLatin1String("qt.qpa.xcb"))
36 return true;
37
38 return msg.contains(QLatin1String("QSocketNotifier: Invalid socket"))
39 || msg.contains(QLatin1String("QWindowsWindow::setGeometry"))
40 || msg.contains(QLatin1String("This plugin does not support"))
41 || msg.contains(QLatin1String("Note that Qt no longer ships fonts"))
42 || msg.contains(QLatin1String("Another dock KDDockWidgets::DockWidget"))
43 || msg.contains(
44 QLatin1String("There's multiple MainWindows, not sure what to do about parenting"))
45 || msg.contains(QLatin1String("Testing::"))
46 || msg.contains(QLatin1String("outside any known screen, using primary screen"))
47 || msg.contains(QLatin1String("Populating font family aliases took"))
48 || msg.contains(QLatin1String("QSGThreadedRenderLoop: expose event received for window"))
49 || msg.contains(QLatin1String("Group.qml:0: ReferenceError: parent is not defined"))
50
51 // Ignore benign warning in Material style when deleting a dock widget. Should be fixed in
52 // Qt.
53 || (msg.contains(QLatin1String("TypeError: Cannot read property"))
54 && msg.contains(QLatin1String("Material")));
55}
56
57static void fatalWarningsMessageHandler(QtMsgType t, const QMessageLogContext &context,
58 const QString &msg)
59{
60 if (shouldBlacklistWarning(msg, QLatin1String(context.category)))
61 return;
62 s_original(t, context, msg);
63
64#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
65 // There's a ton of benign warnings in Qt5 QML when app exits, which add maintenance burden.
66 // Please, jump to Qt 6
67 if (Core::Platform::instance()->isQtQuick())
68 return;
69#endif
70
71 if (t == QtWarningMsg) {
72 const std::string expectedWarning = Core::Platform::instance()->m_expectedWarning;
73 if (!expectedWarning.empty() && msg.contains(QString::fromStdString(expectedWarning)))
74 return;
75
76 if (!Platform_qt::isGammaray() && !qEnvironmentVariableIsSet("NO_FATAL")) {
77 std::terminate();
78 }
79 }
80}
81
82static void sleepWithEventLoop(int ms)
83{
84 if (!ms)
85 return;
86
87 QEventLoop loop;
88 QTimer::singleShot(ms, &loop, [&loop] {
89 loop.exit();
90 });
91 loop.exec();
92}
93
94template<typename Func>
95static bool waitFor(Func func, int timeout)
96{
97
98 QElapsedTimer timer;
99 timer.start();
100
101 while (!func()) {
102 if (timer.elapsed() >= timeout)
103 return false;
104
106 }
107
108 return true;
109}
110
111
113class EventFilter : public QObject
114{
115public:
116 EventFilter(QEvent::Type type)
117 : m_type(type)
118 {
119 }
120 ~EventFilter() override;
121 bool eventFilter(QObject *, QEvent *e) override
122 {
123 if (e->type() == m_type)
124 m_got = true;
125
126 return false;
127 }
128
129 const QEvent::Type m_type;
130 bool m_got = false;
131};
132
133EventFilter::~EventFilter() = default;
134
135}
136
137bool Platform_qt::tests_waitForWindowActive(Core::Window::Ptr window, int timeout) const
138{
139 Q_ASSERT(window);
140 auto windowqt = static_cast<Window *>(window.get());
141 QWindow *qwindow = windowqt->qtWindow();
142
143 return Tests::waitFor([qwindow] {
144 return qwindow && qwindow->isActive();
145 },
146 timeout);
147}
148
149bool Platform_qt::tests_waitForEvent(Core::Object *w, QEvent::Type type, int timeout) const
150{
151 Tests::EventFilter filter(type);
152 w->installEventFilter(&filter);
153 QElapsedTimer time;
154 time.start();
155
156 while (!filter.m_got && time.elapsed() < timeout) {
157 qGuiApp->processEvents();
159 }
160
161 return filter.m_got;
162}
163
164bool Platform_qt::tests_waitForEvent(Core::View *view, QEvent::Type type, int timeout) const
165{
166 return tests_waitForEvent(QtCommon::View_qt::asQObject(view), type, timeout);
167}
168
169bool Platform_qt::tests_waitForResize(Core::View *view, int timeout) const
170{
171 return tests_waitForEvent(QtCommon::View_qt::asQObject(view), QEvent::Resize, timeout);
172}
173
174bool Platform_qt::tests_waitForResize(Core::Controller *c, int timeout) const
175{
176 return tests_waitForResize(c->view(), timeout);
177}
178
179bool Platform_qt::tests_waitForEvent(std::shared_ptr<Core::Window> window, QEvent::Type type,
180 int timeout) const
181{
182 auto windowqt = static_cast<Window *>(window.get());
183 return tests_waitForEvent(windowqt->qtWindow(), type, timeout);
184}
185
186bool Platform_qt::tests_waitForDeleted(Core::View *view, int timeout) const
187{
188 QObject *o = view ? QtCommon::View_qt::asQObject(view) : nullptr;
189 if (!o)
190 return true;
191
192 QPointer<QObject> ptr = o;
193 QElapsedTimer time;
194 time.start();
195
196 while (ptr && time.elapsed() < timeout) {
197 qGuiApp->processEvents();
199 }
200
201 const bool wasDeleted = !ptr;
202 return wasDeleted;
203}
204
205
206bool Platform_qt::tests_waitForDeleted(Core::Controller *o, int timeout) const
207{
208 if (!o)
209 return true;
210
211 QPointer<QObject> ptr = o;
212 QElapsedTimer time;
213 time.start();
214
215 while (ptr && time.elapsed() < timeout) {
216 qGuiApp->processEvents();
218 }
219
220 const bool wasDeleted = !ptr;
221 return wasDeleted;
222}
223
224void Platform_qt::tests_doubleClickOn(QPoint globalPos, Core::View *receiver)
225{
226 QCursor::setPos(globalPos);
227 tests_pressOn(globalPos, receiver); // double-click involves an initial press
228
229 MouseEvent ev(Event::MouseButtonDblClick, receiver->mapFromGlobal(globalPos),
230 receiver->rootView()->mapFromGlobal(globalPos), globalPos, Qt::LeftButton,
232
233 if (Platform::instance()->isQtQuick()) {
234 // QtQuick case, we need to send the event to the mouse area
235 auto actualReceiver = static_cast<KDDockWidgets::QtCommon::View_qt *>(receiver)->viewProperty("titleBarMouseArea").value<QObject *>();
236 qGuiApp->sendEvent(actualReceiver, &ev);
237 } else {
238 // QtWidgets case
239 Platform::instance()->sendEvent(receiver, &ev);
240 }
241}
242
243void Platform_qt::installMessageHandler()
244{
246}
247
248void Platform_qt::uninstallMessageHandler()
249{
251 qWarning() << Q_FUNC_INFO
252 << "No message handler was installed or the fatalWarningsMessageHandler was "
253 "already uninstalled!";
254 qInstallMessageHandler(Tests::s_original);
255 Tests::s_original = nullptr;
256}
257
258void Platform_qt::tests_initPlatform_impl()
259{
260 qGuiApp->setOrganizationName(QStringLiteral("KDAB"));
261 qGuiApp->setApplicationName(QStringLiteral("dockwidgets-unit-tests"));
262}
263
264void Platform_qt::tests_deinitPlatform_impl()
265{
266 delete qGuiApp;
267}
268
269/*static*/
270QT_BEGIN_NAMESPACE
271extern quintptr Q_CORE_EXPORT qtHookData[];
272QT_END_NAMESPACE
273bool Platform_qt::isGammaray()
274{
275 static bool is = qtHookData[3] != 0;
276 return is;
277}
278
279bool Platform_qt::tests_wait(int ms) const
280{
282 return true;
283}
284
285void Platform_qt::maybeSetOffscreenQPA(int argc, char **argv)
286{
287 bool qpaPassed = false;
288
289 for (int i = 1; i < argc; ++i) {
290 if (qstrcmp(argv[i], "-platform") == 0) {
291 qpaPassed = true;
292 break;
293 }
294 }
295
296 if (!qpaPassed && !qEnvironmentVariableIsSet("KDDW_NO_OFFSCREEN")) {
297 // Use offscreen by default as it's less annoying, doesn't create visible windows
298 qputenv("QT_QPA_PLATFORM", "offscreen");
299 }
300}
301
302void Platform_qt::tests_pressOn(QPoint globalPos, Core::View *receiver)
303{
304 setCursorPos(globalPos);
305 MouseEvent ev(Event::MouseButtonPress, receiver->mapFromGlobal(globalPos),
306 receiver->rootView()->mapFromGlobal(globalPos), globalPos, Qt::LeftButton,
308 sendEvent(receiver, &ev);
309}
310
311void Platform_qt::tests_pressOn(QPoint globalPos, std::shared_ptr<Core::Window> receiver)
312{
313 Platform::instance()->setCursorPos(globalPos);
314 MouseEvent ev(Event::MouseButtonPress, receiver->mapFromGlobal(globalPos),
315 receiver->mapFromGlobal(globalPos), globalPos, Qt::LeftButton, Qt::LeftButton,
317 qGuiApp->sendEvent(static_cast<Window *>(receiver.get())->qtWindow(), &ev);
318}
319
320bool Platform_qt::tests_releaseOn(QPoint globalPos, Core::View *receiver)
321{
322 MouseEvent ev(Event::MouseButtonRelease, receiver->mapFromGlobal(globalPos),
323 receiver->rootView()->mapFromGlobal(globalPos), globalPos, Qt::LeftButton,
325 Platform::instance()->sendEvent(receiver, &ev);
326
327 return true;
328}
329
330void Platform_qt::tests_doubleClickOn(QPoint globalPos, std::shared_ptr<Core::Window> receiver)
331{
332 Platform::instance()->setCursorPos(globalPos);
333 MouseEvent ev(Event::MouseButtonDblClick, receiver->mapFromGlobal(globalPos),
334 receiver->mapFromGlobal(globalPos), globalPos, Qt::LeftButton, Qt::LeftButton,
336
337 tests_pressOn(globalPos, receiver); // double-click involves an initial press
338 qGuiApp->sendEvent(static_cast<Window *>(receiver.get())->qtWindow(), &ev);
339}
340
341bool Platform_qt::tests_mouseMove(QPoint globalPos, Core::View *receiver)
342{
343 Core::ViewGuard receiverP = receiver;
344 Platform::instance()->setCursorPos(globalPos);
345 MouseEvent ev(Event::MouseMove, receiver->mapFromGlobal(globalPos),
346 receiver->rootView()->mapFromGlobal(globalPos), globalPos, Qt::LeftButton,
348
349 if (!receiverP) {
350 qWarning() << "Receiver was deleted";
351 return false;
352 }
353 Platform::instance()->sendEvent(receiver, &ev);
354 Platform::instance()->tests_wait(2);
355
356 return true;
357}
QT_BEGIN_NAMESPACE quintptr Q_CORE_EXPORT qtHookData[]
View * view() const
Returns the view associated with this controller, if any.
bool isQtQuick() const
Returns whether this platform is QtQuick.
static Platform * instance()
Returns the platform singleton.
This class provides a weak reference to a view i.e., it becomes null automatically once a View is des...
Definition ViewGuard.h:27
virtual std::shared_ptr< View > rootView() const =0
Returns the top-level gui element which this view is inside It's the root view of the window.
virtual Point mapFromGlobal(Point) const =0
void sendEvent(Core::View *, QEvent *) const override
static QObject * asQObject(View *)
static QtMessageHandler s_original
static bool shouldBlacklistWarning(const QString &msg, const QString &category)
static bool waitFor(Func func, int timeout)
static void fatalWarningsMessageHandler(QtMsgType t, const QMessageLogContext &context, const QString &msg)
static void sleepWithEventLoop(int ms)
Class to abstract QAction, so code still works with QtQuick and Flutter.
void setPos(int x, int y)
qint64 elapsed() const const
QEvent::Type type() const const
int exec(QEventLoop::ProcessEventsFlags flags)
void exit(int returnCode)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromStdString(const std::string &str)
NoModifier
LeftButton
bool isActive() const const

© Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
KDDockWidgets
Advanced Dock Widget Framework for Qt
https://www.kdab.com/development-resources/qt-tools/kddockwidgets/
Generated by doxygen 1.9.8