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.reportName());
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         sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr),
39                                               std::move(storageMockPtr),
40                                               DbusEnvironment::getObjServer());
41     }
42 
43     void TearDown() override
44     {
45         DbusEnvironment::synchronizeIoc();
46     }
47 
48     std::pair<boost::system::error_code, std::string>
49         addReport(const ReportParams& params)
50     {
51         std::promise<std::pair<boost::system::error_code, std::string>>
52             addReportPromise;
53         DbusEnvironment::getBus()->async_method_call(
54             [&addReportPromise](boost::system::error_code ec,
55                                 const std::string& path) {
56                 addReportPromise.set_value({ec, path});
57             },
58             DbusEnvironment::serviceName(), ReportManager::reportManagerPath,
59             ReportManager::reportManagerIfaceName, "AddReportFutureVersion",
60             params.reportName(), params.reportingType(),
61             params.emitReadingUpdate(), params.logToMetricReportCollection(),
62             static_cast<uint64_t>(params.interval().count()),
63             params.readingParameters());
64         return DbusEnvironment::waitForFuture(addReportPromise.get_future());
65     }
66 
67     template <class T>
68     static T getProperty(std::string property)
69     {
70         std::promise<T> propertyPromise;
71         sdbusplus::asio::getProperty<T>(
72             *DbusEnvironment::getBus(), DbusEnvironment::serviceName(),
73             ReportManager::reportManagerPath,
74             ReportManager::reportManagerIfaceName, property,
75             [&propertyPromise](boost::system::error_code ec) {
76                 EXPECT_THAT(static_cast<bool>(ec), ::testing::Eq(false));
77                 propertyPromise.set_value(T{});
78             },
79             [&propertyPromise](T t) { propertyPromise.set_value(t); });
80         return DbusEnvironment::waitForFuture(propertyPromise.get_future());
81     }
82 };
83 
84 TEST_F(TestReportManager, minInterval)
85 {
86     EXPECT_THAT(getProperty<uint64_t>("MinInterval"),
87                 Eq(static_cast<uint64_t>(ReportManager::minInterval.count())));
88 }
89 
90 TEST_F(TestReportManager, maxReports)
91 {
92     EXPECT_THAT(getProperty<size_t>("MaxReports"),
93                 Eq(ReportManager::maxReports));
94 }
95 
96 TEST_F(TestReportManager, addReport)
97 {
98     reportFactoryMock.expectMake(_, reportParams, Ref(*sut), Ref(storageMock))
99         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
100 
101     auto [ec, path] = addReport(reportParams);
102     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
103     EXPECT_THAT(path, Eq(reportMock.getPath()));
104 }
105 
106 TEST_F(TestReportManager, DISABLED_failToAddReportTwice)
107 {
108     reportFactoryMock.expectMake(_, reportParams, Ref(*sut), Ref(storageMock))
109         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
110 
111     addReport(reportParams);
112 
113     auto [ec, path] = addReport(reportParams);
114     EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists));
115     EXPECT_THAT(path, Eq(std::string()));
116 }
117 
118 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidInterval)
119 {
120     reportFactoryMock.expectMake(_, std::nullopt, Ref(*sut), Ref(storageMock))
121         .Times(0);
122     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock), _)
123         .Times(0);
124 
125     reportParams.reportingType("Periodic");
126     reportParams.interval(reportParams.interval() - 1ms);
127 
128     auto [ec, path] = addReport(reportParams);
129     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
130     EXPECT_THAT(path, Eq(std::string()));
131 }
132 
133 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidReportingType)
134 {
135     reportFactoryMock.expectMake(_, std::nullopt, Ref(*sut), Ref(storageMock))
136         .Times(0);
137     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock), _)
138         .Times(0);
139 
140     reportParams.reportingType("Invalid");
141 
142     auto [ec, path] = addReport(reportParams);
143     EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument));
144     EXPECT_THAT(path, Eq(std::string()));
145 }
146 
147 TEST_F(TestReportManager, DISABLED_failToAddReportWithMoreSensorsThanExpected)
148 {
149     reportFactoryMock.expectMake(_, std::nullopt, Ref(*sut), Ref(storageMock))
150         .Times(0);
151     reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock), _)
152         .Times(0);
153 
154     auto readingParams = reportParams.readingParameters();
155     for (size_t i = 0; i < ReportManager::maxReadingParams + 1; i++)
156     {
157         readingParams.push_back(readingParams.front());
158     }
159     reportParams.readingParameters(std::move(readingParams));
160 
161     auto [ec, path] = addReport(reportParams);
162     EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long));
163     EXPECT_THAT(path, Eq(std::string()));
164 }
165 
166 TEST_F(TestReportManager, DISABLED_failToAddReportWhenMaxReportIsReached)
167 {
168     reportFactoryMock.expectMake(_, std::nullopt, Ref(*sut), Ref(storageMock))
169         .Times(ReportManager::maxReports);
170 
171     for (size_t i = 0; i < ReportManager::maxReports; i++)
172     {
173         reportParams.reportName(reportParams.reportName() + std::to_string(i));
174 
175         auto [ec, path] = addReport(reportParams);
176         EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
177     }
178 
179     reportParams.reportName(reportParams.reportName() +
180                             std::to_string(ReportManager::maxReports));
181     auto [ec, path] = addReport(reportParams);
182     EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open));
183     EXPECT_THAT(path, Eq(std::string()));
184 }
185 
186 TEST_F(TestReportManager, removeReport)
187 {
188     {
189         InSequence seq;
190         reportFactoryMock
191             .expectMake(_, reportParams, Ref(*sut), Ref(storageMock))
192             .WillOnce(Return(ByMove(std::move(reportMockPtr))));
193         EXPECT_CALL(reportMock, Die());
194         EXPECT_CALL(checkPoint, Call("end"));
195     }
196 
197     addReport(reportParams);
198     sut->removeReport(&reportMock);
199     checkPoint.Call("end");
200 }
201 
202 TEST_F(TestReportManager, removingReportThatIsNotInContainerHasNoEffect)
203 {
204     {
205         InSequence seq;
206         EXPECT_CALL(checkPoint, Call("end"));
207         EXPECT_CALL(reportMock, Die());
208     }
209 
210     sut->removeReport(&reportMock);
211     checkPoint.Call("end");
212 }
213 
214 TEST_F(TestReportManager, removingSameReportTwiceHasNoSideEffect)
215 {
216     {
217         InSequence seq;
218         reportFactoryMock
219             .expectMake(_, reportParams, Ref(*sut), Ref(storageMock))
220             .WillOnce(Return(ByMove(std::move(reportMockPtr))));
221         EXPECT_CALL(reportMock, Die());
222         EXPECT_CALL(checkPoint, Call("end"));
223     }
224 
225     addReport(reportParams);
226     sut->removeReport(&reportMock);
227     sut->removeReport(&reportMock);
228     checkPoint.Call("end");
229 }
230 
231 TEST_F(TestReportManager, updateReportCallsUpdateReadingsForExistReport)
232 {
233     reportFactoryMock.expectMake(_, reportParams, Ref(*sut), Ref(storageMock))
234         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
235     EXPECT_CALL(reportMock, updateReadings());
236 
237     addReport(reportParams);
238     sut->updateReport(reportParams.reportName());
239 }
240 
241 TEST_F(TestReportManager, updateReportDoNothingIfReportDoesNotExist)
242 {
243     reportFactoryMock.expectMake(_, reportParams, Ref(*sut), Ref(storageMock))
244         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
245     EXPECT_CALL(reportMock, updateReadings()).Times(0);
246 
247     addReport(reportParams);
248     sut->updateReport("NotAReport");
249 }
250 
251 class TestReportManagerWithAggregationOperationType :
252     public TestReportManager,
253     public WithParamInterface<OperationType>
254 {
255   public:
256     OperationType operationType = GetParam();
257 };
258 
259 INSTANTIATE_TEST_SUITE_P(_, TestReportManagerWithAggregationOperationType,
260                          Values(OperationType::single, OperationType::max,
261                                 OperationType::min, OperationType::avg,
262                                 OperationType::sum));
263 
264 TEST_P(TestReportManagerWithAggregationOperationType,
265        addReportWithDifferentOperationTypes)
266 {
267     reportParams.readingParameters(
268         {{{sdbusplus::message::object_path(
269               "/xyz/openbmc_project/sensors/power/p1")},
270           utils::enumToString(operationType),
271           "MetricId1",
272           "Metadata1",
273           utils::enumToString(CollectionTimeScope::point),
274           0u}});
275 
276     reportFactoryMock.expectMake(_, reportParams, Ref(*sut), Ref(storageMock))
277         .WillOnce(Return(ByMove(std::move(reportMockPtr))));
278 
279     auto [ec, path] = addReport(reportParams);
280     EXPECT_THAT(ec.value(), Eq(boost::system::errc::success));
281     EXPECT_THAT(path, Eq("/"s + reportParams.reportName()));
282 }
283 
284 class TestReportManagerStorage : public TestReportManager
285 {
286   public:
287     using FilePath = interfaces::JsonStorage::FilePath;
288     using DirectoryPath = interfaces::JsonStorage::DirectoryPath;
289 
290     void SetUp() override
291     {
292         ON_CALL(storageMock, list())
293             .WillByDefault(Return(std::vector<FilePath>{FilePath("report1")}));
294         ON_CALL(storageMock, load(FilePath("report1")))
295             .WillByDefault(InvokeWithoutArgs([this] { return data; }));
296     }
297 
298     void makeReportManager()
299     {
300         sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr),
301                                               std::move(storageMockPtr),
302                                               DbusEnvironment::getObjServer());
303     }
304 
305     static std::vector<LabeledMetricParameters>
306         convertToLabeled(const ReadingParameters& params)
307     {
308         return utils::transform(params, [](const auto& item) {
309             return LabeledMetricParameters(
310                 LabeledSensorParameters("service", std::get<0>(item)),
311                 utils::stringToOperationType(std::get<1>(item)),
312                 std::get<2>(item), std::get<3>(item),
313                 utils::stringToCollectionTimeScope(std::get<4>(item)),
314                 CollectionDuration(
315                     std::chrono::milliseconds(std::get<5>(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