1 /****************************************************************************
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the test suite of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
44 #include <QtAlgorithms>
47 #include <qplatformdefs.h>
59 #ifndef FSCTL_SET_SPARSE
60 // MinGW doesn't define this.
61 #define FSCTL_SET_SPARSE (0x900C4)
78 #if defined(QT_LARGEFILE_SUPPORT) && !defined(Q_OS_MAC)
79 maxSizeBits
= 36; // 64 GiB
80 #elif defined(Q_OS_MAC)
81 // HFS+ does not support sparse files, so we limit file size for the test
83 maxSizeBits
= 32; // 4 GiB
85 maxSizeBits
= 24; // 16 MiB
90 void sparseFileData();
91 QByteArray
const &getDataBlock(int index
, qint64 position
);
94 // The LargeFile test case was designed to be run in order as a single unit
97 void cleanupTestCase();
102 // Create and fill large file
103 void createSparseFile();
104 void fillFileSparsely();
105 void closeSparseFile();
107 // Verify file was created
110 // Positioning in large files
111 void filePositioning();
112 void fdPositioning();
113 void streamPositioning();
115 // Read data from file
116 void openFileForReading();
119 // Map/unmap large file
121 void mapOffsetOverflow();
123 void closeFile() { largeFile
.close(); }
126 void fillFileSparsely_data() { sparseFileData(); }
127 void filePositioning_data() { sparseFileData(); }
128 void fdPositioning_data() { sparseFileData(); }
129 void streamPositioning_data() { sparseFileData(); }
130 void readFile_data() { sparseFileData(); }
131 void mapFile_data() { sparseFileData(); }
139 QVector
<QByteArray
> generatedBlocks
;
146 Convenience function to hide reinterpret_cast when copying a POD directly
150 static inline void appendRaw(QByteArray
&array
, T data
)
152 array
.append(reinterpret_cast<char *>(&data
), sizeof(T
));
156 Pad array with filler up to size. On return, array.size() returns size.
158 static inline void topUpWith(QByteArray
&array
, QByteArray filler
, int size
)
160 Q_ASSERT(filler
.size() > 0);
162 for (int i
= (size
- array
.size()) / filler
.size(); i
> 0; --i
)
163 array
.append(filler
);
165 if (array
.size() < size
) {
166 Q_ASSERT(size
- array
.size() < filler
.size());
167 array
.append(filler
.left(size
- array
.size()));
172 Generate a unique data block containing identifiable data. Unaligned,
173 overlapping and partial blocks should not compare equal.
175 static inline QByteArray
generateDataBlock(int blockSize
, QString text
, qint64 userBits
= -1)
178 block
.reserve(blockSize
);
180 // Use of counter and randomBits means content of block will be dependent
181 // on the generation order. For (file-)systems that do not support sparse
182 // files, these can be removed so the test file can be reused and doesn't
183 // have to be generated for every run.
185 static qint64 counter
= 0;
187 qint64 randomBits
= ((qint64
)qrand() << 32)
188 | ((qint64
)qrand() & 0x00000000ffffffff);
190 appendRaw(block
, randomBits
);
191 appendRaw(block
, userBits
);
192 appendRaw(block
, counter
);
193 appendRaw(block
, (qint32
)0xdeadbeef);
194 appendRaw(block
, blockSize
);
196 QByteArray userContent
= text
.toUtf8();
197 appendRaw(block
, userContent
.size());
198 block
.append(userContent
);
199 appendRaw(block
, (qint64
)0);
202 appendRaw(block
, block
.size());
204 QByteArray
filler("0123456789");
205 block
.append(filler
.right(10 - block
.size() % 10));
206 topUpWith(block
, filler
, blockSize
- 2 * sizeof(qint64
));
208 appendRaw(block
, counter
);
209 appendRaw(block
, userBits
);
210 appendRaw(block
, randomBits
);
212 Q_ASSERT( block
.size() >= blockSize
);
213 block
.resize(blockSize
);
220 Generates data blocks the first time they are requested. Keeps copies for reuse.
222 QByteArray
const &tst_LargeFile::getDataBlock(int index
, qint64 position
)
224 if (index
>= generatedBlocks
.size())
225 generatedBlocks
.resize(index
+ 1);
227 if (generatedBlocks
[index
].isNull()) {
228 QString text
= QString("Current %1-byte block (index = %2) "
229 "starts %3 bytes into the file '%4'.")
233 .arg("qt_largefile.tmp");
235 generatedBlocks
[index
] = generateDataBlock(blockSize
, text
, (qint64
)1 << index
);
238 return generatedBlocks
[index
];
241 void tst_LargeFile::initTestCase()
243 QFile
file("qt_largefile.tmp");
244 QVERIFY( !file
.exists() || file
.remove() );
247 void tst_LargeFile::cleanupTestCase()
249 if (largeFile
.isOpen())
252 QFile
file("qt_largefile.tmp");
253 QVERIFY( !file
.exists() || file
.remove() );
256 void tst_LargeFile::init()
262 void tst_LargeFile::cleanup()
270 void tst_LargeFile::sparseFileData()
272 QTest::addColumn
<int>("index");
273 QTest::addColumn
<qint64
>("position");
274 QTest::addColumn
<QByteArray
>("block");
276 QTest::newRow(QString("block[%1] @%2)")
278 .toLocal8Bit().constData())
279 << 0 << (qint64
)0 << getDataBlock(0, 0);
281 // While on Linux sparse files scale well, on Windows, testing at every
282 // power of 2 leads to very large files. i += 4 gives us a good coverage
283 // without taxing too much on resources.
284 for (int index
= 12; index
<= maxSizeBits
; index
+= 4) {
285 qint64 position
= (qint64
)1 << index
;
286 QByteArray block
= getDataBlock(index
, position
);
289 QString("block[%1] @%2)")
290 .arg(index
).arg(position
)
291 .toLocal8Bit().constData())
292 << index
<< position
<< block
;
296 void tst_LargeFile::createSparseFile()
298 #if defined(Q_OS_WIN)
299 // On Windows platforms, we must explicitly set the file to be sparse,
300 // so disk space is not allocated for the full file when writing to it.
301 HANDLE handle
= ::CreateFileA("qt_largefile.tmp",
302 GENERIC_WRITE
, 0, 0, CREATE_ALWAYS
, 0, 0);
303 QVERIFY( INVALID_HANDLE_VALUE
!= handle
);
306 if (!::DeviceIoControl(handle
, FSCTL_SET_SPARSE
, NULL
, 0, NULL
, 0,
308 QWARN("Unable to set test file as sparse. "
309 "Limiting test file to 16MiB.");
313 int fd
= ::_open_osfhandle((intptr_t)handle
, 0);
315 QVERIFY( largeFile
.open(fd
, QIODevice::WriteOnly
| QIODevice::Unbuffered
) );
317 largeFile
.setFileName("qt_largefile.tmp");
318 QVERIFY( largeFile
.open(QIODevice::WriteOnly
| QIODevice::Unbuffered
) );
322 void tst_LargeFile::closeSparseFile()
324 #if defined(Q_OS_WIN)
325 int fd
= largeFile
.handle();
330 #if defined(Q_OS_WIN)
336 void tst_LargeFile::fillFileSparsely()
338 QFETCH( qint64
, position
);
339 QFETCH( QByteArray
, block
);
340 QCOMPARE( block
.size(), blockSize
);
342 static int lastKnownGoodIndex
= 0;
344 ScopeGuard(tst_LargeFile
* test
)
348 QFETCH( int, index
);
355 this_
->maxSizeBits
= lastKnownGoodIndex
;
357 QString("QFile::error %1: '%2'. Maximum size bits reset to %3.")
358 .arg(this_
->largeFile
.error())
359 .arg(this_
->largeFile
.errorString())
360 .arg(this_
->maxSizeBits
)) );
362 lastKnownGoodIndex
= qMax
<int>(index_
, lastKnownGoodIndex
);
366 tst_LargeFile
* const this_
;
373 ScopeGuard
resetMaxSizeBitsOnFailure(this);
375 QVERIFY( largeFile
.seek(position
) );
376 QCOMPARE( largeFile
.pos(), position
);
378 QCOMPARE( largeFile
.write(block
), (qint64
)blockSize
);
379 QCOMPARE( largeFile
.pos(), position
+ blockSize
);
380 QVERIFY( largeFile
.flush() );
382 resetMaxSizeBitsOnFailure
.failed
= false;
385 void tst_LargeFile::fileCreated()
387 QFileInfo
info("qt_largefile.tmp");
389 QVERIFY( info
.exists() );
390 QVERIFY( info
.isFile() );
391 QVERIFY( info
.size() >= ((qint64
)1 << maxSizeBits
) + blockSize
);
394 void tst_LargeFile::filePositioning()
396 QFETCH( qint64
, position
);
398 QFile
file("qt_largefile.tmp");
399 QVERIFY( file
.open(QIODevice::ReadOnly
) );
401 QVERIFY( file
.seek(position
) );
402 QCOMPARE( file
.pos(), position
);
405 void tst_LargeFile::fdPositioning()
407 QFETCH( qint64
, position
);
409 fd_
= QT_OPEN("qt_largefile.tmp",
410 QT_OPEN_RDONLY
| QT_OPEN_LARGEFILE
);
411 QVERIFY( -1 != fd_
);
414 QVERIFY( file
.open(fd_
, QIODevice::ReadOnly
) );
415 QCOMPARE( file
.pos(), (qint64
)0 );
416 QVERIFY( file
.seek(position
) );
417 QCOMPARE( file
.pos(), position
);
421 QCOMPARE( QT_LSEEK(fd_
, QT_OFF_T(0), SEEK_SET
), QT_OFF_T(0) );
422 QCOMPARE( QT_LSEEK(fd_
, QT_OFF_T(position
), SEEK_SET
), QT_OFF_T(position
) );
424 QVERIFY( file
.open(fd_
, QIODevice::ReadOnly
) );
425 QCOMPARE( QT_LSEEK(fd_
, QT_OFF_T(0), SEEK_CUR
), QT_OFF_T(position
) );
426 QCOMPARE( file
.pos(), position
);
427 QVERIFY( file
.seek(0) );
428 QCOMPARE( file
.pos(), (qint64
)0 );
432 QVERIFY( !QT_CLOSE(fd_
) );
436 void tst_LargeFile::streamPositioning()
438 QFETCH( qint64
, position
);
440 #if defined(QT_LARGEFILE_SUPPORT) && defined(Q_CC_MSVC) && _MSC_VER < 1400
441 if (position
>= (qint64
)1 << 31)
442 QSKIP("MSVC 2003 doesn't have 64 bit versions of fseek/ftell.", SkipSingle
);
445 stream_
= QT_FOPEN("qt_largefile.tmp", "rb");
446 QVERIFY( 0 != stream_
);
449 QVERIFY( file
.open(stream_
, QIODevice::ReadOnly
) );
450 QCOMPARE( file
.pos(), (qint64
)0 );
451 QVERIFY( file
.seek(position
) );
452 QCOMPARE( file
.pos(), position
);
456 QVERIFY( !QT_FSEEK(stream_
, QT_OFF_T(0), SEEK_SET
) );
457 QCOMPARE( QT_FTELL(stream_
), QT_OFF_T(0) );
458 QVERIFY( !QT_FSEEK(stream_
, QT_OFF_T(position
), SEEK_SET
) );
459 QCOMPARE( QT_FTELL(stream_
), QT_OFF_T(position
) );
461 QVERIFY( file
.open(stream_
, QIODevice::ReadOnly
) );
462 QCOMPARE( QT_FTELL(stream_
), QT_OFF_T(position
) );
463 QCOMPARE( file
.pos(), position
);
464 QVERIFY( file
.seek(0) );
465 QCOMPARE( file
.pos(), (qint64
)0 );
469 QVERIFY( !::fclose(stream_
) );
473 void tst_LargeFile::openFileForReading()
475 largeFile
.setFileName("qt_largefile.tmp");
476 QVERIFY( largeFile
.open(QIODevice::ReadOnly
) );
479 void tst_LargeFile::readFile()
481 QFETCH( qint64
, position
);
482 QFETCH( QByteArray
, block
);
483 QCOMPARE( block
.size(), blockSize
);
485 QVERIFY( largeFile
.size() >= position
+ blockSize
);
487 QVERIFY( largeFile
.seek(position
) );
488 QCOMPARE( largeFile
.pos(), position
);
490 QCOMPARE( largeFile
.read(blockSize
), block
);
491 QCOMPARE( largeFile
.pos(), position
+ blockSize
);
494 void tst_LargeFile::mapFile()
496 QFETCH( qint64
, position
);
497 QFETCH( QByteArray
, block
);
498 QCOMPARE( block
.size(), blockSize
);
500 // Keep full block mapped to facilitate OS and/or internal reuse by Qt.
501 uchar
*baseAddress
= largeFile
.map(position
, blockSize
);
502 QVERIFY( baseAddress
);
503 QVERIFY( qEqual(block
.begin(), block
.end(), reinterpret_cast<char*>(baseAddress
)) );
505 for (int offset
= 1; offset
< blockSize
; ++offset
) {
506 uchar
*address
= largeFile
.map(position
+ offset
, blockSize
- offset
);
509 if ( !qEqual(block
.begin() + offset
, block
.end(), reinterpret_cast<char*>(address
)) ) {
510 qDebug() << "Expected:" << block
.toHex();
511 qDebug() << "Actual :" << QByteArray(reinterpret_cast<char*>(address
), blockSize
).toHex();
515 QVERIFY( largeFile
.unmap( address
) );
518 QVERIFY( largeFile
.unmap( baseAddress
) );
521 void tst_LargeFile::mapOffsetOverflow()
523 // Out-of-range mappings should fail, and not silently clip the offset
524 for (int i
= 50; i
< 63; ++i
) {
527 address
= largeFile
.map(((qint64
)1 << i
), blockSize
);
530 address
= largeFile
.map(((qint64
)1 << i
) + blockSize
, blockSize
);
535 QTEST_APPLESS_MAIN(tst_LargeFile
)
536 #include "tst_largefile.moc"