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