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