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