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