Allow customization of qDebug output at runtime
authorKai Koehne <kai.koehne@nokia.com>
Fri, 3 Feb 2012 08:35:22 +0000 (09:35 +0100)
committerQt by Nokia <qt-info@nokia.com>
Thu, 9 Feb 2012 17:11:21 +0000 (18:11 +0100)
Check the QT_OUTPUT_PATTERN environment variable in the default
message handler to customize the output of messages. Following
place holders are right now supported:
 %{message}, %{type}, %{file}, %{line}, %{function}

The original cleanupFuncinfo was written by Thiago Macieira.

Change-Id: I6ad25baaa0e6a1c9f886105d2a93ef3310e512a9
Reviewed-by: Olivier Goffart <ogoffart@woboq.com>
Reviewed-by: David Faure <faure@kde.org>
17 files changed:
qmake/Makefile.unix
qmake/Makefile.win32
qmake/Makefile.win32-g++
qmake/Makefile.win32-g++-sh
qmake/qmake.pri
src/corelib/global/qglobal.cpp
src/corelib/global/qlogging.cpp
src/corelib/kernel/qcoreapplication_win.cpp
tests/auto/corelib/global/global.pro
tests/auto/corelib/global/qlogging/app/app.pro [new file with mode: 0644]
tests/auto/corelib/global/qlogging/app/main.cpp [new file with mode: 0644]
tests/auto/corelib/global/qlogging/qlogging.pro [new file with mode: 0644]
tests/auto/corelib/global/qlogging/tst_qlogging.cpp [new file with mode: 0644]
tests/auto/corelib/global/qlogging/tst_qlogging.pro [new file with mode: 0644]
tests/auto/corelib/global/qmessagehandler/qmessagehandler.pro [deleted file]
tests/auto/corelib/global/qmessagehandler/tst_qmessagehandler.cpp [deleted file]
tools/configure/configure.pro

index 286e2eb..16af493 100644 (file)
@@ -22,7 +22,7 @@ QOBJS=qtextcodec.o qutfcodec.o qstring.o qtextstream.o qiodevice.o qmalloc.o qgl
       qfileinfo.o qdatetime.o qstringlist.o qabstractfileengine.o qtemporaryfile.o \
       qmap.o qmetatype.o qsettings.o qsystemerror.o qlibraryinfo.o qvariant.o qvsnprintf.o \
       qlocale.o qlocale_tools.o qlocale_unix.o qlinkedlist.o qurl.o qnumeric.o qcryptographichash.o \
-      qxmlstream.o qxmlutils.o \
+      qxmlstream.o qxmlutils.o qlogging.o \
       $(QTOBJS)
 
 
@@ -66,6 +66,7 @@ DEPEND_SRC=project.cpp property.cpp meta.cpp main.cpp generators/makefile.cpp ge
           $(SOURCE_PATH)/src/corelib/xml/qxmlstream.cpp \
           $(SOURCE_PATH)/src/corelib/xml/qxmlutils.cpp \
           $(SOURCE_PATH)/src/corelib/kernel/qsystemerror.cpp \
+          $(SOURCE_PATH)/src/corelib/global/qlogging.cpp \
           $(QTSRCS)
 
 CPPFLAGS = -g -I. -Igenerators -Igenerators/unix -Igenerators/win32 \
@@ -316,6 +317,9 @@ qxmlstream.o: $(SOURCE_PATH)/src/corelib/xml/qxmlstream.cpp
 qxmlutils.o: $(SOURCE_PATH)/src/corelib/xml/qxmlutils.cpp
        $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/xml/qxmlutils.cpp
 
+qlogging.o: $(SOURCE_PATH)/src/corelib/global/qlogging.cpp
+       $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/global/qlogging.cpp
+
 #default rules
 .cpp.o:
        $(CXX) -c -o $@ $(CXXFLAGS) $<
index e767786..3efe6a6 100644 (file)
@@ -118,8 +118,8 @@ QTOBJS= \
        qmetatype.obj \
        qxmlstream.obj \
        qxmlutils.obj \
-       qnumeric.obj
-
+       qnumeric.obj \
+       qlogging.obj
 
 first all: qmake.exe
 
index 443dba3..585061e 100644 (file)
@@ -95,7 +95,8 @@ QTOBJS= \
        qmetatype.o \
        qxmlstream.o \
        qxmlutils.o \
-       qnumeric.o
+       qnumeric.o \
+       qlogging.o
 
 
 qmake.exe: $(OBJS) $(QTOBJS)
@@ -339,3 +340,5 @@ qxmlstream.o: $(SOURCE_PATH)/src/corelib/xml/qxmlstream.cpp
 qxmlutils.o: $(SOURCE_PATH)/src/corelib/xml/qxmlutils.cpp
        $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/xml/qxmlutils.cpp
 
+qlogging.o: $(SOURCE_PATH)/src/corelib/global/qlogging.cpp
+       $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/global/qlogging.cpp
index bc8356e..6dfb486 100644 (file)
@@ -95,7 +95,8 @@ QTOBJS= \
        qmetatype.o \
        qxmlstream.o \
        qxmlutils.o \
-       qnumeric.o
+       qnumeric.o \
+       qlogging.o
 
 qmake.exe: $(OBJS) $(QTOBJS)
        $(LINKQMAKE)
@@ -337,3 +338,6 @@ qxmlstream.o: $(SOURCE_PATH)/src/corelib/xml/qxmlstream.cpp
 
 qxmlutils.o: $(SOURCE_PATH)/src/corelib/xml/qxmlutils.cpp
        $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/xml/qxmlutils.cpp
+
+qlogging.o: $(SOURCE_PATH)/src/corelib/global/qlogging.cpp
+       $(CXX) $(CXXFLAGS) $(SOURCE_PATH)/src/corelib/global/qlogging.cpp
index 1f59531..ba6ab38 100644 (file)
@@ -76,7 +76,8 @@ bootstrap { #Qt code
         qvector.cpp \
         qvsnprintf.cpp \
         qxmlstream.cpp \
-        qxmlutils.cpp
+        qxmlutils.cpp \
+        qlogging.cpp
 
    HEADERS+= \
         qbitarray.h \
index 01b3e2e..983116f 100644 (file)
@@ -1862,27 +1862,32 @@ extern Q_CORE_EXPORT void qWinMessageHandler(QtMsgType t, const QMessageLogConte
                                              const char *str);
 #endif
 
+// defined in qlogging.cpp
+extern Q_CORE_EXPORT QByteArray qMessageFormatString(QtMsgType type, const QMessageLogContext &context,
+                                                     const char *str);
+
 /*!
     \internal
 */
-static void qDefaultMsgHandler(QtMsgType, const char *buf)
+static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context, const char *buf)
 {
+    QByteArray logMessage = qMessageFormatString(type, context, buf);
 #if defined(Q_OS_WINCE)
-        QString fstr = QString::fromLatin1(buf);
-        fstr += QLatin1Char('\n');
-        OutputDebugString(reinterpret_cast<const wchar_t *> (fstr.utf16()));
+    QString fstr = QString::fromLocal8Bit(logMessage);
+    OutputDebugString(reinterpret_cast<const wchar_t *> (fstr.utf16()));
 #else
-        fprintf(stderr, "%s\n", buf);
-        fflush(stderr);
+    fprintf(stderr, "%s", logMessage.constData());
+    fflush(stderr);
 #endif
 }
 
 /*!
     \internal
 */
-static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &, const char *buf)
+static void qDefaultMsgHandler(QtMsgType type, const char *buf)
 {
-    qDefaultMsgHandler(type, buf);
+    QMessageLogContext emptyContext;
+    qDefaultMessageHandler(type, emptyContext, buf);
 }
 
 QMessageHandler qInstallMessageHandler(QMessageHandler h)
index bc26c9b..39b3205 100644 (file)
 **
 ****************************************************************************/
 
-#include <qlogging.h>
+#include "qlogging.h"
+#include "qlist.h"
+#include "qbytearray.h"
+#include "qstring.h"
+#include "qvarlengtharray.h"
+
+#include <stdio.h>
 
 QT_BEGIN_NAMESPACE
 
@@ -73,4 +79,309 @@ QT_BEGIN_NAMESPACE
     \sa QMessageLogContext, qDebug(), qWarning(), qCritical(), qFatal()
 */
 
+/*!
+    \internal
+*/
+Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info)
+{
+    // Strip the function info down to the base function name
+    // note that this throws away the template definitions,
+    // the parameter types (overloads) and any const/volatile qualifiers.
+
+    if (info.isEmpty())
+        return info;
+
+    int pos;
+
+    // skip trailing [with XXX] for templates (gcc)
+    pos = info.size() - 1;
+    if (info.endsWith(']')) {
+        while (--pos) {
+            if (info.at(pos) == '[')
+                info.truncate(pos);
+        }
+    }
+
+    // operator names with '(', ')', '<', '>' in it
+    static const char operator_call[] = "operator()";
+    static const char operator_lessThan[] = "operator<";
+    static const char operator_greaterThan[] = "operator>";
+    static const char operator_lessThanEqual[] = "operator<=";
+    static const char operator_greaterThanEqual[] = "operator>=";
+
+    // canonize operator names
+    info.replace("operator ", "operator");
+
+    // remove argument list
+    forever {
+        int parencount = 0;
+        pos = info.lastIndexOf(')');
+        if (pos == -1) {
+            // Don't know how to parse this function name
+            return info;
+        }
+
+        // find the beginning of the argument list
+        --pos;
+        ++parencount;
+        while (pos && parencount) {
+            if (info.at(pos) == ')')
+                ++parencount;
+            else if (info.at(pos) == '(')
+                --parencount;
+            --pos;
+        }
+        if (parencount != 0)
+            return info;
+
+        info.truncate(++pos);
+
+        if (info.at(pos - 1) == ')') {
+            if (info.indexOf(operator_call) == pos - (int)strlen(operator_call))
+                break;
+
+            // this function returns a pointer to a function
+            // and we matched the arguments of the return type's parameter list
+            // try again
+            info.remove(0, info.indexOf('('));
+            info.chop(1);
+            continue;
+        } else {
+            break;
+        }
+    }
+
+    // find the beginning of the function name
+    int parencount = 0;
+    int templatecount = 0;
+    --pos;
+
+    // make sure special characters in operator names are kept
+    if (pos > -1) {
+        switch (info.at(pos)) {
+        case ')':
+            if (info.indexOf(operator_call) == pos - (int)strlen(operator_call) + 1)
+                pos -= 2;
+            break;
+        case '<':
+            if (info.indexOf(operator_lessThan) == pos - (int)strlen(operator_lessThan) + 1)
+                --pos;
+            break;
+        case '>':
+            if (info.indexOf(operator_greaterThan) == pos - (int)strlen(operator_greaterThan) + 1)
+                --pos;
+            break;
+        case '=': {
+            int operatorLength = (int)strlen(operator_lessThanEqual);
+            if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1)
+                pos -= 2;
+            else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1)
+                pos -= 2;
+            break;
+        }
+        default:
+            break;
+        }
+    }
+
+    while (pos > -1) {
+        if (parencount < 0 || templatecount < 0)
+            return info;
+
+        char c = info.at(pos);
+        if (c == ')')
+            ++parencount;
+        else if (c == '(')
+            --parencount;
+        else if (c == '>')
+            ++templatecount;
+        else if (c == '<')
+            --templatecount;
+        else if (c == ' ' && templatecount == 0 && parencount == 0)
+            break;
+
+        --pos;
+    }
+    info = info.mid(pos + 1);
+
+    // we have the full function name now.
+    // clean up the templates
+    while ((pos = info.lastIndexOf('>')) != -1) {
+        if (!info.contains('<'))
+            break;
+
+        // find the matching close
+        int end = pos;
+        templatecount = 1;
+        --pos;
+        while (pos && templatecount) {
+            register char c = info.at(pos);
+            if (c == '>')
+                ++templatecount;
+            else if (c == '<')
+                --templatecount;
+            --pos;
+        }
+        ++pos;
+        info.remove(pos, end - pos + 1);
+    }
+
+    return info;
+}
+
+// tokens as recognized in QT_MESSAGE_PATTERN
+static const char typeTokenC[] = "%{type}";
+static const char messageTokenC[] = "%{message}";
+static const char fileTokenC[] = "%{file}";
+static const char lineTokenC[] = "%{line}";
+static const char functionTokenC[] = "%{function}";
+static const char emptyTokenC[] = "";
+
+struct QMessagePattern {
+    QMessagePattern();
+    ~QMessagePattern();
+
+    // 0 terminated arrays of literal tokens / literal or placeholder tokens
+    const char **literals;
+    const char **tokens;
+};
+
+QMessagePattern::QMessagePattern()
+{
+    QString pattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN"));
+    if (pattern.isEmpty()) {
+        pattern = QLatin1String("%{message}");
+    }
+
+    // scanner
+    QList<QString> lexemes;
+    QString lexeme;
+    bool inPlaceholder = false;
+    for (int i = 0; i < pattern.size(); ++i) {
+        const QChar c = pattern.at(i);
+        if ((c == QLatin1Char('%'))
+                && !inPlaceholder) {
+            if ((i + 1 < pattern.size())
+                    && pattern.at(i + 1) == QLatin1Char('{')) {
+                // beginning of placeholder
+                if (!lexeme.isEmpty()) {
+                    lexemes.append(lexeme);
+                    lexeme.clear();
+                }
+                inPlaceholder = true;
+            }
+        }
+
+        lexeme.append(c);
+
+        if ((c == QLatin1Char('}') && inPlaceholder)) {
+            // end of placeholder
+            lexemes.append(lexeme);
+            lexeme.clear();
+            inPlaceholder = false;
+        }
+    }
+    if (!lexeme.isEmpty())
+        lexemes.append(lexeme);
+
+    // tokenizer
+    QVarLengthArray<const char*> literalsVar;
+    tokens = new const char*[lexemes.size() + 1];
+    tokens[lexemes.size()] = 0;
+
+    for (int i = 0; i < lexemes.size(); ++i) {
+        const QString lexeme = lexemes.at(i);
+        if (lexeme.startsWith(QLatin1String("%{"))
+                && lexeme.endsWith(QLatin1Char('}'))) {
+            // placeholder
+            if (lexeme == QLatin1String(typeTokenC)) {
+                tokens[i] = typeTokenC;
+            } else if (lexeme == QLatin1String(messageTokenC))
+                tokens[i] = messageTokenC;
+            else if (lexeme == QLatin1String(fileTokenC))
+                tokens[i] = fileTokenC;
+            else if (lexeme == QLatin1String(lineTokenC))
+                tokens[i] = lineTokenC;
+            else if (lexeme == QLatin1String(functionTokenC))
+                tokens[i] = functionTokenC;
+            else {
+                fprintf(stderr, "%s\n",
+                        QString::fromLatin1("QT_MESSAGE_PATTERN: Unknown placeholder %1\n"
+                                            ).arg(lexeme).toLocal8Bit().constData());
+                fflush(stderr);
+                tokens[i] = emptyTokenC;
+            }
+        } else {
+            char *literal = new char[lexeme.size() + 1];
+            strncpy(literal, lexeme.toLocal8Bit().constData(), lexeme.size());
+            literal[lexeme.size()] = '\0';
+            literalsVar.append(literal);
+            tokens[i] = literal;
+        }
+    }
+    literals = new const char*[literalsVar.size() + 1];
+    literals[literalsVar.size()] = 0;
+    memcpy(literals, literalsVar.constData(), literalsVar.size() * sizeof(const char*));
+}
+
+QMessagePattern::~QMessagePattern()
+{
+    for (int i = 0; literals[i] != 0; ++i)
+        delete [] literals[i];
+    delete [] literals;
+    literals = 0;
+    delete [] tokens;
+    tokens = 0;
+}
+
+Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern)
+
+/*!
+    \internal
+*/
+Q_CORE_EXPORT QByteArray qMessageFormatString(QtMsgType type, const QMessageLogContext &context,
+                                               const char *str)
+{
+    QByteArray message;
+
+    QMessagePattern *pattern = qMessagePattern();
+    if (!pattern) {
+        // after destruction of static QMessagePattern instance
+        message.append(str);
+        message.append('\n');
+        return message;
+    }
+
+    // we do not convert file, function, line literals to local encoding due to overhead
+    for (int i = 0; pattern->tokens[i] != 0; ++i) {
+        const char *token = pattern->tokens[i];
+        if (token == messageTokenC) {
+            message.append(str);
+        } else if (token == typeTokenC) {
+            switch (type) {
+            case QtDebugMsg:   message.append("debug"); break;
+            case QtWarningMsg: message.append("warning"); break;
+            case QtCriticalMsg:message.append("critical"); break;
+            case QtFatalMsg:   message.append("fatal"); break;
+            }
+        } else if (token == fileTokenC) {
+            if (context.file)
+                message.append(context.file);
+            else
+                message.append("unknown");
+        } else if (token == lineTokenC) {
+            message.append(QString::number(context.line).toLatin1().constData());
+        } else if (token == functionTokenC) {
+            if (context.function)
+                message.append(qCleanupFuncinfo(context.function));
+            else
+                message.append("unknown");
+        } else {
+            message.append(token);
+        }
+    }
+    message.append('\n');
+    return message;
+}
+
 QT_END_NAMESPACE
index 9a45f28..c1f7c8a 100644 (file)
@@ -140,30 +140,30 @@ public:
     { LeaveCriticalSection(&cs); }
 };
 
-Q_CORE_EXPORT void qWinMsgHandler(QtMsgType t, const char* str)
-{
-    Q_UNUSED(t);
-    // OutputDebugString is not threadsafe.
+// defined in qlogging.cpp
+extern Q_CORE_EXPORT QByteArray qMessageFormatString(QtMsgType type,
+                                                     const QMessageLogContext &context,
+                                                     const char *str);
 
+Q_CORE_EXPORT void qWinMessageHandler(QtMsgType t, const QMessageLogContext &context, const char *str)
+{
     // cannot use QMutex here, because qWarning()s in the QMutex
     // implementation may cause this function to recurse
     static QWinMsgHandlerCriticalSection staticCriticalSection;
 
-    if (!str)
-        str = "(null)";
+    QByteArray message = qMessageFormatString(t, context, str);
+    QString s(QString::fromLocal8Bit(message));
 
+    // OutputDebugString is not threadsafe.
     staticCriticalSection.lock();
-
-    QString s(QString::fromLocal8Bit(str));
-    s += QLatin1Char('\n');
     OutputDebugString((wchar_t*)s.utf16());
-
     staticCriticalSection.unlock();
 }
 
-Q_CORE_EXPORT void qWinMessageHandler(QtMsgType t, const QMessageLogContext &, const char* str)
+Q_CORE_EXPORT void qWinMsgHandler(QtMsgType t, const char *str)
 {
-    qWinMsgHandler(t, str);
+    QMessageLogContext emptyContext;
+    qWinMessageHandler(t, emptyContext, str);
 }
 
 /*****************************************************************************
index a6c638f..d4293a8 100644 (file)
@@ -6,4 +6,4 @@ SUBDIRS=\
     qglobal \
     qnumeric \
     qrand \
-    qmessagehandler
+    qlogging
diff --git a/tests/auto/corelib/global/qlogging/app/app.pro b/tests/auto/corelib/global/qlogging/app/app.pro
new file mode 100644 (file)
index 0000000..a167cc4
--- /dev/null
@@ -0,0 +1,9 @@
+TEMPLATE = app
+
+TARGET = app
+QT = core
+
+CONFIG -= debug_and_release app_bundle
+CONFIG += debug console
+
+SOURCES += main.cpp
diff --git a/tests/auto/corelib/global/qlogging/app/main.cpp b/tests/auto/corelib/global/qlogging/app/main.cpp
new file mode 100644 (file)
index 0000000..c26b29e
--- /dev/null
@@ -0,0 +1,55 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qglobal.h>
+
+struct T {
+    T() { qDebug("static constructor"); }
+    ~T() { qDebug("static destructor"); }
+} t;
+
+int main(int, char **)
+{
+    qDebug("qDebug");
+    qWarning("qWarning");
+    qCritical("qCritical");
+    return 0;
+}
diff --git a/tests/auto/corelib/global/qlogging/qlogging.pro b/tests/auto/corelib/global/qlogging/qlogging.pro
new file mode 100644 (file)
index 0000000..449b7df
--- /dev/null
@@ -0,0 +1,5 @@
+TEMPLATE = subdirs
+
+SUBDIRS += \
+    tst_qlogging.pro \
+    app
\ No newline at end of file
diff --git a/tests/auto/corelib/global/qlogging/tst_qlogging.cpp b/tests/auto/corelib/global/qlogging/tst_qlogging.cpp
new file mode 100644 (file)
index 0000000..742a858
--- /dev/null
@@ -0,0 +1,645 @@
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qdebug.h>
+#include <qglobal.h>
+#include <QtCore/QProcess>
+#include <QtTest/QtTest>
+
+class tst_qmessagehandler : public QObject
+{
+    Q_OBJECT
+private slots:
+    void cleanup();
+
+    void defaultHandler();
+    void installMessageHandler();
+    void installMsgHandler();
+    void installBothHandler();
+
+    void cleanupFuncinfo_data();
+    void cleanupFuncinfo();
+
+    void qMessagePattern();
+};
+
+static QtMsgType s_type;
+const char *s_file;
+int s_line;
+const char *s_function;
+static QString s_message;
+
+void customMessageHandler(QtMsgType type, const QMessageLogContext &context, const char *msg)
+{
+    s_type = type;
+    s_file = context.file;
+    s_line = context.line;
+    s_function = context.function;
+    s_message = QString::fromLocal8Bit(msg);
+}
+
+void customMsgHandler(QtMsgType type, const char *msg)
+{
+    s_type = type;
+    s_file = 0;
+    s_line = 0;
+    s_function = 0;
+    s_message = QString::fromLocal8Bit(msg);
+}
+
+void tst_qmessagehandler::cleanup()
+{
+    qInstallMsgHandler(0);
+    qInstallMessageHandler(0);
+    s_type = QtFatalMsg;
+    s_file = 0;
+    s_line = 0;
+    s_function = 0;
+}
+
+void tst_qmessagehandler::defaultHandler()
+{
+    // check that the default works
+    QTest::ignoreMessage(QtDebugMsg, "defaultHandler");
+    qDebug("defaultHandler");
+}
+
+void tst_qmessagehandler::installMessageHandler()
+{
+    QMessageHandler oldHandler = qInstallMessageHandler(customMessageHandler);
+
+    qDebug("installMessageHandler"); int line = __LINE__;
+
+    QCOMPARE(s_type, QtDebugMsg);
+    QCOMPARE(s_message, QString::fromLocal8Bit("installMessageHandler"));
+    QCOMPARE(s_file, __FILE__);
+    QCOMPARE(s_function, Q_FUNC_INFO);
+    QCOMPARE(s_line, line);
+
+    QMessageHandler myHandler = qInstallMessageHandler(oldHandler);
+    QCOMPARE((void*)myHandler, (void*)customMessageHandler);
+}
+
+void tst_qmessagehandler::installMsgHandler()
+{
+    QtMsgHandler oldHandler = qInstallMsgHandler(customMsgHandler);
+
+    qDebug("installMsgHandler");
+
+    QCOMPARE(s_type, QtDebugMsg);
+    QCOMPARE(s_message, QString::fromLocal8Bit("installMsgHandler"));
+    QCOMPARE(s_file, (const char*)0);
+    QCOMPARE(s_function, (const char*)0);
+    QCOMPARE(s_line, 0);
+
+    QtMsgHandler myHandler = qInstallMsgHandler(oldHandler);
+    QCOMPARE((void*)myHandler, (void*)customMsgHandler);
+}
+
+void tst_qmessagehandler::installBothHandler()
+{
+    qInstallMessageHandler(customMessageHandler);
+    qInstallMsgHandler(customMsgHandler);
+
+    qDebug("installBothHandler"); int line = __LINE__;
+
+    QCOMPARE(s_type, QtDebugMsg);
+    QCOMPARE(s_message, QString::fromLocal8Bit("installBothHandler"));
+    QCOMPARE(s_file, __FILE__);
+    QCOMPARE(s_function, Q_FUNC_INFO);
+    QCOMPARE(s_line, line);
+}
+
+# define ADD(x)          QTest::newRow(x) << Q_FUNC_INFO << x;
+
+class TestClass1
+{
+public:
+    enum Something { foo };
+
+    void func_void() { ADD("TestClass1::func_void"); }
+    int func_int() { ADD("TestClass1::func_int"); return 0; }
+    unsigned func_unsigned() { ADD("TestClass1::func_unsigned"); return 0; }
+    long func_long() { ADD("TestClass1::func_long"); return 0; }
+    long long func_ll() { ADD("TestClass1::func_ll"); return 0; }
+    unsigned long long func_ull() { ADD("TestClass1::func_ull"); return 0; }
+    char func_char() { ADD("TestClass1::func_char"); return 0; }
+    signed char func_schar() { ADD("TestClass1::func_schar"); return 0; }
+    unsigned char func_uchar() { ADD("TestClass1::func_uchar"); return 0; }
+    char *func_Pchar() { ADD("TestClass1::func_Pchar"); return 0; }
+    const char *func_KPchar() { ADD("TestClass1::func_KPchar"); return 0; }
+    const volatile char *func_VKPchar() { ADD("TestClass1::func_VKPchar"); return 0; }
+    const volatile unsigned long long * const volatile func_KVPKVull() { ADD("TestClass1::func_KVPKVull"); return 0; }
+    const void * const volatile *func_KPKVvoid() { ADD("TestClass1::func_KPKVvoid"); return 0; }
+
+    QList<int> func_ai() { ADD("TestClass1::func_ai"); return QList<int>(); }
+    QList<unsigned long long const volatile*> func_aptr() { ADD("TestClass1::func_aptr"); return QList<unsigned long long const volatile*>(); }
+
+    QList<Something> func_aenum() { ADD("TestClass1::func_aenum"); return QList<Something>(); }
+    QList<QList<const void *> > func_aaptr() { ADD("TestClass1::func_aaptr"); return QList<QList<const void *> >(); }
+
+    QMap<int, Something> func_ienummap() { ADD("TestClass1::func_ienummap"); return QMap<int, Something>(); }
+
+    template<typename T>
+    T* func_template1() { ADD("TestClass1::func_template1"); return 0; }
+    template<Something val>
+    long func_template2() { ADD("TestClass1::func_template2"); return long(val); }
+
+    typedef unsigned long long * ( *fptr)();
+    typedef unsigned long long * (TestClass1::* pmf)();
+    typedef fptr (TestClass1::* uglypmf)();
+    fptr func_fptr() { ADD("TestClass1::func_fptr"); return 0; }
+    pmf func_pmf() { ADD("TestClass1::func_pmf"); return 0; }
+    uglypmf func_uglypmf(uglypmf = 0) { ADD("TestClass1::func_uglypmf"); return 0; }
+    QMap<QString, uglypmf> func_uglypmf2() { ADD("TestClass1::func_uglypmf2"); return QMap<QString, uglypmf>(); }
+
+    void operator()() { ADD("TestClass1::operator()"); }
+    int operator<(int) { ADD("TestClass1::operator<"); return 0; }
+    int operator>(int) { ADD("TestClass1::operator>"); return 0; }
+    int operator<=(int) { ADD("TestClass1::operator<="); return 0; }
+    int operator>=(int) { ADD("TestClass1::operator>="); return 0; }
+    int operator=(int) { ADD("TestClass1::operator="); return 0; }
+    int operator+(int) { ADD("TestClass1::operator+"); return 0; }
+    int operator-(int) { ADD("TestClass1::operator-"); return 0; }
+    int operator*(int) { ADD("TestClass1::operator*"); return 0; }
+    int operator/(int) { ADD("TestClass1::operator/"); return 0; }
+    int operator%(int) { ADD("TestClass1::operator%"); return 0; }
+    int x;
+    int &operator++() { ADD("TestClass1::operator++"); return x; }
+    int operator++(int) { ADD("TestClass1::operator++"); return 0; }
+    int &operator--() { ADD("TestClass1::operator--"); return x; }
+    int operator--(int) { ADD("TestClass1::operator--"); return 0; }
+
+public:
+    TestClass1()
+        {
+            // instantiate
+            func_void();
+            func_int();
+            func_unsigned();
+            func_long();
+            func_ll();
+            func_ull();
+            func_char();
+            func_schar();
+            func_uchar();
+            func_Pchar();
+            func_KPchar();
+            func_VKPchar();
+            func_KVPKVull();
+            func_KPKVvoid();
+            func_ai();
+            func_aptr();
+            func_aenum();
+            func_aaptr();
+            func_ienummap();
+            func_template1<TestClass1>();
+            func_template2<foo>();
+            func_fptr();
+            func_pmf();
+            func_uglypmf();
+            func_uglypmf2();
+            operator()();
+            operator<(0);
+            operator>(0);
+            operator<=(0);
+            operator>=(0);
+            operator=(0);
+            operator+(0);
+            operator-(0);
+            operator*(0);
+            operator/(0);
+            operator%(0);
+            operator++();
+            operator++(0);
+            operator--();
+            operator--(0);
+        }
+};
+
+template<typename T> class TestClass2
+{
+    long func_long() { ADD("TestClass2::func_long"); return 0; }
+    template<typename S>
+    T* func_template1() { ADD("TestClass2::func_template1"); return 0; }
+    template<TestClass1::Something val>
+    long func_template2() { ADD("TestClass2::func_template2"); return long(val); }
+public:
+    TestClass2()
+        {
+            func_long();
+            func_template1<TestClass2>();
+            func_template2<TestClass1::foo>();
+        }
+};
+
+template<typename T, TestClass1::Something v> class TestClass3
+{
+    long func_long() { ADD("TestClass3::func_long"); return 0; }
+    template<typename S>
+    S* func_template1() { ADD("TestClass3::func_template1"); return 0; }
+    template<TestClass1::Something val>
+    long func_template2() { ADD("TestClass3::func_template2"); return long(val); }
+public:
+    struct Foo { TestClass3 foo; };
+    TestClass3()
+        {
+            func_long();
+            func_template1<TestClass2<T> >();
+            func_template2<TestClass1::foo>();
+        }
+};
+
+class TestClass4
+{
+    TestClass1 c1;
+
+    TestClass2<std::map<long, const void *> > func2()
+        { ADD("TestClass4::func2"); return TestClass2<std::map<long, const void *> >(); }
+    TestClass3<std::map<std::list<int>, const void *>, TestClass1::foo>::Foo func3()
+        { ADD("TestClass4::func3"); return TestClass3<std::map<std::list<int>, const void *>, TestClass1::foo>::Foo(); }
+public:
+    TestClass4()
+        {
+            func2();
+            func3();
+            ADD("TestClass4::TestClass4");
+        }
+    ~TestClass4()
+        {
+            ADD("TestClass4::~TestClass4");
+        }
+};
+
+
+void tst_qmessagehandler::cleanupFuncinfo_data()
+{
+#ifndef QT_BUILD_INTERNAL
+    QSKIP("Requires -developer-build");
+#endif
+    QTest::addColumn<QString>("funcinfo");
+    QTest::addColumn<QString>("expected");
+
+    TestClass4 c4;
+
+    QTest::newRow("msvc_01")
+        << "void __thiscall TestClass1::func_void(void)"
+        << "TestClass1::func_void";
+    QTest::newRow("gcc_01")
+        << "void TestClass1::func_void()"
+        << "TestClass1::func_void";
+
+    QTest::newRow("msvc_02")
+        << "int __thiscall TestClass1::func_int(void)"
+        << "TestClass1::func_int";
+    QTest::newRow("gcc_02")
+        << "int TestClass1::func_int()"
+        << "TestClass1::func_int";
+
+    QTest::newRow("msvc_03")
+        << "unsigned int __thiscall TestClass1::func_unsigned(void)"
+        << "TestClass1::func_unsigned";
+    QTest::newRow("gcc_03")
+        << "unsigned int TestClass1::func_unsigned()"
+        << "TestClass1::func_unsigned";
+
+    QTest::newRow("msvc_04")
+        << "long __thiscall TestClass1::func_long(void)"
+        << "TestClass1::func_long";
+    QTest::newRow("gcc_04")
+        << "long int TestClass1::func_long()"
+        << "TestClass1::func_long";
+
+    QTest::newRow("msvc_05")
+        << "__int64 __thiscall TestClass1::func_ll(void)"
+        << "TestClass1::func_ll";
+    QTest::newRow("gcc_05")
+        << "long long int TestClass1::func_ll()"
+        << "TestClass1::func_ll";
+
+    QTest::newRow("msvc_06")
+        << "unsigned __int64 __thiscall TestClass1::func_ull(void)"
+        << "TestClass1::func_ull";
+    QTest::newRow("gcc_06")
+        << "long long unsigned int TestClass1::func_ull()"
+        << "TestClass1::func_ull";
+
+    QTest::newRow("msvc_07")
+        << "char __thiscall TestClass1::func_char(void)"
+        << "TestClass1::func_char";
+    QTest::newRow("gcc_07")
+        << "char TestClass1::func_char()"
+        << "TestClass1::func_char";
+
+    QTest::newRow("msvc_08")
+        << "signed char __thiscall TestClass1::func_schar(void)"
+        << "TestClass1::func_schar";
+    QTest::newRow("gcc_08")
+        << "signed char TestClass1::func_schar()"
+        << "TestClass1::func_schar";
+
+    QTest::newRow("msvc_09")
+        << "unsigned char __thiscall TestClass1::func_uchar(void)"
+        << "TestClass1::func_uchar";
+    QTest::newRow("gcc_09")
+        << "unsigned char TestClass1::func_uchar()"
+        << "TestClass1::func_uchar";
+
+    QTest::newRow("msvc_10")
+        << "char *__thiscall TestClass1::func_Pchar(void)"
+        << "TestClass1::func_Pchar";
+    QTest::newRow("gcc_10")
+        << "char* TestClass1::func_Pchar()"
+        << "TestClass1::func_Pchar";
+
+    QTest::newRow("msvc_11")
+        << "const char *__thiscall TestClass1::func_KPchar(void)"
+        << "TestClass1::func_KPchar";
+    QTest::newRow("gcc_11")
+        << "const char* TestClass1::func_KPchar()"
+        << "TestClass1::func_KPchar";
+
+    QTest::newRow("msvc_12")
+        << "volatile const char *__thiscall TestClass1::func_VKPchar(void)"
+        << "TestClass1::func_VKPchar";
+    QTest::newRow("gcc_12")
+        << "const volatile char* TestClass1::func_VKPchar()"
+        << "TestClass1::func_VKPchar";
+
+    QTest::newRow("msvc_13")
+        << "volatile const unsigned __int64 *volatile const __thiscall TestClass1::func_KVPKVull(void)"
+        << "TestClass1::func_KVPKVull";
+    QTest::newRow("gcc_13")
+        << "const volatile long long unsigned int* const volatile TestClass1::func_KVPKVull()"
+        << "TestClass1::func_KVPKVull";
+
+    QTest::newRow("msvc_14")
+        << "const void *volatile const *__thiscall TestClass1::func_KPKVvoid(void)"
+        << "TestClass1::func_KPKVvoid";
+    QTest::newRow("gcc_14")
+        << "const void* const volatile* TestClass1::func_KPKVvoid()"
+        << "TestClass1::func_KPKVvoid";
+
+    QTest::newRow("msvc_15")
+        << "class QList<int> __thiscall TestClass1::func_ai(void)"
+        << "TestClass1::func_ai";
+    QTest::newRow("gcc_15")
+        << "QList<int> TestClass1::func_ai()"
+        << "TestClass1::func_ai";
+
+    QTest::newRow("msvc_16")
+        << "class QList<unsigned __int64 const volatile *> __thiscall TestClass1::func_aptr(void)"
+        << "TestClass1::func_aptr";
+    QTest::newRow("gcc_16")
+        << "QList<const volatile long long unsigned int*> TestClass1::func_aptr()"
+        << "TestClass1::func_aptr";
+
+    QTest::newRow("msvc_17")
+        << "class QList<enum TestClass1::Something> __thiscall TestClass1::func_aenum(void)"
+        << "TestClass1::func_aenum";
+    QTest::newRow("gcc_17")
+        << "QList<TestClass1::Something> TestClass1::func_aenum()"
+        << "TestClass1::func_aenum";
+
+    QTest::newRow("msvc_18")
+        << "class QList<class QList<void const *> > __thiscall TestClass1::func_aaptr(void)"
+        << "TestClass1::func_aaptr";
+    QTest::newRow("gcc_18")
+        << "QList<QList<const void*> > TestClass1::func_aaptr()"
+        << "TestClass1::func_aaptr";
+
+    QTest::newRow("msvc_19")
+        << "class QMap<int,enum TestClass1::Something> __thiscall TestClass1::func_ienummap(void)"
+        << "TestClass1::func_ienummap";
+    QTest::newRow("gcc_19")
+        << "QMap<int, TestClass1::Something> TestClass1::func_ienummap()"
+        << "TestClass1::func_ienummap";
+
+    QTest::newRow("msvc_20")
+        << "class TestClass1 *__thiscall TestClass1::func_template1<class TestClass1>(void)"
+        << "TestClass1::func_template1";
+    QTest::newRow("gcc_20")
+        << "T* TestClass1::func_template1() [with T = TestClass1]"
+        << "TestClass1::func_template1";
+
+    QTest::newRow("msvc_21")
+        << "long __thiscall TestClass1::func_template2<foo>(void)"
+        << "TestClass1::func_template2";
+    QTest::newRow("gcc_21")
+        << "long int TestClass1::func_template2() [with TestClass1::Something val = foo]"
+        << "TestClass1::func_template2";
+
+    QTest::newRow("msvc_22")
+        << "unsigned __int64 *(__cdecl *__thiscall TestClass1::func_fptr(void))(void)"
+        << "TestClass1::func_fptr";
+    QTest::newRow("gcc_22")
+        << "long long unsigned int* (* TestClass1::func_fptr())()"
+        << "TestClass1::func_fptr";
+
+    QTest::newRow("msvc_23")
+        << "unsigned __int64 *(__thiscall TestClass1::* __thiscall TestClass1::func_pmf(void))(void)"
+        << "TestClass1::func_pmf";
+    QTest::newRow("gcc_23")
+        << "long long unsigned int* (TestClass1::* TestClass1::func_pmf())()"
+        << "TestClass1::func_pmf";
+
+    QTest::newRow("msvc_24")
+        << "unsigned __int64 *(__cdecl *(__thiscall TestClass1::* __thiscall TestClass1::func_uglypmf(unsigned __int64 *(__cdecl *(__thiscall TestClass1::* )(void))(void)))(void))(void)"
+        << "TestClass1::func_uglypmf";
+    QTest::newRow("gcc_24")
+        << "long long unsigned int* (* (TestClass1::* TestClass1::func_uglypmf(long long unsigned int* (* (TestClass1::*)())()))())()"
+        << "TestClass1::func_uglypmf";
+
+    QTest::newRow("msvc_25")
+        << "class QMap<class QString,unsigned __int64 * (__cdecl*(__thiscall TestClass1::*)(void))(void)> __thiscall TestClass1::func_uglypmf2(void)"
+        << "TestClass1::func_uglypmf2";
+    QTest::newRow("gcc_25")
+        << "QMap<QString, long long unsigned int* (* (TestClass1::*)())()> TestClass1::func_uglypmf2()"
+        << "TestClass1::func_uglypmf2";
+
+    QTest::newRow("msvc_26")
+        << "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)"
+        << "TestClass4::func2";
+    QTest::newRow("gcc_26")
+        << "TestClass2<std::map<long int, const void*, std::less<long int>, std::allocator<std::pair<const long int, const void*> > > > TestClass4::func2()"
+        << "TestClass4::func2";
+
+    QTest::newRow("msvc_27")
+        << "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)"
+        << "TestClass2::func_long";
+    QTest::newRow("gcc_27")
+        << "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*> > >]"
+        << "TestClass2::func_long";
+
+    QTest::newRow("msvc_28")
+        << "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)"
+        << "TestClass2::func_template1";
+    QTest::newRow("gcc_21")
+        << "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*> > >]"
+        << "TestClass2::func_template1";
+
+    QTest::newRow("msvc_29")
+        << "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)"
+        << "TestClass2::func_template2";
+    QTest::newRow("gcc_29")
+        << "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*> > >]"
+        << "TestClass2::func_template2";
+
+    QTest::newRow("msvc_30")
+        << "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)"
+        << "TestClass4::func3";
+    QTest::newRow("gcc_30")
+        << "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()"
+        << "TestClass4::func3";
+
+    QTest::newRow("msvc_31")
+        << "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)"
+        << "TestClass3::func_long";
+    QTest::newRow("gcc_31")
+        << "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]"
+        << "TestClass3::func_long";
+
+    QTest::newRow("msvc_32")
+        << "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)"
+        << "TestClass3::func_template1";
+    QTest::newRow("gcc_32")
+        << "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]"
+        << "TestClass3::func_template1";
+
+    QTest::newRow("msvc_33")
+        << "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)"
+        << "TestClass3::func_template2";
+    QTest::newRow("gcc_33")
+        << "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]"
+        << "TestClass3::func_template2";
+
+    QTest::newRow("msvc_34")
+        << "__thiscall TestClass4::TestClass4(void)"
+        << "TestClass4::TestClass4";
+    QTest::newRow("gcc_34")
+        << "TestClass4::TestClass4()"
+        << "TestClass4::TestClass4";
+
+    QTest::newRow("msvc_35")
+        << "__thiscall TestClass4::~TestClass4(void)"
+        << "TestClass4::~TestClass4";
+    QTest::newRow("gcc_35")
+        << "TestClass4::~TestClass4()"
+        << "TestClass4::~TestClass4";
+
+    QTest::newRow("gcc_36")
+        << "void TestClass1::operator()()"
+        << "TestClass1::operator()";
+
+    QTest::newRow("gcc_37")
+        << "long int TestClass1::func_template2() [with TestClass1::Something val = (TestClass1::Something)0u]"
+        << "TestClass1::func_template2";
+
+    QTest::newRow("gcc_38")
+        << "int TestClass1::operator<(int)"
+        << "TestClass1::operator<";
+
+    QTest::newRow("gcc_39")
+        << "int TestClass1::operator>(int)"
+        << "TestClass1::operator>";}
+
+#ifdef QT_BUILD_INTERNAL
+QT_BEGIN_NAMESPACE
+extern QByteArray qCleanupFuncinfo(QByteArray);
+QT_END_NAMESPACE
+#endif
+
+void tst_qmessagehandler::cleanupFuncinfo()
+{
+#ifdef QT_BUILD_INTERNAL
+    QFETCH(QString, funcinfo);
+
+    QByteArray result = qCleanupFuncinfo(funcinfo.toLatin1());
+    QTEST(QString::fromLatin1(result), "expected");
+#endif
+}
+
+void tst_qmessagehandler::qMessagePattern()
+{
+    QProcess process;
+
+    QStringList environment = QProcess::systemEnvironment();
+    // %{file} is tricky because of shadow builds
+    environment.prepend("QT_MESSAGE_PATTERN=\"%{type} %{line} %{function} %{message}\"");
+    process.setEnvironment(environment);
+#ifdef Q_OS_WIN
+    process.start("app/app.exe");
+#else
+    process.start("app/app");
+#endif
+    process.waitForFinished();
+
+    QByteArray output = process.readAllStandardError();
+//    qDebug() << output;
+    QVERIFY(!output.isEmpty());
+
+    QVERIFY(output.contains("debug 45 T::T static constructor"));
+    //  we can't be sure whether the QT_MESSAGE_PATTERN is already destructed
+    QVERIFY(output.contains("static destructor"));
+    QVERIFY(output.contains("debug 51 main qDebug"));
+    QVERIFY(output.contains("warning 52 main qWarning"));
+    QVERIFY(output.contains("critical 53 main qCritical"));
+
+    environment = QProcess::systemEnvironment();
+    environment.prepend("QT_MESSAGE_PATTERN=\"PREFIX: %{unknown} %{message}\"");
+    process.setEnvironment(environment);
+#ifdef Q_OS_WIN
+    process.start("app/app.exe");
+#else
+    process.start("app/app");
+#endif
+    process.waitForFinished();
+
+    output = process.readAllStandardError();
+//     qDebug() << output;
+    QVERIFY(!output.isEmpty());
+
+    QVERIFY(output.contains("QT_MESSAGE_PATTERN: Unknown placeholder %{unknown}"));
+    QVERIFY(output.contains("PREFIX:  qDebug"));
+}
+
+QTEST_MAIN(tst_qmessagehandler)
+#include "tst_qlogging.moc"
diff --git a/tests/auto/corelib/global/qlogging/tst_qlogging.pro b/tests/auto/corelib/global/qlogging/tst_qlogging.pro
new file mode 100644 (file)
index 0000000..60377e0
--- /dev/null
@@ -0,0 +1,4 @@
+CONFIG += testcase parallel_test
+TARGET = tst_qlogging
+QT = core testlib
+SOURCES = tst_qlogging.cpp
diff --git a/tests/auto/corelib/global/qmessagehandler/qmessagehandler.pro b/tests/auto/corelib/global/qmessagehandler/qmessagehandler.pro
deleted file mode 100644 (file)
index 8bdba4b..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-CONFIG += testcase parallel_test
-TARGET = tst_qmessagehandler
-QT = core testlib
-SOURCES = tst_qmessagehandler.cpp
diff --git a/tests/auto/corelib/global/qmessagehandler/tst_qmessagehandler.cpp b/tests/auto/corelib/global/qmessagehandler/tst_qmessagehandler.cpp
deleted file mode 100644 (file)
index 39bd098..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** GNU Lesser General Public License Usage
-** This file may be used under the terms of the GNU Lesser General Public
-** License version 2.1 as published by the Free Software Foundation and
-** appearing in the file LICENSE.LGPL included in the packaging of this
-** file. Please review the following information to ensure the GNU Lesser
-** General Public License version 2.1 requirements will be met:
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Nokia gives you certain additional
-** rights. These rights are described in the Nokia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU General
-** Public License version 3.0 as published by the Free Software Foundation
-** and appearing in the file LICENSE.GPL included in the packaging of this
-** file. Please review the following information to ensure the GNU General
-** Public License version 3.0 requirements will be met:
-** http://www.gnu.org/copyleft/gpl.html.
-**
-** Other Usage
-** Alternatively, this file may be used in accordance with the terms and
-** conditions contained in a signed written agreement between you and Nokia.
-**
-**
-**
-**
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <qdebug.h>
-#include <QtTest/QtTest>
-
-#include <qglobal.h>
-
-class tst_qmessagehandler : public QObject
-{
-    Q_OBJECT
-private slots:
-    void cleanup();
-
-    void defaultHandler();
-    void installMessageHandler();
-    void installMsgHandler();
-    void installBothHandler();
-};
-
-static QtMsgType s_type;
-const char *s_file;
-int s_line;
-const char *s_function;
-static QString s_message;
-
-void customMessageHandler(QtMsgType type, const QMessageLogContext &context, const char *msg)
-{
-    s_type = type;
-    s_file = context.file;
-    s_line = context.line;
-    s_function = context.function;
-    s_message = QString::fromLocal8Bit(msg);
-}
-
-void customMsgHandler(QtMsgType type, const char *msg)
-{
-    s_type = type;
-    s_file = 0;
-    s_line = 0;
-    s_function = 0;
-    s_message = QString::fromLocal8Bit(msg);
-}
-
-void tst_qmessagehandler::cleanup()
-{
-    qInstallMsgHandler(0);
-    qInstallMessageHandler(0);
-    s_type = QtFatalMsg;
-    s_file = 0;
-    s_line = 0;
-    s_function = 0;
-}
-
-void tst_qmessagehandler::defaultHandler()
-{
-    // check that the default works
-    QTest::ignoreMessage(QtDebugMsg, "defaultHandler");
-    qDebug("defaultHandler");
-}
-
-void tst_qmessagehandler::installMessageHandler()
-{
-    QMessageHandler oldHandler = qInstallMessageHandler(customMessageHandler);
-
-    qDebug("installMessageHandler"); int line = __LINE__;
-
-    QCOMPARE(s_type, QtDebugMsg);
-    QCOMPARE(s_message, QString::fromLocal8Bit("installMessageHandler"));
-    QCOMPARE(s_file, __FILE__);
-    QCOMPARE(s_function, Q_FUNC_INFO);
-    QCOMPARE(s_line, line);
-
-    QMessageHandler myHandler = qInstallMessageHandler(oldHandler);
-    QCOMPARE((void*)myHandler, (void*)customMessageHandler);
-}
-
-void tst_qmessagehandler::installMsgHandler()
-{
-    QtMsgHandler oldHandler = qInstallMsgHandler(customMsgHandler);
-
-    qDebug("installMsgHandler");
-
-    QCOMPARE(s_type, QtDebugMsg);
-    QCOMPARE(s_message, QString::fromLocal8Bit("installMsgHandler"));
-    QCOMPARE(s_file, (const char*)0);
-    QCOMPARE(s_function, (const char*)0);
-    QCOMPARE(s_line, 0);
-
-    QtMsgHandler myHandler = qInstallMsgHandler(oldHandler);
-    QCOMPARE((void*)myHandler, (void*)customMsgHandler);
-}
-
-void tst_qmessagehandler::installBothHandler()
-{
-    qInstallMessageHandler(customMessageHandler);
-    qInstallMsgHandler(customMsgHandler);
-
-    qDebug("installBothHandler"); int line = __LINE__;
-
-    QCOMPARE(s_type, QtDebugMsg);
-    QCOMPARE(s_message, QString::fromLocal8Bit("installBothHandler"));
-    QCOMPARE(s_file, __FILE__);
-    QCOMPARE(s_function, Q_FUNC_INFO);
-    QCOMPARE(s_line, line);
-}
-
-QTEST_MAIN(tst_qmessagehandler)
-#include "tst_qmessagehandler.moc"
index d39d9b4..f68e3c0 100644 (file)
@@ -46,6 +46,7 @@ HEADERS  = configureapp.h environment.h tools.h\
            $$QT_SOURCE_TREE/src/corelib/codecs/qtextcodec.h \
            $$QT_SOURCE_TREE/src/corelib/global/qglobal.h \
            $$QT_SOURCE_TREE/src/corelib/global/qnumeric.h \
+           $$QT_SOURCE_TREE/src/corelib/global/qlogging.h \
            $$QT_SOURCE_TREE/src/corelib/io/qbuffer.h \
            $$QT_SOURCE_TREE/src/corelib/io/qdatastream.h \
            $$QT_SOURCE_TREE/src/corelib/io/qdir.h \
@@ -89,6 +90,7 @@ SOURCES  = main.cpp configureapp.cpp environment.cpp tools.cpp \
            $$QT_SOURCE_TREE/src/corelib/codecs/qtextcodec.cpp \
            $$QT_SOURCE_TREE/src/corelib/global/qglobal.cpp \
            $$QT_SOURCE_TREE/src/corelib/global/qnumeric.cpp \
+           $$QT_SOURCE_TREE/src/corelib/global/qlogging.cpp \
            $$QT_SOURCE_TREE/src/corelib/io/qbuffer.cpp \
            $$QT_SOURCE_TREE/src/corelib/io/qdatastream.cpp \
            $$QT_SOURCE_TREE/src/corelib/io/qdir.cpp \