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