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