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