1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31#include <qcoreapplication.h>
32#include <qstring.h>
33#include <qtemporarydir.h>
34#include <qfile.h>
35#include <qdir.h>
36#include <qset.h>
37#include <qtextcodec.h>
38#include <QtTest/private/qtesthelpers_p.h>
39#ifdef Q_OS_WIN
40# include <windows.h>
41#endif
42#ifdef Q_OS_UNIX // for geteuid()
43# include <sys/types.h>
44# include <unistd.h>
45#endif
46#include "emulationdetector.h"
47
48class tst_QTemporaryDir : public QObject
49{
50 Q_OBJECT
51public:
52public slots:
53 void initTestCase();
54 void cleanupTestCase();
55
56private slots:
57 void construction();
58 void fileTemplate();
59 void fileTemplate_data();
60 void getSetCheck();
61 void fileName();
62 void filePath_data();
63 void filePath();
64 void autoRemove();
65 void nonWritableCurrentDir();
66 void openOnRootDrives();
67 void stressTest();
68 void rename();
69
70 void QTBUG_4796_data();
71 void QTBUG_4796();
72
73 void QTBUG43352_failedSetPermissions();
74
75private:
76 QString m_previousCurrent;
77};
78
79void tst_QTemporaryDir::initTestCase()
80{
81 m_previousCurrent = QDir::currentPath();
82 QDir::setCurrent(QDir::tempPath());
83 QVERIFY(QDir("test-XXXXXX").exists() || QDir().mkdir("test-XXXXXX"));
84 QCoreApplication::setApplicationName("tst_qtemporarydir");
85}
86
87void tst_QTemporaryDir::cleanupTestCase()
88{
89 QVERIFY(QDir().rmdir("test-XXXXXX"));
90
91 QDir::setCurrent(m_previousCurrent);
92}
93
94void tst_QTemporaryDir::construction()
95{
96 QTemporaryDir dir;
97 QString tmp = QDir::tempPath();
98 QCOMPARE(dir.path().left(tmp.size()), tmp);
99 QVERIFY(dir.path().contains("tst_qtemporarydir"));
100 QVERIFY(QFileInfo(dir.path()).isDir());
101 QCOMPARE(dir.errorString(), QString());
102}
103
104// Testing get/set functions
105void tst_QTemporaryDir::getSetCheck()
106{
107 QTemporaryDir obj1;
108 // bool QTemporaryDir::autoRemove()
109 // void QTemporaryDir::setAutoRemove(bool)
110 obj1.setAutoRemove(false);
111 QCOMPARE(false, obj1.autoRemove());
112 obj1.setAutoRemove(true);
113 QCOMPARE(true, obj1.autoRemove());
114}
115
116static QString hanTestText()
117{
118 QString text;
119 text += QChar(0x65B0);
120 text += QChar(0x5E10);
121 text += QChar(0x6237);
122 return text;
123}
124
125static QString umlautTestText()
126{
127 QString text;
128 text += QChar(0xc4);
129 text += QChar(0xe4);
130 text += QChar(0xd6);
131 text += QChar(0xf6);
132 text += QChar(0xdc);
133 text += QChar(0xfc);
134 text += QChar(0xdf);
135 return text;
136}
137
138void tst_QTemporaryDir::fileTemplate_data()
139{
140 QTest::addColumn<QString>(name: "constructorTemplate");
141 QTest::addColumn<QString>(name: "prefix");
142 QTest::addColumn<QString>(name: "suffix");
143
144 QTest::newRow(dataTag: "default") << "" << "tst_qtemporarydir-" << "";
145
146 QTest::newRow(dataTag: "xxx-suffix") << "qt_XXXXXXxxx" << "qt_" << "xxx";
147 QTest::newRow(dataTag: "xXx-suffix") << "qt_XXXXXXxXx" << "qt_" << "xXx";
148 QTest::newRow(dataTag: "no-suffix") << "qt_XXXXXX" << "qt_" << "";
149 QTest::newRow(dataTag: "10X") << "qt_XXXXXXXXXX" << "qt_" << "";
150 QTest::newRow(dataTag: "4Xsuffix") << "qt_XXXXXX_XXXX" << "qt_" << "_XXXX";
151 QTest::newRow(dataTag: "4Xprefix") << "qt_XXXX" << "qt_XXXX" << "";
152 QTest::newRow(dataTag: "5Xprefix") << "qt_XXXXX" << "qt_XXXXX" << "";
153 if (QTestPrivate::canHandleUnicodeFileNames()) {
154 // Test Umlauts (contained in Latin1)
155 QString prefix = "qt_" + umlautTestText();
156 QTest::newRow(dataTag: "Umlauts") << (prefix + "XXXXXX") << prefix << "";
157 // test non-Latin1
158 prefix = "qt_" + hanTestText();
159 QTest::newRow(dataTag: "Chinese") << (prefix + "XXXXXX" + umlautTestText()) << prefix << umlautTestText();
160 }
161}
162
163void tst_QTemporaryDir::fileTemplate()
164{
165 QFETCH(QString, constructorTemplate);
166 QFETCH(QString, prefix);
167 QFETCH(QString, suffix);
168
169 QTemporaryDir tempDir(constructorTemplate);
170
171 QVERIFY(tempDir.isValid());
172
173 QString dirName = QDir(tempDir.path()).dirName();
174 if (prefix.length()) {
175 QCOMPARE(dirName.left(prefix.length()), prefix);
176 QCOMPARE(dirName.right(suffix.length()), suffix);
177 }
178}
179
180
181/*
182 This tests whether the temporary dir really gets placed in QDir::tempPath
183*/
184void tst_QTemporaryDir::fileName()
185{
186 // Get QDir::tempPath and make an absolute path.
187 QString tempPath = QDir::tempPath();
188 QString absoluteTempPath = QDir(tempPath).absolutePath();
189 QTemporaryDir dir;
190 dir.setAutoRemove(true);
191 QString fileName = dir.path();
192 QVERIFY2(fileName.contains("/tst_qtemporarydir-"), qPrintable(fileName));
193 QVERIFY(QDir(fileName).exists());
194 // Get path to the temp dir, without the file name.
195 QString absoluteFilePath = QFileInfo(fileName).absolutePath();
196#if defined(Q_OS_WIN)
197 absoluteFilePath = absoluteFilePath.toLower();
198 absoluteTempPath = absoluteTempPath.toLower();
199#endif
200 QCOMPARE(absoluteFilePath, absoluteTempPath);
201}
202
203void tst_QTemporaryDir::filePath_data()
204{
205 QTest::addColumn<QString>(name: "templatePath");
206 QTest::addColumn<QString>(name: "fileName");
207
208 QTest::newRow(dataTag: "0") << QString() << "/tmpfile";
209 QTest::newRow(dataTag: "1") << QString() << "tmpfile";
210 QTest::newRow(dataTag: "2") << "XXXXX" << "tmpfile";
211 QTest::newRow(dataTag: "3") << "YYYYY" << "subdir/file";
212}
213
214void tst_QTemporaryDir::filePath()
215{
216 QFETCH(QString, templatePath);
217 QFETCH(QString, fileName);
218
219 QTemporaryDir dir(templatePath);
220 const QString filePath = dir.filePath(fileName);
221 const QString expectedFilePath = QDir::isAbsolutePath(path: fileName) ?
222 QString() : dir.path() + QLatin1Char('/') + fileName;
223 QCOMPARE(filePath, expectedFilePath);
224}
225
226void tst_QTemporaryDir::autoRemove()
227{
228 // Test auto remove
229 QString dirName;
230 {
231 QTemporaryDir dir("tempXXXXXX");
232 dir.setAutoRemove(true);
233 QVERIFY(dir.isValid());
234 dirName = dir.path();
235 }
236#ifdef Q_OS_WIN
237 // Windows seems unreliable here: sometimes it says the directory still exists,
238 // immediately after we deleted it.
239 QTRY_VERIFY(!QDir(dirName).exists());
240#else
241 QVERIFY(!QDir(dirName).exists());
242#endif
243
244 // Test if disabling auto remove works.
245 {
246 QTemporaryDir dir("tempXXXXXX");
247 dir.setAutoRemove(false);
248 QVERIFY(dir.isValid());
249 dirName = dir.path();
250 }
251 QVERIFY(QDir(dirName).exists());
252 QVERIFY(QDir().rmdir(dirName));
253 QVERIFY(!QDir(dirName).exists());
254
255 // Do not explicitly call setAutoRemove (tests if it really is the default as documented)
256 {
257 QTemporaryDir dir("tempXXXXXX");
258 QVERIFY(dir.isValid());
259 dirName = dir.path();
260 }
261#ifdef Q_OS_WIN
262 QTRY_VERIFY(!QDir(dirName).exists());
263#else
264 QVERIFY(!QDir(dirName).exists());
265#endif
266
267 // Test autoremove with files and subdirs in the temp dir
268 {
269 QTemporaryDir tempDir("tempXXXXXX");
270 QVERIFY(tempDir.isValid());
271 dirName = tempDir.path();
272 QDir dir(dirName);
273 QVERIFY(dir.mkdir(QString::fromLatin1("dir1")));
274 QVERIFY(dir.mkdir(QString::fromLatin1("dir2")));
275 QVERIFY(dir.mkdir(QString::fromLatin1("dir2/nested")));
276 QFile file(dirName + "/dir1/file");
277 QVERIFY(file.open(QIODevice::WriteOnly));
278 QCOMPARE(file.write("Hello"), 5LL);
279 file.close();
280 QVERIFY(file.setPermissions(QFile::ReadUser));
281 }
282#ifdef Q_OS_WIN
283 QTRY_VERIFY(!QDir(dirName).exists());
284#else
285 QVERIFY(!QDir(dirName).exists());
286#endif
287}
288
289void tst_QTemporaryDir::nonWritableCurrentDir()
290{
291#ifdef Q_OS_UNIX
292
293# if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
294 const char nonWritableDir[] = "/data";
295# else
296 const char nonWritableDir[] = "/home";
297# endif
298
299 if (::geteuid() == 0)
300 QSKIP("not valid running this test as root");
301
302 struct ChdirOnReturn
303 {
304 ChdirOnReturn(const QString& d) : dir(d) {}
305 ~ChdirOnReturn() {
306 QDir::setCurrent(dir);
307 }
308 QString dir;
309 };
310
311 const QFileInfo nonWritableDirFi = QFileInfo(QLatin1String(nonWritableDir));
312 QVERIFY(nonWritableDirFi.isDir());
313
314 if (EmulationDetector::isRunningArmOnX86()) {
315 if (nonWritableDirFi.ownerId() == ::geteuid()) {
316 QSKIP("Sysroot directories are owned by the current user");
317 }
318 }
319
320 QVERIFY(!nonWritableDirFi.isWritable());
321
322 ChdirOnReturn cor(QDir::currentPath());
323 QVERIFY(QDir::setCurrent(nonWritableDirFi.absoluteFilePath()));
324 // QTemporaryDir("tempXXXXXX") is probably a bad idea in any app
325 // where the current dir could anything...
326 QTemporaryDir dir("tempXXXXXX");
327 dir.setAutoRemove(true);
328 QVERIFY(!dir.isValid());
329 QVERIFY(!dir.errorString().isEmpty());
330 QVERIFY(dir.path().isEmpty());
331#endif
332}
333
334void tst_QTemporaryDir::openOnRootDrives()
335{
336#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
337 unsigned int lastErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
338#endif
339 // If it's possible to create a file in the root directory, it
340 // must be possible to create a temp dir there too.
341 foreach (const QFileInfo &driveInfo, QDir::drives()) {
342 QFile testFile(driveInfo.filePath() + "XXXXXX");
343 if (testFile.open(flags: QIODevice::ReadWrite)) {
344 testFile.remove();
345 QTemporaryDir dir(driveInfo.filePath() + "XXXXXX");
346 dir.setAutoRemove(true);
347 QVERIFY(dir.isValid());
348 }
349 }
350#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
351 SetErrorMode(lastErrorMode);
352#endif
353}
354
355void tst_QTemporaryDir::stressTest()
356{
357 const int iterations = 1000;
358 QTemporaryDir rootDir;
359 QVERIFY(rootDir.isValid());
360
361 QSet<QString> names;
362 const QString pattern = rootDir.path() + QStringLiteral("/XXXXXX");
363 for (int i = 0; i < iterations; ++i) {
364 QTemporaryDir dir(pattern);
365 dir.setAutoRemove(false);
366 QVERIFY2(dir.isValid(),
367 qPrintable(QString::fromLatin1("Failed to create #%1 under %2: %3.")
368 .arg(i)
369 .arg(QDir::toNativeSeparators(pattern))
370 .arg(dir.errorString())));
371 QVERIFY(!names.contains(dir.path()));
372 names.insert(value: dir.path());
373 }
374}
375
376void tst_QTemporaryDir::rename()
377{
378 // This test checks what happens if the temporary dir is renamed.
379 // Then the autodelete feature can't possibly find it.
380
381 QDir dir;
382 QVERIFY(!dir.exists("temporary-dir.renamed"));
383
384 QString tempname;
385 {
386 QTemporaryDir tempDir(dir.filePath(fileName: "temporary-dir.XXXXXX"));
387
388 QVERIFY(tempDir.isValid());
389 tempname = tempDir.path();
390
391 QVERIFY(QDir().rename(tempname, "temporary-dir.renamed"));
392 QVERIFY(!QDir(tempname).exists());
393 dir.setPath("temporary-dir.renamed");
394 QCOMPARE(dir.path(), QString("temporary-dir.renamed"));
395 QVERIFY(dir.exists());
396 }
397
398 // Auto-delete couldn't find it
399 QVERIFY(dir.exists());
400 // Clean up by hand
401 QVERIFY(dir.removeRecursively());
402 QVERIFY(!dir.exists());
403}
404
405void tst_QTemporaryDir::QTBUG_4796_data()
406{
407 QTest::addColumn<QString>(name: "prefix");
408 QTest::addColumn<QString>(name: "suffix");
409 QTest::addColumn<bool>(name: "openResult");
410
411 QString unicode = QString::fromUtf8(str: "\xc3\xa5\xc3\xa6\xc3\xb8");
412
413 QTest::newRow(dataTag: "<empty>") << QString() << QString() << true;
414 QTest::newRow(dataTag: ".") << QString(".") << QString() << true;
415 QTest::newRow(dataTag: "..") << QString("..") << QString() << true;
416 QTest::newRow(dataTag: "blaXXXXXX") << QString("bla") << QString() << true;
417 QTest::newRow(dataTag: "does-not-exist/qt_temp.XXXXXX") << QString("does-not-exist/qt_temp") << QString() << false;
418 QTest::newRow(dataTag: "XXXXXX<unicode>") << QString() << unicode << true;
419 QTest::newRow(dataTag: "<unicode>XXXXXX") << unicode << QString() << true;
420}
421
422void tst_QTemporaryDir::QTBUG_4796() // unicode support
423{
424 QVERIFY(QDir("test-XXXXXX").exists());
425
426 struct CleanOnReturn
427 {
428 ~CleanOnReturn()
429 {
430 foreach (const QString &tempName, tempNames)
431 QVERIFY(QDir(tempName).removeRecursively());
432 }
433
434 void reset()
435 {
436 tempNames.clear();
437 }
438
439 QStringList tempNames;
440 };
441
442 CleanOnReturn cleaner;
443
444 QFETCH(QString, prefix);
445 QFETCH(QString, suffix);
446 QFETCH(bool, openResult);
447
448 {
449 QString fileTemplate1 = prefix + QString("XX") + suffix;
450 QString fileTemplate2 = prefix + QString("XXXX") + suffix;
451 QString fileTemplate3 = prefix + QString("XXXXXX") + suffix;
452 QString fileTemplate4 = prefix + QString("XXXXXXXX") + suffix;
453
454 QTemporaryDir dir1(fileTemplate1);
455 QTemporaryDir dir2(fileTemplate2);
456 QTemporaryDir dir3(fileTemplate3);
457 QTemporaryDir dir4(fileTemplate4);
458 QTemporaryDir dir5("test-XXXXXX/" + fileTemplate1);
459 QTemporaryDir dir6("test-XXXXXX/" + fileTemplate3);
460
461 QCOMPARE(dir1.isValid(), openResult);
462 QCOMPARE(dir2.isValid(), openResult);
463 QCOMPARE(dir3.isValid(), openResult);
464 QCOMPARE(dir4.isValid(), openResult);
465 QCOMPARE(dir5.isValid(), openResult);
466 QCOMPARE(dir6.isValid(), openResult);
467
468 // make sure the dir exists under the *correct* name
469 if (openResult) {
470 cleaner.tempNames << dir1.path()
471 << dir2.path()
472 << dir3.path()
473 << dir4.path()
474 << dir5.path()
475 << dir6.path();
476
477 QDir currentDir;
478 QString fileName1 = currentDir.relativeFilePath(fileName: dir1.path());
479 QString fileName2 = currentDir.relativeFilePath(fileName: dir2.path());
480 QString fileName3 = currentDir.relativeFilePath(fileName: dir3.path());
481 QString fileName4 = currentDir.relativeFilePath(fileName: dir4.path());
482 QString fileName5 = currentDir.relativeFilePath(fileName: dir5.path());
483 QString fileName6 = currentDir.relativeFilePath(fileName: dir6.path());
484
485 QVERIFY(fileName1.startsWith(prefix));
486 QVERIFY(fileName2.startsWith(prefix));
487 QVERIFY(fileName5.startsWith("test-XXXXXX/" + prefix));
488 QVERIFY(fileName6.startsWith("test-XXXXXX/" + prefix));
489
490 if (!prefix.isEmpty()) {
491 QVERIFY(fileName3.startsWith(prefix));
492 QVERIFY(fileName4.startsWith(prefix));
493 }
494 }
495 }
496
497#ifdef Q_OS_WIN
498 QTest::qWait(20);
499#endif
500 foreach (const QString &tempName, cleaner.tempNames)
501 QVERIFY2(!QDir(tempName).exists(), qPrintable(tempName));
502
503 cleaner.reset();
504}
505
506void tst_QTemporaryDir::QTBUG43352_failedSetPermissions()
507{
508 QString path = QStandardPaths::writableLocation(type: QStandardPaths::DownloadLocation) + QStringLiteral("/");
509 int count = QDir(path).entryList().size();
510
511 {
512 QTemporaryDir dir(path);
513 }
514
515 QCOMPARE(QDir(path).entryList().size(), count);
516}
517
518QTEST_MAIN(tst_QTemporaryDir)
519#include "tst_qtemporarydir.moc"
520

source code of qtbase/tests/auto/corelib/io/qtemporarydir/tst_qtemporarydir.cpp