1// Copyright 2014 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#include "base/bind.h"
6#include "base/command_line.h"
7#include "base/files/file_path.h"
8#include "base/files/file_util.h"
9#include "base/files/scoped_temp_dir.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/run_loop.h"
12#include "base/test/scoped_path_override.h"
13#include "chrome/browser/extensions/test_extension_system.h"
14#include "chrome/browser/media_galleries/gallery_watch_manager.h"
15#include "chrome/browser/media_galleries/gallery_watch_manager_observer.h"
16#include "chrome/browser/media_galleries/media_galleries_preferences.h"
17#include "chrome/browser/media_galleries/media_galleries_preferences_factory.h"
18#include "chrome/browser/media_galleries/media_galleries_test_util.h"
19#include "chrome/test/base/testing_profile.h"
20#include "components/storage_monitor/test_storage_monitor.h"
21#include "content/public/test/test_browser_thread_bundle.h"
22#include "extensions/browser/extension_system.h"
23#include "extensions/common/extension.h"
24#include "extensions/common/permissions/media_galleries_permission.h"
25#include "testing/gtest/include/gtest/gtest.h"
26
27#if defined(OS_CHROMEOS)
28#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
29#include "chrome/browser/chromeos/settings/cros_settings.h"
30#include "chrome/browser/chromeos/settings/device_settings_service.h"
31#endif
32
33namespace component_updater {
34
35namespace {
36
37void ConfirmWatch(base::RunLoop* loop, const std::string& error) {
38  EXPECT_TRUE(error.empty());
39  loop->Quit();
40}
41
42void ExpectWatchError(base::RunLoop* loop,
43                      const std::string& expected_error,
44                      const std::string& error) {
45  EXPECT_EQ(expected_error, error);
46  loop->Quit();
47}
48
49}  // namespace
50
51class GalleryWatchManagerTest : public GalleryWatchManagerObserver,
52                                public testing::Test {
53 public:
54  GalleryWatchManagerTest()
55      : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
56        profile_(new TestingProfile()),
57        gallery_prefs_(NULL),
58        expect_gallery_changed_(false),
59        expect_gallery_watch_dropped_(false),
60        pending_loop_(NULL) {}
61
62  virtual ~GalleryWatchManagerTest() {}
63
64  virtual void SetUp() OVERRIDE {
65    ASSERT_TRUE(storage_monitor::TestStorageMonitor::CreateAndInstall());
66
67    extensions::TestExtensionSystem* extension_system(
68        static_cast<extensions::TestExtensionSystem*>(
69            extensions::ExtensionSystem::Get(profile_.get())));
70    extension_system->CreateExtensionService(
71        base::CommandLine::ForCurrentProcess(), base::FilePath(), false);
72
73    gallery_prefs_ =
74        MediaGalleriesPreferencesFactory::GetForProfile(profile_.get());
75    base::RunLoop loop;
76    gallery_prefs_->EnsureInitialized(loop.QuitClosure());
77    loop.Run();
78
79    std::vector<std::string> read_permissions;
80    read_permissions.push_back(
81        extensions::MediaGalleriesPermission::kReadPermission);
82    extension_ = AddMediaGalleriesApp("read", read_permissions, profile_.get());
83
84    manager_.reset(new GalleryWatchManager);
85    manager_->AddObserver(profile_.get(), this);
86  }
87
88  virtual void TearDown() OVERRIDE {
89    manager_->RemoveObserver(profile_.get());
90    manager_.reset();
91    storage_monitor::TestStorageMonitor::Destroy();
92  }
93
94 protected:
95  // Create the specified path, and add it to preferences as a gallery.
96  MediaGalleryPrefId AddGallery(const base::FilePath& path) {
97    MediaGalleryPrefInfo gallery_info;
98    EXPECT_FALSE(gallery_prefs_->LookUpGalleryByPath(path, &gallery_info));
99    MediaGalleryPrefId id =
100        gallery_prefs_->AddGallery(gallery_info.device_id,
101                                   gallery_info.path,
102                                   MediaGalleryPrefInfo::kUserAdded,
103                                   gallery_info.volume_label,
104                                   gallery_info.vendor_name,
105                                   gallery_info.model_name,
106                                   gallery_info.total_size_in_bytes,
107                                   gallery_info.last_attach_time,
108                                   0,
109                                   0,
110                                   0);
111    EXPECT_NE(kInvalidMediaGalleryPrefId, id);
112
113    EXPECT_TRUE(gallery_prefs_->SetGalleryPermissionForExtension(
114        *extension_, id, true));
115    return id;
116  }
117
118  TestingProfile* profile() { return profile_.get(); }
119
120  GalleryWatchManager* manager() { return manager_.get(); }
121
122  extensions::Extension* extension() { return extension_.get(); }
123
124  MediaGalleriesPreferences* gallery_prefs() { return gallery_prefs_; }
125
126  bool GalleryWatchesSupported() {
127    return base::FilePathWatcher::RecursiveWatchAvailable();
128  }
129
130  void AddAndConfirmWatch(MediaGalleryPrefId gallery_id) {
131    base::RunLoop loop;
132    manager()->AddWatch(profile(),
133                        extension(),
134                        gallery_id,
135                        base::Bind(&ConfirmWatch, base::Unretained(&loop)));
136    loop.Run();
137  }
138
139  void ExpectGalleryChanged(base::RunLoop* loop) {
140    expect_gallery_changed_ = true;
141    pending_loop_ = loop;
142  }
143
144  void ExpectGalleryWatchDropped(base::RunLoop* loop) {
145    expect_gallery_watch_dropped_ = true;
146    pending_loop_ = loop;
147  }
148
149 private:
150  // GalleryWatchManagerObserver implementation.
151  virtual void OnGalleryChanged(const std::string& extension_id,
152                                MediaGalleryPrefId gallery_id) OVERRIDE {
153    EXPECT_TRUE(expect_gallery_changed_);
154    pending_loop_->Quit();
155  }
156
157  virtual void OnGalleryWatchDropped(const std::string& extension_id,
158                                     MediaGalleryPrefId gallery_id) OVERRIDE {
159    EXPECT_TRUE(expect_gallery_watch_dropped_);
160    pending_loop_->Quit();
161  }
162
163  scoped_ptr<GalleryWatchManager> manager_;
164
165  // Needed for extension service & friends to work.
166  content::TestBrowserThreadBundle thread_bundle_;
167
168  scoped_refptr<extensions::Extension> extension_;
169
170  EnsureMediaDirectoriesExists mock_gallery_locations_;
171
172#if defined(OS_CHROMEOS)
173  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
174  chromeos::ScopedTestCrosSettings test_cros_settings_;
175  chromeos::ScopedTestUserManager test_user_manager_;
176#endif
177
178  storage_monitor::TestStorageMonitor monitor_;
179  scoped_ptr<TestingProfile> profile_;
180  MediaGalleriesPreferences* gallery_prefs_;
181
182  bool expect_gallery_changed_;
183  bool expect_gallery_watch_dropped_;
184  base::RunLoop* pending_loop_;
185
186  DISALLOW_COPY_AND_ASSIGN(GalleryWatchManagerTest);
187};
188
189TEST_F(GalleryWatchManagerTest, Basic) {
190  base::ScopedTempDir temp_dir;
191  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
192  MediaGalleryPrefId id = AddGallery(temp_dir.path());
193
194  base::RunLoop loop;
195  if (GalleryWatchesSupported()) {
196    manager()->AddWatch(profile(),
197                        extension(),
198                        id,
199                        base::Bind(&ConfirmWatch, base::Unretained(&loop)));
200  } else {
201    manager()->AddWatch(
202        profile(),
203        extension(),
204        id,
205        base::Bind(&ExpectWatchError,
206                   base::Unretained(&loop),
207                   GalleryWatchManager::kCouldNotWatchGalleryError));
208  }
209  loop.Run();
210}
211
212TEST_F(GalleryWatchManagerTest, AddAndRemoveTwoWatches) {
213  if (!GalleryWatchesSupported())
214    return;
215
216  EXPECT_TRUE(manager()->GetWatchSet(profile(), extension()->id()).empty());
217
218  base::ScopedTempDir temp1;
219  ASSERT_TRUE(temp1.CreateUniqueTempDir());
220  MediaGalleryPrefId id1 = AddGallery(temp1.path());
221
222  base::ScopedTempDir temp2;
223  ASSERT_TRUE(temp2.CreateUniqueTempDir());
224  MediaGalleryPrefId id2 = AddGallery(temp2.path());
225
226  // Add first watch and test it was added correctly.
227  AddAndConfirmWatch(id1);
228  MediaGalleryPrefIdSet set1 =
229      manager()->GetWatchSet(profile(), extension()->id());
230  EXPECT_EQ(1u, set1.size());
231  EXPECT_TRUE(ContainsKey(set1, id1));
232
233  // Test that the second watch was added correctly too.
234  AddAndConfirmWatch(id2);
235  MediaGalleryPrefIdSet set2 =
236      manager()->GetWatchSet(profile(), extension()->id());
237  EXPECT_EQ(2u, set2.size());
238  EXPECT_TRUE(ContainsKey(set2, id1));
239  EXPECT_TRUE(ContainsKey(set2, id2));
240
241  // Remove first watch and test that the second is still in there.
242  manager()->RemoveWatch(profile(), extension()->id(), id1);
243  MediaGalleryPrefIdSet set3 =
244      manager()->GetWatchSet(profile(), extension()->id());
245  EXPECT_EQ(1u, set3.size());
246  EXPECT_TRUE(ContainsKey(set3, id2));
247
248  // Try removing the first watch again and test that it has no effect.
249  manager()->RemoveWatch(profile(), extension()->id(), id1);
250  EXPECT_EQ(1u, manager()->GetWatchSet(profile(), extension()->id()).size());
251
252  // Remove the second watch and test that the new watch set is empty.
253  manager()->RemoveWatch(profile(), extension()->id(), id2);
254  EXPECT_TRUE(manager()->GetWatchSet(profile(), extension()->id()).empty());
255}
256
257TEST_F(GalleryWatchManagerTest, RemoveAllWatches) {
258  if (!GalleryWatchesSupported())
259    return;
260
261  base::ScopedTempDir temp1;
262  ASSERT_TRUE(temp1.CreateUniqueTempDir());
263  MediaGalleryPrefId id1 = AddGallery(temp1.path());
264
265  base::ScopedTempDir temp2;
266  ASSERT_TRUE(temp2.CreateUniqueTempDir());
267  MediaGalleryPrefId id2 = AddGallery(temp2.path());
268
269  // Add watches.
270  AddAndConfirmWatch(id1);
271  AddAndConfirmWatch(id2);
272
273  EXPECT_EQ(2u, manager()->GetWatchSet(profile(), extension()->id()).size());
274
275  // RemoveAllWatches using a different extension ID and verify watches remain.
276  manager()->RemoveAllWatches(profile(), "OtherExtensionId");
277  EXPECT_EQ(2u, manager()->GetWatchSet(profile(), extension()->id()).size());
278
279  // RemoveAllWatches using the correct extension ID and verify watches gone.
280
281  manager()->RemoveAllWatches(profile(), extension()->id());
282  EXPECT_TRUE(manager()->GetWatchSet(profile(), extension()->id()).empty());
283}
284
285TEST_F(GalleryWatchManagerTest, DropWatchOnGalleryRemoved) {
286  if (!GalleryWatchesSupported())
287    return;
288
289  base::ScopedTempDir temp_dir;
290  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
291  MediaGalleryPrefId id = AddGallery(temp_dir.path());
292  AddAndConfirmWatch(id);
293
294  base::RunLoop success_loop;
295  ExpectGalleryWatchDropped(&success_loop);
296  gallery_prefs()->EraseGalleryById(id);
297  success_loop.Run();
298}
299
300TEST_F(GalleryWatchManagerTest, DropWatchOnGalleryPermissionRevoked) {
301  if (!GalleryWatchesSupported())
302    return;
303
304  base::ScopedTempDir temp_dir;
305  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
306  MediaGalleryPrefId id = AddGallery(temp_dir.path());
307  AddAndConfirmWatch(id);
308
309  base::RunLoop success_loop;
310  ExpectGalleryWatchDropped(&success_loop);
311  gallery_prefs()->SetGalleryPermissionForExtension(*extension(), id, false);
312  success_loop.Run();
313}
314
315TEST_F(GalleryWatchManagerTest, TestWatchOperation) {
316  if (!GalleryWatchesSupported())
317    return;
318
319  base::ScopedTempDir temp_dir;
320  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
321  MediaGalleryPrefId id = AddGallery(temp_dir.path());
322  AddAndConfirmWatch(id);
323
324  base::RunLoop success_loop;
325  ExpectGalleryChanged(&success_loop);
326  ASSERT_EQ(
327      4, base::WriteFile(temp_dir.path().AppendASCII("fake file"), "blah", 4));
328  success_loop.Run();
329}
330
331}  // namespace component_updater
332