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