From d9a50fc1dc8470adc3b028587ccf6fde9bcb75ab Mon Sep 17 00:00:00 2001 From: Kurt Korbatits Date: Fri, 13 Jan 2012 14:01:57 +1000 Subject: Moved largefile unittest to same level as qfile. - Moved largefile from out of qfile unittest directory to be on same level as qfile. Change-Id: I479b0b33594812759f8a6a7be61f8340f64234e9 Reviewed-by: Kurt Korbatits Reviewed-by: Jason McDonald Reviewed-by: Rohan McGovern --- tests/auto/corelib/io/io.pro | 1 + tests/auto/corelib/io/largefile/largefile.pro | 6 + tests/auto/corelib/io/largefile/tst_largefile.cpp | 536 +++++++++++++++++++++ .../auto/corelib/io/qfile/largefile/largefile.pro | 6 - .../corelib/io/qfile/largefile/tst_largefile.cpp | 536 --------------------- tests/auto/corelib/io/qfile/qfile.pro | 2 - 6 files changed, 543 insertions(+), 544 deletions(-) create mode 100644 tests/auto/corelib/io/largefile/largefile.pro create mode 100644 tests/auto/corelib/io/largefile/tst_largefile.cpp delete mode 100644 tests/auto/corelib/io/qfile/largefile/largefile.pro delete mode 100644 tests/auto/corelib/io/qfile/largefile/tst_largefile.cpp (limited to 'tests/auto') diff --git a/tests/auto/corelib/io/io.pro b/tests/auto/corelib/io/io.pro index 13993717ac..ae0b4968b8 100644 --- a/tests/auto/corelib/io/io.pro +++ b/tests/auto/corelib/io/io.pro @@ -7,6 +7,7 @@ SUBDIRS=\ qdir \ qdiriterator \ qfile \ + largefile \ qfileinfo \ qfilesystementry \ qfilesystemwatcher \ diff --git a/tests/auto/corelib/io/largefile/largefile.pro b/tests/auto/corelib/io/largefile/largefile.pro new file mode 100644 index 0000000000..4e629122ec --- /dev/null +++ b/tests/auto/corelib/io/largefile/largefile.pro @@ -0,0 +1,6 @@ +CONFIG += testcase parallel_test +TARGET = tst_largefile +QT = core testlib +SOURCES = tst_largefile.cpp + +wince*: SOURCES += $$QT_SOURCE_TREE/src/corelib/kernel/qfunctions_wince.cpp diff --git a/tests/auto/corelib/io/largefile/tst_largefile.cpp b/tests/auto/corelib/io/largefile/tst_largefile.cpp new file mode 100644 index 0000000000..ba2eced60a --- /dev/null +++ b/tests/auto/corelib/io/largefile/tst_largefile.cpp @@ -0,0 +1,536 @@ +/**************************************************************************** +** +** Copyright (C) 2012 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 ); + + 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"); +#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); +#if defined(__x86_64__) + QEXPECT_FAIL("", "fails on 64-bit Linux (QTBUG-21175)", Abort); +#endif + 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/largefile/largefile.pro b/tests/auto/corelib/io/qfile/largefile/largefile.pro deleted file mode 100644 index 4e629122ec..0000000000 --- a/tests/auto/corelib/io/qfile/largefile/largefile.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase parallel_test -TARGET = tst_largefile -QT = core testlib -SOURCES = tst_largefile.cpp - -wince*: SOURCES += $$QT_SOURCE_TREE/src/corelib/kernel/qfunctions_wince.cpp diff --git a/tests/auto/corelib/io/qfile/largefile/tst_largefile.cpp b/tests/auto/corelib/io/qfile/largefile/tst_largefile.cpp deleted file mode 100644 index ba2eced60a..0000000000 --- a/tests/auto/corelib/io/qfile/largefile/tst_largefile.cpp +++ /dev/null @@ -1,536 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 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 ); - - 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"); -#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); -#if defined(__x86_64__) - QEXPECT_FAIL("", "fails on 64-bit Linux (QTBUG-21175)", Abort); -#endif - 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/qfile.pro b/tests/auto/corelib/io/qfile/qfile.pro index 8bb142c5b4..e2c714c67f 100644 --- a/tests/auto/corelib/io/qfile/qfile.pro +++ b/tests/auto/corelib/io/qfile/qfile.pro @@ -5,6 +5,4 @@ wince* { SUBDIRS = test stdinprocess } -SUBDIRS += largefile - CONFIG += parallel_test -- cgit v1.2.3