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