blob: 3f8992612648076bb3341f0b99599f63bf0932ac [file] [log] [blame]
// 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