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 "chrome/browser/media_galleries/media_folder_finder.h"
6
7#include <set>
8#include <string>
9
10#include "base/base_paths.h"
11#include "base/bind.h"
12#include "base/files/file_util.h"
13#include "base/files/scoped_temp_dir.h"
14#include "base/strings/stringprintf.h"
15#include "base/test/scoped_path_override.h"
16#include "base/threading/sequenced_worker_pool.h"
17#include "chrome/browser/media_galleries/media_scan_types.h"
18#include "chrome/common/chrome_paths.h"
19#include "content/public/browser/browser_thread.h"
20#include "content/public/test/test_browser_thread_bundle.h"
21#include "content/public/test/test_utils.h"
22
23class MediaFolderFinderTest : public testing::Test {
24 public:
25  MediaFolderFinderTest() {
26  }
27
28  virtual ~MediaFolderFinderTest() {
29  }
30
31  virtual void SetUp() OVERRIDE {
32    ASSERT_TRUE(fake_dir_.CreateUniqueTempDir());
33  }
34
35  virtual void TearDown() OVERRIDE {
36    ASSERT_EQ(NULL, media_folder_finder_.get());
37  }
38
39 protected:
40  void CreateMediaFolderFinder(
41      const std::vector<base::FilePath> roots,
42      bool expected_success,
43      const MediaFolderFinder::MediaFolderFinderResults& expected_results) {
44    EXPECT_EQ(NULL, media_folder_finder_.get());
45    received_results_ = false;
46    expected_success_ = expected_success;
47    expected_results_ = expected_results;
48    media_folder_finder_.reset(
49        new MediaFolderFinder(base::Bind(&MediaFolderFinderTest::OnGotResults,
50                                         base::Unretained(this))));
51    media_folder_finder_->SetRootsForTesting(roots);
52  }
53
54  void StartScan() {
55    media_folder_finder_->StartScan();
56  }
57
58  void DeleteMediaFolderFinder() {
59    EXPECT_TRUE(media_folder_finder_.get() != NULL);
60    media_folder_finder_.reset();
61  }
62
63  bool received_results() const {
64    return received_results_;
65  }
66
67  const base::FilePath& fake_dir() const {
68    return fake_dir_.path();
69  }
70
71  void CreateTestDir(const base::FilePath& parent_dir) {
72    if (parent_dir == fake_dir())
73      return;
74
75    ASSERT_TRUE(fake_dir().IsParent(parent_dir));
76    ASSERT_TRUE(base::CreateDirectory(parent_dir));
77  }
78
79  void CreateTestFile(const base::FilePath& parent_dir,
80                      MediaGalleryScanFileType type,
81                      size_t count,
82                      bool big,
83                      MediaFolderFinder::MediaFolderFinderResults* results) {
84    CreateTestDir(parent_dir);
85
86    std::string extension;
87    size_t filesize;
88    MediaGalleryScanResult& result = (*results)[parent_dir];
89    switch (type) {
90      case MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE:
91        extension = "jpg";
92        filesize = 10;
93        result.image_count += count;
94        break;
95      case MEDIA_GALLERY_SCAN_FILE_TYPE_AUDIO:
96        extension = "wav";
97        filesize = 20;
98        result.audio_count += count;
99        break;
100      case MEDIA_GALLERY_SCAN_FILE_TYPE_VIDEO:
101        extension = "avi";
102        filesize = 30;
103        result.video_count += count;
104        break;
105      case MEDIA_GALLERY_SCAN_FILE_TYPE_UNKNOWN:
106        extension = "txt";
107        filesize = 10;
108        if (IsEmptyScanResult(result))
109          results->erase(parent_dir);
110        break;
111      default:
112        NOTREACHED();
113        return;
114    }
115    if (big)
116      filesize *= 100000;
117
118    for (size_t i = 0; i < count; ++i) {
119      base::FilePath test_file(parent_dir.AppendASCII("dummy." + extension));
120      int uniquifier =
121          base::GetUniquePathNumber(test_file, base::FilePath::StringType());
122      if (uniquifier > 0) {
123        test_file = test_file.InsertBeforeExtensionASCII(
124            base::StringPrintf(" (%d)", uniquifier));
125        filesize += uniquifier;
126      }
127
128      std::string dummy_data;
129      dummy_data.resize(filesize);
130
131      int bytes_written =
132          base::WriteFile(test_file, dummy_data.c_str(), filesize);
133      ASSERT_GE(bytes_written, 0);
134      ASSERT_EQ(filesize, static_cast<size_t>(bytes_written));
135    }
136  }
137
138  void RunLoopUntilReceivedCallback() {
139    while (!received_results())
140      content::RunAllBlockingPoolTasksUntilIdle();
141  }
142
143 private:
144  void OnGotResults(
145      bool success,
146      const MediaFolderFinder::MediaFolderFinderResults& results) {
147    received_results_ = true;
148    EXPECT_EQ(expected_success_, success);
149    std::set<base::FilePath> expected_keys =
150        GetKeysFromResults(expected_results_);
151    ASSERT_EQ(expected_keys, GetKeysFromResults(results));
152    for (MediaFolderFinder::MediaFolderFinderResults::const_iterator it =
153             results.begin();
154         it != results.end(); ++it) {
155      const base::FilePath& folder = it->first;
156      const MediaGalleryScanResult& expected = it->second;
157      const MediaGalleryScanResult& actual = results.find(folder)->second;
158      EXPECT_EQ(expected.image_count, actual.image_count)
159          << " Image count for " << folder.value();
160      EXPECT_EQ(expected.audio_count, actual.audio_count)
161          << " Audio count for " << folder.value();
162      EXPECT_EQ(expected.video_count, actual.video_count)
163          << " Video count for " << folder.value();
164    }
165  }
166
167  std::set<base::FilePath> GetKeysFromResults(
168      const MediaFolderFinder::MediaFolderFinderResults& results) {
169    std::set<base::FilePath> keys;
170    for (MediaFolderFinder::MediaFolderFinderResults::const_iterator it =
171             results.begin();
172         it != results.end(); ++it) {
173      keys.insert(it->first);
174    }
175    return keys;
176  }
177
178  content::TestBrowserThreadBundle thread_bundle_;
179
180  base::ScopedTempDir fake_dir_;
181
182  scoped_ptr<MediaFolderFinder> media_folder_finder_;
183
184  bool expected_success_;
185  MediaFolderFinder::MediaFolderFinderResults expected_results_;
186  bool received_results_;
187
188  DISALLOW_COPY_AND_ASSIGN(MediaFolderFinderTest);
189};
190
191TEST_F(MediaFolderFinderTest, NoScan) {
192  MediaFolderFinder::MediaFolderFinderResults expected_results;
193  std::vector<base::FilePath> folders;
194  folders.push_back(fake_dir());
195  CreateMediaFolderFinder(folders, false, expected_results);
196  DeleteMediaFolderFinder();
197  EXPECT_TRUE(received_results());
198}
199
200TEST_F(MediaFolderFinderTest, ScanAndCancel) {
201  MediaFolderFinder::MediaFolderFinderResults expected_results;
202  std::vector<base::FilePath> folders;
203  folders.push_back(fake_dir());
204  CreateMediaFolderFinder(folders, false, expected_results);
205  StartScan();
206  DeleteMediaFolderFinder();
207  content::RunAllBlockingPoolTasksUntilIdle();
208  EXPECT_TRUE(received_results());
209}
210
211TEST_F(MediaFolderFinderTest, ScanNothing) {
212  MediaFolderFinder::MediaFolderFinderResults expected_results;
213  std::vector<base::FilePath> folders;
214  CreateMediaFolderFinder(folders, true, expected_results);
215  StartScan();
216  RunLoopUntilReceivedCallback();
217  DeleteMediaFolderFinder();
218}
219
220TEST_F(MediaFolderFinderTest, EmptyScan) {
221  MediaFolderFinder::MediaFolderFinderResults expected_results;
222  std::vector<base::FilePath> folders;
223  folders.push_back(fake_dir());
224  CreateMediaFolderFinder(folders, true, expected_results);
225  StartScan();
226  RunLoopUntilReceivedCallback();
227  DeleteMediaFolderFinder();
228}
229
230TEST_F(MediaFolderFinderTest, ScanMediaFiles) {
231  MediaFolderFinder::MediaFolderFinderResults expected_results;
232  std::vector<base::FilePath> folders;
233  folders.push_back(fake_dir());
234
235  base::FilePath dir1 = fake_dir().AppendASCII("dir1");
236  base::FilePath dir2 = fake_dir().AppendASCII("dir2");
237  base::FilePath dir2_3 = dir2.AppendASCII("dir2_3");
238  base::FilePath dir2_4 = dir2.AppendASCII("dir2_4");
239  base::FilePath dir2_4_5 = dir2_4.AppendASCII("dir2_4_5");
240  base::FilePath dir2_4_empty = dir2_4.AppendASCII("dir2_4_empty");
241  base::FilePath dir_empty = fake_dir().AppendASCII("dir_empty");
242
243  CreateTestFile(dir1, MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE, 2, true,
244                 &expected_results);
245  CreateTestFile(dir1, MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE, 1, false,
246                 &expected_results);
247  CreateTestFile(dir1, MEDIA_GALLERY_SCAN_FILE_TYPE_UNKNOWN, 1, false,
248                 &expected_results);
249  CreateTestFile(dir2_3, MEDIA_GALLERY_SCAN_FILE_TYPE_AUDIO, 4, true,
250                 &expected_results);
251  CreateTestFile(dir2_3, MEDIA_GALLERY_SCAN_FILE_TYPE_AUDIO, 3, false,
252                 &expected_results);
253  CreateTestFile(dir2_4, MEDIA_GALLERY_SCAN_FILE_TYPE_UNKNOWN, 5, false,
254                 &expected_results);
255  CreateTestFile(dir2_4_5, MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE, 2, true,
256                 &expected_results);
257  CreateTestFile(dir2_4_5, MEDIA_GALLERY_SCAN_FILE_TYPE_AUDIO, 4, true,
258                 &expected_results);
259  CreateTestFile(dir2_4_5, MEDIA_GALLERY_SCAN_FILE_TYPE_VIDEO, 1, true,
260                 &expected_results);
261  CreateTestFile(dir2_4_5, MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE, 5, false,
262                 &expected_results);
263  CreateTestFile(dir2_4_5, MEDIA_GALLERY_SCAN_FILE_TYPE_VIDEO, 3, false,
264                 &expected_results);
265  CreateTestFile(dir2_4_5, MEDIA_GALLERY_SCAN_FILE_TYPE_UNKNOWN, 3, true,
266                 &expected_results);
267  CreateTestDir(dir2_4_empty);
268  CreateTestDir(dir_empty);
269
270  CreateMediaFolderFinder(folders, true, expected_results);
271  StartScan();
272  RunLoopUntilReceivedCallback();
273  DeleteMediaFolderFinder();
274}
275
276TEST_F(MediaFolderFinderTest, SkipHiddenFiles) {
277  MediaFolderFinder::MediaFolderFinderResults expected_results;
278  std::vector<base::FilePath> folders;
279  folders.push_back(fake_dir());
280
281  base::FilePath hidden_dir = fake_dir().AppendASCII(".hidden");
282
283  CreateTestFile(hidden_dir, MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE, 2, true,
284                 &expected_results);
285  expected_results.erase(hidden_dir);
286
287  CreateMediaFolderFinder(folders, true, expected_results);
288  StartScan();
289  RunLoopUntilReceivedCallback();
290  DeleteMediaFolderFinder();
291}
292
293TEST_F(MediaFolderFinderTest, ScanIgnoresSmallMediaFiles) {
294  MediaFolderFinder::MediaFolderFinderResults expected_results;
295  std::vector<base::FilePath> folders;
296  folders.push_back(fake_dir());
297
298  base::FilePath dir1 = fake_dir().AppendASCII("dir1");
299  base::FilePath dir2 = fake_dir().AppendASCII("dir2");
300  base::FilePath dir_empty = fake_dir().AppendASCII("dir_empty");
301
302  CreateTestFile(dir1, MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE, 2, true,
303                 &expected_results);
304  CreateTestFile(dir1, MEDIA_GALLERY_SCAN_FILE_TYPE_VIDEO, 1, false,
305                 &expected_results);
306  CreateTestFile(dir1, MEDIA_GALLERY_SCAN_FILE_TYPE_UNKNOWN, 1, false,
307                 &expected_results);
308  CreateTestFile(dir2, MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE, 1, false,
309                 &expected_results);
310  CreateTestFile(dir2, MEDIA_GALLERY_SCAN_FILE_TYPE_AUDIO, 3, false,
311                 &expected_results);
312  CreateTestFile(dir2, MEDIA_GALLERY_SCAN_FILE_TYPE_VIDEO, 5, false,
313                 &expected_results);
314  CreateTestFile(dir2, MEDIA_GALLERY_SCAN_FILE_TYPE_UNKNOWN, 1, true,
315                 &expected_results);
316  CreateTestDir(dir_empty);
317  ASSERT_EQ(1U, expected_results.erase(dir2));
318
319  CreateMediaFolderFinder(folders, true, expected_results);
320  StartScan();
321  RunLoopUntilReceivedCallback();
322  DeleteMediaFolderFinder();
323}
324
325TEST_F(MediaFolderFinderTest, Overlap) {
326  MediaFolderFinder::MediaFolderFinderResults expected_results;
327  std::vector<base::FilePath> folders;
328  folders.push_back(fake_dir());
329  folders.push_back(fake_dir());
330
331  base::FilePath dir1 = fake_dir().AppendASCII("dir1");
332  folders.push_back(dir1);
333  folders.push_back(dir1);
334
335  CreateTestFile(dir1, MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE, 1, true,
336                 &expected_results);
337
338  CreateMediaFolderFinder(folders, true, expected_results);
339  StartScan();
340  RunLoopUntilReceivedCallback();
341  DeleteMediaFolderFinder();
342}
343
344TEST_F(MediaFolderFinderTest, Prune) {
345  MediaFolderFinder::MediaFolderFinderResults expected_results;
346  std::vector<base::FilePath> folders;
347  folders.push_back(fake_dir());
348
349#if defined(OS_WIN)
350  int pruned_dir_key = base::DIR_IE_INTERNET_CACHE;
351#elif defined(OS_MACOSX)
352  int pruned_dir_key = chrome::DIR_USER_LIBRARY;
353#else
354  int pruned_dir_key = base::DIR_CACHE;
355#endif
356
357  base::FilePath fake_pruned_dir = fake_dir().AppendASCII("dir1");
358  base::ScopedPathOverride scoped_fake_pruned_dir_override(pruned_dir_key,
359                                                           fake_pruned_dir);
360
361  CreateTestFile(fake_dir(), MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE, 1, true,
362                 &expected_results);
363  CreateTestFile(fake_pruned_dir, MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE, 1, true,
364                 &expected_results);
365
366  base::FilePath test_dir = fake_pruned_dir.AppendASCII("dir2");
367  CreateTestFile(test_dir, MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE, 1, true,
368                 &expected_results);
369
370  // |fake_pruned_dir| and |test_dir| are pruned.
371  expected_results.erase(fake_pruned_dir);
372  expected_results.erase(test_dir);
373
374  CreateMediaFolderFinder(folders, true, expected_results);
375  StartScan();
376  RunLoopUntilReceivedCallback();
377  DeleteMediaFolderFinder();
378}
379
380TEST_F(MediaFolderFinderTest, Graylist) {
381  MediaFolderFinder::MediaFolderFinderResults expected_results;
382  std::vector<base::FilePath> folders;
383  folders.push_back(fake_dir());
384
385  base::FilePath fake_home_dir = fake_dir().AppendASCII("dir1");
386  base::FilePath test_dir = fake_home_dir.AppendASCII("dir2");
387  base::ScopedPathOverride scoped_fake_home_dir_override(base::DIR_HOME,
388                                                         fake_home_dir);
389
390  CreateTestFile(fake_dir(), MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE, 1, true,
391                 &expected_results);
392  CreateTestFile(fake_home_dir, MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE, 1, true,
393                 &expected_results);
394  CreateTestFile(test_dir, MEDIA_GALLERY_SCAN_FILE_TYPE_IMAGE, 1, true,
395                 &expected_results);
396
397  // |fake_home_dir| and its ancestors do not show up in results.
398  expected_results.erase(fake_dir());
399  expected_results.erase(fake_home_dir);
400
401  CreateMediaFolderFinder(folders, true, expected_results);
402  StartScan();
403  RunLoopUntilReceivedCallback();
404  DeleteMediaFolderFinder();
405}
406