// 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 #include #include "ppapi/c/pp_errors.h" #include "ppapi/c/ppb_file_io.h" #include "ppapi/c/dev/ppb_testing_dev.h" #include "ppapi/cpp/dev/directory_entry_dev.h" #include "ppapi/cpp/dev/directory_reader_dev.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?"; 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(); } void TestFileRef::RunTests(const std::string& filter) { RUN_TEST_FORCEASYNC_AND_NOT(Create, filter); RUN_TEST_FORCEASYNC_AND_NOT(GetFileSystemType, filter); RUN_TEST_FORCEASYNC_AND_NOT(GetName, filter); RUN_TEST_FORCEASYNC_AND_NOT(GetPath, filter); RUN_TEST_FORCEASYNC_AND_NOT(GetParent, filter); RUN_TEST_FORCEASYNC_AND_NOT(MakeDirectory, filter); RUN_TEST_FORCEASYNC_AND_NOT(QueryAndTouchFile, filter); RUN_TEST_FORCEASYNC_AND_NOT(DeleteFileAndDirectory, filter); RUN_TEST_FORCEASYNC_AND_NOT(RenameFileAndDirectory, filter); RUN_CALLBACK_TEST(TestFileRef, Query, filter); RUN_TEST_FORCEASYNC_AND_NOT(FileNameEscaping, filter); } std::string TestFileRef::TestCreate() { std::vector 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(), force_async_); pp::URLLoader loader(instance_); int32_t rv = loader.Open(request, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("URLLoader::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return "URLLoader::Open() failed."; pp::URLResponseInfo response_info(loader.GetResponseInfo()); if (response_info.is_null()) return "URLLoader::GetResponseInfo returned null"; int32_t status_code = response_info.GetStatusCode(); if (status_code != 200) return "Unexpected HTTP status code"; pp::FileRef file_ref_ext(response_info.GetBodyAsFileRef()); name = file_ref_ext.GetName().AsString(); if (name == "") return ReportMismatch("FileRef::GetName", name, ""); 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); std::string path = file_ref_pers.GetPath().AsString(); if (path != kPersFilePath) return ReportMismatch("FileRef::GetPath", path, kPersFilePath); pp::FileRef file_ref_temp(file_system_temp, kTempFilePath); path = file_ref_temp.GetPath().AsString(); if (path != kTempFilePath) return ReportMismatch("FileRef::GetPath", path, kTempFilePath); pp::URLRequestInfo request(instance_); request.SetURL("test_url_loader_data/hello.txt"); request.SetStreamToFile(true); TestCompletionCallback callback(instance_->pp_instance(), force_async_); pp::URLLoader loader(instance_); int32_t rv = loader.Open(request, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("URLLoader::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return "URLLoader::Open() failed."; pp::URLResponseInfo response_info(loader.GetResponseInfo()); if (response_info.is_null()) return "URLLoader::GetResponseInfo returned null"; int32_t status_code = response_info.GetStatusCode(); if (status_code != 200) return "Unexpected HTTP status code"; pp::FileRef file_ref_ext(response_info.GetBodyAsFileRef()); if (!file_ref_ext.GetPath().is_undefined()) return "The path of an external FileRef should be void."; 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); std::string parent_path = file_ref_pers.GetParent().GetPath().AsString(); if (parent_path != kParentPath) return ReportMismatch("FileRef::GetParent", parent_path, kParentPath); pp::FileRef file_ref_temp(file_system_temp, kTempFilePath); parent_path = file_ref_temp.GetParent().GetPath().AsString(); if (parent_path != kParentPath) return ReportMismatch("FileRef::GetParent", parent_path, kParentPath); // Test the "/" case. pp::FileRef file_ref_slash(file_system_temp, "/"); parent_path = file_ref_slash.GetParent().GetPath().AsString(); if (parent_path != "/") return ReportMismatch("FileRef::GetParent", parent_path, "/"); // Test the "/foo" case (the parent is "/"). pp::FileRef file_ref_with_root_parent(file_system_temp, "/foo"); parent_path = file_ref_with_root_parent.GetParent().GetPath().AsString(); if (parent_path != "/") return ReportMismatch("FileRef::GetParent", parent_path, "/"); pp::URLRequestInfo request(instance_); request.SetURL("test_url_loader_data/hello.txt"); request.SetStreamToFile(true); TestCompletionCallback callback(instance_->pp_instance(), force_async_); pp::URLLoader loader(instance_); int32_t rv = loader.Open(request, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("URLLoader::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return "URLLoader::Open() failed."; pp::URLResponseInfo response_info(loader.GetResponseInfo()); if (response_info.is_null()) return "URLLoader::GetResponseInfo returned null"; int32_t status_code = response_info.GetStatusCode(); if (status_code != 200) return "Unexpected HTTP status code"; pp::FileRef file_ref_ext(response_info.GetBodyAsFileRef()); if (!file_ref_ext.GetParent().is_null()) return "The parent of an external FileRef should be null."; PASS(); } std::string TestFileRef::TestMakeDirectory() { TestCompletionCallback callback(instance_->pp_instance(), force_async_); // Open. pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); int32_t rv = file_system.Open(1024, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::Open", rv); // MakeDirectory. pp::FileRef dir_ref(file_system, "/test_dir_make_directory"); rv = dir_ref.MakeDirectory(callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::MakeDirectory force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::MakeDirectory", rv); // MakeDirectory aborted. callback.reset_run_count(); rv = pp::FileRef(file_system, "/test_dir_make_abort") .MakeDirectory(callback.GetCallback()); if (callback.run_count() > 0) return "FileSystem::MakeDirectory ran callback synchronously."; if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::MakeDirectory force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) { rv = callback.WaitForResult(); if (rv != PP_ERROR_ABORTED) return "FileSystem::MakeDirectory not aborted."; } else if (rv != PP_OK) { return ReportError("FileSystem::MakeDirectory", rv); } // MakeDirectoryIncludingAncestors. dir_ref = pp::FileRef(file_system, "/dir_make_dir_1/dir_make_dir_2"); rv = dir_ref.MakeDirectoryIncludingAncestors(callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::MakeDirectory force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::MakeDirectoryIncludingAncestors", rv); // MakeDirectoryIncludingAncestors aborted. callback.reset_run_count(); rv = pp::FileRef(file_system, "/dir_make_abort_1/dir_make_abort_2") .MakeDirectoryIncludingAncestors(callback.GetCallback()); if (callback.run_count() > 0) { return "FileSystem::MakeDirectoryIncludingAncestors " "ran callback synchronously."; } if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError( "FileSystem::MakeDirectoryIncludingAncestors force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) { rv = callback.WaitForResult(); if (rv != PP_ERROR_ABORTED) return "FileSystem::MakeDirectoryIncludingAncestors not aborted."; } else if (rv != PP_OK) { return ReportError("FileSystem::MakeDirectoryIncludingAncestors", rv); } // MakeDirectory with nested path. dir_ref = pp::FileRef(file_system, "/dir_make_dir_3/dir_make_dir_4"); rv = dir_ref.MakeDirectory(callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::MakeDirectory force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv == PP_OK) { return "Calling FileSystem::MakeDirectory() with a nested directory path " "should have failed."; } PASS(); } std::string TestFileRef::TestQueryAndTouchFile() { TestCompletionCallback callback(instance_->pp_instance(), force_async_); pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); int32_t rv = file_system.Open(1024, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::Open", rv); pp::FileRef file_ref(file_system, "/file_touch"); pp::FileIO file_io(instance_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_TRUNCATE | PP_FILEOPENFLAG_WRITE, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); // Write some data to have a non-zero file size. rv = file_io.Write(0, "test", 4, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Write force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != 4) return ReportError("FileIO::Write", rv); // Touch. // last_access_time's granularity is 1 day // last_modified_time's granularity is 2 seconds const PP_Time last_access_time = 123 * 24 * 3600.0; const PP_Time last_modified_time = 246.0; rv = file_ref.Touch(last_access_time, last_modified_time, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Touch force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::Touch", rv); // Touch aborted. callback.reset_run_count(); rv = pp::FileRef(file_system, "/file_touch_abort") .Touch(last_access_time, last_modified_time, callback.GetCallback()); if (callback.run_count() > 0) return "FileSystem::Touch ran callback synchronously."; if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Touch force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) { rv = callback.WaitForResult(); if (rv != PP_ERROR_ABORTED) return "FileSystem::Touch not aborted."; } else if (rv != PP_OK) { return ReportError("FileSystem::Touch", rv); } // Query. PP_FileInfo info; rv = file_io.Query(&info, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Query force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::Query", rv); if ((info.size != 4) || (info.type != PP_FILETYPE_REGULAR) || (info.system_type != PP_FILESYSTEMTYPE_LOCALTEMPORARY) || (info.last_access_time != last_access_time) || (info.last_modified_time != last_modified_time)) return "FileSystem::Query() has returned bad data."; // Cancellation test. // TODO(viettrungluu): this test causes a bunch of LOG(WARNING)s; investigate. callback.reset_run_count(); // TODO(viettrungluu): check |info| for late writes. rv = pp::FileRef(file_system, "/file_touch").Touch( last_access_time, last_modified_time, callback.GetCallback()); if (callback.run_count() > 0) return "FileSystem::Touch ran callback synchronously."; if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Touch force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) { rv = callback.WaitForResult(); if (rv != PP_ERROR_ABORTED) return "FileSystem::Touch not aborted."; } else if (rv != PP_OK) { return ReportError("FileSystem::Touch", rv); } PASS(); } std::string TestFileRef::TestDeleteFileAndDirectory() { TestCompletionCallback callback(instance_->pp_instance(), force_async_); pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); int32_t rv = file_system.Open(1024, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::Open", rv); pp::FileRef file_ref(file_system, "/file_delete"); pp::FileIO file_io(instance_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); rv = file_ref.Delete(callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileRef::Delete force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileRef::Delete", rv); pp::FileRef dir_ref(file_system, "/dir_delete"); rv = dir_ref.MakeDirectory(callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileRef::MakeDirectory force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileRef::MakeDirectory", rv); rv = dir_ref.Delete(callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileRef::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileRef::Delete", rv); pp::FileRef nested_dir_ref(file_system, "/dir_delete_1/dir_delete_2"); rv = nested_dir_ref.MakeDirectoryIncludingAncestors(callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileRef::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileRef::MakeDirectoryIncludingAncestors", rv); // Hang on to a ref to the parent; otherwise the callback will be aborted. pp::FileRef parent_dir_ref = nested_dir_ref.GetParent(); rv = parent_dir_ref.Delete(callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileRef::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_ERROR_FAILED) return ReportError("FileRef::Delete", rv); pp::FileRef nonexistent_file_ref(file_system, "/nonexistent_file_delete"); rv = nonexistent_file_ref.Delete(callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileRef::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_ERROR_FILENOTFOUND) return ReportError("FileRef::Delete", rv); // Delete aborted. { pp::FileRef file_ref_abort(file_system, "/file_delete_abort"); pp::FileIO file_io_abort(instance_); rv = file_io_abort.Open(file_ref_abort, PP_FILEOPENFLAG_CREATE, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); callback.reset_run_count(); rv = file_ref_abort.Delete(callback.GetCallback()); } if (callback.run_count() > 0) return "FileRef::Delete ran callback synchronously."; if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileRef::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) { rv = callback.WaitForResult(); if (rv != PP_ERROR_ABORTED) return "FileRef::Delete not aborted."; } else if (rv != PP_OK) { return ReportError("FileRef::Delete", rv); } PASS(); } std::string TestFileRef::TestRenameFileAndDirectory() { TestCompletionCallback callback(instance_->pp_instance(), force_async_); pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); int32_t rv = file_system.Open(1024, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::Open", rv); pp::FileRef file_ref(file_system, "/file_rename"); pp::FileIO file_io(instance_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); pp::FileRef target_file_ref(file_system, "/target_file_rename"); rv = file_ref.Rename(target_file_ref, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileRef::Rename force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileRef::Rename", rv); pp::FileRef dir_ref(file_system, "/dir_rename"); rv = dir_ref.MakeDirectory(callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileRef::MakeDirectory force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileRef::MakeDirectory", rv); pp::FileRef target_dir_ref(file_system, "/target_dir_rename"); rv = dir_ref.Rename(target_dir_ref, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileRef::Rename force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileRef::Rename", rv); pp::FileRef nested_dir_ref(file_system, "/dir_rename_1/dir_rename_2"); rv = nested_dir_ref.MakeDirectoryIncludingAncestors(callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileRef::MakeDirectory force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileRef::MakeDirectoryIncludingAncestors", rv); pp::FileRef target_nested_dir_ref(file_system, "/dir_rename_1"); rv = nested_dir_ref.Rename(target_nested_dir_ref, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileRef::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_ERROR_FAILED) return ReportError("FileRef::Rename", rv); // Rename aborted. // TODO(viettrungluu): Figure out what we want to do if the target file // resource is destroyed before completion. 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_); rv = file_io_abort.Open(file_ref_abort, PP_FILEOPENFLAG_CREATE, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); callback.reset_run_count(); rv = file_ref_abort.Rename(target_file_ref_abort, callback.GetCallback()); } if (callback.run_count() > 0) return "FileSystem::Rename ran callback synchronously."; if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Rename force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) { rv = callback.WaitForResult(); if (rv != PP_ERROR_ABORTED) return "FileSystem::Rename not aborted."; } else if (rv != PP_OK) { return ReportError("FileSystem::Rename", rv); } 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 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(), force_async_); pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); int32_t rv = file_system.Open(1024, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::Open", rv); 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()); rv = test_dir_ref.MakeDirectory(callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileRef::MakeDirectory force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileRef::MakeDirectory", rv); // 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_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback()); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); // DirectoryReader only works out-of-process. if (testing_interface_->IsOutOfProcess()) { TestCompletionCallbackWithOutput< std::vector > output_callback(instance_->pp_instance(), force_async_); pp::DirectoryReader_Dev directory_reader(test_dir_ref); rv = directory_reader.ReadEntries(output_callback.GetCallback()); if (rv == PP_OK_COMPLETIONPENDING) rv = output_callback.WaitForResult(); if (rv != PP_OK && rv != PP_ERROR_FILENOTFOUND) return ReportError("DirectoryEntry_Dev::ReadEntries", rv); std::vector entries = output_callback.output(); if (entries.empty()) return "Entry was not found."; if (entries.size() != 1) return "Directory had too many entries."; if (entries.front().file_ref().GetName().AsString() != kTerribleName) return "Entry name did not match."; } PASS(); }