1// Copyright 2013 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 <map>
6#include <set>
7#include <string>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/bind_helpers.h"
12#include "base/files/file_util.h"
13#include "base/files/scoped_temp_dir.h"
14#include "base/run_loop.h"
15#include "base/synchronization/waitable_event.h"
16#include "base/time/time.h"
17#include "chrome/browser/media_galleries/fileapi/iphoto_data_provider.h"
18#include "chrome/browser/media_galleries/fileapi/iphoto_file_util.h"
19#include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
20#include "chrome/browser/media_galleries/fileapi/media_path_filter.h"
21#include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
22#include "content/public/browser/browser_thread.h"
23#include "content/public/test/mock_special_storage_policy.h"
24#include "content/public/test/test_browser_thread.h"
25#include "content/public/test/test_file_system_options.h"
26#include "storage/browser/fileapi/async_file_util.h"
27#include "storage/browser/fileapi/external_mount_points.h"
28#include "storage/browser/fileapi/file_system_context.h"
29#include "storage/browser/fileapi/file_system_operation_context.h"
30#include "storage/browser/fileapi/file_system_operation_runner.h"
31#include "testing/gtest/include/gtest/gtest.h"
32
33using storage::FileSystemOperationContext;
34using storage::FileSystemOperation;
35using storage::FileSystemURL;
36
37namespace iphoto {
38
39namespace {
40
41void ReadDirectoryTestHelperCallback(
42    base::RunLoop* run_loop,
43    FileSystemOperation::FileEntryList* contents,
44    bool* completed,
45    base::File::Error error,
46    const FileSystemOperation::FileEntryList& file_list,
47    bool has_more) {
48  DCHECK(!*completed);
49  *completed = !has_more && error == base::File::FILE_OK;
50  *contents = file_list;
51  run_loop->Quit();
52}
53
54void ReadDirectoryTestHelper(storage::FileSystemOperationRunner* runner,
55                             const FileSystemURL& url,
56                             FileSystemOperation::FileEntryList* contents,
57                             bool* completed) {
58  DCHECK(contents);
59  DCHECK(completed);
60  base::RunLoop run_loop;
61  runner->ReadDirectory(
62      url, base::Bind(&ReadDirectoryTestHelperCallback, &run_loop, contents,
63                      completed));
64  run_loop.Run();
65}
66
67}  // namespace
68
69class TestIPhotoDataProvider : public IPhotoDataProvider {
70 public:
71  explicit TestIPhotoDataProvider(const base::FilePath& fake_library_path)
72     : IPhotoDataProvider(fake_library_path) {
73    EXPECT_TRUE(fake_auto_add_dir_.CreateUniqueTempDir());
74  }
75
76  virtual ~TestIPhotoDataProvider() {}
77
78  virtual void RefreshData(const ReadyCallback& ready_callback) OVERRIDE {
79    ready_callback.Run(true /* success */);
80  }
81
82  virtual std::vector<std::string> GetAlbumNames() const OVERRIDE {
83    std::vector<std::string> names;
84    names.push_back("Album1");
85    names.push_back("has_originals");
86    return names;
87  }
88
89  virtual std::map<std::string, base::FilePath> GetAlbumContents(
90      const std::string& album) const OVERRIDE {
91    std::map<std::string, base::FilePath> contents;
92    contents["a.jpg"] = library_path().AppendASCII("a.jpg");
93    return contents;
94  }
95
96  virtual base::FilePath GetPhotoLocationInAlbum(
97      const std::string& album,
98      const std::string& filename) const OVERRIDE {
99    return library_path().AppendASCII("a.jpg");
100  }
101
102  virtual bool HasOriginals(const std::string& album) const OVERRIDE {
103    return (album == "has_originals");
104  }
105
106  virtual std::map<std::string, base::FilePath> GetOriginals(
107      const std::string& album) const OVERRIDE {
108    std::map<std::string, base::FilePath> contents;
109    contents["a.jpg"] = library_path().AppendASCII("orig.jpg");
110    return contents;
111  }
112
113  virtual base::FilePath GetOriginalPhotoLocation(
114      const std::string& album,
115      const std::string& filename) const OVERRIDE {
116    return library_path().AppendASCII("orig.jpg");
117  }
118
119 private:
120  base::ScopedTempDir fake_auto_add_dir_;
121};
122
123class TestIPhotoFileUtil : public IPhotoFileUtil {
124 public:
125  explicit TestIPhotoFileUtil(MediaPathFilter* media_path_filter,
126                              IPhotoDataProvider* data_provider)
127      : IPhotoFileUtil(media_path_filter),
128        data_provider_(data_provider) {
129  }
130  virtual ~TestIPhotoFileUtil() {}
131
132 private:
133  virtual IPhotoDataProvider* GetDataProvider() OVERRIDE {
134    return data_provider_;
135  }
136
137  IPhotoDataProvider* data_provider_;
138};
139
140class TestMediaFileSystemBackend : public MediaFileSystemBackend {
141 public:
142  TestMediaFileSystemBackend(const base::FilePath& profile_path,
143                             IPhotoFileUtil* iphoto_file_util)
144      : MediaFileSystemBackend(
145            profile_path,
146            MediaFileSystemBackend::MediaTaskRunner().get()),
147        test_file_util_(iphoto_file_util) {}
148
149  virtual storage::AsyncFileUtil* GetAsyncFileUtil(
150      storage::FileSystemType type) OVERRIDE {
151    if (type != storage::kFileSystemTypeIphoto)
152      return NULL;
153
154    return test_file_util_.get();
155  }
156
157 private:
158  scoped_ptr<storage::AsyncFileUtil> test_file_util_;
159};
160
161class IPhotoFileUtilTest : public testing::Test {
162 public:
163  IPhotoFileUtilTest()
164      : io_thread_(content::BrowserThread::IO, &message_loop_) {
165  }
166  virtual ~IPhotoFileUtilTest() {}
167
168  void SetUpDataProvider() {
169    ASSERT_TRUE(fake_library_dir_.CreateUniqueTempDir());
170    ASSERT_EQ(
171        0,
172        base::WriteFile(
173            fake_library_dir_.path().AppendASCII("a.jpg"),
174            NULL,
175            0));
176    ASSERT_EQ(
177        0,
178        base::WriteFile(
179            fake_library_dir_.path().AppendASCII("orig.jpg"),
180            NULL,
181            0));
182
183    iphoto_data_provider_.reset(
184        new TestIPhotoDataProvider(fake_library_dir_.path()));
185  }
186
187  virtual void SetUp() OVERRIDE {
188    ASSERT_TRUE(profile_dir_.CreateUniqueTempDir());
189    ImportedMediaGalleryRegistry::GetInstance()->Initialize();
190
191    scoped_refptr<storage::SpecialStoragePolicy> storage_policy =
192        new content::MockSpecialStoragePolicy();
193
194    // Initialize fake IPhotoDataProvider on media task runner thread.
195    MediaFileSystemBackend::MediaTaskRunner()->PostTask(
196        FROM_HERE,
197        base::Bind(&IPhotoFileUtilTest::SetUpDataProvider,
198                   base::Unretained(this)));
199    base::WaitableEvent event(true, false /* initially_signalled */);
200    MediaFileSystemBackend::MediaTaskRunner()->PostTask(
201        FROM_HERE,
202        base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event)));
203    event.Wait();
204
205    media_path_filter_.reset(new MediaPathFilter());
206    ScopedVector<storage::FileSystemBackend> additional_providers;
207    additional_providers.push_back(new TestMediaFileSystemBackend(
208        profile_dir_.path(),
209        new TestIPhotoFileUtil(media_path_filter_.get(),
210                               iphoto_data_provider_.get())));
211
212    file_system_context_ = new storage::FileSystemContext(
213        base::MessageLoopProxy::current().get(),
214        base::MessageLoopProxy::current().get(),
215        storage::ExternalMountPoints::CreateRefCounted().get(),
216        storage_policy.get(),
217        NULL,
218        additional_providers.Pass(),
219        std::vector<storage::URLRequestAutoMountHandler>(),
220        profile_dir_.path(),
221        content::CreateAllowFileAccessOptions());
222  }
223
224 protected:
225  void TestNonexistentFolder(const std::string& path_append) {
226    FileSystemOperation::FileEntryList contents;
227    FileSystemURL url = CreateURL(path_append);
228    bool completed = false;
229    ReadDirectoryTestHelper(operation_runner(), url, &contents, &completed);
230
231    ASSERT_FALSE(completed);
232  }
233
234  FileSystemURL CreateURL(const std::string& path) const {
235    base::FilePath virtual_path =
236        ImportedMediaGalleryRegistry::GetInstance()->ImportedRoot();
237    virtual_path = virtual_path.AppendASCII("iphoto");
238    virtual_path = virtual_path.AppendASCII(path);
239    return file_system_context_->CreateCrackedFileSystemURL(
240        GURL("http://www.example.com"),
241        storage::kFileSystemTypeIphoto,
242        virtual_path);
243  }
244
245  storage::FileSystemOperationRunner* operation_runner() const {
246    return file_system_context_->operation_runner();
247  }
248
249  scoped_refptr<storage::FileSystemContext> file_system_context() const {
250    return file_system_context_;
251  }
252
253  TestIPhotoDataProvider* data_provider() const {
254    return iphoto_data_provider_.get();
255  }
256
257 private:
258  base::MessageLoop message_loop_;
259  content::TestBrowserThread io_thread_;
260
261  base::ScopedTempDir profile_dir_;
262  base::ScopedTempDir fake_library_dir_;
263
264  scoped_refptr<storage::FileSystemContext> file_system_context_;
265  scoped_ptr<MediaPathFilter> media_path_filter_;
266  scoped_ptr<TestIPhotoDataProvider> iphoto_data_provider_;
267
268  DISALLOW_COPY_AND_ASSIGN(IPhotoFileUtilTest);
269};
270
271TEST_F(IPhotoFileUtilTest, RootContents) {
272  FileSystemOperation::FileEntryList contents;
273  FileSystemURL url = CreateURL("");
274  bool completed = false;
275  ReadDirectoryTestHelper(operation_runner(), url, &contents, &completed);
276
277  ASSERT_TRUE(completed);
278  ASSERT_EQ(1u, contents.size());
279
280  EXPECT_TRUE(contents.front().is_directory);
281
282  EXPECT_EQ(base::FilePath::FromUTF8Unsafe(kIPhotoAlbumsDir).value(),
283            contents.back().name);
284}
285
286TEST_F(IPhotoFileUtilTest, AlbumsDirectoryContents) {
287  FileSystemOperation::FileEntryList contents;
288  FileSystemURL url = CreateURL(kIPhotoAlbumsDir);
289  bool completed = false;
290  ReadDirectoryTestHelper(operation_runner(), url, &contents, &completed);
291
292  ASSERT_TRUE(completed);
293  ASSERT_EQ(2u, contents.size());
294
295  EXPECT_TRUE(contents.front().is_directory);
296
297  EXPECT_EQ("Album1", contents.front().name);
298  EXPECT_EQ("has_originals", contents.back().name);
299}
300
301TEST_F(IPhotoFileUtilTest, AlbumContents) {
302  FileSystemOperation::FileEntryList contents;
303  FileSystemURL url = CreateURL(std::string(kIPhotoAlbumsDir) + "/Album1");
304  bool completed = false;
305  ReadDirectoryTestHelper(operation_runner(), url, &contents, &completed);
306
307  ASSERT_TRUE(completed);
308  ASSERT_EQ(1u, contents.size());
309
310  EXPECT_FALSE(contents.front().is_directory);
311
312  EXPECT_EQ("a.jpg", contents.back().name);
313}
314
315TEST_F(IPhotoFileUtilTest, BadAccess) {
316  FileSystemOperation::FileEntryList contents;
317  FileSystemURL url = CreateURL("None");
318  bool completed = false;
319  ReadDirectoryTestHelper(operation_runner(), url, &contents, &completed);
320  ASSERT_FALSE(completed);
321  ASSERT_EQ(0u, contents.size());
322
323  url = CreateURL(std::string(kIPhotoAlbumsDir) + "/NoAlbum");
324  completed = false;
325  ReadDirectoryTestHelper(operation_runner(), url, &contents, &completed);
326  ASSERT_FALSE(completed);
327  ASSERT_EQ(0u, contents.size());
328}
329
330TEST_F(IPhotoFileUtilTest, Originals) {
331  FileSystemOperation::FileEntryList contents;
332  FileSystemURL url =
333      CreateURL(std::string(kIPhotoAlbumsDir) + "/has_originals");
334  bool completed = false;
335  ReadDirectoryTestHelper(operation_runner(), url, &contents, &completed);
336
337  ASSERT_TRUE(completed);
338  ASSERT_EQ(2u, contents.size());
339  EXPECT_TRUE(contents.front().is_directory);
340  EXPECT_EQ("Originals", contents.front().name);
341  EXPECT_FALSE(contents.back().is_directory);
342  EXPECT_EQ("a.jpg", contents.back().name);
343
344  url = CreateURL(std::string(kIPhotoAlbumsDir) + "/has_originals/Originals");
345  completed = false;
346  ReadDirectoryTestHelper(operation_runner(), url, &contents, &completed);
347  ASSERT_TRUE(completed);
348  ASSERT_EQ(1u, contents.size());
349
350  EXPECT_FALSE(contents.front().is_directory);
351  EXPECT_EQ("a.jpg", contents.front().name);
352}
353
354}  // namespace iphoto
355