KDDockWidgets API Documentation 2.0
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 (t == QtWarningMsg) {
65 const std::string expectedWarning = Core::Platform::instance()->m_expectedWarning;
66 if (!expectedWarning.empty() && msg.contains(QString::fromStdString(expectedWarning)))
67 return;
68
69 if (!Platform_qt::isGammaray() && !qEnvironmentVariableIsSet("NO_FATAL")) {
70 std::terminate();
71 }
72 }
73}
74
75static void sleepWithEventLoop(int ms)
76{
77 if (!ms)
78 return;
79
80 QEventLoop loop;
81 QTimer::singleShot(ms, &loop, [&loop] {
82 loop.exit();
83 });
84 loop.exec();
85}
86
87template<typename Func>
88static bool waitFor(Func func, int timeout)
89{
90
91 QElapsedTimer timer;
92 timer.start();
93
94 while (!func()) {
95 if (timer.elapsed() >= timeout)
96 return false;
97
99 }
100
101 return true;
102}
103
104
106class EventFilter : public QObject
107{
108public:
109 EventFilter(QEvent::Type type)
110 : m_type(type)
111 {
112 }
113 ~EventFilter() override;
114 bool eventFilter(QObject *, QEvent *e) override
115 {
116 if (e->type() == m_type)
117 m_got = true;
118
119 return false;
120 }
121
122 const QEvent::Type m_type;
123 bool m_got = false;
124};
125
126EventFilter::~EventFilter() = default;
127
128}
129
130bool Platform_qt::tests_waitForWindowActive(Core::Window::Ptr window, int timeout) const
131{
132 Q_ASSERT(window);
133 auto windowqt = static_cast<Window *>(window.get());
134 QWindow *qwindow = windowqt->qtWindow();
135
136 return Tests::waitFor([qwindow] {
137 return qwindow && qwindow->isActive();
138 },
139 timeout);
140}
141
142bool Platform_qt::tests_waitForEvent(Core::Object *w, QEvent::Type type, int timeout) const
143{
144 Tests::EventFilter filter(type);
145 w->installEventFilter(&filter);
146 QElapsedTimer time;
147 time.start();
148
149 while (!filter.m_got && time.elapsed() < timeout) {
150 qGuiApp->processEvents();
152 }
153
154 return filter.m_got;
155}
156
157bool Platform_qt::tests_waitForEvent(Core::View *view, QEvent::Type type, int timeout) const
158{
159 return tests_waitForEvent(QtCommon::View_qt::asQObject(view), type, timeout);
160}
161
162bool Platform_qt::tests_waitForResize(Core::View *view, int timeout) const
163{
164 return tests_waitForEvent(QtCommon::View_qt::asQObject(view), QEvent::Resize, timeout);
165}
166
167bool Platform_qt::tests_waitForResize(Core::Controller *c, int timeout) const
168{
169 return tests_waitForResize(c->view(), timeout);
170}
171
172bool Platform_qt::tests_waitForEvent(std::shared_ptr<Core::Window> window, QEvent::Type type,
173 int timeout) const
174{
175 auto windowqt = static_cast<Window *>(window.get());
176 return tests_waitForEvent(windowqt->qtWindow(), type, timeout);
177}
178
179bool Platform_qt::tests_waitForDeleted(Core::View *view, int timeout) const
180{
181 QObject *o = view ? QtCommon::View_qt::asQObject(view) : nullptr;
182 if (!o)
183 return true;
184
185 QPointer<QObject> ptr = o;
186 QElapsedTimer time;
187 time.start();
188
189 while (ptr && time.elapsed() < timeout) {
190 qGuiApp->processEvents();
192 }
193
194 const bool wasDeleted = !ptr;
195 return wasDeleted;
196}
197
198
199bool Platform_qt::tests_waitForDeleted(Core::Controller *o, int timeout) const
200{
201 if (!o)
202 return true;
203
204 QPointer<QObject> ptr = o;
205 QElapsedTimer time;
206 time.start();
207
208 while (ptr && time.elapsed() < timeout) {
209 qGuiApp->processEvents();
211 }
212
213 const bool wasDeleted = !ptr;
214 return wasDeleted;
215}
216
217void Platform_qt::tests_doubleClickOn(QPoint globalPos, Core::View *receiver)
218{
219 QCursor::setPos(globalPos);
220 tests_pressOn(globalPos, receiver); // double-click involves an initial press
221
222 MouseEvent ev(Event::MouseButtonDblClick, receiver->mapFromGlobal(globalPos),
223 receiver->rootView()->mapFromGlobal(globalPos), globalPos, Qt::LeftButton,
225
226 if (Platform::instance()->isQtQuick()) {
227 // QtQuick case, we need to send the event to the mouse area
228 auto actualReceiver = static_cast<KDDockWidgets::QtCommon::View_qt *>(receiver)->viewProperty("titleBarMouseArea").value<QObject *>();
229 qGuiApp->sendEvent(actualReceiver, &ev);
230 } else {
231 // QtWidgets case
232 Platform::instance()->sendEvent(receiver, &ev);
233 }
234}
235
236void Platform_qt::installMessageHandler()
237{
239}
240
241void Platform_qt::uninstallMessageHandler()
242{
244 qWarning() << Q_FUNC_INFO
245 << "No message handler was installed or the fatalWarningsMessageHandler was "
246 "already uninstalled!";
247 qInstallMessageHandler(Tests::s_original);
248 Tests::s_original = nullptr;
249}
250
251void Platform_qt::tests_initPlatform_impl()
252{
253 qGuiApp->setOrganizationName(QStringLiteral("KDAB"));
254 qGuiApp->setApplicationName(QStringLiteral("dockwidgets-unit-tests"));
255}
256
257void Platform_qt::tests_deinitPlatform_impl()
258{
259 delete qGuiApp;
260}
261
262/*static*/
263QT_BEGIN_NAMESPACE
264extern quintptr Q_CORE_EXPORT qtHookData[];
265QT_END_NAMESPACE
266bool Platform_qt::isGammaray()
267{
268 static bool is = qtHookData[3] != 0;
269 return is;
270}
271
272bool Platform_qt::tests_wait(int ms) const
273{
275 return true;
276}
277
278void Platform_qt::maybeSetOffscreenQPA(int argc, char **argv)
279{
280 bool qpaPassed = false;
281
282 for (int i = 1; i < argc; ++i) {
283 if (qstrcmp(argv[i], "-platform") == 0) {
284 qpaPassed = true;
285 break;
286 }
287 }
288
289 if (!qpaPassed && !qEnvironmentVariableIsSet("KDDW_NO_OFFSCREEN")) {
290 // Use offscreen by default as it's less annoying, doesn't create visible windows
291 qputenv("QT_QPA_PLATFORM", "offscreen");
292 }
293}
294
295void Platform_qt::tests_pressOn(QPoint globalPos, Core::View *receiver)
296{
297 setCursorPos(globalPos);
298 MouseEvent ev(Event::MouseButtonPress, receiver->mapFromGlobal(globalPos),
299 receiver->rootView()->mapFromGlobal(globalPos), globalPos, Qt::LeftButton,
301 sendEvent(receiver, &ev);
302}
303
304void Platform_qt::tests_pressOn(QPoint globalPos, std::shared_ptr<Core::Window> receiver)
305{
306 Platform::instance()->setCursorPos(globalPos);
307 MouseEvent ev(Event::MouseButtonPress, receiver->mapFromGlobal(globalPos),
308 receiver->mapFromGlobal(globalPos), globalPos, Qt::LeftButton, Qt::LeftButton,
310 qGuiApp->sendEvent(static_cast<Window *>(receiver.get())->qtWindow(), &ev);
311}
312
313bool Platform_qt::tests_releaseOn(QPoint globalPos, Core::View *receiver)
314{
315 MouseEvent ev(Event::MouseButtonRelease, receiver->mapFromGlobal(globalPos),
316 receiver->rootView()->mapFromGlobal(globalPos), globalPos, Qt::LeftButton,
318 Platform::instance()->sendEvent(receiver, &ev);
319
320 return true;
321}
322
323void Platform_qt::tests_doubleClickOn(QPoint globalPos, std::shared_ptr<Core::Window> receiver)
324{
325 Platform::instance()->setCursorPos(globalPos);
326 MouseEvent ev(Event::MouseButtonDblClick, receiver->mapFromGlobal(globalPos),
327 receiver->mapFromGlobal(globalPos), globalPos, Qt::LeftButton, Qt::LeftButton,
329
330 tests_pressOn(globalPos, receiver); // double-click involves an initial press
331 qGuiApp->sendEvent(static_cast<Window *>(receiver.get())->qtWindow(), &ev);
332}
333
334bool Platform_qt::tests_mouseMove(QPoint globalPos, Core::View *receiver)
335{
336 Core::ViewGuard receiverP = receiver;
337 Platform::instance()->setCursorPos(globalPos);
338 MouseEvent ev(Event::MouseMove, receiver->mapFromGlobal(globalPos),
339 receiver->rootView()->mapFromGlobal(globalPos), globalPos, Qt::LeftButton,
341
342 if (!receiverP) {
343 qWarning() << "Receiver was deleted";
344 return false;
345 }
346 Platform::instance()->sendEvent(receiver, &ev);
347 Platform::instance()->tests_wait(2);
348
349 return true;
350}
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