1 #include "fakes/clock_fake.hpp" 2 #include "helpers.hpp" 3 #include "metric.hpp" 4 #include "mocks/sensor_mock.hpp" 5 #include "params/metric_params.hpp" 6 #include "utils/conv_container.hpp" 7 #include "utils/conversion.hpp" 8 #include "utils/tstring.hpp" 9 10 #include <gmock/gmock.h> 11 12 using namespace testing; 13 using namespace std::chrono_literals; 14 15 namespace tstring = utils::tstring; 16 17 using Timestamp = uint64_t; 18 19 class TestMetric : public Test 20 { 21 public: 22 static std::vector<std::shared_ptr<SensorMock>> 23 makeSensorMocks(size_t amount) 24 { 25 std::vector<std::shared_ptr<SensorMock>> result; 26 for (size_t i = 0; i < amount; ++i) 27 { 28 result.emplace_back(std::make_shared<NiceMock<SensorMock>>()); 29 } 30 return result; 31 } 32 33 std::shared_ptr<Metric> makeSut(const MetricParams& p) 34 { 35 return std::make_shared<Metric>( 36 utils::convContainer<std::shared_ptr<interfaces::Sensor>>( 37 sensorMocks), 38 p.operationType(), p.id(), p.metadata(), p.collectionTimeScope(), 39 p.collectionDuration(), std::move(clockFakePtr)); 40 } 41 42 MetricParams params = MetricParams() 43 .id("id") 44 .metadata("metadata") 45 .operationType(OperationType::avg) 46 .collectionTimeScope(CollectionTimeScope::point) 47 .collectionDuration(CollectionDuration(0ms)); 48 std::vector<std::shared_ptr<SensorMock>> sensorMocks = makeSensorMocks(1u); 49 std::unique_ptr<ClockFake> clockFakePtr = std::make_unique<ClockFake>(); 50 ClockFake& clockFake = *clockFakePtr; 51 std::shared_ptr<Metric> sut; 52 }; 53 54 TEST_F(TestMetric, subscribesForSensorDuringInitialization) 55 { 56 sut = makeSut(params); 57 58 EXPECT_CALL(*sensorMocks.front(), 59 registerForUpdates(Truly([sut = sut.get()](const auto& a0) { 60 return a0.lock().get() == sut; 61 }))); 62 63 sut->initialize(); 64 } 65 66 TEST_F(TestMetric, unsubscribesForSensorDuringDeinitialization) 67 { 68 sut = makeSut(params); 69 70 EXPECT_CALL(*sensorMocks.front(), 71 unregisterFromUpdates(Truly([sut = sut.get()](const auto& a0) { 72 return a0.lock().get() == sut; 73 }))); 74 75 sut->deinitialize(); 76 } 77 78 TEST_F(TestMetric, containsEmptyReadingAfterCreated) 79 { 80 sut = makeSut(params); 81 82 ASSERT_THAT(sut->getReadings(), 83 ElementsAre(MetricValue({"id", "metadata", 0., 0u}))); 84 } 85 86 TEST_F(TestMetric, parsesSensorMetadata) 87 { 88 using ReadingMetadata = 89 utils::LabeledTuple<std::tuple<std::string, std::string>, 90 utils::tstring::SensorDbusPath, 91 utils::tstring::SensorRedfishUri>; 92 93 nlohmann::json metadata; 94 metadata["MetricProperties"] = {"sensor1", "sensor2"}; 95 96 sensorMocks = makeSensorMocks(2); 97 sut = makeSut(params.metadata(metadata.dump())); 98 99 EXPECT_THAT( 100 sut->getReadings(), 101 ElementsAre( 102 MetricValue{"id", ReadingMetadata("", "sensor1").dump(), 0., 0u}, 103 MetricValue{"id", ReadingMetadata("", "sensor2").dump(), 0., 0u})); 104 } 105 106 TEST_F(TestMetric, parsesSensorMetadataWhenMoreMetadataThanSensors) 107 { 108 nlohmann::json metadata; 109 metadata["MetricProperties"] = {"sensor1", "sensor2"}; 110 111 sensorMocks = makeSensorMocks(1); 112 sut = makeSut(params.metadata(metadata.dump())); 113 114 EXPECT_THAT(sut->getReadings(), 115 ElementsAre(MetricValue{"id", metadata.dump(), 0., 0u})); 116 } 117 118 TEST_F(TestMetric, parsesSensorMetadataWhenMoreSensorsThanMetadata) 119 { 120 nlohmann::json metadata; 121 metadata["MetricProperties"] = {"sensor1"}; 122 123 sensorMocks = makeSensorMocks(2); 124 sut = makeSut(params.metadata(metadata.dump())); 125 126 EXPECT_THAT(sut->getReadings(), 127 ElementsAre(MetricValue{"id", metadata.dump(), 0., 0u}, 128 MetricValue{"id", metadata.dump(), 0., 0u})); 129 } 130 131 class TestMetricAfterInitialization : public TestMetric 132 { 133 public: 134 void SetUp() override 135 { 136 sut = makeSut(params); 137 sut->initialize(); 138 } 139 }; 140 141 TEST_F(TestMetricAfterInitialization, containsEmptyReading) 142 { 143 ASSERT_THAT(sut->getReadings(), 144 ElementsAre(MetricValue({"id", "metadata", 0., 0u}))); 145 } 146 147 TEST_F(TestMetricAfterInitialization, updatesMetricValuesOnSensorUpdate) 148 { 149 sut->sensorUpdated(*sensorMocks.front(), Timestamp{18}, 31.2); 150 151 ASSERT_THAT(sut->getReadings(), 152 ElementsAre(MetricValue{"id", "metadata", 31.2, 18u})); 153 } 154 155 TEST_F(TestMetricAfterInitialization, 156 throwsWhenUpdateIsPerformedOnUnknownSensor) 157 { 158 auto sensor = std::make_shared<StrictMock<SensorMock>>(); 159 EXPECT_THROW(sut->sensorUpdated(*sensor, Timestamp{10}), std::out_of_range); 160 EXPECT_THROW(sut->sensorUpdated(*sensor, Timestamp{10}, 20.0), 161 std::out_of_range); 162 } 163 164 TEST_F(TestMetricAfterInitialization, dumpsConfiguration) 165 { 166 namespace ts = utils::tstring; 167 168 ON_CALL(*sensorMocks.front(), id()) 169 .WillByDefault(Return(SensorMock::makeId("service1", "path1"))); 170 171 const auto conf = sut->dumpConfiguration(); 172 173 LabeledMetricParameters expected = {}; 174 expected.at_label<ts::Id>() = params.id(); 175 expected.at_label<ts::MetricMetadata>() = params.metadata(); 176 expected.at_label<ts::OperationType>() = params.operationType(); 177 expected.at_label<ts::CollectionTimeScope>() = params.collectionTimeScope(); 178 expected.at_label<ts::CollectionDuration>() = params.collectionDuration(); 179 expected.at_label<ts::SensorPath>() = { 180 LabeledSensorParameters("service1", "path1")}; 181 182 EXPECT_THAT(conf, Eq(expected)); 183 } 184 185 class TestMetricCalculationFunctions : 186 public TestMetric, 187 public WithParamInterface<MetricParams> 188 { 189 public: 190 void SetUp() override 191 { 192 clockFake.reset(); 193 sut = makeSut(params.operationType(GetParam().operationType()) 194 .collectionTimeScope(GetParam().collectionTimeScope()) 195 .collectionDuration(GetParam().collectionDuration())); 196 } 197 198 static std::vector<std::pair<Milliseconds, double>> defaultReadings() 199 { 200 std::vector<std::pair<Milliseconds, double>> ret; 201 ret.emplace_back(0ms, std::numeric_limits<double>::quiet_NaN()); 202 ret.emplace_back(10ms, 14.); 203 ret.emplace_back(1ms, 3.); 204 ret.emplace_back(5ms, 7.); 205 return ret; 206 } 207 }; 208 209 MetricParams defaultSingleParams() 210 { 211 return MetricParams() 212 .operationType(OperationType::single) 213 .readings(TestMetricCalculationFunctions::defaultReadings()) 214 .expectedReading(11ms, 7.0); 215 } 216 217 INSTANTIATE_TEST_SUITE_P( 218 OperationSingleReturnsLastReading, TestMetricCalculationFunctions, 219 Values( 220 defaultSingleParams().collectionTimeScope(CollectionTimeScope::point), 221 defaultSingleParams() 222 .collectionTimeScope(CollectionTimeScope::interval) 223 .collectionDuration(CollectionDuration(100ms)), 224 defaultSingleParams().collectionTimeScope( 225 CollectionTimeScope::startup))); 226 227 MetricParams defaultPointParams() 228 { 229 return defaultSingleParams().collectionTimeScope( 230 CollectionTimeScope::point); 231 } 232 233 INSTANTIATE_TEST_SUITE_P( 234 TimeScopePointReturnsLastReading, TestMetricCalculationFunctions, 235 Values(defaultPointParams().operationType(OperationType::single), 236 defaultPointParams().operationType(OperationType::min), 237 defaultPointParams().operationType(OperationType::max), 238 defaultPointParams().operationType(OperationType::sum), 239 defaultPointParams().operationType(OperationType::avg))); 240 241 MetricParams defaultMinParams() 242 { 243 return defaultSingleParams().operationType(OperationType::min); 244 } 245 246 INSTANTIATE_TEST_SUITE_P( 247 ReturnsMinForGivenTimeScope, TestMetricCalculationFunctions, 248 Values(defaultMinParams() 249 .collectionTimeScope(CollectionTimeScope::interval) 250 .collectionDuration(CollectionDuration(100ms)) 251 .expectedReading(10ms, 3.0), 252 defaultMinParams() 253 .collectionTimeScope(CollectionTimeScope::interval) 254 .collectionDuration(CollectionDuration(3ms)) 255 .expectedReading(13ms, 7.0), 256 defaultMinParams() 257 .collectionTimeScope(CollectionTimeScope::startup) 258 .expectedReading(10ms, 3.0))); 259 260 MetricParams defaultMaxParams() 261 { 262 return defaultSingleParams().operationType(OperationType::max); 263 } 264 265 INSTANTIATE_TEST_SUITE_P( 266 ReturnsMaxForGivenTimeScope, TestMetricCalculationFunctions, 267 Values(defaultMaxParams() 268 .collectionTimeScope(CollectionTimeScope::interval) 269 .collectionDuration(CollectionDuration(100ms)) 270 .expectedReading(0ms, 14.0), 271 defaultMaxParams() 272 .collectionTimeScope(CollectionTimeScope::interval) 273 .collectionDuration(CollectionDuration(6ms)) 274 .expectedReading(10ms, 14.0), 275 defaultMaxParams() 276 .collectionTimeScope(CollectionTimeScope::interval) 277 .collectionDuration(CollectionDuration(5ms)) 278 .expectedReading(11ms, 7.0), 279 defaultMaxParams() 280 .collectionTimeScope(CollectionTimeScope::startup) 281 .expectedReading(0ms, 14.0))); 282 283 MetricParams defaultSumParams() 284 { 285 return defaultSingleParams().operationType(OperationType::sum); 286 } 287 288 INSTANTIATE_TEST_SUITE_P( 289 ReturnsSumForGivenTimeScope, TestMetricCalculationFunctions, 290 Values(defaultSumParams() 291 .collectionTimeScope(CollectionTimeScope::interval) 292 .collectionDuration(CollectionDuration(100ms)) 293 .expectedReading(16ms, 14. * 10 + 3. * 1 + 7 * 5), 294 defaultSumParams() 295 .collectionTimeScope(CollectionTimeScope::interval) 296 .collectionDuration(CollectionDuration(8ms)) 297 .expectedReading(16ms, 14. * 2 + 3. * 1 + 7 * 5), 298 defaultSumParams() 299 .collectionTimeScope(CollectionTimeScope::interval) 300 .collectionDuration(CollectionDuration(6ms)) 301 .expectedReading(16ms, 3. * 1 + 7 * 5), 302 defaultSumParams() 303 .collectionTimeScope(CollectionTimeScope::startup) 304 .expectedReading(16ms, 14. * 10 + 3. * 1 + 7 * 5))); 305 306 MetricParams defaultAvgParams() 307 { 308 return defaultSingleParams().operationType(OperationType::avg); 309 } 310 311 INSTANTIATE_TEST_SUITE_P( 312 ReturnsAvgForGivenTimeScope, TestMetricCalculationFunctions, 313 Values(defaultAvgParams() 314 .collectionTimeScope(CollectionTimeScope::interval) 315 .collectionDuration(CollectionDuration(100ms)) 316 .expectedReading(16ms, (14. * 10 + 3. * 1 + 7 * 5) / 16.), 317 defaultAvgParams() 318 .collectionTimeScope(CollectionTimeScope::interval) 319 .collectionDuration(CollectionDuration(8ms)) 320 .expectedReading(16ms, (14. * 2 + 3. * 1 + 7 * 5) / 8.), 321 defaultAvgParams() 322 .collectionTimeScope(CollectionTimeScope::interval) 323 .collectionDuration(CollectionDuration(6ms)) 324 .expectedReading(16ms, (3. * 1 + 7 * 5) / 6.), 325 defaultAvgParams() 326 .collectionTimeScope(CollectionTimeScope::startup) 327 .expectedReading(16ms, (14. * 10 + 3. * 1 + 7 * 5) / 16.))); 328 329 TEST_P(TestMetricCalculationFunctions, calculatesReadingValue) 330 { 331 for (auto [timestamp, reading] : GetParam().readings()) 332 { 333 sut->sensorUpdated(*sensorMocks.front(), clockFake.timestamp(), 334 reading); 335 clockFake.advance(timestamp); 336 } 337 338 const auto [expectedTimestamp, expectedReading] = 339 GetParam().expectedReading(); 340 const auto readings = sut->getReadings(); 341 342 EXPECT_THAT(readings, ElementsAre(MetricValue{ 343 "id", "metadata", expectedReading, 344 ClockFake::toTimestamp(expectedTimestamp)})); 345 } 346 347 TEST_P(TestMetricCalculationFunctions, 348 calculatedReadingValueWithIntermediateCalculations) 349 { 350 for (auto [timestamp, reading] : GetParam().readings()) 351 { 352 sut->sensorUpdated(*sensorMocks.front(), clockFake.timestamp(), 353 reading); 354 clockFake.advance(timestamp); 355 sut->getReadings(); 356 } 357 358 const auto [expectedTimestamp, expectedReading] = 359 GetParam().expectedReading(); 360 const auto readings = sut->getReadings(); 361 362 EXPECT_THAT(readings, ElementsAre(MetricValue{ 363 "id", "metadata", expectedReading, 364 ClockFake::toTimestamp(expectedTimestamp)})); 365 } 366