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 FOO module 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 | #include <QtCore/private/qabstractfileengine_p.h> |
30 | #include <QtCore/private/qfsfileengine_p.h> |
31 | |
32 | #include <QtCore/QMutex> |
33 | #include <QtCore/QMutexLocker> |
34 | #include <QtCore/QSharedPointer> |
35 | #include <QtCore/QScopedPointer> |
36 | #include <QtCore/QHash> |
37 | #include <QtCore/QDir> |
38 | #include <QtCore/QDirIterator> |
39 | |
40 | #include <QtTest/QTest> |
41 | |
42 | #include <QtCore/QDebug> |
43 | #include "../../../../shared/filesystem.h" |
44 | |
45 | class tst_QAbstractFileEngine |
46 | : public QObject |
47 | { |
48 | Q_OBJECT |
49 | public slots: |
50 | void initTestCase(); |
51 | void cleanupTestCase(); |
52 | |
53 | private slots: |
54 | void customHandler(); |
55 | |
56 | void fileIO_data(); |
57 | void fileIO(); |
58 | |
59 | void mounting_data(); |
60 | void mounting(); |
61 | private: |
62 | QStringList filesForRemoval; |
63 | QSharedPointer<QTemporaryDir> m_currentDir; |
64 | QString m_previousCurrent; |
65 | }; |
66 | |
67 | class ReferenceFileEngine |
68 | : public QAbstractFileEngine |
69 | { |
70 | public: |
71 | ReferenceFileEngine(const QString &fileName) |
72 | : fileName_(QDir::cleanPath(path: fileName)) |
73 | , position_(-1) |
74 | , openForRead_(false) |
75 | , openForWrite_(false) |
76 | { |
77 | } |
78 | |
79 | bool open(QIODevice::OpenMode openMode) |
80 | { |
81 | if (openForRead_ || openForWrite_) { |
82 | qWarning(msg: "%s: file is already open for %s" , |
83 | Q_FUNC_INFO, |
84 | (openForRead_ ? "reading" : "writing" )); |
85 | return false; |
86 | } |
87 | |
88 | openFile_ = resolveFile(create: openMode & QIODevice::WriteOnly); |
89 | if (!openFile_) |
90 | return false; |
91 | |
92 | position_ = 0; |
93 | if (openMode & QIODevice::ReadOnly) |
94 | openForRead_ = true; |
95 | |
96 | if (openMode & QIODevice::WriteOnly) { |
97 | openForWrite_ = true; |
98 | |
99 | QMutexLocker lock(&openFile_->mutex); |
100 | if (openMode & QIODevice::Truncate |
101 | || !(openForRead_ || openMode & QIODevice::Append)) |
102 | openFile_->content.clear(); |
103 | |
104 | if (openMode & QIODevice::Append) |
105 | position_ = openFile_->content.size(); |
106 | } |
107 | |
108 | return true; |
109 | } |
110 | |
111 | bool close() |
112 | { |
113 | openFile_.clear(); |
114 | |
115 | openForRead_ = false; |
116 | openForWrite_ = false; |
117 | position_ = -1; |
118 | |
119 | return true; |
120 | } |
121 | |
122 | qint64 size() const |
123 | { |
124 | QSharedPointer<File> file = resolveFile(create: false); |
125 | if (!file) |
126 | return 0; |
127 | |
128 | QMutexLocker lock(&file->mutex); |
129 | return file->content.size(); |
130 | } |
131 | |
132 | qint64 pos() const |
133 | { |
134 | if (!openForRead_ && !openForWrite_) { |
135 | qWarning(msg: "%s: file is not open" , Q_FUNC_INFO); |
136 | return -1; |
137 | } |
138 | return position_; |
139 | } |
140 | |
141 | bool seek(qint64 pos) |
142 | { |
143 | if (!openForRead_ && !openForWrite_) { |
144 | qWarning(msg: "%s: file is not open" , Q_FUNC_INFO); |
145 | return false; |
146 | } |
147 | |
148 | if (pos >= 0) { |
149 | position_ = pos; |
150 | return true; |
151 | } |
152 | |
153 | return false; |
154 | } |
155 | |
156 | bool flush() |
157 | { |
158 | if (!openForRead_ && !openForWrite_) { |
159 | qWarning(msg: "%s: file is not open" , Q_FUNC_INFO); |
160 | return false; |
161 | } |
162 | |
163 | return true; |
164 | } |
165 | |
166 | bool remove() |
167 | { |
168 | QMutexLocker lock(&fileSystemMutex); |
169 | int count = fileSystem.remove(key: fileName_); |
170 | |
171 | return (count == 1); |
172 | } |
173 | |
174 | bool copy(const QString &newName) |
175 | { |
176 | QMutexLocker lock(&fileSystemMutex); |
177 | if (!fileSystem.contains(key: fileName_) |
178 | || fileSystem.contains(key: newName)) |
179 | return false; |
180 | |
181 | fileSystem.insert(key: newName, value: fileSystem.value(key: fileName_)); |
182 | return true; |
183 | } |
184 | |
185 | bool rename(const QString &newName) |
186 | { |
187 | QMutexLocker lock(&fileSystemMutex); |
188 | if (!fileSystem.contains(key: fileName_) |
189 | || fileSystem.contains(key: newName)) |
190 | return false; |
191 | |
192 | fileSystem.insert(key: newName, value: fileSystem.take(key: fileName_)); |
193 | return true; |
194 | } |
195 | |
196 | // bool link(const QString &newName) |
197 | // { |
198 | // Q_UNUSED(newName) |
199 | // return false; |
200 | // } |
201 | |
202 | // bool mkdir(const QString &dirName, bool createParentDirectories) const |
203 | // { |
204 | // Q_UNUSED(dirName) |
205 | // Q_UNUSED(createParentDirectories) |
206 | |
207 | // return false; |
208 | // } |
209 | |
210 | // bool rmdir(const QString &dirName, bool recurseParentDirectories) const |
211 | // { |
212 | // Q_UNUSED(dirName) |
213 | // Q_UNUSED(recurseParentDirectories) |
214 | |
215 | // return false; |
216 | // } |
217 | |
218 | bool setSize(qint64 size) |
219 | { |
220 | if (size < 0) |
221 | return false; |
222 | |
223 | QSharedPointer<File> file = resolveFile(create: false); |
224 | if (!file) |
225 | return false; |
226 | |
227 | QMutexLocker lock(&file->mutex); |
228 | file->content.resize(size); |
229 | |
230 | if (openForRead_ || openForWrite_) |
231 | if (position_ > size) |
232 | position_ = size; |
233 | |
234 | return (file->content.size() == size); |
235 | } |
236 | |
237 | FileFlags fileFlags(FileFlags type) const |
238 | { |
239 | QSharedPointer<File> file = resolveFile(create: false); |
240 | if (file) { |
241 | QMutexLocker lock(&file->mutex); |
242 | return (file->fileFlags & type); |
243 | } |
244 | |
245 | return FileFlags(); |
246 | } |
247 | |
248 | // bool setPermissions(uint perms) |
249 | // { |
250 | // Q_UNUSED(perms) |
251 | |
252 | // return false; |
253 | // } |
254 | |
255 | QString fileName(FileName file) const |
256 | { |
257 | switch (file) { |
258 | case DefaultName: |
259 | return QLatin1String("DefaultName" ); |
260 | case BaseName: |
261 | return QLatin1String("BaseName" ); |
262 | case PathName: |
263 | return QLatin1String("PathName" ); |
264 | case AbsoluteName: |
265 | return QLatin1String("AbsoluteName" ); |
266 | case AbsolutePathName: |
267 | return QLatin1String("AbsolutePathName" ); |
268 | case LinkName: |
269 | return QLatin1String("LinkName" ); |
270 | case CanonicalName: |
271 | return QLatin1String("CanonicalName" ); |
272 | case CanonicalPathName: |
273 | return QLatin1String("CanonicalPathName" ); |
274 | case BundleName: |
275 | return QLatin1String("BundleName" ); |
276 | |
277 | default: |
278 | break; |
279 | } |
280 | |
281 | return QString(); |
282 | } |
283 | |
284 | uint ownerId(FileOwner owner) const |
285 | { |
286 | QSharedPointer<File> file = resolveFile(create: false); |
287 | if (file) { |
288 | switch (owner) { |
289 | case OwnerUser: |
290 | { |
291 | QMutexLocker lock(&file->mutex); |
292 | return file->userId; |
293 | } |
294 | case OwnerGroup: |
295 | { |
296 | QMutexLocker lock(&file->mutex); |
297 | return file->groupId; |
298 | } |
299 | } |
300 | } |
301 | |
302 | return -2; |
303 | } |
304 | |
305 | QString owner(FileOwner owner) const |
306 | { |
307 | QSharedPointer<File> file = resolveFile(create: false); |
308 | if (file) { |
309 | uint ownerId; |
310 | switch (owner) { |
311 | case OwnerUser: |
312 | { |
313 | QMutexLocker lock(&file->mutex); |
314 | ownerId = file->userId; |
315 | } |
316 | |
317 | { |
318 | QMutexLocker lock(&fileSystemMutex); |
319 | return fileSystemUsers.value(key: ownerId); |
320 | } |
321 | |
322 | case OwnerGroup: |
323 | { |
324 | QMutexLocker lock(&file->mutex); |
325 | ownerId = file->groupId; |
326 | } |
327 | |
328 | { |
329 | QMutexLocker lock(&fileSystemMutex); |
330 | return fileSystemGroups.value(key: ownerId); |
331 | } |
332 | } |
333 | } |
334 | |
335 | return QString(); |
336 | } |
337 | |
338 | QDateTime fileTime(FileTime time) const |
339 | { |
340 | QSharedPointer<File> file = resolveFile(create: false); |
341 | if (file) { |
342 | QMutexLocker lock(&file->mutex); |
343 | switch (time) { |
344 | case BirthTime: |
345 | return file->birth; |
346 | case MetadataChangeTime: |
347 | return file->change; |
348 | case ModificationTime: |
349 | return file->modification; |
350 | case AccessTime: |
351 | return file->access; |
352 | } |
353 | } |
354 | |
355 | return QDateTime(); |
356 | } |
357 | |
358 | bool setFileTime(const QDateTime &newDate, FileTime time) |
359 | { |
360 | Q_UNUSED(newDate); |
361 | Q_UNUSED(time); |
362 | return false; |
363 | } |
364 | |
365 | void setFileName(const QString &file) |
366 | { |
367 | if (openForRead_ || openForWrite_) |
368 | qWarning(msg: "%s: Can't set file name while file is open" , Q_FUNC_INFO); |
369 | else |
370 | fileName_ = file; |
371 | } |
372 | |
373 | // typedef QAbstractFileEngineIterator Iterator; |
374 | // Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) |
375 | // { |
376 | // Q_UNUSED(filters) |
377 | // Q_UNUSED(filterNames) |
378 | |
379 | // return 0; |
380 | // } |
381 | |
382 | // Iterator *endEntryList() |
383 | // { |
384 | // return 0; |
385 | // } |
386 | |
387 | qint64 read(char *data, qint64 maxLen) |
388 | { |
389 | if (!openForRead_) { |
390 | qWarning(msg: "%s: file must be open for reading" , Q_FUNC_INFO); |
391 | return -1; |
392 | } |
393 | |
394 | if (openFile_.isNull()) { |
395 | qWarning(msg: "%s: file must not be null" , Q_FUNC_INFO); |
396 | return -1; |
397 | } |
398 | |
399 | QMutexLocker lock(&openFile_->mutex); |
400 | qint64 readSize = qMin(a: openFile_->content.size() - position_, b: maxLen); |
401 | if (readSize < 0) |
402 | return -1; |
403 | |
404 | memcpy(dest: data, src: openFile_->content.constData() + position_, n: readSize); |
405 | position_ += readSize; |
406 | |
407 | return readSize; |
408 | } |
409 | |
410 | qint64 write(const char *data, qint64 length) |
411 | { |
412 | if (!openForWrite_) { |
413 | qWarning(msg: "%s: file must be open for writing" , Q_FUNC_INFO); |
414 | return -1; |
415 | } |
416 | |
417 | if (openFile_.isNull()) { |
418 | qWarning(msg: "%s: file must not be null" , Q_FUNC_INFO); |
419 | return -1; |
420 | } |
421 | |
422 | if (length < 0) |
423 | return -1; |
424 | |
425 | QMutexLocker lock(&openFile_->mutex); |
426 | if (openFile_->content.size() == position_) |
427 | openFile_->content.append(s: data, len: length); |
428 | else { |
429 | if (position_ + length > openFile_->content.size()) |
430 | openFile_->content.resize(size: position_ + length); |
431 | openFile_->content.replace(index: position_, len: length, s: data, alen: length); |
432 | } |
433 | |
434 | qint64 writeSize = qMin(a: length, b: openFile_->content.size() - position_); |
435 | position_ += writeSize; |
436 | |
437 | return writeSize; |
438 | } |
439 | |
440 | protected: |
441 | // void setError(QFile::FileError error, const QString &str); |
442 | |
443 | struct File |
444 | { |
445 | File() |
446 | : userId(0) |
447 | , groupId(0) |
448 | , fileFlags( |
449 | ReadOwnerPerm | WriteOwnerPerm | ExeOwnerPerm |
450 | | ReadUserPerm | WriteUserPerm | ExeUserPerm |
451 | | ReadGroupPerm | WriteGroupPerm | ExeGroupPerm |
452 | | ReadOtherPerm | WriteOtherPerm | ExeOtherPerm |
453 | | FileType | ExistsFlag) |
454 | { |
455 | } |
456 | |
457 | QMutex mutex; |
458 | |
459 | uint userId, groupId; |
460 | QAbstractFileEngine::FileFlags fileFlags; |
461 | QDateTime birth, change, modification, access; |
462 | |
463 | QByteArray content; |
464 | }; |
465 | |
466 | QSharedPointer<File> resolveFile(bool create) const |
467 | { |
468 | if (openForRead_ || openForWrite_) { |
469 | if (!openFile_) |
470 | qWarning(msg: "%s: file should not be null" , Q_FUNC_INFO); |
471 | return openFile_; |
472 | } |
473 | |
474 | QMutexLocker lock(&fileSystemMutex); |
475 | if (create) { |
476 | QSharedPointer<File> &p = fileSystem[fileName_]; |
477 | if (p.isNull()) |
478 | p = QSharedPointer<File>::create(); |
479 | return p; |
480 | } |
481 | |
482 | return fileSystem.value(key: fileName_); |
483 | } |
484 | |
485 | static QMutex fileSystemMutex; |
486 | static QHash<uint, QString> fileSystemUsers, fileSystemGroups; |
487 | static QHash<QString, QSharedPointer<File> > fileSystem; |
488 | |
489 | private: |
490 | QString fileName_; |
491 | qint64 position_; |
492 | bool openForRead_; |
493 | bool openForWrite_; |
494 | |
495 | mutable QSharedPointer<File> openFile_; |
496 | }; |
497 | |
498 | class MountingFileEngine : public QFSFileEngine |
499 | { |
500 | public: |
501 | class Iterator : public QAbstractFileEngineIterator |
502 | { |
503 | public: |
504 | Iterator(QDir::Filters filters, const QStringList &filterNames) |
505 | : QAbstractFileEngineIterator(filters, filterNames) |
506 | { |
507 | names.append(t: "foo" ); |
508 | names.append(t: "bar" ); |
509 | index = -1; |
510 | } |
511 | QString currentFileName() const |
512 | { |
513 | return names.at(i: index); |
514 | } |
515 | bool hasNext() const |
516 | { |
517 | return index < names.size() - 1; |
518 | } |
519 | QString next() |
520 | { |
521 | if (!hasNext()) |
522 | return QString(); |
523 | ++index; |
524 | return currentFilePath(); |
525 | } |
526 | QStringList names; |
527 | int index; |
528 | }; |
529 | MountingFileEngine(QString fileName) |
530 | : QFSFileEngine(fileName) |
531 | { |
532 | } |
533 | Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) |
534 | { |
535 | return new Iterator(filters, filterNames); |
536 | } |
537 | FileFlags fileFlags(FileFlags type) const |
538 | { |
539 | if (fileName(file: DefaultName).endsWith(s: ".tar" )) { |
540 | FileFlags ret = QFSFileEngine::fileFlags(type); |
541 | //make this file in file system appear to be a directory |
542 | ret &= ~FileType; |
543 | ret |= DirectoryType; |
544 | return ret; |
545 | } else { |
546 | //file inside the archive |
547 | return ExistsFlag | FileType; |
548 | } |
549 | } |
550 | }; |
551 | |
552 | QMutex ReferenceFileEngine::fileSystemMutex; |
553 | QHash<uint, QString> ReferenceFileEngine::fileSystemUsers, ReferenceFileEngine::fileSystemGroups; |
554 | QHash<QString, QSharedPointer<ReferenceFileEngine::File> > ReferenceFileEngine::fileSystem; |
555 | |
556 | class FileEngineHandler |
557 | : QAbstractFileEngineHandler |
558 | { |
559 | QAbstractFileEngine *create(const QString &fileName) const |
560 | { |
561 | if (fileName.endsWith(s: ".tar" ) || fileName.contains(s: ".tar/" )) |
562 | return new MountingFileEngine(fileName); |
563 | if (fileName.startsWith(s: "QFSFileEngine:" )) |
564 | return new QFSFileEngine(fileName.mid(position: 14)); |
565 | if (fileName.startsWith(s: "reference-file-engine:" )) |
566 | return new ReferenceFileEngine(fileName.mid(position: 22)); |
567 | if (fileName.startsWith(s: "resource:" )) |
568 | return QAbstractFileEngine::create(fileName: QLatin1String(":/tst_qabstractfileengine/resources/" ) + fileName.mid(position: 9)); |
569 | return 0; |
570 | } |
571 | }; |
572 | |
573 | void tst_QAbstractFileEngine::initTestCase() |
574 | { |
575 | m_previousCurrent = QDir::currentPath(); |
576 | m_currentDir = QSharedPointer<QTemporaryDir>::create(); |
577 | QVERIFY2(!m_currentDir.isNull(), qPrintable("Could not create current directory." )); |
578 | QDir::setCurrent(m_currentDir->path()); |
579 | } |
580 | |
581 | void tst_QAbstractFileEngine::cleanupTestCase() |
582 | { |
583 | bool failed = false; |
584 | |
585 | FileEngineHandler handler; |
586 | Q_FOREACH(QString file, filesForRemoval) |
587 | if (!QFile::remove(fileName: file) |
588 | || QFile::exists(fileName: file)) { |
589 | failed = true; |
590 | qDebug() << "Couldn't remove file:" << file; |
591 | } |
592 | |
593 | QVERIFY(!failed); |
594 | |
595 | QDir::setCurrent(m_previousCurrent); |
596 | } |
597 | |
598 | void tst_QAbstractFileEngine::customHandler() |
599 | { |
600 | QScopedPointer<QAbstractFileEngine> file; |
601 | { |
602 | file.reset(other: QAbstractFileEngine::create(fileName: "resource:file.txt" )); |
603 | |
604 | QVERIFY(file); |
605 | } |
606 | |
607 | { |
608 | FileEngineHandler handler; |
609 | |
610 | QFile file("resource:file.txt" ); |
611 | QVERIFY(file.exists()); |
612 | } |
613 | |
614 | { |
615 | QFile file("resource:file.txt" ); |
616 | QVERIFY(!file.exists()); |
617 | } |
618 | } |
619 | |
620 | void tst_QAbstractFileEngine::fileIO_data() |
621 | { |
622 | QTest::addColumn<QString>(name: "fileName" ); |
623 | QTest::addColumn<QByteArray>(name: "readContent" ); |
624 | QTest::addColumn<QByteArray>(name: "writeContent" ); |
625 | QTest::addColumn<bool>(name: "fileExists" ); |
626 | |
627 | QString resourceTxtFile(":/tst_qabstractfileengine/resources/file.txt" ); |
628 | QByteArray readContent("This is a simple text file.\n" ); |
629 | QByteArray writeContent("This contains two lines of text.\n" ); |
630 | |
631 | QTest::newRow(dataTag: "resource" ) << resourceTxtFile << readContent << QByteArray() << true; |
632 | QTest::newRow(dataTag: "native" ) << "native-file.txt" << readContent << writeContent << false; |
633 | QTest::newRow(dataTag: "Forced QFSFileEngine" ) << "QFSFileEngine:QFSFileEngine-file.txt" << readContent << writeContent << false; |
634 | QTest::newRow(dataTag: "Custom FE" ) << "reference-file-engine:file.txt" << readContent << writeContent << false; |
635 | |
636 | QTest::newRow(dataTag: "Forced QFSFileEngine (native)" ) << "QFSFileEngine:native-file.txt" << readContent << writeContent << true; |
637 | QTest::newRow(dataTag: "native (Forced QFSFileEngine)" ) << "QFSFileEngine-file.txt" << readContent << writeContent << true; |
638 | QTest::newRow(dataTag: "Custom FE (2)" ) << "reference-file-engine:file.txt" << readContent << writeContent << true; |
639 | } |
640 | |
641 | void tst_QAbstractFileEngine::fileIO() |
642 | { |
643 | QFETCH(QString, fileName); |
644 | QFETCH(QByteArray, readContent); |
645 | QFETCH(QByteArray, writeContent); |
646 | QFETCH(bool, fileExists); |
647 | |
648 | FileEngineHandler handler; |
649 | |
650 | |
651 | { |
652 | QFile file(fileName); |
653 | QCOMPARE(file.exists(), fileExists); |
654 | |
655 | if (!fileExists) { |
656 | QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Unbuffered)); |
657 | filesForRemoval.append(t: fileName); |
658 | |
659 | QCOMPARE(file.write(readContent), qint64(readContent.size())); |
660 | } |
661 | } |
662 | |
663 | // |
664 | // File content is: readContent |
665 | // |
666 | |
667 | qint64 fileSize = readContent.size(); |
668 | { |
669 | // Reading |
670 | QFile file(fileName); |
671 | QVERIFY(!file.isOpen()); |
672 | |
673 | /* For an exact match, this test requires the repository to |
674 | * be checked out with UNIX-style line endings on Windows. |
675 | * Try to succeed also for the common case of checking out with autocrlf |
676 | * by reading the file as text and checking if the size matches |
677 | * the original size + the '\r' characters added by autocrlf. */ |
678 | |
679 | QFile::OpenMode openMode = QIODevice::ReadOnly | QIODevice::Unbuffered; |
680 | #ifdef Q_OS_WIN |
681 | openMode |= QIODevice::Text; |
682 | #endif |
683 | QVERIFY(file.open(openMode)); |
684 | QVERIFY(file.isOpen()); |
685 | #ifdef Q_OS_WIN |
686 | const qint64 convertedSize = fileSize + readContent.count('\n'); |
687 | if (file.size() == convertedSize) |
688 | fileSize = convertedSize; |
689 | #endif |
690 | QCOMPARE(file.size(), fileSize); |
691 | QCOMPARE(file.pos(), qint64(0)); |
692 | |
693 | QCOMPARE(file.size(), fileSize); |
694 | QCOMPARE(file.readAll(), readContent); |
695 | QCOMPARE(file.pos(), fileSize); |
696 | |
697 | file.close(); |
698 | QVERIFY(!file.isOpen()); |
699 | QCOMPARE(file.size(), fileSize); |
700 | } |
701 | |
702 | if (writeContent.isEmpty()) |
703 | return; |
704 | |
705 | { |
706 | // Writing / appending |
707 | QFile file(fileName); |
708 | |
709 | QVERIFY(!file.isOpen()); |
710 | QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Unbuffered)); |
711 | |
712 | QVERIFY(file.isOpen()); |
713 | QCOMPARE(file.size(), fileSize); |
714 | QCOMPARE(file.pos(), fileSize); |
715 | |
716 | QCOMPARE(file.write(writeContent), qint64(writeContent.size())); |
717 | |
718 | fileSize += writeContent.size(); |
719 | QCOMPARE(file.pos(), fileSize); |
720 | QCOMPARE(file.size(), fileSize); |
721 | |
722 | file.close(); |
723 | QVERIFY(!file.isOpen()); |
724 | QCOMPARE(file.size(), fileSize); |
725 | } |
726 | |
727 | // |
728 | // File content is: readContent + writeContent |
729 | // |
730 | |
731 | { |
732 | // Reading and Writing |
733 | QFile file(fileName); |
734 | |
735 | QVERIFY(!file.isOpen()); |
736 | QVERIFY(file.open(QIODevice::ReadWrite | QIODevice::Unbuffered)); |
737 | |
738 | QVERIFY(file.isOpen()); |
739 | QCOMPARE(file.size(), fileSize); |
740 | QCOMPARE(file.pos(), qint64(0)); |
741 | |
742 | QCOMPARE(file.readAll(), readContent + writeContent); |
743 | QCOMPARE(file.pos(), fileSize); |
744 | QCOMPARE(file.size(), fileSize); |
745 | |
746 | QVERIFY(file.seek(writeContent.size())); |
747 | QCOMPARE(file.pos(), qint64(writeContent.size())); |
748 | QCOMPARE(file.size(), fileSize); |
749 | |
750 | QCOMPARE(file.write(readContent), qint64(readContent.size())); |
751 | QCOMPARE(file.pos(), fileSize); |
752 | QCOMPARE(file.size(), fileSize); |
753 | |
754 | QVERIFY(file.seek(0)); |
755 | QCOMPARE(file.pos(), qint64(0)); |
756 | QCOMPARE(file.size(), fileSize); |
757 | |
758 | QCOMPARE(file.write(writeContent), qint64(writeContent.size())); |
759 | QCOMPARE(file.pos(), qint64(writeContent.size())); |
760 | QCOMPARE(file.size(), fileSize); |
761 | |
762 | QVERIFY(file.seek(0)); |
763 | QCOMPARE(file.read(writeContent.size()), writeContent); |
764 | QCOMPARE(file.pos(), qint64(writeContent.size())); |
765 | QCOMPARE(file.size(), fileSize); |
766 | |
767 | QCOMPARE(file.readAll(), readContent); |
768 | QCOMPARE(file.pos(), fileSize); |
769 | QCOMPARE(file.size(), fileSize); |
770 | |
771 | file.close(); |
772 | QVERIFY(!file.isOpen()); |
773 | QCOMPARE(file.size(), fileSize); |
774 | } |
775 | |
776 | // |
777 | // File content is: writeContent + readContent |
778 | // |
779 | |
780 | { |
781 | // Writing |
782 | QFile file(fileName); |
783 | |
784 | QVERIFY(!file.isOpen()); |
785 | QVERIFY(file.open(QIODevice::ReadWrite | QIODevice::Unbuffered)); |
786 | |
787 | QVERIFY(file.isOpen()); |
788 | QCOMPARE(file.size(), fileSize); |
789 | QCOMPARE(file.pos(), qint64(0)); |
790 | |
791 | QCOMPARE(file.write(writeContent), qint64(writeContent.size())); |
792 | QCOMPARE(file.pos(), qint64(writeContent.size())); |
793 | QCOMPARE(file.size(), fileSize); |
794 | |
795 | QVERIFY(file.resize(writeContent.size())); |
796 | QCOMPARE(file.size(), qint64(writeContent.size())); |
797 | |
798 | file.close(); |
799 | QVERIFY(!file.isOpen()); |
800 | QCOMPARE(file.size(), qint64(writeContent.size())); |
801 | |
802 | QVERIFY(file.resize(fileSize)); |
803 | QCOMPARE(file.size(), fileSize); |
804 | } |
805 | |
806 | // |
807 | // File content is: writeContent + <undefined> |
808 | // File size is : (readContent + writeContent).size() |
809 | // |
810 | |
811 | { |
812 | // Writing / extending |
813 | QFile file(fileName); |
814 | |
815 | QVERIFY(!file.isOpen()); |
816 | QVERIFY(file.open(QIODevice::ReadWrite | QIODevice::Unbuffered)); |
817 | |
818 | QVERIFY(file.isOpen()); |
819 | QCOMPARE(file.size(), fileSize); |
820 | QCOMPARE(file.pos(), qint64(0)); |
821 | |
822 | QVERIFY(file.seek(1024)); |
823 | QCOMPARE(file.pos(), qint64(1024)); |
824 | QCOMPARE(file.size(), fileSize); |
825 | |
826 | fileSize = 1024 + writeContent.size(); |
827 | QCOMPARE(file.write(writeContent), qint64(writeContent.size())); |
828 | QCOMPARE(file.pos(), fileSize); |
829 | QCOMPARE(file.size(), fileSize); |
830 | |
831 | QVERIFY(file.seek(1028)); |
832 | QCOMPARE(file.pos(), qint64(1028)); |
833 | QCOMPARE(file.size(), fileSize); |
834 | |
835 | fileSize = 1028 + writeContent.size(); |
836 | QCOMPARE(file.write(writeContent), qint64(writeContent.size())); |
837 | QCOMPARE(file.pos(), fileSize); |
838 | QCOMPARE(file.size(), fileSize); |
839 | |
840 | file.close(); |
841 | QVERIFY(!file.isOpen()); |
842 | QCOMPARE(file.size(), fileSize); |
843 | } |
844 | |
845 | // |
846 | // File content is: writeContent + <undefined> + writeContent |
847 | // File size is : 1024 + writeContent.size() |
848 | // |
849 | |
850 | { |
851 | // Writing / truncating |
852 | QFile file(fileName); |
853 | |
854 | QVERIFY(!file.isOpen()); |
855 | QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Unbuffered)); |
856 | |
857 | QVERIFY(file.isOpen()); |
858 | QCOMPARE(file.size(), qint64(0)); |
859 | QCOMPARE(file.pos(), qint64(0)); |
860 | |
861 | fileSize = readContent.size(); |
862 | QCOMPARE(file.write(readContent), fileSize); |
863 | QCOMPARE(file.pos(), fileSize); |
864 | QCOMPARE(file.size(), fileSize); |
865 | |
866 | file.close(); |
867 | QVERIFY(!file.isOpen()); |
868 | QCOMPARE(file.size(), fileSize); |
869 | } |
870 | |
871 | // |
872 | // File content is: readContent |
873 | // |
874 | } |
875 | |
876 | void tst_QAbstractFileEngine::mounting_data() |
877 | { |
878 | QTest::addColumn<QString>(name: "fileName" ); |
879 | QTest::newRow(dataTag: "native" ) << "test.tar" ; |
880 | QTest::newRow(dataTag: "Forced QFSFileEngine" ) << "QFSFileEngine:test.tar" ; |
881 | } |
882 | |
883 | void tst_QAbstractFileEngine::mounting() |
884 | { |
885 | FileSystem fs; |
886 | QVERIFY(fs.createFile("test.tar" )); |
887 | FileEngineHandler handler; |
888 | |
889 | QFETCH(QString, fileName); |
890 | const QString absName = fs.absoluteFilePath(fileName); |
891 | |
892 | QVERIFY(QFileInfo(absName).isDir()); |
893 | QDir dir(absName); |
894 | QCOMPARE(dir.entryList(), (QStringList() << "bar" << "foo" )); |
895 | QDir dir2(fs.path()); |
896 | bool found = false; |
897 | foreach (QFileInfo info, dir2.entryInfoList()) { |
898 | if (info.fileName() == QLatin1String("test.tar" )) { |
899 | QVERIFY(!found); |
900 | found = true; |
901 | QVERIFY(info.isDir()); |
902 | } |
903 | } |
904 | QVERIFY(found); |
905 | } |
906 | |
907 | QTEST_APPLESS_MAIN(tst_QAbstractFileEngine) |
908 | #include "tst_qabstractfileengine.moc" |
909 | |
910 | |