// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/logging.h" #include "chrome/common/media_galleries/itunes_library.h" #include "chrome/utility/media_galleries/itunes_library_parser.h" #include "testing/gtest/include/gtest/gtest.h" #define SIMPLE_HEADER() \ "" \ " " \ " Tracks" \ " " #define SIMPLE_TRACK(key, id, path, artist, album) \ "" #key "" \ "" \ " Track ID" #id "" \ " Locationfile://localhost/" path "" \ " Artist" artist "" \ " Album" album "" \ "" #define SIMPLE_FOOTER() \ " " \ " " \ "" namespace itunes { namespace { void CompareTrack(const parser::Track& a, const parser::Track& b) { EXPECT_EQ(a.id, b.id); EXPECT_EQ(a.location.value(), b.location.value()); } void CompareAlbum(const parser::Album& a, const parser::Album& b) { EXPECT_EQ(a.size(), b.size()); parser::Album::const_iterator a_it; parser::Album::const_iterator b_it; for (a_it = a.begin(), b_it = b.begin(); a_it != a.end() && b_it != b.end(); ++a_it, ++b_it) { CompareTrack(*a_it, *b_it); } } void CompareAlbums(const parser::Albums& a, const parser::Albums& b) { EXPECT_EQ(a.size(), b.size()); parser::Albums::const_iterator a_it; parser::Albums::const_iterator b_it; for (a_it = a.begin(), b_it = b.begin(); a_it != a.end() && b_it != b.end(); ++a_it, ++b_it) { EXPECT_EQ(a_it->first, b_it->first); CompareAlbum(a_it->second, b_it->second); } } void CompareLibrary(const parser::Library& a, const parser::Library& b) { EXPECT_EQ(a.size(), b.size()); parser::Library::const_iterator a_it; parser::Library::const_iterator b_it; for (a_it = a.begin(), b_it = b.begin(); a_it != a.end() && b_it != b.end(); ++a_it, ++b_it) { EXPECT_EQ(a_it->first, b_it->first); CompareAlbums(a_it->second, b_it->second); } } class ITunesLibraryParserTest : public testing::Test { public: ITunesLibraryParserTest() {} void TestParser(bool expected_result, const std::string& xml) { ITunesLibraryParser parser; EXPECT_EQ(expected_result, parser.Parse(xml)); if (!expected_result) return; CompareLibrary(expected_library_, parser.library()); } void AddExpectedTrack(uint32 id, const std::string& location, const std::string& artist, const std::string& album) { // On Mac this pretends that C: is a directory. #if defined(OS_MACOSX) std::string os_location = "/" + location; #else const std::string& os_location = location; #endif parser::Track track(id, base::FilePath::FromUTF8Unsafe(os_location)); expected_library_[artist][album].insert(track); } private: parser::Library expected_library_; DISALLOW_COPY_AND_ASSIGN(ITunesLibraryParserTest); }; TEST_F(ITunesLibraryParserTest, EmptyLibrary) { TestParser(false, ""); } TEST_F(ITunesLibraryParserTest, MinimalXML) { AddExpectedTrack(1, "C:/dir/Song With Space.mp3", "Artist A", "Album A"); TestParser( true, SIMPLE_HEADER() SIMPLE_TRACK(1, 1, "C:/dir/Song%20With%20Space.mp3", "Artist A", "Album A") SIMPLE_FOOTER()); } TEST_F(ITunesLibraryParserTest, MultipleSongs) { AddExpectedTrack(1, "C:/dir/SongA1.mp3", "Artist A", "Album A"); AddExpectedTrack(2, "C:/dir/SongA2.mp3", "Artist A", "Album A"); AddExpectedTrack(3, "C:/dir/SongA3.mp3", "Artist A", "Album A"); AddExpectedTrack(4, "C:/dir/SongB1.mp3", "Artist A", "Album B"); AddExpectedTrack(5, "C:/dir/SongB2.mp3", "Artist A", "Album B"); AddExpectedTrack(6, "C:/dir2/SongB1.mp3", "Artist B", "Album B"); AddExpectedTrack(7, "C:/dir2/SongB2.mp3", "Artist B", "Album B"); TestParser( true, SIMPLE_HEADER() SIMPLE_TRACK(1, 1, "C:/dir/SongA1.mp3", "Artist A", "Album A") SIMPLE_TRACK(2, 2, "C:/dir/SongA2.mp3", "Artist A", "Album A") SIMPLE_TRACK(3, 3, "C:/dir/SongA3.mp3", "Artist A", "Album A") SIMPLE_TRACK(4, 4, "C:/dir/SongB1.mp3", "Artist A", "Album B") SIMPLE_TRACK(5, 5, "C:/dir/SongB2.mp3", "Artist A", "Album B") SIMPLE_TRACK(6, 6, "C:/dir2/SongB1.mp3", "Artist B", "Album B") SIMPLE_TRACK(7, 7, "C:/dir2/SongB2.mp3", "Artist B", "Album B") SIMPLE_FOOTER()); } TEST_F(ITunesLibraryParserTest, MismatchedId) { TestParser( false, SIMPLE_HEADER() SIMPLE_TRACK(1, 2, "C:/dir/SongA1.mp3", "Artist A", "Album A") SIMPLE_FOOTER()); AddExpectedTrack(1, "C:/dir/SongA1.mp3", "Artist A", "Album A"); TestParser( true, SIMPLE_HEADER() SIMPLE_TRACK(1, 1, "C:/dir/SongA1.mp3", "Artist A", "Album A") SIMPLE_TRACK(2, 3, "C:/dir/SongA2.mp3", "Artist A", "Album A") SIMPLE_FOOTER()); } TEST_F(ITunesLibraryParserTest, OtherDictionaryEntries) { AddExpectedTrack(1, "C:/dir/SongA1.mp3", "Artist A", "Album A"); TestParser( true, "" " " " Other section" " " // In Other section, not Tracks. SIMPLE_TRACK(10, 10, "C:/dir/SongB2.mp3", "Artist B", "Album B") " " " Tracks" " " " 1" " " // In the body of a track dictionary before the interesting entries. SIMPLE_TRACK(20, 20, "C:/dir/SongB2.mp3", "Artist B", "Album B") // Entries in a different order. " ArtistArtist A" " Location" " file://localhost/C:/dir/SongA1.mp3" " AlbumAlbum A" " Track ID1" // In the body of a track dictionary after the interesting entries. SIMPLE_TRACK(30, 30, "C:/dir/SongB3.mp3", "Artist B", "Album B") " " " 40" " " // Missing album name. " ArtistArtist B" " Location" " file://localhost/C:/dir/SongB4.mp3" " Track ID1" " " SIMPLE_FOOTER()); } TEST_F(ITunesLibraryParserTest, MissingEntry) { AddExpectedTrack(1, "C:/dir/SongA1.mp3", "Artist A", "Album A"); AddExpectedTrack(3, "C:/dir/SongA3.mp3", "Artist A", "Album A"); TestParser( true, SIMPLE_HEADER() SIMPLE_TRACK(1, 1, "C:/dir/SongA1.mp3", "Artist A", "Album A") "2" " Track ID2" " AlbumAlbum A" " FooBar" " " // A whitespace node is important for the test. "" SIMPLE_TRACK(3, 3, "C:/dir/SongA3.mp3", "Artist A", "Album A") SIMPLE_FOOTER()); } TEST_F(ITunesLibraryParserTest, UnknownAlbumOrArtist) { AddExpectedTrack(1, "C:/dir/SongA1.mp3", "Artist A", "Unknown Album"); AddExpectedTrack(2, "C:/dir/SongA2.mp3", "Unknown Artist", "Album A"); AddExpectedTrack(3, "C:/dir/SongA3.mp3", "Unknown Artist", "Unknown Album"); TestParser( true, SIMPLE_HEADER() "1" " Track ID1" " Locationfile://localhost/C:/dir/SongA1.mp3" " ArtistArtist A" "" "2" " Track ID2" " Locationfile://localhost/C:/dir/SongA2.mp3" " AlbumAlbum A" "" "3" " Track ID3" " Locationfile://localhost/C:/dir/SongA3.mp3" "" SIMPLE_FOOTER()); } TEST_F(ITunesLibraryParserTest, AlbumArtist) { AddExpectedTrack(1, "C:/dir/SongA1.mp3", "Artist A", "Unknown Album"); AddExpectedTrack(2, "C:/dir/SongA2.mp3", "Artist A", "Unknown Album"); AddExpectedTrack(3, "C:/dir/SongA3.mp3", "Artist A", "Unknown Album"); AddExpectedTrack(4, "C:/dir/SongA4.mp3", "Artist A", "Album"); TestParser( true, SIMPLE_HEADER() "1" " Track ID1" " Locationfile://localhost/C:/dir/SongA1.mp3" " Album ArtistArtist A" "" "2" " Track ID2" " Locationfile://localhost/C:/dir/SongA2.mp3" " ArtistArtist B" " Album ArtistArtist A" "" "3" " Track ID3" " Locationfile://localhost/C:/dir/SongA3.mp3" " Album ArtistArtist A" " ArtistArtist B" "" "4" " Track ID4" " Locationfile://localhost/C:/dir/SongA4.mp3" " AlbumAlbum" " ArtistArtist B" " Album ArtistArtist A" "" SIMPLE_FOOTER()); } TEST_F(ITunesLibraryParserTest, MacPath) { AddExpectedTrack(1, "dir/Song With Space.mp3", "Artist A", "Album A"); TestParser( true, SIMPLE_HEADER() // This path is concatenated with "http://localhost/", so no leading // slash should be used. SIMPLE_TRACK(1, 1, "dir/Song%20With%20Space.mp3", "Artist A", "Album A") SIMPLE_FOOTER()); } } // namespace } // namespace itunes