From ac0a266e75cd117ecf34d25cc5c947ec5e9d2c01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 31 Jan 2020 13:27:17 +0100 Subject: testlib: Don't disable watchdog when the macOS crash reporter is enabled The debuggerPresent() function was used both to decide whether we should print our own stacktrace, and if we should start the watchdog timer, but checking for the macOS crash reporter only applies to the former usecase. The crash reporter check has now been split into a separate function, only used to decide whether we should print our own stacktrace or not. Change-Id: I282aa57a51c14b07d3cbd547b551b6bf81b61897 Reviewed-by: Edward Welbourne --- src/testlib/qtestcase.cpp | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'src/testlib') diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 7b407c9073..d28cfd8613 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,21 +170,31 @@ static bool debuggerPresent() } } } + return false; +#else + // TODO + return false; +#endif +} - // 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. +static bool hasSystemCrashReporter() +{ +#if defined(Q_OS_MACOS) CFStringRef crashReporterType = static_cast( CFPreferencesCopyAppValue(CFSTR("DialogType"), CFSTR("com.apple.CrashReporter"))); if (crashReporterType == nullptr) return true; + auto equals = [](CFStringRef str1, CFStringRef str2) -> bool { + return CFStringCompare(str1, str2, kCFCompareCaseInsensitive) == kCFCompareEqualTo; + }; + const bool createsStackTrace = !equals(crashReporterType, CFSTR("server")) && !equals(crashReporterType, CFSTR("none")); CFRelease(crashReporterType); return createsStackTrace; #else - // TODO return false; #endif } @@ -216,7 +222,7 @@ static void stackTrace() if (ok && disableStackDump == 1) return; - if (debuggerPresent()) + if (debuggerPresent() || hasSystemCrashReporter()) return; #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) -- cgit v1.2.3 From 5940d20c69e21476c10ff38ed0689ea2f0b68c94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 31 Jan 2020 19:14:15 +0100 Subject: testlib: Fix indentation Change-Id: I54bb2c3dda731e1fd6c7e909aa30a3cedb53612b Reviewed-by: Simon Hausmann --- src/testlib/qtestcase.cpp | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) (limited to 'src/testlib') diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index d28cfd8613..2f62d51c58 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -1957,25 +1957,26 @@ int QTest::qRun() } #ifndef QT_NO_EXCEPTIONS - } catch (...) { - QTestResult::addFailure("Caught unhandled exception", __FILE__, __LINE__); - if (QTestResult::currentTestFunction()) { - QTestResult::finishedCurrentTestFunction(); - QTestResult::setCurrentTestFunction(nullptr); - } + } 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); - } + if (macNeedsActivate) { + IOPMAssertionRelease(powerID); + } #endif - currentTestObject = nullptr; + currentTestObject = nullptr; - // Rethrow exception to make debugging easier. - throw; - return 1; - } + // Re-throw exception to make debugging easier + throw; + return 1; + } #endif #if QT_CONFIG(valgrind) -- cgit v1.2.3 From 0dc31e667fc66ed98e855a0553958555e2fab2d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 31 Jan 2020 18:59:19 +0100 Subject: testlib: Simplify macOS setup/teardown code Change-Id: If92845ccb044becee136c7791fdb3cfe3641b28d Reviewed-by: Simon Hausmann --- src/testlib/qtestcase.cpp | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) (limited to 'src/testlib') diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 2f62d51c58..76a1b36a61 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -302,8 +302,7 @@ static QObject *currentTestObject = nullptr; static QString mainSourcePath; #if defined(Q_OS_MACOS) -bool macNeedsActivate = false; -IOPMAssertionID powerID; +static IOPMAssertionID macPowerSavingDisabled = 0; #endif class TestMethods { @@ -1854,23 +1853,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 @@ -1966,11 +1959,10 @@ int QTest::qRun() QTestLog::stopLogging(); -#if defined(Q_OS_MACX) - if (macNeedsActivate) { - IOPMAssertionRelease(powerID); - } +#if defined(Q_OS_MACOS) + IOPMAssertionRelease(macPowerSavingDisabled); #endif + currentTestObject = nullptr; // Re-throw exception to make debugging easier @@ -2003,8 +1995,7 @@ void QTest::qCleanup() QSignalDumper::endDump(); #if defined(Q_OS_MACOS) - if (macNeedsActivate) - IOPMAssertionRelease(powerID); + IOPMAssertionRelease(macPowerSavingDisabled); #endif } -- cgit v1.2.3 From facb8b74911cc9ff0fd61b4d31f45b14a5c004cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 31 Jan 2020 19:02:08 +0100 Subject: testlib: Properly cleanup after caught exception We were only doing half of what qCleanup was doing in the exception case, and in an inconsistent order. There's no reason not to just call qCleanup to align the behavior. Change-Id: Ic4e63afb4733de5b01a79272cca8908fca3de762 Reviewed-by: Simon Hausmann --- src/testlib/qtestcase.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'src/testlib') diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 76a1b36a61..02eb8a1661 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -1957,13 +1957,7 @@ int QTest::qRun() QTestResult::setCurrentTestFunction(nullptr); } - QTestLog::stopLogging(); - -#if defined(Q_OS_MACOS) - IOPMAssertionRelease(macPowerSavingDisabled); -#endif - - currentTestObject = nullptr; + qCleanup(); // Re-throw exception to make debugging easier throw; -- cgit v1.2.3 From d005d743dea1b072fdd18507f7bf6170f5e3d7ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 31 Jan 2020 20:22:11 +0100 Subject: testlib: Implement FatalSignalHandler inline Change-Id: Ie6f151cb099151616f83ea8d5e11e6625bedeb8c Reviewed-by: Simon Hausmann --- src/testlib/qtestcase.cpp | 169 ++++++++++++++++++++++------------------------ 1 file changed, 82 insertions(+), 87 deletions(-) (limited to 'src/testlib') diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 02eb8a1661..fde20ada33 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -1534,122 +1534,117 @@ void TestMethods::invokeTests(QObject *testObject) const } #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) + FatalSignalHandler() { - struct sigaction act; - memset(&act, 0, sizeof(struct sigaction)); - act.sa_handler = SIG_DFL; - sigaction(signum, &act, NULL); - } -#endif -} + sigemptyset(&handledSignals); -FatalSignalHandler::FatalSignalHandler() -{ - sigemptyset(&handledSignals); - - const int fatalSignals[] = { - SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGBUS, SIGFPE, SIGSEGV, SIGPIPE, SIGTERM, 0 }; + 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; + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = FatalSignalHandler::signal; - // Remove the handler after it is invoked. + // Remove the handler after it is invoked. #if !defined(Q_OS_INTEGRITY) - act.sa_flags = SA_RESETHAND; + 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) + // 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 + // 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))) + // 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; + 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]); + // 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; + struct sigaction oldact; - for (int i = 0; fatalSignals[i]; ++i) { - sigaction(fatalSignals[i], &act, &oldact); - if ( + for (int i = 0; fatalSignals[i]; ++i) { + sigaction(fatalSignals[i], &act, &oldact); + if ( #ifdef SA_SIGINFO - oldact.sa_flags & SA_SIGINFO || + oldact.sa_flags & SA_SIGINFO || #endif - oldact.sa_handler != SIG_DFL) { - sigaction(fatalSignals[i], &oldact, nullptr); - } else - { - sigaddset(&handledSignals, fatalSignals[i]); + oldact.sa_handler != SIG_DFL) { + sigaction(fatalSignals[i], &oldact, nullptr); + } else + { + sigaddset(&handledSignals, fatalSignals[i]); + } } } -} + ~FatalSignalHandler() + { + // Unregister any of our remaining signal handlers + struct sigaction act; + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_DFL; -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; - struct sigaction oldact; + for (int i = 1; i < 32; ++i) { + if (!sigismember(&handledSignals, i)) + continue; + sigaction(i, &act, &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); + // If someone overwrote it in the mean time, put it back + if (oldact.sa_handler != FatalSignalHandler::signal) + sigaction(i, &oldact, nullptr); + } } -} +private: + 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 + } + sigset_t handledSignals; +}; + +#endif } // namespace -- cgit v1.2.3 From 0c8ff269dcda7de68c475535d575c0bb34af788b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 31 Jan 2020 20:34:27 +0100 Subject: testlib: Unite Windows and Unix signal handler code Change-Id: I7b9d480008167c071bf925d655eb97ef437bc206 Reviewed-by: Simon Hausmann --- src/testlib/qtestcase.cpp | 330 +++++++++++++++++++++++----------------------- 1 file changed, 165 insertions(+), 165 deletions(-) (limited to 'src/testlib') diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index fde20ada33..372e208919 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -1533,121 +1533,6 @@ void TestMethods::invokeTests(QObject *testObject) const QTestResult::setCurrentTestFunction(nullptr); } -#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) - -class FatalSignalHandler -{ -public: - 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() - { - // 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); - } - } - -private: - 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 - } - - sigset_t handledSignals; -}; - -#endif - -} // namespace - #if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) // Helper class for resolving symbol names by dynamically loading "dbghelp.dll". @@ -1746,49 +1631,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) - fputc('\n', stdout); - fflush(stdout); +#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 + } - return EXCEPTION_EXECUTE_HANDLER; -} -#endif // Q_OS_WIN) && !Q_OS_WINRT + sigset_t handledSignals; +#endif // defined(Q_OS_UNIX) && !defined(Q_OS_WASM) +}; + +} // namespace static void initEnvironment() { @@ -1896,18 +1909,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)) @@ -1922,11 +1923,10 @@ int QTest::qRun() } else #endif { -#if defined(Q_OS_UNIX) && !defined(Q_OS_WASM) QScopedPointer handler; if (!noCrashHandler) handler.reset(new FatalSignalHandler); -#endif + TestMethods::MetaMethods commandLineMethods; for (const QString &tf : qAsConst(QTest::testFunctions)) { const QByteArray tfB = tf.toLatin1(); -- cgit v1.2.3 From aadf21b25cad7026f07a5fe7bf19fbedfb1866b7 Mon Sep 17 00:00:00 2001 From: Linus Jahn Date: Fri, 31 Jan 2020 16:47:46 +0100 Subject: Fix 'the the' typo in comments Change-Id: I00fcb1c2374e7ca168b6240f9d41c0323fb0867c Reviewed-by: Giuseppe D'Angelo --- src/testlib/qtestcase.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/testlib') diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 9ab12c5c68..70733a692a 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -1301,7 +1301,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; } @@ -1394,7 +1394,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; } -- cgit v1.2.3 From 27db9e458cef512fca3a6b5c9ebbcda7a8172428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Wed, 22 Jan 2020 16:43:40 +0100 Subject: testlib: Clarify that our XUnit reporter is actually a JUnit reporter The reporter was probably named 'xunit' based on the historical use of xUnit to refer to testing frameworks derived from Smalltalk's SUnit. These frameworks typically added their own prefix, e.g. JUnit for Java, RUnit for R, etc. The most popular of these was the JUnit framework, and the corresponding XML output produced by the Ant built tool became somewhat of a de facto standard, which is probably why we chose to model our reporter after it. Nowadays however, naming it 'xunit' is problematic as there is actually a testing famework named xUnit.net, typically shortened to, you guessed it: xunit. Test report consumers will typically have a junit mode, and an xunit mode, and the latter could easily be mistaken for what testlib outputs, unless we clarify this. The clarification also allows us to safely extend our support for the JUnit XML format to incorporate some elements that are nowadays common, but where we are lagging behind the standard. [ChangeLog][QTestLib] The formerly named 'xunitxml' test reporter has been renamed to what it actually is: a JUnit test reporter, and is now triggered by passing -o junitxml to the test binary. Change-Id: Ieb20d3d2b5905c74e55b98174948cc70870c0ef9 Reviewed-by: Friedemann Kleint Reviewed-by: Simon Hausmann --- src/testlib/doc/src/qttestlib-manual.qdoc | 8 +- src/testlib/qjunittestlogger.cpp | 352 ++++++++++++++++++++++++++++++ src/testlib/qjunittestlogger_p.h | 94 ++++++++ src/testlib/qtestcase.cpp | 14 +- src/testlib/qtestjunitstreamer.cpp | 223 +++++++++++++++++++ src/testlib/qtestjunitstreamer_p.h | 88 ++++++++ src/testlib/qtestlog.cpp | 6 +- src/testlib/qtestlog_p.h | 2 +- src/testlib/qtestxunitstreamer.cpp | 223 ------------------- src/testlib/qtestxunitstreamer_p.h | 89 -------- src/testlib/qxunittestlogger.cpp | 352 ------------------------------ src/testlib/qxunittestlogger_p.h | 94 -------- src/testlib/testlib.pro | 8 +- 13 files changed, 776 insertions(+), 777 deletions(-) create mode 100644 src/testlib/qjunittestlogger.cpp create mode 100644 src/testlib/qjunittestlogger_p.h create mode 100644 src/testlib/qtestjunitstreamer.cpp create mode 100644 src/testlib/qtestjunitstreamer_p.h delete mode 100644 src/testlib/qtestxunitstreamer.cpp delete mode 100644 src/testlib/qtestxunitstreamer_p.h delete mode 100644 src/testlib/qxunittestlogger.cpp delete mode 100644 src/testlib/qxunittestlogger_p.h (limited to 'src/testlib') 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/qjunittestlogger.cpp b/src/testlib/qjunittestlogger.cpp new file mode 100644 index 0000000000..2d71f7967b --- /dev/null +++ b/src/testlib/qjunittestlogger.cpp @@ -0,0 +1,352 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#ifdef min // windows.h without NOMINMAX is included by the benchmark headers. +# undef min +#endif +#ifdef max +# undef max +#endif + +#include + +#include + +QT_BEGIN_NAMESPACE + +QJUnitTestLogger::QJUnitTestLogger(const char *filename) + : QAbstractTestLogger(filename) +{ +} + +QJUnitTestLogger::~QJUnitTestLogger() +{ + delete currentLogElement; + delete logFormatter; +} + +void QJUnitTestLogger::startLogging() +{ + QAbstractTestLogger::startLogging(); + + logFormatter = new QTestJUnitStreamer(this); + delete errorLogElement; + errorLogElement = new QTestElement(QTest::LET_SystemError); +} + +void QJUnitTestLogger::stopLogging() +{ + QTestElement *iterator = listOfTestcases; + + char buf[10]; + + currentLogElement = new QTestElement(QTest::LET_TestSuite); + currentLogElement->addAttribute(QTest::AI_Name, QTestResult::currentTestObjectName()); + + qsnprintf(buf, sizeof(buf), "%i", testCounter); + currentLogElement->addAttribute(QTest::AI_Tests, buf); + + qsnprintf(buf, sizeof(buf), "%i", failureCounter); + currentLogElement->addAttribute(QTest::AI_Failures, buf); + + qsnprintf(buf, sizeof(buf), "%i", errorCounter); + currentLogElement->addAttribute(QTest::AI_Errors, buf); + + QTestElement *property; + QTestElement *properties = new QTestElement(QTest::LET_Properties); + + property = new QTestElement(QTest::LET_Property); + property->addAttribute(QTest::AI_Name, "QTestVersion"); + property->addAttribute(QTest::AI_PropertyValue, QTEST_VERSION_STR); + properties->addLogElement(property); + + property = new QTestElement(QTest::LET_Property); + property->addAttribute(QTest::AI_Name, "QtVersion"); + property->addAttribute(QTest::AI_PropertyValue, qVersion()); + properties->addLogElement(property); + + property = new QTestElement(QTest::LET_Property); + property->addAttribute(QTest::AI_Name, "QtBuild"); + property->addAttribute(QTest::AI_PropertyValue, QLibraryInfo::build()); + properties->addLogElement(property); + + currentLogElement->addLogElement(properties); + + currentLogElement->addLogElement(iterator); + + /* For correct indenting, make sure every testcase knows its parent */ + QTestElement* testcase = iterator; + while (testcase) { + testcase->setParent(currentLogElement); + testcase = testcase->nextElement(); + } + + currentLogElement->addLogElement(errorLogElement); + + QTestElement *it = currentLogElement; + logFormatter->output(it); + + QAbstractTestLogger::stopLogging(); +} + +void QJUnitTestLogger::enterTestFunction(const char *function) +{ + currentLogElement = new QTestElement(QTest::LET_TestCase); + currentLogElement->addAttribute(QTest::AI_Name, function); + currentLogElement->addToList(&listOfTestcases); + + ++testCounter; +} + +void QJUnitTestLogger::leaveTestFunction() +{ +} + +void QJUnitTestLogger::addIncident(IncidentTypes type, const char *description, + const char *file, int line) +{ + const char *typeBuf = nullptr; + char buf[100]; + + switch (type) { + case QAbstractTestLogger::XPass: + ++failureCounter; + typeBuf = "xpass"; + break; + case QAbstractTestLogger::Pass: + typeBuf = "pass"; + break; + case QAbstractTestLogger::XFail: + typeBuf = "xfail"; + break; + case QAbstractTestLogger::Fail: + ++failureCounter; + typeBuf = "fail"; + break; + case QAbstractTestLogger::BlacklistedPass: + typeBuf = "bpass"; + break; + case QAbstractTestLogger::BlacklistedFail: + ++failureCounter; + typeBuf = "bfail"; + break; + case QAbstractTestLogger::BlacklistedXPass: + typeBuf = "bxpass"; + break; + case QAbstractTestLogger::BlacklistedXFail: + ++failureCounter; + typeBuf = "bxfail"; + break; + default: + typeBuf = "??????"; + break; + } + + if (type == QAbstractTestLogger::Fail || type == QAbstractTestLogger::XPass) { + QTestElement *failureElement = new QTestElement(QTest::LET_Failure); + failureElement->addAttribute(QTest::AI_Result, typeBuf); + if (file) + failureElement->addAttribute(QTest::AI_File, file); + else + failureElement->addAttribute(QTest::AI_File, ""); + qsnprintf(buf, sizeof(buf), "%i", line); + failureElement->addAttribute(QTest::AI_Line, buf); + failureElement->addAttribute(QTest::AI_Description, description); + addTag(failureElement); + currentLogElement->addLogElement(failureElement); + } + + /* + Only one result can be shown for the whole testfunction. + Check if we currently have a result, and if so, overwrite it + iff the new result is worse. + */ + QTestElementAttribute* resultAttr = + const_cast(currentLogElement->attribute(QTest::AI_Result)); + if (resultAttr) { + const char* oldResult = resultAttr->value(); + bool overwrite = false; + if (!strcmp(oldResult, "pass")) { + overwrite = true; + } + else if (!strcmp(oldResult, "bpass") || !strcmp(oldResult, "bxfail")) { + overwrite = (type == QAbstractTestLogger::XPass || type == QAbstractTestLogger::Fail) || (type == QAbstractTestLogger::XFail) + || (type == QAbstractTestLogger::BlacklistedFail) || (type == QAbstractTestLogger::BlacklistedXPass); + } + else if (!strcmp(oldResult, "bfail") || !strcmp(oldResult, "bxpass")) { + overwrite = (type == QAbstractTestLogger::XPass || type == QAbstractTestLogger::Fail) || (type == QAbstractTestLogger::XFail); + } + else if (!strcmp(oldResult, "xfail")) { + overwrite = (type == QAbstractTestLogger::XPass || type == QAbstractTestLogger::Fail); + } + else if (!strcmp(oldResult, "xpass")) { + overwrite = (type == QAbstractTestLogger::Fail); + } + if (overwrite) { + resultAttr->setPair(QTest::AI_Result, typeBuf); + } + } + else { + currentLogElement->addAttribute(QTest::AI_Result, typeBuf); + } + + if (file) + currentLogElement->addAttribute(QTest::AI_File, file); + else + currentLogElement->addAttribute(QTest::AI_File, ""); + + qsnprintf(buf, sizeof(buf), "%i", line); + currentLogElement->addAttribute(QTest::AI_Line, buf); + + /* + 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) { + QJUnitTestLogger::addMessage(QAbstractTestLogger::Info, QString::fromUtf8(description), file, line); + } +} + +void QJUnitTestLogger::addBenchmarkResult(const QBenchmarkResult &result) +{ + QTestElement *benchmarkElement = new QTestElement(QTest::LET_Benchmark); + + benchmarkElement->addAttribute( + QTest::AI_Metric, + QTest::benchmarkMetricName(result.metric)); + benchmarkElement->addAttribute(QTest::AI_Tag, result.context.tag.toUtf8().data()); + + const qreal valuePerIteration = qreal(result.value) / qreal(result.iterations); + benchmarkElement->addAttribute(QTest::AI_Value, QByteArray::number(valuePerIteration).constData()); + + char buf[100]; + qsnprintf(buf, sizeof(buf), "%i", result.iterations); + benchmarkElement->addAttribute(QTest::AI_Iterations, buf); + currentLogElement->addLogElement(benchmarkElement); +} + +void QJUnitTestLogger::addTag(QTestElement* element) +{ + const char *tag = QTestResult::currentDataTag(); + const char *gtag = QTestResult::currentGlobalDataTag(); + const char *filler = (tag && gtag) ? ":" : ""; + if ((!tag || !tag[0]) && (!gtag || !gtag[0])) { + return; + } + + if (!tag) { + tag = ""; + } + if (!gtag) { + gtag = ""; + } + + QTestCharBuffer buf; + QTest::qt_asprintf(&buf, "%s%s%s", gtag, filler, tag); + element->addAttribute(QTest::AI_Tag, buf.constData()); +} + +void QJUnitTestLogger::addMessage(MessageTypes type, const QString &message, const char *file, int line) +{ + QTestElement *errorElement = new QTestElement(QTest::LET_Error); + const char *typeBuf = nullptr; + + switch (type) { + case QAbstractTestLogger::Warn: + typeBuf = "warn"; + break; + case QAbstractTestLogger::QSystem: + typeBuf = "system"; + break; + case QAbstractTestLogger::QDebug: + typeBuf = "qdebug"; + break; + case QAbstractTestLogger::QInfo: + typeBuf = "qinfo"; + break; + case QAbstractTestLogger::QWarning: + typeBuf = "qwarn"; + break; + case QAbstractTestLogger::QFatal: + typeBuf = "qfatal"; + break; + case QAbstractTestLogger::Skip: + typeBuf = "skip"; + break; + case QAbstractTestLogger::Info: + typeBuf = "info"; + break; + default: + typeBuf = "??????"; + break; + } + + errorElement->addAttribute(QTest::AI_Type, typeBuf); + errorElement->addAttribute(QTest::AI_Description, message.toUtf8().constData()); + addTag(errorElement); + + if (file) + errorElement->addAttribute(QTest::AI_File, file); + else + errorElement->addAttribute(QTest::AI_File, ""); + + char buf[100]; + qsnprintf(buf, sizeof(buf), "%i", line); + errorElement->addAttribute(QTest::AI_Line, buf); + + currentLogElement->addLogElement(errorElement); + ++errorCounter; + + // Also add the message to the system error log (i.e. stderr), if one exists + if (errorLogElement) { + QTestElement *systemErrorElement = new QTestElement(QTest::LET_Error); + systemErrorElement->addAttribute(QTest::AI_Description, message.toUtf8().constData()); + errorLogElement->addLogElement(systemErrorElement); + } +} + +QT_END_NAMESPACE + diff --git a/src/testlib/qjunittestlogger_p.h b/src/testlib/qjunittestlogger_p.h new file mode 100644 index 0000000000..6fd4e4c331 --- /dev/null +++ b/src/testlib/qjunittestlogger_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJUNITTESTLOGGER_P_H +#define QJUNITTESTLOGGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QTestJUnitStreamer; +class QTestElement; + +class QJUnitTestLogger : public QAbstractTestLogger +{ + public: + QJUnitTestLogger(const char *filename); + ~QJUnitTestLogger(); + + void startLogging() override; + void stopLogging() override; + + void enterTestFunction(const char *function) override; + void leaveTestFunction() override; + + void addIncident(IncidentTypes type, const char *description, + const char *file = nullptr, int line = 0) override; + void addBenchmarkResult(const QBenchmarkResult &result) override; + void addTag(QTestElement* element); + + void addMessage(MessageTypes type, const QString &message, + const char *file = nullptr, int line = 0) override; + + private: + QTestElement *listOfTestcases = nullptr; + QTestElement *currentLogElement = nullptr; + QTestElement *errorLogElement = nullptr; + QTestJUnitStreamer *logFormatter = nullptr; + + int testCounter = 0; + int failureCounter = 0; + int errorCounter = 0; +}; + +QT_END_NAMESPACE + +#endif // QJUNITTESTLOGGER_P_H diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 372e208919..189009ee16 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -541,7 +541,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" @@ -553,7 +553,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" @@ -637,8 +637,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) { @@ -677,14 +677,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()) { diff --git a/src/testlib/qtestjunitstreamer.cpp b/src/testlib/qtestjunitstreamer.cpp new file mode 100644 index 0000000000..9c3a9c9ca5 --- /dev/null +++ b/src/testlib/qtestjunitstreamer.cpp @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QTestJUnitStreamer::QTestJUnitStreamer(QJUnitTestLogger *logger) + : testLogger(logger) +{ + QTEST_ASSERT(testLogger); +} + +QTestJUnitStreamer::~QTestJUnitStreamer() = default; + +void QTestJUnitStreamer::indentForElement(const QTestElement* element, char* buf, int size) +{ + if (size == 0) return; + + buf[0] = 0; + + if (!element) return; + + char* endbuf = buf + size; + element = element->parentElement(); + while (element && buf+2 < endbuf) { + *(buf++) = ' '; + *(buf++) = ' '; + *buf = 0; + element = element->parentElement(); + } +} + +void QTestJUnitStreamer::formatStart(const QTestElement *element, QTestCharBuffer *formatted) const +{ + if (!element || !formatted ) + return; + + char indent[20]; + indentForElement(element, indent, sizeof(indent)); + + // Errors are written as CDATA within system-err, comments elsewhere + if (element->elementType() == QTest::LET_Error) { + if (element->parentElement()->elementType() == QTest::LET_SystemError) { + QTest::qt_asprintf(formatted, "elementName()); +} + +void QTestJUnitStreamer::formatEnd(const QTestElement *element, QTestCharBuffer *formatted) const +{ + if (!element || !formatted ) + return; + + if (!element->childElements()) { + formatted->data()[0] = '\0'; + return; + } + + char indent[20]; + indentForElement(element, indent, sizeof(indent)); + + QTest::qt_asprintf(formatted, "%s\n", indent, element->elementName()); +} + +void QTestJUnitStreamer::formatAttributes(const QTestElement* element, const QTestElementAttribute *attribute, QTestCharBuffer *formatted) const +{ + if (!attribute || !formatted ) + return; + + QTest::AttributeIndex attrindex = attribute->index(); + + // For errors within system-err, we only want to output `message' + if (element && element->elementType() == QTest::LET_Error + && element->parentElement()->elementType() == QTest::LET_SystemError) { + + if (attrindex != QTest::AI_Description) return; + + QXmlTestLogger::xmlCdata(formatted, attribute->value()); + return; + } + + char const* key = nullptr; + if (attrindex == QTest::AI_Description) + key = "message"; + else if (attrindex != QTest::AI_File && attrindex != QTest::AI_Line) + key = attribute->name(); + + if (key) { + QTestCharBuffer quotedValue; + QXmlTestLogger::xmlQuote("edValue, attribute->value()); + QTest::qt_asprintf(formatted, " %s=\"%s\"", key, quotedValue.constData()); + } else { + formatted->data()[0] = '\0'; + } +} + +void QTestJUnitStreamer::formatAfterAttributes(const QTestElement *element, QTestCharBuffer *formatted) const +{ + if (!element || !formatted ) + return; + + // Errors are written as CDATA within system-err, comments elsewhere + if (element->elementType() == QTest::LET_Error) { + if (element->parentElement()->elementType() == QTest::LET_SystemError) { + QTest::qt_asprintf(formatted, "]]>\n"); + } else { + QTest::qt_asprintf(formatted, " -->\n"); + } + return; + } + + if (!element->childElements()) + QTest::qt_asprintf(formatted, "/>\n"); + else + QTest::qt_asprintf(formatted, ">\n"); +} + +void QTestJUnitStreamer::output(QTestElement *element) const +{ + QTEST_ASSERT(element); + + outputString("\n"); + outputElements(element); +} + +void QTestJUnitStreamer::outputElements(QTestElement *element, bool) const +{ + QTestCharBuffer buf; + bool hasChildren; + /* + Elements are in reverse order of occurrence, so start from the end and work + our way backwards. + */ + while (element && element->nextElement()) { + element = element->nextElement(); + } + while (element) { + hasChildren = element->childElements(); + + if (element->elementType() != QTest::LET_Benchmark) { + formatStart(element, &buf); + outputString(buf.data()); + + outputElementAttributes(element, element->attributes()); + + formatAfterAttributes(element, &buf); + outputString(buf.data()); + + if (hasChildren) + outputElements(element->childElements(), true); + + formatEnd(element, &buf); + outputString(buf.data()); + } + element = element->previousElement(); + } +} + +void QTestJUnitStreamer::outputElementAttributes(const QTestElement* element, QTestElementAttribute *attribute) const +{ + QTestCharBuffer buf; + while (attribute) { + formatAttributes(element, attribute, &buf); + outputString(buf.data()); + attribute = attribute->nextElement(); + } +} + +void QTestJUnitStreamer::outputString(const char *msg) const +{ + testLogger->outputString(msg); +} + +QT_END_NAMESPACE diff --git a/src/testlib/qtestjunitstreamer_p.h b/src/testlib/qtestjunitstreamer_p.h new file mode 100644 index 0000000000..7d91e2b66c --- /dev/null +++ b/src/testlib/qtestjunitstreamer_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtTest module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTESTJUNITSTREAMER_P_H +#define QTESTJUNITSTREAMER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + + +class QTestElement; +class QTestElementAttribute; +class QJUnitTestLogger; +struct QTestCharBuffer; + +class QTestJUnitStreamer +{ + public: + QTestJUnitStreamer(QJUnitTestLogger *logger); + ~QTestJUnitStreamer(); + + void formatStart(const QTestElement *element, QTestCharBuffer *formatted) const; + void formatEnd(const QTestElement *element, QTestCharBuffer *formatted) const; + void formatAfterAttributes(const QTestElement *element, QTestCharBuffer *formatted) const; + void formatAttributes(const QTestElement *element, const QTestElementAttribute *attribute, QTestCharBuffer *formatted) const; + void output(QTestElement *element) const; + void outputElements(QTestElement *element, bool isChildElement = false) const; + void outputElementAttributes(const QTestElement *element, QTestElementAttribute *attribute) const; + + void outputString(const char *msg) const; + + private: + static void indentForElement(const QTestElement* element, char* buf, int size); + + QJUnitTestLogger *testLogger; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp index 2776740784..7e5f9182b8 100644 --- a/src/testlib/qtestlog.cpp +++ b/src/testlib/qtestlog.cpp @@ -44,7 +44,7 @@ #include #include #include -#include +#include #include #include #include @@ -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); diff --git a/src/testlib/qtestlog_p.h b/src/testlib/qtestlog_p.h index fff36f290d..959aef6968 100644 --- a/src/testlib/qtestlog_p.h +++ b/src/testlib/qtestlog_p.h @@ -71,7 +71,7 @@ public: 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 diff --git a/src/testlib/qtestxunitstreamer.cpp b/src/testlib/qtestxunitstreamer.cpp deleted file mode 100644 index bdbdfa9610..0000000000 --- a/src/testlib/qtestxunitstreamer.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtTest module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -QTestXunitStreamer::QTestXunitStreamer(QXunitTestLogger *logger) - : testLogger(logger) -{ - QTEST_ASSERT(testLogger); -} - -QTestXunitStreamer::~QTestXunitStreamer() = default; - -void QTestXunitStreamer::indentForElement(const QTestElement* element, char* buf, int size) -{ - if (size == 0) return; - - buf[0] = 0; - - if (!element) return; - - char* endbuf = buf + size; - element = element->parentElement(); - while (element && buf+2 < endbuf) { - *(buf++) = ' '; - *(buf++) = ' '; - *buf = 0; - element = element->parentElement(); - } -} - -void QTestXunitStreamer::formatStart(const QTestElement *element, QTestCharBuffer *formatted) const -{ - if (!element || !formatted ) - return; - - char indent[20]; - indentForElement(element, indent, sizeof(indent)); - - // Errors are written as CDATA within system-err, comments elsewhere - if (element->elementType() == QTest::LET_Error) { - if (element->parentElement()->elementType() == QTest::LET_SystemError) { - QTest::qt_asprintf(formatted, "elementName()); -} - -void QTestXunitStreamer::formatEnd(const QTestElement *element, QTestCharBuffer *formatted) const -{ - if (!element || !formatted ) - return; - - if (!element->childElements()) { - formatted->data()[0] = '\0'; - return; - } - - char indent[20]; - indentForElement(element, indent, sizeof(indent)); - - QTest::qt_asprintf(formatted, "%s\n", indent, element->elementName()); -} - -void QTestXunitStreamer::formatAttributes(const QTestElement* element, const QTestElementAttribute *attribute, QTestCharBuffer *formatted) const -{ - if (!attribute || !formatted ) - return; - - QTest::AttributeIndex attrindex = attribute->index(); - - // For errors within system-err, we only want to output `message' - if (element && element->elementType() == QTest::LET_Error - && element->parentElement()->elementType() == QTest::LET_SystemError) { - - if (attrindex != QTest::AI_Description) return; - - QXmlTestLogger::xmlCdata(formatted, attribute->value()); - return; - } - - char const* key = nullptr; - if (attrindex == QTest::AI_Description) - key = "message"; - else if (attrindex != QTest::AI_File && attrindex != QTest::AI_Line) - key = attribute->name(); - - if (key) { - QTestCharBuffer quotedValue; - QXmlTestLogger::xmlQuote("edValue, attribute->value()); - QTest::qt_asprintf(formatted, " %s=\"%s\"", key, quotedValue.constData()); - } else { - formatted->data()[0] = '\0'; - } -} - -void QTestXunitStreamer::formatAfterAttributes(const QTestElement *element, QTestCharBuffer *formatted) const -{ - if (!element || !formatted ) - return; - - // Errors are written as CDATA within system-err, comments elsewhere - if (element->elementType() == QTest::LET_Error) { - if (element->parentElement()->elementType() == QTest::LET_SystemError) { - QTest::qt_asprintf(formatted, "]]>\n"); - } else { - QTest::qt_asprintf(formatted, " -->\n"); - } - return; - } - - if (!element->childElements()) - QTest::qt_asprintf(formatted, "/>\n"); - else - QTest::qt_asprintf(formatted, ">\n"); -} - -void QTestXunitStreamer::output(QTestElement *element) const -{ - QTEST_ASSERT(element); - - outputString("\n"); - outputElements(element); -} - -void QTestXunitStreamer::outputElements(QTestElement *element, bool) const -{ - QTestCharBuffer buf; - bool hasChildren; - /* - Elements are in reverse order of occurrence, so start from the end and work - our way backwards. - */ - while (element && element->nextElement()) { - element = element->nextElement(); - } - while (element) { - hasChildren = element->childElements(); - - if (element->elementType() != QTest::LET_Benchmark) { - formatStart(element, &buf); - outputString(buf.data()); - - outputElementAttributes(element, element->attributes()); - - formatAfterAttributes(element, &buf); - outputString(buf.data()); - - if (hasChildren) - outputElements(element->childElements(), true); - - formatEnd(element, &buf); - outputString(buf.data()); - } - element = element->previousElement(); - } -} - -void QTestXunitStreamer::outputElementAttributes(const QTestElement* element, QTestElementAttribute *attribute) const -{ - QTestCharBuffer buf; - while (attribute) { - formatAttributes(element, attribute, &buf); - outputString(buf.data()); - attribute = attribute->nextElement(); - } -} - -void QTestXunitStreamer::outputString(const char *msg) const -{ - testLogger->outputString(msg); -} - -QT_END_NAMESPACE diff --git a/src/testlib/qtestxunitstreamer_p.h b/src/testlib/qtestxunitstreamer_p.h deleted file mode 100644 index db6d2896f7..0000000000 --- a/src/testlib/qtestxunitstreamer_p.h +++ /dev/null @@ -1,89 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtTest module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QTESTXUNITSTREAMER_P_H -#define QTESTXUNITSTREAMER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -QT_BEGIN_NAMESPACE - - -class QTestElement; -class QTestElementAttribute; -class QXunitTestLogger; -struct QTestCharBuffer; - -class QTestXunitStreamer -{ - public: - QTestXunitStreamer(QXunitTestLogger *logger); - ~QTestXunitStreamer(); - - void formatStart(const QTestElement *element, QTestCharBuffer *formatted) const; - void formatEnd(const QTestElement *element, QTestCharBuffer *formatted) const; - void formatAfterAttributes(const QTestElement *element, QTestCharBuffer *formatted) const; - void formatAttributes(const QTestElement *element, const QTestElementAttribute *attribute, QTestCharBuffer *formatted) const; - void output(QTestElement *element) const; - void outputElements(QTestElement *element, bool isChildElement = false) const; - void outputElementAttributes(const QTestElement *element, QTestElementAttribute *attribute) const; - - void outputString(const char *msg) const; - - private: - void displayXunitXmlHeader() const; - static void indentForElement(const QTestElement* element, char* buf, int size); - - QXunitTestLogger *testLogger; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/testlib/qxunittestlogger.cpp b/src/testlib/qxunittestlogger.cpp deleted file mode 100644 index b3cac9cb82..0000000000 --- a/src/testlib/qxunittestlogger.cpp +++ /dev/null @@ -1,352 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtTest module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#include -#include -#include -#include - -#ifdef min // windows.h without NOMINMAX is included by the benchmark headers. -# undef min -#endif -#ifdef max -# undef max -#endif - -#include - -#include - -QT_BEGIN_NAMESPACE - -QXunitTestLogger::QXunitTestLogger(const char *filename) - : QAbstractTestLogger(filename) -{ -} - -QXunitTestLogger::~QXunitTestLogger() -{ - delete currentLogElement; - delete logFormatter; -} - -void QXunitTestLogger::startLogging() -{ - QAbstractTestLogger::startLogging(); - - logFormatter = new QTestXunitStreamer(this); - delete errorLogElement; - errorLogElement = new QTestElement(QTest::LET_SystemError); -} - -void QXunitTestLogger::stopLogging() -{ - QTestElement *iterator = listOfTestcases; - - char buf[10]; - - currentLogElement = new QTestElement(QTest::LET_TestSuite); - currentLogElement->addAttribute(QTest::AI_Name, QTestResult::currentTestObjectName()); - - qsnprintf(buf, sizeof(buf), "%i", testCounter); - currentLogElement->addAttribute(QTest::AI_Tests, buf); - - qsnprintf(buf, sizeof(buf), "%i", failureCounter); - currentLogElement->addAttribute(QTest::AI_Failures, buf); - - qsnprintf(buf, sizeof(buf), "%i", errorCounter); - currentLogElement->addAttribute(QTest::AI_Errors, buf); - - QTestElement *property; - QTestElement *properties = new QTestElement(QTest::LET_Properties); - - property = new QTestElement(QTest::LET_Property); - property->addAttribute(QTest::AI_Name, "QTestVersion"); - property->addAttribute(QTest::AI_PropertyValue, QTEST_VERSION_STR); - properties->addLogElement(property); - - property = new QTestElement(QTest::LET_Property); - property->addAttribute(QTest::AI_Name, "QtVersion"); - property->addAttribute(QTest::AI_PropertyValue, qVersion()); - properties->addLogElement(property); - - property = new QTestElement(QTest::LET_Property); - property->addAttribute(QTest::AI_Name, "QtBuild"); - property->addAttribute(QTest::AI_PropertyValue, QLibraryInfo::build()); - properties->addLogElement(property); - - currentLogElement->addLogElement(properties); - - currentLogElement->addLogElement(iterator); - - /* For correct indenting, make sure every testcase knows its parent */ - QTestElement* testcase = iterator; - while (testcase) { - testcase->setParent(currentLogElement); - testcase = testcase->nextElement(); - } - - currentLogElement->addLogElement(errorLogElement); - - QTestElement *it = currentLogElement; - logFormatter->output(it); - - QAbstractTestLogger::stopLogging(); -} - -void QXunitTestLogger::enterTestFunction(const char *function) -{ - currentLogElement = new QTestElement(QTest::LET_TestCase); - currentLogElement->addAttribute(QTest::AI_Name, function); - currentLogElement->addToList(&listOfTestcases); - - ++testCounter; -} - -void QXunitTestLogger::leaveTestFunction() -{ -} - -void QXunitTestLogger::addIncident(IncidentTypes type, const char *description, - const char *file, int line) -{ - const char *typeBuf = nullptr; - char buf[100]; - - switch (type) { - case QAbstractTestLogger::XPass: - ++failureCounter; - typeBuf = "xpass"; - break; - case QAbstractTestLogger::Pass: - typeBuf = "pass"; - break; - case QAbstractTestLogger::XFail: - typeBuf = "xfail"; - break; - case QAbstractTestLogger::Fail: - ++failureCounter; - typeBuf = "fail"; - break; - case QAbstractTestLogger::BlacklistedPass: - typeBuf = "bpass"; - break; - case QAbstractTestLogger::BlacklistedFail: - ++failureCounter; - typeBuf = "bfail"; - break; - case QAbstractTestLogger::BlacklistedXPass: - typeBuf = "bxpass"; - break; - case QAbstractTestLogger::BlacklistedXFail: - ++failureCounter; - typeBuf = "bxfail"; - break; - default: - typeBuf = "??????"; - break; - } - - if (type == QAbstractTestLogger::Fail || type == QAbstractTestLogger::XPass) { - QTestElement *failureElement = new QTestElement(QTest::LET_Failure); - failureElement->addAttribute(QTest::AI_Result, typeBuf); - if (file) - failureElement->addAttribute(QTest::AI_File, file); - else - failureElement->addAttribute(QTest::AI_File, ""); - qsnprintf(buf, sizeof(buf), "%i", line); - failureElement->addAttribute(QTest::AI_Line, buf); - failureElement->addAttribute(QTest::AI_Description, description); - addTag(failureElement); - currentLogElement->addLogElement(failureElement); - } - - /* - Only one result can be shown for the whole testfunction. - Check if we currently have a result, and if so, overwrite it - iff the new result is worse. - */ - QTestElementAttribute* resultAttr = - const_cast(currentLogElement->attribute(QTest::AI_Result)); - if (resultAttr) { - const char* oldResult = resultAttr->value(); - bool overwrite = false; - if (!strcmp(oldResult, "pass")) { - overwrite = true; - } - else if (!strcmp(oldResult, "bpass") || !strcmp(oldResult, "bxfail")) { - overwrite = (type == QAbstractTestLogger::XPass || type == QAbstractTestLogger::Fail) || (type == QAbstractTestLogger::XFail) - || (type == QAbstractTestLogger::BlacklistedFail) || (type == QAbstractTestLogger::BlacklistedXPass); - } - else if (!strcmp(oldResult, "bfail") || !strcmp(oldResult, "bxpass")) { - overwrite = (type == QAbstractTestLogger::XPass || type == QAbstractTestLogger::Fail) || (type == QAbstractTestLogger::XFail); - } - else if (!strcmp(oldResult, "xfail")) { - overwrite = (type == QAbstractTestLogger::XPass || type == QAbstractTestLogger::Fail); - } - else if (!strcmp(oldResult, "xpass")) { - overwrite = (type == QAbstractTestLogger::Fail); - } - if (overwrite) { - resultAttr->setPair(QTest::AI_Result, typeBuf); - } - } - else { - currentLogElement->addAttribute(QTest::AI_Result, typeBuf); - } - - if (file) - currentLogElement->addAttribute(QTest::AI_File, file); - else - currentLogElement->addAttribute(QTest::AI_File, ""); - - qsnprintf(buf, sizeof(buf), "%i", line); - currentLogElement->addAttribute(QTest::AI_Line, buf); - - /* - Since XFAIL does not add a failure to the testlog in xunitxml, 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); - } -} - -void QXunitTestLogger::addBenchmarkResult(const QBenchmarkResult &result) -{ - QTestElement *benchmarkElement = new QTestElement(QTest::LET_Benchmark); - - benchmarkElement->addAttribute( - QTest::AI_Metric, - QTest::benchmarkMetricName(result.metric)); - benchmarkElement->addAttribute(QTest::AI_Tag, result.context.tag.toUtf8().data()); - - const qreal valuePerIteration = qreal(result.value) / qreal(result.iterations); - benchmarkElement->addAttribute(QTest::AI_Value, QByteArray::number(valuePerIteration).constData()); - - char buf[100]; - qsnprintf(buf, sizeof(buf), "%i", result.iterations); - benchmarkElement->addAttribute(QTest::AI_Iterations, buf); - currentLogElement->addLogElement(benchmarkElement); -} - -void QXunitTestLogger::addTag(QTestElement* element) -{ - const char *tag = QTestResult::currentDataTag(); - const char *gtag = QTestResult::currentGlobalDataTag(); - const char *filler = (tag && gtag) ? ":" : ""; - if ((!tag || !tag[0]) && (!gtag || !gtag[0])) { - return; - } - - if (!tag) { - tag = ""; - } - if (!gtag) { - gtag = ""; - } - - QTestCharBuffer buf; - QTest::qt_asprintf(&buf, "%s%s%s", gtag, filler, tag); - element->addAttribute(QTest::AI_Tag, buf.constData()); -} - -void QXunitTestLogger::addMessage(MessageTypes type, const QString &message, const char *file, int line) -{ - QTestElement *errorElement = new QTestElement(QTest::LET_Error); - const char *typeBuf = nullptr; - - switch (type) { - case QAbstractTestLogger::Warn: - typeBuf = "warn"; - break; - case QAbstractTestLogger::QSystem: - typeBuf = "system"; - break; - case QAbstractTestLogger::QDebug: - typeBuf = "qdebug"; - break; - case QAbstractTestLogger::QInfo: - typeBuf = "qinfo"; - break; - case QAbstractTestLogger::QWarning: - typeBuf = "qwarn"; - break; - case QAbstractTestLogger::QFatal: - typeBuf = "qfatal"; - break; - case QAbstractTestLogger::Skip: - typeBuf = "skip"; - break; - case QAbstractTestLogger::Info: - typeBuf = "info"; - break; - default: - typeBuf = "??????"; - break; - } - - errorElement->addAttribute(QTest::AI_Type, typeBuf); - errorElement->addAttribute(QTest::AI_Description, message.toUtf8().constData()); - addTag(errorElement); - - if (file) - errorElement->addAttribute(QTest::AI_File, file); - else - errorElement->addAttribute(QTest::AI_File, ""); - - char buf[100]; - qsnprintf(buf, sizeof(buf), "%i", line); - errorElement->addAttribute(QTest::AI_Line, buf); - - currentLogElement->addLogElement(errorElement); - ++errorCounter; - - // Also add the message to the system error log (i.e. stderr), if one exists - if (errorLogElement) { - QTestElement *systemErrorElement = new QTestElement(QTest::LET_Error); - systemErrorElement->addAttribute(QTest::AI_Description, message.toUtf8().constData()); - errorLogElement->addLogElement(systemErrorElement); - } -} - -QT_END_NAMESPACE - diff --git a/src/testlib/qxunittestlogger_p.h b/src/testlib/qxunittestlogger_p.h deleted file mode 100644 index 518ba098f4..0000000000 --- a/src/testlib/qxunittestlogger_p.h +++ /dev/null @@ -1,94 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtTest module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QXUNITTESTLOGGER_P_H -#define QXUNITTESTLOGGER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -QT_BEGIN_NAMESPACE - -class QTestXunitStreamer; -class QTestElement; - -class QXunitTestLogger : public QAbstractTestLogger -{ - public: - QXunitTestLogger(const char *filename); - ~QXunitTestLogger(); - - void startLogging() override; - void stopLogging() override; - - void enterTestFunction(const char *function) override; - void leaveTestFunction() override; - - void addIncident(IncidentTypes type, const char *description, - const char *file = nullptr, int line = 0) override; - void addBenchmarkResult(const QBenchmarkResult &result) override; - void addTag(QTestElement* element); - - void addMessage(MessageTypes type, const QString &message, - const char *file = nullptr, int line = 0) override; - - private: - QTestElement *listOfTestcases = nullptr; - QTestElement *currentLogElement = nullptr; - QTestElement *errorLogElement = nullptr; - QTestXunitStreamer *logFormatter = nullptr; - - int testCounter = 0; - int failureCounter = 0; - int errorCounter = 0; -}; - -QT_END_NAMESPACE - -#endif // QXUNITTESTLOGGER_P_H diff --git a/src/testlib/testlib.pro b/src/testlib/testlib.pro index 530bc6b425..0a0547688a 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 -- cgit v1.2.3 From 8c6c4df3e83776d821f357582a717dbfbeb1c3ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Arve=20S=C3=A6ther?= Date: Tue, 4 Feb 2020 11:32:00 +0100 Subject: Maintain at least 500ms timestamp distance between each test function If we had one test function that just did tst_Mouse::f1() { QTest::mouseMove(w, QPoint(0,0)); } and another test function that did tst_Mouse::f2() { QTest::mouseMove(w, QPoint(500,500)); } their corresponding event timestamps were only 1 apart from each other. This meant that any code that tried to estimate the velocity of a mouse cursor would get a really high velocity estimate inside f2(). This would come as a surprise to most people. So to avoid this, we add a 500 ms timestamp delay between each test function call. In theory this could also prevent generating a mouseDoubleClickEvent when a pair of test functions containing a press-release sequence was run, but there is a separate pre-existing mechanism to handle that case. Change-Id: Icd4fc35853c09f080466d22411208c7b5c4174b5 Reviewed-by: Shawn Rutledge --- src/testlib/qtestcase.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/testlib') diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 6be623d7fc..6805d17b6e 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -296,6 +296,8 @@ namespace QTestPrivate namespace QTest { +extern Q_TESTLIB_EXPORT int lastMouseTimestamp; + class WatchDog; static QObject *currentTestObject = nullptr; @@ -1173,6 +1175,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(); -- cgit v1.2.3 From 1a58e78db0e41da0423a50ffa900a6d90e83a0d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 31 Jan 2020 18:19:14 +0100 Subject: testlib: Be more selective in choosing to show stacktraces on macOS The built in crash reporter on macOS will by default not show the dialog if the application is not one that will run in the foreground. Change-Id: I0020520ae2f14a0e2f84fdca1d80ec6fe1247ffd Reviewed-by: Simon Hausmann Reviewed-by: Timur Pocheptsov --- src/testlib/qtestcase.cpp | 15 +-------------- src/testlib/qtestutil_macos.mm | 28 ++++++++++++++++++++++++++++ src/testlib/qtestutil_macos_p.h | 1 + src/testlib/testlib.pro | 2 +- 4 files changed, 31 insertions(+), 15 deletions(-) (limited to 'src/testlib') diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 6805d17b6e..20c370dc38 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -180,20 +180,7 @@ static bool debuggerPresent() static bool hasSystemCrashReporter() { #if defined(Q_OS_MACOS) - CFStringRef crashReporterType = static_cast( - CFPreferencesCopyAppValue(CFSTR("DialogType"), CFSTR("com.apple.CrashReporter"))); - if (crashReporterType == nullptr) - return true; - - auto equals = [](CFStringRef str1, CFStringRef str2) -> bool { - return CFStringCompare(str1, str2, kCFCompareCaseInsensitive) == kCFCompareEqualTo; - }; - - const bool createsStackTrace = - !equals(crashReporterType, CFSTR("server")) && - !equals(crashReporterType, CFSTR("none")); - CFRelease(crashReporterType); - return createsStackTrace; + return QTestPrivate::macCrashReporterWillShowDialog(); #else return false; #endif 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 QT_BEGIN_NAMESPACE @@ -55,6 +57,32 @@ namespace QTestPrivate { [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"ApplePersistenceIgnoreState"]; } + bool macCrashReporterWillShowDialog() + { + auto dialogType = QCFType(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 0a0547688a..787df6b648 100644 --- a/src/testlib/testlib.pro +++ b/src/testlib/testlib.pro @@ -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) -- cgit v1.2.3 From 93b8cdc974e584d80dbaabf459610a555af25b81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Fri, 7 Feb 2020 12:43:40 +0100 Subject: testlib: Make testlogs enumerable and printable Change-Id: Id0ac30fe0a63757ef722634e6dd90806015467aa Reviewed-by: Simon Hausmann --- src/testlib/qtestlog.cpp | 2 ++ src/testlib/qtestlog_p.h | 4 ++++ 2 files changed, 6 insertions(+) (limited to 'src/testlib') diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp index 7e5f9182b8..7bd108ab00 100644 --- a/src/testlib/qtestlog.cpp +++ b/src/testlib/qtestlog.cpp @@ -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 959aef6968..ddaf14ed9b 100644 --- a/src/testlib/qtestlog_p.h +++ b/src/testlib/qtestlog_p.h @@ -57,6 +57,8 @@ #include #endif +#include + QT_BEGIN_NAMESPACE class QBenchmarkResult; @@ -65,6 +67,7 @@ class QTestData; class Q_TESTLIB_EXPORT QTestLog { + Q_GADGET public: QTestLog() = delete; ~QTestLog() = delete; @@ -79,6 +82,7 @@ public: , XCTest #endif }; + Q_ENUM(LogMode); static void enterTestFunction(const char* function); static void leaveTestFunction(); -- cgit v1.2.3