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 f791570b86
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.6 6.5
Change-Id: I059d444d4bb023c050a22e5b1974565e4f581b5c
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
parent
457309c9fa
commit
f5a5c59918
@ -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(
|
||||
|
@ -8270,16 +8270,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…
Reference in New Issue
Block a user