File indexing completed on 2024-11-10 04:40:10

0001 /*
0002     SPDX-FileCopyrightText: 2013 Christian Mollekopf <mollekopf@kolabsys.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include <QObject>
0008 
0009 #include "changerecorder_p.h"
0010 #include "collectioncreatejob.h"
0011 #include "collectiondeletejob.h"
0012 #include "control.h"
0013 #include "entitytreemodel.h"
0014 #include "entitytreemodel_p.h"
0015 #include "itemcreatejob.h"
0016 #include "monitor_p.h"
0017 #include "qtest_akonadi.h"
0018 
0019 using namespace Akonadi;
0020 
0021 class ModelSignalSpy : public QObject
0022 {
0023     Q_OBJECT
0024 public:
0025     explicit ModelSignalSpy(QAbstractItemModel &model)
0026     {
0027         connect(&model, &QAbstractItemModel::rowsInserted, this, &ModelSignalSpy::onRowsInserted);
0028         connect(&model, &QAbstractItemModel::rowsRemoved, this, &ModelSignalSpy::onRowsRemoved);
0029         connect(&model, &QAbstractItemModel::rowsMoved, this, &ModelSignalSpy::onRowsMoved);
0030         connect(&model, &QAbstractItemModel::dataChanged, this, &ModelSignalSpy::onDataChanged);
0031         connect(&model, &QAbstractItemModel::layoutChanged, this, &ModelSignalSpy::onLayoutChanged);
0032         connect(&model, &QAbstractItemModel::modelReset, this, &ModelSignalSpy::onModelReset);
0033     }
0034 
0035     QStringList mSignals;
0036     QModelIndex parent;
0037     int start;
0038     int end;
0039 
0040 public Q_SLOTS:
0041     void onRowsInserted(const QModelIndex &p, int s, int e)
0042     {
0043         qDebug() << "rowsInserted( parent =" << p << ", start = " << s << ", end = " << e << ", data = " << p.data().toString() << ")";
0044         mSignals << QStringLiteral("rowsInserted");
0045         parent = p;
0046         start = s;
0047         end = e;
0048     }
0049     void onRowsRemoved(const QModelIndex &p, int s, int e)
0050     {
0051         qDebug() << "rowsRemoved( parent = " << p << ", start = " << s << ", end = " << e << ")";
0052         mSignals << QStringLiteral("rowsRemoved");
0053         parent = p;
0054         start = s;
0055         end = e;
0056     }
0057     void onRowsMoved(const QModelIndex & /*unused*/, int /*unused*/, int /*unused*/, const QModelIndex & /*unused*/, int /*unused*/)
0058     {
0059         mSignals << QStringLiteral("rowsMoved");
0060     }
0061     void onDataChanged(const QModelIndex &tl, const QModelIndex &br)
0062     {
0063         qDebug() << "dataChanged( topLeft =" << tl << "(" << tl.data().toString() << "), bottomRight =" << br << "(" << br.data().toString() << ") )";
0064         mSignals << QStringLiteral("dataChanged");
0065     }
0066     void onLayoutChanged()
0067     {
0068         mSignals << QStringLiteral("layoutChanged");
0069     }
0070     void onModelReset()
0071     {
0072         mSignals << QStringLiteral("modelReset");
0073     }
0074 };
0075 
0076 class InspectableETM : public EntityTreeModel
0077 {
0078 public:
0079     explicit InspectableETM(ChangeRecorder *monitor, QObject *parent = nullptr)
0080         : EntityTreeModel(monitor, parent)
0081     {
0082     }
0083     EntityTreeModelPrivate *etmPrivate()
0084     {
0085         return d_ptr.get();
0086     }
0087 };
0088 
0089 QModelIndex getIndex(const QString &string, EntityTreeModel *model)
0090 {
0091     QModelIndexList list = model->match(model->index(0, 0), Qt::DisplayRole, string, 1, Qt::MatchRecursive);
0092     if (list.isEmpty()) {
0093         return QModelIndex();
0094     }
0095     return list.first();
0096 }
0097 
0098 Akonadi::Collection createCollection(const QString &name, const Akonadi::Collection &parent, bool enabled = true, const QStringList &mimeTypes = QStringList())
0099 {
0100     Akonadi::Collection col;
0101     col.setParentCollection(parent);
0102     col.setName(name);
0103     col.setEnabled(enabled);
0104     col.setContentMimeTypes(mimeTypes);
0105 
0106     auto create = new CollectionCreateJob(col);
0107     create->exec();
0108     if (create->error()) {
0109         qWarning() << create->errorString();
0110     }
0111     return create->collection();
0112 }
0113 
0114 /**
0115  * This is a test for the initial population of the ETM.
0116  */
0117 class EtmPopulationTest : public QObject
0118 {
0119     Q_OBJECT
0120 
0121 private Q_SLOTS:
0122     void initTestCase();
0123     void testMonitoringCollectionsPreset();
0124     void testMonitoringCollections();
0125     void testFullPopulation();
0126     void testAddMonitoringCollections();
0127     void testRemoveMonitoringCollections();
0128     void testDisplayFilter();
0129     void testLoadingOfHiddenCollection();
0130 
0131 private:
0132     Collection res;
0133     QString mainCollectionName;
0134     Collection monitorCol;
0135     Collection col1;
0136     Collection col2;
0137     Collection col3;
0138     Collection col4;
0139 };
0140 
0141 void EtmPopulationTest::initTestCase()
0142 {
0143     qRegisterMetaType<Akonadi::Collection::Id>("Akonadi::Collection::Id");
0144     AkonadiTest::checkTestIsIsolated();
0145     AkonadiTest::setAllResourcesOffline();
0146 
0147     res = Collection(AkonadiTest::collectionIdFromPath(QStringLiteral("res3")));
0148 
0149     mainCollectionName = QStringLiteral("main");
0150     monitorCol = createCollection(mainCollectionName, res);
0151     QVERIFY(monitorCol.isValid());
0152     col1 = createCollection(QStringLiteral("col1"), monitorCol);
0153     QVERIFY(col1.isValid());
0154     col2 = createCollection(QStringLiteral("col2"), monitorCol);
0155     QVERIFY(col2.isValid());
0156     col3 = createCollection(QStringLiteral("col3"), monitorCol);
0157     QVERIFY(col3.isValid());
0158     col4 = createCollection(QStringLiteral("col4"), col2);
0159     QVERIFY(col4.isValid());
0160 }
0161 
0162 void EtmPopulationTest::testMonitoringCollectionsPreset()
0163 {
0164     auto changeRecorder = new ChangeRecorder(this);
0165     changeRecorder->setCollectionMonitored(col1, true);
0166     changeRecorder->setCollectionMonitored(col2, true);
0167     AkonadiTest::akWaitForSignal(changeRecorder, &Monitor::monitorReady);
0168     auto model = new InspectableETM(changeRecorder, this);
0169     model->setItemPopulationStrategy(EntityTreeModel::ImmediatePopulation);
0170     model->setCollectionFetchStrategy(EntityTreeModel::FetchCollectionsRecursive);
0171 
0172     QTRY_VERIFY(model->isCollectionTreeFetched());
0173     QTRY_VERIFY(getIndex(QStringLiteral("col1"), model).isValid());
0174     QTRY_VERIFY(getIndex(QStringLiteral("col2"), model).isValid());
0175     QTRY_VERIFY(getIndex(mainCollectionName, model).isValid());
0176     QVERIFY(!getIndex(QStringLiteral("col3"), model).isValid());
0177     QVERIFY(getIndex(QStringLiteral("col4"), model).isValid());
0178 
0179     QTRY_VERIFY(getIndex(QStringLiteral("col1"), model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0180     QTRY_VERIFY(getIndex(QStringLiteral("col2"), model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0181     QTRY_VERIFY(!getIndex(mainCollectionName, model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0182     QTRY_VERIFY(getIndex(QStringLiteral("col4"), model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0183 }
0184 
0185 void EtmPopulationTest::testMonitoringCollections()
0186 {
0187     auto changeRecorder = new ChangeRecorder(this);
0188     AkonadiTest::akWaitForSignal(changeRecorder, &Monitor::monitorReady);
0189     auto model = new InspectableETM(changeRecorder, this);
0190     model->setItemPopulationStrategy(EntityTreeModel::ImmediatePopulation);
0191     model->setCollectionFetchStrategy(EntityTreeModel::FetchCollectionsRecursive);
0192     Akonadi::Collection::List monitored;
0193     monitored << col1 << col2;
0194     model->setCollectionsMonitored(monitored);
0195 
0196     QTRY_VERIFY(model->isCollectionTreeFetched());
0197     QVERIFY(getIndex(QStringLiteral("col1"), model).isValid());
0198     QVERIFY(getIndex(QStringLiteral("col2"), model).isValid());
0199     QTRY_VERIFY(getIndex(mainCollectionName, model).isValid());
0200     QVERIFY(!getIndex(QStringLiteral("col3"), model).isValid());
0201     QVERIFY(getIndex(QStringLiteral("col4"), model).isValid());
0202 
0203     QTRY_VERIFY(getIndex(QStringLiteral("col1"), model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0204     QTRY_VERIFY(getIndex(QStringLiteral("col2"), model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0205     QTRY_VERIFY(!getIndex(mainCollectionName, model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0206     QTRY_VERIFY(getIndex(QStringLiteral("col4"), model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0207 }
0208 
0209 void EtmPopulationTest::testFullPopulation()
0210 {
0211     auto changeRecorder = new ChangeRecorder(this);
0212     // changeRecorder->setCollectionMonitored(Akonadi::Collection::root());
0213     changeRecorder->setAllMonitored(true);
0214     AkonadiTest::akWaitForSignal(changeRecorder, &Monitor::monitorReady);
0215     auto model = new InspectableETM(changeRecorder, this);
0216     model->setItemPopulationStrategy(EntityTreeModel::ImmediatePopulation);
0217     model->setCollectionFetchStrategy(EntityTreeModel::FetchCollectionsRecursive);
0218 
0219     QTRY_VERIFY(model->isCollectionTreeFetched());
0220     QVERIFY(getIndex(QStringLiteral("col1"), model).isValid());
0221     QVERIFY(getIndex(QStringLiteral("col2"), model).isValid());
0222     QVERIFY(getIndex(mainCollectionName, model).isValid());
0223     QVERIFY(getIndex(QStringLiteral("col3"), model).isValid());
0224     QVERIFY(getIndex(QStringLiteral("col4"), model).isValid());
0225 
0226     QTRY_VERIFY(getIndex(QStringLiteral("col1"), model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0227     QTRY_VERIFY(getIndex(QStringLiteral("col2"), model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0228     QTRY_VERIFY(getIndex(mainCollectionName, model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0229     QTRY_VERIFY(getIndex(QStringLiteral("col4"), model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0230 }
0231 
0232 void EtmPopulationTest::testAddMonitoringCollections()
0233 {
0234     auto changeRecorder = new ChangeRecorder(this);
0235     changeRecorder->setCollectionMonitored(col1, true);
0236     changeRecorder->setCollectionMonitored(col2, true);
0237     AkonadiTest::akWaitForSignal(changeRecorder, &Monitor::monitorReady);
0238     auto model = new InspectableETM(changeRecorder, this);
0239     model->setItemPopulationStrategy(EntityTreeModel::ImmediatePopulation);
0240     model->setCollectionFetchStrategy(EntityTreeModel::FetchCollectionsRecursive);
0241 
0242     QTRY_VERIFY(model->isCollectionTreeFetched());
0243     // The main collection may be loaded a little later since it is in the fetchAncestors path
0244     QTRY_VERIFY(getIndex(mainCollectionName, model).isValid());
0245 
0246     model->setCollectionMonitored(col3, true);
0247 
0248     QVERIFY(getIndex(QStringLiteral("col1"), model).isValid());
0249     QVERIFY(getIndex(QStringLiteral("col2"), model).isValid());
0250     QTRY_VERIFY(getIndex(QStringLiteral("col3"), model).isValid());
0251     QVERIFY(getIndex(QStringLiteral("col4"), model).isValid());
0252     QVERIFY(getIndex(mainCollectionName, model).isValid());
0253 
0254     QTRY_VERIFY(getIndex(QStringLiteral("col1"), model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0255     QTRY_VERIFY(getIndex(QStringLiteral("col2"), model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0256     QTRY_VERIFY(getIndex(QStringLiteral("col3"), model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0257     QTRY_VERIFY(!getIndex(mainCollectionName, model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0258     QTRY_VERIFY(getIndex(QStringLiteral("col4"), model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0259 }
0260 
0261 void EtmPopulationTest::testRemoveMonitoringCollections()
0262 {
0263     auto changeRecorder = new ChangeRecorder(this);
0264     changeRecorder->setCollectionMonitored(col1, true);
0265     changeRecorder->setCollectionMonitored(col2, true);
0266     AkonadiTest::akWaitForSignal(changeRecorder, &Monitor::monitorReady);
0267     auto model = new InspectableETM(changeRecorder, this);
0268     model->setItemPopulationStrategy(EntityTreeModel::ImmediatePopulation);
0269     model->setCollectionFetchStrategy(EntityTreeModel::FetchCollectionsRecursive);
0270 
0271     QTRY_VERIFY(model->isCollectionTreeFetched());
0272     // The main collection may be loaded a little later since it is in the fetchAncestors path
0273     QTRY_VERIFY(getIndex(mainCollectionName, model).isValid());
0274 
0275     model->setCollectionMonitored(col2, false);
0276 
0277     QVERIFY(getIndex(QStringLiteral("col1"), model).isValid());
0278     QVERIFY(!getIndex(QStringLiteral("col2"), model).isValid());
0279     QVERIFY(getIndex(mainCollectionName, model).isValid());
0280     QVERIFY(!getIndex(QStringLiteral("col3"), model).isValid());
0281     QVERIFY(!getIndex(QStringLiteral("col4"), model).isValid());
0282 
0283     QTRY_VERIFY(getIndex(QStringLiteral("col1"), model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0284     QTRY_VERIFY(!getIndex(QStringLiteral("col2"), model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0285     QTRY_VERIFY(!getIndex(mainCollectionName, model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0286     QTRY_VERIFY(!getIndex(QStringLiteral("col4"), model).data(Akonadi::EntityTreeModel::IsPopulatedRole).toBool());
0287 }
0288 
0289 void EtmPopulationTest::testDisplayFilter()
0290 {
0291     Collection col5 = createCollection(QStringLiteral("col5"), monitorCol, false);
0292     QVERIFY(col5.isValid());
0293 
0294     auto changeRecorder = new ChangeRecorder(this);
0295     auto model = new InspectableETM(changeRecorder, this);
0296     AkonadiTest::akWaitForSignal(changeRecorder, &Monitor::monitorReady);
0297     model->setItemPopulationStrategy(EntityTreeModel::ImmediatePopulation);
0298     model->setCollectionFetchStrategy(EntityTreeModel::FetchCollectionsRecursive);
0299     model->setListFilter(Akonadi::CollectionFetchScope::Display);
0300 
0301     QTRY_VERIFY(model->isCollectionTreeFetched());
0302     QVERIFY(getIndex(mainCollectionName, model).isValid());
0303     QVERIFY(getIndex(QStringLiteral("col1"), model).isValid());
0304     QVERIFY(getIndex(QStringLiteral("col2"), model).isValid());
0305     QVERIFY(getIndex(QStringLiteral("col3"), model).isValid());
0306     QVERIFY(getIndex(QStringLiteral("col4"), model).isValid());
0307     QVERIFY(!getIndex(QStringLiteral("col5"), model).isValid());
0308 
0309     auto deleteJob = new Akonadi::CollectionDeleteJob(col5);
0310     AKVERIFYEXEC(deleteJob);
0311 }
0312 
0313 /*
0314  * Col5 and it's ancestors should be included although the ancestors don't match the mimetype filter.
0315  */
0316 void EtmPopulationTest::testLoadingOfHiddenCollection()
0317 {
0318     Collection col5 = createCollection(QStringLiteral("col5"), monitorCol, false, QStringList() << QStringLiteral("application/test"));
0319     QVERIFY(col5.isValid());
0320 
0321     auto changeRecorder = new ChangeRecorder(this);
0322     changeRecorder->setMimeTypeMonitored(QStringLiteral("application/test"), true);
0323     AkonadiTest::akWaitForSignal(changeRecorder, &Monitor::monitorReady);
0324     auto model = new InspectableETM(changeRecorder, this);
0325     model->setItemPopulationStrategy(EntityTreeModel::ImmediatePopulation);
0326     model->setCollectionFetchStrategy(EntityTreeModel::FetchCollectionsRecursive);
0327 
0328     QTRY_VERIFY(model->isCollectionTreeFetched());
0329     QVERIFY(getIndex(QStringLiteral("col5"), model).isValid());
0330 
0331     auto deleteJob = new Akonadi::CollectionDeleteJob(col5);
0332     AKVERIFYEXEC(deleteJob);
0333 }
0334 
0335 #include "etmpopulationtest.moc"
0336 
0337 QTEST_AKONADIMAIN(EtmPopulationTest)