1 #include "dbus_environment.hpp" 2 #include "helpers.hpp" 3 #include "interfaces/trigger_manager.hpp" 4 #include "mocks/json_storage_mock.hpp" 5 #include "mocks/report_factory_mock.hpp" 6 #include "mocks/trigger_manager_mock.hpp" 7 #include "params/report_params.hpp" 8 #include "report.hpp" 9 #include "report_manager.hpp" 10 #include "utils/conversion.hpp" 11 #include "utils/dbus_path_utils.hpp" 12 #include "utils/string_utils.hpp" 13 #include "utils/transform.hpp" 14 #include "utils/tstring.hpp" 15 16 using namespace testing; 17 using namespace std::string_literals; 18 using namespace std::chrono_literals; 19 20 class TestReportManager : public Test 21 { 22 public: 23 ReportParams reportParams; 24 25 std::unique_ptr<ReportFactoryMock> reportFactoryMockPtr = 26 std::make_unique<StrictMock<ReportFactoryMock>>(); 27 ReportFactoryMock& reportFactoryMock = *reportFactoryMockPtr; 28 29 std::unique_ptr<StorageMock> storageMockPtr = 30 std::make_unique<NiceMock<StorageMock>>(); 31 StorageMock& storageMock = *storageMockPtr; 32 33 std::unique_ptr<ReportMock> reportMockPtr = 34 std::make_unique<NiceMock<ReportMock>>(reportParams.reportId()); 35 ReportMock& reportMock = *reportMockPtr; 36 37 std::unique_ptr<ReportManager> sut; 38 39 MockFunction<void(std::string)> checkPoint; 40 41 void SetUp() override 42 { 43 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)) 44 .Times(AnyNumber()); 45 46 sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr), 47 std::move(storageMockPtr), 48 DbusEnvironment::getObjServer()); 49 } 50 51 void TearDown() override 52 { 53 DbusEnvironment::synchronizeIoc(); 54 } 55 56 template <class... Args> 57 requires(sizeof...(Args) > 1) 58 std::pair<boost::system::error_code, std::string> addReport( 59 Args&&... args) 60 { 61 std::promise<std::pair<boost::system::error_code, std::string>> 62 addReportPromise; 63 DbusEnvironment::getBus()->async_method_call( 64 [&addReportPromise](boost::system::error_code ec, 65 const std::string& path) { 66 addReportPromise.set_value({ec, path}); 67 }, 68 DbusEnvironment::serviceName(), ReportManager::reportManagerPath, 69 ReportManager::reportManagerIfaceName, "AddReportFutureVersion", 70 std::forward<Args>(args)...); 71 return DbusEnvironment::waitForFuture(addReportPromise.get_future()); 72 } 73 74 auto addReport(const ReportParams& params) 75 { 76 return addReport(params.reportId(), params.reportName(), 77 utils::enumToString(params.reportingType()), 78 utils::enumToString(params.reportUpdates()), 79 params.appendLimit(), 80 utils::transform(params.reportActions(), 81 [](const auto v) { 82 return utils::enumToString(v); 83 }), 84 params.interval().count(), 85 toReadingParameters(params.metricParameters())); 86 } 87 88 template <class T> 89 static T getProperty(const std::string& property) 90 { 91 return DbusEnvironment::getProperty<T>( 92 ReportManager::reportManagerPath, 93 ReportManager::reportManagerIfaceName, property); 94 } 95 }; 96 97 TEST_F(TestReportManager, minInterval) 98 { 99 EXPECT_THAT(getProperty<uint64_t>("MinInterval"), 100 Eq(ReportManager::minInterval.count())); 101 } 102 103 TEST_F(TestReportManager, maxReports) 104 { 105 EXPECT_THAT(getProperty<size_t>("MaxReports"), 106 Eq(ReportManager::maxReports)); 107 } 108 109 TEST_F(TestReportManager, returnsPropertySupportedOperationTypes) 110 { 111 EXPECT_THAT( 112 getProperty<std::vector<std::string>>("SupportedOperationTypes"), 113 UnorderedElementsAre("Maximum", "Minimum", "Average", "Summation")); 114 } 115 116 TEST_F(TestReportManager, addReport) 117 { 118 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 119 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 120 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 121 122 auto [ec, path] = addReport(reportParams); 123 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 124 EXPECT_THAT(path, Eq(reportMock.getPath())); 125 } 126 127 TEST_F(TestReportManager, addOnChangeReport) 128 { 129 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 130 reportFactoryMock 131 .expectMake(reportParams.reportingType(ReportingType::onChange), 132 Ref(*sut), Ref(storageMock)) 133 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 134 135 auto [ec, path] = addReport(reportParams); 136 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 137 EXPECT_THAT(path, Eq(reportMock.getPath())); 138 } 139 140 TEST_F(TestReportManager, nameIsUsedToGenerateIdWhenIdIsEmptyInAddReport) 141 { 142 reportParams.reportId("ReportName"); 143 reportParams.reportName("ReportName"); 144 145 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)); 146 147 auto [ec, path] = addReport(reportParams.reportId("")); 148 149 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 150 EXPECT_THAT(path, Eq("/ReportName")); 151 } 152 153 TEST_F(TestReportManager, nameIsUsedToGenerateIdWhenIdIsNamespace) 154 { 155 reportParams.reportId("Prefix/ReportName"); 156 reportParams.reportName("ReportName"); 157 158 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)); 159 160 auto [ec, path] = addReport(reportParams.reportId("Prefix/")); 161 162 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 163 EXPECT_THAT(path, Eq("/Prefix/ReportName")); 164 } 165 166 TEST_F(TestReportManager, addReportWithMaxLengthId) 167 { 168 std::string reportId = utils::string_utils::getMaxId(); 169 reportParams.reportId(reportId); 170 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)); 171 172 auto [ec, path] = addReport(reportParams); 173 174 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 175 EXPECT_THAT(path, Eq("/"s + reportId)); 176 } 177 178 TEST_F(TestReportManager, addReportWithMaxLengthPrefix) 179 { 180 std::string reportId = utils::string_utils::getMaxPrefix() + "/MyId"; 181 reportParams.reportId(reportId); 182 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)); 183 184 auto [ec, path] = addReport(reportParams); 185 186 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 187 EXPECT_THAT(path, Eq("/"s + reportId)); 188 } 189 190 TEST_F(TestReportManager, addReportWithMaxLengthName) 191 { 192 reportParams.reportName(utils::string_utils::getMaxName()); 193 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)); 194 195 auto [ec, path] = addReport(reportParams); 196 197 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 198 EXPECT_THAT(path, Eq("/"s + reportParams.reportId())); 199 } 200 201 TEST_F(TestReportManager, addReportWithMaxLengthMetricId) 202 { 203 namespace ts = utils::tstring; 204 std::vector<LabeledMetricParameters> newMetricParams{ 205 {LabeledMetricParameters{ 206 {LabeledSensorInfo{"Service", 207 "/xyz/openbmc_project/sensors/power/p1", 208 "metadata1"}}, 209 OperationType::avg, 210 utils::string_utils::getMaxId(), 211 CollectionTimeScope::point, 212 CollectionDuration(Milliseconds(0u))}}}; 213 214 reportParams.metricParameters(newMetricParams); 215 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)); 216 217 auto [ec, path] = addReport(reportParams); 218 219 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 220 EXPECT_THAT(path, Eq("/"s + reportParams.reportId())); 221 } 222 223 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooLongFullId) 224 { 225 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 226 .Times(0); 227 228 reportParams.reportId( 229 std::string(utils::constants::maxReportFullIdLength + 1, 'z')); 230 231 auto [ec, path] = addReport(reportParams); 232 233 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 234 EXPECT_THAT(path, Eq(std::string())); 235 } 236 237 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooLongId) 238 { 239 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 240 .Times(0); 241 242 reportParams.reportId(utils::string_utils::getTooLongId()); 243 244 auto [ec, path] = addReport(reportParams); 245 246 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 247 EXPECT_THAT(path, Eq(std::string())); 248 } 249 250 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooLongPrefix) 251 { 252 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 253 .Times(0); 254 255 reportParams.reportId(utils::string_utils::getTooLongPrefix() + "/MyId"); 256 257 auto [ec, path] = addReport(reportParams); 258 259 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 260 EXPECT_THAT(path, Eq(std::string())); 261 } 262 263 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooManyPrefixes) 264 { 265 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 266 .Times(0); 267 268 std::string reportId; 269 for (size_t i = 0; i < utils::constants::maxPrefixesInId + 1; i++) 270 { 271 reportId += "prefix/"; 272 } 273 reportId += "MyId"; 274 275 reportParams.reportId(reportId); 276 277 auto [ec, path] = addReport(reportParams); 278 279 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 280 EXPECT_THAT(path, Eq(std::string())); 281 } 282 283 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooLongName) 284 { 285 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 286 .Times(0); 287 288 reportParams.reportName(utils::string_utils::getTooLongName()); 289 290 auto [ec, path] = addReport(reportParams); 291 292 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 293 EXPECT_THAT(path, Eq(std::string())); 294 } 295 296 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooLongMetricId) 297 { 298 namespace ts = utils::tstring; 299 300 std::vector<LabeledMetricParameters> newMetricParams{ 301 {LabeledMetricParameters{ 302 {LabeledSensorInfo{"Service", 303 "/xyz/openbmc_project/sensors/power/p1", 304 "metadata1"}}, 305 OperationType::avg, 306 utils::string_utils::getTooLongId(), 307 CollectionTimeScope::point, 308 CollectionDuration(Milliseconds(0u))}}}; 309 310 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 311 .Times(0); 312 313 reportParams.metricParameters(newMetricParams); 314 315 auto [ec, path] = addReport(reportParams); 316 317 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 318 EXPECT_THAT(path, Eq(std::string())); 319 } 320 321 TEST_F(TestReportManager, DISABLED_failToAddReportTwice) 322 { 323 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 324 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 325 326 addReport(reportParams); 327 328 auto [ec, path] = addReport(reportParams); 329 330 EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists)); 331 EXPECT_THAT(path, Eq(std::string())); 332 } 333 334 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidInterval) 335 { 336 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 337 .Times(0); 338 339 reportParams.reportingType(ReportingType::periodic); 340 reportParams.interval(ReportManager::minInterval - 1ms); 341 342 auto [ec, path] = addReport(reportParams); 343 344 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 345 EXPECT_THAT(path, Eq(std::string())); 346 } 347 348 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidReportingType) 349 { 350 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 351 .Times(0); 352 353 auto [ec, path] = addReport( 354 reportParams.reportName(), "InvalidReportingType", 355 utils::transform(reportParams.reportActions(), 356 [](const auto v) { return utils::enumToString(v); }), 357 reportParams.interval().count(), 358 toReadingParameters(reportParams.metricParameters())); 359 360 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 361 EXPECT_THAT(path, Eq(std::string())); 362 } 363 364 TEST_F(TestReportManager, 365 DISABLED_failToAddReportWithMoreMetricPropertiesThanExpected) 366 { 367 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 368 .Times(0); 369 370 reportParams.metricParameters( 371 std::vector<LabeledMetricParameters>{{LabeledMetricParameters{ 372 {LabeledSensorInfo{"Service", 373 "/xyz/openbmc_project/sensors/power/p1", 374 "Metadata1"}}, 375 OperationType::avg, 376 "MetricId1", 377 CollectionTimeScope::point, 378 CollectionDuration(Milliseconds(0u))}}}); 379 380 auto metricParams = reportParams.metricParameters(); 381 auto& metricParamsVec = 382 metricParams[0].at_label<utils::tstring::SensorPath>(); 383 384 for (size_t i = 0; i < ReportManager::maxNumberMetrics; i++) 385 { 386 metricParamsVec.emplace_back(LabeledSensorInfo{ 387 "Service", "/xyz/openbmc_project/sensors/power/p1", "Metadata1"}); 388 } 389 390 reportParams.metricParameters(std::move(metricParams)); 391 392 auto [ec, path] = addReport(reportParams); 393 394 EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long)); 395 EXPECT_THAT(path, Eq(std::string())); 396 } 397 398 TEST_F(TestReportManager, DISABLED_failToAddReportWithMoreMetricsThanExpected) 399 { 400 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 401 .Times(0); 402 403 auto metricParams = std::vector<LabeledMetricParameters>{}; 404 405 for (size_t i = 0; i < ReportManager::maxNumberMetrics + 1; i++) 406 { 407 metricParams.emplace_back( 408 LabeledMetricParameters{{}, 409 OperationType::avg, 410 "MetricId1", 411 CollectionTimeScope::point, 412 CollectionDuration(Milliseconds(0u))}); 413 } 414 415 reportParams.metricParameters(std::move(metricParams)); 416 417 auto [ec, path] = addReport(reportParams); 418 419 EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long)); 420 EXPECT_THAT(path, Eq(std::string())); 421 } 422 423 TEST_F(TestReportManager, DISABLED_failToAddReportWithAppendLimitGreaterThanMax) 424 { 425 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 426 .Times(0); 427 428 reportParams.appendLimit(ReportManager::maxAppendLimit + 1); 429 430 auto [ec, path] = addReport(reportParams); 431 432 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 433 EXPECT_THAT(path, Eq(std::string())); 434 } 435 436 TEST_F(TestReportManager, addReportWithAppendLimitEqualToUint64MaxIsAllowed) 437 { 438 reportParams.appendLimit(std::numeric_limits<uint64_t>::max()); 439 440 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 441 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 442 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 443 444 auto [ec, path] = addReport(reportParams); 445 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 446 EXPECT_THAT(path, Eq(reportMock.getPath())); 447 } 448 449 TEST_F(TestReportManager, DISABLED_failToAddReportWhenMaxReportIsReached) 450 { 451 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 452 .Times(ReportManager::maxReports); 453 454 for (size_t i = 0; i < ReportManager::maxReports; i++) 455 { 456 reportParams.reportId(reportParams.reportName() + std::to_string(i)); 457 458 auto [ec, path] = addReport(reportParams); 459 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 460 } 461 462 reportParams.reportId(reportParams.reportName() + 463 std::to_string(ReportManager::maxReports)); 464 auto [ec, path] = addReport(reportParams); 465 466 EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open)); 467 EXPECT_THAT(path, Eq(std::string())); 468 } 469 470 TEST_F(TestReportManager, removeReport) 471 { 472 { 473 InSequence seq; 474 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 475 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 476 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 477 EXPECT_CALL(reportMock, Die()); 478 EXPECT_CALL(checkPoint, Call("end")); 479 } 480 481 addReport(reportParams); 482 sut->removeReport(&reportMock); 483 checkPoint.Call("end"); 484 } 485 486 TEST_F(TestReportManager, removingReportThatIsNotInContainerHasNoEffect) 487 { 488 { 489 InSequence seq; 490 EXPECT_CALL(checkPoint, Call("end")); 491 EXPECT_CALL(reportMock, Die()); 492 } 493 494 sut->removeReport(&reportMock); 495 checkPoint.Call("end"); 496 } 497 498 TEST_F(TestReportManager, removingSameReportTwiceHasNoSideEffect) 499 { 500 { 501 InSequence seq; 502 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 503 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 504 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 505 EXPECT_CALL(reportMock, Die()); 506 EXPECT_CALL(checkPoint, Call("end")); 507 } 508 509 addReport(reportParams); 510 sut->removeReport(&reportMock); 511 sut->removeReport(&reportMock); 512 checkPoint.Call("end"); 513 } 514 515 class TestReportManagerWithAggregationOperationType : 516 public TestReportManager, 517 public WithParamInterface<OperationType> 518 { 519 public: 520 OperationType operationType = GetParam(); 521 }; 522 523 INSTANTIATE_TEST_SUITE_P(_, TestReportManagerWithAggregationOperationType, 524 Values(OperationType::max, OperationType::min, 525 OperationType::avg, OperationType::sum)); 526 527 TEST_P(TestReportManagerWithAggregationOperationType, 528 addReportWithDifferentOperationTypes) 529 { 530 reportParams.metricParameters( 531 std::vector<LabeledMetricParameters>{{LabeledMetricParameters{ 532 {LabeledSensorInfo{"Service", 533 "/xyz/openbmc_project/sensors/power/p1", 534 "Metadata1"}}, 535 operationType, 536 "MetricId1", 537 CollectionTimeScope::point, 538 CollectionDuration(Milliseconds(0u))}}}); 539 540 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 541 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 542 543 auto [ec, path] = addReport(reportParams); 544 545 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 546 EXPECT_THAT(path, Eq("/"s + reportParams.reportId())); 547 } 548 549 class TestReportManagerStorage : public TestReportManager 550 { 551 public: 552 using FilePath = interfaces::JsonStorage::FilePath; 553 using DirectoryPath = interfaces::JsonStorage::DirectoryPath; 554 555 void SetUp() override 556 { 557 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)).Times(0); 558 559 ON_CALL(storageMock, list()) 560 .WillByDefault(Return(std::vector<FilePath>{FilePath("report1")})); 561 ON_CALL(storageMock, load(FilePath("report1"))) 562 .WillByDefault(InvokeWithoutArgs([this] { return data; })); 563 } 564 565 void makeReportManager() 566 { 567 sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr), 568 std::move(storageMockPtr), 569 DbusEnvironment::getObjServer()); 570 } 571 572 nlohmann::json data = nlohmann::json{ 573 {"Enabled", reportParams.enabled()}, 574 {"Version", Report::reportVersion}, 575 {"Id", reportParams.reportId()}, 576 {"Name", reportParams.reportName()}, 577 {"ReportingType", utils::toUnderlying(reportParams.reportingType())}, 578 {"ReportActions", reportParams.reportActions()}, 579 {"Interval", reportParams.interval().count()}, 580 {"ReportUpdates", utils::toUnderlying(reportParams.reportUpdates())}, 581 {"AppendLimit", reportParams.appendLimit()}, 582 {"ReadingParameters", reportParams.metricParameters()}}; 583 }; 584 585 TEST_F(TestReportManagerStorage, reportManagerCtorAddReportFromStorage) 586 { 587 reportFactoryMock.expectMake(reportParams, _, Ref(storageMock)); 588 589 makeReportManager(); 590 } 591 592 TEST_F(TestReportManagerStorage, 593 reportManagerCtorRemoveFileIfVersionDoesNotMatch) 594 { 595 data["Version"] = Report::reportVersion - 1; 596 597 EXPECT_CALL(storageMock, remove(FilePath("report1"))); 598 599 makeReportManager(); 600 } 601 602 TEST_F(TestReportManagerStorage, 603 reportManagerCtorRemoveFileIfIntervalHasWrongType) 604 { 605 data["Interval"] = "1000"; 606 607 EXPECT_CALL(storageMock, remove(FilePath("report1"))); 608 609 makeReportManager(); 610 } 611