diff options
author | Marius Storm-Olsen <marius.storm-olsen@nokia.com> | 2009-11-09 11:34:59 (GMT) |
---|---|---|
committer | Marius Storm-Olsen <marius.storm-olsen@nokia.com> | 2009-11-09 11:34:59 (GMT) |
commit | 00aeefeeaff4d8c3e25c6f388c5e51c239541f87 (patch) | |
tree | 265f1ff24e1628b8974b96ec1858cb462d4e4b3e /tests | |
parent | 9b7a5241a287b71c1ab4a9cd9cd3d89c03136ced (diff) | |
parent | b65fd82299689a1f353e16caa7d1c896b838762a (diff) | |
download | Qt-00aeefeeaff4d8c3e25c6f388c5e51c239541f87.zip Qt-00aeefeeaff4d8c3e25c6f388c5e51c239541f87.tar.gz Qt-00aeefeeaff4d8c3e25c6f388c5e51c239541f87.tar.bz2 |
Merge commit 'b65fd82299' from qt-core-team 4.6 into 4.6
Conflicts:
configure.exe
src/corelib/io/qfsfileengine.cpp
tests/auto/qfile/tst_qfile.cpp
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/qfile/tst_qfile.cpp | 166 | ||||
-rw-r--r-- | tests/auto/qlocalsocket/tst_qlocalsocket.cpp | 10 | ||||
-rw-r--r-- | tests/auto/qnetworkreply/tst_qnetworkreply.cpp | 479 | ||||
-rw-r--r-- | tests/benchmarks/qnetworkreply/tst_qnetworkreply.cpp | 530 |
4 files changed, 671 insertions, 514 deletions
diff --git a/tests/auto/qfile/tst_qfile.cpp b/tests/auto/qfile/tst_qfile.cpp index 55cc286..b3d6fd9 100644 --- a/tests/auto/qfile/tst_qfile.cpp +++ b/tests/auto/qfile/tst_qfile.cpp @@ -91,6 +91,10 @@ #define STDERR_FILENO 2 #endif +#ifndef QT_OPEN_BINARY +#define QT_OPEN_BINARY 0 +#endif + Q_DECLARE_METATYPE(QFile::FileError) //TESTED_CLASS= @@ -211,6 +215,81 @@ public: // disabled this test for the moment... it hangs void invalidFile_data(); void invalidFile(); + +private: + enum FileType { OpenQFile, OpenFd, OpenStream }; + + bool openFd(QFile &file, QIODevice::OpenMode mode) + { + int fdMode = QT_OPEN_LARGEFILE | QT_OPEN_BINARY; + + // File will be truncated if in Write mode. + if (mode & QIODevice::WriteOnly) + fdMode |= QT_OPEN_WRONLY | QT_OPEN_TRUNC; + if (mode & QIODevice::ReadOnly) + fdMode |= QT_OPEN_RDONLY; + + fd_ = QT_OPEN(qPrintable(file.fileName()), fdMode); + + return (-1 != fd_) && file.open(fd_, mode); + } + + bool openStream(QFile &file, QIODevice::OpenMode mode) + { + char const *streamMode = ""; + + // File will be truncated if in Write mode. + if (mode & QIODevice::WriteOnly) + streamMode = "wb+"; + else if (mode & QIODevice::ReadOnly) + streamMode = "rb"; + + stream_ = QT_FOPEN(qPrintable(file.fileName()), streamMode); + + return stream_ && file.open(stream_, mode); + } + + bool openFile(QFile &file, QIODevice::OpenMode mode, FileType type = OpenQFile) + { + if (mode & QIODevice::WriteOnly && !file.exists()) + { + // Make sure the file exists + QFile createFile(file.fileName()); + if (!createFile.open(QIODevice::ReadWrite)) + return false; + } + + // Note: openFd and openStream will truncate the file if write mode. + switch (type) + { + case OpenQFile: + return file.open(mode); + + case OpenFd: + return openFd(file, mode); + + case OpenStream: + return openStream(file, mode); + } + + return false; + } + + void closeFile(QFile &file) + { + file.close(); + + if (-1 != fd_) + QT_CLOSE(fd_); + if (stream_) + ::fclose(stream_); + + fd_ = -1; + stream_ = 0; + } + + int fd_; + FILE *stream_; }; tst_QFile::tst_QFile() @@ -226,6 +305,8 @@ void tst_QFile::init() { // TODO: Add initialization code here. // This will be executed immediately before each test is run. + fd_ = -1; + stream_ = 0; } void tst_QFile::cleanup() @@ -254,6 +335,11 @@ void tst_QFile::cleanup() QFile::remove("existing-file.txt"); QFile::remove("file-renamed-once.txt"); QFile::remove("file-renamed-twice.txt"); + + if (-1 != fd_) + QT_CLOSE(fd_); + if (stream_) + ::fclose(stream_); } void tst_QFile::initTestCase() @@ -1958,53 +2044,71 @@ void tst_QFile::fullDisk() void tst_QFile::writeLargeDataBlock_data() { QTest::addColumn<QString>("fileName"); + QTest::addColumn<int>("type"); + + QTest::newRow("localfile-QFile") << "./largeblockfile.txt" << (int)OpenQFile; + QTest::newRow("localfile-Fd") << "./largeblockfile.txt" << (int)OpenFd; + QTest::newRow("localfile-Stream") << "./largeblockfile.txt" << (int)OpenStream; - QTest::newRow("localfile") << QString("./largeblockfile.txt"); #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) // Some semi-randomness to avoid collisions. QTest::newRow("unc file") << QString("//" + QtNetworkSettings::winServerName() + "/TESTSHAREWRITABLE/largefile-%1-%2.txt") .arg(QHostInfo::localHostName()) - .arg(QTime::currentTime().msec()); + .arg(QTime::currentTime().msec()) << (int)OpenQFile; #endif } -void tst_QFile::writeLargeDataBlock() +static QByteArray getLargeDataBlock() { - QFETCH(QString, fileName); + static QByteArray array; - // Generate a 64MB array with well defined contents. - QByteArray array; + if (array.isNull()) + { #if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN) - int resizeSize = 1024 * 1024; // WinCE and Symbian do not have much space + int resizeSize = 1024 * 1024; // WinCE and Symbian do not have much space #else - int resizeSize = 64 * 1024 * 1024; + int resizeSize = 64 * 1024 * 1024; #endif - array.resize(resizeSize); - for (int i = 0; i < array.size(); ++i) - array[i] = uchar(i); + array.resize(resizeSize); + for (int i = 0; i < array.size(); ++i) + array[i] = uchar(i); + } - // Remove and open the target file - QFile file(fileName); - file.remove(); - if (file.open(QFile::WriteOnly)) { - QCOMPARE(file.write(array), qint64(array.size())); - file.close(); - QVERIFY(file.open(QFile::ReadOnly)); - array.clear(); - array = file.readAll(); - file.remove(); - } else { - QFAIL(qPrintable(QString("Couldn't open file for writing: [%1]").arg(fileName))); - } - // Check that we got the right content - QCOMPARE(array.size(), resizeSize); - for (int i = 0; i < array.size(); ++i) { - if (array[i] != char(i)) { - QFAIL(qPrintable(QString("Wrong contents! Char at %1 = %2, expected %3") - .arg(i).arg(int(uchar(array[i]))).arg(int(uchar(i))))); - } + return array; +} + +void tst_QFile::writeLargeDataBlock() +{ + QFETCH(QString, fileName); + QFETCH( int, type ); + + QByteArray const originalData = getLargeDataBlock(); + + { + QFile file(fileName); + + QVERIFY2( openFile(file, QIODevice::WriteOnly, (FileType)type), + qPrintable(QString("Couldn't open file for writing: [%1]").arg(fileName)) ); + QCOMPARE( file.write(originalData), (qint64)originalData.size() ); + QVERIFY( file.flush() ); + + closeFile(file); + } + + QByteArray readData; + + { + QFile file(fileName); + + QVERIFY2( openFile(file, QIODevice::ReadOnly, (FileType)type), + qPrintable(QString("Couldn't open file for reading: [%1]").arg(fileName)) ); + readData = file.readAll(); + closeFile(file); } + + QCOMPARE( readData, originalData ); + QVERIFY( QFile::remove(fileName) ); } void tst_QFile::readFromWriteOnlyFile() diff --git a/tests/auto/qlocalsocket/tst_qlocalsocket.cpp b/tests/auto/qlocalsocket/tst_qlocalsocket.cpp index ab7b0ac..5ead049 100644 --- a/tests/auto/qlocalsocket/tst_qlocalsocket.cpp +++ b/tests/auto/qlocalsocket/tst_qlocalsocket.cpp @@ -587,14 +587,16 @@ void tst_QLocalSocket::readBufferOverflow() char buffer[dataBufferSize]; memset(buffer, 0, dataBufferSize); serverSocket->write(buffer, dataBufferSize); - serverSocket->flush(); + serverSocket->waitForBytesWritten(); + // wait until the first 128 bytes are ready to read QVERIFY(client.waitForReadyRead()); QCOMPARE(client.read(buffer, readBufferSize), qint64(readBufferSize)); -#if defined(QT_LOCALSOCKET_TCP) || defined(Q_OS_SYMBIAN) - QTest::qWait(250); -#endif + // 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 diff --git a/tests/auto/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/qnetworkreply/tst_qnetworkreply.cpp index 7adb67f..0b61dcd 100644 --- a/tests/auto/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/qnetworkreply/tst_qnetworkreply.cpp @@ -227,12 +227,6 @@ private Q_SLOTS: void rateControl_data(); void rateControl(); - void downloadPerformance(); - void uploadPerformance(); - void performanceControlRate(); - void httpUploadPerformance(); - void httpDownloadPerformance_data(); - void httpDownloadPerformance(); void downloadProgress_data(); void downloadProgress(); @@ -413,93 +407,6 @@ public slots: } }; -class FixedSizeDataGenerator : public QIODevice -{ - Q_OBJECT - enum { Idle, Started, Stopped } state; -public: - FixedSizeDataGenerator(qint64 size) : state(Idle) - { open(ReadOnly | Unbuffered); - toBeGeneratedTotalCount = toBeGeneratedCount = size; - } - - virtual qint64 bytesAvailable() const - { - return state == Started ? toBeGeneratedCount + QIODevice::bytesAvailable() : 0; - } - - virtual bool isSequential() const{ - return false; - } - - virtual bool reset() const{ - return false; - } - - qint64 size() const { - return toBeGeneratedTotalCount; - } - -public slots: - void start() { state = Started; emit readyRead(); } - -protected: - virtual qint64 readData(char *data, qint64 maxlen) - { - memset(data, '@', maxlen); - - if (toBeGeneratedCount <= 0) { - return -1; - } - - qint64 n = qMin(maxlen, toBeGeneratedCount); - toBeGeneratedCount -= n; - - if (toBeGeneratedCount <= 0) { - // make sure this is a queued connection! - emit readChannelFinished(); - } - - return n; - } - virtual qint64 writeData(const char *, qint64) - { return -1; } - - qint64 toBeGeneratedCount; - qint64 toBeGeneratedTotalCount; -}; - - -class DataGenerator: public QIODevice -{ - Q_OBJECT - enum { Idle, Started, Stopped } state; -public: - DataGenerator() : state(Idle) - { open(ReadOnly); } - - virtual bool isSequential() const { return true; } - virtual qint64 bytesAvailable() const { return state == Started ? 1024*1024 : 0; } - -public slots: - void start() { state = Started; emit readyRead(); } - void stop() { state = Stopped; emit readyRead(); } - -protected: - virtual qint64 readData(char *data, qint64 maxlen) - { - if (state == Stopped) - return -1; // EOF - - // return as many bytes as are wanted - memset(data, '@', maxlen); - return maxlen; - } - virtual qint64 writeData(const char *, qint64) - { return -1; } -}; - - class SocketPair: public QObject { @@ -692,255 +599,6 @@ protected: } }; -class TimedSender: public QThread -{ - Q_OBJECT - qint64 totalBytes; - QSemaphore ready; - QByteArray dataToSend; - QTcpSocket *client; - int timeout; - int port; -public: - int transferRate; - TimedSender(int ms) - : totalBytes(0), timeout(ms), port(-1), transferRate(-1) - { - dataToSend = QByteArray(16*1024, '@'); - start(); - ready.acquire(); - } - - inline int serverPort() const { return port; } - -private slots: - void writeMore() - { - while (client->bytesToWrite() < 128 * 1024) { - writePacket(dataToSend); - } - } - -protected: - void run() - { - QTcpServer server; - server.listen(); - port = server.serverPort(); - ready.release(); - - server.waitForNewConnection(-1); - client = server.nextPendingConnection(); - - writeMore(); - connect(client, SIGNAL(bytesWritten(qint64)), SLOT(writeMore()), Qt::DirectConnection); - - QEventLoop eventLoop; - QTimer::singleShot(timeout, &eventLoop, SLOT(quit())); - - QTime timer; - timer.start(); - eventLoop.exec(); - disconnect(client, SIGNAL(bytesWritten(qint64)), this, 0); - - // wait for the connection to shut down - client->disconnectFromHost(); - if (!client->waitForDisconnected(10000)) - return; - - transferRate = totalBytes * 1000 / timer.elapsed(); - qDebug() << "TimedSender::run" << "receive rate:" << (transferRate / 1024) << "kB/s in" - << timer.elapsed() << "ms"; - } - - void writePacket(const QByteArray &array) - { - client->write(array); - totalBytes += array.size(); - } -}; - -class ThreadedDataReader: public QThread -{ - Q_OBJECT - // used to make the constructor only return after the tcp server started listening - QSemaphore ready; - QTcpSocket *client; - int timeout; - int port; -public: - qint64 transferRate; - ThreadedDataReader() - : port(-1), transferRate(-1) - { - start(); - ready.acquire(); - } - - inline int serverPort() const { return port; } - -protected: - void run() - { - QTcpServer server; - server.listen(); - port = server.serverPort(); - ready.release(); - - server.waitForNewConnection(-1); - client = server.nextPendingConnection(); - - QEventLoop eventLoop; - DataReader reader(client, false); - QObject::connect(client, SIGNAL(disconnected()), &eventLoop, SLOT(quit())); - - QTime timer; - timer.start(); - eventLoop.exec(); - qint64 elapsed = timer.elapsed(); - - transferRate = reader.totalBytes * 1000 / elapsed; - qDebug() << "ThreadedDataReader::run" << "send rate:" << (transferRate / 1024) << "kB/s in" << elapsed << "msec"; - } -}; - -class ThreadedDataReaderHttpServer: public QThread -{ - Q_OBJECT - // used to make the constructor only return after the tcp server started listening - QSemaphore ready; - QTcpSocket *client; - int timeout; - int port; -public: - qint64 transferRate; - ThreadedDataReaderHttpServer() - : port(-1), transferRate(-1) - { - start(); - ready.acquire(); - } - - inline int serverPort() const { return port; } - -protected: - void run() - { - QTcpServer server; - server.listen(); - port = server.serverPort(); - ready.release(); - - server.waitForNewConnection(-1); - client = server.nextPendingConnection(); - client->write("HTTP/1.0 200 OK\r\n"); - client->write("Content-length: 0\r\n"); - client->write("\r\n"); - client->flush(); - - QCoreApplication::processEvents(); - - QEventLoop eventLoop; - DataReader reader(client, false); - QObject::connect(client, SIGNAL(disconnected()), &eventLoop, SLOT(quit())); - - QTime timer; - timer.start(); - eventLoop.exec(); - qint64 elapsed = timer.elapsed(); - - transferRate = reader.totalBytes * 1000 / elapsed; - qDebug() << "ThreadedDataReaderHttpServer::run" << "send rate:" << (transferRate / 1024) << "kB/s in" << elapsed << "msec"; - } -}; - -class HttpDownloadPerformanceClient : QObject { - Q_OBJECT; - QIODevice *device; - public: - HttpDownloadPerformanceClient (QIODevice *dev) : device(dev){ - connect(dev, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); - } - - public slots: - void readyReadSlot() { - device->readAll(); - } - -}; - -class HttpDownloadPerformanceServer : QObject { - Q_OBJECT; - qint64 dataSize; - qint64 dataSent; - QTcpServer server; - QTcpSocket *client; - bool serverSendsContentLength; - bool chunkedEncoding; - -public: - HttpDownloadPerformanceServer (qint64 ds, bool sscl, bool ce) : dataSize(ds), dataSent(0), - client(0), serverSendsContentLength(sscl), chunkedEncoding(ce) { - server.listen(); - connect(&server, SIGNAL(newConnection()), this, SLOT(newConnectionSlot())); - } - - int serverPort() { - return server.serverPort(); - } - -public slots: - - void newConnectionSlot() { - client = server.nextPendingConnection(); - client->setParent(this); - connect(client, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); - connect(client, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWrittenSlot(qint64))); - } - - void readyReadSlot() { - client->readAll(); - client->write("HTTP/1.0 200 OK\n"); - if (serverSendsContentLength) - client->write(QString("Content-Length: " + QString::number(dataSize) + "\n").toAscii()); - if (chunkedEncoding) - client->write(QString("Transfer-Encoding: chunked\n").toAscii()); - client->write("Connection: close\n\n"); - } - - void bytesWrittenSlot(qint64 amount) { - Q_UNUSED(amount); - if (dataSent == dataSize && client) { - // close eventually - - // chunked encoding: we have to send a last "empty" chunk - if (chunkedEncoding) - client->write(QString("0\r\n\r\n").toAscii()); - - client->disconnectFromHost(); - server.close(); - client = 0; - return; - } - - // send data - if (client && client->bytesToWrite() < 100*1024 && dataSent < dataSize) { - qint64 amount = qMin(qint64(16*1024), dataSize - dataSent); - QByteArray data(amount, '@'); - - if (chunkedEncoding) { - client->write(QString(QString("%1").arg(amount,0,16).toUpper() + "\r\n").toAscii()); - client->write(data.constData(), amount); - client->write(QString("\r\n").toAscii()); - } else { - client->write(data.constData(), amount); - } - - dataSent += amount; - } - } -}; - tst_QNetworkReply::tst_QNetworkReply() { @@ -3311,7 +2969,6 @@ void tst_QNetworkReply::ioPostToHttpEmtpyUploadProgress() server.close(); } - void tst_QNetworkReply::rateControl_data() { QTest::addColumn<int>("rate"); @@ -3365,142 +3022,6 @@ void tst_QNetworkReply::rateControl() QVERIFY(sender.transferRate <= maxRate); } -void tst_QNetworkReply::downloadPerformance() -{ - // unlike the above function, this one tries to send as fast as possible - // and measures how fast it was. - TimedSender sender(5000); - QNetworkRequest request("debugpipe://127.0.0.1:" + QString::number(sender.serverPort()) + "/?bare=1"); - QNetworkReplyPtr reply = manager.get(request); - DataReader reader(reply, false); - - QTime loopTime; - connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); - loopTime.start(); - QTestEventLoop::instance().enterLoop(40); - int elapsedTime = loopTime.elapsed(); - sender.wait(); - - qint64 receivedBytes = reader.totalBytes; - qDebug() << "tst_QNetworkReply::downloadPerformance" << "receive rate:" << (receivedBytes * 1000 / elapsedTime / 1024) << "kB/s and" - << elapsedTime << "ms"; -} - -void tst_QNetworkReply::uploadPerformance() -{ - ThreadedDataReader reader; - DataGenerator generator; - - - QNetworkRequest request("debugpipe://127.0.0.1:" + QString::number(reader.serverPort()) + "/?bare=1"); - QNetworkReplyPtr reply = manager.put(request, &generator); - generator.start(); - connect(&reader, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); - QTimer::singleShot(5000, &generator, SLOT(stop())); - - QTestEventLoop::instance().enterLoop(30); - QCOMPARE(reply->error(), QNetworkReply::NoError); - QVERIFY(!QTestEventLoop::instance().timeout()); -} - -void tst_QNetworkReply::httpUploadPerformance() -{ -#ifdef Q_OS_SYMBIAN - // SHow some mercy for non-desktop platform/s - enum {UploadSize = 4*1024*1024}; // 4 MB -#else - enum {UploadSize = 128*1024*1024}; // 128 MB -#endif - ThreadedDataReaderHttpServer reader; - FixedSizeDataGenerator generator(UploadSize); - - QNetworkRequest request(QUrl("http://127.0.0.1:" + QString::number(reader.serverPort()) + "/?bare=1")); - request.setHeader(QNetworkRequest::ContentLengthHeader,UploadSize); - - QNetworkReplyPtr reply = manager.put(request, &generator); - - connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); - - QTime time; - generator.start(); - time.start(); - QTestEventLoop::instance().enterLoop(40); - QCOMPARE(reply->error(), QNetworkReply::NoError); - QVERIFY(!QTestEventLoop::instance().timeout()); - - qint64 elapsed = time.elapsed(); - qDebug() << "tst_QNetworkReply::httpUploadPerformance" << elapsed << "msec, " - << ((UploadSize/1024.0)/(elapsed/1000.0)) << " kB/sec"; - - reader.exit(); - reader.wait(); -} - - -void tst_QNetworkReply::performanceControlRate() -{ - // this is a control comparison for the other two above - // it does the same thing, but instead bypasses the QNetworkAccess system - qDebug() << "The following are the maximum transfer rates that we can get in this system" - " (bypassing QNetworkAccess)"; - - TimedSender sender(5000); - QTcpSocket sink; - sink.connectToHost("127.0.0.1", sender.serverPort()); - DataReader reader(&sink, false); - - QTime loopTime; - connect(&sink, SIGNAL(disconnected()), &QTestEventLoop::instance(), SLOT(exitLoop())); - loopTime.start(); - QTestEventLoop::instance().enterLoop(40); - int elapsedTime = loopTime.elapsed(); - sender.wait(); - - qint64 receivedBytes = reader.totalBytes; - qDebug() << "tst_QNetworkReply::performanceControlRate" << "receive rate:" << (receivedBytes * 1000 / elapsedTime / 1024) << "kB/s and" - << elapsedTime << "ms"; -} - -void tst_QNetworkReply::httpDownloadPerformance_data() -{ - QTest::addColumn<bool>("serverSendsContentLength"); - QTest::addColumn<bool>("chunkedEncoding"); - - QTest::newRow("Server sends no Content-Length") << false << false; - QTest::newRow("Server sends Content-Length") << true << false; - QTest::newRow("Server uses chunked encoding") << false << true; - -} - -void tst_QNetworkReply::httpDownloadPerformance() -{ - QFETCH(bool, serverSendsContentLength); - QFETCH(bool, chunkedEncoding); -#ifdef Q_OS_SYMBIAN - // Show some mercy to non-desktop platform/s - enum {UploadSize = 4*1024*1024}; // 4 MB -#else - enum {UploadSize = 128*1024*1024}; // 128 MB -#endif - HttpDownloadPerformanceServer server(UploadSize, serverSendsContentLength, chunkedEncoding); - - QNetworkRequest request(QUrl("http://127.0.0.1:" + QString::number(server.serverPort()) + "/?bare=1")); - QNetworkReplyPtr reply = manager.get(request); - - connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection); - HttpDownloadPerformanceClient client(reply); - - QTime time; - time.start(); - QTestEventLoop::instance().enterLoop(40); - QCOMPARE(reply->error(), QNetworkReply::NoError); - QVERIFY(!QTestEventLoop::instance().timeout()); - - qint64 elapsed = time.elapsed(); - qDebug() << "tst_QNetworkReply::httpDownloadPerformance" << elapsed << "msec, " - << ((UploadSize/1024.0)/(elapsed/1000.0)) << " kB/sec"; -} - void tst_QNetworkReply::downloadProgress_data() { QTest::addColumn<int>("loopCount"); diff --git a/tests/benchmarks/qnetworkreply/tst_qnetworkreply.cpp b/tests/benchmarks/qnetworkreply/tst_qnetworkreply.cpp index 993db52..1d50013 100644 --- a/tests/benchmarks/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/benchmarks/qnetworkreply/tst_qnetworkreply.cpp @@ -46,11 +46,397 @@ #include <QtNetwork/qnetworkreply.h> #include <QtNetwork/qnetworkrequest.h> #include <QtNetwork/qnetworkaccessmanager.h> +#include <QtNetwork/qtcpsocket.h> +#include <QtNetwork/qtcpserver.h> #include "../../auto/network-settings.h" + +class TimedSender: public QThread +{ + Q_OBJECT + qint64 totalBytes; + QSemaphore ready; + QByteArray dataToSend; + QTcpSocket *client; + int timeout; + int port; +public: + int transferRate; + TimedSender(int ms) + : totalBytes(0), timeout(ms), port(-1), transferRate(-1) + { + dataToSend = QByteArray(16*1024, '@'); + start(); + ready.acquire(); + } + + inline int serverPort() const { return port; } + +private slots: + void writeMore() + { + while (client->bytesToWrite() < 128 * 1024) { + writePacket(dataToSend); + } + } + +protected: + void run() + { + QTcpServer server; + server.listen(); + port = server.serverPort(); + ready.release(); + + server.waitForNewConnection(-1); + client = server.nextPendingConnection(); + + writeMore(); + connect(client, SIGNAL(bytesWritten(qint64)), SLOT(writeMore()), Qt::DirectConnection); + + QEventLoop eventLoop; + QTimer::singleShot(timeout, &eventLoop, SLOT(quit())); + + QTime timer; + timer.start(); + eventLoop.exec(); + disconnect(client, SIGNAL(bytesWritten(qint64)), this, 0); + + // wait for the connection to shut down + client->disconnectFromHost(); + if (!client->waitForDisconnected(10000)) + return; + + transferRate = totalBytes * 1000 / timer.elapsed(); + qDebug() << "TimedSender::run" << "receive rate:" << (transferRate / 1024) << "kB/s in" + << timer.elapsed() << "ms"; + } + + void writePacket(const QByteArray &array) + { + client->write(array); + totalBytes += array.size(); + } +}; + + +class QNetworkReplyPtr: public QSharedPointer<QNetworkReply> +{ +public: + inline QNetworkReplyPtr(QNetworkReply *ptr = 0) + : QSharedPointer<QNetworkReply>(ptr) + { } + + inline operator QNetworkReply *() const { return data(); } +}; + + +class DataReader: public QObject +{ + Q_OBJECT +public: + qint64 totalBytes; + QByteArray data; + QIODevice *device; + bool accumulate; + DataReader(QIODevice *dev, bool acc = true) : totalBytes(0), device(dev), accumulate(acc) + { + connect(device, SIGNAL(readyRead()), SLOT(doRead())); + } + +public slots: + void doRead() + { + QByteArray buffer; + buffer.resize(device->bytesAvailable()); + qint64 bytesRead = device->read(buffer.data(), device->bytesAvailable()); + if (bytesRead == -1) { + QTestEventLoop::instance().exitLoop(); + return; + } + buffer.truncate(bytesRead); + totalBytes += bytesRead; + + if (accumulate) + data += buffer; + } +}; + +class ThreadedDataReader: public QThread +{ + Q_OBJECT + // used to make the constructor only return after the tcp server started listening + QSemaphore ready; + QTcpSocket *client; + int timeout; + int port; +public: + qint64 transferRate; + ThreadedDataReader() + : port(-1), transferRate(-1) + { + start(); + ready.acquire(); + } + + inline int serverPort() const { return port; } + +protected: + void run() + { + QTcpServer server; + server.listen(); + port = server.serverPort(); + ready.release(); + + server.waitForNewConnection(-1); + client = server.nextPendingConnection(); + + QEventLoop eventLoop; + DataReader reader(client, false); + QObject::connect(client, SIGNAL(disconnected()), &eventLoop, SLOT(quit())); + + QTime timer; + timer.start(); + eventLoop.exec(); + qint64 elapsed = timer.elapsed(); + + transferRate = reader.totalBytes * 1000 / elapsed; + qDebug() << "ThreadedDataReader::run" << "send rate:" << (transferRate / 1024) << "kB/s in" << elapsed << "msec"; + } +}; + +class DataGenerator: public QIODevice +{ + Q_OBJECT + enum { Idle, Started, Stopped } state; +public: + DataGenerator() : state(Idle) + { open(ReadOnly); } + + virtual bool isSequential() const { return true; } + virtual qint64 bytesAvailable() const { return state == Started ? 1024*1024 : 0; } + +public slots: + void start() { state = Started; emit readyRead(); } + void stop() { state = Stopped; emit readyRead(); } + +protected: + virtual qint64 readData(char *data, qint64 maxlen) + { + if (state == Stopped) + return -1; // EOF + + // return as many bytes as are wanted + memset(data, '@', maxlen); + return maxlen; + } + virtual qint64 writeData(const char *, qint64) + { return -1; } +}; + +class ThreadedDataReaderHttpServer: public QThread +{ + Q_OBJECT + // used to make the constructor only return after the tcp server started listening + QSemaphore ready; + QTcpSocket *client; + int timeout; + int port; +public: + qint64 transferRate; + ThreadedDataReaderHttpServer() + : port(-1), transferRate(-1) + { + start(); + ready.acquire(); + } + + inline int serverPort() const { return port; } + +protected: + void run() + { + QTcpServer server; + server.listen(); + port = server.serverPort(); + ready.release(); + + server.waitForNewConnection(-1); + client = server.nextPendingConnection(); + client->write("HTTP/1.0 200 OK\r\n"); + client->write("Content-length: 0\r\n"); + client->write("\r\n"); + client->flush(); + + QCoreApplication::processEvents(); + + QEventLoop eventLoop; + DataReader reader(client, false); + QObject::connect(client, SIGNAL(disconnected()), &eventLoop, SLOT(quit())); + + QTime timer; + timer.start(); + eventLoop.exec(); + qint64 elapsed = timer.elapsed(); + + transferRate = reader.totalBytes * 1000 / elapsed; + qDebug() << "ThreadedDataReaderHttpServer::run" << "send rate:" << (transferRate / 1024) << "kB/s in" << elapsed << "msec"; + } +}; + + +class FixedSizeDataGenerator : public QIODevice +{ + Q_OBJECT + enum { Idle, Started, Stopped } state; +public: + FixedSizeDataGenerator(qint64 size) : state(Idle) + { open(ReadOnly | Unbuffered); + toBeGeneratedTotalCount = toBeGeneratedCount = size; + } + + virtual qint64 bytesAvailable() const + { + return state == Started ? toBeGeneratedCount + QIODevice::bytesAvailable() : 0; + } + + virtual bool isSequential() const{ + return false; + } + + virtual bool reset() const{ + return false; + } + + qint64 size() const { + return toBeGeneratedTotalCount; + } + +public slots: + void start() { state = Started; emit readyRead(); } + +protected: + virtual qint64 readData(char *data, qint64 maxlen) + { + memset(data, '@', maxlen); + + if (toBeGeneratedCount <= 0) { + return -1; + } + + qint64 n = qMin(maxlen, toBeGeneratedCount); + toBeGeneratedCount -= n; + + if (toBeGeneratedCount <= 0) { + // make sure this is a queued connection! + emit readChannelFinished(); + } + + return n; + } + virtual qint64 writeData(const char *, qint64) + { return -1; } + + qint64 toBeGeneratedCount; + qint64 toBeGeneratedTotalCount; +}; + +class HttpDownloadPerformanceServer : QObject { + Q_OBJECT; + qint64 dataSize; + qint64 dataSent; + QTcpServer server; + QTcpSocket *client; + bool serverSendsContentLength; + bool chunkedEncoding; + +public: + HttpDownloadPerformanceServer (qint64 ds, bool sscl, bool ce) : dataSize(ds), dataSent(0), + client(0), serverSendsContentLength(sscl), chunkedEncoding(ce) { + server.listen(); + connect(&server, SIGNAL(newConnection()), this, SLOT(newConnectionSlot())); + } + + int serverPort() { + return server.serverPort(); + } + +public slots: + + void newConnectionSlot() { + client = server.nextPendingConnection(); + client->setParent(this); + connect(client, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); + connect(client, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWrittenSlot(qint64))); + } + + void readyReadSlot() { + client->readAll(); + client->write("HTTP/1.0 200 OK\n"); + if (serverSendsContentLength) + client->write(QString("Content-Length: " + QString::number(dataSize) + "\n").toAscii()); + if (chunkedEncoding) + client->write(QString("Transfer-Encoding: chunked\n").toAscii()); + client->write("Connection: close\n\n"); + } + + void bytesWrittenSlot(qint64 amount) { + Q_UNUSED(amount); + if (dataSent == dataSize && client) { + // close eventually + + // chunked encoding: we have to send a last "empty" chunk + if (chunkedEncoding) + client->write(QString("0\r\n\r\n").toAscii()); + + client->disconnectFromHost(); + server.close(); + client = 0; + return; + } + + // send data + if (client && client->bytesToWrite() < 100*1024 && dataSent < dataSize) { + qint64 amount = qMin(qint64(16*1024), dataSize - dataSent); + QByteArray data(amount, '@'); + + if (chunkedEncoding) { + client->write(QString(QString("%1").arg(amount,0,16).toUpper() + "\r\n").toAscii()); + client->write(data.constData(), amount); + client->write(QString("\r\n").toAscii()); + } else { + client->write(data.constData(), amount); + } + + dataSent += amount; + } + } +}; + +class HttpDownloadPerformanceClient : QObject { + Q_OBJECT; + QIODevice *device; + public: + HttpDownloadPerformanceClient (QIODevice *dev) : device(dev){ + connect(dev, SIGNAL(readyRead()), this, SLOT(readyReadSlot())); + } + + public slots: + void readyReadSlot() { + device->readAll(); + } + +}; + + + + class tst_qnetworkreply : public QObject { Q_OBJECT + + QNetworkAccessManager manager; private slots: void httpLatency(); @@ -58,6 +444,14 @@ private slots: void echoPerformance_data(); void echoPerformance(); #endif + + void downloadPerformance(); + void uploadPerformance(); + void performanceControlRate(); + void httpUploadPerformance(); + void httpDownloadPerformance_data(); + void httpDownloadPerformance(); + }; void tst_qnetworkreply::httpLatency() @@ -107,6 +501,142 @@ void tst_qnetworkreply::echoPerformance() } #endif +void tst_qnetworkreply::downloadPerformance() +{ + // unlike the above function, this one tries to send as fast as possible + // and measures how fast it was. + TimedSender sender(5000); + QNetworkRequest request("debugpipe://127.0.0.1:" + QString::number(sender.serverPort()) + "/?bare=1"); + QNetworkReplyPtr reply = manager.get(request); + DataReader reader(reply, false); + + QTime loopTime; + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + loopTime.start(); + QTestEventLoop::instance().enterLoop(40); + int elapsedTime = loopTime.elapsed(); + sender.wait(); + + qint64 receivedBytes = reader.totalBytes; + qDebug() << "tst_QNetworkReply::downloadPerformance" << "receive rate:" << (receivedBytes * 1000 / elapsedTime / 1024) << "kB/s and" + << elapsedTime << "ms"; +} + +void tst_qnetworkreply::uploadPerformance() +{ + ThreadedDataReader reader; + DataGenerator generator; + + + QNetworkRequest request("debugpipe://127.0.0.1:" + QString::number(reader.serverPort()) + "/?bare=1"); + QNetworkReplyPtr reply = manager.put(request, &generator); + generator.start(); + connect(&reader, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTimer::singleShot(5000, &generator, SLOT(stop())); + + QTestEventLoop::instance().enterLoop(30); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QVERIFY(!QTestEventLoop::instance().timeout()); +} + +void tst_qnetworkreply::httpUploadPerformance() +{ +#ifdef Q_OS_SYMBIAN + // SHow some mercy for non-desktop platform/s + enum {UploadSize = 4*1024*1024}; // 4 MB +#else + enum {UploadSize = 128*1024*1024}; // 128 MB +#endif + ThreadedDataReaderHttpServer reader; + FixedSizeDataGenerator generator(UploadSize); + + QNetworkRequest request(QUrl("http://127.0.0.1:" + QString::number(reader.serverPort()) + "/?bare=1")); + request.setHeader(QNetworkRequest::ContentLengthHeader,UploadSize); + + QNetworkReplyPtr reply = manager.put(request, &generator); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + + QTime time; + generator.start(); + time.start(); + QTestEventLoop::instance().enterLoop(40); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QVERIFY(!QTestEventLoop::instance().timeout()); + + qint64 elapsed = time.elapsed(); + qDebug() << "tst_QNetworkReply::httpUploadPerformance" << elapsed << "msec, " + << ((UploadSize/1024.0)/(elapsed/1000.0)) << " kB/sec"; + + reader.exit(); + reader.wait(); +} + + +void tst_qnetworkreply::performanceControlRate() +{ + // this is a control comparison for the other two above + // it does the same thing, but instead bypasses the QNetworkAccess system + qDebug() << "The following are the maximum transfer rates that we can get in this system" + " (bypassing QNetworkAccess)"; + + TimedSender sender(5000); + QTcpSocket sink; + sink.connectToHost("127.0.0.1", sender.serverPort()); + DataReader reader(&sink, false); + + QTime loopTime; + connect(&sink, SIGNAL(disconnected()), &QTestEventLoop::instance(), SLOT(exitLoop())); + loopTime.start(); + QTestEventLoop::instance().enterLoop(40); + int elapsedTime = loopTime.elapsed(); + sender.wait(); + + qint64 receivedBytes = reader.totalBytes; + qDebug() << "tst_QNetworkReply::performanceControlRate" << "receive rate:" << (receivedBytes * 1000 / elapsedTime / 1024) << "kB/s and" + << elapsedTime << "ms"; +} + +void tst_qnetworkreply::httpDownloadPerformance_data() +{ + QTest::addColumn<bool>("serverSendsContentLength"); + QTest::addColumn<bool>("chunkedEncoding"); + + QTest::newRow("Server sends no Content-Length") << false << false; + QTest::newRow("Server sends Content-Length") << true << false; + QTest::newRow("Server uses chunked encoding") << false << true; + +} + +void tst_qnetworkreply::httpDownloadPerformance() +{ + QFETCH(bool, serverSendsContentLength); + QFETCH(bool, chunkedEncoding); +#ifdef Q_OS_SYMBIAN + // Show some mercy to non-desktop platform/s + enum {UploadSize = 4*1024*1024}; // 4 MB +#else + enum {UploadSize = 128*1024*1024}; // 128 MB +#endif + HttpDownloadPerformanceServer server(UploadSize, serverSendsContentLength, chunkedEncoding); + + QNetworkRequest request(QUrl("http://127.0.0.1:" + QString::number(server.serverPort()) + "/?bare=1")); + QNetworkReplyPtr reply = manager.get(request); + + connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection); + HttpDownloadPerformanceClient client(reply); + + QTime time; + time.start(); + QTestEventLoop::instance().enterLoop(40); + QCOMPARE(reply->error(), QNetworkReply::NoError); + QVERIFY(!QTestEventLoop::instance().timeout()); + + qint64 elapsed = time.elapsed(); + qDebug() << "tst_QNetworkReply::httpDownloadPerformance" << elapsed << "msec, " + << ((UploadSize/1024.0)/(elapsed/1000.0)) << " kB/sec"; +} + QTEST_MAIN(tst_qnetworkreply) #include "tst_qnetworkreply.moc" |