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 | |
54 | class tst_QTemporaryFile : public QObject |
55 | { |
56 | Q_OBJECT |
57 | public slots: |
58 | void initTestCase(); |
59 | void cleanupTestCase(); |
60 | |
61 | private 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(); |
90 | private: |
91 | QTemporaryDir m_temporaryDir; |
92 | QString m_previousCurrent; |
93 | }; |
94 | |
95 | void 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 | |
124 | void tst_QTemporaryFile::cleanupTestCase() |
125 | { |
126 | QDir::setCurrent(m_previousCurrent); |
127 | } |
128 | |
129 | void 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 |
138 | void 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 | |
149 | static QString hanTestText() |
150 | { |
151 | QString text; |
152 | text += QChar(0x65B0); |
153 | text += QChar(0x5E10); |
154 | text += QChar(0x6237); |
155 | return text; |
156 | } |
157 | |
158 | static 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 | |
171 | void 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 | |
207 | void 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 | */ |
232 | void 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 | |
252 | void 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 | |
273 | void 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 | |
337 | struct ChdirOnReturn |
338 | { |
339 | ChdirOnReturn(const QString& d) : dir(d) {} |
340 | ~ChdirOnReturn() { |
341 | QDir::setCurrent(dir); |
342 | } |
343 | QString dir; |
344 | }; |
345 | |
346 | void 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 | |
369 | void 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 | |
416 | void 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 | |
440 | void 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 | |
462 | void 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 | |
475 | void 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 | |
494 | void 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 | |
506 | void 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 | |
530 | void 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 | |
547 | void 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 | |
573 | void 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 | |
612 | void 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 | |
625 | void 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 | |
658 | void 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 | |
712 | void 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 | |
738 | void 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 | |
773 | void 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 | |
795 | void 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 | |
817 | void 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 | |
839 | void 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 | |
941 | void 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 | |
971 | QTEST_MAIN(tst_QTemporaryFile) |
972 | #include "tst_qtemporaryfile.moc" |
973 | |