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