diff options
Diffstat (limited to 'src/testlib/qtestcase.h')
-rw-r--r-- | src/testlib/qtestcase.h | 221 |
1 files changed, 149 insertions, 72 deletions
diff --git a/src/testlib/qtestcase.h b/src/testlib/qtestcase.h index a0df8dd305..1a47382304 100644 --- a/src/testlib/qtestcase.h +++ b/src/testlib/qtestcase.h @@ -23,36 +23,64 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_EXCEPTIONS + +#ifdef QTEST_THROW_ON_FAILURE +# define QTEST_FAIL_ACTION QTest::Internal::throwOnFail() +#else +# define QTEST_FAIL_ACTION do { QTest::Internal::maybeThrowOnFail(); return; } while (false) +#endif + +#ifdef QTEST_THROW_ON_SKIP +# define QTEST_SKIP_ACTION QTest::Internal::throwOnSkip() +#else +# define QTEST_SKIP_ACTION do { QTest::Internal::maybeThrowOnSkip(); return; } while (false) +#endif + +#else +# if defined(QTEST_THROW_ON_FAILURE) || defined(QTEST_THROW_ON_SKIP) +# error QTEST_THROW_ON_FAILURE/SKIP require exception support enabled. +# endif +#endif // QT_NO_EXCEPTIONS + +#ifndef QTEST_FAIL_ACTION +# define QTEST_FAIL_ACTION return +#endif + +#ifndef QTEST_SKIP_ACTION +# define QTEST_SKIP_ACTION return +#endif + class qfloat16; class QRegularExpression; #define QVERIFY(statement) \ do {\ if (!QTest::qVerify(static_cast<bool>(statement), #statement, "", __FILE__, __LINE__))\ - return;\ + QTEST_FAIL_ACTION; \ } while (false) #define QFAIL(message) \ do {\ QTest::qFail(static_cast<const char *>(message), __FILE__, __LINE__);\ - return;\ + QTEST_FAIL_ACTION; \ } while (false) #define QVERIFY2(statement, description) \ do {\ if (statement) {\ if (!QTest::qVerify(true, #statement, static_cast<const char *>(description), __FILE__, __LINE__))\ - return;\ + QTEST_FAIL_ACTION; \ } else {\ if (!QTest::qVerify(false, #statement, static_cast<const char *>(description), __FILE__, __LINE__))\ - return;\ + QTEST_FAIL_ACTION; \ }\ } while (false) #define QCOMPARE(actual, expected) \ do {\ if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__))\ - return;\ + QTEST_FAIL_ACTION; \ } while (false) // A wrapper lambda is introduced to extend the lifetime of lhs and rhs in @@ -71,16 +99,16 @@ do { \ #lhs, #rhs, QTest::ComparisonOperation::opId, \ __FILE__, __LINE__); \ }(lhs, rhs)) { \ - return; \ + QTEST_FAIL_ACTION; \ } \ } while (false) -#define QCOMPARE_EQ(lhs, rhs) QCOMPARE_OP_IMPL(lhs, rhs, ==, Equal) -#define QCOMPARE_NE(lhs, rhs) QCOMPARE_OP_IMPL(lhs, rhs, !=, NotEqual) -#define QCOMPARE_LT(lhs, rhs) QCOMPARE_OP_IMPL(lhs, rhs, <, LessThan) -#define QCOMPARE_LE(lhs, rhs) QCOMPARE_OP_IMPL(lhs, rhs, <=, LessThanOrEqual) -#define QCOMPARE_GT(lhs, rhs) QCOMPARE_OP_IMPL(lhs, rhs, >, GreaterThan) -#define QCOMPARE_GE(lhs, rhs) QCOMPARE_OP_IMPL(lhs, rhs, >=, GreaterThanOrEqual) +#define QCOMPARE_EQ(computed, baseline) QCOMPARE_OP_IMPL(computed, baseline, ==, Equal) +#define QCOMPARE_NE(computed, baseline) QCOMPARE_OP_IMPL(computed, baseline, !=, NotEqual) +#define QCOMPARE_LT(computed, baseline) QCOMPARE_OP_IMPL(computed, baseline, <, LessThan) +#define QCOMPARE_LE(computed, baseline) QCOMPARE_OP_IMPL(computed, baseline, <=, LessThanOrEqual) +#define QCOMPARE_GT(computed, baseline) QCOMPARE_OP_IMPL(computed, baseline, >, GreaterThan) +#define QCOMPARE_GE(computed, baseline) QCOMPARE_OP_IMPL(computed, baseline, >=, GreaterThanOrEqual) #ifndef QT_NO_EXCEPTIONS @@ -89,12 +117,9 @@ do { \ QT_TRY { \ __VA_ARGS__; \ /* success */ \ - } QT_CATCH (const std::exception &e) { \ - QTest::qCaught(nullptr, e.what(), __FILE__, __LINE__); \ - return; \ } QT_CATCH (...) { \ - QTest::qCaught(nullptr, nullptr, __FILE__, __LINE__); \ - QT_RETHROW; \ + QTest::qCaught(nullptr, __FILE__, __LINE__); \ + QTEST_FAIL_ACTION; \ } \ } while (false) \ /* end */ @@ -111,22 +136,20 @@ inline void useVerifyThrowsException() {} # define QVERIFY_THROWS_EXCEPTION(exceptiontype, ...) \ do {\ + bool qverify_throws_exception_did_not_throw = false; \ QT_TRY {\ - QT_TRY {\ - __VA_ARGS__;\ - QTest::qFail("Expected exception of type " #exceptiontype " to be thrown" \ - " but no exception caught", __FILE__, __LINE__);\ - return;\ - } QT_CATCH (const exceptiontype &) {\ - /* success */\ - }\ - } QT_CATCH (const std::exception &e) {\ - QTest::qCaught(#exceptiontype, e.what(), __FILE__, __LINE__);\ - return;\ + __VA_ARGS__; \ + QTest::qFail("Expected exception of type " #exceptiontype " to be thrown" \ + " but no exception caught", __FILE__, __LINE__); \ + qverify_throws_exception_did_not_throw = true; \ + } QT_CATCH (const exceptiontype &) { \ + /* success */ \ } QT_CATCH (...) {\ - QTest::qCaught(#exceptiontype, nullptr, __FILE__, __LINE__);\ - QT_RETHROW;\ + QTest::qCaught(#exceptiontype, __FILE__, __LINE__); \ + QTEST_FAIL_ACTION; \ }\ + if (qverify_throws_exception_did_not_throw) \ + QTEST_FAIL_ACTION; \ } while (false) #else // QT_NO_EXCEPTIONS @@ -148,9 +171,10 @@ inline void useVerifyThrowsException() {} /* Ideally we would adapt qWaitFor(), or a variant on it, to implement roughly * what the following provides as QTRY_LOOP_IMPL(); however, for now, the * reporting of how much to increase the timeout to (if within a factor of two) - * on failure and the check for QTest::currentTestFailed() go beyond - * qWaitFor(). (We no longer care about the bug in MSVC < 2017 that precluded - * using qWaitFor() in the implementation here, see QTBUG-59096.) + * on failure and the check for (QTest::runningTest() && + * QTest::currentTestResolved()) go beyond qWaitFor(). (We no longer care about + * the bug in MSVC < 2017 that precluded using qWaitFor() in the implementation + * here, see QTBUG-59096.) */ // NB: not do {...} while (0) wrapped, as qt_test_i is accessed after it @@ -159,24 +183,29 @@ inline void useVerifyThrowsException() {} QTest::qWait(0); \ } \ int qt_test_i = 0; \ - for (; qt_test_i < timeoutValue && !QTest::currentTestFailed() \ + for (; qt_test_i < timeoutValue && !(QTest::runningTest() && QTest::currentTestResolved()) \ && !(expr); qt_test_i += step) { \ QTest::qWait(step); \ } // Ends in a for-block, so doesn't want a following semicolon. #define QTRY_TIMEOUT_DEBUG_IMPL(expr, timeoutValue, step) \ - if (!QTest::currentTestFailed() && !(expr)) { \ + if (!(QTest::runningTest() && QTest::currentTestResolved()) && !(expr)) { \ QTRY_LOOP_IMPL(expr, 2 * (timeoutValue), step) \ - if (expr) { \ + if ((expr)) { \ QFAIL(qPrintable(QTest::Internal::formatTryTimeoutDebugMessage(\ u8"" #expr, timeoutValue, timeoutValue + qt_test_i))); \ } \ } -#define QTRY_IMPL(expr, timeout)\ - const int qt_test_step = timeout < 350 ? timeout / 7 + 1 : 50; \ - const int qt_test_timeoutValue = timeout; \ +#define QTRY_IMPL(expr, timeoutAsGiven)\ + const auto qt_test_timeoutAsMs = [&] { \ + /* make 5s work w/o user action: */ \ + using namespace std::chrono_literals; \ + return std::chrono::milliseconds{timeoutAsGiven}; \ + }(); \ + const int qt_test_step = qt_test_timeoutAsMs.count() < 350 ? qt_test_timeoutAsMs.count() / 7 + 1 : 50; \ + const int qt_test_timeoutValue = qt_test_timeoutAsMs.count(); \ { QTRY_LOOP_IMPL(expr, qt_test_timeoutValue, qt_test_step) } \ QTRY_TIMEOUT_DEBUG_IMPL(expr, qt_test_timeoutValue, qt_test_step) // Ends with an if-block, so doesn't want a following semicolon. @@ -188,7 +217,7 @@ do { \ QVERIFY(expr); \ } while (false) -#define QTRY_VERIFY(expr) QTRY_VERIFY_WITH_TIMEOUT(expr, 5000) +#define QTRY_VERIFY(expr) QTRY_VERIFY_WITH_TIMEOUT(expr, 5s) // Will try to wait for the expression to become true while allowing event processing #define QTRY_VERIFY2_WITH_TIMEOUT(expr, messageExpression, timeout) \ @@ -197,7 +226,7 @@ do { \ QVERIFY2(expr, messageExpression); \ } while (false) -#define QTRY_VERIFY2(expr, messageExpression) QTRY_VERIFY2_WITH_TIMEOUT(expr, messageExpression, 5000) +#define QTRY_VERIFY2(expr, messageExpression) QTRY_VERIFY2_WITH_TIMEOUT(expr, messageExpression, 5s) // Will try to wait for the comparison to become successful while allowing event processing #define QTRY_COMPARE_WITH_TIMEOUT(expr, expected, timeout) \ @@ -206,48 +235,48 @@ do { \ QCOMPARE(expr, expected); \ } while (false) -#define QTRY_COMPARE(expr, expected) QTRY_COMPARE_WITH_TIMEOUT(expr, expected, 5000) +#define QTRY_COMPARE(expr, expected) QTRY_COMPARE_WITH_TIMEOUT(expr, expected, 5s) -#define QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(left, right, op, opId, timeout) \ +#define QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(computed, baseline, op, opId, timeout) \ do { \ - QTRY_IMPL(((left) op (right)), timeout) \ - QCOMPARE_OP_IMPL(left, right, op, opId); \ + QTRY_IMPL(((computed) op (baseline)), timeout) \ + QCOMPARE_OP_IMPL(computed, baseline, op, opId); \ } while (false) -#define QTRY_COMPARE_EQ_WITH_TIMEOUT(left, right, timeout) \ - QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(left, right, ==, Equal, timeout) +#define QTRY_COMPARE_EQ_WITH_TIMEOUT(computed, baseline, timeout) \ + QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(computed, baseline, ==, Equal, timeout) -#define QTRY_COMPARE_EQ(left, right) QTRY_COMPARE_EQ_WITH_TIMEOUT(left, right, 5000) +#define QTRY_COMPARE_EQ(computed, baseline) QTRY_COMPARE_EQ_WITH_TIMEOUT(computed, baseline, 5s) -#define QTRY_COMPARE_NE_WITH_TIMEOUT(left, right, timeout) \ - QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(left, right, !=, NotEqual, timeout) +#define QTRY_COMPARE_NE_WITH_TIMEOUT(computed, baseline, timeout) \ + QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(computed, baseline, !=, NotEqual, timeout) -#define QTRY_COMPARE_NE(left, right) QTRY_COMPARE_NE_WITH_TIMEOUT(left, right, 5000) +#define QTRY_COMPARE_NE(computed, baseline) QTRY_COMPARE_NE_WITH_TIMEOUT(computed, baseline, 5s) -#define QTRY_COMPARE_LT_WITH_TIMEOUT(left, right, timeout) \ - QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(left, right, <, LessThan, timeout) +#define QTRY_COMPARE_LT_WITH_TIMEOUT(computed, baseline, timeout) \ + QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(computed, baseline, <, LessThan, timeout) -#define QTRY_COMPARE_LT(left, right) QTRY_COMPARE_LT_WITH_TIMEOUT(left, right, 5000) +#define QTRY_COMPARE_LT(computed, baseline) QTRY_COMPARE_LT_WITH_TIMEOUT(computed, baseline, 5s) -#define QTRY_COMPARE_LE_WITH_TIMEOUT(left, right, timeout) \ - QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(left, right, <=, LessThanOrEqual, timeout) +#define QTRY_COMPARE_LE_WITH_TIMEOUT(computed, baseline, timeout) \ + QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(computed, baseline, <=, LessThanOrEqual, timeout) -#define QTRY_COMPARE_LE(left, right) QTRY_COMPARE_LE_WITH_TIMEOUT(left, right, 5000) +#define QTRY_COMPARE_LE(computed, baseline) QTRY_COMPARE_LE_WITH_TIMEOUT(computed, baseline, 5s) -#define QTRY_COMPARE_GT_WITH_TIMEOUT(left, right, timeout) \ - QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(left, right, >, GreaterThan, timeout) +#define QTRY_COMPARE_GT_WITH_TIMEOUT(computed, baseline, timeout) \ + QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(computed, baseline, >, GreaterThan, timeout) -#define QTRY_COMPARE_GT(left, right) QTRY_COMPARE_GT_WITH_TIMEOUT(left, right, 5000) +#define QTRY_COMPARE_GT(computed, baseline) QTRY_COMPARE_GT_WITH_TIMEOUT(computed, baseline, 5s) -#define QTRY_COMPARE_GE_WITH_TIMEOUT(left, right, timeout) \ - QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(left, right, >=, GreaterThanOrEqual, timeout) +#define QTRY_COMPARE_GE_WITH_TIMEOUT(computed, baseline, timeout) \ + QTRY_COMPARE_OP_WITH_TIMEOUT_IMPL(computed, baseline, >=, GreaterThanOrEqual, timeout) -#define QTRY_COMPARE_GE(left, right) QTRY_COMPARE_GE_WITH_TIMEOUT(left, right, 5000) +#define QTRY_COMPARE_GE(computed, baseline) QTRY_COMPARE_GE_WITH_TIMEOUT(computed, baseline, 5s) #define QSKIP_INTERNAL(statement) \ do {\ QTest::qSkip(static_cast<const char *>(statement), __FILE__, __LINE__);\ - return;\ + QTEST_SKIP_ACTION; \ } while (false) #define QSKIP(statement, ...) QSKIP_INTERNAL(statement) @@ -255,7 +284,7 @@ do {\ #define QEXPECT_FAIL(dataIndex, comment, mode)\ do {\ if (!QTest::qExpectFail(dataIndex, static_cast<const char *>(comment), QTest::mode, __FILE__, __LINE__))\ - return;\ + QTEST_FAIL_ACTION; \ } while (false) #define QFETCH(Type, name)\ @@ -267,7 +296,7 @@ do {\ #define QTEST(actual, testElement)\ do {\ if (!QTest::qTest(actual, testElement, #actual, #testElement, __FILE__, __LINE__))\ - return;\ + QTEST_FAIL_ACTION; \ } while (false) #ifdef QT_TESTCASE_BUILDDIR @@ -296,6 +325,11 @@ namespace QTest { namespace Internal { + [[noreturn]] Q_TESTLIB_EXPORT void throwOnFail(); + [[noreturn]] Q_TESTLIB_EXPORT void throwOnSkip(); + Q_TESTLIB_EXPORT void maybeThrowOnFail(); + Q_TESTLIB_EXPORT void maybeThrowOnSkip(); + Q_TESTLIB_EXPORT QString formatTryTimeoutDebugMessage(q_no_char8_t::QUtf8StringView expr, int timeout, int actual); template<typename T> // Output registered enums @@ -314,16 +348,17 @@ namespace QTest template <typename T> // Fallback; for built-in types debug streaming must be possible inline typename std::enable_if<!QtPrivate::IsQEnumHelper<T>::Value && !std::is_enum_v<T>, char *>::type toString(const T &t) { + char *result = nullptr; #ifndef QT_NO_DEBUG_STREAM if constexpr (QTypeTraits::has_ostream_operator_v<QDebug, T>) { - return qstrdup(QDebug::toString(t).toUtf8().constData()); + result = qstrdup(QDebug::toString(t).toUtf8().constData()); } else { static_assert(!QMetaTypeId2<T>::IsBuiltIn, "Built-in type must implement debug streaming operator " "or provide QTest::toString specialization"); } #endif - return nullptr; + return result; } template<typename F> // Output QFlags of registered enumerations @@ -342,6 +377,9 @@ namespace QTest return msg; } + // Exported so Qt Quick Test can also use it for generating backtraces upon crashes. + Q_TESTLIB_EXPORT extern bool noCrashHandler; + } // namespace Internal template<typename T> @@ -351,14 +389,14 @@ namespace QTest } template <typename T1, typename T2> - inline char *toString(const QPair<T1, T2> &pair); - - template <typename T1, typename T2> inline char *toString(const std::pair<T1, T2> &pair); template <class... Types> inline char *toString(const std::tuple<Types...> &tuple); + template <typename Rep, typename Period> + inline char *toString(std::chrono::duration<Rep, Period> duration); + Q_TESTLIB_EXPORT char *toHexRepresentation(const char *ba, qsizetype length); Q_TESTLIB_EXPORT char *toPrettyCString(const char *unicode, qsizetype length); Q_TESTLIB_EXPORT char *toPrettyUnicode(QStringView string); @@ -374,7 +412,42 @@ namespace QTest Q_TESTLIB_EXPORT int qExec(QObject *testObject, int argc = 0, char **argv = nullptr); Q_TESTLIB_EXPORT int qExec(QObject *testObject, const QStringList &arguments); +#if QT_CONFIG(batch_test_support) || defined(Q_QDOC) + using TestEntryFunction = int (*)(int, char **); + Q_TESTLIB_EXPORT void qRegisterTestCase(const QString &name, TestEntryFunction entryFunction); +#endif // QT_CONFIG(batch_test_support) + Q_TESTLIB_EXPORT void setMainSourcePath(const char *file, const char *builddir = nullptr); + Q_TESTLIB_EXPORT void setThrowOnFail(bool enable) noexcept; + Q_TESTLIB_EXPORT void setThrowOnSkip(bool enable) noexcept; + + class ThrowOnFailEnabler { + Q_DISABLE_COPY_MOVE(ThrowOnFailEnabler) + public: + ThrowOnFailEnabler() { setThrowOnFail(true); } + ~ThrowOnFailEnabler() { setThrowOnFail(false); } + }; + + class ThrowOnSkipEnabler { + Q_DISABLE_COPY_MOVE(ThrowOnSkipEnabler) + public: + ThrowOnSkipEnabler() { setThrowOnSkip(true); } + ~ThrowOnSkipEnabler() { setThrowOnSkip(false); } + }; + + class ThrowOnFailDisabler { + Q_DISABLE_COPY_MOVE(ThrowOnFailDisabler) + public: + ThrowOnFailDisabler() { setThrowOnFail(false); } + ~ThrowOnFailDisabler() { setThrowOnFail(true); } + }; + + class ThrowOnSkipDisabler { + Q_DISABLE_COPY_MOVE(ThrowOnSkipDisabler) + public: + ThrowOnSkipDisabler() { setThrowOnSkip(false); } + ~ThrowOnSkipDisabler() { setThrowOnSkip(true); } + }; Q_TESTLIB_EXPORT bool qVerify(bool statement, const char *statementStr, const char *description, const char *file, int line); @@ -385,6 +458,8 @@ namespace QTest const char *file, int line); Q_DECL_COLD_FUNCTION Q_TESTLIB_EXPORT void qCaught(const char *expected, const char *what, const char *file, int line); + Q_DECL_COLD_FUNCTION + Q_TESTLIB_EXPORT void qCaught(const char *expected, const char *file, int line); #if QT_DEPRECATED_SINCE(6, 3) QT_DEPRECATED_VERSION_X_6_3("Use qWarning() instead") Q_TESTLIB_EXPORT void qWarn(const char *message, const char *file = nullptr, int line = 0); @@ -414,6 +489,8 @@ namespace QTest Q_TESTLIB_EXPORT const char *currentTestFunction(); Q_TESTLIB_EXPORT const char *currentDataTag(); Q_TESTLIB_EXPORT bool currentTestFailed(); + Q_TESTLIB_EXPORT bool currentTestResolved(); + Q_TESTLIB_EXPORT bool runningTest(); // Internal, for use by macros and QTestEventLoop. Q_TESTLIB_EXPORT Qt::Key asciiToKey(char ascii); Q_TESTLIB_EXPORT char keyToAscii(Qt::Key key); @@ -570,7 +647,7 @@ namespace QTest QTEST_COMPARE_DECL(bool) #endif - template <typename T1, typename T2> + template <typename T1, typename T2 = T1> inline bool qCompare(const T1 &t1, const T2 &t2, const char *actual, const char *expected, const char *file, int line) { |