File indexing completed on 2024-04-28 03:53:13
0001 /* This file is part of the KDE libraries 0002 SPDX-FileCopyrightText: 1997 Matthias Kalle Dalheimer <kalle@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "kconfigtest.h" 0008 #include "helper.h" 0009 0010 #include "config-kconfig.h" 0011 0012 #include <QSignalSpy> 0013 #include <QStandardPaths> 0014 #include <QTemporaryFile> 0015 #include <QTest> 0016 #include <kdesktopfile.h> 0017 #include <qtemporarydir.h> 0018 0019 #include <kauthorized.h> 0020 #include <kconfiggroup.h> 0021 #include <kconfigwatcher.h> 0022 #include <ksharedconfig.h> 0023 0024 #ifdef Q_OS_UNIX 0025 #include <utime.h> 0026 #endif 0027 #ifndef Q_OS_WIN 0028 #include <unistd.h> // getuid 0029 #endif 0030 0031 KCONFIGGROUP_DECLARE_ENUM_QOBJECT(KConfigTest, Testing) 0032 KCONFIGGROUP_DECLARE_FLAGS_QOBJECT(KConfigTest, Flags) 0033 0034 QTEST_MAIN(KConfigTest) 0035 0036 Q_DECLARE_METATYPE(KConfigGroup) 0037 0038 static QString homePath() 0039 { 0040 #ifdef Q_OS_WIN 0041 return QDir::homePath(); 0042 #else 0043 // Don't use QDir::homePath() on Unix, it removes any trailing slash, while KConfig uses $HOME. 0044 return QString::fromLocal8Bit(qgetenv("HOME")); 0045 #endif 0046 } 0047 0048 // clazy:excludeall=non-pod-global-static 0049 0050 static const bool s_bool_entry1 = true; 0051 static const bool s_bool_entry2 = false; 0052 0053 static const QString s_string_entry1(QStringLiteral("hello")); 0054 static const QString s_string_entry2(QStringLiteral(" hello")); 0055 static const QString s_string_entry3(QStringLiteral("hello ")); 0056 static const QString s_string_entry4(QStringLiteral(" hello ")); 0057 static const QString s_string_entry5(QStringLiteral(" ")); 0058 static const QString s_string_entry6{}; 0059 0060 static const char s_utf8bit_entry[] = "Hello äöü"; 0061 static const QString s_translated_string_entry1{QStringLiteral("bonjour")}; 0062 static const QByteArray s_bytearray_entry{"\x00\xff\x7f\x3c abc\x00\x00", 10}; 0063 static const char s_escapekey[] = " []\0017[]==]"; 0064 static const char s_escape_entry[] = "[]\170[]]=3=]\\] "; 0065 static const double s_double_entry{123456.78912345}; 0066 static const float s_float_entry{123.567f}; 0067 static const QPoint s_point_entry{4351, 1235}; 0068 static const QSize s_size_entry{10, 20}; 0069 static const QRect s_rect_entry{10, 23, 5321, 13}; 0070 static const QDateTime s_date_time_entry{QDate{2002, 06, 23}, QTime{12, 55, 40}}; 0071 static const QDateTime s_date_time_with_ms_entry{QDate{2002, 06, 23}, QTime{12, 55, 40, 532}}; 0072 static const QStringList s_stringlist_entry{QStringLiteral("Hello,"), QStringLiteral("World")}; 0073 static const QStringList s_stringlist_empty_entry{}; 0074 static const QStringList s_stringlist_just_empty_element{QString{}}; 0075 static const QStringList s_stringlist_empty_trailing_element{QStringLiteral("Hi"), QString{}}; 0076 static const QStringList s_stringlist_escape_odd_entry{QStringLiteral("Hello\\\\\\"), QStringLiteral("World")}; 0077 static const QStringList s_stringlist_escape_even_entry{QStringLiteral("Hello\\\\\\\\"), QStringLiteral("World")}; 0078 static const QStringList s_stringlist_escape_comma_entry{QStringLiteral("Hel\\\\\\,\\\\,\\,\\\\\\\\,lo"), QStringLiteral("World")}; 0079 static const QList<int> s_int_listentry1{1, 2, 3, 4}; 0080 static const QList<QByteArray> s_bytearray_list_entry1{"", "1,2", "end"}; 0081 static const QVariantList s_variantlist_entry{true, false, QStringLiteral("joe"), 10023}; 0082 static const QVariantList s_variantlist_entry2{s_point_entry, s_size_entry}; 0083 0084 static const QString s_homepath{homePath() + QLatin1String{"/foo"}}; 0085 static const QString s_homepath_escape{homePath() + QLatin1String("/foo/$HOME")}; 0086 static const QString s_canonical_homepath{QFileInfo(homePath()).canonicalFilePath() + QLatin1String("/foo")}; 0087 static const QString s_dollargroup{QStringLiteral("$i")}; 0088 static const QString s_test_subdir{QStringLiteral("kconfigtest_subdir/")}; 0089 static const QString s_kconfig_test_subdir(s_test_subdir + QLatin1String("kconfigtest")); 0090 static const QString s_kconfig_test_illegal_object_path(s_test_subdir + QLatin1String("kconfig-test")); 0091 0092 #ifndef Q_OS_WIN 0093 void initLocale() 0094 { 0095 setenv("LC_ALL", "en_US.utf-8", 1); 0096 setenv("TZ", "UTC", 1); 0097 } 0098 0099 Q_CONSTRUCTOR_FUNCTION(initLocale) 0100 #endif 0101 0102 void KConfigTest::initTestCase() 0103 { 0104 // ensure we don't use files in the real config directory 0105 QStandardPaths::setTestModeEnabled(true); 0106 0107 qRegisterMetaType<KConfigGroup>(); 0108 0109 // These two need to be assigned here, after setTestModeEnabled(true), and before cleanupTestCase() 0110 m_testConfigDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + s_test_subdir; 0111 m_kdeGlobalsPath = QDir::cleanPath(m_testConfigDir + QLatin1String("..")) + QLatin1String("/kdeglobals"); 0112 0113 // to make sure all files from a previous failed run are deleted 0114 cleanupTestCase(); 0115 0116 KSharedConfigPtr mainConfig = KSharedConfig::openConfig(); 0117 mainConfig->group(QStringLiteral("Main")).writeEntry("Key", "Value"); 0118 mainConfig->sync(); 0119 0120 KConfig sc(s_kconfig_test_subdir); 0121 0122 KConfigGroup cg(&sc, QStringLiteral("AAA")); // deleted later by testDelete 0123 cg.writeEntry("stringEntry1", s_string_entry1, KConfig::Persistent | KConfig::Global); 0124 0125 cg = KConfigGroup(&sc, QStringLiteral("GlobalGroup")); 0126 cg.writeEntry("globalEntry", s_string_entry1, KConfig::Persistent | KConfig::Global); 0127 cg.deleteEntry("globalEntry2", KConfig::Global); 0128 0129 cg = KConfigGroup(&sc, QStringLiteral("LocalGroupToBeDeleted")); // deleted later by testDelete 0130 cg.writeEntry("stringEntry1", s_string_entry1); 0131 0132 cg = KConfigGroup(&sc, QStringLiteral("Hello")); 0133 cg.writeEntry("boolEntry1", s_bool_entry1); 0134 cg.writeEntry("boolEntry2", s_bool_entry2); 0135 0136 QByteArray data(s_utf8bit_entry); 0137 QCOMPARE(data.size(), 12); // the source file is in utf8 0138 QCOMPARE(QString::fromUtf8(data).length(), 9); 0139 cg.writeEntry("Test", data); 0140 cg.writeEntry("bytearrayEntry", s_bytearray_entry); 0141 cg.writeEntry(s_escapekey, QString::fromLatin1(s_escape_entry)); 0142 cg.writeEntry("emptyEntry", ""); 0143 cg.writeEntry("stringEntry1", s_string_entry1); 0144 cg.writeEntry("stringEntry2", s_string_entry2); 0145 cg.writeEntry("stringEntry3", s_string_entry3); 0146 cg.writeEntry("stringEntry4", s_string_entry4); 0147 cg.writeEntry("stringEntry5", s_string_entry5); 0148 cg.writeEntry("urlEntry1", QUrl(QStringLiteral("http://qt-project.org"))); 0149 cg.writeEntry("keywith=equalsign", s_string_entry1); 0150 cg.deleteEntry("stringEntry5"); 0151 cg.deleteEntry("stringEntry6"); // deleting a nonexistent entry 0152 cg.writeEntry("byteArrayEntry1", s_string_entry1.toLatin1(), KConfig::Global | KConfig::Persistent); 0153 cg.writeEntry("doubleEntry1", s_double_entry); 0154 cg.writeEntry("floatEntry1", s_float_entry); 0155 0156 sc.deleteGroup(QStringLiteral("deleteMe")); // deleting a nonexistent group 0157 0158 cg = KConfigGroup(&sc, QStringLiteral("Complex Types")); 0159 cg.writeEntry("rectEntry", s_rect_entry); 0160 cg.writeEntry("pointEntry", s_point_entry); 0161 cg.writeEntry("sizeEntry", s_size_entry); 0162 cg.writeEntry("dateTimeEntry", s_date_time_entry); 0163 cg.writeEntry("dateEntry", s_date_time_entry.date()); 0164 cg.writeEntry("dateTimeWithMSEntry", s_date_time_with_ms_entry); 0165 0166 KConfigGroup ct = cg; 0167 cg = KConfigGroup(&ct, QStringLiteral("Nested Group 1")); 0168 cg.writeEntry("stringentry1", s_string_entry1); 0169 0170 cg = KConfigGroup(&ct, QStringLiteral("Nested Group 2")); 0171 cg.writeEntry("stringEntry2", s_string_entry2); 0172 0173 cg = KConfigGroup(&cg, QStringLiteral("Nested Group 2.1")); 0174 cg.writeEntry("stringEntry3", s_string_entry3); 0175 0176 cg = KConfigGroup(&ct, QStringLiteral("Nested Group 3")); 0177 cg.writeEntry("stringEntry3", s_string_entry3); 0178 0179 cg = KConfigGroup(&sc, QStringLiteral("List Types")); 0180 cg.writeEntry("listOfIntsEntry1", s_int_listentry1); 0181 cg.writeEntry("listOfByteArraysEntry1", s_bytearray_list_entry1); 0182 cg.writeEntry("stringListEntry", s_stringlist_entry); 0183 cg.writeEntry("stringListEmptyEntry", s_stringlist_empty_entry); 0184 cg.writeEntry("stringListJustEmptyElement", s_stringlist_just_empty_element); 0185 cg.writeEntry("stringListEmptyTrailingElement", s_stringlist_empty_trailing_element); 0186 cg.writeEntry("stringListEscapeOddEntry", s_stringlist_escape_odd_entry); 0187 cg.writeEntry("stringListEscapeEvenEntry", s_stringlist_escape_even_entry); 0188 cg.writeEntry("stringListEscapeCommaEntry", s_stringlist_escape_comma_entry); 0189 cg.writeEntry("variantListEntry", s_variantlist_entry); 0190 0191 cg = KConfigGroup(&sc, QStringLiteral("Path Type")); 0192 cg.writePathEntry("homepath", s_homepath); 0193 cg.writePathEntry("homepathescape", s_homepath_escape); 0194 cg.writePathEntry("canonicalHomePath", s_canonical_homepath); 0195 0196 cg = KConfigGroup(&sc, QStringLiteral("Enum Types")); 0197 #if defined(_MSC_VER) && _MSC_VER == 1600 0198 cg.writeEntry("dummy", 42); 0199 #else 0200 // Visual C++ 2010 throws an Internal Compiler Error here 0201 cg.writeEntry("enum-10", Tens); 0202 cg.writeEntry("enum-100", Hundreds); 0203 cg.writeEntry("flags-bit0", Flags(bit0)); 0204 cg.writeEntry("flags-bit0-bit1", Flags(bit0 | bit1)); 0205 #endif 0206 0207 cg = KConfigGroup(&sc, QStringLiteral("ParentGroup")); 0208 KConfigGroup cg1(&cg, QStringLiteral("SubGroup1")); 0209 cg1.writeEntry("somestring", "somevalue"); 0210 cg.writeEntry("parentgrpstring", "somevalue"); 0211 KConfigGroup cg2(&cg, QStringLiteral("SubGroup2")); 0212 cg2.writeEntry("substring", "somevalue"); 0213 KConfigGroup cg3(&cg, QStringLiteral("SubGroup/3")); 0214 cg3.writeEntry("sub3string", "somevalue"); 0215 0216 QVERIFY(sc.isDirty()); 0217 QVERIFY(sc.sync()); 0218 QVERIFY(!sc.isDirty()); 0219 0220 QVERIFY2(QFile::exists(m_testConfigDir + QLatin1String("/kconfigtest")), qPrintable(m_testConfigDir + QLatin1String("/kconfigtest must exist"))); 0221 QVERIFY2(QFile::exists(m_kdeGlobalsPath), qPrintable(m_kdeGlobalsPath + QStringLiteral(" must exist"))); 0222 0223 KConfig sc1(s_test_subdir + QLatin1String("kdebugrc"), KConfig::SimpleConfig); 0224 KConfigGroup sg0(&sc1, QStringLiteral("0")); 0225 sg0.writeEntry("AbortFatal", false); 0226 sg0.writeEntry("WarnOutput", 0); 0227 sg0.writeEntry("FatalOutput", 0); 0228 QVERIFY(sc1.sync()); 0229 0230 // Setup stuff to test KConfig::addConfigSources() 0231 KConfig devcfg(s_test_subdir + QLatin1String("specificrc")); 0232 KConfigGroup devonlygrp(&devcfg, QStringLiteral("Specific Only Group")); 0233 devonlygrp.writeEntry("ExistingEntry", "DevValue"); 0234 KConfigGroup devandbasegrp(&devcfg, QStringLiteral("Shared Group")); 0235 devandbasegrp.writeEntry("SomeSharedEntry", "DevValue"); 0236 devandbasegrp.writeEntry("SomeSpecificOnlyEntry", "DevValue"); 0237 QVERIFY(devcfg.sync()); 0238 KConfig basecfg(s_test_subdir + QLatin1String("baserc")); 0239 KConfigGroup basegrp(&basecfg, QStringLiteral("Base Only Group")); 0240 basegrp.writeEntry("ExistingEntry", "BaseValue"); 0241 KConfigGroup baseanddevgrp(&basecfg, QStringLiteral("Shared Group")); 0242 baseanddevgrp.writeEntry("SomeSharedEntry", "BaseValue"); 0243 baseanddevgrp.writeEntry("SomeBaseOnlyEntry", "BaseValue"); 0244 QVERIFY(basecfg.sync()); 0245 0246 KConfig gecfg(s_test_subdir + QLatin1String("groupescapetest"), KConfig::SimpleConfig); 0247 cg = KConfigGroup(&gecfg, s_dollargroup); 0248 cg.writeEntry("entry", "doesntmatter"); 0249 } 0250 0251 void KConfigTest::cleanupTestCase() 0252 { 0253 // ensure we don't delete the real directory 0254 QDir localConfig(m_testConfigDir); 0255 // qDebug() << "Erasing" << localConfig; 0256 if (localConfig.exists()) { 0257 QVERIFY(localConfig.removeRecursively()); 0258 } 0259 QVERIFY(!localConfig.exists()); 0260 if (QFile::exists(m_kdeGlobalsPath)) { 0261 QVERIFY(QFile::remove(m_kdeGlobalsPath)); 0262 } 0263 } 0264 0265 static QList<QByteArray> readLinesFrom(const QString &path) 0266 { 0267 QFile file(path); 0268 const bool opened = file.open(QIODevice::ReadOnly | QIODevice::Text); 0269 QList<QByteArray> lines; 0270 if (!opened) { 0271 qWarning() << "Failed to open" << path; 0272 return lines; 0273 } 0274 QByteArray line; 0275 do { 0276 line = file.readLine(); 0277 if (!line.isEmpty()) { 0278 lines.append(line); 0279 } 0280 } while (!line.isEmpty()); 0281 return lines; 0282 } 0283 0284 static const QString s_defaultArg = s_test_subdir + QLatin1String("kconfigtest"); 0285 static QList<QByteArray> readLines(const QString &fileName = s_defaultArg) 0286 { 0287 const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); 0288 Q_ASSERT(!path.isEmpty()); 0289 return readLinesFrom(path + QLatin1Char('/') + fileName); 0290 } 0291 0292 // see also testDefaults, which tests reverting with a defaults (global) file available 0293 void KConfigTest::testDirtyAfterRevert() 0294 { 0295 KConfig sc(s_test_subdir + QLatin1String("kconfigtest_revert")); 0296 0297 KConfigGroup cg(&sc, QStringLiteral("Hello")); 0298 cg.revertToDefault("does_not_exist"); 0299 QVERIFY(!sc.isDirty()); 0300 cg.writeEntry("Test", "Correct"); 0301 QVERIFY(sc.isDirty()); 0302 sc.sync(); 0303 QVERIFY(!sc.isDirty()); 0304 0305 cg.revertToDefault("Test"); 0306 QVERIFY(sc.isDirty()); 0307 QVERIFY(sc.sync()); 0308 QVERIFY(!sc.isDirty()); 0309 0310 cg.revertToDefault("Test"); 0311 QVERIFY(!sc.isDirty()); 0312 } 0313 0314 void KConfigTest::testRevertAllEntries() 0315 { 0316 // this tests the case were we revert (delete) all entries in a file, 0317 // leaving a blank file 0318 { 0319 KConfig sc(s_test_subdir + QLatin1String("konfigtest2"), KConfig::SimpleConfig); 0320 KConfigGroup cg(&sc, QStringLiteral("Hello")); 0321 cg.writeEntry("Test", "Correct"); 0322 } 0323 0324 { 0325 KConfig sc(s_test_subdir + QLatin1String("konfigtest2"), KConfig::SimpleConfig); 0326 KConfigGroup cg(&sc, QStringLiteral("Hello")); 0327 QCOMPARE(cg.readEntry("Test", "Default"), QStringLiteral("Correct")); 0328 cg.revertToDefault("Test"); 0329 } 0330 0331 KConfig sc(s_test_subdir + QLatin1String("konfigtest2"), KConfig::SimpleConfig); 0332 KConfigGroup cg(&sc, QStringLiteral("Hello")); 0333 QCOMPARE(cg.readEntry("Test", "Default"), QStringLiteral("Default")); 0334 } 0335 0336 void KConfigTest::testSimple() 0337 { 0338 // kdeglobals (which was created in initTestCase) must be found this way: 0339 const QStringList kdeglobals = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QStringLiteral("kdeglobals")); 0340 QVERIFY(!kdeglobals.isEmpty()); 0341 0342 KConfig sc2(s_kconfig_test_subdir); 0343 QCOMPARE(sc2.name(), s_test_subdir + QLatin1String{"kconfigtest"}); 0344 0345 // make sure groupList() isn't returning something it shouldn't 0346 const QStringList lstGroup = sc2.groupList(); 0347 for (const QString &group : lstGroup) { 0348 QVERIFY(!group.isEmpty() && group != QLatin1String("<default>")); 0349 QVERIFY(!group.contains(QChar(0x1d))); 0350 } 0351 0352 KConfigGroup sc3(&sc2, QStringLiteral("GlobalGroup")); 0353 0354 QVERIFY(sc3.hasKey("globalEntry")); // from kdeglobals 0355 QVERIFY(!sc3.isEntryImmutable("globalEntry")); 0356 QCOMPARE(sc3.readEntry("globalEntry"), s_string_entry1); 0357 0358 QVERIFY(!sc3.hasKey("globalEntry2")); 0359 QCOMPARE(sc3.readEntry("globalEntry2", QStringLiteral("bla")), QStringLiteral("bla")); 0360 0361 QVERIFY(!sc3.hasDefault("globalEntry")); 0362 0363 sc3 = KConfigGroup(&sc2, QStringLiteral("Hello")); 0364 QCOMPARE(sc3.readEntry("Test", QByteArray()), QByteArray(s_utf8bit_entry)); 0365 QCOMPARE(sc3.readEntry("bytearrayEntry", QByteArray()), s_bytearray_entry); 0366 QCOMPARE(sc3.readEntry(s_escapekey), QString::fromLatin1(s_escape_entry)); 0367 QCOMPARE(sc3.readEntry("Test", QString{}), QString::fromUtf8(s_utf8bit_entry)); 0368 QCOMPARE(sc3.readEntry("emptyEntry" /*, QString("Fietsbel")*/), QLatin1String("")); 0369 QCOMPARE(sc3.readEntry("emptyEntry", QStringLiteral("Fietsbel")).isEmpty(), true); 0370 QCOMPARE(sc3.readEntry("stringEntry1"), s_string_entry1); 0371 QCOMPARE(sc3.readEntry("stringEntry2"), s_string_entry2); 0372 QCOMPARE(sc3.readEntry("stringEntry3"), s_string_entry3); 0373 QCOMPARE(sc3.readEntry("stringEntry4"), s_string_entry4); 0374 QVERIFY(!sc3.hasKey("stringEntry5")); 0375 QCOMPARE(sc3.readEntry("stringEntry5", QStringLiteral("test")), QStringLiteral("test")); 0376 QVERIFY(!sc3.hasKey("stringEntry6")); 0377 QCOMPARE(sc3.readEntry("stringEntry6", QStringLiteral("foo")), QStringLiteral("foo")); 0378 QCOMPARE(sc3.readEntry("urlEntry1", QUrl{}), QUrl(QStringLiteral("http://qt-project.org"))); 0379 QCOMPARE(sc3.readEntry("boolEntry1", s_bool_entry1), s_bool_entry1); 0380 QCOMPARE(sc3.readEntry("boolEntry2", false), s_bool_entry2); 0381 QCOMPARE(sc3.readEntry("keywith=equalsign", QStringLiteral("wrong")), s_string_entry1); 0382 QCOMPARE(sc3.readEntry("byteArrayEntry1", QByteArray{}), s_string_entry1.toLatin1()); 0383 QCOMPARE(sc3.readEntry("doubleEntry1", 0.0), s_double_entry); 0384 QCOMPARE(sc3.readEntry("floatEntry1", 0.0f), s_float_entry); 0385 } 0386 0387 void KConfigTest::testDefaults() 0388 { 0389 KConfig config(s_test_subdir + QLatin1String("defaulttest"), KConfig::NoGlobals); 0390 const QString defaultsFile = s_test_subdir + QLatin1String("defaulttest.defaults"); 0391 KConfig defaults(defaultsFile, KConfig::SimpleConfig); 0392 const QString defaultsFilePath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + defaultsFile; 0393 0394 const QString Default(QStringLiteral("Default")); 0395 const QString NotDefault(QStringLiteral("Not Default")); 0396 const QString Value1(s_string_entry1); 0397 const QString Value2(s_string_entry2); 0398 0399 KConfigGroup group = defaults.group(QStringLiteral("any group")); 0400 group.writeEntry("entry1", Default); 0401 QVERIFY(group.sync()); 0402 0403 group = config.group(QStringLiteral("any group")); 0404 group.writeEntry("entry1", Value1); 0405 group.writeEntry("entry2", Value2); 0406 QVERIFY(group.sync()); 0407 0408 config.addConfigSources(QStringList{defaultsFilePath}); 0409 0410 config.setReadDefaults(true); 0411 QCOMPARE(group.readEntry("entry1", QString()), Default); 0412 QCOMPARE(group.readEntry("entry2", NotDefault), NotDefault); // no default for entry2 0413 0414 config.setReadDefaults(false); 0415 QCOMPARE(group.readEntry("entry1", Default), Value1); 0416 QCOMPARE(group.readEntry("entry2", NotDefault), Value2); 0417 0418 group.revertToDefault("entry1"); 0419 QCOMPARE(group.readEntry("entry1", QString()), Default); 0420 group.revertToDefault("entry2"); 0421 QCOMPARE(group.readEntry("entry2", QString()), QString()); 0422 0423 // TODO test reverting localized entries 0424 0425 Q_ASSERT(config.isDirty()); 0426 group.sync(); 0427 0428 // Check that everything is OK on disk, too 0429 KConfig reader(s_test_subdir + QLatin1String("defaulttest"), KConfig::NoGlobals); 0430 reader.addConfigSources(QStringList{defaultsFilePath}); 0431 KConfigGroup readerGroup = reader.group(QStringLiteral("any group")); 0432 QCOMPARE(readerGroup.readEntry("entry1", QString()), Default); 0433 QCOMPARE(readerGroup.readEntry("entry2", QString()), QString()); 0434 } 0435 0436 void KConfigTest::testLocale() 0437 { 0438 KConfig config(s_test_subdir + QLatin1String("kconfigtest.locales"), KConfig::SimpleConfig); 0439 const QString Translated(s_translated_string_entry1); 0440 const QString Untranslated(s_string_entry1); 0441 0442 KConfigGroup group = config.group(QStringLiteral("Hello")); 0443 group.writeEntry("stringEntry1", Untranslated); 0444 config.setLocale(QStringLiteral("fr")); 0445 group.writeEntry("stringEntry1", Translated, KConfig::Localized | KConfig::Persistent); 0446 QVERIFY(config.sync()); 0447 0448 QCOMPARE(group.readEntry("stringEntry1", QString()), Translated); 0449 QCOMPARE(group.readEntryUntranslated("stringEntry1"), Untranslated); 0450 0451 config.setLocale(QStringLiteral("C")); // strings written in the "C" locale are written as nonlocalized 0452 group.writeEntry("stringEntry1", Untranslated, KConfig::Localized | KConfig::Persistent); 0453 QVERIFY(config.sync()); 0454 0455 QCOMPARE(group.readEntry("stringEntry1", QString()), Untranslated); 0456 } 0457 0458 void KConfigTest::testEncoding() 0459 { 0460 const QString groupstr = QString::fromUtf8("UTF-8:\xc3\xb6l"); 0461 0462 const QString path = s_test_subdir + QLatin1String("kconfigtestencodings"); 0463 KConfig c(path); 0464 KConfigGroup cg(&c, groupstr); 0465 cg.writeEntry("key", "value"); 0466 QVERIFY(c.sync()); 0467 0468 const QList<QByteArray> lines = readLines(path); 0469 QCOMPARE(lines.count(), 2); 0470 QCOMPARE(lines.first(), QByteArray("[UTF-8:\xc3\xb6l]\n")); 0471 0472 KConfig c2(path); 0473 KConfigGroup cg2(&c2, groupstr); 0474 QCOMPARE(cg2.readEntry("key"), QStringLiteral("value")); 0475 0476 QVERIFY(c2.groupList().contains(groupstr)); 0477 } 0478 0479 void KConfigTest::testLists() 0480 { 0481 KConfig sc2(s_kconfig_test_subdir); 0482 KConfigGroup sc3(&sc2, QStringLiteral("List Types")); 0483 0484 QCOMPARE(sc3.readEntry("stringListEntry", QStringList{}), s_stringlist_entry); 0485 0486 QCOMPARE(sc3.readEntry(QStringLiteral("stringListEmptyEntry"), QStringList(QStringLiteral("wrong"))), s_stringlist_empty_entry); 0487 0488 QCOMPARE(sc3.readEntry(QStringLiteral("stringListJustEmptyElement"), QStringList()), s_stringlist_just_empty_element); 0489 0490 QCOMPARE(sc3.readEntry(QStringLiteral("stringListEmptyTrailingElement"), QStringList()), s_stringlist_empty_trailing_element); 0491 0492 QCOMPARE(sc3.readEntry(QStringLiteral("stringListEscapeOddEntry"), QStringList()), s_stringlist_escape_odd_entry); 0493 0494 QCOMPARE(sc3.readEntry(QStringLiteral("stringListEscapeEvenEntry"), QStringList()), s_stringlist_escape_even_entry); 0495 0496 QCOMPARE(sc3.readEntry(QStringLiteral("stringListEscapeCommaEntry"), QStringList()), s_stringlist_escape_comma_entry); 0497 0498 QCOMPARE(sc3.readEntry("listOfIntsEntry1"), QString::fromLatin1("1,2,3,4")); 0499 QList<int> expectedIntList = s_int_listentry1; 0500 QVERIFY(sc3.readEntry("listOfIntsEntry1", QList<int>()) == expectedIntList); 0501 0502 QCOMPARE(QVariant(sc3.readEntry("variantListEntry", s_variantlist_entry)).toStringList(), QVariant(s_variantlist_entry).toStringList()); 0503 0504 QCOMPARE(sc3.readEntry("listOfByteArraysEntry1", QList<QByteArray>()), s_bytearray_list_entry1); 0505 } 0506 0507 void KConfigTest::testPath() 0508 { 0509 KConfig sc2(s_kconfig_test_subdir); 0510 KConfigGroup sc3(&sc2, QStringLiteral("Path Type")); 0511 QCOMPARE(sc3.readPathEntry(QStringLiteral("homepath"), QString{}), s_homepath); 0512 QCOMPARE(sc3.readPathEntry(QStringLiteral("homepathescape"), QString{}), s_homepath_escape); 0513 QCOMPARE(sc3.readPathEntry(QStringLiteral("canonicalHomePath"), QString{}), s_canonical_homepath); 0514 QCOMPARE(sc3.entryMap().value(QStringLiteral("homepath")), s_homepath); 0515 0516 qputenv("WITHSLASH", "/a/"); 0517 { 0518 QFile file(m_testConfigDir + QLatin1String("/pathtest")); 0519 file.open(QIODevice::WriteOnly | QIODevice::Text); 0520 QTextStream out(&file); 0521 out << "[Test Group]\n" 0522 << "homePath=$HOME/foo\n" 0523 << "homePath2=file://$HOME/foo\n" 0524 << "withSlash=$WITHSLASH/foo\n" 0525 << "withSlash2=$WITHSLASH\n" 0526 << "withBraces[$e]=file://${HOME}/foo\n" 0527 << "URL[$e]=file://${HOME}/foo\n" 0528 << "hostname[$e]=$(hostname)\n" 0529 << "escapes=aaa,bb/b,ccc\\,ccc\n" 0530 << "noeol=foo" // no EOL 0531 ; 0532 } 0533 KConfig cf2(s_test_subdir + QLatin1String("pathtest")); 0534 KConfigGroup group = cf2.group(QStringLiteral("Test Group")); 0535 QVERIFY(group.hasKey("homePath")); 0536 QCOMPARE(group.readPathEntry("homePath", QString{}), s_homepath); 0537 QVERIFY(group.hasKey("homePath2")); 0538 QCOMPARE(group.readPathEntry("homePath2", QString{}), QLatin1String("file://") + s_homepath); 0539 QVERIFY(group.hasKey("withSlash")); 0540 QCOMPARE(group.readPathEntry("withSlash", QString{}), QStringLiteral("/a//foo")); 0541 QVERIFY(group.hasKey("withSlash2")); 0542 QCOMPARE(group.readPathEntry("withSlash2", QString{}), QStringLiteral("/a/")); 0543 QVERIFY(group.hasKey("withBraces")); 0544 QCOMPARE(group.readPathEntry("withBraces", QString{}), QLatin1String("file://") + s_homepath); 0545 QVERIFY(group.hasKey("URL")); 0546 QCOMPARE(group.readEntry("URL", QString{}), QLatin1String("file://") + s_homepath); 0547 QVERIFY(group.hasKey("hostname")); 0548 QCOMPARE(group.readEntry("hostname", QString{}), QStringLiteral("(hostname)")); // the $ got removed because empty var name 0549 QVERIFY(group.hasKey("noeol")); 0550 QCOMPARE(group.readEntry("noeol", QString{}), QStringLiteral("foo")); 0551 0552 const auto val = QStringList{QStringLiteral("aaa"), QStringLiteral("bb/b"), QStringLiteral("ccc,ccc")}; 0553 QCOMPARE(group.readPathEntry(QStringLiteral("escapes"), QStringList()), val); 0554 } 0555 0556 void KConfigTest::testPersistenceOfExpandFlagForPath() 0557 { 0558 // This test checks that a path entry starting with $HOME is still flagged 0559 // with the expand flag after the config was altered without rewriting the 0560 // path entry. 0561 0562 // 1st step: Open the config, add a new dummy entry and then sync the config 0563 // back to the storage. 0564 { 0565 KConfig sc2(s_kconfig_test_subdir); 0566 KConfigGroup sc3(&sc2, QStringLiteral("Path Type")); 0567 sc3.writeEntry("dummy", "dummy"); 0568 QVERIFY(sc2.sync()); 0569 } 0570 0571 // 2nd step: Call testPath() again. Rewriting the config must not break 0572 // the testPath() test. 0573 testPath(); 0574 } 0575 0576 void KConfigTest::testPathQtHome() 0577 { 0578 { 0579 QFile file(m_testConfigDir + QLatin1String("/pathtest")); 0580 file.open(QIODevice::WriteOnly | QIODevice::Text); 0581 QTextStream out(&file); 0582 out << "[Test Group]\n" 0583 << "dataDir[$e]=$QT_DATA_HOME/kconfigtest\n" 0584 << "cacheDir[$e]=$QT_CACHE_HOME/kconfigtest\n" 0585 << "configDir[$e]=$QT_CONFIG_HOME/kconfigtest\n"; 0586 } 0587 KConfig cf2(s_test_subdir + QLatin1String("pathtest")); 0588 KConfigGroup group = cf2.group(QStringLiteral("Test Group")); 0589 qunsetenv("QT_DATA_HOME"); 0590 qunsetenv("QT_CACHE_HOME"); 0591 qunsetenv("QT_CONFIG_HOME"); 0592 QVERIFY(group.hasKey("dataDir")); 0593 QCOMPARE(group.readEntry("dataDir", QString{}), 0594 QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation).append(QStringLiteral("/kconfigtest"))); 0595 QVERIFY(group.hasKey("cacheDir")); 0596 QCOMPARE(group.readEntry("cacheDir", QString{}), 0597 QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation).append(QStringLiteral("/kconfigtest"))); 0598 QVERIFY(group.hasKey("configDir")); 0599 QCOMPARE(group.readEntry("configDir", QString{}), 0600 QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation).append(QStringLiteral("/kconfigtest"))); 0601 qputenv("QT_DATA_HOME", "/1"); 0602 qputenv("QT_CACHE_HOME", "/2"); 0603 qputenv("QT_CONFIG_HOME", "/3"); 0604 QVERIFY(group.hasKey("dataDir")); 0605 QCOMPARE(group.readEntry("dataDir", QString{}), QStringLiteral("/1/kconfigtest")); 0606 QVERIFY(group.hasKey("cacheDir")); 0607 QCOMPARE(group.readEntry("cacheDir", QString{}), QStringLiteral("/2/kconfigtest")); 0608 QVERIFY(group.hasKey("configDir")); 0609 QCOMPARE(group.readEntry("configDir", QString{}), QStringLiteral("/3/kconfigtest")); 0610 } 0611 0612 void KConfigTest::testComplex() 0613 { 0614 KConfig sc2(s_kconfig_test_subdir); 0615 KConfigGroup sc3(&sc2, QStringLiteral("Complex Types")); 0616 0617 QCOMPARE(sc3.readEntry("pointEntry", QPoint()), s_point_entry); 0618 QCOMPARE(sc3.readEntry("sizeEntry", s_size_entry), s_size_entry); 0619 QCOMPARE(sc3.readEntry("rectEntry", QRect(1, 2, 3, 4)), s_rect_entry); 0620 QCOMPARE(sc3.readEntry("dateTimeEntry", QDateTime()).toString(Qt::ISODateWithMs), s_date_time_entry.toString(Qt::ISODateWithMs)); 0621 QCOMPARE(sc3.readEntry("dateEntry", QDate()).toString(Qt::ISODate), s_date_time_entry.date().toString(Qt::ISODate)); 0622 QCOMPARE(sc3.readEntry("dateTimeWithMSEntry", QDateTime()).toString(Qt::ISODateWithMs), s_date_time_with_ms_entry.toString(Qt::ISODateWithMs)); 0623 QCOMPARE(sc3.readEntry("dateTimeEntry", QDate()), s_date_time_entry.date()); 0624 } 0625 0626 void KConfigTest::testEnums() 0627 { 0628 // Visual C++ 2010 (compiler version 16.0) throws an Internal Compiler Error 0629 // when compiling the code in initTestCase that creates these KConfig entries, 0630 // so we can't run this test 0631 #if defined(_MSC_VER) && _MSC_VER == 1600 0632 QSKIP("Visual C++ 2010 can't compile this test"); 0633 #endif 0634 KConfig sc(s_kconfig_test_subdir); 0635 KConfigGroup sc3(&sc, QStringLiteral("Enum Types")); 0636 0637 QCOMPARE(sc3.readEntry("enum-10"), QStringLiteral("Tens")); 0638 QVERIFY(sc3.readEntry("enum-100", Ones) != Ones); 0639 QVERIFY(sc3.readEntry("enum-100", Ones) != Tens); 0640 0641 QCOMPARE(sc3.readEntry("flags-bit0"), QStringLiteral("bit0")); 0642 QVERIFY(sc3.readEntry("flags-bit0", Flags()) == bit0); 0643 0644 int eid = staticMetaObject.indexOfEnumerator("Flags"); 0645 QVERIFY(eid != -1); 0646 QMetaEnum me = staticMetaObject.enumerator(eid); 0647 Flags bitfield = bit0 | bit1; 0648 0649 QCOMPARE(sc3.readEntry("flags-bit0-bit1"), QString::fromLatin1(me.valueToKeys(bitfield))); 0650 QVERIFY(sc3.readEntry("flags-bit0-bit1", Flags()) == bitfield); 0651 } 0652 0653 void KConfigTest::testEntryMap() 0654 { 0655 KConfig sc(s_kconfig_test_subdir); 0656 KConfigGroup cg(&sc, QStringLiteral("Hello")); 0657 QMap<QString, QString> entryMap = cg.entryMap(); 0658 qDebug() << entryMap.keys(); 0659 QCOMPARE(entryMap.value(QStringLiteral("stringEntry1")), s_string_entry1); 0660 QCOMPARE(entryMap.value(QStringLiteral("stringEntry2")), s_string_entry2); 0661 QCOMPARE(entryMap.value(QStringLiteral("stringEntry3")), s_string_entry3); 0662 QCOMPARE(entryMap.value(QStringLiteral("stringEntry4")), s_string_entry4); 0663 QVERIFY(!entryMap.contains(QStringLiteral("stringEntry5"))); 0664 QVERIFY(!entryMap.contains(QStringLiteral("stringEntry6"))); 0665 QCOMPARE(entryMap.value(QStringLiteral("Test")), QString::fromUtf8(s_utf8bit_entry)); 0666 QCOMPARE(entryMap.value(QStringLiteral("bytearrayEntry")), QString::fromUtf8(s_bytearray_entry.constData())); 0667 QCOMPARE(entryMap.value(QStringLiteral("emptyEntry")), QString{}); 0668 QVERIFY(entryMap.contains(QStringLiteral("emptyEntry"))); 0669 QCOMPARE(entryMap.value(QStringLiteral("boolEntry1")), s_bool_entry1 ? QStringLiteral("true") : QStringLiteral("false")); 0670 QCOMPARE(entryMap.value(QStringLiteral("boolEntry2")), s_bool_entry2 ? QStringLiteral("true") : QStringLiteral("false")); 0671 QCOMPARE(entryMap.value(QStringLiteral("keywith=equalsign")), s_string_entry1); 0672 QCOMPARE(entryMap.value(QStringLiteral("byteArrayEntry1")), s_string_entry1); 0673 QCOMPARE(entryMap.value(QStringLiteral("doubleEntry1")).toDouble(), s_double_entry); 0674 QCOMPARE(entryMap.value(QStringLiteral("floatEntry1")).toFloat(), s_float_entry); 0675 } 0676 0677 void KConfigTest::testInvalid() 0678 { 0679 KConfig sc(s_kconfig_test_subdir); 0680 0681 // all of these should print a message to the kdebug.dbg file 0682 KConfigGroup sc3(&sc, QStringLiteral("Invalid Types")); 0683 sc3.writeEntry("badList", s_variantlist_entry2); 0684 0685 QList<int> list; 0686 0687 // 1 element list 0688 list << 1; 0689 sc3.writeEntry(QStringLiteral("badList"), list); 0690 0691 QVERIFY(sc3.readEntry("badList", QPoint()) == QPoint()); 0692 QVERIFY(sc3.readEntry("badList", QRect()) == QRect()); 0693 QVERIFY(sc3.readEntry("badList", QSize()) == QSize()); 0694 QVERIFY(sc3.readEntry("badList", QDate()) == QDate()); 0695 QVERIFY(sc3.readEntry("badList", QDateTime()) == QDateTime()); 0696 0697 // 2 element list 0698 list << 2; 0699 sc3.writeEntry("badList", list); 0700 0701 QVERIFY(sc3.readEntry("badList", QRect()) == QRect()); 0702 QVERIFY(sc3.readEntry("badList", QDate()) == QDate()); 0703 QVERIFY(sc3.readEntry("badList", QDateTime()) == QDateTime()); 0704 0705 // 3 element list 0706 list << 303; 0707 sc3.writeEntry("badList", list); 0708 0709 QVERIFY(sc3.readEntry("badList", QPoint()) == QPoint()); 0710 QVERIFY(sc3.readEntry("badList", QRect()) == QRect()); 0711 QVERIFY(sc3.readEntry("badList", QSize()) == QSize()); 0712 QVERIFY(sc3.readEntry("badList", QDate()) == QDate()); // out of bounds 0713 QVERIFY(sc3.readEntry("badList", QDateTime()) == QDateTime()); 0714 0715 // 4 element list 0716 list << 4; 0717 sc3.writeEntry("badList", list); 0718 0719 QVERIFY(sc3.readEntry("badList", QPoint()) == QPoint()); 0720 QVERIFY(sc3.readEntry("badList", QSize()) == QSize()); 0721 QVERIFY(sc3.readEntry("badList", QDate()) == QDate()); 0722 QVERIFY(sc3.readEntry("badList", QDateTime()) == QDateTime()); 0723 0724 // 5 element list 0725 list[2] = 3; 0726 list << 5; 0727 sc3.writeEntry("badList", list); 0728 0729 QVERIFY(sc3.readEntry("badList", QPoint()) == QPoint()); 0730 QVERIFY(sc3.readEntry("badList", QRect()) == QRect()); 0731 QVERIFY(sc3.readEntry("badList", QSize()) == QSize()); 0732 QVERIFY(sc3.readEntry("badList", QDate()) == QDate()); 0733 QVERIFY(sc3.readEntry("badList", QDateTime()) == QDateTime()); 0734 0735 // 6 element list 0736 list << 6; 0737 sc3.writeEntry("badList", list); 0738 0739 QVERIFY(sc3.readEntry("badList", QPoint()) == QPoint()); 0740 QVERIFY(sc3.readEntry("badList", QRect()) == QRect()); 0741 QVERIFY(sc3.readEntry("badList", QSize()) == QSize()); 0742 } 0743 0744 void KConfigTest::testChangeGroup() 0745 { 0746 KConfig sc(s_kconfig_test_subdir); 0747 KConfigGroup sc3(&sc, QStringLiteral("Hello")); 0748 QCOMPARE(sc3.name(), QStringLiteral("Hello")); 0749 KConfigGroup newGroup(sc3); 0750 0751 KConfigGroup rootGroup(sc.group(QString())); 0752 QCOMPARE(rootGroup.name(), QStringLiteral("<default>")); 0753 KConfigGroup sc32(rootGroup.group(QStringLiteral("Hello"))); 0754 QCOMPARE(sc32.name(), QStringLiteral("Hello")); 0755 KConfigGroup newGroup2(sc32); 0756 } 0757 0758 // Simple test for deleteEntry 0759 void KConfigTest::testDeleteEntry() 0760 { 0761 const QString configFile = s_test_subdir + QLatin1String("kconfigdeletetest"); 0762 0763 { 0764 KConfig conf(configFile); 0765 conf.group(QStringLiteral("Hello")).writeEntry("DelKey", "ToBeDeleted"); 0766 } 0767 const QList<QByteArray> lines = readLines(configFile); 0768 Q_ASSERT(lines.contains("[Hello]\n")); 0769 Q_ASSERT(lines.contains("DelKey=ToBeDeleted\n")); 0770 0771 KConfig sc(configFile); 0772 KConfigGroup group(&sc, QStringLiteral("Hello")); 0773 0774 group.deleteEntry("DelKey"); 0775 QCOMPARE(group.readEntry("DelKey", QStringLiteral("Fietsbel")), QStringLiteral("Fietsbel")); 0776 0777 QVERIFY(group.sync()); 0778 Q_ASSERT(!readLines(configFile).contains("DelKey=ToBeDeleted\n")); 0779 QCOMPARE(group.readEntry("DelKey", QStringLiteral("still deleted")), QStringLiteral("still deleted")); 0780 } 0781 0782 void KConfigTest::testDelete() 0783 { 0784 KConfig sc(s_kconfig_test_subdir); 0785 0786 KConfigGroup ct(&sc, QStringLiteral("Complex Types")); 0787 0788 // First delete a nested group 0789 KConfigGroup delgr(&ct, QStringLiteral("Nested Group 3")); 0790 QVERIFY(delgr.exists()); 0791 QVERIFY(ct.hasGroup(QStringLiteral("Nested Group 3"))); 0792 QVERIFY(ct.groupList().contains(QStringLiteral("Nested Group 3"))); 0793 delgr.deleteGroup(); 0794 QVERIFY(!delgr.exists()); 0795 QVERIFY(!ct.hasGroup(QStringLiteral("Nested Group 3"))); 0796 QVERIFY(!ct.groupList().contains(QStringLiteral("Nested Group 3"))); 0797 0798 KConfigGroup ng(&ct, QStringLiteral("Nested Group 2")); 0799 QVERIFY(sc.hasGroup(QStringLiteral("Complex Types"))); 0800 QVERIFY(sc.groupList().contains(QStringLiteral("Complex Types"))); 0801 QVERIFY(!sc.hasGroup(QStringLiteral("Does not exist"))); 0802 QVERIFY(ct.hasGroup(QStringLiteral("Nested Group 1"))); 0803 QVERIFY(ct.groupList().contains(QStringLiteral("Nested Group 1"))); 0804 sc.deleteGroup(QStringLiteral("Complex Types")); 0805 QCOMPARE(sc.group(QStringLiteral("Complex Types")).keyList().count(), 0); 0806 QVERIFY(!sc.hasGroup(QStringLiteral("Complex Types"))); // #192266 0807 QVERIFY(!sc.group(QStringLiteral("Complex Types")).exists()); 0808 QVERIFY(!sc.groupList().contains(QStringLiteral("Complex Types"))); 0809 QVERIFY(!ct.hasGroup(QStringLiteral("Nested Group 1"))); 0810 QVERIFY(!ct.groupList().contains(QStringLiteral("Nested Group 1"))); 0811 0812 QCOMPARE(ct.group(QStringLiteral("Nested Group 1")).keyList().count(), 0); 0813 QCOMPARE(ct.group(QStringLiteral("Nested Group 2")).keyList().count(), 0); 0814 QCOMPARE(ng.group(QStringLiteral("Nested Group 2.1")).keyList().count(), 0); 0815 0816 KConfigGroup cg(&sc, QStringLiteral("AAA")); 0817 cg.deleteGroup(); 0818 QVERIFY(sc.entryMap(QStringLiteral("Complex Types")).isEmpty()); 0819 QVERIFY(sc.entryMap(QStringLiteral("AAA")).isEmpty()); 0820 QVERIFY(!sc.entryMap(QStringLiteral("Hello")).isEmpty()); // not deleted group 0821 QVERIFY(sc.entryMap(QStringLiteral("FooBar")).isEmpty()); // inexistent group 0822 0823 KConfigGroup(&sc, QStringLiteral("LocalGroupToBeDeleted")).deleteGroup(); 0824 0825 QVERIFY(cg.sync()); 0826 // Check what happens on disk 0827 const QList<QByteArray> lines = readLines(); 0828 // qDebug() << lines; 0829 QVERIFY(!lines.contains("[Complex Types]\n")); 0830 QVERIFY(!lines.contains("[Complex Types][Nested Group 1]\n")); 0831 QVERIFY(!lines.contains("[Complex Types][Nested Group 2]\n")); 0832 QVERIFY(!lines.contains("[Complex Types][Nested Group 2.1]\n")); 0833 QVERIFY(!lines.contains("[LocalGroupToBeDeleted]\n")); 0834 QVERIFY(lines.contains("[AAA]\n")); // deleted from kconfigtest, but present in kdeglobals, so [$d] 0835 QVERIFY(lines.contains("[Hello]\n")); // a group that was not deleted 0836 0837 // test for entries that are marked as deleted when there is no default 0838 KConfig cf(s_kconfig_test_subdir, KConfig::SimpleConfig); // make sure there are no defaults 0839 cg = cf.group(QStringLiteral("Portable Devices")); 0840 cg.writeEntry("devices|manual|(null)", "whatever"); 0841 cg.writeEntry("devices|manual|/mnt/ipod", "/mnt/ipod"); 0842 QVERIFY(cf.sync()); 0843 0844 int count = 0; 0845 const QList<QByteArray> listLines = readLines(); 0846 for (const QByteArray &item : listLines) { 0847 if (item.startsWith("devices|")) { // krazy:exclude=strings 0848 ++count; 0849 } 0850 } 0851 QCOMPARE(count, 2); 0852 cg.deleteEntry("devices|manual|/mnt/ipod"); 0853 QVERIFY(cf.sync()); 0854 const QList<QByteArray> listLines2 = readLines(); 0855 for (const QByteArray &item : listLines2) { 0856 QVERIFY(!item.contains("ipod")); 0857 } 0858 } 0859 0860 void KConfigTest::testDefaultGroup() 0861 { 0862 KConfig sc(s_kconfig_test_subdir); 0863 KConfigGroup defaultGroup(&sc, QStringLiteral("<default>")); 0864 QCOMPARE(defaultGroup.name(), QStringLiteral("<default>")); 0865 QVERIFY(!defaultGroup.exists()); 0866 defaultGroup.writeEntry("TestKey", "defaultGroup"); 0867 QVERIFY(defaultGroup.exists()); 0868 QCOMPARE(defaultGroup.readEntry("TestKey", QString{}), QStringLiteral("defaultGroup")); 0869 QVERIFY(sc.sync()); 0870 0871 { 0872 // Test reading it 0873 KConfig sc2(s_kconfig_test_subdir); 0874 KConfigGroup defaultGroup2(&sc2, QStringLiteral("<default>")); 0875 QCOMPARE(defaultGroup2.name(), QStringLiteral("<default>")); 0876 QVERIFY(defaultGroup2.exists()); 0877 QCOMPARE(defaultGroup2.readEntry("TestKey", QString{}), QStringLiteral("defaultGroup")); 0878 } 0879 { 0880 // Test reading it 0881 KConfig sc2(s_kconfig_test_subdir); 0882 KConfigGroup emptyGroup(&sc2, QString()); 0883 QCOMPARE(emptyGroup.name(), QStringLiteral("<default>")); 0884 QVERIFY(emptyGroup.exists()); 0885 QCOMPARE(emptyGroup.readEntry("TestKey", QString{}), QStringLiteral("defaultGroup")); 0886 } 0887 0888 QList<QByteArray> lines = readLines(); 0889 QVERIFY(!lines.contains("[]\n")); 0890 QVERIFY(!lines.isEmpty()); 0891 QCOMPARE(lines.first(), QByteArray("TestKey=defaultGroup\n")); 0892 0893 // Now that the group exists make sure it isn't returned from groupList() 0894 const QStringList groupList = sc.groupList(); 0895 for (const QString &group : groupList) { 0896 QVERIFY(!group.isEmpty() && group != QLatin1String("<default>")); 0897 } 0898 0899 defaultGroup.deleteGroup(); 0900 QVERIFY(sc.sync()); 0901 0902 // Test if deleteGroup worked 0903 lines = readLines(); 0904 QVERIFY(lines.first() != QByteArray("TestKey=defaultGroup\n")); 0905 } 0906 0907 void KConfigTest::testEmptyGroup() 0908 { 0909 KConfig sc(s_kconfig_test_subdir); 0910 KConfigGroup emptyGroup(&sc, QString()); 0911 QCOMPARE(emptyGroup.name(), QStringLiteral("<default>")); // confusing, heh? 0912 QVERIFY(!emptyGroup.exists()); 0913 emptyGroup.writeEntry("TestKey", "emptyGroup"); 0914 QVERIFY(emptyGroup.exists()); 0915 QCOMPARE(emptyGroup.readEntry("TestKey", QString{}), QStringLiteral("emptyGroup")); 0916 QVERIFY(sc.sync()); 0917 0918 { 0919 // Test reading it 0920 KConfig sc2(s_kconfig_test_subdir); 0921 KConfigGroup defaultGroup(&sc2, QStringLiteral("<default>")); 0922 QCOMPARE(defaultGroup.name(), QStringLiteral("<default>")); 0923 QVERIFY(defaultGroup.exists()); 0924 QCOMPARE(defaultGroup.readEntry("TestKey", QString{}), QStringLiteral("emptyGroup")); 0925 } 0926 { 0927 // Test reading it 0928 KConfig sc2(s_kconfig_test_subdir); 0929 KConfigGroup emptyGroup2(&sc2, QString()); 0930 QCOMPARE(emptyGroup2.name(), QStringLiteral("<default>")); 0931 QVERIFY(emptyGroup2.exists()); 0932 QCOMPARE(emptyGroup2.readEntry("TestKey", QString{}), QStringLiteral("emptyGroup")); 0933 } 0934 0935 QList<QByteArray> lines = readLines(); 0936 QVERIFY(!lines.contains("[]\n")); // there's no support for the [] group, in fact. 0937 QCOMPARE(lines.first(), QByteArray("TestKey=emptyGroup\n")); 0938 0939 // Now that the group exists make sure it isn't returned from groupList() 0940 const QStringList groupList = sc.groupList(); 0941 for (const QString &group : groupList) { 0942 QVERIFY(!group.isEmpty() && group != QLatin1String("<default>")); 0943 } 0944 emptyGroup.deleteGroup(); 0945 QVERIFY(sc.sync()); 0946 0947 // Test if deleteGroup worked 0948 lines = readLines(); 0949 QVERIFY(lines.first() != QByteArray("TestKey=defaultGroup\n")); 0950 } 0951 0952 #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_BLACKBERRY) && !defined(Q_OS_ANDROID) 0953 #define Q_XDG_PLATFORM 0954 #endif 0955 0956 void KConfigTest::testCascadingWithLocale() 0957 { 0958 // This test relies on XDG_CONFIG_DIRS, which only has effect on Unix. 0959 // Cascading (more than two levels) isn't available at all on Windows. 0960 #ifdef Q_XDG_PLATFORM 0961 QTemporaryDir middleDir; 0962 QTemporaryDir globalDir; 0963 const QByteArray oldConfigDirs = qgetenv("XDG_CONFIG_DIRS"); 0964 qputenv("XDG_CONFIG_DIRS", qPrintable(middleDir.path() + QLatin1Char(':') + globalDir.path())); 0965 0966 const QString globalConfigDir = globalDir.path() + QLatin1Char('/') + s_test_subdir; 0967 QVERIFY(QDir().mkpath(globalConfigDir)); 0968 QFile global(globalConfigDir + QLatin1String("foo.desktop")); 0969 QVERIFY(global.open(QIODevice::WriteOnly | QIODevice::Text)); 0970 QTextStream globalOut(&global); 0971 globalOut << "[Group]\n" 0972 << "FromGlobal=true\n" 0973 << "FromGlobal[fr]=vrai\n" 0974 << "Name=Testing\n" 0975 << "Name[fr]=FR\n" 0976 << "Other=Global\n" 0977 << "Other[fr]=Global_FR\n"; 0978 global.close(); 0979 0980 const QString middleConfigDir = middleDir.path() + QLatin1Char('/') + s_test_subdir; 0981 QVERIFY(QDir().mkpath(middleConfigDir)); 0982 QFile local(middleConfigDir + QLatin1String("foo.desktop")); 0983 QVERIFY(local.open(QIODevice::WriteOnly | QIODevice::Text)); 0984 QTextStream out(&local); 0985 out << "[Group]\n" 0986 << "FromLocal=true\n" 0987 << "FromLocal[fr]=vrai\n" 0988 << "Name=Local Testing\n" 0989 << "Name[fr]=FR\n" 0990 << "Other=English Only\n"; 0991 local.close(); 0992 0993 KConfig config(s_test_subdir + QLatin1String("foo.desktop")); 0994 KConfigGroup group = config.group(QStringLiteral("Group")); 0995 QCOMPARE(group.readEntry("FromGlobal"), QStringLiteral("true")); 0996 QCOMPARE(group.readEntry("FromLocal"), QStringLiteral("true")); 0997 QCOMPARE(group.readEntry("Name"), QStringLiteral("Local Testing")); 0998 config.setLocale(QStringLiteral("fr")); 0999 QCOMPARE(group.readEntry("FromGlobal"), QStringLiteral("vrai")); 1000 QCOMPARE(group.readEntry("FromLocal"), QStringLiteral("vrai")); 1001 QCOMPARE(group.readEntry("Name"), QStringLiteral("FR")); 1002 QCOMPARE(group.readEntry("Other"), QStringLiteral("English Only")); // Global_FR is locally overridden 1003 qputenv("XDG_CONFIG_DIRS", oldConfigDirs); 1004 #endif 1005 } 1006 1007 void KConfigTest::testMerge() 1008 { 1009 DefaultLocale defaultLocale; 1010 QLocale::setDefault(QLocale::c()); 1011 KConfig config(s_test_subdir + QLatin1String("mergetest"), KConfig::SimpleConfig); 1012 1013 KConfigGroup cg = config.group(QStringLiteral("some group")); 1014 cg.writeEntry("entry", " random entry"); 1015 cg.writeEntry("another entry", "blah blah blah"); 1016 1017 { 1018 // simulate writing by another process 1019 QFile file(m_testConfigDir + QLatin1String("/mergetest")); 1020 file.open(QIODevice::WriteOnly | QIODevice::Text); 1021 QTextStream out(&file); 1022 out << "[Merged Group]\n" 1023 << "entry1=Testing\n" 1024 << "entry2=More Testing\n" 1025 << "[some group]\n" 1026 << "entry[fr]=French\n" 1027 << "entry[es]=Spanish\n" 1028 << "entry[de]=German\n"; 1029 } 1030 QVERIFY(config.sync()); 1031 1032 { 1033 QList<QByteArray> lines; 1034 // this is what the file should look like 1035 lines << "[Merged Group]\n" 1036 << "entry1=Testing\n" 1037 << "entry2=More Testing\n" 1038 << "\n" 1039 << "[some group]\n" 1040 << "another entry=blah blah blah\n" 1041 << "entry=\\srandom entry\n" 1042 << "entry[de]=German\n" 1043 << "entry[es]=Spanish\n" 1044 << "entry[fr]=French\n"; 1045 QFile file(m_testConfigDir + QLatin1String("/mergetest")); 1046 file.open(QIODevice::ReadOnly | QIODevice::Text); 1047 for (const QByteArray &line : std::as_const(lines)) { 1048 QCOMPARE(line, file.readLine()); 1049 } 1050 } 1051 } 1052 1053 void KConfigTest::testImmutable() 1054 { 1055 { 1056 QFile file(m_testConfigDir + QLatin1String("/immutabletest")); 1057 file.open(QIODevice::WriteOnly | QIODevice::Text); 1058 QTextStream out(&file); 1059 out << "[$i]\n" 1060 << "entry1=Testing\n" 1061 << "[group][$i]\n" 1062 << "[group][subgroup][$i]\n"; 1063 } 1064 1065 KConfig config(s_test_subdir + QLatin1String("immutabletest"), KConfig::SimpleConfig); 1066 QVERIFY(config.isGroupImmutable(QString())); 1067 KConfigGroup cg = config.group(QString()); 1068 QVERIFY(cg.isEntryImmutable("entry1")); 1069 KConfigGroup cg1 = config.group(QStringLiteral("group")); 1070 QVERIFY(cg1.isImmutable()); 1071 KConfigGroup cg1a = cg.group(QStringLiteral("group")); 1072 QVERIFY(cg1a.isImmutable()); 1073 KConfigGroup cg2 = cg1.group(QStringLiteral("subgroup")); 1074 QVERIFY(cg2.isImmutable()); 1075 } 1076 1077 void KConfigTest::testOptionOrder() 1078 { 1079 { 1080 QFile file(m_testConfigDir + QLatin1String("/doubleattrtest")); 1081 file.open(QIODevice::WriteOnly | QIODevice::Text); 1082 QTextStream out(&file); 1083 out << "[group3]\n" 1084 << "entry2=unlocalized\n" 1085 << "entry2[$i][de_DE]=t2\n"; 1086 } 1087 KConfig config(s_test_subdir + QLatin1String("doubleattrtest"), KConfig::SimpleConfig); 1088 config.setLocale(QStringLiteral("de_DE")); 1089 KConfigGroup cg3 = config.group(QStringLiteral("group3")); 1090 QVERIFY(!cg3.isImmutable()); 1091 QCOMPARE(cg3.readEntry("entry2", ""), QStringLiteral("t2")); 1092 QVERIFY(cg3.isEntryImmutable("entry2")); 1093 config.setLocale(QStringLiteral("C")); 1094 QCOMPARE(cg3.readEntry("entry2", ""), QStringLiteral("unlocalized")); 1095 QVERIFY(!cg3.isEntryImmutable("entry2")); 1096 cg3.writeEntry("entry2", "modified"); 1097 QVERIFY(config.sync()); 1098 1099 { 1100 QList<QByteArray> lines; 1101 // this is what the file should look like 1102 lines << "[group3]\n" 1103 << "entry2=modified\n" 1104 << "entry2[de_DE][$i]=t2\n"; 1105 1106 QFile file(m_testConfigDir + QLatin1String("/doubleattrtest")); 1107 file.open(QIODevice::ReadOnly | QIODevice::Text); 1108 for (const QByteArray &line : std::as_const(lines)) { 1109 QCOMPARE(line, file.readLine()); 1110 } 1111 } 1112 } 1113 1114 void KConfigTest::testGroupEscape() 1115 { 1116 KConfig config(s_test_subdir + QLatin1String("groupescapetest"), KConfig::SimpleConfig); 1117 QVERIFY(config.group(s_dollargroup).exists()); 1118 } 1119 1120 void KConfigTest::testSubGroup() 1121 { 1122 KConfig sc(s_kconfig_test_subdir); 1123 KConfigGroup cg(&sc, QStringLiteral("ParentGroup")); 1124 QCOMPARE(cg.readEntry("parentgrpstring", ""), QStringLiteral("somevalue")); 1125 KConfigGroup subcg1(&cg, QStringLiteral("SubGroup1")); 1126 QCOMPARE(subcg1.name(), QStringLiteral("SubGroup1")); 1127 QCOMPARE(subcg1.readEntry("somestring", ""), QStringLiteral("somevalue")); 1128 KConfigGroup subcg2(&cg, QStringLiteral("SubGroup2")); 1129 QCOMPARE(subcg2.name(), QStringLiteral("SubGroup2")); 1130 QCOMPARE(subcg2.readEntry("substring", ""), QStringLiteral("somevalue")); 1131 KConfigGroup subcg3(&cg, QStringLiteral("SubGroup/3")); 1132 QCOMPARE(subcg3.readEntry("sub3string", ""), QStringLiteral("somevalue")); 1133 QCOMPARE(subcg3.name(), QStringLiteral("SubGroup/3")); 1134 KConfigGroup rcg(&sc, QString()); 1135 KConfigGroup srcg(&rcg, QStringLiteral("ParentGroup")); 1136 QCOMPARE(srcg.readEntry("parentgrpstring", ""), QStringLiteral("somevalue")); 1137 1138 QStringList groupList = cg.groupList(); 1139 groupList.sort(); // comes from QSet, so order is undefined 1140 QCOMPARE(groupList, (QStringList{QStringLiteral("SubGroup/3"), QStringLiteral("SubGroup1"), QStringLiteral("SubGroup2")})); 1141 1142 const QStringList expectedSubgroup3Keys{QStringLiteral("sub3string")}; 1143 QCOMPARE(subcg3.keyList(), expectedSubgroup3Keys); 1144 const QStringList expectedParentGroupKeys{QStringLiteral("parentgrpstring")}; 1145 1146 QCOMPARE(cg.keyList(), expectedParentGroupKeys); 1147 1148 QCOMPARE(QStringList(cg.entryMap().keys()), expectedParentGroupKeys); 1149 QCOMPARE(QStringList(subcg3.entryMap().keys()), expectedSubgroup3Keys); 1150 1151 // Create A group containing only other groups. We want to make sure it 1152 // shows up in groupList of sc 1153 KConfigGroup neg(&sc, QStringLiteral("NoEntryGroup")); 1154 KConfigGroup negsub1(&neg, QStringLiteral("NEG Child1")); 1155 negsub1.writeEntry("entry", "somevalue"); 1156 KConfigGroup negsub2(&neg, QStringLiteral("NEG Child2")); 1157 KConfigGroup negsub3(&neg, QStringLiteral("NEG Child3")); 1158 KConfigGroup negsub31(&negsub3, QStringLiteral("NEG Child3-1")); 1159 KConfigGroup negsub4(&neg, QStringLiteral("NEG Child4")); 1160 KConfigGroup negsub41(&negsub4, QStringLiteral("NEG Child4-1")); 1161 negsub41.writeEntry("entry", "somevalue"); 1162 1163 // A group exists if it has content 1164 QVERIFY(negsub1.exists()); 1165 1166 // But it doesn't exist if it has no content 1167 // Ossi and David say: this is how it's supposed to work. 1168 // However you could add a dummy entry for now, or we could add a "Persist" feature to kconfig groups 1169 // which would make it written out, much like "immutable" already makes them persistent. 1170 QVERIFY(!negsub2.exists()); 1171 1172 // A subgroup does not qualify as content if it is also empty 1173 QVERIFY(!negsub3.exists()); 1174 1175 // A subgroup with content is ok 1176 QVERIFY(negsub4.exists()); 1177 1178 // Only subgroups with content show up in groupList() 1179 // QEXPECT_FAIL("", "Empty subgroups do not show up in groupList()", Continue); 1180 // QCOMPARE(neg.groupList(), QStringList() << "NEG Child1" << "NEG Child2" << "NEG Child3" << "NEG Child4"); 1181 // This is what happens 1182 QStringList groups = neg.groupList(); 1183 groups.sort(); // Qt5 made the ordering unreliable, due to QHash 1184 QCOMPARE(groups, (QStringList{QStringLiteral("NEG Child1"), QStringLiteral("NEG Child4")})); 1185 1186 // make sure groupList() isn't returning something it shouldn't 1187 const QStringList listGroup = sc.groupList(); 1188 for (const QString &group : listGroup) { 1189 QVERIFY(!group.isEmpty() && group != QLatin1String("<default>")); 1190 QVERIFY(!group.contains(QChar(0x1d))); 1191 QVERIFY(!group.contains(QLatin1String("subgroup"))); 1192 QVERIFY(!group.contains(QLatin1String("SubGroup"))); 1193 } 1194 1195 QVERIFY(sc.sync()); 1196 1197 // Check that the empty groups are not written out. 1198 const QList<QByteArray> lines = readLines(); 1199 QVERIFY(lines.contains("[NoEntryGroup][NEG Child1]\n")); 1200 QVERIFY(!lines.contains("[NoEntryGroup][NEG Child2]\n")); 1201 QVERIFY(!lines.contains("[NoEntryGroup][NEG Child3]\n")); 1202 QVERIFY(!lines.contains("[NoEntryGroup][NEG Child4]\n")); // implicit group, not written out 1203 QVERIFY(lines.contains("[NoEntryGroup][NEG Child4][NEG Child4-1]\n")); 1204 } 1205 1206 void KConfigTest::testAddConfigSources() 1207 { 1208 KConfig cf(s_test_subdir + QLatin1String("specificrc")); 1209 1210 cf.addConfigSources(QStringList{m_testConfigDir + QLatin1String("/baserc")}); 1211 cf.reparseConfiguration(); 1212 1213 KConfigGroup specificgrp(&cf, QStringLiteral("Specific Only Group")); 1214 QCOMPARE(specificgrp.readEntry("ExistingEntry", ""), QStringLiteral("DevValue")); 1215 1216 KConfigGroup sharedgrp(&cf, QStringLiteral("Shared Group")); 1217 QCOMPARE(sharedgrp.readEntry("SomeSpecificOnlyEntry", ""), QStringLiteral("DevValue")); 1218 QCOMPARE(sharedgrp.readEntry("SomeBaseOnlyEntry", ""), QStringLiteral("BaseValue")); 1219 QCOMPARE(sharedgrp.readEntry("SomeSharedEntry", ""), QStringLiteral("DevValue")); 1220 1221 KConfigGroup basegrp(&cf, QStringLiteral("Base Only Group")); 1222 QCOMPARE(basegrp.readEntry("ExistingEntry", ""), QStringLiteral("BaseValue")); 1223 basegrp.writeEntry("New Entry Base Only", "SomeValue"); 1224 1225 KConfigGroup newgrp(&cf, QStringLiteral("New Group")); 1226 newgrp.writeEntry("New Entry", "SomeValue"); 1227 1228 QVERIFY(cf.sync()); 1229 1230 KConfig plaincfg(s_test_subdir + QLatin1String("specificrc")); 1231 1232 KConfigGroup newgrp2(&plaincfg, QStringLiteral("New Group")); 1233 QCOMPARE(newgrp2.readEntry("New Entry", ""), QStringLiteral("SomeValue")); 1234 1235 KConfigGroup basegrp2(&plaincfg, QStringLiteral("Base Only Group")); 1236 QCOMPARE(basegrp2.readEntry("New Entry Base Only", ""), QStringLiteral("SomeValue")); 1237 } 1238 1239 void KConfigTest::testGroupCopyTo() 1240 { 1241 KConfig cf1(s_kconfig_test_subdir); 1242 KConfigGroup original = cf1.group(QStringLiteral("Enum Types")); 1243 1244 KConfigGroup copy = cf1.group(QStringLiteral("Enum Types Copy")); 1245 original.copyTo(©); // copy from one group to another 1246 QCOMPARE(copy.entryMap(), original.entryMap()); 1247 1248 KConfig cf2(s_test_subdir + QLatin1String("copy_of_kconfigtest"), KConfig::SimpleConfig); 1249 QVERIFY(!cf2.hasGroup(original.name())); 1250 QVERIFY(!cf2.hasGroup(copy.name())); 1251 1252 KConfigGroup newGroup = cf2.group(original.name()); 1253 original.copyTo(&newGroup); // copy from one file to another 1254 QVERIFY(cf2.hasGroup(original.name())); 1255 QVERIFY(!cf2.hasGroup(copy.name())); // make sure we didn't copy more than we wanted 1256 QCOMPARE(newGroup.entryMap(), original.entryMap()); 1257 } 1258 1259 void KConfigTest::testConfigCopyToSync() 1260 { 1261 KConfig cf1(s_kconfig_test_subdir); 1262 // Prepare source file 1263 KConfigGroup group(&cf1, QStringLiteral("CopyToTest")); 1264 group.writeEntry("Type", "Test"); 1265 QVERIFY(cf1.sync()); 1266 1267 // Copy to "destination" 1268 const QString destination = m_testConfigDir + QLatin1String("/kconfigcopytotest"); 1269 QFile::remove(destination); 1270 1271 KConfig cf2(s_test_subdir + QLatin1String("kconfigcopytotest")); 1272 KConfigGroup group2(&cf2, QStringLiteral("CopyToTest")); 1273 1274 group.copyTo(&group2); 1275 1276 QString testVal = group2.readEntry("Type"); 1277 QCOMPARE(testVal, QStringLiteral("Test")); 1278 // should write to disk the copied data from group 1279 QVERIFY(cf2.sync()); 1280 QVERIFY(QFile::exists(destination)); 1281 } 1282 1283 void KConfigTest::testConfigCopyTo() 1284 { 1285 KConfig cf1(s_kconfig_test_subdir); 1286 { 1287 // Prepare source file 1288 KConfigGroup group(&cf1, QStringLiteral("CopyToTest")); 1289 group.writeEntry("Type", "Test"); 1290 QVERIFY(cf1.sync()); 1291 } 1292 1293 { 1294 // Copy to "destination" 1295 const QString destination = m_testConfigDir + QLatin1String("/kconfigcopytotest"); 1296 QFile::remove(destination); 1297 KConfig cf2; 1298 cf1.copyTo(destination, &cf2); 1299 KConfigGroup group2(&cf2, QStringLiteral("CopyToTest")); 1300 QString testVal = group2.readEntry("Type"); 1301 QCOMPARE(testVal, QStringLiteral("Test")); 1302 QVERIFY(cf2.sync()); 1303 QVERIFY(QFile::exists(destination)); 1304 } 1305 1306 // Check copied config file on disk 1307 KConfig cf3(s_test_subdir + QLatin1String("kconfigcopytotest")); 1308 KConfigGroup group3(&cf3, QStringLiteral("CopyToTest")); 1309 QString testVal = group3.readEntry("Type"); 1310 QCOMPARE(testVal, QStringLiteral("Test")); 1311 } 1312 1313 void KConfigTest::testReparent() 1314 { 1315 KConfig cf(s_kconfig_test_subdir); 1316 const QString name(QStringLiteral("Enum Types")); 1317 KConfigGroup group = cf.group(name); 1318 const QMap<QString, QString> originalMap = group.entryMap(); 1319 KConfigGroup parent = cf.group(QStringLiteral("Parent Group")); 1320 1321 QVERIFY(!parent.hasGroup(name)); 1322 1323 QVERIFY(group.entryMap() == originalMap); 1324 1325 group.reparent(&parent); // see if it can be made a sub-group of another group 1326 QVERIFY(parent.hasGroup(name)); 1327 QCOMPARE(group.entryMap(), originalMap); 1328 1329 group.reparent(&cf); // see if it can make it a top-level group again 1330 // QVERIFY(!parent.hasGroup(name)); 1331 QCOMPARE(group.entryMap(), originalMap); 1332 } 1333 1334 static void ageTimeStamp(const QString &path, int nsec) 1335 { 1336 #ifdef Q_OS_UNIX 1337 QDateTime mtime = QFileInfo(path).lastModified().addSecs(-nsec); 1338 struct utimbuf utbuf; 1339 utbuf.actime = mtime.toSecsSinceEpoch(); 1340 utbuf.modtime = utbuf.actime; 1341 utime(QFile::encodeName(path).constData(), &utbuf); 1342 #else 1343 QTest::qSleep(nsec * 1000); 1344 #endif 1345 } 1346 1347 void KConfigTest::testWriteOnSync() 1348 { 1349 QDateTime oldStamp; 1350 QDateTime newStamp; 1351 KConfig sc(s_kconfig_test_subdir, KConfig::IncludeGlobals); 1352 1353 // Age the timestamp of global config file a few sec, and collect it. 1354 QString globFile = m_kdeGlobalsPath; 1355 ageTimeStamp(globFile, 2); // age 2 sec 1356 oldStamp = QFileInfo(globFile).lastModified(); 1357 1358 // Add a local entry and sync the config. 1359 // Should not rewrite the global config file. 1360 KConfigGroup cgLocal(&sc, QStringLiteral("Locals")); 1361 cgLocal.writeEntry("someLocalString", "whatever"); 1362 QVERIFY(sc.sync()); 1363 1364 // Verify that the timestamp of global config file didn't change. 1365 newStamp = QFileInfo(globFile).lastModified(); 1366 QCOMPARE(newStamp, oldStamp); 1367 1368 // Age the timestamp of local config file a few sec, and collect it. 1369 QString locFile = m_testConfigDir + QLatin1String("/kconfigtest"); 1370 ageTimeStamp(locFile, 2); // age 2 sec 1371 oldStamp = QFileInfo(locFile).lastModified(); 1372 1373 // Add a global entry and sync the config. 1374 // Should not rewrite the local config file. 1375 KConfigGroup cgGlobal(&sc, QStringLiteral("Globals")); 1376 cgGlobal.writeEntry("someGlobalString", "whatever", KConfig::Persistent | KConfig::Global); 1377 QVERIFY(sc.sync()); 1378 1379 // Verify that the timestamp of local config file didn't change. 1380 newStamp = QFileInfo(locFile).lastModified(); 1381 QCOMPARE(newStamp, oldStamp); 1382 } 1383 1384 void KConfigTest::testFailOnReadOnlyFileSync() 1385 { 1386 KConfig sc(s_test_subdir + QLatin1String("kconfigfailonreadonlytest")); 1387 KConfigGroup cgLocal(&sc, QStringLiteral("Locals")); 1388 1389 cgLocal.writeEntry("someLocalString", "whatever"); 1390 QVERIFY(cgLocal.sync()); 1391 1392 QFile f(m_testConfigDir + QLatin1String("kconfigfailonreadonlytest")); 1393 QVERIFY(f.exists()); 1394 QVERIFY(f.setPermissions(QFileDevice::ReadOwner)); 1395 1396 #ifndef Q_OS_WIN 1397 if (::getuid() == 0) { 1398 QSKIP("Root can write to read-only files"); 1399 } 1400 #endif 1401 cgLocal.writeEntry("someLocalString", "whatever2"); 1402 QVERIFY(!cgLocal.sync()); 1403 1404 QVERIFY(f.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner)); 1405 QVERIFY(f.remove()); 1406 } 1407 1408 void KConfigTest::testDirtyOnEqual() 1409 { 1410 QDateTime oldStamp; 1411 QDateTime newStamp; 1412 KConfig sc(s_kconfig_test_subdir); 1413 1414 // Initialize value 1415 KConfigGroup cgLocal(&sc, QStringLiteral("random")); 1416 cgLocal.writeEntry("theKey", "whatever"); 1417 QVERIFY(sc.sync()); 1418 1419 // Age the timestamp of local config file a few sec, and collect it. 1420 QString locFile = m_testConfigDir + QLatin1String("/kconfigtest"); 1421 ageTimeStamp(locFile, 2); // age 2 sec 1422 oldStamp = QFileInfo(locFile).lastModified(); 1423 1424 // Write exactly the same again 1425 cgLocal.writeEntry("theKey", "whatever"); 1426 // This should be a no-op 1427 QVERIFY(sc.sync()); 1428 1429 // Verify that the timestamp of local config file didn't change. 1430 newStamp = QFileInfo(locFile).lastModified(); 1431 QCOMPARE(newStamp, oldStamp); 1432 } 1433 1434 void KConfigTest::testDirtyOnEqualOverdo() 1435 { 1436 QByteArray val1( 1437 "\0" 1438 "one", 1439 4); 1440 QByteArray val2( 1441 "\0" 1442 "two", 1443 4); 1444 QByteArray defvalr; 1445 1446 KConfig sc(s_kconfig_test_subdir); 1447 KConfigGroup cgLocal(&sc, QStringLiteral("random")); 1448 cgLocal.writeEntry("someKey", val1); 1449 QCOMPARE(cgLocal.readEntry("someKey", defvalr), val1); 1450 cgLocal.writeEntry("someKey", val2); 1451 QCOMPARE(cgLocal.readEntry("someKey", defvalr), val2); 1452 } 1453 1454 void KConfigTest::testCreateDir() 1455 { 1456 // Test auto-creating the parent directory when needed (KConfigIniBackend::createEnclosing) 1457 const QString kdehome = QDir::home().canonicalPath() + QLatin1String("/.kde-unit-test"); 1458 const QString subdir = kdehome + QLatin1String("/newsubdir"); 1459 const QString file = subdir + QLatin1String("/foo.desktop"); 1460 QFile::remove(file); 1461 QDir().rmdir(subdir); 1462 QVERIFY(!QDir().exists(subdir)); 1463 KDesktopFile desktopFile(file); 1464 desktopFile.desktopGroup().writeEntry("key", "value"); 1465 QVERIFY(desktopFile.sync()); 1466 QVERIFY(QFile::exists(file)); 1467 1468 // Cleanup 1469 QFile::remove(file); 1470 QDir().rmdir(subdir); 1471 } 1472 1473 void KConfigTest::testSyncOnExit() 1474 { 1475 // Often, the KGlobalPrivate global static's destructor ends up calling ~KConfig -> 1476 // KConfig::sync ... and if that code triggers KGlobal code again then things could crash. 1477 // So here's a test for modifying KSharedConfig::openConfig() and not syncing, the process exit will sync. 1478 KConfigGroup grp(KSharedConfig::openConfig(s_test_subdir + QLatin1String("syncOnExitRc")), QStringLiteral("syncOnExit")); 1479 grp.writeEntry("key", "value"); 1480 } 1481 1482 void KConfigTest::testSharedConfig() 1483 { 1484 // Can I use a KConfigGroup even after the KSharedConfigPtr goes out of scope? 1485 KConfigGroup myConfigGroup; 1486 { 1487 KSharedConfigPtr config = KSharedConfig::openConfig(s_kconfig_test_subdir); 1488 myConfigGroup = KConfigGroup(config, QStringLiteral("Hello")); 1489 } 1490 QCOMPARE(myConfigGroup.readEntry("stringEntry1"), s_string_entry1); 1491 1492 // Get the main config 1493 KSharedConfigPtr mainConfig = KSharedConfig::openConfig(); 1494 KConfigGroup mainGroup(mainConfig, QStringLiteral("Main")); 1495 QCOMPARE(mainGroup.readEntry("Key", QString{}), QStringLiteral("Value")); 1496 } 1497 1498 void KConfigTest::testLocaleConfig() 1499 { 1500 // Initialize the testdata 1501 QDir().mkpath(m_testConfigDir); 1502 const QString file = m_testConfigDir + QLatin1String("/localized.test"); 1503 QFile::remove(file); 1504 QFile f(file); 1505 QVERIFY(f.open(QIODevice::WriteOnly)); 1506 QTextStream ts(&f); 1507 ts << "[Test_Wrong]\n"; 1508 ts << "foo[ca]=5\n"; 1509 ts << "foostring[ca]=nice\n"; 1510 ts << "foobool[ca]=true\n"; 1511 ts << "[Test_Right]\n"; 1512 ts << "foo=5\n"; 1513 ts << "foo[ca]=5\n"; 1514 ts << "foostring=primary\n"; 1515 ts << "foostring[ca]=nice\n"; 1516 ts << "foobool=primary\n"; 1517 ts << "foobool[ca]=true\n"; 1518 f.close(); 1519 1520 // Load the testdata 1521 QVERIFY(QFile::exists(file)); 1522 KConfig config(file); 1523 config.setLocale(QStringLiteral("ca")); 1524 1525 // This group has only localized values. That is not supported. The values 1526 // should be dropped on loading. 1527 KConfigGroup cg(&config, QStringLiteral("Test_Wrong")); 1528 QEXPECT_FAIL("", "The localized values are not dropped", Continue); 1529 QVERIFY(!cg.hasKey("foo")); 1530 QEXPECT_FAIL("", "The localized values are not dropped", Continue); 1531 QVERIFY(!cg.hasKey("foostring")); 1532 QEXPECT_FAIL("", "The localized values are not dropped", Continue); 1533 QVERIFY(!cg.hasKey("foobool")); 1534 1535 // Now check the correct config group 1536 KConfigGroup cg2(&config, QStringLiteral("Test_Right")); 1537 QCOMPARE(cg2.readEntry("foo"), QStringLiteral("5")); 1538 QCOMPARE(cg2.readEntry("foo", 3), 5); 1539 QCOMPARE(cg2.readEntry("foostring"), QStringLiteral("nice")); 1540 QCOMPARE(cg2.readEntry("foostring", "ugly"), QStringLiteral("nice")); 1541 QCOMPARE(cg2.readEntry("foobool"), QStringLiteral("true")); 1542 QCOMPARE(cg2.readEntry("foobool", false), true); 1543 1544 // Clean up after the testcase 1545 QFile::remove(file); 1546 } 1547 1548 void KConfigTest::testDeleteWhenLocalized() 1549 { 1550 // Initialize the testdata 1551 const QString subdir = QDir::home().canonicalPath() + QLatin1String("/.kde-unit-test/"); 1552 QDir().mkpath(subdir); 1553 const QString file = subdir + QLatin1String("/localized_delete.test"); 1554 QFile::remove(file); 1555 QFile f(file); 1556 QVERIFY(f.open(QIODevice::WriteOnly)); 1557 QTextStream ts(&f); 1558 ts << "[Test4711]\n"; 1559 ts << "foo=3\n"; 1560 ts << "foo[ca]=5\n"; 1561 ts << "foo[de]=7\n"; 1562 ts << "foostring=ugly\n"; 1563 ts << "foostring[ca]=nice\n"; 1564 ts << "foostring[de]=schoen\n"; 1565 ts << "foobool=false\n"; 1566 ts << "foobool[ca]=true\n"; 1567 ts << "foobool[de]=true\n"; 1568 f.close(); 1569 1570 // Load the testdata. We start in locale "ca". 1571 QVERIFY(QFile::exists(file)); 1572 KConfig config(file); 1573 config.setLocale(QStringLiteral("ca")); 1574 KConfigGroup cg(&config, QStringLiteral("Test4711")); 1575 1576 // Delete a value. Once with localized, once with Normal 1577 cg.deleteEntry("foostring", KConfigBase::Persistent | KConfigBase::Localized); 1578 cg.deleteEntry("foobool"); 1579 QVERIFY(config.sync()); 1580 1581 // The value is now gone. The others are still there. Everything correct 1582 // here. 1583 QVERIFY(!cg.hasKey("foostring")); 1584 QVERIFY(!cg.hasKey("foobool")); 1585 QVERIFY(cg.hasKey("foo")); 1586 1587 // The current state is: (Just return before this comment.) 1588 // [...] 1589 // foobool[ca]=true 1590 // foobool[de]=wahr 1591 // foostring=ugly 1592 // foostring[de]=schoen 1593 1594 // Now switch the locale to "de" and repeat the checks. Results should be 1595 // the same. But they currently are not. The localized value are 1596 // independent of each other. All values are still there in "de". 1597 config.setLocale(QStringLiteral("de")); 1598 QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); 1599 QVERIFY(!cg.hasKey("foostring")); 1600 QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); 1601 QVERIFY(!cg.hasKey("foobool")); 1602 QVERIFY(cg.hasKey("foo")); 1603 // Check where the wrong values come from. 1604 // We get the "de" value. 1605 QCOMPARE(cg.readEntry("foostring", "nothing"), QStringLiteral("schoen")); 1606 // We get the "de" value. 1607 QCOMPARE(cg.readEntry("foobool", false), true); 1608 1609 // Now switch the locale back "ca" and repeat the checks. Results are 1610 // again different. 1611 config.setLocale(QStringLiteral("ca")); 1612 // This line worked above. But now it fails. 1613 QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); 1614 QVERIFY(!cg.hasKey("foostring")); 1615 // This line worked above too. 1616 QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); 1617 QVERIFY(!cg.hasKey("foobool")); 1618 QVERIFY(cg.hasKey("foo")); 1619 // Check where the wrong values come from. 1620 // We get the primary value because the "ca" value was deleted. 1621 QCOMPARE(cg.readEntry("foostring", "nothing"), QStringLiteral("ugly")); 1622 // We get the "ca" value. 1623 QCOMPARE(cg.readEntry("foobool", false), true); 1624 1625 // Now test the deletion of a group. 1626 cg.deleteGroup(); 1627 QVERIFY(config.sync()); 1628 1629 // Current state: [ca] and [de] entries left... oops. 1630 // qDebug() << readLinesFrom(file); 1631 1632 // Bug: The group still exists [because of the localized entries]... 1633 QVERIFY(cg.exists()); 1634 QVERIFY(!cg.hasKey("foo")); 1635 QVERIFY(!cg.hasKey("foostring")); 1636 QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); 1637 QVERIFY(!cg.hasKey("foobool")); 1638 1639 // Now switch the locale to "de" and repeat the checks. All values 1640 // still here because only the primary values are deleted. 1641 config.setLocale(QStringLiteral("de")); 1642 QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); 1643 QVERIFY(!cg.hasKey("foo")); 1644 QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); 1645 QVERIFY(!cg.hasKey("foostring")); 1646 QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); 1647 QVERIFY(!cg.hasKey("foobool")); 1648 // Check where the wrong values come from. 1649 // We get the "de" value. 1650 QCOMPARE(cg.readEntry("foostring", "nothing"), QStringLiteral("schoen")); 1651 // We get the "de" value. 1652 QCOMPARE(cg.readEntry("foobool", false), true); 1653 // We get the "de" value. 1654 QCOMPARE(cg.readEntry("foo", 0), 7); 1655 1656 // Now switch the locale to "ca" and repeat the checks 1657 // "foostring" is now really gone because both the primary value and the 1658 // "ca" value are deleted. 1659 config.setLocale(QStringLiteral("ca")); 1660 QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); 1661 QVERIFY(!cg.hasKey("foo")); 1662 QVERIFY(!cg.hasKey("foostring")); 1663 QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); 1664 QVERIFY(!cg.hasKey("foobool")); 1665 // Check where the wrong values come from. 1666 // We get the "ca" value. 1667 QCOMPARE(cg.readEntry("foobool", false), true); 1668 // We get the "ca" value. 1669 QCOMPARE(cg.readEntry("foo", 0), 5); 1670 1671 // Cleanup 1672 QFile::remove(file); 1673 } 1674 1675 void KConfigTest::testKdeGlobals() 1676 { 1677 { 1678 KConfig glob(QStringLiteral("kdeglobals")); 1679 KConfigGroup general(&glob, QStringLiteral("General")); 1680 general.writeEntry("testKG", "1"); 1681 QVERIFY(glob.sync()); 1682 } 1683 1684 KConfig globRead(QStringLiteral("kdeglobals")); 1685 const KConfigGroup general(&globRead, QStringLiteral("General")); 1686 QCOMPARE(general.readEntry("testKG"), QStringLiteral("1")); 1687 1688 // Check we wrote into kdeglobals 1689 const QList<QByteArray> lines = readLines(QStringLiteral("kdeglobals")); 1690 QVERIFY(lines.contains("[General]\n")); 1691 QVERIFY(lines.contains("testKG=1\n")); 1692 1693 // Writing using NoGlobals 1694 { 1695 KConfig glob(QStringLiteral("kdeglobals"), KConfig::NoGlobals); 1696 KConfigGroup general(&glob, QStringLiteral("General")); 1697 general.writeEntry("testKG", "2"); 1698 QVERIFY(glob.sync()); 1699 } 1700 globRead.reparseConfiguration(); 1701 QCOMPARE(general.readEntry("testKG"), QStringLiteral("2")); 1702 1703 // Reading using NoGlobals 1704 { 1705 KConfig globReadNoGlob(QStringLiteral("kdeglobals"), KConfig::NoGlobals); 1706 const KConfigGroup generalNoGlob(&globReadNoGlob, QStringLiteral("General")); 1707 QCOMPARE(generalNoGlob.readEntry("testKG"), QStringLiteral("2")); 1708 } 1709 } 1710 1711 void KConfigTest::testLocalDeletion() 1712 { 1713 // Prepare kdeglobals 1714 { 1715 KConfig glob(QStringLiteral("kdeglobals")); 1716 KConfigGroup general(&glob, QStringLiteral("OwnTestGroup")); 1717 general.writeEntry("GlobalKey", "DontTouchMe"); 1718 QVERIFY(glob.sync()); 1719 } 1720 1721 QStringList expectedKeys{QStringLiteral("LocalKey")}; 1722 expectedKeys.prepend(QStringLiteral("GlobalWrite")); 1723 1724 // Write into kconfigtest, including deleting GlobalKey 1725 { 1726 KConfig mainConfig(s_kconfig_test_subdir); 1727 KConfigGroup mainGroup(&mainConfig, QStringLiteral("OwnTestGroup")); 1728 mainGroup.writeEntry("LocalKey", QStringLiteral("LocalValue")); 1729 mainGroup.writeEntry("GlobalWrite", QStringLiteral("GlobalValue"), KConfig::Persistent | KConfig::Global); // goes to kdeglobals 1730 QCOMPARE(mainGroup.readEntry("GlobalKey"), QStringLiteral("DontTouchMe")); 1731 mainGroup.deleteEntry("GlobalKey"); // local deletion ([$d]), kdeglobals is unchanged 1732 QCOMPARE(mainGroup.readEntry("GlobalKey", "Default"), QStringLiteral("Default")); // key is gone 1733 QCOMPARE(mainGroup.keyList(), expectedKeys); 1734 } 1735 1736 // Check what ended up in kconfigtest 1737 const QList<QByteArray> lines = readLines(); 1738 QVERIFY(lines.contains("[OwnTestGroup]\n")); 1739 QVERIFY(lines.contains("GlobalKey[$d]\n")); 1740 1741 // Check what ended up in kdeglobals 1742 { 1743 KConfig globReadNoGlob(QStringLiteral("kdeglobals"), KConfig::NoGlobals); 1744 const KConfigGroup generalNoGlob(&globReadNoGlob, QStringLiteral("OwnTestGroup")); 1745 QCOMPARE(generalNoGlob.readEntry("GlobalKey"), QStringLiteral("DontTouchMe")); 1746 QCOMPARE(generalNoGlob.readEntry("GlobalWrite"), QStringLiteral("GlobalValue")); 1747 QVERIFY(!generalNoGlob.hasKey("LocalValue")); 1748 QStringList expectedGlobalKeys{QStringLiteral("GlobalKey")}; 1749 expectedGlobalKeys.append(QStringLiteral("GlobalWrite")); 1750 QCOMPARE(generalNoGlob.keyList(), expectedGlobalKeys); 1751 } 1752 1753 // Check what we see when re-reading the config file 1754 { 1755 KConfig mainConfig(s_kconfig_test_subdir); 1756 KConfigGroup mainGroup(&mainConfig, QStringLiteral("OwnTestGroup")); 1757 QTRY_COMPARE_WITH_TIMEOUT(mainGroup.readEntry("GlobalKey", "Default"), QStringLiteral("Default"), 5000); // key is gone 1758 QTRY_COMPARE_WITH_TIMEOUT(mainGroup.keyList(), expectedKeys, 5000); 1759 } 1760 } 1761 1762 void KConfigTest::testAnonymousConfig() 1763 { 1764 KConfig anonConfig(QString(), KConfig::SimpleConfig); 1765 KConfigGroup general(&anonConfig, QStringLiteral("General")); 1766 QCOMPARE(general.readEntry("testKG"), QString()); // no kdeglobals merging 1767 general.writeEntry("Foo", "Bar"); 1768 QCOMPARE(general.readEntry("Foo"), QStringLiteral("Bar")); 1769 } 1770 1771 void KConfigTest::testQByteArrayUtf8() 1772 { 1773 QTemporaryFile file; 1774 QVERIFY(file.open()); 1775 KConfig config(file.fileName(), KConfig::SimpleConfig); 1776 KConfigGroup general(&config, QStringLiteral("General")); 1777 QByteArray bytes(256, '\0'); 1778 for (int i = 0; i < 256; i++) { 1779 bytes[i] = i; 1780 } 1781 general.writeEntry("Utf8", bytes); 1782 config.sync(); 1783 file.flush(); 1784 file.close(); 1785 QFile readFile(file.fileName()); 1786 QVERIFY(readFile.open(QFile::ReadOnly)); 1787 #define VALUE \ 1788 "Utf8=" \ 1789 "\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x" \ 1790 "1f " \ 1791 "!\"#$%&'()*+,-./" \ 1792 "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" \ 1793 "\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\" \ 1794 "x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xb" \ 1795 "b\\xbc\\xbd\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9" \ 1796 "\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\" \ 1797 "xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff" 1798 const QByteArray fileBytes = readFile.readAll(); 1799 #ifndef Q_OS_WIN 1800 QCOMPARE(fileBytes, QByteArrayLiteral("[General]\n" VALUE "\n")); 1801 #else 1802 QCOMPARE(fileBytes, QByteArrayLiteral("[General]\r\n" VALUE "\r\n")); 1803 #endif 1804 #undef VALUE 1805 1806 // check that reading works 1807 KConfig config2(file.fileName(), KConfig::SimpleConfig); 1808 KConfigGroup general2(&config2, QStringLiteral("General")); 1809 QCOMPARE(bytes, general2.readEntry("Utf8", QByteArray())); 1810 } 1811 1812 void KConfigTest::testQStringUtf8_data() 1813 { 1814 QTest::addColumn<QByteArray>("data"); 1815 QTest::newRow("1") << QByteArray("Téléchargements\tTéléchargements"); 1816 QTest::newRow("2") << QByteArray("$¢ह€𐍈\t$¢ह€𐍈"); 1817 QTest::newRow("3") << QByteArray("\xc2\xe0\xa4\xf0\x90\x8d\t\\xc2\\xe0\\xa4\\xf0\\x90\\x8d"); 1818 // 2 byte overlong 1819 QTest::newRow("4") << QByteArray("\xc1\xbf\t\\xc1\\xbf"); 1820 // 3 byte overlong 1821 QTest::newRow("5") << QByteArray("\xe0\x9f\xbf\t\\xe0\\x9f\\xbf"); 1822 // 4 byte overlong 1823 QTest::newRow("6") << QByteArray("\xf0\x8f\xbf\xbf\t\\xf0\\x8f\\xbf\\xbf"); 1824 // outside unicode range 1825 QTest::newRow("7") << QByteArray("\xf4\x90\x80\x80\t\\xf4\\x90\\x80\\x80"); 1826 // just within range 1827 QTest::newRow("8") << QByteArray("\xc2\x80\t\xc2\x80"); 1828 QTest::newRow("9") << QByteArray("\xe0\xa0\x80\t\xe0\xa0\x80"); 1829 QTest::newRow("10") << QByteArray("\xf0\x90\x80\x80\t\xf0\x90\x80\x80"); 1830 QTest::newRow("11") << QByteArray("\xf4\x8f\xbf\xbf\t\xf4\x8f\xbf\xbf"); 1831 } 1832 1833 void KConfigTest::testQStringUtf8() 1834 { 1835 QFETCH(QByteArray, data); 1836 const QList<QByteArray> d = data.split('\t'); 1837 const QByteArray value = d[0]; 1838 const QByteArray serialized = d[1]; 1839 QTemporaryFile file; 1840 QVERIFY(file.open()); 1841 KConfig config(file.fileName(), KConfig::SimpleConfig); 1842 KConfigGroup general(&config, QStringLiteral("General")); 1843 general.writeEntry("key", value); 1844 config.sync(); 1845 file.flush(); 1846 file.close(); 1847 QFile readFile(file.fileName()); 1848 QVERIFY(readFile.open(QFile::ReadOnly)); 1849 QByteArray fileBytes = readFile.readAll(); 1850 #ifdef Q_OS_WIN 1851 fileBytes.replace(QByteArrayLiteral("\r\n"), QByteArrayLiteral("\n")); 1852 #endif 1853 QCOMPARE(fileBytes, QByteArrayLiteral("[General]\nkey=") + serialized + QByteArrayLiteral("\n")); 1854 1855 // check that reading works 1856 KConfig config2(file.fileName(), KConfig::SimpleConfig); 1857 KConfigGroup general2(&config2, QStringLiteral("General")); 1858 QCOMPARE(value, general2.readEntry("key", QByteArray())); 1859 } 1860 1861 void KConfigTest::testNewlines() 1862 { 1863 // test that kconfig always uses the native line endings 1864 QTemporaryFile file; 1865 QVERIFY(file.open()); 1866 KConfig anonConfig(file.fileName(), KConfig::SimpleConfig); 1867 KConfigGroup general(&anonConfig, QStringLiteral("General")); 1868 general.writeEntry("Foo", "Bar"); 1869 general.writeEntry("Bar", "Foo"); 1870 anonConfig.sync(); 1871 file.flush(); 1872 file.close(); 1873 QFile readFile(file.fileName()); 1874 QVERIFY(readFile.open(QFile::ReadOnly)); 1875 #ifndef Q_OS_WIN 1876 QCOMPARE(readFile.readAll(), QByteArrayLiteral("[General]\nBar=Foo\nFoo=Bar\n")); 1877 #else 1878 QCOMPARE(readFile.readAll(), QByteArrayLiteral("[General]\r\nBar=Foo\r\nFoo=Bar\r\n")); 1879 #endif 1880 } 1881 1882 void KConfigTest::testMoveValuesTo() 1883 { 1884 QTemporaryFile file; 1885 QVERIFY(file.open()); 1886 // Prepare kdeglobals 1887 { 1888 KConfig glob(QStringLiteral("kdeglobals")); 1889 KConfigGroup general(&glob, QStringLiteral("TestGroup")); 1890 general.writeEntry("GlobalKey", "PlsDeleteMe"); 1891 QVERIFY(glob.sync()); 1892 } 1893 1894 KConfigGroup grp = KSharedConfig::openConfig(file.fileName())->group(QStringLiteral("TestGroup")); 1895 1896 grp.writeEntry("test1", "first_value"); 1897 grp.writeEntry("test_empty", ""); 1898 grp.writeEntry("other", "other_value"); 1899 grp.writePathEntry("my_path", QStringLiteral("~/somepath")); 1900 // because this key is from the global file it should be explicitly deleted 1901 grp.deleteEntry("GlobalKey"); 1902 1903 QTemporaryFile targetFile; 1904 QVERIFY(targetFile.open()); 1905 targetFile.close(); 1906 KConfigGroup targetGroup = KSharedConfig::openConfig(targetFile.fileName(), KConfig::SimpleConfig)->group(QStringLiteral("MoveToGroup")); 1907 1908 grp.moveValuesTo({"test1", "test_empty", "does_not_exist", "my_path", "GlobalKey"}, targetGroup); 1909 QVERIFY(grp.config()->isDirty()); 1910 QVERIFY(targetGroup.config()->isDirty()); 1911 1912 QCOMPARE(grp.keyList(), QStringList{QStringLiteral("other")}); 1913 QStringList expectedKeyList{QStringLiteral("my_path"), QStringLiteral("test1"), QStringLiteral("test_empty")}; 1914 QCOMPARE(targetGroup.keyList(), expectedKeyList); 1915 QCOMPARE(targetGroup.readEntry("test1"), QStringLiteral("first_value")); 1916 1917 targetGroup.sync(); 1918 QFile targetReadFile(targetFile.fileName()); 1919 targetReadFile.open(QFile::ReadOnly); 1920 QVERIFY(targetReadFile.readAll().contains(QByteArray("my_path[$e]=~/somepath"))); 1921 } 1922 1923 void KConfigTest::testXdgListEntry() 1924 { 1925 QTemporaryFile file; 1926 QVERIFY(file.open()); 1927 QTextStream out(&file); 1928 out << "[General]\n" 1929 << "Key1=\n" // empty list 1930 // emtpty entries 1931 << "Key2=;\n" 1932 << "Key3=;;\n" 1933 << "Key4=;;;\n" 1934 << "Key5=\\;\n" 1935 << "Key6=1;2\\;3;;\n"; 1936 out.flush(); 1937 file.close(); 1938 KConfig anonConfig(file.fileName(), KConfig::SimpleConfig); 1939 KConfigGroup grp = anonConfig.group(QStringLiteral("General")); 1940 QStringList invalidList; // use this as a default when an empty list is expected 1941 invalidList << QStringLiteral("Error! Default value read!"); 1942 QCOMPARE(grp.readXdgListEntry("Key1", invalidList), (QStringList{})); 1943 QCOMPARE(grp.readXdgListEntry("Key2", invalidList), (QStringList{QString{}})); 1944 QCOMPARE(grp.readXdgListEntry("Key3", invalidList), (QStringList{QString{}, QString{}})); 1945 QCOMPARE(grp.readXdgListEntry("Key4", invalidList), (QStringList{QString{}, QString{}, QString{}})); 1946 QCOMPARE(grp.readXdgListEntry("Key5", invalidList), (QStringList{QStringLiteral(";")})); 1947 QCOMPARE(grp.readXdgListEntry("Key6", invalidList), (QStringList{QStringLiteral("1"), QStringLiteral("2;3"), QString{}})); 1948 } 1949 1950 #include <QThreadPool> 1951 #include <qtconcurrentrun.h> 1952 1953 // To find multithreading bugs: valgrind --tool=helgrind --track-lockorders=no ./kconfigtest testThreads 1954 void KConfigTest::testThreads() 1955 { 1956 QThreadPool::globalInstance()->setMaxThreadCount(6); 1957 // Run in parallel some tests that work on different config files, 1958 // otherwise unexpected things might indeed happen. 1959 const QList<QFuture<void>> futures = { 1960 QtConcurrent::run(&KConfigTest::testAddConfigSources, this), 1961 QtConcurrent::run(&KConfigTest::testSimple, this), 1962 QtConcurrent::run(&KConfigTest::testDefaults, this), 1963 QtConcurrent::run(&KConfigTest::testSharedConfig, this), 1964 QtConcurrent::run(&KConfigTest::testSharedConfig, this), 1965 }; 1966 1967 // QEXPECT_FAIL triggers race conditions, it should be fixed to use QThreadStorage... 1968 // futures << QtConcurrent::run(this, &KConfigTest::testDeleteWhenLocalized); 1969 // futures << QtConcurrent::run(this, &KConfigTest::testEntryMap); 1970 for (QFuture<void> f : futures) { 1971 f.waitForFinished(); 1972 } 1973 } 1974 1975 void KConfigTest::testNotify() 1976 { 1977 #if !KCONFIG_USE_DBUS 1978 QSKIP("KConfig notification requires DBus"); 1979 #endif 1980 1981 KConfig config(s_kconfig_test_subdir); 1982 auto myConfigGroup = KConfigGroup(&config, QStringLiteral("TopLevelGroup")); 1983 1984 // mimics a config in another process, which is watching for events 1985 auto remoteConfig = KSharedConfig::openConfig(s_kconfig_test_subdir); 1986 KConfigWatcher::Ptr watcher = KConfigWatcher::create(remoteConfig); 1987 1988 // some random config that shouldn't be changing when kconfigtest changes, only on kdeglobals 1989 auto otherRemoteConfig = KSharedConfig::openConfig(s_test_subdir + QLatin1String("kconfigtest2")); 1990 KConfigWatcher::Ptr otherWatcher = KConfigWatcher::create(otherRemoteConfig); 1991 1992 QSignalSpy watcherSpy(watcher.data(), &KConfigWatcher::configChanged); 1993 QSignalSpy otherWatcherSpy(otherWatcher.data(), &KConfigWatcher::configChanged); 1994 1995 // write entries in a group and subgroup 1996 myConfigGroup.writeEntry("entryA", "foo", KConfig::Persistent | KConfig::Notify); 1997 auto subGroup = myConfigGroup.group(QStringLiteral("aSubGroup")); 1998 subGroup.writeEntry("entry1", "foo", KConfig::Persistent | KConfig::Notify); 1999 subGroup.writeEntry("entry2", "foo", KConfig::Persistent | KConfig::Notify); 2000 config.sync(); 2001 watcherSpy.wait(); 2002 QCOMPARE(watcherSpy.count(), 2); 2003 2004 std::sort(watcherSpy.begin(), watcherSpy.end(), [](QList<QVariant> a, QList<QVariant> b) { 2005 return a[0].value<KConfigGroup>().name() < b[0].value<KConfigGroup>().name(); 2006 }); 2007 2008 QCOMPARE(watcherSpy[0][0].value<KConfigGroup>().name(), QStringLiteral("TopLevelGroup")); 2009 QCOMPARE(watcherSpy[0][1].value<QByteArrayList>(), QByteArrayList({"entryA"})); 2010 2011 QCOMPARE(watcherSpy[1][0].value<KConfigGroup>().name(), QStringLiteral("aSubGroup")); 2012 QCOMPARE(watcherSpy[1][0].value<KConfigGroup>().parent().name(), QStringLiteral("TopLevelGroup")); 2013 QCOMPARE(watcherSpy[1][1].value<QByteArrayList>(), QByteArrayList({"entry1", "entry2"})); 2014 2015 // delete an entry 2016 watcherSpy.clear(); 2017 myConfigGroup.deleteEntry("entryA", KConfig::Persistent | KConfig::Notify); 2018 config.sync(); 2019 watcherSpy.wait(); 2020 QCOMPARE(watcherSpy.count(), 1); 2021 QCOMPARE(watcherSpy[0][0].value<KConfigGroup>().name(), QStringLiteral("TopLevelGroup")); 2022 QCOMPARE(watcherSpy[0][1].value<QByteArrayList>(), QByteArrayList({"entryA"})); 2023 2024 // revert to default an entry 2025 watcherSpy.clear(); 2026 myConfigGroup.revertToDefault("entryA", KConfig::Persistent | KConfig::Notify); 2027 config.sync(); 2028 watcherSpy.wait(); 2029 QCOMPARE(watcherSpy.count(), 1); 2030 QCOMPARE(watcherSpy[0][0].value<KConfigGroup>().name(), QStringLiteral("TopLevelGroup")); 2031 QCOMPARE(watcherSpy[0][1].value<QByteArrayList>(), QByteArrayList({"entryA"})); 2032 2033 // deleting a group, should notify that every entry in that group has changed 2034 watcherSpy.clear(); 2035 myConfigGroup.deleteGroup(QStringLiteral("aSubGroup"), KConfig::Persistent | KConfig::Notify); 2036 config.sync(); 2037 watcherSpy.wait(); 2038 QCOMPARE(watcherSpy.count(), 1); 2039 QCOMPARE(watcherSpy[0][0].value<KConfigGroup>().name(), QStringLiteral("aSubGroup")); 2040 QCOMPARE(watcherSpy[0][1].value<QByteArrayList>(), QByteArrayList({"entry1", "entry2"})); 2041 2042 // global write still triggers our notification 2043 watcherSpy.clear(); 2044 myConfigGroup.writeEntry("someGlobalEntry", "foo", KConfig::Persistent | KConfig::Notify | KConfig::Global); 2045 config.sync(); 2046 watcherSpy.wait(); 2047 QCOMPARE(watcherSpy.count(), 1); 2048 QCOMPARE(watcherSpy[0][0].value<KConfigGroup>().name(), QStringLiteral("TopLevelGroup")); 2049 QCOMPARE(watcherSpy[0][1].value<QByteArrayList>(), QByteArrayList({"someGlobalEntry"})); 2050 2051 // watching another file should have only triggered from the kdeglobals change 2052 QCOMPARE(otherWatcherSpy.count(), 1); 2053 QCOMPARE(otherWatcherSpy[0][0].value<KConfigGroup>().name(), QStringLiteral("TopLevelGroup")); 2054 QCOMPARE(otherWatcherSpy[0][1].value<QByteArrayList>(), QByteArrayList({"someGlobalEntry"})); 2055 } 2056 2057 void KConfigTest::testNotifyIllegalObjectPath() 2058 { 2059 #if !KCONFIG_USE_DBUS 2060 QSKIP("KConfig notification requires DBus"); 2061 #endif 2062 2063 KConfig config(s_kconfig_test_illegal_object_path); 2064 auto myConfigGroup = KConfigGroup(&config, QStringLiteral("General")); 2065 2066 // mimics a config in another process, which is watching for events 2067 auto remoteConfig = KSharedConfig::openConfig(s_kconfig_test_illegal_object_path); 2068 KConfigWatcher::Ptr watcher = KConfigWatcher::create(remoteConfig); 2069 2070 QSignalSpy watcherSpy(watcher.data(), &KConfigWatcher::configChanged); 2071 2072 // write entries in a group and subgroup 2073 myConfigGroup.writeEntry("entryA", "foo", KConfig::Persistent | KConfig::Notify); 2074 config.sync(); 2075 watcherSpy.wait(); 2076 QCOMPARE(watcherSpy.size(), 1); 2077 } 2078 2079 void KConfigTest::testKAuthorizeEnums() 2080 { 2081 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 2082 KConfigGroup actionRestrictions = config->group(QStringLiteral("KDE Action Restrictions")); 2083 actionRestrictions.writeEntry("shell_access", false); 2084 actionRestrictions.writeEntry("action/open_with", false); 2085 2086 QVERIFY(!KAuthorized::authorize(KAuthorized::SHELL_ACCESS)); 2087 QVERIFY(!KAuthorized::authorizeAction(KAuthorized::OPEN_WITH)); 2088 actionRestrictions.deleteGroup(); 2089 2090 QVERIFY(!KAuthorized::authorize((KAuthorized::GenericRestriction)0)); 2091 QVERIFY(!KAuthorized::authorizeAction((KAuthorized::GenericAction)0)); 2092 } 2093 2094 void KConfigTest::testKdeglobalsVsDefault() 2095 { 2096 // Add testRestore key with global value in kdeglobals 2097 KConfig glob(QStringLiteral("kdeglobals")); 2098 KConfigGroup generalGlob(&glob, QStringLiteral("General")); 2099 generalGlob.writeEntry("testRestore", "global"); 2100 QVERIFY(glob.sync()); 2101 2102 KConfig local(s_test_subdir + QLatin1String("restorerc")); 2103 KConfigGroup generalLocal(&local, QStringLiteral("General")); 2104 // Check if we get global and not the default value from cpp (defaultcpp) when reading data from restorerc 2105 QCOMPARE(generalLocal.readEntry("testRestore", "defaultcpp"), QStringLiteral("global")); 2106 2107 // Add test restore key with restore value in restorerc file 2108 generalLocal.writeEntry("testRestore", "restore"); 2109 QVERIFY(local.sync()); 2110 local.reparseConfiguration(); 2111 // We expect to get the value from restorerc file 2112 QCOMPARE(generalLocal.readEntry("testRestore", "defaultcpp"), QStringLiteral("restore")); 2113 2114 // Revert to default testRestore key and we expect to get default value and not the global one 2115 generalLocal.revertToDefault("testRestore"); 2116 local.sync(); 2117 local.reparseConfiguration(); 2118 QCOMPARE(generalLocal.readEntry("testRestore", "defaultcpp"), QStringLiteral("defaultcpp")); 2119 } 2120 2121 #include "moc_kconfigtest.cpp"