1 #include <fcntl.h>
2 #include <signal.h>
3 #include <sys/prctl.h>
4
5 #include <gpioplus/internal/fd.hpp>
6 #include <gpioplus/test/sys.hpp>
7
8 #include <cerrno>
9 #include <cstring>
10 #include <memory>
11 #include <system_error>
12 #include <type_traits>
13 #include <utility>
14
15 #include <gmock/gmock.h>
16 #include <gtest/gtest.h>
17
18 #ifdef HAVE_GCOV
19 // Needed for the abrt test
20 extern "C" void __gcov_flush(void);
21 #endif
22
23 namespace gpioplus
24 {
25 namespace internal
26 {
27 namespace
28 {
29
30 using testing::Assign;
31 using testing::DoAll;
32 using testing::Return;
33
34 class FdTest : public testing::Test
35 {
36 protected:
37 const int expected_fd = 1234;
38 const int expected_fd2 = 2345;
39 const int expected_fd3 = 3456;
40 testing::StrictMock<test::SysMock> mock;
41 testing::StrictMock<test::SysMock> mock2;
42 };
43
TEST_F(FdTest,ConstructSimple)44 TEST_F(FdTest, ConstructSimple)
45 {
46 Fd fd(expected_fd, std::false_type(), &mock);
47 EXPECT_EQ(expected_fd, *fd);
48 EXPECT_EQ(&mock, fd.getSys());
49
50 EXPECT_CALL(mock, close(expected_fd)).WillOnce(Return(0));
51 }
52
TEST_F(FdTest,ConstructSimplBadFd)53 TEST_F(FdTest, ConstructSimplBadFd)
54 {
55 Fd fd(-1, std::false_type(), &mock);
56 EXPECT_EQ(-1, *fd);
57 }
58
TEST_F(FdTest,ConstructDup)59 TEST_F(FdTest, ConstructDup)
60 {
61 EXPECT_CALL(mock, dup(expected_fd)).WillOnce(Return(expected_fd2));
62 Fd fd(expected_fd, &mock);
63 EXPECT_EQ(expected_fd2, *fd);
64 EXPECT_EQ(&mock, fd.getSys());
65
66 EXPECT_CALL(mock, close(expected_fd2)).WillOnce(Return(0));
67 }
68
TEST_F(FdTest,ConstructDupFail)69 TEST_F(FdTest, ConstructDupFail)
70 {
71 EXPECT_CALL(mock, dup(expected_fd))
72 .WillOnce(DoAll(Assign(&errno, EINVAL), Return(-1)));
73 EXPECT_THROW(Fd(expected_fd, &mock), std::system_error);
74 }
75
abrt_handler(int signum)76 void abrt_handler(int signum)
77 {
78 if (signum == SIGABRT)
79 {
80 #ifdef HAVE_GCOV
81 __gcov_flush();
82 #endif
83 }
84 }
85
TEST_F(FdTest,CloseFails)86 TEST_F(FdTest, CloseFails)
87 {
88 EXPECT_DEATH(
89 {
90 struct sigaction act;
91 act.sa_handler = abrt_handler;
92 sigemptyset(&act.sa_mask);
93 act.sa_flags = 0;
94 ASSERT_EQ(0, sigaction(SIGABRT, &act, nullptr));
95 ASSERT_EQ(0, prctl(PR_SET_DUMPABLE, 0, 0, 0, 0));
96 EXPECT_CALL(mock, close(expected_fd))
97 .WillOnce(DoAll(Assign(&errno, EINVAL), Return(-1)));
98 Fd(expected_fd, std::false_type(), &mock);
99 },
100 "");
101 }
102
TEST_F(FdTest,ConstructSuccess)103 TEST_F(FdTest, ConstructSuccess)
104 {
105 const char* path = "/no-such-path/gpio";
106 const int flags = O_RDWR;
107 EXPECT_CALL(mock, open(path, flags)).WillOnce(Return(expected_fd));
108 Fd fd(path, flags, &mock);
109 EXPECT_EQ(expected_fd, *fd);
110 EXPECT_EQ(&mock, fd.getSys());
111
112 EXPECT_CALL(mock, close(expected_fd)).WillOnce(Return(0));
113 }
114
TEST_F(FdTest,ConstructError)115 TEST_F(FdTest, ConstructError)
116 {
117 const char* path = "/no-such-path/gpio";
118 const int flags = O_RDWR;
119 EXPECT_CALL(mock, open(path, flags))
120 .WillOnce(DoAll(Assign(&errno, EBUSY), Return(-1)));
121 EXPECT_THROW(Fd(path, flags, &mock), std::system_error);
122 }
123
TEST_F(FdTest,ConstructCopy)124 TEST_F(FdTest, ConstructCopy)
125 {
126 Fd fd(expected_fd, std::false_type(), &mock);
127 {
128 EXPECT_CALL(mock, dup(expected_fd)).WillOnce(Return(expected_fd2));
129 Fd fd2(fd);
130 EXPECT_EQ(expected_fd2, *fd2);
131 EXPECT_EQ(expected_fd, *fd);
132
133 EXPECT_CALL(mock, close(expected_fd2)).WillOnce(Return(0));
134 }
135
136 EXPECT_CALL(mock, close(expected_fd)).WillOnce(Return(0));
137 }
138
TEST_F(FdTest,OperatorCopySame)139 TEST_F(FdTest, OperatorCopySame)
140 {
141 Fd fd(expected_fd, std::false_type(), &mock);
142 fd = fd;
143 EXPECT_EQ(expected_fd, *fd);
144
145 EXPECT_CALL(mock, close(expected_fd)).WillOnce(Return(0));
146 }
147
TEST_F(FdTest,OperatorCopy)148 TEST_F(FdTest, OperatorCopy)
149 {
150 Fd fd(expected_fd, std::false_type(), &mock);
151 {
152 Fd fd2(expected_fd2, std::false_type(), &mock2);
153 EXPECT_CALL(mock2, close(expected_fd2)).WillOnce(Return(0));
154 EXPECT_CALL(mock, dup(expected_fd)).WillOnce(Return(expected_fd3));
155 fd2 = fd;
156 EXPECT_EQ(expected_fd3, *fd2);
157 EXPECT_EQ(&mock, fd2.getSys());
158 EXPECT_EQ(expected_fd, *fd);
159 EXPECT_EQ(&mock, fd.getSys());
160
161 EXPECT_CALL(mock, close(expected_fd3)).WillOnce(Return(0));
162 }
163
164 EXPECT_CALL(mock, close(expected_fd)).WillOnce(Return(0));
165 }
166
TEST_F(FdTest,ConstructMove)167 TEST_F(FdTest, ConstructMove)
168 {
169 Fd fd(expected_fd, std::false_type(), &mock);
170 {
171 Fd fd2(std::move(fd));
172 EXPECT_EQ(expected_fd, *fd2);
173 EXPECT_EQ(-1, *fd);
174
175 EXPECT_CALL(mock, close(expected_fd)).WillOnce(Return(0));
176 }
177 }
178
TEST_F(FdTest,OperatorMoveSame)179 TEST_F(FdTest, OperatorMoveSame)
180 {
181 Fd fd(expected_fd, std::false_type(), &mock);
182 // Test move operator but newer compilers complain about move-to-self
183 // so use an explicit r-value ref cast to do the same.
184 fd = static_cast<Fd&&>(fd);
185 EXPECT_EQ(expected_fd, *fd);
186
187 EXPECT_CALL(mock, close(expected_fd)).WillOnce(Return(0));
188 }
189
TEST_F(FdTest,OperatorMove)190 TEST_F(FdTest, OperatorMove)
191 {
192 Fd fd(expected_fd, std::false_type(), &mock);
193 {
194 Fd fd2(expected_fd2, std::false_type(), &mock2);
195 EXPECT_CALL(mock2, close(expected_fd2)).WillOnce(Return(0));
196 fd2 = std::move(fd);
197 EXPECT_EQ(expected_fd, *fd2);
198 EXPECT_EQ(&mock, fd2.getSys());
199 EXPECT_EQ(-1, *fd);
200 EXPECT_EQ(&mock, fd.getSys());
201
202 EXPECT_CALL(mock, close(expected_fd)).WillOnce(Return(0));
203 }
204 }
205
206 class FdMethodTest : public FdTest
207 {
208 protected:
209 const int flags_blocking = O_SYNC | O_NOATIME;
210 const int flags_noblocking = O_NONBLOCK | flags_blocking;
211 std::unique_ptr<Fd> fd;
212
SetUp()213 void SetUp()
214 {
215 fd = std::make_unique<Fd>(expected_fd, std::false_type(), &mock);
216 }
217
TearDown()218 void TearDown()
219 {
220 EXPECT_CALL(mock, close(expected_fd)).WillOnce(Return(0));
221 fd.reset();
222 }
223 };
224
TEST_F(FdMethodTest,SetBlockingOnBlocking)225 TEST_F(FdMethodTest, SetBlockingOnBlocking)
226 {
227 EXPECT_CALL(mock, fcntl_getfl(expected_fd))
228 .WillOnce(Return(flags_blocking));
229 EXPECT_CALL(mock, fcntl_setfl(expected_fd, flags_blocking))
230 .WillOnce(Return(0));
231 fd->setBlocking(true);
232 }
233
TEST_F(FdMethodTest,SetBlockingOnNonBlocking)234 TEST_F(FdMethodTest, SetBlockingOnNonBlocking)
235 {
236 EXPECT_CALL(mock, fcntl_getfl(expected_fd))
237 .WillOnce(Return(flags_noblocking));
238 EXPECT_CALL(mock, fcntl_setfl(expected_fd, flags_blocking))
239 .WillOnce(Return(0));
240 fd->setBlocking(true);
241 }
242
TEST_F(FdMethodTest,SetNonBlockingOnBlocking)243 TEST_F(FdMethodTest, SetNonBlockingOnBlocking)
244 {
245 EXPECT_CALL(mock, fcntl_getfl(expected_fd))
246 .WillOnce(Return(flags_blocking));
247 EXPECT_CALL(mock, fcntl_setfl(expected_fd, flags_noblocking))
248 .WillOnce(Return(0));
249 fd->setBlocking(false);
250 }
251
TEST_F(FdMethodTest,SetNonBlockingOnNonBlocking)252 TEST_F(FdMethodTest, SetNonBlockingOnNonBlocking)
253 {
254 EXPECT_CALL(mock, fcntl_getfl(expected_fd))
255 .WillOnce(Return(flags_noblocking));
256 EXPECT_CALL(mock, fcntl_setfl(expected_fd, flags_noblocking))
257 .WillOnce(Return(0));
258 fd->setBlocking(false);
259 }
260
TEST_F(FdMethodTest,GetFlagsFail)261 TEST_F(FdMethodTest, GetFlagsFail)
262 {
263 EXPECT_CALL(mock, fcntl_getfl(expected_fd))
264 .WillOnce(DoAll(Assign(&errno, EINVAL), Return(-1)));
265 EXPECT_THROW(fd->setBlocking(true), std::system_error);
266 }
267
TEST_F(FdMethodTest,SetFlagsFail)268 TEST_F(FdMethodTest, SetFlagsFail)
269 {
270 EXPECT_CALL(mock, fcntl_getfl(expected_fd)).WillOnce(Return(0));
271 EXPECT_CALL(mock, fcntl_setfl(expected_fd, 0))
272 .WillOnce(DoAll(Assign(&errno, EINVAL), Return(-1)));
273 EXPECT_THROW(fd->setBlocking(true), std::system_error);
274 }
275
276 } // namespace
277 } // namespace internal
278 } // namespace gpioplus
279