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