diff --git a/kdecore/config/kconfig.cpp b/kdecore/config/kconfig.cpp index d5a8a10b54..4cb043ff9d 100644 --- a/kdecore/config/kconfig.cpp +++ b/kdecore/config/kconfig.cpp @@ -1,670 +1,671 @@ /* This file is part of the KDE libraries Copyright (c) 2006, 2007 Thomas Braxton Copyright (c) 1999 Preston Brown Copyright (c) 1997-1999 Matthias Kalle Dalheimer 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 #include #include #include "kconfig.h" #include "kconfig_p.h" #include "kconfigbackend.h" #include "kconfiggroup.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include bool KConfigPrivate::mappingsRegistered=false; KConfigPrivate::KConfigPrivate(const KComponentData &componentData_, KConfig::OpenFlags flags, const char* resource) : openFlags(flags), resourceType(resource), mBackend(0), bDynamicBackend(true), bDirty(false), bReadDefaults(false), bFileImmutable(false), bForceGlobal(false), componentData(componentData_), configState(KConfigBase::NoAccess) { sGlobalFileName = componentData.dirs()->saveLocation("config") + QString::fromLatin1("kdeglobals"); if (wantGlobals()) { const KStandardDirs *const dirs = componentData.dirs(); foreach(const QString& dir, dirs->findAllResources("config", QLatin1String("kdeglobals")) + dirs->findAllResources("config", QLatin1String("system.kdeglobals"))) globalFiles.push_front(dir); } const QString etc_kderc = #ifdef Q_WS_WIN QFile::decodeName( QByteArray(::getenv("WINDIR")) + "\\kde4rc" ); #else QLatin1String("/etc/kde4rc"); #endif KEntryMap tmp; // first entry is always /etc/kderc or empty if cannot read if (KStandardDirs::checkAccess(etc_kderc, R_OK)) { if (!globalFiles.contains(etc_kderc)) globalFiles.push_front(etc_kderc); if (!mappingsRegistered) { KSharedPtr backend = KConfigBackend::create(componentData, etc_kderc, QLatin1String("INI")); backend->parseConfig( "en_US", tmp, KConfigBackend::ParseDefaults); } } else { globalFiles.push_front(QString()); mappingsRegistered = true; } if (!mappingsRegistered) { const QString kde4rc(QDir::home().filePath(".kde4rc")); if (KStandardDirs::checkAccess(kde4rc, R_OK)) { KSharedPtr backend = KConfigBackend::create(componentData, kde4rc, QLatin1String("INI")); backend->parseConfig( "en_US", tmp, KConfigBackend::ParseOptions()); } KConfigBackend::registerMappings(tmp); mappingsRegistered = true; } setLocale(KGlobal::hasLocale() ? KGlobal::locale()->language() : KLocale::defaultLanguage()); } bool KConfigPrivate::lockLocal() { if (mBackend) { if (fileName == QLatin1String("kdeglobals")) { // we don't want to lock "kdeglobals" twice if (wantGlobals()) // "kdeglobals" will be locked with the global lock return true; // so pretend we locked it here } return mBackend->lock(componentData); } // anonymous object - pretend we locked it return true; } KConfig::KConfig( const QString& file, OpenFlags mode, const char* resourceType) : d_ptr(new KConfigPrivate(KGlobal::mainComponent(), mode, resourceType)) { d_ptr->changeFileName(file, resourceType); // set the local file name // read initial information off disk reparseConfiguration(); } KConfig::KConfig( const KComponentData& componentData, const QString& file, OpenFlags mode, const char* resourceType) : d_ptr(new KConfigPrivate(componentData, mode, resourceType)) { d_ptr->changeFileName(file, resourceType); // set the local file name // read initial information off disk reparseConfiguration(); } KConfig::KConfig(KConfigPrivate &d) : d_ptr(&d) { } KConfig::~KConfig() { Q_D(KConfig); if (d->bDirty && d->mBackend.isUnique()) sync(); delete d; } const KComponentData& KConfig::componentData() const { Q_D(const KConfig); return d->componentData; } QStringList KConfig::groupList() const { Q_D(const KConfig); QStringList groups; foreach (const KEntryKey& key, d->entryMap.keys()) if (key.mKey.isNull() && !key.mGroup.isEmpty() && key.mGroup != "" && key.mGroup != "$Version") groups << QString::fromUtf8(key.mGroup); return groups; } QStringList KConfigPrivate::groupList(const QByteArray& group) const { QByteArray theGroup = group + '\x1d'; QSet groups; foreach (const KEntryKey& key, entryMap.keys()) if (key.mKey.isNull() && key.mGroup.startsWith(theGroup)) { QString groupname = QString::fromUtf8(key.mGroup.mid(theGroup.length())); groups << groupname.left(groupname.indexOf('\x1d')); } return groups.toList(); } QStringList KConfig::keyList(const QString& aGroup) const { Q_D(const KConfig); QStringList keys; const QByteArray theGroup(aGroup.isEmpty() ? "" : aGroup.toUtf8()); const KEntryMapConstIterator theEnd = d->entryMap.constEnd(); KEntryMapConstIterator it = d->entryMap.findEntry(theGroup); if (it != theEnd) { ++it; // advance past the special group entry marker QSet tmp; for (; it != theEnd && it.key().mGroup == theGroup; ++it) { const KEntryKey& key = it.key(); if (key.mGroup == theGroup && !key.mKey.isNull() && !it->bDeleted) tmp << QString::fromUtf8(key.mKey); } keys = tmp.toList(); } return keys; } QMap KConfig::entryMap(const QString& aGroup) const { Q_D(const KConfig); QMap theMap; const QByteArray theGroup(aGroup.isEmpty() ? "" : aGroup.toUtf8()); const KEntryMapConstIterator theEnd = d->entryMap.constEnd(); KEntryMapConstIterator it = d->entryMap.findEntry(theGroup, 0, 0); if (it != theEnd) { ++it; // advance past the special group entry marker for (; it != theEnd && it.key().mGroup == theGroup; ++it) { // leave the default values and deleted entries out if (!it->bDeleted && !it.key().bDefault) { const QString key = QString::fromUtf8(it.key().mKey.constData()); // the localized entry should come first, so don't overwrite it // with the non-localized entry if (!theMap.contains(key)) theMap.insert(key,QString::fromUtf8(it->mValue.constData())); } } } return theMap; } void KConfig::sync() { Q_D(KConfig); Q_ASSERT(!isImmutable() && !name().isEmpty()); // can't write to an immutable or anonymous file. if (d->bDirty && d->mBackend) { const QByteArray utf8Locale(locale().toUtf8()); // Check if we can write to the local file. if (!d->mBackend->isWritable()) { // Create the containing dir, maybe it wasn't there d->mBackend->createEnclosing(); } // lock the local file if (d->configState == ReadWrite && !d->lockLocal()) { qWarning() << "couldn't lock local file"; return; } if (d->wantGlobals()) { KSharedPtr tmp = KConfigBackend::create(componentData(), d->sGlobalFileName); if (d->configState == ReadWrite && !tmp->lock(componentData())) { qWarning() << "couldn't lock global file"; return; } tmp->writeConfig(utf8Locale, d->entryMap, KConfigBackend::WriteGlobal, d->componentData); if (tmp->isLocked()) tmp->unlock(); } if (d->mBackend->writeConfig(utf8Locale, d->entryMap, KConfigBackend::WriteOptions(), d->componentData)) d->bDirty = false; if (d->mBackend->isLocked()) d->mBackend->unlock(); } } void KConfig::markAsClean() { Q_D(KConfig); d->bDirty = false; // clear any dirty flags that entries might have set const KEntryMapIterator theEnd = d->entryMap.end(); for (KEntryMapIterator it = d->entryMap.begin(); it != theEnd; ++it) it->bDirty = false; } void KConfig::checkUpdate(const QString &id, const QString &updateFile) { const KConfigGroup cg(this, "$Version"); const QString cfg_id = updateFile+':'+id; QStringList ids = cg.readEntry("update_info", QStringList()); if (!ids.contains(cfg_id)) { KToolInvocation::kdeinitExecWait("kconf_update", QStringList() << "--check" << updateFile); reparseConfiguration(); } } KConfig* KConfig::copyTo(const QString &file, KConfig *config) const { Q_D(const KConfig); if (!config) config = new KConfig(componentData(), QString(), SimpleConfig); config->d_func()->changeFileName(file, d->resourceType); config->d_func()->entryMap = d->entryMap; config->d_func()->bFileImmutable = false; const KEntryMapIterator theEnd = config->d_func()->entryMap.end(); for (KEntryMapIterator it = config->d_func()->entryMap.begin(); it != theEnd; ++it) it->bDirty = true; + config->d_ptr->bDirty = true; return config; } QString KConfig::name() const { Q_D(const KConfig); return d->fileName; } void KConfigPrivate::changeFileName(const QString& name, const char* type) { fileName = name; QString file; if (name.isEmpty()) { if (wantDefaults()) { // accessing default app-specific config "appnamerc" const QString appName = componentData.aboutData()->appName(); if (!appName.isEmpty()) { fileName = appName + QLatin1String("rc"); if (type && *type) resourceType = type; // only change it if it's not empty file = KStandardDirs::locateLocal(resourceType, fileName, componentData); } } else if (wantGlobals()) { // accessing "kdeglobals" resourceType = "config"; fileName = QLatin1String("kdeglobals"); file = sGlobalFileName; } } else if (QDir::isAbsolutePath(fileName)) file = fileName; else { if (type && *type) resourceType = type; // only change it if it's not empty file = KStandardDirs::locateLocal(resourceType, fileName, componentData); if (fileName == QLatin1String("kdeglobals")) openFlags |= KConfig::IncludeGlobals; } bForceGlobal = (fileName == QLatin1String("kdeglobals")); if (file.isEmpty()) { openFlags = KConfig::SimpleConfig; return; } if (bDynamicBackend || !mBackend) // allow dynamic changing of backend mBackend = KConfigBackend::create(componentData, file); else mBackend->setFilePath(file); configState = mBackend->accessMode(); } void KConfig::reparseConfiguration() { Q_D(KConfig); // Don't lose pending changes if (!d->isReadOnly() && d->bDirty) sync(); d->entryMap.clear(); d->bFileImmutable = false; // Parse all desired files from the least to the most specific. if (d->wantGlobals()) d->parseGlobalFiles(); d->parseConfigFiles(); } void KConfigPrivate::parseGlobalFiles() { // qDebug() << "parsing global files" << globalFiles; // TODO: can we cache the values in etc_kderc / other global files // on a per-application basis? const QByteArray utf8Locale = locale.toUtf8(); foreach(const QString& file, globalFiles) { KConfigBackend::ParseOptions parseOpts = KConfigBackend::ParseGlobal|KConfigBackend::ParseExpansions; if (file != sGlobalFileName) parseOpts |= KConfigBackend::ParseDefaults; KSharedPtr backend = KConfigBackend::create(componentData, file); if ( backend->parseConfig( utf8Locale, entryMap, parseOpts) == KConfigBackend::ParseImmutable) break; } } void KConfigPrivate::parseConfigFiles() { if (fileName == QLatin1String("kdeglobals") && wantGlobals()) return; // already parsed in parseGlobalFiles() // can only read the file if there is a backend and a file name if (mBackend && !fileName.isEmpty()) { // don't do variable expansion for .desktop files bool allowExecutableValues = (qstrcmp(resourceType, "config") == 0) || !fileName.endsWith(".desktop"); bFileImmutable = false; QList files; if (wantDefaults()) foreach (const QString& f, componentData.dirs()->findAllResources(resourceType, fileName)) files.prepend(f); else files << mBackend->filePath(); if (!isSimple()) files = extraFiles.toList() + files; // qDebug() << "parsing local files" << files; const QByteArray utf8Locale = locale.toUtf8(); foreach(const QString& file, files) { KConfigBackend::ParseOptions parseOpts; if (allowExecutableValues) parseOpts |= KConfigBackend::ParseExpansions; if (file == mBackend->filePath()) { KConfigBackend::ParseInfo info = mBackend->parseConfig(utf8Locale, entryMap, parseOpts); if (info == KConfigBackend::ParseImmutable) bFileImmutable = true; else if (info == KConfigBackend::ParseOpenError) configState = KConfigBase::NoAccess; else // some other error occurred ; // FIXME what to do here? } else { parseOpts |= KConfigBackend::ParseDefaults; KSharedPtr backend = KConfigBackend::create(componentData, file); bFileImmutable = (backend->parseConfig(utf8Locale, entryMap, parseOpts) == KConfigBackend::ParseImmutable); } if (bFileImmutable) break; } if (componentData.dirs()->isRestrictedResource(resourceType, fileName)) bFileImmutable = true; } } KConfig::AccessMode KConfig::accessMode() const { Q_D(const KConfig); return d->configState; } /*QStringList KConfig::extraConfigFiles() const { Q_D(const KConfig); return d->extraFiles.toList(); }*/ void KConfig::addConfigSources(const QStringList& files) { Q_D(KConfig); foreach(const QString& file, files) d->extraFiles.push(file); } QString KConfig::locale() const { Q_D(const KConfig); return d->locale; } bool KConfigPrivate::setLocale(const QString& aLocale) { if (aLocale != locale) { locale = aLocale; return true; } return false; } bool KConfig::setLocale(const QString& locale) { Q_D(KConfig); if (d->setLocale(locale)) { reparseConfiguration(); return true; } return false; } void KConfig::setReadDefaults(bool b) { Q_D(KConfig); d->bReadDefaults = b; } bool KConfig::readDefaults() const { Q_D(const KConfig); return d->bReadDefaults; } bool KConfig::isImmutable() const { Q_D(const KConfig); return d->bFileImmutable; } bool KConfig::isGroupImmutableImpl(const QByteArray& aGroup) const { Q_D(const KConfig); return isImmutable()|d->entryMap.getEntryOption(aGroup, 0, 0, KEntryMap::EntryImmutable); } void KConfig::setForceGlobal(bool b) { Q_D(KConfig); d->bForceGlobal = b; } bool KConfig::forceGlobal() const { Q_D(const KConfig); return d->bForceGlobal; } KConfigGroup KConfig::groupImpl(const QByteArray &group) { return KConfigGroup(this, group.constData()); } const KConfigGroup KConfig::groupImpl(const QByteArray &group) const { return KConfigGroup(this, group.constData()); } KEntryMap::EntryOptions convertToOptions(KConfig::WriteConfigFlags flags) { KEntryMap::EntryOptions options=0; if (flags&KConfig::Persistent) options |= KEntryMap::EntryDirty; if (flags&KConfig::Global) options |= KEntryMap::EntryGlobal; if (flags&KConfig::Localized) options |= KEntryMap::EntryLocalized; return options; } void KConfig::deleteGroupImpl(const QByteArray &aGroup, WriteConfigFlags flags) { Q_D(KConfig); KEntryMap::EntryOptions options = convertToOptions(flags)|KEntryMap::EntryDeleted; QByteArray theGroup = aGroup + '\x1d'; QSet groups; groups << aGroup; foreach (const KEntryKey& key, d->entryMap.keys()) { if (key.mKey.isNull() && key.mGroup.startsWith(theGroup)) { groups << key.mGroup; } } bool dirty = false; foreach (const QByteArray& group, groups) { const QStringList keys = keyList(QString::fromUtf8(group)); foreach (const QString& key, keys) { if (d->canWriteEntry(group, key.toUtf8().constData())) { d->entryMap.setEntry(group, key.toUtf8(), QByteArray(), options); dirty = true; } } } if (dirty) { d->setDirty(true); } } bool KConfig::isConfigWritable(bool warnUser) { Q_D(KConfig); bool allWritable = (d->mBackend.isNull()? false: d->mBackend->isWritable()); if (warnUser && !allWritable) { QString errorMsg; // Note: We don't ask the user if we should not ask this question again because we can't save the answer. errorMsg += i18n("Please contact your system administrator."); QString cmdToExec = KStandardDirs::findExe(QString("kdialog")); if (!cmdToExec.isEmpty() && componentData().isValid()) { QProcess::execute(cmdToExec,QStringList() << "--title" << componentData().componentName() << "--msgbox" << errorMsg.toLocal8Bit()); } } d->configState = allWritable ? ReadWrite : ReadOnly; // update the read/write status return allWritable; } void KConfigPrivate::setDirty(bool b) { bDirty = b; } bool KConfig::hasGroupImpl(const QByteArray& aGroup) const { Q_D(const KConfig); return d->entryMap.hasEntry(aGroup); } bool KConfigPrivate::canWriteEntry(const QByteArray& group, const char* key, bool isDefault) const { if (bFileImmutable || entryMap.getEntryOption(group, key, KEntryMap::SearchLocalized, KEntryMap::EntryImmutable)) return isDefault; return true; } void KConfigPrivate::putData( const QByteArray& group, const char* key, const QByteArray& value, KConfigBase::WriteConfigFlags flags, bool expand) { // the KConfig object is dirty now // set this before any IO takes place so that if any derivative // classes do caching, they won't try and flush the cache out // from under us before we read. A race condition is still // possible but minimized. if( flags & KConfigBase::Persistent ) setDirty(true); KEntryMap::EntryOptions options = convertToOptions(flags); if (bForceGlobal) options |= KEntryMap::EntryGlobal; if (expand) options |= KEntryMap::EntryExpansion; if (value.isNull()) // deleting entry options |= KEntryMap::EntryDeleted; entryMap.setEntry(group, key, value, options); } QByteArray KConfigPrivate::lookupData(const QByteArray& group, const char* key, KEntryMap::SearchFlags flags) const { if (bReadDefaults) flags |= KEntryMap::SearchDefaults; const KEntryMapConstIterator it = entryMap.findEntry(group, key, flags); if (it == entryMap.constEnd()) return QByteArray(); return it->mValue; } QString KConfigPrivate::lookupData(const QByteArray& group, const char* key, KEntryMap::SearchFlags flags, bool *expand) const { if (bReadDefaults) flags |= KEntryMap::SearchDefaults; return entryMap.getEntry(group, key, QString(), flags, expand); } void KConfig::virtual_hook(int /*id*/, void* /*data*/) { /* nothing */ } diff --git a/kdecore/tests/kconfigtest.cpp b/kdecore/tests/kconfigtest.cpp index 4e6e5f6cc8..814b047b62 100644 --- a/kdecore/tests/kconfigtest.cpp +++ b/kdecore/tests/kconfigtest.cpp @@ -1,939 +1,937 @@ /* 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 #include "kconfigtest.h" #include #include #include "kconfigtest.moc" #include #include #include #include #include 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 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 QDir::homePath()+"/foo" #define HOMEPATHESCAPE QDir::homePath()+"/foo/$HOME" #define SUBGROUPLIST (QStringList() << "SubGroup/3" << "SubGroup1" << "SubGroup2") #define PARENTGROUPKEYS (QStringList() << "parentgrpstring") #define SUBGROUP3KEYS (QStringList() << "sub3string") #define DOLLARGROUP "$i" void KConfigTest::initTestCase() { // 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 nonexistant entry cg.writeEntry( "byteArrayEntry1", QByteArray( STRINGENTRY1 ), KConfig::Global|KConfig::Persistent ); cg.writeEntry( "doubleEntry1", DOUBLEENTRY ); sc.deleteGroup("deleteMe"); // deleting a nonexistant 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(&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" ); sc.sync(); 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() { QDir local = QDir::homePath() + "/.kde-unit-test/share/config"; foreach(const QString &file, local.entryList(QDir::Files)) if(!local.remove(file)) qWarning("%s: removing failed", qPrintable( file )); QCOMPARE((int)local.entryList(QDir::Files).count(), 0); local.cdUp(); local.rmpath("config"); } // ### TODO: call this, and test the state of things afterwards void KConfigTest::revertEntries() { // qDebug("Reverting entries"); KConfig sc( "kconfigtest" ); KConfigGroup cg(&sc, "Hello"); cg.revertToDefault( "boolEntry1" ); cg.revertToDefault( "boolEntry2" ); cg.revertToDefault( "Test" ); cg.revertToDefault( "emptyEntry" ); cg.revertToDefault( "stringEntry1" ); cg.revertToDefault( "stringEntry2" ); cg.revertToDefault( "stringEntry3" ); cg.revertToDefault( "stringEntry4" ); cg.revertToDefault( "stringEntry5" ); sc.sync(); } void KConfigTest::testRevertAllEntries() { // this tests the case were we revert (delete) all entries in a file, // leaving a blank file { KConfig sc( "konfigtest2" ); KConfigGroup cg( &sc, "Hello" ); cg.writeEntry( "Test", "Correct" ); } { KConfig sc( "konfigtest2" ); KConfigGroup cg( &sc, "Hello" ); QCOMPARE( cg.readEntry( "Test", "Default" ), QString("Correct") ); cg.revertToDefault( "Test" ); } KConfig sc( "konfigtest2" ); 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 != ""); 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 ); } void KConfigTest::testDefaults() { KConfig config("defaulttest", KConfig::NoGlobals); #define DEFAULTS "defaulttest.defaults" KConfig defaults(DEFAULTS, 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", DEFAULTS)); config.reparseConfiguration(); 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()); #undef DEFAULTS } 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::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"); QString p = sc3.readPathEntry("homepath", QString()); QCOMPARE( sc3.readPathEntry( "homepath", QString() ), HOMEPATH ); QCOMPARE( sc3.readPathEntry( "homepathescape", QString() ), HOMEPATHESCAPE ); { 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 << "URL[$e]=file://$HOME/foo" << endl << "hostname[$e]=$(hostname)" << endl; } 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("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 } 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)); } 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); newGroup.changeGroup("FooBar"); // deprecated! QCOMPARE(newGroup.name(), QString("FooBar")); QCOMPARE(sc3.name(), QString("Hello")); // unchanged } void KConfigTest::testDelete() { KConfig sc( "kconfigtest" ); KConfigGroup sc3(&sc, "Hello"); sc3.deleteEntry("Test"); QCOMPARE( sc3.readEntry("Test", QString("Fietsbel")), QString("Fietsbel") ); KConfigGroup ct(&sc, "Complex Types"); KConfigGroup ng(&ct, "Nested Group 2"); sc.deleteGroup("Complex Types"); QCOMPARE(sc.group("Complex Types").keyList().count(), 0); QVERIFY(sc.group("Complex Types").exists()); // yep, we deleted it, but it still "exists"... 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 // 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|")) count++; QVERIFY(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")); } #ifdef Q_OS_UNIX QList lines = readLines(); QVERIFY(!lines.contains("[]")); QCOMPARE(lines.first(), QByteArray("TestKey=defaultGroup\n")); #endif defaultGroup.deleteGroup(); sc.sync(); // Test if deleteGroup worked #ifdef Q_OS_UNIX lines = readLines(); QVERIFY(lines.first() != QByteArray("TestKey=defaultGroup\n")); #endif } 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")); } #ifdef Q_OS_UNIX QList lines = readLines(); QVERIFY(!lines.contains("[]")); // there's no support for the [] group, in fact. QCOMPARE(lines.first(), QByteArray("TestKey=emptyGroup\n")); #endif emptyGroup.deleteGroup(); sc.sync(); // Test if deleteGroup worked #ifdef Q_OS_UNIX lines = readLines(); QVERIFY(lines.first() != QByteArray("TestKey=defaultGroup\n")); #endif } 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 cg2 = cg1.group("subgroup"); QVERIFY(cg2.isImmutable()); } 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")); QCOMPARE(cg.groupList(), SUBGROUPLIST ); QCOMPARE(subcg3.keyList(), SUBGROUP3KEYS); QCOMPARE(cg.keyList(), PARENTGROUPKEYS); QCOMPARE(QStringList(cg.entryMap().keys()), PARENTGROUPKEYS); QCOMPARE(QStringList(subcg3.entryMap().keys()), SUBGROUP3KEYS); } void KConfigTest::testKdeglobals() { const QString kdeglobals = KStandardDirs::locateLocal("config", "kdeglobals"); KConfig sc("kdeglobals"); QVERIFY(sc.forceGlobal()); QVERIFY(sc.name() == QLatin1String("kdeglobals")); KConfig sc2("", KConfig::IncludeGlobals); QVERIFY(sc2.forceGlobal()); QVERIFY(sc2.name() == QLatin1String("kdeglobals")); KConfig sc3("kdeglobals", KConfig::SimpleConfig); QVERIFY(sc3.forceGlobal()); QVERIFY(sc3.name() == QLatin1String("kdeglobals")); } 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::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")); - group2.writeEntry("AnotherKey", "Test Worked"); // for sync to have something to sync 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")); - QCOMPARE(group3.readEntry("AnotherKey"), QString("Test Worked")); } 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" ) ); } QList KConfigTest::readLines() { const QString path = KStandardDirs::locateLocal("config", "kconfigtest"); Q_ASSERT(!path.isEmpty()); QFile file(path); Q_ASSERT(file.open(QIODevice::ReadOnly)); QList lines; QByteArray line; do { line = file.readLine(); if (!line.isEmpty()) lines.append(line); } while(!line.isEmpty()); return lines; } 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)); } 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"); }