summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/corelib/CMakeLists.txt1
-rw-r--r--src/testlib/qtestcase.cpp8
-rw-r--r--src/testlib/qtestcase.h32
-rw-r--r--src/testlib/qtestcase.qdoc153
-rw-r--r--src/testlib/qtestresult.cpp116
-rw-r--r--src/testlib/qtestresult_p.h6
-rw-r--r--src/testlib/qttestglobal.h9
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 (![](auto &&qt_lhs_arg, auto &&qt_rhs_arg) { \
+ /* 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