1 #include <chrono> 2 #include <gmock/gmock.h> 3 #include <gtest/gtest.h> 4 #include <memory> 5 #include <sdeventplus/clock.hpp> 6 #include <sdeventplus/event.hpp> 7 #include <sdeventplus/test/sdevent.hpp> 8 #include <sdeventplus/utility/timer.hpp> 9 #include <stdexcept> 10 #include <systemd/sd-event.h> 11 12 namespace sdeventplus 13 { 14 namespace utility 15 { 16 namespace 17 { 18 19 constexpr ClockId testClock = ClockId::Monotonic; 20 21 using std::chrono::microseconds; 22 using std::chrono::milliseconds; 23 using testing::DoAll; 24 using testing::Return; 25 using testing::SaveArg; 26 using testing::SetArgPointee; 27 using TestTimer = Timer<testClock>; 28 29 ssize_t event_ref_times = 0; 30 31 ACTION(EventRef) 32 { 33 event_ref_times++; 34 } 35 36 ACTION(EventUnref) 37 { 38 ASSERT_LT(0, event_ref_times); 39 event_ref_times--; 40 } 41 42 class TimerTest : public testing::Test 43 { 44 protected: 45 testing::StrictMock<test::SdEventMock> mock; 46 sd_event* const expected_event = reinterpret_cast<sd_event*>(1234); 47 sd_event_source* const expected_source = 48 reinterpret_cast<sd_event_source*>(2345); 49 const milliseconds interval{134}; 50 const milliseconds starting_time{10}; 51 sd_event_time_handler_t handler = nullptr; 52 void* handler_userdata; 53 std::unique_ptr<TestTimer> timer; 54 std::function<void()> callback; 55 56 void expectNow(microseconds ret) 57 { 58 EXPECT_CALL(mock, 59 sd_event_now(expected_event, 60 static_cast<clockid_t>(testClock), testing::_)) 61 .WillOnce(DoAll(SetArgPointee<2>(ret.count()), Return(0))); 62 } 63 64 void expectSetTime(microseconds time) 65 { 66 EXPECT_CALL(mock, 67 sd_event_source_set_time(expected_source, time.count())) 68 .WillOnce(Return(0)); 69 } 70 71 void expectSetEnabled(source::Enabled enabled) 72 { 73 EXPECT_CALL(mock, sd_event_source_set_enabled( 74 expected_source, static_cast<int>(enabled))) 75 .WillOnce(Return(0)); 76 } 77 78 void expectGetEnabled(source::Enabled enabled) 79 { 80 EXPECT_CALL(mock, 81 sd_event_source_get_enabled(expected_source, testing::_)) 82 .WillOnce( 83 DoAll(SetArgPointee<1>(static_cast<int>(enabled)), Return(0))); 84 } 85 86 void SetUp() 87 { 88 EXPECT_CALL(mock, sd_event_ref(expected_event)) 89 .WillRepeatedly(DoAll(EventRef(), Return(expected_event))); 90 EXPECT_CALL(mock, sd_event_unref(expected_event)) 91 .WillRepeatedly(DoAll(EventUnref(), Return(nullptr))); 92 Event event(expected_event, &mock); 93 94 auto runCallback = [&](TestTimer&) { 95 if (callback) 96 { 97 callback(); 98 } 99 }; 100 expectNow(starting_time); 101 EXPECT_CALL(mock, sd_event_add_time( 102 expected_event, testing::_, 103 static_cast<clockid_t>(testClock), 104 microseconds(starting_time + interval).count(), 105 1000, testing::_, nullptr)) 106 .WillOnce(DoAll(SetArgPointee<1>(expected_source), 107 SaveArg<5>(&handler), Return(0))); 108 EXPECT_CALL(mock, 109 sd_event_source_set_userdata(expected_source, testing::_)) 110 .WillOnce(DoAll(SaveArg<1>(&handler_userdata), Return(nullptr))); 111 // Timer always enables the source to keep ticking 112 expectSetEnabled(source::Enabled::On); 113 timer = std::make_unique<TestTimer>(event, runCallback, interval); 114 } 115 116 void TearDown() 117 { 118 expectSetEnabled(source::Enabled::Off); 119 EXPECT_CALL(mock, sd_event_source_unref(expected_source)) 120 .WillOnce(Return(nullptr)); 121 timer.reset(); 122 EXPECT_EQ(0, event_ref_times); 123 } 124 }; 125 126 TEST_F(TimerTest, NewTimer) 127 { 128 EXPECT_FALSE(timer->hasExpired()); 129 EXPECT_EQ(interval, timer->getInterval()); 130 } 131 132 TEST_F(TimerTest, IsEnabled) 133 { 134 expectGetEnabled(source::Enabled::On); 135 EXPECT_TRUE(timer->isEnabled()); 136 expectGetEnabled(source::Enabled::Off); 137 EXPECT_FALSE(timer->isEnabled()); 138 } 139 140 TEST_F(TimerTest, GetRemainingDisabled) 141 { 142 expectGetEnabled(source::Enabled::Off); 143 EXPECT_THROW(timer->getRemaining(), std::runtime_error); 144 } 145 146 TEST_F(TimerTest, GetRemainingNegative) 147 { 148 milliseconds now(675), end(453); 149 expectGetEnabled(source::Enabled::On); 150 EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_)) 151 .WillOnce( 152 DoAll(SetArgPointee<1>(microseconds(end).count()), Return(0))); 153 expectNow(now); 154 EXPECT_EQ(milliseconds(0), timer->getRemaining()); 155 } 156 157 TEST_F(TimerTest, GetRemainingPositive) 158 { 159 milliseconds now(453), end(675); 160 expectGetEnabled(source::Enabled::On); 161 EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_)) 162 .WillOnce( 163 DoAll(SetArgPointee<1>(microseconds(end).count()), Return(0))); 164 expectNow(now); 165 EXPECT_EQ(end - now, timer->getRemaining()); 166 } 167 168 TEST_F(TimerTest, SetEnabled) 169 { 170 expectSetEnabled(source::Enabled::On); 171 timer->setEnabled(true); 172 EXPECT_FALSE(timer->hasExpired()); 173 // Value should always be passed through regardless of current state 174 expectSetEnabled(source::Enabled::On); 175 timer->setEnabled(true); 176 EXPECT_FALSE(timer->hasExpired()); 177 178 expectSetEnabled(source::Enabled::Off); 179 timer->setEnabled(false); 180 EXPECT_FALSE(timer->hasExpired()); 181 // Value should always be passed through regardless of current state 182 expectSetEnabled(source::Enabled::Off); 183 timer->setEnabled(false); 184 EXPECT_FALSE(timer->hasExpired()); 185 } 186 187 TEST_F(TimerTest, SetRemaining) 188 { 189 const milliseconds now(90), remaining(30); 190 expectNow(now); 191 expectSetTime(now + remaining); 192 timer->setRemaining(remaining); 193 EXPECT_EQ(interval, timer->getInterval()); 194 EXPECT_FALSE(timer->hasExpired()); 195 } 196 197 TEST_F(TimerTest, ResetRemaining) 198 { 199 const milliseconds now(90); 200 expectNow(now); 201 expectSetTime(now + interval); 202 timer->resetRemaining(); 203 EXPECT_EQ(interval, timer->getInterval()); 204 EXPECT_FALSE(timer->hasExpired()); 205 } 206 207 TEST_F(TimerTest, SetInterval) 208 { 209 const milliseconds new_interval(40); 210 timer->setInterval(new_interval); 211 EXPECT_EQ(new_interval, timer->getInterval()); 212 EXPECT_FALSE(timer->hasExpired()); 213 } 214 215 TEST_F(TimerTest, SetValuesExpiredTimer) 216 { 217 const milliseconds new_time(90); 218 expectNow(new_time); 219 expectSetTime(new_time + interval); 220 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata)); 221 EXPECT_TRUE(timer->hasExpired()); 222 EXPECT_EQ(interval, timer->getInterval()); 223 224 // Timer should remain expired unless clearExpired() or reset() 225 expectSetEnabled(source::Enabled::On); 226 timer->setEnabled(true); 227 EXPECT_TRUE(timer->hasExpired()); 228 expectNow(milliseconds(20)); 229 expectSetTime(milliseconds(50)); 230 timer->setRemaining(milliseconds(30)); 231 EXPECT_TRUE(timer->hasExpired()); 232 timer->setInterval(milliseconds(10)); 233 EXPECT_TRUE(timer->hasExpired()); 234 expectNow(milliseconds(20)); 235 expectSetTime(milliseconds(30)); 236 timer->resetRemaining(); 237 EXPECT_TRUE(timer->hasExpired()); 238 239 timer->clearExpired(); 240 EXPECT_FALSE(timer->hasExpired()); 241 } 242 243 TEST_F(TimerTest, Restart) 244 { 245 const milliseconds new_time(90); 246 expectNow(new_time); 247 expectSetTime(new_time + interval); 248 EXPECT_EQ(0, handler(nullptr, 0, handler_userdata)); 249 EXPECT_TRUE(timer->hasExpired()); 250 EXPECT_EQ(interval, timer->getInterval()); 251 252 const milliseconds new_interval(471); 253 expectNow(starting_time); 254 expectSetTime(starting_time + new_interval); 255 expectSetEnabled(source::Enabled::On); 256 timer->restart(new_interval); 257 EXPECT_FALSE(timer->hasExpired()); 258 EXPECT_EQ(new_interval, timer->getInterval()); 259 } 260 261 } // namespace 262 } // namespace utility 263 } // namespace sdeventplus 264