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