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