Chromium Code Reviews| 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 63872be489187edfd0a5ec31375218cc80b08978..8f5d2af8da2e098a0fd2b7d6adbed6a7c5d40164 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,38 +25,193 @@ 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); |
| +} |
| + |
| +// Helper function for creating message in the format expected by jspipe |
| +// nodes: [ name, payload ] |
| +PP_Var CreatePipeMessage(PepperInterface* ppapi, const char* name) { |
| + VarInterface* var_iface = ppapi->GetVarInterface(); |
| + VarArrayInterface* array_iface = ppapi->GetVarArrayInterface(); |
| + |
| + // 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 = array_iface->Create(); |
| + PP_Var name_var = var_iface->VarFromUtf8(name, strlen(name)); |
| + array_iface->Set(message, 0, name_var); |
| + var_iface->Release(name_var); |
| + return message; |
| +} |
| + |
| +// Helper function for creating "ack" message in format expected |
| +// by jspipe nodes. |
| +PP_Var CreateAckMessage(PepperInterface* ppapi, int32_t count) { |
| + VarArrayInterface* array_iface = ppapi->GetVarArrayInterface(); |
| + |
| + PP_Var message = CreatePipeMessage(ppapi, "ack"); |
| + PP_Var ack_count; |
|
binji
2014/05/01 20:22:31
PP_MakeInt32(...) is a bit nicer
Sam Clegg
2014/05/01 22:16:55
Thanks! Done.
|
| + ack_count.type = PP_VARTYPE_INT32; |
| + ack_count.value.as_int = count; |
| + array_iface->Set(message, 1, ack_count); |
| + |
| + return message; |
| +} |
| + |
| +// Helper function for creating "write" message in format expected |
| +// by jspipe nodes. |
| +PP_Var CreateWriteMessage(PepperInterface* ppapi, |
| + const char* string, |
| + int length=-1) { |
| + VarArrayInterface* array_iface = ppapi->GetVarArrayInterface(); |
| + VarArrayBufferInterface* buffer_iface = ppapi->GetVarArrayBufferInterface(); |
| + VarInterface* var_iface = ppapi->GetVarInterface(); |
| + |
| + if (length == -1) |
| + length = strlen(string); |
| + |
| + PP_Var buffer = buffer_iface->Create(length); |
| + memcpy(buffer_iface->Map(buffer), string, length); |
| + buffer_iface->Unmap(buffer); |
| + |
| + PP_Var message = CreatePipeMessage(ppapi, "write"); |
| + array_iface->Set(message, 1, buffer); |
| + var_iface->Release(buffer); |
| + |
| + return message; |
| +} |
| + |
| class JSPipeTest : public ::testing::Test { |
| public: |
| void SetUp() { |
| ASSERT_EQ(0, ki_push_state_for_testing()); |
| - ASSERT_EQ(0, ki_init(&kp_)); |
| + pepper_ = new FakePepperInterface(); |
| + ASSERT_EQ(0, ki_init_interface(&kp_, pepper_)); |
| + } |
| + |
| + void TearDown() { |
| + ki_uninit(); |
| + pepper_ = NULL; |
| + } |
| + |
| + protected: |
| + KernelProxy kp_; |
| + FakePepperInterface* pepper_; |
| +}; |
| + |
| +class JSPipeNodeTest : public ::testing::Test { |
| + public: |
| + void SetUp() { |
| 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) { |
| + PepperInterface* ppapi = fs_.ppapi(); |
| + PP_Var message = CreateWriteMessage(ppapi, 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) { |
| + PepperInterface* ppapi = fs_.ppapi(); |
| + PP_Var message = CreateAckMessage(ppapi, 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* channel_name, |
| + const char* message_name, |
| + const char* payload, |
| + int payload_length, |
| + int32_t int_payload=0) { |
| + PepperInterface* ppapi = fs_.ppapi(); |
| + VarArrayInterface* array_iface = ppapi->GetVarArrayInterface(); |
| + VarDictionaryInterface* dict_iface = ppapi->GetVarDictionaryInterface(); |
| + VarInterface* var_iface = ppapi->GetVarInterface(); |
| + VarArrayBufferInterface* buffer_iface = ppapi->GetVarArrayBufferInterface(); |
| + ASSERT_EQ(PP_VARTYPE_DICTIONARY, message.type); |
| + |
| + PP_Var keys = dict_iface->GetKeys(message); |
| + ASSERT_EQ(PP_VARTYPE_ARRAY, keys.type); |
| + ASSERT_EQ(1, array_iface->GetLength(keys)); |
| + PP_Var key = array_iface->Get(keys, 0); |
| + ASSERT_STREQ(channel_name, VarToString(var_iface, key).c_str()); |
| + var_iface->Release(keys); |
| + |
| + PP_Var array = dict_iface->Get(message, key); |
| + var_iface->Release(key); |
| + |
| + ASSERT_EQ(PP_VARTYPE_ARRAY, array.type); |
| + 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); |
| + |
| + if (payload != NULL) { |
| + ASSERT_EQ(PP_VARTYPE_ARRAY_BUFFER, item1.type); |
| + ASSERT_STREQ(message_name, VarToString(var_iface, item0).c_str()); |
| + ASSERT_EQ(0, memcmp(payload, buffer_iface->Map(item1), payload_length)); |
| + } else { |
| + ASSERT_EQ(PP_VARTYPE_INT32, item1.type); |
| + ASSERT_EQ(int_payload, item1.value.as_int); |
| + } |
| + |
| + var_iface->Release(item0); |
| + var_iface->Release(item1); |
| + var_iface->Release(array); |
| + } |
| protected: |
| - KernelProxy kp_; |
| DevFsForTesting fs_; |
| ScopedNode pipe_dev_; |
| }; |
| -TEST_F(JSPipeTest, InvalidIoctl) { |
| +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 |
| @@ -87,44 +242,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(); |
| - // Verify that exaclty one message was sent of type PP_VARTYPE_ARRAY |
| + // Verify that exaclty one message sent. |
|
binji
2014/05/01 20:22:31
exactly
Sam Clegg
2014/05/01 22:16:55
Done.
|
| 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); |
| @@ -141,50 +280,106 @@ 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); |
| + |
| + // 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); |
| +} |
| + |
| +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*)fs_.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); |
| } |
| -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, JSPipeOverrun) { |
|
binji
2014/05/01 20:22:31
not implemented
Sam Clegg
2014/05/01 22:16:55
Done.
|
| } |
| // 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; |
| @@ -239,8 +434,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(pepper_, "test"); |
| + ASSERT_EQ(0, ki_ioctl_wrapper(pipe_fd, NACL_IOC_HANDLEMESSAGE, &message)); |
| + pepper_->GetVarInterface()->Release(message); |
| // Pipe should now be readable |
| ASSERT_EQ(1, IsReadable(pipe_fd)); |