From 61dbf282ba718771e432dfbbd618cad2dc728c5b Mon Sep 17 00:00:00 2001 From: Juha Vuolle Date: Fri, 5 Mar 2021 11:57:30 +0200 Subject: QtScxml QML-facing properties bindable support additions part 1 This commit adds the bindable support to following QScxmlStateMachine properties: initialized, initialValues, loader, dataModel and tableData Task-number: QTBUG-89895 Change-Id: I33545d9d45a4fbf52a4a220d559b7ee75351a268 Reviewed-by: Ivan Solovev Reviewed-by: Ulf Hermann --- tests/auto/shared/bindableutils.h | 52 ++++++++++++++++++++++++ tests/auto/statemachine/tst_statemachine.cpp | 60 ++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) (limited to 'tests/auto') diff --git a/tests/auto/shared/bindableutils.h b/tests/auto/shared/bindableutils.h index 6e9f657..4a45f89 100644 --- a/tests/auto/shared/bindableutils.h +++ b/tests/auto/shared/bindableutils.h @@ -32,6 +32,57 @@ #include #include +// This is a helper function to test basics of typical bindable +// properties that are read-only (but can change) 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 +// "data0" is the initial datavalue +// "data1" is an instance of the propertydata and must differ from data0 +// "propertyName" is the name of the property we are interested in testing +// "modifierFunction" that does whatever is needed to change the underlying property +// A custom "dataComparator" can be provided for cases that the data doesn't have operator== +template +void testReadableBindableBasics(TestedClass& testedClass, TestedData data0, TestedData data1, + const char* propertyName, + std::function modifierFunction = [](){ qWarning() << "Error, data modifier function must be provided"; }, + std::function dataComparator = [](TestedData d1, TestedData d2) { return d1 == d2; }) +{ + // 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(propertyName); + + // Fail gracefully if preconditions to use this helper function are not met: + QVERIFY2(metaProperty.isBindable() && metaProperty.hasNotifySignal(), qPrintable(id)); + QVERIFY2(modifierFunction, qPrintable(id)); + // Create a signal spy for the property changed -signal + QSignalSpy spy(&testedClass, metaProperty.notifySignal()); + QUntypedBindable bindable = metaProperty.bindable(&testedClass); + + // Verify initial data is as expected + QVERIFY2(dataComparator(testedClass.property(propertyName).template value(), data0), qPrintable(id)); + // Use the property as the source in a binding + QProperty data1Used([&](){ + return dataComparator(testedClass.property(propertyName).template value(), data1); + }); + // Verify binding's initial state + QVERIFY2(data1Used == false, qPrintable(id)); + // Call the supplied modifier function and verify that the value and binding both change + modifierFunction(); + QVERIFY2(data1Used == true, qPrintable(id)); + QVERIFY2(dataComparator(testedClass.property(propertyName).template value(), data1), qPrintable(id)); + QVERIFY2(spy.count() == 1, qPrintable(id + ", actual: " + QString::number(spy.count()))); +} + + // This is a helper function to test basics of typical bindable // properties that are writable. Primarily ensure: // - properties work as before bindings @@ -44,6 +95,7 @@ // The "data1" and "data2" must differ from one another, and // the "data1" must differ from instance property's initial state // "propertyName" is the name of the property we are interested in testing +// A custom "dataComparator" can be provided for cases that the data doesn't have operator== template void testWritableBindableBasics(TestedClass& testedClass, TestedData data1, TestedData data2, const char* propertyName, diff --git a/tests/auto/statemachine/tst_statemachine.cpp b/tests/auto/statemachine/tst_statemachine.cpp index a7464b3..03b30d6 100644 --- a/tests/auto/statemachine/tst_statemachine.cpp +++ b/tests/auto/statemachine/tst_statemachine.cpp @@ -33,6 +33,9 @@ #include #include #include +#include + +#include "../shared/bindableutils.h" enum { SpyWaitTime = 8000 }; @@ -57,6 +60,8 @@ private Q_SLOTS: void multipleInvokableServices(); // QTBUG-61484 void logWithoutExpr(); + + void bindings(); }; void tst_StateMachine::stateNames_data() @@ -455,6 +460,61 @@ void tst_StateMachine::logWithoutExpr() QTRY_COMPARE(logSpy.count(), 1); } +void tst_StateMachine::bindings() +{ + // -- QScxmlStateMachine::initialized + std::unique_ptr stateMachine1( + QScxmlStateMachine::fromFile(QString(":/tst_statemachine/invoke.scxml"))); + QVERIFY(stateMachine1.get()); + testReadableBindableBasics( + *stateMachine1, false, true, "initialized", [&](){ stateMachine1.get()->start(); }); + + // -- QScxmlStateMachine::initialValues + QVariantMap map1{{"map", 1}}; + QVariantMap map2{{"map", 2}}; + testWritableBindableBasics( + *stateMachine1, map1, map2, "initialValues"); + + // -- QScxmlStateMachine::loader + class MockLoader: public QScxmlCompiler::Loader + { + public: + QByteArray load(const QString&, const QString&, QStringList*) override { return QByteArray(); } + }; + MockLoader loader1; + MockLoader loader2; + testWritableBindableBasics( + *stateMachine1, &loader1, &loader2, "loader"); + + // -- QScxmlStateMachine::dataModel + // Use non-existent file below, as valid file would initialize the model + std::unique_ptr stateMachine2( + QScxmlStateMachine::fromFile(QString("not_a_real_file"))); + std::unique_ptr stateMachine3( + QScxmlStateMachine::fromFile(QString("not_a_real_file"))); + QScxmlNullDataModel model1; + QScxmlNullDataModel model2; + // Use the "readable" test helper as the data can only change once + testReadableBindableBasics( + *stateMachine2, nullptr, &model1, "dataModel", + [&](){ stateMachine2->setDataModel(&model1); }); + // verify that setting the model twice will not break the binding (setting is ignored) + QProperty modelProperty(&model1); + stateMachine3.get()->bindableDataModel().setBinding(Qt::makePropertyBinding(modelProperty)); + QVERIFY(stateMachine3.get()->bindableDataModel().hasBinding()); + QVERIFY(stateMachine3.get()->dataModel() == &model1); + stateMachine3.get()->setDataModel(&model2); // should be ignored + QVERIFY(stateMachine3.get()->dataModel() == &model1); + QVERIFY(stateMachine3.get()->bindableDataModel().hasBinding()); + + // -- QScxmlStateMachine::tableData + // Use the statemachine to generate the tabledDatas for testing + std::unique_ptr stateMachine4( + QScxmlStateMachine::fromFile(QString(":/tst_statemachine/invoke.scxml"))); + testWritableBindableBasics( + *stateMachine2, stateMachine1.get()->tableData(), stateMachine4.get()->tableData(), "tableData"); +} + QTEST_MAIN(tst_StateMachine) #include "tst_statemachine.moc" -- cgit v1.2.3