tests/auto/qftp/tst_qftp.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/auto/qftp/tst_qftp.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,2057 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qfile.h>
+#include <qbuffer.h>
+#include "qftp.h"
+#include <qmap.h>
+#include <time.h>
+#include <stdlib.h>
+#include <QNetworkProxy>
+
+#ifndef TEST_QNETWORK_PROXY
+#define TEST_QNETWORK_PROXY
+#endif
+#include "../network-settings.h"
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+#ifdef Q_OS_SYMBIAN
+// In Symbian OS test data is located in applications private dir
+// Application private dir is default serach path for files, so SRCDIR can be set to empty
+#define SRCDIR ""
+#endif
+
+
+
+class tst_QFtp : public QObject
+{
+    Q_OBJECT
+
+public:
+    tst_QFtp();
+    virtual ~tst_QFtp();
+
+
+public slots:
+    void initTestCase_data();
+    void initTestCase();
+    void cleanupTestCase();
+    void init();
+    void cleanup();
+private slots:
+    void connectToHost_data();
+    void connectToHost();
+    void connectToUnresponsiveHost();
+    void login_data();
+    void login();
+    void close_data();
+    void close();
+
+    void list_data();
+    void list();
+    void cd_data();
+    void cd();
+    void get_data();
+    void get();
+    void put_data();
+    void put();
+    void remove();
+    void mkdir_data();
+    void mkdir();
+    void mkdir2();
+    void rmdir();
+    void rename_data();
+    void rename();
+
+    void commandSequence_data();
+    void commandSequence();
+
+    void abort_data();
+    void abort();
+
+    void bytesAvailable_data();
+    void bytesAvailable();
+
+    void activeMode();
+
+    void proxy_data();
+    void proxy();
+
+    void binaryAscii();
+
+    void doneSignal();
+    void queueMoreCommandsInDoneSlot();
+
+protected slots:
+    void stateChanged( int );
+    void listInfo( const QUrlInfo & );
+    void readyRead();
+    void dataTransferProgress(qint64, qint64);
+
+    void commandStarted( int );
+    void commandFinished( int, bool );
+    void done( bool );
+    void activeModeDone( bool );
+    void mkdir2Slot(int id, bool error);
+    void cdUpSlot(bool);
+
+private:
+    QFtp *newFtp();
+    void addCommand( QFtp::Command, int );
+    bool fileExists( const QString &host, quint16 port, const QString &user, const QString &password, const QString &file, const QString &cdDir = QString::null );
+    bool dirExists( const QString &host, quint16 port, const QString &user, const QString &password, const QString &cdDir, const QString &dirToCreate );
+
+    void renameInit( const QString &host, const QString &user, const QString &password, const QString &createFile );
+    void renameCleanup( const QString &host, const QString &user, const QString &password, const QString &fileToDelete );
+
+    QFtp *ftp;
+
+    QList<int> ids; // helper to make sure that all expected signals are emitted
+    int current_id;
+
+    int connectToHost_state;
+    int close_state;
+    int login_state;
+    int cur_state;
+    struct CommandResult
+    {
+        int id;
+        int success;
+    };
+    QMap< QFtp::Command, CommandResult > resultMap;
+    typedef QMap<QFtp::Command,CommandResult>::Iterator ResMapIt;
+
+    int done_success;
+    int commandSequence_success;
+
+    qlonglong bytesAvailable_finishedGet;
+    qlonglong bytesAvailable_finished;
+    qlonglong bytesAvailable_done;
+
+    QList<QUrlInfo> listInfo_i;
+    QByteArray newData_ba;
+    qlonglong bytesTotal;
+    qlonglong bytesDone;
+
+    bool inFileDirExistsFunction;
+
+    QString uniqueExtension;
+};
+
+//#define DUMP_SIGNALS
+
+const int bytesTotal_init = -10;
+const int bytesDone_init = -10;
+
+tst_QFtp::tst_QFtp()
+{
+    Q_SET_DEFAULT_IAP
+}
+
+tst_QFtp::~tst_QFtp()
+{
+}
+
+void tst_QFtp::initTestCase_data()
+{
+    QTest::addColumn<bool>("setProxy");
+    QTest::addColumn<int>("proxyType");
+
+    QTest::newRow("WithoutProxy") << false << 0;
+#ifdef TEST_QNETWORK_PROXY
+    QTest::newRow("WithSocks5Proxy") << true << int(QNetworkProxy::Socks5Proxy);
+    //### doesn't work well yet.
+    //QTest::newRow("WithHttpProxy") << true << int(QNetworkProxy::HttpProxy);
+#endif
+}
+
+void tst_QFtp::initTestCase()
+{
+}
+
+void tst_QFtp::cleanupTestCase()
+{
+}
+
+void tst_QFtp::init()
+{
+    QFETCH_GLOBAL(bool, setProxy);
+    if (setProxy) {
+        QFETCH_GLOBAL(int, proxyType);
+        if (proxyType == QNetworkProxy::Socks5Proxy) {
+            QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080));
+        } else if (proxyType == QNetworkProxy::HttpProxy) {
+            QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3128));
+        }
+    }
+
+    ftp = 0;
+
+    ids.clear();
+    current_id = 0;
+
+    resultMap.clear();
+    connectToHost_state = -1;
+    close_state = -1;
+    login_state = -1;
+    cur_state = QFtp::Unconnected;
+
+    listInfo_i.clear();
+    newData_ba = QByteArray();
+    bytesTotal = bytesTotal_init;
+    bytesDone = bytesDone_init;
+
+    done_success = -1;
+    commandSequence_success = -1;
+
+    bytesAvailable_finishedGet = 1234567890;
+    bytesAvailable_finished = 1234567890;
+    bytesAvailable_done = 1234567890;
+
+    inFileDirExistsFunction = FALSE;
+
+#if !defined(Q_OS_WINCE)
+    srand(time(0));
+    uniqueExtension = QString("%1%2%3").arg((qulonglong)this).arg(rand()).arg((qulonglong)time(0));
+#else
+    srand(0);
+    uniqueExtension = QString("%1%2%3").arg((qulonglong)this).arg(rand()).arg((qulonglong)(0));
+#endif
+}
+
+void tst_QFtp::cleanup()
+{
+    QFETCH_GLOBAL(bool, setProxy);
+    if (setProxy) {
+        QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy);
+    }
+}
+
+void tst_QFtp::connectToHost_data()
+{
+    QTest::addColumn<QString>("host");
+    QTest::addColumn<uint>("port");
+    QTest::addColumn<int>("state");
+
+    QTest::newRow( "ok01" ) << QtNetworkSettings::serverName() << (uint)21 << (int)QFtp::Connected;
+    QTest::newRow( "error01" ) << QtNetworkSettings::serverName() << (uint)2222 << (int)QFtp::Unconnected;
+    QTest::newRow( "error02" ) << QString("foo.bar") << (uint)21 << (int)QFtp::Unconnected;
+}
+
+void tst_QFtp::connectToHost()
+{
+    QFETCH( QString, host );
+    QFETCH( uint, port );
+
+    ftp = newFtp();
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+
+    QTestEventLoop::instance().enterLoop( 61 );
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    QTEST( connectToHost_state, "state" );
+
+    ResMapIt it = resultMap.find( QFtp::ConnectToHost );
+    QVERIFY( it != resultMap.end() );
+    QFETCH( int, state );
+    if ( state == QFtp::Connected ) {
+        QVERIFY( it.value().success == 1 );
+    } else {
+        QVERIFY( it.value().success == 0 );
+    }
+}
+
+void tst_QFtp::connectToUnresponsiveHost()
+{
+    QFETCH_GLOBAL(bool, setProxy);
+    if (setProxy)
+        QSKIP( "This test takes too long if we test with proxies too", SkipSingle );
+
+    QString host = "1.2.3.4";
+    uint port = 21;
+
+    ftp = newFtp();
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+
+    qDebug( "About to connect to host that won't reply (this test takes 60 seconds)" );
+    QTestEventLoop::instance().enterLoop( 61 );
+#ifdef Q_OS_WIN
+    /* On Windows, we do not get a timeout, because Winsock is behaving in a strange way:
+    We issue two "WSAConnect()" calls, after the first, as a result we get WSAEWOULDBLOCK,
+    after the second, we get WSAEISCONN, which means that the socket is connected, which cannot be.
+    However, after some seconds we get a socket error saying that the remote host closed the connection,
+    which can neither be. For this test, that would actually enable us to finish before timout, but handling that case
+    (in void QFtpPI::error(QAbstractSocket::SocketError e)) breaks
+    a lot of other stuff in QFtp, so we just expect this test to fail on Windows.
+    */
+    QEXPECT_FAIL("", "timeout not working due to strange Windows socket behaviour (see source file of this test for explanation)", Abort);
+#endif
+    QVERIFY2(! QTestEventLoop::instance().timeout(), "Network timeout longer than expected (should have been 60 seconds)");
+
+    QVERIFY( ftp->state() == QFtp::Unconnected);
+    ResMapIt it = resultMap.find( QFtp::ConnectToHost );
+    QVERIFY( it != resultMap.end() );
+    QVERIFY( it.value().success == 0 );
+
+    delete ftp;
+}
+
+void tst_QFtp::login_data()
+{
+    QTest::addColumn<QString>("host");
+    QTest::addColumn<uint>("port");
+    QTest::addColumn<QString>("user");
+    QTest::addColumn<QString>("password");
+    QTest::addColumn<int>("success");
+
+    QTest::newRow( "ok01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << 1;
+    QTest::newRow( "ok02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftp") << QString() << 1;
+    QTest::newRow( "ok03" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftp") << QString("foo") << 1;
+    QTest::newRow( "ok04" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << 1;
+
+    QTest::newRow( "error01" ) << QtNetworkSettings::serverName() << (uint)21 << QString("foo") << QString() << 0;
+    QTest::newRow( "error02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("foo") << QString("bar") << 0;
+}
+
+void tst_QFtp::login()
+{
+    QFETCH( QString, host );
+    QFETCH( uint, port );
+    QFETCH( QString, user );
+    QFETCH( QString, password );
+
+    ftp = newFtp();
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+    addCommand( QFtp::Login, ftp->login( user, password ) );
+
+    QTestEventLoop::instance().enterLoop( 30 );
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    ResMapIt it = resultMap.find( QFtp::Login );
+    QVERIFY( it != resultMap.end() );
+    QTEST( it.value().success, "success" );
+
+    if ( it.value().success ) {
+        QVERIFY( login_state == QFtp::LoggedIn );
+    } else {
+        QVERIFY( login_state != QFtp::LoggedIn );
+    }
+}
+
+void tst_QFtp::close_data()
+{
+    QTest::addColumn<QString>("host");
+    QTest::addColumn<uint>("port");
+    QTest::addColumn<QString>("user");
+    QTest::addColumn<QString>("password");
+    QTest::addColumn<bool>("login");
+
+    QTest::newRow( "login01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << (bool)TRUE;
+    QTest::newRow( "login02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftp") << QString() << (bool)TRUE;
+    QTest::newRow( "login03" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftp") << QString("foo") << (bool)TRUE;
+    QTest::newRow( "login04" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest") << QString("password") << (bool)TRUE;
+
+    QTest::newRow( "no-login01" ) << QtNetworkSettings::serverName() << (uint)21 << QString("") << QString("") << (bool)FALSE;
+}
+
+void tst_QFtp::close()
+{
+    QFETCH( QString, host );
+    QFETCH( uint, port );
+    QFETCH( QString, user );
+    QFETCH( QString, password );
+    QFETCH( bool, login );
+
+    ftp = newFtp();
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+    if ( login )
+        addCommand( QFtp::Login, ftp->login( user, password ) );
+    addCommand( QFtp::Close, ftp->close() );
+
+    QTestEventLoop::instance().enterLoop( 30 );
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    QCOMPARE( close_state, (int)QFtp::Unconnected );
+
+    ResMapIt it = resultMap.find( QFtp::Close );
+    QVERIFY( it != resultMap.end() );
+    QVERIFY( it.value().success == 1 );
+}
+
+void tst_QFtp::list_data()
+{
+    QTest::addColumn<QString>("host");
+    QTest::addColumn<uint>("port");
+    QTest::addColumn<QString>("user");
+    QTest::addColumn<QString>("password");
+    QTest::addColumn<QString>("dir");
+    QTest::addColumn<int>("success");
+    QTest::addColumn<QStringList>("entryNames"); // ### we should rather use a QList<QUrlInfo> here
+
+    QStringList flukeRoot;
+    flukeRoot << "pub";
+    flukeRoot << "qtest";
+    QStringList flukeQtest;
+    flukeQtest << "bigfile";
+    flukeQtest << "nonASCII";
+    flukeQtest << "rfc3252";
+    flukeQtest << "rfc3252.txt";
+    flukeQtest << "upload";
+
+    QTest::newRow( "workDir01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString() << 1 << flukeRoot;
+    QTest::newRow( "workDir02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest")     << QString("password")     << QString() << 1 << flukeRoot;
+
+    QTest::newRow( "relPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("qtest") << 1 << flukeQtest;
+    QTest::newRow( "relPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest")     << QString("password")     << QString("qtest") << 1 << flukeQtest;
+
+    QTest::newRow( "absPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/qtest") << 1 << flukeQtest;
+    QTest::newRow( "absPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest")     << QString("password")     << QString("/var/ftp/qtest") << 1 << flukeQtest;
+
+    QTest::newRow( "nonExist01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("foo")  << 1 << QStringList();
+    QTest::newRow( "nonExist02" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/foo") << 1 << QStringList();
+    // ### The microsoft server does not seem to work properly at the moment --
+    // I am also not able to open a data connection with other, non-Qt FTP
+    // clients to it.
+    // QTest::newRow( "nonExist03" ) << "ftp.microsoft.com" << (uint)21 << QString() << QString() << QString("/foo") << 0 << QStringList();
+
+    QStringList susePub;
+    susePub << "README.mirror-policy" << "axp" << "i386" << "ia64" << "install" << "noarch" << "pubring.gpg-build.suse.de" << "update" << "x86_64";
+    QTest::newRow( "epsvNotSupported" ) << QString("ftp.funet.fi") << (uint)21 << QString::fromLatin1("ftp") << QString::fromLatin1("root@") << QString("/pub/Linux/suse/suse") << 1 << susePub;
+}
+
+void tst_QFtp::list()
+{
+    QFETCH( QString, host );
+    QFETCH( uint, port );
+    QFETCH( QString, user );
+    QFETCH( QString, password );
+    QFETCH( QString, dir );
+
+    ftp = newFtp();
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+    addCommand( QFtp::Login, ftp->login( user, password ) );
+    addCommand( QFtp::List, ftp->list( dir ) );
+    addCommand( QFtp::Close, ftp->close() );
+
+    QTestEventLoop::instance().enterLoop( 30 );
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    ResMapIt it = resultMap.find( QFtp::List );
+    QVERIFY( it != resultMap.end() );
+    QTEST( it.value().success, "success" );
+    QFETCH( QStringList, entryNames );
+    QCOMPARE( listInfo_i.count(), entryNames.count() );
+    for ( uint i=0; i < (uint) entryNames.count(); i++ ) {
+        QCOMPARE( listInfo_i[i].name(), entryNames[i] );
+    }
+}
+
+void tst_QFtp::cd_data()
+{
+    QTest::addColumn<QString>("host");
+    QTest::addColumn<uint>("port");
+    QTest::addColumn<QString>("user");
+    QTest::addColumn<QString>("password");
+    QTest::addColumn<QString>("dir");
+    QTest::addColumn<int>("success");
+    QTest::addColumn<QStringList>("entryNames"); // ### we should rather use a QList<QUrlInfo> here
+
+    QStringList flukeRoot;
+    flukeRoot << "qtest";
+    QStringList flukeQtest;
+    flukeQtest << "bigfile";
+    flukeQtest << "nonASCII";
+    flukeQtest << "rfc3252";
+    flukeQtest << "rfc3252.txt";
+    flukeQtest << "upload";
+
+    QTest::newRow( "relPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("qtest") << 1 << flukeQtest;
+    QTest::newRow( "relPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest")     << QString("password")     << QString("qtest") << 1 << flukeQtest;
+
+    QTest::newRow( "absPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/qtest") << 1 << flukeQtest;
+    QTest::newRow( "absPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest")     << QString("password")     << QString("/var/ftp/qtest") << 1 << flukeQtest;
+
+    QTest::newRow( "nonExist01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("foo")  << 0 << QStringList();
+    QTest::newRow( "nonExist03" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/foo") << 0 << QStringList();
+}
+
+void tst_QFtp::cd()
+{
+    QFETCH( QString, host );
+    QFETCH( uint, port );
+    QFETCH( QString, user );
+    QFETCH( QString, password );
+    QFETCH( QString, dir );
+
+    ftp = newFtp();
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+    addCommand( QFtp::Login, ftp->login( user, password ) );
+    addCommand( QFtp::Cd, ftp->cd( dir ) );
+    addCommand( QFtp::List, ftp->list() );
+    addCommand( QFtp::Close, ftp->close() );
+
+    QTestEventLoop::instance().enterLoop( 30 );
+
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() ) {
+        QFAIL( "Network operation timed out" );
+    }
+
+    ResMapIt it = resultMap.find( QFtp::Cd );
+    QVERIFY( it != resultMap.end() );
+    QTEST( it.value().success, "success" );
+    QFETCH( QStringList, entryNames );
+    QCOMPARE( listInfo_i.count(), entryNames.count() );
+    for ( uint i=0; i < (uint) entryNames.count(); i++ ) {
+        QCOMPARE( listInfo_i[i].name(), entryNames[i] );
+    }
+}
+
+void tst_QFtp::get_data()
+{
+    QTest::addColumn<QString>("host");
+    QTest::addColumn<uint>("port");
+    QTest::addColumn<QString>("user");
+    QTest::addColumn<QString>("password");
+    QTest::addColumn<QString>("file");
+    QTest::addColumn<int>("success");
+    QTest::addColumn<QByteArray>("res");
+    QTest::addColumn<bool>("useIODevice");
+
+    // ### move this into external testdata
+    QFile file( SRCDIR "rfc3252.txt" );
+    QVERIFY( file.open( QIODevice::ReadOnly ) );
+    QByteArray rfc3252 = file.readAll();
+
+    // test the two get() overloads in one routine
+    for ( int i=0; i<2; i++ ) {
+        QTest::newRow( QString("relPath01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+                << "qtest/rfc3252" << 1 << rfc3252 << (bool)(i==1);
+        QTest::newRow( QString("relPath02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest")     << QString("password")
+                << "qtest/rfc3252" << 1 << rfc3252 << (bool)(i==1);
+
+        QTest::newRow( QString("absPath01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+                << "/qtest/rfc3252" << 1 << rfc3252 << (bool)(i==1);
+        QTest::newRow( QString("absPath02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest")     << QString("password")
+                << "/var/ftp/qtest/rfc3252" << 1 << rfc3252 << (bool)(i==1);
+
+        QTest::newRow( QString("nonExist01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+                << QString("foo")  << 0 << QByteArray() << (bool)(i==1);
+        QTest::newRow( QString("nonExist02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+                << QString("/foo") << 0 << QByteArray() << (bool)(i==1);
+    }
+}
+
+void tst_QFtp::get()
+{
+    // for the overload that takes a QIODevice
+    QByteArray buf_ba;
+    QBuffer buf( &buf_ba );
+
+    QFETCH( QString, host );
+    QFETCH( uint, port );
+    QFETCH( QString, user );
+    QFETCH( QString, password );
+    QFETCH( QString, file );
+    QFETCH( bool, useIODevice );
+
+    ftp = newFtp();
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+    addCommand( QFtp::Login, ftp->login( user, password ) );
+    if ( useIODevice ) {
+        buf.open( QIODevice::WriteOnly );
+        addCommand( QFtp::Get, ftp->get( file, &buf ) );
+    } else {
+        addCommand( QFtp::Get, ftp->get( file ) );
+    }
+    addCommand( QFtp::Close, ftp->close() );
+
+    QTestEventLoop::instance().enterLoop( 30 );
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    ResMapIt it = resultMap.find( QFtp::Get );
+    QVERIFY( it != resultMap.end() );
+    QTEST( it.value().success, "success" );
+    if ( useIODevice ) {
+        QTEST( buf_ba, "res" );
+    } else {
+        QTEST( newData_ba, "res" );
+    }
+    QVERIFY( bytesTotal != bytesTotal_init );
+    if ( bytesTotal != -1 ) {
+        QVERIFY( bytesDone == bytesTotal );
+    }
+    if ( useIODevice ) {
+        if ( bytesDone != bytesDone_init ) {
+            QVERIFY( (int)buf_ba.size() == bytesDone );
+        }
+    } else {
+        if ( bytesDone != bytesDone_init ) {
+            QVERIFY( (int)newData_ba.size() == bytesDone );
+        }
+    }
+}
+
+void tst_QFtp::put_data()
+{
+    QTest::addColumn<QString>("host");
+    QTest::addColumn<uint>("port");
+    QTest::addColumn<QString>("user");
+    QTest::addColumn<QString>("password");
+    QTest::addColumn<QString>("file");
+    QTest::addColumn<QByteArray>("fileData");
+    QTest::addColumn<bool>("useIODevice");
+    QTest::addColumn<int>("success");
+
+    // ### move this into external testdata
+    QFile file( SRCDIR "rfc3252.txt" );
+    QVERIFY( file.open( QIODevice::ReadOnly ) );
+    QByteArray rfc3252 = file.readAll();
+
+    QByteArray bigData( 10*1024*1024, 0 );
+    bigData.fill( 'A' );
+
+    // test the two put() overloads in one routine
+    for ( int i=0; i<2; i++ ) {
+        QTest::newRow( QString("relPath01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+                << QString("qtest/upload/rel01_%1") << rfc3252
+                << (bool)(i==1) << 1;
+        /*
+    QTest::newRow( QString("relPath02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest")     << QString("password")
+        << QString("qtest/upload/rel02_%1") << rfc3252
+        << (bool)(i==1) << 1;
+    QTest::newRow( QString("relPath03_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest")     << QString("password")
+        << QString("qtest/upload/rel03_%1") << QByteArray()
+        << (bool)(i==1) << 1;
+    QTest::newRow( QString("relPath04_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest")     << QString("password")
+        << QString("qtest/upload/rel04_%1") << bigData
+        << (bool)(i==1) << 1;
+
+    QTest::newRow( QString("absPath01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+        << QString("/qtest/upload/abs01_%1") << rfc3252
+        << (bool)(i==1) << 1;
+    QTest::newRow( QString("absPath02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest")     << QString("password")
+        << QString("/srv/ftp/qtest/upload/abs02_%1") << rfc3252
+        << (bool)(i==1) << 1;
+
+    QTest::newRow( QString("nonExist01_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+        << QString("foo")  << QByteArray()
+        << (bool)(i==1) << 0;
+    QTest::newRow( QString("nonExist02_%1").arg(i).toLatin1().constData() ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+        << QString("/foo") << QByteArray()
+        << (bool)(i==1) << 0;
+*/
+    }
+}
+
+void tst_QFtp::put()
+{
+    QFETCH( QString, host );
+    QFETCH( uint, port );
+    QFETCH( QString, user );
+    QFETCH( QString, password );
+    QFETCH( QString, file );
+    QFETCH( QByteArray, fileData );
+    QFETCH( bool, useIODevice );
+
+#ifdef Q_OS_WIN
+    QFETCH_GLOBAL(bool, setProxy);
+    if (setProxy) {
+        QFETCH_GLOBAL(int, proxyType);
+        if (proxyType == QNetworkProxy::Socks5Proxy) {
+            QSKIP("With socks5 the put() test takes too long time on Windows.", SkipAll);
+        }
+    }
+#endif
+
+    const int timestep = 50;
+
+    if(file.contains('%'))
+        file = file.arg(uniqueExtension);
+
+    // for the overload that takes a QIODevice
+    QBuffer buf_fileData( &fileData );
+    buf_fileData.open( QIODevice::ReadOnly );
+
+    ResMapIt it;
+    //////////////////////////////////////////////////////////////////
+    // upload the file
+    init();
+    ftp = newFtp();
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+    addCommand( QFtp::Login, ftp->login( user, password ) );
+    if ( useIODevice )
+        addCommand( QFtp::Put, ftp->put( &buf_fileData, file ) );
+    else
+        addCommand( QFtp::Put, ftp->put( fileData, file ) );
+    addCommand( QFtp::Close, ftp->close() );
+
+    for(int time = 0; time <= fileData.length() / 20000; time += timestep) {
+        QTestEventLoop::instance().enterLoop( timestep );
+        if(ftp->currentCommand() == QFtp::None)
+            break;
+    }
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    it = resultMap.find( QFtp::Put );
+    QVERIFY( it != resultMap.end() );
+    QTEST( it.value().success, "success" );
+    if ( !it.value().success ) {
+        QVERIFY( !fileExists( host, port, user, password, file ) );
+        return; // the following tests are only meaningful if the file could be put
+    }
+    QVERIFY( bytesTotal == (int)fileData.size() );
+    QVERIFY( bytesDone == bytesTotal );
+
+    QVERIFY( fileExists( host, port, user, password, file ) );
+
+    //////////////////////////////////////////////////////////////////
+    // fetch file to make sure that it is equal to the uploaded file
+    init();
+    ftp = newFtp();
+    QBuffer buf;
+    buf.open( QIODevice::WriteOnly );
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+    addCommand( QFtp::Login, ftp->login( user, password ) );
+    addCommand( QFtp::Get, ftp->get( file, &buf ) );
+    addCommand( QFtp::Close, ftp->close() );
+
+    for(int time = 0; time <= fileData.length() / 20000; time += timestep) {
+        QTestEventLoop::instance().enterLoop( timestep );
+        if(ftp->currentCommand() == QFtp::None)
+            break;
+    }
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    QVERIFY( done_success == 1 );
+    QTEST( buf.buffer(), "fileData" );
+
+    //////////////////////////////////////////////////////////////////
+    // cleanup (i.e. remove the file) -- this also tests the remove command
+    init();
+    ftp = newFtp();
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+    addCommand( QFtp::Login, ftp->login( user, password ) );
+    addCommand( QFtp::Remove, ftp->remove( file ) );
+    addCommand( QFtp::Close, ftp->close() );
+
+    QTestEventLoop::instance().enterLoop( timestep );
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    it = resultMap.find( QFtp::Remove );
+    QVERIFY( it != resultMap.end() );
+    QCOMPARE( it.value().success, 1 );
+
+    QVERIFY( !fileExists( host, port, user, password, file ) );
+}
+
+void tst_QFtp::remove()
+{
+    DEPENDS_ON( "put" );
+}
+
+void tst_QFtp::mkdir_data()
+{
+    QTest::addColumn<QString>("host");
+    QTest::addColumn<uint>("port");
+    QTest::addColumn<QString>("user");
+    QTest::addColumn<QString>("password");
+    QTest::addColumn<QString>("cdDir");
+    QTest::addColumn<QString>("dirToCreate");
+    QTest::addColumn<int>("success");
+
+    QTest::newRow( "relPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+            << "qtest/upload" << QString("rel01_%1") << 1;
+    QTest::newRow( "relPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest")     << QString("password")
+            << "qtest/upload" << QString("rel02_%1") << 1;
+    QTest::newRow( "relPath03" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest")     << QString("password")
+            << "qtest/upload" << QString("rel03_%1") << 1;
+
+    QTest::newRow( "absPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+            << "." << QString("/qtest/upload/abs01_%1") << 1;
+    QTest::newRow( "absPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest")     << QString("password")
+            << "." << QString("/var/ftp/qtest/upload/abs02_%1") << 1;
+
+    //    QTest::newRow( "nonExist01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("foo")  << 0;
+    QTest::newRow( "nonExist01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+            << "." << QString("foo")  << 0;
+    QTest::newRow( "nonExist02" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString()
+            << "." << QString("/foo") << 0;
+}
+
+void tst_QFtp::mkdir()
+{
+    QFETCH( QString, host );
+    QFETCH( uint, port );
+    QFETCH( QString, user );
+    QFETCH( QString, password );
+    QFETCH( QString, cdDir );
+    QFETCH( QString, dirToCreate );
+
+    if(dirToCreate.contains('%'))
+        dirToCreate = dirToCreate.arg(uniqueExtension);
+
+    //////////////////////////////////////////////////////////////////
+    // create the directory
+    init();
+    ftp = newFtp();
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+    addCommand( QFtp::Login, ftp->login( user, password ) );
+    addCommand( QFtp::Cd, ftp->cd( cdDir ) );
+    addCommand( QFtp::Mkdir, ftp->mkdir( dirToCreate ) );
+    addCommand( QFtp::Close, ftp->close() );
+
+    QTestEventLoop::instance().enterLoop( 30 );
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    ResMapIt it = resultMap.find( QFtp::Mkdir );
+    QVERIFY( it != resultMap.end() );
+    QTEST( it.value().success, "success" );
+    if ( !it.value().success ) {
+        QVERIFY( !dirExists( host, port, user, password, cdDir, dirToCreate ) );
+        return; // the following tests are only meaningful if the dir could be created
+    }
+    QVERIFY( dirExists( host, port, user, password, cdDir, dirToCreate ) );
+
+    //////////////////////////////////////////////////////////////////
+    // create the directory again (should always fail!)
+    init();
+    ftp = newFtp();
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+    addCommand( QFtp::Login, ftp->login( user, password ) );
+    addCommand( QFtp::Cd, ftp->cd( cdDir ) );
+    addCommand( QFtp::Mkdir, ftp->mkdir( dirToCreate ) );
+    addCommand( QFtp::Close, ftp->close() );
+
+    QTestEventLoop::instance().enterLoop( 30 );
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    it = resultMap.find( QFtp::Mkdir );
+    QVERIFY( it != resultMap.end() );
+    QCOMPARE( it.value().success, 0 );
+
+    //////////////////////////////////////////////////////////////////
+    // remove the directory
+    init();
+    ftp = newFtp();
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+    addCommand( QFtp::Login, ftp->login( user, password ) );
+    addCommand( QFtp::Cd, ftp->cd( cdDir ) );
+    addCommand( QFtp::Rmdir, ftp->rmdir( dirToCreate ) );
+    addCommand( QFtp::Close, ftp->close() );
+
+    QTestEventLoop::instance().enterLoop( 30 );
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    it = resultMap.find( QFtp::Rmdir );
+    QVERIFY( it != resultMap.end() );
+    QCOMPARE( it.value().success, 1 );
+
+    QVERIFY( !dirExists( host, port, user, password, cdDir, dirToCreate ) );
+}
+
+void tst_QFtp::mkdir2()
+{
+    ftp = new QFtp;
+    ftp->connectToHost(QtNetworkSettings::serverName());
+    ftp->login();
+    current_id = ftp->cd("kake/test");
+
+    QEventLoop loop;
+    connect(ftp, SIGNAL(done(bool)), &loop, SLOT(quit()));
+    connect(ftp, SIGNAL(commandFinished(int, bool)), this, SLOT(mkdir2Slot(int, bool)));
+    QTimer::singleShot(5000, &loop, SLOT(quit()));
+
+    QSignalSpy commandStartedSpy(ftp, SIGNAL(commandStarted(int)));
+    QSignalSpy commandFinishedSpy(ftp, SIGNAL(commandFinished(int, bool)));
+
+    loop.exec();
+
+    QCOMPARE(commandStartedSpy.count(), 4); // connect, login, cd, mkdir
+    QCOMPARE(commandFinishedSpy.count(), 4);
+
+    for (int i = 0; i < 4; ++i)
+        QCOMPARE(commandFinishedSpy.at(i).at(0), commandStartedSpy.at(i).at(0));
+
+    QVERIFY(!commandFinishedSpy.at(0).at(1).toBool());
+    QVERIFY(!commandFinishedSpy.at(1).at(1).toBool());
+    QVERIFY(commandFinishedSpy.at(2).at(1).toBool());
+    QVERIFY(commandFinishedSpy.at(3).at(1).toBool());
+
+    delete ftp;
+}
+
+void tst_QFtp::mkdir2Slot(int id, bool)
+{
+    if (id == current_id)
+        ftp->mkdir("kake/test");
+}
+
+void tst_QFtp::rmdir()
+{
+    DEPENDS_ON( "mkdir" );
+}
+
+void tst_QFtp::rename_data()
+{
+    QTest::addColumn<QString>("host");
+    QTest::addColumn<QString>("user");
+    QTest::addColumn<QString>("password");
+    QTest::addColumn<QString>("cdDir");
+    QTest::addColumn<QString>("oldfile");
+    QTest::addColumn<QString>("newfile");
+    QTest::addColumn<QString>("createFile");
+    QTest::addColumn<QString>("renamedFile");
+    QTest::addColumn<int>("success");
+
+    QTest::newRow("relPath01") << QtNetworkSettings::serverName() << QString() << QString()
+            << "qtest/upload"
+            << QString("rel_old01_%1") << QString("rel_new01_%1")
+            << QString("qtest/upload/rel_old01_%1") << QString("qtest/upload/rel_new01_%1")
+            << 1;
+    QTest::newRow("relPath02") << QtNetworkSettings::serverName() << QString("ftptest")     << "password"
+            << "qtest/upload"
+            << QString("rel_old02_%1") << QString("rel_new02_%1")
+            << QString("qtest/upload/rel_old02_%1") << QString("qtest/upload/rel_new02_%1")
+            << 1;
+    QTest::newRow("relPath03") << QtNetworkSettings::serverName() << QString("ftptest")     << "password"
+            << "qtest/upload"
+            << QString("rel_old03_%1")<< QString("rel_new03_%1")
+            << QString("qtest/upload/rel_old03_%1") << QString("qtest/upload/rel_new03_%1")
+            << 1;
+
+    QTest::newRow("absPath01") << QtNetworkSettings::serverName() << QString() << QString()
+            << QString()
+            << QString("/qtest/upload/abs_old01_%1") << QString("/qtest/upload/abs_new01_%1")
+            << QString("/qtest/upload/abs_old01_%1") << QString("/qtest/upload/abs_new01_%1")
+            << 1;
+    QTest::newRow("absPath02") << QtNetworkSettings::serverName() << QString("ftptest")     << "password"
+            << QString()
+            << QString("/var/ftp/qtest/upload/abs_old02_%1") << QString("/var/ftp/qtest/upload/abs_new02_%1")
+            << QString("/var/ftp/qtest/upload/abs_old02_%1") << QString("/var/ftp/qtest/upload/abs_new02_%1")
+            << 1;
+
+    QTest::newRow("nonExist01") << QtNetworkSettings::serverName() << QString() << QString()
+            << QString()
+            << QString("foo") << "new_foo"
+            << QString() << QString()
+            << 0;
+    QTest::newRow("nonExist02") << QtNetworkSettings::serverName() << QString() << QString()
+            << QString()
+            << QString("/foo") << QString("/new_foo")
+            << QString() << QString()
+            << 0;
+}
+
+void tst_QFtp::renameInit( const QString &host, const QString &user, const QString &password, const QString &createFile )
+{
+    if ( !createFile.isNull() ) {
+        // upload the file
+        init();
+        ftp = newFtp();
+        addCommand( QFtp::ConnectToHost, ftp->connectToHost( host ) );
+        addCommand( QFtp::Login, ftp->login( user, password ) );
+        addCommand( QFtp::Put, ftp->put( QByteArray(), createFile ) );
+        addCommand( QFtp::Close, ftp->close() );
+
+        QTestEventLoop::instance().enterLoop( 30 );
+        delete ftp;
+        if ( QTestEventLoop::instance().timeout() )
+            QFAIL( "Network operation timed out" );
+
+        ResMapIt it = resultMap.find( QFtp::Put );
+        QVERIFY( it != resultMap.end() );
+        QVERIFY( it.value().success == 1 );
+
+        QVERIFY( fileExists( host, 21, user, password, createFile ) );
+    }
+}
+
+void tst_QFtp::renameCleanup( const QString &host, const QString &user, const QString &password, const QString &fileToDelete )
+{
+    if ( !fileToDelete.isNull() ) {
+        // cleanup (i.e. remove the file)
+        init();
+        ftp = newFtp();
+        addCommand( QFtp::ConnectToHost, ftp->connectToHost( host ) );
+        addCommand( QFtp::Login, ftp->login( user, password ) );
+        addCommand( QFtp::Remove, ftp->remove( fileToDelete ) );
+        addCommand( QFtp::Close, ftp->close() );
+
+        QTestEventLoop::instance().enterLoop( 30 );
+        delete ftp;
+        if ( QTestEventLoop::instance().timeout() )
+            QFAIL( "Network operation timed out" );
+
+        ResMapIt it = resultMap.find( QFtp::Remove );
+        QVERIFY( it != resultMap.end() );
+        QVERIFY( it.value().success == 1 );
+
+        QVERIFY( !fileExists( host, 21, user, password, fileToDelete ) );
+    }
+}
+
+void tst_QFtp::rename()
+{
+    QFETCH( QString, host );
+    QFETCH( QString, user );
+    QFETCH( QString, password );
+    QFETCH( QString, cdDir );
+    QFETCH( QString, oldfile );
+    QFETCH( QString, newfile );
+    QFETCH( QString, createFile );
+    QFETCH( QString, renamedFile );
+
+    if(oldfile.contains('%'))
+        oldfile = oldfile.arg(uniqueExtension);
+    if(newfile.contains('%'))
+        newfile = newfile.arg(uniqueExtension);
+    if(createFile.contains('%'))
+        createFile = createFile.arg(uniqueExtension);
+    if(renamedFile.contains('%'))
+        renamedFile = renamedFile.arg(uniqueExtension);
+
+    renameInit( host, user, password, createFile );
+
+    init();
+    ftp = newFtp();
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host ) );
+    addCommand( QFtp::Login, ftp->login( user, password ) );
+    if ( !cdDir.isNull() )
+        addCommand( QFtp::Cd, ftp->cd( cdDir ) );
+    addCommand( QFtp::Rename, ftp->rename( oldfile, newfile ) );
+    addCommand( QFtp::Close, ftp->close() );
+
+    QTestEventLoop::instance().enterLoop( 30 );
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    ResMapIt it = resultMap.find( QFtp::Rename );
+    QVERIFY( it != resultMap.end() );
+    QTEST( it.value().success, "success" );
+
+    if ( it.value().success ) {
+        QVERIFY( !fileExists( host, 21, user, password, oldfile, cdDir ) );
+        QVERIFY( fileExists( host, 21, user, password, newfile, cdDir ) );
+        QVERIFY( fileExists( host, 21, user, password, renamedFile ) );
+    } else {
+        QVERIFY( !fileExists( host, 21, user, password, newfile, cdDir ) );
+        QVERIFY( !fileExists( host, 21, user, password, renamedFile ) );
+    }
+
+    renameCleanup( host, user, password, renamedFile );
+}
+
+/*
+  The commandSequence() test does not test any particular function. It rather
+  tests a sequence of arbitrary commands specified in the test data.
+*/
+class FtpCommand
+{
+public:
+    FtpCommand() :
+            cmd(QFtp::None)
+    { }
+
+    FtpCommand( QFtp::Command command ) :
+            cmd(command)
+    { }
+
+    FtpCommand( QFtp::Command command, const QStringList &arguments ) :
+            cmd(command), args(arguments)
+    { }
+
+    FtpCommand( const FtpCommand &c )
+    { *this = c; }
+
+    FtpCommand &operator=( const FtpCommand &c )
+                         {
+        this->cmd  = c.cmd;
+        this->args = c.args;
+        return *this;
+    }
+
+    QFtp::Command cmd;
+    QStringList args;
+};
+QDataStream &operator<<( QDataStream &s, const FtpCommand &command )
+{
+    s << (int)command.cmd;
+    s << command.args;
+    return s;
+}
+QDataStream &operator>>( QDataStream &s, FtpCommand &command )
+{
+    int tmp;
+    s >> tmp;
+    command.cmd = (QFtp::Command)tmp;
+    s >> command.args;
+    return s;
+}
+Q_DECLARE_METATYPE(QList<FtpCommand>)
+
+void tst_QFtp::commandSequence_data()
+{
+    // some "constants"
+    QStringList argConnectToHost01;
+    argConnectToHost01 << QtNetworkSettings::serverName() << "21";
+
+    QStringList argLogin01, argLogin02, argLogin03, argLogin04;
+    argLogin01 << QString() << QString();
+    argLogin02 << "ftp"         << QString();
+    argLogin03 << "ftp"         << "foo";
+    argLogin04 << QString("ftptest")     << "password";
+
+    FtpCommand connectToHost01( QFtp::ConnectToHost, argConnectToHost01 );
+    FtpCommand login01( QFtp::Login, argLogin01 );
+    FtpCommand login02( QFtp::Login, argLogin01 );
+    FtpCommand login03( QFtp::Login, argLogin01 );
+    FtpCommand login04( QFtp::Login, argLogin01 );
+    FtpCommand close01( QFtp::Close );
+
+    QTest::addColumn<QList<FtpCommand> >("cmds");
+    QTest::addColumn<int>("success");
+
+    // success data
+    {
+        QList<FtpCommand> cmds;
+        cmds << connectToHost01;
+        QTest::newRow( "simple_ok01" ) << cmds << 1;
+    }
+    {
+        QList<FtpCommand> cmds;
+        cmds << connectToHost01;
+        cmds << login01;
+        QTest::newRow( "simple_ok02" ) << cmds << 1;
+    }
+    {
+        QList<FtpCommand> cmds;
+        cmds << connectToHost01;
+        cmds << login01;
+        cmds << close01;
+        QTest::newRow( "simple_ok03" ) << cmds << 1;
+    }
+    {
+        QList<FtpCommand> cmds;
+        cmds << connectToHost01;
+        cmds << close01;
+        QTest::newRow( "simple_ok04" ) << cmds << 1;
+    }
+    {
+        QList<FtpCommand> cmds;
+        cmds << connectToHost01;
+        cmds << login01;
+        cmds << close01;
+        cmds << connectToHost01;
+        cmds << login02;
+        cmds << close01;
+        QTest::newRow( "connect_twice" ) << cmds << 1;
+    }
+
+    // error data
+    {
+        QList<FtpCommand> cmds;
+        cmds << close01;
+        QTest::newRow( "error01" ) << cmds << 0;
+    }
+    {
+        QList<FtpCommand> cmds;
+        cmds << login01;
+        QTest::newRow( "error02" ) << cmds << 0;
+    }
+    {
+        QList<FtpCommand> cmds;
+        cmds << login01;
+        cmds << close01;
+        QTest::newRow( "error03" ) << cmds << 0;
+    }
+    {
+        QList<FtpCommand> cmds;
+        cmds << connectToHost01;
+        cmds << login01;
+        cmds << close01;
+        cmds << login01;
+        QTest::newRow( "error04" ) << cmds << 0;
+    }
+}
+
+void tst_QFtp::commandSequence()
+{
+    QFETCH( QList<FtpCommand>, cmds );
+
+    ftp = newFtp();
+    QList<FtpCommand>::iterator it;
+    for ( it = cmds.begin(); it != cmds.end(); ++it ) {
+        switch ( (*it).cmd ) {
+        case QFtp::ConnectToHost:
+            {
+                QVERIFY( (*it).args.count() == 2 );
+                uint port;
+                bool portOk;
+                port = (*it).args[1].toUInt( &portOk );
+                QVERIFY( portOk );
+                ids << ftp->connectToHost( (*it).args[0], port );
+            }
+            break;
+        case QFtp::Login:
+            QVERIFY( (*it).args.count() == 2 );
+            ids << ftp->login( (*it).args[0], (*it).args[1] );
+            break;
+        case QFtp::Close:
+            QVERIFY( (*it).args.count() == 0 );
+            ids << ftp->close();
+            break;
+        default:
+            QFAIL( "Error in test: unexpected enum value" );
+            break;
+        }
+    }
+
+    QTestEventLoop::instance().enterLoop( 30 );
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    QTEST( commandSequence_success, "success" );
+}
+
+void tst_QFtp::abort_data()
+{
+    QTest::addColumn<QString>("host");
+    QTest::addColumn<uint>("port");
+    QTest::addColumn<QString>("file");
+    QTest::addColumn<QByteArray>("uploadData");
+
+    QTest::newRow( "get_fluke01" ) << QtNetworkSettings::serverName() << (uint)21 << QString("qtest/bigfile") << QByteArray();
+    QTest::newRow( "get_fluke02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("qtest/rfc3252") << QByteArray();
+
+    // Qt/CE and Symbian test environment has to less memory for this test
+#if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN)
+    QByteArray bigData( 10*1024*1024, 0 );
+    bigData.fill( 'B' );
+
+    QTest::newRow( "put_fluke01" ) << QtNetworkSettings::serverName() << (uint)21 << QString("qtest/upload/abort_put") << bigData;
+#endif
+}
+
+void tst_QFtp::abort()
+{
+    QFETCH( QString, host );
+    QFETCH( uint, port );
+    QFETCH( QString, file );
+    QFETCH( QByteArray, uploadData );
+
+    QFtp::Command cmd;
+    if ( uploadData.size() == 0 )
+        cmd = QFtp::Get;
+    else
+        cmd = QFtp::Put;
+
+    ftp = newFtp();
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+    addCommand( QFtp::Login, ftp->login() );
+    if ( cmd == QFtp::Get )
+        addCommand( cmd, ftp->get( file ) );
+    else
+        addCommand( cmd, ftp->put( uploadData, file ) );
+    addCommand( QFtp::Close, ftp->close() );
+
+    for(int time = 0; time <= uploadData.length() / 30000; time += 30) {
+        QTestEventLoop::instance().enterLoop( 30 );
+        if(ftp->currentCommand() == QFtp::None)
+            break;
+    }
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    ResMapIt it = resultMap.find( cmd );
+    QVERIFY( it != resultMap.end() );
+    // ### how to test the abort?
+    if ( it.value().success ) {
+        // The FTP server on fluke is sadly returning a success, even when
+        // the operation was aborted. So we have to use some heuristics.
+        if ( host == QtNetworkSettings::serverName() ) {
+            if ( cmd == QFtp::Get ) {
+                QVERIFY(bytesDone <= bytesTotal);
+            } else {
+                // put commands should always be aborted, since we use really
+                // big data
+                QVERIFY( bytesDone != bytesTotal );
+            }
+        } else {
+            // this could be tested by verifying that no more progress signals are emited
+            QVERIFY(bytesDone <= bytesTotal);
+        }
+    } else {
+        QVERIFY( bytesDone != bytesTotal );
+    }
+
+    if ( cmd == QFtp::Put ) {
+        //////////////////////////////////////
+        // cleanup (i.e. remove the file)
+        init();
+        ftp = newFtp();
+        addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+        addCommand( QFtp::Login, ftp->login() );
+        addCommand( QFtp::Remove, ftp->remove( file ) );
+        addCommand( QFtp::Close, ftp->close() );
+
+        QTestEventLoop::instance().enterLoop( 30 );
+        delete ftp;
+        if ( QTestEventLoop::instance().timeout() )
+            QFAIL( "Network operation timed out" );
+
+        it = resultMap.find( QFtp::Remove );
+        QVERIFY( it != resultMap.end() );
+        QVERIFY( it.value().success == 1 );
+    }
+}
+
+void tst_QFtp::bytesAvailable_data()
+{
+    QTest::addColumn<QString>("host");
+    QTest::addColumn<QString>("file");
+    QTest::addColumn<int>("type");
+    QTest::addColumn<qlonglong>("bytesAvailFinishedGet");
+    QTest::addColumn<qlonglong>("bytesAvailFinished");
+    QTest::addColumn<qlonglong>("bytesAvailDone");
+
+    QTest::newRow( "fluke01" ) << QtNetworkSettings::serverName() << QString("qtest/bigfile") << 0 << (qlonglong)519240 << (qlonglong)519240 << (qlonglong)519240;
+    QTest::newRow( "fluke02" ) << QtNetworkSettings::serverName() << QString("qtest/rfc3252") << 0 << (qlonglong)25962 << (qlonglong)25962 << (qlonglong)25962;
+
+    QTest::newRow( "fluke03" ) << QtNetworkSettings::serverName() << QString("qtest/bigfile") << 1 << (qlonglong)519240 << (qlonglong)0 << (qlonglong)0;
+    QTest::newRow( "fluke04" ) << QtNetworkSettings::serverName() << QString("qtest/rfc3252") << 1 << (qlonglong)25962 << (qlonglong)0 << (qlonglong)0;
+}
+
+void tst_QFtp::bytesAvailable()
+{
+    QFETCH( QString, host );
+    QFETCH( QString, file );
+    QFETCH( int, type );
+
+    ftp = newFtp();
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host ) );
+    addCommand( QFtp::Login, ftp->login() );
+    addCommand( QFtp::Get, ftp->get( file ) );
+    if ( type != 0 )
+        addCommand( QFtp::Close, ftp->close() );
+
+    QTestEventLoop::instance().enterLoop( 30 );
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    ResMapIt it = resultMap.find( QFtp::Get );
+    QVERIFY( it != resultMap.end() );
+    QVERIFY( it.value().success );
+
+    QFETCH(qlonglong, bytesAvailFinishedGet);
+    QCOMPARE(bytesAvailable_finishedGet, bytesAvailFinishedGet);
+
+    QFETCH(qlonglong, bytesAvailFinished);
+    QCOMPARE(bytesAvailable_finished, bytesAvailFinished);
+
+    QFETCH(qlonglong, bytesAvailDone);
+    QCOMPARE(bytesAvailable_done, bytesAvailDone);
+
+    ftp->readAll();
+    QVERIFY( ftp->bytesAvailable() == 0 );
+    delete ftp;
+}
+
+void tst_QFtp::activeMode()
+{
+    QFile file("tst_QFtp_activeMode_inittab");
+    file.open(QIODevice::ReadWrite);
+    QFtp ftp;
+    ftp.setTransferMode(QFtp::Active);
+    ftp.connectToHost(QtNetworkSettings::serverName(), 21);
+    ftp.login();
+    ftp.list();
+    ftp.get("/qtest/rfc3252.txt", &file);
+    connect(&ftp, SIGNAL(done(bool)), SLOT(activeModeDone(bool)));
+    QTestEventLoop::instance().enterLoop(900);
+    QFile::remove("tst_QFtp_activeMode_inittab");
+    QVERIFY(done_success == 1);
+
+}
+
+void tst_QFtp::activeModeDone(bool error)
+{
+    done_success = error ? -1 : 1;
+    QTestEventLoop::instance().exitLoop();
+}
+
+void tst_QFtp::proxy_data()
+{
+    QTest::addColumn<QString>("host");
+    QTest::addColumn<uint>("port");
+    QTest::addColumn<QString>("user");
+    QTest::addColumn<QString>("password");
+    QTest::addColumn<QString>("dir");
+    QTest::addColumn<int>("success");
+    QTest::addColumn<QStringList>("entryNames"); // ### we should rather use a QList<QUrlInfo> here
+
+    QStringList flukeRoot;
+    flukeRoot << "qtest";
+    QStringList flukeQtest;
+    flukeQtest << "bigfile";
+    flukeQtest << "nonASCII";
+    flukeQtest << "rfc3252";
+    flukeQtest << "rfc3252.txt";
+    flukeQtest << "upload";
+
+    QTest::newRow( "proxy_relPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("qtest") << 1 << flukeQtest;
+    QTest::newRow( "proxy_relPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest")     << QString("password")     << QString("qtest") << 1 << flukeQtest;
+
+    QTest::newRow( "proxy_absPath01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/qtest") << 1 << flukeQtest;
+    QTest::newRow( "proxy_absPath02" ) << QtNetworkSettings::serverName() << (uint)21 << QString("ftptest")     << QString("password")     << QString("/var/ftp/qtest") << 1 << flukeQtest;
+
+    QTest::newRow( "proxy_nonExist01" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("foo")  << 0 << QStringList();
+    QTest::newRow( "proxy_nonExist03" ) << QtNetworkSettings::serverName() << (uint)21 << QString() << QString() << QString("/foo") << 0 << QStringList();
+}
+
+void tst_QFtp::proxy()
+{
+    QFETCH( QString, host );
+    QFETCH( uint, port );
+    QFETCH( QString, user );
+    QFETCH( QString, password );
+    QFETCH( QString, dir );
+
+    ftp = newFtp();
+    addCommand( QFtp::SetProxy, ftp->setProxy( QtNetworkSettings::serverName(), 2121 ) );
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+    addCommand( QFtp::Login, ftp->login( user, password ) );
+    addCommand( QFtp::Cd, ftp->cd( dir ) );
+    addCommand( QFtp::List, ftp->list() );
+
+    QTestEventLoop::instance().enterLoop( 30 );
+
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() ) {
+        QFAIL( "Network operation timed out" );
+    }
+
+    ResMapIt it = resultMap.find( QFtp::Cd );
+    QVERIFY( it != resultMap.end() );
+    QFETCH( int, success );
+    QCOMPARE( it.value().success, success );
+    QFETCH( QStringList, entryNames );
+    QCOMPARE( listInfo_i.count(), entryNames.count() );
+    for ( uint i=0; i < (uint) entryNames.count(); i++ ) {
+        QCOMPARE( listInfo_i[i].name(), entryNames[i] );
+    }
+}
+
+
+void tst_QFtp::binaryAscii()
+{
+    QString file = "asciifile%1.txt";
+
+    if(file.contains('%'))
+        file = file.arg(uniqueExtension);
+
+    QByteArray putData = "a line of text\r\n";
+
+    init();
+    ftp = newFtp();
+    addCommand(QFtp::ConnectToHost, ftp->connectToHost(QtNetworkSettings::serverName(), 21));
+    addCommand(QFtp::Login, ftp->login("ftptest", "password"));
+    addCommand(QFtp::Cd, ftp->cd("qtest/upload"));
+    addCommand(QFtp::Put, ftp->put(putData, file, QFtp::Ascii));
+    addCommand(QFtp::Close, ftp->close());
+
+    QTestEventLoop::instance().enterLoop( 30 );
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    ResMapIt it = resultMap.find(QFtp::Put);
+    QVERIFY(it != resultMap.end());
+    QVERIFY(it.value().success);
+
+    QByteArray getData;
+    QBuffer getBuf(&getData);
+    getBuf.open(QBuffer::WriteOnly);
+
+    init();
+    ftp = newFtp();
+    addCommand(QFtp::ConnectToHost, ftp->connectToHost(QtNetworkSettings::serverName(), 21));
+    addCommand(QFtp::Login, ftp->login("ftptest", "password"));
+    addCommand(QFtp::Cd, ftp->cd("qtest/upload"));
+    addCommand(QFtp::Get, ftp->get(file, &getBuf, QFtp::Binary));
+    addCommand(QFtp::Close, ftp->close());
+
+    QTestEventLoop::instance().enterLoop( 30 );
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    ResMapIt it2 = resultMap.find(QFtp::Get);
+    QVERIFY(it2 != resultMap.end());
+    QVERIFY(it2.value().success);
+    // most modern ftp servers leave the file as it is by default
+    // (and do not remove the windows line ending), the -1 below could be
+    // deleted in the future
+    QVERIFY(getData.size() == putData.size()-1);
+    //////////////////////////////////////////////////////////////////
+    // cleanup (i.e. remove the file) -- this also tests the remove command
+    init();
+    ftp = newFtp();
+    addCommand(QFtp::ConnectToHost, ftp->connectToHost(QtNetworkSettings::serverName(), 21));
+    addCommand(QFtp::Login, ftp->login("ftptest", "password"));
+    addCommand(QFtp::Cd, ftp->cd("qtest/upload"));
+    addCommand(QFtp::Remove, ftp->remove(file));
+    addCommand(QFtp::Close, ftp->close());
+
+    QTestEventLoop::instance().enterLoop( 30 );
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() )
+        QFAIL( "Network operation timed out" );
+
+    it = resultMap.find( QFtp::Remove );
+    QVERIFY( it != resultMap.end() );
+    QCOMPARE( it.value().success, 1 );
+
+    QVERIFY(!fileExists(QtNetworkSettings::serverName(), 21, "ftptest", "password", file));
+}
+
+
+// test QFtp::currentId() and QFtp::currentCommand()
+#define CURRENTCOMMAND_TEST \
+{ \
+  ResMapIt it; \
+  for ( it = resultMap.begin(); it != resultMap.end(); ++it ) { \
+                                                                if ( it.value().id == ftp->currentId() ) { \
+                                                                                                           QVERIFY( it.key() == ftp->currentCommand() ); \
+                                                                                                       } \
+} \
+}
+
+void tst_QFtp::commandStarted( int id )
+{
+#if defined( DUMP_SIGNALS )
+    qDebug( "%d:commandStarted( %d )", ftp->currentId(), id );
+#endif
+    // make sure that the commandStarted and commandFinished are nested correctly
+    QVERIFY( current_id == 0 );
+    current_id = id;
+
+    QVERIFY( !ids.isEmpty() );
+    QVERIFY( ids.first() == id );
+    if ( ids.count() > 1 ) {
+        QVERIFY( ftp->hasPendingCommands() );
+    } else {
+        QVERIFY( !ftp->hasPendingCommands() );
+    }
+
+    QVERIFY( ftp->currentId() == id );
+    QVERIFY( cur_state == ftp->state() );
+    CURRENTCOMMAND_TEST;
+
+    QVERIFY( ftp->error() == QFtp::NoError );
+}
+
+void tst_QFtp::commandFinished( int id, bool error )
+{
+#if defined( DUMP_SIGNALS )
+    qDebug( "%d:commandFinished( %d, %d ) -- errorString: '%s'",
+            ftp->currentId(), id, (int)error, ftp->errorString().toLatin1().constData() );
+#endif
+    if ( ftp->currentCommand() == QFtp::Get ) {
+        bytesAvailable_finishedGet = ftp->bytesAvailable();
+    }
+    bytesAvailable_finished = ftp->bytesAvailable();
+
+    // make sure that the commandStarted and commandFinished are nested correctly
+    QVERIFY( current_id == id );
+    current_id = 0;
+
+    QVERIFY( !ids.isEmpty() );
+    QVERIFY( ids.first() == id );
+    if ( !error && ids.count() > 1) {
+        QVERIFY( ftp->hasPendingCommands() );
+    } else {
+        QVERIFY( !ftp->hasPendingCommands() );
+    }
+    if ( error ) {
+        QVERIFY( ftp->error() != QFtp::NoError );
+        ids.clear();
+    } else {
+        QVERIFY( ftp->error() == QFtp::NoError );
+        ids.pop_front();
+    }
+
+    QVERIFY( ftp->currentId() == id );
+    QVERIFY( cur_state == ftp->state() );
+    CURRENTCOMMAND_TEST;
+
+    if ( QTest::currentTestFunction() != QLatin1String("commandSequence") ) {
+        ResMapIt it = resultMap.find( ftp->currentCommand() );
+        QVERIFY( it != resultMap.end() );
+        QVERIFY( it.value().success == -1 );
+        if ( error )
+            it.value().success = 0;
+        else
+            it.value().success = 1;
+    }
+}
+
+void tst_QFtp::done( bool error )
+{
+#if defined( DUMP_SIGNALS )
+    qDebug( "%d:done( %d )", ftp->currentId(), (int)error );
+#endif
+    bytesAvailable_done = ftp->bytesAvailable();
+
+    QVERIFY( ftp->currentId() == 0 );
+    QVERIFY( current_id == 0 );
+    QVERIFY( ids.isEmpty() );
+    QVERIFY( cur_state == ftp->state() );
+    QVERIFY( !ftp->hasPendingCommands() );
+
+    if ( QTest::currentTestFunction() == QLatin1String("commandSequence") ) {
+        QVERIFY( commandSequence_success == -1 );
+        if ( error )
+            commandSequence_success = 0;
+        else
+            commandSequence_success = 1;
+    }
+    QVERIFY( done_success == -1 );
+    if ( error ) {
+        QVERIFY( ftp->error() != QFtp::NoError );
+        done_success = 0;
+    } else {
+        QVERIFY( ftp->error() == QFtp::NoError );
+        done_success = 1;
+    }
+    QTestEventLoop::instance().exitLoop();
+}
+
+void tst_QFtp::stateChanged( int state )
+{
+#if defined( DUMP_SIGNALS )
+    qDebug( "%d:  stateChanged( %d )", ftp->currentId(), state );
+#endif
+    QCOMPARE( ftp->currentId(), current_id );
+    CURRENTCOMMAND_TEST;
+
+    QVERIFY( state != cur_state );
+    QCOMPARE( state, (int)ftp->state() );
+    if ( state != QFtp::Unconnected ) {
+        // make sure that the states are always emitted in the right order (for
+        // this, we assume an ordering on the enum values, which they have at
+        // the moment)
+        QVERIFY( cur_state < state );
+
+        // make sure that state changes are only emitted in response to certain
+        // commands
+        switch ( state ) {
+        case QFtp::HostLookup:
+        case QFtp::Connecting:
+        case QFtp::Connected:
+            QCOMPARE( (int)ftp->currentCommand(), (int)QFtp::ConnectToHost );
+            break;
+        case QFtp::LoggedIn:
+            QCOMPARE( (int)ftp->currentCommand(), (int)QFtp::Login );
+            break;
+        case QFtp::Closing:
+            QCOMPARE( (int)ftp->currentCommand(), (int)QFtp::Close );
+            break;
+        default:
+            QWARN( QString("Unexpected state '%1'").arg(state).toLatin1().constData() );
+            break;
+        }
+    }
+    cur_state = state;
+
+    if ( QTest::currentTestFunction() == QLatin1String("connectToHost") ) {
+        switch ( state ) {
+        case QFtp::HostLookup:
+        case QFtp::Connecting:
+        case QFtp::LoggedIn:
+        case QFtp::Closing:
+            // ignore
+            break;
+        case QFtp::Connected:
+        case QFtp::Unconnected:
+            QVERIFY( connectToHost_state == -1 );
+            connectToHost_state = state;
+            break;
+        default:
+            QWARN( QString("Unknown state '%1'").arg(state).toLatin1().constData() );
+            break;
+        }
+    } else if ( QTest::currentTestFunction() == QLatin1String("close") ) {
+        ResMapIt it = resultMap.find( QFtp::Close );
+        if ( it!=resultMap.end() && ftp->currentId()==it.value().id ) {
+            if ( state == QFtp::Closing ) {
+                QVERIFY( close_state == -1 );
+                close_state = state;
+            } else if ( state == QFtp::Unconnected ) {
+                QVERIFY( close_state == QFtp::Closing );
+                close_state = state;
+            }
+        }
+    } else if ( QTest::currentTestFunction() == QLatin1String("login") ) {
+        ResMapIt it = resultMap.find( QFtp::Login );
+        if ( it!=resultMap.end() && ftp->currentId()==it.value().id ) {
+            if ( state == QFtp::LoggedIn ) {
+                QVERIFY( login_state == -1 );
+                login_state = state;
+            }
+        }
+    }
+}
+
+void tst_QFtp::listInfo( const QUrlInfo &i )
+{
+#if defined( DUMP_SIGNALS )
+    qDebug( "%d:  listInfo( %s )", ftp->currentId(), i.name().toLatin1().constData() );
+#endif
+    QCOMPARE( ftp->currentId(), current_id );
+    if ( ids.count() > 1 ) {
+        QVERIFY( ftp->hasPendingCommands() );
+    } else {
+        QVERIFY( !ftp->hasPendingCommands() );
+    }
+    QVERIFY( cur_state == ftp->state() );
+    CURRENTCOMMAND_TEST;
+
+    if ( QTest::currentTestFunction()==QLatin1String("list") || QTest::currentTestFunction()==QLatin1String("cd") || QTest::currentTestFunction()==QLatin1String("proxy") || inFileDirExistsFunction ) {
+        ResMapIt it = resultMap.find( QFtp::List );
+        QVERIFY( it != resultMap.end() );
+        QVERIFY( ftp->currentId() == it.value().id );
+        listInfo_i << i;
+    }
+}
+
+void tst_QFtp::readyRead()
+{
+#if defined( DUMP_SIGNALS )
+    qDebug( "%d:  readyRead(), bytesAvailable == %lu", ftp->currentId(), ftp->bytesAvailable() );
+#endif
+    QCOMPARE( ftp->currentId(), current_id );
+    if ( ids.count() > 1 ) {
+        QVERIFY( ftp->hasPendingCommands() );
+    } else {
+        QVERIFY( !ftp->hasPendingCommands() );
+    }
+    QVERIFY( cur_state == ftp->state() );
+    CURRENTCOMMAND_TEST;
+
+    if ( QTest::currentTestFunction() != QLatin1String("bytesAvailable") ) {
+        int oldSize = newData_ba.size();
+        qlonglong bytesAvail = ftp->bytesAvailable();
+        QByteArray ba = ftp->readAll();
+        QVERIFY( ba.size() == (int) bytesAvail );
+        newData_ba.resize( oldSize + ba.size() );
+        memcpy( newData_ba.data()+oldSize, ba.data(), ba.size() );
+
+        if ( bytesTotal != -1 ) {
+            QVERIFY( (int)newData_ba.size() <= bytesTotal );
+        }
+        QVERIFY( (int)newData_ba.size() == bytesDone );
+    }
+}
+
+void tst_QFtp::dataTransferProgress( qint64 done, qint64 total )
+{
+#if defined( DUMP_SIGNALS )
+    qDebug( "%d:  dataTransferProgress( %lli, %lli )", ftp->currentId(), done, total );
+#endif
+    QCOMPARE( ftp->currentId(), current_id );
+    if ( ids.count() > 1 ) {
+        QVERIFY( ftp->hasPendingCommands() );
+    } else {
+        QVERIFY( !ftp->hasPendingCommands() );
+    }
+    QVERIFY( cur_state == ftp->state() );
+    CURRENTCOMMAND_TEST;
+
+    if ( bytesTotal == bytesTotal_init ) {
+        bytesTotal = total;
+    } else {
+        QVERIFY( bytesTotal == total );
+    }
+
+    QVERIFY( bytesTotal != bytesTotal_init );
+    QVERIFY( bytesDone <= done );
+    bytesDone = done;
+    if ( bytesTotal != -1 ) {
+        QVERIFY( bytesDone <= bytesTotal );
+    }
+
+    if ( QTest::currentTestFunction() == QLatin1String("abort") ) {
+        // ### it would be nice if we could specify in our testdata when to do
+        // the abort
+        if ( done >= total/100000 ) {
+            if ( ids.count() != 1 ) {
+                // do abort only once
+                int tmpId = ids.first();
+                ids.clear();
+                ids << tmpId;
+                ftp->abort();
+            }
+        }
+    }
+}
+
+
+QFtp *tst_QFtp::newFtp()
+{
+    QFtp *nFtp = new QFtp( this );
+    connect( nFtp, SIGNAL(commandStarted(int)),
+             SLOT(commandStarted(int)) );
+    connect( nFtp, SIGNAL(commandFinished(int,bool)),
+             SLOT(commandFinished(int,bool)) );
+    connect( nFtp, SIGNAL(done(bool)),
+             SLOT(done(bool)) );
+    connect( nFtp, SIGNAL(stateChanged(int)),
+             SLOT(stateChanged(int)) );
+    connect( nFtp, SIGNAL(listInfo(const QUrlInfo&)),
+             SLOT(listInfo(const QUrlInfo&)) );
+    connect( nFtp, SIGNAL(readyRead()),
+             SLOT(readyRead()) );
+    connect( nFtp, SIGNAL(dataTransferProgress(qint64, qint64)),
+             SLOT(dataTransferProgress(qint64, qint64)) );
+
+    return nFtp;
+}
+
+void tst_QFtp::addCommand( QFtp::Command cmd, int id )
+{
+    ids << id;
+    CommandResult res;
+    res.id = id;
+    res.success = -1;
+    resultMap[ cmd ] = res;
+}
+
+bool tst_QFtp::fileExists( const QString &host, quint16 port, const QString &user, const QString &password, const QString &file, const QString &cdDir )
+{
+    init();
+    ftp = newFtp();
+    // ### make these tests work
+    if (ftp->currentId() != 0) {
+        qWarning("ftp->currentId() != 0");
+        return FALSE;
+    }
+
+    if (ftp->state() != QFtp::Unconnected) {
+        qWarning("ftp->state() != QFtp::Unconnected");
+        return FALSE;
+    }
+
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+    addCommand( QFtp::Login, ftp->login( user, password ) );
+    if ( !cdDir.isNull() )
+        addCommand( QFtp::Cd, ftp->cd( cdDir ) );
+    addCommand( QFtp::List, ftp->list( file ) );
+    addCommand( QFtp::Close, ftp->close() );
+
+    inFileDirExistsFunction = TRUE;
+    QTestEventLoop::instance().enterLoop( 30 );
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() ) {
+        // ### make this test work
+        qWarning("Network operation timed out");
+        return FALSE;
+    }
+    inFileDirExistsFunction = FALSE;
+
+    ResMapIt it = resultMap.find( QFtp::ConnectToHost );
+    // ### make these tests work
+    if (it == resultMap.end()) {
+        qWarning("it != resultMap.end()");
+        return FALSE;
+    }
+
+    if (it.value().success == -1) {
+        qWarning("it.value().success != -1");
+        return FALSE;
+    }
+
+    if ( it.value().success == 1 ) {
+        for ( uint i=0; i < (uint) listInfo_i.count(); i++ ) {
+            if ( QFileInfo(listInfo_i[i].name()).fileName() == QFileInfo(file).fileName() )
+                return TRUE;
+        }
+    }
+
+    //this is not a good warning considering sometime this function is used to test that a file does not exist
+    //qWarning("file doesn't exist");
+    return FALSE;
+}
+
+bool tst_QFtp::dirExists( const QString &host, quint16 port, const QString &user, const QString &password, const QString &cdDir, const QString &dirToCreate )
+{
+    init();
+    ftp = newFtp();
+    // ### make these tests work
+    //    QCOMPARE( ftp->currentId(), 0 );
+    //    QCOMPARE( (int)ftp->state(), (int)QFtp::Unconnected );
+
+    addCommand( QFtp::ConnectToHost, ftp->connectToHost( host, port ) );
+    addCommand( QFtp::Login, ftp->login( user, password ) );
+    if ( dirToCreate.startsWith( "/" ) )
+        addCommand( QFtp::Cd, ftp->cd( dirToCreate ) );
+    else
+        addCommand( QFtp::Cd, ftp->cd( cdDir + "/" + dirToCreate ) );
+    addCommand( QFtp::Close, ftp->close() );
+
+    inFileDirExistsFunction = TRUE;
+    QTestEventLoop::instance().enterLoop( 30 );
+    delete ftp;
+    if ( QTestEventLoop::instance().timeout() ) {
+        // ### make this test work
+        // QFAIL( "Network operation timed out" );
+        return FALSE;
+    }
+    inFileDirExistsFunction = FALSE;
+
+    ResMapIt it = resultMap.find( QFtp::Cd );
+    // ### make these tests work
+    //    QVERIFY( it != resultMap.end() );
+    //    QVERIFY( it.value().success != -1 );
+    return it.value().success == 1;
+}
+
+void tst_QFtp::doneSignal()
+{
+    QFtp ftp;
+    QSignalSpy spy(&ftp, SIGNAL(done(bool)));
+
+    ftp.connectToHost(QtNetworkSettings::serverName());
+    ftp.login("anonymous");
+    ftp.list();
+    ftp.close();
+
+    done_success = 0;
+    while ( ftp.hasPendingCommands() )
+        QCoreApplication::instance()->processEvents();
+    QTest::qWait(200);
+
+    QCOMPARE(spy.count(), 1);
+    QCOMPARE(spy.first().first().toBool(), false);
+}
+
+void tst_QFtp::queueMoreCommandsInDoneSlot()
+{
+    QSKIP("Task 127050 && 113966", SkipSingle);
+
+    QFtp ftp;
+    QSignalSpy doneSpy(&ftp, SIGNAL(done(bool)));
+    QSignalSpy commandFinishedSpy(&ftp, SIGNAL(commandFinished(int, bool)));
+
+    this->ftp = &ftp;
+    connect(&ftp, SIGNAL(done(bool)), this, SLOT(cdUpSlot(bool)));
+
+    ftp.connectToHost("ftp.qt.nokia.com");
+    ftp.login();
+    ftp.cd("qt");
+    ftp.rmdir("qtest-removedir-noexist");
+
+    while ( ftp.hasPendingCommands() || ftp.currentCommand() != QFtp::None ) {
+        QCoreApplication::instance()->processEvents(QEventLoop::AllEvents
+                                                    | QEventLoop::WaitForMoreEvents);
+    }
+
+    QCOMPARE(doneSpy.count(), 2);
+    QCOMPARE(doneSpy.first().first().toBool(), true);
+    QCOMPARE(doneSpy.last().first().toBool(), false);
+
+    QCOMPARE(commandFinishedSpy.count(), 6);
+    int firstId = commandFinishedSpy.at(0).at(0).toInt();
+    QCOMPARE(commandFinishedSpy.at(0).at(1).toBool(), false);
+    QCOMPARE(commandFinishedSpy.at(1).at(0).toInt(), firstId + 1);
+    QCOMPARE(commandFinishedSpy.at(1).at(1).toBool(), false);
+    QCOMPARE(commandFinishedSpy.at(2).at(0).toInt(), firstId + 2);
+    QCOMPARE(commandFinishedSpy.at(2).at(1).toBool(), false);
+    QCOMPARE(commandFinishedSpy.at(3).at(0).toInt(), firstId + 3);
+    QCOMPARE(commandFinishedSpy.at(3).at(1).toBool(), true);
+    QCOMPARE(commandFinishedSpy.at(4).at(0).toInt(), firstId + 4);
+    QCOMPARE(commandFinishedSpy.at(4).at(1).toBool(), false);
+    QCOMPARE(commandFinishedSpy.at(5).at(0).toInt(), firstId + 5);
+    QCOMPARE(commandFinishedSpy.at(5).at(1).toBool(), false);
+
+    this->ftp = 0;
+}
+
+void tst_QFtp::cdUpSlot(bool error)
+{
+    if (error) {
+        ftp->cd("..");
+        ftp->cd("qt");
+    }
+}
+
+QTEST_MAIN(tst_QFtp)
+
+#include "tst_qftp.moc"