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