File indexing completed on 2024-11-10 04:40:10
0001 /* 0002 SPDX-FileCopyrightText: 2009 Stephen Kelly <steveire@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "qtest_akonadi.h" 0008 0009 #include "entitydisplayattribute.h" 0010 #include "entitytreemodel.h" 0011 #include "entitytreemodel_p.h" 0012 #include "fakemonitor.h" 0013 #include "fakeserverdata.h" 0014 #include "fakesession.h" 0015 #include "imapparser_p.h" 0016 #include "modelspy.h" 0017 0018 static const char serverContent1[] = 0019 // The format of these lines are first a type, either 'C' or 'I' for Item and collection. 0020 // The dashes show the depth in the hierarchy 0021 // Collections have a list of mimetypes they can contain, followed by an optional 0022 // displayName which is put into the EntityDisplayAttribute, followed by an optional order 0023 // which is the order in which the collections are returned from the job to the ETM. 0024 0025 "- C (inode/directory) 'Col 1' 4" 0026 "- - C (text/directory, message/rfc822) 'Col 2' 3" 0027 // Items just have the mimetype they contain in the payload. 0028 "- - - I text/directory 'Item 1'" 0029 "- - - I text/directory 'Item 2'" 0030 "- - - I message/rfc822 'Item 3'" 0031 "- - - I message/rfc822 'Item 4'" 0032 "- - C (text/directory) 'Col 3' 3" 0033 "- - - C (text/directory) 'Col 4' 2" 0034 "- - - - C (text/directory) 'Col 5' 1" // <-- First collection to be returned 0035 "- - - - - I text/directory 'Item 5'" 0036 "- - - - - I text/directory 'Item 6'" 0037 "- - - - I text/directory 'Item 7'" 0038 "- - - I text/directory 'Item 8'" 0039 "- - - I text/directory 'Item 9'" 0040 "- - C (message/rfc822) 'Col 6' 3" 0041 "- - - I message/rfc822 'Item 10'" 0042 "- - - I message/rfc822 'Item 11'" 0043 "- - C (text/directory, message/rfc822) 'Col 7' 3" 0044 "- - - I text/directory 'Item 12'" 0045 "- - - I text/directory 'Item 13'" 0046 "- - - I message/rfc822 'Item 14'" 0047 "- - - I message/rfc822 'Item 15'"; 0048 0049 /** 0050 * This test verifies that the ETM reacts as expected to signals from the monitor. 0051 * 0052 * The tested ETM is only talking to fake components so the reaction of the ETM to each signal can be tested. 0053 * 0054 * WARNING: This test does no handle jobs issued by the model. It simply shortcuts (calls emitResult) them, and the connected 0055 * slots are never executed (because the eventloop is not run after emitResult is called). 0056 * This test therefore only tests the reaction of the model to signals of the monitor and not the overall behaviour. 0057 */ 0058 class EntityTreeModelTest : public QObject 0059 { 0060 Q_OBJECT 0061 0062 public: 0063 EntityTreeModelTest() 0064 { 0065 QHashSeed::setDeterministicGlobalSeed(); 0066 } 0067 0068 private Q_SLOTS: 0069 void initTestCase(); 0070 void cleanupTestCase(); 0071 0072 void testInitialFetch(); 0073 void testCollectionMove_data(); 0074 void testCollectionMove(); 0075 void testCollectionAdded_data(); 0076 void testCollectionAdded(); 0077 void testCollectionRemoved_data(); 0078 void testCollectionRemoved(); 0079 void testCollectionChanged_data(); 0080 void testCollectionChanged(); 0081 void testItemMove_data(); 0082 void testItemMove(); 0083 void testItemAdded_data(); 0084 void testItemAdded(); 0085 void testItemRemoved_data(); 0086 void testItemRemoved(); 0087 void testItemChanged_data(); 0088 void testItemChanged(); 0089 void testRemoveCollectionOnChanged(); 0090 0091 private: 0092 QPair<FakeServerData *, Akonadi::EntityTreeModel *> populateModel(const QString &serverContent, const QString &mimeType = QString()) 0093 { 0094 auto const fakeMonitor = new FakeMonitor(this); 0095 0096 fakeMonitor->setSession(m_fakeSession); 0097 fakeMonitor->setCollectionMonitored(Collection::root()); 0098 if (!mimeType.isEmpty()) { 0099 fakeMonitor->setMimeTypeMonitored(mimeType); 0100 } 0101 auto const model = new EntityTreeModel(fakeMonitor, this); 0102 0103 m_modelSpy = new ModelSpy{model, this}; 0104 0105 auto const serverData = new FakeServerData(model, m_fakeSession, fakeMonitor, this); 0106 serverData->setCommands(FakeJobResponse::interpret(serverData, serverContent)); 0107 0108 // Give the model a chance to populate 0109 QTest::qWait(100); 0110 return qMakePair(serverData, model); 0111 } 0112 0113 private: 0114 ModelSpy *m_modelSpy = nullptr; 0115 FakeSession *m_fakeSession = nullptr; 0116 QByteArray m_sessionName; 0117 }; 0118 0119 QModelIndex firstMatchedIndex(const QAbstractItemModel &model, const QString &pattern) 0120 { 0121 if (pattern.isEmpty()) { 0122 return {}; 0123 } 0124 const auto list = model.match(model.index(0, 0), Qt::DisplayRole, pattern, 1, Qt::MatchRecursive); 0125 Q_ASSERT(!list.isEmpty()); 0126 return list.first(); 0127 } 0128 0129 void EntityTreeModelTest::initTestCase() 0130 { 0131 m_sessionName = "EntityTreeModelTest fake session"; 0132 m_fakeSession = new FakeSession(m_sessionName, FakeSession::EndJobsImmediately); 0133 m_fakeSession->setAsDefaultSession(); 0134 0135 qRegisterMetaType<QModelIndex>("QModelIndex"); 0136 } 0137 0138 void EntityTreeModelTest::cleanupTestCase() 0139 { 0140 delete m_fakeSession; 0141 } 0142 0143 void EntityTreeModelTest::testInitialFetch() 0144 { 0145 auto const fakeMonitor = new FakeMonitor(this); 0146 0147 fakeMonitor->setSession(m_fakeSession); 0148 fakeMonitor->setCollectionMonitored(Collection::root()); 0149 auto const model = new EntityTreeModel(fakeMonitor, this); 0150 0151 auto const serverData = new FakeServerData(model, m_fakeSession, fakeMonitor, this); 0152 serverData->setCommands(FakeJobResponse::interpret(serverData, QString::fromLatin1(serverContent1))); 0153 0154 m_modelSpy = new ModelSpy(model, this); 0155 m_modelSpy->startSpying(); 0156 0157 const QList<ExpectedSignal> expectedSignals{// First the model gets a signal about the first collection to be returned, which is not a top-level collection. 0158 // It uses the parentCollection hierarchy to put placeholder collections in the model until the root is reached. 0159 // Then it inserts only one row and emits the correct signals. After that, when the other collections 0160 // arrive, dataChanged is emitted for them. 0161 {RowsAboutToBeInserted, 0, 0}, 0162 {RowsInserted, 0, 0}, 0163 {DataChanged, 0, 0, QVariantList{QStringLiteral("Col 4")}}, 0164 {DataChanged, 0, 0, QVariantList{QStringLiteral("Col 3")}}, 0165 // New collections are prepended 0166 {RowsAboutToBeInserted, 0, 0, QStringLiteral("Col 1")}, 0167 {RowsInserted, 0, 0, QStringLiteral("Col 1"), QVariantList{QStringLiteral("Col 2")}}, 0168 {RowsAboutToBeInserted, 0, 0, QStringLiteral("Col 1")}, 0169 {RowsInserted, 0, 0, QStringLiteral("Col 1"), QVariantList{QStringLiteral("Col 6")}}, 0170 {RowsAboutToBeInserted, 0, 0, QStringLiteral("Col 1")}, 0171 {RowsInserted, 0, 0, QStringLiteral("Col 1"), QVariantList{QStringLiteral("Col 7")}}, 0172 {DataChanged, 0, 0, QVariantList{QStringLiteral("Col 1")}}, 0173 // The items in the collections are appended. 0174 {RowsAboutToBeInserted, 0, 3, QStringLiteral("Col 2")}, 0175 {RowsInserted, 0, 3, QStringLiteral("Col 2")}, 0176 {RowsAboutToBeInserted, 0, 1, QStringLiteral("Col 5")}, 0177 {RowsInserted, 0, 1, QStringLiteral("Col 5")}, 0178 {RowsAboutToBeInserted, 1, 1, QStringLiteral("Col 4")}, 0179 {RowsInserted, 1, 1, QStringLiteral("Col 4")}, 0180 {RowsAboutToBeInserted, 1, 2, QStringLiteral("Col 3")}, 0181 {RowsInserted, 1, 2, QStringLiteral("Col 3")}, 0182 {RowsAboutToBeInserted, 0, 1, QStringLiteral("Col 6")}, 0183 {RowsInserted, 0, 1, QStringLiteral("Col 6")}, 0184 {RowsAboutToBeInserted, 0, 3, QStringLiteral("Col 7")}, 0185 {RowsInserted, 0, 3, QStringLiteral("Col 7")}, 0186 {DataChanged, 3, 3, QVariantList{QStringLiteral("Col 3")}}, 0187 {DataChanged, 0, 0, QVariantList{QStringLiteral("Col 1")}}, 0188 {DataChanged, 0, 0, QVariantList{QStringLiteral("Col 5")}}, 0189 {DataChanged, 0, 0, QVariantList{QStringLiteral("Col 4")}}, 0190 {DataChanged, 2, 2, QVariantList{QStringLiteral("Col 2")}}, 0191 {DataChanged, 1, 1, QVariantList{QStringLiteral("Col 7")}}, 0192 {DataChanged, 0, 0, QVariantList{QStringLiteral("Col 6")}}}; 0193 m_modelSpy->setExpectedSignals(expectedSignals); 0194 0195 // Give the model a chance to run the event loop to process the signals. 0196 QTest::qWait(10); 0197 0198 // We get all the signals we expected. 0199 QTRY_VERIFY(m_modelSpy->expectedSignals().isEmpty()); 0200 0201 QTest::qWait(10); 0202 // We didn't get signals we didn't expect. 0203 QVERIFY(m_modelSpy->isEmpty()); 0204 } 0205 0206 void EntityTreeModelTest::testCollectionMove_data() 0207 { 0208 QTest::addColumn<QString>("serverContent"); 0209 QTest::addColumn<QString>("movedCollection"); 0210 QTest::addColumn<QString>("targetCollection"); 0211 0212 QTest::newRow("move-collection01") << serverContent1 << "Col 5" 0213 << "Col 1"; 0214 QTest::newRow("move-collection02") << serverContent1 << "Col 5" 0215 << "Col 2"; 0216 QTest::newRow("move-collection03") << serverContent1 << "Col 5" 0217 << "Col 3"; 0218 QTest::newRow("move-collection04") << serverContent1 << "Col 5" 0219 << "Col 6"; 0220 QTest::newRow("move-collection05") << serverContent1 << "Col 5" 0221 << "Col 7"; 0222 QTest::newRow("move-collection06") << serverContent1 << "Col 3" 0223 << "Col 2"; 0224 QTest::newRow("move-collection07") << serverContent1 << "Col 3" 0225 << "Col 6"; 0226 QTest::newRow("move-collection08") << serverContent1 << "Col 3" 0227 << "Col 7"; 0228 QTest::newRow("move-collection09") << serverContent1 << "Col 7" 0229 << "Col 2"; 0230 QTest::newRow("move-collection10") << serverContent1 << "Col 7" 0231 << "Col 5"; 0232 QTest::newRow("move-collection11") << serverContent1 << "Col 7" 0233 << "Col 4"; 0234 QTest::newRow("move-collection12") << serverContent1 << "Col 7" 0235 << "Col 3"; 0236 } 0237 0238 void EntityTreeModelTest::testCollectionMove() 0239 { 0240 QFETCH(QString, serverContent); 0241 QFETCH(QString, movedCollection); 0242 QFETCH(QString, targetCollection); 0243 0244 const auto testDrivers = populateModel(serverContent); 0245 auto const serverData = testDrivers.first; 0246 auto const model = testDrivers.second; 0247 0248 const auto movedIndex = firstMatchedIndex(*model, movedCollection); 0249 Q_ASSERT(movedIndex.isValid()); 0250 const auto sourceCollection = movedIndex.parent().data().toString(); 0251 const auto sourceRow = movedIndex.row(); 0252 0253 auto const moveCommand = new FakeCollectionMovedCommand(movedCollection, sourceCollection, targetCollection, serverData); 0254 0255 m_modelSpy->startSpying(); 0256 serverData->setCommands({moveCommand}); 0257 0258 const QList<ExpectedSignal> expectedSignals{{RowsAboutToBeMoved, sourceRow, sourceRow, sourceCollection, 0, targetCollection, {movedCollection}}, 0259 {RowsMoved, sourceRow, sourceRow, sourceCollection, 0, targetCollection, {movedCollection}}}; 0260 m_modelSpy->setExpectedSignals(expectedSignals); 0261 serverData->processNotifications(); 0262 0263 // Give the model a change to run the event loop to process the signals. 0264 QTest::qWait(0); 0265 0266 QVERIFY(m_modelSpy->isEmpty()); 0267 } 0268 0269 void EntityTreeModelTest::testCollectionAdded_data() 0270 { 0271 QTest::addColumn<QString>("serverContent"); 0272 QTest::addColumn<QString>("addedCollection"); 0273 QTest::addColumn<QString>("parentCollection"); 0274 0275 QTest::newRow("add-collection01") << serverContent1 << "new Collection" 0276 << "Col 1"; 0277 QTest::newRow("add-collection02") << serverContent1 << "new Collection" 0278 << "Col 2"; 0279 QTest::newRow("add-collection03") << serverContent1 << "new Collection" 0280 << "Col 3"; 0281 QTest::newRow("add-collection04") << serverContent1 << "new Collection" 0282 << "Col 4"; 0283 QTest::newRow("add-collection05") << serverContent1 << "new Collection" 0284 << "Col 5"; 0285 QTest::newRow("add-collection06") << serverContent1 << "new Collection" 0286 << "Col 6"; 0287 QTest::newRow("add-collection07") << serverContent1 << "new Collection" 0288 << "Col 7"; 0289 } 0290 0291 void EntityTreeModelTest::testCollectionAdded() 0292 { 0293 QFETCH(QString, serverContent); 0294 QFETCH(QString, addedCollection); 0295 QFETCH(QString, parentCollection); 0296 0297 const auto testDrivers = populateModel(serverContent); 0298 auto const serverData = testDrivers.first; 0299 0300 auto const addCommand = new FakeCollectionAddedCommand(addedCollection, parentCollection, serverData); 0301 0302 m_modelSpy->startSpying(); 0303 serverData->setCommands({addCommand}); 0304 0305 const QList<ExpectedSignal> expectedSignals{ 0306 {RowsAboutToBeInserted, 0, 0, parentCollection, QVariantList{addedCollection}}, 0307 {RowsInserted, 0, 0, parentCollection, QVariantList{addedCollection}}, 0308 // The data changed signal comes from the item fetch job that is triggered because we have ImmediatePopulation enabled 0309 {DataChanged, 0, 0, parentCollection, QVariantList{addedCollection}}}; 0310 0311 m_modelSpy->setExpectedSignals(expectedSignals); 0312 serverData->processNotifications(); 0313 0314 // Give the model a chance to run the event loop to process the signals. 0315 QTest::qWait(0); 0316 0317 QVERIFY(m_modelSpy->isEmpty()); 0318 } 0319 0320 void EntityTreeModelTest::testCollectionRemoved_data() 0321 { 0322 QTest::addColumn<QString>("serverContent"); 0323 QTest::addColumn<QString>("removedCollection"); 0324 0325 // The test suite doesn't handle this case yet. 0326 // QTest::newRow("remove-collection01") << serverContent1 << "Col 1"; 0327 QTest::newRow("remove-collection02") << serverContent1 << "Col 2"; 0328 QTest::newRow("remove-collection03") << serverContent1 << "Col 3"; 0329 QTest::newRow("remove-collection04") << serverContent1 << "Col 4"; 0330 QTest::newRow("remove-collection05") << serverContent1 << "Col 5"; 0331 QTest::newRow("remove-collection06") << serverContent1 << "Col 6"; 0332 QTest::newRow("remove-collection07") << serverContent1 << "Col 7"; 0333 } 0334 0335 void EntityTreeModelTest::testCollectionRemoved() 0336 { 0337 QFETCH(QString, serverContent); 0338 QFETCH(QString, removedCollection); 0339 0340 const auto testDrivers = populateModel(serverContent); 0341 auto const serverData = testDrivers.first; 0342 auto const model = testDrivers.second; 0343 0344 const auto removedIndex = firstMatchedIndex(*model, removedCollection); 0345 const auto parentCollection = removedIndex.parent().data().toString(); 0346 const auto sourceRow = removedIndex.row(); 0347 0348 auto const removeCommand = new FakeCollectionRemovedCommand(removedCollection, parentCollection, serverData); 0349 0350 m_modelSpy->startSpying(); 0351 serverData->setCommands({removeCommand}); 0352 0353 const QList<ExpectedSignal> expectedSignals{{RowsAboutToBeRemoved, sourceRow, sourceRow, parentCollection, QVariantList{removedCollection}}, 0354 {RowsRemoved, sourceRow, sourceRow, parentCollection, QVariantList{removedCollection}}}; 0355 0356 m_modelSpy->setExpectedSignals(expectedSignals); 0357 serverData->processNotifications(); 0358 0359 // Give the model a chance to run the event loop to process the signals. 0360 QTest::qWait(0); 0361 0362 QVERIFY(m_modelSpy->isEmpty()); 0363 } 0364 0365 void EntityTreeModelTest::testCollectionChanged_data() 0366 { 0367 QTest::addColumn<QString>("serverContent"); 0368 QTest::addColumn<QString>("collectionName"); 0369 QTest::addColumn<QString>("monitoredMimeType"); 0370 0371 QTest::newRow("change-collection01") << serverContent1 << "Col 1" << QString(); 0372 QTest::newRow("change-collection02") << serverContent1 << "Col 2" << QString(); 0373 QTest::newRow("change-collection03") << serverContent1 << "Col 3" << QString(); 0374 QTest::newRow("change-collection04") << serverContent1 << "Col 4" << QString(); 0375 QTest::newRow("change-collection05") << serverContent1 << "Col 5" << QString(); 0376 QTest::newRow("change-collection06") << serverContent1 << "Col 6" << QString(); 0377 QTest::newRow("change-collection07") << serverContent1 << "Col 7" << QString(); 0378 // Don't remove the parent due to a missing mimetype 0379 QTest::newRow("change-collection08") << serverContent1 << "Col 1" << QStringLiteral("message/rfc822"); 0380 } 0381 0382 void EntityTreeModelTest::testCollectionChanged() 0383 { 0384 QFETCH(QString, serverContent); 0385 QFETCH(QString, collectionName); 0386 QFETCH(QString, monitoredMimeType); // ##### TODO: this is unused. Is this test correct? 0387 0388 const auto testDrivers = populateModel(serverContent); 0389 auto const serverData = testDrivers.first; 0390 auto const model = testDrivers.second; 0391 0392 const auto changedIndex = firstMatchedIndex(*model, collectionName); 0393 const auto parentCollection = changedIndex.parent().data().toString(); 0394 qDebug() << parentCollection; 0395 const auto changedRow = changedIndex.row(); 0396 0397 auto const changeCommand = new FakeCollectionChangedCommand(collectionName, parentCollection, serverData); 0398 0399 m_modelSpy->startSpying(); 0400 serverData->setCommands({changeCommand}); 0401 0402 const QList<ExpectedSignal> expectedSignals{{DataChanged, changedRow, changedRow, parentCollection, QVariantList{collectionName}}}; 0403 0404 m_modelSpy->setExpectedSignals(expectedSignals); 0405 serverData->processNotifications(); 0406 0407 // Give the model a chance to run the event loop to process the signals. 0408 QTest::qWait(0); 0409 0410 QVERIFY(m_modelSpy->isEmpty()); 0411 } 0412 0413 void EntityTreeModelTest::testItemMove_data() 0414 { 0415 QTest::addColumn<QString>("serverContent"); 0416 QTest::addColumn<QString>("movedItem"); 0417 QTest::addColumn<QString>("targetCollection"); 0418 0419 QTest::newRow("move-item01") << serverContent1 << "Item 1" 0420 << "Col 7"; 0421 QTest::newRow("move-item02") << serverContent1 << "Item 5" 0422 << "Col 4"; // Move item to grandparent. 0423 QTest::newRow("move-item03") << serverContent1 << "Item 7" 0424 << "Col 5"; // Move item to sibling. 0425 QTest::newRow("move-item04") << serverContent1 << "Item 8" 0426 << "Col 5"; // Move item to nephew 0427 QTest::newRow("move-item05") << serverContent1 << "Item 8" 0428 << "Col 6"; // Move item to uncle 0429 QTest::newRow("move-item02") << serverContent1 << "Item 5" 0430 << "Col 3"; // Move item to great-grandparent. 0431 } 0432 0433 void EntityTreeModelTest::testItemMove() 0434 { 0435 QFETCH(QString, serverContent); 0436 QFETCH(QString, movedItem); 0437 QFETCH(QString, targetCollection); 0438 0439 const auto testDrivers = populateModel(serverContent); 0440 auto const serverData = testDrivers.first; 0441 auto const model = testDrivers.second; 0442 0443 const auto movedIndex = firstMatchedIndex(*model, movedItem); 0444 const auto sourceCollection = movedIndex.parent().data().toString(); 0445 const auto sourceRow = movedIndex.row(); 0446 0447 const auto targetIndex = firstMatchedIndex(*model, targetCollection); 0448 const auto targetRow = model->rowCount(targetIndex); 0449 0450 auto const moveCommand = new FakeItemMovedCommand(movedItem, sourceCollection, targetCollection, serverData); 0451 0452 m_modelSpy->startSpying(); 0453 serverData->setCommands({moveCommand}); 0454 0455 const QList<ExpectedSignal> expectedSignals{ 0456 // Currently moves are implemented as remove + insert in the ETM. 0457 {RowsAboutToBeRemoved, sourceRow, sourceRow, sourceCollection, QVariantList{movedItem}}, 0458 {RowsRemoved, sourceRow, sourceRow, sourceCollection, QVariantList{movedItem}}, 0459 {RowsAboutToBeInserted, targetRow, targetRow, targetCollection, QVariantList{movedItem}}, 0460 {RowsInserted, targetRow, targetRow, targetCollection, QVariantList{movedItem}}, 0461 // {RowsAboutToBeMoved, sourceRow, sourceRow, sourceCollection, targetRow, targetCollection, QVariantList{movedItem}}, 0462 // {RowsMoved, sourceRow, sourceRow, sourceCollection, targetRow, targetCollection, QVariantList{movedItem}}, 0463 }; 0464 0465 m_modelSpy->setExpectedSignals(expectedSignals); 0466 serverData->processNotifications(); 0467 0468 // Give the model a chance to run the event loop to process the signals. 0469 QTest::qWait(0); 0470 0471 QVERIFY(m_modelSpy->isEmpty()); 0472 } 0473 0474 void EntityTreeModelTest::testItemAdded_data() 0475 { 0476 QTest::addColumn<QString>("serverContent"); 0477 QTest::addColumn<QString>("addedItem"); 0478 QTest::addColumn<QString>("parentCollection"); 0479 0480 QTest::newRow("add-item01") << serverContent1 << "new Item" 0481 << "Col 1"; 0482 QTest::newRow("add-item02") << serverContent1 << "new Item" 0483 << "Col 2"; 0484 QTest::newRow("add-item03") << serverContent1 << "new Item" 0485 << "Col 3"; 0486 QTest::newRow("add-item04") << serverContent1 << "new Item" 0487 << "Col 4"; 0488 QTest::newRow("add-item05") << serverContent1 << "new Item" 0489 << "Col 5"; 0490 QTest::newRow("add-item06") << serverContent1 << "new Item" 0491 << "Col 6"; 0492 QTest::newRow("add-item07") << serverContent1 << "new Item" 0493 << "Col 7"; 0494 } 0495 0496 void EntityTreeModelTest::testItemAdded() 0497 { 0498 QFETCH(QString, serverContent); 0499 QFETCH(QString, addedItem); 0500 QFETCH(QString, parentCollection); 0501 0502 const auto testDrivers = populateModel(serverContent); 0503 auto const serverData = testDrivers.first; 0504 auto const model = testDrivers.second; 0505 0506 const auto parentIndex = firstMatchedIndex(*model, parentCollection); 0507 const auto targetRow = model->rowCount(parentIndex); 0508 0509 auto const addedCommand = new FakeItemAddedCommand(addedItem, parentCollection, serverData); 0510 0511 m_modelSpy->startSpying(); 0512 0513 serverData->setCommands({addedCommand}); 0514 0515 const QList<ExpectedSignal> expectedSignals{{RowsAboutToBeInserted, targetRow, targetRow, parentCollection, QVariantList{addedItem}}, 0516 {RowsInserted, targetRow, targetRow, parentCollection, QVariantList{addedItem}}}; 0517 m_modelSpy->setExpectedSignals(expectedSignals); 0518 serverData->processNotifications(); 0519 0520 // Give the model a chance to run the event loop to process the signals. 0521 QTest::qWait(0); 0522 0523 QVERIFY(m_modelSpy->isEmpty()); 0524 } 0525 0526 void EntityTreeModelTest::testItemRemoved_data() 0527 { 0528 QTest::addColumn<QString>("serverContent"); 0529 QTest::addColumn<QString>("removedItem"); 0530 0531 QTest::newRow("remove-item01") << serverContent1 << "Item 1"; 0532 QTest::newRow("remove-item02") << serverContent1 << "Item 2"; 0533 QTest::newRow("remove-item03") << serverContent1 << "Item 3"; 0534 QTest::newRow("remove-item04") << serverContent1 << "Item 4"; 0535 QTest::newRow("remove-item05") << serverContent1 << "Item 5"; 0536 QTest::newRow("remove-item06") << serverContent1 << "Item 6"; 0537 QTest::newRow("remove-item07") << serverContent1 << "Item 7"; 0538 QTest::newRow("remove-item08") << serverContent1 << "Item 8"; 0539 QTest::newRow("remove-item09") << serverContent1 << "Item 9"; 0540 QTest::newRow("remove-item10") << serverContent1 << "Item 10"; 0541 QTest::newRow("remove-item11") << serverContent1 << "Item 11"; 0542 QTest::newRow("remove-item12") << serverContent1 << "Item 12"; 0543 QTest::newRow("remove-item13") << serverContent1 << "Item 13"; 0544 QTest::newRow("remove-item14") << serverContent1 << "Item 14"; 0545 QTest::newRow("remove-item15") << serverContent1 << "Item 15"; 0546 } 0547 0548 void EntityTreeModelTest::testItemRemoved() 0549 { 0550 QFETCH(QString, serverContent); 0551 QFETCH(QString, removedItem); 0552 0553 const auto testDrivers = populateModel(serverContent); 0554 auto const serverData = testDrivers.first; 0555 auto const model = testDrivers.second; 0556 0557 const auto removedIndex = firstMatchedIndex(*model, removedItem); 0558 const auto sourceCollection = removedIndex.parent().data().toString(); 0559 const auto sourceRow = removedIndex.row(); 0560 0561 auto const removeCommand = new FakeItemRemovedCommand(removedItem, sourceCollection, serverData); 0562 0563 m_modelSpy->startSpying(); 0564 serverData->setCommands({removeCommand}); 0565 0566 const QList<ExpectedSignal> expectedSignals{{RowsAboutToBeRemoved, sourceRow, sourceRow, sourceCollection, QVariantList{removedItem}}, 0567 {RowsRemoved, sourceRow, sourceRow, sourceCollection, QVariantList{removedItem}}}; 0568 0569 m_modelSpy->setExpectedSignals(expectedSignals); 0570 serverData->processNotifications(); 0571 0572 // Give the model a chance to run the event loop to process the signals. 0573 QTest::qWait(0); 0574 0575 QVERIFY(m_modelSpy->isEmpty()); 0576 } 0577 0578 void EntityTreeModelTest::testItemChanged_data() 0579 { 0580 QTest::addColumn<QString>("serverContent"); 0581 QTest::addColumn<QString>("changedItem"); 0582 0583 QTest::newRow("change-item01") << serverContent1 << "Item 1"; 0584 QTest::newRow("change-item02") << serverContent1 << "Item 2"; 0585 QTest::newRow("change-item03") << serverContent1 << "Item 3"; 0586 QTest::newRow("change-item04") << serverContent1 << "Item 4"; 0587 QTest::newRow("change-item05") << serverContent1 << "Item 5"; 0588 QTest::newRow("change-item06") << serverContent1 << "Item 6"; 0589 QTest::newRow("change-item07") << serverContent1 << "Item 7"; 0590 QTest::newRow("change-item08") << serverContent1 << "Item 8"; 0591 QTest::newRow("change-item09") << serverContent1 << "Item 9"; 0592 QTest::newRow("change-item10") << serverContent1 << "Item 10"; 0593 QTest::newRow("change-item11") << serverContent1 << "Item 11"; 0594 QTest::newRow("change-item12") << serverContent1 << "Item 12"; 0595 QTest::newRow("change-item13") << serverContent1 << "Item 13"; 0596 QTest::newRow("change-item14") << serverContent1 << "Item 14"; 0597 QTest::newRow("change-item15") << serverContent1 << "Item 15"; 0598 } 0599 0600 void EntityTreeModelTest::testItemChanged() 0601 { 0602 QFETCH(QString, serverContent); 0603 QFETCH(QString, changedItem); 0604 0605 const auto testDrivers = populateModel(serverContent); 0606 auto const serverData = testDrivers.first; 0607 auto const model = testDrivers.second; 0608 0609 const auto changedIndex = firstMatchedIndex(*model, changedItem); 0610 const auto parentCollection = changedIndex.parent().data().toString(); 0611 const auto sourceRow = changedIndex.row(); 0612 0613 auto const changeCommand = new FakeItemChangedCommand(changedItem, parentCollection, serverData); 0614 0615 m_modelSpy->startSpying(); 0616 serverData->setCommands({changeCommand}); 0617 0618 const QList<ExpectedSignal> expectedSignals{{DataChanged, sourceRow, sourceRow, QVariantList{changedItem}}}; 0619 0620 m_modelSpy->setExpectedSignals(expectedSignals); 0621 serverData->processNotifications(); 0622 0623 // Give the model a chance to run the event loop to process the signals. 0624 QTest::qWait(0); 0625 0626 QVERIFY(m_modelSpy->isEmpty()); 0627 } 0628 0629 void EntityTreeModelTest::testRemoveCollectionOnChanged() 0630 { 0631 const auto serverContent = QStringLiteral( 0632 "- C (inode/directory, text/directory) 'Col 1' 2" 0633 "- - C (text/directory) 'Col 2' 1" 0634 "- - - I text/directory 'Item 1'"); 0635 const auto collectionName = QStringLiteral("Col 2"); 0636 const auto monitoredMimeType = QStringLiteral("text/directory"); 0637 0638 const auto testDrivers = populateModel(serverContent, monitoredMimeType); 0639 auto const serverData = testDrivers.first; 0640 auto const model = testDrivers.second; 0641 0642 const auto changedIndex = firstMatchedIndex(*model, collectionName); 0643 auto changedCol = changedIndex.data(Akonadi::EntityTreeModel::CollectionRole).value<Akonadi::Collection>(); 0644 changedCol.setContentMimeTypes({QStringLiteral("foobar")}); 0645 const auto parentCollection = changedIndex.parent().data().toString(); 0646 0647 auto const changeCommand = new FakeCollectionChangedCommand(changedCol, serverData); 0648 0649 m_modelSpy->startSpying(); 0650 serverData->setCommands({changeCommand}); 0651 0652 const QList<ExpectedSignal> expectedSignals{ 0653 {RowsAboutToBeRemoved, changedIndex.row(), changedIndex.row(), parentCollection, QVariantList{collectionName}}, 0654 {RowsRemoved, changedIndex.row(), changedIndex.row(), parentCollection, QVariantList{collectionName}}, 0655 }; 0656 0657 m_modelSpy->setExpectedSignals(expectedSignals); 0658 serverData->processNotifications(); 0659 0660 // Give the model a chance to run the event loop to process the signals. 0661 QTest::qWait(0); 0662 0663 QVERIFY(m_modelSpy->isEmpty()); 0664 } 0665 0666 #include "entitytreemodeltest.moc" 0667 0668 QTEST_MAIN(EntityTreeModelTest)