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 virtual ~TestITunesDataProvider() {}
60 virtual void OnLibraryChanged(const base::FilePath
& path
,
61 bool error
) override
{
62 ITunesDataProvider::OnLibraryChanged(path
, error
);
66 base::Closure callback_
;
68 DISALLOW_COPY_AND_ASSIGN(TestITunesDataProvider
);
71 class ITunesDataProviderTest
: public InProcessBrowserTest
{
73 ITunesDataProviderTest() {}
74 virtual ~ITunesDataProviderTest() {}
77 virtual void SetUp() override
{
78 ASSERT_TRUE(library_dir_
.CreateUniqueTempDir());
79 WriteLibraryInternal(SetUpLibrary());
80 // The ImportedMediaGalleryRegistry is created on which ever thread calls
81 // GetInstance() first. It shouldn't matter what thread creates, however
82 // in practice it is always created on the UI thread, so this calls
83 // GetInstance here to mirror those real conditions.
84 ImportedMediaGalleryRegistry::GetInstance();
85 InProcessBrowserTest::SetUp();
89 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
91 quit_closure_
= loop
.QuitClosure();
92 MediaFileSystemBackend::MediaTaskRunner()->PostTask(
94 base::Bind(&ITunesDataProviderTest::StartTestOnMediaTaskRunner
,
95 base::Unretained(this)));
99 void WriteLibrary(const std::vector
<LibraryEntry
>& entries
,
100 const base::Closure
& callback
) {
101 SetLibraryChangeCallback(callback
);
102 WriteLibraryInternal(entries
);
105 void SetLibraryChangeCallback(const base::Closure
& callback
) {
106 EXPECT_TRUE(library_changed_callback_
.is_null());
107 library_changed_callback_
= callback
;
110 ITunesDataProvider
* data_provider() const {
111 return ImportedMediaGalleryRegistry::ITunesDataProvider();
114 const base::FilePath
& library_dir() const {
115 return library_dir_
.path();
118 base::FilePath
XmlFile() const {
119 return library_dir_
.path().AppendASCII("library.xml");
122 void ExpectTrackLocation(const std::string
& artist
, const std::string
& album
,
123 const std::string
& track_name
) {
124 base::FilePath track
=
125 library_dir().AppendASCII(track_name
).NormalizePathSeparators();
126 EXPECT_EQ(track
.value(),
127 data_provider()->GetTrackLocation(
128 artist
, album
, track_name
).NormalizePathSeparators().value());
131 void ExpectNoTrack(const std::string
& artist
, const std::string
& album
,
132 const std::string
& track_name
) {
133 EXPECT_TRUE(data_provider()->GetTrackLocation(
134 artist
, album
, track_name
).empty()) << track_name
;
138 // Get the initial set of library entries, called by SetUp. If no entries
139 // are returned the xml file is not created.
140 virtual std::vector
<LibraryEntry
> SetUpLibrary() {
141 return std::vector
<LibraryEntry
>();
144 // Start the test. The data provider is refreshed before calling StartTest
145 // and the result of the refresh is passed in.
146 virtual void StartTest(bool parse_success
) = 0;
149 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
150 ImportedMediaGalleryRegistry
* imported_registry
=
151 ImportedMediaGalleryRegistry::GetInstance();
152 imported_registry
->itunes_data_provider_
.reset();
153 content::BrowserThread::PostTask(content::BrowserThread::UI
, FROM_HERE
,
158 void StartTestOnMediaTaskRunner() {
159 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
160 ImportedMediaGalleryRegistry
* imported_registry
=
161 ImportedMediaGalleryRegistry::GetInstance();
162 imported_registry
->itunes_data_provider_
.reset(
163 new TestITunesDataProvider(
165 base::Bind(&ITunesDataProviderTest::OnLibraryChanged
,
166 base::Unretained(this))));
167 data_provider()->RefreshData(base::Bind(&ITunesDataProviderTest::StartTest
,
168 base::Unretained(this)));
171 void OnLibraryChanged() {
172 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
173 if (!library_changed_callback_
.is_null()) {
174 library_changed_callback_
.Run();
175 library_changed_callback_
.Reset();
179 void WriteLibraryInternal(const std::vector
<LibraryEntry
>& entries
) {
182 std::string xml
= "<plist><dict><key>Tracks</key><dict>\n";
183 for (size_t i
= 0; i
< entries
.size(); ++i
) {
184 std::string separator
;
188 GURL
location("file://localhost" + separator
+
189 entries
[i
].location
.AsUTF8Unsafe());
190 std::string entry_string
= base::StringPrintf(
191 "<key>%" PRIuS
"</key><dict>\n"
192 " <key>Track ID</key><integer>%" PRIuS
"</integer>\n"
193 " <key>Location</key><string>%s</string>\n"
194 " <key>Artist</key><string>%s</string>\n"
195 " <key>Album</key><string>%s</string>\n"
197 i
+ 1, i
+ 1, location
.spec().c_str(), entries
[i
].artist
.c_str(),
198 entries
[i
].album
.c_str());
201 xml
+= "</dict></dict></plist>\n";
202 ASSERT_EQ(static_cast<int>(xml
.size()),
203 base::WriteFile(XmlFile(), xml
.c_str(), xml
.size()));
206 base::ScopedTempDir library_dir_
;
208 base::Closure library_changed_callback_
;
210 base::Closure quit_closure_
;
212 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderTest
);
215 class ITunesDataProviderBasicTest
: public ITunesDataProviderTest
{
217 ITunesDataProviderBasicTest() {}
218 virtual ~ITunesDataProviderBasicTest() {}
220 virtual std::vector
<LibraryEntry
> SetUpLibrary() override
{
221 base::FilePath track
= library_dir().AppendASCII("Track.mp3");
222 std::vector
<LibraryEntry
> entries
;
223 entries
.push_back(LibraryEntry("Artist", "Album", track
));
227 virtual void StartTest(bool parse_success
) override
{
228 EXPECT_TRUE(parse_success
);
231 EXPECT_TRUE(data_provider()->KnownArtist("Artist"));
232 EXPECT_FALSE(data_provider()->KnownArtist("Artist2"));
235 EXPECT_TRUE(data_provider()->KnownAlbum("Artist", "Album"));
236 EXPECT_FALSE(data_provider()->KnownAlbum("Artist", "Album2"));
237 EXPECT_FALSE(data_provider()->KnownAlbum("Artist2", "Album"));
240 ExpectTrackLocation("Artist", "Album", "Track.mp3");
241 ExpectNoTrack("Artist", "Album", "Track2.mp3");
242 ExpectNoTrack("Artist", "Album2", "Track.mp3");
243 ExpectNoTrack("Artist2", "Album", "Track.mp3");
246 std::set
<ITunesDataProvider::ArtistName
> artists
=
247 data_provider()->GetArtistNames();
248 ASSERT_EQ(1U, artists
.size());
249 EXPECT_EQ("Artist", *artists
.begin());
252 std::set
<ITunesDataProvider::AlbumName
> albums
=
253 data_provider()->GetAlbumNames("Artist");
254 ASSERT_EQ(1U, albums
.size());
255 EXPECT_EQ("Album", *albums
.begin());
257 albums
= data_provider()->GetAlbumNames("Artist2");
258 EXPECT_EQ(0U, albums
.size());
261 base::FilePath track
=
262 library_dir().AppendASCII("Track.mp3").NormalizePathSeparators();
263 ITunesDataProvider::Album album
=
264 data_provider()->GetAlbum("Artist", "Album");
265 ASSERT_EQ(1U, album
.size());
266 EXPECT_EQ(track
.BaseName().AsUTF8Unsafe(), album
.begin()->first
);
267 EXPECT_EQ(track
.value(),
268 album
.begin()->second
.NormalizePathSeparators().value());
270 album
= data_provider()->GetAlbum("Artist", "Album2");
271 EXPECT_EQ(0U, album
.size());
273 album
= data_provider()->GetAlbum("Artist2", "Album");
274 EXPECT_EQ(0U, album
.size());
280 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderBasicTest
);
283 class ITunesDataProviderRefreshTest
: public ITunesDataProviderTest
{
285 ITunesDataProviderRefreshTest() {}
286 virtual ~ITunesDataProviderRefreshTest() {}
288 virtual std::vector
<LibraryEntry
> SetUpLibrary() override
{
289 base::FilePath track
= library_dir().AppendASCII("Track.mp3");
290 std::vector
<LibraryEntry
> entries
;
291 entries
.push_back(LibraryEntry("Artist", "Album", track
));
295 virtual void StartTest(bool parse_success
) override
{
296 EXPECT_TRUE(parse_success
);
299 ExpectTrackLocation("Artist", "Album", "Track.mp3");
300 ExpectNoTrack("Artist2", "Album2", "Track2.mp3");
303 base::FilePath track2
= library_dir().AppendASCII("Track2.mp3");
304 std::vector
<LibraryEntry
> entries
;
305 entries
.push_back(LibraryEntry("Artist2", "Album2", track2
));
306 WriteLibrary(entries
,
307 base::Bind(&ITunesDataProviderRefreshTest::CheckAfterWrite
,
308 base::Unretained(this)));
311 void CheckAfterWrite() {
313 ExpectTrackLocation("Artist", "Album", "Track.mp3");
314 ExpectNoTrack("Artist2", "Album2", "Track2.mp3");
316 data_provider()->RefreshData(
317 base::Bind(&ITunesDataProviderRefreshTest::CheckRefresh
,
318 base::Unretained(this)));
321 void CheckRefresh(bool is_valid
) {
322 EXPECT_TRUE(is_valid
);
324 ExpectTrackLocation("Artist2", "Album2", "Track2.mp3");
325 ExpectNoTrack("Artist", "Album", "Track.mp3");
330 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderRefreshTest
);
333 class ITunesDataProviderInvalidTest
: public ITunesDataProviderTest
{
335 ITunesDataProviderInvalidTest() {}
336 virtual ~ITunesDataProviderInvalidTest() {}
338 virtual std::vector
<LibraryEntry
> SetUpLibrary() override
{
339 base::FilePath track
= library_dir().AppendASCII("Track.mp3");
340 std::vector
<LibraryEntry
> entries
;
341 entries
.push_back(LibraryEntry("Artist", "Album", track
));
345 virtual void StartTest(bool parse_success
) override
{
346 EXPECT_TRUE(parse_success
);
348 SetLibraryChangeCallback(
349 base::Bind(&ITunesDataProvider::RefreshData
,
350 base::Unretained(data_provider()),
351 base::Bind(&ITunesDataProviderInvalidTest::CheckInvalid
,
352 base::Unretained(this))));
353 ASSERT_EQ(1L, base::WriteFile(XmlFile(), " ", 1));
356 void CheckInvalid(bool is_valid
) {
357 EXPECT_FALSE(is_valid
);
362 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderInvalidTest
);
365 class ITunesDataProviderUniqueNameTest
: public ITunesDataProviderTest
{
367 ITunesDataProviderUniqueNameTest() {}
368 virtual ~ITunesDataProviderUniqueNameTest() {}
370 virtual std::vector
<LibraryEntry
> SetUpLibrary() override
{
371 base::FilePath track
= library_dir().AppendASCII("Track.mp3");
372 std::vector
<LibraryEntry
> entries
;
373 // Dupe album names should get uniquified with the track id, which in the
374 // test framework is the vector index.
375 entries
.push_back(LibraryEntry("Artist", "Album", track
));
376 entries
.push_back(LibraryEntry("Artist", "Album", track
));
377 entries
.push_back(LibraryEntry("Artist", "Album2", track
));
381 virtual void StartTest(bool parse_success
) override
{
382 EXPECT_TRUE(parse_success
);
384 base::FilePath track
=
385 library_dir().AppendASCII("Track.mp3").NormalizePathSeparators();
386 EXPECT_EQ(track
.value(),
387 data_provider()->GetTrackLocation(
389 "Track (1).mp3").NormalizePathSeparators().value());
390 EXPECT_EQ(track
.value(),
391 data_provider()->GetTrackLocation(
393 "Track (2).mp3").NormalizePathSeparators().value());
394 EXPECT_EQ(track
.value(),
395 data_provider()->GetTrackLocation(
397 "Track.mp3").NormalizePathSeparators().value());
403 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderUniqueNameTest
);
406 class ITunesDataProviderEscapeTest
: public ITunesDataProviderTest
{
407 // Albums and tracks that aren't the same, but become the same after
408 // replacing bad characters are not handled properly, but that case should
409 // never happen in practice.
411 ITunesDataProviderEscapeTest() {}
412 virtual ~ITunesDataProviderEscapeTest() {}
414 virtual std::vector
<LibraryEntry
> SetUpLibrary() override
{
415 base::FilePath track
= library_dir().AppendASCII("Track:1.mp3");
416 std::vector
<LibraryEntry
> entries
;
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("Artist/name", "Album:name", track
));
420 entries
.push_back(LibraryEntry(kDeNormalizedName
, kNormalizedName
, track
));
424 virtual void StartTest(bool parse_success
) override
{
425 EXPECT_TRUE(parse_success
);
427 base::FilePath track
=
428 library_dir().AppendASCII("Track:1.mp3").NormalizePathSeparators();
429 EXPECT_EQ(track
.value(),
430 data_provider()->GetTrackLocation(
431 "Artist__name", "Album_name_",
432 "Track_1.mp3").NormalizePathSeparators().value());
433 EXPECT_EQ(track
.value(),
434 data_provider()->GetTrackLocation(
435 "Artist_name", "Album_name",
436 "Track_1 (2).mp3").NormalizePathSeparators().value());
437 EXPECT_EQ(track
.value(),
438 data_provider()->GetTrackLocation(
439 "Artist_name", "Album_name",
440 "Track_1 (3).mp3").NormalizePathSeparators().value());
441 EXPECT_EQ(track
.value(),
442 data_provider()->GetTrackLocation(
443 kNormalizedName
, kNormalizedName
,
444 "Track_1.mp3").NormalizePathSeparators().value());
450 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderEscapeTest
);
453 IN_PROC_BROWSER_TEST_F(ITunesDataProviderBasicTest
, BasicTest
) {
457 IN_PROC_BROWSER_TEST_F(ITunesDataProviderRefreshTest
, RefreshTest
) {
461 IN_PROC_BROWSER_TEST_F(ITunesDataProviderInvalidTest
, InvalidTest
) {
465 IN_PROC_BROWSER_TEST_F(ITunesDataProviderUniqueNameTest
, UniqueNameTest
) {
469 IN_PROC_BROWSER_TEST_F(ITunesDataProviderEscapeTest
, EscapeTest
) {
473 } // namespace itunes