1 #include "dbus_environment.hpp" 2 #include "helpers.hpp" 3 #include "mocks/json_storage_mock.hpp" 4 #include "mocks/report_manager_mock.hpp" 5 #include "mocks/sensor_mock.hpp" 6 #include "mocks/threshold_mock.hpp" 7 #include "mocks/trigger_factory_mock.hpp" 8 #include "mocks/trigger_manager_mock.hpp" 9 #include "params/trigger_params.hpp" 10 #include "trigger.hpp" 11 #include "trigger_manager.hpp" 12 #include "utils/conversion_trigger.hpp" 13 #include "utils/transform.hpp" 14 #include "utils/tstring.hpp" 15 16 #include <boost/range/combine.hpp> 17 18 using namespace testing; 19 using namespace std::literals::string_literals; 20 21 static constexpr size_t expectedTriggerVersion = 1; 22 23 class TestTrigger : public Test 24 { 25 public: 26 TriggerParams triggerParams; 27 TriggerParams triggerDiscreteParams = 28 TriggerParams() 29 .id("DiscreteTrigger") 30 .name("My Discrete Trigger") 31 .thresholdParams(std::vector<discrete::LabeledThresholdParam>{ 32 discrete::LabeledThresholdParam{ 33 "userId", discrete::Severity::warning, 34 Milliseconds(10).count(), "15.2"}, 35 discrete::LabeledThresholdParam{ 36 "userId_2", discrete::Severity::critical, 37 Milliseconds(5).count(), "32.7"}, 38 }); 39 40 std::unique_ptr<ReportManagerMock> reportManagerMockPtr = 41 std::make_unique<NiceMock<ReportManagerMock>>(); 42 std::unique_ptr<TriggerManagerMock> triggerManagerMockPtr = 43 std::make_unique<NiceMock<TriggerManagerMock>>(); 44 std::unique_ptr<TriggerFactoryMock> triggerFactoryMockPtr = 45 std::make_unique<NiceMock<TriggerFactoryMock>>(); 46 testing::NiceMock<StorageMock> storageMock; 47 std::vector<std::shared_ptr<interfaces::Threshold>> thresholdMocks; 48 std::unique_ptr<Trigger> sut; 49 50 void SetUp() override 51 { 52 sut = makeTrigger(triggerParams); 53 } 54 55 static std::vector<LabeledSensorInfo> 56 convertToLabeledSensor(const SensorsInfo& sensorsInfo) 57 { 58 return utils::transform(sensorsInfo, [](const auto& sensorInfo) { 59 const auto& [sensorPath, sensorMetadata] = sensorInfo; 60 return LabeledSensorInfo("service1", sensorPath, sensorMetadata); 61 }); 62 } 63 64 std::unique_ptr<Trigger> makeTrigger(const TriggerParams& params) 65 { 66 thresholdMocks = 67 ThresholdMock::makeThresholds(params.thresholdParams()); 68 69 return std::make_unique<Trigger>( 70 DbusEnvironment::getIoc(), DbusEnvironment::getObjServer(), 71 params.id(), params.name(), params.triggerActions(), 72 std::make_shared<std::vector<std::string>>( 73 params.reportIds().begin(), params.reportIds().end()), 74 std::vector<std::shared_ptr<interfaces::Threshold>>(thresholdMocks), 75 *triggerManagerMockPtr, storageMock, *triggerFactoryMockPtr, 76 SensorMock::makeSensorMocks(params.sensors()), 77 *reportManagerMockPtr); 78 } 79 80 static interfaces::JsonStorage::FilePath to_file_path(std::string name) 81 { 82 return interfaces::JsonStorage::FilePath( 83 std::to_string(std::hash<std::string>{}(name))); 84 } 85 86 template <class T> 87 static T getProperty(const std::string& path, const std::string& property) 88 { 89 return DbusEnvironment::getProperty<T>(path, Trigger::triggerIfaceName, 90 property); 91 } 92 93 template <class T> 94 static boost::system::error_code setProperty(const std::string& path, 95 const std::string& property, 96 const T& newValue) 97 { 98 return DbusEnvironment::setProperty<T>(path, Trigger::triggerIfaceName, 99 property, newValue); 100 } 101 102 boost::system::error_code deleteTrigger(const std::string& path) 103 { 104 std::promise<boost::system::error_code> methodPromise; 105 DbusEnvironment::getBus()->async_method_call( 106 [&methodPromise](boost::system::error_code ec) { 107 methodPromise.set_value(ec); 108 }, 109 DbusEnvironment::serviceName(), path, Trigger::deleteIfaceName, 110 "Delete"); 111 return DbusEnvironment::waitForFuture(methodPromise.get_future()); 112 } 113 }; 114 115 TEST_F(TestTrigger, checkIfPropertiesAreSet) 116 { 117 EXPECT_THAT(getProperty<std::string>(sut->getPath(), "Name"), 118 Eq(triggerParams.name())); 119 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistent"), Eq(true)); 120 EXPECT_THAT( 121 getProperty<std::vector<std::string>>(sut->getPath(), "TriggerActions"), 122 Eq(utils::transform( 123 triggerParams.triggerActions(), 124 [](const auto& action) { return actionToString(action); }))); 125 EXPECT_THAT((getProperty<SensorsInfo>(sut->getPath(), "Sensors")), 126 Eq(utils::fromLabeledSensorsInfo(triggerParams.sensors()))); 127 EXPECT_THAT( 128 getProperty<std::vector<std::string>>(sut->getPath(), "ReportNames"), 129 Eq(triggerParams.reportIds())); 130 EXPECT_THAT( 131 getProperty<bool>(sut->getPath(), "Discrete"), 132 Eq(isTriggerThresholdDiscrete(triggerParams.thresholdParams()))); 133 EXPECT_THAT( 134 getProperty<TriggerThresholdParams>(sut->getPath(), "Thresholds"), 135 Eq(std::visit(utils::FromLabeledThresholdParamConversion(), 136 triggerParams.thresholdParams()))); 137 } 138 139 TEST_F(TestTrigger, checkBasicGetters) 140 { 141 EXPECT_THAT(sut->getId(), Eq(triggerParams.id())); 142 EXPECT_THAT(sut->getPath(), Eq(Trigger::triggerDir + triggerParams.id())); 143 EXPECT_THAT(sut->getReportIds(), Eq(triggerParams.reportIds())); 144 } 145 146 TEST_F(TestTrigger, setPropertyNameToCorrectValue) 147 { 148 std::string name = "custom name 1234 %^#5"; 149 EXPECT_THAT(setProperty(sut->getPath(), "Name", name), 150 Eq(boost::system::errc::success)); 151 EXPECT_THAT(getProperty<std::string>(sut->getPath(), "Name"), Eq(name)); 152 } 153 154 TEST_F(TestTrigger, setPropertyReportNames) 155 { 156 std::vector<std::string> newNames = {"abc", "one", "two"}; 157 EXPECT_THAT(setProperty(sut->getPath(), "ReportNames", newNames), 158 Eq(boost::system::errc::success)); 159 EXPECT_THAT( 160 getProperty<std::vector<std::string>>(sut->getPath(), "ReportNames"), 161 Eq(newNames)); 162 EXPECT_THAT( 163 getProperty<std::vector<std::string>>(sut->getPath(), "ReportNames"), 164 Eq(sut->getReportIds())); 165 } 166 167 TEST_F(TestTrigger, settingPropertyReportNamesUptadesTriggerIdsInReports) 168 { 169 std::vector<std::string> newPropertyVal = {"abc", "one", "two"}; 170 171 for (const auto& reportId : newPropertyVal) 172 { 173 EXPECT_CALL( 174 *reportManagerMockPtr, 175 updateTriggerIds(reportId, sut->getId(), TriggerIdUpdate::Add)); 176 } 177 for (const auto& reportId : triggerParams.reportIds()) 178 { 179 EXPECT_CALL( 180 *reportManagerMockPtr, 181 updateTriggerIds(reportId, sut->getId(), TriggerIdUpdate::Remove)); 182 } 183 184 EXPECT_THAT(setProperty(sut->getPath(), "ReportNames", newPropertyVal), 185 Eq(boost::system::errc::success)); 186 } 187 188 TEST_F(TestTrigger, settingPropertyReportNamesWillNotRemoveTriggerIdsInReports) 189 { 190 std::vector<std::string> newPropertyVal = triggerParams.reportIds(); 191 std::vector<std::string> newNames{"abc", "one", "two"}; 192 newPropertyVal.insert(newPropertyVal.end(), newNames.begin(), 193 newNames.end()); 194 195 for (const auto& reportId : newNames) 196 { 197 EXPECT_CALL( 198 *reportManagerMockPtr, 199 updateTriggerIds(reportId, sut->getId(), TriggerIdUpdate::Add)); 200 } 201 202 EXPECT_THAT(setProperty(sut->getPath(), "ReportNames", newPropertyVal), 203 Eq(boost::system::errc::success)); 204 } 205 206 TEST_F(TestTrigger, 207 settingPropertyReportNamesToSameValueWillNotUpdateTriggerIdsInReports) 208 { 209 std::vector<std::string> newPropertyVal = triggerParams.reportIds(); 210 211 EXPECT_CALL(*reportManagerMockPtr, updateTriggerIds(_, _, _)).Times(0); 212 213 EXPECT_THAT(setProperty(sut->getPath(), "ReportNames", newPropertyVal), 214 Eq(boost::system::errc::success)); 215 } 216 217 TEST_F(TestTrigger, 218 DISABLED_settingPropertyReportNamesThrowsExceptionWhenDuplicateReportIds) 219 { 220 std::vector<std::string> newPropertyVal{"trigger1", "trigger2", "trigger1"}; 221 222 EXPECT_CALL(*reportManagerMockPtr, updateTriggerIds(_, _, _)).Times(0); 223 224 EXPECT_THAT(setProperty(sut->getPath(), "ReportNames", newPropertyVal), 225 Eq(boost::system::errc::invalid_argument)); 226 } 227 228 TEST_F(TestTrigger, setPropertySensors) 229 { 230 EXPECT_CALL(*triggerFactoryMockPtr, updateSensors(_, _)); 231 for (const auto& threshold : thresholdMocks) 232 { 233 auto thresholdMockPtr = 234 std::dynamic_pointer_cast<NiceMock<ThresholdMock>>(threshold); 235 EXPECT_CALL(*thresholdMockPtr, updateSensors(_)); 236 } 237 SensorsInfo newSensors({std::make_pair( 238 sdbusplus::message::object_path("/abc/def"), "metadata")}); 239 EXPECT_THAT(setProperty(sut->getPath(), "Sensors", newSensors), 240 Eq(boost::system::errc::success)); 241 } 242 243 TEST_F(TestTrigger, setPropertyThresholds) 244 { 245 EXPECT_CALL(*triggerFactoryMockPtr, updateThresholds(_, _, _, _, _)); 246 TriggerThresholdParams newThresholds = 247 std::vector<discrete::ThresholdParam>( 248 {std::make_tuple("discrete threshold", "OK", 10, "12.3")}); 249 EXPECT_THAT(setProperty(sut->getPath(), "Thresholds", newThresholds), 250 Eq(boost::system::errc::success)); 251 } 252 253 TEST_F(TestTrigger, checkIfNumericCoversionsAreGood) 254 { 255 const auto& labeledParamsBase = 256 std::get<std::vector<numeric::LabeledThresholdParam>>( 257 triggerParams.thresholdParams()); 258 const auto paramsToCheck = 259 std::visit(utils::FromLabeledThresholdParamConversion(), 260 triggerParams.thresholdParams()); 261 const auto labeledParamsToCheck = 262 std::get<std::vector<numeric::LabeledThresholdParam>>(std::visit( 263 utils::ToLabeledThresholdParamConversion(), paramsToCheck)); 264 265 for (const auto& [tocheck, base] : 266 boost::combine(labeledParamsToCheck, labeledParamsBase)) 267 { 268 EXPECT_THAT(tocheck.at_label<utils::tstring::Type>(), 269 Eq(base.at_label<utils::tstring::Type>())); 270 EXPECT_THAT(tocheck.at_label<utils::tstring::Direction>(), 271 Eq(base.at_label<utils::tstring::Direction>())); 272 EXPECT_THAT(tocheck.at_label<utils::tstring::DwellTime>(), 273 Eq(base.at_label<utils::tstring::DwellTime>())); 274 EXPECT_THAT(tocheck.at_label<utils::tstring::ThresholdValue>(), 275 Eq(base.at_label<utils::tstring::ThresholdValue>())); 276 } 277 } 278 279 TEST_F(TestTrigger, checkIfDiscreteCoversionsAreGood) 280 { 281 const auto& labeledParamsBase = 282 std::get<std::vector<discrete::LabeledThresholdParam>>( 283 triggerDiscreteParams.thresholdParams()); 284 const auto paramsToCheck = 285 std::visit(utils::FromLabeledThresholdParamConversion(), 286 triggerDiscreteParams.thresholdParams()); 287 const auto labeledParamsToCheck = 288 std::get<std::vector<discrete::LabeledThresholdParam>>(std::visit( 289 utils::ToLabeledThresholdParamConversion(), paramsToCheck)); 290 291 for (const auto& [tocheck, base] : 292 boost::combine(labeledParamsToCheck, labeledParamsBase)) 293 { 294 EXPECT_THAT(tocheck.at_label<utils::tstring::UserId>(), 295 Eq(base.at_label<utils::tstring::UserId>())); 296 EXPECT_THAT(tocheck.at_label<utils::tstring::Severity>(), 297 Eq(base.at_label<utils::tstring::Severity>())); 298 EXPECT_THAT(tocheck.at_label<utils::tstring::DwellTime>(), 299 Eq(base.at_label<utils::tstring::DwellTime>())); 300 EXPECT_THAT(tocheck.at_label<utils::tstring::ThresholdValue>(), 301 Eq(base.at_label<utils::tstring::ThresholdValue>())); 302 } 303 } 304 305 TEST_F(TestTrigger, deleteTrigger) 306 { 307 EXPECT_CALL(storageMock, remove(to_file_path(sut->getId()))); 308 EXPECT_CALL(*triggerManagerMockPtr, removeTrigger(sut.get())); 309 for (const auto& reportId : triggerParams.reportIds()) 310 { 311 EXPECT_CALL( 312 *reportManagerMockPtr, 313 updateTriggerIds(reportId, sut->getId(), TriggerIdUpdate::Remove)); 314 } 315 auto ec = deleteTrigger(sut->getPath()); 316 EXPECT_THAT(ec, Eq(boost::system::errc::success)); 317 } 318 319 TEST_F(TestTrigger, deletingNonExistingTriggerReturnInvalidRequestDescriptor) 320 { 321 auto ec = deleteTrigger(Trigger::triggerDir + "NonExisting"s); 322 EXPECT_THAT(ec.value(), Eq(EBADR)); 323 } 324 325 TEST_F(TestTrigger, settingPersistencyToFalseRemovesTriggerFromStorage) 326 { 327 EXPECT_CALL(storageMock, remove(to_file_path(sut->getId()))); 328 329 bool persistent = false; 330 EXPECT_THAT(setProperty(sut->getPath(), "Persistent", persistent), 331 Eq(boost::system::errc::success)); 332 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistent"), 333 Eq(persistent)); 334 } 335 336 class TestTriggerInitialization : public TestTrigger 337 { 338 public: 339 void SetUp() override 340 {} 341 342 nlohmann::json storedConfiguration; 343 }; 344 345 TEST_F(TestTriggerInitialization, 346 exceptionDuringTriggerStoreDisablesPersistency) 347 { 348 EXPECT_CALL(storageMock, store(_, _)) 349 .WillOnce(Throw(std::runtime_error("Generic error!"))); 350 351 sut = makeTrigger(triggerParams); 352 353 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistent"), Eq(false)); 354 } 355 356 TEST_F(TestTriggerInitialization, creatingTriggerThrowsExceptionWhenIdIsInvalid) 357 { 358 EXPECT_CALL(storageMock, store(_, _)).Times(0); 359 360 EXPECT_THROW(makeTrigger(triggerParams.id("inv?lidId")), 361 sdbusplus::exception::SdBusError); 362 } 363 364 TEST_F(TestTriggerInitialization, creatingTriggerUpdatesTriggersIdsInReports) 365 { 366 for (const auto& reportId : triggerParams.reportIds()) 367 { 368 EXPECT_CALL(*reportManagerMockPtr, 369 updateTriggerIds(reportId, triggerParams.id(), 370 TriggerIdUpdate::Add)); 371 } 372 373 sut = makeTrigger(triggerParams); 374 } 375 376 class TestTriggerStore : public TestTrigger 377 { 378 public: 379 nlohmann::json storedConfiguration; 380 nlohmann::json storedDiscreteConfiguration; 381 std::unique_ptr<Trigger> sutDiscrete; 382 383 void SetUp() override 384 { 385 ON_CALL(storageMock, store(_, _)) 386 .WillByDefault(SaveArg<1>(&storedConfiguration)); 387 sut = makeTrigger(triggerParams); 388 389 ON_CALL(storageMock, store(_, _)) 390 .WillByDefault(SaveArg<1>(&storedDiscreteConfiguration)); 391 sutDiscrete = makeTrigger(triggerDiscreteParams); 392 } 393 }; 394 395 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerVersion) 396 { 397 ASSERT_THAT(storedConfiguration.at("Version"), Eq(expectedTriggerVersion)); 398 } 399 400 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerId) 401 { 402 ASSERT_THAT(storedConfiguration.at("Id"), Eq(triggerParams.id())); 403 } 404 405 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerName) 406 { 407 ASSERT_THAT(storedConfiguration.at("Name"), Eq(triggerParams.name())); 408 } 409 410 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerTriggerActions) 411 { 412 ASSERT_THAT(storedConfiguration.at("TriggerActions"), 413 Eq(utils::transform(triggerParams.triggerActions(), 414 [](const auto& action) { 415 return actionToString(action); 416 }))); 417 } 418 419 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerReportIds) 420 { 421 ASSERT_THAT(storedConfiguration.at("ReportIds"), 422 Eq(triggerParams.reportIds())); 423 } 424 425 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerSensors) 426 { 427 nlohmann::json expectedItem; 428 expectedItem["service"] = "service1"; 429 expectedItem["path"] = "/xyz/openbmc_project/sensors/temperature/BMC_Temp"; 430 expectedItem["metadata"] = "metadata1"; 431 432 ASSERT_THAT(storedConfiguration.at("Sensors"), ElementsAre(expectedItem)); 433 } 434 435 TEST_F(TestTriggerStore, settingPersistencyToTrueStoresTriggerThresholdParams) 436 { 437 nlohmann::json expectedItem0; 438 expectedItem0["type"] = 0; 439 expectedItem0["dwellTime"] = 10; 440 expectedItem0["direction"] = 1; 441 expectedItem0["thresholdValue"] = 0.5; 442 443 nlohmann::json expectedItem1; 444 expectedItem1["type"] = 3; 445 expectedItem1["dwellTime"] = 10; 446 expectedItem1["direction"] = 2; 447 expectedItem1["thresholdValue"] = 90.2; 448 449 ASSERT_THAT(storedConfiguration.at("ThresholdParamsDiscriminator"), Eq(0)); 450 ASSERT_THAT(storedConfiguration.at("ThresholdParams"), 451 ElementsAre(expectedItem0, expectedItem1)); 452 } 453 454 TEST_F(TestTriggerStore, 455 settingPersistencyToTrueStoresDiscreteTriggerThresholdParams) 456 { 457 nlohmann::json expectedItem0; 458 expectedItem0["userId"] = "userId"; 459 expectedItem0["severity"] = discrete::Severity::warning; 460 expectedItem0["dwellTime"] = 10; 461 expectedItem0["thresholdValue"] = "15.2"; 462 463 nlohmann::json expectedItem1; 464 expectedItem1["userId"] = "userId_2"; 465 expectedItem1["severity"] = discrete::Severity::critical; 466 expectedItem1["dwellTime"] = 5; 467 expectedItem1["thresholdValue"] = "32.7"; 468 469 ASSERT_THAT(storedDiscreteConfiguration.at("ThresholdParamsDiscriminator"), 470 Eq(1)); 471 ASSERT_THAT(storedDiscreteConfiguration.at("ThresholdParams"), 472 ElementsAre(expectedItem0, expectedItem1)); 473 } 474