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/memory/scoped_ptr.h"
9#include "base/memory/weak_ptr.h"
10#include "base/run_loop.h"
11#include "base/strings/string16.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/time/time.h"
14#include "chrome/browser/extensions/test_extension_system.h"
15#include "chrome/browser/media_galleries/media_galleries_dialog_controller_test_util.h"
16#include "chrome/browser/media_galleries/media_galleries_preferences.h"
17#include "chrome/browser/media_galleries/media_galleries_scan_result_controller.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
33class MediaGalleriesScanResultControllerTest : public testing::Test {
34 public:
35  MediaGalleriesScanResultControllerTest()
36      : dialog_(NULL),
37        dialog_update_count_at_destruction_(0),
38        controller_(NULL),
39        profile_(new TestingProfile()),
40        weak_factory_(this) {
41  }
42
43  virtual ~MediaGalleriesScanResultControllerTest() {
44    EXPECT_FALSE(controller_);
45    EXPECT_FALSE(dialog_);
46  }
47
48  virtual void SetUp() OVERRIDE {
49    ASSERT_TRUE(storage_monitor::TestStorageMonitor::CreateAndInstall());
50
51    extensions::TestExtensionSystem* extension_system(
52        static_cast<extensions::TestExtensionSystem*>(
53            extensions::ExtensionSystem::Get(profile_.get())));
54    extension_system->CreateExtensionService(
55        CommandLine::ForCurrentProcess(), base::FilePath(), false);
56
57    gallery_prefs_.reset(new MediaGalleriesPreferences(profile_.get()));
58    base::RunLoop loop;
59    gallery_prefs_->EnsureInitialized(loop.QuitClosure());
60    loop.Run();
61
62    std::vector<std::string> read_permissions;
63    read_permissions.push_back(
64        extensions::MediaGalleriesPermission::kReadPermission);
65    extension_ = AddMediaGalleriesApp("read", read_permissions, profile_.get());
66  }
67
68  virtual void TearDown() OVERRIDE {
69    storage_monitor::TestStorageMonitor::Destroy();
70  }
71
72  void StartDialog() {
73    ASSERT_FALSE(controller_);
74    controller_ = new MediaGalleriesScanResultController(
75        *extension_.get(),
76        gallery_prefs_.get(),
77        base::Bind(
78            &MediaGalleriesScanResultControllerTest::CreateMockDialog,
79            base::Unretained(this)),
80        base::Bind(
81            &MediaGalleriesScanResultControllerTest::OnControllerDone,
82            base::Unretained(this)));
83  }
84
85  size_t GetFirstSectionSize() const {
86    return controller()->GetSectionEntries(0).size();
87  }
88
89  MediaGalleriesScanResultController* controller() const {
90    return controller_;
91  }
92
93  MockMediaGalleriesDialog* dialog() {
94    return dialog_;
95  }
96
97  int dialog_update_count_at_destruction() {
98    EXPECT_FALSE(dialog_);
99    return dialog_update_count_at_destruction_;
100  }
101
102  extensions::Extension* extension() {
103    return extension_.get();
104  }
105
106  MediaGalleriesPreferences* gallery_prefs() {
107    return gallery_prefs_.get();
108  }
109
110  MediaGalleryPrefId AddGallery(const std::string& path,
111                                MediaGalleryPrefInfo::Type type,
112                                int audio_count, int image_count,
113                                int video_count) {
114    MediaGalleryPrefInfo gallery_info;
115    gallery_prefs_->LookUpGalleryByPath(MakeMediaGalleriesTestingPath(path),
116                                        &gallery_info);
117    return gallery_prefs_->AddGallery(
118        gallery_info.device_id,
119        gallery_info.path,
120        type,
121        gallery_info.volume_label,
122        gallery_info.vendor_name,
123        gallery_info.model_name,
124        gallery_info.total_size_in_bytes,
125        gallery_info.last_attach_time,
126        audio_count, image_count, video_count);
127  }
128
129  MediaGalleryPrefId AddScanResult(const std::string& path, int audio_count,
130                                   int image_count, int video_count) {
131    return AddGallery(path, MediaGalleryPrefInfo::kScanResult, audio_count,
132                      image_count, video_count);
133  }
134
135 private:
136  MediaGalleriesDialog* CreateMockDialog(
137      MediaGalleriesDialogController* controller) {
138    EXPECT_FALSE(dialog_);
139    dialog_update_count_at_destruction_ = 0;
140    dialog_ = new MockMediaGalleriesDialog(base::Bind(
141        &MediaGalleriesScanResultControllerTest::OnDialogDestroyed,
142        weak_factory_.GetWeakPtr()));
143    return dialog_;
144  }
145
146  void OnDialogDestroyed(int update_count) {
147    EXPECT_TRUE(dialog_);
148    dialog_update_count_at_destruction_ = update_count;
149    dialog_ = NULL;
150  }
151
152  void OnControllerDone() {
153    controller_ = NULL;
154  }
155
156  // Needed for extension service & friends to work.
157  content::TestBrowserThreadBundle thread_bundle_;
158
159  // The dialog is owned by the controller, but this pointer should only be
160  // valid while the dialog is live within the controller.
161  MockMediaGalleriesDialog* dialog_;
162  int dialog_update_count_at_destruction_;
163
164  // The controller owns itself.
165  MediaGalleriesScanResultController* controller_;
166
167  scoped_refptr<extensions::Extension> extension_;
168
169  EnsureMediaDirectoriesExists mock_gallery_locations_;
170
171#if defined OS_CHROMEOS
172  chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
173  chromeos::ScopedTestCrosSettings test_cros_settings_;
174  chromeos::ScopedTestUserManager test_user_manager_;
175#endif
176
177  storage_monitor::TestStorageMonitor monitor_;
178  scoped_ptr<TestingProfile> profile_;
179  scoped_ptr<MediaGalleriesPreferences> gallery_prefs_;
180
181  base::WeakPtrFactory<MediaGalleriesScanResultControllerTest> weak_factory_;
182
183  DISALLOW_COPY_AND_ASSIGN(MediaGalleriesScanResultControllerTest);
184};
185
186TEST_F(MediaGalleriesScanResultControllerTest, EmptyDialog) {
187  StartDialog();
188  EXPECT_TRUE(controller());
189  EXPECT_TRUE(dialog());
190  EXPECT_EQ(0U, GetFirstSectionSize());
191
192  controller()->DialogFinished(true);
193  EXPECT_FALSE(controller());
194  EXPECT_FALSE(dialog());
195  EXPECT_EQ(0, dialog_update_count_at_destruction());
196}
197
198TEST_F(MediaGalleriesScanResultControllerTest, AddScanResults) {
199  // Start with two scan results.
200  MediaGalleryPrefId scan_id = AddScanResult("scan_id", 1, 0, 0);
201  MediaGalleryPrefId auto_id =
202      AddGallery("auto_id", MediaGalleryPrefInfo::kAutoDetected, 2, 0, 0);
203  EXPECT_EQ(0U, gallery_prefs()->GalleriesForExtension(*extension()).size());
204
205  // Show the dialog, but cancel it.
206  StartDialog();
207  EXPECT_EQ(2U, GetFirstSectionSize());
208  controller()->DialogFinished(false);
209  EXPECT_EQ(0U, gallery_prefs()->GalleriesForExtension(*extension()).size());
210
211  // Show the dialog, unselect both and accept it.
212  StartDialog();
213  EXPECT_EQ(2U, GetFirstSectionSize());
214  controller()->DidToggleEntry(scan_id, false);
215  controller()->DidToggleEntry(auto_id, false);
216  controller()->DialogFinished(true);
217  EXPECT_EQ(0U, gallery_prefs()->GalleriesForExtension(*extension()).size());
218
219  // Show the dialog, leave one selected and accept it.
220  StartDialog();
221  EXPECT_EQ(2U, GetFirstSectionSize());
222  controller()->DidToggleEntry(scan_id, false);
223  controller()->DialogFinished(true);
224  MediaGalleryPrefIdSet permitted =
225      gallery_prefs()->GalleriesForExtension(*extension());
226  ASSERT_EQ(1U, permitted.size());
227  EXPECT_EQ(auto_id, *permitted.begin());
228
229  // Show the dialog, toggle the remaining entry twice and then accept it.
230  StartDialog();
231  EXPECT_EQ(1U, GetFirstSectionSize());
232  controller()->DidToggleEntry(scan_id, false);
233  controller()->DidToggleEntry(scan_id, true);
234  controller()->DialogFinished(true);
235  EXPECT_EQ(2U, gallery_prefs()->GalleriesForExtension(*extension()).size());
236}
237
238TEST_F(MediaGalleriesScanResultControllerTest, Blacklisted) {
239  // Start with two scan results.
240  MediaGalleryPrefId scan_id = AddScanResult("scan_id", 1, 0, 0);
241  MediaGalleryPrefId auto_id =
242      AddGallery("auto_id", MediaGalleryPrefInfo::kAutoDetected, 2, 0, 0);
243  EXPECT_EQ(0U, gallery_prefs()->GalleriesForExtension(*extension()).size());
244
245  // Show the dialog, but cancel it.
246  StartDialog();
247  EXPECT_EQ(2U, GetFirstSectionSize());
248  controller()->DialogFinished(false);
249  EXPECT_EQ(0U, gallery_prefs()->GalleriesForExtension(*extension()).size());
250
251  // Blacklist one and try again.
252  gallery_prefs()->ForgetGalleryById(scan_id);
253  StartDialog();
254  EXPECT_EQ(1U, GetFirstSectionSize());
255  controller()->DialogFinished(false);
256
257  // Adding it as a user gallery should change its type.
258  AddGallery("scan_id", MediaGalleryPrefInfo::kUserAdded, 1, 0, 0);
259  StartDialog();
260  EXPECT_EQ(2U, GetFirstSectionSize());
261
262  // Blacklisting the other while the dialog is open should remove it.
263  gallery_prefs()->ForgetGalleryById(auto_id);
264  EXPECT_EQ(1U, GetFirstSectionSize());
265  controller()->DialogFinished(false);
266  EXPECT_EQ(0U, gallery_prefs()->GalleriesForExtension(*extension()).size());
267  EXPECT_EQ(1, dialog_update_count_at_destruction());
268}
269
270TEST_F(MediaGalleriesScanResultControllerTest, PrefUpdates) {
271  MediaGalleryPrefId selected = AddScanResult("selected", 1, 0, 0);
272  MediaGalleryPrefId unselected = AddScanResult("unselected", 1, 0, 0);
273  MediaGalleryPrefId selected_add_permission =
274      AddScanResult("selected_add_permission", 1, 0, 0);
275  MediaGalleryPrefId unselected_add_permission =
276      AddScanResult("unselected_add_permission", 1, 0, 0);
277  MediaGalleryPrefId selected_removed =
278      AddScanResult("selected_removed", 1, 0, 0);
279  MediaGalleryPrefId unselected_removed =
280      AddScanResult("unselected_removed", 1, 0, 0);
281  MediaGalleryPrefId selected_update =
282      AddScanResult("selected_update", 1, 0, 0);
283  MediaGalleryPrefId unselected_update =
284      AddScanResult("unselected_update", 1, 0, 0);
285
286  gallery_prefs()->AddGalleryByPath(MakeMediaGalleriesTestingPath("user"),
287                                    MediaGalleryPrefInfo::kUserAdded);
288  gallery_prefs()->AddGalleryByPath(
289      MakeMediaGalleriesTestingPath("auto_detected"),
290      MediaGalleryPrefInfo::kAutoDetected);
291  MediaGalleryPrefId blacklisted = gallery_prefs()->AddGalleryByPath(
292      MakeMediaGalleriesTestingPath("blacklisted"),
293      MediaGalleryPrefInfo::kAutoDetected);
294  gallery_prefs()->ForgetGalleryById(blacklisted);
295  EXPECT_EQ(0U, gallery_prefs()->GalleriesForExtension(*extension()).size());
296
297  StartDialog();
298  EXPECT_EQ(8U, GetFirstSectionSize());
299  controller()->DidToggleEntry(unselected, false);
300  controller()->DidToggleEntry(unselected_add_permission, false);
301  controller()->DidToggleEntry(unselected_removed, false);
302  controller()->DidToggleEntry(unselected_update, false);
303  EXPECT_EQ(0, dialog()->update_count());
304  EXPECT_EQ(8U, GetFirstSectionSize());
305
306  // Add permission.
307  gallery_prefs()->SetGalleryPermissionForExtension(*extension(),
308                                                    unselected_add_permission,
309                                                    true);
310  EXPECT_EQ(1, dialog()->update_count());
311  EXPECT_EQ(7U, GetFirstSectionSize());
312  gallery_prefs()->SetGalleryPermissionForExtension(*extension(),
313                                                    selected_add_permission,
314                                                    true);
315  EXPECT_EQ(2, dialog()->update_count());
316  EXPECT_EQ(6U, GetFirstSectionSize());
317
318  // Blacklist scan results.
319  gallery_prefs()->ForgetGalleryById(unselected_removed);
320  EXPECT_EQ(3, dialog()->update_count());
321  EXPECT_EQ(5U, GetFirstSectionSize());
322  gallery_prefs()->ForgetGalleryById(selected_removed);
323  EXPECT_EQ(4, dialog()->update_count());
324  EXPECT_EQ(4U, GetFirstSectionSize());
325
326  // Update names.
327  const MediaGalleryPrefInfo& unselected_update_info =
328      gallery_prefs()->known_galleries().find(unselected_update)->second;
329  gallery_prefs()->AddGallery(
330      unselected_update_info.device_id, base::FilePath(),
331      MediaGalleryPrefInfo::kScanResult,
332      base::ASCIIToUTF16("Updated & Unselected"),
333      base::string16(), base::string16(), 0, base::Time(), 1, 0, 0);
334  EXPECT_EQ(5, dialog()->update_count());
335  EXPECT_EQ(4U, GetFirstSectionSize());
336  const MediaGalleryPrefInfo& selected_update_info =
337      gallery_prefs()->known_galleries().find(selected_update)->second;
338  gallery_prefs()->AddGallery(
339      selected_update_info.device_id, base::FilePath(),
340      MediaGalleryPrefInfo::kScanResult,
341      base::ASCIIToUTF16("Updated & Selected"),
342      base::string16(), base::string16(), 0, base::Time(), 1, 0, 0);
343  EXPECT_EQ(6, dialog()->update_count());
344  EXPECT_EQ(4U, GetFirstSectionSize());
345
346  MediaGalleriesDialogController::Entries results =
347      controller()->GetSectionEntries(0);
348  EXPECT_EQ(selected, results[0].pref_info.pref_id);
349  EXPECT_TRUE(results[0].selected);
350  EXPECT_EQ(selected_update, results[1].pref_info.pref_id);
351  EXPECT_TRUE(results[1].selected);
352  EXPECT_EQ(base::ASCIIToUTF16("Updated & Selected"),
353            results[1].pref_info.volume_label);
354  EXPECT_EQ(unselected, results[2].pref_info.pref_id);
355  EXPECT_FALSE(results[2].selected);
356  EXPECT_EQ(unselected_update, results[3].pref_info.pref_id);
357  EXPECT_FALSE(results[3].selected);
358  EXPECT_EQ(base::ASCIIToUTF16("Updated & Unselected"),
359            results[3].pref_info.volume_label);
360
361  controller()->DialogFinished(true);
362  EXPECT_EQ(4U, gallery_prefs()->GalleriesForExtension(*extension()).size());
363  StartDialog();
364  EXPECT_EQ(2U, GetFirstSectionSize());
365  controller()->DialogFinished(false);
366}
367
368TEST_F(MediaGalleriesScanResultControllerTest, ForgetGallery) {
369  // Start with two scan results.
370  MediaGalleryPrefId scan1 = AddScanResult("scan1", 1, 0, 0);
371  MediaGalleryPrefId scan2 = AddScanResult("scan2", 2, 0, 0);
372  EXPECT_EQ(0U, gallery_prefs()->GalleriesForExtension(*extension()).size());
373
374  // Remove one and then cancel.
375  StartDialog();
376  EXPECT_EQ(2U, GetFirstSectionSize());
377  controller()->DidForgetEntry(scan1);
378  controller()->DialogFinished(false);
379  EXPECT_EQ(0U, gallery_prefs()->GalleriesForExtension(*extension()).size());
380
381  // Remove one and then have it blacklisted from prefs.
382  StartDialog();
383  EXPECT_EQ(2U, GetFirstSectionSize());
384  controller()->DidForgetEntry(scan1);
385  EXPECT_EQ(1, dialog()->update_count());
386  controller()->DidToggleEntry(scan2, false);  // Uncheck the second.
387  gallery_prefs()->ForgetGalleryById(scan1);
388  controller()->DialogFinished(true);
389  EXPECT_EQ(0U, gallery_prefs()->GalleriesForExtension(*extension()).size());
390  EXPECT_EQ(2, dialog_update_count_at_destruction());
391
392  // Remove the other.
393  StartDialog();
394  EXPECT_EQ(1U, GetFirstSectionSize());
395  controller()->DidForgetEntry(scan2);
396  controller()->DialogFinished(true);
397  EXPECT_EQ(0U, gallery_prefs()->GalleriesForExtension(*extension()).size());
398
399  // Check that nothing shows up.
400  StartDialog();
401  EXPECT_EQ(0U, GetFirstSectionSize());
402  controller()->DialogFinished(false);
403}
404
405TEST_F(MediaGalleriesScanResultControllerTest, SortOrder) {
406  // Intentionally out of order numerically and alphabetically.
407  MediaGalleryPrefId third = AddScanResult("third", 2, 2, 2);
408  MediaGalleryPrefId second =
409      AddGallery("second", MediaGalleryPrefInfo::kAutoDetected, 9, 0, 0);
410  MediaGalleryPrefId first = AddScanResult("first", 8, 2, 3);
411  MediaGalleryPrefId fifth = AddScanResult("abb", 3, 0, 0);
412  MediaGalleryPrefId fourth = AddScanResult("aaa", 3, 0, 0);
413
414  StartDialog();
415  MediaGalleriesDialogController::Entries results =
416      controller()->GetSectionEntries(0);
417  ASSERT_EQ(5U, results.size());
418  EXPECT_EQ(first, results[0].pref_info.pref_id);
419  EXPECT_EQ(second, results[1].pref_info.pref_id);
420  EXPECT_EQ(third, results[2].pref_info.pref_id);
421  EXPECT_EQ(fourth, results[3].pref_info.pref_id);
422  EXPECT_EQ(fifth, results[4].pref_info.pref_id);
423  controller()->DialogFinished(false);
424}
425