Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qmessagespy_p.h
Go to the documentation of this file.
1
// Copyright (C) 2026 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4
#
ifndef
QMESSAGESPY_P_H
5
#
define
QMESSAGESPY_P_H
6
7
#
include
<
QtCore
/
qloggingcategory
.
h
>
8
#
include
<
QtCore
/
qstring
.
h
>
9
#
include
<
QtCore
/
qspan
.
h
>
10
#
include
<
QtCore
/
qmutex
.
h
>
11
#
include
<
QtCore
/
qregularexpression
.
h
>
12
#
include
<
QtTest
/
qtest
.
h
>
13
14
#
include
<
QtCore
/
q20algorithm
.
h
>
15
#
include
<
QtCore
/
q20vector
.
h
>
16
#
include
<
bitset
>
17
#
include
<
chrono
>
18
#
include
<
memory
>
19
#
include
<
variant
>
20
#
include
<
vector
>
21
22
//
23
// W A R N I N G
24
// -------------
25
//
26
// This file is not part of the Qt API. It exists purely as an
27
// implementation detail. This header file may change from version to
28
// version without notice, or even be removed.
29
//
30
// We mean it.
31
//
32
33
QT_BEGIN_NAMESPACE
34
35
namespace
QtMultimediaPrivate
{
36
37
/*!
38
\internal
39
40
QLoggingCategoryEnabler temporarily enables a logging category for all
41
message types, and restores the original state on destruction.
42
43
Usage:
44
\code
45
QLoggingCategoryEnabler enable(qLcMyCategory());
46
// all message types for qLcMyCategory are now active for this scope
47
\endcode
48
*/
49
class
QLoggingCategoryEnabler
50
{
51
Q_DISABLE_COPY_MOVE
(
QLoggingCategoryEnabler
)
52
public
:
53
explicit
QLoggingCategoryEnabler
(
const
QLoggingCategory
&
category
)
54
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
55
:
m_category
(
const_cast
<
QLoggingCategory
&>(
category
))
56
{
57
for
(
int
type
=
QtDebugMsg
;
type
<=
QtFatalMsg
; ++
type
) {
58
auto
msgType
=
static_cast
<
QtMsgType
>(
type
);
59
m_wasEnabled
[
type
] =
category
.
isEnabled
(
msgType
);
60
if
(!
m_wasEnabled
[
type
])
61
m_category
.
setEnabled
(
msgType
,
true
);
62
}
63
}
64
65
~
QLoggingCategoryEnabler
()
66
{
67
for
(
int
type
=
QtDebugMsg
;
type
<=
QtFatalMsg
; ++
type
) {
68
auto
msgType
=
static_cast
<
QtMsgType
>(
type
);
69
if
(!
m_wasEnabled
[
type
])
70
m_category
.
setEnabled
(
msgType
,
false
);
71
}
72
}
73
74
private
:
75
QLoggingCategory
&
m_category
;
76
std::bitset<QtFatalMsg + 1> m_wasEnabled;
77
};
78
79
/*!
80
\internal
81
82
QMessageSpy intercepts Qt logging messages via qInstallMessageHandler.
83
Messages matching a pending expect() are silently consumed; all other
84
messages are forwarded to the previous handler (e.g. QTest's default).
85
86
Usage:
87
\code
88
QMessageSpy spy;
89
90
// Register expectation BEFORE triggering the message:
91
auto token = spy.expect(QtDebugMsg, "expected message");
92
triggerCodeThatLogs();
93
QVERIFY(token.wait());
94
95
// Regex variant:
96
auto token2 = spy.expect(QtWarningMsg, QRegularExpression("file: .*\\.wav"));
97
triggerCode();
98
QVERIFY(token2.wait(2000ms));
99
\endcode
100
101
Multiple waiters are supported simultaneously.
102
*/
103
class
QMessageSpy
104
{
105
Q_DISABLE_COPY_MOVE
(
QMessageSpy
)
106
107
struct
PendingExpect
108
{
109
const
QtMsgType type;
110
const
std::variant<QString, QRegularExpression> matcher;
111
bool
matched =
false
;
112
};
113
114
public
:
115
/*!
116
\internal
117
WaitToken is returned by expect(). Call wait() on it after the
118
message-generating code runs.
119
*/
120
class
WaitToken
121
{
122
public
:
123
WaitToken
() =
delete
;
124
WaitToken
(
WaitToken
&&)
noexcept
=
default
;
125
126
[[nodiscard]]
bool
127
wait
(
std
::chrono::milliseconds timeout = std::chrono::milliseconds{ 5000 })
const
128
{
129
return
QTest::qWaitFor([
this
]() {
130
return
m_state->matched;
131
}, timeout);
132
}
133
134
[[nodiscard]]
bool
matched
()
const
{
return
m_state->matched; }
135
136
private
:
137
friend
class
QMessageSpy
;
138
explicit
WaitToken(
std
::shared_ptr<PendingExpect> state) : m_state(std::move(state))
139
{
140
Q_ASSERT(m_state);
141
}
142
const
std
::shared_ptr<PendingExpect> m_state;
143
};
144
145
explicit
QMessageSpy
(
const
QLoggingCategory &category)
146
:
QMessageSpy
(QLatin1StringView(category.categoryName()))
147
{
148
}
149
150
explicit
QMessageSpy
(QLatin1StringView category = {})
151
:
m_categoryFilter
{
152
category
.
isEmpty
() ?
std
::
nullopt
:
std
::
make_optional
(
QByteArray
(
category
)),
153
}
154
{
155
Q_ASSERT(!s_instance);
156
s_instance =
this
;
157
m_previousHandler = qInstallMessageHandler(messageHandler);
158
}
159
160
~
QMessageSpy
()
161
{
162
qInstallMessageHandler(m_previousHandler);
163
s_instance =
nullptr
;
164
}
165
166
/*!
167
Register an expectation for a message of \a type matching \a message
168
exactly. Returns a WaitToken; call wait() on it after triggering the
169
message. The matching message is consumed (not forwarded to the previous
170
handler).
171
*/
172
[[nodiscard]]
WaitToken
expect
(QtMsgType type,
const
QString &message)
173
{
174
auto
state =
std
::make_shared<PendingExpect>(PendingExpect{ type, message,
false
});
175
std::lock_guard lock(m_mutex);
176
m_pending.push_back(state);
177
return
WaitToken
{
178
std
::move(state),
179
};
180
}
181
182
/*!
183
Register an expectation for a message of \a type matching \a pattern.
184
*/
185
[[nodiscard]]
WaitToken
expect
(QtMsgType type,
const
QRegularExpression &pattern)
186
{
187
auto
state =
188
std
::make_shared<PendingExpect>(PendingExpect{ type, pattern,
false
});
189
std::lock_guard lock(m_mutex);
190
m_pending.push_back(state);
191
return
WaitToken
{
192
std
::move(state),
193
};
194
}
195
196
private
:
197
static
bool
matchesPending(PendingExpect &p, QtMsgType type,
const
QString &msg)
198
{
199
if
(p.matched || p.type != type)
200
return
false
;
201
return
std
::visit([&](
auto
&m) ->
bool
{
202
using
T = std::decay_t<
decltype
(m)>;
203
if
constexpr
(std::is_same_v<T, QString>)
204
return
m == msg;
205
else
206
return
m.match(msg).hasMatch();
207
}, p.matcher);
208
}
209
210
static
void
messageHandler(QtMsgType type,
const
QMessageLogContext &context,
211
const
QString &msg)
212
{
213
Q_ASSERT(s_instance);
214
215
if
(!(s_instance->m_categoryFilter)
216
|| QByteArrayView{ context.category } == s_instance->m_categoryFilter) {
217
std::lock_guard lock(s_instance->m_mutex);
218
bool
doCleanupOnExit =
false
;
219
auto
cleanup = qScopeGuard([&] {
220
if
(!doCleanupOnExit)
221
q20::erase_if(s_instance->m_pending, [](
const
std::weak_ptr<PendingExpect> &p) {
222
auto
locked = p.lock();
223
return
!locked || p.lock()->matched;
224
});
225
});
226
227
// Check pending expects against the raw message first; first match wins and is consumed
228
for
(std::weak_ptr p : s_instance->m_pending) {
229
auto
pending = p.lock();
230
if
(!pending) {
231
doCleanupOnExit =
true
;
232
continue
;
233
}
234
if
(matchesPending(*pending, type, msg)) {
235
pending->matched =
true
;
236
doCleanupOnExit =
true
;
237
return
;
// consumed — do NOT forward
238
}
239
}
240
}
241
242
// Not consumed — forward to previous handler
243
if
(s_instance->m_previousHandler)
244
s_instance->m_previousHandler(type, context, msg);
245
}
246
247
static
inline
QMessageSpy
*s_instance =
nullptr
;
248
249
QtMessageHandler m_previousHandler =
nullptr
;
250
QMutex m_mutex;
251
const
std::optional<QByteArray> m_categoryFilter;
252
std::vector<std::weak_ptr<PendingExpect>> m_pending;
253
};
254
255
}
// namespace QtMultimediaPrivate
256
257
QT_END_NAMESPACE
258
259
#
endif
// QMESSAGESPY_P_H
QtMultimediaPrivate::QLoggingCategoryEnabler
Definition
qmessagespy_p.h:50
QtMultimediaPrivate::QMessageSpy::WaitToken
Definition
qmessagespy_p.h:121
QtMultimediaPrivate::QMessageSpy::WaitToken::WaitToken
WaitToken(WaitToken &&) noexcept=default
QtMultimediaPrivate::QMessageSpy::WaitToken::wait
bool wait(std::chrono::milliseconds timeout=std::chrono::milliseconds{ 5000 }) const
Definition
qmessagespy_p.h:127
QtMultimediaPrivate::QMessageSpy::WaitToken::matched
bool matched() const
Definition
qmessagespy_p.h:134
QtMultimediaPrivate::QMessageSpy::WaitToken::WaitToken
WaitToken()=delete
QtMultimediaPrivate::QMessageSpy
Definition
qmessagespy_p.h:104
QtMultimediaPrivate::QMessageSpy::QMessageSpy
QMessageSpy(const QLoggingCategory &category)
Definition
qmessagespy_p.h:145
QtMultimediaPrivate::QMessageSpy::expect
WaitToken expect(QtMsgType type, const QString &message)
Register an expectation for a message of type matching message exactly.
Definition
qmessagespy_p.h:172
QtMultimediaPrivate::QMessageSpy::~QMessageSpy
~QMessageSpy()
Definition
qmessagespy_p.h:160
QtMultimediaPrivate::QMessageSpy::QMessageSpy
QMessageSpy(QLatin1StringView category={})
Definition
qmessagespy_p.h:150
QtMultimediaPrivate::impl
Definition
qaudiosystem_p.h:257
std
[33]
Definition
src_corelib_tools_qhash.cpp:421
qtmultimedia
src
multimediatestlib
qmessagespy_p.h
Generated on
for Qt by
1.18.0