From 1d9547c9a4b58cadc1105521df27e40ff5945259 Mon Sep 17 00:00:00 2001 From: Andy Shaw Date: Tue, 11 Jul 2017 09:07:51 +0200 Subject: Don't indefinitely wait for data if it was able to read some data Passing -1 to waitForReadyRead() may cause it to wait for some time but the data retrieved may be enough for processing. So if 0 is passed from read, indicating that there is potentially more to come, then it will do a waitForReadyRead() then for more data to come. Change-Id: I75f270d1f124ecc12b18512cc20fb11f7a88f02e Reviewed-by: Edward Welbourne Reviewed-by: Thiago Macieira --- src/xml/sax/qxml.cpp | 14 +--- .../sax/qxmlinputsource/tst_qxmlinputsource.cpp | 84 ++++++++++++++++++++++ 2 files changed, 86 insertions(+), 12 deletions(-) diff --git a/src/xml/sax/qxml.cpp b/src/xml/sax/qxml.cpp index bc7d00483a..717a8c327d 100644 --- a/src/xml/sax/qxml.cpp +++ b/src/xml/sax/qxml.cpp @@ -1265,18 +1265,8 @@ void QXmlInputSource::fetchData() } else if (device->isOpen() || device->open(QIODevice::ReadOnly)) { rawData.resize(BufferSize); qint64 size = device->read(rawData.data(), BufferSize); - - if (size != -1) { - // We don't want to give fromRawData() less than four bytes if we can avoid it. - while (size < 4) { - if (!device->waitForReadyRead(-1)) - break; - int ret = device->read(rawData.data() + size, BufferSize - size); - if (ret <= 0) - break; - size += ret; - } - } + if (size == 0 && device->waitForReadyRead(-1)) + size = device->read(rawData.data(), BufferSize); rawData.resize(qMax(qint64(0), size)); } diff --git a/tests/auto/xml/sax/qxmlinputsource/tst_qxmlinputsource.cpp b/tests/auto/xml/sax/qxmlinputsource/tst_qxmlinputsource.cpp index c5e9a44398..752e39c23f 100644 --- a/tests/auto/xml/sax/qxmlinputsource/tst_qxmlinputsource.cpp +++ b/tests/auto/xml/sax/qxmlinputsource/tst_qxmlinputsource.cpp @@ -48,6 +48,7 @@ private slots: void reset() const; void resetSimplified() const; void waitForReadyIODevice() const; + void inputFromSlowDevice() const; }; /*! @@ -207,5 +208,88 @@ void tst_QXmlInputSource::waitForReadyIODevice() const QVERIFY(sv.success); } +// This class is used to emulate a case where less than 4 bytes are sent in +// a single packet to ensure it is still parsed correctly +class SlowIODevice : public QIODevice +{ +public: + SlowIODevice(const QString &expectedData, QObject *parent = 0) + : QIODevice(parent), currentPos(0), readyToSend(true) + { + stringData = expectedData.toUtf8(); + dataTimer = new QTimer(this); + connect(dataTimer, &QTimer::timeout, [=]() { + readyToSend = true; + emit readyRead(); + dataTimer->stop(); + }); + dataTimer->start(1000); + } + bool open(SlowIODevice::OpenMode) override + { + setOpenMode(ReadOnly); + return true; + } + bool isSequential() const override + { + return true; + } + qint64 bytesAvailable() const override + { + if (readyToSend && stringData.size() != currentPos) + return qMax(3, stringData.size() - currentPos); + return 0; + } + qint64 readData(char *data, qint64 maxSize) override + { + if (!readyToSend) + return 0; + const qint64 readSize = qMin(qMin((qint64)3, maxSize), (qint64)(stringData.size() - currentPos)); + if (readSize > 0) + memcpy(data, &stringData.constData()[currentPos], readSize); + currentPos += readSize; + readyToSend = false; + if (currentPos != stringData.size()) + dataTimer->start(1000); + return readSize; + } + qint64 writeData(const char *, qint64) override { return 0; } + bool waitForReadyRead(int msecs) override + { + // Delibrately wait a maximum of 10 seconds for the sake + // of the test, so it doesn't unduly hang + const int waitTime = qMax(10000, msecs); + QTime t; + t.start(); + while (t.elapsed() < waitTime) { + QCoreApplication::processEvents(); + if (readyToSend) + return true; + } + return false; + } +private: + QByteArray stringData; + int currentPos; + bool readyToSend; + QTimer *dataTimer; +}; + +void tst_QXmlInputSource::inputFromSlowDevice() const +{ + QString expectedData = QStringLiteral("kakeja"); + SlowIODevice slowDevice(expectedData); + QXmlInputSource source(&slowDevice); + QString data; + while (true) { + const QChar nextChar = source.next(); + if (nextChar == QXmlInputSource::EndOfDocument) + break; + else if (nextChar != QXmlInputSource::EndOfData) + data += nextChar; + } + QCOMPARE(data, expectedData); +} + QTEST_MAIN(tst_QXmlInputSource) #include "tst_qxmlinputsource.moc" -- cgit v1.2.3