1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Copyright (C) 2016 Intel Corporation. |
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 | |
31 | #include <QtCore/QCoreApplication> |
32 | #include <QtCore/QtDebug> |
33 | #include <QtTest/QtTest> |
34 | |
35 | #include <QtConcurrentRun> |
36 | #include <QFutureSynchronizer> |
37 | |
38 | class tst_QDebug: public QObject |
39 | { |
40 | Q_OBJECT |
41 | public: |
42 | enum EnumType { EnumValue1 = 1, EnumValue2 = 2 }; |
43 | enum FlagType { EnumFlag1 = 1, EnumFlag2 = 2 }; |
44 | Q_ENUM(EnumType) |
45 | Q_DECLARE_FLAGS(Flags, FlagType) |
46 | Q_FLAG(Flags) |
47 | |
48 | private slots: |
49 | void assignment() const; |
50 | void warningWithoutDebug() const; |
51 | void criticalWithoutDebug() const; |
52 | void debugWithBool() const; |
53 | void debugSpaceHandling() const; |
54 | void debugNoQuotes() const; |
55 | void verbosity() const; |
56 | void stateSaver() const; |
57 | void veryLongWarningMessage() const; |
58 | void qDebugQChar() const; |
59 | void qDebugQString() const; |
60 | void qDebugQStringRef() const; |
61 | void qDebugQStringView() const; |
62 | void qDebugQLatin1String() const; |
63 | void qDebugQByteArray() const; |
64 | void qDebugQFlags() const; |
65 | void textStreamModifiers() const; |
66 | void resetFormat() const; |
67 | void defaultMessagehandler() const; |
68 | void threadSafety() const; |
69 | }; |
70 | |
71 | void tst_QDebug::assignment() const |
72 | { |
73 | QDebug debug1(QtDebugMsg); |
74 | QDebug debug2(QtWarningMsg); |
75 | |
76 | QTest::ignoreMessage(type: QtDebugMsg, message: "foo" ); |
77 | QTest::ignoreMessage(type: QtWarningMsg, message: "bar 1 2" ); |
78 | |
79 | debug1 << "foo" ; |
80 | debug2 << "bar" ; |
81 | debug1 = debug2; |
82 | debug1 << "1" ; |
83 | debug2 << "2" ; |
84 | } |
85 | |
86 | static QtMsgType s_msgType; |
87 | static QString s_msg; |
88 | static QByteArray s_file; |
89 | static int s_line; |
90 | static QByteArray s_function; |
91 | |
92 | static void myMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) |
93 | { |
94 | s_msg = msg; |
95 | s_msgType = type; |
96 | s_file = context.file; |
97 | s_line = context.line; |
98 | s_function = context.function; |
99 | } |
100 | |
101 | // Helper class to ensure that the testlib message handler gets |
102 | // restored at the end of each test function, even if the test |
103 | // fails or throws an exception. |
104 | class MessageHandlerSetter |
105 | { |
106 | public: |
107 | MessageHandlerSetter(QtMessageHandler newMessageHandler) |
108 | : oldMessageHandler(qInstallMessageHandler(newMessageHandler)) |
109 | { } |
110 | |
111 | ~MessageHandlerSetter() |
112 | { |
113 | qInstallMessageHandler(oldMessageHandler); |
114 | } |
115 | |
116 | private: |
117 | QtMessageHandler oldMessageHandler; |
118 | }; |
119 | |
120 | /*! \internal |
121 | The qWarning() stream should be usable even if QT_NO_DEBUG is defined. |
122 | */ |
123 | void tst_QDebug::warningWithoutDebug() const |
124 | { |
125 | QString file, function; |
126 | int line = 0; |
127 | MessageHandlerSetter mhs(myMessageHandler); |
128 | { qWarning() << "A qWarning() message" ; } |
129 | #ifndef QT_NO_MESSAGELOGCONTEXT |
130 | file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO; |
131 | #endif |
132 | QCOMPARE(s_msgType, QtWarningMsg); |
133 | QCOMPARE(s_msg, QString::fromLatin1("A qWarning() message" )); |
134 | QCOMPARE(QString::fromLatin1(s_file), file); |
135 | QCOMPARE(s_line, line); |
136 | QCOMPARE(QString::fromLatin1(s_function), function); |
137 | } |
138 | |
139 | /*! \internal |
140 | The qCritical() stream should be usable even if QT_NO_DEBUG is defined. |
141 | */ |
142 | void tst_QDebug::criticalWithoutDebug() const |
143 | { |
144 | QString file, function; |
145 | int line = 0; |
146 | MessageHandlerSetter mhs(myMessageHandler); |
147 | { qCritical() << "A qCritical() message" ; } |
148 | #ifndef QT_NO_MESSAGELOGCONTEXT |
149 | file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO; |
150 | #endif |
151 | QCOMPARE(s_msgType, QtCriticalMsg); |
152 | QCOMPARE(s_msg, QString::fromLatin1("A qCritical() message" )); |
153 | QCOMPARE(QString::fromLatin1(s_file), file); |
154 | QCOMPARE(s_line, line); |
155 | QCOMPARE(QString::fromLatin1(s_function), function); |
156 | } |
157 | |
158 | void tst_QDebug::debugWithBool() const |
159 | { |
160 | QString file, function; |
161 | int line = 0; |
162 | MessageHandlerSetter mhs(myMessageHandler); |
163 | { qDebug() << false << true; } |
164 | #ifndef QT_NO_MESSAGELOGCONTEXT |
165 | file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO; |
166 | #endif |
167 | QCOMPARE(s_msgType, QtDebugMsg); |
168 | QCOMPARE(s_msg, QString::fromLatin1("false true" )); |
169 | QCOMPARE(QString::fromLatin1(s_file), file); |
170 | QCOMPARE(s_line, line); |
171 | QCOMPARE(QString::fromLatin1(s_function), function); |
172 | } |
173 | |
174 | class MyPoint |
175 | { |
176 | public: |
177 | MyPoint(int val1, int val2) |
178 | : v1(val1), v2(val2) {} |
179 | int v1; |
180 | int v2; |
181 | }; |
182 | QDebug operator<< (QDebug s, const MyPoint& point) |
183 | { |
184 | const QDebugStateSaver saver(s); |
185 | s.nospace() << "MyPoint(" << point.v1 << ", " << point.v2 << ")" ; |
186 | return s; |
187 | } |
188 | |
189 | class MyLine |
190 | { |
191 | public: |
192 | MyLine(const MyPoint& point1, const MyPoint& point2) |
193 | : p1(point1), p2(point2) {} |
194 | MyPoint p1; |
195 | MyPoint p2; |
196 | }; |
197 | QDebug operator<< (QDebug s, const MyLine& line) |
198 | { |
199 | const QDebugStateSaver saver(s); |
200 | s.nospace(); |
201 | s << "MyLine(" << line.p1 << ", " << line.p2; |
202 | if (s.verbosity() > 2) |
203 | s << ", Manhattan length=" << (qAbs(t: line.p2.v1 - line.p1.v1) + qAbs(t: line.p2.v2 - line.p1.v2)); |
204 | s << ')'; |
205 | return s; |
206 | } |
207 | |
208 | void tst_QDebug::debugSpaceHandling() const |
209 | { |
210 | MessageHandlerSetter mhs(myMessageHandler); |
211 | { |
212 | QDebug d = qDebug(); |
213 | QVERIFY(d.autoInsertSpaces()); |
214 | d.setAutoInsertSpaces(false); |
215 | QVERIFY(!d.autoInsertSpaces()); |
216 | d << " " ; |
217 | d.setAutoInsertSpaces(true); |
218 | QVERIFY(d.autoInsertSpaces()); |
219 | d << "foo" ; |
220 | d.nospace(); |
221 | d << "key=" << "value" ; |
222 | d.space(); |
223 | d << 1 << 2; |
224 | MyLine line(MyPoint(10, 11), MyPoint (12, 13)); |
225 | d << line; |
226 | d << "bar" ; |
227 | // With the old implementation of MyPoint doing dbg.nospace() << ...; dbg.space() we ended up with |
228 | // MyLine(MyPoint(10, 11) , MyPoint(12, 13) ) |
229 | } |
230 | QCOMPARE(s_msg, QString::fromLatin1(" foo key=value 1 2 MyLine(MyPoint(10, 11), MyPoint(12, 13)) bar" )); |
231 | |
232 | QVERIFY(qDebug().autoInsertSpaces()); |
233 | qDebug() << QPoint(21, 22) << QRect(23, 24, 25, 26) << QLine(27, 28, 29, 30); |
234 | QCOMPARE(s_msg, QString::fromLatin1("QPoint(21,22) QRect(23,24 25x26) QLine(QPoint(27,28),QPoint(29,30))" )); |
235 | qDebug() << QPointF(21, 22) << QRectF(23, 24, 25, 26) << QLineF(27, 28, 29, 30); |
236 | QCOMPARE(s_msg, QString::fromLatin1("QPointF(21,22) QRectF(23,24 25x26) QLineF(QPointF(27,28),QPointF(29,30))" )); |
237 | qDebug() << QMimeType() << QMimeDatabase().mimeTypeForName(nameOrAlias: "application/pdf" ) << "foo" ; |
238 | QCOMPARE(s_msg, QString::fromLatin1("QMimeType(invalid) QMimeType(\"application/pdf\") foo" )); |
239 | } |
240 | |
241 | void tst_QDebug::debugNoQuotes() const |
242 | { |
243 | MessageHandlerSetter mhs(myMessageHandler); |
244 | { |
245 | QDebug d = qDebug(); |
246 | d << QStringLiteral("Hello" ); |
247 | d.noquote(); |
248 | d << QStringLiteral("Hello" ); |
249 | d.quote(); |
250 | d << QStringLiteral("Hello" ); |
251 | } |
252 | QCOMPARE(s_msg, QString::fromLatin1("\"Hello\" Hello \"Hello\"" )); |
253 | |
254 | { |
255 | QDebug d = qDebug(); |
256 | d << QChar('H'); |
257 | d << QLatin1String("Hello" ); |
258 | d << QByteArray("Hello" ); |
259 | d.noquote(); |
260 | d << QChar('H'); |
261 | d << QLatin1String("Hello" ); |
262 | d << QByteArray("Hello" ); |
263 | } |
264 | QCOMPARE(s_msg, QString::fromLatin1("'H' \"Hello\" \"Hello\" H Hello Hello" )); |
265 | } |
266 | |
267 | void tst_QDebug::verbosity() const |
268 | { |
269 | MyLine line(MyPoint(10, 11), MyPoint (12, 13)); |
270 | QString output; |
271 | QDebug d(&output); |
272 | d.nospace(); |
273 | d << line << '\n'; |
274 | const int oldVerbosity = d.verbosity(); |
275 | d.setVerbosity(0); |
276 | QCOMPARE(d.verbosity(), 0); |
277 | d.setVerbosity(7); |
278 | QCOMPARE(d.verbosity(), 7); |
279 | const int newVerbosity = oldVerbosity + 2; |
280 | d.setVerbosity(newVerbosity); |
281 | QCOMPARE(d.verbosity(), newVerbosity); |
282 | d << line << '\n'; |
283 | d.setVerbosity(oldVerbosity ); |
284 | QCOMPARE(d.verbosity(), oldVerbosity ); |
285 | d << line; |
286 | const QStringList lines = output.split(sep: QLatin1Char('\n')); |
287 | QCOMPARE(lines.size(), 3); |
288 | // Verbose should be longer |
289 | QVERIFY2(lines.at(1).size() > lines.at(0).size(), qPrintable(lines.join(QLatin1Char(',')))); |
290 | // Switching back to brief produces same output |
291 | QCOMPARE(lines.at(0).size(), lines.at(2).size()); |
292 | } |
293 | |
294 | void tst_QDebug::stateSaver() const |
295 | { |
296 | MessageHandlerSetter mhs(myMessageHandler); |
297 | { |
298 | QDebug d = qDebug(); |
299 | d << 42; |
300 | { |
301 | QDebugStateSaver saver(d); |
302 | d << 43; |
303 | } |
304 | d << 44; |
305 | } |
306 | QCOMPARE(s_msg, QString::fromLatin1("42 43 44" )); |
307 | |
308 | { |
309 | QDebug d = qDebug(); |
310 | { |
311 | QDebugStateSaver saver(d); |
312 | d.nospace() << Qt::hex << Qt::right << qSetFieldWidth(width: 3) << qSetPadChar(ch: '0') << 42; |
313 | } |
314 | d << 42; |
315 | } |
316 | QCOMPARE(s_msg, QString::fromLatin1("02a 42" )); |
317 | |
318 | { |
319 | QDebug d = qDebug(); |
320 | { |
321 | QDebugStateSaver saver(d); |
322 | d.nospace().noquote() << QStringLiteral("Hello" ); |
323 | } |
324 | d << QStringLiteral("World" ); |
325 | } |
326 | QCOMPARE(s_msg, QString::fromLatin1("Hello \"World\"" )); |
327 | |
328 | { |
329 | QDebug d = qDebug(); |
330 | d.noquote().nospace() << QStringLiteral("Hello" ) << Qt::hex << 42; |
331 | { |
332 | QDebugStateSaver saver(d); |
333 | d.resetFormat(); |
334 | d << QStringLiteral("World" ) << 42; |
335 | } |
336 | d << QStringLiteral("!" ) << 42; |
337 | } |
338 | QCOMPARE(s_msg, QString::fromLatin1("Hello2a\"World\" 42!2a" )); |
339 | } |
340 | |
341 | void tst_QDebug::veryLongWarningMessage() const |
342 | { |
343 | QString file, function; |
344 | int line = 0; |
345 | MessageHandlerSetter mhs(myMessageHandler); |
346 | QString test; |
347 | { |
348 | QString part("0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789\n" ); |
349 | for (int i = 0; i < 1000; ++i) |
350 | test.append(s: part); |
351 | qWarning(msg: "Test output:\n%s\nend" , qPrintable(test)); |
352 | } |
353 | #ifndef QT_NO_MESSAGELOGCONTEXT |
354 | file = __FILE__; line = __LINE__ - 3; function = Q_FUNC_INFO; |
355 | #endif |
356 | QCOMPARE(s_msgType, QtWarningMsg); |
357 | QCOMPARE(s_msg, QString::fromLatin1("Test output:\n" )+test+QString::fromLatin1("\nend" )); |
358 | QCOMPARE(QString::fromLatin1(s_file), file); |
359 | QCOMPARE(s_line, line); |
360 | QCOMPARE(QString::fromLatin1(s_function), function); |
361 | } |
362 | |
363 | void tst_QDebug::qDebugQChar() const |
364 | { |
365 | QString file, function; |
366 | int line = 0; |
367 | MessageHandlerSetter mhs(myMessageHandler); |
368 | { |
369 | QDebug d = qDebug(); |
370 | d << QChar('f') << QChar(QLatin1Char('\xE4')); // f, ä |
371 | d.nospace().noquote() << QChar('o') << QChar('o') << QChar(QLatin1Char('\xC4')); // o, o, Ä |
372 | } |
373 | #ifndef QT_NO_MESSAGELOGCONTEXT |
374 | file = __FILE__; line = __LINE__ - 5; function = Q_FUNC_INFO; |
375 | #endif |
376 | QCOMPARE(s_msgType, QtDebugMsg); |
377 | QCOMPARE(s_msg, QString::fromLatin1("'f' '\\u00e4' oo\\u00c4" )); |
378 | QCOMPARE(QString::fromLatin1(s_file), file); |
379 | QCOMPARE(s_line, line); |
380 | QCOMPARE(QString::fromLatin1(s_function), function); |
381 | |
382 | } |
383 | |
384 | void tst_QDebug::qDebugQString() const |
385 | { |
386 | /* Use a basic string. */ |
387 | { |
388 | QString file, function; |
389 | int line = 0; |
390 | const QString in(QLatin1String("input" )); |
391 | const QStringRef inRef(&in); |
392 | |
393 | MessageHandlerSetter mhs(myMessageHandler); |
394 | { qDebug() << inRef; } |
395 | #ifndef QT_NO_MESSAGELOGCONTEXT |
396 | file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO; |
397 | #endif |
398 | QCOMPARE(s_msgType, QtDebugMsg); |
399 | QCOMPARE(s_msg, QString::fromLatin1("\"input\"" )); |
400 | QCOMPARE(QString::fromLatin1(s_file), file); |
401 | QCOMPARE(s_line, line); |
402 | QCOMPARE(QString::fromLatin1(s_function), function); |
403 | } |
404 | |
405 | /* simpler tests from now on */ |
406 | MessageHandlerSetter mhs(myMessageHandler); |
407 | |
408 | QString string = "Hello" ; |
409 | qDebug() << string; |
410 | QCOMPARE(s_msg, QString("\"Hello\"" )); |
411 | |
412 | qDebug().noquote().nospace() << string; |
413 | QCOMPARE(s_msg, string); |
414 | |
415 | qDebug().noquote().nospace() << qSetFieldWidth(width: 8) << string; |
416 | QCOMPARE(s_msg, " " + string); |
417 | |
418 | string = "Sm\xc3\xb8rg\xc3\xa5sbord " // Latin script |
419 | "\xce\x91\xce\xb8\xce\xae\xce\xbd\xce\xb1 " // Greek script |
420 | "\xd0\x9c\xd0\xbe\xd1\x81\xd0\xba\xd0\xb2\xd0\xb0" ; // Cyrillic script |
421 | qDebug().noquote().nospace() << string; |
422 | QCOMPARE(s_msg, string); |
423 | |
424 | // This string only contains printable characters |
425 | qDebug() << string; |
426 | QCOMPARE(s_msg, '"' + string + '"'); |
427 | |
428 | string = "\n\t\\\"" ; |
429 | qDebug().noquote().nospace() << string; |
430 | QCOMPARE(s_msg, string); |
431 | |
432 | // This string only contains characters that must be escaped |
433 | qDebug() << string; |
434 | QCOMPARE(s_msg, QString("\"\\n\\t\\\\\\\"\"" )); |
435 | |
436 | // Unicode escapes, BMP |
437 | string = "\1" // U+0001: START OF HEADING (category Cc) |
438 | "\x7f" // U+007F: DELETE (category Cc) |
439 | "\xc2\xad" // U+00AD: SOFT HYPHEN (category Cf) |
440 | "\xef\xbb\xbf" ; // U+FEFF: ZERO WIDTH NO-BREAK SPACE / BOM (category Cf) |
441 | qDebug() << string; |
442 | QCOMPARE(s_msg, QString("\"\\u0001\\u007F\\u00AD\\uFEFF\"" )); |
443 | |
444 | // Unicode printable non-BMP |
445 | string = "\xf0\x90\x80\x80" ; // U+10000: LINEAR B SYLLABLE B008 A (category Lo) |
446 | qDebug() << string; |
447 | QCOMPARE(s_msg, '"' + string + '"'); |
448 | |
449 | // non-BMP and non-printable |
450 | string = "\xf3\xa0\x80\x81 " // U+E0001: LANGUAGE TAG (category Cf) |
451 | "\xf4\x80\x80\x80" ; // U+100000: Plane 16 Private Use (category Co) |
452 | qDebug() << string; |
453 | QCOMPARE(s_msg, QString("\"\\U000E0001 \\U00100000\"" )); |
454 | |
455 | // broken surrogate pairs |
456 | ushort utf16[] = { 0xDC00, 0xD800, 'x', 0xD800, 0 }; |
457 | string = QString::fromUtf16(utf16); |
458 | qDebug() << string; |
459 | QCOMPARE(s_msg, QString("\"\\uDC00\\uD800x\\uD800\"" )); |
460 | } |
461 | |
462 | void tst_QDebug::qDebugQStringRef() const |
463 | { |
464 | /* Use a basic string. */ |
465 | { |
466 | QString file, function; |
467 | int line = 0; |
468 | const QString in(QLatin1String("input" )); |
469 | const QStringRef inRef(&in); |
470 | |
471 | MessageHandlerSetter mhs(myMessageHandler); |
472 | { qDebug() << inRef; } |
473 | #ifndef QT_NO_MESSAGELOGCONTEXT |
474 | file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO; |
475 | #endif |
476 | QCOMPARE(s_msgType, QtDebugMsg); |
477 | QCOMPARE(s_msg, QString::fromLatin1("\"input\"" )); |
478 | QCOMPARE(QString::fromLatin1(s_file), file); |
479 | QCOMPARE(s_line, line); |
480 | QCOMPARE(QString::fromLatin1(s_function), function); |
481 | } |
482 | |
483 | /* Use a null QStringRef. */ |
484 | { |
485 | QString file, function; |
486 | int line = 0; |
487 | |
488 | const QStringRef inRef; |
489 | |
490 | MessageHandlerSetter mhs(myMessageHandler); |
491 | { qDebug() << inRef; } |
492 | #ifndef QT_NO_MESSAGELOGCONTEXT |
493 | file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO; |
494 | #endif |
495 | QCOMPARE(s_msgType, QtDebugMsg); |
496 | QCOMPARE(s_msg, QString::fromLatin1("\"\"" )); |
497 | QCOMPARE(QString::fromLatin1(s_file), file); |
498 | QCOMPARE(s_line, line); |
499 | QCOMPARE(QString::fromLatin1(s_function), function); |
500 | } |
501 | } |
502 | |
503 | void tst_QDebug::qDebugQStringView() const |
504 | { |
505 | /* Use a basic string. */ |
506 | { |
507 | QLatin1String file, function; |
508 | int line = 0; |
509 | const QStringView inView = u"input" ; |
510 | |
511 | MessageHandlerSetter mhs(myMessageHandler); |
512 | { qDebug() << inView; } |
513 | #ifndef QT_NO_MESSAGELOGCONTEXT |
514 | file = QLatin1String(__FILE__); line = __LINE__ - 2; function = QLatin1String(Q_FUNC_INFO); |
515 | #endif |
516 | QCOMPARE(s_msgType, QtDebugMsg); |
517 | QCOMPARE(s_msg, QLatin1String("\"input\"" )); |
518 | QCOMPARE(QLatin1String(s_file), file); |
519 | QCOMPARE(s_line, line); |
520 | QCOMPARE(QLatin1String(s_function), function); |
521 | } |
522 | |
523 | /* Use a null QStringView. */ |
524 | { |
525 | QString file, function; |
526 | int line = 0; |
527 | |
528 | const QStringView inView; |
529 | |
530 | MessageHandlerSetter mhs(myMessageHandler); |
531 | { qDebug() << inView; } |
532 | #ifndef QT_NO_MESSAGELOGCONTEXT |
533 | file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO; |
534 | #endif |
535 | QCOMPARE(s_msgType, QtDebugMsg); |
536 | QCOMPARE(s_msg, QLatin1String("\"\"" )); |
537 | QCOMPARE(QLatin1String(s_file), file); |
538 | QCOMPARE(s_line, line); |
539 | QCOMPARE(QLatin1String(s_function), function); |
540 | } |
541 | } |
542 | |
543 | void tst_QDebug::qDebugQLatin1String() const |
544 | { |
545 | QString file, function; |
546 | int line = 0; |
547 | MessageHandlerSetter mhs(myMessageHandler); |
548 | { |
549 | QDebug d = qDebug(); |
550 | d << QLatin1String("foo" ) << QLatin1String("" ) << QLatin1String("barbaz" , 3); |
551 | d.nospace().noquote() << QLatin1String("baz" ); |
552 | } |
553 | #ifndef QT_NO_MESSAGELOGCONTEXT |
554 | file = __FILE__; line = __LINE__ - 5; function = Q_FUNC_INFO; |
555 | #endif |
556 | QCOMPARE(s_msgType, QtDebugMsg); |
557 | QCOMPARE(s_msg, QString::fromLatin1("\"foo\" \"\" \"bar\" baz" )); |
558 | QCOMPARE(QString::fromLatin1(s_file), file); |
559 | QCOMPARE(s_line, line); |
560 | QCOMPARE(QString::fromLatin1(s_function), function); |
561 | |
562 | /* simpler tests from now on */ |
563 | QLatin1String string("\"Hello\"" ); |
564 | qDebug() << string; |
565 | QCOMPARE(s_msg, QString("\"\\\"Hello\\\"\"" )); |
566 | |
567 | qDebug().noquote().nospace() << string; |
568 | QCOMPARE(s_msg, QString(string)); |
569 | |
570 | qDebug().noquote().nospace() << qSetFieldWidth(width: 8) << string; |
571 | QCOMPARE(s_msg, " " + QString(string)); |
572 | |
573 | string = QLatin1String("\nSm\xF8rg\xE5sbord\\" ); |
574 | qDebug().noquote().nospace() << string; |
575 | QCOMPARE(s_msg, QString(string)); |
576 | |
577 | qDebug() << string; |
578 | QCOMPARE(s_msg, QString("\"\\nSm\\u00F8rg\\u00E5sbord\\\\\"" )); |
579 | } |
580 | |
581 | void tst_QDebug::qDebugQByteArray() const |
582 | { |
583 | QString file, function; |
584 | int line = 0; |
585 | MessageHandlerSetter mhs(myMessageHandler); |
586 | { |
587 | QDebug d = qDebug(); |
588 | d << QByteArrayLiteral("foo" ) << QByteArrayLiteral("" ) << QByteArray("barbaz" , 3); |
589 | d.nospace().noquote() << QByteArrayLiteral("baz" ); |
590 | } |
591 | #ifndef QT_NO_MESSAGELOGCONTEXT |
592 | file = __FILE__; line = __LINE__ - 5; function = Q_FUNC_INFO; |
593 | #endif |
594 | QCOMPARE(s_msgType, QtDebugMsg); |
595 | QCOMPARE(s_msg, QString::fromLatin1("\"foo\" \"\" \"bar\" baz" )); |
596 | QCOMPARE(QString::fromLatin1(s_file), file); |
597 | QCOMPARE(s_line, line); |
598 | QCOMPARE(QString::fromLatin1(s_function), function); |
599 | |
600 | /* simpler tests from now on */ |
601 | QByteArray ba = "\"Hello\"" ; |
602 | qDebug() << ba; |
603 | QCOMPARE(s_msg, QString("\"\\\"Hello\\\"\"" )); |
604 | |
605 | qDebug().noquote().nospace() << ba; |
606 | QCOMPARE(s_msg, QString::fromLatin1(ba)); |
607 | |
608 | qDebug().noquote().nospace() << qSetFieldWidth(width: 8) << ba; |
609 | QCOMPARE(s_msg, " " + QString::fromLatin1(ba)); |
610 | |
611 | ba = "\nSm\xC3\xB8rg\xC3\xA5sbord\\" ; |
612 | qDebug().noquote().nospace() << ba; |
613 | QCOMPARE(s_msg, QString::fromUtf8(ba)); |
614 | |
615 | qDebug() << ba; |
616 | QCOMPARE(s_msg, QString("\"\\nSm\\xC3\\xB8rg\\xC3\\xA5sbord\\\\\"" )); |
617 | |
618 | // ensure that it closes hex escape sequences correctly |
619 | qDebug() << QByteArray("\377FFFF" ); |
620 | QCOMPARE(s_msg, QString("\"\\xFF\"\"FFFF\"" )); |
621 | } |
622 | |
623 | enum TestEnum { |
624 | Flag1 = 0x1, |
625 | Flag2 = 0x10 |
626 | }; |
627 | |
628 | Q_DECLARE_FLAGS(TestFlags, TestEnum) |
629 | |
630 | void tst_QDebug::qDebugQFlags() const |
631 | { |
632 | QString file, function; |
633 | int line = 0; |
634 | QFlags<TestEnum> flags(Flag1 | Flag2); |
635 | |
636 | MessageHandlerSetter mhs(myMessageHandler); |
637 | { qDebug() << flags; } |
638 | #ifndef QT_NO_MESSAGELOGCONTEXT |
639 | file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO; |
640 | #endif |
641 | QCOMPARE(s_msgType, QtDebugMsg); |
642 | QCOMPARE(s_msg, QString::fromLatin1("QFlags(0x1|0x10)" )); |
643 | QCOMPARE(QString::fromLatin1(s_file), file); |
644 | QCOMPARE(s_line, line); |
645 | QCOMPARE(QString::fromLatin1(s_function), function); |
646 | |
647 | // Test the output of QFlags with an enum not declared with Q_DECLARE_FLAGS and Q_FLAGS |
648 | QFlags<EnumType> flags2(EnumValue2); |
649 | qDebug() << flags2; |
650 | QCOMPARE(s_msg, QString::fromLatin1("QFlags<tst_QDebug::EnumType>(EnumValue2)" )); |
651 | |
652 | // A now for one that was fully declared |
653 | tst_QDebug::Flags flags3(EnumFlag1); |
654 | qDebug() << flags3; |
655 | QCOMPARE(s_msg, QString::fromLatin1("QFlags<tst_QDebug::FlagType>(EnumFlag1)" )); |
656 | } |
657 | |
658 | void tst_QDebug::textStreamModifiers() const |
659 | { |
660 | QString file, function; |
661 | int line = 0; |
662 | MessageHandlerSetter mhs(myMessageHandler); |
663 | { qDebug() << Qt::hex << short(0xf) << int(0xf) << unsigned(0xf) << long(0xf) << qint64(0xf) << quint64(0xf); } |
664 | #ifndef QT_NO_MESSAGELOGCONTEXT |
665 | file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO; |
666 | #endif |
667 | QCOMPARE(s_msgType, QtDebugMsg); |
668 | QCOMPARE(s_msg, QString::fromLatin1("f f f f f f" )); |
669 | QCOMPARE(QString::fromLatin1(s_file), file); |
670 | QCOMPARE(s_line, line); |
671 | QCOMPARE(QString::fromLatin1(s_function), function); |
672 | } |
673 | |
674 | void tst_QDebug::resetFormat() const |
675 | { |
676 | QString file, function; |
677 | int line = 0; |
678 | MessageHandlerSetter mhs(myMessageHandler); |
679 | { |
680 | QDebug d = qDebug(); |
681 | d.nospace().noquote() << Qt::hex << int(0xf); |
682 | d.resetFormat() << int(0xf) << QStringLiteral("foo" ); |
683 | } |
684 | #ifndef QT_NO_MESSAGELOGCONTEXT |
685 | file = __FILE__; line = __LINE__ - 5; function = Q_FUNC_INFO; |
686 | #endif |
687 | QCOMPARE(s_msgType, QtDebugMsg); |
688 | QCOMPARE(s_msg, QString::fromLatin1("f15 \"foo\"" )); |
689 | QCOMPARE(QString::fromLatin1(s_file), file); |
690 | QCOMPARE(s_line, line); |
691 | QCOMPARE(QString::fromLatin1(s_function), function); |
692 | } |
693 | |
694 | void tst_QDebug::defaultMessagehandler() const |
695 | { |
696 | MessageHandlerSetter mhs(0); // set 0, should set default handler |
697 | QtMessageHandler defaultMessageHandler1 = qInstallMessageHandler((QtMessageHandler)0); // set 0, should set and return default handler |
698 | QVERIFY(defaultMessageHandler1); |
699 | QtMessageHandler defaultMessageHandler2 = qInstallMessageHandler(myMessageHandler); // set myMessageHandler and return default handler |
700 | bool same = (*defaultMessageHandler1 == *defaultMessageHandler2); |
701 | QVERIFY(same); |
702 | QtMessageHandler messageHandler = qInstallMessageHandler((QtMessageHandler)0); // set 0, should set default and return myMessageHandler |
703 | same = (*messageHandler == *myMessageHandler); |
704 | QVERIFY(same); |
705 | } |
706 | |
707 | QMutex s_mutex; |
708 | QStringList s_messages; |
709 | QSemaphore s_sema; |
710 | |
711 | static void threadSafeMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) |
712 | { |
713 | QMutexLocker lock(&s_mutex); |
714 | s_messages.append(t: msg); |
715 | Q_UNUSED(type); |
716 | Q_UNUSED(context); |
717 | } |
718 | |
719 | static void doDebug() // called in each thread |
720 | { |
721 | s_sema.acquire(); |
722 | qDebug() << "doDebug" ; |
723 | } |
724 | |
725 | void tst_QDebug::threadSafety() const |
726 | { |
727 | MessageHandlerSetter mhs(threadSafeMessageHandler); |
728 | const int numThreads = 10; |
729 | QThreadPool::globalInstance()->setMaxThreadCount(numThreads); |
730 | QFutureSynchronizer<void> sync; |
731 | for (int i = 0; i < numThreads; ++i) { |
732 | sync.addFuture(future: QtConcurrent::run(functionPointer: &doDebug)); |
733 | } |
734 | s_sema.release(n: numThreads); |
735 | sync.waitForFinished(); |
736 | QMutexLocker lock(&s_mutex); |
737 | QCOMPARE(s_messages.count(), numThreads); |
738 | for (int i = 0; i < numThreads; ++i) { |
739 | QCOMPARE(s_messages.at(i), QStringLiteral("doDebug" )); |
740 | } |
741 | } |
742 | |
743 | // Should compile: instentiation of unrelated operator<< should not cause cause compilation |
744 | // error in QDebug operators (QTBUG-47375) |
745 | class TestClassA {}; |
746 | class TestClassB {}; |
747 | |
748 | template <typename T> |
749 | TestClassA& operator<< (TestClassA& s, T&) { return s; }; |
750 | template<> TestClassA& operator<< <TestClassB>(TestClassA& s, TestClassB& l); |
751 | |
752 | QTEST_MAIN(tst_QDebug); |
753 | #include "tst_qdebug.moc" |
754 | |