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