1 #include <systemd/sd-event.h> 2 #include <time.h> 3 4 #include <sdeventplus/clock.hpp> 5 #include <sdeventplus/exception.hpp> 6 #include <sdeventplus/source/time.hpp> 7 #include <sdeventplus/test/sdevent.hpp> 8 9 #include <cerrno> 10 #include <chrono> 11 #include <functional> 12 #include <memory> 13 #include <utility> 14 15 #include <gmock/gmock.h> 16 #include <gtest/gtest.h> 17 18 namespace sdeventplus 19 { 20 namespace source 21 { 22 namespace 23 { 24 25 using testing::DoAll; 26 using testing::Return; 27 using testing::ReturnPointee; 28 using testing::SaveArg; 29 using testing::SetArgPointee; 30 31 using UniqueEvent = std::unique_ptr<Event, std::function<void(Event*)>>; 32 33 class TimeTest : public testing::Test 34 { 35 protected: 36 testing::StrictMock<test::SdEventMock> mock; 37 sd_event_source* const expected_source = 38 reinterpret_cast<sd_event_source*>(1234); 39 sd_event* const expected_event = reinterpret_cast<sd_event*>(2345); 40 UniqueEvent event = make_event(expected_event); 41 42 UniqueEvent make_event(sd_event* event) 43 { 44 auto deleter = [this, event](Event* e) { 45 EXPECT_CALL(this->mock, sd_event_unref(event)) 46 .WillOnce(Return(nullptr)); 47 delete e; 48 }; 49 return UniqueEvent(new Event(event, std::false_type(), &mock), deleter); 50 } 51 52 void expect_time_destroy(sd_event* event, sd_event_source* source) 53 { 54 EXPECT_CALL(mock, sd_event_source_unref(source)) 55 .WillOnce(Return(nullptr)); 56 EXPECT_CALL(mock, sd_event_unref(event)).WillOnce(Return(nullptr)); 57 } 58 }; 59 60 TEST_F(TimeTest, ConstructSuccess) 61 { 62 constexpr ClockId id = ClockId::RealTime; 63 const Time<id>::TimePoint expected_time(std::chrono::seconds{2}); 64 const Time<id>::Accuracy expected_accuracy(std::chrono::milliseconds{50}); 65 Time<id>::TimePoint saved_time; 66 Time<id>::Callback callback = [&saved_time](Time<id>&, 67 Time<id>::TimePoint time) { 68 saved_time = time; 69 }; 70 71 EXPECT_CALL(mock, sd_event_ref(expected_event)) 72 .WillOnce(Return(expected_event)); 73 sd_event_time_handler_t handler; 74 EXPECT_CALL(mock, 75 sd_event_add_time(expected_event, testing::_, CLOCK_REALTIME, 76 2000000, 50000, testing::_, nullptr)) 77 .WillOnce(DoAll(SetArgPointee<1>(expected_source), SaveArg<5>(&handler), 78 Return(0))); 79 sd_event_destroy_t destroy; 80 void* userdata; 81 { 82 testing::InSequence seq; 83 EXPECT_CALL(mock, sd_event_source_set_destroy_callback(expected_source, 84 testing::_)) 85 .WillOnce(DoAll(SaveArg<1>(&destroy), Return(0))); 86 EXPECT_CALL(mock, 87 sd_event_source_set_userdata(expected_source, testing::_)) 88 .WillOnce(DoAll(SaveArg<1>(&userdata), Return(nullptr))); 89 EXPECT_CALL(mock, sd_event_source_get_userdata(expected_source)) 90 .WillRepeatedly(ReturnPointee(&userdata)); 91 } 92 Time<id> time(*event, expected_time, expected_accuracy, 93 std::move(callback)); 94 EXPECT_FALSE(callback); 95 EXPECT_NE(&time, userdata); 96 EXPECT_EQ(expected_event, time.get_event().get()); 97 EXPECT_EQ(expected_source, time.get()); 98 99 EXPECT_EQ(0, handler(nullptr, 2000100, userdata)); 100 EXPECT_EQ(Time<id>::TimePoint(std::chrono::microseconds(2000100)), 101 saved_time); 102 103 time.set_callback(std::bind([]() {})); 104 EXPECT_EQ(0, handler(nullptr, 0, userdata)); 105 EXPECT_EQ(Time<id>::TimePoint(std::chrono::microseconds(2000100)), 106 saved_time); 107 108 expect_time_destroy(expected_event, expected_source); 109 destroy(userdata); 110 } 111 112 TEST_F(TimeTest, ConstructError) 113 { 114 constexpr ClockId id = ClockId::Monotonic; 115 const Time<id>::TimePoint expected_time(std::chrono::seconds{2}); 116 const Time<id>::Accuracy expected_accuracy(std::chrono::milliseconds{50}); 117 Time<id>::Callback callback = [](Time<id>&, Time<id>::TimePoint) {}; 118 119 EXPECT_CALL(mock, 120 sd_event_add_time(expected_event, testing::_, CLOCK_MONOTONIC, 121 2000000, 50000, testing::_, nullptr)) 122 .WillOnce(Return(-ENOSYS)); 123 EXPECT_THROW( 124 Time<id>(*event, expected_time, expected_accuracy, std::move(callback)), 125 SdEventError); 126 EXPECT_TRUE(callback); 127 } 128 129 class TimeMethodTest : public TimeTest 130 { 131 protected: 132 static constexpr ClockId id = ClockId::BootTime; 133 std::unique_ptr<Time<id>> time; 134 sd_event_destroy_t destroy; 135 void* userdata; 136 137 void SetUp() 138 { 139 EXPECT_CALL(mock, sd_event_ref(expected_event)) 140 .WillOnce(Return(expected_event)); 141 EXPECT_CALL(mock, sd_event_add_time(expected_event, testing::_, 142 CLOCK_BOOTTIME, 2000000, 50000, 143 testing::_, nullptr)) 144 .WillOnce(DoAll(SetArgPointee<1>(expected_source), Return(0))); 145 { 146 testing::InSequence seq; 147 EXPECT_CALL(mock, sd_event_source_set_destroy_callback( 148 expected_source, testing::_)) 149 .WillOnce(DoAll(SaveArg<1>(&destroy), Return(0))); 150 EXPECT_CALL( 151 mock, sd_event_source_set_userdata(expected_source, testing::_)) 152 .WillOnce(DoAll(SaveArg<1>(&userdata), Return(nullptr))); 153 EXPECT_CALL(mock, sd_event_source_get_userdata(expected_source)) 154 .WillRepeatedly(ReturnPointee(&userdata)); 155 } 156 time = std::make_unique<Time<id>>( 157 *event, Time<id>::TimePoint(std::chrono::seconds{2}), 158 std::chrono::milliseconds{50}, 159 [](Time<id>&, Time<id>::TimePoint) {}); 160 } 161 162 void TearDown() 163 { 164 expect_time_destroy(expected_event, expected_source); 165 time.reset(); 166 destroy(userdata); 167 } 168 }; 169 170 TEST_F(TimeMethodTest, Copy) 171 { 172 EXPECT_CALL(mock, sd_event_ref(expected_event)) 173 .WillOnce(Return(expected_event)); 174 EXPECT_CALL(mock, sd_event_source_ref(expected_source)) 175 .WillOnce(Return(expected_source)); 176 auto time2 = std::make_unique<Time<id>>(*time); 177 { 178 EXPECT_CALL(mock, sd_event_ref(expected_event)) 179 .WillOnce(Return(expected_event)); 180 EXPECT_CALL(mock, sd_event_source_ref(expected_source)) 181 .WillOnce(Return(expected_source)); 182 Time<id> time3(*time); 183 184 expect_time_destroy(expected_event, expected_source); 185 EXPECT_CALL(mock, sd_event_ref(expected_event)) 186 .WillOnce(Return(expected_event)); 187 EXPECT_CALL(mock, sd_event_source_ref(expected_source)) 188 .WillOnce(Return(expected_source)); 189 *time2 = time3; 190 191 expect_time_destroy(expected_event, expected_source); 192 } 193 194 // Delete the original time 195 time2.swap(time); 196 expect_time_destroy(expected_event, expected_source); 197 time2.reset(); 198 199 // Make sure our new copy can still access data 200 time->set_callback(nullptr); 201 } 202 203 TEST_F(TimeMethodTest, SetTimeSuccess) 204 { 205 EXPECT_CALL(mock, sd_event_source_set_time(expected_source, 1000000)) 206 .WillOnce(Return(0)); 207 time->set_time(Time<id>::TimePoint(std::chrono::seconds{1})); 208 } 209 210 TEST_F(TimeMethodTest, SetTimeError) 211 { 212 EXPECT_CALL(mock, sd_event_source_set_time(expected_source, 1000000)) 213 .WillOnce(Return(-EINVAL)); 214 EXPECT_THROW(time->set_time(Time<id>::TimePoint(std::chrono::seconds{1})), 215 SdEventError); 216 } 217 218 TEST_F(TimeMethodTest, GetTimeSuccess) 219 { 220 EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_)) 221 .WillOnce(DoAll(SetArgPointee<1>(10), Return(0))); 222 EXPECT_EQ(Time<id>::TimePoint(std::chrono::microseconds{10}), 223 time->get_time()); 224 } 225 226 TEST_F(TimeMethodTest, GetTimeError) 227 { 228 EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_)) 229 .WillOnce(Return(-ENOSYS)); 230 EXPECT_THROW(time->get_time(), SdEventError); 231 } 232 233 TEST_F(TimeMethodTest, SetAccuracySuccess) 234 { 235 EXPECT_CALL(mock, 236 sd_event_source_set_time_accuracy(expected_source, 5000000)) 237 .WillOnce(Return(0)); 238 time->set_accuracy(std::chrono::seconds{5}); 239 } 240 241 TEST_F(TimeMethodTest, SetAccuracyError) 242 { 243 EXPECT_CALL(mock, 244 sd_event_source_set_time_accuracy(expected_source, 5000000)) 245 .WillOnce(Return(-EINVAL)); 246 EXPECT_THROW(time->set_accuracy(std::chrono::seconds{5}), SdEventError); 247 } 248 249 TEST_F(TimeMethodTest, GetAccuracySuccess) 250 { 251 EXPECT_CALL(mock, 252 sd_event_source_get_time_accuracy(expected_source, testing::_)) 253 .WillOnce(DoAll(SetArgPointee<1>(1000), Return(0))); 254 EXPECT_EQ(std::chrono::milliseconds{1}, time->get_accuracy()); 255 } 256 257 TEST_F(TimeMethodTest, GetAccuracyError) 258 { 259 EXPECT_CALL(mock, 260 sd_event_source_get_time_accuracy(expected_source, testing::_)) 261 .WillOnce(Return(-ENOSYS)); 262 EXPECT_THROW(time->get_accuracy(), SdEventError); 263 } 264 265 } // namespace 266 } // namespace source 267 } // namespace sdeventplus 268