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 sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr), 39 std::move(storageMockPtr), 40 DbusEnvironment::getObjServer()); 41 } 42 43 void TearDown() override 44 { 45 DbusEnvironment::synchronizeIoc(); 46 } 47 48 std::pair<boost::system::error_code, std::string> 49 addReport(const ReportParams& params) 50 { 51 std::promise<std::pair<boost::system::error_code, std::string>> 52 addReportPromise; 53 DbusEnvironment::getBus()->async_method_call( 54 [&addReportPromise](boost::system::error_code ec, 55 const std::string& path) { 56 addReportPromise.set_value({ec, path}); 57 }, 58 DbusEnvironment::serviceName(), ReportManager::reportManagerPath, 59 ReportManager::reportManagerIfaceName, "AddReport", 60 params.reportName(), params.reportingType(), 61 params.emitReadingUpdate(), params.logToMetricReportCollection(), 62 static_cast<uint64_t>(params.interval().count()), 63 params.readingParameters()); 64 return DbusEnvironment::waitForFuture(addReportPromise.get_future()); 65 } 66 67 template <class T> 68 static T getProperty(std::string property) 69 { 70 std::promise<T> propertyPromise; 71 sdbusplus::asio::getProperty<T>( 72 *DbusEnvironment::getBus(), DbusEnvironment::serviceName(), 73 ReportManager::reportManagerPath, 74 ReportManager::reportManagerIfaceName, property, 75 [&propertyPromise](boost::system::error_code ec) { 76 EXPECT_THAT(static_cast<bool>(ec), ::testing::Eq(false)); 77 propertyPromise.set_value(T{}); 78 }, 79 [&propertyPromise](T t) { propertyPromise.set_value(t); }); 80 return DbusEnvironment::waitForFuture(propertyPromise.get_future()); 81 } 82 }; 83 84 TEST_F(TestReportManager, minInterval) 85 { 86 EXPECT_THAT(getProperty<uint64_t>("MinInterval"), 87 Eq(static_cast<uint64_t>(ReportManager::minInterval.count()))); 88 } 89 90 TEST_F(TestReportManager, maxReports) 91 { 92 EXPECT_THAT(getProperty<size_t>("MaxReports"), 93 Eq(ReportManager::maxReports)); 94 } 95 96 TEST_F(TestReportManager, addReport) 97 { 98 reportFactoryMock.expectMake(_, reportParams, Ref(*sut), Ref(storageMock)) 99 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 100 101 auto [ec, path] = addReport(reportParams); 102 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 103 EXPECT_THAT(path, Eq(reportMock.getPath())); 104 } 105 106 TEST_F(TestReportManager, DISABLED_failToAddReportTwice) 107 { 108 reportFactoryMock.expectMake(_, reportParams, Ref(*sut), Ref(storageMock)) 109 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 110 111 addReport(reportParams); 112 113 auto [ec, path] = addReport(reportParams); 114 EXPECT_THAT(ec.value(), Eq(boost::system::errc::file_exists)); 115 EXPECT_THAT(path, Eq(std::string())); 116 } 117 118 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidInterval) 119 { 120 reportFactoryMock.expectMake(_, std::nullopt, Ref(*sut), Ref(storageMock)) 121 .Times(0); 122 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock), _) 123 .Times(0); 124 125 reportParams.reportingType("Periodic"); 126 reportParams.interval(reportParams.interval() - 1ms); 127 128 auto [ec, path] = addReport(reportParams); 129 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 130 EXPECT_THAT(path, Eq(std::string())); 131 } 132 133 TEST_F(TestReportManager, DISABLED_failToAddReportWithInvalidReportingType) 134 { 135 reportFactoryMock.expectMake(_, std::nullopt, Ref(*sut), Ref(storageMock)) 136 .Times(0); 137 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock), _) 138 .Times(0); 139 140 reportParams.reportingType("Invalid"); 141 142 auto [ec, path] = addReport(reportParams); 143 EXPECT_THAT(ec.value(), Eq(boost::system::errc::invalid_argument)); 144 EXPECT_THAT(path, Eq(std::string())); 145 } 146 147 TEST_F(TestReportManager, DISABLED_failToAddReportWithMoreSensorsThanExpected) 148 { 149 reportFactoryMock.expectMake(_, std::nullopt, Ref(*sut), Ref(storageMock)) 150 .Times(0); 151 reportFactoryMock.expectMake(std::nullopt, Ref(*sut), Ref(storageMock), _) 152 .Times(0); 153 154 auto readingParams = reportParams.readingParameters(); 155 for (size_t i = 0; i < ReportManager::maxReadingParams + 1; i++) 156 { 157 readingParams.push_back(readingParams.front()); 158 } 159 reportParams.readingParameters(std::move(readingParams)); 160 161 auto [ec, path] = addReport(reportParams); 162 EXPECT_THAT(ec.value(), Eq(boost::system::errc::argument_list_too_long)); 163 EXPECT_THAT(path, Eq(std::string())); 164 } 165 166 TEST_F(TestReportManager, DISABLED_failToAddReportWhenMaxReportIsReached) 167 { 168 reportFactoryMock.expectMake(_, std::nullopt, Ref(*sut), Ref(storageMock)) 169 .Times(ReportManager::maxReports); 170 171 for (size_t i = 0; i < ReportManager::maxReports; i++) 172 { 173 reportParams.reportName(reportParams.reportName() + std::to_string(i)); 174 175 auto [ec, path] = addReport(reportParams); 176 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 177 } 178 179 reportParams.reportName(reportParams.reportName() + 180 std::to_string(ReportManager::maxReports)); 181 auto [ec, path] = addReport(reportParams); 182 EXPECT_THAT(ec.value(), Eq(boost::system::errc::too_many_files_open)); 183 EXPECT_THAT(path, Eq(std::string())); 184 } 185 186 TEST_F(TestReportManager, removeReport) 187 { 188 { 189 InSequence seq; 190 reportFactoryMock 191 .expectMake(_, reportParams, Ref(*sut), Ref(storageMock)) 192 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 193 EXPECT_CALL(reportMock, Die()); 194 EXPECT_CALL(checkPoint, Call("end")); 195 } 196 197 addReport(reportParams); 198 sut->removeReport(&reportMock); 199 checkPoint.Call("end"); 200 } 201 202 TEST_F(TestReportManager, removingReportThatIsNotInContainerHasNoEffect) 203 { 204 { 205 InSequence seq; 206 EXPECT_CALL(checkPoint, Call("end")); 207 EXPECT_CALL(reportMock, Die()); 208 } 209 210 sut->removeReport(&reportMock); 211 checkPoint.Call("end"); 212 } 213 214 TEST_F(TestReportManager, removingSameReportTwiceHasNoSideEffect) 215 { 216 { 217 InSequence seq; 218 reportFactoryMock 219 .expectMake(_, reportParams, Ref(*sut), Ref(storageMock)) 220 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 221 EXPECT_CALL(reportMock, Die()); 222 EXPECT_CALL(checkPoint, Call("end")); 223 } 224 225 addReport(reportParams); 226 sut->removeReport(&reportMock); 227 sut->removeReport(&reportMock); 228 checkPoint.Call("end"); 229 } 230 231 TEST_F(TestReportManager, updateReportCallsUpdateReadingsForExistReport) 232 { 233 reportFactoryMock.expectMake(_, reportParams, Ref(*sut), Ref(storageMock)) 234 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 235 EXPECT_CALL(reportMock, updateReadings()); 236 237 addReport(reportParams); 238 sut->updateReport(reportParams.reportName()); 239 } 240 241 TEST_F(TestReportManager, updateReportDoNothingIfReportDoesNotExist) 242 { 243 reportFactoryMock.expectMake(_, reportParams, Ref(*sut), Ref(storageMock)) 244 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 245 EXPECT_CALL(reportMock, updateReadings()).Times(0); 246 247 addReport(reportParams); 248 sut->updateReport("NotAReport"); 249 } 250 251 class TestReportManagerWithAggregationOperationType : 252 public TestReportManager, 253 public WithParamInterface<OperationType> 254 { 255 public: 256 OperationType operationType = GetParam(); 257 }; 258 259 INSTANTIATE_TEST_SUITE_P(_, TestReportManagerWithAggregationOperationType, 260 Values(OperationType::single, OperationType::max, 261 OperationType::min, OperationType::avg, 262 OperationType::sum)); 263 264 TEST_P(TestReportManagerWithAggregationOperationType, 265 addReportWithDifferentOperationTypes) 266 { 267 reportParams.readingParameters( 268 {{{sdbusplus::message::object_path( 269 "/xyz/openbmc_project/sensors/power/p1")}, 270 utils::enumToString(operationType), 271 "MetricId1", 272 "Metadata1"}}); 273 274 reportFactoryMock.expectMake(_, reportParams, Ref(*sut), Ref(storageMock)) 275 .WillOnce(Return(ByMove(std::move(reportMockPtr)))); 276 277 auto [ec, path] = addReport(reportParams); 278 EXPECT_THAT(ec.value(), Eq(boost::system::errc::success)); 279 EXPECT_THAT(path, Eq("/"s + reportParams.reportName())); 280 } 281 282 class TestReportManagerStorage : public TestReportManager 283 { 284 public: 285 using FilePath = interfaces::JsonStorage::FilePath; 286 using DirectoryPath = interfaces::JsonStorage::DirectoryPath; 287 288 void SetUp() override 289 { 290 ON_CALL(storageMock, list()) 291 .WillByDefault(Return(std::vector<FilePath>{FilePath("report1")})); 292 ON_CALL(storageMock, load(FilePath("report1"))) 293 .WillByDefault(InvokeWithoutArgs([this] { return data; })); 294 } 295 296 void makeReportManager() 297 { 298 sut = std::make_unique<ReportManager>(std::move(reportFactoryMockPtr), 299 std::move(storageMockPtr), 300 DbusEnvironment::getObjServer()); 301 } 302 303 static std::vector<LabeledMetricParameters> 304 convertToLabeled(const ReadingParameters& params) 305 { 306 return utils::transform(params, [](const auto& item) { 307 return LabeledMetricParameters( 308 LabeledSensorParameters("service", std::get<0>(item)), 309 utils::stringToOperationType(std::get<1>(item)), 310 std::get<2>(item), std::get<3>(item)); 311 }); 312 } 313 314 nlohmann::json data = nlohmann::json{ 315 {"Version", Report::reportVersion}, 316 {"Name", reportParams.reportName()}, 317 {"ReportingType", reportParams.reportingType()}, 318 {"EmitsReadingsUpdate", reportParams.emitReadingUpdate()}, 319 {"LogToMetricReportsCollection", 320 reportParams.logToMetricReportCollection()}, 321 {"Interval", reportParams.interval().count()}, 322 {"ReadingParameters", 323 convertToLabeled(reportParams.readingParameters())}}; 324 }; 325 326 TEST_F(TestReportManagerStorage, reportManagerCtorAddReportFromStorage) 327 { 328 reportFactoryMock.expectMake( 329 reportParams, _, Ref(storageMock), 330 ElementsAreArray(convertToLabeled(reportParams.readingParameters()))); 331 332 makeReportManager(); 333 } 334 335 TEST_F(TestReportManagerStorage, 336 reportManagerCtorRemoveFileIfVersionDoesNotMatch) 337 { 338 data["Version"] = Report::reportVersion - 1; 339 340 EXPECT_CALL(storageMock, remove(FilePath("report1"))); 341 342 makeReportManager(); 343 } 344 345 TEST_F(TestReportManagerStorage, 346 reportManagerCtorRemoveFileIfIntervalHasWrongType) 347 { 348 data["Interval"] = "1000"; 349 350 EXPECT_CALL(storageMock, remove(FilePath("report1"))); 351 352 makeReportManager(); 353 } 354