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, 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, 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, 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, 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, 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, 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, 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, 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, failToAddReportWithMoreMetricPropertiesThanExpected) 352 { 353 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 354 .Times(0); 355 356 reportParams.metricParameters( 357 std::vector<LabeledMetricParameters>{{LabeledMetricParameters{ 358 {LabeledSensorInfo{"Service", 359 "/xyz/openbmc_project/sensors/power/p1", 360 "Metadata1"}}, 361 OperationType::avg, 362 CollectionTimeScope::point, 363 CollectionDuration(Milliseconds(0u))}}}); 364 365 auto metricParams = reportParams.metricParameters(); 366 auto& metricParamsVec = 367 metricParams[0].at_label<utils::tstring::SensorPath>(); 368 369 for (size_t i = 0; i < ReportManager::maxNumberMetrics; i++) 370 { 371 metricParamsVec.emplace_back(LabeledSensorInfo{ 372 "Service", "/xyz/openbmc_project/sensors/power/p1", "Metadata1"}); 373 } 374 375 reportParams.metricParameters(std::move(metricParams)); 376 377 auto [ec, path] = addReport(reportParams); 378 379 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 380 EXPECT_THAT(path, Eq(std::string())); 381 } 382 383 TEST_F(TestReportManager, failToAddReportWithMoreMetricsThanExpected) 384 { 385 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 386 .Times(0); 387 388 auto metricParams = std::vector<LabeledMetricParameters>{}; 389 390 for (size_t i = 0; i < ReportManager::maxNumberMetrics + 1; i++) 391 { 392 metricParams.emplace_back( 393 LabeledMetricParameters{{}, 394 OperationType::avg, 395 CollectionTimeScope::point, 396 CollectionDuration(Milliseconds(0u))}); 397 } 398 399 reportParams.metricParameters(std::move(metricParams)); 400 401 auto [ec, path] = addReport(reportParams); 402 403 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 404 EXPECT_THAT(path, Eq(std::string())); 405 } 406 407 TEST_F(TestReportManager, failToAddReportWithAppendLimitGreaterThanMax) 408 { 409 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 410 .Times(0); 411 412 reportParams.appendLimit(ReportManager::maxAppendLimit + 1); 413 414 auto [ec, path] = addReport(reportParams); 415 416 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 417 EXPECT_THAT(path, Eq(std::string())); 418 } 419 420 TEST_F(TestReportManager, addReportWithAppendLimitEqualToUint64MaxIsAllowed) 421 { 422 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 423 reportFactoryMock 424 .expectMake(reportParams.appendLimit(ReportManager::maxAppendLimit), 425 Ref(*sut), Ref(storageMock)) 426 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 427 428 auto [ec, path] = addReport( 429 reportParams.appendLimit(std::numeric_limits<uint64_t>::max())); 430 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 431 EXPECT_THAT(path, Eq(reportMock.getPath())); 432 } 433 434 TEST_F(TestReportManager, failToAddReportWhenMaxReportIsReached) 435 { 436 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 437 .Times(ReportManager::maxReports); 438 439 for (size_t i = 0; i < ReportManager::maxReports; i++) 440 { 441 reportParams.reportId(reportParams.reportName() + std::to_string(i)); 442 443 auto [ec, path] = addReport(reportParams); 444 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 445 } 446 447 reportParams.reportId(reportParams.reportName() + 448 std::to_string(ReportManager::maxReports)); 449 auto [ec, path] = addReport(reportParams); 450 451 EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open)); 452 EXPECT_THAT(path, Eq(std::string())); 453 } 454 455 TEST_F(TestReportManager, removeReport) 456 { 457 { 458 InSequence seq; 459 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 460 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 461 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 462 EXPECT_CALL(reportMock, Die()); 463 EXPECT_CALL(checkPoint, Call("end")); 464 } 465 466 addReport(reportParams); 467 sut->removeReport(&reportMock); 468 checkPoint.Call("end"); 469 } 470 471 TEST_F(TestReportManager, removingReportThatIsNotInContainerHasNoEffect) 472 { 473 { 474 InSequence seq; 475 EXPECT_CALL(checkPoint, Call("end")); 476 EXPECT_CALL(reportMock, Die()); 477 } 478 479 sut->removeReport(&reportMock); 480 checkPoint.Call("end"); 481 } 482 483 TEST_F(TestReportManager, removingSameReportTwiceHasNoSideEffect) 484 { 485 { 486 InSequence seq; 487 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 488 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 489 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 490 EXPECT_CALL(reportMock, Die()); 491 EXPECT_CALL(checkPoint, Call("end")); 492 } 493 494 addReport(reportParams); 495 sut->removeReport(&reportMock); 496 sut->removeReport(&reportMock); 497 checkPoint.Call("end"); 498 } 499 500 class TestReportManagerWithAggregationOperationType : 501 public TestReportManager, 502 public WithParamInterface<OperationType> 503 { 504 public: 505 OperationType operationType = GetParam(); 506 }; 507 508 INSTANTIATE_TEST_SUITE_P(_, TestReportManagerWithAggregationOperationType, 509 Values(OperationType::max, OperationType::min, 510 OperationType::avg, OperationType::sum)); 511 512 TEST_P(TestReportManagerWithAggregationOperationType, 513 addReportWithDifferentOperationTypes) 514 { 515 reportParams.metricParameters( 516 std::vector<LabeledMetricParameters>{{LabeledMetricParameters{ 517 {LabeledSensorInfo{"Service", 518 "/xyz/openbmc_project/sensors/power/p1", 519 "Metadata1"}}, 520 operationType, 521 CollectionTimeScope::point, 522 CollectionDuration(Milliseconds(0u))}}}); 523 524 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 525 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 526 527 auto [ec, path] = addReport(reportParams); 528 529 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 530 EXPECT_THAT(path, Eq("/"s + reportParams.reportId())); 531 } 532 533 class TestReportManagerStorage : public TestReportManager 534 { 535 public: 536 using FilePath = interfaces::JsonStorage::FilePath; 537 using DirectoryPath = interfaces::JsonStorage::DirectoryPath; 538 539 void SetUp() override 540 { 541 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)).Times(0); 542 543 ON_CALL(storageMock, list()) 544 .WillByDefault(Return(std::vector<FilePath>{FilePath("report1")})); 545 ON_CALL(storageMock, load(FilePath("report1"))) 546 .WillByDefault(InvokeWithoutArgs([this] { return data; })); 547 } 548 549 void makeReportManager() 550 { 551 sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr), 552 std::move(storageMockPtr), 553 DbusEnvironment::getObjServer()); 554 } 555 556 nlohmann::json data = nlohmann::json{ 557 {"Enabled", reportParams.enabled()}, 558 {"Version", Report::reportVersion}, 559 {"Id", reportParams.reportId()}, 560 {"Name", reportParams.reportName()}, 561 {"ReportingType", utils::toUnderlying(reportParams.reportingType())}, 562 {"ReportActions", reportParams.reportActions()}, 563 {"Interval", reportParams.interval().count()}, 564 {"ReportUpdates", utils::toUnderlying(reportParams.reportUpdates())}, 565 {"AppendLimit", reportParams.appendLimit()}, 566 {"ReadingParameters", reportParams.metricParameters()}}; 567 }; 568 569 TEST_F(TestReportManagerStorage, reportManagerCtorAddReportFromStorage) 570 { 571 reportFactoryMock.expectMake(reportParams, _, Ref(storageMock)); 572 573 makeReportManager(); 574 } 575 576 TEST_F(TestReportManagerStorage, 577 reportManagerCtorRemoveFileIfVersionDoesNotMatch) 578 { 579 data["Version"] = Report::reportVersion - 1; 580 581 EXPECT_CALL(storageMock, remove(FilePath("report1"))); 582 583 makeReportManager(); 584 } 585 586 TEST_F(TestReportManagerStorage, 587 reportManagerCtorRemoveFileIfIntervalHasWrongType) 588 { 589 data["Interval"] = "1000"; 590 591 EXPECT_CALL(storageMock, remove(FilePath("report1"))); 592 593 makeReportManager(); 594 } 595