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