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)