Clarify readLine() behavior on sequential devices

QIODevice::readLine() can also return partial lines, which was not
properly documented. Add an autotest for QLocalSocket to illustrate
and test this behavior.

Pick-to: 6.2
Change-Id: Ia2c1c438cc68d2672d34881e11fdf7837232f3b4
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
This commit is contained in:
Alex Trotsenko 2021-07-03 16:03:54 +03:00
parent d9a9eca54d
commit 03272e601c
2 changed files with 108 additions and 1 deletions

View File

@ -1320,12 +1320,17 @@ QByteArray QIODevice::readAll()
newline will not be inserted into the buffer. On windows newline
characters are replaced with '\\n'.
Note that on sequential devices, data may not be immediately available,
which may result in a partial line being returned. By calling the
canReadLine() function before reading, you can check whether a complete
line (including the newline character) can be read.
This function calls readLineData(), which is implemented using
repeated calls to getChar(). You can provide a more efficient
implementation by reimplementing readLineData() in your own
subclass.
\sa getChar(), read(), write()
\sa getChar(), read(), canReadLine(), write()
*/
qint64 QIODevice::readLine(char *data, qint64 maxSize)
{

View File

@ -34,6 +34,7 @@
#include <QWaitCondition>
#include <QLoggingCategory>
#include <QMutex>
#include <QList>
#include <qtextstream.h>
#include <qdatastream.h>
@ -64,6 +65,7 @@ class tst_QLocalSocket : public QObject
Q_OBJECT
public:
using ByteArrayList = QList<QByteArray>;
tst_QLocalSocket();
private slots:
@ -90,6 +92,9 @@ private slots:
void sendData_data();
void sendData();
void readLine_data();
void readLine();
void readBufferOverflow();
void simpleCommandProtocol1();
@ -704,6 +709,103 @@ void tst_QLocalSocket::sendData()
QCOMPARE(spy.count(), (canListen ? 1 : 0));
}
void tst_QLocalSocket::readLine_data()
{
QTest::addColumn<ByteArrayList>("input");
QTest::addColumn<ByteArrayList>("output");
QTest::addColumn<int>("maxSize");
QTest::addColumn<bool>("wholeLinesOnly");
QTest::newRow("0") << ByteArrayList{ "\n", "A", "\n", "B", "B", "A", "\n" }
<< ByteArrayList{ "\n", "", "", "A\n", "", "", "", "",
"BBA\n", "", "" }
<< 80 << true;
QTest::newRow("1") << ByteArrayList{ "A", "\n", "\n", "B", "B", "\n", "A", "A" }
<< ByteArrayList{ "", "A\n", "", "\n", "", "", "", "BB\n",
"", "", "", "AA", "" }
<< 80 << true;
QTest::newRow("2") << ByteArrayList{ "\nA\nA\n" }
<< ByteArrayList{ "\n", "A", "\n", "A", "\n", "", "" }
<< 1 << false;
QTest::newRow("3") << ByteArrayList{ "A\n\n\nA", "A" }
<< ByteArrayList{ "A\n", "\n", "\n", "A", "", "A", "", "" }
<< 2 << false;
QTest::newRow("4") << ByteArrayList{ "He", "ll", "o\n", " \n", "wo", "rl", "d", "!\n" }
<< ByteArrayList{ "", "Hel", "", "lo\n", "", " \n", "", "", "wor",
"", "", "ld!", "\n", "", "" }
<< 3 << true;
QTest::newRow("5") << ByteArrayList{ "Hello\n world!" }
<< ByteArrayList{ "Hello\n", "", " world!", "" }
<< 80 << true;
QTest::newRow("6") << ByteArrayList{ "\nHello", " \n", " wor", "ld!\n" }
<< ByteArrayList{ "\n", "Hell", "o", "", " \n", "", " wor", "",
"ld!\n", "", "" }
<< 4 << false;
QTest::newRow("7") << ByteArrayList{ "Hello\n world", "!" }
<< ByteArrayList{ "Hello\n", " world", "", "!", "", "" }
<< 80 << false;
}
void tst_QLocalSocket::readLine()
{
QFETCH(ByteArrayList, input);
QFETCH(ByteArrayList, output);
QFETCH(int, maxSize);
QFETCH(bool, wholeLinesOnly);
const QString serverName = QLatin1String("tst_localsocket");
LocalServer server;
QVERIFY(server.listen(serverName));
LocalSocket client;
client.connectToServer(serverName);
QVERIFY(server.waitForNewConnection());
QLocalSocket *serverSocket = server.nextPendingConnection();
QVERIFY(serverSocket);
QCOMPARE(client.state(), QLocalSocket::ConnectedState);
ByteArrayList result;
qsizetype pos = 0;
do {
// This test assumes that such small chunks of data are synchronously
// delivered to the receiver on all supported platforms.
if (pos < input.size()) {
const QByteArray &chunk = input.at(pos);
QCOMPARE(serverSocket->write(chunk), qint64(chunk.size()));
QVERIFY(serverSocket->waitForBytesWritten());
QCOMPARE(serverSocket->bytesToWrite(), qint64(0));
QVERIFY(client.waitForReadyRead());
} else {
serverSocket->close();
QVERIFY(!client.waitForReadyRead());
}
while (!wholeLinesOnly || (client.bytesAvailable() >= qint64(maxSize))
|| client.canReadLine() || (pos == input.size())) {
const bool chunkEmptied = (client.bytesAvailable() == 0);
QByteArray line(maxSize, Qt::Uninitialized);
const qint64 readResult = client.readLine(line.data(), maxSize + 1);
if (chunkEmptied) {
if (pos == input.size())
QCOMPARE(readResult, qint64(-1));
else
QCOMPARE(readResult, qint64(0));
break;
}
QVERIFY((readResult > 0) && (readResult <= maxSize));
line.resize(readResult);
result.append(line);
}
result.append(QByteArray());
} while (++pos <= input.size());
QCOMPARE(client.state(), QLocalSocket::UnconnectedState);
QCOMPARE(result, output);
}
void tst_QLocalSocket::readBufferOverflow()
{
const int readBufferSize = 128;