aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDominik Holland <dominik.holland@pelagicore.com>2018-10-04 16:39:02 +0200
committerRobert Griebl <robert.griebl@pelagicore.com>2018-10-15 12:25:13 +0000
commitd5d0a33049fefcd384c8dcb936b004191912ab33 (patch)
treec467a71f476e9b5bcd555e09ae84e05b29c39d63
parent37d5be4dd75ddaa151b2e0d5699b642e5130dcdc (diff)
Add an autotest for the QIviSimulationEngine
Task-number: AUTOSUITE-626 Change-Id: Idccf6b55c67ee046c013d6f4556d0b96e7dbac65 Reviewed-by: Robert Griebl <robert.griebl@pelagicore.com>
-rw-r--r--tests/auto/core/core.pro1
-rw-r--r--tests/auto/core/qivisimulationengine/qivisimulationengine.pro10
-rw-r--r--tests/auto/core/qivisimulationengine/tst_qivisimulationengine.cpp872
3 files changed, 883 insertions, 0 deletions
diff --git a/tests/auto/core/core.pro b/tests/auto/core/core.pro
index 435510f..3f687ad 100644
--- a/tests/auto/core/core.pro
+++ b/tests/auto/core/core.pro
@@ -8,6 +8,7 @@ SUBDIRS = servicemanagertest \
queryparser \
qivipagingmodel \
qivisearchandbrowsemodel \
+ qivisimulationengine \
QT_FOR_CONFIG += ivicore
qtConfig(ivigenerator): SUBDIRS += ivigenerator
diff --git a/tests/auto/core/qivisimulationengine/qivisimulationengine.pro b/tests/auto/core/qivisimulationengine/qivisimulationengine.pro
new file mode 100644
index 0000000..86245de
--- /dev/null
+++ b/tests/auto/core/qivisimulationengine/qivisimulationengine.pro
@@ -0,0 +1,10 @@
+QT += testlib ivicore ivicore-private qml
+
+TARGET = tst_qivisimulationengine
+QMAKE_PROJECT_NAME = $$TARGET
+CONFIG += testcase
+
+TEMPLATE = app
+
+SOURCES += \
+ tst_qivisimulationengine.cpp
diff --git a/tests/auto/core/qivisimulationengine/tst_qivisimulationengine.cpp b/tests/auto/core/qivisimulationengine/tst_qivisimulationengine.cpp
new file mode 100644
index 0000000..365b2f3
--- /dev/null
+++ b/tests/auto/core/qivisimulationengine/tst_qivisimulationengine.cpp
@@ -0,0 +1,872 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Pelagicore AG
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtIvi module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT-QTAS$
+** Commercial License Usage
+** Licensees holding valid commercial Qt Automotive Suite 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.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest>
+#include <QIviSimulationEngine>
+#include <QIviPendingReply>
+#include <QQmlEngine>
+#include <QQmlContext>
+#include <QQmlComponent>
+#include <QScopedPointer>
+
+class BaseClass : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int propertyInBase READ propertyInBase WRITE setPropertyInBase NOTIFY propertyInBaseChanged)
+ Q_PROPERTY(bool readOnlyInBase READ readOnlyInBase NOTIFY readOnlyInBaseChanged)
+ Q_PROPERTY(long constantInBase READ constantInBase CONSTANT)
+
+public:
+ int propertyInBase() const { return m_propertyInBase; }
+ bool readOnlyInBase() const { return m_readOnlyInBase; }
+ long constantInBase() const { return m_constantInBase; }
+
+public slots:
+ void setPropertyInBase(int propertyInBase)
+ {
+ if (m_propertyInBase == propertyInBase)
+ return;
+
+ m_propertyInBase = propertyInBase;
+ emit propertyInBaseChanged(m_propertyInBase);
+ }
+
+ void setReadOnlyInBase(bool readOnlyInBase)
+ {
+ if (m_readOnlyInBase == readOnlyInBase)
+ return;
+
+ m_readOnlyInBase = readOnlyInBase;
+ emit readOnlyInBaseChanged(m_readOnlyInBase);
+ }
+
+
+signals:
+ void propertyInBaseChanged(int propertyInBase);
+ void readOnlyInBaseChanged(bool readOnlyInBase);
+ void signalInBase(bool value = false);
+
+private:
+ int m_propertyInBase = -1;
+ bool m_readOnlyInBase = false;
+ long m_constantInBase = -1;
+};
+
+class DerivedClass : public BaseClass
+{
+ Q_OBJECT
+ Q_PROPERTY(int propertyInDerived READ propertyInDerived WRITE setPropertyInDerived NOTIFY propertyInDerivedChanged)
+ Q_PROPERTY(bool readOnlyInDerived READ readOnlyInDerived NOTIFY readOnlyInDerivedChanged)
+ Q_PROPERTY(long constantInDerived READ constantInDerived CONSTANT)
+ Q_PROPERTY(bool complexPropertyInDerived READ complexPropertyInDerived WRITE setComplexPropertyInDerived NOTIFY signalInBase)
+
+public:
+ int propertyInDerived() const { return m_propertyInDerived; }
+ bool readOnlyInDerived() const { return m_readOnlyInDerived; }
+ long constantInDerived() const { return m_constantInDerived; }
+ bool complexPropertyInDerived() const { return m_complexPropertyInDerived; }
+
+public slots:
+ void setPropertyInDerived(int propertyInDerived)
+ {
+ if (m_propertyInDerived == propertyInDerived)
+ return;
+
+ m_propertyInDerived = propertyInDerived;
+ emit propertyInDerivedChanged(m_propertyInDerived);
+ }
+
+ void setReadOnlyInDerived(bool readOnlyInDerived)
+ {
+ if (m_readOnlyInDerived == readOnlyInDerived)
+ return;
+
+ m_readOnlyInDerived = readOnlyInDerived;
+ emit readOnlyInDerivedChanged(m_readOnlyInDerived);
+ }
+
+ void setComplexPropertyInDerived(bool complexPropertyInDerived)
+ {
+ if (m_complexPropertyInDerived == complexPropertyInDerived)
+ return;
+
+ m_complexPropertyInDerived = complexPropertyInDerived;
+ emit signalInBase(complexPropertyInDerived);
+ }
+
+signals:
+ void propertyInDerivedChanged(int propertyInDerived);
+ void readOnlyInDerivedChanged(bool readOnlyInBase);
+ void signalInDerived(bool value = false);
+
+private:
+ int m_propertyInDerived = -1;
+ bool m_readOnlyInDerived = false;
+ long m_constantInDerived = -1;
+ bool m_complexPropertyInDerived = false;
+};
+
+class SimpleAPI: public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int testProperty READ testProperty WRITE setTestProperty NOTIFY testPropertyChanged)
+
+public:
+ int testProperty() const { return m_testProperty; }
+
+public slots:
+ void setTestProperty(int testProperty)
+ {
+ QIVI_SIMULATION_TRY_CALL(SimpleAPI, "setTestProperty", void, testProperty)
+
+ m_callCounter++;
+ if (m_testProperty == testProperty)
+ return;
+
+ m_testProperty = testProperty;
+ emit testPropertyChanged(testProperty);
+ }
+
+ void simpleFunction()
+ {
+ QIVI_SIMULATION_TRY_CALL(SimpleAPI, "simpleFunction", void)
+
+ m_callCounter++;
+ emit simpleFunctionCalled();
+ }
+
+ void functionWithArguments(int intArgument, const QString &stringArgument)
+ {
+ QIVI_SIMULATION_TRY_CALL(SimpleAPI, "functionWithArguments", void, intArgument, stringArgument)
+
+ m_callCounter++;
+ emit functionWithArgumentsCalled(intArgument, stringArgument);
+ }
+
+ int functionWithReturnValue(int intArgument)
+ {
+ QIVI_SIMULATION_TRY_CALL(SimpleAPI, "functionWithReturnValue", int, intArgument)
+
+ m_callCounter++;
+ emit functionWithReturnValueCalled(intArgument);
+ return intArgument;
+ }
+
+ QIviPendingReply<void> functionWithVoidPendingReply()
+ {
+ m_callCounter++;
+ return QIviPendingReply<void>::createFailedReply();
+ }
+
+ QIviPendingReply<int> functionWithIntPendingReply()
+ {
+ m_callCounter++;
+ return QIviPendingReply<int>::createFailedReply();
+ }
+
+signals:
+ void testPropertyChanged(int testProperty);
+ void simpleFunctionCalled();
+ void functionWithArgumentsCalled(int intArgument, const QString &stringArgument);
+ void functionWithReturnValueCalled(int intArgument);
+
+public:
+ int m_callCounter = 0;
+ int m_testProperty = -1;
+};
+
+void verifyQml(QQmlEngine *engine, const QByteArray &qml)
+{
+ QQmlComponent component(engine);
+ component.setData(qml, QUrl());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+}
+
+QVariant callTestFunction(QObject* object, const QByteArray &function, QVariantList &expectedValues, const QVariant &returnValue, const QVariant &value1, const QVariant &value2)
+{
+ //call the testfunction
+ QVariant retValue;
+ QGenericReturnArgument retArgument;
+ if (returnValue.isValid()) {
+ retValue = QVariant(returnValue.type());
+ retArgument = QGenericReturnArgument(retValue.typeName(), retValue.data());
+ }
+
+ if (!value1.isValid()) {
+ QMetaObject::invokeMethod(object, function, retArgument);
+ } else if (!value2.isValid()) {
+ expectedValues.append(value1);
+ QMetaObject::invokeMethod(object, function, retArgument, QGenericArgument(value1.typeName(), value1.data()));
+ } else {
+ expectedValues.append(value1);
+ expectedValues.append(value2);
+ QMetaObject::invokeMethod(object, function, retArgument, QGenericArgument(value1.typeName(), value1.data()), QGenericArgument(value2.typeName(), value2.data()));
+ }
+
+ return retValue;
+}
+
+class tst_QIviSimulationEngine : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void testUsageInCorrectEngine();
+
+ void testPropertyRead_data();
+ void testPropertyRead();
+ void testPropertyReadDerived_data();
+ void testPropertyReadDerived();
+ void testPropertyChange_data();
+ void testPropertyChange();
+ void testPropertyChangeDerived_data();
+ void testPropertyChangeDerived();
+ void testPropertyWrite_data();
+ void testPropertyWrite();
+ void testPropertyWriteDerived_data();
+ void testPropertyWriteDerived();
+
+ void testFunctionCalls_data();
+ void testFunctionCalls();
+ void testFunctionOverride_data();
+ void testFunctionOverride();
+ void testCallingBaseFunction_data();
+ void testCallingBaseFunction();
+ void testRecursionPrevention();
+ void testMultipleInstances();
+};
+
+void tst_QIviSimulationEngine::testUsageInCorrectEngine()
+{
+ QIviSimulationEngine engine;
+
+ BaseClass testObject;
+ engine.registerSimulationInstance<BaseClass>(&testObject, "TestAPI", 1, 0, "BaseClass");
+ verifyQml(&engine, "import TestAPI 1.0; BaseClass {}");
+
+ QCOMPARE(testObject.propertyInBase(), -1);
+ QIviSimulationEngine engine2;
+ QQmlComponent component(&engine2);
+ component.setData("import QtQuick 2.0; \n\
+ import TestAPI 1.0; \n\
+ BaseClass { \n\
+ Component.onCompleted: { \n\
+ propertyInBase = 100; \n\
+ }\n\
+ } \n\
+ ", QUrl());
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".* QML BaseClass: QIviSimulationProxy can only be used in the same Engine it is registered in"));
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+
+ QCOMPARE(testObject.propertyInBase(), -1);
+}
+
+void tst_QIviSimulationEngine::testPropertyRead_data()
+{
+ QTest::addColumn<QByteArray>("property");
+ QTest::addColumn<QVariant>("intialValue");
+ QTest::newRow("propertyInBase") << QByteArray("propertyInBase") << QVariant::fromValue(-1);
+ QTest::newRow("readOnlyInBase") << QByteArray("readOnlyInBase") << QVariant::fromValue(false);
+ QTest::newRow("constantInBase") << QByteArray("constantInBase") << QVariant::fromValue(-1);
+}
+
+void tst_QIviSimulationEngine::testPropertyRead()
+{
+ QFETCH(QByteArray, property);
+ QFETCH(QVariant, intialValue);
+
+ QIviSimulationEngine engine;
+
+ BaseClass testObject;
+ engine.registerSimulationInstance<BaseClass>(&testObject, "TestAPI", 1, 0, "BaseClass");
+
+ QByteArray qml ("import QtQuick 2.0; \n\
+ import TestAPI 1.0; \n\
+ BaseClass { \n\
+ property var initialPropertyValue; \n\
+ property var bindingProperty: PROPERTY; \n\
+ Component.onCompleted: { \n\
+ initialPropertyValue = PROPERTY; \n\
+ }\n\
+ }");
+
+ qml.replace("PROPERTY", property);
+ QQmlComponent component(&engine);
+ component.setData(qml, QUrl());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+
+ //Check that reading the PROPERTY from QML works
+ QCOMPARE(obj->property("initialPropertyValue"), intialValue);
+ //A bound property should work as well
+ QCOMPARE(obj->property("bindingProperty"), intialValue);
+}
+
+void tst_QIviSimulationEngine::testPropertyReadDerived_data()
+{
+ QTest::addColumn<QByteArray>("property");
+ QTest::addColumn<QVariant>("intialValue");
+ QTest::newRow("propertyInBase") << QByteArray("propertyInBase") << QVariant::fromValue(-1);
+ QTest::newRow("readOnlyInBase") << QByteArray("readOnlyInBase") << QVariant::fromValue(false);
+ QTest::newRow("constantInBase") << QByteArray("constantInBase") << QVariant::fromValue(-1);
+ QTest::newRow("propertyInDerived") << QByteArray("propertyInDerived") << QVariant::fromValue(-1);
+ QTest::newRow("readOnlyInDerived") << QByteArray("readOnlyInDerived") << QVariant::fromValue(false);
+ QTest::newRow("constantInDerived") << QByteArray("constantInDerived") << QVariant::fromValue(-1);
+ QTest::newRow("complexPropertyInDerived") << QByteArray("complexPropertyInDerived") << QVariant::fromValue(false);
+}
+
+void tst_QIviSimulationEngine::testPropertyReadDerived()
+{
+ QFETCH(QByteArray, property);
+ QFETCH(QVariant, intialValue);
+
+ QIviSimulationEngine engine;
+
+ DerivedClass testObject;
+ engine.registerSimulationInstance<DerivedClass>(&testObject, "TestAPI", 1, 0, "DerivedClass");
+
+ QByteArray qml ("import QtQuick 2.0; \n\
+ import TestAPI 1.0; \n\
+ DerivedClass { \n\
+ property var initialPropertyValue; \n\
+ property var bindingProperty: PROPERTY; \n\
+ Component.onCompleted: { \n\
+ initialPropertyValue = PROPERTY; \n\
+ }\n\
+ }");
+
+ qml.replace("PROPERTY", property);
+ QQmlComponent component(&engine);
+ component.setData(qml, QUrl());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+
+ //Check that reading the PROPERTY from QML works
+ QCOMPARE(obj->property("initialPropertyValue"), intialValue);
+ //A bound property should work as well
+ QCOMPARE(obj->property("bindingProperty"), intialValue);
+}
+
+void tst_QIviSimulationEngine::testPropertyChange_data()
+{
+ QTest::addColumn<QByteArray>("property");
+ QTest::addColumn<QByteArray>("slot");
+ QTest::addColumn<QByteArray>("setter");
+ QTest::addColumn<QVariant>("value");
+ QTest::newRow("propertyInBase") << QByteArray("propertyInBase") << QByteArray("onPropertyInBaseChanged") << QByteArray("setPropertyInBase") << QVariant::fromValue(10);
+ QTest::newRow("readOnlyInBase") << QByteArray("readOnlyInBase")<< QByteArray("onReadOnlyInBaseChanged")<< QByteArray("setReadOnlyInBase") << QVariant::fromValue(true);
+}
+
+void tst_QIviSimulationEngine::testPropertyChange()
+{
+ QFETCH(QByteArray, property);
+ QFETCH(QByteArray, slot);
+ QFETCH(QByteArray, setter);
+ QFETCH(QVariant, value);
+
+ QIviSimulationEngine engine;
+
+ BaseClass testObject;
+ engine.registerSimulationInstance<BaseClass>(&testObject, "TestAPI", 1, 0, "BaseClass");
+
+ QByteArray qml ("import QtQuick 2.0; \n\
+ import TestAPI 1.0; \n\
+ BaseClass { \n\
+ property var updatedPropertyValue; \n\
+ property var bindingProperty: PROPERTY; \n\
+ SLOT: { updatedPropertyValue = PROPERTY } \n\
+ }");
+
+ qml.replace("PROPERTY", property);
+ qml.replace("SLOT", slot);
+ QQmlComponent component(&engine);
+ component.setData(qml, QUrl());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+
+ //call the setter
+ QMetaObject::invokeMethod(&testObject, setter, QGenericArgument(value.typeName(), value.data()));
+
+ //Check the slot in QML was called and that the new property is updated
+ QCOMPARE(obj->property("updatedPropertyValue"), value);
+ //A bound property should work as well
+ QCOMPARE(obj->property("bindingProperty"), value);
+}
+
+void tst_QIviSimulationEngine::testPropertyChangeDerived_data()
+{
+ QTest::addColumn<QByteArray>("property");
+ QTest::addColumn<QByteArray>("slot");
+ QTest::addColumn<QByteArray>("setter");
+ QTest::addColumn<QVariant>("value");
+ QTest::newRow("propertyInBase") << QByteArray("propertyInBase") << QByteArray("onPropertyInBaseChanged") << QByteArray("setPropertyInBase") << QVariant::fromValue(10);
+ QTest::newRow("readOnlyInBase") << QByteArray("readOnlyInBase")<< QByteArray("onReadOnlyInBaseChanged")<< QByteArray("setReadOnlyInBase") << QVariant::fromValue(true);
+ QTest::newRow("propertyInDerived") << QByteArray("propertyInDerived") << QByteArray("onPropertyInDerivedChanged") << QByteArray("setPropertyInDerived") << QVariant::fromValue(10);
+ QTest::newRow("readOnlyInDerived") << QByteArray("readOnlyInDerived") << QByteArray("onReadOnlyInDerivedChanged") << QByteArray("setReadOnlyInDerived") << QVariant::fromValue(true);
+ QTest::newRow("complexPropertyInDerived") << QByteArray("complexPropertyInDerived") << QByteArray("onComplexPropertyInDerivedChanged") << QByteArray("setComplexPropertyInDerived") << QVariant::fromValue(true);
+}
+
+void tst_QIviSimulationEngine::testPropertyChangeDerived()
+{
+ QFETCH(QByteArray, property);
+ QFETCH(QByteArray, slot);
+ QFETCH(QByteArray, setter);
+ QFETCH(QVariant, value);
+
+ QIviSimulationEngine engine;
+
+ DerivedClass testObject;
+ engine.registerSimulationInstance<DerivedClass>(&testObject, "TestAPI", 1, 0, "DerivedClass");
+
+ QByteArray qml ("import QtQuick 2.0; \n\
+ import TestAPI 1.0; \n\
+ DerivedClass { \n\
+ property var updatedPropertyValue; \n\
+ property var bindingProperty: PROPERTY; \n\
+ SLOT: { updatedPropertyValue = PROPERTY } \n\
+ }");
+
+ qml.replace("PROPERTY", property);
+ qml.replace("SLOT", slot);
+ QQmlComponent component(&engine);
+ component.setData(qml, QUrl());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+
+ //call the setter
+ QMetaObject::invokeMethod(&testObject, setter, QGenericArgument(value.typeName(), value.data()));
+
+ //Check the slot in QML was called and that the new property is updated
+ QCOMPARE(obj->property("updatedPropertyValue"), value);
+ //A bound property should work as well
+ QCOMPARE(obj->property("bindingProperty"), value);
+}
+
+void tst_QIviSimulationEngine::testPropertyWrite_data()
+{
+ QTest::addColumn<QByteArray>("property");
+ QTest::addColumn<QByteArray>("slot");
+ QTest::addColumn<QVariant>("value");
+ QTest::newRow("propertyInBase") << QByteArray("propertyInBase") << QByteArray("onPropertyInBaseChanged") << QVariant::fromValue(10);
+}
+
+void tst_QIviSimulationEngine::testPropertyWrite()
+{
+ QFETCH(QByteArray, property);
+ QFETCH(QByteArray, slot);
+ QFETCH(QVariant, value);
+
+ QIviSimulationEngine engine;
+
+ BaseClass testObject;
+ engine.registerSimulationInstance<BaseClass>(&testObject, "TestAPI", 1, 0, "BaseClass");
+
+ QByteArray qml ("import QtQuick 2.0; \n\
+ import TestAPI 1.0; \n\
+ BaseClass { \n\
+ property var updatedPropertyValue; \n\
+ property var bindingProperty: PROPERTY; \n\
+ SLOT: { updatedPropertyValue = PROPERTY } \n\
+ function changeProperty(newValue) { PROPERTY = newValue } \n\
+ }");
+
+ qml.replace("PROPERTY", property);
+ qml.replace("SLOT", slot);
+ QQmlComponent component(&engine);
+ component.setData(qml, QUrl());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+
+ //call the QML setter
+ QMetaObject::invokeMethod(obj.data(), "changeProperty", Q_ARG(QVariant, value));
+
+ //Check the slot in QML was called and that the new property is updated
+ QCOMPARE(obj->property("updatedPropertyValue"), value);
+ //A bound property should work as well
+ QCOMPARE(obj->property("bindingProperty"), value);
+}
+
+void tst_QIviSimulationEngine::testPropertyWriteDerived_data()
+{
+ QTest::addColumn<QByteArray>("property");
+ QTest::addColumn<QByteArray>("slot");
+ QTest::addColumn<QVariant>("value");
+ QTest::newRow("propertyInBase") << QByteArray("propertyInBase") << QByteArray("onPropertyInBaseChanged") << QVariant::fromValue(10);
+ QTest::newRow("propertyInDerived") << QByteArray("propertyInDerived") << QByteArray("onPropertyInDerivedChanged") << QVariant::fromValue(10);
+ QTest::newRow("complexPropertyInDerived") << QByteArray("complexPropertyInDerived") << QByteArray("onComplexPropertyInDerivedChanged") << QVariant::fromValue(true);
+}
+
+void tst_QIviSimulationEngine::testPropertyWriteDerived()
+{
+ QFETCH(QByteArray, property);
+ QFETCH(QByteArray, slot);
+ QFETCH(QVariant, value);
+
+ QIviSimulationEngine engine;
+
+ DerivedClass testObject;
+ engine.registerSimulationInstance<DerivedClass>(&testObject, "TestAPI", 1, 0, "DerivedClass");
+
+ QByteArray qml ("import QtQuick 2.0; \n\
+ import TestAPI 1.0; \n\
+ DerivedClass { \n\
+ property var updatedPropertyValue; \n\
+ property var bindingProperty: PROPERTY; \n\
+ SLOT: { updatedPropertyValue = PROPERTY } \n\
+ function changeProperty(newValue) { PROPERTY = newValue } \n\
+ }");
+
+ qml.replace("PROPERTY", property);
+ qml.replace("SLOT", slot);
+ QQmlComponent component(&engine);
+ component.setData(qml, QUrl());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+
+ //call the QML setter
+ QMetaObject::invokeMethod(obj.data(), "changeProperty", Q_ARG(QVariant, value));
+
+ //Check the slot in QML was called and that the new property is updated
+ QCOMPARE(obj->property("updatedPropertyValue"), value);
+ //A bound property should work as well
+ QCOMPARE(obj->property("bindingProperty"), value);
+}
+
+void tst_QIviSimulationEngine::testFunctionCalls_data()
+{
+ QTest::addColumn<QByteArray>("function");
+ QTest::addColumn<QByteArray>("signal");
+ QTest::addColumn<QVariant>("returnValue");
+ QTest::addColumn<QVariant>("value1");
+ QTest::addColumn<QVariant>("value2");
+ QTest::newRow("simpleFunction") << QByteArray("simpleFunction") << QByteArray(SIGNAL(simpleFunctionCalled())) << QVariant() << QVariant() << QVariant();
+ QTest::newRow("functionWithArguments") << QByteArray("functionWithArguments") << QByteArray(SIGNAL(functionWithArgumentsCalled(int, QString))) << QVariant() << QVariant(100) << QVariant("Test");
+ QTest::newRow("functionWithReturnValue") << QByteArray("functionWithReturnValue") << QByteArray(SIGNAL(functionWithReturnValueCalled(int))) << QVariant(100) << QVariant(100) << QVariant();
+}
+
+void tst_QIviSimulationEngine::testFunctionCalls()
+{
+ QFETCH(QByteArray, function);
+ QFETCH(QByteArray, signal);
+ QFETCH(QVariant, returnValue);
+ QFETCH(QVariant, value1);
+ QFETCH(QVariant, value2);
+
+ QIviSimulationEngine engine;
+
+ SimpleAPI testObject;
+ engine.registerSimulationInstance<SimpleAPI>(&testObject, "TestAPI", 1, 0, "SimpleAPI");
+
+ QByteArray qml ("import QtQuick 2.0; \n\
+ import TestAPI 1.0; \n\
+ SimpleAPI { \n\
+ }");
+
+ QQmlComponent component(&engine);
+ component.setData(qml, QUrl());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+
+ QSignalSpy spy(&testObject, signal.data());
+
+ QCOMPARE(testObject.m_callCounter, 0);
+
+ //call the testfunction
+ QVariantList expectedValues;
+ QVariant retValue = callTestFunction(&testObject, function, expectedValues, returnValue, value1, value2);
+
+ //verify the implementation was called and the signal emitted
+ QCOMPARE(testObject.m_callCounter, 1);
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.at(0), expectedValues);
+
+ //If provided check the returnValue as well
+ if (returnValue.isValid())
+ QCOMPARE(retValue, returnValue);
+}
+
+void tst_QIviSimulationEngine::testFunctionOverride_data()
+{
+ QTest::addColumn<QByteArray>("function");
+ QTest::addColumn<QByteArray>("signal");
+ QTest::addColumn<QVariant>("returnValue");
+ QTest::addColumn<QVariant>("value1");
+ QTest::addColumn<QVariant>("value2");
+ QTest::newRow("simpleFunction") << QByteArray("simpleFunction") << QByteArray(SIGNAL(simpleFunctionCalled())) << QVariant() << QVariant() << QVariant();
+ QTest::newRow("functionWithArguments") << QByteArray("functionWithArguments") << QByteArray(SIGNAL(functionWithArgumentsCalled(int, QString))) << QVariant() << QVariant(100) << QVariant("Test");
+ QTest::newRow("functionWithReturnValue") << QByteArray("functionWithReturnValue") << QByteArray(SIGNAL(functionWithReturnValueCalled(int))) << QVariant(100) << QVariant(100) << QVariant();
+}
+
+void tst_QIviSimulationEngine::testFunctionOverride()
+{
+ QFETCH(QByteArray, function);
+ QFETCH(QByteArray, signal);
+ QFETCH(QVariant, returnValue);
+ QFETCH(QVariant, value1);
+ QFETCH(QVariant, value2);
+
+ QIviSimulationEngine engine;
+
+ SimpleAPI testObject;
+ engine.registerSimulationInstance<SimpleAPI>(&testObject, "TestAPI", 1, 0, "SimpleAPI");
+
+ QByteArray qml ("import QtQuick 2.0; \n\
+ import TestAPI 1.0; \n\
+ SimpleAPI { \n\
+ function simpleFunction() { \n\
+ simpleFunctionCalled(); \n\
+ } \n\
+ function functionWithArguments(intArgument, stringArgument) { \n\
+ functionWithArgumentsCalled(intArgument, stringArgument); \n\
+ } \n\
+ function functionWithReturnValue(intArgument) { \n\
+ functionWithReturnValueCalled(intArgument) \n\
+ return intArgument; \n\
+ } \n\
+ }");
+
+ QQmlComponent component(&engine);
+ component.setData(qml, QUrl());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+
+ QSignalSpy spy(&testObject, signal.data());
+
+ QCOMPARE(testObject.m_callCounter, 0);
+
+ //call the testfunction
+ QVariantList expectedValues;
+ QVariant retValue = callTestFunction(&testObject, function, expectedValues, returnValue, value1, value2);
+
+ //verify the implementation was NOT called but the signal was emitted from QML
+ QCOMPARE(testObject.m_callCounter, 0);
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.at(0), expectedValues);
+
+ //If provided check the returnValue as well
+ if (returnValue.isValid())
+ QCOMPARE(retValue, returnValue);
+}
+
+void tst_QIviSimulationEngine::testCallingBaseFunction_data()
+{
+ QTest::addColumn<QByteArray>("function");
+ QTest::addColumn<QByteArray>("signal");
+ QTest::addColumn<QVariant>("returnValue");
+ QTest::addColumn<QVariant>("value1");
+ QTest::addColumn<QVariant>("value2");
+ QTest::newRow("simpleFunction") << QByteArray("simpleFunction") << QByteArray(SIGNAL(simpleFunctionCalled())) << QVariant() << QVariant() << QVariant();
+ QTest::newRow("functionWithArguments") << QByteArray("functionWithArguments") << QByteArray(SIGNAL(functionWithArgumentsCalled(int, QString))) << QVariant() << QVariant(100) << QVariant("Test");
+ QTest::newRow("functionWithReturnValue") << QByteArray("functionWithReturnValue") << QByteArray(SIGNAL(functionWithReturnValueCalled(int))) << QVariant(100) << QVariant(100) << QVariant();
+}
+
+void tst_QIviSimulationEngine::testCallingBaseFunction()
+{
+ QFETCH(QByteArray, function);
+ QFETCH(QByteArray, signal);
+ QFETCH(QVariant, returnValue);
+ QFETCH(QVariant, value1);
+ QFETCH(QVariant, value2);
+
+ QIviSimulationEngine engine;
+
+ SimpleAPI testObject;
+ engine.registerSimulationInstance<SimpleAPI>(&testObject, "TestAPI", 1, 0, "SimpleAPI");
+
+ QByteArray qml ("import QtQuick 2.0; \n\
+ import TestAPI 1.0; \n\
+ SimpleAPI { \n\
+ function simpleFunction() { \n\
+ simpleFunctionCalled(); \n\
+ Base.simpleFunction(); \n\
+ } \n\
+ function functionWithArguments(intArgument, stringArgument) { \n\
+ functionWithArgumentsCalled(intArgument, stringArgument); \n\
+ Base.functionWithArguments(intArgument, stringArgument); \n\
+ } \n\
+ function functionWithReturnValue(intArgument) { \n\
+ functionWithReturnValueCalled(intArgument) \n\
+ return Base.functionWithReturnValue(intArgument); \n\
+ } \n\
+ }");
+
+ QQmlComponent component(&engine);
+ component.setData(qml, QUrl());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+
+ QSignalSpy spy(&testObject, signal.data());
+
+ QCOMPARE(testObject.m_callCounter, 0);
+
+ //call the testfunction
+ QVariantList expectedValues;
+ QVariant retValue = callTestFunction(&testObject, function, expectedValues, returnValue, value1, value2);
+
+ //verify the implementation was called once.
+ QCOMPARE(testObject.m_callCounter, 1);
+ //Verify that the signal was emitted twice: 1. QML, 2. C++ implementation
+ QCOMPARE(spy.count(), 2);
+ QCOMPARE(spy.at(0), expectedValues);
+ QCOMPARE(spy.at(1), expectedValues);
+
+ //If provided check the returnValue as well
+ if (returnValue.isValid())
+ QCOMPARE(retValue, returnValue);
+}
+
+void tst_QIviSimulationEngine::testRecursionPrevention()
+{
+ QIviSimulationEngine engine;
+
+ SimpleAPI testObject;
+ engine.registerSimulationInstance<SimpleAPI>(&testObject, "TestAPI", 1, 0, "SimpleAPI");
+
+ QByteArray qml ("import QtQuick 2.0; \n\
+ import TestAPI 1.0; \n\
+ SimpleAPI { \n\
+ function setTestProperty(value) { \n\
+ testProperty = value \n\
+ } \n\
+ }");
+
+ QQmlComponent component(&engine);
+ component.setData(qml, QUrl());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+
+ QSignalSpy spy(&testObject, SIGNAL(testPropertyChanged(int)));
+
+ QCOMPARE(testObject.m_callCounter, 0);
+
+ // call the setProperty function
+ // This will set the testProperty, which will cause the setProperty function to be called again
+ // from C++ as it's the WRITE method of the property
+ QVariantList expectedValues;
+ callTestFunction(&testObject, "setTestProperty", expectedValues, QVariant(), QVariant(5), QVariant());
+
+ //verify the implementation was called once.
+ QCOMPARE(testObject.m_callCounter, 1);
+ //Verify that the signal was emitted once (only from the C++ method)
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.at(0), expectedValues);
+}
+
+void tst_QIviSimulationEngine::testMultipleInstances()
+{
+ QIviSimulationEngine engine;
+
+ SimpleAPI testObject;
+ engine.registerSimulationInstance<SimpleAPI>(&testObject, "TestAPI", 1, 0, "SimpleAPI");
+
+ QByteArray qml ("import QtQuick 2.0; \n\
+ import TestAPI 1.0; \n\
+ Item { \n\
+ signal firstInstanceCalled(); \n\
+ signal secondInstanceCalled(); \n\
+ SimpleAPI { \n\
+ function simpleFunction() { \n\
+ simpleFunctionCalled(); \n\
+ firstInstanceCalled(); \n\
+ } \n\
+ function functionWithReturnValue(intArgument) { \n\
+ functionWithReturnValueCalled(intArgument) \n\
+ firstInstanceCalled(); \n\
+ return intArgument; \n\
+ } \n\
+ } \n\
+ SimpleAPI { \n\
+ function functionWithArguments(intArgument, stringArgument) { \n\
+ functionWithArgumentsCalled(intArgument, stringArgument); \n\
+ secondInstanceCalled(); \n\
+ } \n\
+ function functionWithReturnValue(intArgument) { \n\
+ functionWithReturnValueCalled(intArgument) \n\
+ secondInstanceCalled(); \n\
+ return intArgument; \n\
+ } \n\
+ } \n\
+ }");
+
+ QQmlComponent component(&engine);
+ component.setData(qml, QUrl());
+ QScopedPointer<QObject> obj(component.create());
+ QVERIFY2(obj, qPrintable(component.errorString()));
+
+ QSignalSpy firstInstanceSpy(obj.data(), SIGNAL(firstInstanceCalled()));
+ QSignalSpy secondInstanceSpy(obj.data(), SIGNAL(secondInstanceCalled()));
+
+ QSignalSpy simpleSpy(&testObject, SIGNAL(simpleFunctionCalled()));
+ QSignalSpy argumentsSpy(&testObject, SIGNAL(functionWithArgumentsCalled(int, QString)));
+ QSignalSpy returnValueSpy(&testObject, SIGNAL(functionWithReturnValueCalled(int)));
+
+ // call the simpleFunction function (defined in the first instance)
+ QVariantList expectedValues;
+ callTestFunction(&testObject, "simpleFunction", expectedValues, QVariant(), QVariant(), QVariant());
+
+ //Verify that the first QML instance in QML was called
+ QCOMPARE(firstInstanceSpy.count(), 1);
+ QCOMPARE(secondInstanceSpy.count(), 0);
+ firstInstanceSpy.clear();
+
+ //Verify that the signal was emitted from QML
+ QCOMPARE(simpleSpy.count(), 1);
+ QCOMPARE(simpleSpy.at(0), expectedValues);
+
+ // call the functionWithArguments function (defined in the second instance)
+ expectedValues.clear();
+ callTestFunction(&testObject, "functionWithArguments", expectedValues, QVariant(), QVariant(100), QVariant("Test"));
+
+ //Verify that the second QML instance in QML was called
+ QCOMPARE(firstInstanceSpy.count(), 0);
+ QCOMPARE(secondInstanceSpy.count(), 1);
+ secondInstanceSpy.clear();
+
+ //Verify that the signal was emitted from QML
+ QCOMPARE(argumentsSpy.count(), 1);
+ QCOMPARE(argumentsSpy.at(0), expectedValues);
+
+ // call the functionWithReturnValue function (defined in both instances)
+ expectedValues.clear();
+ callTestFunction(&testObject, "functionWithReturnValue", expectedValues, QVariant(), QVariant(100), QVariant());
+
+ //Verify that the second QML instance in QML was called as it is the first once which is created
+ QCOMPARE(firstInstanceSpy.count(), 1);
+ QCOMPARE(secondInstanceSpy.count(), 0);
+
+ //Verify that the signal was emitted from QML
+ QCOMPARE(returnValueSpy.count(), 1);
+ QCOMPARE(returnValueSpy.at(0), expectedValues);
+}
+
+QTEST_MAIN(tst_QIviSimulationEngine)
+
+#include "tst_qivisimulationengine.moc"
+