add much more test coverage

pull/1/head
Michael Zanetti 2014-04-06 03:33:51 +02:00
parent c027bb1481
commit 34ab701f95
9 changed files with 205 additions and 35 deletions

View File

@ -125,7 +125,6 @@ QList<Vendor> DeviceManager::supportedVendors() const
Optionally filtered by vendorId. */
QList<DeviceClass> DeviceManager::supportedDevices(const VendorId &vendorId) const
{
qDebug() << "returning devices" << vendorId;
QList<DeviceClass> ret;
if (vendorId.isNull()) {
ret = m_supportedDevices.values();

View File

@ -26,7 +26,7 @@
#include <QStringList>
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<DeviceClass> DevicePluginMock::supportedDevices() const
{
QList<DeviceClass> ret;
DeviceClass deviceClassMock(pluginId(), guhVendorId, mockDeviceId);
DeviceClass deviceClassMock(pluginId(), guhVendorId, mockDeviceClassId);
deviceClassMock.setName("Mock Device");
QVariantList mockParams;

View File

@ -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(
"<html>"
@ -169,5 +182,5 @@ QString HttpDaemon::generateWebPage()
body.append("</body></html>\n");
return contentHeader + body;
return generateHeader() + body;
}

View File

@ -29,6 +29,7 @@ private slots:
void discardClient();
private:
QString generateHeader();
QString generateWebPage();
private:

View File

@ -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");

View File

@ -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

View File

@ -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<bool, QString> 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<bool, QString> result = validateVariant(templateMap.value(key), map.value(key));
if (!result.first) {
qDebug() << "Object not matching template";
return result;
if (map.contains(strippedKey)) {
QPair<bool, QString> 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<bool, QString> JsonTypes::validateMap(const QVariantMap &templateMap, cons
QPair<bool, QString> 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<bool, QString> 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<bool, QString> result = validateVariant(entryTemplate, listEntry);

View File

@ -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>("deviceId");
QTest::addColumn<ActionTypeId>("actionTypeId");
QTest::addColumn<bool>("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>("deviceClassId");
QTest::addColumn<int>("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>("deviceClassId");
QTest::addColumn<int>("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>("deviceClassId");
QTest::addColumn<int>("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<QString>("enabled");

View File

@ -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