]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/tests/placesitemmodeltest.cpp
Follow-up of commit 9760f9607d
[dolphin.git] / src / tests / placesitemmodeltest.cpp
index 5eb65e076dd914e48a6e59fb6073f1894f027658..9f5e49d38a22fcf26a8b3345ed18043535a90ec3 100644 (file)
@@ -19,9 +19,6 @@
 
 #include <QTest>
 #include <QSignalSpy>
-#include <QDebug>
-#include <QList>
-#include <QByteArray>
 #include <QStandardPaths>
 #include <QAction>
 #include <QDBusInterface>
 #include <KConfig>
 #include <KConfigGroup>
 #include <KAboutData>
+#include <KFilePlacesModel>
 
 #include "panels/places/placesitemmodel.h"
 #include "panels/places/placesitem.h"
 #include "views/viewproperties.h"
-#include "kitemviews/kitemrange.h"
 
 Q_DECLARE_METATYPE(KItemRangeList)
-Q_DECLARE_METATYPE(PlacesItem::GroupType)
+Q_DECLARE_METATYPE(KItemRange)
 
 #ifdef Q_OS_WIN
 //c:\ as root for windows
@@ -63,8 +60,8 @@ private slots:
     void cleanupTestCase();
 
     void testModelSort();
-    void testModelMove();
     void testGroups();
+    void testDeletePlace();
     void testPlaceItem_data();
     void testPlaceItem();
     void testTearDownDevice();
@@ -76,30 +73,43 @@ private slots:
     void testEditBookmark();
     void testEditAfterCreation();
     void testEditMetadata();
+    void testRefresh();
+    void testIcons_data();
+    void testIcons();
+    void testDragAndDrop();
+    void testHideDevices();
+    void testDuplicatedEntries();
+    void renameAfterCreation();
 
 private:
     PlacesItemModel* m_model;
+    QSet<int> m_tobeRemoved;
     QMap<QString, QDBusInterface *> m_interfacesMap;
+    int m_expectedModelCount = 15;
+    bool m_hasDesktopFolder = false;
+    bool m_hasDownloadsFolder = false;
 
     void setBalooEnabled(bool enabled);
     int indexOf(const QUrl &url);
     QDBusInterface *fakeManager();
     QDBusInterface *fakeDevice(const QString &udi);
-    QStringList placesUrls() const;
+    QStringList placesUrls(PlacesItemModel *model = nullptr) const;
     QStringList initialUrls() const;
     void createPlaceItem(const QString &text, const QUrl &url, const QString &icon);
+    void removePlaceAfter(int index);
+    void cancelPlaceRemoval(int index);
+    void removeTestUserData();
+    QMimeData *createMimeData(const QList<int> &indexes) const;
 };
 
-#define CHECK_PLACES_URLS(urls)                                              \
-    QStringList tmp(urls);                                                   \
-    QStringList places = placesUrls();                                       \
-    while(!places.isEmpty()) {                                               \
-        tmp.removeOne(places.takeFirst());                                   \
-    }                                                                        \
-    if (!tmp.isEmpty()) {                                                    \
-        qWarning() << "Expected:" << urls;                                   \
-        qWarning() << "Got:" << places;                                      \
-        QCOMPARE(places, urls);                                              \
+#define CHECK_PLACES_URLS(urls)                                             \
+    {                                                                       \
+        QStringList places = placesUrls();                                  \
+        if (places != urls) {                                               \
+            qWarning() << "Expected:" << urls;                              \
+            qWarning() << "Got:" << places;                                 \
+            QCOMPARE(places, urls);                                         \
+        }                                                                   \
     }
 
 void PlacesItemModelTest::setBalooEnabled(bool enabled)
@@ -137,23 +147,89 @@ QDBusInterface *PlacesItemModelTest::fakeDevice(const QString &udi)
     return iface;
 }
 
-QStringList PlacesItemModelTest::placesUrls() const
+QStringList PlacesItemModelTest::placesUrls(PlacesItemModel *model) const
 {
     QStringList urls;
-    for (int row = 0; row < m_model->count(); ++row) {
-        urls << m_model->placesItem(row)->url().toDisplayString(QUrl::PreferLocalFile);
+    if (!model) {
+        model = m_model;
+    }
+
+    for (int row = 0; row < model->count(); ++row) {
+        urls << model->placesItem(row)->url().toDisplayString(QUrl::PreferLocalFile);
+    }
+    return urls;
+}
+
+QStringList PlacesItemModelTest::initialUrls() const
+{
+    static QStringList urls;
+    if (urls.isEmpty()) {
+        urls << QDir::homePath();
+
+        if (m_hasDesktopFolder) {
+            urls << QDir::homePath() + QStringLiteral("/Desktop");
+        }
+
+        if (m_hasDownloadsFolder) {
+            urls << QDir::homePath() + QStringLiteral("/Downloads");
+        }
+
+        urls << QStringLiteral(KDE_ROOT_PATH) << QStringLiteral("trash:/")
+             << QStringLiteral("remote:/")
+             << QStringLiteral("/media/nfs")
+             << QStringLiteral("timeline:/today") << QStringLiteral("timeline:/yesterday")
+             << QStringLiteral("search:/documents") << QStringLiteral("search:/images") << QStringLiteral("search:/audio") << QStringLiteral("search:/videos")
+             << QStringLiteral("/foreign")
+             << QStringLiteral("/media/floppy0") << QStringLiteral("/media/XO-Y4") << QStringLiteral("/media/cdrom");
     }
     return urls;
 }
 
 void PlacesItemModelTest::createPlaceItem(const QString &text, const QUrl &url, const QString &icon)
 {
-    PlacesItem *item = m_model->createPlacesItem(text,
-                                                 url,
-                                                 icon);
-    QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
-    m_model->appendItemToGroup(item);
-    QTRY_COMPARE(itemsInsertedSpy.count(), 1);
+    m_model->createPlacesItem(text, url, icon);
+}
+
+void PlacesItemModelTest::removePlaceAfter(int index)
+{
+    m_tobeRemoved.insert(index);
+}
+
+void PlacesItemModelTest::cancelPlaceRemoval(int index)
+{
+    m_tobeRemoved.remove(index);
+}
+
+void PlacesItemModelTest::removeTestUserData()
+{
+    // user hardcoded path to avoid removal of any user personal data
+    QDir dir(QStringLiteral("/home/renato/.qttest/share/placesitemmodeltest"));
+    if (dir.exists()) {
+        QVERIFY(dir.removeRecursively());
+    }
+}
+
+QMimeData *PlacesItemModelTest::createMimeData(const QList<int> &indexes) const
+{
+    QByteArray itemData;
+    QDataStream stream(&itemData, QIODevice::WriteOnly);
+    QList<QUrl> urls;
+
+    for (int index : indexes) {
+        const QUrl itemUrl = m_model->placesItem(index)->url();
+        if (itemUrl.isValid()) {
+            urls << itemUrl;
+        }
+        stream << index;
+    }
+
+    QMimeData* mimeData = new QMimeData();
+    mimeData->setUrls(urls);
+    // copied from PlacesItemModel::internalMimeType()
+    const QString internalMimeType = "application/x-dolphinplacesmodel-" +
+            QString::number((qptrdiff)m_model);
+    mimeData->setData(internalMimeType, itemData);
+    return mimeData;
 }
 
 void PlacesItemModelTest::init()
@@ -161,18 +237,28 @@ void PlacesItemModelTest::init()
     m_model = new PlacesItemModel();
     // WORKAROUND: need to wait for bookmark to load, check: PlacesItemModel::updateBookmarks
     QTest::qWait(300);
-    QCOMPARE(m_model->count(), 17);
+    QCOMPARE(m_model->count(), m_expectedModelCount);
 }
 
 void PlacesItemModelTest::cleanup()
 {
+    const auto tobeRemoved = m_tobeRemoved;
+    for (const int i : tobeRemoved) {
+        int before = m_model->count();
+        m_model->deleteItem(i);
+        QTRY_COMPARE(m_model->count(), before - 1);
+    }
+    m_tobeRemoved.clear();
     delete m_model;
     m_model = nullptr;
+    removeTestUserData();
 }
 
 void PlacesItemModelTest::initTestCase()
 {
     QStandardPaths::setTestModeEnabled(true);
+    // remove test user data
+    removeTestUserData();
 
     const QString fakeHw = QFINDTESTDATA("data/fakecomputer.xml");
     QVERIFY(!fakeHw.isEmpty());
@@ -185,25 +271,27 @@ void PlacesItemModelTest::initTestCase()
         QVERIFY(QFile::remove(bookmarsFileName));
     }
 
+    if (QDir(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).exists()) {
+        m_hasDesktopFolder = true;
+        m_expectedModelCount++;
+    }
+
+    if (QDir(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)).exists()) {
+        m_hasDownloadsFolder = true;
+        m_expectedModelCount++;
+    }
+
     qRegisterMetaType<KItemRangeList>();
+    qRegisterMetaType<KItemRange>();
 }
 
 void PlacesItemModelTest::cleanupTestCase()
 {
     qDeleteAll(m_interfacesMap);
     QFile::remove(bookmarksFile());
-}
-
-QStringList PlacesItemModelTest::initialUrls() const
-{
-    QStringList urls;
-
-    urls << QDir::homePath() << QStringLiteral("remote:/") << QStringLiteral(KDE_ROOT_PATH) << QStringLiteral("trash:/")
-            << QStringLiteral("timeline:/today") << QStringLiteral("timeline:/yesterday") << QStringLiteral("timeline:/thismonth") << QStringLiteral("timeline:/lastmonth")
-            << QStringLiteral("search:/documents") << QStringLiteral("search:/images") << QStringLiteral("search:/audio") << QStringLiteral("search:/videos")
-            << QStringLiteral("/media/cdrom") << QStringLiteral("/foreign") << QStringLiteral("/media/XO-Y4") << QStringLiteral("/media/nfs") << QStringLiteral("/media/floppy0");
 
-    return urls;
+    // Remove any previous properties file
+    removeTestUserData();
 }
 
 void PlacesItemModelTest::testModelSort()
@@ -211,36 +299,30 @@ void PlacesItemModelTest::testModelSort()
     CHECK_PLACES_URLS(initialUrls());
 }
 
-void PlacesItemModelTest::testModelMove()
-{
-    QStringList urls = initialUrls();
-    KBookmarkManager *bookmarkManager = KBookmarkManager::managerForFile(bookmarksFile(), QStringLiteral("kfilePlaces"));
-    KBookmarkGroup root = bookmarkManager->root();
-    KBookmark systemRoot = m_model->placesItem(1)->bookmark();
-    KBookmark last = m_model->placesItem(m_model->count() - 1)->bookmark();
-
-    // try to move the "root" path to the end of the list
-    root.moveBookmark(systemRoot, last);
-    bookmarkManager->emitChanged(root);
-
-    // make sure that the items still grouped and the "root" item was moved to the end of places group instead
-    urls.move(1, 2);
-    CHECK_PLACES_URLS(urls);
-}
-
 void PlacesItemModelTest::testGroups()
 {
     const auto groups = m_model->groups();
 
-    QCOMPARE(groups.size(), 4);
+
+    QCOMPARE(groups.size(), 6);
+
     QCOMPARE(groups.at(0).first, 0);
     QCOMPARE(groups.at(0).second.toString(), QStringLiteral("Places"));
-    QCOMPARE(groups.at(1).first, 4);
-    QCOMPARE(groups.at(1).second.toString(), QStringLiteral("Recently Saved"));
-    QCOMPARE(groups.at(2).first, 8);
-    QCOMPARE(groups.at(2).second.toString(), QStringLiteral("Search For"));
-    QCOMPARE(groups.at(3).first, 12);
-    QCOMPARE(groups.at(3).second.toString(), QStringLiteral("Devices"));
+
+    QCOMPARE(groups.at(1).first, 5);
+    QCOMPARE(groups.at(1).second.toString(), QStringLiteral("Remote"));
+
+    QCOMPARE(groups.at(2).first, 7);
+    QCOMPARE(groups.at(2).second.toString(), QStringLiteral("Recently Saved"));
+
+    QCOMPARE(groups.at(3).first, 9);
+    QCOMPARE(groups.at(3).second.toString(), QStringLiteral("Search For"));
+
+    QCOMPARE(groups.at(4).first, 13);
+    QCOMPARE(groups.at(4).second.toString(), QStringLiteral("Devices"));
+
+    QCOMPARE(groups.at(5).first, 14);
+    QCOMPARE(groups.at(5).second.toString(), QStringLiteral("Removable Devices"));
 }
 
 void PlacesItemModelTest::testPlaceItem_data()
@@ -248,20 +330,20 @@ void PlacesItemModelTest::testPlaceItem_data()
     QTest::addColumn<QUrl>("url");
     QTest::addColumn<bool>("expectedIsHidden");
     QTest::addColumn<bool>("expectedIsSystemItem");
-    QTest::addColumn<PlacesItem::GroupType>("expectedGroupType");
+    QTest::addColumn<QString>("expectedGroup");
     QTest::addColumn<bool>("expectedStorageSetupNeeded");
 
     // places
-    QTest::newRow("Places - Home") << QUrl::fromLocalFile(QDir::homePath()) << false << true << PlacesItem::PlacesType << false;
+    QTest::newRow("Places - Home") << QUrl::fromLocalFile(QDir::homePath()) << false << true << QStringLiteral("Places") << false;
 
     // baloo -search
-    QTest::newRow("Baloo - Documents") << QUrl("search:/documents") << false << true << PlacesItem::SearchForType << false;
+    QTest::newRow("Baloo - Documents") << QUrl("search:/documents") << false << true << QStringLiteral("Search For") << false;
 
     // baloo - timeline
-    QTest::newRow("Baloo - Last Month") << QUrl("timeline:/lastmonth") << false << true << PlacesItem::RecentlySavedType << false;
+    QTest::newRow("Baloo - Today") << QUrl("timeline:/today") << false << true << QStringLiteral("Recently Saved") << false;
 
     // devices
-    QTest::newRow("Devices - Floppy") << QUrl("file:///media/floppy0") << false << false << PlacesItem::DevicesType << false;
+    QTest::newRow("Devices - Floppy") << QUrl("file:///media/floppy0") << false << false << QStringLiteral("Removable Devices") << false;
 }
 
 void PlacesItemModelTest::testPlaceItem()
@@ -269,7 +351,7 @@ void PlacesItemModelTest::testPlaceItem()
     QFETCH(QUrl, url);
     QFETCH(bool, expectedIsHidden);
     QFETCH(bool, expectedIsSystemItem);
-    QFETCH(PlacesItem::GroupType, expectedGroupType);
+    QFETCH(QString, expectedGroup);
     QFETCH(bool, expectedStorageSetupNeeded);
 
     const int index = indexOf(url);
@@ -277,10 +359,38 @@ void PlacesItemModelTest::testPlaceItem()
     QCOMPARE(item->url(), url);
     QCOMPARE(item->isHidden(), expectedIsHidden);
     QCOMPARE(item->isSystemItem(), expectedIsSystemItem);
-    QCOMPARE(item->groupType(), expectedGroupType);
+    QCOMPARE(item->group(), expectedGroup);
     QCOMPARE(item->storageSetupNeeded(), expectedStorageSetupNeeded);
 }
 
+void PlacesItemModelTest::testDeletePlace()
+{
+    const QUrl tempUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
+    QStringList urls = initialUrls();
+    QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
+    QSignalSpy itemsRemovedSpy(m_model, &PlacesItemModel::itemsRemoved);
+
+    PlacesItemModel *model = new PlacesItemModel();
+
+    // create a new place
+    createPlaceItem(QStringLiteral("Temporary Dir"), tempUrl, QString());
+    urls.insert(5, tempUrl.toLocalFile());
+
+    // check if the new entry was created
+    QTRY_COMPARE(itemsInsertedSpy.count(), 1);
+    CHECK_PLACES_URLS(urls);
+    QTRY_COMPARE(model->count(), m_model->count());
+
+    // delete item
+    m_model->deleteItem(5);
+
+    // make sure that the new item is removed
+    QTRY_COMPARE(itemsRemovedSpy.count(), 1);
+    QTRY_COMPARE(m_model->count(), m_expectedModelCount);
+    CHECK_PLACES_URLS(initialUrls());
+    QTRY_COMPARE(model->count(), m_model->count());
+}
+
 void PlacesItemModelTest::testTearDownDevice()
 {
     const QUrl mediaUrl = QUrl::fromLocalFile(QStringLiteral("/media/XO-Y4"));
@@ -293,7 +403,7 @@ void PlacesItemModelTest::testTearDownDevice()
     auto teardownAction = m_model->teardownAction(index);
     QVERIFY(teardownAction);
 
-    QCOMPARE(m_model->count(), 17);
+    QCOMPARE(m_model->count(), m_expectedModelCount);
 
     QSignalSpy spyItemsRemoved(m_model, &PlacesItemModel::itemsRemoved);
     fakeManager()->call(QStringLiteral("unplug"), "/org/kde/solid/fakehw/volume_part1_size_993284096");
@@ -309,7 +419,7 @@ void PlacesItemModelTest::testTearDownDevice()
 
     QSignalSpy spyItemsInserted(m_model, &PlacesItemModel::itemsInserted);
     fakeManager()->call(QStringLiteral("plug"), "/org/kde/solid/fakehw/volume_part1_size_993284096");
-    QTRY_COMPARE(m_model->count(), 17);
+    QTRY_COMPARE(m_model->count(), m_expectedModelCount);
     QCOMPARE(spyItemsInserted.count(), 1);
     index = indexOf(mediaUrl);
 
@@ -337,7 +447,7 @@ void PlacesItemModelTest::testDefaultViewProperties_data()
     QTest::newRow("Places - Audio") << QUrl("search:/audio") << DolphinView::DetailsView << false << QList<QByteArray>({"text", "artist", "album"});
 
     // baloo - timeline
-    QTest::newRow("Baloo - Last Month") << QUrl("timeline:/lastmonth") << DolphinView::DetailsView << true << QList<QByteArray>({"text", "modificationtime"});
+    QTest::newRow("Baloo - Today") << QUrl("timeline:/today") << DolphinView::DetailsView << true << QList<QByteArray>({"text", "modificationtime"});
 
     // devices
     QTest::newRow("Devices - Floppy") << QUrl("file:///media/floppy0") << DolphinView::IconsView << true << QList<QByteArray>({"text"});
@@ -351,7 +461,7 @@ void PlacesItemModelTest::testDefaultViewProperties()
     QFETCH(bool, expectedPreviewShow);
     QFETCH(QList<QByteArray>, expectedVisibleRole);
 
-    ViewProperties properties(m_model->convertedUrl(url));
+    ViewProperties properties(KFilePlacesModel::convertedUrl(url));
     QCOMPARE(properties.viewMode(), expectedViewMode);
     QCOMPARE(properties.previewsShown(), expectedPreviewShow);
     QCOMPARE(properties.visibleRoles(), expectedVisibleRole);
@@ -359,10 +469,12 @@ void PlacesItemModelTest::testDefaultViewProperties()
 
 void PlacesItemModelTest::testClear()
 {
-    QCOMPARE(m_model->count(), 17);
+    QCOMPARE(m_model->count(), m_expectedModelCount);
     m_model->clear();
     QCOMPARE(m_model->count(), 0);
     QCOMPARE(m_model->hiddenCount(), 0);
+    m_model->refresh();
+    QTRY_COMPARE(m_model->count(), m_expectedModelCount);
 }
 
 void PlacesItemModelTest::testHideItem()
@@ -410,43 +522,47 @@ void PlacesItemModelTest::testHideItem()
      // mark model to hide invisible items
     m_model->setHiddenItemsShown(true);
 
-    QTRY_COMPARE(m_model->count(), 17);
+    QTRY_COMPARE(m_model->count(), m_expectedModelCount);
 }
 
 void PlacesItemModelTest::testSystemItems()
 {
-    QCOMPARE(m_model->count(), 17);
+    QCOMPARE(m_model->count(), m_expectedModelCount);
     for (int r = 0; r < m_model->count(); r++) {
         QCOMPARE(m_model->placesItem(r)->isSystemItem(), !m_model->placesItem(r)->device().isValid());
     }
 
-    // create a new entry (non system item)
-    PlacesItem *item = m_model->createPlacesItem(QStringLiteral("Temporary Dir"),
-                                                 QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation)),
-                                                 QString());
-
     QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
-    m_model->appendItemToGroup(item);
+
+    // create a new entry (non system item)
+    createPlaceItem(QStringLiteral("Temporary Dir"),  QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation)), QString());
 
     // check if the new entry was created
     QTRY_COMPARE(itemsInsertedSpy.count(), 1);
+
+    // make sure the new place get removed
+    removePlaceAfter(5);
+
     QList<QVariant> args = itemsInsertedSpy.takeFirst();
     KItemRangeList range = args.at(0).value<KItemRangeList>();
-    QCOMPARE(range.first().index, 4);
+    QCOMPARE(range.first().index, 5);
     QCOMPARE(range.first().count, 1);
-    QVERIFY(!m_model->placesItem(4)->isSystemItem());
+    QVERIFY(!m_model->placesItem(5)->isSystemItem());
     QCOMPARE(m_model->count(), 18);
 
-    // remove new entry
+    QTest::qWait(300);
+    // check if the removal signal is correct
     QSignalSpy itemsRemovedSpy(m_model, &PlacesItemModel::itemsRemoved);
-    m_model->removeItem(4);
-    m_model->saveBookmarks();
+    m_model->deleteItem(5);
     QTRY_COMPARE(itemsRemovedSpy.count(), 1);
     args = itemsRemovedSpy.takeFirst();
     range = args.at(0).value<KItemRangeList>();
-    QCOMPARE(range.first().index, 4);
+    QCOMPARE(range.first().index, 5);
     QCOMPARE(range.first().count, 1);
-    QTRY_COMPARE(m_model->count(), 17);
+    QTRY_COMPARE(m_model->count(), m_expectedModelCount);
+
+    //cancel removal (it was removed above)
+    cancelPlaceRemoval(5);
 }
 
 void PlacesItemModelTest::testEditBookmark()
@@ -455,80 +571,298 @@ void PlacesItemModelTest::testEditBookmark()
 
     createPlaceItem(QStringLiteral("Temporary Dir"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation)), QString());
 
+    // make sure that the new item will be removed later
+    removePlaceAfter(5);
+
     QSignalSpy itemsChangedSply(m_model, &PlacesItemModel::itemsChanged);
-    m_model->item(4)->setText(QStringLiteral("Renamed place"));
-    m_model->saveBookmarks();
+
+    // modify place text
+    m_model->item(3)->setText(QStringLiteral("Renamed place"));
+    m_model->refresh();
+
+    // check if the correct signal was fired
     QTRY_COMPARE(itemsChangedSply.count(), 1);
     QList<QVariant> args = itemsChangedSply.takeFirst();
     KItemRangeList range = args.at(0).value<KItemRangeList>();
-    QCOMPARE(range.first().index, 4);
+    QCOMPARE(range.first().index, 3);
     QCOMPARE(range.first().count, 1);
     QSet<QByteArray> roles = args.at(1).value<QSet<QByteArray> >();
     QCOMPARE(roles.size(), 1);
     QCOMPARE(*roles.begin(), QByteArrayLiteral("text"));
-    QCOMPARE(m_model->item(4)->text(), QStringLiteral("Renamed place"));
+    QCOMPARE(m_model->item(3)->text(), QStringLiteral("Renamed place"));
 
     // check if the item was updated in the other model
-    QTRY_COMPARE(other->item(4)->text(), QStringLiteral("Renamed place"));
-
-    // remove new entry
-    QSignalSpy itemsRemovedSpy(m_model, &PlacesItemModel::itemsRemoved);
-    m_model->removeItem(4);
-    m_model->saveBookmarks();
-    QTRY_COMPARE(itemsRemovedSpy.count(), 1);
-    args = itemsRemovedSpy.takeFirst();
-    range = args.at(0).value<KItemRangeList>();
-    QCOMPARE(range.first().index, 4);
-    QCOMPARE(range.first().count, 1);
-    QTRY_COMPARE(m_model->count(), 17);
+    QTRY_COMPARE(other->item(3)->text(), QStringLiteral("Renamed place"));
 }
 
 void PlacesItemModelTest::testEditAfterCreation()
 {
-    createPlaceItem(QStringLiteral("Temporary Dir"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation)), QString());
+    const QUrl tempUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
+    QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
+
+    // create a new place
+    createPlaceItem(QStringLiteral("Temporary Dir"), tempUrl, QString());
+    QTRY_COMPARE(itemsInsertedSpy.count(), 1);
 
     PlacesItemModel *model = new PlacesItemModel();
     QTRY_COMPARE(model->count(), m_model->count());
 
-    PlacesItem *item = m_model->placesItem(4);
+    // make sure that the new item will be removed later
+    removePlaceAfter(5);
+
+    // modify place text
+    PlacesItem *item = m_model->placesItem(3);
     item->setText(QStringLiteral("Renamed place"));
-    m_model->saveBookmarks();
+    m_model->refresh();
 
+    // check if the second model got the changes
     QTRY_COMPARE(model->count(), m_model->count());
-    QTRY_COMPARE(model->placesItem(4)->text(), m_model->placesItem(4)->text());
-    QTRY_COMPARE(model->placesItem(4)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
-                 m_model->placesItem(4)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")));
-    QTRY_COMPARE(model->placesItem(4)->icon(), m_model->placesItem(4)->icon());
-    QTRY_COMPARE(model->placesItem(4)->url(), m_model->placesItem(4)->url());
-
-    m_model->removeItem(4);
-    m_model->saveBookmarks();
-    QTRY_COMPARE(model->count(), m_model->count());
+    QTRY_COMPARE(model->placesItem(3)->text(), m_model->placesItem(3)->text());
+    QTRY_COMPARE(model->placesItem(3)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
+                 m_model->placesItem(3)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")));
+    QTRY_COMPARE(model->placesItem(3)->icon(), m_model->placesItem(3)->icon());
+    QTRY_COMPARE(model->placesItem(3)->url(), m_model->placesItem(3)->url());
 }
 
 void PlacesItemModelTest::testEditMetadata()
 {
-    createPlaceItem(QStringLiteral("Temporary Dir"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation)), QString());
+    const QUrl tempUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
+    QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
 
+    // create a new place
+    createPlaceItem(QStringLiteral("Temporary Dir"), tempUrl, QString());
+    QTRY_COMPARE(itemsInsertedSpy.count(), 1);
+
+    // check if the new entry was created
     PlacesItemModel *model = new PlacesItemModel();
     QTRY_COMPARE(model->count(), m_model->count());
 
-    PlacesItem *item = m_model->placesItem(4);
+    // make sure that the new item will be removed later
+    removePlaceAfter(5);
+
+    // modify place metadata
+    PlacesItem *item = m_model->placesItem(3);
     item->bookmark().setMetaDataItem(QStringLiteral("OnlyInApp"), KAboutData::applicationData().componentName());
-    m_model->saveBookmarks();
+    m_model->refresh();
 
-    QTRY_COMPARE(model->count(), m_model->count());
-    QTRY_COMPARE(model->placesItem(4)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
+    // check if the place was modified in both models
+    QTRY_COMPARE(model->placesItem(3)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
                  KAboutData::applicationData().componentName());
-    QTRY_COMPARE(model->placesItem(4)->text(), m_model->placesItem(4)->text());
-    QTRY_COMPARE(model->placesItem(4)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
-                 m_model->placesItem(4)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")));
-    QTRY_COMPARE(model->placesItem(4)->icon(), m_model->placesItem(4)->icon());
-    QTRY_COMPARE(model->placesItem(4)->url(), m_model->placesItem(4)->url());
-
-    m_model->removeItem(4);
-    m_model->saveBookmarks();
+    QTRY_COMPARE(model->placesItem(3)->text(), m_model->placesItem(3)->text());
+    QTRY_COMPARE(model->placesItem(3)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
+                 m_model->placesItem(3)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")));
+    QTRY_COMPARE(model->placesItem(3)->icon(), m_model->placesItem(3)->icon());
+    QTRY_COMPARE(model->placesItem(3)->url(), m_model->placesItem(3)->url());
+}
+
+void PlacesItemModelTest::testRefresh()
+{
+    const QUrl tempUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
+    QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
+
+    // create a new place
+    createPlaceItem(QStringLiteral("Temporary Dir"), tempUrl, QString());
+    QTRY_COMPARE(itemsInsertedSpy.count(), 1);
+
+    PlacesItemModel *model = new PlacesItemModel();
+    QTRY_COMPARE(model->count(), m_model->count());
+
+    // make sure that the new item will be removed later
+    removePlaceAfter(5);
+
+    PlacesItem *item = m_model->placesItem(5);
+    PlacesItem *sameItem = model->placesItem(5);
+    QCOMPARE(item->text(), sameItem->text());
+
+    // modify place text
+    item->setText(QStringLiteral("Renamed place"));
+
+    // item from another model is not affected at the moment
+    QVERIFY(item->text() != sameItem->text());
+
+    // propagate change
+    m_model->refresh();
+
+    // item must be equal
+    QTRY_COMPARE(item->text(), sameItem->text());
+}
+
+void PlacesItemModelTest::testIcons_data()
+{
+    QTest::addColumn<QUrl>("url");
+    QTest::addColumn<QString>("expectedIconName");
+
+    // places
+    QTest::newRow("Places - Home") << QUrl::fromLocalFile(QDir::homePath()) << QStringLiteral("user-home");
+
+    // baloo -search
+    QTest::newRow("Baloo - Documents") << QUrl("search:/documents") << QStringLiteral("folder-text");
+
+    // baloo - timeline
+    QTest::newRow("Baloo - Today") << QUrl("timeline:/today") << QStringLiteral("go-jump-today");
+
+    // devices
+    QTest::newRow("Devices - Floppy") << QUrl("file:///media/floppy0") << QStringLiteral("blockdevice");
+}
+
+void PlacesItemModelTest::testIcons()
+{
+    QFETCH(QUrl, url);
+    QFETCH(QString, expectedIconName);
+
+    PlacesItem *item = m_model->placesItem(indexOf(url));
+    QCOMPARE(item->icon(), expectedIconName);
+
+    for (int r = 0; r < m_model->count(); r++) {
+        QVERIFY(!m_model->placesItem(r)->icon().isEmpty());
+    }
+}
+
+void PlacesItemModelTest::testDragAndDrop()
+{
+    QList<QVariant> args;
+    KItemRangeList range;
+    QStringList urls = initialUrls();
+    QSignalSpy itemsInsertedSpy(m_model, &PlacesItemModel::itemsInserted);
+    QSignalSpy itemsRemovedSpy(m_model, &PlacesItemModel::itemsRemoved);
+
+    CHECK_PLACES_URLS(initialUrls());
+    // Move the home directory to the end of the places group
+    QMimeData *dropData = createMimeData(QList<int>() << 0);
+    m_model->dropMimeDataBefore(m_model->count() - 1, dropData);
+    urls.move(0, 4);
+    delete dropData;
+
+    QTRY_COMPARE(itemsInsertedSpy.count(), 1);
+    QTRY_COMPARE(itemsRemovedSpy.count(), 1);
+
+    // remove item from actual position
+    args = itemsRemovedSpy.takeFirst();
+    range = args.at(0).value<KItemRangeList>();
+    QCOMPARE(range.size(), 1);
+    QCOMPARE(range.at(0).count, 1);
+    QCOMPARE(range.at(0).index, 0);
+
+    // insert intem in his group
+    args = itemsInsertedSpy.takeFirst();
+    range = args.at(0).value<KItemRangeList>();
+    QCOMPARE(range.size(), 1);
+    QCOMPARE(range.at(0).count, 1);
+    QCOMPARE(range.at(0).index, 4);
+
+    CHECK_PLACES_URLS(urls);
+
+    itemsInsertedSpy.clear();
+    itemsRemovedSpy.clear();
+
+    // Move home directory item back to its original position
+    dropData = createMimeData(QList<int>() << 4);
+    m_model->dropMimeDataBefore(0, dropData);
+    urls.move(4, 0);
+    delete dropData;
+
+    QTRY_COMPARE(itemsInsertedSpy.count(), 1);
+    QTRY_COMPARE(itemsRemovedSpy.count(), 1);
+
+    // remove item from actual position
+    args = itemsRemovedSpy.takeFirst();
+    range = args.at(0).value<KItemRangeList>();
+    QCOMPARE(range.size(), 1);
+    QCOMPARE(range.at(0).count, 1);
+    QCOMPARE(range.at(0).index, 4);
+
+    // insert intem in the requested position
+    args = itemsInsertedSpy.takeFirst();
+    range = args.at(0).value<KItemRangeList>();
+    QCOMPARE(range.size(), 1);
+    QCOMPARE(range.at(0).count, 1);
+    QCOMPARE(range.at(0).index, 0);
+
+    CHECK_PLACES_URLS(urls);
+}
+
+void PlacesItemModelTest::testHideDevices()
+{
+    QSignalSpy itemsRemoved(m_model, &PlacesItemModel::itemsRemoved);
+    QStringList urls = initialUrls();
+
+    m_model->setGroupHidden(KFilePlacesModel::RemovableDevicesType, true);
+    QTRY_VERIFY(m_model->isGroupHidden(KFilePlacesModel::RemovableDevicesType));
+    QTRY_COMPARE(itemsRemoved.count(), 3);
+
+    // remove removable-devices
+    urls.removeOne(QStringLiteral("/media/floppy0"));
+    urls.removeOne(QStringLiteral("/media/XO-Y4"));
+    urls.removeOne(QStringLiteral("/media/cdrom"));
+
+    // check if the correct urls was removed
+    CHECK_PLACES_URLS(urls);
+
+    delete m_model;
+    m_model = new PlacesItemModel();
+    QTRY_COMPARE(m_model->count(), urls.count());
+    CHECK_PLACES_URLS(urls);
+
+    // revert changes
+    m_model->setGroupHidden(KFilePlacesModel::RemovableDevicesType, false);
+    urls = initialUrls();
+    QTRY_COMPARE(m_model->count(), urls.count());
+    CHECK_PLACES_URLS(urls);
+}
+
+void PlacesItemModelTest::testDuplicatedEntries()
+{
+    QStringList urls = initialUrls();
+    // create a duplicated entry on bookmark
+    KBookmarkManager *bookmarkManager = KBookmarkManager::managerForFile(bookmarksFile(), QStringLiteral("kfilePlaces"));
+    KBookmarkGroup root = bookmarkManager->root();
+    KBookmark bookmark = root.addBookmark(QStringLiteral("Duplicated Search Videos"), QUrl("search:/videos"), {});
+
+    const QString id = QUuid::createUuid().toString();
+    bookmark.setMetaDataItem(QStringLiteral("ID"), id);
+    bookmark.setMetaDataItem(QStringLiteral("OnlyInApp"), KAboutData::applicationData().componentName());
+    bookmarkManager->emitChanged(bookmarkManager->root());
+
+    PlacesItemModel *newModel = new PlacesItemModel();
+    QTRY_COMPARE(placesUrls(newModel).count(QStringLiteral("search:/videos")), 1);
+    QTRY_COMPARE(urls, placesUrls(newModel));
+    delete newModel;
+}
+
+void PlacesItemModelTest::renameAfterCreation()
+{
+    const QUrl tempUrl = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
+    QStringList urls = initialUrls();
+    PlacesItemModel *model = new PlacesItemModel();
+
+    CHECK_PLACES_URLS(urls);
     QTRY_COMPARE(model->count(), m_model->count());
+
+    // create a new place
+    createPlaceItem(QStringLiteral("Temporary Dir"), tempUrl, QString());
+    urls.insert(5, tempUrl.toLocalFile());
+
+    // make sure that the new item will be removed later
+    removePlaceAfter(5);
+
+    CHECK_PLACES_URLS(urls);
+    QCOMPARE(model->count(), m_model->count());
+
+
+    // modify place text
+    QSignalSpy changedSpy(m_model, &PlacesItemModel::itemsChanged);
+
+    PlacesItem *item = m_model->placesItem(3);
+    item->setText(QStringLiteral("New Temporary Dir"));
+    item->setUrl(item->url());
+    item->setIcon(item->icon());
+    m_model->refresh();
+
+    QTRY_COMPARE(changedSpy.count(), 1);
+
+    // check if the place was modified in both models
+    QTRY_COMPARE(m_model->placesItem(3)->text(), QStringLiteral("New Temporary Dir"));
+    QTRY_COMPARE(model->placesItem(3)->text(), QStringLiteral("New Temporary Dir"));
 }
 
 QTEST_MAIN(PlacesItemModelTest)