diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/corelib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/testlib/qtestcase.cpp | 8 | ||||
-rw-r--r-- | src/testlib/qtestcase.h | 32 | ||||
-rw-r--r-- | src/testlib/qtestcase.qdoc | 153 | ||||
-rw-r--r-- | src/testlib/qtestresult.cpp | 116 | ||||
-rw-r--r-- | src/testlib/qtestresult_p.h | 6 | ||||
-rw-r--r-- | src/testlib/qttestglobal.h | 9 |
7 files changed, 307 insertions, 18 deletions
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 47b9bd0d117..7f5417430a0 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -75,6 +75,7 @@ qt_internal_add_module(Core global/q23functional.h global/qxpfunctional.h global/q20iterator.h + global/qxpfunctional.h io/qabstractfileengine.cpp io/qabstractfileengine_p.h io/qbuffer.cpp io/qbuffer.h io/qdataurl.cpp io/qdataurl_p.h diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp index 7760ab8f8c7..919447cce30 100644 --- a/src/testlib/qtestcase.cpp +++ b/src/testlib/qtestcase.cpp @@ -1728,6 +1728,14 @@ void TestMethods::invokeTests(QObject *testObject) const QSignalDumper::endDump(); } +bool reportResult(bool success, qxp::function_ref<const char *()> lhs, + qxp::function_ref<const char *()> rhs, + const char *lhsExpr, const char *rhsExpr, + ComparisonOperation op, const char *file, int line) +{ + return QTestResult::reportResult(success, lhs, rhs, lhsExpr, rhsExpr, op, file, line); +} + } // namespace QTest namespace { diff --git a/src/testlib/qtestcase.h b/src/testlib/qtestcase.h index 892a10cef3b..555458e0419 100644 --- a/src/testlib/qtestcase.h +++ b/src/testlib/qtestcase.h @@ -13,6 +13,7 @@ #include <QtCore/qsharedpointer.h> #include <QtCore/qtemporarydir.h> #include <QtCore/qthread.h> +#include <QtCore/qxpfunctional.h> #include <string.h> @@ -54,6 +55,32 @@ do {\ return;\ } while (false) +// A wrapper lambda is introduced to extend the lifetime of lhs and rhs in +// case they are temporary objects. +// We also use IILE to prevent potential name clashes and shadowing of variables +// from user code. A drawback of the approach is that it looks ugly :( +#define QCOMPARE_OP_IMPL(lhs, rhs, op, opId) \ +do { \ + if ( { \ + /* assumes that op does not actually move from qt_{lhs, rhs}_arg */ \ + return QTest::reportResult(std::forward<decltype(qt_lhs_arg)>(qt_lhs_arg) \ + op \ + std::forward<decltype(qt_rhs_arg)>(qt_rhs_arg), \ + [&qt_lhs_arg] { return QTest::toString(qt_lhs_arg); }, \ + [&qt_rhs_arg] { return QTest::toString(qt_rhs_arg); }, \ + #lhs, #rhs, QTest::ComparisonOperation::opId, \ + __FILE__, __LINE__); \ + }(lhs, rhs)) { \ + return; \ + } \ +} 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) #ifndef QT_NO_EXCEPTIONS @@ -574,6 +601,11 @@ namespace QTest return qCompare(actual, *static_cast<const T *>(QTest::qElementData(elementName, qMetaTypeId<T>())), actualStr, expected, file, line); } + + Q_TESTLIB_EXPORT bool reportResult(bool success, qxp::function_ref<const char*()> lhs, + qxp::function_ref<const char*()> rhs, + const char *lhsExpr, const char *rhsExpr, + ComparisonOperation op, const char *file, int line); } #undef QTEST_COMPARE_DECL diff --git a/src/testlib/qtestcase.qdoc b/src/testlib/qtestcase.qdoc index 2133fec97c6..171a0b906cd 100644 --- a/src/testlib/qtestcase.qdoc +++ b/src/testlib/qtestcase.qdoc @@ -22,8 +22,10 @@ You can use \l QVERIFY2() when it is practical and valuable to put additional information into the test failure report. +//! [macro-usage-limitation] \note This macro can only be used in a test function that is invoked by the test framework. +//! [macro-usage-limitation] For example, the following code shows this macro being used to verify that a \l QSignalSpy object is valid: @@ -34,7 +36,8 @@ \c QVERIFY(x == y), because it reports both the expected and actual value when the comparison fails. - \sa QCOMPARE(), QTRY_VERIFY(), QSignalSpy, QEXPECT_FAIL() + \sa QCOMPARE(), QTRY_VERIFY(), QSignalSpy, QEXPECT_FAIL(), QCOMPARE_EQ(), + QCOMPARE_NE(), QCOMPARE_LT(), QCOMPARE_LE(), QCOMPARE_GT(), QCOMPARE_GE() */ /*! \macro QVERIFY2(condition, message) @@ -73,7 +76,8 @@ \c {FAIL! : tst_QFile::open_write() 'opened' returned FALSE. (open /tmp/qt.a3B42Cd: No space left on device)} - \sa QVERIFY(), QCOMPARE(), QEXPECT_FAIL() + \sa QVERIFY(), QCOMPARE(), QEXPECT_FAIL(), QCOMPARE_EQ(), QCOMPARE_NE(), + QCOMPARE_LT(), QCOMPARE_LE(), QCOMPARE_GT(), QCOMPARE_GE() */ /*! \macro QCOMPARE(actual, expected) @@ -114,11 +118,12 @@ delimiters: \snippet code/src_qtestlib_qtestcase.cpp 35 - \note QCOMPARE() can only be used in a test function that is invoked - by the test framework. + \include qtestcase.qdoc macro-usage-limitation - For your own classes, you can use \l QTest::toString() to format values for - outputting into the test log. +//! [to-string-overload-desc] + For your own classes, you can overload \l QTest::toString() to format values + for output into the test log. +//! [to-string-overload-desc] Example: \snippet code/src_qtestlib_qtestcase_snippet.cpp 34 @@ -127,7 +132,141 @@ be released with \c delete[] (rather than \c free() or plain \c delete) once the calling code is done with it. - \sa QVERIFY(), QTRY_COMPARE(), QTest::toString(), QEXPECT_FAIL() + \sa QVERIFY(), QTRY_COMPARE(), QTest::toString(), QEXPECT_FAIL(), + QCOMPARE_EQ(), QCOMPARE_NE(), QCOMPARE_LT(), QCOMPARE_LE(), + QCOMPARE_GT(), QCOMPARE_GE() +*/ + +/*! \macro QCOMPARE_EQ(left, right) + \since 6.4 + + \relates QTest + + The QCOMPARE_EQ() macro checks that \a left is equal to \a right using + the equality operator. If that is true, execution continues. If not, a + failure is recorded in the test log and the test function returns without + attempting any later checks. + + It is generally similar to calling \c {QVERIFY(left == right);} + but prints a formatted error message reporting \a left and \a right argument + expressions and values in case of failure. + + \include qtestcase.qdoc macro-usage-limitation + + \include qtestcase.qdoc to-string-overload-desc + + \note Unlike QCOMPARE(), this macro does not provide overloads for custom + types and pointers. So passing e.g. two \c {const char *} values as + parameters will compare \e pointers, while QCOMPARE() does a comparison of + C-style strings. + + \sa QCOMPARE(), QCOMPARE_NE(), QCOMPARE_LT(), QCOMPARE_LE(), QCOMPARE_GT(), + QCOMPARE_GE() +*/ + +/*! \macro QCOMPARE_NE(left, right) + \since 6.4 + + \relates QTest + + The QCOMPARE_NE() macro checks that \a left is not equal to \a right using + the inequality operator. If that is true, execution continues. If not, a + failure is recorded in the test log and the test function returns without + attempting any later checks. + + It is generally similar to calling \c {QVERIFY(left != right);} + but prints a formatted error message reporting \a left and \a right argument + expressions and values in case of failure. + + \include qtestcase.qdoc macro-usage-limitation + + \include qtestcase.qdoc to-string-overload-desc + + \sa QCOMPARE_EQ(), QCOMPARE_LT(), QCOMPARE_LE(), QCOMPARE_GT(), QCOMPARE_GE() +*/ + +/*! \macro QCOMPARE_LT(left, right) + \since 6.4 + + \relates QTest + + The QCOMPARE_LT() macro checks that \a left is less than \a right using the + less-than operator. If that is true, execution continues. If not, a failure + is recorded in the test log and the test function returns without attempting + any later checks. + + It is generally similar to calling \c {QVERIFY(left < right);} + but prints a formatted error message reporting \a left and \a right argument + expressions and values in case of failure. + + \include qtestcase.qdoc macro-usage-limitation + + \include qtestcase.qdoc to-string-overload-desc + + \sa QCOMPARE_EQ(), QCOMPARE_NE(), QCOMPARE_LE(), QCOMPARE_GT(), QCOMPARE_GE() +*/ + +/*! \macro QCOMPARE_LE(left, right) + \since 6.4 + + \relates QTest + + The QCOMPARE_LE() macro checks that \a left is at most \a right using the + less-than-or-equal-to operator. If that is true, execution continues. If + not, a failure is recorded in the test log and the test function returns + without attempting any later checks. + + It is generally similar to calling \c {QVERIFY(left <= right);} + but prints a formatted error message reporting \a left and \a right argument + expressions and values in case of failure. + + \include qtestcase.qdoc macro-usage-limitation + + \include qtestcase.qdoc to-string-overload-desc + + \sa QCOMPARE_EQ(), QCOMPARE_NE(), QCOMPARE_LT(), QCOMPARE_GT(), QCOMPARE_GE() +*/ + +/*! \macro QCOMPARE_GT(left, right) + \since 6.4 + + \relates QTest + + The QCOMPARE_GT() macro checks that \a left is greater than \a right using + the greater-than operator. If that is true, execution continues. If not, a + failure is recorded in the test log and the test function returns without + attempting any later checks. + + It is generally similar to calling \c {QVERIFY(left > right);} + but prints a formatted error message reporting \a left and \a right argument + expressions and values in case of failure. + + \include qtestcase.qdoc macro-usage-limitation + + \include qtestcase.qdoc to-string-overload-desc + + \sa QCOMPARE_EQ(), QCOMPARE_NE(), QCOMPARE_LT(), QCOMPARE_LE(), QCOMPARE_GE() +*/ + +/*! \macro QCOMPARE_GE(left, right) + \since 6.4 + + \relates QTest + + The QCOMPARE_GE() macro checks that \a left is at least \a right using the + greater-than-or-equal-to operator. If that is true, execution continues. If + not, a failure is recorded in the test log and the test function returns + without attempting any later checks. + + It is generally similar to calling \c {QVERIFY(left >= right);} + but prints a formatted error message reporting \a left and \a right argument + expressions and values in case of failure. + + \include qtestcase.qdoc macro-usage-limitation + + \include qtestcase.qdoc to-string-overload-desc + + \sa QCOMPARE_EQ(), QCOMPARE_NE(), QCOMPARE_LT(), QCOMPARE_LE(), QCOMPARE_GT() */ /*! \macro QVERIFY_EXCEPTION_THROWN(expression, exceptiontype) diff --git a/src/testlib/qtestresult.cpp b/src/testlib/qtestresult.cpp index 40e76146ba1..22d8f114241 100644 --- a/src/testlib/qtestresult.cpp +++ b/src/testlib/qtestresult.cpp @@ -311,22 +311,34 @@ bool QTestResult::verify(bool statement, const char *statementStr, return checkStatement(statement, msg, file, line); } +static const char *leftArgNameForOp(QTest::ComparisonOperation op) +{ + return op == QTest::ComparisonOperation::CustomCompare ? "Actual " : "Left "; +} + +static const char *rightArgNameForOp(QTest::ComparisonOperation op) +{ + return op == QTest::ComparisonOperation::CustomCompare ? "Expected " : "Right "; +} + // Format failures using the toString() template template <class Actual, class Expected> void formatFailMessage(char *msg, size_t maxMsgLen, const char *failureMsg, const Actual &val1, const Expected &val2, - const char *actual, const char *expected) + const char *actual, const char *expected, + QTest::ComparisonOperation op) { auto val1S = QTest::toString(val1); auto val2S = QTest::toString(val2); size_t len1 = mbstowcs(nullptr, actual, maxMsgLen); // Last parameter is not ignored on QNX size_t len2 = mbstowcs(nullptr, expected, maxMsgLen); // (result is never larger than this). - qsnprintf(msg, maxMsgLen, "%s\n Actual (%s)%*s %s\n Expected (%s)%*s %s", - failureMsg, - actual, qMax(len1, len2) - len1 + 1, ":", val1S ? val1S : "<null>", - expected, qMax(len1, len2) - len2 + 1, ":", val2S ? val2S : "<null>"); + qsnprintf(msg, maxMsgLen, "%s\n %s(%s)%*s %s\n %s(%s)%*s %s", failureMsg, + leftArgNameForOp(op), actual, qMax(len1, len2) - len1 + 1, ":", + val1S ? val1S : "<null>", + rightArgNameForOp(op), expected, qMax(len1, len2) - len2 + 1, ":", + val2S ? val2S : "<null>"); delete [] val1S; delete [] val2S; @@ -336,14 +348,16 @@ void formatFailMessage(char *msg, size_t maxMsgLen, void formatFailMessage(char *msg, size_t maxMsgLen, const char *failureMsg, const char *val1, const char *val2, - const char *actual, const char *expected) + const char *actual, const char *expected, + QTest::ComparisonOperation op) { size_t len1 = mbstowcs(nullptr, actual, maxMsgLen); // Last parameter is not ignored on QNX size_t len2 = mbstowcs(nullptr, expected, maxMsgLen); // (result is never larger than this). - qsnprintf(msg, maxMsgLen, "%s\n Actual (%s)%*s %s\n Expected (%s)%*s %s", - failureMsg, - actual, qMax(len1, len2) - len1 + 1, ":", val1 ? val1 : "<null>", - expected, qMax(len1, len2) - len2 + 1, ":", val2 ? val2 : "<null>"); + qsnprintf(msg, maxMsgLen, "%s\n %s(%s)%*s %s\n %s(%s)%*s %s", failureMsg, + leftArgNameForOp(op), actual, qMax(len1, len2) - len1 + 1, ":", + val1 ? val1 : "<null>", + rightArgNameForOp(op), expected, qMax(len1, len2) - len2 + 1, ":", + val2 ? val2 : "<null>"); } template <class Actual, class Expected> @@ -382,7 +396,8 @@ static bool compareHelper(bool success, const char *failureMsg, return checkStatement(success, msg, file, line); } - formatFailMessage(msg, maxMsgLen, failureMsg, val1, val2, actual, expected); + formatFailMessage(msg, maxMsgLen, failureMsg, val1, val2, actual, expected, + QTest::ComparisonOperation::CustomCompare); return checkStatement(success, msg, file, line); } @@ -520,4 +535,83 @@ const char *QTestResult::currentAppName() return ::currentAppName; } +static const char *macroNameForOp(QTest::ComparisonOperation op) +{ + using namespace QTest; + switch (op) { + case ComparisonOperation::CustomCompare: + return "QCOMPARE"; /* not used */ + case ComparisonOperation::Equal: + return "QCOMPARE_EQ"; + case ComparisonOperation::NotEqual: + return "QCOMPARE_NE"; + case ComparisonOperation::LessThan: + return "QCOMPARE_LT"; + case ComparisonOperation::LessThanOrEqual: + return "QCOMPARE_LE"; + case ComparisonOperation::GreaterThan: + return "QCOMPARE_GT"; + case ComparisonOperation::GreaterThanOrEqual: + return "QCOMPARE_GE"; + } + Q_UNREACHABLE(); + return ""; +} + +static const char *failureMessageForOp(QTest::ComparisonOperation op) +{ + using namespace QTest; + switch (op) { + case ComparisonOperation::CustomCompare: + return "Compared values are not the same"; /* not used */ + case ComparisonOperation::Equal: + return "Left value is expected to be equal to right value, but is not"; + case ComparisonOperation::NotEqual: + return "Left value is expected to be different from right value, but is not"; + case ComparisonOperation::LessThan: + return "Left value is expected to be less than right value, but is not"; + case ComparisonOperation::LessThanOrEqual: + return "Left value is expected to be less than or equal to right value, but is not"; + case ComparisonOperation::GreaterThan: + return "Left value is expected to be greater than right value, but is not"; + case ComparisonOperation::GreaterThanOrEqual: + return "Left value is expected to be greater than or equal to right value, but is not"; + } + Q_UNREACHABLE(); + return ""; +} + +bool QTestResult::reportResult(bool success, qxp::function_ref<const char *()> lhs, + qxp::function_ref<const char *()> rhs, + const char *lhsExpr, const char *rhsExpr, + QTest::ComparisonOperation op, const char *file, int line) +{ + const size_t maxMsgLen = 1024; + char msg[maxMsgLen] = {'\0'}; + + QTEST_ASSERT(lhsExpr); + QTEST_ASSERT(rhsExpr); + + if (QTestLog::verboseLevel() >= 2) { + qsnprintf(msg, maxMsgLen, "%s(%s, %s)", macroNameForOp(op), lhsExpr, rhsExpr); + QTestLog::info(msg, file, line); + } + + if (success) { + if (QTest::expectFailMode) { + qsnprintf(msg, maxMsgLen, "%s(%s, %s) returned TRUE unexpectedly.", + macroNameForOp(op), lhsExpr, rhsExpr); + } + return checkStatement(success, msg, file, line); + } + + const std::unique_ptr<const char[]> lhsPtr{ lhs() }; + const std::unique_ptr<const char[]> rhsPtr{ rhs() }; + + formatFailMessage(msg, maxMsgLen, failureMessageForOp(op), lhsPtr.get(), rhsPtr.get(), + lhsExpr, rhsExpr, op); + + return checkStatement(success, msg, file, line); +} + QT_END_NAMESPACE diff --git a/src/testlib/qtestresult_p.h b/src/testlib/qtestresult_p.h index fac81b5e80f..fa4e117fb37 100644 --- a/src/testlib/qtestresult_p.h +++ b/src/testlib/qtestresult_p.h @@ -17,6 +17,7 @@ #include <QtTest/qttestglobal.h> #include <QtCore/qstringfwd.h> +#include <QtCore/qxpfunctional.h> #include <QtCore/private/qglobal_p.h> QT_BEGIN_NAMESPACE @@ -95,6 +96,11 @@ public: static void setCurrentAppName(const char *appName); static const char *currentAppName(); + static bool reportResult(bool success, qxp::function_ref<const char *()> lhs, + qxp::function_ref<const char *()> rhs, + const char *lhsExpr, const char *rhsExpr, + QTest::ComparisonOperation op, const char *file, int line); + private: Q_DISABLE_COPY(QTestResult) }; diff --git a/src/testlib/qttestglobal.h b/src/testlib/qttestglobal.h index 4557eb5a070..8ede78c2a2f 100644 --- a/src/testlib/qttestglobal.h +++ b/src/testlib/qttestglobal.h @@ -23,6 +23,15 @@ QT_BEGIN_NAMESPACE namespace QTest { enum TestFailMode { Abort = 1, Continue = 2 }; + enum class ComparisonOperation { + CustomCompare, /* Used for QCOMPARE() */ + Equal, + NotEqual, + LessThan, + LessThanOrEqual, + GreaterThan, + GreaterThanOrEqual, + }; } QT_END_NAMESPACE |