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