summaryrefslogtreecommitdiffstats
path: root/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp')
-rw-r--r--tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp1356
1 files changed, 1356 insertions, 0 deletions
diff --git a/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp
new file mode 100644
index 0000000000..83d30cc40a
--- /dev/null
+++ b/tests/auto/network/socket/qudpsocket/tst_qudpsocket.cpp
@@ -0,0 +1,1356 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#include <qcoreapplication.h>
+#include <qfileinfo.h>
+#include <qdatastream.h>
+#include <qudpsocket.h>
+#include <qhostaddress.h>
+#include <qhostinfo.h>
+#include <qmap.h>
+#include <QNetworkProxy>
+#include <QNetworkInterface>
+
+#include <qstringlist.h>
+#include "../../../network-settings.h"
+
+#ifndef QT_NO_BEARERMANAGEMENT
+#include <QtNetwork/qnetworkconfigmanager.h>
+#include <QtNetwork/qnetworkconfiguration.h>
+#include <QtNetwork/qnetworksession.h>
+#endif
+
+Q_DECLARE_METATYPE(QHostAddress)
+Q_DECLARE_METATYPE(QNetworkInterface)
+Q_DECLARE_METATYPE(QSharedPointer<QNetworkSession>)
+//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 dualStack();
+ 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();
+ void multicastTtlOption_data();
+ void multicastTtlOption();
+ void multicastLoopbackOption_data();
+ void multicastLoopbackOption();
+ void multicastJoinBeforeBind_data();
+ void multicastJoinBeforeBind();
+ void multicastLeaveAfterClose_data();
+ void multicastLeaveAfterClose();
+ void setMulticastInterface_data();
+ void setMulticastInterface();
+ void multicast_data();
+ void multicast();
+ void echo_data();
+ void echo();
+
+protected slots:
+ void empty_readyReadSlot();
+ void empty_connectedSlot();
+
+private:
+#ifndef QT_NO_BEARERMANAGEMENT
+ QNetworkConfigurationManager *netConfMan;
+ QNetworkConfiguration networkConfiguration;
+ QSharedPointer<QNetworkSession> networkSession;
+#endif
+};
+
+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;
+ QTest::newRow("WithSocks5Proxy") << true << int(QNetworkProxy::Socks5Proxy);
+
+#ifndef QT_NO_BEARERMANAGEMENT
+ netConfMan = new QNetworkConfigurationManager(this);
+ networkConfiguration = netConfMan->defaultConfiguration();
+ networkSession = QSharedPointer<QNetworkSession>(new QNetworkSession(networkConfiguration));
+ if (!networkSession->isOpen()) {
+ networkSession->open();
+ QVERIFY(networkSession->waitForOpened(30000));
+ }
+#endif
+}
+
+void tst_QUdpSocket::init()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy) {
+ QFETCH_GLOBAL(int, proxyType);
+ if (proxyType == QNetworkProxy::Socks5Proxy) {
+ QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080));
+ }
+ }
+}
+
+void tst_QUdpSocket::cleanup()
+{
+ QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy);
+}
+
+
+//----------------------------------------------------------------------------------
+
+void tst_QUdpSocket::constructing()
+{
+ QUdpSocket socket;
+#ifdef FORCE_SESSION
+ socket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+
+ 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;
+#ifdef FORCE_SESSION
+ serverSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+
+ 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::AnyIPv4 || serverSocket.localAddress() == QHostAddress::AnyIPv6))
+ serverAddress = serverSocket.localAddress();
+
+ for (int i = 0; i < 3; ++i) {
+ QUdpSocket clientSocket;
+#ifdef FORCE_SESSION
+ clientSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ 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) {
+ QFETCH_GLOBAL(int, proxyType);
+ if (proxyType == QNetworkProxy::Socks5Proxy) {
+ QSKIP("With socks5 Broadcast is not supported.", SkipSingle);
+ }
+ }
+#ifdef Q_OS_AIX
+ QSKIP("Broadcast does not work on darko", SkipAll);
+#endif
+ const char *message[] = {"Yo mista", "", "Yo", "Wassap"};
+
+ QList<QHostAddress> broadcastAddresses;
+ foreach (QNetworkInterface iface, QNetworkInterface::allInterfaces()) {
+ if ((iface.flags() & QNetworkInterface::CanBroadcast)
+ && iface.flags() & QNetworkInterface::IsUp) {
+ for (int i=0;i<iface.addressEntries().count();i++)
+ broadcastAddresses.append(iface.addressEntries().at(i).broadcast());
+ }
+ }
+ if (broadcastAddresses.isEmpty())
+ QSKIP("No interface can broadcast", SkipAll);
+ for (int i = 0; i < 4; ++i) {
+ QUdpSocket serverSocket;
+#ifdef FORCE_SESSION
+ serverSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ QVERIFY2(serverSocket.bind(QHostAddress::Any, 5000), serverSocket.errorString().toLatin1().constData());
+
+ QCOMPARE(serverSocket.state(), QUdpSocket::BoundState);
+
+ connect(&serverSocket, SIGNAL(readyRead()), SLOT(empty_readyReadSlot()));
+
+ QUdpSocket broadcastSocket;
+#ifdef FORCE_SESSION
+ broadcastSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ broadcastSocket.bind();
+
+ for (int j = 0; j < 100; ++j) {
+ for (int k = 0; k < 4; k++) {
+ broadcastSocket.writeDatagram(message[i], strlen(message[i]),
+ QHostAddress::Broadcast, 5000);
+ foreach (QHostAddress addr, broadcastAddresses)
+ broadcastSocket.writeDatagram(message[i], strlen(message[i]), addr, 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;
+#ifdef FORCE_SESSION
+ peter.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+ paul.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+
+ QVERIFY2(peter.bind(), peter.errorString().toLatin1().constData());
+ QVERIFY2(paul.bind(), paul.errorString().toLatin1().constData());
+
+ QHostAddress peterAddress = QHostAddress::LocalHost;
+ if (!(peter.localAddress() == QHostAddress::AnyIPv4 || peter.localAddress() == QHostAddress::AnyIPv6))
+ peterAddress = peter.localAddress();
+ QHostAddress pualAddress = QHostAddress::LocalHost;
+ if (!(paul.localAddress() == QHostAddress::AnyIPv4 || paul.localAddress() == QHostAddress::AnyIPv6))
+ 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(9000));
+ QVERIFY(paul.waitForReadyRead(9000));
+ 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()
+{
+ QFETCH(QByteArray, peterMessage);
+ QFETCH(QByteArray, paulMessage);
+ QFETCH(bool, success);
+
+ QUdpSocket peter;
+ QUdpSocket paul;
+#ifdef FORCE_SESSION
+ peter.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+ paul.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+
+ 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::dualStack()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ if (setProxy)
+ QSKIP("test server SOCKS proxy doesn't support IPv6", SkipSingle);
+ QUdpSocket dualSock;
+ QByteArray dualData("dual");
+ QVERIFY(dualSock.bind(QHostAddress(QHostAddress::Any), 0));
+
+ QUdpSocket v4Sock;
+ QByteArray v4Data("v4");
+ QVERIFY(v4Sock.bind(QHostAddress(QHostAddress::AnyIPv4), 0));
+
+ QUdpSocket v6Sock;
+ QByteArray v6Data("v6");
+ QVERIFY(v6Sock.bind(QHostAddress(QHostAddress::AnyIPv6), 0));
+
+ QHostAddress from;
+ quint16 port;
+ QByteArray buffer;
+ //test v4 -> dual
+ QCOMPARE((int)v4Sock.writeDatagram(v4Data.constData(), v4Data.length(), QHostAddress(QHostAddress::LocalHost), dualSock.localPort()), v4Data.length());
+ QVERIFY(dualSock.waitForReadyRead(5000));
+ buffer.reserve(100);
+ qint64 size = dualSock.readDatagram(buffer.data(), 100, &from, &port);
+ QCOMPARE((int)size, v4Data.length());
+ buffer.resize(size);
+ QCOMPARE(buffer, v4Data);
+
+ //test v6 -> dual
+ QCOMPARE((int)v6Sock.writeDatagram(v6Data.constData(), v6Data.length(), QHostAddress(QHostAddress::LocalHostIPv6), dualSock.localPort()), v6Data.length());
+ QVERIFY(dualSock.waitForReadyRead(5000));
+ buffer.reserve(100);
+ size = dualSock.readDatagram(buffer.data(), 100, &from, &port);
+ QCOMPARE((int)size, v6Data.length());
+ buffer.resize(size);
+ QCOMPARE(buffer, v6Data);
+
+ //test dual -> v4
+ QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHost), v4Sock.localPort()), dualData.length());
+ QVERIFY(v4Sock.waitForReadyRead(5000));
+ buffer.reserve(100);
+ size = v4Sock.readDatagram(buffer.data(), 100, &from, &port);
+ QCOMPARE((int)size, dualData.length());
+ buffer.resize(size);
+ QCOMPARE(buffer, dualData);
+
+ //test dual -> v6
+ QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHostIPv6), v6Sock.localPort()), dualData.length());
+ QVERIFY(v6Sock.waitForReadyRead(5000));
+ buffer.reserve(100);
+ size = v6Sock.readDatagram(buffer.data(), 100, &from, &port);
+ QCOMPARE((int)size, dualData.length());
+ buffer.resize(size);
+ QCOMPARE(buffer, dualData);
+
+}
+
+void tst_QUdpSocket::empty_readyReadSlot()
+{
+ QTestEventLoop::instance().exitLoop();
+}
+
+void tst_QUdpSocket::empty_connectedSlot()
+{
+ QTestEventLoop::instance().exitLoop();
+}
+
+//----------------------------------------------------------------------------------
+
+void tst_QUdpSocket::readLine()
+{
+ QUdpSocket socket1;
+ QUdpSocket socket2;
+#ifdef FORCE_SESSION
+ socket1.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+ socket2.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ QVERIFY2(socket1.bind(), socket1.errorString().toLatin1().constData());
+
+ socket2.connectToHost("127.0.0.1", socket1.localPort());
+ QVERIFY(socket2.waitForConnected(5000));
+}
+
+//----------------------------------------------------------------------------------
+
+void tst_QUdpSocket::pendingDatagramSize()
+{
+ QUdpSocket server;
+#ifdef FORCE_SESSION
+ server.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ QVERIFY2(server.bind(), server.errorString().toLatin1().constData());
+
+ QHostAddress serverAddress = QHostAddress::LocalHost;
+ if (!(server.localAddress() == QHostAddress::AnyIPv4 || server.localAddress() == QHostAddress::AnyIPv6))
+ serverAddress = server.localAddress();
+
+ QUdpSocket client;
+#ifdef FORCE_SESSION
+ client.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ 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;
+#ifdef FORCE_SESSION
+ server.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ QVERIFY2(server.bind(), server.errorString().toLatin1().constData());
+
+ QHostAddress serverAddress = QHostAddress::LocalHost;
+ if (!(server.localAddress() == QHostAddress::AnyIPv4 || server.localAddress() == QHostAddress::AnyIPv6))
+ serverAddress = server.localAddress();
+
+ QUdpSocket client;
+#ifdef FORCE_SESSION
+ client.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+
+ 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)) {
+#ifdef Q_OS_SYMBIAN
+ //symbian receive buffer for datagrams is ~30k, but it can send datagrams up to the maximum 64k...
+ if (i > 28) {
+ i = 64;
+ continue;
+ }
+#endif
+ 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;
+#ifdef FORCE_SESSION
+ server.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ QVERIFY2(server.bind(), server.errorString().toLatin1().constData());
+
+ QHostAddress serverAddress = QHostAddress::LocalHost;
+ if (!(server.localAddress() == QHostAddress::AnyIPv4 || server.localAddress() == QHostAddress::AnyIPv6))
+ serverAddress = server.localAddress();
+
+ QUdpSocket client;
+#ifdef FORCE_SESSION
+ client.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ client.connectToHost(serverAddress, server.localPort());
+ QVERIFY(client.waitForConnected(10000));
+
+ 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) {
+ QFETCH_GLOBAL(int, proxyType);
+ if (proxyType == QNetworkProxy::Socks5Proxy) {
+ QSKIP("With socks5 explicit port binding is not supported.", SkipAll);
+ }
+ }
+
+ QUdpSocket socket;
+#ifdef FORCE_SESSION
+ socket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ QVERIFY2(socket.bind(), socket.errorString().toLatin1().constData());
+ QUdpSocket socket2;
+#ifdef FORCE_SESSION
+ socket2.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ 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 administrator?");
+ 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);
+ QHostAddress remote = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first();
+
+ QTest::newRow("localhost-unbound") << false << localhost;
+ QTest::newRow("localhost-bound") << true << localhost;
+ QTest::newRow("remote-unbound") << false << remote;
+ QTest::newRow("remote-bound") << true << remote;
+}
+
+void tst_QUdpSocket::writeDatagramToNonExistingPeer()
+{
+ QFETCH(bool, bind);
+ QFETCH(QHostAddress, peerAddress);
+
+ quint16 peerPort = 33533 + int(bind);
+
+ QUdpSocket sUdp;
+#ifdef FORCE_SESSION
+ sUdp.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ 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);
+ QHostAddress remote = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first();
+ // write (required to be connected)
+ QTest::newRow("localhost") << localhost;
+ QTest::newRow("remote") << remote;
+}
+
+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;
+#ifdef FORCE_SESSION
+ sConnected.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ QSignalSpy sConnectedReadyReadSpy(&sConnected, SIGNAL(readyRead()));
+ QSignalSpy sConnectedErrorSpy(&sConnected, SIGNAL(error(QAbstractSocket::SocketError)));
+ sConnected.connectToHost(peerAddress, peerPort, QIODevice::ReadWrite);
+ QVERIFY(sConnected.waitForConnected(10000));
+
+ // 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);
+ QVERIFY2(serverProcess.waitForStarted(3000),
+ qPrintable("Failed to start subprocess: " + serverProcess.errorString()));
+
+ // 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);
+ QVERIFY2(clientProcess.waitForStarted(3000),
+ qPrintable("Failed to start subprocess: " + clientProcess.errorString()));
+
+ // 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);
+ QVERIFY2(serverProcess.waitForStarted(3000),
+ qPrintable("Failed to start subprocess: " + serverProcess.errorString()));
+
+ // 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);
+ QVERIFY2(clientProcess.waitForStarted(3000),
+ qPrintable("Failed to start subprocess: " + clientProcess.errorString()));
+
+ // 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;
+#ifdef FORCE_SESSION
+ receiver.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ QVERIFY(receiver.bind());
+
+ QVERIFY(!receiver.waitForReadyRead(100));
+ QVERIFY(!receiver.hasPendingDatagrams());
+
+ QUdpSocket sender;
+#ifdef FORCE_SESSION
+ sender.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ 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));
+}
+
+void tst_QUdpSocket::multicastTtlOption_data()
+{
+ QTest::addColumn<QHostAddress>("bindAddress");
+ QTest::addColumn<int>("ttl");
+ QTest::addColumn<int>("expected");
+
+ QList<QHostAddress> addresses;
+ addresses += QHostAddress(QHostAddress::Any);
+ addresses += QHostAddress(QHostAddress::AnyIPv6);
+
+ foreach (const QHostAddress &address, addresses) {
+ QTest::newRow(QString("%1 0").arg(address.toString()).toAscii()) << address << 0 << 0;
+ QTest::newRow(QString("%1 1").arg(address.toString()).toAscii()) << address << 1 << 1;
+ QTest::newRow(QString("%1 2").arg(address.toString()).toAscii()) << address << 2 << 2;
+ QTest::newRow(QString("%1 128").arg(address.toString()).toAscii()) << address << 128 << 128;
+ QTest::newRow(QString("%1 255").arg(address.toString()).toAscii()) << address << 255 << 255;
+ QTest::newRow(QString("%1 1024").arg(address.toString()).toAscii()) << address << 1024 << 1;
+ }
+}
+
+void tst_QUdpSocket::multicastTtlOption()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ QFETCH(QHostAddress, bindAddress);
+ QFETCH(int, ttl);
+ QFETCH(int, expected);
+ if (setProxy) {
+ // UDP multicast does not work with proxies
+ expected = 0;
+ }
+
+ QUdpSocket udpSocket;
+#ifdef FORCE_SESSION
+ udpSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ // bind, but ignore the result, we are only interested in initializing the socket
+ (void) udpSocket.bind(bindAddress, 0);
+ udpSocket.setSocketOption(QUdpSocket::MulticastTtlOption, ttl);
+ QCOMPARE(udpSocket.socketOption(QUdpSocket::MulticastTtlOption).toInt(), expected);
+}
+
+void tst_QUdpSocket::multicastLoopbackOption_data()
+{
+ QTest::addColumn<QHostAddress>("bindAddress");
+ QTest::addColumn<int>("loopback");
+ QTest::addColumn<int>("expected");
+
+ QList<QHostAddress> addresses;
+ addresses += QHostAddress(QHostAddress::Any);
+ addresses += QHostAddress(QHostAddress::AnyIPv6);
+
+ foreach (const QHostAddress &address, addresses) {
+ QTest::newRow(QString("%1 0").arg(address.toString()).toAscii()) << address << 0 << 0;
+ QTest::newRow(QString("%1 1").arg(address.toString()).toAscii()) << address << 1 << 1;
+ QTest::newRow(QString("%1 2").arg(address.toString()).toAscii()) << address << 2 << 1;
+ QTest::newRow(QString("%1 0 again").arg(address.toString()).toAscii()) << address << 0 << 0;
+ QTest::newRow(QString("%1 2 again").arg(address.toString()).toAscii()) << address << 2 << 1;
+ QTest::newRow(QString("%1 0 last time").arg(address.toString()).toAscii()) << address << 0 << 0;
+ QTest::newRow(QString("%1 1 again").arg(address.toString()).toAscii()) << address << 1 << 1;
+ }
+}
+
+void tst_QUdpSocket::multicastLoopbackOption()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ QFETCH(QHostAddress, bindAddress);
+ QFETCH(int, loopback);
+ QFETCH(int, expected);
+ if (setProxy) {
+ // UDP multicast does not work with proxies
+ expected = 0;
+ }
+
+ QUdpSocket udpSocket;
+#ifdef FORCE_SESSION
+ udpSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ // bind, but ignore the result, we are only interested in initializing the socket
+ (void) udpSocket.bind(bindAddress, 0);
+ udpSocket.setSocketOption(QUdpSocket::MulticastLoopbackOption, loopback);
+ QCOMPARE(udpSocket.socketOption(QUdpSocket::MulticastLoopbackOption).toInt(), expected);
+}
+
+void tst_QUdpSocket::multicastJoinBeforeBind_data()
+{
+ QTest::addColumn<QHostAddress>("groupAddress");
+ QTest::newRow("valid ipv4 group address") << QHostAddress("239.255.118.62");
+ QTest::newRow("invalid ipv4 group address") << QHostAddress(QHostAddress::Broadcast);
+ QTest::newRow("valid ipv6 group address") << QHostAddress("FF01::114");
+ QTest::newRow("invalid ipv6 group address") << QHostAddress(QHostAddress::AnyIPv6);
+}
+
+void tst_QUdpSocket::multicastJoinBeforeBind()
+{
+ QFETCH(QHostAddress, groupAddress);
+
+ QUdpSocket udpSocket;
+#ifdef FORCE_SESSION
+ udpSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ // cannot join group before binding
+ QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::joinMulticastGroup() called on a QUdpSocket when not in QUdpSocket::BoundState");
+ QVERIFY(!udpSocket.joinMulticastGroup(groupAddress));
+}
+
+void tst_QUdpSocket::multicastLeaveAfterClose_data()
+{
+ QTest::addColumn<QHostAddress>("groupAddress");
+ QTest::newRow("valid ipv4 group address") << QHostAddress("239.255.118.62");
+ QTest::newRow("valid ipv6 group address") << QHostAddress("FF01::114");
+}
+
+void tst_QUdpSocket::multicastLeaveAfterClose()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ QFETCH(QHostAddress, groupAddress);
+ if (setProxy) {
+ QSKIP("UDP Multicast does not work with proxies", SkipAll);
+ }
+
+ QUdpSocket udpSocket;
+#ifdef FORCE_SESSION
+ udpSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+#ifdef Q_OS_SYMBIAN
+ QVERIFY2(udpSocket.bind(),
+ qPrintable(udpSocket.errorString()));
+#else
+ QVERIFY2(udpSocket.bind(groupAddress, 0),
+ qPrintable(udpSocket.errorString()));
+#endif
+ QVERIFY2(udpSocket.joinMulticastGroup(groupAddress),
+ qPrintable(udpSocket.errorString()));
+ udpSocket.close();
+ QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::leaveMulticastGroup() called on a QUdpSocket when not in QUdpSocket::BoundState");
+ QVERIFY(!udpSocket.leaveMulticastGroup(groupAddress));
+}
+
+void tst_QUdpSocket::setMulticastInterface_data()
+{
+ QTest::addColumn<QNetworkInterface>("iface");
+ QTest::addColumn<QHostAddress>("address");
+ QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
+ foreach (const QNetworkInterface &iface, interfaces) {
+ foreach (const QNetworkAddressEntry &entry, iface.addressEntries()) {
+ QTest::newRow(QString("%1:%2").arg(iface.name()).arg(entry.ip().toString()).toAscii())
+ << iface
+ << entry.ip();
+ }
+ }
+}
+
+void tst_QUdpSocket::setMulticastInterface()
+{
+#ifdef Q_OS_SYMBIAN
+ QSKIP("Symbian has no IPV6_MULTICAST_IF equivalent", SkipAll);
+#else
+ QFETCH_GLOBAL(bool, setProxy);
+ QFETCH(QNetworkInterface, iface);
+ QFETCH(QHostAddress, address);
+
+ QUdpSocket udpSocket;
+ // bind initializes the socket
+ bool bound = udpSocket.bind((address.protocol() == QAbstractSocket::IPv6Protocol
+ ? QHostAddress(QHostAddress::AnyIPv6)
+ : QHostAddress(QHostAddress::Any)),
+ 0);
+ if (!bound)
+ QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::setMulticastInterface() called on a QUdpSocket when not in QUdpSocket::BoundState");
+ udpSocket.setMulticastInterface(iface);
+ if (!bound)
+ QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::multicastInterface() called on a QUdpSocket when not in QUdpSocket::BoundState");
+ QNetworkInterface iface2 = udpSocket.multicastInterface();
+ if (!setProxy) {
+ QVERIFY(iface2.isValid());
+ QCOMPARE(iface.name(), iface2.name());
+ } else {
+ QVERIFY(!iface2.isValid());
+ }
+#endif
+}
+
+void tst_QUdpSocket::multicast_data()
+{
+ QHostAddress anyAddress = QHostAddress(QHostAddress::AnyIPv4);
+ QHostAddress groupAddress = QHostAddress("239.255.118.62");
+ QHostAddress any6Address = QHostAddress(QHostAddress::AnyIPv6);
+ QHostAddress group6Address = QHostAddress("FF01::114");
+
+ QTest::addColumn<QHostAddress>("bindAddress");
+ QTest::addColumn<bool>("bindResult");
+ QTest::addColumn<QHostAddress>("groupAddress");
+ QTest::addColumn<bool>("joinResult");
+ QTest::newRow("valid bind, group ipv4 address") << anyAddress << true << groupAddress << true;
+ QTest::newRow("valid bind, invalid group ipv4 address") << anyAddress << true << anyAddress << false;
+ QTest::newRow("same bind, group ipv4 address") << groupAddress << true << groupAddress << true;
+ QTest::newRow("valid bind, group ipv6 address") << any6Address << true << group6Address << true;
+ QTest::newRow("valid bind, invalid group ipv6 address") << any6Address << true << any6Address << false;
+ QTest::newRow("same bind, group ipv6 address") << group6Address << true << group6Address << true;
+}
+
+void tst_QUdpSocket::multicast()
+{
+ QFETCH_GLOBAL(bool, setProxy);
+ QFETCH(QHostAddress, bindAddress);
+ QFETCH(bool, bindResult);
+ QFETCH(QHostAddress, groupAddress);
+ QFETCH(bool, joinResult);
+ if (setProxy) {
+ // UDP multicast does not work with proxies
+ if (
+#ifndef Q_OS_WIN
+ //windows native socket engine binds 0.0.0.0 instead of the requested multicast address
+ (bindAddress.protocol() == QAbstractSocket::IPv4Protocol && (bindAddress.toIPv4Address() & 0xffff0000) == 0xefff0000) ||
+#endif
+ bindAddress.protocol() == QAbstractSocket::IPv6Protocol) {
+ // proxy cannot bind to IPv6 or multicast addresses
+ bindResult = false;
+ }
+ joinResult = false;
+ }
+
+ QUdpSocket receiver;
+#ifdef FORCE_SESSION
+ receiver.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+
+ // bind first, then verify that we can join the multicast group
+#ifdef Q_OS_SYMBIAN
+ if (!setProxy) {
+ QEXPECT_FAIL("same bind, group ipv4 address", "bind to group address not supported on symbian", Abort);
+ QEXPECT_FAIL("same bind, group ipv6 address", "bind to group address not supported on symbian", Abort);
+ }
+#endif
+ QVERIFY2(receiver.bind(bindAddress, 0) == bindResult,
+ qPrintable(receiver.errorString()));
+ if (!bindResult)
+ return;
+
+ QVERIFY2(receiver.joinMulticastGroup(groupAddress) == joinResult,
+ qPrintable(receiver.errorString()));
+ if (!joinResult)
+ return;
+
+ QList<QByteArray> datagrams = QList<QByteArray>()
+ << QByteArray("0123")
+ << QByteArray("4567")
+ << QByteArray("89ab")
+ << QByteArray("cdef");
+
+ QUdpSocket sender;
+#ifdef FORCE_SESSION
+ sender.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ sender.bind();
+ foreach (const QByteArray &datagram, datagrams) {
+ QCOMPARE(int(sender.writeDatagram(datagram, groupAddress, receiver.localPort())),
+ int(datagram.size()));
+ }
+
+ QVERIFY2(receiver.waitForReadyRead(),
+ qPrintable(receiver.errorString()));
+ QVERIFY(receiver.hasPendingDatagrams());
+ QList<QByteArray> receivedDatagrams;
+ while (receiver.hasPendingDatagrams()) {
+ QByteArray datagram;
+ datagram.resize(receiver.pendingDatagramSize());
+ receiver.readDatagram(datagram.data(), datagram.size(), 0, 0);
+ receivedDatagrams << datagram;
+ }
+#ifdef Q_OS_SYMBIAN
+ QEXPECT_FAIL("valid bind, group ipv4 address", "IPv4 multicast not supported on symbian", Abort);
+#endif
+ QCOMPARE(receivedDatagrams, datagrams);
+
+ QVERIFY2(receiver.leaveMulticastGroup(groupAddress), qPrintable(receiver.errorString()));
+}
+
+void tst_QUdpSocket::echo_data()
+{
+ QTest::addColumn<bool>("connect");
+ QTest::newRow("writeDatagram") << false;
+ QTest::newRow("write") << true;
+}
+
+void tst_QUdpSocket::echo()
+{
+ QFETCH(bool, connect);
+ QHostInfo info = QHostInfo::fromName(QtNetworkSettings::serverName());
+ QVERIFY(info.addresses().count());
+ QHostAddress remote = info.addresses().first();
+
+ QUdpSocket sock;
+#ifdef FORCE_SESSION
+ sock.setProperty("_q_networksession", QVariant::fromValue(networkSession));
+#endif
+ if (connect) {
+ sock.connectToHost(remote, 7);
+ QVERIFY(sock.waitForConnected(10000));
+ } else {
+ sock.bind();
+ }
+ QByteArray out(30, 'x');
+ QByteArray in;
+ int successes = 0;
+ for (int i=0;i<10;i++) {
+ if (connect) {
+ sock.write(out);
+ } else {
+ sock.writeDatagram(out, remote, 7);
+ }
+ if (sock.waitForReadyRead(1000)) {
+ while (sock.hasPendingDatagrams()) {
+ QHostAddress from;
+ quint16 port;
+ if (connect) {
+ in = sock.read(sock.pendingDatagramSize());
+ } else {
+ in.resize(sock.pendingDatagramSize());
+ sock.readDatagram(in.data(), in.length(), &from, &port);
+ }
+ if (in==out)
+ successes++;
+ }
+ }
+ if (!sock.isValid())
+ QFAIL(sock.errorString().toLatin1().constData());
+ qDebug() << "packets in" << successes << "out" << i;
+ QTest::qWait(50); //choke to avoid triggering flood/DDoS protections on echo service
+ }
+ QVERIFY(successes >= 9);
+}
+
+QTEST_MAIN(tst_QUdpSocket)
+#include "tst_qudpsocket.moc"