File indexing completed on 2024-04-28 15:27:50
0001 /* 0002 SPDX-FileCopyrightText: 2009 Stephen Kelly <steveire@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "proxymodeltest.h" 0008 0009 #include <QMimeData> 0010 #include <QSortFilterProxyModel> 0011 0012 ProxyModelTest::ProxyModelTest(QObject *parent) 0013 : QObject(parent) 0014 , m_rootModel(new DynamicTreeModel(this)) 0015 , m_sourceModel(m_rootModel) 0016 , m_proxyModel(nullptr) 0017 , m_intermediateProxyModel(nullptr) 0018 , m_modelSpy(new ModelSpy(this)) 0019 , m_modelCommander(new ModelCommander(m_rootModel, this)) 0020 { 0021 } 0022 0023 void ProxyModelTest::setLazyPersistence(Persistence persistence) 0024 { 0025 m_modelSpy->setLazyPersistence(persistence == LazyPersistence); 0026 } 0027 0028 void ProxyModelTest::setUseIntermediateProxy(SourceModel sourceModel) 0029 { 0030 if (sourceModel == DynamicTree) { 0031 return; 0032 } 0033 0034 m_intermediateProxyModel = new QSortFilterProxyModel(this); 0035 m_intermediateProxyModel->setSourceModel(m_rootModel); 0036 m_sourceModel = m_intermediateProxyModel; 0037 } 0038 0039 static bool hasMetaMethodStartingWith(QObject *object, const QString &checkedSignature) 0040 { 0041 const QMetaObject *mo = object->metaObject(); 0042 bool found = false; 0043 for (int methodIndex = 0; methodIndex < mo->methodCount(); ++methodIndex) { 0044 QMetaMethod mm = mo->method(methodIndex); 0045 QString signature = QString::fromLatin1(mm.methodSignature()); 0046 0047 if (signature.startsWith(checkedSignature)) { 0048 found = true; 0049 break; 0050 } 0051 } 0052 return found; 0053 } 0054 0055 void ProxyModelTest::initRootModel(DynamicTreeModel *rootModel, const QString ¤tTest, const QString ¤tTag) 0056 { 0057 Q_UNUSED(rootModel) 0058 // Get the model into the state it is expected to be in. 0059 0060 if (!hasMetaMethodStartingWith(m_modelCommander, "init_" + currentTest)) { 0061 return; 0062 } 0063 0064 QMetaObject::invokeMethod(m_modelCommander, QString("init_" + currentTest).toLatin1(), Q_ARG(QString, currentTag)); 0065 } 0066 0067 static QSet<QString> unmatchedItems(const QStringList &list, const QStringList &items) 0068 { 0069 const QSet<QString> itemsAset(items.constBegin(), items.constEnd()); 0070 const QSet<QString> listAsSet(list.constBegin(), list.constEnd()); 0071 return listAsSet - itemsAset; 0072 } 0073 0074 void ProxyModelTest::verifyExecutedTests() 0075 { 0076 if (m_dataTags.contains(ProxyModelTestData::failTag())) { 0077 return; 0078 } 0079 const QSet<QString> unimplemented = unmatchedItems(m_modelCommanderTags, m_dataTags); 0080 QString unimplementedTestsString(QStringLiteral("(")); 0081 for (const QString &test : unimplemented) { 0082 unimplementedTestsString.append(test + ","); 0083 } 0084 unimplementedTestsString.append(")"); 0085 0086 if (!unimplemented.isEmpty()) { 0087 QString failString = QStringLiteral("Some tests in %1 were not implemented: %2").arg(m_currentTest, unimplementedTestsString); 0088 m_dataTags.clear(); 0089 m_currentTest = QTest::currentTestFunction(); 0090 QFAIL(failString.toLatin1()); 0091 } 0092 } 0093 0094 void ProxyModelTest::init() 0095 { 0096 QVERIFY(m_modelSpy->isEmpty()); 0097 m_rootModel->clear(); 0098 0099 const char *currentTest = QTest::currentTestFunction(); 0100 const char *currentTag = QTest::currentDataTag(); 0101 QVERIFY(currentTest != nullptr); 0102 initRootModel(m_rootModel, currentTest, currentTag); 0103 0104 Q_ASSERT(sourceModel()); 0105 QAbstractProxyModel *proxyModel = getProxy(); 0106 0107 Q_ASSERT(proxyModel); 0108 // Don't set the sourceModel in getProxy. 0109 Q_ASSERT(!proxyModel->sourceModel()); 0110 connectProxy(proxyModel); 0111 0112 // Get the model into the state it is expected to be in. 0113 m_modelSpy->startSpying(); 0114 QVERIFY(m_modelSpy->isEmpty()); 0115 0116 if (m_currentTest != currentTest) { 0117 verifyExecutedTests(); 0118 m_dataTags.clear(); 0119 0120 QString metaMethod = QString("execute_" + QLatin1String(currentTest)); 0121 0122 if (!hasMetaMethodStartingWith(m_modelCommander, metaMethod)) { 0123 return; 0124 } 0125 0126 QMetaObject::invokeMethod(m_modelCommander, metaMethod.toLatin1(), Q_RETURN_ARG(QStringList, m_modelCommanderTags), Q_ARG(QString, QString())); 0127 m_currentTest = currentTest; 0128 } 0129 m_dataTags.append(currentTag); 0130 } 0131 0132 void ProxyModelTest::cleanup() 0133 { 0134 QVERIFY(m_modelSpy->isEmpty()); 0135 m_modelSpy->stopSpying(); 0136 m_modelSpy->setModel(nullptr); 0137 m_proxyModel->setSourceModel(nullptr); 0138 delete m_proxyModel; 0139 m_proxyModel = nullptr; 0140 QVERIFY(m_modelSpy->isEmpty()); 0141 } 0142 0143 void ProxyModelTest::cleanupTestCase() 0144 { 0145 verifyExecutedTests(); 0146 m_modelCommanderTags.clear(); 0147 if (!m_intermediateProxyModel) { 0148 return; 0149 } 0150 0151 m_sourceModel = m_rootModel; 0152 delete m_intermediateProxyModel; 0153 m_intermediateProxyModel = nullptr; 0154 0155 m_modelSpy->clear(); 0156 } 0157 0158 PersistentIndexChange ProxyModelTest::getChange(IndexFinder parentFinder, int start, int end, int difference, bool toInvalid) 0159 { 0160 Q_ASSERT(start <= end); 0161 PersistentIndexChange change; 0162 change.parentFinder = parentFinder; 0163 change.startRow = start; 0164 change.endRow = end; 0165 change.difference = difference; 0166 change.toInvalid = toInvalid; 0167 return change; 0168 } 0169 0170 void ProxyModelTest::handleSignal(QVariantList expected) 0171 { 0172 QVERIFY(!expected.isEmpty()); 0173 int signalType = expected.takeAt(0).toInt(); 0174 if (NoSignal == signalType) { 0175 return; 0176 } 0177 0178 Q_ASSERT(!m_modelSpy->isEmpty()); 0179 QVariantList result = getResultSignal(); 0180 0181 QCOMPARE(result.takeAt(0).toInt(), signalType); 0182 // Check that the signal we expected to receive was emitted exactly. 0183 switch (signalType) { 0184 case RowsAboutToBeInserted: 0185 case RowsInserted: 0186 case RowsAboutToBeRemoved: 0187 case RowsRemoved: { 0188 QVERIFY(expected.size() == 3); 0189 IndexFinder parentFinder = qvariant_cast<IndexFinder>(expected.at(0)); 0190 parentFinder.setModel(m_proxyModel); 0191 QModelIndex parent = parentFinder.getIndex(); 0192 0193 // This is where is usually goes wrong... 0194 #if 0 0195 qDebug() << qvariant_cast<QModelIndex>(result.at(0)) << parent; 0196 qDebug() << result.at(1) << expected.at(1); 0197 qDebug() << result.at(2) << expected.at(2); 0198 #endif 0199 0200 QCOMPARE(qvariant_cast<QModelIndex>(result.at(0)), parent); 0201 QCOMPARE(result.at(1), expected.at(1)); 0202 QCOMPARE(result.at(2), expected.at(2)); 0203 break; 0204 } 0205 case LayoutAboutToBeChanged: 0206 case LayoutChanged: { 0207 QVERIFY(expected.size() == 0); 0208 QVERIFY(result.size() == 0); 0209 break; 0210 } 0211 case RowsAboutToBeMoved: 0212 case RowsMoved: { 0213 QVERIFY(expected.size() == 5); 0214 IndexFinder scrParentFinder = qvariant_cast<IndexFinder>(expected.at(0)); 0215 scrParentFinder.setModel(m_proxyModel); 0216 QModelIndex srcParent = scrParentFinder.getIndex(); 0217 QCOMPARE(qvariant_cast<QModelIndex>(result.at(0)), srcParent); 0218 QCOMPARE(result.at(1), expected.at(1)); 0219 QCOMPARE(result.at(2), expected.at(2)); 0220 IndexFinder destParentFinder = qvariant_cast<IndexFinder>(expected.at(3)); 0221 destParentFinder.setModel(m_proxyModel); 0222 QModelIndex destParent = destParentFinder.getIndex(); 0223 QCOMPARE(qvariant_cast<QModelIndex>(result.at(3)), destParent); 0224 QCOMPARE(result.at(4), expected.at(4)); 0225 break; 0226 } 0227 case DataChanged: { 0228 QCOMPARE(expected.size(), 2); 0229 IndexFinder topLeftFinder = qvariant_cast<IndexFinder>(expected.at(0)); 0230 topLeftFinder.setModel(m_proxyModel); 0231 QModelIndex topLeft = topLeftFinder.getIndex(); 0232 IndexFinder bottomRightFinder = qvariant_cast<IndexFinder>(expected.at(1)); 0233 bottomRightFinder.setModel(m_proxyModel); 0234 0235 QModelIndex bottomRight = bottomRightFinder.getIndex(); 0236 0237 QVERIFY(topLeft.isValid() && bottomRight.isValid()); 0238 0239 #if 0 0240 qDebug() << qvariant_cast<QModelIndex>(result.at(0)) << topLeft; 0241 qDebug() << qvariant_cast<QModelIndex>(result.at(1)) << bottomRight; 0242 #endif 0243 0244 QCOMPARE(qvariant_cast<QModelIndex>(result.at(0)), topLeft); 0245 QCOMPARE(qvariant_cast<QModelIndex>(result.at(1)), bottomRight); 0246 } 0247 } 0248 } 0249 0250 QVariantList ProxyModelTest::getResultSignal() 0251 { 0252 return m_modelSpy->takeFirst(); 0253 } 0254 0255 void ProxyModelTest::testEmptyModel() 0256 { 0257 Q_ASSERT(sourceModel()); 0258 QAbstractProxyModel *proxyModel = getProxy(); 0259 // Many of these just check that the proxy does not crash when it does not have a source model. 0260 QCOMPARE(proxyModel->rowCount(), 0); 0261 QCOMPARE(proxyModel->columnCount(), 0); 0262 QVERIFY(!proxyModel->index(0, 0).isValid()); 0263 QVERIFY(!proxyModel->data(QModelIndex()).isValid()); 0264 QVERIFY(!proxyModel->parent(QModelIndex()).isValid()); 0265 QVERIFY(!proxyModel->mapToSource(QModelIndex()).isValid()); 0266 QVERIFY(!proxyModel->mapFromSource(QModelIndex()).isValid()); 0267 QVERIFY(!proxyModel->headerData(0, Qt::Horizontal, Qt::DisplayRole).isValid()); 0268 QVERIFY(!proxyModel->headerData(0, Qt::Vertical, Qt::DisplayRole).isValid()); 0269 Qt::ItemFlags flags = proxyModel->flags(QModelIndex()); 0270 QVERIFY(flags == Qt::ItemIsDropEnabled || flags == 0); 0271 QVERIFY(proxyModel->itemData(QModelIndex()).isEmpty()); 0272 QVERIFY(proxyModel->mapSelectionToSource(QItemSelection()).isEmpty()); 0273 QVERIFY(proxyModel->mapSelectionFromSource(QItemSelection()).isEmpty()); 0274 proxyModel->revert(); 0275 QVERIFY(proxyModel->submit()); 0276 QVERIFY(!proxyModel->sourceModel()); 0277 QVERIFY(!proxyModel->canFetchMore(QModelIndex())); 0278 proxyModel->fetchMore(QModelIndex()); 0279 QMimeData *data = new QMimeData(); 0280 QVERIFY(!proxyModel->dropMimeData(data, Qt::CopyAction, 0, 0, QModelIndex())); 0281 delete data; 0282 QVERIFY(!proxyModel->hasChildren()); 0283 QVERIFY(!proxyModel->hasIndex(0, 0, QModelIndex())); 0284 proxyModel->supportedDragActions(); 0285 proxyModel->supportedDropActions(); 0286 delete proxyModel; 0287 } 0288 0289 void ProxyModelTest::testSourceReset() 0290 { 0291 m_modelSpy->stopSpying(); 0292 ModelInsertCommand *ins = new ModelInsertCommand(m_rootModel, this); 0293 ins->setStartRow(0); 0294 ins->interpret( 0295 QLatin1String("- 1" 0296 "- 2" 0297 "- - 3" 0298 "- - 4" 0299 "- 5" 0300 "- 6" 0301 "- 7" 0302 "- 8" 0303 "- 9" 0304 "- - 10")); 0305 ins->doCommand(); 0306 // The proxymodel should reset any internal state it holds when the source model is reset. 0307 QPersistentModelIndex pmi = m_proxyModel->index(0, 0); 0308 testMappings(); 0309 m_rootModel->clear(); // Resets the model. 0310 testMappings(); // Calls some rowCount() etc which should test internal structures in the proxy. 0311 m_proxyModel->setSourceModel(nullptr); 0312 0313 m_modelSpy->startSpying(); 0314 } 0315 0316 void ProxyModelTest::testDestroyModel() 0317 { 0318 QAbstractItemModel *currentSourceModel = m_sourceModel; 0319 DynamicTreeModel *rootModel = new DynamicTreeModel(this); 0320 m_sourceModel = rootModel; 0321 0322 ModelInsertCommand *ins = new ModelInsertCommand(rootModel, this); 0323 ins->setStartRow(0); 0324 ins->interpret( 0325 QLatin1String(" - 1" 0326 " - 1" 0327 " - - 1" 0328 " - 1" 0329 " - 1" 0330 " - 1" 0331 " - 1" 0332 " - 1" 0333 " - - 1")); 0334 ins->doCommand(); 0335 0336 QAbstractProxyModel *proxyModel = getProxy(); 0337 connectProxy(proxyModel); 0338 0339 if (proxyModel->hasChildren()) { 0340 m_modelSpy->startSpying(); 0341 const bool prevPersistMode = m_modelSpy->useLazyPersistence(); 0342 m_modelSpy->setLazyPersistence(false); // don't persist on model reset during destruction 0343 QCOMPARE(m_modelSpy->size(), 0); 0344 delete m_sourceModel; 0345 m_sourceModel = nullptr; 0346 m_modelSpy->stopSpying(); 0347 m_modelSpy->setLazyPersistence(prevPersistMode); 0348 testMappings(); 0349 // QItemSelectionModel in Qt6 signals a source model change if the source is destroyed 0350 QVERIFY(m_modelSpy->size() == 0 || m_modelSpy->size() == 2); 0351 if (m_modelSpy->size() == 2) { 0352 QVERIFY(m_modelSpy->takeFirst().first() == ModelAboutToBeReset); 0353 QVERIFY(m_modelSpy->takeFirst().first() == ModelReset); 0354 } 0355 } 0356 m_sourceModel = currentSourceModel; 0357 } 0358 0359 void ProxyModelTest::doTestMappings(const QModelIndex &parent) 0360 { 0361 if (!m_proxyModel) { 0362 return; 0363 } 0364 QModelIndex idx; 0365 QModelIndex srcIdx; 0366 for (int column = 0; column < m_proxyModel->columnCount(parent); ++column) { 0367 for (int row = 0; row < m_proxyModel->rowCount(parent); ++row) { 0368 idx = m_proxyModel->index(row, column, parent); 0369 QVERIFY(idx.isValid()); 0370 QVERIFY(idx.row() == row); 0371 QVERIFY(idx.column() == column); 0372 QVERIFY(idx.parent() == parent); 0373 QVERIFY(idx.model() == m_proxyModel); 0374 srcIdx = m_proxyModel->mapToSource(idx); 0375 QVERIFY(srcIdx.isValid()); 0376 QVERIFY(srcIdx.model() == m_proxyModel->sourceModel()); 0377 QVERIFY(m_sourceModel == m_proxyModel->sourceModel()); 0378 QVERIFY(idx.data() == srcIdx.data()); 0379 QVERIFY(m_proxyModel->mapFromSource(srcIdx) == idx); 0380 if (m_proxyModel->hasChildren(idx)) { 0381 doTestMappings(idx); 0382 } 0383 } 0384 } 0385 } 0386 0387 void ProxyModelTest::testMappings() 0388 { 0389 doTestMappings(QModelIndex()); 0390 } 0391 0392 void ProxyModelTest::verifyModel(const QModelIndex &parent, int start, int end) 0393 { 0394 Q_UNUSED(start); 0395 Q_UNUSED(end); 0396 0397 QVERIFY(parent.model() == m_proxyModel || !parent.isValid()); 0398 } 0399 0400 void ProxyModelTest::verifyModel(const QModelIndex &parent, int start, int end, const QModelIndex &destParent, int dest) 0401 { 0402 Q_UNUSED(start); 0403 Q_UNUSED(end); 0404 Q_UNUSED(dest); 0405 0406 QVERIFY(parent.model() == m_proxyModel || !parent.isValid()); 0407 QVERIFY(destParent.model() == m_proxyModel || !destParent.isValid()); 0408 } 0409 0410 void ProxyModelTest::verifyModel(const QModelIndex &topLeft, const QModelIndex &bottomRight) 0411 { 0412 QVERIFY(topLeft.model() == m_proxyModel || !topLeft.isValid()); 0413 QVERIFY(bottomRight.model() == m_proxyModel || !bottomRight.isValid()); 0414 } 0415 0416 void ProxyModelTest::connectProxy(QAbstractProxyModel *proxyModel) 0417 { 0418 if (m_proxyModel) { 0419 disconnect(m_proxyModel, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), this, SLOT(testMappings())); 0420 disconnect(m_proxyModel, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(testMappings())); 0421 disconnect(m_proxyModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(testMappings())); 0422 disconnect(m_proxyModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(testMappings())); 0423 disconnect(m_proxyModel, SIGNAL(layoutAboutToBeChanged()), this, SLOT(testMappings())); 0424 disconnect(m_proxyModel, SIGNAL(layoutChanged()), this, SLOT(testMappings())); 0425 disconnect(m_proxyModel, SIGNAL(rowsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)), this, SLOT(testMappings())); 0426 disconnect(m_proxyModel, SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)), this, SLOT(testMappings())); 0427 disconnect(m_proxyModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(testMappings())); 0428 0429 disconnect(m_proxyModel, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), this, SLOT(verifyModel(QModelIndex, int, int))); 0430 disconnect(m_proxyModel, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(verifyModel(QModelIndex, int, int))); 0431 disconnect(m_proxyModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(verifyModel(QModelIndex, int, int))); 0432 disconnect(m_proxyModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(verifyModel(QModelIndex, int, int))); 0433 disconnect(m_proxyModel, 0434 SIGNAL(rowsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)), 0435 this, 0436 SLOT(verifyModel(QModelIndex, int, int, QModelIndex, int))); 0437 disconnect(m_proxyModel, SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)), this, SLOT(verifyModel(QModelIndex, int, int, QModelIndex, int))); 0438 disconnect(m_proxyModel, SIGNAL(columnsAboutToBeInserted(QModelIndex, int, int)), this, SLOT(verifyModel(QModelIndex, int, int))); 0439 disconnect(m_proxyModel, SIGNAL(columnsInserted(QModelIndex, int, int)), this, SLOT(verifyModel(QModelIndex, int, int))); 0440 disconnect(m_proxyModel, SIGNAL(columnsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(verifyModel(QModelIndex, int, int))); 0441 disconnect(m_proxyModel, SIGNAL(columnsRemoved(QModelIndex, int, int)), this, SLOT(verifyModel(QModelIndex, int, int))); 0442 disconnect(m_proxyModel, 0443 SIGNAL(columnsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)), 0444 this, 0445 SLOT(verifyModel(QModelIndex, int, int, QModelIndex, int))); 0446 disconnect(m_proxyModel, 0447 SIGNAL(columnsMoved(QModelIndex, int, int, QModelIndex, int)), 0448 this, 0449 SLOT(verifyModel(QModelIndex, int, int, QModelIndex, int))); 0450 disconnect(m_proxyModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(verifyModel(QModelIndex, QModelIndex))); 0451 } 0452 0453 m_proxyModel = proxyModel; 0454 QVERIFY(m_modelSpy->isEmpty()); 0455 m_modelSpy->setModel(m_proxyModel); 0456 0457 QVERIFY(m_modelSpy->isEmpty()); 0458 0459 m_modelSpy->startSpying(); 0460 m_proxyModel->setSourceModel(m_sourceModel); 0461 m_modelSpy->stopSpying(); 0462 0463 QVERIFY(m_modelSpy->size() == 2); 0464 QVERIFY(m_modelSpy->takeFirst().at(0) == ModelAboutToBeReset); 0465 QVERIFY(m_modelSpy->takeFirst().at(0) == ModelReset); 0466 QVERIFY(m_modelSpy->isEmpty()); 0467 testMappings(); 0468 0469 connect(m_proxyModel, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), SLOT(testMappings())); 0470 connect(m_proxyModel, SIGNAL(rowsInserted(QModelIndex, int, int)), SLOT(testMappings())); 0471 connect(m_proxyModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), SLOT(testMappings())); 0472 connect(m_proxyModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), SLOT(testMappings())); 0473 connect(m_proxyModel, SIGNAL(layoutAboutToBeChanged()), SLOT(testMappings())); 0474 connect(m_proxyModel, SIGNAL(layoutChanged()), SLOT(testMappings())); 0475 connect(m_proxyModel, SIGNAL(rowsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)), SLOT(testMappings())); 0476 connect(m_proxyModel, SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)), SLOT(testMappings())); 0477 connect(m_proxyModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), SLOT(testMappings())); 0478 0479 connect(m_proxyModel, SIGNAL(rowsAboutToBeInserted(QModelIndex, int, int)), SLOT(verifyModel(QModelIndex, int, int))); 0480 connect(m_proxyModel, SIGNAL(rowsInserted(QModelIndex, int, int)), SLOT(verifyModel(QModelIndex, int, int))); 0481 connect(m_proxyModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), SLOT(verifyModel(QModelIndex, int, int))); 0482 connect(m_proxyModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), SLOT(verifyModel(QModelIndex, int, int))); 0483 connect(m_proxyModel, SIGNAL(rowsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)), SLOT(verifyModel(QModelIndex, int, int, QModelIndex, int))); 0484 connect(m_proxyModel, SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)), SLOT(verifyModel(QModelIndex, int, int, QModelIndex, int))); 0485 connect(m_proxyModel, SIGNAL(columnsAboutToBeInserted(QModelIndex, int, int)), SLOT(verifyModel(QModelIndex, int, int))); 0486 connect(m_proxyModel, SIGNAL(columnsInserted(QModelIndex, int, int)), SLOT(verifyModel(QModelIndex, int, int))); 0487 connect(m_proxyModel, SIGNAL(columnsAboutToBeRemoved(QModelIndex, int, int)), SLOT(verifyModel(QModelIndex, int, int))); 0488 connect(m_proxyModel, SIGNAL(columnsRemoved(QModelIndex, int, int)), SLOT(verifyModel(QModelIndex, int, int))); 0489 connect(m_proxyModel, SIGNAL(columnsAboutToBeMoved(QModelIndex, int, int, QModelIndex, int)), SLOT(verifyModel(QModelIndex, int, int, QModelIndex, int))); 0490 connect(m_proxyModel, SIGNAL(columnsMoved(QModelIndex, int, int, QModelIndex, int)), SLOT(verifyModel(QModelIndex, int, int, QModelIndex, int))); 0491 connect(m_proxyModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), SLOT(verifyModel(QModelIndex, QModelIndex))); 0492 } 0493 0494 void ProxyModelTest::doTest() 0495 { 0496 QFETCH(SignalList, signalList); 0497 QFETCH(PersistentChangeList, changeList); 0498 0499 QVERIFY(m_modelSpy->isEmpty()); 0500 0501 QString testName = QTest::currentTestFunction(); 0502 QString testDataTag = QTest::currentDataTag(); 0503 0504 if (testDataTag == ProxyModelTestData::failTag()) { 0505 return; 0506 } 0507 0508 if ((signalList.size() == 1 && signalList.first().size() == 1) && signalList.first().first().toString() == QLatin1String("skip")) { 0509 return; 0510 } 0511 0512 static int numTests = 0; 0513 if (qApp->arguments().contains(QLatin1String("-count"))) { 0514 qDebug() << "numTests" << ++numTests; 0515 } 0516 0517 m_modelSpy->preTestPersistIndexes(changeList); 0518 0519 // Run the test. 0520 0521 Q_ASSERT(m_modelSpy->isEmpty()); 0522 m_modelSpy->startSpying(); 0523 QMetaObject::invokeMethod(m_modelCommander, QString("execute_" + testName).toLatin1(), Q_ARG(QString, testDataTag)); 0524 m_modelSpy->stopSpying(); 0525 0526 if (modelSpy()->isEmpty()) { 0527 QVERIFY(signalList.isEmpty()); 0528 } 0529 0530 // Make sure we didn't get any signals we didn't expect. 0531 if (signalList.isEmpty()) { 0532 QVERIFY(modelSpy()->isEmpty()); 0533 } 0534 0535 const bool isLayoutChange = signalList.contains(QVariantList() << LayoutAboutToBeChanged); 0536 0537 while (!signalList.isEmpty()) { 0538 // Process each signal we received as a result of running the test. 0539 QVariantList expected = signalList.takeAt(0); 0540 handleSignal(expected); 0541 } 0542 0543 // Make sure we didn't get any signals we didn't expect. 0544 QVERIFY(m_modelSpy->isEmpty()); 0545 0546 // Persistent indexes should change by the amount described in change objects. 0547 const auto lst = m_modelSpy->getChangeList(); 0548 for (PersistentIndexChange change : lst) { 0549 for (int i = 0; i < change.indexes.size(); i++) { 0550 QModelIndex idx = change.indexes.at(i); 0551 QPersistentModelIndex persistentIndex = change.persistentIndexes.at(i); 0552 0553 // Persistent indexes go to an invalid state if they are removed from the model. 0554 if (change.toInvalid) { 0555 QVERIFY(!persistentIndex.isValid()); 0556 continue; 0557 } 0558 #if 0 0559 qDebug() << idx << idx.data() << change.difference << change.toInvalid << persistentIndex.row(); 0560 #endif 0561 0562 QCOMPARE(idx.row() + change.difference, persistentIndex.row()); 0563 QCOMPARE(idx.column(), persistentIndex.column()); 0564 if (!isLayoutChange) { 0565 QCOMPARE(idx.parent(), persistentIndex.parent()); 0566 } 0567 } 0568 0569 for (int i = 0; i < change.descendantIndexes.size(); i++) { 0570 QModelIndex idx = change.descendantIndexes.at(i); 0571 QPersistentModelIndex persistentIndex = change.persistentDescendantIndexes.at(i); 0572 0573 // The descendant indexes of indexes which were removed should now also be invalid. 0574 if (change.toInvalid) { 0575 QVERIFY(!persistentIndex.isValid()); 0576 continue; 0577 } 0578 // Otherwise they should be unchanged. 0579 QCOMPARE(idx.row(), persistentIndex.row()); 0580 QCOMPARE(idx.column(), persistentIndex.column()); 0581 if (!isLayoutChange) { 0582 QCOMPARE(idx.parent(), persistentIndex.parent()); 0583 } 0584 } 0585 } 0586 0587 QModelIndexList unchangedIndexes = m_modelSpy->getUnchangedIndexes(); 0588 QList<QPersistentModelIndex> unchangedPersistentIndexes = m_modelSpy->getUnchangedPersistentIndexes(); 0589 0590 // Indexes unaffected by the signals should be unchanged. 0591 for (int i = 0; i < unchangedIndexes.size(); ++i) { 0592 QModelIndex unchangedIdx = unchangedIndexes.at(i); 0593 QPersistentModelIndex persistentIndex = unchangedPersistentIndexes.at(i); 0594 QCOMPARE(unchangedIdx.row(), persistentIndex.row()); 0595 QCOMPARE(unchangedIdx.column(), persistentIndex.column()); 0596 if (!isLayoutChange) { 0597 QCOMPARE(unchangedIdx.parent(), persistentIndex.parent()); 0598 } 0599 } 0600 m_modelSpy->clearTestData(); 0601 } 0602 0603 void ProxyModelTest::connectTestSignals(QObject *receiver) 0604 { 0605 if (!receiver) { 0606 return; 0607 } 0608 for (int methodIndex = 0; methodIndex < metaObject()->methodCount(); ++methodIndex) { 0609 QMetaMethod mm = metaObject()->method(methodIndex); 0610 if (mm.methodType() == QMetaMethod::Signal && QString(mm.methodSignature()).startsWith(QLatin1String("test")) 0611 && QString(mm.methodSignature()).endsWith(QLatin1String("Data()"))) { 0612 int slotIndex = receiver->metaObject()->indexOfSlot(mm.methodSignature()); 0613 Q_ASSERT(slotIndex >= 0); 0614 metaObject()->connect(this, methodIndex, receiver, slotIndex); 0615 } 0616 } 0617 } 0618 0619 void ProxyModelTest::disconnectTestSignals(QObject *receiver) 0620 { 0621 if (!receiver) { 0622 return; 0623 } 0624 for (int methodIndex = 0; methodIndex < metaObject()->methodCount(); ++methodIndex) { 0625 QMetaMethod mm = metaObject()->method(methodIndex); 0626 if (mm.methodType() == QMetaMethod::Signal && QString(mm.methodSignature()).startsWith(QLatin1String("test")) 0627 && QString(mm.methodSignature()).endsWith(QLatin1String("Data()"))) { 0628 int slotIndex = receiver->metaObject()->indexOfSlot(mm.methodSignature()); 0629 Q_ASSERT(slotIndex >= 0); 0630 metaObject()->disconnect(this, methodIndex, receiver, slotIndex); 0631 } 0632 } 0633 } 0634 0635 uint qHash(const QVariant &var) 0636 { 0637 if (!var.isValid() || var.isNull()) { 0638 return -1; 0639 } 0640 0641 switch (var.type()) { 0642 case QVariant::Int: 0643 return qHash(var.toInt()); 0644 case QVariant::UInt: 0645 return qHash(var.toUInt()); 0646 case QVariant::Bool: 0647 return qHash(var.toUInt()); 0648 case QVariant::Double: 0649 return qHash(var.toUInt()); 0650 case QVariant::LongLong: 0651 return qHash(var.toLongLong()); 0652 case QVariant::ULongLong: 0653 return qHash(var.toULongLong()); 0654 case QVariant::String: 0655 return qHash(var.toString()); 0656 case QVariant::Char: 0657 return qHash(var.toChar()); 0658 case QVariant::StringList: 0659 return qHash(var.toString()); 0660 case QVariant::ByteArray: 0661 return qHash(var.toByteArray()); 0662 case QVariant::Date: 0663 case QVariant::Time: 0664 case QVariant::DateTime: 0665 case QVariant::Url: 0666 case QVariant::Locale: 0667 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0668 case QVariant::RegExp: 0669 #endif 0670 return qHash(var.toString()); 0671 case QVariant::Map: 0672 case QVariant::List: 0673 case QVariant::BitArray: 0674 case QVariant::Size: 0675 case QVariant::SizeF: 0676 case QVariant::Rect: 0677 case QVariant::LineF: 0678 case QVariant::Line: 0679 case QVariant::RectF: 0680 case QVariant::Point: 0681 case QVariant::PointF: 0682 // not supported yet 0683 break; 0684 case QVariant::UserType: 0685 case QVariant::Invalid: 0686 default: 0687 return -1; 0688 } 0689 0690 // could not generate a hash for the given variant 0691 Q_ASSERT(0); 0692 return -1; 0693 } 0694 0695 #include "moc_proxymodeltest.cpp"