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)); |