root/chrome/browser/media_galleries/media_file_system_registry_unittest.cc

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

DEFINITIONS

This source file includes following definitions.
  1. registry
  2. fs_name
  3. RegisterFileSystem
  4. RevokeFileSystem
  5. GetRegisteredPath
  6. AddFSEntry
  7. GetGalleryInfoCallback
  8. CheckGalleryInfo
  9. GetExpectedFolderName
  10. empty_dir
  11. dcim_dir
  12. test_file_system_context
  13. ProcessAttach
  14. ProcessDetach
  15. registry
  16. num_auto_galleries
  17. MediaFileSystemInfoComparator
  18. MockProfileSharedRenderProcessHostFactory
  19. ReleaseRPH
  20. CreateRenderProcessHost
  21. profile_
  22. GetMediaGalleriesPrefs
  23. CheckGalleries
  24. GetGalleriesInfo
  25. all_permission_extension
  26. regular_permission_extension
  27. profile
  28. AddNameForReadCompare
  29. AddNameForAllCompare
  30. ContainsEntry
  31. CompareResults
  32. GetAndClearComparisonCount
  33. CreateProfileState
  34. GetProfileState
  35. GetPreferences
  36. AddUserGallery
  37. AttachDevice
  38. DetachDevice
  39. SetGalleryPermission
  40. AssertAllAutoAddedGalleries
  41. InitForGalleriesInfoTest
  42. CheckNewGalleryInfo
  43. GetAutoAddedGalleries
  44. GetExtensionGalleriesHostCount
  45. SetUp
  46. TearDown
  47. TEST_F
  48. TEST_F
  49. TEST_F
  50. TEST_F
  51. TEST_F
  52. TEST_F
  53. TEST_F
  54. TEST_F
  55. TEST_F
  56. TEST_F
  57. TEST_F
  58. 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.

// MediaFileSystemRegistry unit tests.

#include <algorithm>
#include <set>

#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/message_loop/message_loop.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/values.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/browser/media_galleries/media_file_system_context.h"
#include "chrome/browser/media_galleries/media_file_system_registry.h"
#include "chrome/browser/media_galleries/media_galleries_preferences_factory.h"
#include "chrome/browser/media_galleries/media_galleries_test_util.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "components/storage_monitor/removable_device_constants.h"
#include "components/storage_monitor/storage_info.h"
#include "components/storage_monitor/storage_monitor.h"
#include "components/storage_monitor/test_storage_monitor.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_process_host_factory.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_thread.h"
#include "content/public/test/web_contents_tester.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/extension.h"
#include "sync/api/string_ordinal.h"
#include "testing/gtest/include/gtest/gtest.h"

#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/settings/device_settings_service.h"
#endif

using content::BrowserThread;
using storage_monitor::StorageInfo;
using storage_monitor::StorageMonitor;
using storage_monitor::TestStorageMonitor;

// Not anonymous so it can be friends with MediaFileSystemRegistry.
class TestMediaFileSystemContext : public MediaFileSystemContext {
 public:
  struct FSInfo {
    FSInfo() {}
    FSInfo(const std::string& device_id, const base::FilePath& path,
           const std::string& fs_name);

    bool operator<(const FSInfo& other) const;

    std::string device_id;
    base::FilePath path;
    std::string fs_name;
  };

  explicit TestMediaFileSystemContext(MediaFileSystemRegistry* registry);
  virtual ~TestMediaFileSystemContext() {}

  // MediaFileSystemContext implementation.
  virtual bool RegisterFileSystem(const std::string& device_id,
                                  const std::string& fs_name,
                                  const base::FilePath& path) OVERRIDE;

  virtual void RevokeFileSystem(const std::string& fs_name) OVERRIDE;

  virtual base::FilePath GetRegisteredPath(
      const std::string& fs_name) const OVERRIDE;

  MediaFileSystemRegistry* registry() { return registry_; }

 private:
  void AddFSEntry(const std::string& device_id,
                  const base::FilePath& path,
                  const std::string& fs_name);

  MediaFileSystemRegistry* registry_;

  // The currently allocated mock file systems.
  std::map<std::string /*fs_name*/, FSInfo> file_systems_by_name_;
};

TestMediaFileSystemContext::FSInfo::FSInfo(const std::string& device_id,
                                           const base::FilePath& path,
                                           const std::string& fs_name)
    : device_id(device_id),
      path(path),
      fs_name(fs_name) {
}

bool TestMediaFileSystemContext::FSInfo::operator<(const FSInfo& other) const {
  if (device_id != other.device_id)
    return device_id < other.device_id;
  if (path.value() != other.path.value())
    return path.value() < other.path.value();
  return fs_name < other.fs_name;
}

TestMediaFileSystemContext::TestMediaFileSystemContext(
    MediaFileSystemRegistry* registry)
    : registry_(registry) {
  registry_->file_system_context_.reset(this);
}

bool TestMediaFileSystemContext::RegisterFileSystem(
    const std::string& device_id,
    const std::string& fs_name,
    const base::FilePath& path) {
  AddFSEntry(device_id, path, fs_name);
  return true;
}

void TestMediaFileSystemContext::RevokeFileSystem(const std::string& fs_name) {
  if (!ContainsKey(file_systems_by_name_, fs_name))
    return;
  EXPECT_EQ(1U, file_systems_by_name_.erase(fs_name));
}

base::FilePath TestMediaFileSystemContext::GetRegisteredPath(
    const std::string& fs_name) const {
  std::map<std::string /*fs_name*/, FSInfo>::const_iterator it =
      file_systems_by_name_.find(fs_name);
  if (it == file_systems_by_name_.end())
    return base::FilePath();
  return it->second.path;
}

void TestMediaFileSystemContext::AddFSEntry(const std::string& device_id,
                                            const base::FilePath& path,
                                            const std::string& fs_name) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  DCHECK(path.IsAbsolute());
  DCHECK(!path.ReferencesParent());

  FSInfo info(device_id, path, fs_name);
  file_systems_by_name_[fs_name] = info;
}

namespace {

typedef std::map<MediaGalleryPrefId, MediaFileSystemInfo> FSInfoMap;

void GetGalleryInfoCallback(
    FSInfoMap* results,
    const std::vector<MediaFileSystemInfo>& file_systems) {
  for (size_t i = 0; i < file_systems.size(); ++i) {
    ASSERT_FALSE(ContainsKey(*results, file_systems[i].pref_id));
    (*results)[file_systems[i].pref_id] = file_systems[i];
  }
}

void CheckGalleryInfo(const MediaFileSystemInfo& info,
                      TestMediaFileSystemContext* fs_context,
                      const base::FilePath& path,
                      bool removable,
                      bool media_device) {
  EXPECT_EQ(path, info.path);
  EXPECT_EQ(removable, info.removable);
  EXPECT_EQ(media_device, info.media_device);
  EXPECT_NE(0UL, info.pref_id);

  if (removable)
    EXPECT_NE(0UL, info.transient_device_id.size());
  else
    EXPECT_EQ(0UL, info.transient_device_id.size());

  base::FilePath fsid_path = fs_context->GetRegisteredPath(info.fsid);
  EXPECT_EQ(path, fsid_path);
}

class MockProfileSharedRenderProcessHostFactory
    : public content::RenderProcessHostFactory {
 public:
  MockProfileSharedRenderProcessHostFactory() {}
  virtual ~MockProfileSharedRenderProcessHostFactory();

  // RPH created with this factory are owned by it.  If the RPH is destroyed
  // for testing purposes, it must be removed from the factory first.
  content::MockRenderProcessHost* ReleaseRPH(
      content::BrowserContext* browser_context);

  virtual content::RenderProcessHost* CreateRenderProcessHost(
      content::BrowserContext* browser_context,
      content::SiteInstance* site_instance) const OVERRIDE;

 private:
  typedef std::map<content::BrowserContext*, content::MockRenderProcessHost*>
      ProfileRPHMap;
  mutable ProfileRPHMap rph_map_;

  DISALLOW_COPY_AND_ASSIGN(MockProfileSharedRenderProcessHostFactory);
};

class ProfileState {
 public:
  explicit ProfileState(
      MockProfileSharedRenderProcessHostFactory* rph_factory);
  ~ProfileState();

  MediaGalleriesPreferences* GetMediaGalleriesPrefs();

  void CheckGalleries(
      const std::string& test,
      const std::vector<MediaFileSystemInfo>& regular_extension_galleries,
      const std::vector<MediaFileSystemInfo>& all_extension_galleries);

  FSInfoMap GetGalleriesInfo(extensions::Extension* extension);

  extensions::Extension* all_permission_extension();
  extensions::Extension* regular_permission_extension();
  Profile* profile();

  void AddNameForReadCompare(const base::string16& name);
  void AddNameForAllCompare(const base::string16& name);

 private:
  void CompareResults(const std::string& test,
                      const std::vector<base::string16>& names,
                      const std::vector<MediaFileSystemInfo>& expected,
                      const std::vector<MediaFileSystemInfo>& actual);
  bool ContainsEntry(const MediaFileSystemInfo& info,
                     const std::vector<MediaFileSystemInfo>& container);

  int GetAndClearComparisonCount();

  int num_comparisons_;

  scoped_ptr<TestingProfile> profile_;

  scoped_refptr<extensions::Extension> all_permission_extension_;
  scoped_refptr<extensions::Extension> regular_permission_extension_;
  scoped_refptr<extensions::Extension> no_permissions_extension_;

  scoped_ptr<content::WebContents> single_web_contents_;
  scoped_ptr<content::WebContents> shared_web_contents1_;
  scoped_ptr<content::WebContents> shared_web_contents2_;

  // The RenderProcessHosts are freed when their respective WebContents /
  // RenderViewHosts go away.
  content::MockRenderProcessHost* single_rph_;
  content::MockRenderProcessHost* shared_rph_;

  std::vector<base::string16> compare_names_read_;
  std::vector<base::string16> compare_names_all_;

  DISALLOW_COPY_AND_ASSIGN(ProfileState);
};

base::string16 GetExpectedFolderName(const base::FilePath& path) {
#if defined(OS_CHROMEOS)
  return path.BaseName().LossyDisplayName();
#else
  return path.LossyDisplayName();
#endif
}

}  // namespace

class MediaFileSystemRegistryTest : public ChromeRenderViewHostTestHarness {
 public:
  void CreateProfileState(size_t profile_count);

  ProfileState* GetProfileState(size_t i);

  MediaGalleriesPreferences* GetPreferences(Profile* profile);

  base::FilePath empty_dir() {
    return empty_dir_;
  }

  base::FilePath dcim_dir() {
    return dcim_dir_;
  }

  TestMediaFileSystemContext* test_file_system_context() {
    return test_file_system_context_;
  }

  // Create a user added gallery based on the information passed and add it to
  // |profiles|. Returns the device id.
  std::string AddUserGallery(StorageInfo::Type type,
                             const std::string& unique_id,
                             const base::FilePath& path);

  // Returns the device id.
  std::string AttachDevice(StorageInfo::Type type,
                           const std::string& unique_id,
                           const base::FilePath& location);

  void DetachDevice(const std::string& device_id);

  void SetGalleryPermission(ProfileState* profile_state,
                            extensions::Extension* extension,
                            const std::string& device_id,
                            bool has_access);

  void AssertAllAutoAddedGalleries();

  void InitForGalleriesInfoTest(FSInfoMap* galleries_info);

  void CheckNewGalleryInfo(ProfileState* profile_state,
                           const FSInfoMap& galleries_info,
                           const base::FilePath& location,
                           bool removable,
                           bool media_device);

  std::vector<MediaFileSystemInfo> GetAutoAddedGalleries(
      ProfileState* profile_state);

  void ProcessAttach(const std::string& id,
                     const base::string16& name,
                     const base::FilePath::StringType& location) {
    StorageInfo info(id, location, name, base::string16(), base::string16(), 0);
    StorageMonitor::GetInstance()->receiver()->ProcessAttach(info);
  }

  void ProcessDetach(const std::string& id) {
    StorageMonitor::GetInstance()->receiver()->ProcessDetach(id);
  }

  MediaFileSystemRegistry* registry() {
    return test_file_system_context_->registry();
  }

  size_t GetExtensionGalleriesHostCount(
      const MediaFileSystemRegistry* registry) const;

  int num_auto_galleries() {
    return media_directories_.num_galleries();
  }

 protected:
  virtual void SetUp() OVERRIDE;
  virtual void TearDown() OVERRIDE;

 private:
  // This makes sure that at least one default gallery exists on the file
  // system.
  EnsureMediaDirectoriesExists media_directories_;

  // Some test gallery directories.
  base::ScopedTempDir galleries_dir_;
  // An empty directory in |galleries_dir_|
  base::FilePath empty_dir_;
  // A directory in |galleries_dir_| with a DCIM directory in it.
  base::FilePath dcim_dir_;

  // MediaFileSystemRegistry owns this.
  TestMediaFileSystemContext* test_file_system_context_;

  // Needed for extension service & friends to work.

#if defined(OS_CHROMEOS)
  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
  chromeos::ScopedTestCrosSettings test_cros_settings_;
  scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
#endif

  MockProfileSharedRenderProcessHostFactory rph_factory_;

  ScopedVector<ProfileState> profile_states_;
};

namespace {

bool MediaFileSystemInfoComparator(const MediaFileSystemInfo& a,
                                   const MediaFileSystemInfo& b) {
  CHECK_NE(a.name, b.name);  // Name must be unique.
  return a.name < b.name;
}

///////////////////////////////////////////////
// MockProfileSharedRenderProcessHostFactory //
///////////////////////////////////////////////

MockProfileSharedRenderProcessHostFactory::
    ~MockProfileSharedRenderProcessHostFactory() {
  STLDeleteValues(&rph_map_);
}

content::MockRenderProcessHost*
MockProfileSharedRenderProcessHostFactory::ReleaseRPH(
    content::BrowserContext* browser_context) {
  ProfileRPHMap::iterator existing = rph_map_.find(browser_context);
  if (existing == rph_map_.end())
    return NULL;
  content::MockRenderProcessHost* result = existing->second;
  rph_map_.erase(existing);
  return result;
}

content::RenderProcessHost*
MockProfileSharedRenderProcessHostFactory::CreateRenderProcessHost(
    content::BrowserContext* browser_context,
    content::SiteInstance* site_instance) const {
  ProfileRPHMap::const_iterator existing = rph_map_.find(browser_context);
  if (existing != rph_map_.end())
    return existing->second;
  rph_map_[browser_context] =
      new content::MockRenderProcessHost(browser_context);
  return rph_map_[browser_context];
}

//////////////////
// ProfileState //
//////////////////

ProfileState::ProfileState(
    MockProfileSharedRenderProcessHostFactory* rph_factory)
    : num_comparisons_(0),
      profile_(new TestingProfile()) {
  extensions::TestExtensionSystem* extension_system(
      static_cast<extensions::TestExtensionSystem*>(
          extensions::ExtensionSystem::Get(profile_.get())));
  extension_system->CreateExtensionService(
      CommandLine::ForCurrentProcess(), base::FilePath(), false);

  std::vector<std::string> all_permissions;
  all_permissions.push_back("allAutoDetected");
  all_permissions.push_back("read");
  std::vector<std::string> read_permissions;
  read_permissions.push_back("read");

  all_permission_extension_ =
      AddMediaGalleriesApp("all", all_permissions, profile_.get());
  regular_permission_extension_ =
      AddMediaGalleriesApp("regular", read_permissions, profile_.get());
  no_permissions_extension_ =
      AddMediaGalleriesApp("no", read_permissions, profile_.get());

  single_web_contents_.reset(
      content::WebContentsTester::CreateTestWebContents(profile_.get(), NULL));
  single_rph_ = rph_factory->ReleaseRPH(profile_.get());

  shared_web_contents1_.reset(
      content::WebContentsTester::CreateTestWebContents(profile_.get(), NULL));
  shared_web_contents2_.reset(
      content::WebContentsTester::CreateTestWebContents(profile_.get(), NULL));
  shared_rph_ = rph_factory->ReleaseRPH(profile_.get());
}

ProfileState::~ProfileState() {
  // TestExtensionSystem uses DeleteSoon, so we need to delete the profiles
  // and then run the message queue to clean up.  But first we have to
  // delete everything that references the profile.
  single_web_contents_.reset();
  shared_web_contents1_.reset();
  shared_web_contents2_.reset();
  profile_.reset();

  base::MessageLoop::current()->RunUntilIdle();
}

MediaGalleriesPreferences* ProfileState::GetMediaGalleriesPrefs() {
  MediaGalleriesPreferences* prefs =
      MediaGalleriesPreferencesFactory::GetForProfile(profile_.get());
  base::RunLoop loop;
  prefs->EnsureInitialized(loop.QuitClosure());
  loop.Run();
  return prefs;
}

void ProfileState::CheckGalleries(
    const std::string& test,
    const std::vector<MediaFileSystemInfo>& regular_extension_galleries,
    const std::vector<MediaFileSystemInfo>& all_extension_galleries) {
  content::RenderViewHost* rvh = single_web_contents_->GetRenderViewHost();
  MediaFileSystemRegistry* registry =
      g_browser_process->media_file_system_registry();

  // No Media Galleries permissions.
  std::vector<MediaFileSystemInfo> empty_expectation;
  std::vector<base::string16> empty_names;
  registry->GetMediaFileSystemsForExtension(
      rvh, no_permissions_extension_.get(),
      base::Bind(&ProfileState::CompareResults, base::Unretained(this),
                 base::StringPrintf("%s (no permission)", test.c_str()),
                 base::ConstRef(empty_names),
                 base::ConstRef(empty_expectation)));
  base::MessageLoop::current()->RunUntilIdle();
  EXPECT_EQ(1, GetAndClearComparisonCount());

  // Read permission only.
  registry->GetMediaFileSystemsForExtension(
      rvh, regular_permission_extension_.get(),
      base::Bind(&ProfileState::CompareResults, base::Unretained(this),
                 base::StringPrintf("%s (regular permission)", test.c_str()),
                 base::ConstRef(compare_names_read_),
                 base::ConstRef(regular_extension_galleries)));
  base::MessageLoop::current()->RunUntilIdle();
  EXPECT_EQ(1, GetAndClearComparisonCount());

  // All galleries permission.
  registry->GetMediaFileSystemsForExtension(
      rvh, all_permission_extension_.get(),
      base::Bind(&ProfileState::CompareResults, base::Unretained(this),
                 base::StringPrintf("%s (all permission)", test.c_str()),
                 base::ConstRef(compare_names_all_),
                 base::ConstRef(all_extension_galleries)));
  base::MessageLoop::current()->RunUntilIdle();
  EXPECT_EQ(1, GetAndClearComparisonCount());
}

FSInfoMap ProfileState::GetGalleriesInfo(extensions::Extension* extension) {
  content::RenderViewHost* rvh = single_web_contents_->GetRenderViewHost();
  FSInfoMap results;
  MediaFileSystemRegistry* registry =
      g_browser_process->media_file_system_registry();
  registry->GetMediaFileSystemsForExtension(
      rvh, extension,
      base::Bind(&GetGalleryInfoCallback, base::Unretained(&results)));
  base::MessageLoop::current()->RunUntilIdle();
  return results;
}

extensions::Extension* ProfileState::all_permission_extension() {
  return all_permission_extension_.get();
}

extensions::Extension* ProfileState::regular_permission_extension() {
  return regular_permission_extension_.get();
}

Profile* ProfileState::profile() {
  return profile_.get();
}

void ProfileState::AddNameForReadCompare(const base::string16& name) {
  compare_names_read_.push_back(name);
}

void ProfileState::AddNameForAllCompare(const base::string16& name) {
  compare_names_all_.push_back(name);
}

bool ProfileState::ContainsEntry(
    const MediaFileSystemInfo& info,
    const std::vector<MediaFileSystemInfo>& container) {
  for (size_t i = 0; i < container.size(); ++i) {
    if (info.path.value() == container[i].path.value()) {
      EXPECT_FALSE(container[i].fsid.empty());
      if (!info.fsid.empty())
        EXPECT_EQ(info.fsid, container[i].fsid);
      return true;
    }
  }
  return false;
}

void ProfileState::CompareResults(
    const std::string& test,
    const std::vector<base::string16>& names,
    const std::vector<MediaFileSystemInfo>& expected,
    const std::vector<MediaFileSystemInfo>& actual) {
  num_comparisons_++;
  EXPECT_EQ(expected.size(), actual.size()) << test;

  // Order isn't important, so sort the results.
  std::vector<MediaFileSystemInfo> sorted(actual);
  std::sort(sorted.begin(), sorted.end(), MediaFileSystemInfoComparator);
  std::vector<MediaFileSystemInfo> expect(expected);
  std::sort(expect.begin(), expect.end(), MediaFileSystemInfoComparator);
  std::vector<base::string16> expect_names(names);
  std::sort(expect_names.begin(), expect_names.end());

  for (size_t i = 0; i < expect.size() && i < sorted.size(); ++i) {
    if (expect_names.size() > i)
      EXPECT_EQ(expect_names[i], sorted[i].name) << test;
    EXPECT_TRUE(ContainsEntry(expect[i], sorted)) << test;
  }
}

int ProfileState::GetAndClearComparisonCount() {
  int result = num_comparisons_;
  num_comparisons_ = 0;
  return result;
}

}  // namespace

/////////////////////////////////
// MediaFileSystemRegistryTest //
/////////////////////////////////

void MediaFileSystemRegistryTest::CreateProfileState(size_t profile_count) {
  for (size_t i = 0; i < profile_count; ++i) {
    ProfileState* state = new ProfileState(&rph_factory_);
    profile_states_.push_back(state);
  }
}

ProfileState* MediaFileSystemRegistryTest::GetProfileState(size_t i) {
  return profile_states_[i];
}

MediaGalleriesPreferences* MediaFileSystemRegistryTest::GetPreferences(
    Profile* profile) {
  MediaGalleriesPreferences* prefs = registry()->GetPreferences(profile);
  base::RunLoop loop;
  prefs->EnsureInitialized(loop.QuitClosure());
  loop.Run();
  return prefs;
}

std::string MediaFileSystemRegistryTest::AddUserGallery(
    StorageInfo::Type type,
    const std::string& unique_id,
    const base::FilePath& path) {
  std::string device_id = StorageInfo::MakeDeviceId(type, unique_id);
  DCHECK(!StorageInfo::IsMediaDevice(device_id));

  for (size_t i = 0; i < profile_states_.size(); ++i) {
    profile_states_[i]->GetMediaGalleriesPrefs()->AddGallery(
        device_id, base::FilePath(), MediaGalleryPrefInfo::kUserAdded,
        base::string16(), base::string16(), base::string16(), 0,
        base::Time::Now(), 0, 0, 0);
  }
  return device_id;
}

std::string MediaFileSystemRegistryTest::AttachDevice(
    StorageInfo::Type type,
    const std::string& unique_id,
    const base::FilePath& location) {
  std::string device_id = StorageInfo::MakeDeviceId(type, unique_id);
  DCHECK(StorageInfo::IsRemovableDevice(device_id));
  base::string16 label = location.BaseName().LossyDisplayName();
  ProcessAttach(device_id, label, location.value());
  base::MessageLoop::current()->RunUntilIdle();
  return device_id;
}

void MediaFileSystemRegistryTest::DetachDevice(const std::string& device_id) {
  DCHECK(StorageInfo::IsRemovableDevice(device_id));
  ProcessDetach(device_id);
  base::MessageLoop::current()->RunUntilIdle();
}

void MediaFileSystemRegistryTest::SetGalleryPermission(
    ProfileState* profile_state, extensions::Extension* extension,
    const std::string& device_id, bool has_access) {
  MediaGalleriesPreferences* preferences =
      profile_state->GetMediaGalleriesPrefs();
  MediaGalleryPrefIdSet pref_id =
      preferences->LookUpGalleriesByDeviceId(device_id);
  ASSERT_EQ(1U, pref_id.size());
  preferences->SetGalleryPermissionForExtension(*extension, *pref_id.begin(),
                                                has_access);
}

void MediaFileSystemRegistryTest::AssertAllAutoAddedGalleries() {
  for (size_t i = 0; i < profile_states_.size(); ++i) {
    MediaGalleriesPreferences* prefs =
        profile_states_[0]->GetMediaGalleriesPrefs();

    // Make sure that we have at least one gallery and that they are all
    // auto added galleries.
    const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries();
#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
    ASSERT_GT(galleries.size(), 0U);
#endif
    for (MediaGalleriesPrefInfoMap::const_iterator it = galleries.begin();
         it != galleries.end();
         ++it) {
      ASSERT_EQ(MediaGalleryPrefInfo::kAutoDetected, it->second.type);
    }
  }
}

void MediaFileSystemRegistryTest::InitForGalleriesInfoTest(
    FSInfoMap* galleries_info) {
  CreateProfileState(1);
  AssertAllAutoAddedGalleries();

  // Get all existing gallery names.
  ProfileState* profile_state = GetProfileState(0U);
  *galleries_info = profile_state->GetGalleriesInfo(
      profile_state->all_permission_extension());
#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
  ASSERT_EQ(3U, galleries_info->size());
#else
  ASSERT_EQ(0U, galleries_info->size());
#endif
}

void MediaFileSystemRegistryTest::CheckNewGalleryInfo(
    ProfileState* profile_state,
    const FSInfoMap& galleries_info,
    const base::FilePath& location,
    bool removable,
    bool media_device) {
  // Get new galleries.
  FSInfoMap new_galleries_info = profile_state->GetGalleriesInfo(
      profile_state->all_permission_extension());
  ASSERT_EQ(galleries_info.size() + 1U, new_galleries_info.size());

  bool found_new = false;
  for (FSInfoMap::const_iterator it = new_galleries_info.begin();
       it != new_galleries_info.end();
       ++it) {
    if (ContainsKey(galleries_info, it->first))
      continue;

    ASSERT_FALSE(found_new);
    CheckGalleryInfo(it->second, test_file_system_context_, location,
                     removable, media_device);
    found_new = true;
  }
  ASSERT_TRUE(found_new);
}

std::vector<MediaFileSystemInfo>
MediaFileSystemRegistryTest::GetAutoAddedGalleries(
    ProfileState* profile_state) {
  const MediaGalleriesPrefInfoMap& galleries =
      profile_state->GetMediaGalleriesPrefs()->known_galleries();
  std::vector<MediaFileSystemInfo> result;
  for (MediaGalleriesPrefInfoMap::const_iterator it = galleries.begin();
       it != galleries.end();
       ++it) {
    if (it->second.type == MediaGalleryPrefInfo::kAutoDetected) {
      base::FilePath path = it->second.AbsolutePath();
      MediaFileSystemInfo info(path.BaseName().LossyDisplayName(), path,
                               std::string(), 0, std::string(), false, false);
      result.push_back(info);
    }
  }
  std::sort(result.begin(), result.end(), MediaFileSystemInfoComparator);
  return result;
}

size_t MediaFileSystemRegistryTest::GetExtensionGalleriesHostCount(
    const MediaFileSystemRegistry* registry) const {
  size_t extension_galleries_host_count = 0;
  for (MediaFileSystemRegistry::ExtensionGalleriesHostMap::const_iterator it =
           registry->extension_hosts_map_.begin();
       it != registry->extension_hosts_map_.end();
       ++it) {
    extension_galleries_host_count += it->second.size();
  }
  return extension_galleries_host_count;
}


void MediaFileSystemRegistryTest::SetUp() {
  ChromeRenderViewHostTestHarness::SetUp();
  ASSERT_TRUE(TestStorageMonitor::CreateAndInstall());

  DeleteContents();
  SetRenderProcessHostFactory(&rph_factory_);

  test_file_system_context_ = new TestMediaFileSystemContext(
      g_browser_process->media_file_system_registry());

#if defined(OS_CHROMEOS)
  test_user_manager_.reset(new chromeos::ScopedTestUserManager());
#endif

  ASSERT_TRUE(galleries_dir_.CreateUniqueTempDir());
  empty_dir_ = galleries_dir_.path().AppendASCII("empty");
  ASSERT_TRUE(base::CreateDirectory(empty_dir_));
  dcim_dir_ = galleries_dir_.path().AppendASCII("with_dcim");
  ASSERT_TRUE(base::CreateDirectory(dcim_dir_));
  ASSERT_TRUE(base::CreateDirectory(
      dcim_dir_.Append(storage_monitor::kDCIMDirectoryName)));
}

void MediaFileSystemRegistryTest::TearDown() {
  profile_states_.clear();
  MediaFileSystemRegistry* registry =
      g_browser_process->media_file_system_registry();
  EXPECT_EQ(0U, GetExtensionGalleriesHostCount(registry));
  TestStorageMonitor::Destroy();
#if defined(OS_CHROMEOS)
  test_user_manager_.reset();
#endif

  ChromeRenderViewHostTestHarness::TearDown();
}

///////////
// Tests //
///////////

TEST_F(MediaFileSystemRegistryTest, Basic) {
  CreateProfileState(1);
  AssertAllAutoAddedGalleries();

  ProfileState* profile_state = GetProfileState(0);
  std::vector<MediaFileSystemInfo> auto_galleries =
      GetAutoAddedGalleries(profile_state);
  std::vector<MediaFileSystemInfo> empty_expectation;
  profile_state->CheckGalleries("basic", empty_expectation, auto_galleries);
}

TEST_F(MediaFileSystemRegistryTest, UserAddedGallery) {
  CreateProfileState(1);
  AssertAllAutoAddedGalleries();
  ProfileState* profile_state = GetProfileState(0);
  std::vector<MediaFileSystemInfo> auto_galleries =
      GetAutoAddedGalleries(profile_state);
  std::vector<MediaFileSystemInfo> added_galleries;
  profile_state->CheckGalleries("user added init", added_galleries,
                                auto_galleries);

  // Add a user gallery to the regular permission extension.
  std::string device_id = AddUserGallery(StorageInfo::FIXED_MASS_STORAGE,
                                         empty_dir().AsUTF8Unsafe(),
                                         empty_dir());
  SetGalleryPermission(profile_state,
                       profile_state->regular_permission_extension(),
                       device_id,
                       true /*has access*/);
  MediaFileSystemInfo added_info(empty_dir().LossyDisplayName(), empty_dir(),
                                 std::string(), 0, std::string(), false, false);
  added_galleries.push_back(added_info);
  profile_state->CheckGalleries("user added regular", added_galleries,
                                auto_galleries);

  // Add it to the all galleries extension.
  SetGalleryPermission(profile_state,
                       profile_state->all_permission_extension(),
                       device_id,
                       true /*has access*/);
  auto_galleries.push_back(added_info);
  profile_state->CheckGalleries("user added all", added_galleries,
                                auto_galleries);
}

// Regression test to make sure erasing galleries does not result a crash.
TEST_F(MediaFileSystemRegistryTest, EraseGalleries) {
  CreateProfileState(1);
  AssertAllAutoAddedGalleries();

  ProfileState* profile_state = GetProfileState(0);
  std::vector<MediaFileSystemInfo> auto_galleries =
      GetAutoAddedGalleries(profile_state);
  std::vector<MediaFileSystemInfo> empty_expectation;
  profile_state->CheckGalleries("erase", empty_expectation, auto_galleries);

  MediaGalleriesPreferences* prefs = profile_state->GetMediaGalleriesPrefs();
  MediaGalleriesPrefInfoMap galleries = prefs->known_galleries();
  for (MediaGalleriesPrefInfoMap::const_iterator it = galleries.begin();
       it != galleries.end(); ++it) {
    prefs->ForgetGalleryById(it->first);
  }
}

// Regression test to make sure calling GetPreferences() does not re-insert
// galleries on auto-detected removable devices that were blacklisted.
TEST_F(MediaFileSystemRegistryTest,
       GetPreferencesDoesNotReinsertBlacklistedGalleries) {
  CreateProfileState(1);
  AssertAllAutoAddedGalleries();

  ProfileState* profile_state = GetProfileState(0);
  const size_t gallery_count = GetAutoAddedGalleries(profile_state).size();

  // Attach a device.
  const std::string device_id = AttachDevice(
      StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM,
      "removable_dcim_fake_id",
      dcim_dir());
  EXPECT_EQ(gallery_count + 1, GetAutoAddedGalleries(profile_state).size());

  // Forget the device.
  bool forget_gallery = false;
  MediaGalleriesPreferences* prefs = GetPreferences(profile_state->profile());
  const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries();
  for (MediaGalleriesPrefInfoMap::const_iterator it = galleries.begin();
       it != galleries.end(); ++it) {
    if (it->second.device_id == device_id) {
      prefs->ForgetGalleryById(it->first);
      forget_gallery = true;
      break;
    }
  }
  base::MessageLoop::current()->RunUntilIdle();
  EXPECT_TRUE(forget_gallery);
  EXPECT_EQ(gallery_count, GetAutoAddedGalleries(profile_state).size());

  // Call GetPreferences() and the gallery count should not change.
  prefs = GetPreferences(profile_state->profile());
  EXPECT_EQ(gallery_count, GetAutoAddedGalleries(profile_state).size());
}

TEST_F(MediaFileSystemRegistryTest, GalleryNameDefault) {
  FSInfoMap galleries_info;
  InitForGalleriesInfoTest(&galleries_info);

  for (FSInfoMap::const_iterator it = galleries_info.begin();
       it != galleries_info.end();
       ++it) {
    CheckGalleryInfo(it->second, test_file_system_context(),
                     it->second.path, false, false);
  }
}

// TODO(gbillock): Move the remaining test into the linux directory.
#if !defined(OS_MACOSX) && !defined(OS_WIN)
TEST_F(MediaFileSystemRegistryTest, GalleryMTP) {
  FSInfoMap galleries_info;
  InitForGalleriesInfoTest(&galleries_info);

  base::FilePath location(FILE_PATH_LITERAL("/mtp_bogus"));
  AttachDevice(StorageInfo::MTP_OR_PTP, "mtp_fake_id", location);
  CheckNewGalleryInfo(GetProfileState(0U), galleries_info, location,
                      true /*removable*/, true /* media device */);
}
#endif

TEST_F(MediaFileSystemRegistryTest, GalleryDCIM) {
  FSInfoMap galleries_info;
  InitForGalleriesInfoTest(&galleries_info);

  AttachDevice(StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM,
               "removable_dcim_fake_id",
               dcim_dir());
  CheckNewGalleryInfo(GetProfileState(0U), galleries_info, dcim_dir(),
                      true /*removable*/, true /* media device */);
}

TEST_F(MediaFileSystemRegistryTest, GalleryNoDCIM) {
  FSInfoMap galleries_info;
  InitForGalleriesInfoTest(&galleries_info);

  std::string device_id =
      AttachDevice(StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM,
                   empty_dir().AsUTF8Unsafe(),
                   empty_dir());
  std::string device_id2 =
      AddUserGallery(StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM,
                     empty_dir().AsUTF8Unsafe(),
                     empty_dir());
  ASSERT_EQ(device_id, device_id2);
  // Add permission for new non-default gallery.
  ProfileState* profile_state = GetProfileState(0U);
  SetGalleryPermission(profile_state,
                       profile_state->all_permission_extension(),
                       device_id,
                       true /*has access*/);
  CheckNewGalleryInfo(profile_state, galleries_info, empty_dir(),
                      true /*removable*/, false /* media device */);
}

TEST_F(MediaFileSystemRegistryTest, GalleryUserAddedPath) {
  FSInfoMap galleries_info;
  InitForGalleriesInfoTest(&galleries_info);

  std::string device_id = AddUserGallery(StorageInfo::FIXED_MASS_STORAGE,
                                         empty_dir().AsUTF8Unsafe(),
                                         empty_dir());
  // Add permission for new non-default gallery.
  ProfileState* profile_state = GetProfileState(0U);
  SetGalleryPermission(profile_state,
                       profile_state->all_permission_extension(),
                       device_id,
                       true /*has access*/);
  CheckNewGalleryInfo(profile_state, galleries_info, empty_dir(),
                      false /*removable*/, false /* media device */);
}

TEST_F(MediaFileSystemRegistryTest, DetachedDeviceGalleryPath) {
  const std::string device_id = AttachDevice(
      StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM,
      "removable_dcim_fake_id",
      dcim_dir());

  MediaGalleryPrefInfo pref_info;
  pref_info.device_id = device_id;
  EXPECT_EQ(dcim_dir().value(), pref_info.AbsolutePath().value());

  MediaGalleryPrefInfo pref_info_with_relpath;
  pref_info_with_relpath.path =
      base::FilePath(FILE_PATH_LITERAL("test_relpath"));
  pref_info_with_relpath.device_id = device_id;
  EXPECT_EQ(dcim_dir().Append(pref_info_with_relpath.path).value(),
            pref_info_with_relpath.AbsolutePath().value());

  DetachDevice(device_id);
  EXPECT_TRUE(pref_info.AbsolutePath().empty());
  EXPECT_TRUE(pref_info_with_relpath.AbsolutePath().empty());
}

TEST_F(MediaFileSystemRegistryTest, TestNameConstruction) {
  CreateProfileState(1);
  AssertAllAutoAddedGalleries();

  ProfileState* profile_state = GetProfileState(0);

  std::string user_gallery = AddUserGallery(StorageInfo::FIXED_MASS_STORAGE,
                                            empty_dir().AsUTF8Unsafe(),
                                            empty_dir());
  SetGalleryPermission(profile_state,
                       profile_state->regular_permission_extension(),
                       user_gallery,
                       true /*has access*/);
  SetGalleryPermission(profile_state,
                       profile_state->all_permission_extension(),
                       user_gallery,
                       true /*has access*/);

  std::vector<MediaFileSystemInfo> auto_galleries =
      GetAutoAddedGalleries(profile_state);
  MediaFileSystemInfo added_info(empty_dir().BaseName().LossyDisplayName(),
                                 empty_dir(), std::string(), 0, std::string(),
                                 false, false);
  auto_galleries.push_back(added_info);
  std::vector<MediaFileSystemInfo> one_expectation;
  one_expectation.push_back(added_info);

  base::string16 empty_dir_name = GetExpectedFolderName(empty_dir());
  profile_state->AddNameForReadCompare(empty_dir_name);
  profile_state->AddNameForAllCompare(empty_dir_name);

  // This part of the test is conditional on default directories existing
  // on the test platform. In ChromeOS, these directories do not exist.
  base::FilePath path;
  if (num_auto_galleries() > 0) {
    ASSERT_TRUE(PathService::Get(chrome::DIR_USER_MUSIC, &path));
    profile_state->AddNameForAllCompare(GetExpectedFolderName(path));
    ASSERT_TRUE(PathService::Get(chrome::DIR_USER_PICTURES, &path));
    profile_state->AddNameForAllCompare(GetExpectedFolderName(path));
    ASSERT_TRUE(PathService::Get(chrome::DIR_USER_VIDEOS, &path));
    profile_state->AddNameForAllCompare(GetExpectedFolderName(path));

    profile_state->CheckGalleries("names-dir", one_expectation, auto_galleries);
  } else {
    profile_state->CheckGalleries("names", one_expectation, one_expectation);
  }
}

TEST_F(MediaFileSystemRegistryTest, PreferenceListener) {
  CreateProfileState(1);
  AssertAllAutoAddedGalleries();

  // Add a user gallery to the regular permission extension.
  std::string device_id = AddUserGallery(StorageInfo::FIXED_MASS_STORAGE,
                                         empty_dir().AsUTF8Unsafe(),
                                         empty_dir());
  ProfileState* profile_state = GetProfileState(0);
  SetGalleryPermission(profile_state,
                       profile_state->regular_permission_extension(),
                       device_id,
                       true /*has access*/);

  FSInfoMap fs_info = profile_state->GetGalleriesInfo(
      profile_state->regular_permission_extension());
  ASSERT_EQ(1U, fs_info.size());
  EXPECT_FALSE(test_file_system_context()->GetRegisteredPath(
      fs_info.begin()->second.fsid).empty());

  // Revoke permission and ensure that the file system is revoked.
  SetGalleryPermission(profile_state,
                       profile_state->regular_permission_extension(),
                       device_id,
                       false /*has access*/);
  EXPECT_TRUE(test_file_system_context()->GetRegisteredPath(
      fs_info.begin()->second.fsid).empty());
}

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