Add tests (#193)

This commit is contained in:
Jonas Kvinge 2019-06-28 01:33:22 +02:00 committed by GitHub
parent b9f4407815
commit 60bd90848b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 3156 additions and 7 deletions

View File

@ -395,6 +395,7 @@ endif()
# Subdirectories
add_subdirectory(src)
add_subdirectory(dist)
add_subdirectory(tests)
add_subdirectory(ext/libstrawberry-common)
add_subdirectory(ext/libstrawberry-tagreader)
add_subdirectory(ext/strawberry-tagreader)

View File

@ -106,7 +106,8 @@ CollectionModel::CollectionModel(CollectionBackend *backend, Application *app, Q
cover_loader_options_.pad_output_image_ = true;
cover_loader_options_.scale_output_image_ = true;
connect(app_->album_cover_loader(), SIGNAL(ImageLoaded(quint64, QImage)), SLOT(AlbumArtLoaded(quint64, QImage)));
if (app_)
connect(app_->album_cover_loader(), SIGNAL(ImageLoaded(quint64, QImage)), SLOT(AlbumArtLoaded(quint64, QImage)));
//icon_cache_->setCacheDirectory(Utilities::GetConfigPath(Utilities::Path_CacheRoot) + "/pixmapcache");
//icon_cache_->setMaximumCacheSize(CollectionModel::kIconCacheSize);
@ -172,7 +173,8 @@ void CollectionModel::Init(bool async) {
endResetModel();
// Show a loading indicator in the status bar too.
init_task_id_ = app_->task_manager()->StartTask(tr("Loading songs"));
if (app_)
init_task_id_ = app_->task_manager()->StartTask(tr("Loading songs"));
ResetAsync();
}
@ -794,7 +796,8 @@ void CollectionModel::ResetAsyncQueryFinished(QFuture<CollectionModel::QueryResu
PostQuery(root_, result, false);
if (init_task_id_ != -1) {
app_->task_manager()->SetTaskFinished(init_task_id_);
if (app_)
app_->task_manager()->SetTaskFinished(init_task_id_);
init_task_id_ = -1;
}

View File

@ -69,7 +69,8 @@
using std::shared_ptr;
using std::stable_sort;
const char *OrganiseDialog::kDefaultFormat = "%albumartist/%album{ (Disc %disc)}/{%track - }%albumartist - %album - %title.%extension";
const char *OrganiseDialog::kDefaultFormat = "%albumartist/%album{ (Disc %disc)}/{%track - }{%albumartist - }%album{ (Disc %disc)} - %title.%extension";
const char *OrganiseDialog::kSettingsGroup = "OrganiseDialog";
OrganiseDialog::OrganiseDialog(TaskManager *task_manager, CollectionBackend *backend, QWidget *parent)

View File

@ -59,6 +59,8 @@ class OrganiseDialog : public QDialog {
OrganiseDialog(TaskManager *task_manager, CollectionBackend *backend = nullptr, QWidget *parent = nullptr);
~OrganiseDialog();
static const char *kDefaultFormat;
QSize sizeHint() const;
void SetDestinationModel(QAbstractItemModel *model, bool devices = false);
@ -71,6 +73,8 @@ class OrganiseDialog : public QDialog {
void SetCopy(bool copy);
static Organise::NewSongInfoList ComputeNewSongsFilenames(const SongList &songs, const OrganiseFormat &format);
signals:
void FileCopied(int);
@ -97,10 +101,7 @@ class OrganiseDialog : public QDialog {
SongList LoadSongsBlocking(const QStringList &filenames);
void SetLoadingSongs(bool loading);
static Organise::NewSongInfoList ComputeNewSongsFilenames(const SongList &songs, const OrganiseFormat &format);
private:
static const char *kDefaultFormat;
static const char *kSettingsGroup;
Ui_OrganiseDialog *ui_;

99
tests/CMakeLists.txt Normal file
View File

@ -0,0 +1,99 @@
cmake_minimum_required(VERSION 2.8.11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -fpermissive -Wno-c++11-narrowing -U__STRICT_ANSI__")
find_package(Qt5 ${QT_MIN_VERSION} COMPONENTS Test)
find_package(GTest)
find_library(GMOCK_LIBRARY gmock)
if(Qt5Test_FOUND AND GTEST_FOUND AND GMOCK_LIBRARY)
include_directories(${GTEST_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS} ${QT_QTTEST_INCLUDE_DIR})
include_directories(${TAGLIB_INCLUDE_DIRS})
if(HAVE_GSTREAMER)
link_directories(${GSTREAMER_LIBRARY_DIRS})
include_directories(${GSTREAMER_INCLUDE_DIRS})
include_directories(${GSTREAMER_APP_INCLUDE_DIRS})
include_directories(${GSTREAMER_AUDIO_INCLUDE_DIRS})
include_directories(${GSTREAMER_BASE_INCLUDE_DIRS})
include_directories(${GSTREAMER_TAG_INCLUDE_DIRS})
endif()
include_directories(${CMAKE_SOURCE_DIR}/src)
include_directories(${CMAKE_BINARY_DIR}/src)
include_directories(${CMAKE_SOURCE_DIR}/tests/src)
include_directories(${CMAKE_BINARY_DIR}/tests/src)
include_directories(${CMAKE_SOURCE_DIR}/ext/strawberry-tagreader)
include_directories(${CMAKE_SOURCE_DIR}/ext/libstrawberry-common)
include_directories(${CMAKE_SOURCE_DIR}/ext/libstrawberry-tagreader)
include_directories(${CMAKE_BINARY_DIR}/ext/libstrawberry-tagreader)
if(HAVE_LIBGPOD)
include_directories(${LIBGPOD_INCLUDE_DIRS})
endif(HAVE_LIBGPOD)
add_definitions(-DGTEST_USE_OWN_TR1_TUPLE=1)
set(TESTUTILS-SOURCES
src/mock_networkaccessmanager.cpp
src/mock_playlistitem.cpp
src/test_utils.cpp
src/testobjectdecorators.cpp
)
set(TESTUTILS-MOC-HEADERS src/mock_networkaccessmanager.h src/test_utils.h src/testobjectdecorators.h)
qt5_wrap_cpp(TESTUTILS-SOURCES-MOC ${TESTUTILS-MOC-HEADERS})
add_library(test_utils STATIC EXCLUDE_FROM_ALL ${TESTUTILS-SOURCES} ${TESTUTILS-SOURCES-MOC})
target_link_libraries(test_utils ${GTEST_BOTH_LIBRARIES} ${GMOCK_LIBRARY} ${QT_LIBRARIES} ${Qt5Test_LIBRARIES})
add_custom_target(strawberry_test echo "Running Strawberry tests" WORKING_DIRECTORY ${CURRENT_BINARY_DIR})
add_custom_target(build_tests WORKING_DIRECTORY ${CURRENT_BINARY_DIR})
add_dependencies(strawberry_test build_tests)
qt5_add_resources(TEST-RESOURCE-SOURCES data/testdata.qrc)
add_library(test_gui_main STATIC EXCLUDE_FROM_ALL ${TEST-RESOURCE-SOURCES} src/main.cpp)
target_link_libraries(test_gui_main strawberry_lib)
set_target_properties(test_gui_main PROPERTIES COMPILE_DEFINITIONS GUI)
add_library(test_main STATIC EXCLUDE_FROM_ALL ${TEST-RESOURCE-SOURCES} src/main.cpp)
target_link_libraries(test_main strawberry_lib)
# Given a file foo_test.cpp, creates a target foo_test and adds it to the test target.
macro(add_test_file test_source gui_required)
get_filename_component(TEST_NAME ${test_source} NAME_WE)
add_executable(${TEST_NAME}
EXCLUDE_FROM_ALL
${test_source}
)
target_link_libraries(${TEST_NAME} ${GMOCK_LIBRARY} strawberry_lib test_utils Qt5::Test)
set(GUI_REQUIRED ${gui_required})
if (GUI_REQUIRED)
target_link_libraries(${TEST_NAME} test_gui_main)
else (GUI_REQUIRED)
target_link_libraries(${TEST_NAME} test_main)
endif (GUI_REQUIRED)
check_cxx_compiler_flag("-Wno-bool-conversions" SUPPORTS_NOBOOL)
if (SUPPORTS_NOBOOL)
set_target_properties(${TEST_NAME} PROPERTIES COMPILE_FLAGS "-Wno-bool-conversions")
endif (SUPPORTS_NOBOOL)
add_custom_command(TARGET strawberry_test POST_BUILD
COMMAND ./${TEST_NAME}${CMAKE_EXECUTABLE_SUFFIX})
add_dependencies(build_tests ${TEST_NAME})
endmacro (add_test_file)
add_test_file(src/utilities_test.cpp false)
add_test_file(src/concurrentrun_test.cpp false)
add_test_file(src/closure_test.cpp false)
add_test_file(src/mergedproxymodel_test.cpp false)
add_test_file(src/sqlite_test.cpp false)
add_test_file(src/song_test.cpp false)
add_test_file(src/collectionbackend_test.cpp false)
add_test_file(src/collectionmodel_test.cpp true)
add_test_file(src/playlist_test.cpp true)
add_test_file(src/songplaylistitem_test.cpp false)
add_test_file(src/organiseformat_test.cpp false)
endif() # Qt5Test_FOUND AND GTEST_FOUND AND GMOCK_LIBRARY

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

15
tests/data/testdata.qrc Normal file
View File

@ -0,0 +1,15 @@
<RCC>
<qresource prefix="/">
<file>audio/strawberry.wav</file>
<file>audio/strawberry.flac</file>
<file>audio/strawberry.wv</file>
<file>audio/strawberry.oga</file>
<file>audio/strawberry.opus</file>
<file>audio/strawberry.spx</file>
<file>audio/strawberry.aif</file>
<file>audio/strawberry.asf</file>
<file>audio/strawberry.mp3</file>
<file>audio/strawberry.m4a</file>
<file>audio/strawberry.mp4</file>
</qresource>
</RCC>

148
tests/src/closure_test.cpp Normal file
View File

@ -0,0 +1,148 @@
#include "config.h"
#include <functional>
#include <memory>
#include <gtest/gtest.h>
#include <QObject>
#include <QPointer>
#include <QSharedPointer>
#include <QSignalSpy>
#include "core/closure.h"
#include "test_utils.h"
TEST(ClosureTest, ClosureInvokesReceiver) {
TestQObject sender;
TestQObject receiver;
_detail::ClosureBase* closure = NewClosure(&sender, SIGNAL(Emitted()), &receiver, SLOT(Invoke()));
EXPECT_EQ(0, receiver.invoked());
sender.Emit();
EXPECT_EQ(1, receiver.invoked());
}
TEST(ClosureTest, ClosureDeletesSelf) {
TestQObject sender;
TestQObject receiver;
_detail::ClosureBase* closure = NewClosure(&sender, SIGNAL(Emitted()), &receiver, SLOT(Invoke()));
_detail::ObjectHelper* helper = closure->helper();
QSignalSpy spy(helper, SIGNAL(destroyed()));
EXPECT_EQ(0, receiver.invoked());
sender.Emit();
EXPECT_EQ(1, receiver.invoked());
EXPECT_EQ(0, spy.count());
QEventLoop loop;
QObject::connect(helper, SIGNAL(destroyed()), &loop, SLOT(quit()));
loop.exec();
EXPECT_EQ(1, spy.count());
}
TEST(ClosureTest, ClosureDoesNotCrashWithSharedPointerSender) {
TestQObject receiver;
TestQObject* sender;
std::unique_ptr<QSignalSpy> spy;
QPointer<_detail::ObjectHelper> closure;
{
QSharedPointer<TestQObject> sender_shared(new TestQObject);
sender = sender_shared.data();
closure = QPointer<_detail::ObjectHelper>(NewClosure(sender_shared, SIGNAL(Emitted()), &receiver, SLOT(Invoke()))->helper());
spy.reset(new QSignalSpy(sender, SIGNAL(destroyed())));
}
ASSERT_EQ(0, receiver.invoked());
sender->Emit();
ASSERT_EQ(1, receiver.invoked());
ASSERT_EQ(0, spy->count());
QEventLoop loop;
QObject::connect(sender, SIGNAL(destroyed()), &loop, SLOT(quit()));
loop.exec();
ASSERT_EQ(1, spy->count());
EXPECT_TRUE(closure.isNull());
}
namespace {
void Foo(bool* called, int question, int* answer) {
*called = true;
*answer = question;
}
} // namespace
TEST(ClosureTest, ClosureWorksWithFunctionPointers) {
TestQObject sender;
bool called = false;
int question = 42;
int answer = 0;
NewClosure(&sender, SIGNAL(Emitted()), &Foo, &called, question, &answer);
EXPECT_FALSE(called);
sender.Emit();
EXPECT_TRUE(called);
EXPECT_EQ(question, answer);
}
TEST(ClosureTest, ClosureWorksWithStandardFunctions) {
TestQObject sender;
bool called = false;
int question = 42;
int answer = 0;
std::function<void(bool*,int,int*)> callback(&Foo);
NewClosure(&sender, SIGNAL(Emitted()), callback, &called, question, &answer);
EXPECT_FALSE(called);
sender.Emit();
EXPECT_TRUE(called);
EXPECT_EQ(question, answer);
}
namespace {
class Bar {
public:
explicit Bar(int a) : foo_(a) {}
bool Foo(int* answer) {
*answer = foo_;
return true;
}
private:
int foo_;
};
}
TEST(ClosureTest, ClosureWorksWithMemberFunctionPointers) {
TestQObject sender;
Bar receiver(42);
int q = 1;
NewClosure(&sender, SIGNAL(Emitted()), &receiver, &Bar::Foo, &q);
EXPECT_EQ(1, q);
sender.Emit();
EXPECT_EQ(42, q);
}
TEST(ClosureTest, ClosureCallsLambda) {
TestQObject sender;
bool called = false;
NewClosure(&sender, SIGNAL(Emitted()), [&called] () { called = true; });
EXPECT_FALSE(called);
sender.Emit();
EXPECT_TRUE(called);
}

View File

@ -0,0 +1,370 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include <memory>
#include <gtest/gtest.h>
#include <QFileInfo>
#include <QSignalSpy>
#include <QThread>
#include <QtDebug>
#include "test_utils.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:
virtual void SetUp() {
database_.reset(new MemoryDatabase(nullptr));
backend_.reset(new CollectionBackend);
backend_->Init(database_.get(), 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> database_;
std::unique_ptr<CollectionBackend> 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<Directory>();
EXPECT_EQ(QFileInfo("/tmp").canonicalFilePath(), dir.path);
EXPECT_EQ(1, dir.id);
EXPECT_EQ(0, spy[0][1].value<SubdirectoryList>().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<Directory>();
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:
virtual void SetUp() {
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<SongList*>(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<SongList*>(added_spy[0][0].data()));
SongList songs_deleted = *(reinterpret_cast<SongList*>(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<SongList*>(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 retreive 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<SongList*>(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());
}
} // namespace

View File

@ -0,0 +1,335 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include <memory>
#include <gtest/gtest.h>
#include <QString>
#include <QUrl>
#include <QThread>
#include <QSignalSpy>
#include <QSortFilterProxyModel>
#include <QtDebug>
#include "test_utils.h"
#include "core/database.h"
#include "collection/collectionmodel.h"
#include "collection/collectionbackend.h"
#include "collection/collection.h"
namespace {
class CollectionModelTest : public ::testing::Test {
protected:
void SetUp() {
database_.reset(new MemoryDatabase(nullptr));
backend_.reset(new CollectionBackend);
backend_->Init(database_.get(), SCollection::kSongsTable, SCollection::kDirsTable, SCollection::kSubdirsTable, SCollection::kFtsTable);
model_.reset(new CollectionModel(backend_.get(), nullptr));
added_dir_ = false;
model_sorted_.reset(new QSortFilterProxyModel);
model_sorted_->setSourceModel(model_.get());
model_sorted_->setSortRole(CollectionModel::Role_SortText);
model_sorted_->setDynamicSortFilter(true);
model_sorted_->sort(0);
}
Song AddSong(Song& song) {
song.set_directory_id(1);
if (song.mtime() == -1) song.set_mtime(1);
if (song.ctime() == -1) song.set_ctime(1);
if (song.url().isEmpty()) song.set_url(QUrl("file:///tmp/foo"));
if (song.filesize() == -1) song.set_filesize(1);
if (!added_dir_) {
backend_->AddDirectory("/tmp");
added_dir_ = true;
}
backend_->AddOrUpdateSongs(SongList() << song);
return song;
}
Song AddSong(const QString& title, const QString& artist, const QString& album, int length) {
Song song;
song.Init(title, artist, album, length);
return AddSong(song);
}
std::shared_ptr<Database> database_;
std::unique_ptr<CollectionBackend> backend_;
std::unique_ptr<CollectionModel> model_;
std::unique_ptr<QSortFilterProxyModel> model_sorted_;
bool added_dir_;
};
TEST_F(CollectionModelTest, Initialisation) {
EXPECT_EQ(0, model_->rowCount(QModelIndex()));
}
TEST_F(CollectionModelTest, WithInitialArtists) {
AddSong("Title", "Artist 1", "Album", 123);
AddSong("Title", "Artist 2", "Album", 123);
AddSong("Title", "Foo", "Album", 123);
model_->Init(false);
ASSERT_EQ(5, model_sorted_->rowCount(QModelIndex()));
EXPECT_EQ("A", model_sorted_->index(0, 0, QModelIndex()).data().toString());
EXPECT_EQ("Artist 1", model_sorted_->index(1, 0, QModelIndex()).data().toString());
EXPECT_EQ("Artist 2", model_sorted_->index(2, 0, QModelIndex()).data().toString());
EXPECT_EQ("F", model_sorted_->index(3, 0, QModelIndex()).data().toString());
EXPECT_EQ("Foo", model_sorted_->index(4, 0, QModelIndex()).data().toString());
}
TEST_F(CollectionModelTest, CompilationAlbums) {
Song song;
song.Init("Title", "Artist", "Album", 123);
song.set_compilation(true);
AddSong(song);
model_->Init(false);
model_->fetchMore(model_->index(0, 0));
ASSERT_EQ(1, model_->rowCount(QModelIndex()));
QModelIndex va_index = model_->index(0, 0, QModelIndex());
EXPECT_EQ("Various artists", va_index.data().toString());
EXPECT_TRUE(model_->hasChildren(va_index));
ASSERT_EQ(model_->rowCount(va_index), 1);
QModelIndex album_index = model_->index(0, 0, va_index);
EXPECT_EQ(model_->data(album_index).toString(), "Album");
EXPECT_TRUE(model_->hasChildren(album_index));
}
TEST_F(CollectionModelTest, NumericHeaders) {
AddSong("Title", "1artist", "Album", 123);
AddSong("Title", "2artist", "Album", 123);
AddSong("Title", "0artist", "Album", 123);
AddSong("Title", "zartist", "Album", 123);
model_->Init(false);
ASSERT_EQ(6, model_sorted_->rowCount(QModelIndex()));
EXPECT_EQ("0-9", model_sorted_->index(0, 0, QModelIndex()).data().toString());
EXPECT_EQ("0artist", model_sorted_->index(1, 0, QModelIndex()).data().toString());
EXPECT_EQ("1artist", model_sorted_->index(2, 0, QModelIndex()).data().toString());
EXPECT_EQ("2artist", model_sorted_->index(3, 0, QModelIndex()).data().toString());
EXPECT_EQ("Z", model_sorted_->index(4, 0, QModelIndex()).data().toString());
EXPECT_EQ("zartist", model_sorted_->index(5, 0, QModelIndex()).data().toString());
}
TEST_F(CollectionModelTest, MixedCaseHeaders) {
AddSong("Title", "Artist", "Album", 123);
AddSong("Title", "artist", "Album", 123);
model_->Init(false);
ASSERT_EQ(3, model_sorted_->rowCount(QModelIndex()));
EXPECT_EQ("A", model_sorted_->index(0, 0, QModelIndex()).data().toString());
EXPECT_EQ("Artist", model_sorted_->index(1, 0, QModelIndex()).data().toString());
EXPECT_EQ("artist", model_sorted_->index(2, 0, QModelIndex()).data().toString());
}
TEST_F(CollectionModelTest, UnknownArtists) {
AddSong("Title", "", "Album", 123);
model_->Init(false);
model_->fetchMore(model_->index(0, 0));
ASSERT_EQ(1, model_->rowCount(QModelIndex()));
QModelIndex unknown_index = model_->index(0, 0, QModelIndex());
EXPECT_EQ("Unknown", unknown_index.data().toString());
ASSERT_EQ(1, model_->rowCount(unknown_index));
EXPECT_EQ("Album", model_->index(0, 0, unknown_index).data().toString());
}
TEST_F(CollectionModelTest, UnknownAlbums) {
AddSong("Title", "Artist", "", 123);
AddSong("Title", "Artist", "Album", 123);
model_->Init(false);
model_->fetchMore(model_->index(0, 0));
QModelIndex artist_index = model_->index(0, 0, QModelIndex());
ASSERT_EQ(2, model_->rowCount(artist_index));
QModelIndex unknown_album_index = model_->index(0, 0, artist_index);
QModelIndex real_album_index = model_->index(1, 0, artist_index);
EXPECT_EQ("Unknown", unknown_album_index.data().toString());
EXPECT_EQ("Album", real_album_index.data().toString());
}
TEST_F(CollectionModelTest, VariousArtistSongs) {
SongList songs;
for (int i=0 ; i < 4 ; ++i) {
QString n = QString::number(i+1);
Song song;
song.Init("Title " + n, "Artist " + n, "Album", 0);
songs << song;
}
// Different ways of putting songs in "Various Artist". Make sure they all work
songs[0].set_compilation_detected(true);
songs[1].set_compilation(true);
songs[2].set_compilation_on(true);
songs[3].set_compilation_detected(true); songs[3].set_artist("Various Artists");
for (int i=0 ; i<4 ; ++i)
AddSong(songs[i]);
model_->Init(false);
QModelIndex artist_index = model_->index(0, 0, QModelIndex());
model_->fetchMore(artist_index);
ASSERT_EQ(1, model_->rowCount(artist_index));
QModelIndex album_index = model_->index(0, 0, artist_index);
model_->fetchMore(album_index);
ASSERT_EQ(4, model_->rowCount(album_index));
EXPECT_EQ("Artist 1 - Title 1", model_->index(0, 0, album_index).data().toString());
EXPECT_EQ("Artist 2 - Title 2", model_->index(1, 0, album_index).data().toString());
EXPECT_EQ("Artist 3 - Title 3", model_->index(2, 0, album_index).data().toString());
EXPECT_EQ("Title 4", model_->index(3, 0, album_index).data().toString());
}
TEST_F(CollectionModelTest, RemoveSongsLazyLoaded) {
Song one = AddSong("Title 1", "Artist", "Album", 123); one.set_id(1);
Song two = AddSong("Title 2", "Artist", "Album", 123); two.set_id(2);
AddSong("Title 3", "Artist", "Album", 123);
model_->Init(false);
// Lazy load the items
QModelIndex artist_index = model_->index(0, 0, QModelIndex());
model_->fetchMore(artist_index);
ASSERT_EQ(1, model_->rowCount(artist_index));
QModelIndex album_index = model_->index(0, 0, artist_index);
model_->fetchMore(album_index);
ASSERT_EQ(3, model_->rowCount(album_index));
// Remove the first two songs
QSignalSpy spy_preremove(model_.get(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)));
QSignalSpy spy_remove(model_.get(), SIGNAL(rowsRemoved(QModelIndex,int,int)));
QSignalSpy spy_reset(model_.get(), SIGNAL(modelReset()));
backend_->DeleteSongs(SongList() << one << two);
ASSERT_EQ(2, spy_preremove.count());
ASSERT_EQ(2, spy_remove.count());
ASSERT_EQ(0, spy_reset.count());
artist_index = model_->index(0, 0, QModelIndex());
ASSERT_EQ(1, model_->rowCount(artist_index));
album_index = model_->index(0, 0, artist_index);
ASSERT_EQ(1, model_->rowCount(album_index));
EXPECT_EQ("Title 3", model_->index(0, 0, album_index).data().toString());
}
TEST_F(CollectionModelTest, RemoveSongsNotLazyLoaded) {
Song one = AddSong("Title 1", "Artist", "Album", 123); one.set_id(1);
Song two = AddSong("Title 2", "Artist", "Album", 123); two.set_id(2);
model_->Init(false);
// Remove the first two songs
QSignalSpy spy_preremove(model_.get(), SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)));
QSignalSpy spy_remove(model_.get(), SIGNAL(rowsRemoved(QModelIndex,int,int)));
QSignalSpy spy_reset(model_.get(), SIGNAL(modelReset()));
backend_->DeleteSongs(SongList() << one << two);
ASSERT_EQ(0, spy_preremove.count());
ASSERT_EQ(0, spy_remove.count());
ASSERT_EQ(1, spy_reset.count());
}
TEST_F(CollectionModelTest, RemoveEmptyAlbums) {
Song one = AddSong("Title 1", "Artist", "Album 1", 123); one.set_id(1);
Song two = AddSong("Title 2", "Artist", "Album 2", 123); two.set_id(2);
Song three = AddSong("Title 3", "Artist", "Album 2", 123); three.set_id(3);
model_->Init(false);
QModelIndex artist_index = model_->index(0, 0, QModelIndex());
model_->fetchMore(artist_index);
ASSERT_EQ(2, model_->rowCount(artist_index));
// Remove one song from each album
backend_->DeleteSongs(SongList() << one << two);
// Check the model
artist_index = model_->index(0, 0, QModelIndex());
model_->fetchMore(artist_index);
ASSERT_EQ(1, model_->rowCount(artist_index));
QModelIndex album_index = model_->index(0, 0, artist_index);
model_->fetchMore(album_index);
EXPECT_EQ("Album 2", album_index.data().toString());
ASSERT_EQ(1, model_->rowCount(album_index));
EXPECT_EQ("Title 3", model_->index(0, 0, album_index).data().toString());
}
TEST_F(CollectionModelTest, RemoveEmptyArtists) {
Song one = AddSong("Title", "Artist", "Album", 123); one.set_id(1);
model_->Init(false);
// Lazy load the items
QModelIndex artist_index = model_->index(0, 0, QModelIndex());
model_->fetchMore(artist_index);
ASSERT_EQ(1, model_->rowCount(artist_index));
QModelIndex album_index = model_->index(0, 0, artist_index);
model_->fetchMore(album_index);
ASSERT_EQ(1, model_->rowCount(album_index));
// The artist header is there too right?
ASSERT_EQ(2, model_->rowCount(QModelIndex()));
// Remove the song
backend_->DeleteSongs(SongList() << one);
// Everything should be gone - even the artist header
ASSERT_EQ(0, model_->rowCount(QModelIndex()));
}
} // namespace

View File

@ -0,0 +1,169 @@
#include <functional>
#include <gtest/gtest.h>
#include <QThreadPool>
#include <QEventLoop>
#include <QFutureWatcher>
#include "core/concurrentrun.h"
#include "test_utils.h"
int f() {
return 1337;
}
TEST(ConcurrentRunTest, ConcurrentRun0StartAndWait) {
QThreadPool threadpool;
QFuture<int> future = ConcurrentRun::Run<int>(&threadpool, &f);
QFutureWatcher<int> watcher;
watcher.setFuture(future);
QEventLoop loop;
QObject::connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
EXPECT_EQ(1337, watcher.result());
}
int g(int i) {
return ++i;
}
TEST(ConcurrentRunTest, ConcurrentRun1StartAndWait) {
QThreadPool threadpool;
int i = 1336;
QFuture<int> future = ConcurrentRun::Run<int, int>(&threadpool, &g, i);
QFutureWatcher<int> watcher;
watcher.setFuture(future);
QEventLoop loop;
QObject::connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
EXPECT_EQ(1337, watcher.result());
}
int max(int i, int j) {
return (i > j ? i : j);
}
TEST(ConcurrentRunTest, ConcurrentRun2StartAndWait) {
int i = 10;
int j = 42;
QThreadPool threadpool;
QFuture<int> future = ConcurrentRun::Run<int, int, int>(&threadpool, &max, i, j);
QFutureWatcher<int> watcher;
watcher.setFuture(future);
QEventLoop loop;
QObject::connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
EXPECT_EQ(42, watcher.result());
}
int sum(int a, int b, int c) {
return a + b + c;
}
TEST(ConcurrentRunTest, ConcurrentRun3StartAndWait) {
int i = 10;
int j = 42;
int k = 50;
QThreadPool threadpool;
QFuture<int> future = ConcurrentRun::Run<int, int, int, int>(&threadpool, &sum, i, j, k);
QFutureWatcher<int> watcher;
watcher.setFuture(future);
QEventLoop loop;
QObject::connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
EXPECT_EQ(102, watcher.result());
}
void aFunction(int* n) {
*n = 1337;
}
void bFunction(int* n, int *m) {
aFunction(n);
*m = 1338;
}
void cFunction(int* n, int *m, int *o) {
bFunction(n, m);
*o = 1339;
}
TEST(ConcurrentRunTest, ConcurrentRunVoidFunction1Start) {
QThreadPool threadpool;
int n = 10;
QFuture<void> future = ConcurrentRun::Run<void, int*>(&threadpool, &aFunction, &n);
QFutureWatcher<void> watcher;
watcher.setFuture(future);
QEventLoop loop;
QObject::connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
EXPECT_EQ(1337, n);
}
TEST(ConcurrentRunTest, ConcurrentRunVoidFunction2Start) {
QThreadPool threadpool;
int n = 10, m = 11;
QFuture<void> future = ConcurrentRun::Run<void, int*, int*>(&threadpool, &bFunction, &n, &m);
QFutureWatcher<void> watcher;
watcher.setFuture(future);
QEventLoop loop;
QObject::connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
EXPECT_EQ(1337, n);
EXPECT_EQ(1338, m);
}
TEST(ConcurrentRunTest, ConcurrentRunVoidFunction3Start) {
QThreadPool threadpool;
int n = 10, m = 11, o = 12;
QFuture<void> future = ConcurrentRun::Run<void, int*, int*, int*>(&threadpool, &cFunction, &n, &m, &o);
QFutureWatcher<void> watcher;
watcher.setFuture(future);
QEventLoop loop;
QObject::connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
EXPECT_EQ(1337, n);
EXPECT_EQ(1338, m);
EXPECT_EQ(1339, o);
}
class A {
public:
void f(int* i) {
*i = *i + 1;
}
};
TEST(ConcurrentRunTest, ConcurrentRunVoidBindFunctionStart) {
QThreadPool threadpool;
A a;
int nb = 10;
QFuture<void> future = ConcurrentRun::Run<void>(&threadpool, std::bind(&A::f, &a, &nb));
QFutureWatcher<void> watcher;
watcher.setFuture(future);
QEventLoop loop;
QObject::connect(&watcher, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec();
EXPECT_EQ(11, nb);
}

36
tests/src/logging_env.h Normal file
View File

@ -0,0 +1,36 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef LOGGING_ENV_H
#define LOGGING_ENV_H
#include <gtest/gtest.h>
#include "core/logging.h"
class LoggingEnvironment : public ::testing::Environment {
public:
void SetUp() {
logging::Init();
logging::SetLevels("*:4");
}
};
#endif // LOGGING_ENV_H

44
tests/src/main.cpp Normal file
View File

@ -0,0 +1,44 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include <gmock/gmock.h>
#include <QApplication>
#include "logging_env.h"
#include "metatypes_env.h"
#include "resources_env.h"
int main(int argc, char** argv) {
testing::InitGoogleMock(&argc, argv);
testing::AddGlobalTestEnvironment(new MetatypesEnvironment);
#ifdef GUI
QApplication a(argc, argv);
#else
QCoreApplication a(argc, argv);
#endif
testing::AddGlobalTestEnvironment(new ResourcesEnvironment);
testing::AddGlobalTestEnvironment(new LoggingEnvironment);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,181 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "gtest/gtest.h"
#include "test_utils.h"
#include "core/mergedproxymodel.h"
#include <QStandardItemModel>
#include <QSignalSpy>
class MergedProxyModelTest : public ::testing::Test {
protected:
void SetUp() {
merged_.setSourceModel(&source_);
}
QStandardItemModel source_;
MergedProxyModel merged_;
};
TEST_F(MergedProxyModelTest, Flat) {
source_.appendRow(new QStandardItem("one"));
source_.appendRow(new QStandardItem("two"));
ASSERT_EQ(2, merged_.rowCount(QModelIndex()));
QModelIndex one_i = merged_.index(0, 0, QModelIndex());
QModelIndex two_i = merged_.index(1, 0, QModelIndex());
EXPECT_EQ("one", one_i.data().toString());
EXPECT_EQ("two", two_i.data().toString());
EXPECT_FALSE(merged_.parent(one_i).isValid());
EXPECT_FALSE(merged_.hasChildren(one_i));
}
TEST_F(MergedProxyModelTest, Tree) {
QStandardItem* one = new QStandardItem("one");
QStandardItem* two = new QStandardItem("two");
source_.appendRow(one);
one->appendRow(two);
ASSERT_EQ(1, merged_.rowCount(QModelIndex()));
QModelIndex one_i = merged_.index(0, 0, QModelIndex());
ASSERT_EQ(1, merged_.rowCount(one_i));
QModelIndex two_i = merged_.index(0, 0, one_i);
EXPECT_EQ("one", one_i.data().toString());
EXPECT_EQ("two", two_i.data().toString());
EXPECT_EQ("one", two_i.parent().data().toString());
}
TEST_F(MergedProxyModelTest, Merged) {
source_.appendRow(new QStandardItem("one"));
QStandardItemModel submodel;
submodel.appendRow(new QStandardItem("two"));
merged_.AddSubModel(source_.index(0, 0, QModelIndex()), &submodel);
ASSERT_EQ(1, merged_.rowCount(QModelIndex()));
QModelIndex one_i = merged_.index(0, 0, QModelIndex());
EXPECT_EQ("one", merged_.data(one_i).toString());
EXPECT_TRUE(merged_.hasChildren(one_i));
ASSERT_EQ(1, merged_.rowCount(one_i));
QModelIndex two_i = merged_.index(0, 0, one_i);
EXPECT_EQ("two", merged_.data(two_i).toString());
EXPECT_EQ(0, merged_.rowCount(two_i));
EXPECT_FALSE(merged_.hasChildren(two_i));
}
TEST_F(MergedProxyModelTest, SourceInsert) {
QSignalSpy before_spy(&merged_, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)));
QSignalSpy after_spy(&merged_, SIGNAL(rowsInserted(QModelIndex,int,int)));
source_.appendRow(new QStandardItem("one"));
ASSERT_EQ(1, before_spy.count());
ASSERT_EQ(1, after_spy.count());
EXPECT_FALSE(before_spy[0][0].value<QModelIndex>().isValid());
EXPECT_EQ(0, before_spy[0][1].toInt());
EXPECT_EQ(0, before_spy[0][2].toInt());
EXPECT_FALSE(after_spy[0][0].value<QModelIndex>().isValid());
EXPECT_EQ(0, after_spy[0][1].toInt());
EXPECT_EQ(0, after_spy[0][2].toInt());
}
TEST_F(MergedProxyModelTest, SourceRemove) {
source_.appendRow(new QStandardItem("one"));
QSignalSpy before_spy(&merged_, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)));
QSignalSpy after_spy(&merged_, SIGNAL(rowsRemoved(QModelIndex,int,int)));
source_.removeRow(0, QModelIndex());
ASSERT_EQ(1, before_spy.count());
ASSERT_EQ(1, after_spy.count());
EXPECT_FALSE(before_spy[0][0].value<QModelIndex>().isValid());
EXPECT_EQ(0, before_spy[0][1].toInt());
EXPECT_EQ(0, before_spy[0][2].toInt());
EXPECT_FALSE(after_spy[0][0].value<QModelIndex>().isValid());
EXPECT_EQ(0, after_spy[0][1].toInt());
EXPECT_EQ(0, after_spy[0][2].toInt());
}
TEST_F(MergedProxyModelTest, SubInsert) {
source_.appendRow(new QStandardItem("one"));
QStandardItemModel submodel;
merged_.AddSubModel(source_.index(0, 0, QModelIndex()), &submodel);
QSignalSpy before_spy(&merged_, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)));
QSignalSpy after_spy(&merged_, SIGNAL(rowsInserted(QModelIndex,int,int)));
submodel.appendRow(new QStandardItem("two"));
ASSERT_EQ(1, before_spy.count());
ASSERT_EQ(1, after_spy.count());
EXPECT_EQ("one", before_spy[0][0].value<QModelIndex>().data());
EXPECT_EQ(0, before_spy[0][1].toInt());
EXPECT_EQ(0, before_spy[0][2].toInt());
EXPECT_EQ("one", after_spy[0][0].value<QModelIndex>().data());
EXPECT_EQ(0, after_spy[0][1].toInt());
EXPECT_EQ(0, after_spy[0][2].toInt());
}
TEST_F(MergedProxyModelTest, SubRemove) {
source_.appendRow(new QStandardItem("one"));
QStandardItemModel submodel;
merged_.AddSubModel(source_.index(0, 0, QModelIndex()), &submodel);
submodel.appendRow(new QStandardItem("two"));
QSignalSpy before_spy(&merged_, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)));
QSignalSpy after_spy(&merged_, SIGNAL(rowsRemoved(QModelIndex,int,int)));
submodel.removeRow(0, QModelIndex());
ASSERT_EQ(1, before_spy.count());
ASSERT_EQ(1, after_spy.count());
EXPECT_EQ("one", before_spy[0][0].value<QModelIndex>().data());
EXPECT_EQ(0, before_spy[0][1].toInt());
EXPECT_EQ(0, before_spy[0][2].toInt());
EXPECT_EQ("one", after_spy[0][0].value<QModelIndex>().data());
EXPECT_EQ(0, after_spy[0][1].toInt());
EXPECT_EQ(0, after_spy[0][2].toInt());
}

46
tests/src/metatypes_env.h Normal file
View File

@ -0,0 +1,46 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef METATYPES_ENV_H
#define METATYPES_ENV_H
#include <gtest/gtest.h>
#include <QMetaType>
#include <QModelIndex>
#include "core/song.h"
#include "core/songloader.h"
#include "collection/directory.h"
class MetatypesEnvironment : public ::testing::Environment {
public:
void SetUp() {
qRegisterMetaType<Directory>("Directory");
qRegisterMetaType<DirectoryList>("DirectoryList");
qRegisterMetaType<Subdirectory>("Subdirectory");
qRegisterMetaType<SubdirectoryList>("SubdirectoryList");
qRegisterMetaType<SongList>("SongList");
qRegisterMetaType<QModelIndex>("QModelIndex");
qRegisterMetaType<SongLoader::Result>("SongLoader::Result");
}
};
#endif // RESOURCES_ENV_H

View File

@ -0,0 +1,72 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef MOCKCOLLECTIONBACKEND_H
#define MOCKCOLLECTIONBACKEND_H
#include <gmock/gmock.h>
#include "collection/collectionbackend.h"
class MockCollectionBackend : public CollectionBackendInterface {
public:
MOCK_CONST_METHOD0(songs_table, QString());
// Get a list of directories in the collection. Emits DirectoriesDiscovered.
MOCK_METHOD0(LoadDirectoriesAsync, void());
// Counts the songs in the collection.
MOCK_METHOD0(UpdateTotalSongCountAsync, void());
MOCK_METHOD0(UpdateTotalAlbumCountAsync, void());
MOCK_METHOD0(UpdateTotalArtistCountAsync, void());
MOCK_METHOD1(FindSongsInDirectory, SongList(int));
MOCK_METHOD1(SubdirsInDirectory, SubdirectoryList(int));
MOCK_METHOD0(GetAllDirectories, DirectoryList());
MOCK_METHOD3(ChangeDirPath, void(int, const QString&, const QString&));
MOCK_METHOD1(GetAllArtists, QStringList(const QueryOptions&));
MOCK_METHOD1(GetAllArtistsWithAlbums, QStringList(const QueryOptions&));
MOCK_METHOD3(GetSongs, SongList(const QString&, const QString&, const QueryOptions&));
MOCK_METHOD2(GetCompilationSongs, SongList(const QString&, const QueryOptions&));
MOCK_METHOD1(GetAllAlbums, AlbumList(const QueryOptions&));
MOCK_METHOD2(GetAlbumsByArtist, AlbumList(const QString&, const QueryOptions&));
MOCK_METHOD1(GetCompilationAlbums, AlbumList(const QueryOptions&));
MOCK_METHOD4(UpdateManualAlbumArtAsync, void(const QString&, const QString&, const QString&, const QString&));
MOCK_METHOD3(GetAlbumArt, Album(const QString&, const QString&, const QString&));
MOCK_METHOD1(GetSongById, Song(int));
MOCK_METHOD1(GetSongsByUrl, SongList(const QUrl&));
MOCK_METHOD2(GetSongByUrl, Song(const QUrl&, qint64));
MOCK_METHOD1(AddDirectory, void(const QString&));
MOCK_METHOD1(RemoveDirectory, void(const Directory&));
MOCK_METHOD1(ExecQuery, bool(CollectionQuery*));
MOCK_METHOD2(GetSongsByAlbum, SongList(const QString&, const QueryOptions&));
};
#endif // MOCKCOLLECTIONBACKEND_H

View File

@ -0,0 +1,137 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "mock_networkaccessmanager.h"
#include <algorithm>
#include <QtGlobal>
#include <QMap>
#include <QByteArray>
#include <QString>
#include <QIODevice>
#include <QNetworkRequest>
#include <QUrlQuery>
#include <QtDebug>
using std::min;
using ::testing::MakeMatcher;
using ::testing::Matcher;
using ::testing::MatcherInterface;
using ::testing::MatchResultListener;
using ::testing::Return;
class RequestForUrlMatcher : public MatcherInterface<const QNetworkRequest&> {
public:
RequestForUrlMatcher(const QString& contains, const QMap<QString, QString>& expected_params)
: contains_(contains), expected_params_(expected_params) {}
virtual ~RequestForUrlMatcher() {}
virtual bool Matches(const QNetworkRequest& req) const {
const QUrl& url = req.url();
if (!url.toString().contains(contains_)) {
return false;
}
QUrlQuery url_query(url);
for (QMap<QString, QString>::const_iterator it = expected_params_.constBegin(); it != expected_params_.constEnd(); ++it) {
if (!url_query.hasQueryItem(it.key()) || url_query.queryItemValue(it.key()) != it.value()) {
return false;
}
}
return true;
}
virtual bool MatchAndExplain(const QNetworkRequest& req, MatchResultListener* listener) const {
*listener << "which is " << req.url().toString().toUtf8().constData();
return Matches(req);
}
virtual void DescribeTo(::std::ostream* os) const {
*os << "matches url";
}
private:
QString contains_;
QMap<QString, QString> expected_params_;
};
inline Matcher<const QNetworkRequest&> RequestForUrl(const QString& contains, const QMap<QString, QString>& params) {
return MakeMatcher(new RequestForUrlMatcher(contains, params));
}
MockNetworkReply* MockNetworkAccessManager::ExpectGet(const QString& contains, const QMap<QString, QString>& expected_params, int status, const QByteArray& data) {
MockNetworkReply* reply = new MockNetworkReply(data);
reply->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, status);
EXPECT_CALL(*this, createRequest(GetOperation, RequestForUrl(contains, expected_params), nullptr)). WillOnce(Return(reply));
return reply;
}
MockNetworkReply::MockNetworkReply()
: data_(nullptr) {
}
MockNetworkReply::MockNetworkReply(const QByteArray& data)
: data_(data),
pos_(0) {
}
void MockNetworkReply::SetData(const QByteArray& data) {
data_ = data;
pos_ = 0;
}
qint64 MockNetworkReply::readData(char* data, qint64 size) {
if (data_.size() == pos_) {
return -1;
}
qint64 bytes_to_read = min(data_.size() - pos_, size);
memcpy(data, data_.constData() + pos_, bytes_to_read);
pos_ += bytes_to_read;
return bytes_to_read;
}
qint64 MockNetworkReply::writeData(const char* data, qint64) {
ADD_FAILURE() << "Something tried to write to a QNetworkReply";
return -1;
}
void MockNetworkReply::Done() {
setOpenMode(QIODevice::ReadOnly);
emit finished();
}
void MockNetworkReply::setAttribute(QNetworkRequest::Attribute code, const QVariant& value) {
QNetworkReply::setAttribute(code, value);
}

View File

@ -0,0 +1,74 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef MOCK_NETWORKACCESSMANAGER_H
#define MOCK_NETWORKACCESSMANAGER_H
#include <QtGlobal>
#include <QMap>
#include <QByteArray>
#include <QUrl>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include "test_utils.h"
#include "gmock/gmock.h"
// Usage:
// Create a MockNetworkAccessManager.
// Call ExpectGet() with appropriate expectations and the data you want back.
// This will return a MockNetworkReply*. When you are ready for the reply to arrive, call MockNetworkReply::Done().
class MockNetworkReply : public QNetworkReply {
Q_OBJECT
public:
MockNetworkReply();
MockNetworkReply(const QByteArray& data);
// Use these to set expectations.
void SetData(const QByteArray& data);
virtual void setAttribute(QNetworkRequest::Attribute code, const QVariant& value);
// Call this when you are ready for the finished() signal.
void Done();
protected:
MOCK_METHOD0(abort, void());
virtual qint64 readData(char* data, qint64);
virtual qint64 writeData(const char* data, qint64);
QByteArray data_;
qint64 pos_;
};
class MockNetworkAccessManager : public QNetworkAccessManager {
Q_OBJECT
public:
MockNetworkReply* ExpectGet(
const QString& contains, // A string that should be present in the URL.
const QMap<QString, QString>& params, // Required URL parameters.
int status, // Returned HTTP status code.
const QByteArray& ret_data); // Returned data.
protected:
MOCK_METHOD3(createRequest, QNetworkReply*(Operation, const QNetworkRequest&, QIODevice*));
};
#endif // MOCK_NETWORKACCESSMANAGER_H

View File

@ -0,0 +1,28 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "core/song.h"
#include "mock_playlistitem.h"
using ::testing::_;
using ::testing::Return;
MockPlaylistItem::MockPlaylistItem() : PlaylistItem(Song::Source_LocalFile) {}

View File

@ -0,0 +1,50 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef MOCK_PLAYLISTITEM_H
#define MOCK_PLAYLISTITEM_H
#include <gmock/gmock.h>
#include <QVariant>
#include <QUrl>
#include "core/song.h"
#include "core/settingsprovider.h"
#include "collection/sqlrow.h"
#include "playlist/playlistitem.h"
class MockPlaylistItem : public PlaylistItem {
public:
MockPlaylistItem();
MOCK_CONST_METHOD0(options, Options());
MOCK_METHOD1(InitFromQuery, bool(const SqlRow& settings));
MOCK_METHOD0(Reload, void());
MOCK_CONST_METHOD0(Metadata, Song());
MOCK_CONST_METHOD0(Url, QUrl());
MOCK_METHOD1(SetTemporaryMetadata, void(const Song& metadata));
MOCK_METHOD0(ClearTemporaryMetadata, void());
MOCK_METHOD1(DatabaseValue, QVariant(DatabaseColumn));
};
#endif // MOCK_PLAYLISTITEM_H

View File

@ -0,0 +1,54 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef MOCK_SETTINGSPROVIDER_H
#define MOCK_SETTINGSPROVIDER_H
#include "core/settingsprovider.h"
#include <gmock/gmock.h>
class MockSettingsProvider : public SettingsProvider {
public:
MOCK_METHOD1(set_group, void(const char* group));
MOCK_CONST_METHOD2(value, QVariant(const QString& key, const QVariant& default_value));
MOCK_METHOD2(setValue, void(const QString& key, const QVariant& value));
MOCK_METHOD1(beginReadArray, int(const QString& prefix));
MOCK_METHOD2(beginWriteArray, void(const QString& prefix, int size));
MOCK_METHOD1(setArrayIndex, void(int i));
MOCK_METHOD0(endArray, void());
};
class DummySettingsProvider : public SettingsProvider {
public:
DummySettingsProvider() {}
void set_group(const char *group) {}
QVariant value(const QString&, const QVariant& = QVariant()) const { return QVariant(); }
void setValue(const QString&, const QVariant&) {}
int beginReadArray(const QString&) { return 0; }
void beginWriteArray(const QString&, int = -1) {}
void setArrayIndex(int) {}
void endArray() {}
};
#endif // MOCK_SETTINGSPROVIDER_H

View File

@ -0,0 +1,173 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include <gtest/gtest.h>
#include "test_utils.h"
#include "organise/organiseformat.h"
#include "core/timeconstants.h"
#include "core/song.h"
#include "core/logging.h"
#include <QUrl>
class OrganiseFormatTest : public ::testing::Test {
protected:
OrganiseFormat format_;
Song song_;
};
TEST_F(OrganiseFormatTest, BasicReplace) {
song_.set_title("title");
song_.set_album("album");
song_.set_artist("artist");
song_.set_albumartist("albumartist");
song_.set_track(321);
song_.set_disc(789);
song_.set_year(2010);
song_.set_originalyear(1995);
song_.set_genre("genre");
song_.set_composer("composer");
song_.set_performer("performer");
song_.set_grouping("grouping");
song_.set_comment("comment");
song_.set_length_nanosec(987 * kNsecPerSec);
song_.set_samplerate(654);
song_.set_bitdepth(32);
song_.set_bitrate(123);
format_.set_format("%album %albumartist %artist %bitrate %comment %composer %performer %grouping %disc %genre %length %samplerate %bitdepth %title %track %year");
ASSERT_TRUE(format_.IsValid());
EXPECT_EQ("album_albumartist_artist_123_comment_composer_performer_grouping_789_genre_987_654_32_title_321_2010", format_.GetFilenameForSong(song_));
}
TEST_F(OrganiseFormatTest, Extension) {
song_.set_url(QUrl("file:///some/path/filename.flac"));
format_.set_format("%extension");
ASSERT_TRUE(format_.IsValid());
EXPECT_EQ("flac", format_.GetFilenameForSong(song_));
}
TEST_F(OrganiseFormatTest, ArtistInitial) {
song_.set_artist("bob");
format_.set_format("%artistinitial");
ASSERT_TRUE(format_.IsValid());
EXPECT_EQ("B", format_.GetFilenameForSong(song_));
}
TEST_F(OrganiseFormatTest, AlbumArtistInitial) {
song_.set_albumartist("bob");
format_.set_format("%artistinitial");
ASSERT_TRUE(format_.IsValid());
EXPECT_EQ("B", format_.GetFilenameForSong(song_));
}
TEST_F(OrganiseFormatTest, InvalidTag) {
format_.set_format("%invalid");
EXPECT_FALSE(format_.IsValid());
}
TEST_F(OrganiseFormatTest, Blocks) {
format_.set_format("Before{Inside%year}After");
ASSERT_TRUE(format_.IsValid());
song_.set_year(-1);
EXPECT_EQ("BeforeAfter", format_.GetFilenameForSong(song_));
song_.set_year(0);
EXPECT_EQ("BeforeAfter", format_.GetFilenameForSong(song_));
song_.set_year(123);
EXPECT_EQ("BeforeInside123After", format_.GetFilenameForSong(song_));
}
TEST_F(OrganiseFormatTest, ReplaceSpaces) {
song_.set_title("The Song Title");
format_.set_format("The Format String %title");
format_.set_replace_spaces(false);
EXPECT_EQ("The Format String The Song Title", format_.GetFilenameForSong(song_));
format_.set_replace_spaces(true);
EXPECT_EQ("The_Format_String_The_Song_Title", format_.GetFilenameForSong(song_));
}
TEST_F(OrganiseFormatTest, ReplaceNonAscii) {
song_.set_artist(QString::fromUtf8("Röyksopp"));
format_.set_format("%artist");
format_.set_remove_non_ascii(false);
EXPECT_EQ(QString::fromUtf8("Röyksopp"), format_.GetFilenameForSong(song_));
format_.set_remove_non_ascii(true);
EXPECT_EQ("Royksopp", format_.GetFilenameForSong(song_));
song_.set_artist(QString::fromUtf8("Владимир Высоцкий"));
EXPECT_EQ("_________________", format_.GetFilenameForSong(song_));
}
TEST_F(OrganiseFormatTest, TrackNumberPadding) {
format_.set_format("%track");
song_.set_track(9);
EXPECT_EQ("09", format_.GetFilenameForSong(song_));
song_.set_track(99);
EXPECT_EQ("99", format_.GetFilenameForSong(song_));
song_.set_track(999);
EXPECT_EQ("999", format_.GetFilenameForSong(song_));
song_.set_track(0);
EXPECT_EQ("", format_.GetFilenameForSong(song_));
}
#if 0
TEST_F(OrganiseFormatTest, ReplaceSlashes) {
format_.set_format("%title");
song_.set_title("foo/bar\\baz");
qLog(Debug) << format_.GetFilenameForSong(song_);
EXPECT_EQ("foo_bar_baz", format_.GetFilenameForSong(song_));
}
#endif

543
tests/src/playlist_test.cpp Normal file
View File

@ -0,0 +1,543 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include <memory>
#include <gtest/gtest.h>
#include "test_utils.h"
#include "collection/collectionplaylistitem.h"
#include "playlist/playlist.h"
#include "mock_settingsprovider.h"
#include "mock_playlistitem.h"
#include <QtDebug>
#include <QUndoStack>
using std::shared_ptr;
using ::testing::Return;
namespace {
class PlaylistTest : public ::testing::Test {
protected:
PlaylistTest()
: playlist_(nullptr, nullptr, nullptr, 1),
sequence_(nullptr, new DummySettingsProvider)
{
}
void SetUp() {
playlist_.set_sequence(&sequence_);
}
MockPlaylistItem* MakeMockItem(const QString& title, const QString& artist = QString(), const QString& album = QString(), int length = 123) const {
Song metadata;
metadata.Init(title, artist, album, length);
MockPlaylistItem* ret = new MockPlaylistItem;
EXPECT_CALL(*ret, Metadata()).WillRepeatedly(Return(metadata));
return ret;
}
shared_ptr<PlaylistItem> MakeMockItemP(const QString& title, const QString& artist = QString(), const QString& album = QString(), int length = 123) const {
return shared_ptr<PlaylistItem>(MakeMockItem(title, artist, album, length));
}
Playlist playlist_;
PlaylistSequence sequence_;
};
TEST_F(PlaylistTest, Basic) {
EXPECT_EQ(0, playlist_.rowCount(QModelIndex()));
}
TEST_F(PlaylistTest, InsertItems) {
MockPlaylistItem* item = MakeMockItem("Title", "Artist", "Album", 123);
shared_ptr<PlaylistItem> item_ptr(item);
// Insert the item
EXPECT_EQ(0, playlist_.rowCount(QModelIndex()));
playlist_.InsertItems(PlaylistItemList() << item_ptr, -1);
ASSERT_EQ(1, playlist_.rowCount(QModelIndex()));
// Get the metadata
EXPECT_EQ("Title", playlist_.data(playlist_.index(0, Playlist::Column_Title)));
EXPECT_EQ("Artist", playlist_.data(playlist_.index(0, Playlist::Column_Artist)));
EXPECT_EQ("Album", playlist_.data(playlist_.index(0, Playlist::Column_Album)));
EXPECT_EQ(123, playlist_.data(playlist_.index(0, Playlist::Column_Length)));
}
TEST_F(PlaylistTest, Indexes) {
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("One") << MakeMockItemP("Two") << MakeMockItemP("Three"));
ASSERT_EQ(3, playlist_.rowCount(QModelIndex()));
// Start "playing" track 1
playlist_.set_current_row(0);
EXPECT_EQ(0, playlist_.current_row());
EXPECT_EQ("One", playlist_.current_item()->Metadata().title());
EXPECT_EQ(-1, playlist_.previous_row());
EXPECT_EQ(1, playlist_.next_row());
// Stop playing
EXPECT_EQ(0, playlist_.last_played_row());
playlist_.set_current_row(-1);
EXPECT_EQ(0, playlist_.last_played_row());
EXPECT_EQ(-1, playlist_.current_row());
// Play track 2
playlist_.set_current_row(1);
EXPECT_EQ(1, playlist_.current_row());
EXPECT_EQ("Two", playlist_.current_item()->Metadata().title());
EXPECT_EQ(0, playlist_.previous_row());
EXPECT_EQ(2, playlist_.next_row());
// Play track 3
playlist_.set_current_row(2);
EXPECT_EQ(2, playlist_.current_row());
EXPECT_EQ("Three", playlist_.current_item()->Metadata().title());
EXPECT_EQ(1, playlist_.previous_row());
EXPECT_EQ(-1, playlist_.next_row());
}
TEST_F(PlaylistTest, RepeatPlaylist) {
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("One") << MakeMockItemP("Two") << MakeMockItemP("Three"));
ASSERT_EQ(3, playlist_.rowCount(QModelIndex()));
playlist_.sequence()->SetRepeatMode(PlaylistSequence::Repeat_Playlist);
playlist_.set_current_row(0);
EXPECT_EQ(1, playlist_.next_row());
playlist_.set_current_row(1);
EXPECT_EQ(2, playlist_.next_row());
playlist_.set_current_row(2);
EXPECT_EQ(0, playlist_.next_row());
}
TEST_F(PlaylistTest, RepeatTrack) {
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("One") << MakeMockItemP("Two") << MakeMockItemP("Three"));
ASSERT_EQ(3, playlist_.rowCount(QModelIndex()));
playlist_.sequence()->SetRepeatMode(PlaylistSequence::Repeat_Track);
playlist_.set_current_row(0);
EXPECT_EQ(0, playlist_.next_row());
}
TEST_F(PlaylistTest, RepeatAlbum) {
playlist_.InsertItems(PlaylistItemList()
<< MakeMockItemP("One", "Album one")
<< MakeMockItemP("Two", "Album two")
<< MakeMockItemP("Three", "Album one"));
ASSERT_EQ(3, playlist_.rowCount(QModelIndex()));
playlist_.sequence()->SetRepeatMode(PlaylistSequence::Repeat_Album);
playlist_.set_current_row(0);
EXPECT_EQ(2, playlist_.next_row());
playlist_.set_current_row(2);
EXPECT_EQ(0, playlist_.next_row());
}
TEST_F(PlaylistTest, RemoveBeforeCurrent) {
playlist_.InsertItems(PlaylistItemList()
<< MakeMockItemP("One") << MakeMockItemP("Two") << MakeMockItemP("Three"));
ASSERT_EQ(3, playlist_.rowCount(QModelIndex()));
// Remove a row before the currently playing track
playlist_.set_current_row(2);
EXPECT_EQ(2, playlist_.current_row());
playlist_.removeRow(1, QModelIndex());
EXPECT_EQ(1, playlist_.current_row());
EXPECT_EQ(1, playlist_.last_played_row());
EXPECT_EQ(0, playlist_.previous_row());
EXPECT_EQ(-1, playlist_.next_row());
}
TEST_F(PlaylistTest, RemoveAfterCurrent) {
playlist_.InsertItems(PlaylistItemList()
<< MakeMockItemP("One") << MakeMockItemP("Two") << MakeMockItemP("Three"));
ASSERT_EQ(3, playlist_.rowCount(QModelIndex()));
// Remove a row after the currently playing track
playlist_.set_current_row(0);
EXPECT_EQ(0, playlist_.current_row());
playlist_.removeRow(1, QModelIndex());
EXPECT_EQ(0, playlist_.current_row());
EXPECT_EQ(0, playlist_.last_played_row());
EXPECT_EQ(-1, playlist_.previous_row());
EXPECT_EQ(1, playlist_.next_row());
playlist_.set_current_row(1);
EXPECT_EQ(-1, playlist_.next_row());
}
TEST_F(PlaylistTest, RemoveCurrent) {
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("One") << MakeMockItemP("Two") << MakeMockItemP("Three"));
ASSERT_EQ(3, playlist_.rowCount(QModelIndex()));
// Remove the currently playing track's row
playlist_.set_current_row(1);
EXPECT_EQ(1, playlist_.current_row());
playlist_.removeRow(1, QModelIndex());
EXPECT_EQ(-1, playlist_.current_row());
EXPECT_EQ(-1, playlist_.last_played_row());
EXPECT_EQ(-1, playlist_.previous_row());
EXPECT_EQ(0, playlist_.next_row());
}
TEST_F(PlaylistTest, InsertBeforeCurrent) {
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("One") << MakeMockItemP("Two") << MakeMockItemP("Three"));
ASSERT_EQ(3, playlist_.rowCount(QModelIndex()));
playlist_.set_current_row(1);
EXPECT_EQ(1, playlist_.current_row());
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("Four"), 0);
ASSERT_EQ(4, playlist_.rowCount(QModelIndex()));
EXPECT_EQ(2, playlist_.current_row());
EXPECT_EQ(2, playlist_.last_played_row());
EXPECT_EQ(1, playlist_.previous_row());
EXPECT_EQ(3, playlist_.next_row());
EXPECT_EQ("Four", playlist_.data(playlist_.index(0, Playlist::Column_Title)));
EXPECT_EQ("One", playlist_.data(playlist_.index(1, Playlist::Column_Title)));
}
TEST_F(PlaylistTest, InsertAfterCurrent) {
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("One") << MakeMockItemP("Two") << MakeMockItemP("Three"));
ASSERT_EQ(3, playlist_.rowCount(QModelIndex()));
playlist_.set_current_row(1);
EXPECT_EQ(1, playlist_.current_row());
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("Four"), 2);
ASSERT_EQ(4, playlist_.rowCount(QModelIndex()));
EXPECT_EQ(1, playlist_.current_row());
EXPECT_EQ(1, playlist_.last_played_row());
EXPECT_EQ(0, playlist_.previous_row());
EXPECT_EQ(2, playlist_.next_row());
EXPECT_EQ("Two", playlist_.data(playlist_.index(1, Playlist::Column_Title)));
EXPECT_EQ("Four", playlist_.data(playlist_.index(2, Playlist::Column_Title)));
EXPECT_EQ("Three", playlist_.data(playlist_.index(3, Playlist::Column_Title)));
}
TEST_F(PlaylistTest, Clear) {
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("One") << MakeMockItemP("Two") << MakeMockItemP("Three"));
ASSERT_EQ(3, playlist_.rowCount(QModelIndex()));
playlist_.set_current_row(1);
EXPECT_EQ(1, playlist_.current_row());
playlist_.Clear();
EXPECT_EQ(0, playlist_.rowCount(QModelIndex()));
EXPECT_EQ(-1, playlist_.current_row());
EXPECT_EQ(-1, playlist_.last_played_row());
EXPECT_EQ(-1, playlist_.previous_row());
EXPECT_EQ(-1, playlist_.next_row());
}
TEST_F(PlaylistTest, UndoAdd) {
EXPECT_FALSE(playlist_.undo_stack()->canUndo());
EXPECT_FALSE(playlist_.undo_stack()->canRedo());
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("Title"));
EXPECT_EQ(1, playlist_.rowCount(QModelIndex()));
EXPECT_FALSE(playlist_.undo_stack()->canRedo());
ASSERT_TRUE(playlist_.undo_stack()->canUndo());
playlist_.undo_stack()->undo();
EXPECT_EQ(0, playlist_.rowCount(QModelIndex()));
EXPECT_FALSE(playlist_.undo_stack()->canUndo());
ASSERT_TRUE(playlist_.undo_stack()->canRedo());
playlist_.undo_stack()->redo();
EXPECT_EQ(1, playlist_.rowCount(QModelIndex()));
EXPECT_FALSE(playlist_.undo_stack()->canRedo());
EXPECT_TRUE(playlist_.undo_stack()->canUndo());
EXPECT_EQ("Title", playlist_.data(playlist_.index(0, Playlist::Column_Title)));
}
TEST_F(PlaylistTest, UndoMultiAdd) {
// Add 1 item
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("One"));
// Add 2 items
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("Two") << MakeMockItemP("Three"));
// Undo adding 2 items
ASSERT_TRUE(playlist_.undo_stack()->canUndo());
EXPECT_EQ("add 2 songs", playlist_.undo_stack()->undoText());
playlist_.undo_stack()->undo();
// Undo adding 1 item
ASSERT_TRUE(playlist_.undo_stack()->canUndo());
EXPECT_EQ("add 1 songs", playlist_.undo_stack()->undoText());
playlist_.undo_stack()->undo();
EXPECT_FALSE(playlist_.undo_stack()->canUndo());
}
#if 0
TEST_F(PlaylistTest, UndoRemove) {
EXPECT_FALSE(playlist_.undo_stack()->canUndo());
EXPECT_FALSE(playlist_.undo_stack()->canRedo());
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("Title"));
playlist_.removeRow(0);
EXPECT_EQ(0, playlist_.rowCount(QModelIndex()));
EXPECT_FALSE(playlist_.undo_stack()->canRedo());
ASSERT_TRUE(playlist_.undo_stack()->canUndo());
playlist_.undo_stack()->undo();
//EXPECT_EQ(1, playlist_.rowCount(QModelIndex()));
//ASSERT_TRUE(playlist_.undo_stack()->canRedo());
//EXPECT_EQ("Title", playlist_.data(playlist_.index(0, Playlist::Column_Title)));
//playlist_.undo_stack()->redo();
//EXPECT_EQ(0, playlist_.rowCount(QModelIndex()));
//EXPECT_FALSE(playlist_.undo_stack()->canRedo());
//EXPECT_TRUE(playlist_.undo_stack()->canUndo());
}
TEST_F(PlaylistTest, UndoMultiRemove) {
// Add 3 items
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("One") << MakeMockItemP("Two") << MakeMockItemP("Three"));
ASSERT_EQ(3, playlist_.rowCount(QModelIndex()));
// Remove 1 item
playlist_.removeRow(1); // Item "Two"
// Remove 2 items
playlist_.removeRows(0, 2); // "One" and "Three"
ASSERT_EQ(0, playlist_.rowCount(QModelIndex()));
// Undo removing all 3 items
ASSERT_TRUE(playlist_.undo_stack()->canUndo());
EXPECT_EQ("remove 3 songs", playlist_.undo_stack()->undoText());
playlist_.undo_stack()->undo();
ASSERT_EQ(3, playlist_.rowCount(QModelIndex()));
}
#endif
TEST_F(PlaylistTest, UndoClear) {
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("One") << MakeMockItemP("Two") << MakeMockItemP("Three"));
ASSERT_EQ(3, playlist_.rowCount(QModelIndex()));
playlist_.Clear();
ASSERT_EQ(0, playlist_.rowCount(QModelIndex()));
ASSERT_TRUE(playlist_.undo_stack()->canUndo());
EXPECT_EQ("remove 3 songs", playlist_.undo_stack()->undoText());
playlist_.undo_stack()->undo();
ASSERT_EQ(3, playlist_.rowCount(QModelIndex()));
}
#if 0
TEST_F(PlaylistTest, UndoRemoveCurrent) {
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("Title"));
playlist_.set_current_row(0);
EXPECT_EQ(0, playlist_.current_row());
EXPECT_EQ(0, playlist_.last_played_row());
playlist_.removeRow(0);
EXPECT_EQ(-1, playlist_.current_row());
EXPECT_EQ(-1, playlist_.last_played_row());
playlist_.undo_stack()->undo();
EXPECT_EQ(0, playlist_.current_row());
EXPECT_EQ(0, playlist_.last_played_row());
}
TEST_F(PlaylistTest, UndoRemoveOldCurrent) {
playlist_.InsertItems(PlaylistItemList() << MakeMockItemP("Title"));
playlist_.set_current_row(0);
EXPECT_EQ(0, playlist_.current_row());
EXPECT_EQ(0, playlist_.last_played_row());
playlist_.removeRow(0);
EXPECT_EQ(-1, playlist_.current_row());
EXPECT_EQ(-1, playlist_.last_played_row());
playlist_.set_current_row(-1);
playlist_.undo_stack()->undo();
EXPECT_EQ(0, playlist_.current_row());
EXPECT_EQ(0, playlist_.last_played_row());
}
TEST_F(PlaylistTest, ShuffleThenNext) {
// Add 100 items
PlaylistItemList items;
for (int i=0 ; i<100 ; ++i)
items << MakeMockItemP("Item " + QString::number(i));
playlist_.InsertItems(items);
playlist_.set_current_row(0);
// Shuffle until the current index is not at the end
forever {
playlist_.Shuffle();
if (playlist_.current_row() != items.count()-1)
break;
}
int index = playlist_.current_row();
EXPECT_EQ("Item 0", playlist_.current_item()->Metadata().title());
EXPECT_EQ("Item 0", playlist_.data(playlist_.index(index, Playlist::Column_Title)));
EXPECT_EQ(index, playlist_.last_played_row());
EXPECT_EQ(index + 1, playlist_.next_row());
// Shuffle until the current index *is* at the end
forever {
playlist_.Shuffle();
if (playlist_.current_row() == items.count()-1)
break;
}
index = playlist_.current_row();
EXPECT_EQ("Item 0", playlist_.current_item()->Metadata().title());
EXPECT_EQ("Item 0", playlist_.data(playlist_.index(index, Playlist::Column_Title)));
EXPECT_EQ(index, playlist_.last_played_row());
EXPECT_EQ(-1, playlist_.next_row());
EXPECT_EQ(index-1, playlist_.previous_row());
}
#endif
TEST_F(PlaylistTest, CollectionIdMapSingle) {
Song song;
song.Init("title", "artist", "album", 123);
song.set_id(1);
PlaylistItemPtr item(new CollectionPlaylistItem(song));
playlist_.InsertItems(PlaylistItemList() << item);
EXPECT_EQ(0, playlist_.collection_items_by_id(-1).count());
EXPECT_EQ(0, playlist_.collection_items_by_id(0).count());
EXPECT_EQ(0, playlist_.collection_items_by_id(2).count());
ASSERT_EQ(1, playlist_.collection_items_by_id(1).count());
EXPECT_EQ(song.title(), playlist_.collection_items_by_id(1)[0]->Metadata().title());
playlist_.Clear();
EXPECT_EQ(0, playlist_.collection_items_by_id(1).count());
}
TEST_F(PlaylistTest, CollectionIdMapInvalid) {
Song invalid;
invalid.Init("title", "artist", "album", 123);
ASSERT_EQ(-1, invalid.id());
PlaylistItemPtr item(new CollectionPlaylistItem(invalid));
playlist_.InsertItems(PlaylistItemList() << item);
EXPECT_EQ(0, playlist_.collection_items_by_id(-1).count());
EXPECT_EQ(0, playlist_.collection_items_by_id(0).count());
EXPECT_EQ(0, playlist_.collection_items_by_id(1).count());
EXPECT_EQ(0, playlist_.collection_items_by_id(2).count());
}
TEST_F(PlaylistTest, CollectionIdMapMulti) {
Song one;
one.Init("title", "artist", "album", 123);
one.set_id(1);
Song two;
two.Init("title 2", "artist 2", "album 2", 123);
two.set_id(2);
PlaylistItemPtr item_one(new CollectionPlaylistItem(one));
PlaylistItemPtr item_two(new CollectionPlaylistItem(two));
PlaylistItemPtr item_three(new CollectionPlaylistItem(one));
playlist_.InsertItems(PlaylistItemList() << item_one << item_two << item_three);
EXPECT_EQ(2, playlist_.collection_items_by_id(1).count());
EXPECT_EQ(1, playlist_.collection_items_by_id(2).count());
playlist_.removeRow(1); // item_two
EXPECT_EQ(2, playlist_.collection_items_by_id(1).count());
EXPECT_EQ(0, playlist_.collection_items_by_id(2).count());
playlist_.removeRow(1); // item_three
EXPECT_EQ(1, playlist_.collection_items_by_id(1).count());
EXPECT_EQ(0, playlist_.collection_items_by_id(2).count());
playlist_.removeRow(0); // item_one
EXPECT_EQ(0, playlist_.collection_items_by_id(1).count());
EXPECT_EQ(0, playlist_.collection_items_by_id(2).count());
}
} // namespace

37
tests/src/resources_env.h Normal file
View File

@ -0,0 +1,37 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef RESOURCES_ENV_H
#define RESOURCES_ENV_H
#include <gtest/gtest.h>
#include <QResource>
class ResourcesEnvironment : public ::testing::Environment {
public:
void SetUp() {
Q_INIT_RESOURCE(data);
Q_INIT_RESOURCE(translations);
Q_INIT_RESOURCE(testdata);
}
};
#endif // RESOURCES_ENV_H

147
tests/src/song_test.cpp Normal file
View File

@ -0,0 +1,147 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
* Copyright 2019, Jonas Kvinge <jonas@jkvinge.net>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include <id3v2tag.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <QFile>
#include <QByteArray>
#include <QStringList>
#include <QTemporaryFile>
#include <QTextCodec>
#include <QRegExp>
#include "core/song.h"
#include "tagreader.h"
#include "test_utils.h"
namespace {
class SongTest : public ::testing::Test {
protected:
static void SetUpTestCase() {
// Return something from uninteresting mock functions.
testing::DefaultValue<TagLib::String>::Set("foobarbaz");
}
static Song ReadSongFromFile(const QString& filename) {
TagReader tag_reader;
Song song;
::pb::tagreader::SongMetadata pb_song;
// We need to init protobuf object from a Song object, to have default values initialized correctly.
song.ToProtobuf(&pb_song);
tag_reader.ReadFile(filename, &pb_song);
song.InitFromProtobuf(pb_song);
return song;
}
static void WriteSongToFile(const Song& song, const QString& filename) {
TagReader tag_reader;
::pb::tagreader::SongMetadata pb_song;
song.ToProtobuf(&pb_song);
tag_reader.SaveFile(filename, pb_song);
}
};
TEST_F(SongTest, TestAudioFileTagging) {
const QStringList files_to_test = QStringList() << ":/audio/strawberry.wav"
<< ":/audio/strawberry.flac"
<< ":/audio/strawberry.wv"
<< ":/audio/strawberry.oga"
<< ":/audio/strawberry.opus"
<< ":/audio/strawberry.spx"
<< ":/audio/strawberry.aif"
<< ":/audio/strawberry.m4a"
<< ":/audio/strawberry.mp4"
<< ":/audio/strawberry.mp3"
<< ":/audio/strawberry.asf";
for (const QString& test_filename : files_to_test) {
TemporaryResource r(test_filename);
Song song = ReadSongFromFile(r.fileName());
// Compare files
QFile orig_file(test_filename);
orig_file.open(QIODevice::ReadOnly);
EXPECT_TRUE(orig_file.isOpen());
QByteArray orig_file_data = orig_file.readAll();
QFile temp_file(r.fileName());
temp_file.open(QIODevice::ReadOnly);
EXPECT_TRUE(temp_file.isOpen());
QByteArray temp_file_data = temp_file.readAll();
EXPECT_TRUE(!orig_file_data.isEmpty());
EXPECT_TRUE(!temp_file_data.isEmpty());
EXPECT_TRUE(orig_file_data == temp_file_data);
if (test_filename.contains(QRegExp(".*\\.wav$"))) continue;
// Write tags
song.set_title("strawberry title");
song.set_artist("strawberry artist");
song.set_album("strawberry album");
song.set_albumartist("strawberry album artist");
song.set_composer("strawberry composer");
song.set_performer("strawberry performer");
song.set_grouping("strawberry grouping");
song.set_genre("strawberry genre");
song.set_comment("strawberry comment");
song.set_track(12);
song.set_disc(1234);
song.set_year(2019);
WriteSongToFile(song, r.fileName());
// Read tags
Song new_song = ReadSongFromFile(r.fileName());
EXPECT_EQ("strawberry title", new_song.title());
EXPECT_EQ("strawberry artist", new_song.artist());
EXPECT_EQ("strawberry album", new_song.album());
if (!test_filename.contains(QRegExp(".*\\.aif$")) && !test_filename.contains(QRegExp(".*\\.asf$"))) {
EXPECT_EQ("strawberry album artist", new_song.albumartist());
EXPECT_EQ("strawberry composer", new_song.composer());
if (!test_filename.contains(QRegExp(".*\\.mp4$")) && !test_filename.contains(QRegExp(".*\\.m4a$"))) {
EXPECT_EQ("strawberry performer", new_song.performer());
}
EXPECT_EQ("strawberry grouping", new_song.grouping());
EXPECT_EQ(1234, new_song.disc());
}
EXPECT_EQ("strawberry genre", new_song.genre());
if (!test_filename.contains(QRegExp(".*\\.asf$"))) {
EXPECT_EQ("strawberry comment", new_song.comment());
}
EXPECT_EQ(12, new_song.track());
EXPECT_EQ(2019, new_song.year());
}
}
} // namespace

View File

@ -0,0 +1,79 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include <memory>
#include <gtest/gtest.h>
#include <QTemporaryFile>
#include <QFileInfo>
#include <QString>
#include <QUrl>
#include <QtDebug>
#include "test_utils.h"
#include "playlist/songplaylistitem.h"
namespace {
class SongPlaylistItemTest : public ::testing::TestWithParam<const char*> {
protected:
SongPlaylistItemTest() : temp_file_(GetParam()) {}
void SetUp() {
// SongPlaylistItem::Url() checks if the file exists, so we need a real file
temp_file_.open();
absolute_file_name_ = QFileInfo(temp_file_.fileName()).absoluteFilePath();
song_.Init("Title", "Artist", "Album", 123);
song_.set_url(QUrl::fromLocalFile(absolute_file_name_));
item_.reset(new SongPlaylistItem(song_));
if (!absolute_file_name_.startsWith('/'))
absolute_file_name_.prepend('/');
}
Song song_;
QTemporaryFile temp_file_;
QString absolute_file_name_;
std::unique_ptr<SongPlaylistItem> item_;
};
INSTANTIATE_TEST_CASE_P(RealFiles, SongPlaylistItemTest, testing::Values(
"normalfile.flac",
"file with spaces.flac",
"file with # hash.flac",
"file with ? question.flac"
));
TEST_P(SongPlaylistItemTest, Url) {
QUrl expected;
expected.setScheme("file");
expected.setPath(absolute_file_name_);
EXPECT_EQ(expected, item_->Url());
}
} //namespace

16
tests/src/sqlite_test.cpp Normal file
View File

@ -0,0 +1,16 @@
#include <gtest/gtest.h>
#include <sqlite3.h>
TEST(SqliteTest, FTS3SupportEnabled) {
sqlite3* db = nullptr;
int rc = sqlite3_open(":memory:", &db);
ASSERT_EQ(0, rc);
char* errmsg = nullptr;
rc = sqlite3_exec(db, "CREATE VIRTUAL TABLE foo USING fts3(content, TEXT)", nullptr, nullptr, &errmsg);
ASSERT_EQ(0, rc) << errmsg;
sqlite3_close(db);
}

87
tests/src/test_utils.cpp Normal file
View File

@ -0,0 +1,87 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include "test_utils.h"
#include <QObject>
#include <QIODevice>
#include <QDir>
#include <QNetworkRequest>
#include <QVariant>
#include <QString>
#include <QUrl>
std::ostream& operator<<(std::ostream& stream, const QString& str) {
stream << str.toStdString();
return stream;
}
std::ostream& operator <<(std::ostream& stream, const QUrl& url) {
stream << url.toString().toStdString();
return stream;
}
std::ostream& operator <<(std::ostream& stream, const QNetworkRequest& req) {
stream << req.url().toString().toStdString();
return stream;
}
std::ostream& operator <<(std::ostream& stream, const QVariant& var) {
stream << var.toString().toStdString();
return stream;
}
void PrintTo(const ::QString& str, std::ostream& os) {
os << str.toStdString();
}
void PrintTo(const ::QVariant& var, std::ostream& os) {
os << var.toString().toStdString();
}
void PrintTo(const ::QUrl& url, std::ostream& os) {
os << url.toString().toStdString();
}
TemporaryResource::TemporaryResource(const QString& filename) {
setFileTemplate(QDir::tempPath() + "/strawberry_test-XXXXXX." + filename.section('.', -1, -1));
open();
QFile resource(filename);
resource.open(QIODevice::ReadOnly);
write(resource.readAll());
reset();
}
TestQObject::TestQObject(QObject* parent)
: QObject(parent),
invoked_(0) {
}
void TestQObject::Emit() {
emit Emitted();
}
void TestQObject::Invoke() {
++invoked_;
}

87
tests/src/test_utils.h Normal file
View File

@ -0,0 +1,87 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef TEST_UTILS_H
#define TEST_UTILS_H
#include <iostream>
#include <QMetaType>
#include <QModelIndex>
#include <QTemporaryFile>
class QNetworkRequest;
class QString;
class QUrl;
class QVariant;
std::ostream& operator <<(std::ostream& stream, const QString& str);
std::ostream& operator <<(std::ostream& stream, const QVariant& var);
std::ostream& operator <<(std::ostream& stream, const QUrl& url);
std::ostream& operator <<(std::ostream& stream, const QNetworkRequest& req);
template <typename T>
std::ostream& operator <<(std::ostream& stream, const QList<T>& list) {
stream << "QList(";
foreach (const T& item, list) {
stream << item << ",";
}
stream << ")";
return stream;
}
void PrintTo(const ::QString& str, std::ostream& os);
void PrintTo(const ::QVariant& var, std::ostream& os);
void PrintTo(const ::QUrl& url, std::ostream& os);
#define EXPOSE_SIGNAL0(n) \
void Emit##n() { emit n(); }
#define EXPOSE_SIGNAL1(n, t1) \
void Emit##n(const t1& a1) { emit n(a1); }
#define EXPOSE_SIGNAL2(n, t1, t2) \
void Emit##n(const t1& a1, const t2& a2) { emit n(a1, a2); }
Q_DECLARE_METATYPE(QModelIndex);
class TemporaryResource : public QTemporaryFile {
public:
TemporaryResource(const QString& filename);
};
class TestQObject : public QObject {
Q_OBJECT
public:
TestQObject(QObject* parent = 0);
void Emit();
int invoked() const { return invoked_; }
signals:
void Emitted();
public slots:
void Invoke();
private:
int invoked_;
};
#endif // TEST_UTILS_H

View File

@ -0,0 +1,30 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#define protected public
#include <QProgressBar>
#undef protected
#include "testobjectdecorators.h"
void TestObjectDecorators::initStyleOption(QProgressBar* self, QStyleOptionProgressBar* opt) {
self->initStyleOption(opt);
}

View File

@ -0,0 +1,36 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#ifndef TESTOBJECTDECORATORS_H
#define TESTOBJECTDECORATORS_H
#include <QObject>
class QProgressBar;
class QStyleOptionProgressBar;
class TestObjectDecorators : public QObject {
Q_OBJECT
public slots:
void initStyleOption(QProgressBar* self, QStyleOptionProgressBar* opt);
};
#endif // TESTOBJECTDECORATORS_H

View File

@ -0,0 +1,50 @@
/*
* Strawberry Music Player
* This file was part of Clementine.
* Copyright 2010, David Sansome <me@davidsansome.com>
*
* 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 <http://www.gnu.org/licenses/>.
*
*/
#include <gtest/gtest.h>
#include <QString>
#include <QDateTime>
#include <QtDebug>
#include "test_utils.h"
#include "core/utilities.h"
TEST(UtilitiesTest, HmacFunctions) {
QString key("key");
QString data("The quick brown fox jumps over the lazy dog");
// Test Hmac Md5
QByteArray result_hash_md5 = Utilities::HmacMd5(key.toLocal8Bit(), data.toLocal8Bit()).toHex();
bool result_md5 = result_hash_md5 == QString("80070713463e7749b90c2dc24911e275");
EXPECT_TRUE(result_md5);
// Test Hmac Sha256
QByteArray result_hash_sha256 = Utilities::HmacSha256(key.toLocal8Bit(), data.toLocal8Bit()).toHex();
bool result_sha256 = result_hash_sha256 == QString("f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8");
EXPECT_TRUE(result_sha256);
}
TEST(UtilitiesTest, ParseRFC822DateTim) {
QDateTime result_DateTime = Utilities::ParseRFC822DateTime(QString("22 Feb 2008 00:16:17 GMT"));
EXPECT_TRUE(result_DateTime.isValid());
result_DateTime = Utilities::ParseRFC822DateTime(QString("Thu, 13 Dec 2012 13:27:52 +0000"));
EXPECT_TRUE(result_DateTime.isValid());
result_DateTime = Utilities::ParseRFC822DateTime(QString("Mon, 12 March 2012 20:00:00 +0100"));
EXPECT_TRUE(result_DateTime.isValid());
}