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