From 3296bf7b7da2e854efd37f50cf0ecc1538d19c86 Mon Sep 17 00:00:00 2001 From: David Sansome Date: Tue, 2 Mar 2010 23:37:12 +0000 Subject: [PATCH] LibraryBackend tests should test at a higher level --- src/librarybackend.cpp | 19 +-- src/librarybackend.h | 7 +- src/song.h | 4 + tests/CMakeLists.txt | 3 +- tests/librarybackend_test.cpp | 222 +++++++++++++--------------------- tests/main.cpp | 11 ++ tests/resources_env.h | 16 +++ 7 files changed, 132 insertions(+), 150 deletions(-) create mode 100644 tests/main.cpp create mode 100644 tests/resources_env.h diff --git a/src/librarybackend.cpp b/src/librarybackend.cpp index a088ecc15..0d0bc2e92 100644 --- a/src/librarybackend.cpp +++ b/src/librarybackend.cpp @@ -14,18 +14,18 @@ const char* LibraryBackend::kDatabaseName = "clementine.db"; const int LibraryBackend::kSchemaVersion = 2; -LibraryBackend::LibraryBackend(QObject* parent, QSqlDriver* driver) +LibraryBackend::LibraryBackend(QObject* parent, const QString& database_name) : QObject(parent), - injected_driver_(driver) + injected_database_name_(database_name) { QSettings s; s.beginGroup("Library"); - directory_ = s.value("database_directory", DefaultDirectory()).toString(); + directory_ = s.value("database_directory", DefaultDatabaseDirectory()).toString(); Connect(); } -QString LibraryBackend::DefaultDirectory() { +QString LibraryBackend::DefaultDatabaseDirectory() { QDir ret(QDir::homePath() + "/.config/" + QCoreApplication::organizationName()); return QDir::toNativeSeparators(ret.path()); } @@ -49,12 +49,13 @@ QSqlDatabase LibraryBackend::Connect() { return db; } - if (injected_driver_) { - db = QSqlDatabase::addDatabase(injected_driver_, connection_id); - } else { - db = QSqlDatabase::addDatabase("QSQLITE", connection_id); + db = QSqlDatabase::addDatabase("QSQLITE", connection_id); + + if (!injected_database_name_.isNull()) + db.setDatabaseName(injected_database_name_); + else db.setDatabaseName(directory_ + "/" + kDatabaseName); - } + if (!db.open()) { emit Error("LibraryBackend: " + db.lastError().text()); return db; diff --git a/src/librarybackend.h b/src/librarybackend.h index f63fd9be2..a68da0887 100644 --- a/src/librarybackend.h +++ b/src/librarybackend.h @@ -15,7 +15,7 @@ class LibraryBackend : public QObject { Q_OBJECT public: - LibraryBackend(QObject* parent = 0, QSqlDriver* driver = 0); + LibraryBackend(QObject* parent = 0, const QString& database_name = QString()); struct Album { QString artist; @@ -27,7 +27,7 @@ class LibraryBackend : public QObject { typedef QList AlbumList; // This actually refers to the location of the sqlite database - static QString DefaultDirectory(); + static QString DefaultDatabaseDirectory(); // Get a list of directories in the library. Emits DirectoriesDiscovered. void LoadDirectoriesAsync(); @@ -107,7 +107,8 @@ class LibraryBackend : public QObject { QString directory_; QMutex connect_mutex_; - QSqlDriver* injected_driver_; + // Used by tests + QString injected_database_name_; }; #endif // LIBRARYBACKEND_H diff --git a/src/song.h b/src/song.h index 2e6f00786..b455e80a1 100644 --- a/src/song.h +++ b/src/song.h @@ -142,6 +142,10 @@ class Song { void set_art_manual(const QString& v) { d->art_manual_ = v; } void set_image(const QImage& i) { d->image_ = i; } + // Setters that should only be used by tests + void set_filename(const QString& v) { d->filename_ = v; } + void set_directory_id(int v) { d->directory_id_ = v; } + // Comparison functions bool IsMetadataEqual(const Song& other) const; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 82b78edc3..89a278b91 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -39,7 +39,8 @@ macro(add_test_file test_source) get_filename_component(TEST_NAME ${ARGV0} NAME_WE) add_executable(${TEST_NAME} ${ARGV0} - ../3rdparty/gmock/src/gmock_main.cc) + main.cpp + ) target_link_libraries(${TEST_NAME} gmock clementine_lib) add_custom_command(TARGET test POST_BUILD COMMAND ./${TEST_NAME}) diff --git a/tests/librarybackend_test.cpp b/tests/librarybackend_test.cpp index da8b5be9d..dbd29aa52 100644 --- a/tests/librarybackend_test.cpp +++ b/tests/librarybackend_test.cpp @@ -1,9 +1,9 @@ -#include "gmock/gmock-printers.h" #include "test_utils.h" #include "gtest/gtest.h" -#include "mock_sqldriver.h" +#include "gmock/gmock.h" #include "librarybackend.h" +#include "song.h" #include @@ -15,167 +15,115 @@ using ::testing::AtMost; using ::testing::Invoke; using ::testing::Return; -using boost::scoped_ptr; - void PrintTo(const ::QString& str, std::ostream& os) { os << str.toStdString(); } class LibraryBackendTest : public ::testing::Test { protected: - LibraryBackendTest() - : current_result_(0) { - } - virtual void SetUp() { - // Owned by QSqlDatabase. - driver_ = new MockSqlDriver; - // DB connect calls. - EXPECT_CALL(*driver_, open(_, _, _, _, _, _)).WillOnce(Return(true)); - EXPECT_CALL(*driver_, isOpen()).WillRepeatedly(Return(true)); - EXPECT_CALL(*driver_, hasFeature(_)).WillRepeatedly(Return(true)); + backend_.reset(new LibraryBackend(NULL, ":memory:")); - QStringList tables; - tables << "Foo"; - EXPECT_CALL(*driver_, tables(QSql::Tables)).WillOnce(Return(tables)); - - MockSqlResult* result = new MockSqlResult(driver_); - EXPECT_CALL(*driver_, createResult()). - WillOnce(Return(result)); - EXPECT_CALL(*result, reset(QString("SELECT version FROM schema_version"))).WillOnce( - DoAll( - Invoke(result, &MockSqlResult::hackSetActive), - Return(true))); - EXPECT_CALL(*result, fetch(1)).WillOnce( - DoAll( - Invoke(result, &MockSqlResult::setAt), - Return(true))); - EXPECT_CALL(*result, data(0)).WillOnce(Return(QVariant(2))); - - EXPECT_CALL(*driver_, close()); - - backend_.reset(new LibraryBackend(NULL, driver_)); + connection_name_ = "thread_" + QString::number( + reinterpret_cast(QThread::currentThread())); + database_ = QSqlDatabase::database(connection_name_); } void TearDown() { // Make sure Qt does not re-use the connection. - QSqlDatabase::removeDatabase("thread_" + QString::number( - reinterpret_cast(QThread::currentThread()))); + database_ = QSqlDatabase(); + QSqlDatabase::removeDatabase(connection_name_); } - // Call this to mock a single query with one row of results. - void ExpectQuery(const QString& query, - const QList& bind_values, - const QMap& named_bind_values, - const QList& columns) { - // Owned by QSqlDatabase. - current_result_ = new MockSqlResult(driver_); - EXPECT_CALL(*driver_, createResult()). - WillOnce(Return(current_result_)); - - // Query string is set. - EXPECT_CALL(*current_result_, reset(QString(query))).WillOnce( - DoAll( - Invoke(current_result_, &MockSqlResult::hackSetActive), - Return(true))); - // Query is executed. - EXPECT_CALL(*current_result_, exec()).WillOnce(Return(true)); - - // Values are bound. - for (int i = 0; i < bind_values.size(); ++i) { - ExpectBind(i, bind_values[i]); - } - for (QMap::const_iterator it = named_bind_values.begin(); - it != named_bind_values.end(); ++it) { - ExpectBind(it.key(), it.value()); - } - - // One row is fetched. - EXPECT_CALL(*current_result_, fetch(1)).WillOnce( - DoAll( - Invoke(current_result_, &MockSqlResult::setAt), - Return(true))); - // Tries to fetch second row but we say we do not have any more rows. - // Can be called 0-1 times. This catches the case where a query is only expected - // to have one row so QSqlQuery::next() is only called once. - EXPECT_CALL(*current_result_, fetch(2)).Times(AtMost(1)).WillOnce(Return(false)); - - // Expect data() calls for each column. - ExpectData(columns); + 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_filename("foo.mp3"); + ret.set_mtime(0); + ret.set_ctime(0); + ret.set_filesize(0); + return ret; } - void ExpectBind(int index, const QVariant& value) { - EXPECT_CALL(*current_result_, bindValue(index, value, _)); - } - - void ExpectBind(const QString& bind, const QVariant& value) { - EXPECT_CALL(*current_result_, bindValue(bind, value, _)); - } - - void ExpectData(const QList& columns) { - for (int i = 0; i < columns.size(); ++i) { - EXPECT_CALL(*current_result_, data(i)).WillRepeatedly( - Return(columns[i])).RetiresOnSaturation(); - } - } - - MockSqlDriver* driver_; - scoped_ptr backend_; - - MockSqlResult* current_result_; + boost::scoped_ptr backend_; + QString connection_name_; + QSqlDatabase database_; }; TEST_F(LibraryBackendTest, DatabaseInitialises) { + // Check that these tables exist + QStringList tables = database_.tables(); + EXPECT_TRUE(tables.contains("songs")); + EXPECT_TRUE(tables.contains("directories")); + ASSERT_TRUE(tables.contains("schema_version")); + // Check the schema version is correct + QSqlQuery q("SELECT version FROM schema_version", database_); + ASSERT_TRUE(q.exec()); + ASSERT_TRUE(q.next()); + EXPECT_EQ(2, q.value(0).toInt()); + EXPECT_FALSE(q.next()); } -TEST_F(LibraryBackendTest, GetSongsSuccessfully) { - const char* query = - "SELECT ROWID, title, album, artist, albumartist, composer, " - "track, disc, bpm, year, genre, comment, compilation, " - "length, bitrate, samplerate, directory, filename, " - "mtime, ctime, filesize, sampler, art_automatic, art_manual" - " FROM songs " - "WHERE (compilation = 0 AND sampler = 0) AND artist = ?" - " AND album = ?"; - QList bind_values; - bind_values << "foo"; - bind_values << "bar"; +TEST_F(LibraryBackendTest, EmptyDatabase) { + // Check the database is empty to start with + QStringList artists = backend_->GetAllArtists(); + EXPECT_TRUE(artists.isEmpty()); - QList columns; - for (int i = 0; i < 24; ++i) { - // Fill every column with 42. - columns << 42; - } - - ExpectQuery(query, bind_values, QMap(), columns); - - SongList songs = backend_->GetSongs("foo", "bar"); - ASSERT_EQ(1, songs.length()); - EXPECT_EQ(42, songs[0].length()); - EXPECT_EQ(42, songs[0].id()); + LibraryBackend::AlbumList albums = backend_->GetAllAlbums(); + EXPECT_TRUE(albums.isEmpty()); } -TEST_F(LibraryBackendTest, GetSongByIdSuccessfully) { - const char* query = - "SELECT ROWID, title, album, artist, albumartist, composer, " - "track, disc, bpm, year, genre, comment, compilation, " - "length, bitrate, samplerate, directory, filename, " - "mtime, ctime, filesize, sampler, art_automatic, art_manual" - " FROM songs " - "WHERE ROWID = :id"; - QMap bind_values; - bind_values[":id"] = 42; +TEST_F(LibraryBackendTest, AddSong) { + // Add a directory + backend_->AddDirectory("/test"); - QList columns; - for (int i = 0; i < 24; ++i) { - // Fill every column with 42. - columns << 42; - } + // Add the song + Song s = MakeDummySong(1); + s.set_title("Foo"); + s.set_artist("Bar"); + s.set_album("Meep"); + backend_->AddOrUpdateSongs(SongList() << s); - ExpectQuery(query, QList(), bind_values, columns); + // Check the artist + QStringList artists = backend_->GetAllArtists(); + ASSERT_EQ(1, artists.size()); + EXPECT_EQ(s.artist(), artists[0]); - Song song = backend_->GetSongById(42); - EXPECT_EQ(42, song.length()); - EXPECT_EQ(42, song.id()); + // Check the various album getters + LibraryBackend::AlbumList albums = backend_->GetAllAlbums(); + ASSERT_EQ(1, albums.size()); + EXPECT_EQ(s.album(), albums[0].album_name); + EXPECT_EQ(s.artist(), albums[0].artist); + + albums = backend_->GetAlbumsByArtist("Bar"); + ASSERT_EQ(1, albums.size()); + EXPECT_EQ(s.album(), albums[0].album_name); + EXPECT_EQ(s.artist(), albums[0].artist); + + LibraryBackend::Album album = backend_->GetAlbumArt("Bar", "Meep"); + EXPECT_EQ(s.album(), album.album_name); + EXPECT_EQ(s.artist(), album.artist); + + // Check we can get the song back + SongList songs = backend_->GetSongs("Bar", "Meep"); + ASSERT_EQ(1, songs.size()); + EXPECT_EQ(s.album(), songs[0].album()); + EXPECT_EQ(s.artist(), songs[0].artist()); + EXPECT_EQ(s.title(), songs[0].title()); + EXPECT_EQ(1, songs[0].id()); + + // Check we can get the song by ID + Song song2 = backend_->GetSongById(1); + EXPECT_EQ(s.album(), song2.album()); + EXPECT_EQ(s.artist(), song2.artist()); // This is an error - song2.artist() should obviously be "Blur" + EXPECT_EQ(s.title(), song2.title()); + EXPECT_EQ(1, song2.id()); +} + +TEST_F(LibraryBackendTest, AddSongWithoutFilename) { +} + +TEST_F(LibraryBackendTest, GetAlbumArtNonExistent) { } diff --git a/tests/main.cpp b/tests/main.cpp new file mode 100644 index 000000000..92f16d7f7 --- /dev/null +++ b/tests/main.cpp @@ -0,0 +1,11 @@ +#include + +#include "resources_env.h" + +int main(int argc, char** argv) { + testing::InitGoogleMock(&argc, argv); + + testing::AddGlobalTestEnvironment(new ResourcesEnvironment); + + return RUN_ALL_TESTS(); +} diff --git a/tests/resources_env.h b/tests/resources_env.h new file mode 100644 index 000000000..4f0d661d4 --- /dev/null +++ b/tests/resources_env.h @@ -0,0 +1,16 @@ +#ifndef RESOURCES_ENV_H +#define RESOURCES_ENV_H + +#include + +#include + +class ResourcesEnvironment : public ::testing::Environment { +public: + void SetUp() { + Q_INIT_RESOURCE(data); + Q_INIT_RESOURCE(translations); + } +}; + +#endif // RESOURCES_ENV_H