feat(StatusQ): Ability to change sources in LeftJoinModel

- ability to change left/right models
- improved handling of model deletion
- base class changed to QAbstractListModel, dataChanged signal
  emision improved

Closes: #12912
This commit is contained in:
Michał Cieślak 2023-12-18 10:53:17 +01:00 committed by Michał
parent 6fe2067c22
commit fbe6cc95d1
4 changed files with 377 additions and 103 deletions

View File

@ -1,8 +1,9 @@
#pragma once
#include <QIdentityProxyModel>
#include <QAbstractListModel>
#include <QPointer>
class LeftJoinModel : public QIdentityProxyModel
class LeftJoinModel : public QAbstractListModel
{
Q_OBJECT
@ -27,9 +28,9 @@ public:
void setJoinRole(const QString& joinRole);
const QString& joinRole() const;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QHash<int, QByteArray> roleNames() const override;
QVariant data(const QModelIndex& index, int role) const override;
void setSourceModel(QAbstractItemModel* newSourceModel) override;
signals:
void leftModelChanged();
@ -37,10 +38,15 @@ signals:
void joinRoleChanged();
private:
void initializeIfReady();
void initialize();
void initializeIfReady(bool reset);
void initialize(bool reset);
void connectLeftModelSignals();
void connectRightModelSignals();
int m_rightModelRolesOffset = 0;
QHash<int, QByteArray> m_leftRoleNames;
QHash<int, QByteArray> m_rightRoleNames;
QHash<int, QByteArray> m_roleNames;
QVector<int> m_joinedRoles;
@ -48,11 +54,10 @@ private:
int m_leftModelJoinRole = 0;
int m_rightModelJoinRole = 0;
QAbstractItemModel* m_leftModel = nullptr;
QAbstractItemModel* m_rightModel = nullptr;
QPointer<QAbstractItemModel> m_leftModel;
QPointer<QAbstractItemModel> m_rightModel;
bool m_leftModelDestroyed = false;
bool m_rightModelDestroyed = false;
bool m_initialized = false;
mutable QPersistentModelIndex m_lastUsedRightModelIndex;
};

View File

@ -5,19 +5,19 @@
#include <algorithm>
LeftJoinModel::LeftJoinModel(QObject* parent)
: QIdentityProxyModel{parent}
: QAbstractListModel{parent}
{
}
void LeftJoinModel::initializeIfReady()
void LeftJoinModel::initializeIfReady(bool reset)
{
if (m_leftModel && m_rightModel && !m_joinRole.isEmpty()
&& !m_leftModel->roleNames().empty()
&& !m_rightModel->roleNames().empty())
initialize();
initialize(reset);
}
void LeftJoinModel::initialize()
void LeftJoinModel::initialize(bool reset)
{
auto leftRoleNames = m_leftModel->roleNames();
auto rightRoleNames = m_rightModel->roleNames();
@ -51,6 +51,9 @@ void LeftJoinModel::initialize()
return;
}
if (reset)
beginResetModel();
auto leftRoles = leftRoleNames.keys();
auto maxLeftRole = std::max_element(leftRoles.cbegin(), leftRoles.cend());
auto rightRolesOffset = *maxLeftRole + 1;
@ -73,6 +76,92 @@ void LeftJoinModel::initialize()
m_rightModelJoinRole = rightRoleNames.key(m_joinRole.toUtf8());
m_rightModelRolesOffset = rightRolesOffset;
m_leftRoleNames = std::move(leftRoleNames);
m_rightRoleNames = std::move(rightRoleNames);
disconnect(m_leftModel, nullptr, this, nullptr);
disconnect(m_rightModel, nullptr, this, nullptr);
connectRightModelSignals();
connectLeftModelSignals();
m_initialized = true;
if (reset)
endResetModel();
}
void LeftJoinModel::connectLeftModelSignals()
{
connect(m_leftModel, &QAbstractItemModel::dataChanged, this,
[this](auto& topLeft, auto& bottomRight, auto& roles) {
auto tl = index(topLeft.row());
auto br = index(bottomRight.row());
if (roles.contains(m_leftModelJoinRole))
emit dataChanged(tl, br, m_joinedRoles + roles);
else
emit dataChanged(tl, br, roles);
});
connect(m_leftModel, &QAbstractItemModel::rowsAboutToBeInserted,
this, [this](const QModelIndex& parent, int first, int last) {
if (!parent.isValid())
beginInsertRows({}, first, last);
});
connect(m_leftModel, &QAbstractItemModel::rowsInserted,
this, [this](const QModelIndex& parent, int first, int last) {
if (!parent.isValid())
endInsertRows();
});
connect(m_leftModel, &QAbstractItemModel::rowsAboutToBeRemoved,
this, [this](const QModelIndex& parent, int first, int last) {
if (!parent.isValid())
beginRemoveRows({}, first, last);
});
connect(m_leftModel, &QAbstractItemModel::rowsRemoved,
this, [this](const QModelIndex& parent, int first, int last) {
if (!parent.isValid())
endRemoveRows();
});
connect(m_leftModel, &QAbstractItemModel::rowsAboutToBeMoved,
this, [this](const QModelIndex &sourceParent, int sourceStart,
int sourceEnd, const QModelIndex &destinationParent, int destinationRow) {
if (!sourceParent.isValid() && !destinationParent.isValid())
beginMoveRows({}, sourceStart, sourceEnd, {}, destinationRow);
});
connect(m_leftModel, &QAbstractItemModel::rowsMoved,
this, [this](const QModelIndex &sourceParent, int sourceStart,
int sourceEnd, const QModelIndex &destinationParent, int destinationRow) {
if (!sourceParent.isValid() && !destinationParent.isValid())
endMoveRows();
});
connect(m_leftModel, &QAbstractItemModel::layoutAboutToBeChanged, this, [this]() {
emit layoutAboutToBeChanged();
});
connect(m_leftModel, &QAbstractItemModel::layoutChanged, this, [this]() {
emit layoutChanged();
});
connect(m_leftModel, &QAbstractItemModel::modelAboutToBeReset, this, [this]() {
beginResetModel();
});
connect(m_leftModel, &QAbstractItemModel::modelReset, this, [this]() {
endResetModel();
});
}
void LeftJoinModel::connectRightModelSignals()
{
connect(m_rightModel, &QAbstractItemModel::dataChanged, this,
[this](auto& topLeft, auto& bottomRight, auto& roles) {
QVector<int> rolesTranslated;
@ -86,16 +175,11 @@ void LeftJoinModel::initialize()
role += m_rightModelRolesOffset;
}
emit dataChanged(index(0, 0), index(rowCount() - 1, 0), rolesTranslated);
emit dataChanged(index(0), index(rowCount() - 1), rolesTranslated);
});
disconnect(m_leftModel, &QAbstractItemModel::rowsInserted,
this, &LeftJoinModel::initializeIfReady);
disconnect(m_rightModel, &QAbstractItemModel::rowsInserted,
this, &LeftJoinModel::initializeIfReady);
auto emitJoinedRolesChanged = [this] {
emit dataChanged(index(0, 0), index(rowCount() - 1, 0), m_joinedRoles);
emit dataChanged(index(0), index(rowCount() - 1), m_joinedRoles);
};
connect(m_rightModel, &QAbstractItemModel::rowsRemoved, this,
@ -106,19 +190,11 @@ void LeftJoinModel::initialize()
emitJoinedRolesChanged);
connect(m_rightModel, &QAbstractItemModel::layoutChanged, this,
emitJoinedRolesChanged);
connect(this, &QAbstractItemModel::dataChanged, this,
[this](auto& topLeft, auto& bottomRight, auto& roles) {
if (roles.contains(m_leftModelJoinRole))
emit dataChanged(topLeft, bottomRight, m_joinedRoles);
});
QIdentityProxyModel::setSourceModel(m_leftModel);
}
QVariant LeftJoinModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
if (!index.isValid() || m_leftModel == nullptr)
return {};
auto idx = m_leftModel->index(index.row(), index.column());
@ -126,7 +202,7 @@ QVariant LeftJoinModel::data(const QModelIndex& index, int role) const
if (role < m_rightModelRolesOffset)
return m_leftModel->data(idx, role);
if (m_rightModelDestroyed)
if (m_rightModel == nullptr)
return {};
auto joinRoleLeftValue = m_leftModel->data(idx, m_leftModelJoinRole);
@ -154,27 +230,29 @@ void LeftJoinModel::setLeftModel(QAbstractItemModel* model)
if (m_leftModel == model)
return;
if (m_leftModel != nullptr || m_leftModelDestroyed) {
qWarning("Changing left model is not supported!");
return;
}
if (m_leftModel)
disconnect(m_leftModel, nullptr, this, nullptr);
bool was_initialized = m_initialized;
if (was_initialized)
beginResetModel();
m_initialized = false;
m_leftModel = model;
// Some models may have roles undefined until first row is inserted,
// like ListModel, therefore in such cases initialization must be deferred
// until first insertion.
connect(m_leftModel, &QAbstractItemModel::rowsInserted,
this, &LeftJoinModel::initializeIfReady);
connect(m_leftModel, &QObject::destroyed, this, [this] {
this->m_leftModel = nullptr;
this->m_leftModelDestroyed = true;
});
this, [this]() { initializeIfReady(true); });
emit leftModelChanged();
initializeIfReady();
initializeIfReady(!was_initialized);
if (was_initialized)
endResetModel();
}
QAbstractItemModel* LeftJoinModel::leftModel() const
@ -187,25 +265,41 @@ void LeftJoinModel::setRightModel(QAbstractItemModel* model)
if (m_rightModel == model)
return;
if (m_rightModel != nullptr || m_rightModelDestroyed) {
qWarning("Changing right model is not supported!");
if (m_rightModel)
disconnect(m_rightModel, nullptr, this, nullptr);
if (m_initialized &&
(model == nullptr || model->roleNames() == m_rightRoleNames)) {
m_rightModel = model;
emit rightModelChanged();
auto count = rowCount();
if (count > 0)
emit dataChanged(index(0), index(count - 1), m_joinedRoles);
return;
}
bool was_initialized = m_initialized;
if (was_initialized)
beginResetModel();
m_initialized = false;
m_rightModel = model;
// see: LeftJoinModel::setLeftModel
connect(m_rightModel, &QAbstractItemModel::rowsInserted,
this, &LeftJoinModel::initializeIfReady);
connect(m_rightModel, &QObject::destroyed, this, [this] {
this->m_rightModel = nullptr;
this->m_rightModelDestroyed = true;
});
this, [this]() { initializeIfReady(true); });
emit rightModelChanged();
initializeIfReady();
initializeIfReady(!was_initialized);
if (was_initialized)
endResetModel();
}
QAbstractItemModel* LeftJoinModel::rightModel() const
@ -227,7 +321,7 @@ void LeftJoinModel::setJoinRole(const QString& joinRole)
emit joinRoleChanged();
initializeIfReady();
initializeIfReady(true);
}
const QString& LeftJoinModel::joinRole() const
@ -235,10 +329,13 @@ const QString& LeftJoinModel::joinRole() const
return m_joinRole;
}
void LeftJoinModel::setSourceModel(QAbstractItemModel* newSourceModel)
int LeftJoinModel::rowCount(const QModelIndex &parent) const
{
qWarning() << "Source model is not intended to be set directly on this model."
" Use setLeftModel and setRightModel instead!";
if (parent.isValid())
return 0;
return m_leftModel == nullptr || !m_initialized
? 0 : m_leftModel->rowCount();
}
QHash<int, QByteArray> LeftJoinModel::roleNames() const

View File

@ -17,8 +17,9 @@
#include <StatusQ/concatmodel.h>
namespace {
// Workaround for a bug in QIdentityProxyModel (returning roleNames improperly)
// Workaround for https://bugreports.qt.io/browse/QTBUG-57971 (ListModel doesn't
// emit modelReset when role names are initially set, therefore QIdentityProxyModel
// doesn't update role names appropriately)
class IdentityModel : public QIdentityProxyModel {
public:
QHash<int,QByteArray> roleNames() const override {

View File

@ -103,25 +103,6 @@ private slots:
QCOMPARE(model.roleNames(), {});
}
void setSourceModelDirectlyTest()
{
TestSourceModel leftModel({
{ "title", { "Token 1", "Token 2" }},
{ "communityId", { "community_1", "community_2" }}
});
LeftJoinModel model;
QAbstractItemModelTester tester(&model);
QTest::ignoreMessage(QtWarningMsg,
"Source model is not intended to be set directly "
"on this model. Use setLeftModel and setRightModel instead!");
model.setSourceModel(&leftModel);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
}
void initializationTest()
{
TestSourceModel leftModel({
@ -488,6 +469,64 @@ private slots:
}
}
// TODO: cover also move and layoutChanged
void insertRemovePropagationTest()
{
TestSourceModel leftModel({
{ "title", { "Token 1", "Token 2" }},
{ "communityId", { "community_1", "community_2" }}
});
TestSourceModel rightModel({
{ "name", { "Community 1", "Community 2" }},
{ "communityId", { "community_1", "community_2" }},
{ "color", { "red", "green" }}
});
LeftJoinModel model;
QAbstractItemModelTester tester(&model);
model.setLeftModel(&leftModel);
model.setRightModel(&rightModel);
model.setJoinRole("communityId");
QSignalSpy rowsInsertedSpy(&model, &LeftJoinModel::rowsInserted);
QSignalSpy rowsRemovedSpy(&model, &LeftJoinModel::rowsRemoved);
leftModel.insert(1, {"Token 1_1", "community_2"});
QCOMPARE(model.rowCount(), 3);
QCOMPARE(model.data(model.index(0, 0), 0), QString("Token 1"));
QCOMPARE(model.data(model.index(1, 0), 0), QString("Token 1_1"));
QCOMPARE(model.data(model.index(2, 0), 0), QString("Token 2"));
QCOMPARE(model.data(model.index(0, 0), 1), QString("community_1"));
QCOMPARE(model.data(model.index(1, 0), 1), QString("community_2"));
QCOMPARE(model.data(model.index(2, 0), 1), QString("community_2"));
QCOMPARE(model.data(model.index(0, 0), 2), QString("Community 1"));
QCOMPARE(model.data(model.index(1, 0), 2), QString("Community 2"));
QCOMPARE(model.data(model.index(2, 0), 2), QString("Community 2"));
leftModel.remove(1);
QCOMPARE(model.rowCount(), 2);
QCOMPARE(model.data(model.index(0, 0), 0), QString("Token 1"));
QCOMPARE(model.data(model.index(1, 0), 0), QString("Token 2"));
QCOMPARE(model.data(model.index(0, 0), 1), QString("community_1"));
QCOMPARE(model.data(model.index(1, 0), 1), QString("community_2"));
QCOMPARE(model.data(model.index(0, 0), 2), QString("Community 1"));
QCOMPARE(model.data(model.index(1, 0), 2), QString("Community 2"));
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.first().at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.first().at(1), 1);
QCOMPARE(rowsInsertedSpy.first().at(2), 1);
QCOMPARE(rowsRemovedSpy.count(), 1);
QCOMPARE(rowsRemovedSpy.first().at(0), QModelIndex{});
QCOMPARE(rowsRemovedSpy.first().at(1), 1);
QCOMPARE(rowsRemovedSpy.first().at(2), 1);
}
void rightModelJoinRoleChangesPropagationTest()
{
TestSourceModel leftModel({
@ -624,15 +663,14 @@ private slots:
QSignalSpy dataChangedSpy(&model, &LeftJoinModel::dataChanged);
leftModel.update(1, 1, "community_1");
QCOMPARE(dataChangedSpy.count(), 2);
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.first().at(0), model.index(1, 0));
QCOMPARE(dataChangedSpy.first().at(1), model.index(1, 0));
QCOMPARE(dataChangedSpy.first().at(2).value<QVector<int>>(), {2});
QCOMPARE(dataChangedSpy.at(1).at(0), model.index(1, 0));
QCOMPARE(dataChangedSpy.at(1).at(1), model.index(1, 0));
QCOMPARE(dataChangedSpy.at(1).at(2).value<QVector<int>>(), {1});
auto changedRoles = dataChangedSpy.first().at(2).value<QVector<int>>();
QVERIFY(changedRoles.contains(1));
QVERIFY(changedRoles.contains(2));
QCOMPARE(model.rowCount(), 3);
QCOMPARE(model.data(model.index(0, 0), 0), QString("Token 1"));
@ -663,6 +701,8 @@ private slots:
LeftJoinModel model;
QAbstractItemModelTester tester(&model);
QSignalSpy modelResetSpy(&model, &LeftJoinModel::modelReset);
model.setLeftModel(leftModel.get());
model.setRightModel(rightModel.get());
@ -686,13 +726,21 @@ private slots:
{ "communityId", { "community_1", "community_2" }}
});
QTest::ignoreMessage(QtWarningMsg,
"Changing left model is not supported!");
model.setLeftModel(&newLeftModel);
QTest::ignoreMessage(QtWarningMsg,
"Changing right model is not supported!");
model.setRightModel(&newRightModel);
QCOMPARE(model.rowCount(), 3);
QCOMPARE(model.data(model.index(0, 0), 0), QString("Token 1"));
QCOMPARE(model.data(model.index(1, 0), 0), QString("Token 2"));
QCOMPARE(model.data(model.index(2, 0), 0), QString("Token 3"));
QCOMPARE(model.data(model.index(0, 0), 1), QString("community_1"));
QCOMPARE(model.data(model.index(1, 0), 1), QString("community_2"));
QCOMPARE(model.data(model.index(2, 0), 1), QString("community_1"));
QCOMPARE(model.data(model.index(0, 0), 2), QString("Community 1"));
QCOMPARE(model.data(model.index(1, 0), 2), QString("Community 2"));
QCOMPARE(model.data(model.index(2, 0), 2), QString("Community 1"));
QCOMPARE(modelResetSpy.count(), 1);
}
void modelsDeletedAfterInitializationTest()
@ -712,11 +760,15 @@ private slots:
LeftJoinModel model;
QAbstractItemModelTester tester(&model);
QSignalSpy modelResetSpy(&model, &LeftJoinModel::modelReset);
model.setLeftModel(leftModel.get());
model.setRightModel(rightModel.get());
model.setJoinRole("communityId");
QCOMPARE(modelResetSpy.count(), 1);
leftModel.reset();
rightModel.reset();
@ -736,13 +788,25 @@ private slots:
{ "communityId", { "community_1", "community_2" }}
});
QTest::ignoreMessage(QtWarningMsg,
"Changing left model is not supported!");
model.setLeftModel(&newLeftModel);
QTest::ignoreMessage(QtWarningMsg,
"Changing right model is not supported!");
QCOMPARE(modelResetSpy.count(), 2);
QCOMPARE(model.rowCount(), 0);
model.setRightModel(&newRightModel);
QCOMPARE(model.rowCount(), 3);
QCOMPARE(model.data(model.index(0, 0), 0), QString("Token 1"));
QCOMPARE(model.data(model.index(1, 0), 0), QString("Token 2"));
QCOMPARE(model.data(model.index(2, 0), 0), QString("Token 3"));
QCOMPARE(model.data(model.index(0, 0), 1), QString("community_1"));
QCOMPARE(model.data(model.index(1, 0), 1), QString("community_2"));
QCOMPARE(model.data(model.index(2, 0), 1), QString("community_1"));
QCOMPARE(model.data(model.index(0, 0), 2), QString("Community 1"));
QCOMPARE(model.data(model.index(1, 0), 2), QString("Community 2"));
QCOMPARE(model.data(model.index(2, 0), 2), QString("Community 1"));
QCOMPARE(modelResetSpy.count(), 3);
}
void rightModelDeletedAfterInitializationTest()
@ -786,13 +850,120 @@ private slots:
{ "communityId", { "community_1", "community_2" }}
});
QTest::ignoreMessage(QtWarningMsg,
"Changing left model is not supported!");
model.setLeftModel(&newLeftModel);
QTest::ignoreMessage(QtWarningMsg,
"Changing right model is not supported!");
model.setRightModel(&newRightModel);
QCOMPARE(model.rowCount(), 3);
QCOMPARE(model.data(model.index(0, 0), 0), QString("Token 1"));
QCOMPARE(model.data(model.index(1, 0), 0), QString("Token 2"));
QCOMPARE(model.data(model.index(2, 0), 0), QString("Token 3"));
QCOMPARE(model.data(model.index(0, 0), 1), QString("community_1"));
QCOMPARE(model.data(model.index(1, 0), 1), QString("community_2"));
QCOMPARE(model.data(model.index(2, 0), 1), QString("community_1"));
QCOMPARE(model.data(model.index(0, 0), 2), QString("Community 1"));
QCOMPARE(model.data(model.index(1, 0), 2), QString("Community 2"));
QCOMPARE(model.data(model.index(2, 0), 2), QString("Community 1"));
}
void rightModelChangedWithSameRolesAfterInitializationTest()
{
TestSourceModel leftModel({
{ "title", { "Token 1", "Token 2", "Token 3"}},
{ "communityId", { "community_1", "community_2", "community_1" }}
});
TestSourceModel rightModel({
{ "name", { "Community 1", "Community 2" }},
{ "communityId", { "community_1", "community_2" }}
});
LeftJoinModel model;
QAbstractItemModelTester tester(&model);
model.setLeftModel(&leftModel);
model.setRightModel(&rightModel);
model.setJoinRole("communityId");
TestSourceModel newRightModel({
{ "name", { "Community A", "Community B" }},
{ "communityId", { "community_1", "community_2" }}
});
QSignalSpy modelResetSpy(&model, &LeftJoinModel::modelReset);
QSignalSpy dataChangedSpy(&model, &LeftJoinModel::dataChanged);
model.setRightModel(&newRightModel);
QHash<int, QByteArray> roles{{0, "title" }, {1, "communityId"}, {2, "name"}};
QCOMPARE(model.roleNames(), roles);
QCOMPARE(model.rowCount(), 3);
QCOMPARE(model.rowCount(), 3);
QCOMPARE(model.data(model.index(0, 0), 0), QString("Token 1"));
QCOMPARE(model.data(model.index(1, 0), 0), QString("Token 2"));
QCOMPARE(model.data(model.index(2, 0), 0), QString("Token 3"));
QCOMPARE(model.data(model.index(0, 0), 1), QString("community_1"));
QCOMPARE(model.data(model.index(1, 0), 1), QString("community_2"));
QCOMPARE(model.data(model.index(2, 0), 1), QString("community_1"));
QCOMPARE(model.data(model.index(0, 0), 2), QString("Community A"));
QCOMPARE(model.data(model.index(1, 0), 2), QString("Community B"));
QCOMPARE(model.data(model.index(2, 0), 2), QString("Community A"));
QCOMPARE(modelResetSpy.count(), 0);
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.first().at(0), model.index(0, 0));
QCOMPARE(dataChangedSpy.first().at(1), model.index(model.rowCount() - 1, 0));
QCOMPARE(dataChangedSpy.first().at(2).value<QVector<int>>(), {2});
}
void rightModelChangedWithDifferentRolesAfterInitializationTest()
{
TestSourceModel leftModel({
{ "title", { "Token 1", "Token 2", "Token 3"}},
{ "communityId", { "community_1", "community_2", "community_1" }}
});
TestSourceModel rightModel({
{ "name", { "Community 1", "Community 2" }},
{ "communityId", { "community_1", "community_2" }}
});
LeftJoinModel model;
QAbstractItemModelTester tester(&model);
model.setLeftModel(&leftModel);
model.setRightModel(&rightModel);
model.setJoinRole("communityId");
TestSourceModel newRightModel({
{ "communityId", { "community_1", "community_2" }},
{ "name", { "Community A", "Community B" }}
});
QSignalSpy modelResetSpy(&model, &LeftJoinModel::modelReset);
QSignalSpy dataChangedSpy(&model, &LeftJoinModel::dataChanged);
model.setRightModel(&newRightModel);
QHash<int, QByteArray> roles{{0, "title" }, {1, "communityId"}, {3, "name"}};
QCOMPARE(model.roleNames(), roles);
QCOMPARE(model.rowCount(), 3);
QCOMPARE(model.rowCount(), 3);
QCOMPARE(model.data(model.index(0, 0), 0), QString("Token 1"));
QCOMPARE(model.data(model.index(1, 0), 0), QString("Token 2"));
QCOMPARE(model.data(model.index(2, 0), 0), QString("Token 3"));
QCOMPARE(model.data(model.index(0, 0), 1), QString("community_1"));
QCOMPARE(model.data(model.index(1, 0), 1), QString("community_2"));
QCOMPARE(model.data(model.index(2, 0), 1), QString("community_1"));
QCOMPARE(model.data(model.index(0, 0), 3), QString("Community A"));
QCOMPARE(model.data(model.index(1, 0), 3), QString("Community B"));
QCOMPARE(model.data(model.index(2, 0), 3), QString("Community A"));
QCOMPARE(modelResetSpy.count(), 1);
QCOMPARE(dataChangedSpy.count(), 0);
}
};