added a first test suite

pull/1/head
Michael Zanetti 2014-01-26 17:12:15 +01:00
parent e6c2273668
commit ebbae7f0da
34 changed files with 383 additions and 39 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.pro.user
builddir
doc/html

2
.qmake.conf Normal file
View File

@ -0,0 +1,2 @@
top_srcdir=$$PWD
top_builddir=$$shadowed($$PWD)

View File

@ -1,10 +1,11 @@
TEMPLATE=subdirs
SUBDIRS += libhive server plugins
SUBDIRS += libhive server plugins tests
server.depends = libhive plugins
plugins.depends = libhive
tests.depends = libhive
doc.depends = libhive server
doc.commands = cd ${PWD}; qdoc config.qdocconf
doc.commands = cd $$top_srcdir/doc; qdoc config.qdocconf
QMAKE_EXTRA_TARGETS += doc

View File

@ -69,11 +69,6 @@
#include <QSettings>
#include <QStringList>
Q_IMPORT_PLUGIN(DevicePluginElro)
Q_IMPORT_PLUGIN(DevicePluginIntertechno)
Q_IMPORT_PLUGIN(DevicePluginMeisterAnker)
Q_IMPORT_PLUGIN(DevicePluginWifiDetector)
/*! Constructs the DeviceManager with the given \a parent. There should only be one DeviceManager in the system created by \l{HiveCore}.
Use \c HiveCore::instance()->deviceManager() instead to access the DeviceManager.
*/
@ -84,8 +79,11 @@ DeviceManager::DeviceManager(QObject *parent) :
m_pluginTimer.setInterval(15000);
connect(&m_pluginTimer, &QTimer::timeout, this, &DeviceManager::timerEvent);
// Give hardware a chance to start up before loading plugins etc.
QMetaObject::invokeMethod(this, "loadPlugins", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "loadConfiguredDevices", Qt::QueuedConnection);
// Make sure this is always emitted after plugins and devices are loaded
QMetaObject::invokeMethod(this, "loaded", Qt::QueuedConnection);
}
/*! Returns all the \l{DevicePlugin}{DevicePlugins} loaded in the system. */
@ -239,14 +237,13 @@ void DeviceManager::loadPlugins()
m_devicePlugins.insert(pluginIface->pluginId(), pluginIface);
connect(pluginIface, &DevicePlugin::emitTrigger, this, &DeviceManager::emitTrigger);
}
}
}
void DeviceManager::loadConfiguredDevices()
{
QSettings settings;
qDebug() << "loading devices";
qDebug() << "loading devices from" << settings.fileName();
foreach (const QString &idString, settings.childGroups()) {
settings.beginGroup(idString);
Device *device = new Device(settings.value("pluginid").toUuid(), QUuid(idString), settings.value("deviceClassId").toUuid(), this);

View File

@ -49,6 +49,7 @@ public:
DeviceClass findDeviceClass(const QUuid &deviceClassId) const;
signals:
void loaded();
void emitTrigger(const Trigger &trigger);
public slots:

View File

@ -1,7 +1,11 @@
#include "jsonrpcserver.h"
#include "jsontypes.h"
#ifdef TESTING_ENABLED
#include "mocktcpserver.h"
#else
#include "tcpserver.h"
#endif
#include "jsonhandler.h"
#include "hivecore.h"
@ -21,9 +25,13 @@
JsonRPCServer::JsonRPCServer(QObject *parent):
QObject(parent),
#ifdef TESTING_ENABLED
m_tcpServer(new MockTcpServer(this))
#else
m_tcpServer(new TcpServer(this))
#endif
{
connect(m_tcpServer, &TcpServer::jsonDataAvailable, this, &JsonRPCServer::processData);
connect(m_tcpServer, SIGNAL(jsonDataAvailable(int,QByteArray)), this, SLOT(processData(int,QByteArray)));
m_tcpServer->startServer();
@ -70,7 +78,7 @@ void JsonRPCServer::processData(int clientId, const QByteArray &jsonData)
data.insert("types", JsonTypes::allTypes());
QVariantMap methods;
foreach (JsonHandler *handler, m_handlers) {
qDebug() << "got handler" << handler->name() << handler->introspect();
// qDebug() << "got handler" << handler->name() << handler->introspect();
methods.unite(handler->introspect());
}
data.insert("methods", methods);

View File

@ -9,7 +9,11 @@
#include <QVariantMap>
#include <QString>
#ifdef TESTING_ENABLED
class MockTcpServer;
#else
class TcpServer;
#endif
class Device;
class JsonHandler;
@ -32,7 +36,11 @@ private:
void sendErrorResponse(int clientId, int commandId, const QString &error);
private:
#ifdef TESTING_ENABLED
MockTcpServer *m_tcpServer;
#else
TcpServer *m_tcpServer;
#endif
QHash<QString, JsonHandler*> m_handlers;
};

View File

@ -3,6 +3,11 @@
#include <QtPlugin>
Q_IMPORT_PLUGIN(DevicePluginElro)
Q_IMPORT_PLUGIN(DevicePluginIntertechno)
Q_IMPORT_PLUGIN(DevicePluginMeisterAnker)
Q_IMPORT_PLUGIN(DevicePluginWifiDetector)
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

View File

@ -42,8 +42,7 @@
#include <QDebug>
#include <QStringList>
#include <QStandardPaths>
QString rulesFileName = "hiveyourhome/rules";
#include <QCoreApplication>
/*! Constructs the RuleEngine with the given \a parent. Although it wouldn't harm to have multiple RuleEngines, there is one
instance available from \l{HiveCore}. This one should be used instead of creating multiple ones.
@ -51,7 +50,9 @@ QString rulesFileName = "hiveyourhome/rules";
RuleEngine::RuleEngine(QObject *parent) :
QObject(parent)
{
QSettings settings(rulesFileName);
m_settingsFile = QCoreApplication::instance()->organizationName() + "/rules";
qDebug() << "laoding rules from" << m_settingsFile;
QSettings settings(m_settingsFile);
foreach (const QString &idString, settings.childGroups()) {
qDebug() << "found rule" << idString;
@ -160,7 +161,7 @@ RuleEngine::RuleError RuleEngine::addRule(const Trigger &trigger, const QList<St
m_rules.append(rule);
emit ruleAdded(rule.id());
QSettings settings(rulesFileName);
QSettings settings(m_settingsFile);
settings.beginGroup(rule.id().toString());
settings.beginGroup("trigger");
settings.setValue("triggerTypeId", trigger.triggerTypeId());
@ -209,7 +210,7 @@ RuleEngine::RuleError RuleEngine::removeRule(const QUuid &ruleId)
m_rules.takeAt(i);
QSettings settings(rulesFileName);
QSettings settings(m_settingsFile);
settings.beginGroup(rule.id().toString());
settings.remove("");
settings.endGroup();

View File

@ -34,6 +34,7 @@ signals:
void ruleRemoved(const QUuid &ruleId);
private:
QString m_settingsFile;
QList<Rule> m_rules;
};

21
server/server.pri Normal file
View File

@ -0,0 +1,21 @@
SOURCES += $$top_srcdir/server/hivecore.cpp \
$$top_srcdir/server/tcpserver.cpp \
$$top_srcdir/server/ruleengine.cpp \
$$top_srcdir/server/rule.cpp \
$$top_srcdir/server/jsonrpc/jsonrpcserver.cpp \
$$top_srcdir/server/jsonrpc/jsonhandler.cpp \
$$top_srcdir/server/jsonrpc/devicehandler.cpp \
$$top_srcdir/server/jsonrpc/jsontypes.cpp \
$$top_srcdir/server/jsonrpc/ruleshandler.cpp \
$$top_srcdir/server/jsonrpc/actionhandler.cpp
HEADERS += $$top_srcdir/server/hivecore.h \
$$top_srcdir/server/tcpserver.h \
$$top_srcdir/server/ruleengine.h \
$$top_srcdir/server/rule.h \
$$top_srcdir/server/jsonrpc/jsonrpcserver.h \
$$top_srcdir/server/jsonrpc/jsonhandler.h \
$$top_srcdir/server/jsonrpc/devicehandler.h \
$$top_srcdir/server/jsonrpc/jsontypes.h \
$$top_srcdir/server/jsonrpc/ruleshandler.h \
$$top_srcdir/server/jsonrpc/actionhandler.h

View File

@ -9,30 +9,10 @@ INSTALLS += target
QT += network
CONFIG += c++11
LIBS += -L../libhive/ -lhive
LIBS += -L$$top_builddir/libhive/ -lhive
SOURCES += main.cpp \
hivecore.cpp \
tcpserver.cpp \
ruleengine.cpp \
rule.cpp \
jsonrpc/jsonrpcserver.cpp \
jsonrpc/jsonhandler.cpp \
jsonrpc/devicehandler.cpp \
jsonrpc/jsontypes.cpp \
jsonrpc/ruleshandler.cpp \
jsonrpc/actionhandler.cpp
HEADERS += hivecore.h \
tcpserver.h \
ruleengine.h \
rule.h \
jsonrpc/jsonrpcserver.h \
jsonrpc/jsonhandler.h \
jsonrpc/devicehandler.h \
jsonrpc/jsontypes.h \
jsonrpc/ruleshandler.h \
jsonrpc/actionhandler.h
include(server.pri)
SOURCES += main.cpp
# FIXME: Drop this and link them dynamically
LIBS += -L../plugins/deviceplugins/elro/ -lhive_devicepluginelro

17
tests/auto/auto.pro Normal file
View File

@ -0,0 +1,17 @@
TARGET = hivetests
QT += testlib network
CONFIG += testcase
DEFINES += TESTING_ENABLED
INCLUDEPATH += $$top_srcdir/server/ $$top_srcdir/server/jsonrpc $$top_srcdir/libhive $$top_srcdir/tests/auto/
LIBS += -L$$top_builddir/libhive/ -lhive
QMAKE_LFLAGS += -Wl,--rpath=$$top_builddir/libhive
include($$top_srcdir/server/server.pri)
SOURCES += testjsonrpc.cpp \
mocktcpserver.cpp
HEADERS += mocktcpserver.h
LIBS += -L../mocks/mockdeviceplugin/ -lhive_devicepluginmockdevice

View File

@ -0,0 +1,46 @@
#include "mocktcpserver.h"
QList<MockTcpServer*> MockTcpServer::s_allServers;
MockTcpServer::MockTcpServer(QObject *parent):
QObject(parent)
{
s_allServers.append(this);
}
MockTcpServer::~MockTcpServer()
{
s_allServers.removeAll(this);
}
void MockTcpServer::sendResponse(int clientId, const QByteArray &data)
{
emit outgoingData(clientId, data);
}
QList<MockTcpServer *> MockTcpServer::servers()
{
return s_allServers;
}
void MockTcpServer::injectData(int clientId, const QByteArray &data)
{
emit jsonDataAvailable(clientId, data);
}
bool MockTcpServer::startServer()
{
qDebug() << "should start server";
return true;
}
bool MockTcpServer::stopServer()
{
qDebug() << "should stop server";
return true;
}
void MockTcpServer::sendToAll(QByteArray data)
{
qDebug() << "should send to all clients:" << data;
}

View File

@ -0,0 +1,37 @@
#ifndef MOCKTCPSERVER_H
#define MOCKTCPSERVER_H
#include <QObject>
#include <QNetworkInterface>
#include <QDebug>
class MockTcpServer : public QObject
{
Q_OBJECT
public:
explicit MockTcpServer(QObject *parent = 0);
~MockTcpServer();
void sendResponse(int clientId, const QByteArray &data);
/************** Used for testing **************************/
static QList<MockTcpServer*> servers();
void injectData(int clientId, const QByteArray &data);
signals:
void outgoingData(int clientId, const QByteArray &data);
/************** Used for testing **************************/
signals:
void jsonDataAvailable(int clientId, const QByteArray &data);
public slots:
bool startServer();
bool stopServer();
void sendToAll(QByteArray data);
private:
static QList<MockTcpServer*> s_allServers;
};
#endif // TCPSERVER_H

View File

@ -0,0 +1,94 @@
#include "hivecore.h"
#include "devicemanager.h"
#include "mocktcpserver.h"
#include <QtTest/QtTest>
#include <QCoreApplication>
//#include <QSignalSpy>
Q_IMPORT_PLUGIN(DevicePluginMockDevice)
class TestJSONRPC: public QObject
{
Q_OBJECT
private slots:
void initTestcase();
void introspect();
void getSupportedDevices();
private:
MockTcpServer *m_mockTcpServer;
int m_clientId;
};
void TestJSONRPC::initTestcase()
{
QCoreApplication::instance()->setOrganizationName("hiveyourhome-test");
qDebug() << "creating core";
HiveCore::instance();
qDebug() << "creating spy";
// Wait for the DeviceManager to signal that it has loaded plugins and everything
QSignalSpy spy(HiveCore::instance()->deviceManager(), SIGNAL(loaded()));
QVERIFY(spy.isValid());
QVERIFY(spy.wait());
// If Hive should create more than one TcpServer at some point, this needs to be updated.
QCOMPARE(MockTcpServer::servers().count(), 1);
m_mockTcpServer = MockTcpServer::servers().first();
m_clientId = 0;
}
void TestJSONRPC::introspect()
{
QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(int,QByteArray)));
QVERIFY(spy.isValid());
m_mockTcpServer->injectData(m_clientId, "{\"id\":42, \"method\":\"JSONRPC.Introspect\"}");
if (spy.count() == 0) {
spy.wait();
}
// Make sure we got exactly one response
QVERIFY(spy.count() == 1);
// Make sure the introspect response goes to the correct clientId
QCOMPARE(spy.first().first().toInt(), m_clientId);
// Make sure the response it a valid JSON string
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().last().toByteArray(), &error);
QCOMPARE(error.error, QJsonParseError::NoError);
// Make sure the response's id is the same as our command
QCOMPARE(jsonDoc.toVariant().toMap().value("id").toInt(), 42);
}
void TestJSONRPC::getSupportedDevices()
{
QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(int,QByteArray)));
m_mockTcpServer->injectData(m_clientId, "{\"id\":1, \"method\":\"Devices.GetSupportedDevices\"}");
if (spy.count() == 0) {
spy.wait();
}
// Make sure the response it a valid JSON string
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().last().toByteArray(), &error);
QCOMPARE(error.error, QJsonParseError::NoError);
QVariant supportedDevices = jsonDoc.toVariant();
qDebug() << spy.first().last();
// Make sure there is exactly 1 supported device class with the name Mock Wifi Device
QCOMPARE(supportedDevices.toMap().value("params").toMap().value("deviceClasses").toList().count(), 1);
QString deviceName = supportedDevices.toMap().value("params").toMap().value("deviceClasses").toList().first().toMap().value("name").toString();
QCOMPARE(deviceName, QString("Mock WiFi Device"));
}
QTEST_MAIN(TestJSONRPC)
#include "testjsonrpc.moc"

View File

@ -0,0 +1,80 @@
#include "devicepluginmockdevice.h"
#include "device.h"
#include "devicemanager.h"
#include <QDebug>
#include <QStringList>
QUuid pluginUuid = QUuid("2ce2ebc6-7dbb-4b89-ad67-6226aa955041");
QUuid mockWifiDetectorId = QUuid("37279e41-a478-43fa-92b4-c889db578670");
QUuid inRangeStateTypeId = QUuid("110deaf9-5615-4e08-942b-d5443a3bf965");
QUuid inRangeTriggerTypeId = QUuid("7f77120e-b3d1-493f-936e-9d86d7489785");
DevicePluginMockDevice::DevicePluginMockDevice()
{
}
QList<DeviceClass> DevicePluginMockDevice::supportedDevices() const
{
QList<DeviceClass> ret;
DeviceClass deviceClassMockWifiDetector(pluginId(), mockWifiDetectorId);
deviceClassMockWifiDetector.setName("Mock WiFi Device");
QVariantList detectorParams;
QVariantMap macParam;
macParam.insert("name", "mac");
macParam.insert("type", "string");
detectorParams.append(macParam);
deviceClassMockWifiDetector.setParams(detectorParams);
QList<StateType> detectorStates;
StateType inRangeState(inRangeStateTypeId);
inRangeState.setName("inRange");
inRangeState.setType(QVariant::Bool);
inRangeState.setDefaultValue(false);
detectorStates.append(inRangeState);
deviceClassMockWifiDetector.setStates(detectorStates);
QList<TriggerType> detectorTriggers;
QVariantList detectorTriggerParams;
QVariantMap paramInRange;
paramInRange.insert("name", "inRange");
paramInRange.insert("type", "bool");
detectorTriggerParams.append(paramInRange);
TriggerType inRangeTrigger(inRangeTriggerTypeId);
inRangeTrigger.setName("inRange");
inRangeTrigger.setParameters(detectorTriggerParams);
detectorTriggers.append(inRangeTrigger);
deviceClassMockWifiDetector.setTriggers(detectorTriggers);
ret.append(deviceClassMockWifiDetector);
return ret;
}
DeviceManager::HardwareResources DevicePluginMockDevice::requiredHardware() const
{
return DeviceManager::HardwareResourceTimer;
}
QString DevicePluginMockDevice::pluginName() const
{
return "WiFi Detector";
}
QUuid DevicePluginMockDevice::pluginId() const
{
return pluginUuid;
}
void DevicePluginMockDevice::hiveTimer()
{
}

View File

@ -0,0 +1,26 @@
#ifndef DEVICEPLUGINMOCKDEVICE_H
#define DEVICEPLUGINMOCKDEVICE_H
#include "deviceplugin.h"
class DevicePluginMockDevice: public DevicePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.hiveyourhome.DevicePlugin" FILE "devicepluginmockdevice.json")
Q_INTERFACES(DevicePlugin)
public:
explicit DevicePluginMockDevice();
QList<DeviceClass> supportedDevices() const override;
DeviceManager::HardwareResources requiredHardware() const override;
QString pluginName() const override;
QUuid pluginId() const override;
void hiveTimer() override;
};
#endif // DEVICEPLUGINMOCKDEVICE_H

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,9 @@
include($$top_srcdir/plugins/plugins.pri)
TARGET = $$qtLibraryTarget(hive_devicepluginmockdevice)
SOURCES += \
devicepluginmockdevice.cpp
HEADERS += \
devicepluginmockdevice.h

2
tests/mocks/mocks.pro Normal file
View File

@ -0,0 +1,2 @@
TEMPLATE = subdirs
SUBDIRS += mockdeviceplugin

4
tests/tests.pro Normal file
View File

@ -0,0 +1,4 @@
TEMPLATE = subdirs
SUBDIRS = mocks auto
auto.depens = mocks