/* This file is part of the KDE libraries Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kconfigtest.h" #include #include #include #include #include "moc_kconfigtest.cpp" #include #include #include #include #ifdef Q_OS_UNIX #include #endif KCONFIGGROUP_DECLARE_ENUM_QOBJECT(KConfigTest,Testing) KCONFIGGROUP_DECLARE_FLAGS_QOBJECT(KConfigTest,Flags) QTEST_KDEMAIN_CORE( KConfigTest ) #define BOOLENTRY1 true #define BOOLENTRY2 false #define STRINGENTRY1 "hello" #define STRINGENTRY2 " hello" #define STRINGENTRY3 "hello " #define STRINGENTRY4 " hello " #define STRINGENTRY5 " " #define STRINGENTRY6 "" #define UTF8BITENTRY "Hello äöü" #define TRANSLATEDSTRINGENTRY1 "bonjour" #define BYTEARRAYENTRY QByteArray( "\x00\xff\x7f\x3c abc\x00\x00", 10 ) #define ESCAPEKEY " []\0017[]==]" #define ESCAPEENTRY "[]\170[]]=3=]\\] " #define DOUBLEENTRY 123456.78912345 #define FLOATENTRY 123.567f #define POINTENTRY QPoint( 4351, 1235 ) #define SIZEENTRY QSize( 10, 20 ) #define RECTENTRY QRect( 10, 23, 5321, 13 ) #define DATETIMEENTRY QDateTime( QDate( 2002, 06, 23 ), QTime( 12, 55, 40 ) ) #define STRINGLISTENTRY (QStringList( "Hello," ) << " World") #define STRINGLISTEMPTYENTRY QStringList() #define STRINGLISTJUSTEMPTYELEMENT QStringList(QString()) #define STRINGLISTEMPTYTRAINLINGELEMENT (QStringList( "Hi" ) << QString()) #define STRINGLISTESCAPEODDENTRY (QStringList( "Hello\\\\\\" ) << "World") #define STRINGLISTESCAPEEVENENTRY (QStringList( "Hello\\\\\\\\" ) << "World") #define STRINGLISTESCAPECOMMAENTRY (QStringList( "Hel\\\\\\,\\\\,\\,\\\\\\\\,lo" ) << "World") #define INTLISTENTRY1 QList() << 1 << 2 << 3 << 4 #define BYTEARRAYLISTENTRY1 QList() << "" << "1,2" << "end" #define VARIANTLISTENTRY (QVariantList() << true << false << QString("joe") << 10023) #define VARIANTLISTENTRY2 (QVariantList() << POINTENTRY << SIZEENTRY) #define HOMEPATH QString(QDir::homePath()+"/foo") #define HOMEPATHESCAPE QString(QDir::homePath()+"/foo/$HOME") #define DOLLARGROUP "$i" void KConfigTest::initTestCase() { // Use a different directory for the config files created by this test, // so that cleanupTestCase doesn't delete kdebugrc // This makes the save location for the config resource, "$HOME/.kde-unit-test/kconfigtest/" KGlobal::dirs()->addResourceType("config", 0, "kconfigtest"); // to make sure all files from a previous failed run are deleted cleanupTestCase(); KConfig sc( "kconfigtest" ); KConfigGroup cg(&sc, "AAA"); cg.writeEntry("stringEntry1", STRINGENTRY1, KConfig::Persistent|KConfig::Global); cg.deleteEntry("stringEntry2", KConfig::Global); cg = KConfigGroup(&sc, "Hello"); cg.writeEntry( "boolEntry1", BOOLENTRY1 ); cg.writeEntry( "boolEntry2", BOOLENTRY2 ); QByteArray data( UTF8BITENTRY ); QCOMPARE( data.size(), 12 ); // the source file is in utf8 QCOMPARE( QString::fromUtf8(data).length(), 9 ); cg.writeEntry ("Test", data); cg.writeEntry( "bytearrayEntry", BYTEARRAYENTRY ); cg.writeEntry( ESCAPEKEY, ESCAPEENTRY ); cg.writeEntry( "emptyEntry", ""); cg.writeEntry( "stringEntry1", STRINGENTRY1 ); cg.writeEntry( "stringEntry2", STRINGENTRY2 ); cg.writeEntry( "stringEntry3", STRINGENTRY3 ); cg.writeEntry( "stringEntry4", STRINGENTRY4 ); cg.writeEntry( "stringEntry5", STRINGENTRY5 ); cg.writeEntry( "keywith=equalsign", STRINGENTRY1 ); cg.deleteEntry( "stringEntry5" ); cg.deleteEntry( "stringEntry6" ); // deleting a nonexistent entry cg.writeEntry( "byteArrayEntry1", QByteArray( STRINGENTRY1 ), KConfig::Global|KConfig::Persistent ); cg.writeEntry( "doubleEntry1", DOUBLEENTRY ); cg.writeEntry( "floatEntry1", FLOATENTRY ); sc.deleteGroup("deleteMe"); // deleting a nonexistent group cg = KConfigGroup(&sc, "Complex Types"); cg.writeEntry( "rectEntry", RECTENTRY ); cg.writeEntry( "pointEntry", POINTENTRY ); cg.writeEntry( "sizeEntry", SIZEENTRY ); cg.writeEntry( "dateTimeEntry", DATETIMEENTRY ); cg.writeEntry( "dateEntry", DATETIMEENTRY.date() ); KConfigGroup ct = cg; cg = KConfigGroup(&ct, "Nested Group 1"); cg.writeEntry("stringentry1", STRINGENTRY1); cg = KConfigGroup(&ct, "Nested Group 2"); cg.writeEntry( "stringEntry2", STRINGENTRY2 ); cg = KConfigGroup(&cg, "Nested Group 2.1"); cg.writeEntry( "stringEntry3", STRINGENTRY3 ); cg = KConfigGroup(&ct, "Nested Group 3"); cg.writeEntry( "stringEntry3", STRINGENTRY3 ); cg = KConfigGroup(&sc, "List Types" ); cg.writeEntry( "listOfIntsEntry1", INTLISTENTRY1 ); cg.writeEntry( "listOfByteArraysEntry1", BYTEARRAYLISTENTRY1 ); cg.writeEntry( "stringListEntry", STRINGLISTENTRY ); cg.writeEntry( "stringListEmptyEntry", STRINGLISTEMPTYENTRY ); cg.writeEntry( "stringListJustEmptyElement", STRINGLISTJUSTEMPTYELEMENT ); cg.writeEntry( "stringListEmptyTrailingElement", STRINGLISTEMPTYTRAINLINGELEMENT ); cg.writeEntry( "stringListEscapeOddEntry", STRINGLISTESCAPEODDENTRY ); cg.writeEntry( "stringListEscapeEvenEntry", STRINGLISTESCAPEEVENENTRY ); cg.writeEntry( "stringListEscapeCommaEntry", STRINGLISTESCAPECOMMAENTRY ); cg.writeEntry( "variantListEntry", VARIANTLISTENTRY ); cg = KConfigGroup(&sc, "Path Type" ); cg.writePathEntry( "homepath", HOMEPATH ); cg.writePathEntry( "homepathescape", HOMEPATHESCAPE ); cg = KConfigGroup(&sc, "Enum Types" ); writeEntry( cg, "enum-10", Tens ); writeEntry( cg, "enum-100", Hundreds ); writeEntry( cg, "flags-bit0", Flags(bit0)); writeEntry( cg, "flags-bit0-bit1", Flags(bit0|bit1) ); cg = KConfigGroup(&sc, "ParentGroup" ); KConfigGroup cg1(&cg, "SubGroup1" ); cg1.writeEntry( "somestring", "somevalue" ); cg.writeEntry( "parentgrpstring", "somevalue" ); KConfigGroup cg2(&cg, "SubGroup2" ); cg2.writeEntry( "substring", "somevalue" ); KConfigGroup cg3(&cg, "SubGroup/3"); cg3.writeEntry( "sub3string", "somevalue" ); QVERIFY(sc.isDirty()); sc.sync(); QVERIFY(!sc.isDirty()); KConfig sc1("kdebugrc", KConfig::SimpleConfig); KConfigGroup sg0(&sc1, "0"); sg0.writeEntry("AbortFatal", false); sg0.writeEntry("WarnOutput", 0); sg0.writeEntry("FatalOutput", 0); sc1.sync(); //Setup stuff to test KConfig::addConfigSources() KConfig devcfg("specificrc"); KConfigGroup devonlygrp(&devcfg, "Specific Only Group"); devonlygrp.writeEntry("ExistingEntry", "DevValue"); KConfigGroup devandbasegrp(&devcfg, "Shared Group"); devandbasegrp.writeEntry("SomeSharedEntry", "DevValue"); devandbasegrp.writeEntry("SomeSpecificOnlyEntry", "DevValue"); devcfg.sync(); KConfig basecfg("baserc"); KConfigGroup basegrp(&basecfg, "Base Only Group"); basegrp.writeEntry("ExistingEntry", "BaseValue"); KConfigGroup baseanddevgrp(&basecfg, "Shared Group"); baseanddevgrp.writeEntry("SomeSharedEntry", "BaseValue"); baseanddevgrp.writeEntry("SomeBaseOnlyEntry", "BaseValue"); basecfg.sync(); KConfig gecfg("groupescapetest", KConfig::SimpleConfig); cg = KConfigGroup(&gecfg, DOLLARGROUP); cg.writeEntry( "entry", "doesntmatter" ); } void KConfigTest::cleanupTestCase() { const QString localConfig = KGlobal::dirs()->saveLocation("config"); KTempDir::removeDir(localConfig); QVERIFY(!QFile::exists(localConfig)); } static QList readLinesFrom(const QString& path) { QFile file(path); const bool opened = file.open(QIODevice::ReadOnly|QIODevice::Text); Q_ASSERT(opened); Q_UNUSED(opened); QList lines; QByteArray line; do { line = file.readLine(); if (!line.isEmpty()) lines.append(line); } while(!line.isEmpty()); return lines; } static QList readLines(const char* fileName = "kconfigtest") { const QString path = KStandardDirs::locateLocal("config", fileName); Q_ASSERT(!path.isEmpty()); return readLinesFrom(path); } // see also testDefaults, which tests reverting with a defaults (global) file available void KConfigTest::testDirtyAfterRevert() { KConfig sc( "kconfigtest_revert" ); KConfigGroup cg(&sc, "Hello"); cg.revertToDefault( "does_not_exist" ); QVERIFY(!sc.isDirty()); cg.writeEntry("Test", "Correct"); QVERIFY(sc.isDirty()); sc.sync(); QVERIFY(!sc.isDirty()); cg.revertToDefault("Test"); QVERIFY(sc.isDirty()); sc.sync(); QVERIFY(!sc.isDirty()); cg.revertToDefault("Test"); QVERIFY(!sc.isDirty()); } void KConfigTest::testRevertAllEntries() { // this tests the case were we revert (delete) all entries in a file, // leaving a blank file { KConfig sc( "konfigtest2", KConfig::SimpleConfig ); KConfigGroup cg( &sc, "Hello" ); cg.writeEntry( "Test", "Correct" ); } { KConfig sc( "konfigtest2", KConfig::SimpleConfig ); KConfigGroup cg( &sc, "Hello" ); QCOMPARE( cg.readEntry( "Test", "Default" ), QString("Correct") ); cg.revertToDefault( "Test" ); } KConfig sc( "konfigtest2", KConfig::SimpleConfig ); KConfigGroup cg( &sc, "Hello" ); QCOMPARE( cg.readEntry( "Test", "Default" ), QString("Default") ); } void KConfigTest::testSimple() { KConfig sc2( "kconfigtest" ); QCOMPARE(sc2.name(), QString("kconfigtest")); // make sure groupList() isn't returning something it shouldn't foreach(const QString& group, sc2.groupList()) { QVERIFY(!group.isEmpty() && group != ""); QVERIFY(!group.contains(QChar(0x1d))); } KConfigGroup sc3( &sc2, "AAA"); QVERIFY( sc3.hasKey( "stringEntry1" ) ); QVERIFY( !sc3.isEntryImmutable("stringEntry1") ); QCOMPARE( sc3.readEntry( "stringEntry1" ), QString( STRINGENTRY1 ) ); QVERIFY( !sc3.hasKey( "stringEntry2" ) ); QCOMPARE( sc3.readEntry( "stringEntry2", QString("bla") ), QString( "bla" ) ); QVERIFY( !sc3.hasDefault( "stringEntry1" ) ); sc3 = KConfigGroup(&sc2, "Hello"); QCOMPARE( sc3.readEntry( "Test", QByteArray() ), QByteArray( UTF8BITENTRY ) ); QCOMPARE( sc3.readEntry( "bytearrayEntry", QByteArray() ), BYTEARRAYENTRY ); QCOMPARE( sc3.readEntry( ESCAPEKEY ), QString( ESCAPEENTRY ) ); QCOMPARE( sc3.readEntry( "Test", QString() ), QString::fromUtf8( UTF8BITENTRY ) ); QCOMPARE( sc3.readEntry( "emptyEntry"/*, QString("Fietsbel")*/), QString("") ); QCOMPARE( sc3.readEntry("emptyEntry", QString("Fietsbel")).isEmpty(), true ); QCOMPARE( sc3.readEntry( "stringEntry1" ), QString( STRINGENTRY1 ) ); QCOMPARE( sc3.readEntry( "stringEntry2" ), QString( STRINGENTRY2 ) ); QCOMPARE( sc3.readEntry( "stringEntry3" ), QString( STRINGENTRY3 ) ); QCOMPARE( sc3.readEntry( "stringEntry4" ), QString( STRINGENTRY4 ) ); QVERIFY( !sc3.hasKey( "stringEntry5" ) ); QCOMPARE( sc3.readEntry( "stringEntry5", QString("test") ), QString( "test" ) ); QVERIFY( !sc3.hasKey( "stringEntry6" ) ); QCOMPARE( sc3.readEntry( "stringEntry6", QString("foo") ), QString( "foo" ) ); QCOMPARE( sc3.readEntry( "boolEntry1", BOOLENTRY1 ), BOOLENTRY1 ); QCOMPARE( sc3.readEntry( "boolEntry2", false ), BOOLENTRY2 ); QCOMPARE( sc3.readEntry("keywith=equalsign", QString("wrong")), QString(STRINGENTRY1)); QCOMPARE( sc3.readEntry( "byteArrayEntry1", QByteArray() ), QByteArray( STRINGENTRY1 ) ); QCOMPARE( sc3.readEntry( "doubleEntry1", 0.0 ), DOUBLEENTRY ); QCOMPARE( sc3.readEntry( "floatEntry1", 0.0f ), FLOATENTRY ); } void KConfigTest::testDefaults() { KConfig config("defaulttest", KConfig::NoGlobals); const QString defaultsFile = "defaulttest.defaults"; KConfig defaults(defaultsFile, KConfig::SimpleConfig); const QString Default("Default"); const QString NotDefault("Not Default"); const QString Value1(STRINGENTRY1); const QString Value2(STRINGENTRY2); KConfigGroup group = defaults.group("any group"); group.writeEntry("entry1", Default); group.sync(); group = config.group("any group"); group.writeEntry("entry1", Value1); group.writeEntry("entry2", Value2); group.sync(); config.addConfigSources(QStringList() << KStandardDirs::locateLocal("config", defaultsFile)); config.setReadDefaults(true); QCOMPARE(group.readEntry("entry1", QString()), Default); QCOMPARE(group.readEntry("entry2", NotDefault), NotDefault); // no default for entry2 config.setReadDefaults(false); QCOMPARE(group.readEntry("entry1", Default), Value1); QCOMPARE(group.readEntry("entry2", NotDefault), Value2); group.revertToDefault("entry1"); QCOMPARE(group.readEntry("entry1", QString()), Default); group.revertToDefault("entry2"); QCOMPARE(group.readEntry("entry2", QString()), QString()); // TODO test reverting localized entries Q_ASSERT(config.isDirty()); group.sync(); // Check that everything is OK on disk, too KConfig reader("defaulttest", KConfig::NoGlobals); reader.addConfigSources(QStringList() << KStandardDirs::locateLocal("config", defaultsFile)); KConfigGroup readerGroup = reader.group("any group"); QCOMPARE(readerGroup.readEntry("entry1", QString()), Default); QCOMPARE(readerGroup.readEntry("entry2", QString()), QString()); } void KConfigTest::testLocale() { KConfig config("kconfigtest.locales", KConfig::SimpleConfig); const QString Translated(TRANSLATEDSTRINGENTRY1); const QString Untranslated(STRINGENTRY1); KConfigGroup group = config.group("Hello"); group.writeEntry("stringEntry1", Untranslated); config.setLocale("fr"); group.writeEntry("stringEntry1", Translated, KConfig::Localized|KConfig::Persistent); config.sync(); QCOMPARE(group.readEntry("stringEntry1", QString()), Translated); QCOMPARE(group.readEntryUntranslated("stringEntry1"), Untranslated); config.setLocale("C"); // strings written in the "C" locale are written as nonlocalized group.writeEntry("stringEntry1", Untranslated, KConfig::Localized|KConfig::Persistent); config.sync(); QCOMPARE(group.readEntry("stringEntry1", QString()), Untranslated); } void KConfigTest::testEncoding() { QString groupstr = QString::fromUtf8("UTF-8:\xc3\xb6l"); KConfig c( "kconfigtestencodings" ); KConfigGroup cg(&c, groupstr); cg.writeEntry("key", "value"); c.sync(); QList lines = readLines("kconfigtestencodings"); QCOMPARE(lines.count(), 2); QCOMPARE(lines.first(), QByteArray("[UTF-8:\xc3\xb6l]\n")); KConfig c2( "kconfigtestencodings" ); KConfigGroup cg2(&c2, groupstr); QVERIFY(cg2.readEntry("key") == QByteArray("value")); QVERIFY(c2.groupList().contains(groupstr)); } void KConfigTest::testLists() { KConfig sc2( "kconfigtest" ); KConfigGroup sc3(&sc2, "List Types"); QCOMPARE( sc3.readEntry( QString("stringListEntry"), QStringList()), STRINGLISTENTRY ); QCOMPARE( sc3.readEntry( QString("stringListEmptyEntry"), QStringList("wrong") ), STRINGLISTEMPTYENTRY ); QCOMPARE( sc3.readEntry( QString("stringListJustEmptyElement"), QStringList() ), STRINGLISTJUSTEMPTYELEMENT ); QCOMPARE( sc3.readEntry( QString("stringListEmptyTrailingElement"), QStringList() ), STRINGLISTEMPTYTRAINLINGELEMENT ); QCOMPARE( sc3.readEntry( QString("stringListEscapeOddEntry"), QStringList()), STRINGLISTESCAPEODDENTRY ); QCOMPARE( sc3.readEntry( QString("stringListEscapeEvenEntry"), QStringList()), STRINGLISTESCAPEEVENENTRY ); QCOMPARE( sc3.readEntry( QString("stringListEscapeCommaEntry"), QStringList()), STRINGLISTESCAPECOMMAENTRY ); QCOMPARE( sc3.readEntry( "listOfIntsEntry1" ), QString::fromLatin1( "1,2,3,4" ) ); QList expectedIntList = INTLISTENTRY1; QVERIFY( sc3.readEntry( "listOfIntsEntry1", QList() ) == expectedIntList ); QCOMPARE( QVariant(sc3.readEntry( "variantListEntry", VARIANTLISTENTRY )).toStringList(), QVariant(VARIANTLISTENTRY).toStringList() ); QCOMPARE( sc3.readEntry( "listOfByteArraysEntry1", QList()), BYTEARRAYLISTENTRY1 ); } void KConfigTest::testPath() { KConfig sc2( "kconfigtest" ); KConfigGroup sc3(&sc2, "Path Type"); QCOMPARE( sc3.readPathEntry( "homepath", QString() ), HOMEPATH ); QCOMPARE( sc3.readPathEntry( "homepathescape", QString() ), HOMEPATHESCAPE ); QCOMPARE( sc3.entryMap()["homepath"], HOMEPATH ); { QFile file(KStandardDirs::locateLocal("config", "pathtest")); file.open(QIODevice::WriteOnly|QIODevice::Text); QTextStream out(&file); out.setCodec("UTF-8"); out << "[Test Group]" << endl << "homePath=$HOME/foo" << endl << "homePath2=file://$HOME/foo" << endl << "withBraces[$e]=file://${HOME}/foo" << endl << "URL[$e]=file://${HOME}/foo" << endl << "hostname[$e]=$(hostname)" << endl << "noeol=foo"; // no EOL } KConfig cf2("pathtest"); KConfigGroup group = cf2.group("Test Group"); QVERIFY(group.hasKey("homePath")); QCOMPARE(group.readPathEntry("homePath", QString()), HOMEPATH); QVERIFY(group.hasKey("homePath2")); QCOMPARE(group.readPathEntry("homePath2", QString()), QString("file://" + HOMEPATH) ); QVERIFY(group.hasKey("withBraces")); QCOMPARE(group.readPathEntry("withBraces", QString()), QString("file://" + HOMEPATH) ); QVERIFY(group.hasKey("URL")); QCOMPARE(group.readEntry("URL", QString()), QString("file://" + HOMEPATH) ); #if !defined(Q_OS_WIN32) && !defined(Q_OS_MAC) // I don't know if this will work on windows // This test hangs on OS X QVERIFY(group.hasKey("hostname")); QCOMPARE(group.readEntry("hostname", QString()), QHostInfo::localHostName()); #endif QVERIFY(group.hasKey("noeol")); QCOMPARE(group.readEntry("noeol", QString()), QString("foo")); } void KConfigTest::testPersistenceOfExpandFlagForPath() { // This test checks that a path entry starting with $HOME is still flagged // with the expand flag after the config was altered without rewriting the // path entry. // 1st step: Open the config, add a new dummy entry and then sync the config // back to the storage. { KConfig sc2( "kconfigtest" ); KConfigGroup sc3(&sc2, "Path Type"); sc3.writeEntry( "dummy", "dummy" ); sc2.sync(); } // 2nd step: Call testPath() again. Rewriting the config must not break // the testPath() test. testPath(); } void KConfigTest::testComplex() { KConfig sc2( "kconfigtest" ); KConfigGroup sc3(&sc2, "Complex Types"); QCOMPARE( sc3.readEntry( "pointEntry", QPoint() ), POINTENTRY ); QCOMPARE( sc3.readEntry( "sizeEntry", SIZEENTRY ), SIZEENTRY); QCOMPARE( sc3.readEntry( "rectEntry", QRect(1,2,3,4) ), RECTENTRY ); QCOMPARE( sc3.readEntry( "dateTimeEntry", QDateTime() ).toString(Qt::ISODate), DATETIMEENTRY.toString(Qt::ISODate) ); QCOMPARE( sc3.readEntry( "dateEntry", QDate() ).toString(Qt::ISODate), DATETIMEENTRY.date().toString(Qt::ISODate) ); QCOMPARE( sc3.readEntry( "dateTimeEntry", QDate() ), DATETIMEENTRY.date() ); } void KConfigTest::testEnums() { KConfig sc("kconfigtest"); KConfigGroup sc3(&sc, "Enum Types" ); QCOMPARE( sc3.readEntry( "enum-10" ), QString("Tens")); QVERIFY( readEntry( sc3, "enum-100", Ones) != Ones); QVERIFY( readEntry( sc3, "enum-100", Ones) != Tens); QCOMPARE( sc3.readEntry( "flags-bit0" ), QString("bit0")); QVERIFY( readEntry( sc3, "flags-bit0", Flags() ) == bit0 ); int eid = staticMetaObject.indexOfEnumerator( "Flags" ); QVERIFY( eid != -1 ); QMetaEnum me = staticMetaObject.enumerator( eid ); Flags bitfield = bit0|bit1; QCOMPARE( sc3.readEntry( "flags-bit0-bit1" ), QString( me.valueToKeys(bitfield) ) ); QVERIFY( readEntry( sc3, "flags-bit0-bit1", Flags() ) == bitfield ); } void KConfigTest::testEntryMap() { KConfig sc("kconfigtest"); KConfigGroup cg(&sc, "Hello"); QMap entryMap = cg.entryMap(); qDebug() << entryMap.keys(); QCOMPARE(entryMap.value("stringEntry1"), QString(STRINGENTRY1)); QCOMPARE(entryMap.value("stringEntry2"), QString(STRINGENTRY2)); QCOMPARE(entryMap.value("stringEntry3"), QString(STRINGENTRY3)); QCOMPARE(entryMap.value("stringEntry4"), QString(STRINGENTRY4)); QVERIFY(!entryMap.contains("stringEntry5")); QVERIFY(!entryMap.contains("stringEntry6")); QCOMPARE(entryMap.value("Test"), QString::fromUtf8(UTF8BITENTRY)); QCOMPARE(entryMap.value("bytearrayEntry"), QString::fromUtf8(BYTEARRAYENTRY)); QCOMPARE(entryMap.value("emptyEntry"), QString()); QVERIFY(entryMap.contains("emptyEntry")); QCOMPARE(entryMap.value("boolEntry1"), QString(BOOLENTRY1?"true":"false")); QCOMPARE(entryMap.value("boolEntry2"), QString(BOOLENTRY2?"true":"false")); QCOMPARE(entryMap.value("keywith=equalsign"), QString(STRINGENTRY1)); QCOMPARE(entryMap.value("byteArrayEntry1"), QString(STRINGENTRY1)); QCOMPARE(entryMap.value("doubleEntry1"), QString::number(DOUBLEENTRY, 'g', 15)); QCOMPARE(entryMap.value("floatEntry1"), QString::number(FLOATENTRY, 'g', 8)); } void KConfigTest::testInvalid() { KConfig sc( "kconfigtest" ); // all of these should print a message to the kdebug.dbg file KConfigGroup sc3(&sc, "Invalid Types" ); sc3.writeEntry( "badList", VARIANTLISTENTRY2 ); QList list; // 1 element list list << 1; sc3.writeEntry( QString("badList"), list); QVERIFY( sc3.readEntry( "badList", QPoint() ) == QPoint() ); QVERIFY( sc3.readEntry( "badList", QRect() ) == QRect() ); QVERIFY( sc3.readEntry( "badList", QSize() ) == QSize() ); QVERIFY( sc3.readEntry( "badList", QDate() ) == QDate() ); QVERIFY( sc3.readEntry( "badList", QDateTime() ) == QDateTime() ); // 2 element list list << 2; sc3.writeEntry( "badList", list); QVERIFY( sc3.readEntry( "badList", QRect() ) == QRect() ); QVERIFY( sc3.readEntry( "badList", QDate() ) == QDate() ); QVERIFY( sc3.readEntry( "badList", QDateTime() ) == QDateTime() ); // 3 element list list << 303; sc3.writeEntry( "badList", list); QVERIFY( sc3.readEntry( "badList", QPoint() ) == QPoint() ); QVERIFY( sc3.readEntry( "badList", QRect() ) == QRect() ); QVERIFY( sc3.readEntry( "badList", QSize() ) == QSize() ); QVERIFY( sc3.readEntry( "badList", QDate() ) == QDate() ); // out of bounds QVERIFY( sc3.readEntry( "badList", QDateTime() ) == QDateTime() ); // 4 element list list << 4; sc3.writeEntry( "badList", list ); QVERIFY( sc3.readEntry( "badList", QPoint() ) == QPoint() ); QVERIFY( sc3.readEntry( "badList", QSize() ) == QSize() ); QVERIFY( sc3.readEntry( "badList", QDate() ) == QDate() ); QVERIFY( sc3.readEntry( "badList", QDateTime() ) == QDateTime() ); // 5 element list list[2] = 3; list << 5; sc3.writeEntry( "badList", list); QVERIFY( sc3.readEntry( "badList", QPoint() ) == QPoint() ); QVERIFY( sc3.readEntry( "badList", QRect() ) == QRect() ); QVERIFY( sc3.readEntry( "badList", QSize() ) == QSize() ); QVERIFY( sc3.readEntry( "badList", QDate() ) == QDate() ); QVERIFY( sc3.readEntry( "badList", QDateTime() ) == QDateTime() ); // 6 element list list << 6; sc3.writeEntry( "badList", list); QVERIFY( sc3.readEntry( "badList", QPoint() ) == QPoint() ); QVERIFY( sc3.readEntry( "badList", QRect() ) == QRect() ); QVERIFY( sc3.readEntry( "badList", QSize() ) == QSize() ); } void KConfigTest::testChangeGroup() { KConfig sc( "kconfigtest" ); KConfigGroup sc3(&sc, "Hello"); QCOMPARE(sc3.name(), QString("Hello")); KConfigGroup newGroup(sc3); KConfigGroup rootGroup(sc.group("")); QCOMPARE(rootGroup.name(), QString("")); KConfigGroup sc32(rootGroup.group("Hello")); QCOMPARE(sc32.name(), QString("Hello")); KConfigGroup newGroup2(sc32); } // Simple test for deleteEntry void KConfigTest::testDeleteEntry() { const char* configFile = "kconfigdeletetest"; { KConfig conf(configFile); conf.group("Hello").writeEntry("DelKey", "ToBeDeleted"); } const QList lines = readLines(configFile); Q_ASSERT(lines.contains("[Hello]\n")); Q_ASSERT(lines.contains("DelKey=ToBeDeleted\n")); KConfig sc(configFile); KConfigGroup group(&sc, "Hello"); group.deleteEntry("DelKey"); QCOMPARE( group.readEntry("DelKey", QString("Fietsbel")), QString("Fietsbel") ); group.sync(); Q_ASSERT(!readLines(configFile).contains("DelKey=ToBeDeleted\n")); QCOMPARE( group.readEntry("DelKey", QString("still deleted")), QString("still deleted") ); } void KConfigTest::testDelete() { KConfig sc( "kconfigtest" ); KConfigGroup ct(&sc, "Complex Types"); // First delete a nested group KConfigGroup delgr(&ct, "Nested Group 3"); QVERIFY(delgr.exists()); QVERIFY(ct.hasGroup("Nested Group 3")); delgr.deleteGroup(); QVERIFY(!delgr.exists()); QVERIFY(!ct.hasGroup("Nested Group 3")); QVERIFY(ct.groupList().contains("Nested Group 3")); KConfigGroup ng(&ct, "Nested Group 2"); QVERIFY(sc.hasGroup("Complex Types")); QVERIFY(!sc.hasGroup("Does not exist")); sc.deleteGroup("Complex Types"); QCOMPARE(sc.group("Complex Types").keyList().count(), 0); QVERIFY(!sc.hasGroup("Complex Types")); // #192266 QVERIFY(!sc.group("Complex Types").exists()); QVERIFY(!ct.hasGroup("Nested Group 1")); QCOMPARE(ct.group("Nested Group 1").keyList().count(), 0); QCOMPARE(ct.group("Nested Group 2").keyList().count(), 0); QCOMPARE(ng.group("Nested Group 2.1").keyList().count(), 0); KConfigGroup cg(&sc , "AAA" ); cg.deleteGroup(); QVERIFY( sc.entryMap("Complex Types").isEmpty() ); QVERIFY( sc.entryMap("AAA").isEmpty() ); QVERIFY( !sc.entryMap("Hello").isEmpty() ); //not deleted group QVERIFY( sc.entryMap("FooBar").isEmpty() ); //inexistant group cg.sync(); // Check what happens on disk const QList lines = readLines(); //qDebug() << lines; QVERIFY(!lines.contains("[Complex Types]\n")); QVERIFY(!lines.contains("[Complex Types][Nested Group 1]\n")); QVERIFY(!lines.contains("[Complex Types][Nested Group 2]\n")); QVERIFY(!lines.contains("[Complex Types][Nested Group 2.1]\n")); QVERIFY(!lines.contains("[AAA]\n")); QVERIFY(lines.contains("[Hello]\n")); // a group that was not deleted // test for entries that are marked as deleted when there is no default KConfig cf("kconfigtest", KConfig::SimpleConfig); // make sure there are no defaults cg = cf.group("Portable Devices"); cg.writeEntry("devices|manual|(null)", "whatever"); cg.writeEntry("devices|manual|/mnt/ipod", "/mnt/ipod"); cf.sync(); int count=0; foreach(const QByteArray& item, readLines()) if (item.startsWith("devices|")) // krazy:exclude=strings count++; QCOMPARE(count, 2); cg.deleteEntry("devices|manual|/mnt/ipod"); cf.sync(); foreach(const QByteArray& item, readLines()) QVERIFY(!item.contains("ipod")); } void KConfigTest::testDefaultGroup() { KConfig sc( "kconfigtest" ); KConfigGroup defaultGroup(&sc, ""); QCOMPARE(defaultGroup.name(), QString("")); QVERIFY(!defaultGroup.exists()); defaultGroup.writeEntry("TestKey", "defaultGroup"); QVERIFY(defaultGroup.exists()); QCOMPARE(defaultGroup.readEntry("TestKey", QString()), QString("defaultGroup")); sc.sync(); { // Test reading it KConfig sc2("kconfigtest"); KConfigGroup defaultGroup2(&sc2, ""); QCOMPARE(defaultGroup2.name(), QString("")); QVERIFY(defaultGroup2.exists()); QCOMPARE(defaultGroup2.readEntry("TestKey", QString()), QString("defaultGroup")); } { // Test reading it KConfig sc2("kconfigtest"); KConfigGroup emptyGroup(&sc2, ""); QCOMPARE(emptyGroup.name(), QString("")); QVERIFY(emptyGroup.exists()); QCOMPARE(emptyGroup.readEntry("TestKey", QString()), QString("defaultGroup")); } QList lines = readLines(); QVERIFY(!lines.contains("[]\n")); QCOMPARE(lines.first(), QByteArray("TestKey=defaultGroup\n")); // Now that the group exists make sure it isn't returned from groupList() foreach(const QString& group, sc.groupList()) { QVERIFY(!group.isEmpty() && group != ""); } defaultGroup.deleteGroup(); sc.sync(); // Test if deleteGroup worked lines = readLines(); QVERIFY(lines.first() != QByteArray("TestKey=defaultGroup\n")); } void KConfigTest::testEmptyGroup() { KConfig sc( "kconfigtest" ); KConfigGroup emptyGroup(&sc, ""); QCOMPARE(emptyGroup.name(), QString("")); // confusing, heh? QVERIFY(!emptyGroup.exists()); emptyGroup.writeEntry("TestKey", "emptyGroup"); QVERIFY(emptyGroup.exists()); QCOMPARE(emptyGroup.readEntry("TestKey", QString()), QString("emptyGroup")); sc.sync(); { // Test reading it KConfig sc2("kconfigtest"); KConfigGroup defaultGroup(&sc2, ""); QCOMPARE(defaultGroup.name(), QString("")); QVERIFY(defaultGroup.exists()); QCOMPARE(defaultGroup.readEntry("TestKey", QString()), QString("emptyGroup")); } { // Test reading it KConfig sc2("kconfigtest"); KConfigGroup emptyGroup2(&sc2, ""); QCOMPARE(emptyGroup2.name(), QString("")); QVERIFY(emptyGroup2.exists()); QCOMPARE(emptyGroup2.readEntry("TestKey", QString()), QString("emptyGroup")); } QList lines = readLines(); QVERIFY(!lines.contains("[]\n")); // there's no support for the [] group, in fact. QCOMPARE(lines.first(), QByteArray("TestKey=emptyGroup\n")); // Now that the group exists make sure it isn't returned from groupList() foreach(const QString& group, sc.groupList()) { QVERIFY(!group.isEmpty() && group != ""); } emptyGroup.deleteGroup(); sc.sync(); // Test if deleteGroup worked lines = readLines(); QVERIFY(lines.first() != QByteArray("TestKey=defaultGroup\n")); } void KConfigTest::testCascadingWithLocale() { KTempDir middleDir; KGlobal::dirs()->addPrefix(middleDir.name()); KTempDir globalDir; KGlobal::dirs()->addPrefix(globalDir.name()); const QString globalConfigDir = globalDir.name() + "/share/config"; QVERIFY(QDir().mkpath(globalConfigDir)); QFile global(globalConfigDir + "/foo.desktop"); QVERIFY(global.open(QIODevice::WriteOnly|QIODevice::Text)); QTextStream globalOut(&global); globalOut << "[Group]" << endl << "FromGlobal=true" << endl << "FromGlobal[fr]=vrai" << endl << "Name=Testing" << endl << "Name[fr]=FR" << endl << "Other=Global" << endl << "Other[fr]=Global_FR" << endl; global.close(); const QString middleConfigDir = middleDir.name() + "/share/config"; QVERIFY(QDir().mkpath(middleConfigDir)); QFile local(middleConfigDir + "/foo.desktop"); QVERIFY(local.open(QIODevice::WriteOnly|QIODevice::Text)); QTextStream out(&local); out << "[Group]" << endl << "FromLocal=true" << endl << "FromLocal[fr]=vrai" << endl << "Name=Local Testing" << endl << "Name[fr]=FR" << endl << "Other=English Only" << endl; local.close(); KConfig config("foo.desktop"); KConfigGroup group = config.group("Group"); QCOMPARE(group.readEntry("FromGlobal"), QString("true")); QCOMPARE(group.readEntry("FromLocal"), QString("true")); QCOMPARE(group.readEntry("Name"), QString("Local Testing")); config.setLocale("fr"); QCOMPARE(group.readEntry("FromGlobal"), QString("vrai")); QCOMPARE(group.readEntry("FromLocal"), QString("vrai")); QCOMPARE(group.readEntry("Name"), QString("FR")); QCOMPARE(group.readEntry("Other"), QString("English Only")); // Global_FR is locally overriden } void KConfigTest::testMerge() { KConfig config("mergetest", KConfig::SimpleConfig); KConfigGroup cg = config.group("some group"); cg.writeEntry("entry", " random entry"); cg.writeEntry("another entry", "blah blah blah"); { // simulate writing by another process QFile file(KStandardDirs::locateLocal("config", "mergetest")); file.open(QIODevice::WriteOnly|QIODevice::Text); QTextStream out(&file); out.setCodec("UTF-8"); out << "[Merged Group]" << endl << "entry1=Testing" << endl << "entry2=More Testing" << endl << "[some group]" << endl << "entry[fr]=French" << endl << "entry[es]=Spanish" << endl << "entry[de]=German" << endl; } config.sync(); { QList lines; // this is what the file should look like lines << "[Merged Group]\n" << "entry1=Testing\n" << "entry2=More Testing\n" << "\n" << "[some group]\n" << "another entry=blah blah blah\n" << "entry=\\srandom entry\n" << "entry[de]=German\n" << "entry[es]=Spanish\n" << "entry[fr]=French\n"; QFile file(KStandardDirs::locateLocal("config", "mergetest")); file.open(QIODevice::ReadOnly|QIODevice::Text); foreach (const QByteArray& line, lines) { QCOMPARE(line, file.readLine()); } } } void KConfigTest::testImmutable() { { QFile file(KStandardDirs::locateLocal("config", "immutabletest")); file.open(QIODevice::WriteOnly|QIODevice::Text); QTextStream out(&file); out.setCodec("UTF-8"); out << "[$i]" << endl << "entry1=Testing" << endl << "[group][$i]" << endl << "[group][subgroup][$i]" << endl; } KConfig config("immutabletest", KConfig::SimpleConfig); QVERIFY(config.isGroupImmutable(QByteArray())); KConfigGroup cg = config.group(QByteArray()); QVERIFY(cg.isEntryImmutable("entry1")); KConfigGroup cg1 = config.group("group"); QVERIFY(cg1.isImmutable()); KConfigGroup cg1a = cg.group("group"); QVERIFY(cg1a.isImmutable()); KConfigGroup cg2 = cg1.group("subgroup"); QVERIFY(cg2.isImmutable()); } void KConfigTest::testOptionOrder() { { QFile file(KStandardDirs::locateLocal("config", "doubleattrtest")); file.open(QIODevice::WriteOnly|QIODevice::Text); QTextStream out(&file); out.setCodec("UTF-8"); out << "[group3]" << endl << "entry2=unlocalized" << endl << "entry2[$i][de_DE]=t2" << endl; } KConfig config("doubleattrtest", KConfig::SimpleConfig); config.setLocale("de_DE"); KConfigGroup cg3 = config.group("group3"); QVERIFY(!cg3.isImmutable()); QCOMPARE(cg3.readEntry("entry2",""), QString("t2")); QVERIFY(cg3.isEntryImmutable("entry2")); config.setLocale("C"); QCOMPARE(cg3.readEntry("entry2",""), QString("unlocalized")); QVERIFY(!cg3.isEntryImmutable("entry2")); cg3.writeEntry("entry2","modified"); config.sync(); { QList lines; // this is what the file should look like lines << "[group3]\n" << "entry2=modified\n" << "entry2[de_DE][$i]=t2\n"; QFile file(KStandardDirs::locateLocal("config", "doubleattrtest")); file.open(QIODevice::ReadOnly|QIODevice::Text); foreach (const QByteArray& line, lines) { QCOMPARE(line, file.readLine()); } } } void KConfigTest::testGroupEscape() { KConfig config("groupescapetest", KConfig::SimpleConfig); QVERIFY( config.group(DOLLARGROUP).exists() ); } void KConfigTest::testSubGroup() { KConfig sc( "kconfigtest" ); KConfigGroup cg( &sc, "ParentGroup" ); QCOMPARE(cg.readEntry( "parentgrpstring", ""), QString("somevalue") ); KConfigGroup subcg1( &cg, "SubGroup1"); QCOMPARE(subcg1.name(), QString("SubGroup1")); QCOMPARE(subcg1.readEntry( "somestring", ""), QString("somevalue") ); KConfigGroup subcg2( &cg, "SubGroup2"); QCOMPARE(subcg2.name(), QString("SubGroup2")); QCOMPARE(subcg2.readEntry( "substring", ""), QString("somevalue") ); KConfigGroup subcg3( &cg, "SubGroup/3"); QCOMPARE(subcg3.readEntry( "sub3string", ""), QString("somevalue") ); QCOMPARE(subcg3.name(), QString("SubGroup/3")); KConfigGroup rcg( &sc, "" ); KConfigGroup srcg( &rcg, "ParentGroup" ); QCOMPARE(srcg.readEntry( "parentgrpstring", ""), QString("somevalue") ); QCOMPARE(cg.groupList(), (QStringList() << "SubGroup/3" << "SubGroup1" << "SubGroup2")); const QStringList expectedSubgroup3Keys = (QStringList() << "sub3string"); QCOMPARE(subcg3.keyList(), expectedSubgroup3Keys); const QStringList expectedParentGroupKeys(QStringList() << "parentgrpstring"); QCOMPARE(cg.keyList(), expectedParentGroupKeys); QCOMPARE(QStringList(cg.entryMap().keys()), expectedParentGroupKeys); QCOMPARE(QStringList(subcg3.entryMap().keys()), expectedSubgroup3Keys); // Create A group containing only other groups. We want to make sure it // shows up in groupList of sc KConfigGroup neg(&sc, "NoEntryGroup"); KConfigGroup negsub1(&neg, "NEG Child1"); negsub1.writeEntry( "entry", "somevalue" ); KConfigGroup negsub2(&neg, "NEG Child2"); KConfigGroup negsub3(&neg, "NEG Child3"); KConfigGroup negsub31(&negsub3, "NEG Child3-1"); KConfigGroup negsub4(&neg, "NEG Child4"); KConfigGroup negsub41(&negsub4, "NEG Child4-1"); negsub41.writeEntry( "entry", "somevalue" ); // A group exists if it has content QVERIFY(negsub1.exists()); // But it doesn't exist if it has no content // Ossi and David say: this is how it's supposed to work. // However you could add a dummy entry for now, or we could add a "Persist" feature to kconfig groups // which would make it written out, much like "immutable" already makes them persistent. QVERIFY(!negsub2.exists()); // A subgroup does not qualify as content if it is also empty QVERIFY(!negsub3.exists()); // A subgroup with content is ok QVERIFY(negsub4.exists()); // Only subgroups with content show up in groupList() //QEXPECT_FAIL("", "Empty subgroups do not show up in groupList()", Continue); //QCOMPARE(neg.groupList(), QStringList() << "NEG Child1" << "NEG Child2" << "NEG Child3" << "NEG Child4"); // This is what happens QCOMPARE(neg.groupList(), QStringList() << "NEG Child1" << "NEG Child4"); // make sure groupList() isn't returning something it shouldn't foreach(const QString& group, sc.groupList()) { QVERIFY(!group.isEmpty() && group != ""); QVERIFY(!group.contains(QChar(0x1d))); QVERIFY(!group.contains("subgroup")); QVERIFY(!group.contains("SubGroup")); } sc.sync(); // Check that the empty groups are not written out. const QList lines = readLines(); QVERIFY(lines.contains("[NoEntryGroup][NEG Child1]\n")); QVERIFY(!lines.contains("[NoEntryGroup][NEG Child2]\n")); QVERIFY(!lines.contains("[NoEntryGroup][NEG Child3]\n")); QVERIFY(!lines.contains("[NoEntryGroup][NEG Child4]\n")); // implicit group, not written out QVERIFY(lines.contains("[NoEntryGroup][NEG Child4][NEG Child4-1]\n")); } void KConfigTest::testAddConfigSources() { KConfig cf("specificrc"); cf.addConfigSources(QStringList() << KStandardDirs::locateLocal("config", "baserc")); cf.reparseConfiguration(); KConfigGroup specificgrp(&cf, "Specific Only Group"); QCOMPARE(specificgrp.readEntry("ExistingEntry", ""), QString("DevValue")); KConfigGroup sharedgrp(&cf, "Shared Group"); QCOMPARE(sharedgrp.readEntry("SomeSpecificOnlyEntry",""), QString("DevValue")); QCOMPARE(sharedgrp.readEntry("SomeBaseOnlyEntry",""), QString("BaseValue")); QCOMPARE(sharedgrp.readEntry("SomeSharedEntry",""), QString("DevValue")); KConfigGroup basegrp(&cf, "Base Only Group"); QCOMPARE(basegrp.readEntry("ExistingEntry", ""), QString("BaseValue")); basegrp.writeEntry("New Entry Base Only", "SomeValue"); KConfigGroup newgrp(&cf, "New Group"); newgrp.writeEntry("New Entry", "SomeValue"); cf.sync(); KConfig plaincfg("specificrc"); KConfigGroup newgrp2(&plaincfg, "New Group"); QCOMPARE(newgrp2.readEntry("New Entry", ""), QString("SomeValue")); KConfigGroup basegrp2(&plaincfg, "Base Only Group"); QCOMPARE(basegrp2.readEntry("New Entry Base Only", ""), QString("SomeValue")); } void KConfigTest::testGroupCopyTo() { KConfig cf1("kconfigtest"); KConfigGroup original = cf1.group("Enum Types"); KConfigGroup copy = cf1.group("Enum Types Copy"); original.copyTo(©); // copy from one group to another QCOMPARE(copy.entryMap(), original.entryMap()); KConfig cf2("copy_of_kconfigtest", KConfig::SimpleConfig); QVERIFY(!cf2.hasGroup(original.name())); QVERIFY(!cf2.hasGroup(copy.name())); KConfigGroup newGroup = cf2.group(original.name()); original.copyTo(&newGroup); // copy from one file to another QVERIFY(cf2.hasGroup(original.name())); QVERIFY(!cf2.hasGroup(copy.name())); // make sure we didn't copy more than we wanted QCOMPARE(newGroup.entryMap(), original.entryMap()); } void KConfigTest::testConfigCopyToSync() { KConfig cf1("kconfigtest"); // Prepare source file KConfigGroup group(&cf1, "CopyToTest"); group.writeEntry("Type", "Test"); cf1.sync(); // Copy to "destination" const QString destination = KStandardDirs::locateLocal("config", "kconfigcopytotest"); QFile::remove(destination); KConfig cf2("kconfigcopytotest"); KConfigGroup group2(&cf2, "CopyToTest"); group.copyTo(&group2); QString testVal = group2.readEntry("Type"); QCOMPARE(testVal, QString("Test")); // should write to disk the copied data from group cf2.sync(); QVERIFY(QFile::exists(destination)); } void KConfigTest::testConfigCopyTo() { KConfig cf1("kconfigtest"); { // Prepare source file KConfigGroup group(&cf1, "CopyToTest"); group.writeEntry("Type", "Test"); cf1.sync(); } { // Copy to "destination" const QString destination = KStandardDirs::locateLocal("config", "kconfigcopytotest"); QFile::remove(destination); KConfig cf2; cf1.copyTo(destination, &cf2); KConfigGroup group2(&cf2, "CopyToTest"); QString testVal = group2.readEntry("Type"); QCOMPARE(testVal, QString("Test")); cf2.sync(); QVERIFY(QFile::exists(destination)); } // Check copied config file on disk KConfig cf3("kconfigcopytotest"); KConfigGroup group3(&cf3, "CopyToTest"); QString testVal = group3.readEntry("Type"); QCOMPARE(testVal, QString("Test")); } void KConfigTest::testReparent() { KConfig cf("kconfigtest"); const QString name("Enum Types"); KConfigGroup group = cf.group(name); const QMap originalMap = group.entryMap(); KConfigGroup parent = cf.group("Parent Group"); QVERIFY(!parent.hasGroup(name)); QVERIFY(group.entryMap() == originalMap); group.reparent(&parent); // see if it can be made a sub-group of another group QVERIFY(parent.hasGroup(name)); QCOMPARE(group.entryMap(), originalMap); group.reparent(&cf); // see if it can make it a top-level group again // QVERIFY(!parent.hasGroup(name)); QCOMPARE(group.entryMap(), originalMap); } void KConfigTest::testKAboutDataOrganizationDomain() { KAboutData data( "app", 0, ki18n("program"), "version", ki18n("description"), KAboutData::License_LGPL, ki18n("copyright"), ki18n("hello world"), "http://www.koffice.org" ); QCOMPARE( data.organizationDomain(), QString::fromLatin1( "koffice.org" ) ); KAboutData data2( "app", 0, ki18n("program"), "version", ki18n("description"), KAboutData::License_LGPL, ki18n("copyright"), ki18n("hello world"), "http://edu.kde.org/kig" ); QCOMPARE( data2.organizationDomain(), QString::fromLatin1( "kde.org" ) ); } static void ageTimeStamp(const QString& path, int nsec) { #ifdef Q_OS_UNIX QDateTime mtime = QFileInfo(path).lastModified().addSecs(-nsec); struct utimbuf utbuf; utbuf.actime = mtime.toTime_t(); utbuf.modtime = utbuf.actime; utime(QFile::encodeName(path), &utbuf); #else QTest::qSleep(nsec * 1000); #endif } void KConfigTest::testWriteOnSync() { QDateTime oldStamp, newStamp; KConfig sc("kconfigtest", KConfig::IncludeGlobals); // Age the timestamp of global config file a few sec, and collect it. QString globFile = KStandardDirs::locateLocal("config", "kdeglobals"); ageTimeStamp(globFile, 2); // age 2 sec oldStamp = QFileInfo(globFile).lastModified(); // Add a local entry and sync the config. // Should not rewrite the global config file. KConfigGroup cgLocal(&sc, "Locals"); cgLocal.writeEntry("someLocalString", "whatever"); sc.sync(); // Verify that the timestamp of global config file didn't change. newStamp = QFileInfo(globFile).lastModified(); QCOMPARE(newStamp, oldStamp); // Age the timestamp of local config file a few sec, and collect it. QString locFile = KStandardDirs::locateLocal("config", "kconfigtest"); ageTimeStamp(locFile, 2); // age 2 sec oldStamp = QFileInfo(locFile).lastModified(); // Add a global entry and sync the config. // Should not rewrite the local config file. KConfigGroup cgGlobal(&sc, "Globals"); cgGlobal.writeEntry("someGlobalString", "whatever", KConfig::Persistent|KConfig::Global); sc.sync(); // Verify that the timestamp of local config file didn't change. newStamp = QFileInfo(locFile).lastModified(); QCOMPARE(newStamp, oldStamp); } void KConfigTest::testDirtyOnEqual() { QDateTime oldStamp, newStamp; KConfig sc("kconfigtest"); // Initialize value KConfigGroup cgLocal(&sc, "random"); cgLocal.writeEntry("theKey", "whatever"); sc.sync(); // Age the timestamp of local config file a few sec, and collect it. QString locFile = KStandardDirs::locateLocal("config", "kconfigtest"); ageTimeStamp(locFile, 2); // age 2 sec oldStamp = QFileInfo(locFile).lastModified(); // Write exactly the same again cgLocal.writeEntry("theKey", "whatever"); // This should be a no-op sc.sync(); // Verify that the timestamp of local config file didn't change. newStamp = QFileInfo(locFile).lastModified(); QCOMPARE(newStamp, oldStamp); } void KConfigTest::testDirtyOnEqualOverdo() { QByteArray val1("\0""one", 4); QByteArray val2("\0""two", 4); QByteArray defvalr; KConfig sc("kconfigtest"); KConfigGroup cgLocal(&sc, "random"); cgLocal.writeEntry("someKey", val1); QCOMPARE(cgLocal.readEntry("someKey", defvalr), val1); cgLocal.writeEntry("someKey", val2); QCOMPARE(cgLocal.readEntry("someKey", defvalr), val2); } void KConfigTest::testCreateDir() { // Test auto-creating the parent directory when needed (KConfigIniBackend::createEnclosing) QString kdehome = QDir::home().canonicalPath() + "/.kde-unit-test"; QString subdir = kdehome + "/newsubdir"; QString file = subdir + "/foo.desktop"; QFile::remove(file); QDir().rmdir(subdir); QVERIFY(!QDir().exists(subdir)); KDesktopFile desktopFile(file); desktopFile.desktopGroup().writeEntry("key", "value"); desktopFile.sync(); QVERIFY(QFile::exists(file)); // Cleanup QFile::remove(file); QDir().rmdir(subdir); } void KConfigTest::testSyncOnExit() { // Often, the KGlobalPrivate global static's destructor ends up calling ~KConfig -> // KConfig::sync ... and if that code triggers KGlobal code again then things could crash. // So here's a test for modifying KGlobal::config() and not syncing, the process exit will sync. KConfigGroup grp(KGlobal::config(), "syncOnExit"); grp.writeEntry("key", "value"); } void KConfigTest::testSharedConfig() { // Can I use a KConfigGroup even after the KSharedConfigPtr goes out of scope? KConfigGroup myConfigGroup; { KSharedConfigPtr config = KSharedConfig::openConfig("kconfigtest"); myConfigGroup = KConfigGroup(config, "Hello"); } QCOMPARE(myConfigGroup.readEntry("stringEntry1"), QString(STRINGENTRY1)); } void KConfigTest::testLocaleConfig() { // Initialize the testdata QDir dir; QString subdir = QDir::home().canonicalPath() + "/.kde-unit-test/"; dir.mkpath(subdir); QString file = subdir + "/localized.test"; QFile::remove(file); QFile f(file); QVERIFY(f.open(QIODevice::WriteOnly)); QTextStream ts(&f); ts << "[Test_Wrong]\n"; ts << "foo[ca]=5\n"; ts << "foostring[ca]=nice\n"; ts << "foobool[ca]=true\n"; ts << "[Test_Right]\n"; ts << "foo=5\n"; ts << "foo[ca]=5\n"; ts << "foostring=primary\n"; ts << "foostring[ca]=nice\n"; ts << "foobool=primary\n"; ts << "foobool[ca]=true\n"; f.close(); // Load the testdata QVERIFY(QFile::exists(file)); KConfig config(file); config.setLocale("ca"); // This group has only localized values. That is not supported. The values // should be dropped on loading. KConfigGroup cg(&config, "Test_Wrong"); QEXPECT_FAIL("", "The localized values are not dropped", Continue); QVERIFY(!cg.hasKey("foo")); QEXPECT_FAIL("", "The localized values are not dropped", Continue); QVERIFY(!cg.hasKey("foostring")); QEXPECT_FAIL("", "The localized values are not dropped", Continue); QVERIFY(!cg.hasKey("foobool")); // Now check the correct config group KConfigGroup cg2(&config, "Test_Right"); QCOMPARE(cg2.readEntry("foo"), QString("5")); QCOMPARE(cg2.readEntry("foo", 3), 5); QCOMPARE(cg2.readEntry("foostring"), QString("nice")); QCOMPARE(cg2.readEntry("foostring", "ugly"), QString("nice")); QCOMPARE(cg2.readEntry("foobool"), QString("true")); QCOMPARE(cg2.readEntry("foobool", false), true); // Clean up after the testcase QFile::remove(file); } void KConfigTest::testDeleteWhenLocalized() { // Initialize the testdata QDir dir; QString subdir = QDir::home().canonicalPath() + "/.kde-unit-test/"; dir.mkpath(subdir); QString file = subdir + "/localized_delete.test"; QFile::remove(file); QFile f(file); QVERIFY(f.open(QIODevice::WriteOnly)); QTextStream ts(&f); ts << "[Test4711]\n"; ts << "foo=3\n"; ts << "foo[ca]=5\n"; ts << "foo[de]=7\n"; ts << "foostring=ugly\n"; ts << "foostring[ca]=nice\n"; ts << "foostring[de]=schoen\n"; ts << "foobool=false\n"; ts << "foobool[ca]=true\n"; ts << "foobool[de]=true\n"; f.close(); // Load the testdata. We start in locale "ca". QVERIFY(QFile::exists(file)); KConfig config(file); config.setLocale("ca"); KConfigGroup cg(&config, "Test4711"); // Delete a value. Once with localized, once with Normal cg.deleteEntry("foostring", KConfigBase::Persistent | KConfigBase::Localized); cg.deleteEntry("foobool"); config.sync(); // The value is now gone. The others are still there. Everything correct // here. QVERIFY(!cg.hasKey("foostring")); QVERIFY(!cg.hasKey("foobool")); QVERIFY(cg.hasKey("foo")); // The current state is: (Just return before this comment.) // [...] // foobool[ca]=true // foobool[de]=wahr // foostring=ugly // foostring[de]=schoen // Now switch the locale to "de" and repeat the checks. Results should be // the same. But they currently are not. The localized value are // independent of each other. All values are still there in "de". config.setLocale("de"); QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foostring")); QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foobool")); QVERIFY(cg.hasKey("foo")); // Check where the wrong values come from. // We get the "de" value. QCOMPARE(cg.readEntry("foostring", "nothing"), QString("schoen")); // We get the "de" value. QCOMPARE(cg.readEntry("foobool", false), true); // Now switch the locale back "ca" and repeat the checks. Results are // again different. config.setLocale("ca"); // This line worked above. But now it fails. QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foostring")); // This line worked above too. QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foobool")); QVERIFY(cg.hasKey("foo")); // Check where the wrong values come from. // We get the primary value because the "ca" value was deleted. QCOMPARE(cg.readEntry("foostring", "nothing"), QString("ugly")); // We get the "ca" value. QCOMPARE(cg.readEntry("foobool", false), true); // Now test the deletion of a group. cg.deleteGroup(); config.sync(); // Current state: [ca] and [de] entries left... oops. //qDebug() << readLinesFrom(file); // Bug: The group still exists [because of the localized entries]... QVERIFY(cg.exists()); QVERIFY(!cg.hasKey("foo")); QVERIFY(!cg.hasKey("foostring")); QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foobool")); // Now switch the locale to "de" and repeat the checks. All values // still here because only the primary values are deleted. config.setLocale("de"); QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foo")); QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foostring")); QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foobool")); // Check where the wrong values come from. // We get the "de" value. QCOMPARE(cg.readEntry("foostring", "nothing"), QString("schoen")); // We get the "de" value. QCOMPARE(cg.readEntry("foobool", false), true); // We get the "de" value. QCOMPARE(cg.readEntry("foo", 0), 7); // Now switch the locale to "ca" and repeat the checks // "foostring" is now really gone because both the primary value and the // "ca" value are deleted. config.setLocale("ca"); QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foo")); QVERIFY(!cg.hasKey("foostring")); QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foobool")); // Check where the wrong values come from. // We get the "ca" value. QCOMPARE(cg.readEntry("foobool", false), true); // We get the "ca" value. QCOMPARE(cg.readEntry("foo", 0), 5); // Cleanup QFile::remove(file); } void KConfigTest::testKdeGlobals() { { KConfig glob("kdeglobals"); KConfigGroup general(&glob, "General"); general.writeEntry("testKG", "1"); glob.sync(); } KConfig globRead("kdeglobals"); const KConfigGroup general(&globRead, "General"); QCOMPARE(general.readEntry("testKG"), QString("1")); // Check we wrote into kdeglobals const QList lines = readLines("kdeglobals"); QVERIFY(lines.contains("[General]\n")); QVERIFY(lines.contains("testKG=1\n")); // Writing using NoGlobals { KConfig glob("kdeglobals", KConfig::NoGlobals); KConfigGroup general(&glob, "General"); general.writeEntry("testKG", "2"); glob.sync(); } globRead.reparseConfiguration(); QCOMPARE(general.readEntry("testKG"), QString("2")); // Reading using NoGlobals { KConfig globReadNoGlob("kdeglobals", KConfig::NoGlobals); const KConfigGroup generalNoGlob(&globReadNoGlob, "General"); QCOMPARE(generalNoGlob.readEntry("testKG"), QString("2")); } // TODO now use kconfigtest and writeEntry(,Global) -> should go into kdeglobals } void KConfigTest::testAnonymousConfig() { KConfig anonConfig(QString(), KConfig::SimpleConfig); KConfigGroup general(&anonConfig, "General"); QCOMPARE(general.readEntry("testKG"), QString()); // no kdeglobals merging general.writeEntry("Foo", "Bar"); QCOMPARE(general.readEntry("Foo"), QString("Bar")); } void KConfigTest::testNoKdeHome() { const QString kdeHome = QDir::homePath() + "/.kde-unit-test-does-not-exist"; setenv("KDEHOME", QFile::encodeName( kdeHome ), 1); KTempDir::removeDir(kdeHome); QVERIFY(!QFile::exists(kdeHome)); // Do what kde4-config does, and ensure kdehome doesn't get created (#233892) KComponentData componentData("KConfigTest"); QVERIFY(!QFile::exists(kdeHome)); componentData.dirs(); QVERIFY(!QFile::exists(kdeHome)); componentData.config(); QVERIFY(!QFile::exists(kdeHome)); // Now try to actually save something, see if it works. KConfigGroup group(componentData.config(), "Group"); group.writeEntry("Key", "Value"); group.sync(); QVERIFY(QFile::exists(kdeHome)); QVERIFY(QFile::exists(kdeHome + "/share/config/KConfigTestrc")); // Cleanup KTempDir::removeDir(kdeHome); } #include #include // To find multithreading bugs: valgrind --tool=helgrind --track-lockorders=no ./kconfigtest testThreads void KConfigTest::testThreads() { QThreadPool::globalInstance()->setMaxThreadCount(6); QList > futures; // Run in parallel some tests that work on different config files, // otherwise unexpected things might indeed happen. futures << QtConcurrent::run(this, &KConfigTest::testAddConfigSources); futures << QtConcurrent::run(this, &KConfigTest::testSimple); futures << QtConcurrent::run(this, &KConfigTest::testDefaults); // QEXPECT_FAIL triggers race conditions, it should be fixed to use QThreadStorage... //futures << QtConcurrent::run(this, &KConfigTest::testDeleteWhenLocalized); //futures << QtConcurrent::run(this, &KConfigTest::testEntryMap); Q_FOREACH(QFuture f, futures) // krazy:exclude=foreach f.waitForFinished(); }