/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2015 Simon Stürz * * Copyright (C) 2014 Michael Zanetti * * * * This file is part of nymea. * * * * nymea is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, version 2 of the License. * * * ** * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with nymea. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "nymeatestbase.h" #include "nymeasettings.h" #include "servers/mocktcpserver.h" #include "nymeacore.h" using namespace nymeaserver; class TestRules: public NymeaTestBase { Q_OBJECT private: void cleanupMockHistory(); void cleanupRules(); DeviceId addDisplayPinDevice(); QVariantMap createEventDescriptor(const DeviceId &deviceId, const EventTypeId &eventTypeId); QVariantMap createActionWithParams(const DeviceId &deviceId); QVariantMap createStateEvaluatorFromSingleDescriptor(const QVariantMap &stateDescriptor); void setWritableStateValue(const DeviceId &deviceId, const StateTypeId &stateTypeId, const QVariant &value); void verifyRuleExecuted(const ActionTypeId &actionTypeId); void verifyRuleNotExecuted(); QVariant validIntStateBasedRule(const QString &name, const bool &executable, const bool &enabled); void generateEvent(const EventTypeId &eventTypeId); private slots: void initTestCase(); void cleanup(); void emptyRule(); void getInvalidRule(); void addRemoveRules_data(); void addRemoveRules(); void editRules_data(); void editRules(); void executeRuleActions_data(); void executeRuleActions(); void findRule(); void removeInvalidRule(); void loadStoreConfig(); void evaluateEvent(); void evaluateEventParams(); void testStateEvaluator_data(); void testStateEvaluator(); void testStateEvaluator2_data(); void testStateEvaluator2(); void testStateEvaluator3_data(); void testStateEvaluator3(); void testChildEvaluator_data(); void testChildEvaluator(); void testStateChange(); void enableDisableRule(); void testEventBasedAction(); void testEventBasedRuleWithExitAction(); void testStateBasedAction(); void removePolicyUpdate(); void removePolicyCascade(); void removePolicyUpdateRendersUselessRule(); void testRuleActionParams_data(); void testRuleActionParams(); void testRuleActionPAramsFromEventParameter_data(); void testRuleActionPAramsFromEventParameter(); void testInitStatesActive(); void testInterfaceBasedEventRule(); void testInterfaceBasedStateRule(); void testLoopingRules(); void testScene(); void testHousekeeping_data(); void testHousekeeping(); }; void TestRules::cleanupMockHistory() { QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); QNetworkRequest request(QUrl(QString("http://localhost:%1/clearactionhistory").arg(QString::number(m_mockDevice1Port)))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); } void TestRules::cleanupRules() { QVariant response = injectAndWait("Rules.GetRules"); foreach (const QVariant &ruleDescription, response.toMap().value("params").toMap().value("ruleDescriptions").toList()) { QVariantMap params; params.insert("ruleId", ruleDescription.toMap().value("id").toString()); verifyRuleError(injectAndWait("Rules.RemoveRule", params)); } } DeviceId TestRules::addDisplayPinDevice() { // Discover device QVariantList discoveryParams; QVariantMap resultCountParam; resultCountParam.insert("paramTypeId", mockDisplayPinDiscoveryResultCountParamTypeId); resultCountParam.insert("value", 1); discoveryParams.append(resultCountParam); QVariantMap params; params.insert("deviceClassId", mockDisplayPinDeviceClassId); params.insert("discoveryParams", discoveryParams); QVariant response = injectAndWait("Devices.GetDiscoveredDevices", params); verifyDeviceError(response, Device::DeviceErrorNoError); // Pair device DeviceDescriptorId descriptorId = DeviceDescriptorId(response.toMap().value("params").toMap().value("deviceDescriptors").toList().first().toMap().value("id").toString()); params.clear(); params.insert("deviceClassId", mockDisplayPinDeviceClassId); params.insert("name", "Display pin mock device"); params.insert("deviceDescriptorId", descriptorId.toString()); response = injectAndWait("Devices.PairDevice", params); verifyDeviceError(response); PairingTransactionId pairingTransactionId(response.toMap().value("params").toMap().value("pairingTransactionId").toString()); QString displayMessage = response.toMap().value("params").toMap().value("displayMessage").toString(); qDebug() << "displayMessage" << displayMessage; params.clear(); params.insert("pairingTransactionId", pairingTransactionId.toString()); params.insert("secret", "243681"); response = injectAndWait("Devices.ConfirmPairing", params); verifyDeviceError(response); return DeviceId(response.toMap().value("params").toMap().value("deviceId").toString()); } QVariantMap TestRules::createEventDescriptor(const DeviceId &deviceId, const EventTypeId &eventTypeId) { QVariantMap eventDescriptor; eventDescriptor.insert("eventTypeId", eventTypeId); eventDescriptor.insert("deviceId", deviceId); return eventDescriptor; } QVariantMap TestRules::createActionWithParams(const DeviceId &deviceId) { QVariantMap action; QVariantList ruleActionParams; QVariantMap param1; param1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); param1.insert("value", 4); QVariantMap param2; param2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); param2.insert("value", true); ruleActionParams.append(param1); ruleActionParams.append(param2); action.insert("deviceId", deviceId); action.insert("actionTypeId", mockWithParamsActionTypeId); action.insert("ruleActionParams", ruleActionParams); return action; } QVariantMap TestRules::createStateEvaluatorFromSingleDescriptor(const QVariantMap &stateDescriptor) { QVariantMap stateEvaluator; stateEvaluator.insert("stateDescriptor", stateDescriptor); return stateEvaluator; } void TestRules::setWritableStateValue(const DeviceId &deviceId, const StateTypeId &stateTypeId, const QVariant &value) { QVariantMap params; params.insert("deviceId", deviceId); params.insert("stateTypeId", stateTypeId); QVariant response = injectAndWait("Devices.GetStateValue", params); verifyDeviceError(response); QVariant currentStateValue = response.toMap().value("params").toMap().value("value"); bool shouldGetNotification = currentStateValue != value; QSignalSpy stateSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); QVariantMap paramMap; paramMap.insert("paramTypeId", stateTypeId.toString()); paramMap.insert("value", value); params.clear(); response.clear(); params.insert("deviceId", deviceId); params.insert("actionTypeId", stateTypeId.toString()); params.insert("params", QVariantList() << paramMap); printJson(params); response = injectAndWait("Actions.ExecuteAction", params); qCDebug(dcTests()) << "Execute action response" << response; verifyDeviceError(response); if (shouldGetNotification) { stateSpy.wait(200); // Wait for state changed notification QVariantList stateChangedVariants = checkNotifications(stateSpy, "Devices.StateChanged"); QVERIFY2(stateChangedVariants.count() == 1, "Did not get Devices.StateChanged notification."); QVariantMap notification = stateChangedVariants.first().toMap().value("params").toMap(); QVERIFY2(notification.contains("deviceId"), "Devices.StateChanged notification does not contain deviceId"); QVERIFY2(DeviceId(notification.value("deviceId").toString()) == deviceId, "Devices.StateChanged notification does not contain the correct deviceId"); QVERIFY2(notification.contains("stateTypeId"), "Devices.StateChanged notification does not contain stateTypeId"); QVERIFY2(StateTypeId(notification.value("stateTypeId").toString()) == stateTypeId, "Devices.StateChanged notification does not contain the correct stateTypeId"); QVERIFY2(notification.contains("value"), "Devices.StateChanged notification does not contain new state value"); QVERIFY2(notification.value("value") == QVariant(value), "Devices.StateChanged notification does not contain the new value"); } } void TestRules::verifyRuleExecuted(const ActionTypeId &actionTypeId) { // Verify rule got executed bool actionFound = false; QByteArray actionHistory; int i = 0; while (!actionFound && i < 50) { QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); QNetworkRequest request(QUrl(QString("http://localhost:%1/actionhistory").arg(QString::number(m_mockDevice1Port)))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); actionHistory = reply->readAll(); actionFound = actionTypeId == ActionTypeId(actionHistory); reply->deleteLater(); if (!actionFound) { QTest::qWait(100); } i++; } QVERIFY2(actionFound, "Action not triggered. Current action history: \"" + actionHistory + "\""); } void TestRules::verifyRuleNotExecuted() { QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); QNetworkRequest request(QUrl(QString("http://localhost:%1/actionhistory").arg(QString::number(m_mockDevice1Port)))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); QByteArray actionHistory = reply->readAll(); QVERIFY2(actionHistory.isEmpty(), "Action is triggered while it should not have been. Current action history: \"" + actionHistory + "\""); reply->deleteLater(); } /***********************************************************************/ void TestRules::cleanup() { cleanupMockHistory(); cleanupRules(); } void TestRules::emptyRule() { QVariantMap params; params.insert("name", QString()); params.insert("actions", QVariantList()); QVariant response = injectAndWait("Rules.AddRule", params); verifyRuleError(response, RuleEngine::RuleErrorInvalidRuleFormat); } void TestRules::getInvalidRule() { QVariantMap params; params.insert("ruleId", QUuid::createUuid()); QVariant response = injectAndWait("Rules.GetRuleDetails", params); verifyRuleError(response, RuleEngine::RuleErrorRuleNotFound); } QVariant TestRules::validIntStateBasedRule(const QString &name, const bool &executable, const bool &enabled) { QVariantMap params; // StateDescriptor QVariantMap stateDescriptor; stateDescriptor.insert("stateTypeId", mockIntStateTypeId); stateDescriptor.insert("deviceId", m_mockDeviceId); stateDescriptor.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorLess)); stateDescriptor.insert("value", 25); // StateEvaluator QVariantMap stateEvaluator; stateEvaluator.insert("stateDescriptor", stateDescriptor); stateEvaluator.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd)); // RuleAction QVariantMap action; action.insert("actionTypeId", mockWithParamsActionTypeId); QVariantList actionParams; QVariantMap param1; param1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); param1.insert("value", 5); actionParams.append(param1); QVariantMap param2; param2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); param2.insert("value", true); actionParams.append(param2); action.insert("deviceId", m_mockDeviceId); action.insert("ruleActionParams", actionParams); // RuleExitAction QVariantMap exitAction; exitAction.insert("actionTypeId", mockWithoutParamsActionTypeId); exitAction.insert("deviceId", m_mockDeviceId); exitAction.insert("ruleActionParams", QVariantList()); params.insert("name", name); params.insert("enabled", enabled); params.insert("executable", executable); params.insert("stateEvaluator", stateEvaluator); params.insert("actions", QVariantList() << action); params.insert("exitActions", QVariantList() << exitAction); return params; } void TestRules::generateEvent(const EventTypeId &eventTypeId) { // Trigger an event QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); // trigger event in mock device QNetworkRequest request(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(m_mockDevice1Port).arg(eventTypeId.toString()))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); } void TestRules::initTestCase() { NymeaTestBase::initTestCase(); QLoggingCategory::setFilterRules("*.debug=false\n" "Tests.debug=true\n" "RuleEngine.debug=true\n" // "RuleEngineDebug.debug=true\n" "MockDevice.*=true"); } void TestRules::addRemoveRules_data() { // RuleAction QVariantMap validActionNoParams; validActionNoParams.insert("actionTypeId", mockWithoutParamsActionTypeId); validActionNoParams.insert("deviceId", m_mockDeviceId); validActionNoParams.insert("ruleActionParams", QVariantList()); QVariantMap invalidAction; invalidAction.insert("actionTypeId", ActionTypeId()); invalidAction.insert("deviceId", m_mockDeviceId); invalidAction.insert("ruleActionParams", QVariantList()); // RuleExitAction QVariantMap validExitActionNoParams; validExitActionNoParams.insert("actionTypeId", mockWithoutParamsActionTypeId); validExitActionNoParams.insert("deviceId", m_mockDeviceId); validExitActionNoParams.insert("ruleActionParams", QVariantList()); QVariantMap invalidExitAction; invalidExitAction.insert("actionTypeId", ActionTypeId()); invalidExitAction.insert("deviceId", m_mockDeviceId); invalidExitAction.insert("ruleActionParams", QVariantList()); // StateDescriptor QVariantMap stateDescriptor; stateDescriptor.insert("stateTypeId", mockIntStateTypeId); stateDescriptor.insert("deviceId", m_mockDeviceId); stateDescriptor.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorLess)); stateDescriptor.insert("value", 20); // StateEvaluator QVariantMap validStateEvaluator; validStateEvaluator.insert("stateDescriptor", stateDescriptor); validStateEvaluator.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd)); QVariantMap invalidStateEvaluator; stateDescriptor.remove("deviceId"); invalidStateEvaluator.insert("stateDescriptor", stateDescriptor); // EventDescriptor QVariantMap validEventDescriptor1; validEventDescriptor1.insert("eventTypeId", mockEvent1EventTypeId); validEventDescriptor1.insert("deviceId", m_mockDeviceId); validEventDescriptor1.insert("paramDescriptors", QVariantList()); QVariantMap validEventDescriptor2; validEventDescriptor2.insert("eventTypeId", mockEvent2EventTypeId); validEventDescriptor2.insert("deviceId", m_mockDeviceId); QVariantList params; QVariantMap param1; param1.insert("paramTypeId", mockEvent2EventIntParamParamTypeId); param1.insert("value", 3); param1.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorEquals)); params.append(param1); validEventDescriptor2.insert("paramDescriptors", params); QVariantMap validEventDescriptor3; validEventDescriptor3.insert("eventTypeId", mockEvent2EventTypeId); validEventDescriptor3.insert("deviceId", m_mockDeviceId); validEventDescriptor3.insert("paramDescriptors", QVariantList()); // EventDescriptorList QVariantList eventDescriptorList; eventDescriptorList.append(validEventDescriptor1); eventDescriptorList.append(validEventDescriptor2); QVariantMap invalidEventDescriptor; invalidEventDescriptor.insert("eventTypeId", mockEvent1EventTypeId); invalidEventDescriptor.insert("deviceId", DeviceId()); invalidEventDescriptor.insert("paramDescriptors", QVariantList()); // RuleAction event based QVariantMap validActionEventBased; validActionEventBased.insert("actionTypeId", mockWithParamsActionTypeId); validActionEventBased.insert("deviceId", m_mockDeviceId); QVariantMap validActionEventBasedParam1; validActionEventBasedParam1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); validActionEventBasedParam1.insert("eventTypeId", mockEvent2EventTypeId); validActionEventBasedParam1.insert("eventParamTypeId", mockEvent2EventIntParamParamTypeId); QVariantMap validActionEventBasedParam2; validActionEventBasedParam2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); validActionEventBasedParam2.insert("value", false); validActionEventBased.insert("ruleActionParams", QVariantList() << validActionEventBasedParam1 << validActionEventBasedParam2); QVariantMap invalidActionEventBased; invalidActionEventBased.insert("actionTypeId", mockWithoutParamsActionTypeId); invalidActionEventBased.insert("deviceId", m_mockDeviceId); validActionEventBasedParam1.insert("value", 10); invalidActionEventBased.insert("ruleActionParams", QVariantList() << validActionEventBasedParam1); QVariantMap invalidActionEventBased2; invalidActionEventBased2.insert("actionTypeId", mockWithParamsActionTypeId); invalidActionEventBased2.insert("deviceId", m_mockDeviceId); QVariantMap invalidActionEventBasedParam2; invalidActionEventBasedParam2.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); invalidActionEventBasedParam2.insert("eventTypeId", mockEvent1EventTypeId); invalidActionEventBasedParam2.insert("eventParamTypeId", "value"); QVariantMap invalidActionEventBasedParam3; invalidActionEventBasedParam3.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); invalidActionEventBasedParam3.insert("value", 2); invalidActionEventBased2.insert("ruleActionParams", QVariantList() << invalidActionEventBasedParam2 << invalidActionEventBasedParam3); QVariantMap invalidActionEventBased3; invalidActionEventBased3.insert("actionTypeId", mockWithParamsActionTypeId); invalidActionEventBased3.insert("deviceId", m_mockDeviceId); QVariantMap invalidActionEventBasedParam4; invalidActionEventBasedParam4.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); invalidActionEventBasedParam4.insert("eventTypeId", mockEvent1EventTypeId); invalidActionEventBasedParam4.insert("eventParamTypeId", mockEvent2EventIntParamParamTypeId); invalidActionEventBased3.insert("ruleActionParams", QVariantList() << invalidActionEventBasedParam4); QTest::addColumn("enabled"); QTest::addColumn("action1"); QTest::addColumn("exitAction1"); QTest::addColumn("eventDescriptor"); QTest::addColumn("eventDescriptorList"); QTest::addColumn("stateEvaluator"); QTest::addColumn("error"); QTest::addColumn("jsonError"); QTest::addColumn("name"); // Rules with event based actions QTest::newRow("valid rule. enabled, 1 Action (eventBased), 1 EventDescriptor, name") << true << validActionEventBased << QVariantMap() << validEventDescriptor3 << QVariantList() << QVariantMap() << RuleEngine::RuleErrorNoError << true << "ActionEventRule1"; QTest::newRow("invalid rule. enabled, 1 Action (eventBased), 1 EventDescriptor, name") << true << invalidActionEventBased2 << QVariantMap() << validEventDescriptor3 << QVariantList() << QVariantMap() << RuleEngine::RuleErrorInvalidRuleActionParameter << false << "TestRule"; QTest::newRow("invalid rule. enabled, 1 Action (eventBased), types not matching, name") << true << invalidActionEventBased3 << QVariantMap() << validEventDescriptor1 << QVariantList() << QVariantMap() << RuleEngine::RuleErrorTypesNotMatching << false << "TestRule"; QTest::newRow("invalid rule. enabled, 1 Action (eventBased), 1 EventDescriptor, name") << true << invalidActionEventBased << QVariantMap() << validEventDescriptor2 << QVariantList() << QVariantMap() << RuleEngine::RuleErrorTypesNotMatching << false << "TestRule"; QTest::newRow("invalid rule. enabled, 1 Action (eventBased), 1 StateEvaluator, name") << true << validActionEventBased << QVariantMap() << QVariantMap() << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorInvalidRuleActionParameter << false << "TestRule"; QTest::newRow("invalid rule. enabled, 1 Action (eventBased), 1 EventDescriptor, name") << true << validActionEventBased << validActionEventBased << validEventDescriptor2 << QVariantList() << QVariantMap() << RuleEngine::RuleErrorInvalidRuleFormat << false << "TestRule"; QTest::newRow("invalid rule. enabled, 1 Action, 1 ExitAction (EventBased), name") << true << validActionNoParams << validActionEventBased << validEventDescriptor2 << QVariantList() << QVariantMap() << RuleEngine::RuleErrorInvalidRuleFormat << false << "TestRule"; // Rules with exit actions QTest::newRow("valid rule. enabled, 1 Action, 1 Exit Action, 1 StateEvaluator, name") << true << validActionNoParams << validExitActionNoParams << QVariantMap() << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorNoError << true << "TestRule"; QTest::newRow("valid rule. disabled, 1 Action, 1 Exit Action, 1 StateEvaluator, name") << false << validActionNoParams << validExitActionNoParams << QVariantMap() << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorNoError << true << "TestRule"; QTest::newRow("invalid rule. disabled, 1 Action, 1 invalid Exit Action, 1 StateEvaluator, name") << false << validActionNoParams << invalidExitAction << QVariantMap() << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorActionTypeNotFound << false << "TestRule"; QTest::newRow("valid rule. 1 Action, 1 Exit Action, 1 EventDescriptor, 1 StateEvaluator, name") << true << validActionNoParams << validExitActionNoParams << validEventDescriptor1 << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorNoError << false << "TestRule"; QTest::newRow("invalid rule. 1 Action, 1 Exit Action, eventDescriptorList, NO StateEvaluator, name")<< true << validActionNoParams << validExitActionNoParams << QVariantMap() << eventDescriptorList << QVariantMap() << RuleEngine::RuleErrorInvalidRuleFormat << false << "TestRule"; QTest::newRow("valid rule. 1 Action, 1 Exit Action, eventDescriptorList, 1 StateEvaluator, name") << true << validActionNoParams << validExitActionNoParams << QVariantMap() << eventDescriptorList << validStateEvaluator << RuleEngine::RuleErrorNoError << false << "TestRule"; // Rules without exit actions QTest::newRow("valid rule. enabled, 1 EventDescriptor, StateEvaluator, 1 Action, name") << true << validActionNoParams << QVariantMap() << validEventDescriptor1 << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorNoError << true << "TestRule"; QTest::newRow("valid rule. disabled, 1 EventDescriptor, StateEvaluator, 1 Action, name") << false << validActionNoParams << QVariantMap() << validEventDescriptor1 << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorNoError << true << "TestRule"; QTest::newRow("valid rule. 2 EventDescriptors, 1 Action, name") << true << validActionNoParams << QVariantMap() << QVariantMap() << eventDescriptorList << validStateEvaluator << RuleEngine::RuleErrorNoError << true << "TestRule"; QTest::newRow("invalid action") << true << invalidAction << QVariantMap() << validEventDescriptor1 << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorActionTypeNotFound << false << "TestRule"; QTest::newRow("invalid event descriptor") << true << validActionNoParams << QVariantMap() << invalidEventDescriptor << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorEventTypeNotFound << false << "TestRule"; } void TestRules::addRemoveRules() { QFETCH(bool, enabled); QFETCH(QVariantMap, action1); QFETCH(QVariantMap, exitAction1); QFETCH(QVariantMap, eventDescriptor); QFETCH(QVariantList, eventDescriptorList); QFETCH(QVariantMap, stateEvaluator); QFETCH(RuleEngine::RuleError, error); QFETCH(bool, jsonError); QFETCH(QString, name); QVariantMap params; params.insert("name", name); QVariantList actions; actions.append(action1); params.insert("actions", actions); if (!eventDescriptor.isEmpty()) { params.insert("eventDescriptors", QVariantList() << eventDescriptor); } if (!eventDescriptorList.isEmpty()) { params.insert("eventDescriptors", eventDescriptorList); } QVariantList exitActions; if (!exitAction1.isEmpty()) { exitActions.append(exitAction1); params.insert("exitActions", exitActions); } params.insert("stateEvaluator", stateEvaluator); if (!enabled) { params.insert("enabled", enabled); } QVariant response = injectAndWait("Rules.AddRule", params); if (!jsonError) { verifyRuleError(response, error); } RuleId newRuleId = RuleId(response.toMap().value("params").toMap().value("ruleId").toString()); response = injectAndWait("Rules.GetRules"); QVariantList rules = response.toMap().value("params").toMap().value("ruleDescriptions").toList(); if (error != RuleEngine::RuleErrorNoError) { QVERIFY2(rules.count() == 0, "There should be no rules."); return; } QVERIFY2(rules.count() == 1, "There should be exactly one rule"); QCOMPARE(RuleId(rules.first().toMap().value("id").toString()), newRuleId); params.clear(); params.insert("ruleId", newRuleId); response = injectAndWait("Rules.GetRuleDetails", params); QVariantMap rule = response.toMap().value("params").toMap().value("rule").toMap(); qDebug() << rule.value("name").toString(); QVERIFY2(rule.value("enabled").toBool() == enabled, "Rule enabled state doesn't match"); QVariantList eventDescriptors = rule.value("eventDescriptors").toList(); if (!eventDescriptor.isEmpty()) { QVERIFY2(eventDescriptors.count() == 1, "There should be exactly one eventDescriptor"); QVERIFY2(eventDescriptors.first().toMap() == eventDescriptor, "Event descriptor doesn't match"); } else if (eventDescriptorList.isEmpty()){ QVERIFY2(eventDescriptors.count() == eventDescriptorList.count(), QString("There should be exactly %1 eventDescriptor").arg(eventDescriptorList.count()).toLatin1().data()); foreach (const QVariant &eventDescriptorVariant, eventDescriptorList) { bool found = false; foreach (const QVariant &replyEventDescriptorVariant, eventDescriptors) { if (eventDescriptorVariant.toMap().value("deviceId") == replyEventDescriptorVariant.toMap().value("deviceId") && eventDescriptorVariant.toMap().value("eventTypeId") == replyEventDescriptorVariant.toMap().value("eventTypeId")) { found = true; QVERIFY2(eventDescriptorVariant == replyEventDescriptorVariant, "Event descriptor doesn't match"); } } QVERIFY2(found, "Missing event descriptor"); } } QVariantList replyActions = rule.value("actions").toList(); QVERIFY2(actions == replyActions, "Actions don't match"); QVariantList replyExitActions = rule.value("exitActions").toList(); QVERIFY2(exitActions == replyExitActions, "ExitActions don't match"); params.clear(); params.insert("ruleId", newRuleId); response = injectAndWait("Rules.RemoveRule", params); verifyRuleError(response); response = injectAndWait("Rules.GetRules"); rules = response.toMap().value("params").toMap().value("ruleDescriptions").toList(); QVERIFY2(rules.count() == 0, "There should be no rules."); } void TestRules::editRules_data() { // RuleAction QVariantMap validActionNoParams; validActionNoParams.insert("actionTypeId", mockWithoutParamsActionTypeId); validActionNoParams.insert("deviceId", m_mockDeviceId); validActionNoParams.insert("ruleActionParams", QVariantList()); QVariantMap invalidAction; invalidAction.insert("actionTypeId", ActionTypeId()); invalidAction.insert("deviceId", m_mockDeviceId); invalidAction.insert("ruleActionParams", QVariantList()); // RuleExitAction QVariantMap validExitActionNoParams; validExitActionNoParams.insert("actionTypeId", mockWithoutParamsActionTypeId); validExitActionNoParams.insert("deviceId", m_mockDeviceId); validExitActionNoParams.insert("ruleActionParams", QVariantList()); QVariantMap invalidExitAction; invalidExitAction.insert("actionTypeId", ActionTypeId()); invalidExitAction.insert("deviceId", m_mockDeviceId); invalidExitAction.insert("ruleActionParams", QVariantList()); // StateDescriptor QVariantMap stateDescriptor; stateDescriptor.insert("stateTypeId", mockIntStateTypeId); stateDescriptor.insert("deviceId", m_mockDeviceId); stateDescriptor.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorLess)); stateDescriptor.insert("value", 20); // StateEvaluator QVariantMap validStateEvaluator; validStateEvaluator.insert("stateDescriptor", stateDescriptor); validStateEvaluator.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd)); QVariantMap invalidStateEvaluator; stateDescriptor.remove("deviceId"); invalidStateEvaluator.insert("stateDescriptor", stateDescriptor); // EventDescriptor QVariantMap validEventDescriptor1; validEventDescriptor1.insert("eventTypeId", mockEvent1EventTypeId); validEventDescriptor1.insert("deviceId", m_mockDeviceId); validEventDescriptor1.insert("paramDescriptors", QVariantList()); QVariantMap validEventDescriptor2; validEventDescriptor2.insert("eventTypeId", mockEvent2EventTypeId); validEventDescriptor2.insert("deviceId", m_mockDeviceId); QVariantList params; QVariantMap param1; param1.insert("paramTypeId", mockEvent2EventIntParamParamTypeId); param1.insert("value", 3); param1.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorEquals)); params.append(param1); validEventDescriptor2.insert("paramDescriptors", params); QVariantMap validEventDescriptor3; validEventDescriptor3.insert("eventTypeId", mockEvent2EventTypeId); validEventDescriptor3.insert("deviceId", m_mockDeviceId); validEventDescriptor3.insert("paramDescriptors", QVariantList()); // EventDescriptorList QVariantList eventDescriptorList; eventDescriptorList.append(validEventDescriptor1); eventDescriptorList.append(validEventDescriptor2); QVariantMap invalidEventDescriptor; invalidEventDescriptor.insert("eventTypeId", mockEvent1EventTypeId); invalidEventDescriptor.insert("deviceId", DeviceId()); invalidEventDescriptor.insert("paramDescriptors", QVariantList()); // RuleAction event based QVariantMap validActionEventBased; validActionEventBased.insert("actionTypeId", mockWithParamsActionTypeId); validActionEventBased.insert("deviceId", m_mockDeviceId); QVariantMap validActionEventBasedParam1; validActionEventBasedParam1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); validActionEventBasedParam1.insert("eventTypeId", mockEvent2EventTypeId); validActionEventBasedParam1.insert("eventParamTypeId", mockEvent2EventIntParamParamTypeId); QVariantMap validActionEventBasedParam2; validActionEventBasedParam2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); validActionEventBasedParam2.insert("value", false); validActionEventBased.insert("ruleActionParams", QVariantList() << validActionEventBasedParam1 << validActionEventBasedParam2); QVariantMap invalidActionEventBased; invalidActionEventBased.insert("actionTypeId", mockWithoutParamsActionTypeId); invalidActionEventBased.insert("deviceId", m_mockDeviceId); validActionEventBasedParam1.insert("value", 10); invalidActionEventBased.insert("ruleActionParams", QVariantList() << validActionEventBasedParam1); QVariantMap invalidActionEventBased2; invalidActionEventBased2.insert("actionTypeId", mockWithParamsActionTypeId); invalidActionEventBased2.insert("deviceId", m_mockDeviceId); QVariantMap invalidActionEventBasedParam2; invalidActionEventBasedParam2.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); invalidActionEventBasedParam2.insert("eventTypeId", mockEvent1EventTypeId); invalidActionEventBasedParam2.insert("eventParamTypeId", "value"); QVariantMap invalidActionEventBasedParam3; invalidActionEventBasedParam3.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); invalidActionEventBasedParam3.insert("value", 2); invalidActionEventBased2.insert("ruleActionParams", QVariantList() << invalidActionEventBasedParam2 << invalidActionEventBasedParam3); QVariantMap invalidActionEventBased3; invalidActionEventBased3.insert("actionTypeId", mockWithParamsActionTypeId); invalidActionEventBased3.insert("deviceId", m_mockDeviceId); QVariantMap invalidActionEventBasedParam4; invalidActionEventBasedParam4.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); invalidActionEventBasedParam4.insert("eventTypeId", mockEvent1EventTypeId); invalidActionEventBasedParam4.insert("eventParamTypeId", mockEvent2EventIntParamParamTypeId); invalidActionEventBased3.insert("ruleActionParams", QVariantList() << invalidActionEventBasedParam4); QTest::addColumn("enabled"); QTest::addColumn("action"); QTest::addColumn("exitAction"); QTest::addColumn("eventDescriptor"); QTest::addColumn("eventDescriptorList"); QTest::addColumn("stateEvaluator"); QTest::addColumn("error"); QTest::addColumn("name"); // Rules with event based actions QTest::newRow("valid rule. enabled, 1 Action (eventBased), 1 EventDescriptor, name") << true << validActionEventBased << QVariantMap() << validEventDescriptor3 << QVariantList() << QVariantMap() << RuleEngine::RuleErrorNoError << "ActionEventRule1"; QTest::newRow("invalid rule. enabled, 1 Action (eventBased), 1 EventDescriptor, name") << true << invalidActionEventBased2 << QVariantMap() << validEventDescriptor3 << QVariantList() << QVariantMap() << RuleEngine::RuleErrorInvalidRuleActionParameter << "TestRule"; QTest::newRow("invalid rule. enabled, 1 Action (eventBased), types not matching, name") << true << invalidActionEventBased3 << QVariantMap() << validEventDescriptor1 << QVariantList() << QVariantMap() << RuleEngine::RuleErrorTypesNotMatching << "TestRule"; QTest::newRow("invalid rule. enabled, 1 Action (eventBased), 1 EventDescriptor, name") << true << invalidActionEventBased << QVariantMap() << validEventDescriptor2 << QVariantList() << QVariantMap() << RuleEngine::RuleErrorTypesNotMatching << "TestRule"; QTest::newRow("invalid rule. enabled, 1 Action (eventBased), 1 StateEvaluator, name") << true << validActionEventBased << QVariantMap() << QVariantMap() << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorInvalidRuleActionParameter << "TestRule"; QTest::newRow("invalid rule. enabled, 1 Action (eventBased), 1 EventDescriptor, name") << true << validActionEventBased << validActionEventBased << validEventDescriptor2 << QVariantList() << QVariantMap() << RuleEngine::RuleErrorInvalidRuleFormat << "TestRule"; QTest::newRow("invalid rule. enabled, 1 Action, 1 ExitAction (EventBased), name") << true << validActionNoParams << validActionEventBased << validEventDescriptor2 << QVariantList() << QVariantMap() << RuleEngine::RuleErrorInvalidRuleFormat << "TestRule"; // Rules with exit actions QTest::newRow("valid rule. enabled, 1 Action, 1 Exit Action, 1 StateEvaluator, name") << true << validActionNoParams << validExitActionNoParams << QVariantMap() << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorNoError << "TestRule"; QTest::newRow("valid rule. disabled, 1 Action, 1 Exit Action, 1 StateEvaluator, name") << false << validActionNoParams << validExitActionNoParams << QVariantMap() << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorNoError << "TestRule"; QTest::newRow("valid rule. 1 Action, 1 Exit Action, 1 EventDescriptor, 1 StateEvaluator, name") << true << validActionNoParams << validExitActionNoParams << validEventDescriptor1 << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorNoError << "TestRule"; QTest::newRow("valid rule. 1 Action, 1 Exit Action, eventDescriptorList, 1 StateEvaluator, name") << true << validActionNoParams << validExitActionNoParams << QVariantMap() << eventDescriptorList << validStateEvaluator << RuleEngine::RuleErrorNoError << "TestRule"; // Rules without exit actions QTest::newRow("valid rule. enabled, 1 EventDescriptor, StateEvaluator, 1 Action, name") << true << validActionNoParams << QVariantMap() << validEventDescriptor1 << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorNoError << "TestRule"; QTest::newRow("valid rule. disabled, 1 EventDescriptor, StateEvaluator, 1 Action, name") << false << validActionNoParams << QVariantMap() << validEventDescriptor1 << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorNoError << "TestRule"; QTest::newRow("valid rule. 2 EventDescriptors, 1 Action, name") << true << validActionNoParams << QVariantMap() << QVariantMap() << eventDescriptorList << validStateEvaluator << RuleEngine::RuleErrorNoError << "TestRule"; } void TestRules::editRules() { QFETCH(bool, enabled); QFETCH(QVariantMap, action); QFETCH(QVariantMap, exitAction); QFETCH(QVariantMap, eventDescriptor); QFETCH(QVariantList, eventDescriptorList); QFETCH(QVariantMap, stateEvaluator); QFETCH(RuleEngine::RuleError, error); QFETCH(QString, name); // Add the rule we want to edit QVariantList eventParamDescriptors; QVariantMap eventDescriptor1; eventDescriptor1.insert("eventTypeId", mockEvent1EventTypeId); eventDescriptor1.insert("deviceId", m_mockDeviceId); eventDescriptor1.insert("paramDescriptors", QVariantList()); QVariantMap eventDescriptor2; eventDescriptor2.insert("eventTypeId", mockEvent2EventTypeId); eventDescriptor2.insert("deviceId", m_mockDeviceId); eventDescriptor2.insert("paramDescriptors", QVariantList()); QVariantMap eventParam1; eventParam1.insert("paramTypeId", mockEvent2EventIntParamParamTypeId); eventParam1.insert("value", 3); eventParam1.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorEquals)); eventParamDescriptors.append(eventParam1); eventDescriptor2.insert("paramDescriptors", eventParamDescriptors); QVariantList eventDescriptorList1; eventDescriptorList1.append(eventDescriptor1); eventDescriptorList1.append(eventDescriptor2); QVariantMap stateEvaluator0; QVariantMap stateDescriptor1; stateDescriptor1.insert("deviceId", m_mockDeviceId); stateDescriptor1.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorEquals)); stateDescriptor1.insert("stateTypeId", mockIntStateTypeId); stateDescriptor1.insert("value", 1); QVariantMap stateDescriptor2; stateDescriptor2.insert("deviceId", m_mockDeviceId); stateDescriptor2.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorEquals)); stateDescriptor2.insert("stateTypeId", mockBoolStateTypeId); stateDescriptor2.insert("value", true); QVariantMap stateEvaluator1; stateEvaluator1.insert("stateDescriptor", stateDescriptor1); stateEvaluator1.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd)); QVariantMap stateEvaluator2; stateEvaluator2.insert("stateDescriptor", stateDescriptor2); stateEvaluator2.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd)); QVariantList childEvaluators; childEvaluators.append(stateEvaluator1); childEvaluators.append(stateEvaluator2); stateEvaluator0.insert("childEvaluators", childEvaluators); stateEvaluator0.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd)); QVariantMap action1; action1.insert("actionTypeId", mockWithoutParamsActionTypeId); action1.insert("deviceId", m_mockDeviceId); action1.insert("ruleActionParams", QVariantList()); QVariantMap action2; action2.insert("actionTypeId", mockWithParamsActionTypeId); qDebug() << "got action id" << mockWithParamsActionTypeId; action2.insert("deviceId", m_mockDeviceId); QVariantList action2Params; QVariantMap action2Param1; action2Param1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); action2Param1.insert("value", 5); action2Params.append(action2Param1); QVariantMap action2Param2; action2Param2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); action2Param2.insert("value", true); action2Params.append(action2Param2); action2.insert("ruleActionParams", action2Params); // RuleAction event based QVariantMap validActionEventBased; validActionEventBased.insert("actionTypeId", mockWithParamsActionTypeId); validActionEventBased.insert("deviceId", m_mockDeviceId); QVariantMap validActionEventBasedParam1; validActionEventBasedParam1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); validActionEventBasedParam1.insert("eventTypeId", mockEvent2EventTypeId); validActionEventBasedParam1.insert("eventParamTypeId", mockEvent2EventIntParamParamTypeId); QVariantMap validActionEventBasedParam2; validActionEventBasedParam2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); validActionEventBasedParam2.insert("value", false); validActionEventBased.insert("ruleActionParams", QVariantList() << validActionEventBasedParam1 << validActionEventBasedParam2); QVariantList validEventDescriptors3; QVariantMap validEventDescriptor3; validEventDescriptor3.insert("eventTypeId", mockEvent2EventTypeId); validEventDescriptor3.insert("deviceId", m_mockDeviceId); validEventDescriptor3.insert("paramDescriptors", QVariantList()); validEventDescriptors3.append(validEventDescriptor3); QVariantMap params; QVariantList actions; actions.append(action1); actions.append(action2); params.insert("actions", actions); params.insert("eventDescriptors", eventDescriptorList1); params.insert("stateEvaluator", stateEvaluator0); params.insert("name", "TestRule"); QVariant response = injectAndWait("Rules.AddRule", params); RuleId ruleId = RuleId(response.toMap().value("params").toMap().value("ruleId").toString()); verifyRuleError(response); // enable notifications QCOMPARE(enableNotifications(), true); // now create the new rule and edit the original one params.clear(); params.insert("ruleId", ruleId.toString()); params.insert("name", name); if (!eventDescriptor.isEmpty()) { params.insert("eventDescriptors", QVariantList() << eventDescriptor); } if (!eventDescriptorList.isEmpty()) { params.insert("eventDescriptors", eventDescriptorList); } actions.clear(); actions.append(action); params.insert("actions", actions); QVariantList exitActions; if (!exitAction.isEmpty()) { exitActions.append(exitAction); params.insert("exitActions", exitActions); } params.insert("stateEvaluator", stateEvaluator); if (!enabled) { params.insert("enabled", enabled); } // Setup connection to mock client QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray))); response.clear(); response = injectAndWait("Rules.EditRule", params); verifyRuleError(response, error); if (error == RuleEngine::RuleErrorNoError){ clientSpy.wait(1); // We need to get exactly 2 replies. The actual reply and the Changed notification // Make sure there are no other notifications (e.g. RuleAdded or similar) QCOMPARE(clientSpy.count(), 2); QVariant notification = checkNotification(clientSpy, "Rules.RuleConfigurationChanged"); QVERIFY2(notification != QVariant(), "not received \"Rules.RuleConfigurationChanged\" notification"); // now check if the received rule matches the our new rule QVariantMap rule = response.toMap().value("params").toMap().value("rule").toMap(); QVERIFY2(rule.value("enabled").toBool() == enabled, "Rule enabled state doesn't match"); QVariantList eventDescriptors = rule.value("eventDescriptors").toList(); if (!eventDescriptor.isEmpty()) { QVERIFY2(eventDescriptors.count() == 1, "There should be exactly one eventDescriptor"); QVERIFY2(eventDescriptors.first().toMap() == eventDescriptor, "Event descriptor doesn't match"); } else if (eventDescriptorList.isEmpty()){ QVERIFY2(eventDescriptors.count() == eventDescriptorList.count(), QString("There should be exactly %1 eventDescriptor").arg(eventDescriptorList.count()).toLatin1().data()); foreach (const QVariant &eventDescriptorVariant, eventDescriptorList) { bool found = false; foreach (const QVariant &replyEventDescriptorVariant, eventDescriptors) { if (eventDescriptorVariant.toMap().value("deviceId") == replyEventDescriptorVariant.toMap().value("deviceId") && eventDescriptorVariant.toMap().value("eventTypeId") == replyEventDescriptorVariant.toMap().value("eventTypeId")) { found = true; QVERIFY2(eventDescriptorVariant == replyEventDescriptorVariant, "Event descriptor doesn't match"); } } QVERIFY2(found, "Missing event descriptor"); } } QVariantList replyActions = rule.value("actions").toList(); QVERIFY2(actions == replyActions, "Actions don't match"); QVariantList replyExitActions = rule.value("exitActions").toList(); QVERIFY2(exitActions == replyExitActions, "ExitActions don't match"); } // Remove the rule params.clear(); params.insert("ruleId", ruleId); response = injectAndWait("Rules.RemoveRule", params); verifyRuleError(response); // check if removed response = injectAndWait("Rules.GetRules"); QVariantList rules = response.toMap().value("params").toMap().value("rules").toList(); QVERIFY2(rules.count() == 0, "There should be no rules."); } void TestRules::executeRuleActions_data() { QTest::addColumn("params"); QTest::addColumn("ruleError"); QTest::newRow("executable rule, enabled") << validIntStateBasedRule("Executeable", true, true).toMap() << RuleEngine::RuleErrorNoError; QTest::newRow("executable rule, disabled") << validIntStateBasedRule("Executeable", true, false).toMap() << RuleEngine::RuleErrorNoError; QTest::newRow("not executable rule, enabled") << validIntStateBasedRule("Not Executable", false, true).toMap() << RuleEngine::RuleErrorNotExecutable; QTest::newRow("not executable rule, disabled") << validIntStateBasedRule("Not Executable", false, false).toMap() << RuleEngine::RuleErrorNotExecutable; } void TestRules::executeRuleActions() { QFETCH(QVariantMap, params); QFETCH(RuleEngine::RuleError, ruleError); // ADD rule QVariant response = injectAndWait("Rules.AddRule", params); verifyRuleError(response); RuleId ruleId = RuleId(response.toMap().value("params").toMap().value("ruleId").toString()); QVERIFY(!ruleId.isNull()); cleanupMockHistory(); QTest::qWait(200); // EEXCUTE action invalid ruleId QVariantMap executeParams; executeParams.insert("ruleId", QUuid::createUuid().toString()); response = injectAndWait("Rules.ExecuteActions", executeParams); verifyRuleError(response, RuleEngine::RuleErrorRuleNotFound); // EXECUTE actions qDebug() << "Execute rule actions"; executeParams.clear(); executeParams.insert("ruleId", ruleId.toString()); response = injectAndWait("Rules.ExecuteActions", executeParams); verifyRuleError(response, ruleError); // give the ruleeingine time to execute the actions QTest::qWait(1000); if (ruleError == RuleEngine::RuleErrorNoError) { verifyRuleExecuted(mockWithParamsActionTypeId); } else { verifyRuleNotExecuted(); } cleanupMockHistory(); QTest::qWait(200); // EXECUTE exit actions invalid ruleId executeParams.clear(); executeParams.insert("ruleId", QUuid::createUuid().toString()); response = injectAndWait("Rules.ExecuteExitActions", executeParams); verifyRuleError(response, RuleEngine::RuleErrorRuleNotFound); // EXECUTE exit actions qDebug() << "Execute rule exit actions"; executeParams.clear(); executeParams.insert("ruleId", ruleId.toString()); response = injectAndWait("Rules.ExecuteExitActions", executeParams); verifyRuleError(response, ruleError); // give the ruleeingine time to execute the actions QTest::qWait(1000); if (ruleError == RuleEngine::RuleErrorNoError) { verifyRuleExecuted(mockWithoutParamsActionTypeId); } else { verifyRuleNotExecuted(); } cleanupMockHistory(); // REMOVE rule QVariantMap removeParams; removeParams.insert("ruleId", ruleId); response = injectAndWait("Rules.RemoveRule", removeParams); verifyRuleError(response); } void TestRules::findRule() { // ADD rule QVariantMap params = validIntStateBasedRule("Executeable", true, true).toMap(); QVariant response = injectAndWait("Rules.AddRule", params); verifyRuleError(response); RuleId ruleId = RuleId(response.toMap().value("params").toMap().value("ruleId").toString()); QVERIFY(!ruleId.isNull()); params.clear(); params.insert("deviceId", m_mockDeviceId); response = injectAndWait("Rules.FindRules", params); QCOMPARE(response.toMap().value("params").toMap().value("ruleIds").toList().count(), 1); QCOMPARE(response.toMap().value("params").toMap().value("ruleIds").toList().first().toString(), ruleId.toString()); // REMOVE rule QVariantMap removeParams; removeParams.insert("ruleId", ruleId); response = injectAndWait("Rules.RemoveRule", removeParams); verifyRuleError(response); } void TestRules::removeInvalidRule() { QVariantMap params; params.insert("ruleId", RuleId::createRuleId()); QVariant response = injectAndWait("Rules.RemoveRule", params); verifyRuleError(response, RuleEngine::RuleErrorRuleNotFound); } void TestRules::loadStoreConfig() { QVariantMap eventDescriptor1; eventDescriptor1.insert("eventTypeId", mockEvent1EventTypeId); eventDescriptor1.insert("deviceId", m_mockDeviceId); eventDescriptor1.insert("paramDescriptors", QVariantList()); QVariantMap eventDescriptor2; eventDescriptor2.insert("eventTypeId", mockEvent2EventTypeId); eventDescriptor2.insert("deviceId", m_mockDeviceId); eventDescriptor2.insert("paramDescriptors", QVariantList()); QVariantList eventParamDescriptors; QVariantMap eventParam1; eventParam1.insert("paramTypeId", mockEvent2EventIntParamParamTypeId); eventParam1.insert("value", 3); eventParam1.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorEquals)); eventParamDescriptors.append(eventParam1); eventDescriptor2.insert("paramDescriptors", eventParamDescriptors); QVariantList eventDescriptorList; eventDescriptorList.append(eventDescriptor1); eventDescriptorList.append(eventDescriptor2); QVariantMap stateEvaluator1; QVariantList childEvaluators; QVariantMap stateDescriptor2; stateDescriptor2.insert("deviceId", m_mockDeviceId); stateDescriptor2.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorEquals)); stateDescriptor2.insert("stateTypeId", mockIntStateTypeId); stateDescriptor2.insert("value", 1); QVariantMap stateEvaluator2; stateEvaluator2.insert("stateDescriptor", stateDescriptor2); stateEvaluator2.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd)); QVariantMap stateDescriptor3; stateDescriptor3.insert("deviceId", m_mockDeviceId); stateDescriptor3.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorEquals)); stateDescriptor3.insert("stateTypeId", mockBoolStateTypeId); stateDescriptor3.insert("value", true); QVariantMap stateEvaluator3; stateEvaluator3.insert("stateDescriptor", stateDescriptor3); stateEvaluator3.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd)); QVariantMap stateDescriptor4; stateDescriptor4.insert("interface", "battery"); stateDescriptor4.insert("interfaceState", "batteryCritical"); stateDescriptor4.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorEquals)); stateDescriptor4.insert("value", true); QVariantMap stateEvaluator4; stateEvaluator4.insert("stateDescriptor", stateDescriptor4); stateEvaluator4.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd)); childEvaluators.append(stateEvaluator2); childEvaluators.append(stateEvaluator3); childEvaluators.append(stateEvaluator4); stateEvaluator1.insert("childEvaluators", childEvaluators); stateEvaluator1.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd)); QVariantMap action1; action1.insert("actionTypeId", mockWithoutParamsActionTypeId); action1.insert("deviceId", m_mockDeviceId); action1.insert("ruleActionParams", QVariantList()); QVariantMap action2; action2.insert("actionTypeId", mockWithParamsActionTypeId); action2.insert("deviceId", m_mockDeviceId); QVariantList action2Params; QVariantMap action2Param1; action2Param1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); action2Param1.insert("value", 5); action2Params.append(action2Param1); QVariantMap action2Param2; action2Param2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); action2Param2.insert("value", true); action2Params.append(action2Param2); action2.insert("ruleActionParams", action2Params); // RuleAction event based QVariantMap validActionEventBased; validActionEventBased.insert("actionTypeId", mockWithParamsActionTypeId); validActionEventBased.insert("deviceId", m_mockDeviceId); QVariantMap validActionEventBasedParam1; validActionEventBasedParam1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); validActionEventBasedParam1.insert("eventTypeId", mockEvent2EventTypeId); validActionEventBasedParam1.insert("eventParamTypeId", mockEvent2EventIntParamParamTypeId); QVariantMap validActionEventBasedParam2; validActionEventBasedParam2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); validActionEventBasedParam2.insert("value", false); validActionEventBased.insert("ruleActionParams", QVariantList() << validActionEventBasedParam1 << validActionEventBasedParam2); QVariantList validEventDescriptors3; QVariantMap validEventDescriptor3; validEventDescriptor3.insert("eventTypeId", mockEvent2EventTypeId); validEventDescriptor3.insert("deviceId", m_mockDeviceId); validEventDescriptor3.insert("paramDescriptors", QVariantList()); validEventDescriptors3.append(validEventDescriptor3); // Interface based event descriptor QVariantMap eventDescriptorInterfaces; eventDescriptorInterfaces.insert("interface", "battery"); eventDescriptorInterfaces.insert("interfaceEvent", "batteryCritical"); QVariantMap eventDescriptorInterfacesParam; eventDescriptorInterfacesParam.insert("paramName", "batteryCritical"); eventDescriptorInterfacesParam.insert("value", true); eventDescriptorInterfacesParam.insert("operator", "ValueOperatorEquals"); QVariantList eventDescriptorInterfacesParams; eventDescriptorInterfacesParams.append(eventDescriptorInterfacesParam); eventDescriptorInterfaces.insert("paramDescriptors", eventDescriptorInterfacesParams); QVariantList eventDescriptorsInterfaces; eventDescriptorsInterfaces.append(eventDescriptorInterfaces); // Interface based state evaluator QVariantMap stateDescriptorInterfaces; stateDescriptorInterfaces.insert("interface", "battery"); stateDescriptorInterfaces.insert("interfaceState", "batteryCritical"); stateDescriptorInterfaces.insert("operator", "ValueOperatorEquals"); stateDescriptorInterfaces.insert("value", true); QVariantMap stateEvaluatorInterfaces; stateEvaluatorInterfaces.insert("stateDescriptor", stateDescriptorInterfaces); stateEvaluatorInterfaces.insert("operator", "StateOperatorAnd"); // Interface based actions QVariantMap ruleActionParamInterfaces; ruleActionParamInterfaces.insert("paramName", "power"); ruleActionParamInterfaces.insert("value", true); QVariantList ruleActionParamsInterfaces; ruleActionParamsInterfaces.append(ruleActionParamInterfaces); QVariantMap actionInterfaces; actionInterfaces.insert("interface", "light"); actionInterfaces.insert("interfaceAction", "power"); actionInterfaces.insert("ruleActionParams", ruleActionParamsInterfaces); QVariantList actionsInterfaces; actionsInterfaces.append(actionInterfaces); // rule 1 qCDebug(dcTests()) << "Adding rule 1"; QVariantMap params; QVariantList actions; actions.append(action1); actions.append(action2); params.insert("actions", actions); params.insert("eventDescriptors", eventDescriptorList); params.insert("stateEvaluator", stateEvaluator1); params.insert("name", "TestRule"); QVariant response = injectAndWait("Rules.AddRule", params); RuleId newRuleId = RuleId(response.toMap().value("params").toMap().value("ruleId").toString()); verifyRuleError(response); // rule 2 qCDebug(dcTests()) << "Adding rule 2"; QVariantMap params2; QVariantList actions2; actions2.append(action1); QVariantList exitActions2; exitActions2.append(action2); params2.insert("actions", actions2); params2.insert("exitActions", exitActions2); params2.insert("stateEvaluator", stateEvaluator1); params2.insert("name", "TestRule2"); QVariant response2 = injectAndWait("Rules.AddRule", params2); RuleId newRuleId2 = RuleId(response2.toMap().value("params").toMap().value("ruleId").toString()); verifyRuleError(response2); // rule 3 qCDebug(dcTests()) << "Adding rule 3"; QVariantMap params3; QVariantList actions3; actions3.append(validActionEventBased); params3.insert("actions", actions3); params3.insert("eventDescriptors", validEventDescriptors3); params3.insert("name", "TestRule3"); QVariant response3 = injectAndWait("Rules.AddRule", params3); RuleId newRuleId3 = RuleId(response3.toMap().value("params").toMap().value("ruleId").toString()); verifyRuleError(response3); // rule 4, interface based qCDebug(dcTests()) << "Adding rule 4"; QVariantMap params4; params4.insert("name", "TestRule4 - Interface based"); params4.insert("eventDescriptors", eventDescriptorsInterfaces); params4.insert("stateEvaluator", stateEvaluatorInterfaces); params4.insert("actions", actionsInterfaces); QVariant response4 = injectAndWait("Rules.AddRule", params4); RuleId newRuleId4 = RuleId(response4.toMap().value("params").toMap().value("ruleId").toString()); verifyRuleError(response4); qCDebug(dcTests()) << "Getting rules"; response = injectAndWait("Rules.GetRules"); QVariantList rules = response.toMap().value("params").toMap().value("ruleDescriptions").toList(); qDebug() << "GetRules before server shutdown:" << response; qCDebug(dcTests()) << "Restarting server"; restartServer(); response = injectAndWait("Rules.GetRules"); rules = response.toMap().value("params").toMap().value("ruleDescriptions").toList(); QVERIFY2(rules.count() == 4, "There should be exactly four rule."); QStringList idList; foreach (const QVariant &ruleDescription, rules) { idList.append(ruleDescription.toMap().value("id").toString()); } QVERIFY2(idList.contains(newRuleId.toString()), "Rule 1 should be in ruleIds list."); QVERIFY2(idList.contains(newRuleId2.toString()), "Rule 2 should be in ruleIds list."); QVERIFY2(idList.contains(newRuleId3.toString()), "Rule 3 should be in ruleIds list."); QVERIFY2(idList.contains(newRuleId4.toString()), "Rule 4 should be in ruleIds list."); // Rule 1 params.clear(); params.insert("ruleId", newRuleId); response.clear(); response = injectAndWait("Rules.GetRuleDetails", params); QVariantMap rule1 = response.toMap().value("params").toMap().value("rule").toMap(); QVariantList eventDescriptors = rule1.value("eventDescriptors").toList(); QVERIFY2(eventDescriptors.count() == 2, "There should be exactly 2 eventDescriptors"); foreach (const QVariant &expectedEventDescriptorVariant, eventDescriptorList) { bool found = false; foreach (const QVariant &replyEventDescriptorVariant, eventDescriptors) { if (expectedEventDescriptorVariant.toMap().value("eventTypeId") == replyEventDescriptorVariant.toMap().value("eventTypeId") && expectedEventDescriptorVariant.toMap().value("deviceId") == replyEventDescriptorVariant.toMap().value("deviceId")) { found = true; qDebug() << endl << replyEventDescriptorVariant << endl << expectedEventDescriptorVariant; QVERIFY2(replyEventDescriptorVariant == expectedEventDescriptorVariant, "EventDescriptor doesn't match"); } } QVERIFY2(found, "missing eventdescriptor"); } qDebug() << "Rule after loading from config:" << rule1; QVERIFY2(rule1.value("name").toString() == "TestRule", "Loaded wrong name for rule"); QVariantMap replyStateEvaluator= rule1.value("stateEvaluator").toMap(); QVariantList replyChildEvaluators = replyStateEvaluator.value("childEvaluators").toList(); QCOMPARE(replyChildEvaluators.count(), 3); QVERIFY2(replyStateEvaluator.value("operator") == "StateOperatorAnd", "There should be the AND operator."); foreach (const QVariant &childEvaluator, replyChildEvaluators) { QVERIFY2(childEvaluator.toMap().contains("stateDescriptor"), "StateDescriptor missing in StateEvaluator"); QVariantMap stateDescriptor = childEvaluator.toMap().value("stateDescriptor").toMap(); if (stateDescriptor.contains("deviceId") && stateDescriptor.contains("stateTypeId")) { QVERIFY2(stateDescriptor.value("deviceId") == m_mockDeviceId, "DeviceId of stateDescriptor does not match"); QVERIFY2(stateDescriptor.value("stateTypeId") == mockIntStateTypeId || stateDescriptor.value("stateTypeId") == mockBoolStateTypeId, "StateTypeId of stateDescriptor doesn't match"); } else if (stateDescriptor.contains("interface") && stateDescriptor.contains("interfaceState")) { QVERIFY2(stateDescriptor.value("interface") == "battery", "Interface of stateDescriptor does not match"); QVERIFY2(stateDescriptor.value("interfaceState") == "batteryCritical", "InterfaceState of stateDescriptor doesn't match"); } else { QVERIFY2(false, "StateDescriptor must have either deviceId/stateTypeId or interface/interfaceState."); } } QVariantList replyActions = rule1.value("actions").toList(); foreach (const QVariant &actionVariant, actions) { bool found = false; foreach (const QVariant &replyActionVariant, replyActions) { if (actionVariant.toMap().value("actionTypeId") == replyActionVariant.toMap().value("actionTypeId") && actionVariant.toMap().value("deviceId") == replyActionVariant.toMap().value("deviceId")) { found = true; // Check rule action params QVariantList actionParams = actionVariant.toMap().value("ruleActionParams").toList(); QVariantList replyActionParams = replyActionVariant.toMap().value("ruleActionParams").toList(); QVERIFY2(actionParams.count() == replyActionParams.count(), "Not the same list size of action params"); foreach (const QVariant &ruleParam, actionParams) { QVERIFY(replyActionParams.contains(ruleParam)); } } } QVERIFY2(found, "Action not found after loading from config."); } // Rule 2 params.clear(); params.insert("ruleId", newRuleId2); response.clear(); response = injectAndWait("Rules.GetRuleDetails", params); QVariantMap rule2 = response.toMap().value("params").toMap().value("rule").toMap(); QVERIFY2(rule2.value("name").toString() == "TestRule2", "Loaded wrong name for rule"); QVariantMap replyStateEvaluator2= rule2.value("stateEvaluator").toMap(); QVariantList replyChildEvaluators2 = replyStateEvaluator.value("childEvaluators").toList(); QVERIFY2(replyStateEvaluator2.value("operator") == "StateOperatorAnd", "There should be the AND operator."); QCOMPARE(replyChildEvaluators2.count(), 3); foreach (const QVariant &childEvaluator, replyChildEvaluators2) { QVERIFY2(childEvaluator.toMap().contains("stateDescriptor"), "StateDescriptor missing in StateEvaluator"); QVariantMap stateDescriptor = childEvaluator.toMap().value("stateDescriptor").toMap(); if (stateDescriptor.contains("deviceId") && stateDescriptor.contains("stateTypeId")) { QVERIFY2(stateDescriptor.value("deviceId") == m_mockDeviceId, "DeviceId of stateDescriptor does not match"); QVERIFY2(stateDescriptor.value("stateTypeId") == mockIntStateTypeId || stateDescriptor.value("stateTypeId") == mockBoolStateTypeId, "StateTypeId of stateDescriptor doesn't match"); } else if (stateDescriptor.contains("interface") && stateDescriptor.contains("interfaceState")) { QVERIFY2(stateDescriptor.value("interface") == "battery", "Interface of stateDescriptor does not match"); QVERIFY2(stateDescriptor.value("interfaceState") == "batteryCritical", "InterfaceState of stateDescriptor doesn't match"); } else { QVERIFY2(false, "StateDescriptor must have either deviceId/stateTypeId or interface/interfaceState."); } } QVariantList replyActions2 = rule2.value("actions").toList(); QVERIFY2(replyActions2.count() == 1, "Rule 2 should have exactly 1 action"); foreach (const QVariant &actionVariant, actions2) { bool found = false; foreach (const QVariant &replyActionVariant, replyActions2) { if (actionVariant.toMap().value("actionTypeId") == replyActionVariant.toMap().value("actionTypeId") && actionVariant.toMap().value("deviceId") == replyActionVariant.toMap().value("deviceId")) { found = true; // Check rule action params QVariantList actionParams = actionVariant.toMap().value("ruleActionParams").toList(); QVariantList replyActionParams = replyActionVariant.toMap().value("ruleActionParams").toList(); QVERIFY2(actionParams.count() == replyActionParams.count(), "Not the same list size of action params"); foreach (const QVariant &ruleParam, actionParams) { QVERIFY(replyActionParams.contains(ruleParam)); } } } QVERIFY2(found, "Action not found after loading from config."); } QVariantList replyExitActions2 = rule2.value("exitActions").toList(); QVERIFY2(replyExitActions2.count() == 1, "Rule 2 should have exactly 1 exitAction"); foreach (const QVariant &exitActionVariant, replyExitActions2) { bool found = false; foreach (const QVariant &replyActionVariant, replyExitActions2) { if (exitActionVariant.toMap().value("actionTypeId") == replyActionVariant.toMap().value("actionTypeId") && exitActionVariant.toMap().value("deviceId") == replyActionVariant.toMap().value("deviceId")) { found = true; // Check rule action params QVariantList actionParams = exitActionVariant.toMap().value("ruleActionParams").toList(); QVariantList replyActionParams = replyActionVariant.toMap().value("ruleActionParams").toList(); QVERIFY2(actionParams.count() == replyActionParams.count(), "Not the same list size of action params"); foreach (const QVariant &ruleParam, actionParams) { QVERIFY(replyActionParams.contains(ruleParam)); } } } QVERIFY2(found, "Exit Action not found after loading from config."); } // Rule 3 params.clear(); params.insert("ruleId", newRuleId3); response.clear(); response = injectAndWait("Rules.GetRuleDetails", params); QVariantMap rule3 = response.toMap().value("params").toMap().value("rule").toMap(); qDebug() << rule3; QVariantList eventDescriptors3 = rule3.value("eventDescriptors").toList(); QVERIFY2(eventDescriptors3.count() == 1, "There should be exactly 1 eventDescriptor"); QVariantMap eventDescriptor = eventDescriptors3.first().toMap(); QVERIFY2(eventDescriptor.value("eventTypeId").toString() == mockEvent2EventTypeId.toString(), "Loaded the wrong eventTypeId in rule 3"); QVERIFY2(eventDescriptor.value("deviceId").toString() == m_mockDeviceId.toString(), "Loaded the wrong deviceId from eventDescriptor in rule 3"); QVariantList replyExitActions3 = rule3.value("exitActions").toList(); QVERIFY2(replyExitActions3.isEmpty(), "Rule 3 should not have any exitAction"); QVariantList replyActions3 = rule3.value("actions").toList(); QVERIFY2(replyActions3.count() == 1, "Rule 3 should have exactly 1 action"); foreach (const QVariant &actionVariant, actions3) { bool found = false; foreach (const QVariant &replyActionVariant, replyActions3) { if (actionVariant.toMap().value("actionTypeId") == replyActionVariant.toMap().value("actionTypeId") && actionVariant.toMap().value("deviceId") == replyActionVariant.toMap().value("deviceId")) { found = true; // Check rule action params QVariantList actionParams = actionVariant.toMap().value("ruleActionParams").toList(); QVariantList replyActionParams = replyActionVariant.toMap().value("ruleActionParams").toList(); QVERIFY2(actionParams.count() == replyActionParams.count(), "Not the same list size of action params"); foreach (const QVariant &ruleParam, actionParams) { QVERIFY(replyActionParams.contains(ruleParam)); } } } QVERIFY2(found, "Action not found after loading from config."); } // Rule 4 params.clear(); params.insert("ruleId", newRuleId4); response.clear(); response = injectAndWait("Rules.GetRuleDetails", params); QVariantMap rule4 = response.toMap().value("params").toMap().value("rule").toMap(); qDebug() << rule4; QVariantList eventDescriptors4 = rule4.value("eventDescriptors").toList(); QVERIFY2(eventDescriptors4.count() == 1, "There should be exactly 1 eventDescriptor"); eventDescriptor = eventDescriptors4.first().toMap(); QVERIFY2(eventDescriptor.value("interface").toString() == "battery", "Loaded the wrong interface name in rule 4"); QVERIFY2(eventDescriptor.value("interfaceEvent").toString() == "batteryCritical", "Loaded the wrong interfaceEvent from eventDescriptor in rule 4"); QCOMPARE(eventDescriptor.value("paramDescriptors").toList().count(), 1); QVERIFY2(eventDescriptor.value("paramDescriptors").toList().first().toMap().value("paramName").toString() == "batteryCritical", "Loaded wrong ParamDescriptor in rule 4"); QVERIFY2(eventDescriptor.value("paramDescriptors").toList().first().toMap().value("value").toBool() == true, "Loaded wrong ParamDescriptor in rule 3"); QVariantList replyActions4 = rule4.value("actions").toList(); QVERIFY2(replyActions4.count() == 1, "Rule 4 should have exactly 1 action"); foreach (const QVariant &actionVariant, actionsInterfaces) { bool found = false; foreach (const QVariant &replyActionVariant, replyActions4) { if (actionVariant.toMap().value("interface") == replyActionVariant.toMap().value("interface") && actionVariant.toMap().value("interfaceAction") == replyActionVariant.toMap().value("interfaceAction")) { found = true; // Check rule action params QVariantList actionParams = actionVariant.toMap().value("ruleActionParams").toList(); QVariantList replyActionParams = replyActionVariant.toMap().value("ruleActionParams").toList(); QVERIFY2(actionParams.count() == replyActionParams.count(), "Not the same list size of action params"); foreach (const QVariant &ruleParam, actionParams) { QVERIFY(replyActionParams.contains(ruleParam)); } } } QVERIFY2(found, "Action not found after loading from config."); } QVariantList replyExitActions4 = rule4.value("exitActions").toList(); QVERIFY2(replyExitActions4.isEmpty(), "Rule 4 should not have any exitAction"); // Remove Rule1 params.clear(); params.insert("ruleId", newRuleId); response = injectAndWait("Rules.RemoveRule", params); verifyRuleError(response); // Remove Rule2 params2.clear(); params2.insert("ruleId", newRuleId2); response = injectAndWait("Rules.RemoveRule", params2); verifyRuleError(response); // Remove Rule2 params3.clear(); params3.insert("ruleId", newRuleId3); response = injectAndWait("Rules.RemoveRule", params3); verifyRuleError(response); restartServer(); response = injectAndWait("Rules.GetRules"); rules = response.toMap().value("params").toMap().value("rules").toList(); QVERIFY2(rules.count() == 0, "There should be no rules."); } void TestRules::evaluateEvent() { // Add a rule QVariantMap addRuleParams; addRuleParams.insert("name", "TestRule"); QVariantList events; QVariantMap event1; event1.insert("eventTypeId", mockEvent1EventTypeId); event1.insert("deviceId", m_mockDeviceId); events.append(event1); addRuleParams.insert("eventDescriptors", events); QVariantList actions; QVariantMap action; action.insert("actionTypeId", mockWithoutParamsActionTypeId); action.insert("deviceId", m_mockDeviceId); actions.append(action); addRuleParams.insert("actions", actions); QVariant response = injectAndWait("Rules.AddRule", addRuleParams); verifyRuleError(response); // Trigger an event QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); // trigger event in mock device QNetworkRequest request(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(m_mockDevice1Port).arg(mockEvent1EventTypeId.toString()))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleExecuted(mockWithoutParamsActionTypeId); } void TestRules::evaluateEventParams() { // Init bool state to true QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBoolStateTypeId.toString()).arg("true"))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); // Add a rule QVariantMap addRuleParams; addRuleParams.insert("name", "TestRule"); QVariantList params; QVariantMap boolParam; boolParam.insert("paramTypeId", mockBoolStateTypeId); boolParam.insert("operator", "ValueOperatorEquals"); boolParam.insert("value", true); params.append(boolParam); QVariantMap event1; event1.insert("eventTypeId", mockBoolStateTypeId); event1.insert("deviceId", m_mockDeviceId); event1.insert("paramDescriptors", params); QVariantList events; events.append(event1); addRuleParams.insert("eventDescriptors", events); QVariantList actions; QVariantMap action; action.insert("actionTypeId", mockWithoutParamsActionTypeId); action.insert("deviceId", m_mockDeviceId); actions.append(action); addRuleParams.insert("actions", actions); QVariant response = injectAndWait("Rules.AddRule", addRuleParams); verifyRuleError(response); // Trigger a non matching param spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBoolStateTypeId.toString()).arg("false"))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleNotExecuted(); // Trigger a matching param spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBoolStateTypeId.toString()).arg("true"))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleExecuted(mockWithoutParamsActionTypeId); // Reset back to false to not mess with other tests spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBoolStateTypeId.toString()).arg("false"))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); } void TestRules::testStateChange() { // Add a rule QVariantMap addRuleParams; QVariantMap stateEvaluator; QVariantMap stateDescriptor; stateDescriptor.insert("deviceId", m_mockDeviceId); stateDescriptor.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorGreaterOrEqual)); stateDescriptor.insert("stateTypeId", mockIntStateTypeId); stateDescriptor.insert("value", 42); stateEvaluator.insert("stateDescriptor", stateDescriptor); addRuleParams.insert("stateEvaluator", stateEvaluator); addRuleParams.insert("name", "TestRule"); QVariantList actions; QVariantMap action; action.insert("actionTypeId", mockWithoutParamsActionTypeId); action.insert("deviceId", m_mockDeviceId); actions.append(action); addRuleParams.insert("actions", actions); QVariant response = injectAndWait("Rules.AddRule", addRuleParams); verifyRuleError(response); // Change the state QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); // state state to 42 qDebug() << "setting mock int state to 42"; QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockIntStateTypeId.toString()).arg(42))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleExecuted(mockWithoutParamsActionTypeId); cleanupMockHistory(); // set state to 45 qDebug() << "setting mock int state to 45"; spy.clear(); request.setUrl(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockIntStateTypeId.toString()).arg(45))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleNotExecuted(); cleanupMockHistory(); // set state to 30 qDebug() << "setting mock int state to 30"; spy.clear(); request.setUrl(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockIntStateTypeId.toString()).arg(30))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleNotExecuted(); cleanupMockHistory(); // set state to 100 qDebug() << "setting mock int state to 100"; spy.clear(); request.setUrl(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockIntStateTypeId.toString()).arg(100))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); verifyRuleExecuted(mockWithoutParamsActionTypeId); reply->deleteLater(); } void TestRules::testStateEvaluator_data() { QTest::addColumn("deviceId"); QTest::addColumn("stateTypeId"); QTest::addColumn("value"); QTest::addColumn("operatorType"); QTest::addColumn("shouldMatch"); QTest::newRow("invalid stateId") << m_mockDeviceId << StateTypeId::createStateTypeId() << QVariant(10) << Types::ValueOperatorEquals << false; QTest::newRow("invalid deviceId") << DeviceId::createDeviceId() << mockIntStateTypeId << QVariant(10) << Types::ValueOperatorEquals << false; QTest::newRow("equals, not matching") << m_mockDeviceId << mockIntStateTypeId << QVariant(7777) << Types::ValueOperatorEquals << false; QTest::newRow("equals, matching") << m_mockDeviceId << mockIntStateTypeId << QVariant(10) << Types::ValueOperatorEquals << true; QTest::newRow("not equal, not matching") << m_mockDeviceId << mockIntStateTypeId << QVariant(10) << Types::ValueOperatorNotEquals << false; QTest::newRow("not equal, matching") << m_mockDeviceId << mockIntStateTypeId << QVariant(7777) << Types::ValueOperatorNotEquals << true; QTest::newRow("Greater, not matching") << m_mockDeviceId << mockIntStateTypeId << QVariant(7777) << Types::ValueOperatorGreater << false; QTest::newRow("Greater, matching") << m_mockDeviceId << mockIntStateTypeId << QVariant(2) << Types::ValueOperatorGreater << true; QTest::newRow("GreaterOrEqual, not matching") << m_mockDeviceId << mockIntStateTypeId << QVariant(7777) << Types::ValueOperatorGreaterOrEqual << false; QTest::newRow("GreaterOrEqual, matching (greater)") << m_mockDeviceId << mockIntStateTypeId << QVariant(2) << Types::ValueOperatorGreaterOrEqual << true; QTest::newRow("GreaterOrEqual, matching (equals)") << m_mockDeviceId << mockIntStateTypeId << QVariant(10) << Types::ValueOperatorGreaterOrEqual << true; QTest::newRow("Less, not matching") << m_mockDeviceId << mockIntStateTypeId << QVariant(2) << Types::ValueOperatorLess << false; QTest::newRow("Less, matching") << m_mockDeviceId << mockIntStateTypeId << QVariant(7777) << Types::ValueOperatorLess << true; QTest::newRow("LessOrEqual, not matching") << m_mockDeviceId << mockIntStateTypeId << QVariant(2) << Types::ValueOperatorLessOrEqual << false; QTest::newRow("LessOrEqual, matching (less)") << m_mockDeviceId << mockIntStateTypeId << QVariant(777) << Types::ValueOperatorLessOrEqual << true; QTest::newRow("LessOrEqual, matching (equals)") << m_mockDeviceId << mockIntStateTypeId << QVariant(10) << Types::ValueOperatorLessOrEqual << true; QTest::newRow("Less, not matching, double") << m_mockDeviceId << mockDoubleStateTypeId << QVariant(2.1) << Types::ValueOperatorLess << false; QTest::newRow("Less, not matching, double as string") << m_mockDeviceId << mockDoubleStateTypeId << QVariant("2.1") << Types::ValueOperatorLess << false; QTest::newRow("Less, matching, double") << m_mockDeviceId << mockDoubleStateTypeId << QVariant(4.2) << Types::ValueOperatorLess << true; QTest::newRow("Less, matching, double as string") << m_mockDeviceId << mockDoubleStateTypeId << QVariant("4.2") << Types::ValueOperatorLess << true; } void TestRules::testStateEvaluator() { QFETCH(DeviceId, deviceId); QFETCH(StateTypeId, stateTypeId); QFETCH(QVariant, value); QFETCH(Types::ValueOperator, operatorType); QFETCH(bool, shouldMatch); StateDescriptor descriptor(stateTypeId, deviceId, value, operatorType); StateEvaluator evaluator(descriptor); QVERIFY2(evaluator.evaluate() == shouldMatch, shouldMatch ? "State should match" : "State shouldn't match"); } void TestRules::testStateEvaluator2_data() { QTest::addColumn("intValue"); QTest::addColumn("intOperator"); QTest::addColumn("boolValue"); QTest::addColumn("boolOperator"); QTest::addColumn("stateOperator"); QTest::addColumn("shouldMatch"); QTest::newRow("Y: 10 && false") << 10 << Types::ValueOperatorEquals << false << Types::ValueOperatorEquals << Types::StateOperatorAnd << true; QTest::newRow("N: 10 && true") << 10 << Types::ValueOperatorEquals << true << Types::ValueOperatorEquals << Types::StateOperatorAnd << false; QTest::newRow("N: 11 && false") << 11 << Types::ValueOperatorEquals << false << Types::ValueOperatorEquals << Types::StateOperatorAnd << false; QTest::newRow("Y: 11 || false") << 11 << Types::ValueOperatorEquals << false << Types::ValueOperatorEquals << Types::StateOperatorOr << true; QTest::newRow("Y: 10 || false") << 10 << Types::ValueOperatorEquals << false << Types::ValueOperatorEquals << Types::StateOperatorOr << true; QTest::newRow("Y: 10 || true") << 10 << Types::ValueOperatorEquals << true << Types::ValueOperatorEquals << Types::StateOperatorOr << true; QTest::newRow("N: 11 || true") << 11 << Types::ValueOperatorEquals << true << Types::ValueOperatorEquals << Types::StateOperatorOr << false; } void TestRules::testStateEvaluator2() { QFETCH(int, intValue); QFETCH(Types::ValueOperator, intOperator); QFETCH(bool, boolValue); QFETCH(Types::ValueOperator, boolOperator); QFETCH(Types::StateOperator, stateOperator); QFETCH(bool, shouldMatch); StateDescriptor descriptor1(mockIntStateTypeId, m_mockDeviceId, intValue, intOperator); StateEvaluator evaluator1(descriptor1); StateDescriptor descriptor2(mockBoolStateTypeId, m_mockDeviceId, boolValue, boolOperator); StateEvaluator evaluator2(descriptor2); QList childEvaluators; childEvaluators.append(evaluator1); childEvaluators.append(evaluator2); StateEvaluator mainEvaluator(childEvaluators); mainEvaluator.setOperatorType(stateOperator); QVERIFY2(mainEvaluator.evaluate() == shouldMatch, shouldMatch ? "State should match" : "State shouldn't match"); } void TestRules::testStateEvaluator3_data() { testStateEvaluator2_data(); } void TestRules::testStateEvaluator3() { QFETCH(int, intValue); QFETCH(Types::ValueOperator, intOperator); QFETCH(bool, boolValue); QFETCH(Types::ValueOperator, boolOperator); QFETCH(Types::StateOperator, stateOperator); QFETCH(bool, shouldMatch); StateDescriptor descriptor1(mockIntStateTypeId, m_mockDeviceId, intValue, intOperator); StateEvaluator childEvaluator(descriptor1); QList childEvaluators; childEvaluators.append(childEvaluator); StateDescriptor descriptor2(mockBoolStateTypeId, m_mockDeviceId, boolValue, boolOperator); StateEvaluator mainEvaluator(descriptor2); mainEvaluator.setChildEvaluators(childEvaluators); mainEvaluator.setOperatorType(stateOperator); QVERIFY2(mainEvaluator.evaluate() == shouldMatch, shouldMatch ? "State should match" : "State shouldn't match"); } void TestRules::testChildEvaluator_data() { cleanup(); DeviceId testDeviceId = addDisplayPinDevice(); QVERIFY2(!testDeviceId.isNull(), "Could not add push button device for child evaluators"); enableNotifications(); // Create child evaluators // Action QVariantMap action; action.insert("actionTypeId", mockWithoutParamsActionTypeId); action.insert("deviceId", m_mockDeviceId); action.insert("ruleActionParams", QVariantList()); // Exit action (with params) QVariantMap exitAction; QVariantList actionParams; QVariantMap param1; param1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); param1.insert("value", 12); actionParams.append(param1); QVariantMap param2; param2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); param2.insert("value", true); actionParams.append(param2); exitAction.insert("actionTypeId", mockWithParamsActionTypeId); exitAction.insert("deviceId", m_mockDeviceId); exitAction.insert("ruleActionParams", actionParams); // Stateevaluators QVariantMap stateDescriptorPercentage; stateDescriptorPercentage.insert("deviceId", testDeviceId); stateDescriptorPercentage.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorGreaterOrEqual)); stateDescriptorPercentage.insert("stateTypeId", mockDisplayPinPercentageStateTypeId); stateDescriptorPercentage.insert("value", 50); QVariantMap stateDescriptorDouble; stateDescriptorDouble.insert("deviceId", testDeviceId); stateDescriptorDouble.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorEquals)); stateDescriptorDouble.insert("stateTypeId", mockDisplayPinDoubleActionDoubleParamTypeId); stateDescriptorDouble.insert("value", 20.5); QVariantMap stateDescriptorAllowedValues; stateDescriptorAllowedValues.insert("deviceId", testDeviceId); stateDescriptorAllowedValues.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorEquals)); stateDescriptorAllowedValues.insert("stateTypeId", mockDisplayPinAllowedValuesStateTypeId); stateDescriptorAllowedValues.insert("value", "String value 2"); QVariantMap stateDescriptorColor; stateDescriptorColor.insert("deviceId", testDeviceId); stateDescriptorColor.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorEquals)); stateDescriptorColor.insert("stateTypeId", mockDisplayPinColorStateTypeId); stateDescriptorColor.insert("value", "#00FF00"); QVariantMap firstStateEvaluator; firstStateEvaluator.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorOr)); firstStateEvaluator.insert("childEvaluators", QVariantList() << createStateEvaluatorFromSingleDescriptor(stateDescriptorPercentage) << createStateEvaluatorFromSingleDescriptor(stateDescriptorDouble)); QVariantMap secondStateEvaluator; secondStateEvaluator.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd)); secondStateEvaluator.insert("childEvaluators", QVariantList() << createStateEvaluatorFromSingleDescriptor(stateDescriptorAllowedValues) << createStateEvaluatorFromSingleDescriptor(stateDescriptorColor)); QVariantMap stateEvaluator; stateEvaluator.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd)); stateEvaluator.insert("childEvaluators", QVariantList() << firstStateEvaluator << secondStateEvaluator); // The rule QVariantMap ruleMap; ruleMap.insert("name", "Child evaluator rule"); ruleMap.insert("stateEvaluator", stateEvaluator); ruleMap.insert("actions", QVariantList() << action); ruleMap.insert("exitActions", QVariantList() << exitAction); printJson(ruleMap); // (percentage >= 50 || double == 20.5) && (color == #00FF00 && allowedValue == "String value 2") ? action : exit action QTest::addColumn("deviceId"); QTest::addColumn("ruleMap"); QTest::addColumn("percentageValue"); QTest::addColumn("doubleValue"); QTest::addColumn("allowedValue"); QTest::addColumn("colorValue"); QTest::addColumn("trigger"); QTest::addColumn("active"); QTest::newRow("Unchanged | 2 | 2.5 | String value 1 | #FF0000") << testDeviceId << ruleMap << 2 << 2.5 << "String value 1" << "#FF0000" << false << false; // QTest::newRow("Unchanged | 60 | 2.5 | String value 2 | #FF0000") << testDeviceId << ruleMap << 60 << 2.5 << "String value 2" << "#FF0000" << false << false; // QTest::newRow("Unchanged | 60 | 20.5 | String value 2 | #FF0000") << testDeviceId << ruleMap << 60 << 20.5 << "String value 2" << "#FF0000" << false << false; // QTest::newRow("Active | 60 | 20.5 | String value 2 | #00FF00") << testDeviceId << ruleMap << 60 << 20.5 << "String value 2" << "#00FF00" << true << true; // QTest::newRow("Active | 60 | 20.5 | String value 2 | #00FF00") << testDeviceId << ruleMap << 60 << 20.5 << "String value 2" << "#00FF00" << true << true; } void TestRules::testChildEvaluator() { QFETCH(DeviceId, deviceId); QFETCH(QVariantMap, ruleMap); QFETCH(int, percentageValue); QFETCH(double, doubleValue); QFETCH(QString, allowedValue); QFETCH(QString, colorValue); QFETCH(bool, trigger); QFETCH(bool, active); // Init the states setWritableStateValue(deviceId, StateTypeId(mockDisplayPinPercentageStateTypeId.toString()), QVariant(0)); setWritableStateValue(deviceId, StateTypeId(mockDisplayPinDoubleActionDoubleParamTypeId.toString()), QVariant(0)); setWritableStateValue(deviceId, StateTypeId(mockDisplayPinAllowedValuesStateTypeId.toString()), QVariant("String value 1")); setWritableStateValue(deviceId, StateTypeId(mockDisplayPinColorStateTypeId.toString()), QVariant("#000000")); qCDebug(dcTests()) << "Adding rule"; // Add rule QVariant response = injectAndWait("Rules.AddRule", ruleMap); verifyRuleError(response); RuleId ruleId = RuleId(response.toMap().value("params").toMap().value("ruleId").toString()); // Set the states qCDebug(dcTests()) << "Setting state 1"; setWritableStateValue(deviceId, StateTypeId(mockDisplayPinPercentageStateTypeId.toString()), QVariant::fromValue(percentageValue)); qCDebug(dcTests()) << "Setting state 2"; setWritableStateValue(deviceId, StateTypeId(mockDisplayPinDoubleActionDoubleParamTypeId.toString()), QVariant::fromValue(doubleValue)); qCDebug(dcTests()) << "Setting state 3"; setWritableStateValue(deviceId, StateTypeId(mockDisplayPinAllowedValuesStateTypeId.toString()), QVariant::fromValue(allowedValue)); qCDebug(dcTests()) << "Setting state 4"; setWritableStateValue(deviceId, StateTypeId(mockDisplayPinColorStateTypeId.toString()), QVariant::fromValue(colorValue)); // Verfiy if the rule executed successfully // Actions if (trigger && active) { qCDebug(dcTests()) << "Checking if actions were executed"; verifyRuleExecuted(mockWithoutParamsActionTypeId); cleanupMockHistory(); } // Exit actions if (trigger && !active) { qCDebug(dcTests()) << "Checking if exit actions were executed"; verifyRuleExecuted(mockWithParamsActionTypeId); cleanupMockHistory(); } // Nothing triggert if (!trigger) { qCDebug(dcTests()) << "Making sure nothing triggered"; verifyRuleNotExecuted(); } // REMOVE rule qCDebug(dcTests()) << "Removing rule"; QVariantMap removeParams; removeParams.insert("ruleId", ruleId); response = injectAndWait("Rules.RemoveRule", removeParams); verifyRuleError(response); } void TestRules::enableDisableRule() { // Add a rule QVariantMap addRuleParams; QVariantList events; QVariantMap event1; event1.insert("eventTypeId", mockEvent1EventTypeId); event1.insert("deviceId", m_mockDeviceId); events.append(event1); addRuleParams.insert("eventDescriptors", events); addRuleParams.insert("name", "TestRule"); QVariantList actions; QVariantMap action; action.insert("actionTypeId", mockWithoutParamsActionTypeId); action.insert("deviceId", m_mockDeviceId); actions.append(action); addRuleParams.insert("actions", actions); QVariant response = injectAndWait("Rules.AddRule", addRuleParams); verifyRuleError(response); RuleId id = RuleId(response.toMap().value("params").toMap().value("ruleId").toString()); // Trigger an event QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); // trigger event in mock device QNetworkRequest request(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(m_mockDevice1Port).arg(mockEvent1EventTypeId.toString()))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleExecuted(mockWithoutParamsActionTypeId); cleanupMockHistory(); // Now DISABLE the rule invalid ruleId QVariantMap disableParams; disableParams.insert("ruleId", QUuid::createUuid().toString()); response = injectAndWait("Rules.DisableRule", disableParams); verifyRuleError(response, RuleEngine::RuleErrorRuleNotFound); // Now DISABLE the rule disableParams.clear(); disableParams.insert("ruleId", id.toString()); response = injectAndWait("Rules.DisableRule", disableParams); verifyRuleError(response); // trigger event in mock device spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(m_mockDevice1Port).arg(mockEvent1EventTypeId.toString()))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleNotExecuted(); cleanupMockHistory(); // Now ENABLE the rule again invald ruleId QVariantMap enableParams; enableParams.insert("ruleId", QUuid::createUuid().toString()); response = injectAndWait("Rules.EnableRule", enableParams); verifyRuleError(response, RuleEngine::RuleErrorRuleNotFound); // Now ENABLE the rule again enableParams.clear(); enableParams.insert("ruleId", id.toString()); response = injectAndWait("Rules.EnableRule", enableParams); verifyRuleError(response); // trigger event in mock device spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(m_mockDevice1Port).arg(mockEvent1EventTypeId.toString()))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleExecuted(mockWithoutParamsActionTypeId); } void TestRules::testEventBasedAction() { // Add a rule QVariantMap addRuleParams; QVariantMap eventDescriptor; eventDescriptor.insert("eventTypeId", mockIntStateTypeId); eventDescriptor.insert("deviceId", m_mockDeviceId); addRuleParams.insert("eventDescriptors", QVariantList() << eventDescriptor); addRuleParams.insert("name", "TestRule"); addRuleParams.insert("enabled", true); QVariantList actions; QVariantMap action; QVariantList ruleActionParams; QVariantMap param1; param1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); param1.insert("eventTypeId", mockIntStateTypeId); param1.insert("eventParamTypeId", mockIntStateTypeId); QVariantMap param2; param2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); param2.insert("value", true); ruleActionParams.append(param1); ruleActionParams.append(param2); action.insert("actionTypeId", mockWithParamsActionTypeId); action.insert("deviceId", m_mockDeviceId); action.insert("ruleActionParams", ruleActionParams); actions.append(action); addRuleParams.insert("actions", actions); qDebug() << addRuleParams; QVariant response = injectAndWait("Rules.AddRule", addRuleParams); verifyRuleError(response); // Change the state QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); // state state to 42 qDebug() << "setting mock int state to 42"; QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockIntStateTypeId.toString()).arg(42))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleExecuted(mockWithParamsActionTypeId); // TODO: check if this action was really executed with the int state value 42 } void TestRules::testEventBasedRuleWithExitAction() { QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); // Init bool state to true spy.clear(); QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBoolStateTypeId.toString()).arg(true))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); // Add a rule QVariantMap addRuleParams; QVariantMap eventDescriptor; eventDescriptor.insert("eventTypeId", mockEvent1EventTypeId); eventDescriptor.insert("deviceId", m_mockDeviceId); addRuleParams.insert("eventDescriptors", QVariantList() << eventDescriptor); addRuleParams.insert("name", "TestRule"); addRuleParams.insert("enabled", true); QVariantMap stateEvaluator; QVariantMap stateDescriptor; stateDescriptor.insert("deviceId", m_mockDeviceId); stateDescriptor.insert("stateTypeId", mockBoolStateTypeId); stateDescriptor.insert("operator", "ValueOperatorEquals"); stateDescriptor.insert("value", true); stateEvaluator.insert("stateDescriptor", stateDescriptor); stateEvaluator.insert("operator", "StateOperatorAnd"); addRuleParams.insert("stateEvaluator", stateEvaluator); QVariantList actions; QVariantMap action; QVariantList ruleActionParams; QVariantMap param1; param1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); param1.insert("value", true); QVariantMap param2; param2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); param2.insert("value", true); ruleActionParams.append(param1); ruleActionParams.append(param2); action.insert("actionTypeId", mockWithoutParamsActionTypeId); action.insert("deviceId", m_mockDeviceId); actions.append(action); addRuleParams.insert("actions", actions); actions.clear(); action.insert("actionTypeId", mockWithParamsActionTypeId); action.insert("ruleActionParams", ruleActionParams); actions.append(action); addRuleParams.insert("exitActions", actions); qDebug() << addRuleParams; QVariant response = injectAndWait("Rules.AddRule", addRuleParams); verifyRuleError(response); // trigger event spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(m_mockDevice1Port).arg(mockEvent1EventTypeId.toString()))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); // Verify the actions got executed verifyRuleExecuted(mockWithoutParamsActionTypeId); // set bool state to false spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBoolStateTypeId.toString()).arg(false))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); // trigger event spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(m_mockDevice1Port).arg(mockEvent1EventTypeId.toString()))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); // Verify the exit actions got executed verifyRuleExecuted(mockWithoutParamsActionTypeId); } void TestRules::testStateBasedAction() { QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); // Init bool state to true spy.clear(); QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBoolStateTypeId.toString()).arg(true))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); // Init int state to 11 spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockIntStateTypeId.toString()).arg(11))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); // Add a rule QVariantMap addRuleParams; QVariantMap eventDescriptor; eventDescriptor.insert("eventTypeId", mockEvent1EventTypeId); eventDescriptor.insert("deviceId", m_mockDeviceId); addRuleParams.insert("eventDescriptors", QVariantList() << eventDescriptor); addRuleParams.insert("name", "TestRule"); addRuleParams.insert("enabled", true); QVariantList actions; QVariantMap action; QVariantList ruleActionParams; QVariantMap param1; param1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); param1.insert("stateDeviceId", m_mockDeviceId); param1.insert("stateTypeId", mockIntStateTypeId); QVariantMap param2; param2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); param2.insert("stateDeviceId", m_mockDeviceId); param2.insert("stateTypeId", mockBoolStateTypeId); ruleActionParams.append(param1); ruleActionParams.append(param2); actions.clear(); action.insert("deviceId", m_mockDeviceId); action.insert("actionTypeId", mockWithParamsActionTypeId); action.insert("ruleActionParams", ruleActionParams); actions.append(action); addRuleParams.insert("actions", actions); qCDebug(dcTests) << "Adding rule"; QVariant response = injectAndWait("Rules.AddRule", addRuleParams); verifyRuleError(response); // trigger event spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(m_mockDevice1Port).arg(mockEvent1EventTypeId.toString()))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); LogFilter filter; filter.addDeviceId(m_mockDeviceId); filter.addTypeId(mockWithParamsActionTypeId); QList entries = NymeaCore::instance()->logEngine()->logEntries(filter); qCDebug(dcTests()) << "Log entries:" << entries; // set bool state to false spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBoolStateTypeId.toString()).arg(false))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); // trigger event spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(m_mockDevice1Port).arg(mockEvent1EventTypeId.toString()))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); entries = NymeaCore::instance()->logEngine()->logEntries(filter); qCDebug(dcTests()) << "Log entries:" << entries; } void TestRules::removePolicyUpdate() { // ADD parent device QVariantMap params; params.insert("deviceClassId", mockParentDeviceClassId); params.insert("name", "Parent device"); QSignalSpy addedSpy(NymeaCore::instance()->deviceManager(), &DeviceManager::deviceAdded); QVariant response = injectAndWait("Devices.AddConfiguredDevice", params); verifyDeviceError(response); DeviceId parentDeviceId = DeviceId(response.toMap().value("params").toMap().value("deviceId").toString()); QVERIFY(!parentDeviceId.isNull()); addedSpy.wait(); // find child device response = injectAndWait("Devices.GetConfiguredDevices"); QVariantList devices = response.toMap().value("params").toMap().value("devices").toList(); DeviceId childDeviceId; foreach (const QVariant deviceVariant, devices) { QVariantMap deviceMap = deviceVariant.toMap(); if (deviceMap.value("deviceClassId").toString() == mockChildDeviceClassId.toString()) { if (deviceMap.value("parentId") == parentDeviceId.toString()) { //qDebug() << QJsonDocument::fromVariant(deviceVariant).toJson(); childDeviceId = DeviceId(deviceMap.value("id").toString()); } } } QVERIFY2(!childDeviceId.isNull(), "Could not find child device"); // Add rule with child device QVariantList eventDescriptors; eventDescriptors.append(createEventDescriptor(childDeviceId, mockChildBoolValueEventTypeId)); eventDescriptors.append(createEventDescriptor(parentDeviceId, mockParentBoolValueEventTypeId)); eventDescriptors.append(createEventDescriptor(m_mockDeviceId, mockEvent1EventTypeId)); params.clear(); response.clear(); params.insert("name", "RemovePolicy"); params.insert("eventDescriptors", eventDescriptors); params.insert("actions", QVariantList() << createActionWithParams(m_mockDeviceId)); response = injectAndWait("Rules.AddRule", params); verifyRuleError(response); RuleId ruleId = RuleId(response.toMap().value("params").toMap().value("ruleId").toString()); QVERIFY2(!ruleId.isNull(), "Could not get ruleId"); // Try to remove child device params.clear(); response.clear(); params.insert("deviceId", childDeviceId); response = injectAndWait("Devices.RemoveConfiguredDevice", params); verifyDeviceError(response, Device::DeviceErrorDeviceIsChild); // Try to remove child device params.clear(); response.clear(); params.insert("deviceId", parentDeviceId); response = injectAndWait("Devices.RemoveConfiguredDevice", params); verifyDeviceError(response, Device::DeviceErrorDeviceInRule); // Remove policy params.clear(); response.clear(); params.insert("deviceId", parentDeviceId); params.insert("removePolicy", "RemovePolicyUpdate"); response = injectAndWait("Devices.RemoveConfiguredDevice", params); verifyDeviceError(response); // get updated rule params.clear(); params.insert("ruleId", ruleId); response = injectAndWait("Rules.GetRuleDetails", params); verifyRuleError(response); QVariantMap rule = response.toMap().value("params").toMap().value("rule").toMap(); qDebug() << "Updated rule:" << QJsonDocument::fromVariant(rule).toJson(); QVERIFY(rule.value("eventDescriptors").toList().count() == 1); // REMOVE rule QVariantMap removeParams; removeParams.insert("ruleId", ruleId); response = injectAndWait("Rules.RemoveRule", removeParams); verifyRuleError(response); } void TestRules::removePolicyCascade() { // ADD parent device QVariantMap params; params.insert("deviceClassId", mockParentDeviceClassId); params.insert("name", "Parent device"); QSignalSpy addedSpy(NymeaCore::instance()->deviceManager(), &DeviceManager::deviceAdded); QVariant response = injectAndWait("Devices.AddConfiguredDevice", params); verifyDeviceError(response); DeviceId parentDeviceId = DeviceId(response.toMap().value("params").toMap().value("deviceId").toString()); QVERIFY(!parentDeviceId.isNull()); addedSpy.wait(); // find child device response = injectAndWait("Devices.GetConfiguredDevices"); QVariantList devices = response.toMap().value("params").toMap().value("devices").toList(); DeviceId childDeviceId; foreach (const QVariant deviceVariant, devices) { QVariantMap deviceMap = deviceVariant.toMap(); if (deviceMap.value("deviceClassId").toString() == mockChildDeviceClassId.toString()) { if (deviceMap.value("parentId") == parentDeviceId.toString()) { //qDebug() << QJsonDocument::fromVariant(deviceVariant).toJson(); childDeviceId = DeviceId(deviceMap.value("id").toString()); } } } QVERIFY2(!childDeviceId.isNull(), "Could not find child device"); // Add rule with child device QVariantList eventDescriptors; eventDescriptors.append(createEventDescriptor(childDeviceId, mockChildBoolValueEventTypeId)); eventDescriptors.append(createEventDescriptor(parentDeviceId, mockParentBoolValueEventTypeId)); eventDescriptors.append(createEventDescriptor(m_mockDeviceId, mockEvent1EventTypeId)); params.clear(); response.clear(); params.insert("name", "RemovePolicy"); params.insert("eventDescriptors", eventDescriptors); params.insert("actions", QVariantList() << createActionWithParams(m_mockDeviceId)); response = injectAndWait("Rules.AddRule", params); verifyRuleError(response); RuleId ruleId = RuleId(response.toMap().value("params").toMap().value("ruleId").toString()); QVERIFY2(!ruleId.isNull(), "Could not get ruleId"); // Try to remove child device params.clear(); response.clear(); params.insert("deviceId", childDeviceId); response = injectAndWait("Devices.RemoveConfiguredDevice", params); verifyDeviceError(response, Device::DeviceErrorDeviceIsChild); // Try to remove child device params.clear(); response.clear(); params.insert("deviceId", parentDeviceId); response = injectAndWait("Devices.RemoveConfiguredDevice", params); verifyDeviceError(response, Device::DeviceErrorDeviceInRule); // Remove policy params.clear(); response.clear(); params.insert("deviceId", parentDeviceId); params.insert("removePolicy", "RemovePolicyCascade"); response = injectAndWait("Devices.RemoveConfiguredDevice", params); verifyDeviceError(response); // get updated rule params.clear(); params.insert("ruleId", ruleId); response = injectAndWait("Rules.GetRuleDetails", params); verifyRuleError(response, RuleEngine::RuleErrorRuleNotFound); } void TestRules::removePolicyUpdateRendersUselessRule() { // ADD parent device QVariantMap params; params.insert("deviceClassId", mockParentDeviceClassId); params.insert("name", "Parent device"); QSignalSpy addedSpy(NymeaCore::instance()->deviceManager(), &DeviceManager::deviceAdded); QVariant response = injectAndWait("Devices.AddConfiguredDevice", params); verifyDeviceError(response); DeviceId parentDeviceId = DeviceId(response.toMap().value("params").toMap().value("deviceId").toString()); QVERIFY(!parentDeviceId.isNull()); addedSpy.wait(); // find child device qCDebug(dcTests()) << "Gettin devices"; response = injectAndWait("Devices.GetConfiguredDevices"); QVariantList devices = response.toMap().value("params").toMap().value("devices").toList(); DeviceId childDeviceId; foreach (const QVariant deviceVariant, devices) { QVariantMap deviceMap = deviceVariant.toMap(); if (deviceMap.value("deviceClassId").toString() == mockChildDeviceClassId.toString()) { if (deviceMap.value("parentId") == parentDeviceId.toString()) { //qDebug() << QJsonDocument::fromVariant(deviceVariant).toJson(); childDeviceId = DeviceId(deviceMap.value("id").toString()); } } } QVERIFY2(!childDeviceId.isNull(), "Could not find child device"); // Add rule with child device QVariantList eventDescriptors; eventDescriptors.append(createEventDescriptor(childDeviceId, mockChildBoolValueEventTypeId)); eventDescriptors.append(createEventDescriptor(parentDeviceId, mockParentBoolValueEventTypeId)); eventDescriptors.append(createEventDescriptor(m_mockDeviceId, mockEvent1EventTypeId)); params.clear(); response.clear(); params.insert("name", "RemovePolicy"); params.insert("eventDescriptors", eventDescriptors); QVariantMap action; action.insert("deviceId", childDeviceId); action.insert("actionTypeId", mockChildBoolValueActionTypeId); QVariantMap ruleActionParam; ruleActionParam.insert("paramTypeId", mockChildBoolValueActionBoolValueParamTypeId); ruleActionParam.insert("value", true); action.insert("ruleActionParams", QVariantList() << ruleActionParam); params.insert("actions", QVariantList() << action); qCDebug(dcTests()) << "Adding Rule"; response = injectAndWait("Rules.AddRule", params); verifyRuleError(response); RuleId ruleId = RuleId(response.toMap().value("params").toMap().value("ruleId").toString()); QVERIFY2(!ruleId.isNull(), "Could not get ruleId"); // Try to remove child device qCDebug(dcTests()) << "Removing device (expecing failure - device is child)"; params.clear(); response.clear(); params.insert("deviceId", childDeviceId); response = injectAndWait("Devices.RemoveConfiguredDevice", params); verifyDeviceError(response, Device::DeviceErrorDeviceIsChild); // Try to remove child device qCDebug(dcTests()) << "Removing device (expeciting failure - device in use)"; params.clear(); response.clear(); params.insert("deviceId", parentDeviceId); response = injectAndWait("Devices.RemoveConfiguredDevice", params); verifyDeviceError(response, Device::DeviceErrorDeviceInRule); // Remove policy qCDebug(dcTests()) << "Removing device with update policy"; params.clear(); response.clear(); params.insert("deviceId", parentDeviceId); params.insert("removePolicy", "RemovePolicyUpdate"); response = injectAndWait("Devices.RemoveConfiguredDevice", params); verifyDeviceError(response); // get updated rule. It should've been deleted given it ended up with no actions qCDebug(dcTests()) << "Getting details"; params.clear(); params.insert("ruleId", ruleId); response = injectAndWait("Rules.GetRuleDetails", params); verifyRuleError(response, RuleEngine::RuleErrorRuleNotFound); } void TestRules::testRuleActionParams_data() { QVariantMap action; QVariantList ruleActionParams; QVariantMap param1; param1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); param1.insert("value", 4); QVariantMap param2; param2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); param2.insert("value", true); ruleActionParams.append(param1); ruleActionParams.append(param2); action.insert("actionTypeId", mockWithParamsActionTypeId); action.insert("deviceId", m_mockDeviceId); action.insert("ruleActionParams", ruleActionParams); QVariantMap invalidAction1; invalidAction1.insert("actionTypeId", mockWithParamsActionTypeId); invalidAction1.insert("deviceId", m_mockDeviceId); invalidAction1.insert("ruleActionParams", QVariantList() << param2); QVariantMap invalidAction2; invalidAction2.insert("actionTypeId", mockWithParamsActionTypeId); invalidAction2.insert("deviceId", m_mockDeviceId); invalidAction2.insert("ruleActionParams", QVariantList() << param1); QTest::addColumn("action"); QTest::addColumn("exitAction"); QTest::addColumn("error"); QTest::newRow("valid action params") << action << QVariantMap() << RuleEngine::RuleErrorNoError; QTest::newRow("valid action and exit action params") << action << action << RuleEngine::RuleErrorNoError; QTest::newRow("invalid action params1") << invalidAction1 << QVariantMap() << RuleEngine::RuleErrorMissingParameter; QTest::newRow("invalid action params2") << invalidAction2 << QVariantMap() << RuleEngine::RuleErrorMissingParameter; QTest::newRow("valid action and invalid exit action params1") << action << invalidAction1 << RuleEngine::RuleErrorMissingParameter; QTest::newRow("valid action and invalid exit action params2") << action << invalidAction2 << RuleEngine::RuleErrorMissingParameter; } void TestRules::testRuleActionParams() { QFETCH(QVariantMap, action); QFETCH(QVariantMap, exitAction); QFETCH(RuleEngine::RuleError, error); // Add a rule QVariantMap addRuleParams; addRuleParams.insert("name", "TestRule"); addRuleParams.insert("enabled", true); if (!action.isEmpty()) addRuleParams.insert("actions", QVariantList() << action); if (!exitAction.isEmpty()) addRuleParams.insert("exitActions", QVariantList() << exitAction); QVariant response = injectAndWait("Rules.AddRule", addRuleParams); verifyRuleError(response, error); } void TestRules::testRuleActionPAramsFromEventParameter_data() { QTest::addColumn("event"); QTest::addColumn("action"); QTest::addColumn("error"); QVariantMap intEvent; intEvent.insert("eventTypeId", mockIntStateTypeId); intEvent.insert("deviceId", m_mockDeviceId); QVariantMap intAction; intAction.insert("actionTypeId", mockWithParamsActionTypeId); intAction.insert("deviceId", m_mockDeviceId); QVariantList ruleActionParams; QVariantMap intParam; intParam.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); intParam.insert("eventTypeId", mockIntStateTypeId); intParam.insert("eventParamTypeId", mockIntStateTypeId); ruleActionParams.append(intParam); QVariantMap boolParam; boolParam.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); boolParam.insert("value", true); ruleActionParams.append(boolParam); intAction.insert("ruleActionParams", ruleActionParams); QVariantMap boolAction; boolAction.insert("actionTypeId", mockWithParamsActionTypeId); boolAction.insert("deviceId", m_mockDeviceId); ruleActionParams.clear(); intParam.clear(); intParam.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId); intParam.insert("value", 5); ruleActionParams.append(intParam); boolParam.clear(); boolParam.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId); boolParam.insert("eventTypeId", mockIntStateTypeId); boolParam.insert("eventParamTypeId", mockIntStateTypeId); ruleActionParams.append(boolParam); boolAction.insert("ruleActionParams", ruleActionParams); QTest::newRow("int -> int") << intEvent << intAction << RuleEngine::RuleErrorNoError; QTest::newRow("int -> bool") << intEvent << boolAction << RuleEngine::RuleErrorNoError; } void TestRules::testRuleActionPAramsFromEventParameter() { QFETCH(QVariantMap, event); QFETCH(QVariantMap, action); QFETCH(RuleEngine::RuleError, error); QVariantMap addRuleParams; addRuleParams.insert("name", "TestRule"); addRuleParams.insert("enabled", true); addRuleParams.insert("eventDescriptors", QVariantList() << event); addRuleParams.insert("actions", QVariantList() << action); QVariant response = injectAndWait("Rules.AddRule", addRuleParams); verifyRuleError(response, error); } void TestRules::testInitStatesActive() { // Create a rule to toggle the power state on event 1 QVariantMap params; params.insert("name", "testrule"); QVariantMap eventDescriptor; eventDescriptor.insert("deviceId", m_mockDeviceId); eventDescriptor.insert("eventTypeId", mockEvent1EventTypeId); QVariantList eventDescriptors; eventDescriptors.append(eventDescriptor); params.insert("eventDescriptors", eventDescriptors); QVariantMap stateEvaluator; QVariantMap stateDescriptor; stateDescriptor.insert("stateTypeId", mockPowerStateTypeId); stateDescriptor.insert("operator", "ValueOperatorEquals"); stateDescriptor.insert("value", false); stateDescriptor.insert("deviceId", m_mockDeviceId); stateEvaluator.insert("stateDescriptor", stateDescriptor); params.insert("stateEvaluator", stateEvaluator); QVariantList actions; QVariantMap action; action.insert("actionTypeId", mockPowerActionTypeId); action.insert("deviceId", m_mockDeviceId); QVariantList actionParams; QVariantMap actionParam; actionParam.insert("paramTypeId", mockPowerActionPowerParamTypeId); actionParam.insert("value", true); actionParams.append(actionParam); action.insert("ruleActionParams", actionParams); actions.append(action); params.insert("actions", actions); QVariantList exitActions; QVariantMap exitAction; exitAction.insert("actionTypeId", mockPowerActionTypeId); exitAction.insert("deviceId", m_mockDeviceId); QVariantList exitActionParams; QVariantMap exitActionParam; exitActionParam.insert("paramTypeId", mockPowerActionPowerParamTypeId); exitActionParam.insert("value", false); exitActionParams.append(exitActionParam); exitAction.insert("ruleActionParams", exitActionParams); exitActions.append(exitAction); params.insert("exitActions", exitActions); QVariant response = injectAndWait("Rules.AddRule", params); RuleId ruleId = RuleId::fromUuid(response.toMap().value("params").toMap().value("ruleId").toUuid()); QVERIFY2(!ruleId.isNull(), "Error adding rule"); // Get the current state value, make sure it's false params.clear(); params.insert("deviceId", m_mockDeviceId); params.insert("stateTypeId", mockPowerStateTypeId); response = injectAndWait("Devices.GetStateValue", params); QVERIFY2(response.toMap().value("params").toMap().value("value").toBool() == false, "State initially true while it should be false"); // Trigger the event generateEvent(mockEvent1EventTypeId); // Make sure the value changed after the event has triggered response = injectAndWait("Devices.GetStateValue", params); QVERIFY2(response.toMap().value("params").toMap().value("value").toBool() == true, "State is false while it should have changed to true"); // Trigger the event again... generateEvent(mockEvent1EventTypeId); // ... and make sure the value changed back to false response = injectAndWait("Devices.GetStateValue", params); QVERIFY2(response.toMap().value("params").toMap().value("value").toBool() == false, "State is true while it should have changed to false"); restartServer(); // Make sure the value changed is still false response = injectAndWait("Devices.GetStateValue", params); QVERIFY2(response.toMap().value("params").toMap().value("value").toBool() == false, "State is true while it should have changed to false"); // Trigger the event generateEvent(mockEvent1EventTypeId); // Make sure the value changed after the event has triggered response = injectAndWait("Devices.GetStateValue", params); QVERIFY2(response.toMap().value("params").toMap().value("value").toBool() == true, "State is false while it should have changed to true"); } void TestRules::testInterfaceBasedEventRule() { QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); // state battery critical state to false initially QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBatteryCriticalStateTypeId.toString()).arg(false))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); QVariantMap powerAction; powerAction.insert("interface", "light"); powerAction.insert("interfaceAction", "power"); QVariantMap powerActionParam; powerActionParam.insert("paramName", "power"); powerActionParam.insert("value", true); powerAction.insert("ruleActionParams", QVariantList() << powerActionParam); QVariantMap lowBatteryEvent; lowBatteryEvent.insert("interface", "battery"); lowBatteryEvent.insert("interfaceEvent", "batteryCritical"); QVariantMap eventParams; eventParams.insert("paramName", "batteryCritical"); eventParams.insert("value", true); eventParams.insert("operator", "ValueOperatorEquals"); lowBatteryEvent.insert("paramDescriptors", QVariantList() << eventParams); QVariantMap addRuleParams; addRuleParams.insert("name", "TestInterfaceBasedRule"); addRuleParams.insert("enabled", true); addRuleParams.insert("actions", QVariantList() << powerAction); addRuleParams.insert("eventDescriptors", QVariantList() << lowBatteryEvent); qDebug(dcTests) << "Inserting rule"; QVariant response = injectAndWait("Rules.AddRule", addRuleParams); QCOMPARE(response.toMap().value("status").toString(), QString("success")); QCOMPARE(response.toMap().value("params").toMap().value("ruleError").toString(), QString("RuleErrorNoError")); QVariantMap getRuleParams; getRuleParams.insert("ruleId", response.toMap().value("params").toMap().value("ruleId")); qDebug(dcTests) << "Getting rule details"; response = injectAndWait("Rules.GetRuleDetails", getRuleParams); QCOMPARE(response.toMap().value("params").toMap().value("ruleError").toString(), QString("RuleErrorNoError")); QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("eventDescriptors").toList().first().toMap().value("interface").toString(), QString("battery")); QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("eventDescriptors").toList().first().toMap().value("interfaceEvent").toString(), QString("batteryCritical")); QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("interface").toString(), QString("light")); QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("interfaceAction").toString(), QString("power")); QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("ruleActionParams").toList().first().toMap().value("paramName").toString(), QString("power")); QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("ruleActionParams").toList().first().toMap().value("value").toString(), QString("true")); qDebug(dcTests) << "Clearing action history"; // Change the state to true, action should trigger spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/clearactionhistory").arg(m_mockDevice1Port))); reply = nam.get(request); qDebug(dcTests) << "Changing battery state -> true"; spy.wait(); spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBatteryCriticalStateTypeId.toString()).arg(true))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleExecuted(mockPowerActionTypeId); qDebug(dcTests) << "Clearing action history"; // Change the state to false, action should not trigger spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/clearactionhistory").arg(m_mockDevice1Port))); reply = nam.get(request); qDebug(dcTests) << "Changing battery state -> false"; spy.wait(); spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBatteryCriticalStateTypeId.toString()).arg(false))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleNotExecuted(); } void TestRules::testInterfaceBasedStateRule() { QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); // state battery critical state to false initially QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBatteryCriticalStateTypeId.toString()).arg(false))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); QVariantMap powerAction; powerAction.insert("interface", "light"); powerAction.insert("interfaceAction", "power"); QVariantMap powerActionParam; powerActionParam.insert("paramName", "power"); powerActionParam.insert("value", true); powerAction.insert("ruleActionParams", QVariantList() << powerActionParam); QVariantMap lowBatteryState; lowBatteryState.insert("interface", "battery"); lowBatteryState.insert("interfaceState", "batteryCritical"); QVariantMap stateDescriptor; stateDescriptor.insert("interface", "battery"); stateDescriptor.insert("interfaceState", "batteryCritical"); stateDescriptor.insert("value", true); stateDescriptor.insert("operator", "ValueOperatorEquals"); QVariantMap stateEvaluator; stateEvaluator.insert("stateDescriptor", stateDescriptor); QVariantMap addRuleParams; addRuleParams.insert("name", "TestInterfaceBasedStateRule"); addRuleParams.insert("enabled", true); addRuleParams.insert("stateEvaluator", stateEvaluator); addRuleParams.insert("actions", QVariantList() << powerAction); QVariant response = injectAndWait("Rules.AddRule", addRuleParams); QCOMPARE(response.toMap().value("status").toString(), QString("success")); QCOMPARE(response.toMap().value("params").toMap().value("ruleError").toString(), QString("RuleErrorNoError")); QVariantMap getRuleParams; getRuleParams.insert("ruleId", response.toMap().value("params").toMap().value("ruleId")); response = injectAndWait("Rules.GetRuleDetails", getRuleParams); QCOMPARE(response.toMap().value("params").toMap().value("ruleError").toString(), QString("RuleErrorNoError")); QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("stateEvaluator").toMap().value("stateDescriptor").toMap().value("interface").toString(), QString("battery")); QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("stateEvaluator").toMap().value("stateDescriptor").toMap().value("interfaceState").toString(), QString("batteryCritical")); QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("interface").toString(), QString("light")); QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("interfaceAction").toString(), QString("power")); QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("ruleActionParams").toList().first().toMap().value("paramName").toString(), QString("power")); QCOMPARE(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("ruleActionParams").toList().first().toMap().value("value").toString(), QString("true")); // Change the state spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBatteryCriticalStateTypeId.toString()).arg(true))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleExecuted(mockPowerActionTypeId); } void TestRules::testLoopingRules() { QVariantMap powerOnActionParam; powerOnActionParam.insert("paramTypeId", mockPowerStateTypeId); powerOnActionParam.insert("value", true); QVariantMap powerOffActionParam; powerOffActionParam.insert("paramTypeId", mockPowerStateTypeId); powerOffActionParam.insert("value", false); QVariantMap powerOnEventParam = powerOnActionParam; powerOnEventParam.insert("operator", "ValueOperatorEquals"); QVariantMap powerOffEventParam = powerOffActionParam; powerOffEventParam.insert("operator", "ValueOperatorEquals"); QVariantMap onEvent; onEvent.insert("eventTypeId", mockPowerStateTypeId); onEvent.insert("deviceId", m_mockDeviceId); onEvent.insert("paramDescriptors", QVariantList() << powerOnEventParam); QVariantMap offEvent; offEvent.insert("eventTypeId", mockPowerStateTypeId); offEvent.insert("deviceId", m_mockDeviceId); offEvent.insert("paramDescriptors", QVariantList() << powerOffEventParam); QVariantMap onAction; onAction.insert("actionTypeId", mockPowerStateTypeId); onAction.insert("deviceId", m_mockDeviceId); onAction.insert("ruleActionParams", QVariantList() << powerOnActionParam); QVariantMap offAction; offAction.insert("actionTypeId", mockPowerStateTypeId); offAction.insert("deviceId", m_mockDeviceId); offAction.insert("ruleActionParams", QVariantList() << powerOffActionParam); // Add rule 1 QVariantMap addRuleParams; addRuleParams.insert("name", "Rule off -> on"); addRuleParams.insert("eventDescriptors", QVariantList() << offEvent); addRuleParams.insert("actions", QVariantList() << onAction); QVariant response = injectAndWait("Rules.AddRule", addRuleParams); qWarning() << response; verifyRuleError(response); // Add rule 1 addRuleParams.clear(); addRuleParams.insert("name", "Rule on -> off"); addRuleParams.insert("eventDescriptors", QVariantList() << onEvent); addRuleParams.insert("actions", QVariantList() << offAction); response = injectAndWait("Rules.AddRule", addRuleParams); verifyRuleError(response); cleanupMockHistory(); QVariantMap params; params.insert("deviceId", m_mockDeviceId); params.insert("actionTypeId", mockPowerStateTypeId); params.insert("params", QVariantList() << powerOffActionParam); response = injectAndWait("Actions.ExecuteAction", params); verifyRuleExecuted(mockPowerActionTypeId); cleanupMockHistory(); params.clear(); params.insert("deviceId", m_mockDeviceId); params.insert("actionTypeId", mockPowerStateTypeId); params.insert("params", QVariantList() << powerOnActionParam); response = injectAndWait("Actions.ExecuteAction", params); verifyRuleExecuted(mockPowerActionTypeId); // No need to check anything else. This test sets up a binding loop and if the core doesn't catch it it'll crash here. } void TestRules::testScene() { // Given scenes are rules without stateEvaluator and eventDescriptors, they evaluate to true when asked for "active()" // This test should catch the case where such a rule might wrongly be exected by evaluating it // when another state change happens or when a time event is evaluated NymeaCore::instance()->timeManager()->stopTimer(); QDateTime now = QDateTime::currentDateTime(); NymeaCore::instance()->timeManager()->setTime(now); QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); // state power state to false initially QNetworkRequest request(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockPowerStateTypeId.toString()).arg(false))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); // state battery critical state to false initially spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBatteryCriticalStateTypeId.toString()).arg(false))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); // Add a scene setting power to true QVariantMap powerAction; powerAction.insert("deviceId", m_mockDeviceId); powerAction.insert("actionTypeId", mockPowerStateTypeId); QVariantMap powerActionParam; powerActionParam.insert("paramTypeId", mockPowerStateTypeId); powerActionParam.insert("value", true); powerAction.insert("ruleActionParams", QVariantList() << powerActionParam); QVariantMap addRuleParams; addRuleParams.insert("name", "TestScene"); addRuleParams.insert("enabled", true); addRuleParams.insert("executable", true); addRuleParams.insert("actions", QVariantList() << powerAction); QVariant response = injectAndWait("Rules.AddRule", addRuleParams); QCOMPARE(response.toMap().value("status").toString(), QString("success")); QCOMPARE(response.toMap().value("params").toMap().value("ruleError").toString(), QString("RuleErrorNoError")); // trigger state change on battery critical spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockBatteryCriticalStateTypeId.toString()).arg(true))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleNotExecuted(); // Now trigger a time change NymeaCore::instance()->timeManager()->setTime(now.addSecs(1)); verifyRuleNotExecuted(); } void TestRules::testHousekeeping_data() { QTest::addColumn("testAction"); QTest::addColumn("testExitAction"); QTest::addColumn("testStateEvaluator"); QTest::addColumn("testEventDescriptor"); QTest::newRow("action") << true << false << false << false; QTest::newRow("exitAction") << false << true << false << false; QTest::newRow("stateDescriptor") << false << false << true << false; QTest::newRow("eventDescriptor")<< false << false << false << true; } void TestRules::testHousekeeping() { QFETCH(bool, testAction); QFETCH(bool, testExitAction); QFETCH(bool, testStateEvaluator); QFETCH(bool, testEventDescriptor); QVariantMap params; params.insert("deviceClassId", mockDeviceClassId); params.insert("name", "TestDeviceToBeRemoved"); QVariantList deviceParams; QVariantMap httpParam; httpParam.insert("paramTypeId", mockDeviceHttpportParamTypeId); httpParam.insert("value", 6667); deviceParams.append(httpParam); params.insert("deviceParams", deviceParams); QVariant response = injectAndWait("Devices.AddConfiguredDevice", params); DeviceId deviceId = DeviceId::fromUuid(response.toMap().value("params").toMap().value("deviceId").toUuid()); QVERIFY2(!deviceId.isNull(), "Something went wrong creating the device for testing."); // Create a rule with this device params.clear(); params.insert("name", "testrule"); if (testEventDescriptor) { QVariantList eventDescriptors; QVariantMap eventDescriptor; eventDescriptor.insert("eventTypeId", mockEvent1EventTypeId); eventDescriptor.insert("deviceId", testEventDescriptor ? deviceId : m_mockDeviceId); eventDescriptors.append(eventDescriptor); params.insert("eventDescriptors", eventDescriptors); } QVariantMap stateEvaluator; QVariantMap stateDescriptor; stateDescriptor.insert("stateTypeId", mockIntStateTypeId); stateDescriptor.insert("operator", "ValueOperatorGreater"); stateDescriptor.insert("value", 555); stateDescriptor.insert("deviceId", testStateEvaluator ? deviceId : m_mockDeviceId); stateEvaluator.insert("stateDescriptor", stateDescriptor); params.insert("stateEvaluator", stateEvaluator); QVariantList actions; QVariantMap action; action.insert("actionTypeId", mockWithoutParamsActionTypeId); action.insert("deviceId", testAction ? deviceId : m_mockDeviceId); actions.append(action); params.insert("actions", actions); if (!testEventDescriptor) { QVariantList exitActions; QVariantMap exitAction; exitAction.insert("actionTypeId", mockWithoutParamsActionTypeId); exitAction.insert("deviceId", testExitAction ? deviceId : m_mockDeviceId); exitActions.append(exitAction); params.insert("exitActions", exitActions); } response = injectAndWait("Rules.AddRule", params); RuleId ruleId = RuleId::fromUuid(response.toMap().value("params").toMap().value("ruleId").toUuid()); // Verfy that the rule has been created successfully and our device is in there. params.clear(); params.insert("ruleId", ruleId); response = injectAndWait("Rules.GetRuleDetails", params); if (testEventDescriptor) { QVERIFY2(response.toMap().value("params").toMap().value("rule").toMap().value("eventDescriptors").toList().first().toMap().value("deviceId").toUuid().toString() == (testEventDescriptor ? deviceId.toString() : m_mockDeviceId.toString()), "Couldn't find device in eventDescriptor of rule"); } QVERIFY2(response.toMap().value("params").toMap().value("rule").toMap().value("stateEvaluator").toMap().value("stateDescriptor").toMap().value("deviceId").toUuid().toString() == (testStateEvaluator ? deviceId.toString() : m_mockDeviceId.toString()), "Couldn't find device in stateEvaluator of rule"); QVERIFY2(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().first().toMap().value("deviceId").toUuid().toString() == (testAction ? deviceId.toString() : m_mockDeviceId.toString()), "Couldn't find device in actions of rule"); if (!testEventDescriptor) { QVERIFY2(response.toMap().value("params").toMap().value("rule").toMap().value("exitActions").toList().first().toMap().value("deviceId").toUuid().toString() == (testExitAction ? deviceId.toString() : m_mockDeviceId.toString()), "Couldn't find device in exitActions of rule"); } // Manually delete this device from config NymeaSettings settings(NymeaSettings::SettingsRoleDevices); settings.beginGroup("DeviceConfig"); settings.remove(deviceId.toString()); settings.endGroup(); restartServer(); // Now make sure the appropriate entries with our device have disappeared response = injectAndWait("Rules.GetRuleDetails", params); if (testEventDescriptor) { QVERIFY2(response.toMap().value("params").toMap().value("rule").toMap().value("eventDescriptors").toList().count() == (testEventDescriptor ? 0: 1), "EventDescriptor still in rule... should've been removed by housekeeping."); } QVERIFY2(response.toMap().value("params").toMap().value("rule").toMap().value("stateEvaluator").toMap().value("stateDescriptor").toMap().isEmpty() == (testStateEvaluator ? true : false), "StateEvaluator still in rule... should've been removed by housekeeping."); QVERIFY2(response.toMap().value("params").toMap().value("rule").toMap().value("actions").toList().count() == (testAction ? 0 : 1), "Action still in rule... should've been removed by housekeeping."); if (!testEventDescriptor) { QVERIFY2(response.toMap().value("params").toMap().value("rule").toMap().value("exitActions").toList().count() == (testExitAction ? 0: 1), "ExitAction still in rule... should've been removed by housekeeping."); } } #include "testrules.moc" QTEST_MAIN(TestRules)