| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <pthread.h> |
| |
| #include <set> |
| #include <string> |
| |
| #include <gmock/gmock.h> |
| #include <ppapi/c/pp_errors.h> |
| #include <ppapi/c/pp_instance.h> |
| |
| #include "fake_ppapi/fake_messaging_interface.h" |
| #include "fake_ppapi/fake_pepper_interface.h" |
| #include "nacl_io/ioctl.h" |
| #include "nacl_io/jsfs/js_fs.h" |
| #include "nacl_io/jsfs/js_fs_node.h" |
| #include "nacl_io/kernel_intercept.h" |
| #include "nacl_io/kernel_proxy.h" |
| #include "nacl_io/log.h" |
| #include "nacl_io/osdirent.h" |
| #include "nacl_io/osunistd.h" |
| #include "sdk_util/auto_lock.h" |
| #include "sdk_util/scoped_ref.h" |
| #include "sdk_util/simple_lock.h" |
| |
| using namespace nacl_io; |
| using namespace sdk_util; |
| |
| namespace { |
| |
| class JsFsForTesting : public JsFs { |
| public: |
| JsFsForTesting(PepperInterface* ppapi) { |
| FsInitArgs args; |
| args.ppapi = ppapi; |
| Error error = Init(args); |
| EXPECT_EQ(0, error); |
| } |
| }; |
| |
| class FakeMessagingInterfaceJsFs : public MessagingInterface { |
| public: |
| explicit FakeMessagingInterfaceJsFs(VarInterface* var_interface) |
| : var_interface_(var_interface), has_message_(false) { |
| pthread_cond_init(&cond_, NULL); |
| } |
| |
| ~FakeMessagingInterfaceJsFs() { pthread_cond_destroy(&cond_); } |
| |
| virtual void PostMessage(PP_Instance instance, PP_Var message) { |
| var_interface_->AddRef(message); |
| |
| AUTO_LOCK(lock_); |
| ASSERT_FALSE(has_message_); |
| |
| message_ = message; |
| has_message_ = true; |
| pthread_cond_signal(&cond_); |
| } |
| |
| PP_Var WaitForMessage() { |
| AUTO_LOCK(lock_); |
| while (!has_message_) { |
| pthread_cond_wait(&cond_, lock_.mutex()); |
| } |
| |
| has_message_ = false; |
| return message_; |
| } |
| |
| private: |
| VarInterface* var_interface_; |
| SimpleLock lock_; |
| pthread_cond_t cond_; |
| PP_Var message_; |
| bool has_message_; |
| }; |
| |
| class FakePepperInterfaceJsFs : public FakePepperInterface { |
| public: |
| FakePepperInterfaceJsFs() : messaging_interface_(GetVarInterface()) {} |
| |
| virtual nacl_io::MessagingInterface* GetMessagingInterface() { |
| return &messaging_interface_; |
| } |
| |
| private: |
| FakeMessagingInterfaceJsFs messaging_interface_; |
| }; |
| |
| class JsFsTest : public ::testing::Test { |
| public: |
| void SetUp() { |
| ASSERT_EQ(0, ki_push_state_for_testing()); |
| ASSERT_EQ(0, ki_init_interface(&kp_, &ppapi_)); |
| fs_.reset(new JsFsForTesting(&ppapi_)); |
| |
| js_thread_started_ = false; |
| } |
| |
| void TearDown() { |
| if (js_thread_started_) |
| pthread_join(js_thread_, NULL); |
| |
| for (RequestResponses::iterator it = request_responses_.begin(), |
| end = request_responses_.end(); |
| it != end; |
| ++it) { |
| ppapi_.GetVarInterface()->Release(it->request); |
| ppapi_.GetVarInterface()->Release(it->response); |
| } |
| |
| |
| ki_uninit(); |
| } |
| |
| void StartJsThread() { |
| ASSERT_EQ(0, pthread_create(&js_thread_, NULL, JsThreadMainThunk, this)); |
| js_thread_started_ = true; |
| } |
| |
| static void* JsThreadMainThunk(void* arg) { |
| static_cast<JsFsTest*>(arg)->JsThreadMain(); |
| return NULL; |
| } |
| |
| PP_Var WaitForRequest() { |
| FakeMessagingInterfaceJsFs* messaging_if = |
| static_cast<FakeMessagingInterfaceJsFs*>( |
| ppapi_.GetMessagingInterface()); |
| return messaging_if->WaitForMessage(); |
| } |
| |
| void JsThreadMain() { |
| for (RequestResponses::iterator it = request_responses_.begin(), |
| end = request_responses_.end(); |
| it != end; |
| ++it) { |
| PP_Var request = WaitForRequest(); |
| EXPECT_TRUE(VarsAreEqual(it->request, request)) |
| << "Vars are not equal: " << VarToString(it->request) |
| << " != " << VarToString(request); |
| ppapi_.GetVarInterface()->Release(request); |
| EXPECT_EQ(0, |
| fs_->Filesystem_Ioctl(NACL_IOC_HANDLEMESSAGE, &it->response)); |
| // Passed ownership of response_ to filesystem, so set this to undefined |
| // so it isn't double-released in TearDown(). |
| it->response = PP_MakeUndefined(); |
| } |
| } |
| |
| void Expect(PP_Var request, PP_Var response) { |
| RequestResponse rr; |
| // Pass ownership of both vars from caller to callee. |
| rr.request = request; |
| rr.response = response; |
| request_responses_.push_back(rr); |
| } |
| |
| bool CreateDict(PP_Var* out_var) { |
| *out_var = ppapi_.GetVarDictionaryInterface()->Create(); |
| return out_var->type == PP_VARTYPE_DICTIONARY; |
| } |
| |
| bool SetDictKeyValue(PP_Var* var, const char* key, int32_t value) { |
| return SetDictKeyValue(var, key, PP_MakeInt32(value)); |
| } |
| |
| bool SetDictKeyValue(PP_Var* var, const char* key, size_t value) { |
| return SetDictKeyValue(var, key, PP_MakeInt32(static_cast<size_t>(value))); |
| } |
| |
| bool SetDictKeyValue(PP_Var* var, const char* key, int64_t value) { |
| VarArrayInterface* array_if = ppapi_.GetVarArrayInterface(); |
| PP_Var value_var = array_if->Create(); |
| return array_if->Set(value_var, 0, PP_MakeInt32(value >> 32)) && |
| array_if->Set(value_var, 1, PP_MakeInt32(value & 0xffffffff)) && |
| SetDictKeyValue(var, key, value_var); |
| } |
| |
| bool SetDictKeyValue(PP_Var* var, const char* key, const char* value) { |
| VarInterface* var_if = ppapi_.GetVarInterface(); |
| PP_Var value_var = var_if->VarFromUtf8(value, strlen(value)); |
| return SetDictKeyValue(var, key, value_var); |
| } |
| |
| bool SetDictKeyValue(PP_Var* var, const char* key, PP_Var value_var) { |
| VarDictionaryInterface* dict_if = ppapi_.GetVarDictionaryInterface(); |
| VarInterface* var_if = ppapi_.GetVarInterface(); |
| PP_Var key_var = var_if->VarFromUtf8(key, strlen(key)); |
| PP_Bool result = dict_if->Set(*var, key_var, value_var); |
| var_if->Release(key_var); |
| var_if->Release(value_var); |
| return result == PP_TRUE; |
| } |
| |
| bool CreateArray(PP_Var* out_var) { |
| *out_var = ppapi_.GetVarArrayInterface()->Create(); |
| return out_var->type == PP_VARTYPE_ARRAY; |
| } |
| |
| bool SetArrayValue(PP_Var* var, uint32_t i, int32_t value) { |
| return SetArrayValue(var, i, PP_MakeInt32(value)); |
| } |
| |
| bool SetArrayValue(PP_Var* var, uint32_t i, const char* value) { |
| VarInterface* var_if = ppapi_.GetVarInterface(); |
| PP_Var value_var = var_if->VarFromUtf8(value, strlen(value)); |
| return SetArrayValue(var, i, value_var); |
| } |
| |
| bool SetArrayValue(PP_Var* var, uint32_t i, int64_t value) { |
| VarArrayInterface* array_if = ppapi_.GetVarArrayInterface(); |
| PP_Var value_var = array_if->Create(); |
| return array_if->Set(value_var, 0, PP_MakeInt32(value >> 32)) && |
| array_if->Set(value_var, 1, PP_MakeInt32(value & 0xffffffff)) && |
| SetArrayValue(var, i, value_var); |
| } |
| |
| bool SetArrayValue(PP_Var* var, uint32_t i, PP_Var value_var) { |
| VarArrayInterface* array_if = ppapi_.GetVarArrayInterface(); |
| VarInterface* var_if = ppapi_.GetVarInterface(); |
| PP_Bool result = array_if->Set(*var, i, value_var); |
| var_if->Release(value_var); |
| return result == PP_TRUE; |
| } |
| |
| std::string VarToString(PP_Var var) { |
| VarDictionaryInterface* dict_if = ppapi_.GetVarDictionaryInterface(); |
| VarArrayInterface* array_if = ppapi_.GetVarArrayInterface(); |
| VarInterface* var_if = ppapi_.GetVarInterface(); |
| VarArrayBufferInterface* array_buffer_if = |
| ppapi_.GetVarArrayBufferInterface(); |
| |
| switch (var.type) { |
| case PP_VARTYPE_UNDEFINED: |
| return "undefined"; |
| case PP_VARTYPE_NULL: |
| return "null"; |
| case PP_VARTYPE_BOOL: |
| return var.value.as_bool ? "true" : "false"; |
| case PP_VARTYPE_INT32: { |
| char buffer[32]; |
| snprintf(buffer, 32, "%d", var.value.as_int); |
| return buffer; |
| } |
| case PP_VARTYPE_DOUBLE: { |
| char buffer[32]; |
| snprintf(buffer, 32, "%g", var.value.as_double); |
| return buffer; |
| } |
| case PP_VARTYPE_STRING: { |
| uint32_t var_len; |
| const char* var_str = var_if->VarToUtf8(var, &var_len); |
| std::string result("\""); |
| result += std::string(var_str, var_len); |
| result += "\""; |
| return result; |
| } |
| case PP_VARTYPE_ARRAY: { |
| std::string result("["); |
| uint32_t var_len = array_if->GetLength(var); |
| |
| for (uint32_t i = 0; i < var_len; ++i) { |
| PP_Var var_item = array_if->Get(var, i); |
| result += VarToString(var_item); |
| var_if->Release(var_item); |
| if (i != var_len - 1) |
| result += ", "; |
| } |
| result += "]"; |
| return result; |
| } |
| case PP_VARTYPE_DICTIONARY: { |
| std::string result("{"); |
| PP_Var var_keys = dict_if->GetKeys(var); |
| uint32_t var_len = array_if->GetLength(var_keys); |
| |
| for (uint32_t i = 0; i < var_len; ++i) { |
| PP_Var key = array_if->Get(var_keys, i); |
| result += VarToString(key); |
| result += ": "; |
| PP_Var var_value = dict_if->Get(var, key); |
| result += VarToString(var_value); |
| var_if->Release(key); |
| var_if->Release(var_value); |
| if (i != var_len - 1) |
| result += ", "; |
| } |
| result += "}"; |
| var_if->Release(var_keys); |
| return result; |
| } |
| case PP_VARTYPE_ARRAY_BUFFER: { |
| uint32_t var_len; |
| if (!array_buffer_if->ByteLength(var, &var_len)) { |
| LOG_ERROR("Unable to get byte length of var."); |
| return "undefined"; |
| } |
| |
| std::string result("new Uint8Array(["); |
| |
| void* var_ptr = array_buffer_if->Map(var); |
| for (uint32_t i = 0; i < var_len; ++i) { |
| char buffer[8]; |
| snprintf(buffer, 8, "%d", static_cast<uint8_t*>(var_ptr)[i]); |
| result += buffer; |
| if (i != var_len - 1) |
| result += ", "; |
| } |
| result += "])"; |
| array_buffer_if->Unmap(var); |
| return result; |
| } |
| |
| default: |
| ADD_FAILURE() << "Unexpected var type: " << var.type; |
| return "undefined"; |
| } |
| } |
| |
| bool VarsAreEqual(PP_Var expected, PP_Var var) { |
| if (expected.type != var.type) |
| return false; |
| |
| VarDictionaryInterface* dict_if = ppapi_.GetVarDictionaryInterface(); |
| VarArrayInterface* array_if = ppapi_.GetVarArrayInterface(); |
| VarInterface* var_if = ppapi_.GetVarInterface(); |
| VarArrayBufferInterface* array_buffer_if = |
| ppapi_.GetVarArrayBufferInterface(); |
| |
| switch (var.type) { |
| case PP_VARTYPE_UNDEFINED: |
| case PP_VARTYPE_NULL: |
| return true; |
| case PP_VARTYPE_BOOL: |
| return expected.value.as_bool == var.value.as_bool; |
| case PP_VARTYPE_INT32: |
| return expected.value.as_int == var.value.as_int; |
| case PP_VARTYPE_DOUBLE: |
| return expected.value.as_double == var.value.as_double; |
| case PP_VARTYPE_STRING: { |
| uint32_t var_len; |
| uint32_t expected_len; |
| const char* var_str = var_if->VarToUtf8(var, &var_len); |
| const char* expected_str = var_if->VarToUtf8(expected, &expected_len); |
| |
| if (expected_len != var_len) |
| return false; |
| |
| return memcmp(expected_str, var_str, var_len) == 0; |
| } |
| case PP_VARTYPE_ARRAY: { |
| uint32_t var_len = array_if->GetLength(var); |
| uint32_t expected_len = array_if->GetLength(expected); |
| |
| if (expected_len != var_len) |
| return false; |
| |
| for (uint32_t i = 0; i < var_len; ++i) { |
| PP_Var var_item = array_if->Get(var, i); |
| PP_Var expected_item = array_if->Get(expected, i); |
| bool equal = VarsAreEqual(expected_item, var_item); |
| var_if->Release(var_item); |
| var_if->Release(expected_item); |
| |
| if (!equal) |
| return false; |
| } |
| |
| return true; |
| } |
| case PP_VARTYPE_DICTIONARY: { |
| PP_Var var_keys = dict_if->GetKeys(var); |
| PP_Var expected_keys = dict_if->GetKeys(expected); |
| |
| uint32_t var_len = array_if->GetLength(var_keys); |
| uint32_t expected_len = array_if->GetLength(expected_keys); |
| |
| bool result = true; |
| |
| if (expected_len == var_len) { |
| for (uint32_t i = 0; i < var_len; ++i) { |
| PP_Var key = array_if->Get(var_keys, i); |
| PP_Var var_value = dict_if->Get(var, key); |
| PP_Var expected_value = dict_if->Get(expected, key); |
| bool equal = VarsAreEqual(expected_value, var_value); |
| var_if->Release(key); |
| var_if->Release(var_value); |
| var_if->Release(expected_value); |
| |
| if (!equal) { |
| result = false; |
| break; |
| } |
| } |
| } else { |
| result = false; |
| } |
| |
| var_if->Release(var_keys); |
| var_if->Release(expected_keys); |
| return result; |
| } |
| case PP_VARTYPE_ARRAY_BUFFER: { |
| uint32_t var_len; |
| if (!array_buffer_if->ByteLength(var, &var_len)) |
| return false; |
| |
| uint32_t expected_len; |
| if (!array_buffer_if->ByteLength(expected, &expected_len)) |
| return false; |
| |
| if (expected_len != var_len) |
| return false; |
| |
| void* var_ptr = array_buffer_if->Map(var); |
| void* expected_ptr = array_buffer_if->Map(expected); |
| bool equal = memcmp(var_ptr, expected_ptr, var_len) == 0; |
| array_buffer_if->Unmap(var); |
| array_buffer_if->Unmap(expected); |
| |
| return equal; |
| } |
| |
| default: |
| ADD_FAILURE() << "Unexpected var type: " << var.type; |
| return false; |
| } |
| } |
| |
| PP_Var CreateDummyArrayBuffer(uint32_t length) { |
| VarArrayBufferInterface* array_buffer_if = |
| ppapi_.GetVarArrayBufferInterface(); |
| PP_Var var = array_buffer_if->Create(length); |
| uint8_t* data = static_cast<uint8_t*>(array_buffer_if->Map(var)); |
| FillDummyBuffer(data, length); |
| array_buffer_if->Unmap(var); |
| return var; |
| } |
| |
| void FillDummyBuffer(uint8_t* buf, size_t buf_len) { |
| for (uint32_t i = 0; i < buf_len; ++i) { |
| buf[i] = i & 255; |
| } |
| } |
| |
| bool EqualsDummyArrayBuffer(uint8_t* buf, size_t buf_len) { |
| for (uint32_t i = 0; i < buf_len; ++i) { |
| if (buf[i] != (i & 255)) { |
| LOG_ERROR("Byte %d of ArrayBuffer doesn't match: %d != %d.", |
| i, |
| buf[i], |
| i & 255); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| protected: |
| FakePepperInterfaceJsFs ppapi_; |
| ScopedRef<JsFsForTesting> fs_; |
| KernelProxy kp_; |
| pthread_t js_thread_; |
| bool js_thread_started_; |
| |
| struct RequestResponse { |
| PP_Var request; |
| PP_Var response; |
| }; |
| |
| typedef std::vector<RequestResponse> RequestResponses; |
| RequestResponses request_responses_; |
| }; |
| |
| class JsFsNodeTest : public JsFsTest { |
| public: |
| static const int fd; |
| |
| virtual void SetUp() { |
| JsFsTest::SetUp(); |
| |
| PP_Var expected; |
| ASSERT_EQ(true, CreateDict(&expected)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 1)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "open")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "path", "/foo")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "oflag", O_RDONLY)); |
| |
| PP_Var response; |
| ASSERT_EQ(true, CreateDict(&response)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "id", 1)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "fd", fd)); |
| |
| Expect(expected, response); |
| } |
| |
| virtual void TearDown() { |
| JsFsTest::TearDown(); |
| } |
| |
| void OpenNode() { |
| EXPECT_EQ(0, fs_->Open(Path("/foo"), O_RDONLY, &node_)); |
| EXPECT_EQ(fd, sdk_util::static_scoped_ref_cast<JsFsNode>(node_)->fd()); |
| } |
| |
| protected: |
| ScopedNode node_; |
| }; |
| |
| const int JsFsNodeTest::fd = 123; |
| |
| } // namespace |
| |
| TEST_F(JsFsTest, Open) { |
| PP_Var expected; |
| ASSERT_EQ(true, CreateDict(&expected)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 1)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "open")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "path", "/foo")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "oflag", O_RDONLY)); |
| |
| PP_Var response; |
| ASSERT_EQ(true, CreateDict(&response)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "id", 1)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "fd", 123)); |
| |
| Expect(expected, response); |
| StartJsThread(); |
| |
| ScopedNode node; |
| EXPECT_EQ(0, fs_->Open(Path("/foo"), O_RDONLY, &node)); |
| EXPECT_EQ(123, sdk_util::static_scoped_ref_cast<JsFsNode>(node)->fd()); |
| } |
| |
| TEST_F(JsFsTest, Unlink) { |
| PP_Var expected; |
| ASSERT_EQ(true, CreateDict(&expected)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 1)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "unlink")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "path", "/foo")); |
| |
| PP_Var response; |
| ASSERT_EQ(true, CreateDict(&response)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "id", 1)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0)); |
| |
| Expect(expected, response); |
| StartJsThread(); |
| |
| EXPECT_EQ(0, fs_->Unlink(Path("/foo"))); |
| } |
| |
| TEST_F(JsFsTest, Mkdir) { |
| PP_Var expected; |
| ASSERT_EQ(true, CreateDict(&expected)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 1)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "mkdir")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "path", "/foo")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "mode", 0644)); |
| |
| PP_Var response; |
| ASSERT_EQ(true, CreateDict(&response)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "id", 1)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0)); |
| |
| Expect(expected, response); |
| StartJsThread(); |
| |
| EXPECT_EQ(0, fs_->Mkdir(Path("/foo"), 0644)); |
| } |
| |
| TEST_F(JsFsTest, Rmdir) { |
| PP_Var expected; |
| ASSERT_EQ(true, CreateDict(&expected)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 1)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "rmdir")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "path", "/foo")); |
| |
| PP_Var response; |
| ASSERT_EQ(true, CreateDict(&response)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "id", 1)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0)); |
| |
| Expect(expected, response); |
| StartJsThread(); |
| |
| EXPECT_EQ(0, fs_->Rmdir(Path("/foo"))); |
| } |
| |
| TEST_F(JsFsTest, Remove) { |
| PP_Var expected; |
| ASSERT_EQ(true, CreateDict(&expected)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 1)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "remove")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "path", "/foo")); |
| |
| PP_Var response; |
| ASSERT_EQ(true, CreateDict(&response)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "id", 1)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0)); |
| |
| Expect(expected, response); |
| StartJsThread(); |
| |
| EXPECT_EQ(0, fs_->Remove(Path("/foo"))); |
| } |
| |
| TEST_F(JsFsTest, Rename) { |
| PP_Var expected; |
| ASSERT_EQ(true, CreateDict(&expected)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 1)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "rename")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "old", "/foo")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "new", "/bar")); |
| |
| PP_Var response; |
| ASSERT_EQ(true, CreateDict(&response)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "id", 1)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0)); |
| |
| Expect(expected, response); |
| StartJsThread(); |
| |
| EXPECT_EQ(0, fs_->Rename(Path("/foo"), Path("/bar"))); |
| } |
| |
| TEST_F(JsFsNodeTest, GetStat) { |
| PP_Var expected; |
| ASSERT_EQ(true, CreateDict(&expected)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 2)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "fstat")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "fildes", fd)); |
| |
| PP_Var response; |
| ASSERT_EQ(true, CreateDict(&response)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "id", 2)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "st_ino", (int64_t) 1)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "st_mode", 2)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "st_nlink", 3)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "st_uid", 4)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "st_gid", 5)); |
| #ifdef __APPLE__ |
| ASSERT_EQ(true, SetDictKeyValue(&response, "st_rdev", (dev_t) 6)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "st_size", (off_t) 7)); |
| #else |
| ASSERT_EQ(true, SetDictKeyValue(&response, "st_rdev", (int64_t) 6)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "st_size", (int64_t) 7)); |
| #endif |
| ASSERT_EQ(true, SetDictKeyValue(&response, "st_blksize", 8)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "st_blocks", 9)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "st_atime", (int64_t) 10)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "st_mtime", (int64_t) 11)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "st_ctime", (int64_t) 12)); |
| |
| Expect(expected, response); |
| StartJsThread(); |
| OpenNode(); |
| |
| struct stat statbuf; |
| EXPECT_EQ(0, node_->GetStat(&statbuf)); |
| EXPECT_EQ(fs_->dev(), statbuf.st_dev); |
| EXPECT_EQ(1, statbuf.st_ino); |
| EXPECT_EQ(2, statbuf.st_mode); |
| EXPECT_EQ(3, statbuf.st_nlink); |
| EXPECT_EQ(4, statbuf.st_uid); |
| EXPECT_EQ(5, statbuf.st_gid); |
| EXPECT_EQ(6, statbuf.st_rdev); |
| EXPECT_EQ(7, statbuf.st_size); |
| EXPECT_EQ(8, statbuf.st_blksize); |
| EXPECT_EQ(9, statbuf.st_blocks); |
| EXPECT_EQ(10, statbuf.st_atime); |
| EXPECT_EQ(11, statbuf.st_mtime); |
| EXPECT_EQ(12, statbuf.st_ctime); |
| } |
| |
| TEST_F(JsFsNodeTest, FSync) { |
| PP_Var expected; |
| ASSERT_EQ(true, CreateDict(&expected)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 2)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "fsync")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "fildes", fd)); |
| |
| PP_Var response; |
| ASSERT_EQ(true, CreateDict(&response)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "id", 2)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0)); |
| |
| Expect(expected, response); |
| StartJsThread(); |
| OpenNode(); |
| |
| EXPECT_EQ(0, node_->FSync()); |
| } |
| |
| TEST_F(JsFsNodeTest, FTruncate) { |
| PP_Var expected; |
| ASSERT_EQ(true, CreateDict(&expected)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 2)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "ftruncate")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "fildes", fd)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "length", 0)); |
| |
| PP_Var response; |
| ASSERT_EQ(true, CreateDict(&response)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "id", 2)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0)); |
| |
| Expect(expected, response); |
| StartJsThread(); |
| OpenNode(); |
| |
| EXPECT_EQ(0, node_->FTruncate(0)); |
| } |
| |
| TEST_F(JsFsNodeTest, Read) { |
| const size_t kReadLength = 100; |
| |
| PP_Var expected; |
| ASSERT_EQ(true, CreateDict(&expected)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 2)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "pread")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "fildes", fd)); |
| ASSERT_EQ(true, |
| SetDictKeyValue(&expected, "nbyte", static_cast<int>(kReadLength))); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "offset", 200)); |
| |
| PP_Var response; |
| ASSERT_EQ(true, CreateDict(&response)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "id", 2)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0)); |
| ASSERT_EQ( |
| true, |
| SetDictKeyValue(&response, "buf", CreateDummyArrayBuffer(kReadLength))); |
| |
| Expect(expected, response); |
| StartJsThread(); |
| OpenNode(); |
| |
| HandleAttr attr; |
| attr.offs = 200; |
| uint8_t buf[kReadLength]; |
| int bytes_read; |
| EXPECT_EQ(0, node_->Read(attr, buf, kReadLength, &bytes_read)); |
| EXPECT_EQ(kReadLength, bytes_read); |
| EXPECT_TRUE(EqualsDummyArrayBuffer(buf, kReadLength)); |
| } |
| |
| TEST_F(JsFsNodeTest, Write) { |
| const size_t kWriteLength = 100; |
| |
| PP_Var expected; |
| ASSERT_EQ(true, CreateDict(&expected)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 2)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "pwrite")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "fildes", fd)); |
| ASSERT_EQ( |
| true, |
| SetDictKeyValue(&expected, "buf", CreateDummyArrayBuffer(kWriteLength))); |
| ASSERT_EQ( |
| true, |
| SetDictKeyValue(&expected, "nbyte", static_cast<int>(kWriteLength))); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "offset", 200)); |
| |
| PP_Var response; |
| ASSERT_EQ(true, CreateDict(&response)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "id", 2)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "nwrote", kWriteLength)); |
| |
| Expect(expected, response); |
| StartJsThread(); |
| OpenNode(); |
| |
| HandleAttr attr; |
| attr.offs = 200; |
| |
| uint8_t buf[kWriteLength]; |
| FillDummyBuffer(buf, kWriteLength); |
| |
| int bytes_written; |
| EXPECT_EQ(0, node_->Write(attr, buf, kWriteLength, &bytes_written)); |
| EXPECT_EQ(kWriteLength, bytes_written); |
| } |
| |
| TEST_F(JsFsNodeTest, GetDents) { |
| PP_Var expected; |
| ASSERT_EQ(true, CreateDict(&expected)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "id", 2)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "cmd", "getdents")); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "fildes", fd)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "offs", 0)); |
| ASSERT_EQ(true, SetDictKeyValue(&expected, "count", 2)); |
| |
| PP_Var entry0; |
| ASSERT_EQ(true, CreateDict(&entry0)); |
| ASSERT_EQ(true, SetDictKeyValue(&entry0, "d_ino", 2)); |
| ASSERT_EQ(true, SetDictKeyValue(&entry0, "d_name", ".")); |
| PP_Var entry1; |
| ASSERT_EQ(true, CreateDict(&entry1)); |
| ASSERT_EQ(true, SetDictKeyValue(&entry1, "d_ino", 3)); |
| ASSERT_EQ(true, SetDictKeyValue(&entry1, "d_name", "..")); |
| PP_Var array; |
| ASSERT_EQ(true, CreateArray(&array)); |
| ASSERT_EQ(true, SetArrayValue(&array, 0, entry0)); |
| ASSERT_EQ(true, SetArrayValue(&array, 1, entry1)); |
| PP_Var response; |
| ASSERT_EQ(true, CreateDict(&response)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "id", 2)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "error", 0)); |
| ASSERT_EQ(true, SetDictKeyValue(&response, "dirents", array)); |
| |
| Expect(expected, response); |
| StartJsThread(); |
| OpenNode(); |
| |
| dirent buf[2]; |
| int bytes_written; |
| EXPECT_EQ(0, node_->GetDents(0, buf, sizeof(dirent) * 2, &bytes_written)); |
| EXPECT_EQ(sizeof(dirent) * 2, bytes_written); |
| EXPECT_EQ(2, buf[0].d_ino); |
| EXPECT_EQ(sizeof(dirent), buf[0].d_reclen); |
| EXPECT_STREQ(".", buf[0].d_name); |
| EXPECT_EQ(3, buf[1].d_ino); |
| EXPECT_EQ(sizeof(dirent), buf[1].d_reclen); |
| EXPECT_STREQ("..", buf[1].d_name); |
| } |