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 <string>
      6 #include <vector>
      7 
      8 #include "base/bind.h"
      9 #include "base/files/file_path.h"
     10 #include "base/files/file_util.h"
     11 #include "base/files/scoped_temp_dir.h"
     12 #include "base/format_macros.h"
     13 #include "base/logging.h"
     14 #include "base/memory/scoped_ptr.h"
     15 #include "base/message_loop/message_loop.h"
     16 #include "base/run_loop.h"
     17 #include "base/stl_util.h"
     18 #include "base/strings/stringprintf.h"
     19 #include "chrome/browser/media_galleries/fileapi/iphoto_data_provider.h"
     20 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
     21 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
     22 #include "chrome/test/base/in_process_browser_test.h"
     23 #include "content/public/browser/browser_thread.h"
     24 #include "url/gurl.h"
     25 
     26 using base::FilePath;
     27 
     28 namespace iphoto {
     29 
     30 class TestIPhotoDataProvider : public IPhotoDataProvider {
     31  public:
     32   TestIPhotoDataProvider(const base::FilePath& xml_library_path,
     33                          const base::Closure& callback)
     34       : IPhotoDataProvider(xml_library_path),
     35         callback_(callback) {
     36   }
     37   virtual ~TestIPhotoDataProvider() {}
     38 
     39  private:
     40   virtual void OnLibraryChanged(const base::FilePath& path,
     41                                 bool error) OVERRIDE {
     42     IPhotoDataProvider::OnLibraryChanged(path, error);
     43     callback_.Run();
     44   }
     45 
     46   base::Closure callback_;
     47 
     48   DISALLOW_COPY_AND_ASSIGN(TestIPhotoDataProvider);
     49 };
     50 
     51 class IPhotoDataProviderTest : public InProcessBrowserTest {
     52  public:
     53   IPhotoDataProviderTest() {}
     54   virtual ~IPhotoDataProviderTest() {}
     55 
     56  protected:
     57   virtual void SetUp() OVERRIDE {
     58     ASSERT_TRUE(library_dir_.CreateUniqueTempDir());
     59     WriteLibraryInternal();
     60     // The ImportedMediaGalleryRegistry is created on which ever thread calls
     61     // GetInstance() first.  It shouldn't matter what thread creates, however
     62     // in practice it is always created on the UI thread, so this calls
     63     // GetInstance here to mirror those real conditions.
     64     ImportedMediaGalleryRegistry::GetInstance();
     65     InProcessBrowserTest::SetUp();
     66   }
     67 
     68   void RunTest() {
     69     DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     70     base::RunLoop loop;
     71     quit_closure_ = loop.QuitClosure();
     72     MediaFileSystemBackend::MediaTaskRunner()->PostTask(
     73         FROM_HERE,
     74         base::Bind(&IPhotoDataProviderTest::StartTestOnMediaTaskRunner,
     75                    base::Unretained(this)));
     76     loop.Run();
     77   }
     78 
     79   void WriteLibrary(const base::Closure& callback) {
     80     SetLibraryChangeCallback(callback);
     81     WriteLibraryInternal();
     82   }
     83 
     84   void SetLibraryChangeCallback(const base::Closure& callback) {
     85     EXPECT_TRUE(library_changed_callback_.is_null());
     86     library_changed_callback_ = callback;
     87   }
     88 
     89   IPhotoDataProvider* data_provider() const {
     90     return ImportedMediaGalleryRegistry::IPhotoDataProvider();
     91   }
     92 
     93   const base::FilePath& library_dir() const {
     94     return library_dir_.path();
     95   }
     96 
     97   base::FilePath XmlFile() const {
     98     return library_dir_.path().AppendASCII("library.xml");
     99   }
    100 
    101   // Start the test. The data provider is refreshed before calling StartTest
    102   // and the result of the refresh is passed in.
    103   virtual void StartTest(bool parse_success) = 0;
    104 
    105   void TestDone() {
    106     DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
    107     ImportedMediaGalleryRegistry* imported_registry =
    108         ImportedMediaGalleryRegistry::GetInstance();
    109     imported_registry->iphoto_data_provider_.reset();
    110     content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
    111                                      quit_closure_);
    112   }
    113 
    114   // Override to provide a full library string.
    115   virtual std::string GetLibraryString() {
    116     return "<plist><dict>\n</dict></plist>\n";
    117   }
    118 
    119  private:
    120   void StartTestOnMediaTaskRunner() {
    121     DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
    122     ImportedMediaGalleryRegistry* imported_registry =
    123         ImportedMediaGalleryRegistry::GetInstance();
    124     imported_registry->iphoto_data_provider_.reset(
    125         new TestIPhotoDataProvider(
    126             XmlFile(),
    127             base::Bind(&IPhotoDataProviderTest::OnLibraryChanged,
    128                        base::Unretained(this))));
    129     data_provider()->RefreshData(base::Bind(&IPhotoDataProviderTest::StartTest,
    130                                             base::Unretained(this)));
    131   };
    132 
    133   void OnLibraryChanged() {
    134     DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
    135     if (!library_changed_callback_.is_null()) {
    136       library_changed_callback_.Run();
    137       library_changed_callback_.Reset();
    138     }
    139   }
    140 
    141   void WriteLibraryInternal() {
    142     std::string xml = GetLibraryString();
    143     ASSERT_EQ(static_cast<int>(xml.size()),
    144               base::WriteFile(XmlFile(), xml.c_str(), xml.size()));
    145   }
    146 
    147   base::ScopedTempDir library_dir_;
    148 
    149   base::Closure library_changed_callback_;
    150 
    151   base::Closure quit_closure_;
    152 
    153   DISALLOW_COPY_AND_ASSIGN(IPhotoDataProviderTest);
    154 };
    155 
    156 class IPhotoDataProviderBasicTest : public IPhotoDataProviderTest {
    157  public:
    158   IPhotoDataProviderBasicTest() {}
    159   virtual ~IPhotoDataProviderBasicTest() {}
    160 
    161   virtual std::string GetLibraryString() OVERRIDE {
    162     return "<plist><dict>\n"
    163       "<key>List of Albums</key>\n"
    164       "<array>"
    165       "    <dict>\n"
    166       "      <key>AlbumId</key>"
    167       "      <integer>14</integer>"
    168       "      <key>AlbumName</key>"
    169       "      <string>Album1</string>"
    170       "      <key>KeyList</key>"
    171       "      <array>"
    172       "      <string>1</string>"
    173       "      <string>3</string>"  // [3] and [4] are name dupes
    174       "      <string>4</string>"
    175       "      </array>"
    176       "    </dict>\n"
    177       "    <dict>\n"
    178       "      <key>AlbumId</key>"
    179       "      <integer>15</integer>"
    180       "      <key>AlbumName</key>"
    181       "      <string>Album2</string>"
    182       "      <key>KeyList</key>"
    183       "      <array>"
    184       "      <string>2</string>"
    185       "      </array>"
    186       "    </dict>\n"
    187       "    <dict>\n"
    188       "      <key>AlbumId</key>"
    189       "      <integer>16</integer>"
    190       "      <key>AlbumName</key>"
    191       "      <string>Album5</string>"
    192       "      <key>KeyList</key>"
    193       "      <array>"
    194       "      <string>5</string>"  // A name dupe of [2], but in another album.
    195       "      </array>"
    196       "    </dict>\n"
    197       "</array>\n"
    198       "<key>Master Image List</key>\n"
    199       "<dict>\n"
    200       "  <key>1</key>\n"
    201       "  <dict>\n"
    202       "    <key>MediaType</key>"
    203       "    <string>Image</string>"
    204       "    <key>Caption</key>"
    205       "    <string>caption</string>"
    206       "    <key>GUID</key>\n"
    207       "    <string>guid1</string>"
    208       "    <key>ImagePath</key>"
    209       "    <string>/vol/path1.jpg</string>"
    210       "    <key>ThumbPath</key>"
    211       "    <string>/vol/thumb1.jpg</string>"
    212       "  </dict>\n"
    213       "  <key>2</key>\n"
    214       "  <dict>\n"
    215       "    <key>MediaType</key>"
    216       "    <string>Image</string>"
    217       "    <key>Caption</key>"
    218       "    <string>caption2</string>"
    219       "    <key>GUID</key>\n"
    220       "    <string>guid2</string>"
    221       "    <key>ImagePath</key>"
    222       "    <string>/vol/path2.jpg</string>"
    223       "    <key>ThumbPath</key>"
    224       "    <string>/vol/thumb2.jpg</string>"
    225       "  </dict>\n"
    226       "  <key>3</key>\n"
    227       "  <dict>\n"
    228       "    <key>MediaType</key>"
    229       "    <string>Image</string>"
    230       "    <key>Caption</key>"
    231       "    <string>caption3</string>"
    232       "    <key>GUID</key>\n"
    233       "    <string>guid3</string>"
    234       "    <key>ImagePath</key>"
    235       "    <string>/vol/path3.jpg</string>"
    236       "    <key>ThumbPath</key>"
    237       "    <string>/vol/thumb3.jpg</string>"
    238       "  </dict>\n"
    239       "  <key>4</key>\n"  // A name duplicate of [3] in another path.
    240       "  <dict>\n"
    241       "    <key>MediaType</key>"
    242       "    <string>Image</string>"
    243       "    <key>Caption</key>"
    244       "    <string>caption</string>"
    245       "    <key>GUID</key>\n"
    246       "    <string>guid3</string>"
    247       "    <key>ImagePath</key>"
    248       "    <string>/vol/dupe/path3.jpg</string>"
    249       "    <key>ThumbPath</key>"
    250       "    <string>/vol/dupe/thumb3.jpg</string>"
    251       "  </dict>\n"
    252       "  <key>5</key>\n"  // A name duplicate of [2] in another path.
    253       "  <dict>\n"
    254       "    <key>MediaType</key>"
    255       "    <string>Image</string>"
    256       "    <key>Caption</key>"
    257       "    <string>caption5</string>"
    258       "    <key>GUID</key>\n"
    259       "    <string>guid2</string>"
    260       "    <key>ImagePath</key>"
    261       "    <string>/vol/dupe/path2.jpg</string>"
    262       "    <key>ThumbPath</key>"
    263       "    <string>/vol/dupe/thumb2.jpg</string>"
    264       "    <key>OriginalPath</key>"             \
    265       "    <string>/original/vol/another2.jpg</string>"  \
    266       "  </dict>\n"
    267       "</dict>\n"
    268       "</dict></plist>\n";
    269   }
    270 
    271   virtual void StartTest(bool parse_success) OVERRIDE {
    272     EXPECT_TRUE(parse_success);
    273 
    274     std::vector<std::string> names = data_provider()->GetAlbumNames();
    275     EXPECT_EQ(3U, names.size());
    276     EXPECT_EQ("Album1", names[0]);
    277 
    278     EXPECT_EQ(FilePath("/vol/path1.jpg").value(),
    279               data_provider()->GetPhotoLocationInAlbum(
    280                   "Album1", "path1.jpg").value());
    281     EXPECT_EQ(FilePath("/vol/path3.jpg").value(),
    282               data_provider()->GetPhotoLocationInAlbum(
    283                   "Album1", "path3.jpg").value());
    284     EXPECT_EQ(FilePath("/vol/dupe/path3.jpg").value(),
    285               data_provider()->GetPhotoLocationInAlbum(
    286                   "Album1", "path3(4).jpg").value());
    287     EXPECT_EQ(FilePath().value(),
    288               data_provider()->GetPhotoLocationInAlbum(
    289                   "Album1", "path5.jpg").value());
    290 
    291     // path2.jpg is name-duped, but in different albums, and so should not
    292     // be mangled.
    293     EXPECT_EQ(FilePath("/vol/dupe/path2.jpg").value(),
    294               data_provider()->GetPhotoLocationInAlbum(
    295                   "Album5", "path2.jpg").value());
    296     EXPECT_EQ(FilePath("/vol/path2.jpg").value(),
    297               data_provider()->GetPhotoLocationInAlbum(
    298                   "Album2", "path2.jpg").value());
    299 
    300     std::map<std::string, base::FilePath> photos =
    301         data_provider()->GetAlbumContents("nonexistent");
    302     EXPECT_EQ(0U, photos.size());
    303     photos = data_provider()->GetAlbumContents("Album1");
    304     EXPECT_EQ(3U, photos.size());
    305     EXPECT_TRUE(ContainsKey(photos, "path1.jpg"));
    306     EXPECT_FALSE(ContainsKey(photos, "path2.jpg"));
    307     EXPECT_TRUE(ContainsKey(photos, "path3.jpg"));
    308     EXPECT_TRUE(ContainsKey(photos, "path3(4).jpg"));
    309     EXPECT_EQ(FilePath("/vol/path1.jpg").value(), photos["path1.jpg"].value());
    310     EXPECT_EQ(FilePath("/vol/path3.jpg").value(),
    311         photos["path3.jpg"].value());
    312     EXPECT_EQ(FilePath("/vol/dupe/path3.jpg").value(),
    313         photos["path3(4).jpg"].value());
    314 
    315     photos = data_provider()->GetAlbumContents("Album2");
    316     EXPECT_EQ(1U, photos.size());
    317     EXPECT_TRUE(ContainsKey(photos, "path2.jpg"));
    318 
    319     EXPECT_FALSE(data_provider()->HasOriginals("Album1"));
    320     EXPECT_TRUE(data_provider()->HasOriginals("Album5"));
    321     std::map<std::string, base::FilePath> originals =
    322         data_provider()->GetOriginals("Album1");
    323     EXPECT_EQ(0U, originals.size());
    324     originals = data_provider()->GetOriginals("Album5");
    325     EXPECT_EQ(1U, originals.size());
    326     EXPECT_TRUE(ContainsKey(originals, "path2.jpg"));
    327     EXPECT_FALSE(ContainsKey(originals, "path1.jpg"));
    328     EXPECT_EQ(FilePath("/original/vol/another2.jpg").value(),
    329               originals["path2.jpg"].value());
    330     base::FilePath original_path =
    331         data_provider()->GetOriginalPhotoLocation("Album5", "path2.jpg");
    332     EXPECT_EQ(FilePath("/original/vol/another2.jpg").value(),
    333               original_path.value());
    334 
    335     TestDone();
    336   }
    337 
    338  private:
    339   DISALLOW_COPY_AND_ASSIGN(IPhotoDataProviderBasicTest);
    340 };
    341 
    342 class IPhotoDataProviderRefreshTest : public IPhotoDataProviderTest {
    343  public:
    344   IPhotoDataProviderRefreshTest() {}
    345   virtual ~IPhotoDataProviderRefreshTest() {}
    346 
    347   std::string another_album;
    348 
    349   virtual std::string GetLibraryString() OVERRIDE {
    350     return "<plist><dict>\n"
    351       "<key>List of Albums</key>\n"
    352       "<array>"
    353       "    <dict>"
    354       "      <key>AlbumId</key>"
    355       "      <integer>14</integer>"
    356       "      <key>AlbumName</key>"
    357       "      <string>Album1</string>"
    358       "      <key>KeyList</key>"
    359       "      <array>"
    360       "      <string>1</string>"
    361       "      </array>"
    362       "    </dict>\n" +
    363       another_album +
    364       "</array>\n"
    365       "<key>Master Image List</key>\n"
    366       "<dict>\n"
    367       "  <key>1</key>\n"
    368       "  <dict>\n"
    369       "    <key>MediaType</key>"
    370       "    <string>Image</string>"
    371       "    <key>Caption1</key>"
    372       "    <string>caption</string>"
    373       "    <key>GUID</key>\n"
    374       "    <string>guid1</string>"
    375       "    <key>ImagePath</key>"
    376       "    <string>/vol/path1.jpg</string>"
    377       "    <key>ThumbPath</key>"
    378       "    <string>/vol/thumb1.jpg</string>"
    379       "  </dict>\n"
    380       "</dict>\n"
    381       "</dict></plist>\n";
    382   }
    383 
    384   virtual void StartTest(bool parse_success) OVERRIDE {
    385     EXPECT_TRUE(parse_success);
    386 
    387     EXPECT_EQ(FilePath("/vol/path1.jpg"),
    388               data_provider()->GetPhotoLocationInAlbum("Album1", "path1.jpg"));
    389     std::vector<std::string> names = data_provider()->GetAlbumNames();
    390     EXPECT_EQ(1U, names.size());
    391     EXPECT_EQ("Album1", names[0]);
    392 
    393     another_album =
    394       "    <dict>"
    395       "      <key>AlbumId</key>"
    396       "      <integer>14</integer>"
    397       "      <key>AlbumName</key>"
    398       "      <string>Another Album</string>"
    399       "      <key>KeyList</key>"
    400       "      <array>"
    401       "      <string>1</string>"
    402       "      </array>"
    403       "    </dict>\n";
    404 
    405     WriteLibrary(base::Bind(&IPhotoDataProviderRefreshTest::CheckAfterWrite,
    406                             base::Unretained(this)));
    407   }
    408 
    409   void CheckAfterWrite() {
    410     // No change -- data has not been parsed.
    411     EXPECT_EQ(FilePath("/vol/path1.jpg"),
    412               data_provider()->GetPhotoLocationInAlbum("Album1", "path1.jpg"));
    413     std::vector<std::string> names = data_provider()->GetAlbumNames();
    414     EXPECT_EQ(1U, names.size());
    415     EXPECT_EQ("Album1", names[0]);
    416 
    417     data_provider()->RefreshData(
    418         base::Bind(&IPhotoDataProviderRefreshTest::CheckRefresh,
    419                    base::Unretained(this)));
    420   }
    421 
    422   void CheckRefresh(bool is_valid) {
    423     EXPECT_TRUE(is_valid);
    424 
    425     EXPECT_EQ(FilePath("/vol/path1.jpg"),
    426               data_provider()->GetPhotoLocationInAlbum("Album1", "path1.jpg"));
    427     std::vector<std::string> names = data_provider()->GetAlbumNames();
    428     EXPECT_EQ(2U, names.size());
    429     if (names.size() == 2U) {
    430       EXPECT_EQ("Album1", names[0]);
    431       EXPECT_EQ("Another Album", names[1]);
    432     }
    433 
    434     TestDone();
    435   }
    436 
    437  private:
    438   DISALLOW_COPY_AND_ASSIGN(IPhotoDataProviderRefreshTest);
    439 };
    440 
    441 class IPhotoDataProviderInvalidTest : public IPhotoDataProviderTest {
    442  public:
    443   IPhotoDataProviderInvalidTest() {}
    444   virtual ~IPhotoDataProviderInvalidTest() {}
    445 
    446   virtual void StartTest(bool parse_success) OVERRIDE {
    447     EXPECT_TRUE(parse_success);
    448 
    449     SetLibraryChangeCallback(
    450         base::Bind(&IPhotoDataProvider::RefreshData,
    451                    base::Unretained(data_provider()),
    452                    base::Bind(&IPhotoDataProviderInvalidTest::CheckInvalid,
    453                               base::Unretained(this))));
    454     EXPECT_EQ(1L, base::WriteFile(XmlFile(), " ", 1));
    455   }
    456 
    457   void CheckInvalid(bool is_valid) {
    458     EXPECT_FALSE(is_valid);
    459     TestDone();
    460   }
    461 
    462  private:
    463   DISALLOW_COPY_AND_ASSIGN(IPhotoDataProviderInvalidTest);
    464 };
    465 
    466 IN_PROC_BROWSER_TEST_F(IPhotoDataProviderBasicTest, BasicTest) {
    467   RunTest();
    468 }
    469 
    470 IN_PROC_BROWSER_TEST_F(IPhotoDataProviderRefreshTest, RefreshTest) {
    471   RunTest();
    472 }
    473 
    474 IN_PROC_BROWSER_TEST_F(IPhotoDataProviderInvalidTest, InvalidTest) {
    475   RunTest();
    476 }
    477 
    478 }  // namespace iphoto
    479