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