status-desktop/ui/StatusQ/tests/tst_ConcatModel.cpp

2034 lines
81 KiB
C++

#include <QIdentityProxyModel>
#include <QJsonArray>
#include <QJsonObject>
#include <QQmlEngine>
#include <QSignalSpy>
#include <QTest>
#include <memory>
#include <set>
#include <StatusQ/concatmodel.h>
#include <TestHelpers/listmodelwrapper.h>
namespace {
// 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 {
if (sourceModel())
return sourceModel()->roleNames();
return {};
}
};
} // unnamed namespace
class TestConcatModel: public QObject
{
Q_OBJECT
int roleForName(const QHash<int, QByteArray>& roles, const QByteArray& name) const
{
auto keys = roles.keys(name);
if (keys.empty())
return -1;
return keys.first();
}
private slots:
void initializationTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine, QJsonArray {
QJsonObject {{ "key", 1}, { "balance", 11 }, { "name", "n1" }},
QJsonObject {{ "key", 2},{ "balance", 12 }, { "name", "n2" }},
QJsonObject {{ "key", 3},{ "balance", 13}, { "name", "n3" }},
});
ListModelWrapper sourceModel2(engine, QJsonArray {});
ListModelWrapper sourceModel3(engine, QJsonArray {
QJsonObject {{ "balance", 14 }, { "name", "n4" }, { "color", "red"}},
QJsonObject {{ "balance", 15 }, { "name", "n5" }, { "color", "green"}},
QJsonObject {{ "balance", 16 }, { "name", "n6" }, { "color", "blue"}},
QJsonObject {{ "balance", 17 }, { "name", "n7" }, { "color", "pink"}},
});
ListModelWrapper sourceModel4(engine, QJsonArray {});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1;
source1.setModel(sourceModel1);
SourceModel source2;
source2.setModel(sourceModel2);
SourceModel source3;
source3.setModel(sourceModel3);
SourceModel source4;
source4.setModel(sourceModel4);
sources.append(&sources, &source1);
sources.append(&sources, &source2);
sources.append(&sources, &source3);
sources.append(&sources, &source4);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
auto roles = model.roleNames();
QCOMPARE(model.rowCount(), 7);
QCOMPARE(roles.size(), 5);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "key")), 1);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "key")), 2);
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "key")), 3);
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "key")), {});
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "key")), {});
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "key")), {});
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "key")), {});
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "balance")), 11);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "balance")), 12);
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "balance")), 13);
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "balance")), 14);
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "balance")), 15);
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "balance")), 16);
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "balance")), 17);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "name")), "n1");
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "name")), "n2");
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "name")), "n3");
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "name")), "n4");
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "name")), "n5");
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "name")), "n6");
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "name")), "n7");
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "color")), {});
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "color")), {});
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "color")), {});
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "color")), "red");
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "color")), "green");
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "color")), "blue");
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "color")), "pink");
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "whichModel")), "");
// out of bounds
QCOMPARE(model.index(-1, 0).isValid(), false);
QCOMPARE(model.index(7, 0).isValid(), false);
auto roleKeys = roles.keys();
auto roleOutOfRange = *std::max_element(roleKeys.begin(),
roleKeys.end()) + 1;
QCOMPARE(model.data(model.index(0, 0), roleOutOfRange), {});
// getting source model and source model row
QCOMPARE(model.sourceModel(0), sourceModel1);
QCOMPARE(model.sourceModel(1), sourceModel1);
QCOMPARE(model.sourceModel(2), sourceModel1);
QCOMPARE(model.sourceModel(3), sourceModel3);
QCOMPARE(model.sourceModel(4), sourceModel3);
QCOMPARE(model.sourceModel(5), sourceModel3);
QCOMPARE(model.sourceModel(6), sourceModel3);
QCOMPARE(model.sourceModel(7), nullptr);
QCOMPARE(model.sourceModel(-1), nullptr);
QCOMPARE(model.sourceModelRow(0), 0);
QCOMPARE(model.sourceModelRow(1), 1);
QCOMPARE(model.sourceModelRow(2), 2);
QCOMPARE(model.sourceModelRow(3), 0);
QCOMPARE(model.sourceModelRow(4), 1);
QCOMPARE(model.sourceModelRow(5), 2);
QCOMPARE(model.sourceModelRow(6), 3);
QCOMPARE(model.sourceModelRow(7), -1);
QCOMPARE(model.sourceModelRow(-1), -1);
// getting row by source model source model row
QCOMPARE(model.fromSourceRow(nullptr, 0), -1);
QCOMPARE(model.fromSourceRow(sourceModel1, 0), 0);
QCOMPARE(model.fromSourceRow(sourceModel1, 1), 1);
QCOMPARE(model.fromSourceRow(sourceModel1, 2), 2);
QCOMPARE(model.fromSourceRow(sourceModel1, 3), -1);
QCOMPARE(model.fromSourceRow(sourceModel1, -1), -1);
QCOMPARE(model.fromSourceRow(sourceModel2, 0), -1);
QCOMPARE(model.fromSourceRow(sourceModel3, 0), 3);
QCOMPARE(model.fromSourceRow(sourceModel3, 1), 4);
QCOMPARE(model.fromSourceRow(sourceModel3, 2), 5);
QCOMPARE(model.fromSourceRow(sourceModel3, 3), 6);
QCOMPARE(model.fromSourceRow(sourceModel3, 4), -1);
}
void dataChangeTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine, QJsonArray {
QJsonObject {{ "key", 1}, { "balance", 11 }, { "name", "n1" }},
QJsonObject {{ "key", 2},{ "balance", 12 }, { "name", "n2" }},
QJsonObject {{ "key", 3},{ "balance", 13}, { "name", "n3" }},
});
ListModelWrapper sourceModel2(engine, QJsonArray {});
ListModelWrapper sourceModel3(engine, QJsonArray {
QJsonObject {{ "balance", 14 }, { "name", "n4" }, { "color", "red"}},
QJsonObject {{ "balance", 15 }, { "name", "n5" }, { "color", "green"}},
QJsonObject {{ "balance", 16 }, { "name", "n6" }, { "color", "blue"}},
QJsonObject {{ "balance", 17 }, { "name", "n7" }, { "color", "pink"}},
});
ListModelWrapper sourceModel4(engine, QJsonArray {});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1;
source1.setModel(sourceModel1);
SourceModel source2;
source2.setModel(sourceModel2);
SourceModel source3;
source3.setModel(sourceModel3);
SourceModel source4;
source4.setModel(sourceModel4);
sources.append(&sources, &source1);
sources.append(&sources, &source2);
sources.append(&sources, &source3);
sources.append(&sources, &source4);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
auto roles = model.roleNames();
// first non-empty source model modifications
{
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
sourceModel1.setProperty(0, "key", 21);
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.at(0).at(0), model.index(0, 0));
QCOMPARE(dataChangedSpy.at(0).at(1), model.index(0, 0));
QCOMPARE(dataChangedSpy.at(0).at(2).value<QVector<int>>(),
{ roleForName(roles, "key") });
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "key")), 21);
}
{
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
sourceModel1.setProperty(1, "balance", 22);
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.at(0).at(0), model.index(1, 0));
QCOMPARE(dataChangedSpy.at(0).at(1), model.index(1, 0));
QCOMPARE(dataChangedSpy.at(0).at(2).value<QVector<int>>(),
{ roleForName(roles, "balance") });
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "balance")), 22);
}
{
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
sourceModel1.setProperty(2, "name", "n13");
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.at(0).at(0), model.index(2, 0));
QCOMPARE(dataChangedSpy.at(0).at(1), model.index(2, 0));
QCOMPARE(dataChangedSpy.at(0).at(2).value<QVector<int>>(),
{ roleForName(roles, "name") });
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "name")), "n13");
}
// second non-empty source model modifications
{
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
sourceModel3.setProperty(0, "balance", 24);
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.at(0).at(0), model.index(3, 0));
QCOMPARE(dataChangedSpy.at(0).at(1), model.index(3, 0));
QCOMPARE(dataChangedSpy.at(0).at(2).value<QVector<int>>(),
{ roleForName(roles, "balance") });
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "balance")), 24);
}
{
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
sourceModel3.setProperty(1, "name", "n25");
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.at(0).at(0), model.index(4, 0));
QCOMPARE(dataChangedSpy.at(0).at(1), model.index(4, 0));
QCOMPARE(dataChangedSpy.at(0).at(2).value<QVector<int>>(),
{ roleForName(roles, "name") });
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "name")), "n25");
}
{
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
sourceModel3.setProperty(2, "color", "orange");
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.at(0).at(0), model.index(5, 0));
QCOMPARE(dataChangedSpy.at(0).at(1), model.index(5, 0));
QCOMPARE(dataChangedSpy.at(0).at(2).value<QVector<int>>(),
{ roleForName(roles, "color") });
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "color")), "orange");
}
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "key")), 21);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "key")), 2);
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "key")), 3);
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "key")), {});
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "key")), {});
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "key")), {});
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "key")), {});
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "balance")), 11);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "balance")), 22);
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "balance")), 13);
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "balance")), 24);
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "balance")), 15);
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "balance")), 16);
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "balance")), 17);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "name")), "n1");
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "name")), "n2");
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "name")), "n13");
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "name")), "n4");
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "name")), "n25");
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "name")), "n6");
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "name")), "n7");
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "color")), {});
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "color")), {});
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "color")), {});
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "color")), "red");
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "color")), "green");
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "color")), "orange");
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "color")), "pink");
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "whichModel")), "");
}
void dataChangeOnNotTrackedRoleTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine, QJsonArray {
QJsonObject {{ "key", 1}, { "balance", 11 }, { "name", "n1" }},
QJsonObject {{ "key", 2},{ "balance", 12 }, { "name", "n2" }},
QJsonObject {{ "key", 3},{ "balance", 13}, { "name", "n3" }},
});
ListModelWrapper sourceModel2(engine, QJsonArray {});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1, source2;
source1.setModel(sourceModel1);
source2.setModel(sourceModel2);
sources.append(&sources, &source1);
sources.append(&sources, &source2);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
QCOMPARE(model.rowCount(), 3);
sourceModel2.append(QJsonArray {
QJsonObject {{ "someRole", 1}}, QJsonObject {{ "someRole", 2}},
QJsonObject {{ "someRole", 3}}, QJsonObject {{ "someRole", 4}}
});
QCOMPARE(model.rowCount(), 7);
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
sourceModel2.setProperty(0, "someRole", 42);
QCOMPARE(dataChangedSpy.count(), 0);
}
void dataInsertionTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine, QJsonArray {
QJsonObject {{ "key", 1}, { "balance", 11 }, { "name", "n1" }},
QJsonObject {{ "key", 2},{ "balance", 12 }, { "name", "n2" }},
QJsonObject {{ "key", 3},{ "balance", 13}, { "name", "n3" }},
});
ListModelWrapper sourceModel2(engine, QJsonArray {});
ListModelWrapper sourceModel3(engine, QJsonArray {
QJsonObject {{ "balance", 14 }, { "name", "n4" }, { "color", "red"}},
QJsonObject {{ "balance", 15 }, { "name", "n5" }, { "color", "green"}},
QJsonObject {{ "balance", 16 }, { "name", "n6" }, { "color", "blue"}},
QJsonObject {{ "balance", 17 }, { "name", "n7" }, { "color", "pink"}},
});
ListModelWrapper sourceModel4(engine, QJsonArray {});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1;
source1.setModel(sourceModel1);
SourceModel source2;
source2.setModel(sourceModel2);
SourceModel source3;
source3.setModel(sourceModel3);
SourceModel source4;
source4.setModel(sourceModel4);
sources.append(&sources, &source1);
sources.append(&sources, &source2);
sources.append(&sources, &source3);
sources.append(&sources, &source4);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
auto roles = model.roleNames();
// inserting into first model
{
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
sourceModel1.insert(0, QJsonObject {
{ "key", 200}, { "balance", 300 }, { "name", "n200" }
});
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 0);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 0);
QCOMPARE(model.rowCount(), 8);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "key")), 200);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "balance")), 300);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "name")), "n200");
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "color")), {});
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "whichModel")), "");
}
{
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
sourceModel1.insert(2, QJsonObject {{ "key", 201}, { "balance", 301 }, { "name", "n201" }});
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 2);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 2);
QCOMPARE(model.rowCount(), 9);
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "key")), 201);
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "balance")), 301);
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "name")), "n201");
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "color")), {});
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "whichModel")), "");
}
// inserting into second, empty model
{
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
sourceModel2.insert(0, QJsonObject {{ "key", 202}, { "balance", 302 }, { "name", "n202" }});
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 5);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 5);
QCOMPARE(model.rowCount(), 10);
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "key")), 202);
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "balance")), 302);
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "name")), "n202");
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "color")), {});
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "whichModel")), "");
}
// inserting into third
{
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
sourceModel3.insert(2, QJsonObject {
{ "balance", 303 }, { "name", "n203" }, { "color", "brown" }
});
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 8);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 8);
QCOMPARE(model.rowCount(), 11);
QCOMPARE(model.data(model.index(8, 0), roleForName(roles, "key")), {});
QCOMPARE(model.data(model.index(8, 0), roleForName(roles, "balance")), 303);
QCOMPARE(model.data(model.index(8, 0), roleForName(roles, "name")), "n203");
QCOMPARE(model.data(model.index(8, 0), roleForName(roles, "color")), "brown");
QCOMPARE(model.data(model.index(8, 0), roleForName(roles, "whichModel")), "");
}
// inserting into forth, empty model
{
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
sourceModel4.insert(0, QJsonObject {
{ "key", 204 }, { "balance", 304 }, { "name", "n204" }, { "color", "black" }
});
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 11);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 11);
QCOMPARE(model.rowCount(), 12);
QCOMPARE(model.data(model.index(11, 0), roleForName(roles, "key")), 204);
QCOMPARE(model.data(model.index(11, 0), roleForName(roles, "balance")), 304);
QCOMPARE(model.data(model.index(11, 0), roleForName(roles, "name")), "n204");
QCOMPARE(model.data(model.index(11, 0), roleForName(roles, "color")), "black");
QCOMPARE(model.data(model.index(11, 0), roleForName(roles, "whichModel")), "");
}
// inserting multiple items (first model)
{
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
sourceModel1.append(QJsonArray {
QJsonObject {{ "key", 205}, { "balance", 305 }, { "name", "n205" }},
QJsonObject {{ "key", 206},{ "balance", 306 }, { "name", "n206" }}
});
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 5);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 6);
QCOMPARE(model.rowCount(), 14);
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "key")), 205);
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "balance")), 305);
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "name")), "n205");
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "color")), {});
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "key")), 206);
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "balance")), 306);
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "name")), "n206");
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "color")), {});
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "whichModel")), "");
}
}
void dataInsertionToEmptyModelTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine);
ListModelWrapper sourceModel2(engine);
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1;
source1.setModel(sourceModel1);
SourceModel source2;
source2.setModel(sourceModel2);
sources.append(&sources, &source1);
sources.append(&sources, &source2);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
QCOMPARE(model.roleNames(), {});
{
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
sourceModel1.append(QJsonArray {
QJsonObject {{ "key", 1}, { "balance", 11 }, { "name", "n1" }},
QJsonObject {{ "key", 2},{ "balance", 12 }, { "name", "n2" }}
});
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 0);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 1);
QCOMPARE(model.rowCount(), 2);
auto roles = model.roleNames();
QCOMPARE(roles.size(), 4);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "key")), 1);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "balance")), 11);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "name")), "n1");
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "key")), 2);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "balance")), 12);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "name")), "n2");
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "whichModel")), "");
}
{
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
sourceModel2.append(QJsonArray {
QJsonObject {{ "key", 3}, { "color", "red" }},
QJsonObject {{ "key", 4}, { "color", "blue" }}
});
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 2);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 3);
QCOMPARE(model.rowCount(), 4);
auto roles = model.roleNames();
QCOMPARE(roles.size(), 4);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "key")), 1);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "balance")), 11);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "name")), "n1");
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "key")), 2);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "balance")), 12);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "name")), "n2");
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "key")), 3);
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "balance")), {});
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "name")), {});
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "key")), 4);
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "balance")), {});
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "name")), {});
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "whichModel")), "");
}
}
void deferredNonEmptyModelSettingTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine, QJsonArray {
QJsonObject {{ "key", 1}, { "color", "red" }},
QJsonObject {{ "key", 2}, { "color", "blue" }}
});
ListModelWrapper sourceModel2(engine, QJsonArray {
QJsonObject {{ "key", 3}, { "color", "orange" }},
QJsonObject {{ "key", 4}, { "color", "green" }}
});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1, source2;
sources.append(&sources, &source1);
sources.append(&sources, &source2);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
{
QSignalSpy rowsAboutToBeInsertedSpy(
&model, &ConcatModel::rowsAboutToBeInserted);
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
// checking validity inside rowsAboutToBeInserted signal
{
QObject context;
connect(&model, &ConcatModel::rowsAboutToBeInserted, &context,
[&model] { QCOMPARE(model.rowCount(), 0); });
source1.setModel(sourceModel1);
}
QCOMPARE(rowsAboutToBeInsertedSpy.count(), 1);
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(1), 0);
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(2), 1);
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 0);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 1);
QCOMPARE(model.roleNames().size(), 3);
QCOMPARE(model.rowCount(), 2);
// check if connections are established correctly
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
sourceModel1.setProperty(1, "color", "white");
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.at(0).at(0), model.index(1, 0));
QCOMPARE(dataChangedSpy.at(0).at(1), model.index(1, 0));
}
{
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
{
QObject context;
connect(&model, &ConcatModel::rowsAboutToBeInserted, &context,
[&model] { QCOMPARE(model.rowCount(), 2); });
source2.setModel(sourceModel2);
}
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 2);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 3);
QCOMPARE(model.roleNames().size(), 3);
QCOMPARE(model.rowCount(), 4);
// check if connections are established correctly
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
sourceModel2.setProperty(1, "color", "black");
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.at(0).at(0), model.index(3, 0));
QCOMPARE(dataChangedSpy.at(0).at(1), model.index(3, 0));
}
}
void deferredEmptyModelSettingTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine);
ListModelWrapper sourceModel2(engine);
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1, source2;
sources.append(&sources, &source1);
sources.append(&sources, &source2);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
source1.setModel(sourceModel1);
source2.setModel(sourceModel2);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
{
QSignalSpy rowsAboutToBeInsertedSpy(
&model, &ConcatModel::rowsAboutToBeInserted);
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
// checking validity inside rowsAboutToBeInserted signal
{
QObject context;
connect(&model, &ConcatModel::rowsAboutToBeInserted, &context,
[&model] { QCOMPARE(model.rowCount(), 0); });
sourceModel2.append(QJsonArray {
QJsonObject {{ "key", 1}, { "color", "red" }},
QJsonObject {{ "key", 2}, { "color", "blue" }}
});
}
QCOMPARE(rowsAboutToBeInsertedSpy.count(), 1);
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(1), 0);
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(2), 1);
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 0);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 1);
QCOMPARE(model.roleNames().size(), 3);
QCOMPARE(model.rowCount(), 2);
// check if connections are established correctly
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
sourceModel2.setProperty(1, "color", "white");
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.at(0).at(0), model.index(1, 0));
QCOMPARE(dataChangedSpy.at(0).at(1), model.index(1, 0));
}
{
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
{
QObject context;
connect(&model, &ConcatModel::rowsAboutToBeInserted, &context,
[&model] { QCOMPARE(model.rowCount(), 2); });
sourceModel1.append(QJsonArray {
QJsonObject {{ "key", 3}, { "color", "green" }, { "value", 42}},
QJsonObject {{ "key", 4}, { "color", "white" }, { "value", 43}}
});
}
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 0);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 1);
QCOMPARE(model.roleNames().size(), 3);
QCOMPARE(model.rowCount(), 4);
// check if connections are established correctly
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
sourceModel2.setProperty(1, "color", "black");
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.at(0).at(0), model.index(3, 0));
QCOMPARE(dataChangedSpy.at(0).at(1), model.index(3, 0));
}
}
void settingModelsWithDifferentRolesTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine, QJsonArray {
QJsonObject {{ "color", "red" }, { "key", 1}},
QJsonObject {{ "color", "blue" }, { "key", 2}}
});
ListModelWrapper sourceModel2(engine, QJsonArray {
QJsonObject {{ "key", 3}},
QJsonObject {{ "key", 4}}
});
ListModelWrapper sourceModel3(engine, QJsonArray {
QJsonObject {{ "key", 1}, { "color", "red" }, { "value", 42}},
QJsonObject {{ "key", 2}, { "color", "blue" }, { "value", 42}}
});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1, source2, source3;
source1.setModel(sourceModel1);
sources.append(&sources, &source1);
sources.append(&sources, &source2);
sources.append(&sources, &source3);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
QCOMPARE(model.rowCount(), 2);
QCOMPARE(model.roleNames().count(), 3);
{
QSignalSpy rowsAboutToBeInsertedSpy(
&model, &ConcatModel::rowsAboutToBeInserted);
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
// checking validity inside rowsAboutToBeInserted signal
{
QObject context;
connect(&model, &ConcatModel::rowsAboutToBeInserted, &context,
[&model] { QCOMPARE(model.rowCount(), 2); });
source2.setModel(sourceModel2);
}
QCOMPARE(rowsAboutToBeInsertedSpy.count(), 1);
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(1), 2);
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(2), 3);
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 2);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 3);
QCOMPARE(model.roleNames().size(), 3);
QCOMPARE(model.rowCount(), 4);
// check if connections are established correctly
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
sourceModel1.setProperty(1, "color", "white");
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.at(0).at(0), model.index(1, 0));
QCOMPARE(dataChangedSpy.at(0).at(1), model.index(1, 0));
}
{
QSignalSpy rowsAboutToBeInsertedSpy(
&model, &ConcatModel::rowsAboutToBeInserted);
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
// checking validity inside rowsAboutToBeInserted signal
{
QObject context;
connect(&model, &ConcatModel::rowsAboutToBeInserted, &context,
[&model] { QCOMPARE(model.rowCount(), 4); });
source3.setModel(sourceModel3);
}
QCOMPARE(rowsAboutToBeInsertedSpy.count(), 1);
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(1), 4);
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(2), 5);
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 4);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 5);
QCOMPARE(model.roleNames().size(), 3);
QCOMPARE(model.rowCount(), 6);
// check if connections are established correctly
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
sourceModel3.setProperty(0, "color", "white");
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.at(0).at(0), model.index(4, 0));
QCOMPARE(dataChangedSpy.at(0).at(1), model.index(4, 0));
}
}
void unsettingModelsTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine, QJsonArray {
QJsonObject {{ "key", 1}, { "color", "red" }},
QJsonObject {{ "key", 2}, { "color", "blue" }}
});
ListModelWrapper sourceModel2(engine, QJsonArray {
QJsonObject {{ "key", 3}},
QJsonObject {{ "key", 4}}
});
ListModelWrapper sourceModel3(engine, QJsonArray {
QJsonObject {{ "key", 5}, { "color", "red" }, { "value", 42}},
QJsonObject {{ "key", 6}, { "color", "green" }, { "value", 43}}
});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1, source2, source3;
source1.setModel(sourceModel1);
source2.setModel(sourceModel2);
source3.setModel(sourceModel3);
sources.append(&sources, &source1);
sources.append(&sources, &source2);
sources.append(&sources, &source3);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
QCOMPARE(model.rowCount(), 6);
QCOMPARE(model.roleNames().count(), 4);
{
QSignalSpy rowsAboutToBeRemovedSpy(&model, &ConcatModel::rowsAboutToBeRemoved);
QSignalSpy rowsRemovedSpy(&model, &ConcatModel::rowsRemoved);
// checking validity inside rowsAboutToBeRemoved signal
{
QObject context;
connect(&model, &ConcatModel::rowsAboutToBeRemoved, &context,
[&model] { QCOMPARE(model.rowCount(), 6); });
source1.setModel(nullptr);
}
QCOMPARE(model.rowCount(), 4);
QCOMPARE(rowsAboutToBeRemovedSpy.count(), 1);
QCOMPARE(rowsRemovedSpy.count(), 1);
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(1), 0);
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(2), 1);
QCOMPARE(rowsRemovedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsRemovedSpy.at(0).at(1), 0);
QCOMPARE(rowsRemovedSpy.at(0).at(2), 1);
}
{
QSignalSpy rowsAboutToBeRemovedSpy(&model, &ConcatModel::rowsAboutToBeRemoved);
QSignalSpy rowsRemovedSpy(&model, &ConcatModel::rowsRemoved);
// checking validity inside rowsAboutToBeRemoved signal
{
QObject context;
connect(&model, &ConcatModel::rowsAboutToBeRemoved, &context,
[&model] { QCOMPARE(model.rowCount(), 4); });
source3.setModel(nullptr);
}
QCOMPARE(rowsAboutToBeRemovedSpy.count(), 1);
QCOMPARE(rowsRemovedSpy.count(), 1);
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(1), 2);
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(2), 3);
QCOMPARE(rowsRemovedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsRemovedSpy.at(0).at(1), 2);
QCOMPARE(rowsRemovedSpy.at(0).at(2), 3);
}
{
QSignalSpy rowsAboutToBeRemovedSpy(&model, &ConcatModel::rowsAboutToBeRemoved);
QSignalSpy rowsRemovedSpy(&model, &ConcatModel::rowsRemoved);
// checking validity inside rowsAboutToBeRemoved signal
{
QObject context;
connect(&model, &ConcatModel::rowsAboutToBeRemoved, &context,
[&model] { QCOMPARE(model.rowCount(), 2); });
source2.setModel(nullptr);
}
QCOMPARE(rowsAboutToBeRemovedSpy.count(), 1);
QCOMPARE(rowsRemovedSpy.count(), 1);
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(1), 0);
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(2), 1);
QCOMPARE(rowsRemovedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsRemovedSpy.at(0).at(1), 0);
QCOMPARE(rowsRemovedSpy.at(0).at(2), 1);
}
{
QSignalSpy rowsAboutToBeRemovedSpy(&model, &ConcatModel::rowsAboutToBeRemoved);
QSignalSpy rowsRemovedSpy(&model, &ConcatModel::rowsRemoved);
source2.setModel(nullptr);
QCOMPARE(rowsAboutToBeRemovedSpy.count(), 0);
QCOMPARE(rowsRemovedSpy.count(), 0);
}
// check if signals are disconnected properly
{
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
sourceModel1.setProperty(0, "key", 11);
sourceModel2.setProperty(0, "key", 12);
sourceModel3.setProperty(0, "key", 13);
QCOMPARE(dataChangedSpy.count(), 0);
}
}
void replacingModelsTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine, QJsonArray {
QJsonObject {{ "key", 1}, { "color", "red" }},
QJsonObject {{ "key", 2}, { "color", "blue" }}
});
ListModelWrapper sourceModel2(engine, QJsonArray {
QJsonObject {{ "key", 3}},
QJsonObject {{ "key", 4}}
});
ListModelWrapper sourceModel3(engine, QJsonArray {
QJsonObject {{ "key", 5}, { "color", "red" }, { "value", 42}},
QJsonObject {{ "key", 6}, { "color", "green" }, { "value", 43}}
});
ListModelWrapper sourceModel4(engine, QJsonArray {
QJsonObject {{ "color", "orange" }, { "value", 44}, { "key", 7}},
QJsonObject {{ "color", "green" }, { "value", 45}, { "key", 8}},
QJsonObject {{ "color", "brown" }, { "value", 46}, { "key", 9}}
});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1, source2, source3;
source1.setModel(sourceModel1);
source2.setModel(sourceModel2);
source3.setModel(sourceModel3);
sources.append(&sources, &source1);
sources.append(&sources, &source2);
sources.append(&sources, &source3);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
QCOMPARE(model.rowCount(), 6);
QCOMPARE(model.roleNames().count(), 4);
QSignalSpy rowsAboutToBeRemovedSpy(&model, &ConcatModel::rowsAboutToBeRemoved);
QSignalSpy rowsRemovedSpy(&model, &ConcatModel::rowsRemoved);
QSignalSpy rowsAboutToBeInsertedSpy(
&model, &ConcatModel::rowsAboutToBeInserted);
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
// checking validity inside rowsAboutToBeRemoved signal
{
QObject context;
connect(&model, &ConcatModel::rowsAboutToBeRemoved, &context,
[&model] { QCOMPARE(model.rowCount(), 6); });
connect(&model, &ConcatModel::rowsRemoved, &context,
[&model] { QCOMPARE(model.rowCount(), 4); });
connect(&model, &ConcatModel::rowsAboutToBeInserted, &context,
[&model] { QCOMPARE(model.rowCount(), 4); });
source2.setModel(sourceModel4);
}
QCOMPARE(model.rowCount(), 7);
QCOMPARE(rowsAboutToBeRemovedSpy.count(), 1);
QCOMPARE(rowsRemovedSpy.count(), 1);
QCOMPARE(rowsAboutToBeInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(1), 2);
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(2), 3);
QCOMPARE(rowsRemovedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsRemovedSpy.at(0).at(1), 2);
QCOMPARE(rowsRemovedSpy.at(0).at(2), 3);
QCOMPARE(rowsAboutToBeInsertedSpy.count(), 1);
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(1), 2);
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(2), 4);
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 2);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 4);
// check if previous model is disconnected
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
sourceModel2.setProperty(0, "key", 234);
QCOMPARE(dataChangedSpy.count(), 0);
// content validation
auto roles = model.roleNames();
QCOMPARE(roles.count(), 4);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "key")), 1);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "key")), 2);
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "key")), 7);
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "key")), 8);
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "key")), 9);
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "key")), 5);
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "key")), 6);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "color")), "red");
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "color")), "blue");
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "color")), "orange");
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "color")), "green");
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "color")), "brown");
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "color")), "red");
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "color")), "green");
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "value")), {});
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "value")), {});
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "value")), 44);
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "value")), 45);
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "value")), 46);
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "value")), 42);
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "value")), 43);
}
void deletingModelsTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine, QJsonArray {
QJsonObject {{ "key", 1}, { "color", "red" }},
QJsonObject {{ "key", 2}, { "color", "blue" }}
});
auto sourceModel2 = std::make_unique<ListModelWrapper>(engine, QJsonArray {
QJsonObject {{ "key", 3}},
QJsonObject {{ "key", 4}}
});
ListModelWrapper sourceModel3(engine, QJsonArray {
QJsonObject {{ "key", 5}, { "color", "red" }, { "value", 42}},
QJsonObject {{ "key", 6}, { "color", "green" }, { "value", 43}}
});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1, source2, source3;
source1.setModel(sourceModel1);
source2.setModel(*sourceModel2);
source3.setModel(sourceModel3);
sources.append(&sources, &source1);
sources.append(&sources, &source2);
sources.append(&sources, &source3);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
auto roles = model.roleNames();
QCOMPARE(model.rowCount(), 6);
QCOMPARE(roles.count(), 4);
QSignalSpy rowsAboutToBeRemovedSpy(&model, &ConcatModel::rowsAboutToBeRemoved);
QSignalSpy rowsRemovedSpy(&model, &ConcatModel::rowsRemoved);
sourceModel2.reset();
QCOMPARE(model.rowCount(), 6);
QCOMPARE(rowsAboutToBeRemovedSpy.count(), 0);
QCOMPARE(rowsRemovedSpy.count(), 0);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "key")), 1);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "key")), 2);
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "key")), {});
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "key")), {});
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "key")), 5);
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "key")), 6);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "whichModel")), "");
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "whichModel")), "");
}
void removalTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine, QJsonArray {
QJsonObject {{ "key", 1}, { "color", "red" }},
QJsonObject {{ "key", 2}, { "color", "blue" }}
});
ListModelWrapper sourceModel2(engine, QJsonArray {
QJsonObject {{ "key", 3}},
QJsonObject {{ "key", 4}},
QJsonObject {{ "key", 5}}
});
ListModelWrapper sourceModel3(engine, QJsonArray {
QJsonObject {{ "key", 6}, { "color", "red" }, { "value", 42}},
QJsonObject {{ "key", 7}, { "color", "green" }, { "value", 43}}
});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1, source2, source3;
source1.setModel(sourceModel1);
source2.setModel(sourceModel2);
source3.setModel(sourceModel3);
sources.append(&sources, &source1);
sources.append(&sources, &source2);
sources.append(&sources, &source3);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
auto roles = model.roleNames();
QCOMPARE(model.rowCount(), 7);
QCOMPARE(roles.count(), 4);
QSignalSpy rowsAboutToBeRemovedSpy(&model, &ConcatModel::rowsAboutToBeRemoved);
QSignalSpy rowsRemovedSpy(&model, &ConcatModel::rowsRemoved);
// checking validity inside rowsAboutToBeRemoved signal
{
QObject context;
connect(&model, &ConcatModel::rowsAboutToBeRemoved, &context,
[this, &model, &roles] {
QCOMPARE(model.rowCount(), 7);
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "key")), 4);
});
sourceModel2.remove(1, 2);
}
QCOMPARE(model.rowCount(), 5);
QCOMPARE(rowsAboutToBeRemovedSpy.count(), 1);
QCOMPARE(rowsRemovedSpy.count(), 1);
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(1), 3);
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(2), 4);
QCOMPARE(rowsRemovedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsRemovedSpy.at(0).at(1), 3);
QCOMPARE(rowsRemovedSpy.at(0).at(2), 4);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "key")), 1);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "key")), 2);
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "key")), 3);
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "key")), 6);
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "key")), 7);
}
void moveTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine, QJsonArray {
QJsonObject {{ "key", 1}, { "color", "red" }},
QJsonObject {{ "key", 2}, { "color", "blue" }}
});
ListModelWrapper sourceModel2(engine, QJsonArray {
QJsonObject {{ "key", 3}},
QJsonObject {{ "key", 4}},
QJsonObject {{ "key", 5}}
});
ListModelWrapper sourceModel3(engine, QJsonArray {
QJsonObject {{ "key", 6}, { "color", "red" }, { "value", 42}},
QJsonObject {{ "key", 7}, { "color", "green" }, { "value", 43}}
});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1, source2, source3;
source1.setModel(sourceModel1);
source2.setModel(sourceModel2);
source3.setModel(sourceModel3);
sources.append(&sources, &source1);
sources.append(&sources, &source2);
sources.append(&sources, &source3);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
auto roles = model.roleNames();
QCOMPARE(model.rowCount(), 7);
QCOMPARE(roles.count(), 4);
QSignalSpy rowsAboutToBeMovedSpy(&model, &ConcatModel::rowsAboutToBeMoved);
QSignalSpy rowsMovedSpy(&model, &ConcatModel::rowsMoved);
// checking validity inside rowsAboutToBeRemoved signal
{
QObject context;
connect(&model, &ConcatModel::rowsAboutToBeMoved, &context,
[this, &model, &roles] {
QCOMPARE(model.rowCount(), 7);
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "key")), 4);
});
sourceModel2.move(1, 0, 2);
}
QCOMPARE(model.rowCount(), 7);
QCOMPARE(rowsAboutToBeMovedSpy.count(), 1);
QCOMPARE(rowsMovedSpy.count(), 1);
QCOMPARE(rowsAboutToBeMovedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeMovedSpy.at(0).at(1), 3);
QCOMPARE(rowsAboutToBeMovedSpy.at(0).at(2), 4);
QCOMPARE(rowsAboutToBeMovedSpy.at(0).at(3), QModelIndex{});
QCOMPARE(rowsAboutToBeMovedSpy.at(0).at(4), 2);
QCOMPARE(rowsMovedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsMovedSpy.at(0).at(1), 3);
QCOMPARE(rowsMovedSpy.at(0).at(2), 4);
QCOMPARE(rowsMovedSpy.at(0).at(3), QModelIndex{});
QCOMPARE(rowsMovedSpy.at(0).at(4), 2);
}
void layoutChangedTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine, QJsonArray {
QJsonObject {{ "key", 1}, { "color", "red" }},
QJsonObject {{ "key", 2}, { "color", "blue" }}
});
ListModelWrapper sourceModel2(engine, QJsonArray {
QJsonObject {{ "key", 3}},
QJsonObject {{ "key", 4}},
QJsonObject {{ "key", 5}}
});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1, source2, source3;
source1.setModel(sourceModel1);
source2.setModel(sourceModel2);
sources.append(&sources, &source1);
sources.append(&sources, &source2);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
auto roles = model.roleNames();
QCOMPARE(model.rowCount(), 5);
QCOMPARE(roles.count(), 3);
// register types to avoid warnings regarding signal params
qRegisterMetaType<QList<QPersistentModelIndex>>();
qRegisterMetaType<QAbstractItemModel::LayoutChangeHint>();
QSignalSpy layoutAboutToBeChangedSpy(&model, &ConcatModel::layoutAboutToBeChanged);
QSignalSpy layoutChangedSpy(&model, &ConcatModel::layoutChanged);
emit sourceModel1.model()->layoutAboutToBeChanged();
QCOMPARE(layoutAboutToBeChangedSpy.count(), 1);
QCOMPARE(layoutChangedSpy.count(), 0);
emit sourceModel1.model()->layoutChanged();
QCOMPARE(layoutAboutToBeChangedSpy.count(), 1);
QCOMPARE(layoutChangedSpy.count(), 1);
}
void modelResetWhenEmptyTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine);
ListModelWrapper sourceModel2(engine);
ListModelWrapper sourceModel3(engine);
ListModelWrapper sourceModel4(engine);
ListModelWrapper sourceModel5(engine, QJsonArray {
QJsonObject {{ "key", 1}, { "color", "red" }},
QJsonObject {{ "key", 2}, { "color", "blue" }}
});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1, source2, source3;
IdentityModel proxy1, proxy2, proxy3;
proxy1.setSourceModel(sourceModel1);
proxy2.setSourceModel(sourceModel2);
proxy3.setSourceModel(sourceModel3);
source1.setModel(&proxy1);
source2.setModel(&proxy2);
source3.setModel(&proxy3);
sources.append(&sources, &source1);
sources.append(&sources, &source2);
sources.append(&sources, &source3);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
{
QSignalSpy modelAboutToBeResetSpy(&model, &ConcatModel::modelAboutToBeReset);
QSignalSpy modelResetSpy(&model, &ConcatModel::modelReset);
QSignalSpy rowsAboutToBeInsertedSpy(&model, &ConcatModel::rowsAboutToBeInserted);
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
proxy2.setSourceModel(sourceModel4);
QCOMPARE(modelAboutToBeResetSpy.count(), 0);
QCOMPARE(modelResetSpy.count(), 0);
QCOMPARE(rowsAboutToBeInsertedSpy.count(), 0);
QCOMPARE(rowsInsertedSpy.count(), 0);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
}
{
QSignalSpy modelAboutToBeResetSpy(&model, &ConcatModel::modelAboutToBeReset);
QSignalSpy modelResetSpy(&model, &ConcatModel::modelReset);
QSignalSpy rowsAboutToBeInsertedSpy(&model, &ConcatModel::rowsAboutToBeInserted);
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
// checking validity inside rowsAboutToBeInserted signal
{
QObject context;
connect(&model, &ConcatModel::rowsAboutToBeInserted, &context,
[&model] { QCOMPARE(model.rowCount(), 0); });
proxy2.setSourceModel(sourceModel5);
}
QCOMPARE(modelAboutToBeResetSpy.count(), 0);
QCOMPARE(modelResetSpy.count(), 0);
QCOMPARE(rowsAboutToBeInsertedSpy.count(), 1);
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(1), 0);
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(2), 1);
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 0);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 1);
auto roles = model.roleNames();
QCOMPARE(model.rowCount(), 2);
QCOMPARE(roles.count(), 3);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "key")), 1);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "key")), 2);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "color")), "red");
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "color")), "blue");
}
}
void modelResetWhenNotEmptyTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine, QJsonArray {
QJsonObject {{ "key", 1}, { "color", "red" }},
QJsonObject {{ "key", 2}, { "color", "blue" }}
});
ListModelWrapper sourceModel2(engine, QJsonArray {
QJsonObject {{ "key", 3}},
QJsonObject {{ "key", 4}},
QJsonObject {{ "key", 5}}
});
ListModelWrapper sourceModel3(engine);
ListModelWrapper sourceModel4(engine);
ListModelWrapper sourceModel5(engine, QJsonArray {
QJsonObject {{ "color", "red" }, { "name", "a" }, { "key", 11}},
QJsonObject {{ "color", "blue" }, { "name", "b" }, { "key", 12}},
QJsonObject {{ "color", "pink" }, { "name", "c" }, { "key", 13}}
});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1, source2, source3;
IdentityModel proxy1, proxy2, proxy3;
proxy1.setSourceModel(sourceModel1);
proxy2.setSourceModel(sourceModel2);
proxy3.setSourceModel(sourceModel3);
source1.setModel(&proxy1);
source2.setModel(&proxy2);
source3.setModel(&proxy3);
sources.append(&sources, &source1);
sources.append(&sources, &source2);
sources.append(&sources, &source3);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
auto roles = model.roleNames();
QCOMPARE(model.rowCount(), 5);
QCOMPARE(roles.count(), 3);
// reset to empty model
{
QSignalSpy modelAboutToBeResetSpy(&model, &ConcatModel::modelAboutToBeReset);
QSignalSpy modelResetSpy(&model, &ConcatModel::modelReset);
QSignalSpy rowsAboutToBeInsertedSpy(&model, &ConcatModel::rowsAboutToBeInserted);
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
QSignalSpy rowsAboutToBeRemovedSpy(&model, &ConcatModel::rowsAboutToBeRemoved);
QSignalSpy rowsRemovedSpy(&model, &ConcatModel::rowsRemoved);
// checking validity inside rowsAboutToBeRemoved signal
{
QObject context;
connect(&model, &ConcatModel::rowsAboutToBeRemoved, &context,
[this, &model, &roles] {
QCOMPARE(model.rowCount(), 5);
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "key")), 4);
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "color")), {});
});
proxy2.setSourceModel(sourceModel4);
}
QCOMPARE(modelAboutToBeResetSpy.count(), 0);
QCOMPARE(modelResetSpy.count(), 0);
QCOMPARE(rowsAboutToBeInsertedSpy.count(), 0);
QCOMPARE(rowsInsertedSpy.count(), 0);
QCOMPARE(rowsAboutToBeRemovedSpy.count(), 1);
QCOMPARE(rowsRemovedSpy.count(), 1);
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(1), 2);
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(2), 4);
QCOMPARE(model.rowCount(), 2);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "key")), 1);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "key")), 2);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "color")), "red");
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "color")), "blue");
// insert some data to check if roles are re-initialized properly
sourceModel4.append(QJsonArray {
QJsonObject {{ "color", "purple"}, { "key", 3} },
QJsonObject {{ "color", "green" }, { "key", 4}}
});
QCOMPARE(model.rowCount(), 4);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "key")), 1);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "key")), 2);
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "key")), 3);
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "key")), 4);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "color")), "red");
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "color")), "blue");
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "color")), "purple");
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "color")), "green");
sourceModel4.clear();
}
// reset to not empty model
{
QSignalSpy modelAboutToBeResetSpy(&model, &ConcatModel::modelAboutToBeReset);
QSignalSpy modelResetSpy(&model, &ConcatModel::modelReset);
QSignalSpy rowsAboutToBeRemovedSpy(&model, &ConcatModel::rowsAboutToBeRemoved);
QSignalSpy rowsRemovedSpy(&model, &ConcatModel::rowsRemoved);
QSignalSpy rowsAboutToBeInsertedSpy(&model, &ConcatModel::rowsAboutToBeInserted);
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
// checking validity inside rowsAboutToBeRemoved, rowsRemoved and
// rowsAboutToBeInserted signals
{
QObject context;
connect(&model, &ConcatModel::rowsAboutToBeRemoved, &context,
[this, &model, &roles] {
QCOMPARE(model.rowCount(), 2);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "key")), 1);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "key")), 2);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "color")), "red");
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "color")), "blue");
});
connect(&model, &ConcatModel::rowsRemoved, &context,
[this, &model, &roles] {
QCOMPARE(model.rowCount(), 0);
});
connect(&model, &ConcatModel::rowsAboutToBeInserted, &context,
[this, &model, &roles] {
QCOMPARE(model.rowCount(), 0);
});
proxy1.setSourceModel(sourceModel5);
}
QCOMPARE(modelAboutToBeResetSpy.count(), 0);
QCOMPARE(modelResetSpy.count(), 0);
QCOMPARE(rowsAboutToBeRemovedSpy.count(), 1);
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(1), 0);
QCOMPARE(rowsAboutToBeRemovedSpy.at(0).at(2), 1);
QCOMPARE(rowsRemovedSpy.count(), 1);
QCOMPARE(rowsAboutToBeInsertedSpy.count(), 1);
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(1), 0);
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(2), 2);
QCOMPARE(rowsInsertedSpy.count(), 1);
QCOMPARE(model.rowCount(), 3);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "key")), 11);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "key")), 12);
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "key")), 13);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "color")), "red");
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "color")), "blue");
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "color")), "pink");
}
}
void sameModelUsedMultipleTimesTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel(engine, QJsonArray {
QJsonObject {{ "key", 1}, { "color", "red" }},
QJsonObject {{ "key", 2}, { "color", "blue" }}
});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1, source2, source3;
source1.setModel(sourceModel);
source2.setModel(sourceModel);
source3.setModel(sourceModel);
sources.append(&sources, &source1);
sources.append(&sources, &source2);
sources.append(&sources, &source3);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
auto roles = model.roleNames();
QCOMPARE(model.rowCount(), 6);
QCOMPARE(roles.count(), 3);
QSignalSpy rowsAboutToBeInsertedSpy(
&model, &ConcatModel::rowsAboutToBeInserted);
QSignalSpy rowsInsertedSpy(&model, &ConcatModel::rowsInserted);
sourceModel.insert(1, QJsonObject {{ "key", 15 }, { "color", "black" }});
QCOMPARE(model.rowCount(), 9);
QCOMPARE(rowsAboutToBeInsertedSpy.count(), 3);
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(1), 1);
QCOMPARE(rowsAboutToBeInsertedSpy.at(0).at(2), 1);
QCOMPARE(rowsAboutToBeInsertedSpy.at(1).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeInsertedSpy.at(1).at(1), 3);
QCOMPARE(rowsAboutToBeInsertedSpy.at(1).at(2), 3);
QCOMPARE(rowsAboutToBeInsertedSpy.at(2).at(0), QModelIndex{});
QCOMPARE(rowsAboutToBeInsertedSpy.at(2).at(1), 5);
QCOMPARE(rowsAboutToBeInsertedSpy.at(2).at(2), 5);
QCOMPARE(rowsInsertedSpy.count(), 3);
QCOMPARE(rowsInsertedSpy.at(0).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(0).at(1), 5);
QCOMPARE(rowsInsertedSpy.at(0).at(2), 5);
QCOMPARE(rowsInsertedSpy.at(1).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(1).at(1), 3);
QCOMPARE(rowsInsertedSpy.at(1).at(2), 3);
QCOMPARE(rowsInsertedSpy.at(2).at(0), QModelIndex{});
QCOMPARE(rowsInsertedSpy.at(2).at(1), 1);
QCOMPARE(rowsInsertedSpy.at(2).at(2), 1);
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
sourceModel.setProperty(0, "key", 0);
QCOMPARE(model.rowCount(), 9);
QCOMPARE(dataChangedSpy.count(), 3);
QCOMPARE(dataChangedSpy.at(0).at(0), model.index(0, 0));
QCOMPARE(dataChangedSpy.at(0).at(1), model.index(0, 0));
QCOMPARE(dataChangedSpy.at(0).at(2).value<QVector<int>>(),
{ roleForName(roles, "key") });
QCOMPARE(dataChangedSpy.at(1).at(0), model.index(3, 0));
QCOMPARE(dataChangedSpy.at(1).at(1), model.index(3, 0));
QCOMPARE(dataChangedSpy.at(1).at(2).value<QVector<int>>(),
{ roleForName(roles, "key") });
QCOMPARE(dataChangedSpy.at(2).at(0), model.index(6, 0));
QCOMPARE(dataChangedSpy.at(2).at(1), model.index(6, 0));
QCOMPARE(dataChangedSpy.at(2).at(2).value<QVector<int>>(),
{ roleForName(roles, "key") });
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "key")), 0);
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "key")), 15);
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "key")), 2);
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "key")), 0);
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "key")), 15);
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "key")), 2);
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "key")), 0);
QCOMPARE(model.data(model.index(7, 0), roleForName(roles, "key")), 15);
QCOMPARE(model.data(model.index(8, 0), roleForName(roles, "key")), 2);
}
void expectedRolesTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine, QJsonArray {
QJsonObject {{ "key", 1}},
QJsonObject {{ "key", 2}},
QJsonObject {{ "key", 3}}
});
ListModelWrapper sourceModel2(engine, QJsonArray {
QJsonObject {{ "key", 4}, { "color", "red" }, { "name", "name 1"}},
QJsonObject {{ "key", 5}, { "color", "blue" }, { "name", "name 2"}}
});
ListModelWrapper sourceModel3(engine, QJsonArray {
QJsonObject {{ "key", 6}, { "color", "red" }, { "value", 42}},
QJsonObject {{ "key", 7}, { "color", "green" }, { "value", 43}}
});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1, source2, source3;
source1.setModel(sourceModel1);
sources.append(&sources, &source1);
sources.append(&sources, &source2);
sources.append(&sources, &source3);
model.setExpectedRoles({"key", "color", "value"});
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.componentComplete();
auto roles = model.roleNames();
QCOMPARE(model.rowCount(), 3);
QCOMPARE(roles.size(), 4);
std::set<QByteArray> roleNamesSet(roles.cbegin(), roles.cend());
std::set<QByteArray> expectedRoleNamesSet({"key", "color", "value", "whichModel"});
QCOMPARE(roleNamesSet, expectedRoleNamesSet);
source2.setModel(sourceModel2);
source3.setModel(sourceModel3);
QCOMPARE(model.rowCount(), 7);
}
void markerRoleTest()
{
QQmlEngine engine;
ConcatModel model;
ListModelWrapper sourceModel1(engine, QJsonArray {
QJsonObject {{ "key", 1}, { "balance", 11 }, { "name", "n1" }},
QJsonObject {{ "key", 2},{ "balance", 12 }, { "name", "n2" }},
QJsonObject {{ "key", 3},{ "balance", 13}, { "name", "n3" }},
});
ListModelWrapper sourceModel2(engine, QJsonArray {});
ListModelWrapper sourceModel3(engine, QJsonArray {
QJsonObject {{ "balance", 14 }, { "name", "n4" }, { "color", "red"}},
QJsonObject {{ "balance", 15 }, { "name", "n5" }, { "color", "green"}},
QJsonObject {{ "balance", 16 }, { "name", "n6" }, { "color", "blue"}},
QJsonObject {{ "balance", 17 }, { "name", "n7" }, { "color", "pink"}},
});
ListModelWrapper sourceModel4(engine, QJsonArray {});
QQmlListProperty<SourceModel> sources = model.sources();
SourceModel source1, source2, source3, source4;
source1.setModel(sourceModel1);
source2.setModel(sourceModel2);
source3.setModel(sourceModel3);
source4.setModel(sourceModel4);
source1.setMarkerRoleValue("model 1");
source2.setMarkerRoleValue("model 2");
source3.setMarkerRoleValue("model 3");
source4.setMarkerRoleValue("model 4");
sources.append(&sources, &source1);
sources.append(&sources, &source2);
sources.append(&sources, &source3);
sources.append(&sources, &source4);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QCOMPARE(model.index(0, 0).isValid(), false);
model.setMarkerRoleName("marker");
model.componentComplete();
QCOMPARE(model.markerRoleName(), "marker");
QCOMPARE(source1.markerRoleValue(), "model 1");
QCOMPARE(source2.markerRoleValue(), "model 2");
QCOMPARE(source3.markerRoleValue(), "model 3");
QCOMPARE(source4.markerRoleValue(), "model 4");
QTest::ignoreMessage(QtWarningMsg,
"Property \"markerRoleName\" is intended to be "
"initialized once before roles initialization "
"and not modified later.");
model.setMarkerRoleName("marker2");
QCOMPARE(model.markerRoleName(), "marker");
auto roles = model.roleNames();
QCOMPARE(model.rowCount(), 7);
QCOMPARE(roles.size(), 5);
QVERIFY(roleForName(roles, "marker") != -1);
QCOMPARE(model.data(model.index(0, 0), roleForName(roles, "marker")), "model 1");
QCOMPARE(model.data(model.index(1, 0), roleForName(roles, "marker")), "model 1");
QCOMPARE(model.data(model.index(2, 0), roleForName(roles, "marker")), "model 1");
QCOMPARE(model.data(model.index(3, 0), roleForName(roles, "marker")), "model 3");
QCOMPARE(model.data(model.index(4, 0), roleForName(roles, "marker")), "model 3");
QCOMPARE(model.data(model.index(5, 0), roleForName(roles, "marker")), "model 3");
QCOMPARE(model.data(model.index(6, 0), roleForName(roles, "marker")), "model 3");
{
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
source1.setMarkerRoleValue("model 1_");
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.at(0).at(0), model.index(0, 0));
QCOMPARE(dataChangedSpy.at(0).at(1), model.index(2, 0));
QCOMPARE(dataChangedSpy.at(0).at(2).value<QVector<int>>(),
{ roleForName(roles, "marker") });
}
{
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
source2.setMarkerRoleValue("model 2_");
QCOMPARE(dataChangedSpy.count(), 0);
}
{
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
source3.setMarkerRoleValue("model 3_");
QCOMPARE(dataChangedSpy.count(), 1);
QCOMPARE(dataChangedSpy.at(0).at(0), model.index(3, 0));
QCOMPARE(dataChangedSpy.at(0).at(1), model.index(6, 0));
QCOMPARE(dataChangedSpy.at(0).at(2).value<QVector<int>>(),
{ roleForName(roles, "marker") });
}
{
QSignalSpy dataChangedSpy(&model, &ConcatModel::dataChanged);
source4.setMarkerRoleValue("model 4_");
QCOMPARE(dataChangedSpy.count(), 0);
}
}
};
QTEST_MAIN(TestConcatModel)
#include "tst_ConcatModel.moc"