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/common/extensions/extension.h" 19 #include "chrome/test/base/in_process_browser_test.h" 20 #include "chrome/test/base/ui_test_utils.h" 21 #include "content/public/browser/browser_context.h" 22 #include "webkit/browser/fileapi/external_mount_points.h" 23 #include "webkit/common/fileapi/file_system_types.h" 24 25 namespace utils = extension_function_test_utils; 26 27 using content::BrowserContext; 28 using extensions::Extension; 29 30 namespace { 31 32 // Data that defines FileSelector behaviour in each test case. 33 struct 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. 59 void ExpectFileContentEquals(const base::FilePath& selected_path, 60 const std::string& expected_contents) { 61 std::string test_file_contents; 62 ASSERT_TRUE(file_util::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. 70 class 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. 127 class 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. 159 class 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 file_util::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 tmp_mount_point_); 178 } 179 180 base::FilePath GetFullPathOnTmpMountPoint( 181 const base::FilePath& relative_path) { 182 return tmp_mount_point_.Append(relative_path); 183 } 184 185 // Creates a new FileBrowserHandlerInternalSelectFileFunction to be used in 186 // the test. This function will be called from ExtensionFunctinoDispatcher 187 // whenever an extension function for fileBrowserHandlerInternal.selectFile 188 // will be needed. 189 static ExtensionFunction* TestSelectFileFunctionFactory() { 190 EXPECT_TRUE(test_cases_); 191 EXPECT_TRUE(!test_cases_ || current_test_case_ < test_cases_->size()); 192 193 // If this happens, test failed. But, we still don't want to crash, so 194 // return valid extension function. 195 if (!test_cases_ || current_test_case_ >= test_cases_->size()) 196 return new FileBrowserHandlerInternalSelectFileFunction(); 197 198 // Create file creator factory for the current test case. 199 MockFileSelectorFactory* mock_factory = 200 new MockFileSelectorFactory(test_cases_->at(current_test_case_)); 201 current_test_case_++; 202 203 return new FileBrowserHandlerInternalSelectFileFunction( 204 mock_factory, false); 205 } 206 207 // Sets up test parameters for extension function invocations that will be 208 // made during the test. 209 void SetTestCases(const std::vector<TestCase>* test_cases) { 210 test_cases_ = test_cases; 211 current_test_case_ = 0; 212 } 213 214 private: 215 // List of test parameters for each extension function invocation that will be 216 // made during a test. 217 // Should be owned by the test code. 218 static const std::vector<TestCase>* test_cases_; 219 static size_t current_test_case_; 220 221 base::ScopedTempDir scoped_tmp_dir_; 222 // Our test mount point path. 223 base::FilePath tmp_mount_point_; 224 }; 225 226 const std::vector<TestCase>* FileBrowserHandlerExtensionTest::test_cases_ = 227 NULL; 228 size_t FileBrowserHandlerExtensionTest::current_test_case_ = 0; 229 230 // End to end test that verifies that fileBrowserHandler.selectFile works as 231 // expected. It will run test extension under 232 // chrome/test/data/extensions/api_test/file_browser/filehandler_create. 233 // The extension will invoke fileBrowserHandler.selectFile function twice. 234 // Once with suggested name "some_file_name.txt", and once with suggested name 235 // "fail". The file selection should succeed the first time, but fail the second 236 // time. When the file is selected the test extension will verify that it can 237 // create, read and write the file under the selected file path. 238 IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, EndToEnd) { 239 // Path that will be "selected" by file selector. 240 const base::FilePath selected_path = 241 GetFullPathOnTmpMountPoint(base::FilePath("test_file.txt")); 242 243 std::vector<std::string> allowed_extensions; 244 allowed_extensions.push_back("txt"); 245 allowed_extensions.push_back("html"); 246 247 std::vector<TestCase> test_cases; 248 test_cases.push_back( 249 TestCase(base::FilePath("some_file_name.txt"), 250 allowed_extensions, 251 true, 252 selected_path)); 253 test_cases.push_back( 254 TestCase(base::FilePath("fail"), 255 std::vector<std::string>(), 256 false, 257 base::FilePath())); 258 259 SetTestCases(&test_cases); 260 261 // Override extension function that will be used during the test. 262 ASSERT_TRUE(ExtensionFunctionDispatcher::OverrideFunction( 263 "fileBrowserHandlerInternal.selectFile", 264 FileBrowserHandlerExtensionTest::TestSelectFileFunctionFactory)); 265 266 // Selected path should still not exist. 267 ASSERT_FALSE(base::PathExists(selected_path)); 268 269 const Extension* extension = LoadExtension( 270 test_data_dir_.AppendASCII("file_browser/filehandler_create")); 271 ASSERT_TRUE(extension) << message_; 272 273 AddTmpMountPoint(extension->id()); 274 275 ResultCatcher catcher; 276 277 GURL url = extension->GetResourceURL("test.html"); 278 ui_test_utils::NavigateToURL(browser(), url); 279 280 ASSERT_TRUE(catcher.GetNextResult()) << message_; 281 282 // Selected path should have been created by the test extension after the 283 // extension function call. 284 ASSERT_TRUE(base::PathExists(selected_path)); 285 286 // Let's check that the file has the expected content. 287 const std::string expected_contents = "hello from test extension."; 288 content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE, 289 base::Bind(&ExpectFileContentEquals, selected_path, expected_contents)); 290 291 // Make sure test doesn't finish until we check on file thread that the 292 // selected file's content is as expected. 293 content::RunAllPendingInMessageLoop(content::BrowserThread::FILE); 294 295 SetTestCases(NULL); 296 } 297 298 // Tests that verifies the fileBrowserHandlerInternal.selectFile function fails 299 // when invoked without user gesture. 300 IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, NoUserGesture) { 301 scoped_refptr<FileBrowserHandlerInternalSelectFileFunction> 302 select_file_function( 303 new FileBrowserHandlerInternalSelectFileFunction()); 304 305 std::string error = 306 utils::RunFunctionAndReturnError( 307 select_file_function.get(), 308 "[{\"suggestedName\": \"foo\"}]", 309 browser()); 310 311 const std::string expected_error = 312 "This method can only be called in response to user gesture, such as a " 313 "mouse click or key press."; 314 EXPECT_EQ(expected_error, error); 315 } 316 317 // Tests that checks that the fileHandlerInternal.selectFile function returns 318 // dictionary with |success == false| and no file entry when user cancels file 319 // selection. 320 IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, SelectionFailed) { 321 TestCase test_case(base::FilePath("some_file_name.txt"), 322 std::vector<std::string>(), 323 false, 324 base::FilePath()); 325 326 scoped_refptr<FileBrowserHandlerInternalSelectFileFunction> 327 select_file_function( 328 new FileBrowserHandlerInternalSelectFileFunction( 329 new MockFileSelectorFactory(test_case), 330 false)); 331 332 select_file_function->set_has_callback(true); 333 select_file_function->set_user_gesture(true); 334 335 scoped_ptr<base::DictionaryValue> result(utils::ToDictionary( 336 utils::RunFunctionAndReturnSingleResult( 337 select_file_function.get(), 338 "[{\"suggestedName\": \"some_file_name.txt\"}]", 339 browser()))); 340 341 EXPECT_FALSE(utils::GetBoolean(result.get(), "success")); 342 DictionaryValue* entry_info; 343 EXPECT_FALSE(result->GetDictionary("entry", &entry_info)); 344 } 345 346 // Tests that user cannot be suggested a full file path when selecting a file, 347 // only a file name (i.e. that extension function caller has no influence on 348 // which directory contents will be initially displayed in selection dialog). 349 IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, SuggestedFullPath) { 350 TestCase test_case(base::FilePath("some_file_name.txt"), 351 std::vector<std::string>(), 352 false, 353 base::FilePath()); 354 355 scoped_refptr<FileBrowserHandlerInternalSelectFileFunction> 356 select_file_function( 357 new FileBrowserHandlerInternalSelectFileFunction( 358 new MockFileSelectorFactory(test_case), 359 false)); 360 361 select_file_function->set_has_callback(true); 362 select_file_function->set_user_gesture(true); 363 364 scoped_ptr<base::DictionaryValue> result(utils::ToDictionary( 365 utils::RunFunctionAndReturnSingleResult( 366 select_file_function.get(), 367 "[{\"suggestedName\": \"/path_to_file/some_file_name.txt\"}]", 368 browser()))); 369 370 EXPECT_FALSE(utils::GetBoolean(result.get(), "success")); 371 DictionaryValue* entry_info; 372 EXPECT_FALSE(result->GetDictionary("entry", &entry_info)); 373 } 374 375 } // namespace 376