/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2015 Simon Stuerz * * Copyright (C) 2014 Michael Zanetti * * * * This file is part of guh. * * * * Guh 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. * * * * Guh is distributed in the hope that it will be useful, * * 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 guh. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "guhtestbase.h" #include "guhcore.h" #include "devicemanager.h" #include "mocktcpserver.h" #include #include #include #include #include #include #include using namespace guhserver; class TestRules: public GuhTestBase { Q_OBJECT private: void cleanupMockHistory(); void cleanupRules(); void verifyRuleExecuted(const ActionTypeId &actionTypeId); void verifyRuleNotExecuted(); QVariant validIntStateBasedRule(const QString &name, const bool &executable, const bool &enabled); private slots: void cleanup(); void emptyRule(); void getInvalidRule(); void addRemoveRules_data(); void addRemoveRules(); void editRules_data(); void editRules(); void executeRuleActions_data(); void executeRuleActions(); void removeInvalidRule(); void loadStoreConfig(); void evaluateEvent(); void testStateEvaluator_data(); void testStateEvaluator(); void testStateEvaluator2_data(); void testStateEvaluator2(); void testStateChange(); void enableDisableRule(); void testEventBasedAction(); void testRuleActionParams_data(); void testRuleActionParams(); }; void TestRules::cleanupMockHistory() { QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); QNetworkRequest request(QUrl(QString("http://localhost:%1/clearactionhistory").arg(m_mockDevice1Port).arg(mockEvent1Id.toString()))); QNetworkReply *reply = nam.get(request); spy.wait(500); 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)); } } 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::RuleErrorMissingParameter); } void TestRules::getInvalidRule() { QVariantMap params; params.insert("ruleId", QUuid::createUuid()); QVariant response = injectAndWait("Rules.GetRuleDetails", params); verifyRuleError(response, RuleEngine::RuleErrorRuleNotFound); } void TestRules::verifyRuleExecuted(const ActionTypeId &actionTypeId) { // Verify rule got executed QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); QNetworkRequest request(QUrl(QString("http://localhost:%1/actionhistory").arg(m_mockDevice1Port))); QNetworkReply *reply = nam.get(request); spy.wait(500); QCOMPARE(spy.count(), 1); QByteArray actionHistory = reply->readAll(); qDebug() << "have action history" << actionHistory; QVERIFY2(actionTypeId == ActionTypeId(actionHistory), "Action not triggered"); reply->deleteLater(); } void TestRules::verifyRuleNotExecuted() { QNetworkAccessManager nam; QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*))); QNetworkRequest request(QUrl(QString("http://localhost:%1/actionhistory").arg(m_mockDevice1Port))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); QByteArray actionHistory = reply->readAll(); qDebug() << "have action history" << actionHistory; QVERIFY2(actionHistory.isEmpty(), "Action is triggered while it should not have been."); reply->deleteLater(); } QVariant TestRules::validIntStateBasedRule(const QString &name, const bool &executable, const bool &enabled) { QVariantMap params; // StateDescriptor QVariantMap stateDescriptor; stateDescriptor.insert("stateTypeId", mockIntStateId); 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", mockActionIdWithParams); QVariantList actionParams; QVariantMap param1; param1.insert("name", "mockActionParam1"); param1.insert("value", 5); actionParams.append(param1); QVariantMap param2; param2.insert("name", "mockActionParam2"); param2.insert("value", true); actionParams.append(param2); action.insert("deviceId", m_mockDeviceId); action.insert("ruleActionParams", actionParams); // RuleExitAction QVariantMap exitAction; exitAction.insert("actionTypeId", mockActionIdNoParams); 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::addRemoveRules_data() { // RuleAction QVariantMap validActionNoParams; validActionNoParams.insert("actionTypeId", mockActionIdNoParams); validActionNoParams.insert("deviceId", m_mockDeviceId); validActionNoParams.insert("ruleActionParams", QVariantList()); QVariantMap validActionWithParams; validActionWithParams.insert("actionTypeId", mockActionIdNoParams); validActionWithParams.insert("deviceId", m_mockDeviceId); validActionWithParams.insert("ruleActionParams", QVariantList()); QVariantMap invalidAction; invalidAction.insert("actionTypeId", ActionTypeId()); invalidAction.insert("deviceId", m_mockDeviceId); invalidAction.insert("ruleActionParams", QVariantList()); // RuleExitAction QVariantMap validExitActionNoParams; validExitActionNoParams.insert("actionTypeId", mockActionIdNoParams); 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", mockIntStateId); 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", mockEvent1Id); validEventDescriptor1.insert("deviceId", m_mockDeviceId); validEventDescriptor1.insert("paramDescriptors", QVariantList()); QVariantMap validEventDescriptor2; validEventDescriptor2.insert("eventTypeId", mockEvent2Id); validEventDescriptor2.insert("deviceId", m_mockDeviceId); QVariantList params; QVariantMap param1; param1.insert("name", "mockParamInt"); param1.insert("value", 3); param1.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorEquals)); params.append(param1); validEventDescriptor2.insert("paramDescriptors", params); QVariantMap validEventDescriptor3; validEventDescriptor3.insert("eventTypeId", mockEvent2Id); validEventDescriptor3.insert("deviceId", m_mockDeviceId); validEventDescriptor3.insert("paramDescriptors", QVariantList()); // EventDescriptorList QVariantList eventDescriptorList; eventDescriptorList.append(validEventDescriptor1); eventDescriptorList.append(validEventDescriptor2); QVariantMap invalidEventDescriptor; invalidEventDescriptor.insert("eventTypeId", mockEvent1Id); invalidEventDescriptor.insert("deviceId", DeviceId()); invalidEventDescriptor.insert("paramDescriptors", QVariantList()); // RuleAction event based QVariantMap validActionEventBased; validActionEventBased.insert("actionTypeId", mockActionIdWithParams); validActionEventBased.insert("deviceId", m_mockDeviceId); QVariantMap validActionEventBasedParam1; validActionEventBasedParam1.insert("name", "mockActionParam1"); validActionEventBasedParam1.insert("eventTypeId", mockEvent2Id); validActionEventBasedParam1.insert("eventParamName", "mockParamInt"); QVariantMap validActionEventBasedParam2; validActionEventBasedParam2.insert("name", "mockActionParam2"); validActionEventBasedParam2.insert("value", false); validActionEventBased.insert("ruleActionParams", QVariantList() << validActionEventBasedParam1 << validActionEventBasedParam2); QVariantMap invalidActionEventBased; invalidActionEventBased.insert("actionTypeId", mockActionIdNoParams); invalidActionEventBased.insert("deviceId", m_mockDeviceId); validActionEventBasedParam1.insert("value", 10); invalidActionEventBased.insert("ruleActionParams", QVariantList() << validActionEventBasedParam1); QVariantMap invalidActionEventBased2; invalidActionEventBased2.insert("actionTypeId", mockActionIdWithParams); invalidActionEventBased2.insert("deviceId", m_mockDeviceId); QVariantMap invalidActionEventBasedParam2; invalidActionEventBasedParam2.insert("name", "mockActionParam1"); invalidActionEventBasedParam2.insert("eventTypeId", mockEvent1Id); invalidActionEventBasedParam2.insert("eventParamName", "value"); QVariantMap invalidActionEventBasedParam3; invalidActionEventBasedParam3.insert("name", "mockActionParam2"); invalidActionEventBasedParam3.insert("value", 2); invalidActionEventBased2.insert("ruleActionParams", QVariantList() << invalidActionEventBasedParam2 << invalidActionEventBasedParam3); QVariantMap invalidActionEventBased3; invalidActionEventBased3.insert("actionTypeId", mockActionIdWithParams); invalidActionEventBased3.insert("deviceId", m_mockDeviceId); QVariantMap invalidActionEventBasedParam4; invalidActionEventBasedParam4.insert("name", "mockActionParam1"); invalidActionEventBasedParam4.insert("eventTypeId", mockEvent1Id); invalidActionEventBasedParam4.insert("eventParamName", "mockParamInt"); 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::RuleErrorInvalidRuleActionParameter << 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("invalid rule. 1 Action, 1 Exit Action, 1 EventDescriptor, 1 StateEvaluator, name") << true << validActionNoParams << validExitActionNoParams << validEventDescriptor1 << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorInvalidRuleFormat << false << "TestRule"; QTest::newRow("invalid rule. 1 Action, 1 Exit Action, eventDescriptorList, 1 StateEvaluator, name") << true << validActionNoParams << validExitActionNoParams << QVariantMap() << eventDescriptorList << validStateEvaluator << RuleEngine::RuleErrorInvalidRuleFormat << 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. diabled, 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 rule: eventDescriptor and eventDescriptorList used") << true << validActionNoParams << QVariantMap() << validEventDescriptor1 << eventDescriptorList << validStateEvaluator << RuleEngine::RuleErrorInvalidParameter << false << "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::RuleErrorDeviceNotFound << false << "TestRule"; QTest::newRow("invalid StateDescriptor") << true << validActionNoParams << QVariantMap() << validEventDescriptor1 << QVariantList() << invalidStateEvaluator << RuleEngine::RuleErrorInvalidParameter << true << "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("eventDescriptor", eventDescriptor); } if (!eventDescriptorList.isEmpty()) { params.insert("eventDescriptorList", 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); // verifySuccess(response); 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 shoud 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 shoud 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", mockActionIdNoParams); 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", mockActionIdNoParams); 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", mockIntStateId); 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", mockEvent1Id); validEventDescriptor1.insert("deviceId", m_mockDeviceId); validEventDescriptor1.insert("paramDescriptors", QVariantList()); QVariantMap validEventDescriptor2; validEventDescriptor2.insert("eventTypeId", mockEvent2Id); validEventDescriptor2.insert("deviceId", m_mockDeviceId); QVariantList params; QVariantMap param1; param1.insert("name", "mockParamInt"); param1.insert("value", 3); param1.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorEquals)); params.append(param1); validEventDescriptor2.insert("paramDescriptors", params); QVariantMap validEventDescriptor3; validEventDescriptor3.insert("eventTypeId", mockEvent2Id); validEventDescriptor3.insert("deviceId", m_mockDeviceId); validEventDescriptor3.insert("paramDescriptors", QVariantList()); // EventDescriptorList QVariantList eventDescriptorList; eventDescriptorList.append(validEventDescriptor1); eventDescriptorList.append(validEventDescriptor2); QVariantMap invalidEventDescriptor; invalidEventDescriptor.insert("eventTypeId", mockEvent1Id); invalidEventDescriptor.insert("deviceId", DeviceId()); invalidEventDescriptor.insert("paramDescriptors", QVariantList()); // RuleAction event based QVariantMap validActionEventBased; validActionEventBased.insert("actionTypeId", mockActionIdWithParams); validActionEventBased.insert("deviceId", m_mockDeviceId); QVariantMap validActionEventBasedParam1; validActionEventBasedParam1.insert("name", "mockActionParam1"); validActionEventBasedParam1.insert("eventTypeId", mockEvent2Id); validActionEventBasedParam1.insert("eventParamName", "mockParamInt"); QVariantMap validActionEventBasedParam2; validActionEventBasedParam2.insert("name", "mockActionParam2"); validActionEventBasedParam2.insert("value", false); validActionEventBased.insert("ruleActionParams", QVariantList() << validActionEventBasedParam1 << validActionEventBasedParam2); QVariantMap invalidActionEventBased; invalidActionEventBased.insert("actionTypeId", mockActionIdNoParams); invalidActionEventBased.insert("deviceId", m_mockDeviceId); validActionEventBasedParam1.insert("value", 10); invalidActionEventBased.insert("ruleActionParams", QVariantList() << validActionEventBasedParam1); QVariantMap invalidActionEventBased2; invalidActionEventBased2.insert("actionTypeId", mockActionIdWithParams); invalidActionEventBased2.insert("deviceId", m_mockDeviceId); QVariantMap invalidActionEventBasedParam2; invalidActionEventBasedParam2.insert("name", "mockActionParam1"); invalidActionEventBasedParam2.insert("eventTypeId", mockEvent1Id); invalidActionEventBasedParam2.insert("eventParamName", "value"); QVariantMap invalidActionEventBasedParam3; invalidActionEventBasedParam3.insert("name", "mockActionParam2"); invalidActionEventBasedParam3.insert("value", 2); invalidActionEventBased2.insert("ruleActionParams", QVariantList() << invalidActionEventBasedParam2 << invalidActionEventBasedParam3); QVariantMap invalidActionEventBased3; invalidActionEventBased3.insert("actionTypeId", mockActionIdWithParams); invalidActionEventBased3.insert("deviceId", m_mockDeviceId); QVariantMap invalidActionEventBasedParam4; invalidActionEventBasedParam4.insert("name", "mockActionParam1"); invalidActionEventBasedParam4.insert("eventTypeId", mockEvent1Id); invalidActionEventBasedParam4.insert("eventParamName", "mockParamInt"); 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::RuleErrorInvalidRuleActionParameter << "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("invalid rule. 1 Action, 1 Exit Action, 1 EventDescriptor, 1 StateEvaluator, name") << true << validActionNoParams << validExitActionNoParams << validEventDescriptor1 << QVariantList() << validStateEvaluator << RuleEngine::RuleErrorInvalidRuleFormat << "TestRule"; QTest::newRow("invalid rule. 1 Action, 1 Exit Action, eventDescriptorList, 1 StateEvaluator, name") << true << validActionNoParams << validExitActionNoParams << QVariantMap() << eventDescriptorList << validStateEvaluator << RuleEngine::RuleErrorInvalidRuleFormat << "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. diabled, 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"; QTest::newRow("invalid rule: eventDescriptor and eventDescriptorList used") << true << validActionNoParams << QVariantMap() << validEventDescriptor1 << eventDescriptorList << validStateEvaluator << RuleEngine::RuleErrorInvalidParameter << "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", mockEvent1Id); eventDescriptor1.insert("deviceId", m_mockDeviceId); eventDescriptor1.insert("paramDescriptors", QVariantList()); QVariantMap eventDescriptor2; eventDescriptor2.insert("eventTypeId", mockEvent2Id); eventDescriptor2.insert("deviceId", m_mockDeviceId); eventDescriptor2.insert("paramDescriptors", QVariantList()); QVariantMap eventParam1; eventParam1.insert("name", "mockParamInt"); 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", mockIntStateId); stateDescriptor1.insert("value", 1); QVariantMap stateDescriptor2; stateDescriptor2.insert("deviceId", m_mockDeviceId); stateDescriptor2.insert("operator", JsonTypes::valueOperatorToString(Types::ValueOperatorEquals)); stateDescriptor2.insert("stateTypeId", mockBoolStateId); 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", mockActionIdNoParams); action1.insert("deviceId", m_mockDeviceId); action1.insert("ruleActionParams", QVariantList()); QVariantMap action2; action2.insert("actionTypeId", mockActionIdWithParams); qDebug() << "got action id" << mockActionIdWithParams; action2.insert("deviceId", m_mockDeviceId); QVariantList action2Params; QVariantMap action2Param1; action2Param1.insert("name", "mockActionParam1"); action2Param1.insert("value", 5); action2Params.append(action2Param1); QVariantMap action2Param2; action2Param2.insert("name", "mockActionParam2"); action2Param2.insert("value", true); action2Params.append(action2Param2); action2.insert("ruleActionParams", action2Params); // RuleAction event based QVariantMap validActionEventBased; validActionEventBased.insert("actionTypeId", mockActionIdWithParams); validActionEventBased.insert("deviceId", m_mockDeviceId); QVariantMap validActionEventBasedParam1; validActionEventBasedParam1.insert("name", "mockActionParam1"); validActionEventBasedParam1.insert("eventTypeId", mockEvent2Id); validActionEventBasedParam1.insert("eventParamName", "mockParamInt"); QVariantMap validActionEventBasedParam2; validActionEventBasedParam2.insert("name", "mockActionParam2"); validActionEventBasedParam2.insert("value", false); validActionEventBased.insert("ruleActionParams", QVariantList() << validActionEventBasedParam1 << validActionEventBasedParam2); QVariantList validEventDescriptors3; QVariantMap validEventDescriptor3; validEventDescriptor3.insert("eventTypeId", mockEvent2Id); 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("eventDescriptorList", 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("eventDescriptor", eventDescriptor); } if (!eventDescriptorList.isEmpty()) { params.insert("eventDescriptorList", 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(500); 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 shoud 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 shoud 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(); // EXECUTE actions QVariantMap executeParams; executeParams.insert("ruleId", ruleId.toString()); response = injectAndWait("Rules.ExecuteActions", executeParams); verifyRuleError(response, ruleError); if (ruleError == RuleEngine::RuleErrorNoError) { verifyRuleExecuted(mockActionIdWithParams); } else { verifyRuleNotExecuted(); } cleanupMockHistory(); // EXECUTE exit actions response = injectAndWait("Rules.ExecuteExitActions", executeParams); verifyRuleError(response, ruleError); if (ruleError == RuleEngine::RuleErrorNoError) { verifyRuleExecuted(mockActionIdNoParams); } else { verifyRuleNotExecuted(); } cleanupMockHistory(); // 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", mockEvent1Id); eventDescriptor1.insert("deviceId", m_mockDeviceId); eventDescriptor1.insert("paramDescriptors", QVariantList()); QVariantMap eventDescriptor2; eventDescriptor2.insert("eventTypeId", mockEvent2Id); eventDescriptor2.insert("deviceId", m_mockDeviceId); eventDescriptor2.insert("paramDescriptors", QVariantList()); QVariantList eventParamDescriptors; QVariantMap eventParam1; eventParam1.insert("name", "mockParamInt"); 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", mockIntStateId); 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", mockBoolStateId); stateDescriptor3.insert("value", true); QVariantMap stateEvaluator3; stateEvaluator3.insert("stateDescriptor", stateDescriptor3); stateEvaluator3.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd)); childEvaluators.append(stateEvaluator2); childEvaluators.append(stateEvaluator3); stateEvaluator1.insert("childEvaluators", childEvaluators); stateEvaluator1.insert("operator", JsonTypes::stateOperatorToString(Types::StateOperatorAnd)); QVariantMap action1; action1.insert("actionTypeId", mockActionIdNoParams); action1.insert("deviceId", m_mockDeviceId); action1.insert("ruleActionParams", QVariantList()); QVariantMap action2; action2.insert("actionTypeId", mockActionIdWithParams); qDebug() << "got action id" << mockActionIdWithParams; action2.insert("deviceId", m_mockDeviceId); QVariantList action2Params; QVariantMap action2Param1; action2Param1.insert("name", "mockActionParam1"); action2Param1.insert("value", 5); action2Params.append(action2Param1); QVariantMap action2Param2; action2Param2.insert("name", "mockActionParam2"); action2Param2.insert("value", true); action2Params.append(action2Param2); action2.insert("ruleActionParams", action2Params); // RuleAction event based QVariantMap validActionEventBased; validActionEventBased.insert("actionTypeId", mockActionIdWithParams); validActionEventBased.insert("deviceId", m_mockDeviceId); QVariantMap validActionEventBasedParam1; validActionEventBasedParam1.insert("name", "mockActionParam1"); validActionEventBasedParam1.insert("eventTypeId", mockEvent2Id); validActionEventBasedParam1.insert("eventParamName", "mockParamInt"); QVariantMap validActionEventBasedParam2; validActionEventBasedParam2.insert("name", "mockActionParam2"); validActionEventBasedParam2.insert("value", false); validActionEventBased.insert("ruleActionParams", QVariantList() << validActionEventBasedParam1 << validActionEventBasedParam2); QVariantList validEventDescriptors3; QVariantMap validEventDescriptor3; validEventDescriptor3.insert("eventTypeId", mockEvent2Id); validEventDescriptor3.insert("deviceId", m_mockDeviceId); validEventDescriptor3.insert("paramDescriptors", QVariantList()); validEventDescriptors3.append(validEventDescriptor3); // rule 1 QVariantMap params; QVariantList actions; actions.append(action1); actions.append(action2); params.insert("actions", actions); params.insert("eventDescriptorList", 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 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 QVariantMap params3; QVariantList actions3; actions3.append(validActionEventBased); params3.insert("actions", actions3); params3.insert("eventDescriptorList", validEventDescriptors3); params3.insert("name", "TestRule3"); QVariant response3 = injectAndWait("Rules.AddRule", params3); RuleId newRuleId3 = RuleId(response3.toMap().value("params").toMap().value("ruleId").toString()); verifyRuleError(response3); restartServer(); response = injectAndWait("Rules.GetRules"); QVariantList rules = response.toMap().value("params").toMap().value("ruleDescriptions").toList(); QVERIFY2(rules.count() == 3, "There should be exactly three 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."); // 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 shoud 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; QVERIFY2(replyEventDescriptorVariant == expectedEventDescriptorVariant, "EventDescriptor doesn't match"); } } QVERIFY2(found, "missing eventdescriptor"); } QVERIFY2(rule1.value("name").toString() == "TestRule", "Loaded wrong name for rule"); QVariantMap replyStateEvaluator= rule1.value("stateEvaluator").toMap(); QVariantList replyChildEvaluators = replyStateEvaluator.value("childEvaluators").toList(); QVERIFY2(replyStateEvaluator.value("operator") == "StateOperatorAnd", "There should be the AND operator."); QVERIFY2(replyChildEvaluators.count() == 2, "There shoud be exactly 2 childEvaluators"); foreach (const QVariant &childEvaluator, replyChildEvaluators) { QVERIFY2(childEvaluator.toMap().contains("stateDescriptor"), "StateDescriptor missing in StateEvaluator"); QVariantMap stateDescriptor = childEvaluator.toMap().value("stateDescriptor").toMap(); QVERIFY2(stateDescriptor.value("deviceId") == m_mockDeviceId, "DeviceId of stateDescriptor does not match"); QVERIFY2(stateDescriptor.value("stateTypeId") == mockIntStateId || stateDescriptor.value("stateTypeId") == mockBoolStateId, "StateTypeId of stateDescriptor doesn't match"); } 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; QJsonDocument bDoc = QJsonDocument::fromVariant(actionVariant); QString bString = bDoc.toJson(); QJsonDocument aDoc = QJsonDocument::fromVariant(replyActionVariant); QString aString = aDoc.toJson(); QVERIFY2(actionVariant == replyActionVariant, QString("Action doesn't match after loading from config.\nBefore storing: %1\nAfter storing:%2").arg(bString).arg(aString).toUtf8().data()); } } 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."); QVERIFY2(replyChildEvaluators2.count() == 2, "There shoud be exactly 2 childEvaluators"); foreach (const QVariant &childEvaluator, replyChildEvaluators2) { QVERIFY2(childEvaluator.toMap().contains("stateDescriptor"), "StateDescriptor missing in StateEvaluator"); QVariantMap stateDescriptor = childEvaluator.toMap().value("stateDescriptor").toMap(); QVERIFY2(stateDescriptor.value("deviceId") == m_mockDeviceId, "DeviceId of stateDescriptor does not match"); QVERIFY2(stateDescriptor.value("stateTypeId") == mockIntStateId || stateDescriptor.value("stateTypeId") == mockBoolStateId, "StateTypeId of stateDescriptor doesn't match"); } 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; QJsonDocument bDoc = QJsonDocument::fromVariant(actionVariant); QString bString = bDoc.toJson(); QJsonDocument aDoc = QJsonDocument::fromVariant(replyActionVariant); QString aString = aDoc.toJson(); QVERIFY2(actionVariant == replyActionVariant, QString("Action doesn't match after loading from config.\nBefore storing: %1\nAfter storing:%2").arg(bString).arg(aString).toUtf8().data()); } } 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; QJsonDocument bDoc = QJsonDocument::fromVariant(exitActionVariant); QString bString = bDoc.toJson(); QJsonDocument aDoc = QJsonDocument::fromVariant(replyActionVariant); QString aString = aDoc.toJson(); QVERIFY2(exitActionVariant == replyActionVariant, QString("Action doesn't match after loading from config.\nBefore storing: %1\nAfter storing:%2").arg(bString).arg(aString).toUtf8().data()); } } 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 shoud be exactly 1 eventDescriptor"); QVariantMap eventDescriptor = eventDescriptors3.first().toMap(); QVERIFY2(eventDescriptor.value("eventTypeId").toString() == mockEvent2Id.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; QJsonDocument bDoc = QJsonDocument::fromVariant(actionVariant); QString bString = bDoc.toJson(); QJsonDocument aDoc = QJsonDocument::fromVariant(replyActionVariant); QString aString = aDoc.toJson(); QVERIFY2(actionVariant == replyActionVariant, QString("Action doesn't match after loading from config.\nBefore storing: %1\nAfter storing:%2").arg(bString).arg(aString).toUtf8().data()); } } QVERIFY2(found, "Action not found after loading from config."); } // 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", mockEvent1Id); event1.insert("deviceId", m_mockDeviceId); events.append(event1); addRuleParams.insert("eventDescriptorList", events); QVariantList actions; QVariantMap action; action.insert("actionTypeId", mockActionIdNoParams); 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(mockEvent1Id.toString()))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleExecuted(mockActionIdNoParams); } 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", mockIntStateId); stateDescriptor.insert("value", 42); stateEvaluator.insert("stateDescriptor", stateDescriptor); addRuleParams.insert("stateEvaluator", stateEvaluator); addRuleParams.insert("name", "TestRule"); QVariantList actions; QVariantMap action; action.insert("actionTypeId", mockActionIdNoParams); 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(mockIntStateId.toString()).arg(42))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleExecuted(mockActionIdNoParams); 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(mockIntStateId.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(mockIntStateId.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(mockIntStateId.toString()).arg(100))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); verifyRuleExecuted(mockActionIdNoParams); 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() << mockIntStateId << QVariant(10) << Types::ValueOperatorEquals << false; QTest::newRow("equals, not matching") << m_mockDeviceId << mockIntStateId << QVariant(7777) << Types::ValueOperatorEquals << false; QTest::newRow("equals, matching") << m_mockDeviceId << mockIntStateId << QVariant(10) << Types::ValueOperatorEquals << true; QTest::newRow("not equal, not matching") << m_mockDeviceId << mockIntStateId << QVariant(10) << Types::ValueOperatorNotEquals << false; QTest::newRow("not equal, matching") << m_mockDeviceId << mockIntStateId << QVariant(7777) << Types::ValueOperatorNotEquals << true; QTest::newRow("Greater, not matching") << m_mockDeviceId << mockIntStateId << QVariant(7777) << Types::ValueOperatorGreater << false; QTest::newRow("Greater, matching") << m_mockDeviceId << mockIntStateId << QVariant(2) << Types::ValueOperatorGreater << true; QTest::newRow("GreaterOrEqual, not matching") << m_mockDeviceId << mockIntStateId << QVariant(7777) << Types::ValueOperatorGreaterOrEqual << false; QTest::newRow("GreaterOrEqual, matching (greater)") << m_mockDeviceId << mockIntStateId << QVariant(2) << Types::ValueOperatorGreaterOrEqual << true; QTest::newRow("GreaterOrEqual, matching (equals)") << m_mockDeviceId << mockIntStateId << QVariant(10) << Types::ValueOperatorGreaterOrEqual << true; QTest::newRow("Less, not matching") << m_mockDeviceId << mockIntStateId << QVariant(2) << Types::ValueOperatorLess << false; QTest::newRow("Less, matching") << m_mockDeviceId << mockIntStateId << QVariant(7777) << Types::ValueOperatorLess << true; QTest::newRow("LessOrEqual, not matching") << m_mockDeviceId << mockIntStateId << QVariant(2) << Types::ValueOperatorLessOrEqual << false; QTest::newRow("LessOrEqual, matching (less)") << m_mockDeviceId << mockIntStateId << QVariant(777) << Types::ValueOperatorLessOrEqual << true; QTest::newRow("LessOrEqual, matching (equals)") << m_mockDeviceId << mockIntStateId << QVariant(10) << Types::ValueOperatorLessOrEqual << 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(mockIntStateId, m_mockDeviceId, intValue, intOperator); StateEvaluator evaluator1(descriptor1); StateDescriptor descriptor2(mockBoolStateId, 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::enableDisableRule() { // Add a rule QVariantMap addRuleParams; QVariantList events; QVariantMap event1; event1.insert("eventTypeId", mockEvent1Id); event1.insert("deviceId", m_mockDeviceId); events.append(event1); addRuleParams.insert("eventDescriptorList", events); addRuleParams.insert("name", "TestRule"); QVariantList actions; QVariantMap action; action.insert("actionTypeId", mockActionIdNoParams); 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(mockEvent1Id.toString()))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleExecuted(mockActionIdNoParams); cleanupMockHistory(); // Now disable the rule QVariantMap disableParams; 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(mockEvent1Id.toString()))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleNotExecuted(); cleanupMockHistory(); // Now enable the rule again response = injectAndWait("Rules.EnableRule", disableParams); verifyRuleError(response); // trigger event in mock device spy.clear(); request = QNetworkRequest(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(m_mockDevice1Port).arg(mockEvent1Id.toString()))); reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleExecuted(mockActionIdNoParams); } void TestRules::testEventBasedAction() { // Add a rule QVariantMap addRuleParams; QVariantMap eventDescriptor; eventDescriptor.insert("eventTypeId", mockIntStateId); eventDescriptor.insert("deviceId", m_mockDeviceId); addRuleParams.insert("eventDescriptor", eventDescriptor); addRuleParams.insert("name", "TestRule"); addRuleParams.insert("enabled", true); QVariantList actions; QVariantMap action; QVariantList ruleActionParams; QVariantMap param1; param1.insert("name", "mockActionParam1"); param1.insert("eventTypeId", mockIntStateId); param1.insert("eventParamName", "value"); QVariantMap param2; param2.insert("name", "mockActionParam2"); param2.insert("value", true); ruleActionParams.append(param1); ruleActionParams.append(param2); action.insert("actionTypeId", mockActionIdWithParams); 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(mockIntStateId.toString()).arg(42))); QNetworkReply *reply = nam.get(request); spy.wait(); QCOMPARE(spy.count(), 1); reply->deleteLater(); verifyRuleExecuted(mockActionIdWithParams); // TODO: check if this action was realy executed with the int state value 42 } void TestRules::testRuleActionParams_data() { QVariantMap action; QVariantList ruleActionParams; QVariantMap param1; param1.insert("name", "mockActionParam1"); param1.insert("value", 4); QVariantMap param2; param2.insert("name", "mockActionParam2"); param2.insert("value", true); ruleActionParams.append(param1); ruleActionParams.append(param2); action.insert("actionTypeId", mockActionIdWithParams); action.insert("deviceId", m_mockDeviceId); action.insert("ruleActionParams", ruleActionParams); QVariantMap invalidAction1; invalidAction1.insert("actionTypeId", mockActionIdWithParams); invalidAction1.insert("deviceId", m_mockDeviceId); invalidAction1.insert("ruleActionParams", QVariantList() << param2); QVariantMap invalidAction2; invalidAction2.insert("actionTypeId", mockActionIdWithParams); 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::RuleErrorInvalidRuleActionParameter; QTest::newRow("invalid action params2") << invalidAction2 << QVariantMap() << RuleEngine::RuleErrorInvalidRuleActionParameter; QTest::newRow("valid action and invalid exit action params1") << action << invalidAction1 << RuleEngine::RuleErrorInvalidRuleActionParameter; QTest::newRow("valid action and invalid exit action params2") << action << invalidAction2 << RuleEngine::RuleErrorInvalidRuleActionParameter; } 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); } #include "testrules.moc" QTEST_MAIN(TestRules)