| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/file_manager/fileapi_util.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/files/file_error_or.h" |
| #include "base/functional/bind.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/bind.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "chrome/browser/ash/file_system_provider/fake_extension_provider.h" |
| #include "chrome/browser/ash/file_system_provider/fake_provided_file_system.h" |
| #include "chrome/browser/ash/file_system_provider/service.h" |
| #include "chrome/browser/ash/file_system_provider/service_factory.h" |
| #include "chrome/browser/ash/fileapi/file_system_backend.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "chrome/test/base/testing_profile_manager.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/public/test/test_utils.h" |
| #include "storage/browser/file_system/external_mount_points.h" |
| #include "storage/browser/file_system/file_system_url.h" |
| #include "storage/browser/test/async_file_test_helper.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/storage_key/storage_key.h" |
| #include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h" |
| #include "ui/shell_dialogs/selected_file_info.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| |
| namespace file_manager { |
| namespace util { |
| namespace { |
| |
| // Helper class that sets up a temporary file system. |
| class TempFileSystem { |
| public: |
| TempFileSystem(Profile* profile, const GURL& appURL) |
| : name_(base::UnguessableToken::Create().ToString()), |
| appURL_(appURL), |
| origin_(url::Origin::Create(appURL)), |
| file_system_context_( |
| GetFileSystemContextForSourceURL(profile, appURL)) {} |
| |
| ~TempFileSystem() { |
| storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem(name_); |
| } |
| |
| // Finishes setting up the temporary file system. Must be called before use. |
| bool SetUp() { |
| if (!temp_dir_.CreateUniqueTempDir()) { |
| return false; |
| } |
| if (!storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem( |
| name_, storage::kFileSystemTypeLocal, |
| storage::FileSystemMountOption(), temp_dir_.GetPath())) { |
| return false; |
| } |
| |
| // Grant the test extension the ability to access the just created |
| // file system. |
| ash::FileSystemBackend::Get(*file_system_context_) |
| ->GrantFileAccessToOrigin(origin_, base::FilePath(name_)); |
| return true; |
| } |
| |
| bool TearDown() { |
| return storage::ExternalMountPoints::GetSystemInstance()->RevokeFileSystem( |
| name_); |
| } |
| |
| // For the given FileSystemURL creates a file. |
| base::File::Error CreateFile(const storage::FileSystemURL& url) { |
| return storage::AsyncFileTestHelper::CreateFile(file_system_context_, url); |
| } |
| |
| // For the given FileSystemURL creates a directory. |
| base::File::Error CreateDirectory(const storage::FileSystemURL& url) { |
| return storage::AsyncFileTestHelper::CreateDirectory(file_system_context_, |
| url); |
| } |
| |
| // Creates an external file system URL for the given path. |
| storage::FileSystemURL CreateFileSystemURL(const std::string& path) { |
| return file_system_context_->CreateCrackedFileSystemURL( |
| blink::StorageKey::CreateFirstParty(origin_), |
| storage::kFileSystemTypeExternal, |
| base::FilePath().Append(name_).Append( |
| base::FilePath::FromUTF8Unsafe(path))); |
| } |
| |
| private: |
| const std::string name_; |
| const GURL appURL_; |
| const url::Origin origin_; |
| const raw_ptr<storage::FileSystemContext> file_system_context_; |
| base::ScopedTempDir temp_dir_; |
| }; |
| |
| class FileManagerFileAPIUtilTest : public ::testing::Test { |
| public: |
| // Carries information on how to create a FileSystemURL for a given file name. |
| // For !valid orders we create a test URL. Otherwise, we use temp file system. |
| struct FileSystemURLOrder { |
| std::string file_name; |
| bool valid; |
| }; |
| |
| void SetUp() override { |
| testing::Test::SetUp(); |
| profile_manager_ = std::make_unique<TestingProfileManager>( |
| TestingBrowserProcess::GetGlobal()); |
| ASSERT_TRUE(profile_manager_->SetUp()); |
| profile_ = profile_manager_->CreateTestingProfile("testing_profile"); |
| } |
| |
| void TearDown() override { |
| profile_manager_->DeleteAllTestingProfiles(); |
| profile_ = nullptr; |
| profile_manager_.reset(); |
| } |
| |
| TestingProfile* GetProfile() { return profile_; } |
| |
| protected: |
| // Checks if the conversion of FileDefinition to EntryDefinition works |
| // correctly for the given |appURLStr| and a set of |orders|. If the |
| // order indicates that the file should not be created, we expect the |
| // conversion to return base::File::FILE_ERROR_NOT_FOUND error. Otherwise, |
| // we expect base::File::FILE_OK status. |
| void CheckConvertFileDefinitionListToEntryDefinitionList( |
| const std::string& appURLStr, |
| const std::vector<FileSystemURLOrder>& orders) { |
| GURL appURL(appURLStr); |
| ASSERT_TRUE(appURL.is_valid()); |
| auto temp_file_system = std::make_unique<TempFileSystem>(profile_, appURL); |
| ASSERT_TRUE(temp_file_system->SetUp()); |
| |
| std::vector<base::File::Error> errors; |
| std::vector<FileDefinition> file_definitions; |
| for (const FileSystemURLOrder& order : orders) { |
| storage::FileSystemURL fs_url; |
| if (order.valid) { |
| fs_url = temp_file_system->CreateFileSystemURL(order.file_name); |
| errors.push_back(base::File::FILE_OK); |
| } else { |
| fs_url = storage::FileSystemURL::CreateForTest( |
| blink::StorageKey::CreateFirstParty(url::Origin::Create(appURL)), |
| storage::kFileSystemTypeExternal, base::FilePath(order.file_name)); |
| errors.push_back(base::File::FILE_ERROR_NOT_FOUND); |
| } |
| file_definitions.push_back({.virtual_path = fs_url.virtual_path()}); |
| } |
| |
| base::RunLoop run_loop; |
| EntryDefinitionListCallback callback = base::BindOnce( |
| [](std::unique_ptr<TempFileSystem> temp_file_system, |
| std::vector<base::File::Error> errors, |
| base::OnceClosure quit_closure, |
| std::unique_ptr<EntryDefinitionList> entries) { |
| ASSERT_EQ(errors.size(), entries->size()); |
| for (size_t i = 0; i < errors.size(); ++i) { |
| const EntryDefinition& entry_def = (*entries)[i]; |
| EXPECT_EQ(errors[i], entry_def.error) |
| << "for " << entry_def.full_path << " at " << i; |
| } |
| |
| EXPECT_TRUE(temp_file_system->TearDown()); |
| std::move(quit_closure).Run(); |
| }, |
| std::move(temp_file_system), std::move(errors), run_loop.QuitClosure()); |
| ConvertFileDefinitionListToEntryDefinitionList( |
| GetFileSystemContextForSourceURL(profile_, appURL), |
| url::Origin::Create(appURL), file_definitions, std::move(callback)); |
| run_loop.Run(); |
| } |
| |
| void TestGenerateUnusedFilename(std::vector<std::string> existing_files, |
| std::string target_filename, |
| base::FileErrorOr<std::string> expected); |
| |
| const std::string file_system_id_ = "test-filesystem"; |
| |
| private: |
| base::test::ScopedFeatureList feature_list_; |
| content::BrowserTaskEnvironment task_environment_; |
| std::unique_ptr<TestingProfileManager> profile_manager_; |
| raw_ptr<TestingProfile, DanglingUntriaged> profile_; |
| }; |
| |
| // Passes the |result| to the |output| pointer. |
| void PassFileChooserFileInfoList(FileChooserFileInfoList* output, |
| FileChooserFileInfoList result) { |
| for (const auto& file : result) { |
| output->push_back(file->Clone()); |
| } |
| } |
| |
| TEST_F(FileManagerFileAPIUtilTest, |
| ConvertSelectedFileInfoListToFileChooserFileInfoList) { |
| Profile* const profile = GetProfile(); |
| const std::string extension_id = "abc"; |
| auto fake_provider = |
| ash::file_system_provider::FakeExtensionProvider::Create(extension_id); |
| const auto kProviderId = fake_provider->GetId(); |
| auto* service = ash::file_system_provider::Service::Get(profile); |
| service->RegisterProvider(std::move(fake_provider)); |
| service->MountFileSystem( |
| kProviderId, ash::file_system_provider::MountOptions(file_system_id_, |
| "Test FileSystem")); |
| |
| // Obtain the file system context. |
| content::StoragePartition* const partition = |
| profile->GetStoragePartitionForUrl(GURL("http://example.com")); |
| ASSERT_TRUE(partition); |
| storage::FileSystemContext* const context = partition->GetFileSystemContext(); |
| ASSERT_TRUE(context); |
| |
| // Prepare the test input. |
| SelectedFileInfoList selected_info_list; |
| |
| // Native file. |
| { |
| ui::SelectedFileInfo info; |
| info.file_path = base::FilePath(FILE_PATH_LITERAL("/native/File 1.txt")); |
| info.local_path = base::FilePath(FILE_PATH_LITERAL("/native/File 1.txt")); |
| info.display_name = "display_name"; |
| selected_info_list.push_back(info); |
| } |
| |
| const std::string path = FILE_PATH_LITERAL(base::StrCat( |
| {"/provided/", extension_id, ":", file_system_id_, ":/hello.txt"})); |
| // Non-native file with cache. |
| { |
| ui::SelectedFileInfo info; |
| info.file_path = base::FilePath(path); |
| info.local_path = base::FilePath(FILE_PATH_LITERAL("/native/cache/xxx")); |
| info.display_name = "display_name"; |
| selected_info_list.push_back(info); |
| } |
| |
| // Non-native file without. |
| { |
| ui::SelectedFileInfo info; |
| info.file_path = base::FilePath(path); |
| selected_info_list.push_back(info); |
| } |
| |
| // Run the test target. |
| FileChooserFileInfoList result; |
| ConvertSelectedFileInfoListToFileChooserFileInfoList( |
| context, url::Origin::Create(GURL("http://example.com")), |
| selected_info_list, |
| base::BindOnce(&PassFileChooserFileInfoList, &result)); |
| content::RunAllTasksUntilIdle(); |
| |
| // Check the result. |
| ASSERT_EQ(3u, result.size()); |
| |
| EXPECT_TRUE(result[0]->is_native_file()); |
| EXPECT_EQ(FILE_PATH_LITERAL("/native/File 1.txt"), |
| result[0]->get_native_file()->file_path.value()); |
| EXPECT_EQ(u"display_name", result[0]->get_native_file()->display_name); |
| |
| EXPECT_TRUE(result[1]->is_native_file()); |
| EXPECT_EQ(FILE_PATH_LITERAL("/native/cache/xxx"), |
| result[1]->get_native_file()->file_path.value()); |
| EXPECT_EQ(u"display_name", result[1]->get_native_file()->display_name); |
| |
| EXPECT_TRUE(result[2]->is_file_system()); |
| EXPECT_TRUE(result[2]->get_file_system()->url.is_valid()); |
| const storage::FileSystemURL url = |
| context->CrackURLInFirstPartyContext(result[2]->get_file_system()->url); |
| EXPECT_EQ(GURL("http://example.com"), url.origin().GetURL()); |
| EXPECT_EQ(storage::kFileSystemTypeIsolated, url.mount_type()); |
| EXPECT_EQ(storage::kFileSystemTypeProvided, url.type()); |
| EXPECT_EQ(55u, result[2]->get_file_system()->length); |
| } |
| |
| TEST_F(FileManagerFileAPIUtilTest, |
| ConvertFileDefinitionListToEntryDefinitionListExtension) { |
| std::vector<FileSystemURLOrder> orders = { |
| {.file_name = "x.txt", .valid = true}, |
| {.file_name = "no-such-file.txt", .valid = false}, |
| {.file_name = "z.txt", .valid = true}, |
| }; |
| CheckConvertFileDefinitionListToEntryDefinitionList("chrome-extension://abc", |
| orders); |
| CheckConvertFileDefinitionListToEntryDefinitionList("chrome-extension://abc/", |
| orders); |
| CheckConvertFileDefinitionListToEntryDefinitionList( |
| "chrome-extension://abc/efg", orders); |
| } |
| |
| TEST_F(FileManagerFileAPIUtilTest, |
| ConvertFileDefinitionListToEntryDefinitionListApp) { |
| std::vector<FileSystemURLOrder> orders = { |
| {.file_name = "a.txt", .valid = false}, |
| {.file_name = "b.txt", .valid = false}, |
| {.file_name = "i-am-a-file.txt", .valid = true}, |
| }; |
| CheckConvertFileDefinitionListToEntryDefinitionList("chrome://file-manager", |
| orders); |
| CheckConvertFileDefinitionListToEntryDefinitionList("chrome://file-manager/", |
| orders); |
| CheckConvertFileDefinitionListToEntryDefinitionList( |
| "chrome://file-manager/abc", orders); |
| } |
| |
| TEST_F(FileManagerFileAPIUtilTest, |
| ConvertFileDefinitionListToEntryDefinitionNullContext) { |
| Profile* const profile = GetProfile(); |
| const GURL appURL("chrome-extension://abc/"); |
| auto temp_file_system = std::make_unique<TempFileSystem>(profile, appURL); |
| ASSERT_TRUE(temp_file_system->SetUp()); |
| storage::FileSystemURL x_file_url = |
| temp_file_system->CreateFileSystemURL("."); |
| FileDefinition x_fd = {.virtual_path = x_file_url.virtual_path()}; |
| |
| // Check a simple case where the context is already null before we have |
| // a chance to call the conversion function. |
| base::RunLoop run_loop; |
| EntryDefinitionListCallback callback = base::BindOnce( |
| [](std::unique_ptr<TempFileSystem> temp_file_system, |
| base::OnceClosure quit_closure, |
| std::unique_ptr<EntryDefinitionList> entries) { |
| ASSERT_EQ(1u, entries->size()); |
| EXPECT_EQ(base::File::FILE_ERROR_INVALID_OPERATION, |
| entries->at(0).error); |
| EXPECT_TRUE(temp_file_system->TearDown()); |
| std::move(quit_closure).Run(); |
| }, |
| std::move(temp_file_system), run_loop.QuitClosure()); |
| |
| ConvertFileDefinitionListToEntryDefinitionList( |
| nullptr, url::Origin::Create(appURL), {x_fd}, std::move(callback)); |
| run_loop.Run(); |
| } |
| |
| TEST_F(FileManagerFileAPIUtilTest, |
| ConvertFileDefinitionListToEntryDefinitionContextReset) { |
| Profile* const profile = GetProfile(); |
| const GURL appURL("chrome-extension://abc/"); |
| auto temp_file_system = std::make_unique<TempFileSystem>(profile, appURL); |
| ASSERT_TRUE(temp_file_system->SetUp()); |
| storage::FileSystemURL x_file_url = |
| temp_file_system->CreateFileSystemURL("."); |
| FileDefinition x_fd = {.virtual_path = x_file_url.virtual_path()}; |
| scoped_refptr<storage::FileSystemContext> file_system_context = |
| GetFileSystemContextForSourceURL(profile, appURL); |
| |
| base::RunLoop run_loop; |
| EntryDefinitionListCallback callback = base::BindOnce( |
| [](std::unique_ptr<TempFileSystem> temp_file_system, |
| base::OnceClosure quit_closure, |
| std::unique_ptr<EntryDefinitionList> entries) { |
| ASSERT_EQ(1u, entries->size()); |
| EXPECT_EQ(base::File::FILE_OK, entries->at(0).error); |
| EXPECT_TRUE(temp_file_system->TearDown()); |
| std::move(quit_closure).Run(); |
| }, |
| std::move(temp_file_system), run_loop.QuitClosure()); |
| |
| // Check the case where the context is not null, but is reset to null as |
| // soon as function call is completed. Conversion takes place on a |
| // different thread, after the function call returns. However, since |
| // it holds to a copy of a scoped pointer we expect it to succeed. |
| ConvertFileDefinitionListToEntryDefinitionList(file_system_context, |
| url::Origin::Create(appURL), |
| {x_fd}, std::move(callback)); |
| file_system_context.reset(); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(FileManagerFileAPIUtilTest, IsFileManagerURL) { |
| EXPECT_TRUE(IsFileManagerURL(GetFileManagerURL())); |
| EXPECT_TRUE(IsFileManagerURL(GetFileManagerURL().Resolve("/some/path"))); |
| EXPECT_TRUE(IsFileManagerURL( |
| GetFileManagerURL().Resolve("/some/path").Resolve("#anchor"))); |
| EXPECT_TRUE(IsFileManagerURL(GetFileManagerURL() |
| .Resolve("/some/path") |
| .Resolve("#anchor") |
| .Resolve("?a=b"))); |
| EXPECT_FALSE(IsFileManagerURL(GURL("chrome://not-file-manager"))); |
| EXPECT_FALSE(IsFileManagerURL(GURL("chrome://not-file-manager/"))); |
| EXPECT_FALSE(IsFileManagerURL( |
| GURL("chrome-extension://iamnotafilemanagerextensionid"))); |
| EXPECT_FALSE(IsFileManagerURL( |
| GURL("chrome-extension://iamnotafilemanagerextensionid/"))); |
| } |
| |
| void FileManagerFileAPIUtilTest::TestGenerateUnusedFilename( |
| std::vector<std::string> existing_files, |
| std::string target_filename, |
| base::FileErrorOr<std::string> expected) { |
| const GURL appURL("chrome-extension://abc/"); |
| auto temp_file_system = |
| std::make_unique<TempFileSystem>(GetProfile(), appURL); |
| ASSERT_TRUE(temp_file_system->SetUp()); |
| storage::FileSystemURL root_url = temp_file_system->CreateFileSystemURL(""); |
| scoped_refptr<storage::FileSystemContext> file_system_context = |
| GetFileSystemContextForSourceURL(GetProfile(), appURL); |
| |
| for (const std::string& file : existing_files) { |
| if (file.back() == '/') { |
| temp_file_system->CreateDirectory( |
| temp_file_system->CreateFileSystemURL(file)); |
| } else { |
| temp_file_system->CreateFile(temp_file_system->CreateFileSystemURL(file)); |
| } |
| } |
| |
| base::RunLoop run_loop; |
| GenerateUnusedFilename( |
| root_url, base::FilePath(target_filename), file_system_context, |
| base::BindLambdaForTesting( |
| [&](base::FileErrorOr<storage::FileSystemURL> result) { |
| if (!expected.has_value()) { |
| EXPECT_FALSE(result.has_value()) |
| << "Unexpected result " << result->ToGURL(); |
| EXPECT_EQ(expected.error(), result.error()); |
| } else { |
| EXPECT_TRUE(result.has_value()) |
| << "Unexpected error " << result.error(); |
| EXPECT_EQ(temp_file_system->CreateFileSystemURL(expected.value()) |
| .ToGURL(), |
| result->ToGURL()); |
| } |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| } |
| |
| TEST_F(FileManagerFileAPIUtilTest, GenerateUnusedFilenameBasic) { |
| TestGenerateUnusedFilename({}, "foo.bar", {"foo.bar"}); |
| TestGenerateUnusedFilename({"foo.bar"}, "foo.bar", {"foo (1).bar"}); |
| TestGenerateUnusedFilename({"foo.bar/"}, "foo.bar", {"foo (1).bar"}); |
| TestGenerateUnusedFilename({"foo (1).bar"}, "foo.bar", {"foo.bar"}); |
| TestGenerateUnusedFilename({"foo.bar", "foo (1).bar"}, "foo.bar", |
| {"foo (2).bar"}); |
| TestGenerateUnusedFilename({"foo.bar", "foo (1).bar/"}, "foo.bar", |
| {"foo (2).bar"}); |
| TestGenerateUnusedFilename({"foo.bar", "foo (2).bar"}, "foo.bar", |
| {"foo (1).bar"}); |
| TestGenerateUnusedFilename({"foo.bar/", "foo (1).bar"}, "foo (1).bar", |
| {"foo (2).bar"}); |
| TestGenerateUnusedFilename({"foo (3).bar"}, "foo (3).bar", {"foo (1).bar"}); |
| TestGenerateUnusedFilename({"foo (2).bar"}, "foo (1).bar", {"foo (1).bar"}); |
| TestGenerateUnusedFilename({"foo (2) (1).bar"}, "foo (2) (1).bar", |
| {"foo (2) (2).bar"}); |
| TestGenerateUnusedFilename({"foo (2) (2).bar"}, "foo (2) (2).bar", |
| {"foo (2) (1).bar"}); |
| TestGenerateUnusedFilename({}, " foo.bar", {" foo.bar"}); |
| TestGenerateUnusedFilename({" foo.bar"}, " foo.bar", {" foo (1).bar"}); |
| TestGenerateUnusedFilename({"foo.bar"}, " foo.bar", {" foo.bar"}); |
| } |
| |
| TEST_F(FileManagerFileAPIUtilTest, GenerateUnusedFilenameNewLine) { |
| TestGenerateUnusedFilename({}, "new\nline.bar", {"new\nline.bar"}); |
| TestGenerateUnusedFilename({"new\nline.bar"}, "new\nline.bar", |
| {"new\nline (1).bar"}); |
| TestGenerateUnusedFilename({"new\nline.bar", "new\nline (1).bar"}, |
| "new\nline.bar", {"new\nline (2).bar"}); |
| TestGenerateUnusedFilename({}, "new\nline (\n)", {"new\nline (\n)"}); |
| TestGenerateUnusedFilename({"new\nline (\n)"}, "new\nline (\n)", |
| {"new\nline (\n) (1)"}); |
| } |
| |
| TEST_F(FileManagerFileAPIUtilTest, GenerateUnusedFilenameUnicode) { |
| TestGenerateUnusedFilename({}, "é è ê ô œ.txt€", {"é è ê ô œ.txt€"}); |
| TestGenerateUnusedFilename({"é è ê ô œ.txt€"}, "é è ê ô œ.txt€", |
| {"é è ê ô œ (1).txt€"}); |
| } |
| |
| TEST_F(FileManagerFileAPIUtilTest, GenerateUnusedFilenameNoExtension) { |
| TestGenerateUnusedFilename({}, "no-ext", {"no-ext"}); |
| TestGenerateUnusedFilename({"no-ext"}, "no-ext", {"no-ext (1)"}); |
| TestGenerateUnusedFilename({"no-ext/"}, "no-ext", {"no-ext (1)"}); |
| TestGenerateUnusedFilename({"no-ext (1)"}, "no-ext (1)", {"no-ext (2)"}); |
| |
| TestGenerateUnusedFilename({}, "a", {"a"}); |
| TestGenerateUnusedFilename({"a"}, "a", {"a (1)"}); |
| TestGenerateUnusedFilename({"a/"}, "a", {"a (1)"}); |
| } |
| |
| TEST_F(FileManagerFileAPIUtilTest, GenerateUnusedFilenameDoubleExtension) { |
| TestGenerateUnusedFilename({}, "double.ext.10.13.txt", |
| {"double.ext.10.13.txt"}); |
| TestGenerateUnusedFilename({"double.ext.10.13.txt"}, "double.ext.10.13.txt", |
| {"double.ext.10.13 (1).txt"}); |
| TestGenerateUnusedFilename({"double.ext.10.13.txt/"}, "double.ext.10.13.txt", |
| {"double.ext.10.13 (1).txt"}); |
| |
| TestGenerateUnusedFilename({}, "archive.tar.gz", {"archive.tar.gz"}); |
| TestGenerateUnusedFilename({"archive.tar.gz"}, "archive.tar.gz", |
| {"archive (1).tar.gz"}); |
| } |
| |
| TEST_F(FileManagerFileAPIUtilTest, GenerateUnusedFilenameInvalidFilename) { |
| TestGenerateUnusedFilename( |
| {}, "", base::unexpected(base::File::FILE_ERROR_INVALID_OPERATION)); |
| TestGenerateUnusedFilename( |
| {}, "path/with/slashes", |
| base::unexpected(base::File::FILE_ERROR_INVALID_OPERATION)); |
| } |
| |
| TEST_F(FileManagerFileAPIUtilTest, GenerateUnusedFilenameFileSystemProvider) { |
| Profile* const profile = GetProfile(); |
| const std::string extension_id = "abc"; |
| |
| // Create and mount the FileSystemProvider. |
| auto fake_provider = |
| ash::file_system_provider::FakeExtensionProvider::Create(extension_id); |
| const auto kProviderId = fake_provider->GetId(); |
| auto* service = ash::file_system_provider::Service::Get(profile); |
| service->RegisterProvider(std::move(fake_provider)); |
| const base::File::Error result = service->MountFileSystem( |
| kProviderId, ash::file_system_provider::MountOptions(file_system_id_, |
| "Test FileSystem")); |
| ASSERT_EQ(base::File::FILE_OK, result); |
| |
| auto* provided_file_system = |
| static_cast<ash::file_system_provider::FakeProvidedFileSystem*>( |
| service->GetProvidedFileSystem(kProviderId, file_system_id_)); |
| ASSERT_TRUE(provided_file_system); |
| const base::FilePath mount_point_name = |
| provided_file_system->GetFileSystemInfo().mount_path().BaseName(); |
| |
| const std::string origin = "chrome-extension://abc/"; |
| storage::FileSystemContext* const context = |
| GetFileSystemContextForSourceURL(profile, GURL(origin)); |
| ASSERT_TRUE(context); |
| |
| // Make sure we can access the filesystem from the above origin. |
| ash::FileSystemBackend::Get(*context)->GrantFileAccessToOrigin( |
| url::Origin::Create(GURL(origin)), base::FilePath(mount_point_name)); |
| |
| const storage::ExternalMountPoints* const mount_points = |
| storage::ExternalMountPoints::GetSystemInstance(); |
| auto destination_folder_url = mount_points->CreateCrackedFileSystemURL( |
| blink::StorageKey::CreateFromStringForTesting(origin), |
| storage::kFileSystemTypeExternal, mount_point_name); |
| auto expected_url = mount_points->CreateCrackedFileSystemURL( |
| blink::StorageKey::CreateFromStringForTesting(origin), |
| storage::kFileSystemTypeExternal, |
| mount_point_name.Append("hello (1).txt")); |
| |
| base::RunLoop run_loop; |
| GenerateUnusedFilename( |
| destination_folder_url, |
| base::FilePath(ash::file_system_provider::kFakeFilePath).BaseName(), |
| context, |
| base::BindLambdaForTesting( |
| [&](base::FileErrorOr<storage::FileSystemURL> result) { |
| EXPECT_TRUE(result.has_value()) |
| << "Unexpected error " << result.error(); |
| EXPECT_EQ(expected_url.ToGURL(), result->ToGURL()); |
| run_loop.Quit(); |
| })); |
| run_loop.Run(); |
| } |
| |
| } // namespace |
| } // namespace util |
| } // namespace file_manager |