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