file_browser_handler_api_test.cc revision e5d81f57cb97b3b6b7fccc9c5610d21eb81db09d
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// File contains browser tests for the fileBrowserHandler api. 6 7#include "chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.h" 8 9#include <vector> 10 11#include "base/bind.h" 12#include "base/files/scoped_temp_dir.h" 13#include "base/values.h" 14#include "chrome/browser/extensions/extension_apitest.h" 15#include "chrome/browser/extensions/extension_function_test_utils.h" 16#include "chrome/browser/profiles/profile.h" 17#include "chrome/browser/ui/browser.h" 18#include "chrome/test/base/in_process_browser_test.h" 19#include "chrome/test/base/ui_test_utils.h" 20#include "content/public/browser/browser_context.h" 21#include "extensions/common/extension.h" 22#include "webkit/browser/fileapi/external_mount_points.h" 23#include "webkit/common/fileapi/file_system_types.h" 24 25namespace utils = extension_function_test_utils; 26 27using content::BrowserContext; 28using extensions::Extension; 29 30namespace { 31 32// Data that defines FileSelector behaviour in each test case. 33struct TestCase { 34 TestCase(const base::FilePath& suggested_name, 35 const std::vector<std::string>& allowed_extensions, 36 bool success, 37 const base::FilePath& selected_path) 38 : suggested_name(suggested_name), 39 allowed_extensions(allowed_extensions), 40 success(success), 41 selected_path(selected_path) { 42 } 43 ~TestCase() {} 44 45 // Path that we expect to be suggested to the file selector. 46 base::FilePath suggested_name; 47 48 // Extensions that we expect to be allowed to the file selector. 49 std::vector<std::string> allowed_extensions; 50 51 // Whether file selector should fail. 52 bool success; 53 // The path file selector should return back to the function. 54 base::FilePath selected_path; 55}; 56 57// Checks that file under path |selected_path| contains |expected_contents|. 58// Must be called on the file thread. 59void ExpectFileContentEquals(const base::FilePath& selected_path, 60 const std::string& expected_contents) { 61 std::string test_file_contents; 62 ASSERT_TRUE(base::ReadFileToString(selected_path, &test_file_contents)); 63 EXPECT_EQ(expected_contents, test_file_contents); 64} 65 66// Mocks FileSelector used by FileBrowserHandlerInternalSelectFileFunction. 67// When |SelectFile| is called, it will check that file name suggestion is as 68// expected, and respond to the extension function with specified selection 69// results. 70class MockFileSelector : public file_manager::FileSelector { 71 public: 72 MockFileSelector(const base::FilePath& suggested_name, 73 const std::vector<std::string>& allowed_extensions, 74 bool success, 75 const base::FilePath& selected_path) 76 : suggested_name_(suggested_name), 77 allowed_extensions_(allowed_extensions), 78 success_(success), 79 selected_path_(selected_path) { 80 } 81 virtual ~MockFileSelector() {} 82 83 // file_manager::FileSelector implementation. 84 // |browser| is not used. 85 virtual void SelectFile( 86 const base::FilePath& suggested_name, 87 const std::vector<std::string>& allowed_extensions, 88 Browser* browser, 89 FileBrowserHandlerInternalSelectFileFunction* function) OVERRIDE { 90 // Confirm that the function suggested us the right name. 91 EXPECT_EQ(suggested_name_, suggested_name); 92 // Confirm that the function allowed us the right extensions. 93 EXPECT_EQ(allowed_extensions_.size(), allowed_extensions.size()); 94 if (allowed_extensions_.size() == allowed_extensions.size()) { 95 for (size_t i = 0; i < allowed_extensions_.size(); ++i) { 96 EXPECT_EQ(allowed_extensions_[i], allowed_extensions[i]); 97 } 98 } 99 100 // Send response to the extension function. 101 // The callback will take a reference to the function and keep it alive. 102 base::MessageLoopProxy::current()->PostTask(FROM_HERE, 103 base::Bind(&FileBrowserHandlerInternalSelectFileFunction:: 104 OnFilePathSelected, 105 function, success_, selected_path_)); 106 delete this; 107 } 108 109 private: 110 // File name that is expected to be suggested by the function. 111 base::FilePath suggested_name_; 112 113 // Extensions that is expected to be allowed by the function. 114 std::vector<std::string> allowed_extensions_; 115 116 // Whether the selection should succeed. 117 bool success_; 118 // File path that should be returned to the function. 119 base::FilePath selected_path_; 120 121 DISALLOW_COPY_AND_ASSIGN(MockFileSelector); 122}; 123 124// Mocks file selector factory for the test. 125// When |CreateFileSelector| is invoked it will create mock file selector for 126// the extension function with test parameters from the object ctor. 127class MockFileSelectorFactory : public file_manager::FileSelectorFactory { 128 public: 129 explicit MockFileSelectorFactory(const TestCase& test_case) 130 : suggested_name_(test_case.suggested_name), 131 allowed_extensions_(test_case.allowed_extensions), 132 success_(test_case.success), 133 selected_path_(test_case.selected_path) { 134 } 135 virtual ~MockFileSelectorFactory() {} 136 137 // file_manager::FileSelectorFactory implementation. 138 virtual file_manager::FileSelector* CreateFileSelector() const OVERRIDE { 139 return new MockFileSelector(suggested_name_, 140 allowed_extensions_, 141 success_, 142 selected_path_); 143 } 144 145 private: 146 // File name that is expected to be suggested by the function. 147 base::FilePath suggested_name_; 148 // Extensions that is expected to be allowed by the function. 149 std::vector<std::string> allowed_extensions_; 150 // Whether the selection should succeed. 151 bool success_; 152 // File path that should be returned to the function. 153 base::FilePath selected_path_; 154 155 DISALLOW_COPY_AND_ASSIGN(MockFileSelectorFactory); 156}; 157 158// Extension api test for the fileBrowserHandler extension API. 159class FileBrowserHandlerExtensionTest : public ExtensionApiTest { 160 protected: 161 virtual void SetUp() OVERRIDE { 162 // Create mount point directory that will be used in the test. 163 // Mount point will be called "tmp", and it will be located in a tmp 164 // directory with an unique name. 165 ASSERT_TRUE(scoped_tmp_dir_.CreateUniqueTempDir()); 166 tmp_mount_point_ = scoped_tmp_dir_.path().Append("tmp"); 167 base::CreateDirectory(tmp_mount_point_); 168 169 ExtensionApiTest::SetUp(); 170 } 171 172 // Creates new, test mount point. 173 void AddTmpMountPoint(const std::string& extension_id) { 174 BrowserContext::GetMountPoints(browser()->profile())->RegisterFileSystem( 175 "tmp", 176 fileapi::kFileSystemTypeNativeLocal, 177 fileapi::FileSystemMountOption(), 178 tmp_mount_point_); 179 } 180 181 base::FilePath GetFullPathOnTmpMountPoint( 182 const base::FilePath& relative_path) { 183 return tmp_mount_point_.Append(relative_path); 184 } 185 186 // Creates a new FileBrowserHandlerInternalSelectFileFunction to be used in 187 // the test. This function will be called from ExtensionFunctinoDispatcher 188 // whenever an extension function for fileBrowserHandlerInternal.selectFile 189 // will be needed. 190 static ExtensionFunction* TestSelectFileFunctionFactory() { 191 EXPECT_TRUE(test_cases_); 192 EXPECT_TRUE(!test_cases_ || current_test_case_ < test_cases_->size()); 193 194 // If this happens, test failed. But, we still don't want to crash, so 195 // return valid extension function. 196 if (!test_cases_ || current_test_case_ >= test_cases_->size()) 197 return new FileBrowserHandlerInternalSelectFileFunction(); 198 199 // Create file creator factory for the current test case. 200 MockFileSelectorFactory* mock_factory = 201 new MockFileSelectorFactory(test_cases_->at(current_test_case_)); 202 current_test_case_++; 203 204 return new FileBrowserHandlerInternalSelectFileFunction( 205 mock_factory, false); 206 } 207 208 // Sets up test parameters for extension function invocations that will be 209 // made during the test. 210 void SetTestCases(const std::vector<TestCase>* test_cases) { 211 test_cases_ = test_cases; 212 current_test_case_ = 0; 213 } 214 215 private: 216 // List of test parameters for each extension function invocation that will be 217 // made during a test. 218 // Should be owned by the test code. 219 static const std::vector<TestCase>* test_cases_; 220 static size_t current_test_case_; 221 222 base::ScopedTempDir scoped_tmp_dir_; 223 // Our test mount point path. 224 base::FilePath tmp_mount_point_; 225}; 226 227const std::vector<TestCase>* FileBrowserHandlerExtensionTest::test_cases_ = 228 NULL; 229size_t FileBrowserHandlerExtensionTest::current_test_case_ = 0; 230 231// End to end test that verifies that fileBrowserHandler.selectFile works as 232// expected. It will run test extension under 233// chrome/test/data/extensions/api_test/file_browser/filehandler_create. 234// The extension will invoke fileBrowserHandler.selectFile function twice. 235// Once with suggested name "some_file_name.txt", and once with suggested name 236// "fail". The file selection should succeed the first time, but fail the second 237// time. When the file is selected the test extension will verify that it can 238// create, read and write the file under the selected file path. 239IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, EndToEnd) { 240 // Path that will be "selected" by file selector. 241 const base::FilePath selected_path = 242 GetFullPathOnTmpMountPoint(base::FilePath("test_file.txt")); 243 244 std::vector<std::string> allowed_extensions; 245 allowed_extensions.push_back("txt"); 246 allowed_extensions.push_back("html"); 247 248 std::vector<TestCase> test_cases; 249 test_cases.push_back( 250 TestCase(base::FilePath("some_file_name.txt"), 251 allowed_extensions, 252 true, 253 selected_path)); 254 test_cases.push_back( 255 TestCase(base::FilePath("fail"), 256 std::vector<std::string>(), 257 false, 258 base::FilePath())); 259 260 SetTestCases(&test_cases); 261 262 // Override extension function that will be used during the test. 263 ASSERT_TRUE(extensions::ExtensionFunctionDispatcher::OverrideFunction( 264 "fileBrowserHandlerInternal.selectFile", 265 FileBrowserHandlerExtensionTest::TestSelectFileFunctionFactory)); 266 267 // Selected path should still not exist. 268 ASSERT_FALSE(base::PathExists(selected_path)); 269 270 const Extension* extension = LoadExtension( 271 test_data_dir_.AppendASCII("file_browser/filehandler_create")); 272 ASSERT_TRUE(extension) << message_; 273 274 AddTmpMountPoint(extension->id()); 275 276 ResultCatcher catcher; 277 278 GURL url = extension->GetResourceURL("test.html"); 279 ui_test_utils::NavigateToURL(browser(), url); 280 281 ASSERT_TRUE(catcher.GetNextResult()) << message_; 282 283 // Selected path should have been created by the test extension after the 284 // extension function call. 285 ASSERT_TRUE(base::PathExists(selected_path)); 286 287 // Let's check that the file has the expected content. 288 const std::string expected_contents = "hello from test extension."; 289 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE, 290 base::Bind(&ExpectFileContentEquals, selected_path, expected_contents)); 291 292 // Make sure test doesn't finish until we check on file thread that the 293 // selected file's content is as expected. 294 content::RunAllPendingInMessageLoop(content::BrowserThread::FILE); 295 296 SetTestCases(NULL); 297} 298 299// Tests that verifies the fileBrowserHandlerInternal.selectFile function fails 300// when invoked without user gesture. 301IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, NoUserGesture) { 302 scoped_refptr<FileBrowserHandlerInternalSelectFileFunction> 303 select_file_function( 304 new FileBrowserHandlerInternalSelectFileFunction()); 305 306 std::string error = 307 utils::RunFunctionAndReturnError( 308 select_file_function.get(), 309 "[{\"suggestedName\": \"foo\"}]", 310 browser()); 311 312 const std::string expected_error = 313 "This method can only be called in response to user gesture, such as a " 314 "mouse click or key press."; 315 EXPECT_EQ(expected_error, error); 316} 317 318// Tests that checks that the fileHandlerInternal.selectFile function returns 319// dictionary with |success == false| and no file entry when user cancels file 320// selection. 321IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, SelectionFailed) { 322 TestCase test_case(base::FilePath("some_file_name.txt"), 323 std::vector<std::string>(), 324 false, 325 base::FilePath()); 326 327 scoped_refptr<FileBrowserHandlerInternalSelectFileFunction> 328 select_file_function( 329 new FileBrowserHandlerInternalSelectFileFunction( 330 new MockFileSelectorFactory(test_case), 331 false)); 332 333 select_file_function->set_has_callback(true); 334 select_file_function->set_user_gesture(true); 335 336 scoped_ptr<base::DictionaryValue> result(utils::ToDictionary( 337 utils::RunFunctionAndReturnSingleResult( 338 select_file_function.get(), 339 "[{\"suggestedName\": \"some_file_name.txt\"}]", 340 browser()))); 341 342 EXPECT_FALSE(utils::GetBoolean(result.get(), "success")); 343 base::DictionaryValue* entry_info; 344 EXPECT_FALSE(result->GetDictionary("entry", &entry_info)); 345} 346 347// Tests that user cannot be suggested a full file path when selecting a file, 348// only a file name (i.e. that extension function caller has no influence on 349// which directory contents will be initially displayed in selection dialog). 350IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, SuggestedFullPath) { 351 TestCase test_case(base::FilePath("some_file_name.txt"), 352 std::vector<std::string>(), 353 false, 354 base::FilePath()); 355 356 scoped_refptr<FileBrowserHandlerInternalSelectFileFunction> 357 select_file_function( 358 new FileBrowserHandlerInternalSelectFileFunction( 359 new MockFileSelectorFactory(test_case), 360 false)); 361 362 select_file_function->set_has_callback(true); 363 select_file_function->set_user_gesture(true); 364 365 scoped_ptr<base::DictionaryValue> result(utils::ToDictionary( 366 utils::RunFunctionAndReturnSingleResult( 367 select_file_function.get(), 368 "[{\"suggestedName\": \"/path_to_file/some_file_name.txt\"}]", 369 browser()))); 370 371 EXPECT_FALSE(utils::GetBoolean(result.get(), "success")); 372 base::DictionaryValue* entry_info; 373 EXPECT_FALSE(result->GetDictionary("entry", &entry_info)); 374} 375 376} // namespace 377