summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Solovev <ivan.solovev@qt.io>2023-10-04 16:57:41 +0200
committerIvan Solovev <ivan.solovev@qt.io>2023-10-06 12:55:06 +0200
commit2d459667f3e9910b63964417969b7aa4583b3ad6 (patch)
treec8c462d0906dd13459369b7760bcb5e85c8e0b55
parent2d14e9a88497b1aad3b75304d5dfa7d37900e9c3 (diff)
SignalTransition: fix binding loops
This one is a bit special, because the setter assumes that there is a QML engine handling the object. As a result, creating a helper object for testing binding loops is a bit tricky. Do it by having a helper QQmlComponent based on a qml file with a SignalTransition object as a root element. Fix the binding loop in the setter in a usual way. Task-number: QTBUG-116542 Pick-to: 6.6 6.5 Change-Id: Ibd22dee0619a69b52901e9fe2145fcfbd9dcf98c Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
-rw-r--r--src/statemachineqml/signaltransition.cpp7
-rw-r--r--tests/auto/qml/qqmlstatemachine/data/signaltransitionhelper.qml10
-rw-r--r--tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp10
3 files changed, 22 insertions, 5 deletions
diff --git a/src/statemachineqml/signaltransition.cpp b/src/statemachineqml/signaltransition.cpp
index 0c16df0..311edda 100644
--- a/src/statemachineqml/signaltransition.cpp
+++ b/src/statemachineqml/signaltransition.cpp
@@ -89,10 +89,9 @@ const QJSValue& SignalTransition::signal()
void SignalTransition::setSignal(const QJSValue &signal)
{
- if (m_signal.value().strictlyEquals(signal)) {
- m_signal.removeBindingUnlessInWrapper();
+ m_signal.removeBindingUnlessInWrapper();
+ if (m_signal.valueBypassingBindings().strictlyEquals(signal))
return;
- }
QV4::ExecutionEngine *jsEngine = QQmlEngine::contextForObject(this)->engine()->handle();
QV4::Scope scope(jsEngine);
@@ -100,7 +99,7 @@ void SignalTransition::setSignal(const QJSValue &signal)
QObject *sender;
QMetaMethod signalMethod;
- m_signal = signal;
+ m_signal.setValueBypassingBindings(signal);
QV4::ScopedValue value(scope, QJSValuePrivate::asReturnedValue(&signal));
// Did we get the "slot" that can be used to invoke the signal?
diff --git a/tests/auto/qml/qqmlstatemachine/data/signaltransitionhelper.qml b/tests/auto/qml/qqmlstatemachine/data/signaltransitionhelper.qml
new file mode 100644
index 0000000..f915cdb
--- /dev/null
+++ b/tests/auto/qml/qqmlstatemachine/data/signaltransitionhelper.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+import QtQuick
+import QtQml.StateMachine
+
+SignalTransition {
+ // Do not crash on SignalTransition without signal
+ onTriggered: () => {}
+}
diff --git a/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp b/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp
index 81e2fb6..e9f1721 100644
--- a/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp
+++ b/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp
@@ -116,10 +116,18 @@ void tst_qqmlstatemachine::tst_bindings()
QVariant signal2;
QMetaObject::invokeMethod(obj.get(), "getSignal1", Q_RETURN_ARG(QVariant, signal1));
QMetaObject::invokeMethod(obj.get(), "getSignal2", Q_RETURN_ARG(QVariant, signal2));
+ // The setter needs an active engine, so we use a helper component to create
+ // a helper instance for testing binding loops.
+ QQmlComponent helperComponent(&engine, testFileUrl("signaltransitionhelper.qml"));
// QJSValue does not implement operator== so we supply own comparator
QTestPrivate::testReadWritePropertyBasics<SignalTransition, QJSValue>(
*st1, signal1.value<QJSValue>(), signal2.value<QJSValue>(), "signal",
- [](QJSValue d1, QJSValue d2) { return d1.strictlyEquals(d2); });
+ [](QJSValue d1, QJSValue d2) { return d1.strictlyEquals(d2); },
+ [](const QJSValue &val) { return QTest::toString(val); },
+ [&helperComponent]() {
+ return std::unique_ptr<SignalTransition>(
+ qobject_cast<SignalTransition*>(helperComponent.create()));
+ });
if (QTest::currentTestFailed()) {
qWarning() << "SignalTransition::signal bindable test failed.";
return;