1 #include "dbus_environment.hpp" 2 #include "discrete_threshold.hpp" 3 #include "helpers.hpp" 4 #include "mocks/clock_mock.hpp" 5 #include "mocks/sensor_mock.hpp" 6 #include "mocks/trigger_action_mock.hpp" 7 #include "types/duration_types.hpp" 8 #include "utils/conv_container.hpp" 9 10 #include <gmock/gmock.h> 11 12 using namespace testing; 13 using namespace std::chrono_literals; 14 15 class TestDiscreteThreshold : public Test 16 { 17 public: 18 std::vector<std::shared_ptr<SensorMock>> sensorMocks = { 19 std::make_shared<NiceMock<SensorMock>>(), 20 std::make_shared<NiceMock<SensorMock>>()}; 21 std::vector<std::string> sensorNames = {"Sensor1", "Sensor2"}; 22 std::unique_ptr<TriggerActionMock> actionMockPtr = 23 std::make_unique<StrictMock<TriggerActionMock>>(); 24 TriggerActionMock& actionMock = *actionMockPtr; 25 std::shared_ptr<DiscreteThreshold> sut; 26 std::string triggerId = "MyTrigger"; 27 std::unique_ptr<NiceMock<ClockMock>> clockMockPtr = 28 std::make_unique<NiceMock<ClockMock>>(); 29 30 std::shared_ptr<DiscreteThreshold> 31 makeThreshold(Milliseconds dwellTime, std::string thresholdValue, 32 discrete::Severity severity = discrete::Severity::ok, 33 std::string thresholdName = "treshold name") 34 { 35 std::vector<std::unique_ptr<interfaces::TriggerAction>> actions; 36 actions.push_back(std::move(actionMockPtr)); 37 38 return std::make_shared<DiscreteThreshold>( 39 DbusEnvironment::getIoc(), triggerId, 40 utils::convContainer<std::shared_ptr<interfaces::Sensor>>( 41 sensorMocks), 42 std::move(actions), dwellTime, thresholdValue, thresholdName, 43 severity, std::move(clockMockPtr)); 44 } 45 46 void SetUp() override 47 { 48 for (size_t idx = 0; idx < sensorMocks.size(); idx++) 49 { 50 ON_CALL(*sensorMocks.at(idx), getName()) 51 .WillByDefault(Return(sensorNames[idx])); 52 } 53 54 sut = makeThreshold(0ms, "90.0", discrete::Severity::critical); 55 } 56 }; 57 58 TEST_F(TestDiscreteThreshold, initializeThresholdExpectAllSensorsAreRegistered) 59 { 60 for (auto& sensor : sensorMocks) 61 { 62 EXPECT_CALL(*sensor, 63 registerForUpdates(Truly([sut = sut.get()](const auto& x) { 64 return x.lock().get() == sut; 65 }))); 66 } 67 68 sut->initialize(); 69 } 70 71 TEST_F(TestDiscreteThreshold, thresholdIsNotInitializeExpectNoActionCommit) 72 { 73 EXPECT_CALL(actionMock, commit(_, _, _, _, _)).Times(0); 74 } 75 76 class TestDiscreteThresholdValues : 77 public TestDiscreteThreshold, 78 public WithParamInterface<std::string> 79 {}; 80 81 INSTANTIATE_TEST_SUITE_P(_, TestDiscreteThresholdValues, 82 Values("90", ".90", "90.123", "0.0")); 83 84 TEST_P(TestDiscreteThresholdValues, thresholdValueIsNumericAndStoredCorrectly) 85 { 86 sut = makeThreshold(0ms, GetParam(), discrete::Severity::critical); 87 LabeledThresholdParam expected = discrete::LabeledThresholdParam( 88 "treshold name", discrete::Severity::critical, 0, GetParam()); 89 EXPECT_EQ(sut->getThresholdParam(), expected); 90 } 91 92 class TestBadDiscreteThresholdValues : 93 public TestDiscreteThreshold, 94 public WithParamInterface<std::string> 95 {}; 96 97 INSTANTIATE_TEST_SUITE_P(_, TestBadDiscreteThresholdValues, 98 Values("90ad", "ab.90", "x90", "On", "Off", "")); 99 100 TEST_P(TestBadDiscreteThresholdValues, throwsWhenNotNumericValues) 101 { 102 EXPECT_THROW(makeThreshold(0ms, GetParam()), std::invalid_argument); 103 } 104 105 class TestDiscreteThresholdInit : public TestDiscreteThreshold 106 { 107 void SetUp() override 108 {} 109 }; 110 111 TEST_F(TestDiscreteThresholdInit, nonEmptyNameIsNotChanged) 112 { 113 auto sut = makeThreshold(0ms, "12.3", discrete::Severity::ok, "non-empty"); 114 EXPECT_THAT( 115 std::get<discrete::LabeledThresholdParam>(sut->getThresholdParam()) 116 .at_label<utils::tstring::UserId>(), 117 Eq("non-empty")); 118 } 119 120 TEST_F(TestDiscreteThresholdInit, emptyNameIsChanged) 121 { 122 auto sut = makeThreshold(0ms, "12.3", discrete::Severity::ok, ""); 123 EXPECT_THAT( 124 std::get<discrete::LabeledThresholdParam>(sut->getThresholdParam()) 125 .at_label<utils::tstring::UserId>(), 126 Not(Eq(""))); 127 } 128 129 struct DiscreteParams 130 { 131 struct UpdateParams 132 { 133 size_t sensor; 134 double value; 135 Milliseconds sleepAfter; 136 137 UpdateParams(size_t sensor, double value, 138 Milliseconds sleepAfter = 0ms) : 139 sensor(sensor), 140 value(value), sleepAfter(sleepAfter) 141 {} 142 }; 143 144 struct ExpectedParams 145 { 146 size_t sensor; 147 double value; 148 Milliseconds waitMin; 149 150 ExpectedParams(size_t sensor, double value, 151 Milliseconds waitMin = 0ms) : 152 sensor(sensor), 153 value(value), waitMin(waitMin) 154 {} 155 }; 156 157 DiscreteParams& Updates(std::vector<UpdateParams> val) 158 { 159 updates = std::move(val); 160 return *this; 161 } 162 163 DiscreteParams& Expected(std::vector<ExpectedParams> val) 164 { 165 expected = std::move(val); 166 return *this; 167 } 168 169 DiscreteParams& ThresholdValue(std::string val) 170 { 171 thresholdValue = std::move(val); 172 return *this; 173 } 174 175 DiscreteParams& DwellTime(Milliseconds val) 176 { 177 dwellTime = std::move(val); 178 return *this; 179 } 180 181 friend void PrintTo(const DiscreteParams& o, std::ostream* os) 182 { 183 *os << "{ DwellTime: " << o.dwellTime.count() << "ms "; 184 *os << ", ThresholdValue: " << o.thresholdValue; 185 *os << ", Updates: [ "; 186 for (const auto& [index, value, sleepAfter] : o.updates) 187 { 188 *os << "{ SensorIndex: " << index << ", Value: " << value 189 << ", SleepAfter: " << sleepAfter.count() << "ms }, "; 190 } 191 *os << " ] Expected: [ "; 192 for (const auto& [index, value, waitMin] : o.expected) 193 { 194 *os << "{ SensorIndex: " << index << ", Value: " << value 195 << ", waitMin: " << waitMin.count() << "ms }, "; 196 } 197 *os << " ] }"; 198 } 199 200 std::vector<UpdateParams> updates; 201 std::vector<ExpectedParams> expected; 202 std::string thresholdValue = "0.0"; 203 Milliseconds dwellTime = 0ms; 204 }; 205 206 class TestDiscreteThresholdCommon : 207 public TestDiscreteThreshold, 208 public WithParamInterface<DiscreteParams> 209 { 210 public: 211 void sleep(Milliseconds duration) 212 { 213 if (duration != 0ms) 214 { 215 DbusEnvironment::sleepFor(duration); 216 } 217 } 218 219 void testBodySensorIsUpdatedMultipleTimes() 220 { 221 std::vector<std::chrono::time_point<std::chrono::high_resolution_clock>> 222 timestamps(sensorMocks.size()); 223 224 sut->initialize(); 225 226 InSequence seq; 227 228 for (const auto& [index, value, waitMin] : GetParam().expected) 229 { 230 EXPECT_CALL(actionMock, 231 commit(triggerId, Optional(StrEq("treshold name")), 232 sensorNames[index], _, 233 TriggerValue(GetParam().thresholdValue))) 234 .WillOnce(DoAll( 235 InvokeWithoutArgs([idx = index, ×tamps] { 236 timestamps[idx] = 237 std::chrono::high_resolution_clock::now(); 238 }), 239 InvokeWithoutArgs(DbusEnvironment::setPromise("commit")))); 240 } 241 242 auto start = std::chrono::high_resolution_clock::now(); 243 244 for (const auto& [index, value, sleepAfter] : GetParam().updates) 245 { 246 sut->sensorUpdated(*sensorMocks[index], 42ms, value); 247 sleep(sleepAfter); 248 } 249 250 EXPECT_THAT(DbusEnvironment::waitForFutures("commit"), true); 251 for (const auto& [index, value, waitMin] : GetParam().expected) 252 { 253 EXPECT_THAT(timestamps[index] - start, Ge(waitMin)); 254 } 255 } 256 }; 257 258 class TestDiscreteThresholdNoDwellTime : public TestDiscreteThresholdCommon 259 { 260 public: 261 void SetUp() override 262 { 263 for (size_t idx = 0; idx < sensorMocks.size(); idx++) 264 { 265 ON_CALL(*sensorMocks.at(idx), getName()) 266 .WillByDefault(Return(sensorNames[idx])); 267 } 268 269 sut = makeThreshold(0ms, GetParam().thresholdValue); 270 } 271 }; 272 273 INSTANTIATE_TEST_SUITE_P( 274 _, TestDiscreteThresholdNoDwellTime, 275 Values(DiscreteParams() 276 .ThresholdValue("90.0") 277 .Updates({{0, 80.0}, {0, 89.0}}) 278 .Expected({}), 279 DiscreteParams() 280 .ThresholdValue("90.0") 281 .Updates({{0, 80.0}, {0, 90.0}, {0, 80.0}, {0, 90.0}}) 282 .Expected({{0, 90.0}, {0, 90.0}}), 283 DiscreteParams() 284 .ThresholdValue("90.0") 285 .Updates({{0, 90.0}, {0, 99.0}, {1, 100.0}, {1, 90.0}}) 286 .Expected({{0, 90.0}, {1, 90.0}}))); 287 288 TEST_P(TestDiscreteThresholdNoDwellTime, senorsIsUpdatedMultipleTimes) 289 { 290 testBodySensorIsUpdatedMultipleTimes(); 291 } 292 293 class TestDiscreteThresholdWithDwellTime : public TestDiscreteThresholdCommon 294 { 295 public: 296 void SetUp() override 297 { 298 for (size_t idx = 0; idx < sensorMocks.size(); idx++) 299 { 300 ON_CALL(*sensorMocks.at(idx), getName()) 301 .WillByDefault(Return(sensorNames[idx])); 302 } 303 304 sut = makeThreshold(GetParam().dwellTime, GetParam().thresholdValue); 305 } 306 }; 307 308 INSTANTIATE_TEST_SUITE_P( 309 _, TestDiscreteThresholdWithDwellTime, 310 Values(DiscreteParams() 311 .DwellTime(200ms) 312 .ThresholdValue("90.0") 313 .Updates({{0, 90.0, 100ms}, {0, 91.0}, {0, 90.0}}) 314 .Expected({{0, 90.0, 300ms}}), 315 DiscreteParams() 316 .DwellTime(100ms) 317 .ThresholdValue("90.0") 318 .Updates({{0, 90.0, 100ms}}) 319 .Expected({{0, 90.0, 100ms}}), 320 DiscreteParams() 321 .DwellTime(1000ms) 322 .ThresholdValue("90.0") 323 .Updates({{0, 90.0, 700ms}, 324 {0, 91.0, 100ms}, 325 {0, 90.0, 300ms}, 326 {0, 91.0, 100ms}}) 327 .Expected({}), 328 DiscreteParams() 329 .DwellTime(200ms) 330 .ThresholdValue("90.0") 331 .Updates({{0, 90.0}, 332 {1, 89.0, 100ms}, 333 {1, 90.0, 100ms}, 334 {1, 89.0, 100ms}, 335 {1, 90.0, 300ms}, 336 {1, 89.0, 100ms}}) 337 .Expected({{0, 90, 200ms}, {1, 90, 500ms}}))); 338 339 TEST_P(TestDiscreteThresholdWithDwellTime, senorsIsUpdatedMultipleTimes) 340 { 341 testBodySensorIsUpdatedMultipleTimes(); 342 } 343