| 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 |  |