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

This is causing huge code bloat because everything is a local lambda.
Instead, pass direct type-erased function and data pointers to the
replacement function. Testing with tst_qcborvalue, this reduces the
compilation time and the output binary size significantly:

                   Before                   After
Compiler        Time      Size          Time     Size
GCC 13.2        136.99 s  202.3 MB      13.88 s  14.3 MB
GCC 14.0        131.49 s  202.7 MB      14.69 s  14.4 MB
Clang 17        77.2 s    146.7 MB      13.62 s  12.2 MB
Clang 18        141.9 s   187.1 MB      13.62 s  12.4 MB

This causes a difference in how toString() overloads are
found. Previously it would match far more overloads because the
toString() calls were expanded by the macro. Now, we depend on
Argument-Dependent Lookup and associated namespaces, so toString()
overloads should not be in the QTest namespace any more.

With this patch applied, the testlib testcase of tst_selftest
started failing, because nullptr is now handled differently.
However, I consider it as a bugfix, because previously it was
falling back to a default implementation, and now it is using
the QTest::toString(std::nullptr_t) overload, which is a
desired behavior. Update the reference files for tst_selftest
with the new expected output.

Task-number: QTBUG-124272
Change-Id: Ie28eadac333c4bcd8c08fffd17c5484186accdf6
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 09:57:59 -07:00 committed by Ivan Solovev
parent 4ae6f40b3a
commit c14f399d2a
14 changed files with 95 additions and 39 deletions

View File

@ -4,7 +4,7 @@
#ifndef QTEST_NETWORK_H
#define QTEST_NETWORK_H
#include <QtTest/qtest.h>
#include <QtTest/qtesttostring.h>
// enable NETWORK features
#ifndef QT_NETWORK_LIB
@ -43,6 +43,7 @@ inline char *toString<QHostAddress>(const QHostAddress &addr)
return toString(addr.toString());
}
} // namespace QTest
inline char *toString(QNetworkReply::NetworkError code)
{
@ -57,7 +58,7 @@ inline char *toString(QNetworkReply::NetworkError code)
inline char *toString(const QNetworkCookie &cookie)
{
return toString(cookie.toRawForm());
return QTest::toString(cookie.toRawForm());
}
inline char *toString(const QList<QNetworkCookie> &list)
@ -69,11 +70,9 @@ inline char *toString(const QList<QNetworkCookie> &list)
result.chop(2); // remove trailing ", "
}
result.append(')');
return toString(result);
return QTest::toString(result);
}
} // namespace QTest
QT_END_NAMESPACE
#endif

View File

@ -62,15 +62,16 @@ inline QByteArray toString(QSizePolicy sp)
}
} // namespace Internal
} // namespace QTest
inline char *toString(QSizePolicy::Policy p)
{
return qstrdup(Internal::toString(p));
return qstrdup(QTest::Internal::toString(p));
}
inline char *toString(QSizePolicy::ControlTypes ct)
{
return qstrdup(Internal::toString(ct).constData());
return qstrdup(QTest::Internal::toString(ct).constData());
}
inline char *toString(QSizePolicy::ControlType ct)
@ -80,11 +81,9 @@ inline char *toString(QSizePolicy::ControlType ct)
inline char *toString(QSizePolicy sp)
{
return qstrdup(Internal::toString(sp).constData());
return qstrdup(QTest::Internal::toString(sp).constData());
}
} // namespace QTest
QT_END_NAMESPACE
#endif

View File

@ -1745,14 +1745,33 @@ void TestMethods::invokeTests(QObject *testObject) const
QSignalDumper::endDump();
}
#if QT_DEPRECATED_SINCE(6, 8)
static const char *functionRefFormatter(const void *f)
{
auto formatter = static_cast<const qxp::function_ref<const char *()> *>(f);
return (*formatter)();
};
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);
return QTestResult::reportResult(success, &lhs, &rhs,
functionRefFormatter, functionRefFormatter,
lhsExpr, rhsExpr, op, file, line);
}
#endif // QT_DEPRECATED_SINCE(6, 8)
bool reportResult(bool success, const void *lhs, const void *rhs,
const char *(*lhsFormatter)(const void*),
const char *(*rhsFormatter)(const void*),
const char *lhsExpr, const char *rhsExpr,
ComparisonOperation op, const char *file, int line)
{
return QTestResult::reportResult(success, lhs, rhs, lhsFormatter, rhsFormatter,
lhsExpr, rhsExpr, op, file, line);
}
} // namespace QTest
static void initEnvironment()
@ -2748,7 +2767,12 @@ bool QTest::compare_helper(bool success, const char *failureMsg,
const char *actual, const char *expected,
const char *file, int line)
{
return QTestResult::reportResult(success, actualVal, expectedVal, actual, expected,
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,
QTest::ComparisonOperation::CustomCompare,
file, line, failureMsg);
}

View File

@ -95,8 +95,7 @@ do { \
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); }, \
qt_lhs_arg, qt_rhs_arg, \
#lhs, #rhs, QTest::ComparisonOperation::opId, \
__FILE__, __LINE__); \
}(lhs, rhs)) { \
@ -330,6 +329,18 @@ namespace QTest
Q_TESTLIB_EXPORT QString formatTryTimeoutDebugMessage(q_no_char8_t::QUtf8StringView expr, int timeout, int actual);
template <typename T1> const char *genericToString(const void *arg)
{
using QTest::toString;
return toString(*static_cast<const T1 *>(arg));
}
template <> inline const char *genericToString<char *>(const void *arg)
{
using QTest::toString;
return toString(static_cast<const char *>(arg));
}
// Exported so Qt Quick Test can also use it for generating backtraces upon crashes.
Q_TESTLIB_EXPORT extern bool noCrashHandler;
@ -644,10 +655,32 @@ namespace QTest
qMetaTypeId<T>())), actualStr, expected, file, line);
}
#if QT_DEPRECATED_SINCE(6, 8)
QT_DEPRECATED_VERSION_X_6_8("use the overload without qxp::function_ref")
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);
#endif // QT_DEPRECATED_SINCE(6, 8)
Q_TESTLIB_EXPORT bool reportResult(bool success, const void *lhs, const void *rhs,
const char *(*lhsFormatter)(const void*),
const char *(*rhsFormatter)(const void*),
const char *lhsExpr, const char *rhsExpr,
ComparisonOperation op, const char *file, int line);
template <typename T1, typename T2>
inline bool reportResult(bool result, const T1 &lhs, const T2 &rhs,
const char *lhsExpr, const char *rhsExpr,
ComparisonOperation op, const char *file, int line)
{
using D1 = std::decay_t<T1>;
using D2 = std::decay_t<T2>;
using Internal::genericToString;
return reportResult(result, std::addressof(lhs), std::addressof(rhs),
genericToString<D1>, genericToString<D2>,
lhsExpr, rhsExpr, op, file, line);
}
}

View File

@ -1608,7 +1608,8 @@
*/
/*!
\fn char *QTest::toString(QSizePolicy::ControlType ct)
\fn char *toString(QSizePolicy::ControlType ct)
\relates QTest
\overload
\since 5.5
@ -1616,7 +1617,8 @@
*/
/*!
\fn char *QTest::toString(QSizePolicy::ControlTypes cts)
\fn char *toString(QSizePolicy::ControlTypes cts)
\relates QTest
\overload
\since 5.5
@ -1624,7 +1626,8 @@
*/
/*!
\fn char *QTest::toString(QSizePolicy::Policy p)
\fn char *toString(QSizePolicy::Policy p)
\relates QTest
\overload
\since 5.5
@ -1632,7 +1635,8 @@
*/
/*!
\fn char *QTest::toString(QSizePolicy sp)
\fn char *toString(QSizePolicy sp)
\relates QTest
\overload
\since 5.5

View File

@ -628,8 +628,9 @@ static const char *failureMessageForOp(QTest::ComparisonOperation op)
Q_UNREACHABLE_RETURN("");
}
bool QTestResult::reportResult(bool success, qxp::function_ref<const char *()> lhs,
qxp::function_ref<const char *()> rhs,
bool QTestResult::reportResult(bool success, const void *lhs, const void *rhs,
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)
@ -653,8 +654,8 @@ bool QTestResult::reportResult(bool success, qxp::function_ref<const char *()> l
return checkStatement(success, msg, file, line);
}
const std::unique_ptr<const char[]> lhsPtr{ lhs() };
const std::unique_ptr<const char[]> rhsPtr{ rhs() };
const std::unique_ptr<const char[]> lhsPtr{ lhsFormatter(lhs) };
const std::unique_ptr<const char[]> rhsPtr{ rhsFormatter(rhs) };
if (!failureMessage)
failureMessage = failureMessageForOp(op);

View File

@ -17,7 +17,6 @@
#include <QtTest/qttestglobal.h>
#include <QtCore/qstringfwd.h>
#include <QtCore/qxpfunctional.h>
#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
@ -102,8 +101,9 @@ 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,
static bool reportResult(bool success, const void *lhs, const void *rhs,
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

@ -9,7 +9,7 @@
<testcase name="basics" classname="tst_TestLib" time="@TEST_DURATION@">
<failure type="fail" message="Compared QObject pointers are not the same">
<![CDATA[ Actual (QTest::testObject()): tst_TestLib/"TestObject"
Expected (nullptr) : (nullptr)]]>
Expected (nullptr) : "nullptr"]]>
</failure>
</testcase>
<testcase name="delays" classname="tst_TestLib" time="@TEST_DURATION@"/>

View File

@ -11,7 +11,7 @@
<Incident type="fail" file="qtbase/tests/auto/testlib/selftests/testlib/tst_testlib.cpp" line="0">
<Description><![CDATA[Compared QObject pointers are not the same
Actual (QTest::testObject()): tst_TestLib/"TestObject"
Expected (nullptr) : (nullptr)]]></Description>
Expected (nullptr) : "nullptr"]]></Description>
</Incident>
<Duration msecs="0"/>
</TestFunction>

View File

@ -5,9 +5,9 @@ not ok 2 - basics()
---
type: QCOMPARE
message: Compared QObject pointers are not the same
wanted: (nullptr) (nullptr)
wanted: "nullptr" (nullptr)
found: tst_TestLib/"TestObject" (QTest::testObject())
expected: (nullptr) (nullptr)
expected: "nullptr" (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

View File

@ -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|n Actual (QTest::testObject()): tst_TestLib/"TestObject"|n Expected (nullptr) : (nullptr)' 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) : "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']

View File

@ -3,7 +3,7 @@ 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) : (nullptr)
Expected (nullptr) : "nullptr"
Loc: [qtbase/tests/auto/testlib/selftests/testlib/tst_testlib.cpp(0)]
PASS : tst_TestLib::delays()
PASS : tst_TestLib::reals(zero)

View File

@ -13,7 +13,7 @@
<Incident type="fail" file="qtbase/tests/auto/testlib/selftests/testlib/tst_testlib.cpp" line="0">
<Description><![CDATA[Compared QObject pointers are not the same
Actual (QTest::testObject()): tst_TestLib/"TestObject"
Expected (nullptr) : (nullptr)]]></Description>
Expected (nullptr) : "nullptr"]]></Description>
</Incident>
<Duration msecs="0"/>
</TestFunction>

View File

@ -93,11 +93,13 @@ static ClassWithPointerGetter getClassForValue(int val)
// various toString() overloads
namespace QTest {
char *toString(const int *val)
template <> char *toString(const int *const &val)
{
return val ? toString(*val) : toString(nullptr);
}
} // namespace QTest
char *toString(const MyClass &val)
{
char *msg = new char[128];
@ -117,8 +119,6 @@ char *toString(const MyClass *val)
return toString(nullptr);
}
} // namespace QTest
enum MyUnregisteredEnum { MyUnregisteredEnumValue1, MyUnregisteredEnumValue2 };
class tst_ExtendedCompare : public QObject
@ -293,8 +293,6 @@ public:
}
};
namespace QTest {
char *toString(const ClassWithDeferredSetter &val)
{
char *msg = new char[128];
@ -302,8 +300,6 @@ char *toString(const ClassWithDeferredSetter &val)
return msg;
}
} // namespace QTest
void tst_ExtendedCompare::checkComparisonWithTimeout()
{
QFETCH_GLOBAL(QTest::ComparisonOperation, operation);