2 * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
4 * SPDX-License-Identifier: GPL-2.0-or-later
7 #include "views/viewproperties.h"
8 #include "dolphin_generalsettings.h"
11 #include <KFileMetaData/UserMetaData>
13 #include <QStorageInfo>
16 class ViewPropertiesTest
: public QObject
25 void testReadOnlyBehavior();
26 void testReadOnlyDirectory();
28 void testParamMigrationToFileAttr();
29 void testParamMigrationToFileAttrKeepDirectory();
30 void testExtendedAttributeFull();
31 void testUseAsDefaultViewSettings();
32 void testUseAsCustomDefaultViewSettings();
35 bool m_globalViewProps
;
39 void ViewPropertiesTest::initTestCase()
41 QStandardPaths::setTestModeEnabled(true);
43 GeneralSettings::self()->setViewPropsTimestamp(QDateTime::currentDateTime());
44 QVERIFY(GeneralSettings::self()->save());
47 void ViewPropertiesTest::init()
49 m_globalViewProps
= GeneralSettings::self()->globalViewProps();
50 GeneralSettings::self()->setGlobalViewProps(false);
51 GeneralSettings::self()->save();
53 // It is mandatory to create the test-directory inside the home-directory
54 // of the user: ViewProperties does not write inside directories
55 // outside the home-directory to prevent overwriting other user-settings
56 // in case if write-permissions are given.
57 m_testDir
= new TestDir(QDir::homePath() + "/.viewPropertiesTest-");
60 void ViewPropertiesTest::cleanup()
65 GeneralSettings::self()->setGlobalViewProps(m_globalViewProps
);
66 GeneralSettings::self()->save();
70 * Test whether only reading properties won't result in creating
71 * a .directory file when destructing the ViewProperties instance
72 * and autosaving is enabled.
74 void ViewPropertiesTest::testReadOnlyBehavior()
76 QString dotDirectoryFile
= m_testDir
->url().toLocalFile() + "/.directory";
77 QVERIFY(!QFile::exists(dotDirectoryFile
));
79 QScopedPointer
<ViewProperties
> props(new ViewProperties(m_testDir
->url()));
80 QVERIFY(props
->isAutoSaveEnabled());
81 const QByteArray sortRole
= props
->sortRole();
85 QVERIFY(!QFile::exists(dotDirectoryFile
));
88 void ViewPropertiesTest::testReadOnlyDirectory()
90 const QUrl testDirUrl
= m_testDir
->url();
91 const QString localFolder
= testDirUrl
.toLocalFile();
92 const QString dotDirectoryFile
= localFolder
+ "/.directory";
93 QVERIFY(!QFile::exists(dotDirectoryFile
));
95 // restrict write permissions
96 QVERIFY(QFile(localFolder
).setPermissions(QFileDevice::ReadOwner
));
98 QScopedPointer
<ViewProperties
> props(new ViewProperties(testDirUrl
));
99 const QString destinationDir
= props
->destinationDir(QStringLiteral("local")) + localFolder
;
101 QVERIFY(props
->isAutoSaveEnabled());
102 props
->setSortRole("someNewSortRole");
105 qDebug() << destinationDir
;
106 QVERIFY(QDir(destinationDir
).exists());
108 QVERIFY(!QFile::exists(dotDirectoryFile
));
109 KFileMetaData::UserMetaData
metadata(localFolder
);
110 const QString viewProperties
= metadata
.attribute(QStringLiteral("kde.fm.viewproperties#1"));
111 QVERIFY(viewProperties
.isEmpty());
113 props
.reset(new ViewProperties(testDirUrl
));
114 QVERIFY(props
->isAutoSaveEnabled());
115 QCOMPARE(props
->sortRole(), "someNewSortRole");
118 metadata
= KFileMetaData::UserMetaData(destinationDir
);
119 if (metadata
.isSupported()) {
120 QVERIFY(metadata
.hasAttribute("kde.fm.viewproperties#1"));
122 QVERIFY(QFile::exists(destinationDir
+ "/.directory"));
125 // un-restrict write permissions
126 QFile(localFolder
).setPermissions(QFileDevice::ReadOwner
| QFileDevice::WriteOwner
);
129 void ViewPropertiesTest::testAutoSave()
131 QString dotDirectoryFile
= m_testDir
->url().toLocalFile() + "/.directory";
132 QVERIFY(!QFile::exists(dotDirectoryFile
));
134 QScopedPointer
<ViewProperties
> props(new ViewProperties(m_testDir
->url()));
135 QVERIFY(props
->isAutoSaveEnabled());
136 props
->setSortRole("someNewSortRole");
139 KFileMetaData::UserMetaData
metadata(m_testDir
->url().toLocalFile());
140 if (metadata
.isSupported()) {
141 auto viewProperties
= metadata
.attribute(QStringLiteral("kde.fm.viewproperties#1"));
142 QVERIFY(!viewProperties
.isEmpty());
143 QVERIFY(!QFile::exists(dotDirectoryFile
));
145 QVERIFY(QFile::exists(dotDirectoryFile
));
149 void ViewPropertiesTest::testParamMigrationToFileAttr()
151 QString dotDirectoryFilePath
= m_testDir
->url().toLocalFile() + "/.directory";
152 QVERIFY(!QFile::exists(dotDirectoryFilePath
));
154 const char *settingsContent
= R
"SETTINGS("
158 Timestamp
=2023,12,29,10,44,15.793
159 VisibleRoles
=text
,CustomizedDetails
,Details_text
,Details_modificationtime
,Details_type
162 HiddenFilesShown
=true)SETTINGS
";
163 auto dotDirectoryFile = QFile(dotDirectoryFilePath);
164 QVERIFY(dotDirectoryFile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate));
165 QTextStream out(&dotDirectoryFile);
166 out << settingsContent;
167 dotDirectoryFile.close();
169 KFileMetaData::UserMetaData metadata(m_testDir->url().toLocalFile());
171 QScopedPointer<ViewProperties> props(new ViewProperties(m_testDir->url()));
172 QCOMPARE(props->viewMode(), DolphinView::Mode::DetailsView);
173 QVERIFY(props->hiddenFilesShown());
176 if (metadata.isSupported()) {
177 auto viewProperties = metadata.attribute(QStringLiteral("kde
.fm
.viewproperties
#1"));
178 QVERIFY(!viewProperties.isEmpty());
179 QVERIFY(!QFile::exists(dotDirectoryFilePath));
181 QVERIFY(QFile::exists(dotDirectoryFilePath));
185 ViewProperties props(m_testDir->url());
186 QCOMPARE(props.viewMode(), DolphinView::Mode::DetailsView);
187 QVERIFY(props.hiddenFilesShown());
190 void ViewPropertiesTest::testParamMigrationToFileAttrKeepDirectory()
192 QString dotDirectoryFilePath = m_testDir->url().toLocalFile() + "/.directory";
193 QVERIFY(!QFile::exists(dotDirectoryFilePath));
195 const char *settingsContent = R"SETTINGS("
199 Timestamp=2023,12,29,10,44,15.793
200 VisibleRoles=text,CustomizedDetails,Details_text,Details_modificationtime,Details_type
203 HiddenFilesShown=true
206 ThoseShouldBeKept=true
208 auto dotDirectoryFile
= QFile(dotDirectoryFilePath
);
209 QVERIFY(dotDirectoryFile
.open(QIODevice::WriteOnly
| QIODevice::Text
| QIODevice::Truncate
));
210 QTextStream
out(&dotDirectoryFile
);
211 out
<< settingsContent
;
212 dotDirectoryFile
.close();
214 KFileMetaData::UserMetaData
metadata(m_testDir
->url().toLocalFile());
216 QScopedPointer
<ViewProperties
> props(new ViewProperties(m_testDir
->url()));
217 QCOMPARE(props
->viewMode(), DolphinView::Mode::DetailsView
);
218 QVERIFY(props
->hiddenFilesShown());
221 if (metadata
.isSupported()) {
222 auto viewProperties
= metadata
.attribute(QStringLiteral("kde.fm.viewproperties#1"));
223 QVERIFY(!viewProperties
.isEmpty());
226 QVERIFY(QFile::exists(dotDirectoryFilePath
));
227 KConfig
directorySettings(dotDirectoryFilePath
, KConfig::SimpleConfig
);
228 QCOMPARE(directorySettings
.groupList(), {"Other"});
231 ViewProperties
props(m_testDir
->url());
232 QVERIFY(props
.hiddenFilesShown());
233 QCOMPARE(props
.viewMode(), DolphinView::Mode::DetailsView
);
235 QVERIFY(QFile::exists(dotDirectoryFilePath
));
238 void ViewPropertiesTest::testExtendedAttributeFull()
241 QSKIP("Only unix is supported, for this test");
243 QString dotDirectoryFile
= m_testDir
->url().toLocalFile() + "/.directory";
244 QVERIFY(!QFile::exists(dotDirectoryFile
));
246 KFileMetaData::UserMetaData
metadata(m_testDir
->url().toLocalFile());
247 if (!metadata
.isSupported()) {
248 QSKIP("need extended attribute/filesystem metadata to be usefull");
251 QStorageInfo
storageInfo(m_testDir
->url().toLocalFile());
252 auto blockSize
= storageInfo
.blockSize();
254 KFileMetaData::UserMetaData::Error result
;
255 // write a close to block size theorical maximum size for attributes in Linux for ext4
256 // and btrfs (4Kib typically) when ReiserFS/XFS allow XATTR_SIZE_MAX (64Kib)
257 result
= metadata
.setAttribute("data", QString(blockSize
- 50, 'a'));
258 if (result
!= KFileMetaData::UserMetaData::NoSpace
) {
259 QSKIP("File system supports metadata bigger than file system block size");
262 // write a close to 4k attribute, maximum size in Linux for ext4
263 // so next writing the file metadata fails
264 result
= metadata
.setAttribute("data", QString(blockSize
- 60, 'a'));
265 QCOMPARE(result
, KFileMetaData::UserMetaData::NoError
);
267 QScopedPointer
<ViewProperties
> props(new ViewProperties(m_testDir
->url()));
268 QVERIFY(props
->isAutoSaveEnabled());
269 props
->setSortRole("someNewSortRole");
272 if (metadata
.isSupported()) {
273 auto viewProperties
= metadata
.attribute(QStringLiteral("kde.fm.viewproperties#1"));
274 QVERIFY(viewProperties
.isEmpty());
275 QVERIFY(QFile::exists(dotDirectoryFile
));
277 QFile
dotDirectory(dotDirectoryFile
);
278 KConfig
viewSettings(dotDirectoryFile
, KConfig::SimpleConfig
);
279 QCOMPARE(viewSettings
.groupList(), {"Dolphin"});
280 QCOMPARE(viewSettings
.group("Dolphin").readEntry("SortRole"), "someNewSortRole");
282 QVERIFY(QFile::exists(dotDirectoryFile
));
286 void ViewPropertiesTest::testUseAsDefaultViewSettings()
288 // Create a global viewproperties folder
289 QUrl globalPropertiesPath
=
290 QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation
).append("/view_properties/").append(QStringLiteral("global")));
291 QVERIFY(QDir().mkpath(globalPropertiesPath
.toLocalFile()));
292 auto cleanupGlobalDir
= qScopeGuard([globalPropertiesPath
] {
293 QDir().rmdir(globalPropertiesPath
.toLocalFile());
295 ViewProperties
globalProps(globalPropertiesPath
);
297 // Check that theres no .directory file and metadata is supported
298 QString dotDirectoryFile
= m_testDir
->url().toLocalFile() + "/.directory";
299 QVERIFY(!QFile::exists(dotDirectoryFile
));
300 KFileMetaData::UserMetaData
testDirMetadata(m_testDir
->url().toLocalFile());
301 KFileMetaData::UserMetaData
globalDirMetadata(globalPropertiesPath
.toLocalFile());
302 if (!testDirMetadata
.isSupported()) {
303 QSKIP("need extended attribute/filesystem metadata to be usefull");
306 const auto newDefaultViewMode
= DolphinView::Mode::DetailsView
;
308 // Equivalent of useAsDefault in ViewPropertiesDialog
309 // This sets the DetailsView as default viewMode
310 GeneralSettings::setGlobalViewProps(true);
311 globalProps
.setViewMode(newDefaultViewMode
);
312 globalProps
.setDirProperties(globalProps
);
314 GeneralSettings::setGlobalViewProps(false);
317 QScopedPointer
<ViewProperties
> globalDirProperties(new ViewProperties(globalPropertiesPath
));
318 auto defaultData
= globalDirProperties
.data();
321 QScopedPointer
<ViewProperties
> testDirProperties(new ViewProperties(m_testDir
->url()));
322 auto testData
= testDirProperties
.data();
324 // Make sure globalDirProperties are not empty, so they will be used
325 auto globalDirPropString
= globalDirMetadata
.attribute(QStringLiteral("kde.fm.viewproperties#1"));
326 QVERIFY(!globalDirPropString
.isEmpty());
328 // Make sure testDirProperties is empty, so default values are used for it
329 auto testDirPropString
= testDirMetadata
.attribute(QStringLiteral("kde.fm.viewproperties#1"));
330 QVERIFY(testDirPropString
.isEmpty());
332 // Compare that default and new folder viewMode is the new default
333 QCOMPARE(defaultData
->viewMode(), newDefaultViewMode
);
334 QCOMPARE(testData
->viewMode(), defaultData
->viewMode());
337 void ViewPropertiesTest::testUseAsCustomDefaultViewSettings()
339 // Create a global viewproperties folder
340 QUrl globalPropertiesPath
=
341 QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation
).append("/view_properties/").append(QStringLiteral("global")));
342 QVERIFY(QDir().mkpath(globalPropertiesPath
.toLocalFile()));
343 auto cleanupGlobalDir
= qScopeGuard([globalPropertiesPath
] {
344 QDir().rmdir(globalPropertiesPath
.toLocalFile());
346 ViewProperties
globalProps(globalPropertiesPath
);
348 // Check that theres no .directory file and metadata is supported
349 QString dotDirectoryFile
= m_testDir
->url().toLocalFile() + "/.directory";
350 QVERIFY(!QFile::exists(dotDirectoryFile
));
351 KFileMetaData::UserMetaData
testDirMetadata(m_testDir
->url().toLocalFile());
352 KFileMetaData::UserMetaData
globalDirMetadata(globalPropertiesPath
.toLocalFile());
353 if (!testDirMetadata
.isSupported()) {
354 QSKIP("need extended attribute/filesystem metadata to be usefull");
357 // Equivalent of useAsDefault in ViewPropertiesDialog
358 // This sets the DetailsView as default viewMode
359 GeneralSettings::setGlobalViewProps(true);
360 globalProps
.setViewMode(DolphinView::Mode::DetailsView
);
361 globalProps
.setDirProperties(globalProps
);
363 GeneralSettings::setGlobalViewProps(false);
365 // Make sure globalDirProperties are not empty, so they will be used
366 auto globalDirPropString
= globalDirMetadata
.attribute(QStringLiteral("kde.fm.viewproperties#1"));
367 QVERIFY(!globalDirPropString
.isEmpty());
370 QScopedPointer
<ViewProperties
> globalDirProperties(new ViewProperties(globalPropertiesPath
));
371 auto defaultData
= globalDirProperties
.data();
372 QCOMPARE(defaultData
->viewMode(), DolphinView::Mode::DetailsView
);
374 // Load testdir data, set to icon, i.e default hardcoded, not current user default
375 QScopedPointer
<ViewProperties
> testDirProperties(new ViewProperties(m_testDir
->url()));
376 testDirProperties
->setViewMode(DolphinView::Mode::IconsView
);
377 testDirProperties
->save();
379 // testDirProperties is not default
380 auto testDirPropString
= testDirMetadata
.attribute(QStringLiteral("kde.fm.viewproperties#1"));
381 QVERIFY(!testDirPropString
.isEmpty());
382 QCOMPARE(testDirProperties
.data()->viewMode(), DolphinView::Mode::IconsView
);
384 // testDirProperties is now default
385 testDirProperties
->setViewMode(DolphinView::Mode::DetailsView
);
386 testDirProperties
->save();
388 // no more metedata => the defaults settings are in effect for the folder
389 testDirPropString
= testDirMetadata
.attribute(QStringLiteral("kde.fm.viewproperties#1"));
390 QVERIFY(testDirPropString
.isEmpty());
391 QCOMPARE(testDirProperties
.data()->viewMode(), DolphinView::Mode::DetailsView
);
394 QTEST_GUILESS_MAIN(ViewPropertiesTest
)
396 #include "viewpropertiestest.moc"