Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / ppapi / proxy / file_system_resource_unittest.cc
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/message_loop/message_loop.h"
6 #include "ppapi/c/pp_errors.h"
7 #include "ppapi/c/ppb_file_io.h"
8 #include "ppapi/c/ppb_file_ref.h"
9 #include "ppapi/c/ppb_file_system.h"
10 #include "ppapi/proxy/file_system_resource.h"
11 #include "ppapi/proxy/locking_resource_releaser.h"
12 #include "ppapi/proxy/plugin_message_filter.h"
13 #include "ppapi/proxy/ppapi_message_utils.h"
14 #include "ppapi/proxy/ppapi_messages.h"
15 #include "ppapi/proxy/ppapi_proxy_test.h"
16 #include "ppapi/shared_impl/proxy_lock.h"
17 #include "ppapi/shared_impl/scoped_pp_var.h"
18 #include "ppapi/shared_impl/var.h"
19 #include "ppapi/thunk/enter.h"
20 #include "ppapi/thunk/ppb_file_system_api.h"
21 #include "ppapi/thunk/thunk.h"
22
23 using ppapi::proxy::ResourceMessageTestSink;
24 using ppapi::thunk::EnterResource;
25 using ppapi::thunk::PPB_FileSystem_API;
26
27 namespace ppapi {
28 namespace proxy {
29
30 namespace {
31
32 const int64_t kExpectedFileSystemSize = 100;
33 const int64_t kQuotaRequestAmount1 = 10;
34 const int64_t kQuotaRequestAmount2 = 20;
35
36 class MockCompletionCallback {
37  public:
38   MockCompletionCallback() : called_(false) {}
39
40   bool called() { return called_; }
41   int32_t result() { return result_; }
42
43   static void Callback(void* user_data, int32_t result) {
44     MockCompletionCallback* that =
45         reinterpret_cast<MockCompletionCallback*>(user_data);
46     that->called_ = true;
47     that->result_ = result;
48   }
49
50  private:
51   bool called_;
52   int32_t result_;
53 };
54
55 class MockRequestQuotaCallback {
56  public:
57   MockRequestQuotaCallback() : called_(false) {}
58
59   bool called() { return called_; }
60   int64_t result() { return result_; }
61
62   void Reset() { called_ = false; }
63
64   void Callback(int64_t result) {
65     ASSERT_FALSE(called_);
66     called_ = true;
67     result_ = result;
68   }
69
70  private:
71   bool called_;
72   int64_t result_;
73 };
74
75 class FileSystemResourceTest : public PluginProxyTest {
76  public:
77   FileSystemResourceTest() {}
78
79   void SendReply(const ResourceMessageCallParams& params,
80                  int32_t result,
81                  const IPC::Message& nested_message) {
82     ResourceMessageReplyParams reply_params(params.pp_resource(),
83                                             params.sequence());
84     reply_params.set_result(result);
85     PluginMessageFilter::DispatchResourceReplyForTest(
86         reply_params, nested_message);
87   }
88
89   void SendOpenReply(const ResourceMessageCallParams& params, int32_t result) {
90     SendReply(params, result, PpapiPluginMsg_FileSystem_OpenReply());
91   }
92
93   // Opens the given file system.
94   void OpenFileSystem(PP_Resource file_system) {
95     MockCompletionCallback cb;
96     int32_t result = thunk::GetPPB_FileSystem_1_0_Thunk()->Open(
97         file_system,
98         kExpectedFileSystemSize,
99         PP_MakeCompletionCallback(&MockCompletionCallback::Callback, &cb));
100     ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
101
102     // Should have sent two new "open" messages to the browser and renderer.
103     ResourceMessageTestSink::ResourceCallVector open_messages =
104         sink().GetAllResourceCallsMatching(PpapiHostMsg_FileSystem_Open::ID);
105     ASSERT_EQ(2U, open_messages.size());
106     sink().ClearMessages();
107
108     // The resource is expecting two replies.
109     SendOpenReply(open_messages[0].first, PP_OK);
110     SendOpenReply(open_messages[1].first, PP_OK);
111
112     ASSERT_TRUE(cb.called());
113     ASSERT_EQ(PP_OK, cb.result());
114   }
115
116   // Opens the given file in the given file system. Since there is no host,
117   // the file handle will be invalid.
118   void OpenFile(PP_Resource file_io,
119                 PP_Resource file_ref,
120                 PP_Resource file_system) {
121     MockCompletionCallback cb;
122     int32_t result = thunk::GetPPB_FileIO_1_1_Thunk()->Open(
123         file_io,
124         file_ref,
125         PP_FILEOPENFLAG_WRITE,
126         PP_MakeCompletionCallback(&MockCompletionCallback::Callback, &cb));
127     ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
128
129     // Should have sent an "open" message.
130     ResourceMessageCallParams params;
131     IPC::Message msg;
132     ASSERT_TRUE(sink().GetFirstResourceCallMatching(
133         PpapiHostMsg_FileIO_Open::ID, &params, &msg));
134     sink().ClearMessages();
135
136     // Send a success reply.
137     ResourceMessageReplyParams reply_params(params.pp_resource(),
138                                             params.sequence());
139     reply_params.set_result(PP_OK);
140     PluginMessageFilter::DispatchResourceReplyForTest(
141         reply_params,
142         PpapiPluginMsg_FileIO_OpenReply(file_system,
143                                         0 /* max_written_offset */));
144   }
145 };
146
147 }  // namespace
148
149 // Test that Open fails if either host returns failure. The other tests exercise
150 // the case where both hosts return PP_OK.
151 TEST_F(FileSystemResourceTest, OpenFailure) {
152   const PPB_FileSystem_1_0* fs_iface = thunk::GetPPB_FileSystem_1_0_Thunk();
153   // Fail if the first reply doesn't return PP_OK.
154   {
155     LockingResourceReleaser file_system(
156         fs_iface->Create(pp_instance(), PP_FILESYSTEMTYPE_LOCALTEMPORARY));
157
158     MockCompletionCallback cb;
159     int32_t result = thunk::GetPPB_FileSystem_1_0_Thunk()->Open(
160         file_system.get(),
161         kExpectedFileSystemSize,
162         PP_MakeCompletionCallback(&MockCompletionCallback::Callback, &cb));
163     ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
164
165     ResourceMessageTestSink::ResourceCallVector open_messages =
166         sink().GetAllResourceCallsMatching(PpapiHostMsg_FileSystem_Open::ID);
167     ASSERT_EQ(2U, open_messages.size());
168     sink().ClearMessages();
169
170     SendOpenReply(open_messages[0].first, PP_ERROR_FAILED);
171     SendOpenReply(open_messages[1].first, PP_OK);
172
173     ASSERT_TRUE(cb.called());
174     ASSERT_EQ(PP_ERROR_FAILED, cb.result());
175   }
176   // Fail if the second reply doesn't return PP_OK.
177   {
178     LockingResourceReleaser file_system(
179         fs_iface->Create(pp_instance(), PP_FILESYSTEMTYPE_LOCALTEMPORARY));
180
181     MockCompletionCallback cb;
182     int32_t result = thunk::GetPPB_FileSystem_1_0_Thunk()->Open(
183         file_system.get(),
184         kExpectedFileSystemSize,
185         PP_MakeCompletionCallback(&MockCompletionCallback::Callback, &cb));
186     ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
187
188     ResourceMessageTestSink::ResourceCallVector open_messages =
189         sink().GetAllResourceCallsMatching(PpapiHostMsg_FileSystem_Open::ID);
190     ASSERT_EQ(2U, open_messages.size());
191     sink().ClearMessages();
192
193     SendOpenReply(open_messages[0].first, PP_OK);
194     SendOpenReply(open_messages[1].first, PP_ERROR_FAILED);
195
196     ASSERT_TRUE(cb.called());
197     ASSERT_EQ(PP_ERROR_FAILED, cb.result());
198   }
199 }
200
201 TEST_F(FileSystemResourceTest, RequestQuota) {
202   const PPB_FileSystem_1_0* fs_iface = thunk::GetPPB_FileSystem_1_0_Thunk();
203   const PPB_FileRef_1_1* fref_iface = thunk::GetPPB_FileRef_1_1_Thunk();
204   const PPB_FileIO_1_1* fio_iface = thunk::GetPPB_FileIO_1_1_Thunk();
205
206   LockingResourceReleaser file_system(
207       fs_iface->Create(pp_instance(), PP_FILESYSTEMTYPE_LOCALTEMPORARY));
208
209   OpenFileSystem(file_system.get());
210
211   // Create and open two files in the file system. FileIOResource calls
212   // FileSystemResource::OpenQuotaFile on success.
213   LockingResourceReleaser file_ref1(
214       fref_iface->Create(file_system.get(), "/file1"));
215   LockingResourceReleaser file_io1(fio_iface->Create(pp_instance()));
216   OpenFile(file_io1.get(), file_ref1.get(), file_system.get());
217   LockingResourceReleaser file_ref2(
218       fref_iface->Create(file_system.get(), "/file2"));
219   LockingResourceReleaser file_io2(fio_iface->Create(pp_instance()));
220   OpenFile(file_io2.get(), file_ref2.get(), file_system.get());
221
222   EnterResource<PPB_FileSystem_API> enter(file_system.get(), true);
223   ASSERT_FALSE(enter.failed());
224   PPB_FileSystem_API* file_system_api = enter.object();
225
226   MockRequestQuotaCallback cb1;
227   int64_t result = file_system_api->RequestQuota(
228       kQuotaRequestAmount1,
229       base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb1)));
230   ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
231
232   // Should have sent a "reserve quota" message, with the amount of the request
233   // and a map of all currently open files to their max written offsets.
234   ResourceMessageCallParams params;
235   IPC::Message msg;
236   ASSERT_TRUE(sink().GetFirstResourceCallMatching(
237       PpapiHostMsg_FileSystem_ReserveQuota::ID, &params, &msg));
238   sink().ClearMessages();
239
240   int64_t amount = 0;
241   FileGrowthMap file_growths;
242   ASSERT_TRUE(UnpackMessage<PpapiHostMsg_FileSystem_ReserveQuota>(
243       msg, &amount, &file_growths));
244   ASSERT_EQ(kQuotaRequestAmount1, amount);
245   ASSERT_EQ(2U, file_growths.size());
246   ASSERT_EQ(0, file_growths[file_io1.get()].max_written_offset);
247   ASSERT_EQ(0, file_growths[file_io2.get()].max_written_offset);
248
249   // Make another request while the "reserve quota" message is pending.
250   MockRequestQuotaCallback cb2;
251   result = file_system_api->RequestQuota(
252       kQuotaRequestAmount2,
253       base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb2)));
254   ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
255   // No new "reserve quota" message should be sent while one is pending.
256   ASSERT_FALSE(sink().GetFirstResourceCallMatching(
257       PpapiHostMsg_FileSystem_ReserveQuota::ID, &params, &msg));
258   {
259     ProxyAutoUnlock unlock_to_prevent_deadlock;
260     // Reply with quota reservation amount sufficient to cover both requests.
261     // Both callbacks should be called with the requests granted.
262     SendReply(params,
263               PP_OK,
264               PpapiPluginMsg_FileSystem_ReserveQuotaReply(
265                   kQuotaRequestAmount1 + kQuotaRequestAmount2,
266                   FileGrowthMapToFileSizeMapForTesting(file_growths)));
267   }
268   ASSERT_TRUE(cb1.called());
269   ASSERT_EQ(kQuotaRequestAmount1, cb1.result());
270   ASSERT_TRUE(cb2.called());
271   ASSERT_EQ(kQuotaRequestAmount2, cb2.result());
272   cb1.Reset();
273   cb2.Reset();
274
275   // All requests should fail when insufficient quota is returned to satisfy
276   // the first request.
277   result = file_system_api->RequestQuota(
278       kQuotaRequestAmount1,
279       base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb1)));
280   ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
281   result = file_system_api->RequestQuota(
282       kQuotaRequestAmount2,
283       base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb2)));
284   ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
285
286   ASSERT_TRUE(sink().GetFirstResourceCallMatching(
287       PpapiHostMsg_FileSystem_ReserveQuota::ID, &params, &msg));
288   sink().ClearMessages();
289   {
290     ProxyAutoUnlock unlock_to_prevent_deadlock;
291     // Reply with quota reservation amount insufficient to cover the first
292     // request.
293     SendReply(params,
294               PP_OK,
295               PpapiPluginMsg_FileSystem_ReserveQuotaReply(
296                   kQuotaRequestAmount1 - 1,
297                   FileGrowthMapToFileSizeMapForTesting(file_growths)));
298   }
299   ASSERT_TRUE(cb1.called());
300   ASSERT_EQ(0, cb1.result());
301   ASSERT_TRUE(cb2.called());
302   ASSERT_EQ(0, cb2.result());
303   cb1.Reset();
304   cb2.Reset();
305
306   // A new request should be made if the quota reservation is enough to satisfy
307   // at least one request.
308   result = file_system_api->RequestQuota(
309       kQuotaRequestAmount1,
310       base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb1)));
311   ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
312   result = file_system_api->RequestQuota(
313       kQuotaRequestAmount2,
314       base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb2)));
315   ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
316
317   ASSERT_TRUE(sink().GetFirstResourceCallMatching(
318       PpapiHostMsg_FileSystem_ReserveQuota::ID, &params, &msg));
319   sink().ClearMessages();
320   {
321     ProxyAutoUnlock unlock_to_prevent_deadlock;
322     // Reply with quota reservation amount sufficient only to cover the first
323     // request.
324     SendReply(params,
325               PP_OK,
326               PpapiPluginMsg_FileSystem_ReserveQuotaReply(
327                   kQuotaRequestAmount1,
328                   FileGrowthMapToFileSizeMapForTesting(file_growths)));
329   }
330   ASSERT_TRUE(cb1.called());
331   ASSERT_EQ(kQuotaRequestAmount1, cb1.result());
332   ASSERT_FALSE(cb2.called());
333
334   // Another request message should have been sent.
335   ASSERT_TRUE(sink().GetFirstResourceCallMatching(
336       PpapiHostMsg_FileSystem_ReserveQuota::ID, &params, &msg));
337   sink().ClearMessages();
338   {
339     ProxyAutoUnlock unlock_to_prevent_deadlock;
340     // Reply with quota reservation amount sufficient to cover the second
341     // request and some extra.
342     SendReply(params,
343               PP_OK,
344               PpapiPluginMsg_FileSystem_ReserveQuotaReply(
345                   kQuotaRequestAmount1 + kQuotaRequestAmount2,
346                   FileGrowthMapToFileSizeMapForTesting(file_growths)));
347   }
348
349   ASSERT_TRUE(cb2.called());
350   ASSERT_EQ(kQuotaRequestAmount2, cb2.result());
351   cb1.Reset();
352   cb2.Reset();
353
354   // There is kQuotaRequestAmount1 of quota left, and a request for it should
355   // succeed immediately.
356   result = file_system_api->RequestQuota(
357       kQuotaRequestAmount1,
358       base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb1)));
359   ASSERT_EQ(kQuotaRequestAmount1, result);
360 }
361
362 }  // namespace proxy
363 }  // namespace ppapi