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 TEST_F(TestDiscreteThresholdInit, nonEmptyNameIsNotChanged) 111 { 112 auto sut = makeThreshold(0ms, "12.3", discrete::Severity::ok, "non-empty"); 113 EXPECT_THAT( 114 std::get<discrete::LabeledThresholdParam>(sut->getThresholdParam()) 115 .at_label<utils::tstring::UserId>(), 116 Eq("non-empty")); 117 } 118 119 TEST_F(TestDiscreteThresholdInit, emptyNameIsChanged) 120 { 121 auto sut = makeThreshold(0ms, "12.3", discrete::Severity::ok, ""); 122 EXPECT_THAT( 123 std::get<discrete::LabeledThresholdParam>(sut->getThresholdParam()) 124 .at_label<utils::tstring::UserId>(), 125 Not(Eq(""))); 126 } 127 128 struct DiscreteParams 129 { 130 struct UpdateParams 131 { 132 size_t sensor; 133 double value; 134 Milliseconds sleepAfter; 135 136 UpdateParams(size_t sensor, double value, 137 Milliseconds sleepAfter = 0ms) : 138 sensor(sensor), value(value), sleepAfter(sleepAfter) 139 {} 140 }; 141 142 struct ExpectedParams 143 { 144 size_t sensor; 145 double value; 146 Milliseconds waitMin; 147 148 ExpectedParams(size_t sensor, double value, 149 Milliseconds waitMin = 0ms) : 150 sensor(sensor), value(value), waitMin(waitMin) 151 {} 152 }; 153 154 DiscreteParams& Updates(std::vector<UpdateParams> val) 155 { 156 updates = std::move(val); 157 return *this; 158 } 159 160 DiscreteParams& Expected(std::vector<ExpectedParams> val) 161 { 162 expected = std::move(val); 163 return *this; 164 } 165 166 DiscreteParams& ThresholdValue(std::string val) 167 { 168 thresholdValue = std::move(val); 169 return *this; 170 } 171 172 DiscreteParams& DwellTime(Milliseconds val) 173 { 174 dwellTime = std::move(val); 175 return *this; 176 } 177 178 friend void PrintTo(const DiscreteParams& o, std::ostream* os) 179 { 180 *os << "{ DwellTime: " << o.dwellTime.count() << "ms "; 181 *os << ", ThresholdValue: " << o.thresholdValue; 182 *os << ", Updates: [ "; 183 for (const auto& [index, value, sleepAfter] : o.updates) 184 { 185 *os << "{ SensorIndex: " << index << ", Value: " << value 186 << ", SleepAfter: " << sleepAfter.count() << "ms }, "; 187 } 188 *os << " ] Expected: [ "; 189 for (const auto& [index, value, waitMin] : o.expected) 190 { 191 *os << "{ SensorIndex: " << index << ", Value: " << value 192 << ", waitMin: " << waitMin.count() << "ms }, "; 193 } 194 *os << " ] }"; 195 } 196 197 std::vector<UpdateParams> updates; 198 std::vector<ExpectedParams> expected; 199 std::string thresholdValue = "0.0"; 200 Milliseconds dwellTime = 0ms; 201 }; 202 203 class TestDiscreteThresholdCommon : 204 public TestDiscreteThreshold, 205 public WithParamInterface<DiscreteParams> 206 { 207 public: 208 void sleep(Milliseconds duration) 209 { 210 if (duration != 0ms) 211 { 212 DbusEnvironment::sleepFor(duration); 213 } 214 } 215 216 void testBodySensorIsUpdatedMultipleTimes() 217 { 218 std::vector<std::chrono::time_point<std::chrono::high_resolution_clock>> 219 timestamps(sensorMocks.size()); 220 221 sut->initialize(); 222 223 InSequence seq; 224 225 for (const auto& [index, value, waitMin] : GetParam().expected) 226 { 227 EXPECT_CALL(actionMock, 228 commit(triggerId, Optional(StrEq("treshold name")), 229 sensorNames[index], _, 230 TriggerValue(GetParam().thresholdValue))) 231 .WillOnce(DoAll( 232 InvokeWithoutArgs([idx = index, ×tamps] { 233 timestamps[idx] = std::chrono::high_resolution_clock::now(); 234 }), 235 InvokeWithoutArgs(DbusEnvironment::setPromise("commit")))); 236 } 237 238 auto start = std::chrono::high_resolution_clock::now(); 239 240 for (const auto& [index, value, sleepAfter] : GetParam().updates) 241 { 242 sut->sensorUpdated(*sensorMocks[index], 42ms, value); 243 sleep(sleepAfter); 244 } 245 246 EXPECT_THAT(DbusEnvironment::waitForFutures("commit"), true); 247 for (const auto& [index, value, waitMin] : GetParam().expected) 248 { 249 EXPECT_THAT(timestamps[index] - start, Ge(waitMin)); 250 } 251 } 252 }; 253 254 class TestDiscreteThresholdNoDwellTime : public TestDiscreteThresholdCommon 255 { 256 public: 257 void SetUp() override 258 { 259 for (size_t idx = 0; idx < sensorMocks.size(); idx++) 260 { 261 ON_CALL(*sensorMocks.at(idx), getName()) 262 .WillByDefault(Return(sensorNames[idx])); 263 } 264 265 sut = makeThreshold(0ms, GetParam().thresholdValue); 266 } 267 }; 268 269 INSTANTIATE_TEST_SUITE_P( 270 _, TestDiscreteThresholdNoDwellTime, 271 Values(DiscreteParams() 272 .ThresholdValue("90.0") 273 .Updates({{0, 80.0}, {0, 89.0}}) 274 .Expected({}), 275 DiscreteParams() 276 .ThresholdValue("90.0") 277 .Updates({{0, 80.0}, {0, 90.0}, {0, 80.0}, {0, 90.0}}) 278 .Expected({{0, 90.0}, {0, 90.0}}), 279 DiscreteParams() 280 .ThresholdValue("90.0") 281 .Updates({{0, 90.0}, {0, 99.0}, {1, 100.0}, {1, 90.0}}) 282 .Expected({{0, 90.0}, {1, 90.0}}))); 283 284 TEST_P(TestDiscreteThresholdNoDwellTime, senorsIsUpdatedMultipleTimes) 285 { 286 testBodySensorIsUpdatedMultipleTimes(); 287 } 288 289 class TestDiscreteThresholdWithDwellTime : public TestDiscreteThresholdCommon 290 { 291 public: 292 void SetUp() override 293 { 294 for (size_t idx = 0; idx < sensorMocks.size(); idx++) 295 { 296 ON_CALL(*sensorMocks.at(idx), getName()) 297 .WillByDefault(Return(sensorNames[idx])); 298 } 299 300 sut = makeThreshold(GetParam().dwellTime, GetParam().thresholdValue); 301 } 302 }; 303 304 INSTANTIATE_TEST_SUITE_P( 305 _, TestDiscreteThresholdWithDwellTime, 306 Values(DiscreteParams() 307 .DwellTime(200ms) 308 .ThresholdValue("90.0") 309 .Updates({{0, 90.0, 100ms}, {0, 91.0}, {0, 90.0}}) 310 .Expected({{0, 90.0, 300ms}}), 311 DiscreteParams() 312 .DwellTime(100ms) 313 .ThresholdValue("90.0") 314 .Updates({{0, 90.0, 100ms}}) 315 .Expected({{0, 90.0, 100ms}}), 316 DiscreteParams() 317 .DwellTime(1000ms) 318 .ThresholdValue("90.0") 319 .Updates({{0, 90.0, 700ms}, 320 {0, 91.0, 100ms}, 321 {0, 90.0, 300ms}, 322 {0, 91.0, 100ms}}) 323 .Expected({}), 324 DiscreteParams() 325 .DwellTime(200ms) 326 .ThresholdValue("90.0") 327 .Updates({{0, 90.0}, 328 {1, 89.0, 100ms}, 329 {1, 90.0, 100ms}, 330 {1, 89.0, 100ms}, 331 {1, 90.0, 300ms}, 332 {1, 89.0, 100ms}}) 333 .Expected({{0, 90, 200ms}, {1, 90, 500ms}}))); 334 335 TEST_P(TestDiscreteThresholdWithDwellTime, senorsIsUpdatedMultipleTimes) 336 { 337 testBodySensorIsUpdatedMultipleTimes(); 338 } 339