kdelibs/kdecore/tests/kconfigtest.cpp
Ivailo Monev fa51ecdfaf kdecore: remove unused KAboutData methods and redundant constructor arguments
the homepage and the bugs email address shall always be defined in
kglobalsettings header for official Katana applications, libraries, etc.
the option to override them is still a thing

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2023-08-10 17:41:54 +03:00

1597 lines
57 KiB
C++

/* 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 <qtest_kde.h>
#include <ktempdir.h>
#include <kdesktopfile.h>
#include <kstandarddirs.h>
#include <kconfig.h>
#include <kdebug.h>
#include <kconfiggroup.h>
#include <QTextStream>
#include <future>
#include <utime.h>
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<int>() << 1 << 2 << 3 << 4
#define BYTEARRAYLISTENTRY1 QList<QByteArray>() << "" << "1,2" << "end"
#define VARIANTLISTENTRY (QVariantList() << true << false << QString("joe") << 10023)
#define VARIANTLISTENTRY2 (QVariantList() << POINTENTRY << SIZEENTRY)
#define HOMEPATH QString(QDir::homePath()+"/foo")
#define HOMEPATHENV QString(QDir::homePath()+"/foo/$HOME")
#define HOMEPATHENVEXPANDED QString(QDir::homePath()+"/foo/"+QDir::homePath())
#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( "homepathenv", HOMEPATHENV );
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<QByteArray> readLinesFrom(const QString& path)
{
QFile file(path);
const bool opened = file.open(QIODevice::ReadOnly|QIODevice::Text);
Q_ASSERT(opened);
Q_UNUSED(opened);
QList<QByteArray> lines;
QByteArray line;
do {
line = file.readLine();
if (!line.isEmpty())
lines.append(line);
} while(!line.isEmpty());
return lines;
}
static QList<QByteArray> 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 != "<default>");
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<QByteArray> 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<int> expectedIntList = INTLISTENTRY1;
QVERIFY( sc3.readEntry( "listOfIntsEntry1", QList<int>() ) == expectedIntList );
QCOMPARE( QVariant(sc3.readEntry( "variantListEntry", VARIANTLISTENTRY )).toStringList(),
QVariant(VARIANTLISTENTRY).toStringList() );
QCOMPARE( sc3.readEntry( "listOfByteArraysEntry1", QList<QByteArray>()), BYTEARRAYLISTENTRY1 );
}
void KConfigTest::testPath()
{
KConfig sc2( "kconfigtest" );
KConfigGroup sc3(&sc2, "Path Type");
QCOMPARE( sc3.readPathEntry( "homepath", QString() ), HOMEPATH );
QCOMPARE( sc3.readPathEntry( "homepathenv", QString() ), HOMEPATHENVEXPANDED );
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) );
QVERIFY(group.hasKey("hostname"));
QCOMPARE(group.readEntry("hostname", QString()), QString("$(hostname)"));
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<QString, QString> 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<int> 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("<default>"));
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<QByteArray> 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<QByteArray> 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, "<default>");
QCOMPARE(defaultGroup.name(), QString("<default>"));
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, "<default>");
QCOMPARE(defaultGroup2.name(), QString("<default>"));
QVERIFY(defaultGroup2.exists());
QCOMPARE(defaultGroup2.readEntry("TestKey", QString()), QString("defaultGroup"));
}
{
// Test reading it
KConfig sc2("kconfigtest");
KConfigGroup emptyGroup(&sc2, "");
QCOMPARE(emptyGroup.name(), QString("<default>"));
QVERIFY(emptyGroup.exists());
QCOMPARE(emptyGroup.readEntry("TestKey", QString()), QString("defaultGroup"));
}
QList<QByteArray> 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 != "<default>");
}
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("<default>")); // 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, "<default>");
QCOMPARE(defaultGroup.name(), QString("<default>"));
QVERIFY(defaultGroup.exists());
QCOMPARE(defaultGroup.readEntry("TestKey", QString()), QString("emptyGroup"));
}
{
// Test reading it
KConfig sc2("kconfigtest");
KConfigGroup emptyGroup2(&sc2, "");
QCOMPARE(emptyGroup2.name(), QString("<default>"));
QVERIFY(emptyGroup2.exists());
QCOMPARE(emptyGroup2.readEntry("TestKey", QString()), QString("emptyGroup"));
}
QList<QByteArray> 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 != "<default>");
}
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<QByteArray> 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<QByteArray> 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 != "<default>");
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<QByteArray> 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); // 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<QString, QString> 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);
}
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<QByteArray> 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);
QDir kdeDir(kdeHome);
QVERIFY(!kdeDir.exists());
// Do what kde4-config does, and ensure kdehome doesn't get created (#233892)
KComponentData componentData("KConfigTest");
QVERIFY(!kdeDir.exists());
componentData.dirs();
QVERIFY(!kdeDir.exists());
componentData.config();
QVERIFY(!kdeDir.exists());
// Now try to actually save something, see if it works.
KConfigGroup group(componentData.config(), "Group");
group.writeEntry("Key", "Value");
group.sync();
QVERIFY(kdeDir.exists());
QVERIFY(QFile::exists(kdeHome + "/share/config/KConfigTestrc"));
// Cleanup
KTempDir::removeDir(kdeHome);
}
// To find multithreading bugs: valgrind --tool=helgrind --track-lockorders=no ./kconfigtest testThreads
void KConfigTest::testThreads()
{
// Run in parallel some tests that work on different config files,
// otherwise unexpected things might indeed happen.
std::future<void> future1 = std::async(std::launch::async, &KConfigTest::testAddConfigSources, this);
std::future<void> future2 = std::async(std::launch::async, &KConfigTest::testSimple, this);
std::future<void> future3 = std::async(std::launch::async, &KConfigTest::testDefaults, this);
// QEXPECT_FAIL triggers race conditions...
// std::future<void> future4 = std::async(std::launch::async, &KConfigTest::testDeleteWhenLocalized, this);
// std::future<void> future5 = std::async(std::launch::async, &KConfigTest::testEntryMap, this);
kDebug() << "Joining all threads";
future1.wait();
future2.wait();
future3.wait();
// future4.wait();
// future5.wait();
}
#include "moc_kconfigtest.cpp"