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.reportId()); 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 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)) 39 .Times(AnyNumber()); 40 41 sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr), 42 std::move(storageMockPtr), 43 DbusEnvironment::getObjServer()); 44 } 45 46 void TearDown() override 47 { 48 DbusEnvironment::synchronizeIoc(); 49 } 50 51 template <class... Args> 52 requires(sizeof...(Args) > 1) 53 std::pair<boost::system::error_code, std::string> addReport( 54 Args&&... args) 55 { 56 std::promise<std::pair<boost::system::error_code, std::string>> 57 addReportPromise; 58 DbusEnvironment::getBus()->async_method_call( 59 [&addReportPromise](boost::system::error_code ec, 60 const std::string& path) { 61 addReportPromise.set_value({ec, path}); 62 }, 63 DbusEnvironment::serviceName(), ReportManager::reportManagerPath, 64 ReportManager::reportManagerIfaceName, "AddReportFutureVersion", 65 std::forward<Args>(args)...); 66 return DbusEnvironment::waitForFuture(addReportPromise.get_future()); 67 } 68 69 auto addReport(const ReportParams& params) 70 { 71 return addReport(params.reportId(), params.reportName(), 72 utils::enumToString(params.reportingType()), 73 utils::enumToString(params.reportUpdates()), 74 params.appendLimit(), 75 utils::transform(params.reportActions(), 76 [](const auto v) { 77 return utils::enumToString(v); 78 }), 79 params.interval().count(), 80 toReadingParameters(params.metricParameters())); 81 } 82 83 template <class T> 84 static T getProperty(const std::string& property) 85 { 86 return DbusEnvironment::getProperty<T>( 87 ReportManager::reportManagerPath, 88 ReportManager::reportManagerIfaceName, property); 89 } 90 }; 91 92 TEST_F(TestReportManager, minInterval) 93 { 94 EXPECT_THAT(getProperty<uint64_t>("MinInterval"), 95 Eq(ReportManager::minInterval.count())); 96 } 97 98 TEST_F(TestReportManager, maxReports) 99 { 100 EXPECT_THAT(getProperty<size_t>("MaxReports"), 101 Eq(ReportManager::maxReports)); 102 } 103 104 TEST_F(TestReportManager, addReport) 105 { 106 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 107 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 108 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 109 110 auto [ec, path] = addReport(reportParams); 111 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 112 EXPECT_THAT(path, Eq(reportMock.getPath())); 113 } 114 115 TEST_F(TestReportManager, nameIsUsedToGenerateIdWhenIdIsEmptyInAddReport) 116 { 117 reportParams.reportId("ReportName"); 118 reportParams.reportName("ReportName"); 119 120 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)); 121 122 auto [ec, path] = addReport(reportParams.reportId("")); 123 124 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 125 EXPECT_THAT(path, Eq("/ReportName")); 126 } 127 128 TEST_F(TestReportManager, nameIsUsedToGenerateIdWhenIdIsNamespace) 129 { 130 reportParams.reportId("Prefix/ReportName"); 131 reportParams.reportName("ReportName"); 132 133 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)); 134 135 auto [ec, path] = addReport(reportParams.reportId("Prefix/")); 136 137 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 138 EXPECT_THAT(path, Eq("/Prefix/ReportName")); 139 } 140 141 TEST_F(TestReportManager, addReportWithMaxLengthId) 142 { 143 std::string reportId(ReportManager::maxReportIdLength, 'z'); 144 reportParams.reportId(reportId); 145 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)); 146 147 auto [ec, path] = addReport(reportParams); 148 149 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 150 EXPECT_THAT(path, Eq("/"s + reportId)); 151 } 152 153 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooLongName) 154 { 155 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 156 .Times(0); 157 158 reportParams.reportId( 159 std::string(ReportManager::maxReportIdLength + 1, 'z')); 160 161 auto [ec, path] = addReport(reportParams); 162 163 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 164 EXPECT_THAT(path, Eq(std::string())); 165 } 166 167 TEST_F(TestReportManager, DISABLED_failToAddReportTwice) 168 { 169 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 170 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 171 172 addReport(reportParams); 173 174 auto [ec, path] = addReport(reportParams); 175 176 EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists)); 177 EXPECT_THAT(path, Eq(std::string())); 178 } 179 180 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidInterval) 181 { 182 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 183 .Times(0); 184 185 reportParams.reportingType(ReportingType::periodic); 186 reportParams.interval(reportParams.interval() - 1ms); 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_failToAddReportWithInvalidReportingType) 195 { 196 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 197 .Times(0); 198 199 auto [ec, path] = addReport( 200 reportParams.reportName(), "InvalidReportingType", 201 utils::transform(reportParams.reportActions(), 202 [](const auto v) { return utils::enumToString(v); }), 203 reportParams.interval().count(), 204 toReadingParameters(reportParams.metricParameters())); 205 206 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 207 EXPECT_THAT(path, Eq(std::string())); 208 } 209 210 TEST_F(TestReportManager, 211 DISABLED_failToAddReportWithMoreMetricPropertiesThanExpected) 212 { 213 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 214 .Times(0); 215 216 reportParams.metricParameters( 217 std::vector<LabeledMetricParameters>{{LabeledMetricParameters{ 218 {LabeledSensorInfo{"Service", 219 "/xyz/openbmc_project/sensors/power/p1", 220 "Metadata1"}}, 221 OperationType::single, 222 "MetricId1", 223 CollectionTimeScope::point, 224 CollectionDuration(Milliseconds(0u))}}}); 225 226 auto metricParams = reportParams.metricParameters(); 227 auto& metricParamsVec = 228 metricParams[0].at_label<utils::tstring::SensorPath>(); 229 230 for (size_t i = 0; i < ReportManager::maxNumberMetrics; i++) 231 { 232 metricParamsVec.emplace_back(LabeledSensorInfo{ 233 "Service", "/xyz/openbmc_project/sensors/power/p1", "Metadata1"}); 234 } 235 236 reportParams.metricParameters(std::move(metricParams)); 237 238 auto [ec, path] = addReport(reportParams); 239 240 EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long)); 241 EXPECT_THAT(path, Eq(std::string())); 242 } 243 244 TEST_F(TestReportManager, DISABLED_failToAddReportWithMoreMetricsThanExpected) 245 { 246 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 247 .Times(0); 248 249 auto metricParams = std::vector<LabeledMetricParameters>{}; 250 251 for (size_t i = 0; i < ReportManager::maxNumberMetrics + 1; i++) 252 { 253 metricParams.emplace_back( 254 LabeledMetricParameters{{}, 255 OperationType::single, 256 "MetricId1", 257 CollectionTimeScope::point, 258 CollectionDuration(Milliseconds(0u))}); 259 } 260 261 reportParams.metricParameters(std::move(metricParams)); 262 263 auto [ec, path] = addReport(reportParams); 264 265 EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long)); 266 EXPECT_THAT(path, Eq(std::string())); 267 } 268 269 TEST_F(TestReportManager, DISABLED_failToAddReportWithAppendLimitGreaterThanMax) 270 { 271 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 272 .Times(0); 273 274 reportParams.appendLimit(ReportManager::maxAppendLimit + 1); 275 276 auto [ec, path] = addReport(reportParams); 277 278 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 279 EXPECT_THAT(path, Eq(std::string())); 280 } 281 282 TEST_F(TestReportManager, addReportWithAppendLimitEqualToUint64MaxIsAllowed) 283 { 284 reportParams.appendLimit(std::numeric_limits<uint64_t>::max()); 285 286 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 287 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 288 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 289 290 auto [ec, path] = addReport(reportParams); 291 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 292 EXPECT_THAT(path, Eq(reportMock.getPath())); 293 } 294 295 TEST_F(TestReportManager, DISABLED_failToAddReportWhenMaxReportIsReached) 296 { 297 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 298 .Times(ReportManager::maxReports); 299 300 for (size_t i = 0; i < ReportManager::maxReports; i++) 301 { 302 reportParams.reportId(reportParams.reportName() + std::to_string(i)); 303 304 auto [ec, path] = addReport(reportParams); 305 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 306 } 307 308 reportParams.reportId(reportParams.reportName() + 309 std::to_string(ReportManager::maxReports)); 310 auto [ec, path] = addReport(reportParams); 311 312 EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open)); 313 EXPECT_THAT(path, Eq(std::string())); 314 } 315 316 TEST_F(TestReportManager, removeReport) 317 { 318 { 319 InSequence seq; 320 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 321 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 322 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 323 EXPECT_CALL(reportMock, Die()); 324 EXPECT_CALL(checkPoint, Call("end")); 325 } 326 327 addReport(reportParams); 328 sut->removeReport(&reportMock); 329 checkPoint.Call("end"); 330 } 331 332 TEST_F(TestReportManager, removingReportThatIsNotInContainerHasNoEffect) 333 { 334 { 335 InSequence seq; 336 EXPECT_CALL(checkPoint, Call("end")); 337 EXPECT_CALL(reportMock, Die()); 338 } 339 340 sut->removeReport(&reportMock); 341 checkPoint.Call("end"); 342 } 343 344 TEST_F(TestReportManager, removingSameReportTwiceHasNoSideEffect) 345 { 346 { 347 InSequence seq; 348 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 349 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 350 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 351 EXPECT_CALL(reportMock, Die()); 352 EXPECT_CALL(checkPoint, Call("end")); 353 } 354 355 addReport(reportParams); 356 sut->removeReport(&reportMock); 357 sut->removeReport(&reportMock); 358 checkPoint.Call("end"); 359 } 360 361 TEST_F(TestReportManager, updateReportCallsUpdateReadingsForExistReport) 362 { 363 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 364 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 365 EXPECT_CALL(reportMock, updateReadings()); 366 367 addReport(reportParams); 368 sut->updateReport(reportParams.reportId()); 369 } 370 371 TEST_F(TestReportManager, updateReportDoNothingIfReportDoesNotExist) 372 { 373 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 374 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 375 EXPECT_CALL(reportMock, updateReadings()).Times(0); 376 377 addReport(reportParams); 378 sut->updateReport("NotAReport"); 379 } 380 381 class TestReportManagerWithAggregationOperationType : 382 public TestReportManager, 383 public WithParamInterface<OperationType> 384 { 385 public: 386 OperationType operationType = GetParam(); 387 }; 388 389 INSTANTIATE_TEST_SUITE_P(_, TestReportManagerWithAggregationOperationType, 390 Values(OperationType::single, OperationType::max, 391 OperationType::min, OperationType::avg, 392 OperationType::sum)); 393 394 TEST_P(TestReportManagerWithAggregationOperationType, 395 addReportWithDifferentOperationTypes) 396 { 397 reportParams.metricParameters( 398 std::vector<LabeledMetricParameters>{{LabeledMetricParameters{ 399 {LabeledSensorInfo{"Service", 400 "/xyz/openbmc_project/sensors/power/p1", 401 "Metadata1"}}, 402 operationType, 403 "MetricId1", 404 CollectionTimeScope::point, 405 CollectionDuration(Milliseconds(0u))}}}); 406 407 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 408 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 409 410 auto [ec, path] = addReport(reportParams); 411 412 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 413 EXPECT_THAT(path, Eq("/"s + reportParams.reportId())); 414 } 415 416 class TestReportManagerStorage : public TestReportManager 417 { 418 public: 419 using FilePath = interfaces::JsonStorage::FilePath; 420 using DirectoryPath = interfaces::JsonStorage::DirectoryPath; 421 422 void SetUp() override 423 { 424 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)).Times(0); 425 426 ON_CALL(storageMock, list()) 427 .WillByDefault(Return(std::vector<FilePath>{FilePath("report1")})); 428 ON_CALL(storageMock, load(FilePath("report1"))) 429 .WillByDefault(InvokeWithoutArgs([this] { return data; })); 430 } 431 432 void makeReportManager() 433 { 434 sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr), 435 std::move(storageMockPtr), 436 DbusEnvironment::getObjServer()); 437 } 438 439 nlohmann::json data = nlohmann::json{ 440 {"Enabled", reportParams.enabled()}, 441 {"Version", Report::reportVersion}, 442 {"Id", reportParams.reportId()}, 443 {"Name", reportParams.reportName()}, 444 {"ReportingType", utils::toUnderlying(reportParams.reportingType())}, 445 {"ReportActions", reportParams.reportActions()}, 446 {"Interval", reportParams.interval().count()}, 447 {"ReportUpdates", utils::toUnderlying(reportParams.reportUpdates())}, 448 {"AppendLimit", reportParams.appendLimit()}, 449 {"ReadingParameters", reportParams.metricParameters()}}; 450 }; 451 452 TEST_F(TestReportManagerStorage, reportManagerCtorAddReportFromStorage) 453 { 454 reportFactoryMock.expectMake(reportParams, _, Ref(storageMock)); 455 456 makeReportManager(); 457 } 458 459 TEST_F(TestReportManagerStorage, 460 reportManagerCtorRemoveFileIfVersionDoesNotMatch) 461 { 462 data["Version"] = Report::reportVersion - 1; 463 464 EXPECT_CALL(storageMock, remove(FilePath("report1"))); 465 466 makeReportManager(); 467 } 468 469 TEST_F(TestReportManagerStorage, 470 reportManagerCtorRemoveFileIfIntervalHasWrongType) 471 { 472 data["Interval"] = "1000"; 473 474 EXPECT_CALL(storageMock, remove(FilePath("report1"))); 475 476 makeReportManager(); 477 } 478