#include #include #include #include #include #include #include #include #include #include #include #include #include namespace sdeventplus { namespace source { namespace { using testing::DoAll; using testing::Return; using testing::ReturnPointee; using testing::SaveArg; using testing::SetArgPointee; using UniqueEvent = std::unique_ptr>; class TimeTest : public testing::Test { protected: testing::StrictMock mock; sd_event_source* const expected_source = reinterpret_cast(1234); sd_event* const expected_event = reinterpret_cast(2345); UniqueEvent event = make_event(expected_event); UniqueEvent make_event(sd_event* event) { auto deleter = [this, event](Event* e) { EXPECT_CALL(this->mock, sd_event_unref(event)) .WillOnce(Return(nullptr)); delete e; }; return UniqueEvent(new Event(event, std::false_type(), &mock), deleter); } void expect_time_destroy(sd_event* event, sd_event_source* source) { EXPECT_CALL(mock, sd_event_source_unref(source)) .WillOnce(Return(nullptr)); EXPECT_CALL(mock, sd_event_unref(event)).WillOnce(Return(nullptr)); } }; TEST_F(TimeTest, ConstructSuccess) { constexpr ClockId id = ClockId::RealTime; const Time::TimePoint expected_time(std::chrono::seconds{2}); const Time::Accuracy expected_accuracy(std::chrono::milliseconds{50}); Time::TimePoint saved_time; Time::Callback callback = [&saved_time](Time&, Time::TimePoint time) { saved_time = time; }; EXPECT_CALL(mock, sd_event_ref(expected_event)) .WillOnce(Return(expected_event)); sd_event_time_handler_t handler; EXPECT_CALL(mock, sd_event_add_time(expected_event, testing::_, CLOCK_REALTIME, 2000000, 50000, testing::_, nullptr)) .WillOnce(DoAll(SetArgPointee<1>(expected_source), SaveArg<5>(&handler), Return(0))); sd_event_destroy_t destroy; void* userdata; { testing::InSequence seq; EXPECT_CALL(mock, sd_event_source_set_destroy_callback(expected_source, testing::_)) .WillOnce(DoAll(SaveArg<1>(&destroy), Return(0))); EXPECT_CALL(mock, sd_event_source_set_userdata(expected_source, testing::_)) .WillOnce(DoAll(SaveArg<1>(&userdata), Return(nullptr))); EXPECT_CALL(mock, sd_event_source_get_userdata(expected_source)) .WillRepeatedly(ReturnPointee(&userdata)); } Time time(*event, expected_time, expected_accuracy, std::move(callback)); EXPECT_FALSE(callback); EXPECT_NE(&time, userdata); EXPECT_EQ(expected_event, time.get_event().get()); EXPECT_EQ(expected_source, time.get()); EXPECT_EQ(0, handler(nullptr, 2000100, userdata)); EXPECT_EQ(Time::TimePoint(std::chrono::microseconds(2000100)), saved_time); time.set_callback(std::bind([]() {})); EXPECT_EQ(0, handler(nullptr, 0, userdata)); EXPECT_EQ(Time::TimePoint(std::chrono::microseconds(2000100)), saved_time); expect_time_destroy(expected_event, expected_source); destroy(userdata); } TEST_F(TimeTest, ConstructError) { constexpr ClockId id = ClockId::Monotonic; const Time::TimePoint expected_time(std::chrono::seconds{2}); const Time::Accuracy expected_accuracy(std::chrono::milliseconds{50}); Time::Callback callback = [](Time&, Time::TimePoint) {}; EXPECT_CALL(mock, sd_event_add_time(expected_event, testing::_, CLOCK_MONOTONIC, 2000000, 50000, testing::_, nullptr)) .WillOnce(Return(-ENOSYS)); EXPECT_THROW( Time(*event, expected_time, expected_accuracy, std::move(callback)), SdEventError); EXPECT_TRUE(callback); } class TimeMethodTest : public TimeTest { protected: static constexpr ClockId id = ClockId::BootTime; std::unique_ptr> time; sd_event_destroy_t destroy; void* userdata; void SetUp() { EXPECT_CALL(mock, sd_event_ref(expected_event)) .WillOnce(Return(expected_event)); EXPECT_CALL(mock, sd_event_add_time(expected_event, testing::_, CLOCK_BOOTTIME, 2000000, 50000, testing::_, nullptr)) .WillOnce(DoAll(SetArgPointee<1>(expected_source), Return(0))); { testing::InSequence seq; EXPECT_CALL(mock, sd_event_source_set_destroy_callback( expected_source, testing::_)) .WillOnce(DoAll(SaveArg<1>(&destroy), Return(0))); EXPECT_CALL( mock, sd_event_source_set_userdata(expected_source, testing::_)) .WillOnce(DoAll(SaveArg<1>(&userdata), Return(nullptr))); EXPECT_CALL(mock, sd_event_source_get_userdata(expected_source)) .WillRepeatedly(ReturnPointee(&userdata)); } time = std::make_unique>( *event, Time::TimePoint(std::chrono::seconds{2}), std::chrono::milliseconds{50}, [](Time&, Time::TimePoint) {}); } void TearDown() { expect_time_destroy(expected_event, expected_source); time.reset(); destroy(userdata); } }; TEST_F(TimeMethodTest, Copy) { EXPECT_CALL(mock, sd_event_ref(expected_event)) .WillOnce(Return(expected_event)); EXPECT_CALL(mock, sd_event_source_ref(expected_source)) .WillOnce(Return(expected_source)); auto time2 = std::make_unique>(*time); { EXPECT_CALL(mock, sd_event_ref(expected_event)) .WillOnce(Return(expected_event)); EXPECT_CALL(mock, sd_event_source_ref(expected_source)) .WillOnce(Return(expected_source)); Time time3(*time); expect_time_destroy(expected_event, expected_source); EXPECT_CALL(mock, sd_event_ref(expected_event)) .WillOnce(Return(expected_event)); EXPECT_CALL(mock, sd_event_source_ref(expected_source)) .WillOnce(Return(expected_source)); *time2 = time3; expect_time_destroy(expected_event, expected_source); } // Delete the original time time2.swap(time); expect_time_destroy(expected_event, expected_source); time2.reset(); // Make sure our new copy can still access data time->set_callback(nullptr); } TEST_F(TimeMethodTest, SetTimeSuccess) { EXPECT_CALL(mock, sd_event_source_set_time(expected_source, 1000000)) .WillOnce(Return(0)); time->set_time(Time::TimePoint(std::chrono::seconds{1})); } TEST_F(TimeMethodTest, SetTimeError) { EXPECT_CALL(mock, sd_event_source_set_time(expected_source, 1000000)) .WillOnce(Return(-EINVAL)); EXPECT_THROW(time->set_time(Time::TimePoint(std::chrono::seconds{1})), SdEventError); } TEST_F(TimeMethodTest, GetTimeSuccess) { EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_)) .WillOnce(DoAll(SetArgPointee<1>(10), Return(0))); EXPECT_EQ(Time::TimePoint(std::chrono::microseconds{10}), time->get_time()); } TEST_F(TimeMethodTest, GetTimeError) { EXPECT_CALL(mock, sd_event_source_get_time(expected_source, testing::_)) .WillOnce(Return(-ENOSYS)); EXPECT_THROW(time->get_time(), SdEventError); } TEST_F(TimeMethodTest, SetAccuracySuccess) { EXPECT_CALL(mock, sd_event_source_set_time_accuracy(expected_source, 5000000)) .WillOnce(Return(0)); time->set_accuracy(std::chrono::seconds{5}); } TEST_F(TimeMethodTest, SetAccuracyError) { EXPECT_CALL(mock, sd_event_source_set_time_accuracy(expected_source, 5000000)) .WillOnce(Return(-EINVAL)); EXPECT_THROW(time->set_accuracy(std::chrono::seconds{5}), SdEventError); } TEST_F(TimeMethodTest, GetAccuracySuccess) { EXPECT_CALL(mock, sd_event_source_get_time_accuracy(expected_source, testing::_)) .WillOnce(DoAll(SetArgPointee<1>(1000), Return(0))); EXPECT_EQ(std::chrono::milliseconds{1}, time->get_accuracy()); } TEST_F(TimeMethodTest, GetAccuracyError) { EXPECT_CALL(mock, sd_event_source_get_time_accuracy(expected_source, testing::_)) .WillOnce(Return(-ENOSYS)); EXPECT_THROW(time->get_accuracy(), SdEventError); } } // namespace } // namespace source } // namespace sdeventplus