Home | History | Annotate | Download | only in tests
      1 // Copyright (c) 2011 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_ref.h"
      6 
      7 #include <stdio.h>
      8 
      9 #include <sstream>
     10 #include <vector>
     11 
     12 #include "ppapi/c/pp_errors.h"
     13 #include "ppapi/c/ppb_file_io.h"
     14 #include "ppapi/c/private/ppb_testing_private.h"
     15 #include "ppapi/cpp/directory_entry.h"
     16 #include "ppapi/cpp/file_io.h"
     17 #include "ppapi/cpp/file_ref.h"
     18 #include "ppapi/cpp/file_system.h"
     19 #include "ppapi/cpp/instance.h"
     20 #include "ppapi/cpp/module.h"
     21 #include "ppapi/cpp/url_loader.h"
     22 #include "ppapi/cpp/url_request_info.h"
     23 #include "ppapi/cpp/url_response_info.h"
     24 #include "ppapi/tests/test_utils.h"
     25 #include "ppapi/tests/testing_instance.h"
     26 
     27 REGISTER_TEST_CASE(FileRef);
     28 
     29 namespace {
     30 
     31 const char* kPersFileName = "persistent";
     32 const char* kTempFileName = "temporary";
     33 const char* kParentPath = "/foo/bar";
     34 const char* kPersFilePath = "/foo/bar/persistent";
     35 const char* kTempFilePath = "/foo/bar/temporary";
     36 const char* kTerribleName = "!@#$%^&*()-_=+{}[] ;:'\"|`~\t\n\r\b?";
     37 
     38 typedef std::vector<pp::DirectoryEntry> DirEntries;
     39 
     40 std::string ReportMismatch(const std::string& method_name,
     41                            const std::string& returned_result,
     42                            const std::string& expected_result) {
     43   return method_name + " returned '" + returned_result + "'; '" +
     44       expected_result + "' expected.";
     45 }
     46 
     47 }  // namespace
     48 
     49 bool TestFileRef::Init() {
     50   return CheckTestingInterface() && EnsureRunningOverHTTP();
     51 }
     52 
     53 std::string TestFileRef::MakeExternalFileRef(pp::FileRef* file_ref_ext) {
     54   pp::URLRequestInfo request(instance_);
     55   request.SetURL("test_url_loader_data/hello.txt");
     56   request.SetStreamToFile(true);
     57 
     58   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
     59 
     60   pp::URLLoader loader(instance_);
     61   callback.WaitForResult(loader.Open(request, callback.GetCallback()));
     62   CHECK_CALLBACK_BEHAVIOR(callback);
     63   ASSERT_EQ(PP_OK, callback.result());
     64 
     65   pp::URLResponseInfo response_info(loader.GetResponseInfo());
     66   ASSERT_FALSE(response_info.is_null());
     67   ASSERT_EQ(200, response_info.GetStatusCode());
     68 
     69   *file_ref_ext = pp::FileRef(response_info.GetBodyAsFileRef());
     70   ASSERT_EQ(PP_FILESYSTEMTYPE_EXTERNAL, file_ref_ext->GetFileSystemType());
     71   PASS();
     72 }
     73 
     74 int32_t TestFileRef::DeleteDirectoryRecursively(pp::FileRef* dir) {
     75   if (!dir)
     76     return PP_ERROR_BADARGUMENT;
     77 
     78   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
     79   TestCompletionCallbackWithOutput<DirEntries> output_callback(
     80       instance_->pp_instance(), callback_type());
     81 
     82   output_callback.WaitForResult(
     83       dir->ReadDirectoryEntries(output_callback.GetCallback()));
     84   int32_t rv = output_callback.result();
     85   if (rv != PP_OK && rv != PP_ERROR_FILENOTFOUND)
     86     return rv;
     87 
     88   DirEntries entries = output_callback.output();
     89   for (DirEntries::const_iterator it = entries.begin();
     90        it != entries.end();
     91        ++it) {
     92     pp::FileRef file_ref = it->file_ref();
     93     if (it->file_type() == PP_FILETYPE_DIRECTORY) {
     94       rv = DeleteDirectoryRecursively(&file_ref);
     95       if (rv != PP_OK && rv != PP_ERROR_FILENOTFOUND)
     96         return rv;
     97     } else {
     98       callback.WaitForResult(file_ref.Delete(callback.GetCallback()));
     99       rv = callback.result();
    100       if (rv != PP_OK && rv != PP_ERROR_FILENOTFOUND)
    101         return rv;
    102     }
    103   }
    104   callback.WaitForResult(dir->Delete(callback.GetCallback()));
    105   return callback.result();
    106 }
    107 
    108 void TestFileRef::RunTests(const std::string& filter) {
    109   RUN_CALLBACK_TEST(TestFileRef, Create, filter);
    110   RUN_CALLBACK_TEST(TestFileRef, GetFileSystemType, filter);
    111   RUN_CALLBACK_TEST(TestFileRef, GetName, filter);
    112   RUN_CALLBACK_TEST(TestFileRef, GetPath, filter);
    113   RUN_CALLBACK_TEST(TestFileRef, GetParent, filter);
    114   RUN_CALLBACK_TEST(TestFileRef, MakeDirectory, filter);
    115   RUN_CALLBACK_TEST(TestFileRef, QueryAndTouchFile, filter);
    116   RUN_CALLBACK_TEST(TestFileRef, DeleteFileAndDirectory, filter);
    117   RUN_CALLBACK_TEST(TestFileRef, RenameFileAndDirectory, filter);
    118   RUN_CALLBACK_TEST(TestFileRef, Query, filter);
    119   RUN_CALLBACK_TEST(TestFileRef, FileNameEscaping, filter);
    120   RUN_CALLBACK_TEST(TestFileRef, ReadDirectoryEntries, filter);
    121 }
    122 
    123 std::string TestFileRef::TestCreate() {
    124   std::vector<std::string> invalid_paths;
    125   invalid_paths.push_back("invalid_path");  // no '/' at the first character
    126   invalid_paths.push_back(std::string());   // empty path
    127   // The following are directory traversal checks
    128   invalid_paths.push_back("..");
    129   invalid_paths.push_back("/../invalid_path");
    130   invalid_paths.push_back("/../../invalid_path");
    131   invalid_paths.push_back("/invalid/../../path");
    132   const size_t num_invalid_paths = invalid_paths.size();
    133 
    134   pp::FileSystem file_system_pers(
    135       instance_, PP_FILESYSTEMTYPE_LOCALPERSISTENT);
    136   pp::FileSystem file_system_temp(
    137       instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    138   for (size_t j = 0; j < num_invalid_paths; ++j) {
    139     pp::FileRef file_ref_pers(file_system_pers, invalid_paths[j].c_str());
    140     if (file_ref_pers.pp_resource() != 0) {
    141       return "file_ref_pers expected to be invalid for path: " +
    142           invalid_paths[j];
    143     }
    144     pp::FileRef file_ref_temp(file_system_temp, invalid_paths[j].c_str());
    145     if (file_ref_temp.pp_resource() != 0) {
    146       return "file_ref_temp expected to be invalid for path: " +
    147           invalid_paths[j];
    148     }
    149   }
    150   PASS();
    151 }
    152 
    153 std::string TestFileRef::TestGetFileSystemType() {
    154   pp::FileSystem file_system_pers(
    155       instance_, PP_FILESYSTEMTYPE_LOCALPERSISTENT);
    156   pp::FileSystem file_system_temp(
    157       instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    158 
    159   pp::FileRef file_ref_pers(file_system_pers, kPersFilePath);
    160   if (file_ref_pers.GetFileSystemType() != PP_FILESYSTEMTYPE_LOCALPERSISTENT)
    161     return "file_ref_pers expected to be persistent.";
    162 
    163   pp::FileRef file_ref_temp(file_system_temp, kTempFilePath);
    164   if (file_ref_temp.GetFileSystemType() != PP_FILESYSTEMTYPE_LOCALTEMPORARY)
    165     return "file_ref_temp expected to be temporary.";
    166 
    167   pp::FileRef file_ref_ext;
    168   std::string result = MakeExternalFileRef(&file_ref_ext);
    169   if (!result.empty())
    170     return result;
    171   PASS();
    172 }
    173 
    174 std::string TestFileRef::TestGetName() {
    175   pp::FileSystem file_system_pers(
    176       instance_, PP_FILESYSTEMTYPE_LOCALPERSISTENT);
    177   pp::FileSystem file_system_temp(
    178       instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    179 
    180   pp::FileRef file_ref_pers(file_system_pers, kPersFilePath);
    181   std::string name = file_ref_pers.GetName().AsString();
    182   if (name != kPersFileName)
    183     return ReportMismatch("FileRef::GetName", name, kPersFileName);
    184 
    185   pp::FileRef file_ref_temp(file_system_temp, kTempFilePath);
    186   name = file_ref_temp.GetName().AsString();
    187   if (name != kTempFileName)
    188     return ReportMismatch("FileRef::GetName", name, kTempFileName);
    189 
    190   // Test the "/" case.
    191   pp::FileRef file_ref_slash(file_system_temp, "/");
    192   name = file_ref_slash.GetName().AsString();
    193   if (name != "/")
    194     return ReportMismatch("FileRef::GetName", name, "/");
    195 
    196   pp::URLRequestInfo request(instance_);
    197   request.SetURL("test_url_loader_data/hello.txt");
    198   request.SetStreamToFile(true);
    199 
    200   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    201 
    202   pp::URLLoader loader(instance_);
    203   callback.WaitForResult(loader.Open(request, callback.GetCallback()));
    204   CHECK_CALLBACK_BEHAVIOR(callback);
    205   ASSERT_EQ(PP_OK, callback.result());
    206 
    207   pp::URLResponseInfo response_info(loader.GetResponseInfo());
    208   ASSERT_FALSE(response_info.is_null());
    209   ASSERT_EQ(200, response_info.GetStatusCode());
    210 
    211   pp::FileRef file_ref_ext(response_info.GetBodyAsFileRef());
    212   name = file_ref_ext.GetName().AsString();
    213   ASSERT_FALSE(name.empty());
    214 
    215   PASS();
    216 }
    217 
    218 std::string TestFileRef::TestGetPath() {
    219   pp::FileSystem file_system_pers(
    220       instance_, PP_FILESYSTEMTYPE_LOCALPERSISTENT);
    221   pp::FileSystem file_system_temp(
    222       instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    223 
    224   pp::FileRef file_ref_pers(file_system_pers, kPersFilePath);
    225   ASSERT_EQ(kPersFilePath, file_ref_pers.GetPath().AsString());
    226 
    227   pp::FileRef file_ref_temp(file_system_temp, kTempFilePath);
    228   ASSERT_EQ(kTempFilePath, file_ref_temp.GetPath().AsString());
    229 
    230   pp::URLRequestInfo request(instance_);
    231   request.SetURL("test_url_loader_data/hello.txt");
    232   request.SetStreamToFile(true);
    233 
    234   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    235 
    236   pp::URLLoader loader(instance_);
    237   callback.WaitForResult(loader.Open(request, callback.GetCallback()));
    238   CHECK_CALLBACK_BEHAVIOR(callback);
    239   ASSERT_EQ(PP_OK, callback.result());
    240 
    241   pp::URLResponseInfo response_info(loader.GetResponseInfo());
    242   ASSERT_FALSE(response_info.is_null());
    243   ASSERT_EQ(200, response_info.GetStatusCode());
    244 
    245   pp::FileRef file_ref_ext(response_info.GetBodyAsFileRef());
    246   ASSERT_TRUE(file_ref_ext.GetPath().is_undefined());
    247 
    248   PASS();
    249 }
    250 
    251 std::string TestFileRef::TestGetParent() {
    252   pp::FileSystem file_system_pers(
    253       instance_, PP_FILESYSTEMTYPE_LOCALPERSISTENT);
    254   pp::FileSystem file_system_temp(
    255       instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    256 
    257   pp::FileRef file_ref_pers(file_system_pers, kPersFilePath);
    258   ASSERT_EQ(kParentPath, file_ref_pers.GetParent().GetPath().AsString());
    259 
    260   pp::FileRef file_ref_temp(file_system_temp, kTempFilePath);
    261   ASSERT_EQ(kParentPath, file_ref_temp.GetParent().GetPath().AsString());
    262 
    263   // Test the "/" case.
    264   pp::FileRef file_ref_slash(file_system_temp, "/");
    265   ASSERT_EQ("/", file_ref_slash.GetParent().GetPath().AsString());
    266 
    267   // Test the "/foo" case (the parent is "/").
    268   pp::FileRef file_ref_with_root_parent(file_system_temp, "/foo");
    269   ASSERT_EQ("/", file_ref_with_root_parent.GetParent().GetPath().AsString());
    270 
    271   pp::URLRequestInfo request(instance_);
    272   request.SetURL("test_url_loader_data/hello.txt");
    273   request.SetStreamToFile(true);
    274 
    275   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    276 
    277   pp::URLLoader loader(instance_);
    278   callback.WaitForResult(loader.Open(request, callback.GetCallback()));
    279   CHECK_CALLBACK_BEHAVIOR(callback);
    280   ASSERT_EQ(PP_OK, callback.result());
    281 
    282   pp::URLResponseInfo response_info(loader.GetResponseInfo());
    283   ASSERT_FALSE(response_info.is_null());
    284   ASSERT_EQ(200, response_info.GetStatusCode());
    285 
    286   pp::FileRef file_ref_ext(response_info.GetBodyAsFileRef());
    287   ASSERT_TRUE(file_ref_ext.GetParent().is_null());
    288 
    289   PASS();
    290 }
    291 
    292 std::string TestFileRef::TestMakeDirectory() {
    293   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    294 
    295   // Open.
    296   pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    297   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
    298   CHECK_CALLBACK_BEHAVIOR(callback);
    299   ASSERT_EQ(PP_OK, callback.result());
    300 
    301   // MakeDirectory.
    302   pp::FileRef dir_ref(file_system, "/test_dir_make_directory");
    303   callback.WaitForResult(dir_ref.MakeDirectory(callback.GetCallback()));
    304   CHECK_CALLBACK_BEHAVIOR(callback);
    305   ASSERT_EQ(PP_OK, callback.result());
    306 
    307   // MakeDirectory aborted.
    308   int32_t rv = PP_ERROR_FAILED;
    309   {
    310     rv = pp::FileRef(file_system, "/test_dir_make_abort")
    311         .MakeDirectory(callback.GetCallback());
    312   }
    313   callback.WaitForAbortResult(rv);
    314   CHECK_CALLBACK_BEHAVIOR(callback);
    315 
    316   // MakeDirectoryIncludingAncestors.
    317   dir_ref = pp::FileRef(file_system, "/dir_make_dir_1/dir_make_dir_2");
    318   callback.WaitForResult(
    319       dir_ref.MakeDirectoryIncludingAncestors(callback.GetCallback()));
    320   CHECK_CALLBACK_BEHAVIOR(callback);
    321   ASSERT_EQ(PP_OK, callback.result());
    322 
    323   // MakeDirectoryIncludingAncestors aborted.
    324   {
    325     rv = pp::FileRef(file_system, "/dir_make_abort_1/dir_make_abort_2")
    326         .MakeDirectoryIncludingAncestors(callback.GetCallback());
    327   }
    328   callback.WaitForAbortResult(rv);
    329   CHECK_CALLBACK_BEHAVIOR(callback);
    330 
    331   // MakeDirectory with nested path should fail.
    332   dir_ref = pp::FileRef(file_system, "/dir_make_dir_3/dir_make_dir_4");
    333   callback.WaitForResult(dir_ref.MakeDirectory(callback.GetCallback()));
    334   CHECK_CALLBACK_BEHAVIOR(callback);
    335   ASSERT_NE(PP_OK, callback.result());
    336 
    337   PASS();
    338 }
    339 
    340 std::string TestFileRef::TestQueryAndTouchFile() {
    341   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    342   pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    343   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
    344   CHECK_CALLBACK_BEHAVIOR(callback);
    345   ASSERT_EQ(PP_OK, callback.result());
    346 
    347   pp::FileRef file_ref(file_system, "/file_touch");
    348   pp::FileIO file_io(instance_);
    349   callback.WaitForResult(
    350       file_io.Open(file_ref,
    351                    PP_FILEOPENFLAG_CREATE |
    352                    PP_FILEOPENFLAG_TRUNCATE |
    353                    PP_FILEOPENFLAG_WRITE,
    354                    callback.GetCallback()));
    355   CHECK_CALLBACK_BEHAVIOR(callback);
    356   ASSERT_EQ(PP_OK, callback.result());
    357 
    358   // Write some data to have a non-zero file size.
    359   callback.WaitForResult(file_io.Write(0, "test", 4, callback.GetCallback()));
    360   CHECK_CALLBACK_BEHAVIOR(callback);
    361   ASSERT_EQ(4, callback.result());
    362 
    363   // Touch.
    364   const PP_Time last_access_time = 123 * 24 * 3600.0;
    365   // last_modified_time's granularity is 2 seconds
    366   // See note in test_file_io.cc for why we use this time.
    367   const PP_Time last_modified_time = 100 * 24 * 3600.0;
    368   callback.WaitForResult(file_ref.Touch(last_access_time, last_modified_time,
    369                                         callback.GetCallback()));
    370   CHECK_CALLBACK_BEHAVIOR(callback);
    371   ASSERT_EQ(PP_OK, callback.result());
    372 
    373   // Touch aborted.
    374   int32_t rv = PP_ERROR_FAILED;
    375   {
    376     rv = pp::FileRef(file_system, "/file_touch_abort")
    377         .Touch(last_access_time, last_modified_time, callback.GetCallback());
    378   }
    379   callback.WaitForResult(rv);
    380   CHECK_CALLBACK_BEHAVIOR(callback);
    381   if (rv == PP_OK_COMPLETIONPENDING) {
    382     // Touch tried to run asynchronously and should have been aborted.
    383     ASSERT_EQ(PP_ERROR_ABORTED, callback.result());
    384   } else {
    385     // Touch ran synchronously and should have failed because the file does not
    386     // exist.
    387     ASSERT_EQ(PP_ERROR_FILENOTFOUND, callback.result());
    388   }
    389 
    390   // Query.
    391   PP_FileInfo info;
    392   callback.WaitForResult(file_io.Query(&info, callback.GetCallback()));
    393   CHECK_CALLBACK_BEHAVIOR(callback);
    394   ASSERT_EQ(PP_OK, callback.result());
    395   ASSERT_EQ(4, info.size);
    396   ASSERT_EQ(PP_FILETYPE_REGULAR, info.type);
    397   ASSERT_EQ(PP_FILESYSTEMTYPE_LOCALTEMPORARY, info.system_type);
    398 
    399   // Disabled due to DST-related failure: crbug.com/314579
    400   // ASSERT_EQ(last_access_time, info.last_access_time);
    401   // ASSERT_EQ(last_modified_time, info.last_modified_time);
    402 
    403   // Cancellation test.
    404   // TODO(viettrungluu): this test causes a bunch of LOG(WARNING)s; investigate.
    405   // TODO(viettrungluu): check |info| for late writes.
    406   {
    407     rv = pp::FileRef(file_system, "/file_touch").Touch(
    408         last_access_time, last_modified_time, callback.GetCallback());
    409   }
    410   callback.WaitForAbortResult(rv);
    411   CHECK_CALLBACK_BEHAVIOR(callback);
    412 
    413   PASS();
    414 }
    415 
    416 std::string TestFileRef::TestDeleteFileAndDirectory() {
    417   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    418   pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    419   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
    420   CHECK_CALLBACK_BEHAVIOR(callback);
    421   ASSERT_EQ(PP_OK, callback.result());
    422 
    423   pp::FileRef file_ref(file_system, "/file_delete");
    424   pp::FileIO file_io(instance_);
    425   callback.WaitForResult(
    426       file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback()));
    427   CHECK_CALLBACK_BEHAVIOR(callback);
    428   ASSERT_EQ(PP_OK, callback.result());
    429 
    430   callback.WaitForResult(file_ref.Delete(callback.GetCallback()));
    431   CHECK_CALLBACK_BEHAVIOR(callback);
    432   ASSERT_EQ(PP_OK, callback.result());
    433 
    434   pp::FileRef dir_ref(file_system, "/dir_delete");
    435   callback.WaitForResult(dir_ref.MakeDirectory(callback.GetCallback()));
    436   CHECK_CALLBACK_BEHAVIOR(callback);
    437   ASSERT_EQ(PP_OK, callback.result());
    438 
    439   callback.WaitForResult(dir_ref.Delete(callback.GetCallback()));
    440   CHECK_CALLBACK_BEHAVIOR(callback);
    441   ASSERT_EQ(PP_OK, callback.result());
    442 
    443   pp::FileRef nested_dir_ref(file_system, "/dir_delete_1/dir_delete_2");
    444   callback.WaitForResult(
    445       nested_dir_ref.MakeDirectoryIncludingAncestors(callback.GetCallback()));
    446   CHECK_CALLBACK_BEHAVIOR(callback);
    447   ASSERT_EQ(PP_OK, callback.result());
    448 
    449   // Attempt to delete the parent directory (should fail; it's non-empty).
    450   pp::FileRef parent_dir_ref = nested_dir_ref.GetParent();
    451   callback.WaitForResult(parent_dir_ref.Delete(callback.GetCallback()));
    452   CHECK_CALLBACK_BEHAVIOR(callback);
    453   ASSERT_EQ(PP_ERROR_FAILED, callback.result());
    454 
    455   pp::FileRef nonexistent_file_ref(file_system, "/nonexistent_file_delete");
    456   callback.WaitForResult(nonexistent_file_ref.Delete(callback.GetCallback()));
    457   CHECK_CALLBACK_BEHAVIOR(callback);
    458   ASSERT_EQ(PP_ERROR_FILENOTFOUND, callback.result());
    459 
    460   // Delete aborted.
    461   int32_t rv = PP_ERROR_FAILED;
    462   {
    463     pp::FileRef file_ref_abort(file_system, "/file_delete_abort");
    464     pp::FileIO file_io_abort(instance_);
    465     callback.WaitForResult(
    466         file_io_abort.Open(file_ref_abort, PP_FILEOPENFLAG_CREATE,
    467                            callback.GetCallback()));
    468     CHECK_CALLBACK_BEHAVIOR(callback);
    469     ASSERT_EQ(PP_OK, callback.result());
    470     rv = file_ref_abort.Delete(callback.GetCallback());
    471   }
    472   callback.WaitForAbortResult(rv);
    473   CHECK_CALLBACK_BEHAVIOR(callback);
    474 
    475   PASS();
    476 }
    477 
    478 std::string TestFileRef::TestRenameFileAndDirectory() {
    479   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    480   pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    481   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
    482   CHECK_CALLBACK_BEHAVIOR(callback);
    483   ASSERT_EQ(PP_OK, callback.result());
    484 
    485   pp::FileRef file_ref(file_system, "/file_rename");
    486   pp::FileIO file_io(instance_);
    487   callback.WaitForResult(
    488       file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback()));
    489   CHECK_CALLBACK_BEHAVIOR(callback);
    490   ASSERT_EQ(PP_OK, callback.result());
    491 
    492   pp::FileRef target_file_ref(file_system, "/target_file_rename");
    493   callback.WaitForResult(
    494       file_ref.Rename(target_file_ref, callback.GetCallback()));
    495   CHECK_CALLBACK_BEHAVIOR(callback);
    496   ASSERT_EQ(PP_OK, callback.result());
    497 
    498   pp::FileRef dir_ref(file_system, "/dir_rename");
    499   callback.WaitForResult(dir_ref.MakeDirectory(callback.GetCallback()));
    500   CHECK_CALLBACK_BEHAVIOR(callback);
    501   ASSERT_EQ(PP_OK, callback.result());
    502 
    503   pp::FileRef target_dir_ref(file_system, "/target_dir_rename");
    504   callback.WaitForResult(
    505       dir_ref.Rename(target_dir_ref, callback.GetCallback()));
    506   CHECK_CALLBACK_BEHAVIOR(callback);
    507   ASSERT_EQ(PP_OK, callback.result());
    508 
    509   pp::FileRef nested_dir_ref(file_system, "/dir_rename_1/dir_rename_2");
    510   callback.WaitForResult(
    511       nested_dir_ref.MakeDirectoryIncludingAncestors(callback.GetCallback()));
    512   CHECK_CALLBACK_BEHAVIOR(callback);
    513   ASSERT_EQ(PP_OK, callback.result());
    514 
    515   // Try to rename nested directory to the parent name. Should fail.
    516   pp::FileRef target_nested_dir_ref(file_system, "/dir_rename_1");
    517   callback.WaitForResult(
    518       nested_dir_ref.Rename(target_nested_dir_ref, callback.GetCallback()));
    519   CHECK_CALLBACK_BEHAVIOR(callback);
    520   ASSERT_EQ(PP_ERROR_FAILED, callback.result());
    521 
    522   // Rename aborted.
    523   // TODO(viettrungluu): Figure out what we want to do if the target file
    524   // resource is destroyed before completion.
    525   int32_t rv = PP_ERROR_FAILED;
    526   pp::FileRef target_file_ref_abort(file_system,
    527                                     "/target_file_rename_abort");
    528   {
    529     pp::FileRef file_ref_abort(file_system, "/file_rename_abort");
    530     pp::FileIO file_io_abort(instance_);
    531     callback.WaitForResult(
    532         file_io_abort.Open(file_ref_abort, PP_FILEOPENFLAG_CREATE,
    533                            callback.GetCallback()));
    534     CHECK_CALLBACK_BEHAVIOR(callback);
    535     ASSERT_EQ(PP_OK, callback.result());
    536 
    537     rv = file_ref_abort.Rename(target_file_ref_abort, callback.GetCallback());
    538   }
    539   callback.WaitForAbortResult(rv);
    540   CHECK_CALLBACK_BEHAVIOR(callback);
    541 
    542   PASS();
    543 }
    544 
    545 std::string TestFileRef::TestQuery() {
    546   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    547 
    548   pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    549   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
    550   CHECK_CALLBACK_BEHAVIOR(callback);
    551   ASSERT_EQ(PP_OK, callback.result());
    552 
    553   pp::FileRef file_ref(file_system, "/file");
    554   pp::FileIO file_io(instance_);
    555   callback.WaitForResult(file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE,
    556                                       callback.GetCallback()));
    557   CHECK_CALLBACK_BEHAVIOR(callback);
    558   ASSERT_EQ(PP_OK, callback.result());
    559 
    560   // We touch the file so we can easily check access and modified time.
    561   callback.WaitForResult(file_io.Touch(0, 0, callback.GetCallback()));
    562   CHECK_CALLBACK_BEHAVIOR(callback);
    563   ASSERT_EQ(PP_OK, callback.result());
    564 
    565   TestCompletionCallbackWithOutput<PP_FileInfo> out_callback(
    566       instance_->pp_instance(), callback_type());
    567   out_callback.WaitForResult(file_ref.Query(out_callback.GetCallback()));
    568   CHECK_CALLBACK_BEHAVIOR(out_callback);
    569   ASSERT_EQ(PP_OK, out_callback.result());
    570 
    571   PP_FileInfo info = out_callback.output();
    572   ASSERT_EQ(0, info.size);
    573   ASSERT_EQ(PP_FILETYPE_REGULAR, info.type);
    574   ASSERT_EQ(PP_FILESYSTEMTYPE_LOCALTEMPORARY, info.system_type);
    575   ASSERT_DOUBLE_EQ(0.0, info.last_access_time);
    576   ASSERT_DOUBLE_EQ(0.0, info.last_modified_time);
    577 
    578   // Query a file ref on an external filesystem.
    579   pp::FileRef file_ref_ext;
    580   std::string result = MakeExternalFileRef(&file_ref_ext);
    581   if (!result.empty())
    582     return result;
    583   out_callback.WaitForResult(file_ref_ext.Query(out_callback.GetCallback()));
    584   CHECK_CALLBACK_BEHAVIOR(out_callback);
    585   if (out_callback.result() != PP_OK)
    586     return ReportError("Query() result", out_callback.result());
    587   ASSERT_EQ(PP_OK, out_callback.result());
    588 
    589   info = out_callback.output();
    590   ASSERT_EQ(PP_FILETYPE_REGULAR, info.type);
    591   ASSERT_EQ(PP_FILESYSTEMTYPE_EXTERNAL, info.system_type);
    592 
    593   // We can't touch the file, so just sanity check the times.
    594   ASSERT_TRUE(info.creation_time >= 0.0);
    595   ASSERT_TRUE(info.last_modified_time >= 0.0);
    596   ASSERT_TRUE(info.last_access_time >= 0.0);
    597 
    598   // Query a file ref for a file that doesn't exist.
    599   pp::FileRef missing_file_ref(file_system, "/missing_file");
    600   out_callback.WaitForResult(missing_file_ref.Query(
    601       out_callback.GetCallback()));
    602   CHECK_CALLBACK_BEHAVIOR(out_callback);
    603   ASSERT_EQ(PP_ERROR_FILENOTFOUND, out_callback.result());
    604 
    605   PASS();
    606 }
    607 
    608 std::string TestFileRef::TestFileNameEscaping() {
    609   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    610   pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    611   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
    612   CHECK_CALLBACK_BEHAVIOR(callback);
    613   ASSERT_EQ(PP_OK, callback.result());
    614 
    615   std::string test_dir_path = "/dir_for_escaping_test";
    616   // Create a directory in which to test.
    617   pp::FileRef test_dir_ref(file_system, test_dir_path.c_str());
    618   callback.WaitForResult(test_dir_ref.MakeDirectory(callback.GetCallback()));
    619   CHECK_CALLBACK_BEHAVIOR(callback);
    620   ASSERT_EQ(PP_OK, callback.result());
    621 
    622   // Create the file with the terrible name.
    623   std::string full_file_path = test_dir_path + "/" + kTerribleName;
    624   pp::FileRef file_ref(file_system, full_file_path.c_str());
    625   pp::FileIO file_io(instance_);
    626   callback.WaitForResult(
    627       file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback()));
    628   CHECK_CALLBACK_BEHAVIOR(callback);
    629   ASSERT_EQ(PP_OK, callback.result());
    630 
    631   // FileRef::ReadDirectoryEntries only works out-of-process.
    632   if (testing_interface_->IsOutOfProcess()) {
    633     TestCompletionCallbackWithOutput<DirEntries>
    634         output_callback(instance_->pp_instance(), callback_type());
    635 
    636     output_callback.WaitForResult(
    637         test_dir_ref.ReadDirectoryEntries(output_callback.GetCallback()));
    638     CHECK_CALLBACK_BEHAVIOR(output_callback);
    639     ASSERT_EQ(PP_OK, output_callback.result());
    640 
    641     DirEntries entries = output_callback.output();
    642     ASSERT_EQ(1, entries.size());
    643     ASSERT_EQ(kTerribleName, entries.front().file_ref().GetName().AsString());
    644   }
    645 
    646   PASS();
    647 }
    648 
    649 std::string TestFileRef::TestReadDirectoryEntries() {
    650   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    651   pp::FileSystem file_system(
    652       instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    653   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
    654   CHECK_CALLBACK_BEHAVIOR(callback);
    655   ASSERT_EQ(PP_OK, callback.result());
    656 
    657   // Setup testing directories and files.
    658   const char* test_dir_name = "/test_get_next_file";
    659   const char* file_prefix = "file_";
    660   const char* dir_prefix = "dir_";
    661 
    662   pp::FileRef test_dir(file_system, test_dir_name);
    663   int32_t rv = DeleteDirectoryRecursively(&test_dir);
    664   ASSERT_TRUE(rv == PP_OK || rv == PP_ERROR_FILENOTFOUND);
    665 
    666   callback.WaitForResult(test_dir.MakeDirectory(callback.GetCallback()));
    667   CHECK_CALLBACK_BEHAVIOR(callback);
    668   ASSERT_EQ(PP_OK, callback.result());
    669 
    670   static const int kNumFiles = 3;
    671   std::set<std::string> expected_file_names;
    672   for (int i = 1; i <= kNumFiles; ++i) {
    673     std::ostringstream buffer;
    674     buffer << test_dir_name << '/' << file_prefix << i;
    675     pp::FileRef file_ref(file_system, buffer.str().c_str());
    676 
    677     pp::FileIO file_io(instance_);
    678     callback.WaitForResult(
    679         file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback()));
    680     CHECK_CALLBACK_BEHAVIOR(callback);
    681     ASSERT_EQ(PP_OK, callback.result());
    682 
    683     expected_file_names.insert(buffer.str());
    684   }
    685 
    686   static const int kNumDirectories = 3;
    687   std::set<std::string> expected_dir_names;
    688   for (int i = 1; i <= kNumDirectories; ++i) {
    689     std::ostringstream buffer;
    690     buffer << test_dir_name << '/' << dir_prefix << i;
    691     pp::FileRef file_ref(file_system, buffer.str().c_str());
    692 
    693     callback.WaitForResult(file_ref.MakeDirectory(callback.GetCallback()));
    694     CHECK_CALLBACK_BEHAVIOR(callback);
    695     ASSERT_EQ(PP_OK, callback.result());
    696 
    697     expected_dir_names.insert(buffer.str());
    698   }
    699 
    700   // Test that |ReadDirectoryEntries()| is able to fetch all
    701   // directories and files that we created.
    702   {
    703     TestCompletionCallbackWithOutput<DirEntries> output_callback(
    704         instance_->pp_instance(), callback_type());
    705 
    706     output_callback.WaitForResult(
    707         test_dir.ReadDirectoryEntries(output_callback.GetCallback()));
    708     CHECK_CALLBACK_BEHAVIOR(output_callback);
    709     ASSERT_EQ(PP_OK, output_callback.result());
    710 
    711     DirEntries entries = output_callback.output();
    712     size_t sum = expected_file_names.size() + expected_dir_names.size();
    713     ASSERT_EQ(sum, entries.size());
    714 
    715     for (DirEntries::const_iterator it = entries.begin();
    716          it != entries.end(); ++it) {
    717       pp::FileRef file_ref = it->file_ref();
    718       std::string file_path = file_ref.GetPath().AsString();
    719       std::set<std::string>::iterator found =
    720           expected_file_names.find(file_path);
    721       if (found != expected_file_names.end()) {
    722         if (it->file_type() != PP_FILETYPE_REGULAR)
    723           return file_path + " should have been a regular file.";
    724         expected_file_names.erase(found);
    725       } else {
    726         found = expected_dir_names.find(file_path);
    727         if (found == expected_dir_names.end())
    728           return "Unexpected file path: " + file_path;
    729         if (it->file_type() != PP_FILETYPE_DIRECTORY)
    730           return file_path + " should have been a directory.";
    731         expected_dir_names.erase(found);
    732       }
    733     }
    734     ASSERT_TRUE(expected_file_names.empty());
    735     ASSERT_TRUE(expected_dir_names.empty());
    736   }
    737 
    738   // Test cancellation of asynchronous |ReadDirectoryEntries()|.
    739   TestCompletionCallbackWithOutput<DirEntries> output_callback(
    740       instance_->pp_instance(), callback_type());
    741   {
    742     rv = pp::FileRef(file_system, test_dir_name)
    743         .ReadDirectoryEntries(output_callback.GetCallback());
    744   }
    745   output_callback.WaitForAbortResult(rv);
    746   CHECK_CALLBACK_BEHAVIOR(output_callback);
    747 
    748 
    749   PASS();
    750 }
    751