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/dbus_path_utils.hpp" 11 #include "utils/string_utils.hpp" 12 #include "utils/transform.hpp" 13 14 using namespace testing; 15 using sdbusplus::message::object_path; 16 using namespace std::literals::string_literals; 17 18 class TestTriggerManager : public Test 19 { 20 public: 21 TriggerParams triggerParams; 22 std::pair<boost::system::error_code, std::string> 23 addTrigger(const TriggerParams& params) 24 { 25 const auto sensorInfos = 26 utils::fromLabeledSensorsInfo(params.sensors()); 27 28 std::promise<std::pair<boost::system::error_code, std::string>> 29 addTriggerPromise; 30 DbusEnvironment::getBus()->async_method_call( 31 [&addTriggerPromise](boost::system::error_code ec, 32 const std::string& path) { 33 addTriggerPromise.set_value({ec, path}); 34 }, 35 DbusEnvironment::serviceName(), TriggerManager::triggerManagerPath, 36 TriggerManager::triggerManagerIfaceName, "AddTrigger", params.id(), 37 params.name(), 38 utils::transform( 39 params.triggerActions(), 40 [](const auto& action) { return actionToString(action); }), 41 sensorInfos, params.reports(), 42 std::visit(utils::FromLabeledThresholdParamConversion(), 43 params.thresholdParams())); 44 return DbusEnvironment::waitForFuture(addTriggerPromise.get_future()); 45 } 46 47 std::unique_ptr<TriggerManager> makeTriggerManager() 48 { 49 return std::make_unique<TriggerManager>( 50 std::move(triggerFactoryMockPtr), std::move(storageMockPtr), 51 DbusEnvironment::getObjServer()); 52 } 53 54 void SetUp() override 55 { 56 sut = makeTriggerManager(); 57 } 58 59 std::unique_ptr<StorageMock> storageMockPtr = 60 std::make_unique<NiceMock<StorageMock>>(); 61 StorageMock& storageMock = *storageMockPtr; 62 std::unique_ptr<TriggerFactoryMock> triggerFactoryMockPtr = 63 std::make_unique<NiceMock<TriggerFactoryMock>>(); 64 TriggerFactoryMock& triggerFactoryMock = *triggerFactoryMockPtr; 65 std::unique_ptr<TriggerMock> triggerMockPtr = 66 std::make_unique<NiceMock<TriggerMock>>(TriggerParams().id()); 67 TriggerMock& triggerMock = *triggerMockPtr; 68 std::unique_ptr<TriggerManager> sut; 69 MockFunction<void(std::string)> checkPoint; 70 }; 71 72 TEST_F(TestTriggerManager, addTrigger) 73 { 74 triggerFactoryMock.expectMake(TriggerParams(), Ref(*sut), Ref(storageMock)) 75 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 76 77 auto [ec, path] = addTrigger(TriggerParams()); 78 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 79 EXPECT_THAT(path, Eq(triggerMock.getPath())); 80 } 81 82 TEST_F(TestTriggerManager, addTriggerWithDiscreteThresholds) 83 { 84 TriggerParams triggerParamsDiscrete; 85 auto thresholds = std::vector<discrete::LabeledThresholdParam>{ 86 {"discrete_threshold1", discrete::Severity::ok, 10, "11.0"}, 87 {"discrete_threshold2", discrete::Severity::warning, 10, "12.0"}, 88 {"discrete_threshold3", discrete::Severity::critical, 10, "13.0"}}; 89 90 triggerParamsDiscrete.thresholdParams(thresholds); 91 92 auto [ec, path] = addTrigger(triggerParamsDiscrete); 93 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 94 EXPECT_THAT(path, Eq(triggerMock.getPath())); 95 } 96 97 TEST_F(TestTriggerManager, addDiscreteTriggerWithoutThresholds) 98 { 99 TriggerParams triggerParamsDiscrete; 100 auto thresholds = std::vector<discrete::LabeledThresholdParam>(); 101 102 triggerParamsDiscrete.thresholdParams(thresholds); 103 104 auto [ec, path] = addTrigger(triggerParamsDiscrete); 105 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 106 EXPECT_THAT(path, Eq(triggerMock.getPath())); 107 } 108 109 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerTwice) 110 { 111 triggerFactoryMock.expectMake(TriggerParams(), Ref(*sut), Ref(storageMock)) 112 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 113 114 addTrigger(TriggerParams()); 115 116 auto [ec, path] = addTrigger(TriggerParams()); 117 EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists)); 118 EXPECT_THAT(path, Eq(std::string())); 119 } 120 121 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithInvalidId) 122 { 123 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 124 .Times(0); 125 126 auto [ec, path] = addTrigger(TriggerParams().id("not valid?")); 127 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 128 EXPECT_THAT(path, Eq(std::string())); 129 } 130 131 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithDuplicatesInReportsIds) 132 { 133 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 134 .Times(0); 135 136 auto [ec, path] = addTrigger( 137 TriggerParams().reportIds({"trigger1", "trigger2", "trigger1"})); 138 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 139 EXPECT_THAT(path, Eq(std::string())); 140 } 141 142 TEST_F(TestTriggerManager, addTriggerWithProperReportPaths) 143 { 144 auto [ec, path] = addTrigger(TriggerParams().reports( 145 {object_path("/xyz/openbmc_project/Telemetry/Reports/MyReport"), 146 object_path( 147 "/xyz/openbmc_project/Telemetry/Reports/MyPrefix/MyReport")})); 148 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 149 EXPECT_THAT(path, Eq(triggerMock.getPath())); 150 } 151 152 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithBadReportsPath) 153 { 154 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 155 .Times(0); 156 157 auto [ec, path] = addTrigger(TriggerParams().reports( 158 {object_path("/xyz/openbmc_project/Telemetry/NotReports/MyReport")})); 159 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 160 EXPECT_THAT(path, Eq(std::string())); 161 } 162 163 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithTooManyReportPrefixes) 164 { 165 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 166 .Times(0); 167 168 auto [ec, path] = addTrigger(TriggerParams().reports({object_path( 169 "/xyz/openbmc_project/Telemetry/Reports/P1/P2/MyReport")})); 170 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 171 EXPECT_THAT(path, Eq(std::string())); 172 } 173 174 TEST_F(TestTriggerManager, addTriggerWithoutIdAndName) 175 { 176 triggerFactoryMock 177 .expectMake(TriggerParams() 178 .id(TriggerManager::triggerNameDefault) 179 .name(TriggerManager::triggerNameDefault), 180 Ref(*sut), Ref(storageMock)) 181 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 182 183 auto [ec, path] = addTrigger(TriggerParams().id("").name("")); 184 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 185 EXPECT_THAT(path, Not(Eq(""))); 186 } 187 188 TEST_F(TestTriggerManager, addTriggerWithPrefixId) 189 { 190 triggerFactoryMock 191 .expectMake(TriggerParams() 192 .id("TelemetryService/HackyName") 193 .name("Hacky/Name!@#$"), 194 Ref(*sut), Ref(storageMock)) 195 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 196 197 auto [ec, path] = addTrigger( 198 TriggerParams().id("TelemetryService/").name("Hacky/Name!@#$")); 199 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 200 EXPECT_THAT(path, Not(Eq(""))); 201 } 202 203 TEST_F(TestTriggerManager, addTriggerWithoutIdTwice) 204 { 205 addTrigger(TriggerParams().id("")); 206 207 auto [ec, path] = addTrigger(TriggerParams().id("")); 208 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 209 EXPECT_THAT(path, Not(Eq(""))); 210 } 211 212 TEST_F(TestTriggerManager, addTriggerWithoutIdAndWithLongNameTwice) 213 { 214 std::string longName = utils::string_utils::getMaxName(); 215 addTrigger(TriggerParams().id("").name(longName)); 216 217 auto [ec, path] = addTrigger(TriggerParams().id("").name(longName)); 218 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 219 EXPECT_THAT(path, Not(Eq(""))); 220 } 221 222 TEST_F(TestTriggerManager, addTriggerWithMaxLengthId) 223 { 224 std::string reportId = utils::string_utils::getMaxId(); 225 triggerParams.id(reportId); 226 triggerFactoryMock.expectMake(triggerParams, Ref(*sut), Ref(storageMock)); 227 228 auto [ec, path] = addTrigger(triggerParams); 229 230 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 231 EXPECT_THAT(path, Eq("/"s + reportId)); 232 } 233 234 TEST_F(TestTriggerManager, addTriggerWithMaxLengthPrefix) 235 { 236 std::string reportId = utils::string_utils::getMaxPrefix() + "/MyId"; 237 triggerParams.id(reportId); 238 triggerFactoryMock.expectMake(triggerParams, Ref(*sut), Ref(storageMock)); 239 240 auto [ec, path] = addTrigger(triggerParams); 241 242 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 243 EXPECT_THAT(path, Eq("/"s + reportId)); 244 } 245 246 TEST_F(TestTriggerManager, addTriggerWithMaxLengthName) 247 { 248 triggerParams.name(utils::string_utils::getMaxName()); 249 triggerFactoryMock.expectMake(triggerParams, Ref(*sut), Ref(storageMock)); 250 251 auto [ec, path] = addTrigger(triggerParams); 252 253 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 254 EXPECT_THAT(path, Eq("/"s + triggerParams.id())); 255 } 256 257 TEST_F(TestTriggerManager, addTriggerWithMaxLengthDiscreteThresholdName) 258 { 259 namespace ts = utils::tstring; 260 261 triggerParams = 262 TriggerParams() 263 .id("DiscreteTrigger") 264 .name("My Discrete Trigger") 265 .thresholdParams(std::vector<discrete::LabeledThresholdParam>{ 266 discrete::LabeledThresholdParam{ 267 utils::string_utils::getMaxName(), 268 discrete::Severity::warning, Milliseconds(10).count(), 269 "15.2"}}); 270 271 triggerFactoryMock.expectMake(triggerParams, Ref(*sut), Ref(storageMock)); 272 273 auto [ec, path] = addTrigger(triggerParams); 274 275 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 276 EXPECT_THAT(path, Eq("/"s + triggerParams.id())); 277 } 278 279 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithTooLongFullId) 280 { 281 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 282 .Times(0); 283 284 triggerParams.id( 285 std::string(utils::constants::maxReportFullIdLength + 1, 'z')); 286 287 auto [ec, path] = addTrigger(triggerParams); 288 289 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 290 EXPECT_THAT(path, Eq(std::string())); 291 } 292 293 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithTooLongId) 294 { 295 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 296 .Times(0); 297 298 triggerParams.id(utils::string_utils::getTooLongId()); 299 300 auto [ec, path] = addTrigger(triggerParams); 301 302 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 303 EXPECT_THAT(path, Eq(std::string())); 304 } 305 306 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithTooLongPrefix) 307 { 308 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 309 .Times(0); 310 311 triggerParams.id(utils::string_utils::getTooLongPrefix() + "/MyId"); 312 313 auto [ec, path] = addTrigger(triggerParams); 314 315 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 316 EXPECT_THAT(path, Eq(std::string())); 317 } 318 319 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithTooManyPrefixes) 320 { 321 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 322 .Times(0); 323 324 std::string reportId; 325 for (size_t i = 0; i < utils::constants::maxPrefixesInId + 1; i++) 326 { 327 reportId += "prefix/"; 328 } 329 reportId += "MyId"; 330 331 triggerParams.id(reportId); 332 333 auto [ec, path] = addTrigger(triggerParams); 334 335 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 336 EXPECT_THAT(path, Eq(std::string())); 337 } 338 339 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithTooLongName) 340 { 341 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 342 .Times(0); 343 344 triggerParams.name(utils::string_utils::getTooLongName()); 345 346 auto [ec, path] = addTrigger(triggerParams); 347 348 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 349 EXPECT_THAT(path, Eq(std::string())); 350 } 351 352 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWithTooLongMetricId) 353 { 354 namespace ts = utils::tstring; 355 356 triggerParams = 357 TriggerParams() 358 .id("DiscreteTrigger") 359 .name("My Discrete Trigger") 360 .thresholdParams(std::vector<discrete::LabeledThresholdParam>{ 361 discrete::LabeledThresholdParam{ 362 utils::string_utils::getTooLongName(), 363 discrete::Severity::warning, Milliseconds(10).count(), 364 "15.2"}}); 365 366 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 367 .Times(0); 368 369 auto [ec, path] = addTrigger(triggerParams); 370 371 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 372 EXPECT_THAT(path, Eq(std::string())); 373 } 374 375 TEST_F(TestTriggerManager, DISABLED_failToAddTriggerWhenMaxTriggerIsReached) 376 { 377 auto triggerParams = TriggerParams(); 378 379 triggerFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 380 .Times(TriggerManager::maxTriggers); 381 382 for (size_t i = 0; i < TriggerManager::maxTriggers; i++) 383 { 384 triggerParams.id(TriggerParams().id() + std::to_string(i)); 385 386 auto [ec, path] = addTrigger(triggerParams); 387 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 388 } 389 390 triggerParams.id(TriggerParams().id() + 391 std::to_string(TriggerManager::maxTriggers)); 392 auto [ec, path] = addTrigger(triggerParams); 393 EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open)); 394 EXPECT_THAT(path, Eq(std::string())); 395 } 396 397 TEST_F(TestTriggerManager, removeTrigger) 398 { 399 { 400 InSequence seq; 401 triggerFactoryMock 402 .expectMake(TriggerParams(), Ref(*sut), Ref(storageMock)) 403 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 404 EXPECT_CALL(triggerMock, Die()); 405 EXPECT_CALL(checkPoint, Call("end")); 406 } 407 408 addTrigger(TriggerParams()); 409 sut->removeTrigger(&triggerMock); 410 checkPoint.Call("end"); 411 } 412 413 TEST_F(TestTriggerManager, removingTriggerThatIsNotInContainerHasNoEffect) 414 { 415 { 416 InSequence seq; 417 EXPECT_CALL(checkPoint, Call("end")); 418 EXPECT_CALL(triggerMock, Die()); 419 } 420 421 sut->removeTrigger(&triggerMock); 422 checkPoint.Call("end"); 423 } 424 425 TEST_F(TestTriggerManager, removingSameTriggerTwiceHasNoSideEffect) 426 { 427 { 428 InSequence seq; 429 triggerFactoryMock 430 .expectMake(TriggerParams(), Ref(*sut), Ref(storageMock)) 431 .WillOnce(Return(ByMove(std::move(triggerMockPtr)))); 432 EXPECT_CALL(triggerMock, Die()); 433 EXPECT_CALL(checkPoint, Call("end")); 434 } 435 436 addTrigger(TriggerParams()); 437 sut->removeTrigger(&triggerMock); 438 sut->removeTrigger(&triggerMock); 439 checkPoint.Call("end"); 440 } 441 class TestTriggerManagerStorage : public TestTriggerManager 442 { 443 public: 444 using FilePath = interfaces::JsonStorage::FilePath; 445 using DirectoryPath = interfaces::JsonStorage::DirectoryPath; 446 447 void SetUp() override 448 { 449 ON_CALL(storageMock, list()) 450 .WillByDefault(Return(std::vector<FilePath>{ 451 {FilePath("trigger1")}, {FilePath("trigger2")}})); 452 453 ON_CALL(storageMock, load(FilePath("trigger1"))) 454 .WillByDefault(InvokeWithoutArgs([this] { return data1; })); 455 456 data2["Id"] = "Trigger2"; 457 data2["Name"] = "Second Trigger"; 458 ON_CALL(storageMock, load(FilePath("trigger2"))) 459 .WillByDefault(InvokeWithoutArgs([this] { return data2; })); 460 } 461 462 nlohmann::json data1 = nlohmann::json{ 463 {"Version", Trigger::triggerVersion}, 464 {"Id", TriggerParams().id()}, 465 {"Name", TriggerParams().name()}, 466 {"ThresholdParamsDiscriminator", 467 TriggerParams().thresholdParams().index()}, 468 {"TriggerActions", utils::transform(TriggerParams().triggerActions(), 469 [](const auto& action) { 470 return actionToString(action); 471 })}, 472 {"ThresholdParams", utils::labeledThresholdParamsToJson( 473 TriggerParams().thresholdParams())}, 474 {"ReportIds", TriggerParams().reportIds()}, 475 {"Sensors", TriggerParams().sensors()}}; 476 477 nlohmann::json data2 = data1; 478 }; 479 480 TEST_F(TestTriggerManagerStorage, triggerManagerCtorAddTriggerFromStorage) 481 { 482 triggerFactoryMock.expectMake(TriggerParams(), _, Ref(storageMock)); 483 triggerFactoryMock.expectMake( 484 TriggerParams().id("Trigger2").name("Second Trigger"), _, 485 Ref(storageMock)); 486 EXPECT_CALL(storageMock, remove(_)).Times(0); 487 488 sut = makeTriggerManager(); 489 } 490 491 TEST_F(TestTriggerManagerStorage, 492 triggerManagerCtorRemoveDiscreteTriggerFromStorage) 493 { 494 LabeledTriggerThresholdParams thresholdParams = 495 std::vector<discrete::LabeledThresholdParam>{ 496 {"userId1", discrete::Severity::warning, 15, "10.0"}, 497 {"userId2", discrete::Severity::critical, 5, "20.0"}}; 498 499 data1["ThresholdParamsDiscriminator"] = thresholdParams.index(); 500 501 data1["ThresholdParams"] = 502 utils::labeledThresholdParamsToJson(thresholdParams); 503 504 EXPECT_CALL(storageMock, remove(FilePath("trigger1"))).Times(0); 505 506 sut = makeTriggerManager(); 507 } 508 509 TEST_F(TestTriggerManagerStorage, 510 triggerManagerCtorRemoveDiscreteTriggerFromStorage2) 511 { 512 data1["IsDiscrete"] = true; 513 514 EXPECT_CALL(storageMock, remove(FilePath("trigger1"))).Times(0); 515 516 sut = makeTriggerManager(); 517 } 518 519 TEST_F(TestTriggerManagerStorage, 520 triggerManagerCtorAddProperRemoveInvalidTriggerFromStorage) 521 { 522 data1["Version"] = Trigger::triggerVersion - 1; 523 524 triggerFactoryMock.expectMake( 525 TriggerParams().id("Trigger2").name("Second Trigger"), _, 526 Ref(storageMock)); 527 EXPECT_CALL(storageMock, remove(FilePath("trigger1"))); 528 529 sut = makeTriggerManager(); 530 } 531