File indexing completed on 2024-04-14 03:52:44

0001 /*
0002     This file is part of the KDE project
0003     SPDX-FileCopyrightText: 2007 Kevin Ottens <ervin@kde.org>
0004 
0005     SPDX-License-Identifier: GPL-2.0-only
0006 */
0007 
0008 #include <QDBusInterface>
0009 #include <QDebug>
0010 #include <QObject>
0011 #include <QSignalSpy>
0012 #include <QTemporaryDir>
0013 #include <QTest>
0014 
0015 #include <KBookmark>
0016 #include <KBookmarkManager>
0017 #include <KConfig>
0018 #include <KConfigGroup>
0019 #include <KProtocolInfo>
0020 #include <QStandardPaths>
0021 #include <kfileplacesmodel.h>
0022 #include <solid/device.h>
0023 
0024 #include <stdlib.h>
0025 
0026 Q_DECLARE_METATYPE(KFilePlacesModel::GroupType)
0027 
0028 // Avoid QHash randomization so that the order of the devices is stable
0029 static void seedInit()
0030 {
0031     qputenv("QT_HASH_SEED", "0");
0032     // This env var has no effect because this comes too late. qCpuFeatures() was already called by
0033     // a Q_CONSTRUCTOR_FUNCTION inside QtGui (see image/qimage_conversions.cpp). Argh. QTBUG-47566.
0034     qputenv("QT_NO_CPU_FEATURE", "sse4.2");
0035 }
0036 Q_CONSTRUCTOR_FUNCTION(seedInit)
0037 
0038 class KFilePlacesModelTest : public QObject
0039 {
0040     Q_OBJECT
0041 
0042 private Q_SLOTS:
0043     void initTestCase();
0044     void cleanupTestCase();
0045 
0046     void testInitialList();
0047     void testAddingInLaterVersion_data();
0048     void testAddingInLaterVersion();
0049     void testReparse();
0050     void testInternalBookmarksHaveIds();
0051     void testHiding();
0052     void testMove();
0053     void testPlacesLifecycle();
0054     void testDevicePlugging();
0055     void testDragAndDrop();
0056     void testDeviceSetupTeardown();
0057     void testEnableBaloo();
0058     void testRemoteUrls_data();
0059     void testRemoteUrls();
0060     void testRefresh();
0061     void testConvertedUrl_data();
0062     void testConvertedUrl();
0063     void testBookmarkObject();
0064     void testDataChangedSignal();
0065     void testIconRole_data();
0066     void testIconRole();
0067     void testMoveFunction();
0068     void testPlaceGroupHidden();
0069     void testPlaceGroupHiddenVsPlaceChildShown();
0070     void testPlaceGroupHiddenAndShownWithHiddenChild();
0071     void testPlaceGroupHiddenGroupIndexesIntegrity();
0072     void testPlaceGroupHiddenSignal();
0073     void testPlaceGroupHiddenRole();
0074     void testSupportedSchemes();
0075 
0076 private:
0077     QStringList placesUrls(KFilePlacesModel *model = nullptr) const;
0078     QDBusInterface *fakeManager();
0079     QDBusInterface *fakeDevice(const QString &udi);
0080     void createPlacesModels();
0081 
0082     KFilePlacesModel *m_places;
0083     KFilePlacesModel *m_places2; // To check that they always stay in sync
0084     // actually supposed to work across processes,
0085     // but much harder to test
0086 
0087     QMap<QString, QDBusInterface *> m_interfacesMap;
0088     QTemporaryDir m_tmpHome;
0089     bool m_hasRecentlyUsedKio;
0090 };
0091 
0092 static QString bookmarksFile()
0093 {
0094     return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/user-places.xbel");
0095 }
0096 
0097 void KFilePlacesModelTest::initTestCase()
0098 {
0099     QVERIFY(m_tmpHome.isValid());
0100     qputenv("HOME", m_tmpHome.path().toUtf8()); // use a empty home dir
0101 
0102     QStandardPaths::setTestModeEnabled(true);
0103 
0104     // Ensure we'll have a clean bookmark file to start
0105     QFile::remove(bookmarksFile());
0106 
0107     // disable baloo by default
0108     KConfig config(QStringLiteral("baloofilerc"));
0109     KConfigGroup basicSettings = config.group(QStringLiteral("Basic Settings"));
0110     basicSettings.writeEntry("Indexing-Enabled", false);
0111     config.sync();
0112 
0113     qRegisterMetaType<QModelIndex>();
0114     qRegisterMetaType<KFilePlacesModel::GroupType>();
0115 
0116     const QString fakeHw = QFINDTESTDATA("fakecomputer.xml");
0117     QVERIFY(!fakeHw.isEmpty());
0118     qputenv("SOLID_FAKEHW", QFile::encodeName(fakeHw));
0119     m_hasRecentlyUsedKio = qEnvironmentVariableIsSet("KDE_FULL_SESSION") && KProtocolInfo::isKnownProtocol(QStringLiteral("recentlyused"));
0120 
0121     createPlacesModels();
0122 }
0123 
0124 void KFilePlacesModelTest::createPlacesModels()
0125 {
0126     KBookmarkManager mgr(bookmarksFile());
0127     QSignalSpy spy(&mgr, &KBookmarkManager::changed);
0128     m_places = new KFilePlacesModel();
0129     m_places2 = new KFilePlacesModel();
0130 
0131     // When the xbel file is empty, KFilePlacesModel fills it with 3 default items
0132     // 5 when KIO worker recentlyused:/ is installed
0133     QCOMPARE(m_places->rowCount(), m_hasRecentlyUsedKio ? 5 : 3);
0134 
0135     QVERIFY(spy.wait());
0136 
0137     // Devices have a delayed loading. Waiting for KDirWatch also waits for that to happen
0138     QCOMPARE(m_places->rowCount(), m_hasRecentlyUsedKio ? 10 : 8);
0139 }
0140 
0141 void KFilePlacesModelTest::cleanupTestCase()
0142 {
0143     delete m_places;
0144     delete m_places2;
0145     qDeleteAll(m_interfacesMap);
0146     QFile::remove(bookmarksFile());
0147 }
0148 
0149 QStringList KFilePlacesModelTest::placesUrls(KFilePlacesModel *model) const
0150 {
0151     KFilePlacesModel *currentModel = model;
0152     if (!currentModel) {
0153         currentModel = m_places;
0154     }
0155     QStringList urls;
0156     for (int row = 0; row < currentModel->rowCount(); ++row) {
0157         QModelIndex index = currentModel->index(row, 0);
0158         urls << currentModel->url(index).toDisplayString(QUrl::PreferLocalFile);
0159     }
0160     return urls;
0161 }
0162 
0163 /* clang-format off */
0164 #define CHECK_PLACES_URLS(urls) \
0165     if (placesUrls() != urls) { \
0166         qDebug() << "Expected:" << urls; \
0167         qDebug() << "Got:" << placesUrls(); \
0168         QCOMPARE(placesUrls(), urls); \
0169     } \
0170     for (int row = 0; row < urls.size(); ++row) { \
0171         QModelIndex index = m_places->index(row, 0); \
0172         \
0173         QCOMPARE(m_places->url(index).toString(), QUrl::fromUserInput(urls[row]).toString()); \
0174         QCOMPARE(m_places->data(index, KFilePlacesModel::UrlRole).toUrl(), QUrl(m_places->url(index))); \
0175         \
0176         index = m_places2->index(row, 0);                                                               \
0177         \
0178         QCOMPARE(m_places2->url(index).toString(), QUrl::fromUserInput(urls[row]).toString()); \
0179         QCOMPARE(m_places2->data(index, KFilePlacesModel::UrlRole).toUrl(), QUrl(m_places2->url(index))); \
0180     } \
0181     \
0182     QCOMPARE(urls.size(), m_places->rowCount()); \
0183     QCOMPARE(urls.size(), m_places2->rowCount());
0184 /*clang-format on */
0185 
0186 QDBusInterface *KFilePlacesModelTest::fakeManager()
0187 {
0188     return fakeDevice(QStringLiteral("/org/kde/solid/fakehw"));
0189 }
0190 
0191 QDBusInterface *KFilePlacesModelTest::fakeDevice(const QString &udi)
0192 {
0193     QDBusInterface *interface = m_interfacesMap[udi];
0194     if (interface) {
0195         return interface;
0196     }
0197 
0198     QDBusInterface *iface = new QDBusInterface(QDBusConnection::sessionBus().baseService(), udi);
0199     m_interfacesMap[udi] = iface;
0200 
0201     return iface;
0202 }
0203 
0204 static const QStringList initialListOfPlaces()
0205 {
0206     return QStringList{QDir::homePath(), QStringLiteral("trash:/")};
0207 }
0208 
0209 static const QStringList initialListOfShared()
0210 {
0211     return QStringList{QStringLiteral("remote:/"), QStringLiteral("/media/nfs")};
0212 }
0213 
0214 static const QStringList initialListOfRecent()
0215 {
0216     auto list = QStringList();
0217     if (qEnvironmentVariableIsSet("KDE_FULL_SESSION") && KProtocolInfo::isKnownProtocol(QStringLiteral("recentlyused"))) {
0218         list << QStringLiteral("recentlyused:/files");
0219         list << QStringLiteral("recentlyused:/locations");
0220     }
0221     return list;
0222 }
0223 
0224 static const QStringList initialListOfDevices()
0225 {
0226     return QStringList{QStringLiteral("/foreign")};
0227 }
0228 
0229 static const QStringList initialListOfRemovableDevices()
0230 {
0231     return QStringList{QStringLiteral("/media/floppy0"), QStringLiteral("/media/XO-Y4"), QStringLiteral("/media/cdrom")};
0232 }
0233 
0234 static const QStringList initialListOfUrls()
0235 {
0236     return QStringList() << initialListOfPlaces() << initialListOfShared() << initialListOfRecent() << initialListOfDevices()
0237                          << initialListOfRemovableDevices();
0238 }
0239 
0240 void KFilePlacesModelTest::testInitialList()
0241 {
0242     const QStringList urls = initialListOfUrls();
0243     CHECK_PLACES_URLS(urls);
0244 }
0245 
0246 void KFilePlacesModelTest::testAddingInLaterVersion_data()
0247 {
0248     QTest::addColumn<QByteArray>("contents");
0249     QTest::addColumn<QStringList>("expectedUrls");
0250 
0251     // Create a places file with only Home in it, and no version number
0252     static const char contentsPart1[] =
0253         "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
0254         "<xbel xmlns:bookmark=\"http://www.freedesktop.org/standards/desktop-bookmarks\">\n";
0255 
0256     static const char versionXml[] =
0257         "  <info>\n"
0258         "   <metadata owner=\"http://www.kde.org\">\n"
0259         "    <kde_places_version>2</kde_places_version>\n"
0260         "   </metadata>\n"
0261         "  </info>\n";
0262 
0263     static const char contentsPart2[] =
0264         " <bookmark href=\"trash:/\">\n"
0265         "  <title>Home</title>\n"
0266         "  <info>\n"
0267         "   <metadata owner=\"http://freedesktop.org\">\n"
0268         "    <bookmark:icon name=\"user-home\"/>\n"
0269         "   </metadata>\n"
0270         "   <metadata owner=\"http://www.kde.org\">\n"
0271         "    <ID>1481703882/0</ID>\n"
0272         "    <isSystemItem>true</isSystemItem>\n"
0273         "   </metadata>\n"
0274         "  </info>\n"
0275         " </bookmark>\n"
0276         "</xbel>";
0277 
0278     // No version key: KFilePlacesModel will add the missing entries: home and remote
0279     // Just not in the usual order
0280     QStringList expectedWithReorder = initialListOfUrls();
0281     expectedWithReorder.move(1, 0);
0282     QTest::newRow("just_home_no_version") << (QByteArray(contentsPart1) + contentsPart2) << expectedWithReorder;
0283 
0284     // Existing version key: home and remote were removed by the user, leave them out
0285     QStringList expectedUrls{QStringLiteral("trash:/")};
0286     expectedUrls << initialListOfShared() << initialListOfRecent() << initialListOfDevices() << initialListOfRemovableDevices();
0287     QVERIFY(expectedUrls.removeOne(QStringLiteral("remote:/")));
0288     QTest::newRow("just_home_version_2") << (QByteArray(contentsPart1) + versionXml + contentsPart2) << expectedUrls;
0289 }
0290 
0291 void KFilePlacesModelTest::testAddingInLaterVersion()
0292 {
0293     QFETCH(QByteArray, contents);
0294     QFETCH(QStringList, expectedUrls);
0295 
0296     // Avoid interference
0297     delete m_places;
0298     delete m_places2;
0299     QCoreApplication::processEvents();
0300 
0301     KBookmarkManager mgr(bookmarksFile());
0302 
0303     auto cleanupFunc = [this, &mgr]() {
0304         QFile::remove(bookmarksFile());
0305         // let KDirWatch process the deletion
0306         QTRY_VERIFY(mgr.root().first().isNull());
0307         createPlacesModels();
0308         testInitialList();
0309     };
0310     struct Cleanup {
0311         explicit Cleanup(const std::function<void()> &f)
0312             : func(f)
0313         {
0314         }
0315         ~Cleanup()
0316         {
0317             func();
0318         }
0319         std::function<void()> func;
0320     } cleanup(cleanupFunc);
0321 
0322     QTest::qWait(1000); // for KDirWatch
0323     QSignalSpy spy(&mgr, &KBookmarkManager::changed);
0324 
0325     // WHEN
0326     QFile file(bookmarksFile());
0327     QVERIFY(file.open(QIODevice::WriteOnly));
0328     file.write(contents);
0329     file.close();
0330     QVERIFY(spy.wait());
0331 
0332     // THEN
0333     KFilePlacesModel model;
0334     QCoreApplication::processEvents(); // Devices have a delayed loading
0335 
0336     if (placesUrls(&model) != expectedUrls) {
0337         qDebug() << "Expected:" << expectedUrls;
0338         qDebug() << "Got:" << placesUrls(&model);
0339         QCOMPARE(placesUrls(&model), expectedUrls);
0340     }
0341 }
0342 
0343 void KFilePlacesModelTest::testReparse()
0344 {
0345     // add item
0346     m_places->addPlace(QStringLiteral("foo"), QUrl::fromLocalFile(QStringLiteral("/foo")), QString(), QString());
0347 
0348     QStringList urls = initialListOfUrls();
0349     // it will be added at the end of places section
0350     urls.insert(2, QStringLiteral("/foo"));
0351     CHECK_PLACES_URLS(urls);
0352 
0353     // reparse the bookmark file
0354     KBookmarkManager bookmarkManager(bookmarksFile());
0355 
0356     // check if they are the same
0357     CHECK_PLACES_URLS(urls);
0358 
0359     // try to remove item
0360     m_places->removePlace(m_places->index(2, 0));
0361 
0362     urls = initialListOfUrls();
0363     CHECK_PLACES_URLS(urls);
0364 }
0365 
0366 void KFilePlacesModelTest::testInternalBookmarksHaveIds()
0367 {
0368     KBookmarkManager bookmarkManager(bookmarksFile());
0369     KBookmarkGroup root = bookmarkManager.root();
0370 
0371     // Verify every entry has an id or an udi
0372     KBookmark bookmark = root.first();
0373     while (!bookmark.isNull()) {
0374         QVERIFY(!bookmark.metaDataItem(QStringLiteral("ID")).isEmpty() || !bookmark.metaDataItem(QStringLiteral("UDI")).isEmpty());
0375         // It's mutually exclusive though
0376         QVERIFY(bookmark.metaDataItem(QStringLiteral("ID")).isEmpty() || bookmark.metaDataItem(QStringLiteral("UDI")).isEmpty());
0377 
0378         bookmark = root.next(bookmark);
0379     }
0380 
0381     // Verify that adding a bookmark behind its back the model gives it an id
0382     // (in real life it requires the user to modify the file by hand,
0383     // unlikely but better safe than sorry).
0384     // It induces a small race condition which means several ids will be
0385     // successively set on the same bookmark but no big deal since it won't
0386     // break the system
0387     KBookmark foo = root.addBookmark(QStringLiteral("Foo"), QUrl(QStringLiteral("file:/foo")), QStringLiteral("red-folder"));
0388     QCOMPARE(foo.text(), QStringLiteral("Foo"));
0389     QVERIFY(foo.metaDataItem(QStringLiteral("ID")).isEmpty());
0390     bookmarkManager.emitChanged(root);
0391     QCOMPARE(foo.text(), QStringLiteral("Foo"));
0392     QVERIFY(!foo.metaDataItem(QStringLiteral("ID")).isEmpty());
0393 
0394     // Verify that all the ids are different
0395     bookmark = root.first();
0396     QSet<QString> ids;
0397     while (!bookmark.isNull()) {
0398         QString id;
0399         if (!bookmark.metaDataItem(QStringLiteral("UDI")).isEmpty()) {
0400             id = bookmark.metaDataItem(QStringLiteral("UDI"));
0401         } else {
0402             id = bookmark.metaDataItem(QStringLiteral("ID"));
0403         }
0404 
0405         if (ids.contains(id)) {
0406             // Some debugging help
0407             qDebug() << "Bookmarks file:" << bookmarksFile() << "contains one (or more) duplicated bookmarks:";
0408             QFile debugFile(bookmarksFile());
0409             QVERIFY(debugFile.open(QIODevice::ReadOnly));
0410             qDebug() << QString::fromUtf8(debugFile.readAll());
0411             QVERIFY2(!ids.contains(id), qPrintable("Duplicated ID found: " + id));
0412         }
0413         ids << id;
0414         bookmark = root.next(bookmark);
0415     }
0416 
0417     // Cleanup foo
0418     root.deleteBookmark(foo);
0419     bookmarkManager.emitChanged(root);
0420 }
0421 
0422 void KFilePlacesModelTest::testHiding()
0423 {
0424     // Verify that nothing is hidden
0425     for (int row = 0; row < m_places->rowCount(); ++row) {
0426         QModelIndex index = m_places->index(row, 0);
0427         QVERIFY(!m_places->isHidden(index));
0428     }
0429 
0430     QModelIndex a = m_places->index(2, 0);
0431     QModelIndex b = m_places->index(6, 0);
0432 
0433     QList<QVariant> args;
0434     QSignalSpy spy(m_places, &QAbstractItemModel::dataChanged);
0435 
0436     // Verify that hidden is taken into account and is not global
0437     m_places->setPlaceHidden(a, true);
0438     QVERIFY(m_places->isHidden(a));
0439     QVERIFY(m_places->data(a, KFilePlacesModel::HiddenRole).toBool());
0440     QVERIFY(!m_places->isHidden(b));
0441     QVERIFY(!m_places->data(b, KFilePlacesModel::HiddenRole).toBool());
0442     QCOMPARE(spy.count(), 1);
0443     args = spy.takeFirst();
0444     QCOMPARE(args.at(0).toModelIndex(), a);
0445     QCOMPARE(args.at(1).toModelIndex(), a);
0446 
0447     m_places->setPlaceHidden(b, true);
0448     QVERIFY(m_places->isHidden(a));
0449     QVERIFY(m_places->data(a, KFilePlacesModel::HiddenRole).toBool());
0450     QVERIFY(m_places->isHidden(b));
0451     QVERIFY(m_places->data(b, KFilePlacesModel::HiddenRole).toBool());
0452     QCOMPARE(spy.count(), 1);
0453     args = spy.takeFirst();
0454     QCOMPARE(args.at(0).toModelIndex(), b);
0455     QCOMPARE(args.at(1).toModelIndex(), b);
0456 
0457     m_places->setPlaceHidden(a, false);
0458     m_places->setPlaceHidden(b, false);
0459     QVERIFY(!m_places->isHidden(a));
0460     QVERIFY(!m_places->data(a, KFilePlacesModel::HiddenRole).toBool());
0461     QVERIFY(!m_places->isHidden(b));
0462     QVERIFY(!m_places->data(b, KFilePlacesModel::HiddenRole).toBool());
0463     QCOMPARE(spy.count(), 2);
0464     args = spy.takeFirst();
0465     QCOMPARE(args.at(0).toModelIndex(), a);
0466     QCOMPARE(args.at(1).toModelIndex(), a);
0467     args = spy.takeFirst();
0468     QCOMPARE(args.at(0).toModelIndex(), b);
0469     QCOMPARE(args.at(1).toModelIndex(), b);
0470 }
0471 
0472 void KFilePlacesModelTest::testMove()
0473 {
0474     QList<QVariant> args;
0475     QSignalSpy spy_inserted(m_places, &QAbstractItemModel::rowsInserted);
0476     QSignalSpy spy_removed(m_places, &QAbstractItemModel::rowsRemoved);
0477 
0478     KBookmarkManager bookmarkManager(bookmarksFile());
0479     KBookmarkGroup root = bookmarkManager.root();
0480 
0481     KBookmark system_home = m_places->bookmarkForIndex(m_places->index(0, 0));
0482 
0483     // Trying move the root at the end of the list, should move it to the end of places section instead
0484     // to keep it grouped
0485     KBookmark last = root.first();
0486     while (!root.next(last).isNull()) {
0487         last = root.next(last);
0488     }
0489     root.moveBookmark(system_home, last);
0490     bookmarkManager.emitChanged(root);
0491 
0492     QStringList urls;
0493     urls << QStringLiteral("trash:/") << QDir::homePath() << initialListOfShared() << initialListOfRecent() << initialListOfDevices()
0494          << initialListOfRemovableDevices();
0495 
0496     CHECK_PLACES_URLS(urls);
0497     QCOMPARE(spy_inserted.count(), 1);
0498     args = spy_inserted.takeFirst();
0499     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0500     QCOMPARE(args.at(1).toInt(), 1);
0501     QCOMPARE(args.at(2).toInt(), 1);
0502     QCOMPARE(spy_removed.count(), 1);
0503     args = spy_removed.takeFirst();
0504     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0505     QCOMPARE(args.at(1).toInt(), 0);
0506     QCOMPARE(args.at(2).toInt(), 0);
0507 
0508     // Move home at the beginning of the list (at its original place)
0509     root.moveBookmark(system_home, KBookmark());
0510     bookmarkManager.emitChanged(root);
0511     urls.clear();
0512     urls << initialListOfPlaces() << initialListOfShared() << initialListOfRecent() << initialListOfDevices() << initialListOfRemovableDevices();
0513     CHECK_PLACES_URLS(urls);
0514     QCOMPARE(spy_inserted.count(), 1);
0515     args = spy_inserted.takeFirst();
0516     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0517     QCOMPARE(args.at(1).toInt(), 1);
0518     QCOMPARE(args.at(2).toInt(), 1);
0519     QCOMPARE(spy_removed.count(), 1);
0520     args = spy_removed.takeFirst();
0521     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0522     QCOMPARE(args.at(1).toInt(), 0);
0523     QCOMPARE(args.at(2).toInt(), 0);
0524 }
0525 
0526 void KFilePlacesModelTest::testDragAndDrop()
0527 {
0528     QList<QVariant> args;
0529     QSignalSpy spy_moved(m_places, &QAbstractItemModel::rowsMoved);
0530 
0531     // Monitor rowsInserted() and rowsRemoved() to ensure they are never emitted:
0532     // Moving with drag and drop is expected to emit rowsMoved()
0533     QSignalSpy spy_inserted(m_places, &QAbstractItemModel::rowsInserted);
0534     QSignalSpy spy_removed(m_places, &QAbstractItemModel::rowsRemoved);
0535 
0536     // Move /home at the end of the places list
0537     QModelIndexList indexes;
0538     indexes << m_places->index(0, 0);
0539     QMimeData *mimeData = m_places->mimeData(indexes);
0540     QVERIFY(m_places->dropMimeData(mimeData, Qt::MoveAction, 2, 0, QModelIndex()));
0541 
0542     QStringList urls;
0543     urls << QStringLiteral("trash:/") << QDir::homePath() << initialListOfShared() << initialListOfRecent() << initialListOfDevices()
0544          << initialListOfRemovableDevices();
0545     CHECK_PLACES_URLS(urls);
0546     QCOMPARE(spy_inserted.count(), 0);
0547     QCOMPARE(spy_removed.count(), 0);
0548     QCOMPARE(spy_moved.count(), 1);
0549     args = spy_moved.takeFirst();
0550     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0551     QCOMPARE(args.at(1).toInt(), 0);
0552     QCOMPARE(args.at(2).toInt(), 0);
0553     QCOMPARE(args.at(3).toModelIndex(), QModelIndex());
0554     QCOMPARE(args.at(4).toInt(), 2);
0555 
0556     // Move home back at the beginning of the list
0557     indexes.clear();
0558     indexes << m_places->index(1, 0);
0559     mimeData = m_places->mimeData(indexes);
0560     QVERIFY(m_places->dropMimeData(mimeData, Qt::MoveAction, 0, 0, QModelIndex()));
0561 
0562     urls.clear();
0563     urls << QDir::homePath() << QStringLiteral("trash:/") << initialListOfShared() << initialListOfRecent() << initialListOfDevices()
0564          << initialListOfRemovableDevices();
0565     CHECK_PLACES_URLS(urls);
0566     QCOMPARE(spy_inserted.count(), 0);
0567     QCOMPARE(spy_removed.count(), 0);
0568     QCOMPARE(spy_moved.count(), 1);
0569     args = spy_moved.takeFirst();
0570     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0571     QCOMPARE(args.at(1).toInt(), 1);
0572     QCOMPARE(args.at(2).toInt(), 1);
0573     QCOMPARE(args.at(3).toModelIndex(), QModelIndex());
0574     QCOMPARE(args.at(4).toInt(), 0);
0575 
0576     // Dropping on an item is not allowed
0577     indexes.clear();
0578     indexes << m_places->index(4, 0);
0579     mimeData = m_places->mimeData(indexes);
0580     QVERIFY(!m_places->dropMimeData(mimeData, Qt::MoveAction, -1, 0, m_places->index(2, 0)));
0581     CHECK_PLACES_URLS(urls);
0582     QCOMPARE(spy_inserted.count(), 0);
0583     QCOMPARE(spy_removed.count(), 0);
0584     QCOMPARE(spy_moved.count(), 0);
0585 }
0586 
0587 void KFilePlacesModelTest::testPlacesLifecycle()
0588 {
0589     QList<QVariant> args;
0590     QSignalSpy spy_inserted(m_places, &QAbstractItemModel::rowsInserted);
0591     QSignalSpy spy_removed(m_places, &QAbstractItemModel::rowsRemoved);
0592     QSignalSpy spy_changed(m_places, &QAbstractItemModel::dataChanged);
0593 
0594     m_places->addPlace(QStringLiteral("Foo"), QUrl::fromLocalFile(QStringLiteral("/home/foo")));
0595 
0596     QStringList urls;
0597     urls << initialListOfPlaces() << QStringLiteral("/home/foo") << initialListOfShared() << initialListOfRecent() << initialListOfDevices()
0598          << initialListOfRemovableDevices();
0599     CHECK_PLACES_URLS(urls);
0600     QCOMPARE(spy_inserted.count(), 1);
0601     args = spy_inserted.takeFirst();
0602     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0603     QCOMPARE(args.at(1).toInt(), 2);
0604     QCOMPARE(args.at(2).toInt(), 2);
0605     QCOMPARE(spy_removed.count(), 0);
0606 
0607     KBookmarkManager bookmarkManager(bookmarksFile());
0608     KBookmarkGroup root = bookmarkManager.root();
0609     KBookmark before_trash = m_places->bookmarkForIndex(m_places->index(0, 0));
0610     KBookmark foo = m_places->bookmarkForIndex(m_places->index(2, 0));
0611 
0612     root.moveBookmark(foo, before_trash);
0613     bookmarkManager.emitChanged(root);
0614 
0615     urls.clear();
0616     urls << QDir::homePath() << QStringLiteral("/home/foo") << QStringLiteral("trash:/") << initialListOfShared() << initialListOfRecent()
0617          << initialListOfDevices() << initialListOfRemovableDevices();
0618     CHECK_PLACES_URLS(urls);
0619     QCOMPARE(spy_inserted.count(), 1);
0620     args = spy_inserted.takeFirst();
0621     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0622     QCOMPARE(args.at(1).toInt(), 2);
0623     QCOMPARE(args.at(2).toInt(), 2);
0624     QCOMPARE(spy_removed.count(), 1);
0625     args = spy_removed.takeFirst();
0626     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0627     QCOMPARE(args.at(1).toInt(), 1);
0628     QCOMPARE(args.at(2).toInt(), 1);
0629 
0630     m_places->editPlace(m_places->index(1, 0), QStringLiteral("Foo"), QUrl::fromLocalFile(QStringLiteral("/mnt/foo")));
0631 
0632     urls.clear();
0633     urls << QDir::homePath() << QStringLiteral("/mnt/foo") << QStringLiteral("trash:/") << initialListOfShared() << initialListOfRecent()
0634          << initialListOfDevices() << initialListOfRemovableDevices();
0635     CHECK_PLACES_URLS(urls);
0636     QCOMPARE(spy_inserted.count(), 0);
0637     QCOMPARE(spy_removed.count(), 0);
0638     QCOMPARE(spy_changed.count(), 1);
0639     args = spy_changed.takeFirst();
0640     QCOMPARE(args.at(0).toModelIndex(), m_places->index(1, 0));
0641     QCOMPARE(args.at(1).toModelIndex(), m_places->index(1, 0));
0642 
0643     foo = m_places->bookmarkForIndex(m_places->index(1, 0));
0644     foo.setFullText(QStringLiteral("Bar"));
0645 
0646     urls.clear();
0647     urls << QDir::homePath() << QStringLiteral("/mnt/foo") << QStringLiteral("trash:/") << initialListOfShared() << initialListOfRecent()
0648          << initialListOfDevices() << initialListOfRemovableDevices();
0649 
0650     CHECK_PLACES_URLS(urls);
0651     QCOMPARE(spy_inserted.count(), 0);
0652     QCOMPARE(spy_removed.count(), 0);
0653     QCOMPARE(spy_changed.count(), m_hasRecentlyUsedKio ? 11 : 9);
0654     args = spy_changed[2];
0655     QCOMPARE(args.at(0).toModelIndex(), m_places->index(2, 0));
0656     QCOMPARE(args.at(1).toModelIndex(), m_places->index(2, 0));
0657     spy_changed.clear();
0658 
0659     m_places->removePlace(m_places->index(1, 0));
0660 
0661     urls.clear();
0662     urls << initialListOfPlaces() << initialListOfShared() << initialListOfRecent() << initialListOfDevices() << initialListOfRemovableDevices();
0663     CHECK_PLACES_URLS(urls);
0664     QCOMPARE(spy_inserted.count(), 0);
0665     QCOMPARE(spy_removed.count(), 1);
0666     args = spy_removed.takeFirst();
0667     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0668     QCOMPARE(args.at(1).toInt(), 1);
0669     QCOMPARE(args.at(2).toInt(), 1);
0670 
0671     m_places->addPlace(QStringLiteral("Foo"), QUrl::fromLocalFile(QStringLiteral("/home/foo")), QString(), QString(), m_places->index(0, 0));
0672 
0673     urls.clear();
0674     urls << QDir::homePath() << QStringLiteral("/home/foo") << QStringLiteral("trash:/") << initialListOfShared() << initialListOfRecent()
0675          << initialListOfDevices() << initialListOfRemovableDevices();
0676     CHECK_PLACES_URLS(urls);
0677     QCOMPARE(spy_inserted.count(), 1);
0678     args = spy_inserted.takeFirst();
0679     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0680     QCOMPARE(args.at(1).toInt(), 1);
0681     QCOMPARE(args.at(2).toInt(), 1);
0682     QCOMPARE(spy_removed.count(), 0);
0683 
0684     m_places->removePlace(m_places->index(1, 0));
0685 }
0686 
0687 void KFilePlacesModelTest::testDevicePlugging()
0688 {
0689     QList<QVariant> args;
0690     QSignalSpy spy_inserted(m_places, &QAbstractItemModel::rowsInserted);
0691     QSignalSpy spy_removed(m_places, &QAbstractItemModel::rowsRemoved);
0692 
0693     fakeManager()->call(QStringLiteral("unplug"), "/org/kde/solid/fakehw/volume_part1_size_993284096");
0694 
0695     QStringList urls;
0696     urls << initialListOfPlaces() << initialListOfShared() << initialListOfRecent() << initialListOfDevices() << QStringLiteral("/media/floppy0")
0697          << QStringLiteral("/media/cdrom");
0698     CHECK_PLACES_URLS(urls);
0699     QCOMPARE(spy_inserted.count(), 0);
0700     QCOMPARE(spy_removed.count(), 1);
0701     args = spy_removed.takeFirst();
0702     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0703     QCOMPARE(args.at(1).toInt(), m_hasRecentlyUsedKio ? 8 : 6);
0704     QCOMPARE(args.at(2).toInt(), m_hasRecentlyUsedKio ? 8 : 6);
0705 
0706     fakeManager()->call(QStringLiteral("plug"), "/org/kde/solid/fakehw/volume_part1_size_993284096");
0707 
0708     urls.clear();
0709     urls << initialListOfPlaces() << initialListOfShared() << initialListOfRecent() << initialListOfDevices() << initialListOfRemovableDevices();
0710     CHECK_PLACES_URLS(urls);
0711     QCOMPARE(spy_inserted.count(), 1);
0712     args = spy_inserted.takeFirst();
0713     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0714     QCOMPARE(args.at(1).toInt(), m_hasRecentlyUsedKio ? 8 : 6);
0715     QCOMPARE(args.at(2).toInt(), m_hasRecentlyUsedKio ? 8 : 6);
0716     QCOMPARE(spy_removed.count(), 0);
0717 
0718     // Move the device in the list, and check that it memorizes the position across plug/unplug
0719 
0720     KBookmarkManager bookmarkManager(bookmarksFile());
0721     KBookmarkGroup root = bookmarkManager.root();
0722     KBookmark before_floppy;
0723 
0724     KBookmark device = root.first(); // The device we'll move is the 6th bookmark
0725     const int count = m_hasRecentlyUsedKio ? 7 : 5;
0726     for (int i = 0; i < count; i++) {
0727         if (i == 2) {
0728             // store item before to be able to move it back to original position
0729             device = before_floppy = root.next(device);
0730         } else {
0731             device = root.next(device);
0732         }
0733     }
0734 
0735     root.moveBookmark(device, before_floppy);
0736     bookmarkManager.emitChanged(root);
0737 
0738     urls.clear();
0739     urls << initialListOfPlaces() << initialListOfShared() << initialListOfRecent() << initialListOfDevices() << QStringLiteral("/media/XO-Y4")
0740          << QStringLiteral("/media/floppy0") << QStringLiteral("/media/cdrom");
0741     CHECK_PLACES_URLS(urls);
0742     QCOMPARE(spy_inserted.count(), 1);
0743     args = spy_inserted.takeFirst();
0744     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0745     QCOMPARE(args.at(1).toInt(), m_hasRecentlyUsedKio ? 8 : 6);
0746     QCOMPARE(args.at(2).toInt(), m_hasRecentlyUsedKio ? 8 : 6);
0747     QCOMPARE(spy_removed.count(), 1);
0748     args = spy_removed.takeFirst();
0749     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0750     QCOMPARE(args.at(1).toInt(), m_hasRecentlyUsedKio ? 7 : 5);
0751     QCOMPARE(args.at(2).toInt(), m_hasRecentlyUsedKio ? 7 : 5);
0752 
0753     fakeManager()->call(QStringLiteral("unplug"), "/org/kde/solid/fakehw/volume_part1_size_993284096");
0754 
0755     urls.clear();
0756     urls << initialListOfPlaces() << initialListOfShared() << initialListOfRecent() << initialListOfDevices() << QStringLiteral("/media/floppy0")
0757          << QStringLiteral("/media/cdrom");
0758     CHECK_PLACES_URLS(urls);
0759     QCOMPARE(spy_inserted.count(), 0);
0760     QCOMPARE(spy_removed.count(), 1);
0761     args = spy_removed.takeFirst();
0762     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0763     QCOMPARE(args.at(1).toInt(), m_hasRecentlyUsedKio ? 7 : 5);
0764     QCOMPARE(args.at(2).toInt(), m_hasRecentlyUsedKio ? 7 : 5);
0765 
0766     fakeManager()->call(QStringLiteral("plug"), "/org/kde/solid/fakehw/volume_part1_size_993284096");
0767 
0768     urls.clear();
0769     urls << initialListOfPlaces() << initialListOfShared() << initialListOfRecent() << initialListOfDevices() << QStringLiteral("/media/XO-Y4")
0770          << QStringLiteral("/media/floppy0") << QStringLiteral("/media/cdrom");
0771     CHECK_PLACES_URLS(urls);
0772     QCOMPARE(spy_inserted.count(), 1);
0773     args = spy_inserted.takeFirst();
0774     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0775     QCOMPARE(args.at(1).toInt(), m_hasRecentlyUsedKio ? 7 : 5);
0776     QCOMPARE(args.at(2).toInt(), m_hasRecentlyUsedKio ? 7 : 5);
0777     QCOMPARE(spy_removed.count(), 0);
0778 
0779     KBookmark seventh = root.first();
0780     for (int i = 0; i < count; i++) {
0781         seventh = root.next(seventh);
0782     }
0783     root.moveBookmark(device, seventh);
0784     bookmarkManager.emitChanged(root);
0785 
0786     urls.clear();
0787     urls << initialListOfPlaces() << initialListOfShared() << initialListOfRecent() << initialListOfDevices() << initialListOfRemovableDevices();
0788     CHECK_PLACES_URLS(urls);
0789     QCOMPARE(spy_inserted.count(), 1);
0790     args = spy_inserted.takeFirst();
0791     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0792     QCOMPARE(args.at(1).toInt(), m_hasRecentlyUsedKio ? 8 : 6);
0793     QCOMPARE(args.at(2).toInt(), m_hasRecentlyUsedKio ? 8 : 6);
0794     QCOMPARE(spy_removed.count(), 1);
0795     args = spy_removed.takeFirst();
0796     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0797     QCOMPARE(args.at(1).toInt(), m_hasRecentlyUsedKio ? 7 : 5);
0798     QCOMPARE(args.at(2).toInt(), m_hasRecentlyUsedKio ? 7 : 5);
0799 }
0800 
0801 void KFilePlacesModelTest::testDeviceSetupTeardown()
0802 {
0803     QList<QVariant> args;
0804     QSignalSpy spy_changed(m_places, &QAbstractItemModel::dataChanged);
0805 
0806     fakeDevice(QStringLiteral("/org/kde/solid/fakehw/volume_part1_size_993284096/StorageAccess"))->call(QStringLiteral("teardown"));
0807 
0808     QCOMPARE(spy_changed.count(), 1);
0809     args = spy_changed.takeFirst();
0810     QCOMPARE(args.at(0).toModelIndex().row(), m_hasRecentlyUsedKio ? 8 : 6);
0811     QCOMPARE(args.at(1).toModelIndex().row(), m_hasRecentlyUsedKio ? 8 : 6);
0812 
0813     fakeDevice(QStringLiteral("/org/kde/solid/fakehw/volume_part1_size_993284096/StorageAccess"))->call(QStringLiteral("setup"));
0814 
0815     QCOMPARE(spy_changed.count(), 1);
0816     args = spy_changed.takeFirst();
0817     QCOMPARE(args.at(0).toModelIndex().row(), m_hasRecentlyUsedKio ? 8 : 6);
0818     QCOMPARE(args.at(1).toModelIndex().row(), m_hasRecentlyUsedKio ? 8 : 6);
0819 }
0820 
0821 void KFilePlacesModelTest::testEnableBaloo()
0822 {
0823     KConfig config(QStringLiteral("baloofilerc"));
0824     KConfigGroup basicSettings = config.group(QStringLiteral("Basic Settings"));
0825     basicSettings.writeEntry("Indexing-Enabled", true);
0826     config.sync();
0827 
0828     KFilePlacesModel places_with_baloo;
0829     QStringList urls;
0830     for (int row = 0; row < places_with_baloo.rowCount(); ++row) {
0831         QModelIndex index = places_with_baloo.index(row, 0);
0832         urls << places_with_baloo.url(index).toDisplayString(QUrl::PreferLocalFile);
0833     }
0834 
0835     if (m_hasRecentlyUsedKio) {
0836         QVERIFY(urls.contains("recentlyused:/files"));
0837         QVERIFY(urls.contains("recentlyused:/locations"));
0838     }
0839 }
0840 
0841 void KFilePlacesModelTest::testRemoteUrls_data()
0842 {
0843     QTest::addColumn<QUrl>("url");
0844     QTest::addColumn<int>("expectedRow");
0845     QTest::addColumn<QString>("expectedGroup");
0846 
0847     QTest::newRow("Ftp") << QUrl(QStringLiteral("ftp://192.168.1.1/ftp")) << 4 << QStringLiteral("Remote");
0848     QTest::newRow("Samba") << QUrl(QStringLiteral("smb://192.168.1.1/share")) << 4 << QStringLiteral("Remote");
0849     QTest::newRow("Sftp") << QUrl(QStringLiteral("sftp://192.168.1.1/share")) << 4 << QStringLiteral("Remote");
0850     QTest::newRow("Fish") << QUrl(QStringLiteral("fish://192.168.1.1/share")) << 4 << QStringLiteral("Remote");
0851     QTest::newRow("Webdav") << QUrl(QStringLiteral("webdav://192.168.1.1/share")) << 4 << QStringLiteral("Remote");
0852 }
0853 
0854 void KFilePlacesModelTest::testRemoteUrls()
0855 {
0856     QFETCH(QUrl, url);
0857     QFETCH(int, expectedRow);
0858     QFETCH(QString, expectedGroup);
0859 
0860     QList<QVariant> args;
0861     QSignalSpy spy_inserted(m_places, &QAbstractItemModel::rowsInserted);
0862 
0863     // insert a new network url
0864     m_places->addPlace(QStringLiteral("My Shared"), url, QString(), QString(), QModelIndex());
0865 
0866     // check if url list is correct after insertion
0867     QStringList urls;
0868     urls << QDir::homePath() << QStringLiteral("trash:/") // places
0869          << QStringLiteral("remote:/") << QStringLiteral("/media/nfs") << url.toString() << initialListOfRecent() << QStringLiteral("/foreign")
0870          << QStringLiteral("/media/floppy0") << QStringLiteral("/media/XO-Y4") << QStringLiteral("/media/cdrom");
0871     CHECK_PLACES_URLS(urls);
0872 
0873     // check if the new url was inserted in the right position (end of "Remote" section)
0874     QTRY_COMPARE(spy_inserted.count(), 1);
0875     args = spy_inserted.takeFirst();
0876     QCOMPARE(args.at(0).toModelIndex(), QModelIndex());
0877     QCOMPARE(args.at(1).toInt(), expectedRow);
0878     QCOMPARE(args.at(2).toInt(), expectedRow);
0879 
0880     // check if the new url has the right group "Remote"
0881     const QModelIndex index = m_places->index(expectedRow, 0);
0882     QCOMPARE(index.data(KFilePlacesModel::GroupRole).toString(), expectedGroup);
0883 
0884     m_places->removePlace(index);
0885 }
0886 
0887 void KFilePlacesModelTest::testRefresh()
0888 {
0889     KBookmarkManager bookmarkManager(bookmarksFile());
0890     KBookmarkGroup root = bookmarkManager.root();
0891     KBookmark homePlace = root.first();
0892     const QModelIndex homePlaceIndex = m_places->index(0, 0);
0893 
0894     QCOMPARE(m_places->text(homePlaceIndex), homePlace.fullText());
0895 
0896     // modify bookmark
0897     homePlace.setFullText("Test change the text");
0898     QVERIFY(m_places->text(homePlaceIndex) != homePlace.fullText());
0899 
0900     // reload bookmark data
0901     m_places->refresh();
0902     QCOMPARE(m_places->text(homePlaceIndex), homePlace.fullText());
0903 }
0904 
0905 void KFilePlacesModelTest::testConvertedUrl_data()
0906 {
0907     QTest::addColumn<QUrl>("url");
0908     QTest::addColumn<QUrl>("expectedUrl");
0909 
0910     // places
0911     QTest::newRow("Places - Home") << QUrl::fromLocalFile(QDir::homePath()) << QUrl::fromLocalFile(QDir::homePath());
0912 
0913     // baloo -search
0914     QTest::newRow("Baloo - Documents") << QUrl("search:/documents") << QUrl("baloosearch:/documents");
0915 
0916     QTest::newRow("Baloo - Unknown Type") << QUrl("search:/unknown") << QUrl("search:/unknown");
0917 
0918     // baloo - timeline
0919     const QDate lastMonthDate = QDate::currentDate().addMonths(-1);
0920     QTest::newRow("Baloo - Last Month") << QUrl("timeline:/lastmonth")
0921                                         << QUrl(QString("timeline:/%1-%2").arg(lastMonthDate.year()).arg(lastMonthDate.month(), 2, 10, QLatin1Char('0')));
0922 
0923     // devices
0924     QTest::newRow("Devices - Floppy") << QUrl("file:///media/floppy0") << QUrl("file:///media/floppy0");
0925 }
0926 
0927 void KFilePlacesModelTest::testConvertedUrl()
0928 {
0929     QFETCH(QUrl, url);
0930     QFETCH(QUrl, expectedUrl);
0931 
0932     const QUrl convertedUrl = KFilePlacesModel::convertedUrl(url);
0933 
0934     QCOMPARE(convertedUrl.scheme(), expectedUrl.scheme());
0935     QCOMPARE(convertedUrl.path(), expectedUrl.path());
0936     QCOMPARE(convertedUrl, expectedUrl);
0937 }
0938 
0939 void KFilePlacesModelTest::testBookmarkObject()
0940 {
0941     // make sure that all items return a valid bookmark
0942     for (int row = 0; row < m_places->rowCount(); row++) {
0943         const QModelIndex index = m_places->index(row, 0);
0944         const KBookmark bookmark = m_places->bookmarkForIndex(index);
0945         QVERIFY(!bookmark.isNull());
0946     }
0947 }
0948 
0949 void KFilePlacesModelTest::testDataChangedSignal()
0950 {
0951     QSignalSpy dataChangedSpy(m_places, &KFilePlacesModel::dataChanged);
0952 
0953     const QModelIndex index = m_places->index(1, 0);
0954     const KBookmark bookmark = m_places->bookmarkForIndex(index);
0955 
0956     // call function with the same data
0957     m_places->editPlace(index, bookmark.fullText(), bookmark.url(), bookmark.icon(), bookmark.metaDataItem(QStringLiteral("OnlyInApp")));
0958     QCOMPARE(dataChangedSpy.count(), 0);
0959 
0960     // call function with different data
0961     const QString originalText = bookmark.fullText();
0962     m_places->editPlace(index, QStringLiteral("My text"), bookmark.url(), bookmark.icon(), bookmark.metaDataItem(QStringLiteral("OnlyInApp")));
0963     QCOMPARE(dataChangedSpy.count(), 1);
0964     QList<QVariant> args = dataChangedSpy.takeFirst();
0965     QCOMPARE(args.at(0).toModelIndex().row(), 1);
0966     QCOMPARE(args.at(0).toModelIndex().column(), 0);
0967     QCOMPARE(args.at(1).toModelIndex().row(), 1);
0968     QCOMPARE(args.at(1).toModelIndex().column(), 0);
0969     QCOMPARE(m_places->text(index), QStringLiteral("My text"));
0970 
0971     // restore original value
0972     dataChangedSpy.clear();
0973     m_places->editPlace(index, originalText, bookmark.url(), bookmark.icon(), bookmark.metaDataItem(QStringLiteral("OnlyInApp")));
0974     QCOMPARE(dataChangedSpy.count(), 1);
0975 }
0976 
0977 void KFilePlacesModelTest::testIconRole_data()
0978 {
0979     QTest::addColumn<QModelIndex>("index");
0980     QTest::addColumn<QString>("expectedIconName");
0981 
0982     // places
0983     int index = 0;
0984     QTest::newRow("Places - Home") << m_places->index(index++, 0) << QStringLiteral("user-home");
0985     QTest::newRow("Places - Trash") << m_places->index(index++, 0) << QStringLiteral("user-trash");
0986 
0987     QTest::newRow("Remote - Network") << m_places->index(index++, 0) << QStringLiteral("folder-network");
0988     QTest::newRow("Devices - Nfs") << m_places->index(index++, 0) << QStringLiteral("hwinfo");
0989     if (m_hasRecentlyUsedKio) {
0990         QTest::newRow("Recent Files") << m_places->index(index++, 0) << QStringLiteral("document-open-recent");
0991         QTest::newRow("Recent Locations") << m_places->index(index++, 0) << QStringLiteral("folder-open-recent");
0992     }
0993     QTest::newRow("Devices - foreign") << m_places->index(index++, 0) << QStringLiteral("blockdevice");
0994     QTest::newRow("Devices - Floppy") << m_places->index(index++, 0) << QStringLiteral("blockdevice");
0995     QTest::newRow("Devices - cdrom") << m_places->index(index++, 0) << QStringLiteral("blockdevice");
0996 }
0997 
0998 void KFilePlacesModelTest::testIconRole()
0999 {
1000     QFETCH(QModelIndex, index);
1001     QFETCH(QString, expectedIconName);
1002 
1003     QVERIFY(index.data(KFilePlacesModel::IconNameRole).toString().startsWith(expectedIconName));
1004 }
1005 
1006 void KFilePlacesModelTest::testMoveFunction()
1007 {
1008     QList<QVariant> args;
1009     QStringList urls = initialListOfUrls();
1010     QSignalSpy rowsMoved(m_places, &KFilePlacesModel::rowsMoved);
1011 
1012     // move item 0 to pos 1
1013     QVERIFY(m_places->movePlace(0, 2));
1014     urls.move(0, 1);
1015     QTRY_COMPARE(rowsMoved.count(), 1);
1016     args = rowsMoved.takeFirst();
1017     QCOMPARE(args.at(1).toInt(), 0); // start
1018     QCOMPARE(args.at(2).toInt(), 0); // end
1019     QCOMPARE(args.at(4).toInt(), 2); // row (destination)
1020     QCOMPARE(placesUrls(), urls);
1021     rowsMoved.clear();
1022 
1023     // move it back
1024     QVERIFY(m_places->movePlace(1, 0));
1025     urls.move(1, 0);
1026     QTRY_COMPARE(rowsMoved.count(), 1);
1027     args = rowsMoved.takeFirst();
1028     QCOMPARE(args.at(1).toInt(), 1); // start
1029     QCOMPARE(args.at(2).toInt(), 1); // end
1030     QCOMPARE(args.at(4).toInt(), 0); // row (destination)
1031     QCOMPARE(placesUrls(), urls);
1032     rowsMoved.clear();
1033 
1034     // target position is greater than model rows
1035     // will move to the end of the first group
1036     QVERIFY(m_places->movePlace(0, 20));
1037     urls.move(0, 1);
1038     QTRY_COMPARE(rowsMoved.count(), 1);
1039     args = rowsMoved.takeFirst();
1040     QCOMPARE(args.at(1).toInt(), 0); // start
1041     QCOMPARE(args.at(2).toInt(), 0); // end
1042     QCOMPARE(args.at(4).toInt(), 2); // row (destination)
1043     QCOMPARE(placesUrls(), urls);
1044     rowsMoved.clear();
1045 
1046     // move it back
1047     QVERIFY(m_places->movePlace(1, 0));
1048     urls.move(1, 0);
1049     QTRY_COMPARE(rowsMoved.count(), 1);
1050     args = rowsMoved.takeFirst();
1051     QCOMPARE(args.at(1).toInt(), 1); // start
1052     QCOMPARE(args.at(2).toInt(), 1); // end
1053     QCOMPARE(args.at(4).toInt(), 0); // row (destination)
1054     QCOMPARE(placesUrls(), urls);
1055     rowsMoved.clear();
1056 
1057     QVERIFY(m_places->movePlace(m_hasRecentlyUsedKio ? 8 : 7, m_hasRecentlyUsedKio ? 6 : 5));
1058     urls.move(m_hasRecentlyUsedKio ? 8 : 7, m_hasRecentlyUsedKio ? 6 : 5);
1059     QTRY_COMPARE(rowsMoved.count(), 1);
1060     args = rowsMoved.takeFirst();
1061     QCOMPARE(args.at(1).toInt(), m_hasRecentlyUsedKio ? 8 : 7); // start
1062     QCOMPARE(args.at(2).toInt(), m_hasRecentlyUsedKio ? 8 : 7); // end
1063     QCOMPARE(args.at(4).toInt(), m_hasRecentlyUsedKio ? 6 : 5); // row (destination)
1064     QCOMPARE(placesUrls(), urls);
1065     rowsMoved.clear();
1066 
1067     // move it back
1068     QVERIFY(m_places->movePlace(m_hasRecentlyUsedKio ? 6 : 5, m_hasRecentlyUsedKio ? 9 : 8));
1069     urls.move(m_hasRecentlyUsedKio ? 6 : 5, m_hasRecentlyUsedKio ? 8 : 7);
1070     QTRY_COMPARE(rowsMoved.count(), 1);
1071     args = rowsMoved.takeFirst();
1072     QCOMPARE(args.at(1).toInt(), m_hasRecentlyUsedKio ? 6 : 5); // start
1073     QCOMPARE(args.at(2).toInt(), m_hasRecentlyUsedKio ? 6 : 5); // end
1074     QCOMPARE(args.at(4).toInt(), m_hasRecentlyUsedKio ? 9 : 8); // row (destination)
1075     QCOMPARE(placesUrls(), urls);
1076     rowsMoved.clear();
1077 
1078     // use a invalid start position
1079     QVERIFY(!m_places->movePlace(100, 20));
1080     QCOMPARE(rowsMoved.count(), 0);
1081 
1082     // use same start and target position
1083     QVERIFY(!m_places->movePlace(1, 1));
1084     QCOMPARE(rowsMoved.count(), 0);
1085 }
1086 
1087 void KFilePlacesModelTest::testPlaceGroupHidden()
1088 {
1089     // GIVEN
1090     QCOMPARE(m_places->hiddenCount(), 0);
1091 
1092     QStringList urls;
1093     urls << initialListOfPlaces() << initialListOfShared() << initialListOfRecent() << initialListOfDevices() << initialListOfRemovableDevices();
1094     CHECK_PLACES_URLS(urls);
1095     QList<QModelIndex> indexesHidden;
1096 
1097     // WHEN
1098     m_places->setGroupHidden(KFilePlacesModel::PlacesType, true);
1099 
1100     // THEN
1101     for (int row = 0; row < m_places->rowCount(); ++row) {
1102         QModelIndex index = m_places->index(row, 0);
1103         if (m_places->groupType(index) == KFilePlacesModel::PlacesType) {
1104             QVERIFY(m_places->isHidden(index));
1105             indexesHidden << index;
1106         }
1107     }
1108     QCOMPARE(indexesHidden.count(), initialListOfPlaces().size());
1109     QCOMPARE(m_places->hiddenCount(), indexesHidden.size());
1110 
1111     // and GIVEN
1112     QList<QModelIndex> indexesShown;
1113 
1114     // WHEN
1115     m_places->setGroupHidden(KFilePlacesModel::PlacesType, false);
1116 
1117     // THEN
1118     for (int row = 0; row < m_places->rowCount(); ++row) {
1119         QModelIndex index = m_places->index(row, 0);
1120         if (m_places->groupType(index) == KFilePlacesModel::PlacesType) {
1121             QVERIFY(!m_places->isHidden(index));
1122             indexesShown << index;
1123         }
1124     }
1125     QCOMPARE(m_places->hiddenCount(), 0);
1126     QCOMPARE(indexesShown.count(), initialListOfPlaces().size());
1127 }
1128 
1129 void KFilePlacesModelTest::testPlaceGroupHiddenVsPlaceChildShown()
1130 {
1131     // GIVEN
1132     for (int row = 0; row < m_places->rowCount(); ++row) {
1133         QModelIndex index = m_places->index(row, 0);
1134         QVERIFY(!m_places->isHidden(index));
1135     }
1136     m_places->setGroupHidden(KFilePlacesModel::PlacesType, true);
1137 
1138     QModelIndex firstIndex = m_places->index(0, 0);
1139     const int amountOfPlaces = initialListOfPlaces().size();
1140     for (int row = 0; row < amountOfPlaces; ++row) {
1141         QModelIndex index = m_places->index(row, 0);
1142         QVERIFY(m_places->isHidden(index));
1143     }
1144     // WHEN
1145     m_places->setPlaceHidden(firstIndex, false);
1146 
1147     // THEN
1148     QVERIFY(m_places->isHidden(firstIndex)); // a child cannot show against its parent state
1149 
1150     // leaving in a clean state
1151     m_places->setGroupHidden(KFilePlacesModel::PlacesType, false);
1152 }
1153 
1154 void KFilePlacesModelTest::testPlaceGroupHiddenAndShownWithHiddenChild()
1155 {
1156     // GIVEN
1157     QCOMPARE(m_places->hiddenCount(), 0);
1158 
1159     QStringList urls;
1160     urls << initialListOfPlaces() << initialListOfShared() << initialListOfRecent() << initialListOfDevices() << initialListOfRemovableDevices();
1161     CHECK_PLACES_URLS(urls);
1162 
1163     QModelIndex firstIndexHidden = m_places->index(0, 0);
1164     m_places->setPlaceHidden(firstIndexHidden, true); // first place index is hidden within an hidden parent
1165     m_places->setGroupHidden(KFilePlacesModel::PlacesType, true);
1166     QCOMPARE(m_places->hiddenCount(), initialListOfPlaces().size());
1167 
1168     // WHEN
1169     m_places->setGroupHidden(KFilePlacesModel::PlacesType, false);
1170 
1171     // THEN
1172     QList<QModelIndex> indexesShown;
1173     for (int row = 0; row < m_places->rowCount(); ++row) {
1174         QModelIndex index = m_places->index(row, 0);
1175         if (index == firstIndexHidden) {
1176             QVERIFY(m_places->isHidden(firstIndexHidden));
1177             continue;
1178         }
1179         if (m_places->groupType(index) == KFilePlacesModel::PlacesType) {
1180             QVERIFY(!m_places->isHidden(index));
1181             indexesShown << index;
1182         }
1183     }
1184     QCOMPARE(m_places->hiddenCount(), 1);
1185     QCOMPARE(indexesShown.count(), initialListOfPlaces().size() - 1 /*first child remains hidden*/);
1186 
1187     // leaving in a clean state
1188     m_places->setPlaceHidden(firstIndexHidden, false);
1189 }
1190 
1191 void KFilePlacesModelTest::testPlaceGroupHiddenGroupIndexesIntegrity()
1192 {
1193     // GIVEN
1194     m_places->setGroupHidden(KFilePlacesModel::PlacesType, true);
1195     QVERIFY(m_places->groupIndexes(KFilePlacesModel::UnknownType).isEmpty());
1196     QVERIFY(m_places->isGroupHidden(KFilePlacesModel::PlacesType));
1197     QCOMPARE(m_places->groupIndexes(KFilePlacesModel::PlacesType).count(), initialListOfPlaces().count());
1198     QCOMPARE(m_places->groupIndexes(KFilePlacesModel::RecentlySavedType).count(), m_hasRecentlyUsedKio ? 2 : 0);
1199     QCOMPARE(m_places->groupIndexes(KFilePlacesModel::SearchForType).count(), 0);
1200     QCOMPARE(m_places->groupIndexes(KFilePlacesModel::DevicesType).count(), initialListOfDevices().count());
1201     QCOMPARE(m_places->groupIndexes(KFilePlacesModel::RemovableDevicesType).count(), initialListOfRemovableDevices().count());
1202 
1203     // WHEN
1204     m_places->setGroupHidden(KFilePlacesModel::PlacesType, false);
1205 
1206     // THEN
1207     // Make sure that hidden place group doesn't change model
1208     QVERIFY(!m_places->isGroupHidden(KFilePlacesModel::PlacesType));
1209     QCOMPARE(m_places->groupIndexes(KFilePlacesModel::PlacesType).count(), initialListOfPlaces().count());
1210     QCOMPARE(m_places->groupIndexes(KFilePlacesModel::RecentlySavedType).count(), m_hasRecentlyUsedKio ? 2 : 0);
1211     QCOMPARE(m_places->groupIndexes(KFilePlacesModel::SearchForType).count(), 0);
1212     QCOMPARE(m_places->groupIndexes(KFilePlacesModel::DevicesType).count(), initialListOfDevices().count());
1213     QCOMPARE(m_places->groupIndexes(KFilePlacesModel::RemovableDevicesType).count(), initialListOfRemovableDevices().count());
1214 }
1215 
1216 void KFilePlacesModelTest::testPlaceGroupHiddenSignal()
1217 {
1218     QSignalSpy groupHiddenSignal(m_places, &KFilePlacesModel::groupHiddenChanged);
1219     m_places->setGroupHidden(KFilePlacesModel::SearchForType, true);
1220 
1221     // hide SearchForType group
1222     QTRY_COMPARE(groupHiddenSignal.count(), 1);
1223     QList<QVariant> args = groupHiddenSignal.takeFirst();
1224     QCOMPARE(args.at(0).toInt(), static_cast<int>(KFilePlacesModel::SearchForType));
1225     QCOMPARE(args.at(1).toBool(), true);
1226     groupHiddenSignal.clear();
1227 
1228     // try hide SearchForType which is already hidden
1229     m_places->setGroupHidden(KFilePlacesModel::SearchForType, true);
1230     QCOMPARE(groupHiddenSignal.count(), 0);
1231 
1232     // show SearchForType group
1233     m_places->setGroupHidden(KFilePlacesModel::SearchForType, false);
1234     QTRY_COMPARE(groupHiddenSignal.count(), 1);
1235     args = groupHiddenSignal.takeFirst();
1236     QCOMPARE(args.at(0).toInt(), static_cast<int>(KFilePlacesModel::SearchForType));
1237     QCOMPARE(args.at(1).toBool(), false);
1238 }
1239 
1240 void KFilePlacesModelTest::testPlaceGroupHiddenRole()
1241 {
1242     // on startup all groups are visible
1243     for (int r = 0, rMax = m_places->rowCount(); r < rMax; r++) {
1244         const QModelIndex index = m_places->index(r, 0);
1245         QCOMPARE(index.data(KFilePlacesModel::GroupHiddenRole).toBool(), false);
1246     }
1247 
1248     // set SearchFor group hidden
1249     m_places->setGroupHidden(KFilePlacesModel::SearchForType, true);
1250     for (auto groupType : {KFilePlacesModel::PlacesType,
1251                            KFilePlacesModel::RemoteType,
1252                            KFilePlacesModel::RecentlySavedType,
1253                            KFilePlacesModel::SearchForType,
1254                            KFilePlacesModel::DevicesType,
1255                            KFilePlacesModel::RemovableDevicesType}) {
1256         const bool groupShouldBeHidden = (groupType == KFilePlacesModel::SearchForType);
1257         const QModelIndexList indexes = m_places->groupIndexes(groupType);
1258         for (const QModelIndex &index : indexes) {
1259             QCOMPARE(index.data(KFilePlacesModel::GroupHiddenRole).toBool(), groupShouldBeHidden);
1260         }
1261     }
1262 
1263     // set SearchFor group visible again
1264     m_places->setGroupHidden(KFilePlacesModel::SearchForType, false);
1265     for (int r = 0, rMax = m_places->rowCount(); r < rMax; r++) {
1266         const QModelIndex index = m_places->index(r, 0);
1267         QCOMPARE(index.data(KFilePlacesModel::GroupHiddenRole).toBool(), false);
1268     }
1269 }
1270 
1271 void KFilePlacesModelTest::testSupportedSchemes()
1272 {
1273     QCoreApplication::processEvents(); // support running this test on its own
1274 
1275     QCOMPARE(m_places->supportedSchemes(), QStringList());
1276     QCOMPARE(placesUrls(), initialListOfUrls());
1277     m_places->setSupportedSchemes({"trash"});
1278     QCOMPARE(m_places->supportedSchemes(), QStringList("trash"));
1279     QCOMPARE(placesUrls(), QStringList("trash:/"));
1280     m_places->setSupportedSchemes({});
1281     QCOMPARE(m_places->supportedSchemes(), QStringList());
1282     QCOMPARE(placesUrls(), initialListOfUrls());
1283 }
1284 
1285 QTEST_MAIN(KFilePlacesModelTest)
1286 
1287 #include "kfileplacesmodeltest.moc"