Provide complete test coverage for SQLiteResource

parent d116d2df
......@@ -10,10 +10,11 @@ set(AKABEI_VERSION_STRING "${MAJOR_AKABEI_VERSION}.${MINOR_AKABEI_VERSION}.${PAT
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
option(BUILD_TESTS "Builds various tests and examples for Akabei" ON)
option(BUILD_COVERAGE "Builds code coverage data for Akabei" OFF)
if(CMAKE_COMPILER_IS_GNUCXX AND BUILD_COVERAGE AND BUILD_TESTS)
include(CodeCoverage)
setup_target_for_coverage(${PROJECT_NAME}_coverage ctest coverage)
setup_target_for_coverage(coverage ctest coverage)
SET(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
SET(CMAKE_C_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
endif()
......
......@@ -141,7 +141,8 @@ FUNCTION(SETUP_TARGET_FOR_COVERAGE _targetname _testrunner _outputname)
COMMAND ${_testrunner} ${ARGV3}
# Capturing lcov counters and generating report
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info
# NOTE: We use --derive-func-data as a workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48463
COMMAND ${LCOV_PATH} --directory . --capture --output-file ${_outputname}.info --derive-func-data
COMMAND ${LCOV_PATH} --remove ${_outputname}.info 'tests/*' '/usr/*' --output-file ${_outputname}.info.cleaned
COMMAND ${GENHTML_PATH} -o ${_outputname} ${_outputname}.info.cleaned
COMMAND ${CMAKE_COMMAND} -E remove ${_outputname}.info ${_outputname}.info.cleaned
......
......@@ -24,11 +24,6 @@
#include <QMutexLocker>
AbstractSQLiteConnection::AbstractSQLiteConnection()
{
}
AbstractSQLiteConnection::~AbstractSQLiteConnection()
{
......@@ -43,7 +38,7 @@ public:
~Private() {}
QList<Row> table;
QMutex mutex;
mutable QMutex mutex;
};
/********** SQLiteResource **************/
......@@ -64,7 +59,7 @@ SQLiteResource& SQLiteResource::operator=(const SQLiteResource& other)
return *this;
}
QList<QVariant> SQLiteResource::getColumn(const QString& columnName)
QList<QVariant> SQLiteResource::getColumn(const QString& columnName) const
{
QMutexLocker locker(&d->mutex);
QList<QVariant> data;
......@@ -73,7 +68,10 @@ QList<QVariant> SQLiteResource::getColumn(const QString& columnName)
QVariantMap map( *it );
if (map.count(columnName) == 0) {
throw SQLiteException("Unexistent column in table");
std::string error = QStringLiteral(
"Tried to access non-existing column '%1'.").arg(
columnName).toStdString();
throw SQLiteException(error.c_str());
}
data.append(map[columnName]);
......@@ -82,18 +80,25 @@ QList<QVariant> SQLiteResource::getColumn(const QString& columnName)
return data;
}
QVariant SQLiteResource::getDataAt(int rowNumber, const QString& columnName)
QVariant SQLiteResource::getDataAt(
int rowNumber, const QString& columnName) const
{
QMutexLocker locker(&d->mutex);
if (d->table.isEmpty() || rowNumber >= d->table.size() || d->table[rowNumber].count(columnName) == 0) {
std::string error = QStringLiteral("Access to non-existent data. Trying to access %1.").arg(columnName).toStdString();
if (d->table.isEmpty() || rowNumber >= d->table.size() || rowNumber < 0) {
std::string error = QStringLiteral(
"Access to non-existent data. Trying to access non-existing row "
"%1.").arg(rowNumber).toStdString();
throw SQLiteException(error.c_str());
} else if (d->table[rowNumber].count(columnName) == 0) {
std::string error = QStringLiteral(
"Tried to access non-existing column '%1'.").arg(
columnName).toStdString();
throw SQLiteException(error.c_str());
}
return d->table[rowNumber][columnName];
}
int SQLiteResource::getRowsCount()
int SQLiteResource::getRowsCount() const
{
QMutexLocker locker(&d->mutex);
return d->table.size();
......
......@@ -51,18 +51,18 @@ public:
* @param columnName the column name
* @returns the value of the specified cell
*/
QVariant getDataAt(int, QString const&);
QVariant getDataAt(int, QString const&) const;
/**
* @param columnName the column name
* @returns a list of values of that column
*/
QList<QVariant> getColumn(QString const&);
QList<QVariant> getColumn(QString const&) const;
/**
* @returns the number of rows in this table
*/
int getRowsCount();
int getRowsCount() const;
SQLiteResource &operator=(const SQLiteResource &other);
......@@ -115,9 +115,6 @@ public:
virtual void bind(QString const&, int) = 0;
virtual void bind(QString const&, QString const&) = 0;
virtual void bind(QString const&, QByteArray const&) = 0;
protected:
AbstractSQLiteConnection();
};
......
......@@ -132,3 +132,8 @@ set(akabei_permissions_test_SRCS
add_executable(akabei_permissions_test ${akabei_permissions_test_SRCS} ${akabei_permissions_test_MOC_SRCS})
target_link_libraries(akabei_permissions_test ${COMMON_TEST_LIBRARIES})
add_test(akabei_permissions_test akabei_permissions_test)
add_executable(sqliteresourcetest sqliteresourcetest.cpp)
target_link_libraries(sqliteresourcetest ${COMMON_TEST_LIBRARIES})
add_test(sqliteresourcetest sqliteresourcetest)
#include <functional>
#include <QCoreApplication>
#include <QString>
#include <QTest>
QString dbSchemaPath() {
return QCoreApplication::applicationDirPath() +
QStringLiteral("/../../etc/dbschema.sql");
}
// Based on QVERIFY_EXCEPTION_THROWN macro from Qt 5.3, implemented as an
// inline function and including an optional substring check for the exception
// message.
template <typename ExceptionType>
inline void verifyExceptionThrown(
std::function<void()> expression, QString substring = QStringLiteral(""))
{
try {
try {
expression();
QTest::qFail(
"Expected an exception to be thrown but no exception caught",
__FILE__, __LINE__);
} catch (const ExceptionType &e) {
if (!substring.isEmpty() && !QString(e.what()).contains(substring)) {
QTest::qFail(QStringLiteral(
"Expected exception containing “%1” but error message "
"was “%2”").arg(substring).arg(
e.what()).toStdString().c_str(), __FILE__, __LINE__);
}
}
} catch (const ExceptionType &e) {
QByteArray msg = QByteArray() +
"Expected exception of a certain type to be thrown but "
"std::exception caught with message: " + e.what();
QTest::qFail(msg.constData(), __FILE__, __LINE__);
} catch (...) {
QTest::qFail(
"Expected exception of a certain type to be thrown but unknown "
"exception caught", __FILE__, __LINE__);
}
};
/* This file is part of the Chakra project
Copyright (C) 2016 Adrián Chaves (Gallaecio)
This program 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 2 of the License, or (at your option) any later version.
*/
#include <AbstractSQLiteConnection.h>
#include "common.h"
class SQLiteResourceTest : public QObject
{
Q_OBJECT
public:
SQLiteResourceTest(QObject* parent = 0)
: QObject(parent)
, m_rowCount(3)
, m_columnCount(3)
, m_rowTemplate("Column %1, row %2")
, m_columnTemplate("Column %1")
{
for (int rowNum=1; rowNum<=m_rowCount; ++rowNum) {
Row row;
for (int colNum=1; colNum<=m_columnCount; ++colNum) {
row.insert(m_columnTemplate.arg(colNum),
QVariant::fromValue(
m_rowTemplate.arg(colNum).arg(rowNum)));
}
m_rowList.append(row);
}
};
private:
void validateResource(const SQLiteResource& resource)
{
QCOMPARE(resource.getRowsCount(), m_rowCount);
for (int colNum=1; colNum<=m_columnCount; ++colNum) {
auto columName = m_columnTemplate.arg(colNum);
auto column = resource.getColumn(columName);
for (int rowNum=1; rowNum<=m_rowCount; ++rowNum) {
int rowIndex = rowNum-1;
QString expectedData(m_rowTemplate.arg(colNum).arg(rowNum));
QCOMPARE(column.at(rowIndex).toString(), expectedData);
QCOMPARE(resource.getDataAt(rowIndex, columName).toString(),
expectedData);
}
}
};
QList<Row> m_rowList;
int m_rowCount;
int m_columnCount;
QString m_rowTemplate;
QString m_columnTemplate;
private Q_SLOTS:
void mainConstructor()
{
SQLiteResource resource(m_rowList);
validateResource(resource);
};
void copyConstructor()
{
SQLiteResource source(m_rowList);
SQLiteResource copy(source);
validateResource(copy);
}
void referenceAssignment()
{
SQLiteResource source(m_rowList);
SQLiteResource target(QList<Row>{});
target = source;
validateResource(target);
}
void nonExistentColumnName() {
SQLiteResource resource(m_rowList);
QString columnName(m_columnTemplate.arg(m_columnCount+1));
QString columnString(QString("column '%1'").arg(columnName));
verifyExceptionThrown<SQLiteException>(
[&resource, columnName]() {
resource.getColumn(columnName);
},
columnString);
verifyExceptionThrown<SQLiteException>(
[&resource, columnName]() {
resource.getDataAt(0, columnName);
},
columnString);
}
void nonExistentRowIndex() {
SQLiteResource resource(QList<Row>{});
QString columnTemplate(m_columnTemplate);
int rowCount(m_rowCount);
verifyExceptionThrown<SQLiteException>(
[&resource, columnTemplate]() {
resource.getDataAt(0, columnTemplate.arg(1));
},
QString("row 0"));
resource = SQLiteResource(m_rowList);
verifyExceptionThrown<SQLiteException>(
[&resource, columnTemplate, rowCount]() {
resource.getDataAt(rowCount, columnTemplate.arg(1));
},
QStringLiteral("row %1").arg(rowCount));
verifyExceptionThrown<SQLiteException>(
[&resource, columnTemplate]() {
resource.getDataAt(-1, columnTemplate.arg(1));
},
QStringLiteral("row %1").arg(-1));
}
void columnNameMissingInOneRow() {
QString columnName(m_columnTemplate.arg(m_columnCount+1));
QString columnString(QString("column '%1'").arg(columnName));
SQLiteResource resource(QList<Row>{
{
{
columnString,
QVariant::fromValue(QStringLiteral("value A 1"))
}
},
{
}
});
verifyExceptionThrown<SQLiteException>(
[&resource, columnName]() {
resource.getDataAt(0, columnName);
},
columnString);
}
};
QTEST_MAIN(SQLiteResourceTest)
#include "sqliteresourcetest.moc"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment