File indexing completed on 2024-04-21 03:56:06
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 constexpr auto verifyModelNoMove = qOverload<const QModelIndex &, int, int>(&ProxyModelTest::verifyModel); 0419 constexpr auto verifyModelMove = qOverload<const QModelIndex &, int, int, const QModelIndex &, int>(&ProxyModelTest::verifyModel); 0420 constexpr auto verifyModelDataChange = qOverload<const QModelIndex &, const QModelIndex &>(&ProxyModelTest::verifyModel); 0421 0422 if (m_proxyModel) { 0423 disconnect(m_proxyModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &ProxyModelTest::testMappings); 0424 disconnect(m_proxyModel, &QAbstractItemModel::rowsInserted, this, &ProxyModelTest::testMappings); 0425 disconnect(m_proxyModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ProxyModelTest::testMappings); 0426 disconnect(m_proxyModel, &QAbstractItemModel::rowsRemoved, this, &ProxyModelTest::testMappings); 0427 disconnect(m_proxyModel, &QAbstractItemModel::layoutAboutToBeChanged, this, &ProxyModelTest::testMappings); 0428 disconnect(m_proxyModel, &QAbstractItemModel::layoutChanged, this, &ProxyModelTest::testMappings); 0429 disconnect(m_proxyModel, &QAbstractItemModel::rowsAboutToBeMoved, this, &ProxyModelTest::testMappings); 0430 disconnect(m_proxyModel, &QAbstractItemModel::rowsMoved, this, &ProxyModelTest::testMappings); 0431 disconnect(m_proxyModel, &QAbstractItemModel::dataChanged, this, &ProxyModelTest::testMappings); 0432 0433 disconnect(m_proxyModel, &QAbstractItemModel::rowsAboutToBeInserted, this, verifyModelNoMove); 0434 disconnect(m_proxyModel, &QAbstractItemModel::rowsInserted, this, verifyModelNoMove); 0435 disconnect(m_proxyModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, verifyModelNoMove); 0436 disconnect(m_proxyModel, &QAbstractItemModel::rowsRemoved, this, verifyModelNoMove); 0437 disconnect(m_proxyModel, &QAbstractItemModel::rowsAboutToBeMoved, this, verifyModelMove); 0438 disconnect(m_proxyModel, &QAbstractItemModel::rowsMoved, this, verifyModelMove); 0439 disconnect(m_proxyModel, &QAbstractItemModel::columnsAboutToBeInserted, this, verifyModelNoMove); 0440 disconnect(m_proxyModel, &QAbstractItemModel::columnsInserted, this, verifyModelNoMove); 0441 disconnect(m_proxyModel, &QAbstractItemModel::columnsAboutToBeRemoved, this, verifyModelNoMove); 0442 disconnect(m_proxyModel, &QAbstractItemModel::columnsRemoved, this, verifyModelNoMove); 0443 disconnect(m_proxyModel, &QAbstractItemModel::columnsAboutToBeMoved, this, verifyModelMove); 0444 disconnect(m_proxyModel, &QAbstractItemModel::columnsMoved, this, verifyModelMove); 0445 disconnect(m_proxyModel, &QAbstractItemModel::dataChanged, this, verifyModelDataChange); 0446 } 0447 0448 m_proxyModel = proxyModel; 0449 QVERIFY(m_modelSpy->isEmpty()); 0450 m_modelSpy->setModel(m_proxyModel); 0451 0452 QVERIFY(m_modelSpy->isEmpty()); 0453 0454 m_modelSpy->startSpying(); 0455 m_proxyModel->setSourceModel(m_sourceModel); 0456 m_modelSpy->stopSpying(); 0457 0458 QVERIFY(m_modelSpy->size() == 2); 0459 QVERIFY(m_modelSpy->takeFirst().at(0) == ModelAboutToBeReset); 0460 QVERIFY(m_modelSpy->takeFirst().at(0) == ModelReset); 0461 QVERIFY(m_modelSpy->isEmpty()); 0462 testMappings(); 0463 0464 connect(m_proxyModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &ProxyModelTest::testMappings); 0465 connect(m_proxyModel, &QAbstractItemModel::rowsInserted, this, &ProxyModelTest::testMappings); 0466 connect(m_proxyModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &ProxyModelTest::testMappings); 0467 connect(m_proxyModel, &QAbstractItemModel::rowsRemoved, this, &ProxyModelTest::testMappings); 0468 connect(m_proxyModel, &QAbstractItemModel::layoutAboutToBeChanged, this, &ProxyModelTest::testMappings); 0469 connect(m_proxyModel, &QAbstractItemModel::layoutChanged, this, &ProxyModelTest::testMappings); 0470 connect(m_proxyModel, &QAbstractItemModel::rowsAboutToBeMoved, this, &ProxyModelTest::testMappings); 0471 connect(m_proxyModel, &QAbstractItemModel::rowsMoved, this, &ProxyModelTest::testMappings); 0472 connect(m_proxyModel, &QAbstractItemModel::dataChanged, this, &ProxyModelTest::testMappings); 0473 0474 connect(m_proxyModel, &QAbstractItemModel::rowsAboutToBeInserted, this, verifyModelNoMove); 0475 connect(m_proxyModel, &QAbstractItemModel::rowsInserted, this, verifyModelNoMove); 0476 connect(m_proxyModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, verifyModelNoMove); 0477 connect(m_proxyModel, &QAbstractItemModel::rowsRemoved, this, verifyModelNoMove); 0478 connect(m_proxyModel, &QAbstractItemModel::rowsAboutToBeMoved, this, verifyModelMove); 0479 connect(m_proxyModel, &QAbstractItemModel::rowsMoved, this, verifyModelMove); 0480 connect(m_proxyModel, &QAbstractItemModel::columnsAboutToBeInserted, this, verifyModelNoMove); 0481 connect(m_proxyModel, &QAbstractItemModel::columnsInserted, this, verifyModelNoMove); 0482 connect(m_proxyModel, &QAbstractItemModel::columnsAboutToBeRemoved, this, verifyModelNoMove); 0483 connect(m_proxyModel, &QAbstractItemModel::columnsRemoved, this, verifyModelNoMove); 0484 connect(m_proxyModel, &QAbstractItemModel::columnsAboutToBeMoved, this, verifyModelMove); 0485 connect(m_proxyModel, &QAbstractItemModel::columnsMoved, this, verifyModelMove); 0486 connect(m_proxyModel, &QAbstractItemModel::dataChanged, this, verifyModelDataChange); 0487 } 0488 0489 void ProxyModelTest::doTest() 0490 { 0491 QFETCH(SignalList, signalList); 0492 QFETCH(PersistentChangeList, changeList); 0493 0494 QVERIFY(m_modelSpy->isEmpty()); 0495 0496 QString testName = QTest::currentTestFunction(); 0497 QString testDataTag = QTest::currentDataTag(); 0498 0499 if (testDataTag == ProxyModelTestData::failTag()) { 0500 return; 0501 } 0502 0503 if ((signalList.size() == 1 && signalList.first().size() == 1) && signalList.first().first().toString() == QLatin1String("skip")) { 0504 return; 0505 } 0506 0507 static int numTests = 0; 0508 if (qApp->arguments().contains(QLatin1String("-count"))) { 0509 qDebug() << "numTests" << ++numTests; 0510 } 0511 0512 m_modelSpy->preTestPersistIndexes(changeList); 0513 0514 // Run the test. 0515 0516 Q_ASSERT(m_modelSpy->isEmpty()); 0517 m_modelSpy->startSpying(); 0518 QMetaObject::invokeMethod(m_modelCommander, QString("execute_" + testName).toLatin1(), Q_ARG(QString, testDataTag)); 0519 m_modelSpy->stopSpying(); 0520 0521 if (modelSpy()->isEmpty()) { 0522 QVERIFY(signalList.isEmpty()); 0523 } 0524 0525 // Make sure we didn't get any signals we didn't expect. 0526 if (signalList.isEmpty()) { 0527 QVERIFY(modelSpy()->isEmpty()); 0528 } 0529 0530 const bool isLayoutChange = signalList.contains(QVariantList() << LayoutAboutToBeChanged); 0531 0532 while (!signalList.isEmpty()) { 0533 // Process each signal we received as a result of running the test. 0534 QVariantList expected = signalList.takeAt(0); 0535 handleSignal(expected); 0536 } 0537 0538 // Make sure we didn't get any signals we didn't expect. 0539 QVERIFY(m_modelSpy->isEmpty()); 0540 0541 // Persistent indexes should change by the amount described in change objects. 0542 const auto lst = m_modelSpy->getChangeList(); 0543 for (PersistentIndexChange change : lst) { 0544 for (int i = 0; i < change.indexes.size(); i++) { 0545 QModelIndex idx = change.indexes.at(i); 0546 QPersistentModelIndex persistentIndex = change.persistentIndexes.at(i); 0547 0548 // Persistent indexes go to an invalid state if they are removed from the model. 0549 if (change.toInvalid) { 0550 QVERIFY(!persistentIndex.isValid()); 0551 continue; 0552 } 0553 #if 0 0554 qDebug() << idx << idx.data() << change.difference << change.toInvalid << persistentIndex.row(); 0555 #endif 0556 0557 QCOMPARE(idx.row() + change.difference, persistentIndex.row()); 0558 QCOMPARE(idx.column(), persistentIndex.column()); 0559 if (!isLayoutChange) { 0560 QCOMPARE(idx.parent(), persistentIndex.parent()); 0561 } 0562 } 0563 0564 for (int i = 0; i < change.descendantIndexes.size(); i++) { 0565 QModelIndex idx = change.descendantIndexes.at(i); 0566 QPersistentModelIndex persistentIndex = change.persistentDescendantIndexes.at(i); 0567 0568 // The descendant indexes of indexes which were removed should now also be invalid. 0569 if (change.toInvalid) { 0570 QVERIFY(!persistentIndex.isValid()); 0571 continue; 0572 } 0573 // Otherwise they should be unchanged. 0574 QCOMPARE(idx.row(), persistentIndex.row()); 0575 QCOMPARE(idx.column(), persistentIndex.column()); 0576 if (!isLayoutChange) { 0577 QCOMPARE(idx.parent(), persistentIndex.parent()); 0578 } 0579 } 0580 } 0581 0582 QModelIndexList unchangedIndexes = m_modelSpy->getUnchangedIndexes(); 0583 QList<QPersistentModelIndex> unchangedPersistentIndexes = m_modelSpy->getUnchangedPersistentIndexes(); 0584 0585 // Indexes unaffected by the signals should be unchanged. 0586 for (int i = 0; i < unchangedIndexes.size(); ++i) { 0587 QModelIndex unchangedIdx = unchangedIndexes.at(i); 0588 QPersistentModelIndex persistentIndex = unchangedPersistentIndexes.at(i); 0589 QCOMPARE(unchangedIdx.row(), persistentIndex.row()); 0590 QCOMPARE(unchangedIdx.column(), persistentIndex.column()); 0591 if (!isLayoutChange) { 0592 QCOMPARE(unchangedIdx.parent(), persistentIndex.parent()); 0593 } 0594 } 0595 m_modelSpy->clearTestData(); 0596 } 0597 0598 void ProxyModelTest::connectTestSignals(QObject *receiver) 0599 { 0600 if (!receiver) { 0601 return; 0602 } 0603 for (int methodIndex = 0; methodIndex < metaObject()->methodCount(); ++methodIndex) { 0604 QMetaMethod mm = metaObject()->method(methodIndex); 0605 if (mm.methodType() == QMetaMethod::Signal && QString(mm.methodSignature()).startsWith(QLatin1String("test")) 0606 && QString(mm.methodSignature()).endsWith(QLatin1String("Data()"))) { 0607 int slotIndex = receiver->metaObject()->indexOfSlot(mm.methodSignature()); 0608 Q_ASSERT(slotIndex >= 0); 0609 metaObject()->connect(this, methodIndex, receiver, slotIndex); 0610 } 0611 } 0612 } 0613 0614 void ProxyModelTest::disconnectTestSignals(QObject *receiver) 0615 { 0616 if (!receiver) { 0617 return; 0618 } 0619 for (int methodIndex = 0; methodIndex < metaObject()->methodCount(); ++methodIndex) { 0620 QMetaMethod mm = metaObject()->method(methodIndex); 0621 if (mm.methodType() == QMetaMethod::Signal && QString(mm.methodSignature()).startsWith(QLatin1String("test")) 0622 && QString(mm.methodSignature()).endsWith(QLatin1String("Data()"))) { 0623 int slotIndex = receiver->metaObject()->indexOfSlot(mm.methodSignature()); 0624 Q_ASSERT(slotIndex >= 0); 0625 metaObject()->disconnect(this, methodIndex, receiver, slotIndex); 0626 } 0627 } 0628 } 0629 0630 uint qHash(const QVariant &var) 0631 { 0632 if (!var.isValid() || var.isNull()) { 0633 return -1; 0634 } 0635 0636 switch (var.userType()) { 0637 case QMetaType::Int: 0638 return qHash(var.toInt()); 0639 case QMetaType::UInt: 0640 return qHash(var.toUInt()); 0641 case QMetaType::Bool: 0642 return qHash(var.toUInt()); 0643 case QMetaType::Double: 0644 return qHash(var.toUInt()); 0645 case QMetaType::LongLong: 0646 return qHash(var.toLongLong()); 0647 case QMetaType::ULongLong: 0648 return qHash(var.toULongLong()); 0649 case QMetaType::QString: 0650 return qHash(var.toString()); 0651 case QMetaType::Char: 0652 return qHash(var.toChar()); 0653 case QMetaType::QStringList: 0654 return qHash(var.toString()); 0655 case QMetaType::QByteArray: 0656 return qHash(var.toByteArray()); 0657 case QMetaType::QDate: 0658 case QMetaType::QTime: 0659 case QMetaType::QDateTime: 0660 case QMetaType::QUrl: 0661 case QMetaType::QLocale: 0662 return qHash(var.toString()); 0663 case QMetaType::QVariantMap: 0664 case QMetaType::QVariantList: 0665 case QMetaType::QBitArray: 0666 case QMetaType::QSize: 0667 case QMetaType::QSizeF: 0668 case QMetaType::QRect: 0669 case QMetaType::QLineF: 0670 case QMetaType::QLine: 0671 case QMetaType::QRectF: 0672 case QMetaType::QPoint: 0673 case QMetaType::QPointF: 0674 // not supported yet 0675 break; 0676 case QMetaType::User: 0677 case QMetaType::UnknownType: 0678 default: 0679 return -1; 0680 } 0681 0682 // could not generate a hash for the given variant 0683 Q_ASSERT(0); 0684 return -1; 0685 } 0686 0687 #include "moc_proxymodeltest.cpp"