root/chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api_test.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ExpectFileContentEquals
  2. selected_path_
  3. SelectFile
  4. selected_path_
  5. CreateFileSelector
  6. SetUp
  7. AddTmpMountPoint
  8. GetFullPathOnTmpMountPoint
  9. TestSelectFileFunctionFactory
  10. SetTestCases
  11. IN_PROC_BROWSER_TEST_F
  12. IN_PROC_BROWSER_TEST_F
  13. IN_PROC_BROWSER_TEST_F
  14. IN_PROC_BROWSER_TEST_F

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// File contains browser tests for the fileBrowserHandler api.

#include "chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.h"

#include <vector>

#include "base/bind.h"
#include "base/files/scoped_temp_dir.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_function_test_utils.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/browser_context.h"
#include "extensions/common/extension.h"
#include "webkit/browser/fileapi/external_mount_points.h"
#include "webkit/common/fileapi/file_system_types.h"

namespace utils = extension_function_test_utils;

using content::BrowserContext;
using extensions::Extension;

namespace {

// Data that defines FileSelector behaviour in each test case.
struct TestCase {
  TestCase(const base::FilePath& suggested_name,
           const std::vector<std::string>& allowed_extensions,
           bool success,
           const base::FilePath& selected_path)
      : suggested_name(suggested_name),
        allowed_extensions(allowed_extensions),
        success(success),
        selected_path(selected_path) {
  }
  ~TestCase() {}

  // Path that we expect to be suggested to the file selector.
  base::FilePath suggested_name;

  // Extensions that we expect to be allowed to the file selector.
  std::vector<std::string> allowed_extensions;

  // Whether file selector should fail.
  bool success;
  // The path file selector should return back to the function.
  base::FilePath selected_path;
};

// Checks that file under path |selected_path| contains |expected_contents|.
// Must be called on the file thread.
void ExpectFileContentEquals(const base::FilePath& selected_path,
                             const std::string& expected_contents) {
  std::string test_file_contents;
  ASSERT_TRUE(base::ReadFileToString(selected_path, &test_file_contents));
  EXPECT_EQ(expected_contents, test_file_contents);
}

// Mocks FileSelector used by FileBrowserHandlerInternalSelectFileFunction.
// When |SelectFile| is called, it will check that file name suggestion is as
// expected, and respond to the extension function with specified selection
// results.
class MockFileSelector : public file_manager::FileSelector {
 public:
  MockFileSelector(const base::FilePath& suggested_name,
                   const std::vector<std::string>& allowed_extensions,
                   bool success,
                   const base::FilePath& selected_path)
      : suggested_name_(suggested_name),
        allowed_extensions_(allowed_extensions),
        success_(success),
        selected_path_(selected_path) {
  }
  virtual ~MockFileSelector() {}

  // file_manager::FileSelector implementation.
  // |browser| is not used.
  virtual void SelectFile(
      const base::FilePath& suggested_name,
      const std::vector<std::string>& allowed_extensions,
      Browser* browser,
      FileBrowserHandlerInternalSelectFileFunction* function) OVERRIDE {
    // Confirm that the function suggested us the right name.
    EXPECT_EQ(suggested_name_, suggested_name);
    // Confirm that the function allowed us the right extensions.
    EXPECT_EQ(allowed_extensions_.size(), allowed_extensions.size());
    if (allowed_extensions_.size() == allowed_extensions.size()) {
      for (size_t i = 0; i < allowed_extensions_.size(); ++i) {
        EXPECT_EQ(allowed_extensions_[i], allowed_extensions[i]);
      }
    }

    // Send response to the extension function.
    // The callback will take a reference to the function and keep it alive.
    base::MessageLoopProxy::current()->PostTask(FROM_HERE,
        base::Bind(&FileBrowserHandlerInternalSelectFileFunction::
                       OnFilePathSelected,
                   function, success_, selected_path_));
    delete this;
  }

 private:
  // File name that is expected to be suggested by the function.
  base::FilePath suggested_name_;

  // Extensions that is expected to be allowed by the function.
  std::vector<std::string> allowed_extensions_;

  // Whether the selection should succeed.
  bool success_;
  // File path that should be returned to the function.
  base::FilePath selected_path_;

  DISALLOW_COPY_AND_ASSIGN(MockFileSelector);
};

// Mocks file selector factory for the test.
// When |CreateFileSelector| is invoked it will create mock file selector for
// the extension function with test parameters from the object ctor.
class MockFileSelectorFactory : public file_manager::FileSelectorFactory {
 public:
  explicit MockFileSelectorFactory(const TestCase& test_case)
      : suggested_name_(test_case.suggested_name),
        allowed_extensions_(test_case.allowed_extensions),
        success_(test_case.success),
        selected_path_(test_case.selected_path) {
  }
  virtual ~MockFileSelectorFactory() {}

  // file_manager::FileSelectorFactory implementation.
  virtual file_manager::FileSelector* CreateFileSelector() const OVERRIDE {
    return new MockFileSelector(suggested_name_,
                                allowed_extensions_,
                                success_,
                                selected_path_);
  }

 private:
  // File name that is expected to be suggested by the function.
  base::FilePath suggested_name_;
  // Extensions that is expected to be allowed by the function.
  std::vector<std::string> allowed_extensions_;
  // Whether the selection should succeed.
  bool success_;
  // File path that should be returned to the function.
  base::FilePath selected_path_;

  DISALLOW_COPY_AND_ASSIGN(MockFileSelectorFactory);
};

// Extension api test for the fileBrowserHandler extension API.
class FileBrowserHandlerExtensionTest : public ExtensionApiTest {
 protected:
  virtual void SetUp() OVERRIDE {
    // Create mount point directory that will be used in the test.
    // Mount point will be called "tmp", and it will be located in a tmp
    // directory with an unique name.
    ASSERT_TRUE(scoped_tmp_dir_.CreateUniqueTempDir());
    tmp_mount_point_ = scoped_tmp_dir_.path().Append("tmp");
    base::CreateDirectory(tmp_mount_point_);

    ExtensionApiTest::SetUp();
  }

  // Creates new, test mount point.
  void AddTmpMountPoint(const std::string& extension_id) {
    BrowserContext::GetMountPoints(browser()->profile())->RegisterFileSystem(
        "tmp",
        fileapi::kFileSystemTypeNativeLocal,
        fileapi::FileSystemMountOption(),
        tmp_mount_point_);
  }

  base::FilePath GetFullPathOnTmpMountPoint(
      const base::FilePath& relative_path) {
    return tmp_mount_point_.Append(relative_path);
  }

  // Creates a new FileBrowserHandlerInternalSelectFileFunction to be used in
  // the test.  This function will be called from ExtensionFunctinoDispatcher
  // whenever an extension function for fileBrowserHandlerInternal.selectFile
  // will be needed.
  static ExtensionFunction* TestSelectFileFunctionFactory() {
    EXPECT_TRUE(test_cases_);
    EXPECT_TRUE(!test_cases_ || current_test_case_ < test_cases_->size());

    // If this happens, test failed. But, we still don't want to crash, so
    // return valid extension function.
    if (!test_cases_ || current_test_case_ >= test_cases_->size())
      return new FileBrowserHandlerInternalSelectFileFunction();

    // Create file creator factory for the current test case.
    MockFileSelectorFactory* mock_factory =
        new MockFileSelectorFactory(test_cases_->at(current_test_case_));
    current_test_case_++;

    return new FileBrowserHandlerInternalSelectFileFunction(
        mock_factory, false);
  }

  // Sets up test parameters for extension function invocations that will be
  // made during the test.
  void SetTestCases(const std::vector<TestCase>* test_cases) {
    test_cases_ = test_cases;
    current_test_case_ = 0;
  }

 private:
  // List of test parameters for each extension function invocation that will be
  // made during a test.
  // Should be owned by the test code.
  static const std::vector<TestCase>* test_cases_;
  static size_t current_test_case_;

  base::ScopedTempDir scoped_tmp_dir_;
  // Our test mount point path.
  base::FilePath tmp_mount_point_;
};

const std::vector<TestCase>* FileBrowserHandlerExtensionTest::test_cases_ =
    NULL;
size_t FileBrowserHandlerExtensionTest::current_test_case_ = 0;

// End to end test that verifies that fileBrowserHandler.selectFile works as
// expected. It will run test extension under
// chrome/test/data/extensions/api_test/file_browser/filehandler_create.
// The extension will invoke fileBrowserHandler.selectFile function twice.
// Once with suggested name "some_file_name.txt", and once with suggested name
// "fail". The file selection should succeed the first time, but fail the second
// time. When the file is selected the test extension will verify that it can
// create, read and write the file under the selected file path.
IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, EndToEnd) {
  // Path that will be "selected" by file selector.
  const base::FilePath selected_path =
      GetFullPathOnTmpMountPoint(base::FilePath("test_file.txt"));

  std::vector<std::string> allowed_extensions;
  allowed_extensions.push_back("txt");
  allowed_extensions.push_back("html");

  std::vector<TestCase> test_cases;
  test_cases.push_back(
      TestCase(base::FilePath("some_file_name.txt"),
               allowed_extensions,
               true,
               selected_path));
  test_cases.push_back(
      TestCase(base::FilePath("fail"),
               std::vector<std::string>(),
               false,
               base::FilePath()));

  SetTestCases(&test_cases);

  // Override extension function that will be used during the test.
  ASSERT_TRUE(extensions::ExtensionFunctionDispatcher::OverrideFunction(
      "fileBrowserHandlerInternal.selectFile",
      FileBrowserHandlerExtensionTest::TestSelectFileFunctionFactory));

  // Selected path should still not exist.
  ASSERT_FALSE(base::PathExists(selected_path));

  const Extension* extension = LoadExtension(
      test_data_dir_.AppendASCII("file_browser/filehandler_create"));
  ASSERT_TRUE(extension) << message_;

  AddTmpMountPoint(extension->id());

  ResultCatcher catcher;

  GURL url = extension->GetResourceURL("test.html");
  ui_test_utils::NavigateToURL(browser(), url);

  ASSERT_TRUE(catcher.GetNextResult()) << message_;

  // Selected path should have been created by the test extension after the
  // extension function call.
  ASSERT_TRUE(base::PathExists(selected_path));

  // Let's check that the file has the expected content.
  const std::string expected_contents = "hello from test extension.";
  content::BrowserThread::PostTask(content::BrowserThread::FILE, FROM_HERE,
      base::Bind(&ExpectFileContentEquals, selected_path, expected_contents));

  // Make sure test doesn't finish until we check on file thread that the
  // selected file's content is as expected.
  content::RunAllPendingInMessageLoop(content::BrowserThread::FILE);

  SetTestCases(NULL);
}

// Tests that verifies the fileBrowserHandlerInternal.selectFile function fails
// when invoked without user gesture.
IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, NoUserGesture) {
  scoped_refptr<FileBrowserHandlerInternalSelectFileFunction>
      select_file_function(
          new FileBrowserHandlerInternalSelectFileFunction());

  std::string error =
      utils::RunFunctionAndReturnError(
          select_file_function.get(),
          "[{\"suggestedName\": \"foo\"}]",
          browser());

  const std::string expected_error =
      "This method can only be called in response to user gesture, such as a "
      "mouse click or key press.";
  EXPECT_EQ(expected_error, error);
}

// Tests that checks that the fileHandlerInternal.selectFile function returns
// dictionary with |success == false| and no file entry when user cancels file
// selection.
IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, SelectionFailed) {
  TestCase test_case(base::FilePath("some_file_name.txt"),
                     std::vector<std::string>(),
                     false,
                     base::FilePath());

  scoped_refptr<FileBrowserHandlerInternalSelectFileFunction>
      select_file_function(
          new FileBrowserHandlerInternalSelectFileFunction(
              new MockFileSelectorFactory(test_case),
              false));

  select_file_function->set_has_callback(true);
  select_file_function->set_user_gesture(true);

  scoped_ptr<base::DictionaryValue> result(utils::ToDictionary(
      utils::RunFunctionAndReturnSingleResult(
          select_file_function.get(),
          "[{\"suggestedName\": \"some_file_name.txt\"}]",
          browser())));

  EXPECT_FALSE(utils::GetBoolean(result.get(), "success"));
  base::DictionaryValue* entry_info;
  EXPECT_FALSE(result->GetDictionary("entry", &entry_info));
}

// Tests that user cannot be suggested a full file path when selecting a file,
// only a file name (i.e. that extension function caller has no influence on
// which directory contents will be initially displayed in selection dialog).
IN_PROC_BROWSER_TEST_F(FileBrowserHandlerExtensionTest, SuggestedFullPath) {
  TestCase test_case(base::FilePath("some_file_name.txt"),
                     std::vector<std::string>(),
                     false,
                     base::FilePath());

  scoped_refptr<FileBrowserHandlerInternalSelectFileFunction>
      select_file_function(
          new FileBrowserHandlerInternalSelectFileFunction(
              new MockFileSelectorFactory(test_case),
              false));

  select_file_function->set_has_callback(true);
  select_file_function->set_user_gesture(true);

  scoped_ptr<base::DictionaryValue> result(utils::ToDictionary(
      utils::RunFunctionAndReturnSingleResult(
          select_file_function.get(),
          "[{\"suggestedName\": \"/path_to_file/some_file_name.txt\"}]",
          browser())));

  EXPECT_FALSE(utils::GetBoolean(result.get(), "success"));
  base::DictionaryValue* entry_info;
  EXPECT_FALSE(result->GetDictionary("entry", &entry_info));
}

}  // namespace

/* [<][>][^][v][top][bottom][index][help] */