blob: f1925461d6c8b241d3192981cf57e67e968547ea [file] [log] [blame]
#include <systemd/sd-event.h>
#include <sdeventplus/event.hpp>
#include <sdeventplus/exception.hpp>
#include <sdeventplus/internal/sdevent.hpp>
#include <sdeventplus/source/base.hpp>
#include <sdeventplus/test/sdevent.hpp>
#include <sdeventplus/types.hpp>
#include <cerrno>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <type_traits>
#include <utility>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace sdeventplus
{
namespace source
{
namespace
{
using testing::DoAll;
using testing::Return;
using testing::SaveArg;
using testing::SetArgPointee;
using UniqueEvent = std::unique_ptr<Event, std::function<void(Event*)>>;
class BaseImplData;
class BaseImpl : public Base
{
public:
BaseImpl(const Event& event, sd_event_source* source, std::false_type);
BaseImpl(const BaseImpl& other, sdeventplus::internal::NoOwn) :
Base(other, sdeventplus::internal::NoOwn())
{}
using Base::get_prepare;
};
class BaseImplData : public BaseImpl, public detail::BaseData
{
public:
BaseImplData(const BaseImpl& base) :
BaseImpl(base, sdeventplus::internal::NoOwn()), BaseData(base)
{}
};
BaseImpl::BaseImpl(const Event& event, sd_event_source* source,
std::false_type) :
Base(event, source, std::false_type())
{
set_userdata(std::make_unique<BaseImplData>(*this));
}
class BaseTest : public testing::Test
{
protected:
testing::StrictMock<test::SdEventMock> mock;
sd_event_source* const expected_source =
reinterpret_cast<sd_event_source*>(1234);
sd_event_source* const expected_source2 =
reinterpret_cast<sd_event_source*>(3456);
sd_event* const expected_event = reinterpret_cast<sd_event*>(2345);
sd_event* const expected_event2 = reinterpret_cast<sd_event*>(4567);
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);
}
// Using a unique_ptr to make sure we don't get any superfluous moves or
// copies.
std::tuple<std::unique_ptr<BaseImpl>, std::function<void()>>
make_base(const Event& event, sd_event_source* source)
{
EXPECT_CALL(mock, sd_event_ref(event.get()))
.WillOnce(Return(event.get()));
sd_event_destroy_t destroy;
void* userdata;
{
testing::InSequence seq;
EXPECT_CALL(
mock, sd_event_source_set_destroy_callback(source, testing::_))
.WillOnce(DoAll(SaveArg<1>(&destroy), Return(0)));
EXPECT_CALL(mock, sd_event_source_set_userdata(source, testing::_))
.WillOnce(DoAll(SaveArg<1>(&userdata), Return(nullptr)));
}
auto ret = std::make_unique<BaseImpl>(event, source, std::false_type());
EXPECT_CALL(mock, sd_event_source_get_userdata(source))
.WillRepeatedly(Return(userdata));
EXPECT_NE(ret.get(), userdata);
EXPECT_EQ(source, ret->get());
EXPECT_NE(&event, &ret->get_event());
EXPECT_EQ(event.get(), ret->get_event().get());
EXPECT_FALSE(ret->get_prepare());
return {std::move(ret), std::bind(destroy, userdata)};
}
void set_prepare_placeholder(BaseImpl& base)
{
EXPECT_CALL(mock, sd_event_source_set_prepare(base.get(), testing::_))
.WillOnce(Return(0));
base.set_prepare([](Base&) {});
EXPECT_TRUE(base.get_prepare());
}
void empty_base(BaseImpl&& other)
{
BaseImpl mover(std::move(other));
EXPECT_THROW(other.get(), std::bad_optional_access);
EXPECT_THROW(other.get_event().get(), std::bad_optional_access);
EXPECT_THROW(other.get_prepare(), std::bad_optional_access);
expect_base_destruct(mover.get_event(), mover.get());
}
void expect_base_destruct(const Event& event, sd_event_source* source)
{
EXPECT_CALL(mock, sd_event_source_unref(source))
.WillOnce(Return(nullptr));
EXPECT_CALL(mock, sd_event_unref(event.get()))
.WillOnce(Return(nullptr));
}
};
TEST_F(BaseTest, NewBaseFail)
{
EXPECT_CALL(mock, sd_event_ref(expected_event))
.WillOnce(Return(expected_event));
EXPECT_CALL(
mock, sd_event_source_set_destroy_callback(expected_source, testing::_))
.WillOnce(Return(-EINVAL));
expect_base_destruct(*event, expected_source);
EXPECT_THROW(BaseImpl(*event, expected_source, std::false_type()),
SdEventError);
}
TEST_F(BaseTest, NewBaseNoRef)
{
EXPECT_CALL(mock, sd_event_ref(expected_event))
.WillOnce(Return(expected_event));
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)));
}
BaseImpl source(*event, expected_source, std::false_type());
EXPECT_NE(&source, userdata);
EXPECT_EQ(expected_source, source.get());
EXPECT_NE(event.get(), &source.get_event());
EXPECT_EQ(expected_event, source.get_event().get());
EXPECT_CALL(mock, sd_event_source_get_userdata(expected_source))
.WillOnce(Return(userdata));
EXPECT_FALSE(source.get_prepare());
expect_base_destruct(*event, expected_source);
destroy(userdata);
}
TEST_F(BaseTest, UserdataOutlives)
{
EXPECT_CALL(mock, sd_event_ref(expected_event))
.WillOnce(Return(expected_event));
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)));
}
auto source = std::make_unique<BaseImpl>(*event, expected_source,
std::false_type());
EXPECT_CALL(mock, sd_event_source_get_userdata(expected_source))
.WillRepeatedly(Return(userdata));
EXPECT_FALSE(source->get_prepare());
expect_base_destruct(*event, expected_source);
source.reset();
EXPECT_FALSE(reinterpret_cast<BaseImpl*>(userdata)->get_prepare());
destroy(userdata);
}
TEST_F(BaseTest, CopyCorrectness)
{
std::unique_ptr<BaseImpl> base1, base2;
std::function<void()> destroy;
std::tie(base1, destroy) = make_base(*event, expected_source);
set_prepare_placeholder(*base1);
EXPECT_TRUE(base1->get_prepare());
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));
base2 = std::make_unique<BaseImpl>(*base1);
EXPECT_EQ(&base1->get_prepare(), &base2->get_prepare());
empty_base(std::move(*base1));
EXPECT_THROW(base1->get_prepare(), std::bad_optional_access);
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));
*base1 = *base2;
EXPECT_EQ(&base1->get_prepare(), &base2->get_prepare());
expect_base_destruct(*event, expected_source);
base2.reset();
expect_base_destruct(*event, expected_source);
base1.reset();
destroy();
}
class BaseMethodTest : public BaseTest
{
protected:
std::unique_ptr<BaseImpl> base;
std::function<void()> destroy;
void SetUp()
{
std::tie(base, destroy) = make_base(*event, expected_source);
}
void TearDown()
{
expect_base_destruct(base->get_event(), base->get());
base.reset();
destroy();
}
};
TEST_F(BaseMethodTest, GetDescriptionSuccess)
{
const char* expected = "test_desc";
EXPECT_CALL(mock,
sd_event_source_get_description(expected_source, testing::_))
.WillOnce(DoAll(SetArgPointee<1>(expected), Return(0)));
// Intentionally comparing pointers to make sure no copying is happening
EXPECT_EQ(expected, base->get_description());
}
TEST_F(BaseMethodTest, GetDescriptionError)
{
EXPECT_CALL(mock,
sd_event_source_get_description(expected_source, testing::_))
.WillOnce(Return(-EINVAL));
EXPECT_THROW(base->get_description(), SdEventError);
}
TEST_F(BaseMethodTest, SetDescriptionSuccess)
{
const char* expected = "test desc";
// Intentionally comparing pointers to make sure no copying is happening
EXPECT_CALL(mock,
sd_event_source_set_description(expected_source, expected))
.WillOnce(Return(0));
base->set_description(expected);
}
TEST_F(BaseMethodTest, SetDescriptionError)
{
const char* expected = "test desc";
// Intentionally comparing pointers to make sure no copying is happening
EXPECT_CALL(mock,
sd_event_source_set_description(expected_source, expected))
.WillOnce(Return(-EINVAL));
EXPECT_THROW(base->set_description(expected), SdEventError);
}
TEST_F(BaseMethodTest, SetPrepareCallback)
{
bool completed = false;
Base::Callback callback = [&completed](Base&) { completed = true; };
sd_event_handler_t event_handler;
EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
.WillOnce(DoAll(SaveArg<1>(&event_handler), Return(0)));
base->set_prepare(std::move(callback));
EXPECT_TRUE(base->get_prepare());
EXPECT_FALSE(callback);
EXPECT_FALSE(completed);
EXPECT_EQ(0, event_handler(nullptr, base.get()));
EXPECT_TRUE(completed);
}
TEST_F(BaseMethodTest, SetPrepareCallbackNoUserdata)
{
Base::Callback callback = [](Base&) {};
sd_event_handler_t event_handler;
EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
.WillOnce(DoAll(SaveArg<1>(&event_handler), Return(0)));
base->set_prepare(std::move(callback));
EXPECT_TRUE(base->get_prepare());
EXPECT_FALSE(callback);
EXPECT_EQ(-EINVAL, event_handler(nullptr, nullptr));
}
TEST_F(BaseMethodTest, SetPrepareError)
{
EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
.WillOnce(Return(0));
base->set_prepare([](Base&) {});
EXPECT_TRUE(base->get_prepare());
Base::Callback callback = [](Base&) {};
EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
.WillOnce(Return(-EINVAL));
EXPECT_THROW(base->set_prepare(std::move(callback)), SdEventError);
EXPECT_FALSE(base->get_prepare());
EXPECT_TRUE(callback);
}
TEST_F(BaseMethodTest, SetPrepareNull)
{
EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
.WillOnce(Return(0));
base->set_prepare([](Base&) {});
EXPECT_TRUE(base->get_prepare());
EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, nullptr))
.WillOnce(Return(0));
base->set_prepare(nullptr);
EXPECT_FALSE(base->get_prepare());
}
TEST_F(BaseMethodTest, GetPendingSuccess)
{
EXPECT_CALL(mock, sd_event_source_get_pending(expected_source))
.WillOnce(Return(0));
EXPECT_FALSE(base->get_pending());
EXPECT_CALL(mock, sd_event_source_get_pending(expected_source))
.WillOnce(Return(4));
EXPECT_TRUE(base->get_pending());
}
TEST_F(BaseMethodTest, GetPendingError)
{
EXPECT_CALL(mock, sd_event_source_get_pending(expected_source))
.WillOnce(Return(-EINVAL));
EXPECT_THROW(base->get_pending(), SdEventError);
}
TEST_F(BaseMethodTest, GetPrioritySuccess)
{
EXPECT_CALL(mock, sd_event_source_get_priority(expected_source, testing::_))
.WillOnce(DoAll(SetArgPointee<1>(1024), Return(0)));
EXPECT_EQ(1024, base->get_priority());
}
TEST_F(BaseMethodTest, GetPriorityError)
{
EXPECT_CALL(mock, sd_event_source_get_priority(expected_source, testing::_))
.WillOnce(Return(-EINVAL));
EXPECT_THROW(base->get_priority(), SdEventError);
}
TEST_F(BaseMethodTest, SetPrioritySuccess)
{
EXPECT_CALL(mock, sd_event_source_set_priority(expected_source, 1024))
.WillOnce(Return(0));
base->set_priority(1024);
}
TEST_F(BaseMethodTest, SetPriorityError)
{
EXPECT_CALL(mock, sd_event_source_set_priority(expected_source, 1024))
.WillOnce(Return(-EINVAL));
EXPECT_THROW(base->set_priority(1024), SdEventError);
}
TEST_F(BaseMethodTest, GetEnabledSuccess)
{
EXPECT_CALL(mock, sd_event_source_get_enabled(expected_source, testing::_))
.WillOnce(DoAll(SetArgPointee<1>(SD_EVENT_ON), Return(0)));
EXPECT_EQ(Enabled::On, base->get_enabled());
}
TEST_F(BaseMethodTest, GetEnabledError)
{
EXPECT_CALL(mock, sd_event_source_get_enabled(expected_source, testing::_))
.WillOnce(Return(-EINVAL));
EXPECT_THROW(base->get_enabled(), SdEventError);
}
TEST_F(BaseMethodTest, SetEnabledSuccess)
{
EXPECT_CALL(mock, sd_event_source_set_enabled(expected_source, SD_EVENT_ON))
.WillOnce(Return(0));
base->set_enabled(Enabled::On);
}
TEST_F(BaseMethodTest, SetEnabledError)
{
EXPECT_CALL(mock,
sd_event_source_set_enabled(expected_source, SD_EVENT_ONESHOT))
.WillOnce(Return(-EINVAL));
EXPECT_THROW(base->set_enabled(Enabled::OneShot), SdEventError);
}
TEST_F(BaseMethodTest, GetFloatingSuccess)
{
EXPECT_CALL(mock, sd_event_source_get_floating(expected_source))
.WillOnce(Return(2));
EXPECT_TRUE(base->get_floating());
EXPECT_CALL(mock, sd_event_source_get_floating(expected_source))
.WillOnce(Return(0));
EXPECT_FALSE(base->get_floating());
}
TEST_F(BaseMethodTest, GetFloatingError)
{
EXPECT_CALL(mock, sd_event_source_get_floating(expected_source))
.WillOnce(Return(-EINVAL));
EXPECT_THROW(base->get_floating(), SdEventError);
}
TEST_F(BaseMethodTest, SetFloatingSuccess)
{
EXPECT_CALL(mock, sd_event_source_set_floating(expected_source, 1))
.WillOnce(Return(0));
base->set_floating(true);
}
TEST_F(BaseMethodTest, SetFloatingError)
{
EXPECT_CALL(mock, sd_event_source_set_floating(expected_source, 1))
.WillOnce(Return(-EINVAL));
EXPECT_THROW(base->set_floating(true), SdEventError);
}
} // namespace
} // namespace source
} // namespace sdeventplus