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