xref: /openbmc/telemetry/tests/src/test_report.cpp (revision b7b7e1b603fd9df56a72547c8048d182a001d647)
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