Extend QTestPrivate property tests - actual implementation [2/2]
This patch provides the actual implementation to detect binding loops in property setters. These test will help to catch all the existing binding loops that were introduced when migrating to new bindable properties. The logic of the new tests is taken from tst_QObject::objectNameBinding(), but generalized to be applicable to all bindable properties. The original code from tst_QObject can now be removed. The patch effectively reverts f791570b86ce4a0da45bb6e617701a48ee8189b7 because a lambda returning a nullptr now means that the binding loop test should be skipped, which is not a good default behavior. Now when all the existing bindable properties are fixed, it's fine to give a compilation error when adding new tests, if the class is not default-constructible. Task-number: QTBUG-116345 Pick-to: 6.5 Change-Id: I059d444d4bb023c050a22e5b1974565e4f581b5c Reviewed-by: Ulf Hermann <ulf.hermann@qt.io> (cherry picked from commit f5a5c59918021b0bf9a43e6130df2a3f02ea5b97) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
9c1fb39c9c
commit
b799a2107c
@ -88,7 +88,9 @@ namespace QTestPrivate {
|
||||
\c TestedClass. This instance is used to test for binding loops. By default,
|
||||
the method returns a default-constructed \c TestedClass. A custom
|
||||
\a helperConstructor should be provided if \c TestedClass is not
|
||||
default-constructible. (NOTE: The parameter is currently unused!)
|
||||
default-constructible. Some very specific properties cannot be tested for
|
||||
binding loops. Pass a lambda that returns an \c {std::nullptr} as
|
||||
\a helperConstructor in such case.
|
||||
|
||||
\note Any test calling this method will need to call
|
||||
\code
|
||||
@ -108,12 +110,7 @@ void testReadWritePropertyBasics(
|
||||
std::function<char *(const PropertyType &)> represent =
|
||||
[](const PropertyType &val) { return QTest::toString(val); },
|
||||
std::function<std::unique_ptr<TestedClass>(void)> helperConstructor =
|
||||
[]() {
|
||||
if constexpr (std::is_default_constructible_v<TestedClass>)
|
||||
return std::make_unique<TestedClass>();
|
||||
else
|
||||
return std::unique_ptr<TestedClass>();
|
||||
})
|
||||
[]() { return std::make_unique<TestedClass>(); })
|
||||
{
|
||||
// get the property
|
||||
const QMetaObject *metaObject = instance.metaObject();
|
||||
@ -203,7 +200,23 @@ void testReadWritePropertyBasics(
|
||||
if (spy)
|
||||
QCOMPARE(spy->size(), 4);
|
||||
|
||||
Q_UNUSED(helperConstructor);
|
||||
// test binding loop
|
||||
if (std::unique_ptr<TestedClass> helperObj = std::move(helperConstructor())) {
|
||||
// Reset to 'initial', so that the binding loop test could check the
|
||||
// 'changed' value, because some tests already rely on the 'instance' to
|
||||
// have the 'changed' value once this test passes
|
||||
testedObj.setProperty(propertyName, QVariant::fromValue(initial));
|
||||
const QPropertyBinding<PropertyType> binding([&]() {
|
||||
QObject *obj = static_cast<QObject *>(helperObj.get());
|
||||
obj->setProperty(propertyName, QVariant::fromValue(changed));
|
||||
return obj->property(propertyName).template value<PropertyType>();
|
||||
}, {});
|
||||
bindable.setBinding(binding);
|
||||
QPROPERTY_TEST_COMPARISON_HELPER(
|
||||
testedObj.property(propertyName).template value<PropertyType>(), changed,
|
||||
comparator, represent);
|
||||
QVERIFY2(!binding.error().hasError(), qPrintable(binding.error().description()));
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -264,7 +277,9 @@ void testReadWritePropertyBasics(
|
||||
\c TestedClass. This instance is used to test for binding loops. By default,
|
||||
the method returns a default-constructed \c TestedClass. A custom
|
||||
\a helperConstructor should be provided if \c TestedClass is not
|
||||
default-constructible. (NOTE: The parameter is currently unused!)
|
||||
default-constructible. Some very specific properties cannot be tested for
|
||||
binding loops. Pass a lambda that returns an \c {std::nullptr} as
|
||||
\a helperConstructor in such case.
|
||||
|
||||
\note Any test calling this method will need to call
|
||||
\code
|
||||
@ -286,15 +301,8 @@ void testWriteOncePropertyBasics(
|
||||
std::function<char *(const PropertyType &)> represent =
|
||||
[](const PropertyType &val) { return QTest::toString(val); },
|
||||
std::function<std::unique_ptr<TestedClass>(void)> helperConstructor =
|
||||
[]() {
|
||||
if constexpr (std::is_default_constructible_v<TestedClass>)
|
||||
return std::make_unique<TestedClass>();
|
||||
else
|
||||
return std::unique_ptr<TestedClass>();
|
||||
})
|
||||
[]() { return std::make_unique<TestedClass>(); })
|
||||
{
|
||||
Q_UNUSED(helperConstructor);
|
||||
|
||||
// get the property
|
||||
const QMetaObject *metaObject = instance.metaObject();
|
||||
QMetaProperty metaProperty = metaObject->property(metaObject->indexOfProperty(propertyName));
|
||||
@ -327,10 +335,19 @@ void testWriteOncePropertyBasics(
|
||||
propObserver.setBinding(bindable.makeBinding());
|
||||
QPROPERTY_TEST_COMPARISON_HELPER(propObserver.value(), prior, comparator, represent);
|
||||
|
||||
// Create a binding that sets the 'changed' value to the property
|
||||
QProperty<PropertyType> propSetter(changed);
|
||||
// Create a binding that sets the 'changed' value to the property.
|
||||
// This also tests binding loops.
|
||||
QVERIFY(!bindable.hasBinding());
|
||||
bindable.setBinding(Qt::makePropertyBinding(propSetter));
|
||||
std::unique_ptr<TestedClass> helperObj(std::move(helperConstructor()));
|
||||
QProperty<PropertyType> propSetter(changed); // if the helperConstructor() returns nullptr
|
||||
const QPropertyBinding<PropertyType> binding = helperObj
|
||||
? Qt::makePropertyBinding([&]() {
|
||||
QObject *obj = static_cast<QObject *>(helperObj.get());
|
||||
obj->setProperty(propertyName, QVariant::fromValue(changed));
|
||||
return obj->property(propertyName).template value<PropertyType>();
|
||||
})
|
||||
: Qt::makePropertyBinding(propSetter);
|
||||
bindable.setBinding(binding);
|
||||
QVERIFY(bindable.hasBinding());
|
||||
|
||||
QPROPERTY_TEST_COMPARISON_HELPER(
|
||||
|
@ -8197,16 +8197,6 @@ void tst_QObject::objectNameBinding()
|
||||
QObject obj;
|
||||
QTestPrivate::testReadWritePropertyBasics<QObject, QString>(obj, "test1", "test2",
|
||||
"objectName");
|
||||
|
||||
const QPropertyBinding<QString> binding([]() {
|
||||
QObject obj2;
|
||||
obj2.setObjectName(QLatin1String("no loop"));
|
||||
return obj2.objectName();
|
||||
}, {});
|
||||
obj.bindableObjectName().setBinding(binding);
|
||||
|
||||
QCOMPARE(obj.objectName(), QLatin1String("no loop"));
|
||||
QVERIFY2(!binding.error().hasError(), qPrintable(binding.error().description()));
|
||||
}
|
||||
|
||||
namespace EmitToDestroyedClass {
|
||||
|
Loading…
x
Reference in New Issue
Block a user