/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include <QtTest/QtTest>
#include <qplatformdefs.h>

#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QTemporaryDir>

#include <private/qabstractfileengine_p.h>
#include <private/qfsfileengine_p.h>
#include <private/qfilesystemengine_p.h>

#include "emulationdetector.h"

#ifdef Q_OS_WIN
QT_BEGIN_NAMESPACE
extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;
QT_END_NAMESPACE
#endif

#if !defined(QT_NO_NETWORK)
#include <QHostInfo>
#endif
#if QT_CONFIG(process)
# include <QProcess>
#endif
#ifdef Q_OS_WIN
# include <qt_windows.h>
#else
# include <sys/types.h>
# include <unistd.h>
#endif
#ifdef Q_OS_MAC
# include <sys/mount.h>
#elif defined(Q_OS_LINUX)
# include <sys/vfs.h>
#elif defined(Q_OS_FREEBSD)
# include <sys/param.h>
# include <sys/mount.h>
#elif defined(Q_OS_VXWORKS)
# include <fcntl.h>
#if defined(_WRS_KERNEL)
#undef QT_OPEN
#define QT_OPEN(path, oflag) ::open(path, oflag, 0)
#endif
#endif

#ifdef Q_OS_QNX
#ifdef open
#undef open
#endif
#endif

#include <stdio.h>
#include <errno.h>

#ifdef Q_OS_ANDROID
// Android introduces a braindamaged fileno macro that isn't
// compatible with the POSIX fileno or its own FILE type.
#  undef fileno
#endif

#if defined(Q_OS_WIN)
#include "../../../network-settings.h"
#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)


class StdioFileGuard
{
    Q_DISABLE_COPY(StdioFileGuard)
public:
    explicit StdioFileGuard(FILE *f = nullptr) : m_file(f) {}
    ~StdioFileGuard() { close(); }

    operator FILE *() const { return m_file; }

    void close();

private:
    FILE * m_file;
};

void StdioFileGuard::close()
{
    if (m_file != nullptr) {
        fclose(m_file);
        m_file = nullptr;
    }
}

class tst_QFile : public QObject
{
    Q_OBJECT
public:
    tst_QFile();

private slots:
    void init();
    void cleanup();
    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 createFileNewOnly();
    void openFileExistingOnly();
    void append();
    void permissions_data();
    void permissions();
#ifdef Q_OS_WIN
    void permissionsNtfs_data();
    void permissionsNtfs();
#endif
    void setPermissions();
    void copy();
    void copyAfterFail();
    void copyRemovesTemporaryFile() const;
    void copyShouldntOverwrite();
    void copyFallback();
#ifndef Q_OS_WINRT
    void link();
    void linkToDir();
    void absolutePathLinkToRelativePath();
    void readBrokenLink();
#endif
    void readTextFile_data();
    void readTextFile();
    void readTextFile2();
    void writeTextFile_data();
    void writeTextFile();
    /* void largeFileSupport(); */
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
    void largeUncFileSupport();
#endif
    void flush();
    void bufferedRead();
#ifdef Q_OS_UNIX
    void isSequential();
#endif
    void encodeName();
    void truncate();
    void seekToPos();
    void seekAfterEndOfFile();
    void FILEReadWrite();
    void i18nFileName_data();
    void i18nFileName();
    void longFileName_data();
    void longFileName();
    void fileEngineHandler();
#ifdef QT_BUILD_INTERNAL
    void useQFileInAFileHandler();
#endif
    void getCharFF();
    void remove_and_exists();
    void removeOpenFile();
    void fullDisk();
    void writeLargeDataBlock_data();
    void writeLargeDataBlock();
    void readFromWriteOnlyFile();
    void writeToReadOnlyFile();
#if defined(Q_OS_LINUX) || defined(Q_OS_AIX) || defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
    void virtualFile();
#endif
    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 mapWrittenFile_data();
    void mapWrittenFile();

    void openStandardStreamsFileDescriptors();
    void openStandardStreamsBufferedStreams();

    void resize_data();
    void resize();

    void objectConstructors();

    void caseSensitivity();

    void autocloseHandle();

    void posAfterFailedStat();

    void openDirectory();
    void writeNothing();

    void invalidFile_data();
    void invalidFile();

    void reuseQFile();

    void moveToTrash_data();
    void moveToTrash();

private:
#ifdef BUILTIN_TESTDATA
    QSharedPointer<QTemporaryDir> m_dataDir;
#endif
    enum FileType {
        OpenQFile,
        OpenFd,
        OpenStream,
        NumberOfFileTypes
    };

    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);
    }

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

            case NumberOfFileTypes:
                break;
        }

        return false;
    }

    void closeFile(QFile &file)
    {
        file.close();

        if (-1 != fd_)
            QT_CLOSE(fd_);
        if (stream_)
            ::fclose(stream_);

        fd_ = -1;
        stream_ = 0;
    }

    int fd_;
    FILE *stream_;

    QTemporaryDir m_temporaryDir;
    const QString m_oldDir;
    QString m_stdinProcess;
    QString m_testSourceFile;
    QString m_testLogFile;
    QString m_dosFile;
    QString m_forCopyingFile;
    QString m_forRenamingFile;
    QString m_twoDotsFile;
    QString m_testFile;
    QString m_resourcesDir;
    QString m_noEndOfLineFile;
};

static const char noReadFile[] = "noreadfile";
static const char readOnlyFile[] = "readonlyfile";

void tst_QFile::init()
{
    fd_ = -1;
    stream_ = 0;
}

void tst_QFile::cleanup()
{
    if (-1 != fd_)
        QT_CLOSE(fd_);
    fd_ = -1;
    if (stream_)
        ::fclose(stream_);
    stream_ = 0;

    // Windows UNC tests set a different working directory which might not be restored on failures.
    if (QDir::currentPath() != m_temporaryDir.path())
        QVERIFY(QDir::setCurrent(m_temporaryDir.path()));

    // Clean out everything except the readonly-files.
    const QDir dir(m_temporaryDir.path());
    foreach (const QFileInfo &fi, dir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot)) {
        const QString fileName = fi.fileName();
        if (fileName != QLatin1String(noReadFile) && fileName != QLatin1String(readOnlyFile)) {
            const QString absoluteFilePath = fi.absoluteFilePath();
            if (fi.isDir() && !fi.isSymLink()) {
                QDir remainingDir(absoluteFilePath);
                QVERIFY2(remainingDir.removeRecursively(), qPrintable(absoluteFilePath));
            } else {
                if (!(QFile::permissions(absoluteFilePath) & QFile::WriteUser))
                    QVERIFY2(QFile::setPermissions(absoluteFilePath, QFile::WriteUser), qPrintable(absoluteFilePath));
                QVERIFY2(QFile::remove(absoluteFilePath), qPrintable(absoluteFilePath));
            }
        }
    }
}

tst_QFile::tst_QFile() : m_oldDir(QDir::currentPath())
{
}

static QByteArray msgOpenFailed(QIODevice::OpenMode om, const QFile &file)
{
    QString result;
    QDebug(&result).noquote().nospace() << "Could not open \""
        << QDir::toNativeSeparators(file.fileName()) << "\" using "
        << om << ": " << file.errorString();
    return result.toLocal8Bit();
}

static QByteArray msgOpenFailed(const QFile &file)
{
    return (QLatin1String("Could not open \"") + QDir::toNativeSeparators(file.fileName())
        + QLatin1String("\": ") + file.errorString()).toLocal8Bit();
}

static QByteArray msgFileDoesNotExist(const QString &name)
{
    return (QLatin1Char('"') + QDir::toNativeSeparators(name)
        + QLatin1String("\" does not exist.")).toLocal8Bit();
}

void tst_QFile::initTestCase()
{
    QVERIFY2(m_temporaryDir.isValid(), qPrintable(m_temporaryDir.errorString()));
#if QT_CONFIG(process)
#if defined(Q_OS_ANDROID)
    m_stdinProcess = QCoreApplication::applicationDirPath() + QLatin1String("/libstdinprocess_helper.so");
#elif defined(Q_OS_WIN)
    m_stdinProcess = QFINDTESTDATA("stdinprocess_helper.exe");
#else
    m_stdinProcess = QFINDTESTDATA("stdinprocess_helper");
#endif
    QVERIFY(!m_stdinProcess.isEmpty());
#endif
    m_testLogFile = QFINDTESTDATA("testlog.txt");
    QVERIFY(!m_testLogFile.isEmpty());
    m_dosFile = QFINDTESTDATA("dosfile.txt");
    QVERIFY(!m_dosFile.isEmpty());
    m_forCopyingFile = QFINDTESTDATA("forCopying.txt");
    QVERIFY(!m_forCopyingFile .isEmpty());
    m_forRenamingFile = QFINDTESTDATA("forRenaming.txt");
    QVERIFY(!m_forRenamingFile.isEmpty());
    m_twoDotsFile = QFINDTESTDATA("two.dots.file");
    QVERIFY(!m_twoDotsFile.isEmpty());

#ifndef BUILTIN_TESTDATA
    m_testSourceFile = QFINDTESTDATA("tst_qfile.cpp");
    QVERIFY(!m_testSourceFile.isEmpty());
    m_testFile = QFINDTESTDATA("testfile.txt");
    QVERIFY(!m_testFile.isEmpty());
    m_resourcesDir = QFINDTESTDATA("resources");
    QVERIFY(!m_resourcesDir.isEmpty());
#else
    m_dataDir = QEXTRACTTESTDATA("/");
    QVERIFY2(!m_dataDir.isNull(), qPrintable("Could not extract test data"));
    m_testFile = m_dataDir->path() + "/testfile.txt";
    m_testSourceFile = m_dataDir->path() + "/tst_qfile.cpp";
    m_resourcesDir = m_dataDir->path() + "/resources";
#endif
    m_noEndOfLineFile = QFINDTESTDATA("noendofline.txt");
    QVERIFY(!m_noEndOfLineFile.isEmpty());

    QVERIFY(QDir::setCurrent(m_temporaryDir.path()));

    // create a file and make it read-only
    QFile file(QString::fromLatin1(readOnlyFile));
    QVERIFY2(file.open(QFile::WriteOnly), msgOpenFailed(file).constData());
    file.write("a", 1);
    file.close();
    QVERIFY2(file.setPermissions(QFile::ReadOwner), qPrintable(file.errorString()));
    // create another file and make it not readable
    file.setFileName(QString::fromLatin1(noReadFile));
    QVERIFY2(file.open(QFile::WriteOnly), msgOpenFailed(file).constData());
    file.write("b", 1);
    file.close();
#ifndef Q_OS_WIN // Not supported on Windows.
    QVERIFY2(file.setPermissions({ }), qPrintable(file.errorString()));
#else
    QVERIFY2(file.open(QFile::WriteOnly), msgOpenFailed(file).constData());
#endif
}

void tst_QFile::cleanupTestCase()
{
    QFile file(QString::fromLatin1(readOnlyFile));
    QVERIFY(file.setPermissions(QFile::ReadOwner | QFile::WriteOwner));
    file.setFileName(QString::fromLatin1(noReadFile));
    QVERIFY(file.setPermissions(QFile::ReadOwner | QFile::WriteOwner));
    QVERIFY(QDir::setCurrent(m_oldDir)); //release test directory for removal
}

//------------------------------------------
// 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( m_testFile );
    QVERIFY2(f.exists(), msgFileDoesNotExist(m_testFile));

    QFile file("nobodyhassuchafile");
    file.remove();
    QVERIFY(!file.exists());

    QFile file2("nobodyhassuchafile");
    QVERIFY2(file2.open(QIODevice::WriteOnly), msgOpenFailed(file2).constData());
    file2.close();

    QVERIFY(file.exists());

    QVERIFY2(file.open(QIODevice::WriteOnly), msgOpenFailed(file).constData());
    file.close();
    QVERIFY(file.exists());

    file.remove();
    QVERIFY(!file.exists());

#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
    const QString uncPath = "//" + QtNetworkSettings::winServerName() + "/testshare/readme.txt";
    QFile unc(uncPath);
    QVERIFY2(unc.exists(), msgFileDoesNotExist(uncPath).constData());
#endif

    QTest::ignoreMessage(QtWarningMsg, "Broken filename passed to function");
    QVERIFY(!QFile::exists(QDir::currentPath() + QLatin1Char('/') +
                           QChar(QChar::Null) + QLatin1String("x/y")));
}

void tst_QFile::open_data()
{
    QTest::addColumn<QString>("filename");
    QTest::addColumn<int>("mode");
    QTest::addColumn<bool>("ok");
    QTest::addColumn<QFile::FileError>("status");

    QTest::newRow( "exist_readOnly"  )
        << m_testFile << int(QIODevice::ReadOnly)
        << true << QFile::NoError;

    QTest::newRow( "exist_writeOnly" )
        << QString::fromLatin1(readOnlyFile)
        << int(QIODevice::WriteOnly)
        << false
        << QFile::OpenError;

    QTest::newRow( "exist_append"    )
        << QString::fromLatin1(readOnlyFile) << int(QIODevice::Append)
        << false << QFile::OpenError;

    QTest::newRow( "nonexist_readOnly"  )
        << QString("nonExist.txt") << int(QIODevice::ReadOnly)
        << false << QFile::OpenError;

    QTest::newRow("emptyfile")
        << QString("")
        << int(QIODevice::ReadOnly)
        << false
        << QFile::OpenError;

    QTest::newRow("nullfile") << QString() << int(QIODevice::ReadOnly) << false
        << QFile::OpenError;

    QTest::newRow("two-dots") << m_twoDotsFile << int(QIODevice::ReadOnly) << true
        << QFile::NoError;

    QTest::newRow("readonlyfile") << QString::fromLatin1(readOnlyFile) << int(QIODevice::WriteOnly)
                                  << false << QFile::OpenError;
    QTest::newRow("noreadfile") << QString::fromLatin1(noReadFile) << int(QIODevice::ReadOnly)
                                << false << QFile::OpenError;
    QTest::newRow("resource_file") << QString::fromLatin1(":/does/not/exist")
                                   << int(QIODevice::ReadOnly)
                                   << false
                                   << QFile::OpenError;
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
    //opening devices requires administrative privileges (and elevation).
    HANDLE hTest = CreateFile(_T("\\\\.\\PhysicalDrive0"), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    if (hTest != INVALID_HANDLE_VALUE) {
        CloseHandle(hTest);
        QTest::newRow("//./PhysicalDrive0") << QString("//./PhysicalDrive0") << int(QIODevice::ReadOnly)
                                            << true << QFile::NoError;
    } else {
        QTest::newRow("//./PhysicalDrive0") << QString("//./PhysicalDrive0") << int(QIODevice::ReadOnly)
                                            << false << QFile::OpenError;
    }
    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_UNIX) && !defined(Q_OS_VXWORKS)
    if (::getuid() == 0)
        // root and Chuck Norris don't care for file permissions. Skip.
        QSKIP("Running this test as root doesn't make sense");
#endif

#if defined(Q_OS_WIN32) || defined(Q_OS_WINRT)
    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");

    const QIODevice::OpenMode om(mode);
    const bool succeeded = f.open(om);
    if (ok)
        QVERIFY2(succeeded, msgOpenFailed(om, f).constData());
    else
        QVERIFY(!succeeded);

    QTEST( f.error(), "status" );
}

void tst_QFile::openUnbuffered()
{
    QFile file(m_testFile);
    QVERIFY2(file.open(QIODevice::ReadOnly | QIODevice::Unbuffered), msgOpenFailed(file).constData());
    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<QString>("filename");
    QTest::addColumn<qint64>("size");

    QTest::newRow( "exist01" ) << m_testFile << (qint64)245;
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
    // 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 );

    {
        QFile f( filename );
        QCOMPARE( f.size(), size );

        QVERIFY2(f.open(QIODevice::ReadOnly), msgOpenFailed(f).constData());
        QCOMPARE( f.size(), size );
    }

    {
        StdioFileGuard stream(QT_FOPEN(filename.toLocal8Bit().constData(), "rb"));
        QVERIFY( stream );
        QFile f;
        QVERIFY( f.open(stream, QIODevice::ReadOnly) );
        QCOMPARE( f.size(), size );

        f.close();
    }

    {
        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 file("newfile.txt");
    QVERIFY2(file.open(QIODevice::WriteOnly), msgOpenFailed(file).constData());
    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();
}

void tst_QFile::setSize()
{
    QFile f("createme.txt");
    QVERIFY2(f.open(QIODevice::Truncate | QIODevice::ReadWrite), msgOpenFailed(f).constData());
    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 f("setsizeseek.txt");
    QVERIFY2(f.open(QFile::WriteOnly), msgOpenFailed(f).constData());
    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( m_testFile );
    QVERIFY2(f.open(QFile::ReadOnly), msgOpenFailed(f).constData());

    int size = f.size();
    f.seek( size );

    bool end = f.atEnd();
    f.close();
    QVERIFY(end);
}

void tst_QFile::readLine()
{
    QFile f( m_testFile );
    QVERIFY2(f.open(QFile::ReadOnly), msgOpenFailed(f).constData());

    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( m_testFile );
    QVERIFY2(f.open(QFile::ReadOnly), msgOpenFailed(f).constData());


    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");
    QVERIFY2(file.open(QFile::ReadWrite), msgOpenFailed(file).constData());
    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<bool>("textMode");
    QTest::addColumn<QString>("fileName");
    QTest::newRow( "TextMode unixfile" ) <<  true << m_testFile;
    QTest::newRow( "BinaryMode unixfile" ) <<  false << m_testFile;
    QTest::newRow( "TextMode dosfile" ) <<  true << m_dosFile;
    QTest::newRow( "BinaryMode dosfile" ) <<  false << m_dosFile;
    QTest::newRow( "TextMode bigfile" ) <<  true << m_testSourceFile;
    QTest::newRow( "BinaryMode  bigfile" ) <<  false << m_testSourceFile;
    QVERIFY(QFile(m_testSourceFile).size() > 64*1024);
}

void tst_QFile::readAll()
{
    QFETCH( bool, textMode );
    QFETCH( QString, fileName );

    QFile file(fileName);
    const QIODevice::OpenMode om = textMode ? (QFile::Text | QFile::ReadOnly) : QFile::ReadOnly;
    QVERIFY2(file.open(om), msgOpenFailed(om, file).constData());

    QByteArray a = file.readAll();
    file.reset();
    QCOMPARE(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.");

    QVERIFY2(writer.open(QIODevice::ReadWrite | QIODevice::Unbuffered), msgOpenFailed(writer).constData());
    QVERIFY2(reader.open(QIODevice::ReadOnly), msgOpenFailed(reader).constData());

    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);
}

#if QT_CONFIG(process)
class StdinReaderProcessGuard { // Ensure the stdin reader process is stopped on destruction.
    Q_DISABLE_COPY(StdinReaderProcessGuard)

public:
    StdinReaderProcessGuard(QProcess *p) : m_process(p) {}
    ~StdinReaderProcessGuard() { stop(); }

    bool stop(int msecs = 30000)
    {
        if (m_process->state() != QProcess::Running)
            return true;
        m_process->closeWriteChannel();
        if (m_process->waitForFinished(msecs))
            return m_process->exitStatus() == QProcess::NormalExit && !m_process->exitCode();
        m_process->terminate();
        if (!m_process->waitForFinished())
            m_process->kill();
        return false;
    }

private:
    QProcess *m_process;
};
#endif // QT_CONFIG(process)

void tst_QFile::readAllStdin()
{
#if !QT_CONFIG(process)
    QSKIP("No qprocess support", SkipAll);
#else
#if defined(Q_OS_ANDROID)
    QSKIP("This test crashes when doing nanosleep. See QTBUG-69034.");
#endif
    QByteArray lotsOfData(1024, '@'); // 10 megs

    QProcess process;
    StdinReaderProcessGuard processGuard(&process);
    process.start(m_stdinProcess, QStringList(QStringLiteral("all")));
    QVERIFY2(process.waitForStarted(), qPrintable(process.errorString()));
    for (int i = 0; i < 5; ++i) {
        QTest::qWait(1000);
        process.write(lotsOfData);
        while (process.bytesToWrite() > 0)
            QVERIFY(process.waitForBytesWritten());
    }

    QVERIFY(processGuard.stop());
    QCOMPARE(process.readAll().size(), lotsOfData.size() * 5);
#endif
}

void tst_QFile::readLineStdin()
{
#if !QT_CONFIG(process)
    QSKIP("No qprocess support", SkipAll);
#else
#if defined(Q_OS_ANDROID)
    QSKIP("This test crashes when doing nanosleep. See QTBUG-69034.");
#endif
    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;
        StdinReaderProcessGuard processGuard(&process);
        process.start(m_stdinProcess,
                      QStringList() << QStringLiteral("line") << QString::number(i),
                      QIODevice::Text | QIODevice::ReadWrite);
        QVERIFY2(process.waitForStarted(), qPrintable(process.errorString()));
        for (int i = 0; i < 5; ++i) {
            QTest::qWait(1000);
            process.write(lotsOfData);
            while (process.bytesToWrite() > 0)
                QVERIFY(process.waitForBytesWritten());
        }

        QVERIFY(processGuard.stop(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 !QT_CONFIG(process)
    QSKIP("No qprocess support", SkipAll);
#else
#if defined(Q_OS_ANDROID)
    QSKIP("This test crashes when calling ::poll. See QTBUG-69034.");
#endif
    for (int i = 0; i < 2; ++i) {
        QProcess process;
        StdinReaderProcessGuard processGuard(&process);
        process.start(m_stdinProcess,
                      QStringList() << QStringLiteral("line") << QString::number(i),
                      QIODevice::Text | QIODevice::ReadWrite);
        QVERIFY2(process.waitForStarted(), qPrintable(process.errorString()));

        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);
        }

        QVERIFY(processGuard.stop(5000));
    }
#endif
}

void tst_QFile::text()
{
    // dosfile.txt is a binary CRLF file
    QFile file(m_dosFile);
    QVERIFY2(file.open(QFile::Text | QFile::ReadOnly), msgOpenFailed(file).constData());
    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(m_noEndOfLineFile);
    QVERIFY2(file.open(QFile::ReadOnly), msgOpenFailed(file).constData());

    int nlines = 0;
    while (!file.atEnd()) {
        ++nlines;
        file.readLine();
    }

    QCOMPARE(nlines, 3);
}

void tst_QFile::readBlock()
{
    QFile f( m_testFile );
    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( m_testFile );
    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(m_testFile);
    QVERIFY2(f.open(QFile::ReadOnly), msgOpenFailed(f).constData());

    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");
    QVERIFY2(out.open(QIODevice::ReadWrite), msgOpenFailed(out).constData());
    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');
}

#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
QString driveLetters()
{
    wchar_t volumeName[MAX_PATH];
    wchar_t path[MAX_PATH];
    const HANDLE h = FindFirstVolumeW(volumeName, MAX_PATH);
    if (h == INVALID_HANDLE_VALUE)
        return QString();
    QString result;
    do {
        if (GetVolumePathNamesForVolumeNameW(volumeName, path, MAX_PATH, NULL)) {
            if (path[1] == L':')
                result.append(QChar(path[0]));
        }
    } while (FindNextVolumeW(h, volumeName, MAX_PATH));
    FindVolumeClose(h);
    return result;
}

static inline QChar invalidDriveLetter()
{
    const QString drives = driveLetters().toLower();
    for (char c = 'a'; c <= 'z'; ++c)
        if (!drives.contains(QLatin1Char(c)))
            return QLatin1Char(c);
    Q_ASSERT(false); // All drive letters used?!
    return QChar();
}

#endif // Q_OS_WIN

void tst_QFile::invalidFile_data()
{
    QTest::addColumn<QString>("fileName");
#if !defined(Q_OS_WIN)
    QTest::newRow( "x11" ) << QString( "qwe//" );
#else
#if !defined(Q_OS_WINRT)
    QTest::newRow( "colon2" ) << invalidDriveLetter() + QString::fromLatin1(":ail:invalid");
#endif
    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( "fail<invalid" );
    QTest::newRow( "gt" ) << QString( "fail>invalid" );
    QTest::newRow( "pipe" ) << QString( "fail|invalid" );
#endif
}

void tst_QFile::invalidFile()
{
    QFETCH( QString, fileName );
    QFile f( fileName );
    QVERIFY2( !f.open( QIODevice::ReadWrite ), qPrintable(fileName) );
}

void tst_QFile::createFile()
{
    if ( QFile::exists( "createme.txt" ) )
        QFile::remove( "createme.txt" );
    QVERIFY( !QFile::exists( "createme.txt" ) );

    QFile f( "createme.txt" );
    QVERIFY2( f.open(QIODevice::WriteOnly), msgOpenFailed(f).constData());
    f.close();
    QVERIFY( QFile::exists( "createme.txt" ) );
}

void tst_QFile::createFileNewOnly()
{
    QFile::remove("createme.txt");
    QVERIFY(!QFile::exists("createme.txt"));

    QFile f("createme.txt");
    QVERIFY2(f.open(QIODevice::NewOnly), msgOpenFailed(f).constData());
    f.close();
    QVERIFY(QFile::exists("createme.txt"));

    QVERIFY(!f.open(QIODevice::NewOnly));
    QVERIFY(QFile::exists("createme.txt"));
    QFile::remove("createme.txt");
}

void tst_QFile::openFileExistingOnly()
{
    QFile::remove("dontcreateme.txt");
    QVERIFY(!QFile::exists("dontcreateme.txt"));

    QFile f("dontcreateme.txt");
    QVERIFY(!f.open(QIODevice::ExistingOnly | QIODevice::ReadOnly));
    QVERIFY(!f.open(QIODevice::ExistingOnly | QIODevice::WriteOnly));
    QVERIFY(!f.open(QIODevice::ExistingOnly | QIODevice::ReadWrite));
    QVERIFY(!f.open(QIODevice::ExistingOnly));
    QVERIFY(!QFile::exists("dontcreateme.txt"));

    QVERIFY2(f.open(QIODevice::NewOnly), msgOpenFailed(f).constData());
    f.close();
    QVERIFY(QFile::exists("dontcreateme.txt"));

    QVERIFY2(f.open(QIODevice::ExistingOnly | QIODevice::ReadOnly), msgOpenFailed(f).constData());
    f.close();
    QVERIFY2(f.open(QIODevice::ExistingOnly | QIODevice::WriteOnly), msgOpenFailed(f).constData());
    f.close();
    QVERIFY2(f.open(QIODevice::ExistingOnly | QIODevice::ReadWrite), msgOpenFailed(f).constData());
    f.close();
    QVERIFY(!f.open(QIODevice::ExistingOnly));
    QVERIFY(QFile::exists("dontcreateme.txt"));
    QFile::remove("dontcreateme.txt");
}

void tst_QFile::append()
{
    const QString name("appendme.txt");
    if (QFile::exists(name))
        QFile::remove(name);
    QVERIFY(!QFile::exists(name));

    QFile f(name);
    QVERIFY2(f.open(QIODevice::WriteOnly | QIODevice::Truncate), msgOpenFailed(f).constData());
    f.putChar('a');
    f.close();

    QVERIFY2(f.open(QIODevice::Append), msgOpenFailed(f).constData());
    QCOMPARE(f.pos(), 1);
    f.putChar('a');
    f.close();
    QCOMPARE(int(f.size()), 2);

    QVERIFY2(f.open(QIODevice::Append | QIODevice::Truncate), msgOpenFailed(f).constData());
    QCOMPARE(f.pos(), 0);
    f.putChar('a');
    f.close();
    QCOMPARE(int(f.size()), 1);
}

void tst_QFile::permissions_data()
{
    QTest::addColumn<QString>("file");
    QTest::addColumn<uint>("perms");
    QTest::addColumn<bool>("expected");
    QTest::addColumn<bool>("create");

    QTest::newRow("data0") << QCoreApplication::instance()->applicationFilePath() << uint(QFile::ExeUser) << true << false;
    QTest::newRow("data1") << m_testSourceFile << uint(QFile::ReadUser) << true << false;
    QTest::newRow("readonly") << QString::fromLatin1("readonlyfile") << uint(QFile::WriteUser) << false << false;
    QTest::newRow("longfile") << QString::fromLatin1("longFileNamelongFileNamelongFileNamelongFileName"
                                                    "longFileNamelongFileNamelongFileNamelongFileName"
                                                    "longFileNamelongFileNamelongFileNamelongFileName"
                                                    "longFileNamelongFileNamelongFileNamelongFileName"
                                                    "longFileNamelongFileNamelongFileNamelongFileName.txt") << uint(QFile::ReadUser) << true << true;
    QTest::newRow("resource1") << ":/tst_qfileinfo/resources/file1.ext1" << uint(QFile::ReadUser) << true << false;
    QTest::newRow("resource2") << ":/tst_qfileinfo/resources/file1.ext1" << uint(QFile::WriteUser) << false << false;
    QTest::newRow("resource3") << ":/tst_qfileinfo/resources/file1.ext1" << uint(QFile::ExeUser) << false << false;
}

void tst_QFile::permissions()
{
    QFETCH(QString, file);
    QFETCH(uint, perms);
    QFETCH(bool, expected);
    QFETCH(bool, create);
    if (create) {
        QFile fc(file);
        QVERIFY2(fc.open(QFile::WriteOnly), msgOpenFailed(fc).constData());
        QVERIFY(fc.write("hello\n"));
        fc.close();
    }

    QFile f(file);
    QFile::Permissions memberResult = f.permissions() & perms;
    QFile::Permissions staticResult = QFile::permissions(file) & perms;

    if (create) {
        QFile::remove(file);
    }

#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
    if (qt_ntfs_permission_lookup)
        QEXPECT_FAIL("readonly", "QTBUG-25630", Abort);
#endif
#ifdef Q_OS_UNIX
    if (strcmp(QTest::currentDataTag(), "readonly") == 0) {
        // in case accidentally run as root
        if (::getuid() == 0)
            QSKIP("Running this test as root doesn't make sense");
    }
#endif
    QCOMPARE((memberResult == QFile::Permissions(perms)), expected);
    QCOMPARE((staticResult == QFile::Permissions(perms)), expected);
}

#ifdef Q_OS_WIN
void tst_QFile::permissionsNtfs_data()
{
    permissions_data();
}

void tst_QFile::permissionsNtfs()
{
    QScopedValueRollback<int> ntfsMode(qt_ntfs_permission_lookup);
    qt_ntfs_permission_lookup++;
    permissions();
}
#endif

void tst_QFile::setPermissions()
{
    if ( QFile::exists( "createme.txt" ) )
        QFile::remove( "createme.txt" );
    QVERIFY( !QFile::exists( "createme.txt" ) );

    QFile f("createme.txt");
    QVERIFY2(f.open(QIODevice::WriteOnly | QIODevice::Truncate), msgOpenFailed(f).constData());
    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(m_testSourceFile, "tst_qfile_copy.cpp"));
    QFile in1(m_testSourceFile), in2("tst_qfile_copy.cpp");
    QVERIFY2(in1.open(QFile::ReadOnly), msgOpenFailed(in1).constData());
    QVERIFY2(in2.open(QFile::ReadOnly), msgOpenFailed(in2).constData());
    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");

    QVERIFY2(file1.open(QIODevice::ReadWrite), msgOpenFailed(file1).constData());
    QVERIFY2(file2.open(QIODevice::ReadWrite), msgOpenFailed(file1).constData());
    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"));
}

void tst_QFile::copyRemovesTemporaryFile() const
{
    const QString newName(QLatin1String("copyRemovesTemporaryFile"));
    QVERIFY(QFile::copy(m_forCopyingFile, newName));

    QVERIFY(!QFile::exists(QStringLiteral("qt_temp.XXXXXX")));
}

void tst_QFile::copyShouldntOverwrite()
{
    // Copy should not overwrite existing files.
    QFile::remove("tst_qfile.cpy");
    QFile file(m_testSourceFile);
    QVERIFY(file.copy("tst_qfile.cpy"));

    bool ok = QFile::setPermissions("tst_qfile.cpy", QFile::WriteOther);
    QVERIFY(ok);
    QVERIFY(!file.copy("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());

     // Need to reset permissions on Windows to be able to delete
    QVERIFY(QFile::setPermissions("file-copy-destination.txt",
           QFile::ReadOwner | QFile::WriteOwner));
    QVERIFY(QFile::remove("file-copy-destination.txt"));

    // Fallback copy of open file.
    QVERIFY2(file.open(QIODevice::ReadOnly), msgOpenFailed(file).constData());
    QVERIFY(file.copy("file-copy-destination.txt"));
    QVERIFY(QFile::exists("file-copy-destination.txt"));
    QVERIFY(!file.isOpen());

    file.close();
    QFile::setPermissions("file-copy-destination.txt",
            QFile::ReadOwner | QFile::WriteOwner);
}

#ifdef Q_OS_WIN
#include <objbase.h>
#include <shlobj.h>
#endif

#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
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

#ifndef Q_OS_WINRT
void tst_QFile::link()
{
    QFile::remove("myLink.lnk");

    QFileInfo info1(m_testSourceFile);
    QString referenceTarget = QDir::cleanPath(info1.absoluteFilePath());

    QVERIFY(QFile::link(m_testSourceFile, "myLink.lnk"));

    QFileInfo info2("myLink.lnk");
    QVERIFY(info2.isSymLink());
    QCOMPARE(info2.symLinkTarget(), referenceTarget);

    QFile link("myLink.lnk");
    QVERIFY2(link.open(QIODevice::ReadOnly), msgOpenFailed(link).constData());
    QCOMPARE(link.symLinkTarget(), referenceTarget);
    link.close();

    QCOMPARE(QFile::symLinkTarget("myLink.lnk"), referenceTarget);

#if defined(Q_OS_WIN)
    QString wd = getWorkingDirectoryForLink(info2.absoluteFilePath());
    QCOMPARE(QDir::fromNativeSeparators(wd), QDir::cleanPath(info1.absolutePath()));
#endif
}

void tst_QFile::linkToDir()
{
    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()));
}

void tst_QFile::absolutePathLinkToRelativePath()
{
    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", Continue);
    QCOMPARE(QFileInfo(QFile(QFileInfo("myDir/myLink.lnk").absoluteFilePath()).symLinkTarget()).absoluteFilePath(),
             QFileInfo("myDir/test.txt").absoluteFilePath());
}

void tst_QFile::readBrokenLink()
{
    QFile::remove("myLink2.lnk");
    QFileInfo info1("file12");
    QVERIFY(QFile::link("file12", "myLink2.lnk"));
    QFileInfo info2("myLink2.lnk");
    QVERIFY(info2.isSymLink());
    QCOMPARE(info2.symLinkTarget(), info1.absoluteFilePath());
    QVERIFY(QFile::remove(info2.absoluteFilePath()));
    QVERIFY(QFile::link("ole/..", "myLink2.lnk"));
    QCOMPARE(QFileInfo("myLink2.lnk").symLinkTarget(), QDir::currentPath());
}
#endif // Q_OS_WINRT

void tst_QFile::readTextFile_data()
{
    QTest::addColumn<QByteArray>("in");
    QTest::addColumn<QByteArray>("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");
    QVERIFY2(winfile.open(QFile::WriteOnly | QFile::Truncate), msgOpenFailed(winfile).constData());
    winfile.write(in);
    winfile.close();

    QVERIFY2(winfile.open(QFile::ReadOnly), msgOpenFailed(winfile).constData());
    QCOMPARE(winfile.readAll(), in);
    winfile.close();

    QVERIFY2(winfile.open(QFile::ReadOnly | QFile::Text), msgOpenFailed(winfile).constData());
    QCOMPARE(winfile.readAll(), out);
}

void tst_QFile::readTextFile2()
{
    {
        QFile file(m_testLogFile);
        QVERIFY2(file.open(QIODevice::ReadOnly), msgOpenFailed(file).constData());
        file.read(4097);
    }

    {
        QFile file(m_testLogFile);
        QVERIFY2(file.open(QIODevice::ReadOnly | QIODevice::Text), msgOpenFailed(file).constData());
        file.read(4097);
    }
}

void tst_QFile::writeTextFile_data()
{
    QTest::addColumn<QByteArray>("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");
    QVERIFY2(file.open(QFile::WriteOnly | QFile::Truncate | QFile::Text),
             msgOpenFailed(file).constData());
    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_WINRT)
// Helper for executing QFile::open() with warning in QTRY_VERIFY(), which evaluates the condition
// multiple times
static bool qFileOpen(QFile &file, QIODevice::OpenMode ioFlags)
{
    const bool result = file.isOpen() || file.open(ioFlags);
    if (!result)
        qWarning() << "Cannot open" << file.fileName() << ':' << file.errorString();
    return result;
}

// Helper for executing fopen() with warning in QTRY_VERIFY(), which evaluates the condition
// multiple times
static bool fOpen(const QByteArray &fileName, const char *mode, FILE **file)
{
    if (*file == nullptr)
        *file = fopen(fileName.constData(), mode);
    if (*file == nullptr)
        qWarning("Cannot open %s: %s", fileName.constData(), strerror(errno));
    return *file != nullptr;
}

void tst_QFile::largeUncFileSupport()
{
    // Currently there is a single network test server that is used by all VMs running tests in
    // the CI. This test accesses a file shared with Samba on that server. Unfortunately many
    // clients accessing the file at the same time is a sharing violation. This test already
    // attempted to deal with the problem with retries, but that has led to the test timing out,
    // not eventually succeeding. Due to the timeouts blacklisting the test wouldn't help.
    // See https://bugreports.qt.io/browse/QTQAINFRA-1727 which will be resolved by the new
    // test server architecture where the server is no longer shared.
    QSKIP("Multiple instances of running this test at the same time fail due to QTQAINFRA-1727");

    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");
    const QByteArray largeFileEncoded = QFile::encodeName(largeFile);

    {
        // 1) Native file handling.
        QFile file(largeFile);
        QVERIFY2(file.exists(), msgFileDoesNotExist(largeFile));

        QCOMPARE(file.size(), size);
        // Retry in case of sharing violation
        QTRY_VERIFY2(qFileOpen(file, QIODevice::ReadOnly), msgOpenFailed(file).constData());
        QCOMPARE(file.size(), size);
        QVERIFY(file.seek(dataOffset));
        QCOMPARE(file.read(knownData.size()), knownData);
    }
    {
        // 2) stdlib file handling.
        FILE *fhF = nullptr;
        // Retry in case of sharing violation
        QTRY_VERIFY(fOpen(largeFileEncoded, "rb", &fhF));
        StdioFileGuard fh(fhF);
        QFile file;
        QVERIFY(file.open(fh, QIODevice::ReadOnly));
        QCOMPARE(file.size(), size);
        QVERIFY(file.seek(dataOffset));
        QCOMPARE(file.read(knownData.size()), knownData);
    }
    {
        // 3) stdio file handling.
        FILE *fhF = nullptr;
        // Retry in case of sharing violation
        QTRY_VERIFY(fOpen(largeFileEncoded, "rb", &fhF));
        StdioFileGuard fh(fhF);
        int fd = int(_fileno(fh));
        QFile file;
        QVERIFY(file.open(fd, QIODevice::ReadOnly));
        QCOMPARE(file.size(), size);
        QVERIFY(file.seek(dataOffset));
        QCOMPARE(file.read(knownData.size()), knownData);
    }
}
#endif

void tst_QFile::flush()
{
    QString fileName("stdfile.txt");

    QFile::remove(fileName);

    {
        QFile file(fileName);
        QVERIFY2(file.open(QFile::WriteOnly), msgOpenFailed(file).constData());
        QCOMPARE(file.write("abc", 3),qint64(3));
    }

    {
        QFile file(fileName);
        QVERIFY2(file.open(QFile::WriteOnly | QFile::Append), msgOpenFailed(file).constData());
        QCOMPARE(file.pos(), qlonglong(3));
        QCOMPARE(file.write("def", 3), qlonglong(3));
        QCOMPARE(file.pos(), qlonglong(6));
    }

    {
        QFile file("stdfile.txt");
        QVERIFY2(file.open(QFile::ReadOnly), msgOpenFailed(file).constData());
        QCOMPARE(file.readAll(), QByteArray("abcdef"));
    }
}

void tst_QFile::bufferedRead()
{
    QFile::remove("stdfile.txt");

    QFile file("stdfile.txt");
    QVERIFY2(file.open(QFile::WriteOnly), msgOpenFailed(file).constData());
    file.write("abcdef");
    file.close();

    StdioFileGuard stdFile(fopen("stdfile.txt", "r"));
    QVERIFY(stdFile);
    char c;
    QCOMPARE(int(fread(&c, 1, 1, stdFile)), 1);
    QCOMPARE(c, 'a');
    QCOMPARE(int(ftell(stdFile)), 1);

    {
        QFile file;
        QVERIFY2(file.open(stdFile, QFile::ReadOnly), msgOpenFailed(file).constData());
        QCOMPARE(file.pos(), qlonglong(1));
        QCOMPARE(file.read(&c, 1), qlonglong(1));
        QCOMPARE(c, 'b');
        QCOMPARE(file.pos(), qlonglong(2));
    }
}

#ifdef Q_OS_UNIX
void tst_QFile::isSequential()
{
    QFile zero("/dev/null");
    QVERIFY2(zero.open(QFile::ReadOnly), msgOpenFailed(zero).constData());
    QVERIFY(zero.isSequential());
}
#endif

void tst_QFile::encodeName()
{
    QCOMPARE(QFile::encodeName(QString()), QByteArray());
}

void tst_QFile::truncate()
{
    const QIODevice::OpenModeFlag modes[] = { QFile::ReadWrite, QIODevice::WriteOnly, QIODevice::Append };
    for (auto mode : modes) {
        QFile file("truncate.txt");
        QVERIFY2(file.open(QFile::WriteOnly), msgOpenFailed(file).constData());
        file.write(QByteArray(200, '@'));
        file.close();

        QVERIFY2(file.open(mode | QFile::Truncate), msgOpenFailed(file).constData());
        file.write(QByteArray(100, '$'));
        file.close();

        QVERIFY2(file.open(QFile::ReadOnly), msgOpenFailed(file).constData());
        QCOMPARE(file.readAll(), QByteArray(100, '$'));
    }
}

void tst_QFile::seekToPos()
{
    {
        QFile file("seekToPos.txt");
        QVERIFY2(file.open(QFile::WriteOnly), msgOpenFailed(file).constData());
        file.write("a\r\nb\r\nc\r\n");
        file.flush();
    }

    QFile file("seekToPos.txt");
    QVERIFY2(file.open(QFile::ReadOnly | QFile::Text), msgOpenFailed(file).constData());
    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);
        QVERIFY2(file.open(QFile::WriteOnly), msgOpenFailed(file).constData());
        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);
    QVERIFY2(file.open(QFile::ReadOnly), msgOpenFailed(file).constData());
    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();
}

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");
        QVERIFY2(f.open(QFile::WriteOnly), msgOpenFailed(f).constData());
        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();
    }

    StdioFileGuard fp(fopen("FILEReadWrite.txt", "r+b"));
    QVERIFY(fp);
    QFile file;
    QVERIFY2(file.open(fp, QFile::ReadWrite), msgOpenFailed(file).constData());
    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();
    fp.close();

    // check modified file
    {
        QFile f("FILEReadWrite.txt");
        QVERIFY2(f.open(QFile::ReadOnly), msgOpenFailed(file).constData());
        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();
    }
}


/*
#include <qglobal.h>
#define BUFFSIZE 1
#define FILESIZE   0x10000000f
void tst_QFile::largeFileSupport()
{
#ifdef Q_OS_SOLARIS
    QSKIP("Solaris does not support statfs");
#else
    qlonglong sizeNeeded = 2147483647;
    sizeNeeded *= 2;
    sizeNeeded += 1024;
    qlonglong freespace = qlonglong(0);
#ifdef Q_OS_WIN
    _ULARGE_INTEGER free;
    if (::GetDiskFreeSpaceEx((wchar_t*)QDir::currentPath().utf16(), &free, 0, 0))
        freespace = free.QuadPart;
    if (freespace != 0) {
#else
    struct statfs info;
    if (statfs(const_cast<char *>(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<BUFFSIZE; i++)
                        QCOMPARE(c[i], 'a');
                    QVERIFY(bigFile.seek(sizeNeeded - BUFFSIZE));
                    QVERIFY(bigFile.read(c, BUFFSIZE) == BUFFSIZE);
                    for (i=0; i<BUFFSIZE; i++)
                        QCOMPARE(c[i], 'a');
                    bigFile.close();
                    QVERIFY(bigFile.remove());
                } else {
                    QVERIFY(bigFile.remove());
                    QFAIL("Could not reopen file");
                }
            } else {
                QFAIL("Could not open file");
            }
        } else {
            QSKIP("Not enough space to run test");
        }
    } else {
        QFAIL("Could not determin disk space");
    }
#endif
}
*/

void tst_QFile::i18nFileName_data()
{
    QTest::addColumn<QString>("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);
        QVERIFY2(file.open(QFile::WriteOnly | QFile::Text), msgOpenFailed(file).constData());
        QTextStream ts(&file);
        ts.setCodec("UTF-8");
        ts << fileName << Qt::endl;
     }
     {
        QFile file(fileName);
        QVERIFY2(file.open(QFile::ReadOnly | QFile::Text), msgOpenFailed(file).constData());
        QTextStream ts(&file);
        ts.setCodec("UTF-8");
        QString line = ts.readLine();
        QCOMPARE(line, fileName);
     }
}


void tst_QFile::longFileName_data()
{
    QTest::addColumn<QString>("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);
        QVERIFY2(file.open(QFile::WriteOnly | QFile::Text), msgOpenFailed(file).constData());
        QTextStream ts(&file);
        ts << fileName << Qt::endl;
    }
    {
        QFile file(fileName);
        QVERIFY2(file.open(QFile::ReadOnly | QFile::Text), msgOpenFailed(file).constData());
        QTextStream ts(&file);
        QString line = ts.readLine();
        QCOMPARE(line, fileName);
    }
    QString newName = fileName + QLatin1Char('1');
    {
        QVERIFY(QFile::copy(fileName, newName));
        QFile file(newName);
        QVERIFY2(file.open(QFile::ReadOnly | QFile::Text), msgOpenFailed(file).constData());
        QTextStream ts(&file);
        QString line = ts.readLine();
        QCOMPARE(line, fileName);

    }
    QVERIFY(QFile::remove(newName));
    {
        QVERIFY(QFile::rename(fileName, newName));
        QFile file(newName);
        QVERIFY2(file.open(QFile::ReadOnly | QFile::Text), msgOpenFailed(file).constData());
        QTextStream ts(&file);
        QString line = ts.readLine();
        QCOMPARE(line, fileName);
    }
    QVERIFY2(QFile::exists(newName), msgFileDoesNotExist(newName).constData());
}

#ifdef QT_BUILD_INTERNAL
class MyEngine : public QAbstractFileEngine
{
public:
    MyEngine(int n) { number = n; }
    virtual ~MyEngine() {}

    void setFileName(const QString &) {}
    bool open(QIODevice::OpenMode) { 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 { }; }
    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(); }
    bool setFileTime(const QDateTime &newDate, FileTime time)
    { Q_UNUSED(newDate) Q_UNUSED(time) return false; }

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);
    }
};
#endif

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

#ifdef QT_BUILD_INTERNAL
    // 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));
#endif
}

#ifdef QT_BUILD_INTERNAL
class MyRecursiveHandler : public QAbstractFileEngineHandler
{
public:
    inline QAbstractFileEngine *create(const QString &fileName) const
    {
        if (fileName.startsWith(":!")) {
            QDir dir;

#ifndef BUILTIN_TESTDATA
            const QString realFile = QFINDTESTDATA(fileName.mid(2));
#else
            const QString realFile = m_dataDir->filePath(fileName.mid(2));
#endif
            if (dir.exists(realFile))
                return new QFSFileEngine(realFile);
        }
        return 0;
    }

#ifdef BUILTIN_TESTDATA
    QSharedPointer<QTemporaryDir> m_dataDir;
#endif
};
#endif

#ifdef QT_BUILD_INTERNAL
void tst_QFile::useQFileInAFileHandler()
{
    // This test should not dead-lock
    MyRecursiveHandler handler;
#ifdef BUILTIN_TESTDATA
    handler.m_dataDir = m_dataDir;
#endif
    QFile file(":!tst_qfile.cpp");
    QVERIFY(file.exists());
}
#endif

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("testing that remove/exists work...");
    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("testing that remove closes the file first...");

        bool removed = f.remove(); // remove should both close and remove the file
        QVERIFY(removed);
        QVERIFY(!f.isOpen());
        QVERIFY(!f.exists());
        QCOMPARE(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("testing that remove closes the file first...");
            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 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());
        QCOMPARE(f.error(), QFile::NoError);
    }
}

void tst_QFile::fullDisk()
{
    QFile file("/dev/full");
    if (!file.exists())
        QSKIP("/dev/full doesn't exist on this system");

    QVERIFY2(file.open(QIODevice::WriteOnly), msgOpenFailed(file).constData());
    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:
    QVERIFY2(file.open(QIODevice::WriteOnly), msgOpenFailed(file).constData());
    file.write("foobar", 6);
    file.close();
    QVERIFY(file.error() != QFile::NoError);
}

void tst_QFile::writeLargeDataBlock_data()
{
    QTest::addColumn<QString>("fileName");
    QTest::addColumn<int>("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;

#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) && !defined(QT_NO_NETWORK)
    // 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_VXWORKS)
        int resizeSize = 1024 * 1024; // VxWorks does 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), msgOpenFailed(file));
        qint64 fileWriteOriginalData = file.write(originalData);
        qint64 originalDataSize      = (qint64)originalData.size();
#if defined(Q_OS_WIN)
        if (fileWriteOriginalData != originalDataSize) {
            qWarning() << qPrintable(QString("Error writing a large data block to [%1]: %2")
                .arg(fileName)
                .arg(file.errorString()));
            QEXPECT_FAIL("unc file", "QTBUG-26906 writing", Abort);
        }
#endif
        QCOMPARE( fileWriteOriginalData, originalDataSize );
        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();

#if defined(Q_OS_WIN)
        if (readData != originalData) {
            qWarning() << qPrintable(QString("Error reading a large data block from [%1]: %2")
                .arg(fileName)
                .arg(file.errorString()));
            QEXPECT_FAIL("unc file", "QTBUG-26906 reading", Abort);
        }
#endif
        closeFile(file);
    }
    QCOMPARE( readData, originalData );
    QVERIFY( QFile::remove(fileName) );
}

void tst_QFile::readFromWriteOnlyFile()
{
    QFile file("writeonlyfile");
    QVERIFY2(file.open(QFile::WriteOnly), msgOpenFailed(file).constData());
    char c;
    QTest::ignoreMessage(QtWarningMsg, "QIODevice::read (QFile, \"writeonlyfile\"): WriteOnly device");
    QCOMPARE(file.read(&c, 1), qint64(-1));
}

void tst_QFile::writeToReadOnlyFile()
{
    QFile file("readonlyfile");
    QVERIFY2(file.open(QFile::ReadOnly), msgOpenFailed(file).constData());
    char c = 0;
    QTest::ignoreMessage(QtWarningMsg, "QIODevice::write (QFile, \"readonlyfile\"): ReadOnly device");
    QCOMPARE(file.write(&c, 1), qint64(-1));
}

#if defined(Q_OS_LINUX) || defined(Q_OS_AIX) || defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
// This platform have 0-sized virtual files
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());
#else // defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
    fname = "/proc/curproc/map";
#endif

    // consistency check
    QFileInfo fi(fname);
    QVERIFY2(fi.exists(), msgFileDoesNotExist(fname).constData());
    QVERIFY(fi.isFile());
    QCOMPARE(fi.size(), Q_INT64_C(0));

    // open the file
    QFile f(fname);
    QVERIFY2(f.open(QIODevice::ReadOnly), msgOpenFailed(f).constData());
    if (EmulationDetector::isRunningArmOnX86())
        QEXPECT_FAIL("","QEMU does not read /proc/self/maps size correctly", Continue);
    QCOMPARE(f.size(), Q_INT64_C(0));
    if (EmulationDetector::isRunningArmOnX86())
        QEXPECT_FAIL("","QEMU does not read /proc/self/maps size correctly", Continue);
    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));
}
#endif

void tst_QFile::textFile()
{
    const char *openMode = QOperatingSystemVersion::current().type() != QOperatingSystemVersion::Windows
        ? "w" : "wt";
    StdioFileGuard fs(fopen("writeabletextfile", openMode));
    QVERIFY(fs);
    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();
    fs.close();

    QFile file("writeabletextfile");
    QVERIFY2(file.open(QIODevice::ReadOnly), msgOpenFailed(file).constData());

    QByteArray data = file.readAll();

    QByteArray expected = part1 + part2;
#ifdef Q_OS_WIN
    expected.replace("\n", "\015\012");
#endif
    QCOMPARE(data, expected);
    file.close();
}

static const char renameSourceFile[] = "renamefile";

void tst_QFile::rename_data()
{
    QTest::addColumn<QString>("source");
    QTest::addColumn<QString>("destination");
    QTest::addColumn<bool>("result");

    QTest::newRow("a -> b") << QString("a") << QString("b") << false;
    QTest::newRow("a -> .") << QString("a") << QString(".") << false;
    QTest::newRow("renamefile -> renamefile") << QString::fromLatin1(renameSourceFile) << QString::fromLatin1(renameSourceFile) << false;
    QTest::newRow("renamefile -> noreadfile") << QString::fromLatin1(renameSourceFile) << QString::fromLatin1(noReadFile) << false;
#if defined(Q_OS_UNIX)
    QTest::newRow("renamefile -> /etc/renamefile") << QString::fromLatin1(renameSourceFile) << QString("/etc/renamefile") << false;
#endif
    QTest::newRow("renamefile -> renamedfile") << QString::fromLatin1(renameSourceFile) << QString("renamedfile") << true;
    QTest::newRow("renamefile -> ..") << QString::fromLatin1(renameSourceFile) << QString("..") << false;
    QTest::newRow("renamefile -> rEnAmEfIlE") << QString::fromLatin1(renameSourceFile) << QStringLiteral("rEnAmEfIlE") << true;
}

void tst_QFile::rename()
{
    QFETCH(QString, source);
    QFETCH(QString, destination);
    QFETCH(bool, result);

    const QByteArray content = QByteArrayLiteral("testdatacontent") + QTime::currentTime().toString().toLatin1();

#if defined(Q_OS_UNIX)
    if (strcmp(QTest::currentDataTag(), "renamefile -> /etc/renamefile") == 0) {
#if !defined(Q_OS_VXWORKS)
        if (::getuid() == 0)
#endif
            QSKIP("Running this test as root doesn't make sense");
    }
#endif

    const QString sourceFileName = QString::fromLatin1(renameSourceFile);
    QFile sourceFile(sourceFileName);
    QVERIFY2(sourceFile.open(QFile::WriteOnly | QFile::Text), qPrintable(sourceFile.errorString()));
    QVERIFY2(sourceFile.write(content), qPrintable(sourceFile.errorString()));
    sourceFile.close();

    QFile file(source);
    const bool success = file.rename(destination);
    if (result) {
        QVERIFY2(success, qPrintable(file.errorString()));
        QCOMPARE(file.error(), QFile::NoError);
        // This will report the source file still existing for a rename changing the case
        // on Windows, Mac.
        if (sourceFileName.compare(destination, Qt::CaseInsensitive))
            QVERIFY(!sourceFile.exists());
        QFile destinationFile(destination);
        QVERIFY2(destinationFile.open(QFile::ReadOnly | QFile::Text), qPrintable(destinationFile.errorString()));
        QCOMPARE(destinationFile.readAll(), content);
        destinationFile.close();
    } else {
        QVERIFY(!success);
        QCOMPARE(file.error(), QFile::RenameError);
    }
}

/*!
 \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 \macos.

 Here, we reproduce that condition by having a QFile sub-class with this
 peculiar atEnd() behavior.
*/
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 = QStringLiteral("forRenaming.txt");
    // Copy from source tree
    if (!QFile::exists(originalName))
        QVERIFY(QFile::copy(m_forRenamingFile, originalName));

    PeculiarAtEnd file;
    file.setFileName(originalName);
    QVERIFY2(file.open(QIODevice::ReadOnly), qPrintable(file.errorString()));

    QVERIFY(file.rename(newName));

    file.close();
}

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");
    QVERIFY2(file.open(QIODevice::ReadWrite), msgOpenFailed(file).constData());
    QVERIFY2(file2.open(QIODevice::ReadWrite), msgOpenFailed(file2).constData());

    // 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()
{
    const QString fileName(QStringLiteral("appendfile.txt"));
    QFile writeFile(fileName);
    QVERIFY2(writeFile.open(QIODevice::Append | QIODevice::Truncate), msgOpenFailed(writeFile).constData());

    QFile readFile(fileName);
    QVERIFY2(readFile.open(QIODevice::ReadOnly), msgOpenFailed(readFile).constData());

    // 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));
        writeFile.flush();
        QVERIFY(readFile.getChar(&c));
        QCOMPARE(c, char(i));
        QCOMPARE(readFile.pos(), writeFile.pos());
    }

    // Write blocks and read them back
    for (int j = 0; j < 18; ++j) {
        const int size = 1 << j;
        writeFile.write(QByteArray(size, '@'));
        writeFile.flush();
        QCOMPARE(readFile.read(size).size(), size);
    }
}

void tst_QFile::miscWithUncPathAsCurrentDir()
{
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
    QString current = QDir::currentPath();
    const QString path = QLatin1String("//") + QtNetworkSettings::winServerName()
        + QLatin1String("/testshare");
    QVERIFY2(QDir::setCurrent(path), qPrintable(QDir::toNativeSeparators(path)));
    QFile file("test.pri");
    QVERIFY2(file.exists(), msgFileDoesNotExist(file.fileName()).constData());
    QCOMPARE(int(file.size()), 34);
    QVERIFY2(file.open(QIODevice::ReadOnly), msgOpenFailed(file).constData());
    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;
    QFile file(m_testSourceFile);
    QVERIFY2(file.open(QIODevice::ReadOnly), msgOpenFailed(file).constData());
    fd = int(file.handle());
    QVERIFY(fd > 2);
    QCOMPARE(int(file.handle()), fd);
    char c = '\0';
    const auto readResult = QT_READ(int(file.handle()), &c, 1);
    QCOMPARE(readResult, static_cast<decltype(readResult)>(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();
    QVERIFY2(file.open(QIODevice::ReadOnly | QIODevice::Unbuffered), msgOpenFailed(file).constData());
    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, '*');

    //test round trip of adopted stdio file handle
    QFile file2;
    StdioFileGuard fp(fopen(qPrintable(m_testSourceFile), "r"));
    QVERIFY(fp);
    file2.open(fp, QIODevice::ReadOnly);
    QCOMPARE(int(file2.handle()), int(fileno(fp)));
    QCOMPARE(int(file2.handle()), int(fileno(fp)));
    fp.close();

    //test round trip of adopted posix file handle
#ifdef Q_OS_UNIX
    QFile file3;
    fd = QT_OPEN(qPrintable(m_testSourceFile), QT_OPEN_RDONLY);
    file3.open(fd, QIODevice::ReadOnly);
    QCOMPARE(int(file3.handle()), fd);
    QT_CLOSE(fd);
#endif
}

void tst_QFile::nativeHandleLeaks()
{
    int fd1, fd2;

#ifdef Q_OS_WIN
    HANDLE handle1, handle2;
#endif

    {
        QFile file("qt_file.tmp");
        QVERIFY2(file.open(QIODevice::ReadWrite), msgOpenFailed(file).constData());

        fd1 = file.handle();
        QVERIFY( -1 != fd1 );
    }

#ifdef Q_OS_WIN
# ifndef Q_OS_WINRT
    handle1 = ::CreateFileA("qt_file.tmp", GENERIC_READ, 0, NULL,
            OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
# else
    handle1 = ::CreateFile2(L"qt_file.tmp", GENERIC_READ, 0, OPEN_ALWAYS, NULL);
# endif
    QVERIFY( INVALID_HANDLE_VALUE != handle1 );
    QVERIFY( ::CloseHandle(handle1) );
#endif

    {
        QFile file("qt_file.tmp");
        QVERIFY2(file.open(QIODevice::ReadOnly), msgOpenFailed(file).constData());

        fd2 = file.handle();
        QVERIFY( -1 != fd2 );
    }

#ifdef Q_OS_WIN
# ifndef Q_OS_WINRT
    handle2 = ::CreateFileA("qt_file.tmp", GENERIC_READ, 0, NULL,
            OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
# else
    handle2 = ::CreateFile2(L"qt_file.tmp", GENERIC_READ, 0, OPEN_ALWAYS, NULL);
# endif
    QVERIFY( INVALID_HANDLE_VALUE != handle2 );
    QVERIFY( ::CloseHandle(handle2) );
#endif

    QCOMPARE( fd2, fd1 );
}

void tst_QFile::readEof_data()
{
    QTest::addColumn<QString>("filename");
    QTest::addColumn<int>("imode");

    QTest::newRow("buffered") << m_testFile << 0;
    QTest::newRow("unbuffered") << m_testFile << int(QIODevice::Unbuffered);

#if defined(Q_OS_UNIX)
    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);
        QVERIFY2(file.open(QIODevice::ReadOnly | mode), msgOpenFailed(file).constData());
        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);
        QCOMPARE(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);
        QCOMPARE(file.error(), QFile::NoError);
        QVERIFY(file.atEnd());
    }

    {
        QFile file(filename);
        QVERIFY2(file.open(QIODevice::ReadOnly | mode), msgOpenFailed(file).constData());
        bool isSequential = file.isSequential();
        if (!isSequential) {
            QVERIFY(file.seek(245));
            QVERIFY(file.atEnd());
        }

        QByteArray ret = file.read(10);
        QVERIFY(ret.isEmpty());
        QCOMPARE(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());
        QCOMPARE(file.error(), QFile::NoError);
        QVERIFY(file.atEnd());
    }

    {
        QFile file(filename);
        QVERIFY2(file.open(QIODevice::ReadOnly | mode), msgOpenFailed(file).constData());
        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);
        QCOMPARE(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);
        QCOMPARE(file.error(), QFile::NoError);
        QVERIFY(file.atEnd());
    }

    {
        QFile file(filename);
        QVERIFY2(file.open(QIODevice::ReadOnly | mode), msgOpenFailed(file).constData());
        bool isSequential = file.isSequential();
        if (!isSequential) {
            QVERIFY(file.seek(245));
            QVERIFY(file.atEnd());
        }

        QByteArray ret = file.readLine();
        QVERIFY(ret.isNull());
        QCOMPARE(file.error(), QFile::NoError);
        QVERIFY(file.atEnd());

        // Do it again to ensure that we get the same result
        ret = file.readLine();
        QVERIFY(ret.isNull());
        QCOMPARE(file.error(), QFile::NoError);
        QVERIFY(file.atEnd());
    }

    {
        QFile file(filename);
        QVERIFY2(file.open(QIODevice::ReadOnly | mode), msgOpenFailed(file).constData());
        bool isSequential = file.isSequential();
        if (!isSequential) {
            QVERIFY(file.seek(245));
            QVERIFY(file.atEnd());
        }

        char c;
        QVERIFY(!file.getChar(&c));
        QCOMPARE(file.error(), QFile::NoError);
        QVERIFY(file.atEnd());

        // Do it again to ensure that we get the same result
        QVERIFY(!file.getChar(&c));
        QCOMPARE(file.error(), QFile::NoError);
        QVERIFY(file.atEnd());
    }
}

void tst_QFile::posAfterFailedStat()
{
    // Regression test for a bug 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());
    QVERIFY2(file.open(QIODevice::Append), msgOpenFailed(file).constData());
    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<int>("fileSize");
    QTest::addColumn<int>("offset");
    QTest::addColumn<int>("size");
    QTest::addColumn<QFile::FileError>("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";

    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
    QVERIFY2(file.open(QFile::ReadWrite), msgOpenFailed(file).constData());
    QVERIFY(file.resize(fileSize));
    QVERIFY(file.flush());
    file.close();
    QVERIFY2(file.open(QFile::ReadWrite), msgOpenFailed(file).constData());
    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);
    QCOMPARE(memory[0], uchar('Q'));
    QVERIFY(file.unmap(memory));
    QCOMPARE(file.error(), QFile::NoError);

    // hpux won't let you map multiple times.
#if !defined(Q_OS_HPUX) && !defined(Q_USE_DEPRECATED_MAP_API)
    // 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_VXWORKS)
#if 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));
    }
#endif
    QVERIFY(file.remove());
}

void tst_QFile::mapResource_data()
{
    QTest::addColumn<int>("offset");
    QTest::addColumn<int>("size");
    QTest::addColumn<QFile::FileError>("error");
    QTest::addColumn<QString>("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<int>("openMode");
    QTest::addColumn<int>("flags");

    QTest::newRow("ReadOnly") << int(QIODevice::ReadOnly) << int(QFileDevice::NoOptions);
    //QTest::newRow("WriteOnly") << int(QIODevice::WriteOnly); // this doesn't make sense
    QTest::newRow("ReadWrite") << int(QIODevice::ReadWrite) << int(QFileDevice::NoOptions);
    QTest::newRow("ReadOnly,Unbuffered") << int(QIODevice::ReadOnly | QIODevice::Unbuffered) << int(QFileDevice::NoOptions);
    QTest::newRow("ReadWrite,Unbuffered") << int(QIODevice::ReadWrite | QIODevice::Unbuffered) << int(QFileDevice::NoOptions);
    QTest::newRow("ReadOnly + MapPrivate") << int(QIODevice::ReadOnly) << int(QFileDevice::MapPrivateOption);
    QTest::newRow("ReadWrite + MapPrivate") << int(QIODevice::ReadWrite) << int(QFileDevice::MapPrivateOption);
}

void tst_QFile::mapOpenMode()
{
    QFETCH(int, openMode);
    QFETCH(int, flags);
    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
    QVERIFY2(file.open(QFile::ReadWrite), msgOpenFailed(file).constData());
    QVERIFY(file.write(pattern));
    QVERIFY(file.flush());
    file.close();

    // open according to our mode
    const QIODevice::OpenMode om(openMode);
    QVERIFY2(file.open(om), msgOpenFailed(om, file).constData());

    uchar *memory = file.map(0, fileSize, QFileDevice::MemoryMapFlags(flags));
    QVERIFY(memory);
    QVERIFY(memcmp(memory, pattern, fileSize) == 0);

    if ((openMode & QIODevice::WriteOnly) || (flags & QFileDevice::MapPrivateOption)) {
        // 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, (flags & QFileDevice::MapPrivateOption) ? 'A' : 'a');
    }

    file.close();
}

void tst_QFile::mapWrittenFile_data()
{
    QTest::addColumn<int>("mode");
    QTest::newRow("buffered") << 0;
    QTest::newRow("unbuffered") << int(QIODevice::Unbuffered);
}

void tst_QFile::mapWrittenFile()
{
    static const char data[128] = "Some data padded with nulls\n";
    QFETCH(int, mode);

    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);
    const QIODevice::OpenMode om = QIODevice::ReadWrite | QIODevice::OpenMode(mode);
    QVERIFY2(file.open(om), msgOpenFailed(om, file).constData());
    QCOMPARE(file.write(data, sizeof data), qint64(sizeof data));
    if ((mode & QIODevice::Unbuffered) == 0)
        file.flush();

    // test that we can read the data we've just written, without closing the file
    uchar *memory = file.map(0, sizeof data);
    QVERIFY(memory);
    QVERIFY(memcmp(memory, data, sizeof data) == 0);

    file.close();
    file.remove();
}

void tst_QFile::openDirectory()
{
    QFile f1(m_resourcesDir);
    // 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();
}

static qint64 streamExpectedSize(int fd)
{
    QT_STATBUF sb;
    if (QT_FSTAT(fd, &sb) != -1)
        return sb.st_size;
    qErrnoWarning("Could not fstat fd %d", fd);
    return 0;
}

static qint64 streamCurrentPosition(int fd)
{
    QT_STATBUF sb;
    if (QT_FSTAT(fd, &sb) != -1) {
        QT_OFF_T pos = -1;
        if ((sb.st_mode & QT_STAT_MASK) == QT_STAT_REG)
            pos = QT_LSEEK(fd, 0, SEEK_CUR);
        if (pos != -1)
            return pos;
        // failure to lseek() is not a problem
    } else {
        qErrnoWarning("Could not fstat fd %d", fd);
    }
    return 0;
}

static qint64 streamCurrentPosition(FILE *f)
{
    QT_STATBUF sb;
    if (QT_FSTAT(QT_FILENO(f), &sb) != -1) {
        QT_OFF_T pos = -1;
        if ((sb.st_mode & QT_STAT_MASK) == QT_STAT_REG)
            pos = QT_FTELL(f);
        if (pos != -1)
            return pos;
        // failure to ftell() is not a problem
    } else {
        qErrnoWarning("Could not fstat fd %d", QT_FILENO(f));
    }
    return 0;
}

class MessageHandler {
public:
    MessageHandler(QtMessageHandler messageHandler = handler)
    {
        ok = true;
        oldMessageHandler = qInstallMessageHandler(messageHandler);
    }

    ~MessageHandler()
    {
        qInstallMessageHandler(oldMessageHandler);
    }

    static bool testPassed()
    {
        return ok;
    }
protected:
    static void handler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
    {
        if (msg == QString::fromLatin1("QIODevice::seek: Cannot call seek on a sequential device"))
            ok = false;
        // Defer to old message handler.
        if (oldMessageHandler)
            oldMessageHandler(type, context, msg);
    }

    static QtMessageHandler oldMessageHandler;
    static bool ok;
};

bool MessageHandler::ok = true;
QtMessageHandler MessageHandler::oldMessageHandler = 0;

void tst_QFile::openStandardStreamsFileDescriptors()
{

    // Check that QIODevice::seek() isn't called when opening a sequential device (QFile).
    MessageHandler msgHandler;

    {
        QFile in;
        in.open(STDIN_FILENO, QIODevice::ReadOnly);
        QCOMPARE( in.pos(), streamCurrentPosition(STDIN_FILENO) );
        QCOMPARE( in.size(), streamExpectedSize(STDIN_FILENO) );
    }

    {
        QFile out;
        QVERIFY(out.open(STDOUT_FILENO, QIODevice::WriteOnly));
        QCOMPARE( out.pos(), streamCurrentPosition(STDOUT_FILENO) );
        QCOMPARE( out.size(), streamExpectedSize(STDOUT_FILENO) );
    }

    {
        QFile err;
        err.open(STDERR_FILENO, QIODevice::WriteOnly);
        QCOMPARE( err.pos(), streamCurrentPosition(STDERR_FILENO) );
        QCOMPARE( err.size(), streamExpectedSize(STDERR_FILENO) );
    }

    QVERIFY(msgHandler.testPassed());
}

void tst_QFile::openStandardStreamsBufferedStreams()
{
    // Check that QIODevice::seek() isn't called when opening a sequential device (QFile).
    MessageHandler msgHandler;

    // Using streams
    {
        QFile in;
        in.open(stdin, QIODevice::ReadOnly);
        QCOMPARE( in.pos(), streamCurrentPosition(stdin) );
        QCOMPARE( in.size(), streamExpectedSize(QT_FILENO(stdin)) );
    }

    {
        QFile out;
        out.open(stdout, QIODevice::WriteOnly);
        QCOMPARE( out.pos(), streamCurrentPosition(stdout) );
        QCOMPARE( out.size(), streamExpectedSize(QT_FILENO(stdout)) );
    }

    {
        QFile err;
        err.open(stderr, QIODevice::WriteOnly);
        QCOMPARE( err.pos(), streamCurrentPosition(stderr) );
        QCOMPARE( err.size(), streamExpectedSize(QT_FILENO(stderr)) );
    }

    QVERIFY(msgHandler.testPassed());
}

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<int>("filetype");

    QTest::newRow("native") << int(OpenQFile);
    QTest::newRow("fileno") << int(OpenFd);
    QTest::newRow("stream") << int(OpenStream);
}

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));
}

void tst_QFile::objectConstructors()
{
    QObject ob;
    QFile* file1 = new QFile(m_testFile, &ob);
    QFile* file2 = new QFile(&ob);
    QVERIFY(file1->exists());
    QVERIFY(!file2->exists());
}

void tst_QFile::caseSensitivity()
{
#if defined(Q_OS_WIN)
    const bool caseSensitive = false;
#elif defined(Q_OS_MAC)
     const bool caseSensitive = pathconf(QDir::currentPath().toLatin1().constData(), _PC_CASE_SENSITIVE);
#else
    const bool caseSensitive = true;
#endif

    QByteArray testData("a little test");
    QString filename("File.txt");
    {
        QFile f(filename);
        QVERIFY2(f.open(QIODevice::WriteOnly), msgOpenFailed(f));
        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()
{
    {
        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);
        QT_CLOSE(fd_);
        fd_ = -1;
    }

    {
        QFile file("readonlyfile");
        QVERIFY(openFile(file, QIODevice::ReadOnly, OpenStream, QFile::AutoCloseHandle));
        int fd = QT_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(), int(QT_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;
    }
}

void tst_QFile::reuseQFile()
{
    // QTemporaryDir is current dir, no need to remove these files
    const QString filename1("filegt16k");
    const QString filename2("file16k");

    // create test files for reusing QFile object
    QFile file;
    file.setFileName(filename1);
    QVERIFY(file.open(QIODevice::WriteOnly));
    QByteArray ba(17408, 'a');
    qint64 written = file.write(ba);
    QCOMPARE(written, 17408);
    file.close();

    file.setFileName(filename2);
    QVERIFY(file.open(QIODevice::WriteOnly));
    ba.resize(16384);
    written = file.write(ba);
    QCOMPARE(written, 16384);
    file.close();

    QVERIFY(file.open(QIODevice::ReadOnly));
    QCOMPARE(file.size(), 16384);
    QCOMPARE(file.pos(), qint64(0));
    QVERIFY(file.seek(10));
    QCOMPARE(file.pos(), qint64(10));
    QVERIFY(file.seek(0));
    QCOMPARE(file.pos(), qint64(0));
    QCOMPARE(file.readAll(), ba);
    file.close();

    file.setFileName(filename1);
    QVERIFY(file.open(QIODevice::ReadOnly));

    // read first file
    {
        // get file size without touching QFile
        QFileInfo fi(filename1);
        const qint64 fileSize = fi.size();
        file.read(fileSize);
        QVERIFY(file.atEnd());
        file.close();
    }

    // try again with the next file with the same QFile object
    file.setFileName(filename2);
    QVERIFY(file.open(QIODevice::ReadOnly));

    // read second file
    {
        // get file size without touching QFile
        QFileInfo fi(filename2);
        const qint64 fileSize = fi.size();
        file.read(fileSize);
        QVERIFY(file.atEnd());
        file.close();
    }
}

void tst_QFile::moveToTrash_data()
{
    QTest::addColumn<QString>("source");
    QTest::addColumn<bool>("create");
    QTest::addColumn<bool>("result");

    // success cases
    {
        QTemporaryFile temp;
        if (!temp.open())
            QSKIP("Failed to create temporary file!");
        QTest::newRow("temporary file") << temp.fileName() << true << true;
    }
    {
        QTemporaryDir tempDir;
        if (!tempDir.isValid())
            QSKIP("Failed to create temporary directory!");
        tempDir.setAutoRemove(false);
        QTest::newRow("temporary dir")
            << tempDir.path() + QLatin1Char('/')
            << true << true;
    }
    {
        QTemporaryDir homeDir(QDir::homePath() + QLatin1String("/XXXXXX"));
        if (!homeDir.isValid())
            QSKIP("Failed to create temporary directory in $HOME!");
        QTemporaryFile homeFile(homeDir.path()
                              + QLatin1String("/tst_qfile-XXXXXX"));
        if (!homeFile.open())
            QSKIP("Failed to create temporary file in $HOME");
        homeDir.setAutoRemove(false);
        QTest::newRow("home file")
            << homeFile.fileName()
            << true << true;

        QTest::newRow("home dir")
            << homeDir.path() + QLatin1Char('/')
            << true << true;
    }
    QTest::newRow("relative") << QStringLiteral("tst_qfile_moveToTrash.tmp") << true << true;

    // failure cases
    QTest::newRow("root") << QDir::rootPath() << false << false;
    QTest::newRow("no-such-file") << QString::fromLatin1("no/such/file") << false << false;
}

void tst_QFile::moveToTrash()
{
    QFETCH(QString, source);
    QFETCH(bool, create);
    QFETCH(bool, result);

#if defined(Q_OS_WINRT)
    QSKIP("WinRT does not have a trash", SkipAll);
#endif

    auto ensureFile = [](const QString &source, bool create) {
        if (QFileInfo::exists(source) || !create)
            return;
        if (source.endsWith(QLatin1Char('/'))) {
            QDir::root().mkdir(source);
            QFile file(source + QLatin1String("test"));
            if (!file.open(QIODevice::WriteOnly))
                QSKIP("Couldn't create directory with file");
        } else {
            QFile sourceFile(source);
            QVERIFY2(sourceFile.open(QFile::WriteOnly | QFile::Text), qPrintable(sourceFile.errorString()));
            sourceFile.close();
        }
    };
    auto cleanupFile = [source, create]() {
        if (!QFileInfo::exists(source) || !create)
            return;
        if (source.endsWith(QLatin1Char('/'))) {
            QDir(source).removeRecursively();
        } else {
            QFile sourceFile(source);
            sourceFile.remove();
        }
    };

    ensureFile(source, create);

    /* This test makes assumptions about the file system layout
       which might be wrong - moveToTrash may fail if the file lives
       on a file system that is different from the home file system, and
       has no .Trash directory.
    */
    const QStorageInfo sourceStorage(source);
    const bool mayFail = sourceStorage.isValid()
                      && QStorageInfo(source) != QStorageInfo(QDir::home());

    // non-static version
    {
        QFile sourceFile(source);
        const bool success = sourceFile.moveToTrash();

        // tolerate moveToTrash failing
        if (result && !success && mayFail)
            result = false;

        if (result) {
            // if any of the test fails, we still want to remove the file
            auto onFailure = qScopeGuard(cleanupFile);
            QVERIFY2(success, qPrintable(sourceFile.errorString()));
            QCOMPARE(sourceFile.error(), QFile::NoError);
            QVERIFY(source != sourceFile.fileName());
            if (!sourceFile.fileName().isEmpty()) {
                QVERIFY2(sourceFile.exists(), qPrintable(sourceFile.fileName()));
                // remove file/dir in trash as well, don't fill disk
                if (source.endsWith(QLatin1Char('/')))
                    QDir(sourceFile.fileName()).removeRecursively();
                else
                    sourceFile.remove();
            }
        } else {
            QVERIFY(!success);
            QVERIFY(!sourceFile.errorString().isEmpty());
            QCOMPARE(source, sourceFile.fileName());
        }
    }

    // don't retry
    if (mayFail)
        return;

    // static version
    {
        ensureFile(source, create);
        QString pathInTrash;
        const bool success = QFile::moveToTrash(source, &pathInTrash);
        QCOMPARE(success, result);
        if (result) {
            auto onFailure = qScopeGuard(cleanupFile);
            QVERIFY(source != pathInTrash);
            if (!pathInTrash.isEmpty()) {
                // remove file/dir in trash as well, don't fill disk
                QVERIFY2(QFile::exists(pathInTrash), qPrintable(pathInTrash));
                if (source.endsWith(QLatin1Char('/')))
                    QDir(pathInTrash).removeRecursively();
                else
                    QFile::remove(pathInTrash);
            }
        }
    }
}

QTEST_MAIN(tst_QFile)
#include "tst_qfile.moc"
