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