| Index: native_client_sdk/src/tests/nacl_io_test/jspipe_test.cc
|
| diff --git a/native_client_sdk/src/tests/nacl_io_test/jspipe_test.cc b/native_client_sdk/src/tests/nacl_io_test/jspipe_test.cc
|
| index b14bf1844ebf3a0127cc9a78f58d8ef05d65095a..610aadc543d76a42626fea88742ecae6799e86d1 100644
|
| --- a/native_client_sdk/src/tests/nacl_io_test/jspipe_test.cc
|
| +++ b/native_client_sdk/src/tests/nacl_io_test/jspipe_test.cc
|
| @@ -25,41 +25,218 @@ using namespace nacl_io;
|
|
|
| namespace {
|
|
|
| +// Helper function for calling ki_ioctl without having
|
| +// to construct a va_list.
|
| +int ki_ioctl_wrapper(int fd, int request, ...) {
|
| + va_list ap;
|
| + va_start(ap, request);
|
| + int rtn = ki_ioctl(fd, request, ap);
|
| + va_end(ap);
|
| + return rtn;
|
| +}
|
| +
|
| +// Helper function for converting PP_Var to C++ string
|
| +std::string VarToString(VarInterface* var_iface, PP_Var var) {
|
| + EXPECT_EQ(PP_VARTYPE_STRING, var.type);
|
| + uint32_t len = 0;
|
| + const char* str = var_iface->VarToUtf8(var, &len);
|
| + return std::string(str, len);
|
| +}
|
| +
|
| +PP_Var VarFromCStr(VarInterface* iface, const char* string) {
|
| + return iface->VarFromUtf8(string, strlen(string));
|
| +}
|
| +
|
| +// Helper function for creating message in the format expected by jspipe
|
| +// nodes: [ name, payload ]
|
| +PP_Var CreatePipeMessage(PepperInterface* ppapi, const char* pipe,
|
| + const char* operation, PP_Var payload) {
|
| + VarInterface* var_iface = ppapi->GetVarInterface();
|
| + VarDictionaryInterface* dict_iface = ppapi->GetVarDictionaryInterface();
|
| +
|
| + // Create a two element array containing the name of the message
|
| + // as the first element. Its up to the caller the then set the
|
| + // second array element.
|
| + PP_Var message = dict_iface->Create();
|
| + PP_Var pipe_var = VarFromCStr(var_iface, pipe);
|
| + PP_Var operation_var = VarFromCStr(var_iface, operation);
|
| + PP_Var pipe_key = VarFromCStr(var_iface, "pipe");
|
| + PP_Var payload_key = VarFromCStr(var_iface, "payload");
|
| + PP_Var operation_key = VarFromCStr(var_iface, "operation");
|
| + dict_iface->Set(message, pipe_key, pipe_var);
|
| + dict_iface->Set(message, operation_key, operation_var);
|
| + dict_iface->Set(message, payload_key, payload);
|
| + var_iface->Release(pipe_var);
|
| + var_iface->Release(operation_var);
|
| + var_iface->Release(payload);
|
| + var_iface->Release(pipe_key);
|
| + var_iface->Release(payload_key);
|
| + var_iface->Release(operation_key);
|
| + return message;
|
| +}
|
| +
|
| +// Helper function for creating "ack" message in format expected
|
| +// by jspipe nodes.
|
| +PP_Var CreateAckMessage(PepperInterface* ppapi, const char* pipe,
|
| + int32_t count) {
|
| + return CreatePipeMessage(ppapi, pipe, "ack", PP_MakeInt32(count));
|
| +}
|
| +
|
| +// Helper function for creating "write" message in format expected
|
| +// by jspipe nodes.
|
| +PP_Var CreateWriteMessage(PepperInterface* ppapi,
|
| + const char* pipe,
|
| + const char* string,
|
| + int length=-1) {
|
| + VarArrayBufferInterface* buffer_iface = ppapi->GetVarArrayBufferInterface();
|
| +
|
| + if (length == -1)
|
| + length = strlen(string);
|
| +
|
| + PP_Var buffer = buffer_iface->Create(length);
|
| + memcpy(buffer_iface->Map(buffer), string, length);
|
| + buffer_iface->Unmap(buffer);
|
| +
|
| + return CreatePipeMessage(ppapi, pipe, "write", buffer);
|
| +}
|
| +
|
| class JSPipeTest : public ::testing::Test {
|
| public:
|
| - JSPipeTest() : fs_(&pepper_) {}
|
| -
|
| void SetUp() {
|
| ASSERT_EQ(0, ki_push_state_for_testing());
|
| - ASSERT_EQ(0, ki_init(&kp_));
|
| + ASSERT_EQ(0, ki_init_interface(&kp_, &ppapi_));
|
| + }
|
| +
|
| + void TearDown() {
|
| + ki_uninit();
|
| + }
|
| +
|
| + protected:
|
| + FakePepperInterface ppapi_;
|
| + KernelProxy kp_;
|
| +};
|
| +
|
| +class JSPipeNodeTest : public ::testing::Test {
|
| + public:
|
| + JSPipeNodeTest() : fs_(&ppapi_) {}
|
| +
|
| + void SetUp() {
|
| + name_ = "jspipe1";
|
| ASSERT_EQ(0, fs_.Access(Path("/jspipe1"), R_OK | W_OK));
|
| ASSERT_EQ(EACCES, fs_.Access(Path("/jspipe1"), X_OK));
|
| ASSERT_EQ(0, fs_.Open(Path("/jspipe1"), O_RDWR, &pipe_dev_));
|
| ASSERT_NE(NULL_NODE, pipe_dev_.get());
|
| }
|
|
|
| - void TearDown() { ki_uninit(); }
|
| + /**
|
| + * Create a PP_Var message in the same way that we expect
|
| + * JavaScript code to, and send it to the pipe using ioctl()
|
| + */
|
| + int JSPipeInject(const char* string, int length=-1) {
|
| + PP_Var message = CreateWriteMessage(&ppapi_, name_, string, length);
|
| +
|
| + // Send the message via ioctl
|
| + int rtn = pipe_dev_->Ioctl(NACL_IOC_HANDLEMESSAGE, &message);
|
| +
|
| + // Release message
|
| + ppapi_.GetVarInterface()->Release(message);
|
| + return rtn;
|
| + }
|
| +
|
| + int JSPipeInjectAck(int32_t count) {
|
| + PP_Var message = CreateAckMessage(&ppapi_, name_, count);
|
| +
|
| + // Send the message via ioctl
|
| + int rtn = pipe_dev_->Ioctl(NACL_IOC_HANDLEMESSAGE, &message);
|
| +
|
| + // Release message
|
| + ppapi_.GetVarInterface()->Release(message);
|
| + return rtn;
|
| + }
|
| +
|
| + // Verify the contents of the jspipe mesage, which should be
|
| + // {'<pipe_name>' : ['<command_name>', payload] }
|
| + void VerifyPipeMessage(PP_Var message,
|
| + const char* pipe_name,
|
| + const char* operation,
|
| + const char* payload,
|
| + int payload_length,
|
| + int32_t int_payload=0) {
|
| + VarArrayInterface* array_iface = ppapi_.GetVarArrayInterface();
|
| + VarDictionaryInterface* dict_iface = ppapi_.GetVarDictionaryInterface();
|
| + VarInterface* var_iface = ppapi_.GetVarInterface();
|
| + VarArrayBufferInterface* buffer_iface = ppapi_.GetVarArrayBufferInterface();
|
| +
|
| + // Verify we have a dictionary with 3 keys
|
| + ASSERT_EQ(PP_VARTYPE_DICTIONARY, message.type);
|
| + PP_Var keys = dict_iface->GetKeys(message);
|
| + ASSERT_EQ(PP_VARTYPE_ARRAY, keys.type);
|
| + ASSERT_EQ(3, array_iface->GetLength(keys));
|
| + var_iface->Release(keys);
|
| +
|
| + // Verify the keys
|
| + PP_Var key1 = VarFromCStr(var_iface, "pipe");
|
| + PP_Var key2 = VarFromCStr(var_iface, "operation");
|
| + PP_Var key3 = VarFromCStr(var_iface, "payload");
|
| +
|
| + // Verify pipe name and operation values
|
| + PP_Var value1 = dict_iface->Get(message, key1);
|
| + ASSERT_STREQ(pipe_name, VarToString(var_iface, value1).c_str());
|
| + var_iface->Release(value1);
|
| + var_iface->Release(key1);
|
| +
|
| + PP_Var value2 = dict_iface->Get(message, key2);
|
| + ASSERT_STREQ(operation, VarToString(var_iface, value2).c_str());
|
| + var_iface->Release(value2);
|
| + var_iface->Release(key2);
|
| +
|
| + // Verify the payload
|
| + PP_Var payload_var = dict_iface->Get(message, key3);
|
| + if (payload != NULL) {
|
| + ASSERT_EQ(PP_VARTYPE_ARRAY_BUFFER, payload_var.type);
|
| + ASSERT_EQ(0, memcmp(payload, buffer_iface->Map(payload_var),
|
| + payload_length));
|
| + } else {
|
| + ASSERT_EQ(PP_VARTYPE_INT32, payload_var.type);
|
| + ASSERT_EQ(int_payload, payload_var.value.as_int);
|
| + }
|
| + var_iface->Release(key3);
|
| + var_iface->Release(payload_var);
|
| + }
|
|
|
| protected:
|
| - KernelProxy kp_;
|
| - FakePepperInterface pepper_;
|
| + FakePepperInterface ppapi_;
|
| DevFsForTesting fs_;
|
| ScopedNode pipe_dev_;
|
| + const char* name_;
|
| };
|
|
|
| -TEST_F(JSPipeTest, InvalidIoctl) {
|
| +TEST(JSPipeTestBasic, MissingPepper) {
|
| + // Create a devfs filesystem without giving it any Pepper implemenation.
|
| + TypedFsFactory<DevFs> factory;
|
| + ScopedFilesystem fs;
|
| + FsInitArgs args(1);
|
| + factory.CreateFilesystem(args, &fs);
|
| + ScopedNode pipe_dev;
|
| + ASSERT_EQ(0, fs->Open(Path("/jspipe1"), O_RDWR, &pipe_dev));
|
| +
|
| + // Writing to a pipe should return EIO because Pepper is missing.
|
| + HandleAttr attrs;
|
| + int written = -1;
|
| + ASSERT_EQ(EIO, pipe_dev->Write(attrs, "test", 4, &written));
|
| +}
|
| +
|
| +TEST_F(JSPipeNodeTest, InvalidIoctl) {
|
| // 123 is not a valid ioctl request.
|
| EXPECT_EQ(EINVAL, pipe_dev_->Ioctl(123));
|
| }
|
|
|
| -TEST_F(JSPipeTest, JSPipeInput) {
|
| +TEST_F(JSPipeNodeTest, JSPipeInput) {
|
| + std::string message("hello, how are you?\n");
|
| +
|
| // First we send some data into the pipe. This is how messages
|
| // from javascript are injected into the pipe nodes.
|
| - std::string message("hello, how are you?\n");
|
| - struct tioc_nacl_input_string packaged_message;
|
| - packaged_message.length = message.size();
|
| - packaged_message.buffer = message.data();
|
| - ASSERT_EQ(0, pipe_dev_->Ioctl(TIOCNACLINPUT, &packaged_message));
|
| + ASSERT_EQ(0, JSPipeInject(message.c_str()));
|
|
|
| // Now we make buffer we'll read into.
|
| // We fill the buffer and a backup buffer with arbitrary data
|
| @@ -90,44 +267,28 @@ TEST_F(JSPipeTest, JSPipeInput) {
|
| 100 - message.size()));
|
| }
|
|
|
| -TEST_F(JSPipeTest, JSPipeOutput) {
|
| - const char* message = "hello";
|
| - const int message_len = strlen(message);
|
| +TEST_F(JSPipeNodeTest, JSPipeOutput) {
|
| + std::string message("hello");
|
|
|
| int bytes_written = 999;
|
| HandleAttr attrs;
|
| - ASSERT_EQ(0, pipe_dev_->Write(attrs, message, message_len, &bytes_written));
|
| - ASSERT_EQ(message_len, bytes_written);
|
| + ASSERT_EQ(0, pipe_dev_->Write(attrs, message.c_str(), message.size(),
|
| + &bytes_written));
|
| + ASSERT_EQ(message.size(), bytes_written);
|
|
|
| - // Verify that the correct messages was sent via PostMessage.
|
| FakeMessagingInterface* iface =
|
| - (FakeMessagingInterface*)fs_.ppapi()->GetMessagingInterface();
|
| - VarArrayInterface* array_iface = fs_.ppapi()->GetVarArrayInterface();
|
| - VarInterface* var_iface = fs_.ppapi()->GetVarInterface();
|
| - VarArrayBufferInterface* buffer_iface =
|
| - fs_.ppapi()->GetVarArrayBufferInterface();
|
| + (FakeMessagingInterface*)ppapi_.GetMessagingInterface();
|
|
|
| - // Verify that exaclty one message was sent of type PP_VARTYPE_ARRAY
|
| + // Verify that exactly one message sent.
|
| ASSERT_EQ(1, iface->messages.size());
|
| - PP_Var array = iface->messages[0];
|
| - ASSERT_EQ(PP_VARTYPE_ARRAY, array.type);
|
| -
|
| - // Verify that the array contains two element, the prefix,
|
| - // and an ArrayBuffer containing the message.
|
| - ASSERT_EQ(2, array_iface->GetLength(array));
|
| - PP_Var item0 = array_iface->Get(array, 0);
|
| - PP_Var item1 = array_iface->Get(array, 1);
|
| - ASSERT_EQ(PP_VARTYPE_STRING, item0.type);
|
| - ASSERT_EQ(PP_VARTYPE_ARRAY_BUFFER, item1.type);
|
| - uint32_t len = 0;
|
| - const char* item0_string = var_iface->VarToUtf8(item0, &len);
|
| - ASSERT_STREQ("jspipe1", std::string(item0_string, len).c_str());
|
| - ASSERT_EQ(0, memcmp(message, buffer_iface->Map(item1), strlen(message)));
|
| - var_iface->Release(item0);
|
| - var_iface->Release(item1);
|
| + PP_Var message_var = iface->messages[0];
|
| +
|
| + // Verify the content of the message.
|
| + VerifyPipeMessage(message_var, "jspipe1", "write", message.c_str(),
|
| + message.size());
|
| }
|
|
|
| -TEST_F(JSPipeTest, JSPipeOutputWithNulls) {
|
| +TEST_F(JSPipeNodeTest, JSPipeOutputWithNulls) {
|
| char message[20];
|
| int message_len = sizeof(message);
|
|
|
| @@ -143,51 +304,104 @@ TEST_F(JSPipeTest, JSPipeOutputWithNulls) {
|
|
|
| // Verify that the correct messages was sent via PostMessage.
|
| FakeMessagingInterface* iface =
|
| - (FakeMessagingInterface*)fs_.ppapi()->GetMessagingInterface();
|
| - VarArrayInterface* array_iface = fs_.ppapi()->GetVarArrayInterface();
|
| - VarInterface* var_iface = fs_.ppapi()->GetVarInterface();
|
| - VarArrayBufferInterface* buffer_iface =
|
| - fs_.ppapi()->GetVarArrayBufferInterface();
|
| -
|
| - // Verify that exactly one message was sent of type PP_VARTYPE_ARRAY
|
| - EXPECT_EQ(1, iface->messages.size());
|
| - PP_Var array = iface->messages[0];
|
| - ASSERT_EQ(PP_VARTYPE_ARRAY, array.type);
|
| -
|
| - // Verify that the array contains two element, the prefix,
|
| - // and an ArrayBuffer containing the message.
|
| - ASSERT_EQ(2, array_iface->GetLength(array));
|
| - PP_Var item0 = array_iface->Get(array, 0);
|
| - PP_Var item1 = array_iface->Get(array, 1);
|
| - ASSERT_EQ(PP_VARTYPE_STRING, item0.type);
|
| - ASSERT_EQ(PP_VARTYPE_ARRAY_BUFFER, item1.type);
|
| - uint32_t len = 0;
|
| - ASSERT_STREQ("jspipe1", var_iface->VarToUtf8(item0, &len));
|
| - ASSERT_EQ(0, memcmp(message, buffer_iface->Map(item1), strlen(message)));
|
| - var_iface->Release(item0);
|
| - var_iface->Release(item1);
|
| + (FakeMessagingInterface*)ppapi_.GetMessagingInterface();
|
| +
|
| + // Verify that exaclty one message sent.
|
| + ASSERT_EQ(1, iface->messages.size());
|
| + PP_Var message_var = iface->messages[0];
|
| +
|
| + // Verify the content of the message.
|
| + VerifyPipeMessage(message_var, "jspipe1", "write", message, message_len);
|
| }
|
|
|
| -static int ki_ioctl_wrapper(int fd, int request, ...) {
|
| - va_list ap;
|
| - va_start(ap, request);
|
| - int rtn = ki_ioctl(fd, request, ap);
|
| - va_end(ap);
|
| - return rtn;
|
| +#define CHUNK_SIZE 678
|
| +TEST_F(JSPipeNodeTest, JSPipeOutputBuffer) {
|
| + int ospace_orig = -1;
|
| + ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETOSPACE, &ospace_orig));
|
| + ASSERT_GT(ospace_orig, 0);
|
| +
|
| + HandleAttr attrs;
|
| + attrs.flags = O_NONBLOCK;
|
| + char* message = (char*)malloc(CHUNK_SIZE);
|
| +
|
| + // Keep writing data until we block.
|
| + int total_written = 0;
|
| + while (1) {
|
| + int bytes_written;
|
| + // Write some data
|
| + int rtn = pipe_dev_->Write(attrs, message, CHUNK_SIZE, &bytes_written);
|
| + if (rtn != 0) {
|
| + ASSERT_EQ(EWOULDBLOCK, rtn);
|
| + int ospace = -1;
|
| + ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETOSPACE, &ospace));
|
| + ASSERT_EQ(0, ospace);
|
| + ASSERT_EQ(total_written, ospace_orig);
|
| + break;
|
| + }
|
| + total_written += bytes_written;
|
| + }
|
| +
|
| + // At this point writes should always block
|
| + int bytes_written;
|
| + int rtn = pipe_dev_->Write(attrs, message, CHUNK_SIZE, &bytes_written);
|
| + ASSERT_EQ(EWOULDBLOCK, rtn);
|
| +
|
| + // Now inject and ACK message from JavaScript.
|
| + ASSERT_EQ(0, JSPipeInjectAck(10));
|
| +
|
| + // Now it should be possible to write 10 bytes to the pipe.
|
| + rtn = pipe_dev_->Write(attrs, message, CHUNK_SIZE, &bytes_written);
|
| + ASSERT_EQ(0, rtn);
|
| + ASSERT_EQ(10, bytes_written);
|
| +
|
| + free(message);
|
| }
|
|
|
| -static int JSPipeWrite(int fd, const char* string) {
|
| - struct tioc_nacl_input_string input;
|
| - input.buffer = string;
|
| - input.length = strlen(input.buffer);
|
| - return ki_ioctl_wrapper(fd, TIOCNACLINPUT, &input);
|
| +TEST_F(JSPipeNodeTest, JSPipeInputBuffer) {
|
| + char* message = (char*)malloc(CHUNK_SIZE);
|
| + memset(message, 1, CHUNK_SIZE);
|
| +
|
| + int ispace_orig = -1;
|
| + ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETISPACE, &ispace_orig));
|
| +
|
| + // Keep injecting data until the ioctl fails
|
| + int total_written = 0;
|
| + while (1) {
|
| + int rtn = JSPipeInject(message, CHUNK_SIZE);
|
| + if (rtn != 0) {
|
| + ASSERT_LT(total_written, ispace_orig);
|
| + ASSERT_GT(total_written, ispace_orig - CHUNK_SIZE - 1);
|
| + break;
|
| + }
|
| + total_written += CHUNK_SIZE;
|
| + }
|
| +
|
| + int ispace = -1;
|
| + ASSERT_EQ(0, pipe_dev_->Ioctl(NACL_IOC_PIPE_GETISPACE, &ispace));
|
| + ASSERT_EQ(0, ispace);
|
| +
|
| + // Check that no messages have thus far been sent to JavaScript
|
| + FakeMessagingInterface* iface =
|
| + (FakeMessagingInterface*)ppapi_.GetMessagingInterface();
|
| + ASSERT_EQ(0, iface->messages.size());
|
| +
|
| + // Read some data from the pipe, which should trigger an ack message
|
| + int bytes_read = -1;
|
| + HandleAttr attrs;
|
| + ASSERT_EQ(0, pipe_dev_->Read(attrs, message, 5, &bytes_read));
|
| + ASSERT_EQ(5, bytes_read);
|
| +
|
| + // Verify that an ack was sent to JavaScript
|
| + ASSERT_EQ(1, iface->messages.size());
|
| + PP_Var message_var = iface->messages[0];
|
| + VerifyPipeMessage(message_var, "jspipe1", "ack", NULL, 0, 5);
|
| }
|
|
|
| // Returns:
|
| // 0 -> Not readable
|
| // 1 -> Readable
|
| // -1 -> Error occured
|
| -static int IsReadable(int fd) {
|
| +int IsReadable(int fd) {
|
| struct timeval timeout = {0, 0};
|
| fd_set readfds;
|
| fd_set errorfds;
|
| @@ -242,8 +456,10 @@ TEST_F(JSPipeTest, JSPipeSelect) {
|
| ASSERT_FALSE(FD_ISSET(pipe_fd, &readfds));
|
| ASSERT_FALSE(FD_ISSET(pipe_fd, &errorfds));
|
|
|
| - // Send 4 bytes to the pipe
|
| - ASSERT_EQ(0, JSPipeWrite(pipe_fd, "test"));
|
| + // Send 4 bytes to the pipe via ioctl
|
| + PP_Var message = CreateWriteMessage(&ppapi_, "jspipe1", "test");
|
| + ASSERT_EQ(0, ki_ioctl_wrapper(pipe_fd, NACL_IOC_HANDLEMESSAGE, &message));
|
| + ppapi_.GetVarInterface()->Release(message);
|
|
|
| // Pipe should now be readable
|
| ASSERT_EQ(1, IsReadable(pipe_fd));
|
|
|