media_file_system_registry_unittest.cc revision f2477e01787aa58f445919b809d89e252beef54f
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
57using content::BrowserThread;
58
59// Not anonymous so it can be friends with MediaFileSystemRegistry.
60class 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
100TestMediaFileSystemContext::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
108bool 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
116TestMediaFileSystemContext::TestMediaFileSystemContext(
117    MediaFileSystemRegistry* registry)
118    : registry_(registry),
119      fsid_(0) {
120  registry_->file_system_context_.reset(this);
121}
122
123std::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
129void 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
135base::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
144std::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
156namespace {
157
158typedef std::map<MediaGalleryPrefId, MediaFileSystemInfo> FSInfoMap;
159
160void 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
169void 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
188class 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
211class 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 string16& name);
231  void AddNameForAllCompare(const string16& name);
232
233 private:
234  void CompareResults(const std::string& test,
235                      const std::vector<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<string16> compare_names_read_;
261  std::vector<string16> compare_names_all_;
262
263  DISALLOW_COPY_AND_ASSIGN(ProfileState);
264};
265
266}  // namespace
267
268class 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 string16& name,
321                     const base::FilePath::StringType& location) {
322    StorageInfo info(id, string16(), location, name, string16(), string16(), 0);
323    StorageMonitor::GetInstance()->receiver()->ProcessAttach(info);
324  }
325
326  void ProcessDetach(const std::string& id) {
327    StorageMonitor::GetInstance()->receiver()->ProcessDetach(id);
328  }
329
330  MediaFileSystemRegistry* registry() {
331    return test_file_system_context_->registry();
332  }
333
334  size_t GetExtensionGalleriesHostCount(
335      const MediaFileSystemRegistry* registry) const;
336
337  int num_auto_galleries() {
338    return media_directories_.num_galleries();
339  }
340
341 protected:
342  virtual void SetUp() OVERRIDE;
343  virtual void TearDown() OVERRIDE;
344
345 private:
346  // This makes sure that at least one default gallery exists on the file
347  // system.
348  EnsureMediaDirectoriesExists media_directories_;
349
350  // Some test gallery directories.
351  base::ScopedTempDir galleries_dir_;
352  // An empty directory in |galleries_dir_|
353  base::FilePath empty_dir_;
354  // A directory in |galleries_dir_| with a DCIM directory in it.
355  base::FilePath dcim_dir_;
356
357  // MediaFileSystemRegistry owns this.
358  TestMediaFileSystemContext* test_file_system_context_;
359
360  // Needed for extension service & friends to work.
361
362#if defined OS_CHROMEOS
363  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
364  chromeos::ScopedTestCrosSettings test_cros_settings_;
365  scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
366#endif
367
368  MockProfileSharedRenderProcessHostFactory rph_factory_;
369
370  ScopedVector<ProfileState> profile_states_;
371};
372
373namespace {
374
375bool MediaFileSystemInfoComparator(const MediaFileSystemInfo& a,
376                                   const MediaFileSystemInfo& b) {
377  CHECK_NE(a.name, b.name);  // Name must be unique.
378  return a.name < b.name;
379}
380
381///////////////////////////////////////////////
382// MockProfileSharedRenderProcessHostFactory //
383///////////////////////////////////////////////
384
385MockProfileSharedRenderProcessHostFactory::
386    ~MockProfileSharedRenderProcessHostFactory() {
387  STLDeleteValues(&rph_map_);
388}
389
390content::MockRenderProcessHost*
391MockProfileSharedRenderProcessHostFactory::ReleaseRPH(
392    content::BrowserContext* browser_context) {
393  ProfileRPHMap::iterator existing = rph_map_.find(browser_context);
394  if (existing == rph_map_.end())
395    return NULL;
396  content::MockRenderProcessHost* result = existing->second;
397  rph_map_.erase(existing);
398  return result;
399}
400
401content::RenderProcessHost*
402MockProfileSharedRenderProcessHostFactory::CreateRenderProcessHost(
403    content::BrowserContext* browser_context,
404    content::SiteInstance* site_instance) const {
405  ProfileRPHMap::const_iterator existing = rph_map_.find(browser_context);
406  if (existing != rph_map_.end())
407    return existing->second;
408  rph_map_[browser_context] =
409      new content::MockRenderProcessHost(browser_context);
410  return rph_map_[browser_context];
411}
412
413//////////////////
414// ProfileState //
415//////////////////
416
417ProfileState::ProfileState(
418    MockProfileSharedRenderProcessHostFactory* rph_factory)
419    : num_comparisons_(0),
420      profile_(new TestingProfile()) {
421  extensions::TestExtensionSystem* extension_system(
422      static_cast<extensions::TestExtensionSystem*>(
423          extensions::ExtensionSystem::Get(profile_.get())));
424  extension_system->CreateExtensionService(
425      CommandLine::ForCurrentProcess(), base::FilePath(), false);
426
427  std::vector<std::string> all_permissions;
428  all_permissions.push_back("allAutoDetected");
429  all_permissions.push_back("read");
430  std::vector<std::string> read_permissions;
431  read_permissions.push_back("read");
432
433  all_permission_extension_ =
434      AddMediaGalleriesApp("all", all_permissions, profile_.get());
435  regular_permission_extension_ =
436      AddMediaGalleriesApp("regular", read_permissions, profile_.get());
437  no_permissions_extension_ =
438      AddMediaGalleriesApp("no", read_permissions, profile_.get());
439
440  single_web_contents_.reset(
441      content::WebContentsTester::CreateTestWebContents(profile_.get(), NULL));
442  single_rph_ = rph_factory->ReleaseRPH(profile_.get());
443
444  shared_web_contents1_.reset(
445      content::WebContentsTester::CreateTestWebContents(profile_.get(), NULL));
446  shared_web_contents2_.reset(
447      content::WebContentsTester::CreateTestWebContents(profile_.get(), NULL));
448  shared_rph_ = rph_factory->ReleaseRPH(profile_.get());
449}
450
451ProfileState::~ProfileState() {
452  // TestExtensionSystem uses DeleteSoon, so we need to delete the profiles
453  // and then run the message queue to clean up.  But first we have to
454  // delete everything that references the profile.
455  single_web_contents_.reset();
456  shared_web_contents1_.reset();
457  shared_web_contents2_.reset();
458  profile_.reset();
459
460  base::MessageLoop::current()->RunUntilIdle();
461}
462
463MediaGalleriesPreferences* ProfileState::GetMediaGalleriesPrefs() {
464  MediaGalleriesPreferences* prefs =
465      MediaGalleriesPreferencesFactory::GetForProfile(profile_.get());
466  base::RunLoop loop;
467  prefs->EnsureInitialized(loop.QuitClosure());
468  loop.Run();
469  return prefs;
470}
471
472void ProfileState::CheckGalleries(
473    const std::string& test,
474    const std::vector<MediaFileSystemInfo>& regular_extension_galleries,
475    const std::vector<MediaFileSystemInfo>& all_extension_galleries) {
476  content::RenderViewHost* rvh = single_web_contents_->GetRenderViewHost();
477  MediaFileSystemRegistry* registry =
478      g_browser_process->media_file_system_registry();
479
480  // No Media Galleries permissions.
481  std::vector<MediaFileSystemInfo> empty_expectation;
482  std::vector<string16> empty_names;
483  registry->GetMediaFileSystemsForExtension(
484      rvh, no_permissions_extension_.get(),
485      base::Bind(&ProfileState::CompareResults, base::Unretained(this),
486                 base::StringPrintf("%s (no permission)", test.c_str()),
487                 base::ConstRef(empty_names),
488                 base::ConstRef(empty_expectation)));
489  base::MessageLoop::current()->RunUntilIdle();
490  EXPECT_EQ(1, GetAndClearComparisonCount());
491
492  // Read permission only.
493  registry->GetMediaFileSystemsForExtension(
494      rvh, regular_permission_extension_.get(),
495      base::Bind(&ProfileState::CompareResults, base::Unretained(this),
496                 base::StringPrintf("%s (regular permission)", test.c_str()),
497                 base::ConstRef(compare_names_read_),
498                 base::ConstRef(regular_extension_galleries)));
499  base::MessageLoop::current()->RunUntilIdle();
500  EXPECT_EQ(1, GetAndClearComparisonCount());
501
502  // All galleries permission.
503  registry->GetMediaFileSystemsForExtension(
504      rvh, all_permission_extension_.get(),
505      base::Bind(&ProfileState::CompareResults, base::Unretained(this),
506                 base::StringPrintf("%s (all permission)", test.c_str()),
507                 base::ConstRef(compare_names_all_),
508                 base::ConstRef(all_extension_galleries)));
509  base::MessageLoop::current()->RunUntilIdle();
510  EXPECT_EQ(1, GetAndClearComparisonCount());
511}
512
513FSInfoMap ProfileState::GetGalleriesInfo(extensions::Extension* extension) {
514  content::RenderViewHost* rvh = single_web_contents_->GetRenderViewHost();
515  FSInfoMap results;
516  MediaFileSystemRegistry* registry =
517      g_browser_process->media_file_system_registry();
518  registry->GetMediaFileSystemsForExtension(
519      rvh, extension,
520      base::Bind(&GetGalleryInfoCallback, base::Unretained(&results)));
521  base::MessageLoop::current()->RunUntilIdle();
522  return results;
523}
524
525extensions::Extension* ProfileState::all_permission_extension() {
526  return all_permission_extension_.get();
527}
528
529extensions::Extension* ProfileState::regular_permission_extension() {
530  return regular_permission_extension_.get();
531}
532
533Profile* ProfileState::profile() {
534  return profile_.get();
535}
536
537void ProfileState::AddNameForReadCompare(const string16& name) {
538  compare_names_read_.push_back(name);
539}
540
541void ProfileState::AddNameForAllCompare(const string16& name) {
542  compare_names_all_.push_back(name);
543}
544
545bool ProfileState::ContainsEntry(
546    const MediaFileSystemInfo& info,
547    const std::vector<MediaFileSystemInfo>& container) {
548  for (size_t i = 0; i < container.size(); ++i) {
549    if (info.path.value() == container[i].path.value()) {
550      EXPECT_FALSE(container[i].fsid.empty());
551      if (!info.fsid.empty())
552        EXPECT_EQ(info.fsid, container[i].fsid);
553      return true;
554    }
555  }
556  return false;
557}
558
559void ProfileState::CompareResults(
560    const std::string& test,
561    const std::vector<string16>& names,
562    const std::vector<MediaFileSystemInfo>& expected,
563    const std::vector<MediaFileSystemInfo>& actual) {
564  num_comparisons_++;
565  EXPECT_EQ(expected.size(), actual.size()) << test;
566
567  // Order isn't important, so sort the results.
568  std::vector<MediaFileSystemInfo> sorted(actual);
569  std::sort(sorted.begin(), sorted.end(), MediaFileSystemInfoComparator);
570  std::vector<MediaFileSystemInfo> expect(expected);
571  std::sort(expect.begin(), expect.end(), MediaFileSystemInfoComparator);
572  std::vector<string16> expect_names(names);
573  std::sort(expect_names.begin(), expect_names.end());
574
575  for (size_t i = 0; i < expect.size() && i < sorted.size(); ++i) {
576    if (expect_names.size() > i)
577      EXPECT_EQ(expect_names[i], sorted[i].name) << test;
578    EXPECT_TRUE(ContainsEntry(expect[i], sorted)) << test;
579  }
580}
581
582int ProfileState::GetAndClearComparisonCount() {
583  int result = num_comparisons_;
584  num_comparisons_ = 0;
585  return result;
586}
587
588}  // namespace
589
590/////////////////////////////////
591// MediaFileSystemRegistryTest //
592/////////////////////////////////
593
594void MediaFileSystemRegistryTest::CreateProfileState(size_t profile_count) {
595  for (size_t i = 0; i < profile_count; ++i) {
596    ProfileState* state = new ProfileState(&rph_factory_);
597    profile_states_.push_back(state);
598  }
599}
600
601ProfileState* MediaFileSystemRegistryTest::GetProfileState(size_t i) {
602  return profile_states_[i];
603}
604
605MediaGalleriesPreferences* MediaFileSystemRegistryTest::GetPreferences(
606    Profile* profile) {
607  MediaGalleriesPreferences* prefs = registry()->GetPreferences(profile);
608  base::RunLoop loop;
609  prefs->EnsureInitialized(loop.QuitClosure());
610  loop.Run();
611  return prefs;
612}
613
614std::string MediaFileSystemRegistryTest::AddUserGallery(
615    StorageInfo::Type type,
616    const std::string& unique_id,
617    const base::FilePath& path) {
618  std::string device_id = StorageInfo::MakeDeviceId(type, unique_id);
619  DCHECK(!StorageInfo::IsMediaDevice(device_id));
620
621  for (size_t i = 0; i < profile_states_.size(); ++i) {
622    profile_states_[i]->GetMediaGalleriesPrefs()->AddGallery(
623        device_id, base::FilePath(), true /*user_added*/,
624        string16(), string16(), string16(), 0, base::Time::Now());
625  }
626  return device_id;
627}
628
629std::string MediaFileSystemRegistryTest::AttachDevice(
630    StorageInfo::Type type,
631    const std::string& unique_id,
632    const base::FilePath& location) {
633  std::string device_id = StorageInfo::MakeDeviceId(type, unique_id);
634  DCHECK(StorageInfo::IsRemovableDevice(device_id));
635  string16 label = location.BaseName().LossyDisplayName();
636  ProcessAttach(device_id, label, location.value());
637  base::MessageLoop::current()->RunUntilIdle();
638  return device_id;
639}
640
641void MediaFileSystemRegistryTest::DetachDevice(const std::string& device_id) {
642  DCHECK(StorageInfo::IsRemovableDevice(device_id));
643  ProcessDetach(device_id);
644  base::MessageLoop::current()->RunUntilIdle();
645}
646
647void MediaFileSystemRegistryTest::SetGalleryPermission(
648    ProfileState* profile_state, extensions::Extension* extension,
649    const std::string& device_id, bool has_access) {
650  MediaGalleriesPreferences* preferences =
651      profile_state->GetMediaGalleriesPrefs();
652  MediaGalleryPrefIdSet pref_id =
653      preferences->LookUpGalleriesByDeviceId(device_id);
654  ASSERT_EQ(1U, pref_id.size());
655  preferences->SetGalleryPermissionForExtension(*extension, *pref_id.begin(),
656                                                has_access);
657}
658
659void MediaFileSystemRegistryTest::AssertAllAutoAddedGalleries() {
660  for (size_t i = 0; i < profile_states_.size(); ++i) {
661    MediaGalleriesPreferences* prefs =
662        profile_states_[0]->GetMediaGalleriesPrefs();
663
664    // Make sure that we have at least one gallery and that they are all
665    // auto added galleries.
666    const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries();
667#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
668    ASSERT_GT(galleries.size(), 0U);
669#endif
670    for (MediaGalleriesPrefInfoMap::const_iterator it = galleries.begin();
671         it != galleries.end();
672         ++it) {
673      ASSERT_EQ(MediaGalleryPrefInfo::kAutoDetected, it->second.type);
674    }
675  }
676}
677
678void MediaFileSystemRegistryTest::InitForGalleriesInfoTest(
679    FSInfoMap* galleries_info) {
680  CreateProfileState(1);
681  AssertAllAutoAddedGalleries();
682
683  // Get all existing gallery names.
684  ProfileState* profile_state = GetProfileState(0U);
685  *galleries_info = profile_state->GetGalleriesInfo(
686      profile_state->all_permission_extension());
687#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
688  ASSERT_EQ(3U, galleries_info->size());
689#else
690  ASSERT_EQ(0U, galleries_info->size());
691#endif
692}
693
694void MediaFileSystemRegistryTest::CheckNewGalleryInfo(
695    ProfileState* profile_state,
696    const FSInfoMap& galleries_info,
697    const base::FilePath& location,
698    bool removable,
699    bool media_device) {
700  // Get new galleries.
701  FSInfoMap new_galleries_info = profile_state->GetGalleriesInfo(
702      profile_state->all_permission_extension());
703  ASSERT_EQ(galleries_info.size() + 1U, new_galleries_info.size());
704
705  bool found_new = false;
706  for (FSInfoMap::const_iterator it = new_galleries_info.begin();
707       it != new_galleries_info.end();
708       ++it) {
709    if (ContainsKey(galleries_info, it->first))
710      continue;
711
712    ASSERT_FALSE(found_new);
713    CheckGalleryInfo(it->second, test_file_system_context_, location,
714                     removable, media_device);
715    found_new = true;
716  }
717  ASSERT_TRUE(found_new);
718}
719
720std::vector<MediaFileSystemInfo>
721MediaFileSystemRegistryTest::GetAutoAddedGalleries(
722    ProfileState* profile_state) {
723  const MediaGalleriesPrefInfoMap& galleries =
724      profile_state->GetMediaGalleriesPrefs()->known_galleries();
725  std::vector<MediaFileSystemInfo> result;
726  for (MediaGalleriesPrefInfoMap::const_iterator it = galleries.begin();
727       it != galleries.end();
728       ++it) {
729    if (it->second.type == MediaGalleryPrefInfo::kAutoDetected) {
730      base::FilePath path = it->second.AbsolutePath();
731      MediaFileSystemInfo info(path.BaseName().LossyDisplayName(), path,
732                               std::string(), 0, std::string(), false, false);
733      result.push_back(info);
734    }
735  }
736  std::sort(result.begin(), result.end(), MediaFileSystemInfoComparator);
737  return result;
738}
739
740size_t MediaFileSystemRegistryTest::GetExtensionGalleriesHostCount(
741    const MediaFileSystemRegistry* registry) const {
742  size_t extension_galleries_host_count = 0;
743  for (MediaFileSystemRegistry::ExtensionGalleriesHostMap::const_iterator it =
744           registry->extension_hosts_map_.begin();
745       it != registry->extension_hosts_map_.end();
746       ++it) {
747    extension_galleries_host_count += it->second.size();
748  }
749  return extension_galleries_host_count;
750}
751
752
753void MediaFileSystemRegistryTest::SetUp() {
754  ChromeRenderViewHostTestHarness::SetUp();
755  ASSERT_TRUE(TestStorageMonitor::CreateAndInstall());
756
757  DeleteContents();
758  SetRenderProcessHostFactory(&rph_factory_);
759
760  test_file_system_context_ = new TestMediaFileSystemContext(
761      g_browser_process->media_file_system_registry());
762
763#if defined(OS_CHROMEOS)
764  test_user_manager_.reset(new chromeos::ScopedTestUserManager());
765#endif
766
767  ASSERT_TRUE(galleries_dir_.CreateUniqueTempDir());
768  empty_dir_ = galleries_dir_.path().AppendASCII("empty");
769  ASSERT_TRUE(file_util::CreateDirectory(empty_dir_));
770  dcim_dir_ = galleries_dir_.path().AppendASCII("with_dcim");
771  ASSERT_TRUE(file_util::CreateDirectory(dcim_dir_));
772  ASSERT_TRUE(file_util::CreateDirectory(dcim_dir_.Append(kDCIMDirectoryName)));
773}
774
775void MediaFileSystemRegistryTest::TearDown() {
776  profile_states_.clear();
777  MediaFileSystemRegistry* registry =
778      g_browser_process->media_file_system_registry();
779  EXPECT_EQ(0U, GetExtensionGalleriesHostCount(registry));
780  TestStorageMonitor::RemoveSingleton();
781#if defined(OS_CHROMEOS)
782  test_user_manager_.reset();
783#endif
784
785  ChromeRenderViewHostTestHarness::TearDown();
786}
787
788///////////
789// Tests //
790///////////
791
792TEST_F(MediaFileSystemRegistryTest, Basic) {
793  CreateProfileState(1);
794  AssertAllAutoAddedGalleries();
795
796  ProfileState* profile_state = GetProfileState(0);
797  std::vector<MediaFileSystemInfo> auto_galleries =
798      GetAutoAddedGalleries(profile_state);
799  std::vector<MediaFileSystemInfo> empty_expectation;
800  profile_state->CheckGalleries("basic", empty_expectation, auto_galleries);
801}
802
803TEST_F(MediaFileSystemRegistryTest, UserAddedGallery) {
804  CreateProfileState(1);
805  AssertAllAutoAddedGalleries();
806  ProfileState* profile_state = GetProfileState(0);
807  std::vector<MediaFileSystemInfo> auto_galleries =
808      GetAutoAddedGalleries(profile_state);
809  std::vector<MediaFileSystemInfo> added_galleries;
810  profile_state->CheckGalleries("user added init", added_galleries,
811                                auto_galleries);
812
813  // Add a user gallery to the regular permission extension.
814  std::string device_id = AddUserGallery(StorageInfo::FIXED_MASS_STORAGE,
815                                         empty_dir().AsUTF8Unsafe(),
816                                         empty_dir());
817  SetGalleryPermission(profile_state,
818                       profile_state->regular_permission_extension(),
819                       device_id,
820                       true /*has access*/);
821  MediaFileSystemInfo added_info(empty_dir().LossyDisplayName(), empty_dir(),
822                                 std::string(), 0, std::string(), false, false);
823  added_galleries.push_back(added_info);
824  profile_state->CheckGalleries("user added regular", added_galleries,
825                                auto_galleries);
826
827  // Add it to the all galleries extension.
828  SetGalleryPermission(profile_state,
829                       profile_state->all_permission_extension(),
830                       device_id,
831                       true /*has access*/);
832  auto_galleries.push_back(added_info);
833  profile_state->CheckGalleries("user added all", added_galleries,
834                                auto_galleries);
835}
836
837// Regression test to make sure erasing galleries does not result a crash.
838TEST_F(MediaFileSystemRegistryTest, EraseGalleries) {
839  CreateProfileState(1);
840  AssertAllAutoAddedGalleries();
841
842  ProfileState* profile_state = GetProfileState(0);
843  std::vector<MediaFileSystemInfo> auto_galleries =
844      GetAutoAddedGalleries(profile_state);
845  std::vector<MediaFileSystemInfo> empty_expectation;
846  profile_state->CheckGalleries("erase", empty_expectation, auto_galleries);
847
848  MediaGalleriesPreferences* prefs = profile_state->GetMediaGalleriesPrefs();
849  MediaGalleriesPrefInfoMap galleries = prefs->known_galleries();
850  for (MediaGalleriesPrefInfoMap::const_iterator it = galleries.begin();
851       it != galleries.end(); ++it) {
852    prefs->ForgetGalleryById(it->first);
853  }
854}
855
856// Regression test to make sure calling GetPreferences() does not re-insert
857// galleries on auto-detected removable devices that were blacklisted.
858TEST_F(MediaFileSystemRegistryTest,
859       GetPreferencesDoesNotReinsertBlacklistedGalleries) {
860  CreateProfileState(1);
861  AssertAllAutoAddedGalleries();
862
863  ProfileState* profile_state = GetProfileState(0);
864  const size_t gallery_count = GetAutoAddedGalleries(profile_state).size();
865
866  // Attach a device.
867  const std::string device_id = AttachDevice(
868      StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM,
869      "removable_dcim_fake_id",
870      dcim_dir());
871  EXPECT_EQ(gallery_count + 1, GetAutoAddedGalleries(profile_state).size());
872
873  // Forget the device.
874  bool forget_gallery = false;
875  MediaGalleriesPreferences* prefs = GetPreferences(profile_state->profile());
876  const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries();
877  for (MediaGalleriesPrefInfoMap::const_iterator it = galleries.begin();
878       it != galleries.end(); ++it) {
879    if (it->second.device_id == device_id) {
880      prefs->ForgetGalleryById(it->first);
881      forget_gallery = true;
882      break;
883    }
884  }
885  base::MessageLoop::current()->RunUntilIdle();
886  EXPECT_TRUE(forget_gallery);
887  EXPECT_EQ(gallery_count, GetAutoAddedGalleries(profile_state).size());
888
889  // Call GetPreferences() and the gallery count should not change.
890  prefs = GetPreferences(profile_state->profile());
891  EXPECT_EQ(gallery_count, GetAutoAddedGalleries(profile_state).size());
892}
893
894TEST_F(MediaFileSystemRegistryTest, GalleryNameDefault) {
895  FSInfoMap galleries_info;
896  InitForGalleriesInfoTest(&galleries_info);
897
898  for (FSInfoMap::const_iterator it = galleries_info.begin();
899       it != galleries_info.end();
900       ++it) {
901    CheckGalleryInfo(it->second, test_file_system_context(),
902                     it->second.path, false, false);
903  }
904}
905
906// TODO(gbillock): Move the remaining test into the linux directory.
907#if !defined(OS_MACOSX) && !defined(OS_WIN)
908TEST_F(MediaFileSystemRegistryTest, GalleryMTP) {
909  FSInfoMap galleries_info;
910  InitForGalleriesInfoTest(&galleries_info);
911
912  base::FilePath location(FILE_PATH_LITERAL("/mtp_bogus"));
913  AttachDevice(StorageInfo::MTP_OR_PTP, "mtp_fake_id", location);
914  CheckNewGalleryInfo(GetProfileState(0U), galleries_info, location,
915                      true /*removable*/, true /* media device */);
916}
917#endif
918
919TEST_F(MediaFileSystemRegistryTest, GalleryDCIM) {
920  FSInfoMap galleries_info;
921  InitForGalleriesInfoTest(&galleries_info);
922
923  AttachDevice(StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM,
924               "removable_dcim_fake_id",
925               dcim_dir());
926  CheckNewGalleryInfo(GetProfileState(0U), galleries_info, dcim_dir(),
927                      true /*removable*/, true /* media device */);
928}
929
930TEST_F(MediaFileSystemRegistryTest, GalleryNoDCIM) {
931  FSInfoMap galleries_info;
932  InitForGalleriesInfoTest(&galleries_info);
933
934  std::string device_id =
935      AttachDevice(StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM,
936                   empty_dir().AsUTF8Unsafe(),
937                   empty_dir());
938  std::string device_id2 =
939      AddUserGallery(StorageInfo::REMOVABLE_MASS_STORAGE_NO_DCIM,
940                     empty_dir().AsUTF8Unsafe(),
941                     empty_dir());
942  ASSERT_EQ(device_id, device_id2);
943  // Add permission for new non-default gallery.
944  ProfileState* profile_state = GetProfileState(0U);
945  SetGalleryPermission(profile_state,
946                       profile_state->all_permission_extension(),
947                       device_id,
948                       true /*has access*/);
949  CheckNewGalleryInfo(profile_state, galleries_info, empty_dir(),
950                      true /*removable*/, false /* media device */);
951}
952
953TEST_F(MediaFileSystemRegistryTest, GalleryUserAddedPath) {
954  FSInfoMap galleries_info;
955  InitForGalleriesInfoTest(&galleries_info);
956
957  std::string device_id = AddUserGallery(StorageInfo::FIXED_MASS_STORAGE,
958                                         empty_dir().AsUTF8Unsafe(),
959                                         empty_dir());
960  // Add permission for new non-default gallery.
961  ProfileState* profile_state = GetProfileState(0U);
962  SetGalleryPermission(profile_state,
963                       profile_state->all_permission_extension(),
964                       device_id,
965                       true /*has access*/);
966  CheckNewGalleryInfo(profile_state, galleries_info, empty_dir(),
967                      false /*removable*/, false /* media device */);
968}
969
970TEST_F(MediaFileSystemRegistryTest, DetachedDeviceGalleryPath) {
971  const std::string device_id = AttachDevice(
972      StorageInfo::REMOVABLE_MASS_STORAGE_WITH_DCIM,
973      "removable_dcim_fake_id",
974      dcim_dir());
975
976  MediaGalleryPrefInfo pref_info;
977  pref_info.device_id = device_id;
978  EXPECT_EQ(dcim_dir().value(), pref_info.AbsolutePath().value());
979
980  MediaGalleryPrefInfo pref_info_with_relpath;
981  pref_info_with_relpath.path =
982      base::FilePath(FILE_PATH_LITERAL("test_relpath"));
983  pref_info_with_relpath.device_id = device_id;
984  EXPECT_EQ(dcim_dir().Append(pref_info_with_relpath.path).value(),
985            pref_info_with_relpath.AbsolutePath().value());
986
987  DetachDevice(device_id);
988  EXPECT_TRUE(pref_info.AbsolutePath().empty());
989  EXPECT_TRUE(pref_info_with_relpath.AbsolutePath().empty());
990}
991
992TEST_F(MediaFileSystemRegistryTest, TestNameConstruction) {
993  CreateProfileState(1);
994  AssertAllAutoAddedGalleries();
995
996  ProfileState* profile_state = GetProfileState(0);
997
998  std::string user_gallery = AddUserGallery(StorageInfo::FIXED_MASS_STORAGE,
999                                            empty_dir().AsUTF8Unsafe(),
1000                                            empty_dir());
1001  SetGalleryPermission(profile_state,
1002                       profile_state->regular_permission_extension(),
1003                       user_gallery,
1004                       true /*has access*/);
1005  SetGalleryPermission(profile_state,
1006                       profile_state->all_permission_extension(),
1007                       user_gallery,
1008                       true /*has access*/);
1009
1010  std::vector<MediaFileSystemInfo> auto_galleries =
1011      GetAutoAddedGalleries(profile_state);
1012  MediaFileSystemInfo added_info(empty_dir().BaseName().LossyDisplayName(),
1013                                 empty_dir(), std::string(), 0, std::string(),
1014                                 false, false);
1015  auto_galleries.push_back(added_info);
1016  std::vector<MediaFileSystemInfo> one_expectation;
1017  one_expectation.push_back(added_info);
1018
1019  profile_state->AddNameForReadCompare(
1020#if defined(OS_CHROMEOS)
1021      empty_dir().BaseName().LossyDisplayName());
1022#else
1023      empty_dir().LossyDisplayName());
1024#endif
1025  profile_state->AddNameForAllCompare(
1026#if defined(OS_CHROMEOS)
1027      empty_dir().BaseName().LossyDisplayName());
1028#else
1029      empty_dir().LossyDisplayName());
1030#endif
1031
1032  // This part of the test is conditional on default directories existing
1033  // on the test platform. In ChromeOS, these directories do not exist.
1034  base::FilePath path;
1035  if (num_auto_galleries() > 0) {
1036    ASSERT_TRUE(PathService::Get(chrome::DIR_USER_MUSIC, &path));
1037#if defined(OS_CHROMEOS)
1038    profile_state->AddNameForAllCompare(path.BaseName().LossyDisplayName());
1039#else
1040    profile_state->AddNameForAllCompare(path.LossyDisplayName());
1041#endif
1042    ASSERT_TRUE(PathService::Get(chrome::DIR_USER_PICTURES, &path));
1043#if defined(OS_CHROMEOS)
1044    profile_state->AddNameForAllCompare(path.BaseName().LossyDisplayName());
1045#else
1046    profile_state->AddNameForAllCompare(path.LossyDisplayName());
1047#endif
1048    ASSERT_TRUE(PathService::Get(chrome::DIR_USER_VIDEOS, &path));
1049#if defined(OS_CHROMEOS)
1050    profile_state->AddNameForAllCompare(path.BaseName().LossyDisplayName());
1051#else
1052    profile_state->AddNameForAllCompare(path.LossyDisplayName());
1053#endif
1054
1055    profile_state->CheckGalleries("names-dir", one_expectation, auto_galleries);
1056  } else {
1057    profile_state->CheckGalleries("names", one_expectation, one_expectation);
1058  }
1059}
1060