Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / tests / nacl_io_test / tty_test.cc
blob06a2b777d6ecb714989daff7ffd8fca3a8165c55
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.
5 #include <errno.h>
6 #include <fcntl.h>
7 #include <string.h>
8 #include <sys/ioctl.h>
9 #include <sys/select.h>
10 #include <sys/stat.h>
11 #include <sys/time.h>
12 #include <string>
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;
25 namespace {
27 static int ki_ioctl_wrapper(int fd, int request, ...) {
28 va_list ap;
29 va_start(ap, request);
30 int rtn = ki_ioctl(fd, request, ap);
31 va_end(ap);
32 return rtn;
35 static int ki_fcntl_wrapper(int fd, int request, ...) {
36 va_list ap;
37 va_start(ap, request);
38 int rtn = ki_fcntl(fd, request, ap);
39 va_end(ap);
40 return rtn;
43 static void SetNonBlocking(int fd) {
44 int flags = ki_fcntl_wrapper(fd, F_GETFL);
45 ASSERT_NE(-1, flags);
46 flags |= O_NONBLOCK;
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 {
52 public:
53 TtyNodeTest() : fs_(&ppapi_) {}
55 void SetUp() {
56 ASSERT_EQ(0, fs_.Open(Path("/tty"), O_RDWR, &dev_tty_));
57 ASSERT_NE(NULL_NODE, dev_tty_.get());
58 struct stat buf;
59 ASSERT_EQ(0, dev_tty_->GetStat(&buf));
60 ASSERT_EQ(S_IRUSR | S_IWUSR, buf.st_mode & S_IRWXU);
63 protected:
64 FakePepperInterface ppapi_;
65 DevFsForTesting fs_;
66 ScopedNode dev_tty_;
69 class TtyTest : public ::testing::Test {
70 public:
71 void SetUp() {
72 ASSERT_EQ(0, ki_push_state_for_testing());
73 ASSERT_EQ(0, ki_init_interface(&kp_, &ppapi_));
75 var_iface_ = ppapi_.GetVarInterface();
78 void TearDown() {
79 ki_uninit();
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);
86 return result;
89 protected:
90 FakePepperInterface ppapi_;
91 KernelProxy kp_;
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.
111 int bytes_read;
112 char buffer[100];
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.
124 HandleAttr attrs;
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()));
140 struct user_data_t {
141 const char* output_buf;
142 size_t output_count;
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;
149 return 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);
157 HandleAttr attrs;
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));
177 // Returns:
178 // 0 -> Not readable
179 // 1 -> Readable
180 // -1 -> Error occured
181 static int IsReadable(int fd) {
182 struct timeval timeout = {0, 0};
183 fd_set readfds;
184 fd_set errorfds;
185 FD_ZERO(&readfds);
186 FD_ZERO(&errorfds);
187 FD_SET(fd, &readfds);
188 FD_SET(fd, &errorfds);
189 int rtn = ki_select(fd + 1, &readfds, NULL, &errorfds, &timeout);
190 if (rtn == 0)
191 return 0; // not readable
192 if (rtn != 1)
193 return -1; // error
194 if (FD_ISSET(fd, &errorfds))
195 return -2; // error
196 if (!FD_ISSET(fd, &readfds))
197 return -3; // error
198 return 1; // readable
201 TEST_F(TtyTest, TtySelect) {
202 struct timeval timeout;
203 fd_set readfds;
204 fd_set writefds;
205 fd_set errorfds;
207 int tty_fd = ki_open("/dev/tty", O_RDONLY, 0);
208 ASSERT_GT(tty_fd, 0) << "tty open failed: " << errno;
210 FD_ZERO(&readfds);
211 FD_ZERO(&errorfds);
212 FD_SET(tty_fd, &readfds);
213 FD_SET(tty_fd, &errorfds);
214 // 10 millisecond timeout
215 timeout.tv_sec = 0;
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));
223 FD_ZERO(&readfds);
224 FD_ZERO(&writefds);
225 FD_ZERO(&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);
231 ASSERT_EQ(1, rtn);
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.
269 char c;
270 ASSERT_EQ(1, ki_read(tty_fd, &c, 1));
271 ASSERT_EQ('a', c);
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.
304 winsize.ws_col = 0;
305 winsize.ws_row = 0;
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) {
318 usleep(50 * 1000);
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);
325 return NULL;
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);
333 fd_set readfds;
334 fd_set errorfds;
335 FD_ZERO(&readfds);
336 FD_ZERO(&errorfds);
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;
344 timeout.tv_sec = 20;
345 timeout.tv_usec = 0;
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);
353 ASSERT_EQ(-1, rtn);
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);
364 usleep(50 * 1000);
366 int fd = ki_open("/dev/tty", O_RDONLY, 0);
367 thiz->TtyWrite(fd, "test\n");
368 return NULL;
371 TEST_F(TtyTest, InputDuringSelect) {
372 // Test that input which occurs while in select causes
373 // select to return.
374 int tty_fd = ki_open("/dev/tty", O_RDONLY, 0);
376 fd_set readfds;
377 fd_set errorfds;
378 FD_ZERO(&readfds);
379 FD_ZERO(&errorfds);
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;
387 timeout.tv_sec = 20;
388 timeout.tv_usec = 0;
390 int rtn = ki_select(tty_fd + 1, &readfds, NULL, &errorfds, &timeout);
391 pthread_join(resize_thread, NULL);
393 ASSERT_EQ(1, rtn);
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;
400 SetNonBlocking(fd);
401 int bytes_read;
402 char buffer[100];
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));
412 } // namespace