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.
5 #include "ppapi/tests/test_file_mapping.h"
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"
22 REGISTER_TEST_CASE(FileMapping
);
26 // TODO(dmichael): Move these to test_utils so we can share them?
27 int32_t ReadEntireFile(PP_Instance instance
,
31 CallbackType callback_type
) {
32 TestCompletionCallback
callback(instance
, callback_type
);
34 int32_t read_offset
= offset
;
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)
43 read_offset
+= callback
.result();
44 data
->append(buf
, callback
.result());
50 int32_t WriteEntireBuffer(PP_Instance instance
,
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
= static_cast<int32_t>(data
.size());
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();
71 callback
.WaitForResult(file_io
->Flush(callback
.GetCallback()));
72 return callback
.result();
77 std::string
TestFileMapping::MapAndCheckResults(uint32_t prot
,
79 TestCompletionCallback
callback(instance_
->pp_instance(), callback_type());
81 pp::FileSystem
file_system(instance_
, PP_FILESYSTEMTYPE_LOCALTEMPORARY
);
82 pp::FileRef
file_ref(file_system
, "/mapped_file");
84 callback
.WaitForResult(file_system
.Open(1024, callback
.GetCallback()));
85 ASSERT_EQ(PP_OK
, callback
.result());
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');
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(),
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
,
119 callback
.GetCallback().pp_completion_callback()));
120 CHECK_CALLBACK_BEHAVIOR(callback
);
121 ASSERT_EQ(PP_OK
, callback
.result());
122 ASSERT_NE(NULL
, address
);
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
);
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(),
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.
146 if (prot
& PP_FILEMAPPROTECTION_WRITE
) {
147 std::string old_file_contents
;
148 ASSERT_EQ(PP_OK
, ReadEntireFile(instance_
->pp_instance(),
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(),
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
);
181 ASSERT_EQ(old_file_contents
, new_file_contents
);
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());
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();
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
);
208 std::string
TestFileMapping::TestBadParameters() {
209 TestCompletionCallback
callback(instance_
->pp_instance(), callback_type());
211 pp::FileSystem
file_system(instance_
, PP_FILESYSTEMTYPE_LOCALTEMPORARY
);
212 pp::FileRef
file_ref(file_system
, "/mapped_file");
214 callback
.WaitForResult(file_system
.Open(1024, callback
.GetCallback()));
215 ASSERT_EQ(PP_OK
, callback
.result());
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');
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(),
239 void* address
= NULL
;
240 callback
.WaitForResult(
241 file_mapping_if_
->Map(
242 PP_Instance(0xbadbad),
243 file_io
.pp_resource(),
245 PP_FILEMAPPROTECTION_READ
,
246 PP_FILEMAPFLAG_PRIVATE
,
249 callback
.GetCallback().pp_completion_callback()));
250 CHECK_CALLBACK_BEHAVIOR(callback
);
251 ASSERT_EQ(PP_ERROR_BADARGUMENT
, callback
.result());
252 ASSERT_EQ(NULL
, address
);
255 callback
.WaitForResult(
256 file_mapping_if_
->Map(
257 instance_
->pp_instance(),
258 PP_Resource(0xbadbad),
260 PP_FILEMAPPROTECTION_READ
,
261 PP_FILEMAPFLAG_PRIVATE
,
264 callback
.GetCallback().pp_completion_callback()));
265 CHECK_CALLBACK_BEHAVIOR(callback
);
266 ASSERT_EQ(PP_ERROR_BADARGUMENT
, callback
.result());
267 ASSERT_EQ(NULL
, address
);
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
,
279 callback
.GetCallback().pp_completion_callback()));
280 CHECK_CALLBACK_BEHAVIOR(callback
);
281 ASSERT_EQ(PP_ERROR_NOMEMORY
, callback
.result());
282 ASSERT_EQ(NULL
, address
);
285 callback
.WaitForResult(
286 file_mapping_if_
->Map(
287 instance_
->pp_instance(),
288 file_io
.pp_resource(),
290 PP_FILEMAPPROTECTION_READ
,
291 PP_FILEMAPFLAG_PRIVATE
,
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???
301 callback
.WaitForResult(
302 file_mapping_if_
->Map(
303 instance_
->pp_instance(),
304 file_io
.pp_resource(),
306 ~static_cast<uint32_t>(PP_FILEMAPPROTECTION_READ
),
307 PP_FILEMAPFLAG_PRIVATE
,
310 callback
.GetCallback().pp_completion_callback()));
311 CHECK_CALLBACK_BEHAVIOR(callback
);
312 ASSERT_EQ(PP_ERROR_BADARGUMENT
, callback
.result());
313 ASSERT_EQ(NULL
, address
);
316 callback
.WaitForResult(
317 file_mapping_if_
->Map(
318 instance_
->pp_instance(),
319 file_io
.pp_resource(),
321 PP_FILEMAPPROTECTION_READ
,
325 callback
.GetCallback().pp_completion_callback()));
326 CHECK_CALLBACK_BEHAVIOR(callback
);
327 ASSERT_EQ(PP_ERROR_BADARGUMENT
, callback
.result());
328 ASSERT_EQ(NULL
, address
);
331 callback
.WaitForResult(
332 file_mapping_if_
->Map(
333 instance_
->pp_instance(),
334 file_io
.pp_resource(),
336 PP_FILEMAPPROTECTION_READ
,
337 PP_FILEMAPFLAG_SHARED
| PP_FILEMAPFLAG_PRIVATE
,
340 callback
.GetCallback().pp_completion_callback()));
341 CHECK_CALLBACK_BEHAVIOR(callback
);
342 ASSERT_EQ(PP_ERROR_BADARGUMENT
, callback
.result());
343 ASSERT_EQ(NULL
, address
);
346 callback
.WaitForResult(
347 file_mapping_if_
->Map(
348 instance_
->pp_instance(),
349 file_io
.pp_resource(),
351 PP_FILEMAPPROTECTION_READ
,
352 ~static_cast<uint32_t>(PP_FILEMAPFLAG_SHARED
),
355 callback
.GetCallback().pp_completion_callback()));
356 CHECK_CALLBACK_BEHAVIOR(callback
);
357 ASSERT_EQ(PP_ERROR_BADARGUMENT
, callback
.result());
358 ASSERT_EQ(NULL
, address
);
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(),
366 PP_FILEMAPPROTECTION_READ
,
367 ~static_cast<uint32_t>(PP_FILEMAPFLAG_SHARED
),
370 callback
.GetCallback().pp_completion_callback()));
371 CHECK_CALLBACK_BEHAVIOR(callback
);
372 ASSERT_EQ(PP_ERROR_BADARGUMENT
, callback
.result());
373 ASSERT_EQ(NULL
, address
);
376 callback
.WaitForResult(
377 file_mapping_if_
->Unmap(
378 instance_
->pp_instance(),
381 callback
.GetCallback().pp_completion_callback()));
382 CHECK_CALLBACK_BEHAVIOR(callback
);
383 ASSERT_EQ(PP_ERROR_BADARGUMENT
, callback
.result());
385 // Unmap bad address.
386 callback
.WaitForResult(
387 file_mapping_if_
->Unmap(
388 instance_
->pp_instance(),
389 reinterpret_cast<const void*>(0xdeadbeef),
391 callback
.GetCallback().pp_completion_callback()));
392 CHECK_CALLBACK_BEHAVIOR(callback
);
393 ASSERT_EQ(PP_ERROR_BADARGUMENT
, callback
.result());
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
));
416 std::string
TestFileMapping::TestPartialRegions() {
417 TestCompletionCallback
callback(instance_
->pp_instance(), callback_type());
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");
423 callback
.WaitForResult(file_system
.Open(1024, callback
.GetCallback()));
424 ASSERT_EQ(PP_OK
, callback
.result());
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');
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(),
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
,
456 callback
.GetCallback().pp_completion_callback()));
457 CHECK_CALLBACK_BEHAVIOR(callback
);
458 ASSERT_EQ(PP_OK
, callback
.result());
459 ASSERT_NE(NULL
, address
);
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
,
469 callback
.GetCallback().pp_completion_callback()));
470 CHECK_CALLBACK_BEHAVIOR(callback
);
471 ASSERT_EQ(PP_OK
, callback
.result());
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(),
489 callback
.WaitForResult(
490 file_mapping_if_
->Map(
491 instance_
->pp_instance(),
492 file_io2
.pp_resource(),
494 PP_FILEMAPPROTECTION_WRITE
| PP_FILEMAPPROTECTION_READ
,
495 PP_FILEMAPFLAG_SHARED
| PP_FILEMAPFLAG_FIXED
,
497 &address_of_middle_page
,
498 callback
.GetCallback().pp_completion_callback()));
499 CHECK_CALLBACK_BEHAVIOR(callback
);
500 ASSERT_EQ(PP_OK
, callback
.result());
502 // Write something else to the mapped region, then unmap, and see if it
503 // gets written to both files. (Note we have to Unmap to make sure that the
504 // write is committed).
505 memset(address
, 'c', kNumPages
* page_size
);
506 callback
.WaitForResult(
507 file_mapping_if_
->Unmap(
508 instance_
->pp_instance(), address
, kNumPages
* page_size
,
509 callback
.GetCallback().pp_completion_callback()));
510 CHECK_CALLBACK_BEHAVIOR(callback
);
511 ASSERT_EQ(PP_OK
, callback
.result());
512 // The first and third page should have been written with 'c', but the
513 // second page should be untouched.
514 std::string expected_file_contents1
= std::string(page_size
, 'c') +
515 std::string(page_size
, 'a') +
516 std::string(page_size
, 'c');
517 std::string new_file_contents1
;
518 ASSERT_EQ(PP_OK
, ReadEntireFile(instance_
->pp_instance(),
523 ASSERT_EQ(expected_file_contents1
, new_file_contents1
);
525 // The second file should have been entirely over-written.
526 std::string expected_file_contents2
= std::string(page_size
, 'c');
527 std::string new_file_contents2
;
528 ASSERT_EQ(PP_OK
, ReadEntireFile(instance_
->pp_instance(),
533 ASSERT_EQ(expected_file_contents2
, new_file_contents2
);
535 // TODO(dmichael): Test non-zero offset