1 /***************************************************************************
2 * Copyright (C) 2017 by Renato Araujo Oliveira <renato.araujo@kdab.com> *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 ***************************************************************************/
25 #include <QStandardPaths>
27 #include <QDBusInterface>
29 #include <KBookmarkManager>
31 #include <KConfigGroup>
34 #include "panels/places/placesitemmodel.h"
35 #include "panels/places/placesitem.h"
36 #include "views/viewproperties.h"
37 #include "kitemviews/kitemrange.h"
39 Q_DECLARE_METATYPE(KItemRangeList
)
40 Q_DECLARE_METATYPE(PlacesItem::GroupType
)
43 //c:\ as root for windows
44 #define KDE_ROOT_PATH "C:\\"
46 #define KDE_ROOT_PATH "/"
49 static QString
bookmarksFile()
51 return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation
) + "/user-places.xbel";
54 class PlacesItemModelTest
: public QObject
63 void cleanupTestCase();
68 void testPlaceItem_data();
70 void testTearDownDevice();
71 void testDefaultViewProperties_data();
72 void testDefaultViewProperties();
75 void testSystemItems();
76 void testEditBookmark();
77 void testEditAfterCreation();
78 void testEditMetadata();
81 PlacesItemModel
* m_model
;
82 QMap
<QString
, QDBusInterface
*> m_interfacesMap
;
84 void setBalooEnabled(bool enabled
);
85 int indexOf(const QUrl
&url
);
86 QDBusInterface
*fakeManager();
87 QDBusInterface
*fakeDevice(const QString
&udi
);
88 QStringList
placesUrls() const;
89 QStringList
initialUrls() const;
90 void createPlaceItem(const QString
&text
, const QUrl
&url
, const QString
&icon
);
93 #define CHECK_PLACES_URLS(urls) \
94 QStringList tmp(urls); \
95 QStringList places = placesUrls(); \
96 while(!places.isEmpty()) { \
97 tmp.removeOne(places.takeFirst()); \
99 if (!tmp.isEmpty()) { \
100 qWarning() << "Expected:" << urls; \
101 qWarning() << "Got:" << places; \
102 QCOMPARE(places, urls); \
105 void PlacesItemModelTest::setBalooEnabled(bool enabled
)
107 KConfig
config(QStringLiteral("baloofilerc"));
108 KConfigGroup basicSettings
= config
.group("Basic Settings");
109 basicSettings
.writeEntry("Indexing-Enabled", enabled
);
113 int PlacesItemModelTest::indexOf(const QUrl
&url
)
115 for (int r
= 0; r
< m_model
->count(); r
++) {
116 if (m_model
->placesItem(r
)->url() == url
) {
123 QDBusInterface
*PlacesItemModelTest::fakeManager()
125 return fakeDevice(QStringLiteral("/org/kde/solid/fakehw"));
128 QDBusInterface
*PlacesItemModelTest::fakeDevice(const QString
&udi
)
130 if (m_interfacesMap
.contains(udi
)) {
131 return m_interfacesMap
[udi
];
134 QDBusInterface
*iface
= new QDBusInterface(QDBusConnection::sessionBus().baseService(), udi
);
135 m_interfacesMap
[udi
] = iface
;
140 QStringList
PlacesItemModelTest::placesUrls() const
143 for (int row
= 0; row
< m_model
->count(); ++row
) {
144 urls
<< m_model
->placesItem(row
)->url().toDisplayString(QUrl::PreferLocalFile
);
149 void PlacesItemModelTest::createPlaceItem(const QString
&text
, const QUrl
&url
, const QString
&icon
)
151 PlacesItem
*item
= m_model
->createPlacesItem(text
,
154 QSignalSpy
itemsInsertedSpy(m_model
, &PlacesItemModel::itemsInserted
);
155 m_model
->appendItemToGroup(item
);
156 QTRY_COMPARE(itemsInsertedSpy
.count(), 1);
159 void PlacesItemModelTest::init()
161 m_model
= new PlacesItemModel();
162 // WORKAROUND: need to wait for bookmark to load, check: PlacesItemModel::updateBookmarks
164 QCOMPARE(m_model
->count(), 17);
167 void PlacesItemModelTest::cleanup()
173 void PlacesItemModelTest::initTestCase()
175 QStandardPaths::setTestModeEnabled(true);
177 const QString fakeHw
= QFINDTESTDATA("data/fakecomputer.xml");
178 QVERIFY(!fakeHw
.isEmpty());
179 qputenv("SOLID_FAKEHW", QFile::encodeName(fakeHw
));
181 setBalooEnabled(true);
182 const QString bookmarsFileName
= bookmarksFile();
183 if (QFileInfo::exists(bookmarsFileName
)) {
184 // Ensure we'll have a clean bookmark file to start
185 QVERIFY(QFile::remove(bookmarsFileName
));
188 qRegisterMetaType
<KItemRangeList
>();
191 void PlacesItemModelTest::cleanupTestCase()
193 qDeleteAll(m_interfacesMap
);
194 QFile::remove(bookmarksFile());
197 QStringList
PlacesItemModelTest::initialUrls() const
201 urls
<< QDir::homePath() << QStringLiteral("remote:/") << QStringLiteral(KDE_ROOT_PATH
) << QStringLiteral("trash:/")
202 << QStringLiteral("timeline:/today") << QStringLiteral("timeline:/yesterday") << QStringLiteral("timeline:/thismonth") << QStringLiteral("timeline:/lastmonth")
203 << QStringLiteral("search:/documents") << QStringLiteral("search:/images") << QStringLiteral("search:/audio") << QStringLiteral("search:/videos")
204 << QStringLiteral("/media/cdrom") << QStringLiteral("/foreign") << QStringLiteral("/media/XO-Y4") << QStringLiteral("/media/nfs") << QStringLiteral("/media/floppy0");
209 void PlacesItemModelTest::testModelSort()
211 CHECK_PLACES_URLS(initialUrls());
214 void PlacesItemModelTest::testModelMove()
216 QStringList urls
= initialUrls();
217 KBookmarkManager
*bookmarkManager
= KBookmarkManager::managerForFile(bookmarksFile(), QStringLiteral("kfilePlaces"));
218 KBookmarkGroup root
= bookmarkManager
->root();
219 KBookmark systemRoot
= m_model
->placesItem(1)->bookmark();
220 KBookmark last
= m_model
->placesItem(m_model
->count() - 1)->bookmark();
222 // try to move the "root" path to the end of the list
223 root
.moveBookmark(systemRoot
, last
);
224 bookmarkManager
->emitChanged(root
);
226 // make sure that the items still grouped and the "root" item was moved to the end of places group instead
228 CHECK_PLACES_URLS(urls
);
231 void PlacesItemModelTest::testGroups()
233 const auto groups
= m_model
->groups();
235 QCOMPARE(groups
.size(), 4);
236 QCOMPARE(groups
.at(0).first
, 0);
237 QCOMPARE(groups
.at(0).second
.toString(), QStringLiteral("Places"));
238 QCOMPARE(groups
.at(1).first
, 4);
239 QCOMPARE(groups
.at(1).second
.toString(), QStringLiteral("Recently Saved"));
240 QCOMPARE(groups
.at(2).first
, 8);
241 QCOMPARE(groups
.at(2).second
.toString(), QStringLiteral("Search For"));
242 QCOMPARE(groups
.at(3).first
, 12);
243 QCOMPARE(groups
.at(3).second
.toString(), QStringLiteral("Devices"));
246 void PlacesItemModelTest::testPlaceItem_data()
248 QTest::addColumn
<QUrl
>("url");
249 QTest::addColumn
<bool>("expectedIsHidden");
250 QTest::addColumn
<bool>("expectedIsSystemItem");
251 QTest::addColumn
<PlacesItem::GroupType
>("expectedGroupType");
252 QTest::addColumn
<bool>("expectedStorageSetupNeeded");
255 QTest::newRow("Places - Home") << QUrl::fromLocalFile(QDir::homePath()) << false << true << PlacesItem::PlacesType
<< false;
258 QTest::newRow("Baloo - Documents") << QUrl("search:/documents") << false << true << PlacesItem::SearchForType
<< false;
261 QTest::newRow("Baloo - Last Month") << QUrl("timeline:/lastmonth") << false << true << PlacesItem::RecentlySavedType
<< false;
264 QTest::newRow("Devices - Floppy") << QUrl("file:///media/floppy0") << false << false << PlacesItem::DevicesType
<< false;
267 void PlacesItemModelTest::testPlaceItem()
270 QFETCH(bool, expectedIsHidden
);
271 QFETCH(bool, expectedIsSystemItem
);
272 QFETCH(PlacesItem::GroupType
, expectedGroupType
);
273 QFETCH(bool, expectedStorageSetupNeeded
);
275 const int index
= indexOf(url
);
276 PlacesItem
*item
= m_model
->placesItem(index
);
277 QCOMPARE(item
->url(), url
);
278 QCOMPARE(item
->isHidden(), expectedIsHidden
);
279 QCOMPARE(item
->isSystemItem(), expectedIsSystemItem
);
280 QCOMPARE(item
->groupType(), expectedGroupType
);
281 QCOMPARE(item
->storageSetupNeeded(), expectedStorageSetupNeeded
);
284 void PlacesItemModelTest::testTearDownDevice()
286 const QUrl mediaUrl
= QUrl::fromLocalFile(QStringLiteral("/media/XO-Y4"));
287 int index
= indexOf(mediaUrl
);
288 QVERIFY(index
!= -1);
290 auto ejectAction
= m_model
->ejectAction(index
);
291 QVERIFY(!ejectAction
);
293 auto teardownAction
= m_model
->teardownAction(index
);
294 QVERIFY(teardownAction
);
296 QCOMPARE(m_model
->count(), 17);
298 QSignalSpy
spyItemsRemoved(m_model
, &PlacesItemModel::itemsRemoved
);
299 fakeManager()->call(QStringLiteral("unplug"), "/org/kde/solid/fakehw/volume_part1_size_993284096");
300 QTRY_COMPARE(m_model
->count(), 16);
301 QCOMPARE(spyItemsRemoved
.count(), 1);
302 const QList
<QVariant
> spyItemsRemovedArgs
= spyItemsRemoved
.takeFirst();
303 const KItemRangeList removedRange
= spyItemsRemovedArgs
.at(0).value
<KItemRangeList
>();
304 QCOMPARE(removedRange
.size(), 1);
305 QCOMPARE(removedRange
.first().index
, index
);
306 QCOMPARE(removedRange
.first().count
, 1);
308 QCOMPARE(indexOf(mediaUrl
), -1);
310 QSignalSpy
spyItemsInserted(m_model
, &PlacesItemModel::itemsInserted
);
311 fakeManager()->call(QStringLiteral("plug"), "/org/kde/solid/fakehw/volume_part1_size_993284096");
312 QTRY_COMPARE(m_model
->count(), 17);
313 QCOMPARE(spyItemsInserted
.count(), 1);
314 index
= indexOf(mediaUrl
);
316 const QList
<QVariant
> args
= spyItemsInserted
.takeFirst();
317 const KItemRangeList insertedRange
= args
.at(0).value
<KItemRangeList
>();
318 QCOMPARE(insertedRange
.size(), 1);
319 QCOMPARE(insertedRange
.first().index
, index
);
320 QCOMPARE(insertedRange
.first().count
, 1);
323 void PlacesItemModelTest::testDefaultViewProperties_data()
325 QTest::addColumn
<QUrl
>("url");
326 QTest::addColumn
<DolphinView::Mode
>("expectedViewMode");
327 QTest::addColumn
<bool>("expectedPreviewShow");
328 QTest::addColumn
<QList
<QByteArray
> >("expectedVisibleRole");
331 QTest::newRow("Places - Home") << QUrl::fromLocalFile(QDir::homePath()) << DolphinView::IconsView
<< true << QList
<QByteArray
>({"text"});
334 QTest::newRow("Baloo - Documents") << QUrl("search:/documents") << DolphinView::DetailsView
<< false << QList
<QByteArray
>({"text", "path"});
337 QTest::newRow("Places - Audio") << QUrl("search:/audio") << DolphinView::DetailsView
<< false << QList
<QByteArray
>({"text", "artist", "album"});
340 QTest::newRow("Baloo - Last Month") << QUrl("timeline:/lastmonth") << DolphinView::DetailsView
<< true << QList
<QByteArray
>({"text", "modificationtime"});
343 QTest::newRow("Devices - Floppy") << QUrl("file:///media/floppy0") << DolphinView::IconsView
<< true << QList
<QByteArray
>({"text"});
347 void PlacesItemModelTest::testDefaultViewProperties()
350 QFETCH(DolphinView::Mode
, expectedViewMode
);
351 QFETCH(bool, expectedPreviewShow
);
352 QFETCH(QList
<QByteArray
>, expectedVisibleRole
);
354 ViewProperties
properties(m_model
->convertedUrl(url
));
355 QCOMPARE(properties
.viewMode(), expectedViewMode
);
356 QCOMPARE(properties
.previewsShown(), expectedPreviewShow
);
357 QCOMPARE(properties
.visibleRoles(), expectedVisibleRole
);
360 void PlacesItemModelTest::testClear()
362 QCOMPARE(m_model
->count(), 17);
364 QCOMPARE(m_model
->count(), 0);
365 QCOMPARE(m_model
->hiddenCount(), 0);
368 void PlacesItemModelTest::testHideItem()
370 const QUrl mediaUrl
= QUrl::fromLocalFile(QStringLiteral("/media/XO-Y4"));
371 const int index
= indexOf(mediaUrl
);
373 PlacesItem
*item
= m_model
->placesItem(index
);
375 QSignalSpy
spyItemsRemoved(m_model
, &PlacesItemModel::itemsRemoved
);
376 QList
<QVariant
> spyItemsRemovedArgs
;
377 KItemRangeList removedRange
;
379 QSignalSpy
spyItemsInserted(m_model
, &PlacesItemModel::itemsInserted
);
380 QList
<QVariant
> spyItemsInsertedArgs
;
381 KItemRangeList insertedRange
;
385 item
->setHidden(true);
387 // check if items removed was fired
388 QTRY_COMPARE(m_model
->count(), 16);
389 QCOMPARE(spyItemsRemoved
.count(), 1);
390 spyItemsRemovedArgs
= spyItemsRemoved
.takeFirst();
391 removedRange
= spyItemsRemovedArgs
.at(0).value
<KItemRangeList
>();
392 QCOMPARE(removedRange
.size(), 1);
393 QCOMPARE(removedRange
.first().index
, index
);
394 QCOMPARE(removedRange
.first().count
, 1);
396 // allow model to show hidden items
397 m_model
->setHiddenItemsShown(true);
399 // check if the items inserted was fired
400 spyItemsInsertedArgs
= spyItemsInserted
.takeFirst();
401 insertedRange
= spyItemsInsertedArgs
.at(0).value
<KItemRangeList
>();
402 QCOMPARE(insertedRange
.size(), 1);
403 QCOMPARE(insertedRange
.first().index
, index
);
404 QCOMPARE(insertedRange
.first().count
, 1);
406 // mark item as visible
407 item
= m_model
->placesItem(index
);
408 item
->setHidden(false);
410 // mark model to hide invisible items
411 m_model
->setHiddenItemsShown(true);
413 QTRY_COMPARE(m_model
->count(), 17);
416 void PlacesItemModelTest::testSystemItems()
418 QCOMPARE(m_model
->count(), 17);
419 for (int r
= 0; r
< m_model
->count(); r
++) {
420 QCOMPARE(m_model
->placesItem(r
)->isSystemItem(), !m_model
->placesItem(r
)->device().isValid());
423 // create a new entry (non system item)
424 PlacesItem
*item
= m_model
->createPlacesItem(QStringLiteral("Temporary Dir"),
425 QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation
)),
428 QSignalSpy
itemsInsertedSpy(m_model
, &PlacesItemModel::itemsInserted
);
429 m_model
->appendItemToGroup(item
);
431 // check if the new entry was created
432 QTRY_COMPARE(itemsInsertedSpy
.count(), 1);
433 QList
<QVariant
> args
= itemsInsertedSpy
.takeFirst();
434 KItemRangeList range
= args
.at(0).value
<KItemRangeList
>();
435 QCOMPARE(range
.first().index
, 4);
436 QCOMPARE(range
.first().count
, 1);
437 QVERIFY(!m_model
->placesItem(4)->isSystemItem());
438 QCOMPARE(m_model
->count(), 18);
441 QSignalSpy
itemsRemovedSpy(m_model
, &PlacesItemModel::itemsRemoved
);
442 m_model
->removeItem(4);
443 m_model
->saveBookmarks();
444 QTRY_COMPARE(itemsRemovedSpy
.count(), 1);
445 args
= itemsRemovedSpy
.takeFirst();
446 range
= args
.at(0).value
<KItemRangeList
>();
447 QCOMPARE(range
.first().index
, 4);
448 QCOMPARE(range
.first().count
, 1);
449 QTRY_COMPARE(m_model
->count(), 17);
452 void PlacesItemModelTest::testEditBookmark()
454 QScopedPointer
<PlacesItemModel
> other(new PlacesItemModel());
456 createPlaceItem(QStringLiteral("Temporary Dir"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation
)), QString());
458 QSignalSpy
itemsChangedSply(m_model
, &PlacesItemModel::itemsChanged
);
459 m_model
->item(4)->setText(QStringLiteral("Renamed place"));
460 m_model
->saveBookmarks();
461 QTRY_COMPARE(itemsChangedSply
.count(), 1);
462 QList
<QVariant
> args
= itemsChangedSply
.takeFirst();
463 KItemRangeList range
= args
.at(0).value
<KItemRangeList
>();
464 QCOMPARE(range
.first().index
, 4);
465 QCOMPARE(range
.first().count
, 1);
466 QSet
<QByteArray
> roles
= args
.at(1).value
<QSet
<QByteArray
> >();
467 QCOMPARE(roles
.size(), 1);
468 QCOMPARE(*roles
.begin(), QByteArrayLiteral("text"));
469 QCOMPARE(m_model
->item(4)->text(), QStringLiteral("Renamed place"));
471 // check if the item was updated in the other model
472 QTRY_COMPARE(other
->item(4)->text(), QStringLiteral("Renamed place"));
475 QSignalSpy
itemsRemovedSpy(m_model
, &PlacesItemModel::itemsRemoved
);
476 m_model
->removeItem(4);
477 m_model
->saveBookmarks();
478 QTRY_COMPARE(itemsRemovedSpy
.count(), 1);
479 args
= itemsRemovedSpy
.takeFirst();
480 range
= args
.at(0).value
<KItemRangeList
>();
481 QCOMPARE(range
.first().index
, 4);
482 QCOMPARE(range
.first().count
, 1);
483 QTRY_COMPARE(m_model
->count(), 17);
486 void PlacesItemModelTest::testEditAfterCreation()
488 createPlaceItem(QStringLiteral("Temporary Dir"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation
)), QString());
490 PlacesItemModel
*model
= new PlacesItemModel();
491 QTRY_COMPARE(model
->count(), m_model
->count());
493 PlacesItem
*item
= m_model
->placesItem(4);
494 item
->setText(QStringLiteral("Renamed place"));
495 m_model
->saveBookmarks();
497 QTRY_COMPARE(model
->count(), m_model
->count());
498 QTRY_COMPARE(model
->placesItem(4)->text(), m_model
->placesItem(4)->text());
499 QTRY_COMPARE(model
->placesItem(4)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
500 m_model
->placesItem(4)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")));
501 QTRY_COMPARE(model
->placesItem(4)->icon(), m_model
->placesItem(4)->icon());
502 QTRY_COMPARE(model
->placesItem(4)->url(), m_model
->placesItem(4)->url());
504 m_model
->removeItem(4);
505 m_model
->saveBookmarks();
506 QTRY_COMPARE(model
->count(), m_model
->count());
509 void PlacesItemModelTest::testEditMetadata()
511 createPlaceItem(QStringLiteral("Temporary Dir"), QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation
)), QString());
513 PlacesItemModel
*model
= new PlacesItemModel();
514 QTRY_COMPARE(model
->count(), m_model
->count());
516 PlacesItem
*item
= m_model
->placesItem(4);
517 item
->bookmark().setMetaDataItem(QStringLiteral("OnlyInApp"), KAboutData::applicationData().componentName());
518 m_model
->saveBookmarks();
520 QTRY_COMPARE(model
->count(), m_model
->count());
521 QTRY_COMPARE(model
->placesItem(4)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
522 KAboutData::applicationData().componentName());
523 QTRY_COMPARE(model
->placesItem(4)->text(), m_model
->placesItem(4)->text());
524 QTRY_COMPARE(model
->placesItem(4)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")),
525 m_model
->placesItem(4)->bookmark().metaDataItem(QStringLiteral("OnlyInApp")));
526 QTRY_COMPARE(model
->placesItem(4)->icon(), m_model
->placesItem(4)->icon());
527 QTRY_COMPARE(model
->placesItem(4)->url(), m_model
->placesItem(4)->url());
529 m_model
->removeItem(4);
530 m_model
->saveBookmarks();
531 QTRY_COMPARE(model
->count(), m_model
->count());
534 QTEST_MAIN(PlacesItemModelTest
)
536 #include "placesitemmodeltest.moc"