diff --git a/ui/StatusQ/src/leftjoinmodel.cpp b/ui/StatusQ/src/leftjoinmodel.cpp index 506f0a71b6..d4966cfbd1 100644 --- a/ui/StatusQ/src/leftjoinmodel.cpp +++ b/ui/StatusQ/src/leftjoinmodel.cpp @@ -22,41 +22,56 @@ void LeftJoinModel::initialize() auto leftRoleNames = m_leftModel->roleNames(); auto rightRoleNames = m_rightModel->roleNames(); - if (leftRoleNames.isEmpty() || rightRoleNames.isEmpty()) { - qWarning() << "Both left and right models have to contain some roles!"; + auto leftNames = leftRoleNames.values(); + auto rightNames = rightRoleNames.values(); + + QSet leftNamesSet(leftNames.cbegin(), leftNames.cend()); + QSet 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; } - auto leftModelJoinRoleList = leftRoleNames.keys(m_joinRole.toUtf8()); - auto rightModelJoinRoleList = rightRoleNames.keys(m_joinRole.toUtf8()); + auto namesIntersection = leftNamesSet.intersect(rightNamesSet); + auto hasCommonJoinRole = namesIntersection.remove(m_joinRole.toUtf8()); - if (leftModelJoinRoleList.size() != 1 - || rightModelJoinRoleList.size() != 1) { + if (!hasCommonJoinRole) { qWarning().noquote() << QString("Both left and right models have to " "contain join role %1!").arg(m_joinRole); return; } - m_leftModelJoinRole = leftModelJoinRoleList.at(0); - m_rightModelJoinRole = rightModelJoinRoleList.at(0); + if (!namesIntersection.isEmpty()) { + qWarning().nospace() << "Source models contain conflicting model names: " + << QList(namesIntersection.cbegin(), + namesIntersection.cend()) + << "!"; + return; + } auto leftRoles = leftRoleNames.keys(); auto maxLeftRole = std::max_element(leftRoles.cbegin(), leftRoles.cend()); auto rightRolesOffset = *maxLeftRole + 1; auto roleNames = leftRoleNames; + QVector joinedRoles; auto i = rightRoleNames.constBegin(); while (i != rightRoleNames.constEnd()) { if (i.value() != m_joinRole) { auto roleWithOffset = i.key() + rightRolesOffset; roleNames.insert(roleWithOffset, i.value()); - m_joinedRoles.append(roleWithOffset); + joinedRoles.append(roleWithOffset); } ++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_roleNames = roleNames; connect(m_rightModel, &QAbstractItemModel::dataChanged, this, [this](auto& topLeft, auto& bottomRight, auto& roles) { diff --git a/ui/StatusQ/tests/tst_LeftJoinModel.cpp b/ui/StatusQ/tests/tst_LeftJoinModel.cpp index d4a01617b6..4812d3e3fb 100644 --- a/ui/StatusQ/tests/tst_LeftJoinModel.cpp +++ b/ui/StatusQ/tests/tst_LeftJoinModel.cpp @@ -11,7 +11,6 @@ namespace { class TestSourceModel : public QAbstractListModel { public: - explicit TestSourceModel(QList> data) : m_data(std::move(data)) { @@ -86,7 +85,7 @@ private: QHash m_roles; }; -} +} // anonymous namespace class TestLeftJoinModel: public QObject { @@ -102,6 +101,25 @@ 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({ @@ -135,23 +153,145 @@ private slots: QCOMPARE(model.roleNames(), roles); } - void setSourceModelDirectlyTest() + void collidingRolesTest() { 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" }} }); 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); + model.setLeftModel(&leftModel); QCOMPARE(model.rowCount(), 0); 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() @@ -266,6 +406,7 @@ private slots: TestSourceModel rightModel({ { "name", { "Community 1", "Community 2" }}, + { "color", { "red", "blue" }}, { "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(0, 0), 2), QString("Community 1")); 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()