1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2017 Intel Corporation.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the test suite of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:GPL-EXCEPT$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU
20** General Public License version 3 as published by the Free Software
21** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include <QtTest/QtTest>
31#include <qcoreapplication.h>
32#include <qstring.h>
33#include <qtemporarydir.h>
34#include <qtemporaryfile.h>
35#include <qfile.h>
36#include <qdatetime.h>
37#include <qdir.h>
38#include <qset.h>
39#include <qtextcodec.h>
40
41#include <QtTest/private/qtesthelpers_p.h>
42
43#if defined(Q_OS_WIN)
44# include <windows.h>
45#endif
46#if defined(Q_OS_UNIX)
47# include <sys/types.h>
48# include <sys/stat.h>
49# include <errno.h>
50# include <fcntl.h> // open(2)
51# include <unistd.h> // close(2)
52#endif
53
54class tst_QTemporaryFile : public QObject
55{
56 Q_OBJECT
57public slots:
58 void initTestCase();
59 void cleanupTestCase();
60
61private slots:
62 void construction();
63 void fileTemplate();
64 void fileTemplate_data();
65 void getSetCheck();
66 void fileName();
67 void fileNameIsEmpty();
68 void autoRemove();
69 void nonWritableCurrentDir();
70 void io();
71 void openCloseOpenClose();
72 void removeAndReOpen();
73 void removeUnnamed();
74 void size();
75 void resize();
76 void openOnRootDrives();
77 void stressTest();
78 void rename();
79 void renameFdLeak();
80 void reOpenThroughQFile();
81 void keepOpenMode();
82 void resetTemplateAfterError();
83 void setTemplateAfterOpen();
84 void autoRemoveAfterFailedRename();
85 void createNativeFile_data();
86 void createNativeFile();
87 void QTBUG_4796_data();
88 void QTBUG_4796();
89 void guaranteeUnique();
90private:
91 QTemporaryDir m_temporaryDir;
92 QString m_previousCurrent;
93};
94
95void tst_QTemporaryFile::initTestCase()
96{
97 QVERIFY2(m_temporaryDir.isValid(), qPrintable(m_temporaryDir.errorString()));
98 m_previousCurrent = QDir::currentPath();
99 QVERIFY(QDir::setCurrent(m_temporaryDir.path()));
100
101 // For QTBUG_4796
102 QVERIFY(QDir("test-XXXXXX").exists() || QDir().mkdir("test-XXXXXX"));
103 QCoreApplication::setApplicationName("tst_qtemporaryfile");
104
105#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
106 QString sourceDir(":/android_testdata/");
107 QDirIterator it(sourceDir, QDirIterator::Subdirectories);
108 while (it.hasNext()) {
109 it.next();
110
111 QFileInfo sourceFileInfo = it.fileInfo();
112 if (!sourceFileInfo.isDir()) {
113 QFileInfo destinationFileInfo(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1Char('/') + sourceFileInfo.filePath().mid(sourceDir.length()));
114
115 if (!destinationFileInfo.exists()) {
116 QVERIFY(QDir().mkpath(destinationFileInfo.path()));
117 QVERIFY(QFile::copy(sourceFileInfo.filePath(), destinationFileInfo.filePath()));
118 }
119 }
120 }
121#endif
122}
123
124void tst_QTemporaryFile::cleanupTestCase()
125{
126 QDir::setCurrent(m_previousCurrent);
127}
128
129void tst_QTemporaryFile::construction()
130{
131 QTemporaryFile file(0);
132 QString tmp = QDir::tempPath();
133 QCOMPARE(file.fileTemplate().left(tmp.size()), tmp);
134 QCOMPARE(file.fileTemplate().at(tmp.size()), QChar('/'));
135}
136
137// Testing get/set functions
138void tst_QTemporaryFile::getSetCheck()
139{
140 QTemporaryFile obj1;
141 // bool QTemporaryFile::autoRemove()
142 // void QTemporaryFile::setAutoRemove(bool)
143 obj1.setAutoRemove(false);
144 QCOMPARE(false, obj1.autoRemove());
145 obj1.setAutoRemove(true);
146 QCOMPARE(true, obj1.autoRemove());
147}
148
149static QString hanTestText()
150{
151 QString text;
152 text += QChar(0x65B0);
153 text += QChar(0x5E10);
154 text += QChar(0x6237);
155 return text;
156}
157
158static QString umlautTestText()
159{
160 QString text;
161 text += QChar(0xc4);
162 text += QChar(0xe4);
163 text += QChar(0xd6);
164 text += QChar(0xf6);
165 text += QChar(0xdc);
166 text += QChar(0xfc);
167 text += QChar(0xdf);
168 return text;
169}
170
171void tst_QTemporaryFile::fileTemplate_data()
172{
173 QTest::addColumn<QString>(name: "constructorTemplate");
174 QTest::addColumn<QString>(name: "prefix");
175 QTest::addColumn<QString>(name: "suffix");
176 QTest::addColumn<QString>(name: "fileTemplate");
177
178 QTest::newRow(dataTag: "constructor default") << "" << "." << "" << "";
179 QTest::newRow(dataTag: "constructor with xxx sufix") << "qt_XXXXXXxxx" << "qt_" << "xxx" << "";
180 QTest::newRow(dataTag: "constructor with xXx sufix") << "qt_XXXXXXxXx" << "qt_" << "xXx" << "";
181 QTest::newRow(dataTag: "constructor with no sufix") << "qt_XXXXXX" << "qt_" << "" << "";
182 QTest::newRow(dataTag: "constructor with >6 X's and xxx suffix") << "qt_XXXXXXXXXXxxx" << "qt_" << "xxx" << "";
183 QTest::newRow(dataTag: "constructor with >6 X's, no suffix") << "qt_XXXXXXXXXX" << "qt_" << "" << "";
184
185 QTest::newRow(dataTag: "constructor with XXXX suffix") << "qt_XXXXXX_XXXX" << "qt_" << "_XXXX" << "";
186 QTest::newRow(dataTag: "constructor with XXXXX suffix") << "qt_XXXXXX_XXXXX" << "qt_" << "_XXXXX" << "";
187 QTest::newRow(dataTag: "constructor with XXXX prefix") << "qt_XXXX" << "qt_XXXX." << "" << "";
188 QTest::newRow(dataTag: "constructor with XXXXX prefix") << "qt_XXXXX" << "qt_XXXXX." << "" << "";
189 QTest::newRow(dataTag: "constructor with XXXX prefix and suffix") << "qt_XXXX_XXXXXX_XXXX" << "qt_XXXX_" << "_XXXX" << "";
190 QTest::newRow(dataTag: "constructor with XXXXX prefix and suffix") << "qt_XXXXX_XXXXXX_XXXXX" << "qt_XXXXX_" << "_XXXXX" << "";
191
192 QTest::newRow(dataTag: "set template, no suffix") << "" << "foo" << "" << "foo";
193 QTest::newRow(dataTag: "set template, with lowercase XXXXXX") << "" << "qt_" << "xxxxxx" << "qt_XXXXXXxxxxxx";
194 QTest::newRow(dataTag: "set template, with xxx") << "" << "qt_" << ".xxx" << "qt_XXXXXX.xxx";
195 QTest::newRow(dataTag: "set template, with >6 X's") << "" << "qt_" << ".xxx" << "qt_XXXXXXXXXXXXXX.xxx";
196 QTest::newRow(dataTag: "set template, with >6 X's, no suffix") << "" << "qt_" << "" << "qt_XXXXXXXXXXXXXX";
197 if (QTestPrivate::canHandleUnicodeFileNames()) {
198 // Test Umlauts (contained in Latin1)
199 QString prefix = "qt_" + umlautTestText();
200 QTest::newRow(dataTag: "Umlauts") << (prefix + "XXXXXX") << prefix << QString() << QString();
201 // Test Chinese
202 prefix = "qt_" + hanTestText();
203 QTest::newRow(dataTag: "Chinese characters") << (prefix + "XXXXXX") << prefix << QString() << QString();
204 }
205}
206
207void tst_QTemporaryFile::fileTemplate()
208{
209 QFETCH(QString, constructorTemplate);
210 QFETCH(QString, prefix);
211 QFETCH(QString, suffix);
212 QFETCH(QString, fileTemplate);
213
214 QTemporaryFile file(constructorTemplate);
215 if (!fileTemplate.isEmpty())
216 file.setFileTemplate(fileTemplate);
217
218 QVERIFY2(file.open(), qPrintable(file.errorString()));
219
220 QString fileName = QFileInfo(file).fileName();
221 if (prefix.length())
222 QCOMPARE(fileName.left(prefix.length()), prefix);
223
224 if (suffix.length())
225 QCOMPARE(fileName.right(suffix.length()), suffix);
226}
227
228
229/*
230 This tests whether the temporary file really gets placed in QDir::tempPath
231*/
232void tst_QTemporaryFile::fileName()
233{
234 // Get QDir::tempPath and make an absolute path.
235 QString tempPath = QDir::tempPath();
236 QString absoluteTempPath = QDir(tempPath).absolutePath();
237 QTemporaryFile file;
238 file.setAutoRemove(true);
239 file.open();
240 QString fileName = file.fileName();
241 QVERIFY2(fileName.contains("/tst_qtemporaryfile."), qPrintable(fileName));
242 QVERIFY(QFile::exists(fileName));
243 // Get path to the temp file, without the file name.
244 QString absoluteFilePath = QFileInfo(fileName).absolutePath();
245#if defined(Q_OS_WIN)
246 absoluteFilePath = absoluteFilePath.toLower();
247 absoluteTempPath = absoluteTempPath.toLower();
248#endif
249 QCOMPARE(absoluteFilePath, absoluteTempPath);
250}
251
252void tst_QTemporaryFile::fileNameIsEmpty()
253{
254 QString filename;
255 {
256 QTemporaryFile file;
257 QVERIFY(file.fileName().isEmpty());
258
259 QVERIFY(file.open());
260 QVERIFY(!file.fileName().isEmpty());
261
262 filename = file.fileName();
263 QVERIFY(QFile::exists(filename));
264
265 file.close();
266 QVERIFY(!file.isOpen());
267 QVERIFY(QFile::exists(filename));
268 QVERIFY(!file.fileName().isEmpty());
269 }
270 QVERIFY(!QFile::exists(filename));
271}
272
273void tst_QTemporaryFile::autoRemove()
274{
275 // Test auto remove
276 QString fileName;
277 {
278 QTemporaryFile file("tempXXXXXX");
279 file.setAutoRemove(true);
280 QVERIFY(file.open());
281 fileName = file.fileName();
282 file.close();
283 }
284 QVERIFY(!fileName.isEmpty());
285 QVERIFY(!QFile::exists(fileName));
286
287 // same, but gets the file name after closing
288 {
289 QTemporaryFile file("tempXXXXXX");
290 file.setAutoRemove(true);
291 QVERIFY(file.open());
292 file.close();
293 fileName = file.fileName();
294 }
295 QVERIFY(!fileName.isEmpty());
296 QVERIFY(!QFile::exists(fileName));
297
298 // Test if disabling auto remove works.
299 {
300 QTemporaryFile file("tempXXXXXX");
301 file.setAutoRemove(false);
302 QVERIFY(file.open());
303 fileName = file.fileName();
304 file.close();
305 }
306 QVERIFY(!fileName.isEmpty());
307 QVERIFY(QFile::exists(fileName));
308 QVERIFY(QFile::remove(fileName));
309
310 // same, but gets the file name after closing
311 {
312 QTemporaryFile file("tempXXXXXX");
313 file.setAutoRemove(false);
314 QVERIFY(file.open());
315 file.close();
316 fileName = file.fileName();
317 }
318 QVERIFY(!fileName.isEmpty());
319 QVERIFY(QFile::exists(fileName));
320 QVERIFY(QFile::remove(fileName));
321
322 // Do not explicitly call setAutoRemove (tests if it really is the default as documented)
323 {
324 QTemporaryFile file("tempXXXXXX");
325 QVERIFY(file.open());
326 fileName = file.fileName();
327 // QTBUG-39976, file mappings should be cleared as well.
328 QVERIFY(file.write("test"));
329 QVERIFY(file.flush());
330 uchar *mapped = file.map(offset: 0, size: file.size());
331 QVERIFY(mapped);
332 file.close();
333 }
334 QVERIFY(!QFile::exists(fileName));
335}
336
337struct ChdirOnReturn
338{
339 ChdirOnReturn(const QString& d) : dir(d) {}
340 ~ChdirOnReturn() {
341 QDir::setCurrent(dir);
342 }
343 QString dir;
344};
345
346void tst_QTemporaryFile::nonWritableCurrentDir()
347{
348#ifdef Q_OS_UNIX
349 if (::geteuid() == 0)
350 QSKIP("not valid running this test as root");
351
352 ChdirOnReturn cor(QDir::currentPath());
353
354#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
355 QDir::setCurrent("/data");
356#else
357 QDir::setCurrent("/home");
358#endif
359
360 // QTemporaryFile("tempXXXXXX") is probably a bad idea in any app
361 // where the current dir could anything...
362 QTemporaryFile file("tempXXXXXX");
363 file.setAutoRemove(true);
364 QVERIFY(!file.open());
365 QVERIFY(file.fileName().isEmpty());
366#endif
367}
368
369void tst_QTemporaryFile::io()
370{
371 QByteArray data("OLE\nOLE\nOLE");
372 QTemporaryFile file;
373 QDateTime before = QDateTime::currentDateTimeUtc().addMSecs(msecs: -250);
374
375 // discard msec component (round down) - not all FSs and OSs support them
376 before.setSecsSinceEpoch(before.toSecsSinceEpoch());
377
378 QVERIFY(file.open());
379 QVERIFY(file.symLinkTarget().isEmpty()); // it's not a link!
380 QFile::Permissions perm = file.permissions();
381 QVERIFY(perm & QFile::ReadOwner);
382 QVERIFY(file.setPermissions(perm));
383
384 QCOMPARE(int(file.size()), 0);
385 QVERIFY(file.resize(data.size()));
386 QCOMPARE(int(file.size()), data.size());
387 QCOMPARE((int)file.write(data), data.size());
388 QCOMPARE(int(file.size()), data.size());
389
390 QDateTime mtime = file.fileTime(time: QFile::FileModificationTime).toUTC();
391 QDateTime btime = file.fileTime(time: QFile::FileBirthTime).toUTC();
392 QDateTime ctime = file.fileTime(time: QFile::FileMetadataChangeTime).toUTC();
393 QDateTime atime = file.fileTime(time: QFile::FileAccessTime).toUTC();
394
395 QDateTime after = QDateTime::currentDateTimeUtc().toUTC().addMSecs(msecs: 250);
396 // round msecs up
397 after.setSecsSinceEpoch(after.toSecsSinceEpoch() + 1);
398
399 // mtime must be valid, the rest could fail
400 QVERIFY(mtime <= after && mtime >= before);
401 QVERIFY(!btime.isValid() || (btime <= after && btime >= before));
402 QVERIFY(!ctime.isValid() || (ctime <= after && ctime >= before));
403 QVERIFY(!btime.isValid() || (btime <= after && btime >= before));
404
405 QVERIFY(file.setFileTime(before.addSecs(-10), QFile::FileModificationTime));
406 mtime = file.fileTime(time: QFile::FileModificationTime).toUTC();
407 QCOMPARE(mtime, before.addSecs(-10));
408
409 file.reset();
410 QFile compare(file.fileName());
411 compare.open(flags: QIODevice::ReadOnly);
412 QCOMPARE(compare.readAll() , data);
413 QCOMPARE(compare.fileTime(QFile::FileModificationTime), mtime);
414}
415
416void tst_QTemporaryFile::openCloseOpenClose()
417{
418 QString fileName;
419 {
420 // Create a temp file
421 QTemporaryFile file("tempXXXXXX");
422 file.setAutoRemove(true);
423 QVERIFY(file.open());
424 file.write(data: "OLE");
425 fileName = file.fileName();
426 QVERIFY(QFile::exists(fileName));
427 file.close();
428
429 // Check that it still exists after being closed
430 QVERIFY(QFile::exists(fileName));
431 QVERIFY(!file.isOpen());
432 QVERIFY(file.open());
433 QCOMPARE(file.readAll(), QByteArray("OLE"));
434 // Check that it's still the same file after being opened again.
435 QCOMPARE(file.fileName(), fileName);
436 }
437 QVERIFY(!QFile::exists(fileName));
438}
439
440void tst_QTemporaryFile::removeAndReOpen()
441{
442 QString fileName;
443 {
444 QTemporaryFile file;
445 file.open();
446 fileName = file.fileName(); // materializes any unnamed file
447 QVERIFY(QFile::exists(fileName));
448
449 QVERIFY(file.remove());
450 QVERIFY(file.fileName().isEmpty());
451 QVERIFY(!QFile::exists(fileName));
452 QVERIFY(!file.remove());
453
454 QVERIFY(file.open());
455 QCOMPARE(QFileInfo(file.fileName()).path(), QFileInfo(fileName).path());
456 fileName = file.fileName();
457 QVERIFY(QFile::exists(fileName));
458 }
459 QVERIFY(!QFile::exists(fileName));
460}
461
462void tst_QTemporaryFile::removeUnnamed()
463{
464 QTemporaryFile file;
465 file.open();
466
467 // we did not call fileName(), so the file name may not have a name
468 QVERIFY(file.remove());
469 QVERIFY(file.fileName().isEmpty());
470
471 // if it was unnamed, this will succeed again, so we can't check the result
472 file.remove();
473}
474
475void tst_QTemporaryFile::size()
476{
477 QTemporaryFile file;
478 QVERIFY(file.open());
479 QVERIFY(!file.isSequential());
480 QByteArray str("foobar");
481 file.write(data: str);
482
483 // On CE it takes more time for the filesystem to update
484 // the information. Usually you have to close it or seek
485 // to get latest information. flush() does not help either.
486 QCOMPARE(file.size(), qint64(6));
487 file.seek(offset: 0);
488 QCOMPARE(file.size(), qint64(6));
489
490 QVERIFY(QFile::exists(file.fileName()));
491 QVERIFY(file.exists());
492}
493
494void tst_QTemporaryFile::resize()
495{
496 QTemporaryFile file;
497 file.setAutoRemove(true);
498 QVERIFY(file.open());
499 QVERIFY(file.resize(100));
500
501 QCOMPARE(QFileInfo(file.fileName()).size(), qint64(100));
502
503 file.close();
504}
505
506void tst_QTemporaryFile::openOnRootDrives()
507{
508#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
509 unsigned int lastErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS);
510#endif
511 // If it's possible to create a file in the root directory, it
512 // must be possible to create a temp file there too.
513 foreach (QFileInfo driveInfo, QDir::drives()) {
514 QFile testFile(driveInfo.filePath() + "XXXXXX.txt");
515 if (testFile.open(flags: QIODevice::ReadWrite)) {
516 testFile.remove();
517 QTemporaryFile file(driveInfo.filePath() + "XXXXXX.txt");
518 file.setAutoRemove(true);
519 QVERIFY(file.open());
520
521 QFileInfo fi(file.fileName());
522 QCOMPARE(fi.absoluteDir(), driveInfo.filePath());
523 }
524 }
525#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
526 SetErrorMode(lastErrorMode);
527#endif
528}
529
530void tst_QTemporaryFile::stressTest()
531{
532 const int iterations = 1000;
533
534 QSet<QString> names;
535 for (int i = 0; i < iterations; ++i) {
536 QTemporaryFile file;
537 file.setAutoRemove(false);
538 QVERIFY2(file.open(), qPrintable(file.errorString()));
539 QVERIFY(!names.contains(file.fileName()));
540 names.insert(value: file.fileName());
541 }
542 for (QSet<QString>::const_iterator it = names.constBegin(); it != names.constEnd(); ++it) {
543 QFile::remove(fileName: *it);
544 }
545}
546
547void tst_QTemporaryFile::rename()
548{
549 // This test checks that the temporary file is deleted, even after a
550 // rename.
551
552 QDir dir;
553 QVERIFY(!dir.exists("temporary-file.txt"));
554
555 QString tempname;
556 {
557 QTemporaryFile file(dir.filePath(fileName: "temporary-file.XXXXXX"));
558
559 QVERIFY(file.open());
560 tempname = file.fileName();
561 QVERIFY(dir.exists(tempname));
562
563 QVERIFY(file.rename("temporary-file.txt"));
564 QVERIFY(!dir.exists(tempname));
565 QVERIFY(dir.exists("temporary-file.txt"));
566 QCOMPARE(file.fileName(), QString("temporary-file.txt"));
567 }
568
569 QVERIFY(!dir.exists(tempname));
570 QVERIFY(!dir.exists("temporary-file.txt"));
571}
572
573void tst_QTemporaryFile::renameFdLeak()
574{
575#if defined(Q_OS_UNIX) && !defined(Q_OS_ANDROID)
576 const QByteArray sourceFile = QFile::encodeName(QFINDTESTDATA(__FILE__));
577 QVERIFY(!sourceFile.isEmpty());
578 // Test this on Unix only
579
580 // Open a bunch of files to force the fd count to go up
581 static const int count = 10;
582 int bunch_of_files[count];
583 for (int i = 0; i < count; ++i) {
584 bunch_of_files[i] = ::open(file: sourceFile.constData(), O_RDONLY);
585 QVERIFY(bunch_of_files[i] != -1);
586 }
587
588 int fd;
589 {
590 QTemporaryFile file;
591 file.setAutoRemove(false);
592 QVERIFY(file.open());
593
594 // close the bunch of files
595 for (int i = 0; i < count; ++i)
596 ::close(fd: bunch_of_files[i]);
597
598 // save the file descriptor for later
599 fd = file.handle();
600
601 // rename the file to something
602 QString newPath = QDir::tempPath() + "/tst_qtemporaryfile-renameFdLeak-" + QString::number(getpid());
603 file.rename(newName: newPath);
604 QFile::remove(fileName: newPath);
605 }
606
607 // check if QTemporaryFile closed the file
608 QVERIFY(::close(fd) == -1 && errno == EBADF);
609#endif
610}
611
612void tst_QTemporaryFile::reOpenThroughQFile()
613{
614 QByteArray data("abcdefghij");
615
616 QTemporaryFile file;
617 QVERIFY(((QFile &)file).open(QIODevice::WriteOnly));
618 QCOMPARE(file.write(data), (qint64)data.size());
619
620 file.close();
621 QVERIFY(file.open());
622 QCOMPARE(file.readAll(), data);
623}
624
625void tst_QTemporaryFile::keepOpenMode()
626{
627 QByteArray data("abcdefghij");
628
629 {
630 QTemporaryFile file;
631 QVERIFY(((QFile &)file).open(QIODevice::WriteOnly));
632 QVERIFY(QIODevice::WriteOnly == file.openMode());
633
634 QCOMPARE(file.write(data), (qint64)data.size());
635 file.close();
636
637 QVERIFY(((QFile &)file).open(QIODevice::ReadOnly));
638 QVERIFY(QIODevice::ReadOnly == file.openMode());
639 QCOMPARE(file.readAll(), data);
640 }
641
642 {
643 QTemporaryFile file;
644 QVERIFY(file.open());
645 QCOMPARE(file.openMode(), QIODevice::ReadWrite);
646 QCOMPARE(file.write(data), (qint64)data.size());
647 QVERIFY(file.rename("temporary-file.txt"));
648
649 QVERIFY(((QFile &)file).open(QIODevice::ReadOnly));
650 QCOMPARE(file.openMode(), QIODevice::ReadOnly);
651 QCOMPARE(file.readAll(), data);
652
653 QVERIFY(((QFile &)file).open(QIODevice::WriteOnly));
654 QCOMPARE(file.openMode(), QIODevice::WriteOnly);
655 }
656}
657
658void tst_QTemporaryFile::resetTemplateAfterError()
659{
660 // calling setFileTemplate on a failed open
661
662 QString tempPath = QDir::tempPath();
663
664 QString const fileTemplate("destination/qt_temp_file_test.XXXXXX");
665 QString const fileTemplate2(tempPath + "/qt_temp_file_test.XXXXXX");
666
667 QVERIFY2( QDir(tempPath).exists() || QDir().mkpath(tempPath), "Test precondition" );
668 QVERIFY2( !QFile::exists("destination"), "Test precondition" );
669 QVERIFY2( !QFile::exists(fileTemplate2) || QFile::remove(fileTemplate2), "Test precondition" );
670
671 QFile file(fileTemplate2);
672 QByteArray fileContent("This file is intentionally NOT left empty.");
673
674 QVERIFY( file.open(QIODevice::ReadWrite | QIODevice::Truncate) );
675 QCOMPARE( file.write(fileContent), (qint64)fileContent.size() );
676 QVERIFY( file.flush() );
677
678 QString fileName;
679 {
680 QTemporaryFile temp;
681
682 QVERIFY( temp.fileName().isEmpty() );
683 QVERIFY( !temp.fileTemplate().isEmpty() );
684
685 temp.setFileTemplate( fileTemplate );
686
687 QVERIFY( temp.fileName().isEmpty() );
688 QCOMPARE( temp.fileTemplate(), fileTemplate );
689
690 QVERIFY( !temp.open() );
691
692 QVERIFY( temp.fileName().isEmpty() );
693 QCOMPARE( temp.fileTemplate(), fileTemplate );
694
695 temp.setFileTemplate( fileTemplate2 );
696 QVERIFY( temp.open() );
697
698 fileName = temp.fileName();
699 QVERIFY( QFile::exists(fileName) );
700 QVERIFY( !fileName.isEmpty() );
701 QVERIFY2( fileName != fileTemplate2,
702 ("Generated name shouldn't be same as template: " + fileTemplate2).toLocal8Bit().constData() );
703 }
704
705 QVERIFY( !QFile::exists(fileName) );
706
707 file.seek(offset: 0);
708 QCOMPARE( QString(file.readAll()), QString(fileContent) );
709 QVERIFY( file.remove() );
710}
711
712void tst_QTemporaryFile::setTemplateAfterOpen()
713{
714 QTemporaryFile temp;
715
716 QVERIFY( temp.fileName().isEmpty() );
717 QVERIFY( !temp.fileTemplate().isEmpty() );
718
719 QVERIFY( temp.open() );
720
721 QString const fileName = temp.fileName();
722 QString const newTemplate("funny-path/funny-name-XXXXXX.tmp");
723
724 QVERIFY( !fileName.isEmpty() );
725 QVERIFY( QFile::exists(fileName) );
726 QVERIFY( !temp.fileTemplate().isEmpty() );
727 QVERIFY( temp.fileTemplate() != newTemplate );
728
729 temp.close(); // QTemporaryFile::setFileTemplate will assert on isOpen() up to 4.5.2
730 temp.setFileTemplate(newTemplate);
731 QCOMPARE( temp.fileTemplate(), newTemplate );
732
733 QVERIFY( temp.open() );
734 QCOMPARE( temp.fileName(), fileName );
735 QCOMPARE( temp.fileTemplate(), newTemplate );
736}
737
738void tst_QTemporaryFile::autoRemoveAfterFailedRename()
739{
740 struct CleanOnReturn
741 {
742 ~CleanOnReturn()
743 {
744 if (!tempName.isEmpty())
745 QFile::remove(fileName: tempName);
746 }
747
748 void reset()
749 {
750 tempName.clear();
751 }
752
753 QString tempName;
754 };
755
756 CleanOnReturn cleaner;
757
758 {
759 QTemporaryFile file;
760 QVERIFY( file.open() );
761 cleaner.tempName = file.fileName();
762
763 QVERIFY( QFile::exists(cleaner.tempName) );
764 QVERIFY( !QFileInfo("i-do-not-exist").isDir() );
765 QVERIFY( !file.rename("i-do-not-exist/file.txt") );
766 QVERIFY( QFile::exists(cleaner.tempName) );
767 }
768
769 QVERIFY( !QFile::exists(cleaner.tempName) );
770 cleaner.reset();
771}
772
773void tst_QTemporaryFile::createNativeFile_data()
774{
775 QTest::addColumn<QString>(name: "filePath");
776 QTest::addColumn<qint64>(name: "currentPos");
777 QTest::addColumn<bool>(name: "valid");
778 QTest::addColumn<QByteArray>(name: "content");
779
780#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
781 const QString nativeFilePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QStringLiteral("/resources/test.txt");
782#else
783 const QString nativeFilePath = QFINDTESTDATA("resources/test.txt");
784#endif
785
786 // File might not exist locally in case of sandboxing or remote testing
787 if (!nativeFilePath.startsWith(s: QLatin1String(":/"))) {
788 QTest::newRow(dataTag: "nativeFile") << nativeFilePath << (qint64)-1 << false << QByteArray();
789 QTest::newRow(dataTag: "nativeFileWithPos") << nativeFilePath << (qint64)5 << false << QByteArray();
790 }
791 QTest::newRow(dataTag: "resourceFile") << ":/resources/test.txt" << (qint64)-1 << true << QByteArray("This is a test");
792 QTest::newRow(dataTag: "resourceFileWithPos") << ":/resources/test.txt" << (qint64)5 << true << QByteArray("This is a test");
793}
794
795void tst_QTemporaryFile::createNativeFile()
796{
797 QFETCH(QString, filePath);
798 QFETCH(qint64, currentPos);
799 QFETCH(bool, valid);
800 QFETCH(QByteArray, content);
801
802 QFile f(filePath);
803 if (currentPos != -1) {
804 f.open(flags: QIODevice::ReadOnly);
805 f.seek(offset: currentPos);
806 }
807 QTemporaryFile *tempFile = QTemporaryFile::createNativeFile(file&: f);
808 QCOMPARE(valid, (bool)tempFile);
809 if (currentPos != -1)
810 QCOMPARE(currentPos, f.pos());
811 if (valid) {
812 QCOMPARE(content, tempFile->readAll());
813 delete tempFile;
814 }
815}
816
817void tst_QTemporaryFile::QTBUG_4796_data()
818{
819 QTest::addColumn<QString>(name: "prefix");
820 QTest::addColumn<QString>(name: "suffix");
821 QTest::addColumn<bool>(name: "openResult");
822
823 QString unicode = QString::fromUtf8(str: "\xc3\xa5\xc3\xa6\xc3\xb8");
824
825 QTest::newRow(dataTag: "<empty>") << QString() << QString() << true;
826 QTest::newRow(dataTag: ".") << QString(".") << QString() << true;
827 QTest::newRow(dataTag: "..") << QString("..") << QString() << true;
828 QTest::newRow(dataTag: "blaXXXXXX") << QString("bla") << QString() << true;
829 QTest::newRow(dataTag: "XXXXXXbla") << QString() << QString("bla") << true;
830 QTest::newRow(dataTag: "does-not-exist/qt_temp.XXXXXX") << QString("does-not-exist/qt_temp") << QString() << false;
831
832 if (QTestPrivate::canHandleUnicodeFileNames()) {
833 QTest::newRow(dataTag: "XXXXXX<unicode>") << QString() << unicode << true;
834 QTest::newRow(dataTag: "<unicode>XXXXXX") << unicode << QString() << true;
835 QTest::newRow(dataTag: "<unicode>XXXXXX<unicode>") << unicode << unicode << true;
836 }
837}
838
839void tst_QTemporaryFile::QTBUG_4796()
840{
841 QVERIFY(QDir("test-XXXXXX").exists());
842
843 struct CleanOnReturn
844 {
845 ~CleanOnReturn()
846 {
847 Q_FOREACH(QString tempName, tempNames)
848 QFile::remove(fileName: tempName);
849 }
850
851 void reset()
852 {
853 tempNames.clear();
854 }
855
856 QStringList tempNames;
857 };
858
859 CleanOnReturn cleaner;
860
861 QFETCH(QString, prefix);
862 QFETCH(QString, suffix);
863 QFETCH(bool, openResult);
864
865 {
866 QString fileTemplate1 = prefix + QString("XX") + suffix;
867 QString fileTemplate2 = prefix + QString("XXXX") + suffix;
868 QString fileTemplate3 = prefix + QString("XXXXXX") + suffix;
869 QString fileTemplate4 = prefix + QString("XXXXXXXX") + suffix;
870
871 QTemporaryFile file1(fileTemplate1);
872 QTemporaryFile file2(fileTemplate2);
873 QTemporaryFile file3(fileTemplate3);
874 QTemporaryFile file4(fileTemplate4);
875 QTemporaryFile file5("test-XXXXXX/" + fileTemplate1);
876 QTemporaryFile file6("test-XXXXXX/" + fileTemplate3);
877
878 QCOMPARE(file1.open(), openResult);
879 QCOMPARE(file2.open(), openResult);
880 QCOMPARE(file3.open(), openResult);
881 QCOMPARE(file4.open(), openResult);
882 QCOMPARE(file5.open(), openResult);
883 QCOMPARE(file6.open(), openResult);
884
885 // force the files to exist, if they are supposed to
886 QCOMPARE(!file1.fileName().isEmpty(), openResult);
887 QCOMPARE(!file2.fileName().isEmpty(), openResult);
888 QCOMPARE(!file3.fileName().isEmpty(), openResult);
889 QCOMPARE(!file4.fileName().isEmpty(), openResult);
890 QCOMPARE(!file5.fileName().isEmpty(), openResult);
891 QCOMPARE(!file6.fileName().isEmpty(), openResult);
892
893 QCOMPARE(file1.exists(), openResult);
894 QCOMPARE(file2.exists(), openResult);
895 QCOMPARE(file3.exists(), openResult);
896 QCOMPARE(file4.exists(), openResult);
897 QCOMPARE(file5.exists(), openResult);
898 QCOMPARE(file6.exists(), openResult);
899
900 // make sure the file exists under the *correct* name
901 if (openResult) {
902 cleaner.tempNames << file1.fileName()
903 << file2.fileName()
904 << file3.fileName()
905 << file4.fileName()
906 << file5.fileName()
907 << file6.fileName();
908
909 QDir currentDir;
910 QString fileName1 = currentDir.relativeFilePath(fileName: file1.fileName());
911 QString fileName2 = currentDir.relativeFilePath(fileName: file2.fileName());
912 QString fileName3 = currentDir.relativeFilePath(fileName: file3.fileName());
913 QString fileName4 = currentDir.relativeFilePath(fileName: file4.fileName());
914 QString fileName5 = currentDir.relativeFilePath(fileName: file5.fileName());
915 QString fileName6 = currentDir.relativeFilePath(fileName: file6.fileName());
916
917 QVERIFY(fileName1.startsWith(fileTemplate1 + QLatin1Char('.')));
918 QVERIFY(fileName2.startsWith(fileTemplate2 + QLatin1Char('.')));
919 QVERIFY(fileName5.startsWith("test-XXXXXX/" + fileTemplate1 + QLatin1Char('.')));
920 QVERIFY(fileName6.startsWith("test-XXXXXX/" + prefix));
921
922 if (!prefix.isEmpty()) {
923 QVERIFY(fileName3.startsWith(prefix));
924 QVERIFY(fileName4.startsWith(prefix));
925 }
926
927 if (!suffix.isEmpty()) {
928 QVERIFY(fileName3.endsWith(suffix));
929 QVERIFY(fileName4.endsWith(suffix));
930 QVERIFY(fileName6.endsWith(suffix));
931 }
932 }
933 }
934
935 Q_FOREACH(QString const &tempName, cleaner.tempNames)
936 QVERIFY( !QFile::exists(tempName) );
937
938 cleaner.reset();
939}
940
941void tst_QTemporaryFile::guaranteeUnique()
942{
943 QDir dir(QDir::tempPath());
944 QString takenFileName;
945
946 // First pass. See which filename QTemporaryFile will try first.
947 {
948 QTemporaryFile tmpFile("testFile1.XXXXXX");
949 tmpFile.open();
950 takenFileName = tmpFile.fileName();
951 QVERIFY(QFile::exists(takenFileName));
952 }
953
954 QVERIFY(!QFile::exists(takenFileName));
955
956 // Create a directory with same name.
957 QVERIFY(dir.mkdir(takenFileName));
958
959 // Second pass, now we have blocked its first attempt with a directory.
960 {
961 QTemporaryFile tmpFile("testFile1.XXXXXX");
962 QVERIFY(tmpFile.open());
963 QString uniqueFileName = tmpFile.fileName();
964 QVERIFY(QFileInfo(uniqueFileName).isFile());
965 QVERIFY(uniqueFileName != takenFileName);
966 }
967
968 QVERIFY(dir.rmdir(takenFileName));
969}
970
971QTEST_MAIN(tst_QTemporaryFile)
972#include "tst_qtemporaryfile.moc"
973

source code of qtbase/tests/auto/corelib/io/qtemporaryfile/tst_qtemporaryfile.cpp