Refactor Player and PlaylistManagers to have interfaces and add more MPRIS1 tests

This commit is contained in:
David Sansome 2011-02-13 18:37:45 +00:00
parent 12da941885
commit f801252e3d
15 changed files with 405 additions and 50 deletions

View File

@ -189,7 +189,7 @@ DBusStatus Mpris1Player::GetStatus(Engine::State state) const {
break;
}
PlaylistManager* playlists_ = player_->playlists();
PlaylistManagerInterface* playlists_ = player_->playlists();
PlaylistSequence::RepeatMode repeat_mode = playlists_->sequence()->repeat_mode();
status.random = playlists_->sequence()->shuffle_mode() == PlaylistSequence::Shuffle_Off ? 0 : 1;
@ -228,7 +228,7 @@ int Mpris1Player::GetCaps() const {
int Mpris1Player::GetCaps(Engine::State state) const {
int caps = CAN_HAS_TRACKLIST;
PlaylistItemPtr current_item = player_->GetCurrentItem();
PlaylistManager* playlists = player_->playlists();
PlaylistManagerInterface* playlists = player_->playlists();
// play is disabled when playlist is empty or when last.fm stream is already playing
if (playlists->active()->rowCount() != 0

View File

@ -36,7 +36,8 @@
using boost::shared_ptr;
Player::Player(PlaylistManager* playlists, LastFMService* lastfm, QObject* parent)
Player::Player(PlaylistManagerInterface* playlists, LastFMService* lastfm,
QObject* parent)
: PlayerInterface(parent),
playlists_(playlists),
lastfm_(lastfm),

View File

@ -30,7 +30,7 @@
#include "playlist/playlistitem.h"
class LastFMService;
class PlaylistManager;
class PlaylistManagerInterface;
class Settings;
class MainWindow;
@ -47,7 +47,7 @@ public:
virtual PlaylistItemPtr GetCurrentItem() const = 0;
virtual PlaylistItemPtr GetItemAt(int pos) const = 0;
virtual PlaylistManager* playlists() const = 0;
virtual PlaylistManagerInterface* playlists() const = 0;
public slots:
virtual void ReloadSettings() = 0;
@ -99,7 +99,8 @@ class Player : public PlayerInterface {
Q_OBJECT
public:
Player(PlaylistManager* playlists, LastFMService* lastfm, QObject* parent = 0);
Player(PlaylistManagerInterface* playlists, LastFMService* lastfm,
QObject* parent = 0);
~Player();
void Init();
@ -110,7 +111,7 @@ public:
PlaylistItemPtr GetCurrentItem() const { return current_item_; }
PlaylistItemPtr GetItemAt(int pos) const;
PlaylistManager* playlists() const { return playlists_; }
PlaylistManagerInterface* playlists() const { return playlists_; }
public slots:
void ReloadSettings();
@ -147,7 +148,7 @@ public slots:
void NextInternal(Engine::TrackChangeType);
private:
PlaylistManager* playlists_;
PlaylistManagerInterface* playlists_;
LastFMService* lastfm_;
QSettings settings_;

View File

@ -31,7 +31,7 @@
using smart_playlists::GeneratorPtr;
PlaylistManager::PlaylistManager(TaskManager* task_manager, QObject *parent)
: QObject(parent),
: PlaylistManagerInterface(parent),
task_manager_(task_manager),
playlist_backend_(NULL),
library_backend_(NULL),
@ -70,7 +70,7 @@ void PlaylistManager::Init(LibraryBackend* library_backend,
emit PlaylistManagerInitialized();
}
const QList<Playlist*> PlaylistManager::GetAllPlaylists() const {
QList<Playlist*> PlaylistManager::GetAllPlaylists() const {
QList<Playlist*> result;
foreach(const Data& data, playlists_.values()) {

View File

@ -35,7 +35,85 @@ class TaskManager;
class QModelIndex;
class QUrl;
class PlaylistManager : public QObject {
class PlaylistManagerInterface : public QObject {
Q_OBJECT
public:
PlaylistManagerInterface(QObject* parent) : QObject(parent) {}
virtual int current_id() const = 0;
virtual int active_id() const = 0;
virtual Playlist* playlist(int id) const = 0;
virtual Playlist* current() const = 0;
virtual Playlist* active() const = 0;
// Returns the collection of playlists managed by this PlaylistManager.
virtual QList<Playlist*> GetAllPlaylists() const = 0;
virtual const QItemSelection& selection(int id) const = 0;
virtual const QItemSelection& current_selection() const = 0;
virtual const QItemSelection& active_selection() const = 0;
virtual QString name(int index) const = 0;
virtual TaskManager* task_manager() const = 0;
virtual LibraryBackend* library_backend() const = 0;
virtual PlaylistBackend* playlist_backend() const = 0;
virtual PlaylistSequence* sequence() const = 0;
virtual PlaylistParser* parser() const = 0;
public slots:
virtual void New(const QString& name, const SongList& songs = SongList()) = 0;
virtual void Load(const QString& filename) = 0;
virtual void Save(int id, const QString& filename) = 0;
virtual void Rename(int id, const QString& new_name) = 0;
virtual void Remove(int id) = 0;
virtual void ChangePlaylistOrder(const QList<int>& ids) = 0;
virtual void SetCurrentPlaylist(int id) = 0;
virtual void SetActivePlaylist(int id) = 0;
virtual void SetActiveToCurrent() = 0;
virtual void SelectionChanged(const QItemSelection& selection) = 0;
// Convenience slots that defer to either current() or active()
virtual void ClearCurrent() = 0;
virtual void ShuffleCurrent() = 0;
virtual void SetActivePlaying() = 0;
virtual void SetActivePaused() = 0;
virtual void SetActiveStopped() = 0;
virtual void SetActiveStreamMetadata(const QUrl& url, const Song& song) = 0;
// Rate current song using 0.0 - 1.0 scale.
virtual void RateCurrentSong(double rating) = 0;
// Rate current song using 0 - 5 scale.
virtual void RateCurrentSong(int rating) = 0;
virtual void PlaySmartPlaylist(smart_playlists::GeneratorPtr generator, bool as_new, bool clear) = 0;
signals:
void PlaylistManagerInitialized();
void PlaylistAdded(int id, const QString& name);
void PlaylistRemoved(int id);
void PlaylistRenamed(int id, const QString& new_name);
void CurrentChanged(Playlist* new_playlist);
void ActiveChanged(Playlist* new_playlist);
void Error(const QString& message);
void SummaryTextChanged(const QString& summary);
// Forwarded from individual playlists
void CurrentSongChanged(const Song& song);
// Signals that one of manager's playlists has changed (new items, new
// ordering etc.) - the argument shows which.
void PlaylistChanged(Playlist* playlist);
void EditingFinished(const QModelIndex& index);
void PlayRequested(const QModelIndex& index);
};
class PlaylistManager : public PlaylistManagerInterface {
Q_OBJECT
public:
@ -50,7 +128,7 @@ public:
Playlist* active() const { return playlist(active_id()); }
// Returns the collection of playlists managed by this PlaylistManager.
const QList<Playlist*> GetAllPlaylists() const;
QList<Playlist*> GetAllPlaylists() const;
const QItemSelection& selection(int id) const;
const QItemSelection& current_selection() const { return selection(current_id()); }
@ -95,27 +173,6 @@ public slots:
void PlaySmartPlaylist(smart_playlists::GeneratorPtr generator, bool as_new, bool clear);
signals:
void PlaylistManagerInitialized();
void PlaylistAdded(int id, const QString& name);
void PlaylistRemoved(int id);
void PlaylistRenamed(int id, const QString& new_name);
void CurrentChanged(Playlist* new_playlist);
void ActiveChanged(Playlist* new_playlist);
void Error(const QString& message);
void SummaryTextChanged(const QString& summary);
// Forwarded from individual playlists
void CurrentSongChanged(const Song& song);
// Signals that one of manager's playlists has changed (new items, new
// ordering etc.) - the argument shows which.
void PlaylistChanged(Playlist* playlist);
void EditingFinished(const QModelIndex& index);
void PlayRequested(const QModelIndex& index);
private slots:
void OneOfPlaylistsChanged();
void UpdateSummaryText();

View File

@ -10,7 +10,7 @@ public:
PlaylistItemPtr GetCurrentItem() const;
PlaylistItemPtr GetItemAt(int pos) const;
PlaylistManager* playlists() const;
PlaylistManagerInterface* playlists() const;
public slots:
// Manual track change to the specified track

View File

@ -1,4 +1,4 @@
class PlaylistManager : QObject {
class PlaylistManagerInterface : QObject {
%TypeHeaderCode
#include "playlist/playlistmanager.h"
@ -73,5 +73,5 @@ signals:
void PlayRequested(const QModelIndex& index);
private:
PlaylistManager();
PlaylistManagerInterface();
};

View File

@ -133,7 +133,7 @@ Script* PythonEngine::CreateScript(const ScriptInfo& info) {
AddObject(manager()->data().library_->backend(), sipType_LibraryBackend, "library");
AddObject(manager()->data().library_view_, sipType_LibraryView, "library_view");
AddObject(manager()->data().player_, sipType_PlayerInterface, "player");
AddObject(manager()->data().playlists_, sipType_PlaylistManager, "playlists");
AddObject(manager()->data().playlists_, sipType_PlaylistManagerInterface, "playlists");
AddObject(manager()->data().radio_model_, sipType_RadioModel, "radio_model");
AddObject(manager()->data().settings_dialog_, sipType_SettingsDialog, "settings_dialog");
AddObject(manager()->data().task_manager_, sipType_TaskManager, "task_manager");

View File

@ -18,7 +18,7 @@ class ScriptInterface : QObject {
CLASS(ParserBase),
CLASS(PlayerInterface),
CLASS(Playlist),
CLASS(PlaylistManager),
CLASS(PlaylistManagerInterface),
CLASS(PlaylistParser),
CLASS(PlaylistSequence),
CLASS(Queue),

View File

@ -42,7 +42,6 @@ set(TESTUTILS-SOURCES
test_utils.cpp
mock_networkaccessmanager.cpp
mock_taglib.cpp
mock_player.cpp
mock_playlistitem.cpp
)
@ -115,7 +114,7 @@ add_test_file(organiseformat_test.cpp false)
add_test_file(playlist_test.cpp true)
add_test_file(plsparser_test.cpp false)
add_test_file(python_test.cpp true)
add_test_file(mpris1_test.cpp false)
add_test_file(mpris1_test.cpp true)
add_test_file(scopedtransaction_test.cpp false)
add_test_file(songloader_test.cpp false)
add_test_file(songplaylistitem_test.cpp false)

48
tests/mock_engine.h Normal file
View File

@ -0,0 +1,48 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine 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.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MOCK_ENGINE_H
#define MOCK_ENGINE_H
#include <gmock/gmock.h>
#include "engines/enginebase.h"
class MockEngine : public Engine::Base {
public:
MOCK_METHOD0(Init, bool());
MOCK_METHOD1(CanDecode, bool(const QUrl&));
MOCK_METHOD1(StartPreloading, void(const QUrl&));
MOCK_METHOD1(Play, bool(quint64));
MOCK_METHOD0(Stop, void());
MOCK_METHOD0(Pause, void());
MOCK_METHOD0(Unpause, void());
MOCK_METHOD1(Seek, void(quint64));
MOCK_METHOD1(AddBackgroundStream, int(const QUrl&));
MOCK_METHOD1(StopBackgroundStream, void(int));
MOCK_METHOD2(SetBackgroundStreamVolume, void(int, int));
MOCK_CONST_METHOD0(state, Engine::State());
MOCK_CONST_METHOD0(position_nanosec, qint64());
MOCK_CONST_METHOD0(length_nanosec, qint64());
MOCK_METHOD1(SetVolumeSW, void(uint));
};
#endif // MOCK_ENGINE_H

View File

@ -1,5 +0,0 @@
#include "mock_player.h"
MockPlayer::MockPlayer()
{
}

View File

@ -27,7 +27,7 @@
class MockPlayer : public PlayerInterface {
public:
MockPlayer();
MockPlayer() {}
MOCK_CONST_METHOD0(engine, EngineBase*());
MOCK_CONST_METHOD0(GetState, Engine::State());
@ -35,7 +35,7 @@ public:
MOCK_CONST_METHOD0(GetCurrentItem, PlaylistItemPtr());
MOCK_CONST_METHOD1(GetItemAt, PlaylistItemPtr(int));
MOCK_CONST_METHOD0(playlists, PlaylistManager*());
MOCK_CONST_METHOD0(playlists, PlaylistManagerInterface*());
MOCK_METHOD0(ReloadSettings, void());

View File

@ -0,0 +1,76 @@
/* This file is part of Clementine.
Copyright 2010, David Sansome <me@davidsansome.com>
Clementine 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.
Clementine 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 Clementine. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MOCK_PLAYLISTMANAGER_H
#define MOCK_PLAYLISTMANAGER_H
#include "playlist/playlistmanager.h"
class MockPlaylistManager : public PlaylistManagerInterface {
public:
MockPlaylistManager(QObject* parent = NULL) : PlaylistManagerInterface(parent) {}
MOCK_CONST_METHOD0(current_id, int());
MOCK_CONST_METHOD0(active_id, int());
MOCK_CONST_METHOD1(playlist, Playlist*(int));
MOCK_CONST_METHOD0(current, Playlist*());
MOCK_CONST_METHOD0(active, Playlist*());
MOCK_CONST_METHOD0(GetAllPlaylists, QList<Playlist*>());
MOCK_CONST_METHOD1(selection, const QItemSelection&(int));
MOCK_CONST_METHOD0(current_selection, const QItemSelection&());
MOCK_CONST_METHOD0(active_selection, const QItemSelection&());
MOCK_CONST_METHOD1(name, QString(int));
MOCK_CONST_METHOD0(task_manager, TaskManager*());
MOCK_CONST_METHOD0(library_backend, LibraryBackend*());
MOCK_CONST_METHOD0(playlist_backend, PlaylistBackend*());
MOCK_CONST_METHOD0(sequence, PlaylistSequence*());
MOCK_CONST_METHOD0(parser, PlaylistParser*());
MOCK_METHOD2(New, void(const QString&, const SongList&));
MOCK_METHOD1(Load, void(const QString&));
MOCK_METHOD2(Save, void(int, const QString&));
MOCK_METHOD2(Rename, void(int, const QString&));
MOCK_METHOD1(Remove, void(int));
MOCK_METHOD1(ChangePlaylistOrder, void(const QList<int>&));
MOCK_METHOD1(SetCurrentPlaylist, void(int));
MOCK_METHOD1(SetActivePlaylist, void(int));
MOCK_METHOD0(SetActiveToCurrent, void());
MOCK_METHOD1(SelectionChanged, void(const QItemSelection&));
MOCK_METHOD0(ClearCurrent, void());
MOCK_METHOD0(ShuffleCurrent, void());
MOCK_METHOD0(SetActivePlaying, void());
MOCK_METHOD0(SetActivePaused, void());
MOCK_METHOD0(SetActiveStopped, void());
MOCK_METHOD2(SetActiveStreamMetadata, void(const QUrl&, const Song&));
MOCK_METHOD1(RateCurrentSong, void(double));
MOCK_METHOD1(RateCurrentSong, void(int));
MOCK_METHOD3(PlaySmartPlaylist, void(smart_playlists::GeneratorPtr, bool, bool));
void EmitPlaylistManagerInitialized() {
emit PlaylistManagerInitialized();
}
};
#endif // MOCK_PLAYLISTMANAGER_H

View File

@ -18,6 +18,8 @@
#include "core/encoding.h"
#include "core/mpris1.h"
#include "core/song.h"
#include "playlist/playlistmanager.h"
#include "playlist/playlistsequence.h"
#include "radio/fixlastfm.h"
#include <lastfm/Track>
@ -25,20 +27,31 @@
#include "gtest/gtest.h"
#include "test_utils.h"
#include "mock_engine.h"
#include "mock_player.h"
#include "mock_playlistmanager.h"
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QSignalSpy>
#include <QTemporaryFile>
#include <QTextCodec>
#include <id3v2tag.h>
using ::testing::_;
using ::testing::Return;
namespace {
class Mpris1Test : public ::testing::Test {
class Mpris1BasicTest : public ::testing::Test {
protected:
void SetUp() {
sequence_.reset(new PlaylistSequence);
EXPECT_CALL(player_, engine()).WillRepeatedly(Return(&engine_));
EXPECT_CALL(player_, playlists()).WillRepeatedly(Return(&playlists_));
EXPECT_CALL(playlists_, sequence()).WillRepeatedly(Return(sequence_.get()));
}
QString service_name() const {
@ -46,11 +59,14 @@ protected:
QString::number(QCoreApplication::applicationPid());
}
MockEngine engine_;
MockPlayer player_;
MockPlaylistManager playlists_;
boost::scoped_ptr<PlaylistSequence> sequence_;
};
TEST_F(Mpris1Test, CreatesDBusService) {
TEST_F(Mpris1BasicTest, CreatesDBusService) {
EXPECT_FALSE(QDBusConnection::sessionBus().interface()->
isServiceRegistered(service_name()));
@ -64,5 +80,167 @@ TEST_F(Mpris1Test, CreatesDBusService) {
isServiceRegistered(service_name()));
}
class Mpris1Test : public Mpris1BasicTest {
protected:
void SetUp() {
Mpris1BasicTest::SetUp();
mpris_.reset(new mpris::Mpris1(&player_, NULL, NULL, service_name()));
}
boost::scoped_ptr<mpris::Mpris1> mpris_;
};
TEST_F(Mpris1Test, CorrectNameAndVersion) {
QCoreApplication::setApplicationName("Banana");
QCoreApplication::setApplicationVersion("Cheese");
EXPECT_EQ("Banana Cheese", mpris_->root()->Identity());
Version version = mpris_->root()->MprisVersion();
EXPECT_EQ(1, version.major);
EXPECT_EQ(0, version.minor);
}
TEST_F(Mpris1Test, Mutes) {
EXPECT_CALL(player_, Mute());
mpris_->player()->Mute();
}
TEST_F(Mpris1Test, GetsVolume) {
EXPECT_CALL(player_, GetVolume()).WillOnce(Return(50));
EXPECT_EQ(50, mpris_->player()->VolumeGet());
}
TEST_F(Mpris1Test, SetsVolume) {
EXPECT_CALL(player_, SetVolume(42));
mpris_->player()->VolumeSet(42);
}
TEST_F(Mpris1Test, RaisesVolume) {
EXPECT_CALL(player_, GetVolume()).WillOnce(Return(50));
EXPECT_CALL(player_, SetVolume(51));
mpris_->player()->VolumeUp(1);
}
TEST_F(Mpris1Test, LowersVolume) {
EXPECT_CALL(player_, GetVolume()).WillOnce(Return(50));
EXPECT_CALL(player_, SetVolume(49));
mpris_->player()->VolumeDown(1);
}
TEST_F(Mpris1Test, Pauses) {
EXPECT_CALL(player_, Pause());
mpris_->player()->Pause();
}
TEST_F(Mpris1Test, Stops) {
EXPECT_CALL(player_, Stop());
mpris_->player()->Stop();
}
TEST_F(Mpris1Test, Plays) {
EXPECT_CALL(player_, Play());
mpris_->player()->Play();
}
TEST_F(Mpris1Test, GoesPrevious) {
EXPECT_CALL(player_, Previous());
mpris_->player()->Prev();
}
TEST_F(Mpris1Test, GoesNext) {
EXPECT_CALL(player_, Next());
mpris_->player()->Next();
}
TEST_F(Mpris1Test, SetsPosition) {
EXPECT_CALL(player_, SeekTo(42));
mpris_->player()->PositionSet(42000);
}
TEST_F(Mpris1Test, GetsStatus) {
// Engine statuses
EXPECT_CALL(player_, GetState()).WillOnce(Return(Engine::Empty));
DBusStatus status = mpris_->player()->GetStatus();
EXPECT_EQ(DBusStatus::Mpris_Stopped, status.play);
EXPECT_CALL(player_, GetState()).WillOnce(Return(Engine::Idle));
status = mpris_->player()->GetStatus();
EXPECT_EQ(DBusStatus::Mpris_Stopped, status.play);
EXPECT_CALL(player_, GetState()).WillOnce(Return(Engine::Paused));
status = mpris_->player()->GetStatus();
EXPECT_EQ(DBusStatus::Mpris_Paused, status.play);
EXPECT_CALL(player_, GetState()).WillOnce(Return(Engine::Playing));
status = mpris_->player()->GetStatus();
EXPECT_EQ(DBusStatus::Mpris_Playing, status.play);
EXPECT_CALL(player_, GetState()).WillRepeatedly(Return(Engine::Empty));
// Repeat modes
sequence_->SetRepeatMode(PlaylistSequence::Repeat_Off);
status = mpris_->player()->GetStatus();
EXPECT_EQ(0, status.repeat);
EXPECT_EQ(0, status.repeat_playlist);
sequence_->SetRepeatMode(PlaylistSequence::Repeat_Album);
status = mpris_->player()->GetStatus();
EXPECT_EQ(0, status.repeat);
EXPECT_EQ(1, status.repeat_playlist);
sequence_->SetRepeatMode(PlaylistSequence::Repeat_Playlist);
status = mpris_->player()->GetStatus();
EXPECT_EQ(0, status.repeat);
EXPECT_EQ(1, status.repeat_playlist);
sequence_->SetRepeatMode(PlaylistSequence::Repeat_Track);
status = mpris_->player()->GetStatus();
EXPECT_EQ(1, status.repeat);
EXPECT_EQ(1, status.repeat_playlist);
// Shuffle modes
sequence_->SetShuffleMode(PlaylistSequence::Shuffle_Off);
status = mpris_->player()->GetStatus();
EXPECT_EQ(0, status.random);
sequence_->SetShuffleMode(PlaylistSequence::Shuffle_Album);
status = mpris_->player()->GetStatus();
EXPECT_EQ(1, status.random);
sequence_->SetShuffleMode(PlaylistSequence::Shuffle_All);
status = mpris_->player()->GetStatus();
EXPECT_EQ(1, status.random);
}
TEST_F(Mpris1Test, HandlesShuffleModeChanged) {
sequence_->SetShuffleMode(PlaylistSequence::Shuffle_Off);
EXPECT_CALL(player_, GetState()).WillRepeatedly(Return(Engine::Empty));
QSignalSpy spy(mpris_->player(), SIGNAL(StatusChange(DBusStatus)));
playlists_.EmitPlaylistManagerInitialized();
EXPECT_EQ(0, spy.count());
sequence_->SetShuffleMode(PlaylistSequence::Shuffle_All);
ASSERT_EQ(1, spy.count());
EXPECT_EQ(1, spy[0][0].value<DBusStatus>().random);
spy.clear();
sequence_->SetShuffleMode(PlaylistSequence::Shuffle_All);
ASSERT_EQ(0, spy.count());
sequence_->SetShuffleMode(PlaylistSequence::Shuffle_Album);
ASSERT_EQ(1, spy.count());
EXPECT_EQ(1, spy[0][0].value<DBusStatus>().random);
spy.clear();
sequence_->SetShuffleMode(PlaylistSequence::Shuffle_Off);
ASSERT_EQ(1, spy.count());
EXPECT_EQ(0, spy[0][0].value<DBusStatus>().random);
spy.clear();
}
} // namespace