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