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, nameIsUsedToGenerateIdWhenIdIsEmptyInAddReport) 125 { 126 reportParams.reportId("ReportName"); 127 reportParams.reportName("ReportName"); 128 129 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)); 130 131 auto [ec, path] = addReport(reportParams.reportId("")); 132 133 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 134 EXPECT_THAT(path, Eq("/ReportName")); 135 } 136 137 TEST_F(TestReportManager, nameIsUsedToGenerateIdWhenIdIsNamespace) 138 { 139 reportParams.reportId("Prefix/ReportName"); 140 reportParams.reportName("ReportName"); 141 142 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)); 143 144 auto [ec, path] = addReport(reportParams.reportId("Prefix/")); 145 146 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 147 EXPECT_THAT(path, Eq("/Prefix/ReportName")); 148 } 149 150 TEST_F(TestReportManager, addReportWithMaxLengthId) 151 { 152 std::string reportId(ReportManager::maxReportIdLength, 'z'); 153 reportParams.reportId(reportId); 154 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)); 155 156 auto [ec, path] = addReport(reportParams); 157 158 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 159 EXPECT_THAT(path, Eq("/"s + reportId)); 160 } 161 162 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooLongName) 163 { 164 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 165 .Times(0); 166 167 reportParams.reportId( 168 std::string(ReportManager::maxReportIdLength + 1, 'z')); 169 170 auto [ec, path] = addReport(reportParams); 171 172 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 173 EXPECT_THAT(path, Eq(std::string())); 174 } 175 176 TEST_F(TestReportManager, DISABLED_failToAddReportTwice) 177 { 178 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 179 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 180 181 addReport(reportParams); 182 183 auto [ec, path] = addReport(reportParams); 184 185 EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists)); 186 EXPECT_THAT(path, Eq(std::string())); 187 } 188 189 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidInterval) 190 { 191 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 192 .Times(0); 193 194 reportParams.reportingType(ReportingType::periodic); 195 reportParams.interval(reportParams.interval() - 1ms); 196 197 auto [ec, path] = addReport(reportParams); 198 199 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 200 EXPECT_THAT(path, Eq(std::string())); 201 } 202 203 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidReportingType) 204 { 205 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 206 .Times(0); 207 208 auto [ec, path] = addReport( 209 reportParams.reportName(), "InvalidReportingType", 210 utils::transform(reportParams.reportActions(), 211 [](const auto v) { return utils::enumToString(v); }), 212 reportParams.interval().count(), 213 toReadingParameters(reportParams.metricParameters())); 214 215 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 216 EXPECT_THAT(path, Eq(std::string())); 217 } 218 219 TEST_F(TestReportManager, 220 DISABLED_failToAddReportWithMoreMetricPropertiesThanExpected) 221 { 222 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 223 .Times(0); 224 225 reportParams.metricParameters( 226 std::vector<LabeledMetricParameters>{{LabeledMetricParameters{ 227 {LabeledSensorInfo{"Service", 228 "/xyz/openbmc_project/sensors/power/p1", 229 "Metadata1"}}, 230 OperationType::avg, 231 "MetricId1", 232 CollectionTimeScope::point, 233 CollectionDuration(Milliseconds(0u))}}}); 234 235 auto metricParams = reportParams.metricParameters(); 236 auto& metricParamsVec = 237 metricParams[0].at_label<utils::tstring::SensorPath>(); 238 239 for (size_t i = 0; i < ReportManager::maxNumberMetrics; i++) 240 { 241 metricParamsVec.emplace_back(LabeledSensorInfo{ 242 "Service", "/xyz/openbmc_project/sensors/power/p1", "Metadata1"}); 243 } 244 245 reportParams.metricParameters(std::move(metricParams)); 246 247 auto [ec, path] = addReport(reportParams); 248 249 EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long)); 250 EXPECT_THAT(path, Eq(std::string())); 251 } 252 253 TEST_F(TestReportManager, DISABLED_failToAddReportWithMoreMetricsThanExpected) 254 { 255 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 256 .Times(0); 257 258 auto metricParams = std::vector<LabeledMetricParameters>{}; 259 260 for (size_t i = 0; i < ReportManager::maxNumberMetrics + 1; i++) 261 { 262 metricParams.emplace_back( 263 LabeledMetricParameters{{}, 264 OperationType::avg, 265 "MetricId1", 266 CollectionTimeScope::point, 267 CollectionDuration(Milliseconds(0u))}); 268 } 269 270 reportParams.metricParameters(std::move(metricParams)); 271 272 auto [ec, path] = addReport(reportParams); 273 274 EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long)); 275 EXPECT_THAT(path, Eq(std::string())); 276 } 277 278 TEST_F(TestReportManager, DISABLED_failToAddReportWithAppendLimitGreaterThanMax) 279 { 280 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 281 .Times(0); 282 283 reportParams.appendLimit(ReportManager::maxAppendLimit + 1); 284 285 auto [ec, path] = addReport(reportParams); 286 287 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 288 EXPECT_THAT(path, Eq(std::string())); 289 } 290 291 TEST_F(TestReportManager, addReportWithAppendLimitEqualToUint64MaxIsAllowed) 292 { 293 reportParams.appendLimit(std::numeric_limits<uint64_t>::max()); 294 295 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 296 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 297 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 298 299 auto [ec, path] = addReport(reportParams); 300 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 301 EXPECT_THAT(path, Eq(reportMock.getPath())); 302 } 303 304 TEST_F(TestReportManager, DISABLED_failToAddReportWhenMaxReportIsReached) 305 { 306 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 307 .Times(ReportManager::maxReports); 308 309 for (size_t i = 0; i < ReportManager::maxReports; i++) 310 { 311 reportParams.reportId(reportParams.reportName() + std::to_string(i)); 312 313 auto [ec, path] = addReport(reportParams); 314 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 315 } 316 317 reportParams.reportId(reportParams.reportName() + 318 std::to_string(ReportManager::maxReports)); 319 auto [ec, path] = addReport(reportParams); 320 321 EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open)); 322 EXPECT_THAT(path, Eq(std::string())); 323 } 324 325 TEST_F(TestReportManager, removeReport) 326 { 327 { 328 InSequence seq; 329 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 330 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 331 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 332 EXPECT_CALL(reportMock, Die()); 333 EXPECT_CALL(checkPoint, Call("end")); 334 } 335 336 addReport(reportParams); 337 sut->removeReport(&reportMock); 338 checkPoint.Call("end"); 339 } 340 341 TEST_F(TestReportManager, removingReportThatIsNotInContainerHasNoEffect) 342 { 343 { 344 InSequence seq; 345 EXPECT_CALL(checkPoint, Call("end")); 346 EXPECT_CALL(reportMock, Die()); 347 } 348 349 sut->removeReport(&reportMock); 350 checkPoint.Call("end"); 351 } 352 353 TEST_F(TestReportManager, removingSameReportTwiceHasNoSideEffect) 354 { 355 { 356 InSequence seq; 357 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 358 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 359 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 360 EXPECT_CALL(reportMock, Die()); 361 EXPECT_CALL(checkPoint, Call("end")); 362 } 363 364 addReport(reportParams); 365 sut->removeReport(&reportMock); 366 sut->removeReport(&reportMock); 367 checkPoint.Call("end"); 368 } 369 370 class TestReportManagerWithAggregationOperationType : 371 public TestReportManager, 372 public WithParamInterface<OperationType> 373 { 374 public: 375 OperationType operationType = GetParam(); 376 }; 377 378 INSTANTIATE_TEST_SUITE_P(_, TestReportManagerWithAggregationOperationType, 379 Values(OperationType::max, OperationType::min, 380 OperationType::avg, OperationType::sum)); 381 382 TEST_P(TestReportManagerWithAggregationOperationType, 383 addReportWithDifferentOperationTypes) 384 { 385 reportParams.metricParameters( 386 std::vector<LabeledMetricParameters>{{LabeledMetricParameters{ 387 {LabeledSensorInfo{"Service", 388 "/xyz/openbmc_project/sensors/power/p1", 389 "Metadata1"}}, 390 operationType, 391 "MetricId1", 392 CollectionTimeScope::point, 393 CollectionDuration(Milliseconds(0u))}}}); 394 395 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 396 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 397 398 auto [ec, path] = addReport(reportParams); 399 400 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 401 EXPECT_THAT(path, Eq("/"s + reportParams.reportId())); 402 } 403 404 class TestReportManagerStorage : public TestReportManager 405 { 406 public: 407 using FilePath = interfaces::JsonStorage::FilePath; 408 using DirectoryPath = interfaces::JsonStorage::DirectoryPath; 409 410 void SetUp() override 411 { 412 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)).Times(0); 413 414 ON_CALL(storageMock, list()) 415 .WillByDefault(Return(std::vector<FilePath>{FilePath("report1")})); 416 ON_CALL(storageMock, load(FilePath("report1"))) 417 .WillByDefault(InvokeWithoutArgs([this] { return data; })); 418 } 419 420 void makeReportManager() 421 { 422 sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr), 423 std::move(storageMockPtr), 424 DbusEnvironment::getObjServer()); 425 } 426 427 nlohmann::json data = nlohmann::json{ 428 {"Enabled", reportParams.enabled()}, 429 {"Version", Report::reportVersion}, 430 {"Id", reportParams.reportId()}, 431 {"Name", reportParams.reportName()}, 432 {"ReportingType", utils::toUnderlying(reportParams.reportingType())}, 433 {"ReportActions", reportParams.reportActions()}, 434 {"Interval", reportParams.interval().count()}, 435 {"ReportUpdates", utils::toUnderlying(reportParams.reportUpdates())}, 436 {"AppendLimit", reportParams.appendLimit()}, 437 {"ReadingParameters", reportParams.metricParameters()}}; 438 }; 439 440 TEST_F(TestReportManagerStorage, reportManagerCtorAddReportFromStorage) 441 { 442 reportFactoryMock.expectMake(reportParams, _, Ref(storageMock)); 443 444 makeReportManager(); 445 } 446 447 TEST_F(TestReportManagerStorage, 448 reportManagerCtorRemoveFileIfVersionDoesNotMatch) 449 { 450 data["Version"] = Report::reportVersion - 1; 451 452 EXPECT_CALL(storageMock, remove(FilePath("report1"))); 453 454 makeReportManager(); 455 } 456 457 TEST_F(TestReportManagerStorage, 458 reportManagerCtorRemoveFileIfIntervalHasWrongType) 459 { 460 data["Interval"] = "1000"; 461 462 EXPECT_CALL(storageMock, remove(FilePath("report1"))); 463 464 makeReportManager(); 465 } 466