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.
9 #include <sys/select.h>
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
;
28 // Helper function for calling ki_ioctl without having
29 // to construct a va_list.
30 int ki_ioctl_wrapper(int fd
, int request
, ...) {
32 va_start(ap
, request
);
33 int rtn
= ki_ioctl(fd
, request
, ap
);
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
);
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
);
78 // Helper function for creating "ack" message in format expected
80 PP_Var
CreateAckMessage(PepperInterface
* ppapi
, const char* pipe
,
82 return CreatePipeMessage(ppapi
, pipe
, "ack", PP_MakeInt32(count
));
85 // Helper function for creating "write" message in format expected
87 PP_Var
CreateWriteMessage(PepperInterface
* ppapi
,
91 VarArrayBufferInterface
* buffer_iface
= ppapi
->GetVarArrayBufferInterface();
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
{
106 ASSERT_EQ(0, ki_push_state_for_testing());
107 ASSERT_EQ(0, ki_init_interface(&kp_
, &ppapi_
));
115 FakePepperInterface ppapi_
;
119 class JSPipeNodeTest
: public ::testing::Test
{
121 JSPipeNodeTest() : fs_(&ppapi_
) {}
125 ASSERT_EQ(0, fs_
.Open(Path("/jspipe1"), O_RDWR
, &pipe_dev_
));
126 ASSERT_NE(NULL_NODE
, pipe_dev_
.get());
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
);
143 ppapi_
.GetVarInterface()->Release(message
);
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
);
154 ppapi_
.GetVarInterface()->Release(message
);
158 // Verify the contents of the jspipe mesage, which should be
160 // "pipe": '<pipe_name>',
161 // "operation": '<command_name>',
162 // "payload": payload
164 void VerifyPipeMessage(PP_Var message
,
165 const char* pipe_name
,
166 const char* operation
,
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
);
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
),
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
);
213 FakePepperInterface ppapi_
;
215 ScopedNode pipe_dev_
;
219 TEST(JSPipeTestBasic
, MissingPepper
) {
220 // Create a devfs filesystem without giving it any Pepper implemenation.
221 TypedFsFactory
<DevFs
> factory
;
224 factory
.CreateFilesystem(args
, &fs
);
226 ASSERT_EQ(0, fs
->Open(Path("/jspipe1"), O_RDWR
, &pipe_dev
));
228 // Writing to a pipe should return EIO because Pepper is missing.
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.
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.
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,
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;
280 ASSERT_EQ(0, pipe_dev_
->Write(attrs
, message
.c_str(), message
.size(),
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(),
296 TEST_F(JSPipeNodeTest
, JSPipeOutputWithNulls
) {
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;
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);
329 attrs
.flags
= O_NONBLOCK
;
330 char* message
= (char*)malloc(CHUNK_SIZE
);
332 // Keep writing data until we block.
333 int total_written
= 0;
337 int rtn
= pipe_dev_
->Write(attrs
, message
, CHUNK_SIZE
, &bytes_written
);
339 ASSERT_EQ(EWOULDBLOCK
, rtn
);
341 ASSERT_EQ(0, pipe_dev_
->Ioctl(NACL_IOC_PIPE_GETOSPACE
, &ospace
));
342 ASSERT_EQ(0, ospace
);
343 ASSERT_EQ(total_written
, ospace_orig
);
346 total_written
+= bytes_written
;
349 // At this point writes should always block
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
);
360 ASSERT_EQ(10, bytes_written
);
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;
375 int rtn
= JSPipeInject(message
, CHUNK_SIZE
);
377 ASSERT_LT(total_written
, ispace_orig
);
378 ASSERT_GT(total_written
, ispace_orig
- CHUNK_SIZE
- 1);
381 total_written
+= CHUNK_SIZE
;
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
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);
410 // -1 -> Error occured
411 int IsReadable(int fd
) {
412 struct timeval timeout
= {0, 0};
417 FD_SET(fd
, &readfds
);
418 FD_SET(fd
, &errorfds
);
419 int rtn
= ki_select(fd
+ 1, &readfds
, NULL
, &errorfds
, &timeout
);
421 return 0; // not readable
424 if (FD_ISSET(fd
, &errorfds
))
426 if (!FD_ISSET(fd
, &readfds
))
428 return 1; // readable
431 TEST_F(JSPipeTest
, JSPipeSelect
) {
432 struct timeval timeout
;
437 int pipe_fd
= ki_open("/dev/jspipe1", O_RDONLY
, 0);
438 ASSERT_GT(pipe_fd
, 0) << "jspipe1 open failed: " << errno
;
442 FD_SET(pipe_fd
, &readfds
);
443 FD_SET(pipe_fd
, &errorfds
);
444 // 10 millisecond timeout
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
));
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
);
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
));