Home | History | Annotate | Download | only in fileapi
      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 <set>
      6 #include <string>
      7 #include <vector>
      8 
      9 #include "base/bind_helpers.h"
     10 #include "base/files/file_path.h"
     11 #include "base/files/file_util.h"
     12 #include "base/files/scoped_temp_dir.h"
     13 #include "base/memory/ref_counted.h"
     14 #include "base/memory/scoped_vector.h"
     15 #include "base/message_loop/message_loop.h"
     16 #include "base/message_loop/message_loop_proxy.h"
     17 #include "base/run_loop.h"
     18 #include "base/strings/stringprintf.h"
     19 #include "base/time/time.h"
     20 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
     21 #include "chrome/browser/media_galleries/fileapi/media_path_filter.h"
     22 #include "chrome/browser/media_galleries/fileapi/picasa_data_provider.h"
     23 #include "chrome/browser/media_galleries/fileapi/picasa_file_util.h"
     24 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
     25 #include "chrome/common/media_galleries/picasa_types.h"
     26 #include "chrome/common/media_galleries/pmp_constants.h"
     27 #include "content/public/browser/browser_thread.h"
     28 #include "content/public/test/mock_special_storage_policy.h"
     29 #include "content/public/test/test_browser_thread.h"
     30 #include "content/public/test/test_file_system_options.h"
     31 #include "storage/browser/fileapi/async_file_util.h"
     32 #include "storage/browser/fileapi/external_mount_points.h"
     33 #include "storage/browser/fileapi/file_system_context.h"
     34 #include "storage/browser/fileapi/file_system_operation_context.h"
     35 #include "storage/browser/fileapi/file_system_operation_runner.h"
     36 #include "storage/browser/fileapi/isolated_context.h"
     37 #include "storage/common/blob/shareable_file_reference.h"
     38 #include "testing/gtest/include/gtest/gtest.h"
     39 
     40 using storage::FileSystemOperationContext;
     41 using storage::FileSystemOperation;
     42 using storage::FileSystemURL;
     43 
     44 namespace picasa {
     45 
     46 namespace {
     47 
     48 base::Time::Exploded test_date_exploded = { 2013, 4, 0, 16, 0, 0, 0, 0 };
     49 
     50 bool WriteJPEGHeader(const base::FilePath& path) {
     51   const char kJpegHeader[] = "\xFF\xD8\xFF";  // Per HTML5 specification.
     52   return base::WriteFile(path, kJpegHeader, arraysize(kJpegHeader)) != -1;
     53 }
     54 
     55 class TestFolder {
     56  public:
     57   TestFolder(const std::string& name, const base::Time& timestamp,
     58              const std::string& uid, unsigned int image_files,
     59              unsigned int non_image_files)
     60       : name_(name),
     61         timestamp_(timestamp),
     62         uid_(uid),
     63         image_files_(image_files),
     64         non_image_files_(non_image_files),
     65         folder_info_("", base::Time(), "", base::FilePath()) {
     66   }
     67 
     68   bool Init() {
     69     if (!folder_dir_.CreateUniqueTempDir())
     70       return false;
     71 
     72     folder_info_ = AlbumInfo(name_, timestamp_, uid_, folder_dir_.path());
     73 
     74     for (unsigned int i = 0; i < image_files_; ++i) {
     75       std::string image_filename = base::StringPrintf("img%05d.jpg", i);
     76       image_filenames_.insert(image_filename);
     77 
     78       base::FilePath path = folder_dir_.path().AppendASCII(image_filename);
     79 
     80       if (!WriteJPEGHeader(path))
     81         return false;
     82     }
     83 
     84     for (unsigned int i = 0; i < non_image_files_; ++i) {
     85       base::FilePath path = folder_dir_.path().AppendASCII(
     86           base::StringPrintf("hello%05d.txt", i));
     87       if (base::WriteFile(path, NULL, 0) == -1)
     88         return false;
     89     }
     90 
     91     return true;
     92   }
     93 
     94   double GetVariantTimestamp() const {
     95     DCHECK(!folder_dir_.path().empty());
     96     base::Time variant_epoch = base::Time::FromLocalExploded(
     97         picasa::kPmpVariantTimeEpoch);
     98 
     99     int64 microseconds_since_epoch =
    100         (folder_info_.timestamp - variant_epoch).InMicroseconds();
    101 
    102     return static_cast<double>(microseconds_since_epoch) /
    103                                base::Time::kMicrosecondsPerDay;
    104   }
    105 
    106   const std::set<std::string>& image_filenames() const {
    107     DCHECK(!folder_dir_.path().empty());
    108     return image_filenames_;
    109   }
    110 
    111   const AlbumInfo& folder_info() const {
    112     DCHECK(!folder_dir_.path().empty());
    113     return folder_info_;
    114   }
    115 
    116   const base::Time& timestamp() const {
    117     return timestamp_;
    118   }
    119 
    120  private:
    121   const std::string name_;
    122   const base::Time timestamp_;
    123   const std::string uid_;
    124   unsigned int image_files_;
    125   unsigned int non_image_files_;
    126 
    127   std::set<std::string> image_filenames_;
    128 
    129   base::ScopedTempDir folder_dir_;
    130   AlbumInfo folder_info_;
    131 };
    132 
    133 void ReadDirectoryTestHelperCallback(
    134     base::RunLoop* run_loop,
    135     FileSystemOperation::FileEntryList* contents,
    136     bool* completed, base::File::Error error,
    137     const FileSystemOperation::FileEntryList& file_list,
    138     bool has_more) {
    139   DCHECK(!*completed);
    140   *completed = !has_more && error == base::File::FILE_OK;
    141   *contents = file_list;
    142   run_loop->Quit();
    143 }
    144 
    145 void ReadDirectoryTestHelper(storage::FileSystemOperationRunner* runner,
    146                              const FileSystemURL& url,
    147                              FileSystemOperation::FileEntryList* contents,
    148                              bool* completed) {
    149   DCHECK(contents);
    150   DCHECK(completed);
    151   base::RunLoop run_loop;
    152   runner->ReadDirectory(
    153       url, base::Bind(&ReadDirectoryTestHelperCallback, &run_loop, contents,
    154                       completed));
    155   run_loop.Run();
    156 }
    157 
    158 void SynchronouslyRunOnMediaTaskRunner(const base::Closure& closure) {
    159   base::RunLoop loop;
    160   MediaFileSystemBackend::MediaTaskRunner()->PostTaskAndReply(
    161       FROM_HERE,
    162       closure,
    163       loop.QuitClosure());
    164   loop.Run();
    165 }
    166 
    167 void CreateSnapshotFileTestHelperCallback(
    168     base::RunLoop* run_loop,
    169     base::File::Error* error,
    170     base::FilePath* platform_path_result,
    171     base::File::Error result,
    172     const base::File::Info& file_info,
    173     const base::FilePath& platform_path,
    174     const scoped_refptr<storage::ShareableFileReference>& file_ref) {
    175   DCHECK(run_loop);
    176   DCHECK(error);
    177   DCHECK(platform_path_result);
    178 
    179   *error = result;
    180   *platform_path_result = platform_path;
    181   run_loop->Quit();
    182 }
    183 
    184 }  // namespace
    185 
    186 class TestPicasaFileUtil : public PicasaFileUtil {
    187  public:
    188   TestPicasaFileUtil(MediaPathFilter* media_path_filter,
    189                      PicasaDataProvider* data_provider)
    190       : PicasaFileUtil(media_path_filter),
    191         data_provider_(data_provider) {
    192   }
    193   virtual ~TestPicasaFileUtil() {}
    194  private:
    195   virtual PicasaDataProvider* GetDataProvider() OVERRIDE {
    196     return data_provider_;
    197   }
    198 
    199   PicasaDataProvider* data_provider_;
    200 };
    201 
    202 class TestMediaFileSystemBackend : public MediaFileSystemBackend {
    203  public:
    204   TestMediaFileSystemBackend(const base::FilePath& profile_path,
    205                              PicasaFileUtil* picasa_file_util)
    206       : MediaFileSystemBackend(profile_path,
    207                                MediaFileSystemBackend::MediaTaskRunner().get()),
    208         test_file_util_(picasa_file_util) {}
    209 
    210   virtual storage::AsyncFileUtil* GetAsyncFileUtil(
    211       storage::FileSystemType type) OVERRIDE {
    212     if (type != storage::kFileSystemTypePicasa)
    213       return NULL;
    214 
    215     return test_file_util_.get();
    216   }
    217 
    218  private:
    219   scoped_ptr<storage::AsyncFileUtil> test_file_util_;
    220 };
    221 
    222 class PicasaFileUtilTest : public testing::Test {
    223  public:
    224   PicasaFileUtilTest()
    225       : io_thread_(content::BrowserThread::IO, &message_loop_) {
    226   }
    227   virtual ~PicasaFileUtilTest() {}
    228 
    229   virtual void SetUp() OVERRIDE {
    230     ASSERT_TRUE(profile_dir_.CreateUniqueTempDir());
    231     ImportedMediaGalleryRegistry::GetInstance()->Initialize();
    232 
    233     scoped_refptr<storage::SpecialStoragePolicy> storage_policy =
    234         new content::MockSpecialStoragePolicy();
    235 
    236     SynchronouslyRunOnMediaTaskRunner(base::Bind(
    237         &PicasaFileUtilTest::SetUpOnMediaTaskRunner, base::Unretained(this)));
    238 
    239     media_path_filter_.reset(new MediaPathFilter());
    240 
    241     ScopedVector<storage::FileSystemBackend> additional_providers;
    242     additional_providers.push_back(new TestMediaFileSystemBackend(
    243         profile_dir_.path(),
    244         new TestPicasaFileUtil(media_path_filter_.get(),
    245                                picasa_data_provider_.get())));
    246 
    247     file_system_context_ = new storage::FileSystemContext(
    248         base::MessageLoopProxy::current().get(),
    249         base::MessageLoopProxy::current().get(),
    250         storage::ExternalMountPoints::CreateRefCounted().get(),
    251         storage_policy.get(),
    252         NULL,
    253         additional_providers.Pass(),
    254         std::vector<storage::URLRequestAutoMountHandler>(),
    255         profile_dir_.path(),
    256         content::CreateAllowFileAccessOptions());
    257   }
    258 
    259   virtual void TearDown() OVERRIDE {
    260     SynchronouslyRunOnMediaTaskRunner(
    261         base::Bind(&PicasaFileUtilTest::TearDownOnMediaTaskRunner,
    262                    base::Unretained(this)));
    263   }
    264 
    265  protected:
    266   void SetUpOnMediaTaskRunner() {
    267     picasa_data_provider_.reset(new PicasaDataProvider(base::FilePath()));
    268   }
    269 
    270   void TearDownOnMediaTaskRunner() {
    271     picasa_data_provider_.reset();
    272   }
    273 
    274   // |test_folders| must be in alphabetical order for easy verification
    275   void SetupFolders(ScopedVector<TestFolder>* test_folders,
    276                     const std::vector<AlbumInfo>& albums,
    277                     const AlbumImagesMap& albums_images) {
    278     std::vector<AlbumInfo> folders;
    279     for (ScopedVector<TestFolder>::iterator it = test_folders->begin();
    280         it != test_folders->end(); ++it) {
    281       TestFolder* test_folder = *it;
    282       ASSERT_TRUE(test_folder->Init());
    283       folders.push_back(test_folder->folder_info());
    284     }
    285 
    286     PicasaDataProvider::UniquifyNames(albums,
    287                                       &picasa_data_provider_->album_map_);
    288     PicasaDataProvider::UniquifyNames(folders,
    289                                       &picasa_data_provider_->folder_map_);
    290     picasa_data_provider_->albums_images_ = albums_images;
    291     picasa_data_provider_->state_ =
    292         PicasaDataProvider::ALBUMS_IMAGES_FRESH_STATE;
    293   }
    294 
    295   void VerifyFolderDirectoryList(const ScopedVector<TestFolder>& test_folders) {
    296     FileSystemOperation::FileEntryList contents;
    297     FileSystemURL url = CreateURL(kPicasaDirFolders);
    298     bool completed = false;
    299     ReadDirectoryTestHelper(operation_runner(), url, &contents, &completed);
    300 
    301     ASSERT_TRUE(completed);
    302     ASSERT_EQ(test_folders.size(), contents.size());
    303 
    304     for (size_t i = 0; i < contents.size(); ++i) {
    305       EXPECT_TRUE(contents[i].is_directory);
    306 
    307       // Because the timestamp is written out as a floating point Microsoft
    308       // variant time, we only expect it to be accurate to within a second.
    309       base::TimeDelta delta = test_folders[i]->folder_info().timestamp -
    310                               contents[i].last_modified_time;
    311       EXPECT_LT(delta, base::TimeDelta::FromSeconds(1));
    312 
    313       FileSystemOperation::FileEntryList folder_contents;
    314       FileSystemURL folder_url = CreateURL(
    315           std::string(kPicasaDirFolders) + "/" +
    316           base::FilePath(contents[i].name).AsUTF8Unsafe());
    317       bool folder_read_completed = false;
    318       ReadDirectoryTestHelper(operation_runner(), folder_url, &folder_contents,
    319                               &folder_read_completed);
    320 
    321       EXPECT_TRUE(folder_read_completed);
    322 
    323       const std::set<std::string>& image_filenames =
    324           test_folders[i]->image_filenames();
    325 
    326       EXPECT_EQ(image_filenames.size(), folder_contents.size());
    327 
    328       for (FileSystemOperation::FileEntryList::const_iterator file_it =
    329                folder_contents.begin(); file_it != folder_contents.end();
    330            ++file_it) {
    331         EXPECT_EQ(1u, image_filenames.count(
    332             base::FilePath(file_it->name).AsUTF8Unsafe()));
    333       }
    334     }
    335   }
    336 
    337   std::string DateToPathString(const base::Time& time) {
    338     return PicasaDataProvider::DateToPathString(time);
    339   }
    340 
    341   void TestNonexistentDirectory(const std::string& path) {
    342     FileSystemOperation::FileEntryList contents;
    343     FileSystemURL url = CreateURL(path);
    344     bool completed = false;
    345     ReadDirectoryTestHelper(operation_runner(), url, &contents, &completed);
    346 
    347     ASSERT_FALSE(completed);
    348   }
    349 
    350   void TestEmptyDirectory(const std::string& path) {
    351     FileSystemOperation::FileEntryList contents;
    352     FileSystemURL url = CreateURL(path);
    353     bool completed = false;
    354     ReadDirectoryTestHelper(operation_runner(), url, &contents, &completed);
    355 
    356     ASSERT_TRUE(completed);
    357     EXPECT_EQ(0u, contents.size());
    358   }
    359 
    360   FileSystemURL CreateURL(const std::string& path) const {
    361     base::FilePath virtual_path =
    362         ImportedMediaGalleryRegistry::GetInstance()->ImportedRoot();
    363     virtual_path = virtual_path.AppendASCII("picasa");
    364     virtual_path = virtual_path.AppendASCII(path);
    365     return file_system_context_->CreateCrackedFileSystemURL(
    366         GURL("http://www.example.com"),
    367         storage::kFileSystemTypePicasa,
    368         virtual_path);
    369   }
    370 
    371   storage::FileSystemOperationRunner* operation_runner() const {
    372     return file_system_context_->operation_runner();
    373   }
    374 
    375   scoped_refptr<storage::FileSystemContext> file_system_context() const {
    376     return file_system_context_;
    377   }
    378 
    379  private:
    380   base::MessageLoop message_loop_;
    381   content::TestBrowserThread io_thread_;
    382 
    383   base::ScopedTempDir profile_dir_;
    384 
    385   scoped_refptr<storage::FileSystemContext> file_system_context_;
    386   scoped_ptr<PicasaDataProvider> picasa_data_provider_;
    387   scoped_ptr<MediaPathFilter> media_path_filter_;
    388 
    389   DISALLOW_COPY_AND_ASSIGN(PicasaFileUtilTest);
    390 };
    391 
    392 TEST_F(PicasaFileUtilTest, DateFormat) {
    393   base::Time::Exploded exploded_shortmonth = { 2013, 4, 0, 16, 0, 0, 0, 0 };
    394   base::Time shortmonth = base::Time::FromLocalExploded(exploded_shortmonth);
    395 
    396   base::Time::Exploded exploded_shortday = { 2013, 11, 0, 3, 0, 0, 0, 0 };
    397   base::Time shortday = base::Time::FromLocalExploded(exploded_shortday);
    398 
    399   EXPECT_EQ("2013-04-16", DateToPathString(shortmonth));
    400   EXPECT_EQ("2013-11-03", DateToPathString(shortday));
    401 }
    402 
    403 TEST_F(PicasaFileUtilTest, NameDeduplication) {
    404   ScopedVector<TestFolder> test_folders;
    405   std::vector<std::string> expected_names;
    406 
    407   base::Time test_date = base::Time::FromLocalExploded(test_date_exploded);
    408   base::Time test_date_2 = test_date - base::TimeDelta::FromDays(1);
    409 
    410   std::string test_date_string = DateToPathString(test_date);
    411   std::string test_date_2_string = DateToPathString(test_date_2);
    412 
    413   test_folders.push_back(
    414       new TestFolder("diff_date", test_date_2, "uuid3", 0, 0));
    415   expected_names.push_back("diff_date " + test_date_2_string);
    416 
    417   test_folders.push_back(
    418       new TestFolder("diff_date", test_date, "uuid2", 0, 0));
    419   expected_names.push_back("diff_date " + test_date_string);
    420 
    421   test_folders.push_back(
    422       new TestFolder("duplicate", test_date, "uuid4", 0, 0));
    423   expected_names.push_back("duplicate " + test_date_string + " (1)");
    424 
    425   test_folders.push_back(
    426       new TestFolder("duplicate", test_date, "uuid5", 0, 0));
    427   expected_names.push_back("duplicate " + test_date_string + " (2)");
    428 
    429   test_folders.push_back(
    430       new TestFolder("unique_name", test_date, "uuid1", 0, 0));
    431   expected_names.push_back("unique_name " + test_date_string);
    432 
    433   SetupFolders(&test_folders, std::vector<AlbumInfo>(), AlbumImagesMap());
    434 
    435   FileSystemOperation::FileEntryList contents;
    436   FileSystemURL url = CreateURL(kPicasaDirFolders);
    437   bool completed = false;
    438   ReadDirectoryTestHelper(operation_runner(), url, &contents, &completed);
    439 
    440   ASSERT_TRUE(completed);
    441   ASSERT_EQ(expected_names.size(), contents.size());
    442   for (size_t i = 0; i < contents.size(); ++i) {
    443     EXPECT_EQ(expected_names[i],
    444               base::FilePath(contents[i].name).AsUTF8Unsafe());
    445     EXPECT_EQ(test_folders[i]->timestamp(), contents[i].last_modified_time);
    446     EXPECT_TRUE(contents[i].is_directory);
    447   }
    448 }
    449 
    450 TEST_F(PicasaFileUtilTest, RootFolders) {
    451   ScopedVector<TestFolder> empty_folders_list;
    452   SetupFolders(&empty_folders_list, std::vector<AlbumInfo>(), AlbumImagesMap());
    453 
    454   FileSystemOperation::FileEntryList contents;
    455   FileSystemURL url = CreateURL("");
    456   bool completed = false;
    457   ReadDirectoryTestHelper(operation_runner(), url, &contents, &completed);
    458 
    459   ASSERT_TRUE(completed);
    460   ASSERT_EQ(2u, contents.size());
    461 
    462   EXPECT_TRUE(contents.front().is_directory);
    463   EXPECT_TRUE(contents.back().is_directory);
    464 
    465   EXPECT_EQ(0, contents.front().size);
    466   EXPECT_EQ(0, contents.back().size);
    467 
    468   EXPECT_EQ(FILE_PATH_LITERAL("albums"), contents.front().name);
    469   EXPECT_EQ(FILE_PATH_LITERAL("folders"), contents.back().name);
    470 }
    471 
    472 TEST_F(PicasaFileUtilTest, NonexistentFolder) {
    473   ScopedVector<TestFolder> empty_folders_list;
    474   SetupFolders(&empty_folders_list, std::vector<AlbumInfo>(), AlbumImagesMap());
    475 
    476   TestNonexistentDirectory(std::string(kPicasaDirFolders) + "/foo");
    477   TestNonexistentDirectory(std::string(kPicasaDirFolders) + "/foo/bar");
    478   TestNonexistentDirectory(std::string(kPicasaDirFolders) + "/foo/bar/baz");
    479 }
    480 
    481 TEST_F(PicasaFileUtilTest, FolderContentsTrivial) {
    482   ScopedVector<TestFolder> test_folders;
    483   base::Time test_date = base::Time::FromLocalExploded(test_date_exploded);
    484 
    485   test_folders.push_back(
    486       new TestFolder("folder-1-empty", test_date, "uid-empty", 0, 0));
    487   test_folders.push_back(
    488       new TestFolder("folder-2-images", test_date, "uid-images", 5, 0));
    489   test_folders.push_back(
    490       new TestFolder("folder-3-nonimages", test_date, "uid-nonimages", 0, 5));
    491   test_folders.push_back(
    492       new TestFolder("folder-4-both", test_date, "uid-both", 5, 5));
    493 
    494   SetupFolders(&test_folders, std::vector<AlbumInfo>(), AlbumImagesMap());
    495   VerifyFolderDirectoryList(test_folders);
    496 }
    497 
    498 TEST_F(PicasaFileUtilTest, FolderWithManyFiles) {
    499   ScopedVector<TestFolder> test_folders;
    500   base::Time test_date = base::Time::FromLocalExploded(test_date_exploded);
    501 
    502   test_folders.push_back(
    503       new TestFolder("folder-many-files", test_date, "uid-both", 50, 50));
    504 
    505   SetupFolders(&test_folders, std::vector<AlbumInfo>(), AlbumImagesMap());
    506   VerifyFolderDirectoryList(test_folders);
    507 }
    508 
    509 TEST_F(PicasaFileUtilTest, ManyFolders) {
    510   ScopedVector<TestFolder> test_folders;
    511   base::Time test_date = base::Time::FromLocalExploded(test_date_exploded);
    512 
    513   for (unsigned int i = 0; i < 50; ++i) {
    514     base::Time date = test_date - base::TimeDelta::FromDays(i);
    515 
    516     test_folders.push_back(
    517         new TestFolder(base::StringPrintf("folder-%05d", i),
    518                        date,
    519                        base::StringPrintf("uid%05d", i), i % 5, i % 3));
    520   }
    521 
    522   SetupFolders(&test_folders, std::vector<AlbumInfo>(), AlbumImagesMap());
    523   VerifyFolderDirectoryList(test_folders);
    524 }
    525 
    526 TEST_F(PicasaFileUtilTest, AlbumExistence) {
    527   ScopedVector<TestFolder> test_folders;
    528   base::Time test_date = base::Time::FromLocalExploded(test_date_exploded);
    529 
    530   std::vector<AlbumInfo> albums;
    531   AlbumInfo info;
    532   info.name = "albumname";
    533   info.uid = "albumuid";
    534   info.timestamp = test_date;
    535   albums.push_back(info);
    536 
    537   AlbumImagesMap albums_images;
    538   albums_images[info.uid] = AlbumImages();
    539 
    540   SetupFolders(&test_folders, albums, albums_images);
    541 
    542   TestEmptyDirectory(std::string(kPicasaDirAlbums) + "/albumname 2013-04-16");
    543   TestNonexistentDirectory(std::string(kPicasaDirAlbums) +
    544                            "/albumname 2013-04-16/toodeep");
    545   TestNonexistentDirectory(std::string(kPicasaDirAlbums) + "/wrongname");
    546 }
    547 
    548 TEST_F(PicasaFileUtilTest, AlbumContents) {
    549   ScopedVector<TestFolder> test_folders;
    550   base::Time test_date = base::Time::FromLocalExploded(test_date_exploded);
    551 
    552   std::vector<AlbumInfo> albums;
    553   AlbumInfo info;
    554   info.name = "albumname";
    555   info.uid = "albumuid";
    556   info.timestamp = test_date;
    557   albums.push_back(info);
    558 
    559   base::ScopedTempDir temp_dir;
    560   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
    561 
    562   base::FilePath image_path = temp_dir.path().AppendASCII("img.jpg");
    563   ASSERT_TRUE(WriteJPEGHeader(image_path));
    564 
    565   AlbumImagesMap albums_images;
    566   albums_images[info.uid] = AlbumImages();
    567   albums_images[info.uid]["mapped_name.jpg"] = image_path;
    568 
    569   SetupFolders(&test_folders, albums, albums_images);
    570 
    571   FileSystemOperation::FileEntryList contents;
    572   FileSystemURL url =
    573       CreateURL(std::string(kPicasaDirAlbums) + "/albumname 2013-04-16");
    574   bool completed = false;
    575   ReadDirectoryTestHelper(operation_runner(), url, &contents, &completed);
    576 
    577   ASSERT_TRUE(completed);
    578   EXPECT_EQ(1u, contents.size());
    579   EXPECT_EQ("mapped_name.jpg",
    580             base::FilePath(contents.begin()->name).AsUTF8Unsafe());
    581   EXPECT_FALSE(contents.begin()->is_directory);
    582 
    583   // Create a snapshot file to verify the file path.
    584   base::RunLoop loop;
    585   base::File::Error error;
    586   base::FilePath platform_path_result;
    587   storage::FileSystemOperationRunner::SnapshotFileCallback snapshot_callback =
    588       base::Bind(&CreateSnapshotFileTestHelperCallback,
    589                  &loop,
    590                  &error,
    591                  &platform_path_result);
    592   operation_runner()->CreateSnapshotFile(
    593       CreateURL(std::string(kPicasaDirAlbums) +
    594                 "/albumname 2013-04-16/mapped_name.jpg"),
    595       snapshot_callback);
    596   loop.Run();
    597   EXPECT_EQ(base::File::FILE_OK, error);
    598   EXPECT_EQ(image_path, platform_path_result);
    599 }
    600 
    601 }  // namespace picasa
    602