• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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 {
TestCase__anon74e32c720111::TestCase34   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   }
~TestCase__anon74e32c720111::TestCase43   ~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.
ExpectFileContentEquals(const base::FilePath & selected_path,const std::string & expected_contents)59 void 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.
70 class MockFileSelector : public file_manager::FileSelector {
71  public:
MockFileSelector(const base::FilePath & suggested_name,const std::vector<std::string> & allowed_extensions,bool success,const base::FilePath & selected_path)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   }
~MockFileSelector()81   virtual ~MockFileSelector() {}
82 
83   // file_manager::FileSelector implementation.
84   // |browser| is not used.
SelectFile(const base::FilePath & suggested_name,const std::vector<std::string> & allowed_extensions,Browser * browser,FileBrowserHandlerInternalSelectFileFunction * function)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:
MockFileSelectorFactory(const TestCase & test_case)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   }
~MockFileSelectorFactory()135   virtual ~MockFileSelectorFactory() {}
136 
137   // file_manager::FileSelectorFactory implementation.
CreateFileSelector() const138   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:
SetUp()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.
AddTmpMountPoint(const std::string & extension_id)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 
GetFullPathOnTmpMountPoint(const base::FilePath & relative_path)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.
TestSelectFileFunctionFactory()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.
SetTestCases(const std::vector<TestCase> * test_cases)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 
227 const std::vector<TestCase>* FileBrowserHandlerExtensionTest::test_cases_ =
228     NULL;
229 size_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.
IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest,EndToEnd)239 IN_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.
IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest,NoUserGesture)301 IN_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.
IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest,SelectionFailed)321 IN_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).
IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest,SuggestedFullPath)350 IN_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