test/handle: Implement
diff --git a/.gitignore b/.gitignore
index 3b68266..707eb18 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,4 +43,5 @@
# Output binaries
/example/pulse
/test/chip
+/test/handle
/test/internal_fd
diff --git a/test/Makefile.am b/test/Makefile.am
index 7451640..abf37c1 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -12,6 +12,11 @@
chip_CPPFLAGS = $(gtest_cppflags)
chip_LDADD = $(gtest_ldadd)
+check_PROGRAMS += handle
+handle_SOURCES = handle.cpp
+handle_CPPFLAGS = $(gtest_cppflags)
+handle_LDADD = $(gtest_ldadd)
+
check_PROGRAMS += internal_fd
internal_fd_SOURCES = internal/fd.cpp
internal_fd_CPPFLAGS = $(gtest_cppflags)
diff --git a/test/handle.cpp b/test/handle.cpp
new file mode 100644
index 0000000..08a66db
--- /dev/null
+++ b/test/handle.cpp
@@ -0,0 +1,227 @@
+#include <cerrno>
+#include <cstdint>
+#include <gmock/gmock.h>
+#include <gpioplus/handle.hpp>
+#include <gpioplus/test/sys.hpp>
+#include <gtest/gtest.h>
+#include <linux/gpio.h>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <system_error>
+#include <vector>
+
+namespace gpioplus
+{
+namespace
+{
+
+using testing::DoAll;
+using testing::Return;
+using testing::SaveArgPointee;
+using testing::SetArgPointee;
+
+TEST(HandleFlags, HandleFlagsFromLineFlags)
+{
+ LineFlags line_flags(GPIOLINE_FLAG_KERNEL | GPIOLINE_FLAG_OPEN_DRAIN);
+ HandleFlags handle_flags(line_flags);
+ EXPECT_FALSE(handle_flags.output);
+ EXPECT_FALSE(handle_flags.active_low);
+ EXPECT_TRUE(handle_flags.open_drain);
+ EXPECT_FALSE(handle_flags.open_source);
+}
+
+TEST(HandleFlags, HandleFlagsToInt)
+{
+ HandleFlags handle_flags;
+ handle_flags.output = false;
+ handle_flags.active_low = true;
+ handle_flags.open_drain = false;
+ handle_flags.open_source = false;
+ EXPECT_EQ(GPIOHANDLE_REQUEST_INPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW,
+ handle_flags.toInt());
+
+ handle_flags.output = true;
+ handle_flags.active_low = false;
+ handle_flags.open_drain = true;
+ handle_flags.open_source = true;
+ EXPECT_EQ(GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_OPEN_DRAIN |
+ GPIOHANDLE_REQUEST_OPEN_SOURCE,
+ handle_flags.toInt());
+}
+
+class HandleTest : public testing::Test
+{
+ protected:
+ const int chip_fd = 1234;
+ const int handle_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(HandleTest, ConstructSuccess)
+{
+ const std::string label{"test"};
+ std::vector<Handle::Line> lines;
+ for (uint32_t i = 0; i < 7; ++i)
+ {
+ lines.push_back({i, 1});
+ }
+
+ struct gpiohandle_request req, ret;
+ ret.fd = handle_fd;
+ EXPECT_CALL(mock, gpio_get_linehandle(chip_fd, testing::_))
+ .WillOnce(
+ DoAll(SaveArgPointee<1>(&req), SetArgPointee<1>(ret), Return(0)));
+ Handle handle(*chip, lines,
+ HandleFlags(LineFlags(GPIOLINE_FLAG_ACTIVE_LOW)),
+ label.c_str());
+
+ EXPECT_EQ(handle_fd, *handle.getFd());
+ EXPECT_EQ(GPIOHANDLE_REQUEST_INPUT | GPIOHANDLE_REQUEST_ACTIVE_LOW,
+ req.flags);
+ EXPECT_EQ(label, req.consumer_label);
+ EXPECT_EQ(lines.size(), req.lines);
+ for (uint32_t i = 0; i < lines.size(); ++i)
+ {
+ EXPECT_EQ(i, req.lineoffsets[i]);
+ EXPECT_EQ(1, req.default_values[i]);
+ }
+
+ EXPECT_CALL(mock, close(handle_fd)).WillOnce(Return(0));
+}
+
+TEST_F(HandleTest, ConstructTooMany)
+{
+ const std::vector<Handle::Line> lines(GPIOHANDLES_MAX + 1);
+ EXPECT_THROW(Handle(*chip, lines, HandleFlags(), "too_many"),
+ std::runtime_error);
+}
+
+TEST_F(HandleTest, ConstructError)
+{
+ const std::string label{"error"};
+ std::vector<Handle::Line> lines;
+ for (uint32_t i = 0; i < 5; ++i)
+ {
+ lines.push_back({i, 0});
+ }
+
+ struct gpiohandle_request req;
+ EXPECT_CALL(mock, gpio_get_linehandle(chip_fd, testing::_))
+ .WillOnce(DoAll(SaveArgPointee<1>(&req), Return(-EINVAL)));
+ EXPECT_THROW(Handle(*chip, lines,
+ HandleFlags(LineFlags(GPIOLINE_FLAG_IS_OUT)),
+ label.c_str()),
+ std::runtime_error);
+
+ EXPECT_EQ(GPIOHANDLE_REQUEST_OUTPUT, req.flags);
+ EXPECT_EQ(label, req.consumer_label);
+ EXPECT_EQ(lines.size(), req.lines);
+ for (uint32_t i = 0; i < lines.size(); ++i)
+ {
+ EXPECT_EQ(i, req.lineoffsets[i]);
+ EXPECT_EQ(0, req.default_values[i]);
+ }
+}
+
+class HandleMethodTest : public HandleTest
+{
+ protected:
+ std::unique_ptr<Handle> handle;
+ const std::vector<Handle::Line> lines{{1, 1}, {4, 0}};
+
+ void SetUp()
+ {
+ HandleTest::SetUp();
+ struct gpiohandle_request ret;
+ ret.fd = handle_fd;
+ EXPECT_CALL(mock, gpio_get_linehandle(chip_fd, testing::_))
+ .WillOnce(DoAll(SetArgPointee<1>(ret), Return(0)));
+ handle = std::make_unique<Handle>(
+ *chip, lines, HandleFlags(LineFlags(GPIOLINE_FLAG_IS_OUT)),
+ "method");
+ }
+
+ void TearDown()
+ {
+ EXPECT_CALL(mock, close(handle_fd)).WillOnce(Return(0));
+ handle.reset();
+ HandleTest::TearDown();
+ }
+};
+
+TEST_F(HandleMethodTest, GetValuesRet)
+{
+ const std::vector<uint8_t> expected{0, 0};
+ struct gpiohandle_data ret;
+ ret.values[0] = 0;
+ ret.values[1] = 0;
+
+ EXPECT_CALL(mock, gpiohandle_get_line_values(handle_fd, testing::_))
+ .WillOnce(DoAll(SetArgPointee<1>(ret), Return(0)));
+ EXPECT_EQ(expected, handle->getValues());
+}
+
+TEST_F(HandleMethodTest, GetValuesSuccess)
+{
+ const std::vector<uint8_t> expected{1, 1};
+ struct gpiohandle_data ret;
+ ret.values[0] = 1;
+ ret.values[1] = 1;
+
+ EXPECT_CALL(mock, gpiohandle_get_line_values(handle_fd, testing::_))
+ .WillOnce(DoAll(SetArgPointee<1>(ret), Return(0)));
+ std::vector<uint8_t> output;
+ handle->getValues(output);
+ EXPECT_EQ(expected, output);
+}
+
+TEST_F(HandleMethodTest, GetValuesFailure)
+{
+ EXPECT_CALL(mock, gpiohandle_get_line_values(handle_fd, testing::_))
+ .WillOnce(Return(-EINVAL));
+ EXPECT_THROW(handle->getValues(), std::system_error);
+}
+
+TEST_F(HandleMethodTest, SetValuesSuccess)
+{
+ struct gpiohandle_data req;
+ EXPECT_CALL(mock, gpiohandle_set_line_values(handle_fd, testing::_))
+ .WillOnce(DoAll(SaveArgPointee<1>(&req), Return(0)));
+ handle->setValues({0, 1});
+ EXPECT_EQ(0, req.values[0]);
+ EXPECT_EQ(1, req.values[1]);
+}
+
+TEST_F(HandleMethodTest, SetValuesInvalid)
+{
+ EXPECT_THROW(handle->setValues({1}), std::runtime_error);
+ EXPECT_THROW(handle->setValues({1, 0, 1}), std::runtime_error);
+}
+
+TEST_F(HandleMethodTest, SetValuesFailure)
+{
+ struct gpiohandle_data req;
+ EXPECT_CALL(mock, gpiohandle_set_line_values(handle_fd, testing::_))
+ .WillOnce(DoAll(SaveArgPointee<1>(&req), Return(-EINVAL)));
+ EXPECT_THROW(handle->setValues({1, 1}), std::system_error);
+ EXPECT_EQ(1, req.values[0]);
+ EXPECT_EQ(1, req.values[1]);
+}
+
+} // namespace
+} // namespace gpioplus