1 #include "fakes/clock_fake.hpp" 2 #include "helpers.hpp" 3 #include "metric.hpp" 4 #include "mocks/metric_listener_mock.hpp" 5 #include "mocks/sensor_mock.hpp" 6 #include "params/metric_params.hpp" 7 #include "utils/conv_container.hpp" 8 #include "utils/conversion.hpp" 9 #include "utils/tstring.hpp" 10 11 #include <gmock/gmock.h> 12 13 using namespace testing; 14 using namespace std::chrono_literals; 15 16 namespace tstring = utils::tstring; 17 18 constexpr Milliseconds systemTimestamp = 42ms; 19 20 class TestMetric : public Test 21 { 22 public: 23 TestMetric() 24 { 25 clockFake.steady.reset(); 26 clockFake.system.set(systemTimestamp); 27 } 28 29 static std::vector<std::shared_ptr<SensorMock>> 30 makeSensorMocks(size_t amount) 31 { 32 std::vector<std::shared_ptr<SensorMock>> result; 33 for (size_t i = 0; i < amount; ++i) 34 { 35 auto& metricMock = 36 result.emplace_back(std::make_shared<NiceMock<SensorMock>>()); 37 ON_CALL(*metricMock, metadata()) 38 .WillByDefault(Return("metadata" + std::to_string(i))); 39 } 40 return result; 41 } 42 43 std::shared_ptr<Metric> makeSut(const MetricParams& p) 44 { 45 return std::make_shared<Metric>( 46 utils::convContainer<std::shared_ptr<interfaces::Sensor>>( 47 sensorMocks), 48 p.operationType(), p.collectionTimeScope(), p.collectionDuration(), 49 std::move(clockFakePtr)); 50 } 51 52 MetricParams params = MetricParams() 53 .operationType(OperationType::avg) 54 .collectionTimeScope(CollectionTimeScope::point) 55 .collectionDuration(CollectionDuration(0ms)); 56 std::vector<std::shared_ptr<SensorMock>> sensorMocks = makeSensorMocks(1u); 57 std::unique_ptr<ClockFake> clockFakePtr = std::make_unique<ClockFake>(); 58 ClockFake& clockFake = *clockFakePtr; 59 NiceMock<MetricListenerMock> listenerMock; 60 std::shared_ptr<Metric> sut; 61 }; 62 63 TEST_F(TestMetric, subscribesForSensorDuringInitialization) 64 { 65 sut = makeSut(params); 66 67 EXPECT_CALL(*sensorMocks.front(), 68 registerForUpdates(Truly([sut = sut.get()](const auto& a0) { 69 return a0.lock().get() == sut; 70 }))); 71 72 sut->initialize(); 73 } 74 75 TEST_F(TestMetric, unsubscribesForSensorDuringDeinitialization) 76 { 77 sut = makeSut(params); 78 79 EXPECT_CALL(*sensorMocks.front(), 80 unregisterFromUpdates(Truly([sut = sut.get()](const auto& a0) { 81 return a0.lock().get() == sut; 82 }))); 83 84 sut->deinitialize(); 85 } 86 87 TEST_F(TestMetric, containsEmptyReadingAfterCreated) 88 { 89 sut = makeSut(params); 90 91 ASSERT_THAT(sut->getUpdatedReadings(), ElementsAre()); 92 } 93 94 TEST_F(TestMetric, 95 notifiesRegisteredListenersOnManualUpdateWhenMetricValueChanges) 96 { 97 sut = makeSut(params.collectionTimeScope(CollectionTimeScope::startup)); 98 sut->sensorUpdated(*sensorMocks.front(), Milliseconds{18}, 31.2); 99 sut->registerForUpdates(listenerMock); 100 101 EXPECT_CALL(listenerMock, metricUpdated()).Times(2); 102 103 sut->updateReadings(Milliseconds{50u}); 104 sut->updateReadings(Milliseconds{100u}); 105 } 106 107 TEST_F(TestMetric, 108 doesntNotifyRegisteredListenersOnManualUpdateWhenMetricValueDoesntChange) 109 { 110 sut = makeSut(params.collectionTimeScope(CollectionTimeScope::startup) 111 .operationType(OperationType::max)); 112 sut->sensorUpdated(*sensorMocks.front(), Milliseconds{18}, 31.2); 113 sut->registerForUpdates(listenerMock); 114 115 EXPECT_CALL(listenerMock, metricUpdated()).Times(0); 116 117 sut->updateReadings(Milliseconds{50u}); 118 sut->sensorUpdated(*sensorMocks.front(), Milliseconds{180}, 11.); 119 sut->updateReadings(Milliseconds{100u}); 120 } 121 122 class TestMetricAfterInitialization : public TestMetric 123 { 124 public: 125 void SetUp() override 126 { 127 sut = makeSut(params); 128 sut->initialize(); 129 } 130 }; 131 132 TEST_F(TestMetricAfterInitialization, containsEmptyReading) 133 { 134 ASSERT_THAT(sut->getUpdatedReadings(), ElementsAre()); 135 } 136 137 TEST_F(TestMetricAfterInitialization, updatesMetricValuesOnSensorUpdate) 138 { 139 sut->sensorUpdated(*sensorMocks.front(), Milliseconds{18}, 31.2); 140 141 ASSERT_THAT( 142 sut->getUpdatedReadings(), 143 ElementsAre(MetricValue{"metadata0", 31.2, 144 std::chrono::duration_cast<Milliseconds>( 145 clockFake.system.timestamp()) 146 .count()})); 147 } 148 149 TEST_F(TestMetricAfterInitialization, 150 throwsWhenUpdateIsPerformedOnUnknownSensor) 151 { 152 auto sensor = std::make_shared<StrictMock<SensorMock>>(); 153 EXPECT_THROW(sut->sensorUpdated(*sensor, Milliseconds{10}, 20.0), 154 std::out_of_range); 155 } 156 157 TEST_F(TestMetricAfterInitialization, dumpsConfiguration) 158 { 159 namespace ts = utils::tstring; 160 161 ON_CALL(*sensorMocks.front(), id()) 162 .WillByDefault(Return(SensorMock::makeId("service1", "path1"))); 163 ON_CALL(*sensorMocks.front(), metadata()) 164 .WillByDefault(Return("metadata1")); 165 166 const auto conf = sut->dumpConfiguration(); 167 168 LabeledMetricParameters expected = {}; 169 expected.at_label<ts::OperationType>() = params.operationType(); 170 expected.at_label<ts::CollectionTimeScope>() = params.collectionTimeScope(); 171 expected.at_label<ts::CollectionDuration>() = params.collectionDuration(); 172 expected.at_label<ts::SensorPath>() = { 173 LabeledSensorInfo("service1", "path1", "metadata1")}; 174 175 EXPECT_THAT(conf, Eq(expected)); 176 } 177 178 TEST_F(TestMetricAfterInitialization, notifiesRegisteredListeners) 179 { 180 EXPECT_CALL(listenerMock, metricUpdated()); 181 182 sut->registerForUpdates(listenerMock); 183 sut->sensorUpdated(*sensorMocks.front(), Milliseconds{18}, 31.2); 184 } 185 186 TEST_F(TestMetricAfterInitialization, 187 doesntNotifyRegisteredListenersWhenValueDoesntChange) 188 { 189 EXPECT_CALL(listenerMock, metricUpdated()); 190 191 sut->registerForUpdates(listenerMock); 192 sut->sensorUpdated(*sensorMocks.front(), Milliseconds{18}, 31.2); 193 sut->sensorUpdated(*sensorMocks.front(), Milliseconds{70}, 31.2); 194 } 195 196 TEST_F(TestMetricAfterInitialization, doesntNotifyAfterUnRegisterListener) 197 { 198 EXPECT_CALL(listenerMock, metricUpdated()).Times(0); 199 200 sut->registerForUpdates(listenerMock); 201 sut->unregisterFromUpdates(listenerMock); 202 sut->sensorUpdated(*sensorMocks.front(), Milliseconds{18}, 31.2); 203 } 204 205 class TestMetricCalculationFunctions : 206 public TestMetric, 207 public WithParamInterface<MetricParams> 208 { 209 public: 210 void SetUp() override 211 { 212 sut = makeSut(params.operationType(GetParam().operationType()) 213 .collectionTimeScope(GetParam().collectionTimeScope()) 214 .collectionDuration(GetParam().collectionDuration())); 215 } 216 217 static std::vector<std::pair<Milliseconds, double>> defaultReadings() 218 { 219 std::vector<std::pair<Milliseconds, double>> ret; 220 ret.emplace_back(0ms, std::numeric_limits<double>::quiet_NaN()); 221 ret.emplace_back(10ms, 14.); 222 ret.emplace_back(1ms, 3.); 223 ret.emplace_back(5ms, 7.); 224 return ret; 225 } 226 }; 227 228 MetricParams defaultCollectionFunctionParams() 229 { 230 return MetricParams() 231 .readings(TestMetricCalculationFunctions::defaultReadings()) 232 .expectedReading(systemTimestamp + 16ms, 7.0); 233 } 234 235 MetricParams defaultPointParams() 236 { 237 return defaultCollectionFunctionParams() 238 .collectionTimeScope(CollectionTimeScope::point) 239 .expectedIsTimerRequired(false); 240 } 241 242 INSTANTIATE_TEST_SUITE_P( 243 TimeScopePointReturnsLastReading, TestMetricCalculationFunctions, 244 Values(defaultPointParams().operationType(OperationType::min), 245 defaultPointParams().operationType(OperationType::max), 246 defaultPointParams().operationType(OperationType::sum), 247 defaultPointParams().operationType(OperationType::avg))); 248 249 MetricParams defaultMinParams() 250 { 251 return defaultCollectionFunctionParams().operationType(OperationType::min); 252 } 253 254 INSTANTIATE_TEST_SUITE_P( 255 ReturnsMinForGivenTimeScope, TestMetricCalculationFunctions, 256 Values(defaultMinParams() 257 .collectionTimeScope(CollectionTimeScope::interval) 258 .collectionDuration(CollectionDuration(100ms)) 259 .expectedReading(systemTimestamp + 16ms, 3.0), 260 defaultMinParams() 261 .collectionTimeScope(CollectionTimeScope::interval) 262 .collectionDuration(CollectionDuration(3ms)) 263 .expectedReading(systemTimestamp + 16ms, 7.0), 264 defaultMinParams() 265 .collectionTimeScope(CollectionTimeScope::startup) 266 .expectedReading(systemTimestamp + 16ms, 3.0) 267 .expectedIsTimerRequired(false))); 268 269 MetricParams defaultMaxParams() 270 { 271 return defaultCollectionFunctionParams().operationType(OperationType::max); 272 } 273 274 INSTANTIATE_TEST_SUITE_P( 275 ReturnsMaxForGivenTimeScope, TestMetricCalculationFunctions, 276 Values(defaultMaxParams() 277 .collectionTimeScope(CollectionTimeScope::interval) 278 .collectionDuration(CollectionDuration(100ms)) 279 .expectedReading(systemTimestamp + 16ms, 14.0), 280 defaultMaxParams() 281 .collectionTimeScope(CollectionTimeScope::interval) 282 .collectionDuration(CollectionDuration(6ms)) 283 .expectedReading(systemTimestamp + 16ms, 14.0), 284 defaultMaxParams() 285 .collectionTimeScope(CollectionTimeScope::interval) 286 .collectionDuration(CollectionDuration(5ms)) 287 .expectedReading(systemTimestamp + 16ms, 7.0), 288 defaultMaxParams() 289 .collectionTimeScope(CollectionTimeScope::startup) 290 .expectedReading(systemTimestamp + 16ms, 14.0) 291 .expectedIsTimerRequired(false))); 292 293 MetricParams defaultSumParams() 294 { 295 return defaultCollectionFunctionParams().operationType(OperationType::sum); 296 } 297 298 INSTANTIATE_TEST_SUITE_P( 299 ReturnsSumForGivenTimeScope, TestMetricCalculationFunctions, 300 Values(defaultSumParams() 301 .collectionTimeScope(CollectionTimeScope::interval) 302 .collectionDuration(CollectionDuration(100ms)) 303 .expectedReading(systemTimestamp + 16ms, 304 14. * 0.01 + 3. * 0.001 + 7. * 0.005), 305 defaultSumParams() 306 .collectionTimeScope(CollectionTimeScope::interval) 307 .collectionDuration(CollectionDuration(8ms)) 308 .expectedReading(systemTimestamp + 16ms, 309 14. * 0.002 + 3. * 0.001 + 7 * 0.005), 310 defaultSumParams() 311 .collectionTimeScope(CollectionTimeScope::interval) 312 .collectionDuration(CollectionDuration(6ms)) 313 .expectedReading(systemTimestamp + 16ms, 3. * 0.001 + 7 * 0.005), 314 defaultSumParams() 315 .collectionTimeScope(CollectionTimeScope::startup) 316 .expectedReading(systemTimestamp + 16ms, 317 14. * 0.01 + 3. * 0.001 + 7 * 0.005))); 318 319 MetricParams defaultAvgParams() 320 { 321 return defaultCollectionFunctionParams().operationType(OperationType::avg); 322 } 323 324 INSTANTIATE_TEST_SUITE_P( 325 ReturnsAvgForGivenTimeScope, TestMetricCalculationFunctions, 326 Values(defaultAvgParams() 327 .collectionTimeScope(CollectionTimeScope::interval) 328 .collectionDuration(CollectionDuration(100ms)) 329 .expectedReading(systemTimestamp + 16ms, 330 (14. * 10 + 3. * 1 + 7 * 5) / 16.), 331 defaultAvgParams() 332 .collectionTimeScope(CollectionTimeScope::interval) 333 .collectionDuration(CollectionDuration(8ms)) 334 .expectedReading(systemTimestamp + 16ms, 335 (14. * 2 + 3. * 1 + 7 * 5) / 8.), 336 defaultAvgParams() 337 .collectionTimeScope(CollectionTimeScope::interval) 338 .collectionDuration(CollectionDuration(6ms)) 339 .expectedReading(systemTimestamp + 16ms, (3. * 1 + 7 * 5) / 6.), 340 defaultAvgParams() 341 .collectionTimeScope(CollectionTimeScope::startup) 342 .expectedReading(systemTimestamp + 16ms, 343 (14. * 10 + 3. * 1 + 7 * 5) / 16.))); 344 345 TEST_P(TestMetricCalculationFunctions, calculatesReadingValue) 346 { 347 for (auto [timestamp, reading] : GetParam().readings()) 348 { 349 sut->sensorUpdated(*sensorMocks.front(), clockFake.steadyTimestamp(), 350 reading); 351 clockFake.advance(timestamp); 352 } 353 354 const auto [expectedTimestamp, 355 expectedReading] = GetParam().expectedReading(); 356 const auto readings = sut->getUpdatedReadings(); 357 358 EXPECT_THAT(readings, ElementsAre(MetricValue{"metadata0", expectedReading, 359 expectedTimestamp.count()})); 360 } 361 362 TEST_P(TestMetricCalculationFunctions, 363 calculatedReadingValueWithIntermediateCalculations) 364 { 365 for (auto [timestamp, reading] : GetParam().readings()) 366 { 367 sut->sensorUpdated(*sensorMocks.front(), clockFake.steadyTimestamp(), 368 reading); 369 clockFake.advance(timestamp); 370 sut->getUpdatedReadings(); 371 } 372 373 const auto [expectedTimestamp, 374 expectedReading] = GetParam().expectedReading(); 375 const auto readings = sut->getUpdatedReadings(); 376 377 EXPECT_THAT(readings, ElementsAre(MetricValue{"metadata0", expectedReading, 378 expectedTimestamp.count()})); 379 } 380 381 TEST_P(TestMetricCalculationFunctions, returnsIsTimerRequired) 382 { 383 EXPECT_THAT(sut->isTimerRequired(), 384 Eq(GetParam().expectedIsTimerRequired())); 385 } 386 387 class TestMetricWithMultipleSensors : public TestMetric 388 { 389 public: 390 TestMetricWithMultipleSensors() 391 { 392 sensorMocks = makeSensorMocks(7u); 393 394 sut = makeSut(params); 395 sut->initialize(); 396 } 397 }; 398 399 TEST_F(TestMetricWithMultipleSensors, readingsContainsAllReadingsInOrder) 400 { 401 for (size_t i = 0; i < sensorMocks.size(); ++i) 402 { 403 sut->sensorUpdated(*sensorMocks[i], Milliseconds{i + 100}, i + 10.0); 404 sut->getUpdatedReadings(); 405 } 406 407 clockFake.system.set(Milliseconds{72}); 408 409 EXPECT_THAT(sut->getUpdatedReadings(), 410 ElementsAre(MetricValue{"metadata0", 10.0, 72}, 411 MetricValue{"metadata1", 11.0, 72}, 412 MetricValue{"metadata2", 12.0, 72}, 413 MetricValue{"metadata3", 13.0, 72}, 414 MetricValue{"metadata4", 14.0, 72}, 415 MetricValue{"metadata5", 15.0, 72}, 416 MetricValue{"metadata6", 16.0, 72})); 417 } 418 419 TEST_F(TestMetricWithMultipleSensors, readingsContainOnlyAvailableSensors) 420 { 421 for (auto i : {5u, 3u, 6u, 0u}) 422 { 423 sut->sensorUpdated(*sensorMocks[i], Milliseconds{i + 100}, i + 10.0); 424 sut->getUpdatedReadings(); 425 } 426 427 clockFake.system.set(Milliseconds{62}); 428 429 EXPECT_THAT(sut->getUpdatedReadings(), 430 ElementsAre(MetricValue{"metadata5", 15.0, 62}, 431 MetricValue{"metadata3", 13.0, 62}, 432 MetricValue{"metadata6", 16.0, 62}, 433 MetricValue{"metadata0", 10.0, 62})); 434 } 435 436 TEST_F(TestMetricWithMultipleSensors, readingsContainsAllReadingsOutOfOrder) 437 { 438 for (auto i : {6u, 5u, 3u, 4u, 0u, 2u, 1u}) 439 { 440 sut->sensorUpdated(*sensorMocks[i], Milliseconds{i + 100}, i + 10.0); 441 sut->getUpdatedReadings(); 442 } 443 444 clockFake.system.set(Milliseconds{52}); 445 446 EXPECT_THAT(sut->getUpdatedReadings(), 447 ElementsAre(MetricValue{"metadata6", 16.0, 52}, 448 MetricValue{"metadata5", 15.0, 52}, 449 MetricValue{"metadata3", 13.0, 52}, 450 MetricValue{"metadata4", 14.0, 52}, 451 MetricValue{"metadata0", 10.0, 52}, 452 MetricValue{"metadata2", 12.0, 52}, 453 MetricValue{"metadata1", 11.0, 52})); 454 } 455