diff options
author | Jo Asplin <jo.asplin@nokia.com> | 2011-09-06 13:46:40 +0200 |
---|---|---|
committer | Jo Asplin <jo.asplin@nokia.com> | 2011-09-09 09:32:17 +0200 |
commit | c59f9ad7768a007ca7a49ea11b16617529e86d52 (patch) | |
tree | d481be2a727f8461a76c2b729fc7e93784a3cff1 /tests/auto/network/socket | |
parent | 2d41aff1e8557a43268bce631df834bfa79593cf (diff) |
Diffstat (limited to 'tests/auto/network/socket')
49 files changed, 10082 insertions, 0 deletions
diff --git a/tests/auto/network/socket/platformsocketengine/.gitignore b/tests/auto/network/socket/platformsocketengine/.gitignore new file mode 100644 index 0000000000..afe93891ff --- /dev/null +++ b/tests/auto/network/socket/platformsocketengine/.gitignore @@ -0,0 +1 @@ +tst_platformsocketengine diff --git a/tests/auto/network/socket/platformsocketengine/platformsocketengine.pri b/tests/auto/network/socket/platformsocketengine/platformsocketengine.pri new file mode 100644 index 0000000000..15f31fdbb5 --- /dev/null +++ b/tests/auto/network/socket/platformsocketengine/platformsocketengine.pri @@ -0,0 +1,19 @@ +QT += network + +QNETWORK_SRC = $$QT_SOURCE_TREE/src/network + +INCLUDEPATH += $$QNETWORK_SRC + +win32 { + wince*: { + LIBS += -lws2 + } else { + LIBS += -lws2_32 + } +} + +unix:contains(QT_CONFIG, reduce_exports) { + SOURCES += $$QNETWORK_SRC/socket/qnativesocketengine_unix.cpp + SOURCES += $$QNETWORK_SRC/socket/qnativesocketengine.cpp + SOURCES += $$QNETWORK_SRC/socket/qabstractsocketengine.cpp +} diff --git a/tests/auto/network/socket/platformsocketengine/platformsocketengine.pro b/tests/auto/network/socket/platformsocketengine/platformsocketengine.pro new file mode 100644 index 0000000000..99ae358a93 --- /dev/null +++ b/tests/auto/network/socket/platformsocketengine/platformsocketengine.pro @@ -0,0 +1,16 @@ +load(qttest_p4) +SOURCES += tst_platformsocketengine.cpp + +include(../platformsocketengine/platformsocketengine.pri) + +requires(contains(QT_CONFIG,private_tests)) + +MOC_DIR=tmp + +QT = core-private network-private + +symbian { + TARGET.CAPABILITY = NetworkServices + INCLUDEPATH += $$OS_LAYER_SYSTEMINCLUDE + LIBS += -lesock +} diff --git a/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp b/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp new file mode 100644 index 0000000000..9e93108fc7 --- /dev/null +++ b/tests/auto/network/socket/platformsocketengine/tst_platformsocketengine.cpp @@ -0,0 +1,763 @@ +/**************************************************************************** +** +** 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/QTest> + +#ifdef Q_OS_WIN +#include <winsock2.h> +#endif + +#include <qcoreapplication.h> + + +#include <qdatastream.h> + +#include <qhostaddress.h> +#include <qdatetime.h> + +#ifdef Q_OS_UNIX +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#endif + +#include <stddef.h> + +#ifdef Q_OS_SYMBIAN +#include <QNetworkConfigurationManager> +#include <QNetworkConfiguration> +#include <QNetworkSession> +#include <QScopedPointer> +#define PLATFORMSOCKETENGINE QSymbianSocketEngine +#define PLATFORMSOCKETENGINESTRING "QSymbianSocketEngine" +#include <private/qsymbiansocketengine_p.h> +#include <private/qcore_symbian_p.h> +#else +#define PLATFORMSOCKETENGINE QNativeSocketEngine +#define PLATFORMSOCKETENGINESTRING "QNativeSocketEngine" +#include <private/qnativesocketengine_p.h> +#endif + +#include <qstringlist.h> + +#include "../../../network-settings.h" + +//TESTED_FILES=network/qnativesocketengine.cpp network/qnativesocketengine_p.h network/qnativesocketengine_unix.cpp + +class tst_PlatformSocketEngine : public QObject +{ + Q_OBJECT + +public: + tst_PlatformSocketEngine(); + virtual ~tst_PlatformSocketEngine(); + + +public slots: + void init(); + void cleanup(); +private slots: + void construction(); + void simpleConnectToIMAP(); + void udpLoopbackTest(); + void udpIPv6LoopbackTest(); + void broadcastTest(); + void serverTest(); + void udpLoopbackPerformance(); + void tcpLoopbackPerformance(); + void readWriteBufferSize(); + void bind(); + void networkError(); + void setSocketDescriptor(); + void invalidSend(); + void receiveUrgentData(); + void tooManySockets(); +}; + +tst_PlatformSocketEngine::tst_PlatformSocketEngine() +{ + Q_SET_DEFAULT_IAP +} + +tst_PlatformSocketEngine::~tst_PlatformSocketEngine() +{ +} + +void tst_PlatformSocketEngine::init() +{ +} + +void tst_PlatformSocketEngine::cleanup() +{ +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::construction() +{ + PLATFORMSOCKETENGINE socketDevice; + + QVERIFY(!socketDevice.isValid()); + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.isValid()); + QVERIFY(socketDevice.protocol() == QAbstractSocket::IPv4Protocol); + QVERIFY(socketDevice.socketType() == QAbstractSocket::TcpSocket); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + QVERIFY(socketDevice.socketDescriptor() != -1); + QVERIFY(socketDevice.localAddress() == QHostAddress()); + QVERIFY(socketDevice.localPort() == 0); + QVERIFY(socketDevice.peerAddress() == QHostAddress()); + QVERIFY(socketDevice.peerPort() == 0); + QVERIFY(socketDevice.error() == QAbstractSocket::UnknownSocketError); + + QTest::ignoreMessage(QtWarningMsg, PLATFORMSOCKETENGINESTRING "::bytesAvailable() was called in QAbstractSocket::UnconnectedState"); + QVERIFY(socketDevice.bytesAvailable() == 0); + + QTest::ignoreMessage(QtWarningMsg, PLATFORMSOCKETENGINESTRING "::hasPendingDatagrams() was called in QAbstractSocket::UnconnectedState"); + QVERIFY(!socketDevice.hasPendingDatagrams()); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::simpleConnectToIMAP() +{ + PLATFORMSOCKETENGINE socketDevice; + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + + const bool isConnected = socketDevice.connectToHost(QtNetworkSettings::serverIP(), 143); + if (!isConnected) { + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectingState); + QVERIFY(socketDevice.waitForWrite()); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState); + } + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState); + QVERIFY(socketDevice.peerAddress() == QtNetworkSettings::serverIP()); + + // Wait for the greeting + QVERIFY(socketDevice.waitForRead()); + + // Read the greeting + qint64 available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + QByteArray array; + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); + + // Write a logout message + QByteArray array2 = "ZZZ LOGOUT\r\n"; + QVERIFY(socketDevice.write(array2.data(), + array2.size()) == array2.size()); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + + available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QCOMPARE(array.constData(), + "* BYE LOGOUT received\r\n" + "ZZZ OK Completed\r\n"); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + char c; + QVERIFY(socketDevice.read(&c, sizeof(c)) == -1); + QVERIFY(socketDevice.error() == QAbstractSocket::RemoteHostClosedError); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::udpLoopbackTest() +{ + PLATFORMSOCKETENGINE udpSocket; + + // Initialize device #1 + QVERIFY(udpSocket.initialize(QAbstractSocket::UdpSocket)); + QVERIFY(udpSocket.isValid()); + QVERIFY(udpSocket.socketDescriptor() != -1); + QVERIFY(udpSocket.protocol() == QAbstractSocket::IPv4Protocol); + QVERIFY(udpSocket.socketType() == QAbstractSocket::UdpSocket); + QVERIFY(udpSocket.state() == QAbstractSocket::UnconnectedState); + + // Bind #1 to localhost + QVERIFY(udpSocket.bind(QHostAddress("127.0.0.1"), 0)); + QVERIFY(udpSocket.state() == QAbstractSocket::BoundState); + quint16 port = udpSocket.localPort(); + QVERIFY(port != 0); + + // Initialize device #2 + PLATFORMSOCKETENGINE udpSocket2; + QVERIFY(udpSocket2.initialize(QAbstractSocket::UdpSocket)); + + // Connect device #2 to #1 + QVERIFY(udpSocket2.connectToHost(QHostAddress("127.0.0.1"), port)); + QVERIFY(udpSocket2.state() == QAbstractSocket::ConnectedState); + + // Write a message to #1 + QByteArray message1 = "hei der"; + QVERIFY(udpSocket2.write(message1.data(), + message1.size()) == message1.size()); + + // Read the message from #2 + QVERIFY(udpSocket.waitForRead()); + QVERIFY(udpSocket.hasPendingDatagrams()); + qint64 available = udpSocket.pendingDatagramSize(); + QVERIFY(available > 0); + QByteArray answer; + answer.resize(available); + QHostAddress senderAddress; + quint16 senderPort = 0; + QVERIFY(udpSocket.readDatagram(answer.data(), answer.size(), + &senderAddress, + &senderPort) == message1.size()); + QVERIFY(senderAddress == QHostAddress("127.0.0.1")); + QVERIFY(senderPort != 0); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::udpIPv6LoopbackTest() +{ + PLATFORMSOCKETENGINE udpSocket; + + // Initialize device #1 + bool init = udpSocket.initialize(QAbstractSocket::UdpSocket, QAbstractSocket::IPv6Protocol); + + if (!init) { + QVERIFY(udpSocket.error() == QAbstractSocket::UnsupportedSocketOperationError); + } else { + QVERIFY(udpSocket.protocol() == QAbstractSocket::IPv6Protocol); + + // Bind #1 to localhost + QVERIFY(udpSocket.bind(QHostAddress("::1"), 0)); + QVERIFY(udpSocket.state() == QAbstractSocket::BoundState); + quint16 port = udpSocket.localPort(); + QVERIFY(port != 0); + + // Initialize device #2 + PLATFORMSOCKETENGINE udpSocket2; + QVERIFY(udpSocket2.initialize(QAbstractSocket::UdpSocket, QAbstractSocket::IPv6Protocol)); + + // Connect device #2 to #1 + QVERIFY(udpSocket2.connectToHost(QHostAddress("::1"), port)); + QVERIFY(udpSocket2.state() == QAbstractSocket::ConnectedState); + + // Write a message to #1 + QByteArray message1 = "hei der"; + QVERIFY(udpSocket2.write(message1.data(), + message1.size()) == message1.size()); + + // Read the message from #2 + QVERIFY(udpSocket.waitForRead()); + QVERIFY(udpSocket.hasPendingDatagrams()); + qint64 available = udpSocket.pendingDatagramSize(); + QVERIFY(available > 0); + QByteArray answer; + answer.resize(available); + QHostAddress senderAddress; + quint16 senderPort = 0; + QVERIFY(udpSocket.readDatagram(answer.data(), answer.size(), + &senderAddress, + &senderPort) == message1.size()); + QVERIFY(senderAddress == QHostAddress("::1")); + QVERIFY(senderPort != 0); + } +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::broadcastTest() +{ +#ifdef Q_OS_SYMBIAN + //broadcast isn't supported on loopback connections, but is on WLAN +#ifndef QT_NO_BEARERMANAGEMENT + QScopedPointer<QNetworkConfigurationManager> netConfMan(new QNetworkConfigurationManager()); + QNetworkConfiguration networkConfiguration(netConfMan->defaultConfiguration()); + QScopedPointer<QNetworkSession> networkSession(new QNetworkSession(networkConfiguration)); + if (!networkSession->isOpen()) { + networkSession->open(); + bool ok = networkSession->waitForOpened(30000); + qDebug() << networkSession->isOpen() << networkSession->error() << networkSession->errorString(); + QVERIFY(ok); + } +#endif +#endif +#ifdef Q_OS_AIX + QSKIP("Broadcast does not work on darko", SkipAll); +#endif + PLATFORMSOCKETENGINE broadcastSocket; + + // Initialize a regular Udp socket + QVERIFY(broadcastSocket.initialize(QAbstractSocket::UdpSocket)); + + // Bind to any port on all interfaces + QVERIFY(broadcastSocket.bind(QHostAddress::Any, 0)); + QVERIFY(broadcastSocket.state() == QAbstractSocket::BoundState); + quint16 port = broadcastSocket.localPort(); + QVERIFY(port > 0); + + // Broadcast an inappropriate troll message + QByteArray trollMessage + = "MOOT wtf is a MOOT? talk english not your sutpiD ENGLISH."; + qint64 written = broadcastSocket.writeDatagram(trollMessage.data(), + trollMessage.size(), + QHostAddress::Broadcast, + port); + +#ifdef Q_OS_SYMBIAN + //On symbian, broadcasts return 0 bytes written if none of the interfaces support it. + //Notably the loopback interfaces do not. (though they do support multicast!?) + if (written == 0) + QEXPECT_FAIL("", "No active interface supports broadcast", Abort); +#endif + QCOMPARE((int)written, trollMessage.size()); + + // Wait until we receive it ourselves +#if defined(Q_OS_FREEBSD) + QEXPECT_FAIL("", "Broadcasting to 255.255.255.255 does not work on FreeBSD", Abort); +#endif + QVERIFY(broadcastSocket.waitForRead()); + QVERIFY(broadcastSocket.hasPendingDatagrams()); + + qlonglong available = broadcastSocket.pendingDatagramSize(); + QByteArray response; + response.resize(available); + QVERIFY(broadcastSocket.readDatagram(response.data(), response.size()) + == response.size()); + QCOMPARE(response, trollMessage); + +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::serverTest() +{ + PLATFORMSOCKETENGINE server; + + // Initialize a Tcp socket + QVERIFY(server.initialize(QAbstractSocket::TcpSocket)); + + // Bind to any port on all interfaces + QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0)); + QVERIFY(server.state() == QAbstractSocket::BoundState); + quint16 port = server.localPort(); + + // Listen for incoming connections + QVERIFY(server.listen()); + QVERIFY(server.state() == QAbstractSocket::ListeningState); + + // Initialize a Tcp socket + PLATFORMSOCKETENGINE client; + QVERIFY(client.initialize(QAbstractSocket::TcpSocket)); + if (!client.connectToHost(QHostAddress("127.0.0.1"), port)) { + QVERIFY(client.state() == QAbstractSocket::ConnectingState); + QVERIFY(client.waitForWrite()); + QVERIFY(client.state() == QAbstractSocket::ConnectedState); + } + + // The server accepts the connection + int socketDescriptor = server.accept(); + QVERIFY(socketDescriptor > 0); + + // A socket device is initialized on the server side, passing the + // socket descriptor from accept(). It's pre-connected. + PLATFORMSOCKETENGINE serverSocket; + QVERIFY(serverSocket.initialize(socketDescriptor)); + QVERIFY(serverSocket.state() == QAbstractSocket::ConnectedState); + + // The server socket sends a greeting to the clietn + QByteArray greeting = "Greetings!"; + QVERIFY(serverSocket.write(greeting.data(), + greeting.size()) == greeting.size()); + + // The client waits for the greeting to arrive + QVERIFY(client.waitForRead()); + qint64 available = client.bytesAvailable(); + QVERIFY(available > 0); + + // The client reads the greeting and checks that it's correct + QByteArray response; + response.resize(available); + QVERIFY(client.read(response.data(), + response.size()) == response.size()); + QCOMPARE(response, greeting); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::udpLoopbackPerformance() +{ +#ifdef SYMBIAN_WINSOCK_CONNECTIVITY + QSKIP("Not working on Emulator without WinPCAP", SkipAll); +#endif + PLATFORMSOCKETENGINE udpSocket; + + // Initialize device #1 + QVERIFY(udpSocket.initialize(QAbstractSocket::UdpSocket)); + QVERIFY(udpSocket.isValid()); + QVERIFY(udpSocket.socketDescriptor() != -1); + QVERIFY(udpSocket.protocol() == QAbstractSocket::IPv4Protocol); + QVERIFY(udpSocket.socketType() == QAbstractSocket::UdpSocket); + QVERIFY(udpSocket.state() == QAbstractSocket::UnconnectedState); + + // Bind #1 to localhost + QVERIFY(udpSocket.bind(QHostAddress("127.0.0.1"), 0)); + QVERIFY(udpSocket.state() == QAbstractSocket::BoundState); + quint16 port = udpSocket.localPort(); + QVERIFY(port != 0); + + // Initialize device #2 + PLATFORMSOCKETENGINE udpSocket2; + QVERIFY(udpSocket2.initialize(QAbstractSocket::UdpSocket)); + + // Connect device #2 to #1 + QVERIFY(udpSocket2.connectToHost(QHostAddress("127.0.0.1"), port)); + QVERIFY(udpSocket2.state() == QAbstractSocket::ConnectedState); + + const int messageSize = 8192; + QByteArray message1(messageSize, '@'); + QByteArray answer(messageSize, '@'); + + QHostAddress localhost = QHostAddress::LocalHost; + + qlonglong readBytes = 0; + QTime timer; + timer.start(); + while (timer.elapsed() < 5000) { + udpSocket2.write(message1.data(), message1.size()); + udpSocket.waitForRead(); + while (udpSocket.hasPendingDatagrams()) { + readBytes += (qlonglong) udpSocket.readDatagram(answer.data(), + answer.size()); + } + } + + qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", + readBytes / (1024.0 * 1024.0), + timer.elapsed() / 1024.0, + (readBytes / (timer.elapsed() / 1000.0)) / (1024 * 1024)); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::tcpLoopbackPerformance() +{ + PLATFORMSOCKETENGINE server; + + // Initialize a Tcp socket + QVERIFY(server.initialize(QAbstractSocket::TcpSocket)); + + // Bind to any port on all interfaces + QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0)); + QVERIFY(server.state() == QAbstractSocket::BoundState); + quint16 port = server.localPort(); + + // Listen for incoming connections + QVERIFY(server.listen()); + QVERIFY(server.state() == QAbstractSocket::ListeningState); + + // Initialize a Tcp socket + PLATFORMSOCKETENGINE client; + QVERIFY(client.initialize(QAbstractSocket::TcpSocket)); + + // Connect to our server + if (!client.connectToHost(QHostAddress("127.0.0.1"), port)) { + QVERIFY(client.state() == QAbstractSocket::ConnectingState); + QVERIFY(client.waitForWrite()); + QVERIFY(client.state() == QAbstractSocket::ConnectedState); + } + + // The server accepts the connection + int socketDescriptor = server.accept(); + QVERIFY(socketDescriptor > 0); + + // A socket device is initialized on the server side, passing the + // socket descriptor from accept(). It's pre-connected. + PLATFORMSOCKETENGINE serverSocket; + QVERIFY(serverSocket.initialize(socketDescriptor)); + QVERIFY(serverSocket.state() == QAbstractSocket::ConnectedState); + +#if defined (Q_OS_SYMBIAN) && defined (__WINS__) + const int messageSize = 1024 * 16; +#else + const int messageSize = 1024 * 256; +#endif + QByteArray message1(messageSize, '@'); + QByteArray answer(messageSize, '@'); + + QTime timer; + timer.start(); + qlonglong readBytes = 0; + while (timer.elapsed() < 5000) { + qlonglong written = serverSocket.write(message1.data(), message1.size()); + while (written > 0) { + client.waitForRead(); + if (client.bytesAvailable() > 0) { + qlonglong readNow = client.read(answer.data(), answer.size()); + written -= readNow; + readBytes += readNow; + } + } + } + + qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", + readBytes / (1024.0 * 1024.0), + timer.elapsed() / 1024.0, + (readBytes / (timer.elapsed() / 1000.0)) / (1024 * 1024)); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::readWriteBufferSize() +{ + PLATFORMSOCKETENGINE device; + + QVERIFY(device.initialize(QAbstractSocket::TcpSocket)); + + qint64 bufferSize = device.receiveBufferSize(); + QVERIFY(bufferSize != -1); + device.setReceiveBufferSize(bufferSize + 1); +#if defined(Q_OS_WINCE) + QEXPECT_FAIL(0, "Not supported by default on WinCE", Continue); +#endif + QVERIFY(device.receiveBufferSize() > bufferSize); + + bufferSize = device.sendBufferSize(); + QVERIFY(bufferSize != -1); + device.setSendBufferSize(bufferSize + 1); + QVERIFY(device.sendBufferSize() > bufferSize); + +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::tooManySockets() +{ +#if defined Q_OS_WIN + QSKIP("Certain windows machines suffocate and spend too much time in this test.", SkipAll); +#endif + QList<PLATFORMSOCKETENGINE *> sockets; + PLATFORMSOCKETENGINE *socketLayer = 0; + for (;;) { + socketLayer = new PLATFORMSOCKETENGINE; + sockets.append(socketLayer); + + if (!socketLayer->initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)) + break; + } + + QCOMPARE(socketLayer->error(), QAbstractSocket::SocketResourceError); + + qDeleteAll(sockets); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::bind() +{ +#if !defined Q_OS_WIN && !defined Q_OS_SYMBIAN + PLATFORMSOCKETENGINE binder; + QVERIFY(binder.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(!binder.bind(QHostAddress::Any, 82)); + QVERIFY(binder.error() == QAbstractSocket::SocketAccessError); +#endif + + PLATFORMSOCKETENGINE binder2; + QVERIFY(binder2.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(binder2.bind(QHostAddress::Any, 31180)); + + PLATFORMSOCKETENGINE binder3; + QVERIFY(binder3.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(!binder3.bind(QHostAddress::Any, 31180)); + +#ifdef SYMBIAN_WINSOCK_CONNECTIVITY + qDebug("On Symbian Emulator (WinSock) we get EADDRNOTAVAIL instead of EADDRINUSE"); + QVERIFY(binder3.error() == QAbstractSocket::SocketAddressNotAvailableError); +#else + QVERIFY(binder3.error() == QAbstractSocket::AddressInUseError); +#endif +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::networkError() +{ + PLATFORMSOCKETENGINE client; + + QVERIFY(client.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + + const bool isConnected = client.connectToHost(QtNetworkSettings::serverIP(), 143); + if (!isConnected) { + QVERIFY(client.state() == QAbstractSocket::ConnectingState); + QVERIFY(client.waitForWrite()); + QVERIFY(client.state() == QAbstractSocket::ConnectedState); + } + QVERIFY(client.state() == QAbstractSocket::ConnectedState); + + // An unexpected network error! +#ifdef Q_OS_WIN + // could use shutdown to produce different errors + ::closesocket(client.socketDescriptor()); +#elif defined(Q_OS_SYMBIAN) + RSocket sock; + QVERIFY(QSymbianSocketManager::instance().lookupSocket(client.socketDescriptor(), sock)); + TRequestStatus stat; + sock.Shutdown(RSocket::EImmediate, stat); + User::WaitForRequest(stat); +#else + ::close(client.socketDescriptor()); +#endif + + QVERIFY(client.read(0, 0) == -1); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::setSocketDescriptor() +{ + PLATFORMSOCKETENGINE socket1; + QVERIFY(socket1.initialize(QAbstractSocket::TcpSocket)); + + PLATFORMSOCKETENGINE socket2; + QVERIFY(socket2.initialize(socket1.socketDescriptor())); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::invalidSend() +{ + PLATFORMSOCKETENGINE socket; + QVERIFY(socket.initialize(QAbstractSocket::TcpSocket)); + + QTest::ignoreMessage(QtWarningMsg, PLATFORMSOCKETENGINESTRING "::writeDatagram() was" + " called by a socket other than QAbstractSocket::UdpSocket"); + QCOMPARE(socket.writeDatagram("hei", 3, QHostAddress::LocalHost, 143), + (qlonglong) -1); +} + +//--------------------------------------------------------------------------- +void tst_PlatformSocketEngine::receiveUrgentData() +{ + PLATFORMSOCKETENGINE server; + + QVERIFY(server.initialize(QAbstractSocket::TcpSocket)); + + // Bind to any port on all interfaces + QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0)); + QVERIFY(server.state() == QAbstractSocket::BoundState); + quint16 port = server.localPort(); + + QVERIFY(server.listen()); + QVERIFY(server.state() == QAbstractSocket::ListeningState); + + PLATFORMSOCKETENGINE client; + QVERIFY(client.initialize(QAbstractSocket::TcpSocket)); + + if (!client.connectToHost(QHostAddress("127.0.0.1"), port)) { + QVERIFY(client.state() == QAbstractSocket::ConnectingState); + QVERIFY(client.waitForWrite()); + QVERIFY(client.state() == QAbstractSocket::ConnectedState); + } + + int socketDescriptor = server.accept(); + QVERIFY(socketDescriptor > 0); + + PLATFORMSOCKETENGINE serverSocket; + QVERIFY(serverSocket.initialize(socketDescriptor)); + QVERIFY(serverSocket.state() == QAbstractSocket::ConnectedState); + + char msg; + int available; + QByteArray response; + +#if defined Q_OS_HPUX + QSKIP("Native OOB data test doesn't work on HP-UX.", SkipAll); +#elif defined (Q_OS_WINCE) + QSKIP("Native OOB data test doesn't work on WinCE.", SkipAll); +#endif + + // The server sends an urgent message + msg = 'Q'; +#if defined(Q_OS_SYMBIAN) + RSocket sock; + QVERIFY(QSymbianSocketManager::instance().lookupSocket(socketDescriptor, sock)); + TRequestStatus stat; + TSockXfrLength len; + sock.Send(TPtrC8((TUint8*)&msg,1), KSockWriteUrgent, stat, len); + User::WaitForRequest(stat); + QVERIFY(stat == KErrNone); + QCOMPARE(len(), 1); +#else + QCOMPARE(int(::send(socketDescriptor, &msg, sizeof(msg), MSG_OOB)), 1); +#endif + + // The client receives the urgent message + QVERIFY(client.waitForRead()); + available = client.bytesAvailable(); + QCOMPARE(available, 1); + response.resize(available); + QCOMPARE(client.read(response.data(), response.size()), qint64(1)); + QCOMPARE(response.at(0), msg); + + // The client sends an urgent message + msg = 'T'; + int clientDescriptor = client.socketDescriptor(); +#if defined(Q_OS_SYMBIAN) + QVERIFY(QSymbianSocketManager::instance().lookupSocket(clientDescriptor, sock)); + sock.Send(TPtrC8((TUint8*)&msg,1), KSockWriteUrgent, stat, len); + User::WaitForRequest(stat); + QVERIFY(stat == KErrNone); + QCOMPARE(len(), 1); +#else + QCOMPARE(int(::send(clientDescriptor, &msg, sizeof(msg), MSG_OOB)), 1); +#endif + + // The server receives the urgent message + QVERIFY(serverSocket.waitForRead()); + available = serverSocket.bytesAvailable(); + QCOMPARE(available, 1); + response.resize(available); + QCOMPARE(serverSocket.read(response.data(), response.size()), qint64(1)); + QCOMPARE(response.at(0), msg); + +} + +QTEST_MAIN(tst_PlatformSocketEngine) +#include "tst_platformsocketengine.moc" diff --git a/tests/auto/network/socket/qabstractsocket/.gitignore b/tests/auto/network/socket/qabstractsocket/.gitignore new file mode 100644 index 0000000000..b962a1a54b --- /dev/null +++ b/tests/auto/network/socket/qabstractsocket/.gitignore @@ -0,0 +1 @@ +tst_qabstractsocket diff --git a/tests/auto/network/socket/qabstractsocket/qabstractsocket.pro b/tests/auto/network/socket/qabstractsocket/qabstractsocket.pro new file mode 100644 index 0000000000..814a7d2600 --- /dev/null +++ b/tests/auto/network/socket/qabstractsocket/qabstractsocket.pro @@ -0,0 +1,11 @@ +############################################################ +# Project file for autotest for file qabstractsocket.h +############################################################ + +load(qttest_p4) +QT = core network + +SOURCES += tst_qabstractsocket.cpp + +symbian: TARGET.CAPABILITY = NetworkServices + diff --git a/tests/auto/network/socket/qabstractsocket/tst_qabstractsocket.cpp b/tests/auto/network/socket/qabstractsocket/tst_qabstractsocket.cpp new file mode 100644 index 0000000000..309215d5ea --- /dev/null +++ b/tests/auto/network/socket/qabstractsocket/tst_qabstractsocket.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** 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 <qdebug.h> +#include <qabstractsocket.h> + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QAbstractSocket : public QObject +{ +Q_OBJECT + +public: + tst_QAbstractSocket(); + virtual ~tst_QAbstractSocket(); + +private slots: + void getSetCheck(); +}; + +tst_QAbstractSocket::tst_QAbstractSocket() +{ +} + +tst_QAbstractSocket::~tst_QAbstractSocket() +{ +} + +class MyAbstractSocket : public QAbstractSocket +{ +public: + MyAbstractSocket() : QAbstractSocket(QAbstractSocket::TcpSocket, 0) {} + void setLocalPort(quint16 port) { QAbstractSocket::setLocalPort(port); } + void setPeerPort(quint16 port) { QAbstractSocket::setPeerPort(port); } +}; + +// Testing get/set functions +void tst_QAbstractSocket::getSetCheck() +{ + MyAbstractSocket obj1; + // qint64 QAbstractSocket::readBufferSize() + // void QAbstractSocket::setReadBufferSize(qint64) + obj1.setReadBufferSize(qint64(0)); + QCOMPARE(qint64(0), obj1.readBufferSize()); + obj1.setReadBufferSize((Q_INT64_C(-9223372036854775807) - 1)); + QCOMPARE((Q_INT64_C(-9223372036854775807) - 1), obj1.readBufferSize()); + obj1.setReadBufferSize(Q_INT64_C(9223372036854775807)); + QCOMPARE(Q_INT64_C(9223372036854775807), obj1.readBufferSize()); + + // quint16 QAbstractSocket::localPort() + // void QAbstractSocket::setLocalPort(quint16) + obj1.setLocalPort(quint16(0)); + QCOMPARE(quint16(0), obj1.localPort()); + obj1.setLocalPort(quint16(0xffff)); + QCOMPARE(quint16(0xffff), obj1.localPort()); + + // quint16 QAbstractSocket::peerPort() + // void QAbstractSocket::setPeerPort(quint16) + obj1.setPeerPort(quint16(0)); + QCOMPARE(quint16(0), obj1.peerPort()); + obj1.setPeerPort(quint16(0xffff)); + QCOMPARE(quint16(0xffff), obj1.peerPort()); +} + +QTEST_MAIN(tst_QAbstractSocket) +#include "tst_qabstractsocket.moc" diff --git a/tests/auto/network/socket/qhttpsocketengine/.gitignore b/tests/auto/network/socket/qhttpsocketengine/.gitignore new file mode 100644 index 0000000000..c3eb6526ba --- /dev/null +++ b/tests/auto/network/socket/qhttpsocketengine/.gitignore @@ -0,0 +1 @@ +tst_qhttpsocketengine diff --git a/tests/auto/network/socket/qhttpsocketengine/qhttpsocketengine.pro b/tests/auto/network/socket/qhttpsocketengine/qhttpsocketengine.pro new file mode 100644 index 0000000000..f26abbe8d9 --- /dev/null +++ b/tests/auto/network/socket/qhttpsocketengine/qhttpsocketengine.pro @@ -0,0 +1,13 @@ +load(qttest_p4) +SOURCES += tst_qhttpsocketengine.cpp + + +include(../platformsocketengine/platformsocketengine.pri) + +MOC_DIR=tmp + +QT = core-private network-private + +symbian: TARGET.CAPABILITY = NetworkServices + + diff --git a/tests/auto/network/socket/qhttpsocketengine/tst_qhttpsocketengine.cpp b/tests/auto/network/socket/qhttpsocketengine/tst_qhttpsocketengine.cpp new file mode 100644 index 0000000000..5a90abea0e --- /dev/null +++ b/tests/auto/network/socket/qhttpsocketengine/tst_qhttpsocketengine.cpp @@ -0,0 +1,748 @@ +/**************************************************************************** +** +** 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/QTest> +#include <QtTest/QTestEventLoop> +#include <QtCore/QQueue> +#include <QtCore/QString> +#include <QtCore/QCoreApplication> + +#include <private/qhttpsocketengine_p.h> +#include <qhostinfo.h> +#include <qhostaddress.h> +#include <qtcpsocket.h> +#include <qhttp.h> +#include <qdebug.h> +#include <qtcpserver.h> + +#include "../../../network-settings.h" + +class tst_QHttpSocketEngine : public QObject +{ + Q_OBJECT + +public: + tst_QHttpSocketEngine(); + virtual ~tst_QHttpSocketEngine(); + + +public slots: + void init(); + void cleanup(); +private slots: + void construction(); + void errorTest_data(); + void errorTest(); + void simpleConnectToIMAP(); + void simpleErrorsAndStates(); + + void tcpSocketBlockingTest(); + void tcpSocketNonBlockingTest(); + void downloadBigFile(); + // void tcpLoopbackPerformance(); + void passwordAuth(); + +protected slots: + void tcpSocketNonBlocking_hostFound(); + void tcpSocketNonBlocking_connected(); + void tcpSocketNonBlocking_closed(); + void tcpSocketNonBlocking_readyRead(); + void tcpSocketNonBlocking_bytesWritten(qint64); + void exitLoopSlot(); + void downloadBigFileSlot(); + +private: + QTcpSocket *tcpSocketNonBlocking_socket; + QStringList tcpSocketNonBlocking_data; + qint64 tcpSocketNonBlocking_totalWritten; + QTcpSocket *tmpSocket; + qint64 bytesAvailable; +}; + +class MiniHttpServer: public QTcpServer +{ + Q_OBJECT + QTcpSocket *client; + QList<QByteArray> dataToTransmit; + +public: + QByteArray receivedData; + + MiniHttpServer(const QList<QByteArray> &data) : client(0), dataToTransmit(data) + { + listen(); + connect(this, SIGNAL(newConnection()), this, SLOT(doAccept())); + } + +public slots: + void doAccept() + { + client = nextPendingConnection(); + connect(client, SIGNAL(readyRead()), this, SLOT(sendData())); + } + + void sendData() + { + receivedData += client->readAll(); + int idx = client->property("dataTransmitionIdx").toInt(); + if (receivedData.contains("\r\n\r\n") || + receivedData.contains("\n\n")) { + if (idx < dataToTransmit.length()) + client->write(dataToTransmit.at(idx++)); + if (idx == dataToTransmit.length()) { + client->disconnectFromHost(); + disconnect(client, 0, this, 0); + client = 0; + } else { + client->setProperty("dataTransmitionIdx", idx); + } + } + } +}; + +tst_QHttpSocketEngine::tst_QHttpSocketEngine() +{ + Q_SET_DEFAULT_IAP +} + +tst_QHttpSocketEngine::~tst_QHttpSocketEngine() +{ +} + + +void tst_QHttpSocketEngine::init() +{ + tmpSocket = 0; + bytesAvailable = 0; +} + +void tst_QHttpSocketEngine::cleanup() +{ +} + +//--------------------------------------------------------------------------- +void tst_QHttpSocketEngine::construction() +{ + QHttpSocketEngine socketDevice; + + QVERIFY(!socketDevice.isValid()); + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.isValid()); + QVERIFY(socketDevice.protocol() == QAbstractSocket::IPv4Protocol); + QVERIFY(socketDevice.socketType() == QAbstractSocket::TcpSocket); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + // QVERIFY(socketDevice.socketDescriptor() != -1); + QVERIFY(socketDevice.localAddress() == QHostAddress()); + QVERIFY(socketDevice.localPort() == 0); + QVERIFY(socketDevice.peerAddress() == QHostAddress()); + QVERIFY(socketDevice.peerPort() == 0); + QVERIFY(socketDevice.error() == QAbstractSocket::UnknownSocketError); + + //QTest::ignoreMessage(QtWarningMsg, "QSocketLayer::bytesAvailable() was called in QAbstractSocket::UnconnectedState"); + QVERIFY(socketDevice.bytesAvailable() == 0); + + //QTest::ignoreMessage(QtWarningMsg, "QSocketLayer::hasPendingDatagrams() was called in QAbstractSocket::UnconnectedState"); + QVERIFY(!socketDevice.hasPendingDatagrams()); +} + +//--------------------------------------------------------------------------- +void tst_QHttpSocketEngine::errorTest_data() +{ + QTest::addColumn<QString>("hostname"); + QTest::addColumn<int>("port"); + QTest::addColumn<QString>("username"); + QTest::addColumn<QString>("response"); + QTest::addColumn<int>("expectedError"); + + QQueue<QByteArray> responses; + QTest::newRow("proxy-host-not-found") << "this-host-does-not-exist." << 1080 << QString() + << QString() + << int(QAbstractSocket::ProxyNotFoundError); + QTest::newRow("proxy-connection-refused") << QtNetworkSettings::serverName() << 2 << QString() + << QString() + << int(QAbstractSocket::ProxyConnectionRefusedError); + + QTest::newRow("garbage1") << QString() << 0 << QString() + << "This is not HTTP\r\n\r\n" + << int(QAbstractSocket::ProxyProtocolError); + + QTest::newRow("garbage2") << QString() << 0 << QString() + << "This is not HTTP" + << int(QAbstractSocket::ProxyConnectionClosedError); + + QTest::newRow("garbage3") << QString() << 0 << QString() + << "" + << int(QAbstractSocket::ProxyConnectionClosedError); + + QTest::newRow("forbidden") << QString() << 0 << QString() + << "HTTP/1.0 403 Forbidden\r\n\r\n" + << int(QAbstractSocket::SocketAccessError); + + QTest::newRow("method-not-allowed") << QString() << 0 << QString() + << "HTTP/1.0 405 Method Not Allowed\r\n\r\n" + << int(QAbstractSocket::SocketAccessError); + + QTest::newRow("proxy-authentication-too-short") + << QString() << 0 << "foo" + << "HTTP/1.0 407 Proxy Authentication Required\r\n\r\n" + << int(QAbstractSocket::ProxyProtocolError); + + QTest::newRow("proxy-authentication-invalid-method") + << QString() << 0 << "foo" + << "HTTP/1.0 407 Proxy Authentication Required\r\n" + "Proxy-Authenticate: Frobnicator\r\n\r\n" + << int(QAbstractSocket::ProxyProtocolError); + + QTest::newRow("proxy-authentication-required") + << QString() << 0 << "foo" + << "HTTP/1.0 407 Proxy Authentication Required\r\n" + "Proxy-Connection: close\r\n" + "Proxy-Authenticate: Basic, realm=wonderland\r\n\r\n" + << int(QAbstractSocket::ProxyAuthenticationRequiredError); + + QTest::newRow("proxy-authentication-required2") + << QString() << 0 << "foo" + << "HTTP/1.0 407 Proxy Authentication Required\r\n" + "Proxy-Connection: keep-alive\r\n" + "Proxy-Authenticate: Basic, realm=wonderland\r\n\r\n" + "\1" + "HTTP/1.0 407 Proxy Authentication Required\r\n" + "Proxy-Authenticate: Basic, realm=wonderland\r\n\r\n" + << int(QAbstractSocket::ProxyAuthenticationRequiredError); + + QTest::newRow("proxy-authentication-required-noclose") + << QString() << 0 << "foo" + << "HTTP/1.0 407 Proxy Authentication Required\r\n" + "Proxy-Authenticate: Basic\r\n" + "\r\n" + << int(QAbstractSocket::ProxyAuthenticationRequiredError); + + QTest::newRow("connection-refused") << QString() << 0 << QString() + << "HTTP/1.0 503 Service Unavailable\r\n\r\n" + << int(QAbstractSocket::ConnectionRefusedError); + + QTest::newRow("host-not-found") << QString() << 0 << QString() + << "HTTP/1.0 404 Not Found\r\n\r\n" + << int(QAbstractSocket::HostNotFoundError); + + QTest::newRow("weird-http-reply") << QString() << 0 << QString() + << "HTTP/1.0 206 Partial Content\r\n\r\n" + << int(QAbstractSocket::ProxyProtocolError); +} + +void tst_QHttpSocketEngine::errorTest() +{ + QFETCH(QString, hostname); + QFETCH(int, port); + QFETCH(QString, username); + QFETCH(QString, response); + QFETCH(int, expectedError); + + MiniHttpServer server(response.toLatin1().split('\1')); + + if (hostname.isEmpty()) { + hostname = "127.0.0.1"; + port = server.serverPort(); + } + QTcpSocket socket; + socket.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, hostname, port, username, username)); + socket.connectToHost("0.1.2.3", 12345); + + connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), + &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(int(socket.error()), expectedError); +} + +//--------------------------------------------------------------------------- +void tst_QHttpSocketEngine::simpleConnectToIMAP() +{ + QHttpSocketEngine socketDevice; + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + + socketDevice.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3128)); + + QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::serverIP(), 143)); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectingState); + QVERIFY(socketDevice.waitForWrite()); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState); + QVERIFY(socketDevice.peerAddress() == QtNetworkSettings::serverIP()); + QVERIFY(!socketDevice.localAddress().isNull()); + QVERIFY(socketDevice.localPort() > 0); + + // Wait for the greeting + QVERIFY(socketDevice.waitForRead()); + + // Read the greeting + qint64 available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + QByteArray array; + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); + + + // Write a logout message + QByteArray array2 = "XXXX LOGOUT\r\n"; + QVERIFY(socketDevice.write(array2.data(), + array2.size()) == array2.size()); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + + available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n"); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + char c; + QCOMPARE(socketDevice.read(&c, sizeof(c)), (qint64) -1); + QVERIFY(socketDevice.error() == QAbstractSocket::RemoteHostClosedError); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); +} + +//--------------------------------------------------------------------------- +void tst_QHttpSocketEngine::simpleErrorsAndStates() +{ + { + QHttpSocketEngine socketDevice; + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + + socketDevice.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3128)); + + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + QVERIFY(!socketDevice.connectToHost(QHostAddress(QtNetworkSettings::serverName()), 8088)); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectingState); + if (socketDevice.waitForWrite(15000)) { + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState || + socketDevice.state() == QAbstractSocket::UnconnectedState); + } else { + QVERIFY(socketDevice.error() == QAbstractSocket::SocketTimeoutError); + } + } + +} + +/* +//--------------------------------------------------------------------------- +void tst_QHttpSocketEngine::tcpLoopbackPerformance() +{ + QTcpServer server; + + // Bind to any port on all interfaces + QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0)); + QVERIFY(server.state() == QAbstractSocket::BoundState); + quint16 port = server.localPort(); + + // Listen for incoming connections + QVERIFY(server.listen()); + QVERIFY(server.state() == QAbstractSocket::ListeningState); + + // Initialize a Tcp socket + QHttpSocketEngine client; + QVERIFY(client.initialize(QAbstractSocket::TcpSocket)); + + client.setProxy(QHostAddress("80.232.37.158"), 1081); + + // Connect to our server + if (!client.connectToHost(QHostAddress("127.0.0.1"), port)) { + QVERIFY(client.waitForWrite()); + QVERIFY(client.connectToHost(QHostAddress("127.0.0.1"), port)); + } + + // The server accepts the connectio + int socketDescriptor = server.accept(); + QVERIFY(socketDescriptor > 0); + + // A socket device is initialized on the server side, passing the + // socket descriptor from accept(). It's pre-connected. + QSocketLayer serverSocket; + QVERIFY(serverSocket.initialize(socketDescriptor)); + QVERIFY(serverSocket.state() == QAbstractSocket::ConnectedState); + + const int messageSize = 1024 * 256; + QByteArray message1(messageSize, '@'); + QByteArray answer(messageSize, '@'); + + QTime timer; + timer.start(); + qlonglong readBytes = 0; + while (timer.elapsed() < 5000) { + qlonglong written = serverSocket.write(message1.data(), message1.size()); + while (written > 0) { + client.waitForRead(); + if (client.bytesAvailable() > 0) { + qlonglong readNow = client.read(answer.data(), answer.size()); + written -= readNow; + readBytes += readNow; + } + } + } + + qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", + readBytes / (1024.0 * 1024.0), + timer.elapsed() / 1024.0, + (readBytes / (timer.elapsed() / 1000.0)) / (1024 * 1024)); +} +*/ + + + +void tst_QHttpSocketEngine::tcpSocketBlockingTest() +{ + QHttpSocketEngineHandler http; + + QTcpSocket socket; + + // Connect + socket.connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket.waitForConnected()); + QCOMPARE(socket.state(), QTcpSocket::ConnectedState); + + // Read greeting + QVERIFY(socket.waitForReadyRead(5000)); + QString s = socket.readLine(); + QVERIFY2(QtNetworkSettings::compareReplyIMAP(s.toLatin1()), qPrintable(s)); + + // Write NOOP + QCOMPARE((int) socket.write("1 NOOP\r\n", 8), 8); + + if (!socket.canReadLine()) + QVERIFY(socket.waitForReadyRead(5000)); + + // Read response + s = socket.readLine(); + QCOMPARE(s.toLatin1().constData(), "1 OK Completed\r\n"); + + // Write LOGOUT + QCOMPARE((int) socket.write("2 LOGOUT\r\n", 10), 10); + + if (!socket.canReadLine()) + QVERIFY(socket.waitForReadyRead(5000)); + + // Read two lines of respose + s = socket.readLine(); + QCOMPARE(s.toLatin1().constData(), "* BYE LOGOUT received\r\n"); + + if (!socket.canReadLine()) + QVERIFY(socket.waitForReadyRead(5000)); + + s = socket.readLine(); + QCOMPARE(s.toLatin1().constData(), "2 OK Completed\r\n"); + + // Close the socket + socket.close(); + + // Check that it's closed + QCOMPARE(socket.state(), QTcpSocket::UnconnectedState); +} + +//---------------------------------------------------------------------------------- + +void tst_QHttpSocketEngine::tcpSocketNonBlockingTest() +{ + QHttpSocketEngineHandler http; + + QTcpSocket socket; + connect(&socket, SIGNAL(hostFound()), SLOT(tcpSocketNonBlocking_hostFound())); + connect(&socket, SIGNAL(connected()), SLOT(tcpSocketNonBlocking_connected())); + connect(&socket, SIGNAL(disconnected()), SLOT(tcpSocketNonBlocking_closed())); + connect(&socket, SIGNAL(bytesWritten(qint64)), SLOT(tcpSocketNonBlocking_bytesWritten(qint64))); + connect(&socket, SIGNAL(readyRead()), SLOT(tcpSocketNonBlocking_readyRead())); + tcpSocketNonBlocking_socket = &socket; + + // Connect + socket.connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket.state() == QTcpSocket::HostLookupState || + socket.state() == QTcpSocket::ConnectingState); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + if (socket.state() == QTcpSocket::ConnectingState) { + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + } + + QCOMPARE(socket.state(), QTcpSocket::ConnectedState); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + // Read greeting + QVERIFY(!tcpSocketNonBlocking_data.isEmpty()); + QByteArray data = tcpSocketNonBlocking_data.at(0).toLatin1(); + QVERIFY2(QtNetworkSettings::compareReplyIMAP(data), data.constData()); + + + tcpSocketNonBlocking_data.clear(); + + tcpSocketNonBlocking_totalWritten = 0; + + // Write NOOP + QCOMPARE((int) socket.write("1 NOOP\r\n", 8), 8); + + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + QVERIFY(tcpSocketNonBlocking_totalWritten == 8); + + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + + // Read response + QVERIFY(!tcpSocketNonBlocking_data.isEmpty()); + QCOMPARE(tcpSocketNonBlocking_data.at(0).toLatin1().constData(), "1 OK Completed\r\n"); + tcpSocketNonBlocking_data.clear(); + + + tcpSocketNonBlocking_totalWritten = 0; + + // Write LOGOUT + QCOMPARE((int) socket.write("2 LOGOUT\r\n", 10), 10); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + QVERIFY(tcpSocketNonBlocking_totalWritten == 10); + + // Wait for greeting + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + // Read two lines of respose + QCOMPARE(tcpSocketNonBlocking_data.at(0).toLatin1().constData(), "* BYE LOGOUT received\r\n"); + QCOMPARE(tcpSocketNonBlocking_data.at(1).toLatin1().constData(), "2 OK Completed\r\n"); + tcpSocketNonBlocking_data.clear(); + + // Close the socket + socket.close(); + + // Check that it's closed + QCOMPARE(socket.state(), QTcpSocket::UnconnectedState); +} + +void tst_QHttpSocketEngine::tcpSocketNonBlocking_hostFound() +{ + QTestEventLoop::instance().exitLoop(); +} + +void tst_QHttpSocketEngine::tcpSocketNonBlocking_connected() +{ + QTestEventLoop::instance().exitLoop(); +} + +void tst_QHttpSocketEngine::tcpSocketNonBlocking_readyRead() +{ + while (tcpSocketNonBlocking_socket->canReadLine()) + tcpSocketNonBlocking_data.append(tcpSocketNonBlocking_socket->readLine()); + + QTestEventLoop::instance().exitLoop(); +} + +void tst_QHttpSocketEngine::tcpSocketNonBlocking_bytesWritten(qint64 written) +{ + tcpSocketNonBlocking_totalWritten += written; + QTestEventLoop::instance().exitLoop(); +} + +void tst_QHttpSocketEngine::tcpSocketNonBlocking_closed() +{ +} + +//---------------------------------------------------------------------------------- + +void tst_QHttpSocketEngine::downloadBigFile() +{ + QHttpSocketEngineHandler http; + + if (tmpSocket) + delete tmpSocket; + tmpSocket = new QTcpSocket; + + connect(tmpSocket, SIGNAL(connected()), SLOT(exitLoopSlot())); + connect(tmpSocket, SIGNAL(readyRead()), SLOT(downloadBigFileSlot())); + + tmpSocket->connectToHost(QtNetworkSettings::serverName(), 80); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) + QFAIL("Network operation timed out"); + + QByteArray hostName = QtNetworkSettings::serverName().toLatin1(); + QVERIFY(tmpSocket->state() == QAbstractSocket::ConnectedState); + QVERIFY(tmpSocket->write("GET /qtest/mediumfile HTTP/1.0\r\n") > 0); + QVERIFY(tmpSocket->write("Host: ") > 0); + QVERIFY(tmpSocket->write(hostName.data()) > 0); + QVERIFY(tmpSocket->write("\r\n") > 0); + QVERIFY(tmpSocket->write("\r\n") > 0); + + bytesAvailable = 0; + + QTime stopWatch; + stopWatch.start(); + +#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) + QTestEventLoop::instance().enterLoop(240); +#else + QTestEventLoop::instance().enterLoop(60); +#endif + if (QTestEventLoop::instance().timeout()) + QFAIL("Network operation timed out"); + + QVERIFY(bytesAvailable >= 10000000); + + QVERIFY(tmpSocket->state() == QAbstractSocket::ConnectedState); + + qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", + bytesAvailable / (1024.0 * 1024.0), + stopWatch.elapsed() / 1024.0, + (bytesAvailable / (stopWatch.elapsed() / 1000.0)) / (1024 * 1024)); + + delete tmpSocket; + tmpSocket = 0; +} + +void tst_QHttpSocketEngine::exitLoopSlot() +{ + QTestEventLoop::instance().exitLoop(); +} + + +void tst_QHttpSocketEngine::downloadBigFileSlot() +{ + bytesAvailable += tmpSocket->readAll().size(); + if (bytesAvailable >= 10000000) + QTestEventLoop::instance().exitLoop(); +} + +void tst_QHttpSocketEngine::passwordAuth() +{ + QHttpSocketEngine socketDevice; + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + + socketDevice.setProxy(QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3128, "qsockstest", "password")); + + QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::serverIP(), 143)); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectingState); + QVERIFY(socketDevice.waitForWrite()); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState); + QVERIFY(socketDevice.peerAddress() == QtNetworkSettings::serverIP()); + + // Wait for the greeting + QVERIFY(socketDevice.waitForRead()); + + // Read the greeting + qint64 available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + QByteArray array; + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); + + + // Write a logout message + QByteArray array2 = "XXXX LOGOUT\r\n"; + QVERIFY(socketDevice.write(array2.data(), + array2.size()) == array2.size()); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + + available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n"); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + char c; + QVERIFY(socketDevice.read(&c, sizeof(c)) == -1); + QVERIFY(socketDevice.error() == QAbstractSocket::RemoteHostClosedError); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); +} + +//---------------------------------------------------------------------------------- + +QTEST_MAIN(tst_QHttpSocketEngine) +#include "tst_qhttpsocketengine.moc" diff --git a/tests/auto/network/socket/qlocalsocket/.gitignore b/tests/auto/network/socket/qlocalsocket/.gitignore new file mode 100644 index 0000000000..b45c266ce3 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/.gitignore @@ -0,0 +1,2 @@ +tst_qlocalsocket +lackey/lackey.exe diff --git a/tests/auto/network/socket/qlocalsocket/example/client/client.pro b/tests/auto/network/socket/qlocalsocket/example/client/client.pro new file mode 100644 index 0000000000..84f20d6ec0 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/example/client/client.pro @@ -0,0 +1,10 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +CONFIG += console +QT = core network + +SOURCES += main.cpp + + diff --git a/tests/auto/network/socket/qlocalsocket/example/client/main.cpp b/tests/auto/network/socket/qlocalsocket/example/client/main.cpp new file mode 100644 index 0000000000..acf5cbc388 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/example/client/main.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** 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 <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include <string.h> +#include <qstring.h> +#include <qdebug.h> + +#include "qlocalsocket.h" + +#define SOCK_PATH "echo_socket" + +int main(void) +{ + QLocalSocket socket; + socket.connectToServer(SOCK_PATH); + socket.open(QIODevice::ReadWrite); + + printf("Connected.\n"); + char str[100]; + while(printf("> "), fgets(str, 100, stdin), !feof(stdin)) { + if (socket.write(str, strlen(str)) == -1) { + perror("send"); + return EXIT_FAILURE; + } + + int t; + if ((t = socket.read(str, 100)) > 0) { + str[t] = '\0'; + printf("echo> %s", str); + } else { + if (t < 0) + perror("recv"); + else + printf("Server closed connection.\n"); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + diff --git a/tests/auto/network/socket/qlocalsocket/example/example.pro b/tests/auto/network/socket/qlocalsocket/example/example.pro new file mode 100644 index 0000000000..8c678cd05a --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/example/example.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = client server + diff --git a/tests/auto/network/socket/qlocalsocket/example/server/main.cpp b/tests/auto/network/socket/qlocalsocket/example/server/main.cpp new file mode 100644 index 0000000000..0d3de3a2e1 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/example/server/main.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** 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 "qlocalserver.h" +#include "qlocalsocket.h" + +#include <qcoreapplication.h> +#include <qdebug.h> + +class EchoServer : public QLocalServer +{ +public: + void incomingConnection(int socketDescriptor) { + QLocalServer::incomingConnection(socketDescriptor); + QLocalSocket *socket = nextPendingConnection(); + socket->open(QIODevice::ReadWrite); + + qDebug() << "server connection"; + + do { + const int Timeout = 5 * 1000; + while (!socket->canReadLine()) { + if (!socket->waitForReadyRead(Timeout)) { + return; + } + } + char str[100]; + int n = socket->readLine(str, 100); + if (n < 0) { + perror("recv"); + break; + } + if (n == 0) + break; + qDebug() << "Read" << str; + if ("exit" == str) + qApp->quit(); + + if (socket->write(str, 100) < 0) { + perror("send"); + break; + } + } while (true); + } +}; + +#define SOCK_PATH "echo_socket" + +int main(int argc, char *argv[]) +{ + QCoreApplication application(argc, argv); + + EchoServer echoServer; + echoServer.listen(SOCK_PATH); + + return application.exec(); +} + diff --git a/tests/auto/network/socket/qlocalsocket/example/server/server.pro b/tests/auto/network/socket/qlocalsocket/example/server/server.pro new file mode 100644 index 0000000000..bfd14d2bb7 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/example/server/server.pro @@ -0,0 +1,13 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +CONFIG += console + +QT = core network + +# Input +SOURCES += main.cpp + + diff --git a/tests/auto/network/socket/qlocalsocket/lackey/lackey.pro b/tests/auto/network/socket/qlocalsocket/lackey/lackey.pro new file mode 100644 index 0000000000..2573222c8b --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/lackey/lackey.pro @@ -0,0 +1,16 @@ +#include(../src/src.pri) + +QT = core script network testlib + +DESTDIR = ./ + +win32: CONFIG += console +mac:CONFIG -= app_bundle + +DEFINES += QLOCALSERVER_DEBUG +DEFINES += QLOCALSOCKET_DEBUG + +SOURCES += main.cpp +TARGET = lackey + +symbian:TARGET.CAPABILITY = ALL -TCB diff --git a/tests/auto/network/socket/qlocalsocket/lackey/main.cpp b/tests/auto/network/socket/qlocalsocket/lackey/main.cpp new file mode 100644 index 0000000000..f7b2ff10b3 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/lackey/main.cpp @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** 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 <qscriptengine.h> + #include <QFile> +#include <QTest> + +#include <qlocalsocket.h> +#include <qlocalserver.h> + +class QScriptLocalSocket : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString serverName WRITE connectToServer READ serverName) + +public: + QScriptLocalSocket(QObject *parent = 0) : QObject(parent) + { + lc = new QLocalSocket(this); + } + +public slots: + QString serverName() + { + return lc->serverName(); + } + + void connectToServer(const QString &name) { + lc->connectToServer(name); + } + + void sleep(int x) const + { + QTest::qSleep(x); + } + + bool isConnected() { + return (lc->state() == QLocalSocket::ConnectedState); + } + + void open() { + lc->open(QIODevice::ReadWrite); + } + + bool waitForConnected() { + return lc->waitForConnected(100000); + } + void waitForReadyRead() { + lc->waitForReadyRead(); + } + + void write(const QString &string) { + QTextStream out(lc); + out << string << endl; + } + + bool waitForBytesWritten(int t = 3000) { + return lc->waitForBytesWritten(t); + } + + QString readLine() { + QTextStream in(lc); + return in.readLine(); + } + + QString errorString() { + return lc->errorString(); + } + + void close() { + lc->close(); + } + +public: + QLocalSocket *lc; +}; + +class QScriptLocalServer : public QLocalServer +{ + Q_OBJECT + Q_PROPERTY(int maxPendingConnections WRITE setMaxPendingConnections READ maxPendingConnections) + Q_PROPERTY(QString name WRITE listen READ serverName) + Q_PROPERTY(bool listening READ isListening) + +public: + QScriptLocalServer(QObject *parent = 0) : QLocalServer(parent) + { + } + +public slots: + bool listen(const QString &name) { + if (!QLocalServer::listen(name)) { + if (serverError() == QAbstractSocket::AddressInUseError) { + QFile::remove(serverName()); + return QLocalServer::listen(name); + } + return false; + } + return true; + } + + QScriptLocalSocket *nextConnection() { + QLocalSocket *other = nextPendingConnection(); + QScriptLocalSocket *s = new QScriptLocalSocket(this); + delete s->lc; + s->lc = other; + return s; + } + + bool waitForNewConnection() { + return QLocalServer::waitForNewConnection(30000); + } + + QString errorString() { + return QLocalServer::errorString(); + } + + +}; + +template <typename T> +static QScriptValue _q_ScriptValueFromQObject(QScriptEngine *engine, T* const &in) +{ + return engine->newQObject(in); +} +template <typename T> +static void _q_ScriptValueToQObject(const QScriptValue &v, T* &out) +{ out = qobject_cast<T*>(v.toQObject()); +} +template <typename T> +static int _q_ScriptRegisterQObjectMetaType(QScriptEngine *engine, const QScriptValue &prototype) +{ + return qScriptRegisterMetaType<T*>(engine, _q_ScriptValueFromQObject<T>, _q_ScriptValueToQObject<T>, prototype); +} + +QT_BEGIN_NAMESPACE +Q_SCRIPT_DECLARE_QMETAOBJECT(QScriptLocalSocket, QObject*); +Q_SCRIPT_DECLARE_QMETAOBJECT(QScriptLocalServer, QObject*); +QT_END_NAMESPACE + +static void interactive(QScriptEngine &eng) +{ + QTextStream qin(stdin, QFile::ReadOnly); + + const char *qscript_prompt = "qs> "; + const char *dot_prompt = ".... "; + const char *prompt = qscript_prompt; + + QString code; + + forever { + QString line; + + printf("%s", prompt); + fflush(stdout); + + line = qin.readLine(); + if (line.isNull()) + break; + + code += line; + code += QLatin1Char('\n'); + + if (line.trimmed().isEmpty()) { + continue; + + } else if (! eng.canEvaluate(code)) { + prompt = dot_prompt; + + } else { + QScriptValue result = eng.evaluate(code); + code.clear(); + prompt = qscript_prompt; + if (!result.isUndefined()) + fprintf(stderr, "%s\n", qPrintable(result.toString())); + } + } +} +Q_DECLARE_METATYPE(QScriptLocalSocket*) +Q_DECLARE_METATYPE(QScriptLocalServer*) +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + QScriptEngine eng; + QScriptValue globalObject = eng.globalObject(); + + _q_ScriptRegisterQObjectMetaType<QScriptLocalServer>(&eng, QScriptValue()); + + QScriptValue lss = qScriptValueFromQMetaObject<QScriptLocalServer>(&eng); + eng.globalObject().setProperty("QScriptLocalServer", lss); + + _q_ScriptRegisterQObjectMetaType<QScriptLocalSocket>(&eng, QScriptValue()); + + QScriptValue lsc = qScriptValueFromQMetaObject<QScriptLocalSocket>(&eng); + eng.globalObject().setProperty("QScriptLocalSocket", lsc); + + if (! *++argv) { + interactive(eng); + return EXIT_SUCCESS; + } + + QStringList arguments; + for (int i = 0; i < argc - 1; ++i) + arguments << QString::fromLocal8Bit(argv[i]); + + while (!arguments.isEmpty()) { + QString fn = arguments.takeFirst(); + + if (fn == QLatin1String("-i")) { + interactive(eng); + break; + } + + QString contents; + + if (fn == QLatin1String("-")) { + QTextStream stream(stdin, QFile::ReadOnly); + contents = stream.readAll(); + } else { + QFile file(fn); + if (!file.exists()) { + fprintf(stderr, "%s doesn't exists\n", qPrintable(fn)); + return EXIT_FAILURE; + } + if (file.open(QFile::ReadOnly)) { + QTextStream stream(&file); + contents = stream.readAll(); + file.close(); + } + } + + if (contents.isEmpty()) + continue; + + if (contents[0] == '#') { + contents.prepend("//"); + QScriptValue args = eng.newArray(); + args.setProperty("0", QScriptValue(&eng, fn)); + int i = 1; + while (!arguments.isEmpty()) + args.setProperty(i++, QScriptValue(&eng, arguments.takeFirst())); + eng.currentContext()->activationObject().setProperty("args", args); + } + QScriptValue r = eng.evaluate(contents); + if (eng.hasUncaughtException()) { + int line = eng.uncaughtExceptionLineNumber(); + fprintf(stderr, "%d: %s\n\t%s\n\n", line, qPrintable(fn), qPrintable(r.toString())); + return EXIT_FAILURE; + } + if (r.isNumber()) + return r.toInt32(); + } + + return EXIT_SUCCESS; +} + +#include "main.moc" diff --git a/tests/auto/network/socket/qlocalsocket/lackey/scripts/client.js b/tests/auto/network/socket/qlocalsocket/lackey/scripts/client.js new file mode 100755 index 0000000000..76cc0b97ad --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/lackey/scripts/client.js @@ -0,0 +1,35 @@ +#/bin/qscript +function QVERIFY(x, socket) { + if (!(x)) { + throw(socket.errorString()); + } +} + +var socket = new QScriptLocalSocket; +var tries = 0; +do { + socket.serverName = "qlocalsocket_autotest"; + if ((socket.errorString() != "QLocalSocket::connectToServer: Invalid name") + && (socket.errorString() != "QLocalSocket::connectToServer: Connection refused")) + break; + socket.sleep(1); + ++tries; + print("isConnected:", socket.isConnected()); +} while ((socket.errorString() == "QLocalSocket::connectToServer: Invalid name" + || (socket.errorString() == "QlocalSocket::connectToServer: Connection refused")) + && tries < 5000); +if (tries == 5000) { + print("too many tries, exiting"); +} else { +socket.waitForConnected(); +//print("isConnected:", socket.isConnected()); +if (!socket.isConnected()) + print("Not Connected:", socket.errorString()); +socket.waitForReadyRead(); +var text = socket.readLine(); +var testLine = "test"; +QVERIFY((text == testLine), socket); +QVERIFY((socket.errorString() == "Unknown error"), socket); +socket.close(); +//print("client: exiting", text); +} diff --git a/tests/auto/network/socket/qlocalsocket/lackey/scripts/server.js b/tests/auto/network/socket/qlocalsocket/lackey/scripts/server.js new file mode 100644 index 0000000000..98a83bc9dd --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/lackey/scripts/server.js @@ -0,0 +1,19 @@ +#/bin/qscript +function QVERIFY(x, server) { + if (!(x)) { + throw(server.errorString()); + } +} +var server = new QScriptLocalServer; +QVERIFY(server.listen("qlocalsocket_autotest"), server); +var done = args[1]; +var testLine = "test"; +while (done > 0) { + QVERIFY(server.waitForNewConnection(), server); + var serverSocket = server.nextConnection(); + serverSocket.write(testLine); + QVERIFY(serverSocket.waitForBytesWritten(), serverSocket); + QVERIFY(serverSocket.errorString() == "" + ||serverSocket.errorString() == "Unknown error", serverSocket); + --done; +} diff --git a/tests/auto/network/socket/qlocalsocket/qlocalsocket.pro b/tests/auto/network/socket/qlocalsocket/qlocalsocket.pro new file mode 100644 index 0000000000..931c1e0602 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/qlocalsocket.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +SUBDIRS = test # lackey should be moved to the QtScript module +!wince*:!symbian: SUBDIRS += example +symbian: TARGET.CAPABILITY = NetworkServices diff --git a/tests/auto/network/socket/qlocalsocket/test/test.pro b/tests/auto/network/socket/qlocalsocket/test/test.pro new file mode 100644 index 0000000000..b2755b5411 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/test/test.pro @@ -0,0 +1,50 @@ +load(qttest_p4) + +DEFINES += QLOCALSERVER_DEBUG +DEFINES += QLOCALSOCKET_DEBUG + +symbian { + # nothing +} else:wince* { + DEFINES += QT_LOCALSOCKET_TCP + DEFINES += SRCDIR=\\\"../\\\" +} else { + DEFINES += SRCDIR=\\\"$$PWD/../\\\" +} + +QT = core network + +SOURCES += ../tst_qlocalsocket.cpp + +TARGET = tst_qlocalsocket +CONFIG(debug_and_release) { + CONFIG(debug, debug|release) { + DESTDIR = ../debug + } else { + DESTDIR = ../release + } +} else { + DESTDIR = .. +} + +wince* { + additionalFiles.files = ../lackey/lackey.exe + additionalFiles.path = lackey +} + +symbian { + additionalFiles.files = lackey.exe + additionalFiles.path = \\sys\\bin + TARGET.UID3 = 0xE0340005 + DEFINES += SYMBIAN_SRCDIR_UID=$$lower($$replace(TARGET.UID3,"0x","")) +} + +wince*|symbian { + scriptFiles.files = ../lackey/scripts/*.js + scriptFiles.path = lackey/scripts + DEPLOYMENT += additionalFiles scriptFiles + QT += script # for easy deployment of QtScript + + requires(contains(QT_CONFIG,script)) +} + diff --git a/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp b/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp new file mode 100644 index 0000000000..57daa92f82 --- /dev/null +++ b/tests/auto/network/socket/qlocalsocket/tst_qlocalsocket.cpp @@ -0,0 +1,1120 @@ +/**************************************************************************** +** +** 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 <qtextstream.h> +#include <QtNetwork/qlocalsocket.h> +#include <QtNetwork/qlocalserver.h> +#include "../../../../shared/util.h" + +#ifdef Q_OS_SYMBIAN + #include <unistd.h> +#endif +//TESTED_CLASS=QLocalServer, QLocalSocket +//TESTED_FILES=network/socket/qlocalserver.cpp network/socket/qlocalsocket.cpp +#ifdef Q_OS_SYMBIAN + #define STRINGIFY(x) #x + #define TOSTRING(x) STRINGIFY(x) + #define SRCDIR "C:/Private/" TOSTRING(SYMBIAN_SRCDIR_UID) "/" +#endif +Q_DECLARE_METATYPE(QLocalSocket::LocalSocketError) +Q_DECLARE_METATYPE(QLocalSocket::LocalSocketState) + +class tst_QLocalSocket : public QObject +{ + Q_OBJECT + +public: + tst_QLocalSocket(); + virtual ~tst_QLocalSocket(); + +public Q_SLOTS: + void init(); + void cleanup(); + +private slots: + // basics + void server_basic(); + void server_connectionsCount(); + void socket_basic(); + + void listen_data(); + void listen(); + + void listenAndConnect_data(); + void listenAndConnect(); + + void sendData_data(); + void sendData(); + + void readBufferOverflow(); + + void fullPath(); + + void hitMaximumConnections_data(); + void hitMaximumConnections(); + + void setSocketDescriptor(); + + void threadedConnection_data(); + void threadedConnection(); + + void processConnection_data(); + void processConnection(); + + void longPath(); + void waitForDisconnect(); + void waitForDisconnectByServer(); + + void removeServer(); + + void recycleServer(); + + void multiConnect(); + void writeOnlySocket(); + void writeToClientAndDisconnect(); + void debug(); + void bytesWrittenSignal(); + void syncDisconnectNotify(); + void asyncDisconnectNotify(); + +#ifdef Q_OS_SYMBIAN +private: + void unlink(QString serverName); +#endif +}; + +tst_QLocalSocket::tst_QLocalSocket() +{ + if (!QFile::exists("lackey/lackey" +#ifdef Q_OS_WIN + ".exe" +#endif + )) + qWarning() << "lackey executable doesn't exists!"; +} + +tst_QLocalSocket::~tst_QLocalSocket() +{ +} + +void tst_QLocalSocket::init() +{ + qRegisterMetaType<QLocalSocket::LocalSocketState>("QLocalSocket::LocalSocketState"); + qRegisterMetaType<QLocalSocket::LocalSocketError>("QLocalSocket::LocalSocketError"); +} + +void tst_QLocalSocket::cleanup() +{ +} + +class LocalServer : public QLocalServer +{ + Q_OBJECT + +public: + LocalServer() : QLocalServer() + { + connect(this, SIGNAL(newConnection()), this, SLOT(slotNewConnection())); + } + + bool listen(const QString &name) + { + removeServer(name); + return QLocalServer::listen(name); + } + + QList<int> hits; + +protected: + void incomingConnection(quintptr socketDescriptor) + { + hits.append(socketDescriptor); + QLocalServer::incomingConnection(socketDescriptor); + } + +private slots: + void slotNewConnection() { + QVERIFY(!hits.isEmpty()); + QVERIFY(hasPendingConnections()); + } +}; + +class LocalSocket : public QLocalSocket +{ + Q_OBJECT + +public: + LocalSocket(QObject *parent = 0) : QLocalSocket(parent) + { + connect(this, SIGNAL(connected()), + this, SLOT(slotConnected())); + connect(this, SIGNAL(disconnected()), + this, SLOT(slotDisconnected())); + connect(this, SIGNAL(error(QLocalSocket::LocalSocketError)), + this, SLOT(slotError(QLocalSocket::LocalSocketError))); + connect(this, SIGNAL(stateChanged(QLocalSocket::LocalSocketState)), + this, SLOT(slotStateChanged(QLocalSocket::LocalSocketState))); + connect(this, SIGNAL(readyRead()), + this, SLOT(slotReadyRead())); + } + +private slots: + void slotConnected() + { + QCOMPARE(state(), QLocalSocket::ConnectedState); + } + void slotDisconnected() + { + QCOMPARE(state(), QLocalSocket::UnconnectedState); + } + void slotError(QLocalSocket::LocalSocketError newError) + { + QVERIFY(errorString() != "Unknown error"); + QCOMPARE(error(), newError); + } + void slotStateChanged(QLocalSocket::LocalSocketState newState) + { + QCOMPARE(state(), newState); + } + void slotReadyRead() + { + QVERIFY(bytesAvailable() > 0); + } +}; + +// basic test make sure no segfaults and check default values +void tst_QLocalSocket::server_basic() +{ + LocalServer server; + QSignalSpy spyNewConnection(&server, SIGNAL(newConnection())); + server.close(); + QCOMPARE(server.errorString(), QString()); + QCOMPARE(server.hasPendingConnections(), false); + QCOMPARE(server.isListening(), false); + QCOMPARE(server.maxPendingConnections(), 30); + QCOMPARE(server.nextPendingConnection(), (QLocalSocket*)0); + QCOMPARE(server.serverName(), QString()); + QCOMPARE(server.fullServerName(), QString()); + QCOMPARE(server.serverError(), QAbstractSocket::UnknownSocketError); + server.setMaxPendingConnections(20); + bool timedOut = true; + QCOMPARE(server.waitForNewConnection(3000, &timedOut), false); + QVERIFY(!timedOut); + QCOMPARE(server.listen(QString()), false); + + QCOMPARE(server.hits.count(), 0); + QCOMPARE(spyNewConnection.count(), 0); +} + +void tst_QLocalSocket::server_connectionsCount() +{ + LocalServer server; + server.setMaxPendingConnections(10); + QCOMPARE(server.maxPendingConnections(), 10); +} + +// basic test make sure no segfaults and check default values +void tst_QLocalSocket::socket_basic() +{ + LocalSocket socket; + QSignalSpy spyConnected(&socket, SIGNAL(connected())); + QSignalSpy spyDisconnected(&socket, SIGNAL(disconnected())); + QSignalSpy spyError(&socket, SIGNAL(error(QLocalSocket::LocalSocketError))); + QSignalSpy spyStateChanged(&socket, SIGNAL(stateChanged(QLocalSocket::LocalSocketState))); + QSignalSpy spyReadyRead(&socket, SIGNAL(readyRead())); + + QCOMPARE(socket.serverName(), QString()); + QCOMPARE(socket.fullServerName(), QString()); + socket.abort(); + QVERIFY(socket.bytesAvailable() == 0); + QVERIFY(socket.bytesToWrite() == 0); + QCOMPARE(socket.canReadLine(), false); + socket.close(); + socket.disconnectFromServer(); + QCOMPARE(QLocalSocket::UnknownSocketError, socket.error()); + QVERIFY(socket.errorString() != QString()); + QCOMPARE(socket.flush(), false); + QCOMPARE(socket.isValid(), false); + QVERIFY(socket.readBufferSize() == 0); + socket.setReadBufferSize(0); + //QCOMPARE(socket.socketDescriptor(), -1); + QCOMPARE(socket.state(), QLocalSocket::UnconnectedState); + QCOMPARE(socket.waitForConnected(0), false); + QCOMPARE(socket.waitForDisconnected(0), false); + QCOMPARE(socket.waitForReadyRead(0), false); + + QCOMPARE(spyConnected.count(), 0); + QCOMPARE(spyDisconnected.count(), 0); + QCOMPARE(spyError.count(), 0); + QCOMPARE(spyStateChanged.count(), 0); + QCOMPARE(spyReadyRead.count(), 0); +} + +void tst_QLocalSocket::listen_data() +{ + QTest::addColumn<QString>("name"); + QTest::addColumn<bool>("canListen"); + QTest::addColumn<bool>("close"); + QTest::newRow("null") << QString() << false << false; + QTest::newRow("tst_localsocket") << "tst_localsocket" << true << true; + QTest::newRow("tst_localsocket") << "tst_localsocket" << true << false; +} + +// start a server that listens, but don't connect a socket, make sure everything is in order +void tst_QLocalSocket::listen() +{ + LocalServer server; + QSignalSpy spyNewConnection(&server, SIGNAL(newConnection())); + + QFETCH(QString, name); +#ifdef Q_OS_SYMBIAN + unlink(name); +#endif + QFETCH(bool, canListen); + QFETCH(bool, close); + QVERIFY2((server.listen(name) == canListen), server.errorString().toLatin1().constData()); + + // test listening + QCOMPARE(server.serverName(), name); + QVERIFY(server.fullServerName().contains(name)); + QCOMPARE(server.isListening(), canListen); + QCOMPARE(server.hasPendingConnections(), false); + QCOMPARE(server.nextPendingConnection(), (QLocalSocket*)0); + QCOMPARE(server.hits.count(), 0); + QCOMPARE(spyNewConnection.count(), 0); + if (canListen) { + QVERIFY(server.errorString() == QString()); + QCOMPARE(server.serverError(), QAbstractSocket::UnknownSocketError); + // already isListening + QVERIFY(!server.listen(name)); + } else { + QVERIFY(server.errorString() != QString()); + QCOMPARE(server.serverError(), QAbstractSocket::HostNotFoundError); + } + QCOMPARE(server.maxPendingConnections(), 30); + bool timedOut = false; + QCOMPARE(server.waitForNewConnection(3000, &timedOut), false); + QCOMPARE(timedOut, canListen); + if (close) + server.close(); +} + +void tst_QLocalSocket::listenAndConnect_data() +{ + QTest::addColumn<QString>("name"); + QTest::addColumn<bool>("canListen"); + QTest::addColumn<int>("connections"); + for (int i = 0; i < 3; ++i) { + int connections = i; + if (i == 2) + connections = 5; + QTest::newRow(QString("null %1").arg(i).toLatin1()) << QString() << false << connections; + QTest::newRow(QString("tst_localsocket %1").arg(i).toLatin1()) << "tst_localsocket" << true << connections; + } +} + +void tst_QLocalSocket::listenAndConnect() +{ + LocalServer server; + QSignalSpy spyNewConnection(&server, SIGNAL(newConnection())); + + QFETCH(QString, name); + QFETCH(bool, canListen); +#ifdef Q_OS_SYMBIAN + unlink(name); +#endif + QCOMPARE(server.listen(name), canListen); + QTest::qWait(1000); + //QVERIFY(!server.errorString().isEmpty()); + QCOMPARE(server.serverError(), + canListen ? QAbstractSocket::UnknownSocketError : QAbstractSocket::HostNotFoundError); + + // test creating connection(s) + QFETCH(int, connections); + QList<QLocalSocket*> sockets; + for (int i = 0; i < connections; ++i) { + LocalSocket *socket = new LocalSocket; + + QSignalSpy spyConnected(socket, SIGNAL(connected())); + QSignalSpy spyDisconnected(socket, SIGNAL(disconnected())); + QSignalSpy spyError(socket, SIGNAL(error(QLocalSocket::LocalSocketError))); + QSignalSpy spyStateChanged(socket, SIGNAL(stateChanged(QLocalSocket::LocalSocketState))); + QSignalSpy spyReadyRead(socket, SIGNAL(readyRead())); + + socket->connectToServer(name); +#if defined(QT_LOCALSOCKET_TCP) + QTest::qWait(250); +#endif + + QCOMPARE(socket->serverName(), name); + QVERIFY(socket->fullServerName().contains(name)); + sockets.append(socket); + if (canListen) { + QVERIFY(socket->waitForConnected()); + QVERIFY(socket->isValid()); + QCOMPARE(socket->errorString(), QString("Unknown error")); + QCOMPARE(socket->error(), QLocalSocket::UnknownSocketError); + QCOMPARE(socket->state(), QLocalSocket::ConnectedState); + //QVERIFY(socket->socketDescriptor() != -1); + QCOMPARE(spyError.count(), 0); + } else { + QVERIFY(socket->errorString() != QString()); + QVERIFY(socket->error() != QLocalSocket::UnknownSocketError); + QCOMPARE(socket->state(), QLocalSocket::UnconnectedState); + //QVERIFY(socket->socketDescriptor() == -1); + QCOMPARE(qVariantValue<QLocalSocket::LocalSocketError>(spyError.first()[0]), + QLocalSocket::ServerNotFoundError); + } + + QVERIFY(socket->bytesAvailable() == 0); + QVERIFY(socket->bytesToWrite() == 0); + QCOMPARE(socket->canReadLine(), false); + QCOMPARE(socket->flush(), false); + QCOMPARE(socket->isValid(), canListen); + QCOMPARE(socket->readBufferSize(), (qint64)0); + QCOMPARE(socket->waitForConnected(0), canListen); + QCOMPARE(socket->waitForReadyRead(0), false); + + QTRY_COMPARE(spyConnected.count(), canListen ? 1 : 0); + QCOMPARE(spyDisconnected.count(), 0); + + // error signals + QVERIFY(spyError.count() >= 0); + if (canListen) { + if (spyError.count() > 0) + QCOMPARE(qVariantValue<QLocalSocket::LocalSocketError>(spyError.first()[0]), + QLocalSocket::SocketTimeoutError); + } else { + QCOMPARE(qVariantValue<QLocalSocket::LocalSocketError>(spyError.first()[0]), + QLocalSocket::ServerNotFoundError); + } + + // Check first and last state + QCOMPARE(qVariantValue<QLocalSocket::LocalSocketState>(spyStateChanged.first()[0]), + QLocalSocket::ConnectingState); +#if 0 + for (int j = 0; j < spyStateChanged.count(); ++j) { + QLocalSocket::LocalSocketState s; + s = qVariantValue<QLocalSocket::LocalSocketState>(spyStateChanged.at(j).at(0)); + qDebug() << s; + } +#endif + if (canListen) + QCOMPARE(qVariantValue<QLocalSocket::LocalSocketState>(spyStateChanged.last()[0]), + QLocalSocket::ConnectedState); + QCOMPARE(spyStateChanged.count(), 2); + QCOMPARE(spyReadyRead.count(), 0); + + bool timedOut = true; + QCOMPARE(server.waitForNewConnection(3000, &timedOut), canListen); + QVERIFY(!timedOut); + QCOMPARE(server.hasPendingConnections(), canListen); + QCOMPARE(server.isListening(), canListen); + // NOTE: socket disconnecting is not tested here + + // server checks post connection + if (canListen) { + QCOMPARE(server.serverName(), name); + QVERIFY(server.fullServerName().contains(name)); + QVERIFY(server.nextPendingConnection() != (QLocalSocket*)0); + QTRY_COMPARE(server.hits.count(), i + 1); + QCOMPARE(spyNewConnection.count(), i + 1); + QVERIFY(server.errorString() == QString()); + QCOMPARE(server.serverError(), QAbstractSocket::UnknownSocketError); + } else { + QVERIFY(server.serverName().isEmpty()); + QVERIFY(server.fullServerName().isEmpty()); + QVERIFY(server.nextPendingConnection() == (QLocalSocket*)0); + QCOMPARE(spyNewConnection.count(), 0); + QCOMPARE(server.hits.count(), 0); + QVERIFY(server.errorString() != QString()); + QCOMPARE(server.serverError(), QAbstractSocket::HostNotFoundError); + } + } + qDeleteAll(sockets.begin(), sockets.end()); + + server.close(); + + QCOMPARE(server.hits.count(), (canListen ? connections : 0)); + QCOMPARE(spyNewConnection.count(), (canListen ? connections : 0)); +} + +void tst_QLocalSocket::sendData_data() +{ + listenAndConnect_data(); +} + +void tst_QLocalSocket::sendData() +{ + QFETCH(QString, name); +#ifdef Q_OS_SYMBIAN + unlink(name); +#endif + QFETCH(bool, canListen); + + LocalServer server; + QSignalSpy spy(&server, SIGNAL(newConnection())); + + QCOMPARE(server.listen(name), canListen); + + LocalSocket socket; + QSignalSpy spyConnected(&socket, SIGNAL(connected())); + QSignalSpy spyDisconnected(&socket, SIGNAL(disconnected())); + QSignalSpy spyError(&socket, SIGNAL(error(QLocalSocket::LocalSocketError))); + QSignalSpy spyStateChanged(&socket, SIGNAL(stateChanged(QLocalSocket::LocalSocketState))); + QSignalSpy spyReadyRead(&socket, SIGNAL(readyRead())); + + // test creating a connection + socket.connectToServer(name); + bool timedOut = true; + + QCOMPARE(server.waitForNewConnection(3000, &timedOut), canListen); + +#if defined(QT_LOCALSOCKET_TCP) + QTest::qWait(250); +#endif + QVERIFY(!timedOut); + QCOMPARE(spyConnected.count(), canListen ? 1 : 0); + QCOMPARE(socket.state(), canListen ? QLocalSocket::ConnectedState : QLocalSocket::UnconnectedState); + + // test sending/receiving data + if (server.hasPendingConnections()) { + QString testLine = "test"; +#ifdef Q_OS_SYMBIAN + for (int i = 0; i < 25 * 1024; ++i) +#else + for (int i = 0; i < 50000; ++i) +#endif + testLine += "a"; + QLocalSocket *serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket); + QCOMPARE(serverSocket->state(), QLocalSocket::ConnectedState); + QTextStream out(serverSocket); + QTextStream in(&socket); + out << testLine << endl; + bool wrote = serverSocket->waitForBytesWritten(3000); + + if (!socket.canReadLine()) + QVERIFY(socket.waitForReadyRead()); + + QVERIFY(socket.bytesAvailable() >= 0); + QCOMPARE(socket.bytesToWrite(), (qint64)0); + QCOMPARE(socket.flush(), false); + QCOMPARE(socket.isValid(), canListen); + QCOMPARE(socket.readBufferSize(), (qint64)0); + QCOMPARE(spyReadyRead.count(), 1); + + QVERIFY(testLine.startsWith(in.readLine())); + + QVERIFY(wrote || serverSocket->waitForBytesWritten(1000)); + + QCOMPARE(serverSocket->errorString(), QString("Unknown error")); + QCOMPARE(socket.errorString(), QString("Unknown error")); + } + + socket.disconnectFromServer(); + QCOMPARE(spyConnected.count(), canListen ? 1 : 0); + QCOMPARE(spyDisconnected.count(), canListen ? 1 : 0); + QCOMPARE(spyError.count(), canListen ? 0 : 1); + QCOMPARE(spyStateChanged.count(), canListen ? 4 : 2); + QCOMPARE(spyReadyRead.count(), canListen ? 1 : 0); + + server.close(); + + QCOMPARE(server.hits.count(), (canListen ? 1 : 0)); + QCOMPARE(spy.count(), (canListen ? 1 : 0)); +} + +void tst_QLocalSocket::readBufferOverflow() +{ + const int readBufferSize = 128; + const int dataBufferSize = readBufferSize * 2; + const QString serverName = QLatin1String("myPreciousTestServer"); + LocalServer server; + server.listen(serverName); + QVERIFY(server.isListening()); + + LocalSocket client; + client.setReadBufferSize(readBufferSize); + client.connectToServer(serverName); + + bool timedOut = true; + QVERIFY(server.waitForNewConnection(3000, &timedOut)); + QVERIFY(!timedOut); + + QCOMPARE(client.state(), QLocalSocket::ConnectedState); + QVERIFY(server.hasPendingConnections()); + + QLocalSocket* serverSocket = server.nextPendingConnection(); + char buffer[dataBufferSize]; + memset(buffer, 0, dataBufferSize); + serverSocket->write(buffer, dataBufferSize); + serverSocket->waitForBytesWritten(); + + // wait until the first 128 bytes are ready to read + QVERIFY(client.waitForReadyRead()); + QCOMPARE(client.read(buffer, readBufferSize), qint64(readBufferSize)); + // wait until the second 128 bytes are ready to read + QVERIFY(client.waitForReadyRead()); + QCOMPARE(client.read(buffer, readBufferSize), qint64(readBufferSize)); + // no more bytes available + QVERIFY(client.bytesAvailable() == 0); +} + +// QLocalSocket/Server can take a name or path, check that it works as expected +void tst_QLocalSocket::fullPath() +{ + QLocalServer server; + QString name = "qlocalsocket_pathtest"; +#if defined(Q_OS_SYMBIAN) + QString path = ""; +#elif defined(QT_LOCALSOCKET_TCP) + QString path = "QLocalServer"; +#elif defined(Q_OS_WIN) + QString path = "\\\\.\\pipe\\"; +#else + QString path = "/tmp"; +#endif + QString serverName = path + '/' + name; + QVERIFY2(server.listen(serverName), server.errorString().toLatin1().constData()); + QCOMPARE(server.serverName(), serverName); + QCOMPARE(server.fullServerName(), serverName); + + LocalSocket socket; + socket.connectToServer(serverName); + + QCOMPARE(socket.serverName(), serverName); + QCOMPARE(socket.fullServerName(), serverName); + socket.disconnectFromServer(); +#ifdef QT_LOCALSOCKET_TCP + QTest::qWait(250); +#endif + QCOMPARE(socket.serverName(), QString()); + QCOMPARE(socket.fullServerName(), QString()); +} + +void tst_QLocalSocket::hitMaximumConnections_data() +{ + QTest::addColumn<int>("max"); + QTest::newRow("none") << 0; + QTest::newRow("1") << 1; + QTest::newRow("3") << 3; +} + +void tst_QLocalSocket::hitMaximumConnections() +{ + QFETCH(int, max); + LocalServer server; + QString name = "tst_localsocket"; +#ifdef Q_OS_SYMBIAN + unlink(name); +#endif + server.setMaxPendingConnections(max); + QVERIFY2(server.listen(name), server.errorString().toLatin1().constData()); + int connections = server.maxPendingConnections() + 1; + QList<QLocalSocket*> sockets; + for (int i = 0; i < connections; ++i) { + LocalSocket *socket = new LocalSocket; + sockets.append(socket); + socket->connectToServer(name); + } + bool timedOut = true; + QVERIFY(server.waitForNewConnection(3000, &timedOut)); + QVERIFY(!timedOut); + QVERIFY(server.hits.count() > 0); + qDeleteAll(sockets.begin(), sockets.end()); +} + +// check that state and mode are kept +void tst_QLocalSocket::setSocketDescriptor() +{ + LocalSocket socket; + quintptr minusOne = -1; + socket.setSocketDescriptor(minusOne, QLocalSocket::ConnectingState, QIODevice::Append); + QCOMPARE(socket.socketDescriptor(), minusOne); + QCOMPARE(socket.state(), QLocalSocket::ConnectingState); + QVERIFY((socket.openMode() & QIODevice::Append) != 0); +} + +class Client : public QThread +{ + +public: + void run() + { + QString testLine = "test"; + LocalSocket socket; + QSignalSpy spyReadyRead(&socket, SIGNAL(readyRead())); + socket.connectToServer("qlocalsocket_threadtest"); + QVERIFY(socket.waitForConnected(1000)); + + // We should *not* have this signal yet! + QCOMPARE(spyReadyRead.count(), 0); + socket.waitForReadyRead(); + QCOMPARE(spyReadyRead.count(), 1); + QTextStream in(&socket); + QCOMPARE(in.readLine(), testLine); + socket.close(); + } +}; + +class Server : public QThread +{ + +public: + int clients; + QMutex mutex; + QWaitCondition wc; + void run() + { + QString testLine = "test"; + LocalServer server; + server.setMaxPendingConnections(10); + QVERIFY2(server.listen("qlocalsocket_threadtest"), + server.errorString().toLatin1().constData()); + mutex.lock(); + wc.wakeAll(); + mutex.unlock(); + int done = clients; + while (done > 0) { + bool timedOut = true; + QVERIFY(server.waitForNewConnection(7000, &timedOut)); + QVERIFY(!timedOut); + QLocalSocket *serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket); + QTextStream out(serverSocket); + out << testLine << endl; + QCOMPARE(serverSocket->state(), QLocalSocket::ConnectedState); + QVERIFY2(serverSocket->waitForBytesWritten(), serverSocket->errorString().toLatin1().constData()); + QCOMPARE(serverSocket->errorString(), QString("Unknown error")); + --done; + delete serverSocket; + } + QCOMPARE(server.hits.count(), clients); + } +}; + +void tst_QLocalSocket::threadedConnection_data() +{ + QTest::addColumn<int>("threads"); + QTest::newRow("1 client") << 1; + QTest::newRow("2 clients") << 2; + QTest::newRow("5 clients") << 5; +#ifndef Q_OS_WINCE + QTest::newRow("10 clients") << 10; + QTest::newRow("20 clients") << 20; +#endif +} + +void tst_QLocalSocket::threadedConnection() +{ +#ifdef Q_OS_SYMBIAN + unlink("qlocalsocket_threadtest"); +#endif + + QFETCH(int, threads); + Server server; +#if defined(Q_OS_SYMBIAN) + server.setStackSize(0x14000); +#endif + server.clients = threads; + server.mutex.lock(); + server.start(); + server.wc.wait(&server.mutex); + + QList<Client*> clients; + for (int i = 0; i < threads; ++i) { + clients.append(new Client()); +#if defined(Q_OS_SYMBIAN) + clients.last()->setStackSize(0x14000); +#endif + clients.last()->start(); + } + + server.wait(); + while (!clients.isEmpty()) { + QVERIFY(clients.first()->wait(3000)); + delete clients.takeFirst(); + } +} + +void tst_QLocalSocket::processConnection_data() +{ + QTest::addColumn<int>("processes"); + QTest::newRow("1 client") << 1; +#ifndef Q_OS_WIN + QTest::newRow("2 clients") << 2; + QTest::newRow("5 clients") << 5; +#endif + QTest::newRow("30 clients") << 30; +} + +/*! + Create external processes that produce and consume. + */ +void tst_QLocalSocket::processConnection() +{ +#if defined(QT_NO_PROCESS) || defined(Q_CC_NOKIAX86) + QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll); +#else + QFETCH(int, processes); + QStringList serverArguments = QStringList() << SRCDIR "lackey/scripts/server.js" << QString::number(processes); + QProcess producer; + producer.setProcessChannelMode(QProcess::ForwardedChannels); +#ifdef Q_WS_QWS + serverArguments << "-qws"; +#endif + QList<QProcess*> consumers; + producer.start("lackey/lackey", serverArguments); + QVERIFY(producer.waitForStarted(-1)); + QTest::qWait(2000); + for (int i = 0; i < processes; ++i) { + QStringList arguments = QStringList() << SRCDIR "lackey/scripts/client.js"; +#ifdef Q_WS_QWS + arguments << "-qws"; +#endif + QProcess *p = new QProcess; + p->setProcessChannelMode(QProcess::ForwardedChannels); + consumers.append(p); + p->start("lackey/lackey", arguments); + } + + while (!consumers.isEmpty()) { + consumers.first()->waitForFinished(20000); + QCOMPARE(consumers.first()->exitStatus(), QProcess::NormalExit); + QCOMPARE(consumers.first()->exitCode(), 0); + QProcess *consumer = consumers.takeFirst(); + consumer->terminate(); + delete consumer; + } + producer.waitForFinished(15000); +#endif +} + +void tst_QLocalSocket::longPath() +{ +#ifndef Q_OS_WIN + QString name; + for (int i = 0; i < 256; ++i) + name += 'a'; + LocalServer server; + QVERIFY(!server.listen(name)); + + LocalSocket socket; + socket.connectToServer(name); + QCOMPARE(socket.state(), QLocalSocket::UnconnectedState); +#endif +} + +void tst_QLocalSocket::waitForDisconnect() +{ + QString name = "tst_localsocket"; +#ifdef Q_OS_SYMBIAN + unlink(name); +#endif + LocalServer server; + QVERIFY(server.listen(name)); + LocalSocket socket; + socket.connectToServer(name); + QVERIFY(socket.waitForConnected(3000)); + QVERIFY(server.waitForNewConnection(3000)); + QLocalSocket *serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket); + socket.disconnectFromServer(); + QTime timer; + timer.start(); + QVERIFY(serverSocket->waitForDisconnected(3000)); + QVERIFY(timer.elapsed() < 2000); +} + +void tst_QLocalSocket::waitForDisconnectByServer() +{ + QString name = "tst_localsocket"; + LocalServer server; + QVERIFY(server.listen(name)); + LocalSocket socket; + QSignalSpy spy(&socket, SIGNAL(disconnected())); + QVERIFY(spy.isValid()); + socket.connectToServer(name); + QVERIFY(socket.waitForConnected(3000)); + QVERIFY(server.waitForNewConnection(3000)); + QLocalSocket *serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket); + serverSocket->close(); + QVERIFY(serverSocket->state() == QLocalSocket::UnconnectedState); + QVERIFY(socket.waitForDisconnected(3000)); + QCOMPARE(spy.count(), 1); +} + +void tst_QLocalSocket::removeServer() +{ + // this is a hostile takeover, but recovering from a crash results in the same + QLocalServer server, server2; + QVERIFY(QLocalServer::removeServer("cleanuptest")); + QVERIFY(server.listen("cleanuptest")); +#ifndef Q_OS_WIN + // on Windows, there can be several sockets listening on the same pipe + // on Unix, there can only be one socket instance + QVERIFY(! server2.listen("cleanuptest")); +#endif + QVERIFY(QLocalServer::removeServer("cleanuptest")); + QVERIFY(server2.listen("cleanuptest")); +} + +void tst_QLocalSocket::recycleServer() +{ +#ifdef Q_OS_SYMBIAN + unlink("recycletest1"); +#endif + + QLocalServer server; + QLocalSocket client; + + QVERIFY(server.listen("recycletest1")); + client.connectToServer("recycletest1"); + QVERIFY(client.waitForConnected(201)); + QVERIFY(server.waitForNewConnection(201)); + QVERIFY(server.nextPendingConnection() != 0); + + server.close(); + client.disconnectFromServer(); + qApp->processEvents(); + + QVERIFY(server.listen("recycletest2")); + client.connectToServer("recycletest2"); + QVERIFY(client.waitForConnected(202)); + QVERIFY(server.waitForNewConnection(202)); + QVERIFY(server.nextPendingConnection() != 0); +} + +void tst_QLocalSocket::multiConnect() +{ + QLocalServer server; + QLocalSocket client1; + QLocalSocket client2; + QLocalSocket client3; + + QVERIFY(server.listen("multiconnect")); + + client1.connectToServer("multiconnect"); + client2.connectToServer("multiconnect"); + client3.connectToServer("multiconnect"); + + QVERIFY(client1.waitForConnected(201)); + QVERIFY(client2.waitForConnected(202)); + QVERIFY(client3.waitForConnected(203)); + + QVERIFY(server.waitForNewConnection(201)); + QVERIFY(server.nextPendingConnection() != 0); + QVERIFY(server.waitForNewConnection(202)); + QVERIFY(server.nextPendingConnection() != 0); + QVERIFY(server.waitForNewConnection(203)); + QVERIFY(server.nextPendingConnection() != 0); +} + +void tst_QLocalSocket::writeOnlySocket() +{ + QLocalServer server; +#ifdef Q_OS_SYMBIAN + unlink("writeOnlySocket"); +#endif + QVERIFY(server.listen("writeOnlySocket")); + + QLocalSocket client; + client.connectToServer("writeOnlySocket", QIODevice::WriteOnly); + QVERIFY(client.waitForConnected()); +#if defined(Q_OS_SYMBIAN) + QTest::qWait(250); +#endif + QVERIFY(server.waitForNewConnection(200)); + QLocalSocket* serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket); + + QCOMPARE(client.bytesAvailable(), qint64(0)); + QCOMPARE(client.state(), QLocalSocket::ConnectedState); +} + +void tst_QLocalSocket::writeToClientAndDisconnect() +{ +#ifdef Q_OS_SYMBIAN + unlink("writeAndDisconnectServer"); +#endif + + QLocalServer server; + QLocalSocket client; + QSignalSpy readChannelFinishedSpy(&client, SIGNAL(readChannelFinished())); + + QVERIFY(server.listen("writeAndDisconnectServer")); + client.connectToServer("writeAndDisconnectServer"); + QVERIFY(client.waitForConnected(200)); + QVERIFY(server.waitForNewConnection(200)); + QLocalSocket* clientSocket = server.nextPendingConnection(); + QVERIFY(clientSocket); + + char buffer[100]; + memset(buffer, 0, sizeof(buffer)); + QCOMPARE(clientSocket->write(buffer, sizeof(buffer)), (qint64)sizeof(buffer)); + clientSocket->waitForBytesWritten(); + clientSocket->close(); + server.close(); + + QTRY_COMPARE(readChannelFinishedSpy.count(), 1); + QCOMPARE(client.read(buffer, sizeof(buffer)), (qint64)sizeof(buffer)); + client.waitForDisconnected(); + QCOMPARE(client.state(), QLocalSocket::UnconnectedState); +} + +void tst_QLocalSocket::debug() +{ + // Make sure this compiles + qDebug() << QLocalSocket::ConnectionRefusedError << QLocalSocket::UnconnectedState; +} + +class WriteThread : public QThread +{ +Q_OBJECT +public: + void run() { + QLocalSocket socket; + socket.connectToServer("qlocalsocket_readyread"); + + if (!socket.waitForConnected(3000)) + exec(); + connect(&socket, SIGNAL(bytesWritten(qint64)), + this, SLOT(bytesWritten(qint64)), Qt::QueuedConnection); + socket.write("testing\n"); + exec(); + } +public slots: + void bytesWritten(qint64) { + exit(); + } + +private: +}; + +/* + Tests the emission of the bytesWritten(qint64) + signal. + + Create a thread that will write to a socket. + If the bytesWritten(qint64) signal is generated, + the slot connected to it will exit the thread, + indicating test success. + +*/ +void tst_QLocalSocket::bytesWrittenSignal() +{ + QLocalServer server; + QVERIFY(server.listen("qlocalsocket_readyread")); + WriteThread writeThread; + writeThread.start(); + bool timedOut = false; + QVERIFY(server.waitForNewConnection(3000, &timedOut)); + QVERIFY(!timedOut); + QTest::qWait(2000); + QVERIFY(writeThread.wait(2000)); +} + +void tst_QLocalSocket::syncDisconnectNotify() +{ +#ifdef Q_OS_SYMBIAN + unlink("syncDisconnectNotify"); +#endif + + QLocalServer server; + QVERIFY(server.listen("syncDisconnectNotify")); + QLocalSocket client; + client.connectToServer("syncDisconnectNotify"); + QVERIFY(server.waitForNewConnection()); + QLocalSocket* serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket); + delete serverSocket; + QCOMPARE(client.waitForReadyRead(), false); +} + +void tst_QLocalSocket::asyncDisconnectNotify() +{ +#ifdef Q_OS_SYMBIAN + unlink("asyncDisconnectNotify"); +#endif + + QLocalServer server; + QVERIFY(server.listen("asyncDisconnectNotify")); + QLocalSocket client; + QSignalSpy disconnectedSpy(&client, SIGNAL(disconnected())); + client.connectToServer("asyncDisconnectNotify"); + QVERIFY(server.waitForNewConnection()); + QLocalSocket* serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket); + delete serverSocket; + QTRY_VERIFY(!disconnectedSpy.isEmpty()); +} + +#ifdef Q_OS_SYMBIAN +void tst_QLocalSocket::unlink(QString name) +{ + if(name.length() == 0) + return; + + QString fullName; + // determine the full server path + if (name.startsWith(QLatin1Char('/'))) { + fullName = name; + } else { + fullName = QDir::cleanPath(QDir::tempPath()); + fullName += QLatin1Char('/') + name; + fullName = QDir::toNativeSeparators(fullName); + } + + int result = ::unlink(fullName.toUtf8().data()); + + if(result != 0) { + qWarning() << "Unlinking " << fullName << " failed with " << strerror(errno); + } +} +#endif +QTEST_MAIN(tst_QLocalSocket) +#include "tst_qlocalsocket.moc" + diff --git a/tests/auto/network/socket/qsocks5socketengine/.gitignore b/tests/auto/network/socket/qsocks5socketengine/.gitignore new file mode 100644 index 0000000000..7d64805f3c --- /dev/null +++ b/tests/auto/network/socket/qsocks5socketengine/.gitignore @@ -0,0 +1 @@ +tst_qsocks5socketengine diff --git a/tests/auto/network/socket/qsocks5socketengine/qsocks5socketengine.pro b/tests/auto/network/socket/qsocks5socketengine/qsocks5socketengine.pro new file mode 100644 index 0000000000..3a144aeacf --- /dev/null +++ b/tests/auto/network/socket/qsocks5socketengine/qsocks5socketengine.pro @@ -0,0 +1,17 @@ +load(qttest_p4) +SOURCES += tst_qsocks5socketengine.cpp + + +include(../platformsocketengine/platformsocketengine.pri) + + +MOC_DIR=tmp + +QT = core-private network-private + +# Symbian toolchain does not support correct include semantics +symbian:INCLUDEPATH+=..\\..\\..\\include\\QtNetwork\\private +symbian: TARGET.CAPABILITY = NetworkServices + + +requires(contains(QT_CONFIG,private_tests)) diff --git a/tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp b/tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp new file mode 100644 index 0000000000..2678816482 --- /dev/null +++ b/tests/auto/network/socket/qsocks5socketengine/tst_qsocks5socketengine.cpp @@ -0,0 +1,963 @@ +/**************************************************************************** +** +** 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/QTest> +#include <QtTest/QTestEventLoop> + +#include <QtCore/QQueue> +#include <QtCore/QString> +#include <QtCore/QCoreApplication> +#include <QtCore/QMetaType> + +#include <private/qsocks5socketengine_p.h> +#include <qhostinfo.h> +#include <qhostaddress.h> +#include <qtcpsocket.h> +#include <qhttp.h> +#include <qauthenticator.h> +#include <qdebug.h> +#include <qtcpserver.h> +#include <qmetatype.h> +#include <qdebug.h> + +#include "../../../network-settings.h" + +Q_DECLARE_METATYPE(QQueue<QByteArray>) + +class tst_QSocks5SocketEngine : public QObject, public QAbstractSocketEngineReceiver +{ + Q_OBJECT + +public: + tst_QSocks5SocketEngine(); + virtual ~tst_QSocks5SocketEngine(); + + +public slots: + void init(); + void cleanup(); +private slots: + void construction(); + void errorTest_data(); + void errorTest(); + void simpleConnectToIMAP(); + void simpleErrorsAndStates(); + void udpTest(); + void serverTest(); + void tcpSocketBlockingTest(); + void tcpSocketNonBlockingTest(); + void downloadBigFile(); + // void tcpLoopbackPerformance(); + void passwordAuth(); + void passwordAuth2(); + +protected slots: + void tcpSocketNonBlocking_hostFound(); + void tcpSocketNonBlocking_connected(); + void tcpSocketNonBlocking_closed(); + void tcpSocketNonBlocking_readyRead(); + void tcpSocketNonBlocking_bytesWritten(qint64); + void exitLoopSlot(); + void downloadBigFileSlot(); + void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); + +private: + void readNotification() { } + void writeNotification() { } + void exceptionNotification() { } + void connectionNotification() { } + QTcpSocket *tcpSocketNonBlocking_socket; + QStringList tcpSocketNonBlocking_data; + qint64 tcpSocketNonBlocking_totalWritten; + QTcpSocket *tmpSocket; + qint64 bytesAvailable; +}; + +class MiniSocks5Server: public QTcpServer +{ + Q_OBJECT +public: + QQueue<QByteArray> responses; + + MiniSocks5Server(const QQueue<QByteArray> r) + : responses(r) + { + listen(); + connect(this, SIGNAL(newConnection()), SLOT(handleNewConnection())); + } + +private slots: + void handleNewConnection() + { + QTcpSocket *client = nextPendingConnection(); + connect(client, SIGNAL(readyRead()), SLOT(handleClientCommand())); + client->setProperty("pendingResponses", qVariantFromValue(responses)); + } + + void handleClientCommand() + { + // WARNING + // this assumes that the client command is received in its entirety + // should be ok, since SOCKSv5 commands are rather small + QTcpSocket *client = static_cast<QTcpSocket *>(sender()); + QQueue<QByteArray> pendingResponses = + qvariant_cast<QQueue<QByteArray> >(client->property("pendingResponses")); + if (pendingResponses.isEmpty()) + client->disconnectFromHost(); + else + client->write(pendingResponses.dequeue()); + client->setProperty("pendingResponses", qVariantFromValue(pendingResponses)); + } +}; + +tst_QSocks5SocketEngine::tst_QSocks5SocketEngine() +{ + Q_SET_DEFAULT_IAP +} + +tst_QSocks5SocketEngine::~tst_QSocks5SocketEngine() +{ +} + +void tst_QSocks5SocketEngine::init() +{ + tmpSocket = 0; + bytesAvailable = 0; +} + +void tst_QSocks5SocketEngine::cleanup() +{ +} + +//--------------------------------------------------------------------------- +void tst_QSocks5SocketEngine::construction() +{ + QSocks5SocketEngine socketDevice; + + QVERIFY(!socketDevice.isValid()); + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.isValid()); + QVERIFY(socketDevice.protocol() == QAbstractSocket::IPv4Protocol); + QVERIFY(socketDevice.socketType() == QAbstractSocket::TcpSocket); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + // QVERIFY(socketDevice.socketDescriptor() != -1); + QVERIFY(socketDevice.localAddress() == QHostAddress()); + QVERIFY(socketDevice.localPort() == 0); + QVERIFY(socketDevice.peerAddress() == QHostAddress()); + QVERIFY(socketDevice.peerPort() == 0); + QVERIFY(socketDevice.error() == QAbstractSocket::UnknownSocketError); + + //QTest::ignoreMessage(QtWarningMsg, "QSocketLayer::bytesAvailable() was called in QAbstractSocket::UnconnectedState"); + QVERIFY(socketDevice.bytesAvailable() == 0); + + //QTest::ignoreMessage(QtWarningMsg, "QSocketLayer::hasPendingDatagrams() was called in QAbstractSocket::UnconnectedState"); + QVERIFY(!socketDevice.hasPendingDatagrams()); +} + +//--------------------------------------------------------------------------- +void tst_QSocks5SocketEngine::errorTest_data() +{ + QTest::addColumn<QString>("hostname"); + QTest::addColumn<int>("port"); + QTest::addColumn<QString>("username"); + QTest::addColumn<QQueue<QByteArray> >("responses"); + QTest::addColumn<int>("expectedError"); + + QQueue<QByteArray> responses; + QTest::newRow("proxy-host-not-found") << "this-host-does-not-exist." << 1080 << QString() + << responses + << int(QAbstractSocket::ProxyNotFoundError); + QTest::newRow("proxy-connection-refused") << "127.0.0.1" << 2 << QString() + << responses + << int(QAbstractSocket::ProxyConnectionRefusedError); + +#define REPLY(name, contents) \ + static const char raw_ ## name [] = contents; \ + const QByteArray name = QByteArray::fromRawData(raw_ ## name, sizeof raw_ ## name - 1) + + REPLY(garbage, "\4\4\4\4"); + // authentication method replies + REPLY(noAuthentication, "\5\0"); + REPLY(passwordAuthentication, "\5\2"); + REPLY(garbageAuthentication, "\5\177"); + REPLY(noAcceptableAuthentication, "\5\377"); + // authentication replies + REPLY(authenticationAccepted, "\5\0"); + REPLY(authenticationNotAccepted, "\5\1"); + // connection replies + REPLY(connectionAccepted, "\5\0\0\4\177\0\0\1\0\100"); + REPLY(connectionNotAllowed, "\5\2\0"); + REPLY(networkUnreachable, "\5\3\0"); + REPLY(hostUnreachable, "\5\4\0"); + REPLY(connectionRefused, "\5\5\0"); + +#undef REPLY + + responses << garbage; + QTest::newRow("garbage1") << QString() << 0 << QString() << responses + << int(QAbstractSocket::ProxyProtocolError); + + responses.clear(); + responses << noAuthentication << garbage; + QTest::newRow("garbage2") << QString() << 0 << QString() << responses + << int(QAbstractSocket::ProxyProtocolError); + + responses.clear(); + responses << garbageAuthentication; + QTest::newRow("unknown-auth-method") << QString() << 0 << QString() + << responses + << int(QAbstractSocket::SocketAccessError); + + responses.clear(); + responses << noAcceptableAuthentication; + QTest::newRow("no-acceptable-authentication") << QString() << 0 << QString() + << responses + << int(QAbstractSocket::ProxyAuthenticationRequiredError); + + responses.clear(); + responses << passwordAuthentication << authenticationNotAccepted; + QTest::newRow("authentication-required") << QString() << 0 << "foo" + << responses + << int(QAbstractSocket::ProxyAuthenticationRequiredError); + + responses.clear(); + responses << noAuthentication << connectionNotAllowed; + QTest::newRow("connection-not-allowed") << QString() << 0 << QString() + << responses + << int(QAbstractSocket::SocketAccessError); + + responses.clear(); + responses << noAuthentication << networkUnreachable; + QTest::newRow("network-unreachable") << QString() << 0 << QString() + << responses + << int(QAbstractSocket::NetworkError); + + responses.clear(); + responses << noAuthentication << hostUnreachable; + QTest::newRow("host-unreachable") << QString() << 0 << QString() + << responses + << int(QAbstractSocket::HostNotFoundError); + + responses.clear(); + responses << noAuthentication << connectionRefused; + QTest::newRow("connection-refused") << QString() << 0 << QString() + << responses + << int(QAbstractSocket::ConnectionRefusedError); +} + +void tst_QSocks5SocketEngine::errorTest() +{ + QFETCH(QString, hostname); + QFETCH(int, port); + QFETCH(QString, username); + QFETCH(QQueue<QByteArray>, responses); + QFETCH(int, expectedError); + + MiniSocks5Server server(responses); + + if (hostname.isEmpty()) { + hostname = "127.0.0.1"; + port = server.serverPort(); + } + QTcpSocket socket; + socket.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, hostname, port, username, username)); + socket.connectToHost("0.1.2.3", 12345); + + connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), + &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(10); + QVERIFY(!QTestEventLoop::instance().timeout()); + + QCOMPARE(int(socket.error()), expectedError); +} + +//--------------------------------------------------------------------------- +void tst_QSocks5SocketEngine::simpleConnectToIMAP() +{ + QSocks5SocketEngine socketDevice; + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + + socketDevice.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080)); + + QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::serverIP(), 143)); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectingState); + QVERIFY(socketDevice.waitForWrite()); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState); + QVERIFY(socketDevice.peerAddress() == QtNetworkSettings::serverIP()); + + // Wait for the greeting + QVERIFY(socketDevice.waitForRead()); + + // Read the greeting + qint64 available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + QByteArray array; + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); + + // Write a logout message + QByteArray array2 = "XXXX LOGOUT\r\n"; + QVERIFY(socketDevice.write(array2.data(), + array2.size()) == array2.size()); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + + available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n"); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + char c; + QVERIFY(socketDevice.read(&c, sizeof(c)) == -1); + QVERIFY(socketDevice.error() == QAbstractSocket::RemoteHostClosedError); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); +} + +//--------------------------------------------------------------------------- +void tst_QSocks5SocketEngine::simpleErrorsAndStates() +{ + { + QSocks5SocketEngine socketDevice; + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + + socketDevice.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080)); + + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + QVERIFY(!socketDevice.connectToHost(QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first(), 8088)); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectingState); + if (socketDevice.waitForWrite(15000)) { + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState || + socketDevice.state() == QAbstractSocket::ConnectedState); + } else { + QVERIFY(socketDevice.error() == QAbstractSocket::SocketTimeoutError); + } + } + +} + +/* +//--------------------------------------------------------------------------- +void tst_QSocks5SocketEngine::tcpLoopbackPerformance() +{ + QTcpServer server; + + // Bind to any port on all interfaces + QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0)); + QVERIFY(server.state() == QAbstractSocket::BoundState); + quint16 port = server.localPort(); + + // Listen for incoming connections + QVERIFY(server.listen()); + QVERIFY(server.state() == QAbstractSocket::ListeningState); + + // Initialize a Tcp socket + QSocks5SocketEngine client; + QVERIFY(client.initialize(QAbstractSocket::TcpSocket)); + + client.setProxy(QHostAddress("80.232.37.158"), 1081); + + // Connect to our server + if (!client.connectToHost(QHostAddress("127.0.0.1"), port)) { + QVERIFY(client.waitForWrite()); + QVERIFY(client.connectToHost(QHostAddress("127.0.0.1"), port)); + } + + // The server accepts the connectio + int socketDescriptor = server.accept(); + QVERIFY(socketDescriptor > 0); + + // A socket device is initialized on the server side, passing the + // socket descriptor from accept(). It's pre-connected. + QSocketLayer serverSocket; + QVERIFY(serverSocket.initialize(socketDescriptor)); + QVERIFY(serverSocket.state() == QAbstractSocket::ConnectedState); + + const int messageSize = 1024 * 256; + QByteArray message1(messageSize, '@'); + QByteArray answer(messageSize, '@'); + + QTime timer; + timer.start(); + qlonglong readBytes = 0; + while (timer.elapsed() < 5000) { + qlonglong written = serverSocket.write(message1.data(), message1.size()); + while (written > 0) { + client.waitForRead(); + if (client.bytesAvailable() > 0) { + qlonglong readNow = client.read(answer.data(), answer.size()); + written -= readNow; + readBytes += readNow; + } + } + } + + qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", + readBytes / (1024.0 * 1024.0), + timer.elapsed() / 1024.0, + (readBytes / (timer.elapsed() / 1000.0)) / (1024 * 1024)); +} +*/ + +//--------------------------------------------------------------------------- +void tst_QSocks5SocketEngine::serverTest() +{ + QSocks5SocketEngine server; + + // Initialize a Tcp socket + QVERIFY(server.initialize(QAbstractSocket::TcpSocket)); + + QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080); + + server.setProxy(proxy); + + // Bind to any port on all interfaces + QVERIFY(server.bind(QHostAddress("0.0.0.0"), 0)); + QVERIFY(server.state() == QAbstractSocket::BoundState); + + // Listen for incoming connections + QVERIFY(server.listen()); + QVERIFY(server.state() == QAbstractSocket::ListeningState); + + // Initialize a Tcp socket + QSocks5SocketEngine client; + QVERIFY(client.initialize(QAbstractSocket::TcpSocket)); + + client.setProxy(proxy); + + // QTest::wait(100000); // ### timing problem on win32 + + + // Connect to our server + if (!client.connectToHost(server.localAddress(), server.localPort())) { + QVERIFY(client.waitForWrite()); + // QTest::wait(100); // ### timing problem on win32 + QVERIFY(client.state() == QAbstractSocket::ConnectedState); + //QTest::wait(100); + } + + QVERIFY(server.waitForRead()); + + // The server accepts the connection + int socketDescriptor = server.accept(); + QVERIFY(socketDescriptor > 0); + + // A socket device is initialized on the server side, passing the + // socket descriptor from accept(). It's pre-connected. + + QSocks5SocketEngine serverSocket; + QVERIFY(serverSocket.initialize(socketDescriptor)); + QVERIFY(serverSocket.state() == QAbstractSocket::ConnectedState); + + QVERIFY(serverSocket.localAddress() == client.peerAddress()); + QVERIFY(serverSocket.localPort() == client.peerPort()); + // this seems depends on the socks server implementation, especially + // when connecting /to/ the socks server /through/ the same socks server + //QVERIFY(serverSocket.peerAddress() == client.localAddress()); + //QVERIFY(serverSocket.peerPort() == client.localPort()); + + // The server socket sends a greeting to the client + QByteArray greeting = "Greetings!"; + QVERIFY(serverSocket.write(greeting.data(), + greeting.size()) == greeting.size()); + + // The client waits for the greeting to arrive + QVERIFY(client.waitForRead()); + qint64 available = client.bytesAvailable(); + QVERIFY(available > 0); + + // The client reads the greeting and checks that it's correct + QByteArray response; + response.resize(available); + QVERIFY(client.read(response.data(), + response.size()) == response.size()); + QCOMPARE(response, greeting); +} + + +//--------------------------------------------------------------------------- +void tst_QSocks5SocketEngine::udpTest() +{ +#ifdef SYMBIAN_WINSOCK_CONNECTIVITY + QSKIP("UDP works bads on non WinPCAP emulator setting", SkipAll); +#endif + + QSocks5SocketEngine udpSocket; + + // Initialize device #1 + QVERIFY(udpSocket.initialize(QAbstractSocket::UdpSocket)); + QVERIFY(udpSocket.isValid()); + + QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080); + + udpSocket.setProxy(proxy); + + QVERIFY(udpSocket.protocol() == QAbstractSocket::IPv4Protocol); + QVERIFY(udpSocket.socketType() == QAbstractSocket::UdpSocket); + QVERIFY(udpSocket.state() == QAbstractSocket::UnconnectedState); + + // Bind #1 + QVERIFY(udpSocket.bind(QHostAddress("0.0.0.0"), 0)); + QVERIFY(udpSocket.state() == QAbstractSocket::BoundState); + QVERIFY(udpSocket.localPort() != 0); + + // Initialize device #2 + QSocks5SocketEngine udpSocket2; + QVERIFY(udpSocket2.initialize(QAbstractSocket::UdpSocket)); + + udpSocket2.setProxy(proxy); + + // Connect device #2 to #1 + QVERIFY(udpSocket2.connectToHost(udpSocket.localAddress(), udpSocket.localPort())); + QVERIFY(udpSocket2.state() == QAbstractSocket::ConnectedState); + + // Write a message to #1 + QByteArray message1 = "hei der"; + QVERIFY(udpSocket2.write(message1.data(), + message1.size()) == message1.size()); + + // Read the message from #2 + QVERIFY(udpSocket.waitForRead()); + QVERIFY(udpSocket.hasPendingDatagrams()); + qint64 available = udpSocket.pendingDatagramSize(); + QVERIFY(available > 0); + QByteArray answer; + answer.resize(available); + QHostAddress senderAddress; + quint16 senderPort = 0; + QVERIFY(udpSocket.readDatagram(answer.data(), answer.size(), + &senderAddress, + &senderPort) == message1.size()); + QVERIFY(senderAddress == udpSocket2.localAddress()); + QVERIFY(senderPort == udpSocket2.localPort()); +} + +void tst_QSocks5SocketEngine::tcpSocketBlockingTest() +{ + QSocks5SocketEngineHandler socks5; + + QTcpSocket socket; + + // Connect + socket.connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket.waitForConnected()); + QCOMPARE(socket.state(), QTcpSocket::ConnectedState); + + // Read greeting + QVERIFY(socket.waitForReadyRead(5000)); + QString s = socket.readLine(); + QVERIFY2(QtNetworkSettings::compareReplyIMAP(s.toLatin1()), s.toLatin1().constData()); + + // Write NOOP + QCOMPARE((int) socket.write("1 NOOP\r\n", 8), 8); + + if (!socket.canReadLine()) + QVERIFY(socket.waitForReadyRead(5000)); + + // Read response + s = socket.readLine(); + QCOMPARE(s.toLatin1().constData(), "1 OK Completed\r\n"); + + // Write LOGOUT + QCOMPARE((int) socket.write("2 LOGOUT\r\n", 10), 10); + + if (!socket.canReadLine()) + QVERIFY(socket.waitForReadyRead(5000)); + + // Read two lines of respose + s = socket.readLine(); + QCOMPARE(s.toLatin1().constData(), "* BYE LOGOUT received\r\n"); + + if (!socket.canReadLine()) + QVERIFY(socket.waitForReadyRead(5000)); + + s = socket.readLine(); + QCOMPARE(s.toLatin1().constData(), "2 OK Completed\r\n"); + + // Close the socket + socket.close(); + + // Check that it's closed + QCOMPARE(socket.state(), QTcpSocket::UnconnectedState); +} + +//---------------------------------------------------------------------------------- + +void tst_QSocks5SocketEngine::tcpSocketNonBlockingTest() +{ + QSocks5SocketEngineHandler socks5; + + QTcpSocket socket; + connect(&socket, SIGNAL(hostFound()), SLOT(tcpSocketNonBlocking_hostFound())); + connect(&socket, SIGNAL(connected()), SLOT(tcpSocketNonBlocking_connected())); + connect(&socket, SIGNAL(disconnected()), SLOT(tcpSocketNonBlocking_closed())); + connect(&socket, SIGNAL(bytesWritten(qint64)), SLOT(tcpSocketNonBlocking_bytesWritten(qint64))); + connect(&socket, SIGNAL(readyRead()), SLOT(tcpSocketNonBlocking_readyRead())); + tcpSocketNonBlocking_socket = &socket; + + // Connect + socket.connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket.state() == QTcpSocket::HostLookupState || + socket.state() == QTcpSocket::ConnectingState); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + if (socket.state() == QTcpSocket::ConnectingState) { + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + } + + QCOMPARE(socket.state(), QTcpSocket::ConnectedState); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + // Read greeting + QVERIFY(!tcpSocketNonBlocking_data.isEmpty()); + QByteArray data = tcpSocketNonBlocking_data.at(0).toLatin1(); + QVERIFY2(QtNetworkSettings::compareReplyIMAP(data), data.constData()); + + tcpSocketNonBlocking_data.clear(); + + tcpSocketNonBlocking_totalWritten = 0; + + // Write NOOP + QCOMPARE((int) socket.write("1 NOOP\r\n", 8), 8); + + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + QVERIFY(tcpSocketNonBlocking_totalWritten == 8); + + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + // Read response + QVERIFY(!tcpSocketNonBlocking_data.isEmpty()); + QCOMPARE(tcpSocketNonBlocking_data.at(0).toLatin1().constData(), "1 OK Completed\r\n"); + tcpSocketNonBlocking_data.clear(); + + + tcpSocketNonBlocking_totalWritten = 0; + + // Write LOGOUT + QCOMPARE((int) socket.write("2 LOGOUT\r\n", 10), 10); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + QVERIFY(tcpSocketNonBlocking_totalWritten == 10); + + // Wait for greeting + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) { + QFAIL("Timed out"); + } + + // Read two lines of respose + QCOMPARE(tcpSocketNonBlocking_data.at(0).toLatin1().constData(), "* BYE LOGOUT received\r\n"); + QCOMPARE(tcpSocketNonBlocking_data.at(1).toLatin1().constData(), "2 OK Completed\r\n"); + tcpSocketNonBlocking_data.clear(); + + // Close the socket + socket.close(); + + // Check that it's closed + QCOMPARE(socket.state(), QTcpSocket::UnconnectedState); +} + +void tst_QSocks5SocketEngine::tcpSocketNonBlocking_hostFound() +{ + QTestEventLoop::instance().exitLoop(); +} + +void tst_QSocks5SocketEngine::tcpSocketNonBlocking_connected() +{ + QTestEventLoop::instance().exitLoop(); +} + +void tst_QSocks5SocketEngine::tcpSocketNonBlocking_readyRead() +{ + while (tcpSocketNonBlocking_socket->canReadLine()) + tcpSocketNonBlocking_data.append(tcpSocketNonBlocking_socket->readLine()); + + QTestEventLoop::instance().exitLoop(); +} + +void tst_QSocks5SocketEngine::tcpSocketNonBlocking_bytesWritten(qint64 written) +{ + tcpSocketNonBlocking_totalWritten += written; + QTestEventLoop::instance().exitLoop(); +} + +void tst_QSocks5SocketEngine::tcpSocketNonBlocking_closed() +{ +} + +//---------------------------------------------------------------------------------- + +void tst_QSocks5SocketEngine::downloadBigFile() +{ + QSocks5SocketEngineHandler socks5; + + if (tmpSocket) + delete tmpSocket; + tmpSocket = new QTcpSocket; + + connect(tmpSocket, SIGNAL(connected()), SLOT(exitLoopSlot())); + connect(tmpSocket, SIGNAL(readyRead()), SLOT(downloadBigFileSlot())); + + tmpSocket->connectToHost(QtNetworkSettings::serverName(), 80); + + QTestEventLoop::instance().enterLoop(30); + if (QTestEventLoop::instance().timeout()) + QFAIL("Network operation timed out"); + + QByteArray hostName = QtNetworkSettings::serverName().toLatin1(); + QVERIFY(tmpSocket->state() == QAbstractSocket::ConnectedState); + QVERIFY(tmpSocket->write("GET /qtest/mediumfile HTTP/1.0\r\n") > 0); + QVERIFY(tmpSocket->write("HOST: ") > 0); + QVERIFY(tmpSocket->write(hostName.data()) > 0); + QVERIFY(tmpSocket->write("\r\n") > 0); + QVERIFY(tmpSocket->write("\r\n") > 0); + + bytesAvailable = 0; + + QTime stopWatch; + stopWatch.start(); + +#if !defined(Q_OS_WINCE) && !defined(Q_OS_SYMBIAN) + QTestEventLoop::instance().enterLoop(60); +#else + QTestEventLoop::instance().enterLoop(180); +#endif + if (QTestEventLoop::instance().timeout()) + QFAIL("Network operation timed out"); + + QCOMPARE(bytesAvailable, qint64(10000000)); + + QVERIFY(tmpSocket->state() == QAbstractSocket::ConnectedState); + + /*qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", + bytesAvailable / (1024.0 * 1024.0), + stopWatch.elapsed() / 1024.0, + (bytesAvailable / (stopWatch.elapsed() / 1000.0)) / (1024 * 1024));*/ + + delete tmpSocket; + tmpSocket = 0; +} + +void tst_QSocks5SocketEngine::exitLoopSlot() +{ + QTestEventLoop::instance().exitLoop(); +} + + +void tst_QSocks5SocketEngine::downloadBigFileSlot() +{ + QByteArray tmp=tmpSocket->readAll(); + int correction=tmp.indexOf((char)0,0); //skip header + if (correction==-1) correction=0; + bytesAvailable += (tmp.size()-correction); + if (bytesAvailable >= 10000000) + QTestEventLoop::instance().exitLoop(); +} + +void tst_QSocks5SocketEngine::passwordAuth() +{ + QSocks5SocketEngine socketDevice; + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + + socketDevice.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080, "qsockstest", "password")); + + // Connect to imap.trolltech.com's IP + QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::serverIP(), 143)); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectingState); + QVERIFY(socketDevice.waitForWrite()); + if (!socketDevice.connectToHost(QtNetworkSettings::serverIP(), 143)) { + qDebug("%d, %s", socketDevice.error(), socketDevice.errorString().toLatin1().constData()); + } + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState); + QVERIFY(socketDevice.peerAddress() == QtNetworkSettings::serverIP()); + + // Wait for the greeting + QVERIFY(socketDevice.waitForRead()); + + // Read the greeting + qint64 available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + QByteArray array; + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); + + // Write a logout message + QByteArray array2 = "XXXX LOGOUT\r\n"; + QVERIFY(socketDevice.write(array2.data(), + array2.size()) == array2.size()); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + + available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n"); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + char c; + QVERIFY(socketDevice.read(&c, sizeof(c)) == -1); + QVERIFY(socketDevice.error() == QAbstractSocket::RemoteHostClosedError); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); +} + +//---------------------------------------------------------------------------------- + +void tst_QSocks5SocketEngine::proxyAuthenticationRequired(const QNetworkProxy &, + QAuthenticator *auth) +{ + auth->setUser("qsockstest"); + auth->setPassword("password"); +} + +void tst_QSocks5SocketEngine::passwordAuth2() +{ + QSocks5SocketEngine socketDevice; + + // Initialize device + QVERIFY(socketDevice.initialize(QAbstractSocket::TcpSocket, QAbstractSocket::IPv4Protocol)); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); + + socketDevice.setProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081)); + socketDevice.setReceiver(this); + + QVERIFY(!socketDevice.connectToHost(QtNetworkSettings::serverIP(), 143)); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectingState); + while (socketDevice.state() == QAbstractSocket::ConnectingState) { + QVERIFY(socketDevice.waitForWrite()); + socketDevice.connectToHost(QtNetworkSettings::serverIP(), 143); + } + if (socketDevice.state() != QAbstractSocket::ConnectedState) + qDebug("%d, %s", socketDevice.error(), socketDevice.errorString().toLatin1().constData()); + QVERIFY(socketDevice.state() == QAbstractSocket::ConnectedState); + QVERIFY(socketDevice.peerAddress() == QtNetworkSettings::serverIP()); + + // Wait for the greeting + QVERIFY(socketDevice.waitForRead()); + + // Read the greeting + qint64 available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + QByteArray array; + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QVERIFY2(QtNetworkSettings::compareReplyIMAP(array), array.constData()); + + // Write a logout message + QByteArray array2 = "XXXX LOGOUT\r\n"; + QVERIFY(socketDevice.write(array2.data(), + array2.size()) == array2.size()); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + + available = socketDevice.bytesAvailable(); + QVERIFY(available > 0); + array.resize(available); + QVERIFY(socketDevice.read(array.data(), array.size()) == available); + + // Check that the greeting is what we expect it to be + QCOMPARE(array.constData(), "* BYE LOGOUT received\r\nXXXX OK Completed\r\n"); + + // Wait for the response + QVERIFY(socketDevice.waitForRead()); + char c; + QVERIFY(socketDevice.read(&c, sizeof(c)) == -1); + QVERIFY(socketDevice.error() == QAbstractSocket::RemoteHostClosedError); + QVERIFY(socketDevice.state() == QAbstractSocket::UnconnectedState); +} + +//---------------------------------------------------------------------------------- + +QTEST_MAIN(tst_QSocks5SocketEngine) +#include "tst_qsocks5socketengine.moc" diff --git a/tests/auto/network/socket/qtcpserver/.gitignore b/tests/auto/network/socket/qtcpserver/.gitignore new file mode 100644 index 0000000000..c00e0a4ca9 --- /dev/null +++ b/tests/auto/network/socket/qtcpserver/.gitignore @@ -0,0 +1,3 @@ +tst_qtcpserver +crashingServer/crashingServer +crashingServer/crashingServer.exe diff --git a/tests/auto/network/socket/qtcpserver/crashingServer/crashingServer.pro b/tests/auto/network/socket/qtcpserver/crashingServer/crashingServer.pro new file mode 100644 index 0000000000..700e9520ec --- /dev/null +++ b/tests/auto/network/socket/qtcpserver/crashingServer/crashingServer.pro @@ -0,0 +1,9 @@ +SOURCES += main.cpp +QT = core network +CONFIG -= app_bundle +DESTDIR = ./ + +# This means the auto test works on some machines for MinGW. No dialog stalls +# the application. +win32-g++*:CONFIG += console +symbian: TARGET.CAPABILITY += NetworkServices ReadUserData diff --git a/tests/auto/network/socket/qtcpserver/crashingServer/main.cpp b/tests/auto/network/socket/qtcpserver/crashingServer/main.cpp new file mode 100644 index 0000000000..35da65f671 --- /dev/null +++ b/tests/auto/network/socket/qtcpserver/crashingServer/main.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** 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 <QtCore> +#include <QtNetwork> + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + + QTcpServer server; + if (!server.listen(QHostAddress::LocalHost, 49199)) { + qDebug("Failed to listen: %s", server.errorString().toLatin1().constData()); + return 1; + } + +#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) + QFile file(QLatin1String("/test_signal.txt")); + file.open(QIODevice::WriteOnly); + file.write("Listening\n"); + file.flush(); + file.close(); +#else + printf("Listening\n"); + fflush(stdout); +#endif + + server.waitForNewConnection(5000); + qFatal("Crash"); + return 0; +} diff --git a/tests/auto/network/socket/qtcpserver/qtcpserver.pro b/tests/auto/network/socket/qtcpserver/qtcpserver.pro new file mode 100644 index 0000000000..e123cfe73b --- /dev/null +++ b/tests/auto/network/socket/qtcpserver/qtcpserver.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +SUBDIRS = test crashingServer + + diff --git a/tests/auto/network/socket/qtcpserver/test/test.pro b/tests/auto/network/socket/qtcpserver/test/test.pro new file mode 100644 index 0000000000..65e1d82613 --- /dev/null +++ b/tests/auto/network/socket/qtcpserver/test/test.pro @@ -0,0 +1,37 @@ +load(qttest_p4) +SOURCES += ../tst_qtcpserver.cpp + +win32: { +wince*: { + LIBS += -lws2 + crashApp.files = ../crashingServer/crashingServer.exe + crashApp.path = crashingServer + DEPLOYMENT += crashApp +} else { + LIBS += -lws2_32 +} +} + +symbian { + crashApp.files = $$QT_BUILD_TREE/examples/widgets/wiggly/$${BUILD_DIR}/crashingServer.exe + crashApp.path = . + DEPLOYMENT += crashApp + TARGET.CAPABILITY += NetworkServices ReadUserData +} + +TARGET = ../tst_qtcpserver + +win32 { + CONFIG(debug, debug|release) { + TARGET = ../../debug/tst_qtcpserver +} else { + TARGET = ../../release/tst_qtcpserver + } +} + +QT = core network + +MOC_DIR=tmp + + + diff --git a/tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp b/tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp new file mode 100644 index 0000000000..ca28a9c7c8 --- /dev/null +++ b/tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp @@ -0,0 +1,817 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +// Just to get Q_OS_SYMBIAN +#include <qglobal.h> +#if defined(_WIN32) && !defined(Q_OS_SYMBIAN) +#include <winsock2.h> +#else +#include <sys/types.h> +#include <sys/socket.h> +#define SOCKET int +#define INVALID_SOCKET -1 +#endif + +#include <QtTest/QtTest> + +#ifndef Q_OS_WIN +#include <unistd.h> +#include <sys/ioctl.h> +#endif + +#include <qcoreapplication.h> +#include <qtcpsocket.h> +#include <qtcpserver.h> +#include <qhostaddress.h> +#include <qprocess.h> +#include <qstringlist.h> +#include <qplatformdefs.h> +#include <qhostinfo.h> + +#include <QNetworkProxy> +Q_DECLARE_METATYPE(QNetworkProxy) +Q_DECLARE_METATYPE(QList<QNetworkProxy>) + +#include <QNetworkSession> +#include <QNetworkConfiguration> +#include <QNetworkConfigurationManager> +#include "../../../network-settings.h" + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QTcpServer : public QObject +{ + Q_OBJECT + +public: + tst_QTcpServer(); + virtual ~tst_QTcpServer(); + + +public slots: + void initTestCase_data(); + void initTestCase(); + void init(); + void cleanup(); +private slots: + void getSetCheck(); + void constructing(); + void clientServerLoop(); + void ipv6Server(); + void dualStack_data(); + void dualStack(); + void ipv6ServerMapped(); + void crashTests(); + void maxPendingConnections(); + void listenError(); + void waitForConnectionTest(); + void setSocketDescriptor(); + void listenWhileListening(); + void addressReusable(); + void setNewSocketDescriptorBlocking(); + void invalidProxy_data(); + void invalidProxy(); + void proxyFactory_data(); + void proxyFactory(); + + void qtbug14268_peek(); + +private: +#ifndef QT_NO_BEARERMANAGEMENT + QNetworkSession *networkSession; +#endif +}; + +// Testing get/set functions +void tst_QTcpServer::getSetCheck() +{ + QTcpServer obj1; + // int QTcpServer::maxPendingConnections() + // void QTcpServer::setMaxPendingConnections(int) + obj1.setMaxPendingConnections(0); + QCOMPARE(0, obj1.maxPendingConnections()); + obj1.setMaxPendingConnections(INT_MIN); + QCOMPARE(INT_MIN, obj1.maxPendingConnections()); + obj1.setMaxPendingConnections(INT_MAX); + QCOMPARE(INT_MAX, obj1.maxPendingConnections()); +} + +tst_QTcpServer::tst_QTcpServer() +{ + Q_SET_DEFAULT_IAP +} + +tst_QTcpServer::~tst_QTcpServer() +{ +} + +void tst_QTcpServer::initTestCase_data() +{ + QTest::addColumn<bool>("setProxy"); + QTest::addColumn<int>("proxyType"); + + QTest::newRow("WithoutProxy") << false << 0; + QTest::newRow("WithSocks5Proxy") << true << int(QNetworkProxy::Socks5Proxy); +} + +void tst_QTcpServer::initTestCase() +{ +#ifndef QT_NO_BEARERMANAGEMENT + QNetworkConfigurationManager man; + networkSession = new QNetworkSession(man.defaultConfiguration(), this); + networkSession->open(); + QVERIFY(networkSession->waitForOpened()); +#endif +} + +void tst_QTcpServer::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_QTcpServer::cleanup() +{ + QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpServer::constructing() +{ + QTcpServer socket; + + // Check the initial state of the QTcpSocket. + QCOMPARE(socket.isListening(), false); + QCOMPARE((int)socket.serverPort(), 0); + QCOMPARE(socket.serverAddress(), QHostAddress()); + QCOMPARE(socket.maxPendingConnections(), 30); + QCOMPARE(socket.hasPendingConnections(), false); + QCOMPARE(socket.socketDescriptor(), -1); + QCOMPARE(socket.serverError(), QAbstractSocket::UnknownSocketError); + + // Check the state of the socket layer? +} + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::clientServerLoop() +{ + QTcpServer server; + + QSignalSpy spy(&server, SIGNAL(newConnection())); + + QVERIFY(!server.isListening()); + QVERIFY(!server.hasPendingConnections()); + QVERIFY(server.listen(QHostAddress::Any, 11423)); + QVERIFY(server.isListening()); + + QTcpSocket client; + + QHostAddress serverAddress = QHostAddress::LocalHost; + if (!(server.serverAddress() == QHostAddress::Any) && !(server.serverAddress() == QHostAddress::AnyIPv6)) + serverAddress = server.serverAddress(); + + client.connectToHost(serverAddress, server.serverPort()); + QVERIFY(client.waitForConnected(5000)); + + QVERIFY(server.waitForNewConnection(5000)); + QVERIFY(server.hasPendingConnections()); + + QCOMPARE(spy.count(), 1); + + QTcpSocket *serverSocket = server.nextPendingConnection(); + QVERIFY(serverSocket != 0); + + QVERIFY(serverSocket->write("Greetings, client!\n", 19) == 19); + serverSocket->flush(); + + QVERIFY(client.waitForReadyRead(5000)); + QByteArray arr = client.readAll(); + QCOMPARE(arr.constData(), "Greetings, client!\n"); + + QVERIFY(client.write("Well, hello to you!\n", 20) == 20); + client.flush(); + + QVERIFY(serverSocket->waitForReadyRead(5000)); + arr = serverSocket->readAll(); + QCOMPARE(arr.constData(), "Well, hello to you!\n"); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::ipv6Server() +{ + //### need to enter the event loop for the server to get the connection ?? ( windows) + QTcpServer server; + if (!server.listen(QHostAddress::LocalHostIPv6, 8944)) { + QVERIFY(server.serverError() == QAbstractSocket::UnsupportedSocketOperationError); + return; + } + + QVERIFY(server.serverPort() == 8944); + QVERIFY(server.serverAddress() == QHostAddress::LocalHostIPv6); + + QTcpSocket client; + client.connectToHost("::1", 8944); + QVERIFY(client.waitForConnected(5000)); + + QVERIFY(server.waitForNewConnection()); + QVERIFY(server.hasPendingConnections()); + + QTcpSocket *serverSocket = 0; + QVERIFY((serverSocket = server.nextPendingConnection())); + serverSocket->close(); + delete serverSocket; +} + +Q_DECLARE_METATYPE(QHostAddress); + +void tst_QTcpServer::dualStack_data() +{ + QTest::addColumn<QHostAddress>("bindAddress"); + QTest::addColumn<bool>("v4ok"); + QTest::addColumn<bool>("v6ok"); + QTest::newRow("any") << QHostAddress(QHostAddress::Any) << true << true; + QTest::newRow("anyIPv4") << QHostAddress(QHostAddress::AnyIPv4) << true << false; + QTest::newRow("anyIPv6") << QHostAddress(QHostAddress::AnyIPv6) << false << true; +} + +void tst_QTcpServer::dualStack() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + QSKIP("test server proxy doesn't support ipv6", SkipSingle); + QFETCH(QHostAddress, bindAddress); + QFETCH(bool, v4ok); + QFETCH(bool, v6ok); + + QTcpServer server; + QVERIFY(server.listen(bindAddress)); + + QTcpSocket v4client; + v4client.connectToHost(QHostAddress::LocalHost, server.serverPort()); + + QTcpSocket v6client; + v6client.connectToHost(QHostAddress::LocalHostIPv6, server.serverPort()); + + QCOMPARE(v4client.waitForConnected(5000), v4ok); + QCOMPARE(v6client.waitForConnected(5000), v6ok); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::ipv6ServerMapped() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QTcpServer server; + QVERIFY(server.listen(QHostAddress::LocalHost)); + + // let's try the normal case + QTcpSocket client1; + client1.connectToHost("127.0.0.1", server.serverPort()); + QVERIFY(server.waitForNewConnection(5000)); + delete server.nextPendingConnection(); + + // let's try the mapped one in the nice format + QTcpSocket client2; + client2.connectToHost("::ffff:127.0.0.1", server.serverPort()); + QVERIFY(server.waitForNewConnection(5000)); + delete server.nextPendingConnection(); + + // let's try the mapped in hex format + QTcpSocket client3; + client3.connectToHost("::ffff:7F00:0001", server.serverPort()); + QVERIFY(server.waitForNewConnection(5000)); + delete server.nextPendingConnection(); + + // However connecting to the v6 localhost should not work + QTcpSocket client4; + client4.connectToHost("::1", server.serverPort()); + QVERIFY(!server.waitForNewConnection(5000)); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::crashTests() +{ + QTcpServer server; + server.close(); + QVERIFY(server.listen()); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::maxPendingConnections() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + QSKIP("With socks5 only 1 connection is allowed ever", SkipAll); + } + } + //### sees to fail sometimes ... a timing issue with the test on windows + QTcpServer server; + server.setMaxPendingConnections(2); + + QTcpSocket socket1; + QTcpSocket socket2; + QTcpSocket socket3; + + QVERIFY(server.listen()); + + socket1.connectToHost(QHostAddress::LocalHost, server.serverPort()); + socket2.connectToHost(QHostAddress::LocalHost, server.serverPort()); + socket3.connectToHost(QHostAddress::LocalHost, server.serverPort()); + + QVERIFY(server.waitForNewConnection(5000)); + + QVERIFY(server.hasPendingConnections()); + QVERIFY(server.nextPendingConnection()); + QVERIFY(server.hasPendingConnections()); + QVERIFY(server.nextPendingConnection()); + QVERIFY(!server.hasPendingConnections()); + QCOMPARE(server.nextPendingConnection(), (QTcpSocket*)0); + + QVERIFY(server.waitForNewConnection(5000)); + + QVERIFY(server.hasPendingConnections()); + QVERIFY(server.nextPendingConnection()); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::listenError() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + QSKIP("With socks5 we can not make hard requirements on the address or port", SkipAll); + } + } + QTcpServer server; + QVERIFY(!server.listen(QHostAddress("1.2.3.4"), 0)); + QCOMPARE(server.serverError(), QAbstractSocket::SocketAddressNotAvailableError); + QCOMPARE(server.errorString().toLatin1().constData(), "The address is not available"); +} + +class ThreadConnector : public QThread +{ +public: + ThreadConnector(const QHostAddress &host, quint16 port) + : host(host), port(port) + { } + + ~ThreadConnector() + { + wait(); + } + +protected: + void run() + { + sleep(2); + + QTcpSocket socket; + socket.connectToHost(host, port); + + QEventLoop loop; + QTimer::singleShot(5000, &loop, SLOT(quit())); + loop.exec(); + } + +private: + QHostAddress host; + quint16 port; +}; + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::waitForConnectionTest() +{ + + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + QSKIP("Localhost servers don't work well with SOCKS5", SkipAll); + } + } + + QTcpSocket findLocalIpSocket; + findLocalIpSocket.connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(findLocalIpSocket.waitForConnected(5000)); + + QTcpServer server; + bool timeout = false; + QVERIFY(server.listen(findLocalIpSocket.localAddress())); + QVERIFY(!server.waitForNewConnection(1000, &timeout)); + QCOMPARE(server.serverError(), QAbstractSocket::SocketTimeoutError); + QVERIFY(timeout); + + ThreadConnector connector(findLocalIpSocket.localAddress(), server.serverPort()); + connector.start(); + +#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) + QVERIFY(server.waitForNewConnection(9000, &timeout)); +#else + QVERIFY(server.waitForNewConnection(3000, &timeout)); +#endif + QVERIFY(!timeout); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::setSocketDescriptor() +{ + QTcpServer server; +#ifdef Q_OS_SYMBIAN + QTest::ignoreMessage(QtWarningMsg, "QSymbianSocketEngine::initialize - socket descriptor not found"); +#endif + QVERIFY(!server.setSocketDescriptor(42)); + QCOMPARE(server.serverError(), QAbstractSocket::UnsupportedSocketOperationError); +#ifndef Q_OS_SYMBIAN + //adopting Open C sockets is not supported, neither is adopting externally created RSocket +#ifdef Q_OS_WIN + // ensure winsock is started + WSADATA wsaData; + QVERIFY(WSAStartup(MAKEWORD(2,0), &wsaData) == NO_ERROR); +#endif + + SOCKET sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + + QVERIFY(sock != INVALID_SOCKET); + + sockaddr_in sin; + memset(&sin, 0, sizeof(sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_port = 0; + sin.sin_addr.s_addr = 0x00000000; + QVERIFY(::bind(sock, (sockaddr*)&sin, sizeof(sockaddr_in)) == 0); + QVERIFY(::listen(sock, 10) == 0); + QVERIFY(server.setSocketDescriptor(sock)); + +#ifdef Q_OS_WIN + WSACleanup(); +#endif +#endif +} + +//---------------------------------------------------------------------------------- +void tst_QTcpServer::listenWhileListening() +{ + QTcpServer server; + QVERIFY(server.listen()); + QTest::ignoreMessage(QtWarningMsg, "QTcpServer::listen() called when already listening"); + QVERIFY(!server.listen()); +} + +//---------------------------------------------------------------------------------- + +class SeverWithBlockingSockets : public QTcpServer +{ +public: + SeverWithBlockingSockets() + : ok(false) { } + + bool ok; + +protected: + void incomingConnection(int socketDescriptor) + { + // how a user woulddo it (qabstractsocketengine is not public) + unsigned long arg = 0; +#if defined(Q_OS_SYMBIAN) + arg = fcntl(socketDescriptor, F_GETFL, NULL); + arg &= (~O_NONBLOCK); + ok = ::fcntl(socketDescriptor, F_SETFL, arg) != -1; +#elif defined(Q_OS_WIN) + ok = ::ioctlsocket(socketDescriptor, FIONBIO, &arg) == 0; + ::closesocket(socketDescriptor); +#else + ok = ::ioctl(socketDescriptor, FIONBIO, &arg) == 0; + ::close(socketDescriptor); +#endif + } +}; + +void tst_QTcpServer::addressReusable() +{ +#if defined(Q_OS_SYMBIAN) && defined(Q_CC_NOKIAX86) + QSKIP("Symbian: Emulator does not support process launching", SkipAll ); +#endif + +#if defined(QT_NO_PROCESS) + QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll); +#else + + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + QSKIP("With socks5 this test does not make senans at the momment", SkipAll); + } + } +#if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) + QString signalName = QString::fromLatin1("/test_signal.txt"); + QFile::remove(signalName); + // The crashingServer process will crash once it gets a connection. + QProcess process; + process.start("crashingServer/crashingServer"); + int waitCount = 5; + while (waitCount-- && !QFile::exists(signalName)) + QTest::qWait(1000); + QVERIFY(QFile::exists(signalName)); + QFile::remove(signalName); +#else + // The crashingServer process will crash once it gets a connection. + QProcess process; + process.start("crashingServer/crashingServer"); + QVERIFY(process.waitForReadyRead(5000)); +#endif + + QTcpSocket socket; + socket.connectToHost(QHostAddress::LocalHost, 49199); + QVERIFY(socket.waitForConnected(5000)); + + QVERIFY(process.waitForFinished(30000)); + + // Give the system some time. + QTest::qSleep(10); + + QTcpServer server; + QVERIFY(server.listen(QHostAddress::LocalHost, 49199)); +#endif +} + +void tst_QTcpServer::setNewSocketDescriptorBlocking() +{ +#ifdef Q_OS_SYMBIAN + QSKIP("open C ioctls on Qt sockets not supported", SkipAll); +#else + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + if (proxyType == QNetworkProxy::Socks5Proxy) { + QSKIP("With socks5 we can not make the socket descripter blocking", SkipAll); + } + } + SeverWithBlockingSockets server; + QVERIFY(server.listen()); + + QTcpSocket socket; + socket.connectToHost(QHostAddress::LocalHost, server.serverPort()); + QVERIFY(server.waitForNewConnection(5000)); + QVERIFY(server.ok); +#endif +} + +void tst_QTcpServer::invalidProxy_data() +{ + QTest::addColumn<int>("type"); + QTest::addColumn<QString>("host"); + QTest::addColumn<int>("port"); + QTest::addColumn<int>("expectedError"); + + QString fluke = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first().toString(); + QTest::newRow("ftp-proxy") << int(QNetworkProxy::FtpCachingProxy) << fluke << 143 + << int(QAbstractSocket::UnsupportedSocketOperationError); + QTest::newRow("http-proxy") << int(QNetworkProxy::HttpProxy) << fluke << 3128 + << int(QAbstractSocket::UnsupportedSocketOperationError); + + QTest::newRow("no-such-host") << int(QNetworkProxy::Socks5Proxy) + << "this-host-will-never-exist.troll.no" << 1080 + << int(QAbstractSocket::ProxyNotFoundError); + QTest::newRow("socks5-on-http") << int(QNetworkProxy::Socks5Proxy) << fluke << 3128 + << int(QAbstractSocket::SocketTimeoutError); +} + +void tst_QTcpServer::invalidProxy() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QFETCH(int, type); + QFETCH(QString, host); + QFETCH(int, port); + QNetworkProxy::ProxyType proxyType = QNetworkProxy::ProxyType(type); + QNetworkProxy proxy(proxyType, host, port); + + QTcpServer server; + server.setProxy(proxy); + bool listenResult = server.listen(); + + QVERIFY(!listenResult); + QVERIFY(!server.errorString().isEmpty()); + + // note: the following test is not a hard failure. + // Sometimes, error codes change for the better + QTEST(int(server.serverError()), "expectedError"); +} + +// copied from tst_qnetworkreply.cpp +class MyProxyFactory: public QNetworkProxyFactory +{ +public: + int callCount; + QList<QNetworkProxy> toReturn; + QNetworkProxyQuery lastQuery; + inline MyProxyFactory() { clear(); } + + inline void clear() + { + callCount = 0; + toReturn = QList<QNetworkProxy>() << QNetworkProxy::DefaultProxy; + lastQuery = QNetworkProxyQuery(); + } + + virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query) + { + lastQuery = query; + ++callCount; + return toReturn; + } +}; + +void tst_QTcpServer::proxyFactory_data() +{ + QTest::addColumn<QList<QNetworkProxy> >("proxyList"); + QTest::addColumn<QNetworkProxy>("proxyUsed"); + QTest::addColumn<bool>("fails"); + QTest::addColumn<int>("expectedError"); + + QList<QNetworkProxy> proxyList; + + // tests that do get to listen + + proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080); + QTest::newRow("socks5") + << proxyList << proxyList.at(0) + << false << int(QAbstractSocket::UnknownSocketError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3128) + << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080); + QTest::newRow("cachinghttp+socks5") + << proxyList << proxyList.at(1) + << false << int(QAbstractSocket::UnknownSocketError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121) + << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3128) + << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080); + QTest::newRow("ftp+cachinghttp+socks5") + << proxyList << proxyList.at(2) + << false << int(QAbstractSocket::UnknownSocketError); + + // tests that fail to listen + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3128); + QTest::newRow("http") + << proxyList << proxyList.at(0) + << true << int(QAbstractSocket::UnsupportedSocketOperationError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3128); + QTest::newRow("cachinghttp") + << proxyList << QNetworkProxy() + << true << int(QAbstractSocket::UnsupportedSocketOperationError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121); + QTest::newRow("ftp") + << proxyList << QNetworkProxy() + << true << int(QAbstractSocket::UnsupportedSocketOperationError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121) + << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3128); + QTest::newRow("ftp+cachinghttp") + << proxyList << QNetworkProxy() + << true << int(QAbstractSocket::UnsupportedSocketOperationError); +} + +void tst_QTcpServer::proxyFactory() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QFETCH(QList<QNetworkProxy>, proxyList); + QFETCH(QNetworkProxy, proxyUsed); + QFETCH(bool, fails); + + MyProxyFactory *factory = new MyProxyFactory; + factory->toReturn = proxyList; + QNetworkProxyFactory::setApplicationProxyFactory(factory); + + QTcpServer server; + bool listenResult = server.listen(); + + // Verify that the factory was called properly + QCOMPARE(factory->callCount, 1); + QCOMPARE(factory->lastQuery, QNetworkProxyQuery(0, QString(), QNetworkProxyQuery::TcpServer)); + + QCOMPARE(listenResult, !fails); + QCOMPARE(server.errorString().isEmpty(), !fails); + + // note: the following test is not a hard failure. + // Sometimes, error codes change for the better + QTEST(int(server.serverError()), "expectedError"); +} + +class Qtbug14268Helper : public QObject +{ + Q_OBJECT +public: + QByteArray lastDataPeeked; +public slots: + void newConnection() { + QTcpServer* server=static_cast<QTcpServer*>(sender()); + QTcpSocket* s=server->nextPendingConnection(); + connect(s,SIGNAL(readyRead()),this,SLOT(onServerReadyRead())); + } + void onServerReadyRead() { + QTcpSocket* clientSocket=static_cast<QTcpSocket*>(sender()); + lastDataPeeked = clientSocket->peek(128*1024).toHex(); + QTestEventLoop::instance().exitLoop(); + } +}; + +// there is a similar test inside tst_qtcpsocket that uses the waitFor* functions instead +void tst_QTcpServer::qtbug14268_peek() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QTcpServer server; + server.listen(); + + Qtbug14268Helper helper; + QObject::connect(&server, SIGNAL(newConnection()), &helper, SLOT(newConnection())); + + QTcpSocket client; + client.connectToHost(QHostAddress::LocalHost, server.serverPort()); + QVERIFY(client.waitForConnected(2000)); + + client.write("abc\n"); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(helper.lastDataPeeked == QByteArray("6162630a")); + + client.write("def\n"); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(helper.lastDataPeeked == QByteArray("6162630a6465660a")); + + client.write("ghi\n"); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(helper.lastDataPeeked == QByteArray("6162630a6465660a6768690a")); +} + +QTEST_MAIN(tst_QTcpServer) +#include "tst_qtcpserver.moc" diff --git a/tests/auto/network/socket/qtcpsocket/.gitignore b/tests/auto/network/socket/qtcpsocket/.gitignore new file mode 100644 index 0000000000..d456ab15d0 --- /dev/null +++ b/tests/auto/network/socket/qtcpsocket/.gitignore @@ -0,0 +1,3 @@ +tst_qtcpsocket +stressTest/stressTest +crashingServer/crashingServer diff --git a/tests/auto/network/socket/qtcpsocket/qtcpsocket.pro b/tests/auto/network/socket/qtcpsocket/qtcpsocket.pro new file mode 100644 index 0000000000..5dfff5bb88 --- /dev/null +++ b/tests/auto/network/socket/qtcpsocket/qtcpsocket.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs + + +!wince*: SUBDIRS = test stressTest +wince*|symbian|vxworks* : SUBDIRS = test + + +requires(contains(QT_CONFIG,private_tests)) diff --git a/tests/auto/network/socket/qtcpsocket/stressTest/Test.cpp b/tests/auto/network/socket/qtcpsocket/stressTest/Test.cpp new file mode 100644 index 0000000000..995fc0528c --- /dev/null +++ b/tests/auto/network/socket/qtcpsocket/stressTest/Test.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +// Qt +#include <QByteArray> +#include <QCoreApplication> +#include <QDataStream> +#include <QTimer> + +// Test +#include "Test.h" + +//------------------------------------------------------------------------------ +My4Socket::My4Socket(QObject *parent) + : QTcpSocket(parent), safeShutDown(false) +{ + connect(this, SIGNAL(readyRead()), this, SLOT(read())); + connect(this, SIGNAL(disconnected()), this, SLOT(closed())); +} + +//------------------------------------------------------------------------------ +void My4Socket::read(void) +{ + QDataStream in(this); + + quint32 num, reply; + + while (bytesAvailable()) { + in >> num; + if (num == 42) { + safeShutDown = true; + qDebug("SUCCESS"); + QCoreApplication::instance()->quit(); + return; + } + reply = num + 1; + if (reply == 42) + ++reply; + } + + // Reply with a bigger number + sendTest(reply); +} + +//------------------------------------------------------------------------------ +void My4Socket::closed(void) +{ + if (!safeShutDown) + qDebug("FAILED"); + QCoreApplication::instance()->quit(); +} + +//------------------------------------------------------------------------------ +void My4Socket::sendTest(quint32 num) +{ + QByteArray block; + QDataStream out(&block, QIODevice::WriteOnly); + out << num; + + write(block, block.size()); +} + +//------------------------------------------------------------------------------ +My4Server::My4Server(QObject *parent) + : QTcpServer(parent) +{ + if (listen(QHostAddress::Any, 7700)) + qDebug("qt4server"); + QTimer::singleShot(5000, this, SLOT(stopServer())); +} + +//------------------------------------------------------------------------------ +void My4Server::incomingConnection(int socketId) +{ + m_socket = new My4Socket(this); + m_socket->setSocketDescriptor(socketId); +} + +//------------------------------------------------------------------------------ +void My4Server::stopServer() +{ + if (m_socket) { + qDebug("SUCCESS"); + m_socket->safeShutDown = true; + m_socket->sendTest(42); + } else { + QCoreApplication::instance()->quit(); + } +} + +//------------------------------------------------------------------------------ +Test::Test(Type type) +{ + switch (type) { + case Qt4Client: { + qDebug("qt4client"); + My4Socket *s = new My4Socket(this); + s->connectToHost("localhost", 7700); + s->sendTest(1); + break; + } + case Qt4Server: { + new My4Server(this); + break; + } + default: + break; + } +} diff --git a/tests/auto/network/socket/qtcpsocket/stressTest/Test.h b/tests/auto/network/socket/qtcpsocket/stressTest/Test.h new file mode 100644 index 0000000000..5440e7ffa9 --- /dev/null +++ b/tests/auto/network/socket/qtcpsocket/stressTest/Test.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#ifndef TEST_H +#define TEST_H + +//------------------------------------------------------------------------------ + +#include <QTcpServer> +#include <QTcpSocket> + +//------------------------------------------------------------------------------ +class My4Socket : public QTcpSocket +{ + Q_OBJECT +public: + My4Socket(QObject *parent); + + void sendTest(quint32 num); + bool safeShutDown; + +private slots: + void read(); + void closed(); +}; + +//------------------------------------------------------------------------------ +class My4Server : public QTcpServer +{ + Q_OBJECT +public: + My4Server(QObject *parent = 0); + +protected: + void incomingConnection(int socket); + +private slots: + void stopServer(); + +private: + My4Socket *m_socket; +}; + +//------------------------------------------------------------------------------ +class Test : public QObject +{ + Q_OBJECT + +public: + enum Type { + Qt4Client, + Qt4Server, + }; + Test(Type type); +}; + +//------------------------------------------------------------------------------ +#endif // TEST_H diff --git a/tests/auto/network/socket/qtcpsocket/stressTest/main.cpp b/tests/auto/network/socket/qtcpsocket/stressTest/main.cpp new file mode 100644 index 0000000000..76ce7bd820 --- /dev/null +++ b/tests/auto/network/socket/qtcpsocket/stressTest/main.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** 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 "Test.h" + +#include <QCoreApplication> +#include <QStringList> + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + QString arg; + if (app.arguments().size() > 1) + arg = app.arguments().at(1).toLower().trimmed(); + + Test::Type type; + if (arg == QLatin1String("qt4client")) + type = Test::Qt4Client; + else if (arg == QLatin1String("qt4server")) + type = Test::Qt4Server; + else { + qDebug("usage: ./stressTest <qt4client|qt4server>"); + return 0; + } + + Test test(type); + + return app.exec(); +} diff --git a/tests/auto/network/socket/qtcpsocket/stressTest/stressTest.pro b/tests/auto/network/socket/qtcpsocket/stressTest/stressTest.pro new file mode 100644 index 0000000000..f6215f80a0 --- /dev/null +++ b/tests/auto/network/socket/qtcpsocket/stressTest/stressTest.pro @@ -0,0 +1,12 @@ +HEADERS += Test.h +SOURCES += main.cpp Test.cpp +QT += network + +CONFIG -= app_bundle +CONFIG += console +DESTDIR = ./ +MOC_DIR = .moc/ +TMP_DIR = .tmp/ + +symbian: TARGET.CAPABILITY = NetworkServices + diff --git a/tests/auto/network/socket/qtcpsocket/test/test.pro b/tests/auto/network/socket/qtcpsocket/test/test.pro new file mode 100644 index 0000000000..61bfaad1a1 --- /dev/null +++ b/tests/auto/network/socket/qtcpsocket/test/test.pro @@ -0,0 +1,33 @@ +load(qttest_p4) + +QT += widgets +QT += core-private network-private +SOURCES += ../tst_qtcpsocket.cpp +win32: { +wince*: { + LIBS += -lws2 +} else { + LIBS += -lws2_32 +} +} +QT += network +vxworks:QT -= gui + +symbian: { + TARGET.EPOCHEAPSIZE="0x100 0x3000000" + TARGET.CAPABILITY = NetworkServices ReadUserData +} + +TARGET = tst_qtcpsocket + +win32 { + CONFIG(debug, debug|release) { + DESTDIR = ../debug +} else { + DESTDIR = ../release + } +} else { + DESTDIR = ../ +} + +CONFIG+=insignificant_test # unstable, QTBUG-21043 diff --git a/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp b/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp new file mode 100644 index 0000000000..bae3f7c2f5 --- /dev/null +++ b/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp @@ -0,0 +1,2684 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + + +// Just to get Q_OS_SYMBIAN +#include <qglobal.h> + +#if defined(_WIN32) && !defined(Q_OS_SYMBIAN) +#include <winsock2.h> +#else +#include <sys/types.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <unistd.h> +#define SOCKET int +#define INVALID_SOCKET -1 +#endif + +#include <qplatformdefs.h> + +#include <QtTest/QtTest> + +#include <QAuthenticator> +#include <QCoreApplication> +#include <QEventLoop> +#include <QFile> +#include <QHostAddress> +#include <QHostInfo> +#include <QMap> +#ifndef Q_OS_VXWORKS +#include <QMessageBox> +#include <QPushButton> +#endif +#include <QPointer> +#include <QProcess> +#include <QStringList> +#include <QTcpServer> +#include <QTcpSocket> +#ifndef QT_NO_OPENSSL +#include <QSslSocket> +#endif +#include <QTextStream> +#include <QThread> +#include <QTime> +#include <QTimer> +#include <QDebug> +// RVCT compiles also unused inline methods +# include <QNetworkProxy> + +#ifdef Q_OS_LINUX +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <unistd.h> +#endif + +#include "private/qhostinfo_p.h" + +#include "../../../network-settings.h" +#include "../../../../shared/util.h" + +Q_DECLARE_METATYPE(QAbstractSocket::SocketError) +Q_DECLARE_METATYPE(QAbstractSocket::SocketState) +Q_DECLARE_METATYPE(QNetworkProxy) +Q_DECLARE_METATYPE(QList<QNetworkProxy>) + +//TESTED_CLASS= +//TESTED_FILES= + +QT_FORWARD_DECLARE_CLASS(QTcpSocket) +QT_FORWARD_DECLARE_CLASS(SocketPair) + +class tst_QTcpSocket : public QObject +{ + Q_OBJECT + +public: + tst_QTcpSocket(); + virtual ~tst_QTcpSocket(); + + static void enterLoop(int secs) + { + ++loopLevel; + QTestEventLoop::instance().enterLoop(secs); + --loopLevel; + } + static void exitLoop() + { + // Safe exit - if we aren't in an event loop, don't + // exit one. + if (loopLevel > 0) + QTestEventLoop::instance().exitLoop(); + } + static bool timeout() + { + return QTestEventLoop::instance().timeout(); + } + +public slots: + void initTestCase_data(); + void init(); + void cleanup(); +private slots: + void socketsConstructedBeforeEventLoop(); + void constructing(); + void setInvalidSocketDescriptor(); + void setSocketDescriptor(); + void socketDescriptor(); + void blockingIMAP(); + void nonBlockingIMAP(); + void hostNotFound(); + void timeoutConnect_data(); + void timeoutConnect(); + void delayedClose(); + void partialRead(); + void unget(); + void readAllAfterClose(); + void openCloseOpenClose(); + void connectDisconnectConnectDisconnect(); + void disconnectWhileConnecting_data(); + void disconnectWhileConnecting(); + void disconnectWhileConnectingNoEventLoop_data(); + void disconnectWhileConnectingNoEventLoop(); + void disconnectWhileLookingUp_data(); + void disconnectWhileLookingUp(); + void downloadBigFile(); + void readLine(); + void readLineString(); + void readChunks(); + void waitForBytesWritten(); + void waitForBytesWrittenMinusOne(); + void waitForReadyRead(); + void waitForReadyReadMinusOne(); + void flush(); + void synchronousApi(); + void dontCloseOnTimeout(); + void recursiveReadyRead(); + void atEnd(); + void socketInAThread(); + void socketsInThreads(); + void waitForReadyReadInASlot(); + void remoteCloseError(); + void openMessageBoxInErrorSlot(); +#ifndef Q_OS_WIN + void connectToLocalHostNoService(); +#endif + void waitForConnectedInHostLookupSlot(); + void waitForConnectedInHostLookupSlot2(); + void readyReadSignalsAfterWaitForReadyRead(); +#ifdef Q_OS_LINUX + void linuxKernelBugLocalSocket(); +#endif + void abortiveClose(); + void localAddressEmptyOnBSD(); + void zeroAndMinusOneReturns(); + void connectionRefused(); + void suddenRemoteDisconnect_data(); + void suddenRemoteDisconnect(); + void connectToMultiIP(); + void moveToThread0(); + void increaseReadBufferSize(); + void taskQtBug5799ConnectionErrorWaitForConnected(); + void taskQtBug5799ConnectionErrorEventLoop(); + void taskQtBug7054TimeoutErrorResetting(); + + void invalidProxy_data(); + void invalidProxy(); + void proxyFactory_data(); + void proxyFactory(); + + void qtbug14268_peek(); + + +protected slots: + void nonBlockingIMAP_hostFound(); + void nonBlockingIMAP_connected(); + void nonBlockingIMAP_closed(); + void nonBlockingIMAP_readyRead(); + void nonBlockingIMAP_bytesWritten(qint64); + void readRegularFile_readyRead(); + void exitLoopSlot(); + void downloadBigFileSlot(); + void recursiveReadyReadSlot(); + void waitForReadyReadInASlotSlot(); + void messageBoxSlot(); + void hostLookupSlot(); + void abortiveClose_abortSlot(); + void remoteCloseErrorSlot(); + void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth); + void earlySocketBytesSent(qint64 bytes); + void earlySocketReadyRead(); + +private: + QByteArray expectedReplyIMAP(); + void fetchExpectedReplyIMAP(); + QTcpSocket *newSocket() const; + QTcpSocket *nonBlockingIMAP_socket; + QStringList nonBlockingIMAP_data; + qint64 nonBlockingIMAP_totalWritten; + + QTcpSocket *tmpSocket; + qint64 bytesAvailable; + qint64 expectedLength; + bool readingBody; + QTime timer; + + QByteArray expectedReplyIMAP_cached; + + mutable int proxyAuthCalled; + + bool gotClosedSignal; + int numConnections; + static int loopLevel; + + SocketPair *earlyConstructedSockets; + int earlyBytesWrittenCount; + int earlyReadyReadCount; +}; + +enum ProxyTests { + NoProxy = 0x00, + Socks5Proxy = 0x01, + HttpProxy = 0x02, + TypeMask = 0x0f, + + NoAuth = 0x00, + AuthBasic = 0x10, + AuthNtlm = 0x20, + AuthMask = 0xf0 +}; + +int tst_QTcpSocket::loopLevel = 0; + +class SocketPair: public QObject +{ + Q_OBJECT +public: + QTcpSocket *endPoints[2]; + + SocketPair(QObject *parent = 0) + : QObject(parent) + { + endPoints[0] = endPoints[1] = 0; + } + + bool create() + { + QTcpServer server; + server.listen(); + + QTcpSocket *active = new QTcpSocket(this); + active->connectToHost("127.0.0.1", server.serverPort()); + + if (!active->waitForConnected(1000)) + return false; + + if (!server.waitForNewConnection(1000)) + return false; + + QTcpSocket *passive = server.nextPendingConnection(); + passive->setParent(this); + + endPoints[0] = active; + endPoints[1] = passive; + return true; + } +}; + +tst_QTcpSocket::tst_QTcpSocket() +{ + tmpSocket = 0; + + //This code relates to the socketsConstructedBeforeEventLoop test case + earlyConstructedSockets = new SocketPair; + QVERIFY(earlyConstructedSockets->create()); + earlyBytesWrittenCount = 0; + earlyReadyReadCount = 0; + connect(earlyConstructedSockets->endPoints[0], SIGNAL(readyRead()), this, SLOT(earlySocketReadyRead())); + connect(earlyConstructedSockets->endPoints[1], SIGNAL(bytesWritten(qint64)), this, SLOT(earlySocketBytesSent(qint64))); + earlyConstructedSockets->endPoints[1]->write("hello work"); +} + +tst_QTcpSocket::~tst_QTcpSocket() +{ + +} + +void tst_QTcpSocket::initTestCase_data() +{ + QTest::addColumn<bool>("setProxy"); + QTest::addColumn<int>("proxyType"); + QTest::addColumn<bool>("ssl"); + + qDebug() << QtNetworkSettings::serverName(); + QTest::newRow("WithoutProxy") << false << 0 << false; + QTest::newRow("WithSocks5Proxy") << true << int(Socks5Proxy) << false; + QTest::newRow("WithSocks5ProxyAuth") << true << int(Socks5Proxy | AuthBasic) << false; + + QTest::newRow("WithHttpProxy") << true << int(HttpProxy) << false; + QTest::newRow("WithHttpProxyBasicAuth") << true << int(HttpProxy | AuthBasic) << false; +// QTest::newRow("WithHttpProxyNtlmAuth") << true << int(HttpProxy | AuthNtlm) << false; + +#ifndef QT_NO_OPENSSL + QTest::newRow("WithoutProxy SSL") << false << 0 << true; + QTest::newRow("WithSocks5Proxy SSL") << true << int(Socks5Proxy) << true; + QTest::newRow("WithSocks5AuthProxy SSL") << true << int(Socks5Proxy | AuthBasic) << true; + + QTest::newRow("WithHttpProxy SSL") << true << int(HttpProxy) << true; + QTest::newRow("WithHttpProxyBasicAuth SSL") << true << int(HttpProxy | AuthBasic) << true; +// QTest::newRow("WithHttpProxyNtlmAuth SSL") << true << int(HttpProxy | AuthNtlm) << true; +#endif +} + +void tst_QTcpSocket::init() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) { + QFETCH_GLOBAL(int, proxyType); + QList<QHostAddress> addresses = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses(); + QVERIFY2(addresses.count() > 0, "failed to get ip address for test server"); + QString fluke = addresses.first().toString(); + QNetworkProxy proxy; + + switch (proxyType) { + case Socks5Proxy: + proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, fluke, 1080); + break; + + case Socks5Proxy | AuthBasic: + proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, fluke, 1081); + break; + + case HttpProxy | NoAuth: + proxy = QNetworkProxy(QNetworkProxy::HttpProxy, fluke, 3128); + break; + + case HttpProxy | AuthBasic: + proxy = QNetworkProxy(QNetworkProxy::HttpProxy, fluke, 3129); + break; + + case HttpProxy | AuthNtlm: + proxy = QNetworkProxy(QNetworkProxy::HttpProxy, fluke, 3130); + break; + } + QNetworkProxy::setApplicationProxy(proxy); + } + + qt_qhostinfo_clear_cache(); +} + +QTcpSocket *tst_QTcpSocket::newSocket() const +{ + QTcpSocket *socket; +#ifndef QT_NO_OPENSSL + QFETCH_GLOBAL(bool, ssl); + socket = ssl ? new QSslSocket : new QTcpSocket; +#else + socket = new QTcpSocket; +#endif + + proxyAuthCalled = 0; + connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + Qt::DirectConnection); + return socket; +} + +void tst_QTcpSocket::cleanup() +{ + QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); +} + +void tst_QTcpSocket::proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth) +{ + ++proxyAuthCalled; + auth->setUser("qsockstest"); + auth->setPassword("password"); +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::socketsConstructedBeforeEventLoop() +{ + QFETCH_GLOBAL(bool, setProxy); + QFETCH_GLOBAL(bool, ssl); + if (setProxy || ssl) + return; + //This test checks that sockets constructed before QCoreApplication::exec() still emit signals + //see construction code in the tst_QTcpSocket constructor + enterLoop(3); + QCOMPARE(earlyBytesWrittenCount, 1); + QCOMPARE(earlyReadyReadCount, 1); + earlyConstructedSockets->endPoints[0]->close(); + earlyConstructedSockets->endPoints[1]->close(); +} + +void tst_QTcpSocket::earlySocketBytesSent(qint64 /* bytes */) +{ + earlyBytesWrittenCount++; +} + +void tst_QTcpSocket::earlySocketReadyRead() +{ + earlyReadyReadCount++; +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::constructing() +{ + QTcpSocket *socket = newSocket(); + + // Check the initial state of the QTcpSocket. + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + QVERIFY(socket->isSequential()); + QVERIFY(!socket->isOpen()); + QVERIFY(!socket->isValid()); + QCOMPARE(socket->socketType(), QTcpSocket::TcpSocket); + + char c; + QCOMPARE(socket->getChar(&c), false); + QCOMPARE((int) socket->bytesAvailable(), 0); + QCOMPARE(socket->canReadLine(), false); + QCOMPARE(socket->readLine(), QByteArray()); + QCOMPARE(socket->socketDescriptor(), -1); + QCOMPARE((int) socket->localPort(), 0); + QVERIFY(socket->localAddress() == QHostAddress()); + QCOMPARE((int) socket->peerPort(), 0); + QVERIFY(socket->peerAddress() == QHostAddress()); + QCOMPARE(socket->error(), QTcpSocket::UnknownSocketError); + QCOMPARE(socket->errorString(), QString("Unknown error")); + + // Check the state of the socket layer? + delete socket; +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::setInvalidSocketDescriptor() +{ + QTcpSocket *socket = newSocket(); + QCOMPARE(socket->socketDescriptor(), -1); +#ifdef Q_OS_SYMBIAN + QTest::ignoreMessage(QtWarningMsg, "QSymbianSocketEngine::initialize - socket descriptor not found"); +#endif + QVERIFY(!socket->setSocketDescriptor(-5, QTcpSocket::UnconnectedState)); + QCOMPARE(socket->socketDescriptor(), -1); + + QCOMPARE(socket->error(), QTcpSocket::UnsupportedSocketOperationError); + + delete socket; +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::setSocketDescriptor() +{ +#ifdef Q_OS_SYMBIAN + QSKIP("adopting open c socket handles is not supported", SkipAll); +#else + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; // this test doesn't make sense with proxies + +#ifdef Q_OS_WIN + // need the dummy to ensure winsock is started + QTcpSocket *dummy = newSocket(); + dummy->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(dummy->waitForConnected()); + + SOCKET sock = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sock == INVALID_SOCKET) { + qErrnoWarning(WSAGetLastError(), "INVALID_SOCKET"); + } +#else + SOCKET sock = ::socket(AF_INET, SOCK_STREAM, 0); + + // artificially increase the value of sock + SOCKET sock2 = ::fcntl(sock, F_DUPFD, sock + 50); + ::close(sock); + sock = sock2; +#endif + + QVERIFY(sock != INVALID_SOCKET); + QTcpSocket *socket = newSocket(); + QVERIFY(socket->setSocketDescriptor(sock, QTcpSocket::UnconnectedState)); + QCOMPARE(socket->socketDescriptor(), (int)sock); + + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QCOMPARE(socket->state(), QTcpSocket::HostLookupState); + QCOMPARE(socket->socketDescriptor(), (int)sock); + QVERIFY(socket->waitForConnected(10000)); + // skip this, it has been broken for years, see task 260735 + // if somebody complains, consider fixing it, but it might break existing applications. + QEXPECT_FAIL("", "bug has been around for years, will not fix without need", Continue); + QCOMPARE(socket->socketDescriptor(), (int)sock); + delete socket; +#ifdef Q_OS_WIN + delete dummy; +#endif +#endif +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::socketDescriptor() +{ + QTcpSocket *socket = newSocket(); + + QCOMPARE(socket->socketDescriptor(), -1); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY((socket->state() == QAbstractSocket::HostLookupState && socket->socketDescriptor() == -1) || + (socket->state() == QAbstractSocket::ConnectingState && socket->socketDescriptor() != -1)); + QVERIFY(socket->waitForConnected(10000)); + QVERIFY(socket->state() == QAbstractSocket::ConnectedState); + QVERIFY(socket->socketDescriptor() != -1); + + delete socket; +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::blockingIMAP() +{ + QTcpSocket *socket = newSocket(); + + // Connect + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(10000)); + QCOMPARE(socket->state(), QTcpSocket::ConnectedState); + QVERIFY(socket->isValid()); + + // Read greeting + QVERIFY(socket->waitForReadyRead(5000)); + QString s = socket->readLine(); + // only test if an OK was returned, to make the test compatible between different + // IMAP server versions + QCOMPARE(s.left(4).toLatin1().constData(), "* OK"); + + // Write NOOP + QCOMPARE((int) socket->write("1 NOOP\r\n", 8), 8); + QCOMPARE((int) socket->write("2 NOOP\r\n", 8), 8); + + if (!socket->canReadLine()) + QVERIFY(socket->waitForReadyRead(5000)); + + // Read response + s = socket->readLine(); + QCOMPARE(s.toLatin1().constData(), "1 OK Completed\r\n"); + + // Write a third NOOP to verify that write doesn't clear the read buffer + QCOMPARE((int) socket->write("3 NOOP\r\n", 8), 8); + + // Read second response + if (!socket->canReadLine()) + QVERIFY(socket->waitForReadyRead(5000)); + s = socket->readLine(); + QCOMPARE(s.toLatin1().constData(), "2 OK Completed\r\n"); + + // Read third response + if (!socket->canReadLine()) + QVERIFY(socket->waitForReadyRead(5000)); + s = socket->readLine(); + QCOMPARE(s.toLatin1().constData(), "3 OK Completed\r\n"); + + + // Write LOGOUT + QCOMPARE((int) socket->write("4 LOGOUT\r\n", 10), 10); + + if (!socket->canReadLine()) + QVERIFY(socket->waitForReadyRead(5000)); + + // Read two lines of respose + s = socket->readLine(); + QCOMPARE(s.toLatin1().constData(), "* BYE LOGOUT received\r\n"); + + if (!socket->canReadLine()) + QVERIFY(socket->waitForReadyRead(5000)); + + s = socket->readLine(); + QCOMPARE(s.toLatin1().constData(), "4 OK Completed\r\n"); + + // Close the socket + socket->close(); + + // Check that it's closed + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + + delete socket; +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::hostNotFound() +{ + QTcpSocket *socket = newSocket(); + + socket->connectToHost("nosuchserver.troll.no", 80); + QVERIFY(!socket->waitForConnected()); + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + QCOMPARE(int(socket->error()), int(QTcpSocket::HostNotFoundError)); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::timeoutConnect_data() +{ + QTest::addColumn<QString>("address"); + QTest::newRow("host") << QtNetworkSettings::serverName(); + QTest::newRow("ip") << QtNetworkSettings::serverIP().toString(); +} + +void tst_QTcpSocket::timeoutConnect() +{ + QFETCH(QString, address); + QTcpSocket *socket = newSocket(); + + QElapsedTimer timer; + timer.start(); + + // Port 1357 is configured to drop packets on the test server + socket->connectToHost(address, 1357); + QVERIFY(timer.elapsed() < 150); + QVERIFY(!socket->waitForConnected(1000)); //200ms is too short when using SOCKS proxy authentication + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + QCOMPARE(int(socket->error()), int(QTcpSocket::SocketTimeoutError)); + + timer.start(); + socket->connectToHost(address, 1357); + QVERIFY(timer.elapsed() < 150); + QTimer::singleShot(50, &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY(socket->state() == QTcpSocket::ConnectingState + || socket->state() == QTcpSocket::HostLookupState); + socket->abort(); + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + QCOMPARE(socket->openMode(), QIODevice::NotOpen); + + delete socket; +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::nonBlockingIMAP() +{ + QTcpSocket *socket = newSocket(); + connect(socket, SIGNAL(hostFound()), SLOT(nonBlockingIMAP_hostFound())); + connect(socket, SIGNAL(connected()), SLOT(nonBlockingIMAP_connected())); + connect(socket, SIGNAL(disconnected()), SLOT(nonBlockingIMAP_closed())); + connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(nonBlockingIMAP_bytesWritten(qint64))); + connect(socket, SIGNAL(readyRead()), SLOT(nonBlockingIMAP_readyRead())); + nonBlockingIMAP_socket = socket; + + // Connect + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->state() == QTcpSocket::HostLookupState || + socket->state() == QTcpSocket::ConnectingState); + + enterLoop(30); + if (timeout()) { + QFAIL("Timed out"); + } + + if (socket->state() == QTcpSocket::ConnectingState) { + enterLoop(30); + if (timeout()) { + QFAIL("Timed out"); + } + } + + QCOMPARE(socket->state(), QTcpSocket::ConnectedState); + + enterLoop(30); + if (timeout()) { + QFAIL("Timed out"); + } + + // Read greeting + QVERIFY(!nonBlockingIMAP_data.isEmpty()); + QCOMPARE(nonBlockingIMAP_data.at(0).left(4).toLatin1().constData(), "* OK"); + nonBlockingIMAP_data.clear(); + + nonBlockingIMAP_totalWritten = 0; + + // Write NOOP + QCOMPARE((int) socket->write("1 NOOP\r\n", 8), 8); + + + enterLoop(30); + if (timeout()) { + QFAIL("Timed out"); + } + + QVERIFY(nonBlockingIMAP_totalWritten == 8); + + + enterLoop(30); + if (timeout()) { + QFAIL("Timed out"); + } + + + // Read response + QVERIFY(!nonBlockingIMAP_data.isEmpty()); + QCOMPARE(nonBlockingIMAP_data.at(0).toLatin1().constData(), "1 OK Completed\r\n"); + nonBlockingIMAP_data.clear(); + + + nonBlockingIMAP_totalWritten = 0; + + // Write LOGOUT + QCOMPARE((int) socket->write("2 LOGOUT\r\n", 10), 10); + + enterLoop(30); + if (timeout()) { + QFAIL("Timed out"); + } + + QVERIFY(nonBlockingIMAP_totalWritten == 10); + + // Wait for greeting + enterLoop(30); + if (timeout()) { + QFAIL("Timed out"); + } + + // Read two lines of respose + QCOMPARE(nonBlockingIMAP_data.at(0).toLatin1().constData(), "* BYE LOGOUT received\r\n"); + QCOMPARE(nonBlockingIMAP_data.at(1).toLatin1().constData(), "2 OK Completed\r\n"); + nonBlockingIMAP_data.clear(); + + // Close the socket + socket->close(); + + // Check that it's closed + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + + delete socket; +} + +void tst_QTcpSocket::nonBlockingIMAP_hostFound() +{ + exitLoop(); +} + +void tst_QTcpSocket::nonBlockingIMAP_connected() +{ + exitLoop(); +} + +void tst_QTcpSocket::nonBlockingIMAP_readyRead() +{ + while (nonBlockingIMAP_socket->canReadLine()) + nonBlockingIMAP_data.append(nonBlockingIMAP_socket->readLine()); + + exitLoop(); +} + +void tst_QTcpSocket::nonBlockingIMAP_bytesWritten(qint64 written) +{ + nonBlockingIMAP_totalWritten += written; + exitLoop(); +} + +void tst_QTcpSocket::nonBlockingIMAP_closed() +{ +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::delayedClose() +{ + QTcpSocket *socket = newSocket(); + connect(socket, SIGNAL(connected()), SLOT(nonBlockingIMAP_connected())); + connect(socket, SIGNAL(disconnected()), SLOT(exitLoopSlot())); + + socket->connectToHost(QtNetworkSettings::serverName(), 143); + + enterLoop(30); + if (timeout()) + QFAIL("Timed out"); + + QCOMPARE(socket->state(), QTcpSocket::ConnectedState); + + QCOMPARE((int) socket->write("1 LOGOUT\r\n", 10), 10); + + // Add a huge bulk of data to be written after the logout + // command. The server will shut down after receiving the LOGOUT, + // so this data will not be read. But our close call should + // schedule a delayed close because all the data can not be + // written in one go. + QCOMPARE((int) socket->write(QByteArray(100000, '\n'), 100000), 100000); + + socket->close(); + + QCOMPARE((int) socket->state(), (int) QTcpSocket::ClosingState); + + enterLoop(10); + if (timeout()) + QFAIL("Timed out"); + + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + + delete socket; +} + + +//---------------------------------------------------------------------------------- + +QByteArray tst_QTcpSocket::expectedReplyIMAP() +{ + if (expectedReplyIMAP_cached.isEmpty()) { + fetchExpectedReplyIMAP(); + } + + return expectedReplyIMAP_cached; +} + +// Figure out how the current IMAP server responds +void tst_QTcpSocket::fetchExpectedReplyIMAP() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY2(socket->waitForConnected(10000), qPrintable(socket->errorString())); + QVERIFY2(socket->state() == QTcpSocket::ConnectedState, qPrintable(socket->errorString())); + + QTRY_VERIFY(socket->canReadLine()); + + QByteArray greeting = socket->readLine(); + delete socket; + + QVERIFY2(QtNetworkSettings::compareReplyIMAP(greeting), greeting.constData()); + + expectedReplyIMAP_cached = greeting; +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::partialRead() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(10000)); + QVERIFY(socket->state() == QTcpSocket::ConnectedState); + char buf[512]; + + QByteArray greeting = expectedReplyIMAP(); + QVERIFY(!greeting.isEmpty()); + + for (int i = 0; i < 10; i += 2) { + while (socket->bytesAvailable() < 2) + QVERIFY(socket->waitForReadyRead(5000)); + QVERIFY(socket->read(buf, 2) == 2); + buf[2] = '\0'; + QCOMPARE((char *)buf, greeting.mid(i, 2).data()); + } + + delete socket; +} + +//---------------------------------------------------------------------------------- + +void tst_QTcpSocket::unget() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(10000)); + QVERIFY(socket->state() == QTcpSocket::ConnectedState); + char buf[512]; + + QByteArray greeting = expectedReplyIMAP(); + QVERIFY(!greeting.isEmpty()); + + for (int i = 0; i < 10; i += 2) { + while (socket->bytesAvailable() < 2) + QVERIFY(socket->waitForReadyRead(10000)); + int bA = socket->bytesAvailable(); + QVERIFY(socket->read(buf, 2) == 2); + buf[2] = '\0'; + QCOMPARE((char *)buf, greeting.mid(i, 2).data()); + QCOMPARE((int)socket->bytesAvailable(), bA - 2); + socket->ungetChar(buf[1]); + socket->ungetChar(buf[0]); + QCOMPARE((int)socket->bytesAvailable(), bA); + QVERIFY(socket->read(buf, 2) == 2); + buf[2] = '\0'; + QCOMPARE((char *)buf, greeting.mid(i, 2).data()); + } + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::readRegularFile_readyRead() +{ + exitLoop(); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::readAllAfterClose() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + connect(socket, SIGNAL(readyRead()), SLOT(readRegularFile_readyRead())); + enterLoop(10); + if (timeout()) + QFAIL("Network operation timed out"); + + socket->close(); + QByteArray array = socket->readAll(); + QCOMPARE(array.size(), 0); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::openCloseOpenClose() +{ + QTcpSocket *socket = newSocket(); + + for (int i = 0; i < 3; ++i) { + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + QCOMPARE(int(socket->openMode()), int(QIODevice::NotOpen)); + QVERIFY(socket->isSequential()); + QVERIFY(!socket->isOpen()); + QVERIFY(socket->socketType() == QTcpSocket::TcpSocket); + + char c; + QCOMPARE(socket->getChar(&c), false); + QCOMPARE((int) socket->bytesAvailable(), 0); + QCOMPARE(socket->canReadLine(), false); + QCOMPARE(socket->readLine(), QByteArray()); + QCOMPARE(socket->socketDescriptor(), -1); + QCOMPARE((int) socket->localPort(), 0); + QVERIFY(socket->localAddress() == QHostAddress()); + QCOMPARE((int) socket->peerPort(), 0); + QVERIFY(socket->peerAddress() == QHostAddress()); + QCOMPARE(socket->error(), QTcpSocket::UnknownSocketError); + QCOMPARE(socket->errorString(), QString("Unknown error")); + + QVERIFY(socket->state() == QTcpSocket::UnconnectedState); + + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(10000)); + socket->close(); + } + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::connectDisconnectConnectDisconnect() +{ + QTcpSocket *socket = newSocket(); + + for (int i = 0; i < 3; ++i) { + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + QVERIFY(socket->socketType() == QTcpSocket::TcpSocket); + + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForReadyRead(10000)); + QCOMPARE(QString::fromLatin1(socket->read(4)), QString("* OK")); + + socket->disconnectFromHost(); + if (socket->state() != QTcpSocket::UnconnectedState) + QVERIFY(socket->waitForDisconnected(10000)); + QCOMPARE(int(socket->openMode()), int(QIODevice::ReadWrite)); + } + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::disconnectWhileConnecting_data() +{ + QTest::addColumn<QByteArray>("data"); + QTest::addColumn<bool>("closeDirectly"); + + QTest::newRow("without-data") << QByteArray() << false; + QTest::newRow("without-data+close") << QByteArray() << true; + QTest::newRow("with-data") << QByteArray("Hello, world!") << false; + QTest::newRow("with-data+close") << QByteArray("Hello, world!") << true; + + QByteArray bigData(1024*1024, '@'); + QTest::newRow("with-big-data") << bigData << false; + QTest::newRow("with-big-data+close") << bigData << true; +} + +void tst_QTcpSocket::disconnectWhileConnecting() +{ + QFETCH(QByteArray, data); + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; //proxy not useful for localhost test case + + QTcpServer server; + QVERIFY(server.listen(QHostAddress::LocalHost)); + + // proceed to the connect-write-disconnect + QTcpSocket *socket = newSocket(); + socket->connectToHost("127.0.0.1", server.serverPort()); + if (!data.isEmpty()) + socket->write(data); + if (socket->state() == QAbstractSocket::ConnectedState) + QSKIP("localhost connections are immediate, test case is invalid", SkipSingle); + + QFETCH(bool, closeDirectly); + if (closeDirectly) { + socket->close(); + QCOMPARE(int(socket->openMode()), int(QIODevice::NotOpen)); + } else { + socket->disconnectFromHost(); + } + + connect(socket, SIGNAL(disconnected()), SLOT(exitLoopSlot())); +#ifndef Q_OS_SYMBIAN + enterLoop(10); +#else + enterLoop(30); +#endif + QVERIFY2(!timeout(), "Network timeout"); + QVERIFY(socket->state() == QAbstractSocket::UnconnectedState); + if (!closeDirectly) { + QCOMPARE(int(socket->openMode()), int(QIODevice::ReadWrite)); + socket->close(); + } + QCOMPARE(int(socket->openMode()), int(QIODevice::NotOpen)); + + // accept the other side and verify that it was sent properly: + QVERIFY(server.hasPendingConnections() || server.waitForNewConnection(0)); + QTcpSocket *othersocket = server.nextPendingConnection(); + if (othersocket->state() != QAbstractSocket::UnconnectedState) + QVERIFY2(othersocket->waitForDisconnected(10000), "Network timeout"); + QVERIFY(othersocket->state() == QAbstractSocket::UnconnectedState); + QCOMPARE(othersocket->readAll(), data); + + delete socket; + delete othersocket; +} + +//---------------------------------------------------------------------------------- +class ReceiverThread: public QThread +{ + QTcpServer *server; +public: + int serverPort; + bool ok; + QByteArray receivedData; + volatile bool quit; + + ReceiverThread() + : server(0), ok(false), quit(false) + { } + + ~ReceiverThread() { } + + bool listen() + { + server = new QTcpServer; + if (!server->listen(QHostAddress::LocalHost)) + return false; + serverPort = server->serverPort(); + server->moveToThread(this); + return true; + } + + static void cleanup(void *ptr) + { + ReceiverThread* self = reinterpret_cast<ReceiverThread*>(ptr); + self->quit = true; + self->wait(30000); + delete self; + } + +protected: + void run() + { + bool timedOut = false; + while (!quit) { +#ifndef Q_OS_SYMBIAN + if (server->waitForNewConnection(500, &timedOut)) +#else + if (server->waitForNewConnection(5000, &timedOut)) +#endif + break; + if (!timedOut) + return; + } + + QTcpSocket *socket = server->nextPendingConnection(); + while (!quit) { +#ifndef Q_OS_SYMBIAN + if (socket->waitForDisconnected(500)) +#else + if (socket->waitForDisconnected(5000)) +#endif + break; + if (socket->error() != QAbstractSocket::SocketTimeoutError) + return; + } + + if (!quit) { + receivedData = socket->readAll(); + ok = true; + } + delete socket; + delete server; + server = 0; + } +}; + +void tst_QTcpSocket::disconnectWhileConnectingNoEventLoop_data() +{ + disconnectWhileConnecting_data(); +} + +void tst_QTcpSocket::disconnectWhileConnectingNoEventLoop() +{ + QFETCH(QByteArray, data); + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; //proxy not useful for localhost test case + + QScopedPointer<ReceiverThread, ReceiverThread> thread (new ReceiverThread); + QVERIFY(thread->listen()); + thread->start(); + + // proceed to the connect-write-disconnect + QTcpSocket *socket = newSocket(); + socket->connectToHost("127.0.0.1", thread->serverPort); + if (!data.isEmpty()) + socket->write(data); + if (socket->state() == QAbstractSocket::ConnectedState) { + QSKIP("localhost connections are immediate, test case is invalid", SkipSingle); + } + + QFETCH(bool, closeDirectly); + if (closeDirectly) { + socket->close(); + QCOMPARE(int(socket->openMode()), int(QIODevice::NotOpen)); + } else { + socket->disconnectFromHost(); + } + +#ifndef Q_OS_SYMBIAN + QVERIFY2(socket->waitForDisconnected(10000), "Network timeout"); +#else + QVERIFY2(socket->waitForDisconnected(30000), "Network timeout"); +#endif + QVERIFY(socket->state() == QAbstractSocket::UnconnectedState); + if (!closeDirectly) { + QCOMPARE(int(socket->openMode()), int(QIODevice::ReadWrite)); + socket->close(); + } + QCOMPARE(int(socket->openMode()), int(QIODevice::NotOpen)); + delete socket; + + // check if the other side received everything ok + QVERIFY(thread->wait(30000)); + QVERIFY(thread->ok); + QCOMPARE(thread->receivedData, data); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::disconnectWhileLookingUp_data() +{ + QTest::addColumn<bool>("doClose"); + + QTest::newRow("disconnect") << false; + QTest::newRow("close") << true; +} + +void tst_QTcpSocket::disconnectWhileLookingUp() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; // we let the proxies do the lookup now + + // just connect and disconnect, then make sure nothing weird happened + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 21); + + // check that connect is in progress + QVERIFY(socket->state() != QAbstractSocket::UnconnectedState); + + QFETCH(bool, doClose); + if (doClose) { + socket->close(); + QVERIFY(socket->openMode() == QIODevice::NotOpen); + } else { + socket->disconnectFromHost(); + QVERIFY(socket->openMode() == QIODevice::ReadWrite); + } + + // let anything queued happen + QEventLoop loop; +#ifndef Q_OS_SYMBIAN + QTimer::singleShot(50, &loop, SLOT(quit())); +#else + QTimer::singleShot(5000, &loop, SLOT(quit())); +#endif + loop.exec(); + + // recheck + if (doClose) { + QVERIFY(socket->openMode() == QIODevice::NotOpen); + } else { + QVERIFY(socket->openMode() == QIODevice::ReadWrite); + } + + QVERIFY(socket->state() == QAbstractSocket::UnconnectedState); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::downloadBigFile() +{ + if (tmpSocket) + delete tmpSocket; + tmpSocket = newSocket(); + + connect(tmpSocket, SIGNAL(connected()), SLOT(exitLoopSlot())); + connect(tmpSocket, SIGNAL(readyRead()), SLOT(downloadBigFileSlot())); + connect(tmpSocket, SIGNAL(disconnected()), SLOT(exitLoopSlot())); + + tmpSocket->connectToHost(QtNetworkSettings::serverName(), 80); + + enterLoop(30); + if (timeout()) { + delete tmpSocket; + tmpSocket = 0; + QFAIL("Network operation timed out"); + } + + QByteArray hostName = QtNetworkSettings::serverName().toLatin1(); + QVERIFY(tmpSocket->state() == QAbstractSocket::ConnectedState); + QVERIFY(tmpSocket->write("GET /qtest/mediumfile HTTP/1.0\r\n") > 0); + QVERIFY(tmpSocket->write("HOST: ") > 0); + QVERIFY(tmpSocket->write(hostName.data()) > 0); + QVERIFY(tmpSocket->write("\r\n") > 0); + QVERIFY(tmpSocket->write("\r\n") > 0); + + bytesAvailable = 0; + expectedLength = 0; + readingBody = false; + + QTime stopWatch; + stopWatch.start(); + + enterLoop(600); + if (timeout()) { + delete tmpSocket; + tmpSocket = 0; + if (bytesAvailable > 0) + qDebug("Slow Connection, only downloaded %ld of %d", long(bytesAvailable), 10000281); + QFAIL("Network operation timed out"); + } + + QCOMPARE(bytesAvailable, expectedLength); + + qDebug("\t\t%.1fMB/%.1fs: %.1fMB/s", + bytesAvailable / (1024.0 * 1024.0), + stopWatch.elapsed() / 1024.0, + (bytesAvailable / (stopWatch.elapsed() / 1000.0)) / (1024 * 1024)); + + delete tmpSocket; + tmpSocket = 0; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::exitLoopSlot() +{ + exitLoop(); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::downloadBigFileSlot() +{ + if (!readingBody) { + while (tmpSocket->canReadLine()) { + QByteArray array = tmpSocket->readLine(); + if (array.startsWith("Content-Length")) + expectedLength = array.simplified().split(' ').at(1).toInt(); + if (array == "\r\n") { + readingBody = true; + break; + } + } + } + if (readingBody) { + bytesAvailable += tmpSocket->readAll().size(); + if (bytesAvailable == expectedLength) + exitLoop(); + } +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::readLine() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(5000)); + + while (!socket->canReadLine()) + QVERIFY(socket->waitForReadyRead(10000)); + + char buffer[1024]; + + qint64 linelen = socket->readLine(buffer, sizeof(buffer)); + QVERIFY(linelen >= 3); + QVERIFY(linelen < 1024); + + QByteArray reply = QByteArray::fromRawData(buffer, linelen); + QCOMPARE((int) buffer[linelen-2], (int) '\r'); + QCOMPARE((int) buffer[linelen-1], (int) '\n'); + QCOMPARE((int) buffer[linelen], (int) '\0'); + + QVERIFY2(QtNetworkSettings::compareReplyIMAP(reply), reply.constData()); + + QCOMPARE(socket->write("1 NOOP\r\n"), qint64(8)); + + while (socket->bytesAvailable() < 10) + QVERIFY(socket->waitForReadyRead(10000)); + + QCOMPARE(socket->readLine(buffer, 11), qint64(10)); + QCOMPARE((const char *)buffer, "1 OK Compl"); + + while (socket->bytesAvailable() < 6) + QVERIFY(socket->waitForReadyRead(10000)); + + QCOMPARE(socket->readLine(buffer, 11), qint64(6)); + QCOMPARE((const char *)buffer, "eted\r\n"); + + QVERIFY(!socket->waitForReadyRead(100)); + QCOMPARE(socket->readLine(buffer, sizeof(buffer)), qint64(0)); + QVERIFY(socket->error() == QAbstractSocket::SocketTimeoutError + || socket->error() == QAbstractSocket::RemoteHostClosedError); + QCOMPARE(socket->bytesAvailable(), qint64(0)); + + socket->close(); + QCOMPARE(socket->readLine(buffer, sizeof(buffer)), qint64(-1)); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::readLineString() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForReadyRead(10000)); + + QByteArray arr = socket->readLine(); + QVERIFY2(QtNetworkSettings::compareReplyIMAP(arr), arr.constData()); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::readChunks() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(10000)); + QVERIFY(socket->waitForReadyRead(5000)); + + char buf[4096]; + memset(buf, '@', sizeof(buf)); + qint64 dataLength = socket->read(buf, sizeof(buf)); + QVERIFY(dataLength > 0); + + QCOMPARE(buf[dataLength - 2], '\r'); + QCOMPARE(buf[dataLength - 1], '\n'); + QCOMPARE(buf[dataLength], '@'); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::waitForBytesWritten() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 80); + QVERIFY(socket->waitForConnected(10000)); + + socket->write("GET / HTTP/1.0\r\n\r\n"); + qint64 toWrite = socket->bytesToWrite(); + QVERIFY(socket->waitForBytesWritten(5000)); + QVERIFY(toWrite > socket->bytesToWrite()); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::waitForBytesWrittenMinusOne() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 80); + QVERIFY(socket->waitForConnected(10000)); + + socket->write("GET / HTTP/1.0\r\n\r\n"); + qint64 toWrite = socket->bytesToWrite(); + QVERIFY(socket->waitForBytesWritten(-1)); + QVERIFY(toWrite > socket->bytesToWrite()); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::waitForReadyRead() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 80); + socket->write("GET / HTTP/1.0\r\n\r\n"); + QVERIFY(socket->waitForReadyRead(5000)); + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::waitForReadyReadMinusOne() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 80); + socket->write("GET / HTTP/1.0\r\n\r\n"); + QVERIFY(socket->waitForReadyRead(-1)); + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::flush() +{ + QTcpSocket *socket = newSocket(); + socket->flush(); + + connect(socket, SIGNAL(connected()), SLOT(exitLoopSlot())); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + enterLoop(60); + QVERIFY(socket->isOpen()); + + socket->write("1 LOGOUT\r\n"); + QCOMPARE(socket->bytesToWrite(), qint64(10)); + socket->flush(); + QCOMPARE(socket->bytesToWrite(), qint64(0)); + socket->close(); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::synchronousApi() +{ + QTcpSocket *ftpSocket = newSocket(); + ftpSocket->connectToHost(QtNetworkSettings::serverName(), 21); + ftpSocket->write("QUIT\r\n"); + QVERIFY(ftpSocket->waitForDisconnected(10000)); + QVERIFY(ftpSocket->bytesAvailable() > 0); + QByteArray arr = ftpSocket->readAll(); + QVERIFY(arr.size() > 0); + delete ftpSocket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::dontCloseOnTimeout() +{ + QTcpServer server; + server.setProxy(QNetworkProxy(QNetworkProxy::NoProxy)); + QVERIFY(server.listen()); + + QHostAddress serverAddress = QHostAddress::LocalHost; + if (!(server.serverAddress() == QHostAddress::AnyIPv4) && !(server.serverAddress() == QHostAddress::AnyIPv6)) + serverAddress = server.serverAddress(); + + QTcpSocket *socket = newSocket(); + socket->connectToHost(serverAddress, server.serverPort()); +#ifndef Q_OS_SYMBIAN + QVERIFY(!socket->waitForReadyRead(100)); +#else + QVERIFY(!socket->waitForReadyRead(5000)); +#endif + QCOMPARE(socket->error(), QTcpSocket::SocketTimeoutError); + QVERIFY(socket->isOpen()); + +#ifndef Q_OS_SYMBIAN + QVERIFY(!socket->waitForDisconnected(100)); +#else + QVERIFY(!socket->waitForDisconnected(5000)); +#endif + QCOMPARE(socket->error(), QTcpSocket::SocketTimeoutError); + QVERIFY(socket->isOpen()); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::recursiveReadyRead() +{ + QTcpSocket *smtp = newSocket(); + connect(smtp, SIGNAL(connected()), SLOT(exitLoopSlot())); + connect(smtp, SIGNAL(readyRead()), SLOT(recursiveReadyReadSlot())); + tmpSocket = smtp; + + QSignalSpy spy(smtp, SIGNAL(readyRead())); + + smtp->connectToHost("smtp.trolltech.com", 25); + enterLoop(30); + QVERIFY2(!timeout(), + "Timed out when connecting to smtp.trolltech.com:25"); + + enterLoop(30); + QVERIFY2(!timeout(), + "Timed out when waiting for the readyRead() signal"); + + QCOMPARE(spy.count(), 1); + + delete smtp; +} + +void tst_QTcpSocket::recursiveReadyReadSlot() +{ + // make sure the server spits out more data + tmpSocket->write("NOOP\r\n"); + tmpSocket->flush(); + + // indiscriminately enter the event loop and start processing + // events again. but oops! future socket notifications will cause + // undesired recursive behavior. Unless QTcpSocket is smart, which + // it of course is. :-) + QEventLoop loop; + for (int i = 0; i < 100; ++i) + loop.processEvents(); + + // all we really wanted to do was process some events, then exit + // the loop + exitLoop(); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::atEnd() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 21); + + QVERIFY(socket->waitForReadyRead(15000)); + QTextStream stream(socket); + QVERIFY(!stream.atEnd()); + QString greeting = stream.readLine(); + QVERIFY(stream.atEnd()); + + // Test server must use some vsFTPd 2.x.x version + QVERIFY2(greeting.length() == sizeof("220 (vsFTPd 2.x.x)")-1, qPrintable(greeting)); + QVERIFY2(greeting.startsWith("220 (vsFTPd 2."), qPrintable(greeting)); + QVERIFY2(greeting.endsWith(")"), qPrintable(greeting)); + + delete socket; +} + +class TestThread : public QThread +{ + Q_OBJECT + +public: + inline QByteArray data() const + { + return socketData; + } + +protected: + inline void run() + { +#ifndef QT_NO_OPENSSL + QFETCH_GLOBAL(bool, ssl); + if (ssl) + socket = new QSslSocket; + else +#endif + socket = new QTcpSocket; + connect(socket, SIGNAL(readyRead()), this, SLOT(getData()), Qt::DirectConnection); + connect(socket, SIGNAL(disconnected()), this, SLOT(closed()), Qt::DirectConnection); + connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), Qt::DirectConnection); + + socket->connectToHost(QtNetworkSettings::serverName(), 21); + socket->write("QUIT\r\n"); + exec(); + + delete socket; + } + +private slots: + inline void getData() + { + socketData += socket->readAll(); + } + + inline void closed() + { + quit(); + } + inline void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth) + { + auth->setUser("qsockstest"); + auth->setPassword("password"); + } +private: + int exitCode; + QTcpSocket *socket; + QByteArray socketData; +}; + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::socketInAThread() +{ + for (int i = 0; i < 3; ++i) { + TestThread thread; + thread.start(); + QVERIFY(thread.wait(15000)); + QByteArray data = thread.data(); + QVERIFY2(QtNetworkSettings::compareReplyFtp(data), data.constData()); + } +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::socketsInThreads() +{ + for (int i = 0; i < 3; ++i) { + TestThread thread1; + TestThread thread2; + TestThread thread3; + + thread1.start(); + thread2.start(); + thread3.start(); + + QVERIFY(thread2.wait(15000)); + QVERIFY(thread3.wait(15000)); + QVERIFY(thread1.wait(15000)); + + QByteArray data1 = thread1.data(); + QByteArray data2 = thread2.data(); + QByteArray data3 = thread3.data(); + + QVERIFY2(QtNetworkSettings::compareReplyFtp(data1), data1.constData()); + QVERIFY2(QtNetworkSettings::compareReplyFtp(data2), data2.constData()); + QVERIFY2(QtNetworkSettings::compareReplyFtp(data3), data3.constData()); + } +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::waitForReadyReadInASlot() +{ + QTcpSocket *socket = newSocket(); + tmpSocket = socket; + connect(socket, SIGNAL(connected()), this, SLOT(waitForReadyReadInASlotSlot())); + + socket->connectToHost(QtNetworkSettings::serverName(), 80); + socket->write("GET / HTTP/1.0\r\n\r\n"); + + enterLoop(30); + QVERIFY(!timeout()); + + delete socket; +} + +void tst_QTcpSocket::waitForReadyReadInASlotSlot() +{ + QVERIFY(tmpSocket->waitForReadyRead(10000)); + exitLoop(); +} + +class RemoteCloseErrorServer : public QTcpServer +{ + Q_OBJECT +public: + RemoteCloseErrorServer() + { + connect(this, SIGNAL(newConnection()), + this, SLOT(getConnection())); + } + +private slots: + void getConnection() + { + tst_QTcpSocket::exitLoop(); + } +}; + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::remoteCloseError() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; //proxy not useful for localhost test case + RemoteCloseErrorServer server; + QVERIFY(server.listen(QHostAddress::LocalHost)); + + QCoreApplication::instance()->processEvents(); + + QTcpSocket *clientSocket = newSocket(); + connect(clientSocket, SIGNAL(readyRead()), this, SLOT(exitLoopSlot())); + + clientSocket->connectToHost(server.serverAddress(), server.serverPort()); + + enterLoop(30); + QVERIFY(!timeout()); + + QVERIFY(server.hasPendingConnections()); + QTcpSocket *serverSocket = server.nextPendingConnection(); + connect(clientSocket, SIGNAL(disconnected()), this, SLOT(exitLoopSlot())); + + serverSocket->write("Hello"); + + enterLoop(30); + QVERIFY(!timeout()); + + QCOMPARE(clientSocket->bytesAvailable(), qint64(5)); + + qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError"); + QSignalSpy errorSpy(clientSocket, SIGNAL(error(QAbstractSocket::SocketError))); + QSignalSpy disconnectedSpy(clientSocket, SIGNAL(disconnected())); + + clientSocket->write("World"); + serverSocket->disconnectFromHost(); + + tmpSocket = clientSocket; + connect(clientSocket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(remoteCloseErrorSlot())); + + enterLoop(30); + QVERIFY(!timeout()); + + QCOMPARE(disconnectedSpy.count(), 1); + QCOMPARE(errorSpy.count(), 1); + QCOMPARE(clientSocket->error(), QAbstractSocket::RemoteHostClosedError); + + delete serverSocket; + + clientSocket->connectToHost(server.serverAddress(), server.serverPort()); + + enterLoop(30); + QVERIFY(!timeout()); + + QVERIFY(server.hasPendingConnections()); + serverSocket = server.nextPendingConnection(); + serverSocket->disconnectFromHost(); + + enterLoop(30); + QVERIFY(!timeout()); + + QCOMPARE(clientSocket->state(), QAbstractSocket::UnconnectedState); + + delete clientSocket; +} + +void tst_QTcpSocket::remoteCloseErrorSlot() +{ + QCOMPARE(tmpSocket->state(), QAbstractSocket::ConnectedState); + static_cast<QTcpSocket *>(sender())->close(); +} + +void tst_QTcpSocket::messageBoxSlot() +{ +#if !defined(Q_OS_VXWORKS) // no gui + QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender()); + socket->deleteLater(); + QMessageBox box; + QTimer::singleShot(100, &box, SLOT(close())); + + // This should not delete the socket + box.exec(); + + // Fire a non-0 singleshot to leave time for the delete + QTimer::singleShot(250, this, SLOT(exitLoopSlot())); +#endif +} +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::openMessageBoxInErrorSlot() +{ +#if defined(Q_OS_VXWORKS) // no gui + QSKIP("no default gui available on VxWorks", SkipAll); +#else + QTcpSocket *socket = newSocket(); + QPointer<QTcpSocket> p(socket); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(messageBoxSlot())); + + socket->connectToHost("hostnotfoundhostnotfound.troll.no", 9999); // Host not found, fyi + enterLoop(30); + QVERIFY(!p); +#endif +} + +//---------------------------------------------------------------------------------- +#ifndef Q_OS_WIN +void tst_QTcpSocket::connectToLocalHostNoService() +{ + // This test was created after we received a report that claimed + // QTcpSocket would crash if trying to connect to "localhost" on a random + // port with no service listening. + QTcpSocket *socket = newSocket(); + socket->connectToHost("localhost", 31415); // no service running here, one suspects + + while(socket->state() == QTcpSocket::HostLookupState || socket->state() == QTcpSocket::ConnectingState) { + QTest::qWait(100); + } + QCOMPARE(socket->state(), QTcpSocket::UnconnectedState); + delete socket; +} +#endif + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::waitForConnectedInHostLookupSlot() +{ + // This test tries to reproduce the problem where waitForConnected() is + // called at a point where the host lookup is already done. QTcpSocket + // will try to abort the "pending lookup", but since it's already done and + // the queued signal is already underway, we will receive the signal after + // waitForConnected() has returned, and control goes back to the event + // loop. When the signal has been received, the connection is torn down, + // then reopened. Yikes. If we reproduce this by calling + // waitForConnected() inside hostLookupSlot(), it will even crash. + tmpSocket = newSocket(); + QEventLoop loop; + connect(tmpSocket, SIGNAL(connected()), &loop, SLOT(quit())); + QTimer timer; + connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + QSignalSpy timerSpy(&timer, SIGNAL(timeout())); + timer.start(15000); + + connect(tmpSocket, SIGNAL(hostFound()), this, SLOT(hostLookupSlot())); + tmpSocket->connectToHost(QtNetworkSettings::serverName(), 143); + + // only execute the loop if not already connected + if (tmpSocket->state() != QAbstractSocket::ConnectedState) + loop.exec(); + + QCOMPARE(timerSpy.count(), 0); + + delete tmpSocket; +} + +void tst_QTcpSocket::hostLookupSlot() +{ + // This will fail to cancel the pending signal + QVERIFY(tmpSocket->waitForConnected(10000)); +} + +class Foo : public QObject +{ + Q_OBJECT + QTcpSocket *sock; +public: + bool attemptedToConnect; + bool networkTimeout; + int count; + + inline Foo(QObject *parent = 0) : QObject(parent) + { + attemptedToConnect = false; + networkTimeout = false; + count = 0; +#ifndef QT_NO_OPENSSL + QFETCH_GLOBAL(bool, ssl); + if (ssl) + sock = new QSslSocket; + else +#endif + sock = new QTcpSocket; + connect(sock, SIGNAL(connected()), this, SLOT(connectedToIt())); + connect(sock, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)), + SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*))); + } + + inline ~Foo() + { + delete sock; + } + +public slots: + inline void connectedToIt() + { count++; } + + inline void doIt() + { + attemptedToConnect = true; + sock->connectToHost(QtNetworkSettings::serverName(), 80); + +#ifdef Q_OS_MAC + pthread_yield_np(); +#elif defined Q_OS_LINUX + pthread_yield(); +#endif + if (!sock->waitForConnected()) { + networkTimeout = true; + } + tst_QTcpSocket::exitLoop(); + } + + inline void exitLoop() + { + tst_QTcpSocket::exitLoop(); + } + + inline void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *auth) + { + auth->setUser("qsockstest"); + auth->setPassword("password"); + } +}; + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::waitForConnectedInHostLookupSlot2() +{ +#if defined(Q_OS_WIN) || defined(Q_OS_VXWORKS) + QSKIP("waitForConnectedInHostLookupSlot2 is not run on Windows and VxWorks", SkipAll); +#else + + Foo foo; + QPushButton top("Go", 0); + top.show(); + connect(&top, SIGNAL(clicked()), &foo, SLOT(doIt())); + + QTimer::singleShot(100, &top, SLOT(animateClick())); + QTimer::singleShot(5000, &foo, SLOT(exitLoop())); + + enterLoop(30); + if (timeout() || foo.networkTimeout) + QFAIL("Network timeout"); + + QVERIFY(foo.attemptedToConnect); + QCOMPARE(foo.count, 1); +#endif +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::readyReadSignalsAfterWaitForReadyRead() +{ + QTcpSocket *socket = newSocket(); + + QSignalSpy readyReadSpy(socket, SIGNAL(readyRead())); + + // Connect + socket->connectToHost(QtNetworkSettings::serverName(), 143); + + // Wait for the read + QVERIFY(socket->waitForReadyRead(10000)); + + QCOMPARE(readyReadSpy.count(), 1); + + QString s = socket->readLine(); + QVERIFY2(QtNetworkSettings::compareReplyIMAP(s.toLatin1()), s.toLatin1().constData()); + QCOMPARE(socket->bytesAvailable(), qint64(0)); + + QCoreApplication::instance()->processEvents(); + QCOMPARE(socket->bytesAvailable(), qint64(0)); + QCOMPARE(readyReadSpy.count(), 1); + + delete socket; +} + +class TestThread2 : public QThread +{ + Q_OBJECT +public: + void run() + { + QFile fileWriter("fifo"); + QVERIFY(fileWriter.open(QFile::WriteOnly)); + QCOMPARE(fileWriter.write(QByteArray(32, '@')), qint64(32)); + QCOMPARE(fileWriter.write(QByteArray(32, '@')), qint64(32)); + QCOMPARE(fileWriter.write(QByteArray(32, '@')), qint64(32)); + QCOMPARE(fileWriter.write(QByteArray(32, '@')), qint64(32)); + } +}; + +//---------------------------------------------------------------------------------- +#ifdef Q_OS_LINUX +void tst_QTcpSocket::linuxKernelBugLocalSocket() +{ + QFile::remove("fifo"); + mkfifo("fifo", 0666); + + TestThread2 test; + test.start(); + + QFile fileReader("fifo"); + QVERIFY(fileReader.open(QFile::ReadOnly)); + + test.wait(); + + QTcpSocket *socket = newSocket(); + socket->setSocketDescriptor(fileReader.handle()); + QVERIFY(socket->waitForReadyRead(5000)); + QCOMPARE(socket->bytesAvailable(), qint64(128)); + + QFile::remove("fifo"); + + delete socket; +} +#endif + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::abortiveClose() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; //proxy not useful for localhost test case + QTcpServer server; + QVERIFY(server.listen(QHostAddress::LocalHost)); + connect(&server, SIGNAL(newConnection()), this, SLOT(exitLoopSlot())); + + QTcpSocket *clientSocket = newSocket(); + clientSocket->connectToHost(server.serverAddress(), server.serverPort()); + + enterLoop(10); + QVERIFY(server.hasPendingConnections()); + + QVERIFY(tmpSocket = server.nextPendingConnection()); + + qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError"); + QSignalSpy readyReadSpy(clientSocket, SIGNAL(readyRead())); + QSignalSpy errorSpy(clientSocket, SIGNAL(error(QAbstractSocket::SocketError))); + + connect(clientSocket, SIGNAL(disconnected()), this, SLOT(exitLoopSlot())); + QTimer::singleShot(0, this, SLOT(abortiveClose_abortSlot())); + + enterLoop(5); + + QCOMPARE(readyReadSpy.count(), 0); + QCOMPARE(errorSpy.count(), 1); + + QCOMPARE(*static_cast<const int *>(errorSpy.at(0).at(0).constData()), + int(QAbstractSocket::RemoteHostClosedError)); + + delete clientSocket; +} + +void tst_QTcpSocket::abortiveClose_abortSlot() +{ + tmpSocket->abort(); +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::localAddressEmptyOnBSD() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; //proxy not useful for localhost test case + QTcpServer server; + QVERIFY(server.listen(QHostAddress::LocalHost)); + + QTcpSocket *tcpSocket = 0; + // we try 10 times, but note that this doesn't always provoke the bug + for (int i = 0; i < 10; ++i) { + delete tcpSocket; + tcpSocket = newSocket(); + tcpSocket->connectToHost(QHostAddress::LocalHost, server.serverPort()); + if (!tcpSocket->waitForConnected(0)) { + // to provoke the bug, we need a local socket that connects immediately + // --i; + tcpSocket->abort(); + if (tcpSocket->state() != QTcpSocket::UnconnectedState) + QVERIFY(tcpSocket->waitForDisconnected(-1)); + continue; + } + QCOMPARE(tcpSocket->localAddress(), QHostAddress(QHostAddress::LocalHost)); + } + delete tcpSocket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::zeroAndMinusOneReturns() +{ + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 80); + socket->write("GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n"); + QVERIFY(socket->waitForReadyRead(15000)); + + char c[16]; + QVERIFY(socket->getChar(c)); + QCOMPARE(socket->read(c, 16), qint64(16)); + QVERIFY(socket->readLine(c, 16) > 0); + QVERIFY(!socket->readAll().isEmpty()); + + // the last operation emptied the read buffer + // all read operations from this point on should fail + // with return 0 because the socket is still open + QVERIFY(socket->readAll().isEmpty()); + QCOMPARE(socket->read(c, 16), qint64(0)); + QCOMPARE(socket->readLine(c, 16), qint64(0)); + QVERIFY(!socket->getChar(c)); + + socket->write("GET / HTTP/1.0\r\n\r\n"); + QVERIFY(socket->waitForDisconnected(15000)); + QCOMPARE(socket->error(), QAbstractSocket::RemoteHostClosedError); + + QCOMPARE(socket->write("BLUBBER"), qint64(-1)); + QVERIFY(socket->getChar(c)); + QCOMPARE(socket->read(c, 16), qint64(16)); + QVERIFY(socket->readLine(c, 16) > 0); + QVERIFY(!socket->readAll().isEmpty()); + + // the last operation emptied the read buffer + // all read operations from this point on should fail + // with return -1 because the socket is not connected + QVERIFY(socket->readAll().isEmpty()); + QCOMPARE(socket->read(c, 16), qint64(-1)); + QCOMPARE(socket->readLine(c, 16), qint64(-1)); + QVERIFY(!socket->getChar(c)); + QVERIFY(!socket->putChar('a')); + + socket->close(); + + // now the QIODevice is closed, which means getChar complains + QCOMPARE(socket->write("BLUBBER"), qint64(-1)); + QCOMPARE(socket->read(c, 16), qint64(-1)); + QCOMPARE(socket->readLine(c, 16), qint64(-1)); + QVERIFY(!socket->getChar(c)); + QVERIFY(!socket->putChar('a')); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::connectionRefused() +{ + qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError"); + qRegisterMetaType<QAbstractSocket::SocketState>("QAbstractSocket::SocketState"); + + QTcpSocket *socket = newSocket(); + QSignalSpy stateSpy(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState))); + QSignalSpy errorSpy(socket, SIGNAL(error(QAbstractSocket::SocketError))); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), + &QTestEventLoop::instance(), SLOT(exitLoop())); + + socket->connectToHost(QtNetworkSettings::serverName(), 144); + + enterLoop(10); + disconnect(socket, SIGNAL(error(QAbstractSocket::SocketError)), + &QTestEventLoop::instance(), SLOT(exitLoop())); + QVERIFY2(!timeout(), "Network timeout"); + + QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState); + QCOMPARE(socket->error(), QAbstractSocket::ConnectionRefusedError); + + QCOMPARE(stateSpy.count(), 3); + QCOMPARE(qVariantValue<QAbstractSocket::SocketState>(stateSpy.at(0).at(0)), QAbstractSocket::HostLookupState); + QCOMPARE(qVariantValue<QAbstractSocket::SocketState>(stateSpy.at(1).at(0)), QAbstractSocket::ConnectingState); + QCOMPARE(qVariantValue<QAbstractSocket::SocketState>(stateSpy.at(2).at(0)), QAbstractSocket::UnconnectedState); + QCOMPARE(errorSpy.count(), 1); + + delete socket; +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::suddenRemoteDisconnect_data() +{ + QTest::addColumn<QString>("client"); + QTest::addColumn<QString>("server"); + + QTest::newRow("Qt4 Client <-> Qt4 Server") << QString::fromLatin1("qt4client") << QString::fromLatin1("qt4server"); +} + +void tst_QTcpSocket::suddenRemoteDisconnect() +{ +#if defined( Q_OS_SYMBIAN ) + QSKIP("Symbian: QProcess IO is not yet supported, fix when supported", SkipAll); +#else + QFETCH(QString, client); + QFETCH(QString, server); + + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + QFETCH_GLOBAL(bool, ssl); + if (ssl) + return; + + // Start server + QProcess serverProcess; + serverProcess.setReadChannel(QProcess::StandardError); + serverProcess.start(QString::fromLatin1("stressTest/stressTest %1").arg(server), + QIODevice::ReadWrite | QIODevice::Text); + while (!serverProcess.canReadLine()) + QVERIFY(serverProcess.waitForReadyRead(10000)); + QCOMPARE(serverProcess.readLine().data(), (server.toLatin1() + "\n").data()); + + // Start client + QProcess clientProcess; + clientProcess.setReadChannel(QProcess::StandardError); + clientProcess.start(QString::fromLatin1("stressTest/stressTest %1").arg(client), + QIODevice::ReadWrite | QIODevice::Text); + while (!clientProcess.canReadLine()) + QVERIFY(clientProcess.waitForReadyRead(10000)); + QCOMPARE(clientProcess.readLine().data(), (client.toLatin1() + "\n").data()); + + // Let them play for a while + qDebug("Running stress test for 5 seconds"); + QEventLoop loop; + connect(&serverProcess, SIGNAL(finished(int)), &loop, SLOT(quit())); + connect(&clientProcess, SIGNAL(finished(int)), &loop, SLOT(quit())); + QTime stopWatch; + stopWatch.start(); + QTimer::singleShot(20000, &loop, SLOT(quit())); + + while ((serverProcess.state() == QProcess::Running + || clientProcess.state() == QProcess::Running) && stopWatch.elapsed() < 20000) + loop.exec(); + + QVERIFY(stopWatch.elapsed() < 20000); + + // Check that both exited normally. + QCOMPARE(clientProcess.readAll().constData(), "SUCCESS\n"); + QCOMPARE(serverProcess.readAll().constData(), "SUCCESS\n"); +#endif +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::connectToMultiIP() +{ + QSKIP("TODO: setup DNS in the new network", SkipAll); + +#if defined(Q_OS_VXWORKS) + QSKIP("VxSim in standard config doesn't even run a DNS resolver", SkipAll); +#else + QFETCH_GLOBAL(bool, ssl); + if (ssl) + return; + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + QSKIP("This test takes too long if we also add the proxies.", SkipSingle); + + qDebug("Please wait, this test can take a while..."); + + QTcpSocket *socket = newSocket(); + // rationale: this domain resolves to 3 A-records, 2 of them are + // invalid. QTcpSocket should never spend more than 30 seconds per IP, and + // 30s*2 = 60s. + QTime stopWatch; + stopWatch.start(); + socket->connectToHost("multi.dev.troll.no", 80); + QVERIFY(socket->waitForConnected(60500)); + QVERIFY(stopWatch.elapsed() < 70000); + socket->abort(); + + stopWatch.restart(); + socket->connectToHost("multi.dev.troll.no", 81); + QVERIFY(!socket->waitForConnected(2000)); + QVERIFY(stopWatch.elapsed() < 2000); + QCOMPARE(socket->error(), QAbstractSocket::SocketTimeoutError); + + delete socket; +#endif +} + +//---------------------------------------------------------------------------------- +void tst_QTcpSocket::moveToThread0() +{ + QFETCH_GLOBAL(int, proxyType); + if (proxyType & AuthMask) + return; + + { + // Case 1: Moved after connecting, before waiting for connection. + QTcpSocket *socket = newSocket();; + socket->connectToHost(QtNetworkSettings::serverName(), 143); + socket->moveToThread(0); + QVERIFY(socket->waitForConnected(5000)); + socket->write("XXX LOGOUT\r\n"); + QVERIFY(socket->waitForBytesWritten(5000)); + QVERIFY(socket->waitForDisconnected()); + delete socket; + } + { + // Case 2: Moved before connecting + QTcpSocket *socket = newSocket(); + socket->moveToThread(0); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(5000)); + socket->write("XXX LOGOUT\r\n"); + QVERIFY(socket->waitForBytesWritten(5000)); + QVERIFY(socket->waitForDisconnected()); + delete socket; + } + { + // Case 3: Moved after writing, while waiting for bytes to be written. + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(5000)); + socket->write("XXX LOGOUT\r\n"); + socket->moveToThread(0); + QVERIFY(socket->waitForBytesWritten(5000)); + QVERIFY(socket->waitForDisconnected()); + delete socket; + } + { + // Case 4: Moved after writing, while waiting for response. + QTcpSocket *socket = newSocket(); + socket->connectToHost(QtNetworkSettings::serverName(), 143); + QVERIFY(socket->waitForConnected(5000)); + socket->write("XXX LOGOUT\r\n"); + QVERIFY(socket->waitForBytesWritten(5000)); + socket->moveToThread(0); + QVERIFY(socket->waitForDisconnected()); + delete socket; + } +} + +void tst_QTcpSocket::increaseReadBufferSize() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; //proxy not useful for localhost test case + QTcpServer server; + QTcpSocket *active = newSocket(); + connect(active, SIGNAL(readyRead()), SLOT(exitLoopSlot())); + + // connect two sockets to each other: + QVERIFY(server.listen(QHostAddress::LocalHost)); + active->connectToHost("127.0.0.1", server.serverPort()); + QVERIFY(active->waitForConnected(5000)); + QVERIFY(server.waitForNewConnection(5000)); + + QTcpSocket *passive = server.nextPendingConnection(); + QVERIFY(passive); + + // now write 512 bytes of data on one end + QByteArray data(512, 'a'); + passive->write(data); + QVERIFY2(passive->waitForBytesWritten(5000), "Network timeout"); + + // set the read buffer size to less than what was written and iterate: + active->setReadBufferSize(256); + enterLoop(10); + QVERIFY2(!timeout(), "Network timeout"); + QCOMPARE(active->bytesAvailable(), active->readBufferSize()); + + // increase the buffer size and iterate again: + active->setReadBufferSize(384); + enterLoop(10); + QVERIFY2(!timeout(), "Network timeout"); + QCOMPARE(active->bytesAvailable(), active->readBufferSize()); + + // once more, but now it should read everything there was to read + active->setReadBufferSize(1024); + enterLoop(10); + QVERIFY2(!timeout(), "Network timeout"); + QCOMPARE(active->bytesAvailable(), qint64(data.size())); + + // drain it and compare + QCOMPARE(active->readAll(), data); + + // now one more test by setting the buffer size to unlimited: + passive->write(data); + QVERIFY2(passive->waitForBytesWritten(5000), "Network timeout"); + active->setReadBufferSize(256); + enterLoop(10); + QVERIFY2(!timeout(), "Network timeout"); + QCOMPARE(active->bytesAvailable(), active->readBufferSize()); + active->setReadBufferSize(0); + enterLoop(10); + QVERIFY2(!timeout(), "Network timeout"); + QCOMPARE(active->bytesAvailable(), qint64(data.size())); + QCOMPARE(active->readAll(), data); + + delete active; +} + +void tst_QTcpSocket::taskQtBug5799ConnectionErrorWaitForConnected() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + // check that we get a proper error connecting to port 12346 + // use waitForConnected, e.g. this should use a synchronous select() on the OS level + + QTcpSocket socket; + socket.connectToHost(QtNetworkSettings::serverName(), 12346); + QTime timer; + timer.start(); + socket.waitForConnected(10000); + QVERIFY2(timer.elapsed() < 9900, "Connection to closed port timed out instead of refusing, something is wrong"); + QVERIFY2(socket.state() == QAbstractSocket::UnconnectedState, "Socket connected unexpectedly!"); + QVERIFY2(socket.error() == QAbstractSocket::ConnectionRefusedError, + QString("Could not reach server: %1").arg(socket.errorString()).toLocal8Bit()); +} + +void tst_QTcpSocket::taskQtBug5799ConnectionErrorEventLoop() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + // check that we get a proper error connecting to port 12346 + // This testcase uses an event loop + QTcpSocket socket; + connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), &QTestEventLoop::instance(), SLOT(exitLoop())); + socket.connectToHost(QtNetworkSettings::serverName(), 12346); + + QTestEventLoop::instance().enterLoop(10); + QVERIFY2(!QTestEventLoop::instance().timeout(), "Connection to closed port timed out instead of refusing, something is wrong"); + QVERIFY2(socket.state() == QAbstractSocket::UnconnectedState, "Socket connected unexpectedly!"); + QVERIFY2(socket.error() == QAbstractSocket::ConnectionRefusedError, + QString("Could not reach server: %1").arg(socket.errorString()).toLocal8Bit()); +} + +void tst_QTcpSocket::taskQtBug7054TimeoutErrorResetting() +{ + QTcpSocket *socket = newSocket(); + + socket->connectToHost(QtNetworkSettings::serverName(), 443); + QVERIFY(socket->waitForConnected(5*1000)); + QVERIFY(socket->error() == QAbstractSocket::UnknownSocketError); + + // We connected to the HTTPS port. Wait two seconds to receive data. We will receive + // nothing because we would need to start the SSL handshake + QVERIFY(!socket->waitForReadyRead(2*1000)); + QVERIFY(socket->error() == QAbstractSocket::SocketTimeoutError); + + // Now write some crap to make the server disconnect us. 4 lines are enough. + socket->write("a\r\nb\r\nc\r\nd\r\n"); + socket->waitForBytesWritten(2*1000); + + // we try to waitForReadyRead another time, but this time instead of a timeout we + // should get a better error since the server disconnected us + QVERIFY(!socket->waitForReadyRead(2*1000)); + // It must NOT be the SocketTimeoutError that had been set before + QVERIFY(socket->error() == QAbstractSocket::RemoteHostClosedError); +} + +void tst_QTcpSocket::invalidProxy_data() +{ + QTest::addColumn<int>("type"); + QTest::addColumn<QString>("host"); + QTest::addColumn<int>("port"); + QTest::addColumn<bool>("failsAtConnect"); + QTest::addColumn<int>("expectedError"); + + QString fluke = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first().toString(); + QTest::newRow("ftp-proxy") << int(QNetworkProxy::FtpCachingProxy) << fluke << 21 << true + << int(QAbstractSocket::UnsupportedSocketOperationError); + QTest::newRow("http-caching-proxy") << int(QNetworkProxy::HttpCachingProxy) << fluke << 3128 << true + << int(QAbstractSocket::UnsupportedSocketOperationError); + QTest::newRow("no-such-host-socks5") << int(QNetworkProxy::Socks5Proxy) + << "this-host-will-never-exist.troll.no" << 1080 << false + << int(QAbstractSocket::ProxyNotFoundError); + QTest::newRow("no-such-host-http") << int(QNetworkProxy::HttpProxy) + << "this-host-will-never-exist.troll.no" << 3128 << false + << int(QAbstractSocket::ProxyNotFoundError); +#if !defined(Q_OS_SYMBIAN) + QTest::newRow("http-on-socks5") << int(QNetworkProxy::HttpProxy) << fluke << 1080 << false + << int(QAbstractSocket::ProxyConnectionClosedError); + QTest::newRow("socks5-on-http") << int(QNetworkProxy::Socks5Proxy) << fluke << 3128 << false + << int(QAbstractSocket::SocketTimeoutError); +#endif +} + +void tst_QTcpSocket::invalidProxy() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QFETCH(int, type); + QFETCH(QString, host); + QFETCH(int, port); + QFETCH(bool, failsAtConnect); + QNetworkProxy::ProxyType proxyType = QNetworkProxy::ProxyType(type); + QNetworkProxy proxy(proxyType, host, port); + + QTcpSocket *socket = newSocket(); + socket->setProxy(proxy); + socket->connectToHost(QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first().toString(), 80); + + if (failsAtConnect) { + QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState); + } else { + QCOMPARE(socket->state(), QAbstractSocket::ConnectingState); + QVERIFY(!socket->waitForConnected(5000)); + } + QVERIFY(!socket->errorString().isEmpty()); + + // note: the following test is not a hard failure. + // Sometimes, error codes change for the better + QTEST(int(socket->error()), "expectedError"); + + delete socket; +} + +// copied from tst_qnetworkreply.cpp +class MyProxyFactory: public QNetworkProxyFactory +{ +public: + int callCount; + QList<QNetworkProxy> toReturn; + QNetworkProxyQuery lastQuery; + inline MyProxyFactory() { clear(); } + + inline void clear() + { + callCount = 0; + toReturn = QList<QNetworkProxy>() << QNetworkProxy::DefaultProxy; + lastQuery = QNetworkProxyQuery(); + } + + virtual QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query) + { + lastQuery = query; + ++callCount; + return toReturn; + } +}; + +void tst_QTcpSocket::proxyFactory_data() +{ + QTest::addColumn<QList<QNetworkProxy> >("proxyList"); + QTest::addColumn<QNetworkProxy>("proxyUsed"); + QTest::addColumn<bool>("failsAtConnect"); + QTest::addColumn<int>("expectedError"); + + QList<QNetworkProxy> proxyList; + + // tests that do connect + + proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::serverName(), 3129); + QTest::newRow("http") + << proxyList << proxyList.at(0) + << false << int(QAbstractSocket::UnknownSocketError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081); + QTest::newRow("socks5") + << proxyList << proxyList.at(0) + << false << int(QAbstractSocket::UnknownSocketError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129) + << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081); + QTest::newRow("cachinghttp+socks5") + << proxyList << proxyList.at(1) + << false << int(QAbstractSocket::UnknownSocketError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121) + << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129) + << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1081); + QTest::newRow("ftp+cachinghttp+socks5") + << proxyList << proxyList.at(2) + << false << int(QAbstractSocket::UnknownSocketError); + + // tests that fail to connect + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129); + QTest::newRow("cachinghttp") + << proxyList << QNetworkProxy() + << true << int(QAbstractSocket::UnsupportedSocketOperationError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121); + QTest::newRow("ftp") + << proxyList << QNetworkProxy() + << true << int(QAbstractSocket::UnsupportedSocketOperationError); + + proxyList.clear(); + proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::serverName(), 2121) + << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::serverName(), 3129); + QTest::newRow("ftp+cachinghttp") + << proxyList << QNetworkProxy() + << true << int(QAbstractSocket::UnsupportedSocketOperationError); +} + +void tst_QTcpSocket::proxyFactory() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + QFETCH(QList<QNetworkProxy>, proxyList); + QFETCH(QNetworkProxy, proxyUsed); + QFETCH(bool, failsAtConnect); + + MyProxyFactory *factory = new MyProxyFactory; + factory->toReturn = proxyList; + QNetworkProxyFactory::setApplicationProxyFactory(factory); + + QTcpSocket *socket = newSocket(); + QString host = QtNetworkSettings::serverName(); + socket->connectToHost(host, 80); + + // Verify that the factory was called properly + QCOMPARE(factory->callCount, 1); + QCOMPARE(factory->lastQuery, QNetworkProxyQuery(host, 80)); + + if (failsAtConnect) { + QCOMPARE(socket->state(), QAbstractSocket::UnconnectedState); + } else { + QCOMPARE(socket->state(), QAbstractSocket::ConnectingState); + QVERIFY(socket->waitForConnected(5000)); + QCOMPARE(proxyAuthCalled, 1); + } + QVERIFY(!socket->errorString().isEmpty()); + + // note: the following test is not a hard failure. + // Sometimes, error codes change for the better + QTEST(int(socket->error()), "expectedError"); + + delete socket; +} + +// there is a similar test inside tst_qtcpserver that uses the event loop instead +void tst_QTcpSocket::qtbug14268_peek() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + SocketPair socketPair; + QVERIFY(socketPair.create()); + QTcpSocket *outgoing = socketPair.endPoints[0]; + QTcpSocket *incoming = socketPair.endPoints[1]; + + QVERIFY(incoming->state() == QTcpSocket::ConnectedState); + QVERIFY(outgoing->state() == QTcpSocket::ConnectedState); + + outgoing->write("abc\n"); + QVERIFY(outgoing->waitForBytesWritten(2000)); + QVERIFY(incoming->waitForReadyRead(2000)); + QVERIFY(incoming->peek(128*1024) == QByteArray("abc\n")); + + outgoing->write("def\n"); + QVERIFY(outgoing->waitForBytesWritten(2000)); + QVERIFY(incoming->waitForReadyRead(2000)); + QVERIFY(incoming->peek(128*1024) == QByteArray("abc\ndef\n")); + + outgoing->write("ghi\n"); + QVERIFY(outgoing->waitForBytesWritten(2000)); + QVERIFY(incoming->waitForReadyRead(2000)); + QVERIFY(incoming->peek(128*1024) == QByteArray("abc\ndef\nghi\n")); + + QVERIFY(incoming->read(128*1024) == QByteArray("abc\ndef\nghi\n")); +} + + + +QTEST_MAIN(tst_QTcpSocket) +#include "tst_qtcpsocket.moc" diff --git a/tests/auto/network/socket/qudpsocket/.gitignore b/tests/auto/network/socket/qudpsocket/.gitignore new file mode 100644 index 0000000000..c6134126a0 --- /dev/null +++ b/tests/auto/network/socket/qudpsocket/.gitignore @@ -0,0 +1,2 @@ +tst_qudpsocket +clientserver/clientserver diff --git a/tests/auto/network/socket/qudpsocket/clientserver/clientserver.pro b/tests/auto/network/socket/qudpsocket/clientserver/clientserver.pro new file mode 100644 index 0000000000..6da148659c --- /dev/null +++ b/tests/auto/network/socket/qudpsocket/clientserver/clientserver.pro @@ -0,0 +1,8 @@ +QT = core network +SOURCES += main.cpp +CONFIG += console +CONFIG -= app_bundle +TARGET = clientserver +DESTDIR = ./ + +symbian: TARGET.CAPABILITY += NetworkServices diff --git a/tests/auto/network/socket/qudpsocket/clientserver/main.cpp b/tests/auto/network/socket/qudpsocket/clientserver/main.cpp new file mode 100644 index 0000000000..9145a5199f --- /dev/null +++ b/tests/auto/network/socket/qudpsocket/clientserver/main.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** 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 <QtNetwork> + +class ClientServer : public QUdpSocket +{ + Q_OBJECT +public: + enum Type { + ConnectedClient, + UnconnectedClient, + Server + }; + + ClientServer(Type type, const QString &host, quint16 port) + : type(type) + { + switch (type) { + case Server: + if (bind(0, ShareAddress | ReuseAddressHint)) { + printf("%d\n", localPort()); + } else { + printf("XXX\n"); + } + break; + case ConnectedClient: + connectToHost(host, port); + startTimer(250); + printf("ok\n"); + break; + case UnconnectedClient: + peerAddress = host; + peerPort = port; + if (bind(QHostAddress::Any, port + 1, ShareAddress | ReuseAddressHint)) { + startTimer(250); + printf("ok\n"); + } else { + printf("XXX\n"); + } + break; + } + fflush(stdout); + + connect(this, SIGNAL(readyRead()), this, SLOT(readData())); + } + +protected: + void timerEvent(QTimerEvent *event) + { + static int n = 0; + switch (type) { + case ConnectedClient: + write(QByteArray::number(n++)); + break; + case UnconnectedClient: + writeDatagram(QByteArray::number(n++), peerAddress, peerPort); + break; + default: + break; + } + + QUdpSocket::timerEvent(event); + } + +private slots: + void readData() + { + printf("readData()\n"); + switch (type) { + case ConnectedClient: { + while (bytesAvailable() || hasPendingDatagrams()) { + QByteArray data = readAll(); + printf("got %d\n", data.toInt()); + } + break; + } + case UnconnectedClient: { + while (hasPendingDatagrams()) { + QByteArray data; + data.resize(pendingDatagramSize()); + readDatagram(data.data(), data.size()); + printf("got %d\n", data.toInt()); + } + break; + } + case Server: { + while (hasPendingDatagrams()) { + QHostAddress sender; + quint16 senderPort; + QByteArray data; + data.resize(pendingDatagramSize()); + readDatagram(data.data(), data.size(), &sender, &senderPort); + printf("got %d\n", data.toInt()); + printf("sending %d\n", data.toInt() * 2); + writeDatagram(QByteArray::number(data.toInt() * 2), sender, senderPort); + } + break; + } + } + fflush(stdout); + } + +private: + Type type; + QHostAddress peerAddress; + quint16 peerPort; +}; + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + ClientServer::Type type; + if (app.arguments().size() < 4) { + qDebug("usage: ./%s [ConnectedClient <server> <port>|UnconnectedClient <server> <port>|Server]", argv[0]); + return 1; + } + + QString arg = app.arguments().at(1).trimmed().toLower(); + if (arg == "connectedclient") + type = ClientServer::ConnectedClient; + else if (arg == "unconnectedclient") + type = ClientServer::UnconnectedClient; + else if (arg == "server") + type = ClientServer::Server; + + ClientServer clientServer(type, app.arguments().at(2), + app.arguments().at(3).toInt()); + + return app.exec(); +} + +#include "main.moc" diff --git a/tests/auto/network/socket/qudpsocket/qudpsocket.pro b/tests/auto/network/socket/qudpsocket/qudpsocket.pro new file mode 100644 index 0000000000..4ddb7178a4 --- /dev/null +++ b/tests/auto/network/socket/qudpsocket/qudpsocket.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +SUBDIRS = test clientserver + + diff --git a/tests/auto/network/socket/qudpsocket/test/test.pro b/tests/auto/network/socket/qudpsocket/test/test.pro new file mode 100644 index 0000000000..508dc97053 --- /dev/null +++ b/tests/auto/network/socket/qudpsocket/test/test.pro @@ -0,0 +1,28 @@ +load(qttest_p4) +SOURCES += ../tst_qudpsocket.cpp +QT = core network + +MOC_DIR=tmp + +win32 { + CONFIG(debug, debug|release) { + DESTDIR = ../debug +} else { + DESTDIR = ../release + } +} else { + DESTDIR = ../ +} + +wince*|symbian: { + addApp.files = ../clientserver/clientserver.exe + addApp.path = clientserver + DEPLOYMENT += addApp +} + +TARGET = tst_qudpsocket + +symbian: TARGET.CAPABILITY += NetworkServices + + +CONFIG+=insignificant_test 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" diff --git a/tests/auto/network/socket/qudpsocket/udpServer/main.cpp b/tests/auto/network/socket/qudpsocket/udpServer/main.cpp new file mode 100644 index 0000000000..ff3ca111d0 --- /dev/null +++ b/tests/auto/network/socket/qudpsocket/udpServer/main.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** 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 <QtNetwork> + +class Server : public QObject +{ + Q_OBJECT +public: + Server(int port) + { + connect(&serverSocket, SIGNAL(readyRead()), + this, SLOT(sendEcho())); + if (serverSocket.bind(QHostAddress::Any, port, + QUdpSocket::ReuseAddressHint + | QUdpSocket::ShareAddress)) { + printf("OK\n"); + } else { + printf("FAILED\n"); + } + fflush(stdout); + } + +private slots: + void sendEcho() + { + QHostAddress senderAddress; + quint16 senderPort; + + char data[1024]; + qint64 bytes = serverSocket.readDatagram(data, sizeof(data), &senderAddress, &senderPort); + if (bytes == 1 && data[0] == '\0') + QCoreApplication::instance()->quit(); + + for (int i = 0; i < bytes; ++i) + data[i] += 1; + serverSocket.writeDatagram(data, bytes, senderAddress, senderPort); + } + +private: + QUdpSocket serverSocket; +}; + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + Server server(app.arguments().at(1).toInt()); + + return app.exec(); +} + +#include "main.moc" diff --git a/tests/auto/network/socket/qudpsocket/udpServer/udpServer.pro b/tests/auto/network/socket/qudpsocket/udpServer/udpServer.pro new file mode 100644 index 0000000000..7438d40fb0 --- /dev/null +++ b/tests/auto/network/socket/qudpsocket/udpServer/udpServer.pro @@ -0,0 +1,7 @@ +SOURCES += main.cpp +QT = core network +CONFIG -= app_bundle +CONFIG += console + +symbian:TARGET.CAPABILITY="ALL -TCB" + diff --git a/tests/auto/network/socket/socket.pro b/tests/auto/network/socket/socket.pro new file mode 100644 index 0000000000..49fb52d1a5 --- /dev/null +++ b/tests/auto/network/socket/socket.pro @@ -0,0 +1,17 @@ +TEMPLATE=subdirs +SUBDIRS=\ + qhttpsocketengine \ + qudpsocket \ + qtcpsocket \ + #qlocalsocket \ # FIXME: uses qtscript (QTBUG-19242) + qtcpserver \ + qsocks5socketengine \ + qabstractsocket \ + platformsocketengine \ + +!contains(QT_CONFIG, private_tests): SUBDIRS -= \ + platformsocketengine \ + qhttpsocketengine \ + qsocks5socketengine \ + + |