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