tests/auto/qudpsocket/tst_qudpsocket.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/auto/qudpsocket/tst_qudpsocket.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,854 @@
+/****************************************************************************
+**
+** 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 <qfileinfo.h>
+#include <qdatastream.h>
+#include <qudpsocket.h>
+#include <qhostaddress.h>
+#include <qhostinfo.h>
+#include <qmap.h>
+#ifdef TEST_QNETWORK_PROXY
+# include <QNetworkProxy>
+#endif
+
+
+#include <qstringlist.h>
+#include "../network-settings.h"
+
+Q_DECLARE_METATYPE(QHostAddress)
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+QT_FORWARD_DECLARE_CLASS(QUdpSocket)
+
+class tst_QUdpSocket : public QObject
+{
+    Q_OBJECT
+
+public:
+    tst_QUdpSocket();
+    virtual ~tst_QUdpSocket();
+
+
+public slots:
+    void initTestCase_data();
+    void init();
+    void cleanup();
+private slots:
+    void constructing();
+    void unconnectedServerAndClientTest();
+    void broadcasting();
+    void loop_data();
+    void loop();
+    void ipv6Loop_data();
+    void ipv6Loop();
+    void readLine();
+    void pendingDatagramSize();
+    void writeDatagram();
+    void performance();
+    void bindMode();
+    void writeDatagramToNonExistingPeer_data();
+    void writeDatagramToNonExistingPeer();
+    void writeToNonExistingPeer_data();
+    void writeToNonExistingPeer();
+    void outOfProcessConnectedClientServerTest();
+    void outOfProcessUnconnectedClientServerTest();
+    void zeroLengthDatagram();
+
+protected slots:
+    void empty_readyReadSlot();
+    void empty_connectedSlot();
+};
+
+tst_QUdpSocket::tst_QUdpSocket()
+{
+    Q_SET_DEFAULT_IAP
+}
+
+tst_QUdpSocket::~tst_QUdpSocket()
+{
+}
+
+void tst_QUdpSocket::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);
+#endif
+}
+
+void tst_QUdpSocket::init()
+{
+    QFETCH_GLOBAL(bool, setProxy);
+    if (setProxy) {
+#ifdef TEST_QNETWORK_PROXY
+        QFETCH_GLOBAL(int, proxyType);
+        if (proxyType == QNetworkProxy::Socks5Proxy) {
+            QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080));
+        }
+#endif
+    }
+}
+
+void tst_QUdpSocket::cleanup()
+{
+    QFETCH_GLOBAL(bool, setProxy);
+    if (setProxy) {
+#ifdef TEST_QNETWORK_PROXY
+        QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy);
+#endif
+    }
+}
+
+
+//----------------------------------------------------------------------------------
+
+void tst_QUdpSocket::constructing()
+{
+    QUdpSocket socket;
+
+    QVERIFY(socket.isSequential());
+    QVERIFY(!socket.isOpen());
+    QVERIFY(socket.socketType() == QUdpSocket::UdpSocket);
+    QCOMPARE((int) socket.bytesAvailable(), 0);
+    QCOMPARE(socket.canReadLine(), false);
+    QCOMPARE(socket.readLine(), QByteArray());
+    QCOMPARE(socket.socketDescriptor(), -1);
+    QCOMPARE(socket.error(), QUdpSocket::UnknownSocketError);
+    QCOMPARE(socket.errorString(), QString("Unknown error"));
+
+    // Check the state of the socket api
+}
+
+void tst_QUdpSocket::unconnectedServerAndClientTest()
+{
+    QUdpSocket serverSocket;
+
+    qRegisterMetaType<QAbstractSocket::SocketState>("QAbstractSocket::SocketState");
+
+    QSignalSpy stateChangedSpy(&serverSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)));
+    QVERIFY2(serverSocket.bind(), serverSocket.errorString().toLatin1().constData());
+    QCOMPARE(stateChangedSpy.count(), 1);
+
+    const char *message[] = {"Yo mista", "Yo", "Wassap"};
+
+    QHostAddress serverAddress = QHostAddress::LocalHost;
+    if (!(serverSocket.localAddress() == QHostAddress::Any))
+        serverAddress = serverSocket.localAddress();
+
+    for (int i = 0; i < 3; ++i) {
+        QUdpSocket clientSocket;
+        QCOMPARE(int(clientSocket.writeDatagram(message[i], strlen(message[i]),
+                                               serverAddress, serverSocket.localPort())),
+                int(strlen(message[i])));
+        char buf[1024];
+        QHostAddress host;
+        quint16 port;
+        QVERIFY(serverSocket.waitForReadyRead(5000));
+        QCOMPARE(int(serverSocket.readDatagram(buf, sizeof(buf), &host, &port)),
+                int(strlen(message[i])));
+        buf[strlen(message[i])] = '\0';
+        QCOMPARE(QByteArray(buf), QByteArray(message[i]));
+    }
+}
+
+//----------------------------------------------------------------------------------
+
+void tst_QUdpSocket::broadcasting()
+{
+    QFETCH_GLOBAL(bool, setProxy);
+    if (setProxy) {
+#ifdef TEST_QNETWORK_PROXY
+        QFETCH_GLOBAL(int, proxyType);
+        if (proxyType == QNetworkProxy::Socks5Proxy) {
+            QSKIP("With socks5 Broadcast is not supported.", SkipSingle);
+        }
+#endif
+    }
+#ifdef Q_OS_AIX
+    QSKIP("Broadcast does not work on darko", SkipAll);
+#endif
+    const char *message[] = {"Yo mista", "", "Yo", "Wassap"};
+
+    for (int i = 0; i < 4; ++i) {
+        QUdpSocket serverSocket;
+        QVERIFY2(serverSocket.bind(QHostAddress::Any, 5000), serverSocket.errorString().toLatin1().constData());
+
+        QCOMPARE(serverSocket.state(), QUdpSocket::BoundState);
+
+        connect(&serverSocket, SIGNAL(readyRead()), SLOT(empty_readyReadSlot()));
+
+        QUdpSocket broadcastSocket;
+
+        for (int j = 0; j < 100; ++j) {
+            broadcastSocket.writeDatagram(message[i], strlen(message[i]),
+                QHostAddress::Broadcast, 5000);
+            QTestEventLoop::instance().enterLoop(15);
+            if (QTestEventLoop::instance().timeout()) {
+#if defined(Q_OS_FREEBSD)
+                QEXPECT_FAIL("",
+                             "Broadcasting to 255.255.255.255 does not work on FreeBSD",
+                             Abort);
+                QVERIFY(false); // seems that QFAIL() doesn't respect the QEXPECT_FAIL() :/
+#endif
+                QFAIL("Network operation timed out");
+            }
+            QVERIFY(serverSocket.hasPendingDatagrams());
+
+            do {
+                QByteArray arr; arr.resize(serverSocket.pendingDatagramSize() + 1);
+                QHostAddress host;
+                quint16 port;
+                QCOMPARE((int) serverSocket.readDatagram(arr.data(), arr.size() - 1, &host, &port),
+                    (int) strlen(message[i]));
+                arr.resize(strlen(message[i]));
+                QCOMPARE(arr, QByteArray(message[i]));
+            } while (serverSocket.hasPendingDatagrams());
+        }
+    }
+}
+
+//----------------------------------------------------------------------------------
+
+void tst_QUdpSocket::loop_data()
+{
+    QTest::addColumn<QByteArray>("peterMessage");
+    QTest::addColumn<QByteArray>("paulMessage");
+    QTest::addColumn<bool>("success");
+
+    QTest::newRow("\"Almond!\" | \"Joy!\"") << QByteArray("Almond!") << QByteArray("Joy!") << true;
+    QTest::newRow("\"A\" | \"B\"") << QByteArray("A") << QByteArray("B") << true;
+    QTest::newRow("\"AB\" | \"B\"") << QByteArray("AB") << QByteArray("B") << true;
+    QTest::newRow("\"AB\" | \"BB\"") << QByteArray("AB") << QByteArray("BB") << true;
+    QTest::newRow("\"A\\0B\" | \"B\\0B\"") << QByteArray::fromRawData("A\0B", 3) << QByteArray::fromRawData("B\0B", 3) << true;
+    QTest::newRow("\"(nil)\" | \"(nil)\"") << QByteArray() << QByteArray() << true;
+    QTest::newRow("Bigmessage") << QByteArray(600, '@') << QByteArray(600, '@') << true;
+}
+
+void tst_QUdpSocket::loop()
+{
+    QFETCH(QByteArray, peterMessage);
+    QFETCH(QByteArray, paulMessage);
+    QFETCH(bool, success);
+
+    QUdpSocket peter;
+    QUdpSocket paul;
+
+    QVERIFY2(peter.bind(), peter.errorString().toLatin1().constData());
+    QVERIFY2(paul.bind(), paul.errorString().toLatin1().constData());
+
+    QHostAddress peterAddress = QHostAddress::LocalHost;
+    if (!(peter.localAddress() == QHostAddress::Any))
+        peterAddress = peter.localAddress();
+    QHostAddress pualAddress = QHostAddress::LocalHost;
+    if (!(paul.localAddress() == QHostAddress::Any))
+        pualAddress = paul.localAddress();
+
+    QCOMPARE(peter.writeDatagram(peterMessage.data(), peterMessage.length(),
+                                pualAddress, paul.localPort()), qint64(peterMessage.length()));
+    QCOMPARE(paul.writeDatagram(paulMessage.data(), paulMessage.length(),
+                               peterAddress, peter.localPort()), qint64(paulMessage.length()));
+
+    QVERIFY(peter.waitForReadyRead(5000));
+    QVERIFY(paul.waitForReadyRead(5000));
+    char peterBuffer[16*1024];
+    char paulBuffer[16*1024];
+    if (success) {
+        QCOMPARE(peter.readDatagram(peterBuffer, sizeof(peterBuffer)), qint64(paulMessage.length()));
+        QCOMPARE(paul.readDatagram(paulBuffer, sizeof(peterBuffer)), qint64(peterMessage.length()));
+    } else {
+        QVERIFY(peter.readDatagram(peterBuffer, sizeof(peterBuffer)) != paulMessage.length());
+        QVERIFY(paul.readDatagram(paulBuffer, sizeof(peterBuffer)) != peterMessage.length());
+    }
+
+    QCOMPARE(QByteArray(peterBuffer, paulMessage.length()), paulMessage);
+    QCOMPARE(QByteArray(paulBuffer, peterMessage.length()), peterMessage);
+}
+
+//----------------------------------------------------------------------------------
+
+void tst_QUdpSocket::ipv6Loop_data()
+{
+    loop_data();
+}
+
+void tst_QUdpSocket::ipv6Loop()
+{
+#if defined(Q_OS_SYMBIAN)
+    QSKIP("Symbian IPv6 is not yet supported", SkipAll);
+#endif
+    QFETCH(QByteArray, peterMessage);
+    QFETCH(QByteArray, paulMessage);
+    QFETCH(bool, success);
+
+    QUdpSocket peter;
+    QUdpSocket paul;
+
+    quint16 peterPort = 28124;
+    quint16 paulPort = 28123;
+
+    if (!peter.bind(QHostAddress::LocalHostIPv6, peterPort)) {
+    QCOMPARE(peter.error(), QUdpSocket::UnsupportedSocketOperationError);
+    } else {
+    QVERIFY(paul.bind(QHostAddress::LocalHostIPv6, paulPort));
+
+    QCOMPARE(peter.writeDatagram(peterMessage.data(), peterMessage.length(), QHostAddress("::1"),
+                                    paulPort), qint64(peterMessage.length()));
+    QCOMPARE(paul.writeDatagram(paulMessage.data(), paulMessage.length(),
+                                   QHostAddress("::1"), peterPort), qint64(paulMessage.length()));
+
+    char peterBuffer[16*1024];
+    char paulBuffer[16*1024];
+#if !defined(Q_OS_WINCE)
+        QVERIFY(peter.waitForReadyRead(5000));
+        QVERIFY(paul.waitForReadyRead(5000));
+#else
+        QVERIFY(peter.waitForReadyRead(15000));
+        QVERIFY(paul.waitForReadyRead(15000));
+#endif
+    if (success) {
+        QCOMPARE(peter.readDatagram(peterBuffer, sizeof(peterBuffer)), qint64(paulMessage.length()));
+        QCOMPARE(paul.readDatagram(paulBuffer, sizeof(peterBuffer)), qint64(peterMessage.length()));
+    } else {
+        QVERIFY(peter.readDatagram(peterBuffer, sizeof(peterBuffer)) != paulMessage.length());
+        QVERIFY(paul.readDatagram(paulBuffer, sizeof(peterBuffer)) != peterMessage.length());
+    }
+
+    QCOMPARE(QByteArray(peterBuffer, paulMessage.length()), paulMessage);
+    QCOMPARE(QByteArray(paulBuffer, peterMessage.length()), peterMessage);
+    }
+}
+
+void tst_QUdpSocket::empty_readyReadSlot()
+{
+    QTestEventLoop::instance().exitLoop();
+}
+
+void tst_QUdpSocket::empty_connectedSlot()
+{
+    QTestEventLoop::instance().exitLoop();
+}
+
+//----------------------------------------------------------------------------------
+
+void tst_QUdpSocket::readLine()
+{
+    QUdpSocket socket1;
+    QVERIFY2(socket1.bind(), socket1.errorString().toLatin1().constData());
+
+    QUdpSocket socket2;
+    socket2.connectToHost("127.0.0.1", socket1.localPort());
+    QVERIFY(socket2.waitForConnected(5000));
+}
+
+//----------------------------------------------------------------------------------
+
+void tst_QUdpSocket::pendingDatagramSize()
+{
+    QUdpSocket server;
+    QVERIFY2(server.bind(), server.errorString().toLatin1().constData());
+
+    QHostAddress serverAddress = QHostAddress::LocalHost;
+    if (!(server.localAddress() == QHostAddress::Any))
+        serverAddress = server.localAddress();
+
+    QUdpSocket client;
+    QVERIFY(client.writeDatagram("this is", 7, serverAddress, server.localPort()) == 7);
+    QVERIFY(client.writeDatagram(0, 0, serverAddress, server.localPort()) == 0);
+    QVERIFY(client.writeDatagram("3 messages", 10, serverAddress, server.localPort()) == 10);
+
+    char c = 0;
+    QVERIFY(server.waitForReadyRead());
+    if (server.hasPendingDatagrams()) {
+#if defined Q_OS_HPUX && defined __ia64
+        QEXPECT_FAIL("", "HP-UX 11i v2 can't determine the datagram size correctly.", Abort);
+#endif
+        QCOMPARE(server.pendingDatagramSize(), qint64(7));
+        c = '\0';
+        QCOMPARE(server.readDatagram(&c, 1), qint64(1));
+        QCOMPARE(c, 't');
+        c = '\0';
+    } else {
+        QSKIP("does not have the 1st datagram", SkipSingle);
+    }
+
+    if (server.hasPendingDatagrams()) {
+        QCOMPARE(server.pendingDatagramSize(), qint64(0));
+        QCOMPARE(server.readDatagram(&c, 1), qint64(0));
+        QCOMPARE(c, '\0'); // untouched
+    c = '\0';
+    } else {
+        QSKIP("does not have the 2nd datagram", SkipSingle);
+    }
+
+    if (server.hasPendingDatagrams()) {
+        QCOMPARE(server.pendingDatagramSize(), qint64(10));
+        QCOMPARE(server.readDatagram(&c, 1), qint64(1));
+        QCOMPARE(c, '3');
+    } else {
+        QSKIP("does not have the 3rd datagram", SkipSingle);
+    }
+}
+
+
+void tst_QUdpSocket::writeDatagram()
+{
+    QUdpSocket server;
+    QVERIFY2(server.bind(), server.errorString().toLatin1().constData());
+
+    QHostAddress serverAddress = QHostAddress::LocalHost;
+    if (!(server.localAddress() == QHostAddress::Any))
+        serverAddress = server.localAddress();
+
+    QUdpSocket client;
+
+    qRegisterMetaType<qint64>("qint64");
+    qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
+
+    for(int i=0;;i++) {
+        QSignalSpy errorspy(&client, SIGNAL(error(QAbstractSocket::SocketError)));
+        QSignalSpy bytesspy(&client, SIGNAL(bytesWritten(qint64)));
+
+        qint64 written = client.writeDatagram(QByteArray(i * 1024, 'w'), serverAddress,
+                                                  server.localPort());
+
+        if (written != i * 1024) {
+#if defined (Q_OS_HPUX)
+            QSKIP("HP-UX 11.11 on hai (PA-RISC 64) truncates too long datagrams.", SkipSingle);
+#endif
+            QCOMPARE(bytesspy.count(), 0);
+            QCOMPARE(errorspy.count(), 1);
+            QCOMPARE(*static_cast<const int *>(errorspy.at(0).at(0).constData()),
+                    int(QUdpSocket::DatagramTooLargeError));
+            QCOMPARE(client.error(), QUdpSocket::DatagramTooLargeError);
+            break;
+        }
+        QVERIFY(bytesspy.count() == 1);
+        QCOMPARE(*static_cast<const qint64 *>(bytesspy.at(0).at(0).constData()),
+                qint64(i * 1024));
+        QCOMPARE(errorspy.count(), 0);
+        if (!server.waitForReadyRead(5000))
+            QSKIP(QString("UDP packet lost at size %1, unable to complete the test.").arg(i * 1024).toLatin1().data(), SkipSingle);
+        QCOMPARE(server.pendingDatagramSize(), qint64(i * 1024));
+        QCOMPARE(server.readDatagram(0, 0), qint64(0));
+    }
+}
+
+void tst_QUdpSocket::performance()
+{
+#if defined(Q_OS_SYMBIAN)
+    // Large packets seems not to go through on Symbian
+    // Reason might be also fragmentation due to VPN connection etc
+
+    QFETCH_GLOBAL(bool, setProxy);
+    QFETCH_GLOBAL(int, proxyType);
+
+    int arrSize = 8192;
+    if (setProxy && proxyType == QNetworkProxy::Socks5Proxy)
+        arrSize = 1024;
+
+    QByteArray arr(arrSize, '@');
+#else
+    QByteArray arr(8192, '@');
+#endif // Q_OS_SYMBIAN
+
+    QUdpSocket server;
+    QVERIFY2(server.bind(), server.errorString().toLatin1().constData());
+
+    QHostAddress serverAddress = QHostAddress::LocalHost;
+    if (!(server.localAddress() == QHostAddress::Any))
+        serverAddress = server.localAddress();
+
+    QUdpSocket client;
+    client.connectToHost(serverAddress, server.localPort());
+
+    QTime stopWatch;
+    stopWatch.start();
+
+    qint64 nbytes = 0;
+    while (stopWatch.elapsed() < 5000) {
+        for (int i = 0; i < 100; ++i) {
+            if (client.write(arr.data(), arr.size()) > 0) {
+                do {
+                    nbytes += server.readDatagram(arr.data(), arr.size());
+                } while (server.hasPendingDatagrams());
+            }
+        }
+    }
+
+    float secs = stopWatch.elapsed() / 1000.0;
+    qDebug("\t%.2fMB/%.2fs: %.2fMB/s", float(nbytes / (1024.0*1024.0)),
+           secs, float(nbytes / (1024.0*1024.0)) / secs);
+
+#if defined(Q_OS_SYMBIAN)
+    if(nbytes == 0) {
+        qDebug("No bytes passed through local UDP socket, since UDP socket write returns EWOULDBLOCK");
+        qDebug("Should try with blocking sockets, but it is not currently possible due to Open C defect");
+    }
+#endif
+
+}
+
+void tst_QUdpSocket::bindMode()
+{
+    QFETCH_GLOBAL(bool, setProxy);
+    if (setProxy) {
+#ifdef TEST_QNETWORK_PROXY
+        QFETCH_GLOBAL(int, proxyType);
+        if (proxyType == QNetworkProxy::Socks5Proxy) {
+            QSKIP("With socks5 explicit port binding is not supported.", SkipAll);
+        }
+#endif
+    }
+
+    QUdpSocket socket;
+    QVERIFY2(socket.bind(), socket.errorString().toLatin1().constData());
+    QUdpSocket socket2;
+    QVERIFY(!socket2.bind(socket.localPort()));
+#if defined(Q_OS_SYMBIAN)
+    if(RProcess().HasCapability(ECapabilityNetworkControl)) {
+        qDebug("Test executed *with* NetworkControl capability");
+        // In Symbian OS ReuseAddressHint together with NetworkControl capability
+        // gives application *always* right to bind to port. I.e. it does not matter
+        // if first socket was bound with any bind flag. Since autotests in Symbian
+        // are currently executed with ALL -TCB rights, this path is the one executed.
+        QVERIFY(socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint));
+        socket.close();
+        socket2.close();
+
+        QVERIFY2(socket.bind(0, QUdpSocket::ShareAddress), socket.errorString().toLatin1().constData());
+        QVERIFY(!socket2.bind(socket.localPort()));
+        QVERIFY2(socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint), socket2.errorString().toLatin1().constData());
+        socket.close();
+        socket2.close();
+
+        QVERIFY2(socket.bind(0, QUdpSocket::DontShareAddress), socket.errorString().toLatin1().constData());
+        QVERIFY(!socket2.bind(socket.localPort()));
+        QVERIFY(socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint));
+        socket.close();
+        socket2.close();
+    } else {
+        qDebug("Test executed *without* NetworkControl capability");
+        // If we don't have NetworkControl capability, attempt to bind already bound
+        // address will *always* fail. I.e. it does not matter if first socket was
+        // bound with any bind flag.
+        QVERIFY(!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint));
+        socket.close();
+
+        QVERIFY2(socket.bind(0, QUdpSocket::ShareAddress), socket.errorString().toLatin1().constData());
+        QVERIFY(!socket2.bind(socket.localPort()));
+        QVERIFY2(!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint), socket2.errorString().toLatin1().constData());
+        socket.close();
+
+        QVERIFY2(socket.bind(0, QUdpSocket::DontShareAddress), socket.errorString().toLatin1().constData());
+        QVERIFY(!socket2.bind(socket.localPort()));
+        QVERIFY(!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint));
+        socket.close();
+    }
+#elif defined(Q_OS_UNIX)
+    QVERIFY(!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint));
+    socket.close();
+    QVERIFY2(socket.bind(0, QUdpSocket::ShareAddress), socket.errorString().toLatin1().constData());
+    QVERIFY2(socket2.bind(socket.localPort()), socket2.errorString().toLatin1().constData());
+    socket2.close();
+    QVERIFY2(socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint), socket2.errorString().toLatin1().constData());
+#else
+
+    // Depending on the user's privileges, this or will succeed or
+    // fail. Admins are allowed to reuse the address, but nobody else.
+    if (!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint), socket2.errorString().toLatin1().constData())
+        qWarning("Failed to bind with QUdpSocket::ReuseAddressHint, user isn't an adminstrator?");
+    socket.close();
+    QVERIFY2(socket.bind(0, QUdpSocket::ShareAddress), socket.errorString().toLatin1().constData());
+    QVERIFY(!socket2.bind(socket.localPort()));
+    socket.close();
+    QVERIFY2(socket.bind(0, QUdpSocket::DontShareAddress), socket.errorString().toLatin1().constData());
+    QVERIFY(!socket2.bind(socket.localPort()));
+    QVERIFY(!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint));
+#endif
+}
+
+void tst_QUdpSocket::writeDatagramToNonExistingPeer_data()
+{
+    QTest::addColumn<bool>("bind");
+    QTest::addColumn<QHostAddress>("peerAddress");
+    QHostAddress localhost(QHostAddress::LocalHost);
+#if !defined(Q_OS_SYMBIAN)
+    QHostAddress remote = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first();
+#endif
+
+    QTest::newRow("localhost-unbound") << false << localhost;
+    QTest::newRow("localhost-bound") << true << localhost;
+#if !defined(Q_OS_SYMBIAN)
+    QTest::newRow("remote-unbound") << false << remote;
+    QTest::newRow("remote-bound") << true << remote;
+#endif
+}
+
+void tst_QUdpSocket::writeDatagramToNonExistingPeer()
+{
+    QFETCH(bool, bind);
+    QFETCH(QHostAddress, peerAddress);
+
+    quint16 peerPort = 33533 + int(bind);
+
+    QUdpSocket sUdp;
+    QSignalSpy sReadyReadSpy(&sUdp, SIGNAL(readyRead()));
+    if (bind)
+        QVERIFY(sUdp.bind());
+    QCOMPARE(sUdp.writeDatagram("", 1, peerAddress, peerPort), qint64(1));
+    QTestEventLoop::instance().enterLoop(1);
+    QCOMPARE(sReadyReadSpy.count(), 0);
+}
+
+void tst_QUdpSocket::writeToNonExistingPeer_data()
+{
+    QTest::addColumn<QHostAddress>("peerAddress");
+    QHostAddress localhost(QHostAddress::LocalHost);
+#if !defined(Q_OS_SYMBIAN)
+    QHostAddress remote = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first();
+#endif
+    // write (required to be connected)
+    QTest::newRow("localhost") << localhost;
+#if !defined(Q_OS_SYMBIAN)
+    QTest::newRow("remote") << remote;
+#endif
+}
+
+void tst_QUdpSocket::writeToNonExistingPeer()
+{
+    QSKIP("Connected-mode UDP sockets and their behaviour are erratic", SkipAll);
+    QFETCH(QHostAddress, peerAddress);
+    quint16 peerPort = 34534;
+    qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
+
+    QUdpSocket sConnected;
+    QSignalSpy sConnectedReadyReadSpy(&sConnected, SIGNAL(readyRead()));
+    QSignalSpy sConnectedErrorSpy(&sConnected, SIGNAL(error(QAbstractSocket::SocketError)));
+    sConnected.connectToHost(peerAddress, peerPort, QIODevice::ReadWrite);
+
+    // the first write succeeds...
+    QCOMPARE(sConnected.write("", 1), qint64(1));
+
+    // the second one should fail!
+    QTest::qSleep(1000);                   // do not process events
+    QCOMPARE(sConnected.write("", 1), qint64(-1));
+    QCOMPARE(int(sConnected.error()), int(QUdpSocket::ConnectionRefusedError));
+
+    // the third one will succeed...
+    QCOMPARE(sConnected.write("", 1), qint64(1));
+    QTestEventLoop::instance().enterLoop(1);
+    QCOMPARE(sConnectedReadyReadSpy.count(), 0);
+    QCOMPARE(sConnectedErrorSpy.count(), 1);
+    QCOMPARE(int(sConnected.error()), int(QUdpSocket::ConnectionRefusedError));
+
+    // we should now get a read error
+    QCOMPARE(sConnected.write("", 1), qint64(1));
+    QTest::qSleep(1000);                   // do not process events
+    char buf[2];
+    QVERIFY(!sConnected.hasPendingDatagrams());
+    QCOMPARE(sConnected.bytesAvailable(), Q_INT64_C(0));
+    QCOMPARE(sConnected.pendingDatagramSize(), Q_INT64_C(-1));
+    QCOMPARE(sConnected.readDatagram(buf, 2), Q_INT64_C(-1));
+    QCOMPARE(int(sConnected.error()), int(QUdpSocket::ConnectionRefusedError));
+
+    QCOMPARE(sConnected.write("", 1), qint64(1));
+    QTest::qSleep(1000);                   // do not process events
+    QCOMPARE(sConnected.read(buf, 2), Q_INT64_C(0));
+    QCOMPARE(int(sConnected.error()), int(QUdpSocket::ConnectionRefusedError));
+
+    // we should still be connected
+    QCOMPARE(int(sConnected.state()), int(QUdpSocket::ConnectedState));
+}
+
+void tst_QUdpSocket::outOfProcessConnectedClientServerTest()
+{
+#if defined(Q_OS_WINCE) || defined (Q_OS_SYMBIAN)
+    QSKIP("This test depends on reading data from QProcess (not supported on Qt/WinCE and Symbian).", SkipAll);
+#endif
+#if defined(QT_NO_PROCESS)
+    QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll);
+#else
+
+    QProcess serverProcess;
+    serverProcess.start(QLatin1String("clientserver/clientserver server 1 1"),
+                        QIODevice::ReadWrite | QIODevice::Text);
+
+    // Wait until the server has started and reports success.
+    while (!serverProcess.canReadLine())
+        QVERIFY(serverProcess.waitForReadyRead(3000));
+    QByteArray serverGreeting = serverProcess.readLine();
+    QVERIFY(serverGreeting != QByteArray("XXX\n"));
+    int serverPort = serverGreeting.trimmed().toInt();
+    QVERIFY(serverPort > 0 && serverPort < 65536);
+
+    QProcess clientProcess;
+    clientProcess.start(QString::fromLatin1("clientserver/clientserver connectedclient %1 %2")
+                        .arg(QLatin1String("127.0.0.1")).arg(serverPort),
+                        QIODevice::ReadWrite | QIODevice::Text);
+    // Wait until the server has started and reports success.
+    while (!clientProcess.canReadLine())
+        QVERIFY(clientProcess.waitForReadyRead(3000));
+    QByteArray clientGreeting = clientProcess.readLine();
+    QCOMPARE(clientGreeting, QByteArray("ok\n"));
+
+    // Let the client and server talk for 3 seconds
+    QTest::qWait(3000);
+
+    QStringList serverData = QString::fromLocal8Bit(serverProcess.readAll()).split("\n");
+    QStringList clientData = QString::fromLocal8Bit(clientProcess.readAll()).split("\n");
+    QVERIFY(serverData.size() > 5);
+    QVERIFY(clientData.size() > 5);
+
+    for (int i = 0; i < clientData.size() / 2; ++i) {
+        QCOMPARE(clientData.at(i * 2), QString("readData()"));
+        QCOMPARE(serverData.at(i * 3), QString("readData()"));
+
+        QString cdata = clientData.at(i * 2 + 1);
+        QString sdata = serverData.at(i * 3 + 1);
+        QVERIFY(cdata.startsWith(QLatin1String("got ")));
+
+        QCOMPARE(cdata.mid(4).trimmed().toInt(), sdata.mid(4).trimmed().toInt() * 2);
+        QVERIFY(serverData.at(i * 3 + 2).startsWith(QLatin1String("sending ")));
+        QCOMPARE(serverData.at(i * 3 + 2).trimmed().mid(8).toInt(),
+                 sdata.mid(4).trimmed().toInt() * 2);
+    }
+
+    clientProcess.kill();
+    QVERIFY(clientProcess.waitForFinished());
+    serverProcess.kill();
+    QVERIFY(serverProcess.waitForFinished());
+#endif
+}
+
+void tst_QUdpSocket::outOfProcessUnconnectedClientServerTest()
+{
+#if defined(Q_OS_WINCE) || defined (Q_OS_SYMBIAN)
+    QSKIP("This test depends on reading data from QProcess (not supported on Qt/WinCE and Symbian).", SkipAll);
+#endif
+#if defined(QT_NO_PROCESS)
+    QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll);
+#else
+
+    QProcess serverProcess;
+    serverProcess.start(QLatin1String("clientserver/clientserver server 1 1"),
+                        QIODevice::ReadWrite | QIODevice::Text);
+
+    // Wait until the server has started and reports success.
+    while (!serverProcess.canReadLine())
+        QVERIFY(serverProcess.waitForReadyRead(3000));
+    QByteArray serverGreeting = serverProcess.readLine();
+    QVERIFY(serverGreeting != QByteArray("XXX\n"));
+    int serverPort = serverGreeting.trimmed().toInt();
+    QVERIFY(serverPort > 0 && serverPort < 65536);
+
+    QProcess clientProcess;
+    clientProcess.start(QString::fromLatin1("clientserver/clientserver unconnectedclient %1 %2")
+                        .arg(QLatin1String("127.0.0.1")).arg(serverPort),
+                        QIODevice::ReadWrite | QIODevice::Text);
+    // Wait until the server has started and reports success.
+    while (!clientProcess.canReadLine())
+        QVERIFY(clientProcess.waitForReadyRead(3000));
+    QByteArray clientGreeting = clientProcess.readLine();
+    QCOMPARE(clientGreeting, QByteArray("ok\n"));
+
+    // Let the client and server talk for 3 seconds
+    QTest::qWait(3000);
+
+    QStringList serverData = QString::fromLocal8Bit(serverProcess.readAll()).split("\n");
+    QStringList clientData = QString::fromLocal8Bit(clientProcess.readAll()).split("\n");
+
+    QVERIFY(serverData.size() > 5);
+    QVERIFY(clientData.size() > 5);
+
+    for (int i = 0; i < clientData.size() / 2; ++i) {
+        QCOMPARE(clientData.at(i * 2), QString("readData()"));
+        QCOMPARE(serverData.at(i * 3), QString("readData()"));
+
+        QString cdata = clientData.at(i * 2 + 1);
+        QString sdata = serverData.at(i * 3 + 1);
+        QVERIFY(cdata.startsWith(QLatin1String("got ")));
+
+        QCOMPARE(cdata.mid(4).trimmed().toInt(), sdata.mid(4).trimmed().toInt() * 2);
+        QVERIFY(serverData.at(i * 3 + 2).startsWith(QLatin1String("sending ")));
+        QCOMPARE(serverData.at(i * 3 + 2).trimmed().mid(8).toInt(),
+                 sdata.mid(4).trimmed().toInt() * 2);
+    }
+
+    clientProcess.kill();
+    QVERIFY(clientProcess.waitForFinished());
+    serverProcess.kill();
+    QVERIFY(serverProcess.waitForFinished());
+#endif
+}
+
+void tst_QUdpSocket::zeroLengthDatagram()
+{
+    QFETCH_GLOBAL(bool, setProxy);
+    if (setProxy)
+        return;
+
+    QUdpSocket receiver;
+    QVERIFY(receiver.bind());
+
+    QVERIFY(!receiver.waitForReadyRead(100));
+    QVERIFY(!receiver.hasPendingDatagrams());
+
+    QUdpSocket sender;
+    QCOMPARE(sender.writeDatagram(QByteArray(), QHostAddress::LocalHost, receiver.localPort()), qint64(0));
+
+    QVERIFY(receiver.waitForReadyRead(1000));
+    QVERIFY(receiver.hasPendingDatagrams());
+
+    char buf;
+    QCOMPARE(receiver.readDatagram(&buf, 1), qint64(0));
+}
+
+QTEST_MAIN(tst_QUdpSocket)
+#include "tst_qudpsocket.moc"