Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / ppapi / tests / test_file_mapping.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 "ppapi/tests/test_file_mapping.h"
6
7 #include <string.h>
8
9 #include <limits>
10 #include <string>
11
12 #include "ppapi/c/pp_errors.h"
13 #include "ppapi/c/ppb_file_io.h"
14 #include "ppapi/c/ppb_file_mapping.h"
15 #include "ppapi/cpp/file_io.h"
16 #include "ppapi/cpp/file_ref.h"
17 #include "ppapi/cpp/file_system.h"
18 #include "ppapi/cpp/instance.h"
19 #include "ppapi/cpp/module.h"
20 #include "ppapi/tests/test_utils.h"
21
22 REGISTER_TEST_CASE(FileMapping);
23
24 namespace {
25
26 // TODO(dmichael): Move these to test_utils so we can share them?
27 int32_t ReadEntireFile(PP_Instance instance,
28                        pp::FileIO* file_io,
29                        int32_t offset,
30                        std::string* data,
31                        CallbackType callback_type) {
32   TestCompletionCallback callback(instance, callback_type);
33   char buf[256];
34   int32_t read_offset = offset;
35
36   for (;;) {
37     callback.WaitForResult(
38         file_io->Read(read_offset, buf, sizeof(buf), callback.GetCallback()));
39     if (callback.result() < 0)
40       return callback.result();
41     if (callback.result() == 0)
42       break;
43     read_offset += callback.result();
44     data->append(buf, callback.result());
45   }
46
47   return PP_OK;
48 }
49
50 int32_t WriteEntireBuffer(PP_Instance instance,
51                           pp::FileIO* file_io,
52                           int32_t offset,
53                           const std::string& data,
54                           CallbackType callback_type) {
55   TestCompletionCallback callback(instance, callback_type);
56   int32_t write_offset = offset;
57   const char* buf = data.c_str();
58   int32_t size = data.size();
59
60   while (write_offset < offset + size) {
61     callback.WaitForResult(file_io->Write(write_offset,
62                                           &buf[write_offset - offset],
63                                           size - write_offset + offset,
64                                           callback.GetCallback()));
65     if (callback.result() < 0)
66       return callback.result();
67     if (callback.result() == 0)
68       return PP_ERROR_FAILED;
69     write_offset += callback.result();
70   }
71   callback.WaitForResult(file_io->Flush(callback.GetCallback()));
72   return callback.result();
73 }
74
75 }  // namespace
76
77 std::string TestFileMapping::MapAndCheckResults(uint32_t prot,
78                                                 uint32_t flags) {
79   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
80
81   pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
82   pp::FileRef file_ref(file_system, "/mapped_file");
83
84   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
85   ASSERT_EQ(PP_OK, callback.result());
86
87   const int64_t page_size =
88       file_mapping_if_->GetMapPageSize(instance_->pp_instance());
89   const int64_t kNumPages = 4;
90   // Make a string that's big enough that it spans all of the first |n-1| pages,
91   // plus a little bit of the |nth| page.
92   std::string file_contents((page_size * (kNumPages - 1)) + 128, 'a');
93
94   pp::FileIO file_io(instance_);
95   callback.WaitForResult(file_io.Open(file_ref,
96                                       PP_FILEOPENFLAG_CREATE |
97                                       PP_FILEOPENFLAG_TRUNCATE |
98                                       PP_FILEOPENFLAG_READ |
99                                       PP_FILEOPENFLAG_WRITE,
100                                       callback.GetCallback()));
101   ASSERT_EQ(PP_OK, callback.result());
102   ASSERT_EQ(PP_OK, WriteEntireBuffer(instance_->pp_instance(),
103                                      &file_io,
104                                      0,
105                                      file_contents,
106                                      callback_type()));
107
108   // TODO(dmichael): Use C++ interface.
109   void* address = NULL;
110   callback.WaitForResult(
111       file_mapping_if_->Map(
112           instance_->pp_instance(),
113           file_io.pp_resource(),
114           kNumPages * page_size,
115           prot,
116           flags,
117           0,
118           &address,
119           callback.GetCallback().pp_completion_callback()));
120   CHECK_CALLBACK_BEHAVIOR(callback);
121   ASSERT_EQ(PP_OK, callback.result());
122   ASSERT_NE(NULL, address);
123
124   if (prot & PP_FILEMAPPROTECTION_READ) {
125     // Make sure we can read.
126     std::string mapped_data(static_cast<char*>(address), file_contents.size());
127     // The initial data should match.
128     ASSERT_EQ(file_contents, mapped_data);
129
130     // Now write some data and flush it.
131     const std::string file_contents2(file_contents.size(), 'x');
132     ASSERT_EQ(PP_OK, WriteEntireBuffer(instance_->pp_instance(),
133                                        &file_io,
134                                        0,
135                                        file_contents2,
136                                        callback_type()));
137     // If the region was mapped SHARED, it should get updated.
138     std::string mapped_data2(static_cast<char*>(address), file_contents.size());
139     if (flags & PP_FILEMAPFLAG_SHARED)
140       ASSERT_EQ(file_contents2, mapped_data2);
141     // In POSIX, it is unspecified in the PRIVATE case whether changes to the
142     // file are visible to the mapped region. So we can't really test anything
143     // here in that case.
144     // TODO(dmichael): Make sure our Pepper documentation reflects this.
145   }
146   if (prot & PP_FILEMAPPROTECTION_WRITE) {
147     std::string old_file_contents;
148     ASSERT_EQ(PP_OK, ReadEntireFile(instance_->pp_instance(),
149                                     &file_io,
150                                     0,
151                                     &old_file_contents,
152                                     callback_type()));
153     // Write something else to the mapped region, then unmap, and see if it
154     // gets written to the file. (Note we have to Unmap to make sure that the
155     // write is committed).
156     memset(address, 'y', file_contents.size());
157     // Note, we might not have read access to the mapped region here, so we
158     // make a string with the same contents without actually reading.
159     std::string mapped_data3(file_contents.size(), 'y');
160     callback.WaitForResult(
161         file_mapping_if_->Unmap(
162             instance_->pp_instance(), address, file_contents.size(),
163             callback.GetCallback().pp_completion_callback()));
164     CHECK_CALLBACK_BEHAVIOR(callback);
165     ASSERT_EQ(PP_OK, callback.result());
166     std::string new_file_contents;
167     ASSERT_EQ(PP_OK, ReadEntireFile(instance_->pp_instance(),
168                                     &file_io,
169                                     0,
170                                     &new_file_contents,
171                                     callback_type()));
172
173     // Sanity-check that the data we wrote isn't the same as what was already
174     // there, otherwise our test is invalid.
175     ASSERT_NE(mapped_data3, old_file_contents);
176     // If it's SHARED, the file should match what we wrote to the mapped region.
177     // Otherwise, it should not have changed.
178     if (flags & PP_FILEMAPFLAG_SHARED)
179       ASSERT_EQ(mapped_data3, new_file_contents);
180     else
181       ASSERT_EQ(old_file_contents, new_file_contents);
182   } else {
183     // We didn't do the "WRITE" test, but we still want to Unmap.
184     callback.WaitForResult(
185         file_mapping_if_->Unmap(
186             instance_->pp_instance(), address, file_contents.size(),
187             callback.GetCallback().pp_completion_callback()));
188     CHECK_CALLBACK_BEHAVIOR(callback);
189     ASSERT_EQ(PP_OK, callback.result());
190   }
191   PASS();
192 }
193
194 bool TestFileMapping::Init() {
195   // TODO(dmichael): Use unversioned string when this goes to stable?
196   file_mapping_if_ = static_cast<const PPB_FileMapping_0_1*>(
197       pp::Module::Get()->GetBrowserInterface(PPB_FILEMAPPING_INTERFACE_0_1));
198   return !!file_mapping_if_ && CheckTestingInterface() &&
199          EnsureRunningOverHTTP();
200 }
201
202 void TestFileMapping::RunTests(const std::string& filter) {
203   RUN_CALLBACK_TEST(TestFileMapping, BadParameters, filter);
204   RUN_CALLBACK_TEST(TestFileMapping, Map, filter);
205   RUN_CALLBACK_TEST(TestFileMapping, PartialRegions, filter);
206 }
207
208 std::string TestFileMapping::TestBadParameters() {
209   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
210
211   pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
212   pp::FileRef file_ref(file_system, "/mapped_file");
213
214   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
215   ASSERT_EQ(PP_OK, callback.result());
216
217   const int64_t page_size =
218       file_mapping_if_->GetMapPageSize(instance_->pp_instance());
219   // const int64_t kNumPages = 4;
220   // Make a string that's big enough that it spans 3 pages, plus a little extra.
221   //std::string file_contents((page_size * (kNumPages - 1)) + 128, 'a');
222   std::string file_contents(page_size, 'a');
223
224   pp::FileIO file_io(instance_);
225   callback.WaitForResult(file_io.Open(file_ref,
226                                       PP_FILEOPENFLAG_CREATE |
227                                       PP_FILEOPENFLAG_TRUNCATE |
228                                       PP_FILEOPENFLAG_READ |
229                                       PP_FILEOPENFLAG_WRITE,
230                                       callback.GetCallback()));
231   ASSERT_EQ(PP_OK, callback.result());
232   ASSERT_EQ(PP_OK, WriteEntireBuffer(instance_->pp_instance(),
233                                      &file_io,
234                                      0,
235                                      file_contents,
236                                      callback_type()));
237
238   // Bad instance.
239   void* address = NULL;
240   callback.WaitForResult(
241       file_mapping_if_->Map(
242           PP_Instance(0xbadbad),
243           file_io.pp_resource(),
244           page_size,
245           PP_FILEMAPPROTECTION_READ,
246           PP_FILEMAPFLAG_PRIVATE,
247           0,
248           &address,
249           callback.GetCallback().pp_completion_callback()));
250   CHECK_CALLBACK_BEHAVIOR(callback);
251   ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());
252   ASSERT_EQ(NULL, address);
253
254   // Bad resource.
255   callback.WaitForResult(
256       file_mapping_if_->Map(
257           instance_->pp_instance(),
258           PP_Resource(0xbadbad),
259           page_size,
260           PP_FILEMAPPROTECTION_READ,
261           PP_FILEMAPFLAG_PRIVATE,
262           0,
263           &address,
264           callback.GetCallback().pp_completion_callback()));
265   CHECK_CALLBACK_BEHAVIOR(callback);
266   ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());
267   ASSERT_EQ(NULL, address);
268
269   // Length too big.
270   callback.WaitForResult(
271       file_mapping_if_->Map(
272           instance_->pp_instance(),
273           file_io.pp_resource(),
274           std::numeric_limits<int64_t>::max(),
275           PP_FILEMAPPROTECTION_READ,
276           PP_FILEMAPFLAG_PRIVATE,
277           0,
278           &address,
279           callback.GetCallback().pp_completion_callback()));
280   CHECK_CALLBACK_BEHAVIOR(callback);
281   ASSERT_EQ(PP_ERROR_NOMEMORY, callback.result());
282   ASSERT_EQ(NULL, address);
283
284   // Length too small.
285   callback.WaitForResult(
286       file_mapping_if_->Map(
287           instance_->pp_instance(),
288           file_io.pp_resource(),
289           -1,
290           PP_FILEMAPPROTECTION_READ,
291           PP_FILEMAPFLAG_PRIVATE,
292           0,
293           &address,
294           callback.GetCallback().pp_completion_callback()));
295   CHECK_CALLBACK_BEHAVIOR(callback);
296   ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());
297   ASSERT_EQ(NULL, address);
298   // TODO(dmichael): Check & test length that is not a multiple of page size???
299
300   // Bad protection.
301   callback.WaitForResult(
302       file_mapping_if_->Map(
303           instance_->pp_instance(),
304           file_io.pp_resource(),
305           page_size,
306           ~PP_FILEMAPPROTECTION_READ,
307           PP_FILEMAPFLAG_PRIVATE,
308           0,
309           &address,
310           callback.GetCallback().pp_completion_callback()));
311   CHECK_CALLBACK_BEHAVIOR(callback);
312   ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());
313   ASSERT_EQ(NULL, address);
314
315   // No flags.
316   callback.WaitForResult(
317       file_mapping_if_->Map(
318           instance_->pp_instance(),
319           file_io.pp_resource(),
320           page_size,
321           PP_FILEMAPPROTECTION_READ,
322           0,
323           0,
324           &address,
325           callback.GetCallback().pp_completion_callback()));
326   CHECK_CALLBACK_BEHAVIOR(callback);
327   ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());
328   ASSERT_EQ(NULL, address);
329
330   // Both flags.
331   callback.WaitForResult(
332       file_mapping_if_->Map(
333           instance_->pp_instance(),
334           file_io.pp_resource(),
335           page_size,
336           PP_FILEMAPPROTECTION_READ,
337           PP_FILEMAPFLAG_SHARED | PP_FILEMAPFLAG_PRIVATE,
338           0,
339           &address,
340           callback.GetCallback().pp_completion_callback()));
341   CHECK_CALLBACK_BEHAVIOR(callback);
342   ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());
343   ASSERT_EQ(NULL, address);
344
345   // Bad flags.
346   callback.WaitForResult(
347       file_mapping_if_->Map(
348           instance_->pp_instance(),
349           file_io.pp_resource(),
350           page_size,
351           PP_FILEMAPPROTECTION_READ,
352           ~PP_FILEMAPFLAG_SHARED,
353           0,
354           &address,
355           callback.GetCallback().pp_completion_callback()));
356   CHECK_CALLBACK_BEHAVIOR(callback);
357   ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());
358   ASSERT_EQ(NULL, address);
359
360   // Bad offset; not a multiple of page size.
361   callback.WaitForResult(
362       file_mapping_if_->Map(
363           instance_->pp_instance(),
364           file_io.pp_resource(),
365           page_size,
366           PP_FILEMAPPROTECTION_READ,
367           ~PP_FILEMAPFLAG_SHARED,
368           1,
369           &address,
370           callback.GetCallback().pp_completion_callback()));
371   CHECK_CALLBACK_BEHAVIOR(callback);
372   ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());
373   ASSERT_EQ(NULL, address);
374
375   // Unmap NULL.
376   callback.WaitForResult(
377       file_mapping_if_->Unmap(
378           instance_->pp_instance(),
379           NULL,
380           page_size,
381           callback.GetCallback().pp_completion_callback()));
382   CHECK_CALLBACK_BEHAVIOR(callback);
383   ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());
384
385   // Unmap bad address.
386   callback.WaitForResult(
387       file_mapping_if_->Unmap(
388           instance_->pp_instance(),
389           reinterpret_cast<const void*>(0xdeadbeef),
390           page_size,
391           callback.GetCallback().pp_completion_callback()));
392   CHECK_CALLBACK_BEHAVIOR(callback);
393   ASSERT_EQ(PP_ERROR_BADARGUMENT, callback.result());
394
395   PASS();
396 }
397
398 std::string TestFileMapping::TestMap() {
399   ASSERT_SUBTEST_SUCCESS(MapAndCheckResults(PP_FILEMAPPROTECTION_READ,
400                                             PP_FILEMAPFLAG_SHARED));
401   ASSERT_SUBTEST_SUCCESS(MapAndCheckResults(PP_FILEMAPPROTECTION_WRITE,
402                                             PP_FILEMAPFLAG_SHARED));
403   ASSERT_SUBTEST_SUCCESS(
404       MapAndCheckResults(PP_FILEMAPPROTECTION_WRITE | PP_FILEMAPPROTECTION_READ,
405                          PP_FILEMAPFLAG_SHARED));
406   ASSERT_SUBTEST_SUCCESS(MapAndCheckResults(PP_FILEMAPPROTECTION_READ,
407                                             PP_FILEMAPFLAG_PRIVATE));
408   ASSERT_SUBTEST_SUCCESS(MapAndCheckResults(PP_FILEMAPPROTECTION_WRITE,
409                                             PP_FILEMAPFLAG_PRIVATE));
410   ASSERT_SUBTEST_SUCCESS(
411       MapAndCheckResults(PP_FILEMAPPROTECTION_WRITE | PP_FILEMAPPROTECTION_READ,
412                          PP_FILEMAPFLAG_PRIVATE));
413   PASS();
414 }
415
416 std::string TestFileMapping::TestPartialRegions() {
417   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
418
419   pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
420   pp::FileRef file_ref1(file_system, "/mapped_file1");
421   pp::FileRef file_ref2(file_system, "/mapped_file2");
422
423   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
424   ASSERT_EQ(PP_OK, callback.result());
425
426   const int64_t page_size =
427       file_mapping_if_->GetMapPageSize(instance_->pp_instance());
428   const int64_t kNumPages = 3;
429   std::string file_contents1(kNumPages * page_size, 'a');
430
431   pp::FileIO file_io1(instance_);
432   callback.WaitForResult(file_io1.Open(file_ref1,
433                                        PP_FILEOPENFLAG_CREATE |
434                                        PP_FILEOPENFLAG_TRUNCATE |
435                                        PP_FILEOPENFLAG_READ |
436                                        PP_FILEOPENFLAG_WRITE,
437                                        callback.GetCallback()));
438   ASSERT_EQ(PP_OK, callback.result());
439   ASSERT_EQ(PP_OK, WriteEntireBuffer(instance_->pp_instance(),
440                                      &file_io1,
441                                      0,
442                                      file_contents1,
443                                      callback_type()));
444
445   // TODO(dmichael): Use C++ interface.
446   void* address = NULL;
447   callback.WaitForResult(
448       file_mapping_if_->Map(
449           instance_->pp_instance(),
450           file_io1.pp_resource(),
451           kNumPages * page_size,
452           PP_FILEMAPPROTECTION_WRITE | PP_FILEMAPPROTECTION_READ,
453           PP_FILEMAPFLAG_SHARED,
454           0,
455           &address,
456           callback.GetCallback().pp_completion_callback()));
457   CHECK_CALLBACK_BEHAVIOR(callback);
458   ASSERT_EQ(PP_OK, callback.result());
459   ASSERT_NE(NULL, address);
460
461   // Unmap only the middle page.
462   void* address_of_middle_page =
463       reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(address) + page_size);
464   callback.WaitForResult(
465       file_mapping_if_->Unmap(
466           instance_->pp_instance(),
467           address_of_middle_page,
468           page_size,
469           callback.GetCallback().pp_completion_callback()));
470   CHECK_CALLBACK_BEHAVIOR(callback);
471   ASSERT_EQ(PP_OK, callback.result());
472
473   // Write another file, map it in to the middle hole that was left above.
474   pp::FileIO file_io2(instance_);
475   callback.WaitForResult(file_io2.Open(file_ref2,
476                                        PP_FILEOPENFLAG_CREATE |
477                                        PP_FILEOPENFLAG_TRUNCATE |
478                                        PP_FILEOPENFLAG_READ |
479                                        PP_FILEOPENFLAG_WRITE,
480                                        callback.GetCallback()));
481   ASSERT_EQ(PP_OK, callback.result());
482   // This second file will have 1 page worth of data.
483   std::string file_contents2(page_size, 'b');
484   ASSERT_EQ(PP_OK, WriteEntireBuffer(instance_->pp_instance(),
485                                      &file_io2,
486                                      0,
487                                      file_contents2,
488                                      callback_type()));
489   callback.WaitForResult(
490       file_mapping_if_->Map(
491           instance_->pp_instance(),
492           file_io2.pp_resource(),
493           page_size,
494           PP_FILEMAPPROTECTION_WRITE | PP_FILEMAPPROTECTION_READ,
495           PP_FILEMAPFLAG_SHARED | PP_FILEMAPFLAG_FIXED,
496           0,
497           &address_of_middle_page,
498           callback.GetCallback().pp_completion_callback()));
499   CHECK_CALLBACK_BEHAVIOR(callback);
500   ASSERT_EQ(PP_OK, callback.result());
501   PASS();
502
503   // Write something else to the mapped region, then unmap, and see if it
504   // gets written to both files. (Note we have to Unmap to make sure that the
505   // write is committed).
506   memset(address, 'c', kNumPages * page_size);
507   callback.WaitForResult(
508       file_mapping_if_->Unmap(
509           instance_->pp_instance(), address, kNumPages * page_size,
510           callback.GetCallback().pp_completion_callback()));
511   CHECK_CALLBACK_BEHAVIOR(callback);
512   ASSERT_EQ(PP_OK, callback.result());
513   // The first and third page should have been written with 'c', but the
514   // second page should be untouched.
515   std::string expected_file_contents1 = std::string(page_size, 'c') +
516                                         std::string(page_size, 'a') +
517                                         std::string(page_size, 'c');
518   std::string new_file_contents1;
519   ASSERT_EQ(PP_OK, ReadEntireFile(instance_->pp_instance(),
520                                   &file_io1,
521                                   0,
522                                   &new_file_contents1,
523                                   callback_type()));
524   ASSERT_EQ(expected_file_contents1, new_file_contents1);
525
526   // The second file should have been entirely over-written.
527   std::string expected_file_contents2 = std::string(page_size, 'c');
528   std::string new_file_contents2;
529   ASSERT_EQ(PP_OK, ReadEntireFile(instance_->pp_instance(),
530                                   &file_io2,
531                                   0,
532                                   &new_file_contents2,
533                                   callback_type()));
534   ASSERT_EQ(expected_file_contents2, new_file_contents2);
535
536   // TODO(dmichael): Test non-zero offset
537
538   PASS();
539 }
540