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