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