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