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