Upstream version 7.36.149.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   const PPB_FileSystem_1_0* file_system_iface;
78   const PPB_FileRef_1_1* file_ref_iface;
79   const PPB_FileIO_1_1* file_io_iface;
80
81   FileSystemResourceTest()
82       : file_system_iface(thunk::GetPPB_FileSystem_1_0_Thunk()),
83         file_ref_iface(thunk::GetPPB_FileRef_1_1_Thunk()),
84         file_io_iface(thunk::GetPPB_FileIO_1_1_Thunk()) {
85   }
86
87   void SendReply(const ResourceMessageCallParams& params,
88                  int32_t result,
89                  const IPC::Message& nested_message) {
90     ResourceMessageReplyParams reply_params(params.pp_resource(),
91                                             params.sequence());
92     reply_params.set_result(result);
93     PluginMessageFilter::DispatchResourceReplyForTest(
94         reply_params, nested_message);
95   }
96
97   void SendOpenReply(const ResourceMessageCallParams& params, int32_t result) {
98     SendReply(params, result, PpapiPluginMsg_FileSystem_OpenReply());
99   }
100
101   // Opens the given file system.
102   void OpenFileSystem(PP_Resource file_system) {
103     MockCompletionCallback cb;
104     int32_t result = file_system_iface->Open(
105         file_system,
106         kExpectedFileSystemSize,
107         PP_MakeCompletionCallback(&MockCompletionCallback::Callback, &cb));
108     ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
109
110     // Should have sent two new "open" messages to the browser and renderer.
111     ResourceMessageTestSink::ResourceCallVector open_messages =
112         sink().GetAllResourceCallsMatching(PpapiHostMsg_FileSystem_Open::ID);
113     ASSERT_EQ(2U, open_messages.size());
114     sink().ClearMessages();
115
116     // The resource is expecting two replies.
117     SendOpenReply(open_messages[0].first, PP_OK);
118     SendOpenReply(open_messages[1].first, PP_OK);
119
120     ASSERT_TRUE(cb.called());
121     ASSERT_EQ(PP_OK, cb.result());
122   }
123
124   // Opens the given file in the given file system. Since there is no host,
125   // the file handle will be invalid.
126   void OpenFile(PP_Resource file_io,
127                 PP_Resource file_ref,
128                 PP_Resource file_system) {
129     MockCompletionCallback cb;
130     int32_t result = file_io_iface->Open(
131         file_io,
132         file_ref,
133         PP_FILEOPENFLAG_WRITE,
134         PP_MakeCompletionCallback(&MockCompletionCallback::Callback, &cb));
135     ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
136
137     // Should have sent an "open" message.
138     ResourceMessageCallParams params;
139     IPC::Message msg;
140     ASSERT_TRUE(sink().GetFirstResourceCallMatching(
141         PpapiHostMsg_FileIO_Open::ID, &params, &msg));
142     sink().ClearMessages();
143
144     // Send a success reply.
145     ResourceMessageReplyParams reply_params(params.pp_resource(),
146                                             params.sequence());
147     reply_params.set_result(PP_OK);
148     PluginMessageFilter::DispatchResourceReplyForTest(
149         reply_params,
150         PpapiPluginMsg_FileIO_OpenReply(file_system,
151                                         0 /* max_written_offset */));
152   }
153 };
154
155 }  // namespace
156
157 // Test that Open fails if either host returns failure. The other tests exercise
158 // the case where both hosts return PP_OK.
159 TEST_F(FileSystemResourceTest, OpenFailure) {
160   // Fail if the first reply doesn't return PP_OK.
161   {
162     LockingResourceReleaser file_system(
163         file_system_iface->Create(pp_instance(),
164                                   PP_FILESYSTEMTYPE_LOCALTEMPORARY));
165
166     MockCompletionCallback cb;
167     int32_t result = file_system_iface->Open(
168         file_system.get(),
169         kExpectedFileSystemSize,
170         PP_MakeCompletionCallback(&MockCompletionCallback::Callback, &cb));
171     ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
172
173     ResourceMessageTestSink::ResourceCallVector open_messages =
174         sink().GetAllResourceCallsMatching(PpapiHostMsg_FileSystem_Open::ID);
175     ASSERT_EQ(2U, open_messages.size());
176     sink().ClearMessages();
177
178     SendOpenReply(open_messages[0].first, PP_ERROR_FAILED);
179     SendOpenReply(open_messages[1].first, PP_OK);
180
181     ASSERT_TRUE(cb.called());
182     ASSERT_EQ(PP_ERROR_FAILED, cb.result());
183   }
184   // Fail if the second reply doesn't return PP_OK.
185   {
186     LockingResourceReleaser file_system(
187         file_system_iface->Create(pp_instance(),
188                                   PP_FILESYSTEMTYPE_LOCALTEMPORARY));
189
190     MockCompletionCallback cb;
191     int32_t result = file_system_iface->Open(
192         file_system.get(),
193         kExpectedFileSystemSize,
194         PP_MakeCompletionCallback(&MockCompletionCallback::Callback, &cb));
195     ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
196
197     ResourceMessageTestSink::ResourceCallVector open_messages =
198         sink().GetAllResourceCallsMatching(PpapiHostMsg_FileSystem_Open::ID);
199     ASSERT_EQ(2U, open_messages.size());
200     sink().ClearMessages();
201
202     SendOpenReply(open_messages[0].first, PP_OK);
203     SendOpenReply(open_messages[1].first, PP_ERROR_FAILED);
204
205     ASSERT_TRUE(cb.called());
206     ASSERT_EQ(PP_ERROR_FAILED, cb.result());
207   }
208 }
209
210 TEST_F(FileSystemResourceTest, RequestQuota) {
211   LockingResourceReleaser file_system(
212       file_system_iface->Create(pp_instance(),
213                                 PP_FILESYSTEMTYPE_LOCALTEMPORARY));
214
215   OpenFileSystem(file_system.get());
216
217   // Create and open two files in the file system. FileIOResource calls
218   // FileSystemResource::OpenQuotaFile on success.
219   LockingResourceReleaser file_ref1(
220       file_ref_iface->Create(file_system.get(), "/file1"));
221   LockingResourceReleaser file_io1(file_io_iface->Create(pp_instance()));
222   OpenFile(file_io1.get(), file_ref1.get(), file_system.get());
223   LockingResourceReleaser file_ref2(
224       file_ref_iface->Create(file_system.get(), "/file2"));
225   LockingResourceReleaser file_io2(file_io_iface->Create(pp_instance()));
226   OpenFile(file_io2.get(), file_ref2.get(), file_system.get());
227
228   EnterResource<PPB_FileSystem_API> enter(file_system.get(), true);
229   ASSERT_FALSE(enter.failed());
230   PPB_FileSystem_API* file_system_api = enter.object();
231
232   MockRequestQuotaCallback cb1;
233   int64_t result = file_system_api->RequestQuota(
234       kQuotaRequestAmount1,
235       base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb1)));
236   ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
237
238   // Should have sent a "reserve quota" message, with the amount of the request
239   // and a map of all currently open files to their max written offsets.
240   ResourceMessageCallParams params;
241   IPC::Message msg;
242   ASSERT_TRUE(sink().GetFirstResourceCallMatching(
243       PpapiHostMsg_FileSystem_ReserveQuota::ID, &params, &msg));
244   sink().ClearMessages();
245
246   int64_t amount = 0;
247   FileGrowthMap file_growths;
248   ASSERT_TRUE(UnpackMessage<PpapiHostMsg_FileSystem_ReserveQuota>(
249       msg, &amount, &file_growths));
250   ASSERT_EQ(kQuotaRequestAmount1, amount);
251   ASSERT_EQ(2U, file_growths.size());
252   ASSERT_EQ(0, file_growths[file_io1.get()].max_written_offset);
253   ASSERT_EQ(0, file_growths[file_io2.get()].max_written_offset);
254
255   // Make another request while the "reserve quota" message is pending.
256   MockRequestQuotaCallback cb2;
257   result = file_system_api->RequestQuota(
258       kQuotaRequestAmount2,
259       base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb2)));
260   ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
261   // No new "reserve quota" message should be sent while one is pending.
262   ASSERT_FALSE(sink().GetFirstResourceCallMatching(
263       PpapiHostMsg_FileSystem_ReserveQuota::ID, &params, &msg));
264   {
265     ProxyAutoUnlock unlock_to_prevent_deadlock;
266     // Reply with quota reservation amount sufficient to cover both requests.
267     // Both callbacks should be called with the requests granted.
268     SendReply(params,
269               PP_OK,
270               PpapiPluginMsg_FileSystem_ReserveQuotaReply(
271                   kQuotaRequestAmount1 + kQuotaRequestAmount2,
272                   FileGrowthMapToFileSizeMapForTesting(file_growths)));
273   }
274   ASSERT_TRUE(cb1.called());
275   ASSERT_EQ(kQuotaRequestAmount1, cb1.result());
276   ASSERT_TRUE(cb2.called());
277   ASSERT_EQ(kQuotaRequestAmount2, cb2.result());
278   cb1.Reset();
279   cb2.Reset();
280
281   // All requests should fail when insufficient quota is returned to satisfy
282   // the first request.
283   result = file_system_api->RequestQuota(
284       kQuotaRequestAmount1,
285       base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb1)));
286   ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
287   result = file_system_api->RequestQuota(
288       kQuotaRequestAmount2,
289       base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb2)));
290   ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
291
292   ASSERT_TRUE(sink().GetFirstResourceCallMatching(
293       PpapiHostMsg_FileSystem_ReserveQuota::ID, &params, &msg));
294   sink().ClearMessages();
295   {
296     ProxyAutoUnlock unlock_to_prevent_deadlock;
297     // Reply with quota reservation amount insufficient to cover the first
298     // request.
299     SendReply(params,
300               PP_OK,
301               PpapiPluginMsg_FileSystem_ReserveQuotaReply(
302                   kQuotaRequestAmount1 - 1,
303                   FileGrowthMapToFileSizeMapForTesting(file_growths)));
304   }
305   ASSERT_TRUE(cb1.called());
306   ASSERT_EQ(0, cb1.result());
307   ASSERT_TRUE(cb2.called());
308   ASSERT_EQ(0, cb2.result());
309   cb1.Reset();
310   cb2.Reset();
311
312   // A new request should be made if the quota reservation is enough to satisfy
313   // at least one request.
314   result = file_system_api->RequestQuota(
315       kQuotaRequestAmount1,
316       base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb1)));
317   ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
318   result = file_system_api->RequestQuota(
319       kQuotaRequestAmount2,
320       base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb2)));
321   ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
322
323   ASSERT_TRUE(sink().GetFirstResourceCallMatching(
324       PpapiHostMsg_FileSystem_ReserveQuota::ID, &params, &msg));
325   sink().ClearMessages();
326   {
327     ProxyAutoUnlock unlock_to_prevent_deadlock;
328     // Reply with quota reservation amount sufficient only to cover the first
329     // request.
330     SendReply(params,
331               PP_OK,
332               PpapiPluginMsg_FileSystem_ReserveQuotaReply(
333                   kQuotaRequestAmount1,
334                   FileGrowthMapToFileSizeMapForTesting(file_growths)));
335   }
336   ASSERT_TRUE(cb1.called());
337   ASSERT_EQ(kQuotaRequestAmount1, cb1.result());
338   ASSERT_FALSE(cb2.called());
339
340   // Another request message should have been sent.
341   ASSERT_TRUE(sink().GetFirstResourceCallMatching(
342       PpapiHostMsg_FileSystem_ReserveQuota::ID, &params, &msg));
343   sink().ClearMessages();
344   {
345     ProxyAutoUnlock unlock_to_prevent_deadlock;
346     // Reply with quota reservation amount sufficient to cover the second
347     // request and some extra.
348     SendReply(params,
349               PP_OK,
350               PpapiPluginMsg_FileSystem_ReserveQuotaReply(
351                   kQuotaRequestAmount1 + kQuotaRequestAmount2,
352                   FileGrowthMapToFileSizeMapForTesting(file_growths)));
353   }
354
355   ASSERT_TRUE(cb2.called());
356   ASSERT_EQ(kQuotaRequestAmount2, cb2.result());
357   cb1.Reset();
358   cb2.Reset();
359
360   // There is kQuotaRequestAmount1 of quota left, and a request for it should
361   // succeed immediately.
362   result = file_system_api->RequestQuota(
363       kQuotaRequestAmount1,
364       base::Bind(&MockRequestQuotaCallback::Callback, base::Unretained(&cb1)));
365   ASSERT_EQ(kQuotaRequestAmount1, result);
366 }
367
368 }  // namespace proxy
369 }  // namespace ppapi