qt6-bb10/qtdeclarative/tests/auto/qml/qqmlanybinding/tst_qqmlanybinding.cpp

133 lines
4.1 KiB
C++

// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtQml/QQmlEngine>
#include <QtQml/QQmlComponent>
#include <QtCore/QScopedPointer>
#include <QtQml/private/qqmlanybinding_p.h>
#include <QtQuickTestUtils/private/qmlutils_p.h>
#include "withbindable.h"
class tst_qqmlanybinding : public QQmlDataTest
{
Q_OBJECT
public:
tst_qqmlanybinding();
private slots:
void basicActions_data();
void basicActions();
void unboundQQmlPropertyBindingDoesNotCrash();
};
tst_qqmlanybinding::tst_qqmlanybinding()
: QQmlDataTest(QT_QMLTEST_DATADIR)
{
}
void tst_qqmlanybinding::basicActions_data()
{
QTest::addColumn<QString>("fileName");
QTest::addRow("old style") << QStringLiteral("oldstylebinding.qml");
QTest::addRow("new style") << QStringLiteral("newstylebinding.qml");
}
static int getRefCount(const QQmlAnyBinding &binding)
{
if (binding.isAbstractPropertyBinding()) {
return binding.asAbstractBinding()->ref;
} else {
// this temporarily adds a refcount because we construc a new untypedpropertybinding
// thus -1
return QPropertyBindingPrivate::get(binding.asUntypedPropertyBinding())->refCount() - 1;
}
}
void tst_qqmlanybinding::basicActions()
{
QQmlEngine engine;
QFETCH(QString, fileName);
QQmlComponent comp(&engine, testFileUrl(fileName));
QScopedPointer<QObject> root(comp.create());
QVERIFY2(root, qPrintable(comp.errorString()));
QQmlProperty prop(root.get(), "prop");
QQmlProperty trigger(root.get(), "trigger");
// sanity check
QCOMPARE(prop.read().toInt(), 1);
// We can take the binding from a property
auto binding = QQmlAnyBinding::ofProperty(prop);
if (fileName == u"oldstylebinding.qml") {
// and it is a QQmlAbstractBinding,
QVERIFY(binding.isAbstractPropertyBinding());
} else {
// and it is a new style binding,
QVERIFY(binding.isUntypedPropertyBinding());
}
// which is not null.
QVERIFY(binding);
// Getting the binding did not remove it.
trigger.write(42);
QCOMPARE(prop.read().toInt(), 42);
// remove does that,
QQmlAnyBinding::removeBindingFrom(prop);
QVERIFY(!QQmlAnyBinding::ofProperty(prop));
// but does not change the value.
QCOMPARE(prop.read().toInt(), 42);
// As there is no binding anymore, there won't be any change if trigger changes.
trigger.write(2);
QCOMPARE(prop.read().toInt(), 42);
// The binding we took is still valid (and we are currently the sole owner).
QVERIFY(getRefCount(binding) == 1);
// We can reinstall the binding
binding.installOn(prop);
// This changes the value to reflect that trigger has changed
QCOMPARE(prop.read().toInt(), 2);
// The binding behaves normally.
trigger.write(3);
QCOMPARE(prop.read().toInt(), 3);
// "binding" still has a reference to the binding
QVERIFY(getRefCount(binding) == 2);
// aliases are resolved correctly
QQmlProperty a2(root.get(), "a2");
QCOMPARE(QQmlAnyBinding::ofProperty(a2), binding);
}
void tst_qqmlanybinding::unboundQQmlPropertyBindingDoesNotCrash()
{
QQmlEngine engine;
QQmlComponent comp(&engine, testFileUrl("unboundBinding.qml"));
QScopedPointer<QObject> root(comp.create());
QVERIFY2(root, qPrintable(comp.errorString()));
QQmlProperty prop(root.get(), "prop");
QQmlProperty trigger(root.get(), "trigger");
// Store a reference to the binding of prop.
auto binding = QQmlAnyBinding::ofProperty(prop);
QVERIFY(binding.isUntypedPropertyBinding());
QVERIFY(!binding.asUntypedPropertyBinding().isNull());
// If we break the binding,
prop.write(42);
QCOMPARE(prop.read(), 42);
{
auto noBinding = QQmlAnyBinding::ofProperty(prop);
QVERIFY(noBinding.asUntypedPropertyBinding().isNull());
}
// and trigger binding reevaluation
trigger.write(1);
// there is no crash.
// When we reinstall the binding,
binding.installOn(prop);
// the value is correctly updated.
QCOMPARE(prop.read(), 1);
}
QTEST_MAIN(tst_qqmlanybinding)
#include "tst_qqmlanybinding.moc"