summaryrefslogtreecommitdiffstats
path: root/chromium/ppapi/proxy/file_system_resource_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/ppapi/proxy/file_system_resource_unittest.cc')
-rw-r--r--chromium/ppapi/proxy/file_system_resource_unittest.cc369
1 files changed, 369 insertions, 0 deletions
diff --git a/chromium/ppapi/proxy/file_system_resource_unittest.cc b/chromium/ppapi/proxy/file_system_resource_unittest.cc
new file mode 100644
index 00000000000..9b5d981242d
--- /dev/null
+++ b/chromium/ppapi/proxy/file_system_resource_unittest.cc
@@ -0,0 +1,369 @@
+// 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 "base/message_loop/message_loop.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/c/ppb_file_io.h"
+#include "ppapi/c/ppb_file_ref.h"
+#include "ppapi/c/ppb_file_system.h"
+#include "ppapi/proxy/file_system_resource.h"
+#include "ppapi/proxy/locking_resource_releaser.h"
+#include "ppapi/proxy/plugin_message_filter.h"
+#include "ppapi/proxy/ppapi_message_utils.h"
+#include "ppapi/proxy/ppapi_messages.h"
+#include "ppapi/proxy/ppapi_proxy_test.h"
+#include "ppapi/shared_impl/proxy_lock.h"
+#include "ppapi/shared_impl/scoped_pp_var.h"
+#include "ppapi/shared_impl/var.h"
+#include "ppapi/thunk/enter.h"
+#include "ppapi/thunk/ppb_file_system_api.h"
+#include "ppapi/thunk/thunk.h"
+
+using ppapi::proxy::ResourceMessageTestSink;
+using ppapi::thunk::EnterResource;
+using ppapi::thunk::PPB_FileSystem_API;
+
+namespace ppapi {
+namespace proxy {
+
+namespace {
+
+const int64_t kExpectedFileSystemSize = 100;
+const int64_t kQuotaRequestAmount1 = 10;
+const int64_t kQuotaRequestAmount2 = 20;
+
+class MockCompletionCallback {
+ public:
+ MockCompletionCallback() : called_(false) {}
+
+ bool called() { return called_; }
+ int32_t result() { return result_; }
+
+ static void Callback(void* user_data, int32_t result) {
+ MockCompletionCallback* that =
+ reinterpret_cast<MockCompletionCallback*>(user_data);
+ that->called_ = true;
+ that->result_ = result;
+ }
+
+ private:
+ bool called_;
+ int32_t result_;
+};
+
+class MockRequestQuotaCallback {
+ public:
+ MockRequestQuotaCallback() : called_(false) {}
+
+ bool called() { return called_; }
+ int64_t result() { return result_; }
+
+ void Reset() { called_ = false; }
+
+ void Callback(int64_t result) {
+ ASSERT_FALSE(called_);
+ called_ = true;
+ result_ = result;
+ }
+
+ private:
+ bool called_;
+ int64_t result_;
+};
+
+class FileSystemResourceTest : public PluginProxyTest {
+ public:
+ const PPB_FileSystem_1_0* file_system_iface;
+ const PPB_FileRef_1_1* file_ref_iface;
+ const PPB_FileIO_1_1* file_io_iface;
+
+ FileSystemResourceTest()
+ : file_system_iface(thunk::GetPPB_FileSystem_1_0_Thunk()),
+ file_ref_iface(thunk::GetPPB_FileRef_1_1_Thunk()),
+ file_io_iface(thunk::GetPPB_FileIO_1_1_Thunk()) {
+ }
+
+ void SendReply(const ResourceMessageCallParams& params,
+ int32_t result,
+ const IPC::Message& nested_message) {
+ ResourceMessageReplyParams reply_params(params.pp_resource(),
+ params.sequence());
+ reply_params.set_result(result);
+ PluginMessageFilter::DispatchResourceReplyForTest(
+ reply_params, nested_message);
+ }
+
+ void SendOpenReply(const ResourceMessageCallParams& params, int32_t result) {
+ SendReply(params, result, PpapiPluginMsg_FileSystem_OpenReply());
+ }
+
+ // Opens the given file system.
+ void OpenFileSystem(PP_Resource file_system) {
+ MockCompletionCallback cb;
+ int32_t result = file_system_iface->Open(
+ file_system,
+ kExpectedFileSystemSize,
+ PP_MakeCompletionCallback(&MockCompletionCallback::Callback, &cb));
+ ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
+
+ // Should have sent two new "open" messages to the browser and renderer.
+ ResourceMessageTestSink::ResourceCallVector open_messages =
+ sink().GetAllResourceCallsMatching(PpapiHostMsg_FileSystem_Open::ID);
+ ASSERT_EQ(2U, open_messages.size());
+ sink().ClearMessages();
+
+ // The resource is expecting two replies.
+ SendOpenReply(open_messages[0].first, PP_OK);
+ SendOpenReply(open_messages[1].first, PP_OK);
+
+ ASSERT_TRUE(cb.called());
+ ASSERT_EQ(PP_OK, cb.result());
+ }
+
+ // Opens the given file in the given file system. Since there is no host,
+ // the file handle will be invalid.
+ void OpenFile(PP_Resource file_io,
+ PP_Resource file_ref,
+ PP_Resource file_system) {
+ MockCompletionCallback cb;
+ int32_t result = file_io_iface->Open(
+ file_io,
+ file_ref,
+ PP_FILEOPENFLAG_WRITE,
+ PP_MakeCompletionCallback(&MockCompletionCallback::Callback, &cb));
+ ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
+
+ // Should have sent an "open" message.
+ ResourceMessageCallParams params;
+ IPC::Message msg;
+ ASSERT_TRUE(sink().GetFirstResourceCallMatching(
+ PpapiHostMsg_FileIO_Open::ID, &params, &msg));
+ sink().ClearMessages();
+
+ // Send a success reply.
+ ResourceMessageReplyParams reply_params(params.pp_resource(),
+ params.sequence());
+ reply_params.set_result(PP_OK);
+ PluginMessageFilter::DispatchResourceReplyForTest(
+ reply_params,
+ PpapiPluginMsg_FileIO_OpenReply(file_system,
+ 0 /* max_written_offset */));
+ }
+};
+
+} // namespace
+
+// Test that Open fails if either host returns failure. The other tests exercise
+// the case where both hosts return PP_OK.
+TEST_F(FileSystemResourceTest, OpenFailure) {
+ // Fail if the first reply doesn't return PP_OK.
+ {
+ LockingResourceReleaser file_system(
+ file_system_iface->Create(pp_instance(),
+ PP_FILESYSTEMTYPE_LOCALTEMPORARY));
+
+ MockCompletionCallback cb;
+ int32_t result = file_system_iface->Open(
+ file_system.get(),
+ kExpectedFileSystemSize,
+ PP_MakeCompletionCallback(&MockCompletionCallback::Callback, &cb));
+ ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
+
+ ResourceMessageTestSink::ResourceCallVector open_messages =
+ sink().GetAllResourceCallsMatching(PpapiHostMsg_FileSystem_Open::ID);
+ ASSERT_EQ(2U, open_messages.size());
+ sink().ClearMessages();
+
+ SendOpenReply(open_messages[0].first, PP_ERROR_FAILED);
+ SendOpenReply(open_messages[1].first, PP_OK);
+
+ ASSERT_TRUE(cb.called());
+ ASSERT_EQ(PP_ERROR_FAILED, cb.result());
+ }
+ // Fail if the second reply doesn't return PP_OK.
+ {
+ LockingResourceReleaser file_system(
+ file_system_iface->Create(pp_instance(),
+ PP_FILESYSTEMTYPE_LOCALTEMPORARY));
+
+ MockCompletionCallback cb;
+ int32_t result = file_system_iface->Open(
+ file_system.get(),
+ kExpectedFileSystemSize,
+ PP_MakeCompletionCallback(&MockCompletionCallback::Callback, &cb));
+ ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
+
+ ResourceMessageTestSink::ResourceCallVector open_messages =
+ sink().GetAllResourceCallsMatching(PpapiHostMsg_FileSystem_Open::ID);
+ ASSERT_EQ(2U, open_messages.size());
+ sink().ClearMessages();
+
+ SendOpenReply(open_messages[0].first, PP_OK);
+ SendOpenReply(open_messages[1].first, PP_ERROR_FAILED);
+
+ ASSERT_TRUE(cb.called());
+ ASSERT_EQ(PP_ERROR_FAILED, cb.result());
+ }
+}
+
+TEST_F(FileSystemResourceTest, RequestQuota) {
+ LockingResourceReleaser file_system(
+ file_system_iface->Create(pp_instance(),
+ PP_FILESYSTEMTYPE_LOCALTEMPORARY));
+
+ OpenFileSystem(file_system.get());
+
+ // Create and open two files in the file system. FileIOResource calls
+ // FileSystemResource::OpenQuotaFile on success.
+ LockingResourceReleaser file_ref1(
+ file_ref_iface->Create(file_system.get(), "/file1"));
+ LockingResourceReleaser file_io1(file_io_iface->Create(pp_instance()));
+ OpenFile(file_io1.get(), file_ref1.get(), file_system.get());
+ LockingResourceReleaser file_ref2(
+ file_ref_iface->Create(file_system.get(), "/file2"));
+ LockingResourceReleaser file_io2(file_io_iface->Create(pp_instance()));
+ OpenFile(file_io2.get(), file_ref2.get(), file_system.get());
+
+ EnterResource<PPB_FileSystem_API> enter(file_system.get(), true);
+ ASSERT_FALSE(enter.failed());
+ PPB_FileSystem_API* file_system_api = enter.object();
+
+ MockRequestQuotaCallback cb1;
+ int64_t result = file_system_api->RequestQuota(
+ kQuotaRequestAmount1,
+ base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb1)));
+ ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
+
+ // Should have sent a "reserve quota" message, with the amount of the request
+ // and a map of all currently open files to their max written offsets.
+ ResourceMessageCallParams params;
+ IPC::Message msg;
+ ASSERT_TRUE(sink().GetFirstResourceCallMatching(
+ PpapiHostMsg_FileSystem_ReserveQuota::ID, &params, &msg));
+ sink().ClearMessages();
+
+ int64_t amount = 0;
+ FileGrowthMap file_growths;
+ ASSERT_TRUE(UnpackMessage<PpapiHostMsg_FileSystem_ReserveQuota>(
+ msg, &amount, &file_growths));
+ ASSERT_EQ(kQuotaRequestAmount1, amount);
+ ASSERT_EQ(2U, file_growths.size());
+ ASSERT_EQ(0, file_growths[file_io1.get()].max_written_offset);
+ ASSERT_EQ(0, file_growths[file_io2.get()].max_written_offset);
+
+ // Make another request while the "reserve quota" message is pending.
+ MockRequestQuotaCallback cb2;
+ result = file_system_api->RequestQuota(
+ kQuotaRequestAmount2,
+ base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb2)));
+ ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
+ // No new "reserve quota" message should be sent while one is pending.
+ ASSERT_FALSE(sink().GetFirstResourceCallMatching(
+ PpapiHostMsg_FileSystem_ReserveQuota::ID, &params, &msg));
+ {
+ ProxyAutoUnlock unlock_to_prevent_deadlock;
+ // Reply with quota reservation amount sufficient to cover both requests.
+ // Both callbacks should be called with the requests granted.
+ SendReply(params,
+ PP_OK,
+ PpapiPluginMsg_FileSystem_ReserveQuotaReply(
+ kQuotaRequestAmount1 + kQuotaRequestAmount2,
+ FileGrowthMapToFileSizeMapForTesting(file_growths)));
+ }
+ ASSERT_TRUE(cb1.called());
+ ASSERT_EQ(kQuotaRequestAmount1, cb1.result());
+ ASSERT_TRUE(cb2.called());
+ ASSERT_EQ(kQuotaRequestAmount2, cb2.result());
+ cb1.Reset();
+ cb2.Reset();
+
+ // All requests should fail when insufficient quota is returned to satisfy
+ // the first request.
+ result = file_system_api->RequestQuota(
+ kQuotaRequestAmount1,
+ base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb1)));
+ ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
+ result = file_system_api->RequestQuota(
+ kQuotaRequestAmount2,
+ base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb2)));
+ ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
+
+ ASSERT_TRUE(sink().GetFirstResourceCallMatching(
+ PpapiHostMsg_FileSystem_ReserveQuota::ID, &params, &msg));
+ sink().ClearMessages();
+ {
+ ProxyAutoUnlock unlock_to_prevent_deadlock;
+ // Reply with quota reservation amount insufficient to cover the first
+ // request.
+ SendReply(params,
+ PP_OK,
+ PpapiPluginMsg_FileSystem_ReserveQuotaReply(
+ kQuotaRequestAmount1 - 1,
+ FileGrowthMapToFileSizeMapForTesting(file_growths)));
+ }
+ ASSERT_TRUE(cb1.called());
+ ASSERT_EQ(0, cb1.result());
+ ASSERT_TRUE(cb2.called());
+ ASSERT_EQ(0, cb2.result());
+ cb1.Reset();
+ cb2.Reset();
+
+ // A new request should be made if the quota reservation is enough to satisfy
+ // at least one request.
+ result = file_system_api->RequestQuota(
+ kQuotaRequestAmount1,
+ base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb1)));
+ ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
+ result = file_system_api->RequestQuota(
+ kQuotaRequestAmount2,
+ base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb2)));
+ ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
+
+ ASSERT_TRUE(sink().GetFirstResourceCallMatching(
+ PpapiHostMsg_FileSystem_ReserveQuota::ID, &params, &msg));
+ sink().ClearMessages();
+ {
+ ProxyAutoUnlock unlock_to_prevent_deadlock;
+ // Reply with quota reservation amount sufficient only to cover the first
+ // request.
+ SendReply(params,
+ PP_OK,
+ PpapiPluginMsg_FileSystem_ReserveQuotaReply(
+ kQuotaRequestAmount1,
+ FileGrowthMapToFileSizeMapForTesting(file_growths)));
+ }
+ ASSERT_TRUE(cb1.called());
+ ASSERT_EQ(kQuotaRequestAmount1, cb1.result());
+ ASSERT_FALSE(cb2.called());
+
+ // Another request message should have been sent.
+ ASSERT_TRUE(sink().GetFirstResourceCallMatching(
+ PpapiHostMsg_FileSystem_ReserveQuota::ID, &params, &msg));
+ sink().ClearMessages();
+ {
+ ProxyAutoUnlock unlock_to_prevent_deadlock;
+ // Reply with quota reservation amount sufficient to cover the second
+ // request and some extra.
+ SendReply(params,
+ PP_OK,
+ PpapiPluginMsg_FileSystem_ReserveQuotaReply(
+ kQuotaRequestAmount1 + kQuotaRequestAmount2,
+ FileGrowthMapToFileSizeMapForTesting(file_growths)));
+ }
+
+ ASSERT_TRUE(cb2.called());
+ ASSERT_EQ(kQuotaRequestAmount2, cb2.result());
+ cb1.Reset();
+ cb2.Reset();
+
+ // There is kQuotaRequestAmount1 of quota left, and a request for it should
+ // succeed immediately.
+ result = file_system_api->RequestQuota(
+ kQuotaRequestAmount1,
+ base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb1)));
+ ASSERT_EQ(kQuotaRequestAmount1, result);
+}
+
+} // namespace proxy
+} // namespace ppapi