1/****************************************************************************
2**
3** Copyright (C) 2020 The Qt Company Ltd.
4** Copyright (C) 2014 Olivier Goffart <ogoffart@woboq.com>
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the test suite of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:GPL-EXCEPT$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU
20** General Public License version 3 as published by the Free Software
21** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include <qdebug.h>
31#include <qglobal.h>
32#if QT_CONFIG(process)
33# include <QtCore/QProcess>
34#endif
35#include <QtTest/QTest>
36
37#include <QtCore/QScopeGuard>
38
39class tst_qmessagehandler : public QObject
40{
41 Q_OBJECT
42public:
43 tst_qmessagehandler();
44
45public slots:
46 void initTestCase();
47
48private slots:
49 void cleanup();
50
51 void defaultHandler();
52 void installMessageHandler();
53#if QT_DEPRECATED_SINCE(5, 0)
54 void installMsgHandler();
55 void installBothHandler();
56#endif
57
58#ifdef QT_BUILD_INTERNAL
59 void cleanupFuncinfo_data();
60 void cleanupFuncinfo();
61#endif
62
63 void qMessagePattern_data();
64 void qMessagePattern();
65 void setMessagePattern();
66
67 void formatLogMessage_data();
68 void formatLogMessage();
69
70private:
71 QStringList m_baseEnvironment;
72};
73
74static QtMsgType s_type;
75const char *s_file;
76int s_line;
77const char *s_function;
78static QString s_message;
79
80void customMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
81{
82 s_type = type;
83 s_file = context.file;
84 s_line = context.line;
85 s_function = context.function;
86 s_message = msg;
87}
88
89void customMsgHandler(QtMsgType type, const char *msg)
90{
91 s_type = type;
92 s_file = 0;
93 s_line = 0;
94 s_function = 0;
95 s_message = QString::fromLocal8Bit(str: msg);
96}
97
98tst_qmessagehandler::tst_qmessagehandler()
99{
100 // ensure it's unset, otherwise we'll have trouble
101 qputenv(varName: "QT_MESSAGE_PATTERN", value: "");
102}
103
104void tst_qmessagehandler::initTestCase()
105{
106#if QT_CONFIG(process)
107 m_baseEnvironment = QProcess::systemEnvironment();
108 for (int i = 0; i < m_baseEnvironment.count(); ++i) {
109 if (m_baseEnvironment.at(i).startsWith(s: "QT_MESSAGE_PATTERN=")) {
110 m_baseEnvironment.removeAt(i);
111 break;
112 }
113 }
114#endif // QT_CONFIG(process)
115}
116
117void tst_qmessagehandler::cleanup()
118{
119#if QT_DEPRECATED_SINCE(5, 0)
120 qInstallMsgHandler(0);
121#endif
122 qInstallMessageHandler((QtMessageHandler)0);
123 s_type = QtFatalMsg;
124 s_file = 0;
125 s_line = 0;
126 s_function = 0;
127}
128
129void tst_qmessagehandler::defaultHandler()
130{
131 // check that the default works
132 QTest::ignoreMessage(type: QtDebugMsg, message: "defaultHandler");
133 qDebug(msg: "defaultHandler");
134}
135
136void tst_qmessagehandler::installMessageHandler()
137{
138 QtMessageHandler oldHandler = qInstallMessageHandler(customMessageHandler);
139
140 qDebug(msg: "installMessageHandler"); int line = __LINE__;
141
142 QCOMPARE(s_type, QtDebugMsg);
143 QCOMPARE(s_message, QString::fromLocal8Bit("installMessageHandler"));
144 QCOMPARE(s_file, __FILE__);
145 QCOMPARE(s_function, Q_FUNC_INFO);
146 QCOMPARE(s_line, line);
147
148 QtMessageHandler myHandler = qInstallMessageHandler(oldHandler);
149 QCOMPARE((void*)myHandler, (void*)customMessageHandler);
150}
151
152#if QT_DEPRECATED_SINCE(5, 0)
153void tst_qmessagehandler::installMsgHandler()
154{
155 QtMsgHandler oldHandler = qInstallMsgHandler(customMsgHandler);
156
157 qDebug(msg: "installMsgHandler");
158
159 QCOMPARE(s_type, QtDebugMsg);
160 QCOMPARE(s_message, QString::fromLocal8Bit("installMsgHandler"));
161 QCOMPARE(s_file, (const char*)0);
162 QCOMPARE(s_function, (const char*)0);
163 QCOMPARE(s_line, 0);
164
165 QtMsgHandler myHandler = qInstallMsgHandler(oldHandler);
166 QCOMPARE((void*)myHandler, (void*)customMsgHandler);
167}
168
169void tst_qmessagehandler::installBothHandler()
170{
171 qInstallMessageHandler(customMessageHandler);
172 qInstallMsgHandler(customMsgHandler);
173
174 qDebug(msg: "installBothHandler"); int line = __LINE__;
175
176 QCOMPARE(s_type, QtDebugMsg);
177 QCOMPARE(s_message, QString::fromLocal8Bit("installBothHandler"));
178 QCOMPARE(s_file, __FILE__);
179 QCOMPARE(s_function, Q_FUNC_INFO);
180 QCOMPARE(s_line, line);
181}
182#endif
183
184# define ADD(x) QTest::newRow(x) << Q_FUNC_INFO << x;
185
186class TestClass1
187{
188public:
189 enum Something { foo };
190 char c;
191
192 void func_void() { ADD("TestClass1::func_void"); }
193 int func_int() { ADD("TestClass1::func_int"); return 0; }
194 unsigned func_unsigned() { ADD("TestClass1::func_unsigned"); return 0; }
195 long func_long() { ADD("TestClass1::func_long"); return 0; }
196 long long func_ll() { ADD("TestClass1::func_ll"); return 0; }
197 unsigned long long func_ull() { ADD("TestClass1::func_ull"); return 0; }
198 char func_char() { ADD("TestClass1::func_char"); return 0; }
199 signed char func_schar() { ADD("TestClass1::func_schar"); return 0; }
200 unsigned char func_uchar() { ADD("TestClass1::func_uchar"); return 0; }
201 char &func_Rchar() { ADD("TestClass1::func_Rchar"); return c; }
202 char *func_Pchar() { ADD("TestClass1::func_Pchar"); return 0; }
203 const char *func_KPchar() { ADD("TestClass1::func_KPchar"); return 0; }
204 const volatile char *func_VKPchar() { ADD("TestClass1::func_VKPchar"); return 0; }
205 const volatile unsigned long long * const volatile func_KVPKVull() { ADD("TestClass1::func_KVPKVull"); return 0; }
206 const void * const volatile *func_KPKVvoid() { ADD("TestClass1::func_KPKVvoid"); return 0; }
207
208 QList<int> func_ai() { ADD("TestClass1::func_ai"); return QList<int>(); }
209 QList<unsigned long long const volatile*> func_aptr() { ADD("TestClass1::func_aptr"); return QList<unsigned long long const volatile*>(); }
210
211 QList<Something> func_aenum() { ADD("TestClass1::func_aenum"); return QList<Something>(); }
212 QList<QList<const void *> > func_aaptr() { ADD("TestClass1::func_aaptr"); return QList<QList<const void *> >(); }
213
214 QMap<int, Something> func_ienummap() { ADD("TestClass1::func_ienummap"); return QMap<int, Something>(); }
215
216 template<typename T>
217 T* func_template1() { ADD("TestClass1::func_template1"); return 0; }
218 template<Something val>
219 long func_template2() { ADD("TestClass1::func_template2"); return long(val); }
220
221 typedef unsigned long long * ( *fptr)();
222 typedef unsigned long long * (TestClass1::* pmf)();
223 typedef fptr (TestClass1::* uglypmf)();
224 fptr func_fptr() { ADD("TestClass1::func_fptr"); return 0; }
225 pmf func_pmf() { ADD("TestClass1::func_pmf"); return 0; }
226 uglypmf func_uglypmf(uglypmf = 0) { ADD("TestClass1::func_uglypmf"); return 0; }
227 QMap<QString, uglypmf> func_uglypmf2() { ADD("TestClass1::func_uglypmf2"); return QMap<QString, uglypmf>(); }
228
229 void operator()() { ADD("TestClass1::operator()"); }
230 int operator<(int) { ADD("TestClass1::operator<"); return 0; }
231 int operator>(int) { ADD("TestClass1::operator>"); return 0; }
232 int operator<=(int) { ADD("TestClass1::operator<="); return 0; }
233 int operator>=(int) { ADD("TestClass1::operator>="); return 0; }
234 int operator=(int) { ADD("TestClass1::operator="); return 0; }
235 int operator+(int) { ADD("TestClass1::operator+"); return 0; }
236 int operator-(int) { ADD("TestClass1::operator-"); return 0; }
237 int operator*(int) { ADD("TestClass1::operator*"); return 0; }
238 int operator/(int) { ADD("TestClass1::operator/"); return 0; }
239 int operator%(int) { ADD("TestClass1::operator%"); return 0; }
240 int x;
241 int &operator++() { ADD("TestClass1::operator++"); return x; }
242 int operator++(int) { ADD("TestClass1::operator++"); return 0; }
243 int &operator--() { ADD("TestClass1::operator--"); return x; }
244 int operator--(int) { ADD("TestClass1::operator--"); return 0; }
245
246 int nested_struct()
247 {
248 struct Nested { void nested() { ADD("TestClass1::nested_struct"); } };
249 Nested().nested();
250 return 0;
251 }
252 int nested_struct_const() const
253 {
254 struct Nested { void nested() { ADD("TestClass1::nested_struct_const"); } };
255 Nested().nested();
256 return 0;
257 }
258
259#ifdef Q_COMPILER_REF_QUALIFIERS
260 int lvalue() & { ADD("TestClass1::lvalue"); return 0; }
261 int const_lvalue() const & { ADD("TestClass1::const_lvalue"); return 0; }
262 int rvalue() && { ADD("TestClass1::rvalue"); return 0; }
263 int const_rvalue() const && { ADD("TestClass1::const_rvalue"); return 0; }
264#endif
265 int decltype_param(int x = 0, decltype(x) = 0) { ADD("TestClass1::decltype_param"); return x; }
266 template<typename T> int decltype_template_param(T x = 0, decltype(x) = 0)
267 { ADD("TestClass1::decltype_template_param"); return x; }
268 template<typename T> void decltype_template_param2(T x, decltype(x + QString()))
269 { ADD("TestClass1::decltype_template_param2"); }
270 auto decltype_return(int x = 0) -> decltype(x)
271 { ADD("TestClass1::decltype_return"); return x; }
272 template <typename T> auto decltype_template_return(T x = 0) -> decltype(x)
273 { ADD("TestClass1::decltype_template_return"); return x; }
274
275public:
276 TestClass1()
277 {
278 // instantiate
279 func_void();
280 func_int();
281 func_unsigned();
282 func_long();
283 func_ll();
284 func_ull();
285 func_char();
286 func_schar();
287 func_uchar();
288 func_Rchar();
289 func_Pchar();
290 func_KPchar();
291 func_VKPchar();
292 func_KVPKVull();
293 func_KPKVvoid();
294 func_ai();
295 func_aptr();
296 func_aenum();
297 func_aaptr();
298 func_ienummap();
299 func_template1<TestClass1>();
300 func_template2<foo>();
301 func_fptr();
302 func_pmf();
303 func_uglypmf();
304 func_uglypmf2();
305 operator()();
306 operator<(0);
307 operator>(0);
308 operator<=(0);
309 operator>=(0);
310 operator=(0);
311 operator+(0);
312 operator-(0);
313 operator*(0);
314 operator/(0);
315 operator%(0);
316 operator++();
317 operator++(0);
318 operator--();
319 operator--(0);
320
321 nested_struct();
322 nested_struct_const();
323
324#ifdef Q_COMPILER_REF_QUALIFIERS
325 lvalue();
326 const_lvalue();
327 std::move(*this).rvalue();
328 std::move(*this).const_rvalue();
329#endif
330 decltype_param();
331 decltype_template_param(x: 0);
332 decltype_template_param2(x: QByteArray(), QString());
333 decltype_return();
334 decltype_template_return(x: 0);
335 }
336};
337
338template<typename T> class TestClass2
339{
340 long func_long() { ADD("TestClass2::func_long"); return 0; }
341 template<typename S>
342 T* func_template1() { ADD("TestClass2::func_template1"); return 0; }
343 template<TestClass1::Something val>
344 long func_template2() { ADD("TestClass2::func_template2"); return long(val); }
345public:
346 TestClass2()
347 {
348 func_long();
349 func_template1<TestClass2>();
350 func_template2<TestClass1::foo>();
351 }
352};
353
354template<typename T, TestClass1::Something v> class TestClass3
355{
356 long func_long() { ADD("TestClass3::func_long"); return 0; }
357 template<typename S>
358 S* func_template1() { ADD("TestClass3::func_template1"); return 0; }
359 template<TestClass1::Something val>
360 long func_template2() { ADD("TestClass3::func_template2"); return long(val); }
361public:
362 struct Foo { TestClass3 foo; };
363 TestClass3()
364 {
365 func_long();
366 func_template1<TestClass2<T> >();
367 func_template2<TestClass1::foo>();
368 }
369};
370
371class TestClass4
372{
373 TestClass1 c1;
374
375 TestClass2<std::map<long, const void *> > func2()
376 { ADD("TestClass4::func2"); return TestClass2<std::map<long, const void *> >(); }
377 TestClass3<std::map<std::list<int>, const void *>, TestClass1::foo>::Foo func3()
378 { ADD("TestClass4::func3"); return TestClass3<std::map<std::list<int>, const void *>, TestClass1::foo>::Foo(); }
379public:
380 TestClass4()
381 {
382 func2();
383 func3();
384 ADD("TestClass4::TestClass4");
385 }
386 ~TestClass4()
387 {
388 ADD("TestClass4::~TestClass4");
389 }
390};
391
392
393#ifdef QT_BUILD_INTERNAL
394void tst_qmessagehandler::cleanupFuncinfo_data()
395{
396 QTest::addColumn<QString>(name: "funcinfo");
397 QTest::addColumn<QString>(name: "expected");
398
399 TestClass4 c4;
400
401 QTest::newRow(dataTag: "msvc_01")
402 << "void __thiscall TestClass1::func_void(void)"
403 << "TestClass1::func_void";
404 QTest::newRow(dataTag: "gcc_01")
405 << "void TestClass1::func_void()"
406 << "TestClass1::func_void";
407
408 QTest::newRow(dataTag: "msvc_02")
409 << "int __thiscall TestClass1::func_int(void)"
410 << "TestClass1::func_int";
411 QTest::newRow(dataTag: "gcc_02")
412 << "int TestClass1::func_int()"
413 << "TestClass1::func_int";
414
415 QTest::newRow(dataTag: "msvc_03")
416 << "unsigned int __thiscall TestClass1::func_unsigned(void)"
417 << "TestClass1::func_unsigned";
418 QTest::newRow(dataTag: "gcc_03")
419 << "unsigned int TestClass1::func_unsigned()"
420 << "TestClass1::func_unsigned";
421
422 QTest::newRow(dataTag: "msvc_04")
423 << "long __thiscall TestClass1::func_long(void)"
424 << "TestClass1::func_long";
425 QTest::newRow(dataTag: "gcc_04")
426 << "long int TestClass1::func_long()"
427 << "TestClass1::func_long";
428
429 QTest::newRow(dataTag: "msvc_05")
430 << "__int64 __thiscall TestClass1::func_ll(void)"
431 << "TestClass1::func_ll";
432 QTest::newRow(dataTag: "gcc_05")
433 << "long long int TestClass1::func_ll()"
434 << "TestClass1::func_ll";
435
436 QTest::newRow(dataTag: "msvc_06")
437 << "unsigned __int64 __thiscall TestClass1::func_ull(void)"
438 << "TestClass1::func_ull";
439 QTest::newRow(dataTag: "gcc_06")
440 << "long long unsigned int TestClass1::func_ull()"
441 << "TestClass1::func_ull";
442
443 QTest::newRow(dataTag: "msvc_07")
444 << "char __thiscall TestClass1::func_char(void)"
445 << "TestClass1::func_char";
446 QTest::newRow(dataTag: "gcc_07")
447 << "char TestClass1::func_char()"
448 << "TestClass1::func_char";
449
450 QTest::newRow(dataTag: "msvc_08")
451 << "signed char __thiscall TestClass1::func_schar(void)"
452 << "TestClass1::func_schar";
453 QTest::newRow(dataTag: "gcc_08")
454 << "signed char TestClass1::func_schar()"
455 << "TestClass1::func_schar";
456
457 QTest::newRow(dataTag: "msvc_09")
458 << "unsigned char __thiscall TestClass1::func_uchar(void)"
459 << "TestClass1::func_uchar";
460 QTest::newRow(dataTag: "gcc_09")
461 << "unsigned char TestClass1::func_uchar()"
462 << "TestClass1::func_uchar";
463
464 QTest::newRow(dataTag: "msvc_09a")
465 << "char &__thiscall TestClass1::func_Rchar(void)"
466 << "TestClass1::func_Rchar";
467 QTest::newRow(dataTag: "gcc_09a")
468 << "char& TestClass1::func_Rchar()"
469 << "TestClass1::func_Rchar";
470 QTest::newRow(dataTag: "clang_09a")
471 << "char &TestClass1::func_Rchar()"
472 << "TestClass1::func_Rchar";
473
474 QTest::newRow(dataTag: "msvc_10")
475 << "char *__thiscall TestClass1::func_Pchar(void)"
476 << "TestClass1::func_Pchar";
477 QTest::newRow(dataTag: "gcc_10")
478 << "char* TestClass1::func_Pchar()"
479 << "TestClass1::func_Pchar";
480 QTest::newRow(dataTag: "clang_10")
481 << "char *TestClass1::func_Pchar()"
482 << "TestClass1::func_Pchar";
483
484 QTest::newRow(dataTag: "msvc_11")
485 << "const char *__thiscall TestClass1::func_KPchar(void)"
486 << "TestClass1::func_KPchar";
487 QTest::newRow(dataTag: "gcc_11")
488 << "const char* TestClass1::func_KPchar()"
489 << "TestClass1::func_KPchar";
490
491 QTest::newRow(dataTag: "msvc_12")
492 << "volatile const char *__thiscall TestClass1::func_VKPchar(void)"
493 << "TestClass1::func_VKPchar";
494 QTest::newRow(dataTag: "gcc_12")
495 << "const volatile char* TestClass1::func_VKPchar()"
496 << "TestClass1::func_VKPchar";
497
498 QTest::newRow(dataTag: "msvc_13")
499 << "volatile const unsigned __int64 *volatile const __thiscall TestClass1::func_KVPKVull(void)"
500 << "TestClass1::func_KVPKVull";
501 QTest::newRow(dataTag: "gcc_13")
502 << "const volatile long long unsigned int* const volatile TestClass1::func_KVPKVull()"
503 << "TestClass1::func_KVPKVull";
504
505 QTest::newRow(dataTag: "msvc_14")
506 << "const void *volatile const *__thiscall TestClass1::func_KPKVvoid(void)"
507 << "TestClass1::func_KPKVvoid";
508 QTest::newRow(dataTag: "gcc_14")
509 << "const void* const volatile* TestClass1::func_KPKVvoid()"
510 << "TestClass1::func_KPKVvoid";
511
512 QTest::newRow(dataTag: "msvc_15")
513 << "class QList<int> __thiscall TestClass1::func_ai(void)"
514 << "TestClass1::func_ai";
515 QTest::newRow(dataTag: "gcc_15")
516 << "QList<int> TestClass1::func_ai()"
517 << "TestClass1::func_ai";
518
519 QTest::newRow(dataTag: "msvc_16")
520 << "class QList<unsigned __int64 const volatile *> __thiscall TestClass1::func_aptr(void)"
521 << "TestClass1::func_aptr";
522 QTest::newRow(dataTag: "gcc_16")
523 << "QList<const volatile long long unsigned int*> TestClass1::func_aptr()"
524 << "TestClass1::func_aptr";
525
526 QTest::newRow(dataTag: "msvc_17")
527 << "class QList<enum TestClass1::Something> __thiscall TestClass1::func_aenum(void)"
528 << "TestClass1::func_aenum";
529 QTest::newRow(dataTag: "gcc_17")
530 << "QList<TestClass1::Something> TestClass1::func_aenum()"
531 << "TestClass1::func_aenum";
532
533 QTest::newRow(dataTag: "msvc_18")
534 << "class QList<class QList<void const *> > __thiscall TestClass1::func_aaptr(void)"
535 << "TestClass1::func_aaptr";
536 QTest::newRow(dataTag: "gcc_18")
537 << "QList<QList<const void*> > TestClass1::func_aaptr()"
538 << "TestClass1::func_aaptr";
539
540 QTest::newRow(dataTag: "msvc_19")
541 << "class QMap<int,enum TestClass1::Something> __thiscall TestClass1::func_ienummap(void)"
542 << "TestClass1::func_ienummap";
543 QTest::newRow(dataTag: "gcc_19")
544 << "QMap<int, TestClass1::Something> TestClass1::func_ienummap()"
545 << "TestClass1::func_ienummap";
546
547 QTest::newRow(dataTag: "msvc_20")
548 << "class TestClass1 *__thiscall TestClass1::func_template1<class TestClass1>(void)"
549 << "TestClass1::func_template1";
550 QTest::newRow(dataTag: "gcc_20")
551 << "T* TestClass1::func_template1() [with T = TestClass1]"
552 << "TestClass1::func_template1";
553
554 QTest::newRow(dataTag: "msvc_21")
555 << "long __thiscall TestClass1::func_template2<foo>(void)"
556 << "TestClass1::func_template2";
557 QTest::newRow(dataTag: "gcc_21")
558 << "long int TestClass1::func_template2() [with TestClass1::Something val = foo]"
559 << "TestClass1::func_template2";
560
561 QTest::newRow(dataTag: "msvc_22")
562 << "unsigned __int64 *(__cdecl *__thiscall TestClass1::func_fptr(void))(void)"
563 << "TestClass1::func_fptr";
564 QTest::newRow(dataTag: "gcc_22")
565 << "long long unsigned int* (* TestClass1::func_fptr())()"
566 << "TestClass1::func_fptr";
567
568 QTest::newRow(dataTag: "msvc_23")
569 << "unsigned __int64 *(__thiscall TestClass1::* __thiscall TestClass1::func_pmf(void))(void)"
570 << "TestClass1::func_pmf";
571 QTest::newRow(dataTag: "gcc_23")
572 << "long long unsigned int* (TestClass1::* TestClass1::func_pmf())()"
573 << "TestClass1::func_pmf";
574
575 QTest::newRow(dataTag: "msvc_24")
576 << "unsigned __int64 *(__cdecl *(__thiscall TestClass1::* __thiscall TestClass1::func_uglypmf(unsigned __int64 *(__cdecl *(__thiscall TestClass1::* )(void))(void)))(void))(void)"
577 << "TestClass1::func_uglypmf";
578 QTest::newRow(dataTag: "gcc_24")
579 << "long long unsigned int* (* (TestClass1::* TestClass1::func_uglypmf(long long unsigned int* (* (TestClass1::*)())()))())()"
580 << "TestClass1::func_uglypmf";
581
582 QTest::newRow(dataTag: "msvc_25")
583 << "class QMap<class QString,unsigned __int64 * (__cdecl*(__thiscall TestClass1::*)(void))(void)> __thiscall TestClass1::func_uglypmf2(void)"
584 << "TestClass1::func_uglypmf2";
585 QTest::newRow(dataTag: "gcc_25")
586 << "QMap<QString, long long unsigned int* (* (TestClass1::*)())()> TestClass1::func_uglypmf2()"
587 << "TestClass1::func_uglypmf2";
588
589 QTest::newRow(dataTag: "msvc_26")
590 << "class TestClass2<class std::map<long,void const *,struct std::less<long>,class std::allocator<struct std::pair<long const ,void const *> > > > __thiscall TestClass4::func2(void)"
591 << "TestClass4::func2";
592 QTest::newRow(dataTag: "gcc_26")
593 << "TestClass2<std::map<long int, const void*, std::less<long int>, std::allocator<std::pair<const long int, const void*> > > > TestClass4::func2()"
594 << "TestClass4::func2";
595
596 QTest::newRow(dataTag: "msvc_27")
597 << "long __thiscall TestClass2<class std::map<long,void const *,struct std::less<long>,class std::allocator<struct std::pair<long const ,void const *> > > >::func_long(void)"
598 << "TestClass2::func_long";
599 QTest::newRow(dataTag: "gcc_27")
600 << "long int TestClass2<T>::func_long() [with T = std::map<long int, const void*, std::less<long int>, std::allocator<std::pair<const long int, const void*> > >]"
601 << "TestClass2::func_long";
602
603 QTest::newRow(dataTag: "msvc_28")
604 << "class std::map<long,void const *,struct std::less<long>,class std::allocator<struct std::pair<long const ,void const *> > > *__thiscall TestClass2<class std::map<long,void const *,struct std::less<long>,class std::allocator<struct std::pair<long const ,void const *> > > >::func_template1<class TestClass2<class std::map<long,void const *,struct std::less<long>,class std::allocator<struct std::pair<long const ,void const *> > > >>(void)"
605 << "TestClass2::func_template1";
606 QTest::newRow(dataTag: "gcc_21")
607 << "T* TestClass2<T>::func_template1() [with S = TestClass2<std::map<long int, const void*, std::less<long int>, std::allocator<std::pair<const long int, const void*> > > >, T = std::map<long int, const void*, std::less<long int>, std::allocator<std::pair<const long int, const void*> > >]"
608 << "TestClass2::func_template1";
609
610 QTest::newRow(dataTag: "msvc_29")
611 << "long __thiscall TestClass2<class std::map<long,void const *,struct std::less<long>,class std::allocator<struct std::pair<long const ,void const *> > > >::func_template2<foo>(void)"
612 << "TestClass2::func_template2";
613 QTest::newRow(dataTag: "gcc_29")
614 << "long int TestClass2<T>::func_template2() [with TestClass1::Something val = foo, T = std::map<long int, const void*, std::less<long int>, std::allocator<std::pair<const long int, const void*> > >]"
615 << "TestClass2::func_template2";
616
617 QTest::newRow(dataTag: "msvc_30")
618 << "struct TestClass3<class std::map<class std::list<int,class std::allocator<int> >,void const *,struct std::less<class std::list<int,class std::allocator<int> > >,class std::allocator<struct std::pair<class std::list<int,class std::allocator<int> > const ,void const *> > >,0>::Foo __thiscall TestClass4::func3(void)"
619 << "TestClass4::func3";
620 QTest::newRow(dataTag: "gcc_30")
621 << "TestClass3<std::map<std::list<int, std::allocator<int> >, const void*, std::less<std::list<int, std::allocator<int> > >, std::allocator<std::pair<const std::list<int, std::allocator<int> >, const void*> > >, foo>::Foo TestClass4::func3()"
622 << "TestClass4::func3";
623
624 QTest::newRow(dataTag: "msvc_31")
625 << "long __thiscall TestClass3<class std::map<class std::list<int,class std::allocator<int> >,void const *,struct std::less<class std::list<int,class std::allocator<int> > >,class std::allocator<struct std::pair<class std::list<int,class std::allocator<int> > const ,void const *> > >,0>::func_long(void)"
626 << "TestClass3::func_long";
627 QTest::newRow(dataTag: "gcc_31")
628 << "long int TestClass3<T, v>::func_long() [with T = std::map<std::list<int, std::allocator<int> >, const void*, std::less<std::list<int, std::allocator<int> > >, std::allocator<std::pair<const std::list<int, std::allocator<int> >, const void*> > >, TestClass1::Something v = foo]"
629 << "TestClass3::func_long";
630
631 QTest::newRow(dataTag: "msvc_32")
632 << "class TestClass2<class std::map<class std::list<int,class std::allocator<int> >,void const *,struct std::less<class std::list<int,class std::allocator<int> > >,class std::allocator<struct std::pair<class std::list<int,class std::allocator<int> > const ,void const *> > > > *__thiscall TestClass3<class std::map<class std::list<int,class std::allocator<int> >,void const *,struct std::less<class std::list<int,class std::allocator<int> > >,class std::allocator<struct std::pair<class std::list<int,class std::allocator<int> > const ,void const *> > >,0>::func_template1<class TestClass2<class std::map<class std::list<int,class std::allocator<int> >,void const *,struct std::less<class std::list<int,class std::allocator<int> > >,class std::allocator<struct std::pair<class std::list<int,class std::allocator<int> > const ,void const *> > > >>(void)"
633 << "TestClass3::func_template1";
634 QTest::newRow(dataTag: "gcc_32")
635 << "S* TestClass3<T, v>::func_template1() [with S = TestClass2<std::map<std::list<int, std::allocator<int> >, const void*, std::less<std::list<int, std::allocator<int> > >, std::allocator<std::pair<const std::list<int, std::allocator<int> >, const void*> > > >, T = std::map<std::list<int, std::allocator<int> >, const void*, std::less<std::list<int, std::allocator<int> > >, std::allocator<std::pair<const std::list<int, std::allocator<int> >, const void*> > >, TestClass1::Something v = foo]"
636 << "TestClass3::func_template1";
637
638 QTest::newRow(dataTag: "msvc_33")
639 << "long __thiscall TestClass3<class std::map<class std::list<int,class std::allocator<int> >,void const *,struct std::less<class std::list<int,class std::allocator<int> > >,class std::allocator<struct std::pair<class std::list<int,class std::allocator<int> > const ,void const *> > >,0>::func_template2<foo>(void)"
640 << "TestClass3::func_template2";
641 QTest::newRow(dataTag: "gcc_33")
642 << "long int TestClass3<T, v>::func_template2() [with TestClass1::Something val = foo, T = std::map<std::list<int, std::allocator<int> >, const void*, std::less<std::list<int, std::allocator<int> > >, std::allocator<std::pair<const std::list<int, std::allocator<int> >, const void*> > >, TestClass1::Something v = foo]"
643 << "TestClass3::func_template2";
644
645 QTest::newRow(dataTag: "msvc_34")
646 << "__thiscall TestClass4::TestClass4(void)"
647 << "TestClass4::TestClass4";
648 QTest::newRow(dataTag: "gcc_34")
649 << "TestClass4::TestClass4()"
650 << "TestClass4::TestClass4";
651
652 QTest::newRow(dataTag: "msvc_35")
653 << "__thiscall TestClass4::~TestClass4(void)"
654 << "TestClass4::~TestClass4";
655 QTest::newRow(dataTag: "gcc_35")
656 << "TestClass4::~TestClass4()"
657 << "TestClass4::~TestClass4";
658
659 QTest::newRow(dataTag: "gcc_36")
660 << "void TestClass1::operator()()"
661 << "TestClass1::operator()";
662
663 QTest::newRow(dataTag: "gcc_37")
664 << "long int TestClass1::func_template2() [with TestClass1::Something val = (TestClass1::Something)0u]"
665 << "TestClass1::func_template2";
666
667 QTest::newRow(dataTag: "gcc_38")
668 << "int TestClass1::operator<(int)"
669 << "TestClass1::operator<";
670
671 QTest::newRow(dataTag: "gcc_39")
672 << "int TestClass1::operator>(int)"
673 << "TestClass1::operator>";
674
675 QTest::newRow(dataTag: "objc_1")
676 << "-[SomeClass someMethod:withArguments:]"
677 << "-[SomeClass someMethod:withArguments:]";
678
679 QTest::newRow(dataTag: "objc_2")
680 << "+[SomeClass withClassMethod:withArguments:]"
681 << "+[SomeClass withClassMethod:withArguments:]";
682
683 QTest::newRow(dataTag: "objc_3")
684 << "-[SomeClass someMethodWithoutArguments]"
685 << "-[SomeClass someMethodWithoutArguments]";
686
687 QTest::newRow(dataTag: "objc_4")
688 << "__31-[SomeClass someMethodSchedulingBlock]_block_invoke"
689 << "__31-[SomeClass someMethodSchedulingBlock]_block_invoke";
690}
691#endif
692
693#ifdef QT_BUILD_INTERNAL
694QT_BEGIN_NAMESPACE
695extern QByteArray qCleanupFuncinfo(QByteArray);
696QT_END_NAMESPACE
697#endif
698
699#ifdef QT_BUILD_INTERNAL
700void tst_qmessagehandler::cleanupFuncinfo()
701{
702 QFETCH(QString, funcinfo);
703
704// qDebug() << funcinfo.toLatin1();
705 QByteArray result = qCleanupFuncinfo(funcinfo.toLatin1());
706 QEXPECT_FAIL("TestClass1::nested_struct", "Nested function processing is broken", Continue);
707 QEXPECT_FAIL("TestClass1::nested_struct_const", "Nested function processing is broken", Continue);
708 QTEST(QString::fromLatin1(result), "expected");
709}
710#endif
711
712void tst_qmessagehandler::qMessagePattern_data()
713{
714 QTest::addColumn<QString>(name: "pattern");
715 QTest::addColumn<bool>(name: "valid");
716 QTest::addColumn<QList<QByteArray> >(name: "expected");
717
718 // %{file} is tricky because of shadow builds
719 QTest::newRow(dataTag: "basic") << "%{type} %{appname} %{line} %{function} %{message}" << true << (QList<QByteArray>()
720 << "debug 39 T::T static constructor"
721 // we can't be sure whether the QT_MESSAGE_PATTERN is already destructed
722 << "static destructor"
723 << "debug tst_qlogging 60 MyClass::myFunction from_a_function 34"
724 << "debug tst_qlogging 70 main qDebug"
725 << "info tst_qlogging 71 main qInfo"
726 << "warning tst_qlogging 72 main qWarning"
727 << "critical tst_qlogging 73 main qCritical"
728 << "warning tst_qlogging 76 main qDebug with category"
729 << "debug tst_qlogging 80 main qDebug2");
730
731
732 QTest::newRow(dataTag: "invalid") << "PREFIX: %{unknown} %{message}" << false << (QList<QByteArray>()
733 << "QT_MESSAGE_PATTERN: Unknown placeholder %{unknown}"
734 << "PREFIX: qDebug");
735
736 // test the if condition
737 QTest::newRow(dataTag: "ifs") << "[%{if-debug}D%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{if-category}%{category}: %{endif}%{message}"
738 << true << (QList<QByteArray>()
739 << "[D] static constructor"
740 // we can't be sure whether the QT_MESSAGE_PATTERN is already destructed
741 << "static destructor"
742 << "[D] qDebug"
743 << "[W] qWarning"
744 << "[C] qCritical"
745 << "[W] category: qDebug with category"
746 << "[D] qDebug2");
747
748 // test few errors cases
749 QTest::newRow(dataTag: "ifs-invalid1") << "PREFIX: %{unknown} %{endif} %{if-warning}"
750 << false << (QList<QByteArray>()
751 << "QT_MESSAGE_PATTERN: Unknown placeholder %{unknown}"
752 << "QT_MESSAGE_PATTERN: %{endif} without an %{if-*}"
753 << "QT_MESSAGE_PATTERN: missing %{endif}");
754
755 QTest::newRow(dataTag: "ifs-invalid2") << "A %{if-debug}DEBUG%{if-warning}WARNING%{endif} %{message} "
756 << false << (QList<QByteArray>()
757 << "QT_MESSAGE_PATTERN: %{if-*} cannot be nested"
758 << "A DEBUG qDebug "
759 << "A qWarning ");
760
761 QTest::newRow(dataTag: "pid-tid") << "%{pid}/%{threadid}: %{message}"
762 << true << QList<QByteArray>(); // can't match anything, just test validity
763 QTest::newRow(dataTag: "qthreadptr") << "ThreadId:%{qthreadptr}: %{message}"
764 << true << (QList<QByteArray>()
765 << "ThreadId:0x");
766
767 // This test won't work when midnight is too close... wait a bit
768 while (QTime::currentTime() > QTime(23, 59, 30))
769 QTest::qWait(ms: 10000);
770 QTest::newRow(dataTag: "time") << "/%{time yyyy - MM - d}/%{message}"
771 << true << (QList<QByteArray>()
772 << ('/' + QDateTime::currentDateTime().toString(format: "yyyy - MM - d").toLocal8Bit() + "/qDebug"));
773
774 QTest::newRow(dataTag: "time-time") << "/%{time yyyy - MM - d}/%{time dd-MM-yy}/%{message}"
775 << true << (QList<QByteArray>()
776 << ('/' + QDateTime::currentDateTime().toString(format: "yyyy - MM - d").toLocal8Bit()
777 + '/' + QDateTime::currentDateTime().toString(format: "dd-MM-yy").toLocal8Bit()
778 + "/qDebug"));
779
780 QTest::newRow(dataTag: "skipped-time-shown-time")
781 << "/%{if-warning}%{time yyyy - MM - d}%{endif}%{if-debug}%{time dd-MM-yy}%{endif}/%{message}"
782 << true << (QList<QByteArray>()
783 << ('/' + QDateTime::currentDateTime().toString(format: "dd-MM-yy").toLocal8Bit() + "/qDebug"));
784
785 // %{time} should have a padding of 6 so if it takes less than 10 seconds to show
786 // the first message, there should be 5 spaces
787 QTest::newRow(dataTag: "time-process") << "<%{time process}>%{message}" << true << (QList<QByteArray>()
788 << "< ");
789
790#ifdef __GLIBC__
791#ifdef QT_NAMESPACE
792#define QT_NAMESPACE_STR QT_STRINGIFY(QT_NAMESPACE::)
793#else
794#define QT_NAMESPACE_STR ""
795#endif
796
797#ifndef QT_NO_DEBUG
798 QTest::newRow(dataTag: "backtrace") << "[%{backtrace}] %{message}" << true << (QList<QByteArray>()
799 // MyClass::qt_static_metacall is explicitly marked as hidden in the Q_OBJECT macro
800 << "[MyClass::myFunction|MyClass::mySlot1|?helper?|" QT_NAMESPACE_STR "QMetaMethod::invoke|" QT_NAMESPACE_STR "QMetaObject::invokeMethod] from_a_function 34");
801#endif
802
803 QTest::newRow(dataTag: "backtrace depth,separator") << "[%{backtrace depth=2 separator=\"\n\"}] %{message}" << true << (QList<QByteArray>()
804 << "[MyClass::myFunction\nMyClass::mySlot1] from_a_function 34"
805 << "[T::T\n");
806#endif
807
808}
809
810
811void tst_qmessagehandler::qMessagePattern()
812{
813#if !QT_CONFIG(process)
814 QSKIP("This test requires QProcess support");
815#else
816#ifdef Q_OS_ANDROID
817 QSKIP("This test crashes on Android");
818#endif
819 QFETCH(QString, pattern);
820 QFETCH(bool, valid);
821 QFETCH(QList<QByteArray>, expected);
822
823 QProcess process;
824
825 // Add the executable's directory to path so that we can find the test helper next to it
826 // in a cross-platform way. We must do this because the CWD is not pointing to this directory
827 // in debug-and-release builds.
828 QByteArray path = qgetenv(varName: "PATH");
829 qputenv(varName: "PATH",
830 value: path + QDir::listSeparator().toLatin1()
831 + QCoreApplication::applicationDirPath().toLocal8Bit());
832 auto restore = qScopeGuard(f: [&] { qputenv(varName: "PATH", value: path); });
833
834#if !defined(Q_OS_ANDROID)
835 const QString appExe(QLatin1String("helper"));
836#else
837 const QString appExe(QCoreApplication::applicationDirPath() + QLatin1String("/libhelper.so"));
838#endif
839
840 //
841 // test QT_MESSAGE_PATTERN
842 //
843 QStringList environment = m_baseEnvironment;
844 environment.prepend(t: "QT_MESSAGE_PATTERN=\"" + pattern + QLatin1Char('"'));
845 process.setEnvironment(environment);
846
847 process.start(command: appExe);
848 QVERIFY2(process.waitForStarted(), qPrintable(
849 QString::fromLatin1("Could not start %1: %2").arg(appExe, process.errorString())));
850 QByteArray pid = QByteArray::number(process.processId());
851 process.waitForFinished();
852
853 QByteArray output = process.readAllStandardError();
854// qDebug() << output;
855 QVERIFY(!output.isEmpty());
856 QCOMPARE(!output.contains("QT_MESSAGE_PATTERN"), valid);
857
858 for (const QByteArray &e : qAsConst(t&: expected)) {
859 if (!output.contains(a: e)) {
860 qDebug() << output;
861 qDebug() << "expected: " << e;
862 QVERIFY(output.contains(e));
863 }
864 }
865 if (pattern.startsWith(s: "%{pid}"))
866 QVERIFY2(output.startsWith('"' + pid), "PID: " + pid + "\noutput:\n" + output);
867#endif
868}
869
870void tst_qmessagehandler::setMessagePattern()
871{
872#if !QT_CONFIG(process)
873 QSKIP("This test requires QProcess support");
874#else
875#ifdef Q_OS_ANDROID
876 QSKIP("This test crashes on Android");
877#endif
878
879 //
880 // test qSetMessagePattern
881 //
882
883 QProcess process;
884#ifdef Q_OS_WIN
885 // On Windows the CWD is not the same directory as the helper, so we cannot use "./"
886 // Instead we rely on CreateProcess to find the executable.
887 const QString appExe(QLatin1String("helper"));
888#elif !defined(Q_OS_ANDROID)
889 const QString appExe(QLatin1String("./helper"));
890#else
891 const QString appExe(QCoreApplication::applicationDirPath() + QLatin1String("/libhelper.so"));
892#endif
893
894 // make sure there is no QT_MESSAGE_PATTERN in the environment
895 QStringList environment;
896 environment.reserve(alloc: m_baseEnvironment.size());
897 const auto doesNotStartWith = [](QLatin1String s) {
898 return [s](const QString &str) { return !str.startsWith(s); };
899 };
900 std::copy_if(first: m_baseEnvironment.cbegin(), last: m_baseEnvironment.cend(),
901 result: std::back_inserter(x&: environment),
902 pred: doesNotStartWith(QLatin1String("QT_MESSAGE_PATTERN")));
903 process.setEnvironment(environment);
904
905 process.start(command: appExe);
906 QVERIFY2(process.waitForStarted(), qPrintable(
907 QString::fromLatin1("Could not start %1: %2").arg(appExe, process.errorString())));
908 process.waitForFinished();
909
910 QByteArray output = process.readAllStandardError();
911 //qDebug() << output;
912 QByteArray expected = "static constructor\n"
913 "[debug] qDebug\n"
914 "[info] qInfo\n"
915 "[warning] qWarning\n"
916 "[critical] qCritical\n"
917 "[warning] qDebug with category\n";
918#ifdef Q_OS_WIN
919 output.replace("\r\n", "\n");
920#endif
921 QCOMPARE(QString::fromLatin1(output), QString::fromLatin1(expected));
922#endif // QT_CONFIG(process)
923}
924
925Q_DECLARE_METATYPE(QtMsgType)
926
927void tst_qmessagehandler::formatLogMessage_data()
928{
929 QTest::addColumn<QString>(name: "pattern");
930 QTest::addColumn<QString>(name: "result");
931
932 QTest::addColumn<QtMsgType>(name: "type");
933 QTest::addColumn<QByteArray>(name: "file");
934 QTest::addColumn<int>(name: "line");
935 QTest::addColumn<QByteArray>(name: "function");
936 QTest::addColumn<QByteArray>(name: "category");
937 QTest::addColumn<QString>(name: "message");
938
939#define BA QByteArrayLiteral
940
941 QTest::newRow(dataTag: "basic") << "%{type} %{file} %{line} %{function} %{message}"
942 << "debug main.cpp 1 func msg"
943 << QtDebugMsg << BA("main.cpp") << 1 << BA("func") << BA("") << "msg";
944
945 // test the if conditions
946 QString format = "[%{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{if-category}%{category}: %{endif}%{message}";
947 QTest::newRow(dataTag: "if-debug")
948 << format << "[D] msg"
949 << QtDebugMsg << BA("") << 0 << BA("func") << QByteArray() << "msg";
950 QTest::newRow(dataTag: "if_info")
951 << format << "[I] msg"
952 << QtInfoMsg << BA("") << 0 << BA("func") << QByteArray() << "msg";
953 QTest::newRow(dataTag: "if_warning")
954 << format << "[W] msg"
955 << QtWarningMsg << BA("") << 0 << BA("func") << QByteArray() << "msg";
956 QTest::newRow(dataTag: "if_critical")
957 << format << "[C] msg"
958 << QtCriticalMsg << BA("") << 0 << BA("func") << QByteArray() << "msg";
959 QTest::newRow(dataTag: "if_fatal")
960 << format << "[F] msg"
961 << QtFatalMsg << BA("") << 0 << BA("func") << QByteArray() << "msg";
962 QTest::newRow(dataTag: "if_cat")
963 << format << "[F] cat: msg"
964 << QtFatalMsg << BA("") << 0 << BA("func") << BA("cat") << "msg";
965}
966
967void tst_qmessagehandler::formatLogMessage()
968{
969 QFETCH(QString, pattern);
970 QFETCH(QString, result);
971
972 QFETCH(QtMsgType, type);
973 QFETCH(QByteArray, file);
974 QFETCH(int, line);
975 QFETCH(QByteArray, function);
976 QFETCH(QByteArray, category);
977 QFETCH(QString, message);
978
979 qSetMessagePattern(messagePattern: pattern);
980 QMessageLogContext ctxt(file, line, function, category.isEmpty() ? 0 : category.data());
981 QString r = qFormatLogMessage(type, context: ctxt, buf: message);
982 QCOMPARE(r, result);
983}
984
985
986QTEST_MAIN(tst_qmessagehandler)
987#include "tst_qlogging.moc"
988

source code of qtbase/tests/auto/corelib/global/qlogging/tst_qlogging.cpp