StatusQ: Add generic LeftJoinModel model mimicking SQL Left Join
Closes: #12502
This commit is contained in:
parent
628d9cdd31
commit
ffadd7522c
|
@ -0,0 +1,283 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
import StatusQ 0.1
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property var colors: [
|
||||||
|
"red", "green", "purple", "orange","gray", "ping", "brown", "blue"
|
||||||
|
]
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: leftModel
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
title: "Token 1"
|
||||||
|
communityId: "1"
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
title: "Token 2"
|
||||||
|
communityId: "1"
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
title: "Token 3"
|
||||||
|
communityId: "2"
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
title: "Token 4"
|
||||||
|
communityId: "3"
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
title: "Token 5"
|
||||||
|
communityId: ""
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
title: "Token 6"
|
||||||
|
communityId: "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel {
|
||||||
|
id: rightModel
|
||||||
|
|
||||||
|
ListElement {
|
||||||
|
communityId: "1"
|
||||||
|
name: "Community 1"
|
||||||
|
color: "red"
|
||||||
|
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
communityId: "2"
|
||||||
|
name: "Community 2"
|
||||||
|
color: "green"
|
||||||
|
}
|
||||||
|
ListElement {
|
||||||
|
communityId: "3"
|
||||||
|
name: "Community 3"
|
||||||
|
color: "blue"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LeftJoinModel {
|
||||||
|
id: leftJoinModel
|
||||||
|
|
||||||
|
leftModel: leftModel
|
||||||
|
rightModel: rightModel
|
||||||
|
|
||||||
|
joinRole: "communityId"
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 40
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
border.color: "gray"
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
model: leftModel
|
||||||
|
|
||||||
|
header: Label {
|
||||||
|
height: implicitHeight * 2
|
||||||
|
text: `Left model (${leftModel.count})`
|
||||||
|
|
||||||
|
font.bold: true
|
||||||
|
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar {}
|
||||||
|
|
||||||
|
delegate: Label {
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
|
text: `${model.title}, community id: ${model.communityId || "-"}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
border.color: "gray"
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
model: rightModel
|
||||||
|
|
||||||
|
header: Label {
|
||||||
|
height: implicitHeight * 2
|
||||||
|
text: `Right model (${rightModel.count})`
|
||||||
|
|
||||||
|
font.bold: true
|
||||||
|
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar {}
|
||||||
|
|
||||||
|
delegate: RowLayout {
|
||||||
|
width: ListView.view.width - 10
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: `${model.name}, community id: ${model.communityId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.preferredWidth: parent.height
|
||||||
|
Layout.preferredHeight: parent.height
|
||||||
|
|
||||||
|
color: model.color
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
const colorIndex = root.colors.indexOf(
|
||||||
|
model.color)
|
||||||
|
const nextIndex = (colorIndex + 1)
|
||||||
|
% root.colors.length
|
||||||
|
|
||||||
|
rightModel.setProperty(index, "color",
|
||||||
|
root.colors[nextIndex])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
border.color: "gray"
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: leftJoinListView
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
model: leftJoinModel
|
||||||
|
|
||||||
|
header: Label {
|
||||||
|
height: implicitHeight * 2
|
||||||
|
text: `Left Join model (${leftJoinListView.count})`
|
||||||
|
|
||||||
|
font.bold: true
|
||||||
|
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar {}
|
||||||
|
|
||||||
|
delegate: RowLayout {
|
||||||
|
id: row
|
||||||
|
|
||||||
|
width: ListView.view.width - 10
|
||||||
|
|
||||||
|
readonly property bool hasCommunity: model.communityId.length
|
||||||
|
|
||||||
|
Label {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
text: model.title + (row.hasCommunity
|
||||||
|
? `, community id: ${model.communityId}, name: ${model.name}`
|
||||||
|
: "")
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.preferredWidth: parent.height
|
||||||
|
Layout.preferredHeight: parent.height
|
||||||
|
|
||||||
|
color: model.color || "transparent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Left model count: " + leftModel.count
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: "Right model count: " + rightModel.count
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: "add 100 left model items"
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
|
||||||
|
const count = leftModel.count
|
||||||
|
|
||||||
|
const entry = {
|
||||||
|
title: "Token " + (count + 1),
|
||||||
|
communityId: (Math.floor(Math.random() * rightModel.count) + 1).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
leftModel.append(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: "add 10 right model items"
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
const count = rightModel.count
|
||||||
|
|
||||||
|
const entry = {
|
||||||
|
communityId: count.toString(),
|
||||||
|
name: "Community " + count,
|
||||||
|
color: root.colors[Math.floor(Math.random() * colors.length)]
|
||||||
|
}
|
||||||
|
|
||||||
|
rightModel.append(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: "shuffle"
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
const count = leftModel.count
|
||||||
|
const iterations = count / 2
|
||||||
|
|
||||||
|
for (let i = 0; i < iterations; i++) {
|
||||||
|
leftModel.move(Math.floor(Math.random() * (count - 1)),
|
||||||
|
Math.floor(Math.random() * (count - 1)),
|
||||||
|
Math.floor(Math.random() * 2) + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// category: Models
|
|
@ -89,6 +89,7 @@ endif()
|
||||||
add_library(StatusQ SHARED
|
add_library(StatusQ SHARED
|
||||||
${STATUSQ_QRC_COMPILED}
|
${STATUSQ_QRC_COMPILED}
|
||||||
include/StatusQ/QClipboardProxy.h
|
include/StatusQ/QClipboardProxy.h
|
||||||
|
include/StatusQ/leftjoinmodel.h
|
||||||
include/StatusQ/modelutilsinternal.h
|
include/StatusQ/modelutilsinternal.h
|
||||||
include/StatusQ/permissionutilsinternal.h
|
include/StatusQ/permissionutilsinternal.h
|
||||||
include/StatusQ/rolesrenamingmodel.h
|
include/StatusQ/rolesrenamingmodel.h
|
||||||
|
@ -97,6 +98,7 @@ add_library(StatusQ SHARED
|
||||||
include/StatusQ/statuswindow.h
|
include/StatusQ/statuswindow.h
|
||||||
include/StatusQ/stringutilsinternal.h
|
include/StatusQ/stringutilsinternal.h
|
||||||
src/QClipboardProxy.cpp
|
src/QClipboardProxy.cpp
|
||||||
|
src/leftjoinmodel.cpp
|
||||||
src/modelutilsinternal.cpp
|
src/modelutilsinternal.cpp
|
||||||
src/permissionutilsinternal.cpp
|
src/permissionutilsinternal.cpp
|
||||||
src/plugin.cpp
|
src/plugin.cpp
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QIdentityProxyModel>
|
||||||
|
|
||||||
|
class LeftJoinModel : public QIdentityProxyModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QAbstractItemModel* leftModel READ leftModel
|
||||||
|
WRITE setLeftModel NOTIFY leftModelChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(QAbstractItemModel* rightModel READ rightModel
|
||||||
|
WRITE setRightModel NOTIFY rightModelChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(QString joinRole READ joinRole
|
||||||
|
WRITE setJoinRole NOTIFY joinRoleChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit LeftJoinModel(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
void setLeftModel(QAbstractItemModel* model);
|
||||||
|
QAbstractItemModel* leftModel() const;
|
||||||
|
|
||||||
|
void setRightModel(QAbstractItemModel* model);
|
||||||
|
QAbstractItemModel* rightModel() const;
|
||||||
|
|
||||||
|
void setJoinRole(const QString& joinRole);
|
||||||
|
const QString& joinRole() const;
|
||||||
|
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
QVariant data(const QModelIndex& index, int role) const override;
|
||||||
|
void setSourceModel(QAbstractItemModel* newSourceModel) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void leftModelChanged();
|
||||||
|
void rightModelChanged();
|
||||||
|
void joinRoleChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initializeIfReady();
|
||||||
|
void initialize();
|
||||||
|
|
||||||
|
int m_rightModelRolesOffset = 0;
|
||||||
|
QHash<int, QByteArray> m_roleNames;
|
||||||
|
QVector<int> m_joinedRoles;
|
||||||
|
|
||||||
|
QString m_joinRole;
|
||||||
|
int m_leftModelJoinRole = 0;
|
||||||
|
int m_rightModelJoinRole = 0;
|
||||||
|
|
||||||
|
QAbstractItemModel* m_leftModel = nullptr;
|
||||||
|
QAbstractItemModel* m_rightModel = nullptr;
|
||||||
|
|
||||||
|
bool m_leftModelDestroyed = false;
|
||||||
|
bool m_rightModelDestroyed = false;
|
||||||
|
|
||||||
|
mutable QPersistentModelIndex m_lastUsedRightModelIndex;
|
||||||
|
};
|
|
@ -0,0 +1,240 @@
|
||||||
|
#include "StatusQ/leftjoinmodel.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
LeftJoinModel::LeftJoinModel(QObject* parent)
|
||||||
|
: QIdentityProxyModel{parent}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void LeftJoinModel::initializeIfReady()
|
||||||
|
{
|
||||||
|
if (m_leftModel && m_rightModel && !m_joinRole.isEmpty()
|
||||||
|
&& !m_leftModel->roleNames().empty()
|
||||||
|
&& !m_rightModel->roleNames().empty())
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
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!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto leftModelJoinRoleList = leftRoleNames.keys(m_joinRole.toUtf8());
|
||||||
|
auto rightModelJoinRoleList = rightRoleNames.keys(m_joinRole.toUtf8());
|
||||||
|
|
||||||
|
if (leftModelJoinRoleList.size() != 1
|
||||||
|
|| rightModelJoinRoleList.size() != 1) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
auto leftRoles = leftRoleNames.keys();
|
||||||
|
auto maxLeftRole = std::max_element(leftRoles.cbegin(), leftRoles.cend());
|
||||||
|
auto rightRolesOffset = *maxLeftRole + 1;
|
||||||
|
auto roleNames = leftRoleNames;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_rightModelRolesOffset = rightRolesOffset;
|
||||||
|
m_roleNames = roleNames;
|
||||||
|
|
||||||
|
connect(m_rightModel, &QAbstractItemModel::dataChanged, this,
|
||||||
|
[this](auto& topLeft, auto& bottomRight, auto& roles) {
|
||||||
|
QVector<int> rolesTranslated;
|
||||||
|
|
||||||
|
if (roles.contains(m_rightModelJoinRole)) {
|
||||||
|
rolesTranslated = m_joinedRoles;
|
||||||
|
} else {
|
||||||
|
rolesTranslated = roles;
|
||||||
|
|
||||||
|
for (auto& role : rolesTranslated)
|
||||||
|
role += m_rightModelRolesOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit dataChanged(index(0, 0), index(rowCount() - 1, 0), 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
connect(m_rightModel, &QAbstractItemModel::rowsRemoved, this,
|
||||||
|
emitJoinedRolesChanged);
|
||||||
|
connect(m_rightModel, &QAbstractItemModel::rowsInserted, this,
|
||||||
|
emitJoinedRolesChanged);
|
||||||
|
connect(m_rightModel, &QAbstractItemModel::modelReset, this,
|
||||||
|
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())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto idx = m_leftModel->index(index.row(), index.column());
|
||||||
|
|
||||||
|
if (role < m_rightModelRolesOffset)
|
||||||
|
return m_leftModel->data(idx, role);
|
||||||
|
|
||||||
|
if (m_rightModelDestroyed)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
QVariant joinRoleLeftValue = m_leftModel->data(idx, m_leftModelJoinRole);
|
||||||
|
|
||||||
|
if (m_lastUsedRightModelIndex.isValid()
|
||||||
|
&& m_rightModel->data(m_lastUsedRightModelIndex,
|
||||||
|
m_rightModelJoinRole) == joinRoleLeftValue)
|
||||||
|
{
|
||||||
|
return m_rightModel->data(m_lastUsedRightModelIndex,
|
||||||
|
role - m_rightModelRolesOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
int rightModelCount = m_rightModel->rowCount();
|
||||||
|
|
||||||
|
for (int i = 0; i < rightModelCount; i++) {
|
||||||
|
auto rightModelIdx = m_rightModel->index(i, 0);
|
||||||
|
auto rightJointRoleValue = m_rightModel->data(rightModelIdx,
|
||||||
|
m_rightModelJoinRole);
|
||||||
|
|
||||||
|
if (joinRoleLeftValue == rightJointRoleValue) {
|
||||||
|
m_lastUsedRightModelIndex = rightModelIdx;
|
||||||
|
|
||||||
|
return m_rightModel->data(rightModelIdx,
|
||||||
|
role - m_rightModelRolesOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void LeftJoinModel::setLeftModel(QAbstractItemModel* model)
|
||||||
|
{
|
||||||
|
if (m_leftModel == model)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_leftModel != nullptr || m_leftModelDestroyed) {
|
||||||
|
qWarning("Changing left model is not supported!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
|
||||||
|
emit leftModelChanged();
|
||||||
|
|
||||||
|
initializeIfReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
QAbstractItemModel* LeftJoinModel::leftModel() const
|
||||||
|
{
|
||||||
|
return m_leftModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LeftJoinModel::setRightModel(QAbstractItemModel* model)
|
||||||
|
{
|
||||||
|
if (m_rightModel == model)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (m_rightModel != nullptr || m_rightModelDestroyed) {
|
||||||
|
qWarning("Changing right model is not supported!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
|
||||||
|
emit rightModelChanged();
|
||||||
|
|
||||||
|
initializeIfReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
QAbstractItemModel* LeftJoinModel::rightModel() const
|
||||||
|
{
|
||||||
|
return m_rightModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LeftJoinModel::setJoinRole(const QString& joinRole)
|
||||||
|
{
|
||||||
|
if (m_joinRole.isEmpty() && joinRole.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!m_joinRole.isEmpty()) {
|
||||||
|
qWarning("Changing join role is not supported!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_joinRole = joinRole;
|
||||||
|
|
||||||
|
emit joinRoleChanged();
|
||||||
|
|
||||||
|
initializeIfReady();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString& LeftJoinModel::joinRole() const
|
||||||
|
{
|
||||||
|
return m_joinRole;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LeftJoinModel::setSourceModel(QAbstractItemModel* newSourceModel)
|
||||||
|
{
|
||||||
|
qWarning() << "Source model is not intended to be set directly on this model."
|
||||||
|
" Use setLeftModel and setRightModel instead!";
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> LeftJoinModel::roleNames() const
|
||||||
|
{
|
||||||
|
return m_roleNames;
|
||||||
|
}
|
|
@ -4,6 +4,7 @@
|
||||||
#include <qqmlsortfilterproxymodeltypes.h>
|
#include <qqmlsortfilterproxymodeltypes.h>
|
||||||
|
|
||||||
#include "StatusQ/QClipboardProxy.h"
|
#include "StatusQ/QClipboardProxy.h"
|
||||||
|
#include "StatusQ/leftjoinmodel.h"
|
||||||
#include "StatusQ/modelutilsinternal.h"
|
#include "StatusQ/modelutilsinternal.h"
|
||||||
#include "StatusQ/permissionutilsinternal.h"
|
#include "StatusQ/permissionutilsinternal.h"
|
||||||
#include "StatusQ/rolesrenamingmodel.h"
|
#include "StatusQ/rolesrenamingmodel.h"
|
||||||
|
@ -25,6 +26,7 @@ public:
|
||||||
qmlRegisterType<StatusSyntaxHighlighter>("StatusQ", 0, 1, "StatusSyntaxHighlighter");
|
qmlRegisterType<StatusSyntaxHighlighter>("StatusQ", 0, 1, "StatusSyntaxHighlighter");
|
||||||
qmlRegisterType<RXValidator>("StatusQ", 0, 1, "RXValidator");
|
qmlRegisterType<RXValidator>("StatusQ", 0, 1, "RXValidator");
|
||||||
|
|
||||||
|
qmlRegisterType<LeftJoinModel>("StatusQ", 0, 1, "LeftJoinModel");
|
||||||
qmlRegisterType<RolesRenamingModel>("StatusQ", 0, 1, "RolesRenamingModel");
|
qmlRegisterType<RolesRenamingModel>("StatusQ", 0, 1, "RolesRenamingModel");
|
||||||
qmlRegisterType<RoleRename>("StatusQ", 0, 1, "RoleRename");
|
qmlRegisterType<RoleRename>("StatusQ", 0, 1, "RoleRename");
|
||||||
|
|
||||||
|
|
|
@ -44,3 +44,7 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||||
add_executable(RolesRenamingModelTest tst_RolesRenamingModel.cpp)
|
add_executable(RolesRenamingModelTest tst_RolesRenamingModel.cpp)
|
||||||
target_link_libraries(RolesRenamingModelTest PRIVATE Qt5::Qml Qt5::Test StatusQ)
|
target_link_libraries(RolesRenamingModelTest PRIVATE Qt5::Qml Qt5::Test StatusQ)
|
||||||
add_test(RolesRenamingModelTest COMMAND RolesRenamingModelTest)
|
add_test(RolesRenamingModelTest COMMAND RolesRenamingModelTest)
|
||||||
|
|
||||||
|
add_executable(LeftJoinModelTest tst_LeftJoinModel.cpp)
|
||||||
|
target_link_libraries(LeftJoinModelTest PRIVATE Qt5::Test StatusQ)
|
||||||
|
add_test(LeftJoinModelTest COMMAND LeftJoinModelTest)
|
||||||
|
|
|
@ -0,0 +1,626 @@
|
||||||
|
#include <QSignalSpy>
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <StatusQ/leftjoinmodel.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class TestSourceModel : public QAbstractListModel {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
explicit TestSourceModel(QList<QPair<QString, QVariantList>> data)
|
||||||
|
: m_data(std::move(data))
|
||||||
|
{
|
||||||
|
m_roles.reserve(m_data.size());
|
||||||
|
|
||||||
|
for (auto i = 0; i < m_data.size(); i++)
|
||||||
|
m_roles.insert(i, m_data.at(i).first.toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex& parent) const override
|
||||||
|
{
|
||||||
|
assert(m_data.size());
|
||||||
|
return m_data.first().second.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant data(const QModelIndex& index, int role) const override
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const auto row = index.row();
|
||||||
|
|
||||||
|
if (role >= m_data.length() || row >= m_data.at(0).second.length())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return m_data.at(role).second.at(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(int index, QVariantList row)
|
||||||
|
{
|
||||||
|
beginInsertRows(QModelIndex{}, index, index);
|
||||||
|
|
||||||
|
assert(row.size() == m_data.size());
|
||||||
|
|
||||||
|
for (int i = 0; i < m_data.size(); i++) {
|
||||||
|
auto& roleVariantList = m_data[i].second;
|
||||||
|
assert(index <= roleVariantList.size());
|
||||||
|
roleVariantList.insert(index, row.at(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(int index, int role, QVariant value)
|
||||||
|
{
|
||||||
|
assert(role < m_data.size() && index < m_data[role].second.size());
|
||||||
|
m_data[role].second[index].setValue(std::move(value));
|
||||||
|
|
||||||
|
emit dataChanged(this->index(index, 0), this->index(index, 0), { role });
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(int index)
|
||||||
|
{
|
||||||
|
beginRemoveRows(QModelIndex{}, index, index);
|
||||||
|
|
||||||
|
for (int i = 0; i < m_data.size(); i++) {
|
||||||
|
auto& roleVariantList = m_data[i].second;
|
||||||
|
assert(index < roleVariantList.size());
|
||||||
|
roleVariantList.removeAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
endRemoveRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> roleNames() const override
|
||||||
|
{
|
||||||
|
return m_roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<QPair<QString, QVariantList>> m_data;
|
||||||
|
QHash<int, QByteArray> m_roles;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestLeftJoinModel: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void emptyModelTest() {
|
||||||
|
LeftJoinModel model;
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(), 0);
|
||||||
|
QCOMPARE(model.roleNames(), {});
|
||||||
|
}
|
||||||
|
|
||||||
|
void initializationTest()
|
||||||
|
{
|
||||||
|
TestSourceModel leftModel({
|
||||||
|
{ "title", { "Token 1", "Token 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
TestSourceModel rightModel({
|
||||||
|
{ "name", { "Community 1", "Community 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
LeftJoinModel model;
|
||||||
|
model.setLeftModel(&leftModel);
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(), 0);
|
||||||
|
QCOMPARE(model.roleNames(), {});
|
||||||
|
|
||||||
|
model.setRightModel(&rightModel);
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(), 0);
|
||||||
|
QCOMPARE(model.roleNames(), {});
|
||||||
|
|
||||||
|
model.setJoinRole("communityId");
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(), 2);
|
||||||
|
|
||||||
|
QHash<int, QByteArray> roles{{0, "title" }, {1, "communityId"}, {2, "name"}};
|
||||||
|
QCOMPARE(model.roleNames(), roles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSourceModelDirectlyTest()
|
||||||
|
{
|
||||||
|
TestSourceModel leftModel({
|
||||||
|
{ "title", { "Token 1", "Token 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
LeftJoinModel 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 noJoinRoleTest()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
TestSourceModel leftModel({
|
||||||
|
{ "title", { "Token 1", "Token 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
TestSourceModel rightModel({
|
||||||
|
{ "name", { "Community 1", "Community 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
LeftJoinModel model;
|
||||||
|
model.setLeftModel(&leftModel);
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(), 0);
|
||||||
|
QCOMPARE(model.roleNames(), {});
|
||||||
|
|
||||||
|
model.setRightModel(&rightModel);
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(), 0);
|
||||||
|
QCOMPARE(model.roleNames(), {});
|
||||||
|
|
||||||
|
QTest::ignoreMessage(QtWarningMsg,
|
||||||
|
"Both left and right models have to contain "
|
||||||
|
"join role someRole!");
|
||||||
|
|
||||||
|
model.setJoinRole("someRole");
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(), 0);
|
||||||
|
QCOMPARE(model.roleNames(), {});
|
||||||
|
}
|
||||||
|
{
|
||||||
|
TestSourceModel leftModel({
|
||||||
|
{ "title", { "Token 1", "Token 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
TestSourceModel rightModel({
|
||||||
|
{ "name", { "Community 1", "Community 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
LeftJoinModel model;
|
||||||
|
model.setLeftModel(&leftModel);
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(), 0);
|
||||||
|
QCOMPARE(model.roleNames(), {});
|
||||||
|
|
||||||
|
model.setRightModel(&rightModel);
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(), 0);
|
||||||
|
QCOMPARE(model.roleNames(), {});
|
||||||
|
|
||||||
|
QTest::ignoreMessage(QtWarningMsg,
|
||||||
|
"Both left and right models have to contain "
|
||||||
|
"join role title!");
|
||||||
|
|
||||||
|
model.setJoinRole("title");
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(), 0);
|
||||||
|
QCOMPARE(model.roleNames(), {});
|
||||||
|
}
|
||||||
|
{
|
||||||
|
TestSourceModel leftModel({
|
||||||
|
{ "title", { "Token 1", "Token 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
TestSourceModel rightModel({
|
||||||
|
{ "name", { "Community 1", "Community 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
LeftJoinModel model;
|
||||||
|
model.setLeftModel(&leftModel);
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(), 0);
|
||||||
|
QCOMPARE(model.roleNames(), {});
|
||||||
|
|
||||||
|
model.setRightModel(&rightModel);
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(), 0);
|
||||||
|
QCOMPARE(model.roleNames(), {});
|
||||||
|
|
||||||
|
QTest::ignoreMessage(QtWarningMsg,
|
||||||
|
"Both left and right models have to contain "
|
||||||
|
"join role name!");
|
||||||
|
|
||||||
|
model.setJoinRole("name");
|
||||||
|
|
||||||
|
QCOMPARE(model.rowCount(), 0);
|
||||||
|
QCOMPARE(model.roleNames(), {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void basicAccesTest()
|
||||||
|
{
|
||||||
|
TestSourceModel leftModel({
|
||||||
|
{ "title", { "Token 1", "Token 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
TestSourceModel rightModel({
|
||||||
|
{ "name", { "Community 1", "Community 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
LeftJoinModel model;
|
||||||
|
model.setLeftModel(&leftModel);
|
||||||
|
model.setRightModel(&rightModel);
|
||||||
|
model.setJoinRole("communityId");
|
||||||
|
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void changesPropagationTest()
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
model.setLeftModel(&leftModel);
|
||||||
|
model.setRightModel(&rightModel);
|
||||||
|
model.setJoinRole("communityId");
|
||||||
|
|
||||||
|
{
|
||||||
|
QSignalSpy dataChangedSpy(&model, &LeftJoinModel::dataChanged);
|
||||||
|
|
||||||
|
rightModel.update(0, 0, "Community 1 Updated");
|
||||||
|
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});
|
||||||
|
|
||||||
|
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 Updated"));
|
||||||
|
QCOMPARE(model.data(model.index(1, 0), 2), QString("Community 2"));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
QSignalSpy dataChangedSpy(&model, &LeftJoinModel::dataChanged);
|
||||||
|
|
||||||
|
leftModel.update(1, 0, "Token 2 Updated");
|
||||||
|
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>>(), {0});
|
||||||
|
|
||||||
|
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 Updated"));
|
||||||
|
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 Updated"));
|
||||||
|
QCOMPARE(model.data(model.index(1, 0), 2), QString("Community 2"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rightModelJoinRoleChangesPropagationTest()
|
||||||
|
{
|
||||||
|
TestSourceModel leftModel({
|
||||||
|
{ "title", { "Token 1", "Token 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
TestSourceModel rightModel({
|
||||||
|
{ "name", { "Community 1", "Community 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
LeftJoinModel model;
|
||||||
|
model.setLeftModel(&leftModel);
|
||||||
|
model.setRightModel(&rightModel);
|
||||||
|
model.setJoinRole("communityId");
|
||||||
|
|
||||||
|
QSignalSpy dataChangedSpy(&model, &LeftJoinModel::dataChanged);
|
||||||
|
|
||||||
|
rightModel.update(1, 1, "community_3");
|
||||||
|
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});
|
||||||
|
|
||||||
|
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), {});
|
||||||
|
}
|
||||||
|
|
||||||
|
void rightModelRemovalPropagationTest()
|
||||||
|
{
|
||||||
|
TestSourceModel leftModel({
|
||||||
|
{ "title", { "Token 1", "Token 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
TestSourceModel rightModel({
|
||||||
|
{ "name", { "Community 1", "Community 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
LeftJoinModel model;
|
||||||
|
model.setLeftModel(&leftModel);
|
||||||
|
model.setRightModel(&rightModel);
|
||||||
|
model.setJoinRole("communityId");
|
||||||
|
|
||||||
|
QSignalSpy dataChangedSpy(&model, &LeftJoinModel::dataChanged);
|
||||||
|
|
||||||
|
rightModel.remove(1);
|
||||||
|
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});
|
||||||
|
|
||||||
|
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), {});
|
||||||
|
}
|
||||||
|
|
||||||
|
void rightModelAdditionPropagationTest()
|
||||||
|
{
|
||||||
|
TestSourceModel leftModel({
|
||||||
|
{ "title", { "Token 1", "Token 2", "Token 3"}},
|
||||||
|
{ "communityId", { "community_1", "community_2", "community_3" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
TestSourceModel rightModel({
|
||||||
|
{ "name", { "Community 1", "Community 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
LeftJoinModel model;
|
||||||
|
model.setLeftModel(&leftModel);
|
||||||
|
model.setRightModel(&rightModel);
|
||||||
|
model.setJoinRole("communityId");
|
||||||
|
|
||||||
|
QSignalSpy dataChangedSpy(&model, &LeftJoinModel::dataChanged);
|
||||||
|
|
||||||
|
rightModel.insert(2, {"Community 3", "community_3"});
|
||||||
|
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});
|
||||||
|
|
||||||
|
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_3"));
|
||||||
|
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 3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void leftModelJoinRoleChangesPropagationTest()
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
model.setLeftModel(&leftModel);
|
||||||
|
model.setRightModel(&rightModel);
|
||||||
|
model.setJoinRole("communityId");
|
||||||
|
|
||||||
|
QSignalSpy dataChangedSpy(&model, &LeftJoinModel::dataChanged);
|
||||||
|
|
||||||
|
leftModel.update(1, 1, "community_1");
|
||||||
|
QCOMPARE(dataChangedSpy.count(), 2);
|
||||||
|
|
||||||
|
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});
|
||||||
|
|
||||||
|
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_1"));
|
||||||
|
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 1"));
|
||||||
|
QCOMPARE(model.data(model.index(2, 0), 2), QString("Community 1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void modelsDeletedBeforeInitializationTest()
|
||||||
|
{
|
||||||
|
auto leftModel = std::make_unique<TestSourceModel>(
|
||||||
|
QList<QPair<QString, QVariantList>>{
|
||||||
|
{ "title", { "Token 1", "Token 2", "Token 3"}},
|
||||||
|
{ "communityId", { "community_1", "community_2", "community_1" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
auto rightModel = std::make_unique<TestSourceModel>(
|
||||||
|
QList<QPair<QString, QVariantList>>{
|
||||||
|
{ "name", { "Community 1", "Community 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
LeftJoinModel model;
|
||||||
|
model.setLeftModel(leftModel.get());
|
||||||
|
model.setRightModel(rightModel.get());
|
||||||
|
|
||||||
|
leftModel.reset();
|
||||||
|
rightModel.reset();
|
||||||
|
|
||||||
|
model.setJoinRole("communityId");
|
||||||
|
|
||||||
|
QCOMPARE(model.roleNames(), {});
|
||||||
|
QCOMPARE(model.rowCount(), 0);
|
||||||
|
QCOMPARE(model.data(model.index(0, 0), 0), {});
|
||||||
|
QCOMPARE(model.data(model.index(0, 0), 2), {});
|
||||||
|
|
||||||
|
TestSourceModel newLeftModel({
|
||||||
|
{ "title", { "Token 1", "Token 2", "Token 3"}},
|
||||||
|
{ "communityId", { "community_1", "community_2", "community_1" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
TestSourceModel newRightModel({
|
||||||
|
{ "name", { "Community 1", "Community 2" }},
|
||||||
|
{ "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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void modelsDeletedAfterInitializationTest()
|
||||||
|
{
|
||||||
|
auto leftModel = std::make_unique<TestSourceModel>(
|
||||||
|
QList<QPair<QString, QVariantList>>{
|
||||||
|
{ "title", { "Token 1", "Token 2", "Token 3"}},
|
||||||
|
{ "communityId", { "community_1", "community_2", "community_1" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
auto rightModel = std::make_unique<TestSourceModel>(
|
||||||
|
QList<QPair<QString, QVariantList>>{
|
||||||
|
{ "name", { "Community 1", "Community 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
LeftJoinModel model;
|
||||||
|
model.setLeftModel(leftModel.get());
|
||||||
|
model.setRightModel(rightModel.get());
|
||||||
|
|
||||||
|
model.setJoinRole("communityId");
|
||||||
|
|
||||||
|
leftModel.reset();
|
||||||
|
rightModel.reset();
|
||||||
|
|
||||||
|
QHash<int, QByteArray> roles{{0, "title" }, {1, "communityId"}, {2, "name"}};
|
||||||
|
QCOMPARE(model.roleNames(), roles);
|
||||||
|
QCOMPARE(model.rowCount(), 0);
|
||||||
|
QCOMPARE(model.data(model.index(0, 0), 0), {});
|
||||||
|
QCOMPARE(model.data(model.index(0, 0), 2), {});
|
||||||
|
|
||||||
|
TestSourceModel newLeftModel({
|
||||||
|
{ "title", { "Token 1", "Token 2", "Token 3"}},
|
||||||
|
{ "communityId", { "community_1", "community_2", "community_1" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
TestSourceModel newRightModel({
|
||||||
|
{ "name", { "Community 1", "Community 2" }},
|
||||||
|
{ "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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rightModelDeletedAfterInitializationTest()
|
||||||
|
{
|
||||||
|
auto leftModel = std::make_unique<TestSourceModel>(
|
||||||
|
QList<QPair<QString, QVariantList>>{
|
||||||
|
{ "title", { "Token 1", "Token 2", "Token 3"}},
|
||||||
|
{ "communityId", { "community_1", "community_2", "community_1" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
auto rightModel = std::make_unique<TestSourceModel>(
|
||||||
|
QList<QPair<QString, QVariantList>>{
|
||||||
|
{ "name", { "Community 1", "Community 2" }},
|
||||||
|
{ "communityId", { "community_1", "community_2" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
LeftJoinModel model;
|
||||||
|
model.setLeftModel(leftModel.get());
|
||||||
|
model.setRightModel(rightModel.get());
|
||||||
|
|
||||||
|
model.setJoinRole("communityId");
|
||||||
|
|
||||||
|
rightModel.reset();
|
||||||
|
|
||||||
|
QHash<int, QByteArray> roles{{0, "title" }, {1, "communityId"}, {2, "name"}};
|
||||||
|
QCOMPARE(model.roleNames(), roles);
|
||||||
|
QCOMPARE(model.rowCount(), 3);
|
||||||
|
QCOMPARE(model.data(model.index(0, 0), 0), "Token 1");
|
||||||
|
QCOMPARE(model.data(model.index(0, 0), 1), "community_1");
|
||||||
|
QCOMPARE(model.data(model.index(0, 0), 2), {});
|
||||||
|
|
||||||
|
TestSourceModel newLeftModel({
|
||||||
|
{ "title", { "Token 1", "Token 2", "Token 3"}},
|
||||||
|
{ "communityId", { "community_1", "community_2", "community_1" }}
|
||||||
|
});
|
||||||
|
|
||||||
|
TestSourceModel newRightModel({
|
||||||
|
{ "name", { "Community 1", "Community 2" }},
|
||||||
|
{ "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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QTEST_MAIN(TestLeftJoinModel)
|
||||||
|
#include "tst_LeftJoinModel.moc"
|
Loading…
Reference in New Issue