Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / tests / nacl_io_test / jspipe_test.cc
blob0a8e867e7211ad9e6eea641fea073faf82a99960
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.
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 "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"
24 using namespace nacl_io;
26 namespace {
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;
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);
46 PP_Var VarFromCStr(VarInterface* iface, const char* string) {
47 return iface->VarFromUtf8(string, strlen(string));
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();
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;
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));
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();
93 if (length == -1)
94 length = strlen(string);
96 PP_Var buffer = buffer_iface->Create(length);
97 memcpy(buffer_iface->Map(buffer), string, length);
98 buffer_iface->Unmap(buffer);
100 return CreatePipeMessage(ppapi, pipe, "write", buffer);
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_));
110 void TearDown() {
111 ki_uninit();
114 protected:
115 FakePepperInterface ppapi_;
116 KernelProxy kp_;
119 class JSPipeNodeTest : public ::testing::Test {
120 public:
121 JSPipeNodeTest() : fs_(&ppapi_) {}
123 void SetUp() {
124 name_ = "jspipe1";
125 ASSERT_EQ(0, fs_.Open(Path("/jspipe1"), O_RDWR, &pipe_dev_));
126 ASSERT_NE(NULL_NODE, pipe_dev_.get());
127 struct stat buf;
128 ASSERT_EQ(0, pipe_dev_->GetStat(&buf));
129 ASSERT_EQ(S_IRUSR | S_IWUSR, buf.st_mode & S_IRWXU);
133 * Create a PP_Var message in the same way that we expect
134 * JavaScript code to, and send it to the pipe using ioctl()
136 int JSPipeInject(const char* string, int length=-1) {
137 PP_Var message = CreateWriteMessage(&ppapi_, name_, string, length);
139 // Send the message via ioctl
140 int rtn = pipe_dev_->Ioctl(NACL_IOC_HANDLEMESSAGE, &message);
142 // Release message
143 ppapi_.GetVarInterface()->Release(message);
144 return rtn;
147 int JSPipeInjectAck(int32_t count) {
148 PP_Var message = CreateAckMessage(&ppapi_, name_, count);
150 // Send the message via ioctl
151 int rtn = pipe_dev_->Ioctl(NACL_IOC_HANDLEMESSAGE, &message);
153 // Release message
154 ppapi_.GetVarInterface()->Release(message);
155 return rtn;
158 // Verify the contents of the jspipe mesage, which should be
159 // {
160 // "pipe": '<pipe_name>',
161 // "operation": '<command_name>',
162 // "payload": payload
163 // }
164 void VerifyPipeMessage(PP_Var message,
165 const char* pipe_name,
166 const char* operation,
167 const char* payload,
168 int payload_length,
169 int32_t int_payload=0) {
170 VarArrayInterface* array_iface = ppapi_.GetVarArrayInterface();
171 VarDictionaryInterface* dict_iface = ppapi_.GetVarDictionaryInterface();
172 VarInterface* var_iface = ppapi_.GetVarInterface();
173 VarArrayBufferInterface* buffer_iface = ppapi_.GetVarArrayBufferInterface();
175 // Verify we have a dictionary with 3 keys
176 ASSERT_EQ(PP_VARTYPE_DICTIONARY, message.type);
177 PP_Var keys = dict_iface->GetKeys(message);
178 ASSERT_EQ(PP_VARTYPE_ARRAY, keys.type);
179 ASSERT_EQ(3, array_iface->GetLength(keys));
180 var_iface->Release(keys);
182 // Verify the keys
183 PP_Var key1 = VarFromCStr(var_iface, "pipe");
184 PP_Var key2 = VarFromCStr(var_iface, "operation");
185 PP_Var key3 = VarFromCStr(var_iface, "payload");
187 // Verify pipe name and operation values
188 PP_Var value1 = dict_iface->Get(message, key1);
189 ASSERT_STREQ(pipe_name, VarToString(var_iface, value1).c_str());
190 var_iface->Release(value1);
191 var_iface->Release(key1);
193 PP_Var value2 = dict_iface->Get(message, key2);
194 ASSERT_STREQ(operation, VarToString(var_iface, value2).c_str());
195 var_iface->Release(value2);
196 var_iface->Release(key2);
198 // Verify the payload
199 PP_Var payload_var = dict_iface->Get(message, key3);
200 if (payload != NULL) {
201 ASSERT_EQ(PP_VARTYPE_ARRAY_BUFFER, payload_var.type);
202 ASSERT_EQ(0, memcmp(payload, buffer_iface->Map(payload_var),
203 payload_length));
204 } else {
205 ASSERT_EQ(PP_VARTYPE_INT32, payload_var.type);
206 ASSERT_EQ(int_payload, payload_var.value.as_int);
208 var_iface->Release(key3);
209 var_iface->Release(payload_var);
212 protected:
213 FakePepperInterface ppapi_;
214 DevFsForTesting fs_;
215 ScopedNode pipe_dev_;
216 const char* name_;
219 TEST(JSPipeTestBasic, MissingPepper) {
220 // Create a devfs filesystem without giving it any Pepper implemenation.
221 TypedFsFactory<DevFs> factory;
222 ScopedFilesystem fs;
223 FsInitArgs args(1);
224 factory.CreateFilesystem(args, &fs);
225 ScopedNode pipe_dev;
226 ASSERT_EQ(0, fs->Open(Path("/jspipe1"), O_RDWR, &pipe_dev));
228 // Writing to a pipe should return EIO because Pepper is missing.
229 HandleAttr attrs;
230 int written = -1;
231 ASSERT_EQ(EIO, pipe_dev->Write(attrs, "test", 4, &written));
234 TEST_F(JSPipeNodeTest, InvalidIoctl) {
235 // 123 is not a valid ioctl request.
236 EXPECT_EQ(EINVAL, pipe_dev_->Ioctl(123));
239 TEST_F(JSPipeNodeTest, JSPipeInput) {
240 std::string message("hello, how are you?\n");
242 // First we send some data into the pipe. This is how messages
243 // from javascript are injected into the pipe nodes.
244 ASSERT_EQ(0, JSPipeInject(message.c_str()));
246 // Now we make buffer we'll read into.
247 // We fill the buffer and a backup buffer with arbitrary data
248 // and compare them after reading to make sure read doesn't
249 // clobber parts of the buffer it shouldn't.
250 int bytes_read;
251 char buffer[100];
252 char backup_buffer[100];
253 memset(buffer, 'a', sizeof(buffer));
254 memset(backup_buffer, 'a', sizeof(backup_buffer));
256 // We read a small chunk first to ensure it doesn't give us
257 // more than we ask for.
258 HandleAttr attrs;
259 ASSERT_EQ(0, pipe_dev_->Read(attrs, buffer, 5, &bytes_read));
260 EXPECT_EQ(5, bytes_read);
261 EXPECT_EQ(0, memcmp(message.data(), buffer, 5));
262 EXPECT_EQ(0, memcmp(buffer + 5, backup_buffer + 5, sizeof(buffer)-5));
264 // Now we ask for more data than is left in the pipe, to ensure
265 // it doesn't give us more than there is.
266 ASSERT_EQ(0, pipe_dev_->Read(attrs, buffer + 5, sizeof(buffer)-5,
267 &bytes_read));
268 EXPECT_EQ(bytes_read, message.size() - 5);
269 EXPECT_EQ(0, memcmp(message.data(), buffer, message.size()));
270 EXPECT_EQ(0, memcmp(buffer + message.size(),
271 backup_buffer + message.size(),
272 100 - message.size()));
275 TEST_F(JSPipeNodeTest, JSPipeOutput) {
276 std::string message("hello");
278 int bytes_written = 999;
279 HandleAttr attrs;
280 ASSERT_EQ(0, pipe_dev_->Write(attrs, message.c_str(), message.size(),
281 &bytes_written));
282 ASSERT_EQ(message.size(), bytes_written);
284 FakeMessagingInterface* iface =
285 (FakeMessagingInterface*)ppapi_.GetMessagingInterface();
287 // Verify that exactly one message sent.
288 ASSERT_EQ(1, iface->messages.size());
289 PP_Var message_var = iface->messages[0];
291 // Verify the content of the message.
292 VerifyPipeMessage(message_var, "jspipe1", "write", message.c_str(),
293 message.size());
296 TEST_F(JSPipeNodeTest, JSPipeOutputWithNulls) {
297 char message[20];
298 int message_len = sizeof(message);
300 // Construct a 20-byte message containing the string 'hello' but with
301 // null chars on either end.
302 memset(message, 0 , message_len);
303 memcpy(message+10, "hello", 5);
305 int bytes_written = 999;
306 HandleAttr attrs;
307 EXPECT_EQ(0, pipe_dev_->Write(attrs, message, message_len, &bytes_written));
308 EXPECT_EQ(message_len, bytes_written);
310 // Verify that the correct messages was sent via PostMessage.
311 FakeMessagingInterface* iface =
312 (FakeMessagingInterface*)ppapi_.GetMessagingInterface();
314 // Verify that exaclty one message sent.
315 ASSERT_EQ(1, iface->messages.size());
316 PP_Var message_var = iface->messages[0];
318 // Verify the content of the message.
319 VerifyPipeMessage(message_var, "jspipe1", "write", message, message_len);
322 #define CHUNK_SIZE 678
323 TEST_F(JSPipeNodeTest, JSPipeOutputBuffer) {
324 int ospace_orig = -1;
325 ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETOSPACE, &ospace_orig));
326 ASSERT_GT(ospace_orig, 0);
328 HandleAttr attrs;
329 attrs.flags = O_NONBLOCK;
330 char* message = (char*)malloc(CHUNK_SIZE);
332 // Keep writing data until we block.
333 int total_written = 0;
334 while (1) {
335 int bytes_written;
336 // Write some data
337 int rtn = pipe_dev_->Write(attrs, message, CHUNK_SIZE, &bytes_written);
338 if (rtn != 0) {
339 ASSERT_EQ(EWOULDBLOCK, rtn);
340 int ospace = -1;
341 ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETOSPACE, &ospace));
342 ASSERT_EQ(0, ospace);
343 ASSERT_EQ(total_written, ospace_orig);
344 break;
346 total_written += bytes_written;
349 // At this point writes should always block
350 int bytes_written;
351 int rtn = pipe_dev_->Write(attrs, message, CHUNK_SIZE, &bytes_written);
352 ASSERT_EQ(EWOULDBLOCK, rtn);
354 // Now inject and ACK message from JavaScript.
355 ASSERT_EQ(0, JSPipeInjectAck(10));
357 // Now it should be possible to write 10 bytes to the pipe.
358 rtn = pipe_dev_->Write(attrs, message, CHUNK_SIZE, &bytes_written);
359 ASSERT_EQ(0, rtn);
360 ASSERT_EQ(10, bytes_written);
362 free(message);
365 TEST_F(JSPipeNodeTest, JSPipeInputBuffer) {
366 char* message = (char*)malloc(CHUNK_SIZE);
367 memset(message, 1, CHUNK_SIZE);
369 int ispace_orig = -1;
370 ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETISPACE, &ispace_orig));
372 // Keep injecting data until the ioctl fails
373 int total_written = 0;
374 while (1) {
375 int rtn = JSPipeInject(message, CHUNK_SIZE);
376 if (rtn != 0) {
377 ASSERT_LT(total_written, ispace_orig);
378 ASSERT_GT(total_written, ispace_orig - CHUNK_SIZE - 1);
379 break;
381 total_written += CHUNK_SIZE;
384 int ispace = -1;
385 ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETISPACE, &ispace));
386 ASSERT_EQ(0, ispace);
388 // Check that no messages have thus far been sent to JavaScript
389 FakeMessagingInterface* iface =
390 (FakeMessagingInterface*)ppapi_.GetMessagingInterface();
391 ASSERT_EQ(0, iface->messages.size());
393 // Read some data from the pipe, which should trigger an ack message
394 int bytes_read = -1;
395 HandleAttr attrs;
396 ASSERT_EQ(0, pipe_dev_->Read(attrs, message, 5, &bytes_read));
397 ASSERT_EQ(5, bytes_read);
399 // Verify that an ack was sent to JavaScript
400 ASSERT_EQ(1, iface->messages.size());
401 PP_Var message_var = iface->messages[0];
402 VerifyPipeMessage(message_var, "jspipe1", "ack", NULL, 0, 5);
404 free(message);
407 // Returns:
408 // 0 -> Not readable
409 // 1 -> Readable
410 // -1 -> Error occured
411 int IsReadable(int fd) {
412 struct timeval timeout = {0, 0};
413 fd_set readfds;
414 fd_set errorfds;
415 FD_ZERO(&readfds);
416 FD_ZERO(&errorfds);
417 FD_SET(fd, &readfds);
418 FD_SET(fd, &errorfds);
419 int rtn = ki_select(fd + 1, &readfds, NULL, &errorfds, &timeout);
420 if (rtn == 0)
421 return 0; // not readable
422 if (rtn != 1)
423 return -1; // error
424 if (FD_ISSET(fd, &errorfds))
425 return -2; // error
426 if (!FD_ISSET(fd, &readfds))
427 return -3; // error
428 return 1; // readable
431 TEST_F(JSPipeTest, JSPipeSelect) {
432 struct timeval timeout;
433 fd_set readfds;
434 fd_set writefds;
435 fd_set errorfds;
437 int pipe_fd = ki_open("/dev/jspipe1", O_RDONLY, 0);
438 ASSERT_GT(pipe_fd, 0) << "jspipe1 open failed: " << errno;
440 FD_ZERO(&readfds);
441 FD_ZERO(&errorfds);
442 FD_SET(pipe_fd, &readfds);
443 FD_SET(pipe_fd, &errorfds);
444 // 10 millisecond timeout
445 timeout.tv_sec = 0;
446 timeout.tv_usec = 10 * 1000;
447 // Should timeout when no input is available.
448 int rtn = ki_select(pipe_fd + 1, &readfds, NULL, &errorfds, &timeout);
449 ASSERT_EQ(0, rtn) << "select failed: " << rtn << " err=" << strerror(errno);
450 ASSERT_FALSE(FD_ISSET(pipe_fd, &readfds));
451 ASSERT_FALSE(FD_ISSET(pipe_fd, &errorfds));
453 FD_ZERO(&readfds);
454 FD_ZERO(&writefds);
455 FD_ZERO(&errorfds);
456 FD_SET(pipe_fd, &readfds);
457 FD_SET(pipe_fd, &writefds);
458 FD_SET(pipe_fd, &errorfds);
459 // Pipe should be writable on startup.
460 rtn = ki_select(pipe_fd + 1, &readfds, &writefds, &errorfds, NULL);
461 ASSERT_EQ(1, rtn);
462 ASSERT_TRUE(FD_ISSET(pipe_fd, &writefds));
463 ASSERT_FALSE(FD_ISSET(pipe_fd, &readfds));
464 ASSERT_FALSE(FD_ISSET(pipe_fd, &errorfds));
466 // Send 4 bytes to the pipe via ioctl
467 PP_Var message = CreateWriteMessage(&ppapi_, "jspipe1", "test");
468 ASSERT_EQ(0, ki_ioctl_wrapper(pipe_fd, NACL_IOC_HANDLEMESSAGE, &message));
469 ppapi_.GetVarInterface()->Release(message);
471 // Pipe should now be readable
472 ASSERT_EQ(1, IsReadable(pipe_fd));
474 ki_close(pipe_fd);