File indexing completed on 2025-02-09 04:25:04
0001 /* 0002 SPDX-FileCopyrightText: 2016 Sune Vuorela <sune@debian.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #include "kdescendantsproxymodel.h" 0008 0009 #include <QAbstractListModel> 0010 #include <QSignalSpy> 0011 #include <QStandardItemModel> 0012 #include <QTest> 0013 0014 struct Node { 0015 ~Node() 0016 { 0017 qDeleteAll(children); 0018 } 0019 0020 QString label; 0021 Node *parent = nullptr; 0022 QList<Node *> children; 0023 int knownChildren = 0; 0024 }; 0025 0026 class SimpleObjectModel : public QAbstractListModel 0027 { 0028 Q_OBJECT 0029 public: 0030 explicit SimpleObjectModel(QObject *parent = nullptr, bool incremental = false); 0031 ~SimpleObjectModel() override; 0032 0033 QModelIndex index(int, int, const QModelIndex &parent = QModelIndex()) const override; 0034 QModelIndex parent(const QModelIndex &) const override; 0035 int rowCount(const QModelIndex &parent = QModelIndex()) const override; 0036 bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; 0037 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 0038 0039 bool insert(const QModelIndex &parent, int row, const QString &text); 0040 bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; 0041 bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationRow) override; 0042 0043 Node *getRootNode() const 0044 { 0045 return m_root; 0046 } 0047 0048 private: 0049 Node *m_root; 0050 // simulate a model that loads in new rows with fetchMore() 0051 bool m_incremental; 0052 }; 0053 0054 SimpleObjectModel::SimpleObjectModel(QObject *parent, bool incremental) 0055 : QAbstractListModel(parent) 0056 , m_incremental(incremental) 0057 { 0058 m_root = new Node; 0059 } 0060 0061 SimpleObjectModel::~SimpleObjectModel() 0062 { 0063 delete m_root; 0064 } 0065 0066 QModelIndex SimpleObjectModel::index(int row, int col, const QModelIndex &parent) const 0067 { 0068 Node *parentItem; 0069 if (!parent.isValid()) { 0070 parentItem = static_cast<Node *>(m_root); 0071 } else { 0072 parentItem = static_cast<Node *>(parent.internalPointer()); 0073 } 0074 0075 if (row < 0 || parentItem->children.size() <= row) { 0076 return QModelIndex(); 0077 } 0078 Node *childItem = parentItem->children[row]; 0079 0080 return createIndex(row, col, childItem); 0081 } 0082 0083 QModelIndex SimpleObjectModel::parent(const QModelIndex &index) const 0084 { 0085 Node *childItem = static_cast<Node *>(index.internalPointer()); 0086 if (!childItem) { 0087 return QModelIndex(); 0088 } 0089 0090 Node *parent = childItem->parent; 0091 Node *grandParent = parent->parent; 0092 0093 int childRow = 0; 0094 if (grandParent) { 0095 childRow = grandParent->children.indexOf(parent); 0096 } 0097 0098 if (parent == m_root) { 0099 return QModelIndex(); 0100 } 0101 return createIndex(childRow, 0, parent); 0102 } 0103 0104 int SimpleObjectModel::rowCount(const QModelIndex &index) const 0105 { 0106 Node *item = static_cast<Node *>(index.internalPointer()); 0107 if (!item) { 0108 item = m_root; 0109 } 0110 0111 if (m_incremental) { 0112 return item->knownChildren; 0113 } 0114 return item->children.count(); 0115 } 0116 0117 bool SimpleObjectModel::hasChildren(const QModelIndex &index) const 0118 { 0119 Node *item = static_cast<Node *>(index.internalPointer()); 0120 if (!item) { 0121 item = m_root; 0122 } 0123 0124 return !item->children.isEmpty(); 0125 } 0126 0127 QVariant SimpleObjectModel::data(const QModelIndex &index, int role) const 0128 { 0129 if (role != Qt::DisplayRole) { 0130 return QVariant(); 0131 } 0132 0133 Node *node = static_cast<Node *>(index.internalPointer()); 0134 if (!node) { 0135 return QVariant(); 0136 } 0137 return node->label; 0138 } 0139 0140 bool SimpleObjectModel::insert(const QModelIndex &index, int row, const QString &text) 0141 { 0142 if (row < 0) { 0143 return false; 0144 } 0145 0146 Node *parent = static_cast<Node *>(index.internalPointer()); 0147 if (!parent) { 0148 parent = m_root; 0149 } 0150 0151 if (row > parent->children.count()) { 0152 return false; 0153 } 0154 0155 beginInsertRows(index, row, row); 0156 Node *child = new Node; 0157 child->parent = parent; 0158 child->label = text; 0159 parent->children.insert(row, child); 0160 endInsertRows(); 0161 0162 return true; 0163 } 0164 0165 bool SimpleObjectModel::removeRows(int row, int count, const QModelIndex &index) 0166 { 0167 if (row < 0) { 0168 return false; 0169 } 0170 0171 Node *parent = static_cast<Node *>(index.internalPointer()); 0172 if (!parent) { 0173 parent = m_root; 0174 } 0175 0176 const int last = row + count - 1; 0177 0178 if (last >= parent->children.count()) { 0179 return false; 0180 } 0181 0182 beginRemoveRows(index, row, last); 0183 for (int i = last; i >= row; i--) { 0184 Node *child = parent->children.takeAt(i); 0185 delete child; 0186 } 0187 endRemoveRows(); 0188 0189 return true; 0190 } 0191 0192 bool SimpleObjectModel::moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationRow) 0193 { 0194 Node *sourceNode = static_cast<Node *>(sourceParent.internalPointer()); 0195 if (!sourceNode) { 0196 sourceNode = m_root; 0197 } 0198 Node *destinationNode = static_cast<Node *>(destinationParent.internalPointer()); 0199 if (!destinationNode) { 0200 destinationNode = m_root; 0201 } 0202 0203 const int sourceLast = sourceRow + count - 1; 0204 0205 if (sourceNode != destinationNode) { 0206 if (count <= 0 || sourceRow < 0 || sourceRow >= sourceNode->children.count() || destinationRow < 0 0207 || destinationRow > destinationNode->children.count()) { 0208 return false; 0209 } 0210 0211 if (!beginMoveRows(sourceParent, sourceRow, sourceLast, destinationParent, destinationRow)) { 0212 return false; 0213 } 0214 0215 Node *child = sourceNode->children.takeAt(sourceRow); 0216 child->parent = destinationNode; 0217 destinationNode->children.insert(destinationRow, child); 0218 0219 endMoveRows(); 0220 return true; 0221 } 0222 0223 if (count <= 0 || sourceRow == destinationRow || sourceRow < 0 || sourceRow >= destinationNode->children.count() || destinationRow < 0 0224 || destinationRow > destinationNode->children.count() || count - destinationRow > destinationNode->children.count() - sourceRow) { 0225 return false; 0226 } 0227 0228 // beginMoveRows wants indexes before the source rows are removed from the old order 0229 if (!beginMoveRows(sourceParent, sourceRow, sourceLast, destinationParent, destinationRow)) { 0230 return false; 0231 } 0232 0233 if (sourceRow < destinationRow) { 0234 for (int i = count - 1; i >= 0; --i) { 0235 destinationNode->children.move(sourceRow + i, destinationRow - count + i); 0236 } 0237 } else { 0238 for (int i = 0; i < count; ++i) { 0239 destinationNode->children.move(sourceRow + i, destinationRow + i); 0240 } 0241 } 0242 0243 endMoveRows(); 0244 return true; 0245 } 0246 0247 class tst_KDescendantProxyModel : public QObject 0248 { 0249 Q_OBJECT 0250 QAbstractItemModel *createTree(const QString &prefix) 0251 { 0252 /* 0253 * |- parent1 0254 * | |- child1 0255 * | `- child2 0256 * `- parent2 0257 * |- child1 0258 * `- child2 0259 */ 0260 QStandardItemModel *model = new QStandardItemModel(this); 0261 for (int i = 0; i < 2; i++) { 0262 QStandardItem *item = new QStandardItem(); 0263 item->setData(QString(prefix + QString::number(i)), Qt::DisplayRole); 0264 for (int j = 0; j < 2; j++) { 0265 QStandardItem *child = new QStandardItem(); 0266 child->setData(QString(prefix + QString::number(i) + "-" + QString::number(j)), Qt::DisplayRole); 0267 item->appendRow(child); 0268 } 0269 model->appendRow(item); 0270 } 0271 return model; 0272 } 0273 0274 private Q_SLOTS: 0275 void testResetModelContent(); 0276 void testChangeSeparator(); 0277 void testChangeInvisibleSeparator(); 0278 void testRemoveSeparator(); 0279 0280 void testResetCollapsedModelContent(); 0281 void testInsertInCollapsedModel(); 0282 void testRemoveInCollapsedModel(); 0283 void testMoveInsideCollapsed(); 0284 void testExpandInsideCollapsed(); 0285 void testEmptyModel(); 0286 void testEmptyChild(); 0287 }; 0288 0289 /// Tests that replacing the source model results in data getting changed 0290 void tst_KDescendantProxyModel::testResetModelContent() 0291 { 0292 auto model1 = createTree("FirstModel"); 0293 KDescendantsProxyModel proxy; 0294 proxy.setSourceModel(model1); 0295 QCOMPARE(proxy.rowCount(), 6); 0296 0297 { 0298 QStringList results = QStringList() << "FirstModel0" 0299 << "FirstModel0-0" 0300 << "FirstModel0-1" 0301 << "FirstModel1" 0302 << "FirstModel1-0" 0303 << "FirstModel1-1"; 0304 QCOMPARE(proxy.rowCount(), results.count()); 0305 for (int i = 0; i < proxy.rowCount(); i++) { 0306 QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); 0307 } 0308 } 0309 auto model2 = createTree("SecondModel"); 0310 { 0311 proxy.setSourceModel(model2); 0312 QStringList results = QStringList() << "SecondModel0" 0313 << "SecondModel0-0" 0314 << "SecondModel0-1" 0315 << "SecondModel1" 0316 << "SecondModel1-0" 0317 << "SecondModel1-1"; 0318 QCOMPARE(proxy.rowCount(), results.count()); 0319 for (int i = 0; i < proxy.rowCount(); i++) { 0320 QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); 0321 } 0322 } 0323 0324 delete model2; 0325 delete model1; 0326 } 0327 0328 /// tests that change separator works, as well as emits the relevant data changed signals 0329 void tst_KDescendantProxyModel::testChangeSeparator() 0330 { 0331 auto model1 = createTree("FirstModel"); 0332 KDescendantsProxyModel proxy; 0333 proxy.setSourceModel(model1); 0334 proxy.setDisplayAncestorData(true); 0335 QSignalSpy dataChangedSpy(&proxy, &QAbstractItemModel::dataChanged); 0336 QCOMPARE(proxy.rowCount(), 6); 0337 { 0338 QStringList results = QStringList() << "FirstModel0" 0339 << "FirstModel0 / FirstModel0-0" 0340 << "FirstModel0 / FirstModel0-1" 0341 << "FirstModel1" 0342 << "FirstModel1 / FirstModel1-0" 0343 << "FirstModel1 / FirstModel1-1"; 0344 QCOMPARE(proxy.rowCount(), results.count()); 0345 for (int i = 0; i < proxy.rowCount(); i++) { 0346 QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); 0347 } 0348 } 0349 proxy.setAncestorSeparator("LOL"); 0350 QCOMPARE(dataChangedSpy.count(), 1); 0351 { 0352 QStringList results = QStringList() << "FirstModel0" 0353 << "FirstModel0LOLFirstModel0-0" 0354 << "FirstModel0LOLFirstModel0-1" 0355 << "FirstModel1" 0356 << "FirstModel1LOLFirstModel1-0" 0357 << "FirstModel1LOLFirstModel1-1"; 0358 QCOMPARE(proxy.rowCount(), results.count()); 0359 for (int i = 0; i < proxy.rowCount(); i++) { 0360 QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); 0361 } 0362 } 0363 0364 delete model1; 0365 } 0366 0367 /// tests that change separator that is not shown does not change the content and does not 0368 /// emit data changed signals, since the data isn't changed 0369 void tst_KDescendantProxyModel::testChangeInvisibleSeparator() 0370 { 0371 auto model1 = createTree("FirstModel"); 0372 KDescendantsProxyModel proxy; 0373 proxy.setSourceModel(model1); 0374 QSignalSpy dataChangedSpy(&proxy, &QAbstractItemModel::dataChanged); 0375 QCOMPARE(proxy.rowCount(), 6); 0376 { 0377 QStringList results = QStringList() << "FirstModel0" 0378 << "FirstModel0-0" 0379 << "FirstModel0-1" 0380 << "FirstModel1" 0381 << "FirstModel1-0" 0382 << "FirstModel1-1"; 0383 QCOMPARE(proxy.rowCount(), results.count()); 0384 for (int i = 0; i < proxy.rowCount(); i++) { 0385 QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); 0386 } 0387 } 0388 proxy.setAncestorSeparator("LOL"); 0389 QCOMPARE(dataChangedSpy.count(), 0); 0390 { 0391 QStringList results = QStringList() << "FirstModel0" 0392 << "FirstModel0-0" 0393 << "FirstModel0-1" 0394 << "FirstModel1" 0395 << "FirstModel1-0" 0396 << "FirstModel1-1"; 0397 QCOMPARE(proxy.rowCount(), results.count()); 0398 for (int i = 0; i < proxy.rowCount(); i++) { 0399 QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); 0400 } 0401 } 0402 0403 delete model1; 0404 } 0405 0406 /// tests that data is properly updated when separator is removed/hidden 0407 /// and data changed signal is emitted 0408 void tst_KDescendantProxyModel::testRemoveSeparator() 0409 { 0410 auto model1 = createTree("FirstModel"); 0411 KDescendantsProxyModel proxy; 0412 proxy.setSourceModel(model1); 0413 QSignalSpy dataChangedSpy(&proxy, &QAbstractItemModel::dataChanged); 0414 proxy.setDisplayAncestorData(true); 0415 QCOMPARE(dataChangedSpy.count(), 1); 0416 dataChangedSpy.clear(); 0417 QCOMPARE(proxy.rowCount(), 6); 0418 { 0419 QStringList results = QStringList() << "FirstModel0" 0420 << "FirstModel0 / FirstModel0-0" 0421 << "FirstModel0 / FirstModel0-1" 0422 << "FirstModel1" 0423 << "FirstModel1 / FirstModel1-0" 0424 << "FirstModel1 / FirstModel1-1"; 0425 QCOMPARE(proxy.rowCount(), results.count()); 0426 for (int i = 0; i < proxy.rowCount(); i++) { 0427 QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); 0428 } 0429 } 0430 proxy.setDisplayAncestorData(false); 0431 QCOMPARE(dataChangedSpy.count(), 1); 0432 { 0433 QStringList results = QStringList() << "FirstModel0" 0434 << "FirstModel0-0" 0435 << "FirstModel0-1" 0436 << "FirstModel1" 0437 << "FirstModel1-0" 0438 << "FirstModel1-1"; 0439 QCOMPARE(proxy.rowCount(), results.count()); 0440 for (int i = 0; i < proxy.rowCount(); i++) { 0441 QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); 0442 } 0443 } 0444 } 0445 0446 void tst_KDescendantProxyModel::testResetCollapsedModelContent() 0447 { 0448 auto model1 = createTree("FirstModel"); 0449 KDescendantsProxyModel proxy; 0450 proxy.setExpandsByDefault(false); 0451 proxy.setSourceModel(model1); 0452 QCOMPARE(proxy.rowCount(), 2); 0453 0454 { 0455 QStringList results = QStringList() << "FirstModel0" 0456 << "FirstModel1"; 0457 QCOMPARE(proxy.rowCount(), results.count()); 0458 for (int i = 0; i < proxy.rowCount(); i++) { 0459 QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); 0460 } 0461 } 0462 { 0463 QModelIndex idx = model1->index(0, 0); 0464 proxy.expandSourceIndex(idx); 0465 QStringList results = QStringList() << "FirstModel0" 0466 << "FirstModel0-0" 0467 << "FirstModel0-1" 0468 << "FirstModel1"; 0469 QCOMPARE(proxy.rowCount(), results.count()); 0470 for (int i = 0; i < proxy.rowCount(); i++) { 0471 QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); 0472 } 0473 } 0474 { 0475 QModelIndex idx = model1->index(1, 0); 0476 proxy.expandSourceIndex(idx); 0477 QStringList results = QStringList() << "FirstModel0" 0478 << "FirstModel0-0" 0479 << "FirstModel0-1" 0480 << "FirstModel1" 0481 << "FirstModel1-0" 0482 << "FirstModel1-1"; 0483 QCOMPARE(proxy.rowCount(), results.count()); 0484 for (int i = 0; i < proxy.rowCount(); i++) { 0485 QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); 0486 } 0487 } 0488 0489 auto model2 = createTree("SecondModel"); 0490 { 0491 proxy.setSourceModel(model2); 0492 QModelIndex idx = model2->index(0, 0); 0493 proxy.expandSourceIndex(idx); 0494 idx = model2->index(1, 0); 0495 proxy.expandSourceIndex(idx); 0496 QStringList results = QStringList() << "SecondModel0" 0497 << "SecondModel0-0" 0498 << "SecondModel0-1" 0499 << "SecondModel1" 0500 << "SecondModel1-0" 0501 << "SecondModel1-1"; 0502 QCOMPARE(proxy.rowCount(), results.count()); 0503 for (int i = 0; i < proxy.rowCount(); i++) { 0504 QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); 0505 } 0506 } 0507 0508 delete model2; 0509 delete model1; 0510 } 0511 0512 void tst_KDescendantProxyModel::testInsertInCollapsedModel() 0513 { 0514 QStandardItemModel *model1 = static_cast<QStandardItemModel *>(createTree("Model")); 0515 KDescendantsProxyModel proxy; 0516 proxy.setExpandsByDefault(false); 0517 proxy.setSourceModel(model1); 0518 QCOMPARE(proxy.rowCount(), 2); 0519 0520 QSignalSpy insertSpy(&proxy, &QAbstractItemModel::rowsInserted); 0521 QCOMPARE(insertSpy.count(), 0); 0522 0523 QStandardItem *parent = model1->item(0, 0); 0524 QVERIFY(parent); 0525 0526 QStandardItem *child = new QStandardItem(); 0527 child->setData(QString(QStringLiteral("Model") + QString::number(0) + "-" + QString::number(2)), Qt::DisplayRole); 0528 parent->appendRow(child); 0529 0530 // Adding a child to the collapsed parent doesn't have an effect to the proxy 0531 QCOMPARE(proxy.rowCount(), 2); 0532 QCOMPARE(insertSpy.count(), 0); 0533 0534 // If we expand everything inserted should be here 0535 QModelIndex idx = model1->index(0, 0); 0536 proxy.expandSourceIndex(idx); 0537 0538 QCOMPARE(proxy.rowCount(), 5); 0539 QCOMPARE(insertSpy.count(), 1); 0540 0541 QCOMPARE(proxy.index(3, 0).data(Qt::DisplayRole).toString(), QStringLiteral("Model0-2")); 0542 0543 // Add another child to the expanded node, now the proxy is affected immediately 0544 child = new QStandardItem(); 0545 child->setData(QString(QStringLiteral("Model") + QString::number(0) + "-" + QString::number(3)), Qt::DisplayRole); 0546 parent->appendRow(child); 0547 0548 QCOMPARE(proxy.rowCount(), 6); 0549 QCOMPARE(insertSpy.count(), 2); 0550 } 0551 0552 void tst_KDescendantProxyModel::testRemoveInCollapsedModel() 0553 { 0554 QStandardItemModel *model1 = static_cast<QStandardItemModel *>(createTree("Model")); 0555 KDescendantsProxyModel proxy; 0556 proxy.setExpandsByDefault(false); 0557 proxy.setSourceModel(model1); 0558 QCOMPARE(proxy.rowCount(), 2); 0559 0560 QSignalSpy removeSpy(&proxy, &QAbstractItemModel::rowsRemoved); 0561 QCOMPARE(removeSpy.count(), 0); 0562 0563 QStandardItem *parent = model1->item(0, 0); 0564 QVERIFY(parent); 0565 0566 parent->removeRow(0); 0567 0568 // Adding a child to the collapsed parent doesn't have an effect to the proxy 0569 QCOMPARE(proxy.rowCount(), 2); 0570 QCOMPARE(removeSpy.count(), 0); 0571 0572 // If we expand everything inserted should be here 0573 QModelIndex idx = model1->index(0, 0); 0574 proxy.expandSourceIndex(idx); 0575 0576 QCOMPARE(proxy.rowCount(), 3); 0577 0578 QCOMPARE(proxy.index(1, 0).data(Qt::DisplayRole).toString(), QStringLiteral("Model0-1")); 0579 parent->removeRow(0); 0580 0581 QCOMPARE(proxy.rowCount(), 2); 0582 QCOMPARE(removeSpy.count(), 1); 0583 0584 idx = model1->index(1, 0); 0585 proxy.expandSourceIndex(idx); 0586 QCOMPARE(proxy.rowCount(), 4); 0587 } 0588 0589 void tst_KDescendantProxyModel::testMoveInsideCollapsed() 0590 { 0591 SimpleObjectModel *model = new SimpleObjectModel(this); 0592 model->insert(QModelIndex(), 0, QStringLiteral("Model0")); 0593 model->insert(QModelIndex(), 1, QStringLiteral("Model1")); 0594 model->insert(QModelIndex(), 2, QStringLiteral("Model2")); 0595 0596 model->insert(model->index(0, 0, QModelIndex()), 0, QStringLiteral("Model0-0")); 0597 model->insert(model->index(1, 0, QModelIndex()), 0, QStringLiteral("Model1-0")); 0598 0599 QCOMPARE(model->rowCount(), 3); 0600 0601 KDescendantsProxyModel proxy; 0602 proxy.setExpandsByDefault(false); 0603 proxy.setSourceModel(model); 0604 QCOMPARE(proxy.rowCount(), 3); 0605 0606 QSignalSpy removeSpy(&proxy, &QAbstractItemModel::rowsRemoved); 0607 QCOMPARE(removeSpy.count(), 0); 0608 QSignalSpy insertSpy(&proxy, &QAbstractItemModel::rowsInserted); 0609 QCOMPARE(insertSpy.count(), 0); 0610 0611 model->moveRows(QModelIndex(), 2, 1, model->index(0, 0, QModelIndex()), 1); 0612 0613 QCOMPARE(removeSpy.count(), 1); 0614 0615 QVERIFY(!removeSpy.first()[0].value<QModelIndex>().isValid()); 0616 QCOMPARE(removeSpy.first()[1].toInt(), 2); 0617 QCOMPARE(removeSpy.first()[2].toInt(), 2); 0618 0619 QCOMPARE(model->rowCount(), 2); 0620 QCOMPARE(proxy.rowCount(), 2); 0621 0622 model->moveRows(model->index(0, 0, QModelIndex()), 0, 1, QModelIndex(), 1); 0623 QCOMPARE(insertSpy.count(), 1); 0624 QCOMPARE(model->rowCount(), 3); 0625 QCOMPARE(proxy.rowCount(), 3); 0626 0627 QVERIFY(!insertSpy.first()[0].value<QModelIndex>().isValid()); 0628 QCOMPARE(insertSpy.first()[1].toInt(), 1); 0629 QCOMPARE(insertSpy.first()[2].toInt(), 1); 0630 0631 QModelIndex idx = model->index(0, 0); 0632 proxy.expandSourceIndex(idx); 0633 idx = model->index(1, 0); 0634 proxy.expandSourceIndex(idx); 0635 idx = model->index(2, 0); 0636 proxy.expandSourceIndex(idx); 0637 QStringList results = QStringList() << "Model0" 0638 << "Model2" 0639 << "Model0-0" 0640 << "Model1" 0641 << "Model1-0"; 0642 QCOMPARE(proxy.rowCount(), results.count()); 0643 for (int i = 0; i < proxy.rowCount(); i++) { 0644 QCOMPARE(proxy.index(i, 0).data(Qt::DisplayRole).toString(), results[i]); 0645 } 0646 } 0647 0648 void tst_KDescendantProxyModel::testExpandInsideCollapsed() 0649 { 0650 SimpleObjectModel *model = new SimpleObjectModel(this); 0651 model->insert(QModelIndex(), 0, QStringLiteral("Model0")); 0652 model->insert(QModelIndex(), 1, QStringLiteral("Model1")); 0653 model->insert(QModelIndex(), 2, QStringLiteral("Model2")); 0654 0655 auto parentIndex = model->index(0, 0, QModelIndex()); 0656 model->insert(parentIndex, 0, QStringLiteral("Model0-0")); 0657 auto childIndex = model->index(0, 0, parentIndex); 0658 model->insert(childIndex, 0, QStringLiteral("Model0-0-0")); 0659 0660 QCOMPARE(model->rowCount(), 3); 0661 0662 KDescendantsProxyModel proxy; 0663 proxy.setExpandsByDefault(false); 0664 proxy.setSourceModel(model); 0665 QCOMPARE(proxy.rowCount(), 3); 0666 0667 proxy.expandSourceIndex(childIndex); 0668 QVERIFY(proxy.isSourceIndexExpanded(childIndex)); 0669 QVERIFY(!proxy.isSourceIndexExpanded(parentIndex)); 0670 QCOMPARE(proxy.rowCount(), 3); 0671 proxy.expandSourceIndex(parentIndex); 0672 QCOMPARE(proxy.rowCount(), 5); 0673 } 0674 0675 void tst_KDescendantProxyModel::testEmptyModel() 0676 { 0677 SimpleObjectModel *model = new SimpleObjectModel(this, true); 0678 model->insert(QModelIndex(), 0, QStringLiteral("Row0")); 0679 model->insert(QModelIndex(), 0, QStringLiteral("Row1")); 0680 KDescendantsProxyModel proxy; 0681 proxy.setSourceModel(model); 0682 QCOMPARE(proxy.rowCount(), 0); 0683 } 0684 0685 void tst_KDescendantProxyModel::testEmptyChild() 0686 { 0687 SimpleObjectModel *model = new SimpleObjectModel(this, true); 0688 model->insert(QModelIndex(), 0, QStringLiteral("Row0")); 0689 auto parentIndex = model->index(0, 0, QModelIndex()); 0690 model->insert(parentIndex, 0, QStringLiteral("Row0-0")); 0691 model->insert(parentIndex, 0, QStringLiteral("Row0-1")); 0692 model->insert(QModelIndex(), 0, QStringLiteral("Row1")); 0693 0694 // simulate that the row count for the root node is known because it was listed 0695 model->getRootNode()->knownChildren = 2; 0696 0697 KDescendantsProxyModel proxy; 0698 proxy.setSourceModel(model); 0699 proxy.setExpandsByDefault(false); 0700 QCOMPARE(proxy.rowCount(), 2); 0701 0702 // remove the row that has children but a rowCount of 0 0703 model->removeRows(0, 1, QModelIndex()); 0704 QCOMPARE(proxy.rowCount(), 1); 0705 } 0706 0707 QTEST_MAIN(tst_KDescendantProxyModel) 0708 0709 #include "kdescendantsproxymodeltest.moc"