1 #include "dbus_environment.hpp"
2 #include "helpers.hpp"
3 #include "interfaces/trigger_manager.hpp"
4 #include "mocks/json_storage_mock.hpp"
5 #include "mocks/report_factory_mock.hpp"
6 #include "mocks/trigger_manager_mock.hpp"
7 #include "params/report_params.hpp"
8 #include "report.hpp"
9 #include "report_manager.hpp"
10 #include "utils/conversion.hpp"
11 #include "utils/transform.hpp"
12 
13 using namespace testing;
14 using namespace std::string_literals;
15 using namespace std::chrono_literals;
16 
17 class TestReportManager : public Test
18 {
19   public:
20     ReportParams reportParams;
21 
22     std::unique_ptr<ReportFactoryMock> reportFactoryMockPtr =
23         std::make_unique<StrictMock<ReportFactoryMock>>();
24     ReportFactoryMock& reportFactoryMock = *reportFactoryMockPtr;
25 
26     std::unique_ptr<StorageMock> storageMockPtr =
27         std::make_unique<NiceMock<StorageMock>>();
28     StorageMock& storageMock = *storageMockPtr;
29 
30     std::unique_ptr<ReportMock> reportMockPtr =
31         std::make_unique<NiceMock<ReportMock>>(reportParams.reportId());
32     ReportMock& reportMock = *reportMockPtr;
33 
34     std::unique_ptr<ReportManager> sut;
35 
36     MockFunction<void(std::string)> checkPoint;
37 
38     void SetUp() override
39     {
40         EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _))
41             .Times(AnyNumber());
42 
43         sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr),
44                                               std::move(storageMockPtr),
45                                               DbusEnvironment::getObjServer());
46     }
47 
48     void TearDown() override
49     {
50         DbusEnvironment::synchronizeIoc();
51     }
52 
53     template <class... Args>
54     requires(sizeof...(Args) > 1)
55         std::pair<boost::system::error_code, std::string> addReport(
56             Args&&... args)
57     {
58         std::promise<std::pair<boost::system::error_code, std::string>>
59             addReportPromise;
60         DbusEnvironment::getBus()->async_method_call(
61             [&addReportPromise](boost::system::error_code ec,
62                                 const std::string& path) {
63                 addReportPromise.set_value({ec, path});
64             },
65             DbusEnvironment::serviceName(), ReportManager::reportManagerPath,
66             ReportManager::reportManagerIfaceName, "AddReportFutureVersion",
67             std::forward<Args>(args)...);
68         return DbusEnvironment::waitForFuture(addReportPromise.get_future());
69     }
70 
71     auto addReport(const ReportParams& params)
72     {
73         return addReport(params.reportId(), params.reportName(),
74                          utils::enumToString(params.reportingType()),
75                          utils::enumToString(params.reportUpdates()),
76                          params.appendLimit(),
77                          utils::transform(params.reportActions(),
78                                           [](const auto v) {
79                                               return utils::enumToString(v);
80                                           }),
81                          params.interval().count(),
82                          toReadingParameters(params.metricParameters()));
83     }
84 
85     template <class T>
86     static T getProperty(const std::string& property)
87     {
88         return DbusEnvironment::getProperty<T>(
89             ReportManager::reportManagerPath,
90             ReportManager::reportManagerIfaceName, property);
91     }
92 };
93 
94 TEST_F(TestReportManager, minInterval)
95 {
96     EXPECT_THAT(getProperty<uint64_t>("MinInterval"),
97                 Eq(ReportManager::minInterval.count()));
98 }
99 
100 TEST_F(TestReportManager, maxReports)
101 {
102     EXPECT_THAT(getProperty<size_t>("MaxReports"),
103                 Eq(ReportManager::maxReports));
104 }
105 
106 TEST_F(TestReportManager, returnsPropertySupportedOperationTypes)
107 {
108     EXPECT_THAT(
109         getProperty<std::vector<std::string>>("SupportedOperationTypes"),
110         UnorderedElementsAre("Maximum", "Minimum", "Average", "Summation"));
111 }
112 
113 TEST_F(TestReportManager, addReport)
114 {
115     EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
116     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
117         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
118 
119     auto [ec, path] = addReport(reportParams);
120     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
121     EXPECT_THAT(path, Eq(reportMock.getPath()));
122 }
123 
124 TEST_F(TestReportManager, nameIsUsedToGenerateIdWhenIdIsEmptyInAddReport)
125 {
126     reportParams.reportId("ReportName");
127     reportParams.reportName("ReportName");
128 
129     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock));
130 
131     auto [ec, path] = addReport(reportParams.reportId(""));
132 
133     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
134     EXPECT_THAT(path, Eq("/ReportName"));
135 }
136 
137 TEST_F(TestReportManager, nameIsUsedToGenerateIdWhenIdIsNamespace)
138 {
139     reportParams.reportId("Prefix/ReportName");
140     reportParams.reportName("ReportName");
141 
142     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock));
143 
144     auto [ec, path] = addReport(reportParams.reportId("Prefix/"));
145 
146     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
147     EXPECT_THAT(path, Eq("/Prefix/ReportName"));
148 }
149 
150 TEST_F(TestReportManager, addReportWithMaxLengthId)
151 {
152     std::string reportId(ReportManager::maxReportIdLength, 'z');
153     reportParams.reportId(reportId);
154     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock));
155 
156     auto [ec, path] = addReport(reportParams);
157 
158     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
159     EXPECT_THAT(path, Eq("/"s + reportId));
160 }
161 
162 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooLongName)
163 {
164     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
165         .Times(0);
166 
167     reportParams.reportId(
168         std::string(ReportManager::maxReportIdLength + 1, 'z'));
169 
170     auto [ec, path] = addReport(reportParams);
171 
172     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
173     EXPECT_THAT(path, Eq(std::string()));
174 }
175 
176 TEST_F(TestReportManager, DISABLED_failToAddReportTwice)
177 {
178     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
179         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
180 
181     addReport(reportParams);
182 
183     auto [ec, path] = addReport(reportParams);
184 
185     EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists));
186     EXPECT_THAT(path, Eq(std::string()));
187 }
188 
189 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidInterval)
190 {
191     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
192         .Times(0);
193 
194     reportParams.reportingType(ReportingType::periodic);
195     reportParams.interval(reportParams.interval() - 1ms);
196 
197     auto [ec, path] = addReport(reportParams);
198 
199     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
200     EXPECT_THAT(path, Eq(std::string()));
201 }
202 
203 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidReportingType)
204 {
205     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
206         .Times(0);
207 
208     auto [ec, path] = addReport(
209         reportParams.reportName(), "InvalidReportingType",
210         utils::transform(reportParams.reportActions(),
211                          [](const auto v) { return utils::enumToString(v); }),
212         reportParams.interval().count(),
213         toReadingParameters(reportParams.metricParameters()));
214 
215     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
216     EXPECT_THAT(path, Eq(std::string()));
217 }
218 
219 TEST_F(TestReportManager,
220        DISABLED_failToAddReportWithMoreMetricPropertiesThanExpected)
221 {
222     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
223         .Times(0);
224 
225     reportParams.metricParameters(
226         std::vector<LabeledMetricParameters>{{LabeledMetricParameters{
227             {LabeledSensorInfo{"Service",
228                                "/xyz/openbmc_project/sensors/power/p1",
229                                "Metadata1"}},
230             OperationType::avg,
231             "MetricId1",
232             CollectionTimeScope::point,
233             CollectionDuration(Milliseconds(0u))}}});
234 
235     auto metricParams = reportParams.metricParameters();
236     auto& metricParamsVec =
237         metricParams[0].at_label<utils::tstring::SensorPath>();
238 
239     for (size_t i = 0; i < ReportManager::maxNumberMetrics; i++)
240     {
241         metricParamsVec.emplace_back(LabeledSensorInfo{
242             "Service", "/xyz/openbmc_project/sensors/power/p1", "Metadata1"});
243     }
244 
245     reportParams.metricParameters(std::move(metricParams));
246 
247     auto [ec, path] = addReport(reportParams);
248 
249     EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long));
250     EXPECT_THAT(path, Eq(std::string()));
251 }
252 
253 TEST_F(TestReportManager, DISABLED_failToAddReportWithMoreMetricsThanExpected)
254 {
255     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
256         .Times(0);
257 
258     auto metricParams = std::vector<LabeledMetricParameters>{};
259 
260     for (size_t i = 0; i < ReportManager::maxNumberMetrics + 1; i++)
261     {
262         metricParams.emplace_back(
263             LabeledMetricParameters{{},
264                                     OperationType::avg,
265                                     "MetricId1",
266                                     CollectionTimeScope::point,
267                                     CollectionDuration(Milliseconds(0u))});
268     }
269 
270     reportParams.metricParameters(std::move(metricParams));
271 
272     auto [ec, path] = addReport(reportParams);
273 
274     EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long));
275     EXPECT_THAT(path, Eq(std::string()));
276 }
277 
278 TEST_F(TestReportManager, DISABLED_failToAddReportWithAppendLimitGreaterThanMax)
279 {
280     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
281         .Times(0);
282 
283     reportParams.appendLimit(ReportManager::maxAppendLimit + 1);
284 
285     auto [ec, path] = addReport(reportParams);
286 
287     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
288     EXPECT_THAT(path, Eq(std::string()));
289 }
290 
291 TEST_F(TestReportManager, addReportWithAppendLimitEqualToUint64MaxIsAllowed)
292 {
293     reportParams.appendLimit(std::numeric_limits<uint64_t>::max());
294 
295     EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
296     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
297         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
298 
299     auto [ec, path] = addReport(reportParams);
300     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
301     EXPECT_THAT(path, Eq(reportMock.getPath()));
302 }
303 
304 TEST_F(TestReportManager, DISABLED_failToAddReportWhenMaxReportIsReached)
305 {
306     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
307         .Times(ReportManager::maxReports);
308 
309     for (size_t i = 0; i < ReportManager::maxReports; i++)
310     {
311         reportParams.reportId(reportParams.reportName() + std::to_string(i));
312 
313         auto [ec, path] = addReport(reportParams);
314         EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
315     }
316 
317     reportParams.reportId(reportParams.reportName() +
318                           std::to_string(ReportManager::maxReports));
319     auto [ec, path] = addReport(reportParams);
320 
321     EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open));
322     EXPECT_THAT(path, Eq(std::string()));
323 }
324 
325 TEST_F(TestReportManager, removeReport)
326 {
327     {
328         InSequence seq;
329         EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
330         reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
331             .WillOnce(Return(ByMove(std::move(reportMockPtr))));
332         EXPECT_CALL(reportMock, Die());
333         EXPECT_CALL(checkPoint, Call("end"));
334     }
335 
336     addReport(reportParams);
337     sut->removeReport(&reportMock);
338     checkPoint.Call("end");
339 }
340 
341 TEST_F(TestReportManager, removingReportThatIsNotInContainerHasNoEffect)
342 {
343     {
344         InSequence seq;
345         EXPECT_CALL(checkPoint, Call("end"));
346         EXPECT_CALL(reportMock, Die());
347     }
348 
349     sut->removeReport(&reportMock);
350     checkPoint.Call("end");
351 }
352 
353 TEST_F(TestReportManager, removingSameReportTwiceHasNoSideEffect)
354 {
355     {
356         InSequence seq;
357         EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
358         reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
359             .WillOnce(Return(ByMove(std::move(reportMockPtr))));
360         EXPECT_CALL(reportMock, Die());
361         EXPECT_CALL(checkPoint, Call("end"));
362     }
363 
364     addReport(reportParams);
365     sut->removeReport(&reportMock);
366     sut->removeReport(&reportMock);
367     checkPoint.Call("end");
368 }
369 
370 class TestReportManagerWithAggregationOperationType :
371     public TestReportManager,
372     public WithParamInterface<OperationType>
373 {
374   public:
375     OperationType operationType = GetParam();
376 };
377 
378 INSTANTIATE_TEST_SUITE_P(_, TestReportManagerWithAggregationOperationType,
379                          Values(OperationType::max, OperationType::min,
380                                 OperationType::avg, OperationType::sum));
381 
382 TEST_P(TestReportManagerWithAggregationOperationType,
383        addReportWithDifferentOperationTypes)
384 {
385     reportParams.metricParameters(
386         std::vector<LabeledMetricParameters>{{LabeledMetricParameters{
387             {LabeledSensorInfo{"Service",
388                                "/xyz/openbmc_project/sensors/power/p1",
389                                "Metadata1"}},
390             operationType,
391             "MetricId1",
392             CollectionTimeScope::point,
393             CollectionDuration(Milliseconds(0u))}}});
394 
395     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
396         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
397 
398     auto [ec, path] = addReport(reportParams);
399 
400     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
401     EXPECT_THAT(path, Eq("/"s + reportParams.reportId()));
402 }
403 
404 class TestReportManagerStorage : public TestReportManager
405 {
406   public:
407     using FilePath = interfaces::JsonStorage::FilePath;
408     using DirectoryPath = interfaces::JsonStorage::DirectoryPath;
409 
410     void SetUp() override
411     {
412         EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)).Times(0);
413 
414         ON_CALL(storageMock, list())
415             .WillByDefault(Return(std::vector<FilePath>{FilePath("report1")}));
416         ON_CALL(storageMock, load(FilePath("report1")))
417             .WillByDefault(InvokeWithoutArgs([this] { return data; }));
418     }
419 
420     void makeReportManager()
421     {
422         sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr),
423                                               std::move(storageMockPtr),
424                                               DbusEnvironment::getObjServer());
425     }
426 
427     nlohmann::json data = nlohmann::json{
428         {"Enabled", reportParams.enabled()},
429         {"Version", Report::reportVersion},
430         {"Id", reportParams.reportId()},
431         {"Name", reportParams.reportName()},
432         {"ReportingType", utils::toUnderlying(reportParams.reportingType())},
433         {"ReportActions", reportParams.reportActions()},
434         {"Interval", reportParams.interval().count()},
435         {"ReportUpdates", utils::toUnderlying(reportParams.reportUpdates())},
436         {"AppendLimit", reportParams.appendLimit()},
437         {"ReadingParameters", reportParams.metricParameters()}};
438 };
439 
440 TEST_F(TestReportManagerStorage, reportManagerCtorAddReportFromStorage)
441 {
442     reportFactoryMock.expectMake(reportParams, _, Ref(storageMock));
443 
444     makeReportManager();
445 }
446 
447 TEST_F(TestReportManagerStorage,
448        reportManagerCtorRemoveFileIfVersionDoesNotMatch)
449 {
450     data["Version"] = Report::reportVersion - 1;
451 
452     EXPECT_CALL(storageMock, remove(FilePath("report1")));
453 
454     makeReportManager();
455 }
456 
457 TEST_F(TestReportManagerStorage,
458        reportManagerCtorRemoveFileIfIntervalHasWrongType)
459 {
460     data["Interval"] = "1000";
461 
462     EXPECT_CALL(storageMock, remove(FilePath("report1")));
463 
464     makeReportManager();
465 }
466