From 184d66caa5f7f93b7383319c5c8985524e0dc824 Mon Sep 17 00:00:00 2001 From: Alex Trotsenko Date: Thu, 5 Mar 2015 10:52:17 +0200 Subject: QDataStream: handle incomplete reads from QIODevice This adds a way to resume reading from a stream after a ReadPastEnd error. This is done by introducing a stream read transaction mechanism that keeps read data in an internal buffer and rolls it back on failure. [ChangeLog][QtCore] Added QDataStream startTransaction(), commitTransaction(), rollbackTransaction(), abortTransaction() functions to support read transactions. Task-number: QTBUG-44418 Change-Id: Ibf946e1939a5573c4182fea7e26608947218c2d9 Reviewed-by: Oswald Buddenhagen --- .../corelib/io/qdatastream/tst_qdatastream.cpp | 164 +++++++++++++++++++++ 1 file changed, 164 insertions(+) (limited to 'tests/auto/corelib/io/qdatastream/tst_qdatastream.cpp') diff --git a/tests/auto/corelib/io/qdatastream/tst_qdatastream.cpp b/tests/auto/corelib/io/qdatastream/tst_qdatastream.cpp index 729cf6547e..d63a7a213b 100644 --- a/tests/auto/corelib/io/qdatastream/tst_qdatastream.cpp +++ b/tests/auto/corelib/io/qdatastream/tst_qdatastream.cpp @@ -186,6 +186,11 @@ private slots: void floatingPointNaN(); + void transaction_data(); + void transaction(); + void nestedTransactionsResult_data(); + void nestedTransactionsResult(); + private: void writebool(QDataStream *s); void writeQBitArray(QDataStream *s); @@ -3190,6 +3195,165 @@ void tst_QDataStream::floatingPointPrecision() } +void tst_QDataStream::transaction_data() +{ + QTest::addColumn("i8Data"); + QTest::addColumn("i16Data"); + QTest::addColumn("i32Data"); + QTest::addColumn("i64Data"); + QTest::addColumn("bData"); + QTest::addColumn("fData"); + QTest::addColumn("dData"); + QTest::addColumn("strData"); + QTest::addColumn("rawData"); + + QTest::newRow("1") << qint8(1) << qint16(2) << qint32(3) << qint64(4) << true << 5.0f + << double(6.0) << QByteArray("Hello world!") << QByteArray("Qt rocks!"); + QTest::newRow("2") << qint8(1 << 6) << qint16(1 << 14) << qint32(1 << 30) << qint64Data(3) << false << 123.0f + << double(234.0) << stringData(5).toUtf8() << stringData(6).toUtf8(); + QTest::newRow("3") << qint8(-1) << qint16(-2) << qint32(-3) << qint64(-4) << true << -123.0f + << double(-234.0) << stringData(3).toUtf8() << stringData(4).toUtf8(); +} + +void tst_QDataStream::transaction() +{ + QByteArray testBuffer; + + QFETCH(qint8, i8Data); + QFETCH(qint16, i16Data); + QFETCH(qint32, i32Data); + QFETCH(qint64, i64Data); + QFETCH(bool, bData); + QFETCH(float, fData); + QFETCH(double, dData); + QFETCH(QByteArray, strData); + QFETCH(QByteArray, rawData); + + { + QDataStream stream(&testBuffer, QIODevice::WriteOnly); + + stream << i8Data << i16Data << i32Data << i64Data + << bData << fData << dData << strData.constData(); + stream.writeRawData(rawData.constData(), rawData.size()); + } + + for (int splitPos = 0; splitPos <= testBuffer.size(); ++splitPos) { + QByteArray readBuffer(testBuffer.left(splitPos)); + SequentialBuffer dev(&readBuffer); + dev.open(QIODevice::ReadOnly); + QDataStream stream(&dev); + + qint8 i8; + qint16 i16; + qint32 i32; + qint64 i64; + bool b; + float f; + double d; + char *str; + QByteArray raw(rawData.size(), 0); + + forever { + stream.startTransaction(); + stream >> i8 >> i16 >> i32 >> i64 >> b >> f >> d >> str; + stream.readRawData(raw.data(), raw.size()); + + if (stream.commitTransaction()) + break; + + QVERIFY(stream.status() == QDataStream::ReadPastEnd); + QVERIFY(splitPos == 0 || !stream.atEnd()); + QVERIFY(readBuffer.size() < testBuffer.size()); + delete [] str; + raw.fill(0); + readBuffer.append(testBuffer.right(testBuffer.size() - splitPos)); + } + + QVERIFY(stream.atEnd()); + QCOMPARE(i8, i8Data); + QCOMPARE(i16, i16Data); + QCOMPARE(i32, i32Data); + QCOMPARE(i64, i64Data); + QCOMPARE(b, bData); + QCOMPARE(f, fData); + QCOMPARE(d, dData); + QVERIFY(strData == str); + delete [] str; + QCOMPARE(raw, rawData); + } +} + +void tst_QDataStream::nestedTransactionsResult_data() +{ + QTest::addColumn("commitFirst"); + QTest::addColumn("rollbackFirst"); + QTest::addColumn("commitSecond"); + QTest::addColumn("rollbackSecond"); + QTest::addColumn("successExpected"); + QTest::addColumn("expectedAtEnd"); + QTest::addColumn("expectedStatus"); + + QTest::newRow("1") << false << false << false << false + << false << true << int(QDataStream::ReadCorruptData); + QTest::newRow("2") << false << false << false << true + << false << true << int(QDataStream::ReadCorruptData); + QTest::newRow("3") << false << false << true << false + << false << true << int(QDataStream::ReadCorruptData); + + QTest::newRow("4") << false << true << false << false + << false << true << int(QDataStream::ReadCorruptData); + QTest::newRow("5") << false << true << false << true + << false << false << int(QDataStream::ReadPastEnd); + QTest::newRow("6") << false << true << true << false + << false << false << int(QDataStream::ReadPastEnd); + + QTest::newRow("7") << true << false << false << false + << false << true << int(QDataStream::ReadCorruptData); + QTest::newRow("8") << true << false << false << true + << false << false << int(QDataStream::ReadPastEnd); + QTest::newRow("9") << true << false << true << false + << true << true << int(QDataStream::Ok); +} + +void tst_QDataStream::nestedTransactionsResult() +{ + QByteArray testBuffer(1, 0); + QDataStream stream(&testBuffer, QIODevice::ReadOnly); + uchar c; + + QFETCH(bool, commitFirst); + QFETCH(bool, rollbackFirst); + QFETCH(bool, commitSecond); + QFETCH(bool, rollbackSecond); + QFETCH(bool, successExpected); + QFETCH(bool, expectedAtEnd); + QFETCH(int, expectedStatus); + + stream.startTransaction(); + stream.startTransaction(); + stream >> c; + + if (commitFirst) + QVERIFY(stream.commitTransaction()); + else if (rollbackFirst) + stream.rollbackTransaction(); + else + stream.abortTransaction(); + + stream.startTransaction(); + + if (commitSecond) + QCOMPARE(stream.commitTransaction(), commitFirst); + else if (rollbackSecond) + stream.rollbackTransaction(); + else + stream.abortTransaction(); + + QCOMPARE(stream.commitTransaction(), successExpected); + QCOMPARE(stream.atEnd(), expectedAtEnd); + QCOMPARE(int(stream.status()), expectedStatus); +} + QTEST_MAIN(tst_QDataStream) #include "tst_qdatastream.moc" -- cgit v1.2.3