Home | History | Annotate | Download | only in tests
      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 
    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(),
    519                                   &file_io1,
    520                                   0,
    521                                   &new_file_contents1,
    522                                   callback_type()));
    523   ASSERT_EQ(expected_file_contents1, new_file_contents1);
    524 
    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(),
    529                                   &file_io2,
    530                                   0,
    531                                   &new_file_contents2,
    532                                   callback_type()));
    533   ASSERT_EQ(expected_file_contents2, new_file_contents2);
    534 
    535   // TODO(dmichael): Test non-zero offset
    536 
    537   PASS();
    538 }
    539 
    540