diff --git a/src/testlib/qtest.h b/src/testlib/qtest.h index cb73a9538a7..e185e81eb8f 100644 --- a/src/testlib/qtest.h +++ b/src/testlib/qtest.h @@ -421,7 +421,7 @@ bool _q_compareSequence(ActualIterator actualIt, ActualIterator actualEnd, delete [] val2; } } - return compare_helper(isOk, msg, nullptr, nullptr, actual, expected, file, line); + return compare_helper(isOk, msg, actual, expected, file, line); } namespace Internal { diff --git a/src/testlib/qtest_gui.h b/src/testlib/qtest_gui.h index 68c13ee57b6..26cf17c6ef7 100644 --- a/src/testlib/qtest_gui.h +++ b/src/testlib/qtest_gui.h @@ -122,17 +122,17 @@ inline bool qCompare(QImage const &t1, QImage const &t2, qsnprintf(msg, 1024, "Compared QImages differ.\n" " Actual (%s).isNull(): %d\n" " Expected (%s).isNull(): %d", actual, t1Null, expected, t2Null); - return compare_helper(false, msg, nullptr, nullptr, actual, expected, file, line); + return compare_helper(false, msg, actual, expected, file, line); } if (t1Null && t2Null) - return compare_helper(true, nullptr, nullptr, nullptr, actual, expected, file, line); + return compare_helper(true, nullptr, actual, expected, file, line); if (!qFuzzyCompare(t1.devicePixelRatio(), t2.devicePixelRatio())) { qsnprintf(msg, 1024, "Compared QImages differ in device pixel ratio.\n" " Actual (%s): %g\n" " Expected (%s): %g", actual, t1.devicePixelRatio(), expected, t2.devicePixelRatio()); - return compare_helper(false, msg, nullptr, nullptr, actual, expected, file, line); + return compare_helper(false, msg, actual, expected, file, line); } if (t1.width() != t2.width() || t1.height() != t2.height()) { qsnprintf(msg, 1024, "Compared QImages differ in size.\n" @@ -140,17 +140,17 @@ inline bool qCompare(QImage const &t1, QImage const &t2, " Expected (%s): %dx%d", actual, t1.width(), t1.height(), expected, t2.width(), t2.height()); - return compare_helper(false, msg, nullptr, nullptr, actual, expected, file, line); + return compare_helper(false, msg, actual, expected, file, line); } if (t1.format() != t2.format()) { qsnprintf(msg, 1024, "Compared QImages differ in format.\n" " Actual (%s): %d\n" " Expected (%s): %d", actual, t1.format(), expected, t2.format()); - return compare_helper(false, msg, nullptr, nullptr, actual, expected, file, line); + return compare_helper(false, msg, actual, expected, file, line); } return compare_helper(t1 == t2, "Compared values are not the same", - nullptr, nullptr, actual, expected, file, line); + actual, expected, file, line); } inline bool qCompare(QPixmap const &t1, QPixmap const &t2, const char *actual, const char *expected, @@ -164,17 +164,17 @@ inline bool qCompare(QPixmap const &t1, QPixmap const &t2, const char *actual, c qsnprintf(msg, 1024, "Compared QPixmaps differ.\n" " Actual (%s).isNull(): %d\n" " Expected (%s).isNull(): %d", actual, t1Null, expected, t2Null); - return compare_helper(false, msg, nullptr, nullptr, actual, expected, file, line); + return compare_helper(false, msg, actual, expected, file, line); } if (t1Null && t2Null) - return compare_helper(true, nullptr, nullptr, nullptr, actual, expected, file, line); + return compare_helper(true, nullptr, actual, expected, file, line); if (!qFuzzyCompare(t1.devicePixelRatio(), t2.devicePixelRatio())) { qsnprintf(msg, 1024, "Compared QPixmaps differ in device pixel ratio.\n" " Actual (%s): %g\n" " Expected (%s): %g", actual, t1.devicePixelRatio(), expected, t2.devicePixelRatio()); - return compare_helper(false, msg, nullptr, nullptr, actual, expected, file, line); + return compare_helper(false, msg, actual, expected, file, line); } if (t1.width() != t2.width() || t1.height() != t2.height()) { qsnprintf(msg, 1024, "Compared QPixmaps differ in size.\n" @@ -182,7 +182,7 @@ inline bool qCompare(QPixmap const &t1, QPixmap const &t2, const char *actual, c " Expected (%s): %dx%d", actual, t1.width(), t1.height(), expected, t2.width(), t2.height()); - return compare_helper(false, msg, nullptr, nullptr, actual, expected, file, line); + return compare_helper(false, msg, actual, expected, file, line); } return qCompare(t1.toImage(), t2.toImage(), actual, expected, file, line); } diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 919447cce30..7eeacccf393 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -2904,7 +2904,9 @@ void QTest::setMainSourcePath(const char *file, const char *builddir) QTest::mainSourcePath = fi.absolutePath(); } +#if QT_DEPRECATED_SINCE(6, 4) /*! \internal + \deprecated [6.4] This function is called by various specializations of QTest::qCompare to decide whether to report a failure and to produce verbose test output. @@ -2912,15 +2914,61 @@ void QTest::setMainSourcePath(const char *file, const char *builddir) will be output if the compare fails. If the compare succeeds, failureMsg will not be output. - If the caller has already passed a failure message showing the compared - values, or if those values cannot be stringified, val1 and val2 can be null. + Using this function is not optimal, because it requires the string + representations of \a actualVal and \a expectedVal to be pre-calculated, + even though they will be used only if the comparison fails. Prefer using the + \l compare_helper() overload that takes qxp::function_ref() for such cases. + + If the caller creates a custom failure message showing the compared values, + or if those values cannot be stringified, use the overload of the function + that takes no \a actualVal and \a expecetedVal parameters. */ bool QTest::compare_helper(bool success, const char *failureMsg, - char *val1, char *val2, + char *actualVal, char *expectedVal, const char *actual, const char *expected, const char *file, int line) { - return QTestResult::compare(success, failureMsg, val1, val2, actual, expected, file, line); + return QTestResult::compare(success, failureMsg, actualVal, expectedVal, + actual, expected, file, line); +} +#endif // QT_DEPRECATED_SINCE(6, 4) + +/*! \internal + This function is called by various specializations of QTest::qCompare + to decide whether to report a failure and to produce verbose test output. + + The \a failureMsg parameter can be \c {nullptr}, in which case a default + message will be output if the compare fails. If the comparison succeeds, + \a failureMsg will not be output. + + This overload of the function uses qxp::function_ref to defer conversion of + \a actualVal and \a expectedVal to strings until that is really needed + (when the comparison fails). This speeds up test case execution on success. +*/ +bool QTest::compare_helper(bool success, const char *failureMsg, + qxp::function_ref actualVal, + qxp::function_ref expectedVal, + const char *actual, const char *expected, + const char *file, int line) +{ + return QTestResult::reportResult(success, actualVal, expectedVal, actual, expected, + QTest::ComparisonOperation::CustomCompare, + file, line, failureMsg); +} + +/*! \internal + This function is called by various specializations of QTest::qCompare + to decide whether to report a failure and to produce verbose test output. + + This overload should be used when there is no string representation of + actual and expected values, so only the \a failureMsg is shown when the + comparison fails. Because of that, \a failureMsg can't be \c {nullptr}. + If the comparison succeeds, \a failureMsg will not be output. +*/ +bool QTest::compare_helper(bool success, const char *failureMsg, const char *actual, + const char *expected, const char *file, int line) +{ + return QTestResult::compare(success, failureMsg, actual, expected, file, line); } template @@ -2950,7 +2998,8 @@ bool QTest::qCompare(qfloat16 const &t1, qfloat16 const &t2, const char *actual, { return compare_helper(floatingCompare(t1, t2), "Compared qfloat16s are not the same (fuzzy compare)", - toString(t1), toString(t2), actual, expected, file, line); + [&t1] { return toString(t1); }, [&t2] { return 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) @@ -3272,7 +3321,8 @@ bool QTest::compare_string_helper(const char *t1, const char *t2, const char *ac const char *expected, const char *file, int line) { return compare_helper(qstrcmp(t1, t2) == 0, "Compared strings are not the same", - toString(t1), toString(t2), actual, expected, file, line); + [t1] { return toString(t1); }, [t2] { return toString(t2); }, + actual, expected, file, line); } /*! diff --git a/src/testlib/qtestcase.h b/src/testlib/qtestcase.h index d4caf20e699..ab64d712f9d 100644 --- a/src/testlib/qtestcase.h +++ b/src/testlib/qtestcase.h @@ -405,10 +405,26 @@ namespace QTest Q_TESTLIB_EXPORT Qt::Key asciiToKey(char ascii); Q_TESTLIB_EXPORT char keyToAscii(Qt::Key key); + // ### TODO: remove QTestResult::compare() overload that takes char * values + // when this overload is removed. +#if QT_DEPRECATED_SINCE(6, 4) + QT_DEPRECATED_VERSION_X_6_4("use an overload that takes function_ref as parameters, " + "or an overload that takes only failure message, if you " + "do not need to stringify the values") Q_TESTLIB_EXPORT bool compare_helper(bool success, const char *failureMsg, - char *val1, char *val2, + char *actualVal, char *expectedVal, const char *actual, const char *expected, const char *file, int line); +#endif // QT_DEPRECATED_SINCE(6, 4) + Q_TESTLIB_EXPORT bool compare_helper(bool success, const char *failureMsg, + qxp::function_ref actualVal, + qxp::function_ref expectedVal, + const char *actual, const char *expected, + const char *file, int line); + Q_TESTLIB_EXPORT bool compare_helper(bool success, const char *failureMsg, + const char *actual, const char *expected, + const char *file, int line); + Q_TESTLIB_EXPORT void addColumnInternal(int id, const char *name); template @@ -484,42 +500,48 @@ namespace QTest const char *expected, const char *file, int line) { return compare_helper(t1 == t2, "Compared pointers are not the same", - toString(t1), toString(t2), actual, expected, file, line); + [t1] { return toString(t1); }, [t2] { return toString(t2); }, + actual, expected, file, line); } inline bool compare_ptr_helper(const volatile QObject *t1, const volatile QObject *t2, const char *actual, const char *expected, const char *file, int line) { return compare_helper(t1 == t2, "Compared QObject pointers are not the same", - toString(t1), toString(t2), actual, expected, file, line); + [t1] { return toString(t1); }, [t2] { return toString(t2); }, + actual, expected, file, line); } inline bool compare_ptr_helper(const volatile QObject *t1, std::nullptr_t, const char *actual, const char *expected, const char *file, int line) { return compare_helper(t1 == nullptr, "Compared QObject pointers are not the same", - toString(t1), toString(nullptr), actual, expected, file, line); + [t1] { return toString(t1); }, [] { return toString(nullptr); }, + actual, expected, file, line); } inline bool compare_ptr_helper(std::nullptr_t, const volatile QObject *t2, const char *actual, const char *expected, const char *file, int line) { return compare_helper(nullptr == t2, "Compared QObject pointers are not the same", - toString(nullptr), toString(t2), actual, expected, file, line); + [] { return toString(nullptr); }, [t2] { return toString(t2); }, + actual, expected, file, line); } inline bool compare_ptr_helper(const volatile void *t1, std::nullptr_t, const char *actual, const char *expected, const char *file, int line) { return compare_helper(t1 == nullptr, "Compared pointers are not the same", - toString(t1), toString(nullptr), actual, expected, file, line); + [t1] { return toString(t1); }, [] { return toString(nullptr); }, + actual, expected, file, line); } inline bool compare_ptr_helper(std::nullptr_t, const volatile void *t2, const char *actual, const char *expected, const char *file, int line) { return compare_helper(nullptr == t2, "Compared pointers are not the same", - toString(nullptr), toString(t2), actual, expected, file, line); + [] { return toString(nullptr); }, [t2] { return toString(t2); }, + actual, expected, file, line); } Q_TESTLIB_EXPORT bool compare_string_helper(const char *t1, const char *t2, const char *actual, @@ -551,7 +573,8 @@ namespace QTest const char *file, int line) { return compare_helper(t1 == t2, "Compared values are not the same", - toString(t1), toString(t2), actual, expected, file, line); + [&t1] { return toString(t1); }, [&t2] { return toString(t2); }, + actual, expected, file, line); } inline bool qCompare(double const &t1, float const &t2, const char *actual, diff --git a/src/testlib/qtestresult.cpp b/src/testlib/qtestresult.cpp index 22d8f114241..5c947301cc3 100644 --- a/src/testlib/qtestresult.cpp +++ b/src/testlib/qtestresult.cpp @@ -402,6 +402,38 @@ static bool compareHelper(bool success, const char *failureMsg, return checkStatement(success, msg, file, line); } +// A simplified version of compareHelper that does not use string +// representations of the values, and prints only failureMsg when the +// comparison fails. +static bool compareHelper(bool success, const char *failureMsg, + const char *actual, const char *expected, + const char *file, int line) +{ + const size_t maxMsgLen = 1024; + char msg[maxMsgLen]; + msg[0] = '\0'; + + QTEST_ASSERT(expected); + QTEST_ASSERT(actual); + // failureMsg can be null, if we do not use it + QTEST_ASSERT(success || failureMsg); + + if (QTestLog::verboseLevel() >= 2) { + qsnprintf(msg, maxMsgLen, "QCOMPARE(%s, %s)", actual, expected); + QTestLog::info(msg, file, line); + } + + if (success) { + if (QTest::expectFailMode) { + qsnprintf(msg, maxMsgLen, "QCOMPARE(%s, %s) returned TRUE unexpectedly.", + actual, expected); + } + return checkStatement(success, msg, file, line); + } + + return checkStatement(success, failureMsg, file, line); +} + bool QTestResult::compare(bool success, const char *failureMsg, char *val1, char *val2, const char *actual, const char *expected, @@ -486,6 +518,15 @@ bool QTestResult::compare(bool success, const char *failureMsg, return compareHelper(success, failureMsg, val1, val2, actual, expected, file, line); } +// Simplified version of compare() that does not take the values, because they +// can't be converted to strings (or the user didn't want to do that). +bool QTestResult::compare(bool success, const char *failureMsg, + const char *actual, const char *expeceted, + const char *file, int line) +{ + return compareHelper(success, failureMsg, actual, expeceted, file, line); +} + void QTestResult::addFailure(const char *message, const char *file, int line) { clearExpectFail(); @@ -584,7 +625,8 @@ static const char *failureMessageForOp(QTest::ComparisonOperation op) bool QTestResult::reportResult(bool success, qxp::function_ref lhs, qxp::function_ref rhs, const char *lhsExpr, const char *rhsExpr, - QTest::ComparisonOperation op, const char *file, int line) + QTest::ComparisonOperation op, const char *file, int line, + const char *failureMessage) { const size_t maxMsgLen = 1024; char msg[maxMsgLen] = {'\0'}; @@ -608,7 +650,10 @@ bool QTestResult::reportResult(bool success, qxp::function_ref l const std::unique_ptr lhsPtr{ lhs() }; const std::unique_ptr rhsPtr{ rhs() }; - formatFailMessage(msg, maxMsgLen, failureMessageForOp(op), lhsPtr.get(), rhsPtr.get(), + if (!failureMessage) + failureMessage = failureMessageForOp(op); + + formatFailMessage(msg, maxMsgLen, failureMessage, lhsPtr.get(), rhsPtr.get(), lhsExpr, rhsExpr, op); return checkStatement(success, msg, file, line); diff --git a/src/testlib/qtestresult_p.h b/src/testlib/qtestresult_p.h index fa4e117fb37..48c2c34611b 100644 --- a/src/testlib/qtestresult_p.h +++ b/src/testlib/qtestresult_p.h @@ -42,6 +42,9 @@ public: static void setBlacklistCurrentTest(bool b); static void addFailure(const char *message, const char *file = nullptr, int line = 0); + // ### TODO: Remove this overload when deprecated QTest::compare_overload + // is removed. Can't declare it deprecated, because it will unconditionally + // provide warnings. static bool compare(bool success, const char *failureMsg, char *val1, char *val2, const char *actual, const char *expected, @@ -80,6 +83,9 @@ public: QStringView val1, const QLatin1StringView &val2, const char *actual, const char *expected, const char *file, int line); + static bool compare(bool success, const char *failureMsg, + const char *actual, const char *expeceted, + const char *file, int line); static void setCurrentGlobalTestData(QTestData *data); static void setCurrentTestData(QTestData *data); static void setCurrentTestFunction(const char *func); @@ -99,7 +105,8 @@ public: static bool reportResult(bool success, qxp::function_ref lhs, qxp::function_ref rhs, const char *lhsExpr, const char *rhsExpr, - QTest::ComparisonOperation op, const char *file, int line); + QTest::ComparisonOperation op, const char *file, int line, + const char *failureMessage = nullptr); private: Q_DISABLE_COPY(QTestResult) diff --git a/tests/auto/corelib/io/qurlquery/tst_qurlquery.cpp b/tests/auto/corelib/io/qurlquery/tst_qurlquery.cpp index 0fc4e3902b0..43a407da9dc 100644 --- a/tests/auto/corelib/io/qurlquery/tst_qurlquery.cpp +++ b/tests/auto/corelib/io/qurlquery/tst_qurlquery.cpp @@ -69,7 +69,8 @@ static bool compare(const QueryItems &actual, const QueryItems &expected, const char *actualStr, const char *expectedStr, const char *file, int line) { return QTest::compare_helper(actual == expected, "Compared values are not the same", - qstrdup(prettyList(actual)), qstrdup(prettyList(expected).data()), + [&actual] { return qstrdup(prettyList(actual).constData()); }, + [&expected] { return qstrdup(prettyList(expected).constData()); }, actualStr, expectedStr, file, line); } diff --git a/tests/auto/testlib/selftests/expected_testlib.junitxml b/tests/auto/testlib/selftests/expected_testlib.junitxml index 8e5bd0e37e0..4a8923cff90 100644 --- a/tests/auto/testlib/selftests/expected_testlib.junitxml +++ b/tests/auto/testlib/selftests/expected_testlib.junitxml @@ -7,7 +7,10 @@ - + + ]]> + diff --git a/tests/auto/testlib/selftests/expected_testlib.lightxml b/tests/auto/testlib/selftests/expected_testlib.lightxml index c4b7924e38d..7d0c1f66c67 100644 --- a/tests/auto/testlib/selftests/expected_testlib.lightxml +++ b/tests/auto/testlib/selftests/expected_testlib.lightxml @@ -9,7 +9,9 @@ - + ]]> diff --git a/tests/auto/testlib/selftests/expected_testlib.tap b/tests/auto/testlib/selftests/expected_testlib.tap index 020006904f2..ed421f12417 100644 --- a/tests/auto/testlib/selftests/expected_testlib.tap +++ b/tests/auto/testlib/selftests/expected_testlib.tap @@ -3,7 +3,12 @@ TAP version 13 ok 1 - initTestCase() not ok 2 - basics() --- - # Compared QObject pointers are not the same + type: QCOMPARE + message: Compared QObject pointers are not the same + wanted: (nullptr) + found: tst_TestLib/"TestObject" (QTest::testObject()) + expected: (nullptr) + actual: tst_TestLib/"TestObject" (QTest::testObject()) at: tst_TestLib::basics() (qtbase/tests/auto/testlib/selftests/testlib/tst_testlib.cpp:0) file: qtbase/tests/auto/testlib/selftests/testlib/tst_testlib.cpp line: 0 diff --git a/tests/auto/testlib/selftests/expected_testlib.teamcity b/tests/auto/testlib/selftests/expected_testlib.teamcity index b72dfde420a..44558008268 100644 --- a/tests/auto/testlib/selftests/expected_testlib.teamcity +++ b/tests/auto/testlib/selftests/expected_testlib.teamcity @@ -2,7 +2,7 @@ ##teamcity[testStarted name='initTestCase()' flowId='tst_TestLib'] ##teamcity[testFinished name='initTestCase()' flowId='tst_TestLib'] ##teamcity[testStarted name='basics()' flowId='tst_TestLib'] -##teamcity[testFailed name='basics()' message='Failure! |[Loc: qtbase/tests/auto/testlib/selftests/testlib/tst_testlib.cpp(0)|]' details='Compared QObject pointers are not the same' flowId='tst_TestLib'] +##teamcity[testFailed name='basics()' message='Failure! |[Loc: qtbase/tests/auto/testlib/selftests/testlib/tst_testlib.cpp(0)|]' details='Compared QObject pointers are not the same|n Actual (QTest::testObject()): tst_TestLib/"TestObject"|n Expected (nullptr) : ' flowId='tst_TestLib'] ##teamcity[testFinished name='basics()' flowId='tst_TestLib'] ##teamcity[testStarted name='delays()' flowId='tst_TestLib'] ##teamcity[testFinished name='delays()' flowId='tst_TestLib'] diff --git a/tests/auto/testlib/selftests/expected_testlib.txt b/tests/auto/testlib/selftests/expected_testlib.txt index b8437fa8b45..98ca6ecf115 100644 --- a/tests/auto/testlib/selftests/expected_testlib.txt +++ b/tests/auto/testlib/selftests/expected_testlib.txt @@ -2,6 +2,8 @@ Config: Using QtTest library PASS : tst_TestLib::initTestCase() FAIL! : tst_TestLib::basics() Compared QObject pointers are not the same + Actual (QTest::testObject()): tst_TestLib/"TestObject" + Expected (nullptr) : Loc: [qtbase/tests/auto/testlib/selftests/testlib/tst_testlib.cpp(0)] PASS : tst_TestLib::delays() PASS : tst_TestLib::reals(zero) diff --git a/tests/auto/testlib/selftests/expected_testlib.xml b/tests/auto/testlib/selftests/expected_testlib.xml index b5e1c078f1e..a31259a9e22 100644 --- a/tests/auto/testlib/selftests/expected_testlib.xml +++ b/tests/auto/testlib/selftests/expected_testlib.xml @@ -11,7 +11,9 @@ - + ]]> diff --git a/tests/auto/testlib/selftests/testlib/tst_testlib.cpp b/tests/auto/testlib/selftests/testlib/tst_testlib.cpp index 598049efddf..435c72d7ff2 100644 --- a/tests/auto/testlib/selftests/testlib/tst_testlib.cpp +++ b/tests/auto/testlib/selftests/testlib/tst_testlib.cpp @@ -14,7 +14,11 @@ class tst_TestLib : public QObject { -Q_OBJECT + Q_OBJECT + +public: + tst_TestLib(); + private slots: void basics() const; void delays() const; @@ -22,6 +26,14 @@ private slots: void reals() const; }; +tst_TestLib::tst_TestLib() +{ + // Set object name, so that it's printed out when some comparison fails. + // Othewise object address will be printed, which will not allow + // tst_sefltest to compare the output with expected. + setObjectName("TestObject"); +} + void tst_TestLib::basics() const { QVERIFY(QByteArray(QTest::currentAppName()).contains("testlib"));