diff options
Diffstat (limited to 'tests/manual/network_remote_stresstest/tst_network_remote_stresstest.cpp')
-rw-r--r-- | tests/manual/network_remote_stresstest/tst_network_remote_stresstest.cpp | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/tests/manual/network_remote_stresstest/tst_network_remote_stresstest.cpp b/tests/manual/network_remote_stresstest/tst_network_remote_stresstest.cpp new file mode 100644 index 0000000000..06ebf444f7 --- /dev/null +++ b/tests/manual/network_remote_stresstest/tst_network_remote_stresstest.cpp @@ -0,0 +1,499 @@ +/**************************************************************************** +** +** 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtCore/QThread> +#include <QtCore/QSemaphore> +#include <QtCore/QElapsedTimer> +#include <QtCore/QSharedPointer> +#include <QtCore/QVector> +#include <QtNetwork/QTcpSocket> +#include <QtNetwork/QSslSocket> +#include <QtNetwork/QNetworkReply> +#include <QtNetwork/QNetworkAccessManager> + +#ifdef QT_BUILD_INTERNAL +# include <private/qnetworkaccessmanager_p.h> +#endif + +#include <qplatformdefs.h> +#ifdef Q_OS_UNIX +# include <sys/types.h> +# include <sys/socket.h> +# include <sys/select.h> +# include <netinet/in.h> +# include <errno.h> +# include <netdb.h> +# include <signal.h> +# include <unistd.h> +# include <fcntl.h> + +typedef int SOCKET; +# define INVALID_SOCKET -1 +# define SOCKET_ERROR -1 + +#elif defined(Q_OS_WIN) +# include <winsock2.h> +#endif + +Q_DECLARE_METATYPE(QVector<QUrl>) + +class tst_NetworkRemoteStressTest : public QObject +{ + Q_OBJECT +public: + enum { AttemptCount = 100 }; + tst_NetworkRemoteStressTest(); + + qint64 byteCounter; + QNetworkAccessManager manager; + QVector<QUrl> httpUrls, httpsUrls, mixedUrls; + bool intermediateDebug; + +private: + void clearManager(); + +public slots: + void initTestCase_data(); + void init(); + + void slotReadAll() { byteCounter += static_cast<QIODevice *>(sender())->readAll().size(); } + +private Q_SLOTS: + void blockingSequentialRemoteHosts(); + void sequentialRemoteHosts(); + void parallelRemoteHosts_data(); + void parallelRemoteHosts(); + void namRemoteGet_data(); + void namRemoteGet(); +}; + +tst_NetworkRemoteStressTest::tst_NetworkRemoteStressTest() + : intermediateDebug(qgetenv("STRESSDEBUG").toInt() > 0) +{ +#ifdef Q_OS_WIN + WSAData wsadata; + + // IPv6 requires Winsock v2.0 or better. + WSAStartup(MAKEWORD(2,0), &wsadata); +#elif defined(Q_OS_UNIX) && !defined(Q_OS_SYMBIAN) + ::signal(SIGALRM, SIG_IGN); +#endif + + QFile urlList(":/url-list.txt"); + if (urlList.open(QIODevice::ReadOnly)) { + while (!urlList.atEnd()) { + QByteArray line = urlList.readLine().trimmed(); + QUrl url = QUrl::fromEncoded(line); + if (url.scheme() == "http") { + httpUrls << url; + mixedUrls << url; + } else if (url.scheme() == "https") { + httpsUrls << url; + mixedUrls << url; + } + } + } + + httpUrls << httpUrls; + httpsUrls << httpsUrls; +} + +void tst_NetworkRemoteStressTest::initTestCase_data() +{ + QTest::addColumn<QVector<QUrl> >("urlList"); + QTest::addColumn<bool>("useSslSocket"); + + QTest::newRow("no-ssl") << httpUrls << false; +// QTest::newRow("no-ssl-in-sslsocket") << httpUrls << true; + QTest::newRow("ssl") << httpsUrls << true; + QTest::newRow("mixed") << mixedUrls << false; +// QTest::newRow("mixed-in-sslsocket") << mixedUrls << true; +} + +void tst_NetworkRemoteStressTest::init() +{ + // clear the internal cache +#ifndef QT_BUILD_INTERNAL + if (strncmp(QTest::currentTestFunction(), "nam") == 0) + QSKIP("QNetworkAccessManager tests disabled", SkipAll); +#endif +} + +void tst_NetworkRemoteStressTest::clearManager() +{ +#ifdef QT_BUILD_INTERNAL + QNetworkAccessManagerPrivate::clearCache(&manager); + manager.setProxy(QNetworkProxy()); + manager.setCache(0); +#endif +} + +bool nativeLookup(const char *hostname, int port, QByteArray &buf) +{ +#if !defined(QT_NO_GETADDRINFO) && 0 + addrinfo *res = 0; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + + int result = getaddrinfo(QUrl::toAce(hostname).constData(), QByteArray::number(port).constData(), &hints, &res); + if (!result) + return false; + for (addrinfo *node = res; node; node = node->ai_next) { + if (node->ai_family == AF_INET) { + buf = QByteArray((char *)node->ai_addr, node->ai_addrlen); + break; + } + } + freeaddrinfo(res); +#else + hostent *result = gethostbyname(hostname); + if (!result || result->h_addrtype != AF_INET) + return false; + + struct sockaddr_in s; + s.sin_family = AF_INET; + s.sin_port = htons(port); + s.sin_addr = *(struct in_addr *) result->h_addr_list[0]; + + buf = QByteArray((char *)&s, sizeof s); +#endif + + return !buf.isEmpty(); +} + +bool nativeSelect(int fd, int timeout, bool selectForWrite) +{ + if (timeout < 0) + return false; + + // wait for connected + fd_set fds, fde; + FD_ZERO(&fds); + FD_ZERO(&fde); + FD_SET(fd, &fds); + FD_SET(fd, &fde); + + int ret; + do { + struct timeval tv; + tv.tv_sec = timeout / 1000; + tv.tv_usec = timeout % 1000; + if (selectForWrite) + ret = ::select(fd + 1, 0, &fds, &fde, &tv); + else + ret = ::select(fd + 1, &fds, 0, &fde, &tv); + } while (ret == -1 && errno == EINTR); + return ret != 0; +} + +void tst_NetworkRemoteStressTest::blockingSequentialRemoteHosts() +{ + QFETCH_GLOBAL(QVector<QUrl>, urlList); + QFETCH_GLOBAL(bool, useSslSocket); + + qint64 totalBytes = 0; + QElapsedTimer outerTimer; + outerTimer.start(); + + for (int i = 0; i < urlList.size(); ++i) { + const QUrl &url = urlList.at(i); + bool isHttps = url.scheme() == "https"; + QElapsedTimer timeout; + byteCounter = 0; + timeout.start(); + + QSharedPointer<QTcpSocket> socket; + if (useSslSocket || isHttps) { + socket = QSharedPointer<QTcpSocket>(new QSslSocket); + } else { + socket = QSharedPointer<QTcpSocket>(new QTcpSocket); + } + + socket->connectToHost(url.host(), url.port(isHttps ? 443 : 80)); + QVERIFY2(socket->waitForConnected(10000), "Timeout connecting to " + url.encodedHost()); + + if (isHttps) { + static_cast<QSslSocket *>(socket.data())->setProtocol(QSsl::TlsV1); + static_cast<QSslSocket *>(socket.data())->startClientEncryption(); + static_cast<QSslSocket *>(socket.data())->ignoreSslErrors(); + QVERIFY2(static_cast<QSslSocket *>(socket.data())->waitForEncrypted(10000), "Timeout starting TLS with " + url.encodedHost()); + } + + socket->write("GET " + url.toEncoded(QUrl::RemoveScheme | QUrl::RemoveAuthority | QUrl::RemoveFragment) + " HTTP/1.0\r\n" + "Connection: close\r\n" + "User-Agent: tst_QTcpSocket_stresstest/1.0\r\n" + "Host: " + url.encodedHost() + "\r\n" + "\r\n"); + while (socket->bytesToWrite()) + QVERIFY2(socket->waitForBytesWritten(10000), "Timeout writing to " + url.encodedHost()); + + while (socket->state() == QAbstractSocket::ConnectedState && !timeout.hasExpired(10000)) { + socket->waitForReadyRead(10000); + byteCounter += socket->readAll().size(); // discard + } + QVERIFY2(!timeout.hasExpired(10000), "Timeout reading from " + url.encodedHost()); + + totalBytes += byteCounter; + if (intermediateDebug) { + double rate = (byteCounter * 1.0 / timeout.elapsed()); + qDebug() << i << url << byteCounter << "bytes in" << timeout.elapsed() << "ms:" + << (rate / 1024.0 * 1000) << "kB/s"; + } + } + qDebug() << "Average transfer rate was" << (totalBytes / 1024.0 * 1000 / outerTimer.elapsed()) << "kB/s"; +} + +void tst_NetworkRemoteStressTest::sequentialRemoteHosts() +{ + QFETCH_GLOBAL(QVector<QUrl>, urlList); + QFETCH_GLOBAL(bool, useSslSocket); + + qint64 totalBytes = 0; + QElapsedTimer outerTimer; + outerTimer.start(); + + for (int i = 0; i < urlList.size(); ++i) { + const QUrl &url = urlList.at(i); + bool isHttps = url.scheme() == "https"; + QElapsedTimer timeout; + byteCounter = 0; + timeout.start(); + + QSharedPointer<QTcpSocket> socket; + if (useSslSocket || isHttps) { + socket = QSharedPointer<QTcpSocket>(new QSslSocket); + } else { + socket = QSharedPointer<QTcpSocket>(new QTcpSocket); + } + if (isHttps) { + static_cast<QSslSocket *>(socket.data())->setProtocol(QSsl::TlsV1); + static_cast<QSslSocket *>(socket.data())->connectToHostEncrypted(url.host(), url.port(443)); + static_cast<QSslSocket *>(socket.data())->ignoreSslErrors(); + } else { + socket->connectToHost(url.host(), url.port(80)); + } + + socket->write("GET " + url.toEncoded(QUrl::RemoveScheme | QUrl::RemoveAuthority | QUrl::RemoveFragment) + " HTTP/1.0\r\n" + "Connection: close\r\n" + "User-Agent: tst_QTcpSocket_stresstest/1.0\r\n" + "Host: " + url.encodedHost() + "\r\n" + "\r\n"); + connect(socket.data(), SIGNAL(readyRead()), SLOT(slotReadAll())); + + QTestEventLoop::instance().connect(socket.data(), SIGNAL(disconnected()), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(30); + QVERIFY2(!QTestEventLoop::instance().timeout(), "Timeout with " + url.encodedHost() + "; " + + QByteArray::number(socket->bytesToWrite()) + " bytes to write"); + + totalBytes += byteCounter; + if (intermediateDebug) { + double rate = (byteCounter * 1.0 / timeout.elapsed()); + qDebug() << i << url << byteCounter << "bytes in" << timeout.elapsed() << "ms:" + << (rate / 1024.0 * 1000) << "kB/s"; + } + } + qDebug() << "Average transfer rate was" << (totalBytes / 1024.0 * 1000 / outerTimer.elapsed()) << "kB/s"; +} + +void tst_NetworkRemoteStressTest::parallelRemoteHosts_data() +{ + QTest::addColumn<int>("parallelAttempts"); + QTest::newRow("1") << 1; + QTest::newRow("2") << 2; + QTest::newRow("4") << 4; + QTest::newRow("5") << 5; + QTest::newRow("6") << 6; + QTest::newRow("8") << 8; + QTest::newRow("10") << 10; + QTest::newRow("25") << 25; + QTest::newRow("500") << 500; +} + +void tst_NetworkRemoteStressTest::parallelRemoteHosts() +{ + QFETCH_GLOBAL(QVector<QUrl>, urlList); + QFETCH_GLOBAL(bool, useSslSocket); + + QFETCH(int, parallelAttempts); + + qint64 totalBytes = 0; + QElapsedTimer outerTimer; + outerTimer.start(); + + QVector<QUrl>::ConstIterator it = urlList.constBegin(); + while (it != urlList.constEnd()) { + QElapsedTimer timeout; + byteCounter = 0; + timeout.start(); + + QVector<QSharedPointer<QTcpSocket> > sockets; + sockets.reserve(parallelAttempts); + for (int j = 0; j < parallelAttempts && it != urlList.constEnd(); ++j, ++it) { + const QUrl &url = *it; + bool isHttps = url.scheme() == "https"; + QTcpSocket *socket; + if (useSslSocket || isHttps) + socket = new QSslSocket; + else + socket = new QTcpSocket; + if (isHttps) { + static_cast<QSslSocket *>(socket)->setProtocol(QSsl::TlsV1); + static_cast<QSslSocket *>(socket)->connectToHostEncrypted(url.host(), url.port(443)); + static_cast<QSslSocket *>(socket)->ignoreSslErrors(); + } else { + socket->connectToHost(url.host(), url.port(isHttps ? 443 : 80)); + } + + socket->write("GET " + url.toEncoded(QUrl::RemoveScheme | QUrl::RemoveAuthority | QUrl::RemoveFragment) + " HTTP/1.0\r\n" + "Connection: close\r\n" + "User-Agent: tst_QTcpSocket_stresstest/1.0\r\n" + "Host: " + url.encodedHost() + "\r\n" + "\r\n"); + connect(socket, SIGNAL(readyRead()), SLOT(slotReadAll())); + QTestEventLoop::instance().connect(socket, SIGNAL(disconnected()), SLOT(exitLoop())); + socket->setProperty("remoteUrl", url); + + sockets.append(QSharedPointer<QTcpSocket>(socket)); + } + + while (!timeout.hasExpired(10000)) { + QTestEventLoop::instance().enterLoop(10); + int done = 0; + for (int j = 0; j < sockets.size(); ++j) + done += sockets[j]->state() == QAbstractSocket::UnconnectedState ? 1 : 0; + if (done == sockets.size()) + break; + } + for (int j = 0; j < sockets.size(); ++j) + if (sockets[j]->state() != QAbstractSocket::UnconnectedState) { + qDebug() << "Socket to" << sockets[j]->property("remoteUrl").toUrl() << "still open with" + << sockets[j]->bytesToWrite() << "bytes to write"; + QFAIL("Timed out"); + } + + totalBytes += byteCounter; + if (intermediateDebug) { + double rate = (byteCounter * 1.0 / timeout.elapsed()); + qDebug() << byteCounter << "bytes in" << timeout.elapsed() << "ms:" + << (rate / 1024.0 * 1000) << "kB/s"; + } + } + qDebug() << "Average transfer rate was" << (totalBytes / 1024.0 * 1000 / outerTimer.elapsed()) << "kB/s"; +} + +void tst_NetworkRemoteStressTest::namRemoteGet_data() +{ + QTest::addColumn<int>("parallelAttempts"); + QTest::newRow("1") << 1; + QTest::newRow("2") << 2; + QTest::newRow("4") << 4; + QTest::newRow("5") << 5; + QTest::newRow("6") << 6; + QTest::newRow("8") << 8; + QTest::newRow("10") << 10; + QTest::newRow("25") << 25; + QTest::newRow("500") << 500; +} + +void tst_NetworkRemoteStressTest::namRemoteGet() +{ + QFETCH_GLOBAL(QVector<QUrl>, urlList); + + QFETCH(int, parallelAttempts); + bool pipelineAllowed = false;// QFETCH(bool, pipelineAllowed); + + qint64 totalBytes = 0; + QElapsedTimer outerTimer; + outerTimer.start(); + + QVector<QUrl>::ConstIterator it = urlList.constBegin(); + while (it != urlList.constEnd()) { + QElapsedTimer timeout; + byteCounter = 0; + timeout.start(); + + QNetworkRequest req; + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, pipelineAllowed); + + QVector<QSharedPointer<QNetworkReply> > replies; + replies.reserve(parallelAttempts); + for (int j = 0; j < parallelAttempts && it != urlList.constEnd(); ++j) { + req.setUrl(*it++); + QNetworkReply *r = manager.get(req); + r->ignoreSslErrors(); + + connect(r, SIGNAL(readyRead()), SLOT(slotReadAll())); + QTestEventLoop::instance().connect(r, SIGNAL(finished()), SLOT(exitLoop())); + + replies.append(QSharedPointer<QNetworkReply>(r)); + } + + while (!timeout.hasExpired(30000)) { + QTestEventLoop::instance().enterLoop(30 - timeout.elapsed() / 1000); + int done = 0; + for (int j = 0; j < replies.size(); ++j) + done += replies[j]->isFinished() ? 1 : 0; + if (done == replies.size()) + break; + } + if (timeout.hasExpired(30000)) { + for (int j = 0; j < replies.size(); ++j) + if (!replies[j]->isFinished()) + qDebug() << "Request" << replies[j]->url() << "not finished"; + QFAIL("Timed out"); + } + replies.clear(); + + totalBytes += byteCounter; + if (intermediateDebug) { + double rate = (byteCounter * 1.0 / timeout.elapsed()); + qDebug() << byteCounter << "bytes in" << timeout.elapsed() << "ms:" + << (rate / 1024.0 * 1000) << "kB/s"; + } + } + qDebug() << "Average transfer rate was" << (totalBytes / 1024.0 * 1000 / outerTimer.elapsed()) << "kB/s"; +} + +QTEST_MAIN(tst_NetworkRemoteStressTest); + +#include "tst_network_remote_stresstest.moc" |