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"