diff options
Diffstat (limited to 'tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp')
-rw-r--r-- | tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp | 307 |
1 files changed, 253 insertions, 54 deletions
diff --git a/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp b/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp index 3dd4b5114c..63cfbce75f 100644 --- a/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp +++ b/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp @@ -1,44 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2018 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** 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 Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** 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-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/qcborstream.h> -#include <QtTest> +#include <QTest> +#include <QBuffer> class tst_QCborStreamReader : public QObject { @@ -73,6 +38,8 @@ private Q_SLOTS: void next(); void validation_data(); void validation(); + void hugeDeviceValidation_data(); + void hugeDeviceValidation(); void recursionLimit_data(); void recursionLimit(); @@ -80,6 +47,11 @@ private Q_SLOTS: void addData_singleElement(); void addData_complex_data() { arrays_data(); } void addData_complex(); + + void duplicatedData_data() { arrays_data(); } + void duplicatedData(); + void extraData_data() { arrays_data(); } + void extraData(); }; #define FOR_CBOR_TYPE(F) \ @@ -114,7 +86,7 @@ template<> char *toString<QCborStreamReader::Type>(const QCborStreamReader::Type QT_END_NAMESPACE // Get the data from TinyCBOR (see src/3rdparty/tinycbor/tests/parser/data.cpp) -#include "data.cpp" +#include "parser/data.cpp" void tst_QCborStreamReader::initTestCase_data() { @@ -288,7 +260,10 @@ void tst_QCborStreamReader::integers() void escapedAppendTo(QString &result, const QByteArray &data) { - result += "h'" + QString::fromLatin1(data.toHex()) + '\''; + QByteArray hex = + data.size() < 512*1024 ? data.toHex() : + "data of size " + QByteArray::number(data.size()); + result += "h'" + QString::fromLatin1(hex) + '\''; } void escapedAppendTo(QString &result, const QString &data) @@ -354,7 +329,7 @@ template <typename T> static inline bool canConvertTo(double v) // integrals to floating-point with loss of precision has implementation- // defined behavior whether the next higher or next lower is returned; // converting FP to integral is UB if it can't be represented.; - Q_STATIC_ASSERT(std::numeric_limits<T>::is_integer); + static_assert(std::numeric_limits<T>::is_integer); double supremum = ldexp(1, std::numeric_limits<T>::digits); if (v >= supremum) @@ -480,6 +455,28 @@ static QString parseOne(QCborStreamReader &reader) return result; } +static QString parse(QCborStreamReader &reader, const QByteArray &data) +{ + qint64 oldPos = 0; + if (QIODevice *dev = reader.device()) + oldPos = dev->pos(); + + QString r = parseOne(reader); + if (r.isEmpty()) + return r; + + if (reader.currentOffset() - oldPos != data.size()) + r = QString("Number of parsed bytes (%1) not expected (%2)") + .arg(reader.currentOffset()).arg(data.size()); + if (QIODevice *dev = reader.device()) { + if (dev->pos() - oldPos != data.size()) + r = QString("QIODevice not advanced (%1) as expected (%2)") + .arg(dev->pos()).arg(data.size()); + } + + return r; +} + bool parseNonRecursive(QString &result, bool &printingStringChunks, QCborStreamReader &reader) { while (reader.lastError() == QCborError::NoError) { @@ -612,13 +609,13 @@ void tst_QCborStreamReader::fixed() } QVERIFY(reader.isValid()); QCOMPARE(reader.lastError(), QCborError::NoError); - QCOMPARE(parseOne(reader), expected); + QCOMPARE(parse(reader, data), expected); // verify that we can re-read reader.reset(); QVERIFY(reader.isValid()); QCOMPARE(reader.lastError(), QCborError::NoError); - QCOMPARE(parseOne(reader), expected); + QCOMPARE(parse(reader, data), expected); } void tst_QCborStreamReader::strings_data() @@ -658,6 +655,7 @@ void tst_QCborStreamReader::strings() QCOMPARE(reader.currentStringChunkSize(), qsizetype(reader.length())); int chunks = 0; + QByteArray fullString; forever { QCborStreamReader::StringResult<QByteArray> controlData; if (reader.isString()) { @@ -668,6 +666,7 @@ void tst_QCborStreamReader::strings() controlData = controlReader.readByteArray(); } QVERIFY(controlData.status != QCborStreamReader::Error); + fullString += controlData.data; for (int i = 0; i < 10; ++i) { // this call must work several times with the same result @@ -690,6 +689,43 @@ void tst_QCborStreamReader::strings() if (!isChunked) QCOMPARE(chunks, 1); + + // Now re-do and compare with toString() and toByteArray(), against + // the control data we calculated above + reader.reset(); + QVERIFY(reader.isString() || reader.isByteArray()); + if (reader.isByteArray()) { + QByteArray prefix("some prefix"); + QByteArray ba = prefix; + QVERIFY(reader.readAndAppendToByteArray(ba)); + QCOMPARE(ba, prefix + fullString); + } else { + QString prefix("some prefix"); + QString str = prefix; + QVERIFY(reader.readAndAppendToString(str)); + QCOMPARE(str, prefix + QString::fromUtf8(fullString)); + } + + // Re-do again using the UTF-8 interface. + reader.reset(); + QVERIFY(reader.isString() || reader.isByteArray()); + if (reader.isString()) { + QByteArray prefix("some prefix"); + QByteArray utf8 = prefix; + QVERIFY(reader.readAndAppendToUtf8String(utf8)); + QCOMPARE(utf8, prefix + fullString); + + reader.reset(); + fullString = prefix; + forever { + auto r = reader.readUtf8String(); + QCOMPARE_NE(r.status, QCborStreamReader::Error); + fullString += r.data; + if (r.status == QCborStreamReader::EndOfString) + break; + } + QCOMPARE(fullString, utf8); + } } void tst_QCborStreamReader::tags_data() @@ -721,7 +757,7 @@ void tst_QCborStreamReader::emptyContainers() QCOMPARE(reader.lastError(), QCborError::NoError); if (reader.isLengthKnown()) QCOMPARE(reader.length(), 0U); - QCOMPARE(parseOne(reader), expected); + QCOMPARE(parse(reader, data), expected); // verify that we can re-read reader.reset(); @@ -729,7 +765,7 @@ void tst_QCborStreamReader::emptyContainers() QCOMPARE(reader.lastError(), QCborError::NoError); if (reader.isLengthKnown()) QCOMPARE(reader.length(), 0U); - QCOMPARE(parseOne(reader), expected); + QCOMPARE(parse(reader, data), expected); } void tst_QCborStreamReader::arrays_data() @@ -758,7 +794,7 @@ static void checkContainer(int len, const QByteArray &data, const QString &expec QVERIFY(reader.isLengthKnown()); QCOMPARE(reader.length(), uint(len)); } - QCOMPARE(parseOne(reader), expected); + QCOMPARE(parse(reader, data), expected); // verify that we can re-read reader.reset(); @@ -768,7 +804,7 @@ static void checkContainer(int len, const QByteArray &data, const QString &expec QVERIFY(reader.isLengthKnown()); QCOMPARE(reader.length(), uint(len)); } - QCOMPARE(parseOne(reader), expected); + QCOMPARE(parse(reader, data), expected); } void tst_QCborStreamReader::arrays() @@ -875,16 +911,26 @@ void tst_QCborStreamReader::next() QVERIFY(doit("\xbf\x9f\1\xff\x9f" + data + "\xff\xff")); } +#include "../cborlargedatavalidation.cpp" + void tst_QCborStreamReader::validation_data() { + // Add QCborStreamReader-specific limitations due to use of QByteArray and + // QString, which are allocated by QArrayData::allocate(). + const qsizetype MaxInvalid = std::numeric_limits<QByteArray::size_type>::max(); + const qsizetype MinInvalid = QByteArray::max_size() + 1; + addValidationColumns(); - addValidationData(); + addValidationData(MinInvalid); + addValidationLargeData(MinInvalid, MaxInvalid); } void tst_QCborStreamReader::validation() { QFETCH_GLOBAL(bool, useDevice); QFETCH(QByteArray, data); + QFETCH(CborError, expectedError); + QCborError error = { QCborError::Code(expectedError) }; QBuffer buffer(&data); QCborStreamReader reader(data); @@ -892,13 +938,103 @@ void tst_QCborStreamReader::validation() buffer.open(QIODevice::ReadOnly); reader.setDevice(&buffer); } - parseOne(reader); - QVERIFY(reader.lastError() != QCborError::NoError); + parse(reader, data); + QCOMPARE(reader.lastError(), error); // next() should fail reader.reset(); QVERIFY(!reader.next()); - QVERIFY(reader.lastError() != QCborError::NoError); + QCOMPARE(reader.lastError(), error); + + // check toString() and toByteArray() too + if (reader.isString() || reader.isByteArray()) { + reader.reset(); + if (reader.isString()) { + QString prefix = "some prefix"; + QString str = prefix; + QVERIFY(!reader.readAndAppendToString(str)); + QVERIFY(str.startsWith(prefix)); // but may have decoded some + } else if (reader.isByteArray()) { + QByteArray prefix = "some prefix"; + QByteArray ba = prefix; + QVERIFY(!reader.readAndAppendToByteArray(ba)); + QVERIFY(ba.startsWith(prefix)); // but may have decoded some + } + QCOMPARE(reader.lastError(), error); + + reader.reset(); + if (reader.isString()) + QVERIFY(reader.readAllString().isNull()); + else + QVERIFY(reader.readAllByteArray().isNull()); + } + + reader.reset(); + + // and the UTF-8 API + if (reader.isString()) { + QByteArray prefix = "some prefix"; + QByteArray ba = prefix; + QVERIFY(!reader.readAndAppendToUtf8String(ba)); + QVERIFY(ba.startsWith(prefix)); // but may have decoded some + QCOMPARE(reader.lastError(), error); + + reader.reset(); + QVERIFY(reader.readAllUtf8String().isNull()); + + reader.reset(); + auto r = reader.readUtf8String(); + for ( ; r.status == QCborStreamReader::Ok; r = reader.readUtf8String()) { + // while the data is valid... + QVERIFY(!r.data.isNull()); + } + QCOMPARE_NE(r.status, QCborStreamReader::EndOfString); + QCOMPARE(reader.lastError(), error); + } +} + +void tst_QCborStreamReader::hugeDeviceValidation_data() +{ + addValidationHugeDevice(QByteArray::max_size() + 1, QString::max_size() + 1); +} + +void tst_QCborStreamReader::hugeDeviceValidation() +{ + QFETCH_GLOBAL(bool, useDevice); + if (!useDevice) + return; + +#if (defined(__SANITIZE_ADDRESS__) || __has_feature(address_sanitizer)) + if ( qstrcmp(QTest::currentDataTag(), "bytearray-just-too-big") == 0 + || qstrcmp(QTest::currentDataTag(), "string-just-too-big") == 0) + QSKIP("This test tries to allocate a huge memory buffer," + " which Address Sanitizer flags as a problem"); +#endif +#if defined(Q_OS_WASM) + QSKIP("This test tries to allocate a huge memory buffer," + " causes problem on WebAssembly platform which has limited resources."); +#endif // Q_OS_WASM + + QFETCH(QSharedPointer<QIODevice>, device); + QFETCH(CborError, expectedError); + QFETCH(CborError, expectedValidationError); + QCborError error = { QCborError::Code(expectedError) }; + + device->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + QCborStreamReader reader(device.data()); + + QVERIFY(parseOne(reader).isEmpty()); + QCOMPARE(reader.lastError(), error); + + reader.reset(); + error = { QCborError::Code(expectedValidationError) }; + if (error == QCborError{}) { + // this test actually succeeds, so don't do it to avoid large memory consumption + } else { + // next() should fail + QVERIFY(!reader.next()); + QCOMPARE(reader.lastError(), error); + } } static const int Recursions = 3; @@ -997,7 +1133,7 @@ void tst_QCborStreamReader::addData_singleElement() reader.addData(data.constData() + i, 1); } - parseOne(reader); + parse(reader, data); QCOMPARE(reader.lastError(), QCborError::EndOfFile); } @@ -1009,7 +1145,7 @@ void tst_QCborStreamReader::addData_singleElement() reader.addData(data.right(1)); } QCOMPARE(reader.lastError(), QCborError::NoError); - QCOMPARE(parseOne(reader), expected); + QCOMPARE(parse(reader, data), expected); } void tst_QCborStreamReader::addData_complex() @@ -1085,6 +1221,69 @@ void tst_QCborStreamReader::addData_complex() "{1, [" + expected + ", " + expected + "]}"); } +void tst_QCborStreamReader::duplicatedData() +{ + QFETCH_GLOBAL(bool, useDevice); + QFETCH(QByteArray, data); + QFETCH(QString, expected); + removeIndicators(expected); + + // double the data up + QByteArray doubledata = data + data; + + QBuffer buffer(&doubledata); + QCborStreamReader reader(doubledata); + if (useDevice) { + buffer.open(QIODevice::ReadOnly); + reader.setDevice(&buffer); + } + QVERIFY(reader.isValid()); + QCOMPARE(reader.lastError(), QCborError::NoError); + QCOMPARE(parse(reader, data), expected); // yes, data + + QVERIFY(reader.currentOffset() < doubledata.size()); + if (useDevice) { + reader.setDevice(&buffer); + QVERIFY(reader.isValid()); + QCOMPARE(reader.lastError(), QCborError::NoError); + QCOMPARE(parse(reader, data), expected); + QCOMPARE(buffer.pos(), doubledata.size()); + } else { + // there's no reader.setData() + } +} + +void tst_QCborStreamReader::extraData() +{ + QFETCH_GLOBAL(bool, useDevice); + QFETCH(QByteArray, data); + QFETCH(QString, expected); + removeIndicators(expected); + + QByteArray extension(9, '\0'); + + // stress test everything with extra bytes (just one byte changing; + // TinyCBOR used to have a bug where the next byte got sometimes read) + for (int c = '\0'; c < 0x100; ++c) { + extension[0] = c; + QByteArray extendeddata = data + extension; + + QBuffer buffer(&extendeddata); + QCborStreamReader reader(extendeddata); + if (useDevice) { + buffer.open(QIODevice::ReadOnly); + reader.setDevice(&buffer); + } + QVERIFY(reader.isValid()); + QCOMPARE(reader.lastError(), QCborError::NoError); + QCOMPARE(parse(reader, data), expected); // yes, data + + // if we were a parser, we could parse the next payload + if (useDevice) + QCOMPARE(buffer.readAll(), extension); + } +} + QTEST_MAIN(tst_QCborStreamReader) |