1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
9 #include <sys/select.h>
14 #include "dev_fs_for_testing.h"
15 #include "gtest/gtest.h"
16 #include "nacl_io/devfs/dev_fs.h"
17 #include "nacl_io/filesystem.h"
18 #include "nacl_io/ioctl.h"
19 #include "nacl_io/kernel_intercept.h"
20 #include "nacl_io/kernel_proxy.h"
21 #include "nacl_io/osdirent.h"
23 using namespace nacl_io
;
27 static int ki_ioctl_wrapper(int fd
, int request
, ...) {
29 va_start(ap
, request
);
30 int rtn
= ki_ioctl(fd
, request
, ap
);
35 static int ki_fcntl_wrapper(int fd
, int request
, ...) {
37 va_start(ap
, request
);
38 int rtn
= ki_fcntl(fd
, request
, ap
);
43 static void SetNonBlocking(int fd
) {
44 int flags
= ki_fcntl_wrapper(fd
, F_GETFL
);
47 ASSERT_EQ(0, ki_fcntl_wrapper(fd
, F_SETFL
, flags
));
48 ASSERT_EQ(flags
, ki_fcntl_wrapper(fd
, F_GETFL
));
51 class TtyNodeTest
: public ::testing::Test
{
53 TtyNodeTest() : fs_(&ppapi_
) {}
56 ASSERT_EQ(0, fs_
.Open(Path("/tty"), O_RDWR
, &dev_tty_
));
57 ASSERT_NE(NULL_NODE
, dev_tty_
.get());
59 ASSERT_EQ(0, dev_tty_
->GetStat(&buf
));
60 ASSERT_EQ(S_IRUSR
| S_IWUSR
, buf
.st_mode
& S_IRWXU
);
64 FakePepperInterface ppapi_
;
69 class TtyTest
: public ::testing::Test
{
72 ASSERT_EQ(0, ki_push_state_for_testing());
73 ASSERT_EQ(0, ki_init_interface(&kp_
, &ppapi_
));
75 var_iface_
= ppapi_
.GetVarInterface();
82 int TtyWrite(int fd
, const char* string
) {
83 PP_Var message_var
= var_iface_
->VarFromUtf8(string
, strlen(string
));
84 int result
= ki_ioctl_wrapper(fd
, NACL_IOC_HANDLEMESSAGE
, &message_var
);
85 var_iface_
->Release(message_var
);
90 FakePepperInterface ppapi_
;
92 VarInterface
* var_iface_
;
95 TEST_F(TtyNodeTest
, InvalidIoctl
) {
96 // 123 is not a valid ioctl request.
97 EXPECT_EQ(EINVAL
, dev_tty_
->Ioctl(123));
100 TEST_F(TtyNodeTest
, TtyInput
) {
101 // Now let's try sending some data over.
102 // First we create the message.
103 std::string
message("hello, how are you?\n");
104 VarInterface
* var_iface
= ppapi_
.GetVarInterface();
105 PP_Var message_var
= var_iface
->VarFromUtf8(message
.data(), message
.size());
107 // Now we make buffer we'll read into.
108 // We fill the buffer and a backup buffer with arbitrary data
109 // and compare them after reading to make sure read doesn't
110 // clobber parts of the buffer it shouldn't.
113 char backup_buffer
[100];
114 memset(buffer
, 'a', 100);
115 memset(backup_buffer
, 'a', 100);
117 // Now we actually send the data
118 EXPECT_EQ(0, dev_tty_
->Ioctl(NACL_IOC_HANDLEMESSAGE
, &message_var
));
120 var_iface
->Release(message_var
);
122 // We read a small chunk first to ensure it doesn't give us
123 // more than we ask for.
125 EXPECT_EQ(0, dev_tty_
->Read(attrs
, buffer
, 5, &bytes_read
));
126 EXPECT_EQ(5, bytes_read
);
127 EXPECT_EQ(0, memcmp(message
.data(), buffer
, 5));
128 EXPECT_EQ(0, memcmp(buffer
+ 5, backup_buffer
+ 5, 95));
130 // Now we ask for more data than is left in the tty, to ensure
131 // it doesn't give us more than is there.
132 EXPECT_EQ(0, dev_tty_
->Read(attrs
, buffer
+ 5, 95, &bytes_read
));
133 EXPECT_EQ(bytes_read
, message
.size() - 5);
134 EXPECT_EQ(0, memcmp(message
.data(), buffer
, message
.size()));
135 EXPECT_EQ(0, memcmp(buffer
+ message
.size(),
136 backup_buffer
+ message
.size(),
137 100 - message
.size()));
141 const char* output_buf
;
145 static ssize_t
output_handler(const char* buf
, size_t count
, void* data
) {
146 user_data_t
* user_data
= static_cast<user_data_t
*>(data
);
147 user_data
->output_buf
= buf
;
148 user_data
->output_count
= count
;
152 TEST_F(TtyNodeTest
, TtyOutput
) {
153 // When no handler is registered then all writes should return EIO
154 int bytes_written
= 10;
155 const char* message
= "hello\n";
156 int message_len
= strlen(message
);
158 EXPECT_EQ(EIO
, dev_tty_
->Write(attrs
, message
, message_len
, &bytes_written
));
160 // Setup output handler with user_data to record calls.
161 user_data_t user_data
;
162 user_data
.output_buf
= NULL
;
163 user_data
.output_count
= 0;
165 tioc_nacl_output handler
;
166 handler
.handler
= output_handler
;
167 handler
.user_data
= &user_data
;
169 EXPECT_EQ(0, dev_tty_
->Ioctl(TIOCNACLOUTPUT
, &handler
));
171 EXPECT_EQ(0, dev_tty_
->Write(attrs
, message
, message_len
, &bytes_written
));
172 EXPECT_EQ(message_len
, bytes_written
);
173 EXPECT_EQ(message_len
, user_data
.output_count
);
174 EXPECT_EQ(0, strncmp(user_data
.output_buf
, message
, message_len
));
180 // -1 -> Error occured
181 static int IsReadable(int fd
) {
182 struct timeval timeout
= {0, 0};
187 FD_SET(fd
, &readfds
);
188 FD_SET(fd
, &errorfds
);
189 int rtn
= ki_select(fd
+ 1, &readfds
, NULL
, &errorfds
, &timeout
);
191 return 0; // not readable
194 if (FD_ISSET(fd
, &errorfds
))
196 if (!FD_ISSET(fd
, &readfds
))
198 return 1; // readable
201 TEST_F(TtyTest
, TtySelect
) {
202 struct timeval timeout
;
207 int tty_fd
= ki_open("/dev/tty", O_RDONLY
, 0);
208 ASSERT_GT(tty_fd
, 0) << "tty open failed: " << errno
;
212 FD_SET(tty_fd
, &readfds
);
213 FD_SET(tty_fd
, &errorfds
);
214 // 10 millisecond timeout
216 timeout
.tv_usec
= 10 * 1000;
217 // Should timeout when no input is available.
218 int rtn
= ki_select(tty_fd
+ 1, &readfds
, NULL
, &errorfds
, &timeout
);
219 ASSERT_EQ(0, rtn
) << "select failed: " << rtn
<< " err=" << strerror(errno
);
220 ASSERT_FALSE(FD_ISSET(tty_fd
, &readfds
));
221 ASSERT_FALSE(FD_ISSET(tty_fd
, &errorfds
));
226 FD_SET(tty_fd
, &readfds
);
227 FD_SET(tty_fd
, &writefds
);
228 FD_SET(tty_fd
, &errorfds
);
229 // TTY should be writable on startup.
230 rtn
= ki_select(tty_fd
+ 1, &readfds
, &writefds
, &errorfds
, NULL
);
232 ASSERT_TRUE(FD_ISSET(tty_fd
, &writefds
));
233 ASSERT_FALSE(FD_ISSET(tty_fd
, &readfds
));
234 ASSERT_FALSE(FD_ISSET(tty_fd
, &errorfds
));
236 // Send 4 bytes to TTY input
237 ASSERT_EQ(0, TtyWrite(tty_fd
, "input:test"));
239 // TTY should not be readable until newline in written
240 ASSERT_EQ(IsReadable(tty_fd
), 0);
241 ASSERT_EQ(0, TtyWrite(tty_fd
, "input:\n"));
243 // TTY should now be readable
244 ASSERT_EQ(1, IsReadable(tty_fd
));
246 ASSERT_EQ(0, ki_close(tty_fd
));
249 TEST_F(TtyTest
, TtyICANON
) {
250 int tty_fd
= ki_open("/dev/tty", O_RDONLY
, 0);
252 ASSERT_EQ(0, IsReadable(tty_fd
));
254 struct termios tattr
;
255 ki_tcgetattr(tty_fd
, &tattr
);
256 tattr
.c_lflag
&= ~(ICANON
| ECHO
); /* Clear ICANON and ECHO. */
257 ki_tcsetattr(tty_fd
, TCSAFLUSH
, &tattr
);
259 ASSERT_EQ(0, IsReadable(tty_fd
));
261 // Set some bytes to the TTY, not including newline
262 ASSERT_EQ(0, TtyWrite(tty_fd
, "a"));
264 // Since we are not in canonical mode the bytes should be
265 // immediately readable.
266 ASSERT_EQ(1, IsReadable(tty_fd
));
268 // Read byte from tty.
270 ASSERT_EQ(1, ki_read(tty_fd
, &c
, 1));
273 ASSERT_EQ(0, IsReadable(tty_fd
));
276 static int g_received_signal
;
278 static void sighandler(int sig
) { g_received_signal
= sig
; }
280 TEST_F(TtyTest
, WindowSize
) {
281 // Get current window size
282 struct winsize old_winsize
= {0};
283 int tty_fd
= ki_open("/dev/tty", O_RDONLY
, 0);
284 ASSERT_EQ(0, ki_ioctl_wrapper(tty_fd
, TIOCGWINSZ
, &old_winsize
));
286 // Install signal handler
287 sighandler_t new_handler
= sighandler
;
288 sighandler_t old_handler
= ki_signal(SIGWINCH
, new_handler
);
289 ASSERT_NE(SIG_ERR
, old_handler
) << "signal return error: " << errno
;
291 g_received_signal
= 0;
293 // Set a new windows size
294 struct winsize winsize
;
295 winsize
.ws_col
= 100;
296 winsize
.ws_row
= 200;
297 EXPECT_EQ(0, ki_ioctl_wrapper(tty_fd
, TIOCSWINSZ
, &winsize
));
298 EXPECT_EQ(SIGWINCH
, g_received_signal
);
300 // Restore old signal handler
301 EXPECT_EQ(new_handler
, ki_signal(SIGWINCH
, old_handler
));
303 // Verify new window size can be queried correctly.
306 EXPECT_EQ(0, ki_ioctl_wrapper(tty_fd
, TIOCGWINSZ
, &winsize
));
307 EXPECT_EQ(100, winsize
.ws_col
);
308 EXPECT_EQ(200, winsize
.ws_row
);
310 // Restore original windows size.
311 EXPECT_EQ(0, ki_ioctl_wrapper(tty_fd
, TIOCSWINSZ
, &old_winsize
));
315 * Sleep for 50ms then send a resize event to /dev/tty.
317 static void* resize_thread_main(void* arg
) {
320 int* tty_fd
= static_cast<int*>(arg
);
321 struct winsize winsize
;
322 winsize
.ws_col
= 100;
323 winsize
.ws_row
= 200;
324 ki_ioctl_wrapper(*tty_fd
, TIOCSWINSZ
, &winsize
);
328 TEST_F(TtyTest
, ResizeDuringSelect
) {
329 // Test that a window resize during a call
330 // to select(3) will cause it to fail with EINTR.
331 int tty_fd
= ki_open("/dev/tty", O_RDONLY
, 0);
337 FD_SET(tty_fd
, &readfds
);
338 FD_SET(tty_fd
, &errorfds
);
340 pthread_t resize_thread
;
341 pthread_create(&resize_thread
, NULL
, resize_thread_main
, &tty_fd
);
343 struct timeval timeout
;
347 // TTY should not be readable either before or after the
348 // call to select(3).
349 ASSERT_EQ(0, IsReadable(tty_fd
));
351 int rtn
= ki_select(tty_fd
+ 1, &readfds
, NULL
, &errorfds
, &timeout
);
352 pthread_join(resize_thread
, NULL
);
354 ASSERT_EQ(EINTR
, errno
);
355 ASSERT_EQ(0, IsReadable(tty_fd
));
359 * Sleep for 50ms then send some input to the /dev/tty.
361 static void* input_thread_main(void* arg
) {
362 TtyTest
* thiz
= static_cast<TtyTest
*>(arg
);
366 int fd
= ki_open("/dev/tty", O_RDONLY
, 0);
367 thiz
->TtyWrite(fd
, "test\n");
371 TEST_F(TtyTest
, InputDuringSelect
) {
372 // Test that input which occurs while in select causes
374 int tty_fd
= ki_open("/dev/tty", O_RDONLY
, 0);
380 FD_SET(tty_fd
, &readfds
);
381 FD_SET(tty_fd
, &errorfds
);
383 pthread_t resize_thread
;
384 pthread_create(&resize_thread
, NULL
, input_thread_main
, this);
386 struct timeval timeout
;
390 int rtn
= ki_select(tty_fd
+ 1, &readfds
, NULL
, &errorfds
, &timeout
);
391 pthread_join(resize_thread
, NULL
);
396 TEST_F(TtyTest
, NonBlocking
) {
397 // Test that non-blocking mode works.
398 int fd
= ki_open("/dev/tty", O_RDONLY
, 0);
399 ASSERT_GT(fd
, 0) << "tty open failed: " << errno
;
403 bytes_read
= ki_read(fd
, buffer
, sizeof(buffer
));
404 ASSERT_EQ(-1, bytes_read
);
405 ASSERT_EQ(EWOULDBLOCK
, errno
);
406 ASSERT_EQ(0, TtyWrite(fd
, "test\n"));
407 bytes_read
= ki_read(fd
, buffer
, sizeof(buffer
));
408 ASSERT_EQ(5, bytes_read
);
409 ASSERT_EQ(0, memcmp(buffer
, "test\n", 5));