add websocket tests

bump guh version
add websockets as reccommended dependency
pull/135/head
Simon Stürz 2015-08-04 17:00:27 +02:00 committed by Michael Zanetti
parent d322699152
commit 214cbb7f3e
6 changed files with 138 additions and 7 deletions

View File

@ -9,8 +9,8 @@ https=false
publicFolder=/usr/share/guh-webinterface/public
[WebSocketServer]
port=4444
https=false
port=4444
[SSL-Configuration]
certificate=/etc/ssl/certs/guhd-certificate.crt

6
debian/changelog vendored
View File

@ -1,3 +1,9 @@
guh (0.6.0) vivid; urgency=medium
* Add websocket server
-- Simon Stürz <simon.stuerz@guh.guru> Sat, 04 Aug 2015 16:13:43 +0200
guh (0.5.0) vivid; urgency=medium
* Add webserver and REST API

2
debian/control vendored
View File

@ -30,6 +30,8 @@ Depends: libqt5network5,
libguh1 (= ${binary:Version}),
${shlibs:Depends},
${misc:Depends}
Recommends:
libqt5websockets5
Description: Server daemon for home automation systems
guh is an open source home automation server, which allows to control
a lot of different devices from many different manufacturers. With the

View File

@ -26,7 +26,7 @@ enable433gpio {
DEFINES += GPIO433
}
# check webserver support
# check websocket support
equals(QT_MAJOR_VERSION, 5):greaterThan(QT_MINOR_VERSION, 3) {
DEFINES += WEBSOCKET
}

View File

@ -104,8 +104,8 @@ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject
connect(m_websocketServer, SIGNAL(clientConnected(const QUuid &)), this, SLOT(clientConnected(const QUuid &)));
connect(m_websocketServer, SIGNAL(clientDisconnected(const QUuid &)), this, SLOT(clientDisconnected(const QUuid &)));
connect(m_websocketServer, SIGNAL(dataAvailable(QUuid, QString, QString, QVariantMap)), this, SLOT(processData(QUuid, QString, QString, QVariantMap)));
m_websocketServer->startServer();
m_websocketServer->startServer();
m_interfaces.append(m_websocketServer);
#else
Q_UNUSED(sslConfiguration)

View File

@ -43,15 +43,22 @@ class TestWebSocketServer: public GuhTestBase
private slots:
#ifdef WEBSOCKET
void testHandshake();
void pingTest();
void introspect();
#endif
private:
void testBasicCall_data();
void testBasicCall();
void introspect();
private:
int m_socketCommandId;
QVariant injectSocketAndWait(const QString &method, const QVariantMap &params = QVariantMap());
QVariant injectSocketData(const QByteArray &data);
#endif
};
#ifdef WEBSOCKET
@ -88,11 +95,127 @@ void TestWebSocketServer::pingTest()
spyPong.wait();
QVERIFY2(spyPong.count() > 0, "no pong");
qDebug() << "ping response" << spyPong.first().at(0) << spyPong.first().at(1).toString();
socket->close();
socket->deleteLater();
}
void TestWebSocketServer::testBasicCall_data()
{
QTest::addColumn<QByteArray>("data");
QTest::addColumn<bool>("valid");
QTest::newRow("valid call") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Introspect\"}") << true;
QTest::newRow("missing id") << QByteArray("{\"method\":\"JSONRPC.Introspect\"}")<< false;
QTest::newRow("missing method") << QByteArray("{\"id\":42}") << false;
QTest::newRow("borked") << QByteArray("{\"id\":42, \"method\":\"JSO")<< false;
QTest::newRow("invalid function") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Foobar\"}") << false;
QTest::newRow("invalid namespace") << QByteArray("{\"id\":42, \"method\":\"FOO.Introspect\"}") << false;
QTest::newRow("missing dot") << QByteArray("{\"id\":42, \"method\":\"JSONRPCIntrospect\"}") << false;
QTest::newRow("invalid params") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Introspect\", \"params\":{\"törööö\":\"chooo-chooo\"}}") << false;
}
void TestWebSocketServer::testBasicCall()
{
QFETCH(QByteArray, data);
QFETCH(bool, valid);
QVariant response = injectSocketData(data);
if (valid)
QVERIFY2(response.toMap().value("status").toString() == "success", "Call wasn't parsed correctly by guh.");
}
void TestWebSocketServer::introspect()
{
QVariant response = injectSocketAndWait("JSONRPC.Introspect");
QVariantMap methods = response.toMap().value("params").toMap().value("methods").toMap();
QVariantMap notifications = response.toMap().value("params").toMap().value("notifications").toMap();
QVariantMap types = response.toMap().value("params").toMap().value("types").toMap();
QVERIFY2(methods.count() > 0, "No methods in Introspect response!");
QVERIFY2(notifications.count() > 0, "No notifications in Introspect response!");
QVERIFY2(types.count() > 0, "No types in Introspect response!");
}
QVariant TestWebSocketServer::injectSocketAndWait(const QString &method, const QVariantMap &params)
{
QVariantMap call;
call.insert("id", m_socketCommandId);
call.insert("method", method);
call.insert("params", params);
QJsonDocument jsonDoc = QJsonDocument::fromVariant(call);
QWebSocket *socket = new QWebSocket("guh tests", QWebSocketProtocol::Version13);
QSignalSpy spyConnection(socket, SIGNAL(connected()));
socket->open(QUrl(QStringLiteral("ws://localhost:4444")));
spyConnection.wait();
if (spyConnection.count() == 0) {
return QVariant();
}
QSignalSpy spy(socket, SIGNAL(textMessageReceived(QString)));
socket->sendTextMessage(QString(jsonDoc.toJson()));
spy.wait();
socket->close();
socket->deleteLater();
for (int i = 0; i < spy.count(); i++) {
// Make sure the response it a valid JSON string
QJsonParseError error;
jsonDoc = QJsonDocument::fromJson(spy.at(i).last().toByteArray(), &error);
if (error.error != QJsonParseError::NoError) {
qWarning() << "JSON parser error" << error.errorString();
return QVariant();
}
QVariantMap response = jsonDoc.toVariant().toMap();
// skip notifications
if (response.contains("notification"))
continue;
if (response.value("id").toInt() == m_socketCommandId) {
m_socketCommandId++;
return jsonDoc.toVariant();
}
}
m_socketCommandId++;
return QVariant();
}
QVariant TestWebSocketServer::injectSocketData(const QByteArray &data)
{
QWebSocket *socket = new QWebSocket("guh tests", QWebSocketProtocol::Version13);
QSignalSpy spyConnection(socket, SIGNAL(connected()));
socket->open(QUrl(QStringLiteral("ws://localhost:4444")));
spyConnection.wait();
if (spyConnection.count() == 0) {
return QVariant();
}
QSignalSpy spy(socket, SIGNAL(textMessageReceived(QString)));
socket->sendTextMessage(QString(data));
spy.wait();
socket->close();
socket->deleteLater();
for (int i = 0; i < spy.count(); i++) {
// Make sure the response it a valid JSON string
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.at(i).last().toByteArray(), &error);
if (error.error != QJsonParseError::NoError) {
qWarning() << "JSON parser error" << error.errorString();
return QVariant();
}
return jsonDoc.toVariant();
}
m_socketCommandId++;
return QVariant();
}
#endif