summaryrefslogtreecommitdiffstats
path: root/src/testlib
diff options
context:
space:
mode:
Diffstat (limited to 'src/testlib')
-rw-r--r--src/testlib/doc/src/qt-webpages.qdoc4
-rw-r--r--src/testlib/doc/src/qttest-best-practices.qdoc2
-rw-r--r--src/testlib/doc/src/qttestlib-manual.qdoc47
-rw-r--r--src/testlib/qabstractitemmodeltester.cpp17
-rw-r--r--src/testlib/qabstracttestlogger.cpp5
-rw-r--r--src/testlib/qabstracttestlogger_p.h10
-rw-r--r--src/testlib/qplaintestlogger.cpp27
-rw-r--r--src/testlib/qplaintestlogger_p.h7
-rw-r--r--src/testlib/qteamcitylogger.cpp5
-rw-r--r--src/testlib/qtestblacklist.cpp6
-rw-r--r--src/testlib/qtestcase.cpp90
-rw-r--r--src/testlib/qtestcase.h6
-rw-r--r--src/testlib/qtestcase.qdoc35
-rw-r--r--src/testlib/qtestdata.h8
-rw-r--r--src/testlib/qtestlog.cpp39
-rw-r--r--src/testlib/qtestlog_p.h2
-rw-r--r--src/testlib/qtestresult.cpp6
-rw-r--r--src/testlib/qtestutil_macos.mm7
-rw-r--r--src/testlib/selfcover.pri4
19 files changed, 244 insertions, 83 deletions
diff --git a/src/testlib/doc/src/qt-webpages.qdoc b/src/testlib/doc/src/qt-webpages.qdoc
index 976435e668..aa907f3685 100644
--- a/src/testlib/doc/src/qt-webpages.qdoc
+++ b/src/testlib/doc/src/qt-webpages.qdoc
@@ -30,8 +30,8 @@
*/
/*!
- \externalpage https://www.froglogic.com/coco/
- \title Froglogic Coco Code Coverage
+ \externalpage https://www.qt.io/product/quality-assurance/coco
+ \title Coco
*/
/*!
diff --git a/src/testlib/doc/src/qttest-best-practices.qdoc b/src/testlib/doc/src/qttest-best-practices.qdoc
index 7143e644fd..da03399668 100644
--- a/src/testlib/doc/src/qttest-best-practices.qdoc
+++ b/src/testlib/doc/src/qttest-best-practices.qdoc
@@ -184,7 +184,7 @@
\section2 Use Coverage Tools
- Use a coverage tool such as \l {Froglogic Coco Code Coverage} or \l {gcov}
+ Use a coverage tool such as \l {Coco} or \l {gcov}
to help write tests that cover as many statements, branches, and conditions
as possible in the function or class being tested. The earlier this is done
in the development cycle for a new feature, the easier it will be to catch
diff --git a/src/testlib/doc/src/qttestlib-manual.qdoc b/src/testlib/doc/src/qttestlib-manual.qdoc
index 8ff82ba28f..cae17c347b 100644
--- a/src/testlib/doc/src/qttestlib-manual.qdoc
+++ b/src/testlib/doc/src/qttestlib-manual.qdoc
@@ -134,6 +134,47 @@
For more examples, refer to the \l{Qt Test Tutorial}.
+ \section1 Increasing Test Function Timeout
+
+ QtTest limits the run-time of each test to catch infinite loops and similar
+ bugs. By default, any test function call will be interrupted after five
+ minutes. For data-driven tests, this applies to each call with a distinct
+ data-tag. This timeout can be configured by setting the \c QTEST_FUNCTION_TIMEOUT
+ environment variable to the maximum number of milliseconds that is acceptable
+ for a single call to take. If a test takes longer than the configured timeout,
+ it is interrupted, and \c qFatal() is called. As a result, the test aborts by
+ default, as if it had crashed.
+
+ To set \c QTEST_FUNCTION_TIMEOUT from the command line on Linux or macOS, enter:
+
+ \badcode
+ QTEST_FUNCTION_TIMEOUT=900000
+ export QTEST_FUNCTION_TIMEOUT
+ \endcode
+
+ On Windows:
+ \badcode
+ SET QTEST_FUNCTION_TIMEOUT=900000
+ \endcode
+
+ Then run the test inside this environment.
+
+ Alternatively, you can set the environment variable programmatically in the
+ test code itself, for example by calling, from the
+ \l{Creating a Test}{initMain()} special method of your test class:
+
+ \badcode
+ qputenv("QTEST_FUNCTION_TIMEOUT", "900000");
+ \endcode
+
+ To calculate a suitable value for the timeout, see how long the test usually
+ takes and decide how much longer it can take without that being a symptom of
+ some problem. Convert that longer time to milliseconds to get the timeout value.
+ For example, if you decide that a test that takes several minutes could
+ reasonably take up to twenty minutes, for example on a slow machine,
+ multiply \c{20 * 60 * 1000 = 1200000} and set the environment variable to
+ \c 1200000 instead of the \c 900000 above.
+
\if !defined(qtforpython)
\section1 Building a Test
@@ -154,6 +195,12 @@
All labeled targets will be run when \c {test} target is called on the
command line.
+ \note On Android, if you have one connected device or emulator, tests will
+ run on that device. If you have more than one device connected, set the
+ environment variable \c {ANDROID_DEVICE_SERIAL} to the
+ \l {Android: Query for devices}{ADB serial number} of the device that
+ you want to run tests on.
+
There are several other advantages with CMake. For example, the result of
a test run can be published on a web server using CDash with virtually no
effort.
diff --git a/src/testlib/qabstractitemmodeltester.cpp b/src/testlib/qabstractitemmodeltester.cpp
index 4c86d65e77..1cd18b98bb 100644
--- a/src/testlib/qabstractitemmodeltester.cpp
+++ b/src/testlib/qabstractitemmodeltester.cpp
@@ -438,7 +438,7 @@ void QAbstractItemModelTesterPrivate::parent()
// when asked for the parent of an invalid index.
MODELTESTER_VERIFY(!model->parent(QModelIndex()).isValid());
- if (!model->hasChildren())
+ if (model->rowCount() == 0 || model->columnCount() == 0)
return;
// Column 0 | Column 1 |
@@ -449,11 +449,12 @@ void QAbstractItemModelTesterPrivate::parent()
// Common error test #1, make sure that a top level index has a parent
// that is a invalid QModelIndex.
QModelIndex topIndex = model->index(0, 0, QModelIndex());
+ MODELTESTER_VERIFY(topIndex.isValid());
MODELTESTER_VERIFY(!model->parent(topIndex).isValid());
// Common error test #2, make sure that a second level index has a parent
// that is the first level index.
- if (model->hasChildren(topIndex)) {
+ if (model->rowCount(topIndex) > 0) {
QModelIndex childIndex = model->index(0, 0, topIndex);
MODELTESTER_VERIFY(childIndex.isValid());
MODELTESTER_COMPARE(model->parent(childIndex), topIndex);
@@ -465,7 +466,7 @@ void QAbstractItemModelTesterPrivate::parent()
if (model->hasIndex(0, 1)) {
QModelIndex topIndex1 = model->index(0, 1, QModelIndex());
MODELTESTER_VERIFY(topIndex1.isValid());
- if (model->hasChildren(topIndex) && model->hasChildren(topIndex1)) {
+ if (model->rowCount(topIndex) > 0 && model->rowCount(topIndex1) > 0) {
QModelIndex childIndex = model->index(0, 0, topIndex);
MODELTESTER_VERIFY(childIndex.isValid());
QModelIndex childIndex1 = model->index(0, 0, topIndex1);
@@ -569,7 +570,7 @@ void QAbstractItemModelTesterPrivate::checkChildren(const QModelIndex &parent, i
// recursively go down the children
if (model->hasChildren(index) && currentDepth < 10)
- checkChildren(index, ++currentDepth);
+ checkChildren(index, currentDepth + 1);
// make sure that after testing the children that the index doesn't change.
QModelIndex newerIndex = model->index(r, c, parent);
@@ -583,7 +584,7 @@ void QAbstractItemModelTesterPrivate::checkChildren(const QModelIndex &parent, i
*/
void QAbstractItemModelTesterPrivate::data()
{
- if (!model->hasChildren())
+ if (model->rowCount() == 0 || model->columnCount() == 0)
return;
MODELTESTER_VERIFY(model->index(0, 0).isValid());
@@ -719,12 +720,12 @@ void QAbstractItemModelTesterPrivate::rowsAboutToBeRemoved(const QModelIndex &pa
Changing c;
c.parent = parent;
c.oldSize = model->rowCount(parent);
- if (start > 0) {
+ if (start > 0 && model->columnCount(parent) > 0) {
const QModelIndex startIndex = model->index(start - 1, 0, parent);
MODELTESTER_VERIFY(startIndex.isValid());
c.last = model->data(startIndex);
}
- if (end < c.oldSize - 1) {
+ if (end < c.oldSize - 1 && model->columnCount(parent) > 0) {
const QModelIndex endIndex = model->index(end + 1, 0, parent);
MODELTESTER_VERIFY(endIndex.isValid());
c.next = model->data(endIndex);
@@ -844,3 +845,5 @@ bool QAbstractItemModelTesterPrivate::compare(const T1 &t1, const T2 &t2,
QT_END_NAMESPACE
+
+#include "moc_qabstractitemmodeltester.cpp"
diff --git a/src/testlib/qabstracttestlogger.cpp b/src/testlib/qabstracttestlogger.cpp
index ff05dd88c7..c7a0784da7 100644
--- a/src/testlib/qabstracttestlogger.cpp
+++ b/src/testlib/qabstracttestlogger.cpp
@@ -90,6 +90,11 @@ QAbstractTestLogger::~QAbstractTestLogger()
stream = nullptr;
}
+bool QAbstractTestLogger::isLoggingToStdout() const
+{
+ return stream == stdout;
+}
+
void QAbstractTestLogger::filterUnprintable(char *str) const
{
unsigned char *idx = reinterpret_cast<unsigned char *>(str);
diff --git a/src/testlib/qabstracttestlogger_p.h b/src/testlib/qabstracttestlogger_p.h
index e5a1404c16..06a39ee65a 100644
--- a/src/testlib/qabstracttestlogger_p.h
+++ b/src/testlib/qabstracttestlogger_p.h
@@ -51,7 +51,8 @@
// We mean it.
//
-#include <qglobal.h>
+#include <QtTest/qttestglobal.h>
+
#include <stdio.h>
#include <stdlib.h>
@@ -60,8 +61,9 @@ QT_BEGIN_NAMESPACE
class QBenchmarkResult;
class QTestData;
-class QAbstractTestLogger
+class Q_TESTLIB_EXPORT QAbstractTestLogger
{
+ Q_DISABLE_COPY_MOVE(QAbstractTestLogger)
public:
enum IncidentTypes {
Pass,
@@ -106,6 +108,8 @@ public:
virtual void addMessage(MessageTypes type, const QString &message,
const char *file = nullptr, int line = 0) = 0;
+ bool isLoggingToStdout() const;
+
void outputString(const char *msg);
protected:
@@ -182,7 +186,7 @@ namespace QTest
namespace QTestPrivate
{
enum IdentifierPart { TestObject = 0x1, TestFunction = 0x2, TestDataTag = 0x4, AllParts = 0xFFFF };
- void generateTestIdentifier(QTestCharBuffer *identifier, int parts = AllParts);
+ void Q_TESTLIB_EXPORT generateTestIdentifier(QTestCharBuffer *identifier, int parts = AllParts);
}
QT_END_NAMESPACE
diff --git a/src/testlib/qplaintestlogger.cpp b/src/testlib/qplaintestlogger.cpp
index 5d9283d8e5..851a284678 100644
--- a/src/testlib/qplaintestlogger.cpp
+++ b/src/testlib/qplaintestlogger.cpp
@@ -225,7 +225,8 @@ void QPlainTestLogger::outputMessage(const char *str)
outputString(str);
}
-void QPlainTestLogger::printMessage(const char *type, const char *msg, const char *file, int line)
+void QPlainTestLogger::printMessage(MessageSource source, const char *type, const char *msg,
+ const char *file, int line)
{
QTEST_ASSERT(type);
QTEST_ASSERT(msg);
@@ -233,13 +234,23 @@ void QPlainTestLogger::printMessage(const char *type, const char *msg, const cha
QTestCharBuffer messagePrefix;
QTestCharBuffer failureLocation;
- if (file) {
#ifdef Q_OS_WIN
-#define FAILURE_LOCATION_STR "\n%s(%d) : failure location"
+ constexpr const char *INCIDENT_LOCATION_STR = "\n%s(%d) : failure location";
+ constexpr const char *OTHER_LOCATION_STR = "\n%s(%d) : message location";
#else
-#define FAILURE_LOCATION_STR "\n Loc: [%s(%d)]"
+ constexpr const char *INCIDENT_LOCATION_STR = "\n Loc: [%s(%d)]";
+ constexpr const char *OTHER_LOCATION_STR = INCIDENT_LOCATION_STR;
#endif
- QTest::qt_asprintf(&failureLocation, FAILURE_LOCATION_STR, file, line);
+
+ if (file) {
+ switch (source) {
+ case MessageSource::Incident:
+ QTest::qt_asprintf(&failureLocation, INCIDENT_LOCATION_STR, file, line);
+ break;
+ case MessageSource::Other:
+ QTest::qt_asprintf(&failureLocation, OTHER_LOCATION_STR, file, line);
+ break;
+ }
}
const char *msgFiller = msg[0] ? " " : "";
@@ -360,7 +371,7 @@ void QPlainTestLogger::stopLogging()
void QPlainTestLogger::enterTestFunction(const char * /*function*/)
{
if (QTestLog::verboseLevel() >= 1)
- printMessage(QTest::messageType2String(Info), "entering");
+ printMessage(MessageSource::Other, QTest::messageType2String(Info), "entering");
}
void QPlainTestLogger::leaveTestFunction()
@@ -375,7 +386,7 @@ void QPlainTestLogger::addIncident(IncidentTypes type, const char *description,
&& QTestLog::verboseLevel() < 0)
return;
- printMessage(QTest::incidentType2String(type), description, file, line);
+ printMessage(MessageSource::Incident, QTest::incidentType2String(type), description, file, line);
}
void QPlainTestLogger::addBenchmarkResult(const QBenchmarkResult &result)
@@ -399,7 +410,7 @@ void QPlainTestLogger::addMessage(MessageTypes type, const QString &message,
if (type != QAbstractTestLogger::QFatal && QTestLog::verboseLevel() < 0)
return;
- printMessage(QTest::messageType2String(type), qPrintable(message), file, line);
+ printMessage(MessageSource::Other, QTest::messageType2String(type), qPrintable(message), file, line);
}
QT_END_NAMESPACE
diff --git a/src/testlib/qplaintestlogger_p.h b/src/testlib/qplaintestlogger_p.h
index 80ef4864c1..1a19b99442 100644
--- a/src/testlib/qplaintestlogger_p.h
+++ b/src/testlib/qplaintestlogger_p.h
@@ -78,7 +78,12 @@ public:
const char *file = nullptr, int line = 0) override;
private:
- void printMessage(const char *type, const char *msg, const char *file = nullptr, int line = 0);
+ enum class MessageSource {
+ Incident,
+ Other,
+ };
+ void printMessage(MessageSource source, const char *type, const char *msg,
+ const char *file = nullptr, int line = 0);
void outputMessage(const char *str);
void printBenchmarkResult(const QBenchmarkResult &result);
};
diff --git a/src/testlib/qteamcitylogger.cpp b/src/testlib/qteamcitylogger.cpp
index 8a77143454..1afec2943a 100644
--- a/src/testlib/qteamcitylogger.cpp
+++ b/src/testlib/qteamcitylogger.cpp
@@ -268,9 +268,8 @@ void QTeamCityLogger::addPendingMessage(const char *type, const QString &msg, co
if (file) {
pendMessage += QString(QLatin1String("%1 |[Loc: %2(%3)|]: %4"))
- .arg(QString::fromUtf8(type), QString::fromUtf8(file))
- .arg(line)
- .arg(msg);
+ .arg(QString::fromUtf8(type), QString::fromUtf8(file),
+ QString::number(line), msg);
}
else {
diff --git a/src/testlib/qtestblacklist.cpp b/src/testlib/qtestblacklist.cpp
index ac4793eebf..850f1d779f 100644
--- a/src/testlib/qtestblacklist.cpp
+++ b/src/testlib/qtestblacklist.cpp
@@ -46,6 +46,7 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qvariant.h>
#include <QtCore/QSysInfo>
+#include <QtCore/QOperatingSystemVersion>
#include <set>
@@ -208,6 +209,11 @@ static QSet<QByteArray> activeConditions()
if (!distributionName.isEmpty()) {
if (result.find(distributionName) == result.end())
result.insert(distributionName);
+ if (distributionName == "macos" || distributionName == "osx") {
+ const auto version = QOperatingSystemVersion::current();
+ if (version.majorVersion() >= 11)
+ distributionRelease = QByteArray::number(version.majorVersion());
+ }
if (!distributionRelease.isEmpty()) {
QByteArray versioned = distributionName + "-" + distributionRelease;
if (result.find(versioned) == result.end())
diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp
index 74507c11e1..2c023a8ee1 100644
--- a/src/testlib/qtestcase.cpp
+++ b/src/testlib/qtestcase.cpp
@@ -82,10 +82,10 @@
#include <QtTest/private/qappletestlogger_p.h>
#endif
+#include <atomic>
#include <cmath>
#include <numeric>
#include <algorithm>
-#include <condition_variable>
#include <mutex>
#include <chrono>
@@ -212,13 +212,13 @@ static void stackTrace()
if (debuggerPresent() || hasSystemCrashReporter())
return;
-#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
+#if defined(Q_OS_LINUX) || (defined(Q_OS_MACOS) && !defined(Q_PROCESSOR_ARM_64))
const int msecsFunctionTime = qRound(QTestLog::msecsFunctionTime());
const int msecsTotalTime = qRound(QTestLog::msecsTotalTime());
fprintf(stderr, "\n=== Received signal at function time: %dms, total time: %dms, dumping stack ===\n",
msecsFunctionTime, msecsTotalTime);
-#endif
-#ifdef Q_OS_LINUX
+
+# ifdef Q_OS_LINUX
char cmd[512];
qsnprintf(cmd, 512, "gdb --pid %d 2>/dev/null <<EOF\n"
"set prompt\n"
@@ -231,7 +231,7 @@ static void stackTrace()
if (system(cmd) == -1)
fprintf(stderr, "calling gdb failed\n");
fprintf(stderr, "=== End of stack trace ===\n");
-#elif defined(Q_OS_MACOS)
+# elif defined(Q_OS_MACOS)
char cmd[512];
qsnprintf(cmd, 512, "lldb -p %d 2>/dev/null <<EOF\n"
"bt all\n"
@@ -241,6 +241,8 @@ static void stackTrace()
if (system(cmd) == -1)
fprintf(stderr, "calling lldb failed\n");
fprintf(stderr, "=== End of stack trace ===\n");
+# endif
+
#endif
}
@@ -1005,16 +1007,30 @@ void TestMethods::invokeTestOnData(int index) const
class WatchDog : public QThread
{
- enum Expectation {
+ enum Expectation : std::size_t {
+ // bits 0..1: state
ThreadStart,
TestFunctionStart,
TestFunctionEnd,
ThreadEnd,
+ ExpectationMask = ThreadStart | TestFunctionStart | TestFunctionEnd | ThreadEnd,
+
+ // bits 2..: generation
};
+ Q_STATIC_ASSERT(size_t(ExpectationMask) == 0x3);
+ // static constexpr size_t GenerationShift = 2; // C++17-ism, so inline in combine and generation.
+
+ static constexpr Expectation state(Expectation e) noexcept
+ { return static_cast<Expectation>(e & ExpectationMask); }
+ static constexpr size_t generation(Expectation e) noexcept
+ { return e >> 2; }
+ static constexpr Expectation combine(Expectation e, size_t gen) noexcept
+ { return static_cast<Expectation>(e | (gen << 2)); }
- bool waitFor(std::unique_lock<QtPrivate::mutex> &m, Expectation e) {
- auto expectationChanged = [this, e] { return expecting != e; };
- switch (e) {
+ bool waitFor(std::unique_lock<QtPrivate::mutex> &m, Expectation e)
+ {
+ auto expectationChanged = [this, e] { return expecting.load(std::memory_order_relaxed) != e; };
+ switch (state(e)) {
case TestFunctionEnd:
return waitCondition.wait_for(m, defaultTimeout(), expectationChanged);
case ThreadStart:
@@ -1027,48 +1043,59 @@ class WatchDog : public QThread
return false;
}
+ void setExpectation(Expectation e)
+ {
+ Q_ASSERT(generation(e) == 0); // no embedded generation allowed
+ const auto locker = qt_scoped_lock(mutex);
+ auto cur = expecting.load(std::memory_order_relaxed);
+ auto gen = generation(cur);
+ if (e == TestFunctionStart)
+ ++gen;
+ e = combine(e, gen);
+ expecting.store(e, std::memory_order_relaxed);
+ waitCondition.notify_all();
+ }
+
public:
WatchDog()
{
auto locker = qt_unique_lock(mutex);
- expecting = ThreadStart;
+ expecting.store(ThreadStart, std::memory_order_relaxed);
start();
waitFor(locker, ThreadStart);
}
- ~WatchDog() {
- {
- const auto locker = qt_scoped_lock(mutex);
- expecting = ThreadEnd;
- waitCondition.notify_all();
- }
+
+ ~WatchDog()
+ {
+ setExpectation(ThreadEnd);
wait();
}
- void beginTest() {
- const auto locker = qt_scoped_lock(mutex);
- expecting = TestFunctionEnd;
- waitCondition.notify_all();
+ void beginTest()
+ {
+ setExpectation(TestFunctionEnd);
}
- void testFinished() {
- const auto locker = qt_scoped_lock(mutex);
- expecting = TestFunctionStart;
- waitCondition.notify_all();
+ void testFinished()
+ {
+ setExpectation(TestFunctionStart);
}
- void run() override {
+ void run() override
+ {
auto locker = qt_unique_lock(mutex);
- expecting = TestFunctionStart;
+ expecting.store(TestFunctionStart, std::memory_order_release);
waitCondition.notify_all();
while (true) {
- switch (expecting) {
+ Expectation e = expecting.load(std::memory_order_acquire);
+ switch (state(e)) {
case ThreadEnd:
return;
case ThreadStart:
Q_UNREACHABLE();
case TestFunctionStart:
case TestFunctionEnd:
- if (Q_UNLIKELY(!waitFor(locker, expecting))) {
+ if (Q_UNLIKELY(!waitFor(locker, e))) {
stackTrace();
qFatal("Test function timed out");
}
@@ -1079,7 +1106,7 @@ public:
private:
QtPrivate::mutex mutex;
QtPrivate::condition_variable waitCondition;
- Expectation expecting;
+ std::atomic<Expectation> expecting;
};
#else // !QT_CONFIG(thread)
@@ -2798,6 +2825,11 @@ char *QTest::toString(const char *str)
/*! \internal
*/
+char *QTest::toString(const volatile void *p) // Use volatile to match compare_ptr_helper()
+{
+ return QTest::toString(const_cast<const void *>(p));
+}
+
char *QTest::toString(const void *p)
{
char *msg = new char[128];
diff --git a/src/testlib/qtestcase.h b/src/testlib/qtestcase.h
index e1518708e8..6c9d23f649 100644
--- a/src/testlib/qtestcase.h
+++ b/src/testlib/qtestcase.h
@@ -101,6 +101,7 @@ do {\
" but no exception caught", __FILE__, __LINE__);\
return;\
} QT_CATCH (const exceptiontype &) {\
+ /* success */\
}\
} QT_CATCH (const std::exception &e) {\
QByteArray msg = QByteArray() + "Expected exception of type " #exceptiontype \
@@ -110,7 +111,7 @@ do {\
} QT_CATCH (...) {\
QTest::qFail("Expected exception of type " #exceptiontype " to be thrown" \
" but unknown exception caught", __FILE__, __LINE__);\
- return;\
+ QT_RETHROW;\
}\
} while (false)
@@ -283,7 +284,8 @@ namespace QTest
Q_TESTLIB_EXPORT char *toPrettyCString(const char *unicode, int length);
Q_TESTLIB_EXPORT char *toPrettyUnicode(QStringView string);
Q_TESTLIB_EXPORT char *toString(const char *);
- Q_TESTLIB_EXPORT char *toString(const void *);
+ Q_TESTLIB_EXPORT char *toString(const volatile void *);
+ Q_TESTLIB_EXPORT char *toString(const void *); // ### FIXME: Qt 7: Remove
Q_TESTLIB_EXPORT void qInit(QObject *testObject, int argc = 0, char **argv = nullptr);
Q_TESTLIB_EXPORT int qRun();
diff --git a/src/testlib/qtestcase.qdoc b/src/testlib/qtestcase.qdoc
index 72f8cdaf8c..dd5ec762a7 100644
--- a/src/testlib/qtestcase.qdoc
+++ b/src/testlib/qtestcase.qdoc
@@ -149,15 +149,24 @@
\relates QTest
- The QVERIFY_EXCEPTION_THROWN macro executes an \a expression and tries
- to catch an exception thrown from the \a expression. If the \a expression
- throws an exception and its type is the same as \a exceptiontype
- or \a exceptiontype is substitutable with the type of thrown exception
- (i.e. usually the type of thrown exception is publicly derived
- from \a exceptiontype) then execution will be continued. If not-substitutable
- type of exception is thrown or the \a expression doesn't throw an exception
- at all, then a failure will be recorded in the test log and
- the test won't be executed further.
+ The QVERIFY_EXCEPTION_THROWN macro executes \a expression
+ and tries to catch an exception thrown from \a expression.
+
+ There are several possible outcomes:
+
+ \list
+ \li If \a expression throws an exception that is either the same as
+ \a exceptiontype or derived from \a exceptiontype, then execution will continue.
+
+ \li Otherwise, if \a expression throws no exception, or the
+ exception thrown derives from \c{std::exception}, then a failure
+ will be recorded in the test log and the macro returns early
+ (from enclosing function).
+
+ \li If the thrown exception derives neither from \c{std::exception} nor from
+ \a exceptiontype, a failure will be recorded in the test log, and the exception is
+ re-thrown. This avoids problems with e.g. pthread cancellation exceptions.
+ \endlist
\note This macro can only be used in a test function that is invoked
by the test framework.
@@ -1258,7 +1267,7 @@
should typically use createTouchDevice() to initialize a QTouchDevice
member variable in your test case class, and use the same instance for all tests.
- \sa QTest::QTouchEventSequence
+ \sa QTest::QTouchEventSequence, touchEvent()
*/
/*!
@@ -1394,6 +1403,9 @@
QTouchEventSequence is called (ie when the object returned runs out of scope), unless
\a autoCommit is set to false. When \a autoCommit is false, commit() has to be called
manually.
+
+ \l createTouchDevice() can be called to create a test touch device for use with this
+ function.
*/
/*!
@@ -1410,6 +1422,9 @@
QTouchEventSequence is called (ie when the object returned runs out of scope), unless
\a autoCommit is set to false. When \a autoCommit is false, commit() has to be called
manually.
+
+ \l createTouchDevice() can be called to create a test touch device for use with this
+ function.
*/
// Internals of qtestmouse.h:
diff --git a/src/testlib/qtestdata.h b/src/testlib/qtestdata.h
index cf10fed8f3..ed81a9de6e 100644
--- a/src/testlib/qtestdata.h
+++ b/src/testlib/qtestdata.h
@@ -85,6 +85,14 @@ inline QTestData &operator<<(QTestData &data, const char * value)
return data;
}
+#ifdef __cpp_char8_t
+Q_WEAK_OVERLOAD
+inline QTestData &operator<<(QTestData &data, const char8_t *value)
+{
+ return data << reinterpret_cast<const char *>(value);
+}
+#endif
+
#ifdef QT_USE_QSTRINGBUILDER
template<typename A, typename B>
inline QTestData &operator<<(QTestData &data, const QStringBuilder<A, B> &value)
diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp
index be50176a08..8917f0dc0e 100644
--- a/src/testlib/qtestlog.cpp
+++ b/src/testlib/qtestlog.cpp
@@ -99,7 +99,7 @@ static void saveCoverageTool(const char * appname, bool testfailed, bool install
static QElapsedTimer elapsedFunctionTime;
static QElapsedTimer elapsedTotalTime;
-#define FOREACH_TEST_LOGGER for (QAbstractTestLogger *logger : QTest::loggers)
+#define FOREACH_TEST_LOGGER for (QAbstractTestLogger *logger : *QTest::loggers())
namespace QTest {
@@ -168,8 +168,7 @@ namespace QTest {
static IgnoreResultList *ignoreResultList = nullptr;
- static QVector<QAbstractTestLogger*> loggers;
- static bool loggerUsingStdout = false;
+ Q_GLOBAL_STATIC(QVector<QAbstractTestLogger *>, loggers)
static int verbosity = 0;
static int maxWarnings = 2002;
@@ -339,6 +338,8 @@ void QTestLog::addXFail(const char *msg, const char *file, int line)
QTEST_ASSERT(msg);
QTEST_ASSERT(file);
+ // Will be counted in addPass() if we get there.
+
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::XFail, msg, file, line);
}
@@ -391,7 +392,7 @@ void QTestLog::addBXFail(const char *msg, const char *file, int line)
QTEST_ASSERT(msg);
QTEST_ASSERT(file);
- ++QTest::blacklists;
+ // Will be counted in addBPass() if we get there.
FOREACH_TEST_LOGGER
logger->addIncident(QAbstractTestLogger::BlacklistedXFail, msg, file, line);
@@ -430,8 +431,7 @@ void QTestLog::stopLogging()
logger->stopLogging();
delete logger;
}
- QTest::loggers.clear();
- QTest::loggerUsingStdout = false;
+ QTest::loggers()->clear();
saveCoverageTool(QTestResult::currentAppName(), failCount() != 0, QTestLog::installedTestCoverage());
}
@@ -439,8 +439,6 @@ void QTestLog::addLogger(LogMode mode, const char *filename)
{
if (filename && strcmp(filename, "-") == 0)
filename = nullptr;
- if (!filename)
- QTest::loggerUsingStdout = true;
QAbstractTestLogger *logger = nullptr;
switch (mode) {
@@ -478,17 +476,36 @@ void QTestLog::addLogger(LogMode mode, const char *filename)
}
QTEST_ASSERT(logger);
- QTest::loggers.append(logger);
+ addLogger(logger);
+}
+
+/*!
+ \internal
+
+ Adds a new logger to the set of loggers that will be used
+ to report incidents and messages during testing.
+
+ The function takes ownership of the logger.
+*/
+void QTestLog::addLogger(QAbstractTestLogger *logger)
+{
+ QTEST_ASSERT(logger);
+ QTest::loggers()->append(logger);
}
int QTestLog::loggerCount()
{
- return QTest::loggers.size();
+ return QTest::loggers()->size();
}
bool QTestLog::loggerUsingStdout()
{
- return QTest::loggerUsingStdout;
+ FOREACH_TEST_LOGGER {
+ if (logger->isLoggingToStdout())
+ return true;
+ }
+
+ return false;
}
void QTestLog::warn(const char *msg, const char *file, int line)
diff --git a/src/testlib/qtestlog_p.h b/src/testlib/qtestlog_p.h
index ddaf14ed9b..bdb22acbd3 100644
--- a/src/testlib/qtestlog_p.h
+++ b/src/testlib/qtestlog_p.h
@@ -64,6 +64,7 @@ QT_BEGIN_NAMESPACE
class QBenchmarkResult;
class QRegularExpression;
class QTestData;
+class QAbstractTestLogger;
class Q_TESTLIB_EXPORT QTestLog
{
@@ -115,6 +116,7 @@ public:
static void stopLogging();
static void addLogger(LogMode mode, const char *filename);
+ static void addLogger(QAbstractTestLogger *logger);
static int loggerCount();
static bool loggerUsingStdout();
diff --git a/src/testlib/qtestresult.cpp b/src/testlib/qtestresult.cpp
index 88028aac6e..2e89930776 100644
--- a/src/testlib/qtestresult.cpp
+++ b/src/testlib/qtestresult.cpp
@@ -251,7 +251,8 @@ bool QTestResult::verify(bool statement, const char *statementStr,
{
QTEST_ASSERT(statementStr);
- char msg[1024] = {'\0'};
+ char msg[1024];
+ msg[0] = '\0';
if (QTestLog::verboseLevel() >= 2) {
qsnprintf(msg, 1024, "QVERIFY(%s)", statementStr);
@@ -309,7 +310,8 @@ static bool compareHelper(bool success, const char *failureMsg,
bool hasValues = true)
{
const size_t maxMsgLen = 1024;
- char msg[maxMsgLen] = {'\0'};
+ char msg[maxMsgLen];
+ msg[0] = '\0';
QTEST_ASSERT(expected);
QTEST_ASSERT(actual);
diff --git a/src/testlib/qtestutil_macos.mm b/src/testlib/qtestutil_macos.mm
index 880cd0f91f..e6638e5cb8 100644
--- a/src/testlib/qtestutil_macos.mm
+++ b/src/testlib/qtestutil_macos.mm
@@ -53,8 +53,11 @@ namespace QTestPrivate {
to start with a clean slate and prevents the "previous restore failed"
dialog from showing if there was a test crash.
*/
- void disableWindowRestore() {
- [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"ApplePersistenceIgnoreState"];
+ void disableWindowRestore()
+ {
+ [NSUserDefaults.standardUserDefaults registerDefaults:@{
+ @"ApplePersistenceIgnoreState" : @YES
+ }];
}
bool macCrashReporterWillShowDialog()
diff --git a/src/testlib/selfcover.pri b/src/testlib/selfcover.pri
index 7de50ba6e6..eb9d5ac53c 100644
--- a/src/testlib/selfcover.pri
+++ b/src/testlib/selfcover.pri
@@ -1,5 +1,5 @@
# Configuration for testlib and its tests, to instrument with
-# FrogLogic's Squish CoCo (cf. testcocoon.prf, which handles similar
+# Coco (cf. testcocoon.prf, which handles similar
# for general code; but testlib needs special handling).
# Only for use when feature testlib_selfcover is enabled:
@@ -13,7 +13,7 @@ COVERAGE_OPTIONS += --cs-include-file-abs-wildcard=*/src/testlib/*
COVERAGE_OPTIONS += --cs-mcc # enable Multiple Condition Coverage
COVERAGE_OPTIONS += --cs-mcdc # enable Multiple Condition / Decision Coverage
# (recommended for ISO 26262 ASIL A, B and C -- highly recommended for ASIL D)
-# https://doc.froglogic.com/squish-coco/4.1/codecoverage.html#sec%3Amcdc
+# https://doc.qt.io/coco/code-coverage-analysis.html#mc-dc
QMAKE_CFLAGS += $$COVERAGE_OPTIONS
QMAKE_CXXFLAGS += $$COVERAGE_OPTIONS