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