Fix an issue where Rules might not be initialized properly at startup

This commit is contained in:
Michael Zanetti 2018-12-16 01:00:54 +01:00
parent 6f1e22546a
commit 15938cafb3
8 changed files with 143 additions and 4 deletions

View File

@ -767,6 +767,8 @@ void NymeaCore::onDeviceDisappeared(const DeviceId &deviceId)
void NymeaCore::deviceManagerLoaded()
{
m_ruleEngine->initRuleStates();
// Do some houskeeping...
qCDebug(dcApplication()) << "Starting housekeeping...";
QDateTime startTime = QDateTime::currentDateTime();

View File

@ -396,7 +396,6 @@ RuleEngine::RuleEngine(QObject *parent) :
rule.setExitActions(exitActions);
rule.setEnabled(enabled);
rule.setExecutable(executable);
rule.setStatesActive(rule.stateEvaluator().evaluate());
appendRule(rule);
settings.endGroup();
}
@ -467,7 +466,7 @@ QList<Rule> RuleEngine::evaluateEvent(const Event &event)
qCDebug(dcRuleEngine).nospace().noquote() << "Rule " << rule.name() << " (" + rule.id().toString() << ") contains event" << event.eventId() << "and all states match.";
rules.append(rule);
} else {
qCDebug(dcRuleEngine).nospace().noquote() << "Rule " << rule.name() << " (" + rule.id().toString() << ") contains event" << event.eventId() << "but state are not matching.";
qCDebug(dcRuleEngine).nospace().noquote() << "Rule " << rule.name() << " (" + rule.id().toString() << ") contains event" << event.eventId() << "and states are not matching.";
rules.append(rule);
}
}
@ -1515,4 +1514,11 @@ void RuleEngine::saveRule(const Rule &rule)
qCDebug(dcRuleEngineDebug()) << "Saved rule to config:" << rule;
}
void RuleEngine::initRuleStates()
{
foreach (const RuleId &ruleId, m_rules.keys()) {
m_rules[ruleId].setStatesActive(m_rules[ruleId].stateEvaluator().evaluate());
}
}
}

View File

@ -68,8 +68,9 @@ public:
RemovePolicyUpdate
};
explicit RuleEngine(QObject *parent = 0);
explicit RuleEngine(QObject *parent = nullptr);
~RuleEngine();
void initRuleStates();
QList<Rule> evaluateEvent(const Event &event);
QList<Rule> evaluateTime(const QDateTime &dateTime);

View File

@ -802,7 +802,7 @@ Device *DeviceManager::findConfiguredDevice(const DeviceId &id) const
return device;
}
}
return 0;
return nullptr;
}
/*! Returns all configured \{Device}{Devices} in the system. */

View File

@ -233,7 +233,13 @@ DeviceManager::DeviceError DevicePluginMock::executeAction(Device *device, const
if (action.actionTypeId() == mockMockFailingActionTypeId)
return DeviceManager::DeviceErrorSetupFailed;
if (action.actionTypeId() == mockPowerActionTypeId) {
qCDebug(dcMockDevice()) << "Setting power to" << action.param(mockPowerActionPowerParamTypeId).value().toBool();
device->setStateValue(mockPowerStateTypeId, action.param(mockPowerActionPowerParamTypeId).value().toBool());
return DeviceManager::DeviceErrorNoError;
}
m_daemons.value(device)->actionExecuted(action.actionTypeId());
return DeviceManager::DeviceErrorNoError;
} else if (device->deviceClassId() == mockDeviceAutoDeviceClassId) {
if (action.actionTypeId() == mockDeviceAutoMockActionAsyncActionTypeId || action.actionTypeId() == mockDeviceAutoMockActionAsyncBrokenActionTypeId) {

View File

@ -44,6 +44,7 @@ StateTypeId mockIntStateId = StateTypeId("80baec19-54de-4948-ac46-31eabfaceb83")
StateTypeId mockBoolStateId = StateTypeId("9dd6a97c-dfd1-43dc-acbd-367932742310");
StateTypeId mockDoubleStateId = StateTypeId("7cac53ee-7048-4dc9-b000-7b585390f34c");
StateTypeId mockBatteryCriticalStateId = StateTypeId("580bc611-1a55-41f3-996f-8d3ccf543db3");
StateTypeId mockPowerStateTypeId = StateTypeId("064aed0d-da4c-49d4-b236-60f97e98ff84");
ActionTypeId mockActionIdPower = ActionTypeId("064aed0d-da4c-49d4-b236-60f97e98ff84");
ActionTypeId mockActionIdWithParams = ActionTypeId("dea0f4e1-65e3-4981-8eaa-2701c53a9185");
ActionTypeId mockActionIdNoParams = ActionTypeId("defd3ed6-1a0d-400b-8879-a0202cf39935");

View File

@ -51,6 +51,7 @@ extern StateTypeId mockIntStateId;
extern StateTypeId mockDoubleStateId;
extern StateTypeId mockBatteryCriticalStateId;
extern StateTypeId mockBoolStateId;
extern StateTypeId mockPowerStateTypeId;
// ParamTypes from mock devices
extern ParamTypeId configParamIntParamTypeId;

View File

@ -46,8 +46,12 @@ private:
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();
@ -101,6 +105,8 @@ private slots:
void testRuleActionPAramsFromEventParameter_data();
void testRuleActionPAramsFromEventParameter();
void testInitStatesActive();
void testInterfaceBasedEventRule();
void testInterfaceBasedStateRule();
@ -349,6 +355,27 @@ QVariant TestRules::validIntStateBasedRule(const QString &name, const bool &exec
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\nRuleEngine.debug=true\nRuleEngineDebug.debug=true\nMockDevice.*=true");
}
void TestRules::addRemoveRules_data()
{
// RuleAction
@ -2525,6 +2552,101 @@ void TestRules::testRuleActionPAramsFromEventParameter()
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", mockEvent1Id);
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", mockActionIdPower);
action.insert("deviceId", m_mockDeviceId);
QVariantList actionParams;
QVariantMap actionParam;
actionParam.insert("paramTypeId", mockActionIdPower);
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", mockActionIdPower);
exitAction.insert("deviceId", m_mockDeviceId);
QVariantList exitActionParams;
QVariantMap exitActionParam;
exitActionParam.insert("paramTypeId", mockActionIdPower);
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(mockEvent1Id);
// 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(mockEvent1Id);
// ... 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(mockEvent1Id);
// 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;