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   // Make a directory.
    302   pp::FileRef dir_ref(file_system, "/dir_make_dir");
    303   callback.WaitForResult(
    304       dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
    305   CHECK_CALLBACK_BEHAVIOR(callback);
    306   ASSERT_EQ(PP_OK, callback.result());
    307 
    308   // Make a directory on the existing path without exclusive flag.
    309   callback.WaitForResult(
    310       dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
    311   CHECK_CALLBACK_BEHAVIOR(callback);
    312   ASSERT_EQ(PP_OK, callback.result());
    313 
    314   // Making a directory should be aborted.
    315   int32_t rv = PP_ERROR_FAILED;
    316   {
    317     rv = pp::FileRef(file_system, "/dir_make_dir_abort")
    318         .MakeDirectory(PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback());
    319   }
    320   callback.WaitForAbortResult(rv);
    321   CHECK_CALLBACK_BEHAVIOR(callback);
    322 
    323   // Make nested directories.
    324   dir_ref = pp::FileRef(file_system, "/dir_make_nested_dir_1/dir");
    325   callback.WaitForResult(
    326       dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_WITH_ANCESTORS,
    327                             callback.GetCallback()));
    328   CHECK_CALLBACK_BEHAVIOR(callback);
    329   ASSERT_EQ(PP_OK, callback.result());
    330 
    331   dir_ref = pp::FileRef(file_system, "/dir_make_nested_dir_2/dir");
    332   callback.WaitForResult(
    333       dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
    334   CHECK_CALLBACK_BEHAVIOR(callback);
    335   ASSERT_EQ(PP_ERROR_FILENOTFOUND, callback.result());
    336 
    337   // Ensure there is no directory on the path to test exclusive cases.
    338   dir_ref = pp::FileRef(file_system, "/dir_make_dir_exclusive");
    339   rv = DeleteDirectoryRecursively(&dir_ref);
    340   ASSERT_TRUE(rv == PP_OK || rv == PP_ERROR_FILENOTFOUND);
    341 
    342   // Make a directory exclusively.
    343   callback.WaitForResult(
    344       dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_EXCLUSIVE,
    345                             callback.GetCallback()));
    346   CHECK_CALLBACK_BEHAVIOR(callback);
    347   ASSERT_EQ(PP_OK, callback.result());
    348 
    349   callback.WaitForResult(
    350       dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_EXCLUSIVE,
    351                             callback.GetCallback()));
    352   CHECK_CALLBACK_BEHAVIOR(callback);
    353   ASSERT_EQ(PP_ERROR_FILEEXISTS, callback.result());
    354 
    355   PASS();
    356 }
    357 
    358 std::string TestFileRef::TestQueryAndTouchFile() {
    359   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    360   pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    361   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
    362   CHECK_CALLBACK_BEHAVIOR(callback);
    363   ASSERT_EQ(PP_OK, callback.result());
    364 
    365   pp::FileRef file_ref(file_system, "/file_touch");
    366   pp::FileIO file_io(instance_);
    367   callback.WaitForResult(
    368       file_io.Open(file_ref,
    369                    PP_FILEOPENFLAG_CREATE |
    370                    PP_FILEOPENFLAG_TRUNCATE |
    371                    PP_FILEOPENFLAG_WRITE,
    372                    callback.GetCallback()));
    373   CHECK_CALLBACK_BEHAVIOR(callback);
    374   ASSERT_EQ(PP_OK, callback.result());
    375 
    376   // Write some data to have a non-zero file size.
    377   callback.WaitForResult(file_io.Write(0, "test", 4, callback.GetCallback()));
    378   CHECK_CALLBACK_BEHAVIOR(callback);
    379   ASSERT_EQ(4, callback.result());
    380 
    381   // Touch.
    382   const PP_Time last_access_time = 123 * 24 * 3600.0;
    383   // last_modified_time's granularity is 2 seconds
    384   // See note in test_file_io.cc for why we use this time.
    385   const PP_Time last_modified_time = 100 * 24 * 3600.0;
    386   callback.WaitForResult(file_ref.Touch(last_access_time, last_modified_time,
    387                                         callback.GetCallback()));
    388   CHECK_CALLBACK_BEHAVIOR(callback);
    389   ASSERT_EQ(PP_OK, callback.result());
    390 
    391   // Touch aborted.
    392   int32_t rv = PP_ERROR_FAILED;
    393   {
    394     rv = pp::FileRef(file_system, "/file_touch_abort")
    395         .Touch(last_access_time, last_modified_time, callback.GetCallback());
    396   }
    397   callback.WaitForResult(rv);
    398   CHECK_CALLBACK_BEHAVIOR(callback);
    399   if (rv == PP_OK_COMPLETIONPENDING) {
    400     // Touch tried to run asynchronously and should have been aborted.
    401     ASSERT_EQ(PP_ERROR_ABORTED, callback.result());
    402   } else {
    403     // Touch ran synchronously and should have failed because the file does not
    404     // exist.
    405     ASSERT_EQ(PP_ERROR_FILENOTFOUND, callback.result());
    406   }
    407 
    408   // Query.
    409   PP_FileInfo info;
    410   callback.WaitForResult(file_io.Query(&info, callback.GetCallback()));
    411   CHECK_CALLBACK_BEHAVIOR(callback);
    412   ASSERT_EQ(PP_OK, callback.result());
    413   ASSERT_EQ(4, info.size);
    414   ASSERT_EQ(PP_FILETYPE_REGULAR, info.type);
    415   ASSERT_EQ(PP_FILESYSTEMTYPE_LOCALTEMPORARY, info.system_type);
    416 
    417   // Disabled due to DST-related failure: crbug.com/314579
    418   // ASSERT_EQ(last_access_time, info.last_access_time);
    419   // ASSERT_EQ(last_modified_time, info.last_modified_time);
    420 
    421   // Cancellation test.
    422   // TODO(viettrungluu): this test causes a bunch of LOG(WARNING)s; investigate.
    423   // TODO(viettrungluu): check |info| for late writes.
    424   {
    425     rv = pp::FileRef(file_system, "/file_touch").Touch(
    426         last_access_time, last_modified_time, callback.GetCallback());
    427   }
    428   callback.WaitForAbortResult(rv);
    429   CHECK_CALLBACK_BEHAVIOR(callback);
    430 
    431   PASS();
    432 }
    433 
    434 std::string TestFileRef::TestDeleteFileAndDirectory() {
    435   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    436   pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    437   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
    438   CHECK_CALLBACK_BEHAVIOR(callback);
    439   ASSERT_EQ(PP_OK, callback.result());
    440 
    441   pp::FileRef file_ref(file_system, "/file_delete");
    442   pp::FileIO file_io(instance_);
    443   callback.WaitForResult(
    444       file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback()));
    445   CHECK_CALLBACK_BEHAVIOR(callback);
    446   ASSERT_EQ(PP_OK, callback.result());
    447 
    448   callback.WaitForResult(file_ref.Delete(callback.GetCallback()));
    449   CHECK_CALLBACK_BEHAVIOR(callback);
    450   ASSERT_EQ(PP_OK, callback.result());
    451 
    452   pp::FileRef dir_ref(file_system, "/dir_delete");
    453   callback.WaitForResult(dir_ref.MakeDirectory(
    454       PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
    455   CHECK_CALLBACK_BEHAVIOR(callback);
    456   ASSERT_EQ(PP_OK, callback.result());
    457 
    458   callback.WaitForResult(dir_ref.Delete(callback.GetCallback()));
    459   CHECK_CALLBACK_BEHAVIOR(callback);
    460   ASSERT_EQ(PP_OK, callback.result());
    461 
    462   pp::FileRef nested_dir_ref(file_system, "/dir_delete_1/dir_delete_2");
    463   callback.WaitForResult(
    464       nested_dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_WITH_ANCESTORS,
    465                                    callback.GetCallback()));
    466   CHECK_CALLBACK_BEHAVIOR(callback);
    467   ASSERT_EQ(PP_OK, callback.result());
    468 
    469   // Attempt to delete the parent directory (should fail; it's non-empty).
    470   pp::FileRef parent_dir_ref = nested_dir_ref.GetParent();
    471   callback.WaitForResult(parent_dir_ref.Delete(callback.GetCallback()));
    472   CHECK_CALLBACK_BEHAVIOR(callback);
    473   ASSERT_EQ(PP_ERROR_FAILED, callback.result());
    474 
    475   pp::FileRef nonexistent_file_ref(file_system, "/nonexistent_file_delete");
    476   callback.WaitForResult(nonexistent_file_ref.Delete(callback.GetCallback()));
    477   CHECK_CALLBACK_BEHAVIOR(callback);
    478   ASSERT_EQ(PP_ERROR_FILENOTFOUND, callback.result());
    479 
    480   // Delete aborted.
    481   int32_t rv = PP_ERROR_FAILED;
    482   {
    483     pp::FileRef file_ref_abort(file_system, "/file_delete_abort");
    484     pp::FileIO file_io_abort(instance_);
    485     callback.WaitForResult(
    486         file_io_abort.Open(file_ref_abort, PP_FILEOPENFLAG_CREATE,
    487                            callback.GetCallback()));
    488     CHECK_CALLBACK_BEHAVIOR(callback);
    489     ASSERT_EQ(PP_OK, callback.result());
    490     rv = file_ref_abort.Delete(callback.GetCallback());
    491   }
    492   callback.WaitForAbortResult(rv);
    493   CHECK_CALLBACK_BEHAVIOR(callback);
    494 
    495   PASS();
    496 }
    497 
    498 std::string TestFileRef::TestRenameFileAndDirectory() {
    499   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    500   pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    501   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
    502   CHECK_CALLBACK_BEHAVIOR(callback);
    503   ASSERT_EQ(PP_OK, callback.result());
    504 
    505   pp::FileRef file_ref(file_system, "/file_rename");
    506   pp::FileIO file_io(instance_);
    507   callback.WaitForResult(
    508       file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback()));
    509   CHECK_CALLBACK_BEHAVIOR(callback);
    510   ASSERT_EQ(PP_OK, callback.result());
    511 
    512   pp::FileRef target_file_ref(file_system, "/target_file_rename");
    513   callback.WaitForResult(
    514       file_ref.Rename(target_file_ref, callback.GetCallback()));
    515   CHECK_CALLBACK_BEHAVIOR(callback);
    516   ASSERT_EQ(PP_OK, callback.result());
    517 
    518   pp::FileRef dir_ref(file_system, "/dir_rename");
    519   callback.WaitForResult(dir_ref.MakeDirectory(
    520       PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
    521   CHECK_CALLBACK_BEHAVIOR(callback);
    522   ASSERT_EQ(PP_OK, callback.result());
    523 
    524   pp::FileRef target_dir_ref(file_system, "/target_dir_rename");
    525   callback.WaitForResult(
    526       dir_ref.Rename(target_dir_ref, callback.GetCallback()));
    527   CHECK_CALLBACK_BEHAVIOR(callback);
    528   ASSERT_EQ(PP_OK, callback.result());
    529 
    530   pp::FileRef nested_dir_ref(file_system, "/dir_rename_1/dir_rename_2");
    531   callback.WaitForResult(
    532       nested_dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_WITH_ANCESTORS,
    533                                    callback.GetCallback()));
    534   CHECK_CALLBACK_BEHAVIOR(callback);
    535   ASSERT_EQ(PP_OK, callback.result());
    536 
    537   // Try to rename nested directory to the parent name. Should fail.
    538   pp::FileRef target_nested_dir_ref(file_system, "/dir_rename_1");
    539   callback.WaitForResult(
    540       nested_dir_ref.Rename(target_nested_dir_ref, callback.GetCallback()));
    541   CHECK_CALLBACK_BEHAVIOR(callback);
    542   ASSERT_EQ(PP_ERROR_FAILED, callback.result());
    543 
    544   // Rename aborted.
    545   // TODO(viettrungluu): Figure out what we want to do if the target file
    546   // resource is destroyed before completion.
    547   int32_t rv = PP_ERROR_FAILED;
    548   pp::FileRef target_file_ref_abort(file_system,
    549                                     "/target_file_rename_abort");
    550   {
    551     pp::FileRef file_ref_abort(file_system, "/file_rename_abort");
    552     pp::FileIO file_io_abort(instance_);
    553     callback.WaitForResult(
    554         file_io_abort.Open(file_ref_abort, PP_FILEOPENFLAG_CREATE,
    555                            callback.GetCallback()));
    556     CHECK_CALLBACK_BEHAVIOR(callback);
    557     ASSERT_EQ(PP_OK, callback.result());
    558 
    559     rv = file_ref_abort.Rename(target_file_ref_abort, callback.GetCallback());
    560   }
    561   callback.WaitForAbortResult(rv);
    562   CHECK_CALLBACK_BEHAVIOR(callback);
    563 
    564   PASS();
    565 }
    566 
    567 std::string TestFileRef::TestQuery() {
    568   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    569 
    570   pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    571   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
    572   CHECK_CALLBACK_BEHAVIOR(callback);
    573   ASSERT_EQ(PP_OK, callback.result());
    574 
    575   pp::FileRef file_ref(file_system, "/file");
    576   pp::FileIO file_io(instance_);
    577   callback.WaitForResult(file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE,
    578                                       callback.GetCallback()));
    579   CHECK_CALLBACK_BEHAVIOR(callback);
    580   ASSERT_EQ(PP_OK, callback.result());
    581 
    582   // We touch the file so we can easily check access and modified time.
    583   callback.WaitForResult(file_io.Touch(0, 0, callback.GetCallback()));
    584   CHECK_CALLBACK_BEHAVIOR(callback);
    585   ASSERT_EQ(PP_OK, callback.result());
    586 
    587   TestCompletionCallbackWithOutput<PP_FileInfo> out_callback(
    588       instance_->pp_instance(), callback_type());
    589   out_callback.WaitForResult(file_ref.Query(out_callback.GetCallback()));
    590   CHECK_CALLBACK_BEHAVIOR(out_callback);
    591   ASSERT_EQ(PP_OK, out_callback.result());
    592 
    593   PP_FileInfo info = out_callback.output();
    594   ASSERT_EQ(0, info.size);
    595   ASSERT_EQ(PP_FILETYPE_REGULAR, info.type);
    596   ASSERT_EQ(PP_FILESYSTEMTYPE_LOCALTEMPORARY, info.system_type);
    597   ASSERT_DOUBLE_EQ(0.0, info.last_access_time);
    598   ASSERT_DOUBLE_EQ(0.0, info.last_modified_time);
    599 
    600   // Query a file ref on an external filesystem.
    601   pp::FileRef file_ref_ext;
    602   std::string result = MakeExternalFileRef(&file_ref_ext);
    603   if (!result.empty())
    604     return result;
    605   out_callback.WaitForResult(file_ref_ext.Query(out_callback.GetCallback()));
    606   CHECK_CALLBACK_BEHAVIOR(out_callback);
    607   if (out_callback.result() != PP_OK)
    608     return ReportError("Query() result", out_callback.result());
    609   ASSERT_EQ(PP_OK, out_callback.result());
    610 
    611   info = out_callback.output();
    612   ASSERT_EQ(PP_FILETYPE_REGULAR, info.type);
    613   ASSERT_EQ(PP_FILESYSTEMTYPE_EXTERNAL, info.system_type);
    614 
    615   // We can't touch the file, so just sanity check the times.
    616   ASSERT_TRUE(info.creation_time >= 0.0);
    617   ASSERT_TRUE(info.last_modified_time >= 0.0);
    618   ASSERT_TRUE(info.last_access_time >= 0.0);
    619 
    620   // Query a file ref for a file that doesn't exist.
    621   pp::FileRef missing_file_ref(file_system, "/missing_file");
    622   out_callback.WaitForResult(missing_file_ref.Query(
    623       out_callback.GetCallback()));
    624   CHECK_CALLBACK_BEHAVIOR(out_callback);
    625   ASSERT_EQ(PP_ERROR_FILENOTFOUND, out_callback.result());
    626 
    627   PASS();
    628 }
    629 
    630 std::string TestFileRef::TestFileNameEscaping() {
    631   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    632   pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    633   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
    634   CHECK_CALLBACK_BEHAVIOR(callback);
    635   ASSERT_EQ(PP_OK, callback.result());
    636 
    637   std::string test_dir_path = "/dir_for_escaping_test";
    638   // Create a directory in which to test.
    639   pp::FileRef test_dir_ref(file_system, test_dir_path.c_str());
    640   callback.WaitForResult(test_dir_ref.MakeDirectory(
    641       PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
    642   CHECK_CALLBACK_BEHAVIOR(callback);
    643   ASSERT_EQ(PP_OK, callback.result());
    644 
    645   // Create the file with the terrible name.
    646   std::string full_file_path = test_dir_path + "/" + kTerribleName;
    647   pp::FileRef file_ref(file_system, full_file_path.c_str());
    648   pp::FileIO file_io(instance_);
    649   callback.WaitForResult(
    650       file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback()));
    651   CHECK_CALLBACK_BEHAVIOR(callback);
    652   ASSERT_EQ(PP_OK, callback.result());
    653 
    654   // FileRef::ReadDirectoryEntries only works out-of-process.
    655   if (testing_interface_->IsOutOfProcess()) {
    656     TestCompletionCallbackWithOutput<DirEntries>
    657         output_callback(instance_->pp_instance(), callback_type());
    658 
    659     output_callback.WaitForResult(
    660         test_dir_ref.ReadDirectoryEntries(output_callback.GetCallback()));
    661     CHECK_CALLBACK_BEHAVIOR(output_callback);
    662     ASSERT_EQ(PP_OK, output_callback.result());
    663 
    664     DirEntries entries = output_callback.output();
    665     ASSERT_EQ(1, entries.size());
    666     ASSERT_EQ(kTerribleName, entries.front().file_ref().GetName().AsString());
    667   }
    668 
    669   PASS();
    670 }
    671 
    672 std::string TestFileRef::TestReadDirectoryEntries() {
    673   TestCompletionCallback callback(instance_->pp_instance(), callback_type());
    674   pp::FileSystem file_system(
    675       instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
    676   callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
    677   CHECK_CALLBACK_BEHAVIOR(callback);
    678   ASSERT_EQ(PP_OK, callback.result());
    679 
    680   // Setup testing directories and files.
    681   const char* test_dir_name = "/test_get_next_file";
    682   const char* file_prefix = "file_";
    683   const char* dir_prefix = "dir_";
    684 
    685   pp::FileRef test_dir(file_system, test_dir_name);
    686   int32_t rv = DeleteDirectoryRecursively(&test_dir);
    687   ASSERT_TRUE(rv == PP_OK || rv == PP_ERROR_FILENOTFOUND);
    688 
    689   callback.WaitForResult(test_dir.MakeDirectory(
    690       PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
    691   CHECK_CALLBACK_BEHAVIOR(callback);
    692   ASSERT_EQ(PP_OK, callback.result());
    693 
    694   static const int kNumFiles = 3;
    695   std::set<std::string> expected_file_names;
    696   for (int i = 1; i <= kNumFiles; ++i) {
    697     std::ostringstream buffer;
    698     buffer << test_dir_name << '/' << file_prefix << i;
    699     pp::FileRef file_ref(file_system, buffer.str().c_str());
    700 
    701     pp::FileIO file_io(instance_);
    702     callback.WaitForResult(
    703         file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback()));
    704     CHECK_CALLBACK_BEHAVIOR(callback);
    705     ASSERT_EQ(PP_OK, callback.result());
    706 
    707     expected_file_names.insert(buffer.str());
    708   }
    709 
    710   static const int kNumDirectories = 3;
    711   std::set<std::string> expected_dir_names;
    712   for (int i = 1; i <= kNumDirectories; ++i) {
    713     std::ostringstream buffer;
    714     buffer << test_dir_name << '/' << dir_prefix << i;
    715     pp::FileRef file_ref(file_system, buffer.str().c_str());
    716 
    717     callback.WaitForResult(file_ref.MakeDirectory(
    718         PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
    719     CHECK_CALLBACK_BEHAVIOR(callback);
    720     ASSERT_EQ(PP_OK, callback.result());
    721 
    722     expected_dir_names.insert(buffer.str());
    723   }
    724 
    725   // Test that |ReadDirectoryEntries()| is able to fetch all
    726   // directories and files that we created.
    727   {
    728     TestCompletionCallbackWithOutput<DirEntries> output_callback(
    729         instance_->pp_instance(), callback_type());
    730 
    731     output_callback.WaitForResult(
    732         test_dir.ReadDirectoryEntries(output_callback.GetCallback()));
    733     CHECK_CALLBACK_BEHAVIOR(output_callback);
    734     ASSERT_EQ(PP_OK, output_callback.result());
    735 
    736     DirEntries entries = output_callback.output();
    737     size_t sum = expected_file_names.size() + expected_dir_names.size();
    738     ASSERT_EQ(sum, entries.size());
    739 
    740     for (DirEntries::const_iterator it = entries.begin();
    741          it != entries.end(); ++it) {
    742       pp::FileRef file_ref = it->file_ref();
    743       std::string file_path = file_ref.GetPath().AsString();
    744       std::set<std::string>::iterator found =
    745           expected_file_names.find(file_path);
    746       if (found != expected_file_names.end()) {
    747         if (it->file_type() != PP_FILETYPE_REGULAR)
    748           return file_path + " should have been a regular file.";
    749         expected_file_names.erase(found);
    750       } else {
    751         found = expected_dir_names.find(file_path);
    752         if (found == expected_dir_names.end())
    753           return "Unexpected file path: " + file_path;
    754         if (it->file_type() != PP_FILETYPE_DIRECTORY)
    755           return file_path + " should have been a directory.";
    756         expected_dir_names.erase(found);
    757       }
    758     }
    759     ASSERT_TRUE(expected_file_names.empty());
    760     ASSERT_TRUE(expected_dir_names.empty());
    761   }
    762 
    763   // Test cancellation of asynchronous |ReadDirectoryEntries()|.
    764   TestCompletionCallbackWithOutput<DirEntries> output_callback(
    765       instance_->pp_instance(), callback_type());
    766   {
    767     rv = pp::FileRef(file_system, test_dir_name)
    768         .ReadDirectoryEntries(output_callback.GetCallback());
    769   }
    770   output_callback.WaitForAbortResult(rv);
    771   CHECK_CALLBACK_BEHAVIOR(output_callback);
    772 
    773 
    774   PASS();
    775 }
    776