/* * Strawberry Music Player * This file was part of Clementine. * Copyright 2010, David Sansome * Copyright 2019, Jonas Kvinge * * Strawberry is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Strawberry is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Strawberry. If not, see . * */ #include #include #include #include #include #include #include "test_utils.h" #include "core/timeconstants.h" #include "core/song.h" #include "core/database.h" #include "core/logging.h" #include "collection/collectionbackend.h" #include "collection/collection.h" namespace { class CollectionBackendTest : public ::testing::Test { protected: void SetUp() override { database_.reset(new MemoryDatabase(nullptr)); backend_.reset(new CollectionBackend); backend_->Init(database_.get(), Song::Source_Collection, SCollection::kSongsTable, SCollection::kDirsTable, SCollection::kSubdirsTable, SCollection::kFtsTable); } Song MakeDummySong(int directory_id) { // Returns a valid song with all the required fields set Song ret; ret.set_directory_id(directory_id); ret.set_url(QUrl::fromLocalFile("foo.flac")); ret.set_mtime(1); ret.set_ctime(1); ret.set_filesize(1); return ret; } std::shared_ptr database_; std::unique_ptr backend_; }; TEST_F(CollectionBackendTest, EmptyDatabase) { // Check the database is empty to start with QStringList artists = backend_->GetAllArtists(); EXPECT_TRUE(artists.isEmpty()); CollectionBackend::AlbumList albums = backend_->GetAllAlbums(); EXPECT_TRUE(albums.isEmpty()); } TEST_F(CollectionBackendTest, AddDirectory) { QSignalSpy spy(backend_.get(), SIGNAL(DirectoryDiscovered(Directory, SubdirectoryList))); backend_->AddDirectory("/tmp"); // Check the signal was emitted correctly ASSERT_EQ(1, spy.count()); Directory dir = spy[0][0].value(); EXPECT_EQ(QFileInfo("/tmp").canonicalFilePath(), dir.path); EXPECT_EQ(1, dir.id); EXPECT_EQ(0, spy[0][1].value().size()); } TEST_F(CollectionBackendTest, RemoveDirectory) { // Add a directory Directory dir; dir.id = 1; dir.path = "/tmp"; backend_->AddDirectory(dir.path); QSignalSpy spy(backend_.get(), SIGNAL(DirectoryDeleted(Directory))); // Remove the directory again backend_->RemoveDirectory(dir); // Check the signal was emitted correctly ASSERT_EQ(1, spy.count()); dir = spy[0][0].value(); EXPECT_EQ("/tmp", dir.path); EXPECT_EQ(1, dir.id); } TEST_F(CollectionBackendTest, AddInvalidSong) { // Adding a song without certain fields set should fail backend_->AddDirectory("/tmp"); Song s; //s.set_url(QUrl::fromLocalFile("foo.flac")); s.set_directory_id(1); QSignalSpy spy(database_.get(), SIGNAL(Error(QString))); backend_->AddOrUpdateSongs(SongList() << s); //ASSERT_EQ(1, spy.count()); spy.takeFirst(); s.set_url(QUrl::fromLocalFile("foo.flac")); backend_->AddOrUpdateSongs(SongList() << s); //ASSERT_EQ(1, spy.count()); spy.takeFirst(); s.set_filesize(100); backend_->AddOrUpdateSongs(SongList() << s); //ASSERT_EQ(1, spy.count()); spy.takeFirst(); s.set_mtime(100); backend_->AddOrUpdateSongs(SongList() << s); //ASSERT_EQ(1, spy.count()); spy.takeFirst(); s.set_ctime(100); backend_->AddOrUpdateSongs(SongList() << s); ASSERT_EQ(0, spy.count()); } TEST_F(CollectionBackendTest, GetAlbumArtNonExistent) { } // Test adding a single song to the database, then getting various information back about it. class SingleSong : public CollectionBackendTest { protected: void SetUp() override { CollectionBackendTest::SetUp(); // Add a directory - this will get ID 1 backend_->AddDirectory("/tmp"); // Make a song in that directory song_ = MakeDummySong(1); song_.set_title("Title"); song_.set_artist("Artist"); song_.set_album("Album"); song_.set_url(QUrl::fromLocalFile("foo.flac")); } void AddDummySong() { QSignalSpy added_spy(backend_.get(), SIGNAL(SongsDiscovered(SongList))); QSignalSpy deleted_spy(backend_.get(), SIGNAL(SongsDeleted(SongList))); // Add the song backend_->AddOrUpdateSongs(SongList() << song_); // Check the correct signals were emitted EXPECT_EQ(0, deleted_spy.count()); ASSERT_EQ(1, added_spy.count()); SongList list = *(reinterpret_cast(added_spy[0][0].data())); ASSERT_EQ(1, list.count()); EXPECT_EQ(song_.title(), list[0].title()); EXPECT_EQ(song_.artist(), list[0].artist()); EXPECT_EQ(song_.album(), list[0].album()); EXPECT_EQ(1, list[0].id()); EXPECT_EQ(1, list[0].directory_id()); } Song song_; }; TEST_F(SingleSong, GetSongWithNoAlbum) { song_.set_album(""); AddDummySong(); if (HasFatalFailure()) return; EXPECT_EQ(1, backend_->GetAllArtists().size()); CollectionBackend::AlbumList albums = backend_->GetAllAlbums(); //EXPECT_EQ(1, albums.size()); //EXPECT_EQ("Artist", albums[0].artist); //EXPECT_EQ("", albums[0].album_name); } TEST_F(SingleSong, GetAllArtists) { AddDummySong(); if (HasFatalFailure()) return; QStringList artists = backend_->GetAllArtists(); ASSERT_EQ(1, artists.size()); EXPECT_EQ(song_.artist(), artists[0]); } TEST_F(SingleSong, GetAllAlbums) { AddDummySong(); if (HasFatalFailure()) return; CollectionBackend::AlbumList albums = backend_->GetAllAlbums(); ASSERT_EQ(1, albums.size()); EXPECT_EQ(song_.album(), albums[0].album_name); EXPECT_EQ(song_.artist(), albums[0].artist); } TEST_F(SingleSong, GetAlbumsByArtist) { AddDummySong(); if (HasFatalFailure()) return; CollectionBackend::AlbumList albums = backend_->GetAlbumsByArtist("Artist"); ASSERT_EQ(1, albums.size()); EXPECT_EQ(song_.album(), albums[0].album_name); EXPECT_EQ(song_.artist(), albums[0].artist); } TEST_F(SingleSong, GetAlbumArt) { AddDummySong(); if (HasFatalFailure()) return; CollectionBackend::Album album = backend_->GetAlbumArt("Artist", "AlbumArtist", "Album"); EXPECT_EQ(song_.album(), album.album_name); EXPECT_EQ(song_.artist(), album.artist); } TEST_F(SingleSong, GetSongs) { AddDummySong(); if (HasFatalFailure()) return; SongList songs = backend_->GetSongs("Artist", "Album"); ASSERT_EQ(1, songs.size()); EXPECT_EQ(song_.album(), songs[0].album()); EXPECT_EQ(song_.artist(), songs[0].artist()); EXPECT_EQ(song_.title(), songs[0].title()); EXPECT_EQ(1, songs[0].id()); } TEST_F(SingleSong, GetSongById) { AddDummySong(); if (HasFatalFailure()) return; Song song = backend_->GetSongById(1); EXPECT_EQ(song_.album(), song.album()); EXPECT_EQ(song_.artist(), song.artist()); EXPECT_EQ(song_.title(), song.title()); EXPECT_EQ(1, song.id()); } TEST_F(SingleSong, FindSongsInDirectory) { AddDummySong(); if (HasFatalFailure()) return; SongList songs = backend_->FindSongsInDirectory(1); ASSERT_EQ(1, songs.size()); EXPECT_EQ(song_.album(), songs[0].album()); EXPECT_EQ(song_.artist(), songs[0].artist()); EXPECT_EQ(song_.title(), songs[0].title()); EXPECT_EQ(1, songs[0].id()); } TEST_F(SingleSong, UpdateSong) { AddDummySong(); if (HasFatalFailure()) return; Song new_song(song_); new_song.set_id(1); new_song.set_title("A different title"); QSignalSpy deleted_spy(backend_.get(), SIGNAL(SongsDeleted(SongList))); QSignalSpy added_spy(backend_.get(), SIGNAL(SongsDiscovered(SongList))); backend_->AddOrUpdateSongs(SongList() << new_song); ASSERT_EQ(1, added_spy.size()); ASSERT_EQ(1, deleted_spy.size()); SongList songs_added = *(reinterpret_cast(added_spy[0][0].data())); SongList songs_deleted = *(reinterpret_cast(deleted_spy[0][0].data())); ASSERT_EQ(1, songs_added.size()); ASSERT_EQ(1, songs_deleted.size()); EXPECT_EQ("Title", songs_deleted[0].title()); EXPECT_EQ("A different title", songs_added[0].title()); EXPECT_EQ(1, songs_deleted[0].id()); EXPECT_EQ(1, songs_added[0].id()); } TEST_F(SingleSong, DeleteSongs) { AddDummySong(); if (HasFatalFailure()) return; Song new_song(song_); new_song.set_id(1); QSignalSpy deleted_spy(backend_.get(), SIGNAL(SongsDeleted(SongList))); backend_->DeleteSongs(SongList() << new_song); ASSERT_EQ(1, deleted_spy.size()); SongList songs_deleted = *(reinterpret_cast(deleted_spy[0][0].data())); ASSERT_EQ(1, songs_deleted.size()); EXPECT_EQ("Title", songs_deleted[0].title()); EXPECT_EQ(1, songs_deleted[0].id()); // Check we can't retrieve that song any more Song song = backend_->GetSongById(1); EXPECT_FALSE(song.is_valid()); EXPECT_EQ(-1, song.id()); // And the artist or album shouldn't show up either QStringList artists = backend_->GetAllArtists(); EXPECT_EQ(0, artists.size()); CollectionBackend::AlbumList albums = backend_->GetAllAlbums(); EXPECT_EQ(0, albums.size()); } TEST_F(SingleSong, MarkSongsUnavailable) { AddDummySong(); if (HasFatalFailure()) return; Song new_song(song_); new_song.set_id(1); QSignalSpy deleted_spy(backend_.get(), SIGNAL(SongsDeleted(SongList))); backend_->MarkSongsUnavailable(SongList() << new_song); ASSERT_EQ(1, deleted_spy.size()); SongList songs_deleted = *(reinterpret_cast(deleted_spy[0][0].data())); ASSERT_EQ(1, songs_deleted.size()); EXPECT_EQ("Title", songs_deleted[0].title()); EXPECT_EQ(1, songs_deleted[0].id()); // Check the song is marked as deleted. Song song = backend_->GetSongById(1); EXPECT_TRUE(song.is_valid()); EXPECT_TRUE(song.is_unavailable()); // And the artist or album shouldn't show up either QStringList artists = backend_->GetAllArtists(); EXPECT_EQ(0, artists.size()); CollectionBackend::AlbumList albums = backend_->GetAllAlbums(); EXPECT_EQ(0, albums.size()); } TEST_F(SingleSong, TestUrls) { QStringList strings = QStringList() << "file:///mnt/music/01 - Pink Floyd - Echoes.flac" << "file:///mnt/music/02 - Björn Afzelius - Det räcker nu.flac" << "file:///mnt/music/03 - Vazelina Bilopphøggers - Bomull i øra.flac" << "file:///mnt/music/Test !#$%&'()-@^_`{}~..flac"; QList urls = QUrl::fromStringList(strings); for (const QUrl &url : urls) { QString str = url.toString(QUrl::FullyEncoded); QUrl test_url = QUrl::fromEncoded(str.toUtf8()); EXPECT_EQ(url, test_url); Song song(Song::Source_Collection); song.set_directory_id(1); song.set_title("Test Title"); song.set_album("Test Album"); song.set_artist("Test Artist"); song.set_url(url); song.set_length_nanosec(kNsecPerSec); song.set_mtime(1); song.set_ctime(1); song.set_filesize(1); song.set_valid(true); backend_->AddOrUpdateSongs(SongList() << song); if (HasFatalFailure()) continue; SongList songs = backend_->GetSongsByUrl(url); EXPECT_EQ(1, songs.count()); if (songs.count() < 1) continue; Song new_song = songs.first(); EXPECT_TRUE(new_song.is_valid()); EXPECT_EQ(new_song.url(), url); new_song = backend_->GetSongByUrl(url); EXPECT_EQ(1, songs.count()); if (songs.count() < 1) continue; EXPECT_TRUE(new_song.is_valid()); EXPECT_EQ(new_song.url(), url); QSqlDatabase db(database_.get()->Connect()); QSqlQuery q(db); q.prepare(QString("SELECT url FROM %1 WHERE url = :url").arg(SCollection::kSongsTable)); q.bindValue(":url", url.toString(QUrl::FullyEncoded)); q.exec(); while (q.next()) { EXPECT_EQ(url, q.value(0).toUrl()); EXPECT_EQ(url, QUrl::fromEncoded(q.value(0).toByteArray())); } } } } // namespace