File indexing completed on 2024-05-12 05:46:29

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