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, DISABLED_failToAddReportWithMoreSensorsThanExpected) 211 { 212 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 213 .Times(0); 214 215 auto metricParams = reportParams.metricParameters(); 216 for (size_t i = 0; i < ReportManager::maxReadingParams + 1; i++) 217 { 218 metricParams.push_back(metricParams.front()); 219 } 220 reportParams.metricParameters(std::move(metricParams)); 221 222 auto [ec, path] = addReport(reportParams); 223 224 EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long)); 225 EXPECT_THAT(path, Eq(std::string())); 226 } 227 228 TEST_F(TestReportManager, DISABLED_failToAddReportWhenMaxReportIsReached) 229 { 230 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 231 .Times(ReportManager::maxReports); 232 233 for (size_t i = 0; i < ReportManager::maxReports; i++) 234 { 235 reportParams.reportId(reportParams.reportName() + std::to_string(i)); 236 237 auto [ec, path] = addReport(reportParams); 238 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 239 } 240 241 reportParams.reportId(reportParams.reportName() + 242 std::to_string(ReportManager::maxReports)); 243 auto [ec, path] = addReport(reportParams); 244 245 EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open)); 246 EXPECT_THAT(path, Eq(std::string())); 247 } 248 249 TEST_F(TestReportManager, removeReport) 250 { 251 { 252 InSequence seq; 253 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 254 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 255 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 256 EXPECT_CALL(reportMock, Die()); 257 EXPECT_CALL(checkPoint, Call("end")); 258 } 259 260 addReport(reportParams); 261 sut->removeReport(&reportMock); 262 checkPoint.Call("end"); 263 } 264 265 TEST_F(TestReportManager, removingReportThatIsNotInContainerHasNoEffect) 266 { 267 { 268 InSequence seq; 269 EXPECT_CALL(checkPoint, Call("end")); 270 EXPECT_CALL(reportMock, Die()); 271 } 272 273 sut->removeReport(&reportMock); 274 checkPoint.Call("end"); 275 } 276 277 TEST_F(TestReportManager, removingSameReportTwiceHasNoSideEffect) 278 { 279 { 280 InSequence seq; 281 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 282 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 283 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 284 EXPECT_CALL(reportMock, Die()); 285 EXPECT_CALL(checkPoint, Call("end")); 286 } 287 288 addReport(reportParams); 289 sut->removeReport(&reportMock); 290 sut->removeReport(&reportMock); 291 checkPoint.Call("end"); 292 } 293 294 TEST_F(TestReportManager, updateReportCallsUpdateReadingsForExistReport) 295 { 296 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 297 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 298 EXPECT_CALL(reportMock, updateReadings()); 299 300 addReport(reportParams); 301 sut->updateReport(reportParams.reportId()); 302 } 303 304 TEST_F(TestReportManager, updateReportDoNothingIfReportDoesNotExist) 305 { 306 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 307 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 308 EXPECT_CALL(reportMock, updateReadings()).Times(0); 309 310 addReport(reportParams); 311 sut->updateReport("NotAReport"); 312 } 313 314 class TestReportManagerWithAggregationOperationType : 315 public TestReportManager, 316 public WithParamInterface<OperationType> 317 { 318 public: 319 OperationType operationType = GetParam(); 320 }; 321 322 INSTANTIATE_TEST_SUITE_P(_, TestReportManagerWithAggregationOperationType, 323 Values(OperationType::single, OperationType::max, 324 OperationType::min, OperationType::avg, 325 OperationType::sum)); 326 327 TEST_P(TestReportManagerWithAggregationOperationType, 328 addReportWithDifferentOperationTypes) 329 { 330 reportParams.metricParameters( 331 std::vector<LabeledMetricParameters>{{LabeledMetricParameters{ 332 {LabeledSensorParameters{"Service", 333 "/xyz/openbmc_project/sensors/power/p1", 334 "Metadata1"}}, 335 operationType, 336 "MetricId1", 337 CollectionTimeScope::point, 338 CollectionDuration(Milliseconds(0u))}}}); 339 340 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 341 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 342 343 auto [ec, path] = addReport(reportParams); 344 345 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 346 EXPECT_THAT(path, Eq("/"s + reportParams.reportId())); 347 } 348 349 class TestReportManagerStorage : public TestReportManager 350 { 351 public: 352 using FilePath = interfaces::JsonStorage::FilePath; 353 using DirectoryPath = interfaces::JsonStorage::DirectoryPath; 354 355 void SetUp() override 356 { 357 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)).Times(0); 358 359 ON_CALL(storageMock, list()) 360 .WillByDefault(Return(std::vector<FilePath>{FilePath("report1")})); 361 ON_CALL(storageMock, load(FilePath("report1"))) 362 .WillByDefault(InvokeWithoutArgs([this] { return data; })); 363 } 364 365 void makeReportManager() 366 { 367 sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr), 368 std::move(storageMockPtr), 369 DbusEnvironment::getObjServer()); 370 } 371 372 nlohmann::json data = nlohmann::json{ 373 {"Enabled", reportParams.enabled()}, 374 {"Version", Report::reportVersion}, 375 {"Id", reportParams.reportId()}, 376 {"Name", reportParams.reportName()}, 377 {"ReportingType", utils::toUnderlying(reportParams.reportingType())}, 378 {"ReportActions", reportParams.reportActions()}, 379 {"Interval", reportParams.interval().count()}, 380 {"ReportUpdates", utils::toUnderlying(reportParams.reportUpdates())}, 381 {"AppendLimit", reportParams.appendLimit()}, 382 {"ReadingParameters", reportParams.metricParameters()}}; 383 }; 384 385 TEST_F(TestReportManagerStorage, reportManagerCtorAddReportFromStorage) 386 { 387 reportFactoryMock.expectMake(reportParams, _, Ref(storageMock)); 388 389 makeReportManager(); 390 } 391 392 TEST_F(TestReportManagerStorage, 393 reportManagerCtorRemoveFileIfVersionDoesNotMatch) 394 { 395 data["Version"] = Report::reportVersion - 1; 396 397 EXPECT_CALL(storageMock, remove(FilePath("report1"))); 398 399 makeReportManager(); 400 } 401 402 TEST_F(TestReportManagerStorage, 403 reportManagerCtorRemoveFileIfIntervalHasWrongType) 404 { 405 data["Interval"] = "1000"; 406 407 EXPECT_CALL(storageMock, remove(FilePath("report1"))); 408 409 makeReportManager(); 410 } 411