views:

186

answers:

4

My Qt application uses Q_ASSERT_X, which calls qFatal(), which (by default) aborts the application. That's great for the application, but I'd like to suppress that behavior when unit testing the application. (I'm using the Google Test Framework.) I have by unit tests in a separate project, statically linking to the class I'm testing. The documentation for qFatal() reads:

Calls the message handler with the fatal message msg. If no message handler has been installed, the message is printed to stderr. Under Windows, the message is sent to the debugger.

If you are using the default message handler this function will abort on Unix systems to create a core dump. On Windows, for debug builds, this function will report a _CRT_ERROR enabling you to connect a debugger to the application.

...

To supress the output at runtime, install your own message handler with qInstallMsgHandler().

So here's my main.cpp file:

#include <gtest/gtest.h>
#include <QApplication>

void testMessageOutput(QtMsgType type, const char *msg) {
    switch (type) {
    case QtDebugMsg:
        fprintf(stderr, "Debug: %s\n", msg);
        break;
    case QtWarningMsg:
        fprintf(stderr, "Warning: %s\n", msg);
        break;
    case QtCriticalMsg:
        fprintf(stderr, "Critical: %s\n", msg);
        break;
    case QtFatalMsg:
        fprintf(stderr, "My Fatal: %s\n", msg);
        break;
    }
}

int main(int argc, char **argv)
{
    qInstallMsgHandler(testMessageOutput);
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

But my application is still stopping at the assert. I can tell that my custom handler is being called, because the output when running my tests is:

My Fatal: ASSERT failure in MyClass::doSomething: "doSomething()", file myclass.cpp, line 21 The program has unexpectedly finished.

What can I do so that my tests keep running even when an assert fails?

+1  A: 

At least for Qt-4.6.2, there's nothing you can do.

src/corelib/global/qglobal.cpp defines void qt_message_output(QtMsgType msgType, const char *buf) which first checks to see if a a handler has been installed. If so it calls it, otherwise it uses the default handlers. Immediately after that, it almost always aborts (Unix/MingWn) or calls exit (others).

I couldn't find a browser to the current source code online, but the Qt-4.2.2 source code is mostly identical and should give you a general idea what's happening.

Kaleb Pederson
Qt is open source, it's all right here: http://qt.gitorious.org/qt
Adam W
Thanks, Kaleb. That stinks. Kind of draconian, but I guess I'll just use the preprocessor to exclude "expected failure" tests from debug builds, and only test them against release builds.
Dave
Thanks @Adam - I checked github but couldn't find it there and Google didn't list it anywhere reasonable in the search results.
Kaleb Pederson
+2  A: 

Q_ASSERT_X compiles to nothing when doing a release build.

So, for unit testing, do a release build and it will not call qFatal.

Adam W
Our posts crossed; that was exactly my conclusion. (See comment on Kaleb's answer.) Great minds really do think alike.... :)
Dave
A: 

-DqFatal=qCritical :)

Frank
A: 

In some cases you might cannot surpress qFatal and continue silently, tested component might be in such state that crash will occur anyhow after few lines. One way to avoid that could be to stub qFatal somewhere at your test code ;)

void qFatal(const char *msg, ...)
{
    QT_THROW(std::some_exception);
}

Then you could have some own assert macro:

#define CUSTOM_QEXPECT_FAIL( method ) { bool failed = false;\
    try { \
        method ; \
    }\
    catch(...) { \
        failed = true; \
    } \
    QVERIFY(failed); }

And use it in your code like:

CUSTOM_QEXPECT_FAIL( testedObj->panicAtTheDisco() );

Not saying that this is in any way nice approach, just trying to prove that something can be done for the issue.

Addition, I didn't read carefully enough that you are statically linking against tested class. Stubbing does not probably work in that case, only if you would build it into your test executable.