QTest: rip out qxp::function_ref from compare_helper()

[ChangeLog][QtTest] The QCOMPARE_xx macros can now only find
QTest::toString() expansions that are either found via Argument
Dependent Lookup on the type in question or are an instatiation of the
QTest::toString<T>() template. This matches the behavior of the
QCOMPARE() macro.

This changes the way how the toString() overloads are selected, so
we need to explicilty constraint the main QTest::toString() template
in order to pick the free functions when they exist.

Change-Id: Ie28eadac333c4bcd8c08fffd17c54e768c5cffd0
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
This commit is contained in:
Thiago Macieira 2024-04-11 11:51:44 -07:00 committed by Ivan Solovev
parent c14f399d2a
commit 0756cc1eae
6 changed files with 100 additions and 28 deletions

View File

@ -2748,6 +2748,7 @@ bool QTest::compare_helper(bool success, const char *failureMsg,
}
#endif // QT_DEPRECATED_SINCE(6, 4)
#if QT_DEPRECATED_SINCE(6, 8)
/*! \internal
\since 6.4
This function is called by various specializations of QTest::qCompare
@ -2767,12 +2768,34 @@ bool QTest::compare_helper(bool success, const char *failureMsg,
const char *actual, const char *expected,
const char *file, int line)
{
auto functionRefFormatter = [](const void *f) {
auto formatter = static_cast<const qxp::function_ref<const char *()> *>(f);
return (*formatter)();
};
return QTestResult::reportResult(success, &actualVal, &expectedVal, functionRefFormatter,
functionRefFormatter, actual, expected,
return QTestResult::reportResult(success, &actualVal, &expectedVal,
QTest::functionRefFormatter,
QTest::functionRefFormatter, actual, expected,
QTest::ComparisonOperation::CustomCompare,
file, line, failureMsg);
}
#endif // QT_DEPRECATED_SINCE(6, 8)
/*! \internal
\since 6.8
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.
*/
bool QTest::compare_helper(bool success, const char *failureMsg,
const void *actualPtr, const void *expectedPtr,
const char *(*actualFormatter)(const void *),
const char *(*expectedFormatter)(const void *),
const char *actual, const char *expected,
const char *file, int line)
{
return QTestResult::reportResult(success, actualPtr, expectedPtr,
actualFormatter, expectedFormatter,
actual, expected,
QTest::ComparisonOperation::CustomCompare,
file, line, failureMsg);
}
@ -2818,9 +2841,10 @@ static bool floatingCompare(const T &actual, const T &expected)
bool QTest::qCompare(qfloat16 const &t1, qfloat16 const &t2, const char *actual, const char *expected,
const char *file, int line)
{
auto formatter = Internal::genericToString<qfloat16>;
return compare_helper(floatingCompare(t1, t2),
"Compared qfloat16s are not the same (fuzzy compare)",
[&t1] { return toString(t1); }, [&t2] { return toString(t2); },
&t1, &t2, formatter, formatter,
actual, expected, file, line);
}
@ -3137,8 +3161,9 @@ char *QTest::toString(const volatile QObject *vo)
bool QTest::compare_string_helper(const char *t1, const char *t2, const char *actual,
const char *expected, const char *file, int line)
{
auto formatter = Internal::genericToString<const char *>;
return compare_helper(qstrcmp(t1, t2) == 0, "Compared strings are not the same",
[t1] { return toString(t1); }, [t2] { return toString(t2); },
&t1, &t2, formatter, formatter,
actual, expected, file, line);
}

View File

@ -341,6 +341,12 @@ namespace QTest
return toString(static_cast<const char *>(arg));
}
template <typename T> const char *pointerToString(const void *arg)
{
using QTest::toString;
return toString(static_cast<const T *>(arg));
}
// Exported so Qt Quick Test can also use it for generating backtraces upon crashes.
Q_TESTLIB_EXPORT extern bool noCrashHandler;
@ -437,7 +443,7 @@ namespace QTest
Q_TESTLIB_EXPORT char keyToAscii(Qt::Key key);
#if QT_DEPRECATED_SINCE(6, 4)
QT_DEPRECATED_VERSION_X_6_4("use an overload that takes function_ref as parameters, "
QT_DEPRECATED_VERSION_X_6_4("use an overload that takes a formatter callback, "
"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,
@ -445,11 +451,22 @@ namespace QTest
const char *actual, const char *expected,
const char *file, int line);
#endif // QT_DEPRECATED_SINCE(6, 4)
#if QT_DEPRECATED_SINCE(6, 8)
QT_DEPRECATED_VERSION_X_6_8("use an overload that takes a formatter callback, "
"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,
qxp::function_ref<const char*()> actualVal,
qxp::function_ref<const char*()> expectedVal,
const char *actual, const char *expected,
const char *file, int line);
#endif // QT_DEPRECATED_SINCE(6, 8)
Q_TESTLIB_EXPORT bool compare_helper(bool success, const char *failureMsg,
const void *actualPtr, const void *expectedPtr,
const char *(*actualFormatter)(const void *),
const char *(*expectedFormatter)(const void *),
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);
@ -517,57 +534,71 @@ namespace QTest
inline bool compare_ptr_helper(const volatile void *t1, const volatile void *t2, const char *actual,
const char *expected, const char *file, int line)
{
auto formatter = Internal::pointerToString<void>;
return compare_helper(t1 == t2, "Compared pointers are not the same",
[t1] { return toString(t1); }, [t2] { return toString(t2); },
actual, expected, file, line);
const_cast<const void *>(t1), const_cast<const void *>(t2),
formatter, formatter, 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)
{
auto formatter = Internal::pointerToString<QObject>;
return compare_helper(t1 == t2, "Compared QObject pointers are not the same",
[t1] { return toString(t1); }, [t2] { return toString(t2); },
actual, expected, file, line);
const_cast<const QObject *>(t1), const_cast<const QObject *>(t2),
formatter, formatter, 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)
{
auto lhsFormatter = Internal::pointerToString<QObject>;
auto rhsFormatter = Internal::genericToString<std::nullptr_t>;
return compare_helper(t1 == nullptr, "Compared QObject pointers are not the same",
[t1] { return toString(t1); }, [] { return toString(nullptr); },
actual, expected, file, line);
const_cast<const QObject *>(t1), nullptr,
lhsFormatter, rhsFormatter, 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)
{
auto lhsFormatter = Internal::genericToString<std::nullptr_t>;
auto rhsFormatter = Internal::pointerToString<QObject>;
return compare_helper(nullptr == t2, "Compared QObject pointers are not the same",
[] { return toString(nullptr); }, [t2] { return toString(t2); },
actual, expected, file, line);
nullptr, const_cast<const QObject *>(t2),
lhsFormatter, rhsFormatter, 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)
{
auto lhsFormatter = Internal::pointerToString<void>;
auto rhsFormatter = Internal::genericToString<std::nullptr_t>;
return compare_helper(t1 == nullptr, "Compared pointers are not the same",
[t1] { return toString(t1); }, [] { return toString(nullptr); },
actual, expected, file, line);
const_cast<const void *>(t1), nullptr,
lhsFormatter, rhsFormatter, 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)
{
auto lhsFormatter = Internal::genericToString<std::nullptr_t>;
auto rhsFormatter = Internal::pointerToString<void>;
return compare_helper(nullptr == t2, "Compared pointers are not the same",
[] { return toString(nullptr); }, [t2] { return toString(t2); },
actual, expected, file, line);
nullptr, const_cast<const void *>(t2),
lhsFormatter, rhsFormatter, actual, expected, file, line);
}
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)
{
using D1 = std::decay_t<T1>;
using D2 = std::decay_t<T2>;
using Internal::genericToString;
return compare_helper(t1 == t2, "Compared values are not the same",
[&t1] { return toString(t1); }, [&t2] { return toString(t2); },
std::addressof(t1), std::addressof(t2),
genericToString<D1>, genericToString<D2>,
actual, expected, file, line);
}

View File

@ -1423,7 +1423,7 @@
*/
/*!
\fn template<typename T> char *QTest::toString(const T &value)
\fn template<typename T, QTest::Internal::is_suitable_type_v<T> = true> char *QTest::toString(const T &value)
Returns a textual representation of \a value. This function is used by
\l QCOMPARE() to output verbose information in case of a test failure.

View File

@ -102,8 +102,8 @@ public:
static const char *currentAppName();
static bool reportResult(bool success, const void *lhs, const void *rhs,
const char *(*lhsFormatter)(const void*),
const char *(*rhsFormatter)(const void*),
const char *(*lhsFormatter)(const void *),
const char *(*rhsFormatter)(const void *),
const char *lhsExpr, const char *rhsExpr,
QTest::ComparisonOperation op, const char *file, int line,
const char *failureMessage = nullptr);

View File

@ -79,6 +79,19 @@ inline typename std::enable_if<!QtPrivate::IsQEnumHelper<F>::Value, char*>::type
return msg;
}
template <typename T>
constexpr bool is_suitable_type_helper_v = std::disjunction_v<std::is_same<T, char>,
std::is_same<T, void>,
std::is_same<T, QObject>
>;
template <typename T>
using is_suitable_type_v =
std::enable_if_t<!(std::is_pointer_v<T>
&& is_suitable_type_helper_v<
std::remove_const_t<std::remove_pointer_t<T>>>),
bool>;
} // namespace Internal
Q_TESTLIB_EXPORT bool compare_string_helper(const char *t1, const char *t2, const char *actual,
@ -91,7 +104,7 @@ Q_TESTLIB_EXPORT char *toString(const char *);
Q_TESTLIB_EXPORT char *toString(const volatile void *);
Q_TESTLIB_EXPORT char *toString(const volatile QObject *);
template<typename T>
template<typename T, Internal::is_suitable_type_v<T> = true>
inline char *toString(const T &t)
{
return Internal::toString(t);

View File

@ -73,9 +73,12 @@ static QByteArray prettyList(const QueryItems &items)
static bool compare(const QueryItems &actual, const QueryItems &expected,
const char *actualStr, const char *expectedStr, const char *file, int line)
{
auto formatter = [](const void *val) -> const char * {
const QueryItems items = *static_cast<const QueryItems *>(val);
return qstrdup(prettyList(items).constData());
};
return QTest::compare_helper(actual == expected, "Compared values are not the same",
[&actual] { return qstrdup(prettyList(actual).constData()); },
[&expected] { return qstrdup(prettyList(expected).constData()); },
&actual, &expected, formatter, formatter,
actualStr, expectedStr, file, line);
}