diff options
Diffstat (limited to 'src/testlib')
-rw-r--r-- | src/testlib/.prev_CMakeLists.txt | 5 | ||||
-rw-r--r-- | src/testlib/CMakeLists.txt | 5 | ||||
-rw-r--r-- | src/testlib/doc/src/qttestlib-manual.qdoc | 8 | ||||
-rw-r--r-- | src/testlib/qjunittestlogger.cpp (renamed from src/testlib/qxunittestlogger.cpp) | 30 | ||||
-rw-r--r-- | src/testlib/qjunittestlogger_p.h (renamed from src/testlib/qxunittestlogger_p.h) | 16 | ||||
-rw-r--r-- | src/testlib/qtestcase.cpp | 441 | ||||
-rw-r--r-- | src/testlib/qtestjunitstreamer.cpp (renamed from src/testlib/qtestxunitstreamer.cpp) | 26 | ||||
-rw-r--r-- | src/testlib/qtestjunitstreamer_p.h (renamed from src/testlib/qtestxunitstreamer_p.h) | 15 | ||||
-rw-r--r-- | src/testlib/qtestlog.cpp | 8 | ||||
-rw-r--r-- | src/testlib/qtestlog_p.h | 6 | ||||
-rw-r--r-- | src/testlib/qtestutil_macos.mm | 28 | ||||
-rw-r--r-- | src/testlib/qtestutil_macos_p.h | 1 | ||||
-rw-r--r-- | src/testlib/testlib.pro | 10 |
13 files changed, 306 insertions, 293 deletions
diff --git a/src/testlib/.prev_CMakeLists.txt b/src/testlib/.prev_CMakeLists.txt index 29c73d597e..8279b0719b 100644 --- a/src/testlib/.prev_CMakeLists.txt +++ b/src/testlib/.prev_CMakeLists.txt @@ -17,6 +17,7 @@ qt_add_module(Test qbenchmarkperfevents.cpp qbenchmarkperfevents_p.h qbenchmarktimemeasurers_p.h qcsvbenchmarklogger.cpp qcsvbenchmarklogger_p.h + qjunittestlogger.cpp qjunittestlogger_p.h qplaintestlogger.cpp qplaintestlogger_p.h qsignaldumper.cpp qsignaldumper_p.h qsignalspy.h @@ -38,6 +39,7 @@ qt_add_module(Test qtestevent.h qtesteventloop.h qtesthelpers_p.h + qtestjunitstreamer.cpp qtestjunitstreamer_p.h qtestkeyboard.h qtestlog.cpp qtestlog_p.h qtestmouse.cpp qtestmouse.h @@ -46,10 +48,8 @@ qt_add_module(Test qtestsystem.h qtesttable.cpp qtesttable_p.h qtesttouch.h - qtestxunitstreamer.cpp qtestxunitstreamer_p.h qttestglobal.h qxmltestlogger.cpp qxmltestlogger_p.h - qxunittestlogger.cpp qxunittestlogger_p.h DEFINES QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII @@ -105,6 +105,7 @@ qt_extend_target(Test CONDITION APPLE_OSX SOURCES qtestutil_macos.mm qtestutil_macos_p.h PUBLIC_LIBRARIES + ${FWAppKit} ${FWApplicationServices} ${FWFoundation} ${FWIOKit} diff --git a/src/testlib/CMakeLists.txt b/src/testlib/CMakeLists.txt index 79138707e3..8e2a3caafa 100644 --- a/src/testlib/CMakeLists.txt +++ b/src/testlib/CMakeLists.txt @@ -17,6 +17,7 @@ qt_add_module(Test qbenchmarkperfevents.cpp qbenchmarkperfevents_p.h qbenchmarktimemeasurers_p.h qcsvbenchmarklogger.cpp qcsvbenchmarklogger_p.h + qjunittestlogger.cpp qjunittestlogger_p.h qplaintestlogger.cpp qplaintestlogger_p.h qsignaldumper.cpp qsignaldumper_p.h qsignalspy.h @@ -38,6 +39,7 @@ qt_add_module(Test qtestevent.h qtesteventloop.h qtesthelpers_p.h + qtestjunitstreamer.cpp qtestjunitstreamer_p.h qtestkeyboard.h qtestlog.cpp qtestlog_p.h qtestmouse.cpp qtestmouse.h @@ -46,10 +48,8 @@ qt_add_module(Test qtestsystem.h qtesttable.cpp qtesttable_p.h qtesttouch.h - qtestxunitstreamer.cpp qtestxunitstreamer_p.h qttestglobal.h qxmltestlogger.cpp qxmltestlogger_p.h - qxunittestlogger.cpp qxunittestlogger_p.h DEFINES QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII @@ -103,6 +103,7 @@ qt_extend_target(Test CONDITION APPLE_OSX SOURCES qtestutil_macos.mm qtestutil_macos_p.h PUBLIC_LIBRARIES + ${FWAppKit} ${FWApplicationServices} ${FWFoundation} ${FWIOKit} diff --git a/src/testlib/doc/src/qttestlib-manual.qdoc b/src/testlib/doc/src/qttestlib-manual.qdoc index 89edabf3f3..3acc08d8f4 100644 --- a/src/testlib/doc/src/qttestlib-manual.qdoc +++ b/src/testlib/doc/src/qttestlib-manual.qdoc @@ -237,7 +237,7 @@ \list \li \c -o \e{filename,format} \br Writes output to the specified file, in the specified format (one of - \c txt, \c xml, \c lightxml, \c xunitxml or \c tap). The special filename \c - + \c txt, \c xml, \c lightxml, \c junitxml or \c tap). The special filename \c - may be used to log to standard output. \li \c -o \e filename \br Writes output to the specified file. @@ -247,8 +247,8 @@ Outputs results as an XML document. \li \c -lightxml \br Outputs results as a stream of XML tags. - \li \c -xunitxml \br - Outputs results as an Xunit XML document. + \li \c -junitxml \br + Outputs results as an JUnit XML document. \li \c -csv \br Outputs results as comma-separated values (CSV). This mode is only suitable for benchmarks, since it suppresses normal pass/fail messages. @@ -264,7 +264,7 @@ If the first version of the \c -o option is used, neither the second version of the \c -o option nor the \c -txt, \c -xml, \c -lightxml, \c -teamcity, - \c -xunitxml or \c -tap options should be used. + \c -junitxml or \c -tap options should be used. If neither version of the \c -o option is used, test results will be logged to standard output. If no format option is used, test results will be logged in diff --git a/src/testlib/qxunittestlogger.cpp b/src/testlib/qjunittestlogger.cpp index b3cac9cb82..2d71f7967b 100644 --- a/src/testlib/qxunittestlogger.cpp +++ b/src/testlib/qjunittestlogger.cpp @@ -37,9 +37,9 @@ ** ****************************************************************************/ -#include <QtTest/private/qxunittestlogger_p.h> +#include <QtTest/private/qjunittestlogger_p.h> #include <QtTest/private/qtestelement_p.h> -#include <QtTest/private/qtestxunitstreamer_p.h> +#include <QtTest/private/qtestjunitstreamer_p.h> #include <QtTest/qtestcase.h> #include <QtTest/private/qtestresult_p.h> #include <QtTest/private/qbenchmark_p.h> @@ -57,27 +57,27 @@ QT_BEGIN_NAMESPACE -QXunitTestLogger::QXunitTestLogger(const char *filename) +QJUnitTestLogger::QJUnitTestLogger(const char *filename) : QAbstractTestLogger(filename) { } -QXunitTestLogger::~QXunitTestLogger() +QJUnitTestLogger::~QJUnitTestLogger() { delete currentLogElement; delete logFormatter; } -void QXunitTestLogger::startLogging() +void QJUnitTestLogger::startLogging() { QAbstractTestLogger::startLogging(); - logFormatter = new QTestXunitStreamer(this); + logFormatter = new QTestJUnitStreamer(this); delete errorLogElement; errorLogElement = new QTestElement(QTest::LET_SystemError); } -void QXunitTestLogger::stopLogging() +void QJUnitTestLogger::stopLogging() { QTestElement *iterator = listOfTestcases; @@ -132,7 +132,7 @@ void QXunitTestLogger::stopLogging() QAbstractTestLogger::stopLogging(); } -void QXunitTestLogger::enterTestFunction(const char *function) +void QJUnitTestLogger::enterTestFunction(const char *function) { currentLogElement = new QTestElement(QTest::LET_TestCase); currentLogElement->addAttribute(QTest::AI_Name, function); @@ -141,11 +141,11 @@ void QXunitTestLogger::enterTestFunction(const char *function) ++testCounter; } -void QXunitTestLogger::leaveTestFunction() +void QJUnitTestLogger::leaveTestFunction() { } -void QXunitTestLogger::addIncident(IncidentTypes type, const char *description, +void QJUnitTestLogger::addIncident(IncidentTypes type, const char *description, const char *file, int line) { const char *typeBuf = nullptr; @@ -242,15 +242,15 @@ void QXunitTestLogger::addIncident(IncidentTypes type, const char *description, currentLogElement->addAttribute(QTest::AI_Line, buf); /* - Since XFAIL does not add a failure to the testlog in xunitxml, add a message, so we still + Since XFAIL does not add a failure to the testlog in junitxml, add a message, so we still have some information about the expected failure. */ if (type == QAbstractTestLogger::XFail) { - QXunitTestLogger::addMessage(QAbstractTestLogger::Info, QString::fromUtf8(description), file, line); + QJUnitTestLogger::addMessage(QAbstractTestLogger::Info, QString::fromUtf8(description), file, line); } } -void QXunitTestLogger::addBenchmarkResult(const QBenchmarkResult &result) +void QJUnitTestLogger::addBenchmarkResult(const QBenchmarkResult &result) { QTestElement *benchmarkElement = new QTestElement(QTest::LET_Benchmark); @@ -268,7 +268,7 @@ void QXunitTestLogger::addBenchmarkResult(const QBenchmarkResult &result) currentLogElement->addLogElement(benchmarkElement); } -void QXunitTestLogger::addTag(QTestElement* element) +void QJUnitTestLogger::addTag(QTestElement* element) { const char *tag = QTestResult::currentDataTag(); const char *gtag = QTestResult::currentGlobalDataTag(); @@ -289,7 +289,7 @@ void QXunitTestLogger::addTag(QTestElement* element) element->addAttribute(QTest::AI_Tag, buf.constData()); } -void QXunitTestLogger::addMessage(MessageTypes type, const QString &message, const char *file, int line) +void QJUnitTestLogger::addMessage(MessageTypes type, const QString &message, const char *file, int line) { QTestElement *errorElement = new QTestElement(QTest::LET_Error); const char *typeBuf = nullptr; diff --git a/src/testlib/qxunittestlogger_p.h b/src/testlib/qjunittestlogger_p.h index 518ba098f4..6fd4e4c331 100644 --- a/src/testlib/qxunittestlogger_p.h +++ b/src/testlib/qjunittestlogger_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QXUNITTESTLOGGER_P_H -#define QXUNITTESTLOGGER_P_H +#ifndef QJUNITTESTLOGGER_P_H +#define QJUNITTESTLOGGER_P_H // // W A R N I N G @@ -55,14 +55,14 @@ QT_BEGIN_NAMESPACE -class QTestXunitStreamer; +class QTestJUnitStreamer; class QTestElement; -class QXunitTestLogger : public QAbstractTestLogger +class QJUnitTestLogger : public QAbstractTestLogger { public: - QXunitTestLogger(const char *filename); - ~QXunitTestLogger(); + QJUnitTestLogger(const char *filename); + ~QJUnitTestLogger(); void startLogging() override; void stopLogging() override; @@ -82,7 +82,7 @@ class QXunitTestLogger : public QAbstractTestLogger QTestElement *listOfTestcases = nullptr; QTestElement *currentLogElement = nullptr; QTestElement *errorLogElement = nullptr; - QTestXunitStreamer *logFormatter = nullptr; + QTestJUnitStreamer *logFormatter = nullptr; int testCounter = 0; int failureCounter = 0; @@ -91,4 +91,4 @@ class QXunitTestLogger : public QAbstractTestLogger QT_END_NAMESPACE -#endif // QXUNITTESTLOGGER_P_H +#endif // QJUNITTESTLOGGER_P_H diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index cb8061737d..fc6b22f062 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -154,10 +154,6 @@ static bool debuggerPresent() #elif defined(Q_OS_WIN) return IsDebuggerPresent(); #elif defined(Q_OS_MACOS) - auto equals = [](CFStringRef str1, CFStringRef str2) -> bool { - return CFStringCompare(str1, str2, kCFCompareCaseInsensitive) == kCFCompareEqualTo; - }; - // Check if there is an exception handler for the process: mach_msg_type_number_t portCount = 0; exception_mask_t masks[EXC_TYPES_COUNT]; @@ -174,25 +170,22 @@ static bool debuggerPresent() } } } - - // Ok, no debugger attached. So, let's see if CrashReporter will throw up a dialog. If so, we - // leave it to the OS to do the stack trace. - CFStringRef crashReporterType = static_cast<CFStringRef>( - CFPreferencesCopyAppValue(CFSTR("DialogType"), CFSTR("com.apple.CrashReporter"))); - if (crashReporterType == nullptr) - return true; - - const bool createsStackTrace = - !equals(crashReporterType, CFSTR("server")) && - !equals(crashReporterType, CFSTR("none")); - CFRelease(crashReporterType); - return createsStackTrace; + return false; #else // TODO return false; #endif } +static bool hasSystemCrashReporter() +{ +#if defined(Q_OS_MACOS) + return QTestPrivate::macCrashReporterWillShowDialog(); +#else + return false; +#endif +} + static void disableCoreDump() { bool ok = false; @@ -216,7 +209,7 @@ static void stackTrace() if (ok && disableStackDump == 1) return; - if (debuggerPresent()) + if (debuggerPresent() || hasSystemCrashReporter()) return; #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) @@ -290,14 +283,15 @@ namespace QTestPrivate namespace QTest { +extern Q_TESTLIB_EXPORT int lastMouseTimestamp; + class WatchDog; static QObject *currentTestObject = nullptr; static QString mainSourcePath; #if defined(Q_OS_MACOS) -bool macNeedsActivate = false; -IOPMAssertionID powerID; +static IOPMAssertionID macPowerSavingDisabled = 0; #endif class TestMethods { @@ -536,7 +530,7 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool " Valid formats are:\n" " txt : Plain text\n" " csv : CSV format (suitable for benchmarks)\n" - " xunitxml : XML XUnit document\n" + " junitxml : XML JUnit document\n" " xml : XML document\n" " lightxml : A stream of XML tags\n" " teamcity : TeamCity format\n" @@ -548,7 +542,7 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool " -o filename : Write the output into file\n" " -txt : Output results in Plain Text\n" " -csv : Output results in a CSV format (suitable for benchmarks)\n" - " -xunitxml : Output results as XML XUnit document\n" + " -junitxml : Output results as XML JUnit document\n" " -xml : Output results as XML document\n" " -lightxml : Output results as stream of XML tags\n" " -teamcity : Output results in TeamCity format\n" @@ -632,8 +626,8 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool logFormat = QTestLog::Plain; } else if (strcmp(argv[i], "-csv") == 0) { logFormat = QTestLog::CSV; - } else if (strcmp(argv[i], "-xunitxml") == 0) { - logFormat = QTestLog::XunitXML; + } else if (strcmp(argv[i], "-junitxml") == 0 || strcmp(argv[i], "-xunitxml") == 0) { + logFormat = QTestLog::JUnitXML; } else if (strcmp(argv[i], "-xml") == 0) { logFormat = QTestLog::XML; } else if (strcmp(argv[i], "-lightxml") == 0) { @@ -672,14 +666,14 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool logFormat = QTestLog::LightXML; else if (strcmp(format, "xml") == 0) logFormat = QTestLog::XML; - else if (strcmp(format, "xunitxml") == 0) - logFormat = QTestLog::XunitXML; + else if (strcmp(format, "junitxml") == 0 || strcmp(format, "xunitxml") == 0) + logFormat = QTestLog::JUnitXML; else if (strcmp(format, "teamcity") == 0) logFormat = QTestLog::TeamCity; else if (strcmp(format, "tap") == 0) logFormat = QTestLog::TAP; else { - fprintf(stderr, "output format must be one of txt, csv, lightxml, xml, tap, teamcity or xunitxml\n"); + fprintf(stderr, "output format must be one of txt, csv, lightxml, xml, tap, teamcity or junitxml\n"); exit(1); } if (strcmp(filename, "-") == 0 && QTestLog::loggerUsingStdout()) { @@ -1168,6 +1162,7 @@ bool TestMethods::invokeTest(int index, const char *data, WatchDog *watchDog) co QTestPrivate::qtestMouseButtons = Qt::NoButton; if (watchDog) watchDog->beginTest(); + QTest::lastMouseTimestamp += 500; // Maintain at least 500ms mouse event timestamps between each test function call invokeTestOnData(index); if (watchDog) watchDog->testFinished(); @@ -1332,7 +1327,7 @@ char *toPrettyCString(const char *p, int length) // 3 bytes: "" and a character // 4 bytes: an hex escape sequence (\xHH) if (dst - buffer.data() > 246) { - // plus the the quote, the three dots and NUL, it's 255 in the worst case + // plus the quote, the three dots and NUL, it's 255 in the worst case trimmed = true; break; } @@ -1425,7 +1420,7 @@ char *toPrettyUnicode(QStringView string) *dst++ = '"'; for ( ; p != end; ++p) { if (dst - buffer.data() > 245) { - // plus the the quote, the three dots and NUL, it's 250, 251 or 255 + // plus the quote, the three dots and NUL, it's 250, 251 or 255 trimmed = true; break; } @@ -1528,126 +1523,6 @@ void TestMethods::invokeTests(QObject *testObject) const QTestResult::setCurrentTestFunction(nullptr); } -#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) -class FatalSignalHandler -{ -public: - FatalSignalHandler(); - ~FatalSignalHandler(); - -private: - static void signal(int); - sigset_t handledSignals; -}; - -void FatalSignalHandler::signal(int signum) -{ - const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime()); - const int msecsTotalTime = qRound(QTestLog::msecsTotalTime()); - if (signum != SIGINT) { - stackTrace(); - if (qEnvironmentVariableIsSet("QTEST_PAUSE_ON_CRASH")) { - fprintf(stderr, "Pausing process %d for debugging\n", getpid()); - raise(SIGSTOP); - } - } - qFatal("Received signal %d\n" - " Function time: %dms Total time: %dms", - signum, msecsFunctionTime, msecsTotalTime); -#if defined(Q_OS_INTEGRITY) - { - struct sigaction act; - memset(&act, 0, sizeof(struct sigaction)); - act.sa_handler = SIG_DFL; - sigaction(signum, &act, NULL); - } -#endif -} - -FatalSignalHandler::FatalSignalHandler() -{ - sigemptyset(&handledSignals); - - const int fatalSignals[] = { - SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGPIPE, SIGTERM, 0 }; - - struct sigaction act; - memset(&act, 0, sizeof(act)); - act.sa_handler = FatalSignalHandler::signal; - - // Remove the handler after it is invoked. -#if !defined(Q_OS_INTEGRITY) - act.sa_flags = SA_RESETHAND; -#endif - -// tvOS/watchOS both define SA_ONSTACK (in sys/signal.h) but mark sigaltstack() as -// unavailable (__WATCHOS_PROHIBITED __TVOS_PROHIBITED in signal.h) -#if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS) - // Let the signal handlers use an alternate stack - // This is necessary if SIGSEGV is to catch a stack overflow -# if defined(Q_CC_GNU) && defined(Q_OF_ELF) - // Put the alternate stack in the .lbss (large BSS) section so that it doesn't - // interfere with normal .bss symbols - __attribute__((section(".lbss.altstack"), aligned(4096))) -# endif - static char alternate_stack[16 * 1024]; - stack_t stack; - stack.ss_flags = 0; - stack.ss_size = sizeof alternate_stack; - stack.ss_sp = alternate_stack; - sigaltstack(&stack, nullptr); - act.sa_flags |= SA_ONSTACK; -#endif - - // Block all fatal signals in our signal handler so we don't try to close - // the testlog twice. - sigemptyset(&act.sa_mask); - for (int i = 0; fatalSignals[i]; ++i) - sigaddset(&act.sa_mask, fatalSignals[i]); - - struct sigaction oldact; - - for (int i = 0; fatalSignals[i]; ++i) { - sigaction(fatalSignals[i], &act, &oldact); - if ( -#ifdef SA_SIGINFO - oldact.sa_flags & SA_SIGINFO || -#endif - oldact.sa_handler != SIG_DFL) { - sigaction(fatalSignals[i], &oldact, nullptr); - } else - { - sigaddset(&handledSignals, fatalSignals[i]); - } - } -} - - -FatalSignalHandler::~FatalSignalHandler() -{ - // Unregister any of our remaining signal handlers - struct sigaction act; - memset(&act, 0, sizeof(act)); - act.sa_handler = SIG_DFL; - - struct sigaction oldact; - - for (int i = 1; i < 32; ++i) { - if (!sigismember(&handledSignals, i)) - continue; - sigaction(i, &act, &oldact); - - // If someone overwrote it in the mean time, put it back - if (oldact.sa_handler != FatalSignalHandler::signal) - sigaction(i, &oldact, nullptr); - } -} - -#endif - - -} // namespace - #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) // Helper class for resolving symbol names by dynamically loading "dbghelp.dll". @@ -1746,49 +1621,177 @@ DebugSymbolResolver::Symbol DebugSymbolResolver::resolveSymbol(DWORD64 address) return result; } -static LONG WINAPI windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo) +#endif // Q_OS_WIN && !Q_OS_WINRT + +class FatalSignalHandler { - enum { maxStackFrames = 100 }; - char appName[MAX_PATH]; - if (!GetModuleFileNameA(NULL, appName, MAX_PATH)) - appName[0] = 0; - const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime()); - const int msecsTotalTime = qRound(QTestLog::msecsTotalTime()); - const void *exceptionAddress = exInfo->ExceptionRecord->ExceptionAddress; - printf("A crash occurred in %s.\n" - "Function time: %dms Total time: %dms\n\n" - "Exception address: 0x%p\n" - "Exception code : 0x%lx\n", - appName, msecsFunctionTime, msecsTotalTime, - exceptionAddress, exInfo->ExceptionRecord->ExceptionCode); - - DebugSymbolResolver resolver(GetCurrentProcess()); - if (resolver.isValid()) { - DebugSymbolResolver::Symbol exceptionSymbol = resolver.resolveSymbol(DWORD64(exceptionAddress)); - if (exceptionSymbol.name) { - printf("Nearby symbol : %s\n", exceptionSymbol.name); - delete [] exceptionSymbol.name; +public: + FatalSignalHandler() + { +#if defined(Q_OS_WIN) +# if !defined(Q_CC_MINGW) + _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); +# endif +# if !defined(Q_OS_WINRT) + SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX); + SetUnhandledExceptionFilter(windowsFaultHandler); +# endif +#elif defined(Q_OS_UNIX) && !defined(Q_OS_WASM) + sigemptyset(&handledSignals); + + const int fatalSignals[] = { + SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGPIPE, SIGTERM, 0 }; + + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = FatalSignalHandler::signal; + + // Remove the handler after it is invoked. +# if !defined(Q_OS_INTEGRITY) + act.sa_flags = SA_RESETHAND; +# endif + + // tvOS/watchOS both define SA_ONSTACK (in sys/signal.h) but mark sigaltstack() as + // unavailable (__WATCHOS_PROHIBITED __TVOS_PROHIBITED in signal.h) +# if defined(SA_ONSTACK) && !defined(Q_OS_TVOS) && !defined(Q_OS_WATCHOS) + // Let the signal handlers use an alternate stack + // This is necessary if SIGSEGV is to catch a stack overflow +# if defined(Q_CC_GNU) && defined(Q_OF_ELF) + // Put the alternate stack in the .lbss (large BSS) section so that it doesn't + // interfere with normal .bss symbols + __attribute__((section(".lbss.altstack"), aligned(4096))) +# endif + static char alternate_stack[16 * 1024]; + stack_t stack; + stack.ss_flags = 0; + stack.ss_size = sizeof alternate_stack; + stack.ss_sp = alternate_stack; + sigaltstack(&stack, nullptr); + act.sa_flags |= SA_ONSTACK; +# endif + + // Block all fatal signals in our signal handler so we don't try to close + // the testlog twice. + sigemptyset(&act.sa_mask); + for (int i = 0; fatalSignals[i]; ++i) + sigaddset(&act.sa_mask, fatalSignals[i]); + + struct sigaction oldact; + + for (int i = 0; fatalSignals[i]; ++i) { + sigaction(fatalSignals[i], &act, &oldact); + if ( +# ifdef SA_SIGINFO + oldact.sa_flags & SA_SIGINFO || +# endif + oldact.sa_handler != SIG_DFL) { + sigaction(fatalSignals[i], &oldact, nullptr); + } else + { + sigaddset(&handledSignals, fatalSignals[i]); + } } - void *stack[maxStackFrames]; - fputs("\nStack:\n", stdout); - const unsigned frameCount = CaptureStackBackTrace(0, DWORD(maxStackFrames), stack, NULL); - for (unsigned f = 0; f < frameCount; ++f) { - DebugSymbolResolver::Symbol symbol = resolver.resolveSymbol(DWORD64(stack[f])); - if (symbol.name) { - printf("#%3u: %s() - 0x%p\n", f + 1, symbol.name, (const void *)symbol.address); - delete [] symbol.name; - } else { - printf("#%3u: Unable to obtain symbol\n", f + 1); +#endif // defined(Q_OS_UNIX) && !defined(Q_OS_WASM) + } + + ~FatalSignalHandler() + { +#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) + // Unregister any of our remaining signal handlers + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_DFL; + + struct sigaction oldact; + + for (int i = 1; i < 32; ++i) { + if (!sigismember(&handledSignals, i)) + continue; + sigaction(i, &act, &oldact); + + // If someone overwrote it in the mean time, put it back + if (oldact.sa_handler != FatalSignalHandler::signal) + sigaction(i, &oldact, nullptr); + } +#endif + } + +private: +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + static LONG WINAPI windowsFaultHandler(struct _EXCEPTION_POINTERS *exInfo) + { + enum { maxStackFrames = 100 }; + char appName[MAX_PATH]; + if (!GetModuleFileNameA(NULL, appName, MAX_PATH)) + appName[0] = 0; + const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime()); + const int msecsTotalTime = qRound(QTestLog::msecsTotalTime()); + const void *exceptionAddress = exInfo->ExceptionRecord->ExceptionAddress; + printf("A crash occurred in %s.\n" + "Function time: %dms Total time: %dms\n\n" + "Exception address: 0x%p\n" + "Exception code : 0x%lx\n", + appName, msecsFunctionTime, msecsTotalTime, + exceptionAddress, exInfo->ExceptionRecord->ExceptionCode); + + DebugSymbolResolver resolver(GetCurrentProcess()); + if (resolver.isValid()) { + DebugSymbolResolver::Symbol exceptionSymbol = resolver.resolveSymbol(DWORD64(exceptionAddress)); + if (exceptionSymbol.name) { + printf("Nearby symbol : %s\n", exceptionSymbol.name); + delete [] exceptionSymbol.name; + } + void *stack[maxStackFrames]; + fputs("\nStack:\n", stdout); + const unsigned frameCount = CaptureStackBackTrace(0, DWORD(maxStackFrames), stack, NULL); + for (unsigned f = 0; f < frameCount; ++f) { + DebugSymbolResolver::Symbol symbol = resolver.resolveSymbol(DWORD64(stack[f])); + if (symbol.name) { + printf("#%3u: %s() - 0x%p\n", f + 1, symbol.name, (const void *)symbol.address); + delete [] symbol.name; + } else { + printf("#%3u: Unable to obtain symbol\n", f + 1); + } + } + } + + fputc('\n', stdout); + fflush(stdout); + + return EXCEPTION_EXECUTE_HANDLER; + } +#endif // defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + +#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) + static void signal(int signum) + { + const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime()); + const int msecsTotalTime = qRound(QTestLog::msecsTotalTime()); + if (signum != SIGINT) { + stackTrace(); + if (qEnvironmentVariableIsSet("QTEST_PAUSE_ON_CRASH")) { + fprintf(stderr, "Pausing process %d for debugging\n", getpid()); + raise(SIGSTOP); } } + qFatal("Received signal %d\n" + " Function time: %dms Total time: %dms", + signum, msecsFunctionTime, msecsTotalTime); +# if defined(Q_OS_INTEGRITY) + { + struct sigaction act; + memset(&act, 0, sizeof(struct sigaction)); + act.sa_handler = SIG_DFL; + sigaction(signum, &act, NULL); + } +# endif } - fputc('\n', stdout); - fflush(stdout); + sigset_t handledSignals; +#endif // defined(Q_OS_UNIX) && !defined(Q_OS_WASM) +}; - return EXCEPTION_EXECUTE_HANDLER; -} -#endif // Q_OS_WIN) && !Q_OS_WINRT +} // namespace static void initEnvironment() { @@ -1848,23 +1851,17 @@ void QTest::qInit(QObject *testObject, int argc, char **argv) initEnvironment(); QBenchmarkGlobalData::current = new QBenchmarkGlobalData; -#if defined(Q_OS_MACX) - macNeedsActivate = qApp && (qstrcmp(qApp->metaObject()->className(), "QApplication") == 0); - - // Don't restore saved window state for auto tests. +#if defined(Q_OS_MACOS) + // Don't restore saved window state for auto tests QTestPrivate::disableWindowRestore(); - // Disable App Nap which may cause tests to stall. + // Disable App Nap which may cause tests to stall QTestPrivate::AppNapDisabler appNapDisabler; -#endif -#if defined(Q_OS_MACX) - if (macNeedsActivate) { - CFStringRef reasonForActivity= CFSTR("No Display Sleep"); - IOReturn ok = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, reasonForActivity, &powerID); - - if (ok != kIOReturnSuccess) - macNeedsActivate = false; // no need to release the assertion on exit. + if (qApp && (qstrcmp(qApp->metaObject()->className(), "QApplication") == 0)) { + IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, + kIOPMAssertionLevelOn, CFSTR("QtTest running tests"), + &macPowerSavingDisabled); } #endif @@ -1902,18 +1899,6 @@ int QTest::qRun() try { #endif -#if defined(Q_OS_WIN) - if (!noCrashHandler) { -# ifndef Q_CC_MINGW - _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG); -# endif -# ifndef Q_OS_WINRT - SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX); - SetUnhandledExceptionFilter(windowsFaultHandler); -# endif - } // !noCrashHandler -#endif // Q_OS_WIN - #if QT_CONFIG(valgrind) if (QBenchmarkGlobalData::current->mode() == QBenchmarkGlobalData::CallgrindParentProcess) { if (Q_UNLIKELY(!qApp)) @@ -1928,11 +1913,10 @@ int QTest::qRun() } else #endif { -#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) QScopedPointer<FatalSignalHandler> handler; if (!noCrashHandler) handler.reset(new FatalSignalHandler); -#endif + TestMethods::MetaMethods commandLineMethods; for (const QString &tf : qAsConst(QTest::testFunctions)) { const QByteArray tfB = tf.toLatin1(); @@ -1951,25 +1935,19 @@ int QTest::qRun() } #ifndef QT_NO_EXCEPTIONS - } catch (...) { - QTestResult::addFailure("Caught unhandled exception", __FILE__, __LINE__); - if (QTestResult::currentTestFunction()) { - QTestResult::finishedCurrentTestFunction(); - QTestResult::setCurrentTestFunction(nullptr); - } - - QTestLog::stopLogging(); -#if defined(Q_OS_MACX) - if (macNeedsActivate) { - IOPMAssertionRelease(powerID); - } -#endif - currentTestObject = nullptr; + } catch (...) { + QTestResult::addFailure("Caught unhandled exception", __FILE__, __LINE__); + if (QTestResult::currentTestFunction()) { + QTestResult::finishedCurrentTestFunction(); + QTestResult::setCurrentTestFunction(nullptr); + } - // Rethrow exception to make debugging easier. - throw; - return 1; - } + qCleanup(); + + // Re-throw exception to make debugging easier + throw; + return 1; + } #endif #if QT_CONFIG(valgrind) @@ -1996,8 +1974,7 @@ void QTest::qCleanup() QSignalDumper::endDump(); #if defined(Q_OS_MACOS) - if (macNeedsActivate) - IOPMAssertionRelease(powerID); + IOPMAssertionRelease(macPowerSavingDisabled); #endif } diff --git a/src/testlib/qtestxunitstreamer.cpp b/src/testlib/qtestjunitstreamer.cpp index bdbdfa9610..9c3a9c9ca5 100644 --- a/src/testlib/qtestxunitstreamer.cpp +++ b/src/testlib/qtestjunitstreamer.cpp @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#include <QtTest/private/qtestxunitstreamer_p.h> -#include <QtTest/private/qxunittestlogger_p.h> +#include <QtTest/private/qtestjunitstreamer_p.h> +#include <QtTest/private/qjunittestlogger_p.h> #include <QtTest/private/qtestelement_p.h> #include <QtTest/private/qtestelementattribute_p.h> #include <QtTest/qtestassert.h> @@ -48,15 +48,15 @@ QT_BEGIN_NAMESPACE -QTestXunitStreamer::QTestXunitStreamer(QXunitTestLogger *logger) +QTestJUnitStreamer::QTestJUnitStreamer(QJUnitTestLogger *logger) : testLogger(logger) { QTEST_ASSERT(testLogger); } -QTestXunitStreamer::~QTestXunitStreamer() = default; +QTestJUnitStreamer::~QTestJUnitStreamer() = default; -void QTestXunitStreamer::indentForElement(const QTestElement* element, char* buf, int size) +void QTestJUnitStreamer::indentForElement(const QTestElement* element, char* buf, int size) { if (size == 0) return; @@ -74,7 +74,7 @@ void QTestXunitStreamer::indentForElement(const QTestElement* element, char* buf } } -void QTestXunitStreamer::formatStart(const QTestElement *element, QTestCharBuffer *formatted) const +void QTestJUnitStreamer::formatStart(const QTestElement *element, QTestCharBuffer *formatted) const { if (!element || !formatted ) return; @@ -95,7 +95,7 @@ void QTestXunitStreamer::formatStart(const QTestElement *element, QTestCharBuffe QTest::qt_asprintf(formatted, "%s<%s", indent, element->elementName()); } -void QTestXunitStreamer::formatEnd(const QTestElement *element, QTestCharBuffer *formatted) const +void QTestJUnitStreamer::formatEnd(const QTestElement *element, QTestCharBuffer *formatted) const { if (!element || !formatted ) return; @@ -111,7 +111,7 @@ void QTestXunitStreamer::formatEnd(const QTestElement *element, QTestCharBuffer QTest::qt_asprintf(formatted, "%s</%s>\n", indent, element->elementName()); } -void QTestXunitStreamer::formatAttributes(const QTestElement* element, const QTestElementAttribute *attribute, QTestCharBuffer *formatted) const +void QTestJUnitStreamer::formatAttributes(const QTestElement* element, const QTestElementAttribute *attribute, QTestCharBuffer *formatted) const { if (!attribute || !formatted ) return; @@ -143,7 +143,7 @@ void QTestXunitStreamer::formatAttributes(const QTestElement* element, const QTe } } -void QTestXunitStreamer::formatAfterAttributes(const QTestElement *element, QTestCharBuffer *formatted) const +void QTestJUnitStreamer::formatAfterAttributes(const QTestElement *element, QTestCharBuffer *formatted) const { if (!element || !formatted ) return; @@ -164,7 +164,7 @@ void QTestXunitStreamer::formatAfterAttributes(const QTestElement *element, QTes QTest::qt_asprintf(formatted, ">\n"); } -void QTestXunitStreamer::output(QTestElement *element) const +void QTestJUnitStreamer::output(QTestElement *element) const { QTEST_ASSERT(element); @@ -172,7 +172,7 @@ void QTestXunitStreamer::output(QTestElement *element) const outputElements(element); } -void QTestXunitStreamer::outputElements(QTestElement *element, bool) const +void QTestJUnitStreamer::outputElements(QTestElement *element, bool) const { QTestCharBuffer buf; bool hasChildren; @@ -205,7 +205,7 @@ void QTestXunitStreamer::outputElements(QTestElement *element, bool) const } } -void QTestXunitStreamer::outputElementAttributes(const QTestElement* element, QTestElementAttribute *attribute) const +void QTestJUnitStreamer::outputElementAttributes(const QTestElement* element, QTestElementAttribute *attribute) const { QTestCharBuffer buf; while (attribute) { @@ -215,7 +215,7 @@ void QTestXunitStreamer::outputElementAttributes(const QTestElement* element, QT } } -void QTestXunitStreamer::outputString(const char *msg) const +void QTestJUnitStreamer::outputString(const char *msg) const { testLogger->outputString(msg); } diff --git a/src/testlib/qtestxunitstreamer_p.h b/src/testlib/qtestjunitstreamer_p.h index db6d2896f7..7d91e2b66c 100644 --- a/src/testlib/qtestxunitstreamer_p.h +++ b/src/testlib/qtestjunitstreamer_p.h @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QTESTXUNITSTREAMER_P_H -#define QTESTXUNITSTREAMER_P_H +#ifndef QTESTJUNITSTREAMER_P_H +#define QTESTJUNITSTREAMER_P_H // // W A R N I N G @@ -58,14 +58,14 @@ QT_BEGIN_NAMESPACE class QTestElement; class QTestElementAttribute; -class QXunitTestLogger; +class QJUnitTestLogger; struct QTestCharBuffer; -class QTestXunitStreamer +class QTestJUnitStreamer { public: - QTestXunitStreamer(QXunitTestLogger *logger); - ~QTestXunitStreamer(); + QTestJUnitStreamer(QJUnitTestLogger *logger); + ~QTestJUnitStreamer(); void formatStart(const QTestElement *element, QTestCharBuffer *formatted) const; void formatEnd(const QTestElement *element, QTestCharBuffer *formatted) const; @@ -78,10 +78,9 @@ class QTestXunitStreamer void outputString(const char *msg) const; private: - void displayXunitXmlHeader() const; static void indentForElement(const QTestElement* element, char* buf, int size); - QXunitTestLogger *testLogger; + QJUnitTestLogger *testLogger; }; QT_END_NAMESPACE diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp index 2776740784..7bd108ab00 100644 --- a/src/testlib/qtestlog.cpp +++ b/src/testlib/qtestlog.cpp @@ -44,7 +44,7 @@ #include <QtTest/private/qabstracttestlogger_p.h> #include <QtTest/private/qplaintestlogger_p.h> #include <QtTest/private/qcsvbenchmarklogger_p.h> -#include <QtTest/private/qxunittestlogger_p.h> +#include <QtTest/private/qjunittestlogger_p.h> #include <QtTest/private/qxmltestlogger_p.h> #include <QtTest/private/qteamcitylogger_p.h> #include <QtTest/private/qtaptestlogger_p.h> @@ -456,8 +456,8 @@ void QTestLog::addLogger(LogMode mode, const char *filename) case QTestLog::LightXML: logger = new QXmlTestLogger(QXmlTestLogger::Light, filename); break; - case QTestLog::XunitXML: - logger = new QXunitTestLogger(filename); + case QTestLog::JUnitXML: + logger = new QJUnitTestLogger(filename); break; case QTestLog::TeamCity: logger = new QTeamCityLogger(filename); @@ -597,4 +597,6 @@ qint64 QTestLog::nsecsFunctionTime() return elapsedFunctionTime.nsecsElapsed(); } +#include "moc_qtestlog_p.cpp" + QT_END_NAMESPACE diff --git a/src/testlib/qtestlog_p.h b/src/testlib/qtestlog_p.h index fff36f290d..ddaf14ed9b 100644 --- a/src/testlib/qtestlog_p.h +++ b/src/testlib/qtestlog_p.h @@ -57,6 +57,8 @@ #include <QtCore/private/qcore_mac_p.h> #endif +#include <QtCore/qobjectdefs.h> + QT_BEGIN_NAMESPACE class QBenchmarkResult; @@ -65,13 +67,14 @@ class QTestData; class Q_TESTLIB_EXPORT QTestLog { + Q_GADGET public: QTestLog() = delete; ~QTestLog() = delete; Q_DISABLE_COPY_MOVE(QTestLog) enum LogMode { - Plain = 0, XML, LightXML, XunitXML, CSV, TeamCity, TAP + Plain = 0, XML, LightXML, JUnitXML, CSV, TeamCity, TAP #if defined(QT_USE_APPLE_UNIFIED_LOGGING) , Apple #endif @@ -79,6 +82,7 @@ public: , XCTest #endif }; + Q_ENUM(LogMode); static void enterTestFunction(const char* function); static void leaveTestFunction(); diff --git a/src/testlib/qtestutil_macos.mm b/src/testlib/qtestutil_macos.mm index 7579c3b164..880cd0f91f 100644 --- a/src/testlib/qtestutil_macos.mm +++ b/src/testlib/qtestutil_macos.mm @@ -39,6 +39,8 @@ #include "qtestutil_macos_p.h" +#include "QtCore/private/qcore_mac_p.h" + #import <AppKit/AppKit.h> QT_BEGIN_NAMESPACE @@ -55,6 +57,32 @@ namespace QTestPrivate { [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"ApplePersistenceIgnoreState"]; } + bool macCrashReporterWillShowDialog() + { + auto dialogType = QCFType<CFStringRef>(CFPreferencesCopyAppValue( + CFSTR("DialogType"), CFSTR("com.apple.CrashReporter"))); + + auto stringCompare = [](CFStringRef str1, CFStringRef str2) -> bool { + return CFStringCompare(str1, str2, kCFCompareCaseInsensitive) == kCFCompareEqualTo; + }; + + if (!dialogType || stringCompare(dialogType, CFSTR("basic"))) { + // The default (basic) dialog type only shows up if the + // application is 'user visible', as indicated by the + // activation policy. + auto *runningApp = NSRunningApplication.currentApplication; + return runningApp && runningApp.activationPolicy == NSApplicationActivationPolicyRegular; + } else if (stringCompare(dialogType, CFSTR("developer")) + || stringCompare(dialogType, CFSTR("crashreport"))) { + // While in developer mode the dialog will show for all + // crashed applications, including backgrounded ones. + return true; + } else { + // Finally, 'server' or 'none' will result in no dialog + return false; + } + } + /*! \internal \class AppNapDisabler \brief Disables App Nap by registereing a bacground activity. diff --git a/src/testlib/qtestutil_macos_p.h b/src/testlib/qtestutil_macos_p.h index 36f27167c0..c9a152dfdb 100644 --- a/src/testlib/qtestutil_macos_p.h +++ b/src/testlib/qtestutil_macos_p.h @@ -58,6 +58,7 @@ QT_BEGIN_NAMESPACE namespace QTestPrivate { void disableWindowRestore(); + bool macCrashReporterWillShowDialog(); class AppNapDisabler { diff --git a/src/testlib/testlib.pro b/src/testlib/testlib.pro index 530bc6b425..787df6b648 100644 --- a/src/testlib/testlib.pro +++ b/src/testlib/testlib.pro @@ -51,10 +51,10 @@ HEADERS = \ qtestblacklist_p.h \ qtesthelpers_p.h \ qttestglobal.h \ - qtestxunitstreamer_p.h \ + qtestjunitstreamer_p.h \ qtaptestlogger_p.h \ qxmltestlogger_p.h \ - qxunittestlogger_p.h + qjunittestlogger_p.h SOURCES = \ qtestcase.cpp \ @@ -77,8 +77,8 @@ SOURCES = \ qtestelement.cpp \ qtestelementattribute.cpp \ qtestmouse.cpp \ - qtestxunitstreamer.cpp \ - qxunittestlogger.cpp \ + qtestjunitstreamer.cpp \ + qjunittestlogger.cpp \ qtestblacklist.cpp \ qtaptestlogger.cpp @@ -112,7 +112,7 @@ mac { macos { HEADERS += qtestutil_macos_p.h OBJECTIVE_SOURCES += qtestutil_macos.mm - LIBS += -framework Foundation -framework ApplicationServices -framework IOKit + LIBS += -framework Foundation -framework ApplicationServices -framework IOKit -framework AppKit } # XCTest support (disabled for now) |