diff --git a/CMakeLists.txt b/CMakeLists.txt index 17696e00..0a8bba40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,8 +78,6 @@ endif() add_subdirectory(${QHEXEDIT_DIR}) add_subdirectory(${QCUSTOMPLOT_DIR}) -add_subdirectory(/home/michael/dev/gtest/git/googletest /home/michael/dev/gtest/build) - find_package(Qt5Widgets REQUIRED) find_package(Qt5LinguistTools REQUIRED) find_package(Qt5Network REQUIRED) @@ -292,6 +290,8 @@ if(WIN32) ELSE( MINGW ) set(SQLB_SRC ${SQLB_SRC} "${CMAKE_CURRENT_SOURCE_DIR}/src/winapp.rc") ENDIF( MINGW ) +else() + set(LPTHREAD pthread) endif(WIN32) #enable version check for MacOS @@ -376,7 +376,7 @@ endif() target_link_libraries(${PROJECT_NAME} qhexedit qcustomplot - pthread + ${LPTHREAD} ${QT_LIBRARIES} ${WIN32_STATIC_LINK} ${LIBSQLITE} diff --git a/src/ExportDataDialog.cpp b/src/ExportDataDialog.cpp index f2f21420..19968af1 100644 --- a/src/ExportDataDialog.cpp +++ b/src/ExportDataDialog.cpp @@ -117,7 +117,7 @@ bool ExportDataDialog::exportQueryCsv(const QString& sQuery, const QString& sFil QByteArray utf8Query = sQuery.toUtf8(); sqlite3_stmt *stmt; - auto pDb = pdb.get("exporting CSV"); + auto pDb = pdb.get(tr("exporting CSV")); int status = sqlite3_prepare_v2(pDb.get(), utf8Query.data(), utf8Query.size(), &stmt, nullptr); if(SQLITE_OK == status) { @@ -201,7 +201,7 @@ bool ExportDataDialog::exportQueryJson(const QString& sQuery, const QString& sFi QByteArray utf8Query = sQuery.toUtf8(); sqlite3_stmt *stmt; - auto pDb = pdb.get("exporting JSON"); + auto pDb = pdb.get(tr("exporting JSON")); int status = sqlite3_prepare_v2(pDb.get(), utf8Query.data(), utf8Query.size(), &stmt, nullptr); QJsonArray json_table; diff --git a/src/ExtendedTableWidget.cpp b/src/ExtendedTableWidget.cpp index c6b0a9bd..d55b5da0 100644 --- a/src/ExtendedTableWidget.cpp +++ b/src/ExtendedTableWidget.cpp @@ -1,5 +1,3 @@ -#include - #include "ExtendedTableWidget.h" #include "sqlitetablemodel.h" #include "FilterTableHeader.h" @@ -612,8 +610,6 @@ void ExtendedTableWidget::updateGeometries() void ExtendedTableWidget::vscrollbarChanged(int value) { - //std::cout << "ExtendedTableWidget::vscrollbarChanged " << value << std::endl; - // Cancel if there is no model set yet - this shouldn't happen (because without a model there should be no scrollbar) but just to be sure... if(!model()) return; diff --git a/src/ImportCsvDialog.cpp b/src/ImportCsvDialog.cpp index 9d5fdc92..6a768b24 100644 --- a/src/ImportCsvDialog.cpp +++ b/src/ImportCsvDialog.cpp @@ -607,7 +607,7 @@ bool ImportCsvDialog::importCsv(const QString& fileName, const QString& name) sQuery.chop(1); // Remove last comma sQuery.append(")"); sqlite3_stmt* stmt; - auto pDb = pdb->get("importing CSV"); + auto pDb = pdb->get(tr("importing CSV")); sqlite3_prepare_v2(pDb.get(), sQuery.toUtf8(), sQuery.toUtf8().length(), &stmt, nullptr); // Parse entire file diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 4c5eac0d..d1bfdbbb 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -1,5 +1,3 @@ -#include - #include "MainWindow.h" #include "ui_MainWindow.h" @@ -824,7 +822,7 @@ void MainWindow::setRecordsetLabel() } ui->labelRecordset->setText(txt); - ui->dataTable->setDisabled( m_browseTableModel->rowCountAvailable() == SqliteTableModel::RowCount::Unknown ); + enableEditing(m_browseTableModel->rowCountAvailable() != SqliteTableModel::RowCount::Unknown, 0); } void MainWindow::refresh() @@ -1152,7 +1150,7 @@ void MainWindow::executeQuery() // Execute next statement int tail_length_before = tail_length; const char* qbegin = tail; - auto pDb = db.get("executing query"); + auto pDb = db.get(tr("executing query")); sql3status = sqlite3_prepare_v2(pDb.get(), tail, tail_length, &vm, &tail); QString queryPart = QString::fromUtf8(qbegin, tail - qbegin); tail_length -= (tail - qbegin); @@ -1668,8 +1666,10 @@ void MainWindow::enableEditing(bool enable_edit, bool enable_insert) bool insert = enable_insert && !db.readOnly(); // Apply settings - ui->buttonNewRecord->setEnabled(insert); - ui->buttonDeleteRecord->setEnabled(edit); + if(insert) + ui->buttonNewRecord->setEnabled(insert); + if(edit) + ui->buttonDeleteRecord->setEnabled(edit); ui->dataTable->setEditTriggers(edit ? QAbstractItemView::SelectedClicked | QAbstractItemView::AnyKeyPressed | QAbstractItemView::EditKeyPressed : QAbstractItemView::NoEditTriggers); } @@ -2953,7 +2953,7 @@ void MainWindow::requestCollation(const QString& name, int eTextRep) "If you choose to proceed, be aware bad things can happen to your database.\n" "Create a backup!").arg(name), QMessageBox::Yes | QMessageBox::No); if(reply == QMessageBox::Yes) { - auto pDb = db.get("creating collation"); + auto pDb = db.get(tr("creating collation")); sqlite3_create_collation(pDb.get(), name.toUtf8(), eTextRep, nullptr, collCompare); } } diff --git a/src/PlotDock.cpp b/src/PlotDock.cpp index 3df62579..316541dd 100644 --- a/src/PlotDock.cpp +++ b/src/PlotDock.cpp @@ -424,12 +424,11 @@ void PlotDock::updatePlot(SqliteTableModel* model, BrowseDataTableSettings* sett ui->plotWidget->replot(); // Warn user if not all data has been fetched and hint about the button for loading all the data - if (model->rowCountAvailable() != SqliteTableModel::RowCount::Complete || !model->isCacheComplete()) { + if (model && (model->rowCountAvailable() != SqliteTableModel::RowCount::Complete || !model->isCacheComplete())) { ui->buttonLoadAllData->setEnabled(true); ui->buttonLoadAllData->setStyleSheet("QToolButton {color: white; background-color: rgb(255, 102, 102)}"); ui->buttonLoadAllData->setToolTip(tr("Load all data and redraw plot.\n" "Warning: not all data has been fetched from the table yet due to the partial fetch mechanism.")); - QToolTip::showText(ui->buttonLoadAllData->mapToGlobal(QPoint(0, 0)), ui->buttonLoadAllData->toolTip()); } else { ui->buttonLoadAllData->setEnabled(false); ui->buttonLoadAllData->setStyleSheet(""); diff --git a/src/RowLoader.cpp b/src/RowLoader.cpp index 7acde9fa..cfa48b2a 100644 --- a/src/RowLoader.cpp +++ b/src/RowLoader.cpp @@ -201,8 +201,6 @@ void RowLoader::run () void RowLoader::process (Task & t) { - //std::cout << "RowLoader new task: " << t.row_begin << " --> " << t.row_end << std::endl; - QString sLimitQuery; if(query.startsWith("PRAGMA", Qt::CaseInsensitive) || query.startsWith("EXPLAIN", Qt::CaseInsensitive)) { @@ -254,11 +252,4 @@ void RowLoader::process (Task & t) if(row != t.row_begin) emit fetched(t.token, t.row_begin, row); - -#if 0 - if(t.cancel) - std::cout << "RowLoader task was CANCELLED\n"; - else - std::cout << "RowLoader task done\n"; -#endif } diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index b563d80c..5361550d 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -497,7 +497,8 @@ bool DBBrowserDB::close() emit dbChanged(getDirty()); emit structureUpdated(); - return true; //< not cancelled + // Return true to tell the calling function that the closing wasn't cancelled by the user + return true; } DBBrowserDB::db_pointer_type DBBrowserDB::get(QString user) @@ -505,7 +506,7 @@ DBBrowserDB::db_pointer_type DBBrowserDB::get(QString user) if(!_db) return nullptr; - auto lk = waitForDbRelease(); + waitForDbRelease(); db_user = user; db_used = true; @@ -513,10 +514,10 @@ DBBrowserDB::db_pointer_type DBBrowserDB::get(QString user) return db_pointer_type(_db, DatabaseReleaser(this)); } -std::unique_lock DBBrowserDB::waitForDbRelease() +void DBBrowserDB::waitForDbRelease() { if(!_db) - return std::unique_lock(); + return; std::unique_lock lk(m); while(db_used) { @@ -525,8 +526,8 @@ std::unique_lock DBBrowserDB::waitForDbRelease() lk.unlock(); QMessageBox msgBox; - msgBox.setText("The database is currently busy: " + str); - msgBox.setInformativeText("Do you want to abort that other operation?"); + msgBox.setText(tr("The database is currently busy: ") + str); + msgBox.setInformativeText(tr("Do you want to abort that other operation?")); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setDefaultButton(QMessageBox::No); int ret = msgBox.exec(); @@ -537,8 +538,6 @@ std::unique_lock DBBrowserDB::waitForDbRelease() lk.lock(); cv.wait(lk, [this](){ return !db_used; }); } - - return std::move(lk); } bool DBBrowserDB::dump(const QString& filename, @@ -1035,9 +1034,8 @@ QString DBBrowserDB::addRecord(const sqlb::ObjectIdentifier& tablename) } else { if(table->isWithoutRowidTable()) return pk_value; - else { + else return QString::number(sqlite3_last_insert_rowid(_db)); - } } } diff --git a/src/sqlitedb.h b/src/sqlitedb.h index 84ce8964..6558b657 100644 --- a/src/sqlitedb.h +++ b/src/sqlitedb.h @@ -192,8 +192,8 @@ private: /// wait for release of the DB locked through a previous get(), /// giving users the option to discard running task through a - /// message box. \returns active lock. - std::unique_lock waitForDbRelease (); + /// message box. + void waitForDbRelease(); QString curDBFilename; QString lastErrorMessage; diff --git a/src/sqlitetablemodel.cpp b/src/sqlitetablemodel.cpp index c9a97ee2..4df1de79 100644 --- a/src/sqlitetablemodel.cpp +++ b/src/sqlitetablemodel.cpp @@ -1,4 +1,3 @@ - #include "sqlitetablemodel.h" #include "sqlitedb.h" #include "sqlite.h" @@ -24,7 +23,7 @@ SqliteTableModel::SqliteTableModel(DBBrowserDB& db, QObject* parent, size_t chun , m_encoding(encoding) { worker = new RowLoader( - [this](){ return m_db.get("reading rows"); }, + [this](){ return m_db.get(tr("reading rows")); }, [this](QString stmt){ return m_db.logSQL(stmt, kLogMsg_App); }, m_headers, m_mutexDataCache, m_cache ); @@ -95,8 +94,6 @@ void SqliteTableModel::handleRowCountComplete (int life_id, int num_rows) void SqliteTableModel::reset() { - //std::cout << "\n\nSqliteTableModel::reset()\n"; - clearCache(); m_sTable.clear(); @@ -249,9 +246,7 @@ QVariant SqliteTableModel::data(const QModelIndex &index, int role) const { cached_row = &m_cache.at(index.row()); row_available = true; - } - else - { + } else { blank_data = makeDefaultCacheEntry(); cached_row = &blank_data; row_available = false; @@ -260,17 +255,13 @@ QVariant SqliteTableModel::data(const QModelIndex &index, int role) const if(role == Qt::DisplayRole || role == Qt::EditRole) { if(!row_available) - return decode("loading..."); + return tr("loading..."); if(role == Qt::DisplayRole && cached_row->at(index.column()).isNull()) { return Settings::getValue("databrowser", "null_text").toString(); - } - else if(role == Qt::DisplayRole && nosync_isBinary(index)) - { + } else if(role == Qt::DisplayRole && nosync_isBinary(index)) { return Settings::getValue("databrowser", "blob_text").toString(); - } - else if(role == Qt::DisplayRole) - { + } else if(role == Qt::DisplayRole) { int limit = Settings::getValue("databrowser", "symbol_limit").toInt(); QByteArray displayText = cached_row->at(index.column()); if (displayText.length() > limit) { @@ -282,16 +273,12 @@ QVariant SqliteTableModel::data(const QModelIndex &index, int role) const } else { return decode(cached_row->at(index.column())); } - } - else if(role == Qt::FontRole) - { + } else if(role == Qt::FontRole) { QFont font; if(!row_available || cached_row->at(index.column()).isNull() || nosync_isBinary(index)) font.setItalic(true); return font; - } - else if(role == Qt::ForegroundRole) - { + } else if(role == Qt::ForegroundRole) { if(!row_available) return QColor(100, 100, 100); if(cached_row->at(index.column()).isNull()) @@ -299,9 +286,7 @@ QVariant SqliteTableModel::data(const QModelIndex &index, int role) const else if (nosync_isBinary(index)) return QColor(Settings::getValue("databrowser", "bin_fg_colour").toString()); return QColor(Settings::getValue("databrowser", "reg_fg_colour").toString()); - } - else if (role == Qt::BackgroundRole) - { + } else if (role == Qt::BackgroundRole) { if(!row_available) return QColor(255, 200, 200); if(cached_row->at(index.column()).isNull()) @@ -309,9 +294,7 @@ QVariant SqliteTableModel::data(const QModelIndex &index, int role) const else if (nosync_isBinary(index)) return QColor(Settings::getValue("databrowser", "bin_bg_colour").toString()); return QColor(Settings::getValue("databrowser", "reg_bg_colour").toString()); - } - else if(role == Qt::ToolTipRole) - { + } else if(role == Qt::ToolTipRole) { sqlb::ForeignKeyClause fk = getForeignKeyClause(index.column()-1); if(fk.isSet()) return tr("References %1(%2)\nHold Ctrl+Shift and click to jump there").arg(fk.table()).arg(fk.columns().join(",")); @@ -503,7 +486,6 @@ bool SqliteTableModel::insertRows(int row, int count, const QModelIndex& parent) beginInsertRows(parent, row, row + count - 1); for(unsigned int i = 0; i < tempList.size(); ++i) { - //std::cout << "inserting at " << i + row << std::endl; m_cache.insert(i + row, std::move(tempList.at(i))); m_currentRowCount++; } @@ -691,7 +673,7 @@ void SqliteTableModel::removeCommentsFromQuery(QString& query) QStringList SqliteTableModel::getColumns(std::shared_ptr pDb, const QString& sQuery, QVector& fieldsTypes) { if(!pDb) - pDb = m_db.get("retrieving list of columns"); + pDb = m_db.get(tr("retrieving list of columns")); sqlite3_stmt* stmt; QByteArray utf8Query = sQuery.toUtf8(); @@ -945,11 +927,8 @@ void SqliteTableModel::triggerCacheLoad (int row) const QMutexLocker lk(&m_mutexDataCache); m_cache.smallestNonAvailableRange(row_begin, row_end); - if(row_end != row_begin) { + if(row_end != row_begin) worker->triggerFetch(m_lifeCounter, row_begin, row_end); - } else { - //std::cout << "entire range already loaded\n"; - } } void SqliteTableModel::triggerCacheLoad (int row_begin, int row_end) const diff --git a/src/sqlitetablemodel.h b/src/sqlitetablemodel.h index 5ea08938..b261ded9 100644 --- a/src/sqlitetablemodel.h +++ b/src/sqlitetablemodel.h @@ -1,13 +1,12 @@ #ifndef SQLITETABLEMODEL_H #define SQLITETABLEMODEL_H -#include - #include #include #include #include #include +#include #include "sqlitetypes.h" #include "RowCache.h" @@ -15,7 +14,6 @@ struct sqlite3; class DBBrowserDB; - class SqliteTableModel : public QAbstractTableModel { Q_OBJECT @@ -87,10 +85,10 @@ public: void setTable(const sqlb::ObjectIdentifier& table, int sortColumn = 0, Qt::SortOrder sortOrder = Qt::AscendingOrder, const QVector &display_format = QVector()); void setChunkSize(size_t chunksize); - void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; const sqlb::ObjectIdentifier& currentTableName() const { return m_sTable; } - Qt::ItemFlags flags(const QModelIndex& index) const; + Qt::ItemFlags flags(const QModelIndex& index) const override; bool isBinary(const QModelIndex& index) const; @@ -118,8 +116,8 @@ signals: void finishedFetch(int fetched_row_begin, int fetched_row_end); protected: - virtual Qt::DropActions supportedDropActions() const; - virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent); + virtual Qt::DropActions supportedDropActions() const override; + virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; private: friend class RowLoader; diff --git a/src/src.pro b/src/src.pro index 5ce86d5d..fc79d313 100644 --- a/src/src.pro +++ b/src/src.pro @@ -15,8 +15,8 @@ QMAKE_CXXFLAGS += -std=c++11 CONFIG(unittest) { QT += testlib - HEADERS += tests/testsqlobjects.h tests/TestImport.h tests/TestRegex.h - SOURCES += tests/testsqlobjects.cpp tests/TestImport.cpp tests/TestMain.cpp tests/TestRegex.cpp + HEADERS += tests/testsqlobjects.h tests/TestImport.h tests/TestRegex.h tests/TestRowCache.h + SOURCES += tests/testsqlobjects.cpp tests/TestImport.cpp tests/TestRegex.cpp tests/TestRowCache.cpp } else { SOURCES += main.cpp } diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 26a6e9d5..86611f74 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -1,5 +1,9 @@ include_directories("${CMAKE_CURRENT_BINARY_DIR}" ..) +if(NOT WIN32) + set(LPTHREAD pthread) +endif() + # test-sqlobjects set(TESTSQLOBJECTS_SRC @@ -54,7 +58,7 @@ else() endif() link_directories("${CMAKE_CURRENT_BINARY_DIR}/${QSCINTILLA_DIR}") add_dependencies(test-sqlobjects qscintilla2) -target_link_libraries(test-sqlobjects qscintilla2 pthread) +target_link_libraries(test-sqlobjects qscintilla2 ${LPTHREAD}) add_test(test-sqlobjects test-sqlobjects) # test-import @@ -126,10 +130,23 @@ else() endif() link_directories("${CMAKE_CURRENT_BINARY_DIR}/${QSCINTILLA_DIR}") add_dependencies(test-regex qscintilla2) -target_link_libraries(test-regex qscintilla2 pthread) +target_link_libraries(test-regex qscintilla2 ${LPTHREAD}) add_test(test-regex test-regex) +# test cache -add_executable(test-cache test_row_cache.cpp) -target_link_libraries(test-cache gtest_main) -add_test(NAME test-cache COMMAND test-cache) +set(TESTCACHE_SRC + TestRowCache.cpp +) + +set(TESTCACHE_MOC_HDR + TestRowCache.h +) + +add_executable(test-cache ${TESTCACHE_MOC} ${TESTCACHE_SRC}) + +qt5_use_modules(test-cache Test Core) +set(QT_LIBRARIES "") + +target_link_libraries(test-cache ${QT_LIBRARIES}) +add_test(test-cache test-cache) diff --git a/src/tests/TestRowCache.cpp b/src/tests/TestRowCache.cpp new file mode 100644 index 00000000..610ba2cf --- /dev/null +++ b/src/tests/TestRowCache.cpp @@ -0,0 +1,189 @@ +#include + +#include "TestRowCache.h" +#include "../RowCache.h" + +QTEST_APPLESS_MAIN(TestRowCache) + +TestRowCache::TestRowCache() +{ +} + +TestRowCache::~TestRowCache() +{ +} + +using C = RowCache; + +void TestRowCache::construction() +{ + C c; + + QCOMPARE(c.numSet(), static_cast(0)); + + QVERIFY(c.count(0) == false); + QVERIFY(c.count(1) == false); + QVERIFY(c.count(2) == false); + + QVERIFY_EXCEPTION_THROWN(c.at(0), std::out_of_range); +} + +void TestRowCache::setGet() +{ + C c; + + c.set(1, 10); + c.set(5, 50); + c.set(0, 0); + c.set(6, 60); + c.set(100, 1000); + + QCOMPARE(c.numSet(), static_cast(5)); + QCOMPARE(c.numSegments(), static_cast(4)); // the '0' set after the '1' position does not merge currently + + int cnt = 0; + const C & cc = c; + for(size_t i = 0; i < 200; i++) { + if(c.count(i)) { + QCOMPARE(c.at(i), static_cast(10*i)); + QCOMPARE(cc.at(i), static_cast(10*i)); + cnt++; + } else { + QVERIFY_EXCEPTION_THROWN(c.at(i), std::out_of_range); + QVERIFY_EXCEPTION_THROWN(cc.at(i), std::out_of_range); + } + } + QCOMPARE(cnt, 5); +} + +void TestRowCache::insert() +{ + C c; + + c.insert(3, 30); + QCOMPARE(c.numSet(), static_cast(1)); + QCOMPARE(c.numSegments(), static_cast(1)); + QCOMPARE(c.at(3), 30); + + c.insert(3, 31); + QCOMPARE(c.numSet(), static_cast(2)); + QCOMPARE(c.numSegments(), static_cast(1)); + QCOMPARE(c.at(3), 31); + QCOMPARE(c.at(4), 30); + + c.insert(0, 0); + QCOMPARE(c.numSet(), static_cast(3)); + QCOMPARE(c.numSegments(), static_cast(2)); + QCOMPARE(c.at(0), 0); + QVERIFY_EXCEPTION_THROWN(c.at(3), std::out_of_range); + QCOMPARE(c.at(4), 31); + QCOMPARE(c.at(5), 30); + QVERIFY_EXCEPTION_THROWN(c.at(6), std::out_of_range); + + c.insert(1, 100); + QCOMPARE(c.numSet(), static_cast(4)); + QCOMPARE(c.numSegments(), static_cast(2)); + QCOMPARE(c.at(0), 0); + QCOMPARE(c.at(1), 100); + QCOMPARE(c.at(5), 31); + QCOMPARE(c.at(6), 30); + + c.insert(8, 1); + QCOMPARE(c.numSet(), static_cast(5)); + QCOMPARE(c.numSegments(), static_cast(3)); + QCOMPARE(c.at(0), 0); + QCOMPARE(c.at(1), 100); + QCOMPARE(c.at(5), 31); + QCOMPARE(c.at(6), 30); + QCOMPARE(c.at(8), 1); +} + +void TestRowCache::erase() +{ + C c; + c.insert(3, 30); + c.insert(3, 31); + c.insert(0, 0); + c.insert(8, 1); + QCOMPARE(c.numSet(), static_cast(4)); + QCOMPARE(c.numSegments(), static_cast(3)); + QCOMPARE(c.at(0), 0); + QCOMPARE(c.at(4), 31); + QCOMPARE(c.at(5), 30); + QCOMPARE(c.at(8), 1); + + // erase entire segment + c.erase(0); + QCOMPARE(c.numSet(), static_cast(3)); + QCOMPARE(c.numSegments(), static_cast(2)); + QCOMPARE(c.at(3), 31); + QCOMPARE(c.at(4), 30); + QCOMPARE(c.at(7), 1); + + // erase inside segment + c.erase(4); + QCOMPARE(c.numSet(), static_cast(2)); + QCOMPARE(c.numSegments(), static_cast(2)); + QCOMPARE(c.at(3), 31); + QCOMPARE(c.at(6), 1); + + // erase non-filled row + c.erase(5); + QCOMPARE(c.numSet(), static_cast(2)); + QCOMPARE(c.numSegments(), static_cast(2)); + QCOMPARE(c.at(3), 31); + QCOMPARE(c.at(5), 1); + + c.erase(5); + QCOMPARE(c.numSet(), static_cast(1)); + QCOMPARE(c.numSegments(), static_cast(1)); + QCOMPARE(c.at(3), 31); + + c.erase(3); + QCOMPARE(c.numSet(), static_cast(0)); + QCOMPARE(c.numSegments(), static_cast(0)); +} + +void TestRowCache::smallestNonAvailableRange() +{ + C c; + c.insert(3, 0); + c.insert(3, 0); + c.insert(0, 0); + c.insert(8, 0); + QCOMPARE(c.numSet(), static_cast(4)); + QVERIFY(c.count(0)); + QVERIFY(c.count(4)); + QVERIFY(c.count(5)); + QVERIFY(c.count(8)); + + using P = std::pair; + + auto test = [&](size_t begin, size_t end) { + P p{ begin, end }; + c.smallestNonAvailableRange(p.first, p.second); + return p; + }; + + QCOMPARE(test( 0, 0), P( 0, 0)); + QCOMPARE(test( 0, 1), P( 1, 1)); + QCOMPARE(test( 0, 2), P( 1, 2)); + QCOMPARE(test( 0, 3), P( 1, 3)); + QCOMPARE(test( 0, 4), P( 1, 4)); + QCOMPARE(test( 0, 5), P( 1, 4)); + QCOMPARE(test( 0, 6), P( 1, 4)); + QCOMPARE(test( 0, 7), P( 1, 7)); + QCOMPARE(test( 0, 8), P( 1, 8)); + QCOMPARE(test( 0, 9), P( 1, 8)); + QCOMPARE(test( 0,10), P( 1,10)); + QCOMPARE(test( 1,10), P( 1,10)); + QCOMPARE(test( 2,10), P( 2,10)); + QCOMPARE(test( 3,10), P( 3,10)); + QCOMPARE(test( 4,10), P( 6,10)); + QCOMPARE(test( 5,10), P( 6,10)); + QCOMPARE(test( 6,10), P( 6,10)); + QCOMPARE(test( 7,10), P( 7,10)); + QCOMPARE(test( 8,10), P( 9,10)); + QCOMPARE(test( 9,10), P( 9,10)); + QCOMPARE(test(10,10), P(10,10)); +} diff --git a/src/tests/TestRowCache.h b/src/tests/TestRowCache.h new file mode 100644 index 00000000..beeeee9c --- /dev/null +++ b/src/tests/TestRowCache.h @@ -0,0 +1,22 @@ +#ifndef TESTROWCACHE_H +#define TESTROWCACHE_H + +#include + +class TestRowCache : public QObject +{ + Q_OBJECT + +public: + TestRowCache(); + ~TestRowCache(); + +private slots: + void construction(); + void setGet(); + void insert(); + void erase(); + void smallestNonAvailableRange(); +}; + +#endif diff --git a/src/tests/test_row_cache.cpp b/src/tests/test_row_cache.cpp deleted file mode 100644 index 24e9e54f..00000000 --- a/src/tests/test_row_cache.cpp +++ /dev/null @@ -1,179 +0,0 @@ - -#include "../RowCache.h" - -#include "gtest/gtest.h" - -using C = RowCache; - -TEST(RowCache, Construction) -{ - C c; - - EXPECT_EQ(0u, c.numSet()); - - EXPECT_FALSE(c.count(0)); - EXPECT_FALSE(c.count(1)); - EXPECT_FALSE(c.count(2)); - - EXPECT_THROW(c.at(0), std::out_of_range); -} - -TEST(RowCache, set_get) -{ - C c; - - c.set(1, 10); - c.set(5, 50); - c.set(0, 0); - c.set(6, 60); - c.set(100, 1000); - - EXPECT_EQ(5u, c.numSet()); - EXPECT_EQ(4u, c.numSegments()); // the '0' set after the '1' position does not merge currently - - int cnt = 0; - const C & cc = c; - for(size_t i = 0; i < 200; i++) { - if(c.count(i)) { - EXPECT_EQ(10*i, c.at(i)); - EXPECT_EQ(10*i, cc.at(i)); - cnt++; - } else { - EXPECT_THROW(c.at(i), std::out_of_range); - EXPECT_THROW(cc.at(i), std::out_of_range); - } - } - EXPECT_EQ(5, cnt); -} - -TEST(RowCache, insert) -{ - C c; - - c.insert(3, 30); - EXPECT_EQ(1u, c.numSet()); - EXPECT_EQ(1u, c.numSegments()); - EXPECT_EQ(30, c.at(3)); - - c.insert(3, 31); - EXPECT_EQ(2u, c.numSet()); - EXPECT_EQ(1u, c.numSegments()); - EXPECT_EQ(31, c.at(3)); - EXPECT_EQ(30, c.at(4)); - - c.insert(0, 0); - EXPECT_EQ(3u, c.numSet()); - EXPECT_EQ(2u, c.numSegments()); - EXPECT_EQ(0, c.at(0)); - EXPECT_THROW(c.at(3), std::out_of_range); - EXPECT_EQ(31, c.at(4)); - EXPECT_EQ(30, c.at(5)); - EXPECT_THROW(c.at(6), std::out_of_range); - - c.insert(1, 100); - EXPECT_EQ(4u, c.numSet()); - EXPECT_EQ(2u, c.numSegments()); - EXPECT_EQ(0, c.at(0)); - EXPECT_EQ(100, c.at(1)); - EXPECT_EQ(31, c.at(5)); - EXPECT_EQ(30, c.at(6)); - - c.insert(8, 1); - EXPECT_EQ(5u, c.numSet()); - EXPECT_EQ(3u, c.numSegments()); - EXPECT_EQ(0, c.at(0)); - EXPECT_EQ(100, c.at(1)); - EXPECT_EQ(31, c.at(5)); - EXPECT_EQ(30, c.at(6)); - EXPECT_EQ(1, c.at(8)); -} - -TEST(RowCache, erase) -{ - C c; - c.insert(3, 30); - c.insert(3, 31); - c.insert(0, 0); - c.insert(8, 1); - EXPECT_EQ(4u, c.numSet()); - EXPECT_EQ(3u, c.numSegments()); - EXPECT_EQ(0, c.at(0)); - EXPECT_EQ(31, c.at(4)); - EXPECT_EQ(30, c.at(5)); - EXPECT_EQ(1, c.at(8)); - - // erase entire segment - c.erase(0); - EXPECT_EQ(3u, c.numSet()); - EXPECT_EQ(2u, c.numSegments()); - EXPECT_EQ(31, c.at(3)); - EXPECT_EQ(30, c.at(4)); - EXPECT_EQ(1, c.at(7)); - - // erase inside segment - c.erase(4); - EXPECT_EQ(2u, c.numSet()); - EXPECT_EQ(2u, c.numSegments()); - EXPECT_EQ(31, c.at(3)); - EXPECT_EQ(1, c.at(6)); - - // erase non-filled row - c.erase(5); - EXPECT_EQ(2u, c.numSet()); - EXPECT_EQ(2u, c.numSegments()); - EXPECT_EQ(31, c.at(3)); - EXPECT_EQ(1, c.at(5)); - - c.erase(5); - EXPECT_EQ(1u, c.numSet()); - EXPECT_EQ(1u, c.numSegments()); - EXPECT_EQ(31, c.at(3)); - - c.erase(3); - EXPECT_EQ(0u, c.numSet()); - EXPECT_EQ(0u, c.numSegments()); -} - -TEST(RowCache, smallestNonAvailableRange) -{ - C c; - c.insert(3, 0); - c.insert(3, 0); - c.insert(0, 0); - c.insert(8, 0); - EXPECT_EQ(4u, c.numSet()); - EXPECT_TRUE(c.count(0)); - EXPECT_TRUE(c.count(4)); - EXPECT_TRUE(c.count(5)); - EXPECT_TRUE(c.count(8)); - - using P = std::pair; - - auto test = [&](int begin, int end) { - P p{ begin, end }; - c.smallestNonAvailableRange(p.first, p.second); - return p; - }; - - EXPECT_EQ(P( 0, 0), test( 0, 0)); - EXPECT_EQ(P( 1, 1), test( 0, 1)); - EXPECT_EQ(P( 1, 2), test( 0, 2)); - EXPECT_EQ(P( 1, 3), test( 0, 3)); - EXPECT_EQ(P( 1, 4), test( 0, 4)); - EXPECT_EQ(P( 1, 4), test( 0, 5)); - EXPECT_EQ(P( 1, 4), test( 0, 6)); - EXPECT_EQ(P( 1, 7), test( 0, 7)); - EXPECT_EQ(P( 1, 8), test( 0, 8)); - EXPECT_EQ(P( 1, 8), test( 0, 9)); - EXPECT_EQ(P( 1,10), test( 0,10)); - EXPECT_EQ(P( 1,10), test( 1,10)); - EXPECT_EQ(P( 2,10), test( 2,10)); - EXPECT_EQ(P( 3,10), test( 3,10)); - EXPECT_EQ(P( 6,10), test( 4,10)); - EXPECT_EQ(P( 6,10), test( 5,10)); - EXPECT_EQ(P( 6,10), test( 6,10)); - EXPECT_EQ(P( 7,10), test( 7,10)); - EXPECT_EQ(P( 9,10), test( 8,10)); - EXPECT_EQ(P( 9,10), test( 9,10)); - EXPECT_EQ(P(10,10), test(10,10)); -}