From kde-commits Fri Nov 26 23:28:30 2010 From: David Faure Date: Fri, 26 Nov 2010 23:28:30 +0000 To: kde-commits Subject: KDE/kdelibs/kdecore Message-Id: <20101126232830.95E4CAC8A2 () svn ! kde ! org> X-MARC-Message: https://marc.info/?l=kde-commits&m=129081416331854 SVN commit 1201149 by dfaure: Ensure that KStandardDirs always returns resolved paths (when $HOME or $KDEHOME is a symlink), even when returning paths that don't exist yet. Otherwise, via caches (the kstandarddirs one or in applications), we can end up with a different path once it does exist, and comparisons will fail (e.g. the one in KConfigPrivate::parseConfigFiles which detects the local file). (So this fixes "kconfigtest testRevertAllEntries" when $HOME is a symlink, but has its own setup-independent unittest now) M +22 -3 kernel/kstandarddirs.cpp M +33 -0 tests/kstandarddirstest.cpp M +1 -0 tests/kstandarddirstest.h --- trunk/KDE/kdelibs/kdecore/kernel/kstandarddirs.cpp #1201148:1201149 @@ -923,9 +923,28 @@ return QFile::decodeName(realpath_buffer); } - if (!dirname.endsWith(QLatin1Char('/'))) - return dirname + QLatin1Char('/'); - return dirname; + // Does not exist yet; resolve symlinks in parent dirs then. + // This ensures that once the directory exists, it will still be resolved + // the same way, so that the general rule that KStandardDirs always returns + // canonical paths stays true, and app code can compare paths more easily. + QString dir = dirname; + if (!dir.endsWith(QLatin1Char('/'))) + dir += QLatin1Char('/'); + QString relative; + while (!KStandardDirs::exists(dir)) { + //qDebug() << "does not exist:" << dir; + const int pos = dir.lastIndexOf(QLatin1Char('/'), -2); + Q_ASSERT(pos > 0); // what? even "/" doesn't exist? + relative.prepend(dir.mid(pos+1)); // keep "subdir/" + dir = dir.left(pos+1); + Q_ASSERT(dir.endsWith(QLatin1Char('/'))); + } + Q_ASSERT(!relative.isEmpty()); // infinite recursion ahead + if (!relative.isEmpty()) { + //qDebug() << "done, resolving" << dir << "and adding" << relative; + dir = realPath(dir) + relative; + } + return dir; #endif } --- trunk/KDE/kdelibs/kdecore/tests/kstandarddirstest.cpp #1201148:1201149 @@ -26,6 +26,7 @@ #include #include #include +#include #include "config-prefix.h" #include #include @@ -407,6 +408,38 @@ localFile.remove(); } +void KStandarddirsTest::testSymlinkResolution() +{ +#ifndef Q_OS_WIN + // This makes the save location for the david resource, "$HOME/.kde-unit-test/symlink/test/" + // where symlink points to "real", and the subdir test will be created later + // This used to confuse KStandardDirs and make it return unresolved paths, + // and thus making comparisons fail later on in KConfig. + const QString symlink = m_kdehome + "/symlink"; + const QString expected = m_kdehome + "/real/test/"; + QVERIFY(KTempDir::removeDir(m_kdehome + "/real")); + QVERIFY(QDir(m_kdehome).mkdir("real")); + QFile::remove(symlink); + QVERIFY(!QFile::exists(symlink)); + QVERIFY(QFile::link("real", symlink)); + QVERIFY(QFileInfo(symlink).isSymLink()); + QVERIFY(!QFile::exists(expected)); + KGlobal::dirs()->addResourceType("david", 0, "symlink/test"); + QVERIFY(!QFile::exists(expected)); + const QString saveLoc = KGlobal::dirs()->resourceDirs("david").first(); + QVERIFY(!QFile::exists(expected)); + // The issue at this point is that saveLoc does not actually exist yet. + QVERIFY(QDir(saveLoc).canonicalPath().isEmpty()); // this is why we can't use canonicalPath + QVERIFY(!QFile::exists(saveLoc)); + QCOMPARE(saveLoc, KStandardDirs::realPath(saveLoc)); // must be resolved + QCOMPARE(saveLoc, expected); + QVERIFY(QDir(m_kdehome).mkpath("real/test")); // KConfig calls mkdir on its own, we simulate that here + const QString sameSaveLoc = KGlobal::dirs()->resourceDirs("david").first(); + QCOMPARE(sameSaveLoc, saveLoc); + QCOMPARE(sameSaveLoc, KGlobal::dirs()->saveLocation("david")); +#endif +} + #include #include --- trunk/KDE/kdelibs/kdecore/tests/kstandarddirstest.h #1201148:1201149 @@ -42,6 +42,7 @@ void testAddResourceDir(); void testSetXdgDataDirs(); void testRestrictedResources(); + void testSymlinkResolution(); void testThreads(); private: