diff --git a/autotests/libs/entitytreemodeltest.cpp b/autotests/libs/entitytreemodeltest.cpp index 1e2c55006..9ef4f143c 100644 --- a/autotests/libs/entitytreemodeltest.cpp +++ b/autotests/libs/entitytreemodeltest.cpp @@ -1,698 +1,696 @@ /* Copyright (c) 2009 Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "fakeserverdata.h" #include "fakesession.h" #include "fakemonitor.h" #include "modelspy.h" #include "imapparser_p.h" #include "entitytreemodel.h" #include #include static const QString serverContent1 = QStringLiteral( // The format of these lines are first a type, either 'C' or 'I' for Item and collection. // The dashes show the depth in the hierarchy // Collections have a list of mimetypes they can contain, followed by an optional // displayName which is put into the EntityDisplayAttribute, followed by an optional order // which is the order in which the collections are returned from the job to the ETM. "- C (inode/directory) 'Col 1' 4" "- - C (text/directory, message/rfc822) 'Col 2' 3" // Items just have the mimetype they contain in the payload. "- - - I text/directory 'Item 1'" "- - - I text/directory 'Item 2'" "- - - I message/rfc822 'Item 3'" "- - - I message/rfc822 'Item 4'" "- - C (text/directory) 'Col 3' 3" "- - - C (text/directory) 'Col 4' 2" "- - - - C (text/directory) 'Col 5' 1" // <-- First collection to be returned "- - - - - I text/directory 'Item 5'" "- - - - - I text/directory 'Item 6'" "- - - - I text/directory 'Item 7'" "- - - I text/directory 'Item 8'" "- - - I text/directory 'Item 9'" "- - C (message/rfc822) 'Col 6' 3" "- - - I message/rfc822 'Item 10'" "- - - I message/rfc822 'Item 11'" "- - C (text/directory, message/rfc822) 'Col 7' 3" "- - - I text/directory 'Item 12'" "- - - I text/directory 'Item 13'" "- - - I message/rfc822 'Item 14'" "- - - I message/rfc822 'Item 15'"); /** * This test verifies that the ETM reacts as expected to signals from the monitor. * * The tested ETM is only talking to fake components so the reaction of the ETM to each signal can be tested. * * WARNING: This test does no handle jobs issued by the model. It simply shortcuts (calls emitResult) them, and the connected * slots are never executed (because the eventloop is not run after emitResult is called). * This test therefore only tests the reaction of the model to signals of the monitor and not the overall behaviour. */ class EntityTreeModelTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testInitialFetch(); void testCollectionMove_data(); void testCollectionMove(); void testCollectionAdded_data(); void testCollectionAdded(); void testCollectionRemoved_data(); void testCollectionRemoved(); void testCollectionChanged_data(); void testCollectionChanged(); void testItemMove_data(); void testItemMove(); void testItemAdded_data(); void testItemAdded(); void testItemRemoved_data(); void testItemRemoved(); void testItemChanged_data(); void testItemChanged(); void testRemoveCollectionOnChanged(); private: ExpectedSignal getExpectedSignal(SignalType type, int start, int end, const QVariantList newData) { return getExpectedSignal(type, start, end, QVariant(), newData); } ExpectedSignal getExpectedSignal(SignalType type, int start, int end, const QVariant &parentData = QVariant(), const QVariantList newData = QVariantList()) { ExpectedSignal expectedSignal; expectedSignal.signalType = type; expectedSignal.startRow = start; expectedSignal.endRow = end; expectedSignal.parentData = parentData; expectedSignal.newData = newData; return expectedSignal; } ExpectedSignal getExpectedSignal(SignalType type, int start, int end, const QVariant &sourceParentData, int destRow, const QVariant &destParentData, const QVariantList newData) { ExpectedSignal expectedSignal; expectedSignal.signalType = type; expectedSignal.startRow = start; expectedSignal.endRow = end; expectedSignal.sourceParentData = sourceParentData; expectedSignal.destRow = destRow; expectedSignal.parentData = destParentData; expectedSignal.newData = newData; return expectedSignal; } QPair populateModel(const QString &serverContent, const QString &mimeType = QString()) { FakeMonitor *fakeMonitor = new FakeMonitor(this); fakeMonitor->setSession(m_fakeSession); fakeMonitor->setCollectionMonitored(Collection::root()); if (!mimeType.isEmpty()) { fakeMonitor->setMimeTypeMonitored(mimeType); } EntityTreeModel *model = new EntityTreeModel(fakeMonitor, this); - m_modelSpy = new ModelSpy(this); - m_modelSpy->setModel(model); + m_modelSpy = new ModelSpy{model, this}; FakeServerData *serverData = new FakeServerData(model, m_fakeSession, fakeMonitor); QList initialFetchResponse = FakeJobResponse::interpret(serverData, serverContent); serverData->setCommands(initialFetchResponse); // Give the model a chance to populate QTest::qWait(100); return qMakePair(serverData, model); } private: ModelSpy *m_modelSpy = nullptr; FakeSession *m_fakeSession = nullptr; QByteArray m_sessionName; }; void EntityTreeModelTest::initTestCase() { m_sessionName = "EntityTreeModelTest fake session"; m_fakeSession = new FakeSession(m_sessionName, FakeSession::EndJobsImmediately); m_fakeSession->setAsDefaultSession(); qRegisterMetaType("QModelIndex"); } void EntityTreeModelTest::testInitialFetch() { FakeMonitor *fakeMonitor = new FakeMonitor(this); fakeMonitor->setSession(m_fakeSession); fakeMonitor->setCollectionMonitored(Collection::root()); EntityTreeModel *model = new EntityTreeModel(fakeMonitor, this); FakeServerData *serverData = new FakeServerData(model, m_fakeSession, fakeMonitor); QList initialFetchResponse = FakeJobResponse::interpret(serverData, serverContent1); serverData->setCommands(initialFetchResponse); - m_modelSpy = new ModelSpy(this); - m_modelSpy->setModel(model); + m_modelSpy = new ModelSpy{model, this}; m_modelSpy->startSpying(); QList expectedSignals; // First the model gets a signal about the first collection to be returned, which is not a top-level collection. // It uses the parentCollection hierarchy to put placeholder collections in the model until the root is reached. // Then it inserts only one row and emits the correct signals. After that, when the other collections // arrive, dataChanged is emitted for them. expectedSignals << getExpectedSignal(RowsAboutToBeInserted, 0, 0); expectedSignals << getExpectedSignal(RowsInserted, 0, 0); expectedSignals << getExpectedSignal(DataChanged, 0, 0, QVariantList() << QStringLiteral("Col 4")); expectedSignals << getExpectedSignal(DataChanged, 0, 0, QVariantList() << QStringLiteral("Col 3")); // New collections are prepended expectedSignals << getExpectedSignal(RowsAboutToBeInserted, 0, 0, QStringLiteral("Collection 1")); expectedSignals << getExpectedSignal(RowsInserted, 0, 0, QStringLiteral("Collection 1"), QVariantList() << QStringLiteral("Col 2")); expectedSignals << getExpectedSignal(RowsAboutToBeInserted, 0, 0, QStringLiteral("Collection 1")); expectedSignals << getExpectedSignal(RowsInserted, 0, 0, QStringLiteral("Collection 1"), QVariantList() << QStringLiteral("Col 6")); expectedSignals << getExpectedSignal(RowsAboutToBeInserted, 0, 0, QStringLiteral("Collection 1")); expectedSignals << getExpectedSignal(RowsInserted, 0, 0, QStringLiteral("Collection 1"), QVariantList() << QStringLiteral("Col 7")); expectedSignals << getExpectedSignal(DataChanged, 0, 0, QVariantList() << QStringLiteral("Col 1")); // The items in the collections are appended. expectedSignals << getExpectedSignal(RowsAboutToBeInserted, 0, 3, QStringLiteral("Col 2")); expectedSignals << getExpectedSignal(RowsInserted, 0, 3, QStringLiteral("Col 2")); expectedSignals << getExpectedSignal(RowsAboutToBeInserted, 0, 1, QStringLiteral("Col 5")); expectedSignals << getExpectedSignal(RowsInserted, 0, 1, QStringLiteral("Col 5")); expectedSignals << getExpectedSignal(RowsAboutToBeInserted, 1, 1, QStringLiteral("Col 4")); expectedSignals << getExpectedSignal(RowsInserted, 1, 1, QStringLiteral("Col 4")); expectedSignals << getExpectedSignal(RowsAboutToBeInserted, 1, 2, QStringLiteral("Col 3")); expectedSignals << getExpectedSignal(RowsInserted, 1, 2, QStringLiteral("Col 3")); expectedSignals << getExpectedSignal(RowsAboutToBeInserted, 0, 1, QStringLiteral("Col 6")); expectedSignals << getExpectedSignal(RowsInserted, 0, 1, QStringLiteral("Col 6")); expectedSignals << getExpectedSignal(RowsAboutToBeInserted, 0, 3, QStringLiteral("Col 7")); expectedSignals << getExpectedSignal(RowsInserted, 0, 3, QStringLiteral("Col 7")); expectedSignals << getExpectedSignal(DataChanged, 0, 0, QVariantList() << QStringLiteral("Col 1")); expectedSignals << getExpectedSignal(DataChanged, 3, 3, QVariantList() << QStringLiteral("Col 3")); expectedSignals << getExpectedSignal(DataChanged, 0, 0, QVariantList() << QStringLiteral("Col 5")); expectedSignals << getExpectedSignal(DataChanged, 0, 0, QVariantList() << QStringLiteral("Col 4")); expectedSignals << getExpectedSignal(DataChanged, 2, 2, QVariantList() << QStringLiteral("Col 2")); expectedSignals << getExpectedSignal(DataChanged, 1, 1, QVariantList() << QStringLiteral("Col 7")); expectedSignals << getExpectedSignal(DataChanged, 0, 0, QVariantList() << QStringLiteral("Col 6")); m_modelSpy->setExpectedSignals(expectedSignals); // Give the model a chance to run the event loop to process the signals. QTest::qWait(10); // We get all the signals we expected. QTRY_VERIFY(m_modelSpy->expectedSignals().isEmpty()); QTest::qWait(10); // We didn't get signals we didn't expect. QVERIFY(m_modelSpy->isEmpty()); } void EntityTreeModelTest::testCollectionMove_data() { QTest::addColumn("serverContent"); QTest::addColumn("movedCollection"); QTest::addColumn("targetCollection"); QTest::newRow("move-collection01") << serverContent1 << "Col 5" << "Col 1"; QTest::newRow("move-collection02") << serverContent1 << "Col 5" << "Col 2"; QTest::newRow("move-collection03") << serverContent1 << "Col 5" << "Col 3"; QTest::newRow("move-collection04") << serverContent1 << "Col 5" << "Col 6"; QTest::newRow("move-collection05") << serverContent1 << "Col 5" << "Col 7"; QTest::newRow("move-collection06") << serverContent1 << "Col 3" << "Col 2"; QTest::newRow("move-collection07") << serverContent1 << "Col 3" << "Col 6"; QTest::newRow("move-collection08") << serverContent1 << "Col 3" << "Col 7"; QTest::newRow("move-collection09") << serverContent1 << "Col 7" << "Col 2"; QTest::newRow("move-collection10") << serverContent1 << "Col 7" << "Col 5"; QTest::newRow("move-collection11") << serverContent1 << "Col 7" << "Col 4"; QTest::newRow("move-collection12") << serverContent1 << "Col 7" << "Col 3"; } void EntityTreeModelTest::testCollectionMove() { QFETCH(QString, serverContent); QFETCH(QString, movedCollection); QFETCH(QString, targetCollection); QPair testDrivers = populateModel(serverContent); FakeServerData *serverData = testDrivers.first; Akonadi::EntityTreeModel *model = testDrivers.second; QModelIndexList list = model->match(model->index(0, 0), Qt::DisplayRole, movedCollection, 1, Qt::MatchRecursive); Q_ASSERT(!list.isEmpty()); QModelIndex movedIndex = list.first(); QString sourceCollection = movedIndex.parent().data().toString(); int sourceRow = movedIndex.row(); FakeCollectionMovedCommand *moveCommand = new FakeCollectionMovedCommand(movedCollection, sourceCollection, targetCollection, serverData); m_modelSpy->startSpying(); serverData->setCommands(QList() << moveCommand); QList expectedSignals; expectedSignals << getExpectedSignal(RowsAboutToBeMoved, sourceRow, sourceRow, sourceCollection, 0, targetCollection, QVariantList() << movedCollection); expectedSignals << getExpectedSignal(RowsMoved, sourceRow, sourceRow, sourceCollection, 0, targetCollection , QVariantList() << movedCollection); m_modelSpy->setExpectedSignals(expectedSignals); serverData->processNotifications(); // Give the model a change to run the event loop to process the signals. QTest::qWait(0); QVERIFY(m_modelSpy->isEmpty()); } void EntityTreeModelTest::testCollectionAdded_data() { QTest::addColumn("serverContent"); QTest::addColumn("addedCollection"); QTest::addColumn("parentCollection"); QTest::newRow("add-collection01") << serverContent1 << "new Collection" << "Col 1"; QTest::newRow("add-collection02") << serverContent1 << "new Collection" << "Col 2"; QTest::newRow("add-collection03") << serverContent1 << "new Collection" << "Col 3"; QTest::newRow("add-collection04") << serverContent1 << "new Collection" << "Col 4"; QTest::newRow("add-collection05") << serverContent1 << "new Collection" << "Col 5"; QTest::newRow("add-collection06") << serverContent1 << "new Collection" << "Col 6"; QTest::newRow("add-collection07") << serverContent1 << "new Collection" << "Col 7"; } void EntityTreeModelTest::testCollectionAdded() { QFETCH(QString, serverContent); QFETCH(QString, addedCollection); QFETCH(QString, parentCollection); QPair testDrivers = populateModel(serverContent); FakeServerData *serverData = testDrivers.first; FakeCollectionAddedCommand *addCommand = new FakeCollectionAddedCommand(addedCollection, parentCollection, serverData); m_modelSpy->startSpying(); serverData->setCommands(QList() << addCommand); QList expectedSignals; expectedSignals << getExpectedSignal(RowsAboutToBeInserted, 0, 0, parentCollection, QVariantList() << addedCollection); expectedSignals << getExpectedSignal(RowsInserted, 0, 0, parentCollection, QVariantList() << addedCollection); // The data changed signal comes from the item fetch job that is triggered because we have ImmediatePopulation enabled expectedSignals << getExpectedSignal(DataChanged, 0, 0, parentCollection, QVariantList() << addedCollection); m_modelSpy->setExpectedSignals(expectedSignals); serverData->processNotifications(); // Give the model a chance to run the event loop to process the signals. QTest::qWait(0); QVERIFY(m_modelSpy->isEmpty()); } void EntityTreeModelTest::testCollectionRemoved_data() { QTest::addColumn("serverContent"); QTest::addColumn("removedCollection"); // The test suite doesn't handle this case yet. // QTest::newRow("remove-collection01") << serverContent1 << "Col 1"; QTest::newRow("remove-collection02") << serverContent1 << "Col 2"; QTest::newRow("remove-collection03") << serverContent1 << "Col 3"; QTest::newRow("remove-collection04") << serverContent1 << "Col 4"; QTest::newRow("remove-collection05") << serverContent1 << "Col 5"; QTest::newRow("remove-collection06") << serverContent1 << "Col 6"; QTest::newRow("remove-collection07") << serverContent1 << "Col 7"; } void EntityTreeModelTest::testCollectionRemoved() { QFETCH(QString, serverContent); QFETCH(QString, removedCollection); QPair testDrivers = populateModel(serverContent); FakeServerData *serverData = testDrivers.first; Akonadi::EntityTreeModel *model = testDrivers.second; QModelIndexList list = model->match(model->index(0, 0), Qt::DisplayRole, removedCollection, 1, Qt::MatchRecursive); Q_ASSERT(!list.isEmpty()); QModelIndex removedIndex = list.first(); QString parentCollection = removedIndex.parent().data().toString(); int sourceRow = removedIndex.row(); FakeCollectionRemovedCommand *removeCommand = new FakeCollectionRemovedCommand(removedCollection, parentCollection, serverData); m_modelSpy->startSpying(); serverData->setCommands(QList() << removeCommand); QList expectedSignals; expectedSignals << getExpectedSignal(RowsAboutToBeRemoved, sourceRow, sourceRow, parentCollection, QVariantList() << removedCollection); expectedSignals << getExpectedSignal(RowsRemoved, sourceRow, sourceRow, parentCollection , QVariantList() << removedCollection); m_modelSpy->setExpectedSignals(expectedSignals); serverData->processNotifications(); // Give the model a chance to run the event loop to process the signals. QTest::qWait(0); QVERIFY(m_modelSpy->isEmpty()); } void EntityTreeModelTest::testCollectionChanged_data() { QTest::addColumn("serverContent"); QTest::addColumn("collectionName"); QTest::addColumn("monitoredMimeType"); QTest::newRow("change-collection01") << serverContent1 << "Col 1" << QString(); QTest::newRow("change-collection02") << serverContent1 << "Col 2" << QString(); QTest::newRow("change-collection03") << serverContent1 << "Col 3" << QString(); QTest::newRow("change-collection04") << serverContent1 << "Col 4" << QString(); QTest::newRow("change-collection05") << serverContent1 << "Col 5" << QString(); QTest::newRow("change-collection06") << serverContent1 << "Col 6" << QString(); QTest::newRow("change-collection07") << serverContent1 << "Col 7" << QString(); //Don't remove the parent due to a missing mimetype QTest::newRow("change-collection08") << serverContent1 << "Col 1" << QStringLiteral("message/rfc822"); } void EntityTreeModelTest::testCollectionChanged() { QFETCH(QString, serverContent); QFETCH(QString, collectionName); QFETCH(QString, monitoredMimeType); QPair testDrivers = populateModel(serverContent, monitoredMimeType); FakeServerData *serverData = testDrivers.first; Akonadi::EntityTreeModel *model = testDrivers.second; QModelIndexList list = model->match(model->index(0, 0), Qt::DisplayRole, collectionName, 1, Qt::MatchRecursive); Q_ASSERT(!list.isEmpty()); QModelIndex changedIndex = list.first(); QString parentCollection = changedIndex.parent().data().toString(); qDebug() << parentCollection; int changedRow = changedIndex.row(); FakeCollectionChangedCommand *changeCommand = new FakeCollectionChangedCommand(collectionName, parentCollection, serverData); m_modelSpy->startSpying(); serverData->setCommands(QList() << changeCommand); QList expectedSignals; expectedSignals << getExpectedSignal(DataChanged, changedRow, changedRow, parentCollection, QVariantList() << collectionName); m_modelSpy->setExpectedSignals(expectedSignals); serverData->processNotifications(); // Give the model a chance to run the event loop to process the signals. QTest::qWait(0); QVERIFY(m_modelSpy->isEmpty()); } void EntityTreeModelTest::testItemMove_data() { QTest::addColumn("serverContent"); QTest::addColumn("movedItem"); QTest::addColumn("targetCollection"); QTest::newRow("move-item01") << serverContent1 << "Item 1" << "Col 7"; QTest::newRow("move-item02") << serverContent1 << "Item 5" << "Col 4"; // Move item to grandparent. QTest::newRow("move-item03") << serverContent1 << "Item 7" << "Col 5"; // Move item to sibling. QTest::newRow("move-item04") << serverContent1 << "Item 8" << "Col 5"; // Move item to nephew QTest::newRow("move-item05") << serverContent1 << "Item 8" << "Col 6"; // Move item to uncle QTest::newRow("move-item02") << serverContent1 << "Item 5" << "Col 3"; // Move item to great-grandparent. } void EntityTreeModelTest::testItemMove() { QFETCH(QString, serverContent); QFETCH(QString, movedItem); QFETCH(QString, targetCollection); QPair testDrivers = populateModel(serverContent); FakeServerData *serverData = testDrivers.first; Akonadi::EntityTreeModel *model = testDrivers.second; QModelIndexList list = model->match(model->index(0, 0), Qt::DisplayRole, movedItem, 1, Qt::MatchRecursive); Q_ASSERT(!list.isEmpty()); QModelIndex movedIndex = list.first(); QString sourceCollection = movedIndex.parent().data().toString(); int sourceRow = movedIndex.row(); QModelIndexList targetList = model->match(model->index(0, 0), Qt::DisplayRole, targetCollection, 1, Qt::MatchRecursive); Q_ASSERT(!targetList.isEmpty()); QModelIndex targetIndex = targetList.first(); int targetRow = model->rowCount(targetIndex); FakeItemMovedCommand *moveCommand = new FakeItemMovedCommand(movedItem, sourceCollection, targetCollection, serverData); m_modelSpy->startSpying(); serverData->setCommands(QList() << moveCommand); QList expectedSignals; //Currently moves are implemented as remove + insert in the ETM. expectedSignals << getExpectedSignal(RowsAboutToBeRemoved, sourceRow, sourceRow, sourceCollection, QVariantList() << movedItem); expectedSignals << getExpectedSignal(RowsRemoved, sourceRow, sourceRow, sourceCollection, QVariantList() << movedItem); expectedSignals << getExpectedSignal(RowsAboutToBeInserted, targetRow, targetRow, targetCollection, QVariantList() << movedItem); expectedSignals << getExpectedSignal(RowsInserted, targetRow, targetRow, targetCollection, QVariantList() << movedItem); // expectedSignals << getExpectedSignal( RowsAboutToBeMoved, sourceRow, sourceRow, sourceCollection, targetRow, targetCollection, QVariantList() << movedItem ); // expectedSignals << getExpectedSignal( RowsMoved, sourceRow, sourceRow, sourceCollection, targetRow, targetCollection, QVariantList() << movedItem ); m_modelSpy->setExpectedSignals(expectedSignals); serverData->processNotifications(); // Give the model a chance to run the event loop to process the signals. QTest::qWait(0); QVERIFY(m_modelSpy->isEmpty()); } void EntityTreeModelTest::testItemAdded_data() { QTest::addColumn("serverContent"); QTest::addColumn("addedItem"); QTest::addColumn("parentCollection"); QTest::newRow("add-item01") << serverContent1 << "new Item" << "Col 1"; QTest::newRow("add-item02") << serverContent1 << "new Item" << "Col 2"; QTest::newRow("add-item03") << serverContent1 << "new Item" << "Col 3"; QTest::newRow("add-item04") << serverContent1 << "new Item" << "Col 4"; QTest::newRow("add-item05") << serverContent1 << "new Item" << "Col 5"; QTest::newRow("add-item06") << serverContent1 << "new Item" << "Col 6"; QTest::newRow("add-item07") << serverContent1 << "new Item" << "Col 7"; } void EntityTreeModelTest::testItemAdded() { QFETCH(QString, serverContent); QFETCH(QString, addedItem); QFETCH(QString, parentCollection); QPair testDrivers = populateModel(serverContent); FakeServerData *serverData = testDrivers.first; Akonadi::EntityTreeModel *model = testDrivers.second; QModelIndexList list = model->match(model->index(0, 0), Qt::DisplayRole, parentCollection, 1, Qt::MatchRecursive); Q_ASSERT(!list.isEmpty()); QModelIndex parentIndex = list.first(); int targetRow = model->rowCount(parentIndex); FakeItemAddedCommand *addedCommand = new FakeItemAddedCommand(addedItem, parentCollection, serverData); m_modelSpy->startSpying(); serverData->setCommands(QList() << addedCommand); QList expectedSignals; expectedSignals << getExpectedSignal(RowsAboutToBeInserted, targetRow, targetRow, parentCollection, QVariantList() << addedItem); expectedSignals << getExpectedSignal(RowsInserted, targetRow, targetRow, parentCollection, QVariantList() << addedItem); m_modelSpy->setExpectedSignals(expectedSignals); serverData->processNotifications(); // Give the model a chance to run the event loop to process the signals. QTest::qWait(0); QVERIFY(m_modelSpy->isEmpty()); } void EntityTreeModelTest::testItemRemoved_data() { QTest::addColumn("serverContent"); QTest::addColumn("removedItem"); QTest::newRow("remove-item01") << serverContent1 << "Item 1"; QTest::newRow("remove-item02") << serverContent1 << "Item 2"; QTest::newRow("remove-item03") << serverContent1 << "Item 3"; QTest::newRow("remove-item04") << serverContent1 << "Item 4"; QTest::newRow("remove-item05") << serverContent1 << "Item 5"; QTest::newRow("remove-item06") << serverContent1 << "Item 6"; QTest::newRow("remove-item07") << serverContent1 << "Item 7"; QTest::newRow("remove-item08") << serverContent1 << "Item 8"; QTest::newRow("remove-item09") << serverContent1 << "Item 9"; QTest::newRow("remove-item10") << serverContent1 << "Item 10"; QTest::newRow("remove-item11") << serverContent1 << "Item 11"; QTest::newRow("remove-item12") << serverContent1 << "Item 12"; QTest::newRow("remove-item13") << serverContent1 << "Item 13"; QTest::newRow("remove-item14") << serverContent1 << "Item 14"; QTest::newRow("remove-item15") << serverContent1 << "Item 15"; } void EntityTreeModelTest::testItemRemoved() { QFETCH(QString, serverContent); QFETCH(QString, removedItem); QPair testDrivers = populateModel(serverContent); FakeServerData *serverData = testDrivers.first; Akonadi::EntityTreeModel *model = testDrivers.second; const QModelIndexList list = model->match(model->index(0, 0), Qt::DisplayRole, removedItem, 1, Qt::MatchRecursive); Q_ASSERT(!list.isEmpty()); QModelIndex removedIndex = list.first(); const QString sourceCollection = removedIndex.parent().data().toString(); int sourceRow = removedIndex.row(); FakeItemRemovedCommand *removeCommand = new FakeItemRemovedCommand(removedItem, sourceCollection, serverData); m_modelSpy->startSpying(); serverData->setCommands(QList() << removeCommand); QList expectedSignals; expectedSignals << getExpectedSignal(RowsAboutToBeRemoved, sourceRow, sourceRow, sourceCollection, QVariantList() << removedItem); expectedSignals << getExpectedSignal(RowsRemoved, sourceRow, sourceRow, sourceCollection, QVariantList() << removedItem); m_modelSpy->setExpectedSignals(expectedSignals); serverData->processNotifications(); // Give the model a chance to run the event loop to process the signals. QTest::qWait(0); QVERIFY(m_modelSpy->isEmpty()); } void EntityTreeModelTest::testItemChanged_data() { QTest::addColumn("serverContent"); QTest::addColumn("changedItem"); QTest::newRow("change-item01") << serverContent1 << "Item 1"; QTest::newRow("change-item02") << serverContent1 << "Item 2"; QTest::newRow("change-item03") << serverContent1 << "Item 3"; QTest::newRow("change-item04") << serverContent1 << "Item 4"; QTest::newRow("change-item05") << serverContent1 << "Item 5"; QTest::newRow("change-item06") << serverContent1 << "Item 6"; QTest::newRow("change-item07") << serverContent1 << "Item 7"; QTest::newRow("change-item08") << serverContent1 << "Item 8"; QTest::newRow("change-item09") << serverContent1 << "Item 9"; QTest::newRow("change-item10") << serverContent1 << "Item 10"; QTest::newRow("change-item11") << serverContent1 << "Item 11"; QTest::newRow("change-item12") << serverContent1 << "Item 12"; QTest::newRow("change-item13") << serverContent1 << "Item 13"; QTest::newRow("change-item14") << serverContent1 << "Item 14"; QTest::newRow("change-item15") << serverContent1 << "Item 15"; } void EntityTreeModelTest::testItemChanged() { QFETCH(QString, serverContent); QFETCH(QString, changedItem); QPair testDrivers = populateModel(serverContent); FakeServerData *serverData = testDrivers.first; Akonadi::EntityTreeModel *model = testDrivers.second; QModelIndexList list = model->match(model->index(0, 0), Qt::DisplayRole, changedItem, 1, Qt::MatchRecursive); Q_ASSERT(!list.isEmpty()); QModelIndex changedIndex = list.first(); QString parentCollection = changedIndex.parent().data().toString(); int sourceRow = changedIndex.row(); FakeItemChangedCommand *changeCommand = new FakeItemChangedCommand(changedItem, parentCollection, serverData); m_modelSpy->startSpying(); serverData->setCommands(QList() << changeCommand); QList expectedSignals; expectedSignals << getExpectedSignal(DataChanged, sourceRow, sourceRow, QVariantList() << changedItem); m_modelSpy->setExpectedSignals(expectedSignals); serverData->processNotifications(); // Give the model a chance to run the event loop to process the signals. QTest::qWait(0); QVERIFY(m_modelSpy->isEmpty()); } void EntityTreeModelTest::testRemoveCollectionOnChanged() { const QString serverContent = QStringLiteral( "- C (inode/directory, text/directory) 'Col 1' 2" "- - C (text/directory) 'Col 2' 1" "- - - I text/directory 'Item 1'"); const QString collectionName = QStringLiteral("Col 2"); const QString monitoredMimeType = QStringLiteral("text/directory"); QPair testDrivers = populateModel(serverContent, monitoredMimeType); FakeServerData *serverData = testDrivers.first; Akonadi::EntityTreeModel *model = testDrivers.second; QModelIndexList list = model->match(model->index(0, 0), Qt::DisplayRole, collectionName, 1, Qt::MatchRecursive); Q_ASSERT(!list.isEmpty()); QModelIndex changedIndex = list.first(); Akonadi::Collection changedCol = changedIndex.data(Akonadi::EntityTreeModel::CollectionRole).value(); changedCol.setContentMimeTypes(QStringList() << QStringLiteral("foobar")); QString parentCollection = changedIndex.parent().data().toString(); FakeCollectionChangedCommand *changeCommand = new FakeCollectionChangedCommand(changedCol, serverData); m_modelSpy->startSpying(); serverData->setCommands(QList() << changeCommand); QList expectedSignals; expectedSignals << getExpectedSignal(RowsAboutToBeRemoved, changedIndex.row(), changedIndex.row(), parentCollection, QVariantList() << collectionName); expectedSignals << getExpectedSignal(RowsRemoved, changedIndex.row(), changedIndex.row(), parentCollection, QVariantList() << collectionName); m_modelSpy->setExpectedSignals(expectedSignals); serverData->processNotifications(); // Give the model a chance to run the event loop to process the signals. QTest::qWait(0); QVERIFY(m_modelSpy->isEmpty()); } #include "entitytreemodeltest.moc" QTEST_MAIN(EntityTreeModelTest) diff --git a/autotests/libs/modelspy.cpp b/autotests/libs/modelspy.cpp index 71ad003a0..ec16f2708 100644 --- a/autotests/libs/modelspy.cpp +++ b/autotests/libs/modelspy.cpp @@ -1,226 +1,220 @@ /* Copyright (c) 2009 Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "modelspy.h" #include #include -ModelSpy::ModelSpy(QObject *parent) - : QObject(parent), QList(), m_isSpying(false) +ModelSpy::ModelSpy(QAbstractItemModel *model, QObject *parent) + : QObject{parent}, m_model{model}, m_isSpying{false} { qRegisterMetaType("QModelIndex"); } bool ModelSpy::isEmpty() const { return QList::isEmpty() && m_expectedSignals.isEmpty(); } -void ModelSpy::setModel(QAbstractItemModel *model) -{ - Q_ASSERT(model); - m_model = model; -} - void ModelSpy::setExpectedSignals(const QList< ExpectedSignal > &expectedSignals) { m_expectedSignals = expectedSignals; } QList ModelSpy::expectedSignals() const { return m_expectedSignals; } void ModelSpy::verifySignal(SignalType type, const QModelIndex &parent, int start, int end) { ExpectedSignal expectedSignal = m_expectedSignals.takeFirst(); QCOMPARE(int(type), int(expectedSignal.signalType)); QCOMPARE(parent.data(), expectedSignal.parentData); QCOMPARE(start, expectedSignal.startRow); QCOMPARE(end, expectedSignal.endRow); if (!expectedSignal.newData.isEmpty()) { // TODO } } void ModelSpy::verifySignal(SignalType type, const QModelIndex &parent, int start, int end, const QModelIndex &destParent, int destStart) { ExpectedSignal expectedSignal = m_expectedSignals.takeFirst(); QCOMPARE(int(type), int(expectedSignal.signalType)); QCOMPARE(start, expectedSignal.startRow); QCOMPARE(end, expectedSignal.endRow); QCOMPARE(parent.data(), expectedSignal.sourceParentData); QCOMPARE(destParent.data(), expectedSignal.parentData); QCOMPARE(destStart, expectedSignal.destRow); QVariantList moveList; QModelIndex _parent = type == RowsAboutToBeMoved ? parent : destParent; int _start = type == RowsAboutToBeMoved ? start : destStart; int _end = type == RowsAboutToBeMoved ? end : destStart + (end - start); for (int row = _start; row <= _end; ++row) { moveList << m_model->index(row, 0, _parent).data(); } QCOMPARE(moveList, expectedSignal.newData); } void ModelSpy::verifySignal(SignalType type, const QModelIndex &topLeft, const QModelIndex &bottomRight) { QCOMPARE(type, DataChanged); ExpectedSignal expectedSignal = m_expectedSignals.takeFirst(); QCOMPARE(int(type), int(expectedSignal.signalType)); QModelIndex parent = topLeft.parent(); //This check won't work for toplevel indexes if (parent.isValid()) { if (expectedSignal.parentData.isValid()) { QCOMPARE(parent.data(), expectedSignal.parentData); } } QCOMPARE(topLeft.row(), expectedSignal.startRow); QCOMPARE(bottomRight.row(), expectedSignal.endRow); for (int i = 0, row = topLeft.row(); row <= bottomRight.row(); ++row, ++i) { QCOMPARE(expectedSignal.newData.at(i), m_model->index(row, 0, parent).data()); } } void ModelSpy::startSpying() { m_isSpying = true; // If a signal is connected to a slot multiple times, the slot gets called multiple times. // As we're doing start and stop spying all the time, we disconnect here first to make sure. disconnect(m_model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int))); disconnect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int))); disconnect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); disconnect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved(QModelIndex,int,int))); disconnect(m_model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); disconnect(m_model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(rowsMoved(QModelIndex,int,int,QModelIndex,int))); disconnect(m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex))); connect(m_model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), SLOT(rowsAboutToBeInserted(QModelIndex,int,int))); connect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(rowsInserted(QModelIndex,int,int))); connect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); connect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(rowsRemoved(QModelIndex,int,int))); connect(m_model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), SLOT(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); connect(m_model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), SLOT(rowsMoved(QModelIndex,int,int,QModelIndex,int))); connect(m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), SLOT(dataChanged(QModelIndex,QModelIndex))); } void ModelSpy::stopSpying() { m_isSpying = false; disconnect(m_model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int))); disconnect(m_model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int))); disconnect(m_model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); disconnect(m_model, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(rowsRemoved(QModelIndex,int,int))); disconnect(m_model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int))); disconnect(m_model, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(rowsMoved(QModelIndex,int,int,QModelIndex,int))); disconnect(m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex))); } void ModelSpy::rowsAboutToBeInserted(const QModelIndex &parent, int start, int end) { if (!m_expectedSignals.isEmpty()) { verifySignal(RowsAboutToBeInserted, parent, start, end); } else { append(QVariantList() << RowsAboutToBeInserted << QVariant::fromValue(parent) << start << end); } } void ModelSpy::rowsInserted(const QModelIndex &parent, int start, int end) { if (!m_expectedSignals.isEmpty()) { verifySignal(RowsInserted, parent, start, end); } else { append(QVariantList() << RowsInserted << QVariant::fromValue(parent) << start << end); } } void ModelSpy::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) { if (!m_expectedSignals.isEmpty()) { verifySignal(RowsAboutToBeRemoved, parent, start, end); } else { append(QVariantList() << RowsAboutToBeRemoved << QVariant::fromValue(parent) << start << end); } } void ModelSpy::rowsRemoved(const QModelIndex &parent, int start, int end) { if (!m_expectedSignals.isEmpty()) { verifySignal(RowsRemoved, parent, start, end); } else { append(QVariantList() << RowsRemoved << QVariant::fromValue(parent) << start << end); } } void ModelSpy::rowsAboutToBeMoved(const QModelIndex &srcParent, int start, int end, const QModelIndex &destParent, int destStart) { if (!m_expectedSignals.isEmpty()) { verifySignal(RowsAboutToBeMoved, srcParent, start, end, destParent, destStart); } else { append(QVariantList() << RowsAboutToBeMoved << QVariant::fromValue(srcParent) << start << end << QVariant::fromValue(destParent) << destStart); } } void ModelSpy::rowsMoved(const QModelIndex &srcParent, int start, int end, const QModelIndex &destParent, int destStart) { if (!m_expectedSignals.isEmpty()) { verifySignal(RowsMoved, srcParent, start, end, destParent, destStart); } else { append(QVariantList() << RowsMoved << QVariant::fromValue(srcParent) << start << end << QVariant::fromValue(destParent) << destStart); } } void ModelSpy::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) { if (!m_expectedSignals.isEmpty()) { verifySignal(DataChanged, topLeft, bottomRight); } else { append(QVariantList() << DataChanged << QVariant::fromValue(topLeft) << QVariant::fromValue(bottomRight)); } } diff --git a/autotests/libs/modelspy.h b/autotests/libs/modelspy.h index ba25e9758..e76f0c566 100644 --- a/autotests/libs/modelspy.h +++ b/autotests/libs/modelspy.h @@ -1,109 +1,107 @@ /* Copyright (c) 2009 Stephen Kelly This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef MODELSPY_H #define MODELSPY_H #include #include #include #include "akonaditestfake_export.h" enum SignalType { NoSignal, RowsAboutToBeInserted, RowsInserted, RowsAboutToBeRemoved, RowsRemoved, RowsAboutToBeMoved, RowsMoved, DataChanged }; struct ExpectedSignal { SignalType signalType; int startRow; int endRow; QVariant parentData; QVariant sourceParentData; int destRow; QVariantList newData; }; Q_DECLARE_METATYPE(QModelIndex) class AKONADITESTFAKE_EXPORT ModelSpy : public QObject, public QList { Q_OBJECT public: - explicit ModelSpy(QObject *parent); - - void setModel(QAbstractItemModel *model); + explicit ModelSpy(QAbstractItemModel *model, QObject *parent = nullptr); bool isEmpty() const; void setExpectedSignals(const QList &expectedSignals); QList expectedSignals() const; void verifySignal(SignalType type, const QModelIndex &parent, int start, int end); void verifySignal(SignalType type, const QModelIndex &parent, int start, int end, const QModelIndex &destParent, int destStart); void verifySignal(SignalType type, const QModelIndex &topLeft, const QModelIndex &bottomRight); void startSpying(); void stopSpying(); bool isSpying() { return m_isSpying; } protected Q_SLOTS: void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end); void rowsInserted(const QModelIndex &parent, int start, int end); void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); void rowsRemoved(const QModelIndex &parent, int start, int end); void rowsAboutToBeMoved(const QModelIndex &srcParent, int start, int end, const QModelIndex &destParent, int destStart); void rowsMoved(const QModelIndex &srcParent, int start, int end, const QModelIndex &destParent, int destStart); void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); private: - QAbstractItemModel *m_model = nullptr; + QAbstractItemModel *const m_model = nullptr; bool m_isSpying; QList m_expectedSignals; }; #ifdef _MSC_VER // FIXME: This is a very lousy implementation to make MSVC happy. Apparently // MSVC insists on instantiating QSet QList::toSet() and complains about // not having qHash() overload for QVariant(List) in QSet // // FIXME: This is good enough for ModelSpy, but should never ever be used in // regular code. inline uint qHash(const QVariant &v) { return v.userType() + v.toUInt(); } inline uint qHash(const QVariantList &list) { return qHashRange(list.cbegin(), list.cend()); } #endif #endif diff --git a/autotests/libs/tagmodeltest.cpp b/autotests/libs/tagmodeltest.cpp index 43b9ea1e7..c587381a1 100644 --- a/autotests/libs/tagmodeltest.cpp +++ b/autotests/libs/tagmodeltest.cpp @@ -1,382 +1,380 @@ /* * Copyright 2015 Daniel Vrátil * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include "fakeserverdata.h" #include "fakesession.h" #include "fakemonitor.h" #include "modelspy.h" #include "tagmodel.h" #include "tagmodel_p.h" static const QString serverContent1 = QStringLiteral( "- T PLAIN 'Tag 1' 4" "- - T PLAIN 'Tag 2' 3" "- - - T PLAIN 'Tag 4' 1" "- - T PLAIN 'Tag 3' 2" "- T PLAIN 'Tag 5' 5"); class TagModelTest : public QObject { Q_OBJECT private Q_SLOTS: void initTestCase(); void testInitialFetch(); void testTagAdded_data(); void testTagAdded(); void testTagChanged_data(); void testTagChanged(); void testTagRemoved_data(); void testTagRemoved(); void testTagMoved_data(); void testTagMoved(); private: ExpectedSignal getExpectedSignal(SignalType type, int start, int end, const QVariantList newData) { return getExpectedSignal(type, start, end, QVariant(), newData); } ExpectedSignal getExpectedSignal(SignalType type, int start, int end, const QVariant &parentData = QVariant(), const QVariantList newData = QVariantList()) { ExpectedSignal expectedSignal; expectedSignal.signalType = type; expectedSignal.startRow = start; expectedSignal.endRow = end; expectedSignal.parentData = parentData; expectedSignal.newData = newData; return expectedSignal; } ExpectedSignal getExpectedSignal(SignalType type, int start, int end, const QVariant &sourceParentData, int destRow, const QVariant &destParentData, const QVariantList newData) { ExpectedSignal expectedSignal; expectedSignal.signalType = type; expectedSignal.startRow = start; expectedSignal.endRow = end; expectedSignal.sourceParentData = sourceParentData; expectedSignal.destRow = destRow; expectedSignal.parentData = destParentData; expectedSignal.newData = newData; return expectedSignal; } QPair populateModel(const QString &serverContent) { FakeMonitor *fakeMonitor = new FakeMonitor(this); fakeMonitor->setSession(m_fakeSession); fakeMonitor->setCollectionMonitored(Collection::root()); fakeMonitor->setTypeMonitored(Akonadi::Monitor::Tags); TagModel *model = new TagModel(fakeMonitor, this); - m_modelSpy = new ModelSpy(this); - m_modelSpy->setModel(model); + m_modelSpy = new ModelSpy{model, this}; FakeServerData *serverData = new FakeServerData(model, m_fakeSession, fakeMonitor); QList initialFetchResponse = FakeJobResponse::interpret(serverData, serverContent); serverData->setCommands(initialFetchResponse); // Give the model a chance to populate QTest::qWait(100); return qMakePair(serverData, model); } private: ModelSpy *m_modelSpy = nullptr; FakeSession *m_fakeSession = nullptr; QByteArray m_sessionName; }; void TagModelTest::initTestCase() { m_sessionName = "TagModelTest fake session"; m_fakeSession = new FakeSession(m_sessionName, FakeSession::EndJobsImmediately); m_fakeSession->setAsDefaultSession(); qRegisterMetaType("QModelIndex"); } void TagModelTest::testInitialFetch() { FakeMonitor *fakeMonitor = new FakeMonitor(this); fakeMonitor->setSession(m_fakeSession); fakeMonitor->setCollectionMonitored(Collection::root()); TagModel *model = new TagModel(fakeMonitor, this); FakeServerData *serverData = new FakeServerData(model, m_fakeSession, fakeMonitor); QList initialFetchResponse = FakeJobResponse::interpret(serverData, serverContent1); serverData->setCommands(initialFetchResponse); - m_modelSpy = new ModelSpy(this); - m_modelSpy->setModel(model); + m_modelSpy = new ModelSpy{model, this}; m_modelSpy->startSpying(); QList expectedSignals; expectedSignals << getExpectedSignal(RowsAboutToBeInserted, 0, 0); expectedSignals << getExpectedSignal(RowsInserted, 0, 0); expectedSignals << getExpectedSignal(RowsAboutToBeInserted, 0, 0, QStringLiteral("Tag 1")); expectedSignals << getExpectedSignal(RowsInserted, 0, 0, QStringLiteral("Tag 1")); expectedSignals << getExpectedSignal(RowsAboutToBeInserted, 1, 1, QStringLiteral("Tag 1")); expectedSignals << getExpectedSignal(RowsInserted, 1, 1, QStringLiteral("Tag 1")); expectedSignals << getExpectedSignal(RowsAboutToBeInserted, 0, 0, QStringLiteral("Tag 2")); expectedSignals << getExpectedSignal(RowsInserted, 0, 0, QStringLiteral("Tag 2")); expectedSignals << getExpectedSignal(RowsAboutToBeInserted, 1, 1); expectedSignals << getExpectedSignal(RowsInserted, 1, 1); m_modelSpy->setExpectedSignals(expectedSignals); // Give the model a chance to run the event loop to process the signals. QTest::qWait(10); // We get all the signals we expected. QTRY_VERIFY(m_modelSpy->expectedSignals().isEmpty()); QTest::qWait(10); // We didn't get signals we didn't expect. QVERIFY(m_modelSpy->isEmpty()); } void TagModelTest::testTagAdded_data() { QTest::addColumn("serverContent"); QTest::addColumn("addedTag"); QTest::addColumn("parentTag"); QTest::newRow("add-tag01") << serverContent1 << "new Tag" << "Tag 1"; QTest::newRow("add-tag02") << serverContent1 << "new Tag" << "Tag 2"; QTest::newRow("add-tag03") << serverContent1 << "new Tag" << "Tag 3"; QTest::newRow("add-tag04") << serverContent1 << "new Tag" << "Tag 4"; QTest::newRow("add-tag05") << serverContent1 << "new Tag" << "Tag 5"; } void TagModelTest::testTagAdded() { QFETCH(QString, serverContent); QFETCH(QString, addedTag); QFETCH(QString, parentTag); QPair testDrivers = populateModel(serverContent); FakeServerData *serverData = testDrivers.first; Akonadi::TagModel *model = testDrivers.second; const QModelIndexList list = model->match(model->index(0, 0), Qt::DisplayRole, parentTag, 1, Qt::MatchRecursive); QVERIFY(!list.isEmpty()); const QModelIndex parentIndex = list.first(); const int newRow = model->rowCount(parentIndex); FakeTagAddedCommand *addCommand = new FakeTagAddedCommand(addedTag, parentTag, serverData); m_modelSpy->startSpying(); serverData->setCommands(QList() << addCommand); QList expectedSignals; expectedSignals << getExpectedSignal(RowsAboutToBeInserted, newRow, newRow, parentTag, QVariantList() << addedTag); expectedSignals << getExpectedSignal(RowsInserted, newRow, newRow, parentTag, QVariantList() << addedTag); m_modelSpy->setExpectedSignals(expectedSignals); serverData->processNotifications(); // Give the model a change to run the event loop to process the signals. QTest::qWait(0); QVERIFY(m_modelSpy->isEmpty()); } void TagModelTest::testTagChanged_data() { QTest::addColumn("serverContent"); QTest::addColumn("tagName"); QTest::newRow("change-tag01") << serverContent1 << "Tag 1"; QTest::newRow("change-tag02") << serverContent1 << "Tag 2"; QTest::newRow("change-tag03") << serverContent1 << "Tag 3"; QTest::newRow("change-tag04") << serverContent1 << "Tag 4"; QTest::newRow("change-tag05") << serverContent1 << "Tag 5"; } void TagModelTest::testTagChanged() { QFETCH(QString, serverContent); QFETCH(QString, tagName); const QPair testDrivers = populateModel(serverContent); FakeServerData *serverData = testDrivers.first; Akonadi::TagModel *model = testDrivers.second; const QModelIndexList list = model->match(model->index(0, 0), Qt::DisplayRole, tagName, 1, Qt::MatchRecursive); QVERIFY(!list.isEmpty()); const QModelIndex changedIndex = list.first(); const QString parentTag = changedIndex.parent().data().toString(); const int changedRow = changedIndex.row(); FakeTagChangedCommand *changeCommand = new FakeTagChangedCommand(tagName, parentTag, serverData); m_modelSpy->startSpying(); serverData->setCommands(QList() << changeCommand); QList expectedSignals; expectedSignals << getExpectedSignal(DataChanged, changedRow, changedRow, parentTag, QVariantList() << tagName); m_modelSpy->setExpectedSignals(expectedSignals); serverData->processNotifications(); // Give the model a change to run the event loop to process the signals. QTest::qWait(0); QVERIFY(m_modelSpy->isEmpty()); } void TagModelTest::testTagRemoved_data() { QTest::addColumn("serverContent"); QTest::addColumn("removedTag"); QTest::newRow("remove-tag01") << serverContent1 << "Tag 1"; QTest::newRow("remove-tag02") << serverContent1 << "Tag 2"; QTest::newRow("remove-tag03") << serverContent1 << "Tag 3"; QTest::newRow("remove-tag04") << serverContent1 << "Tag 4"; QTest::newRow("remove-tag05") << serverContent1 << "Tag 5"; } void TagModelTest::testTagRemoved() { QFETCH(QString, serverContent); QFETCH(QString, removedTag); const QPair testDrivers = populateModel(serverContent); FakeServerData *serverData = testDrivers.first; Akonadi::TagModel *model = testDrivers.second; const QModelIndexList list = model->match(model->index(0, 0), Qt::DisplayRole, removedTag, 1, Qt::MatchRecursive); QVERIFY(!list.isEmpty()); const QModelIndex removedIndex = list.first(); const QString parentTag = removedIndex.parent().data().toString(); const int sourceRow = removedIndex.row(); FakeTagRemovedCommand *removeCommand = new FakeTagRemovedCommand(removedTag, parentTag, serverData); m_modelSpy->startSpying(); serverData->setCommands(QList() << removeCommand); QList expectedSignals; expectedSignals << getExpectedSignal(RowsAboutToBeRemoved, sourceRow, sourceRow, parentTag.isEmpty() ? QVariant() : parentTag, QVariantList() << removedTag); expectedSignals << getExpectedSignal(RowsRemoved, sourceRow, sourceRow, parentTag.isEmpty() ? QVariant() : parentTag, QVariantList() << removedTag); m_modelSpy->setExpectedSignals(expectedSignals); serverData->processNotifications(); // Give the model a change to run the event loop to process the signals. QTest::qWait(0); QVERIFY(m_modelSpy->isEmpty()); } void TagModelTest::testTagMoved_data() { QTest::addColumn("serverContent"); QTest::addColumn("changedTag"); QTest::addColumn("newParent"); QTest::newRow("move-tag01") << serverContent1 << "Tag 1" << "Tag 5"; QTest::newRow("move-tag02") << serverContent1 << "Tag 2" << "Tag 5"; QTest::newRow("move-tag03") << serverContent1 << "Tag 3" << "Tag 4"; QTest::newRow("move-tag04") << serverContent1 << "Tag 4" << "Tag 1"; QTest::newRow("move-tag05") << serverContent1 << "Tag 5" << "Tag 2"; QTest::newRow("move-tag06") << serverContent1 << "Tag 3" << QString(); QTest::newRow("move-tag07") << serverContent1 << "Tag 2" << QString(); } void TagModelTest::testTagMoved() { QFETCH(QString, serverContent); QFETCH(QString, changedTag); QFETCH(QString, newParent); const QPair testDrivers = populateModel(serverContent); FakeServerData *serverData = testDrivers.first; Akonadi::TagModel *model = testDrivers.second; QModelIndexList list = model->match(model->index(0, 0), Qt::DisplayRole, changedTag, 1, Qt::MatchRecursive); QVERIFY(!list.isEmpty()); const QModelIndex changedIndex = list.first(); const QString parentTag = changedIndex.parent().data().toString(); const int sourceRow = changedIndex.row(); QModelIndex newParentIndex; if (!newParent.isEmpty()) { list = model->match(model->index(0, 0), Qt::DisplayRole, newParent, 1, Qt::MatchRecursive); QVERIFY(!list.isEmpty()); newParentIndex = list.first(); } const int destRow = model->rowCount(newParentIndex); FakeTagMovedCommand *moveCommand = new FakeTagMovedCommand(changedTag, parentTag, newParent, serverData); m_modelSpy->startSpying(); serverData->setCommands(QList() << moveCommand); QList expectedSignals; expectedSignals << getExpectedSignal(RowsAboutToBeMoved, sourceRow, sourceRow, parentTag.isEmpty() ? QVariant() : parentTag, destRow, newParent.isEmpty() ? QVariant() : newParent, QVariantList() << changedTag); expectedSignals << getExpectedSignal(RowsMoved, sourceRow, sourceRow, parentTag.isEmpty() ? QVariant() : parentTag, destRow, newParent.isEmpty() ? QVariant() : newParent, QVariantList() << changedTag); m_modelSpy->setExpectedSignals(expectedSignals); serverData->processNotifications(); // Give the model a change to run the event loop to process the signals. QTest::qWait(0); QVERIFY(m_modelSpy->isEmpty()); } #include "tagmodeltest.moc" QTEST_MAIN(TagModelTest)