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