chore(LeftJoinModel): add detection of duplicated roles

This commit is contained in:
Michał Cieślak 2023-10-28 18:42:36 +02:00 committed by Michał
parent cc964508f1
commit 7847cb3b11
2 changed files with 176 additions and 18 deletions

View File

@ -22,41 +22,56 @@ void LeftJoinModel::initialize()
auto leftRoleNames = m_leftModel->roleNames(); auto leftRoleNames = m_leftModel->roleNames();
auto rightRoleNames = m_rightModel->roleNames(); auto rightRoleNames = m_rightModel->roleNames();
if (leftRoleNames.isEmpty() || rightRoleNames.isEmpty()) { auto leftNames = leftRoleNames.values();
qWarning() << "Both left and right models have to contain some roles!"; auto rightNames = rightRoleNames.values();
QSet<QByteArray> leftNamesSet(leftNames.cbegin(), leftNames.cend());
QSet<QByteArray> rightNamesSet(rightNames.cbegin(), rightNames.cend());
if (leftNames.size() != leftNamesSet.size()
|| rightNames.size() != rightNamesSet.size()) {
qWarning() << "Each of the source models must have unique role names!";
return; return;
} }
auto leftModelJoinRoleList = leftRoleNames.keys(m_joinRole.toUtf8()); auto namesIntersection = leftNamesSet.intersect(rightNamesSet);
auto rightModelJoinRoleList = rightRoleNames.keys(m_joinRole.toUtf8()); auto hasCommonJoinRole = namesIntersection.remove(m_joinRole.toUtf8());
if (leftModelJoinRoleList.size() != 1 if (!hasCommonJoinRole) {
|| rightModelJoinRoleList.size() != 1) {
qWarning().noquote() << QString("Both left and right models have to " qWarning().noquote() << QString("Both left and right models have to "
"contain join role %1!").arg(m_joinRole); "contain join role %1!").arg(m_joinRole);
return; return;
} }
m_leftModelJoinRole = leftModelJoinRoleList.at(0); if (!namesIntersection.isEmpty()) {
m_rightModelJoinRole = rightModelJoinRoleList.at(0); qWarning().nospace() << "Source models contain conflicting model names: "
<< QList(namesIntersection.cbegin(),
namesIntersection.cend())
<< "!";
return;
}
auto leftRoles = leftRoleNames.keys(); auto leftRoles = leftRoleNames.keys();
auto maxLeftRole = std::max_element(leftRoles.cbegin(), leftRoles.cend()); auto maxLeftRole = std::max_element(leftRoles.cbegin(), leftRoles.cend());
auto rightRolesOffset = *maxLeftRole + 1; auto rightRolesOffset = *maxLeftRole + 1;
auto roleNames = leftRoleNames; auto roleNames = leftRoleNames;
QVector<int> joinedRoles;
auto i = rightRoleNames.constBegin(); auto i = rightRoleNames.constBegin();
while (i != rightRoleNames.constEnd()) { while (i != rightRoleNames.constEnd()) {
if (i.value() != m_joinRole) { if (i.value() != m_joinRole) {
auto roleWithOffset = i.key() + rightRolesOffset; auto roleWithOffset = i.key() + rightRolesOffset;
roleNames.insert(roleWithOffset, i.value()); roleNames.insert(roleWithOffset, i.value());
m_joinedRoles.append(roleWithOffset); joinedRoles.append(roleWithOffset);
} }
++i; ++i;
} }
m_roleNames = std::move(roleNames);
m_joinedRoles = std::move(joinedRoles);
m_leftModelJoinRole = leftRoleNames.key(m_joinRole.toUtf8());
m_rightModelJoinRole = rightRoleNames.key(m_joinRole.toUtf8());
m_rightModelRolesOffset = rightRolesOffset; m_rightModelRolesOffset = rightRolesOffset;
m_roleNames = roleNames;
connect(m_rightModel, &QAbstractItemModel::dataChanged, this, connect(m_rightModel, &QAbstractItemModel::dataChanged, this,
[this](auto& topLeft, auto& bottomRight, auto& roles) { [this](auto& topLeft, auto& bottomRight, auto& roles) {

View File

@ -11,7 +11,6 @@ namespace {
class TestSourceModel : public QAbstractListModel { class TestSourceModel : public QAbstractListModel {
public: public:
explicit TestSourceModel(QList<QPair<QString, QVariantList>> data) explicit TestSourceModel(QList<QPair<QString, QVariantList>> data)
: m_data(std::move(data)) : m_data(std::move(data))
{ {
@ -86,7 +85,7 @@ private:
QHash<int, QByteArray> m_roles; QHash<int, QByteArray> m_roles;
}; };
} } // anonymous namespace
class TestLeftJoinModel: public QObject class TestLeftJoinModel: public QObject
{ {
@ -102,6 +101,25 @@ private slots:
QCOMPARE(model.roleNames(), {}); 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() void initializationTest()
{ {
TestSourceModel leftModel({ TestSourceModel leftModel({
@ -135,23 +153,145 @@ private slots:
QCOMPARE(model.roleNames(), roles); QCOMPARE(model.roleNames(), roles);
} }
void setSourceModelDirectlyTest() void collidingRolesTest()
{ {
TestSourceModel leftModel({ TestSourceModel leftModel({
{ "title", { "Token 1", "Token 2" }}, { "name", { "Token 1", "Token 2" }},
{ "communityId", { "community_1", "community_2" }}
});
TestSourceModel rightModel({
{ "name", { "Community 1", "Community 2" }},
{ "communityId", { "community_1", "community_2" }} { "communityId", { "community_1", "community_2" }}
}); });
LeftJoinModel model; LeftJoinModel model;
QAbstractItemModelTester tester(&model); QAbstractItemModelTester tester(&model);
QTest::ignoreMessage(QtWarningMsg, model.setLeftModel(&leftModel);
"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.rowCount(), 0);
QCOMPARE(model.roleNames(), {}); QCOMPARE(model.roleNames(), {});
model.setRightModel(&rightModel);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QTest::ignoreMessage(QtWarningMsg,
"Source models contain conflicting model names: "
"(\"name\")!");
model.setJoinRole("communityId");
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
}
void duplicatedRolesTest()
{
{
TestSourceModel leftModel({
{ "name", { "Token 1", "Token 2" }},
{ "name", { "Token 1", "Token 2" }},
{ "communityId", { "community_1", "community_2" }}
});
TestSourceModel rightModel({
{ "title", { "Community 1", "Community 2" }},
{ "communityId", { "community_1", "community_2" }}
});
LeftJoinModel model;
QAbstractItemModelTester tester(&model);
model.setLeftModel(&leftModel);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
model.setRightModel(&rightModel);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QTest::ignoreMessage(QtWarningMsg,
"Each of the source models must have unique "
"role names!");
model.setJoinRole("communityId");
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
}
{
TestSourceModel leftModel({
{ "name", { "Token 1", "Token 2" }},
{ "communityId", { "community_1", "community_2" }},
{ "communityId", { "community_1", "community_2" }}
});
TestSourceModel rightModel({
{ "title", { "Community 1", "Community 2" }},
{ "communityId", { "community_1", "community_2" }}
});
LeftJoinModel model;
QAbstractItemModelTester tester(&model);
model.setLeftModel(&leftModel);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
model.setRightModel(&rightModel);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QTest::ignoreMessage(QtWarningMsg,
"Each of the source models must have unique "
"role names!");
model.setJoinRole("communityId");
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
}
{
TestSourceModel leftModel({
{ "name", { "Token 1", "Token 2" }},
{ "communityId", { "community_1", "community_2" }}
});
TestSourceModel rightModel({
{ "title", { "Community 1", "Community 2" }},
{ "title", { "Community 1", "Community 2" }},
{ "communityId", { "community_1", "community_2" }}
});
LeftJoinModel model;
QAbstractItemModelTester tester(&model);
model.setLeftModel(&leftModel);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
model.setRightModel(&rightModel);
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
QTest::ignoreMessage(QtWarningMsg,
"Each of the source models must have unique "
"role names!");
model.setJoinRole("communityId");
QCOMPARE(model.rowCount(), 0);
QCOMPARE(model.roleNames(), {});
}
} }
void noJoinRoleTest() void noJoinRoleTest()
@ -266,6 +406,7 @@ private slots:
TestSourceModel rightModel({ TestSourceModel rightModel({
{ "name", { "Community 1", "Community 2" }}, { "name", { "Community 1", "Community 2" }},
{ "color", { "red", "blue" }},
{ "communityId", { "community_1", "community_2" }} { "communityId", { "community_1", "community_2" }}
}); });
@ -283,6 +424,8 @@ private slots:
QCOMPARE(model.data(model.index(1, 0), 1), QString("community_2")); 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(0, 0), 2), QString("Community 1"));
QCOMPARE(model.data(model.index(1, 0), 2), QString("Community 2")); QCOMPARE(model.data(model.index(1, 0), 2), QString("Community 2"));
QCOMPARE(model.data(model.index(0, 0), 3), QString("red"));
QCOMPARE(model.data(model.index(1, 0), 3), QString("blue"));
} }
void changesPropagationTest() void changesPropagationTest()