diff options
Diffstat (limited to 'chromium/ppapi/proxy/file_system_resource_unittest.cc')
-rw-r--r-- | chromium/ppapi/proxy/file_system_resource_unittest.cc | 369 |
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, ¶ms, &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, ¶ms, &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, ¶ms, &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, ¶ms, &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, ¶ms, &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, ¶ms, &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 |