summaryrefslogtreecommitdiffstats
path: root/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp')
-rw-r--r--tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp544
1 files changed, 410 insertions, 134 deletions
diff --git a/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp b/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp
index cba7dfe9fc..4bb7042790 100644
--- a/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp
+++ b/tests/auto/corelib/io/qstandardpaths/tst_qstandardpaths.cpp
@@ -1,38 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtTest/QtTest>
+// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2020 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
#include <qstandardpaths.h>
+#include <QTest>
+#include <QOperatingSystemVersion>
#include <qdebug.h>
-#include <qstandardpaths.h>
#include <qfileinfo.h>
-#include <qsysinfo.h>
+#include <qplatformdefs.h>
#include <qregularexpression.h>
+#include <qsysinfo.h>
#if defined(Q_OS_WIN)
# include <qt_windows.h>
#endif
@@ -40,16 +17,61 @@
#ifdef Q_OS_UNIX
#include <unistd.h>
#include <sys/types.h>
+#include <pwd.h>
#endif
-#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_ANDROID)
+#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS) && !defined(Q_OS_ANDROID)
#define Q_XDG_PLATFORM
#endif
-#include "emulationdetector.h"
+using namespace Qt::StringLiterals;
// Update this when adding new enum values; update enumNames too
-static const int MaxStandardLocation = QStandardPaths::AppConfigLocation;
+static const int MaxStandardLocation = QStandardPaths::GenericStateLocation;
+
+static QString genericCacheLoc()
+{
+ return QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation);
+}
+static QString cacheLoc()
+{
+ return QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
+}
+
+static QString genericStateLoc()
+{
+ return QStandardPaths::writableLocation(QStandardPaths::GenericStateLocation);
+}
+static QString stateLoc()
+{
+ return QStandardPaths::writableLocation(QStandardPaths::StateLocation);
+}
+
+static QString genericDataLoc()
+{
+ return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
+}
+static QString appDataLoc()
+{
+ return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
+}
+static QString appLocalDataLoc()
+{
+ return QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
+}
+
+static QString genericConfigLoc()
+{
+ return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
+}
+static QString configLoc()
+{
+ return QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
+}
+static QString appConfigLoc()
+{
+ return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
+}
class tst_qstandardpaths : public QObject
{
@@ -58,6 +80,7 @@ class tst_qstandardpaths : public QObject
private slots:
void initTestCase();
void dump();
+ void init();
void testDefaultLocations();
void testCustomLocations();
void enableTestMode();
@@ -68,6 +91,7 @@ private slots:
void testFindExecutable();
void testFindExecutableLinkToDirectory();
void testRuntimeDirectory();
+ void testCustomRuntimeDirectory_data();
void testCustomRuntimeDirectory();
void testAllWritableLocations_data();
void testAllWritableLocations();
@@ -83,14 +107,17 @@ private:
qputenv("XDG_CONFIG_DIRS", QFile::encodeName(m_globalConfigDir));
m_localAppDir = m_localAppTempDir.path();
m_globalAppDir = m_globalAppTempDir.path();
+ m_stateDir = m_stateTempDir.path();
qputenv("XDG_DATA_HOME", QFile::encodeName(m_localAppDir));
qputenv("XDG_DATA_DIRS", QFile::encodeName(m_globalAppDir));
+ qputenv("XDG_STATE_HOME", QFile::encodeName(m_stateDir));
}
void setDefaultLocations() {
- qputenv("XDG_CONFIG_HOME", QByteArray());
- qputenv("XDG_CONFIG_DIRS", QByteArray());
- qputenv("XDG_DATA_HOME", QByteArray());
- qputenv("XDG_DATA_DIRS", QByteArray());
+ qputenv("XDG_CONFIG_HOME", nullptr);
+ qputenv("XDG_CONFIG_DIRS", nullptr);
+ qputenv("XDG_DATA_HOME", nullptr);
+ qputenv("XDG_DATA_DIRS", nullptr);
+ qputenv("XDG_STATE_HOME", nullptr);
}
#endif
@@ -105,6 +132,8 @@ private:
QTemporaryDir m_localAppTempDir;
QString m_globalAppDir;
QTemporaryDir m_globalAppTempDir;
+ QString m_stateDir;
+ QTemporaryDir m_stateTempDir;
};
static const char * const enumNames[MaxStandardLocation + 1 - int(QStandardPaths::DesktopLocation)] = {
@@ -117,7 +146,7 @@ static const char * const enumNames[MaxStandardLocation + 1 - int(QStandardPaths
"PicturesLocation",
"TempLocation",
"HomeLocation",
- "DataLocation",
+ "AppLocalDataLocation",
"CacheLocation",
"GenericDataLocation",
"RuntimeLocation",
@@ -126,7 +155,11 @@ static const char * const enumNames[MaxStandardLocation + 1 - int(QStandardPaths
"GenericCacheLocation",
"GenericConfigLocation",
"AppDataLocation",
- "AppConfigLocation"
+ "AppConfigLocation",
+ "PublicShareLocation",
+ "TemplatesLocation",
+ "StateLocation",
+ "GenericStateLocation"
};
void tst_qstandardpaths::initTestCase()
@@ -145,6 +178,7 @@ void tst_qstandardpaths::initTestCase()
QVERIFY2(m_globalConfigTempDir.isValid(), qPrintable(m_globalConfigTempDir.errorString()));
QVERIFY2(m_localAppTempDir.isValid(), qPrintable(m_localAppTempDir.errorString()));
QVERIFY2(m_globalAppTempDir.isValid(), qPrintable(m_globalAppTempDir.errorString()));
+ QVERIFY2(m_stateTempDir.isValid(), qPrintable(m_stateTempDir.errorString()));
}
void tst_qstandardpaths::dump()
@@ -161,25 +195,37 @@ void tst_qstandardpaths::dump()
}
}
+void tst_qstandardpaths::init()
+{
+ // Some unittests set a custom org/app names, restore the original ones
+ // before each unittest is run
+ static const QString org = QCoreApplication::organizationName();
+ static const QString app = QCoreApplication::applicationName();
+ QCoreApplication::setOrganizationName(org);
+ QCoreApplication::setApplicationName(app);
+}
+
void tst_qstandardpaths::testDefaultLocations()
{
#ifdef Q_XDG_PLATFORM
setDefaultLocations();
const QString expectedConfHome = QDir::homePath() + QString::fromLatin1("/.config");
- QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation), expectedConfHome);
- QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation), expectedConfHome);
+ QCOMPARE(configLoc(), expectedConfHome);
+ QCOMPARE(genericConfigLoc(), expectedConfHome);
const QStringList confDirs = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation);
- QCOMPARE(confDirs.count(), 2);
+ QCOMPARE(confDirs.size(), 2);
QVERIFY(confDirs.contains(expectedConfHome));
QCOMPARE(QStandardPaths::standardLocations(QStandardPaths::GenericConfigLocation), confDirs);
const QStringList genericDataDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
- QCOMPARE(genericDataDirs.count(), 3);
+ QCOMPARE(genericDataDirs.size(), 3);
const QString expectedDataHome = QDir::homePath() + QString::fromLatin1("/.local/share");
QCOMPARE(genericDataDirs.at(0), expectedDataHome);
QCOMPARE(genericDataDirs.at(1), QString::fromLatin1("/usr/local/share"));
QCOMPARE(genericDataDirs.at(2), QString::fromLatin1("/usr/share"));
+ const QString expectedGenericStateLocation = QDir::homePath() + QString::fromLatin1("/.local/state");
+ QCOMPARE(genericStateLoc(), expectedGenericStateLocation);
#endif
}
@@ -198,8 +244,8 @@ void tst_qstandardpaths::testCustomLocations()
setCustomLocations();
// test writableLocation()
- QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation), m_localConfigDir);
- QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation), m_localConfigDir);
+ QCOMPARE(configLoc(), m_localConfigDir);
+ QCOMPARE(genericConfigLoc(), m_localConfigDir);
// test locate()
const QString thisFileName = QString::fromLatin1("aFile");
@@ -231,37 +277,76 @@ void tst_qstandardpaths::enableTestMode()
setCustomLocations(); // for the global config dir
const QString qttestDir = QDir::homePath() + QLatin1String("/.qttest");
- // ConfigLocation
+ // *Config*Location
const QString configDir = qttestDir + QLatin1String("/config");
- QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation), configDir);
- QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation), configDir);
+ QCOMPARE(configLoc(), configDir);
+ QCOMPARE(genericConfigLoc(), configDir);
const QStringList confDirs = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation);
QCOMPARE(confDirs, QStringList() << configDir << m_globalConfigDir);
+ // AppConfigLocation should be "GenericConfigLocation/organization-name/app-name"
+ QCOMPARE(appConfigLoc(), configDir + "/tst_qstandardpaths"_L1);
- // GenericDataLocation
+ // *Data*Location
const QString dataDir = qttestDir + QLatin1String("/share");
- QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation), dataDir);
+ QCOMPARE(genericDataLoc(), dataDir);
const QStringList gdDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
QCOMPARE(gdDirs, QStringList() << dataDir << m_globalAppDir);
+ // AppDataLocation/AppLocalDataLocation should be
+ // "GenericDataLocation/organization-name/app-name"
+ QCOMPARE(appDataLoc(), dataDir + "/tst_qstandardpaths"_L1);
+ QCOMPARE(appLocalDataLoc(), dataDir + "/tst_qstandardpaths"_L1);
- // GenericCacheLocation
+ // *CacheLocation
const QString cacheDir = qttestDir + QLatin1String("/cache");
- QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation), cacheDir);
+ QCOMPARE(genericCacheLoc(), cacheDir);
const QStringList cacheDirs = QStandardPaths::standardLocations(QStandardPaths::GenericCacheLocation);
QCOMPARE(cacheDirs, QStringList() << cacheDir);
+ // CacheLocation should be "GenericCacheLocation/organization-name/app-name"
+ QCOMPARE(cacheLoc(), cacheDir + "/tst_qstandardpaths"_L1);
+
+ // *StateLocation
+ const QString stateDir = qttestDir + QLatin1String("/state");
+ QCOMPARE(genericStateLoc(), stateDir);
+ const QStringList stateDirs = QStandardPaths::standardLocations(QStandardPaths::GenericStateLocation);
+ QCOMPARE(stateDirs, QStringList() << stateDir);
+ // StateLocation should be "GenericStateLocation/organization-name/app-name"
+ QCOMPARE(stateLoc(), stateDir + "/tst_qstandardpaths"_L1);
+
+ QCoreApplication::setOrganizationName("Qt");
+ QCOMPARE(appConfigLoc(), configDir + "/Qt/tst_qstandardpaths"_L1);
+ QCOMPARE(appDataLoc(), dataDir + "/Qt/tst_qstandardpaths"_L1);
+ QCOMPARE(appLocalDataLoc(), dataDir + "/Qt/tst_qstandardpaths"_L1);
+ QCOMPARE(cacheLoc(), cacheDir + "/Qt/tst_qstandardpaths"_L1);
+ QCOMPARE(stateLoc(), stateDir + "/Qt/tst_qstandardpaths"_L1);
+
+ QCoreApplication::setApplicationName("QtTest");
+ QCOMPARE(appConfigLoc(), configDir + "/Qt/QtTest"_L1);
+ QCOMPARE(appDataLoc(), dataDir + "/Qt/QtTest"_L1);
+ QCOMPARE(appLocalDataLoc(), dataDir + "/Qt/QtTest"_L1);
+ QCOMPARE(cacheLoc(), cacheDir + "/Qt/QtTest"_L1);
+ QCOMPARE(stateLoc(), stateDir + "/Qt/QtTest"_L1);
+
+ // Check these are unaffected by org/app names
+ QCOMPARE(genericConfigLoc(), configDir);
+ QCOMPARE(configLoc(), configDir);
+ QCOMPARE(genericDataLoc(), dataDir);
+ QCOMPARE(genericCacheLoc(), cacheDir);
+ QCOMPARE(genericStateLoc(), stateDir);
#endif
// On all platforms, we want to ensure that the writableLocation is different in test mode and real mode.
// Check this for locations where test programs typically write. Not desktop, download, music etc...
typedef QHash<QStandardPaths::StandardLocation, QString> LocationHash;
LocationHash testLocations;
- testLocations.insert(QStandardPaths::AppDataLocation, QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
- testLocations.insert(QStandardPaths::AppLocalDataLocation, QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation));
- testLocations.insert(QStandardPaths::GenericDataLocation, QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation));
- testLocations.insert(QStandardPaths::ConfigLocation, QStandardPaths::writableLocation(QStandardPaths::ConfigLocation));
- testLocations.insert(QStandardPaths::GenericConfigLocation, QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation));
- testLocations.insert(QStandardPaths::CacheLocation, QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
- testLocations.insert(QStandardPaths::GenericCacheLocation, QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation));
+ testLocations.insert(QStandardPaths::AppDataLocation, appDataLoc());
+ testLocations.insert(QStandardPaths::AppLocalDataLocation, appLocalDataLoc());
+ testLocations.insert(QStandardPaths::GenericDataLocation, genericDataLoc());
+ testLocations.insert(QStandardPaths::ConfigLocation, configLoc());
+ testLocations.insert(QStandardPaths::GenericConfigLocation, genericConfigLoc());
+ testLocations.insert(QStandardPaths::CacheLocation, cacheLoc());
+ testLocations.insert(QStandardPaths::GenericCacheLocation, genericCacheLoc());
+ testLocations.insert(QStandardPaths::StateLocation, stateLoc());
+ testLocations.insert(QStandardPaths::GenericStateLocation, genericStateLoc());
// On Windows, what should "Program Files" become, in test mode?
//testLocations.insert(QStandardPaths::ApplicationsLocation, QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation));
@@ -283,7 +368,7 @@ void tst_qstandardpaths::testLocateAll()
#ifdef Q_XDG_PLATFORM
setCustomLocations();
const QStringList appsDirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, "applications", QStandardPaths::LocateDirectory);
- QCOMPARE(appsDirs.count(), 0); // they don't exist yet
+ QCOMPARE(appsDirs.size(), 0); // they don't exist yet
const QStringList expectedAppsDirs = QStringList() << m_localAppDir + QLatin1String("/applications")
<< m_globalAppDir + QLatin1String("/applications");
QDir().mkdir(expectedAppsDirs.at(0));
@@ -306,33 +391,29 @@ void tst_qstandardpaths::testLocateAll()
void tst_qstandardpaths::testDataLocation()
{
- // On all platforms, DataLocation should be GenericDataLocation / organization name / app name
+ // On all platforms, AppLocalDataLocation should be GenericDataLocation / organization name / app name
// This allows one app to access the data of another app.
// Android is an exception to this case, owing to the fact that
// applications are sandboxed.
#if !defined(Q_OS_ANDROID)
- const QString base = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation);
- QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation), base + "/tst_qstandardpaths");
+ const QString base = genericDataLoc();
+ QCOMPARE(appLocalDataLoc(), base + "/tst_qstandardpaths");
QCoreApplication::instance()->setOrganizationName("Qt");
- QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation), base + "/Qt/tst_qstandardpaths");
+ QCOMPARE(appLocalDataLoc(), base + "/Qt/tst_qstandardpaths");
QCoreApplication::instance()->setApplicationName("QtTest");
- QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation), base + "/Qt/QtTest");
+ QCOMPARE(appLocalDataLoc(), base + "/Qt/QtTest");
#endif
#ifdef Q_XDG_PLATFORM
setDefaultLocations();
const QString expectedAppDataDir = QDir::homePath() + QString::fromLatin1("/.local/share/Qt/QtTest");
- QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation), expectedAppDataDir);
+ QCOMPARE(appLocalDataLoc(), expectedAppDataDir);
const QStringList appDataDirs = QStandardPaths::standardLocations(QStandardPaths::AppLocalDataLocation);
- QCOMPARE(appDataDirs.count(), 3);
+ QCOMPARE(appDataDirs.size(), 3);
QCOMPARE(appDataDirs.at(0), expectedAppDataDir);
QCOMPARE(appDataDirs.at(1), QString::fromLatin1("/usr/local/share/Qt/QtTest"));
QCOMPARE(appDataDirs.at(2), QString::fromLatin1("/usr/share/Qt/QtTest"));
#endif
-
- // reset for other tests
- QCoreApplication::setOrganizationName(QString());
- QCoreApplication::setApplicationName(QString());
}
void tst_qstandardpaths::testAppConfigLocation()
@@ -340,15 +421,12 @@ void tst_qstandardpaths::testAppConfigLocation()
// On all platforms where applications are not sandboxed,
// AppConfigLocation should be GenericConfigLocation / organization name / app name
#if !defined(Q_OS_ANDROID)
- const QString base = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
- QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation), base + "/tst_qstandardpaths");
+ const QString base = genericConfigLoc();
+ QCOMPARE(appConfigLoc(), base + "/tst_qstandardpaths");
QCoreApplication::setOrganizationName("Qt");
- QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation), base + "/Qt/tst_qstandardpaths");
+ QCOMPARE(appConfigLoc(), base + "/Qt/tst_qstandardpaths");
QCoreApplication::setApplicationName("QtTest");
- QCOMPARE(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation), base + "/Qt/QtTest");
- // reset for other tests
- QCoreApplication::setOrganizationName(QString());
- QCoreApplication::setApplicationName(QString());
+ QCOMPARE(appConfigLoc(), base + "/Qt/QtTest");
#endif
}
@@ -361,9 +439,9 @@ static inline QFileInfo findSh()
QByteArray pEnv = qgetenv("PATH");
const QLatin1Char pathSep(':');
const QStringList rawPaths = QString::fromLocal8Bit(pEnv.constData()).split(pathSep, Qt::SkipEmptyParts);
- foreach (const QString &path, rawPaths) {
+ for (const QString &path : rawPaths) {
if (QFile::exists(path + sh))
- return path + sh;
+ return QFileInfo(path + sh);
}
return QFileInfo();
}
@@ -455,6 +533,9 @@ void tst_qstandardpaths::testFindExecutableLinkToDirectory()
QFile::remove(target);
}
+using RuntimeDirSetup = std::optional<QString> (*)(QDir &);
+Q_DECLARE_METATYPE(RuntimeDirSetup);
+
void tst_qstandardpaths::testRuntimeDirectory()
{
#ifdef Q_XDG_PLATFORM
@@ -463,6 +544,195 @@ void tst_qstandardpaths::testRuntimeDirectory()
#endif
}
+// INTEGRITY PJF System doesn't support user ID related APIs. getpwuid is not defined.
+// testCustomRuntimeDirectory_data test will always FAIL for INTEGRITY.
+#if defined(Q_XDG_PLATFORM) && !defined(Q_OS_INTEGRITY)
+static QString fallbackXdgRuntimeDir()
+{
+ static QString username = [] {
+ struct passwd *pw = getpwuid(geteuid());
+ return QString::fromLocal8Bit(pw->pw_name);
+ }();
+
+ // QDir::temp() might change from call to call
+ return QDir::temp().filePath("runtime-" + username);
+}
+#endif
+
+[[maybe_unused]] static QString updateRuntimeDir(const QString &path)
+{
+ qputenv("XDG_RUNTIME_DIR", QFile::encodeName(path));
+ return path;
+}
+
+[[maybe_unused]] static void clearRuntimeDir()
+{
+ qunsetenv("XDG_RUNTIME_DIR");
+#ifdef Q_XDG_PLATFORM
+#if !defined(Q_OS_WASM) && !defined(Q_OS_INTEGRITY)
+ QTest::ignoreMessage(QtWarningMsg,
+ qPrintable("QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to '"
+ + fallbackXdgRuntimeDir() + '\''));
+#endif
+#endif
+}
+
+void tst_qstandardpaths::testCustomRuntimeDirectory_data()
+{
+#ifdef Q_OS_INTEGRITY
+ QSKIP("Test requires getgid/getpwuid API that are not available on INTEGRITY");
+#elif defined(Q_XDG_PLATFORM)
+ QTest::addColumn<RuntimeDirSetup>("setup");
+ auto addRow = [](const char *name, RuntimeDirSetup f) {
+ QTest::newRow(name) << f;
+ };
+
+
+# if defined(Q_OS_UNIX)
+ if (::getuid() == 0)
+ QSKIP("Running this test as root doesn't make sense");
+# endif
+
+ addRow("environment:non-existing", [](QDir &d) -> std::optional<QString> {
+ return updateRuntimeDir(d.filePath("runtime"));
+ });
+
+ addRow("environment:existing", [](QDir &d) -> std::optional<QString> {
+ QString p = d.filePath("runtime");
+ d.mkdir("runtime");
+ QFile::setPermissions(p, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner);
+ return updateRuntimeDir(p);
+ });
+
+ addRow("environment-to-existing-wrong-perm", [](QDir &d) -> std::optional<QString> {
+ QString p = d.filePath("runtime");
+ d.mkdir("runtime");
+ QFile::setPermissions(p, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner |
+ QFile::ExeGroup | QFile::ExeOther);
+ updateRuntimeDir(p);
+ QTest::ignoreMessage(QtWarningMsg,
+ QString("QStandardPaths: wrong permissions on runtime directory %1, "
+ "0711 instead of 0700")
+ .arg(p).toLatin1());
+ return fallbackXdgRuntimeDir();
+ });
+
+ addRow("environment:wrong-owner", [](QDir &) -> std::optional<QString> {
+ QT_STATBUF st;
+ QT_STAT("/", &st);
+
+ updateRuntimeDir("/");
+ QTest::ignoreMessage(QtWarningMsg,
+ QString("QStandardPaths: runtime directory '/' is not owned by UID "
+ "%1, but a directory permissions %2 owned by UID %3 GID %4")
+ .arg(getuid())
+ .arg(st.st_mode & 07777, 4, 8, QChar('0'))
+ .arg(st.st_uid)
+ .arg(st.st_gid).toLatin1());
+ return fallbackXdgRuntimeDir();
+ });
+
+ // static so that it can be used in RuntimeDirSetup callable without capturing
+ static auto failedToOpen = [](const QFile &f) {
+ qCritical("QFile::Open: failed to open '%s': %s",
+ qPrintable(f.fileName()), qPrintable(f.errorString()));
+ return std::nullopt;
+ };
+
+ addRow("environment:file", [](QDir &d) -> std::optional<QString> {
+ QString p = d.filePath("file");
+ QFile f(p);
+ if (!f.open(QIODevice::WriteOnly))
+ return failedToOpen(f);
+ f.setPermissions(QFile::ReadOwner | QFile::WriteOwner);
+
+ updateRuntimeDir(p);
+ QTest::ignoreMessage(QtWarningMsg,
+ QString("QStandardPaths: runtime directory '%1' is not a directory, "
+ "but a regular file permissions 0600 owned by UID %2 GID %3")
+ .arg(p).arg(getuid()).arg(getgid()).toLatin1());
+ return fallbackXdgRuntimeDir();
+ });
+
+ addRow("environment:broken-symlink", [](QDir &d) -> std::optional<QString> {
+ QString p = d.filePath("link");
+ QFile::link(d.filePath("this-goes-nowhere"), p);
+ updateRuntimeDir(p);
+ QTest::ignoreMessage(QtWarningMsg,
+ QString("QStandardPaths: runtime directory '%1' is not a directory, "
+ "but a broken symlink")
+ .arg(p).toLatin1());
+ return fallbackXdgRuntimeDir();
+ });
+
+ addRow("environment:symlink-to-dir", [](QDir &d) -> std::optional<QString> {
+ QString p = d.filePath("link");
+ d.mkdir("dir");
+ QFile::link(d.filePath("dir"), p);
+ QFile::setPermissions(p, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner);
+ updateRuntimeDir(p);
+ QTest::ignoreMessage(QtWarningMsg,
+ QString("QStandardPaths: runtime directory '%1' is not a directory, "
+ "but a symbolic link to a directory permissions 0700 owned by UID %2 GID %3")
+ .arg(p).arg(getuid()).arg(getgid()).toLatin1());
+ return fallbackXdgRuntimeDir();
+ });
+
+ addRow("no-environment:non-existing", [](QDir &) -> std::optional<QString> {
+ clearRuntimeDir();
+ return fallbackXdgRuntimeDir();
+ });
+
+ addRow("no-environment:existing", [](QDir &d) -> std::optional<QString> {
+ clearRuntimeDir();
+ QString p = fallbackXdgRuntimeDir();
+ d.mkdir(p); // probably has wrong permissions
+ QFile::setPermissions(p, QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner);
+ return p;
+ });
+
+ addRow("no-environment:fallback-is-file", [](QDir &) -> std::optional<QString> {
+ QString p = fallbackXdgRuntimeDir();
+ QFile f(p);
+ if (!f.open(QIODevice::WriteOnly))
+ return failedToOpen(f);
+ f.setPermissions(QFile::ReadOwner | QFile::WriteOwner);
+
+ clearRuntimeDir();
+ QTest::ignoreMessage(QtWarningMsg,
+ QString("QStandardPaths: runtime directory '%1' is not a directory, "
+ "but a regular file permissions 0600 owned by UID %2 GID %3")
+ .arg(p).arg(getuid()).arg(getgid()).toLatin1());
+ return QString();
+ });
+
+ addRow("environment-and-fallback-are-files", [](QDir &d) -> std::optional<QString> {
+ QString p = d.filePath("file1");
+ QFile f(p);
+ if (!f.open(QIODevice::WriteOnly))
+ return failedToOpen(f);
+ f.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ReadGroup);
+ updateRuntimeDir(p);
+ QTest::ignoreMessage(QtWarningMsg,
+ QString("QStandardPaths: runtime directory '%1' is not a directory, "
+ "but a regular file permissions 0640 owned by UID %2 GID %3")
+ .arg(p).arg(getuid()).arg(getgid()).toLatin1());
+
+ f.close();
+ f.setFileName(fallbackXdgRuntimeDir());
+ if (!f.open(QIODevice::WriteOnly))
+ return failedToOpen(f);
+ f.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ReadGroup);
+ QTest::ignoreMessage(QtWarningMsg,
+ QString("QStandardPaths: runtime directory '%1' is not a directory, "
+ "but a regular file permissions 0640 owned by UID %2 GID %3")
+ .arg(f.fileName()).arg(getuid()).arg(getgid()).toLatin1());
+
+ return QString();
+ });
+#endif
+}
+
void tst_qstandardpaths::testCustomRuntimeDirectory()
{
#if defined(Q_OS_UNIX)
@@ -473,63 +743,40 @@ void tst_qstandardpaths::testCustomRuntimeDirectory()
#ifdef Q_XDG_PLATFORM
struct EnvVarRestorer
{
- EnvVarRestorer() : origRuntimeDir(qgetenv("XDG_RUNTIME_DIR")) {}
- ~EnvVarRestorer() { qputenv("XDG_RUNTIME_DIR", origRuntimeDir.constData()); }
- const QByteArray origRuntimeDir;
+ ~EnvVarRestorer()
+ {
+ qputenv("XDG_RUNTIME_DIR", origRuntimeDir);
+ qputenv("TMPDIR", origTempDir);
+ }
+ const QByteArray origRuntimeDir = qgetenv("XDG_RUNTIME_DIR");
+ const QByteArray origTempDir = qgetenv("TMPDIR");
};
EnvVarRestorer restorer;
- // When $XDG_RUNTIME_DIR points to a directory with wrong ownership, QStandardPaths should warn
- QByteArray rootOwnedFileName = "/tmp";
- if (EmulationDetector::isRunningArmOnX86()) {
- // Directory "tmp" under toolchain sysroot is detected by qemu and has same uid as current user.
- // Try /opt instead, it might not be located in the sysroot.
- QFileInfo rootOwnedFile = QFileInfo(QString::fromLatin1(rootOwnedFileName));
- if (rootOwnedFile.ownerId() == ::geteuid()) {
- rootOwnedFileName = "/opt";
- }
+ // set up the environment to point to a place we control
+ QTemporaryDir tempDir;
+ QVERIFY2(tempDir.isValid(), qPrintable(tempDir.errorString()));
+
+ QDir d(tempDir.path());
+ qputenv("TMPDIR", QFile::encodeName(tempDir.path()));
+
+ QFETCH(RuntimeDirSetup, setup);
+ std::optional<QString> opt = setup(d);
+ QVERIFY(opt);
+ QString expected = *opt;
+
+ QString runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
+ QCOMPARE(runtimeDir, expected);
+
+ if (!runtimeDir.isEmpty()) {
+ QFileInfo runtimeInfo(runtimeDir);
+ QVERIFY(runtimeInfo.isDir());
+ QVERIFY(!runtimeInfo.isSymLink());
+ auto expectedPerms = QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner
+ | QFile::ReadUser | QFile::WriteUser | QFile::ExeUser;
+ QCOMPARE(QString::number(runtimeInfo.permissions(), 16),
+ QString::number(expectedPerms, 16));
}
- qputenv("XDG_RUNTIME_DIR", QFile::encodeName(rootOwnedFileName));
-
- // It's very unlikely that /tmp is 0600 or that we can chmod it
- // The call below outputs
- // "QStandardPaths: wrong ownership on runtime directory /tmp, 0 instead of $UID"
- // but we can't reliably expect that it's owned by uid 0, I think.
- const uid_t uid = geteuid();
- QTest::ignoreMessage(QtWarningMsg,
- qPrintable(QString::fromLatin1("QStandardPaths: wrong ownership on runtime directory " + rootOwnedFileName + ", 0 instead of %1").arg(uid)));
- const QString runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
- QVERIFY2(runtimeDir.isEmpty(), qPrintable(runtimeDir));
-
- // When $XDG_RUNTIME_DIR points to a directory with wrong permissions, QStandardPaths should warn
- const QByteArray wrongPermissionFileName = "wrong_permissions";
- QDir::current().mkdir(wrongPermissionFileName);
- QFile wrongPermissionFile(wrongPermissionFileName);
- const QFile::Permissions wantedPerms = QFile::ReadUser | QFile::WriteUser | QFile::ExeUser;
- QVERIFY(wrongPermissionFile.setPermissions(wantedPerms | QFile::ExeGroup));
-
- qputenv("XDG_RUNTIME_DIR", wrongPermissionFileName);
- QTest::ignoreMessage(QtWarningMsg,
- qPrintable(QString::fromLatin1("QStandardPaths: wrong permissions on runtime directory " + wrongPermissionFileName + ", 7710 instead of 7700")));
- const QString wrongPermissionRuntimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
- QVERIFY(wrongPermissionRuntimeDir.isEmpty());
- QDir::current().rmdir(wrongPermissionFileName);
-
- // When $XDG_RUNTIME_DIR points to a non-existing directory, QStandardPaths should create it first
- const QByteArray nonExistingDir = "does_not_exist";
- qputenv("XDG_RUNTIME_DIR", nonExistingDir);
- const QString nonExistingRuntimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
- QVERIFY2(!nonExistingRuntimeDir.compare(nonExistingDir), qPrintable(nonExistingRuntimeDir));
- QVERIFY(QDir::current().exists(nonExistingRuntimeDir));
- QDir::current().rmdir(nonExistingRuntimeDir);
-
- // When $XDG_RUNTIME_DIR points to a file, QStandardPaths should warn
- const QString file = QFINDTESTDATA("tst_qstandardpaths.cpp");
- QVERIFY(!file.isEmpty());
- qputenv("XDG_RUNTIME_DIR", QFile::encodeName(file));
- QTest::ignoreMessage(QtWarningMsg, qPrintable(QString::fromLatin1("QStandardPaths: XDG_RUNTIME_DIR points to '%1' which is not a directory").arg(file)));
- const QString noRuntimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
- QVERIFY2(noRuntimeDir.isEmpty(), qPrintable(file));
#endif
}
@@ -591,6 +838,35 @@ void tst_qstandardpaths::testXdgPathCleanup()
QVERIFY(!appsDirs.contains("/applications"));
QVERIFY(!appsDirs.contains(uncleanGlobalAppDir + "/applications"));
QVERIFY(!appsDirs.contains("relative/path/applications"));
+
+ const QString uncleanGlobalConfigDir = "/./" + QFile::encodeName(m_globalConfigDir);
+ qputenv("XDG_CONFIG_DIRS", QFile::encodeName(uncleanGlobalConfigDir) + "::relative/path");
+ const QStringList configDirs = QStandardPaths::standardLocations(QStandardPaths::ConfigLocation);
+ QVERIFY(!configDirs.contains("relative/path"_L1));
+ QVERIFY(!configDirs.contains(""_L1));
+
+ // Relative paths in XDG_* env vars are ignored
+ const QString relative("./someRelativeDir");
+
+ qputenv("XDG_CACHE_HOME", relative.toLatin1());
+ const QString cacheDir = cacheLoc();
+ QCOMPARE_NE(cacheDir, relative);
+
+ qputenv("XDG_STATE_HOME", relative.toLatin1());
+ const QString stateDir = stateLoc();
+ QCOMPARE_NE(stateDir, relative);
+
+ qputenv("XDG_DATA_HOME", relative.toLatin1());
+ const QString localDataDir = genericDataLoc();
+ QCOMPARE_NE(localDataDir, relative);
+
+ qputenv("XDG_CONFIG_HOME", relative.toLatin1());
+ const QString localConfig = configLoc();
+ QCOMPARE_NE(localConfig, relative);
+
+ qputenv("XDG_RUNTIME_DIR", relative.toLatin1());
+ const QString runtimeDir = genericDataLoc();
+ QCOMPARE_NE(runtimeDir, relative);
#endif
}