xref: /openbmc/telemetry/tests/src/test_report_manager.cpp (revision 9f9ff90a39219ff3a2f1179f74fc9a6dc857e5ab)
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 
89 TEST_F(TestReportManager, minInterval)
90 {
91     EXPECT_THAT(getProperty<uint64_t>("MinInterval"),
92                 Eq(static_cast<uint64_t>(ReportManager::minInterval.count())));
93 }
94 
95 TEST_F(TestReportManager, maxReports)
96 {
97     EXPECT_THAT(getProperty<size_t>("MaxReports"),
98                 Eq(ReportManager::maxReports));
99 }
100 
101 TEST_F(TestReportManager, addReport)
102 {
103     reportFactoryMock.expectMake(_, reportParams, Ref(*sut), Ref(storageMock))
104         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
105 
106     auto [ec, path] = addReport(reportParams);
107     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
108     EXPECT_THAT(path, Eq(reportMock.getPath()));
109 }
110 
111 TEST_F(TestReportManager, DISABLED_failToAddReportTwice)
112 {
113     reportFactoryMock.expectMake(_, reportParams, Ref(*sut), Ref(storageMock))
114         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
115 
116     addReport(reportParams);
117 
118     auto [ec, path] = addReport(reportParams);
119     EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists));
120     EXPECT_THAT(path, Eq(std::string()));
121 }
122 
123 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidInterval)
124 {
125     reportFactoryMock.expectMake(_, std::nullopt, Ref(*sut), Ref(storageMock))
126         .Times(0);
127     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock), _)
128         .Times(0);
129 
130     reportParams.reportingType("Periodic");
131     reportParams.interval(reportParams.interval() - 1ms);
132 
133     auto [ec, path] = addReport(reportParams);
134     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
135     EXPECT_THAT(path, Eq(std::string()));
136 }
137 
138 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidReportingType)
139 {
140     reportFactoryMock.expectMake(_, std::nullopt, Ref(*sut), Ref(storageMock))
141         .Times(0);
142     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock), _)
143         .Times(0);
144 
145     reportParams.reportingType("Invalid");
146 
147     auto [ec, path] = addReport(reportParams);
148     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
149     EXPECT_THAT(path, Eq(std::string()));
150 }
151 
152 TEST_F(TestReportManager, DISABLED_failToAddReportWithMoreSensorsThanExpected)
153 {
154     reportFactoryMock.expectMake(_, std::nullopt, Ref(*sut), Ref(storageMock))
155         .Times(0);
156     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock), _)
157         .Times(0);
158 
159     auto readingParams = reportParams.readingParameters();
160     for (size_t i = 0; i < ReportManager::maxReadingParams + 1; i++)
161     {
162         readingParams.push_back(readingParams.front());
163     }
164     reportParams.readingParameters(std::move(readingParams));
165 
166     auto [ec, path] = addReport(reportParams);
167     EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long));
168     EXPECT_THAT(path, Eq(std::string()));
169 }
170 
171 TEST_F(TestReportManager, DISABLED_failToAddReportWhenMaxReportIsReached)
172 {
173     reportFactoryMock.expectMake(_, std::nullopt, Ref(*sut), Ref(storageMock))
174         .Times(ReportManager::maxReports);
175 
176     for (size_t i = 0; i < ReportManager::maxReports; i++)
177     {
178         reportParams.reportName(reportParams.reportName() + std::to_string(i));
179 
180         auto [ec, path] = addReport(reportParams);
181         EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
182     }
183 
184     reportParams.reportName(reportParams.reportName() +
185                             std::to_string(ReportManager::maxReports));
186     auto [ec, path] = addReport(reportParams);
187     EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open));
188     EXPECT_THAT(path, Eq(std::string()));
189 }
190 
191 TEST_F(TestReportManager, removeReport)
192 {
193     {
194         InSequence seq;
195         reportFactoryMock
196             .expectMake(_, reportParams, Ref(*sut), Ref(storageMock))
197             .WillOnce(Return(ByMove(std::move(reportMockPtr))));
198         EXPECT_CALL(reportMock, Die());
199         EXPECT_CALL(checkPoint, Call("end"));
200     }
201 
202     addReport(reportParams);
203     sut->removeReport(&reportMock);
204     checkPoint.Call("end");
205 }
206 
207 TEST_F(TestReportManager, removingReportThatIsNotInContainerHasNoEffect)
208 {
209     {
210         InSequence seq;
211         EXPECT_CALL(checkPoint, Call("end"));
212         EXPECT_CALL(reportMock, Die());
213     }
214 
215     sut->removeReport(&reportMock);
216     checkPoint.Call("end");
217 }
218 
219 TEST_F(TestReportManager, removingSameReportTwiceHasNoSideEffect)
220 {
221     {
222         InSequence seq;
223         reportFactoryMock
224             .expectMake(_, reportParams, Ref(*sut), Ref(storageMock))
225             .WillOnce(Return(ByMove(std::move(reportMockPtr))));
226         EXPECT_CALL(reportMock, Die());
227         EXPECT_CALL(checkPoint, Call("end"));
228     }
229 
230     addReport(reportParams);
231     sut->removeReport(&reportMock);
232     sut->removeReport(&reportMock);
233     checkPoint.Call("end");
234 }
235 
236 TEST_F(TestReportManager, updateReportCallsUpdateReadingsForExistReport)
237 {
238     reportFactoryMock.expectMake(_, reportParams, Ref(*sut), Ref(storageMock))
239         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
240     EXPECT_CALL(reportMock, updateReadings());
241 
242     addReport(reportParams);
243     sut->updateReport(reportParams.reportName());
244 }
245 
246 TEST_F(TestReportManager, updateReportDoNothingIfReportDoesNotExist)
247 {
248     reportFactoryMock.expectMake(_, reportParams, Ref(*sut), Ref(storageMock))
249         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
250     EXPECT_CALL(reportMock, updateReadings()).Times(0);
251 
252     addReport(reportParams);
253     sut->updateReport("NotAReport");
254 }
255 
256 class TestReportManagerWithAggregationOperationType :
257     public TestReportManager,
258     public WithParamInterface<OperationType>
259 {
260   public:
261     OperationType operationType = GetParam();
262 };
263 
264 INSTANTIATE_TEST_SUITE_P(_, TestReportManagerWithAggregationOperationType,
265                          Values(OperationType::single, OperationType::max,
266                                 OperationType::min, OperationType::avg,
267                                 OperationType::sum));
268 
269 TEST_P(TestReportManagerWithAggregationOperationType,
270        addReportWithDifferentOperationTypes)
271 {
272     reportParams.readingParameters(
273         {{{sdbusplus::message::object_path(
274               "/xyz/openbmc_project/sensors/power/p1")},
275           utils::enumToString(operationType),
276           "MetricId1",
277           "Metadata1"}});
278 
279     reportFactoryMock.expectMake(_, reportParams, Ref(*sut), Ref(storageMock))
280         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
281 
282     auto [ec, path] = addReport(reportParams);
283     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
284     EXPECT_THAT(path, Eq("/"s + reportParams.reportName()));
285 }
286 
287 class TestReportManagerStorage : public TestReportManager
288 {
289   public:
290     using FilePath = interfaces::JsonStorage::FilePath;
291     using DirectoryPath = interfaces::JsonStorage::DirectoryPath;
292 
293     void SetUp() override
294     {
295         ON_CALL(storageMock, list())
296             .WillByDefault(Return(std::vector<FilePath>{FilePath("report1")}));
297         ON_CALL(storageMock, load(FilePath("report1")))
298             .WillByDefault(InvokeWithoutArgs([this] { return data; }));
299     }
300 
301     void makeReportManager()
302     {
303         sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr),
304                                               std::move(storageMockPtr),
305                                               DbusEnvironment::getObjServer());
306     }
307 
308     static std::vector<LabeledMetricParameters>
309         convertToLabeled(const ReadingParameters& params)
310     {
311         return utils::transform(params, [](const auto& item) {
312             return LabeledMetricParameters(
313                 LabeledSensorParameters("service", std::get<0>(item)),
314                 utils::stringToOperationType(std::get<1>(item)),
315                 std::get<2>(item), std::get<3>(item));
316         });
317     }
318 
319     nlohmann::json data = nlohmann::json{
320         {"Version", Report::reportVersion},
321         {"Name", reportParams.reportName()},
322         {"ReportingType", reportParams.reportingType()},
323         {"EmitsReadingsUpdate", reportParams.emitReadingUpdate()},
324         {"LogToMetricReportsCollection",
325          reportParams.logToMetricReportCollection()},
326         {"Interval", reportParams.interval().count()},
327         {"ReadingParameters",
328          convertToLabeled(reportParams.readingParameters())}};
329 };
330 
331 TEST_F(TestReportManagerStorage, reportManagerCtorAddReportFromStorage)
332 {
333     reportFactoryMock.expectMake(
334         reportParams, _, Ref(storageMock),
335         ElementsAreArray(convertToLabeled(reportParams.readingParameters())));
336 
337     makeReportManager();
338 }
339 
340 TEST_F(TestReportManagerStorage,
341        reportManagerCtorRemoveFileIfVersionDoesNotMatch)
342 {
343     data["Version"] = Report::reportVersion - 1;
344 
345     EXPECT_CALL(storageMock, remove(FilePath("report1")));
346 
347     makeReportManager();
348 }
349 
350 TEST_F(TestReportManagerStorage,
351        reportManagerCtorRemoveFileIfIntervalHasWrongType)
352 {
353     data["Interval"] = "1000";
354 
355     EXPECT_CALL(storageMock, remove(FilePath("report1")));
356 
357     makeReportManager();
358 }
359