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() {}
75 void SetUp() override
{
76 ASSERT_TRUE(library_dir_
.CreateUniqueTempDir());
77 WriteLibraryInternal(SetUpLibrary());
78 // The ImportedMediaGalleryRegistry is created on which ever thread calls
79 // GetInstance() first. It shouldn't matter what thread creates, however
80 // in practice it is always created on the UI thread, so this calls
81 // GetInstance here to mirror those real conditions.
82 ImportedMediaGalleryRegistry::GetInstance();
83 InProcessBrowserTest::SetUp();
87 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
89 quit_closure_
= loop
.QuitClosure();
90 MediaFileSystemBackend::MediaTaskRunner()->PostTask(
92 base::Bind(&ITunesDataProviderTest::StartTestOnMediaTaskRunner
,
93 base::Unretained(this)));
97 void WriteLibrary(const std::vector
<LibraryEntry
>& entries
,
98 const base::Closure
& callback
) {
99 SetLibraryChangeCallback(callback
);
100 WriteLibraryInternal(entries
);
103 void SetLibraryChangeCallback(const base::Closure
& callback
) {
104 EXPECT_TRUE(library_changed_callback_
.is_null());
105 library_changed_callback_
= callback
;
108 ITunesDataProvider
* data_provider() const {
109 return ImportedMediaGalleryRegistry::ITunesDataProvider();
112 const base::FilePath
& library_dir() const {
113 return library_dir_
.path();
116 base::FilePath
XmlFile() const {
117 return library_dir_
.path().AppendASCII("library.xml");
120 void ExpectTrackLocation(const std::string
& artist
, const std::string
& album
,
121 const std::string
& track_name
) {
122 base::FilePath track
=
123 library_dir().AppendASCII(track_name
).NormalizePathSeparators();
124 EXPECT_EQ(track
.value(),
125 data_provider()->GetTrackLocation(
126 artist
, album
, track_name
).NormalizePathSeparators().value());
129 void ExpectNoTrack(const std::string
& artist
, const std::string
& album
,
130 const std::string
& track_name
) {
131 EXPECT_TRUE(data_provider()->GetTrackLocation(
132 artist
, album
, track_name
).empty()) << track_name
;
136 // Get the initial set of library entries, called by SetUp. If no entries
137 // are returned the xml file is not created.
138 virtual std::vector
<LibraryEntry
> SetUpLibrary() {
139 return std::vector
<LibraryEntry
>();
142 // Start the test. The data provider is refreshed before calling StartTest
143 // and the result of the refresh is passed in.
144 virtual void StartTest(bool parse_success
) = 0;
147 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
148 ImportedMediaGalleryRegistry
* imported_registry
=
149 ImportedMediaGalleryRegistry::GetInstance();
150 imported_registry
->itunes_data_provider_
.reset();
151 content::BrowserThread::PostTask(content::BrowserThread::UI
, FROM_HERE
,
156 void StartTestOnMediaTaskRunner() {
157 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
158 ImportedMediaGalleryRegistry
* imported_registry
=
159 ImportedMediaGalleryRegistry::GetInstance();
160 imported_registry
->itunes_data_provider_
.reset(
161 new TestITunesDataProvider(
163 base::Bind(&ITunesDataProviderTest::OnLibraryChanged
,
164 base::Unretained(this))));
165 data_provider()->RefreshData(base::Bind(&ITunesDataProviderTest::StartTest
,
166 base::Unretained(this)));
169 void OnLibraryChanged() {
170 DCHECK(MediaFileSystemBackend::CurrentlyOnMediaTaskRunnerThread());
171 if (!library_changed_callback_
.is_null()) {
172 library_changed_callback_
.Run();
173 library_changed_callback_
.Reset();
177 void WriteLibraryInternal(const std::vector
<LibraryEntry
>& entries
) {
180 std::string xml
= "<plist><dict><key>Tracks</key><dict>\n";
181 for (size_t i
= 0; i
< entries
.size(); ++i
) {
182 std::string separator
;
186 GURL
location("file://localhost" + separator
+
187 entries
[i
].location
.AsUTF8Unsafe());
188 std::string entry_string
= base::StringPrintf(
189 "<key>%" PRIuS
"</key><dict>\n"
190 " <key>Track ID</key><integer>%" PRIuS
"</integer>\n"
191 " <key>Location</key><string>%s</string>\n"
192 " <key>Artist</key><string>%s</string>\n"
193 " <key>Album</key><string>%s</string>\n"
195 i
+ 1, i
+ 1, location
.spec().c_str(), entries
[i
].artist
.c_str(),
196 entries
[i
].album
.c_str());
199 xml
+= "</dict></dict></plist>\n";
200 ASSERT_EQ(static_cast<int>(xml
.size()),
201 base::WriteFile(XmlFile(), xml
.c_str(), xml
.size()));
204 base::ScopedTempDir library_dir_
;
206 base::Closure library_changed_callback_
;
208 base::Closure quit_closure_
;
210 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderTest
);
213 class ITunesDataProviderBasicTest
: public ITunesDataProviderTest
{
215 ITunesDataProviderBasicTest() {}
217 std::vector
<LibraryEntry
> SetUpLibrary() override
{
218 base::FilePath track
= library_dir().AppendASCII("Track.mp3");
219 std::vector
<LibraryEntry
> entries
;
220 entries
.push_back(LibraryEntry("Artist", "Album", track
));
224 void StartTest(bool parse_success
) override
{
225 EXPECT_TRUE(parse_success
);
228 EXPECT_TRUE(data_provider()->KnownArtist("Artist"));
229 EXPECT_FALSE(data_provider()->KnownArtist("Artist2"));
232 EXPECT_TRUE(data_provider()->KnownAlbum("Artist", "Album"));
233 EXPECT_FALSE(data_provider()->KnownAlbum("Artist", "Album2"));
234 EXPECT_FALSE(data_provider()->KnownAlbum("Artist2", "Album"));
237 ExpectTrackLocation("Artist", "Album", "Track.mp3");
238 ExpectNoTrack("Artist", "Album", "Track2.mp3");
239 ExpectNoTrack("Artist", "Album2", "Track.mp3");
240 ExpectNoTrack("Artist2", "Album", "Track.mp3");
243 std::set
<ITunesDataProvider::ArtistName
> artists
=
244 data_provider()->GetArtistNames();
245 ASSERT_EQ(1U, artists
.size());
246 EXPECT_EQ("Artist", *artists
.begin());
249 std::set
<ITunesDataProvider::AlbumName
> albums
=
250 data_provider()->GetAlbumNames("Artist");
251 ASSERT_EQ(1U, albums
.size());
252 EXPECT_EQ("Album", *albums
.begin());
254 albums
= data_provider()->GetAlbumNames("Artist2");
255 EXPECT_EQ(0U, albums
.size());
258 base::FilePath track
=
259 library_dir().AppendASCII("Track.mp3").NormalizePathSeparators();
260 ITunesDataProvider::Album album
=
261 data_provider()->GetAlbum("Artist", "Album");
262 ASSERT_EQ(1U, album
.size());
263 EXPECT_EQ(track
.BaseName().AsUTF8Unsafe(), album
.begin()->first
);
264 EXPECT_EQ(track
.value(),
265 album
.begin()->second
.NormalizePathSeparators().value());
267 album
= data_provider()->GetAlbum("Artist", "Album2");
268 EXPECT_EQ(0U, album
.size());
270 album
= data_provider()->GetAlbum("Artist2", "Album");
271 EXPECT_EQ(0U, album
.size());
277 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderBasicTest
);
280 class ITunesDataProviderRefreshTest
: public ITunesDataProviderTest
{
282 ITunesDataProviderRefreshTest() {}
284 std::vector
<LibraryEntry
> SetUpLibrary() override
{
285 base::FilePath track
= library_dir().AppendASCII("Track.mp3");
286 std::vector
<LibraryEntry
> entries
;
287 entries
.push_back(LibraryEntry("Artist", "Album", track
));
291 void StartTest(bool parse_success
) override
{
292 EXPECT_TRUE(parse_success
);
295 ExpectTrackLocation("Artist", "Album", "Track.mp3");
296 ExpectNoTrack("Artist2", "Album2", "Track2.mp3");
299 base::FilePath track2
= library_dir().AppendASCII("Track2.mp3");
300 std::vector
<LibraryEntry
> entries
;
301 entries
.push_back(LibraryEntry("Artist2", "Album2", track2
));
302 WriteLibrary(entries
,
303 base::Bind(&ITunesDataProviderRefreshTest::CheckAfterWrite
,
304 base::Unretained(this)));
307 void CheckAfterWrite() {
309 ExpectTrackLocation("Artist", "Album", "Track.mp3");
310 ExpectNoTrack("Artist2", "Album2", "Track2.mp3");
312 data_provider()->RefreshData(
313 base::Bind(&ITunesDataProviderRefreshTest::CheckRefresh
,
314 base::Unretained(this)));
317 void CheckRefresh(bool is_valid
) {
318 EXPECT_TRUE(is_valid
);
320 ExpectTrackLocation("Artist2", "Album2", "Track2.mp3");
321 ExpectNoTrack("Artist", "Album", "Track.mp3");
326 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderRefreshTest
);
329 class ITunesDataProviderInvalidTest
: public ITunesDataProviderTest
{
331 ITunesDataProviderInvalidTest() {}
333 std::vector
<LibraryEntry
> SetUpLibrary() override
{
334 base::FilePath track
= library_dir().AppendASCII("Track.mp3");
335 std::vector
<LibraryEntry
> entries
;
336 entries
.push_back(LibraryEntry("Artist", "Album", track
));
340 void StartTest(bool parse_success
) override
{
341 EXPECT_TRUE(parse_success
);
343 SetLibraryChangeCallback(
344 base::Bind(&ITunesDataProvider::RefreshData
,
345 base::Unretained(data_provider()),
346 base::Bind(&ITunesDataProviderInvalidTest::CheckInvalid
,
347 base::Unretained(this))));
348 ASSERT_EQ(1L, base::WriteFile(XmlFile(), " ", 1));
351 void CheckInvalid(bool is_valid
) {
352 EXPECT_FALSE(is_valid
);
357 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderInvalidTest
);
360 class ITunesDataProviderUniqueNameTest
: public ITunesDataProviderTest
{
362 ITunesDataProviderUniqueNameTest() {}
364 std::vector
<LibraryEntry
> SetUpLibrary() override
{
365 base::FilePath track
= library_dir().AppendASCII("Track.mp3");
366 std::vector
<LibraryEntry
> entries
;
367 // Dupe album names should get uniquified with the track id, which in the
368 // test framework is the vector index.
369 entries
.push_back(LibraryEntry("Artist", "Album", track
));
370 entries
.push_back(LibraryEntry("Artist", "Album", track
));
371 entries
.push_back(LibraryEntry("Artist", "Album2", track
));
375 void StartTest(bool parse_success
) override
{
376 EXPECT_TRUE(parse_success
);
378 base::FilePath track
=
379 library_dir().AppendASCII("Track.mp3").NormalizePathSeparators();
380 EXPECT_EQ(track
.value(),
381 data_provider()->GetTrackLocation(
383 "Track (1).mp3").NormalizePathSeparators().value());
384 EXPECT_EQ(track
.value(),
385 data_provider()->GetTrackLocation(
387 "Track (2).mp3").NormalizePathSeparators().value());
388 EXPECT_EQ(track
.value(),
389 data_provider()->GetTrackLocation(
391 "Track.mp3").NormalizePathSeparators().value());
397 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderUniqueNameTest
);
400 class ITunesDataProviderEscapeTest
: public ITunesDataProviderTest
{
401 // Albums and tracks that aren't the same, but become the same after
402 // replacing bad characters are not handled properly, but that case should
403 // never happen in practice.
405 ITunesDataProviderEscapeTest() {}
407 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 entries
.push_back(LibraryEntry(kDeNormalizedName
, kNormalizedName
, track
));
417 void StartTest(bool parse_success
) override
{
418 EXPECT_TRUE(parse_success
);
420 base::FilePath track
=
421 library_dir().AppendASCII("Track:1.mp3").NormalizePathSeparators();
422 EXPECT_EQ(track
.value(),
423 data_provider()->GetTrackLocation(
424 "Artist__name", "Album_name_",
425 "Track_1.mp3").NormalizePathSeparators().value());
426 EXPECT_EQ(track
.value(),
427 data_provider()->GetTrackLocation(
428 "Artist_name", "Album_name",
429 "Track_1 (2).mp3").NormalizePathSeparators().value());
430 EXPECT_EQ(track
.value(),
431 data_provider()->GetTrackLocation(
432 "Artist_name", "Album_name",
433 "Track_1 (3).mp3").NormalizePathSeparators().value());
434 EXPECT_EQ(track
.value(),
435 data_provider()->GetTrackLocation(
436 kNormalizedName
, kNormalizedName
,
437 "Track_1.mp3").NormalizePathSeparators().value());
443 DISALLOW_COPY_AND_ASSIGN(ITunesDataProviderEscapeTest
);
446 IN_PROC_BROWSER_TEST_F(ITunesDataProviderBasicTest
, BasicTest
) {
450 IN_PROC_BROWSER_TEST_F(ITunesDataProviderRefreshTest
, RefreshTest
) {
454 IN_PROC_BROWSER_TEST_F(ITunesDataProviderInvalidTest
, InvalidTest
) {
458 IN_PROC_BROWSER_TEST_F(ITunesDataProviderUniqueNameTest
, UniqueNameTest
) {
462 IN_PROC_BROWSER_TEST_F(ITunesDataProviderEscapeTest
, EscapeTest
) {
466 } // namespace itunes