| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ppapi/tests/test_file_ref.h" |
| |
| #include <stddef.h> |
| #include <stdio.h> |
| |
| #include <sstream> |
| #include <vector> |
| |
| #include "ppapi/c/pp_errors.h" |
| #include "ppapi/c/ppb_file_io.h" |
| #include "ppapi/c/private/ppb_testing_private.h" |
| #include "ppapi/cpp/directory_entry.h" |
| #include "ppapi/cpp/file_io.h" |
| #include "ppapi/cpp/file_ref.h" |
| #include "ppapi/cpp/file_system.h" |
| #include "ppapi/cpp/instance.h" |
| #include "ppapi/cpp/module.h" |
| #include "ppapi/cpp/url_loader.h" |
| #include "ppapi/cpp/url_request_info.h" |
| #include "ppapi/cpp/url_response_info.h" |
| #include "ppapi/tests/test_utils.h" |
| #include "ppapi/tests/testing_instance.h" |
| |
| REGISTER_TEST_CASE(FileRef); |
| |
| namespace { |
| |
| const char* kPersFileName = "persistent"; |
| const char* kTempFileName = "temporary"; |
| const char* kParentPath = "/foo/bar"; |
| const char* kPersFilePath = "/foo/bar/persistent"; |
| const char* kTempFilePath = "/foo/bar/temporary"; |
| const char* kTerribleName = "!@#$%^&*()-_=+{}[] ;:'\"|`~\t\n\r\b?"; |
| |
| typedef std::vector<pp::DirectoryEntry> DirEntries; |
| |
| std::string ReportMismatch(const std::string& method_name, |
| const std::string& returned_result, |
| const std::string& expected_result) { |
| return method_name + " returned '" + returned_result + "'; '" + |
| expected_result + "' expected."; |
| } |
| |
| } // namespace |
| |
| bool TestFileRef::Init() { |
| return CheckTestingInterface() && EnsureRunningOverHTTP(); |
| } |
| |
| std::string TestFileRef::MakeExternalFileRef(pp::FileRef* file_ref_ext) { |
| pp::URLRequestInfo request(instance_); |
| request.SetURL("test_url_loader_data/hello.txt"); |
| request.SetStreamToFile(true); |
| |
| TestCompletionCallback callback(instance_->pp_instance(), callback_type()); |
| |
| pp::URLLoader loader(instance_); |
| callback.WaitForResult(loader.Open(request, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| pp::URLResponseInfo response_info(loader.GetResponseInfo()); |
| ASSERT_FALSE(response_info.is_null()); |
| ASSERT_EQ(200, response_info.GetStatusCode()); |
| |
| *file_ref_ext = pp::FileRef(response_info.GetBodyAsFileRef()); |
| ASSERT_EQ(PP_FILESYSTEMTYPE_EXTERNAL, file_ref_ext->GetFileSystemType()); |
| PASS(); |
| } |
| |
| int32_t TestFileRef::DeleteDirectoryRecursively(pp::FileRef* dir) { |
| if (!dir) |
| return PP_ERROR_BADARGUMENT; |
| |
| TestCompletionCallback callback(instance_->pp_instance(), callback_type()); |
| TestCompletionCallbackWithOutput<DirEntries> output_callback( |
| instance_->pp_instance(), callback_type()); |
| |
| output_callback.WaitForResult( |
| dir->ReadDirectoryEntries(output_callback.GetCallback())); |
| int32_t rv = output_callback.result(); |
| if (rv != PP_OK && rv != PP_ERROR_FILENOTFOUND) |
| return rv; |
| |
| DirEntries entries = output_callback.output(); |
| for (DirEntries::const_iterator it = entries.begin(); |
| it != entries.end(); |
| ++it) { |
| pp::FileRef file_ref = it->file_ref(); |
| if (it->file_type() == PP_FILETYPE_DIRECTORY) { |
| rv = DeleteDirectoryRecursively(&file_ref); |
| if (rv != PP_OK && rv != PP_ERROR_FILENOTFOUND) |
| return rv; |
| } else { |
| callback.WaitForResult(file_ref.Delete(callback.GetCallback())); |
| rv = callback.result(); |
| if (rv != PP_OK && rv != PP_ERROR_FILENOTFOUND) |
| return rv; |
| } |
| } |
| callback.WaitForResult(dir->Delete(callback.GetCallback())); |
| return callback.result(); |
| } |
| |
| void TestFileRef::RunTests(const std::string& filter) { |
| RUN_CALLBACK_TEST(TestFileRef, Create, filter); |
| RUN_CALLBACK_TEST(TestFileRef, GetFileSystemType, filter); |
| RUN_CALLBACK_TEST(TestFileRef, GetName, filter); |
| RUN_CALLBACK_TEST(TestFileRef, GetPath, filter); |
| RUN_CALLBACK_TEST(TestFileRef, GetParent, filter); |
| RUN_CALLBACK_TEST(TestFileRef, MakeDirectory, filter); |
| RUN_CALLBACK_TEST(TestFileRef, QueryAndTouchFile, filter); |
| RUN_CALLBACK_TEST(TestFileRef, DeleteFileAndDirectory, filter); |
| RUN_CALLBACK_TEST(TestFileRef, RenameFileAndDirectory, filter); |
| RUN_CALLBACK_TEST(TestFileRef, Query, filter); |
| RUN_CALLBACK_TEST(TestFileRef, FileNameEscaping, filter); |
| RUN_CALLBACK_TEST(TestFileRef, ReadDirectoryEntries, filter); |
| } |
| |
| std::string TestFileRef::TestCreate() { |
| std::vector<std::string> invalid_paths; |
| invalid_paths.push_back("invalid_path"); // no '/' at the first character |
| invalid_paths.push_back(std::string()); // empty path |
| // The following are directory traversal checks |
| invalid_paths.push_back(".."); |
| invalid_paths.push_back("/../invalid_path"); |
| invalid_paths.push_back("/../../invalid_path"); |
| invalid_paths.push_back("/invalid/../../path"); |
| const size_t num_invalid_paths = invalid_paths.size(); |
| |
| pp::FileSystem file_system_pers( |
| instance_, PP_FILESYSTEMTYPE_LOCALPERSISTENT); |
| pp::FileSystem file_system_temp( |
| instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); |
| for (size_t j = 0; j < num_invalid_paths; ++j) { |
| pp::FileRef file_ref_pers(file_system_pers, invalid_paths[j].c_str()); |
| if (file_ref_pers.pp_resource() != 0) { |
| return "file_ref_pers expected to be invalid for path: " + |
| invalid_paths[j]; |
| } |
| pp::FileRef file_ref_temp(file_system_temp, invalid_paths[j].c_str()); |
| if (file_ref_temp.pp_resource() != 0) { |
| return "file_ref_temp expected to be invalid for path: " + |
| invalid_paths[j]; |
| } |
| } |
| PASS(); |
| } |
| |
| std::string TestFileRef::TestGetFileSystemType() { |
| pp::FileSystem file_system_pers( |
| instance_, PP_FILESYSTEMTYPE_LOCALPERSISTENT); |
| pp::FileSystem file_system_temp( |
| instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); |
| |
| pp::FileRef file_ref_pers(file_system_pers, kPersFilePath); |
| if (file_ref_pers.GetFileSystemType() != PP_FILESYSTEMTYPE_LOCALPERSISTENT) |
| return "file_ref_pers expected to be persistent."; |
| |
| pp::FileRef file_ref_temp(file_system_temp, kTempFilePath); |
| if (file_ref_temp.GetFileSystemType() != PP_FILESYSTEMTYPE_LOCALTEMPORARY) |
| return "file_ref_temp expected to be temporary."; |
| |
| pp::FileRef file_ref_ext; |
| std::string result = MakeExternalFileRef(&file_ref_ext); |
| if (!result.empty()) |
| return result; |
| PASS(); |
| } |
| |
| std::string TestFileRef::TestGetName() { |
| pp::FileSystem file_system_pers( |
| instance_, PP_FILESYSTEMTYPE_LOCALPERSISTENT); |
| pp::FileSystem file_system_temp( |
| instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); |
| |
| pp::FileRef file_ref_pers(file_system_pers, kPersFilePath); |
| std::string name = file_ref_pers.GetName().AsString(); |
| if (name != kPersFileName) |
| return ReportMismatch("FileRef::GetName", name, kPersFileName); |
| |
| pp::FileRef file_ref_temp(file_system_temp, kTempFilePath); |
| name = file_ref_temp.GetName().AsString(); |
| if (name != kTempFileName) |
| return ReportMismatch("FileRef::GetName", name, kTempFileName); |
| |
| // Test the "/" case. |
| pp::FileRef file_ref_slash(file_system_temp, "/"); |
| name = file_ref_slash.GetName().AsString(); |
| if (name != "/") |
| return ReportMismatch("FileRef::GetName", name, "/"); |
| |
| pp::URLRequestInfo request(instance_); |
| request.SetURL("test_url_loader_data/hello.txt"); |
| request.SetStreamToFile(true); |
| |
| TestCompletionCallback callback(instance_->pp_instance(), callback_type()); |
| |
| pp::URLLoader loader(instance_); |
| callback.WaitForResult(loader.Open(request, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| pp::URLResponseInfo response_info(loader.GetResponseInfo()); |
| ASSERT_FALSE(response_info.is_null()); |
| ASSERT_EQ(200, response_info.GetStatusCode()); |
| |
| pp::FileRef file_ref_ext(response_info.GetBodyAsFileRef()); |
| name = file_ref_ext.GetName().AsString(); |
| ASSERT_FALSE(name.empty()); |
| |
| PASS(); |
| } |
| |
| std::string TestFileRef::TestGetPath() { |
| pp::FileSystem file_system_pers( |
| instance_, PP_FILESYSTEMTYPE_LOCALPERSISTENT); |
| pp::FileSystem file_system_temp( |
| instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); |
| |
| pp::FileRef file_ref_pers(file_system_pers, kPersFilePath); |
| ASSERT_EQ(kPersFilePath, file_ref_pers.GetPath().AsString()); |
| |
| pp::FileRef file_ref_temp(file_system_temp, kTempFilePath); |
| ASSERT_EQ(kTempFilePath, file_ref_temp.GetPath().AsString()); |
| |
| pp::URLRequestInfo request(instance_); |
| request.SetURL("test_url_loader_data/hello.txt"); |
| request.SetStreamToFile(true); |
| |
| TestCompletionCallback callback(instance_->pp_instance(), callback_type()); |
| |
| pp::URLLoader loader(instance_); |
| callback.WaitForResult(loader.Open(request, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| pp::URLResponseInfo response_info(loader.GetResponseInfo()); |
| ASSERT_FALSE(response_info.is_null()); |
| ASSERT_EQ(200, response_info.GetStatusCode()); |
| |
| pp::FileRef file_ref_ext(response_info.GetBodyAsFileRef()); |
| ASSERT_TRUE(file_ref_ext.GetPath().is_undefined()); |
| |
| PASS(); |
| } |
| |
| std::string TestFileRef::TestGetParent() { |
| pp::FileSystem file_system_pers( |
| instance_, PP_FILESYSTEMTYPE_LOCALPERSISTENT); |
| pp::FileSystem file_system_temp( |
| instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); |
| |
| pp::FileRef file_ref_pers(file_system_pers, kPersFilePath); |
| ASSERT_EQ(kParentPath, file_ref_pers.GetParent().GetPath().AsString()); |
| |
| pp::FileRef file_ref_temp(file_system_temp, kTempFilePath); |
| ASSERT_EQ(kParentPath, file_ref_temp.GetParent().GetPath().AsString()); |
| |
| // Test the "/" case. |
| pp::FileRef file_ref_slash(file_system_temp, "/"); |
| ASSERT_EQ("/", file_ref_slash.GetParent().GetPath().AsString()); |
| |
| // Test the "/foo" case (the parent is "/"). |
| pp::FileRef file_ref_with_root_parent(file_system_temp, "/foo"); |
| ASSERT_EQ("/", file_ref_with_root_parent.GetParent().GetPath().AsString()); |
| |
| pp::URLRequestInfo request(instance_); |
| request.SetURL("test_url_loader_data/hello.txt"); |
| request.SetStreamToFile(true); |
| |
| TestCompletionCallback callback(instance_->pp_instance(), callback_type()); |
| |
| pp::URLLoader loader(instance_); |
| callback.WaitForResult(loader.Open(request, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| pp::URLResponseInfo response_info(loader.GetResponseInfo()); |
| ASSERT_FALSE(response_info.is_null()); |
| ASSERT_EQ(200, response_info.GetStatusCode()); |
| |
| pp::FileRef file_ref_ext(response_info.GetBodyAsFileRef()); |
| ASSERT_TRUE(file_ref_ext.GetParent().is_null()); |
| |
| PASS(); |
| } |
| |
| std::string TestFileRef::TestMakeDirectory() { |
| TestCompletionCallback callback(instance_->pp_instance(), callback_type()); |
| |
| // Open. |
| pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); |
| callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| // Make a directory. |
| pp::FileRef dir_ref(file_system, "/dir_make_dir"); |
| callback.WaitForResult( |
| dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| // Make a directory on the existing path without exclusive flag. |
| callback.WaitForResult( |
| dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| // Making a directory should be aborted. |
| int32_t rv = PP_ERROR_FAILED; |
| { |
| rv = pp::FileRef(file_system, "/dir_make_dir_abort") |
| .MakeDirectory(PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()); |
| } |
| callback.WaitForAbortResult(rv); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| |
| // Make nested directories. |
| dir_ref = pp::FileRef(file_system, "/dir_make_nested_dir_1/dir"); |
| callback.WaitForResult( |
| dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_WITH_ANCESTORS, |
| callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| dir_ref = pp::FileRef(file_system, "/dir_make_nested_dir_2/dir"); |
| callback.WaitForResult( |
| dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_ERROR_FILENOTFOUND, callback.result()); |
| |
| // Ensure there is no directory on the path to test exclusive cases. |
| dir_ref = pp::FileRef(file_system, "/dir_make_dir_exclusive"); |
| rv = DeleteDirectoryRecursively(&dir_ref); |
| ASSERT_TRUE(rv == PP_OK || rv == PP_ERROR_FILENOTFOUND); |
| |
| // Make a directory exclusively. |
| callback.WaitForResult( |
| dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_EXCLUSIVE, |
| callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| callback.WaitForResult( |
| dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_EXCLUSIVE, |
| callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_ERROR_FILEEXISTS, callback.result()); |
| |
| PASS(); |
| } |
| |
| std::string TestFileRef::TestQueryAndTouchFile() { |
| TestCompletionCallback callback(instance_->pp_instance(), callback_type()); |
| pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); |
| callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| pp::FileRef file_ref(file_system, "/file_touch"); |
| pp::FileIO file_io(instance_); |
| callback.WaitForResult( |
| file_io.Open(file_ref, |
| PP_FILEOPENFLAG_CREATE | |
| PP_FILEOPENFLAG_TRUNCATE | |
| PP_FILEOPENFLAG_WRITE, |
| callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| // Write some data to have a non-zero file size. |
| callback.WaitForResult(file_io.Write(0, "test", 4, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(4, callback.result()); |
| |
| // Touch. |
| const PP_Time last_access_time = 123 * 24 * 3600.0; |
| // last_modified_time's granularity is 2 seconds |
| // See note in test_file_io.cc for why we use this time. |
| const PP_Time last_modified_time = 100 * 24 * 3600.0; |
| callback.WaitForResult(file_ref.Touch(last_access_time, last_modified_time, |
| callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| // Touch aborted. |
| int32_t rv = PP_ERROR_FAILED; |
| { |
| rv = pp::FileRef(file_system, "/file_touch_abort") |
| .Touch(last_access_time, last_modified_time, callback.GetCallback()); |
| } |
| callback.WaitForResult(rv); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| if (rv == PP_OK_COMPLETIONPENDING) { |
| // Touch tried to run asynchronously and should have been aborted. |
| ASSERT_EQ(PP_ERROR_ABORTED, callback.result()); |
| } else { |
| // Touch ran synchronously and should have failed because the file does not |
| // exist. |
| ASSERT_EQ(PP_ERROR_FILENOTFOUND, callback.result()); |
| } |
| |
| // Query. |
| PP_FileInfo info; |
| callback.WaitForResult(file_io.Query(&info, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| ASSERT_EQ(4, info.size); |
| ASSERT_EQ(PP_FILETYPE_REGULAR, info.type); |
| ASSERT_EQ(PP_FILESYSTEMTYPE_LOCALTEMPORARY, info.system_type); |
| |
| // Disabled due to DST-related failure: crbug.com/314579 |
| // ASSERT_EQ(last_access_time, info.last_access_time); |
| // ASSERT_EQ(last_modified_time, info.last_modified_time); |
| |
| // Cancellation test. |
| // TODO(viettrungluu): this test causes a bunch of LOG(WARNING)s; investigate. |
| // TODO(viettrungluu): check |info| for late writes. |
| { |
| rv = pp::FileRef(file_system, "/file_touch").Touch( |
| last_access_time, last_modified_time, callback.GetCallback()); |
| } |
| callback.WaitForAbortResult(rv); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| |
| PASS(); |
| } |
| |
| std::string TestFileRef::TestDeleteFileAndDirectory() { |
| TestCompletionCallback callback(instance_->pp_instance(), callback_type()); |
| pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); |
| callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| pp::FileRef file_ref(file_system, "/file_delete"); |
| pp::FileIO file_io(instance_); |
| callback.WaitForResult( |
| file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| callback.WaitForResult(file_ref.Delete(callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| pp::FileRef dir_ref(file_system, "/dir_delete"); |
| callback.WaitForResult(dir_ref.MakeDirectory( |
| PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| callback.WaitForResult(dir_ref.Delete(callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| pp::FileRef nested_dir_ref(file_system, "/dir_delete_1/dir_delete_2"); |
| callback.WaitForResult( |
| nested_dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_WITH_ANCESTORS, |
| callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| // Attempt to delete the parent directory (should fail; it's non-empty). |
| pp::FileRef parent_dir_ref = nested_dir_ref.GetParent(); |
| callback.WaitForResult(parent_dir_ref.Delete(callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_ERROR_FAILED, callback.result()); |
| |
| pp::FileRef nonexistent_file_ref(file_system, "/nonexistent_file_delete"); |
| callback.WaitForResult(nonexistent_file_ref.Delete(callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_ERROR_FILENOTFOUND, callback.result()); |
| |
| // Delete aborted. |
| int32_t rv = PP_ERROR_FAILED; |
| { |
| pp::FileRef file_ref_abort(file_system, "/file_delete_abort"); |
| pp::FileIO file_io_abort(instance_); |
| callback.WaitForResult( |
| file_io_abort.Open(file_ref_abort, PP_FILEOPENFLAG_CREATE, |
| callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| rv = file_ref_abort.Delete(callback.GetCallback()); |
| } |
| callback.WaitForAbortResult(rv); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| |
| PASS(); |
| } |
| |
| std::string TestFileRef::TestRenameFileAndDirectory() { |
| TestCompletionCallback callback(instance_->pp_instance(), callback_type()); |
| pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); |
| callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| pp::FileRef file_ref(file_system, "/file_rename"); |
| pp::FileIO file_io(instance_); |
| callback.WaitForResult( |
| file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| pp::FileRef target_file_ref(file_system, "/target_file_rename"); |
| callback.WaitForResult( |
| file_ref.Rename(target_file_ref, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| pp::FileRef dir_ref(file_system, "/dir_rename"); |
| callback.WaitForResult(dir_ref.MakeDirectory( |
| PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| pp::FileRef target_dir_ref(file_system, "/target_dir_rename"); |
| callback.WaitForResult( |
| dir_ref.Rename(target_dir_ref, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| pp::FileRef nested_dir_ref(file_system, "/dir_rename_1/dir_rename_2"); |
| callback.WaitForResult( |
| nested_dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_WITH_ANCESTORS, |
| callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| // Try to rename nested directory to the parent name. Should fail. |
| pp::FileRef target_nested_dir_ref(file_system, "/dir_rename_1"); |
| callback.WaitForResult( |
| nested_dir_ref.Rename(target_nested_dir_ref, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_ERROR_FAILED, callback.result()); |
| |
| // Rename aborted. |
| // TODO(viettrungluu): Figure out what we want to do if the target file |
| // resource is destroyed before completion. |
| int32_t rv = PP_ERROR_FAILED; |
| pp::FileRef target_file_ref_abort(file_system, |
| "/target_file_rename_abort"); |
| { |
| pp::FileRef file_ref_abort(file_system, "/file_rename_abort"); |
| pp::FileIO file_io_abort(instance_); |
| callback.WaitForResult( |
| file_io_abort.Open(file_ref_abort, PP_FILEOPENFLAG_CREATE, |
| callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| rv = file_ref_abort.Rename(target_file_ref_abort, callback.GetCallback()); |
| } |
| callback.WaitForAbortResult(rv); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| |
| PASS(); |
| } |
| |
| std::string TestFileRef::TestQuery() { |
| TestCompletionCallback callback(instance_->pp_instance(), callback_type()); |
| |
| pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); |
| callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| pp::FileRef file_ref(file_system, "/file"); |
| pp::FileIO file_io(instance_); |
| callback.WaitForResult(file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, |
| callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| // We touch the file so we can easily check access and modified time. |
| callback.WaitForResult(file_io.Touch(0, 0, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| TestCompletionCallbackWithOutput<PP_FileInfo> out_callback( |
| instance_->pp_instance(), callback_type()); |
| out_callback.WaitForResult(file_ref.Query(out_callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(out_callback); |
| ASSERT_EQ(PP_OK, out_callback.result()); |
| |
| PP_FileInfo info = out_callback.output(); |
| ASSERT_EQ(0, info.size); |
| ASSERT_EQ(PP_FILETYPE_REGULAR, info.type); |
| ASSERT_EQ(PP_FILESYSTEMTYPE_LOCALTEMPORARY, info.system_type); |
| ASSERT_DOUBLE_EQ(0.0, info.last_access_time); |
| ASSERT_DOUBLE_EQ(0.0, info.last_modified_time); |
| |
| // Query a file ref on an external filesystem. |
| pp::FileRef file_ref_ext; |
| std::string result = MakeExternalFileRef(&file_ref_ext); |
| if (!result.empty()) |
| return result; |
| out_callback.WaitForResult(file_ref_ext.Query(out_callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(out_callback); |
| if (out_callback.result() != PP_OK) |
| return ReportError("Query() result", out_callback.result()); |
| ASSERT_EQ(PP_OK, out_callback.result()); |
| |
| info = out_callback.output(); |
| ASSERT_EQ(PP_FILETYPE_REGULAR, info.type); |
| ASSERT_EQ(PP_FILESYSTEMTYPE_EXTERNAL, info.system_type); |
| |
| // We can't touch the file, so just sanity check the times. |
| ASSERT_TRUE(info.creation_time >= 0.0); |
| ASSERT_TRUE(info.last_modified_time >= 0.0); |
| ASSERT_TRUE(info.last_access_time >= 0.0); |
| |
| // Query a file ref for a file that doesn't exist. |
| pp::FileRef missing_file_ref(file_system, "/missing_file"); |
| out_callback.WaitForResult(missing_file_ref.Query( |
| out_callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(out_callback); |
| ASSERT_EQ(PP_ERROR_FILENOTFOUND, out_callback.result()); |
| |
| PASS(); |
| } |
| |
| std::string TestFileRef::TestFileNameEscaping() { |
| TestCompletionCallback callback(instance_->pp_instance(), callback_type()); |
| pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); |
| callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| std::string test_dir_path = "/dir_for_escaping_test"; |
| // Create a directory in which to test. |
| pp::FileRef test_dir_ref(file_system, test_dir_path.c_str()); |
| callback.WaitForResult(test_dir_ref.MakeDirectory( |
| PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| // Create the file with the terrible name. |
| std::string full_file_path = test_dir_path + "/" + kTerribleName; |
| pp::FileRef file_ref(file_system, full_file_path.c_str()); |
| pp::FileIO file_io(instance_); |
| callback.WaitForResult( |
| file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| // FileRef::ReadDirectoryEntries only works out-of-process. |
| if (testing_interface_->IsOutOfProcess()) { |
| TestCompletionCallbackWithOutput<DirEntries> |
| output_callback(instance_->pp_instance(), callback_type()); |
| |
| output_callback.WaitForResult( |
| test_dir_ref.ReadDirectoryEntries(output_callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(output_callback); |
| ASSERT_EQ(PP_OK, output_callback.result()); |
| |
| DirEntries entries = output_callback.output(); |
| ASSERT_EQ(1, entries.size()); |
| ASSERT_EQ(kTerribleName, entries.front().file_ref().GetName().AsString()); |
| } |
| |
| PASS(); |
| } |
| |
| std::string TestFileRef::TestReadDirectoryEntries() { |
| TestCompletionCallback callback(instance_->pp_instance(), callback_type()); |
| pp::FileSystem file_system( |
| instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); |
| callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| // Setup testing directories and files. |
| const char* test_dir_name = "/test_get_next_file"; |
| const char* file_prefix = "file_"; |
| const char* dir_prefix = "dir_"; |
| |
| pp::FileRef test_dir(file_system, test_dir_name); |
| int32_t rv = DeleteDirectoryRecursively(&test_dir); |
| ASSERT_TRUE(rv == PP_OK || rv == PP_ERROR_FILENOTFOUND); |
| |
| callback.WaitForResult(test_dir.MakeDirectory( |
| PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| static const int kNumFiles = 3; |
| std::set<std::string> expected_file_names; |
| for (int i = 1; i <= kNumFiles; ++i) { |
| std::ostringstream buffer; |
| buffer << test_dir_name << '/' << file_prefix << i; |
| pp::FileRef file_ref(file_system, buffer.str().c_str()); |
| |
| pp::FileIO file_io(instance_); |
| callback.WaitForResult( |
| file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| expected_file_names.insert(buffer.str()); |
| } |
| |
| static const int kNumDirectories = 3; |
| std::set<std::string> expected_dir_names; |
| for (int i = 1; i <= kNumDirectories; ++i) { |
| std::ostringstream buffer; |
| buffer << test_dir_name << '/' << dir_prefix << i; |
| pp::FileRef file_ref(file_system, buffer.str().c_str()); |
| |
| callback.WaitForResult(file_ref.MakeDirectory( |
| PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(callback); |
| ASSERT_EQ(PP_OK, callback.result()); |
| |
| expected_dir_names.insert(buffer.str()); |
| } |
| |
| // Test that |ReadDirectoryEntries()| is able to fetch all |
| // directories and files that we created. |
| { |
| TestCompletionCallbackWithOutput<DirEntries> output_callback( |
| instance_->pp_instance(), callback_type()); |
| |
| output_callback.WaitForResult( |
| test_dir.ReadDirectoryEntries(output_callback.GetCallback())); |
| CHECK_CALLBACK_BEHAVIOR(output_callback); |
| ASSERT_EQ(PP_OK, output_callback.result()); |
| |
| DirEntries entries = output_callback.output(); |
| size_t sum = expected_file_names.size() + expected_dir_names.size(); |
| ASSERT_EQ(sum, entries.size()); |
| |
| for (DirEntries::const_iterator it = entries.begin(); |
| it != entries.end(); ++it) { |
| pp::FileRef file_ref = it->file_ref(); |
| std::string file_path = file_ref.GetPath().AsString(); |
| std::set<std::string>::iterator found = |
| expected_file_names.find(file_path); |
| if (found != expected_file_names.end()) { |
| if (it->file_type() != PP_FILETYPE_REGULAR) |
| return file_path + " should have been a regular file."; |
| expected_file_names.erase(found); |
| } else { |
| found = expected_dir_names.find(file_path); |
| if (found == expected_dir_names.end()) |
| return "Unexpected file path: " + file_path; |
| if (it->file_type() != PP_FILETYPE_DIRECTORY) |
| return file_path + " should have been a directory."; |
| expected_dir_names.erase(found); |
| } |
| } |
| ASSERT_TRUE(expected_file_names.empty()); |
| ASSERT_TRUE(expected_dir_names.empty()); |
| } |
| |
| // Test cancellation of asynchronous |ReadDirectoryEntries()|. |
| TestCompletionCallbackWithOutput<DirEntries> output_callback( |
| instance_->pp_instance(), callback_type()); |
| { |
| rv = pp::FileRef(file_system, test_dir_name) |
| .ReadDirectoryEntries(output_callback.GetCallback()); |
| } |
| output_callback.WaitForAbortResult(rv); |
| CHECK_CALLBACK_BEHAVIOR(output_callback); |
| |
| |
| PASS(); |
| } |