1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // MediaFileSystemRegistry unit tests. 6 7 #include <algorithm> 8 #include <set> 9 10 #include "base/bind_helpers.h" 11 #include "base/command_line.h" 12 #include "base/files/file_util.h" 13 #include "base/files/scoped_temp_dir.h" 14 #include "base/memory/ref_counted.h" 15 #include "base/memory/scoped_ptr.h" 16 #include "base/memory/scoped_vector.h" 17 #include "base/message_loop/message_loop.h" 18 #include "base/path_service.h" 19 #include "base/run_loop.h" 20 #include "base/stl_util.h" 21 #include "base/strings/stringprintf.h" 22 #include "base/strings/utf_string_conversions.h" 23 #include "base/threading/sequenced_worker_pool.h" 24 #include "base/values.h" 25 #include "chrome/browser/extensions/extension_service.h" 26 #include "chrome/browser/extensions/test_extension_system.h" 27 #include "chrome/browser/media_galleries/media_file_system_context.h" 28 #include "chrome/browser/media_galleries/media_file_system_registry.h" 29 #include "chrome/browser/media_galleries/media_galleries_preferences_factory.h" 30 #include "chrome/browser/media_galleries/media_galleries_test_util.h" 31 #include "chrome/common/chrome_paths.h" 32 #include "chrome/test/base/chrome_render_view_host_test_harness.h" 33 #include "chrome/test/base/testing_browser_process.h" 34 #include "chrome/test/base/testing_profile.h" 35 #include "components/storage_monitor/removable_device_constants.h" 36 #include "components/storage_monitor/storage_info.h" 37 #include "components/storage_monitor/storage_monitor.h" 38 #include "components/storage_monitor/test_storage_monitor.h" 39 #include "content/public/browser/render_process_host.h" 40 #include "content/public/browser/render_process_host_factory.h" 41 #include "content/public/browser/render_view_host.h" 42 #include "content/public/browser/web_contents.h" 43 #include "content/public/test/mock_render_process_host.h" 44 #include "content/public/test/test_browser_thread.h" 45 #include "content/public/test/web_contents_tester.h" 46 #include "extensions/browser/extension_system.h" 47 #include "extensions/common/extension.h" 48 #include "sync/api/string_ordinal.h" 49 #include "testing/gtest/include/gtest/gtest.h" 50 51 #if defined(OS_CHROMEOS) 52 #include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h" 53 #include "chrome/browser/chromeos/settings/cros_settings.h" 54 #include "chrome/browser/chromeos/settings/device_settings_service.h" 55 #endif 56 57 using content::BrowserThread; 58 using storage_monitor::StorageInfo; 59 using storage_monitor::StorageMonitor; 60 using storage_monitor::TestStorageMonitor; 61 62 // Not anonymous so it can be friends with MediaFileSystemRegistry. 63 class TestMediaFileSystemContext : public MediaFileSystemContext { 64 public: 65 struct FSInfo { 66 FSInfo() {} 67 FSInfo(const std::string& device_id, const base::FilePath& path, 68 const std::string& fs_name); 69 70 bool operator<(const FSInfo& other) const; 71 72 std::string device_id; 73 base::FilePath path; 74 std::string fs_name; 75 }; 76 77 explicit TestMediaFileSystemContext(MediaFileSystemRegistry* registry); 78 virtual ~TestMediaFileSystemContext() {} 79 80 // MediaFileSystemContext implementation. 81 virtual bool RegisterFileSystem(const std::string& device_id, 82 const std::string& fs_name, 83 const base::FilePath& path) OVERRIDE; 84 85 virtual void RevokeFileSystem(const std::string& fs_name) OVERRIDE; 86 87 virtual base::FilePath GetRegisteredPath( 88 const std::string& fs_name) const OVERRIDE; 89 90 MediaFileSystemRegistry* registry() { return registry_; } 91 92 private: 93 void AddFSEntry(const std::string& device_id, 94 const base::FilePath& path, 95 const std::string& fs_name); 96 97 MediaFileSystemRegistry* registry_; 98 99 // The currently allocated mock file systems. 100 std::map<std::string /*fs_name*/, FSInfo> file_systems_by_name_; 101 }; 102 103 TestMediaFileSystemContext::FSInfo::FSInfo(const std::string& device_id, 104 const base::FilePath& path, 105 const std::string& fs_name) 106 : device_id(device_id), 107 path(path), 108 fs_name(fs_name) { 109 } 110 111 bool TestMediaFileSystemContext::FSInfo::operator<(const FSInfo& other) const { 112 if (device_id != other.device_id) 113 return device_id < other.device_id; 114 if (path.value() != other.path.value()) 115 return path.value() < other.path.value(); 116 return fs_name < other.fs_name; 117 } 118 119 TestMediaFileSystemContext::TestMediaFileSystemContext( 120 MediaFileSystemRegistry* registry) 121 : registry_(registry) { 122 registry_->file_system_context_.reset(this); 123 } 124 125 bool TestMediaFileSystemContext::RegisterFileSystem( 126 const std::string& device_id, 127 const std::string& fs_name, 128 const base::FilePath& path) { 129 AddFSEntry(device_id, path, fs_name); 130 return true; 131 } 132 133 void TestMediaFileSystemContext::RevokeFileSystem(const std::string& fs_name) { 134 if (!ContainsKey(file_systems_by_name_, fs_name)) 135 return; 136 EXPECT_EQ(1U, file_systems_by_name_.erase(fs_name)); 137 } 138 139 base::FilePath TestMediaFileSystemContext::GetRegisteredPath( 140 const std::string& fs_name) const { 141 std::map<std::string /*fs_name*/, FSInfo>::const_iterator it = 142 file_systems_by_name_.find(fs_name); 143 if (it == file_systems_by_name_.end()) 144 return base::FilePath(); 145 return it->second.path; 146 } 147 148 void TestMediaFileSystemContext::AddFSEntry(const std::string& device_id, 149 const base::FilePath& path, 150 const std::string& fs_name) { 151 DCHECK_CURRENTLY_ON(BrowserThread::UI); 152 DCHECK(path.IsAbsolute()); 153 DCHECK(!path.ReferencesParent()); 154 155 FSInfo info(device_id, path, fs_name); 156 file_systems_by_name_[fs_name] = info; 157 } 158 159 namespace { 160 161 typedef std::map<MediaGalleryPrefId, MediaFileSystemInfo> FSInfoMap; 162 163 void GetGalleryInfoCallback( 164 FSInfoMap* results, 165 const std::vector<MediaFileSystemInfo>& file_systems) { 166 for (size_t i = 0; i < file_systems.size(); ++i) { 167 ASSERT_FALSE(ContainsKey(*results, file_systems[i].pref_id)); 168 (*results)[file_systems[i].pref_id] = file_systems[i]; 169 } 170 } 171 172 void CheckGalleryInfo(const MediaFileSystemInfo& info, 173 TestMediaFileSystemContext* fs_context, 174 const base::FilePath& path, 175 bool removable, 176 bool media_device) { 177 EXPECT_EQ(path, info.path); 178 EXPECT_EQ(removable, info.removable); 179 EXPECT_EQ(media_device, info.media_device); 180 EXPECT_NE(0UL, info.pref_id); 181 182 if (removable) 183 EXPECT_NE(0UL, info.transient_device_id.size()); 184 else 185 EXPECT_EQ(0UL, info.transient_device_id.size()); 186 187 base::FilePath fsid_path = fs_context->GetRegisteredPath(info.fsid); 188 EXPECT_EQ(path, fsid_path); 189 } 190 191 class MockProfileSharedRenderProcessHostFactory 192 : public content::RenderProcessHostFactory { 193 public: 194 MockProfileSharedRenderProcessHostFactory() {} 195 virtual ~MockProfileSharedRenderProcessHostFactory(); 196 197 // RPH created with this factory are owned by it. If the RPH is destroyed 198 // for testing purposes, it must be removed from the factory first. 199 content::MockRenderProcessHost* ReleaseRPH( 200 content::BrowserContext* browser_context); 201 202 virtual content::RenderProcessHost* CreateRenderProcessHost( 203 content::BrowserContext* browser_context, 204 content::SiteInstance* site_instance) const OVERRIDE; 205 206 private: 207 typedef std::map<content::BrowserContext*, content::MockRenderProcessHost*> 208 ProfileRPHMap; 209 mutable ProfileRPHMap rph_map_; 210 211 DISALLOW_COPY_AND_ASSIGN(MockProfileSharedRenderProcessHostFactory); 212 }; 213 214 class ProfileState { 215 public: 216 explicit ProfileState( 217 MockProfileSharedRenderProcessHostFactory* rph_factory); 218 ~ProfileState(); 219 220 MediaGalleriesPreferences* GetMediaGalleriesPrefs(); 221 222 void CheckGalleries( 223 const std::string& test, 224 const std::vector<MediaFileSystemInfo>& regular_extension_galleries, 225 const std::vector<MediaFileSystemInfo>& all_extension_galleries); 226 227 FSInfoMap GetGalleriesInfo(extensions::Extension* extension); 228 229 extensions::Extension* all_permission_extension(); 230 extensions::Extension* regular_permission_extension(); 231 Profile* profile(); 232 233 void AddNameForReadCompare(const base::string16& name); 234 void AddNameForAllCompare(const base::string16& name); 235 236 private: 237 void CompareResults(const std::string& test, 238 const std::vector<base::string16>& names, 239 const std::vector<MediaFileSystemInfo>& expected, 240 const std::vector<MediaFileSystemInfo>& actual); 241 bool ContainsEntry(const MediaFileSystemInfo& info, 242 const std::vector<MediaFileSystemInfo>& container); 243 244 int GetAndClearComparisonCount(); 245 246 int num_comparisons_; 247 248 scoped_ptr<TestingProfile> profile_; 249 250 scoped_refptr<extensions::Extension> all_permission_extension_; 251 scoped_refptr<extensions::Extension> regular_permission_extension_; 252 scoped_refptr<extensions::Extension> no_permissions_extension_; 253 254 scoped_ptr<content::WebContents> single_web_contents_; 255 scoped_ptr<content::WebContents> shared_web_contents1_; 256 scoped_ptr<content::WebContents> shared_web_contents2_; 257 258 // The RenderProcessHosts are freed when their respective WebContents / 259 // RenderViewHosts go away. 260 content::MockRenderProcessHost* single_rph_; 261 content::MockRenderProcessHost* shared_rph_; 262 263 std::vector<base::string16> compare_names_read_; 264 std::vector<base::string16> compare_names_all_; 265 266 DISALLOW_COPY_AND_ASSIGN(ProfileState); 267 }; 268 269 base::string16 GetExpectedFolderName(const base::FilePath& path) { 270 #if defined(OS_CHROMEOS) 271 return path.BaseName().LossyDisplayName(); 272 #else 273 return path.LossyDisplayName(); 274 #endif 275 } 276 277 } // namespace 278 279 class MediaFileSystemRegistryTest : public ChromeRenderViewHostTestHarness { 280 public: 281 void CreateProfileState(size_t profile_count); 282 283 ProfileState* GetProfileState(size_t i); 284 285 MediaGalleriesPreferences* GetPreferences(Profile* profile); 286 287 base::FilePath empty_dir() { 288 return empty_dir_; 289 } 290 291 base::FilePath dcim_dir() { 292 return dcim_dir_; 293 } 294 295 TestMediaFileSystemContext* test_file_system_context() { 296 return test_file_system_context_; 297 } 298 299 // Create a user added gallery based on the information passed and add it to 300 // |profiles|. Returns the device id. 301 std::string AddUserGallery(StorageInfo::Type type, 302 const std::string& unique_id, 303 const base::FilePath& path); 304 305 // Returns the device id. 306 std::string AttachDevice(StorageInfo::Type type, 307 const std::string& unique_id, 308 const base::FilePath& location); 309 310 void DetachDevice(const std::string& device_id); 311 312 void SetGalleryPermission(ProfileState* profile_state, 313 extensions::Extension* extension, 314 const std::string& device_id, 315 bool has_access); 316 317 void AssertAllAutoAddedGalleries(); 318 319 void InitForGalleriesInfoTest(FSInfoMap* galleries_info); 320 321 void CheckNewGalleryInfo(ProfileState* profile_state, 322 const FSInfoMap& galleries_info, 323 const base::FilePath& location, 324 bool removable, 325 bool media_device); 326 327 std::vector<MediaFileSystemInfo> GetAutoAddedGalleries( 328 ProfileState* profile_state); 329 330 void ProcessAttach(const std::string& id, 331 const base::string16& name, 332 const base::FilePath::StringType& location) { 333 StorageInfo info(id, location, name, base::string16(), base::string16(), 0); 334 StorageMonitor::GetInstance()->receiver()->ProcessAttach(info); 335 } 336 337 void ProcessDetach(const std::string& id) { 338 StorageMonitor::GetInstance()->receiver()->ProcessDetach(id); 339 } 340 341 MediaFileSystemRegistry* registry() { 342 return test_file_system_context_->registry(); 343 } 344 345 size_t GetExtensionGalleriesHostCount( 346 const MediaFileSystemRegistry* registry) const; 347 348 int num_auto_galleries() { 349 return media_directories_.num_galleries(); 350 } 351 352 protected: 353 virtual void SetUp() OVERRIDE; 354 virtual void TearDown() OVERRIDE; 355 356 private: 357 // This makes sure that at least one default gallery exists on the file 358 // system. 359 EnsureMediaDirectoriesExists media_directories_; 360 361 // Some test gallery directories. 362 base::ScopedTempDir galleries_dir_; 363 // An empty directory in |galleries_dir_| 364 base::FilePath empty_dir_; 365 // A directory in |galleries_dir_| with a DCIM directory in it. 366 base::FilePath dcim_dir_; 367 368 // MediaFileSystemRegistry owns this. 369 TestMediaFileSystemContext* test_file_system_context_; 370 371 // Needed for extension service & friends to work. 372 373 #if defined(OS_CHROMEOS) 374 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; 375 chromeos::ScopedTestCrosSettings test_cros_settings_; 376 scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_; 377 #endif 378 379 MockProfileSharedRenderProcessHostFactory rph_factory_; 380 381 ScopedVector<ProfileState> profile_states_; 382 }; 383 384 namespace { 385 386 bool MediaFileSystemInfoComparator(const MediaFileSystemInfo& a, 387 const MediaFileSystemInfo& b) { 388 CHECK_NE(a.name, b.name); // Name must be unique. 389 return a.name < b.name; 390 } 391 392 /////////////////////////////////////////////// 393 // MockProfileSharedRenderProcessHostFactory // 394 /////////////////////////////////////////////// 395 396 MockProfileSharedRenderProcessHostFactory:: 397 ~MockProfileSharedRenderProcessHostFactory() { 398 STLDeleteValues(&rph_map_); 399 } 400 401 content::MockRenderProcessHost* 402 MockProfileSharedRenderProcessHostFactory::ReleaseRPH( 403 content::BrowserContext* browser_context) { 404 ProfileRPHMap::iterator existing = rph_map_.find(browser_context); 405 if (existing == rph_map_.end()) 406 return NULL; 407 content::MockRenderProcessHost* result = existing->second; 408 rph_map_.erase(existing); 409 return result; 410 } 411 412 content::RenderProcessHost* 413 MockProfileSharedRenderProcessHostFactory::CreateRenderProcessHost( 414 content::BrowserContext* browser_context, 415 content::SiteInstance* site_instance) const { 416 ProfileRPHMap::const_iterator existing = rph_map_.find(browser_context); 417 if (existing != rph_map_.end()) 418 return existing->second; 419 rph_map_[browser_context] = 420 new content::MockRenderProcessHost(browser_context); 421 return rph_map_[browser_context]; 422 } 423 424 ////////////////// 425 // ProfileState // 426 ////////////////// 427 428 ProfileState::ProfileState( 429 MockProfileSharedRenderProcessHostFactory* rph_factory) 430 : num_comparisons_(0), 431 profile_(new TestingProfile()) { 432 extensions::TestExtensionSystem* extension_system( 433 static_cast<extensions::TestExtensionSystem*>( 434 extensions::ExtensionSystem::Get(profile_.get()))); 435 extension_system->CreateExtensionService( 436 CommandLine::ForCurrentProcess(), base::FilePath(), false); 437 438 std::vector<std::string> all_permissions; 439 all_permissions.push_back("allAutoDetected"); 440 all_permissions.push_back("read"); 441 std::vector<std::string> read_permissions; 442 read_permissions.push_back("read"); 443 444 all_permission_extension_ = 445 AddMediaGalleriesApp("all", all_permissions, profile_.get()); 446 regular_permission_extension_ = 447 AddMediaGalleriesApp("regular", read_permissions, profile_.get()); 448 no_permissions_extension_ = 449 AddMediaGalleriesApp("no", read_permissions, profile_.get()); 450 451 single_web_contents_.reset( 452 content::WebContentsTester::CreateTestWebContents(profile_.get(), NULL)); 453 single_rph_ = rph_factory->ReleaseRPH(profile_.get()); 454 455 shared_web_contents1_.reset( 456 content::WebContentsTester::CreateTestWebContents(profile_.get(), NULL)); 457 shared_web_contents2_.reset( 458 content::WebContentsTester::CreateTestWebContents(profile_.get(), NULL)); 459 shared_rph_ = rph_factory->ReleaseRPH(profile_.get()); 460 } 461 462 ProfileState::~ProfileState() { 463 // TestExtensionSystem uses DeleteSoon, so we need to delete the profiles 464 // and then run the message queue to clean up. But first we have to 465 // delete everything that references the profile. 466 single_web_contents_.reset(); 467 shared_web_contents1_.reset(); 468 shared_web_contents2_.reset(); 469 profile_.reset(); 470 471 base::MessageLoop::current()->RunUntilIdle(); 472 } 473 474 MediaGalleriesPreferences* ProfileState::GetMediaGalleriesPrefs() { 475 MediaGalleriesPreferences* prefs = 476 MediaGalleriesPreferencesFactory::GetForProfile(profile_.get()); 477 base::RunLoop loop; 478 prefs->EnsureInitialized(loop.QuitClosure()); 479 loop.Run(); 480 return prefs; 481 } 482 483 void ProfileState::CheckGalleries( 484 const std::string& test, 485 const std::vector<MediaFileSystemInfo>& regular_extension_galleries, 486 const std::vector<MediaFileSystemInfo>& all_extension_galleries) { 487 content::RenderViewHost* rvh = single_web_contents_->GetRenderViewHost(); 488 MediaFileSystemRegistry* registry = 489 g_browser_process->media_file_system_registry(); 490 491 // No Media Galleries permissions. 492 std::vector<MediaFileSystemInfo> empty_expectation; 493 std::vector<base::string16> empty_names; 494 registry->GetMediaFileSystemsForExtension( 495 rvh, no_permissions_extension_.get(), 496 base::Bind(&ProfileState::CompareResults, base::Unretained(this), 497 base::StringPrintf("%s (no permission)", test.c_str()), 498 base::ConstRef(empty_names), 499 base::ConstRef(empty_expectation))); 500 base::MessageLoop::current()->RunUntilIdle(); 501 EXPECT_EQ(1, GetAndClearComparisonCount()); 502 503 // Read permission only. 504 registry->GetMediaFileSystemsForExtension( 505 rvh, regular_permission_extension_.get(), 506 base::Bind(&ProfileState::CompareResults, base::Unretained(this), 507 base::StringPrintf("%s (regular permission)", test.c_str()), 508 base::ConstRef(compare_names_read_), 509 base::ConstRef(regular_extension_galleries))); 510 base::MessageLoop::current()->RunUntilIdle(); 511 EXPECT_EQ(1, GetAndClearComparisonCount()); 512 513 // All galleries permission. 514 registry->GetMediaFileSystemsForExtension( 515 rvh, all_permission_extension_.get(), 516 base::Bind(&ProfileState::CompareResults, base::Unretained(this), 517 base::StringPrintf("%s (all permission)", test.c_str()), 518 base::ConstRef(compare_names_all_), 519 base::ConstRef(all_extension_galleries))); 520 base::MessageLoop::current()->RunUntilIdle(); 521 EXPECT_EQ(1, GetAndClearComparisonCount()); 522 } 523 524 FSInfoMap ProfileState::GetGalleriesInfo(extensions::Extension* extension) { 525 content::RenderViewHost* rvh = single_web_contents_->GetRenderViewHost(); 526 FSInfoMap results; 527 MediaFileSystemRegistry* registry = 528 g_browser_process->media_file_system_registry(); 529 registry->GetMediaFileSystemsForExtension( 530 rvh, extension, 531 base::Bind(&GetGalleryInfoCallback, base::Unretained(&results))); 532 base::MessageLoop::current()->RunUntilIdle(); 533 return results; 534 } 535 536 extensions::Extension* ProfileState::all_permission_extension() { 537 return all_permission_extension_.get(); 538 } 539 540 extensions::Extension* ProfileState::regular_permission_extension() { 541 return regular_permission_extension_.get(); 542 } 543 544 Profile* ProfileState::profile() { 545 return profile_.get(); 546 } 547 548 void ProfileState::AddNameForReadCompare(const base::string16& name) { 549 compare_names_read_.push_back(name); 550 } 551 552 void ProfileState::AddNameForAllCompare(const base::string16& name) { 553 compare_names_all_.push_back(name); 554 } 555 556 bool ProfileState::ContainsEntry( 557 const MediaFileSystemInfo& info, 558 const std::vector<MediaFileSystemInfo>& container) { 559 for (size_t i = 0; i < container.size(); ++i) { 560 if (info.path.value() == container[i].path.value()) { 561 EXPECT_FALSE(container[i].fsid.empty()); 562 if (!info.fsid.empty()) 563 EXPECT_EQ(info.fsid, container[i].fsid); 564 return true; 565 } 566 } 567 return false; 568 } 569 570 void ProfileState::CompareResults( 571 const std::string& test, 572 const std::vector<base::string16>& names, 573 const std::vector<MediaFileSystemInfo>& expected, 574 const std::vector<MediaFileSystemInfo>& actual) { 575 num_comparisons_++; 576 EXPECT_EQ(expected.size(), actual.size()) << test; 577 578 // Order isn't important, so sort the results. 579 std::vector<MediaFileSystemInfo> sorted(actual); 580 std::sort(sorted.begin(), sorted.end(), MediaFileSystemInfoComparator); 581 std::vector<MediaFileSystemInfo> expect(expected); 582 std::sort(expect.begin(), expect.end(), MediaFileSystemInfoComparator); 583 std::vector<base::string16> expect_names(names); 584 std::sort(expect_names.begin(), expect_names.end()); 585 586 for (size_t i = 0; i < expect.size() && i < sorted.size(); ++i) { 587 if (expect_names.size() > i) 588 EXPECT_EQ(expect_names[i], sorted[i].name) << test; 589 EXPECT_TRUE(ContainsEntry(expect[i], sorted)) << test; 590 } 591 } 592 593 int ProfileState::GetAndClearComparisonCount() { 594 int result = num_comparisons_; 595 num_comparisons_ = 0; 596 return result; 597 } 598 599 } // namespace 600 601 ///////////////////////////////// 602 // MediaFileSystemRegistryTest // 603 ///////////////////////////////// 604 605 void MediaFileSystemRegistryTest::CreateProfileState(size_t profile_count) { 606 for (size_t i = 0; i < profile_count; ++i) { 607 ProfileState* state = new ProfileState(&rph_factory_); 608 profile_states_.push_back(state); 609 } 610 } 611 612 ProfileState* MediaFileSystemRegistryTest::GetProfileState(size_t i) { 613 return profile_states_[i]; 614 } 615 616 MediaGalleriesPreferences* MediaFileSystemRegistryTest::GetPreferences( 617 Profile* profile) { 618 MediaGalleriesPreferences* prefs = registry()->GetPreferences(profile); 619 base::RunLoop loop; 620 prefs->EnsureInitialized(loop.QuitClosure()); 621 loop.Run(); 622 return prefs; 623 } 624 625 std::string MediaFileSystemRegistryTest::AddUserGallery( 626 StorageInfo::Type type, 627 const std::string& unique_id, 628 const base::FilePath& path) { 629 std::string device_id = StorageInfo::MakeDeviceId(type, unique_id); 630 DCHECK(!StorageInfo::IsMediaDevice(device_id)); 631 632 for (size_t i = 0; i < profile_states_.size(); ++i) { 633 profile_states_[i]->GetMediaGalleriesPrefs()->AddGallery( 634 device_id, base::FilePath(), MediaGalleryPrefInfo::kUserAdded, 635 base::string16(), base::string16(), base::string16(), 0, 636 base::Time::Now(), 0, 0, 0); 637 } 638 return device_id; 639 } 640 641 std::string MediaFileSystemRegistryTest::AttachDevice( 642 StorageInfo::Type type, 643 const std::string& unique_id, 644 const base::FilePath& location) { 645 std::string device_id = StorageInfo::MakeDeviceId(type, unique_id); 646 DCHECK(StorageInfo::IsRemovableDevice(device_id)); 647 base::string16 label = location.BaseName().LossyDisplayName(); 648 ProcessAttach(device_id, label, location.value()); 649 base::MessageLoop::current()->RunUntilIdle(); 650 return device_id; 651 } 652 653 void MediaFileSystemRegistryTest::DetachDevice(const std::string& device_id) { 654 DCHECK(StorageInfo::IsRemovableDevice(device_id)); 655 ProcessDetach(device_id); 656 base::MessageLoop::current()->RunUntilIdle(); 657 } 658 659 void MediaFileSystemRegistryTest::SetGalleryPermission( 660 ProfileState* profile_state, extensions::Extension* extension, 661 const std::string& device_id, bool has_access) { 662 MediaGalleriesPreferences* preferences = 663 profile_state->GetMediaGalleriesPrefs(); 664 MediaGalleryPrefIdSet pref_id = 665 preferences->LookUpGalleriesByDeviceId(device_id); 666 ASSERT_EQ(1U, pref_id.size()); 667 preferences->SetGalleryPermissionForExtension(*extension, *pref_id.begin(), 668 has_access); 669 } 670 671 void MediaFileSystemRegistryTest::AssertAllAutoAddedGalleries() { 672 for (size_t i = 0; i < profile_states_.size(); ++i) { 673 MediaGalleriesPreferences* prefs = 674 profile_states_[0]->GetMediaGalleriesPrefs(); 675 676 // Make sure that we have at least one gallery and that they are all 677 // auto added galleries. 678 const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries(); 679 #if !defined(OS_CHROMEOS) && !defined(OS_ANDROID) 680 ASSERT_GT(galleries.size(), 0U); 681 #endif 682 for (MediaGalleriesPrefInfoMap::const_iterator it = galleries.begin(); 683 it != galleries.end(); 684 ++it) { 685 ASSERT_EQ(MediaGalleryPrefInfo::kAutoDetected, it->second.type); 686 } 687 } 688 } 689 690 void MediaFileSystemRegistryTest::InitForGalleriesInfoTest( 691 FSInfoMap* galleries_info) { 692 CreateProfileState(1); 693 AssertAllAutoAddedGalleries(); 694 695 // Get all existing gallery names. 696 ProfileState* profile_state = GetProfileState(0U); 697 *galleries_info = profile_state->GetGalleriesInfo( 698 profile_state->all_permission_extension()); 699 #if !defined(OS_CHROMEOS) && !defined(OS_ANDROID) 700 ASSERT_EQ(3U, galleries_info->size()); 701 #else 702 ASSERT_EQ(0U, galleries_info->size()); 703 #endif 704 } 705 706 void MediaFileSystemRegistryTest::CheckNewGalleryInfo( 707 ProfileState* profile_state, 708 const FSInfoMap& galleries_info, 709 const base::FilePath& location, 710 bool removable, 711 bool media_device) { 712 // Get new galleries. 713 FSInfoMap new_galleries_info = profile_state->GetGalleriesInfo( 714 profile_state->all_permission_extension()); 715 ASSERT_EQ(galleries_info.size() + 1U, new_galleries_info.size()); 716 717 bool found_new = false; 718 for (FSInfoMap::const_iterator it = new_galleries_info.begin(); 719 it != new_galleries_info.end(); 720 ++it) { 721 if (ContainsKey(galleries_info, it->first)) 722 continue; 723 724 ASSERT_FALSE(found_new); 725 CheckGalleryInfo(it->second, test_file_system_context_, location, 726 removable, media_device); 727 found_new = true; 728 } 729 ASSERT_TRUE(found_new); 730 } 731 732 std::vector<MediaFileSystemInfo> 733 MediaFileSystemRegistryTest::GetAutoAddedGalleries( 734 ProfileState* profile_state) { 735 const MediaGalleriesPrefInfoMap& galleries = 736 profile_state->GetMediaGalleriesPrefs()->known_galleries(); 737 std::vector<MediaFileSystemInfo> result; 738 for (MediaGalleriesPrefInfoMap::const_iterator it = galleries.begin(); 739 it != galleries.end(); 740 ++it) { 741 if (it->second.type == MediaGalleryPrefInfo::kAutoDetected) { 742 base::FilePath path = it->second.AbsolutePath(); 743 MediaFileSystemInfo info(path.BaseName().LossyDisplayName(), path, 744 std::string(), 0, std::string(), false, false); 745 result.push_back(info); 746 } 747 } 748 std::sort(result.begin(), result.end(), MediaFileSystemInfoComparator); 749 return result; 750 } 751 752 size_t MediaFileSystemRegistryTest::GetExtensionGalleriesHostCount( 753 const MediaFileSystemRegistry* registry) const { 754 size_t extension_galleries_host_count = 0; 755 for (MediaFileSystemRegistry::ExtensionGalleriesHostMap::const_iterator it = 756 registry->extension_hosts_map_.begin(); 757 it != registry->extension_hosts_map_.end(); 758 ++it) { 759 extension_galleries_host_count += it->second.size(); 760 } 761 return extension_galleries_host_count; 762 } 763 764 765 void MediaFileSystemRegistryTest::SetUp() { 766 ChromeRenderViewHostTestHarness::SetUp(); 767 ASSERT_TRUE(TestStorageMonitor::CreateAndInstall()); 768 769 DeleteContents(); 770 SetRenderProcessHostFactory(&rph_factory_); 771 772 test_file_system_context_ = new TestMediaFileSystemContext( 773 g_browser_process->media_file_system_registry()); 774 775 #if defined(OS_CHROMEOS) 776 test_user_manager_.reset(new chromeos::ScopedTestUserManager()); 777 #endif 778 779 ASSERT_TRUE(galleries_dir_.CreateUniqueTempDir()); 780 empty_dir_ = galleries_dir_.path().AppendASCII("empty"); 781 ASSERT_TRUE(base::CreateDirectory(empty_dir_)); 782 dcim_dir_ = galleries_dir_.path().AppendASCII("with_dcim"); 783 ASSERT_TRUE(base::CreateDirectory(dcim_dir_)); 784 ASSERT_TRUE(base::CreateDirectory( 785 dcim_dir_.Append(storage_monitor::kDCIMDirectoryName))); 786 } 787 788 void MediaFileSystemRegistryTest::TearDown() { 789 profile_states_.clear(); 790 MediaFileSystemRegistry* registry = 791 g_browser_process->media_file_system_registry(); 792 EXPECT_EQ(0U, GetExtensionGalleriesHostCount(registry)); 793 TestStorageMonitor::Destroy(); 794 #if defined(OS_CHROMEOS) 795 test_user_manager_.reset(); 796 #endif 797 798 ChromeRenderViewHostTestHarness::TearDown(); 799 } 800 801 /////////// 802 // Tests // 803 /////////// 804 805 TEST_F(MediaFileSystemRegistryTest, Basic) { 806 CreateProfileState(1); 807 AssertAllAutoAddedGalleries(); 808 809 ProfileState* profile_state = GetProfileState(0); 810 std::vector<MediaFileSystemInfo> auto_galleries = 811 GetAutoAddedGalleries(profile_state); 812 std::vector<MediaFileSystemInfo> empty_expectation; 813 profile_state->CheckGalleries("basic", empty_expectation, auto_galleries); 814 } 815 816 TEST_F(MediaFileSystemRegistryTest, UserAddedGallery) { 817 CreateProfileState(1); 818 AssertAllAutoAddedGalleries(); 819 ProfileState* profile_state = GetProfileState(0); 820 std::vector<MediaFileSystemInfo> auto_galleries = 821 GetAutoAddedGalleries(profile_state); 822 std::vector<MediaFileSystemInfo> added_galleries; 823 profile_state->CheckGalleries("user added init", added_galleries, 824 auto_galleries); 825 826 // Add a user gallery to the regular permission extension. 827 std::string device_id = AddUserGallery(StorageInfo::FIXED_MASS_STORAGE, 828 empty_dir().AsUTF8Unsafe(), 829 empty_dir()); 830 SetGalleryPermission(profile_state, 831 profile_state->regular_permission_extension(), 832 device_id, 833 true /*has access*/); 834 MediaFileSystemInfo added_info(empty_dir().LossyDisplayName(), empty_dir(), 835 std::string(), 0, std::string(), false, false); 836 added_galleries.push_back(added_info); 837 profile_state->CheckGalleries("user added regular", added_galleries, 838 auto_galleries); 839 840 // Add it to the all galleries extension. 841 SetGalleryPermission(profile_state, 842 profile_state->all_permission_extension(), 843 device_id, 844 true /*has access*/); 845 auto_galleries.push_back(added_info); 846 profile_state->CheckGalleries("user added all", added_galleries, 847 auto_galleries); 848 } 849 850 // Regression test to make sure erasing galleries does not result a crash. 851 TEST_F(MediaFileSystemRegistryTest, EraseGalleries) { 852 CreateProfileState(1); 853 AssertAllAutoAddedGalleries(); 854 855 ProfileState* profile_state = GetProfileState(0); 856 std::vector<MediaFileSystemInfo> auto_galleries = 857 GetAutoAddedGalleries(profile_state); 858 std::vector<MediaFileSystemInfo> empty_expectation; 859 profile_state->CheckGalleries("erase", empty_expectation, auto_galleries); 860 861 MediaGalleriesPreferences* prefs = profile_state->GetMediaGalleriesPrefs(); 862 MediaGalleriesPrefInfoMap galleries = prefs->known_galleries(); 863 for (MediaGalleriesPrefInfoMap::const_iterator it = galleries.begin(); 864 it != galleries.end(); ++it) { 865 prefs->ForgetGalleryById(it->first); 866 } 867 } 868 869 // Regression test to make sure calling GetPreferences() does not re-insert 870 // galleries on auto-detected removable devices that were blacklisted. 871 TEST_F(MediaFileSystemRegistryTest, 872 GetPreferencesDoesNotReinsertBlacklistedGalleries) { 873 CreateProfileState(1); 874 AssertAllAutoAddedGalleries(); 875 876 ProfileState* profile_state = GetProfileState(0); 877 const size_t gallery_count = GetAutoAddedGalleries(profile_state).size(); 878 879 // Attach a device. 880 const std::string device_id = AttachDevice( 881 StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM, 882 "removable_dcim_fake_id", 883 dcim_dir()); 884 EXPECT_EQ(gallery_count + 1, GetAutoAddedGalleries(profile_state).size()); 885 886 // Forget the device. 887 bool forget_gallery = false; 888 MediaGalleriesPreferences* prefs = GetPreferences(profile_state->profile()); 889 const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries(); 890 for (MediaGalleriesPrefInfoMap::const_iterator it = galleries.begin(); 891 it != galleries.end(); ++it) { 892 if (it->second.device_id == device_id) { 893 prefs->ForgetGalleryById(it->first); 894 forget_gallery = true; 895 break; 896 } 897 } 898 base::MessageLoop::current()->RunUntilIdle(); 899 EXPECT_TRUE(forget_gallery); 900 EXPECT_EQ(gallery_count, GetAutoAddedGalleries(profile_state).size()); 901 902 // Call GetPreferences() and the gallery count should not change. 903 prefs = GetPreferences(profile_state->profile()); 904 EXPECT_EQ(gallery_count, GetAutoAddedGalleries(profile_state).size()); 905 } 906 907 TEST_F(MediaFileSystemRegistryTest, GalleryNameDefault) { 908 FSInfoMap galleries_info; 909 InitForGalleriesInfoTest(&galleries_info); 910 911 for (FSInfoMap::const_iterator it = galleries_info.begin(); 912 it != galleries_info.end(); 913 ++it) { 914 CheckGalleryInfo(it->second, test_file_system_context(), 915 it->second.path, false, false); 916 } 917 } 918 919 // TODO(gbillock): Move the remaining test into the linux directory. 920 #if !defined(OS_MACOSX) && !defined(OS_WIN) 921 TEST_F(MediaFileSystemRegistryTest, GalleryMTP) { 922 FSInfoMap galleries_info; 923 InitForGalleriesInfoTest(&galleries_info); 924 925 base::FilePath location(FILE_PATH_LITERAL("/mtp_bogus")); 926 AttachDevice(StorageInfo::MTP_OR_PTP, "mtp_fake_id", location); 927 CheckNewGalleryInfo(GetProfileState(0U), galleries_info, location, 928 true /*removable*/, true /* media device */); 929 } 930 #endif 931 932 TEST_F(MediaFileSystemRegistryTest, GalleryDCIM) { 933 FSInfoMap galleries_info; 934 InitForGalleriesInfoTest(&galleries_info); 935 936 AttachDevice(StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM, 937 "removable_dcim_fake_id", 938 dcim_dir()); 939 CheckNewGalleryInfo(GetProfileState(0U), galleries_info, dcim_dir(), 940 true /*removable*/, true /* media device */); 941 } 942 943 TEST_F(MediaFileSystemRegistryTest, GalleryNoDCIM) { 944 FSInfoMap galleries_info; 945 InitForGalleriesInfoTest(&galleries_info); 946 947 std::string device_id = 948 AttachDevice(StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM, 949 empty_dir().AsUTF8Unsafe(), 950 empty_dir()); 951 std::string device_id2 = 952 AddUserGallery(StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM, 953 empty_dir().AsUTF8Unsafe(), 954 empty_dir()); 955 ASSERT_EQ(device_id, device_id2); 956 // Add permission for new non-default gallery. 957 ProfileState* profile_state = GetProfileState(0U); 958 SetGalleryPermission(profile_state, 959 profile_state->all_permission_extension(), 960 device_id, 961 true /*has access*/); 962 CheckNewGalleryInfo(profile_state, galleries_info, empty_dir(), 963 true /*removable*/, false /* media device */); 964 } 965 966 TEST_F(MediaFileSystemRegistryTest, GalleryUserAddedPath) { 967 FSInfoMap galleries_info; 968 InitForGalleriesInfoTest(&galleries_info); 969 970 std::string device_id = AddUserGallery(StorageInfo::FIXED_MASS_STORAGE, 971 empty_dir().AsUTF8Unsafe(), 972 empty_dir()); 973 // Add permission for new non-default gallery. 974 ProfileState* profile_state = GetProfileState(0U); 975 SetGalleryPermission(profile_state, 976 profile_state->all_permission_extension(), 977 device_id, 978 true /*has access*/); 979 CheckNewGalleryInfo(profile_state, galleries_info, empty_dir(), 980 false /*removable*/, false /* media device */); 981 } 982 983 TEST_F(MediaFileSystemRegistryTest, DetachedDeviceGalleryPath) { 984 const std::string device_id = AttachDevice( 985 StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM, 986 "removable_dcim_fake_id", 987 dcim_dir()); 988 989 MediaGalleryPrefInfo pref_info; 990 pref_info.device_id = device_id; 991 EXPECT_EQ(dcim_dir().value(), pref_info.AbsolutePath().value()); 992 993 MediaGalleryPrefInfo pref_info_with_relpath; 994 pref_info_with_relpath.path = 995 base::FilePath(FILE_PATH_LITERAL("test_relpath")); 996 pref_info_with_relpath.device_id = device_id; 997 EXPECT_EQ(dcim_dir().Append(pref_info_with_relpath.path).value(), 998 pref_info_with_relpath.AbsolutePath().value()); 999 1000 DetachDevice(device_id); 1001 EXPECT_TRUE(pref_info.AbsolutePath().empty()); 1002 EXPECT_TRUE(pref_info_with_relpath.AbsolutePath().empty()); 1003 } 1004 1005 TEST_F(MediaFileSystemRegistryTest, TestNameConstruction) { 1006 CreateProfileState(1); 1007 AssertAllAutoAddedGalleries(); 1008 1009 ProfileState* profile_state = GetProfileState(0); 1010 1011 std::string user_gallery = AddUserGallery(StorageInfo::FIXED_MASS_STORAGE, 1012 empty_dir().AsUTF8Unsafe(), 1013 empty_dir()); 1014 SetGalleryPermission(profile_state, 1015 profile_state->regular_permission_extension(), 1016 user_gallery, 1017 true /*has access*/); 1018 SetGalleryPermission(profile_state, 1019 profile_state->all_permission_extension(), 1020 user_gallery, 1021 true /*has access*/); 1022 1023 std::vector<MediaFileSystemInfo> auto_galleries = 1024 GetAutoAddedGalleries(profile_state); 1025 MediaFileSystemInfo added_info(empty_dir().BaseName().LossyDisplayName(), 1026 empty_dir(), std::string(), 0, std::string(), 1027 false, false); 1028 auto_galleries.push_back(added_info); 1029 std::vector<MediaFileSystemInfo> one_expectation; 1030 one_expectation.push_back(added_info); 1031 1032 base::string16 empty_dir_name = GetExpectedFolderName(empty_dir()); 1033 profile_state->AddNameForReadCompare(empty_dir_name); 1034 profile_state->AddNameForAllCompare(empty_dir_name); 1035 1036 // This part of the test is conditional on default directories existing 1037 // on the test platform. In ChromeOS, these directories do not exist. 1038 base::FilePath path; 1039 if (num_auto_galleries() > 0) { 1040 ASSERT_TRUE(PathService::Get(chrome::DIR_USER_MUSIC, &path)); 1041 profile_state->AddNameForAllCompare(GetExpectedFolderName(path)); 1042 ASSERT_TRUE(PathService::Get(chrome::DIR_USER_PICTURES, &path)); 1043 profile_state->AddNameForAllCompare(GetExpectedFolderName(path)); 1044 ASSERT_TRUE(PathService::Get(chrome::DIR_USER_VIDEOS, &path)); 1045 profile_state->AddNameForAllCompare(GetExpectedFolderName(path)); 1046 1047 profile_state->CheckGalleries("names-dir", one_expectation, auto_galleries); 1048 } else { 1049 profile_state->CheckGalleries("names", one_expectation, one_expectation); 1050 } 1051 } 1052 1053 TEST_F(MediaFileSystemRegistryTest, PreferenceListener) { 1054 CreateProfileState(1); 1055 AssertAllAutoAddedGalleries(); 1056 1057 // Add a user gallery to the regular permission extension. 1058 std::string device_id = AddUserGallery(StorageInfo::FIXED_MASS_STORAGE, 1059 empty_dir().AsUTF8Unsafe(), 1060 empty_dir()); 1061 ProfileState* profile_state = GetProfileState(0); 1062 SetGalleryPermission(profile_state, 1063 profile_state->regular_permission_extension(), 1064 device_id, 1065 true /*has access*/); 1066 1067 FSInfoMap fs_info = profile_state->GetGalleriesInfo( 1068 profile_state->regular_permission_extension()); 1069 ASSERT_EQ(1U, fs_info.size()); 1070 EXPECT_FALSE(test_file_system_context()->GetRegisteredPath( 1071 fs_info.begin()->second.fsid).empty()); 1072 1073 // Revoke permission and ensure that the file system is revoked. 1074 SetGalleryPermission(profile_state, 1075 profile_state->regular_permission_extension(), 1076 device_id, 1077 false /*has access*/); 1078 EXPECT_TRUE(test_file_system_context()->GetRegisteredPath( 1079 fs_info.begin()->second.fsid).empty()); 1080 } 1081