feat(Storybook): sections decorator model impl and basic tests

Proxy model extending source model by adding foldable sections
based on "section" role from the source model.
This commit is contained in:
Michał Cieślak 2022-11-16 14:37:23 +01:00 committed by Michał
parent 80bb09cc67
commit c1d5fdd04a
4 changed files with 717 additions and 1 deletions

View File

@ -17,7 +17,7 @@ endif()
find_package(
Qt5
COMPONENTS Core Quick QuickControls2 WebEngine
COMPONENTS Core Quick QuickControls2 WebEngine Test
REQUIRED)
file(GLOB_RECURSE QML_FILES "stubs/*.qml" "mocks/*.qml" "pages/*.qml" "src/*.qml" "src/qmldir" "../ui/StatusQ/*.qml" "../ui/app/*.qml")
@ -28,6 +28,7 @@ add_executable(
main.cpp
cachecleaner.cpp cachecleaner.h
directorieswatcher.cpp directorieswatcher.h
sectionsdecoratormodel.cpp sectionsdecoratormodel.h
${QML_FILES} main.qml
${JS_FILES}
)
@ -38,6 +39,11 @@ target_link_libraries(
${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Quick Qt5::QuickControls2 Qt5::WebEngine
SortFilterProxyModel)
enable_testing()
add_executable(SectionsDecoratorModelTest tests/tst_SectionsDecoratorModel.cpp sectionsdecoratormodel.cpp)
add_test(NAME SectionsDecoratorModelTest COMMAND SectionsDecoratorModelTest)
target_link_libraries(SectionsDecoratorModelTest PRIVATE Qt5::Test)
list(APPEND QML_DIRS "${CMAKE_SOURCE_DIR}/../ui/StatusQ/src")
list(APPEND QML_DIRS "${CMAKE_SOURCE_DIR}/../ui/app")
list(APPEND QML_DIRS "${CMAKE_SOURCE_DIR}/../ui/imports")

View File

@ -0,0 +1,162 @@
#include "sectionsdecoratormodel.h"
#include <QScopeGuard>
SectionsDecoratorModel::SectionsDecoratorModel(QObject *parent)
: QAbstractListModel{parent}
{
}
void SectionsDecoratorModel::setSourceModel(QAbstractItemModel *sourceModel)
{
if (m_sourceModel != nullptr) {
qWarning("Changing source model is not supported!");
return;
}
m_sourceModel = sourceModel;
initialize();
connect(sourceModel, &QAbstractItemModel::modelReset, this, &SectionsDecoratorModel::initialize);
connect(sourceModel, &QAbstractItemModel::rowsInserted, this, &SectionsDecoratorModel::initialize);
connect(sourceModel, &QAbstractItemModel::rowsRemoved, this, &SectionsDecoratorModel::initialize);
connect(sourceModel, &QAbstractItemModel::rowsMoved, this, &SectionsDecoratorModel::initialize);
emit sourceModelChanged();
}
QAbstractItemModel* SectionsDecoratorModel::sourceModel() const
{
return m_sourceModel;
}
int SectionsDecoratorModel::rowCount(const QModelIndex &parent) const
{
return m_rowsMetadata.size();
}
QVariant SectionsDecoratorModel::data(const QModelIndex &index, int role) const
{
if (!checkIndex(index, CheckIndexOption::IndexIsValid))
return {};
const int row = index.row();
const RowMetadata &rowMetadata = m_rowsMetadata[row];
if (role == IsFoldedRole) {
return rowMetadata.folded;
} else if (role == SubitemsCountRole) {
return rowMetadata.count;
} else if (role == IsSectionRole) {
return rowMetadata.isSection;
} else if (role == m_sectionRole && rowMetadata.isSection) {
return rowMetadata.sectionName;
}
if (!rowMetadata.isSection) {
return sourceModel()->data(
sourceModel()->index(row - rowMetadata.offset, 0), role);
} else {
return QVariant();
}
}
QHash<int, QByteArray> SectionsDecoratorModel::roleNames() const
{
auto roles = m_sourceModel ? m_sourceModel->roleNames() : QHash<int, QByteArray>{};
roles.insert(IsSectionRole, QByteArrayLiteral("isSection"));
roles.insert(IsFoldedRole, QByteArrayLiteral("isFolded"));
roles.insert(SubitemsCountRole, QByteArrayLiteral("subitemsCount"));
return roles;
}
void SectionsDecoratorModel::flipFolding(int index)
{
auto &row = m_rowsMetadata[index];
row.folded = !row.folded;
const auto idx = this->index(index, 0, {});
if (row.folded) {
beginRemoveRows(QModelIndex(), index + 1, index + row.count);
m_rowsMetadata.erase(m_rowsMetadata.begin() + index + 1,
m_rowsMetadata.begin() + index + 1 + row.count);
calculateOffsets();
endRemoveRows();
} else {
beginInsertRows(QModelIndex(), index + 1, index + row.count);
m_rowsMetadata.insert(m_rowsMetadata.begin() + index + 1, row.count,
RowMetadata{false});
calculateOffsets();
endInsertRows();
}
emit dataChanged(idx, idx, { IsFoldedRole });
}
void SectionsDecoratorModel::calculateOffsets()
{
std::for_each(m_rowsMetadata.begin(), m_rowsMetadata.end(),
[offset = 0](RowMetadata &row) mutable {
if (row.isSection) {
++offset;
if (row.folded)
offset -= row.count;
} else {
row.offset = offset;
}
});
}
std::optional<int> SectionsDecoratorModel::findSectionRole() const
{
const auto roleNames = m_sourceModel->roleNames();
auto i = roleNames.constBegin();
while (i != roleNames.constEnd()) {
if (i.value() == QStringLiteral("section"))
return i.key();
++i;
}
return std::nullopt;
}
void SectionsDecoratorModel::initialize()
{
beginResetModel();
auto endResetModelGuard = qScopeGuard([this] { endResetModel(); });
m_rowsMetadata.clear();
const auto sectionRoleOpt = findSectionRole();
if (!sectionRoleOpt) {
qWarning("Section role not found!");
return;
}
m_sectionRole = *sectionRoleOpt;
QString prevSection;
int prevSectionIndex = 0;
for (int i = 0; i < m_sourceModel->rowCount(); i++) {
const QVariant sectionVariant = m_sourceModel->data(
m_sourceModel->index(i, 0), m_sectionRole);
const QString section = sectionVariant.toString();
if (prevSection != section) {
m_rowsMetadata.push_back({true, section});
prevSection = section;
prevSectionIndex = m_rowsMetadata.size() - 1;
}
m_rowsMetadata.push_back({false});
m_rowsMetadata[prevSectionIndex].count++;
}
calculateOffsets();
}

View File

@ -0,0 +1,46 @@
#pragma once
#include <QAbstractListModel>
#include <optional>
class SectionsDecoratorModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(QAbstractItemModel* sourceModel READ sourceModel
WRITE setSourceModel NOTIFY sourceModelChanged)
public:
static constexpr int IsSectionRole = Qt::UserRole + 100;
static constexpr int IsFoldedRole = Qt::UserRole + 101;
static constexpr int SubitemsCountRole = Qt::UserRole + 102;
explicit SectionsDecoratorModel(QObject *parent = nullptr);
void setSourceModel(QAbstractItemModel *sourceModel);
QAbstractItemModel *sourceModel() const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
Q_INVOKABLE void flipFolding(int index);
signals:
void sourceModelChanged();
private:
struct RowMetadata {
bool isSection = false;
QString sectionName;
bool folded = false;
int offset = 0;
int count = 0;
};
std::optional<int> findSectionRole() const;
void initialize();
void calculateOffsets();
QAbstractItemModel* m_sourceModel = nullptr;
std::vector<RowMetadata> m_rowsMetadata;
int m_sectionRole = 0;
};

View File

@ -0,0 +1,502 @@
#include <QSignalSpy>
#include <QTest>
#include <QAbstractListModel>
#include <QSortFilterProxyModel>
#include <sectionsdecoratormodel.h>
namespace {
class TestSourceModel : public QAbstractListModel {
public:
explicit TestSourceModel(QStringList sections)
: m_sections(std::move(sections))
{
}
static constexpr int TitleRole = Qt::UserRole + 1;
static constexpr int SectionRole = Qt::UserRole + 2;
int rowCount(const QModelIndex &parent) const override {
return m_sections.size();
}
QVariant data(const QModelIndex &index, int role) const override {
if (!index.isValid())
return {};
if (role == TitleRole) {
return QString("title %1").arg(index.row());
}
return m_sections.at(index.row());
}
QHash<int, QByteArray> roleNames() const override {
QHash<int, QByteArray> roles;
roles.insert(TitleRole, "title");
roles.insert(SectionRole, "section");
return roles;
}
QStringList m_sections;
};
} // unnamed namespace
class TestSectionsDecoratorModel: public QObject
{
Q_OBJECT
private slots:
void emptyModelTest() {
SectionsDecoratorModel model;
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames().count(), 3);
QVERIFY(model.roleNames().contains(SectionsDecoratorModel::IsSectionRole));
QVERIFY(model.roleNames().contains(SectionsDecoratorModel::IsFoldedRole));
QVERIFY(model.roleNames().contains(SectionsDecoratorModel::SubitemsCountRole));
}
void emptySourceTest() {
TestSourceModel src(QStringList{});
SectionsDecoratorModel model;
model.setSourceModel(&src);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames().count(), 5);
QVERIFY(model.roleNames().contains(SectionsDecoratorModel::IsSectionRole));
QVERIFY(model.roleNames().contains(SectionsDecoratorModel::IsFoldedRole));
QVERIFY(model.roleNames().contains(SectionsDecoratorModel::SubitemsCountRole));
QVERIFY(model.roleNames().contains(TestSourceModel::TitleRole));
QVERIFY(model.roleNames().contains(TestSourceModel::SectionRole));
}
void initialUnfoldedStateTest() {
TestSourceModel src(QStringList{"Section 1", "Section 1", "Section 1", "Section 2", "Section 2", "Section 3"});
SectionsDecoratorModel model;
model.setSourceModel(&src);
QCOMPARE(model.rowCount(), 9);
QCOMPARE(model.roleNames().count(), 5);
QCOMPARE(model.data(model.index(0, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(1, 0), TestSourceModel::TitleRole).toString(), QString("title 0"));
QCOMPARE(model.data(model.index(2, 0), TestSourceModel::TitleRole).toString(), QString("title 1"));
QCOMPARE(model.data(model.index(3, 0), TestSourceModel::TitleRole).toString(), QString("title 2"));
QCOMPARE(model.data(model.index(4, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(5, 0), TestSourceModel::TitleRole).toString(), QString("title 3"));
QCOMPARE(model.data(model.index(6, 0), TestSourceModel::TitleRole).toString(), QString("title 4"));
QCOMPARE(model.data(model.index(7, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(8, 0), TestSourceModel::TitleRole).toString(), QString("title 5"));
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(6, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(7, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(8, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(6, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(7, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(8, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 3);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 2);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(7, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 1);
QCOMPARE(model.data(model.index(8, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
}
void foldingFromTopToBottomTest() {
TestSourceModel src(QStringList{"Section 1", "Section 1", "Section 1", "Section 2", "Section 2", "Section 3"});
SectionsDecoratorModel model;
model.setSourceModel(&src);
model.flipFolding(0);
QCOMPARE(model.rowCount(), 6);
QCOMPARE(model.roleNames().count(), 5);
QCOMPARE(model.data(model.index(0, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(1, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(2, 0), TestSourceModel::TitleRole).toString(), QString("title 3"));
QCOMPARE(model.data(model.index(3, 0), TestSourceModel::TitleRole).toString(), QString("title 4"));
QCOMPARE(model.data(model.index(4, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(5, 0), TestSourceModel::TitleRole).toString(), QString("title 5"));
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), true);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 3);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 2);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 1);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
model.flipFolding(1);
QCOMPARE(model.rowCount(), 4);
QCOMPARE(model.data(model.index(0, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(1, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(2, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(3, 0), TestSourceModel::TitleRole).toString(), QString("title 5"));
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), true);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), true);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 3);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 2);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 1);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
model.flipFolding(2);
QCOMPARE(model.rowCount(), 3);
QCOMPARE(model.data(model.index(0, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(1, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(2, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), true);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), true);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), true);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 3);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 2);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 1);
}
void foldingFromBottomToTopTest() {
TestSourceModel src(QStringList{"Section 1", "Section 1", "Section 1", "Section 2", "Section 2", "Section 3"});
SectionsDecoratorModel model;
model.setSourceModel(&src);
model.flipFolding(7);
QCOMPARE(model.rowCount(), 8);
QCOMPARE(model.roleNames().count(), 5);
QCOMPARE(model.data(model.index(0, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(1, 0), TestSourceModel::TitleRole).toString(), QString("title 0"));
QCOMPARE(model.data(model.index(2, 0), TestSourceModel::TitleRole).toString(), QString("title 1"));
QCOMPARE(model.data(model.index(3, 0), TestSourceModel::TitleRole).toString(), QString("title 2"));
QCOMPARE(model.data(model.index(4, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(5, 0), TestSourceModel::TitleRole).toString(), QString("title 3"));
QCOMPARE(model.data(model.index(6, 0), TestSourceModel::TitleRole).toString(), QString("title 4"));
QCOMPARE(model.data(model.index(7, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(6, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(7, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(6, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(7, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), true);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 3);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 2);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(6, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(7, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 1);
model.flipFolding(4);
QCOMPARE(model.rowCount(), 6);
QCOMPARE(model.roleNames().count(), 5);
QCOMPARE(model.data(model.index(0, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(1, 0), TestSourceModel::TitleRole).toString(), QString("title 0"));
QCOMPARE(model.data(model.index(2, 0), TestSourceModel::TitleRole).toString(), QString("title 1"));
QCOMPARE(model.data(model.index(3, 0), TestSourceModel::TitleRole).toString(), QString("title 2"));
QCOMPARE(model.data(model.index(4, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(5, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), true);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), true);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 3);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 2);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 1);
model.flipFolding(0);
QCOMPARE(model.rowCount(), 3);
QCOMPARE(model.roleNames().count(), 5);
QCOMPARE(model.data(model.index(0, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(1, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(2, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), true);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), true);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), true);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 3);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 2);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 1);
}
void unfoldingTest() {
TestSourceModel src(QStringList{"Section 1", "Section 1", "Section 1", "Section 2", "Section 2", "Section 3"});
SectionsDecoratorModel model;
model.setSourceModel(&src);
model.flipFolding(0);
model.flipFolding(1);
model.flipFolding(2);
model.flipFolding(2);
model.flipFolding(1);
model.flipFolding(0);
QCOMPARE(model.rowCount(), 9);
QCOMPARE(model.roleNames().count(), 5);
QCOMPARE(model.data(model.index(0, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(1, 0), TestSourceModel::TitleRole).toString(), QString("title 0"));
QCOMPARE(model.data(model.index(2, 0), TestSourceModel::TitleRole).toString(), QString("title 1"));
QCOMPARE(model.data(model.index(3, 0), TestSourceModel::TitleRole).toString(), QString("title 2"));
QCOMPARE(model.data(model.index(4, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(5, 0), TestSourceModel::TitleRole).toString(), QString("title 3"));
QCOMPARE(model.data(model.index(6, 0), TestSourceModel::TitleRole).toString(), QString("title 4"));
QCOMPARE(model.data(model.index(7, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(8, 0), TestSourceModel::TitleRole).toString(), QString("title 5"));
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(6, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(7, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(8, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(6, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(7, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(8, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 3);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 2);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(7, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 1);
QCOMPARE(model.data(model.index(8, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
model.flipFolding(0);
model.flipFolding(1);
model.flipFolding(2);
model.flipFolding(0);
model.flipFolding(4);
model.flipFolding(7);
QCOMPARE(model.rowCount(), 9);
QCOMPARE(model.roleNames().count(), 5);
QCOMPARE(model.data(model.index(0, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(1, 0), TestSourceModel::TitleRole).toString(), QString("title 0"));
QCOMPARE(model.data(model.index(2, 0), TestSourceModel::TitleRole).toString(), QString("title 1"));
QCOMPARE(model.data(model.index(3, 0), TestSourceModel::TitleRole).toString(), QString("title 2"));
QCOMPARE(model.data(model.index(4, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(5, 0), TestSourceModel::TitleRole).toString(), QString("title 3"));
QCOMPARE(model.data(model.index(6, 0), TestSourceModel::TitleRole).toString(), QString("title 4"));
QCOMPARE(model.data(model.index(7, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(8, 0), TestSourceModel::TitleRole).toString(), QString("title 5"));
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(6, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(7, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(8, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(6, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(7, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(8, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 3);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 2);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(7, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 1);
QCOMPARE(model.data(model.index(8, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
}
void basicFilteringTest() {
TestSourceModel src(QStringList{"Section 1", "Section 1", "Section 1", "Section 2", "Section 2", "Section 3"});
QSortFilterProxyModel proxy;
proxy.setSourceModel(&src);
SectionsDecoratorModel model;
model.setSourceModel(&proxy);
QCOMPARE(model.rowCount(), 9);
QCOMPARE(model.roleNames().count(), 5);
QCOMPARE(model.data(model.index(0, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(1, 0), TestSourceModel::TitleRole).toString(), QString("title 0"));
QCOMPARE(model.data(model.index(2, 0), TestSourceModel::TitleRole).toString(), QString("title 1"));
QCOMPARE(model.data(model.index(3, 0), TestSourceModel::TitleRole).toString(), QString("title 2"));
QCOMPARE(model.data(model.index(4, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(5, 0), TestSourceModel::TitleRole).toString(), QString("title 3"));
QCOMPARE(model.data(model.index(6, 0), TestSourceModel::TitleRole).toString(), QString("title 4"));
QCOMPARE(model.data(model.index(7, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(8, 0), TestSourceModel::TitleRole).toString(), QString("title 5"));
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(6, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(7, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(8, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(6, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(7, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(8, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 3);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(2, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(3, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(4, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 2);
QCOMPARE(model.data(model.index(5, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
QCOMPARE(model.data(model.index(7, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 1);
QCOMPARE(model.data(model.index(8, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
}
void filteringTest() {
TestSourceModel src(QStringList{"Section 1", "Section 1", "Section 1", "Section 2", "Section 2", "Section 3"});
QSortFilterProxyModel proxy;
proxy.setSourceModel(&src);
SectionsDecoratorModel model;
model.setSourceModel(&proxy);
QSignalSpy spy(&model, SIGNAL(modelReset()));
proxy.setFilterRole(TestSourceModel::TitleRole);
proxy.setFilterWildcard("*1");
QVERIFY(spy.count() > 1);
QCOMPARE(model.rowCount(), 2);
QCOMPARE(model.roleNames().count(), 5);
QCOMPARE(model.data(model.index(0, 0), TestSourceModel::TitleRole), QVariant{});
QCOMPARE(model.data(model.index(1, 0), TestSourceModel::TitleRole).toString(), QString("title 1"));
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsSectionRole).toBool(), true);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsSectionRole).toBool(), false);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::IsFoldedRole).toBool(), false);
QCOMPARE(model.data(model.index(0, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 1);
QCOMPARE(model.data(model.index(1, 0), SectionsDecoratorModel::SubitemsCountRole).toInt(), 0);
}
};
// TODO: signals emission testing using QSignalSpy
QTEST_MAIN(TestSectionsDecoratorModel)
#include "tst_SectionsDecoratorModel.moc"