test/event: Implement
diff --git a/.gitignore b/.gitignore
index 707eb18..b990003 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,5 +43,6 @@
# Output binaries
/example/pulse
/test/chip
+/test/event
/test/handle
/test/internal_fd
diff --git a/test/Makefile.am b/test/Makefile.am
index abf37c1..4370a4d 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -12,6 +12,11 @@
chip_CPPFLAGS = $(gtest_cppflags)
chip_LDADD = $(gtest_ldadd)
+check_PROGRAMS += event
+event_SOURCES = event.cpp
+event_CPPFLAGS = $(gtest_cppflags)
+event_LDADD = $(gtest_ldadd)
+
check_PROGRAMS += handle
handle_SOURCES = handle.cpp
handle_CPPFLAGS = $(gtest_cppflags)
diff --git a/test/event.cpp b/test/event.cpp
new file mode 100644
index 0000000..fa2337e
--- /dev/null
+++ b/test/event.cpp
@@ -0,0 +1,188 @@
+#include <cerrno>
+#include <cstring>
+#include <gmock/gmock.h>
+#include <gpioplus/event.hpp>
+#include <gpioplus/test/sys.hpp>
+#include <gtest/gtest.h>
+#include <linux/gpio.h>
+#include <memory>
+#include <optional>
+#include <stdexcept>
+#include <string>
+#include <system_error>
+
+namespace gpioplus
+{
+namespace
+{
+
+using testing::Assign;
+using testing::DoAll;
+using testing::Return;
+using testing::SaveArgPointee;
+using testing::SetArgPointee;
+using testing::WithArg;
+
+TEST(EventFlags, EventFlagsToInt)
+{
+ EventFlags event_flags;
+ event_flags.rising_edge = true;
+ event_flags.falling_edge = true;
+ EXPECT_EQ(GPIOEVENT_REQUEST_RISING_EDGE | GPIOEVENT_REQUEST_FALLING_EDGE,
+ event_flags.toInt());
+
+ event_flags.rising_edge = false;
+ event_flags.falling_edge = false;
+ EXPECT_EQ(0, event_flags.toInt());
+}
+
+class EventTest : public testing::Test
+{
+ protected:
+ const int chip_fd = 1234;
+ const int event_fd = 2345;
+ testing::StrictMock<test::SysMock> mock;
+ std::unique_ptr<Chip> chip;
+
+ void SetUp()
+ {
+ EXPECT_CALL(mock, open(testing::_, testing::_))
+ .WillOnce(Return(chip_fd));
+ chip = std::make_unique<Chip>(0, &mock);
+ }
+
+ void TearDown()
+ {
+ EXPECT_CALL(mock, close(chip_fd)).WillOnce(Return(0));
+ chip.reset();
+ }
+};
+
+TEST_F(EventTest, ConstructSuccess)
+{
+ const uint32_t line_offset = 3;
+ const std::string label{"test"};
+ HandleFlags handle_flags(LineFlags(0));
+ EventFlags event_flags;
+ event_flags.rising_edge = true;
+ event_flags.falling_edge = false;
+
+ struct gpioevent_request req, ret;
+ ret.fd = event_fd;
+ EXPECT_CALL(mock, gpio_get_lineevent(chip_fd, testing::_))
+ .WillOnce(
+ DoAll(SaveArgPointee<1>(&req), SetArgPointee<1>(ret), Return(0)));
+ Event event(*chip, line_offset, handle_flags, event_flags, label.c_str());
+
+ EXPECT_EQ(event_fd, *event.getFd());
+ EXPECT_EQ(line_offset, req.lineoffset);
+ EXPECT_EQ(GPIOHANDLE_REQUEST_INPUT, req.handleflags);
+ EXPECT_EQ(GPIOEVENT_REQUEST_RISING_EDGE, req.eventflags);
+ EXPECT_EQ(label, req.consumer_label);
+
+ EXPECT_CALL(mock, close(event_fd)).WillOnce(Return(0));
+}
+
+TEST_F(EventTest, ConstructFailure)
+{
+ const uint32_t line_offset = 3;
+ const std::string label{"test"};
+ HandleFlags handle_flags(LineFlags(0));
+ EventFlags event_flags;
+ event_flags.rising_edge = false;
+ event_flags.falling_edge = false;
+
+ struct gpioevent_request req;
+ EXPECT_CALL(mock, gpio_get_lineevent(chip_fd, testing::_))
+ .WillOnce(DoAll(SaveArgPointee<1>(&req), Return(-EINVAL)));
+ EXPECT_THROW(
+ Event(*chip, line_offset, handle_flags, event_flags, label.c_str()),
+ std::system_error);
+
+ EXPECT_EQ(line_offset, req.lineoffset);
+ EXPECT_EQ(GPIOHANDLE_REQUEST_INPUT, req.handleflags);
+ EXPECT_EQ(0, req.eventflags);
+ EXPECT_EQ(label, req.consumer_label);
+}
+
+class EventMethodTest : public EventTest
+{
+ protected:
+ std::unique_ptr<Event> event;
+
+ void SetUp()
+ {
+ EventTest::SetUp();
+ struct gpioevent_request ret;
+ ret.fd = event_fd;
+ EXPECT_CALL(mock, gpio_get_lineevent(chip_fd, testing::_))
+ .WillOnce(DoAll(SetArgPointee<1>(ret), Return(0)));
+ event = std::make_unique<Event>(*chip, 0, HandleFlags(LineFlags(0)),
+ EventFlags(), "method");
+ }
+
+ void TearDown()
+ {
+ EXPECT_CALL(mock, close(event_fd)).WillOnce(Return(0));
+ event.reset();
+ EventTest::TearDown();
+ }
+};
+
+ACTION_P(WriteStruct, data)
+{
+ memcpy(arg0, &data, sizeof(data));
+}
+
+TEST_F(EventMethodTest, ReadSuccess)
+{
+ struct gpioevent_data ret;
+ ret.timestamp = 5;
+ ret.id = 15;
+ EXPECT_CALL(mock, read(event_fd, testing::_, sizeof(struct gpioevent_data)))
+ .WillOnce(DoAll(WithArg<1>(WriteStruct(ret)), Return(sizeof(ret))));
+ std::optional<Event::Data> data = event->read();
+ EXPECT_TRUE(data);
+ EXPECT_EQ(ret.timestamp, data->timestamp);
+ EXPECT_EQ(ret.id, data->id);
+}
+
+TEST_F(EventMethodTest, ReadAgain)
+{
+ EXPECT_CALL(mock, read(event_fd, testing::_, sizeof(struct gpioevent_data)))
+ .WillOnce(DoAll(Assign(&errno, EAGAIN), Return(-1)));
+ EXPECT_EQ(std::nullopt, event->read());
+}
+
+TEST_F(EventMethodTest, ReadFailure)
+{
+ EXPECT_CALL(mock, read(event_fd, testing::_, sizeof(struct gpioevent_data)))
+ .WillOnce(DoAll(Assign(&errno, EINVAL), Return(-1)));
+ EXPECT_THROW(event->read(), std::system_error);
+}
+
+TEST_F(EventMethodTest, ReadTooSmall)
+{
+ EXPECT_CALL(mock, read(event_fd, testing::_, sizeof(struct gpioevent_data)))
+ .WillOnce(Return(1));
+ EXPECT_THROW(event->read(), std::runtime_error);
+}
+
+TEST_F(EventMethodTest, GetValueSuccess)
+{
+ struct gpiohandle_data data;
+ data.values[0] = 1;
+ EXPECT_CALL(mock, gpiohandle_get_line_values(event_fd, testing::_))
+ .WillOnce(DoAll(SetArgPointee<1>(data), Return(0)));
+ EXPECT_EQ(data.values[0], event->getValue());
+}
+
+TEST_F(EventMethodTest, GetValueFailure)
+{
+ EXPECT_CALL(mock, gpiohandle_get_line_values(event_fd, testing::_))
+ .WillOnce(Return(-EINVAL));
+ EXPECT_THROW(event->getValue(), std::system_error);
+}
+
+} // namespace
+} // namespace gpioplus