1 #include "dbus_environment.hpp" 2 #include "helpers.hpp" 3 #include "mocks/json_storage_mock.hpp" 4 #include "mocks/trigger_factory_mock.hpp" 5 #include "mocks/trigger_mock.hpp" 6 #include "params/trigger_params.hpp" 7 #include "trigger.hpp" 8 #include "trigger_manager.hpp" 9 #include "utils/conversion_trigger.hpp" 10 #include "utils/transform.hpp" 11 12 using namespace testing; 13 using sdbusplus::message::object_path; 14 15 class TestTriggerManager : public Test 16 { 17 public: 18 std::pair<boost::system::error_code, std::string> 19 addTrigger(const TriggerParams& params) 20 { 21 const auto sensorInfos = 22 utils::fromLabeledSensorsInfo(params.sensors()); 23 24 std::promise<std::pair<boost::system::error_code, std::string>> 25 addTriggerPromise; 26 DbusEnvironment::getBus()->async_method_call( 27 [&addTriggerPromise](boost::system::error_code ec, 28 const std::string& path) { 29 addTriggerPromise.set_value({ec, path}); 30 }, 31 DbusEnvironment::serviceName(), TriggerManager::triggerManagerPath, 32 TriggerManager::triggerManagerIfaceName, "AddTrigger", params.id(), 33 params.name(), 34 utils::transform( 35 params.triggerActions(), 36 [](const auto& action) { return actionToString(action); }), 37 sensorInfos, params.reports(), 38 std::visit(utils::FromLabeledThresholdParamConversion(), 39 params.thresholdParams())); 40 return DbusEnvironment::waitForFuture(addTriggerPromise.get_future()); 41 } 42 43 std::unique_ptr<TriggerManager> makeTriggerManager() 44 { 45 return std::make_unique<TriggerManager>( 46 std::move(triggerFactoryMockPtr), std::move(storageMockPtr), 47 DbusEnvironment::getObjServer()); 48 } 49 50 void SetUp() override 51 { 52 sut = makeTriggerManager(); 53 } 54 55 std::unique_ptr<StorageMock> storageMockPtr = 56 std::make_unique<NiceMock<StorageMock>>(); 57 StorageMock& storageMock = *storageMockPtr; 58 std::unique_ptr<TriggerFactoryMock> triggerFactoryMockPtr = 59 std::make_unique<NiceMock<TriggerFactoryMock>>(); 60 TriggerFactoryMock& triggerFactoryMock = *triggerFactoryMockPtr; 61 std::unique_ptr<TriggerMock> triggerMockPtr = 62 std::make_unique<NiceMock<TriggerMock>>(TriggerParams().id()); 63 TriggerMock& triggerMock = *triggerMockPtr; 64 std::unique_ptr<TriggerManager> sut; 65 MockFunction<void(std::string)> checkPoint; 66 }; 67 68 TEST_F(TestTriggerManager, addTrigger) 69 { 70 triggerFactoryMock.expectMake(TriggerParams(), Ref(*sut), Ref(storageMock)) 71 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 72 73 auto [ec, path] = addTrigger(TriggerParams()); 74 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 75 EXPECT_THAT(path, Eq(triggerMock.getPath())); 76 } 77 78 TEST_F(TestTriggerManager, addTriggerWithDiscreteThresholds) 79 { 80 TriggerParams triggerParamsDiscrete; 81 auto thresholds = std::vector<discrete::LabeledThresholdParam>{ 82 {"discrete_threshold1", discrete::Severity::ok, 10, "11.0"}, 83 {"discrete_threshold2", discrete::Severity::warning, 10, "12.0"}, 84 {"discrete_threshold3", discrete::Severity::critical, 10, "13.0"}}; 85 86 triggerParamsDiscrete.thresholdParams(thresholds); 87 88 auto [ec, path] = addTrigger(triggerParamsDiscrete); 89 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 90 EXPECT_THAT(path, Eq(triggerMock.getPath())); 91 } 92 93 TEST_F(TestTriggerManager, addDiscreteTriggerWithoutThresholds) 94 { 95 TriggerParams triggerParamsDiscrete; 96 auto thresholds = std::vector<discrete::LabeledThresholdParam>(); 97 98 triggerParamsDiscrete.thresholdParams(thresholds); 99 100 auto [ec, path] = addTrigger(triggerParamsDiscrete); 101 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 102 EXPECT_THAT(path, Eq(triggerMock.getPath())); 103 } 104 105 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerTwice) 106 { 107 triggerFactoryMock.expectMake(TriggerParams(), Ref(*sut), Ref(storageMock)) 108 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 109 110 addTrigger(TriggerParams()); 111 112 auto [ec, path] = addTrigger(TriggerParams()); 113 EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists)); 114 EXPECT_THAT(path, Eq(std::string())); 115 } 116 117 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithInvalidId) 118 { 119 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 120 .Times(0); 121 122 auto [ec, path] = addTrigger(TriggerParams().id("not valid?")); 123 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 124 EXPECT_THAT(path, Eq(std::string())); 125 } 126 127 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithDuplicatesInReportsIds) 128 { 129 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 130 .Times(0); 131 132 auto [ec, path] = addTrigger( 133 TriggerParams().reportIds({"trigger1", "trigger2", "trigger1"})); 134 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 135 EXPECT_THAT(path, Eq(std::string())); 136 } 137 138 TEST_F(TestTriggerManager, addTriggerWithProperReportPaths) 139 { 140 auto [ec, path] = addTrigger(TriggerParams().reports( 141 {object_path("/xyz/openbmc_project/Telemetry/Reports/MyReport"), 142 object_path( 143 "/xyz/openbmc_project/Telemetry/Reports/MyPrefix/MyReport")})); 144 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 145 EXPECT_THAT(path, Eq(triggerMock.getPath())); 146 } 147 148 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithBadReportsPath) 149 { 150 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 151 .Times(0); 152 153 auto [ec, path] = addTrigger(TriggerParams().reports( 154 {object_path("/xyz/openbmc_project/Telemetry/NotReports/MyReport")})); 155 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 156 EXPECT_THAT(path, Eq(std::string())); 157 } 158 159 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithTooManyReportPrefixes) 160 { 161 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 162 .Times(0); 163 164 auto [ec, path] = addTrigger(TriggerParams().reports({object_path( 165 "/xyz/openbmc_project/Telemetry/Reports/P1/P2/MyReport")})); 166 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 167 EXPECT_THAT(path, Eq(std::string())); 168 } 169 170 TEST_F(TestTriggerManager, addTriggerWithoutIdAndName) 171 { 172 triggerFactoryMock 173 .expectMake(TriggerParams() 174 .id(TriggerManager::triggerNameDefault) 175 .name(TriggerManager::triggerNameDefault), 176 Ref(*sut), Ref(storageMock)) 177 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 178 179 auto [ec, path] = addTrigger(TriggerParams().id("").name("")); 180 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 181 EXPECT_THAT(path, Not(Eq(""))); 182 } 183 184 TEST_F(TestTriggerManager, addTriggerWithPrefixId) 185 { 186 triggerFactoryMock 187 .expectMake(TriggerParams() 188 .id("TelemetryService/HackyName") 189 .name("Hacky/Name!@#$"), 190 Ref(*sut), Ref(storageMock)) 191 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 192 193 auto [ec, path] = addTrigger( 194 TriggerParams().id("TelemetryService/").name("Hacky/Name!@#$")); 195 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 196 EXPECT_THAT(path, Not(Eq(""))); 197 } 198 199 TEST_F(TestTriggerManager, addTriggerWithoutIdTwice) 200 { 201 addTrigger(TriggerParams().id("")); 202 203 auto [ec, path] = addTrigger(TriggerParams().id("")); 204 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 205 EXPECT_THAT(path, Not(Eq(""))); 206 } 207 208 TEST_F(TestTriggerManager, addTriggerWithoutIdAndWithLongNameTwice) 209 { 210 addTrigger(TriggerParams().id("").name( 211 std::string(2 * TriggerManager::maxTriggerIdLength, 'z'))); 212 213 auto [ec, path] = addTrigger(TriggerParams().id("").name( 214 std::string(2 * TriggerManager::maxTriggerIdLength, 'z'))); 215 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 216 EXPECT_THAT(path, Not(Eq(""))); 217 } 218 219 TEST_F(TestTriggerManager, addTriggerWithMaxLengthId) 220 { 221 auto triggerId = std::string(TriggerManager::maxTriggerIdLength, 'z'); 222 auto triggerParams = TriggerParams().id(triggerId); 223 224 triggerFactoryMock.expectMake(triggerParams, Ref(*sut), Ref(storageMock)) 225 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 226 227 auto [ec, path] = addTrigger(triggerParams); 228 229 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 230 EXPECT_THAT(path, Eq(triggerMock.getPath())); 231 } 232 233 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithTooLongId) 234 { 235 auto triggerId = std::string(TriggerManager::maxTriggerIdLength + 1, 'z'); 236 auto triggerParams = TriggerParams().id(triggerId); 237 238 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 239 .Times(0); 240 241 auto [ec, path] = addTrigger(triggerParams); 242 243 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 244 EXPECT_THAT(path, Eq(std::string())); 245 } 246 247 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWhenMaxTriggerIsReached) 248 { 249 auto triggerParams = TriggerParams(); 250 251 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 252 .Times(TriggerManager::maxTriggers); 253 254 for (size_t i = 0; i < TriggerManager::maxTriggers; i++) 255 { 256 triggerParams.id(TriggerParams().id() + std::to_string(i)); 257 258 auto [ec, path] = addTrigger(triggerParams); 259 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 260 } 261 262 triggerParams.id(TriggerParams().id() + 263 std::to_string(TriggerManager::maxTriggers)); 264 auto [ec, path] = addTrigger(triggerParams); 265 EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open)); 266 EXPECT_THAT(path, Eq(std::string())); 267 } 268 269 TEST_F(TestTriggerManager, removeTrigger) 270 { 271 { 272 InSequence seq; 273 triggerFactoryMock 274 .expectMake(TriggerParams(), Ref(*sut), Ref(storageMock)) 275 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 276 EXPECT_CALL(triggerMock, Die()); 277 EXPECT_CALL(checkPoint, Call("end")); 278 } 279 280 addTrigger(TriggerParams()); 281 sut->removeTrigger(&triggerMock); 282 checkPoint.Call("end"); 283 } 284 285 TEST_F(TestTriggerManager, removingTriggerThatIsNotInContainerHasNoEffect) 286 { 287 { 288 InSequence seq; 289 EXPECT_CALL(checkPoint, Call("end")); 290 EXPECT_CALL(triggerMock, Die()); 291 } 292 293 sut->removeTrigger(&triggerMock); 294 checkPoint.Call("end"); 295 } 296 297 TEST_F(TestTriggerManager, removingSameTriggerTwiceHasNoSideEffect) 298 { 299 { 300 InSequence seq; 301 triggerFactoryMock 302 .expectMake(TriggerParams(), Ref(*sut), Ref(storageMock)) 303 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 304 EXPECT_CALL(triggerMock, Die()); 305 EXPECT_CALL(checkPoint, Call("end")); 306 } 307 308 addTrigger(TriggerParams()); 309 sut->removeTrigger(&triggerMock); 310 sut->removeTrigger(&triggerMock); 311 checkPoint.Call("end"); 312 } 313 class TestTriggerManagerStorage : public TestTriggerManager 314 { 315 public: 316 using FilePath = interfaces::JsonStorage::FilePath; 317 using DirectoryPath = interfaces::JsonStorage::DirectoryPath; 318 319 void SetUp() override 320 { 321 ON_CALL(storageMock, list()) 322 .WillByDefault(Return(std::vector<FilePath>{ 323 {FilePath("trigger1")}, {FilePath("trigger2")}})); 324 325 ON_CALL(storageMock, load(FilePath("trigger1"))) 326 .WillByDefault(InvokeWithoutArgs([this] { return data1; })); 327 328 data2["Id"] = "Trigger2"; 329 data2["Name"] = "Second Trigger"; 330 ON_CALL(storageMock, load(FilePath("trigger2"))) 331 .WillByDefault(InvokeWithoutArgs([this] { return data2; })); 332 } 333 334 nlohmann::json data1 = nlohmann::json{ 335 {"Version", Trigger::triggerVersion}, 336 {"Id", TriggerParams().id()}, 337 {"Name", TriggerParams().name()}, 338 {"ThresholdParamsDiscriminator", 339 TriggerParams().thresholdParams().index()}, 340 {"TriggerActions", utils::transform(TriggerParams().triggerActions(), 341 [](const auto& action) { 342 return actionToString(action); 343 })}, 344 {"ThresholdParams", utils::labeledThresholdParamsToJson( 345 TriggerParams().thresholdParams())}, 346 {"ReportIds", TriggerParams().reportIds()}, 347 {"Sensors", TriggerParams().sensors()}}; 348 349 nlohmann::json data2 = data1; 350 }; 351 352 TEST_F(TestTriggerManagerStorage, triggerManagerCtorAddTriggerFromStorage) 353 { 354 triggerFactoryMock.expectMake(TriggerParams(), _, Ref(storageMock)); 355 triggerFactoryMock.expectMake( 356 TriggerParams().id("Trigger2").name("Second Trigger"), _, 357 Ref(storageMock)); 358 EXPECT_CALL(storageMock, remove(_)).Times(0); 359 360 sut = makeTriggerManager(); 361 } 362 363 TEST_F(TestTriggerManagerStorage, 364 triggerManagerCtorRemoveDiscreteTriggerFromStorage) 365 { 366 LabeledTriggerThresholdParams thresholdParams = 367 std::vector<discrete::LabeledThresholdParam>{ 368 {"userId1", discrete::Severity::warning, 15, "10.0"}, 369 {"userId2", discrete::Severity::critical, 5, "20.0"}}; 370 371 data1["ThresholdParamsDiscriminator"] = thresholdParams.index(); 372 373 data1["ThresholdParams"] = 374 utils::labeledThresholdParamsToJson(thresholdParams); 375 376 EXPECT_CALL(storageMock, remove(FilePath("trigger1"))).Times(0); 377 378 sut = makeTriggerManager(); 379 } 380 381 TEST_F(TestTriggerManagerStorage, 382 triggerManagerCtorRemoveDiscreteTriggerFromStorage2) 383 { 384 data1["IsDiscrete"] = true; 385 386 EXPECT_CALL(storageMock, remove(FilePath("trigger1"))).Times(0); 387 388 sut = makeTriggerManager(); 389 } 390 391 TEST_F(TestTriggerManagerStorage, 392 triggerManagerCtorAddProperRemoveInvalidTriggerFromStorage) 393 { 394 data1["Version"] = Trigger::triggerVersion - 1; 395 396 triggerFactoryMock.expectMake( 397 TriggerParams().id("Trigger2").name("Second Trigger"), _, 398 Ref(storageMock)); 399 EXPECT_CALL(storageMock, remove(FilePath("trigger1"))); 400 401 sut = makeTriggerManager(); 402 } 403