| // 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/files/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/users/scoped_test_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); |
| ~TestMediaFileSystemContext() override {} |
| |
| // MediaFileSystemContext implementation. |
| bool RegisterFileSystem(const std::string& device_id, |
| const std::string& fs_name, |
| const base::FilePath& path) override; |
| |
| void RevokeFileSystem(const std::string& fs_name) override; |
| |
| 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() {} |
| ~MockProfileSharedRenderProcessHostFactory() override; |
| |
| // 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); |
| |
| 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: |
| void SetUp() override; |
| 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()); |
| } |