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), params.triggerIds()); 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 EXPECT_THAT( 168 getProperty<std::vector<std::string>>(sut->getPath(), "TriggerIds"), 169 Eq(std::vector<std::string>())); 170 } 171 172 TEST_F(TestReport, readingsAreInitialyEmpty) 173 { 174 EXPECT_THAT(getProperty<Readings>(sut->getPath(), "Readings"), 175 Eq(Readings{})); 176 } 177 178 TEST_F(TestReport, setEnabledWithNewValue) 179 { 180 bool newValue = !defaultParams.enabled(); 181 EXPECT_THAT(setProperty(sut->getPath(), "Enabled", newValue).value(), 182 Eq(boost::system::errc::success)); 183 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Enabled"), Eq(newValue)); 184 } 185 186 TEST_F(TestReport, setIntervalWithValidValue) 187 { 188 uint64_t newValue = defaultParams.interval().count() + 1; 189 EXPECT_THAT(setProperty(sut->getPath(), "Interval", newValue).value(), 190 Eq(boost::system::errc::success)); 191 EXPECT_THAT(getProperty<uint64_t>(sut->getPath(), "Interval"), 192 Eq(newValue)); 193 } 194 195 TEST_F( 196 TestReport, 197 settingIntervalWithInvalidValueDoesNotChangePropertyAndReturnsInvalidArgument) 198 { 199 uint64_t newValue = defaultParams.interval().count() - 1; 200 EXPECT_THAT(setProperty(sut->getPath(), "Interval", newValue).value(), 201 Eq(boost::system::errc::invalid_argument)); 202 EXPECT_THAT(getProperty<uint64_t>(sut->getPath(), "Interval"), 203 Eq(defaultParams.interval().count())); 204 } 205 206 TEST_F(TestReport, settingEmitsReadingsUpdateHaveNoEffect) 207 { 208 EXPECT_THAT( 209 setProperty(sut->getPath(), "EmitsReadingsUpdate", true).value(), 210 Eq(boost::system::errc::read_only_file_system)); 211 EXPECT_THAT(getProperty<bool>(sut->getPath(), "EmitsReadingsUpdate"), 212 Eq(utils::contains(defaultParams.reportActions(), 213 ReportAction::emitsReadingsUpdate))); 214 } 215 216 TEST_F(TestReport, settingLogToMetricReportCollectionHaveNoEffect) 217 { 218 EXPECT_THAT( 219 setProperty(sut->getPath(), "LogToMetricReportsCollection", true) 220 .value(), 221 Eq(boost::system::errc::read_only_file_system)); 222 EXPECT_THAT( 223 getProperty<bool>(sut->getPath(), "LogToMetricReportsCollection"), 224 Eq(utils::contains(defaultParams.reportActions(), 225 ReportAction::logToMetricReportsCollection))); 226 } 227 228 TEST_F(TestReport, settingPersistencyToFalseRemovesReportFromStorage) 229 { 230 EXPECT_CALL(storageMock, remove(to_file_path(sut->getId()))); 231 232 bool persistency = false; 233 EXPECT_THAT(setProperty(sut->getPath(), "Persistency", persistency).value(), 234 Eq(boost::system::errc::success)); 235 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistency"), 236 Eq(persistency)); 237 } 238 239 TEST_F(TestReport, deleteReport) 240 { 241 EXPECT_CALL(*reportManagerMock, removeReport(sut.get())); 242 auto ec = deleteReport(sut->getPath()); 243 EXPECT_THAT(ec, Eq(boost::system::errc::success)); 244 } 245 246 TEST_F(TestReport, deletingNonExistingReportReturnInvalidRequestDescriptor) 247 { 248 auto ec = deleteReport(Report::reportDir + "NonExisting"s); 249 EXPECT_THAT(ec.value(), Eq(EBADR)); 250 } 251 252 TEST_F(TestReport, deleteReportExpectThatFileIsRemoveFromStorage) 253 { 254 EXPECT_CALL(storageMock, remove(to_file_path(sut->getId()))); 255 auto ec = deleteReport(sut->getPath()); 256 EXPECT_THAT(ec, Eq(boost::system::errc::success)); 257 } 258 259 TEST_F(TestReport, triggerIdsAreUpdatedProperly) 260 { 261 sut->updateTriggerIds("trigger1", TriggerIdUpdate::Add); 262 EXPECT_THAT( 263 getProperty<std::vector<std::string>>(sut->getPath(), "TriggerIds"), 264 UnorderedElementsAre("trigger1")); 265 266 sut->updateTriggerIds("trigger2", TriggerIdUpdate::Add); 267 EXPECT_THAT( 268 getProperty<std::vector<std::string>>(sut->getPath(), "TriggerIds"), 269 UnorderedElementsAre("trigger1", "trigger2")); 270 271 sut->updateTriggerIds("trigger3", TriggerIdUpdate::Add); 272 EXPECT_THAT( 273 getProperty<std::vector<std::string>>(sut->getPath(), "TriggerIds"), 274 UnorderedElementsAre("trigger1", "trigger2", "trigger3")); 275 276 sut->updateTriggerIds("trigger1", TriggerIdUpdate::Remove); 277 EXPECT_THAT( 278 getProperty<std::vector<std::string>>(sut->getPath(), "TriggerIds"), 279 UnorderedElementsAre("trigger2", "trigger3")); 280 } 281 282 TEST_F(TestReport, successWhenRemovingSameTriggerIdMultipleTimes) 283 { 284 sut->updateTriggerIds("trigger1", TriggerIdUpdate::Add); 285 sut->updateTriggerIds("trigger2", TriggerIdUpdate::Add); 286 sut->updateTriggerIds("trigger1", TriggerIdUpdate::Remove); 287 sut->updateTriggerIds("trigger1", TriggerIdUpdate::Remove); 288 EXPECT_THAT( 289 getProperty<std::vector<std::string>>(sut->getPath(), "TriggerIds"), 290 UnorderedElementsAre("trigger2")); 291 } 292 293 TEST_F(TestReport, successWhenRemovingNonExistingTriggerId) 294 { 295 sut->updateTriggerIds("trigger1", TriggerIdUpdate::Add); 296 sut->updateTriggerIds("notTrigger", TriggerIdUpdate::Remove); 297 EXPECT_THAT( 298 getProperty<std::vector<std::string>>(sut->getPath(), "TriggerIds"), 299 UnorderedElementsAre("trigger1")); 300 } 301 302 TEST_F(TestReport, noDuplicatesWhenSameTriggerIdIsAdded) 303 { 304 sut->updateTriggerIds("trigger1", TriggerIdUpdate::Add); 305 sut->updateTriggerIds("trigger2", TriggerIdUpdate::Add); 306 sut->updateTriggerIds("trigger1", TriggerIdUpdate::Add); 307 EXPECT_THAT( 308 getProperty<std::vector<std::string>>(sut->getPath(), "TriggerIds"), 309 UnorderedElementsAre("trigger1", "trigger2")); 310 } 311 312 class TestReportStore : 313 public TestReport, 314 public WithParamInterface<std::pair<std::string, nlohmann::json>> 315 { 316 public: 317 void SetUp() override 318 {} 319 320 nlohmann::json storedConfiguration; 321 }; 322 323 INSTANTIATE_TEST_SUITE_P( 324 _, TestReportStore, 325 Values(std::make_pair("Enabled"s, nlohmann::json(ReportParams().enabled())), 326 std::make_pair("Version"s, nlohmann::json(6)), 327 std::make_pair("Id"s, nlohmann::json(ReportParams().reportId())), 328 std::make_pair("Name"s, nlohmann::json(ReportParams().reportName())), 329 std::make_pair("ReportingType", 330 nlohmann::json(ReportParams().reportingType())), 331 std::make_pair("ReportActions", nlohmann::json(utils::transform( 332 ReportParams().reportActions(), 333 [](const auto v) { 334 return utils::toUnderlying( 335 v); 336 }))), 337 std::make_pair("Interval", 338 nlohmann::json(ReportParams().interval().count())), 339 std::make_pair("AppendLimit", 340 nlohmann::json(ReportParams().appendLimit())), 341 std::make_pair( 342 "ReadingParameters", 343 nlohmann::json( 344 {{{tstring::SensorPath::str(), 345 {{{tstring::Service::str(), "Service"}, 346 {tstring::Path::str(), 347 "/xyz/openbmc_project/sensors/power/p1"}, 348 {tstring::Metadata::str(), "metadata1"}}}}, 349 {tstring::OperationType::str(), OperationType::single}, 350 {tstring::Id::str(), "MetricId1"}, 351 {tstring::CollectionTimeScope::str(), 352 CollectionTimeScope::point}, 353 {tstring::CollectionDuration::str(), 0}}, 354 {{tstring::SensorPath::str(), 355 {{{tstring::Service::str(), "Service"}, 356 {tstring::Path::str(), 357 "/xyz/openbmc_project/sensors/power/p2"}, 358 {tstring::Metadata::str(), "metadata2"}}}}, 359 {tstring::OperationType::str(), OperationType::single}, 360 {tstring::Id::str(), "MetricId2"}, 361 {tstring::CollectionTimeScope::str(), 362 CollectionTimeScope::point}, 363 {tstring::CollectionDuration::str(), 0}}})))); 364 365 TEST_P(TestReportStore, settingPersistencyToTrueStoresReport) 366 { 367 sut = makeReport(ReportParams()); 368 369 { 370 InSequence seq; 371 EXPECT_CALL(storageMock, remove(to_file_path(sut->getId()))); 372 EXPECT_CALL(checkPoint, Call()); 373 EXPECT_CALL(storageMock, store(to_file_path(sut->getId()), _)) 374 .WillOnce(SaveArg<1>(&storedConfiguration)); 375 } 376 377 setProperty(sut->getPath(), "Persistency", false); 378 checkPoint.Call(); 379 setProperty(sut->getPath(), "Persistency", true); 380 381 const auto& [key, value] = GetParam(); 382 383 ASSERT_THAT(storedConfiguration.at(key), Eq(value)); 384 } 385 386 TEST_P(TestReportStore, reportIsSavedToStorageAfterCreated) 387 { 388 EXPECT_CALL(storageMock, store(to_file_path(ReportParams().reportId()), _)) 389 .WillOnce(SaveArg<1>(&storedConfiguration)); 390 391 sut = makeReport(ReportParams()); 392 393 const auto& [key, value] = GetParam(); 394 395 ASSERT_THAT(storedConfiguration.at(key), Eq(value)); 396 } 397 398 class TestReportValidNames : 399 public TestReport, 400 public WithParamInterface<ReportParams> 401 { 402 public: 403 void SetUp() override 404 {} 405 }; 406 407 INSTANTIATE_TEST_SUITE_P( 408 ValidNames, TestReportValidNames, 409 Values(ReportParams().reportName("Valid_1"), 410 ReportParams().reportName("Valid_1/Valid_2"), 411 ReportParams().reportName("Valid_1/Valid_2/Valid_3"))); 412 413 TEST_P(TestReportValidNames, reportCtorDoesNotThrowOnValidName) 414 { 415 EXPECT_NO_THROW(makeReport(GetParam())); 416 } 417 418 class TestReportInvalidIds : 419 public TestReport, 420 public WithParamInterface<ReportParams> 421 { 422 public: 423 void SetUp() override 424 {} 425 }; 426 427 INSTANTIATE_TEST_SUITE_P(InvalidNames, TestReportInvalidIds, 428 Values(ReportParams().reportId("/"), 429 ReportParams().reportId("/Invalid"), 430 ReportParams().reportId("Invalid/"), 431 ReportParams().reportId("Invalid/Invalid/"), 432 ReportParams().reportId("Invalid?"))); 433 434 TEST_P(TestReportInvalidIds, failsToCreateReportWithInvalidName) 435 { 436 EXPECT_CALL(storageMock, store).Times(0); 437 438 EXPECT_THROW(makeReport(GetParam()), sdbusplus::exception::SdBusError); 439 } 440 441 class TestReportAllReportTypes : 442 public TestReport, 443 public WithParamInterface<ReportParams> 444 { 445 public: 446 void SetUp() override 447 { 448 sut = makeReport(GetParam()); 449 } 450 }; 451 452 INSTANTIATE_TEST_SUITE_P( 453 _, TestReportAllReportTypes, 454 Values(ReportParams().reportingType(ReportingType::onRequest), 455 ReportParams().reportingType(ReportingType::onChange), 456 ReportParams().reportingType(ReportingType::periodic))); 457 458 TEST_P(TestReportAllReportTypes, returnPropertValueOfReportType) 459 { 460 EXPECT_THAT(utils::toReportingType( 461 getProperty<std::string>(sut->getPath(), "ReportingType")), 462 Eq(GetParam().reportingType())); 463 } 464 465 TEST_P(TestReportAllReportTypes, updateReadingsCallEnabledPropertyOff) 466 { 467 clockFake.system.advance(10ms); 468 469 setProperty(sut->getPath(), "Enabled", false); 470 sut->updateReadings(); 471 const auto [timestamp, readings] = 472 getProperty<Readings>(sut->getPath(), "Readings"); 473 474 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Enabled"), Eq(false)); 475 EXPECT_THAT(Milliseconds{timestamp}, Eq(0ms)); 476 } 477 478 TEST_P(TestReportAllReportTypes, updateReadingsCallEnabledPropertyOn) 479 { 480 clockFake.system.advance(10ms); 481 482 sut->updateReadings(); 483 const auto [timestamp, readings] = 484 getProperty<Readings>(sut->getPath(), "Readings"); 485 486 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Enabled"), Eq(true)); 487 EXPECT_THAT(Milliseconds{timestamp}, Eq(systemTimestamp + 10ms)); 488 } 489 490 class TestReportOnRequestType : public TestReport 491 { 492 void SetUp() override 493 { 494 sut = 495 makeReport(ReportParams().reportingType(ReportingType::onRequest)); 496 } 497 }; 498 499 TEST_F(TestReportOnRequestType, updatesReadingTimestamp) 500 { 501 clockFake.system.advance(10ms); 502 503 ASSERT_THAT(update(sut->getPath()), Eq(boost::system::errc::success)); 504 505 const auto [timestamp, readings] = 506 getProperty<Readings>(sut->getPath(), "Readings"); 507 508 EXPECT_THAT(Milliseconds{timestamp}, Eq(systemTimestamp + 10ms)); 509 } 510 511 TEST_F(TestReportOnRequestType, updatesReadingWhenUpdateIsCalled) 512 { 513 ASSERT_THAT(update(sut->getPath()), Eq(boost::system::errc::success)); 514 515 const auto [timestamp, readings] = 516 getProperty<Readings>(sut->getPath(), "Readings"); 517 518 EXPECT_THAT(readings, 519 ElementsAre(std::make_tuple("a"s, "b"s, 17.1, 114u), 520 std::make_tuple("aa"s, "bb"s, 42.0, 74u))); 521 } 522 523 class TestReportNonOnRequestType : 524 public TestReport, 525 public WithParamInterface<ReportParams> 526 { 527 void SetUp() override 528 { 529 sut = makeReport(GetParam()); 530 } 531 }; 532 533 INSTANTIATE_TEST_SUITE_P( 534 _, TestReportNonOnRequestType, 535 Values(ReportParams().reportingType(ReportingType::periodic), 536 ReportParams().reportingType(ReportingType::onChange))); 537 538 TEST_P(TestReportNonOnRequestType, readingsAreNotUpdateOnUpdateCall) 539 { 540 ASSERT_THAT(update(sut->getPath()), Eq(boost::system::errc::success)); 541 542 EXPECT_THAT(getProperty<Readings>(sut->getPath(), "Readings"), 543 Eq(Readings{})); 544 } 545 546 class TestReportNonPeriodicReport : 547 public TestReport, 548 public WithParamInterface<ReportParams> 549 { 550 public: 551 void SetUp() override 552 { 553 sut = makeReport(GetParam()); 554 } 555 }; 556 557 INSTANTIATE_TEST_SUITE_P( 558 _, TestReportNonPeriodicReport, 559 Values(ReportParams().reportingType(ReportingType::onRequest), 560 ReportParams().reportingType(ReportingType::onChange))); 561 562 TEST_P(TestReportNonPeriodicReport, readingsAreNotUpdatedAfterIntervalExpires) 563 { 564 DbusEnvironment::sleepFor(ReportManager::minInterval + 1ms); 565 566 EXPECT_THAT(getProperty<Readings>(sut->getPath(), "Readings"), 567 Eq(Readings{})); 568 } 569 570 class TestReportPeriodicReport : public TestReport 571 { 572 void SetUp() override 573 { 574 sut = makeReport(ReportParams().reportingType(ReportingType::periodic)); 575 } 576 }; 577 578 TEST_F(TestReportPeriodicReport, readingTimestampIsUpdatedAfterIntervalExpires) 579 { 580 clockFake.system.advance(10ms); 581 DbusEnvironment::sleepFor(ReportManager::minInterval + 1ms); 582 583 const auto [timestamp, readings] = 584 getProperty<Readings>(sut->getPath(), "Readings"); 585 586 EXPECT_THAT(Milliseconds{timestamp}, Eq(systemTimestamp + 10ms)); 587 } 588 589 TEST_F(TestReportPeriodicReport, readingsAreUpdatedAfterIntervalExpires) 590 { 591 DbusEnvironment::sleepFor(ReportManager::minInterval + 1ms); 592 593 const auto [timestamp, readings] = 594 getProperty<Readings>(sut->getPath(), "Readings"); 595 596 EXPECT_THAT(readings, 597 ElementsAre(std::make_tuple("a"s, "b"s, 17.1, 114u), 598 std::make_tuple("aa"s, "bb"s, 42.0, 74u))); 599 } 600 601 struct ReportUpdatesReportParams 602 { 603 ReportParams reportParams; 604 std::vector<ReadingData> expectedReadings; 605 bool expectedEnabled; 606 }; 607 608 class TestReportWithReportUpdatesAndLimit : 609 public TestReport, 610 public WithParamInterface<ReportUpdatesReportParams> 611 { 612 void SetUp() override 613 { 614 sut = makeReport(ReportParams(GetParam().reportParams) 615 .reportingType(ReportingType::periodic) 616 .interval(std::chrono::hours(1000))); 617 } 618 }; 619 620 INSTANTIATE_TEST_SUITE_P( 621 _, TestReportWithReportUpdatesAndLimit, 622 Values( 623 ReportUpdatesReportParams{ 624 ReportParams() 625 .reportUpdates(ReportUpdates::appendWrapsWhenFull) 626 .appendLimit(5), 627 std::vector<ReadingData>{{std::make_tuple("aa"s, "bb"s, 42.0, 74u), 628 std::make_tuple("a"s, "b"s, 17.1, 114u), 629 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 630 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 631 std::make_tuple("a"s, "b"s, 17.1, 114u)}}, 632 true}, 633 ReportUpdatesReportParams{ 634 ReportParams() 635 .reportUpdates(ReportUpdates::appendWrapsWhenFull) 636 .appendLimit(4), 637 std::vector<ReadingData>{ 638 {std::make_tuple("a"s, "b"s, 17.1, 114u), 639 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 640 std::make_tuple("a"s, "b"s, 17.1, 114u), 641 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 642 true}, 643 ReportUpdatesReportParams{ 644 ReportParams() 645 .reportUpdates(ReportUpdates::appendWrapsWhenFull) 646 .appendLimit(0), 647 std::vector<ReadingData>{}, true}, 648 ReportUpdatesReportParams{ 649 ReportParams() 650 .reportUpdates(ReportUpdates::appendStopsWhenFull) 651 .appendLimit(10), 652 std::vector<ReadingData>{ 653 {std::make_tuple("a"s, "b"s, 17.1, 114u), 654 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 655 std::make_tuple("a"s, "b"s, 17.1, 114u), 656 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 657 std::make_tuple("a"s, "b"s, 17.1, 114u), 658 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 659 std::make_tuple("a"s, "b"s, 17.1, 114u), 660 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 661 true}, 662 ReportUpdatesReportParams{ 663 ReportParams() 664 .reportUpdates(ReportUpdates::appendStopsWhenFull) 665 .appendLimit(5), 666 std::vector<ReadingData>{{std::make_tuple("a"s, "b"s, 17.1, 114u), 667 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 668 std::make_tuple("a"s, "b"s, 17.1, 114u), 669 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 670 std::make_tuple("a"s, "b"s, 17.1, 114u)}}, 671 false}, 672 ReportUpdatesReportParams{ 673 ReportParams() 674 .reportUpdates(ReportUpdates::appendStopsWhenFull) 675 .appendLimit(4), 676 std::vector<ReadingData>{ 677 {std::make_tuple("a"s, "b"s, 17.1, 114u), 678 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 679 std::make_tuple("a"s, "b"s, 17.1, 114u), 680 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 681 false}, 682 ReportUpdatesReportParams{ 683 ReportParams() 684 .reportUpdates(ReportUpdates::appendStopsWhenFull) 685 .appendLimit(0), 686 std::vector<ReadingData>{}, false}, 687 ReportUpdatesReportParams{ 688 ReportParams() 689 .reportUpdates(ReportUpdates::overwrite) 690 .appendLimit(500), 691 std::vector<ReadingData>{ 692 {std::make_tuple("a"s, "b"s, 17.1, 114u), 693 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 694 true}, 695 ReportUpdatesReportParams{ 696 ReportParams() 697 .reportUpdates(ReportUpdates::overwrite) 698 .appendLimit(1), 699 std::vector<ReadingData>{ 700 {std::make_tuple("a"s, "b"s, 17.1, 114u), 701 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 702 true}, 703 ReportUpdatesReportParams{ 704 ReportParams() 705 .reportUpdates(ReportUpdates::overwrite) 706 .appendLimit(0), 707 std::vector<ReadingData>{ 708 {std::make_tuple("a"s, "b"s, 17.1, 114u), 709 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 710 true})); 711 712 TEST_P(TestReportWithReportUpdatesAndLimit, 713 readingsAreUpdatedAfterIntervalExpires) 714 { 715 for (int i = 0; i < 4; i++) 716 { 717 sut->updateReadings(); 718 } 719 720 const auto [timestamp, readings] = 721 getProperty<Readings>(sut->getPath(), "Readings"); 722 const auto enabled = getProperty<bool>(sut->getPath(), "Enabled"); 723 724 EXPECT_THAT(readings, ElementsAreArray(GetParam().expectedReadings)); 725 EXPECT_EQ(enabled, GetParam().expectedEnabled); 726 } 727 728 class TestReportInitialization : public TestReport 729 { 730 public: 731 void SetUp() override 732 {} 733 734 void monitorProc(sdbusplus::message::message& msg) 735 { 736 std::string iface; 737 std::vector<std::pair<std::string, std::variant<Readings>>> 738 changed_properties; 739 std::vector<std::string> invalidated_properties; 740 741 msg.read(iface, changed_properties, invalidated_properties); 742 743 if (iface == Report::reportIfaceName) 744 { 745 for (const auto& [name, value] : changed_properties) 746 { 747 if (name == "Readings") 748 { 749 readingsUpdated.Call(); 750 } 751 } 752 } 753 } 754 755 void makeMonitor() 756 { 757 monitor = std::make_unique<sdbusplus::bus::match_t>( 758 *DbusEnvironment::getBus(), 759 sdbusplus::bus::match::rules::propertiesChanged( 760 sut->getPath(), Report::reportIfaceName), 761 [this](auto& msg) { monitorProc(msg); }); 762 } 763 764 std::unique_ptr<sdbusplus::bus::match_t> monitor; 765 MockFunction<void()> readingsUpdated; 766 }; 767 768 TEST_F(TestReportInitialization, 769 metricsAreInitializedWhenEnabledReportConstructed) 770 { 771 initMetricMocks(defaultParams.metricParameters()); 772 for (auto& metric : metricMocks) 773 { 774 EXPECT_CALL(*metric, initialize()).Times(1); 775 } 776 sut = makeReport(defaultParams.enabled(true)); 777 } 778 779 TEST_F(TestReportInitialization, 780 metricsAreNotInitializedWhenDisabledReportConstructed) 781 { 782 initMetricMocks(defaultParams.metricParameters()); 783 for (auto& metric : metricMocks) 784 { 785 EXPECT_CALL(*metric, initialize()).Times(0); 786 } 787 sut = makeReport(defaultParams.enabled(false)); 788 } 789 790 TEST_F(TestReportInitialization, 791 emitReadingsUpdateIsTrueReadingsPropertiesChangedSingalEmits) 792 { 793 EXPECT_CALL(readingsUpdated, Call()) 794 .WillOnce( 795 InvokeWithoutArgs(DbusEnvironment::setPromise("readingsUpdated"))); 796 797 const auto elapsed = DbusEnvironment::measureTime([this] { 798 sut = 799 makeReport(defaultParams.reportingType(ReportingType::periodic) 800 .reportActions({ReportAction::emitsReadingsUpdate})); 801 makeMonitor(); 802 EXPECT_TRUE(DbusEnvironment::waitForFuture("readingsUpdated")); 803 }); 804 805 EXPECT_THAT(elapsed, AllOf(Ge(defaultParams.interval()), 806 Lt(defaultParams.interval() * 2))); 807 } 808 809 TEST_F(TestReportInitialization, 810 emitReadingsUpdateIsFalseReadingsPropertiesChangesSigalDoesNotEmits) 811 { 812 EXPECT_CALL(readingsUpdated, Call()).Times(0); 813 814 sut = makeReport( 815 defaultParams.reportingType(ReportingType::periodic).reportActions({})); 816 makeMonitor(); 817 DbusEnvironment::sleepFor(defaultParams.interval() * 2); 818 } 819 820 TEST_F(TestReportInitialization, appendLimitDeducedProperly) 821 { 822 sut = makeReport( 823 ReportParams().appendLimit(std::numeric_limits<uint64_t>::max())); 824 auto appendLimit = getProperty<uint64_t>(sut->getPath(), "AppendLimit"); 825 EXPECT_EQ(appendLimit, 2ull); 826 } 827 828 TEST_F(TestReportInitialization, appendLimitSetToUintMaxIsStoredCorrectly) 829 { 830 nlohmann::json storedConfiguration; 831 832 EXPECT_CALL(storageMock, store(to_file_path(ReportParams().reportId()), _)) 833 .WillOnce(SaveArg<1>(&storedConfiguration)); 834 835 sut = makeReport( 836 ReportParams().appendLimit(std::numeric_limits<uint64_t>::max())); 837 838 ASSERT_THAT(storedConfiguration.at("AppendLimit"), 839 Eq(std::numeric_limits<uint64_t>::max())); 840 } 841 842 TEST_F(TestReportInitialization, triggerIdsPropertyIsInitialzed) 843 { 844 sut = makeReport(ReportParams().triggerIds({"trigger1", "trigger2"})); 845 846 EXPECT_THAT( 847 getProperty<std::vector<std::string>>(sut->getPath(), "TriggerIds"), 848 UnorderedElementsAre("trigger1", "trigger2")); 849 } 850