From 34ab701f956e5024bd35605c4f27bd91e63e7174 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Sun, 6 Apr 2014 03:33:51 +0200 Subject: [PATCH] add much more test coverage --- libguh/devicemanager.cpp | 1 - .../deviceplugins/mock/devicepluginmock.cpp | 4 +- plugins/deviceplugins/mock/httpdaemon.cpp | 39 +++-- plugins/deviceplugins/mock/httpdaemon.h | 1 + server/jsonrpc/actionhandler.cpp | 2 +- server/jsonrpc/devicehandler.cpp | 8 +- server/jsonrpc/jsontypes.cpp | 26 +-- tests/auto/testjsonrpc.cpp | 152 +++++++++++++++++- tests/scripts/getsupportedvendors.sh | 7 + 9 files changed, 205 insertions(+), 35 deletions(-) create mode 100755 tests/scripts/getsupportedvendors.sh diff --git a/libguh/devicemanager.cpp b/libguh/devicemanager.cpp index 09292fe0..155ae092 100644 --- a/libguh/devicemanager.cpp +++ b/libguh/devicemanager.cpp @@ -125,7 +125,6 @@ QList DeviceManager::supportedVendors() const Optionally filtered by vendorId. */ QList DeviceManager::supportedDevices(const VendorId &vendorId) const { - qDebug() << "returning devices" << vendorId; QList ret; if (vendorId.isNull()) { ret = m_supportedDevices.values(); diff --git a/plugins/deviceplugins/mock/devicepluginmock.cpp b/plugins/deviceplugins/mock/devicepluginmock.cpp index 95952eaa..7c814e15 100644 --- a/plugins/deviceplugins/mock/devicepluginmock.cpp +++ b/plugins/deviceplugins/mock/devicepluginmock.cpp @@ -26,7 +26,7 @@ #include VendorId guhVendorId = VendorId("2062d64d-3232-433c-88bc-0d33c0ba2ba6"); -DeviceClassId mockDeviceId = DeviceClassId("753f0d32-0468-4d08-82ed-1964aab03298"); +DeviceClassId mockDeviceClassId = DeviceClassId("753f0d32-0468-4d08-82ed-1964aab03298"); EventTypeId mockEvent1Id = EventTypeId("45bf3752-0fc6-46b9-89fd-ffd878b5b22b"); EventTypeId mockEvent2Id = EventTypeId("863d5920-b1cf-4eb9-88bd-8f7b8583b1cf"); StateTypeId mockIntStateId = StateTypeId("80baec19-54de-4948-ac46-31eabfaceb83"); @@ -50,7 +50,7 @@ QList DevicePluginMock::supportedDevices() const { QList ret; - DeviceClass deviceClassMock(pluginId(), guhVendorId, mockDeviceId); + DeviceClass deviceClassMock(pluginId(), guhVendorId, mockDeviceClassId); deviceClassMock.setName("Mock Device"); QVariantList mockParams; diff --git a/plugins/deviceplugins/mock/httpdaemon.cpp b/plugins/deviceplugins/mock/httpdaemon.cpp index bf191e87..d31ef444 100644 --- a/plugins/deviceplugins/mock/httpdaemon.cpp +++ b/plugins/deviceplugins/mock/httpdaemon.cpp @@ -53,16 +53,25 @@ void HttpDaemon::readClient() QByteArray data = socket->readLine(); QStringList tokens = QString(data).split(QRegExp("[ \r\n][ \r\n]*")); qDebug() << "incoming data" << tokens[1]; - if (tokens[1].contains('?')) { - QUrl url("http://foo.bar" + tokens[1]); - QUrlQuery query(url); - qDebug() << "query is" << url.path(); - if (url.path() == "/setstate") { - emit setState(StateTypeId(query.queryItems().first().first), QVariant(query.queryItems().first().second)); - } else if (url.path() == "/generateevent") { - qDebug() << "got generateevent" << query.queryItemValue("eventid"); - emit triggerEvent(EventTypeId(query.queryItemValue("eventid"))); + QUrl url("http://foo.bar" + tokens[1]); + QUrlQuery query(url); + qDebug() << "query is" << url.path(); + if (url.path() == "/setstate") { + emit setState(StateTypeId(query.queryItems().first().first), QVariant(query.queryItems().first().second)); + } else if (url.path() == "/generateevent") { + qDebug() << "got generateevent" << query.queryItemValue("eventid"); + emit triggerEvent(EventTypeId(query.queryItemValue("eventid"))); + } else if (url.path() == "/actionhistory") { + QTextStream os(socket); + os.setAutoDetectUnicode(true); + os << generateHeader(); + for (int i = 0; i < m_actionList.count(); ++i) { + os << m_actionList.at(i).first.toString() << '\n'; } + socket->close(); + return; + } else if (url.path() == "/clearactionhistory") { + m_actionList.clear(); } if (tokens[0] == "GET") { QTextStream os(socket); @@ -88,15 +97,19 @@ void HttpDaemon::discardClient() qDebug() << "Connection closed"; } -QString HttpDaemon::generateWebPage() +QString HttpDaemon::generateHeader() { - DeviceClass deviceClass = m_plugin->supportedDevices().first(); - QString contentHeader( "HTTP/1.0 200 Ok\r\n" "Content-Type: text/html; charset=\"utf-8\"\r\n" "\r\n" ); + return contentHeader; +} + +QString HttpDaemon::generateWebPage() +{ + DeviceClass deviceClass = m_plugin->supportedDevices().first(); QString body = QString( "" @@ -169,5 +182,5 @@ QString HttpDaemon::generateWebPage() body.append("\n"); - return contentHeader + body; + return generateHeader() + body; } diff --git a/plugins/deviceplugins/mock/httpdaemon.h b/plugins/deviceplugins/mock/httpdaemon.h index 9e0703a0..3a7dc3f1 100644 --- a/plugins/deviceplugins/mock/httpdaemon.h +++ b/plugins/deviceplugins/mock/httpdaemon.h @@ -29,6 +29,7 @@ private slots: void discardClient(); private: + QString generateHeader(); QString generateWebPage(); private: diff --git a/server/jsonrpc/actionhandler.cpp b/server/jsonrpc/actionhandler.cpp index c79b4aff..d18a4a14 100644 --- a/server/jsonrpc/actionhandler.cpp +++ b/server/jsonrpc/actionhandler.cpp @@ -34,7 +34,7 @@ ActionHandler::ActionHandler(QObject *parent) : setDescription("ExecuteAction", "Execute a single action."); params.insert("actionTypeId", "uuid"); params.insert("deviceId", "uuid"); - params.insert("params", JsonTypes::paramTypeRef()); + params.insert("o:params", JsonTypes::paramTypeRef()); setParams("ExecuteAction", JsonTypes::actionDescription()); returns.insert("success", "bool"); returns.insert("errorMessage", "string"); diff --git a/server/jsonrpc/devicehandler.cpp b/server/jsonrpc/devicehandler.cpp index 86176264..c7868b58 100644 --- a/server/jsonrpc/devicehandler.cpp +++ b/server/jsonrpc/devicehandler.cpp @@ -41,7 +41,7 @@ DeviceHandler::DeviceHandler(QObject *parent) : params.clear(); returns.clear(); setDescription("GetSupportedDevices", "Returns a list of supported Device classes, optionally filtered by vendorId."); - params.insert("vendorId", "o:uuid"); + params.insert("o:vendorId", "uuid"); setParams("GetSupportedDevices", params); QVariantList deviceClasses; deviceClasses.append(JsonTypes::deviceClassRef()); @@ -74,7 +74,7 @@ DeviceHandler::DeviceHandler(QObject *parent) : setParams("AddConfiguredDevice", params); returns.insert("success", "bool"); returns.insert("errorMessage", "string"); - returns.insert("deviceId", "o:uuid"); + returns.insert("o:deviceId", "uuid"); setReturns("AddConfiguredDevice", returns); params.clear(); returns.clear(); @@ -117,7 +117,7 @@ DeviceHandler::DeviceHandler(QObject *parent) : setParams("GetStateTypes", params); QVariantList states; states.append(JsonTypes::stateTypeRef()); - returns.insert("stateTypes", actions); + returns.insert("stateTypes", states); setReturns("GetStateTypes", returns); params.clear(); returns.clear(); @@ -127,7 +127,7 @@ DeviceHandler::DeviceHandler(QObject *parent) : setParams("GetStateValue", params); returns.insert("success", "bool"); returns.insert("errorMessage", "string"); - returns.insert("value", "o:variant"); + returns.insert("o:value", "variant"); setReturns("GetStateValue", returns); // Notifications diff --git a/server/jsonrpc/jsontypes.cpp b/server/jsonrpc/jsontypes.cpp index 69403c7a..4ac9ec04 100644 --- a/server/jsonrpc/jsontypes.cpp +++ b/server/jsonrpc/jsontypes.cpp @@ -89,7 +89,7 @@ void JsonTypes::init() // Action s_action.insert("actionTypeId", "uuid"); s_action.insert("deviceId", "uuid"); - s_action.insert("params", QVariantList() << paramRef()); + s_action.insert("o:params", QVariantList() << paramRef()); // Pugin s_plugin.insert("id", "uuid"); @@ -263,18 +263,20 @@ QPair JsonTypes::validateMap(const QVariantMap &templateMap, cons { s_lastError.clear(); foreach (const QString &key, templateMap.keys()) { - if (templateMap.value(key).toString().startsWith("o:")) { - continue; - } - if (!map.contains(key)) { + QString strippedKey = key; + strippedKey.remove(QRegExp("^o:")); + + if (!key.startsWith("o:") && !map.contains(strippedKey)) { qDebug() << "missing key" << key << templateMap << map; QJsonDocument jsonDoc = QJsonDocument::fromVariant(map); return report(false, QString("Missing key \"%1\" in %2").arg(key).arg(QString(jsonDoc.toJson()))); } - QPair result = validateVariant(templateMap.value(key), map.value(key)); - if (!result.first) { - qDebug() << "Object not matching template"; - return result; + if (map.contains(strippedKey)) { + QPair result = validateVariant(templateMap.value(key), map.value(strippedKey)); + if (!result.first) { + qDebug() << "Object not matching template or object not matching" << templateMap.value(key) << map.value(strippedKey); + return result; + } } } return report(true, ""); @@ -283,8 +285,10 @@ QPair JsonTypes::validateMap(const QVariantMap &templateMap, cons QPair JsonTypes::validateProperty(const QVariant &templateValue, const QVariant &value) { QString strippedTemplateValue = templateValue.toString(); - strippedTemplateValue.remove(QRegExp("^o:")); + if (strippedTemplateValue == "variant") { + return report(true, ""); + } if (strippedTemplateValue == "uuid") { QString errorString = QString("Param %1 is not a uuid.").arg(value.toString()); return report(value.canConvert(QVariant::Uuid), errorString); @@ -307,8 +311,6 @@ QPair JsonTypes::validateList(const QVariantList &templateList, c Q_ASSERT(templateList.count() == 1); QVariant entryTemplate = templateList.first(); - qDebug() << "validating list" << templateList; - for (int i = 0; i < list.count(); ++i) { QVariant listEntry = list.at(i); QPair result = validateVariant(entryTemplate, listEntry); diff --git a/tests/auto/testjsonrpc.cpp b/tests/auto/testjsonrpc.cpp index 98b41af4..718611c7 100644 --- a/tests/auto/testjsonrpc.cpp +++ b/tests/auto/testjsonrpc.cpp @@ -33,6 +33,10 @@ int mockDevice1Port = 1337; int mockDevice2Port = 7331; extern VendorId guhVendorId; +extern DeviceClassId mockDeviceClassId; +extern ActionTypeId mockAction1Id; +extern EventTypeId mockEvent1Id; +extern StateTypeId mockIntStateId; class TestJSONRPC: public QObject { @@ -49,6 +53,19 @@ private slots: void getSupportedDevices_data(); void getSupportedDevices(); + void getConfiguredDevices(); + + void executeAction_data(); + void executeAction(); + + void getActionTypes_data(); + void getActionTypes(); + + void getEventTypes_data(); + void getEventTypes(); + + void getStateTypes_data(); + void getStateTypes(); void enableDisableNotifications_data(); void enableDisableNotifications(); @@ -65,7 +82,7 @@ private: MockTcpServer *m_mockTcpServer; QUuid m_clientId; int m_commandId; - QUuid m_mockDeviceId; + DeviceId m_mockDeviceId; }; void TestJSONRPC::initTestcase() @@ -95,7 +112,7 @@ void TestJSONRPC::initTestcase() QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), true); - m_mockDeviceId = response.toMap().value("params").toMap().value("deviceId").toUuid(); + m_mockDeviceId = DeviceId(response.toMap().value("params").toMap().value("deviceId").toString()); QVERIFY2(!m_mockDeviceId.isNull(), "Newly created mock device must not be null."); } @@ -248,6 +265,137 @@ void TestJSONRPC::getSupportedDevices() } } +void TestJSONRPC::getConfiguredDevices() +{ + QVariant response = injectAndWait("Devices.GetConfiguredDevices"); + + QVariantList devices = response.toMap().value("params").toMap().value("devices").toList(); + QCOMPARE(devices.count(), 1); +} + +void TestJSONRPC::executeAction_data() +{ + QTest::addColumn("deviceId"); + QTest::addColumn("actionTypeId"); + QTest::addColumn("success"); + + QTest::newRow("valid action") << m_mockDeviceId << mockAction1Id << true; + QTest::newRow("invalid device TypeId") << DeviceId("f2965936-0dd0-4014-8f31-4c2ef7fc5952") << mockAction1Id << false; + QTest::newRow("invalid action TypeId") << m_mockDeviceId << ActionTypeId("f2965936-0dd0-4014-8f31-4c2ef7fc5952") << false; +} + +void TestJSONRPC::executeAction() +{ + QFETCH(DeviceId, deviceId); + QFETCH(ActionTypeId, actionTypeId); + QFETCH(bool, success); + + QVariantMap params; + params.insert("actionTypeId", actionTypeId); + params.insert("deviceId", deviceId); + QVariant response = injectAndWait("Actions.ExecuteAction", params); + QCOMPARE(response.toMap().value("params").toMap().value("success").toBool(), success); + + // Fetch action execution history from mock device + QNetworkAccessManager nam; + QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); + + QNetworkRequest request(QUrl(QString("http://localhost:%1/actionhistory").arg(mockDevice1Port))); + QNetworkReply *reply = nam.get(request); + spy.wait(); + QCOMPARE(spy.count(), 1); + reply->deleteLater(); + + if (success) { + QCOMPARE(actionTypeId, ActionTypeId(reply->readAll())); + } else { + QCOMPARE(reply->readAll().length(), 0); + } + + // cleanup for the next run + spy.clear(); + request.setUrl(QUrl(QString("http://localhost:%1/clearactionhistory").arg(mockDevice1Port))); + reply = nam.get(request); + spy.wait(); + QCOMPARE(spy.count(), 1); + reply->deleteLater(); +} + +void TestJSONRPC::getActionTypes_data() +{ + QTest::addColumn("deviceClassId"); + QTest::addColumn("resultCount"); + + QTest::newRow("valid deviceclass") << mockDeviceClassId << 2; + QTest::newRow("invalid deviceclass") << DeviceClassId("094f8024-5caa-48c1-ab6a-de486a92088f") << 0; +} + +void TestJSONRPC::getActionTypes() +{ + QFETCH(DeviceClassId, deviceClassId); + QFETCH(int, resultCount); + + QVariantMap params; + params.insert("deviceClassId", deviceClassId); + QVariant response = injectAndWait("Devices.GetActionTypes", params); + + QVariantList actionTypes = response.toMap().value("params").toMap().value("actionTypes").toList(); + QCOMPARE(actionTypes.count(), resultCount); + if (resultCount > 0) { + QCOMPARE(actionTypes.first().toMap().value("id").toString(), mockAction1Id.toString()); + } +} + +void TestJSONRPC::getEventTypes_data() +{ + QTest::addColumn("deviceClassId"); + QTest::addColumn("resultCount"); + + QTest::newRow("valid deviceclass") << mockDeviceClassId << 2; + QTest::newRow("invalid deviceclass") << DeviceClassId("094f8024-5caa-48c1-ab6a-de486a92088f") << 0; +} + +void TestJSONRPC::getEventTypes() +{ + QFETCH(DeviceClassId, deviceClassId); + QFETCH(int, resultCount); + + QVariantMap params; + params.insert("deviceClassId", deviceClassId); + QVariant response = injectAndWait("Devices.GetEventTypes", params); + + QVariantList eventTypes = response.toMap().value("params").toMap().value("eventTypes").toList(); + QCOMPARE(eventTypes.count(), resultCount); + if (resultCount > 0) { + QCOMPARE(eventTypes.first().toMap().value("id").toString(), mockEvent1Id.toString()); + } +} + +void TestJSONRPC::getStateTypes_data() +{ + QTest::addColumn("deviceClassId"); + QTest::addColumn("resultCount"); + + QTest::newRow("valid deviceclass") << mockDeviceClassId << 2; + QTest::newRow("invalid deviceclass") << DeviceClassId("094f8024-5caa-48c1-ab6a-de486a92088f") << 0; +} + +void TestJSONRPC::getStateTypes() +{ + QFETCH(DeviceClassId, deviceClassId); + QFETCH(int, resultCount); + + QVariantMap params; + params.insert("deviceClassId", deviceClassId); + QVariant response = injectAndWait("Devices.GetStateTypes", params); + + QVariantList stateTypes = response.toMap().value("params").toMap().value("stateTypes").toList(); + QCOMPARE(stateTypes.count(), resultCount); + if (resultCount > 0) { + QCOMPARE(stateTypes.first().toMap().value("id").toString(), mockIntStateId.toString()); + } +} + void TestJSONRPC::enableDisableNotifications_data() { QTest::addColumn("enabled"); diff --git a/tests/scripts/getsupportedvendors.sh b/tests/scripts/getsupportedvendors.sh new file mode 100755 index 00000000..c40e35b9 --- /dev/null +++ b/tests/scripts/getsupportedvendors.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +if [ -z $1 ]; then + echo "usage $0 host" +else + (echo '{"id":1, "method":"Devices.GetSupportedVendors"}'; sleep 1) | nc $1 1234 +fi