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