Adds a test for LibraryBackend (not as trivial as it sounds :-)
Adds Googlemock. Adds mock Qt database drivers.
This commit is contained in:
parent
dae28e10ab
commit
864215cb23
@ -171,7 +171,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
|
|||||||
|
|
||||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/qtsingleapplication")
|
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/qtsingleapplication")
|
||||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/qxt")
|
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/qxt")
|
||||||
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/gtest/include")
|
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/gmock/gtest/include")
|
||||||
|
|
||||||
set(EXECUTABLE_OUTPUT_PATH ..)
|
set(EXECUTABLE_OUTPUT_PATH ..)
|
||||||
add_library(clementine_lib
|
add_library(clementine_lib
|
||||||
|
@ -14,8 +14,9 @@
|
|||||||
const char* LibraryBackend::kDatabaseName = "clementine.db";
|
const char* LibraryBackend::kDatabaseName = "clementine.db";
|
||||||
const int LibraryBackend::kSchemaVersion = 2;
|
const int LibraryBackend::kSchemaVersion = 2;
|
||||||
|
|
||||||
LibraryBackend::LibraryBackend(QObject* parent)
|
LibraryBackend::LibraryBackend(QObject* parent, QSqlDriver* driver)
|
||||||
: QObject(parent)
|
: QObject(parent),
|
||||||
|
injected_driver_(driver)
|
||||||
{
|
{
|
||||||
QSettings s;
|
QSettings s;
|
||||||
s.beginGroup("Library");
|
s.beginGroup("Library");
|
||||||
@ -48,8 +49,12 @@ QSqlDatabase LibraryBackend::Connect() {
|
|||||||
return db;
|
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);
|
||||||
db.setDatabaseName(directory_ + "/" + kDatabaseName);
|
db.setDatabaseName(directory_ + "/" + kDatabaseName);
|
||||||
|
}
|
||||||
if (!db.open()) {
|
if (!db.open()) {
|
||||||
emit Error("LibraryBackend: " + db.lastError().text());
|
emit Error("LibraryBackend: " + db.lastError().text());
|
||||||
return db;
|
return db;
|
||||||
|
@ -15,7 +15,7 @@ class LibraryBackend : public QObject {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LibraryBackend(QObject* parent = 0);
|
LibraryBackend(QObject* parent = 0, QSqlDriver* driver = 0);
|
||||||
|
|
||||||
struct Album {
|
struct Album {
|
||||||
QString artist;
|
QString artist;
|
||||||
@ -106,6 +106,8 @@ class LibraryBackend : public QObject {
|
|||||||
|
|
||||||
QString directory_;
|
QString directory_;
|
||||||
QMutex connect_mutex_;
|
QMutex connect_mutex_;
|
||||||
|
|
||||||
|
QSqlDriver* injected_driver_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LIBRARYBACKEND_H
|
#endif // LIBRARYBACKEND_H
|
||||||
|
@ -1,22 +1,34 @@
|
|||||||
cmake_minimum_required(VERSION 2.6)
|
cmake_minimum_required(VERSION 2.6)
|
||||||
|
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/gtest)
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/gmock/include)
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/gtest/include)
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/gmock/gtest/include)
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../3rdparty/gmock/gtest)
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src)
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../src)
|
||||||
include_directories(${CMAKE_CURRENT_BINARY_DIR}/../src)
|
include_directories(${CMAKE_CURRENT_BINARY_DIR}/../src)
|
||||||
|
|
||||||
set(GTEST-SOURCES
|
set(GTEST-SOURCES
|
||||||
../3rdparty/gtest/src/gtest.cc
|
../3rdparty/gmock/gtest/src/gtest.cc
|
||||||
../3rdparty/gtest/src/gtest-death-test.cc
|
../3rdparty/gmock/gtest/src/gtest-death-test.cc
|
||||||
../3rdparty/gtest/src/gtest-filepath.cc
|
../3rdparty/gmock/gtest/src/gtest-filepath.cc
|
||||||
../3rdparty/gtest/src/gtest-port.cc
|
../3rdparty/gmock/gtest/src/gtest-port.cc
|
||||||
../3rdparty/gtest/src/gtest-test-part.cc
|
../3rdparty/gmock/gtest/src/gtest-test-part.cc
|
||||||
../3rdparty/gtest/src/gtest-typed-test.cc
|
../3rdparty/gmock/gtest/src/gtest-typed-test.cc
|
||||||
test_utils.cpp
|
test_utils.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_library(gtest ${GTEST-SOURCES})
|
add_library(gtest ${GTEST-SOURCES})
|
||||||
|
|
||||||
|
set(GMOCK-SOURCES
|
||||||
|
../3rdparty/gmock/src/gmock-cardinalities.cc
|
||||||
|
../3rdparty/gmock/src/gmock.cc
|
||||||
|
../3rdparty/gmock/src/gmock-internal-utils.cc
|
||||||
|
../3rdparty/gmock/src/gmock-matchers.cc
|
||||||
|
../3rdparty/gmock/src/gmock-printers.cc
|
||||||
|
../3rdparty/gmock/src/gmock-spec-builders.cc)
|
||||||
|
|
||||||
|
add_library(gmock ${GMOCK-SOURCES})
|
||||||
|
target_link_libraries(gmock gtest)
|
||||||
|
|
||||||
add_custom_target(test
|
add_custom_target(test
|
||||||
echo "Running tests"
|
echo "Running tests"
|
||||||
WORKING_DIRECTORY ${CURRENT_BINARY_DIR}
|
WORKING_DIRECTORY ${CURRENT_BINARY_DIR}
|
||||||
@ -27,8 +39,8 @@ macro(add_test_file test_source)
|
|||||||
get_filename_component(TEST_NAME ${ARGV0} NAME_WE)
|
get_filename_component(TEST_NAME ${ARGV0} NAME_WE)
|
||||||
add_executable(${TEST_NAME}
|
add_executable(${TEST_NAME}
|
||||||
${ARGV0}
|
${ARGV0}
|
||||||
../3rdparty/gtest/src/gtest_main.cc)
|
../3rdparty/gmock/src/gmock_main.cc)
|
||||||
target_link_libraries(${TEST_NAME} clementine_lib gtest)
|
target_link_libraries(${TEST_NAME} clementine_lib gmock)
|
||||||
add_custom_command(TARGET test POST_BUILD
|
add_custom_command(TARGET test POST_BUILD
|
||||||
COMMAND ./${TEST_NAME})
|
COMMAND ./${TEST_NAME})
|
||||||
add_dependencies(test ${TEST_NAME})
|
add_dependencies(test ${TEST_NAME})
|
||||||
@ -37,3 +49,4 @@ endmacro (add_test_file)
|
|||||||
|
|
||||||
add_test_file(m3uparser_test.cpp)
|
add_test_file(m3uparser_test.cpp)
|
||||||
add_test_file(song_test.cpp)
|
add_test_file(song_test.cpp)
|
||||||
|
add_test_file(librarybackend_test.cpp)
|
||||||
|
110
tests/librarybackend_test.cpp
Normal file
110
tests/librarybackend_test.cpp
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
#include "gmock/gmock-printers.h"
|
||||||
|
#include "test_utils.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "mock_sqldriver.h"
|
||||||
|
|
||||||
|
#include "librarybackend.h"
|
||||||
|
|
||||||
|
#include <boost/scoped_ptr.hpp>
|
||||||
|
|
||||||
|
#include <QtDebug>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
using ::testing::_;
|
||||||
|
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() {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void SetUp() {
|
||||||
|
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));
|
||||||
|
|
||||||
|
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_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() {
|
||||||
|
// Make sure Qt does not re-use the connection.
|
||||||
|
QSqlDatabase::removeDatabase("thread_" + QString::number(
|
||||||
|
reinterpret_cast<quint64>(QThread::currentThread())));
|
||||||
|
}
|
||||||
|
|
||||||
|
MockSqlDriver* driver_;
|
||||||
|
scoped_ptr<LibraryBackend> backend_;
|
||||||
|
};
|
||||||
|
|
||||||
|
MATCHER(Printer, "") { qDebug() << arg; return true; }
|
||||||
|
|
||||||
|
TEST_F(LibraryBackendTest, DatabaseInitialises) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LibraryBackendTest, GetSongs) {
|
||||||
|
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 = ?";
|
||||||
|
MockSqlResult* result = new MockSqlResult(driver_);
|
||||||
|
EXPECT_CALL(*driver_, createResult()).
|
||||||
|
WillOnce(Return(result));
|
||||||
|
EXPECT_CALL(*result, reset(QString(query))).WillOnce(
|
||||||
|
DoAll(
|
||||||
|
Invoke(result, &MockSqlResult::hackSetActive),
|
||||||
|
Return(true)));
|
||||||
|
|
||||||
|
EXPECT_CALL(*result, bindValue(0, QVariant("foo"), _));
|
||||||
|
EXPECT_CALL(*result, bindValue(1, QVariant("bar"), _));
|
||||||
|
|
||||||
|
EXPECT_CALL(*result, exec()).WillOnce(Return(true));
|
||||||
|
EXPECT_CALL(*result, fetch(1)).WillOnce(
|
||||||
|
DoAll(
|
||||||
|
Invoke(result, &MockSqlResult::setAt),
|
||||||
|
Return(true)));
|
||||||
|
EXPECT_CALL(*result, fetch(2)).WillOnce(Return(false));
|
||||||
|
|
||||||
|
for (int i = 0; i < 24; ++i) {
|
||||||
|
// Fill every column with 42.
|
||||||
|
EXPECT_CALL(*result, data(i)).WillRepeatedly(
|
||||||
|
Return(QVariant(42))).RetiresOnSaturation();
|
||||||
|
}
|
||||||
|
|
||||||
|
SongList songs = backend_->GetSongs("foo", "bar");
|
||||||
|
ASSERT_EQ(1, songs.length());
|
||||||
|
EXPECT_EQ(42, songs[0].length());
|
||||||
|
EXPECT_EQ(42, songs[0].id());
|
||||||
|
}
|
66
tests/mock_sqldriver.h
Normal file
66
tests/mock_sqldriver.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#include <gmock/gmock.h>
|
||||||
|
|
||||||
|
#include <QSqlDriver>
|
||||||
|
#include <QSqlResult>
|
||||||
|
#include <QSqlIndex>
|
||||||
|
#include <QSqlField>
|
||||||
|
|
||||||
|
class MockSqlDriver : public QSqlDriver {
|
||||||
|
public:
|
||||||
|
virtual ~MockSqlDriver() {}
|
||||||
|
MOCK_METHOD0(beginTransaction, bool());
|
||||||
|
MOCK_METHOD0(close, void());
|
||||||
|
MOCK_METHOD0(commitTransaction, bool());
|
||||||
|
MOCK_CONST_METHOD0(createResult, QSqlResult*());
|
||||||
|
MOCK_CONST_METHOD2(escapeIdentifier, QString(const QString&, IdentifierType));
|
||||||
|
MOCK_CONST_METHOD2(formatValue, QString(const QSqlField&, bool));
|
||||||
|
MOCK_CONST_METHOD0(handle, QVariant());
|
||||||
|
MOCK_CONST_METHOD1(hasFeature, bool(DriverFeature));
|
||||||
|
MOCK_CONST_METHOD0(isOpen, bool());
|
||||||
|
MOCK_METHOD6(open, bool(const QString&, const QString&, const QString&, const QString&, int, const QString&));
|
||||||
|
MOCK_CONST_METHOD1(primaryIndex, QSqlIndex(const QString&));
|
||||||
|
MOCK_CONST_METHOD1(record, QSqlRecord(const QString&));
|
||||||
|
MOCK_METHOD0(rollbackTransaction, bool());
|
||||||
|
MOCK_CONST_METHOD4(sqlStatement, QString(StatementType, const QString&, const QSqlRecord&, bool));
|
||||||
|
MOCK_CONST_METHOD1(tables, QStringList(QSql::TableType));
|
||||||
|
|
||||||
|
MOCK_METHOD1(setLastError, void(const QSqlError&));
|
||||||
|
MOCK_METHOD1(setOpen, void(bool));
|
||||||
|
MOCK_METHOD1(setOpenError, void(bool));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class MockSqlResult : public QSqlResult {
|
||||||
|
public:
|
||||||
|
MockSqlResult(QSqlDriver* driver)
|
||||||
|
: QSqlResult(driver) {
|
||||||
|
}
|
||||||
|
virtual ~MockSqlResult() {}
|
||||||
|
MOCK_METHOD3(bindValue, void(int, const QVariant&, QSql::ParamType));
|
||||||
|
MOCK_METHOD3(bindValue, void(const QString&, const QVariant&, QSql::ParamType));
|
||||||
|
MOCK_METHOD1(data, QVariant(int));
|
||||||
|
MOCK_METHOD0(exec, bool());
|
||||||
|
MOCK_METHOD1(fetch, bool(int));
|
||||||
|
MOCK_METHOD0(fetchFirst, bool());
|
||||||
|
MOCK_METHOD0(fetchLast, bool());
|
||||||
|
MOCK_METHOD1(isNull, bool(int));
|
||||||
|
MOCK_CONST_METHOD0(lastInsertId, QVariant());
|
||||||
|
MOCK_METHOD0(numRowsAffected, int());
|
||||||
|
MOCK_METHOD1(prepare, bool(const QString&));
|
||||||
|
MOCK_METHOD1(reset, bool(const QString&));
|
||||||
|
MOCK_METHOD0(size, int());
|
||||||
|
|
||||||
|
void hackSetActive(const QString&) {
|
||||||
|
setActive(true);
|
||||||
|
setAt(0);
|
||||||
|
setSelect(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hackSetActiveVoid() {
|
||||||
|
hackSetActive(QString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAt(int i) {
|
||||||
|
QSqlResult::setAt(i);
|
||||||
|
}
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user