| // Copyright (c) 2012 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. |
| |
| // File contains browser tests for the fileBrowserHandler api. |
| |
| #include "chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.h" |
| |
| #include <stddef.h> |
| |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/macros.h" |
| #include "base/task_scheduler/post_task.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/values.h" |
| #include "chrome/browser/extensions/extension_apitest.h" |
| #include "chrome/browser/extensions/extension_function_test_utils.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/test/base/in_process_browser_test.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "content/public/browser/browser_context.h" |
| #include "extensions/browser/api_test_utils.h" |
| #include "extensions/browser/extension_function_registry.h" |
| #include "extensions/common/extension.h" |
| #include "extensions/test/result_catcher.h" |
| #include "storage/browser/fileapi/external_mount_points.h" |
| #include "storage/common/fileapi/file_system_types.h" |
| |
| namespace utils = extension_function_test_utils; |
| |
| using content::BrowserContext; |
| using extensions::Extension; |
| |
| namespace { |
| |
| // Data that defines FileSelector behaviour in each test case. |
| struct TestCase { |
| TestCase(const base::FilePath& suggested_name, |
| const std::vector<std::string>& allowed_extensions, |
| bool success, |
| const base::FilePath& selected_path) |
| : suggested_name(suggested_name), |
| allowed_extensions(allowed_extensions), |
| success(success), |
| selected_path(selected_path) { |
| } |
| ~TestCase() {} |
| |
| // Path that we expect to be suggested to the file selector. |
| base::FilePath suggested_name; |
| |
| // Extensions that we expect to be allowed to the file selector. |
| std::vector<std::string> allowed_extensions; |
| |
| // Whether file selector should fail. |
| bool success; |
| // The path file selector should return back to the function. |
| base::FilePath selected_path; |
| }; |
| |
| bool OverrideFunction(const std::string& name, |
| extensions::ExtensionFunctionFactory factory) { |
| return ExtensionFunctionRegistry::GetInstance()->OverrideFunctionForTesting( |
| name, factory); |
| } |
| |
| // Mocks FileSelector used by FileBrowserHandlerInternalSelectFileFunction. |
| // When |SelectFile| is called, it will check that file name suggestion is as |
| // expected, and respond to the extension function with specified selection |
| // results. |
| class MockFileSelector : public file_manager::FileSelector { |
| public: |
| MockFileSelector(const base::FilePath& suggested_name, |
| const std::vector<std::string>& allowed_extensions, |
| bool success, |
| const base::FilePath& selected_path) |
| : suggested_name_(suggested_name), |
| allowed_extensions_(allowed_extensions), |
| success_(success), |
| selected_path_(selected_path) { |
| } |
| ~MockFileSelector() override {} |
| |
| // file_manager::FileSelector implementation. |
| // |browser| is not used. |
| void SelectFile( |
| const base::FilePath& suggested_name, |
| const std::vector<std::string>& allowed_extensions, |
| Browser* browser, |
| FileBrowserHandlerInternalSelectFileFunction* function) override { |
| // Confirm that the function suggested us the right name. |
| EXPECT_EQ(suggested_name_, suggested_name); |
| // Confirm that the function allowed us the right extensions. |
| EXPECT_EQ(allowed_extensions_.size(), allowed_extensions.size()); |
| if (allowed_extensions_.size() == allowed_extensions.size()) { |
| for (size_t i = 0; i < allowed_extensions_.size(); ++i) { |
| EXPECT_EQ(allowed_extensions_[i], allowed_extensions[i]); |
| } |
| } |
| |
| // Send response to the extension function. |
| // The callback will take a reference to the function and keep it alive. |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &FileBrowserHandlerInternalSelectFileFunction::OnFilePathSelected, |
| function, success_, selected_path_)); |
| delete this; |
| } |
| |
| private: |
| // File name that is expected to be suggested by the function. |
| base::FilePath suggested_name_; |
| |
| // Extensions that is expected to be allowed by the function. |
| std::vector<std::string> allowed_extensions_; |
| |
| // Whether the selection should succeed. |
| bool success_; |
| // File path that should be returned to the function. |
| base::FilePath selected_path_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MockFileSelector); |
| }; |
| |
| // Mocks file selector factory for the test. |
| // When |CreateFileSelector| is invoked it will create mock file selector for |
| // the extension function with test parameters from the object ctor. |
| class MockFileSelectorFactory : public file_manager::FileSelectorFactory { |
| public: |
| explicit MockFileSelectorFactory(const TestCase& test_case) |
| : suggested_name_(test_case.suggested_name), |
| allowed_extensions_(test_case.allowed_extensions), |
| success_(test_case.success), |
| selected_path_(test_case.selected_path) { |
| } |
| ~MockFileSelectorFactory() override {} |
| |
| // file_manager::FileSelectorFactory implementation. |
| file_manager::FileSelector* CreateFileSelector() const override { |
| return new MockFileSelector(suggested_name_, |
| allowed_extensions_, |
| success_, |
| selected_path_); |
| } |
| |
| private: |
| // File name that is expected to be suggested by the function. |
| base::FilePath suggested_name_; |
| // Extensions that is expected to be allowed by the function. |
| std::vector<std::string> allowed_extensions_; |
| // Whether the selection should succeed. |
| bool success_; |
| // File path that should be returned to the function. |
| base::FilePath selected_path_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MockFileSelectorFactory); |
| }; |
| |
| // Extension api test for the fileBrowserHandler extension API. |
| class FileBrowserHandlerExtensionTest : public ExtensionApiTest { |
| protected: |
| void SetUp() override { |
| // Create mount point directory that will be used in the test. |
| // Mount point will be called "tmp", and it will be located in a tmp |
| // directory with an unique name. |
| ASSERT_TRUE(scoped_tmp_dir_.CreateUniqueTempDir()); |
| tmp_mount_point_ = scoped_tmp_dir_.GetPath().Append("tmp"); |
| base::CreateDirectory(tmp_mount_point_); |
| |
| ExtensionApiTest::SetUp(); |
| } |
| |
| // Creates new, test mount point. |
| void AddTmpMountPoint(const std::string& extension_id) { |
| BrowserContext::GetMountPoints(browser()->profile()) |
| ->RegisterFileSystem("tmp", |
| storage::kFileSystemTypeNativeLocal, |
| storage::FileSystemMountOption(), |
| tmp_mount_point_); |
| } |
| |
| base::FilePath GetFullPathOnTmpMountPoint( |
| const base::FilePath& relative_path) { |
| return tmp_mount_point_.Append(relative_path); |
| } |
| |
| // Creates a new FileBrowserHandlerInternalSelectFileFunction to be used in |
| // the test. This function will be called from ExtensionFunctinoDispatcher |
| // whenever an extension function for fileBrowserHandlerInternal.selectFile |
| // will be needed. |
| static ExtensionFunction* TestSelectFileFunctionFactory() { |
| EXPECT_TRUE(test_cases_); |
| EXPECT_TRUE(!test_cases_ || current_test_case_ < test_cases_->size()); |
| |
| // If this happens, test failed. But, we still don't want to crash, so |
| // return valid extension function. |
| if (!test_cases_ || current_test_case_ >= test_cases_->size()) |
| return new FileBrowserHandlerInternalSelectFileFunction(); |
| |
| // Create file creator factory for the current test case. |
| MockFileSelectorFactory* mock_factory = |
| new MockFileSelectorFactory(test_cases_->at(current_test_case_)); |
| current_test_case_++; |
| |
| return new FileBrowserHandlerInternalSelectFileFunction( |
| mock_factory, false); |
| } |
| |
| // Sets up test parameters for extension function invocations that will be |
| // made during the test. |
| void SetTestCases(const std::vector<TestCase>* test_cases) { |
| test_cases_ = test_cases; |
| current_test_case_ = 0; |
| } |
| |
| private: |
| // List of test parameters for each extension function invocation that will be |
| // made during a test. |
| // Should be owned by the test code. |
| static const std::vector<TestCase>* test_cases_; |
| static size_t current_test_case_; |
| |
| base::ScopedTempDir scoped_tmp_dir_; |
| // Our test mount point path. |
| base::FilePath tmp_mount_point_; |
| }; |
| |
| const std::vector<TestCase>* FileBrowserHandlerExtensionTest::test_cases_ = |
| NULL; |
| size_t FileBrowserHandlerExtensionTest::current_test_case_ = 0; |
| |
| // End to end test that verifies that fileBrowserHandler.selectFile works as |
| // expected. It will run test extension under |
| // chrome/test/data/extensions/api_test/file_browser/filehandler_create. |
| // The extension will invoke fileBrowserHandler.selectFile function twice. |
| // Once with suggested name "some_file_name.txt", and once with suggested name |
| // "fail". The file selection should succeed the first time, but fail the second |
| // time. When the file is selected the test extension will verify that it can |
| // create, read and write the file under the selected file path. |
| IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, EndToEnd) { |
| // Path that will be "selected" by file selector. |
| const base::FilePath selected_path = |
| GetFullPathOnTmpMountPoint(base::FilePath("test_file.txt")); |
| |
| std::vector<std::string> allowed_extensions; |
| allowed_extensions.push_back("txt"); |
| allowed_extensions.push_back("html"); |
| |
| std::vector<TestCase> test_cases; |
| test_cases.push_back( |
| TestCase(base::FilePath("some_file_name.txt"), |
| allowed_extensions, |
| true, |
| selected_path)); |
| test_cases.push_back( |
| TestCase(base::FilePath("fail"), |
| std::vector<std::string>(), |
| false, |
| base::FilePath())); |
| |
| SetTestCases(&test_cases); |
| |
| // Override extension function that will be used during the test. |
| ASSERT_TRUE(OverrideFunction( |
| "fileBrowserHandlerInternal.selectFile", |
| FileBrowserHandlerExtensionTest::TestSelectFileFunctionFactory)); |
| |
| // Selected path should still not exist. |
| ASSERT_FALSE(base::PathExists(selected_path)); |
| |
| const Extension* extension = LoadExtension( |
| test_data_dir_.AppendASCII("file_browser/filehandler_create")); |
| ASSERT_TRUE(extension) << message_; |
| |
| AddTmpMountPoint(extension->id()); |
| |
| extensions::ResultCatcher catcher; |
| |
| GURL url = extension->GetResourceURL("test.html"); |
| ui_test_utils::NavigateToURL(browser(), url); |
| |
| ASSERT_TRUE(catcher.GetNextResult()) << message_; |
| |
| // Selected path should have been created by the test extension after the |
| // extension function call. |
| ASSERT_TRUE(base::PathExists(selected_path)); |
| |
| // Let's check that the file has the expected content. |
| const std::string kExpectedContents = "hello from test extension."; |
| base::RunLoop run_loop; |
| std::string contents; |
| base::PostTaskWithTraitsAndReply( |
| FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE}, |
| base::BindOnce(base::IgnoreResult(base::ReadFileToString), selected_path, |
| &contents), |
| run_loop.QuitClosure()); |
| run_loop.Run(); |
| EXPECT_EQ(kExpectedContents, contents); |
| |
| SetTestCases(NULL); |
| } |
| |
| // Tests that verifies the fileBrowserHandlerInternal.selectFile function fails |
| // when invoked without user gesture. |
| IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, NoUserGesture) { |
| scoped_refptr<FileBrowserHandlerInternalSelectFileFunction> |
| select_file_function( |
| new FileBrowserHandlerInternalSelectFileFunction()); |
| |
| std::string error = |
| utils::RunFunctionAndReturnError( |
| select_file_function.get(), |
| "[{\"suggestedName\": \"foo\"}]", |
| browser()); |
| |
| const std::string expected_error = |
| "This method can only be called in response to user gesture, such as a " |
| "mouse click or key press."; |
| EXPECT_EQ(expected_error, error); |
| } |
| |
| // Tests that checks that the fileHandlerInternal.selectFile function returns |
| // dictionary with |success == false| and no file entry when user cancels file |
| // selection. |
| IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, SelectionFailed) { |
| TestCase test_case(base::FilePath("some_file_name.txt"), |
| std::vector<std::string>(), |
| false, |
| base::FilePath()); |
| |
| scoped_refptr<FileBrowserHandlerInternalSelectFileFunction> |
| select_file_function( |
| new FileBrowserHandlerInternalSelectFileFunction( |
| new MockFileSelectorFactory(test_case), |
| false)); |
| |
| select_file_function->set_has_callback(true); |
| select_file_function->set_user_gesture(true); |
| |
| std::unique_ptr<base::DictionaryValue> result( |
| utils::ToDictionary(utils::RunFunctionAndReturnSingleResult( |
| select_file_function.get(), |
| "[{\"suggestedName\": \"some_file_name.txt\"}]", browser()))); |
| |
| EXPECT_FALSE(extensions::api_test_utils::GetBoolean(result.get(), "success")); |
| base::DictionaryValue* entry_info; |
| EXPECT_FALSE(result->GetDictionary("entry", &entry_info)); |
| } |
| |
| // Tests that user cannot be suggested a full file path when selecting a file, |
| // only a file name (i.e. that extension function caller has no influence on |
| // which directory contents will be initially displayed in selection dialog). |
| IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, SuggestedFullPath) { |
| TestCase test_case(base::FilePath("some_file_name.txt"), |
| std::vector<std::string>(), |
| false, |
| base::FilePath()); |
| |
| scoped_refptr<FileBrowserHandlerInternalSelectFileFunction> |
| select_file_function( |
| new FileBrowserHandlerInternalSelectFileFunction( |
| new MockFileSelectorFactory(test_case), |
| false)); |
| |
| select_file_function->set_has_callback(true); |
| select_file_function->set_user_gesture(true); |
| |
| std::unique_ptr<base::DictionaryValue> result( |
| utils::ToDictionary(utils::RunFunctionAndReturnSingleResult( |
| select_file_function.get(), |
| "[{\"suggestedName\": \"/path_to_file/some_file_name.txt\"}]", |
| browser()))); |
| |
| EXPECT_FALSE(extensions::api_test_utils::GetBoolean(result.get(), "success")); |
| base::DictionaryValue* entry_info; |
| EXPECT_FALSE(result->GetDictionary("entry", &entry_info)); |
| } |
| |
| } // namespace |