1 #include "dbus_environment.hpp" 2 #include "helpers.hpp" 3 #include "mocks/sensor_mock.hpp" 4 #include "mocks/trigger_action_mock.hpp" 5 #include "numeric_threshold.hpp" 6 #include "utils/conv_container.hpp" 7 8 #include <gmock/gmock.h> 9 10 using namespace testing; 11 using namespace std::chrono_literals; 12 13 class TestNumericThreshold : public Test 14 { 15 public: 16 std::vector<std::shared_ptr<SensorMock>> sensorMocks = { 17 std::make_shared<NiceMock<SensorMock>>(), 18 std::make_shared<NiceMock<SensorMock>>()}; 19 std::vector<std::string> sensorNames = {"Sensor1", "Sensor2"}; 20 std::unique_ptr<TriggerActionMock> actionMockPtr = 21 std::make_unique<StrictMock<TriggerActionMock>>(); 22 TriggerActionMock& actionMock = *actionMockPtr; 23 std::shared_ptr<NumericThreshold> sut; 24 25 void makeThreshold(Milliseconds dwellTime, numeric::Direction direction, 26 double thresholdValue, 27 numeric::Type type = numeric::Type::lowerWarning) 28 { 29 std::vector<std::unique_ptr<interfaces::TriggerAction>> actions; 30 actions.push_back(std::move(actionMockPtr)); 31 32 sut = std::make_shared<NumericThreshold>( 33 DbusEnvironment::getIoc(), 34 utils::convContainer<std::shared_ptr<interfaces::Sensor>>( 35 sensorMocks), 36 std::move(actions), dwellTime, direction, thresholdValue, type); 37 } 38 39 void SetUp() override 40 { 41 for (size_t idx = 0; idx < sensorMocks.size(); idx++) 42 { 43 ON_CALL(*sensorMocks.at(idx), getName()) 44 .WillByDefault(Return(sensorNames[idx])); 45 } 46 47 makeThreshold(0ms, numeric::Direction::increasing, 90.0, 48 numeric::Type::upperCritical); 49 } 50 }; 51 52 TEST_F(TestNumericThreshold, initializeThresholdExpectAllSensorsAreRegistered) 53 { 54 for (auto& sensor : sensorMocks) 55 { 56 EXPECT_CALL(*sensor, 57 registerForUpdates(Truly([sut = sut.get()](const auto& x) { 58 return x.lock().get() == sut; 59 }))); 60 } 61 62 sut->initialize(); 63 } 64 65 TEST_F(TestNumericThreshold, thresholdIsNotInitializeExpectNoActionCommit) 66 { 67 EXPECT_CALL(actionMock, commit(_, _, _)).Times(0); 68 } 69 70 TEST_F(TestNumericThreshold, getLabeledParamsReturnsCorrectly) 71 { 72 LabeledThresholdParam expected = numeric::LabeledThresholdParam( 73 numeric::Type::upperCritical, 0, numeric::Direction::increasing, 90.0); 74 EXPECT_EQ(sut->getThresholdParam(), expected); 75 } 76 77 struct NumericParams 78 { 79 NumericParams& Direction(numeric::Direction val) 80 { 81 direction = val; 82 return *this; 83 } 84 85 NumericParams& 86 Updates(std::vector<std::tuple<size_t, Milliseconds, double>> val) 87 { 88 updates = std::move(val); 89 return *this; 90 } 91 92 NumericParams& 93 Expected(std::vector<std::tuple<size_t, Milliseconds, double>> val) 94 { 95 expected = std::move(val); 96 return *this; 97 } 98 99 friend void PrintTo(const NumericParams& o, std::ostream* os) 100 { 101 *os << "{ Direction: " << static_cast<int>(o.direction) 102 << ", Updates: "; 103 for (const auto& [index, timestamp, value] : o.updates) 104 { 105 *os << "{ SensorIndex: " << index 106 << ", Timestamp: " << timestamp.count() << ", Value: " << value 107 << " }, "; 108 } 109 *os << "Expected: "; 110 for (const auto& [index, timestamp, value] : o.expected) 111 { 112 *os << "{ SensorIndex: " << index 113 << ", Timestamp: " << timestamp.count() << ", Value: " << value 114 << " }, "; 115 } 116 *os << " }"; 117 } 118 119 numeric::Direction direction; 120 std::vector<std::tuple<size_t, Milliseconds, double>> updates; 121 std::vector<std::tuple<size_t, Milliseconds, double>> expected; 122 }; 123 124 class TestNumericThresholdNoDwellTime : 125 public TestNumericThreshold, 126 public WithParamInterface<NumericParams> 127 { 128 public: 129 void SetUp() override 130 { 131 for (size_t idx = 0; idx < sensorMocks.size(); idx++) 132 { 133 ON_CALL(*sensorMocks.at(idx), getName()) 134 .WillByDefault(Return(sensorNames[idx])); 135 } 136 137 makeThreshold(0ms, GetParam().direction, 90.0); 138 } 139 }; 140 141 INSTANTIATE_TEST_SUITE_P(_, TestNumericThresholdNoDwellTime, 142 Values(NumericParams() 143 .Direction(numeric::Direction::increasing) 144 .Updates({{0, 1ms, 80.0}, {0, 2ms, 89.0}}) 145 .Expected({}), 146 NumericParams() 147 .Direction(numeric::Direction::increasing) 148 .Updates({{0, 1ms, 80.0}, {0, 2ms, 91.0}}) 149 .Expected({{0, 2ms, 91.0}}), 150 NumericParams() 151 .Direction(numeric::Direction::increasing) 152 .Updates({{0, 1ms, 80.0}, 153 {0, 2ms, 99.0}, 154 {0, 3ms, 80.0}, 155 {0, 4ms, 98.0}}) 156 .Expected({{0, 2ms, 99.0}, {0, 4ms, 98.0}}), 157 NumericParams() 158 .Direction(numeric::Direction::increasing) 159 .Updates({{0, 1ms, 80.0}, 160 {0, 2ms, 99.0}, 161 {1, 3ms, 100.0}, 162 {1, 4ms, 98.0}}) 163 .Expected({{0, 2ms, 99.0}}), 164 NumericParams() 165 .Direction(numeric::Direction::decreasing) 166 .Updates({{0, 1ms, 100.0}, {0, 2ms, 91.0}}) 167 .Expected({}), 168 NumericParams() 169 .Direction(numeric::Direction::decreasing) 170 .Updates({{0, 1ms, 100.0}, {0, 2ms, 80.0}}) 171 .Expected({{0, 2ms, 80.0}}), 172 NumericParams() 173 .Direction(numeric::Direction::decreasing) 174 .Updates({{0, 1ms, 100.0}, 175 {0, 2ms, 80.0}, 176 {0, 3ms, 99.0}, 177 {0, 4ms, 85.0}}) 178 .Expected({{0, 2ms, 80.0}, {0, 4ms, 85.0}}), 179 NumericParams() 180 .Direction(numeric::Direction::decreasing) 181 .Updates({{0, 1ms, 100.0}, 182 {0, 2ms, 80.0}, 183 {1, 3ms, 99.0}, 184 {1, 4ms, 88.0}}) 185 .Expected({{0, 2ms, 80.0}, {1, 4ms, 88.0}}), 186 NumericParams() 187 .Direction(numeric::Direction::either) 188 .Updates({{0, 1ms, 98.0}, {0, 2ms, 91.0}}) 189 .Expected({}), 190 NumericParams() 191 .Direction(numeric::Direction::either) 192 .Updates({{0, 1ms, 100.0}, 193 {0, 2ms, 80.0}, 194 {0, 3ms, 85.0}, 195 {0, 4ms, 91.0}}) 196 .Expected({{0, 2ms, 80.0}, {0, 4ms, 91.0}}), 197 NumericParams() 198 .Direction(numeric::Direction::either) 199 .Updates({{0, 1ms, 100.0}, 200 {1, 2ms, 80.0}, 201 {0, 3ms, 85.0}, 202 {1, 4ms, 91.0}}) 203 .Expected({{0, 3ms, 85.0}, 204 {1, 4ms, 91.0}}))); 205 206 TEST_P(TestNumericThresholdNoDwellTime, senorsIsUpdatedMultipleTimes) 207 { 208 InSequence seq; 209 for (const auto& [index, timestamp, value] : GetParam().expected) 210 { 211 EXPECT_CALL(actionMock, commit(sensorNames[index], timestamp, value)); 212 } 213 214 sut->initialize(); 215 for (const auto& [index, timestamp, value] : GetParam().updates) 216 { 217 sut->sensorUpdated(*sensorMocks[index], timestamp, value); 218 } 219 } 220 221 class TestNumericThresholdWithDwellTime : 222 public TestNumericThreshold, 223 public WithParamInterface<NumericParams> 224 { 225 public: 226 void SetUp() override 227 { 228 for (size_t idx = 0; idx < sensorMocks.size(); idx++) 229 { 230 ON_CALL(*sensorMocks.at(idx), getName()) 231 .WillByDefault(Return(sensorNames[idx])); 232 } 233 234 makeThreshold(2ms, GetParam().direction, 90.0); 235 } 236 237 void sleep() 238 { 239 DbusEnvironment::sleepFor(4ms); 240 } 241 }; 242 243 INSTANTIATE_TEST_SUITE_P(_, TestNumericThresholdWithDwellTime, 244 Values(NumericParams() 245 .Direction(numeric::Direction::increasing) 246 .Updates({{0, 1ms, 80.0}, {0, 2ms, 89.0}}) 247 .Expected({}), 248 NumericParams() 249 .Direction(numeric::Direction::increasing) 250 .Updates({{0, 1ms, 80.0}, {0, 2ms, 91.0}}) 251 .Expected({{0, 2ms, 91.0}}), 252 NumericParams() 253 .Direction(numeric::Direction::increasing) 254 .Updates({{0, 1ms, 80.0}, 255 {0, 2ms, 99.0}, 256 {0, 3ms, 80.0}, 257 {0, 4ms, 98.0}}) 258 .Expected({{0, 2ms, 99.0}, {0, 4ms, 98.0}}), 259 NumericParams() 260 .Direction(numeric::Direction::increasing) 261 .Updates({{0, 1ms, 80.0}, 262 {1, 2ms, 99.0}, 263 {0, 3ms, 100.0}, 264 {1, 4ms, 86.0}}) 265 .Expected({{0, 3ms, 100.0}}), 266 NumericParams() 267 .Direction(numeric::Direction::decreasing) 268 .Updates({{0, 1ms, 100.0}, {0, 2ms, 91.0}}) 269 .Expected({}), 270 NumericParams() 271 .Direction(numeric::Direction::decreasing) 272 .Updates({{0, 1ms, 100.0}, {0, 2ms, 80.0}}) 273 .Expected({{0, 2ms, 80.0}}), 274 NumericParams() 275 .Direction(numeric::Direction::decreasing) 276 .Updates({{0, 1ms, 100.0}, 277 {0, 2ms, 80.0}, 278 {0, 3ms, 99.0}, 279 {0, 4ms, 85.0}}) 280 .Expected({{0, 2ms, 80.0}, {0, 4ms, 85.0}}), 281 NumericParams() 282 .Direction(numeric::Direction::decreasing) 283 .Updates({{0, 1ms, 100.0}, 284 {0, 2ms, 80.0}, 285 {1, 3ms, 99.0}, 286 {1, 4ms, 88.0}}) 287 .Expected({{0, 2ms, 80.0}, {1, 4ms, 88.0}}), 288 NumericParams() 289 .Direction(numeric::Direction::either) 290 .Updates({{0, 1ms, 98.0}, {0, 2ms, 91.0}}) 291 .Expected({}), 292 NumericParams() 293 .Direction(numeric::Direction::either) 294 .Updates({{0, 1ms, 100.0}, 295 {0, 2ms, 80.0}, 296 {0, 3ms, 85.0}, 297 {0, 4ms, 91.0}}) 298 .Expected({{0, 2ms, 80.0}, {0, 4ms, 91.0}}), 299 NumericParams() 300 .Direction(numeric::Direction::either) 301 .Updates({{0, 1ms, 100.0}, 302 {1, 2ms, 80.0}, 303 {0, 3ms, 85.0}, 304 {1, 4ms, 91.0}}) 305 .Expected({{0, 3ms, 85.0}, 306 {1, 4ms, 91.0}}))); 307 308 TEST_P(TestNumericThresholdWithDwellTime, 309 senorsIsUpdatedMultipleTimesSleepAfterEveryUpdate) 310 { 311 InSequence seq; 312 for (const auto& [index, timestamp, value] : GetParam().expected) 313 { 314 EXPECT_CALL(actionMock, commit(sensorNames[index], timestamp, value)); 315 } 316 317 sut->initialize(); 318 for (const auto& [index, timestamp, value] : GetParam().updates) 319 { 320 sut->sensorUpdated(*sensorMocks[index], timestamp, value); 321 sleep(); 322 } 323 } 324 325 class TestNumericThresholdWithDwellTime2 : 326 public TestNumericThreshold, 327 public WithParamInterface<NumericParams> 328 { 329 public: 330 void SetUp() override 331 { 332 for (size_t idx = 0; idx < sensorMocks.size(); idx++) 333 { 334 ON_CALL(*sensorMocks.at(idx), getName()) 335 .WillByDefault(Return(sensorNames[idx])); 336 } 337 338 makeThreshold(2ms, GetParam().direction, 90.0); 339 } 340 341 void sleep() 342 { 343 DbusEnvironment::sleepFor(4ms); 344 } 345 }; 346 347 INSTANTIATE_TEST_SUITE_P(_, TestNumericThresholdWithDwellTime2, 348 Values(NumericParams() 349 .Direction(numeric::Direction::increasing) 350 .Updates({{0, 1ms, 80.0}, {0, 2ms, 89.0}}) 351 .Expected({}), 352 NumericParams() 353 .Direction(numeric::Direction::increasing) 354 .Updates({{0, 1ms, 80.0}, {0, 2ms, 91.0}}) 355 .Expected({{0, 2ms, 91.0}}), 356 NumericParams() 357 .Direction(numeric::Direction::increasing) 358 .Updates({{0, 1ms, 80.0}, 359 {0, 2ms, 99.0}, 360 {0, 3ms, 80.0}, 361 {0, 4ms, 98.0}}) 362 .Expected({{0, 4ms, 98.0}}), 363 NumericParams() 364 .Direction(numeric::Direction::increasing) 365 .Updates({{0, 1ms, 80.0}, 366 {1, 2ms, 99.0}, 367 {0, 3ms, 100.0}, 368 {1, 4ms, 98.0}}) 369 .Expected({{0, 3ms, 100.0}}), 370 NumericParams() 371 .Direction(numeric::Direction::decreasing) 372 .Updates({{0, 1ms, 100.0}, {0, 2ms, 91.0}}) 373 .Expected({}), 374 NumericParams() 375 .Direction(numeric::Direction::decreasing) 376 .Updates({{0, 1ms, 100.0}, {0, 2ms, 80.0}}) 377 .Expected({{0, 2ms, 80.0}}), 378 NumericParams() 379 .Direction(numeric::Direction::decreasing) 380 .Updates({{0, 1ms, 100.0}, 381 {0, 2ms, 80.0}, 382 {0, 3ms, 99.0}, 383 {0, 4ms, 85.0}}) 384 .Expected({{0, 4ms, 85.0}}), 385 NumericParams() 386 .Direction(numeric::Direction::decreasing) 387 .Updates({{0, 1ms, 100.0}, 388 {0, 2ms, 80.0}, 389 {1, 3ms, 99.0}, 390 {1, 4ms, 88.0}}) 391 .Expected({{0, 2ms, 80.0}, {1, 4ms, 88.0}}), 392 NumericParams() 393 .Direction(numeric::Direction::either) 394 .Updates({{0, 1ms, 98.0}, {0, 2ms, 91.0}}) 395 .Expected({}), 396 NumericParams() 397 .Direction(numeric::Direction::either) 398 .Updates({{0, 1ms, 100.0}, 399 {0, 2ms, 80.0}, 400 {0, 3ms, 85.0}, 401 {0, 4ms, 91.0}}) 402 .Expected({{0, 4ms, 91.0}}), 403 NumericParams() 404 .Direction(numeric::Direction::either) 405 .Updates({{0, 1ms, 100.0}, 406 {1, 2ms, 80.0}, 407 {0, 3ms, 85.0}, 408 {1, 4ms, 91.0}}) 409 .Expected({{0, 3ms, 85.0}, 410 {1, 4ms, 91.0}}))); 411 412 TEST_P(TestNumericThresholdWithDwellTime2, 413 senorsIsUpdatedMultipleTimesSleepAfterLastUpdate) 414 { 415 InSequence seq; 416 for (const auto& [index, timestamp, value] : GetParam().expected) 417 { 418 EXPECT_CALL(actionMock, commit(sensorNames[index], timestamp, value)); 419 } 420 421 sut->initialize(); 422 for (const auto& [index, timestamp, value] : GetParam().updates) 423 { 424 sut->sensorUpdated(*sensorMocks[index], timestamp, value); 425 } 426 sleep(); 427 } 428