text: /****************************************************************************
  **
  ** Copyright (C) 2016 The Qt Company Ltd.
  ** Contact: https://www.qt.io/licensing/
  **
  ** This file is part of the test suite of the Qt Toolkit.
  **
  ** $QT_BEGIN_LICENSE:GPL-EXCEPT$
  ** Commercial License Usage
  ** Licensees holding valid commercial Qt licenses may use this file in
  ** accordance with the commercial license agreement provided with the
  ** Software or, alternatively, in accordance with the terms contained in
  ** a written agreement between you and The Qt Company. For licensing terms
  ** and conditions see https://www.qt.io/terms-conditions. For further
  ** information use the contact form at https://www.qt.io/contact-us.
  **
  ** GNU General Public License Usage
  ** Alternatively, this file may be used under the terms of the GNU
  ** General Public License version 3 as published by the Free Software
  ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
  ** included in the packaging of this file. Please review the following
  ** information to ensure the GNU General Public License requirements will
  ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
  **
  ** $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/QNetworkReply>
  #include <QtNetwork/QNetworkAccessManager>
  
  #ifdef QT_BUILD_INTERNAL
  # include <private/qnetworkaccessmanager_p.h>
  #endif
  
  #include "minihttpserver.h"
  #include "../../auto/network-settings.h"
  
  #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
  
  class tst_NetworkStressTest : public QObject
  {
      Q_OBJECT
  public:
      enum { AttemptCount = 100 };
      tst_NetworkStressTest();
      MiniHttpServer server;
  
      qint64 byteCounter;
      QNetworkAccessManager manager;
      bool intermediateDebug;
  
  private:
      void clearManager();
  
  public slots:
      void initTestCase_data();
      void initTestCase();
      void init();
  
      void slotReadAll() { byteCounter += static_cast<QIODevice *>(sender())->readAll().size(); }
  
  private Q_SLOTS:
      void nativeBlockingConnectDisconnect();
      void nativeNonBlockingConnectDisconnect();
      void blockingConnectDisconnect();
      void blockingPipelined();
      void blockingMultipleRequests();
      void connectDisconnect();
      void parallelConnectDisconnect_data();
      void parallelConnectDisconnect();
      void namGet_data();
      void namGet();
  };
  
  tst_NetworkStressTest::tst_NetworkStressTest()
      : 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)
      ::signal(SIGALRM, SIG_IGN);
  #endif
  }
  
  void tst_NetworkStressTest::initTestCase_data()
  {
      QTest::addColumn<bool>("isLocalhost");
      QTest::addColumn<QString>("hostname");
      QTest::addColumn<int>("port");
  
      QTest::newRow("localhost") << true << "localhost" << server.port();
      QTest::newRow("remote") << false << QtNetworkSettings::serverName() << 80;
  }
  
  void tst_NetworkStressTest::initTestCase()
  {
      QVERIFY(QtNetworkSettings::verifyTestNetworkSettings());
  }
  
  void tst_NetworkStressTest::init()
  {
      // clear the internal cache
  #ifndef QT_BUILD_INTERNAL
      if (strncmp(QTest::currentTestFunction(), "nam", 3) == 0)
          QSKIP("QNetworkAccessManager tests disabled");
  #endif
  }
  
  void tst_NetworkStressTest::clearManager()
  {
  #ifdef QT_BUILD_INTERNAL
      QNetworkAccessManagerPrivate::clearAuthenticationCache(&manager);
      QNetworkAccessManagerPrivate::clearConnectionCache(&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_NetworkStressTest::nativeBlockingConnectDisconnect()
  {
      QFETCH_GLOBAL(QString, hostname);
      QFETCH_GLOBAL(int, port);
  
      qint64 totalBytes = 0;
      QElapsedTimer outerTimer;
      outerTimer.start();
  
      for (int i = 0; i < AttemptCount; ++i) {
          QElapsedTimer timeout;
          byteCounter = 0;
          timeout.start();
  
  #if defined(Q_OS_UNIX)
          alarm(10);
  #endif
  
          // look up the host
          QByteArray addr;
          if (!nativeLookup(QUrl::toAce(hostname).constData(), port, addr))
              QFAIL("Lookup failed");
  
          // connect
          SOCKET fd = ::socket(AF_INET, SOCK_STREAM, 0);
          QVERIFY(fd != INVALID_SOCKET);
          QVERIFY(::connect(fd, (sockaddr *)addr.data(), addr.size()) != SOCKET_ERROR);
  
          // send request
          {
              QByteArray request = "GET /qtest/bigfile HTTP/1.1\r\n"
                                   "Connection: close\r\n"
                                   "User-Agent: tst_QTcpSocket_stresstest/1.0\r\n"
                                   "Host: " + hostname.toLatin1() + "\r\n"
                                   "\r\n";
              qint64 bytesWritten = 0;
              while (bytesWritten < request.size()) {
                  qint64 ret = ::send(fd, request.constData() + bytesWritten, request.size() - bytesWritten, 0);
                  if (ret == -1) {
                      ::close(fd);
                      QFAIL("Timeout");
                  }
                  bytesWritten += ret;
              }
          }
  
          // receive reply
          char buf[16384];
          while (true) {
              qint64 ret = ::recv(fd, buf, sizeof buf, 0);
              if (ret == -1) {
                  ::close(fd);
                  QFAIL("Timeout");
              } else if (ret == 0) {
                  break; // EOF
              }
              byteCounter += ret;
          }
          ::close(fd);
  
          totalBytes += byteCounter;
          if (intermediateDebug) {
              double rate = (byteCounter * 1.0 / timeout.elapsed());
              qDebug() << i << byteCounter << "bytes in" << timeout.elapsed() << "ms:"
                      << (rate / 1024.0 / 1024 * 1000) << "MB/s";
          }
      }
      qDebug() << "Average transfer rate was" << (totalBytes / 1024.0 / 1024 * 1000 / outerTimer.elapsed()) << "MB/s";
  
  #if defined(Q_OS_UNIX)
      alarm(0);
  #endif
  }
  
  void tst_NetworkStressTest::nativeNonBlockingConnectDisconnect()
  {
      QFETCH_GLOBAL(QString, hostname);
      QFETCH_GLOBAL(int, port);
  
      qint64 totalBytes = 0;
      QElapsedTimer outerTimer;
      outerTimer.start();
  
      for (int i = 0; i < AttemptCount; ++i) {
          QElapsedTimer timeout;
          byteCounter = 0;
          timeout.start();
  
          // look up the host
          QByteArray addr;
          if (!nativeLookup(QUrl::toAce(hostname).constData(), port, addr))
              QFAIL("Lookup failed");
  
          SOCKET fd;
  
          {
  #if defined(Q_OS_UNIX)
              fd = ::socket(AF_INET, SOCK_STREAM, 0);
              QVERIFY(fd != INVALID_SOCKET);
  
              // set the socket to non-blocking and start connecting
  # if !defined(Q_OS_VXWORKS)
              int flags = ::fcntl(fd, F_GETFL, 0);
              QVERIFY(flags != -1);
              QVERIFY(::fcntl(fd, F_SETFL, flags | O_NONBLOCK) != -1);
  # else // Q_OS_VXWORKS
              int onoff = 1;
              QVERIFY(::ioctl(socketDescriptor, FIONBIO, &onoff) >= 0);
  # endif // Q_OS_VXWORKS
              while (true) {
                  if (::connect(fd, (sockaddr *)addr.data(), addr.size()) == -1) {
                      QVERIFY2(errno == EINPROGRESS, QByteArray("Error connecting: ").append(strerror(errno)).constData());
                      QVERIFY2(nativeSelect(fd, 10000 - timeout.elapsed(), true), "Timeout");
                  } else {
                      break; // connected
                  }
              }
  #elif defined(Q_OS_WIN)
              fd = ::WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
              QVERIFY(fd != INVALID_SOCKET);
  
              // set the socket to non-blocking and start connecting
              unsigned long buf = 0;
              unsigned long outBuf;
              DWORD sizeWritten = 0;
              QVERIFY(::WSAIoctl(fd, FIONBIO, &buf, sizeof(unsigned long), &outBuf, sizeof(unsigned long), &sizeWritten, 0,0) != SOCKET_ERROR);
  
              while (true) {
                  int connectResult = ::WSAConnect(fd, (sockaddr *)addr.data(), addr.size(), 0,0,0,0);
                  if (connectResult == 0 || WSAGetLastError() == WSAEISCONN) {
                      break; // connected
                  } else {
                      QVERIFY2(WSAGetLastError() == WSAEINPROGRESS, "Unexpected error");
                      QVERIFY2(nativeSelect(fd, 10000 - timeout.elapsed(), true), "Timeout");
                  }
              }
  #endif
          }
  
          // send request
          {
              QByteArray request = "GET /qtest/bigfile HTTP/1.1\r\n"
                                   "Connection: close\r\n"
                                   "User-Agent: tst_QTcpSocket_stresstest/1.0\r\n"
                                   "Host: " + hostname.toLatin1() + "\r\n"
                                   "\r\n";
              qint64 bytesWritten = 0;
              while (bytesWritten < request.size()) {
                  qint64 ret = ::send(fd, request.constData() + bytesWritten, request.size() - bytesWritten, 0);
                  if (ret == -1 && errno != EWOULDBLOCK) {
                      ::close(fd);
                      QFAIL(QByteArray("Error writing: ").append(strerror(errno)).constData());
                  } else if (ret == -1) {
                      // wait for writing
                      QVERIFY2(nativeSelect(fd, 10000 - timeout.elapsed(), true), "Timeout");
                      continue;
                  }
                  bytesWritten += ret;
              }
          }
  
          // receive reply
          char buf[16384];
          while (true) {
              qint64 ret = ::recv(fd, buf, sizeof buf, 0);
              if (ret == -1 && errno != EWOULDBLOCK) {
                  ::close(fd);
                  QFAIL("Timeout");
              } else if (ret == -1) {
                  // wait for reading
                  QVERIFY2(nativeSelect(fd, 10000 - timeout.elapsed(), false), "Timeout");
              } else if (ret == 0) {
                  break; // EOF
              }
              byteCounter += ret;
          }
          ::close(fd);
  
          totalBytes += byteCounter;
          if (intermediateDebug) {
              double rate = (byteCounter * 1.0 / timeout.elapsed());
              qDebug() << i << byteCounter << "bytes in" << timeout.elapsed() << "ms:"
                      << (rate / 1024.0 / 1024 * 1000) << "MB/s";
          }
      }
      qDebug() << "Average transfer rate was" << (totalBytes / 1024.0 / 1024 * 1000 / outerTimer.elapsed()) << "MB/s";
  }
  
  void tst_NetworkStressTest::blockingConnectDisconnect()
  {
      QFETCH_GLOBAL(QString, hostname);
      QFETCH_GLOBAL(int, port);
  
      qint64 totalBytes = 0;
      QElapsedTimer outerTimer;
      outerTimer.start();
  
      for (int i = 0; i < AttemptCount; ++i) {
          QElapsedTimer timeout;
          byteCounter = 0;
          timeout.start();
  
          QTcpSocket socket;
          socket.connectToHost(hostname, port);
          QVERIFY2(socket.waitForConnected(), "Timeout");
  
          socket.write("GET /qtest/bigfile HTTP/1.1\r\n"
                       "Connection: close\r\n"
                       "User-Agent: tst_QTcpSocket_stresstest/1.0\r\n"
                       "Host: " + hostname.toLatin1() + "\r\n"
                       "\r\n");
          while (socket.bytesToWrite())
              QVERIFY2(socket.waitForBytesWritten(), "Timeout");
  
          while (socket.state() == QAbstractSocket::ConnectedState && !timeout.hasExpired(10000)) {
              socket.waitForReadyRead();
              byteCounter += socket.readAll().size(); // discard
          }
  
          totalBytes += byteCounter;
          if (intermediateDebug) {
              double rate = (byteCounter * 1.0 / timeout.elapsed());
              qDebug() << i << byteCounter << "bytes in" << timeout.elapsed() << "ms:"
                      << (rate / 1024.0 / 1024 * 1000) << "MB/s";
          }
      }
      qDebug() << "Average transfer rate was" << (totalBytes / 1024.0 / 1024 * 1000 / outerTimer.elapsed()) << "MB/s";
  }
  
  void tst_NetworkStressTest::blockingPipelined()
  {
      QFETCH_GLOBAL(QString, hostname);
      QFETCH_GLOBAL(int, port);
  
      qint64 totalBytes = 0;
      QElapsedTimer outerTimer;
      outerTimer.start();
  
      for (int i = 0; i < AttemptCount / 2; ++i) {
          QElapsedTimer timeout;
          byteCounter = 0;
          timeout.start();
  
          QTcpSocket socket;
          socket.connectToHost(hostname, port);
          QVERIFY2(socket.waitForConnected(), "Timeout");
  
          for (int j = 0 ; j < 3; ++j) {
              if (intermediateDebug)
                  qDebug("Attempt %d%c", i, 'a' + j);
              socket.write("GET /qtest/bigfile HTTP/1.1\r\n"
                           "Connection: " + QByteArray(j == 2 ? "close" : "keep-alive") + "\r\n"
                           "User-Agent: tst_QTcpSocket_stresstest/1.0\r\n"
                           "Host: " + hostname.toLatin1() + "\r\n"
                           "\r\n");
              while (socket.bytesToWrite())
                  QVERIFY2(socket.waitForBytesWritten(), "Timeout");
          }
  
          while (socket.state() == QAbstractSocket::ConnectedState && !timeout.hasExpired(10000)) {
              socket.waitForReadyRead();
              byteCounter += socket.readAll().size(); // discard
          }
  
          totalBytes += byteCounter;
          if (intermediateDebug) {
              double rate = (byteCounter * 1.0 / timeout.elapsed());
              qDebug() << i << byteCounter << "bytes in" << timeout.elapsed() << "ms:"
                      << (rate / 1024.0 / 1024 * 1000) << "MB/s";
          }
      }
      qDebug() << "Average transfer rate was" << (totalBytes / 1024.0 / 1024 * 1000 / outerTimer.elapsed()) << "MB/s";
  }
  
  void tst_NetworkStressTest::blockingMultipleRequests()
  {
      QFETCH_GLOBAL(QString, hostname);
      QFETCH_GLOBAL(int, port);
  
      qint64 totalBytes = 0;
      QElapsedTimer outerTimer;
      outerTimer.start();
  
      for (int i = 0; i < AttemptCount / 5; ++i) {
          QTcpSocket socket;
          socket.connectToHost(hostname, port);
          QVERIFY2(socket.waitForConnected(), "Timeout");
  
          for (int j = 0 ; j < 5; ++j) {
              QElapsedTimer timeout;
              byteCounter = 0;
              timeout.start();
  
              socket.write("GET /qtest/bigfile HTTP/1.1\r\n"
                           "Connection: keep-alive\r\n"
                           "User-Agent: tst_QTcpSocket_stresstest/1.0\r\n"
                           "Host: " + hostname.toLatin1() + "\r\n"
                           "\r\n");
              while (socket.bytesToWrite())
                  QVERIFY2(socket.waitForBytesWritten(), "Timeout");
  
              qint64 bytesRead = 0;
              qint64 bytesExpected = -1;
              QByteArray buffer;
              while (socket.state() == QAbstractSocket::ConnectedState && !timeout.hasExpired(10000)) {
                  socket.waitForReadyRead();
                  buffer += socket.readAll();
                  byteCounter += buffer.size();
                  int pos = buffer.indexOf("\r\n\r\n");
                  if (pos == -1)
                      continue;
  
                  bytesRead = buffer.length() - pos - 4;
  
                  buffer.truncate(pos + 2);
                  buffer = buffer.toLower();
                  pos = buffer.indexOf("\r\ncontent-length: ");
                  if (pos == -1) {
                      qWarning() << "no content-length:" << QString(buffer);
                      break;
                  }
                  pos += int(strlen("\r\ncontent-length: "));
  
                  int eol = buffer.indexOf("\r\n", pos + 2);
                  if (eol == -1) {
                      qWarning() << "invalid header";
                      break;
                  }
  
                  bytesExpected = buffer.mid(pos, eol - pos).toLongLong();
                  break;
              }
              QVERIFY(bytesExpected > 0);
              QVERIFY2(!timeout.hasExpired(10000), "Timeout");
  
              while (socket.state() == QAbstractSocket::ConnectedState && !timeout.hasExpired(10000) && bytesExpected > bytesRead) {
                  socket.waitForReadyRead();
                  int blocklen = socket.read(bytesExpected - bytesRead).length(); // discard
                  bytesRead += blocklen;
                  byteCounter += blocklen;
              }
              QVERIFY2(!timeout.hasExpired(10000), "Timeout");
              QCOMPARE(bytesRead, bytesExpected);
  
              totalBytes += byteCounter;
              if (intermediateDebug) {
                  double rate = (byteCounter * 1.0 / timeout.elapsed());
                  qDebug() << i << byteCounter << "bytes in" << timeout.elapsed() << "ms:"
                          << (rate / 1024.0 / 1024 * 1000) << "MB/s";
              }
          }
  
          socket.disconnectFromHost();
          QVERIFY(socket.state() == QAbstractSocket::UnconnectedState);
      }
      qDebug() << "Average transfer rate was" << (totalBytes / 1024.0 / 1024 * 1000 / outerTimer.elapsed()) << "MB/s";
  }
  
  void tst_NetworkStressTest::connectDisconnect()
  {
      QFETCH_GLOBAL(QString, hostname);
      QFETCH_GLOBAL(int, port);
  
      qint64 totalBytes = 0;
      QElapsedTimer outerTimer;
      outerTimer.start();
  
      for (int i = 0; i < AttemptCount; ++i) {
          QElapsedTimer timeout;
          byteCounter = 0;
          timeout.start();
  
          QTcpSocket socket;
          socket.connectToHost(hostname, port);
  
          socket.write("GET /qtest/bigfile HTTP/1.1\r\n"
                       "Connection: close\r\n"
                       "User-Agent: tst_QTcpSocket_stresstest/1.0\r\n"
                       "Host: " + hostname.toLatin1() + "\r\n"
                       "\r\n");
          connect(&socket, SIGNAL(readyRead()), SLOT(slotReadAll()));
  
          QTestEventLoop::instance().connect(&socket, SIGNAL(disconnected()), SLOT(exitLoop()));
          QTestEventLoop::instance().enterLoop(30);
          QVERIFY2(!QTestEventLoop::instance().timeout(), "Timeout");
  
          totalBytes += byteCounter;
          if (intermediateDebug) {
              double rate = (byteCounter * 1.0 / timeout.elapsed());
              qDebug() << i << byteCounter << "bytes in" << timeout.elapsed() << "ms:"
                      << (rate / 1024.0 / 1024 * 1000) << "MB/s";
          }
      }
      qDebug() << "Average transfer rate was" << (totalBytes / 1024.0 / 1024 * 1000 / outerTimer.elapsed()) << "MB/s";
  }
  
  void tst_NetworkStressTest::parallelConnectDisconnect_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("100") << 100;
      QTest::newRow("500") << 500;
  }
  
  void tst_NetworkStressTest::parallelConnectDisconnect()
  {
      QFETCH_GLOBAL(QString, hostname);
      QFETCH_GLOBAL(int, port);
      QFETCH(int, parallelAttempts);
  
      if (parallelAttempts > 100) {
          QFETCH_GLOBAL(bool, isLocalhost);
          if (!isLocalhost)
              QSKIP("Localhost-only test");
      }
  
      qint64 totalBytes = 0;
      QElapsedTimer outerTimer;
      outerTimer.start();
  
      for (int i = 0; i < qMax(2, AttemptCount/qMax(2, parallelAttempts/4)); ++i) {
          QElapsedTimer timeout;
          byteCounter = 0;
          timeout.start();
  
          QTcpSocket *socket = new QTcpSocket[parallelAttempts];
          for (int j = 0; j < parallelAttempts; ++j) {
              socket[j].connectToHost(hostname, port);
  
              socket[j].write("GET /qtest/bigfile HTTP/1.1\r\n"
                              "Connection: close\r\n"
                              "User-Agent: tst_QTcpSocket_stresstest/1.0\r\n"
                              "Host: " + hostname.toLatin1() + "\r\n"
                              "\r\n");
              connect(&socket[j], SIGNAL(readyRead()), SLOT(slotReadAll()));
  
              QTestEventLoop::instance().connect(&socket[j], SIGNAL(disconnected()), SLOT(exitLoop()));
          }
  
          while (!timeout.hasExpired(30000)) {
              QTestEventLoop::instance().enterLoop(10);
              int done = 0;
              for (int j = 0; j < parallelAttempts; ++j)
                  done += socket[j].state() == QAbstractSocket::UnconnectedState ? 1 : 0;
              if (done == parallelAttempts)
                  break;
          }
          delete[] socket;
          QVERIFY2(!timeout.hasExpired(30000), "Timeout");
  
          totalBytes += byteCounter;
          if (intermediateDebug) {
              double rate = (byteCounter * 1.0 / timeout.elapsed());
              qDebug() << i << byteCounter << "bytes in" << timeout.elapsed() << "ms:"
                      << (rate / 1024.0 / 1024 * 1000) << "MB/s";
          }
      }
      qDebug() << "Average transfer rate was" << (totalBytes / 1024.0 / 1024 * 1000 / outerTimer.elapsed()) << "MB/s";
  }
  
  void tst_NetworkStressTest::namGet_data()
  {
      QTest::addColumn<int>("parallelAttempts");
      QTest::addColumn<bool>("pipelineAllowed");
  
      QTest::newRow("1") << 1 << false;
      QTest::newRow("1p") << 1 << true;
      QTest::newRow("2") << 2 << false;
      QTest::newRow("2p") << 2 << true;
      QTest::newRow("4") << 4 << false;
      QTest::newRow("4p") << 4 << true;
      QTest::newRow("5") << 5 << false;
      QTest::newRow("5p") << 5 << true;
      QTest::newRow("6") << 6 << false;
      QTest::newRow("6p") << 6 << true;
      QTest::newRow("8") << 8 << false;
      QTest::newRow("8p") << 8 << true;
      QTest::newRow("10") << 10 << false;
      QTest::newRow("10p") << 10 << true;
      QTest::newRow("25") << 25 << false;
      QTest::newRow("25p") << 25 << true;
      QTest::newRow("100") << 100 << false;
      QTest::newRow("100p") << 100 << true;
      QTest::newRow("500") << 500 << false;
      QTest::newRow("500p") << 500 << true;
  }
  
  void tst_NetworkStressTest::namGet()
  {
      QFETCH_GLOBAL(QString, hostname);
      QFETCH_GLOBAL(int, port);
      QFETCH(int, parallelAttempts);
      QFETCH(bool, pipelineAllowed);
  
      if (parallelAttempts > 100) {
          QFETCH_GLOBAL(bool, isLocalhost);
          if (!isLocalhost)
              QSKIP("Localhost-only test");
      }
  
      qint64 totalBytes = 0;
      QElapsedTimer outerTimer;
      outerTimer.start();
  
      for (int i = 0; i < qMax(2, AttemptCount/qMax(2, parallelAttempts/4)); ++i) {
          QElapsedTimer timeout;
          byteCounter = 0;
          timeout.start();
  
          QUrl url;
          url.setScheme("http");
          url.setHost(hostname);
          url.setPort(port);
          url.setEncodedPath("/qtest/bigfile");
          QNetworkRequest req(url);
          req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, pipelineAllowed);
  
          QVector<QSharedPointer<QNetworkReply> > replies;
          replies.resize(parallelAttempts);
          for (int j = 0; j < parallelAttempts; ++j) {
              QNetworkReply *r = manager.get(req);
  
              connect(r, SIGNAL(readyRead()), SLOT(slotReadAll()));
              QTestEventLoop::instance().connect(r, SIGNAL(finished()), SLOT(exitLoop()));
  
              replies[j] = QSharedPointer<QNetworkReply>(r);
          }
  
          while (!timeout.hasExpired(30000)) {
              QTestEventLoop::instance().enterLoop(10);
              int done = 0;
              for (int j = 0; j < parallelAttempts; ++j)
                  done += replies[j]->isFinished() ? 1 : 0;
              if (done == parallelAttempts)
                  break;
          }
          replies.clear();
  
          QVERIFY2(!timeout.hasExpired(30000), "Timeout");
          totalBytes += byteCounter;
          if (intermediateDebug) {
              double rate = (byteCounter * 1.0 / timeout.elapsed());
              qDebug() << i << byteCounter << "bytes in" << timeout.elapsed() << "ms:"
                      << (rate / 1024.0 / 1024 * 1000) << "MB/s";
          }
      }
      qDebug() << "Average transfer rate was" << (totalBytes / 1024.0 / 1024 * 1000 / outerTimer.elapsed()) << "MB/s";
  }
  
  QTEST_MAIN(tst_NetworkStressTest);
  
  #include "tst_network_stresstest.moc"
  