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/file_util.h"
     10 #include "base/files/file_path.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/strings/stringprintf.h"
     18 #include "chrome/browser/media_galleries/fileapi/itunes_data_provider.h"
     19 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
     20 #include "chrome/browser/media_galleries/imported_media_gallery_registry.h"
     21 #include "chrome/test/base/in_process_browser_test.h"
     22 #include "content/public/browser/browser_thread.h"
     23 #include "url/gurl.h"
     24 
     25 using chrome::MediaFileSystemBackend;
     26 
     27 namespace itunes {
     28 
     29 namespace {
     30 
     31 struct LibraryEntry {
     32   LibraryEntry(const std::string& artist, const std::string& album,
     33                const base::FilePath& location)
     34       : artist(artist),
     35         album(album),
     36         location(location) {
     37   }
     38   std::string artist;
     39   std::string album;
     40   base::FilePath location;
     41 };
     42 
     43 }  // namespace
     44 
     45 class TestITunesDataProvider : public ITunesDataProvider {
     46  public:
     47   TestITunesDataProvider(const base::FilePath& xml_library_path,
     48                          const base::Closure& callback)
     49       : ITunesDataProvider(xml_library_path),
     50         callback_(callback) {
     51   }
     52   virtual ~TestITunesDataProvider() {}
     53 
     54  private:
     55   virtual void OnLibraryChanged(const base::FilePath& path,
     56                                 bool error) OVERRIDE {
     57     ITunesDataProvider::OnLibraryChanged(path, error);
     58     callback_.Run();
     59   }
     60 
     61   base::Closure callback_;
     62 
     63   DISALLOW_COPY_AND_ASSIGN(TestITunesDataProvider);
     64 };
     65 
     66 class ITunesDataProviderTest : public InProcessBrowserTest {
     67  public:
     68   ITunesDataProviderTest() {}
     69   virtual ~ITunesDataProviderTest() {}
     70 
     71  protected:
     72   virtual void SetUp() OVERRIDE {
     73     ASSERT_TRUE(library_dir_.CreateUniqueTempDir());
     74     WriteLibraryInternal(SetUpLibrary());
     75     // The ImportedMediaGalleryRegistry is created on which ever thread calls
     76     // GetInstance() first.  It shouldn't matter what thread creates, however
     77     // in practice it is always created on the UI thread, so this calls
     78     // GetInstance here to mirror those real conditions.
     79     chrome::ImportedMediaGalleryRegistry::GetInstance();
     80     InProcessBrowserTest::SetUp();
     81   }
     82 
     83   void RunTest() {
     84     DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     85     base::RunLoop loop;
     86     quit_closure_ = loop.QuitClosure();
     87     MediaFileSystemBackend::MediaTaskRunner()->PostTask(
     88         FROM_HERE,
     89         base::Bind(&ITunesDataProviderTest::StartTestOnMediaTaskRunner,
     90                    base::Unretained(this)));
     91     loop.Run();
     92   }
     93 
     94   void WriteLibrary(const std::vector<LibraryEntry>& entries,
     95                     const base::Closure& callback) {
     96     SetLibraryChangeCallback(callback);
     97     WriteLibraryInternal(entries);
     98   }
     99 
    100   void SetLibraryChangeCallback(const base::Closure& callback) {
    101     EXPECT_TRUE(library_changed_callback_.is_null());
    102     library_changed_callback_ = callback;
    103   }
    104 
    105   ITunesDataProvider* data_provider() const {
    106     return chrome::ImportedMediaGalleryRegistry::ITunesDataProvider();
    107   }
    108 
    109   const base::FilePath& library_dir() const {
    110     return library_dir_.path();
    111   }
    112 
    113   base::FilePath XmlFile() const {
    114     return library_dir_.path().AppendASCII("library.xml");
    115   }
    116 
    117   void ExpectTrackLocation(const std::string& artist, const std::string& album,
    118                            const std::string& track_name) {
    119     base::FilePath track =
    120         library_dir().AppendASCII(track_name).NormalizePathSeparators();
    121     EXPECT_EQ(track.value(),
    122               data_provider()->GetTrackLocation(
    123                   artist, album, track_name).NormalizePathSeparators().value());
    124   }
    125 
    126   void ExpectNoTrack(const std::string& artist, const std::string& album,
    127                      const std::string& track_name) {
    128     EXPECT_TRUE(data_provider()->GetTrackLocation(
    129           artist, album, track_name).empty()) << track_name;
    130   }
    131 
    132 
    133   // Get the initial set of library entries, called by SetUp.  If no entries
    134   // are returned the xml file is not created.
    135   virtual std::vector<LibraryEntry> SetUpLibrary() {
    136     return std::vector<LibraryEntry>();
    137   }
    138 
    139   // Start the test. The data provider is refreshed before calling StartTest
    140   // and the result of the refresh is passed in.
    141   virtual void StartTest(bool parse_success) = 0;
    142 
    143   void TestDone() {
    144     DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
    145     chrome::ImportedMediaGalleryRegistry* imported_registry =
    146         chrome::ImportedMediaGalleryRegistry::GetInstance();
    147     imported_registry->itunes_data_provider_.reset();
    148     content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
    149                                      quit_closure_);
    150   }
    151 
    152  private:
    153   void StartTestOnMediaTaskRunner() {
    154     DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
    155     chrome::ImportedMediaGalleryRegistry* imported_registry =
    156         chrome::ImportedMediaGalleryRegistry::GetInstance();
    157     imported_registry->itunes_data_provider_.reset(
    158         new TestITunesDataProvider(
    159             XmlFile(),
    160             base::Bind(&ITunesDataProviderTest::OnLibraryChanged,
    161                        base::Unretained(this))));
    162     data_provider()->RefreshData(base::Bind(&ITunesDataProviderTest::StartTest,
    163                                             base::Unretained(this)));
    164   };
    165 
    166   void OnLibraryChanged() {
    167     DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
    168     if (!library_changed_callback_.is_null()) {
    169       library_changed_callback_.Run();
    170       library_changed_callback_.Reset();
    171     }
    172   }
    173 
    174   void WriteLibraryInternal(const std::vector<LibraryEntry>& entries) {
    175     if (!entries.size())
    176       return;
    177     std::string xml = "<plist><dict><key>Tracks</key><dict>\n";
    178     for (size_t i = 0; i < entries.size(); ++i) {
    179       std::string seperator;
    180 #if defined(OS_WIN)
    181       seperator = "/";
    182 #endif
    183       GURL location("file://localhost" + seperator +
    184                     entries[i].location.AsUTF8Unsafe());
    185       std::string entry_string = base::StringPrintf(
    186           "<key>%" PRIuS "</key><dict>\n"
    187           "  <key>Track ID</key><integer>%" PRIuS "</integer>\n"
    188           "  <key>Location</key><string>%s</string>\n"
    189           "  <key>Artist</key><string>%s</string>\n"
    190           "  <key>Album</key><string>%s</string>\n"
    191           "</dict>\n",
    192           i + 1, i + 1, location.spec().c_str(), entries[i].artist.c_str(),
    193           entries[i].album.c_str());
    194       xml += entry_string;
    195     }
    196     xml += "</dict></dict></plist>\n";
    197     ASSERT_EQ(static_cast<int>(xml.size()),
    198               file_util::WriteFile(XmlFile(), xml.c_str(), xml.size()));
    199   }
    200 
    201   base::ScopedTempDir library_dir_;
    202 
    203   base::Closure library_changed_callback_;
    204 
    205   base::Closure quit_closure_;
    206 
    207   DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderTest);
    208 };
    209 
    210 class ITunesDataProviderBasicTest : public ITunesDataProviderTest {
    211  public:
    212   ITunesDataProviderBasicTest() {}
    213   virtual ~ITunesDataProviderBasicTest() {}
    214 
    215   virtual std::vector<LibraryEntry> SetUpLibrary() OVERRIDE {
    216     base::FilePath track = library_dir().AppendASCII("Track.mp3");
    217     std::vector<LibraryEntry> entries;
    218     entries.push_back(LibraryEntry("Artist", "Album", track));
    219     return entries;
    220   }
    221 
    222   virtual void StartTest(bool parse_success) OVERRIDE {
    223     EXPECT_TRUE(parse_success);
    224 
    225     // KnownArtist
    226     EXPECT_TRUE(data_provider()->KnownArtist("Artist"));
    227     EXPECT_FALSE(data_provider()->KnownArtist("Artist2"));
    228 
    229     // KnownAlbum
    230     EXPECT_TRUE(data_provider()->KnownAlbum("Artist", "Album"));
    231     EXPECT_FALSE(data_provider()->KnownAlbum("Artist", "Album2"));
    232     EXPECT_FALSE(data_provider()->KnownAlbum("Artist2", "Album"));
    233 
    234     // GetTrackLocation
    235     ExpectTrackLocation("Artist", "Album", "Track.mp3");
    236     ExpectNoTrack("Artist", "Album", "Track2.mp3");
    237     ExpectNoTrack("Artist", "Album2", "Track.mp3");
    238     ExpectNoTrack("Artist2", "Album", "Track.mp3");
    239 
    240     // GetArtistNames
    241     std::set<ITunesDataProvider::ArtistName> artists =
    242       data_provider()->GetArtistNames();
    243     ASSERT_EQ(1U, artists.size());
    244     EXPECT_EQ("Artist", *artists.begin());
    245 
    246     // GetAlbumNames
    247     std::set<ITunesDataProvider::AlbumName> albums =
    248         data_provider()->GetAlbumNames("Artist");
    249     ASSERT_EQ(1U, albums.size());
    250     EXPECT_EQ("Album", *albums.begin());
    251 
    252     albums = data_provider()->GetAlbumNames("Artist2");
    253     EXPECT_EQ(0U, albums.size());
    254 
    255     // GetAlbum
    256     base::FilePath track =
    257         library_dir().AppendASCII("Track.mp3").NormalizePathSeparators();
    258     ITunesDataProvider::Album album =
    259         data_provider()->GetAlbum("Artist", "Album");
    260     ASSERT_EQ(1U, album.size());
    261     EXPECT_EQ(track.BaseName().AsUTF8Unsafe(), album.begin()->first);
    262     EXPECT_EQ(track.value(),
    263               album.begin()->second.NormalizePathSeparators().value());
    264 
    265     album = data_provider()->GetAlbum("Artist", "Album2");
    266     EXPECT_EQ(0U, album.size());
    267 
    268     album = data_provider()->GetAlbum("Artist2", "Album");
    269     EXPECT_EQ(0U, album.size());
    270 
    271     TestDone();
    272   }
    273 
    274  private:
    275   DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderBasicTest);
    276 };
    277 
    278 class ITunesDataProviderRefreshTest : public ITunesDataProviderTest {
    279  public:
    280   ITunesDataProviderRefreshTest() {}
    281   virtual ~ITunesDataProviderRefreshTest() {}
    282 
    283   virtual std::vector<LibraryEntry> SetUpLibrary() OVERRIDE {
    284     base::FilePath track = library_dir().AppendASCII("Track.mp3");
    285     std::vector<LibraryEntry> entries;
    286     entries.push_back(LibraryEntry("Artist", "Album", track));
    287     return entries;
    288   }
    289 
    290   virtual void StartTest(bool parse_success) OVERRIDE {
    291     EXPECT_TRUE(parse_success);
    292 
    293     // Initial contents.
    294     ExpectTrackLocation("Artist", "Album", "Track.mp3");
    295     ExpectNoTrack("Artist2", "Album2", "Track2.mp3");
    296 
    297     // New file.
    298     base::FilePath track2 = library_dir().AppendASCII("Track2.mp3");
    299     std::vector<LibraryEntry> entries;
    300     entries.push_back(LibraryEntry("Artist2", "Album2", track2));
    301     WriteLibrary(entries,
    302                  base::Bind(&ITunesDataProviderRefreshTest::CheckAfterWrite,
    303                             base::Unretained(this)));
    304   }
    305 
    306   void CheckAfterWrite() {
    307     // Content the same.
    308     ExpectTrackLocation("Artist", "Album", "Track.mp3");
    309     ExpectNoTrack("Artist2", "Album2", "Track2.mp3");
    310 
    311     data_provider()->RefreshData(
    312         base::Bind(&ITunesDataProviderRefreshTest::CheckRefresh,
    313                    base::Unretained(this)));
    314   }
    315 
    316   void CheckRefresh(bool is_valid) {
    317     EXPECT_TRUE(is_valid);
    318 
    319     ExpectTrackLocation("Artist2", "Album2", "Track2.mp3");
    320     ExpectNoTrack("Artist", "Album", "Track.mp3");
    321     TestDone();
    322   }
    323 
    324  private:
    325   DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderRefreshTest);
    326 };
    327 
    328 class ITunesDataProviderInvalidTest : public ITunesDataProviderTest {
    329  public:
    330   ITunesDataProviderInvalidTest() {}
    331   virtual ~ITunesDataProviderInvalidTest() {}
    332 
    333   virtual std::vector<LibraryEntry> SetUpLibrary() OVERRIDE {
    334     base::FilePath track = library_dir().AppendASCII("Track.mp3");
    335     std::vector<LibraryEntry> entries;
    336     entries.push_back(LibraryEntry("Artist", "Album", track));
    337     return entries;
    338   }
    339 
    340   virtual void StartTest(bool parse_success) OVERRIDE {
    341     EXPECT_TRUE(parse_success);
    342 
    343     SetLibraryChangeCallback(
    344         base::Bind(&ITunesDataProvider::RefreshData,
    345                    base::Unretained(data_provider()),
    346                    base::Bind(&ITunesDataProviderInvalidTest::CheckInvalid,
    347                               base::Unretained(this))));
    348     ASSERT_EQ(1L, file_util::WriteFile(XmlFile(), " ", 1));
    349   }
    350 
    351   void CheckInvalid(bool is_valid) {
    352     EXPECT_FALSE(is_valid);
    353     TestDone();
    354   }
    355 
    356  private:
    357   DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderInvalidTest);
    358 };
    359 
    360 class ITunesDataProviderUniqueNameTest : public ITunesDataProviderTest {
    361  public:
    362   ITunesDataProviderUniqueNameTest() {}
    363   virtual ~ITunesDataProviderUniqueNameTest() {}
    364 
    365   virtual std::vector<LibraryEntry> SetUpLibrary() OVERRIDE {
    366     base::FilePath track = library_dir().AppendASCII("Track.mp3");
    367     std::vector<LibraryEntry> entries;
    368     // Dupe album names should get uniquified with the track id, which in the
    369     // test framework is the vector index.
    370     entries.push_back(LibraryEntry("Artist", "Album", track));
    371     entries.push_back(LibraryEntry("Artist", "Album", track));
    372     entries.push_back(LibraryEntry("Artist", "Album2", track));
    373     return entries;
    374   }
    375 
    376   virtual void StartTest(bool parse_success) OVERRIDE {
    377     EXPECT_TRUE(parse_success);
    378 
    379     base::FilePath track =
    380         library_dir().AppendASCII("Track.mp3").NormalizePathSeparators();
    381     EXPECT_EQ(track.value(),
    382               data_provider()->GetTrackLocation(
    383                   "Artist", "Album",
    384                   "Track (1).mp3").NormalizePathSeparators().value());
    385     EXPECT_EQ(track.value(),
    386               data_provider()->GetTrackLocation(
    387                   "Artist", "Album",
    388                   "Track (2).mp3").NormalizePathSeparators().value());
    389     EXPECT_EQ(track.value(),
    390               data_provider()->GetTrackLocation(
    391                   "Artist", "Album2",
    392                   "Track.mp3").NormalizePathSeparators().value());
    393 
    394     TestDone();
    395   }
    396 
    397  private:
    398   DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderUniqueNameTest);
    399 };
    400 
    401 class ITunesDataProviderEscapeTest : public ITunesDataProviderTest {
    402  // Albums and tracks that aren't the same, but become the same after
    403  // replacing bad characters are not handled properly, but that case should
    404  // never happen in practice.
    405  public:
    406   ITunesDataProviderEscapeTest() {}
    407   virtual ~ITunesDataProviderEscapeTest() {}
    408 
    409   virtual std::vector<LibraryEntry> SetUpLibrary() OVERRIDE {
    410     base::FilePath track = library_dir().AppendASCII("Track:1.mp3");
    411     std::vector<LibraryEntry> entries;
    412     entries.push_back(LibraryEntry("Artist:/name", "Album:name/", track));
    413     entries.push_back(LibraryEntry("Artist/name", "Album:name", track));
    414     entries.push_back(LibraryEntry("Artist/name", "Album:name", track));
    415     return entries;
    416   }
    417 
    418   virtual void StartTest(bool parse_success) OVERRIDE {
    419     EXPECT_TRUE(parse_success);
    420 
    421     base::FilePath track =
    422         library_dir().AppendASCII("Track:1.mp3").NormalizePathSeparators();
    423     EXPECT_EQ(track.value(),
    424               data_provider()->GetTrackLocation(
    425                   "Artist__name", "Album_name_",
    426                   "Track_1.mp3").NormalizePathSeparators().value());
    427     EXPECT_EQ(track.value(),
    428               data_provider()->GetTrackLocation(
    429                   "Artist_name", "Album_name",
    430                   "Track_1 (2).mp3").NormalizePathSeparators().value());
    431     EXPECT_EQ(track.value(),
    432               data_provider()->GetTrackLocation(
    433                   "Artist_name", "Album_name",
    434                   "Track_1 (3).mp3").NormalizePathSeparators().value());
    435 
    436     TestDone();
    437   }
    438 
    439  private:
    440   DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderEscapeTest);
    441 };
    442 
    443 IN_PROC_BROWSER_TEST_F(ITunesDataProviderBasicTest, BasicTest) {
    444   RunTest();
    445 }
    446 
    447 IN_PROC_BROWSER_TEST_F(ITunesDataProviderRefreshTest, RefreshTest) {
    448   RunTest();
    449 }
    450 
    451 IN_PROC_BROWSER_TEST_F(ITunesDataProviderInvalidTest, InvalidTest) {
    452   RunTest();
    453 }
    454 
    455 IN_PROC_BROWSER_TEST_F(ITunesDataProviderUniqueNameTest, UniqueNameTest) {
    456   RunTest();
    457 }
    458 
    459 IN_PROC_BROWSER_TEST_F(ITunesDataProviderEscapeTest, EscapeTest) {
    460   RunTest();
    461 }
    462 
    463 }  // namespace itunes
    464