Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(34)

Unified Diff: native_client_sdk/src/tests/nacl_io_test/jspipe_test.cc

Issue 242533005: [NaCl SDK] nacl_io: Add flow control the JavaScript pipes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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));

Powered by Google App Engine
This is Rietveld 408576698