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/milliseconds.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> makeThreshold(Milliseconds dwellTime, 27 double thresholdValue) 28 { 29 std::vector<std::unique_ptr<interfaces::TriggerAction>> actions; 30 actions.push_back(std::move(actionMockPtr)); 31 32 return std::make_shared<DiscreteThreshold>( 33 DbusEnvironment::getIoc(), 34 utils::convContainer<std::shared_ptr<interfaces::Sensor>>( 35 sensorMocks), 36 sensorNames, std::move(actions), dwellTime, thresholdValue, 37 "treshold_name"); 38 } 39 40 void SetUp() override 41 { 42 sut = makeThreshold(0ms, 90.0); 43 } 44 }; 45 46 TEST_F(TestDiscreteThreshold, initializeThresholdExpectAllSensorsAreRegistered) 47 { 48 for (auto& sensor : sensorMocks) 49 { 50 EXPECT_CALL(*sensor, 51 registerForUpdates(Truly([sut = sut.get()](const auto& x) { 52 return x.lock().get() == sut; 53 }))); 54 } 55 56 sut->initialize(); 57 } 58 59 TEST_F(TestDiscreteThreshold, thresholdIsNotInitializeExpectNoActionCommit) 60 { 61 EXPECT_CALL(actionMock, commit(_, _, _)).Times(0); 62 } 63 64 struct DiscreteParams 65 { 66 struct UpdateParams 67 { 68 size_t sensor; 69 uint64_t timestamp; 70 double value; 71 Milliseconds sleepAfter; 72 73 UpdateParams(size_t sensor, uint64_t timestamp, double value, 74 Milliseconds sleepAfter = 0ms) : 75 sensor(sensor), 76 timestamp(timestamp), value(value), sleepAfter(sleepAfter) 77 {} 78 }; 79 80 struct ExpectedParams 81 { 82 size_t sensor; 83 uint64_t timestamp; 84 double value; 85 Milliseconds waitMin; 86 87 ExpectedParams(size_t sensor, uint64_t timestamp, double value, 88 Milliseconds waitMin = 0ms) : 89 sensor(sensor), 90 timestamp(timestamp), value(value), waitMin(waitMin) 91 {} 92 }; 93 94 DiscreteParams& Updates(std::vector<UpdateParams> val) 95 { 96 updates = std::move(val); 97 return *this; 98 } 99 100 DiscreteParams& Expected(std::vector<ExpectedParams> val) 101 { 102 expected = std::move(val); 103 return *this; 104 } 105 106 DiscreteParams& ThresholdValue(double val) 107 { 108 thresholdValue = val; 109 return *this; 110 } 111 112 DiscreteParams& DwellTime(Milliseconds val) 113 { 114 dwellTime = std::move(val); 115 return *this; 116 } 117 118 friend void PrintTo(const DiscreteParams& o, std::ostream* os) 119 { 120 *os << "{ DwellTime: " << o.dwellTime.count() << "ms "; 121 *os << ", ThresholdValue: " << o.thresholdValue; 122 *os << ", Updates: "; 123 for (const auto& [index, timestamp, value, sleepAfter] : o.updates) 124 { 125 *os << "{ SensorIndex: " << index << ", Timestamp: " << timestamp 126 << ", Value: " << value 127 << ", SleepAfter: " << sleepAfter.count() << "ms }, "; 128 } 129 *os << "Expected: "; 130 for (const auto& [index, timestamp, value, waitMin] : o.expected) 131 { 132 *os << "{ SensorIndex: " << index << ", Timestamp: " << timestamp 133 << ", Value: " << value << ", waitMin: " << waitMin.count() 134 << "ms }, "; 135 } 136 *os << " }"; 137 } 138 139 std::vector<UpdateParams> updates; 140 std::vector<ExpectedParams> expected; 141 double thresholdValue = 0.0; 142 Milliseconds dwellTime = 0ms; 143 }; 144 145 class TestDiscreteThresholdCommon : 146 public TestDiscreteThreshold, 147 public WithParamInterface<DiscreteParams> 148 { 149 public: 150 void sleep(Milliseconds duration) 151 { 152 if (duration != 0ms) 153 { 154 DbusEnvironment::sleepFor(duration); 155 } 156 } 157 158 void testBodySensorIsUpdatedMultipleTimes() 159 { 160 std::vector<std::chrono::time_point<std::chrono::high_resolution_clock>> 161 timestamps(sensorMocks.size()); 162 163 sut->initialize(); 164 165 InSequence seq; 166 167 for (const auto& [index, timestamp, value, waitMin] : 168 GetParam().expected) 169 { 170 EXPECT_CALL(actionMock, 171 commit(sensorNames[index], timestamp, value)) 172 .WillOnce(DoAll( 173 InvokeWithoutArgs([idx = index, ×tamps] { 174 timestamps[idx] = 175 std::chrono::high_resolution_clock::now(); 176 }), 177 InvokeWithoutArgs(DbusEnvironment::setPromise("commit")))); 178 } 179 180 auto start = std::chrono::high_resolution_clock::now(); 181 182 for (const auto& [index, timestamp, value, sleepAfter] : 183 GetParam().updates) 184 { 185 sut->sensorUpdated(*sensorMocks[index], timestamp, value); 186 sleep(sleepAfter); 187 } 188 189 EXPECT_THAT(DbusEnvironment::waitForFutures("commit"), true); 190 for (const auto& [index, timestamp, value, waitMin] : 191 GetParam().expected) 192 { 193 EXPECT_THAT(timestamps[index] - start, Ge(waitMin)); 194 } 195 } 196 }; 197 198 class TestDiscreteThresholdNoDwellTime : public TestDiscreteThresholdCommon 199 { 200 public: 201 void SetUp() override 202 { 203 sut = makeThreshold(0ms, GetParam().thresholdValue); 204 } 205 }; 206 207 INSTANTIATE_TEST_SUITE_P( 208 _, TestDiscreteThresholdNoDwellTime, 209 Values( 210 DiscreteParams() 211 .ThresholdValue(90.0) 212 .Updates({{0, 1, 80.0}, {0, 2, 89.0}}) 213 .Expected({}), 214 DiscreteParams() 215 .ThresholdValue(90.0) 216 .Updates({{0, 1, 80.0}, {0, 2, 90.0}, {0, 3, 80.0}, {0, 4, 90.0}}) 217 .Expected({{0, 2, 90.0}, {0, 4, 90.0}}), 218 DiscreteParams() 219 .ThresholdValue(90.0) 220 .Updates({{0, 1, 90.0}, {0, 2, 99.0}, {1, 3, 100.0}, {1, 4, 90.0}}) 221 .Expected({{0, 1, 90.0}, {1, 4, 90.0}}))); 222 223 TEST_P(TestDiscreteThresholdNoDwellTime, senorsIsUpdatedMultipleTimes) 224 { 225 testBodySensorIsUpdatedMultipleTimes(); 226 } 227 228 class TestDiscreteThresholdWithDwellTime : public TestDiscreteThresholdCommon 229 { 230 public: 231 void SetUp() override 232 { 233 sut = makeThreshold(GetParam().dwellTime, GetParam().thresholdValue); 234 } 235 }; 236 237 INSTANTIATE_TEST_SUITE_P( 238 _, TestDiscreteThresholdWithDwellTime, 239 Values(DiscreteParams() 240 .DwellTime(200ms) 241 .ThresholdValue(90.0) 242 .Updates({{0, 1, 90.0, 100ms}, {0, 2, 91.0}, {0, 3, 90.0}}) 243 .Expected({{0, 3, 90.0, 300ms}}), 244 DiscreteParams() 245 .DwellTime(100ms) 246 .ThresholdValue(90.0) 247 .Updates({{0, 1, 90.0, 100ms}}) 248 .Expected({{0, 1, 90.0, 100ms}}), 249 DiscreteParams() 250 .DwellTime(1000ms) 251 .ThresholdValue(90.0) 252 .Updates({{0, 1, 90.0, 700ms}, 253 {0, 1, 91.0, 100ms}, 254 {0, 1, 90.0, 300ms}, 255 {0, 1, 91.0, 100ms}}) 256 .Expected({}), 257 DiscreteParams() 258 .DwellTime(200ms) 259 .ThresholdValue(90.0) 260 .Updates({{0, 1, 90.0}, 261 {1, 2, 89.0, 100ms}, 262 {1, 3, 90.0, 100ms}, 263 {1, 4, 89.0, 100ms}, 264 {1, 5, 90.0, 300ms}, 265 {1, 6, 89.0, 100ms}}) 266 .Expected({{0, 1, 90, 200ms}, {1, 5, 90, 500ms}}))); 267 268 TEST_P(TestDiscreteThresholdWithDwellTime, senorsIsUpdatedMultipleTimes) 269 { 270 testBodySensorIsUpdatedMultipleTimes(); 271 } 272