blob: 6dbb8ddd0d4bc1e450758abc2972efe91ad62d04 [file] [log] [blame]
/****************************************************************************
**
** 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>
#include <qcoreapplication.h>
#include <qstring.h>
#include <qtemporarydir.h>
#include <qfile.h>
#include <qdir.h>
#include <qset.h>
#include <qtextcodec.h>
#include <QtTest/private/qtesthelpers_p.h>
#ifdef Q_OS_WIN
# include <windows.h>
#endif
#ifdef Q_OS_UNIX // for geteuid()
# include <sys/types.h>
# include <unistd.h>
#endif
#include "emulationdetector.h"
class tst_QTemporaryDir : public QObject
{
Q_OBJECT
public:
public slots:
void initTestCase();
void cleanupTestCase();
private slots:
void construction();
void fileTemplate();
void fileTemplate_data();
void getSetCheck();
void fileName();
void filePath_data();
void filePath();
void autoRemove();
void nonWritableCurrentDir();
void openOnRootDrives();
void stressTest();
void rename();
void QTBUG_4796_data();
void QTBUG_4796();
void QTBUG43352_failedSetPermissions();
private:
QString m_previousCurrent;
};
void tst_QTemporaryDir::initTestCase()
{
m_previousCurrent = QDir::currentPath();
QDir::setCurrent(QDir::tempPath());
QVERIFY(QDir("test-XXXXXX").exists() || QDir().mkdir("test-XXXXXX"));
QCoreApplication::setApplicationName("tst_qtemporarydir");
}
void tst_QTemporaryDir::cleanupTestCase()
{
QVERIFY(QDir().rmdir("test-XXXXXX"));
QDir::setCurrent(m_previousCurrent);
}
void tst_QTemporaryDir::construction()
{
QTemporaryDir dir;
QString tmp = QDir::tempPath();
QCOMPARE(dir.path().left(tmp.size()), tmp);
QVERIFY(dir.path().contains("tst_qtemporarydir"));
QVERIFY(QFileInfo(dir.path()).isDir());
QCOMPARE(dir.errorString(), QString());
}
// Testing get/set functions
void tst_QTemporaryDir::getSetCheck()
{
QTemporaryDir obj1;
// bool QTemporaryDir::autoRemove()
// void QTemporaryDir::setAutoRemove(bool)
obj1.setAutoRemove(false);
QCOMPARE(false, obj1.autoRemove());
obj1.setAutoRemove(true);
QCOMPARE(true, obj1.autoRemove());
}
static QString hanTestText()
{
QString text;
text += QChar(0x65B0);
text += QChar(0x5E10);
text += QChar(0x6237);
return text;
}
static QString umlautTestText()
{
QString text;
text += QChar(0xc4);
text += QChar(0xe4);
text += QChar(0xd6);
text += QChar(0xf6);
text += QChar(0xdc);
text += QChar(0xfc);
text += QChar(0xdf);
return text;
}
void tst_QTemporaryDir::fileTemplate_data()
{
QTest::addColumn<QString>("constructorTemplate");
QTest::addColumn<QString>("prefix");
QTest::addColumn<QString>("suffix");
QTest::newRow("default") << "" << "tst_qtemporarydir-" << "";
QTest::newRow("xxx-suffix") << "qt_XXXXXXxxx" << "qt_" << "xxx";
QTest::newRow("xXx-suffix") << "qt_XXXXXXxXx" << "qt_" << "xXx";
QTest::newRow("no-suffix") << "qt_XXXXXX" << "qt_" << "";
QTest::newRow("10X") << "qt_XXXXXXXXXX" << "qt_" << "";
QTest::newRow("4Xsuffix") << "qt_XXXXXX_XXXX" << "qt_" << "_XXXX";
QTest::newRow("4Xprefix") << "qt_XXXX" << "qt_XXXX" << "";
QTest::newRow("5Xprefix") << "qt_XXXXX" << "qt_XXXXX" << "";
if (QTestPrivate::canHandleUnicodeFileNames()) {
// Test Umlauts (contained in Latin1)
QString prefix = "qt_" + umlautTestText();
QTest::newRow("Umlauts") << (prefix + "XXXXXX") << prefix << "";
// test non-Latin1
prefix = "qt_" + hanTestText();
QTest::newRow("Chinese") << (prefix + "XXXXXX" + umlautTestText()) << prefix << umlautTestText();
}
}
void tst_QTemporaryDir::fileTemplate()
{
QFETCH(QString, constructorTemplate);
QFETCH(QString, prefix);
QFETCH(QString, suffix);
QTemporaryDir tempDir(constructorTemplate);
QVERIFY(tempDir.isValid());
QString dirName = QDir(tempDir.path()).dirName();
if (prefix.length()) {
QCOMPARE(dirName.left(prefix.length()), prefix);
QCOMPARE(dirName.right(suffix.length()), suffix);
}
}
/*
This tests whether the temporary dir really gets placed in QDir::tempPath
*/
void tst_QTemporaryDir::fileName()
{
// Get QDir::tempPath and make an absolute path.
QString tempPath = QDir::tempPath();
QString absoluteTempPath = QDir(tempPath).absolutePath();
QTemporaryDir dir;
dir.setAutoRemove(true);
QString fileName = dir.path();
QVERIFY2(fileName.contains("/tst_qtemporarydir-"), qPrintable(fileName));
QVERIFY(QDir(fileName).exists());
// Get path to the temp dir, without the file name.
QString absoluteFilePath = QFileInfo(fileName).absolutePath();
#if defined(Q_OS_WIN)
absoluteFilePath = absoluteFilePath.toLower();
absoluteTempPath = absoluteTempPath.toLower();
#endif
QCOMPARE(absoluteFilePath, absoluteTempPath);
}
void tst_QTemporaryDir::filePath_data()
{
QTest::addColumn<QString>("templatePath");
QTest::addColumn<QString>("fileName");
QTest::newRow("0") << QString() << "/tmpfile";
QTest::newRow("1") << QString() << "tmpfile";
QTest::newRow("2") << "XXXXX" << "tmpfile";
QTest::newRow("3") << "YYYYY" << "subdir/file";
}
void tst_QTemporaryDir::filePath()
{
QFETCH(QString, templatePath);
QFETCH(QString, fileName);
QTemporaryDir dir(templatePath);
const QString filePath = dir.filePath(fileName);
const QString expectedFilePath = QDir::isAbsolutePath(fileName) ?
QString() : dir.path() + QLatin1Char('/') + fileName;
QCOMPARE(filePath, expectedFilePath);
}
void tst_QTemporaryDir::autoRemove()
{
// Test auto remove
QString dirName;
{
QTemporaryDir dir("tempXXXXXX");
dir.setAutoRemove(true);
QVERIFY(dir.isValid());
dirName = dir.path();
}
#ifdef Q_OS_WIN
// Windows seems unreliable here: sometimes it says the directory still exists,
// immediately after we deleted it.
QTRY_VERIFY(!QDir(dirName).exists());
#else
QVERIFY(!QDir(dirName).exists());
#endif
// Test if disabling auto remove works.
{
QTemporaryDir dir("tempXXXXXX");
dir.setAutoRemove(false);
QVERIFY(dir.isValid());
dirName = dir.path();
}
QVERIFY(QDir(dirName).exists());
QVERIFY(QDir().rmdir(dirName));
QVERIFY(!QDir(dirName).exists());
// Do not explicitly call setAutoRemove (tests if it really is the default as documented)
{
QTemporaryDir dir("tempXXXXXX");
QVERIFY(dir.isValid());
dirName = dir.path();
}
#ifdef Q_OS_WIN
QTRY_VERIFY(!QDir(dirName).exists());
#else
QVERIFY(!QDir(dirName).exists());
#endif
// Test autoremove with files and subdirs in the temp dir
{
QTemporaryDir tempDir("tempXXXXXX");
QVERIFY(tempDir.isValid());
dirName = tempDir.path();
QDir dir(dirName);
QVERIFY(dir.mkdir(QString::fromLatin1("dir1")));
QVERIFY(dir.mkdir(QString::fromLatin1("dir2")));
QVERIFY(dir.mkdir(QString::fromLatin1("dir2/nested")));
QFile file(dirName + "/dir1/file");
QVERIFY(file.open(QIODevice::WriteOnly));
QCOMPARE(file.write("Hello"), 5LL);
file.close();
QVERIFY(file.setPermissions(QFile::ReadUser));
}
#ifdef Q_OS_WIN
QTRY_VERIFY(!QDir(dirName).exists());
#else
QVERIFY(!QDir(dirName).exists());
#endif
}
void tst_QTemporaryDir::nonWritableCurrentDir()
{
#ifdef Q_OS_UNIX
# if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
const char nonWritableDir[] = "/data";
# else
const char nonWritableDir[] = "/home";
# endif
if (::geteuid() == 0)
QSKIP("not valid running this test as root");
struct ChdirOnReturn
{
ChdirOnReturn(const QString& d) : dir(d) {}
~ChdirOnReturn() {
QDir::setCurrent(dir);
}
QString dir;
};
const QFileInfo nonWritableDirFi = QFileInfo(QLatin1String(nonWritableDir));
QVERIFY(nonWritableDirFi.isDir());
if (EmulationDetector::isRunningArmOnX86()) {
if (nonWritableDirFi.ownerId() == ::geteuid()) {
QSKIP("Sysroot directories are owned by the current user");
}
}
QVERIFY(!nonWritableDirFi.isWritable());
ChdirOnReturn cor(QDir::currentPath());
QVERIFY(QDir::setCurrent(nonWritableDirFi.absoluteFilePath()));
// QTemporaryDir("tempXXXXXX") is probably a bad idea in any app
// where the current dir could anything...
QTemporaryDir dir("tempXXXXXX");
dir.setAutoRemove(true);
QVERIFY(!dir.isValid());
QVERIFY(!dir.errorString().isEmpty());
QVERIFY(dir.path().isEmpty());
#endif
}
void tst_QTemporaryDir::openOnRootDrives()
{
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
unsigned int lastErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
#endif
// If it's possible to create a file in the root directory, it
// must be possible to create a temp dir there too.
foreach (const QFileInfo &driveInfo, QDir::drives()) {
QFile testFile(driveInfo.filePath() + "XXXXXX");
if (testFile.open(QIODevice::ReadWrite)) {
testFile.remove();
QTemporaryDir dir(driveInfo.filePath() + "XXXXXX");
dir.setAutoRemove(true);
QVERIFY(dir.isValid());
}
}
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
SetErrorMode(lastErrorMode);
#endif
}
void tst_QTemporaryDir::stressTest()
{
const int iterations = 1000;
QTemporaryDir rootDir;
QVERIFY(rootDir.isValid());
QSet<QString> names;
const QString pattern = rootDir.path() + QStringLiteral("/XXXXXX");
for (int i = 0; i < iterations; ++i) {
QTemporaryDir dir(pattern);
dir.setAutoRemove(false);
QVERIFY2(dir.isValid(),
qPrintable(QString::fromLatin1("Failed to create #%1 under %2: %3.")
.arg(i)
.arg(QDir::toNativeSeparators(pattern))
.arg(dir.errorString())));
QVERIFY(!names.contains(dir.path()));
names.insert(dir.path());
}
}
void tst_QTemporaryDir::rename()
{
// This test checks what happens if the temporary dir is renamed.
// Then the autodelete feature can't possibly find it.
QDir dir;
QVERIFY(!dir.exists("temporary-dir.renamed"));
QString tempname;
{
QTemporaryDir tempDir(dir.filePath("temporary-dir.XXXXXX"));
QVERIFY(tempDir.isValid());
tempname = tempDir.path();
QVERIFY(QDir().rename(tempname, "temporary-dir.renamed"));
QVERIFY(!QDir(tempname).exists());
dir.setPath("temporary-dir.renamed");
QCOMPARE(dir.path(), QString("temporary-dir.renamed"));
QVERIFY(dir.exists());
}
// Auto-delete couldn't find it
QVERIFY(dir.exists());
// Clean up by hand
QVERIFY(dir.removeRecursively());
QVERIFY(!dir.exists());
}
void tst_QTemporaryDir::QTBUG_4796_data()
{
QTest::addColumn<QString>("prefix");
QTest::addColumn<QString>("suffix");
QTest::addColumn<bool>("openResult");
QString unicode = QString::fromUtf8("\xc3\xa5\xc3\xa6\xc3\xb8");
QTest::newRow("<empty>") << QString() << QString() << true;
QTest::newRow(".") << QString(".") << QString() << true;
QTest::newRow("..") << QString("..") << QString() << true;
QTest::newRow("blaXXXXXX") << QString("bla") << QString() << true;
QTest::newRow("does-not-exist/qt_temp.XXXXXX") << QString("does-not-exist/qt_temp") << QString() << false;
QTest::newRow("XXXXXX<unicode>") << QString() << unicode << true;
QTest::newRow("<unicode>XXXXXX") << unicode << QString() << true;
}
void tst_QTemporaryDir::QTBUG_4796() // unicode support
{
QVERIFY(QDir("test-XXXXXX").exists());
struct CleanOnReturn
{
~CleanOnReturn()
{
foreach (const QString &tempName, tempNames)
QVERIFY(QDir(tempName).removeRecursively());
}
void reset()
{
tempNames.clear();
}
QStringList tempNames;
};
CleanOnReturn cleaner;
QFETCH(QString, prefix);
QFETCH(QString, suffix);
QFETCH(bool, openResult);
{
QString fileTemplate1 = prefix + QString("XX") + suffix;
QString fileTemplate2 = prefix + QString("XXXX") + suffix;
QString fileTemplate3 = prefix + QString("XXXXXX") + suffix;
QString fileTemplate4 = prefix + QString("XXXXXXXX") + suffix;
QTemporaryDir dir1(fileTemplate1);
QTemporaryDir dir2(fileTemplate2);
QTemporaryDir dir3(fileTemplate3);
QTemporaryDir dir4(fileTemplate4);
QTemporaryDir dir5("test-XXXXXX/" + fileTemplate1);
QTemporaryDir dir6("test-XXXXXX/" + fileTemplate3);
QCOMPARE(dir1.isValid(), openResult);
QCOMPARE(dir2.isValid(), openResult);
QCOMPARE(dir3.isValid(), openResult);
QCOMPARE(dir4.isValid(), openResult);
QCOMPARE(dir5.isValid(), openResult);
QCOMPARE(dir6.isValid(), openResult);
// make sure the dir exists under the *correct* name
if (openResult) {
cleaner.tempNames << dir1.path()
<< dir2.path()
<< dir3.path()
<< dir4.path()
<< dir5.path()
<< dir6.path();
QDir currentDir;
QString fileName1 = currentDir.relativeFilePath(dir1.path());
QString fileName2 = currentDir.relativeFilePath(dir2.path());
QString fileName3 = currentDir.relativeFilePath(dir3.path());
QString fileName4 = currentDir.relativeFilePath(dir4.path());
QString fileName5 = currentDir.relativeFilePath(dir5.path());
QString fileName6 = currentDir.relativeFilePath(dir6.path());
QVERIFY(fileName1.startsWith(prefix));
QVERIFY(fileName2.startsWith(prefix));
QVERIFY(fileName5.startsWith("test-XXXXXX/" + prefix));
QVERIFY(fileName6.startsWith("test-XXXXXX/" + prefix));
if (!prefix.isEmpty()) {
QVERIFY(fileName3.startsWith(prefix));
QVERIFY(fileName4.startsWith(prefix));
}
}
}
#ifdef Q_OS_WIN
QTest::qWait(20);
#endif
foreach (const QString &tempName, cleaner.tempNames)
QVERIFY2(!QDir(tempName).exists(), qPrintable(tempName));
cleaner.reset();
}
void tst_QTemporaryDir::QTBUG43352_failedSetPermissions()
{
QString path = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + QStringLiteral("/");
int count = QDir(path).entryList().size();
{
QTemporaryDir dir(path);
}
QCOMPARE(QDir(path).entryList().size(), count);
}
QTEST_MAIN(tst_QTemporaryDir)
#include "tst_qtemporarydir.moc"