QDebug: pretty-print QStrings and QStringRefs
[ChangeLog][QtCore][QDebug] Printing of QStrings and QStringRefs whenever "noquote" is not active now prints the strings in a format that can be copied back to C++ code. All characters that aren't printable in US-ASCII are escaped (this includes printable Unicode characters outside of US-ASCII). Pretty-printing will not respect QTextFormat padding or field widths. Change-Id: I169a8a0508e24693f5652f0129defe7f709e5d08 Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
e00d8ad86b
commit
2c01d402e1
@ -1,6 +1,7 @@
|
|||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
**
|
**
|
||||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||||
|
** Copyright (C) 2014 Intel Corporation.
|
||||||
** Contact: http://www.qt-project.org/legal
|
** Contact: http://www.qt-project.org/legal
|
||||||
**
|
**
|
||||||
** This file is part of the QtCore module of the Qt Toolkit.
|
** This file is part of the QtCore module of the Qt Toolkit.
|
||||||
@ -40,9 +41,12 @@
|
|||||||
|
|
||||||
#include "qdebug.h"
|
#include "qdebug.h"
|
||||||
#include <private/qtextstream_p.h>
|
#include <private/qtextstream_p.h>
|
||||||
|
#include <private/qtools_p.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
using QtMiscUtils::toHexUpper;
|
||||||
|
|
||||||
// This file is needed to force compilation of QDebug into the kernel library.
|
// This file is needed to force compilation of QDebug into the kernel library.
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -168,6 +172,105 @@ void QDebug::putUcs4(uint ucs4)
|
|||||||
maybeQuote('\'');
|
maybeQuote('\'');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
static inline void putEscapedString(QTextStreamPrivate *d, const Char *begin, int length)
|
||||||
|
{
|
||||||
|
QChar quote(QLatin1Char('"'));
|
||||||
|
d->write("e, 1);
|
||||||
|
|
||||||
|
const Char *end = begin + length;
|
||||||
|
for (const Char *p = begin; p != end; ++p) {
|
||||||
|
if (sizeof(Char) == sizeof(QChar)) {
|
||||||
|
int runLength = 0;
|
||||||
|
while (p + runLength != end &&
|
||||||
|
p[runLength] < 0x7f && p[runLength] >= 0x20 && p[runLength] != '\\' && p[runLength] != '"')
|
||||||
|
++runLength;
|
||||||
|
if (runLength) {
|
||||||
|
d->write(reinterpret_cast<const QChar *>(p), runLength);
|
||||||
|
p += runLength - 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// print as an escape sequence
|
||||||
|
int buflen = 2;
|
||||||
|
ushort buf[sizeof "\\U12345678" - 1];
|
||||||
|
buf[0] = '\\';
|
||||||
|
|
||||||
|
switch (*p) {
|
||||||
|
case '"':
|
||||||
|
case '\\':
|
||||||
|
buf[1] = *p;
|
||||||
|
break;
|
||||||
|
case '\b':
|
||||||
|
buf[1] = 'b';
|
||||||
|
break;
|
||||||
|
case '\f':
|
||||||
|
buf[1] = 'f';
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
buf[1] = 'n';
|
||||||
|
break;
|
||||||
|
case '\r':
|
||||||
|
buf[1] = 'r';
|
||||||
|
break;
|
||||||
|
case '\t':
|
||||||
|
buf[1] = 't';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (QChar::isHighSurrogate(*p)) {
|
||||||
|
if ((p + 1) != end && QChar::isLowSurrogate(p[1])) {
|
||||||
|
// properly-paired surrogates
|
||||||
|
uint ucs4 = QChar::surrogateToUcs4(*p, p[1]);
|
||||||
|
++p;
|
||||||
|
buf[1] = 'U';
|
||||||
|
buf[2] = '0'; // toHexUpper(ucs4 >> 32);
|
||||||
|
buf[3] = '0'; // toHexUpper(ucs4 >> 28);
|
||||||
|
buf[4] = toHexUpper(ucs4 >> 20);
|
||||||
|
buf[5] = toHexUpper(ucs4 >> 16);
|
||||||
|
buf[6] = toHexUpper(ucs4 >> 12);
|
||||||
|
buf[7] = toHexUpper(ucs4 >> 8);
|
||||||
|
buf[8] = toHexUpper(ucs4 >> 4);
|
||||||
|
buf[9] = toHexUpper(ucs4);
|
||||||
|
buflen = 10;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// improperly-paired surrogates, fall through
|
||||||
|
}
|
||||||
|
buf[1] = 'u';
|
||||||
|
if (sizeof(Char) == 1) {
|
||||||
|
buf[2] = buf[3] = '0';
|
||||||
|
} else {
|
||||||
|
buf[2] = toHexUpper(*p >> 12);
|
||||||
|
buf[3] = toHexUpper(*p >> 8);
|
||||||
|
}
|
||||||
|
buf[4] = toHexUpper(*p >> 4);
|
||||||
|
buf[5] = toHexUpper(*p);
|
||||||
|
buflen = 6;
|
||||||
|
}
|
||||||
|
d->write(reinterpret_cast<QChar *>(buf), buflen);
|
||||||
|
}
|
||||||
|
|
||||||
|
d->write("e, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\internal
|
||||||
|
Duplicated from QtTest::toPrettyUnicode().
|
||||||
|
*/
|
||||||
|
void QDebug::putString(const QChar *begin, size_t length)
|
||||||
|
{
|
||||||
|
if (stream->testFlag(Stream::NoQuotes)) {
|
||||||
|
// no quotes, write the string directly too (no pretty-printing)
|
||||||
|
// this respects the QTextStream state, though
|
||||||
|
stream->ts.d_ptr->putString(begin, length);
|
||||||
|
} else {
|
||||||
|
// we'll reset the QTextStream formatting mechanisms, so save the state
|
||||||
|
QDebugStateSaver saver(*this);
|
||||||
|
stream->ts.d_ptr->params.reset();
|
||||||
|
putEscapedString(stream->ts.d_ptr.data(), reinterpret_cast<const ushort *>(begin), length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn QDebug::swap(QDebug &other)
|
\fn QDebug::swap(QDebug &other)
|
||||||
|
@ -78,6 +78,7 @@ class Q_CORE_EXPORT QDebug
|
|||||||
} *stream;
|
} *stream;
|
||||||
|
|
||||||
void putUcs4(uint ucs4);
|
void putUcs4(uint ucs4);
|
||||||
|
void putString(const QChar *begin, size_t length);
|
||||||
public:
|
public:
|
||||||
inline QDebug(QIODevice *device) : stream(new Stream(device)) {}
|
inline QDebug(QIODevice *device) : stream(new Stream(device)) {}
|
||||||
inline QDebug(QString *string) : stream(new Stream(string)) {}
|
inline QDebug(QString *string) : stream(new Stream(string)) {}
|
||||||
@ -118,8 +119,8 @@ public:
|
|||||||
inline QDebug &operator<<(float t) { stream->ts << t; return maybeSpace(); }
|
inline QDebug &operator<<(float t) { stream->ts << t; return maybeSpace(); }
|
||||||
inline QDebug &operator<<(double t) { stream->ts << t; return maybeSpace(); }
|
inline QDebug &operator<<(double t) { stream->ts << t; return maybeSpace(); }
|
||||||
inline QDebug &operator<<(const char* t) { stream->ts << QString::fromUtf8(t); return maybeSpace(); }
|
inline QDebug &operator<<(const char* t) { stream->ts << QString::fromUtf8(t); return maybeSpace(); }
|
||||||
inline QDebug &operator<<(const QString & t) { maybeQuote(); stream->ts << t; maybeQuote(); return maybeSpace(); }
|
inline QDebug &operator<<(const QString & t) { putString(t.constData(), uint(t.length())); return maybeSpace(); }
|
||||||
inline QDebug &operator<<(const QStringRef & t) { return operator<<(t.toString()); }
|
inline QDebug &operator<<(const QStringRef & t) { putString(t.constData(), uint(t.length())); return maybeSpace(); }
|
||||||
inline QDebug &operator<<(QLatin1String t) { maybeQuote(); stream->ts << t; maybeQuote(); return maybeSpace(); }
|
inline QDebug &operator<<(QLatin1String t) { maybeQuote(); stream->ts << t; maybeQuote(); return maybeSpace(); }
|
||||||
inline QDebug &operator<<(const QByteArray & t) { maybeQuote(); stream->ts << t; maybeQuote(); return maybeSpace(); }
|
inline QDebug &operator<<(const QByteArray & t) { maybeQuote(); stream->ts << t; maybeQuote(); return maybeSpace(); }
|
||||||
inline QDebug &operator<<(const void * t) { stream->ts << t; return maybeSpace(); }
|
inline QDebug &operator<<(const void * t) { stream->ts << t; return maybeSpace(); }
|
||||||
|
@ -806,7 +806,7 @@ inline void QTextStreamPrivate::restoreToSavedConverterState()
|
|||||||
/*!
|
/*!
|
||||||
\internal
|
\internal
|
||||||
*/
|
*/
|
||||||
inline void QTextStreamPrivate::write(const QChar *data, int len)
|
void QTextStreamPrivate::write(const QChar *data, int len)
|
||||||
{
|
{
|
||||||
if (string) {
|
if (string) {
|
||||||
// ### What about seek()??
|
// ### What about seek()??
|
||||||
@ -885,7 +885,7 @@ inline void QTextStreamPrivate::putChar(QChar ch)
|
|||||||
/*!
|
/*!
|
||||||
\internal
|
\internal
|
||||||
*/
|
*/
|
||||||
inline void QTextStreamPrivate::putString(const QChar *data, int len, bool number)
|
void QTextStreamPrivate::putString(const QChar *data, int len, bool number)
|
||||||
{
|
{
|
||||||
QString pad;
|
QString pad;
|
||||||
int padLeft = 0, padRight = 0;
|
int padLeft = 0, padRight = 0;
|
||||||
|
@ -185,6 +185,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
Q_DISABLE_COPY(QTextStream)
|
Q_DISABLE_COPY(QTextStream)
|
||||||
friend class QDebugStateSaverPrivate;
|
friend class QDebugStateSaverPrivate;
|
||||||
|
friend class QDebug;
|
||||||
|
|
||||||
QScopedPointer<QTextStreamPrivate> d_ptr;
|
QScopedPointer<QTextStreamPrivate> d_ptr;
|
||||||
};
|
};
|
||||||
|
@ -2146,6 +2146,8 @@ char *toHexRepresentation(const char *ba, int length)
|
|||||||
\internal
|
\internal
|
||||||
Returns the same QString but with only the ASCII characters still shown;
|
Returns the same QString but with only the ASCII characters still shown;
|
||||||
everything else is replaced with \c {\uXXXX}.
|
everything else is replaced with \c {\uXXXX}.
|
||||||
|
|
||||||
|
Similar to QDebug::putString().
|
||||||
*/
|
*/
|
||||||
char *toPrettyUnicode(const ushort *p, int length)
|
char *toPrettyUnicode(const ushort *p, int length)
|
||||||
{
|
{
|
||||||
|
@ -52,6 +52,7 @@ private slots:
|
|||||||
void stateSaver() const;
|
void stateSaver() const;
|
||||||
void veryLongWarningMessage() const;
|
void veryLongWarningMessage() const;
|
||||||
void qDebugQChar() const;
|
void qDebugQChar() const;
|
||||||
|
void qDebugQString() const;
|
||||||
void qDebugQStringRef() const;
|
void qDebugQStringRef() const;
|
||||||
void qDebugQLatin1String() const;
|
void qDebugQLatin1String() const;
|
||||||
void qDebugQByteArray() const;
|
void qDebugQByteArray() const;
|
||||||
@ -344,6 +345,54 @@ void tst_QDebug::qDebugQChar() const
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QDebug::qDebugQString() const
|
||||||
|
{
|
||||||
|
/* Use a basic string. */
|
||||||
|
{
|
||||||
|
QString file, function;
|
||||||
|
int line = 0;
|
||||||
|
const QString in(QLatin1String("input"));
|
||||||
|
const QStringRef inRef(&in);
|
||||||
|
|
||||||
|
MessageHandlerSetter mhs(myMessageHandler);
|
||||||
|
{ qDebug() << inRef; }
|
||||||
|
#ifndef QT_NO_MESSAGELOGCONTEXT
|
||||||
|
file = __FILE__; line = __LINE__ - 2; function = Q_FUNC_INFO;
|
||||||
|
#endif
|
||||||
|
QCOMPARE(s_msgType, QtDebugMsg);
|
||||||
|
QCOMPARE(s_msg, QString::fromLatin1("\"input\""));
|
||||||
|
QCOMPARE(QString::fromLatin1(s_file), file);
|
||||||
|
QCOMPARE(s_line, line);
|
||||||
|
QCOMPARE(QString::fromLatin1(s_function), function);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* simpler tests from now on */
|
||||||
|
MessageHandlerSetter mhs(myMessageHandler);
|
||||||
|
|
||||||
|
QString string = "Hello";
|
||||||
|
qDebug() << string;
|
||||||
|
QCOMPARE(s_msg, QString("\"Hello\""));
|
||||||
|
|
||||||
|
qDebug().noquote().nospace() << string;
|
||||||
|
QCOMPARE(s_msg, string);
|
||||||
|
|
||||||
|
qDebug().noquote().nospace() << qSetFieldWidth(8) << string;
|
||||||
|
QCOMPARE(s_msg, " " + string);
|
||||||
|
|
||||||
|
string = QLatin1String("\nSm\xF8rg\xE5sbord\\");
|
||||||
|
qDebug().noquote().nospace() << string;
|
||||||
|
QCOMPARE(s_msg, string);
|
||||||
|
|
||||||
|
qDebug() << string;
|
||||||
|
QCOMPARE(s_msg, QString("\"\\nSm\\u00F8rg\\u00E5sbord\\\\\""));
|
||||||
|
|
||||||
|
// surrogate pairs (including broken pairings)
|
||||||
|
ushort utf16[] = { 0xDC00, 0xD800, 0xDC00, 'x', 0xD800, 0xDC00, 0xD800, 0 };
|
||||||
|
string = QString::fromUtf16(utf16);
|
||||||
|
qDebug() << string;
|
||||||
|
QCOMPARE(s_msg, QString("\"\\uDC00\\U00010000x\\U00010000\\uD800\""));
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QDebug::qDebugQStringRef() const
|
void tst_QDebug::qDebugQStringRef() const
|
||||||
{
|
{
|
||||||
/* Use a basic string. */
|
/* Use a basic string. */
|
||||||
@ -403,6 +452,24 @@ void tst_QDebug::qDebugQLatin1String() const
|
|||||||
QCOMPARE(QString::fromLatin1(s_file), file);
|
QCOMPARE(QString::fromLatin1(s_file), file);
|
||||||
QCOMPARE(s_line, line);
|
QCOMPARE(s_line, line);
|
||||||
QCOMPARE(QString::fromLatin1(s_function), function);
|
QCOMPARE(QString::fromLatin1(s_function), function);
|
||||||
|
|
||||||
|
/* simpler tests from now on */
|
||||||
|
QLatin1String string("\"Hello\"");
|
||||||
|
qDebug() << string;
|
||||||
|
QCOMPARE(s_msg, QString("\"\\\"Hello\\\"\""));
|
||||||
|
|
||||||
|
qDebug().noquote().nospace() << string;
|
||||||
|
QCOMPARE(s_msg, QString(string));
|
||||||
|
|
||||||
|
qDebug().noquote().nospace() << qSetFieldWidth(8) << string;
|
||||||
|
QCOMPARE(s_msg, " " + QString(string));
|
||||||
|
|
||||||
|
string = QLatin1String("\nSm\xF8rg\xE5sbord\\");
|
||||||
|
qDebug().noquote().nospace() << string;
|
||||||
|
QCOMPARE(s_msg, QString(string));
|
||||||
|
|
||||||
|
qDebug() << string;
|
||||||
|
QCOMPARE(s_msg, QString("\"\\nSm\\u00F8rg\\u00E5sbord\\\\\""));
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QDebug::qDebugQByteArray() const
|
void tst_QDebug::qDebugQByteArray() const
|
||||||
|
@ -113,7 +113,7 @@
|
|||||||
</TestFunction>
|
</TestFunction>
|
||||||
<TestFunction name="encoding">
|
<TestFunction name="encoding">
|
||||||
<Message type="qdebug" file="" line="0">
|
<Message type="qdebug" file="" line="0">
|
||||||
<Description><![CDATA["Ülrich Ümläut"]]></Description>
|
<Description><![CDATA["\u00DClrich \u00DCml\u00E4ut"]]></Description>
|
||||||
</Message>
|
</Message>
|
||||||
<Incident type="pass" file="" line="0" />
|
<Incident type="pass" file="" line="0" />
|
||||||
<Duration msecs="0"/>
|
<Duration msecs="0"/>
|
||||||
|
@ -115,7 +115,7 @@
|
|||||||
</TestFunction>
|
</TestFunction>
|
||||||
<TestFunction name="encoding">
|
<TestFunction name="encoding">
|
||||||
<Message type="qdebug" file="" line="0">
|
<Message type="qdebug" file="" line="0">
|
||||||
<Description><![CDATA["Ülrich Ümläut"]]></Description>
|
<Description><![CDATA["\u00DClrich \u00DCml\u00E4ut"]]></Description>
|
||||||
</Message>
|
</Message>
|
||||||
<Incident type="pass" file="" line="0" />
|
<Incident type="pass" file="" line="0" />
|
||||||
<Duration msecs="0"/>
|
<Duration msecs="0"/>
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
<failure message="failure message" result="fail"/>
|
<failure message="failure message" result="fail"/>
|
||||||
</testcase>
|
</testcase>
|
||||||
<testcase result="pass" name="encoding">
|
<testcase result="pass" name="encoding">
|
||||||
<!-- message=""Ülrich Ümläut"" type="qdebug" -->
|
<!-- message=""\u00DClrich \u00DCml\u00E4ut"" type="qdebug" -->
|
||||||
</testcase>
|
</testcase>
|
||||||
<testcase result="pass" name="cleanupTestCase"/>
|
<testcase result="pass" name="cleanupTestCase"/>
|
||||||
<system-err>
|
<system-err>
|
||||||
@ -46,6 +46,6 @@
|
|||||||
<![CDATA[quotes " text" more text]]>
|
<![CDATA[quotes " text" more text]]>
|
||||||
<![CDATA[xml close > open < tags < text]]>
|
<![CDATA[xml close > open < tags < text]]>
|
||||||
<![CDATA[all > " mixed ]]]><![CDATA[]> up > " in < the ]]]><![CDATA[]> hopes < of triggering "< ]]]><![CDATA[]> bugs]]>
|
<![CDATA[all > " mixed ]]]><![CDATA[]> up > " in < the ]]]><![CDATA[]> hopes < of triggering "< ]]]><![CDATA[]> bugs]]>
|
||||||
<![CDATA["Ülrich Ümläut"]]>
|
<![CDATA["\u00DClrich \u00DCml\u00E4ut"]]>
|
||||||
</system-err>
|
</system-err>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user