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