| 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 |  | 
| 39 | class tst_qmessagehandler : public QObject | 
| 40 | { | 
| 41 |     Q_OBJECT | 
| 42 | public: | 
| 43 |     tst_qmessagehandler(); | 
| 44 |  | 
| 45 | public slots: | 
| 46 |     void initTestCase(); | 
| 47 |  | 
| 48 | private 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 |  | 
| 70 | private: | 
| 71 |     QStringList m_baseEnvironment; | 
| 72 | }; | 
| 73 |  | 
| 74 | static QtMsgType s_type; | 
| 75 | const char *s_file; | 
| 76 | int s_line; | 
| 77 | const char *s_function; | 
| 78 | static QString s_message; | 
| 79 |  | 
| 80 | void 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 |  | 
| 89 | void 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 |  | 
| 98 | tst_qmessagehandler::tst_qmessagehandler() | 
| 99 | { | 
| 100 |     // ensure it's unset, otherwise we'll have trouble | 
| 101 |     qputenv(varName: "QT_MESSAGE_PATTERN" , value: "" ); | 
| 102 | } | 
| 103 |  | 
| 104 | void 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 |  | 
| 117 | void 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 |  | 
| 129 | void tst_qmessagehandler::defaultHandler() | 
| 130 | { | 
| 131 |     // check that the default works | 
| 132 |     QTest::ignoreMessage(type: QtDebugMsg, message: "defaultHandler" ); | 
| 133 |     qDebug(msg: "defaultHandler" ); | 
| 134 | } | 
| 135 |  | 
| 136 | void 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) | 
| 153 | void 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 |  | 
| 169 | void 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 |  | 
| 186 | class TestClass1 | 
| 187 | { | 
| 188 | public: | 
| 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 |  | 
| 275 | public: | 
| 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 |  | 
| 338 | template<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); } | 
| 345 | public: | 
| 346 |     TestClass2() | 
| 347 |         { | 
| 348 |             func_long(); | 
| 349 |             func_template1<TestClass2>(); | 
| 350 |             func_template2<TestClass1::foo>(); | 
| 351 |         } | 
| 352 | }; | 
| 353 |  | 
| 354 | template<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); } | 
| 361 | public: | 
| 362 |     struct Foo { TestClass3 foo; }; | 
| 363 |     TestClass3() | 
| 364 |         { | 
| 365 |             func_long(); | 
| 366 |             func_template1<TestClass2<T> >(); | 
| 367 |             func_template2<TestClass1::foo>(); | 
| 368 |         } | 
| 369 | }; | 
| 370 |  | 
| 371 | class 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(); } | 
| 379 | public: | 
| 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 | 
| 394 | void 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 | 
| 694 | QT_BEGIN_NAMESPACE | 
| 695 | extern QByteArray qCleanupFuncinfo(QByteArray); | 
| 696 | QT_END_NAMESPACE | 
| 697 | #endif | 
| 698 |  | 
| 699 | #ifdef QT_BUILD_INTERNAL | 
| 700 | void 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 |  | 
| 712 | void 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 |  | 
| 811 | void 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 |  | 
| 870 | void 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 |  | 
| 925 | Q_DECLARE_METATYPE(QtMsgType) | 
| 926 |  | 
| 927 | void 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 |  | 
| 967 | void 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 |  | 
| 986 | QTEST_MAIN(tst_qmessagehandler) | 
| 987 | #include "tst_qlogging.moc" | 
| 988 |  |