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