jspipe_test.cc revision 010d83a9304c5a91596085d917d248abff47903a
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
24using namespace nacl_io;
25
26namespace {
27
28class JSPipeTest : public ::testing::Test {
29 public:
30  JSPipeTest() : fs_(&pepper_) {}
31
32  void SetUp() {
33    ASSERT_EQ(0, ki_push_state_for_testing());
34    ASSERT_EQ(0, ki_init(&kp_));
35    ASSERT_EQ(0, fs_.Access(Path("/jspipe1"), R_OK | W_OK));
36    ASSERT_EQ(EACCES, fs_.Access(Path("/jspipe1"), X_OK));
37    ASSERT_EQ(0, fs_.Open(Path("/jspipe1"), O_RDWR, &pipe_dev_));
38    ASSERT_NE(NULL_NODE, pipe_dev_.get());
39  }
40
41  void TearDown() { ki_uninit(); }
42
43 protected:
44  KernelProxy kp_;
45  FakePepperInterface pepper_;
46  DevFsForTesting fs_;
47  ScopedNode pipe_dev_;
48};
49
50TEST_F(JSPipeTest, InvalidIoctl) {
51  // 123 is not a valid ioctl request.
52  EXPECT_EQ(EINVAL, pipe_dev_->Ioctl(123));
53}
54
55TEST_F(JSPipeTest, JSPipeInput) {
56  // First we send some data into the pipe.  This is how messages
57  // from javascript are injected into the pipe nodes.
58  std::string message("hello, how are you?\n");
59  struct tioc_nacl_input_string packaged_message;
60  packaged_message.length = message.size();
61  packaged_message.buffer = message.data();
62  ASSERT_EQ(0, pipe_dev_->Ioctl(TIOCNACLINPUT, &packaged_message));
63
64  // Now we make buffer we'll read into.
65  // We fill the buffer and a backup buffer with arbitrary data
66  // and compare them after reading to make sure read doesn't
67  // clobber parts of the buffer it shouldn't.
68  int bytes_read;
69  char buffer[100];
70  char backup_buffer[100];
71  memset(buffer, 'a', sizeof(buffer));
72  memset(backup_buffer, 'a', sizeof(backup_buffer));
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  ASSERT_EQ(0, pipe_dev_->Read(attrs, buffer, 5, &bytes_read));
78  EXPECT_EQ(5, bytes_read);
79  EXPECT_EQ(0, memcmp(message.data(), buffer, 5));
80  EXPECT_EQ(0, memcmp(buffer + 5, backup_buffer + 5, sizeof(buffer)-5));
81
82  // Now we ask for more data than is left in the pipe, to ensure
83  // it doesn't give us more than there is.
84  ASSERT_EQ(0, pipe_dev_->Read(attrs, buffer + 5, sizeof(buffer)-5,
85                               &bytes_read));
86  EXPECT_EQ(bytes_read, message.size() - 5);
87  EXPECT_EQ(0, memcmp(message.data(), buffer, message.size()));
88  EXPECT_EQ(0, memcmp(buffer + message.size(),
89                      backup_buffer + message.size(),
90                      100 - message.size()));
91}
92
93TEST_F(JSPipeTest, JSPipeOutput) {
94  const char* message = "hello";
95  const int message_len = strlen(message);
96
97  int bytes_written = 999;
98  HandleAttr attrs;
99  ASSERT_EQ(0, pipe_dev_->Write(attrs, message, message_len, &bytes_written));
100  ASSERT_EQ(message_len, bytes_written);
101
102  // Verify that the correct messages was sent via PostMessage.
103  FakeMessagingInterface* iface =
104      (FakeMessagingInterface*)fs_.ppapi()->GetMessagingInterface();
105  VarArrayInterface* array_iface = fs_.ppapi()->GetVarArrayInterface();
106  VarInterface* var_iface = fs_.ppapi()->GetVarInterface();
107  VarArrayBufferInterface* buffer_iface =
108      fs_.ppapi()->GetVarArrayBufferInterface();
109
110  // Verify that exaclty one message was sent of type PP_VARTYPE_ARRAY
111  ASSERT_EQ(1, iface->messages.size());
112  PP_Var array = iface->messages[0];
113  ASSERT_EQ(PP_VARTYPE_ARRAY, array.type);
114
115  // Verify that the array contains two element, the prefix,
116  // and an ArrayBuffer containing the message.
117  ASSERT_EQ(2, array_iface->GetLength(array));
118  PP_Var item0 = array_iface->Get(array, 0);
119  PP_Var item1 = array_iface->Get(array, 1);
120  ASSERT_EQ(PP_VARTYPE_STRING, item0.type);
121  ASSERT_EQ(PP_VARTYPE_ARRAY_BUFFER, item1.type);
122  uint32_t len = 0;
123  const char* item0_string = var_iface->VarToUtf8(item0, &len);
124  ASSERT_STREQ("jspipe1", std::string(item0_string, len).c_str());
125  ASSERT_EQ(0, memcmp(message, buffer_iface->Map(item1), strlen(message)));
126  var_iface->Release(item0);
127  var_iface->Release(item1);
128}
129
130TEST_F(JSPipeTest, JSPipeOutputWithNulls) {
131  char message[20];
132  int message_len = sizeof(message);
133
134  // Construct a 20-byte  message containing the string 'hello' but with
135  // null chars on either end.
136  memset(message, 0 , message_len);
137  memcpy(message+10, "hello", 5);
138
139  int bytes_written = 999;
140  HandleAttr attrs;
141  EXPECT_EQ(0, pipe_dev_->Write(attrs, message, message_len, &bytes_written));
142  EXPECT_EQ(message_len, bytes_written);
143
144  // Verify that the correct messages was sent via PostMessage.
145  FakeMessagingInterface* iface =
146      (FakeMessagingInterface*)fs_.ppapi()->GetMessagingInterface();
147  VarArrayInterface* array_iface = fs_.ppapi()->GetVarArrayInterface();
148  VarInterface* var_iface = fs_.ppapi()->GetVarInterface();
149  VarArrayBufferInterface* buffer_iface =
150      fs_.ppapi()->GetVarArrayBufferInterface();
151
152  // Verify that exactly one message was sent of type PP_VARTYPE_ARRAY
153  EXPECT_EQ(1, iface->messages.size());
154  PP_Var array = iface->messages[0];
155  ASSERT_EQ(PP_VARTYPE_ARRAY, array.type);
156
157  // Verify that the array contains two element, the prefix,
158  // and an ArrayBuffer containing the message.
159  ASSERT_EQ(2, array_iface->GetLength(array));
160  PP_Var item0 = array_iface->Get(array, 0);
161  PP_Var item1 = array_iface->Get(array, 1);
162  ASSERT_EQ(PP_VARTYPE_STRING, item0.type);
163  ASSERT_EQ(PP_VARTYPE_ARRAY_BUFFER, item1.type);
164  uint32_t len = 0;
165  ASSERT_STREQ("jspipe1", var_iface->VarToUtf8(item0, &len));
166  ASSERT_EQ(0, memcmp(message, buffer_iface->Map(item1), strlen(message)));
167  var_iface->Release(item0);
168  var_iface->Release(item1);
169}
170
171static int ki_ioctl_wrapper(int fd, int request, ...) {
172  va_list ap;
173  va_start(ap, request);
174  int rtn = ki_ioctl(fd, request, ap);
175  va_end(ap);
176  return rtn;
177}
178
179static int JSPipeWrite(int fd, const char* string) {
180  struct tioc_nacl_input_string input;
181  input.buffer = string;
182  input.length = strlen(input.buffer);
183  return ki_ioctl_wrapper(fd, TIOCNACLINPUT, &input);
184}
185
186// Returns:
187//   0 -> Not readable
188//   1 -> Readable
189//  -1 -> Error occured
190static int IsReadable(int fd) {
191  struct timeval timeout = {0, 0};
192  fd_set readfds;
193  fd_set errorfds;
194  FD_ZERO(&readfds);
195  FD_ZERO(&errorfds);
196  FD_SET(fd, &readfds);
197  FD_SET(fd, &errorfds);
198  int rtn = ki_select(fd + 1, &readfds, NULL, &errorfds, &timeout);
199  if (rtn == 0)
200    return 0;  // not readable
201  if (rtn != 1)
202    return -1;  // error
203  if (FD_ISSET(fd, &errorfds))
204    return -2;  // error
205  if (!FD_ISSET(fd, &readfds))
206    return -3;  // error
207  return 1;     // readable
208}
209
210TEST_F(JSPipeTest, JSPipeSelect) {
211  struct timeval timeout;
212  fd_set readfds;
213  fd_set writefds;
214  fd_set errorfds;
215
216  int pipe_fd = ki_open("/dev/jspipe1", O_RDONLY);
217  ASSERT_GT(pipe_fd, 0) << "jspipe1 open failed: " << errno;
218
219  FD_ZERO(&readfds);
220  FD_ZERO(&errorfds);
221  FD_SET(pipe_fd, &readfds);
222  FD_SET(pipe_fd, &errorfds);
223  // 10 millisecond timeout
224  timeout.tv_sec = 0;
225  timeout.tv_usec = 10 * 1000;
226  // Should timeout when no input is available.
227  int rtn = ki_select(pipe_fd + 1, &readfds, NULL, &errorfds, &timeout);
228  ASSERT_EQ(0, rtn) << "select failed: " << rtn << " err=" << strerror(errno);
229  ASSERT_FALSE(FD_ISSET(pipe_fd, &readfds));
230  ASSERT_FALSE(FD_ISSET(pipe_fd, &errorfds));
231
232  FD_ZERO(&readfds);
233  FD_ZERO(&writefds);
234  FD_ZERO(&errorfds);
235  FD_SET(pipe_fd, &readfds);
236  FD_SET(pipe_fd, &writefds);
237  FD_SET(pipe_fd, &errorfds);
238  // Pipe should be writable on startup.
239  rtn = ki_select(pipe_fd + 1, &readfds, &writefds, &errorfds, NULL);
240  ASSERT_EQ(1, rtn);
241  ASSERT_TRUE(FD_ISSET(pipe_fd, &writefds));
242  ASSERT_FALSE(FD_ISSET(pipe_fd, &readfds));
243  ASSERT_FALSE(FD_ISSET(pipe_fd, &errorfds));
244
245  // Send 4 bytes to the pipe
246  ASSERT_EQ(0, JSPipeWrite(pipe_fd, "test"));
247
248  // Pipe should now be readable
249  ASSERT_EQ(1, IsReadable(pipe_fd));
250
251  ki_close(pipe_fd);
252}
253
254}
255