test/chip: Implement
diff --git a/.gitignore b/.gitignore
index 22d5abf..3b68266 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,4 +42,5 @@
 
 # Output binaries
 /example/pulse
+/test/chip
 /test/internal_fd
diff --git a/test/Makefile.am b/test/Makefile.am
index a338bfe..7451640 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -7,6 +7,11 @@
 check_PROGRAMS =
 TESTS = $(check_PROGRAMS)
 
+check_PROGRAMS += chip
+chip_SOURCES = chip.cpp
+chip_CPPFLAGS = $(gtest_cppflags)
+chip_LDADD = $(gtest_ldadd)
+
 check_PROGRAMS += internal_fd
 internal_fd_SOURCES = internal/fd.cpp
 internal_fd_CPPFLAGS = $(gtest_cppflags)
diff --git a/test/chip.cpp b/test/chip.cpp
new file mode 100644
index 0000000..aa3979f
--- /dev/null
+++ b/test/chip.cpp
@@ -0,0 +1,134 @@
+#include <cerrno>
+#include <cstdint>
+#include <cstring>
+#include <fcntl.h>
+#include <gmock/gmock.h>
+#include <gpioplus/chip.hpp>
+#include <gpioplus/test/sys.hpp>
+#include <gtest/gtest.h>
+#include <linux/gpio.h>
+#include <memory>
+#include <system_error>
+
+namespace gpioplus
+{
+namespace
+{
+
+using testing::DoAll;
+using testing::Return;
+using testing::SaveArgPointee;
+using testing::SetArgPointee;
+using testing::StrEq;
+
+TEST(LineFlagsTest, LineFlagsFromFlags)
+{
+    LineFlags line_flags(GPIOLINE_FLAG_KERNEL | GPIOLINE_FLAG_OPEN_DRAIN |
+                         GPIOLINE_FLAG_OPEN_SOURCE);
+    EXPECT_TRUE(line_flags.kernel);
+    EXPECT_FALSE(line_flags.output);
+    EXPECT_FALSE(line_flags.active_low);
+    EXPECT_TRUE(line_flags.open_drain);
+    EXPECT_TRUE(line_flags.open_source);
+
+    line_flags = GPIOLINE_FLAG_IS_OUT | GPIOLINE_FLAG_ACTIVE_LOW;
+    EXPECT_FALSE(line_flags.kernel);
+    EXPECT_TRUE(line_flags.output);
+    EXPECT_TRUE(line_flags.active_low);
+    EXPECT_FALSE(line_flags.open_drain);
+    EXPECT_FALSE(line_flags.open_source);
+}
+
+class ChipMethodTest : public testing::Test
+{
+  protected:
+    const int expected_fd = 1234;
+    testing::StrictMock<test::SysMock> mock;
+    std::unique_ptr<Chip> chip;
+
+    void SetUp()
+    {
+        const int chip_id = 1;
+        const char* path = "/dev/gpiochip1";
+
+        EXPECT_CALL(mock, open(StrEq(path), O_RDONLY | O_CLOEXEC))
+            .WillOnce(Return(expected_fd));
+        chip = std::make_unique<Chip>(chip_id, &mock);
+    }
+
+    void TearDown()
+    {
+        EXPECT_CALL(mock, close(expected_fd)).WillOnce(Return(0));
+        chip.reset();
+    }
+};
+
+TEST_F(ChipMethodTest, Basic)
+{
+    EXPECT_EQ(expected_fd, *chip->getFd());
+    EXPECT_EQ(&mock, chip->getFd().getSys());
+}
+
+TEST_F(ChipMethodTest, GetChipInfoSuccess)
+{
+    const ChipInfo expected_info{"name", "label", 31};
+    struct gpiochip_info info;
+    ASSERT_LE(expected_info.name.size(), sizeof(info.name));
+    strcpy(info.name, expected_info.name.c_str());
+    ASSERT_LE(expected_info.label.size(), sizeof(info.label));
+    strcpy(info.label, expected_info.label.c_str());
+    info.lines = expected_info.lines;
+
+    EXPECT_CALL(mock, gpio_get_chipinfo(expected_fd, testing::_))
+        .WillOnce(DoAll(SetArgPointee<1>(info), Return(0)));
+    ChipInfo retrieved_info = chip->getChipInfo();
+    EXPECT_EQ(expected_info.name, retrieved_info.name);
+    EXPECT_EQ(expected_info.label, retrieved_info.label);
+    EXPECT_EQ(expected_info.lines, retrieved_info.lines);
+}
+
+TEST_F(ChipMethodTest, GetChipInfoFail)
+{
+    EXPECT_CALL(mock, gpio_get_chipinfo(expected_fd, testing::_))
+        .WillOnce(Return(-EINVAL));
+    EXPECT_THROW(chip->getChipInfo(), std::system_error);
+}
+
+TEST_F(ChipMethodTest, GetLineInfoSuccess)
+{
+    const uint32_t line = 176;
+    const LineInfo expected_info{GPIOLINE_FLAG_ACTIVE_LOW, "name", "consumer"};
+    struct gpioline_info info, req;
+    info.flags = GPIOLINE_FLAG_ACTIVE_LOW;
+    ASSERT_LE(expected_info.name.size(), sizeof(info.name));
+    strcpy(info.name, expected_info.name.c_str());
+    ASSERT_LE(expected_info.consumer.size(), sizeof(info.consumer));
+    strcpy(info.consumer, expected_info.consumer.c_str());
+
+    EXPECT_CALL(mock, gpio_get_lineinfo(expected_fd, testing::_))
+        .WillOnce(
+            DoAll(SaveArgPointee<1>(&req), SetArgPointee<1>(info), Return(0)));
+    LineInfo retrieved_info = chip->getLineInfo(line);
+    EXPECT_EQ(line, req.line_offset);
+    EXPECT_FALSE(retrieved_info.flags.kernel);
+    EXPECT_FALSE(retrieved_info.flags.output);
+    EXPECT_TRUE(retrieved_info.flags.active_low);
+    EXPECT_FALSE(retrieved_info.flags.open_drain);
+    EXPECT_FALSE(retrieved_info.flags.open_source);
+    EXPECT_EQ(expected_info.name, retrieved_info.name);
+    EXPECT_EQ(expected_info.consumer, retrieved_info.consumer);
+}
+
+TEST_F(ChipMethodTest, GetLineInfoFail)
+{
+    const uint32_t line = 143;
+
+    struct gpioline_info info;
+    EXPECT_CALL(mock, gpio_get_lineinfo(expected_fd, testing::_))
+        .WillOnce(DoAll(SaveArgPointee<1>(&info), Return(-EINVAL)));
+    EXPECT_THROW(chip->getLineInfo(line), std::system_error);
+    EXPECT_EQ(line, info.line_offset);
+}
+
+} // namespace
+} // namespace gpioplus