1
0
mirror of https://github.com/clementine-player/Clementine synced 2025-01-28 10:09:24 +01:00

Make it possible for the LibraryBackend to be mocked, and add a simple test for Library.

This commit is contained in:
David Sansome 2010-03-23 17:26:54 +00:00
parent 777cc79b95
commit 16e1deaade
18 changed files with 373 additions and 92 deletions

View File

@ -60,6 +60,7 @@ set(CLEMENTINE-SOURCES
xspfparser.cpp
globalshortcuts/globalshortcuts.cpp
fixlastfm.cpp
backgroundthread.cpp
)
# Header files that have Q_OBJECT in

View File

@ -22,7 +22,7 @@ const char* AlbumCoverManager::kSettingsGroup = "CoverManager";
AlbumCoverManager::AlbumCoverManager(QNetworkAccessManager* network, QWidget *parent)
: QDialog(parent),
constructed_(false),
cover_loader_(new BackgroundThread<AlbumCoverLoader>(this)),
cover_loader_(new BackgroundThreadImplementation<AlbumCoverLoader, AlbumCoverLoader>(this)),
cover_fetcher_(new AlbumCoverFetcher(network, this)),
artist_icon_(":/artist.png"),
all_artists_icon_(":/album.png"),
@ -89,7 +89,7 @@ AlbumCoverManager::AlbumCoverManager(QNetworkAccessManager* network, QWidget *pa
ui_.splitter->setSizes(QList<int>() << 200 << width() - 200);
}
cover_loader_->start();
cover_loader_->Start();
constructed_ = true;
}
@ -102,7 +102,7 @@ void AlbumCoverManager::CoverLoaderInitialised() {
SLOT(CoverImageLoaded(quint64,QImage)));
}
void AlbumCoverManager::SetBackend(boost::shared_ptr<LibraryBackend> backend) {
void AlbumCoverManager::SetBackend(boost::shared_ptr<LibraryBackendInterface> backend) {
backend_ = backend;
if (isVisible())
@ -166,7 +166,7 @@ void AlbumCoverManager::ArtistChanged(QListWidgetItem* current) {
// Get the list of albums. How we do it depends on what thing we have
// selected in the artist list.
LibraryBackend::AlbumList albums;
LibraryBackendInterface::AlbumList albums;
switch (current->type()) {
case Various_Artists: albums = backend_->GetCompilationAlbums(); break;
case Specific_Artist: albums = backend_->GetAlbumsByArtist(current->text()); break;
@ -174,7 +174,7 @@ void AlbumCoverManager::ArtistChanged(QListWidgetItem* current) {
default: albums = backend_->GetAllAlbums(); break;
}
foreach (const LibraryBackend::Album& info, albums) {
foreach (const LibraryBackendInterface::Album& info, albums) {
// Don't show songs without an album, obviously
if (info.album_name.isEmpty())
continue;

View File

@ -10,7 +10,7 @@
#include "backgroundthread.h"
#include "albumcoverloader.h"
class LibraryBackend;
class LibraryBackendInterface;
class AlbumCoverFetcher;
class QNetworkAccessManager;
@ -26,7 +26,7 @@ class AlbumCoverManager : public QDialog {
void Reset();
public slots:
void SetBackend(boost::shared_ptr<LibraryBackend> backend);
void SetBackend(boost::shared_ptr<LibraryBackendInterface> backend);
protected:
void showEvent(QShowEvent *);
@ -70,7 +70,7 @@ class AlbumCoverManager : public QDialog {
bool constructed_;
Ui::CoverManager ui_;
boost::shared_ptr<LibraryBackend> backend_;
boost::shared_ptr<LibraryBackendInterface> backend_;
QAction* filter_all_;
QAction* filter_with_covers_;

21
src/backgroundthread.cpp Normal file
View File

@ -0,0 +1,21 @@
#include "backgroundthread.h"
int BackgroundThreadBase::SetIOPriority() {
#ifdef Q_OS_LINUX
return syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, gettid(),
4 | io_priority_ << IOPRIO_CLASS_SHIFT);
#elif defined(Q_OS_DARWIN)
return setpriority(PRIO_DARWIN_THREAD, 0,
io_priority_ == IOPRIO_CLASS_IDLE ? PRIO_DARWIN_BG : 0);
#else
return 0;
#endif
}
int BackgroundThreadBase::gettid() {
#ifdef Q_OS_LINUX
return syscall(SYS_gettid);
#else
return 0;
#endif
}

View File

@ -13,11 +13,29 @@
# include <sys/resource.h>
#endif
// These classes are a bit confusing because they're trying to do so many
// things:
// * Run a worker in a background thread
// * ... or maybe run it in the same thread if we're in a test
// * Use interfaces throughout, so the implementations can be mocked
// * Create concrete implementations of the interfaces when threads start
//
// The types you should use throughout your header files are:
// BackgroundThread<InterfaceType>
// BackgroundThreadFactory<InterfaceType>
//
// You should allow callers to set their own factory (which might return mocks
// of your interface), and default to using a:
// BackgroundThreadFactoryImplementation<InterfaceType, DerivedType>
// This is the base class. We need one because moc doesn't like templated
// classes. This also deals with anything that doesn't depend on the type of
// the worker.
class BackgroundThreadBase : public QThread {
Q_OBJECT
public:
BackgroundThreadBase(QObject* parent = 0) : QThread(parent), io_priority_(IOPRIO_CLASS_NONE) {}
virtual ~BackgroundThreadBase() {}
// Borrowed from schedutils
enum IoPriority {
@ -28,14 +46,16 @@ class BackgroundThreadBase : public QThread {
};
void set_io_priority(IoPriority priority) { io_priority_ = priority; }
void set_cpu_priority(QThread::Priority priority) { cpu_priority_ = priority; }
virtual void Start() { QThread::start(cpu_priority_); }
signals:
void Initialised();
protected:
// Borrowed from schedutils
static inline int ioprio_set(int which, int who, int ioprio);
static inline int gettid();
int SetIOPriority();
static int gettid();
enum {
IOPRIO_WHO_PROCESS = 1,
@ -45,31 +65,61 @@ class BackgroundThreadBase : public QThread {
static const int IOPRIO_CLASS_SHIFT = 13;
IoPriority io_priority_;
QThread::Priority cpu_priority_;
};
template <typename T>
// This is the templated class that stores and returns the worker object.
template <typename InterfaceType>
class BackgroundThread : public BackgroundThreadBase {
public:
BackgroundThread(QObject* parent = 0);
virtual ~BackgroundThread();
~BackgroundThread();
boost::shared_ptr<T> Worker() const { return worker_; }
boost::shared_ptr<InterfaceType> Worker() const { return worker_; }
protected:
boost::shared_ptr<InterfaceType> worker_;
};
// This class actually creates an implementation of the worker object
template <typename InterfaceType, typename DerivedType>
class BackgroundThreadImplementation : public BackgroundThread<InterfaceType> {
public:
BackgroundThreadImplementation(QObject* parent = 0);
protected:
void run();
private:
boost::shared_ptr<T> worker_;
};
template <typename T>
BackgroundThread<T>::BackgroundThread(QObject *parent)
// This is a pure virtual factory for creating threads.
template <typename InterfaceType>
class BackgroundThreadFactory {
public:
virtual ~BackgroundThreadFactory() {}
virtual BackgroundThread<InterfaceType>* GetThread(QObject* parent) = 0;
};
// This implementation of the factory returns a BackgroundThread that creates
// the right derived types...
template <typename InterfaceType, typename DerivedType>
class BackgroundThreadFactoryImplementation : public BackgroundThreadFactory<InterfaceType> {
public:
BackgroundThread<InterfaceType>* GetThread(QObject* parent) {
return new BackgroundThreadImplementation<InterfaceType, DerivedType>(parent);
}
};
template <typename InterfaceType>
BackgroundThread<InterfaceType>::BackgroundThread(QObject *parent)
: BackgroundThreadBase(parent)
{
}
template <typename T>
BackgroundThread<T>::~BackgroundThread() {
template <typename InterfaceType>
BackgroundThread<InterfaceType>::~BackgroundThread() {
if (isRunning()) {
quit();
if (wait(10000))
@ -79,39 +129,27 @@ BackgroundThread<T>::~BackgroundThread() {
}
}
template <typename T>
void BackgroundThread<T>::run() {
#ifdef Q_OS_LINUX
if (io_priority_ != IOPRIO_CLASS_NONE) {
ioprio_set(IOPRIO_WHO_PROCESS, gettid(),
4 | io_priority_ << IOPRIO_CLASS_SHIFT);
}
#endif
worker_.reset(new T);
emit Initialised();
exec();
worker_.reset();
template <typename InterfaceType, typename DerivedType>
BackgroundThreadImplementation<InterfaceType, DerivedType>::
BackgroundThreadImplementation(QObject* parent)
: BackgroundThread<InterfaceType>(parent)
{
}
int BackgroundThreadBase::ioprio_set(int which, int who, int ioprio) {
#ifdef Q_OS_LINUX
return syscall(SYS_ioprio_set, which, who, ioprio);
#elif defined(Q_OS_DARWIN)
return setpriority(PRIO_DARWIN_THREAD, 0, ioprio == IOPRIO_CLASS_IDLE ? PRIO_DARWIN_BG : 0);
#else
return 0;
#endif
template <typename InterfaceType, typename DerivedType>
void BackgroundThreadImplementation<InterfaceType, DerivedType>::run() {
BackgroundThreadBase::SetIOPriority();
BackgroundThread<InterfaceType>::worker_.reset(new DerivedType);
emit BackgroundThreadBase::Initialised();
QThread::exec();
BackgroundThread<InterfaceType>::worker_.reset();
}
int BackgroundThreadBase::gettid() {
#ifdef Q_OS_LINUX
return syscall(SYS_gettid);
#else
return 0;
#endif
}
#endif // BACKGROUNDTHREAD_H

View File

@ -13,31 +13,49 @@
Library::Library(EngineBase* engine, QObject* parent)
: SimpleTreeModel<LibraryItem>(new LibraryItem(this), parent),
engine_(engine),
backend_(new BackgroundThread<LibraryBackend>(this)),
watcher_(new BackgroundThread<LibraryWatcher>(this)),
backend_factory_(new BackgroundThreadFactoryImplementation<LibraryBackendInterface, LibraryBackend>),
watcher_factory_(new BackgroundThreadFactoryImplementation<LibraryWatcher, LibraryWatcher>),
backend_(NULL),
watcher_(NULL),
dir_model_(new LibraryDirectoryModel(this)),
waiting_for_threads_(2),
artist_icon_(":artist.png"),
album_icon_(":album.png"),
no_cover_icon_(":nocover.png")
{
root_->lazy_loaded = true;
connect(backend_, SIGNAL(Initialised()), SLOT(BackendInitialised()));
connect(watcher_, SIGNAL(Initialised()), SLOT(WatcherInitialised()));
waiting_for_threads_ = 2;
}
Library::~Library() {
delete root_;
}
void Library::set_backend_factory(BackgroundThreadFactory<LibraryBackendInterface>* factory) {
backend_factory_.reset(factory);
}
void Library::set_watcher_factory(BackgroundThreadFactory<LibraryWatcher>* factory) {
watcher_factory_.reset(factory);
}
void Library::Init() {
backend_ = backend_factory_->GetThread(this);
watcher_ = watcher_factory_->GetThread(this);
connect(backend_, SIGNAL(Initialised()), SLOT(BackendInitialised()));
connect(watcher_, SIGNAL(Initialised()), SLOT(WatcherInitialised()));
}
void Library::StartThreads() {
Q_ASSERT(waiting_for_threads_);
Q_ASSERT(backend_);
Q_ASSERT(watcher_);
backend_->start();
backend_->Start();
watcher_->set_io_priority(BackgroundThreadBase::IOPRIO_CLASS_IDLE);
watcher_->start(QThread::IdlePriority);
watcher_->set_cpu_priority(QThread::IdlePriority);
watcher_->Start();
}
void Library::BackendInitialised() {
@ -375,7 +393,7 @@ void Library::LazyPopulate(LibraryItem* item) {
switch (item->type) {
case LibraryItem::Type_CompilationArtist:
foreach (const LibraryBackend::Album& album,
foreach (const LibraryBackendInterface::Album& album,
backend_->Worker()->GetCompilationAlbums(query_options_))
CreateAlbumNode(false, album.album_name, item, true, album.art_automatic, album.art_manual, album.artist);
break;
@ -386,7 +404,7 @@ void Library::LazyPopulate(LibraryItem* item) {
break;
case LibraryItem::Type_Artist:
foreach (const LibraryBackend::Album& album,
foreach (const LibraryBackendInterface::Album& album,
backend_->Worker()->GetAlbumsByArtist(item->key, query_options_))
CreateAlbumNode(false, album.album_name, item, false, album.art_automatic, album.art_manual, album.artist);
break;

View File

@ -13,6 +13,8 @@
#include "libraryitem.h"
#include "simpletreemodel.h"
#include <boost/scoped_ptr.hpp>
class LibraryDirectoryModel;
class Library : public SimpleTreeModel<LibraryItem> {
@ -29,10 +31,15 @@ class Library : public SimpleTreeModel<LibraryItem> {
Role_Artist,
};
// Useful for tests. The library takes ownership.
void set_backend_factory(BackgroundThreadFactory<LibraryBackendInterface>* factory);
void set_watcher_factory(BackgroundThreadFactory<LibraryWatcher>* factory);
void Init();
void StartThreads();
LibraryDirectoryModel* GetDirectoryModel() const { return dir_model_; }
boost::shared_ptr<LibraryBackend> GetBackend() const { return backend_->Worker(); }
boost::shared_ptr<LibraryBackendInterface> GetBackend() const { return backend_->Worker(); }
// Get information about the library
void GetChildSongs(LibraryItem* item, QList<QUrl>* urls, SongList* songs) const;
@ -52,7 +59,7 @@ class Library : public SimpleTreeModel<LibraryItem> {
void ScanStarted();
void ScanFinished();
void BackendReady(boost::shared_ptr<LibraryBackend> backend);
void BackendReady(boost::shared_ptr<LibraryBackendInterface> backend);
public slots:
void SetFilterAge(int age);
@ -96,7 +103,9 @@ class Library : public SimpleTreeModel<LibraryItem> {
private:
EngineBase* engine_;
BackgroundThread<LibraryBackend>* backend_;
boost::scoped_ptr<BackgroundThreadFactory<LibraryBackendInterface> > backend_factory_;
boost::scoped_ptr<BackgroundThreadFactory<LibraryWatcher> > watcher_factory_;
BackgroundThread<LibraryBackendInterface>* backend_;
BackgroundThread<LibraryWatcher>* watcher_;
LibraryDirectoryModel* dir_model_;

View File

@ -100,8 +100,13 @@ void LibraryBackend::SqliteLike(sqlite3_context* context, int argc, sqlite3_valu
}
}
LibraryBackendInterface::LibraryBackendInterface(QObject *parent)
: QObject(parent)
{
}
LibraryBackend::LibraryBackend(QObject* parent, const QString& database_name)
: QObject(parent),
: LibraryBackendInterface(parent),
injected_database_name_(database_name)
{
QSettings s;

View File

@ -15,11 +15,11 @@
#include "gtest/gtest_prod.h"
class LibraryBackend : public QObject {
class LibraryBackendInterface : public QObject {
Q_OBJECT
public:
LibraryBackend(QObject* parent = 0, const QString& database_name = QString());
LibraryBackendInterface(QObject* parent = 0);
struct Album {
QString artist;
@ -30,6 +30,63 @@ class LibraryBackend : public QObject {
};
typedef QList<Album> AlbumList;
// Get a list of directories in the library. Emits DirectoriesDiscovered.
virtual void LoadDirectoriesAsync() = 0;
// Counts the songs in the library. Emits TotalSongCountUpdated
virtual void UpdateTotalSongCountAsync() = 0;
virtual SongList FindSongsInDirectory(int id) = 0;
virtual QStringList GetAllArtists(const QueryOptions& opt = QueryOptions()) = 0;
virtual SongList GetSongs(const QString& artist, const QString& album, const QueryOptions& opt = QueryOptions()) = 0;
virtual bool HasCompilations(const QueryOptions& opt = QueryOptions()) = 0;
virtual SongList GetCompilationSongs(const QString& album, const QueryOptions& opt = QueryOptions()) = 0;
virtual AlbumList GetAllAlbums(const QueryOptions& opt = QueryOptions()) = 0;
virtual AlbumList GetAlbumsByArtist(const QString& artist, const QueryOptions& opt = QueryOptions()) = 0;
virtual AlbumList GetCompilationAlbums(const QueryOptions& opt = QueryOptions()) = 0;
virtual void UpdateManualAlbumArtAsync(const QString& artist, const QString& album, const QString& art) = 0;
virtual Album GetAlbumArt(const QString& artist, const QString& album) = 0;
virtual Song GetSongById(int id) = 0;
virtual void AddDirectory(const QString& path) = 0;
virtual void RemoveDirectory(const Directory& dir) = 0;
virtual void UpdateCompilationsAsync() = 0;
public slots:
virtual void LoadDirectories() = 0;
virtual void UpdateTotalSongCount() = 0;
virtual void AddOrUpdateSongs(const SongList& songs) = 0;
virtual void UpdateMTimesOnly(const SongList& songs) = 0;
virtual void DeleteSongs(const SongList& songs) = 0;
virtual void UpdateCompilations() = 0;
virtual void UpdateManualAlbumArt(const QString& artist, const QString& album, const QString& art) = 0;
virtual void ForceCompilation(const QString& artist, const QString& album, bool on) = 0;
signals:
void Error(const QString& message);
void DirectoriesDiscovered(const DirectoryList& directories);
void DirectoriesDeleted(const DirectoryList& directories);
void SongsDiscovered(const SongList& songs);
void SongsDeleted(const SongList& songs);
void TotalSongCountUpdated(int total);
};
class LibraryBackend : public LibraryBackendInterface {
Q_OBJECT
public:
LibraryBackend(QObject* parent = 0, const QString& database_name = QString());
static const int kSchemaVersion;
// This actually refers to the location of the sqlite database
@ -73,17 +130,6 @@ class LibraryBackend : public QObject {
void UpdateManualAlbumArt(const QString& artist, const QString& album, const QString& art);
void ForceCompilation(const QString& artist, const QString& album, bool on);
signals:
void Error(const QString& message);
void DirectoriesDiscovered(const DirectoryList& directories);
void DirectoriesDeleted(const DirectoryList& directories);
void SongsDiscovered(const SongList& songs);
void SongsDeleted(const SongList& songs);
void TotalSongCountUpdated(int total);
private:
struct CompilationInfo {
CompilationInfo() : has_samplers(false), has_not_samplers(false) {}

View File

@ -7,7 +7,7 @@ LibraryDirectoryModel::LibraryDirectoryModel(QObject* parent)
{
}
void LibraryDirectoryModel::SetBackend(boost::shared_ptr<LibraryBackend> backend) {
void LibraryDirectoryModel::SetBackend(boost::shared_ptr<LibraryBackendInterface> backend) {
backend_ = backend;
connect(backend_.get(), SIGNAL(DirectoriesDiscovered(DirectoryList)), SLOT(DirectoriesDiscovered(DirectoryList)));

View File

@ -8,7 +8,7 @@
#include "directory.h"
class LibraryBackend;
class LibraryBackendInterface;
class LibraryDirectoryModel : public QStandardItemModel {
Q_OBJECT
@ -16,7 +16,7 @@ class LibraryDirectoryModel : public QStandardItemModel {
public:
LibraryDirectoryModel(QObject* parent = 0);
void SetBackend(boost::shared_ptr<LibraryBackend> backend);
void SetBackend(boost::shared_ptr<LibraryBackendInterface> backend);
bool IsBackendReady() const { return backend_; }
// To be called by GUIs
@ -35,7 +35,7 @@ class LibraryDirectoryModel : public QStandardItemModel {
static const int kIdRole = Qt::UserRole + 1;
QIcon dir_icon_;
boost::shared_ptr<LibraryBackend> backend_;
boost::shared_ptr<LibraryBackendInterface> backend_;
};
#endif // LIBRARYDIRECTORYMODEL_H

View File

@ -14,7 +14,7 @@
class QFileSystemWatcher;
class QTimer;
class LibraryBackend;
class LibraryBackendInterface;
class LibraryWatcher : public QObject {
Q_OBJECT
@ -22,7 +22,7 @@ class LibraryWatcher : public QObject {
public:
LibraryWatcher(QObject* parent = 0);
void SetBackend(boost::shared_ptr<LibraryBackend> backend) { backend_ = backend; }
void SetBackend(boost::shared_ptr<LibraryBackendInterface> backend) { backend_ = backend; }
void SetEngine(EngineBase* engine) { engine_ = engine; } // TODO: shared_ptr
signals:
@ -51,7 +51,7 @@ class LibraryWatcher : public QObject {
private:
EngineBase* engine_;
boost::shared_ptr<LibraryBackend> backend_;
boost::shared_ptr<LibraryBackendInterface> backend_;
QFileSystemWatcher* fs_watcher_;
QTimer* rescan_timer_;

View File

@ -179,8 +179,8 @@ MainWindow::MainWindow(QNetworkAccessManager* network, QWidget *parent)
connect(library_, SIGNAL(TotalSongCountUpdated(int)), ui_.library_view, SLOT(TotalSongCountUpdated(int)));
connect(library_, SIGNAL(ScanStarted()), SLOT(LibraryScanStarted()));
connect(library_, SIGNAL(ScanFinished()), SLOT(LibraryScanFinished()));
connect(library_, SIGNAL(BackendReady(boost::shared_ptr<LibraryBackend>)),
cover_manager_, SLOT(SetBackend(boost::shared_ptr<LibraryBackend>)));
connect(library_, SIGNAL(BackendReady(boost::shared_ptr<LibraryBackendInterface>)),
cover_manager_, SLOT(SetBackend(boost::shared_ptr<LibraryBackendInterface>)));
// Age filters
QActionGroup* filter_age_group = new QActionGroup(this);
@ -311,6 +311,7 @@ MainWindow::MainWindow(QNetworkAccessManager* network, QWidget *parent)
show();
}
library_->Init();
library_->StartThreads();
}

View File

@ -31,10 +31,12 @@ target_link_libraries(gmock gtest)
set(TESTUTILS-SOURCES
test_utils.cpp
mock_networkaccessmanager.cpp
mock_taglib.cpp)
mock_taglib.cpp
)
set(TESTUTILS-MOC-HEADERS
mock_networkaccessmanager.h)
mock_networkaccessmanager.h
)
qt4_wrap_cpp(TESTUTILS-SOURCES-MOC ${TESTUTILS-MOC-HEADERS})
@ -45,6 +47,10 @@ add_custom_target(test
echo "Running tests"
WORKING_DIRECTORY ${CURRENT_BINARY_DIR}
)
add_custom_target(build_tests
WORKING_DIRECTORY ${CURRENT_BINARY_DIR}
)
add_dependencies(test build_tests)
# Given a file foo_test.cpp, creates a target foo_test and adds it to the test target.
macro(add_test_file test_source)
@ -57,7 +63,7 @@ macro(add_test_file test_source)
target_link_libraries(${TEST_NAME} gmock clementine_lib test_utils)
add_custom_command(TARGET test POST_BUILD
COMMAND ./${TEST_NAME})
add_dependencies(test ${TEST_NAME})
add_dependencies(build_tests ${TEST_NAME})
endmacro (add_test_file)
@ -66,3 +72,4 @@ add_test_file(song_test.cpp)
add_test_file(librarybackend_test.cpp)
add_test_file(albumcoverfetcher_test.cpp)
add_test_file(xspfparser_test.cpp)
add_test_file(library_test.cpp)

46
tests/library_test.cpp Normal file
View File

@ -0,0 +1,46 @@
#include "test_utils.h"
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "library.h"
#include "backgroundthread.h"
#include "mock_backgroundthread.h"
#include "mock_librarybackend.h"
#include <boost/scoped_ptr.hpp>
#include <QtDebug>
#include <QThread>
#include <QSignalSpy>
using ::testing::_;
using ::testing::Return;
class LibraryTest : public ::testing::Test {
protected:
virtual void SetUp() {
library_.reset(new Library(NULL, NULL));
library_->set_backend_factory(
new FakeBackgroundThreadFactory<LibraryBackendInterface, MockLibraryBackend>);
library_->set_watcher_factory(
new FakeBackgroundThreadFactory<LibraryWatcher, LibraryWatcher>);
library_->Init();
backend_ = static_cast<MockLibraryBackend*>(library_->GetBackend().get());
}
boost::scoped_ptr<Library> library_;
MockLibraryBackend* backend_;
};
TEST_F(LibraryTest, TestInitialisation) {
EXPECT_CALL(*backend_, LoadDirectoriesAsync());
EXPECT_CALL(*backend_, UpdateTotalSongCountAsync());
EXPECT_CALL(*backend_, HasCompilations(_))
.WillOnce(Return(false));
EXPECT_CALL(*backend_, GetAllArtists(_))
.WillOnce(Return(QStringList()));
library_->StartThreads();
}

View File

@ -1,13 +1,16 @@
#include <gmock/gmock.h>
#include <QApplication>
#include "resources_env.h"
#include "metatypes_env.h"
int main(int argc, char** argv) {
testing::InitGoogleMock(&argc, argv);
testing::AddGlobalTestEnvironment(new ResourcesEnvironment);
testing::AddGlobalTestEnvironment(new MetatypesEnvironment);
QApplication a(argc, argv);
testing::AddGlobalTestEnvironment(new ResourcesEnvironment);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,28 @@
#ifndef MOCK_BACKGROUNDTHREAD_H
#define MOCK_BACKGROUNDTHREAD_H
#include "backgroundthread.h"
template <typename InterfaceType, typename DerivedType>
class FakeBackgroundThread : public BackgroundThread<InterfaceType> {
public:
FakeBackgroundThread(QObject* parent) : BackgroundThread<InterfaceType>(parent) {
// We have to create the actual worker here instead of in Start() so that
// tests can set mock expectations on it before Initialised is emitted.
BackgroundThread<InterfaceType>::worker_.reset(new DerivedType);
}
void Start() {
emit BackgroundThreadBase::Initialised();
}
};
template <typename InterfaceType, typename DerivedType>
class FakeBackgroundThreadFactory : public BackgroundThreadFactory<InterfaceType> {
public:
BackgroundThread<InterfaceType>* GetThread(QObject* parent) {
return new FakeBackgroundThread<InterfaceType, DerivedType>(parent);
}
};
#endif

View File

@ -0,0 +1,58 @@
#ifndef MOCK_LIBRARYBACKEND_H
#define MOCK_LIBRARYBACKEND_H
#include "librarybackend.h"
class MockLibraryBackend : public LibraryBackendInterface {
public:
MOCK_METHOD0(LoadDirectoriesAsync,
void());
MOCK_METHOD0(UpdateTotalSongCountAsync,
void());
MOCK_METHOD1(FindSongsInDirectory,
SongList(int id));
MOCK_METHOD1(GetAllArtists,
QStringList(const QueryOptions& opt));
MOCK_METHOD3(GetSongs,
SongList(const QString& artist, const QString& album, const QueryOptions& opt));
MOCK_METHOD1(HasCompilations,
bool(const QueryOptions& opt));
MOCK_METHOD2(GetCompilationSongs,
SongList(const QString& album, const QueryOptions& opt));
MOCK_METHOD1(GetAllAlbums,
AlbumList(const QueryOptions& opt));
MOCK_METHOD2(GetAlbumsByArtist,
AlbumList(const QString& artist, const QueryOptions& opt));
MOCK_METHOD1(GetCompilationAlbums,
AlbumList(const QueryOptions& opt));
MOCK_METHOD3(UpdateManualAlbumArtAsync,
void(const QString& artist, const QString& album, const QString& art));
MOCK_METHOD2(GetAlbumArt,
Album(const QString& artist, const QString& album));
MOCK_METHOD1(GetSongById,
Song(int id));
MOCK_METHOD1(AddDirectory,
void(const QString& path));
MOCK_METHOD1(RemoveDirectory,
void(const Directory& dir));
MOCK_METHOD0(UpdateCompilationsAsync,
void());
MOCK_METHOD0(LoadDirectories,
void());
MOCK_METHOD0(UpdateTotalSongCount,
void());
MOCK_METHOD1(AddOrUpdateSongs,
void(const SongList& songs));
MOCK_METHOD1(UpdateMTimesOnly,
void(const SongList& songs));
MOCK_METHOD1(DeleteSongs,
void(const SongList& songs));
MOCK_METHOD0(UpdateCompilations,
void());
MOCK_METHOD3(UpdateManualAlbumArt,
void(const QString& artist, const QString& album, const QString& art));
MOCK_METHOD3(ForceCompilation,
void(const QString& artist, const QString& album, bool on));
};
#endif