diff options
author | Juha Vuolle <juha.vuolle@insta.fi> | 2021-02-18 21:28:37 +0200 |
---|---|---|
committer | Juha Vuolle <juha.vuolle@insta.fi> | 2021-05-03 13:51:52 +0300 |
commit | f87d6ad8eabc03be67bc23136ec0bae488d8ee28 (patch) | |
tree | 9352ed93e51d492e644aae1a0bbf95a653575efc /tests | |
parent | 4c6cdbf36f73a933daf8cc345156a2b52c03b7c1 (diff) |
QtScxml QML-facing properties bindable support
This commit adds the bindable support the QML facing properties that
were feasible for such support.
Task-number: QTBUG-89895
Change-Id: Ia8c9ada1c4bf9f14a1460363e79befd82975539d
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/qml/scxmlqmlcpp/CMakeLists.txt | 15 | ||||
-rw-r--r-- | tests/auto/qml/scxmlqmlcpp/brokenstatemachine.scxml | 61 | ||||
-rw-r--r-- | tests/auto/qml/scxmlqmlcpp/statemachine.scxml | 111 | ||||
-rw-r--r-- | tests/auto/qml/scxmlqmlcpp/submachineA.scxml | 4 | ||||
-rw-r--r-- | tests/auto/qml/scxmlqmlcpp/submachineB.scxml | 4 | ||||
-rw-r--r-- | tests/auto/qml/scxmlqmlcpp/topmachine.scxml | 19 | ||||
-rw-r--r-- | tests/auto/qml/scxmlqmlcpp/tst_scxmlqmlcpp.cpp | 248 |
7 files changed, 379 insertions, 83 deletions
diff --git a/tests/auto/qml/scxmlqmlcpp/CMakeLists.txt b/tests/auto/qml/scxmlqmlcpp/CMakeLists.txt index deb900f..67268d5 100644 --- a/tests/auto/qml/scxmlqmlcpp/CMakeLists.txt +++ b/tests/auto/qml/scxmlqmlcpp/CMakeLists.txt @@ -6,3 +6,18 @@ qt_internal_add_test(tst_scxmlqmlcpp Qt::ScxmlQmlPrivate Qt::CorePrivate ) + +qt_internal_add_resource(tst_scxmlqmlcpp "tst_compiled" + PREFIX + "/" + FILES + "submachineA.scxml" + "submachineB.scxml" + "topmachine.scxml" + "statemachine.scxml" + "brokenstatemachine.scxml" +) + +qt6_add_statecharts(tst_scxmlqmlcpp + topmachine.scxml +) diff --git a/tests/auto/qml/scxmlqmlcpp/brokenstatemachine.scxml b/tests/auto/qml/scxmlqmlcpp/brokenstatemachine.scxml new file mode 100644 index 0000000..4864303 --- /dev/null +++ b/tests/auto/qml/scxmlqmlcpp/brokenstatemachine.scxml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtScxml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<scxml + xmlns="http://www.w3.org/2005/07/scxml" + xmlns:qt="http://theqtcompany.com/scxml/2015/06/" + version="1.0" + name="IntentionallyBrokenStateMachine" + initial="working" +> + I am broken +</scxml> diff --git a/tests/auto/qml/scxmlqmlcpp/statemachine.scxml b/tests/auto/qml/scxmlqmlcpp/statemachine.scxml new file mode 100644 index 0000000..3cf023e --- /dev/null +++ b/tests/auto/qml/scxmlqmlcpp/statemachine.scxml @@ -0,0 +1,111 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtScxml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +--> +<scxml + xmlns="http://www.w3.org/2005/07/scxml" + xmlns:qt="http://theqtcompany.com/scxml/2015/06/" + version="1.0" + name="TrafficLightStateMachine" + initial="working" +> + <state id="working" initial="yellow"> + <state id="red"> + <onentry> + <send event="startGoingGreen" delay="3s"/> + </onentry> + <transition event="startGoingGreen" target="redGoingGreen"/> + </state> + + <state id="yellow" initial="greenGoingRed"> + <state id="redGoingGreen"> + <onentry> + <send event="goGreen" delay="1s"/> + </onentry> + <transition event="goGreen" target="green"/> + </state> + + <state id="greenGoingRed"> + <onentry> + <send event="goRed" delay="1s"/> + </onentry> + <transition event="goRed" target="red"/> + </state> + </state> + + <state id="green"> + <onentry> + <send event="startGoingRed" delay="3s"/> + </onentry> + <transition event="startGoingRed" target="greenGoingRed"/> + </state> + + <transition event="smash" target="broken"/> + </state> + + <state id="broken" initial="blinking"> + <state id="blinking"> + <onentry> + <send event="unblink" delay="1s"/> + </onentry> + <transition event="unblink" target="unblinking"/> + </state> + + <state id="unblinking"> + <onentry> + <send event="blink" delay="1s"/> + </onentry> + <transition event="blink" target="blinking"/> + </state> + + <transition event="repair" target="working"/> + </state> +</scxml> diff --git a/tests/auto/qml/scxmlqmlcpp/submachineA.scxml b/tests/auto/qml/scxmlqmlcpp/submachineA.scxml new file mode 100644 index 0000000..0924b2e --- /dev/null +++ b/tests/auto/qml/scxmlqmlcpp/submachineA.scxml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="SubMachine"> + <final id="topState"/> +</scxml> diff --git a/tests/auto/qml/scxmlqmlcpp/submachineB.scxml b/tests/auto/qml/scxmlqmlcpp/submachineB.scxml new file mode 100644 index 0000000..0924b2e --- /dev/null +++ b/tests/auto/qml/scxmlqmlcpp/submachineB.scxml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="SubMachine"> + <final id="topState"/> +</scxml> diff --git a/tests/auto/qml/scxmlqmlcpp/topmachine.scxml b/tests/auto/qml/scxmlqmlcpp/topmachine.scxml new file mode 100644 index 0000000..b733c6a --- /dev/null +++ b/tests/auto/qml/scxmlqmlcpp/topmachine.scxml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="TopMachine" datamodel="ecmascript"> + <datamodel> + <data id="doneCounter" expr="0"/> + </datamodel> + <state id="topState"> + <invoke type="scxml" id="submachine.1" src="file:submachineA.scxml"/> + <invoke type="scxml" id="submachine.2" src="file:submachineA.scxml"/> + <invoke type="scxml" id="submachine.3" src="file:submachineB.scxml"/> + <transition event="done.invoke"> + <assign location="doneCounter" expr="doneCounter + 1"/> + <if cond="doneCounter === 3"> + <send event="goToFinal" delay="1s"/> + </if> + </transition> + <transition event="goToFinal" target="finalState"/> + </state> + <final id="finalState"/> +</scxml> diff --git a/tests/auto/qml/scxmlqmlcpp/tst_scxmlqmlcpp.cpp b/tests/auto/qml/scxmlqmlcpp/tst_scxmlqmlcpp.cpp index 1a1aa1d..408530e 100644 --- a/tests/auto/qml/scxmlqmlcpp/tst_scxmlqmlcpp.cpp +++ b/tests/auto/qml/scxmlqmlcpp/tst_scxmlqmlcpp.cpp @@ -29,7 +29,14 @@ #include <QtTest> #include <QObject> #include <QtScxml/QScxmlStateMachine> +#include <QtScxml/QScxmlNullDataModel> #include <QtScxmlQml/private/eventconnection_p.h> +#include <QtScxmlQml/private/invokedservices_p.h> +#include <QtScxmlQml/private/statemachineloader_p.h> +#include "topmachine.h" +#include <functional> +#include <QtQml/QQmlEngine> +#include <QtQml/QQmlComponent> #include <memory> #include <QDebug> @@ -39,99 +46,174 @@ class tst_scxmlqmlcpp: public QObject Q_OBJECT private slots: void bindings(); +private: + + // This is a helper function to test basics of typical bindable + // properties that are writable. Primarily ensure: + // - properties work as before bindings + // - added bindable aspects work + // + // "TestedClass" is the class type we are testing + // "TestedData" is the data type of the property we are testing + // "testedClass" is an instance of the class we are interested testing + // "data1" and "data2" are two different instances of property data to set and get + // "propertyName" is the name of the property we are interested in testing + template<typename TestedClass, typename TestedData> + void testWritableBindableBasics(TestedClass& testedClass, TestedData data1, + TestedData data2, const char* propertyName) + { + // Get the property we are testing + const QMetaObject *metaObject = testedClass.metaObject(); + QMetaProperty metaProperty = metaObject->property(metaObject->indexOfProperty(propertyName)); + + // Generate a string to help identify failures (as this is a generic template) + QString id(metaObject->className()); + id.append(QStringLiteral("::")); + id.append(metaProperty.name()); + + // Fail gracefully if preconditions to use this helper function are not met: + QVERIFY2(metaProperty.isBindable() && metaProperty.isWritable() + && metaProperty.hasNotifySignal(), qPrintable(id)); + // Create a signal spy for the property changed -signal + QSignalSpy spy(&testedClass, metaProperty.notifySignal()); + QUntypedBindable bindable = metaProperty.bindable(&testedClass); + + // Test basic property read and write + testedClass.setProperty(propertyName, QVariant::fromValue(data1)); + QVERIFY2(testedClass.property(propertyName).template value<TestedData>() == data1, qPrintable(id)); + QVERIFY2(spy.count() == 1, qPrintable(id + ", actual: " + QString::number(spy.count()))); + + // Test setting a binding as a source for the property + QProperty<TestedData> property1(data1); + QProperty<TestedData> property2(data2); + QVERIFY2(!bindable.hasBinding(), qPrintable(id)); + bindable.setBinding(Qt::makePropertyBinding(property2)); + QVERIFY2(bindable.hasBinding(), qPrintable(id)); + // Check that the value also changed + QVERIFY2(testedClass.property(propertyName).template value<TestedData>() == data2, qPrintable(id)); + QVERIFY2(spy.count() == 2, qPrintable(id + ", actual: " + QString::number(spy.count()))); + // Same test but with a lambda binding (cast to be able to set the lambda directly) + QBindable<TestedData> *typedBindable = static_cast<QBindable<TestedData>*>(&bindable); + typedBindable->setBinding([&](){ return property1.value(); }); + QVERIFY2(typedBindable->hasBinding(), qPrintable(id)); + QVERIFY2(testedClass.property(propertyName).template value<TestedData>() == data1, qPrintable(id)); + QVERIFY2(spy.count() == 3, qPrintable(id + ", actual: " + QString::number(spy.count()))); + + // Remove binding by setting a value directly + QVERIFY2(bindable.hasBinding(), qPrintable(id)); + testedClass.setProperty(propertyName, QVariant::fromValue(data2)); + QVERIFY2(testedClass.property(propertyName).template value<TestedData>() == data2, qPrintable(id)); + QVERIFY2(!bindable.hasBinding(), qPrintable(id)); + QVERIFY2(spy.count() == 4, qPrintable(id + ", actual: " + QString::number(spy.count()))); + + // Test using the property as the source in a binding + QProperty<bool> data1Used([&](){ + return testedClass.property(propertyName).template value<TestedData>() == data1; + }); + QVERIFY2(data1Used == false, qPrintable(id)); + testedClass.setProperty(propertyName, QVariant::fromValue(data1)); + QVERIFY2(data1Used == true, qPrintable(id)); + } }; void tst_scxmlqmlcpp::bindings() { - // First create some statemachine instances - std::unique_ptr<QScxmlStateMachine> sm0(nullptr); - std::unique_ptr<QScxmlStateMachine> sm1(QScxmlStateMachine::fromFile("no_real_file_needed")); - std::unique_ptr<QScxmlStateMachine> sm2(QScxmlStateMachine::fromFile("no_real_file_needed")); - // -- test eventconnection::stateMachine QScxmlEventConnection eventConnection; - QSignalSpy ecStateMachineSpy(&eventConnection, &QScxmlEventConnection::stateMachineChanged); - - // initially no state machine - QVERIFY(eventConnection.bindableStateMachine().value() == nullptr); - QCOMPARE(ecStateMachineSpy.count(), 0); - - // access state machine through getter/setter - eventConnection.setStateMachine(sm1.get()); - QVERIFY(eventConnection.bindableStateMachine().value() == sm1.get()); - QVERIFY(eventConnection.stateMachine() == sm1.get()); - QCOMPARE(ecStateMachineSpy.count(), 1); - - // access state machine through properties - eventConnection.setProperty("stateMachine", QVariant::fromValue(sm2.get())); - QVERIFY(eventConnection.bindableStateMachine().value() == sm2.get()); - QVERIFY(eventConnection.property("stateMachine").value<QScxmlStateMachine*>() == sm2.get()); - QCOMPARE(ecStateMachineSpy.count(), 2); - - // set state machine through property binding - QProperty<QScxmlStateMachine*> smp1; - eventConnection.bindableStateMachine().setBinding(Qt::makePropertyBinding(smp1)); - QVERIFY(eventConnection.stateMachine() == nullptr); - QCOMPARE(ecStateMachineSpy.count(), 3); - - // set state machine through lambda property binding - QProperty<QScxmlStateMachine*> smp2(sm2.get()); - eventConnection.bindableStateMachine().setBinding([&](){ return smp2.value(); }); - QVERIFY(eventConnection.stateMachine() == sm2.get()); - QCOMPARE(ecStateMachineSpy.count(), 4); - - // remove binding by setting a value directly - QVERIFY(eventConnection.bindableStateMachine().hasBinding()); - eventConnection.setStateMachine(nullptr); - QVERIFY(!eventConnection.bindableStateMachine().hasBinding()); - - // bind *to* the stateMachine property - QProperty<bool> hasMachineProperty(false); - QVERIFY(!hasMachineProperty.value()); // initially no state machine - hasMachineProperty.setBinding([&](){ return eventConnection.stateMachine() ? true : false; }); - QVERIFY(!hasMachineProperty.value()); // still no state machine - eventConnection.setStateMachine(sm1.get()); - QVERIFY(hasMachineProperty.value()); // a statemachine via binding + std::unique_ptr<QScxmlStateMachine> sm1(QScxmlStateMachine::fromFile("no_real_file_needed")); + std::unique_ptr<QScxmlStateMachine> sm2(QScxmlStateMachine::fromFile("no_real_file_needed")); + testWritableBindableBasics<QScxmlEventConnection, QScxmlStateMachine*>( + eventConnection, sm1.get(), sm2.get(), "stateMachine"); // -- test eventconnection::events QStringList eventList1{{"event1"},{"event2"}}; QStringList eventList2{{"event3"},{"event4"}}; - QSignalSpy ecEventsSpy(&eventConnection, &QScxmlEventConnection::eventsChanged); - QCOMPARE(eventConnection.events().count(), 0); - - // setter / getter access - eventConnection.setEvents(eventList1); - QCOMPARE(eventConnection.bindableEvents().value(), eventList1); - QCOMPARE(eventConnection.events(), eventList1); - QCOMPARE(ecEventsSpy.count(), 1); - - // property access - eventConnection.setProperty("events", eventList2); - QCOMPARE(eventConnection.bindableEvents().value(), eventList2); - QCOMPARE(eventConnection.property("events"), eventList2); - QCOMPARE(ecEventsSpy.count(), 2); - - // property binding updates - QProperty<bool> toggle(true); - QProperty<QStringList> evp1([&](){ return toggle ? eventList1 : eventList2;}); - eventConnection.bindableEvents().setBinding(Qt::makePropertyBinding(evp1)); - QCOMPARE(eventConnection.events(), eventList1); - toggle = false; - QCOMPARE(eventConnection.events(), eventList2); - QCOMPARE(ecEventsSpy.count(), 4); - - // remove binding by setting value - QVERIFY(eventConnection.bindableEvents().hasBinding()); - eventConnection.setEvents(eventList1); - QVERIFY(!eventConnection.bindableEvents().hasBinding()); - QCOMPARE(ecEventsSpy.count(), 5); - - // bind *to* the events property - QProperty<bool> hasEvents([&](){ return !eventConnection.events().isEmpty(); }); - QVERIFY(hasEvents); - eventConnection.setEvents({}); - QVERIFY(!hasEvents); - QCOMPARE(ecEventsSpy.count(), 6); + testWritableBindableBasics<QScxmlEventConnection, QStringList>( + eventConnection, eventList1, eventList2, "events"); + + // -- test invokedservices::statemachine + QScxmlInvokedServices invokedServices; + testWritableBindableBasics<QScxmlInvokedServices, QScxmlStateMachine*>( + invokedServices, sm1.get(), sm2.get(), "stateMachine"); + + // -- test invokedservices::children + TopMachine topSm; + invokedServices.setStateMachine(&topSm); + QCOMPARE(invokedServices.children().count(), 0); + QCOMPARE(topSm.invokedServices().count(), 0); + + // at some point during the topSm execution there are 3 invoked services + // of the same name ('3' filters out as '1' at QML binding) + topSm.start(); + QTRY_COMPARE(topSm.invokedServices().count(), 3); + QCOMPARE(invokedServices.children().count(), 1); + + // after completion invoked services drop back to 0 + QTRY_COMPARE(topSm.invokedServices().count(), 0); + QCOMPARE(invokedServices.children().count(), 0); + + // bind *to* the invokedservices property and check that we observe same changes + // during the topSm execution + QProperty<qsizetype> serviceCounter; + serviceCounter.setBinding([&](){ return invokedServices.children().count(); }); + + QCOMPARE(serviceCounter, 0); + topSm.start(); + QTRY_COMPARE(serviceCounter, 1); + QCOMPARE(topSm.invokedServices().count(), 3); + + // -- test statemachineloader::initialValues + QScxmlStateMachineLoader stateMachineLoader; + QVariantMap values1{{"key1","value1"}, {"key2","value2"}}; + QVariantMap values2{{"key3","value3"}, {"key4","value4"}}; + testWritableBindableBasics<QScxmlStateMachineLoader, QVariantMap>( + stateMachineLoader, values1, values2, "initialValues"); + + // -- test statemachineloader::source + QUrl source1(QStringLiteral("qrc:///statemachine.scxml")); + QUrl source2(QStringLiteral("qrc:///topmachine.scxml")); + // The 'setSource' assumes a valid qml context, so we need to create a bit differently + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData("import QtQuick\n; import QtScxml\n; Item { StateMachineLoader { objectName: \'sml\'; } }", QUrl()); + std::unique_ptr<QObject> root(component.create()); + QScxmlStateMachineLoader *sml = qobject_cast<QScxmlStateMachineLoader*>(root->findChild<QObject*>("sml")); + QVERIFY(sml != nullptr); + testWritableBindableBasics<QScxmlStateMachineLoader, QUrl>(*sml, source1, source2, "source"); + + // -- test statemachineloader::datamodel + QScxmlNullDataModel model1; + QScxmlNullDataModel model2; + testWritableBindableBasics<QScxmlStateMachineLoader,QScxmlDataModel*> + (stateMachineLoader, &model1, &model2, "dataModel"); + + // -- test statemachineloader::statemachine + // The statemachine can be indirectly set by setting the source + QSignalSpy smSpy(sml, &QScxmlStateMachineLoader::stateMachineChanged); + QUrl sourceNonexistent(QStringLiteral("qrc:///file_doesnt_exist.scxml")); + QUrl sourceBroken(QStringLiteral("qrc:///brokenstatemachine.scxml")); + + QVERIFY(sml->stateMachine() != nullptr); + QTest::ignoreMessage(QtWarningMsg, + "<Unknown File>:3:11: QML StateMachineLoader: Cannot open 'qrc:///file_doesnt_exist.scxml' for reading."); + sml->setSource(sourceNonexistent); + QVERIFY(sml->stateMachine() == nullptr); + QCOMPARE(smSpy.count(), 1); + QTest::ignoreMessage(QtWarningMsg, + "<Unknown File>:3:11: QML StateMachineLoader: :/brokenstatemachine.scxml:59:1: error: initial state 'working' not found for <scxml> element"); + QTest::ignoreMessage(QtWarningMsg, + "SCXML document has errors"); + QTest::ignoreMessage(QtWarningMsg, + "<Unknown File>:3:11: QML StateMachineLoader: Something went wrong while parsing 'qrc:///brokenstatemachine.scxml':\n"); + sml->setSource(sourceBroken); + QVERIFY(sml->stateMachine() == nullptr); + QCOMPARE(smSpy.count(), 1); + + QProperty<bool> hasStateMachine([&](){ return sml->stateMachine() ? true : false; }); + QVERIFY(hasStateMachine == false); + sml->setSource(source1); + QCOMPARE(smSpy.count(), 2); + QVERIFY(hasStateMachine == true); } QTEST_MAIN(tst_scxmlqmlcpp) |