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