diff options
-rw-r--r-- | doc/src/snippets/code/src_corelib_io_qtemporarydir.cpp | 53 | ||||
-rw-r--r-- | src/corelib/io/io.pri | 2 | ||||
-rw-r--r-- | src/corelib/io/qdir_p.h | 6 | ||||
-rw-r--r-- | src/corelib/io/qtemporarydir.cpp | 258 | ||||
-rw-r--r-- | src/corelib/io/qtemporarydir.h | 84 | ||||
-rw-r--r-- | tests/auto/corelib/io/io.pro | 1 | ||||
-rw-r--r-- | tests/auto/corelib/io/qtemporarydir/qtemporarydir.pro | 7 | ||||
-rw-r--r-- | tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp | 471 |
8 files changed, 879 insertions, 3 deletions
diff --git a/doc/src/snippets/code/src_corelib_io_qtemporarydir.cpp b/doc/src/snippets/code/src_corelib_io_qtemporarydir.cpp new file mode 100644 index 0000000000..4cdc54a6c4 --- /dev/null +++ b/doc/src/snippets/code/src_corelib_io_qtemporarydir.cpp @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +{ +//! [0] + // Within a function/method... + + QTemporaryDir dir; + if (dir.isValid()) { + // dir.path() returns the unique directory path + } + + // The QTemporaryDir destructor removes the temporary directory + // as it goes out of scope. +//! [0] +} diff --git a/src/corelib/io/io.pri b/src/corelib/io/io.pri index 4fa5ed035e..ef11621679 100644 --- a/src/corelib/io/io.pri +++ b/src/corelib/io/io.pri @@ -20,6 +20,7 @@ HEADERS += \ io/qprocess.h \ io/qprocess_p.h \ io/qtextstream.h \ + io/qtemporarydir.h \ io/qtemporaryfile.h \ io/qresource_p.h \ io/qresource_iterator_p.h \ @@ -54,6 +55,7 @@ SOURCES += \ io/qnoncontiguousbytedevice.cpp \ io/qprocess.cpp \ io/qtextstream.cpp \ + io/qtemporarydir.cpp \ io/qtemporaryfile.cpp \ io/qresource.cpp \ io/qresource_iterator.cpp \ diff --git a/src/corelib/io/qdir_p.h b/src/corelib/io/qdir_p.h index 7644a0391e..a34427a716 100644 --- a/src/corelib/io/qdir_p.h +++ b/src/corelib/io/qdir_p.h @@ -67,11 +67,11 @@ public: static inline QStringList splitFilters(const QString &nameFilter, QChar sep = 0); - inline void setPath(const QString &path); + void setPath(const QString &path); - inline void clearFileLists(); + void clearFileLists(); - inline void resolveAbsoluteEntry() const; + void resolveAbsoluteEntry() const; QStringList nameFilters; QDir::SortFlags sort; diff --git a/src/corelib/io/qtemporarydir.cpp b/src/corelib/io/qtemporarydir.cpp new file mode 100644 index 0000000000..c3a41fdd4c --- /dev/null +++ b/src/corelib/io/qtemporarydir.cpp @@ -0,0 +1,258 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtemporarydir.h" + +#ifndef QT_NO_TEMPORARYFILE + +#include "qdiriterator.h" +#include "qplatformdefs.h" +#include "private/qdir_p.h" +#include <QDebug> + +#if defined(QT_BUILD_CORE_LIB) +#include "qcoreapplication.h" +#endif + +#include <stdlib.h> // mkdtemp +#ifdef Q_OS_WIN +#include <windows.h> +#include <private/qfsfileengine_p.h> +#endif + +QT_BEGIN_NAMESPACE + +//************* QTemporaryDirPrivate +class QTemporaryDirPrivate +{ +public: + QTemporaryDirPrivate(); + ~QTemporaryDirPrivate(); + + QString defaultTemplateName() const; + void create(const QString &templateName); + + QString path; + bool autoRemove; + bool success; +}; + +QTemporaryDirPrivate::QTemporaryDirPrivate() + : autoRemove(true), + success(false) +{ +} + +QTemporaryDirPrivate::~QTemporaryDirPrivate() +{ +} + +QString QTemporaryDirPrivate::defaultTemplateName() const +{ + QString baseName; +#if defined(QT_BUILD_CORE_LIB) + baseName = QCoreApplication::applicationName(); + if (baseName.isEmpty()) +#endif + baseName = QLatin1String("qt_temp"); + + return QDir::tempPath() + QLatin1Char('/') + baseName + QLatin1String("-XXXXXX"); +} + +void QTemporaryDirPrivate::create(const QString &templateName) +{ +#ifdef Q_OS_WIN + QString buffer = templateName; + // Windows' mktemp believes 26 temp files per process ought to be enough for everyone (!) + // Let's add a few random chars then, before the XXXXXX template. + for (int i = 0 ; i < 4 ; ++i) + buffer += QChar((qrand() & 0xffff) % (26) + 'A'); + if (!buffer.endsWith(QLatin1String("XXXXXX"))) + buffer += QLatin1String("XXXXXX"); + QFileSystemEntry baseEntry(buffer); + QFileSystemEntry::NativePath basePath = baseEntry.nativeFilePath(); + wchar_t* array = (wchar_t*)basePath.utf16(); + if (_wmktemp(array) && ::CreateDirectory(array, 0)) { + success = true; + QFileSystemEntry entry(QString::fromWCharArray(array), QFileSystemEntry::FromNativePath()); + path = entry.filePath(); + } +#else + QByteArray buffer = QFile::encodeName(templateName); + if (!buffer.endsWith("XXXXXX")) + buffer += "XXXXXX"; + if (mkdtemp(buffer.data())) { // modifies buffer + success = true; + path = QFile::decodeName(buffer.constData()); + } +#endif +} + +//************* QTemporaryDir + +/*! + \class QTemporaryDir + \reentrant + \brief The QTemporaryDir class creates a unique directory for temporary use. + + \ingroup io + + + QTemporaryDir is used to create unique temporary dirs safely. + The dir itself is created by the constructor. The name of the + temporary directory is guaranteed to be unique (i.e., you are + guaranteed to not overwrite an existing dir), and the directory will + subsequently be removed upon destruction of the QTemporaryDir + object. The directory name is either auto-generated, or created based + on a template, which is passed to QTemporaryDir's constructor. + + Example: + + \snippet doc/src/snippets/code/src_corelib_io_qtemporarydir.cpp 0 + + It is very important to test that the temporary directory could be + created, using isValid(). Do not use exists(), since a default-constructed + QDir represents the current directory, which exists. + + The path to the temporary dir can be found by calling path(). + + A temporary directory will have some static part of the name and some + part that is calculated to be unique. The default path will be + determined from QCoreApplication::applicationName() (otherwise \c qt_temp) and will + be placed into the temporary path as returned by QDir::tempPath(). + If you specify your own path, a relative path will not be placed in the + temporary directory by default, but be relative to the current working directory. + In all cases, a random string will be appended to the path in order to make it unique. + + \sa QDir::tempPath(), QDir, QTemporaryFile +*/ + +QTemporaryDir::QTemporaryDir() + : d_ptr(new QTemporaryDirPrivate) +{ + d_ptr->create(d_ptr->defaultTemplateName()); +} + +QTemporaryDir::QTemporaryDir(const QString &templateName) + : d_ptr(new QTemporaryDirPrivate) +{ + if (templateName.isEmpty()) + d_ptr->create(d_ptr->defaultTemplateName()); + else + d_ptr->create(templateName); +} + +/*! + Destroys the temporary directory object. + If auto remove mode was set, it will automatically delete the directory + including all its contents. + + \sa autoRemove() +*/ +QTemporaryDir::~QTemporaryDir() +{ + if (d_ptr->success && d_ptr->autoRemove) + remove(); + delete d_ptr; +} + +/*! + Returns true if the QTemporaryDir was created successfully. +*/ +bool QTemporaryDir::isValid() const +{ + return d_ptr->success; +} + +/*! + Returns the path to the temporary directory. + Empty if the QTemporaryDir could not be created. +*/ +QString QTemporaryDir::path() const +{ + return d_ptr->path; +} + +/*! + Returns true if the QTemporaryDir is in auto remove + mode. Auto-remove mode will automatically delete the directory from + disk upon destruction. This makes it very easy to create your + QTemporaryDir object on the stack, fill it with files, do something with + the files, and finally on function return it will automatically clean up + after itself. + + Auto-remove is on by default. + + \sa setAutoRemove(), remove() +*/ +bool QTemporaryDir::autoRemove() const +{ + return d_ptr->autoRemove; +} + +/*! + Sets the QTemporaryDir into auto-remove mode if \a b is true. + + Auto-remove is on by default. + + \sa autoRemove(), remove() +*/ +void QTemporaryDir::setAutoRemove(bool b) +{ + d_ptr->autoRemove = b; +} + +/*! + Removes the temporary directory, including all its contents. +*/ +bool QTemporaryDir::remove() +{ + if (!d_ptr->success) + return false; + Q_ASSERT(!path().isEmpty()); + Q_ASSERT(path() != QLatin1String(".")); + + return QDir(path()).removeRecursively(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_TEMPORARYFILE diff --git a/src/corelib/io/qtemporarydir.h b/src/corelib/io/qtemporarydir.h new file mode 100644 index 0000000000..96dc18d952 --- /dev/null +++ b/src/corelib/io/qtemporarydir.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTEMPORARYDIR_H +#define QTEMPORARYDIR_H + +#include <QtCore/qdir.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_TEMPORARYFILE + +class QTemporaryDirPrivate; + +class Q_CORE_EXPORT QTemporaryDir +{ +public: + QTemporaryDir(); + explicit QTemporaryDir(const QString &templateName); + ~QTemporaryDir(); + + bool isValid() const; + + bool autoRemove() const; + void setAutoRemove(bool b); + bool remove(); + + QString path() const; + +private: + QTemporaryDirPrivate* const d_ptr; + + Q_DISABLE_COPY(QTemporaryDir) +}; + +#endif // QT_NO_TEMPORARYFILE + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QTEMPORARYDIR_H diff --git a/tests/auto/corelib/io/io.pro b/tests/auto/corelib/io/io.pro index 3c68e0a6a7..e044eda1ca 100644 --- a/tests/auto/corelib/io/io.pro +++ b/tests/auto/corelib/io/io.pro @@ -16,6 +16,7 @@ SUBDIRS=\ qresourceengine \ qsettings \ qstandardpaths \ + qtemporarydir \ qtemporaryfile \ qtextstream \ qurl \ diff --git a/tests/auto/corelib/io/qtemporarydir/qtemporarydir.pro b/tests/auto/corelib/io/qtemporarydir/qtemporarydir.pro new file mode 100644 index 0000000000..b34d2cca07 --- /dev/null +++ b/tests/auto/corelib/io/qtemporarydir/qtemporarydir.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +TARGET = tst_qtemporarydir +SOURCES += tst_qtemporarydir.cpp + +QT = core testlib + +CONFIG += parallel_test diff --git a/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp b/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp new file mode 100644 index 0000000000..e1ead8c9c0 --- /dev/null +++ b/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp @@ -0,0 +1,471 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <qcoreapplication.h> +#include <qstring.h> +#include <qtemporarydir.h> +#include <qfile.h> +#include <qdir.h> +#include <qset.h> +#ifdef Q_OS_WIN +# include <windows.h> +#endif + +//TESTED_CLASS=QTemporaryDir +//TESTED_FILES=qtemporarydir.cpp + +class tst_QTemporaryDir : public QObject +{ + Q_OBJECT +public: + tst_QTemporaryDir(); + virtual ~tst_QTemporaryDir(); +public slots: + void init(); + void cleanup(); + + void initTestCase(); + void cleanupTestCase(); + +private slots: + void construction(); + void fileTemplate(); + void fileTemplate_data(); + void getSetCheck(); + void fileName(); + void autoRemove(); + void nonWritableCurrentDir(); + void openOnRootDrives(); + void stressTest(); + void rename(); + void autoRemoveAfterFailedRename(); + + void QTBUG_4796_data(); + void QTBUG_4796(); + +public: +}; + +void tst_QTemporaryDir::initTestCase() +{ + QVERIFY(QDir("test-XXXXXX").exists() || QDir().mkdir("test-XXXXXX")); + QCoreApplication::setApplicationName("tst_qtemporarydir"); +} + +void tst_QTemporaryDir::cleanupTestCase() +{ + QVERIFY(QDir().rmdir("test-XXXXXX")); +} + +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()); +} + +// 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()); +} + +tst_QTemporaryDir::tst_QTemporaryDir() +{ +} + +tst_QTemporaryDir::~tst_QTemporaryDir() +{ + +} + +void tst_QTemporaryDir::init() +{ +} + +void tst_QTemporaryDir::cleanup() +{ +} + +void tst_QTemporaryDir::fileTemplate_data() +{ + QTest::addColumn<QString>("constructorTemplate"); + QTest::addColumn<QString>("prefix"); + + QTest::newRow("constructor default") << "" << "tst_qtemporarydir-"; + + QTest::newRow("constructor with xxx sufix") << "qt_XXXXXXxxx" << "qt_XXXXXXxxx"; + QTest::newRow("constructor with xXx sufix") << "qt_XXXXXXxXx" << "qt_XXXXXXxXx"; + QTest::newRow("constructor with no suffix") << "qt_XXXXXX" << "qt_"; + QTest::newRow("constructor with >6 X's, no suffix") << "qt_XXXXXXXXXX" << "qt_XXXX"; + QTest::newRow("constructor with XXXX suffix") << "qt_XXXXXX_XXXX" << "qt_"; + QTest::newRow("constructor with XXXX prefix") << "qt_XXXX" << "qt_XXXX"; + QTest::newRow("constructor with XXXXX prefix") << "qt_XXXXX" << "qt_XXXXX"; +} + +void tst_QTemporaryDir::fileTemplate() +{ + QFETCH(QString, constructorTemplate); + QFETCH(QString, prefix); + + QTemporaryDir tempDir(constructorTemplate); + + QVERIFY(tempDir.isValid()); + + QString dirName = QDir(tempDir.path()).dirName(); + if (prefix.length()) + QCOMPARE(dirName.left(prefix.length()), prefix); +} + + +/* + 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(QFile::exists(fileName)); + // 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::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(!QFile::exists(dirName)); +#else + QVERIFY(!QFile::exists(dirName)); +#endif + + // Test if disabling auto remove works. + { + QTemporaryDir dir("tempXXXXXX"); + dir.setAutoRemove(false); + QVERIFY(dir.isValid()); + dirName = dir.path(); + } + QVERIFY(QFile::exists(dirName)); + QVERIFY(QDir().rmdir(dirName)); + QVERIFY(!QFile::exists(dirName)); + + // 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(!QFile::exists(dirName)); +#else + QVERIFY(!QFile::exists(dirName)); +#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); + } +#ifdef Q_OS_WIN + QTRY_VERIFY(!QFile::exists(dirName)); +#else + QVERIFY(!QFile::exists(dirName)); +#endif +} + +void tst_QTemporaryDir::nonWritableCurrentDir() +{ +#ifdef Q_OS_UNIX + QString cwd = QDir::currentPath(); + QDir::setCurrent("/"); + // QTemporaryDir("tempXXXXXX") is probably a bad idea in any app + // where the current dir could anything... + QString fileName; + QTemporaryDir dir("tempXXXXXX"); + dir.setAutoRemove(true); + QVERIFY(!dir.isValid()); + fileName = dir.path(); + QDir::setCurrent(cwd); +#endif +} + +void tst_QTemporaryDir::openOnRootDrives() +{ +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + unsigned int lastErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS); +#endif + // If it's possible to create a dir 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.txt"); + if (testFile.open(QIODevice::ReadWrite)) { + testFile.remove(); + QTemporaryDir dir(driveInfo.filePath() + "XXXXXX.txt"); + dir.setAutoRemove(true); + QVERIFY(dir.isValid()); + } + } +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + SetErrorMode(lastErrorMode); +#endif +} + +void tst_QTemporaryDir::stressTest() +{ + const int iterations = 1000; + + QSet<QString> names; + for (int i = 0; i < iterations; ++i) { + QTemporaryDir dir; + dir.setAutoRemove(false); + QVERIFY2(dir.isValid(), qPrintable(QString::number(i))); + QVERIFY(!names.contains(dir.path())); + names.insert(dir.path()); + } + for (QSet<QString>::const_iterator it = names.constBegin(); it != names.constEnd(); ++it) + QDir(*it).removeRecursively(); +} + +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::autoRemoveAfterFailedRename() +{ + struct CleanOnReturn + { + ~CleanOnReturn() + { + if (!tempName.isEmpty()) + QVERIFY(QDir(tempName).removeRecursively()); + } + + void reset() + { + tempName.clear(); + } + + QString tempName; + }; + + CleanOnReturn cleaner; + + { + QTemporaryDir dir; + QVERIFY(dir.isValid()); + cleaner.tempName = dir.path(); + + QVERIFY(QFile::exists(cleaner.tempName)); + QVERIFY(!QFileInfo("i-do-not-exist").isDir()); + QVERIFY(!QDir().rename(cleaner.tempName, "i-do-not-exist/dir.txt")); + QVERIFY(QFile::exists(cleaner.tempName)); + } + + QVERIFY(!QFile::exists(cleaner.tempName)); + cleaner.reset(); +} + +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() +{ + 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(fileTemplate1)); + QVERIFY(fileName2.startsWith(fileTemplate2)); + QVERIFY(fileName5.startsWith("test-XXXXXX/" + fileTemplate1)); + 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(!QFile::exists(tempName), qPrintable(tempName)); + + cleaner.reset(); +} + +QTEST_MAIN(tst_QTemporaryDir) +#include "tst_qtemporarydir.moc" |