File indexing completed on 2024-11-10 04:40:14
0001 /* 0002 SPDX-FileCopyrightText: 2009 Stephen Kelly <steveire@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "modelspy.h" 0008 0009 #include <QTest> 0010 #include <qassert.h> 0011 0012 QVariantList extractModelColumn(const QAbstractItemModel &model, const QModelIndex &parent, const int firstRow, const int lastRow) 0013 { 0014 QVariantList result; 0015 for (auto row = firstRow; row <= lastRow; ++row) { 0016 result.append(model.index(row, 0, parent).data()); 0017 } 0018 return result; 0019 } 0020 0021 ModelSpy::ModelSpy(QAbstractItemModel *model, QObject *parent) 0022 : QObject{parent} 0023 , m_model{model} 0024 , m_isSpying{false} 0025 { 0026 qRegisterMetaType<QModelIndex>("QModelIndex"); 0027 } 0028 0029 bool ModelSpy::isEmpty() const 0030 { 0031 return QList<QVariantList>::isEmpty() && m_expectedSignals.isEmpty(); 0032 } 0033 0034 void ModelSpy::setExpectedSignals(const QList<ExpectedSignal> &expectedSignals) 0035 { 0036 m_expectedSignals = expectedSignals; 0037 } 0038 0039 QList<ExpectedSignal> ModelSpy::expectedSignals() const 0040 { 0041 return m_expectedSignals; 0042 } 0043 0044 void ModelSpy::verifySignal(SignalType type, const QModelIndex &parent, int start, int end) 0045 { 0046 const auto expectedSignal = m_expectedSignals.takeFirst(); 0047 QCOMPARE(int(type), int(expectedSignal.signalType)); 0048 QCOMPARE(parent.data(), expectedSignal.parentData); 0049 QCOMPARE(start, expectedSignal.startRow); 0050 QCOMPARE(end, expectedSignal.endRow); 0051 if (!expectedSignal.newData.isEmpty()) { 0052 // TODO 0053 } 0054 } 0055 0056 void ModelSpy::verifySignal(SignalType type, const QModelIndex &parent, int start, int end, const QModelIndex &destParent, int destStart) 0057 { 0058 const auto expectedSignal = m_expectedSignals.takeFirst(); 0059 QCOMPARE(int(type), int(expectedSignal.signalType)); 0060 QCOMPARE(start, expectedSignal.startRow); 0061 QCOMPARE(end, expectedSignal.endRow); 0062 QCOMPARE(parent.data(), expectedSignal.sourceParentData); 0063 QCOMPARE(destParent.data(), expectedSignal.parentData); 0064 QCOMPARE(destStart, expectedSignal.destRow); 0065 if (type == RowsAboutToBeMoved) { 0066 QCOMPARE(extractModelColumn(*m_model, parent, start, end), expectedSignal.newData); 0067 } else { 0068 const auto destEnd = destStart + (end - start); 0069 QCOMPARE(extractModelColumn(*m_model, destParent, destStart, destEnd), expectedSignal.newData); 0070 } 0071 } 0072 0073 void ModelSpy::verifySignal(SignalType type, const QModelIndex &topLeft, const QModelIndex &bottomRight) 0074 { 0075 QCOMPARE(type, DataChanged); 0076 const auto expectedSignal = m_expectedSignals.takeFirst(); 0077 QCOMPARE(int(type), int(expectedSignal.signalType)); 0078 QModelIndex parent = topLeft.parent(); 0079 // This check won't work for toplevel indexes 0080 if (parent.isValid()) { 0081 if (expectedSignal.parentData.isValid()) { 0082 QCOMPARE(parent.data(), expectedSignal.parentData); 0083 } 0084 } 0085 qDebug() << type << topLeft << bottomRight; 0086 QCOMPARE(topLeft.row(), expectedSignal.startRow); 0087 QCOMPARE(bottomRight.row(), expectedSignal.endRow); 0088 QCOMPARE(extractModelColumn(*m_model, parent, topLeft.row(), bottomRight.row()), expectedSignal.newData); 0089 } 0090 0091 void ModelSpy::startSpying() 0092 { 0093 m_isSpying = true; 0094 0095 // If a signal is connected to a slot multiple times, the slot gets called multiple times. 0096 // As we're doing start and stop spying all the time, we disconnect here first to make sure. 0097 0098 disconnect(m_model, &QAbstractItemModel::rowsAboutToBeInserted, this, &ModelSpy::rowsAboutToBeInserted); 0099 disconnect(m_model, &QAbstractItemModel::rowsInserted, this, &ModelSpy::rowsInserted); 0100 disconnect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ModelSpy::rowsAboutToBeRemoved); 0101 disconnect(m_model, &QAbstractItemModel::rowsRemoved, this, &ModelSpy::rowsRemoved); 0102 disconnect(m_model, &QAbstractItemModel::rowsAboutToBeMoved, this, &ModelSpy::rowsAboutToBeMoved); 0103 disconnect(m_model, &QAbstractItemModel::rowsMoved, this, &ModelSpy::rowsMoved); 0104 0105 disconnect(m_model, &QAbstractItemModel::dataChanged, this, &ModelSpy::dataChanged); 0106 0107 connect(m_model, &QAbstractItemModel::rowsAboutToBeInserted, this, &ModelSpy::rowsAboutToBeInserted); 0108 connect(m_model, &QAbstractItemModel::rowsInserted, this, &ModelSpy::rowsInserted); 0109 connect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ModelSpy::rowsAboutToBeRemoved); 0110 connect(m_model, &QAbstractItemModel::rowsRemoved, this, &ModelSpy::rowsRemoved); 0111 connect(m_model, &QAbstractItemModel::rowsAboutToBeMoved, this, &ModelSpy::rowsAboutToBeMoved); 0112 connect(m_model, &QAbstractItemModel::rowsMoved, this, &ModelSpy::rowsMoved); 0113 0114 connect(m_model, &QAbstractItemModel::dataChanged, this, &ModelSpy::dataChanged); 0115 } 0116 0117 void ModelSpy::stopSpying() 0118 { 0119 m_isSpying = false; 0120 disconnect(m_model, &QAbstractItemModel::rowsAboutToBeInserted, this, &ModelSpy::rowsAboutToBeInserted); 0121 disconnect(m_model, &QAbstractItemModel::rowsInserted, this, &ModelSpy::rowsInserted); 0122 disconnect(m_model, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ModelSpy::rowsAboutToBeRemoved); 0123 disconnect(m_model, &QAbstractItemModel::rowsRemoved, this, &ModelSpy::rowsRemoved); 0124 disconnect(m_model, &QAbstractItemModel::rowsAboutToBeMoved, this, &ModelSpy::rowsAboutToBeMoved); 0125 disconnect(m_model, &QAbstractItemModel::rowsMoved, this, &ModelSpy::rowsMoved); 0126 0127 disconnect(m_model, &QAbstractItemModel::dataChanged, this, &ModelSpy::dataChanged); 0128 } 0129 0130 void ModelSpy::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) 0131 { 0132 if (!m_expectedSignals.isEmpty()) { 0133 verifySignal(RowsAboutToBeInserted, parent, start, end); 0134 } else { 0135 append(QVariantList{RowsAboutToBeInserted, QVariant::fromValue(parent), start, end}); 0136 } 0137 } 0138 0139 void ModelSpy::rowsInserted(const QModelIndex &parent, int start, int end) 0140 { 0141 if (!m_expectedSignals.isEmpty()) { 0142 verifySignal(RowsInserted, parent, start, end); 0143 } else { 0144 append(QVariantList{RowsInserted, QVariant::fromValue(parent), start, end}); 0145 } 0146 } 0147 0148 void ModelSpy::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) 0149 { 0150 if (!m_expectedSignals.isEmpty()) { 0151 verifySignal(RowsAboutToBeRemoved, parent, start, end); 0152 } else { 0153 append(QVariantList{RowsAboutToBeRemoved, QVariant::fromValue(parent), start, end}); 0154 } 0155 } 0156 0157 void ModelSpy::rowsRemoved(const QModelIndex &parent, int start, int end) 0158 { 0159 if (!m_expectedSignals.isEmpty()) { 0160 verifySignal(RowsRemoved, parent, start, end); 0161 } else { 0162 append(QVariantList{RowsRemoved, QVariant::fromValue(parent), start, end}); 0163 } 0164 } 0165 0166 void ModelSpy::rowsAboutToBeMoved(const QModelIndex &srcParent, int start, int end, const QModelIndex &destParent, int destStart) 0167 { 0168 if (!m_expectedSignals.isEmpty()) { 0169 verifySignal(RowsAboutToBeMoved, srcParent, start, end, destParent, destStart); 0170 } else { 0171 append(QVariantList{RowsAboutToBeMoved, QVariant::fromValue(srcParent), start, end, QVariant::fromValue(destParent), destStart}); 0172 } 0173 } 0174 0175 void ModelSpy::rowsMoved(const QModelIndex &srcParent, int start, int end, const QModelIndex &destParent, int destStart) 0176 { 0177 if (!m_expectedSignals.isEmpty()) { 0178 verifySignal(RowsMoved, srcParent, start, end, destParent, destStart); 0179 } else { 0180 append(QVariantList{RowsMoved, QVariant::fromValue(srcParent), start, end, QVariant::fromValue(destParent), destStart}); 0181 } 0182 } 0183 0184 void ModelSpy::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) 0185 { 0186 if (!m_expectedSignals.isEmpty()) { 0187 verifySignal(DataChanged, topLeft, bottomRight); 0188 } else { 0189 append(QVariantList{DataChanged, QVariant::fromValue(topLeft), QVariant::fromValue(bottomRight)}); 0190 } 0191 } 0192 0193 QDebug operator<<(QDebug dbg, SignalType type) 0194 { 0195 switch (type) { 0196 case NoSignal: 0197 return dbg << "SignalType::NoSignal"; 0198 case RowsAboutToBeInserted: 0199 return dbg << "SignalType::RowsAboutToBeInserted"; 0200 case RowsInserted: 0201 return dbg << "SignalType::RowsInserted"; 0202 case RowsAboutToBeRemoved: 0203 return dbg << "SignalType::RowsAboutToBeRemoved"; 0204 case RowsRemoved: 0205 return dbg << "SignalType::RowsRemoved"; 0206 case RowsAboutToBeMoved: 0207 return dbg << "SignalType::RowsAboutToBeMoved"; 0208 case RowsMoved: 0209 return dbg << "SignalType::RowsMoved"; 0210 case DataChanged: 0211 return dbg << "SignalType::DataChanged"; 0212 } 0213 0214 Q_UNREACHABLE(); 0215 } 0216 0217 #include "moc_modelspy.cpp"