1 #include "dbus_environment.hpp" 2 #include "fakes/clock_fake.hpp" 3 #include "helpers.hpp" 4 #include "mocks/json_storage_mock.hpp" 5 #include "mocks/metric_mock.hpp" 6 #include "mocks/report_manager_mock.hpp" 7 #include "params/report_params.hpp" 8 #include "report.hpp" 9 #include "report_manager.hpp" 10 #include "utils/clock.hpp" 11 #include "utils/contains.hpp" 12 #include "utils/conv_container.hpp" 13 #include "utils/transform.hpp" 14 #include "utils/tstring.hpp" 15 16 #include <sdbusplus/exception.hpp> 17 18 using namespace testing; 19 using namespace std::literals::string_literals; 20 using namespace std::chrono_literals; 21 namespace tstring = utils::tstring; 22 23 constexpr Milliseconds systemTimestamp = 55ms; 24 25 class TestReport : public Test 26 { 27 public: 28 ReportParams defaultParams; 29 30 std::unique_ptr<ReportManagerMock> reportManagerMock = 31 std::make_unique<NiceMock<ReportManagerMock>>(); 32 testing::NiceMock<StorageMock> storageMock; 33 std::vector<std::shared_ptr<MetricMock>> metricMocks; 34 std::unique_ptr<ClockFake> clockFakePtr = std::make_unique<ClockFake>(); 35 ClockFake& clockFake = *clockFakePtr; 36 std::unique_ptr<Report> sut; 37 38 MockFunction<void()> checkPoint; 39 40 TestReport() 41 { 42 clockFake.system.set(systemTimestamp); 43 } 44 45 void initMetricMocks( 46 const std::vector<LabeledMetricParameters>& metricParameters) 47 { 48 for (auto i = metricMocks.size(); i < metricParameters.size(); ++i) 49 { 50 metricMocks.emplace_back(std::make_shared<NiceMock<MetricMock>>()); 51 } 52 metricMocks.resize(metricParameters.size()); 53 54 std::vector<MetricValue> readings{{MetricValue{"a", "b", 17.1, 114}, 55 MetricValue{"aa", "bb", 42.0, 74}}}; 56 readings.resize(metricParameters.size()); 57 58 for (size_t i = 0; i < metricParameters.size(); ++i) 59 { 60 ON_CALL(*metricMocks[i], getReadings()) 61 .WillByDefault(Return(std::vector({readings[i]}))); 62 ON_CALL(*metricMocks[i], dumpConfiguration()) 63 .WillByDefault(Return(metricParameters[i])); 64 } 65 } 66 67 void SetUp() override 68 { 69 sut = makeReport(ReportParams()); 70 } 71 72 static interfaces::JsonStorage::FilePath to_file_path(std::string id) 73 { 74 return interfaces::JsonStorage::FilePath( 75 std::to_string(std::hash<std::string>{}(id))); 76 } 77 78 std::unique_ptr<Report> makeReport(const ReportParams& params) 79 { 80 initMetricMocks(params.metricParameters()); 81 82 return std::make_unique<Report>( 83 DbusEnvironment::getIoc(), DbusEnvironment::getObjServer(), 84 params.reportId(), params.reportName(), params.reportingType(), 85 params.reportActions(), params.interval(), params.appendLimit(), 86 params.reportUpdates(), *reportManagerMock, storageMock, 87 utils::convContainer<std::shared_ptr<interfaces::Metric>>( 88 metricMocks), 89 params.enabled(), std::move(clockFakePtr)); 90 } 91 92 template <class T> 93 static T getProperty(const std::string& path, const std::string& property) 94 { 95 return DbusEnvironment::getProperty<T>(path, Report::reportIfaceName, 96 property); 97 } 98 99 template <class T> 100 static boost::system::error_code setProperty(const std::string& path, 101 const std::string& property, 102 const T& newValue) 103 { 104 return DbusEnvironment::setProperty<T>(path, Report::reportIfaceName, 105 property, newValue); 106 } 107 108 boost::system::error_code call(const std::string& path, 109 const std::string& interface, 110 const std::string& method) 111 { 112 std::promise<boost::system::error_code> methodPromise; 113 DbusEnvironment::getBus()->async_method_call( 114 [&methodPromise](boost::system::error_code ec) { 115 methodPromise.set_value(ec); 116 }, 117 DbusEnvironment::serviceName(), path, interface, method); 118 return DbusEnvironment::waitForFuture(methodPromise.get_future()); 119 } 120 121 boost::system::error_code update(const std::string& path) 122 { 123 return call(path, Report::reportIfaceName, "Update"); 124 } 125 126 boost::system::error_code deleteReport(const std::string& path) 127 { 128 return call(path, Report::deleteIfaceName, "Delete"); 129 } 130 }; 131 132 TEST_F(TestReport, returnsId) 133 { 134 EXPECT_THAT(sut->getId(), Eq(defaultParams.reportId())); 135 } 136 137 TEST_F(TestReport, verifyIfPropertiesHaveValidValue) 138 { 139 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Enabled"), 140 Eq(defaultParams.enabled())); 141 EXPECT_THAT(getProperty<uint64_t>(sut->getPath(), "Interval"), 142 Eq(defaultParams.interval().count())); 143 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistency"), Eq(true)); 144 EXPECT_THAT( 145 getProperty<std::vector<std::string>>(sut->getPath(), "ReportActions"), 146 Eq(utils::transform(defaultParams.reportActions(), [](const auto v) { 147 return utils::enumToString(v); 148 }))); 149 EXPECT_THAT(getProperty<bool>(sut->getPath(), "EmitsReadingsUpdate"), 150 Eq(utils::contains(defaultParams.reportActions(), 151 ReportAction::emitsReadingsUpdate))); 152 EXPECT_THAT(getProperty<uint64_t>(sut->getPath(), "AppendLimit"), 153 Eq(defaultParams.appendLimit())); 154 EXPECT_THAT(getProperty<std::string>(sut->getPath(), "ReportingType"), 155 Eq(utils::enumToString(defaultParams.reportingType()))); 156 EXPECT_THAT(getProperty<std::string>(sut->getPath(), "ReportUpdates"), 157 Eq(utils::enumToString(defaultParams.reportUpdates()))); 158 EXPECT_THAT( 159 getProperty<bool>(sut->getPath(), "LogToMetricReportsCollection"), 160 Eq(utils::contains(defaultParams.reportActions(), 161 ReportAction::logToMetricReportsCollection))); 162 EXPECT_THAT(getProperty<ReadingParameters>( 163 sut->getPath(), "ReadingParametersFutureVersion"), 164 Eq(toReadingParameters(defaultParams.metricParameters()))); 165 EXPECT_THAT(getProperty<std::string>(sut->getPath(), "Name"), 166 Eq(defaultParams.reportName())); 167 } 168 169 TEST_F(TestReport, readingsAreInitialyEmpty) 170 { 171 EXPECT_THAT(getProperty<Readings>(sut->getPath(), "Readings"), 172 Eq(Readings{})); 173 } 174 175 TEST_F(TestReport, setEnabledWithNewValue) 176 { 177 bool newValue = !defaultParams.enabled(); 178 EXPECT_THAT(setProperty(sut->getPath(), "Enabled", newValue).value(), 179 Eq(boost::system::errc::success)); 180 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Enabled"), Eq(newValue)); 181 } 182 183 TEST_F(TestReport, setIntervalWithValidValue) 184 { 185 uint64_t newValue = defaultParams.interval().count() + 1; 186 EXPECT_THAT(setProperty(sut->getPath(), "Interval", newValue).value(), 187 Eq(boost::system::errc::success)); 188 EXPECT_THAT(getProperty<uint64_t>(sut->getPath(), "Interval"), 189 Eq(newValue)); 190 } 191 192 TEST_F( 193 TestReport, 194 settingIntervalWithInvalidValueDoesNotChangePropertyAndReturnsInvalidArgument) 195 { 196 uint64_t newValue = defaultParams.interval().count() - 1; 197 EXPECT_THAT(setProperty(sut->getPath(), "Interval", newValue).value(), 198 Eq(boost::system::errc::invalid_argument)); 199 EXPECT_THAT(getProperty<uint64_t>(sut->getPath(), "Interval"), 200 Eq(defaultParams.interval().count())); 201 } 202 203 TEST_F(TestReport, settingEmitsReadingsUpdateHaveNoEffect) 204 { 205 EXPECT_THAT( 206 setProperty(sut->getPath(), "EmitsReadingsUpdate", true).value(), 207 Eq(boost::system::errc::read_only_file_system)); 208 EXPECT_THAT(getProperty<bool>(sut->getPath(), "EmitsReadingsUpdate"), 209 Eq(utils::contains(defaultParams.reportActions(), 210 ReportAction::emitsReadingsUpdate))); 211 } 212 213 TEST_F(TestReport, settingLogToMetricReportCollectionHaveNoEffect) 214 { 215 EXPECT_THAT( 216 setProperty(sut->getPath(), "LogToMetricReportsCollection", true) 217 .value(), 218 Eq(boost::system::errc::read_only_file_system)); 219 EXPECT_THAT( 220 getProperty<bool>(sut->getPath(), "LogToMetricReportsCollection"), 221 Eq(utils::contains(defaultParams.reportActions(), 222 ReportAction::logToMetricReportsCollection))); 223 } 224 225 TEST_F(TestReport, settingPersistencyToFalseRemovesReportFromStorage) 226 { 227 EXPECT_CALL(storageMock, remove(to_file_path(sut->getId()))); 228 229 bool persistency = false; 230 EXPECT_THAT(setProperty(sut->getPath(), "Persistency", persistency).value(), 231 Eq(boost::system::errc::success)); 232 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistency"), 233 Eq(persistency)); 234 } 235 236 TEST_F(TestReport, deleteReport) 237 { 238 EXPECT_CALL(*reportManagerMock, removeReport(sut.get())); 239 auto ec = deleteReport(sut->getPath()); 240 EXPECT_THAT(ec, Eq(boost::system::errc::success)); 241 } 242 243 TEST_F(TestReport, deletingNonExistingReportReturnInvalidRequestDescriptor) 244 { 245 auto ec = deleteReport(Report::reportDir + "NonExisting"s); 246 EXPECT_THAT(ec.value(), Eq(EBADR)); 247 } 248 249 TEST_F(TestReport, deleteReportExpectThatFileIsRemoveFromStorage) 250 { 251 EXPECT_CALL(storageMock, remove(to_file_path(sut->getId()))); 252 auto ec = deleteReport(sut->getPath()); 253 EXPECT_THAT(ec, Eq(boost::system::errc::success)); 254 } 255 256 class TestReportStore : 257 public TestReport, 258 public WithParamInterface<std::pair<std::string, nlohmann::json>> 259 { 260 public: 261 void SetUp() override 262 {} 263 264 nlohmann::json storedConfiguration; 265 }; 266 267 INSTANTIATE_TEST_SUITE_P( 268 _, TestReportStore, 269 Values(std::make_pair("Enabled"s, nlohmann::json(ReportParams().enabled())), 270 std::make_pair("Version"s, nlohmann::json(6)), 271 std::make_pair("Id"s, nlohmann::json(ReportParams().reportId())), 272 std::make_pair("Name"s, nlohmann::json(ReportParams().reportName())), 273 std::make_pair("ReportingType", 274 nlohmann::json(ReportParams().reportingType())), 275 std::make_pair("ReportActions", nlohmann::json(utils::transform( 276 ReportParams().reportActions(), 277 [](const auto v) { 278 return utils::toUnderlying( 279 v); 280 }))), 281 std::make_pair("Interval", 282 nlohmann::json(ReportParams().interval().count())), 283 std::make_pair("AppendLimit", 284 nlohmann::json(ReportParams().appendLimit())), 285 std::make_pair( 286 "ReadingParameters", 287 nlohmann::json( 288 {{{tstring::SensorPath::str(), 289 {{{tstring::Service::str(), "Service"}, 290 {tstring::Path::str(), 291 "/xyz/openbmc_project/sensors/power/p1"}, 292 {tstring::Metadata::str(), "metadata1"}}}}, 293 {tstring::OperationType::str(), OperationType::single}, 294 {tstring::Id::str(), "MetricId1"}, 295 {tstring::CollectionTimeScope::str(), 296 CollectionTimeScope::point}, 297 {tstring::CollectionDuration::str(), 0}}, 298 {{tstring::SensorPath::str(), 299 {{{tstring::Service::str(), "Service"}, 300 {tstring::Path::str(), 301 "/xyz/openbmc_project/sensors/power/p2"}, 302 {tstring::Metadata::str(), "metadata2"}}}}, 303 {tstring::OperationType::str(), OperationType::single}, 304 {tstring::Id::str(), "MetricId2"}, 305 {tstring::CollectionTimeScope::str(), 306 CollectionTimeScope::point}, 307 {tstring::CollectionDuration::str(), 0}}})))); 308 309 TEST_P(TestReportStore, settingPersistencyToTrueStoresReport) 310 { 311 sut = makeReport(ReportParams()); 312 313 { 314 InSequence seq; 315 EXPECT_CALL(storageMock, remove(to_file_path(sut->getId()))); 316 EXPECT_CALL(checkPoint, Call()); 317 EXPECT_CALL(storageMock, store(to_file_path(sut->getId()), _)) 318 .WillOnce(SaveArg<1>(&storedConfiguration)); 319 } 320 321 setProperty(sut->getPath(), "Persistency", false); 322 checkPoint.Call(); 323 setProperty(sut->getPath(), "Persistency", true); 324 325 const auto& [key, value] = GetParam(); 326 327 ASSERT_THAT(storedConfiguration.at(key), Eq(value)); 328 } 329 330 TEST_P(TestReportStore, reportIsSavedToStorageAfterCreated) 331 { 332 EXPECT_CALL(storageMock, store(to_file_path(ReportParams().reportId()), _)) 333 .WillOnce(SaveArg<1>(&storedConfiguration)); 334 335 sut = makeReport(ReportParams()); 336 337 const auto& [key, value] = GetParam(); 338 339 ASSERT_THAT(storedConfiguration.at(key), Eq(value)); 340 } 341 342 class TestReportValidNames : 343 public TestReport, 344 public WithParamInterface<ReportParams> 345 { 346 public: 347 void SetUp() override 348 {} 349 }; 350 351 INSTANTIATE_TEST_SUITE_P( 352 ValidNames, TestReportValidNames, 353 Values(ReportParams().reportName("Valid_1"), 354 ReportParams().reportName("Valid_1/Valid_2"), 355 ReportParams().reportName("Valid_1/Valid_2/Valid_3"))); 356 357 TEST_P(TestReportValidNames, reportCtorDoesNotThrowOnValidName) 358 { 359 EXPECT_NO_THROW(makeReport(GetParam())); 360 } 361 362 class TestReportInvalidIds : 363 public TestReport, 364 public WithParamInterface<ReportParams> 365 { 366 public: 367 void SetUp() override 368 {} 369 }; 370 371 INSTANTIATE_TEST_SUITE_P(InvalidNames, TestReportInvalidIds, 372 Values(ReportParams().reportId("/"), 373 ReportParams().reportId("/Invalid"), 374 ReportParams().reportId("Invalid/"), 375 ReportParams().reportId("Invalid/Invalid/"), 376 ReportParams().reportId("Invalid?"))); 377 378 TEST_P(TestReportInvalidIds, failsToCreateReportWithInvalidName) 379 { 380 EXPECT_CALL(storageMock, store).Times(0); 381 382 EXPECT_THROW(makeReport(GetParam()), sdbusplus::exception::SdBusError); 383 } 384 385 class TestReportAllReportTypes : 386 public TestReport, 387 public WithParamInterface<ReportParams> 388 { 389 public: 390 void SetUp() override 391 { 392 sut = makeReport(GetParam()); 393 } 394 }; 395 396 INSTANTIATE_TEST_SUITE_P( 397 _, TestReportAllReportTypes, 398 Values(ReportParams().reportingType(ReportingType::onRequest), 399 ReportParams().reportingType(ReportingType::onChange), 400 ReportParams().reportingType(ReportingType::periodic))); 401 402 TEST_P(TestReportAllReportTypes, returnPropertValueOfReportType) 403 { 404 EXPECT_THAT(utils::toReportingType( 405 getProperty<std::string>(sut->getPath(), "ReportingType")), 406 Eq(GetParam().reportingType())); 407 } 408 409 TEST_P(TestReportAllReportTypes, updateReadingsCallEnabledPropertyOff) 410 { 411 clockFake.system.advance(10ms); 412 413 setProperty(sut->getPath(), "Enabled", false); 414 sut->updateReadings(); 415 const auto [timestamp, readings] = 416 getProperty<Readings>(sut->getPath(), "Readings"); 417 418 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Enabled"), Eq(false)); 419 EXPECT_THAT(Milliseconds{timestamp}, Eq(0ms)); 420 } 421 422 TEST_P(TestReportAllReportTypes, updateReadingsCallEnabledPropertyOn) 423 { 424 clockFake.system.advance(10ms); 425 426 sut->updateReadings(); 427 const auto [timestamp, readings] = 428 getProperty<Readings>(sut->getPath(), "Readings"); 429 430 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Enabled"), Eq(true)); 431 EXPECT_THAT(Milliseconds{timestamp}, Eq(systemTimestamp + 10ms)); 432 } 433 434 class TestReportOnRequestType : public TestReport 435 { 436 void SetUp() override 437 { 438 sut = 439 makeReport(ReportParams().reportingType(ReportingType::onRequest)); 440 } 441 }; 442 443 TEST_F(TestReportOnRequestType, updatesReadingTimestamp) 444 { 445 clockFake.system.advance(10ms); 446 447 ASSERT_THAT(update(sut->getPath()), Eq(boost::system::errc::success)); 448 449 const auto [timestamp, readings] = 450 getProperty<Readings>(sut->getPath(), "Readings"); 451 452 EXPECT_THAT(Milliseconds{timestamp}, Eq(systemTimestamp + 10ms)); 453 } 454 455 TEST_F(TestReportOnRequestType, updatesReadingWhenUpdateIsCalled) 456 { 457 ASSERT_THAT(update(sut->getPath()), Eq(boost::system::errc::success)); 458 459 const auto [timestamp, readings] = 460 getProperty<Readings>(sut->getPath(), "Readings"); 461 462 EXPECT_THAT(readings, 463 ElementsAre(std::make_tuple("a"s, "b"s, 17.1, 114u), 464 std::make_tuple("aa"s, "bb"s, 42.0, 74u))); 465 } 466 467 class TestReportNonOnRequestType : 468 public TestReport, 469 public WithParamInterface<ReportParams> 470 { 471 void SetUp() override 472 { 473 sut = makeReport(GetParam()); 474 } 475 }; 476 477 INSTANTIATE_TEST_SUITE_P( 478 _, TestReportNonOnRequestType, 479 Values(ReportParams().reportingType(ReportingType::periodic), 480 ReportParams().reportingType(ReportingType::onChange))); 481 482 TEST_P(TestReportNonOnRequestType, readingsAreNotUpdateOnUpdateCall) 483 { 484 ASSERT_THAT(update(sut->getPath()), Eq(boost::system::errc::success)); 485 486 EXPECT_THAT(getProperty<Readings>(sut->getPath(), "Readings"), 487 Eq(Readings{})); 488 } 489 490 class TestReportNonPeriodicReport : 491 public TestReport, 492 public WithParamInterface<ReportParams> 493 { 494 public: 495 void SetUp() override 496 { 497 sut = makeReport(GetParam()); 498 } 499 }; 500 501 INSTANTIATE_TEST_SUITE_P( 502 _, TestReportNonPeriodicReport, 503 Values(ReportParams().reportingType(ReportingType::onRequest), 504 ReportParams().reportingType(ReportingType::onChange))); 505 506 TEST_P(TestReportNonPeriodicReport, readingsAreNotUpdatedAfterIntervalExpires) 507 { 508 DbusEnvironment::sleepFor(ReportManager::minInterval + 1ms); 509 510 EXPECT_THAT(getProperty<Readings>(sut->getPath(), "Readings"), 511 Eq(Readings{})); 512 } 513 514 class TestReportPeriodicReport : public TestReport 515 { 516 void SetUp() override 517 { 518 sut = makeReport(ReportParams().reportingType(ReportingType::periodic)); 519 } 520 }; 521 522 TEST_F(TestReportPeriodicReport, readingTimestampIsUpdatedAfterIntervalExpires) 523 { 524 clockFake.system.advance(10ms); 525 DbusEnvironment::sleepFor(ReportManager::minInterval + 1ms); 526 527 const auto [timestamp, readings] = 528 getProperty<Readings>(sut->getPath(), "Readings"); 529 530 EXPECT_THAT(Milliseconds{timestamp}, Eq(systemTimestamp + 10ms)); 531 } 532 533 TEST_F(TestReportPeriodicReport, readingsAreUpdatedAfterIntervalExpires) 534 { 535 DbusEnvironment::sleepFor(ReportManager::minInterval + 1ms); 536 537 const auto [timestamp, readings] = 538 getProperty<Readings>(sut->getPath(), "Readings"); 539 540 EXPECT_THAT(readings, 541 ElementsAre(std::make_tuple("a"s, "b"s, 17.1, 114u), 542 std::make_tuple("aa"s, "bb"s, 42.0, 74u))); 543 } 544 545 struct ReportUpdatesReportParams 546 { 547 ReportParams reportParams; 548 std::vector<ReadingData> expectedReadings; 549 bool expectedEnabled; 550 }; 551 552 class TestReportWithReportUpdatesAndLimit : 553 public TestReport, 554 public WithParamInterface<ReportUpdatesReportParams> 555 { 556 void SetUp() override 557 { 558 sut = makeReport(ReportParams(GetParam().reportParams) 559 .reportingType(ReportingType::periodic) 560 .interval(std::chrono::hours(1000))); 561 } 562 }; 563 564 INSTANTIATE_TEST_SUITE_P( 565 _, TestReportWithReportUpdatesAndLimit, 566 Values( 567 ReportUpdatesReportParams{ 568 ReportParams() 569 .reportUpdates(ReportUpdates::appendWrapsWhenFull) 570 .appendLimit(5), 571 std::vector<ReadingData>{{std::make_tuple("aa"s, "bb"s, 42.0, 74u), 572 std::make_tuple("a"s, "b"s, 17.1, 114u), 573 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 574 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 575 std::make_tuple("a"s, "b"s, 17.1, 114u)}}, 576 true}, 577 ReportUpdatesReportParams{ 578 ReportParams() 579 .reportUpdates(ReportUpdates::appendWrapsWhenFull) 580 .appendLimit(4), 581 std::vector<ReadingData>{ 582 {std::make_tuple("a"s, "b"s, 17.1, 114u), 583 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 584 std::make_tuple("a"s, "b"s, 17.1, 114u), 585 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 586 true}, 587 ReportUpdatesReportParams{ 588 ReportParams() 589 .reportUpdates(ReportUpdates::appendWrapsWhenFull) 590 .appendLimit(0), 591 std::vector<ReadingData>{}, true}, 592 ReportUpdatesReportParams{ 593 ReportParams() 594 .reportUpdates(ReportUpdates::appendStopsWhenFull) 595 .appendLimit(10), 596 std::vector<ReadingData>{ 597 {std::make_tuple("a"s, "b"s, 17.1, 114u), 598 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 599 std::make_tuple("a"s, "b"s, 17.1, 114u), 600 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 601 std::make_tuple("a"s, "b"s, 17.1, 114u), 602 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 603 std::make_tuple("a"s, "b"s, 17.1, 114u), 604 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 605 true}, 606 ReportUpdatesReportParams{ 607 ReportParams() 608 .reportUpdates(ReportUpdates::appendStopsWhenFull) 609 .appendLimit(5), 610 std::vector<ReadingData>{{std::make_tuple("a"s, "b"s, 17.1, 114u), 611 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 612 std::make_tuple("a"s, "b"s, 17.1, 114u), 613 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 614 std::make_tuple("a"s, "b"s, 17.1, 114u)}}, 615 false}, 616 ReportUpdatesReportParams{ 617 ReportParams() 618 .reportUpdates(ReportUpdates::appendStopsWhenFull) 619 .appendLimit(4), 620 std::vector<ReadingData>{ 621 {std::make_tuple("a"s, "b"s, 17.1, 114u), 622 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 623 std::make_tuple("a"s, "b"s, 17.1, 114u), 624 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 625 false}, 626 ReportUpdatesReportParams{ 627 ReportParams() 628 .reportUpdates(ReportUpdates::appendStopsWhenFull) 629 .appendLimit(0), 630 std::vector<ReadingData>{}, false}, 631 ReportUpdatesReportParams{ 632 ReportParams() 633 .reportUpdates(ReportUpdates::overwrite) 634 .appendLimit(500), 635 std::vector<ReadingData>{ 636 {std::make_tuple("a"s, "b"s, 17.1, 114u), 637 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 638 true}, 639 ReportUpdatesReportParams{ 640 ReportParams() 641 .reportUpdates(ReportUpdates::overwrite) 642 .appendLimit(1), 643 std::vector<ReadingData>{ 644 {std::make_tuple("a"s, "b"s, 17.1, 114u), 645 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 646 true}, 647 ReportUpdatesReportParams{ 648 ReportParams() 649 .reportUpdates(ReportUpdates::overwrite) 650 .appendLimit(0), 651 std::vector<ReadingData>{ 652 {std::make_tuple("a"s, "b"s, 17.1, 114u), 653 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 654 true})); 655 656 TEST_P(TestReportWithReportUpdatesAndLimit, 657 readingsAreUpdatedAfterIntervalExpires) 658 { 659 for (int i = 0; i < 4; i++) 660 { 661 sut->updateReadings(); 662 } 663 664 const auto [timestamp, readings] = 665 getProperty<Readings>(sut->getPath(), "Readings"); 666 const auto enabled = getProperty<bool>(sut->getPath(), "Enabled"); 667 668 EXPECT_THAT(readings, ElementsAreArray(GetParam().expectedReadings)); 669 EXPECT_EQ(enabled, GetParam().expectedEnabled); 670 } 671 672 class TestReportInitialization : public TestReport 673 { 674 public: 675 void SetUp() override 676 {} 677 678 void monitorProc(sdbusplus::message::message& msg) 679 { 680 std::string iface; 681 std::vector<std::pair<std::string, std::variant<Readings>>> 682 changed_properties; 683 std::vector<std::string> invalidated_properties; 684 685 msg.read(iface, changed_properties, invalidated_properties); 686 687 if (iface == Report::reportIfaceName) 688 { 689 for (const auto& [name, value] : changed_properties) 690 { 691 if (name == "Readings") 692 { 693 readingsUpdated.Call(); 694 } 695 } 696 } 697 } 698 699 void makeMonitor() 700 { 701 monitor = std::make_unique<sdbusplus::bus::match_t>( 702 *DbusEnvironment::getBus(), 703 sdbusplus::bus::match::rules::propertiesChanged( 704 sut->getPath(), Report::reportIfaceName), 705 [this](auto& msg) { monitorProc(msg); }); 706 } 707 708 std::unique_ptr<sdbusplus::bus::match_t> monitor; 709 MockFunction<void()> readingsUpdated; 710 }; 711 712 TEST_F(TestReportInitialization, 713 metricsAreInitializedWhenEnabledReportConstructed) 714 { 715 initMetricMocks(defaultParams.metricParameters()); 716 for (auto& metric : metricMocks) 717 { 718 EXPECT_CALL(*metric, initialize()).Times(1); 719 } 720 sut = makeReport(defaultParams.enabled(true)); 721 } 722 723 TEST_F(TestReportInitialization, 724 metricsAreNotInitializedWhenDisabledReportConstructed) 725 { 726 initMetricMocks(defaultParams.metricParameters()); 727 for (auto& metric : metricMocks) 728 { 729 EXPECT_CALL(*metric, initialize()).Times(0); 730 } 731 sut = makeReport(defaultParams.enabled(false)); 732 } 733 734 TEST_F(TestReportInitialization, 735 emitReadingsUpdateIsTrueReadingsPropertiesChangedSingalEmits) 736 { 737 EXPECT_CALL(readingsUpdated, Call()) 738 .WillOnce( 739 InvokeWithoutArgs(DbusEnvironment::setPromise("readingsUpdated"))); 740 741 const auto elapsed = DbusEnvironment::measureTime([this] { 742 sut = 743 makeReport(defaultParams.reportingType(ReportingType::periodic) 744 .reportActions({ReportAction::emitsReadingsUpdate})); 745 makeMonitor(); 746 EXPECT_TRUE(DbusEnvironment::waitForFuture("readingsUpdated")); 747 }); 748 749 EXPECT_THAT(elapsed, AllOf(Ge(defaultParams.interval()), 750 Lt(defaultParams.interval() * 2))); 751 } 752 753 TEST_F(TestReportInitialization, 754 emitReadingsUpdateIsFalseReadingsPropertiesChangesSigalDoesNotEmits) 755 { 756 EXPECT_CALL(readingsUpdated, Call()).Times(0); 757 758 sut = makeReport( 759 defaultParams.reportingType(ReportingType::periodic).reportActions({})); 760 makeMonitor(); 761 DbusEnvironment::sleepFor(defaultParams.interval() * 2); 762 } 763 764 TEST_F(TestReportInitialization, appendLimitDeducedProperly) 765 { 766 sut = makeReport( 767 ReportParams().appendLimit(std::numeric_limits<uint64_t>::max())); 768 auto appendLimit = getProperty<uint64_t>(sut->getPath(), "AppendLimit"); 769 EXPECT_EQ(appendLimit, 2ull); 770 } 771 772 TEST_F(TestReportInitialization, appendLimitSetToUintMaxIsStoredCorrectly) 773 { 774 nlohmann::json storedConfiguration; 775 776 EXPECT_CALL(storageMock, store(to_file_path(ReportParams().reportId()), _)) 777 .WillOnce(SaveArg<1>(&storedConfiguration)); 778 779 sut = makeReport( 780 ReportParams().appendLimit(std::numeric_limits<uint64_t>::max())); 781 782 ASSERT_THAT(storedConfiguration.at("AppendLimit"), 783 Eq(std::numeric_limits<uint64_t>::max())); 784 } 785