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
57using content::BrowserThread;
58using storage_monitor::StorageInfo;
59using storage_monitor::StorageMonitor;
60using storage_monitor::TestStorageMonitor;
61
62// Not anonymous so it can be friends with MediaFileSystemRegistry.
63class 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
103TestMediaFileSystemContext::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
111bool 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
119TestMediaFileSystemContext::TestMediaFileSystemContext(
120    MediaFileSystemRegistry* registry)
121    : registry_(registry) {
122  registry_->file_system_context_.reset(this);
123}
124
125bool 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
133void 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
139base::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
148void 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
159namespace {
160
161typedef std::map<MediaGalleryPrefId, MediaFileSystemInfo> FSInfoMap;
162
163void 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
172void 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
191class 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
214class 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
269base::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
279class 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
384namespace {
385
386bool 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
396MockProfileSharedRenderProcessHostFactory::
397    ~MockProfileSharedRenderProcessHostFactory() {
398  STLDeleteValues(&rph_map_);
399}
400
401content::MockRenderProcessHost*
402MockProfileSharedRenderProcessHostFactory::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
412content::RenderProcessHost*
413MockProfileSharedRenderProcessHostFactory::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
428ProfileState::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
462ProfileState::~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
474MediaGalleriesPreferences* 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
483void 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
524FSInfoMap 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
536extensions::Extension* ProfileState::all_permission_extension() {
537  return all_permission_extension_.get();
538}
539
540extensions::Extension* ProfileState::regular_permission_extension() {
541  return regular_permission_extension_.get();
542}
543
544Profile* ProfileState::profile() {
545  return profile_.get();
546}
547
548void ProfileState::AddNameForReadCompare(const base::string16& name) {
549  compare_names_read_.push_back(name);
550}
551
552void ProfileState::AddNameForAllCompare(const base::string16& name) {
553  compare_names_all_.push_back(name);
554}
555
556bool 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
570void 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
593int ProfileState::GetAndClearComparisonCount() {
594  int result = num_comparisons_;
595  num_comparisons_ = 0;
596  return result;
597}
598
599}  // namespace
600
601/////////////////////////////////
602// MediaFileSystemRegistryTest //
603/////////////////////////////////
604
605void 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
612ProfileState* MediaFileSystemRegistryTest::GetProfileState(size_t i) {
613  return profile_states_[i];
614}
615
616MediaGalleriesPreferences* 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
625std::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
641std::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
653void MediaFileSystemRegistryTest::DetachDevice(const std::string& device_id) {
654  DCHECK(StorageInfo::IsRemovableDevice(device_id));
655  ProcessDetach(device_id);
656  base::MessageLoop::current()->RunUntilIdle();
657}
658
659void 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
671void 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
690void 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
706void 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
732std::vector<MediaFileSystemInfo>
733MediaFileSystemRegistryTest::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
752size_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
765void 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
788void 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
805TEST_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
816TEST_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.
851TEST_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.
871TEST_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
907TEST_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)
921TEST_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
932TEST_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
943TEST_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
966TEST_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
983TEST_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
1005TEST_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
1053TEST_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