ea2ba546797dba1daa6137487a0e3882b801c914
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / tests / nacl_io_test / jspipe_test.cc
1 // Copyright 2014 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 "dev_fs_for_testing.h"
15 #include "fake_ppapi/fake_messaging_interface.h"
16 #include "gtest/gtest.h"
17 #include "nacl_io/devfs/dev_fs.h"
18 #include "nacl_io/filesystem.h"
19 #include "nacl_io/ioctl.h"
20 #include "nacl_io/kernel_intercept.h"
21 #include "nacl_io/kernel_proxy.h"
22 #include "nacl_io/osdirent.h"
23
24 using namespace nacl_io;
25
26 namespace {
27
28 // Helper function for calling ki_ioctl without having
29 // to construct a va_list.
30 int ki_ioctl_wrapper(int fd, int request, ...) {
31   va_list ap;
32   va_start(ap, request);
33   int rtn = ki_ioctl(fd, request, ap);
34   va_end(ap);
35   return rtn;
36 }
37
38 // Helper function for converting PP_Var to C++ string
39 std::string VarToString(VarInterface* var_iface, PP_Var var) {
40   EXPECT_EQ(PP_VARTYPE_STRING, var.type);
41   uint32_t len = 0;
42   const char* str = var_iface->VarToUtf8(var, &len);
43   return std::string(str, len);
44 }
45
46 PP_Var VarFromCStr(VarInterface* iface, const char* string) {
47   return iface->VarFromUtf8(string, strlen(string));
48 }
49
50 // Helper function for creating message in the format expected by jspipe
51 // nodes: [ name, payload ]
52 PP_Var CreatePipeMessage(PepperInterface* ppapi, const char* pipe,
53                          const char* operation, PP_Var payload) {
54   VarInterface* var_iface = ppapi->GetVarInterface();
55   VarDictionaryInterface* dict_iface = ppapi->GetVarDictionaryInterface();
56
57   // Create a two element array containing the name of the message
58   // as the first element.  Its up to the caller the then set the
59   // second array element.
60   PP_Var message = dict_iface->Create();
61   PP_Var pipe_var = VarFromCStr(var_iface, pipe);
62   PP_Var operation_var = VarFromCStr(var_iface, operation);
63   PP_Var pipe_key = VarFromCStr(var_iface, "pipe");
64   PP_Var payload_key = VarFromCStr(var_iface, "payload");
65   PP_Var operation_key = VarFromCStr(var_iface, "operation");
66   dict_iface->Set(message, pipe_key, pipe_var);
67   dict_iface->Set(message, operation_key, operation_var);
68   dict_iface->Set(message, payload_key, payload);
69   var_iface->Release(pipe_var);
70   var_iface->Release(operation_var);
71   var_iface->Release(payload);
72   var_iface->Release(pipe_key);
73   var_iface->Release(payload_key);
74   var_iface->Release(operation_key);
75   return message;
76 }
77
78 // Helper function for creating "ack" message in format expected
79 // by jspipe nodes.
80 PP_Var CreateAckMessage(PepperInterface* ppapi, const char* pipe,
81                         int32_t count) {
82   return CreatePipeMessage(ppapi, pipe, "ack", PP_MakeInt32(count));
83 }
84
85 // Helper function for creating "write" message in format expected
86 // by jspipe nodes.
87 PP_Var CreateWriteMessage(PepperInterface* ppapi,
88                           const char* pipe,
89                           const char* string,
90                           int length=-1) {
91   VarArrayBufferInterface* buffer_iface = ppapi->GetVarArrayBufferInterface();
92
93   if (length == -1)
94     length = strlen(string);
95
96   PP_Var buffer = buffer_iface->Create(length);
97   memcpy(buffer_iface->Map(buffer), string, length);
98   buffer_iface->Unmap(buffer);
99
100   return CreatePipeMessage(ppapi, pipe, "write", buffer);
101 }
102
103 class JSPipeTest : public ::testing::Test {
104  public:
105   void SetUp() {
106     ASSERT_EQ(0, ki_push_state_for_testing());
107     ASSERT_EQ(0, ki_init_interface(&kp_, &ppapi_));
108   }
109
110   void TearDown() {
111     ki_uninit();
112   }
113
114  protected:
115   FakePepperInterface ppapi_;
116   KernelProxy kp_;
117 };
118
119 class JSPipeNodeTest : public ::testing::Test {
120  public:
121   JSPipeNodeTest() : fs_(&ppapi_) {}
122
123   void SetUp() {
124     name_ = "jspipe1";
125     ASSERT_EQ(0, fs_.Access(Path("/jspipe1"), R_OK | W_OK));
126     ASSERT_EQ(EACCES, fs_.Access(Path("/jspipe1"), X_OK));
127     ASSERT_EQ(0, fs_.Open(Path("/jspipe1"), O_RDWR, &pipe_dev_));
128     ASSERT_NE(NULL_NODE, pipe_dev_.get());
129   }
130
131   /**
132    * Create a PP_Var message in the same way that we expect
133    * JavaScript code to, and send it to the pipe using ioctl()
134    */
135   int JSPipeInject(const char* string, int length=-1) {
136     PP_Var message = CreateWriteMessage(&ppapi_, name_, string, length);
137
138     // Send the message via ioctl
139     int rtn = pipe_dev_->Ioctl(NACL_IOC_HANDLEMESSAGE, &message);
140
141     // Release message
142     ppapi_.GetVarInterface()->Release(message);
143     return rtn;
144   }
145
146   int JSPipeInjectAck(int32_t count) {
147     PP_Var message = CreateAckMessage(&ppapi_, name_, count);
148
149     // Send the message via ioctl
150     int rtn = pipe_dev_->Ioctl(NACL_IOC_HANDLEMESSAGE, &message);
151
152     // Release message
153     ppapi_.GetVarInterface()->Release(message);
154     return rtn;
155   }
156
157   // Verify the contents of the jspipe mesage, which should be
158   // {
159   //   "pipe": '<pipe_name>',
160   //   "operation": '<command_name>',
161   //   "payload": payload
162   // }
163   void VerifyPipeMessage(PP_Var message,
164                          const char* pipe_name,
165                          const char* operation,
166                          const char* payload,
167                          int payload_length,
168                          int32_t int_payload=0) {
169     VarArrayInterface* array_iface = ppapi_.GetVarArrayInterface();
170     VarDictionaryInterface* dict_iface = ppapi_.GetVarDictionaryInterface();
171     VarInterface* var_iface = ppapi_.GetVarInterface();
172     VarArrayBufferInterface* buffer_iface = ppapi_.GetVarArrayBufferInterface();
173
174     // Verify we have a dictionary with 3 keys
175     ASSERT_EQ(PP_VARTYPE_DICTIONARY, message.type);
176     PP_Var keys = dict_iface->GetKeys(message);
177     ASSERT_EQ(PP_VARTYPE_ARRAY, keys.type);
178     ASSERT_EQ(3, array_iface->GetLength(keys));
179     var_iface->Release(keys);
180
181     // Verify the keys
182     PP_Var key1 = VarFromCStr(var_iface, "pipe");
183     PP_Var key2 = VarFromCStr(var_iface, "operation");
184     PP_Var key3 = VarFromCStr(var_iface, "payload");
185
186     // Verify pipe name and operation values
187     PP_Var value1 = dict_iface->Get(message, key1);
188     ASSERT_STREQ(pipe_name, VarToString(var_iface, value1).c_str());
189     var_iface->Release(value1);
190     var_iface->Release(key1);
191
192     PP_Var value2 = dict_iface->Get(message, key2);
193     ASSERT_STREQ(operation, VarToString(var_iface, value2).c_str());
194     var_iface->Release(value2);
195     var_iface->Release(key2);
196
197     // Verify the payload
198     PP_Var payload_var = dict_iface->Get(message, key3);
199     if (payload != NULL) {
200       ASSERT_EQ(PP_VARTYPE_ARRAY_BUFFER, payload_var.type);
201       ASSERT_EQ(0, memcmp(payload, buffer_iface->Map(payload_var),
202                           payload_length));
203     } else {
204       ASSERT_EQ(PP_VARTYPE_INT32, payload_var.type);
205       ASSERT_EQ(int_payload, payload_var.value.as_int);
206     }
207     var_iface->Release(key3);
208     var_iface->Release(payload_var);
209   }
210
211  protected:
212   FakePepperInterface ppapi_;
213   DevFsForTesting fs_;
214   ScopedNode pipe_dev_;
215   const char* name_;
216 };
217
218 TEST(JSPipeTestBasic, MissingPepper) {
219   // Create a devfs filesystem without giving it any Pepper implemenation.
220   TypedFsFactory<DevFs> factory;
221   ScopedFilesystem fs;
222   FsInitArgs args(1);
223   factory.CreateFilesystem(args, &fs);
224   ScopedNode pipe_dev;
225   ASSERT_EQ(0, fs->Open(Path("/jspipe1"), O_RDWR, &pipe_dev));
226
227   // Writing to a pipe should return EIO because Pepper is missing.
228   HandleAttr attrs;
229   int written = -1;
230   ASSERT_EQ(EIO, pipe_dev->Write(attrs, "test", 4, &written));
231 }
232
233 TEST_F(JSPipeNodeTest, InvalidIoctl) {
234   // 123 is not a valid ioctl request.
235   EXPECT_EQ(EINVAL, pipe_dev_->Ioctl(123));
236 }
237
238 TEST_F(JSPipeNodeTest, JSPipeInput) {
239   std::string message("hello, how are you?\n");
240
241   // First we send some data into the pipe.  This is how messages
242   // from javascript are injected into the pipe nodes.
243   ASSERT_EQ(0, JSPipeInject(message.c_str()));
244
245   // Now we make buffer we'll read into.
246   // We fill the buffer and a backup buffer with arbitrary data
247   // and compare them after reading to make sure read doesn't
248   // clobber parts of the buffer it shouldn't.
249   int bytes_read;
250   char buffer[100];
251   char backup_buffer[100];
252   memset(buffer, 'a', sizeof(buffer));
253   memset(backup_buffer, 'a', sizeof(backup_buffer));
254
255   // We read a small chunk first to ensure it doesn't give us
256   // more than we ask for.
257   HandleAttr attrs;
258   ASSERT_EQ(0, pipe_dev_->Read(attrs, buffer, 5, &bytes_read));
259   EXPECT_EQ(5, bytes_read);
260   EXPECT_EQ(0, memcmp(message.data(), buffer, 5));
261   EXPECT_EQ(0, memcmp(buffer + 5, backup_buffer + 5, sizeof(buffer)-5));
262
263   // Now we ask for more data than is left in the pipe, to ensure
264   // it doesn't give us more than there is.
265   ASSERT_EQ(0, pipe_dev_->Read(attrs, buffer + 5, sizeof(buffer)-5,
266                                &bytes_read));
267   EXPECT_EQ(bytes_read, message.size() - 5);
268   EXPECT_EQ(0, memcmp(message.data(), buffer, message.size()));
269   EXPECT_EQ(0, memcmp(buffer + message.size(),
270                       backup_buffer + message.size(),
271                       100 - message.size()));
272 }
273
274 TEST_F(JSPipeNodeTest, JSPipeOutput) {
275   std::string message("hello");
276
277   int bytes_written = 999;
278   HandleAttr attrs;
279   ASSERT_EQ(0, pipe_dev_->Write(attrs, message.c_str(), message.size(),
280                                 &bytes_written));
281   ASSERT_EQ(message.size(), bytes_written);
282
283   FakeMessagingInterface* iface =
284       (FakeMessagingInterface*)ppapi_.GetMessagingInterface();
285
286   // Verify that exactly one message sent.
287   ASSERT_EQ(1, iface->messages.size());
288   PP_Var message_var = iface->messages[0];
289
290   // Verify the content of the message.
291   VerifyPipeMessage(message_var, "jspipe1", "write", message.c_str(),
292                     message.size());
293 }
294
295 TEST_F(JSPipeNodeTest, JSPipeOutputWithNulls) {
296   char message[20];
297   int message_len = sizeof(message);
298
299   // Construct a 20-byte  message containing the string 'hello' but with
300   // null chars on either end.
301   memset(message, 0 , message_len);
302   memcpy(message+10, "hello", 5);
303
304   int bytes_written = 999;
305   HandleAttr attrs;
306   EXPECT_EQ(0, pipe_dev_->Write(attrs, message, message_len, &bytes_written));
307   EXPECT_EQ(message_len, bytes_written);
308
309   // Verify that the correct messages was sent via PostMessage.
310   FakeMessagingInterface* iface =
311       (FakeMessagingInterface*)ppapi_.GetMessagingInterface();
312
313   // Verify that exaclty one message sent.
314   ASSERT_EQ(1, iface->messages.size());
315   PP_Var message_var = iface->messages[0];
316
317   // Verify the content of the message.
318   VerifyPipeMessage(message_var, "jspipe1", "write", message, message_len);
319 }
320
321 #define CHUNK_SIZE 678
322 TEST_F(JSPipeNodeTest, JSPipeOutputBuffer) {
323   int ospace_orig = -1;
324   ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETOSPACE, &ospace_orig));
325   ASSERT_GT(ospace_orig, 0);
326
327   HandleAttr attrs;
328   attrs.flags = O_NONBLOCK;
329   char* message = (char*)malloc(CHUNK_SIZE);
330
331   // Keep writing data until we block.
332   int total_written = 0;
333   while (1) {
334     int bytes_written;
335     // Write some data
336     int rtn = pipe_dev_->Write(attrs, message, CHUNK_SIZE, &bytes_written);
337     if (rtn != 0) {
338       ASSERT_EQ(EWOULDBLOCK, rtn);
339       int ospace = -1;
340       ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETOSPACE, &ospace));
341       ASSERT_EQ(0, ospace);
342       ASSERT_EQ(total_written, ospace_orig);
343       break;
344     }
345     total_written += bytes_written;
346   }
347
348   // At this point writes should always block
349   int bytes_written;
350   int rtn = pipe_dev_->Write(attrs, message, CHUNK_SIZE, &bytes_written);
351   ASSERT_EQ(EWOULDBLOCK, rtn);
352
353   // Now inject and ACK message from JavaScript.
354   ASSERT_EQ(0, JSPipeInjectAck(10));
355
356   // Now it should be possible to write 10 bytes to the pipe.
357   rtn = pipe_dev_->Write(attrs, message, CHUNK_SIZE, &bytes_written);
358   ASSERT_EQ(0, rtn);
359   ASSERT_EQ(10, bytes_written);
360
361   free(message);
362 }
363
364 TEST_F(JSPipeNodeTest, JSPipeInputBuffer) {
365   char* message = (char*)malloc(CHUNK_SIZE);
366   memset(message, 1, CHUNK_SIZE);
367
368   int ispace_orig = -1;
369   ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETISPACE, &ispace_orig));
370
371   // Keep injecting data until the ioctl fails
372   int total_written = 0;
373   while (1) {
374     int rtn = JSPipeInject(message, CHUNK_SIZE);
375     if (rtn != 0) {
376       ASSERT_LT(total_written, ispace_orig);
377       ASSERT_GT(total_written, ispace_orig - CHUNK_SIZE - 1);
378       break;
379     }
380     total_written += CHUNK_SIZE;
381   }
382
383   int ispace = -1;
384   ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETISPACE, &ispace));
385   ASSERT_EQ(0, ispace);
386
387   // Check that no messages have thus far been sent to JavaScript
388   FakeMessagingInterface* iface =
389       (FakeMessagingInterface*)ppapi_.GetMessagingInterface();
390   ASSERT_EQ(0, iface->messages.size());
391
392   // Read some data from the pipe, which should trigger an ack message
393   int bytes_read = -1;
394   HandleAttr attrs;
395   ASSERT_EQ(0, pipe_dev_->Read(attrs, message, 5, &bytes_read));
396   ASSERT_EQ(5, bytes_read);
397
398   // Verify that an ack was sent to JavaScript
399   ASSERT_EQ(1, iface->messages.size());
400   PP_Var message_var = iface->messages[0];
401   VerifyPipeMessage(message_var, "jspipe1", "ack", NULL, 0, 5);
402
403   free(message);
404 }
405
406 // Returns:
407 //   0 -> Not readable
408 //   1 -> Readable
409 //  -1 -> Error occured
410 int IsReadable(int fd) {
411   struct timeval timeout = {0, 0};
412   fd_set readfds;
413   fd_set errorfds;
414   FD_ZERO(&readfds);
415   FD_ZERO(&errorfds);
416   FD_SET(fd, &readfds);
417   FD_SET(fd, &errorfds);
418   int rtn = ki_select(fd + 1, &readfds, NULL, &errorfds, &timeout);
419   if (rtn == 0)
420     return 0;  // not readable
421   if (rtn != 1)
422     return -1;  // error
423   if (FD_ISSET(fd, &errorfds))
424     return -2;  // error
425   if (!FD_ISSET(fd, &readfds))
426     return -3;  // error
427   return 1;     // readable
428 }
429
430 TEST_F(JSPipeTest, JSPipeSelect) {
431   struct timeval timeout;
432   fd_set readfds;
433   fd_set writefds;
434   fd_set errorfds;
435
436   int pipe_fd = ki_open("/dev/jspipe1", O_RDONLY);
437   ASSERT_GT(pipe_fd, 0) << "jspipe1 open failed: " << errno;
438
439   FD_ZERO(&readfds);
440   FD_ZERO(&errorfds);
441   FD_SET(pipe_fd, &readfds);
442   FD_SET(pipe_fd, &errorfds);
443   // 10 millisecond timeout
444   timeout.tv_sec = 0;
445   timeout.tv_usec = 10 * 1000;
446   // Should timeout when no input is available.
447   int rtn = ki_select(pipe_fd + 1, &readfds, NULL, &errorfds, &timeout);
448   ASSERT_EQ(0, rtn) << "select failed: " << rtn << " err=" << strerror(errno);
449   ASSERT_FALSE(FD_ISSET(pipe_fd, &readfds));
450   ASSERT_FALSE(FD_ISSET(pipe_fd, &errorfds));
451
452   FD_ZERO(&readfds);
453   FD_ZERO(&writefds);
454   FD_ZERO(&errorfds);
455   FD_SET(pipe_fd, &readfds);
456   FD_SET(pipe_fd, &writefds);
457   FD_SET(pipe_fd, &errorfds);
458   // Pipe should be writable on startup.
459   rtn = ki_select(pipe_fd + 1, &readfds, &writefds, &errorfds, NULL);
460   ASSERT_EQ(1, rtn);
461   ASSERT_TRUE(FD_ISSET(pipe_fd, &writefds));
462   ASSERT_FALSE(FD_ISSET(pipe_fd, &readfds));
463   ASSERT_FALSE(FD_ISSET(pipe_fd, &errorfds));
464
465   // Send 4 bytes to the pipe via ioctl
466   PP_Var message = CreateWriteMessage(&ppapi_, "jspipe1", "test");
467   ASSERT_EQ(0, ki_ioctl_wrapper(pipe_fd, NACL_IOC_HANDLEMESSAGE, &message));
468   ppapi_.GetVarInterface()->Release(message);
469
470   // Pipe should now be readable
471   ASSERT_EQ(1, IsReadable(pipe_fd));
472
473   ki_close(pipe_fd);
474 }
475
476 }