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