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