xref: /openbmc/telemetry/tests/src/test_report_manager.cpp (revision e6d4887453f0b23b46c012da7cb26f2beb38ef0e)
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, addReport)
107 {
108     EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
109     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
110         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
111 
112     auto [ec, path] = addReport(reportParams);
113     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
114     EXPECT_THAT(path, Eq(reportMock.getPath()));
115 }
116 
117 TEST_F(TestReportManager, nameIsUsedToGenerateIdWhenIdIsEmptyInAddReport)
118 {
119     reportParams.reportId("ReportName");
120     reportParams.reportName("ReportName");
121 
122     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock));
123 
124     auto [ec, path] = addReport(reportParams.reportId(""));
125 
126     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
127     EXPECT_THAT(path, Eq("/ReportName"));
128 }
129 
130 TEST_F(TestReportManager, nameIsUsedToGenerateIdWhenIdIsNamespace)
131 {
132     reportParams.reportId("Prefix/ReportName");
133     reportParams.reportName("ReportName");
134 
135     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock));
136 
137     auto [ec, path] = addReport(reportParams.reportId("Prefix/"));
138 
139     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
140     EXPECT_THAT(path, Eq("/Prefix/ReportName"));
141 }
142 
143 TEST_F(TestReportManager, addReportWithMaxLengthId)
144 {
145     std::string reportId(ReportManager::maxReportIdLength, 'z');
146     reportParams.reportId(reportId);
147     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock));
148 
149     auto [ec, path] = addReport(reportParams);
150 
151     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
152     EXPECT_THAT(path, Eq("/"s + reportId));
153 }
154 
155 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooLongName)
156 {
157     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
158         .Times(0);
159 
160     reportParams.reportId(
161         std::string(ReportManager::maxReportIdLength + 1, 'z'));
162 
163     auto [ec, path] = addReport(reportParams);
164 
165     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
166     EXPECT_THAT(path, Eq(std::string()));
167 }
168 
169 TEST_F(TestReportManager, DISABLED_failToAddReportTwice)
170 {
171     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
172         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
173 
174     addReport(reportParams);
175 
176     auto [ec, path] = addReport(reportParams);
177 
178     EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists));
179     EXPECT_THAT(path, Eq(std::string()));
180 }
181 
182 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidInterval)
183 {
184     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
185         .Times(0);
186 
187     reportParams.reportingType(ReportingType::periodic);
188     reportParams.interval(reportParams.interval() - 1ms);
189 
190     auto [ec, path] = addReport(reportParams);
191 
192     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
193     EXPECT_THAT(path, Eq(std::string()));
194 }
195 
196 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidReportingType)
197 {
198     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
199         .Times(0);
200 
201     auto [ec, path] = addReport(
202         reportParams.reportName(), "InvalidReportingType",
203         utils::transform(reportParams.reportActions(),
204                          [](const auto v) { return utils::enumToString(v); }),
205         reportParams.interval().count(),
206         toReadingParameters(reportParams.metricParameters()));
207 
208     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
209     EXPECT_THAT(path, Eq(std::string()));
210 }
211 
212 TEST_F(TestReportManager,
213        DISABLED_failToAddReportWithMoreMetricPropertiesThanExpected)
214 {
215     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
216         .Times(0);
217 
218     reportParams.metricParameters(
219         std::vector<LabeledMetricParameters>{{LabeledMetricParameters{
220             {LabeledSensorInfo{"Service",
221                                "/xyz/openbmc_project/sensors/power/p1",
222                                "Metadata1"}},
223             OperationType::single,
224             "MetricId1",
225             CollectionTimeScope::point,
226             CollectionDuration(Milliseconds(0u))}}});
227 
228     auto metricParams = reportParams.metricParameters();
229     auto& metricParamsVec =
230         metricParams[0].at_label<utils::tstring::SensorPath>();
231 
232     for (size_t i = 0; i < ReportManager::maxNumberMetrics; i++)
233     {
234         metricParamsVec.emplace_back(LabeledSensorInfo{
235             "Service", "/xyz/openbmc_project/sensors/power/p1", "Metadata1"});
236     }
237 
238     reportParams.metricParameters(std::move(metricParams));
239 
240     auto [ec, path] = addReport(reportParams);
241 
242     EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long));
243     EXPECT_THAT(path, Eq(std::string()));
244 }
245 
246 TEST_F(TestReportManager, DISABLED_failToAddReportWithMoreMetricsThanExpected)
247 {
248     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
249         .Times(0);
250 
251     auto metricParams = std::vector<LabeledMetricParameters>{};
252 
253     for (size_t i = 0; i < ReportManager::maxNumberMetrics + 1; i++)
254     {
255         metricParams.emplace_back(
256             LabeledMetricParameters{{},
257                                     OperationType::single,
258                                     "MetricId1",
259                                     CollectionTimeScope::point,
260                                     CollectionDuration(Milliseconds(0u))});
261     }
262 
263     reportParams.metricParameters(std::move(metricParams));
264 
265     auto [ec, path] = addReport(reportParams);
266 
267     EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long));
268     EXPECT_THAT(path, Eq(std::string()));
269 }
270 
271 TEST_F(TestReportManager, DISABLED_failToAddReportWithAppendLimitGreaterThanMax)
272 {
273     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
274         .Times(0);
275 
276     reportParams.appendLimit(ReportManager::maxAppendLimit + 1);
277 
278     auto [ec, path] = addReport(reportParams);
279 
280     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
281     EXPECT_THAT(path, Eq(std::string()));
282 }
283 
284 TEST_F(TestReportManager, addReportWithAppendLimitEqualToUint64MaxIsAllowed)
285 {
286     reportParams.appendLimit(std::numeric_limits<uint64_t>::max());
287 
288     EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
289     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
290         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
291 
292     auto [ec, path] = addReport(reportParams);
293     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
294     EXPECT_THAT(path, Eq(reportMock.getPath()));
295 }
296 
297 TEST_F(TestReportManager, DISABLED_failToAddReportWhenMaxReportIsReached)
298 {
299     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock))
300         .Times(ReportManager::maxReports);
301 
302     for (size_t i = 0; i < ReportManager::maxReports; i++)
303     {
304         reportParams.reportId(reportParams.reportName() + std::to_string(i));
305 
306         auto [ec, path] = addReport(reportParams);
307         EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
308     }
309 
310     reportParams.reportId(reportParams.reportName() +
311                           std::to_string(ReportManager::maxReports));
312     auto [ec, path] = addReport(reportParams);
313 
314     EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open));
315     EXPECT_THAT(path, Eq(std::string()));
316 }
317 
318 TEST_F(TestReportManager, removeReport)
319 {
320     {
321         InSequence seq;
322         EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
323         reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
324             .WillOnce(Return(ByMove(std::move(reportMockPtr))));
325         EXPECT_CALL(reportMock, Die());
326         EXPECT_CALL(checkPoint, Call("end"));
327     }
328 
329     addReport(reportParams);
330     sut->removeReport(&reportMock);
331     checkPoint.Call("end");
332 }
333 
334 TEST_F(TestReportManager, removingReportThatIsNotInContainerHasNoEffect)
335 {
336     {
337         InSequence seq;
338         EXPECT_CALL(checkPoint, Call("end"));
339         EXPECT_CALL(reportMock, Die());
340     }
341 
342     sut->removeReport(&reportMock);
343     checkPoint.Call("end");
344 }
345 
346 TEST_F(TestReportManager, removingSameReportTwiceHasNoSideEffect)
347 {
348     {
349         InSequence seq;
350         EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _));
351         reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
352             .WillOnce(Return(ByMove(std::move(reportMockPtr))));
353         EXPECT_CALL(reportMock, Die());
354         EXPECT_CALL(checkPoint, Call("end"));
355     }
356 
357     addReport(reportParams);
358     sut->removeReport(&reportMock);
359     sut->removeReport(&reportMock);
360     checkPoint.Call("end");
361 }
362 
363 class TestReportManagerWithAggregationOperationType :
364     public TestReportManager,
365     public WithParamInterface<OperationType>
366 {
367   public:
368     OperationType operationType = GetParam();
369 };
370 
371 INSTANTIATE_TEST_SUITE_P(_, TestReportManagerWithAggregationOperationType,
372                          Values(OperationType::single, OperationType::max,
373                                 OperationType::min, OperationType::avg,
374                                 OperationType::sum));
375 
376 TEST_P(TestReportManagerWithAggregationOperationType,
377        addReportWithDifferentOperationTypes)
378 {
379     reportParams.metricParameters(
380         std::vector<LabeledMetricParameters>{{LabeledMetricParameters{
381             {LabeledSensorInfo{"Service",
382                                "/xyz/openbmc_project/sensors/power/p1",
383                                "Metadata1"}},
384             operationType,
385             "MetricId1",
386             CollectionTimeScope::point,
387             CollectionDuration(Milliseconds(0u))}}});
388 
389     reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock))
390         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
391 
392     auto [ec, path] = addReport(reportParams);
393 
394     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
395     EXPECT_THAT(path, Eq("/"s + reportParams.reportId()));
396 }
397 
398 class TestReportManagerStorage : public TestReportManager
399 {
400   public:
401     using FilePath = interfaces::JsonStorage::FilePath;
402     using DirectoryPath = interfaces::JsonStorage::DirectoryPath;
403 
404     void SetUp() override
405     {
406         EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)).Times(0);
407 
408         ON_CALL(storageMock, list())
409             .WillByDefault(Return(std::vector<FilePath>{FilePath("report1")}));
410         ON_CALL(storageMock, load(FilePath("report1")))
411             .WillByDefault(InvokeWithoutArgs([this] { return data; }));
412     }
413 
414     void makeReportManager()
415     {
416         sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr),
417                                               std::move(storageMockPtr),
418                                               DbusEnvironment::getObjServer());
419     }
420 
421     nlohmann::json data = nlohmann::json{
422         {"Enabled", reportParams.enabled()},
423         {"Version", Report::reportVersion},
424         {"Id", reportParams.reportId()},
425         {"Name", reportParams.reportName()},
426         {"ReportingType", utils::toUnderlying(reportParams.reportingType())},
427         {"ReportActions", reportParams.reportActions()},
428         {"Interval", reportParams.interval().count()},
429         {"ReportUpdates", utils::toUnderlying(reportParams.reportUpdates())},
430         {"AppendLimit", reportParams.appendLimit()},
431         {"ReadingParameters", reportParams.metricParameters()}};
432 };
433 
434 TEST_F(TestReportManagerStorage, reportManagerCtorAddReportFromStorage)
435 {
436     reportFactoryMock.expectMake(reportParams, _, Ref(storageMock));
437 
438     makeReportManager();
439 }
440 
441 TEST_F(TestReportManagerStorage,
442        reportManagerCtorRemoveFileIfVersionDoesNotMatch)
443 {
444     data["Version"] = Report::reportVersion - 1;
445 
446     EXPECT_CALL(storageMock, remove(FilePath("report1")));
447 
448     makeReportManager();
449 }
450 
451 TEST_F(TestReportManagerStorage,
452        reportManagerCtorRemoveFileIfIntervalHasWrongType)
453 {
454     data["Interval"] = "1000";
455 
456     EXPECT_CALL(storageMock, remove(FilePath("report1")));
457 
458     makeReportManager();
459 }
460