- add sources.
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / tests / nacl_io_test / mount_node_tty_test.cc
1 // Copyright (c) 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.
4
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>
13
14 #include "gtest/gtest.h"
15 #include "mount_dev_mock.h"
16 #include "nacl_io/ioctl.h"
17 #include "nacl_io/kernel_intercept.h"
18 #include "nacl_io/kernel_proxy.h"
19 #include "nacl_io/mount.h"
20 #include "nacl_io/mount_dev.h"
21 #include "nacl_io/mount_mem.h"
22 #include "nacl_io/osdirent.h"
23
24 using namespace nacl_io;
25
26 namespace {
27
28 class TtyTest : public ::testing::Test {
29  public:
30   void SetUp() {
31     ki_init(&kp_);
32     ASSERT_EQ(0, mnt_.Access(Path("/tty"), R_OK | W_OK));
33     ASSERT_EQ(EACCES, mnt_.Access(Path("/tty"), X_OK));
34     ASSERT_EQ(0, mnt_.Open(Path("/tty"), O_RDWR, &dev_tty_));
35     ASSERT_NE(NULL_NODE, dev_tty_.get());
36   }
37
38   void TearDown() {
39     ki_uninit();
40   }
41
42  protected:
43   KernelProxy kp_;
44   MountDevMock mnt_;
45   ScopedMountNode dev_tty_;
46 };
47
48 TEST_F(TtyTest, InvalidIoctl) {
49   // 123 is not a valid ioctl request.
50   EXPECT_EQ(EINVAL, dev_tty_->Ioctl(123));
51 }
52
53 TEST_F(TtyTest, TtyInput) {
54   // Now let's try sending some data over.
55   // First we create the message.
56   std::string message("hello, how are you?\n");
57   struct tioc_nacl_input_string packaged_message;
58   packaged_message.length = message.size();
59   packaged_message.buffer = message.data();
60
61   // Now we make buffer we'll read into.
62   // We fill the buffer and a backup buffer with arbitrary data
63   // and compare them after reading to make sure read doesn't
64   // clobber parts of the buffer it shouldn't.
65   int bytes_read;
66   char buffer[100];
67   char backup_buffer[100];
68   memset(buffer, 'a', 100);
69   memset(backup_buffer, 'a', 100);
70
71   // Now we actually send the data
72   EXPECT_EQ(0, dev_tty_->Ioctl(TIOCNACLINPUT, &packaged_message));
73
74   // We read a small chunk first to ensure it doesn't give us
75   // more than we ask for.
76   HandleAttr attrs;
77   EXPECT_EQ(0, dev_tty_->Read(attrs, buffer, 5, &bytes_read));
78   EXPECT_EQ(bytes_read, 5);
79   EXPECT_EQ(0, memcmp(message.data(), buffer, 5));
80   EXPECT_EQ(0, memcmp(buffer + 5, backup_buffer + 5, 95));
81
82   // Now we ask for more data than is left in the tty, to ensure
83   // it doesn't give us more than is there.
84   EXPECT_EQ(0, dev_tty_->Read(attrs, buffer + 5, 95, &bytes_read));
85   EXPECT_EQ(bytes_read, message.size() - 5);
86   EXPECT_EQ(0, memcmp(message.data(), buffer, message.size()));
87   EXPECT_EQ(0, memcmp(buffer + message.size(),
88                       backup_buffer + message.size(),
89                       100 - message.size()));
90 }
91
92 struct user_data_t {
93   const char* output_buf;
94   size_t output_count;
95 };
96
97 static ssize_t output_handler(const char* buf, size_t count, void* data) {
98   user_data_t* user_data = static_cast<user_data_t*>(data);
99   user_data->output_buf = buf;
100   user_data->output_count = count;
101   return count;
102 }
103
104 TEST_F(TtyTest, TtyOutput) {
105   // When no handler is registered then all writes should return EIO
106   int bytes_written = 10;
107   const char* message = "hello\n";
108   int message_len = strlen(message);
109   HandleAttr attrs;
110   EXPECT_EQ(EIO, dev_tty_->Write(attrs, message, message_len, &bytes_written));
111
112   // Setup output handler with user_data to record calls.
113   user_data_t user_data;
114   user_data.output_buf = NULL;
115   user_data.output_count = 0;
116
117   tioc_nacl_output handler;
118   handler.handler = output_handler;
119   handler.user_data = &user_data;
120
121   EXPECT_EQ(0, dev_tty_->Ioctl(TIOCNACLOUTPUT, &handler));
122
123   EXPECT_EQ(0, dev_tty_->Write(attrs, message, message_len, &bytes_written));
124   EXPECT_EQ(message_len, bytes_written);
125   EXPECT_EQ(message_len, user_data.output_count);
126   EXPECT_EQ(0, strncmp(user_data.output_buf, message, message_len));
127 }
128
129 static int ki_ioctl_wrapper(int fd, int request, ...) {
130   va_list ap;
131   va_start(ap, request);
132   int rtn = ki_ioctl(fd, request, ap);
133   va_end(ap);
134   return rtn;
135 }
136
137 static int TtyWrite(int fd, const char* string) {
138   struct tioc_nacl_input_string input;
139   input.buffer = string;
140   input.length = strlen(input.buffer);
141   return ki_ioctl_wrapper(fd, TIOCNACLINPUT, &input);
142 }
143
144 // Returns:
145 //   0 -> Not readable
146 //   1 -> Readable
147 //  -1 -> Error occured
148 static int IsReadable(int fd) {
149   struct timeval timeout = { 0, 0 };
150   fd_set readfds;
151   fd_set errorfds;
152   FD_ZERO(&readfds);
153   FD_ZERO(&errorfds);
154   FD_SET(fd, &readfds);
155   FD_SET(fd, &errorfds);
156   int rtn = ki_select(fd + 1, &readfds, NULL, &errorfds, &timeout);
157   if (rtn == 0)
158     return 0; // not readable
159   if (rtn != 1)
160     return -1; // error
161   if (FD_ISSET(fd, &errorfds))
162     return -2; // error
163   if (!FD_ISSET(fd, &readfds))
164     return -3; // error
165   return 1; // readable
166 }
167
168 TEST_F(TtyTest, TtySelect) {
169   struct timeval timeout;
170   fd_set readfds;
171   fd_set writefds;
172   fd_set errorfds;
173
174   int tty_fd = ki_open("/dev/tty", O_RDONLY);
175   ASSERT_TRUE(tty_fd >= 0) << "tty open failed: " << errno;
176
177   FD_ZERO(&readfds);
178   FD_ZERO(&errorfds);
179   FD_SET(tty_fd, &readfds);
180   FD_SET(tty_fd, &errorfds);
181   // 10 millisecond timeout
182   timeout.tv_sec = 0;
183   timeout.tv_usec = 10 * 1000;
184   // Should timeout when no input is available.
185   int rtn = ki_select(tty_fd + 1, &readfds, NULL, &errorfds, &timeout);
186   ASSERT_EQ(rtn, 0) << "select failed: " << rtn << " err=" << strerror(errno);
187   ASSERT_FALSE(FD_ISSET(tty_fd, &readfds));
188   ASSERT_FALSE(FD_ISSET(tty_fd, &errorfds));
189
190   FD_ZERO(&readfds);
191   FD_ZERO(&writefds);
192   FD_ZERO(&errorfds);
193   FD_SET(tty_fd, &readfds);
194   FD_SET(tty_fd, &writefds);
195   FD_SET(tty_fd, &errorfds);
196   // TTY should be writable on startup.
197   rtn = ki_select(tty_fd + 1, &readfds, &writefds, &errorfds, NULL);
198   ASSERT_EQ(rtn, 1);
199   ASSERT_TRUE(FD_ISSET(tty_fd, &writefds));
200   ASSERT_FALSE(FD_ISSET(tty_fd, &readfds));
201   ASSERT_FALSE(FD_ISSET(tty_fd, &errorfds));
202
203   // Send 4 bytes to TTY input
204   ASSERT_EQ(0, TtyWrite(tty_fd, "input:test"));
205
206   // TTY should not be readable until newline in written
207   ASSERT_EQ(IsReadable(tty_fd), 0);
208   ASSERT_EQ(0, TtyWrite(tty_fd, "input:\n"));
209
210   // TTY should now be readable
211   ASSERT_EQ(IsReadable(tty_fd), 1);
212
213   ki_close(tty_fd);
214 }
215
216 TEST_F(TtyTest, TtyICANON) {
217   int tty_fd = ki_open("/dev/tty", O_RDONLY);
218
219   ASSERT_EQ(IsReadable(tty_fd), 0);
220
221   struct termios tattr;
222   tcgetattr(tty_fd, &tattr);
223   tattr.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */
224   tcsetattr(tty_fd, TCSAFLUSH, &tattr);
225
226   ASSERT_EQ(IsReadable(tty_fd), 0);
227
228   // Set some bytes to the TTY, not including newline
229   ASSERT_EQ(0, TtyWrite(tty_fd, "a"));
230
231   // Since we are not in canonical mode the bytes should be
232   // immediately readable.
233   ASSERT_EQ(IsReadable(tty_fd), 1);
234
235   // Read byte from tty.
236   char c;
237   ASSERT_EQ(1, read(tty_fd, &c, 1));
238   ASSERT_EQ('a', c);
239
240   ASSERT_EQ(IsReadable(tty_fd), 0);
241 }
242
243 int g_recieved_signal = 0;
244
245 void sighandler(int sig) {
246   g_recieved_signal = sig;
247 }
248
249 TEST_F(TtyTest, WindowSize) {
250   // Get current window size
251   struct winsize old_winsize = { 0 };
252   ASSERT_EQ(0, dev_tty_->Ioctl(TIOCGWINSZ, &old_winsize));
253
254   // Install signal handler
255   sighandler_t new_handler = sighandler;
256   sighandler_t old_handler = ki_signal(SIGWINCH, new_handler);
257   ASSERT_NE(old_handler, SIG_ERR) << "signal return error: " << errno;
258
259   // Set a new windows size
260   struct winsize winsize;
261   winsize.ws_col = 100;
262   winsize.ws_row = 200;
263   EXPECT_EQ(0, dev_tty_->Ioctl(TIOCSWINSZ, &winsize));
264   EXPECT_EQ(g_recieved_signal, SIGWINCH);
265
266   // Restore old signal handler
267   EXPECT_EQ(new_handler, ki_signal(SIGWINCH, old_handler));
268
269   // Verify new window size can be queried correctly.
270   winsize.ws_col = 0;
271   winsize.ws_row = 0;
272   EXPECT_EQ(0, dev_tty_->Ioctl(TIOCGWINSZ, &winsize));
273   EXPECT_EQ(winsize.ws_col, 100);
274   EXPECT_EQ(winsize.ws_row, 200);
275
276   // Restore original windows size.
277   EXPECT_EQ(0, dev_tty_->Ioctl(TIOCSWINSZ, &old_winsize));
278 }
279
280 /*
281  * Sleep for 50ms then send a resize event to /dev/tty.
282  */
283 static void* resize_thread_main(void* arg) {
284   usleep(50 * 1000);
285
286   int* tty_fd = static_cast<int*>(arg);
287   struct winsize winsize;
288   winsize.ws_col = 100;
289   winsize.ws_row = 200;
290   ki_ioctl_wrapper(*tty_fd, TIOCSWINSZ, &winsize);
291   return NULL;
292 }
293
294 TEST_F(TtyTest, ResizeDuringSelect) {
295   // Test that a window resize during a call
296   // to select(3) will cause it to fail with EINTR.
297   int tty_fd = ki_open("/dev/tty", O_RDONLY);
298
299   fd_set readfds;
300   fd_set errorfds;
301   FD_ZERO(&readfds);
302   FD_ZERO(&errorfds);
303   FD_SET(tty_fd, &readfds);
304   FD_SET(tty_fd, &errorfds);
305
306   pthread_t resize_thread;
307   pthread_create(&resize_thread, NULL, resize_thread_main,
308                  &tty_fd);
309
310   struct timeval timeout;
311   timeout.tv_sec = 20;
312   timeout.tv_usec = 0;
313
314   // TTY should not be readable either before or after the
315   // call to select(3).
316   ASSERT_EQ(IsReadable(tty_fd), 0);
317
318   int rtn = ki_select(tty_fd + 1, &readfds, NULL, &errorfds, &timeout);
319   pthread_join(resize_thread, NULL);
320   ASSERT_EQ(-1, rtn);
321   ASSERT_EQ(EINTR, errno);
322   ASSERT_EQ(IsReadable(tty_fd), 0);
323 }
324
325 /*
326  * Sleep for 50ms then send some input to the /dev/tty.
327  */
328 static void* input_thread_main(void* arg) {
329   usleep(50 * 1000);
330
331   int fd = ki_open("/dev/tty", O_RDONLY);
332   TtyWrite(fd, "test\n");
333   return NULL;
334 }
335
336 TEST_F(TtyTest, InputDuringSelect) {
337   // Test that input which occurs while in select causes
338   // select to return.
339   int tty_fd = ki_open("/dev/tty", O_RDONLY);
340
341   fd_set readfds;
342   fd_set errorfds;
343   FD_ZERO(&readfds);
344   FD_ZERO(&errorfds);
345   FD_SET(tty_fd, &readfds);
346   FD_SET(tty_fd, &errorfds);
347
348   pthread_t resize_thread;
349   pthread_create(&resize_thread, NULL, input_thread_main,
350                  &dev_tty_);
351
352   struct timeval timeout;
353   timeout.tv_sec = 20;
354   timeout.tv_usec = 0;
355
356   int rtn = ki_select(tty_fd + 1, &readfds, NULL, &errorfds, &timeout);
357   pthread_join(resize_thread, NULL);
358
359   ASSERT_EQ(1, rtn);
360 }
361
362 }