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