From 012ba8c0e5270f962dbc891039c32f49d31c565b Mon Sep 17 00:00:00 2001 From: Holger Ihrig Date: Wed, 24 Aug 2011 12:53:32 +0200 Subject: Moving relevant tests to corelib/io Marked Test for qdiriterator as insignificant. See QTBUG-21160 Marked Test for qresourceengine as insignificant. See QTBUG-21159 Task-number: QTBUG-21066 Change-Id: I72848a651ff3e7aff1d6105dd49124e4ed070a44 Reviewed-on: http://codereview.qt.nokia.com/3577 Reviewed-by: Qt Sanity Bot Reviewed-by: Rohan McGovern Reviewed-by: Jason McDonald --- tests/auto/corelib/io/qfile/.gitattributes | 2 + tests/auto/corelib/io/qfile/.gitignore | 8 + tests/auto/corelib/io/qfile/copy-fallback.qrc | 5 + tests/auto/corelib/io/qfile/dosfile.txt | 14 + tests/auto/corelib/io/qfile/forCopying.txt | 1 + tests/auto/corelib/io/qfile/forRenaming.txt | 7 + .../auto/corelib/io/qfile/largefile/largefile.pro | 8 + .../corelib/io/qfile/largefile/tst_largefile.cpp | 538 ++++ tests/auto/corelib/io/qfile/noendofline.txt | 3 + tests/auto/corelib/io/qfile/qfile.pro | 10 + tests/auto/corelib/io/qfile/qfile.qrc | 5 + tests/auto/corelib/io/qfile/rename-fallback.qrc | 5 + tests/auto/corelib/io/qfile/resources/file1.ext1 | 1 + tests/auto/corelib/io/qfile/stdinprocess/main.cpp | 72 + .../corelib/io/qfile/stdinprocess/stdinprocess.pro | 6 + tests/auto/corelib/io/qfile/test/test.pro | 44 + tests/auto/corelib/io/qfile/testfile.txt | 6 + tests/auto/corelib/io/qfile/testlog.txt | 144 + tests/auto/corelib/io/qfile/tst_qfile.cpp | 3406 ++++++++++++++++++++ tests/auto/corelib/io/qfile/two.dots.file | 1 + 20 files changed, 4286 insertions(+) create mode 100644 tests/auto/corelib/io/qfile/.gitattributes create mode 100644 tests/auto/corelib/io/qfile/.gitignore create mode 100644 tests/auto/corelib/io/qfile/copy-fallback.qrc create mode 100644 tests/auto/corelib/io/qfile/dosfile.txt create mode 100644 tests/auto/corelib/io/qfile/forCopying.txt create mode 100644 tests/auto/corelib/io/qfile/forRenaming.txt create mode 100644 tests/auto/corelib/io/qfile/largefile/largefile.pro create mode 100644 tests/auto/corelib/io/qfile/largefile/tst_largefile.cpp create mode 100644 tests/auto/corelib/io/qfile/noendofline.txt create mode 100644 tests/auto/corelib/io/qfile/qfile.pro create mode 100644 tests/auto/corelib/io/qfile/qfile.qrc create mode 100644 tests/auto/corelib/io/qfile/rename-fallback.qrc create mode 100644 tests/auto/corelib/io/qfile/resources/file1.ext1 create mode 100644 tests/auto/corelib/io/qfile/stdinprocess/main.cpp create mode 100644 tests/auto/corelib/io/qfile/stdinprocess/stdinprocess.pro create mode 100644 tests/auto/corelib/io/qfile/test/test.pro create mode 100644 tests/auto/corelib/io/qfile/testfile.txt create mode 100644 tests/auto/corelib/io/qfile/testlog.txt create mode 100644 tests/auto/corelib/io/qfile/tst_qfile.cpp create mode 100644 tests/auto/corelib/io/qfile/two.dots.file (limited to 'tests/auto/corelib/io/qfile') diff --git a/tests/auto/corelib/io/qfile/.gitattributes b/tests/auto/corelib/io/qfile/.gitattributes new file mode 100644 index 0000000000..3be66dc5eb --- /dev/null +++ b/tests/auto/corelib/io/qfile/.gitattributes @@ -0,0 +1,2 @@ +dosfile.txt -crlf +testfile.txt -crlf \ No newline at end of file diff --git a/tests/auto/corelib/io/qfile/.gitignore b/tests/auto/corelib/io/qfile/.gitignore new file mode 100644 index 0000000000..c508239722 --- /dev/null +++ b/tests/auto/corelib/io/qfile/.gitignore @@ -0,0 +1,8 @@ +tst_qfile +stdinprocess/stdinprocess +stdinprocess/stdinprocess.exe +readonlyfile +newfile.txt +appendfile.txt +oldfile.txt +simplefile.txt diff --git a/tests/auto/corelib/io/qfile/copy-fallback.qrc b/tests/auto/corelib/io/qfile/copy-fallback.qrc new file mode 100644 index 0000000000..864491f326 --- /dev/null +++ b/tests/auto/corelib/io/qfile/copy-fallback.qrc @@ -0,0 +1,5 @@ + + + copy-fallback.qrc + + diff --git a/tests/auto/corelib/io/qfile/dosfile.txt b/tests/auto/corelib/io/qfile/dosfile.txt new file mode 100644 index 0000000000..47205062e1 --- /dev/null +++ b/tests/auto/corelib/io/qfile/dosfile.txt @@ -0,0 +1,14 @@ +/dev/system/root / reiserfs acl,user_xattr 1 1 +/dev/sda1 /boot ext3 acl,user_xattr 1 2 +/dev/system/home /home reiserfs acl,user_xattr 1 2 +/dev/system/temp /tmp reiserfs acl,user_xattr 1 2 +/dev/sda2 swap swap pri=42 0 0 +devpts /dev/pts devpts mode=0620,gid=5 0 0 +proc /proc proc defaults 0 0 +usbfs /proc/bus/usb usbfs noauto 0 0 +sysfs /sys sysfs noauto 0 0 +/dev/dvd /media/dvd subfs noauto,fs=cdfss,ro,procuid,nosuid,nodev,exec,iocharset=utf8 0 0 +/dev/fd0 /media/floppy subfs noauto,fs=floppyfss,procuid,nodev,nosuid,sync 0 0 +/dev/sdb2 /media/ipod subfs noauto,rw,noexec,nosuid,nodev,sync,procuid,user,iocharset=utf8 1 2 +/dev/sdc1 /media/usbkey vfat rw,nosuid,nodev,sync,procuid,user 1 2 + diff --git a/tests/auto/corelib/io/qfile/forCopying.txt b/tests/auto/corelib/io/qfile/forCopying.txt new file mode 100644 index 0000000000..d4143d5958 --- /dev/null +++ b/tests/auto/corelib/io/qfile/forCopying.txt @@ -0,0 +1 @@ +A basic file for copying. diff --git a/tests/auto/corelib/io/qfile/forRenaming.txt b/tests/auto/corelib/io/qfile/forRenaming.txt new file mode 100644 index 0000000000..7032162406 --- /dev/null +++ b/tests/auto/corelib/io/qfile/forRenaming.txt @@ -0,0 +1,7 @@ +---------------------------------------------------------- +DO NOT CHANGE ANY CONTENT OR ACCESS RIGHTS OF THIS FILE!!! +---------------------------------------------------------- + +This file is larger than QFile::rename()'s buffer, which is 4096 bytes. This means it is more than what is returned in its call to read(). + +112345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890234567890 diff --git a/tests/auto/corelib/io/qfile/largefile/largefile.pro b/tests/auto/corelib/io/qfile/largefile/largefile.pro new file mode 100644 index 0000000000..6407cb6b3e --- /dev/null +++ b/tests/auto/corelib/io/qfile/largefile/largefile.pro @@ -0,0 +1,8 @@ +load(qttest_p4) + +QT = core +SOURCES += tst_largefile.cpp + +wince*: SOURCES += $$QT_SOURCE_TREE/src/corelib/kernel/qfunctions_wince.cpp + +CONFIG += parallel_test diff --git a/tests/auto/corelib/io/qfile/largefile/tst_largefile.cpp b/tests/auto/corelib/io/qfile/largefile/tst_largefile.cpp new file mode 100644 index 0000000000..bf6cc688ba --- /dev/null +++ b/tests/auto/corelib/io/qfile/largefile/tst_largefile.cpp @@ -0,0 +1,538 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#ifdef Q_OS_WIN + +#include + +#ifndef Q_OS_WINCE +#include +#endif + +#ifndef FSCTL_SET_SPARSE +// MinGW doesn't define this. +#define FSCTL_SET_SPARSE (0x900C4) +#endif + +#endif // Q_OS_WIN + +class tst_LargeFile + : public QObject +{ + Q_OBJECT + +public: + tst_LargeFile() + : blockSize(1 << 12) + , maxSizeBits() + , fd_(-1) + , stream_(0) + { + #if defined(QT_LARGEFILE_SUPPORT) && !defined(Q_OS_MAC) + maxSizeBits = 36; // 64 GiB + #elif defined(Q_OS_MAC) + // HFS+ does not support sparse files, so we limit file size for the test + // on Mac OS. + maxSizeBits = 32; // 4 GiB + #else + maxSizeBits = 24; // 16 MiB + #endif + } + +private: + void sparseFileData(); + QByteArray const &getDataBlock(int index, qint64 position); + +private slots: + // The LargeFile test case was designed to be run in order as a single unit + + void initTestCase(); + void cleanupTestCase(); + + void init(); + void cleanup(); + + // Create and fill large file + void createSparseFile(); + void fillFileSparsely(); + void closeSparseFile(); + + // Verify file was created + void fileCreated(); + + // Positioning in large files + void filePositioning(); + void fdPositioning(); + void streamPositioning(); + + // Read data from file + void openFileForReading(); + void readFile(); + + // Map/unmap large file + void mapFile(); + void mapOffsetOverflow(); + + void closeFile() { largeFile.close(); } + + // Test data + void fillFileSparsely_data() { sparseFileData(); } + void filePositioning_data() { sparseFileData(); } + void fdPositioning_data() { sparseFileData(); } + void streamPositioning_data() { sparseFileData(); } + void readFile_data() { sparseFileData(); } + void mapFile_data() { sparseFileData(); } + +private: + const int blockSize; + int maxSizeBits; + + QFile largeFile; + + QVector generatedBlocks; + + int fd_; + FILE *stream_; +}; + +/* + Convenience function to hide reinterpret_cast when copying a POD directly + into a QByteArray. + */ +template +static inline void appendRaw(QByteArray &array, T data) +{ + array.append(reinterpret_cast(&data), sizeof(T)); +} + +/* + Pad array with filler up to size. On return, array.size() returns size. + */ +static inline void topUpWith(QByteArray &array, QByteArray filler, int size) +{ + for (int i = (size - array.size()) / filler.size(); i > 0; --i) + array.append(filler); + + if (array.size() < size) { + array.append(filler.left(size - array.size())); + } +} + +/* + Generate a unique data block containing identifiable data. Unaligned, + overlapping and partial blocks should not compare equal. + */ +static inline QByteArray generateDataBlock(int blockSize, QString text, qint64 userBits = -1) +{ + QByteArray block; + block.reserve(blockSize); + + // Use of counter and randomBits means content of block will be dependent + // on the generation order. For (file-)systems that do not support sparse + // files, these can be removed so the test file can be reused and doesn't + // have to be generated for every run. + + static qint64 counter = 0; + + qint64 randomBits = ((qint64)qrand() << 32) + | ((qint64)qrand() & 0x00000000ffffffff); + + appendRaw(block, randomBits); + appendRaw(block, userBits); + appendRaw(block, counter); + appendRaw(block, (qint32)0xdeadbeef); + appendRaw(block, blockSize); + + QByteArray userContent = text.toUtf8(); + appendRaw(block, userContent.size()); + block.append(userContent); + appendRaw(block, (qint64)0); + + // size, so far + appendRaw(block, block.size()); + + QByteArray filler("0123456789"); + block.append(filler.right(10 - block.size() % 10)); + topUpWith(block, filler, blockSize - 3 * sizeof(qint64)); + + appendRaw(block, counter); + appendRaw(block, userBits); + appendRaw(block, randomBits); + + ++counter; + return block; +} + +/* + Generates data blocks the first time they are requested. Keeps copies for reuse. + */ +QByteArray const &tst_LargeFile::getDataBlock(int index, qint64 position) +{ + if (index >= generatedBlocks.size()) + generatedBlocks.resize(index + 1); + + if (generatedBlocks[index].isNull()) { + QString text = QString("Current %1-byte block (index = %2) " + "starts %3 bytes into the file '%4'.") + .arg(blockSize) + .arg(index) + .arg(position) + .arg("qt_largefile.tmp"); + + generatedBlocks[index] = generateDataBlock(blockSize, text, (qint64)1 << index); + } + + return generatedBlocks[index]; +} + +void tst_LargeFile::initTestCase() +{ + QFile file("qt_largefile.tmp"); + QVERIFY( !file.exists() || file.remove() ); +} + +void tst_LargeFile::cleanupTestCase() +{ + if (largeFile.isOpen()) + largeFile.close(); + + QFile file("qt_largefile.tmp"); + QVERIFY( !file.exists() || file.remove() ); +} + +void tst_LargeFile::init() +{ + fd_ = -1; + stream_ = 0; +} + +void tst_LargeFile::cleanup() +{ + if (-1 != fd_) + QT_CLOSE(fd_); + if (stream_) + ::fclose(stream_); +} + +void tst_LargeFile::sparseFileData() +{ + QTest::addColumn("index"); + QTest::addColumn("position"); + QTest::addColumn("block"); + + QTest::newRow(QString("block[%1] @%2)") + .arg(0).arg(0) + .toLocal8Bit().constData()) + << 0 << (qint64)0 << getDataBlock(0, 0); + + // While on Linux sparse files scale well, on Windows, testing at every + // power of 2 leads to very large files. i += 4 gives us a good coverage + // without taxing too much on resources. + for (int index = 12; index <= maxSizeBits; index += 4) { + qint64 position = (qint64)1 << index; + QByteArray block = getDataBlock(index, position); + + QTest::newRow( + QString("block[%1] @%2)") + .arg(index).arg(position) + .toLocal8Bit().constData()) + << index << position << block; + } +} + +void tst_LargeFile::createSparseFile() +{ +#if defined(Q_OS_WIN32) + // On Windows platforms, we must explicitly set the file to be sparse, + // so disk space is not allocated for the full file when writing to it. + HANDLE handle = ::CreateFileA("qt_largefile.tmp", + GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0); + QVERIFY( INVALID_HANDLE_VALUE != handle ); + + DWORD bytes; + if (!::DeviceIoControl(handle, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, + &bytes, NULL)) { + QWARN("Unable to set test file as sparse. " + "Limiting test file to 16MiB."); + maxSizeBits = 24; + } + + int fd = ::_open_osfhandle((intptr_t)handle, 0); + QVERIFY( -1 != fd ); + QVERIFY( largeFile.open(fd, QIODevice::WriteOnly | QIODevice::Unbuffered) ); +#else // !Q_OS_WIN32 + largeFile.setFileName("qt_largefile.tmp"); + QVERIFY( largeFile.open(QIODevice::WriteOnly | QIODevice::Unbuffered) ); +#endif +} + +void tst_LargeFile::closeSparseFile() +{ +#if defined(Q_OS_WIN32) + int fd = largeFile.handle(); +#endif + + largeFile.close(); + +#if defined(Q_OS_WIN32) + if (-1 != fd) + ::_close(fd); +#endif +} + +void tst_LargeFile::fillFileSparsely() +{ + QFETCH( qint64, position ); + QFETCH( QByteArray, block ); + QCOMPARE( block.size(), blockSize ); + + static int lastKnownGoodIndex = 0; + struct ScopeGuard { + ScopeGuard(tst_LargeFile* test) + : this_(test) + , failed(true) + { + QFETCH( int, index ); + index_ = index; + } + + ~ScopeGuard() + { + if (failed) { + this_->maxSizeBits = lastKnownGoodIndex; + QWARN( qPrintable( + QString("QFile::error %1: '%2'. Maximum size bits reset to %3.") + .arg(this_->largeFile.error()) + .arg(this_->largeFile.errorString()) + .arg(this_->maxSizeBits)) ); + } else + lastKnownGoodIndex = qMax(index_, lastKnownGoodIndex); + } + + private: + tst_LargeFile * const this_; + int index_; + + public: + bool failed; + }; + + ScopeGuard resetMaxSizeBitsOnFailure(this); + + QVERIFY( largeFile.seek(position) ); + QCOMPARE( largeFile.pos(), position ); + + QCOMPARE( largeFile.write(block), (qint64)blockSize ); + QCOMPARE( largeFile.pos(), position + blockSize ); + QVERIFY( largeFile.flush() ); + + resetMaxSizeBitsOnFailure.failed = false; +} + +void tst_LargeFile::fileCreated() +{ + QFileInfo info("qt_largefile.tmp"); + + QVERIFY( info.exists() ); + QVERIFY( info.isFile() ); + QVERIFY( info.size() >= ((qint64)1 << maxSizeBits) + blockSize ); +} + +void tst_LargeFile::filePositioning() +{ + QFETCH( qint64, position ); + + QFile file("qt_largefile.tmp"); + QVERIFY( file.open(QIODevice::ReadOnly) ); + + QVERIFY( file.seek(position) ); + QCOMPARE( file.pos(), position ); +} + +void tst_LargeFile::fdPositioning() +{ + QFETCH( qint64, position ); + + fd_ = QT_OPEN("qt_largefile.tmp", + QT_OPEN_RDONLY | QT_OPEN_LARGEFILE); + QVERIFY( -1 != fd_ ); + + QFile file; + QVERIFY( file.open(fd_, QIODevice::ReadOnly) ); + QCOMPARE( file.pos(), (qint64)0 ); + QVERIFY( file.seek(position) ); + QCOMPARE( file.pos(), position ); + + file.close(); + + QCOMPARE( QT_LSEEK(fd_, QT_OFF_T(0), SEEK_SET), QT_OFF_T(0) ); + QCOMPARE( QT_LSEEK(fd_, QT_OFF_T(position), SEEK_SET), QT_OFF_T(position) ); + + QVERIFY( file.open(fd_, QIODevice::ReadOnly) ); + QCOMPARE( QT_LSEEK(fd_, QT_OFF_T(0), SEEK_CUR), QT_OFF_T(position) ); + QCOMPARE( file.pos(), position ); + QVERIFY( file.seek(0) ); + QCOMPARE( file.pos(), (qint64)0 ); + + file.close(); + + QVERIFY( !QT_CLOSE(fd_) ); + fd_ = -1; +} + +void tst_LargeFile::streamPositioning() +{ + QFETCH( qint64, position ); + +#if defined(QT_LARGEFILE_SUPPORT) && defined(Q_CC_MSVC) && _MSC_VER < 1400 + if (position >= (qint64)1 << 31) + QSKIP("MSVC 2003 doesn't have 64 bit versions of fseek/ftell.", SkipSingle); +#endif + + stream_ = QT_FOPEN("qt_largefile.tmp", "rb"); + QVERIFY( 0 != stream_ ); + + QFile file; + QVERIFY( file.open(stream_, QIODevice::ReadOnly) ); + QCOMPARE( file.pos(), (qint64)0 ); + QVERIFY( file.seek(position) ); + QCOMPARE( file.pos(), position ); + + file.close(); + + QVERIFY( !QT_FSEEK(stream_, QT_OFF_T(0), SEEK_SET) ); + QCOMPARE( QT_FTELL(stream_), QT_OFF_T(0) ); + QVERIFY( !QT_FSEEK(stream_, QT_OFF_T(position), SEEK_SET) ); + QCOMPARE( QT_FTELL(stream_), QT_OFF_T(position) ); + + QVERIFY( file.open(stream_, QIODevice::ReadOnly) ); + QCOMPARE( QT_FTELL(stream_), QT_OFF_T(position) ); + QCOMPARE( file.pos(), position ); + QVERIFY( file.seek(0) ); + QCOMPARE( file.pos(), (qint64)0 ); + + file.close(); + + QVERIFY( !::fclose(stream_) ); + stream_ = 0; +} + +void tst_LargeFile::openFileForReading() +{ + largeFile.setFileName("qt_largefile.tmp"); + QVERIFY( largeFile.open(QIODevice::ReadOnly) ); +} + +void tst_LargeFile::readFile() +{ + QFETCH( qint64, position ); + QFETCH( QByteArray, block ); + QCOMPARE( block.size(), blockSize ); + + QVERIFY( largeFile.size() >= position + blockSize ); + + QVERIFY( largeFile.seek(position) ); + QCOMPARE( largeFile.pos(), position ); + + QCOMPARE( largeFile.read(blockSize), block ); + QCOMPARE( largeFile.pos(), position + blockSize ); +} + +void tst_LargeFile::mapFile() +{ + QFETCH( qint64, position ); + QFETCH( QByteArray, block ); + QCOMPARE( block.size(), blockSize ); + + // Keep full block mapped to facilitate OS and/or internal reuse by Qt. + uchar *baseAddress = largeFile.map(position, blockSize); + QVERIFY( baseAddress ); + QVERIFY( qEqual(block.begin(), block.end(), reinterpret_cast(baseAddress)) ); + + for (int offset = 1; offset < blockSize; ++offset) { + uchar *address = largeFile.map(position + offset, blockSize - offset); + + QVERIFY( address ); + if ( !qEqual(block.begin() + offset, block.end(), reinterpret_cast(address)) ) { + qDebug() << "Expected:" << block.toHex(); + qDebug() << "Actual :" << QByteArray(reinterpret_cast(address), blockSize).toHex(); + QVERIFY(false); + } + + QVERIFY( largeFile.unmap( address ) ); + } + + QVERIFY( largeFile.unmap( baseAddress ) ); +} + +void tst_LargeFile::mapOffsetOverflow() +{ +#if defined(Q_OS_MAC) + QSKIP("mmap'ping beyond EOF may succeed; generate bus error on access", SkipAll); +#endif + + // Out-of-range mappings should fail, and not silently clip the offset + for (int i = 50; i < 63; ++i) { + uchar *address = 0; + + address = largeFile.map(((qint64)1 << i), blockSize); + QVERIFY( !address ); + + address = largeFile.map(((qint64)1 << i) + blockSize, blockSize); + QVERIFY( !address ); + } +} + +QTEST_APPLESS_MAIN(tst_LargeFile) +#include "tst_largefile.moc" + diff --git a/tests/auto/corelib/io/qfile/noendofline.txt b/tests/auto/corelib/io/qfile/noendofline.txt new file mode 100644 index 0000000000..e120842ad9 --- /dev/null +++ b/tests/auto/corelib/io/qfile/noendofline.txt @@ -0,0 +1,3 @@ +Do not touch the content of this file. +It shows a text file +with no end-of-line-character on the last line \ No newline at end of file diff --git a/tests/auto/corelib/io/qfile/qfile.pro b/tests/auto/corelib/io/qfile/qfile.pro new file mode 100644 index 0000000000..f41d32789a --- /dev/null +++ b/tests/auto/corelib/io/qfile/qfile.pro @@ -0,0 +1,10 @@ +TEMPLATE = subdirs +wince*|symbian:{ + SUBDIRS = test +} else { + SUBDIRS = test stdinprocess +} + +!symbian:SUBDIRS += largefile + +CONFIG += parallel_test diff --git a/tests/auto/corelib/io/qfile/qfile.qrc b/tests/auto/corelib/io/qfile/qfile.qrc new file mode 100644 index 0000000000..2c63d8afeb --- /dev/null +++ b/tests/auto/corelib/io/qfile/qfile.qrc @@ -0,0 +1,5 @@ + + + resources/ + + diff --git a/tests/auto/corelib/io/qfile/rename-fallback.qrc b/tests/auto/corelib/io/qfile/rename-fallback.qrc new file mode 100644 index 0000000000..c8a894a61d --- /dev/null +++ b/tests/auto/corelib/io/qfile/rename-fallback.qrc @@ -0,0 +1,5 @@ + + + rename-fallback.qrc + + diff --git a/tests/auto/corelib/io/qfile/resources/file1.ext1 b/tests/auto/corelib/io/qfile/resources/file1.ext1 new file mode 100644 index 0000000000..e56e15bb7d --- /dev/null +++ b/tests/auto/corelib/io/qfile/resources/file1.ext1 @@ -0,0 +1 @@ +12345 diff --git a/tests/auto/corelib/io/qfile/stdinprocess/main.cpp b/tests/auto/corelib/io/qfile/stdinprocess/main.cpp new file mode 100644 index 0000000000..57a8d904f5 --- /dev/null +++ b/tests/auto/corelib/io/qfile/stdinprocess/main.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +int main(int argc, char *argv[]) +{ + if (argc < 2) { + printf("usage: stdinprocess >\n"); + printf("echos all its input to its output.\n"); + return 1; + } + + QFile file; + + if (strcmp(argv[1], "all") == 0) { + file.open(stdin, QFile::ReadWrite); + printf("%s", file.readAll().constData()); + } else if (strcmp(argv[1], "line") == 0) { + if (strcmp(argv[2], "0") == 0) { + file.open(stdin, QFile::ReadWrite); + } else { + file.open(0, QFile::ReadWrite); + } + + char line[1024]; + while (file.readLine(line, sizeof(line)) > 0) { + printf("%s", line); + fflush(stdout); + } + } + return 0; +} diff --git a/tests/auto/corelib/io/qfile/stdinprocess/stdinprocess.pro b/tests/auto/corelib/io/qfile/stdinprocess/stdinprocess.pro new file mode 100644 index 0000000000..bf791ffc61 --- /dev/null +++ b/tests/auto/corelib/io/qfile/stdinprocess/stdinprocess.pro @@ -0,0 +1,6 @@ +SOURCES += main.cpp +QT = core +CONFIG -= app_bundle debug_and_release_target +CONFIG += console + + diff --git a/tests/auto/corelib/io/qfile/test/test.pro b/tests/auto/corelib/io/qfile/test/test.pro new file mode 100644 index 0000000000..f4ec12f874 --- /dev/null +++ b/tests/auto/corelib/io/qfile/test/test.pro @@ -0,0 +1,44 @@ +load(qttest_p4) +SOURCES += ../tst_qfile.cpp + +wince*|symbian { + QT = core gui + files.files += ..\\dosfile.txt ..\\noendofline.txt ..\\testfile.txt \ + ..\\testlog.txt ..\\two.dots.file ..\\tst_qfile.cpp \ + ..\\Makefile ..\\forCopying.txt ..\\forRenaming.txt + files.path = . + resour.files += ..\\resources\\file1.ext1 + resour.path = resources + + DEPLOYMENT += files resour +} + +wince* { + SOURCES += $$QT_SOURCE_TREE/src/corelib/kernel/qfunctions_wince.cpp # needed for QT_OPEN + DEFINES += SRCDIR=\\\"\\\" +} else:symbian { + # do not define SRCDIR at all + TARGET.EPOCHEAPSIZE = 0x100000 0x3000000 +} else { + QT = core network + DEFINES += SRCDIR=\\\"$$PWD/../\\\" +} + +RESOURCES += ../qfile.qrc ../rename-fallback.qrc ../copy-fallback.qrc + +TARGET = ../tst_qfile + +win32 { + CONFIG(debug, debug|release) { + TARGET = ../../debug/tst_qfile + } else { + TARGET = ../../release/tst_qfile + } + LIBS+=-lole32 -luuid +} + +symbian { + LIBS+=-lefsrv +} + +mac*:CONFIG+=insignificant_test diff --git a/tests/auto/corelib/io/qfile/testfile.txt b/tests/auto/corelib/io/qfile/testfile.txt new file mode 100644 index 0000000000..a5f25a118a --- /dev/null +++ b/tests/auto/corelib/io/qfile/testfile.txt @@ -0,0 +1,6 @@ +---------------------------------------------------------- +DO NOT CHANGE ANY CONTENT OR ACCESS RIGHTS OF THIS FILE!!! +---------------------------------------------------------- + +This demo file has only six lines +and a size of exactly 245 Bytes. diff --git a/tests/auto/corelib/io/qfile/testlog.txt b/tests/auto/corelib/io/qfile/testlog.txt new file mode 100644 index 0000000000..bcc7222b2f --- /dev/null +++ b/tests/auto/corelib/io/qfile/testlog.txt @@ -0,0 +1,144 @@ +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts +2006-09-29 13:50:08.349: -- File log starts_ +2006-09-29 13:50:08.349: -- File log starts diff --git a/tests/auto/corelib/io/qfile/tst_qfile.cpp b/tests/auto/corelib/io/qfile/tst_qfile.cpp new file mode 100644 index 0000000000..f2031497ed --- /dev/null +++ b/tests/auto/corelib/io/qfile/tst_qfile.cpp @@ -0,0 +1,3406 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN) +#include +#endif +#include +#ifndef Q_OS_WIN +# include +# include +#endif +#ifdef Q_OS_MAC +# include +#elif defined(Q_OS_LINUX) +# include +#elif defined(Q_OS_FREEBSD) +# include +# include +#elif defined(Q_OS_IRIX) +# include +#elif defined(Q_OS_WINCE) +# include +# include +#elif defined(Q_OS_SYMBIAN) +# include +#endif + +#include +#include +#include "../../../network-settings.h" + +#if defined(Q_OS_SYMBIAN) +# define SRCDIR "" +#endif + +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif + +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + +#ifndef QT_OPEN_BINARY +#define QT_OPEN_BINARY 0 +#endif + +Q_DECLARE_METATYPE(QFile::FileError) + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QFile : public QObject +{ + Q_OBJECT + +public: + tst_QFile(); + virtual ~tst_QFile(); + + +public slots: + void init(); + void cleanup(); +private slots: + void initTestCase(); + void cleanupTestCase(); + void exists(); + void open_data(); + void open(); + void openUnbuffered(); + void size_data(); + void size(); + void sizeNoExist(); + void seek(); + void setSize(); + void setSizeSeek(); + void atEnd(); + void readLine(); + void readLine2(); + void readLineNullInLine(); + void readAll_data(); + void readAll(); + void readAllBuffer(); + void readAllStdin(); + void readLineStdin(); + void readLineStdin_lineByLine(); + void text(); + void missingEndOfLine(); + void readBlock(); + void getch(); + void ungetChar(); + void createFile(); + void append(); + void permissions_data(); + void permissions(); + void setPermissions(); + void copy(); + void copyAfterFail(); + void copyRemovesTemporaryFile() const; + void copyShouldntOverwrite(); + void copyFallback(); + void link(); + void linkToDir(); + void absolutePathLinkToRelativePath(); + void readBrokenLink(); + void readTextFile_data(); + void readTextFile(); + void readTextFile2(); + void writeTextFile_data(); + void writeTextFile(); + /* void largeFileSupport(); */ +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + void largeUncFileSupport(); +#endif + void tailFile(); + void flush(); + void bufferedRead(); + void isSequential(); + void encodeName(); + void truncate(); + void seekToPos(); + void seekAfterEndOfFile(); + void FILEReadWrite(); + void i18nFileName_data(); + void i18nFileName(); + void longFileName_data(); + void longFileName(); + void fileEngineHandler(); + void useQFileInAFileHandler(); + void getCharFF(); + void remove_and_exists(); + void removeOpenFile(); + void fullDisk(); + void writeLargeDataBlock_data(); + void writeLargeDataBlock(); + void readFromWriteOnlyFile(); + void writeToReadOnlyFile(); + void virtualFile(); + void textFile(); + void rename_data(); + void rename(); + void renameWithAtEndSpecialFile() const; + void renameFallback(); + void renameMultiple(); + void appendAndRead(); + void miscWithUncPathAsCurrentDir(); + void standarderror(); + void handle(); + void nativeHandleLeaks(); + + void readEof_data(); + void readEof(); + + void map_data(); + void map(); + void mapResource_data(); + void mapResource(); + void mapOpenMode_data(); + void mapOpenMode(); + + void openStandardStreams(); + + void resize_data(); + void resize(); + + void objectConstructors(); +#ifdef Q_OS_SYMBIAN + void platformSecurity_data(); + void platformSecurity(); +#endif + void caseSensitivity(); + + void autocloseHandle(); + + // --- Task related tests below this line + void task167217(); + + void openDirectory(); + void writeNothing(); + +public: +// disabled this test for the moment... it hangs + void invalidFile_data(); + void invalidFile(); + +private: + enum FileType { + OpenQFile, + OpenFd, + OpenStream, +#ifdef Q_OS_SYMBIAN + OpenRFile, +#endif + NumberOfFileTypes + }; + + void openStandardStreamsFileDescriptors(); + void openStandardStreamsBufferedStreams(); + + bool openFd(QFile &file, QIODevice::OpenMode mode, QFile::FileHandleFlags handleFlags) + { + int fdMode = QT_OPEN_LARGEFILE | QT_OPEN_BINARY; + + // File will be truncated if in Write mode. + if (mode & QIODevice::WriteOnly) + fdMode |= QT_OPEN_WRONLY | QT_OPEN_TRUNC; + if (mode & QIODevice::ReadOnly) + fdMode |= QT_OPEN_RDONLY; + + fd_ = QT_OPEN(qPrintable(file.fileName()), fdMode); + + return (-1 != fd_) && file.open(fd_, mode, handleFlags); + } + + bool openStream(QFile &file, QIODevice::OpenMode mode, QFile::FileHandleFlags handleFlags) + { + char const *streamMode = ""; + + // File will be truncated if in Write mode. + if (mode & QIODevice::WriteOnly) + streamMode = "wb+"; + else if (mode & QIODevice::ReadOnly) + streamMode = "rb"; + + stream_ = QT_FOPEN(qPrintable(file.fileName()), streamMode); + + return stream_ && file.open(stream_, mode, handleFlags); + } + +#ifdef Q_OS_SYMBIAN + bool openRFile(QFile &file, QIODevice::OpenMode mode, QFile::FileHandleFlags handleFlags) + { + //connect file server first time + if (!rfs_.Handle() && rfs_.Connect() != KErrNone) + return false; + //symbian does not like ./ in filenames, so open by absolute path + QString fileName(QDir::toNativeSeparators(QFileInfo(file).absoluteFilePath())); + TPtrC fn(fileName.utf16(), fileName.length()); + TInt smode = 0; + if (mode & QIODevice::WriteOnly) + smode |= EFileWrite; + if (mode & QIODevice::ReadOnly) + smode |= EFileRead; + TInt r; + if ((mode & QIODevice::Truncate) || (!(mode & QIODevice::ReadOnly) && !(mode & QIODevice::Append))) { + r = rfile_.Replace(rfs_, fn, smode); + } else { + r = rfile_.Open(rfs_, fn, smode); + if (r == KErrNotFound && (mode & QIODevice::WriteOnly)) { + r = rfile_.Create(rfs_, fn, smode); + } + } + return (r == KErrNone) && file.open(rfile_, mode, handleFlags); + } +#endif + + bool openFile(QFile &file, QIODevice::OpenMode mode, FileType type = OpenQFile, QFile::FileHandleFlags handleFlags = QFile::DontCloseHandle) + { + if (mode & QIODevice::WriteOnly && !file.exists()) + { + // Make sure the file exists + QFile createFile(file.fileName()); + if (!createFile.open(QIODevice::ReadWrite)) + return false; + } + + // Note: openFd and openStream will truncate the file if write mode. + switch (type) + { + case OpenQFile: + return file.open(mode); + + case OpenFd: + return openFd(file, mode, handleFlags); + + case OpenStream: + return openStream(file, mode, handleFlags); +#ifdef Q_OS_SYMBIAN + case OpenRFile: + return openRFile(file, mode, handleFlags); +#endif + case NumberOfFileTypes: + break; + } + + return false; + } + + void closeFile(QFile &file) + { + file.close(); + + if (-1 != fd_) + QT_CLOSE(fd_); + if (stream_) + ::fclose(stream_); +#ifdef Q_OS_SYMBIAN + if (rfile_.SubSessionHandle()) + rfile_.Close(); +#endif + + fd_ = -1; + stream_ = 0; + } + + int fd_; + FILE *stream_; +#ifdef Q_OS_SYMBIAN + RFs rfs_; + RFile rfile_; +#endif +}; + +tst_QFile::tst_QFile() +{ +} + +tst_QFile::~tst_QFile() +{ + +} + +void tst_QFile::init() +{ +// TODO: Add initialization code here. +// This will be executed immediately before each test is run. + fd_ = -1; + stream_ = 0; +} + +void tst_QFile::cleanup() +{ +// TODO: Add cleanup code here. +// This will be executed immediately after each test is run. + + // for copyFallback() + if (QFile::exists("file-copy-destination.txt")) { + QFile::setPermissions("file-copy-destination.txt", + QFile::ReadOwner | QFile::WriteOwner); + QFile::remove("file-copy-destination.txt"); + } + + // for renameFallback() + QFile::remove("file-rename-destination.txt"); + + // for copyAfterFail() + QFile::remove("file-to-be-copied.txt"); + QFile::remove("existing-file.txt"); + QFile::remove("copied-file-1.txt"); + QFile::remove("copied-file-2.txt"); + + // for renameMultiple() + QFile::remove("file-to-be-renamed.txt"); + QFile::remove("existing-file.txt"); + QFile::remove("file-renamed-once.txt"); + QFile::remove("file-renamed-twice.txt"); + + if (-1 != fd_) + QT_CLOSE(fd_); + if (stream_) + ::fclose(stream_); +} + +void tst_QFile::initTestCase() +{ + QFile::remove("noreadfile"); + + // create a file and make it read-only + QFile file("readonlyfile"); + file.open(QFile::WriteOnly); + file.write("a", 1); + file.close(); + file.setPermissions(QFile::ReadOwner); + + // create another file and make it not readable + file.setFileName("noreadfile"); + file.open(QFile::WriteOnly); + file.write("b", 1); + file.close(); + file.setPermissions(0); +} + +void tst_QFile::cleanupTestCase() +{ + // clean up the files we created + QFile::remove("readonlyfile"); + QFile::remove("noreadfile"); + QFile::remove("myLink.lnk"); + QFile::remove("appendme.txt"); + QFile::remove("createme.txt"); + QFile::remove("file.txt"); + QFile::remove("genfile.txt"); + QFile::remove("seekToPos.txt"); + QFile::remove("setsizeseek.txt"); + QFile::remove("stdfile.txt"); + QFile::remove("textfile.txt"); + QFile::remove("truncate.txt"); + QFile::remove("winfile.txt"); + QFile::remove("writeonlyfile"); + QFile::remove("largeblockfile.txt"); + QFile::remove("tst_qfile_copy.cpp"); + QFile::remove("nullinline.txt"); + QFile::remove("myLink2.lnk"); + QFile::remove("resources"); + QFile::remove("qfile_map_testfile"); + QFile::remove("readAllBuffer.txt"); + QFile::remove("qt_file.tmp"); + QFile::remove("File.txt"); +} + +//------------------------------------------ +// The 'testfile' is currently just a +// testfile. The path of this file, the +// attributes and the contents itself +// will be changed as far as we have a +// proper way to handle files in the +// testing environment. +//------------------------------------------ + +void tst_QFile::exists() +{ + QFile f( SRCDIR "testfile.txt" ); + QCOMPARE( f.exists(), (bool)TRUE ); + + QFile file("nobodyhassuchafile"); + file.remove(); + QVERIFY(!file.exists()); + + QFile file2("nobodyhassuchafile"); + QVERIFY(file2.open(QIODevice::WriteOnly)); + file2.close(); + + QVERIFY(file.exists()); + + QVERIFY(file.open(QIODevice::WriteOnly)); + file.close(); + QVERIFY(file.exists()); + + file.remove(); + QVERIFY(!file.exists()); + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + QFile unc("//" + QtNetworkSettings::winServerName() + "/testshare/readme.txt"); + QVERIFY(unc.exists()); +#endif +} + +void tst_QFile::open_data() +{ + QTest::addColumn("filename"); + QTest::addColumn("mode"); + QTest::addColumn("ok"); + QTest::addColumn("status"); + +#ifdef Q_OS_MAC + static const QString denied("Operation not permitted"); +#else + static const QString denied("Permission denied"); +#endif + QTest::newRow( "exist_readOnly" ) + << QString(SRCDIR "testfile.txt") << int(QIODevice::ReadOnly) + << (bool)TRUE << QFile::NoError; + + QTest::newRow( "exist_writeOnly" ) + << QString("readonlyfile") + << int(QIODevice::WriteOnly) + << (bool)FALSE + << QFile::OpenError; + + QTest::newRow( "exist_append" ) + << QString("readonlyfile") << int(QIODevice::Append) + << (bool)FALSE << QFile::OpenError; + + QTest::newRow( "nonexist_readOnly" ) + << QString("nonExist.txt") << int(QIODevice::ReadOnly) + << (bool)FALSE << QFile::OpenError; + + QTest::newRow("emptyfile") + << QString("") + << int(QIODevice::ReadOnly) + << (bool)FALSE + << QFile::OpenError; + + QTest::newRow("nullfile") << QString() << int(QIODevice::ReadOnly) << (bool)FALSE + << QFile::OpenError; + + QTest::newRow("two-dots") << QString(SRCDIR "two.dots.file") << int(QIODevice::ReadOnly) << (bool)TRUE + << QFile::NoError; + + QTest::newRow("readonlyfile") << QString("readonlyfile") << int(QIODevice::WriteOnly) + << (bool)FALSE << QFile::OpenError; + QTest::newRow("noreadfile") << QString("noreadfile") << int(QIODevice::ReadOnly) + << (bool)FALSE << QFile::OpenError; +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + QTest::newRow("//./PhysicalDrive0") << QString("//./PhysicalDrive0") << int(QIODevice::ReadOnly) + << (bool)TRUE << QFile::NoError; + QTest::newRow("uncFile") << "//" + QtNetworkSettings::winServerName() + "/testshare/test.pri" << int(QIODevice::ReadOnly) + << true << QFile::NoError; +#endif +} + +void tst_QFile::open() +{ + QFETCH( QString, filename ); + QFETCH( int, mode ); + + QFile f( filename ); + + QFETCH( bool, ok ); + +#if defined(Q_OS_SYMBIAN) + if (qstrcmp(QTest::currentDataTag(), "noreadfile") == 0) + QSKIP("Symbian does not support non-readable files", SkipSingle); +#elif defined(Q_OS_UNIX) + if (::getuid() == 0) + // root and Chuck Norris don't care for file permissions. Skip. + QSKIP("Running this test as root doesn't make sense", SkipAll); +#endif + +#if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) + QEXPECT_FAIL("noreadfile", "Windows does not currently support non-readable files.", Abort); +#endif + if (filename.isEmpty()) + QTest::ignoreMessage(QtWarningMsg, "QFSFileEngine::open: No file name specified"); + + QCOMPARE(f.open( QIODevice::OpenMode(mode) ), ok); + + QTEST( f.error(), "status" ); +} + +void tst_QFile::openUnbuffered() +{ + QFile file(SRCDIR "testfile.txt"); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Unbuffered)); + char c = '\0'; + QVERIFY(file.seek(1)); + QCOMPARE(file.pos(), qint64(1)); + QVERIFY(file.getChar(&c)); + QCOMPARE(file.pos(), qint64(2)); + char d = '\0'; + QVERIFY(file.seek(3)); + QCOMPARE(file.pos(), qint64(3)); + QVERIFY(file.getChar(&d)); + QCOMPARE(file.pos(), qint64(4)); + QVERIFY(file.seek(1)); + QCOMPARE(file.pos(), qint64(1)); + char c2 = '\0'; + QVERIFY(file.getChar(&c2)); + QCOMPARE(file.pos(), qint64(2)); + QVERIFY(file.seek(3)); + QCOMPARE(file.pos(), qint64(3)); + char d2 = '\0'; + QVERIFY(file.getChar(&d2)); + QCOMPARE(file.pos(), qint64(4)); + QCOMPARE(c, c2); + QCOMPARE(d, d2); + QCOMPARE(c, '-'); + QCOMPARE(d, '-'); +} + +void tst_QFile::size_data() +{ + QTest::addColumn("filename"); + QTest::addColumn("size"); + + QTest::newRow( "exist01" ) << QString(SRCDIR "testfile.txt") << (qint64)245; +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + // Only test UNC on Windows./ + QTest::newRow("unc") << "//" + QString(QtNetworkSettings::winServerName() + "/testshare/test.pri") << (qint64)34; +#endif +} + +void tst_QFile::size() +{ + QFETCH( QString, filename ); + QFETCH( qint64, size ); + +#ifdef Q_WS_WINCE + filename = QFileInfo(filename).absoluteFilePath(); +#endif + + { + QFile f( filename ); + QCOMPARE( f.size(), size ); + + QVERIFY( f.open(QIODevice::ReadOnly) ); + QCOMPARE( f.size(), size ); + } + + { + QFile f; + FILE* stream = QT_FOPEN(filename.toLocal8Bit().constData(), "rb"); + QVERIFY( stream ); + QVERIFY( f.open(stream, QIODevice::ReadOnly) ); + QCOMPARE( f.size(), size ); + + f.close(); + fclose(stream); + } + + { +#ifdef Q_WS_WINCE + QSKIP("Currently low level file I/O not well supported on Windows CE", SkipSingle); +#endif + QFile f; + + int fd = QT_OPEN(filename.toLocal8Bit().constData(), QT_OPEN_RDONLY); + + QVERIFY( fd != -1 ); + QVERIFY( f.open(fd, QIODevice::ReadOnly) ); + QCOMPARE( f.size(), size ); + + f.close(); + QT_CLOSE(fd); + } +} + +void tst_QFile::sizeNoExist() +{ + QFile file("nonexist01"); + QVERIFY( !file.exists() ); + QCOMPARE( file.size(), (qint64)0 ); + QVERIFY( !file.open(QIODevice::ReadOnly) ); +} + +void tst_QFile::seek() +{ + QFile::remove("newfile.txt"); + QFile file("newfile.txt"); + file.open(QIODevice::WriteOnly); + QCOMPARE(file.size(), qint64(0)); + QCOMPARE(file.pos(), qint64(0)); + QVERIFY(file.seek(10)); + QCOMPARE(file.pos(), qint64(10)); + QCOMPARE(file.size(), qint64(0)); + file.close(); + QFile::remove("newfile.txt"); +} + +void tst_QFile::setSize() +{ + DEPENDS_ON( "size" ); + + if ( QFile::exists( "createme.txt" ) ) + QFile::remove( "createme.txt" ); + QVERIFY( !QFile::exists( "createme.txt" ) ); + + QFile f("createme.txt"); + QVERIFY(f.open(QIODevice::Truncate | QIODevice::ReadWrite)); + f.putChar('a'); + + f.seek(0); + char c = '\0'; + f.getChar(&c); + QCOMPARE(c, 'a'); + + QCOMPARE(f.size(), (qlonglong)1); + bool ok = f.resize(99); + QVERIFY(ok); + QCOMPARE(f.size(), (qlonglong)99); + + f.seek(0); + c = '\0'; + f.getChar(&c); + QCOMPARE(c, 'a'); + + QVERIFY(f.resize(1)); + QCOMPARE(f.size(), (qlonglong)1); + + f.seek(0); + c = '\0'; + f.getChar(&c); + QCOMPARE(c, 'a'); + + f.close(); + + QCOMPARE(f.size(), (qlonglong)1); + QVERIFY(f.resize(100)); + QCOMPARE(f.size(), (qlonglong)100); + QVERIFY(f.resize(50)); + QCOMPARE(f.size(), (qlonglong)50); +} + +void tst_QFile::setSizeSeek() +{ + QFile::remove("setsizeseek.txt"); + QFile f("setsizeseek.txt"); + QVERIFY(f.open(QFile::WriteOnly)); + f.write("ABCD"); + + QCOMPARE(f.pos(), qint64(4)); + f.resize(2); + QCOMPARE(f.pos(), qint64(2)); + f.resize(4); + QCOMPARE(f.pos(), qint64(2)); + f.resize(0); + QCOMPARE(f.pos(), qint64(0)); + f.resize(4); + QCOMPARE(f.pos(), qint64(0)); + + f.seek(3); + QCOMPARE(f.pos(), qint64(3)); + f.resize(2); + QCOMPARE(f.pos(), qint64(2)); +} + +void tst_QFile::atEnd() +{ + QFile f( SRCDIR "testfile.txt" ); + QVERIFY(f.open( QIODevice::ReadOnly )); + + int size = f.size(); + f.seek( size ); + + bool end = f.atEnd(); + f.close(); + QCOMPARE( end, (bool)TRUE ); +} + +void tst_QFile::readLine() +{ + QFile f( SRCDIR "testfile.txt" ); + QVERIFY(f.open( QIODevice::ReadOnly )); + + int i = 0; + char p[128]; + int foo; + while ( (foo=f.readLine( p, 128 )) > 0 ) { + ++i; + if ( i == 5 ) { + QCOMPARE( p[0], 'T' ); + QCOMPARE( p[3], 's' ); + QCOMPARE( p[11], 'i' ); + } + } + f.close(); + QCOMPARE( i, 6 ); +} + +void tst_QFile::readLine2() +{ + QFile f( SRCDIR "testfile.txt" ); + f.open( QIODevice::ReadOnly ); + + char p[128]; + QCOMPARE(f.readLine(p, 60), qlonglong(59)); + QCOMPARE(f.readLine(p, 60), qlonglong(59)); + memset(p, '@', sizeof(p)); + QCOMPARE(f.readLine(p, 60), qlonglong(59)); + + QCOMPARE(p[57], '-'); + QCOMPARE(p[58], '\n'); + QCOMPARE(p[59], '\0'); + QCOMPARE(p[60], '@'); +} + +void tst_QFile::readLineNullInLine() +{ + QFile::remove("nullinline.txt"); + QFile file("nullinline.txt"); + QVERIFY(file.open(QIODevice::ReadWrite)); + QVERIFY(file.write("linewith\0null\nanotherline\0withnull\n\0\nnull\0", 42) > 0); + QVERIFY(file.flush()); + file.reset(); + + QCOMPARE(file.readLine(), QByteArray("linewith\0null\n", 14)); + QCOMPARE(file.readLine(), QByteArray("anotherline\0withnull\n", 21)); + QCOMPARE(file.readLine(), QByteArray("\0\n", 2)); + QCOMPARE(file.readLine(), QByteArray("null\0", 5)); + QCOMPARE(file.readLine(), QByteArray()); +} + +void tst_QFile::readAll_data() +{ + QTest::addColumn("textMode"); + QTest::addColumn("fileName"); + QTest::newRow( "TextMode unixfile" ) << true << SRCDIR "testfile.txt"; + QTest::newRow( "BinaryMode unixfile" ) << false << SRCDIR "testfile.txt"; + QTest::newRow( "TextMode dosfile" ) << true << SRCDIR "dosfile.txt"; + QTest::newRow( "BinaryMode dosfile" ) << false << SRCDIR "dosfile.txt"; + QTest::newRow( "TextMode bigfile" ) << true << SRCDIR "tst_qfile.cpp"; + QTest::newRow( "BinaryMode bigfile" ) << false << SRCDIR "tst_qfile.cpp"; + QVERIFY(QFile(SRCDIR "tst_qfile.cpp").size() > 64*1024); +} + +void tst_QFile::readAll() +{ + QFETCH( bool, textMode ); + QFETCH( QString, fileName ); + + QFile file(fileName); + if (textMode) + QVERIFY(file.open(QFile::Text | QFile::ReadOnly)); + else + QVERIFY(file.open(QFile::ReadOnly)); + + QByteArray a = file.readAll(); + file.reset(); + QVERIFY(file.pos() == 0); + + QVERIFY(file.bytesAvailable() > 7); + QByteArray b = file.read(1); + char x; + file.getChar(&x); + b.append(x); + b.append(file.read(5)); + b.append(file.readAll()); + + QCOMPARE(a, b); +} + +void tst_QFile::readAllBuffer() +{ + QString fileName = QLatin1String("readAllBuffer.txt"); + + QFile::remove(fileName); + + QFile writer(fileName); + QFile reader(fileName); + + QByteArray data1("This is arguably a very simple text."); + QByteArray data2("This is surely not as simple a test."); + + QVERIFY( writer.open(QIODevice::ReadWrite | QIODevice::Unbuffered) ); + QVERIFY( reader.open(QIODevice::ReadOnly) ); + + QCOMPARE( writer.write(data1), qint64(data1.size()) ); + QVERIFY( writer.seek(0) ); + + QByteArray result; + result = reader.read(18); + QCOMPARE( result.size(), 18 ); + + QCOMPARE( writer.write(data2), qint64(data2.size()) ); // new data, old version buffered in reader + QCOMPARE( writer.write(data2), qint64(data2.size()) ); // new data, unbuffered in reader + + result += reader.readAll(); + + QCOMPARE( result, data1 + data2 ); + + QFile::remove(fileName); +} + +void tst_QFile::readAllStdin() +{ +#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) + QSKIP("Currently no stdin/out supported for Windows CE or Symbian", SkipAll); +#endif +#if defined(QT_NO_PROCESS) + QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll); +#else + QByteArray lotsOfData(1024, '@'); // 10 megs + + QProcess process; + process.start("stdinprocess/stdinprocess all"); + QVERIFY( process.waitForStarted() ); + for (int i = 0; i < 5; ++i) { + QTest::qWait(1000); + process.write(lotsOfData); + while (process.bytesToWrite() > 0) { + QVERIFY(process.waitForBytesWritten()); + } + } + + process.closeWriteChannel(); + process.waitForFinished(); + QCOMPARE(process.readAll().size(), lotsOfData.size() * 5); +#endif +} + +void tst_QFile::readLineStdin() +{ +#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) + QSKIP("Currently no stdin/out supported for Windows CE or Symbian", SkipAll); +#endif +#if defined(QT_NO_PROCESS) + QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll); +#else + + QByteArray lotsOfData(1024, '@'); // 10 megs + for (int i = 0; i < lotsOfData.size(); ++i) { + if ((i % 32) == 31) + lotsOfData[i] = '\n'; + else + lotsOfData[i] = char('0' + i % 32); + } + + for (int i = 0; i < 2; ++i) { + QProcess process; + process.start(QString("stdinprocess/stdinprocess line %1").arg(i), QIODevice::Text | QIODevice::ReadWrite); + for (int i = 0; i < 5; ++i) { + QTest::qWait(1000); + process.write(lotsOfData); + while (process.bytesToWrite() > 0) { + QVERIFY(process.waitForBytesWritten()); + } + } + + process.closeWriteChannel(); + QVERIFY(process.waitForFinished(5000)); + + QByteArray array = process.readAll(); + QCOMPARE(array.size(), lotsOfData.size() * 5); + for (int i = 0; i < array.size(); ++i) { + if ((i % 32) == 31) + QCOMPARE(char(array[i]), '\n'); + else + QCOMPARE(char(array[i]), char('0' + i % 32)); + } + } +#endif +} + +void tst_QFile::readLineStdin_lineByLine() +{ +#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) + QSKIP("Currently no stdin/out supported for Windows CE", SkipAll); +#endif +#if defined(QT_NO_PROCESS) + QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll); +#else + for (int i = 0; i < 2; ++i) { + QProcess process; + process.start(QString("stdinprocess/stdinprocess line %1").arg(i), QIODevice::Text | QIODevice::ReadWrite); + QVERIFY(process.waitForStarted()); + + for (int j = 0; j < 3; ++j) { + QByteArray line = "line " + QByteArray::number(j) + "\n"; + QCOMPARE(process.write(line), qint64(line.size())); + QVERIFY(process.waitForBytesWritten(2000)); + if (process.bytesAvailable() == 0) + QVERIFY(process.waitForReadyRead(2000)); + QCOMPARE(process.readAll(), line); + } + + process.closeWriteChannel(); + QVERIFY(process.waitForFinished(5000)); + } +#endif +} + +void tst_QFile::text() +{ + // dosfile.txt is a binary CRLF file + QFile file(SRCDIR "dosfile.txt"); + QVERIFY(file.open(QFile::Text | QFile::ReadOnly)); + QCOMPARE(file.readLine(), + QByteArray("/dev/system/root / reiserfs acl,user_xattr 1 1\n")); + QCOMPARE(file.readLine(), + QByteArray("/dev/sda1 /boot ext3 acl,user_xattr 1 2\n")); + file.ungetChar('\n'); + file.ungetChar('2'); + QCOMPARE(file.readLine().constData(), QByteArray("2\n").constData()); +} + +void tst_QFile::missingEndOfLine() +{ + QFile file(SRCDIR "noendofline.txt"); + QVERIFY(file.open(QFile::ReadOnly)); + + int nlines = 0; + while (!file.atEnd()) { + ++nlines; + file.readLine(); + } + + QCOMPARE(nlines, 3); +} + +void tst_QFile::readBlock() +{ + QFile f( SRCDIR "testfile.txt" ); + f.open( QIODevice::ReadOnly ); + + int length = 0; + char p[256]; + length = f.read( p, 256 ); + f.close(); + QCOMPARE( length, 245 ); + QCOMPARE( p[59], 'D' ); + QCOMPARE( p[178], 'T' ); + QCOMPARE( p[199], 'l' ); +} + +void tst_QFile::getch() +{ + QFile f( SRCDIR "testfile.txt" ); + f.open( QIODevice::ReadOnly ); + + char c; + int i = 0; + while (f.getChar(&c)) { + QCOMPARE(f.pos(), qint64(i + 1)); + if ( i == 59 ) + QCOMPARE( c, 'D' ); + ++i; + } + f.close(); + QCOMPARE( i, 245 ); +} + +void tst_QFile::ungetChar() +{ + QFile f(SRCDIR "testfile.txt"); + QVERIFY(f.open(QIODevice::ReadOnly)); + + QByteArray array = f.readLine(); + QCOMPARE(array.constData(), "----------------------------------------------------------\n"); + f.ungetChar('\n'); + + array = f.readLine(); + QCOMPARE(array.constData(), "\n"); + + f.ungetChar('\n'); + f.ungetChar('-'); + f.ungetChar('-'); + + array = f.readLine(); + QCOMPARE(array.constData(), "--\n"); + + QFile::remove("genfile.txt"); + QFile out("genfile.txt"); + QVERIFY(out.open(QIODevice::ReadWrite)); + out.write("123"); + out.seek(0); + QCOMPARE(out.readAll().constData(), "123"); + out.ungetChar('3'); + out.write("4"); + out.seek(0); + QCOMPARE(out.readAll().constData(), "124"); + out.ungetChar('4'); + out.ungetChar('2'); + out.ungetChar('1'); + char buf[3]; + QCOMPARE(out.read(buf, sizeof(buf)), qint64(3)); + QCOMPARE(buf[0], '1'); + QCOMPARE(buf[1], '2'); + QCOMPARE(buf[2], '4'); +} + +void tst_QFile::invalidFile_data() +{ + QTest::addColumn("fileName"); +#if !defined(Q_WS_WIN) && !defined(Q_OS_SYMBIAN) + QTest::newRow( "x11" ) << QString( "qwe//" ); +#else + QTest::newRow( "colon1" ) << QString( "fail:invalid" ); + QTest::newRow( "colon2" ) << QString( "f:ail:invalid" ); + QTest::newRow( "colon3" ) << QString( ":failinvalid" ); + QTest::newRow( "forwardslash" ) << QString( "fail/invalid" ); + QTest::newRow( "asterisk" ) << QString( "fail*invalid" ); + QTest::newRow( "questionmark" ) << QString( "fail?invalid" ); + QTest::newRow( "quote" ) << QString( "fail\"invalid" ); + QTest::newRow( "lt" ) << QString( "failinvalid" ); + QTest::newRow( "pipe" ) << QString( "fail|invalid" ); +#endif +} + +void tst_QFile::invalidFile() +{ + QFETCH( QString, fileName ); + QFile f( fileName ); + QVERIFY( !f.open( QIODevice::ReadWrite ) ); +} + +void tst_QFile::createFile() +{ + if ( QFile::exists( "createme.txt" ) ) + QFile::remove( "createme.txt" ); + QVERIFY( !QFile::exists( "createme.txt" ) ); + + QFile f( "createme.txt" ); + QVERIFY( f.open( QIODevice::WriteOnly ) ); + f.close(); + QVERIFY( QFile::exists( "createme.txt" ) ); +} + +void tst_QFile::append() +{ + const QString name("appendme.txt"); + if (QFile::exists(name)) + QFile::remove(name); + QVERIFY(!QFile::exists(name)); + + QFile f(name); + QVERIFY(f.open(QIODevice::WriteOnly | QIODevice::Truncate)); + f.putChar('a'); + f.close(); + + QVERIFY(f.open(QIODevice::Append)); + QVERIFY(f.pos() == 1); + f.putChar('a'); + f.close(); + QCOMPARE(int(f.size()), 2); +} + +void tst_QFile::permissions_data() +{ + QTest::addColumn("file"); + QTest::addColumn("perms"); + QTest::addColumn("expected"); + + QTest::newRow("data0") << QCoreApplication::instance()->applicationFilePath() << uint(QFile::ExeUser) << true; + QTest::newRow("data1") << SRCDIR "tst_qfile.cpp" << uint(QFile::ReadUser) << true; +// QTest::newRow("data2") << "tst_qfile.cpp" << int(QFile::WriteUser) << false; + QTest::newRow("resource1") << ":/tst_qfileinfo/resources/file1.ext1" << uint(QFile::ReadUser) << true; + QTest::newRow("resource2") << ":/tst_qfileinfo/resources/file1.ext1" << uint(QFile::WriteUser) << false; + QTest::newRow("resource3") << ":/tst_qfileinfo/resources/file1.ext1" << uint(QFile::ExeUser) << false; +} + +void tst_QFile::permissions() +{ +#if defined(Q_OS_SYMBIAN) + if (qstrcmp(QTest::currentDataTag(), "data0") == 0) + QSKIP("Symbian does not have execution permissions", SkipSingle); +#endif + QFETCH(QString, file); + QFETCH(uint, perms); + QFETCH(bool, expected); + QFile f(file); + QCOMPARE(((f.permissions() & perms) == QFile::Permissions(perms)), expected); + QCOMPARE(((QFile::permissions(file) & perms) == QFile::Permissions(perms)), expected); +} + +void tst_QFile::setPermissions() +{ + DEPENDS_ON( "permissions" ); //if that doesn't work... + + if ( QFile::exists( "createme.txt" ) ) + QFile::remove( "createme.txt" ); + QVERIFY( !QFile::exists( "createme.txt" ) ); + + QFile f("createme.txt"); + QVERIFY(f.open(QIODevice::WriteOnly | QIODevice::Truncate)); + f.putChar('a'); + f.close(); + + QFile::Permissions perms(QFile::WriteUser | QFile::ReadUser); + QVERIFY(f.setPermissions(perms)); + QVERIFY((f.permissions() & perms) == perms); + +} + +void tst_QFile::copy() +{ + QFile::setPermissions("tst_qfile_copy.cpp", QFile::WriteUser); + QFile::remove("tst_qfile_copy.cpp"); + QFile::remove("test2"); + QVERIFY(QFile::copy(SRCDIR "tst_qfile.cpp", "tst_qfile_copy.cpp")); + QFile in1(SRCDIR "tst_qfile.cpp"), in2("tst_qfile_copy.cpp"); + QVERIFY(in1.open(QFile::ReadOnly)); + QVERIFY(in2.open(QFile::ReadOnly)); + QByteArray data1 = in1.readAll(), data2 = in2.readAll(); + QCOMPARE(data1, data2); + QFile::remove( "main_copy.cpp" ); + + QFile::copy(QDir::currentPath(), QDir::currentPath() + QLatin1String("/test2")); +} + +void tst_QFile::copyAfterFail() +{ + QFile file1("file-to-be-copied.txt"); + QFile file2("existing-file.txt"); + + QVERIFY(file1.open(QIODevice::ReadWrite) && "(test-precondition)"); + QVERIFY(file2.open(QIODevice::ReadWrite) && "(test-precondition)"); + file2.close(); + QVERIFY(!QFile::exists("copied-file-1.txt") && "(test-precondition)"); + QVERIFY(!QFile::exists("copied-file-2.txt") && "(test-precondition)"); + + QVERIFY(!file1.copy("existing-file.txt")); + QCOMPARE(file1.error(), QFile::CopyError); + + QVERIFY(file1.copy("copied-file-1.txt")); + QVERIFY(!file1.isOpen()); + QCOMPARE(file1.error(), QFile::NoError); + + QVERIFY(!file1.copy("existing-file.txt")); + QCOMPARE(file1.error(), QFile::CopyError); + + QVERIFY(file1.copy("copied-file-2.txt")); + QVERIFY(!file1.isOpen()); + QCOMPARE(file1.error(), QFile::NoError); + + QVERIFY(QFile::exists("copied-file-1.txt")); + QVERIFY(QFile::exists("copied-file-2.txt")); + + QVERIFY(QFile::remove("file-to-be-copied.txt") && "(test-cleanup)"); + QVERIFY(QFile::remove("existing-file.txt") && "(test-cleanup)"); + QVERIFY(QFile::remove("copied-file-1.txt") && "(test-cleanup)"); + QVERIFY(QFile::remove("copied-file-2.txt") && "(test-cleanup)"); +} + +void tst_QFile::copyRemovesTemporaryFile() const +{ + const QString newName(QLatin1String("copyRemovesTemporaryFile")); + QVERIFY(QFile::copy(SRCDIR "forCopying.txt", newName)); + + QVERIFY(!QFile::exists(QLatin1String( SRCDIR "qt_temp.XXXXXX"))); + QVERIFY(QFile::remove(newName)); +} + +void tst_QFile::copyShouldntOverwrite() +{ + // Copy should not overwrite existing files. + QFile::remove("tst_qfile.cpy"); + QFile file(SRCDIR "tst_qfile.cpp"); + QVERIFY(file.copy("tst_qfile.cpy")); +#if defined(Q_OS_SYMBIAN) + bool ok = QFile::setPermissions("tst_qfile.cpy", QFile::WriteUser); +#else + bool ok = QFile::setPermissions("tst_qfile.cpy", QFile::WriteOther); +#endif + QVERIFY(ok); + QVERIFY(!file.copy("tst_qfile.cpy")); + QFile::remove("tst_qfile.cpy"); +} + +void tst_QFile::copyFallback() +{ + // Using a resource file to trigger QFile::copy's fallback handling + QFile file(":/copy-fallback.qrc"); + QFile::remove("file-copy-destination.txt"); + + QVERIFY2(file.exists(), "test precondition"); + QVERIFY2(!QFile::exists("file-copy-destination.txt"), "test precondition"); + + // Fallback copy of closed file. + QVERIFY(file.copy("file-copy-destination.txt")); + QVERIFY(QFile::exists("file-copy-destination.txt")); + QVERIFY(!file.isOpen()); + +#ifdef Q_WS_WINCE + // Need to reset permissions on Windows to be able to delete + QVERIFY(QFile::setPermissions("file-copy-destination.txt", + QFile::WriteOther)); +#else + // Need to reset permissions on Windows to be able to delete + QVERIFY(QFile::setPermissions("file-copy-destination.txt", + QFile::ReadOwner | QFile::WriteOwner)); +#endif + QVERIFY(QFile::remove("file-copy-destination.txt")); + + // Fallback copy of open file. + QVERIFY(file.open(QIODevice::ReadOnly)); + QVERIFY(file.copy("file-copy-destination.txt")); + QVERIFY(QFile::exists("file-copy-destination.txt")); + QVERIFY(!file.isOpen()); + + file.close(); + QFile::remove("file-copy-destination.txt"); +} + +#ifdef Q_OS_WIN +#include +#include +#endif + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) +static QString getWorkingDirectoryForLink(const QString &linkFileName) +{ + bool neededCoInit = false; + QString ret; + + IShellLink *psl; + HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl); + if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized + neededCoInit = true; + CoInitialize(NULL); + hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl); + } + + if (SUCCEEDED(hres)) { // Get pointer to the IPersistFile interface. + IPersistFile *ppf; + hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf); + if (SUCCEEDED(hres)) { + hres = ppf->Load((LPOLESTR)linkFileName.utf16(), STGM_READ); + //The original path of the link is retrieved. If the file/folder + //was moved, the return value still have the old path. + if(SUCCEEDED(hres)) { + wchar_t szGotPath[MAX_PATH]; + if (psl->GetWorkingDirectory(szGotPath, MAX_PATH) == NOERROR) + ret = QString::fromWCharArray(szGotPath); + } + ppf->Release(); + } + psl->Release(); + } + + if (neededCoInit) { + CoUninitialize(); + } + + return ret; +} +#endif + +void tst_QFile::link() +{ +#if defined(Q_OS_SYMBIAN) + QSKIP("Symbian does not support links", SkipAll); +#endif + QFile::remove("myLink.lnk"); + + QFileInfo info1(SRCDIR "tst_qfile.cpp"); + QString referenceTarget = QDir::cleanPath(info1.absoluteFilePath()); + + QVERIFY(QFile::link(SRCDIR "tst_qfile.cpp", "myLink.lnk")); + + QFileInfo info2("myLink.lnk"); + QVERIFY(info2.isSymLink()); + QCOMPARE(info2.symLinkTarget(), referenceTarget); + + QFile link("myLink.lnk"); + QVERIFY(link.open(QIODevice::ReadOnly)); + QCOMPARE(link.symLinkTarget(), referenceTarget); + link.close(); + + QCOMPARE(QFile::symLinkTarget("myLink.lnk"), referenceTarget); + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + QString wd = getWorkingDirectoryForLink(info2.absoluteFilePath()); + QCOMPARE(QDir::fromNativeSeparators(wd), QDir::cleanPath(info1.absolutePath())); +#endif + + QVERIFY(QFile::remove(info2.absoluteFilePath())); +} + +void tst_QFile::linkToDir() +{ +#if defined(Q_OS_SYMBIAN) + QSKIP("Symbian does not support linking to directories", SkipAll); +#endif + QFile::remove("myLinkToDir.lnk"); + QDir dir; + dir.mkdir("myDir"); + QFileInfo info1("myDir"); + QVERIFY(QFile::link("myDir", "myLinkToDir.lnk")); + QFileInfo info2("myLinkToDir.lnk"); +#if !(defined Q_OS_HPUX && defined(__ia64)) + // absurd HP-UX filesystem bug on gravlaks - checking if a symlink + // resolves or not alters the file system to make the broken symlink + // later fail... + QVERIFY(info2.isSymLink()); +#endif + QCOMPARE(info2.symLinkTarget(), info1.absoluteFilePath()); + QVERIFY(QFile::remove(info2.absoluteFilePath())); + QFile::remove("myLinkToDir.lnk"); + dir.rmdir("myDir"); +} + +void tst_QFile::absolutePathLinkToRelativePath() +{ +#if defined(Q_OS_SYMBIAN) + QSKIP("Symbian does not support links", SkipAll); +#endif + QFile::remove("myDir/test.txt"); + QFile::remove("myDir/myLink.lnk"); + QDir dir; + dir.mkdir("myDir"); + QFile("myDir/test.txt").open(QFile::WriteOnly); + +#ifdef Q_OS_WIN + QVERIFY(QFile::link("test.txt", "myDir/myLink.lnk")); +#else + QVERIFY(QFile::link("myDir/test.txt", "myDir/myLink.lnk")); +#endif + QEXPECT_FAIL("", "Symlinking using relative paths is currently different on Windows and Unix/Symbian", Continue); + QCOMPARE(QFileInfo(QFile(QFileInfo("myDir/myLink.lnk").absoluteFilePath()).symLinkTarget()).absoluteFilePath(), + QFileInfo("myDir/test.txt").absoluteFilePath()); + + QFile::remove("myDir/test.txt"); + QFile::remove("myDir/myLink.lnk"); + dir.rmdir("myDir"); +} + +void tst_QFile::readBrokenLink() +{ +#if defined(Q_OS_SYMBIAN) + QSKIP("Symbian does not support links", SkipAll); +#endif + QFile::remove("myLink2.lnk"); + QFileInfo info1("file12"); +#if defined(Q_OS_SYMBIAN) + // In Symbian can't link to nonexisting file directly, so create the file temporarily + QFile tempFile("file12"); + tempFile.open(QIODevice::WriteOnly); + tempFile.link("myLink2.lnk"); + tempFile.remove(); +#else + QVERIFY(QFile::link("file12", "myLink2.lnk")); +#endif + QFileInfo info2("myLink2.lnk"); + QVERIFY(info2.isSymLink()); + QCOMPARE(info2.symLinkTarget(), info1.absoluteFilePath()); + QVERIFY(QFile::remove(info2.absoluteFilePath())); + +#if !defined(Q_OS_SYMBIAN) + QVERIFY(QFile::link("ole/..", "myLink2.lnk")); + QCOMPARE(QFileInfo("myLink2.lnk").symLinkTarget(), QDir::currentPath()); +#endif +} + +void tst_QFile::readTextFile_data() +{ + QTest::addColumn("in"); + QTest::addColumn("out"); + + QTest::newRow("empty") << QByteArray() << QByteArray(); + QTest::newRow("a") << QByteArray("a") << QByteArray("a"); + QTest::newRow("a\\rb") << QByteArray("a\rb") << QByteArray("ab"); + QTest::newRow("\\n") << QByteArray("\n") << QByteArray("\n"); + QTest::newRow("\\r\\n") << QByteArray("\r\n") << QByteArray("\n"); + QTest::newRow("\\r") << QByteArray("\r") << QByteArray(); + QTest::newRow("twolines") << QByteArray("Hello\r\nWorld\r\n") << QByteArray("Hello\nWorld\n"); + QTest::newRow("twolines no endline") << QByteArray("Hello\r\nWorld") << QByteArray("Hello\nWorld"); +} + +void tst_QFile::readTextFile() +{ + QFETCH(QByteArray, in); + QFETCH(QByteArray, out); + + QFile winfile("winfile.txt"); + QVERIFY(winfile.open(QFile::WriteOnly | QFile::Truncate)); + winfile.write(in); + winfile.close(); + + QVERIFY(winfile.open(QFile::ReadOnly)); + QCOMPARE(winfile.readAll(), in); + winfile.close(); + + QVERIFY(winfile.open(QFile::ReadOnly | QFile::Text)); + QCOMPARE(winfile.readAll(), out); +} + +void tst_QFile::readTextFile2() +{ + { + QFile file(SRCDIR "testlog.txt"); + QVERIFY(file.open(QIODevice::ReadOnly)); + file.read(4097); + } + + { + QFile file(SRCDIR "testlog.txt"); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); + file.read(4097); + } +} + +void tst_QFile::writeTextFile_data() +{ + QTest::addColumn("in"); + + QTest::newRow("empty") << QByteArray(); + QTest::newRow("a") << QByteArray("a"); + QTest::newRow("a\\rb") << QByteArray("a\rb"); + QTest::newRow("\\n") << QByteArray("\n"); + QTest::newRow("\\r\\n") << QByteArray("\r\n"); + QTest::newRow("\\r") << QByteArray("\r"); + QTest::newRow("twolines crlf") << QByteArray("Hello\r\nWorld\r\n"); + QTest::newRow("twolines crlf no endline") << QByteArray("Hello\r\nWorld"); + QTest::newRow("twolines lf") << QByteArray("Hello\nWorld\n"); + QTest::newRow("twolines lf no endline") << QByteArray("Hello\nWorld"); + QTest::newRow("mixed") << QByteArray("this\nis\r\na\nmixed\r\nfile\n"); +} + +void tst_QFile::writeTextFile() +{ + QFETCH(QByteArray, in); + + QFile file("textfile.txt"); + QVERIFY(file.open(QFile::WriteOnly | QFile::Truncate | QFile::Text)); + QByteArray out = in; +#ifdef Q_OS_WIN + out.replace('\n', "\r\n"); +#endif + QCOMPARE(file.write(in), qlonglong(in.size())); + file.close(); + + file.open(QFile::ReadOnly); + QCOMPARE(file.readAll(), out); +} + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) +void tst_QFile::largeUncFileSupport() +{ + qint64 size = Q_INT64_C(8589934592); + qint64 dataOffset = Q_INT64_C(8589914592); + QByteArray knownData("LargeFile content at offset 8589914592"); + QString largeFile("//" + QtNetworkSettings::winServerName() + "/testsharelargefile/file.bin"); + + { + // 1) Native file handling. + QFile file(largeFile); + QCOMPARE(file.size(), size); + QVERIFY(file.open(QIODevice::ReadOnly)); + QCOMPARE(file.size(), size); + QVERIFY(file.seek(dataOffset)); + QCOMPARE(file.read(knownData.size()), knownData); + } + { + // 2) stdlib file handling. +#if _MSC_VER <= 1310 + QSKIP("platform SDK for MSVC 2003 does not support large files", SkipAll); +#endif + QFile file; + FILE *fh = fopen(QFile::encodeName(largeFile).data(), "rb"); + QVERIFY(file.open(fh, QIODevice::ReadOnly)); + QCOMPARE(file.size(), size); + QVERIFY(file.seek(dataOffset)); + QCOMPARE(file.read(knownData.size()), knownData); + fclose(fh); + } + { + // 3) stdio file handling. + QFile file; + FILE *fh = fopen(QFile::encodeName(largeFile).data(), "rb"); + int fd = int(_fileno(fh)); + QVERIFY(file.open(fd, QIODevice::ReadOnly)); + QCOMPARE(file.size(), size); + QVERIFY(file.seek(dataOffset)); + QCOMPARE(file.read(knownData.size()), knownData); + fclose(fh); + } +} +#endif + +void tst_QFile::tailFile() +{ + QSKIP("File change notifications are so far unsupported.", SkipAll); + + QFile file("tail.txt"); + QVERIFY(file.open(QFile::WriteOnly | QFile::Append)); + + QFile tailFile("tail.txt"); + QVERIFY(tailFile.open(QFile::ReadOnly)); + tailFile.seek(file.size()); + + QSignalSpy readSignal(&tailFile, SIGNAL(readyRead())); + + file.write("", 1); + + QTestEventLoop::instance().enterLoop(5); + + QVERIFY(!QTestEventLoop::instance().timeout()); + QCOMPARE(readSignal.count(), 1); +} + +void tst_QFile::flush() +{ + QString fileName("stdfile.txt"); + + QFile::remove(fileName); + + { + QFile file(fileName); + QVERIFY(file.open(QFile::WriteOnly)); + QCOMPARE(file.write("abc", 3),qint64(3)); + } + + { + QFile file(fileName); + QVERIFY(file.open(QFile::WriteOnly | QFile::Append)); + QCOMPARE(file.pos(), qlonglong(3)); + QCOMPARE(file.write("def", 3), qlonglong(3)); + QCOMPARE(file.pos(), qlonglong(6)); + } + + { + QFile file("stdfile.txt"); + QVERIFY(file.open(QFile::ReadOnly)); + QCOMPARE(file.readAll(), QByteArray("abcdef")); + } + + QFile::remove(fileName); +} + +void tst_QFile::bufferedRead() +{ + QFile::remove("stdfile.txt"); + + QFile file("stdfile.txt"); + QVERIFY(file.open(QFile::WriteOnly)); + file.write("abcdef"); + file.close(); + +#if defined(Q_OS_WINCE) + FILE *stdFile = fopen((QCoreApplication::applicationDirPath() + "/stdfile.txt").toAscii() , "r"); +#else + FILE *stdFile = fopen("stdfile.txt", "r"); +#endif + QVERIFY(stdFile); + char c; + QCOMPARE(int(fread(&c, 1, 1, stdFile)), 1); + QCOMPARE(c, 'a'); + QCOMPARE(int(ftell(stdFile)), 1); + + { + QFile file; + QVERIFY(file.open(stdFile, QFile::ReadOnly)); + QCOMPARE(file.pos(), qlonglong(1)); + QCOMPARE(file.read(&c, 1), qlonglong(1)); + QCOMPARE(c, 'b'); + QCOMPARE(file.pos(), qlonglong(2)); + } + + fclose(stdFile); +} + +void tst_QFile::isSequential() +{ +#if defined (Q_OS_WIN) || defined(Q_OS_SYMBIAN) + QSKIP("Unix only test.", SkipAll); +#endif + + QFile zero("/dev/null"); + QVERIFY(zero.open(QFile::ReadOnly)); + QVERIFY(zero.isSequential()); +} + +void tst_QFile::encodeName() +{ + QCOMPARE(QFile::encodeName(QString::null), QByteArray()); +} + +void tst_QFile::truncate() +{ + for (int i = 0; i < 2; ++i) { + QFile file("truncate.txt"); + QVERIFY(file.open(QFile::WriteOnly)); + file.write(QByteArray(200, '@')); + file.close(); + + QVERIFY(file.open((i ? QFile::WriteOnly : QFile::ReadWrite) | QFile::Truncate)); + file.write(QByteArray(100, '$')); + file.close(); + + QVERIFY(file.open(QFile::ReadOnly)); + QCOMPARE(file.readAll(), QByteArray(100, '$')); + } +} + +void tst_QFile::seekToPos() +{ + { + QFile file("seekToPos.txt"); + QVERIFY(file.open(QFile::WriteOnly)); + file.write("a\r\nb\r\nc\r\n"); + file.flush(); + } + + QFile file("seekToPos.txt"); + QVERIFY(file.open(QFile::ReadOnly | QFile::Text)); + file.seek(1); + char c; + QVERIFY(file.getChar(&c)); + QCOMPARE(c, '\n'); + + QCOMPARE(file.pos(), qint64(3)); + file.seek(file.pos()); + QCOMPARE(file.pos(), qint64(3)); + + file.seek(1); + file.seek(file.pos()); + QCOMPARE(file.pos(), qint64(1)); + +} + +void tst_QFile::seekAfterEndOfFile() +{ + QLatin1String filename("seekAfterEof.dat"); + QFile::remove(filename); + { + QFile file(filename); + QVERIFY(file.open(QFile::WriteOnly)); + file.write("abcd"); + QCOMPARE(file.size(), qint64(4)); + file.seek(8); + file.write("ijkl"); + QCOMPARE(file.size(), qint64(12)); + file.seek(4); + file.write("efgh"); + QCOMPARE(file.size(), qint64(12)); + file.seek(16); + file.write("----"); + QCOMPARE(file.size(), qint64(20)); + file.flush(); + } + + QFile file(filename); + QVERIFY(file.open(QFile::ReadOnly)); + QByteArray contents = file.readAll(); + QCOMPARE(contents.left(12), QByteArray("abcdefghijkl", 12)); + //bytes 12-15 are uninitialised so we don't care what they read as. + QCOMPARE(contents.mid(16), QByteArray("----", 4)); + file.close(); + QFile::remove(filename); +} + +void tst_QFile::FILEReadWrite() +{ + // Tests modifying a file. First creates it then reads in 4 bytes and then overwrites these + // 4 bytes with new values. At the end check to see the file contains the new values. + + QFile::remove("FILEReadWrite.txt"); + + // create test file + { + QFile f("FILEReadWrite.txt"); + QVERIFY(f.open(QFile::WriteOnly)); + QDataStream ds(&f); + qint8 c = 0; + ds << c; + c = 1; + ds << c; + c = 2; + ds << c; + c = 3; + ds << c; + c = 4; + ds << c; + c = 5; + ds << c; + c = 6; + ds << c; + c = 7; + ds << c; + c = 8; + ds << c; + c = 9; + ds << c; + c = 10; + ds << c; + c = 11; + ds << c; + f.close(); + } + +#ifdef Q_OS_WINCE + FILE *fp = fopen(qPrintable(QCoreApplication::applicationDirPath() + "\\FILEReadWrite.txt"), "r+b"); +#else + FILE *fp = fopen("FILEReadWrite.txt", "r+b"); +#endif + QVERIFY(fp); + QFile file; + QVERIFY(file.open(fp, QFile::ReadWrite)); + QDataStream sfile(&file) ; + + qint8 var1,var2,var3,var4; + while (!sfile.atEnd()) + { + qint64 base = file.pos(); + + QCOMPARE(file.pos(), base + 0); + sfile >> var1; + QCOMPARE(file.pos(), base + 1); + file.flush(); // flushing should not change the base + QCOMPARE(file.pos(), base + 1); + sfile >> var2; + QCOMPARE(file.pos(), base + 2); + sfile >> var3; + QCOMPARE(file.pos(), base + 3); + sfile >> var4; + QCOMPARE(file.pos(), base + 4); + file.seek(file.pos() - 4) ; // Move it back 4, for we are going to write new values based on old ones + QCOMPARE(file.pos(), base + 0); + sfile << qint8(var1 + 5); + QCOMPARE(file.pos(), base + 1); + sfile << qint8(var2 + 5); + QCOMPARE(file.pos(), base + 2); + sfile << qint8(var3 + 5); + QCOMPARE(file.pos(), base + 3); + sfile << qint8(var4 + 5); + QCOMPARE(file.pos(), base + 4); + + } + file.close(); + fclose(fp); + + // check modified file + { + QFile f("FILEReadWrite.txt"); + QVERIFY(f.open(QFile::ReadOnly)); + QDataStream ds(&f); + qint8 c = 0; + ds >> c; + QCOMPARE(c, (qint8)5); + ds >> c; + QCOMPARE(c, (qint8)6); + ds >> c; + QCOMPARE(c, (qint8)7); + ds >> c; + QCOMPARE(c, (qint8)8); + ds >> c; + QCOMPARE(c, (qint8)9); + ds >> c; + QCOMPARE(c, (qint8)10); + ds >> c; + QCOMPARE(c, (qint8)11); + ds >> c; + QCOMPARE(c, (qint8)12); + ds >> c; + QCOMPARE(c, (qint8)13); + ds >> c; + QCOMPARE(c, (qint8)14); + ds >> c; + QCOMPARE(c, (qint8)15); + ds >> c; + QCOMPARE(c, (qint8)16); + f.close(); + } + + QFile::remove("FILEReadWrite.txt"); +} + + +/* +#include +#define BUFFSIZE 1 +#define FILESIZE 0x10000000f +void tst_QFile::largeFileSupport() +{ +#ifdef Q_OS_SOLARIS + QSKIP("Solaris does not support statfs", SkipAll); +#else + qlonglong sizeNeeded = 2147483647; + sizeNeeded *= 2; + sizeNeeded += 1024; + qlonglong freespace = qlonglong(0); +#ifdef Q_WS_WIN + _ULARGE_INTEGER free; + if (::GetDiskFreeSpaceEx((wchar_t*)QDir::currentPath().utf16(), &free, 0, 0)) + freespace = free.QuadPart; + if (freespace != 0) { +#elif defined(Q_OS_IRIX) + struct statfs info; + if (statfs(QDir::currentPath().local8Bit(), &info, sizeof(struct statfs), 0) == 0) { + freespace = qlonglong(info.f_bfree * info.f_bsize); +#else + struct statfs info; + if (statfs(const_cast(QDir::currentPath().toLocal8Bit().constData()), &info) == 0) { + freespace = qlonglong(info.f_bavail * info.f_bsize); +#endif + if (freespace > sizeNeeded) { + QFile bigFile("bigfile"); + if (bigFile.open(QFile::ReadWrite)) { + char c[BUFFSIZE] = {'a'}; + QVERIFY(bigFile.write(c, BUFFSIZE) == BUFFSIZE); + qlonglong oldPos = bigFile.pos(); + QVERIFY(bigFile.resize(sizeNeeded)); + QCOMPARE(oldPos, bigFile.pos()); + QVERIFY(bigFile.seek(sizeNeeded - BUFFSIZE)); + QVERIFY(bigFile.write(c, BUFFSIZE) == BUFFSIZE); + + bigFile.close(); + if (bigFile.open(QFile::ReadOnly)) { + QVERIFY(bigFile.read(c, BUFFSIZE) == BUFFSIZE); + int i = 0; + for (i=0; i("fileName"); + + QTest::newRow( "01" ) << QString::fromUtf8("xxxxxxx.txt"); +} + +void tst_QFile::i18nFileName() +{ + QFETCH(QString, fileName); + if (QFile::exists(fileName)) { + QVERIFY(QFile::remove(fileName)); + } + { + QFile file(fileName); + QVERIFY(file.open(QFile::WriteOnly | QFile::Text)); + QTextStream ts(&file); + ts.setCodec("UTF-8"); + ts << fileName << endl; + } + { + QFile file(fileName); + QVERIFY(file.open(QFile::ReadOnly | QFile::Text)); + QTextStream ts(&file); + ts.setCodec("UTF-8"); + QString line = ts.readLine(); + QCOMPARE(line, fileName); + } + QVERIFY(QFile::remove(fileName)); +} + + +void tst_QFile::longFileName_data() +{ + QTest::addColumn("fileName"); + + QTest::newRow( "16 chars" ) << QString::fromLatin1("longFileName.txt"); + QTest::newRow( "52 chars" ) << QString::fromLatin1("longFileNamelongFileNamelongFileNamelongFileName.txt"); + QTest::newRow( "148 chars" ) << QString::fromLatin1("longFileNamelongFileNamelongFileNamelongFileName" + "longFileNamelongFileNamelongFileNamelongFileName" + "longFileNamelongFileNamelongFileNamelongFileName.txt"); + QTest::newRow( "244 chars" ) << QString::fromLatin1("longFileNamelongFileNamelongFileNamelongFileName" + "longFileNamelongFileNamelongFileNamelongFileName" + "longFileNamelongFileNamelongFileNamelongFileName" + "longFileNamelongFileNamelongFileNamelongFileName" + "longFileNamelongFileNamelongFileNamelongFileName.txt"); + QTest::newRow( "244 chars to absolutepath" ) << QFileInfo(QString::fromLatin1("longFileNamelongFileNamelongFileNamelongFileName" + "longFileNamelongFileNamelongFileNamelongFileName" + "longFileNamelongFileNamelongFileNamelongFileName" + "longFileNamelongFileNamelongFileNamelongFileName" + "longFileNamelongFileNamelongFileNamelongFileName.txt")).absoluteFilePath(); + /* needs to be put on a windows 2000 > test machine + QTest::newRow( "244 chars on UNC" ) << QString::fromLatin1("//arsia/D/troll/tmp/longFileNamelongFileNamelongFileNamelongFileName" + "longFileNamelongFileNamelongFileNamelongFileName" + "longFileNamelongFileNamelongFileNamelongFileName" + "longFileNamelongFileNamelongFileNamelongFileName" + "longFileNamelongFileNamelongFileNamelongFileName.txt");*/ +} + +void tst_QFile::longFileName() +{ + QFETCH(QString, fileName); + if (QFile::exists(fileName)) { + QVERIFY(QFile::remove(fileName)); + } + { + QFile file(fileName); +#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) + QEXPECT_FAIL("244 chars", "Full pathname must be less than 260 chars", Abort); + QEXPECT_FAIL("244 chars to absolutepath", "Full pathname must be less than 260 chars", Abort); +#endif + QVERIFY(file.open(QFile::WriteOnly | QFile::Text)); + QTextStream ts(&file); + ts << fileName << endl; + } + { + QFile file(fileName); + QVERIFY(file.open(QFile::ReadOnly | QFile::Text)); + QTextStream ts(&file); + QString line = ts.readLine(); + QCOMPARE(line, fileName); + } + QString newName = fileName + QLatin1String("1"); + { + QVERIFY(QFile::copy(fileName, newName)); + QFile file(newName); + QVERIFY(file.open(QFile::ReadOnly | QFile::Text)); + QTextStream ts(&file); + QString line = ts.readLine(); + QCOMPARE(line, fileName); + + } + QVERIFY(QFile::remove(newName)); + { + QVERIFY(QFile::rename(fileName, newName)); + QFile file(newName); + QVERIFY(file.open(QFile::ReadOnly | QFile::Text)); + QTextStream ts(&file); + QString line = ts.readLine(); + QCOMPARE(line, fileName); + } + QVERIFY(QFile::exists(newName)); + QVERIFY(QFile::remove(newName)); +} + +class MyEngine : public QAbstractFileEngine +{ +public: + MyEngine(int n) { number = n; } + virtual ~MyEngine() {} + + void setFileName(const QString &) {} + bool open(int ) { return false; } + bool close() { return false; } + bool flush() { return false; } + qint64 size() const { return 123 + number; } + qint64 at() const { return -1; } + bool seek(qint64) { return false; } + bool isSequential() const { return false; } + qint64 read(char *, qint64) { return -1; } + qint64 write(const char *, qint64) { return -1; } + bool remove() { return false; } + bool copy(const QString &) { return false; } + bool rename(const QString &) { return false; } + bool link(const QString &) { return false; } + bool mkdir(const QString &, bool) const { return false; } + bool rmdir(const QString &, bool) const { return false; } + bool setSize(qint64) { return false; } + QStringList entryList(QDir::Filters, const QStringList &) const { return QStringList(); } + bool caseSensitive() const { return false; } + bool isRelativePath() const { return false; } + FileFlags fileFlags(FileFlags) const { return 0; } + bool chmod(uint) { return false; } + QString fileName(FileName) const { return name; } + uint ownerId(FileOwner) const { return 0; } + QString owner(FileOwner) const { return QString(); } + QDateTime fileTime(FileTime) const { return QDateTime(); } + +private: + int number; + QString name; +}; + +class MyHandler : public QAbstractFileEngineHandler +{ +public: + inline QAbstractFileEngine *create(const QString &) const + { + return new MyEngine(1); + } +}; + +class MyHandler2 : public QAbstractFileEngineHandler +{ +public: + inline QAbstractFileEngine *create(const QString &) const + { + return new MyEngine(2); + } +}; + +void tst_QFile::fileEngineHandler() +{ + // A file that does not exist has a size of 0. + QFile::remove("ole.bull"); + QFile file("ole.bull"); + QCOMPARE(file.size(), qint64(0)); + + // Instantiating our handler will enable the new engine. + MyHandler handler; + file.setFileName("ole.bull"); + QCOMPARE(file.size(), qint64(124)); + + // A new, identical handler should take preference over the last one. + MyHandler2 handler2; + file.setFileName("ole.bull"); + QCOMPARE(file.size(), qint64(125)); + +} + +class MyRecursiveHandler : public QAbstractFileEngineHandler +{ +public: + inline QAbstractFileEngine *create(const QString &fileName) const + { + if (fileName.startsWith(":!")) { + QDir dir; + QString realFile = SRCDIR + fileName.mid(2); + if (dir.exists(realFile)) + return new QFSFileEngine(realFile); + } + return 0; + } +}; + +void tst_QFile::useQFileInAFileHandler() +{ + // This test should not dead-lock + MyRecursiveHandler handler; + QFile file(":!tst_qfile.cpp"); + QVERIFY(file.exists()); +} + +void tst_QFile::getCharFF() +{ + QFile file("file.txt"); + file.open(QFile::ReadWrite); + file.write("\xff\xff\xff"); + file.flush(); + file.seek(0); + + char c; + QVERIFY(file.getChar(&c)); + QVERIFY(file.getChar(&c)); + QVERIFY(file.getChar(&c)); +} + +void tst_QFile::remove_and_exists() +{ + QFile::remove("tull_i_grunn.txt"); + QFile f("tull_i_grunn.txt"); + + QVERIFY(!f.exists()); + + bool opened = f.open(QIODevice::WriteOnly); + QVERIFY(opened); + + f.write(QString("testing that remove/exists work...").toLatin1()); + f.close(); + + QVERIFY(f.exists()); + + f.remove(); + QVERIFY(!f.exists()); +} + +void tst_QFile::removeOpenFile() +{ + { + // remove an opened, write-only file + QFile::remove("remove_unclosed.txt"); + QFile f("remove_unclosed.txt"); + + QVERIFY(!f.exists()); + bool opened = f.open(QIODevice::WriteOnly); + QVERIFY(opened); + f.write(QString("testing that remove closes the file first...").toLatin1()); + + bool removed = f.remove(); // remove should both close and remove the file + QVERIFY(removed); + QVERIFY(!f.isOpen()); + QVERIFY(!f.exists()); + QVERIFY(f.error() == QFile::NoError); + } + + { + // remove an opened, read-only file + QFile::remove("remove_unclosed.txt"); + + // first, write a file that we can remove + { + QFile f("remove_unclosed.txt"); + QVERIFY(!f.exists()); + bool opened = f.open(QIODevice::WriteOnly); + QVERIFY(opened); + f.write(QString("testing that remove closes the file first...").toLatin1()); + f.close(); + } + + QFile f("remove_unclosed.txt"); + bool opened = f.open(QIODevice::ReadOnly); + QVERIFY(opened); + f.readAll(); + // this used to only fail on FreeBSD (and Mac OS X) + QVERIFY(f.flush()); + bool removed = f.remove(); // remove should both close and remove the file + QVERIFY(removed); + QVERIFY(!f.isOpen()); + QVERIFY(!f.exists()); + QVERIFY(f.error() == QFile::NoError); + } +} + +void tst_QFile::fullDisk() +{ + QFile file("/dev/full"); + if (!file.exists()) + QSKIP("/dev/full doesn't exist on this system", SkipAll); + + QVERIFY(file.open(QIODevice::WriteOnly)); + file.write("foobar", 6); + + QVERIFY(!file.flush()); + QCOMPARE(file.error(), QFile::ResourceError); + QVERIFY(!file.flush()); + QCOMPARE(file.error(), QFile::ResourceError); + + char c = 0; + file.write(&c, 0); + QVERIFY(!file.flush()); + QCOMPARE(file.error(), QFile::ResourceError); + QCOMPARE(file.write(&c, 1), qint64(1)); + QVERIFY(!file.flush()); + QCOMPARE(file.error(), QFile::ResourceError); + + file.close(); + QVERIFY(!file.isOpen()); + QCOMPARE(file.error(), QFile::ResourceError); + + file.open(QIODevice::WriteOnly); + QCOMPARE(file.error(), QFile::NoError); + QVERIFY(file.flush()); // Shouldn't inherit write buffer + file.close(); + QCOMPARE(file.error(), QFile::NoError); + + // try again without flush: + QVERIFY(file.open(QIODevice::WriteOnly)); + file.write("foobar", 6); + file.close(); + QVERIFY(file.error() != QFile::NoError); +} + +void tst_QFile::writeLargeDataBlock_data() +{ + QTest::addColumn("fileName"); + QTest::addColumn("type"); + + QTest::newRow("localfile-QFile") << "./largeblockfile.txt" << (int)OpenQFile; + QTest::newRow("localfile-Fd") << "./largeblockfile.txt" << (int)OpenFd; + QTest::newRow("localfile-Stream") << "./largeblockfile.txt" << (int)OpenStream; +#ifdef Q_OS_SYMBIAN + QTest::newRow("localfile-RFile") << "./largeblockfile.txt" << (int)OpenRFile; +#endif + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + // Some semi-randomness to avoid collisions. + QTest::newRow("unc file") + << QString("//" + QtNetworkSettings::winServerName() + "/TESTSHAREWRITABLE/largefile-%1-%2.txt") + .arg(QHostInfo::localHostName()) + .arg(QTime::currentTime().msec()) << (int)OpenQFile; +#endif +} + +static QByteArray getLargeDataBlock() +{ + static QByteArray array; + + if (array.isNull()) + { +#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) + int resizeSize = 1024 * 1024; // WinCE and Symbian do not have much space +#else + int resizeSize = 64 * 1024 * 1024; +#endif + array.resize(resizeSize); + for (int i = 0; i < array.size(); ++i) + array[i] = uchar(i); + } + + return array; +} + +void tst_QFile::writeLargeDataBlock() +{ + QFETCH(QString, fileName); + QFETCH( int, type ); + + QByteArray const originalData = getLargeDataBlock(); + + { + QFile file(fileName); + + QVERIFY2( openFile(file, QIODevice::WriteOnly, (FileType)type), + qPrintable(QString("Couldn't open file for writing: [%1]").arg(fileName)) ); + QCOMPARE( file.write(originalData), (qint64)originalData.size() ); + QVERIFY( file.flush() ); + + closeFile(file); + } + + QByteArray readData; + + { + QFile file(fileName); + + QVERIFY2( openFile(file, QIODevice::ReadOnly, (FileType)type), + qPrintable(QString("Couldn't open file for reading: [%1]").arg(fileName)) ); + readData = file.readAll(); + closeFile(file); + } + + QCOMPARE( readData, originalData ); + QVERIFY( QFile::remove(fileName) ); +} + +void tst_QFile::readFromWriteOnlyFile() +{ + QFile file("writeonlyfile"); + QVERIFY(file.open(QFile::WriteOnly)); + char c; + QTest::ignoreMessage(QtWarningMsg, "QIODevice::read: WriteOnly device"); + QCOMPARE(file.read(&c, 1), qint64(-1)); +} + +void tst_QFile::writeToReadOnlyFile() +{ + QFile file("readonlyfile"); + QVERIFY(file.open(QFile::ReadOnly)); + char c = 0; + QTest::ignoreMessage(QtWarningMsg, "QIODevice::write: ReadOnly device"); + QCOMPARE(file.write(&c, 1), qint64(-1)); +} + +void tst_QFile::virtualFile() +{ + // test if QFile works with virtual files + QString fname; +#if defined(Q_OS_LINUX) + fname = "/proc/self/maps"; +#elif defined(Q_OS_AIX) + fname = QString("/proc/%1/map").arg(getpid()); +#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) + fname = "/proc/curproc/map"; +#else + QSKIP("This platform does not have 0-sized virtual files", SkipAll); +#endif + + // consistency check + QFileInfo fi(fname); + QVERIFY(fi.exists()); + QVERIFY(fi.isFile()); + QCOMPARE(fi.size(), Q_INT64_C(0)); + + // open the file + QFile f(fname); + QVERIFY(f.open(QIODevice::ReadOnly)); + QCOMPARE(f.size(), Q_INT64_C(0)); + QVERIFY(f.atEnd()); + + // read data + QByteArray data = f.read(16); + QCOMPARE(data.size(), 16); + QCOMPARE(f.pos(), Q_INT64_C(16)); + + // line-reading + data = f.readLine(); + QVERIFY(!data.isEmpty()); + + // read all: + data = f.readAll(); + QVERIFY(f.pos() != 0); + QVERIFY(!data.isEmpty()); + + // seeking + QVERIFY(f.seek(1)); + QCOMPARE(f.pos(), Q_INT64_C(1)); +} + +void tst_QFile::textFile() +{ +#if defined(Q_OS_WINCE) + FILE *fs = ::fopen((QCoreApplication::applicationDirPath() + "/writeabletextfile").toAscii() , "wt"); +#elif defined(Q_OS_WIN) + FILE *fs = ::fopen("writeabletextfile", "wt"); +#else + FILE *fs = ::fopen("writeabletextfile", "w"); +#endif + QFile f; + QByteArray part1("This\nis\na\nfile\nwith\nnewlines\n"); + QByteArray part2("Add\nsome\nmore\nnewlines\n"); + + QVERIFY(f.open(fs, QIODevice::WriteOnly)); + f.write(part1); + f.write(part2); + f.close(); + ::fclose(fs); + + QFile file("writeabletextfile"); + QVERIFY(file.open(QIODevice::ReadOnly)); + + QByteArray data = file.readAll(); + + QByteArray expected = part1 + part2; +#ifdef Q_OS_WIN + expected.replace("\n", "\015\012"); +#endif + QCOMPARE(data, expected); + file.close(); + file.remove(); +} + +void tst_QFile::rename_data() +{ + QTest::addColumn("source"); + QTest::addColumn("destination"); + QTest::addColumn("result"); + + QTest::newRow("a -> b") << QString("a") << QString("b") << false; + QTest::newRow("a -> .") << QString("a") << QString(".") << false; + QTest::newRow("renamefile -> renamefile") << QString("renamefile") << QString("renamefile") << false; + QTest::newRow("renamefile -> noreadfile") << QString("renamefile") << QString("noreadfile") << false; +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) + QTest::newRow("renamefile -> /etc/renamefile") << QString("renamefile") << QString("/etc/renamefile") << false; +#endif + QTest::newRow("renamefile -> renamedfile") << QString("renamefile") << QString("renamedfile") << true; + QTest::newRow("renamefile -> ..") << QString("renamefile") << QString("..") << false; +} + +void tst_QFile::rename() +{ + QFETCH(QString, source); + QFETCH(QString, destination); + QFETCH(bool, result); + + QFile::remove("renamedfile"); + QFile f("renamefile"); + f.open(QFile::WriteOnly); + f.close(); + + QFile file(source); + QCOMPARE(file.rename(destination), result); + + if (result) + QCOMPARE(file.error(), QFile::NoError); + else + QCOMPARE(file.error(), QFile::RenameError); + + QFile::remove("renamefile"); +} + +/*! + \since 4.5 + + Some special files have QFile::atEnd() returning true, even though there is + more data available. True for corner cases, as well as some mounts on OS X. + + Here, we reproduce that condition by having a QFile sub-class with this + peculiar atEnd() behavior. + + See task 231583. + */ +void tst_QFile::renameWithAtEndSpecialFile() const +{ + class PeculiarAtEnd : public QFile + { + public: + virtual bool atEnd() const + { + return true; + } + }; + + const QString newName(QLatin1String("newName.txt")); + /* Cleanup, so we're a bit more robust. */ + QFile::remove(newName); + + const QString originalName(QString(SRCDIR "forRenaming.txt")); + + PeculiarAtEnd file; + file.setFileName(originalName); + QVERIFY(file.open(QIODevice::ReadOnly)); + + QVERIFY(file.rename(newName)); + + file.close(); + /* Guess what, we have to rename it back, otherwise we'll fail on second + * invocation. */ + QVERIFY(QFile::rename(newName, originalName)); +} + +void tst_QFile::renameFallback() +{ + // Using a resource file both to trigger QFile::rename's fallback handling + // and as a *read-only* source whose move should fail. + QFile file(":/rename-fallback.qrc"); + QVERIFY(file.exists() && "(test-precondition)"); + QFile::remove("file-rename-destination.txt"); + + QVERIFY(!file.rename("file-rename-destination.txt")); + QVERIFY(!QFile::exists("file-rename-destination.txt")); + QVERIFY(!file.isOpen()); +} + +void tst_QFile::renameMultiple() +{ + // create the file if it doesn't exist + QFile file("file-to-be-renamed.txt"); + QFile file2("existing-file.txt"); + QVERIFY(file.open(QIODevice::ReadWrite) && "(test-precondition)"); + QVERIFY(file2.open(QIODevice::ReadWrite) && "(test-precondition)"); + + // any stale files from previous test failures? + QFile::remove("file-renamed-once.txt"); + QFile::remove("file-renamed-twice.txt"); + + // begin testing + QVERIFY(QFile::exists("existing-file.txt")); + QVERIFY(!file.rename("existing-file.txt")); + QCOMPARE(file.error(), QFile::RenameError); + QCOMPARE(file.fileName(), QString("file-to-be-renamed.txt")); + + QVERIFY(file.rename("file-renamed-once.txt")); + QVERIFY(!file.isOpen()); + QCOMPARE(file.fileName(), QString("file-renamed-once.txt")); + + QVERIFY(QFile::exists("existing-file.txt")); + QVERIFY(!file.rename("existing-file.txt")); + QCOMPARE(file.error(), QFile::RenameError); + QCOMPARE(file.fileName(), QString("file-renamed-once.txt")); + + QVERIFY(file.rename("file-renamed-twice.txt")); + QVERIFY(!file.isOpen()); + QCOMPARE(file.fileName(), QString("file-renamed-twice.txt")); + + QVERIFY(QFile::exists("existing-file.txt")); + QVERIFY(!QFile::exists("file-to-be-renamed.txt")); + QVERIFY(!QFile::exists("file-renamed-once.txt")); + QVERIFY(QFile::exists("file-renamed-twice.txt")); + + file.remove(); + file2.remove(); + QVERIFY(!QFile::exists("file-renamed-twice.txt")); + QVERIFY(!QFile::exists("existing-file.txt")); +} + +void tst_QFile::appendAndRead() +{ + QFile writeFile(QLatin1String("appendfile.txt")); + QVERIFY(writeFile.open(QIODevice::WriteOnly | QIODevice::Truncate)); + + QFile readFile(QLatin1String("appendfile.txt")); + QVERIFY(readFile.open(QIODevice::ReadOnly)); + + // Write to the end of the file, then read that character back, and so on. + for (int i = 0; i < 100; ++i) { + char c = '\0'; + writeFile.putChar(char(i % 256)); + writeFile.flush(); + QVERIFY(readFile.getChar(&c)); + QCOMPARE(c, char(i % 256)); + QCOMPARE(readFile.pos(), writeFile.pos()); + } + + // Write blocks and read them back + for (int j = 0; j < 18; ++j) { + writeFile.write(QByteArray(1 << j, '@')); + writeFile.flush(); + QCOMPARE(readFile.read(1 << j).size(), 1 << j); + } + + readFile.close(); + QFile::remove(QLatin1String("appendfile.txt")); +} + +void tst_QFile::miscWithUncPathAsCurrentDir() +{ +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + QString current = QDir::currentPath(); + QVERIFY(QDir::setCurrent("//" + QtNetworkSettings::winServerName() + "/testshare")); + QFile file("test.pri"); + QVERIFY(file.exists()); + QCOMPARE(int(file.size()), 34); + QVERIFY(file.open(QIODevice::ReadOnly)); + QVERIFY(QDir::setCurrent(current)); +#endif +} + +void tst_QFile::standarderror() +{ + QFile f; + bool ok = f.open(stderr, QFile::WriteOnly); + QVERIFY(ok); + f.close(); +} + +void tst_QFile::handle() +{ + int fd; +#if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN) + QFile file(SRCDIR "tst_qfile.cpp"); + QVERIFY(file.open(QIODevice::ReadOnly)); + fd = int(file.handle()); + QVERIFY(fd > 2); + QCOMPARE(int(file.handle()), fd); + char c = '\0'; + QT_READ(int(file.handle()), &c, 1); + QCOMPARE(c, '/'); + + // test if the QFile and the handle remain in sync + QVERIFY(file.getChar(&c)); + QCOMPARE(c, '*'); + + // same, but read from QFile first now + file.close(); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Unbuffered)); + fd = int(file.handle()); + QVERIFY(fd > 2); + QVERIFY(file.getChar(&c)); + QCOMPARE(c, '/'); +#ifdef Q_OS_UNIX + QCOMPARE(QT_READ(fd, &c, 1), ssize_t(1)); +#else + QCOMPARE(QT_READ(fd, &c, 1), 1); +#endif + + QCOMPARE(c, '*'); +#endif + + //test round trip of adopted stdio file handle + QFile file2; + FILE *fp = fopen(SRCDIR "tst_qfile.cpp", "r"); + file2.open(fp, QIODevice::ReadOnly); + QCOMPARE(int(file2.handle()), int(fileno(fp))); + QCOMPARE(int(file2.handle()), int(fileno(fp))); + fclose(fp); + + //test round trip of adopted posix file handle +#ifdef Q_OS_UNIX + QFile file3; + fd = QT_OPEN(SRCDIR "tst_qfile.cpp", QT_OPEN_RDONLY); + file3.open(fd, QIODevice::ReadOnly); + QCOMPARE(int(file3.handle()), fd); + QT_CLOSE(fd); +#endif +} + +void tst_QFile::nativeHandleLeaks() +{ +#ifdef Q_OS_SYMBIAN + QSKIP("test assumptions invalid for symbian", SkipAll); +#else + int fd1, fd2; + +#ifdef Q_OS_WIN + HANDLE handle1, handle2; +#endif + + { + QFile file("qt_file.tmp"); + QVERIFY( file.open(QIODevice::ReadWrite) ); + + fd1 = file.handle(); + QVERIFY( -1 != fd1 ); + } + +#ifdef Q_OS_WIN + handle1 = ::CreateFileA("qt_file.tmp", GENERIC_READ, 0, NULL, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + QVERIFY( INVALID_HANDLE_VALUE != handle1 ); + QVERIFY( ::CloseHandle(handle1) ); +#endif + + { + QFile file("qt_file.tmp"); + QVERIFY( file.open(QIODevice::ReadOnly) ); + + fd2 = file.handle(); + QVERIFY( -1 != fd2 ); + } + +#ifdef Q_OS_WIN + handle2 = ::CreateFileA("qt_file.tmp", GENERIC_READ, 0, NULL, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + QVERIFY( INVALID_HANDLE_VALUE != handle2 ); + QVERIFY( ::CloseHandle(handle2) ); +#endif + + QCOMPARE( fd2, fd1 ); + +#ifdef Q_OS_WIN + QCOMPARE( handle2, handle1 ); +#endif +#endif +} + +void tst_QFile::readEof_data() +{ + QTest::addColumn("filename"); + QTest::addColumn("imode"); + + QTest::newRow("buffered") << SRCDIR "testfile.txt" << 0; + QTest::newRow("unbuffered") << SRCDIR "testfile.txt" << int(QIODevice::Unbuffered); + +#if defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) + QTest::newRow("sequential,buffered") << "/dev/null" << 0; + QTest::newRow("sequential,unbuffered") << "/dev/null" << int(QIODevice::Unbuffered); +#endif +} + +void tst_QFile::readEof() +{ + QFETCH(QString, filename); + QFETCH(int, imode); + QIODevice::OpenMode mode = QIODevice::OpenMode(imode); + + { + QFile file(filename); + QVERIFY(file.open(QIODevice::ReadOnly | mode)); + bool isSequential = file.isSequential(); + if (!isSequential) { + QVERIFY(file.seek(245)); + QVERIFY(file.atEnd()); + } + + char buf[10]; + int ret = file.read(buf, sizeof buf); + QCOMPARE(ret, 0); + QVERIFY(file.error() == QFile::NoError); + QVERIFY(file.atEnd()); + + // Do it again to ensure that we get the same result + ret = file.read(buf, sizeof buf); + QCOMPARE(ret, 0); + QVERIFY(file.error() == QFile::NoError); + QVERIFY(file.atEnd()); + } + + { + QFile file(filename); + QVERIFY(file.open(QIODevice::ReadOnly | mode)); + bool isSequential = file.isSequential(); + if (!isSequential) { + QVERIFY(file.seek(245)); + QVERIFY(file.atEnd()); + } + + QByteArray ret = file.read(10); + QVERIFY(ret.isEmpty()); + QVERIFY(file.error() == QFile::NoError); + QVERIFY(file.atEnd()); + + // Do it again to ensure that we get the same result + ret = file.read(10); + QVERIFY(ret.isEmpty()); + QVERIFY(file.error() == QFile::NoError); + QVERIFY(file.atEnd()); + } + + { + QFile file(filename); + QVERIFY(file.open(QIODevice::ReadOnly | mode)); + bool isSequential = file.isSequential(); + if (!isSequential) { + QVERIFY(file.seek(245)); + QVERIFY(file.atEnd()); + } + + char buf[10]; + int ret = file.readLine(buf, sizeof buf); + QCOMPARE(ret, -1); + QVERIFY(file.error() == QFile::NoError); + QVERIFY(file.atEnd()); + + // Do it again to ensure that we get the same result + ret = file.readLine(buf, sizeof buf); + QCOMPARE(ret, -1); + QVERIFY(file.error() == QFile::NoError); + QVERIFY(file.atEnd()); + } + + { + QFile file(filename); + QVERIFY(file.open(QIODevice::ReadOnly | mode)); + bool isSequential = file.isSequential(); + if (!isSequential) { + QVERIFY(file.seek(245)); + QVERIFY(file.atEnd()); + } + + QByteArray ret = file.readLine(); + QVERIFY(ret.isNull()); + QVERIFY(file.error() == QFile::NoError); + QVERIFY(file.atEnd()); + + // Do it again to ensure that we get the same result + ret = file.readLine(); + QVERIFY(ret.isNull()); + QVERIFY(file.error() == QFile::NoError); + QVERIFY(file.atEnd()); + } + + { + QFile file(filename); + QVERIFY(file.open(QIODevice::ReadOnly | mode)); + bool isSequential = file.isSequential(); + if (!isSequential) { + QVERIFY(file.seek(245)); + QVERIFY(file.atEnd()); + } + + char c; + QVERIFY(!file.getChar(&c)); + QVERIFY(file.error() == QFile::NoError); + QVERIFY(file.atEnd()); + + // Do it again to ensure that we get the same result + QVERIFY(!file.getChar(&c)); + QVERIFY(file.error() == QFile::NoError); + QVERIFY(file.atEnd()); + } +} + +void tst_QFile::task167217() +{ + // Regression introduced in 4.3.0; after a failed stat, pos() could no + // longer be calculated correctly. + QFile::remove("tmp.txt"); + QFile file("tmp.txt"); + QVERIFY(!file.exists()); + QVERIFY(file.open(QIODevice::Append)); + QVERIFY(file.exists()); + file.write("qt430", 5); + QVERIFY(!file.isSequential()); + QCOMPARE(file.pos(), qint64(5)); + file.remove(); +} + +#define FILESIZE 65536 * 3 + +void tst_QFile::map_data() +{ + QTest::addColumn("fileSize"); + QTest::addColumn("offset"); + QTest::addColumn("size"); + QTest::addColumn("error"); + + QTest::newRow("zero") << FILESIZE << 0 << FILESIZE << QFile::NoError; + QTest::newRow("small, but 0") << FILESIZE << 30 << FILESIZE - 30 << QFile::NoError; + QTest::newRow("a page") << FILESIZE << 4096 << FILESIZE - 4096 << QFile::NoError; + QTest::newRow("+page") << FILESIZE << 5000 << FILESIZE - 5000 << QFile::NoError; + QTest::newRow("++page") << FILESIZE << 65576 << FILESIZE - 65576 << QFile::NoError; + QTest::newRow("bad size") << FILESIZE << 0 << -1 << QFile::ResourceError; + QTest::newRow("bad offset") << FILESIZE << -1 << 1 << QFile::UnspecifiedError; + QTest::newRow("zerozero") << FILESIZE << 0 << 0 << QFile::UnspecifiedError; +} + +void tst_QFile::map() +{ + QFETCH(int, fileSize); + QFETCH(int, offset); + QFETCH(int, size); + QFETCH(QFile::FileError, error); + + QString fileName = QDir::currentPath() + '/' + "qfile_map_testfile"; + +#ifdef Q_WS_WINCE + fileName = QFileInfo(fileName).absoluteFilePath(); +#endif + + if (QFile::exists(fileName)) { + QVERIFY(QFile::setPermissions(fileName, + QFile::WriteOwner | QFile::ReadOwner | QFile::WriteUser | QFile::ReadUser)); + QFile::remove(fileName); + } + QFile file(fileName); + + // invalid, not open + uchar *memory = file.map(0, size); + QVERIFY(!memory); + QCOMPARE(file.error(), QFile::PermissionsError); + QVERIFY(!file.unmap(memory)); + QCOMPARE(file.error(), QFile::PermissionsError); + + // make a file + QVERIFY(file.open(QFile::ReadWrite)); + QVERIFY(file.resize(fileSize)); + QVERIFY(file.flush()); + file.close(); + QVERIFY(file.open(QFile::ReadWrite)); + memory = file.map(offset, size); + if (error != QFile::NoError) { + + QVERIFY(file.error() != QFile::NoError); + return; + } + + QCOMPARE(file.error(), error); + QVERIFY(memory); + memory[0] = 'Q'; + QVERIFY(file.unmap(memory)); + QCOMPARE(file.error(), QFile::NoError); + + // Verify changes were saved + memory = file.map(offset, size); + QCOMPARE(file.error(), QFile::NoError); + QVERIFY(memory); + QVERIFY(memory[0] == 'Q'); + QVERIFY(file.unmap(memory)); + QCOMPARE(file.error(), QFile::NoError); + + // hpux wont let you map multiple times. +#if !defined(Q_OS_HPUX) && !defined(Q_USE_DEPRECATED_MAP_API) && !defined(Q_OS_WINCE) + // exotic test to make sure that multiple maps work + + // note: windows ce does not reference count mutliple maps + // it's essentially just the same reference but it + // cause a resource lock on the file which prevents it + // from being removed uchar *memory1 = file.map(0, file.size()); + uchar *memory1 = file.map(0, file.size()); + QCOMPARE(file.error(), QFile::NoError); + uchar *memory2 = file.map(0, file.size()); + QCOMPARE(file.error(), QFile::NoError); + QVERIFY(memory1); + QVERIFY(memory2); + QVERIFY(file.unmap(memory1)); + QCOMPARE(file.error(), QFile::NoError); + QVERIFY(file.unmap(memory2)); + QCOMPARE(file.error(), QFile::NoError); + memory1 = file.map(0, file.size()); + QCOMPARE(file.error(), QFile::NoError); + QVERIFY(memory1); + QVERIFY(file.unmap(memory1)); + QCOMPARE(file.error(), QFile::NoError); +#endif + + file.close(); + +#if defined(Q_OS_SYMBIAN) + if (false) // No permissions for user makes no sense in Symbian +#elif defined(Q_OS_UNIX) + if (::getuid() != 0) + // root always has permissions +#endif + { + // Change permissions on a file, just to confirm it would fail + QFile::Permissions originalPermissions = file.permissions(); + QVERIFY(file.setPermissions(QFile::ReadOther)); + QVERIFY(!file.open(QFile::ReadWrite)); + memory = file.map(offset, size); + QCOMPARE(file.error(), QFile::PermissionsError); + QVERIFY(!memory); + QVERIFY(file.setPermissions(originalPermissions)); + } + QVERIFY(file.remove()); +} + +void tst_QFile::mapResource_data() +{ + QTest::addColumn("offset"); + QTest::addColumn("size"); + QTest::addColumn("error"); + QTest::addColumn("fileName"); + + QString validFile = ":/tst_qfileinfo/resources/file1.ext1"; + QString invalidFile = ":/tst_qfileinfo/resources/filefoo.ext1"; + + for (int i = 0; i < 2; ++i) { + QString file = (i == 0) ? validFile : invalidFile; + QTest::newRow("0, 0") << 0 << 0 << QFile::UnspecifiedError << file; + QTest::newRow("0, BIG") << 0 << 4096 << QFile::UnspecifiedError << file; + QTest::newRow("-1, 0") << -1 << 0 << QFile::UnspecifiedError << file; + QTest::newRow("0, -1") << 0 << -1 << QFile::UnspecifiedError << file; + } + + QTest::newRow("0, 1") << 0 << 1 << QFile::NoError << validFile; +} + +void tst_QFile::mapResource() +{ + QFETCH(QString, fileName); + QFETCH(int, offset); + QFETCH(int, size); + QFETCH(QFile::FileError, error); + + QFile file(fileName); + uchar *memory = file.map(offset, size); + QCOMPARE(file.error(), error); + QVERIFY((error == QFile::NoError) ? (memory != 0) : (memory == 0)); + if (error == QFile::NoError) + QCOMPARE(QString(memory[0]), QString::number(offset + 1)); + QVERIFY(file.unmap(memory)); +} + +void tst_QFile::mapOpenMode_data() +{ + QTest::addColumn("openMode"); + + QTest::newRow("ReadOnly") << int(QIODevice::ReadOnly); + //QTest::newRow("WriteOnly") << int(QIODevice::WriteOnly); // this doesn't make sense + QTest::newRow("ReadWrite") << int(QIODevice::ReadWrite); + QTest::newRow("ReadOnly,Unbuffered") << int(QIODevice::ReadOnly | QIODevice::Unbuffered); + QTest::newRow("ReadWrite,Unbuffered") << int(QIODevice::ReadWrite | QIODevice::Unbuffered); +} + +void tst_QFile::mapOpenMode() +{ + QFETCH(int, openMode); + static const qint64 fileSize = 4096; + + QByteArray pattern(fileSize, 'A'); + + QString fileName = QDir::currentPath() + '/' + "qfile_map_testfile"; + if (QFile::exists(fileName)) { + QVERIFY(QFile::setPermissions(fileName, + QFile::WriteOwner | QFile::ReadOwner | QFile::WriteUser | QFile::ReadUser)); + QFile::remove(fileName); + } + QFile file(fileName); + + // make a file + QVERIFY(file.open(QFile::ReadWrite)); + QVERIFY(file.write(pattern)); + QVERIFY(file.flush()); + file.close(); + + // open according to our mode + QVERIFY(file.open(QIODevice::OpenMode(openMode))); + + uchar *memory = file.map(0, fileSize); + QVERIFY(memory); + QVERIFY(memcmp(memory, pattern, fileSize) == 0); + + if (openMode & QIODevice::WriteOnly) { + // try to write to the file + *memory = 'a'; + file.unmap(memory); + file.close(); + file.open(QIODevice::OpenMode(openMode)); + file.seek(0); + char c; + QVERIFY(file.getChar(&c)); + QCOMPARE(c, 'a'); + } + + file.close(); +} + +void tst_QFile::openDirectory() +{ + QFile f1(SRCDIR "resources"); + // it's a directory, it must exist + QVERIFY(f1.exists()); + + // ...but not be openable + QVERIFY(!f1.open(QIODevice::ReadOnly)); + f1.close(); + QVERIFY(!f1.open(QIODevice::ReadOnly|QIODevice::Unbuffered)); + f1.close(); + QVERIFY(!f1.open(QIODevice::ReadWrite)); + f1.close(); + QVERIFY(!f1.open(QIODevice::WriteOnly)); + f1.close(); + QVERIFY(!f1.open(QIODevice::WriteOnly|QIODevice::Unbuffered)); + f1.close(); +} + +void tst_QFile::openStandardStreamsFileDescriptors() +{ +#ifdef Q_WS_WINCE + //allthough Windows CE (not mobile!) has functions that allow redirecting + //the standard file descriptors to a file (see SetStdioPathW/GetStdioPathW) + //it does not have functions to simply open them like below . + QSKIP("Opening standard streams on Windows CE via descriptor not implemented", SkipAll); +#endif + // Using file descriptors + { + QFile in; + in.open(STDIN_FILENO, QIODevice::ReadOnly); + QCOMPARE( in.pos(), (qint64)0 ); + QCOMPARE( in.size(), (qint64)0 ); + QVERIFY( in.isSequential() ); + } + + { + QFile out; + out.open(STDOUT_FILENO, QIODevice::WriteOnly); + QCOMPARE( out.pos(), (qint64)0 ); + QCOMPARE( out.size(), (qint64)0 ); + QVERIFY( out.isSequential() ); + } + + { + QFile err; + err.open(STDERR_FILENO, QIODevice::WriteOnly); + QCOMPARE( err.pos(), (qint64)0 ); + QCOMPARE( err.size(), (qint64)0 ); + QVERIFY( err.isSequential() ); + } +} + +void tst_QFile::openStandardStreamsBufferedStreams() +{ +#if defined (Q_OS_WIN) || defined(Q_OS_SYMBIAN) + QSKIP("Unix only test.", SkipAll); +#endif + // Using streams + { + QFile in; + in.open(stdin, QIODevice::ReadOnly); + QCOMPARE( in.pos(), (qint64)0 ); + QCOMPARE( in.size(), (qint64)0 ); + QVERIFY( in.isSequential() ); + } + + { + QFile out; + out.open(stdout, QIODevice::WriteOnly); + QCOMPARE( out.pos(), (qint64)0 ); + QCOMPARE( out.size(), (qint64)0 ); + QVERIFY( out.isSequential() ); + } + + { + QFile err; + err.open(stderr, QIODevice::WriteOnly); + QCOMPARE( err.pos(), (qint64)0 ); + QCOMPARE( err.size(), (qint64)0 ); + QVERIFY( err.isSequential() ); + } +} + +void tst_QFile::openStandardStreams() +{ + openStandardStreamsFileDescriptors(); + openStandardStreamsBufferedStreams(); +} + +void tst_QFile::writeNothing() +{ + for (int i = 0; i < NumberOfFileTypes; ++i) { + QFile file("file.txt"); + QVERIFY( openFile(file, QIODevice::WriteOnly | QIODevice::Unbuffered, FileType(i)) ); + QVERIFY( 0 == file.write((char *)0, 0) ); + QCOMPARE( file.error(), QFile::NoError ); + closeFile(file); + } +} + +void tst_QFile::resize_data() +{ + QTest::addColumn("filetype"); + + QTest::newRow("native") << int(OpenQFile); + QTest::newRow("fileno") << int(OpenFd); + QTest::newRow("stream") << int(OpenStream); +#ifdef Q_OS_SYMBIAN + QTest::newRow("rfile") << int(OpenRFile); +#endif +} + +void tst_QFile::resize() +{ + QFETCH(int, filetype); + QString filename(QLatin1String("file.txt")); + QFile file(filename); + QVERIFY(openFile(file, QIODevice::ReadWrite, FileType(filetype))); + QVERIFY(file.resize(8)); + QCOMPARE(file.size(), qint64(8)); + closeFile(file); + QFile::resize(filename, 4); + QCOMPARE(QFileInfo(filename).size(), qint64(4)); + QVERIFY(QFile::remove(filename)); +} + +void tst_QFile::objectConstructors() +{ + QObject ob; + QFile* file1 = new QFile(SRCDIR "testfile.txt", &ob); + QFile* file2 = new QFile(&ob); + QVERIFY(file1->exists()); + QVERIFY(!file2->exists()); +} + +#ifdef Q_OS_SYMBIAN +void tst_QFile::platformSecurity_data() +{ + QTest::addColumn("file"); + QTest::addColumn("readable"); + QTest::addColumn("writable"); + + QString selfname = QCoreApplication::applicationFilePath(); + QString ownprivate = QCoreApplication::applicationDirPath(); + QString owndrive = selfname.left(2); + bool amiprivileged = RProcess().HasCapability(ECapabilityAllFiles); + QTest::newRow("resource") << owndrive + "/resource/apps/tst_qfile.rsc" << true << amiprivileged; + QTest::newRow("sys") << selfname << amiprivileged << false; + QTest::newRow("own private") << ownprivate + "/testfile.txt" << true << true; + QTest::newRow("other private") << owndrive + "/private/10003a3f/import/apps/tst_qfile_reg.rsc" << amiprivileged << amiprivileged; +} + +void tst_QFile::platformSecurity() +{ + QFETCH(QString,file); + QFETCH(bool,readable); + QFETCH(bool,writable); + + { + QFile f(file); + QCOMPARE(f.open(QIODevice::ReadOnly), readable); + } + + { + QFile f(file); + QCOMPARE(f.open(QIODevice::ReadOnly | QIODevice::Unbuffered), readable); + } + + //append mode used to avoid truncating the files. + { + QFile f(file); + QCOMPARE(f.open(QIODevice::WriteOnly | QIODevice::Append), writable); + } + + { + QFile f(file); + QCOMPARE(f.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Unbuffered), writable); + } + + { + QFile f(file); + QCOMPARE(f.open(QIODevice::ReadWrite), writable); + } + + { + QFile f(file); + QCOMPARE(f.open(QIODevice::ReadWrite | QIODevice::Unbuffered), writable); + } +} +#endif + +void tst_QFile::caseSensitivity() +{ +#if defined(Q_OS_SYMBIAN) || defined(Q_OS_WIN) || defined(Q_OS_MAC) + const bool caseSensitive = false; +#else + const bool caseSensitive = true; +#endif + QByteArray testData("a little test"); + QString filename("File.txt"); + { + QFile f(filename); + QVERIFY(f.open(QIODevice::WriteOnly)); + QVERIFY(f.write(testData)); + f.close(); + } + QStringList alternates; + QFileInfo fi(filename); + QVERIFY(fi.exists()); + alternates << "file.txt" << "File.TXT" << "fIlE.TxT" << fi.absoluteFilePath().toUpper() << fi.absoluteFilePath().toLower(); + foreach (QString alt, alternates) { + QFileInfo fi2(alt); + QCOMPARE(fi2.exists(), !caseSensitive); + QCOMPARE(fi.size() == fi2.size(), !caseSensitive); + QFile f2(alt); + QCOMPARE(f2.open(QIODevice::ReadOnly), !caseSensitive); + if (!caseSensitive) + QCOMPARE(f2.readAll(), testData); + } +} + +//MSVCRT asserts when any function is called with a closed file handle. +//This replaces the default crashing error handler with one that ignores the error (allowing EBADF to be returned) +class AutoIgnoreInvalidParameter +{ +public: +#if defined(Q_OS_WIN) && defined (Q_CC_MSVC) + static void ignore_invalid_parameter(const wchar_t*, const wchar_t*, const wchar_t*, unsigned int, uintptr_t) {} + AutoIgnoreInvalidParameter() + { + oldHandler = _set_invalid_parameter_handler(ignore_invalid_parameter); + //also disable the abort/retry/ignore popup + oldReportMode = _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); + } + ~AutoIgnoreInvalidParameter() + { + //restore previous settings + _set_invalid_parameter_handler(oldHandler); + _CrtSetReportMode(_CRT_ASSERT, oldReportMode); + } + _invalid_parameter_handler oldHandler; + int oldReportMode; +#endif +}; + +void tst_QFile::autocloseHandle() +{ +#ifdef Q_OS_SYMBIAN + // these tests are a bit different, because using a closed file handle results in a panic rather than error + { + QFile file("readonlyfile"); + QFile file2("readonlyfile"); + QVERIFY(openFile(file, QIODevice::ReadOnly, OpenRFile, QFile::AutoCloseHandle)); + // file is opened with mandatory lock, so opening again should fail + QVERIFY(!file2.open(QIODevice::ReadOnly)); + + file.close(); + // opening again should now succeed (because handle is closed) + QVERIFY(file2.open(QIODevice::ReadOnly)); + } + + { + QFile file("readonlyfile"); + QFile file2("readonlyfile"); + QVERIFY(openFile(file, QIODevice::ReadOnly, OpenRFile, QFile::DontCloseHandle)); + // file is opened with mandatory lock, so opening again should fail + QVERIFY(!file2.open(QIODevice::ReadOnly)); + + file.close(); + // opening again should still fail (because handle is not auto closed) + QVERIFY(!file2.open(QIODevice::ReadOnly)); + + rfile_.Close(); + // now it should succeed + QVERIFY(file2.open(QIODevice::ReadOnly)); + } +#endif + + { + QFile file("readonlyfile"); + QVERIFY(openFile(file, QIODevice::ReadOnly, OpenFd, QFile::AutoCloseHandle)); + int fd = fd_; + QCOMPARE(file.handle(), fd); + file.close(); + fd_ = -1; + QCOMPARE(file.handle(), -1); + AutoIgnoreInvalidParameter a; + Q_UNUSED(a); + //file is closed, read should fail + char buf; + QCOMPARE((int)QT_READ(fd, &buf, 1), -1); + QVERIFY(errno = EBADF); + } + + { + QFile file("readonlyfile"); + QVERIFY(openFile(file, QIODevice::ReadOnly, OpenFd, QFile::DontCloseHandle)); + QCOMPARE(file.handle(), fd_); + file.close(); + QCOMPARE(file.handle(), -1); + //file is not closed, read should succeed + char buf; + QCOMPARE((int)QT_READ(fd_, &buf, 1), 1); + ::close(fd_); + fd_ = -1; + } + + { + QFile file("readonlyfile"); + QVERIFY(openFile(file, QIODevice::ReadOnly, OpenStream, QFile::AutoCloseHandle)); + int fd = fileno(stream_); + QCOMPARE(file.handle(), fd); + file.close(); + stream_ = 0; + QCOMPARE(file.handle(), -1); + AutoIgnoreInvalidParameter a; + Q_UNUSED(a); + //file is closed, read should fail + char buf; + QCOMPARE((int)QT_READ(fd, &buf, 1), -1); //not using fread because the FILE* was freed by fclose + } + + { + QFile file("readonlyfile"); + QVERIFY(openFile(file, QIODevice::ReadOnly, OpenStream, QFile::DontCloseHandle)); + QCOMPARE(file.handle(), fileno(stream_)); + file.close(); + QCOMPARE(file.handle(), -1); + //file is not closed, read should succeed + char buf; + QCOMPARE(int(::fread(&buf, 1, 1, stream_)), 1); + ::fclose(stream_); + stream_ = 0; + } +} + +QTEST_MAIN(tst_QFile) +#include "tst_qfile.moc" diff --git a/tests/auto/corelib/io/qfile/two.dots.file b/tests/auto/corelib/io/qfile/two.dots.file new file mode 100644 index 0000000000..1910281566 --- /dev/null +++ b/tests/auto/corelib/io/qfile/two.dots.file @@ -0,0 +1 @@ +foo \ No newline at end of file -- cgit v1.2.3