diff options
Diffstat (limited to 'src/testlib')
-rw-r--r-- | src/testlib/configure.json | 6 | ||||
-rw-r--r-- | src/testlib/doc/qttestlib.qdocconf | 2 | ||||
-rw-r--r-- | src/testlib/doc/src/qttest-index.qdoc | 10 | ||||
-rw-r--r-- | src/testlib/qabstractitemmodeltester.h | 4 | ||||
-rw-r--r-- | src/testlib/qbenchmarkvalgrind.cpp | 18 | ||||
-rw-r--r-- | src/testlib/qsignalspy.qdoc | 4 | ||||
-rw-r--r-- | src/testlib/qtest.h | 58 | ||||
-rw-r--r-- | src/testlib/qtestblacklist.cpp | 42 | ||||
-rw-r--r-- | src/testlib/qtestblacklist_p.h | 1 | ||||
-rw-r--r-- | src/testlib/qtestcase.cpp | 189 | ||||
-rw-r--r-- | src/testlib/qtestcase.h | 17 | ||||
-rw-r--r-- | src/testlib/qtestcorelist_p.h | 17 | ||||
-rw-r--r-- | src/testlib/qtestlog.cpp | 5 | ||||
-rw-r--r-- | src/testlib/selfcover.pri | 28 | ||||
-rw-r--r-- | src/testlib/testlib.pro | 1 |
15 files changed, 225 insertions, 177 deletions
diff --git a/src/testlib/configure.json b/src/testlib/configure.json index c464037205..df6132cdc2 100644 --- a/src/testlib/configure.json +++ b/src/testlib/configure.json @@ -5,6 +5,12 @@ ], "features": { + "testlib_selfcover": { + "label": "Coverage testing of testlib itself", + "purpose": "Gauges how thoroughly testlib's selftest exercises testlib's code", + "autoDetect": false, + "output": [ "publicFeature" ] + }, "itemmodeltester": { "label": "Tester for item models", "purpose": "Provides a utility to test item models.", diff --git a/src/testlib/doc/qttestlib.qdocconf b/src/testlib/doc/qttestlib.qdocconf index 93a5ab393c..5fdf6d9415 100644 --- a/src/testlib/doc/qttestlib.qdocconf +++ b/src/testlib/doc/qttestlib.qdocconf @@ -27,7 +27,7 @@ qhp.QtTestLib.subprojects.classes.sortPages = true tagfile = ../../../doc/qttestlib/qttestlib.tags -depends += qtcore qtdoc qtwidgets qtgui qmake qtquick +depends += qtcore qtdoc qtwidgets qtgui qmake qtqmltest headerdirs += .. diff --git a/src/testlib/doc/src/qttest-index.qdoc b/src/testlib/doc/src/qttest-index.qdoc index 6e4f954034..23be46b431 100644 --- a/src/testlib/doc/src/qttest-index.qdoc +++ b/src/testlib/doc/src/qttest-index.qdoc @@ -75,11 +75,15 @@ \section1 Reference - These are links to the API reference materials. + \list + \li \l{Qt Test C++ Classes} + \endlist + + The \l {Qt Quick Test} module enables unit testing Qt Quick applications. \list - \li \l{Qt Test C++ Classes}{C++ Classes} - \li \l{Qt Quick Test QML Types}{QML Types} + \li \l{Qt Quick Test QML Types} + \li \l{Qt Quick Test C++ API} \endlist */ diff --git a/src/testlib/qabstractitemmodeltester.h b/src/testlib/qabstractitemmodeltester.h index 757074c6ae..57b8f283bc 100644 --- a/src/testlib/qabstractitemmodeltester.h +++ b/src/testlib/qabstractitemmodeltester.h @@ -123,11 +123,11 @@ do { \ MODELTESTER_VERIFY(variant.canConvert<QFont>()); // General Purpose roles that should return a QColor or a QBrush - variant = model->data(model->index(0, 0), Qt::BackgroundColorRole); + variant = model->data(model->index(0, 0), Qt::BackgroundRole); if (variant.isValid()) MODELTESTER_VERIFY(variant.canConvert<QColor>() || variant.canConvert<QBrush>()); - variant = model->data(model->index(0, 0), Qt::TextColorRole); + variant = model->data(model->index(0, 0), Qt::ForegroundRole); if (variant.isValid()) MODELTESTER_VERIFY(variant.canConvert<QColor>() || variant.canConvert<QBrush>()); diff --git a/src/testlib/qbenchmarkvalgrind.cpp b/src/testlib/qbenchmarkvalgrind.cpp index 1de149258d..7d24eb8293 100644 --- a/src/testlib/qbenchmarkvalgrind.cpp +++ b/src/testlib/qbenchmarkvalgrind.cpp @@ -46,6 +46,7 @@ #include <QtCore/qcoreapplication.h> #include <QtCore/qprocess.h> #include <QtCore/qdir.h> +#include <QtCore/qregularexpression.h> #include <QtCore/qset.h> #include <QtTest/private/callgrind_p.h> @@ -90,13 +91,13 @@ qint64 QBenchmarkValgrindUtils::extractResult(const QString &fileName) qint64 val = -1; bool valSeen = false; - QRegExp rxValue(QLatin1String("^summary: (\\d+)")); + QRegularExpression rxValue(QLatin1String("^summary: (\\d+)")); while (!file.atEnd()) { const QString line(QLatin1String(file.readLine())); - if (rxValue.indexIn(line) != -1) { - Q_ASSERT(rxValue.captureCount() == 1); + QRegularExpressionMatch match = rxValue.match(line); + if (match.hasMatch()) { bool ok; - val = rxValue.cap(1).toLongLong(&ok); + val = match.captured(1).toLongLong(&ok); Q_ASSERT(ok); valSeen = true; break; @@ -120,13 +121,12 @@ QString QBenchmarkValgrindUtils::getNewestFileName() int hiSuffix = -1; QFileInfo lastFileInfo; const QString pattern = QString::fromLatin1("%1.(\\d+)").arg(base); - QRegExp rx(pattern); + QRegularExpression rx(pattern); for (const QFileInfo &fileInfo : fiList) { - const int index = rx.indexIn(fileInfo.fileName()); - Q_ASSERT(index == 0); - Q_UNUSED(index); + QRegularExpressionMatch match = rx.match(fileInfo.fileName()); + Q_ASSERT(match.hasMatch()); bool ok; - const int suffix = rx.cap(1).toInt(&ok); + const int suffix = match.captured(1).toInt(&ok); Q_ASSERT(ok); Q_ASSERT(suffix >= 0); if (suffix > hiSuffix) { diff --git a/src/testlib/qsignalspy.qdoc b/src/testlib/qsignalspy.qdoc index 77affc9a4b..3352307d69 100644 --- a/src/testlib/qsignalspy.qdoc +++ b/src/testlib/qsignalspy.qdoc @@ -63,7 +63,7 @@ Constructs a new QSignalSpy that listens for emissions of the \a signal from the QObject \a object. If QSignalSpy is not able to listen for a - valid signal (for example, because \a object is null or \a signal does + valid signal (for example, because \a object is \nullptr or \a signal does not denote a valid signal of \a object), an explanatory warning message will be output using qWarning() and subsequent calls to \c isValid() will return false. @@ -77,7 +77,7 @@ Constructs a new QSignalSpy that listens for emissions of the \a signal from the QObject \a object. If QSignalSpy is not able to listen for a - valid signal (for example, because \a object is null or \a signal does + valid signal (for example, because \a object is \nullptr or \a signal does not denote a valid signal of \a object), an explanatory warning message will be output using qWarning() and subsequent calls to \c isValid() will return false. diff --git a/src/testlib/qtest.h b/src/testlib/qtest.h index 28b62129b6..ebd94939ce 100644 --- a/src/testlib/qtest.h +++ b/src/testlib/qtest.h @@ -51,6 +51,7 @@ #include <QtCore/qstringlist.h> #include <QtCore/qcborcommon.h> #include <QtCore/qdatetime.h> +#include <QtCore/qabstractitemmodel.h> #include <QtCore/qobject.h> #include <QtCore/qvariant.h> #include <QtCore/qurl.h> @@ -128,6 +129,13 @@ template<> inline char *toString(const QChar &c) return qstrdup(qPrintable(QString::fromLatin1("QChar: '%1' (0x%2)").arg(c).arg(QString::number(static_cast<int>(c.unicode()), 16)))); } +template<> inline char *toString(const QModelIndex &idx) +{ + char msg[128]; + qsnprintf(msg, sizeof(msg), "QModelIndex(%d,%d,%p,%p)", idx.row(), idx.column(), idx.internalPointer(), idx.model()); + return qstrdup(msg); +} + template<> inline char *toString(const QPoint &p) { char msg[128] = {'\0'}; @@ -366,27 +374,41 @@ QT_END_NAMESPACE # define QTEST_SET_MAIN_SOURCE_PATH QTest::setMainSourcePath(__FILE__); #endif +// Hooks for coverage-testing of QTestLib itself: +#if QT_CONFIG(testlib_selfcover) && defined(__COVERAGESCANNER__) +struct QtCoverageScanner +{ + QtCoverageScanner(const char *name) + { + __coveragescanner_clear(); + __coveragescanner_testname(name); + } + ~QtCoverageScanner() + { + __coveragescanner_save(); + __coveragescanner_testname(""); + } +}; +#define TESTLIB_SELFCOVERAGE_START(name) QtCoverageScanner _qtCoverageScanner(name); +#else +#define TESTLIB_SELFCOVERAGE_START(name) +#endif + #define QTEST_APPLESS_MAIN(TestObject) \ int main(int argc, char *argv[]) \ { \ + TESTLIB_SELFCOVERAGE_START(TestObject) \ TestObject tc; \ QTEST_SET_MAIN_SOURCE_PATH \ return QTest::qExec(&tc, argc, argv); \ } #include <QtTest/qtestsystem.h> -#include <set> - -#ifndef QT_NO_OPENGL -# define QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS \ - extern Q_TESTLIB_EXPORT std::set<QByteArray> *(*qgpu_features_ptr)(const QString &); \ - extern Q_GUI_EXPORT std::set<QByteArray> *qgpu_features(const QString &); -# define QTEST_ADD_GPU_BLACKLIST_SUPPORT \ - qgpu_features_ptr = qgpu_features; -#else -# define QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS -# define QTEST_ADD_GPU_BLACKLIST_SUPPORT -#endif + +// Two backwards-compatibility defines for an obsolete feature: +#define QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS +#define QTEST_ADD_GPU_BLACKLIST_SUPPORT +// ### Qt 6: fully remove these. #if defined(QT_NETWORK_LIB) # include <QtTest/qtest_network.h> @@ -403,15 +425,12 @@ int main(int argc, char *argv[]) \ #endif #define QTEST_MAIN(TestObject) \ -QT_BEGIN_NAMESPACE \ -QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS \ -QT_END_NAMESPACE \ int main(int argc, char *argv[]) \ { \ + TESTLIB_SELFCOVERAGE_START(#TestObject) \ QApplication app(argc, argv); \ app.setAttribute(Qt::AA_Use96Dpi, true); \ QTEST_DISABLE_KEYPAD_NAVIGATION \ - QTEST_ADD_GPU_BLACKLIST_SUPPORT \ TestObject tc; \ QTEST_SET_MAIN_SOURCE_PATH \ return QTest::qExec(&tc, argc, argv); \ @@ -422,14 +441,11 @@ int main(int argc, char *argv[]) \ #include <QtTest/qtest_gui.h> #define QTEST_MAIN(TestObject) \ -QT_BEGIN_NAMESPACE \ -QTEST_ADD_GPU_BLACKLIST_SUPPORT_DEFS \ -QT_END_NAMESPACE \ int main(int argc, char *argv[]) \ { \ + TESTLIB_SELFCOVERAGE_START(#TestObject) \ QGuiApplication app(argc, argv); \ app.setAttribute(Qt::AA_Use96Dpi, true); \ - QTEST_ADD_GPU_BLACKLIST_SUPPORT \ TestObject tc; \ QTEST_SET_MAIN_SOURCE_PATH \ return QTest::qExec(&tc, argc, argv); \ @@ -440,6 +456,7 @@ int main(int argc, char *argv[]) \ #define QTEST_MAIN(TestObject) \ int main(int argc, char *argv[]) \ { \ + TESTLIB_SELFCOVERAGE_START(#TestObject) \ QCoreApplication app(argc, argv); \ app.setAttribute(Qt::AA_Use96Dpi, true); \ TestObject tc; \ @@ -452,6 +469,7 @@ int main(int argc, char *argv[]) \ #define QTEST_GUILESS_MAIN(TestObject) \ int main(int argc, char *argv[]) \ { \ + TESTLIB_SELFCOVERAGE_START(#TestObject) \ QCoreApplication app(argc, argv); \ app.setAttribute(Qt::AA_Use96Dpi, true); \ TestObject tc; \ diff --git a/src/testlib/qtestblacklist.cpp b/src/testlib/qtestblacklist.cpp index ae2913da9a..886f1f75b9 100644 --- a/src/testlib/qtestblacklist.cpp +++ b/src/testlib/qtestblacklist.cpp @@ -221,22 +221,6 @@ static bool checkCondition(const QByteArray &condition) static bool ignoreAll = false; static std::set<QByteArray> *ignoredTests = 0; -static std::set<QByteArray> *gpuFeatures = 0; - -Q_TESTLIB_EXPORT std::set<QByteArray> *(*qgpu_features_ptr)(const QString &) = 0; - -static bool isGPUTestBlacklisted(const char *slot, const char *data = 0) -{ - const QByteArray disableKey = QByteArrayLiteral("disable_") + QByteArray(slot); - if (gpuFeatures->find(disableKey) != gpuFeatures->end()) { - QByteArray msg = QByteArrayLiteral("Skipped due to GPU blacklist: ") + disableKey; - if (data) - msg += ':' + QByteArray(data); - QTest::qSkip(msg.constData(), __FILE__, __LINE__); - return true; - } - return false; -} namespace QTestPrivate { @@ -276,17 +260,6 @@ void parseBlackList() } } -void parseGpuBlackList() -{ - if (!qgpu_features_ptr) - return; - QString filename = QTest::qFindTestData(QStringLiteral("GPU_BLACKLIST")); - if (filename.isEmpty()) - return; - if (!gpuFeatures) - gpuFeatures = qgpu_features_ptr(filename); -} - void checkBlackLists(const char *slot, const char *data) { bool ignore = ignoreAll; @@ -302,21 +275,8 @@ void checkBlackLists(const char *slot, const char *data) } QTestResult::setBlacklistCurrentTest(ignore); - - // Tests blacklisted in GPU_BLACKLIST are to be skipped. Just ignoring the result is - // not sufficient since these are expected to crash or behave in undefined ways. - if (!ignore && gpuFeatures) { - QByteArray s_gpu = slot; - ignore = isGPUTestBlacklisted(s_gpu, data); - if (!ignore && data) { - s_gpu += ':'; - s_gpu += data; - isGPUTestBlacklisted(s_gpu); - } - } -} - } +} // QTestPrivate QT_END_NAMESPACE diff --git a/src/testlib/qtestblacklist_p.h b/src/testlib/qtestblacklist_p.h index 08ce052231..4522c64992 100644 --- a/src/testlib/qtestblacklist_p.h +++ b/src/testlib/qtestblacklist_p.h @@ -58,7 +58,6 @@ QT_BEGIN_NAMESPACE namespace QTestPrivate { // Export functions so they can also be used by QQuickTest Q_TESTLIB_EXPORT void parseBlackList(); - Q_TESTLIB_EXPORT void parseGpuBlackList(); Q_TESTLIB_EXPORT void checkBlackLists(const char *slot, const char *data); } diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 0c935a1f1c..1f69429053 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -52,6 +52,7 @@ #include <QtCore/qfileinfo.h> #include <QtCore/qdir.h> #include <QtCore/qdebug.h> +#include <QtCore/qfloat16.h> #include <QtCore/qlibraryinfo.h> #include <QtCore/private/qtools_p.h> #include <QtCore/qdiriterator.h> @@ -249,7 +250,7 @@ static void stackTrace() static bool installCoverageTool(const char * appname, const char * testname) { -#ifdef __COVERAGESCANNER__ +#if defined(__COVERAGESCANNER__) && !QT_CONFIG(testlib_selfcover) if (!qEnvironmentVariableIsEmpty("QT_TESTCOCOON_ACTIVE")) return false; // Set environment variable QT_TESTCOCOON_ACTIVE to prevent an eventual subtest from @@ -286,74 +287,75 @@ namespace QTestPrivate namespace QTest { - class WatchDog; +class WatchDog; - static QObject *currentTestObject = 0; - static QString mainSourcePath; +static QObject *currentTestObject = 0; +static QString mainSourcePath; #if defined(Q_OS_MACOS) - bool macNeedsActivate = false; - IOPMAssertionID powerID; +bool macNeedsActivate = false; +IOPMAssertionID powerID; #endif - class TestMethods { - Q_DISABLE_COPY(TestMethods) - public: - typedef std::vector<QMetaMethod> MetaMethods; +class TestMethods { +public: + Q_DISABLE_COPY_MOVE(TestMethods) - explicit TestMethods(const QObject *o, const MetaMethods &m = MetaMethods()); + typedef std::vector<QMetaMethod> MetaMethods; - void invokeTests(QObject *testObject) const; + explicit TestMethods(const QObject *o, const MetaMethods &m = MetaMethods()); - static QMetaMethod findMethod(const QObject *obj, const char *signature); + void invokeTests(QObject *testObject) const; - private: - bool invokeTest(int index, const char *data, WatchDog *watchDog) const; - void invokeTestOnData(int index) const; + static QMetaMethod findMethod(const QObject *obj, const char *signature); - QMetaMethod m_initTestCaseMethod; // might not exist, check isValid(). - QMetaMethod m_initTestCaseDataMethod; - QMetaMethod m_cleanupTestCaseMethod; - QMetaMethod m_initMethod; - QMetaMethod m_cleanupMethod; +private: + bool invokeTest(int index, const char *data, WatchDog *watchDog) const; + void invokeTestOnData(int index) const; - MetaMethods m_methods; - }; + QMetaMethod m_initTestCaseMethod; // might not exist, check isValid(). + QMetaMethod m_initTestCaseDataMethod; + QMetaMethod m_cleanupTestCaseMethod; + QMetaMethod m_initMethod; + QMetaMethod m_cleanupMethod; - TestMethods::TestMethods(const QObject *o, const MetaMethods &m) - : m_initTestCaseMethod(TestMethods::findMethod(o, "initTestCase()")) - , m_initTestCaseDataMethod(TestMethods::findMethod(o, "initTestCase_data()")) - , m_cleanupTestCaseMethod(TestMethods::findMethod(o, "cleanupTestCase()")) - , m_initMethod(TestMethods::findMethod(o, "init()")) - , m_cleanupMethod(TestMethods::findMethod(o, "cleanup()")) - , m_methods(m) - { - if (m.empty()) { - const QMetaObject *metaObject = o->metaObject(); - const int count = metaObject->methodCount(); - m_methods.reserve(count); - for (int i = 0; i < count; ++i) { - const QMetaMethod me = metaObject->method(i); - if (isValidSlot(me)) - m_methods.push_back(me); - } + MetaMethods m_methods; +}; + +TestMethods::TestMethods(const QObject *o, const MetaMethods &m) + : m_initTestCaseMethod(TestMethods::findMethod(o, "initTestCase()")) + , m_initTestCaseDataMethod(TestMethods::findMethod(o, "initTestCase_data()")) + , m_cleanupTestCaseMethod(TestMethods::findMethod(o, "cleanupTestCase()")) + , m_initMethod(TestMethods::findMethod(o, "init()")) + , m_cleanupMethod(TestMethods::findMethod(o, "cleanup()")) + , m_methods(m) +{ + if (m.empty()) { + const QMetaObject *metaObject = o->metaObject(); + const int count = metaObject->methodCount(); + m_methods.reserve(count); + for (int i = 0; i < count; ++i) { + const QMetaMethod me = metaObject->method(i); + if (isValidSlot(me)) + m_methods.push_back(me); } } +} - QMetaMethod TestMethods::findMethod(const QObject *obj, const char *signature) - { - const QMetaObject *metaObject = obj->metaObject(); - const int funcIndex = metaObject->indexOfMethod(signature); - return funcIndex >= 0 ? metaObject->method(funcIndex) : QMetaMethod(); - } +QMetaMethod TestMethods::findMethod(const QObject *obj, const char *signature) +{ + const QMetaObject *metaObject = obj->metaObject(); + const int funcIndex = metaObject->indexOfMethod(signature); + return funcIndex >= 0 ? metaObject->method(funcIndex) : QMetaMethod(); +} - static int keyDelay = -1; - static int mouseDelay = -1; - static int eventDelay = -1; +static int keyDelay = -1; +static int mouseDelay = -1; +static int eventDelay = -1; #if QT_CONFIG(thread) - static int timeout = -1; +static int timeout = -1; #endif - static bool noCrashHandler = false; +static bool noCrashHandler = false; /*! \internal Invoke a method of the object without generating warning if the method does not exist @@ -500,7 +502,7 @@ static void qPrintDataTags(FILE *stream) } } -static int qToInt(char *str) +static int qToInt(const char *str) { char *pEnd; int l = (int)strtol(str, &pEnd, 10); @@ -511,7 +513,7 @@ static int qToInt(char *str) return l; } -Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml) +Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, const char *const argv[], bool qml) { int logFormat = -1; // Not set const char *logFilename = 0; @@ -815,9 +817,9 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml) // we load the QML files. So just store the data for now. int colon = -1; int offset; - for (offset = 0; *(argv[i]+offset); ++offset) { - if (*(argv[i]+offset) == ':') { - if (*(argv[i]+offset+1) == ':') { + for (offset = 0; argv[i][offset]; ++offset) { + if (argv[i][offset] == ':') { + if (argv[i][offset + 1] == ':') { // "::" is used as a test name separator. // e.g. "ClickTests::test_click:row1". ++offset; @@ -864,6 +866,11 @@ Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml) QTestLog::addLogger(QTestLog::Plain, logFilename); } +// Temporary, backwards compatibility, until qtdeclarative's use of it is converted +Q_TESTLIB_EXPORT void qtest_qParseArgs(int argc, char *argv[], bool qml) { + qtest_qParseArgs(argc, const_cast<const char *const *>(argv), qml); +} + QBenchmarkResult qMedian(const QVector<QBenchmarkResult> &container) { const int count = container.count(); @@ -1214,7 +1221,9 @@ char *formatString(const char *prefix, const char *suffix, size_t numArguments, Returns a pointer to a string that is the string \a ba represented as a space-separated sequence of hex characters. If the input is considered too long, it is truncated. A trucation is indicated in - the returned string as an ellipsis at the end. + the returned string as an ellipsis at the end. The caller has + ownership of the returned pointer and must ensure it is later passed + to operator delete[]. \a length is the length of the string \a ba. */ @@ -1616,7 +1625,7 @@ FatalSignalHandler::~FatalSignalHandler() // Helper class for resolving symbol names by dynamically loading "dbghelp.dll". class DebugSymbolResolver { - Q_DISABLE_COPY(DebugSymbolResolver) + Q_DISABLE_COPY_MOVE(DebugSymbolResolver) public: struct Symbol { Symbol() : name(nullptr), address(0) {} @@ -1832,8 +1841,6 @@ void QTest::qInit(QObject *testObject, int argc, char **argv) #endif QTestPrivate::parseBlackList(); - QTestPrivate::parseGpuBlackList(); - QTestResult::reset(); QTEST_ASSERT(testObject); @@ -2183,13 +2190,12 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co if (found.isEmpty()) { const char *testObjectName = QTestResult::currentTestObjectName(); if (testObjectName) { - QString testsPath = QLibraryInfo::location(QLibraryInfo::TestsPath); - QString candidate = QString::fromLatin1("%1/%2/%3") + const QString testsPath = QLibraryInfo::location(QLibraryInfo::TestsPath); + const QString candidate = QString::fromLatin1("%1/%2/%3") .arg(testsPath, QFile::decodeName(testObjectName).toLower(), base); if (QFileInfo::exists(candidate)) { found = candidate; - } - else if (QTestLog::verboseLevel() >= 2) { + } else if (QTestLog::verboseLevel() >= 2) { QTestLog::info(qPrintable( QString::fromLatin1("testdata %1 not found in tests install path [%2]; " "checking next location") @@ -2211,11 +2217,10 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co } const QString canonicalPath = srcdir.canonicalFilePath(); - QString candidate = QString::fromLatin1("%1/%2").arg(canonicalPath, base); + const QString candidate = QString::fromLatin1("%1/%2").arg(canonicalPath, base); if (!canonicalPath.isEmpty() && QFileInfo::exists(candidate)) { found = candidate; - } - else if (QTestLog::verboseLevel() >= 2) { + } else if (QTestLog::verboseLevel() >= 2) { QTestLog::info(qPrintable( QString::fromLatin1("testdata %1 not found relative to source path [%2]") .arg(base, QDir::toNativeSeparators(candidate))), @@ -2225,31 +2230,48 @@ QString QTest::qFindTestData(const QString& base, const char *file, int line, co // 4. Try resources if (found.isEmpty()) { - QString candidate = QString::fromLatin1(":/%1").arg(base); - if (QFileInfo::exists(candidate)) + const QString candidate = QString::fromLatin1(":/%1").arg(base); + if (QFileInfo::exists(candidate)) { found = candidate; + } else if (QTestLog::verboseLevel() >= 2) { + QTestLog::info(qPrintable( + QString::fromLatin1("testdata %1 not found in resources [%2]") + .arg(base, QDir::toNativeSeparators(candidate))), + file, line); + } } // 5. Try current directory if (found.isEmpty()) { const QString candidate = QDir::currentPath() + QLatin1Char('/') + base; - if (QFileInfo::exists(candidate)) + if (QFileInfo::exists(candidate)) { found = candidate; + } else if (QTestLog::verboseLevel() >= 2) { + QTestLog::info(qPrintable( + QString::fromLatin1("testdata %1 not found in current directory [%2]") + .arg(base, QDir::toNativeSeparators(candidate))), + file, line); + } } // 6. Try main source directory if (found.isEmpty()) { - QString candidate = QTest::mainSourcePath % QLatin1Char('/') % base; - if (QFileInfo::exists(candidate)) + const QString candidate = QTest::mainSourcePath % QLatin1Char('/') % base; + if (QFileInfo::exists(candidate)) { found = candidate; + } else if (QTestLog::verboseLevel() >= 2) { + QTestLog::info(qPrintable( + QString::fromLatin1("testdata %1 not found in main source directory [%2]") + .arg(base, QDir::toNativeSeparators(candidate))), + file, line); + } } if (found.isEmpty()) { QTest::qWarn(qPrintable( QString::fromLatin1("testdata %1 could not be located!").arg(base)), file, line); - } - else if (QTestLog::verboseLevel() >= 1) { + } else if (QTestLog::verboseLevel() >= 1) { QTestLog::info(qPrintable( QString::fromLatin1("testdata %1 was located at %2").arg(base, QDir::toNativeSeparators(found))), file, line); @@ -2323,7 +2345,7 @@ void QTest::addColumnInternal(int id, const char *name) */ QTestData &QTest::newRow(const char *dataTag) { - QTEST_ASSERT_X(dataTag, "QTest::newRow()", "Data tag can not be null"); + QTEST_ASSERT_X(dataTag, "QTest::newRow()", "Data tag cannot be null"); QTestTable *tbl = QTestTable::currentTestTable(); QTEST_ASSERT_X(tbl, "QTest::newRow()", "Cannot add testdata outside of a _data slot."); QTEST_ASSERT_X(tbl->elementCount(), "QTest::newRow()", "Must add columns before attempting to add rows."); @@ -2497,6 +2519,16 @@ bool QTest::compare_helper(bool success, const char *failureMsg, return QTestResult::compare(success, failureMsg, val1, val2, actual, expected, file, line); } +/*! \fn bool QTest::qCompare(const qfloat16 &t1, const qfloat16 &t2, const char *actual, const char *expected, const char *file, int line) + \internal + */ +bool QTest::qCompare(qfloat16 const &t1, qfloat16 const &t2, const char *actual, const char *expected, + const char *file, int line) +{ + return compare_helper(qFuzzyCompare(t1, t2), "Compared qfloat16s are not the same (fuzzy compare)", + toString(t1), toString(t2), actual, expected, file, line); +} + /*! \fn bool QTest::qCompare(const float &t1, const float &t2, const char *actual, const char *expected, const char *file, int line) \internal */ @@ -2619,6 +2651,13 @@ template <> Q_TESTLIB_EXPORT char *QTest::toString<TYPE>(const TYPE &t) \ TO_STRING_FLOAT(float, %g) TO_STRING_FLOAT(double, %.12lg) +template <> Q_TESTLIB_EXPORT char *QTest::toString<qfloat16>(const qfloat16 &t) +{ + char *msg = new char[16]; + qsnprintf(msg, 16, "%.3g", static_cast<float>(t)); + return msg; +} + template <> Q_TESTLIB_EXPORT char *QTest::toString<char>(const char &t) { unsigned char c = static_cast<unsigned char>(t); diff --git a/src/testlib/qtestcase.h b/src/testlib/qtestcase.h index 5fed9d6bcc..5d566b835e 100644 --- a/src/testlib/qtestcase.h +++ b/src/testlib/qtestcase.h @@ -57,6 +57,7 @@ QT_BEGIN_NAMESPACE +class qfloat16; class QRegularExpression; #define QVERIFY(statement) \ @@ -67,17 +68,17 @@ do {\ #define QFAIL(message) \ do {\ - QTest::qFail(message, __FILE__, __LINE__);\ + QTest::qFail(static_cast<const char *>(message), __FILE__, __LINE__);\ return;\ } while (false) #define QVERIFY2(statement, description) \ do {\ if (statement) {\ - if (!QTest::qVerify(true, #statement, (description), __FILE__, __LINE__))\ + if (!QTest::qVerify(true, #statement, static_cast<const char *>(description), __FILE__, __LINE__))\ return;\ } else {\ - if (!QTest::qVerify(false, #statement, (description), __FILE__, __LINE__))\ + if (!QTest::qVerify(false, #statement, static_cast<const char *>(description), __FILE__, __LINE__))\ return;\ }\ } while (false) @@ -184,7 +185,7 @@ do { \ #define QSKIP_INTERNAL(statement) \ do {\ - QTest::qSkip(statement, __FILE__, __LINE__);\ + QTest::qSkip(static_cast<const char *>(statement), __FILE__, __LINE__);\ return;\ } while (false) @@ -200,7 +201,7 @@ do {\ #define QEXPECT_FAIL(dataIndex, comment, mode)\ do {\ - if (!QTest::qExpectFail(dataIndex, comment, QTest::mode, __FILE__, __LINE__))\ + if (!QTest::qExpectFail(dataIndex, static_cast<const char *>(comment), QTest::mode, __FILE__, __LINE__))\ return;\ } while (false) @@ -217,7 +218,7 @@ do {\ } while (false) #define QWARN(msg)\ - QTest::qWarn(msg, __FILE__, __LINE__) + QTest::qWarn(static_cast<const char *>(msg), __FILE__, __LINE__) #ifdef QT_TESTCASE_BUILDDIR # define QFINDTESTDATA(basepath)\ @@ -361,6 +362,9 @@ namespace QTest } #endif + Q_TESTLIB_EXPORT bool qCompare(qfloat16 const &t1, qfloat16 const &t2, + const char *actual, const char *expected, const char *file, int line); + Q_TESTLIB_EXPORT bool qCompare(float const &t1, float const &t2, const char *actual, const char *expected, const char *file, int line); @@ -405,6 +409,7 @@ namespace QTest QTEST_COMPARE_DECL(float) QTEST_COMPARE_DECL(double) + QTEST_COMPARE_DECL(qfloat16) QTEST_COMPARE_DECL(char) QTEST_COMPARE_DECL(signed char) QTEST_COMPARE_DECL(unsigned char) diff --git a/src/testlib/qtestcorelist_p.h b/src/testlib/qtestcorelist_p.h index 5943695876..4d080f6758 100644 --- a/src/testlib/qtestcorelist_p.h +++ b/src/testlib/qtestcorelist_p.h @@ -66,9 +66,6 @@ class QTestCoreList void addToList(T **list); T *nextElement(); T *previousElement(); - int count(T *list); - int count(); - private: T *next; T *prev; @@ -121,20 +118,6 @@ T *QTestCoreList<T>::previousElement() return prev; } -template <class T> -int QTestCoreList<T>::count() -{ - int numOfElements = 0; - T *it = next; - - while (it) { - ++numOfElements; - it = it->nextElement(); - } - - return numOfElements; -} - QT_END_NAMESPACE #endif diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp index 57bb7d95a7..faef3912c4 100644 --- a/src/testlib/qtestlog.cpp +++ b/src/testlib/qtestlog.cpp @@ -74,6 +74,10 @@ QT_BEGIN_NAMESPACE static void saveCoverageTool(const char * appname, bool testfailed, bool installedTestCoverage) { #ifdef __COVERAGESCANNER__ +# if QT_CONFIG(testlib_selfcover) + __coveragescanner_teststate(QTestLog::failCount() > 0 ? "FAILED" : + QTestLog::passCount() > 0 ? "PASSED" : "SKIPPED"); +# else if (!installedTestCoverage) return; // install again to make sure the filename is correct. @@ -84,6 +88,7 @@ static void saveCoverageTool(const char * appname, bool testfailed, bool install __coveragescanner_testname(""); __coveragescanner_clear(); unsetenv("QT_TESTCOCOON_ACTIVE"); +# endif // testlib_selfcover #else Q_UNUSED(appname); Q_UNUSED(testfailed); diff --git a/src/testlib/selfcover.pri b/src/testlib/selfcover.pri new file mode 100644 index 0000000000..7de50ba6e6 --- /dev/null +++ b/src/testlib/selfcover.pri @@ -0,0 +1,28 @@ +# Configuration for testlib and its tests, to instrument with +# FrogLogic's Squish CoCo (cf. testcocoon.prf, which handles similar +# for general code; but testlib needs special handling). + +# Only for use when feature testlib_selfcover is enabled: +!qtConfig(testlib_selfcover): return() + +# This enables verification that testlib itself is adequately tested, +# as a grounds for trusting that testing with it is useful. +# Exclude all non-testlib source from coverage instrumentation: +COVERAGE_OPTIONS = --cs-exclude-file-abs-wildcard=$$QT_SOURCE_TREE/* +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 + +QMAKE_CFLAGS += $$COVERAGE_OPTIONS +QMAKE_CXXFLAGS += $$COVERAGE_OPTIONS +QMAKE_LFLAGS += $$COVERAGE_OPTIONS + +# FIXME: relies on QMAKE_* being just the command-names, with no path prefix +QMAKE_CC = cs$$QMAKE_CC +QMAKE_CXX = cs$$QMAKE_CXX +QMAKE_LINK = cs$$QMAKE_LINK +QMAKE_LINK_SHLIB = cs$$QMAKE_LINK_SHLIB +QMAKE_AR = cs$$QMAKE_AR +QMAKE_LIB = cs$$QMAKE_LIB diff --git a/src/testlib/testlib.pro b/src/testlib/testlib.pro index 34bb581e02..f52a913a08 100644 --- a/src/testlib/testlib.pro +++ b/src/testlib/testlib.pro @@ -146,4 +146,5 @@ mac { !qtHaveModule(network): HEADERSCLEAN_EXCLUDE += qtest_network.h +include(selfcover.pri) load(qt_module) |