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_factory_mock.hpp" 10 #include "mocks/report_manager_mock.hpp" 11 #include "params/report_params.hpp" 12 #include "report.hpp" 13 #include "report_manager.hpp" 14 #include "utils/clock.hpp" 15 #include "utils/contains.hpp" 16 #include "utils/conv_container.hpp" 17 #include "utils/messanger.hpp" 18 #include "utils/transform.hpp" 19 #include "utils/tstring.hpp" 20 21 #include <sdbusplus/exception.hpp> 22 23 using namespace testing; 24 using namespace std::literals::string_literals; 25 using namespace std::chrono_literals; 26 namespace tstring = utils::tstring; 27 28 constexpr Milliseconds systemTimestamp = 55ms; 29 30 namespace 31 { 32 33 ReportParams defaultParams() 34 { 35 return ReportParams(); 36 } 37 38 ReportParams defaultOnChangeParams() 39 { 40 return defaultParams().reportingType(ReportingType::onChange); 41 } 42 43 } // namespace 44 45 class TestReport : public Test 46 { 47 public: 48 std::unique_ptr<ReportManagerMock> reportManagerMock = 49 std::make_unique<NiceMock<ReportManagerMock>>(); 50 std::unique_ptr<ReportFactoryMock> reportFactoryMock = 51 std::make_unique<NiceMock<ReportFactoryMock>>(); 52 NiceMock<StorageMock> storageMock; 53 std::vector<std::shared_ptr<MetricMock>> metricMocks; 54 std::unique_ptr<ClockFake> clockFakePtr = std::make_unique<ClockFake>(); 55 ClockFake& clockFake = *clockFakePtr; 56 std::unique_ptr<Report> sut; 57 utils::Messanger messanger; 58 59 MockFunction<void()> checkPoint; 60 61 TestReport() : messanger(DbusEnvironment::getIoc()) 62 { 63 clockFake.system.set(systemTimestamp); 64 } 65 66 void initMetricMocks( 67 const std::vector<LabeledMetricParameters>& metricParameters) 68 { 69 for (auto i = metricMocks.size(); i < metricParameters.size(); ++i) 70 { 71 metricMocks.emplace_back(std::make_shared<NiceMock<MetricMock>>()); 72 } 73 metricMocks.resize(metricParameters.size()); 74 75 std::vector<MetricValue> readings{{MetricValue{"a", "b", 17.1, 114}, 76 MetricValue{"aa", "bb", 42.0, 74}}}; 77 readings.resize(metricParameters.size()); 78 79 for (size_t i = 0; i < metricParameters.size(); ++i) 80 { 81 ON_CALL(*metricMocks[i], getReadings()) 82 .WillByDefault(Return(std::vector({readings[i]}))); 83 ON_CALL(*metricMocks[i], dumpConfiguration()) 84 .WillByDefault(Return(metricParameters[i])); 85 } 86 } 87 88 std::vector<std::shared_ptr<interfaces::Metric>> 89 getMetricsFromReadingParams(const ReadingParameters& params) 90 { 91 const auto metricParameters = 92 reportFactoryMock->convertMetricParams(params); 93 std::vector<std::shared_ptr<MetricMock>> metricMocks; 94 95 for (size_t i = 0; i < metricParameters.size(); ++i) 96 { 97 metricMocks.emplace_back(std::make_shared<NiceMock<MetricMock>>()); 98 ON_CALL(*metricMocks[i], dumpConfiguration()) 99 .WillByDefault(Return(metricParameters[i])); 100 } 101 102 return utils::convContainer<std::shared_ptr<interfaces::Metric>>( 103 metricMocks); 104 } 105 106 void SetUp() override 107 { 108 sut = makeReport(defaultParams()); 109 } 110 111 static interfaces::JsonStorage::FilePath to_file_path(std::string id) 112 { 113 return interfaces::JsonStorage::FilePath( 114 std::to_string(std::hash<std::string>{}(id))); 115 } 116 117 std::unique_ptr<Report> makeReport(const ReportParams& params) 118 { 119 initMetricMocks(params.metricParameters()); 120 121 return std::make_unique<Report>( 122 DbusEnvironment::getIoc(), DbusEnvironment::getObjServer(), 123 params.reportId(), params.reportName(), params.reportingType(), 124 params.reportActions(), params.interval(), params.appendLimit(), 125 params.reportUpdates(), *reportManagerMock, storageMock, 126 utils::convContainer<std::shared_ptr<interfaces::Metric>>( 127 metricMocks), 128 *reportFactoryMock, params.enabled(), std::move(clockFakePtr)); 129 } 130 131 template <class T> 132 static T getProperty(const std::string& path, const std::string& property) 133 { 134 return DbusEnvironment::getProperty<T>(path, Report::reportIfaceName, 135 property); 136 } 137 138 template <class T> 139 static boost::system::error_code setProperty(const std::string& path, 140 const std::string& property, 141 const T& newValue) 142 { 143 return DbusEnvironment::setProperty<T>(path, Report::reportIfaceName, 144 property, newValue); 145 } 146 147 boost::system::error_code call(const std::string& path, 148 const std::string& interface, 149 const std::string& method) 150 { 151 std::promise<boost::system::error_code> methodPromise; 152 DbusEnvironment::getBus()->async_method_call( 153 [&methodPromise](boost::system::error_code ec) { 154 methodPromise.set_value(ec); 155 }, 156 DbusEnvironment::serviceName(), path, interface, method); 157 return DbusEnvironment::waitForFuture(methodPromise.get_future()); 158 } 159 160 boost::system::error_code update(const std::string& path) 161 { 162 return call(path, Report::reportIfaceName, "Update"); 163 } 164 165 boost::system::error_code deleteReport(const std::string& path) 166 { 167 return call(path, Report::deleteIfaceName, "Delete"); 168 } 169 }; 170 171 TEST_F(TestReport, returnsId) 172 { 173 EXPECT_THAT(sut->getId(), Eq(defaultParams().reportId())); 174 } 175 176 TEST_F(TestReport, verifyIfPropertiesHaveValidValue) 177 { 178 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Enabled"), 179 Eq(defaultParams().enabled())); 180 EXPECT_THAT(getProperty<uint64_t>(sut->getPath(), "Interval"), 181 Eq(defaultParams().interval().count())); 182 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistency"), Eq(true)); 183 EXPECT_THAT( 184 getProperty<std::vector<std::string>>(sut->getPath(), "ReportActions"), 185 Eq(utils::transform(defaultParams().reportActions(), [](const auto v) { 186 return utils::enumToString(v); 187 }))); 188 EXPECT_THAT(getProperty<bool>(sut->getPath(), "EmitsReadingsUpdate"), 189 Eq(utils::contains(defaultParams().reportActions(), 190 ReportAction::emitsReadingsUpdate))); 191 EXPECT_THAT(getProperty<uint64_t>(sut->getPath(), "AppendLimit"), 192 Eq(defaultParams().appendLimit())); 193 EXPECT_THAT(getProperty<std::string>(sut->getPath(), "ReportingType"), 194 Eq(utils::enumToString(defaultParams().reportingType()))); 195 EXPECT_THAT(getProperty<std::string>(sut->getPath(), "ReportUpdates"), 196 Eq(utils::enumToString(defaultParams().reportUpdates()))); 197 EXPECT_THAT( 198 getProperty<bool>(sut->getPath(), "LogToMetricReportsCollection"), 199 Eq(utils::contains(defaultParams().reportActions(), 200 ReportAction::logToMetricReportsCollection))); 201 EXPECT_THAT(getProperty<ReadingParameters>( 202 sut->getPath(), "ReadingParametersFutureVersion"), 203 Eq(toReadingParameters(defaultParams().metricParameters()))); 204 EXPECT_THAT(getProperty<std::string>(sut->getPath(), "Name"), 205 Eq(defaultParams().reportName())); 206 EXPECT_THAT( 207 getProperty<std::vector<std::string>>(sut->getPath(), "TriggerIds"), 208 Eq(std::vector<std::string>())); 209 } 210 211 TEST_F(TestReport, readingsAreInitialyEmpty) 212 { 213 EXPECT_THAT(getProperty<Readings>(sut->getPath(), "Readings"), 214 Eq(Readings{})); 215 } 216 217 TEST_F(TestReport, setReadingParametersWithNewParams) 218 { 219 ReadingParameters newParams = toReadingParameters( 220 std::vector<LabeledMetricParameters>{{LabeledMetricParameters{ 221 {LabeledSensorInfo{"Service", 222 "/xyz/openbmc_project/sensors/power/psu", 223 "NewMetadata123"}}, 224 OperationType::avg, 225 "NewMetricId123", 226 CollectionTimeScope::startup, 227 CollectionDuration(250ms)}}}); 228 auto metrics = getMetricsFromReadingParams(newParams); 229 230 EXPECT_CALL(*reportFactoryMock, updateMetrics(_, _, _)) 231 .WillOnce(SetArgReferee<0>(metrics)); 232 EXPECT_THAT( 233 setProperty(sut->getPath(), "ReadingParametersFutureVersion", newParams) 234 .value(), 235 Eq(boost::system::errc::success)); 236 EXPECT_THAT(getProperty<ReadingParameters>( 237 sut->getPath(), "ReadingParametersFutureVersion"), 238 Eq(newParams)); 239 } 240 241 TEST_F(TestReport, setReportingTypeWithValidNewType) 242 { 243 std::string newType = "Periodic"; 244 std::string currType = utils::enumToString(defaultParams().reportingType()); 245 246 EXPECT_THAT(newType, Ne(currType)); 247 EXPECT_THAT(setProperty(sut->getPath(), "ReportingType", newType).value(), 248 Eq(boost::system::errc::success)); 249 EXPECT_THAT(getProperty<std::string>(sut->getPath(), "ReportingType"), 250 Eq(newType)); 251 } 252 253 TEST_F(TestReport, setReportingTypeWithInvalidType) 254 { 255 std::string newType = "Periodic_ABC"; 256 std::string prevType = utils::enumToString(defaultParams().reportingType()); 257 258 EXPECT_THAT(setProperty(sut->getPath(), "ReportingType", newType).value(), 259 Eq(boost::system::errc::invalid_argument)); 260 EXPECT_THAT(getProperty<std::string>(sut->getPath(), "ReportingType"), 261 Eq(prevType)); 262 } 263 264 TEST_F(TestReport, setReportActionsWithValidNewActions) 265 { 266 std::vector<std::string> newActions = {"EmitsReadingsUpdate"}; 267 std::vector<std::string> currActions = 268 utils::transform(defaultParams().reportActions(), 269 [](const auto v) { return utils::enumToString(v); }); 270 271 EXPECT_THAT(newActions, Ne(currActions)); 272 EXPECT_THAT( 273 setProperty(sut->getPath(), "ReportActions", newActions).value(), 274 Eq(boost::system::errc::success)); 275 EXPECT_THAT( 276 getProperty<std::vector<std::string>>(sut->getPath(), "ReportActions"), 277 UnorderedElementsAre("EmitsReadingsUpdate", 278 "LogToMetricReportsCollection")); 279 } 280 281 TEST_F(TestReport, setReportActionsWithValidUnsortedActions) 282 { 283 std::vector<std::string> newActions = {"LogToMetricReportsCollection", 284 "EmitsReadingsUpdate"}; 285 std::vector<std::string> expectedActions = {"EmitsReadingsUpdate", 286 "LogToMetricReportsCollection"}; 287 std::vector<std::string> currActions = 288 utils::transform(defaultParams().reportActions(), 289 [](const auto v) { return utils::enumToString(v); }); 290 291 EXPECT_THAT(newActions, Ne(currActions)); 292 EXPECT_THAT( 293 setProperty(sut->getPath(), "ReportActions", newActions).value(), 294 Eq(boost::system::errc::success)); 295 EXPECT_THAT( 296 getProperty<std::vector<std::string>>(sut->getPath(), "ReportActions"), 297 Eq(expectedActions)); 298 } 299 300 TEST_F(TestReport, setReportActionsWithEmptyActions) 301 { 302 std::vector<std::string> newActions = {}; 303 std::vector<std::string> expectedActions = {"LogToMetricReportsCollection"}; 304 std::vector<std::string> currActions = 305 utils::transform(defaultParams().reportActions(), 306 [](const auto v) { return utils::enumToString(v); }); 307 308 EXPECT_THAT(newActions, Ne(currActions)); 309 EXPECT_THAT( 310 setProperty(sut->getPath(), "ReportActions", newActions).value(), 311 Eq(boost::system::errc::success)); 312 EXPECT_THAT( 313 getProperty<std::vector<std::string>>(sut->getPath(), "ReportActions"), 314 Eq(expectedActions)); 315 } 316 317 TEST_F(TestReport, setReportActionsWithInvalidActions) 318 { 319 std::vector<std::string> invalidActions = {"EmitsReadingsUpdate_1"}; 320 EXPECT_THAT( 321 setProperty(sut->getPath(), "ReportActions", invalidActions).value(), 322 Eq(boost::system::errc::invalid_argument)); 323 EXPECT_THAT( 324 getProperty<std::vector<std::string>>(sut->getPath(), "ReportActions"), 325 Eq(utils::transform(defaultParams().reportActions(), [](const auto v) { 326 return utils::enumToString(v); 327 }))); 328 } 329 330 TEST_F(TestReport, createReportWithEmptyActions) 331 { 332 std::vector<std::string> expectedActions = {"LogToMetricReportsCollection"}; 333 334 sut = makeReport(ReportParams().reportId("TestId_1").reportActions({})); 335 EXPECT_THAT( 336 getProperty<std::vector<std::string>>(sut->getPath(), "ReportActions"), 337 Eq(expectedActions)); 338 } 339 340 TEST_F(TestReport, createReportWithValidUnsortedActions) 341 { 342 std::vector<std::string> newActions = {"LogToMetricReportsCollection", 343 "EmitsReadingsUpdate"}; 344 std::vector<std::string> expectedActions = {"EmitsReadingsUpdate", 345 "LogToMetricReportsCollection"}; 346 347 sut = makeReport( 348 ReportParams() 349 .reportId("TestId_1") 350 .reportActions(utils::transform(newActions, [](const auto& action) { 351 return utils::toReportAction(action); 352 }))); 353 EXPECT_THAT( 354 getProperty<std::vector<std::string>>(sut->getPath(), "ReportActions"), 355 Eq(expectedActions)); 356 } 357 358 TEST_F(TestReport, setEnabledWithNewValue) 359 { 360 bool newValue = !defaultParams().enabled(); 361 EXPECT_THAT(setProperty(sut->getPath(), "Enabled", newValue).value(), 362 Eq(boost::system::errc::success)); 363 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Enabled"), Eq(newValue)); 364 } 365 366 TEST_F(TestReport, setIntervalWithValidValue) 367 { 368 uint64_t newValue = defaultParams().interval().count() + 1; 369 EXPECT_THAT(setProperty(sut->getPath(), "Interval", newValue).value(), 370 Eq(boost::system::errc::success)); 371 EXPECT_THAT(getProperty<uint64_t>(sut->getPath(), "Interval"), 372 Eq(newValue)); 373 } 374 375 TEST_F( 376 TestReport, 377 settingIntervalWithInvalidValueDoesNotChangePropertyAndReturnsInvalidArgument) 378 { 379 uint64_t newValue = defaultParams().interval().count() - 1; 380 EXPECT_THAT(setProperty(sut->getPath(), "Interval", newValue).value(), 381 Eq(boost::system::errc::invalid_argument)); 382 EXPECT_THAT(getProperty<uint64_t>(sut->getPath(), "Interval"), 383 Eq(defaultParams().interval().count())); 384 } 385 386 TEST_F(TestReport, settingEmitsReadingsUpdateHaveNoEffect) 387 { 388 EXPECT_THAT( 389 setProperty(sut->getPath(), "EmitsReadingsUpdate", true).value(), 390 Eq(boost::system::errc::read_only_file_system)); 391 EXPECT_THAT(getProperty<bool>(sut->getPath(), "EmitsReadingsUpdate"), 392 Eq(utils::contains(defaultParams().reportActions(), 393 ReportAction::emitsReadingsUpdate))); 394 } 395 396 TEST_F(TestReport, settingLogToMetricReportCollectionHaveNoEffect) 397 { 398 EXPECT_THAT( 399 setProperty(sut->getPath(), "LogToMetricReportsCollection", true) 400 .value(), 401 Eq(boost::system::errc::read_only_file_system)); 402 EXPECT_THAT( 403 getProperty<bool>(sut->getPath(), "LogToMetricReportsCollection"), 404 Eq(utils::contains(defaultParams().reportActions(), 405 ReportAction::logToMetricReportsCollection))); 406 } 407 408 TEST_F(TestReport, settingPersistencyToFalseRemovesReportFromStorage) 409 { 410 EXPECT_CALL(storageMock, remove(to_file_path(sut->getId()))); 411 412 bool persistency = false; 413 EXPECT_THAT(setProperty(sut->getPath(), "Persistency", persistency).value(), 414 Eq(boost::system::errc::success)); 415 EXPECT_THAT(getProperty<bool>(sut->getPath(), "Persistency"), 416 Eq(persistency)); 417 } 418 419 TEST_F(TestReport, deleteReport) 420 { 421 EXPECT_CALL(*reportManagerMock, removeReport(sut.get())); 422 auto ec = deleteReport(sut->getPath()); 423 EXPECT_THAT(ec, Eq(boost::system::errc::success)); 424 } 425 426 TEST_F(TestReport, deletingNonExistingReportReturnInvalidRequestDescriptor) 427 { 428 auto ec = deleteReport(Report::reportDir + "NonExisting"s); 429 EXPECT_THAT(ec.value(), Eq(EBADR)); 430 } 431 432 TEST_F(TestReport, deleteReportExpectThatFileIsRemoveFromStorage) 433 { 434 EXPECT_CALL(storageMock, remove(to_file_path(sut->getId()))); 435 auto ec = deleteReport(sut->getPath()); 436 EXPECT_THAT(ec, Eq(boost::system::errc::success)); 437 } 438 439 TEST_F(TestReport, updatesTriggerIdWhenTriggerIsAdded) 440 { 441 utils::Messanger messanger(DbusEnvironment::getIoc()); 442 443 messanger.send(messages::TriggerPresenceChangedInd{ 444 messages::Presence::Exist, "trigger1", {defaultParams().reportId()}}); 445 messanger.send(messages::TriggerPresenceChangedInd{ 446 messages::Presence::Exist, "trigger1", {defaultParams().reportId()}}); 447 messanger.send(messages::TriggerPresenceChangedInd{ 448 messages::Presence::Exist, "trigger2", {"someOtherReport"}}); 449 messanger.send(messages::TriggerPresenceChangedInd{ 450 messages::Presence::Exist, 451 "trigger3", 452 {"someOtherReport", defaultParams().reportId()}}); 453 454 EXPECT_THAT( 455 getProperty<std::vector<std::string>>(sut->getPath(), "TriggerIds"), 456 UnorderedElementsAre("trigger1", "trigger3")); 457 } 458 459 TEST_F(TestReport, updatesTriggerIdWhenTriggerIsRemoved) 460 { 461 utils::Messanger messanger(DbusEnvironment::getIoc()); 462 463 messanger.send(messages::TriggerPresenceChangedInd{ 464 messages::Presence::Exist, "trigger1", {defaultParams().reportId()}}); 465 messanger.send(messages::TriggerPresenceChangedInd{ 466 messages::Presence::Exist, "trigger2", {defaultParams().reportId()}}); 467 messanger.send(messages::TriggerPresenceChangedInd{ 468 messages::Presence::Exist, "trigger3", {defaultParams().reportId()}}); 469 470 messanger.send(messages::TriggerPresenceChangedInd{ 471 messages::Presence::Removed, "trigger1", {defaultParams().reportId()}}); 472 messanger.send(messages::TriggerPresenceChangedInd{ 473 messages::Presence::Removed, "trigger2", {}}); 474 messanger.send(messages::TriggerPresenceChangedInd{ 475 messages::Presence::Removed, "trigger1", {defaultParams().reportId()}}); 476 477 EXPECT_THAT( 478 getProperty<std::vector<std::string>>(sut->getPath(), "TriggerIds"), 479 UnorderedElementsAre("trigger3")); 480 } 481 482 TEST_F(TestReport, updatesTriggerIdWhenTriggerIsModified) 483 { 484 utils::Messanger messanger(DbusEnvironment::getIoc()); 485 486 messanger.send(messages::TriggerPresenceChangedInd{ 487 messages::Presence::Exist, "trigger1", {defaultParams().reportId()}}); 488 messanger.send(messages::TriggerPresenceChangedInd{ 489 messages::Presence::Exist, "trigger2", {defaultParams().reportId()}}); 490 messanger.send(messages::TriggerPresenceChangedInd{ 491 messages::Presence::Exist, "trigger3", {defaultParams().reportId()}}); 492 493 messanger.send(messages::TriggerPresenceChangedInd{ 494 messages::Presence::Exist, "trigger1", {defaultParams().reportId()}}); 495 messanger.send(messages::TriggerPresenceChangedInd{ 496 messages::Presence::Exist, "trigger2", {}}); 497 messanger.send(messages::TriggerPresenceChangedInd{ 498 messages::Presence::Exist, "trigger3", {defaultParams().reportId()}}); 499 500 EXPECT_THAT( 501 getProperty<std::vector<std::string>>(sut->getPath(), "TriggerIds"), 502 UnorderedElementsAre("trigger1", "trigger3")); 503 } 504 505 class TestReportStore : 506 public TestReport, 507 public WithParamInterface<std::pair<std::string, nlohmann::json>> 508 { 509 public: 510 void SetUp() override 511 {} 512 513 nlohmann::json storedConfiguration; 514 }; 515 516 INSTANTIATE_TEST_SUITE_P( 517 _, TestReportStore, 518 Values( 519 std::make_pair("Enabled"s, nlohmann::json(defaultParams().enabled())), 520 std::make_pair("Version"s, nlohmann::json(6)), 521 std::make_pair("Id"s, nlohmann::json(defaultParams().reportId())), 522 std::make_pair("Name"s, nlohmann::json(defaultParams().reportName())), 523 std::make_pair("ReportingType", 524 nlohmann::json(defaultParams().reportingType())), 525 std::make_pair("ReportActions", nlohmann::json(utils::transform( 526 defaultParams().reportActions(), 527 [](const auto v) { 528 return utils::toUnderlying(v); 529 }))), 530 std::make_pair("Interval", 531 nlohmann::json(defaultParams().interval().count())), 532 std::make_pair("AppendLimit", 533 nlohmann::json(ReportParams().appendLimit())), 534 std::make_pair( 535 "ReadingParameters", 536 nlohmann::json( 537 {{{tstring::SensorPath::str(), 538 {{{tstring::Service::str(), "Service"}, 539 {tstring::Path::str(), 540 "/xyz/openbmc_project/sensors/power/p1"}, 541 {tstring::Metadata::str(), "metadata1"}}}}, 542 {tstring::OperationType::str(), OperationType::avg}, 543 {tstring::Id::str(), "MetricId1"}, 544 {tstring::CollectionTimeScope::str(), 545 CollectionTimeScope::point}, 546 {tstring::CollectionDuration::str(), 0}}, 547 {{tstring::SensorPath::str(), 548 {{{tstring::Service::str(), "Service"}, 549 {tstring::Path::str(), 550 "/xyz/openbmc_project/sensors/power/p2"}, 551 {tstring::Metadata::str(), "metadata2"}}}}, 552 {tstring::OperationType::str(), OperationType::avg}, 553 {tstring::Id::str(), "MetricId2"}, 554 {tstring::CollectionTimeScope::str(), 555 CollectionTimeScope::point}, 556 {tstring::CollectionDuration::str(), 0}}})))); 557 558 TEST_P(TestReportStore, settingPersistencyToTrueStoresReport) 559 { 560 sut = makeReport(defaultParams()); 561 562 { 563 InSequence seq; 564 EXPECT_CALL(storageMock, remove(to_file_path(sut->getId()))); 565 EXPECT_CALL(checkPoint, Call()); 566 EXPECT_CALL(storageMock, store(to_file_path(sut->getId()), _)) 567 .WillOnce(SaveArg<1>(&storedConfiguration)); 568 } 569 570 setProperty(sut->getPath(), "Persistency", false); 571 checkPoint.Call(); 572 setProperty(sut->getPath(), "Persistency", true); 573 574 const auto& [key, value] = GetParam(); 575 576 ASSERT_THAT(storedConfiguration.at(key), Eq(value)); 577 } 578 579 TEST_P(TestReportStore, reportIsSavedToStorageAfterCreated) 580 { 581 EXPECT_CALL(storageMock, store(to_file_path(defaultParams().reportId()), _)) 582 .WillOnce(SaveArg<1>(&storedConfiguration)); 583 584 sut = makeReport(defaultParams()); 585 586 const auto& [key, value] = GetParam(); 587 588 ASSERT_THAT(storedConfiguration.at(key), Eq(value)); 589 } 590 591 class TestReportValidNames : 592 public TestReport, 593 public WithParamInterface<ReportParams> 594 { 595 public: 596 void SetUp() override 597 {} 598 }; 599 600 INSTANTIATE_TEST_SUITE_P( 601 ValidNames, TestReportValidNames, 602 Values(defaultParams().reportName("Valid_1"), 603 defaultParams().reportName("Valid_1/Valid_2"), 604 defaultParams().reportName("Valid_1/Valid_2/Valid_3"))); 605 606 TEST_P(TestReportValidNames, reportCtorDoesNotThrowOnValidName) 607 { 608 EXPECT_NO_THROW(makeReport(GetParam())); 609 } 610 611 class TestReportInvalidIds : 612 public TestReport, 613 public WithParamInterface<ReportParams> 614 { 615 public: 616 void SetUp() override 617 {} 618 }; 619 620 INSTANTIATE_TEST_SUITE_P(InvalidNames, TestReportInvalidIds, 621 Values(defaultParams().reportId("/"), 622 defaultParams().reportId("/Invalid"), 623 defaultParams().reportId("Invalid/"), 624 defaultParams().reportId("Invalid/Invalid/"), 625 defaultParams().reportId("Invalid?"))); 626 627 TEST_P(TestReportInvalidIds, failsToCreateReportWithInvalidName) 628 { 629 EXPECT_CALL(storageMock, store).Times(0); 630 631 EXPECT_THROW(makeReport(GetParam()), sdbusplus::exception::SdBusError); 632 } 633 634 class TestReportAllReportTypes : 635 public TestReport, 636 public WithParamInterface<ReportParams> 637 { 638 public: 639 void SetUp() override 640 { 641 sut = makeReport(GetParam()); 642 } 643 }; 644 645 INSTANTIATE_TEST_SUITE_P( 646 _, TestReportAllReportTypes, 647 Values(defaultParams().reportingType(ReportingType::onRequest), 648 defaultParams().reportingType(ReportingType::onChange), 649 defaultParams().reportingType(ReportingType::periodic))); 650 651 TEST_P(TestReportAllReportTypes, returnPropertValueOfReportType) 652 { 653 EXPECT_THAT(utils::toReportingType( 654 getProperty<std::string>(sut->getPath(), "ReportingType")), 655 Eq(GetParam().reportingType())); 656 } 657 658 TEST_P(TestReportAllReportTypes, readingsAreUpdated) 659 { 660 clockFake.system.advance(10ms); 661 662 messanger.send(messages::UpdateReportInd{{sut->getId()}}); 663 const auto [timestamp, readings] = 664 getProperty<Readings>(sut->getPath(), "Readings"); 665 666 EXPECT_THAT(Milliseconds{timestamp}, Eq(systemTimestamp + 10ms)); 667 } 668 669 TEST_P(TestReportAllReportTypes, readingsAreNotUpdatedWhenReportIsDisabled) 670 { 671 clockFake.system.advance(10ms); 672 673 setProperty(sut->getPath(), "Enabled", false); 674 messanger.send(messages::UpdateReportInd{{sut->getId()}}); 675 const auto [timestamp, readings] = 676 getProperty<Readings>(sut->getPath(), "Readings"); 677 678 EXPECT_THAT(Milliseconds{timestamp}, Eq(0ms)); 679 } 680 681 TEST_P(TestReportAllReportTypes, readingsAreNotUpdatedWhenReportIdDiffers) 682 { 683 clockFake.system.advance(10ms); 684 685 messanger.send(messages::UpdateReportInd{{sut->getId() + "x"s}}); 686 const auto [timestamp, readings] = 687 getProperty<Readings>(sut->getPath(), "Readings"); 688 689 EXPECT_THAT(Milliseconds{timestamp}, Eq(0ms)); 690 } 691 692 class TestReportOnRequestType : public TestReport 693 { 694 void SetUp() override 695 { 696 sut = 697 makeReport(defaultParams().reportingType(ReportingType::onRequest)); 698 } 699 }; 700 701 TEST_F(TestReportOnRequestType, updatesReadingTimestamp) 702 { 703 clockFake.system.advance(10ms); 704 705 ASSERT_THAT(update(sut->getPath()), Eq(boost::system::errc::success)); 706 707 const auto [timestamp, readings] = 708 getProperty<Readings>(sut->getPath(), "Readings"); 709 710 EXPECT_THAT(Milliseconds{timestamp}, Eq(systemTimestamp + 10ms)); 711 } 712 713 TEST_F(TestReportOnRequestType, updatesReadingWhenUpdateIsCalled) 714 { 715 ASSERT_THAT(update(sut->getPath()), Eq(boost::system::errc::success)); 716 717 const auto [timestamp, readings] = 718 getProperty<Readings>(sut->getPath(), "Readings"); 719 720 EXPECT_THAT(readings, 721 ElementsAre(std::make_tuple("a"s, "b"s, 17.1, 114u), 722 std::make_tuple("aa"s, "bb"s, 42.0, 74u))); 723 } 724 725 class TestReportNonOnRequestType : 726 public TestReport, 727 public WithParamInterface<ReportParams> 728 { 729 void SetUp() override 730 { 731 sut = makeReport(GetParam()); 732 } 733 }; 734 735 INSTANTIATE_TEST_SUITE_P( 736 _, TestReportNonOnRequestType, 737 Values(defaultParams().reportingType(ReportingType::periodic), 738 defaultParams().reportingType(ReportingType::onChange))); 739 740 TEST_P(TestReportNonOnRequestType, readingsAreNotUpdateOnUpdateCall) 741 { 742 ASSERT_THAT(update(sut->getPath()), Eq(boost::system::errc::success)); 743 744 EXPECT_THAT(getProperty<Readings>(sut->getPath(), "Readings"), 745 Eq(Readings{})); 746 } 747 748 class TestReportNonPeriodicReport : 749 public TestReport, 750 public WithParamInterface<ReportParams> 751 { 752 public: 753 void SetUp() override 754 { 755 sut = makeReport(GetParam()); 756 } 757 }; 758 759 INSTANTIATE_TEST_SUITE_P( 760 _, TestReportNonPeriodicReport, 761 Values(defaultParams().reportingType(ReportingType::onRequest), 762 defaultParams().reportingType(ReportingType::onChange))); 763 764 TEST_P(TestReportNonPeriodicReport, readingsAreNotUpdatedAfterIntervalExpires) 765 { 766 DbusEnvironment::sleepFor(ReportManager::minInterval + 1ms); 767 768 EXPECT_THAT(getProperty<Readings>(sut->getPath(), "Readings"), 769 Eq(Readings{})); 770 } 771 772 class TestReportPeriodicReport : public TestReport 773 { 774 void SetUp() override 775 { 776 sut = 777 makeReport(defaultParams().reportingType(ReportingType::periodic)); 778 } 779 }; 780 781 TEST_F(TestReportPeriodicReport, readingTimestampIsUpdatedAfterIntervalExpires) 782 { 783 clockFake.system.advance(10ms); 784 DbusEnvironment::sleepFor(ReportManager::minInterval + 1ms); 785 786 const auto [timestamp, readings] = 787 getProperty<Readings>(sut->getPath(), "Readings"); 788 789 EXPECT_THAT(Milliseconds{timestamp}, Eq(systemTimestamp + 10ms)); 790 } 791 792 TEST_F(TestReportPeriodicReport, readingsAreUpdatedAfterIntervalExpires) 793 { 794 DbusEnvironment::sleepFor(ReportManager::minInterval + 1ms); 795 796 const auto [timestamp, readings] = 797 getProperty<Readings>(sut->getPath(), "Readings"); 798 799 EXPECT_THAT(readings, 800 ElementsAre(std::make_tuple("a"s, "b"s, 17.1, 114u), 801 std::make_tuple("aa"s, "bb"s, 42.0, 74u))); 802 } 803 804 struct ReportUpdatesReportParams 805 { 806 ReportParams reportParams; 807 std::vector<ReadingData> expectedReadings; 808 bool expectedEnabled; 809 }; 810 811 class TestReportWithReportUpdatesAndLimit : 812 public TestReport, 813 public WithParamInterface<ReportUpdatesReportParams> 814 { 815 void SetUp() override 816 { 817 sut = makeReport(ReportParams(GetParam().reportParams) 818 .reportingType(ReportingType::periodic) 819 .interval(std::chrono::hours(1000))); 820 } 821 }; 822 823 INSTANTIATE_TEST_SUITE_P( 824 _, TestReportWithReportUpdatesAndLimit, 825 Values( 826 ReportUpdatesReportParams{ 827 defaultParams() 828 .reportUpdates(ReportUpdates::appendWrapsWhenFull) 829 .appendLimit(5), 830 std::vector<ReadingData>{{std::make_tuple("aa"s, "bb"s, 42.0, 74u), 831 std::make_tuple("a"s, "b"s, 17.1, 114u), 832 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 833 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 834 std::make_tuple("a"s, "b"s, 17.1, 114u)}}, 835 true}, 836 ReportUpdatesReportParams{ 837 defaultParams() 838 .reportUpdates(ReportUpdates::appendWrapsWhenFull) 839 .appendLimit(4), 840 std::vector<ReadingData>{ 841 {std::make_tuple("a"s, "b"s, 17.1, 114u), 842 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 843 std::make_tuple("a"s, "b"s, 17.1, 114u), 844 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 845 true}, 846 ReportUpdatesReportParams{ 847 defaultParams() 848 .reportUpdates(ReportUpdates::appendWrapsWhenFull) 849 .appendLimit(0), 850 std::vector<ReadingData>{}, true}, 851 ReportUpdatesReportParams{ 852 defaultParams() 853 .reportUpdates(ReportUpdates::appendStopsWhenFull) 854 .appendLimit(10), 855 std::vector<ReadingData>{ 856 {std::make_tuple("a"s, "b"s, 17.1, 114u), 857 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 858 std::make_tuple("a"s, "b"s, 17.1, 114u), 859 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 860 std::make_tuple("a"s, "b"s, 17.1, 114u), 861 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 862 std::make_tuple("a"s, "b"s, 17.1, 114u), 863 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 864 true}, 865 ReportUpdatesReportParams{ 866 defaultParams() 867 .reportUpdates(ReportUpdates::appendStopsWhenFull) 868 .appendLimit(5), 869 std::vector<ReadingData>{{std::make_tuple("a"s, "b"s, 17.1, 114u), 870 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 871 std::make_tuple("a"s, "b"s, 17.1, 114u), 872 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 873 std::make_tuple("a"s, "b"s, 17.1, 114u)}}, 874 false}, 875 ReportUpdatesReportParams{ 876 defaultParams() 877 .reportUpdates(ReportUpdates::appendStopsWhenFull) 878 .appendLimit(4), 879 std::vector<ReadingData>{ 880 {std::make_tuple("a"s, "b"s, 17.1, 114u), 881 std::make_tuple("aa"s, "bb"s, 42.0, 74u), 882 std::make_tuple("a"s, "b"s, 17.1, 114u), 883 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 884 false}, 885 ReportUpdatesReportParams{ 886 defaultParams() 887 .reportUpdates(ReportUpdates::appendStopsWhenFull) 888 .appendLimit(0), 889 std::vector<ReadingData>{}, false}, 890 ReportUpdatesReportParams{ 891 defaultParams() 892 .reportUpdates(ReportUpdates::overwrite) 893 .appendLimit(500), 894 std::vector<ReadingData>{ 895 {std::make_tuple("a"s, "b"s, 17.1, 114u), 896 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 897 true}, 898 ReportUpdatesReportParams{ 899 defaultParams() 900 .reportUpdates(ReportUpdates::overwrite) 901 .appendLimit(1), 902 std::vector<ReadingData>{ 903 {std::make_tuple("a"s, "b"s, 17.1, 114u), 904 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 905 true}, 906 ReportUpdatesReportParams{ 907 defaultParams() 908 .reportUpdates(ReportUpdates::overwrite) 909 .appendLimit(0), 910 std::vector<ReadingData>{ 911 {std::make_tuple("a"s, "b"s, 17.1, 114u), 912 std::make_tuple("aa"s, "bb"s, 42.0, 74u)}}, 913 true})); 914 915 TEST_P(TestReportWithReportUpdatesAndLimit, 916 readingsAreUpdatedAfterIntervalExpires) 917 { 918 for (int i = 0; i < 4; i++) 919 { 920 messanger.send(messages::UpdateReportInd{{sut->getId()}}); 921 } 922 923 const auto [timestamp, readings] = 924 getProperty<Readings>(sut->getPath(), "Readings"); 925 const auto enabled = getProperty<bool>(sut->getPath(), "Enabled"); 926 927 EXPECT_THAT(readings, ElementsAreArray(GetParam().expectedReadings)); 928 EXPECT_EQ(enabled, GetParam().expectedEnabled); 929 } 930 931 class TestReportInitialization : public TestReport 932 { 933 public: 934 void SetUp() override 935 { 936 initMetricMocks(defaultParams().metricParameters()); 937 } 938 939 void monitorProc(sdbusplus::message::message& msg) 940 { 941 std::string iface; 942 std::vector<std::pair<std::string, std::variant<Readings>>> 943 changed_properties; 944 std::vector<std::string> invalidated_properties; 945 946 msg.read(iface, changed_properties, invalidated_properties); 947 948 if (iface == Report::reportIfaceName) 949 { 950 for (const auto& [name, value] : changed_properties) 951 { 952 if (name == "Readings") 953 { 954 readingsUpdated.Call(); 955 } 956 } 957 } 958 } 959 960 void makeMonitor() 961 { 962 monitor = std::make_unique<sdbusplus::bus::match_t>( 963 *DbusEnvironment::getBus(), 964 sdbusplus::bus::match::rules::propertiesChanged( 965 sut->getPath(), Report::reportIfaceName), 966 [this](auto& msg) { monitorProc(msg); }); 967 } 968 969 std::unique_ptr<sdbusplus::bus::match_t> monitor; 970 MockFunction<void()> readingsUpdated; 971 }; 972 973 TEST_F(TestReportInitialization, 974 registersForMetricUpdatesWhenOnChangeReportCreated) 975 { 976 std::vector<const interfaces::MetricListener*> args; 977 for (auto& metric : metricMocks) 978 { 979 EXPECT_CALL(*metric, registerForUpdates(_)) 980 .WillOnce(Invoke([&args](const interfaces::MetricListener& report) { 981 args.emplace_back(&report); 982 })); 983 ; 984 } 985 986 sut = makeReport(defaultParams().reportingType(ReportingType::onChange)); 987 988 EXPECT_THAT(args, SizeIs(metricMocks.size())); 989 for (const auto* reportPtr : args) 990 { 991 EXPECT_THAT(reportPtr, Eq(sut.get())); 992 } 993 } 994 995 TEST_F(TestReportInitialization, 996 deregistersForMetricUpdatesWhenOnChangeReportDestroyed) 997 { 998 sut = makeReport(defaultParams().reportingType(ReportingType::onChange)); 999 1000 for (auto& metric : metricMocks) 1001 { 1002 EXPECT_CALL(*metric, 1003 unregisterFromUpdates(Ref( 1004 static_cast<interfaces::MetricListener&>(*sut.get())))); 1005 } 1006 1007 sut = nullptr; 1008 } 1009 1010 TEST_F(TestReportInitialization, 1011 metricsAreInitializedWhenEnabledReportConstructed) 1012 { 1013 for (auto& metric : metricMocks) 1014 { 1015 EXPECT_CALL(*metric, initialize()); 1016 } 1017 sut = makeReport(defaultParams().enabled(true)); 1018 } 1019 1020 TEST_F(TestReportInitialization, 1021 metricsAreNotInitializedWhenDisabledReportConstructed) 1022 { 1023 for (auto& metric : metricMocks) 1024 { 1025 EXPECT_CALL(*metric, initialize()).Times(0); 1026 } 1027 sut = makeReport(defaultParams().enabled(false)); 1028 } 1029 1030 TEST_F(TestReportInitialization, 1031 emitReadingsUpdateIsTrueReadingsPropertiesChangedSingalEmits) 1032 { 1033 EXPECT_CALL(readingsUpdated, Call()) 1034 .WillOnce( 1035 InvokeWithoutArgs(DbusEnvironment::setPromise("readingsUpdated"))); 1036 1037 const auto elapsed = DbusEnvironment::measureTime([this] { 1038 sut = 1039 makeReport(defaultParams() 1040 .reportingType(ReportingType::periodic) 1041 .reportActions({ReportAction::emitsReadingsUpdate})); 1042 makeMonitor(); 1043 EXPECT_TRUE(DbusEnvironment::waitForFuture("readingsUpdated")); 1044 }); 1045 1046 EXPECT_THAT(elapsed, AllOf(Ge(defaultParams().interval()), 1047 Lt(defaultParams().interval() * 2))); 1048 } 1049 1050 TEST_F(TestReportInitialization, 1051 emitReadingsUpdateIsFalseReadingsPropertiesChangesSigalDoesNotEmits) 1052 { 1053 EXPECT_CALL(readingsUpdated, Call()).Times(0); 1054 1055 sut = makeReport(defaultParams() 1056 .reportingType(ReportingType::periodic) 1057 .reportActions({})); 1058 makeMonitor(); 1059 DbusEnvironment::sleepFor(defaultParams().interval() * 2); 1060 } 1061 1062 TEST_F(TestReportInitialization, appendLimitDeducedProperly) 1063 { 1064 sut = makeReport( 1065 defaultParams().appendLimit(std::numeric_limits<uint64_t>::max())); 1066 auto appendLimit = getProperty<uint64_t>(sut->getPath(), "AppendLimit"); 1067 EXPECT_EQ(appendLimit, 2ull); 1068 } 1069 1070 TEST_F(TestReportInitialization, appendLimitSetToUintMaxIsStoredCorrectly) 1071 { 1072 nlohmann::json storedConfiguration; 1073 1074 EXPECT_CALL(storageMock, store(to_file_path(ReportParams().reportId()), _)) 1075 .WillOnce(SaveArg<1>(&storedConfiguration)); 1076 1077 sut = makeReport( 1078 ReportParams().appendLimit(std::numeric_limits<uint64_t>::max())); 1079 1080 ASSERT_THAT(storedConfiguration.at("AppendLimit"), 1081 Eq(std::numeric_limits<uint64_t>::max())); 1082 } 1083 1084 TEST_F(TestReportInitialization, triggerIdsPropertyIsInitialzed) 1085 { 1086 for (const auto& triggerId : {"trigger1", "trigger2"}) 1087 { 1088 messanger.on_receive<messages::CollectTriggerIdReq>( 1089 [&](const auto& msg) { 1090 messanger.send(messages::CollectTriggerIdResp{triggerId}); 1091 }); 1092 } 1093 1094 sut = makeReport(ReportParams()); 1095 1096 EXPECT_THAT( 1097 getProperty<std::vector<std::string>>(sut->getPath(), "TriggerIds"), 1098 UnorderedElementsAre("trigger1", "trigger2")); 1099 } 1100 1101 class TestReportInitializationOnChangeReport : public TestReportInitialization 1102 { 1103 public: 1104 void SetUp() override 1105 { 1106 initMetricMocks(params.metricParameters()); 1107 } 1108 1109 ReportParams params = defaultOnChangeParams(); 1110 }; 1111 1112 TEST_F(TestReportInitializationOnChangeReport, 1113 doesntUpdateReadingsWhenNotRequired) 1114 { 1115 EXPECT_CALL(*metricMocks[0], updateReadings(_)).Times(0); 1116 1117 ON_CALL(*metricMocks[0], isTimerRequired()).WillByDefault(Return(false)); 1118 1119 sut = makeReport(params); 1120 1121 DbusEnvironment::sleepFor(500ms); 1122 } 1123 1124 TEST_F(TestReportInitializationOnChangeReport, updatesReadingsWhenRequired) 1125 { 1126 EXPECT_CALL(*metricMocks[0], updateReadings(_)) 1127 .WillOnce(Return()) 1128 .WillOnce( 1129 InvokeWithoutArgs(DbusEnvironment::setPromise("readingsUpdated"))) 1130 .WillRepeatedly(Return()); 1131 1132 ON_CALL(*metricMocks[0], isTimerRequired()).WillByDefault(Return(true)); 1133 1134 sut = makeReport(params); 1135 1136 DbusEnvironment::waitForFuture("readingsUpdated"); 1137 } 1138