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.
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/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"
30 LibraryEntry(const std::string& artist, const std::string& album,
31 const base::FilePath& location)
38 base::FilePath location;
41 // 'c' with combinding cedilla.
42 const char kDeNormalizedName[] = {
43 'c', static_cast<unsigned char>(0xCC), static_cast<unsigned char>(0xA7), 0};
45 const char kNormalizedName[] = {
46 static_cast<unsigned char>(0xC3), static_cast<unsigned char>(0xA7), 0};
50 class TestITunesDataProvider : public ITunesDataProvider {
52 TestITunesDataProvider(const base::FilePath& xml_library_path,
53 const base::Closure& callback)
54 : ITunesDataProvider(xml_library_path),
57 ~TestITunesDataProvider() override {}
60 void OnLibraryChanged(const base::FilePath& path, bool error) override {
61 ITunesDataProvider::OnLibraryChanged(path, error);
65 base::Closure callback_;
67 DISALLOW_COPY_AND_ASSIGN(TestITunesDataProvider);
70 class ITunesDataProviderTest : public InProcessBrowserTest {
72 ITunesDataProviderTest() {}
73 virtual ~ITunesDataProviderTest() {}
76 virtual void SetUp() override {
77 ASSERT_TRUE(library_dir_.CreateUniqueTempDir());
78 WriteLibraryInternal(SetUpLibrary());
79 // The ImportedMediaGalleryRegistry is created on which ever thread calls
80 // GetInstance() first. It shouldn't matter what thread creates, however
81 // in practice it is always created on the UI thread, so this calls
82 // GetInstance here to mirror those real conditions.
83 ImportedMediaGalleryRegistry::GetInstance();
84 InProcessBrowserTest::SetUp();
88 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
90 quit_closure_ = loop.QuitClosure();
91 MediaFileSystemBackend::MediaTaskRunner()->PostTask(
93 base::Bind(&ITunesDataProviderTest::StartTestOnMediaTaskRunner,
94 base::Unretained(this)));
98 void WriteLibrary(const std::vector<LibraryEntry>& entries,
99 const base::Closure& callback) {
100 SetLibraryChangeCallback(callback);
101 WriteLibraryInternal(entries);
104 void SetLibraryChangeCallback(const base::Closure& callback) {
105 EXPECT_TRUE(library_changed_callback_.is_null());
106 library_changed_callback_ = callback;
109 ITunesDataProvider* data_provider() const {
110 return ImportedMediaGalleryRegistry::ITunesDataProvider();
113 const base::FilePath& library_dir() const {
114 return library_dir_.path();
117 base::FilePath XmlFile() const {
118 return library_dir_.path().AppendASCII("library.xml");
121 void ExpectTrackLocation(const std::string& artist, const std::string& album,
122 const std::string& track_name) {
123 base::FilePath track =
124 library_dir().AppendASCII(track_name).NormalizePathSeparators();
125 EXPECT_EQ(track.value(),
126 data_provider()->GetTrackLocation(
127 artist, album, track_name).NormalizePathSeparators().value());
130 void ExpectNoTrack(const std::string& artist, const std::string& album,
131 const std::string& track_name) {
132 EXPECT_TRUE(data_provider()->GetTrackLocation(
133 artist, album, track_name).empty()) << track_name;
137 // Get the initial set of library entries, called by SetUp. If no entries
138 // are returned the xml file is not created.
139 virtual std::vector<LibraryEntry> SetUpLibrary() {
140 return std::vector<LibraryEntry>();
143 // Start the test. The data provider is refreshed before calling StartTest
144 // and the result of the refresh is passed in.
145 virtual void StartTest(bool parse_success) = 0;
148 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
149 ImportedMediaGalleryRegistry* imported_registry =
150 ImportedMediaGalleryRegistry::GetInstance();
151 imported_registry->itunes_data_provider_.reset();
152 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
157 void StartTestOnMediaTaskRunner() {
158 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
159 ImportedMediaGalleryRegistry* imported_registry =
160 ImportedMediaGalleryRegistry::GetInstance();
161 imported_registry->itunes_data_provider_.reset(
162 new TestITunesDataProvider(
164 base::Bind(&ITunesDataProviderTest::OnLibraryChanged,
165 base::Unretained(this))));
166 data_provider()->RefreshData(base::Bind(&ITunesDataProviderTest::StartTest,
167 base::Unretained(this)));
170 void OnLibraryChanged() {
171 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
172 if (!library_changed_callback_.is_null()) {
173 library_changed_callback_.Run();
174 library_changed_callback_.Reset();
178 void WriteLibraryInternal(const std::vector<LibraryEntry>& entries) {
181 std::string xml = "<plist><dict><key>Tracks</key><dict>\n";
182 for (size_t i = 0; i < entries.size(); ++i) {
183 std::string separator;
187 GURL location("file://localhost" + separator +
188 entries[i].location.AsUTF8Unsafe());
189 std::string entry_string = base::StringPrintf(
190 "<key>%" PRIuS "</key><dict>\n"
191 " <key>Track ID</key><integer>%" PRIuS "</integer>\n"
192 " <key>Location</key><string>%s</string>\n"
193 " <key>Artist</key><string>%s</string>\n"
194 " <key>Album</key><string>%s</string>\n"
196 i + 1, i + 1, location.spec().c_str(), entries[i].artist.c_str(),
197 entries[i].album.c_str());
200 xml += "</dict></dict></plist>\n";
201 ASSERT_EQ(static_cast<int>(xml.size()),
202 base::WriteFile(XmlFile(), xml.c_str(), xml.size()));
205 base::ScopedTempDir library_dir_;
207 base::Closure library_changed_callback_;
209 base::Closure quit_closure_;
211 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderTest);
214 class ITunesDataProviderBasicTest : public ITunesDataProviderTest {
216 ITunesDataProviderBasicTest() {}
217 virtual ~ITunesDataProviderBasicTest() {}
219 std::vector<LibraryEntry> SetUpLibrary() override {
220 base::FilePath track = library_dir().AppendASCII("Track.mp3");
221 std::vector<LibraryEntry> entries;
222 entries.push_back(LibraryEntry("Artist", "Album", track));
226 void StartTest(bool parse_success) override {
227 EXPECT_TRUE(parse_success);
230 EXPECT_TRUE(data_provider()->KnownArtist("Artist"));
231 EXPECT_FALSE(data_provider()->KnownArtist("Artist2"));
234 EXPECT_TRUE(data_provider()->KnownAlbum("Artist", "Album"));
235 EXPECT_FALSE(data_provider()->KnownAlbum("Artist", "Album2"));
236 EXPECT_FALSE(data_provider()->KnownAlbum("Artist2", "Album"));
239 ExpectTrackLocation("Artist", "Album", "Track.mp3");
240 ExpectNoTrack("Artist", "Album", "Track2.mp3");
241 ExpectNoTrack("Artist", "Album2", "Track.mp3");
242 ExpectNoTrack("Artist2", "Album", "Track.mp3");
245 std::set<ITunesDataProvider::ArtistName> artists =
246 data_provider()->GetArtistNames();
247 ASSERT_EQ(1U, artists.size());
248 EXPECT_EQ("Artist", *artists.begin());
251 std::set<ITunesDataProvider::AlbumName> albums =
252 data_provider()->GetAlbumNames("Artist");
253 ASSERT_EQ(1U, albums.size());
254 EXPECT_EQ("Album", *albums.begin());
256 albums = data_provider()->GetAlbumNames("Artist2");
257 EXPECT_EQ(0U, albums.size());
260 base::FilePath track =
261 library_dir().AppendASCII("Track.mp3").NormalizePathSeparators();
262 ITunesDataProvider::Album album =
263 data_provider()->GetAlbum("Artist", "Album");
264 ASSERT_EQ(1U, album.size());
265 EXPECT_EQ(track.BaseName().AsUTF8Unsafe(), album.begin()->first);
266 EXPECT_EQ(track.value(),
267 album.begin()->second.NormalizePathSeparators().value());
269 album = data_provider()->GetAlbum("Artist", "Album2");
270 EXPECT_EQ(0U, album.size());
272 album = data_provider()->GetAlbum("Artist2", "Album");
273 EXPECT_EQ(0U, album.size());
279 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderBasicTest);
282 class ITunesDataProviderRefreshTest : public ITunesDataProviderTest {
284 ITunesDataProviderRefreshTest() {}
285 virtual ~ITunesDataProviderRefreshTest() {}
287 std::vector<LibraryEntry> SetUpLibrary() override {
288 base::FilePath track = library_dir().AppendASCII("Track.mp3");
289 std::vector<LibraryEntry> entries;
290 entries.push_back(LibraryEntry("Artist", "Album", track));
294 void StartTest(bool parse_success) override {
295 EXPECT_TRUE(parse_success);
298 ExpectTrackLocation("Artist", "Album", "Track.mp3");
299 ExpectNoTrack("Artist2", "Album2", "Track2.mp3");
302 base::FilePath track2 = library_dir().AppendASCII("Track2.mp3");
303 std::vector<LibraryEntry> entries;
304 entries.push_back(LibraryEntry("Artist2", "Album2", track2));
305 WriteLibrary(entries,
306 base::Bind(&ITunesDataProviderRefreshTest::CheckAfterWrite,
307 base::Unretained(this)));
310 void CheckAfterWrite() {
312 ExpectTrackLocation("Artist", "Album", "Track.mp3");
313 ExpectNoTrack("Artist2", "Album2", "Track2.mp3");
315 data_provider()->RefreshData(
316 base::Bind(&ITunesDataProviderRefreshTest::CheckRefresh,
317 base::Unretained(this)));
320 void CheckRefresh(bool is_valid) {
321 EXPECT_TRUE(is_valid);
323 ExpectTrackLocation("Artist2", "Album2", "Track2.mp3");
324 ExpectNoTrack("Artist", "Album", "Track.mp3");
329 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderRefreshTest);
332 class ITunesDataProviderInvalidTest : public ITunesDataProviderTest {
334 ITunesDataProviderInvalidTest() {}
335 virtual ~ITunesDataProviderInvalidTest() {}
337 std::vector<LibraryEntry> SetUpLibrary() override {
338 base::FilePath track = library_dir().AppendASCII("Track.mp3");
339 std::vector<LibraryEntry> entries;
340 entries.push_back(LibraryEntry("Artist", "Album", track));
344 void StartTest(bool parse_success) override {
345 EXPECT_TRUE(parse_success);
347 SetLibraryChangeCallback(
348 base::Bind(&ITunesDataProvider::RefreshData,
349 base::Unretained(data_provider()),
350 base::Bind(&ITunesDataProviderInvalidTest::CheckInvalid,
351 base::Unretained(this))));
352 ASSERT_EQ(1L, base::WriteFile(XmlFile(), " ", 1));
355 void CheckInvalid(bool is_valid) {
356 EXPECT_FALSE(is_valid);
361 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderInvalidTest);
364 class ITunesDataProviderUniqueNameTest : public ITunesDataProviderTest {
366 ITunesDataProviderUniqueNameTest() {}
367 virtual ~ITunesDataProviderUniqueNameTest() {}
369 std::vector<LibraryEntry> SetUpLibrary() override {
370 base::FilePath track = library_dir().AppendASCII("Track.mp3");
371 std::vector<LibraryEntry> entries;
372 // Dupe album names should get uniquified with the track id, which in the
373 // test framework is the vector index.
374 entries.push_back(LibraryEntry("Artist", "Album", track));
375 entries.push_back(LibraryEntry("Artist", "Album", track));
376 entries.push_back(LibraryEntry("Artist", "Album2", track));
380 void StartTest(bool parse_success) override {
381 EXPECT_TRUE(parse_success);
383 base::FilePath track =
384 library_dir().AppendASCII("Track.mp3").NormalizePathSeparators();
385 EXPECT_EQ(track.value(),
386 data_provider()->GetTrackLocation(
388 "Track (1).mp3").NormalizePathSeparators().value());
389 EXPECT_EQ(track.value(),
390 data_provider()->GetTrackLocation(
392 "Track (2).mp3").NormalizePathSeparators().value());
393 EXPECT_EQ(track.value(),
394 data_provider()->GetTrackLocation(
396 "Track.mp3").NormalizePathSeparators().value());
402 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderUniqueNameTest);
405 class ITunesDataProviderEscapeTest : public ITunesDataProviderTest {
406 // Albums and tracks that aren't the same, but become the same after
407 // replacing bad characters are not handled properly, but that case should
408 // never happen in practice.
410 ITunesDataProviderEscapeTest() {}
411 virtual ~ITunesDataProviderEscapeTest() {}
413 std::vector<LibraryEntry> SetUpLibrary() override {
414 base::FilePath track = library_dir().AppendASCII("Track:1.mp3");
415 std::vector<LibraryEntry> entries;
416 entries.push_back(LibraryEntry("Artist:/name", "Album:name/", track));
417 entries.push_back(LibraryEntry("Artist/name", "Album:name", track));
418 entries.push_back(LibraryEntry("Artist/name", "Album:name", track));
419 entries.push_back(LibraryEntry(kDeNormalizedName, kNormalizedName, track));
423 void StartTest(bool parse_success) override {
424 EXPECT_TRUE(parse_success);
426 base::FilePath track =
427 library_dir().AppendASCII("Track:1.mp3").NormalizePathSeparators();
428 EXPECT_EQ(track.value(),
429 data_provider()->GetTrackLocation(
430 "Artist__name", "Album_name_",
431 "Track_1.mp3").NormalizePathSeparators().value());
432 EXPECT_EQ(track.value(),
433 data_provider()->GetTrackLocation(
434 "Artist_name", "Album_name",
435 "Track_1 (2).mp3").NormalizePathSeparators().value());
436 EXPECT_EQ(track.value(),
437 data_provider()->GetTrackLocation(
438 "Artist_name", "Album_name",
439 "Track_1 (3).mp3").NormalizePathSeparators().value());
440 EXPECT_EQ(track.value(),
441 data_provider()->GetTrackLocation(
442 kNormalizedName, kNormalizedName,
443 "Track_1.mp3").NormalizePathSeparators().value());
449 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderEscapeTest);
452 IN_PROC_BROWSER_TEST_F(ITunesDataProviderBasicTest, BasicTest) {
456 IN_PROC_BROWSER_TEST_F(ITunesDataProviderRefreshTest, RefreshTest) {
460 IN_PROC_BROWSER_TEST_F(ITunesDataProviderInvalidTest, InvalidTest) {
464 IN_PROC_BROWSER_TEST_F(ITunesDataProviderUniqueNameTest, UniqueNameTest) {
468 IN_PROC_BROWSER_TEST_F(ITunesDataProviderEscapeTest, EscapeTest) {
472 } // namespace itunes