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()).WillByDefault(Return("metadata")); 38 } 39 return result; 40 } 41 42 std::shared_ptr<Metric> makeSut(const MetricParams& p) 43 { 44 return std::make_shared<Metric>( 45 utils::convContainer<std::shared_ptr<interfaces::Sensor>>( 46 sensorMocks), 47 p.operationType(), p.id(), p.collectionTimeScope(), 48 p.collectionDuration(), std::move(clockFakePtr)); 49 } 50 51 MetricParams params = MetricParams() 52 .id("id") 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->getReadings(), 92 ElementsAre(MetricValue({"id", "metadata", 0., 0u}))); 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->getReadings(), 136 ElementsAre(MetricValue({"id", "metadata", 0., 0u}))); 137 } 138 139 TEST_F(TestMetricAfterInitialization, updatesMetricValuesOnSensorUpdate) 140 { 141 sut->sensorUpdated(*sensorMocks.front(), Milliseconds{18}, 31.2); 142 143 ASSERT_THAT( 144 sut->getReadings(), 145 ElementsAre(MetricValue{"id", "metadata", 31.2, 146 std::chrono::duration_cast<Milliseconds>( 147 clockFake.system.timestamp()) 148 .count()})); 149 } 150 151 TEST_F(TestMetricAfterInitialization, 152 throwsWhenUpdateIsPerformedOnUnknownSensor) 153 { 154 auto sensor = std::make_shared<StrictMock<SensorMock>>(); 155 EXPECT_THROW(sut->sensorUpdated(*sensor, Milliseconds{10}, 20.0), 156 std::out_of_range); 157 } 158 159 TEST_F(TestMetricAfterInitialization, dumpsConfiguration) 160 { 161 namespace ts = utils::tstring; 162 163 ON_CALL(*sensorMocks.front(), id()) 164 .WillByDefault(Return(SensorMock::makeId("service1", "path1"))); 165 ON_CALL(*sensorMocks.front(), metadata()) 166 .WillByDefault(Return("metadata1")); 167 168 const auto conf = sut->dumpConfiguration(); 169 170 LabeledMetricParameters expected = {}; 171 expected.at_label<ts::Id>() = params.id(); 172 expected.at_label<ts::OperationType>() = params.operationType(); 173 expected.at_label<ts::CollectionTimeScope>() = params.collectionTimeScope(); 174 expected.at_label<ts::CollectionDuration>() = params.collectionDuration(); 175 expected.at_label<ts::SensorPath>() = { 176 LabeledSensorInfo("service1", "path1", "metadata1")}; 177 178 EXPECT_THAT(conf, Eq(expected)); 179 } 180 181 TEST_F(TestMetricAfterInitialization, notifiesRegisteredListeners) 182 { 183 EXPECT_CALL(listenerMock, metricUpdated()); 184 185 sut->registerForUpdates(listenerMock); 186 sut->sensorUpdated(*sensorMocks.front(), Milliseconds{18}, 31.2); 187 } 188 189 TEST_F(TestMetricAfterInitialization, 190 doesntNotifyRegisteredListenersWhenValueDoesntChange) 191 { 192 EXPECT_CALL(listenerMock, metricUpdated()); 193 194 sut->registerForUpdates(listenerMock); 195 sut->sensorUpdated(*sensorMocks.front(), Milliseconds{18}, 31.2); 196 sut->sensorUpdated(*sensorMocks.front(), Milliseconds{70}, 31.2); 197 } 198 199 TEST_F(TestMetricAfterInitialization, doesntNotifyAfterUnRegisterListener) 200 { 201 EXPECT_CALL(listenerMock, metricUpdated()).Times(0); 202 203 sut->registerForUpdates(listenerMock); 204 sut->unregisterFromUpdates(listenerMock); 205 sut->sensorUpdated(*sensorMocks.front(), Milliseconds{18}, 31.2); 206 } 207 208 class TestMetricCalculationFunctions : 209 public TestMetric, 210 public WithParamInterface<MetricParams> 211 { 212 public: 213 void SetUp() override 214 { 215 sut = makeSut(params.operationType(GetParam().operationType()) 216 .collectionTimeScope(GetParam().collectionTimeScope()) 217 .collectionDuration(GetParam().collectionDuration())); 218 } 219 220 static std::vector<std::pair<Milliseconds, double>> defaultReadings() 221 { 222 std::vector<std::pair<Milliseconds, double>> ret; 223 ret.emplace_back(0ms, std::numeric_limits<double>::quiet_NaN()); 224 ret.emplace_back(10ms, 14.); 225 ret.emplace_back(1ms, 3.); 226 ret.emplace_back(5ms, 7.); 227 return ret; 228 } 229 }; 230 231 MetricParams defaultCollectionFunctionParams() 232 { 233 return MetricParams() 234 .readings(TestMetricCalculationFunctions::defaultReadings()) 235 .expectedReading(systemTimestamp + 16ms, 7.0); 236 } 237 238 MetricParams defaultPointParams() 239 { 240 return defaultCollectionFunctionParams() 241 .collectionTimeScope(CollectionTimeScope::point) 242 .expectedIsTimerRequired(false); 243 } 244 245 INSTANTIATE_TEST_SUITE_P( 246 TimeScopePointReturnsLastReading, TestMetricCalculationFunctions, 247 Values(defaultPointParams().operationType(OperationType::min), 248 defaultPointParams().operationType(OperationType::max), 249 defaultPointParams().operationType(OperationType::sum), 250 defaultPointParams().operationType(OperationType::avg))); 251 252 MetricParams defaultMinParams() 253 { 254 return defaultCollectionFunctionParams().operationType(OperationType::min); 255 } 256 257 INSTANTIATE_TEST_SUITE_P( 258 ReturnsMinForGivenTimeScope, TestMetricCalculationFunctions, 259 Values(defaultMinParams() 260 .collectionTimeScope(CollectionTimeScope::interval) 261 .collectionDuration(CollectionDuration(100ms)) 262 .expectedReading(systemTimestamp + 16ms, 3.0), 263 defaultMinParams() 264 .collectionTimeScope(CollectionTimeScope::interval) 265 .collectionDuration(CollectionDuration(3ms)) 266 .expectedReading(systemTimestamp + 16ms, 7.0), 267 defaultMinParams() 268 .collectionTimeScope(CollectionTimeScope::startup) 269 .expectedReading(systemTimestamp + 16ms, 3.0) 270 .expectedIsTimerRequired(false))); 271 272 MetricParams defaultMaxParams() 273 { 274 return defaultCollectionFunctionParams().operationType(OperationType::max); 275 } 276 277 INSTANTIATE_TEST_SUITE_P( 278 ReturnsMaxForGivenTimeScope, TestMetricCalculationFunctions, 279 Values(defaultMaxParams() 280 .collectionTimeScope(CollectionTimeScope::interval) 281 .collectionDuration(CollectionDuration(100ms)) 282 .expectedReading(systemTimestamp + 16ms, 14.0), 283 defaultMaxParams() 284 .collectionTimeScope(CollectionTimeScope::interval) 285 .collectionDuration(CollectionDuration(6ms)) 286 .expectedReading(systemTimestamp + 16ms, 14.0), 287 defaultMaxParams() 288 .collectionTimeScope(CollectionTimeScope::interval) 289 .collectionDuration(CollectionDuration(5ms)) 290 .expectedReading(systemTimestamp + 16ms, 7.0), 291 defaultMaxParams() 292 .collectionTimeScope(CollectionTimeScope::startup) 293 .expectedReading(systemTimestamp + 16ms, 14.0) 294 .expectedIsTimerRequired(false))); 295 296 MetricParams defaultSumParams() 297 { 298 return defaultCollectionFunctionParams().operationType(OperationType::sum); 299 } 300 301 INSTANTIATE_TEST_SUITE_P( 302 ReturnsSumForGivenTimeScope, TestMetricCalculationFunctions, 303 Values(defaultSumParams() 304 .collectionTimeScope(CollectionTimeScope::interval) 305 .collectionDuration(CollectionDuration(100ms)) 306 .expectedReading(systemTimestamp + 16ms, 307 14. * 0.01 + 3. * 0.001 + 7. * 0.005), 308 defaultSumParams() 309 .collectionTimeScope(CollectionTimeScope::interval) 310 .collectionDuration(CollectionDuration(8ms)) 311 .expectedReading(systemTimestamp + 16ms, 312 14. * 0.002 + 3. * 0.001 + 7 * 0.005), 313 defaultSumParams() 314 .collectionTimeScope(CollectionTimeScope::interval) 315 .collectionDuration(CollectionDuration(6ms)) 316 .expectedReading(systemTimestamp + 16ms, 3. * 0.001 + 7 * 0.005), 317 defaultSumParams() 318 .collectionTimeScope(CollectionTimeScope::startup) 319 .expectedReading(systemTimestamp + 16ms, 320 14. * 0.01 + 3. * 0.001 + 7 * 0.005))); 321 322 MetricParams defaultAvgParams() 323 { 324 return defaultCollectionFunctionParams().operationType(OperationType::avg); 325 } 326 327 INSTANTIATE_TEST_SUITE_P( 328 ReturnsAvgForGivenTimeScope, TestMetricCalculationFunctions, 329 Values(defaultAvgParams() 330 .collectionTimeScope(CollectionTimeScope::interval) 331 .collectionDuration(CollectionDuration(100ms)) 332 .expectedReading(systemTimestamp + 16ms, 333 (14. * 10 + 3. * 1 + 7 * 5) / 16.), 334 defaultAvgParams() 335 .collectionTimeScope(CollectionTimeScope::interval) 336 .collectionDuration(CollectionDuration(8ms)) 337 .expectedReading(systemTimestamp + 16ms, 338 (14. * 2 + 3. * 1 + 7 * 5) / 8.), 339 defaultAvgParams() 340 .collectionTimeScope(CollectionTimeScope::interval) 341 .collectionDuration(CollectionDuration(6ms)) 342 .expectedReading(systemTimestamp + 16ms, (3. * 1 + 7 * 5) / 6.), 343 defaultAvgParams() 344 .collectionTimeScope(CollectionTimeScope::startup) 345 .expectedReading(systemTimestamp + 16ms, 346 (14. * 10 + 3. * 1 + 7 * 5) / 16.))); 347 348 TEST_P(TestMetricCalculationFunctions, calculatesReadingValue) 349 { 350 for (auto [timestamp, reading] : GetParam().readings()) 351 { 352 sut->sensorUpdated(*sensorMocks.front(), clockFake.steadyTimestamp(), 353 reading); 354 clockFake.advance(timestamp); 355 } 356 357 const auto [expectedTimestamp, expectedReading] = 358 GetParam().expectedReading(); 359 const auto readings = sut->getReadings(); 360 361 EXPECT_THAT(readings, 362 ElementsAre(MetricValue{"id", "metadata", expectedReading, 363 expectedTimestamp.count()})); 364 } 365 366 TEST_P(TestMetricCalculationFunctions, 367 calculatedReadingValueWithIntermediateCalculations) 368 { 369 for (auto [timestamp, reading] : GetParam().readings()) 370 { 371 sut->sensorUpdated(*sensorMocks.front(), clockFake.steadyTimestamp(), 372 reading); 373 clockFake.advance(timestamp); 374 sut->getReadings(); 375 } 376 377 const auto [expectedTimestamp, expectedReading] = 378 GetParam().expectedReading(); 379 const auto readings = sut->getReadings(); 380 381 EXPECT_THAT(readings, 382 ElementsAre(MetricValue{"id", "metadata", expectedReading, 383 expectedTimestamp.count()})); 384 } 385 386 TEST_P(TestMetricCalculationFunctions, returnsIsTimerRequired) 387 { 388 EXPECT_THAT(sut->isTimerRequired(), 389 Eq(GetParam().expectedIsTimerRequired())); 390 } 391