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.reportName()); 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( 72 params.reportName(), utils::enumToString(params.reportingType()), 73 utils::enumToString(params.reportUpdates()), params.appendLimit(), 74 utils::transform( 75 params.reportActions(), 76 [](const auto v) { return utils::enumToString(v); }), 77 params.interval().count(), 78 toReadingParameters(params.metricParameters())); 79 } 80 81 template <class T> 82 static T getProperty(const std::string& property) 83 { 84 return DbusEnvironment::getProperty<T>( 85 ReportManager::reportManagerPath, 86 ReportManager::reportManagerIfaceName, property); 87 } 88 }; 89 90 TEST_F(TestReportManager, minInterval) 91 { 92 EXPECT_THAT(getProperty<uint64_t>("MinInterval"), 93 Eq(ReportManager::minInterval.count())); 94 } 95 96 TEST_F(TestReportManager, maxReports) 97 { 98 EXPECT_THAT(getProperty<size_t>("MaxReports"), 99 Eq(ReportManager::maxReports)); 100 } 101 102 TEST_F(TestReportManager, addReport) 103 { 104 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 105 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 106 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 107 108 auto [ec, path] = addReport(reportParams); 109 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 110 EXPECT_THAT(path, Eq(reportMock.getPath())); 111 } 112 113 TEST_F(TestReportManager, addReportWithMaxLengthName) 114 { 115 std::string reportName(ReportManager::maxReportNameLength, 'z'); 116 reportParams.reportName(reportName); 117 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)); 118 119 auto [ec, path] = addReport(reportParams); 120 121 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 122 EXPECT_THAT(path, Eq("/"s + reportName)); 123 } 124 125 TEST_F(TestReportManager, DISABLED_failToAddReportWithTooLongName) 126 { 127 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 128 .Times(0); 129 130 reportParams.reportName( 131 std::string(ReportManager::maxReportNameLength + 1, 'z')); 132 133 auto [ec, path] = addReport(reportParams); 134 135 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 136 EXPECT_THAT(path, Eq(std::string())); 137 } 138 139 TEST_F(TestReportManager, DISABLED_failToAddReportTwice) 140 { 141 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 142 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 143 144 addReport(reportParams); 145 146 auto [ec, path] = addReport(reportParams); 147 148 EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists)); 149 EXPECT_THAT(path, Eq(std::string())); 150 } 151 152 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidInterval) 153 { 154 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 155 .Times(0); 156 157 reportParams.reportingType(ReportingType::periodic); 158 reportParams.interval(reportParams.interval() - 1ms); 159 160 auto [ec, path] = addReport(reportParams); 161 162 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 163 EXPECT_THAT(path, Eq(std::string())); 164 } 165 166 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidReportingType) 167 { 168 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 169 .Times(0); 170 171 auto [ec, path] = addReport( 172 reportParams.reportName(), "InvalidReportingType", 173 utils::transform(reportParams.reportActions(), 174 [](const auto v) { return utils::enumToString(v); }), 175 reportParams.interval().count(), 176 toReadingParameters(reportParams.metricParameters())); 177 178 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 179 EXPECT_THAT(path, Eq(std::string())); 180 } 181 182 TEST_F(TestReportManager, DISABLED_failToAddReportWithMoreSensorsThanExpected) 183 { 184 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 185 .Times(0); 186 187 auto metricParams = reportParams.metricParameters(); 188 for (size_t i = 0; i < ReportManager::maxReadingParams + 1; i++) 189 { 190 metricParams.push_back(metricParams.front()); 191 } 192 reportParams.metricParameters(std::move(metricParams)); 193 194 auto [ec, path] = addReport(reportParams); 195 196 EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long)); 197 EXPECT_THAT(path, Eq(std::string())); 198 } 199 200 TEST_F(TestReportManager, DISABLED_failToAddReportWhenMaxReportIsReached) 201 { 202 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock)) 203 .Times(ReportManager::maxReports); 204 205 for (size_t i = 0; i < ReportManager::maxReports; i++) 206 { 207 reportParams.reportName(reportParams.reportName() + std::to_string(i)); 208 209 auto [ec, path] = addReport(reportParams); 210 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 211 } 212 213 reportParams.reportName(reportParams.reportName() + 214 std::to_string(ReportManager::maxReports)); 215 auto [ec, path] = addReport(reportParams); 216 217 EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open)); 218 EXPECT_THAT(path, Eq(std::string())); 219 } 220 221 TEST_F(TestReportManager, removeReport) 222 { 223 { 224 InSequence seq; 225 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)); 226 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 227 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 228 EXPECT_CALL(reportMock, Die()); 229 EXPECT_CALL(checkPoint, Call("end")); 230 } 231 232 addReport(reportParams); 233 sut->removeReport(&reportMock); 234 checkPoint.Call("end"); 235 } 236 237 TEST_F(TestReportManager, removingReportThatIsNotInContainerHasNoEffect) 238 { 239 { 240 InSequence seq; 241 EXPECT_CALL(checkPoint, Call("end")); 242 EXPECT_CALL(reportMock, Die()); 243 } 244 245 sut->removeReport(&reportMock); 246 checkPoint.Call("end"); 247 } 248 249 TEST_F(TestReportManager, removingSameReportTwiceHasNoSideEffect) 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 sut->removeReport(&reportMock); 263 checkPoint.Call("end"); 264 } 265 266 TEST_F(TestReportManager, updateReportCallsUpdateReadingsForExistReport) 267 { 268 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 269 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 270 EXPECT_CALL(reportMock, updateReadings()); 271 272 addReport(reportParams); 273 sut->updateReport(reportParams.reportName()); 274 } 275 276 TEST_F(TestReportManager, updateReportDoNothingIfReportDoesNotExist) 277 { 278 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 279 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 280 EXPECT_CALL(reportMock, updateReadings()).Times(0); 281 282 addReport(reportParams); 283 sut->updateReport("NotAReport"); 284 } 285 286 class TestReportManagerWithAggregationOperationType : 287 public TestReportManager, 288 public WithParamInterface<OperationType> 289 { 290 public: 291 OperationType operationType = GetParam(); 292 }; 293 294 INSTANTIATE_TEST_SUITE_P(_, TestReportManagerWithAggregationOperationType, 295 Values(OperationType::single, OperationType::max, 296 OperationType::min, OperationType::avg, 297 OperationType::sum)); 298 299 TEST_P(TestReportManagerWithAggregationOperationType, 300 addReportWithDifferentOperationTypes) 301 { 302 reportParams.metricParameters( 303 std::vector<LabeledMetricParameters>{{LabeledMetricParameters{ 304 {LabeledSensorParameters{"Service", 305 "/xyz/openbmc_project/sensors/power/p1"}}, 306 operationType, 307 "MetricId1", 308 "Metadata1", 309 CollectionTimeScope::point, 310 CollectionDuration(Milliseconds(0u))}}}); 311 312 reportFactoryMock.expectMake(reportParams, Ref(*sut), Ref(storageMock)) 313 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 314 315 auto [ec, path] = addReport(reportParams); 316 317 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 318 EXPECT_THAT(path, Eq("/"s + reportParams.reportName())); 319 } 320 321 class TestReportManagerStorage : public TestReportManager 322 { 323 public: 324 using FilePath = interfaces::JsonStorage::FilePath; 325 using DirectoryPath = interfaces::JsonStorage::DirectoryPath; 326 327 void SetUp() override 328 { 329 EXPECT_CALL(reportFactoryMock, convertMetricParams(_, _)).Times(0); 330 331 ON_CALL(storageMock, list()) 332 .WillByDefault(Return(std::vector<FilePath>{FilePath("report1")})); 333 ON_CALL(storageMock, load(FilePath("report1"))) 334 .WillByDefault(InvokeWithoutArgs([this] { return data; })); 335 } 336 337 void makeReportManager() 338 { 339 sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr), 340 std::move(storageMockPtr), 341 DbusEnvironment::getObjServer()); 342 } 343 344 nlohmann::json data = nlohmann::json{ 345 {"Enabled", reportParams.enabled()}, 346 {"Version", Report::reportVersion}, 347 {"Name", reportParams.reportName()}, 348 {"ReportingType", utils::toUnderlying(reportParams.reportingType())}, 349 {"ReportActions", reportParams.reportActions()}, 350 {"Interval", reportParams.interval().count()}, 351 {"ReportUpdates", utils::toUnderlying(reportParams.reportUpdates())}, 352 {"AppendLimit", reportParams.appendLimit()}, 353 {"ReadingParameters", reportParams.metricParameters()}}; 354 }; 355 356 TEST_F(TestReportManagerStorage, reportManagerCtorAddReportFromStorage) 357 { 358 reportFactoryMock.expectMake(reportParams, _, Ref(storageMock)); 359 360 makeReportManager(); 361 } 362 363 TEST_F(TestReportManagerStorage, 364 reportManagerCtorRemoveFileIfVersionDoesNotMatch) 365 { 366 data["Version"] = Report::reportVersion - 1; 367 368 EXPECT_CALL(storageMock, remove(FilePath("report1"))); 369 370 makeReportManager(); 371 } 372 373 TEST_F(TestReportManagerStorage, 374 reportManagerCtorRemoveFileIfIntervalHasWrongType) 375 { 376 data["Interval"] = "1000"; 377 378 EXPECT_CALL(storageMock, remove(FilePath("report1"))); 379 380 makeReportManager(); 381 } 382