source/base: Implement moving
diff --git a/src/sdeventplus/source/base.cpp b/src/sdeventplus/source/base.cpp
index 684d688..94a5628 100644
--- a/src/sdeventplus/source/base.cpp
+++ b/src/sdeventplus/source/base.cpp
@@ -172,5 +172,23 @@
{
}
+Base& Base::operator=(Base&& other)
+{
+ if (this != &other)
+ {
+ // We need to make sure our current event is not triggered
+ // after it gets deleted in the move
+ if (source)
+ {
+ set_enabled(SD_EVENT_OFF);
+ }
+
+ event = std::move(other.event);
+ source = std::move(other.source);
+ prepare = std::move(other.prepare);
+ }
+ return *this;
+}
+
} // namespace source
} // namespace sdeventplus
diff --git a/src/sdeventplus/source/base.hpp b/src/sdeventplus/source/base.hpp
index 0a8acc1..d576b24 100644
--- a/src/sdeventplus/source/base.hpp
+++ b/src/sdeventplus/source/base.hpp
@@ -19,12 +19,6 @@
virtual ~Base();
- // We don't want to allow any kind of slicing.
- Base(const Base& source) = delete;
- Base& operator=(const Base& source) = delete;
- Base(Base&& source) = delete;
- Base& operator=(Base&& source) = delete;
-
int prepareCallback();
sd_event_source* get() const;
@@ -41,13 +35,21 @@
void set_enabled(int enabled) const;
protected:
- const Event event;
- const internal::SdRef<sd_event_source> source;
+ Event event;
+ internal::SdRef<sd_event_source> source;
// Base sources cannot be directly constructed.
Base(const Event& event, sd_event_source* source);
Base(const Event& event, sd_event_source* source, std::false_type);
+ // We can't ever copy an event_source because the callback
+ // data has to be unique.
+ Base(const Base& other) = delete;
+ Base& operator=(const Base& other) = delete;
+ // We don't want to allow any kind of slicing.
+ Base(Base&& other) = default;
+ Base& operator=(Base&& other);
+
private:
Callback prepare;
};
diff --git a/test/source/base.cpp b/test/source/base.cpp
index 4b89d52..a649166 100644
--- a/test/source/base.cpp
+++ b/test/source/base.cpp
@@ -11,6 +11,7 @@
#include <system_error>
#include <systemd/sd-event.h>
#include <type_traits>
+#include <utility>
namespace sdeventplus
{
@@ -115,6 +116,167 @@
EXPECT_CALL(mock, sd_event_unref(expected_event)).WillOnce(Return(nullptr));
}
+TEST_F(BaseTest, MoveConstruct)
+{
+ EXPECT_CALL(mock, sd_event_ref(expected_event))
+ .WillOnce(Return(expected_event));
+ BaseImpl source(*event, expected_source, std::false_type());
+ EXPECT_EQ(expected_source, source.get());
+ EXPECT_EQ(expected_event, source.get_event().get());
+ EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
+ .WillOnce(Return(0));
+ source.set_prepare([](Base&) {});
+ EXPECT_TRUE(source.get_prepare());
+
+ BaseImpl source2(std::move(source));
+ EXPECT_EQ(nullptr, source.get());
+ EXPECT_EQ(nullptr, source.get_event().get());
+ EXPECT_FALSE(source.get_prepare());
+ EXPECT_EQ(expected_source, source2.get());
+ EXPECT_EQ(expected_event, source2.get_event().get());
+ EXPECT_TRUE(source2.get_prepare());
+
+ {
+ testing::InSequence seq;
+ EXPECT_CALL(mock,
+ sd_event_source_set_enabled(expected_source, SD_EVENT_OFF))
+ .WillOnce(Return(0));
+ EXPECT_CALL(mock, sd_event_source_unref(expected_source))
+ .WillOnce(Return(nullptr));
+ }
+ EXPECT_CALL(mock, sd_event_unref(expected_event)).WillOnce(Return(nullptr));
+}
+
+TEST_F(BaseTest, MoveAssignSelf)
+{
+ EXPECT_CALL(mock, sd_event_ref(expected_event))
+ .WillOnce(Return(expected_event));
+ BaseImpl source(*event, expected_source, std::false_type());
+ EXPECT_EQ(expected_source, source.get());
+ EXPECT_EQ(expected_event, source.get_event().get());
+ EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
+ .WillOnce(Return(0));
+ source.set_prepare([](Base&) {});
+ EXPECT_TRUE(source.get_prepare());
+
+ source = std::move(source);
+ EXPECT_EQ(expected_source, source.get());
+ EXPECT_EQ(expected_event, source.get_event().get());
+ EXPECT_TRUE(source.get_prepare());
+
+ {
+ testing::InSequence seq;
+ EXPECT_CALL(mock,
+ sd_event_source_set_enabled(expected_source, SD_EVENT_OFF))
+ .WillOnce(Return(0));
+ EXPECT_CALL(mock, sd_event_source_unref(expected_source))
+ .WillOnce(Return(nullptr));
+ }
+ EXPECT_CALL(mock, sd_event_unref(expected_event)).WillOnce(Return(nullptr));
+}
+
+TEST_F(BaseTest, MoveAssignEmpty)
+{
+ EXPECT_CALL(mock, sd_event_ref(expected_event))
+ .WillOnce(Return(expected_event));
+ BaseImpl source(*event, expected_source, std::false_type());
+ EXPECT_EQ(expected_source, source.get());
+ EXPECT_EQ(expected_event, source.get_event().get());
+ EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
+ .WillOnce(Return(0));
+ source.set_prepare([](Base&) {});
+ EXPECT_TRUE(source.get_prepare());
+
+ sd_event* const expected_event2 = reinterpret_cast<sd_event*>(1);
+ Event event2(expected_event2, std::false_type(), &mock);
+ EXPECT_CALL(mock, sd_event_ref(expected_event2))
+ .WillOnce(Return(expected_event2));
+ BaseImpl source2(event2, nullptr, std::false_type());
+ EXPECT_EQ(nullptr, source2.get());
+ EXPECT_EQ(expected_event2, source2.get_event().get());
+ EXPECT_FALSE(source2.get_prepare());
+
+ EXPECT_CALL(mock, sd_event_unref(expected_event2))
+ .WillOnce(Return(nullptr));
+ EXPECT_CALL(mock, sd_event_source_unref(nullptr)).WillOnce(Return(nullptr));
+ source2 = std::move(source);
+ EXPECT_EQ(nullptr, source.get());
+ EXPECT_EQ(nullptr, source.get_event().get());
+ EXPECT_FALSE(source.get_prepare());
+ EXPECT_EQ(expected_source, source2.get());
+ EXPECT_EQ(expected_event, source2.get_event().get());
+ EXPECT_TRUE(source2.get_prepare());
+
+ {
+ testing::InSequence seq;
+ EXPECT_CALL(mock,
+ sd_event_source_set_enabled(expected_source, SD_EVENT_OFF))
+ .WillOnce(Return(0));
+ EXPECT_CALL(mock, sd_event_source_unref(expected_source))
+ .WillOnce(Return(nullptr));
+ }
+ EXPECT_CALL(mock, sd_event_unref(expected_event)).WillOnce(Return(nullptr));
+ EXPECT_CALL(mock, sd_event_unref(expected_event2))
+ .WillOnce(Return(nullptr));
+}
+
+TEST_F(BaseTest, MoveAssignExisting)
+{
+ EXPECT_CALL(mock, sd_event_ref(expected_event))
+ .WillOnce(Return(expected_event));
+ BaseImpl source(*event, expected_source, std::false_type());
+ EXPECT_EQ(expected_source, source.get());
+ EXPECT_EQ(expected_event, source.get_event().get());
+ EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source, testing::_))
+ .WillOnce(Return(0));
+ source.set_prepare([](Base&) {});
+ EXPECT_TRUE(source.get_prepare());
+
+ sd_event* const expected_event2 = reinterpret_cast<sd_event*>(1);
+ sd_event_source* const expected_source2 =
+ reinterpret_cast<sd_event_source*>(2);
+ Event event2(expected_event2, std::false_type(), &mock);
+ EXPECT_CALL(mock, sd_event_ref(expected_event2))
+ .WillOnce(Return(expected_event2));
+ BaseImpl source2(event2, expected_source2, std::false_type());
+ EXPECT_EQ(expected_source2, source2.get());
+ EXPECT_EQ(expected_event2, source2.get_event().get());
+ EXPECT_CALL(mock, sd_event_source_set_prepare(expected_source2, testing::_))
+ .WillOnce(Return(0));
+ source2.set_prepare([](Base&) {});
+ EXPECT_TRUE(source2.get_prepare());
+
+ {
+ testing::InSequence seq;
+ EXPECT_CALL(mock,
+ sd_event_source_set_enabled(expected_source2, SD_EVENT_OFF))
+ .WillOnce(Return(0));
+ EXPECT_CALL(mock, sd_event_source_unref(expected_source2))
+ .WillOnce(Return(nullptr));
+ }
+ EXPECT_CALL(mock, sd_event_unref(expected_event2))
+ .WillOnce(Return(nullptr));
+ source2 = std::move(source);
+ EXPECT_EQ(nullptr, source.get());
+ EXPECT_EQ(nullptr, source.get_event().get());
+ EXPECT_FALSE(source.get_prepare());
+ EXPECT_EQ(expected_source, source2.get());
+ EXPECT_EQ(expected_event, source2.get_event().get());
+ EXPECT_TRUE(source2.get_prepare());
+
+ {
+ testing::InSequence seq;
+ EXPECT_CALL(mock,
+ sd_event_source_set_enabled(expected_source, SD_EVENT_OFF))
+ .WillOnce(Return(0));
+ EXPECT_CALL(mock, sd_event_source_unref(expected_source))
+ .WillOnce(Return(nullptr));
+ }
+ EXPECT_CALL(mock, sd_event_unref(expected_event)).WillOnce(Return(nullptr));
+ EXPECT_CALL(mock, sd_event_unref(expected_event2))
+ .WillOnce(Return(nullptr));
+}
+
class BaseMethodTest : public BaseTest
{
protected: