diff options
Diffstat (limited to 'tests/auto/corelib/serialization')
34 files changed, 2416 insertions, 997 deletions
diff --git a/tests/auto/corelib/serialization/CMakeLists.txt b/tests/auto/corelib/serialization/CMakeLists.txt index 576261fcfe..3792336255 100644 --- a/tests/auto/corelib/serialization/CMakeLists.txt +++ b/tests/auto/corelib/serialization/CMakeLists.txt @@ -1,18 +1,20 @@ -# Generated from serialization.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(json) add_subdirectory(qcborstreamreader) add_subdirectory(qcborstreamwriter) -add_subdirectory(qcborvalue) +if(NOT WASM) + add_subdirectory(qcborvalue) +endif() add_subdirectory(qcborvalue_json) if(TARGET Qt::Gui) add_subdirectory(qdatastream) add_subdirectory(qdatastream_core_pixmap) endif() -if(TARGET Qt::Network) +if(TARGET Qt::Network AND NOT WASM) add_subdirectory(qtextstream) endif() -# QTBUG-87671 # special case -if(TARGET Qt::Network AND TARGET Qt::Xml AND NOT ANDROID AND NOT INTEGRITY) +if(TARGET Qt::Gui AND TARGET Qt::Network AND TARGET Qt::Xml AND NOT INTEGRITY AND NOT QNX AND NOT WASM) add_subdirectory(qxmlstream) endif() diff --git a/tests/auto/corelib/serialization/cborlargedatavalidation.cpp b/tests/auto/corelib/serialization/cborlargedatavalidation.cpp index 9d1eb9a81b..2fe1012f12 100644 --- a/tests/auto/corelib/serialization/cborlargedatavalidation.cpp +++ b/tests/auto/corelib/serialization/cborlargedatavalidation.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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 <QTest> #include <QtEndian> @@ -62,7 +26,6 @@ protected: qint64 readData(char *data, qint64 maxlen) override; qint64 writeData(const char *, qint64) override { return -1; } }; -}; qint64 LargeIODevice::readData(char *data, qint64 maxlen) { @@ -154,3 +117,4 @@ void addValidationHugeDevice(qsizetype byteArrayInvalid, qsizetype stringInvalid addSize("4GB", quint64(1) << 32); addSize("max", std::numeric_limits<qint64>::max() - sizeof(buf)); } +} // namespace diff --git a/tests/auto/corelib/serialization/json/CMakeLists.txt b/tests/auto/corelib/serialization/json/CMakeLists.txt index 9022ed9f3d..c73a99a3b8 100644 --- a/tests/auto/corelib/serialization/json/CMakeLists.txt +++ b/tests/auto/corelib/serialization/json/CMakeLists.txt @@ -1,42 +1,35 @@ -# Generated from json.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_json Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_json LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +set(json_resource_files + "bom.json" + "test.json" + "test2.json" + "test3.json" + "simple.duplicates.json" + "test.duplicates.json" + "test3.duplicates.json" +) + qt_internal_add_test(tst_json SOURCES tst_qtjson.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate + Qt::TestPrivate + TESTDATA ${json_resource_files} ) -## Scopes: -##################################################################### - -#### Keys ignored in scope 2:.:.:json.pro:NOT ANDROID: -# TESTDATA = "bom.json" "test.json" "test3.json" "test2.json" - -if(ANDROID OR INTEGRITY) - # Resources: - set(json_resource_files - "bom.json" - "test.json" - "test2.json" - "test3.json" - "simple.duplicates.json" - "test.duplicates.json" - "test3.duplicates.json" - ) - - qt_internal_add_resource(tst_json "json" - PREFIX - "/" - FILES - ${json_resource_files} - ) -endif() - qt_internal_extend_target(tst_json CONDITION NOT QT_FEATURE_doubleconversion AND NOT QT_FEATURE_system_doubleconversion DEFINES QT_NO_DOUBLECONVERSION diff --git a/tests/auto/corelib/serialization/json/tst_qtjson.cpp b/tests/auto/corelib/serialization/json/tst_qtjson.cpp index 72dfc4a47d..54ef9be4f2 100644 --- a/tests/auto/corelib/serialization/json/tst_qtjson.cpp +++ b/tests/auto/corelib/serialization/json/tst_qtjson.cpp @@ -1,33 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Copyright (C) 2021 Intel Corporation. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2022 The Qt Company Ltd. +// Copyright (C) 2022 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> +#include <QtTest/private/qcomparisontesthelper_p.h> +#include <QMap> +#include <QVariantList> QT_WARNING_DISABLE_DEPRECATED @@ -50,6 +28,7 @@ class tst_QtJson: public QObject private Q_SLOTS: void initTestCase(); + void compareCompiles(); void testValueSimple(); void testNumbers(); void testNumbers_2(); @@ -70,9 +49,12 @@ private Q_SLOTS: void testArrayNested(); void testArrayNestedEmpty(); void testArrayComfortOperators(); + void testArrayEquality_data(); + void testArrayEquality(); void testObjectNestedEmpty(); void testValueRef(); + void testValueRefComparison(); void testObjectIteration(); void testArrayIteration(); @@ -105,6 +87,7 @@ private Q_SLOTS: void toJson(); void toJsonSillyNumericValues(); void toJsonLargeNumericValues(); + void toJsonDenormalValues(); void fromJson(); void fromJsonErrors(); void parseNumbers(); @@ -190,6 +173,31 @@ void tst_QtJson::initTestCase() testDataDir = QCoreApplication::applicationDirPath(); } +void tst_QtJson::compareCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<QJsonArray>(); + QTestPrivate::testAllComparisonOperatorsCompile<QJsonArray::iterator>(); + QTestPrivate::testAllComparisonOperatorsCompile<QJsonArray::const_iterator>(); + QTestPrivate::testAllComparisonOperatorsCompile<QJsonArray::iterator, + QJsonArray::const_iterator>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonDocument>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonObject>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonObject::iterator>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonObject::const_iterator>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonValue>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonValueConstRef>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonValueRef>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonArray, QJsonValue>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonObject, QJsonValue>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonObject, QJsonValueConstRef>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonObject, QJsonValueRef>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonValueConstRef, QJsonValue>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonValueRef, QJsonValue>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonValueRef, QJsonValueConstRef>(); + QTestPrivate::testEqualityOperatorsCompile<QJsonObject::iterator, + QJsonObject::const_iterator>(); +} + void tst_QtJson::testValueSimple() { QJsonObject object; @@ -392,17 +400,21 @@ void tst_QtJson::testNumbers_2() QVERIFY2(floatValues[power] == floatValues_1[power], QString("floatValues[%1] != floatValues_1[%1]").arg(power).toLatin1()); } + QT_TEST_EQUALITY_OPS(jDocument1, jDocument2, true); // The last value is below min denorm and should round to 0, everything else should contain a value QVERIFY2(floatValues_1[1075] == 0, "Value after min denorm should round to 0"); // Validate the last actual value is min denorm QVERIFY2(floatValues_1[1074] == 4.9406564584124654417656879286822e-324, QString("Min denorm value is incorrect: %1").arg(floatValues_1[1074]).toLatin1()); - // Validate that every value is half the value before it up to 1 - for (int index = 1074; index > 0; index--) { - QVERIFY2(floatValues_1[index] != 0, QString("2**- %1 should not be 0").arg(index).toLatin1()); - - QVERIFY2(floatValues_1[index - 1] == (floatValues_1[index] * 2), QString("Value should be double adjacent value at index %1").arg(index).toLatin1()); + if constexpr (std::numeric_limits<double>::has_denorm == std::denorm_present) { + // Validate that every value is half the value before it up to 1 + for (int index = 1074; index > 0; index--) { + QVERIFY2(floatValues_1[index] != 0, QString("2**- %1 should not be 0").arg(index).toLatin1()); + QVERIFY2(floatValues_1[index - 1] == (floatValues_1[index] * 2), QString("Value should be double adjacent value at index %1").arg(index).toLatin1()); + } + } else { + qInfo("Skipping denormal test as this system's double type lacks support"); } } @@ -420,6 +432,10 @@ void tst_QtJson::testNumbers_3() QJsonDocument jDocument2(QJsonDocument::fromJson(ba)); + QT_TEST_EQUALITY_OPS(jDocument1, jDocument2, true); + QT_TEST_EQUALITY_OPS(jDocument1, QJsonDocument(), false); + QT_TEST_EQUALITY_OPS(QJsonDocument(), QJsonDocument(), true); + double d1_1(jDocument2.object().value("d1").toDouble()); double d2_1(jDocument2.object().value("d2").toDouble()); QVERIFY(d1_1 != d2_1); @@ -437,7 +453,8 @@ void tst_QtJson::testNumbers_4() array << QJsonValue(-9223372036854775808.0); array << QJsonValue(+18446744073709551616.0); array << QJsonValue(-18446744073709551616.0); - const QByteArray json(QJsonDocument(array).toJson()); + QJsonDocument doc1 = QJsonDocument(array); + const QByteArray json(doc1.toJson()); const QByteArray expected = "[\n" " 1000000000000000,\n" @@ -458,7 +475,8 @@ void tst_QtJson::testNumbers_4() array2 << QJsonValue(Q_INT64_C(-9007199254740992)); array2 << QJsonValue(Q_INT64_C(+9223372036854775807)); array2 << QJsonValue(Q_INT64_C(-9223372036854775807)); - const QByteArray json2(QJsonDocument(array2).toJson()); + QJsonDocument doc2 = QJsonDocument(array2); + const QByteArray json2(doc2.toJson()); const QByteArray expected2 = "[\n" " 1000000000000000,\n" @@ -469,6 +487,8 @@ void tst_QtJson::testNumbers_4() " -9223372036854775807\n" "]\n"; QCOMPARE(json2, expected2); + + QT_TEST_EQUALITY_OPS(doc1, doc2, false); } void tst_QtJson::testNumberComparisons() @@ -517,6 +537,7 @@ void tst_QtJson::testObjectSimple() QJsonValue value(QLatin1String("foo")); object.insert("value", value); QCOMPARE(object.value("value"), value); + QT_TEST_EQUALITY_OPS(object.value("value"), value, true); int size = object.size(); object.remove("boolean"); @@ -525,6 +546,7 @@ void tst_QtJson::testObjectSimple() QJsonValue taken = object.take("value"); QCOMPARE(taken, value); + QT_TEST_EQUALITY_OPS(taken, value, true); QVERIFY2(!object.contains("value"), "key value should have been removed"); QString before = object.value("string").toString(); @@ -654,6 +676,7 @@ void tst_QtJson::testObjectInsertCopies() QCOMPARE(obj.size(), 2); QCOMPARE(obj.value("value"), "TEST"); QCOMPARE(obj.value("prop2"), "TEST"); + QT_TEST_EQUALITY_OPS(rv, obj["value"].toObject(), true); } { QJsonObject obj; @@ -786,15 +809,20 @@ void tst_QtJson::testValueObject() void tst_QtJson::testValueArray() { QJsonArray array; + QJsonArray otherArray = {"wrong value"}; + QJsonValue value(array); + QCOMPARE(value.toArray(), array); + QCOMPARE(value.toArray(otherArray), array); + array.append(999.); array.append(QLatin1String("test")); array.append(true); - - QJsonValue value(array); + value = array; // if we don't modify the original JsonArray, toArray() // on the JsonValue should return the same object (non-detached). QCOMPARE(value.toArray(), array); + QCOMPARE(value.toArray(otherArray), array); // if we modify the original array, it should detach array.append(QLatin1String("test")); @@ -804,14 +832,28 @@ void tst_QtJson::testValueArray() void tst_QtJson::testObjectNested() { QJsonObject inner, outer; + QJsonObject otherObject = {{"wrong key", "wrong value"}}; + QJsonValue v = inner; + QCOMPARE(v.toObject(), inner); + QCOMPARE(v.toObject(otherObject), inner); + QT_TEST_EQUALITY_OPS(v.toObject(), inner, true); + QT_TEST_EQUALITY_OPS(v.toObject(otherObject), inner, true); + inner.insert("number", 999.); outer.insert("nested", inner); + QT_TEST_EQUALITY_OPS(outer, inner, false); // if we don't modify the original JsonObject, value() // should return the same object (non-detached). QJsonObject value = outer.value("nested").toObject(); + v = value; QCOMPARE(value, inner); QCOMPARE(value.value("number").toDouble(), 999.); + QCOMPARE(v.toObject(), inner); + QCOMPARE(v.toObject(otherObject), inner); + QT_TEST_EQUALITY_OPS(v.toObject(), inner, true); + QT_TEST_EQUALITY_OPS(v.toObject(otherObject), inner, true); + QCOMPARE(v["number"].toDouble(), 999.); // if we modify the original object, it should detach and not // affect the nested object @@ -836,6 +878,7 @@ void tst_QtJson::testObjectNested() QCOMPARE(outer.value("nested").toObject().value("nested").toObject(), twoDeep); QCOMPARE(outer.value("nested").toObject().value("nested").toObject().value("boolean").toBool(), true); + QT_TEST_EQUALITY_OPS(outer.value("nested").toObject().value("nested").toObject(), twoDeep, true); } void tst_QtJson::testArrayNested() @@ -861,6 +904,7 @@ void tst_QtJson::testArrayNested() object.insert("boolean", true); outer.append(object); QCOMPARE(outer.last().toObject(), object); + QT_TEST_EQUALITY_OPS(outer.last().toObject(), object, true); QCOMPARE(outer.last().toObject().value("boolean").toBool(), true); // two deep arrays @@ -880,6 +924,7 @@ void tst_QtJson::testArrayNestedEmpty() QJsonValue val = object.value("inner"); QJsonArray value = object.value("inner").toArray(); QVERIFY(QJsonDocument(value).isArray()); + QT_TEST_EQUALITY_OPS(QJsonDocument(), QJsonDocument(value), false); QCOMPARE(value.size(), 0); QCOMPARE(value, inner); QCOMPARE(value.size(), 0); @@ -897,14 +942,46 @@ void tst_QtJson::testObjectNestedEmpty() object.insert("inner2", inner2); QJsonObject value = object.value("inner").toObject(); QVERIFY(QJsonDocument(value).isObject()); + QT_TEST_EQUALITY_OPS(QJsonDocument(), QJsonDocument(value), false); QCOMPARE(value.size(), 0); QCOMPARE(value, inner); + QT_TEST_EQUALITY_OPS(value, inner, true); QCOMPARE(value.size(), 0); object.insert("count", 0.); QCOMPARE(object.value("inner").toObject().size(), 0); QCOMPARE(object.value("inner").type(), QJsonValue::Object); } +void tst_QtJson::testArrayEquality_data() +{ + QTest::addColumn<QJsonArray>("array1"); + QTest::addColumn<QJsonArray>("array2"); + QTest::addColumn<bool>("expectedResult"); + QTest::addRow("QJsonArray(), QJsonArray{665, 666, 667}") + << QJsonArray() << QJsonArray{665, 666, 667} << false; + QTest::addRow("QJsonArray(), QJsonArray{}") + << QJsonArray() << QJsonArray{} <<true; + QTest::addRow("QJsonArray(), QJsonArray{123, QLatin1String(\"foo\")}") + << QJsonArray() << QJsonArray{123, QLatin1String("foo")} << false; + QTest::addRow( + "QJsonArray{123,QLatin1String(\"foo\")}, QJsonArray{123,QLatin1String(\"foo\")}") + << QJsonArray{123, QLatin1String("foo")} + << QJsonArray{123, QLatin1String("foo")} + << true; +} + +void tst_QtJson::testArrayEquality() +{ + QFETCH(QJsonArray, array1); + QFETCH(QJsonArray, array2); + QFETCH(bool, expectedResult); + + QJsonValue value = QJsonValue(array1); + + QT_TEST_EQUALITY_OPS(array1, array2, expectedResult); + QT_TEST_EQUALITY_OPS(value, array2, expectedResult); +} + void tst_QtJson::testArrayComfortOperators() { QJsonArray first; @@ -944,7 +1021,7 @@ void tst_QtJson::testValueRef() QCOMPARE(object.value(QLatin1String("null")), QJsonValue()); object[QLatin1String("null")] = 100.; QCOMPARE(object.value(QLatin1String("null")).type(), QJsonValue::Double); - QJsonValue val = qAsConst(object)[QLatin1String("null")]; + QJsonValue val = std::as_const(object)[QLatin1String("null")]; QCOMPARE(val.toDouble(), 100.); QCOMPARE(object.size(), 2); @@ -954,6 +1031,63 @@ void tst_QtJson::testValueRef() QCOMPARE(object.value(QLatin1String("key")), QJsonValue(42)); } +void tst_QtJson::testValueRefComparison() +{ + QJsonValue a0 = 42.; + QJsonValue a1 = QStringLiteral("142"); + +#define CHECK_IMPL(lhs, rhs, ineq) \ + QCOMPARE(lhs, rhs); \ + QVERIFY(!(lhs != rhs)); \ + QVERIFY(lhs != ineq); \ + QVERIFY(!(lhs == ineq)); \ + QVERIFY(ineq != rhs); \ + QVERIFY(!(ineq == rhs)); \ + /* end */ + +#define CHECK(lhs, rhs, ineq) \ + do { \ + CHECK_IMPL(lhs, rhs, ineq) \ + CHECK_IMPL(std::as_const(lhs), rhs, ineq) \ + CHECK_IMPL(lhs, std::as_const(rhs), ineq) \ + CHECK_IMPL(std::as_const(lhs), std::as_const(rhs), ineq) \ + } while (0) + + // check that the (in)equality operators aren't ambiguous in C++20: + QJsonArray a = {a0, a1}; + + static_assert(std::is_same_v<decltype(a[0]), QJsonValueRef>); + + auto r0 = a.begin()[0]; + auto r1 = a.begin()[1]; + auto c0 = std::as_const(a).begin()[0]; + // ref <> ref + CHECK(r0, r0, r1); + // cref <> ref + CHECK(c0, r0, r1); + // ref <> cref + CHECK(r0, c0, r1); + // ref <> val + CHECK(r0, a0, r1); + // cref <> val + CHECK(c0, a0, r1); + // val <> ref + CHECK(a0, r0, a1); + // val <> cref + CHECK(a0, c0, a1); + // val <> val + CHECK(a0, a0, a1); + + QT_TEST_EQUALITY_OPS(r0, r1, false); + QT_TEST_EQUALITY_OPS(r0, c0, true); + QT_TEST_EQUALITY_OPS(c0, r1, false); + QT_TEST_EQUALITY_OPS(a0, c0, true); + QT_TEST_EQUALITY_OPS(a0, r1, false); + +#undef CHECK +#undef CHECK_IMPL +} + void tst_QtJson::testObjectIteration() { QJsonObject object; @@ -967,8 +1101,9 @@ void tst_QtJson::testObjectIteration() for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it) QFAIL("Iterator after property add-and-remove should be empty"); - for (int i = 0; i < 10; ++i) - object[QString::number(i)] = (double)i; + // insert in weird order to confirm keys are sorted + for (int i : {0, 9, 5, 7, 8, 2, 1, 3, 6, 4}) + object[QString::number(i)] = double(i); QCOMPARE(object.size(), 10); @@ -977,44 +1112,79 @@ void tst_QtJson::testObjectIteration() for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it) { QJsonValue value = it.value(); QCOMPARE((double)it.key().toInt(), value.toDouble()); + QT_TEST_EQUALITY_OPS(it, QJsonObject::iterator(), false); } { QJsonObject object2 = object; QCOMPARE(object, object2); + QT_TEST_EQUALITY_OPS(object, object2, true); QJsonValue val = *object2.begin(); - object2.erase(object2.begin()); + auto next = object2.erase(object2.begin()); QCOMPARE(object.size(), 10); QCOMPARE(object2.size(), 9); + QVERIFY(next == object2.begin()); + QT_TEST_EQUALITY_OPS(next, object2.begin(), true); + + double d = 1; // we erased the first item + for (auto it = object2.constBegin(); it != object2.constEnd(); ++it, d += 1) { + QJsonValue value = it.value(); + QVERIFY(it.value() != val); + QCOMPARE(it.value(), d); + QCOMPARE(it.value().toDouble(), d); + QCOMPARE(it.key().toInt(), value.toDouble()); + } + } - for (QJsonObject::const_iterator it = object2.constBegin(); it != object2.constEnd(); ++it) { + { + QJsonObject object2 = object; + QCOMPARE(object, object2); + QT_TEST_EQUALITY_OPS(object, object2, true); + + QJsonValue val = *(object2.end() - 1); + auto next = object2.erase(object2.end() - 1); + QCOMPARE(object.size(), 10); + QCOMPARE(object2.size(), 9); + QVERIFY(next == object2.end()); + double d = 0; + for (auto it = object2.constBegin(); it != object2.constEnd(); ++it, d += 1) { QJsonValue value = it.value(); QVERIFY(it.value() != val); - QCOMPARE((double)it.key().toInt(), value.toDouble()); + QCOMPARE(it.value(), d); + QCOMPARE(it.value().toDouble(), d); + QCOMPARE(it.key().toInt(), value.toDouble()); } } { QJsonObject object2 = object; QCOMPARE(object, object2); + QT_TEST_EQUALITY_OPS(object, object2, true); QJsonObject::iterator it = object2.find(QString::number(5)); QJsonValue val = *it; - object2.erase(it); + auto next = object2.erase(it); QCOMPARE(object.size(), 10); QCOMPARE(object2.size(), 9); + QCOMPARE(*next, 6); - for (QJsonObject::const_iterator it = object2.constBegin(); it != object2.constEnd(); ++it) { + int i = 0; + for (auto it = object2.constBegin(); it != object2.constEnd(); ++it, ++i) { + if (i == 5) + ++i; QJsonValue value = it.value(); QVERIFY(it.value() != val); - QCOMPARE((double)it.key().toInt(), value.toDouble()); + QCOMPARE(it.value(), i); + QCOMPARE(it.value().toInt(), i); + QCOMPARE(it.key().toInt(), value.toDouble()); } } { QJsonObject::Iterator it = object.begin(); it += 5; + QT_TEST_ALL_COMPARISON_OPS(it, object.begin(), Qt::strong_ordering::greater); QCOMPARE(QJsonValue(it.value()).toDouble(), 5.); it -= 3; QCOMPARE(QJsonValue(it.value()).toDouble(), 2.); @@ -1029,10 +1199,14 @@ void tst_QtJson::testObjectIteration() it += 5; QCOMPARE(QJsonValue(it.value()).toDouble(), 5.); it -= 3; + QT_TEST_ALL_COMPARISON_OPS(object.constBegin(), it, Qt::strong_ordering::less); QCOMPARE(QJsonValue(it.value()).toDouble(), 2.); QJsonObject::ConstIterator it2 = it + 5; + QT_TEST_EQUALITY_OPS(it, it2, false); QCOMPARE(QJsonValue(it2.value()).toDouble(), 7.); it2 = it - 1; + QT_TEST_ALL_COMPARISON_OPS(it2, it, Qt::strong_ordering::less); + QT_TEST_ALL_COMPARISON_OPS(it2, it - 2, Qt::strong_ordering::greater); QCOMPARE(QJsonValue(it2.value()).toDouble(), 1.); } @@ -1041,6 +1215,17 @@ void tst_QtJson::testObjectIteration() it = object.erase(it); QCOMPARE(object.size() , 0); QCOMPARE(it, object.end()); + QT_TEST_ALL_COMPARISON_OPS(it, object.end(), Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(it, object.constEnd(), Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(it, object.begin(), + Qt::strong_ordering::equal); // because object is empty + QT_TEST_ALL_COMPARISON_OPS(it, object.constBegin(), Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(QJsonObject::Iterator(), + QJsonObject::Iterator(), Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(QJsonObject::ConstIterator(), + QJsonObject::Iterator(), Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(QJsonObject::ConstIterator(), + QJsonObject::ConstIterator(), Qt::strong_ordering::equal); } void tst_QtJson::testArrayIteration() @@ -1054,7 +1239,11 @@ void tst_QtJson::testArrayIteration() int i = 0; for (QJsonArray::iterator it = array.begin(); it != array.end(); ++it, ++i) { QJsonValue value = (*it); + QJsonArray::iterator it1 = it; QCOMPARE((double)i, value.toDouble()); + QT_TEST_EQUALITY_OPS(QJsonArray::iterator(), QJsonArray::iterator(), true); + QT_TEST_EQUALITY_OPS(QJsonArray::iterator(), it, false); + QT_TEST_EQUALITY_OPS(it1, it, true); } QCOMPARE(array.begin()->toDouble(), array.constBegin()->toDouble()); @@ -1064,14 +1253,38 @@ void tst_QtJson::testArrayIteration() QCOMPARE(array, array2); QJsonValue val = *array2.begin(); - array2.erase(array2.begin()); + auto next = array2.erase(array2.begin()); QCOMPARE(array.size(), 10); QCOMPARE(array2.size(), 9); + QVERIFY(next == array2.begin()); i = 1; - for (QJsonArray::const_iterator it = array2.constBegin(); it != array2.constEnd(); ++it, ++i) { + for (auto it = array2.constBegin(); it != array2.constEnd(); ++it, ++i) { QJsonValue value = (*it); - QCOMPARE((double)i, value.toDouble()); + QCOMPARE(value.toInt(), i); + QCOMPARE(value.toDouble(), i); + QCOMPARE(it->toInt(), i); + QCOMPARE(it->toDouble(), i); + } + } + + { + QJsonArray array2 = array; + QCOMPARE(array, array2); + + QJsonValue val = array2.last(); + auto next = array2.erase(array2.end() - 1); + QCOMPARE(array.size(), 10); + QCOMPARE(array2.size(), 9); + QVERIFY(next == array2.end()); + + i = 0; + for (auto it = array2.constBegin(); it != array2.constEnd(); ++it, ++i) { + QJsonValue value = (*it); + QCOMPARE(value.toInt(), i); + QCOMPARE(value.toDouble(), i); + QCOMPARE(it->toInt(), i); + QCOMPARE(it->toDouble(), i); } } @@ -1085,6 +1298,13 @@ void tst_QtJson::testArrayIteration() QCOMPARE(QJsonValue(*it2).toDouble(), 7.); it2 = it - 1; QCOMPARE(QJsonValue(*it2).toDouble(), 1.); + QT_TEST_EQUALITY_OPS(it, it2, false); + it = array.begin(); + QT_TEST_EQUALITY_OPS(it, array.begin(), true); + it2 = it + 5; + QT_TEST_ALL_COMPARISON_OPS(it2, it, Qt::strong_ordering::greater); + it += 5; + QT_TEST_EQUALITY_OPS(it, it2, true); } { @@ -1104,6 +1324,26 @@ void tst_QtJson::testArrayIteration() it = array.erase(it); QCOMPARE(array.size() , 0); QCOMPARE(it, array.end()); + QT_TEST_EQUALITY_OPS(it, array.end(), true); + + { + int i = 0; + for (QJsonArray::const_iterator it = array.constBegin(); + it != array.constEnd(); ++it, ++i) { + QJsonArray::const_iterator it1 = it; + QT_TEST_EQUALITY_OPS(QJsonArray::const_iterator(), QJsonArray::const_iterator(), true); + QT_TEST_EQUALITY_OPS(QJsonArray::const_iterator(), it, false); + QT_TEST_EQUALITY_OPS(it1, it, true); + } + } + + { + QJsonArray::iterator nonConstIt = array.begin(); + QJsonArray::const_iterator it = array.constBegin(); + QT_TEST_EQUALITY_OPS(nonConstIt, it, true); + it+=1; + QT_TEST_ALL_COMPARISON_OPS(nonConstIt, it, Qt::strong_ordering::less); + } } void tst_QtJson::testObjectFind() @@ -1117,14 +1357,12 @@ void tst_QtJson::testObjectFind() QJsonObject::iterator it = object.find(QLatin1String("1")); QCOMPARE((*it).toDouble(), 1.); it = object.find(QString("11")); - QCOMPARE((*it).type(), QJsonValue::Undefined); QCOMPARE(it, object.end()); QJsonObject::const_iterator cit = object.constFind(QLatin1String("1")); QCOMPARE((*cit).toDouble(), 1.); cit = object.constFind(QString("11")); - QCOMPARE((*it).type(), QJsonValue::Undefined); - QCOMPARE(it, object.end()); + QCOMPARE(cit, object.constEnd()); } void tst_QtJson::testDocument() @@ -1199,6 +1437,8 @@ void tst_QtJson::testDocument() QCOMPARE(doc5.isObject(), false); QCOMPARE(doc5.array().size(), 1); QCOMPARE(doc5.array().at(0), QJsonValue(23)); + + QT_TEST_EQUALITY_OPS(doc2, doc3, true); } void tst_QtJson::nullValues() @@ -1570,7 +1810,8 @@ void tst_QtJson::fromVariantHash() void tst_QtJson::toVariantMap() { - QCOMPARE(QMetaType::Type(QJsonValue(QJsonObject()).toVariant().type()), QMetaType::QVariantMap); // QTBUG-32524 + QCOMPARE(QMetaType::Type(QJsonValue(QJsonObject()).toVariant().typeId()), + QMetaType::QVariantMap); // QTBUG-32524 QJsonObject object; QVariantMap map = object.toVariantMap(); @@ -1590,7 +1831,7 @@ void tst_QtJson::toVariantMap() QCOMPARE(map.size(), 3); QCOMPARE(map.value("Key"), QVariant(QString("Value"))); QCOMPARE(map.value("null"), QVariant::fromValue(nullptr)); - QCOMPARE(map.value("Array").type(), QVariant::List); + QCOMPARE(map.value("Array").typeId(), QMetaType::QVariantList); QVariantList list = map.value("Array").toList(); QCOMPARE(list.size(), 4); QCOMPARE(list.at(0), QVariant(true)); @@ -1619,7 +1860,7 @@ void tst_QtJson::toVariantHash() QCOMPARE(hash.size(), 3); QCOMPARE(hash.value("Key"), QVariant(QString("Value"))); QCOMPARE(hash.value("null"), QVariant::fromValue(nullptr)); - QCOMPARE(hash.value("Array").type(), QVariant::List); + QCOMPARE(hash.value("Array").typeId(), QMetaType::QVariantList); QVariantList list = hash.value("Array").toList(); QCOMPARE(list.size(), 4); QCOMPARE(list.at(0), QVariant(true)); @@ -1630,7 +1871,8 @@ void tst_QtJson::toVariantHash() void tst_QtJson::toVariantList() { - QCOMPARE(QMetaType::Type(QJsonValue(QJsonArray()).toVariant().type()), QMetaType::QVariantList); // QTBUG-32524 + QCOMPARE(QMetaType::Type(QJsonValue(QJsonArray()).toVariant().typeId()), + QMetaType::QVariantList); // QTBUG-32524 QJsonArray array; QVariantList list = array.toVariantList(); @@ -1650,7 +1892,7 @@ void tst_QtJson::toVariantList() QCOMPARE(list.size(), 3); QCOMPARE(list[0], QVariant(QString("Value"))); QCOMPARE(list[1], QVariant::fromValue(nullptr)); - QCOMPARE(list[2].type(), QVariant::List); + QCOMPARE(list[2].typeId(), QMetaType::QVariantList); QVariantList vlist = list[2].toList(); QCOMPARE(vlist.size(), 4); QCOMPARE(vlist.at(0), QVariant(true)); @@ -1772,16 +2014,13 @@ void tst_QtJson::toJsonLargeNumericValues() QJsonArray array; array.append(QJsonValue(1.234567)); // actual precision bug in Qt 5.0.0 array.append(QJsonValue(1.7976931348623157e+308)); // JS Number.MAX_VALUE - array.append(QJsonValue(5e-324)); // JS Number.MIN_VALUE array.append(QJsonValue(std::numeric_limits<double>::min())); array.append(QJsonValue(std::numeric_limits<double>::max())); array.append(QJsonValue(std::numeric_limits<double>::epsilon())); - array.append(QJsonValue(std::numeric_limits<double>::denorm_min())); array.append(QJsonValue(0.0)); array.append(QJsonValue(-std::numeric_limits<double>::min())); array.append(QJsonValue(-std::numeric_limits<double>::max())); array.append(QJsonValue(-std::numeric_limits<double>::epsilon())); - array.append(QJsonValue(-std::numeric_limits<double>::denorm_min())); array.append(QJsonValue(-0.0)); array.append(QJsonValue(9007199254740992LL)); // JS Number max integer array.append(QJsonValue(-9007199254740992LL)); // JS Number min integer @@ -1795,27 +2034,21 @@ void tst_QtJson::toJsonLargeNumericValues() " 1.234567,\n" " 1.7976931348623157e+308,\n" #ifdef QT_NO_DOUBLECONVERSION // "shortest" double conversion is not very short then - " 4.9406564584124654e-324,\n" " 2.2250738585072014e-308,\n" " 1.7976931348623157e+308,\n" " 2.2204460492503131e-16,\n" - " 4.9406564584124654e-324,\n" " 0,\n" " -2.2250738585072014e-308,\n" " -1.7976931348623157e+308,\n" " -2.2204460492503131e-16,\n" - " -4.9406564584124654e-324,\n" #else - " 5e-324,\n" " 2.2250738585072014e-308,\n" " 1.7976931348623157e+308,\n" " 2.220446049250313e-16,\n" - " 5e-324,\n" " 0,\n" " -2.2250738585072014e-308,\n" " -1.7976931348623157e+308,\n" " -2.220446049250313e-16,\n" - " -5e-324,\n" #endif " 0,\n" " 9007199254740992,\n" @@ -1831,6 +2064,42 @@ void tst_QtJson::toJsonLargeNumericValues() QCOMPARE(json, expected); } +void tst_QtJson::toJsonDenormalValues() +{ + if constexpr (std::numeric_limits<double>::has_denorm == std::denorm_present) { + QJsonObject object; + QJsonArray array; + array.append(QJsonValue(5e-324)); // JS Number.MIN_VALUE + array.append(QJsonValue(std::numeric_limits<double>::denorm_min())); + array.append(QJsonValue(-std::numeric_limits<double>::denorm_min())); + object.insert("Array", array); + + QByteArray json = QJsonDocument(object).toJson(); + QByteArray expected = + "{\n" + " \"Array\": [\n" +#ifdef QT_NO_DOUBLECONVERSION // "shortest" double conversion is not very short then + " 4.9406564584124654e-324,\n" + " 4.9406564584124654e-324,\n" + " -4.9406564584124654e-324\n" +#else + " 5e-324,\n" + " 5e-324,\n" + " -5e-324\n" +#endif + " ]\n" + "}\n"; + + QCOMPARE(json, expected); + QJsonDocument doc; + doc.setObject(object); + json = doc.toJson(); + QCOMPARE(json, expected); + } else { + QSKIP("Skipping 'denorm' as this type lacks denormals on this system"); + } +} + void tst_QtJson::fromJson() { { @@ -2153,12 +2422,12 @@ void tst_QtJson::parseNumbers() QCOMPARE(val.toDouble(), (double)numbers[i].n); } } + // test number parsing + struct Numbers { + const char *str; + double n; + }; { - // test number parsing - struct Numbers { - const char *str; - double n; - }; Numbers numbers [] = { { "0", 0 }, { "1", 1 }, @@ -2174,8 +2443,6 @@ void tst_QtJson::parseNumbers() { "1.1e10", 1.1e10 }, { "1.1e308", 1.1e308 }, { "-1.1e308", -1.1e308 }, - { "1.1e-308", 1.1e-308 }, - { "-1.1e-308", -1.1e-308 }, { "1.1e+308", 1.1e+308 }, { "-1.1e+308", -1.1e+308 }, { "1.e+308", 1.e+308 }, @@ -2197,6 +2464,29 @@ void tst_QtJson::parseNumbers() QCOMPARE(val.toDouble(), numbers[i].n); } } + if constexpr (std::numeric_limits<double>::has_denorm == std::denorm_present) { + Numbers numbers [] = { + { "1.1e-308", 1.1e-308 }, + { "-1.1e-308", -1.1e-308 } + }; + int size = sizeof(numbers)/sizeof(Numbers); + for (int i = 0; i < size; ++i) { + QByteArray json = "[ "; + json += numbers[i].str; + json += " ]"; + QJsonDocument doc = QJsonDocument::fromJson(json); + QVERIFY(!doc.isEmpty()); + QCOMPARE(doc.isArray(), true); + QCOMPARE(doc.isObject(), false); + QJsonArray array = doc.array(); + QCOMPARE(array.size(), 1); + QJsonValue val = array.at(0); + QCOMPARE(val.type(), QJsonValue::Double); + QCOMPARE(val.toDouble(), numbers[i].n); + } + } else { + qInfo("Skipping denormal test as this system's double type lacks support"); + } } void tst_QtJson::parseStrings() @@ -2286,7 +2576,7 @@ void tst_QtJson::parseDuplicateKeys() void tst_QtJson::testParser() { QFile file(testDataDir + "/test.json"); - file.open(QFile::ReadOnly); + QVERIFY(file.open(QFile::ReadOnly)); QByteArray testJson = file.readAll(); QJsonDocument doc = QJsonDocument::fromJson(testJson); @@ -2342,6 +2632,13 @@ void tst_QtJson::testCompaction() } QCOMPARE(obj.size(), 1); QCOMPARE(obj.value(QLatin1String("foo")).toString(), QLatin1String("bar")); + + QJsonObject obj2; + + QT_TEST_EQUALITY_OPS(obj, obj2, false); + QT_TEST_EQUALITY_OPS(QJsonObject(), obj2, true); + obj2 = obj; + QT_TEST_EQUALITY_OPS(obj, obj2, true); } void tst_QtJson::testDebugStream() @@ -2621,57 +2918,57 @@ void tst_QtJson::testDetachBug() void tst_QtJson::valueEquals() { QCOMPARE(QJsonValue(), QJsonValue()); - QVERIFY(QJsonValue() != QJsonValue(QJsonValue::Undefined)); - QVERIFY(QJsonValue() != QJsonValue(true)); - QVERIFY(QJsonValue() != QJsonValue(1.)); - QVERIFY(QJsonValue() != QJsonValue(QJsonArray())); - QVERIFY(QJsonValue() != QJsonValue(QJsonObject())); + QT_TEST_EQUALITY_OPS(QJsonValue(), QJsonValue(QJsonValue::Undefined), false); + QT_TEST_EQUALITY_OPS(QJsonValue(), QJsonValue(true), false); + QT_TEST_EQUALITY_OPS(QJsonValue(), QJsonValue(1.), false); + QT_TEST_EQUALITY_OPS(QJsonValue(), QJsonValue(QJsonArray()), false); + QT_TEST_EQUALITY_OPS(QJsonValue(), QJsonValue(QJsonObject()), false); QCOMPARE(QJsonValue(true), QJsonValue(true)); - QVERIFY(QJsonValue(true) != QJsonValue(false)); - QVERIFY(QJsonValue(true) != QJsonValue(QJsonValue::Undefined)); - QVERIFY(QJsonValue(true) != QJsonValue()); - QVERIFY(QJsonValue(true) != QJsonValue(1.)); - QVERIFY(QJsonValue(true) != QJsonValue(QJsonArray())); - QVERIFY(QJsonValue(true) != QJsonValue(QJsonObject())); + QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(false), false); + QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(QJsonValue::Undefined), false); + QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(), false); + QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(1.), false); + QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(QJsonArray()), false); + QT_TEST_EQUALITY_OPS(QJsonValue(true), QJsonValue(QJsonObject()), false); QCOMPARE(QJsonValue(1), QJsonValue(1)); - QVERIFY(QJsonValue(1) != QJsonValue(2)); + QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(2), false); QCOMPARE(QJsonValue(1), QJsonValue(1.)); - QVERIFY(QJsonValue(1) != QJsonValue(1.1)); - QVERIFY(QJsonValue(1) != QJsonValue(QJsonValue::Undefined)); - QVERIFY(QJsonValue(1) != QJsonValue()); - QVERIFY(QJsonValue(1) != QJsonValue(true)); - QVERIFY(QJsonValue(1) != QJsonValue(QJsonArray())); - QVERIFY(QJsonValue(1) != QJsonValue(QJsonObject())); + QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(1.1), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(QJsonValue::Undefined), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(true), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(QJsonArray()), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1), QJsonValue(QJsonObject()), false); QCOMPARE(QJsonValue(1.), QJsonValue(1.)); - QVERIFY(QJsonValue(1.) != QJsonValue(2.)); - QVERIFY(QJsonValue(1.) != QJsonValue(QJsonValue::Undefined)); - QVERIFY(QJsonValue(1.) != QJsonValue()); - QVERIFY(QJsonValue(1.) != QJsonValue(true)); - QVERIFY(QJsonValue(1.) != QJsonValue(QJsonArray())); - QVERIFY(QJsonValue(1.) != QJsonValue(QJsonObject())); + QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(2.), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(QJsonValue::Undefined), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(true), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(QJsonArray()), false); + QT_TEST_EQUALITY_OPS(QJsonValue(1.), QJsonValue(QJsonObject()), false); QCOMPARE(QJsonValue(QJsonArray()), QJsonValue(QJsonArray())); QJsonArray nonEmptyArray; nonEmptyArray.append(true); - QVERIFY(QJsonValue(QJsonArray()) != nonEmptyArray); - QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(QJsonValue::Undefined)); - QVERIFY(QJsonValue(QJsonArray()) != QJsonValue()); - QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(true)); - QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(1.)); - QVERIFY(QJsonValue(QJsonArray()) != QJsonValue(QJsonObject())); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), nonEmptyArray, false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), QJsonValue(QJsonValue::Undefined), false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), QJsonValue(), false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), QJsonValue(true), false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), QJsonValue(1.), false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonArray()), QJsonValue(QJsonObject()), false); QCOMPARE(QJsonValue(QJsonObject()), QJsonValue(QJsonObject())); QJsonObject nonEmptyObject; nonEmptyObject.insert("Key", true); - QVERIFY(QJsonValue(QJsonObject()) != nonEmptyObject); - QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(QJsonValue::Undefined)); - QVERIFY(QJsonValue(QJsonObject()) != QJsonValue()); - QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(true)); - QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(1.)); - QVERIFY(QJsonValue(QJsonObject()) != QJsonValue(QJsonArray())); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), nonEmptyObject, false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), QJsonValue(QJsonValue::Undefined), false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), QJsonValue(), false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), QJsonValue(true), false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), QJsonValue(1.), false); + QT_TEST_EQUALITY_OPS(QJsonValue(QJsonObject()), QJsonValue(QJsonArray()), false); QCOMPARE(QJsonValue("foo"), QJsonValue(QLatin1String("foo"))); QCOMPARE(QJsonValue("foo"), QJsonValue(QString("foo"))); @@ -2864,7 +3161,7 @@ void tst_QtJson::documentEquals() void tst_QtJson::bom() { QFile file(testDataDir + "/bom.json"); - file.open(QFile::ReadOnly); + QVERIFY(file.open(QFile::ReadOnly)); QByteArray json = file.readAll(); // Import json document into a QJsonDocument @@ -3185,7 +3482,7 @@ void tst_QtJson::documentFromVariant() // As JSON arrays they should be equal. QCOMPARE(da1.array(), da2.array()); - + QT_TEST_EQUALITY_OPS(da1, da2, true); QMap <QString, QVariant> map; map["key"] = string; @@ -3201,6 +3498,7 @@ void tst_QtJson::documentFromVariant() // As JSON objects they should be equal. QCOMPARE(do1.object(), do2.object()); + QT_TEST_EQUALITY_OPS(do1, do2, true); } void tst_QtJson::parseErrorOffset_data() @@ -3299,6 +3597,7 @@ void tst_QtJson::streamSerializationQJsonDocument() QDataStream load(buffer); load >> output; QCOMPARE(output, document); + QT_TEST_EQUALITY_OPS(output, document, true); } void tst_QtJson::streamSerializationQJsonArray_data() @@ -3688,6 +3987,7 @@ void tst_QtJson::noLeakOnNameClash() QVERIFY2(!expected.isNull(), qPrintable(error.errorString())); QCOMPARE(doc, expected); + QT_TEST_EQUALITY_OPS(doc, expected, true); // It should not leak. // In particular it should not forget to deref the container for the inner objects. diff --git a/tests/auto/corelib/serialization/qcborstreamreader/CMakeLists.txt b/tests/auto/corelib/serialization/qcborstreamreader/CMakeLists.txt index 276474ba37..29a935977b 100644 --- a/tests/auto/corelib/serialization/qcborstreamreader/CMakeLists.txt +++ b/tests/auto/corelib/serialization/qcborstreamreader/CMakeLists.txt @@ -1,15 +1,22 @@ -# Generated from qcborstreamreader.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qcborstreamreader Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcborstreamreader LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qcborstreamreader SOURCES tst_qcborstreamreader.cpp INCLUDE_DIRECTORIES ../../../../../src/3rdparty/tinycbor/src - ../../../../../src/3rdparty/tinycbor/tests/parser - PUBLIC_LIBRARIES + ../../../../../src/3rdparty/tinycbor/tests + LIBRARIES Qt::CorePrivate ) diff --git a/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp b/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp index 8d0b5a054e..63cfbce75f 100644 --- a/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp +++ b/tests/auto/corelib/serialization/qcborstreamreader/tst_qcborstreamreader.cpp @@ -1,48 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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 <QTest> #include <QBuffer> -#include <QtCore/private/qbytearray_p.h> - class tst_QCborStreamReader : public QObject { Q_OBJECT @@ -124,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() { @@ -693,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()) { @@ -703,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 @@ -725,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() @@ -917,7 +918,7 @@ 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 = MaxByteArraySize + 1; + const qsizetype MinInvalid = QByteArray::max_size() + 1; addValidationColumns(); addValidationData(MinInvalid); @@ -944,11 +945,57 @@ void tst_QCborStreamReader::validation() reader.reset(); QVERIFY(!reader.next()); 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(MaxByteArraySize + 1, MaxStringSize + 1); + addValidationHugeDevice(QByteArray::max_size() + 1, QString::max_size() + 1); } void tst_QCborStreamReader::hugeDeviceValidation() @@ -957,6 +1004,17 @@ void tst_QCborStreamReader::hugeDeviceValidation() 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); diff --git a/tests/auto/corelib/serialization/qcborstreamwriter/CMakeLists.txt b/tests/auto/corelib/serialization/qcborstreamwriter/CMakeLists.txt index 2796dc214c..c1a9a87677 100644 --- a/tests/auto/corelib/serialization/qcborstreamwriter/CMakeLists.txt +++ b/tests/auto/corelib/serialization/qcborstreamwriter/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qcborstreamwriter.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qcborstreamwriter Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcborstreamwriter LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qcborstreamwriter SOURCES tst_qcborstreamwriter.cpp INCLUDE_DIRECTORIES - ../../../../../src/3rdparty/tinycbor/tests/encoder + ../../../../../src/3rdparty/tinycbor/tests ) diff --git a/tests/auto/corelib/serialization/qcborstreamwriter/tst_qcborstreamwriter.cpp b/tests/auto/corelib/serialization/qcborstreamwriter/tst_qcborstreamwriter.cpp index b32a2b4d73..45e241ef5c 100644 --- a/tests/auto/corelib/serialization/qcborstreamwriter/tst_qcborstreamwriter.cpp +++ b/tests/auto/corelib/serialization/qcborstreamwriter/tst_qcborstreamwriter.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QCborStreamWriter> @@ -65,7 +29,7 @@ private Q_SLOTS: // Get the data from TinyCBOR (see src/3rdparty/tinycbor/tests/encoder/data.cpp) typedef quint64 CborTag; -#include "data.cpp" +#include "encoder/data.cpp" void encodeVariant(QCborStreamWriter &writer, const QVariant &v) { @@ -117,9 +81,9 @@ void encodeVariant(QCborStreamWriter &writer, const QVariant &v) list = v.value<IndeterminateLengthArray>(); writer.startArray(); } else { - writer.startArray(list.length()); + writer.startArray(list.size()); } - for (const QVariant &v2 : qAsConst(list)) + for (const QVariant &v2 : std::as_const(list)) encodeVariant(writer, v2); QVERIFY(writer.endArray()); return; @@ -130,9 +94,9 @@ void encodeVariant(QCborStreamWriter &writer, const QVariant &v) map = v.value<IndeterminateLengthMap>(); writer.startMap(); } else { - writer.startMap(map.length()); + writer.startMap(map.size()); } - for (auto pair : qAsConst(map)) { + for (auto pair : std::as_const(map)) { encodeVariant(writer, pair.first); encodeVariant(writer, pair.second); } diff --git a/tests/auto/corelib/serialization/qcborvalue/CMakeLists.txt b/tests/auto/corelib/serialization/qcborvalue/CMakeLists.txt index 9233888671..4b72396489 100644 --- a/tests/auto/corelib/serialization/qcborvalue/CMakeLists.txt +++ b/tests/auto/corelib/serialization/qcborvalue/CMakeLists.txt @@ -1,15 +1,23 @@ -# Generated from qcborvalue.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qcborvalue Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcborvalue LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qcborvalue SOURCES tst_qcborvalue.cpp INCLUDE_DIRECTORIES ../../../../../src/3rdparty/tinycbor/src ../../../../../src/3rdparty/tinycbor/tests/parser - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate + Qt::TestPrivate ) diff --git a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp index f5a3e4817c..23b25834b9 100644 --- a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp +++ b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp @@ -1,55 +1,23 @@ -/**************************************************************************** -** -** Copyright (C) 2020 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) 2022 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/qcborvalue.h> #include <QTest> +#include <QtTest/private/qcomparisontesthelper_p.h> + #include <QBuffer> #include <QCborStreamReader> #include <QCborStreamWriter> +#include <QDateTime> #include <QtEndian> - -#include <QtCore/private/qbytearray_p.h> +#include <QTimeZone> Q_DECLARE_METATYPE(QCborKnownTags) Q_DECLARE_METATYPE(QCborValue) Q_DECLARE_METATYPE(QCborValue::EncodingOptions) +using namespace Qt::StringLiterals; + class tst_QCborValue : public QObject { Q_OBJECT @@ -61,6 +29,7 @@ private slots: void tagged(); void extendedTypes_data(); void extendedTypes(); + void compareCompiles(); void copyCompare_data() { basics_data(); } void copyCompare(); @@ -72,6 +41,9 @@ private slots: void arrayMutation(); void arrayMutateWithCopies(); void arrayPrepend(); + void arrayValueRef_data() { basics_data(); } + void arrayValueRef(); + void arrayValueRefLargeKey(); void arrayInsertRemove_data() { basics_data(); } void arrayInsertRemove(); void arrayInsertTagged_data() { basics_data(); } @@ -79,16 +51,25 @@ private slots: void arrayStringElements(); void arraySelfAssign_data() { basics_data(); } void arraySelfAssign(); + void arrayNested(); void mapDefaultInitialization(); void mapEmptyInitializerList(); void mapEmptyDetach(); void mapNonEmptyDetach(); void mapSimpleInitializerList(); + void mapFromArrayLargeIntKey_data() { basics_data(); } + void mapFromArrayLargeIntKey(); + void mapFromArrayNegativeIntKey_data() { basics_data(); } + void mapFromArrayNegativeIntKey(); + void mapFromArrayStringKey_data() { basics_data(); } + void mapFromArrayStringKey(); void mapMutation(); void mapMutateWithCopies(); void mapStringValues(); void mapStringKeys(); + void mapValueRef_data() { basics_data(); } + void mapValueRef(); void mapInsertRemove_data() { basics_data(); } void mapInsertRemove(); void mapInsertTagged_data() { basics_data(); } @@ -97,8 +78,12 @@ private slots: void mapSelfAssign(); void mapComplexKeys_data() { basics_data(); } void mapComplexKeys(); + void mapNested(); + void sorting_data(); void sorting(); + void comparisonMap_data(); + void comparisonMap(); void toCbor_data(); void toCbor(); @@ -121,6 +106,19 @@ private slots: void toDiagnosticNotation_data(); void toDiagnosticNotation(); + void cborValueRef_data(); + void cborValueRef(); + void cborValueConstRef_data() { cborValueRef_data(); } + void cborValueConstRef(); + void cborValueRefMutatingArray_data() { cborValueRef_data(); } + void cborValueRefMutatingArray(); + void cborValueRefMutatingMapIntKey_data() { cborValueRef_data(); } + void cborValueRefMutatingMapIntKey(); + void cborValueRefMutatingMapLatin1StringKey_data() { cborValueRef_data(); } + void cborValueRefMutatingMapLatin1StringKey(); + void cborValueRefMutatingMapStringKey_data() { cborValueRef_data(); } + void cborValueRefMutatingMapStringKey(); + void datastreamSerialization_data(); void datastreamSerialization(); void streamVariantSerialization(); @@ -301,6 +299,7 @@ static void basicTypeCheck(QCborValue::Type type, const QCborValue &v, const QVa QCOMPARE(v.isDouble(), type == QCborValue::Double); QCOMPARE(v.isDateTime(), type == QCborValue::DateTime); QCOMPARE(v.isUrl(), type == QCborValue::Url); + QCOMPARE(v.isRegularExpression(), type == QCborValue::RegularExpression); QCOMPARE(v.isUuid(), type == QCborValue::Uuid); QCOMPARE(v.isInvalid(), type == QCborValue::Invalid); QCOMPARE(v.isContainer(), type == QCborValue::Array || type == QCborValue::Map); @@ -398,7 +397,7 @@ void tst_QCborValue::extendedTypes_data() QTest::addColumn<QCborValue>("correctedTaggedValue"); QCborValue v(QCborValue::Invalid); QDateTime dt = QDateTime::currentDateTimeUtc(); - QDateTime dtTzOffset(dt.date(), dt.time(), Qt::OffsetFromUTC, dt.offsetFromUtc()); + QDateTime dtTzOffset(dt.date(), dt.time(), QTimeZone::fromSecondsAheadOfUtc(dt.offsetFromUtc())); QUuid uuid = QUuid::createUuid(); // non-correcting extended types (tagged value remains unchanged) @@ -422,7 +421,7 @@ void tst_QCborValue::extendedTypes_data() << QCborKnownTags::Uuid << QCborValue(uuid.toRfc4122()) << v; // correcting extended types - QDateTime dtNoMsecs = dt.fromSecsSinceEpoch(dt.toSecsSinceEpoch(), Qt::UTC); + QDateTime dtNoMsecs = dt.fromSecsSinceEpoch(dt.toSecsSinceEpoch(), QTimeZone::UTC); QUrl url("https://example.com/\xc2\xa9 "); QTest::newRow("UnixTime_t:Integer") << QCborValue(dtNoMsecs) << QCborKnownTags::UnixTime_t << QCborValue(dtNoMsecs.toSecsSinceEpoch()) @@ -433,10 +432,11 @@ void tst_QCborValue::extendedTypes_data() QTest::newRow("DateTime::JustDate") << QCborValue(QDateTime({2018, 1, 1}, {})) << QCborKnownTags::DateTimeString << QCborValue("2018-01-01") << QCborValue("2018-01-01T00:00:00.000"); - QTest::newRow("DateTime::TzOffset") << QCborValue(QDateTime({2018, 1, 1}, {9, 0, 0}, Qt::UTC)) - << QCborKnownTags::DateTimeString - << QCborValue("2018-01-01T09:00:00.000+00:00") - << QCborValue("2018-01-01T09:00:00.000Z"); + QTest::newRow("DateTime::TzOffset") + << QCborValue(QDateTime({2018, 1, 1}, {9, 0}, QTimeZone::UTC)) + << QCborKnownTags::DateTimeString + << QCborValue("2018-01-01T09:00:00.000+00:00") + << QCborValue("2018-01-01T09:00:00.000Z"); QTest::newRow("Url:NotNormalized") << QCborValue(url) << QCborKnownTags::Url << QCborValue("HTTPS://EXAMPLE.COM/%c2%a9%20") << QCborValue(url.toString()); @@ -451,6 +451,35 @@ void tst_QCborValue::extendedTypes_data() << QCborValue(uuid.toRfc4122() + "\1\2\3\4") << QCborValue(uuid.toRfc4122()); } +void tst_QCborValue::compareCompiles() +{ + // homogeneous types + QTestPrivate::testAllComparisonOperatorsCompile<QCborValue>(); + QTestPrivate::testAllComparisonOperatorsCompile<QCborValueRef>(); + QTestPrivate::testAllComparisonOperatorsCompile<QCborValueConstRef>(); + QTestPrivate::testAllComparisonOperatorsCompile<QCborArray>(); + QTestPrivate::testAllComparisonOperatorsCompile<QCborArray::Iterator>(); + QTestPrivate::testAllComparisonOperatorsCompile<QCborArray::ConstIterator>(); + QTestPrivate::testAllComparisonOperatorsCompile<QCborMap>(); + QTestPrivate::testAllComparisonOperatorsCompile<QCborMap::Iterator>(); + QTestPrivate::testAllComparisonOperatorsCompile<QCborMap::ConstIterator>(); + + // QCborValue, Ref and ConstRef + QTestPrivate::testAllComparisonOperatorsCompile<QCborValueRef, QCborValueConstRef>(); + QTestPrivate::testAllComparisonOperatorsCompile<QCborValueConstRef, QCborValue>(); + QTestPrivate::testAllComparisonOperatorsCompile<QCborValueRef, QCborValue>(); + + // QCbor{Array,Map} <=> QCborValue{,Ref,ConstRef} + QTestPrivate::testAllComparisonOperatorsCompile<QCborArray, QCborValue>(); + QTestPrivate::testAllComparisonOperatorsCompile<QCborArray, QCborValueRef>(); + QTestPrivate::testAllComparisonOperatorsCompile<QCborArray, QCborValueConstRef>(); + QTestPrivate::testAllComparisonOperatorsCompile<QCborMap, QCborValue>(); + QTestPrivate::testAllComparisonOperatorsCompile<QCborMap, QCborValueRef>(); + QTestPrivate::testAllComparisonOperatorsCompile<QCborMap, QCborValueConstRef>(); + QTestPrivate::testAllComparisonOperatorsCompile<QCborArray::Iterator, + QCborArray::ConstIterator>(); +} + void tst_QCborValue::extendedTypes() { QFETCH(QCborValue, extended); @@ -478,9 +507,13 @@ void tst_QCborValue::copyCompare() QT_WARNING_PUSH QT_WARNING_DISABLE_CLANG("-Wself-move") +#if defined(Q_CC_GNU_ONLY) && Q_CC_GNU >= 1301 +QT_WARNING_DISABLE_GCC("-Wself-move") +#endif // self-moving v = std::move(v); QCOMPARE(v, other); // make sure it's still valid + QT_TEST_ALL_COMPARISON_OPS(v, other, Qt::strong_ordering::equal); QT_WARNING_POP // moving @@ -492,24 +525,15 @@ QT_WARNING_POP other = v; v = other; - QCOMPARE(v.compare(other), 0); - QCOMPARE(v, other); - QVERIFY(!(v != other)); - QVERIFY(!(v < other)); -#if 0 && __has_include(<compare>) - QVERIFY(v <= other); - QVERIFY(v >= other); - QVERIFY(!(v > other)); -#endif + QT_TEST_ALL_COMPARISON_OPS(v, other, Qt::strong_ordering::equal); if (v.isUndefined()) other = nullptr; else other = {}; QVERIFY(v.type() != other.type()); - QVERIFY(!(v == other)); - QVERIFY(v != other); + QT_TEST_EQUALITY_OPS(v, other, false); // they're different types, so they can't compare equal QVERIFY(v.compare(other) != 0); @@ -539,9 +563,8 @@ void tst_QCborValue::arrayDefaultInitialization() QVERIFY(a.at(0).isUndefined()); QCOMPARE(a.constBegin(), a.constEnd()); - QVERIFY(a == a); - QVERIFY(a == QCborArray()); - QVERIFY(QCborArray() == a); + QT_TEST_EQUALITY_OPS(a, a, true); + QT_TEST_EQUALITY_OPS(a, QCborArray(), true); QCborValue v(a); QVERIFY(v.isArray()); @@ -550,7 +573,7 @@ void tst_QCborValue::arrayDefaultInitialization() QCborArray a2 = v.toArray(); QVERIFY(a2.isEmpty()); - QCOMPARE(a2, a); + QT_TEST_EQUALITY_OPS(a2, a, true); auto front = v[0]; QVERIFY(front.isUndefined()); front = 1; @@ -590,9 +613,8 @@ void tst_QCborValue::mapDefaultInitialization() QVERIFY(m.value("Hello").isUndefined()); #endif - QVERIFY(m == m); - QVERIFY(m == QCborMap{}); - QVERIFY(QCborMap{} == m); + QT_TEST_EQUALITY_OPS(m, m, true); + QT_TEST_EQUALITY_OPS(m, QCborMap{}, true); const QCborValue v(m); QVERIFY(v.isMap()); @@ -605,7 +627,7 @@ void tst_QCborValue::mapDefaultInitialization() QCborMap m2 = v.toMap(); QVERIFY(m2.isEmpty()); QCOMPARE(m2.size(), 0); - QCOMPARE(m2, m); + QT_TEST_EQUALITY_OPS(m2, m, true); } void tst_QCborValue::arrayEmptyInitializerList() @@ -613,9 +635,8 @@ void tst_QCborValue::arrayEmptyInitializerList() QCborArray a{}; QVERIFY(a.isEmpty()); QCOMPARE(a.size(), 0); - QVERIFY(a == a); - QVERIFY(a == QCborArray()); - QVERIFY(QCborArray() == a); + QT_TEST_EQUALITY_OPS(a, a, true); + QT_TEST_EQUALITY_OPS(a, QCborArray(), true); } void tst_QCborValue::mapEmptyInitializerList() @@ -623,9 +644,8 @@ void tst_QCborValue::mapEmptyInitializerList() QCborMap m{}; QVERIFY(m.isEmpty()); QCOMPARE(m.size(), 0); - QVERIFY(m == m); - QVERIFY(m == QCborMap{}); - QVERIFY(QCborMap{} == m); + QT_TEST_EQUALITY_OPS(m, m, true); + QT_TEST_EQUALITY_OPS(QCborMap{}, m, true); } void tst_QCborValue::arrayEmptyDetach() @@ -635,9 +655,8 @@ void tst_QCborValue::arrayEmptyDetach() QVERIFY(a.isEmpty()); QCOMPARE(a.size(), 0); - QVERIFY(a == a); - QVERIFY(a == QCborArray()); - QVERIFY(QCborArray() == a); + QT_TEST_EQUALITY_OPS(a, a, true); + QT_TEST_EQUALITY_OPS(a, QCborArray(), true); QCborValue v(a); QVERIFY(v.isArray()); @@ -656,9 +675,8 @@ void tst_QCborValue::mapEmptyDetach() QVERIFY(m.isEmpty()); QCOMPARE(m.size(), 0); - QVERIFY(m == m); - QVERIFY(m == QCborMap{}); - QVERIFY(QCborMap{} == m); + QT_TEST_EQUALITY_OPS(m, m, true); + QT_TEST_EQUALITY_OPS(QCborMap{}, m, true); QCborValue v(m); QVERIFY(v.isMap()); @@ -667,7 +685,7 @@ void tst_QCborValue::mapEmptyDetach() QCborMap m2 = v.toMap(); QVERIFY(m2.isEmpty()); - QCOMPARE(m2, m); + QT_TEST_EQUALITY_OPS(m2, m, true); } void tst_QCborValue::arrayNonEmptyDetach() @@ -728,6 +746,13 @@ void tst_QCborValue::mapNonEmptyDetach() { QCborMap copy(m); auto it = m.find(QLatin1String("3")); QVERIFY(it == m.end()); } { QCborMap copy(m); auto it = m.find(QString("3")); QVERIFY(it == m.end()); } { QCborMap copy(m); auto it = m.find(QCborValue(3)); QVERIFY(it == m.end()); } + + QT_TEST_EQUALITY_OPS(m.constBegin(), m.constEnd(), false); + QT_TEST_EQUALITY_OPS(m.begin(), m.end(), false); + QT_TEST_EQUALITY_OPS(m.constFind(3), m.constEnd(), true); + QT_TEST_EQUALITY_OPS(m.find(3), m.end(), true); + QT_TEST_EQUALITY_OPS(m.find(3), m.constEnd(), true); + QT_TEST_EQUALITY_OPS(m.constFind(3), m.end(), true); } void tst_QCborValue::arrayInitializerList() @@ -743,10 +768,9 @@ void tst_QCborValue::arrayInitializerList() QCOMPARE(a.at(5), QCborValue(QCborValue::Undefined)); QCOMPARE(a.at(6), QCborValue(1.0)); - QVERIFY(a == a); - QVERIFY(a != QCborArray{}); - QVERIFY(QCborArray{} != a); - QVERIFY(a == QCborArray({0, -1, false, true, nullptr, {}, 1.0})); + QT_TEST_EQUALITY_OPS(a, a, true); + QT_TEST_EQUALITY_OPS(a, QCborArray{}, false); + QT_TEST_EQUALITY_OPS(a, QCborArray({0, -1, false, true, nullptr, {}, 1.0}), true); QCborValue v = a; QCOMPARE(v[0], QCborValue(0)); @@ -775,12 +799,17 @@ void tst_QCborValue::arrayInitializerList() // iterators auto it = a.constBegin(); auto end = a.constEnd(); + QT_TEST_ALL_COMPARISON_OPS(it, end, Qt::strong_ordering::less); QCOMPARE(end - it, 7); QCOMPARE(it + 7, end); + QT_TEST_EQUALITY_OPS(it + 7, end, true); QVERIFY(it->isInteger()); QCOMPARE(*it, QCborValue(0)); QCOMPARE(it[1], QCborValue(-1)); QCOMPARE(*(it + 2), QCborValue(false)); + QT_TEST_EQUALITY_OPS(*it, QCborValue(0), true); + QT_TEST_EQUALITY_OPS(it[1], QCborValue(-1), true); + QT_TEST_EQUALITY_OPS(*(it + 2), QCborValue(false), true); it += 3; QCOMPARE(*it, QCborValue(true)); ++it; @@ -791,10 +820,28 @@ void tst_QCborValue::arrayInitializerList() QCOMPARE(*end, QCborValue(1.0)); end--; QCOMPARE(it, end); + QT_TEST_EQUALITY_OPS(it, end, true); + QT_TEST_EQUALITY_OPS(it, QCborArray::ConstIterator(), false); + QT_TEST_EQUALITY_OPS(QCborArray::ConstIterator(), end, false); + QT_TEST_EQUALITY_OPS(QCborArray::ConstIterator(), QCborArray::ConstIterator(), true); + QT_TEST_EQUALITY_OPS(QCborArray::ConstIterator(), QCborArray::Iterator(), true); + + { + auto it = a.begin(); + auto it1 = a.constBegin(); + auto end = a.end(); + QT_TEST_ALL_COMPARISON_OPS(it, end, Qt::strong_ordering::less); + QT_TEST_ALL_COMPARISON_OPS(it1, end, Qt::strong_ordering::less); + QT_TEST_EQUALITY_OPS(it + 7, end, true); + QT_TEST_EQUALITY_OPS(it1 + 7, end, true); + QT_TEST_EQUALITY_OPS(it, QCborArray::Iterator(), false); + QT_TEST_EQUALITY_OPS(QCborArray::Iterator(), end, false); + QT_TEST_EQUALITY_OPS(QCborArray::Iterator(), QCborArray::ConstIterator(), true); + } // range for int i = 0; - for (const QCborValue v : qAsConst(a)) { + for (const QCborValue v : std::as_const(a)) { QVERIFY(!v.isInvalid()); QCOMPARE(v.isUndefined(), i == 5); // 6th element is Undefined ++i; @@ -806,10 +853,9 @@ void tst_QCborValue::mapSimpleInitializerList() { QCborMap m{{0, 0}, {1, 0}, {2, "Hello"}, {"Hello", 2}, {3, QLatin1String("World")}, {QLatin1String("World"), 3}}; QCOMPARE(m.size(), 6); - QVERIFY(m == m); - QVERIFY(m != QCborMap{}); - QVERIFY(QCborMap{} != m); - QVERIFY(m == QCborMap({{0, 0}, {1, 0}, {2, "Hello"}, {"Hello", 2}, {3, QLatin1String("World")}, {QLatin1String("World"), 3}})); + QT_TEST_EQUALITY_OPS(m, m, true); + QT_TEST_EQUALITY_OPS(m, QCborMap{}, false); + QT_TEST_EQUALITY_OPS(m, QCborMap({{0, 0}, {1, 0}, {2, "Hello"}, {"Hello", 2}, {3, QLatin1String("World")}, {QLatin1String("World"), 3}}), true); QCborValue vmap = m; { @@ -894,7 +940,7 @@ void tst_QCborValue::mapSimpleInitializerList() // range for int i = 0; - for (auto pair : qAsConst(m)) { + for (auto pair : std::as_const(m)) { QVERIFY(!pair.first.isUndefined()); QVERIFY(!pair.second.isUndefined()); ++i; @@ -902,6 +948,55 @@ void tst_QCborValue::mapSimpleInitializerList() QCOMPARE(i, m.size()); } +template <typename T> static void mapFromArray_template(T key) +{ + QFETCH(QCborValue::Type, type); + QFETCH(QCborValue, v); + if (v.isMap()) + return; // already a map, nothing will happen + + // verify forced conversions work + // (our only Array row is an empty array, so it doesn't produce the warning) + QCborValue v2 = v; + QVERIFY(v2[key].isUndefined()); + QCOMPARE(v2.type(), QCborValue::Map); + QCOMPARE(v.type(), type); + QCborMap m = v2.toMap(); + QCOMPARE(m.size(), 1); + QCOMPARE(m.begin().key(), QCborValue(key)); + + // non-empty array conversions + QCborValue va = QCborArray{v}; + v2 = va; + QTest::ignoreMessage(QtWarningMsg, "Using CBOR array as map forced conversion"); + QVERIFY(v2[key].isUndefined()); + QCOMPARE(v2.type(), QCborValue::Map); + QCOMPARE(va.type(), QCborValue::Array); + m = v2.toMap(); + QCOMPARE(m.size(), 2); + auto it = m.constBegin(); + QCOMPARE(it.key(), QCborValue(0)); + QCOMPARE(it.value(), v); + ++it; + QCOMPARE(it.key(), QCborValue(key)); + QCOMPARE(it.value(), QCborValue()); +} + +void tst_QCborValue::mapFromArrayLargeIntKey() +{ + mapFromArray_template(Q_INT64_C(1) << 20); +} + +void tst_QCborValue::mapFromArrayNegativeIntKey() +{ + mapFromArray_template(-1); +} + +void tst_QCborValue::mapFromArrayStringKey() +{ + mapFromArray_template(QLatin1String("Hello")); +} + void tst_QCborValue::arrayMutation() { QCborArray a{42}; @@ -920,8 +1015,8 @@ void tst_QCborValue::arrayMutation() QVERIFY(v == a.at(0)); } - QVERIFY(a == a); - QVERIFY(a == QCborArray{true}); + QT_TEST_EQUALITY_OPS(a, a, true); + QT_TEST_EQUALITY_OPS(a, QCborArray{true}, true); QCborArray a2 = a; a.append(nullptr); @@ -973,14 +1068,6 @@ void tst_QCborValue::arrayMutation() QVERIFY(val.isArray()); QCOMPARE(val.toArray().size(), 4); QCOMPARE(val[3], 42); - - // Coerce to map on string key: - const QLatin1String any("any"); - val[any] = any; - QVERIFY(val.isMap()); - QCOMPARE(val.toMap().size(), 5); - QVERIFY(val[2].isArray()); - QCOMPARE(val[2].toArray().size(), 5); } void tst_QCborValue::arrayMutateWithCopies() @@ -1051,27 +1138,27 @@ void tst_QCborValue::mapMutation() const QString strValue = QStringLiteral("value"); v = strValue; QVERIFY(v.isString()); - QCOMPARE(v, QCborValue(strValue)); - QCOMPARE(m, QCborMap({{42, strValue}})); + QT_TEST_EQUALITY_OPS(v, QCborValue(strValue), true); + QT_TEST_EQUALITY_OPS(m, QCborMap({{42, strValue}}), true); // HasByteData -> HasByteData const QLatin1String otherStrValue("othervalue"); v = otherStrValue; QVERIFY(v.isString()); - QCOMPARE(v, QCborValue(otherStrValue)); - QCOMPARE(m, QCborMap({{42, otherStrValue}})); + QT_TEST_EQUALITY_OPS(v, QCborValue(otherStrValue), true); + QT_TEST_EQUALITY_OPS(m, QCborMap({{42, otherStrValue}}), true); // HasByteData -> simple v = 42; QVERIFY(v.isInteger()); - QCOMPARE(v, QCborValue(42)); - QCOMPARE(m, QCborMap({{42, 42}})); + QT_TEST_EQUALITY_OPS(v, QCborValue(42), true); + QT_TEST_EQUALITY_OPS(m, QCborMap({{42, 42}}), true); // simple -> container v = QCborArray{1, 2, 3}; QVERIFY(v.isArray()); - QCOMPARE(v, QCborArray({1, 2, 3})); - QCOMPARE(m, QCborMap({{42, QCborArray{1, 2, 3}}})); + QT_TEST_EQUALITY_OPS(v, QCborArray({1, 2, 3}), true); + QT_TEST_EQUALITY_OPS(m, QCborMap({{42, QCborArray{1, 2, 3}}}), true); // container -> simple v = true; @@ -1079,8 +1166,7 @@ void tst_QCborValue::mapMutation() QVERIFY(v.isTrue()); QCOMPARE(m, QCborMap({{42, true}})); QVERIFY(m.begin()->isTrue()); - QVERIFY(m.begin().value() == v); - QVERIFY(v == m.begin().value()); + QT_TEST_EQUALITY_OPS(m.begin().value(), v, true); } QVERIFY(m == QCborMap({{42, true}})); @@ -1102,18 +1188,27 @@ void tst_QCborValue::mapMutation() m2 = m; auto it = m.begin(); // detaches again auto end = m.end(); + auto it1 = m.constBegin(); // detaches again + auto end2 = m.constEnd(); QCOMPARE(end - it, 2); + QT_TEST_ALL_COMPARISON_OPS(it, it + 1, Qt::strong_ordering::less); + QT_TEST_ALL_COMPARISON_OPS(it, it1 + 1, Qt::strong_ordering::less); + QT_TEST_ALL_COMPARISON_OPS(it, it - 1, Qt::strong_ordering::greater); + QT_TEST_ALL_COMPARISON_OPS(it, it1 - 1, Qt::strong_ordering::greater); + QT_TEST_EQUALITY_OPS(it, it1, true); QCOMPARE(it + 2, end); - QCOMPARE(it.key(), QCborValue(42)); - QCOMPARE(it.value(), QCborValue(2.5)); - QCOMPARE((++it).value(), QCborValue(nullptr)); - QCOMPARE(it.key(), QCborValue(nullptr)); - QVERIFY(m2 == m); - QVERIFY(m == m2); + QT_TEST_EQUALITY_OPS(it + 2, end, true); + QT_TEST_EQUALITY_OPS(it + 2, end2, true); + QT_TEST_EQUALITY_OPS(it1 + 2, end2, true); + QT_TEST_EQUALITY_OPS(it.key(), QCborValue(42), true); + QT_TEST_EQUALITY_OPS(it.value(), QCborValue(2.5), true); + QT_TEST_EQUALITY_OPS((++it).value(), QCborValue(nullptr), true); + QT_TEST_EQUALITY_OPS(it.key(), QCborValue(nullptr), true); + QT_TEST_EQUALITY_OPS(m2, m, true); it.value() = -1; - QCOMPARE(it.key(), QCborValue(nullptr)); - QCOMPARE(it.value(), QCborValue(-1)); + QT_TEST_EQUALITY_OPS(it.key(), QCborValue(nullptr), true); + QT_TEST_EQUALITY_OPS(it.value(), QCborValue(-1), true); QCOMPARE((m.end() - 1)->toInteger(), -1); QVERIFY((m2.end() - 1)->isNull()); QCOMPARE(++it, end); @@ -1160,11 +1255,13 @@ void tst_QCborValue::mapMutateWithCopies() // see QTBUG-83366 QCborMap map; map[QLatin1String("value")] = "TEST"; + QT_TEST_EQUALITY_OPS(map[QLatin1String("value")], "TEST", true); QCOMPARE(map.size(), 1); QCOMPARE(map.value("value"), "TEST"); QCborValue v = map.value("value"); map[QLatin1String("prop2")] = v; + QT_TEST_EQUALITY_OPS(map[QLatin1String("prop2")], v, true); QCOMPARE(map.size(), 2); QCOMPARE(map.value("value"), "TEST"); QCOMPARE(map.value("prop2"), "TEST"); @@ -1178,6 +1275,7 @@ void tst_QCborValue::mapMutateWithCopies() // same as previous, but this is a QJsonValueRef QCborValueRef rv = map[QLatin1String("prop2")]; rv = map[QLatin1String("value")]; + QT_TEST_EQUALITY_OPS(map[QLatin1String("value")], rv, true); QCOMPARE(map.size(), 2); QCOMPARE(map.value("value"), "TEST"); QCOMPARE(map.value("prop2"), "TEST"); @@ -1192,6 +1290,7 @@ void tst_QCborValue::mapMutateWithCopies() // after we create the source QCborValueRef QCborValueRef rv = map[QLatin1String("value")]; map[QLatin1String("prop2")] = rv; + QT_TEST_EQUALITY_OPS(map[QLatin1String("prop2")], rv, true); QCOMPARE(map.size(), 2); QCOMPARE(map.value("value"), "TEST"); QCOMPARE(map.value("prop2"), "TEST"); @@ -1218,11 +1317,102 @@ void tst_QCborValue::arrayPrepend() QCborArray a; a.prepend(0); a.prepend(nullptr); - QCOMPARE(a.at(1), QCborValue(0)); - QCOMPARE(a.at(0), QCborValue(nullptr)); + QT_TEST_EQUALITY_OPS(a.at(1), QCborValue(0), true); + QT_TEST_EQUALITY_OPS(a.at(0), QCborValue(nullptr), true); QCOMPARE(a.size(), 2); } +void tst_QCborValue::arrayValueRef() +{ + QFETCH(QCborValue, v); + QCborArray a = { v }; + + // methods that return QCborValueRef + QT_TEST_EQUALITY_OPS(a.first(), v, true); + QT_TEST_EQUALITY_OPS(a.last(), v, true); + QT_TEST_EQUALITY_OPS(a[0], v, true); + QVERIFY(v == a.first()); + QVERIFY(v == a.last()); + QVERIFY(v == a[0]); + QT_TEST_EQUALITY_OPS(a.first(), v, true); + QT_TEST_EQUALITY_OPS(a.last(), v, true); + + auto iteratorCheck = [&v](auto it) { + QT_TEST_EQUALITY_OPS(*it, v, true); + QCOMPARE(it->type(), v.type()); // just to test operator-> + QT_TEST_EQUALITY_OPS(it[0], v, true); + }; + + iteratorCheck(a.begin()); + if (QTest::currentTestFailed()) + return; + iteratorCheck(a.constBegin()); +} + +void tst_QCborValue::arrayValueRefLargeKey() +{ + // make sure the access via QCborValue & QCborValueRef don't convert this + // array to a map + constexpr qsizetype LargeKey = 0x10000; + QCborArray a; + a[LargeKey + 1] = 123; + + QCborValue v(a); + QT_TEST_EQUALITY_OPS(std::as_const(v)[LargeKey], QCborValue(), true); + QCOMPARE(std::as_const(v)[LargeKey + 1], 123); + QT_TEST_EQUALITY_OPS(v[LargeKey], QCborValue(), true); + QCOMPARE(v[LargeKey + 1], 123); + QCOMPARE(v.type(), QCborValue::Array); + + QCborArray outer = { QCborValue(a) }; + QCborValueRef ref = outer[0]; + QT_TEST_EQUALITY_OPS(std::as_const(ref)[LargeKey], QCborValue(), true); + QCOMPARE(std::as_const(ref)[LargeKey + 1], 123); + QT_TEST_EQUALITY_OPS(ref[LargeKey], QCborValue(), true); + QCOMPARE(ref[LargeKey + 1], 123); + QCOMPARE(ref.type(), QCborValue::Array); +} + +void tst_QCborValue::mapValueRef() +{ + QFETCH(QCborValue, v); + QLatin1String stringKey("other string"); + qint64 intKey = 47; + Q_ASSERT(v != stringKey); + Q_ASSERT(v != intKey); + + QCborMap m = { { v, v }, { stringKey, v }, { intKey, v } }; + QCOMPARE(m.size(), 3); + + // methods that return QCborValueRef + QT_TEST_EQUALITY_OPS(m[intKey], v, true); + QT_TEST_EQUALITY_OPS(m[stringKey], v, true); + QT_TEST_EQUALITY_OPS(m[v], v, true); + QVERIFY(v == m[intKey]); + QVERIFY(v == m[stringKey]); + QVERIFY(v == m[v]); + + auto iteratorCheck = [=](auto it) { + QCOMPARE((*it).second, v); + QCOMPARE(it[0].second, v); + QCOMPARE(it[1].second, v); + QCOMPARE(it[2].second, v); + QCOMPARE(it.value(), v); + QCOMPARE(it->type(), v.type()); // just to test operator-> + + // compare keys too + QCOMPARE((*it).first, v); + QCOMPARE(it.key(), v); + QCOMPARE((it + 1).key(), stringKey); + QCOMPARE((it + 2).key(), intKey); + }; + + iteratorCheck(m.begin()); + if (QTest::currentTestFailed()) + return; + iteratorCheck(m.constBegin()); +} + void tst_QCborValue::arrayInsertRemove() { QFETCH(QCborValue, v); @@ -1230,28 +1420,28 @@ void tst_QCborValue::arrayInsertRemove() a.append(42); a.append(v); a.insert(1, QCborValue(nullptr)); - QCOMPARE(a.at(0), QCborValue(42)); - QCOMPARE(a.at(1), QCborValue(nullptr)); - QCOMPARE(a.at(2), v); + QT_TEST_EQUALITY_OPS(a.at(0), QCborValue(42), true); + QT_TEST_EQUALITY_OPS(a.at(1), QCborValue(nullptr), true); + QT_TEST_EQUALITY_OPS(a.at(2), v, true); // remove 42 a.removeAt(0); QCOMPARE(a.size(), 2); - QCOMPARE(a.at(0), QCborValue(nullptr)); - QCOMPARE(a.at(1), v); + QT_TEST_EQUALITY_OPS(a.at(0), QCborValue(nullptr), true); + QT_TEST_EQUALITY_OPS(a.at(1), v, true); auto it = a.begin(); it = a.erase(it); // removes nullptr QCOMPARE(a.size(), 1); - QCOMPARE(a.at(0), v); + QT_TEST_EQUALITY_OPS(a.at(0), v, true); it = a.erase(it); QVERIFY(a.isEmpty()); - QCOMPARE(it, a.end()); + QT_TEST_EQUALITY_OPS(it, a.end(), true); // reinsert the element so we can take it a.append(v); - QCOMPARE(a.takeAt(0), v); + QT_TEST_EQUALITY_OPS(a.takeAt(0), v, true); QVERIFY(a.isEmpty()); } @@ -1260,14 +1450,15 @@ void tst_QCborValue::arrayStringElements() QCborArray a{"Hello"}; a.append(QByteArray("Hello")); a.append(QLatin1String("World")); - QVERIFY(a == a); - QVERIFY(a == QCborArray({QLatin1String("Hello"), - QByteArray("Hello"), QStringLiteral("World")})); + + QT_TEST_EQUALITY_OPS(a, a, true); + QT_TEST_EQUALITY_OPS(a, QCborArray({QLatin1String("Hello"), + QByteArray("Hello"), QStringLiteral("World")}), true); QCborValueRef r1 = a[0]; QCOMPARE(r1.toString(), "Hello"); QCOMPARE(r1.operator QCborValue(), QCborValue("Hello")); - QVERIFY(r1 == QCborValue("Hello")); + QT_TEST_EQUALITY_OPS(r1, QCborValue("Hello"), true); QCborValue v2 = a.at(1); QCOMPARE(v2.toByteArray(), QByteArray("Hello")); @@ -1276,11 +1467,11 @@ void tst_QCborValue::arrayStringElements() // v2 must continue to be valid after the entry getting removed a.removeAt(1); QCOMPARE(v2.toByteArray(), QByteArray("Hello")); - QCOMPARE(v2, QCborValue(QByteArray("Hello"))); + QT_TEST_EQUALITY_OPS(v2, QCborValue(QByteArray("Hello")), true); v2 = a.at(1); QCOMPARE(v2.toString(), "World"); - QCOMPARE(v2, QCborValue("World")); + QT_TEST_EQUALITY_OPS(v2, QCborValue("World"), true); QCOMPARE(a.takeAt(1).toString(), "World"); QCOMPARE(a.takeAt(0).toString(), "Hello"); @@ -1292,12 +1483,12 @@ void tst_QCborValue::mapStringValues() QCborMap m{{0, "Hello"}}; m.insert({1, QByteArray("Hello")}); m.insert({2, QLatin1String("World")}); - QVERIFY(m == m); + QT_TEST_EQUALITY_OPS(m, m, true); QCborValueRef r1 = m[0]; QCOMPARE(r1.toString(), "Hello"); QCOMPARE(r1.operator QCborValue(), QCborValue("Hello")); - QVERIFY(r1 == QCborValue("Hello")); + QT_TEST_EQUALITY_OPS(r1, QCborValue("Hello"), true); QCborValue v2 = m.value(1); QCOMPARE(v2.toByteArray(), QByteArray("Hello")); @@ -1306,7 +1497,7 @@ void tst_QCborValue::mapStringValues() // v2 must continue to be valid after the entry getting removed m.erase(m.constFind(1)); QCOMPARE(v2.toByteArray(), QByteArray("Hello")); - QCOMPARE(v2, QCborValue(QByteArray("Hello"))); + QT_TEST_EQUALITY_OPS(v2, QCborValue(QByteArray("Hello")), true); v2 = (m.begin() + 1).value(); QCOMPARE(v2.toString(), "World"); @@ -1324,14 +1515,12 @@ void tst_QCborValue::mapStringKeys() QCOMPARE(m.value(QLatin1String("World")), QCborValue(2)); QCborMap m2 = m; - QVERIFY(m2 == m); - QVERIFY(m == m2); + QT_TEST_EQUALITY_OPS(m2, m, true); m.insert({QByteArray("foo"), "bar"}); QCOMPARE(m.size(), 3); QCOMPARE(m2.size(), 2); - QVERIFY(m2 != m); - QVERIFY(m != m2); + QT_TEST_EQUALITY_OPS(m2, m, false); QVERIFY(m2.value(QCborValue(QByteArray("foo"))).isUndefined()); QVERIFY(m.value(QCborValue(QLatin1String("foo"))).isUndefined()); @@ -1349,8 +1538,7 @@ void tst_QCborValue::mapInsertRemove() m.insert(2, v); QVERIFY(m.contains(2)); - QVERIFY(m[2] == v); - QVERIFY(v == m[2]); + QT_TEST_EQUALITY_OPS(m[2], v, true); auto it = m.find(2); it = m.erase(it); @@ -1363,10 +1551,8 @@ void tst_QCborValue::mapInsertRemove() r = v; it = m.find(42); - QVERIFY(it.value() == v); - QVERIFY(v == it.value()); - QVERIFY(it.value() == r); - QVERIFY(r == it.value()); + QT_TEST_EQUALITY_OPS(it.value(), v, true); + QT_TEST_EQUALITY_OPS(it.value(), r, true); QCOMPARE(m.extract(it), v); QVERIFY(!m.contains(42)); @@ -1387,12 +1573,12 @@ void tst_QCborValue::arrayInsertTagged() QCborArray a{tagged}; a.insert(1, tagged); QCOMPARE(a.size(), 2); - QCOMPARE(a.at(0), tagged); - QCOMPARE(a.at(1), tagged); - QCOMPARE(a.at(0).taggedValue(), v); - QCOMPARE(a.at(1).taggedValue(), v); - QCOMPARE(a.takeAt(0).taggedValue(), v); - QCOMPARE(a.takeAt(0).taggedValue(), v); + QT_TEST_EQUALITY_OPS(a.at(0), tagged, true); + QT_TEST_EQUALITY_OPS(a.at(1), tagged, true); + QT_TEST_EQUALITY_OPS(a.at(0).taggedValue(), v, true); + QT_TEST_EQUALITY_OPS(a.at(1).taggedValue(), v, true); + QT_TEST_EQUALITY_OPS(a.takeAt(0).taggedValue(), v, true); + QT_TEST_EQUALITY_OPS(a.takeAt(0).taggedValue(), v, true); QVERIFY(a.isEmpty()); } @@ -1406,13 +1592,13 @@ void tst_QCborValue::mapInsertTagged() QCborMap m{{11, tagged}}; m.insert({-21, tagged}); QCOMPARE(m.size(), 2); - QCOMPARE(m.constBegin().value(), tagged); - QCOMPARE(m.value(-21), tagged); - QCOMPARE(m.value(11).taggedValue(), v); - QCOMPARE((m.end() - 1).value().taggedValue(), v); - QCOMPARE(m.extract(m.end() - 1).taggedValue(), v); + QT_TEST_EQUALITY_OPS(m.constBegin().value(), tagged, true); + QT_TEST_EQUALITY_OPS(m.value(-21), tagged, true); + QT_TEST_EQUALITY_OPS(m.value(11).taggedValue(), v, true); + QT_TEST_EQUALITY_OPS((m.end() - 1).value().taggedValue(), v, true); + QT_TEST_EQUALITY_OPS(m.extract(m.end() - 1).taggedValue(), v, true); QVERIFY(!m.contains(-21)); - QCOMPARE(m.take(11).taggedValue(), v); + QT_TEST_EQUALITY_OPS(m.take(11).taggedValue(), v, true); QVERIFY(m.isEmpty()); } @@ -1441,7 +1627,7 @@ void tst_QCborValue::arraySelfAssign() QCOMPARE(a.size(), 2); QCOMPARE(it->toArray().size(), 2); - QCOMPARE(it->toArray().last(), QCborValue(36)); + QT_TEST_EQUALITY_OPS(it->toArray().last(), QCborValue(36), true); } } @@ -1459,12 +1645,12 @@ void tst_QCborValue::mapSelfAssign() QCborValue vm = m; m[1] = vm; // self-assign QCOMPARE(m.size(), 2); - QCOMPARE(m.value(0), v); + QT_TEST_EQUALITY_OPS(m.value(0), v, true); QCborMap m2 = m.value(1).toMap(); // there mustn't be an element with key 1 QCOMPARE(m2.size(), 1); - QCOMPARE(m2.value(0), v); + QT_TEST_EQUALITY_OPS(m2.value(0), v, true); QVERIFY(!m2.contains(1)); } @@ -1476,14 +1662,14 @@ void tst_QCborValue::mapSelfAssign() QCborValueRef rv = m[1]; rv = m; // self-assign (implicit QCborValue creation) QCOMPARE(m.size(), 2); - QCOMPARE(m.value(0), v); + QT_TEST_EQUALITY_OPS(m.value(0), v, true); QCborMap m2 = m.value(1).toMap(); // there must be an element with key 1 QCOMPARE(m2.size(), 2); - QCOMPARE(m2.value(0), v); + QT_TEST_EQUALITY_OPS(m2.value(0), v, true); QVERIFY(m2.contains(1)); - QCOMPARE(m2.value(1), QCborValue()); + QT_TEST_EQUALITY_OPS(m2.value(1), QCborValue(), true); } m = {{0, v}}; @@ -1507,8 +1693,8 @@ void tst_QCborValue::mapSelfAssign() QCOMPARE(m.size(), 2); auto it = m.constEnd() - 1; - QCOMPARE(it.value(), v); - QCOMPARE(it.key(), QCborMap({{0, v}})); + QT_TEST_EQUALITY_OPS(it.value(), v, true); + QT_TEST_EQUALITY_OPS(it.key(), QCborMap({{0, v}}), true); } } @@ -1581,8 +1767,125 @@ void tst_QCborValue::mapComplexKeys() QVERIFY(!m.contains(tagged)); } -void tst_QCborValue::sorting() +void tst_QCborValue::arrayNested() +{ + const QCborArray wrongArray = { false, nullptr, QCborValue() }; + { + QCborArray a1 = { 42, 47 }; + QCborArray a2 = { QCborValue(a1) }; + QCborArray a3 = { 41, 47 }; + QCborArray a4 = { 41, 47, 87 }; + QCOMPARE(a2.size(), 1); + const QCborValue &first = std::as_const(a2).first(); + QVERIFY(first.isArray()); + QCOMPARE(first.toArray(wrongArray).size(), 2); + QCOMPARE(first.toArray(wrongArray).first(), 42); + QCOMPARE(first.toArray(wrongArray).last(), 47); + QT_TEST_ALL_COMPARISON_OPS(a1, a3, Qt::strong_ordering::greater); + QT_TEST_ALL_COMPARISON_OPS(a3, a1, Qt::strong_ordering::less); + QT_TEST_ALL_COMPARISON_OPS(a3, a4, Qt::strong_ordering::less); + QT_TEST_ALL_COMPARISON_OPS(a3, a2, Qt::strong_ordering::greater); + } + { + QCborArray a1 = { 42, 47 }; + QCborArray a2 = { QCborValue(a1) }; + QCborArray a3 = { 41, 47 }; + QCborArray a4 = { 41, 47, 87 }; + QCOMPARE(a2.size(), 1); + QCborValueRef first = a2.first(); + QVERIFY(first.isArray()); + QCOMPARE(first.toArray(wrongArray).size(), 2); + QCOMPARE(first.toArray(wrongArray).first(), 42); + QCOMPARE(first.toArray(wrongArray).last(), 47); + QT_TEST_ALL_COMPARISON_OPS(a1, a3, Qt::strong_ordering::greater); + QT_TEST_ALL_COMPARISON_OPS(a3, a1, Qt::strong_ordering::less); + QT_TEST_ALL_COMPARISON_OPS(a3, a4, Qt::strong_ordering::less); + QT_TEST_ALL_COMPARISON_OPS(a3, a2, Qt::strong_ordering::greater); + } + + { + QCborArray a1; + a1 = { QCborValue(a1) }; // insert it into itself + QCOMPARE(a1.size(), 1); + const QCborValue &first = std::as_const(a1).first(); + QVERIFY(first.isArray()); + QT_TEST_ALL_COMPARISON_OPS(first, QCborArray(), Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(first.toArray(wrongArray), QCborArray(), + Qt::strong_ordering::equal); + } + { + QCborArray a1; + a1 = { QCborValue(a1) }; // insert it into itself + QCborValueRef first = a1.first(); + QVERIFY(first.isArray()); + QT_TEST_ALL_COMPARISON_OPS(first, QCborArray(), Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(first.toArray(wrongArray), QCborArray(), + Qt::strong_ordering::equal); + } + { + QCborArray a1; + a1.append(a1); // insert into itself + QCOMPARE(a1.size(), 1); + const QCborValue &first = std::as_const(a1).first(); + QVERIFY(first.isArray()); + QT_TEST_ALL_COMPARISON_OPS(first, QCborArray(), Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(first.toArray(wrongArray), QCborArray(), + Qt::strong_ordering::equal); + } + { + QCborArray a1; + a1.append(a1); // insert into itself + QCborValueRef first = a1.first(); + QVERIFY(first.isArray()); + QT_TEST_ALL_COMPARISON_OPS(first, QCborArray(), Qt::strong_ordering::equal); + QT_TEST_ALL_COMPARISON_OPS(first.toArray(wrongArray), QCborArray(), + Qt::strong_ordering::equal); + } +} + +void tst_QCborValue::mapNested() { + const QCborMap wrongMap = { { -1, false }, {-2, nullptr }, { -3, QCborValue() } }; + { + QCborMap m1 = { {1, 42}, {2, 47} }; + QCborMap m2 = { { QString(), m1 } }; + QCOMPARE(m2.size(), 1); + const QCborValue &first = m2.constBegin().value(); + QVERIFY(first.isMap()); + QCOMPARE(first.toMap(wrongMap).size(), 2); + QCOMPARE(first.toMap(wrongMap).begin().key(), 1); + QCOMPARE(first.toMap(wrongMap).begin().value(), 42); + } + + { + QCborMap m1; + m1 = { { QString(), QCborValue(m1) } }; // insert it into itself + QCOMPARE(m1.size(), 1); + const QCborValue &first = m1.constBegin().value(); + QVERIFY(first.isMap()); + QCOMPARE(first, QCborMap()); + QCOMPARE(first.toMap(wrongMap), QCborMap()); + } +} + +void tst_QCborValue::sorting_data() +{ + // CBOR data comparisons are done as if we were comparing their canonically + // (deterministic) encoded forms in the byte stream, including the Major + // Type. That has a few surprises noted below: + // 1) because the length of a string precedes it, effectively strings are + // sorted by their UTF-8 length before their contents + // 2) because negative integers are stored in negated form, they sort in + // descending order (i.e. by absolute value) + // 3) negative integers (Major Type 1) sort after all positive integers + // (Major Type 0) + // Effectively, this means integers are sorted as sign+magnitude. + // 4) floating point types (Major Type 7) sort after all integers + + QTest::addColumn<QCborValue>("lhs"); + QTest::addColumn<QCborValue>("rhs"); + QTest::addColumn<Qt::strong_ordering>("expectedOrdering"); + QCborValue vundef, vnull(nullptr); QCborValue vtrue(true), vfalse(false); QCborValue vint1(1), vint2(2); @@ -1591,7 +1894,8 @@ void tst_QCborValue::sorting() QCborValue vs2("Hello"), vs3("World"), vs1("foo"); QCborValue va1(QCborValue::Array), va2(QCborArray{1}), va3(QCborArray{0, 0}); QCborValue vm1(QCborValue::Map), vm2(QCborMap{{1, 0}}), vm3(QCborMap{{0, 0}, {1, 0}}); - QCborValue vdt1(QDateTime::fromMSecsSinceEpoch(0, Qt::UTC)), vdt2(QDateTime::currentDateTimeUtc()); + QCborValue vdt1(QDateTime::fromMSecsSinceEpoch(0, QTimeZone::UTC)); + QCborValue vdt2(QDateTime::currentDateTimeUtc()); QCborValue vtagged1(QCborKnownTags::PositiveBignum, QByteArray()), vtagged2(QCborKnownTags::PositiveBignum, 0.0), // bignums are supposed to have byte arrays... vtagged3(QCborKnownTags::Signature, 0), @@ -1600,67 +1904,262 @@ void tst_QCborValue::sorting() QCborValue vurl1(QUrl("https://example.net")), vurl2(QUrl("https://example.com/")); QCborValue vuuid1{QUuid()}, vuuid2(QUuid::createUuid()); QCborValue vsimple1(QCborSimpleType(1)), vsimple32(QCborSimpleType(32)), vsimple255(QCborSimpleType(255)); - QCborValue vdouble1(1.5), vdouble2(qInf()); + QCborValue vdouble1(1.5), vdouble2(qInf()), vdouble3(qQNaN()); QCborValue vndouble1(-1.5), vndouble2(-qInf()); -#define CHECK_ORDER(v1, v2) \ - QVERIFY(v1 < v2); \ - QVERIFY(!(v2 < v2)) + auto addRow = [](QCborValue lhs, QCborValue rhs, Qt::strong_ordering order) { + QTest::addRow("%s-cmp-%s", qPrintable(lhs.toDiagnosticNotation()), + qPrintable(rhs.toDiagnosticNotation())) + << lhs << rhs << order; + }; + auto addSelfCmp = [](QCborValue v) { + QTest::addRow("self-%s", qPrintable(v.toDiagnosticNotation())) + << v << v << Qt::strong_ordering::equal; + }; + + // self compares + addSelfCmp(vundef); + addSelfCmp(vnull); + addSelfCmp(vfalse); + addSelfCmp(vtrue); + addSelfCmp(vint1); + addSelfCmp(vint2); + addSelfCmp(vneg1); + addSelfCmp(vneg2); + addSelfCmp(vba1); + addSelfCmp(vba2); + addSelfCmp(vba3); + addSelfCmp(vs1); + addSelfCmp(vs2); + addSelfCmp(vs3); + addSelfCmp(va1); + addSelfCmp(va2); + addSelfCmp(va3); + addSelfCmp(vm1); + addSelfCmp(vm2); + addSelfCmp(vm3); + addSelfCmp(vdt1); + addSelfCmp(vdt2); + addSelfCmp(vtagged1); + addSelfCmp(vtagged2); + addSelfCmp(vtagged3); + addSelfCmp(vtagged4); + addSelfCmp(vtagged5); + addSelfCmp(vurl1); + addSelfCmp(vurl2); + addSelfCmp(vuuid1); + addSelfCmp(vuuid2); + addSelfCmp(vsimple1); + addSelfCmp(vsimple32); + addSelfCmp(vsimple255); + addSelfCmp(vdouble1); + addSelfCmp(vdouble2); + addSelfCmp(vdouble3); // surprise: NaNs do compare + addSelfCmp(vndouble1); + addSelfCmp(vndouble2); // intra-type comparisons - CHECK_ORDER(vfalse, vtrue); - CHECK_ORDER(vsimple1, vsimple32); - CHECK_ORDER(vsimple32, vsimple255); - CHECK_ORDER(vint1, vint2); - CHECK_ORDER(vdouble1, vdouble2); - CHECK_ORDER(vndouble1, vndouble2); - // note: shorter length sorts first - CHECK_ORDER(vba1, vba2); - CHECK_ORDER(vba2, vba3); - CHECK_ORDER(vs1, vs2); - CHECK_ORDER(vs2, vs3); - CHECK_ORDER(va1, va2); - CHECK_ORDER(va2, va3); - CHECK_ORDER(vm1, vm2); - CHECK_ORDER(vm2, vm3); - CHECK_ORDER(vdt1, vdt2); - CHECK_ORDER(vtagged1, vtagged2); - CHECK_ORDER(vtagged2, vtagged3); - CHECK_ORDER(vtagged3, vtagged4); - CHECK_ORDER(vtagged4, vtagged5); - CHECK_ORDER(vurl1, vurl2); - CHECK_ORDER(vuuid1, vuuid2); - - // surprise 1: CBOR sorts integrals by absolute value - CHECK_ORDER(vneg1, vneg2); - - // surprise 2: CBOR sorts negatives after positives (sign+magnitude) - CHECK_ORDER(vint2, vneg1); - QVERIFY(vint2.toInteger() > vneg1.toInteger()); - CHECK_ORDER(vdouble2, vndouble1); - QVERIFY(vdouble2.toDouble() > vndouble1.toDouble()); + addRow(vfalse, vtrue, Qt::strong_ordering::less); + addRow(vsimple1, vsimple32, Qt::strong_ordering::less); + addRow(vsimple32, vsimple255, Qt::strong_ordering::less); + addRow(vint1, vint2, Qt::strong_ordering::less); + addRow(vdouble1, vdouble2, Qt::strong_ordering::less); + addRow(vdouble2, vdouble3, Qt::strong_ordering::less); // surprise: NaNs do compare + addRow(vndouble1, vndouble2, Qt::strong_ordering::less); // surprise: -1.5 < -inf + addRow(va1, va2, Qt::strong_ordering::less); + addRow(va2, va3, Qt::strong_ordering::less); + addRow(vm1, vm2, Qt::strong_ordering::less); + addRow(vm2, vm3, Qt::strong_ordering::less); + addRow(vdt1, vdt2, Qt::strong_ordering::less); + addRow(vtagged1, vtagged2, Qt::strong_ordering::less); + addRow(vtagged2, vtagged3, Qt::strong_ordering::less); + addRow(vtagged3, vtagged4, Qt::strong_ordering::less); + addRow(vtagged4, vtagged5, Qt::strong_ordering::less); + addRow(vurl1, vurl2, Qt::strong_ordering::less); + addRow(vuuid1, vuuid2, Qt::strong_ordering::less); + + // surprise 1: CBOR sorts strings by length first + addRow(vba1, vba2, Qt::strong_ordering::less); + addRow(vba2, vba3, Qt::strong_ordering::less); + addRow(vs1, vs2, Qt::strong_ordering::less); + addRow(vs2, vs3, Qt::strong_ordering::less); + + // surprise 2: CBOR sorts integrals by absolute value + addRow(vneg1, vneg2, Qt::strong_ordering::less); + + // surprise 3: CBOR sorts negatives after positives (sign+magnitude) + addRow(vint2, vneg1, Qt::strong_ordering::less); + addRow(vdouble2, vndouble1, Qt::strong_ordering::less); // inter-type comparisons - CHECK_ORDER(vneg2, vba1); - CHECK_ORDER(vba3, vs1); - CHECK_ORDER(vs3, va1); - CHECK_ORDER(va2, vm1); - CHECK_ORDER(vm2, vdt1); - CHECK_ORDER(vdt2, vtagged1); - CHECK_ORDER(vtagged2, vurl1); - CHECK_ORDER(vurl1, vuuid1); - CHECK_ORDER(vuuid2, vtagged3); - CHECK_ORDER(vtagged4, vsimple1); - CHECK_ORDER(vsimple1, vfalse); - CHECK_ORDER(vtrue, vnull); - CHECK_ORDER(vnull, vundef); - CHECK_ORDER(vundef, vsimple32); - CHECK_ORDER(vsimple255, vdouble1); + addRow(vneg2, vba1, Qt::strong_ordering::less); + addRow(vba3, vs1, Qt::strong_ordering::less); + addRow(vs3, va1, Qt::strong_ordering::less); + addRow(va2, vm1, Qt::strong_ordering::less); + addRow(vm2, vdt1, Qt::strong_ordering::less); + addRow(vdt2, vtagged1, Qt::strong_ordering::less); + addRow(vtagged2, vurl1, Qt::strong_ordering::less); + addRow(vurl1, vuuid1, Qt::strong_ordering::less); + addRow(vuuid2, vtagged3, Qt::strong_ordering::less); + addRow(vtagged4, vsimple1, Qt::strong_ordering::less); + addRow(vsimple1, vfalse, Qt::strong_ordering::less); + addRow(vtrue, vnull, Qt::strong_ordering::less); + addRow(vnull, vundef, Qt::strong_ordering::less); + addRow(vundef, vsimple32, Qt::strong_ordering::less); + addRow(vsimple255, vdouble1, Qt::strong_ordering::less); // which shows all doubles sorted after integrals - CHECK_ORDER(vint2, vdouble1); - QVERIFY(vint2.toInteger() > vdouble1.toDouble()); -#undef CHECK_ORDER + addRow(vint2, vdouble1, Qt::strong_ordering::less); + + // Add some non-US-ASCII strings. In the current implementation, QCborValue + // can store a string as either US-ASCII, UTF-8, or UTF-16, so let's exercise + // those comparisons. + + // we don't have a QUtf8StringView constructor, so work around it + auto utf8string = [](QByteArray str) { + Q_ASSERT(str.size() < 24); + str.prepend(char(QCborValue::String) + str.size()); + return QCborValue::fromCbor(str); + }; + + auto addStringCmp = [&](const char *prefix, const char *tag, QUtf8StringView lhs, + QUtf8StringView rhs) { + // CBOR orders strings by UTF-8 length + auto order = Qt::compareThreeWay(lhs.size(), rhs.size()); + if (is_eq(order)) + order = compareThreeWay(lhs, rhs); + Q_ASSERT(is_eq(order) || is_lt(order)); // please keep lhs <= rhs! + + QCborValue lhs_utf8 = utf8string(QByteArrayView(lhs).toByteArray()); + QCborValue rhs_utf8 = utf8string(QByteArrayView(rhs).toByteArray()); + QCborValue lhs_utf16 = QString::fromUtf8(lhs); + QCborValue rhs_utf16 = QString::fromUtf8(rhs); + + QTest::addRow("string-%s%s:utf8-utf8", prefix, tag) << lhs_utf8 << rhs_utf8 << order; + QTest::addRow("string-%s%s:utf8-utf16", prefix, tag) << lhs_utf8 << rhs_utf16 << order; + QTest::addRow("string-%s%s:utf16-utf8", prefix, tag) << lhs_utf16 << rhs_utf8 << order; + QTest::addRow("string-%s%s:utf16-utf16", prefix, tag) << lhs_utf16 << rhs_utf16 << order; + }; + auto addStringCmpSameLength = [&](const char *tag, QUtf8StringView lhs, QUtf8StringView rhs) { + Q_ASSERT(lhs.size() == rhs.size()); + addStringCmp("samelength-", tag, lhs, rhs); + }; + auto addStringCmpShorter = [&](const char *tag, QUtf8StringView lhs, QUtf8StringView rhs) { + Q_ASSERT(lhs.size() < rhs.size()); + addStringCmp("shorter-", tag, lhs, rhs); + }; + + // ascii-only is already tested + addStringCmp("equal-", "1continuation", "ab\u00A0c", "ab\u00A0c"); + addStringCmp("equal-", "2continuation", "ab\u0800", "ab\u0800"); + addStringCmp("equal-", "3continuation", "a\U00010000", "a\U00010000"); + + // these strings all have the same UTF-8 length (5 bytes) + addStringCmpSameLength("less-ascii", "abcde", "ab\u00A0c"); + addStringCmpSameLength("less-1continuation", "ab\u00A0c", "ab\u07FFc"); + addStringCmpSameLength("less-2continuation", "ab\u0800", "ab\uFFFC"); + addStringCmpSameLength("less-3continuation", "a\U00010000", "a\U0010FFFC"); + addStringCmpSameLength("less-0-vs-1continuation", "abcde", "ab\u00A0c"); + addStringCmpSameLength("less-0-vs-2continuation", "abcde", "ab\u0800"); + addStringCmpSameLength("less-0-vs-3continuation", "abcde", "a\U00010000"); + addStringCmpSameLength("less-1-vs-2continuation", "ab\u00A0c", "ab\uFFFC"); + addStringCmpSameLength("less-1-vs-3continuation", "ab\u00A0c", "a\U00010000"); + addStringCmpSameLength("less-2-vs-3continuation", "ab\u0800", "a\U00010000"); + addStringCmpSameLength("less-2-vs-3continuation_surrogate", "a\uFFFCz", "a\U00010000"); // even though U+D800 < U+FFFC + + // these strings have different lengths in UTF-8 + // (0continuation already tested) + addStringCmpShorter("1continuation", "ab\u00A0", "ab\u00A0c"); + addStringCmpShorter("2continuation", "ab\u0800", "ab\u0800c"); + addStringCmpShorter("3continuation", "ab\U00010000", "ab\U00010000c"); + // most of these have the same length in UTF-16! + addStringCmpShorter("0-vs-1continuation", "abc", "ab\u00A0"); + addStringCmpShorter("0-vs-2continuation", "abcd", "ab\u0800"); + addStringCmpShorter("0-vs-3continuation", "abcde", "ab\U00010000"); + addStringCmpShorter("1-vs-2continuation", "ab\u00A0", "ab\u0800"); + addStringCmpShorter("1-vs-3continuation", "abc\u00A0", "ab\U00010000"); + addStringCmpShorter("2-vs-3continuation", "ab\u0800", "ab\U00010000"); + + // lhs is 4xUTF-16 and 8xUTF-8; rhs is 3xUTF-16 but 9xUTF-8 + addStringCmpShorter("3x2-vs-2x3continuation", "\U00010000\U00010000", "\u0800\u0800\u0800"); + + // slight surprising because normally rhs would sort first ("aa" vs "ab" prefix) + // (0continuation_surprise already tested) + addStringCmpShorter("1continuation_surprise", "ab\u00A0", "aa\u00A0c"); + addStringCmpShorter("2continuation_surprise", "ab\u0800", "aa\u0800c"); + addStringCmpShorter("3continuation_surprise", "ab\U00010000", "aa\U00010000c"); + addStringCmpShorter("0-vs-1continuation_surprise", "abc", "aa\u00A0"); + addStringCmpShorter("0-vs-2continuation_surprise", "abcd", "aa\u0800"); + addStringCmpShorter("0-vs-3continuation_surprise", "abcde", "aa\U00010000"); + addStringCmpShorter("1-vs-2continuation_surprise", "ab\u00A0", "aa\u0800"); + addStringCmpShorter("1-vs-3continuation_surprise", "abc\u00A0", "aa\U00010000"); + addStringCmpShorter("2-vs-3continuation_surprise", "ab\u0800", "aa\U00010000"); +} + +void tst_QCborValue::sorting() +{ + QFETCH(QCborValue, lhs); + QFETCH(QCborValue, rhs); + QFETCH(Qt::strong_ordering, expectedOrdering); + + // do a QCOMPARE first so we get a proper QTest error in case QCborValue is + // broken + if (expectedOrdering == Qt::strong_ordering::equal) + QCOMPARE_EQ(lhs, rhs); + else if (expectedOrdering == Qt::strong_ordering::less) + QCOMPARE_LT(lhs, rhs); + else if (expectedOrdering == Qt::strong_ordering::greater) + QCOMPARE_GT(lhs, rhs); + + QCborArray array{lhs, rhs}; + + QCborValueConstRef lhsCRef = array.constBegin()[0]; + QCborValueConstRef rhsCRef = array.constBegin()[1]; + QCborValueRef lhsRef = array[0]; + QCborValueRef rhsRef = array[1]; + + // QCborValue vs QCborValue + QT_TEST_ALL_COMPARISON_OPS(lhs, rhs, expectedOrdering); + // QCborValueConstRef vs QCborValueConstRef + QT_TEST_ALL_COMPARISON_OPS(lhsCRef, rhsCRef, expectedOrdering); + // QCborValueRef vs QCborValueRef + QT_TEST_ALL_COMPARISON_OPS(lhsRef, rhsRef, expectedOrdering); + // QCborValue vs QCborValueConstRef (and reverse) + QT_TEST_ALL_COMPARISON_OPS(lhs, rhsCRef, expectedOrdering); + // QCborValue vs QCborValueRef (and reverse) + QT_TEST_ALL_COMPARISON_OPS(lhs, rhsRef, expectedOrdering); + // QCborValueConstRef vs QCborValueRef (and reverse) + QT_TEST_ALL_COMPARISON_OPS(lhsCRef, rhsRef, expectedOrdering); +} + +void tst_QCborValue::comparisonMap_data() +{ + QTest::addColumn<QCborMap>("left"); + QTest::addColumn<QCborMap>("right"); + QTest::addColumn<Qt::strong_ordering>("expectedOrdering"); + + QTest::addRow("map{{0, 1}, {10, 0}}, map{{10, 1}, {10, 0}}") + << QCborMap{{0, 1}, {10, 0}} + << QCborMap{{10, 1}, {10, 0}} + << Qt::strong_ordering::greater; + + QTest::addRow("map{{0, 1}, {0, 0}}, map{{0, 1}, {0, 0}}") + << QCborMap{{0, 1}, {0, 0}} + << QCborMap{{0, 1}, {0, 0}} + << Qt::strong_ordering::equivalent; + + QTest::addRow("map{{0, 1}, {10, 0}}, map{{10, 1}, {10, 0}, {10, 0}}") + << QCborMap{{10, 1}, {10, 0}} + << QCborMap{{0, 1}, {10, 0}, {10, 0}} + << Qt::strong_ordering::less; +} + +void tst_QCborValue::comparisonMap() +{ + QFETCH(QCborMap, left); + QFETCH(QCborMap, right); + QFETCH(Qt::strong_ordering, expectedOrdering); + QT_TEST_ALL_COMPARISON_OPS(left, right, expectedOrdering); } static void addCommonCborData() @@ -1686,7 +2185,6 @@ static void addCommonCborData() QTest::newRow("simple0") << QCborValue(QCborValue::SimpleType) << raw("\xe0") << noxfrm; QTest::newRow("simple1") << QCborValue(QCborSimpleType(1)) << raw("\xe1") << noxfrm; - QTest::newRow("simple255") << QCborValue(QCborSimpleType(255)) << raw("\xf8\xff") << noxfrm; QTest::newRow("Undefined") << QCborValue() << raw("\xf7") << noxfrm; QTest::newRow("Null") << QCborValue(nullptr) << raw("\xf6") << noxfrm; QTest::newRow("True") << QCborValue(true) << raw("\xf5") << noxfrm; @@ -1749,15 +2247,16 @@ static void addCommonCborData() QTest::newRow("DateTime") << QCborValue(dt) // this is UTC << "\xc0\x78\x18" + dt.toString(Qt::ISODateWithMs).toLatin1() << noxfrm; - QTest::newRow("DateTime-UTC") << QCborValue(QDateTime({2018, 1, 1}, {9, 0, 0}, Qt::UTC)) + QTest::newRow("DateTime-UTC") << QCborValue(QDateTime({2018, 1, 1}, {9, 0}, QTimeZone::UTC)) << raw("\xc0\x78\x18" "2018-01-01T09:00:00.000Z") << noxfrm; - QTest::newRow("DateTime-Local") << QCborValue(QDateTime({2018, 1, 1}, {9, 0, 0}, Qt::LocalTime)) + QTest::newRow("DateTime-Local") << QCborValue(QDateTime({2018, 1, 1}, {9, 0})) << raw("\xc0\x77" "2018-01-01T09:00:00.000") << noxfrm; - QTest::newRow("DateTime+01:00") << QCborValue(QDateTime({2018, 1, 1}, {9, 0, 0}, Qt::OffsetFromUTC, 3600)) - << raw("\xc0\x78\x1d" "2018-01-01T09:00:00.000+01:00") - << noxfrm; + QTest::newRow("DateTime+01:00") + << QCborValue(QDateTime({2018, 1, 1}, {9, 0}, QTimeZone::fromSecondsAheadOfUtc(3600))) + << raw("\xc0\x78\x1d" "2018-01-01T09:00:00.000+01:00") + << noxfrm; QTest::newRow("Url:Empty") << QCborValue(QUrl()) << raw("\xd8\x20\x60") << noxfrm; QTest::newRow("Url") << QCborValue(QUrl("HTTPS://example.com/{%30%31}?q=%3Ca+b%20%C2%A9%3E&%26")) << raw("\xd8\x20\x78\x27" "https://example.com/{01}?q=<a+b \xC2\xA9>&%26") @@ -1865,21 +2364,29 @@ void tst_QCborValue::fromCbor_data() QTest::newRow("String:Chunked:Empty") << QCborValue(QString()) << raw("\x7f\xff"); - QTest::newRow("DateTime:NoMilli") << QCborValue(QDateTime::fromSecsSinceEpoch(1515565477, Qt::UTC)) - << raw("\xc0\x74" "2018-01-10T06:24:37Z"); + QTest::newRow("DateTime:NoMilli") + << QCborValue(QDateTime::fromSecsSinceEpoch(1515565477, QTimeZone::UTC)) + << raw("\xc0\x74" "2018-01-10T06:24:37Z"); // date-only is only permitted local time - QTest::newRow("DateTime:NoTime:Local") << QCborValue(QDateTime(QDate(2020, 4, 15), QTime(0, 0), Qt::LocalTime)) - << raw("\xc0\x6a" "2020-04-15"); - QTest::newRow("DateTime:24:00:00") << QCborValue(QDateTime(QDate(2020, 4, 16), QTime(0, 0), Qt::UTC)) - << raw("\xc0\x74" "2020-04-15T24:00:00Z"); - QTest::newRow("DateTime:+00:00") << QCborValue(QDateTime::fromMSecsSinceEpoch(1515565477125, Qt::UTC)) - << raw("\xc0\x78\x1d" "2018-01-10T06:24:37.125+00:00"); - QTest::newRow("DateTime:+01:00") << QCborValue(QDateTime::fromMSecsSinceEpoch(1515565477125, Qt::OffsetFromUTC, 60*60)) - << raw("\xc0\x78\x1d" "2018-01-10T07:24:37.125+01:00"); - QTest::newRow("UnixTime_t:Integer") << QCborValue(QDateTime::fromSecsSinceEpoch(1515565477, Qt::UTC)) - << raw("\xc1\x1a\x5a\x55\xb1\xa5"); - QTest::newRow("UnixTime_t:Double") << QCborValue(QDateTime::fromMSecsSinceEpoch(1515565477125, Qt::UTC)) - << raw("\xc1\xfb\x41\xd6\x95\x6c""\x69\x48\x00\x00"); + QTest::newRow("DateTime:NoTime:Local") + << QCborValue(QDateTime(QDate(2020, 4, 15), QTime(0, 0))) + << raw("\xc0\x6a" "2020-04-15"); + QTest::newRow("DateTime:24:00:00") + << QCborValue(QDateTime(QDate(2020, 4, 16), QTime(0, 0), QTimeZone::UTC)) + << raw("\xc0\x74" "2020-04-15T24:00:00Z"); + QTest::newRow("DateTime:+00:00") + << QCborValue(QDateTime::fromMSecsSinceEpoch(1515565477125, QTimeZone::UTC)) + << raw("\xc0\x78\x1d" "2018-01-10T06:24:37.125+00:00"); + QTest::newRow("DateTime:+01:00") + << QCborValue(QDateTime::fromMSecsSinceEpoch(1515565477125, + QTimeZone::fromSecondsAheadOfUtc(60 * 60))) + << raw("\xc0\x78\x1d" "2018-01-10T07:24:37.125+01:00"); + QTest::newRow("UnixTime_t:Integer") + << QCborValue(QDateTime::fromSecsSinceEpoch(1515565477, QTimeZone::UTC)) + << raw("\xc1\x1a\x5a\x55\xb1\xa5"); + QTest::newRow("UnixTime_t:Double") + << QCborValue(QDateTime::fromMSecsSinceEpoch(1515565477125, QTimeZone::UTC)) + << raw("\xc1\xfb\x41\xd6\x95\x6c""\x69\x48\x00\x00"); QTest::newRow("Url:NotNormalized") << QCborValue(QUrl("https://example.com/\xc2\xa9 ")) << raw("\xd8\x20\x78\x1dHTTPS://EXAMPLE.COM/%c2%a9%20"); @@ -1999,7 +2506,7 @@ void tst_QCborValue::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 = MaxByteArraySize + 1 - sizeof(QByteArray::size_type); + const qsizetype MinInvalid = QByteArray::max_size() + 1 - sizeof(QByteArray::size_type); addValidationColumns(); addValidationData(MinInvalid); addValidationLargeData(MinInvalid, MaxInvalid); @@ -2086,7 +2593,7 @@ void tst_QCborValue::extendedTypeValidation_data() // representation, which means it can't represent dates before year 1 or // after year 9999. { - QDateTime dt(QDate(-1, 1, 1), QTime(0, 0), Qt::UTC); + QDateTime dt(QDate(-1, 1, 1), QTime(0, 0), QTimeZone::UTC); QTest::newRow("UnixTime_t:negative-year") << encode(0xc1, 0x3b, quint64(-dt.toSecsSinceEpoch()) - 1) << QCborValue(QCborKnownTags::UnixTime_t, dt.toSecsSinceEpoch()); @@ -2158,7 +2665,7 @@ void tst_QCborValue::extendedTypeValidation() QCborValue decoded = QCborValue::fromCbor(data, &error); QVERIFY2(error.error == QCborError(), qPrintable(error.errorString())); QCOMPARE(error.offset, data.size()); - QCOMPARE(decoded, expected); + QT_TEST_EQUALITY_OPS(decoded, expected, true); QByteArray encoded = decoded.toCbor(); #if QT_VERSION < QT_VERSION_CHECK(6,0,0) @@ -2172,11 +2679,16 @@ void tst_QCborValue::hugeDeviceValidation_data() { // because QCborValue will attempt to retain the original string in UTF-8, // the size which it can't store is actually the byte array size - addValidationHugeDevice(MaxByteArraySize + 1, MaxByteArraySize + 1); + addValidationHugeDevice(QByteArray::max_size() + 1, QByteArray::max_size() + 1); } void tst_QCborValue::hugeDeviceValidation() { +#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); QCborError error = { QCborError::Code(expectedError) }; @@ -2407,6 +2919,239 @@ void tst_QCborValue::toDiagnosticNotation() QCOMPARE(result, expected); } +void tst_QCborValue::cborValueRef_data() +{ + basics_data(); + + // Add tagged data and non-empty containers (non-basic) + QTest::newRow("Array:nonempty") << QCborValue::Array << QCborValue(QCborArray{0}); + QTest::newRow("Map:nonempty") << QCborValue::Map << QCborValue(QCborMap{ { 0, 1 } }); + QTest::newRow("Tagged") << QCborValue::Tag << QCborValue(QCborKnownTags::Base64, QByteArray()); +} + +template <typename ValueRef> static void cborValueRef_template() +{ + const QCborArray otherArray = {2}; + const QCborMap otherMap = { { 2, 21 } }; + const QDateTime otherDateTime = QDateTime::fromSecsSinceEpoch(1636654201); + const QUrl otherUrl("http://example.org"); + const QRegularExpression otherRE("[.]*"); + const QUuid otherUuid = QUuid::createUuid(); + + QFETCH(QCborValue, v); + QCborArray a = { v }; + const ValueRef ref = a[0]; + + QT_TEST_EQUALITY_OPS(ref, v, true); + QVERIFY(ref.compare(v) == 0); + QVERIFY(v.compare(ref) == 0); + + // compare properties of the QCborValueRef against the QCborValue it represents + QCOMPARE(ref.type(), v.type()); + QCOMPARE(ref.isInteger(), v.isInteger()); + QCOMPARE(ref.isByteArray(), v.isByteArray()); + QCOMPARE(ref.isString(), v.isString()); + QCOMPARE(ref.isArray(), v.isArray()); + QCOMPARE(ref.isMap(), v.isMap()); + QCOMPARE(ref.isFalse(), v.isFalse()); + QCOMPARE(ref.isTrue(), v.isTrue()); + QCOMPARE(ref.isBool(), v.isBool()); + QCOMPARE(ref.isNull(), v.isNull()); + QCOMPARE(ref.isUndefined(), v.isUndefined()); + QCOMPARE(ref.isDouble(), v.isDouble()); + QCOMPARE(ref.isDateTime(), v.isDateTime()); + QCOMPARE(ref.isUrl(), v.isUrl()); + QCOMPARE(ref.isRegularExpression(), v.isRegularExpression()); + QCOMPARE(ref.isUuid(), v.isUuid()); + QCOMPARE(ref.isInvalid(), v.isInvalid()); + QCOMPARE(ref.isContainer(), v.isContainer()); + QCOMPARE(ref.isSimpleType(), v.isSimpleType()); + QCOMPARE(ref.isSimpleType(QCborSimpleType::False), v.isSimpleType(QCborSimpleType::False)); + QCOMPARE(ref.isSimpleType(QCborSimpleType::True), v.isSimpleType(QCborSimpleType::True)); + QCOMPARE(ref.isSimpleType(QCborSimpleType::Null), v.isSimpleType(QCborSimpleType::Null)); + QCOMPARE(ref.isSimpleType(QCborSimpleType::Undefined), v.isSimpleType(QCborSimpleType::Undefined)); + QCOMPARE(ref.isSimpleType(QCborSimpleType(255)), v.isSimpleType(QCborSimpleType(255))); + + QCOMPARE(ref.tag(), v.tag()); + QCOMPARE(ref.taggedValue(), v.taggedValue()); + + QCOMPARE(ref.toBool(false), v.toBool(false)); + QCOMPARE(ref.toBool(true), v.toBool(true)); + QCOMPARE(ref.toInteger(47), v.toInteger(47)); + QCOMPARE(ref.toDouble(47), v.toDouble(47)); + QCOMPARE(ref.toByteArray("other"), v.toByteArray("other")); + QCOMPARE(ref.toString("other"), v.toString("other")); + QCOMPARE(ref.toArray(otherArray), v.toArray(otherArray)); + QCOMPARE(ref.toMap(otherMap), v.toMap(otherMap)); + QCOMPARE(ref.toDateTime(otherDateTime), v.toDateTime(otherDateTime)); + QCOMPARE(ref.toRegularExpression(otherRE), v.toRegularExpression(otherRE)); + QCOMPARE(ref.toUrl(otherUrl), v.toUrl(otherUrl)); + QCOMPARE(ref.toUuid(otherUuid), v.toUuid(otherUuid)); + QCOMPARE(ref.toSimpleType(QCborSimpleType(254)), v.toSimpleType(QCborSimpleType(254))); + + QCOMPARE(ref.toArray().isEmpty(), v.toArray().isEmpty()); + QCOMPARE(ref.toMap().isEmpty(), v.toMap().isEmpty()); + QCOMPARE(ref[0], std::as_const(v)[0]); + QCOMPARE(ref[QLatin1String("other")], std::as_const(v)[QLatin1String("other")]); + QCOMPARE(ref[QString("other")], std::as_const(v)[QString("other")]); + + if (qIsNaN(v.toDouble())) + QCOMPARE(qIsNaN(ref.toVariant().toDouble()), qIsNaN(v.toVariant().toDouble())); + else + QCOMPARE(ref.toVariant(), v.toVariant()); + QCOMPARE(ref.toJsonValue(), v.toJsonValue()); + QCOMPARE(ref.toCbor(), v.toCbor()); + QCOMPARE(ref.toDiagnosticNotation(), v.toDiagnosticNotation()); +} + +void tst_QCborValue::cborValueRef() +{ + cborValueRef_template<QCborValueRef>(); +} + +void tst_QCborValue::cborValueConstRef() +{ + cborValueRef_template<QCborValueConstRef>(); +} + +void tst_QCborValue::cborValueRefMutatingArray() +{ + // complements arrayMutation() + QFETCH(QCborValue, v); + + { + QCborArray origArray = { 123 }; + QCborArray a = { QCborValue(origArray) }; + QCborValueRef ref = a[0]; + QVERIFY(ref.isArray()); + QVERIFY(!ref.toArray().isEmpty()); + + // this will force the array to grow + ref[1] = v; + + QCborValue va = a.at(0); + QVERIFY(va.isArray()); + QCOMPARE(va.toArray().size(), 2); + QCOMPARE(va.toArray().first(), 123); + QT_TEST_EQUALITY_OPS(va.toArray().last(), v, true); + + // ensure the array didn't get modified + QT_TEST_EQUALITY_OPS(origArray, QCborArray{123}, true); + } + { + QCborArray emptyArray; + QCborArray a = { QCborValue(emptyArray) }; + QCborValueRef ref = a[0]; + QVERIFY(ref.isArray()); + QVERIFY(ref.toArray().isEmpty()); + + // this will force the array to become non-empty + ref[1] = v; + + QCborValue va = a.at(0); + QVERIFY(va.isArray()); + QCOMPARE(va.toArray().size(), 2); + QT_TEST_EQUALITY_OPS(va.toArray().first(), QCborValue(), true); + QT_TEST_EQUALITY_OPS(va.toArray().last(), v, true); + + // ensure the array didn't get modified + QT_TEST_EQUALITY_OPS(emptyArray, QCborArray(), true); + } + { + QCborArray emptyArray = { 123, 456 }; + emptyArray.takeFirst(); + emptyArray.takeFirst(); + QCborArray a = { QCborValue(emptyArray) }; + QCborValueRef ref = a[0]; + QVERIFY(ref.isArray()); + QVERIFY(ref.toArray().isEmpty()); + + // this will force the array to become non-empty + ref[1] = v; + + QCborValue va = a.at(0); + QVERIFY(va.isArray()); + QCOMPARE(va.toArray().size(), 2); + QT_TEST_EQUALITY_OPS(va.toArray().first(), QCborValue(), true); + QT_TEST_EQUALITY_OPS(va.toArray().last(), v, true); + + // ensure the array didn't get modified + QT_TEST_EQUALITY_OPS(emptyArray, QCborArray(), true); + } +} + +void tst_QCborValue::cborValueRefMutatingMapIntKey() +{ + // complements mapMutation() + QFETCH(QCborValue, v); + QCborValue::Type type = v.type(); + + auto executeTest = [=](qint64 index) { + QCborArray a = { v }; + QCborValueRef ref = a[0]; + + if (type == QCborValue::Array && !v.toArray().isEmpty()) + QTest::ignoreMessage(QtWarningMsg, "Using CBOR array as map forced conversion"); + ref[index] = v; + + QCborValue vm = a.at(0); + QVERIFY(vm.isMap()); + QCOMPARE(vm.toMap()[index].type(), type); + QCOMPARE(vm.toMap()[index], v); + + if (type == QCborValue::Array && !v.toArray().isEmpty()) + QCOMPARE(vm.toMap()[0], v.toArray()[0]); + else if (type == QCborValue::Map && !v.toMap().isEmpty()) + QCOMPARE(vm.toMap()[0], v.toMap()[0]); + }; + // accessing a negative index causes it to become a map + executeTest(-1); + if (QTest::currentTestFailed()) + return; + + // if the index is bigger than 0x10000, the array becomes a map + executeTest(0x10000); + if (QTest::currentTestFailed()) + return; + + if (type != QCborValue::Array) + executeTest(5); +} + +template <typename String> static void cborValueRefMutatingMapStringKey_template(const String &key) +{ + // complements mapMutation() too + QFETCH(QCborValue, v); + QCborValue::Type type = v.type(); + QCborArray a = { v }; + QCborValueRef ref = a[0]; + + if (type == QCborValue::Array && !v.toArray().isEmpty()) + QTest::ignoreMessage(QtWarningMsg, "Using CBOR array as map forced conversion"); + + // force conversion to map + ref[key] = v; + + QCborValue vm = a.at(0); + QVERIFY(vm.isMap()); + QCOMPARE(vm.toMap()[key].type(), type); + QCOMPARE(vm.toMap()[key], v); + + if (type == QCborValue::Array && !v.toArray().isEmpty()) + QCOMPARE(vm.toMap()[0], v.toArray()[0]); + else if (type == QCborValue::Map && !v.toMap().isEmpty()) + QCOMPARE(vm.toMap()[0], v.toMap()[0]); +} + +void tst_QCborValue::cborValueRefMutatingMapLatin1StringKey() +{ + cborValueRefMutatingMapStringKey_template(QLatin1String("other")); +} + +void tst_QCborValue::cborValueRefMutatingMapStringKey() +{ + cborValueRefMutatingMapStringKey_template(QString("other")); +} void tst_QCborValue::datastreamSerialization_data() { @@ -2459,6 +3204,7 @@ void tst_QCborValue::streamVariantSerialization() load >> output; QCOMPARE(output.userType(), QMetaType::QCborArray); QCOMPARE(qvariant_cast<QCborArray>(output), array); + QT_TEST_EQUALITY_OPS(qvariant_cast<QCborArray>(output), array, true); } { QCborMap obj{{"foo", 42}}; @@ -2489,7 +3235,7 @@ void tst_QCborValue::debugOutput_data() QTest::addColumn<QCborValue>("v"); QTest::addColumn<QString>("expected"); - QDateTime dt(QDate(2020, 4, 18), QTime(13, 41, 22, 123), Qt::UTC); + QDateTime dt(QDate(2020, 4, 18), QTime(13, 41, 22, 123), QTimeZone::UTC); QBitArray bits = QBitArray::fromBits("\x79\x03", 11); QTest::newRow("Undefined") << QCborValue() << "QCborValue()"; diff --git a/tests/auto/corelib/serialization/qcborvalue_json/CMakeLists.txt b/tests/auto/corelib/serialization/qcborvalue_json/CMakeLists.txt index 213732e04d..14ac0514f9 100644 --- a/tests/auto/corelib/serialization/qcborvalue_json/CMakeLists.txt +++ b/tests/auto/corelib/serialization/qcborvalue_json/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qcborvalue_json.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qcborvalue_json Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcborvalue_json LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qcborvalue_json SOURCES tst_qcborvalue_json.cpp diff --git a/tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp b/tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp index 723dfef90b..941bfa4008 100644 --- a/tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp +++ b/tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp @@ -1,47 +1,14 @@ -/**************************************************************************** -** -** 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) 2018 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/qcborvalue.h> #include <QTest> #include <QJsonValue> #include <QJsonObject> #include <QJsonArray> +#include <QVariant> +#include <QVariantMap> +#include <QVariantList> Q_DECLARE_METATYPE(QCborValue) diff --git a/tests/auto/corelib/serialization/qdatastream/CMakeLists.txt b/tests/auto/corelib/serialization/qdatastream/CMakeLists.txt index b29088c712..ebbb232362 100644 --- a/tests/auto/corelib/serialization/qdatastream/CMakeLists.txt +++ b/tests/auto/corelib/serialization/qdatastream/CMakeLists.txt @@ -1,9 +1,16 @@ -# Generated from qdatastream.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qdatastream Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdatastream LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data list(APPEND test_data "datastream.q42") list(APPEND test_data "typedef.q5") @@ -11,25 +18,7 @@ list(APPEND test_data "typedef.q5") qt_internal_add_test(tst_qdatastream SOURCES tst_qdatastream.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui TESTDATA ${test_data} ) - -## Scopes: -##################################################################### - -if(ANDROID OR INTEGRITY) - # Resources: - set(testdata_resource_files - "datastream.q42" - "typedef.q5" - ) - - qt_internal_add_resource(tst_qdatastream "testdata" - PREFIX - "/" - FILES - ${testdata_resource_files} - ) -endif() diff --git a/tests/auto/corelib/serialization/qdatastream/gen_typedefq5.cpp b/tests/auto/corelib/serialization/qdatastream/gen_typedefq5.cpp index 3a10e40576..6cc2755d8a 100644 --- a/tests/auto/corelib/serialization/qdatastream/gen_typedefq5.cpp +++ b/tests/auto/corelib/serialization/qdatastream/gen_typedefq5.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QDataStream> diff --git a/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp b/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp index 511d8f957f..77ca884897 100644 --- a/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp +++ b/tests/auto/corelib/serialization/qdatastream/tst_qdatastream.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QBuffer> @@ -44,6 +19,8 @@ #include <QtGui/QPixmap> #include <QtGui/QTextLength> +using namespace Qt::StringLiterals; + static_assert(QTypeTraits::has_ostream_operator_v<QDataStream, int>); static_assert(QTypeTraits::has_ostream_operator_v<QDataStream, QList<int>>); static_assert(QTypeTraits::has_ostream_operator_v<QDataStream, QMap<int, QString>>); @@ -157,6 +134,7 @@ private slots: void stream_atEnd(); void stream_writeError(); + void stream_writeSizeLimitExceeded(); void stream_QByteArray2(); @@ -318,6 +296,8 @@ static int NColorRoles[] = { QPalette::PlaceholderText + 1, // Qt_5_12 QPalette::PlaceholderText + 1, // Qt_5_13, Qt_5_14, Qt_5_15 QPalette::PlaceholderText + 1, // Qt_6_0 + QPalette::Accent + 1, // Qt_6_6 + QPalette::Accent + 1, // Qt_6_7 0 // add the correct value for Qt_5_14 here later }; @@ -983,10 +963,10 @@ static void QBitArrayData(QBitArray *b, int index) case 18: filler = "1111111111111111111111111111111111111111111111111111111111111111"; break; } - b->resize(filler.length()); + b->resize(filler.size()); b->fill(0); // reset all bits to zero - for (int i = 0; i < filler.length(); ++i) { + for (int i = 0; i < filler.size(); ++i) { if (filler.at(i) == '1') b->setBit(i, true); } @@ -1228,15 +1208,15 @@ void tst_QDataStream::readQCursor(QDataStream *s) QCOMPARE(d5.hotSpot(), test.hotSpot()); // Comparing non-null QBitmaps will fail. Upcast them first to pass. - QCOMPARE(d5.bitmap(Qt::ReturnByValue).isNull(), test.bitmap(Qt::ReturnByValue).isNull()); + QCOMPARE(d5.bitmap().isNull(), test.bitmap().isNull()); QCOMPARE( - static_cast<QPixmap>(d5.bitmap(Qt::ReturnByValue)), - static_cast<QPixmap>(test.bitmap(Qt::ReturnByValue)) + static_cast<QPixmap>(d5.bitmap()), + static_cast<QPixmap>(test.bitmap()) ); - QCOMPARE(d5.mask(Qt::ReturnByValue).isNull(), test.mask(Qt::ReturnByValue).isNull()); + QCOMPARE(d5.mask().isNull(), test.mask().isNull()); QCOMPARE( - static_cast<QPixmap>(d5.mask(Qt::ReturnByValue)), - static_cast<QPixmap>(test.mask(Qt::ReturnByValue)) + static_cast<QPixmap>(d5.mask()), + static_cast<QPixmap>(test.mask()) ); } #endif @@ -2211,6 +2191,19 @@ void tst_QDataStream::stream_writeError() TEST_WRITE_ERROR(.writeRawData("test", 4)) } +void tst_QDataStream::stream_writeSizeLimitExceeded() +{ + QByteArray ba; + QDataStream ds(&ba, QDataStream::ReadWrite); + // Set the version that supports only 32-bit data size + ds.setVersion(QDataStream::Qt_6_6); + QCOMPARE(ds.status(), QDataStream::Ok); + const qint64 size = qint64(std::numeric_limits<quint32>::max()) + 1; + ds.writeBytes("", size); + QCOMPARE(ds.status(), QDataStream::SizeLimitExceeded); + QVERIFY(ba.isEmpty()); +} + void tst_QDataStream::stream_QByteArray2() { QByteArray ba; @@ -2415,8 +2408,8 @@ void tst_QDataStream::setVersion() */ // revise the test if new color roles or color groups are added - QVERIFY(QPalette::NColorRoles == QPalette::PlaceholderText + 1); - QCOMPARE(int(QPalette::NColorGroups), 3); + QCOMPARE(QPalette::NColorRoles, QPalette::Accent + 1); + QCOMPARE(static_cast<int>(QPalette::NColorGroups), 3); QByteArray ba2; QPalette pal1, pal2; @@ -2817,7 +2810,6 @@ void tst_QDataStream::status_charptr_QByteArray_data() QTest::newRow("badsize 1MB+1") << QByteArray("\x00\x10\x00\x01", 4) + oneMbMinus1 + QByteArray("j") << (int) QDataStream::ReadPastEnd << QByteArray(); QTest::newRow("badsize 3MB") << QByteArray("\x00\x30\x00\x00", 4) + threeMbMinus1 << (int) QDataStream::ReadPastEnd << QByteArray(); QTest::newRow("badsize 3MB+1") << QByteArray("\x00\x30\x00\x01", 4) + threeMbMinus1 + QByteArray("j") << (int) QDataStream::ReadPastEnd << QByteArray(); - QTest::newRow("size -1") << QByteArray("\xff\xff\xff\xff", 4) << (int) QDataStream::ReadPastEnd << QByteArray(); QTest::newRow("size -2") << QByteArray("\xff\xff\xff\xfe", 4) << (int) QDataStream::ReadPastEnd << QByteArray(); } @@ -2840,17 +2832,35 @@ void tst_QDataStream::status_charptr_QByteArray() { QDataStream stream(&data, QIODevice::ReadOnly); char *buf; - uint len; + qint64 len; stream.readBytes(buf, len); - QCOMPARE((int)len, expectedString.size()); + QCOMPARE(len, qint64(expectedString.size())); QCOMPARE(QByteArray(buf, len), expectedString); QCOMPARE(int(stream.status()), expectedStatus); delete [] buf; } +#if QT_DEPRECATED_SINCE(6, 11) +QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED { + // check that old overload still works as expected QDataStream stream(&data, QIODevice::ReadOnly); - QByteArray buf; + char *buf; + auto cleanup = qScopeGuard([&buf] { + delete [] buf; + }); + uint len; + stream.readBytes(buf, len); + + QCOMPARE(len, expectedString.size()); + QCOMPARE(QByteArray(buf, len), expectedString); + QCOMPARE(int(stream.status()), expectedStatus); + } +QT_WARNING_POP +#endif // QT_DEPRECATED_SINCE(6, 11) + { + QDataStream stream(&data, QIODevice::ReadOnly); + QByteArray buf = "Content to be overwritten"; stream >> buf; if (data.startsWith("\xff\xff\xff\xff")) { @@ -2919,12 +2929,20 @@ void tst_QDataStream::status_QString_data() QTest::newRow("badsize 1MB+1") << QByteArray("\x00\x20\x00\x02", 4) + oneMbMinus1Data + QByteArray("j") << (int) QDataStream::ReadPastEnd << QString(); QTest::newRow("badsize 3MB") << QByteArray("\x00\x60\x00\x00", 4) + threeMbMinus1Data << (int) QDataStream::ReadPastEnd << QString(); QTest::newRow("badsize 3MB+1") << QByteArray("\x00\x60\x00\x02", 4) + threeMbMinus1Data + QByteArray("j") << (int) QDataStream::ReadPastEnd << QString(); - QTest::newRow("size -2") << QByteArray("\xff\xff\xff\xfe", 4) << (int) QDataStream::ReadPastEnd << QString(); - QTest::newRow("size MAX") << QByteArray("\x7f\xff\xff\xfe", 4) << (int) QDataStream::ReadPastEnd << QString(); + QTest::newRow("32 bit size should be 64 bit") << QByteArray("\xff\xff\xff\xfe", 4) << (int) QDataStream::ReadPastEnd << QString(); - // corrupt data +#if QT_POINTER_SIZE != 4 + // past end on 64 bit platforms + QTest::newRow("32 bit size MAX string no content") << QByteArray("\xff\xff\xff\xfc", 4) << (int) QDataStream::ReadPastEnd << QString(); +#else + // too big for 32 bit platforms + QTest::newRow("32 bit size MAX string no content") << QByteArray("\xff\xff\xff\xfc", 4) << (int) QDataStream::SizeLimitExceeded << QString(); +#endif + // too big on both 32 and 64 bit platforms because qsizetype is signed + QTest::newRow("64 bit size MAX string no content") << QByteArray("\xff\xff\xff\xfe\xff\xff\xff\xff\xff\xff\xff\xfe", 12) << (int) QDataStream::SizeLimitExceeded << QString(); + + // corrupt data because QChar is 16 bit => even size required QTest::newRow("corrupt1") << QByteArray("yyyy") << (int) QDataStream::ReadCorruptData << QString(); - QTest::newRow("size -3") << QByteArray("\xff\xff\xff\xfd", 4) << (int) QDataStream::ReadCorruptData << QString(); } void tst_QDataStream::status_QString() @@ -2934,7 +2952,7 @@ void tst_QDataStream::status_QString() QFETCH(QString, expectedString); QDataStream stream(&data, QIODevice::ReadOnly); - QString str; + QString str = "Content to be overwritten"; stream >> str; QCOMPARE(str.size(), expectedString.size()); @@ -2998,6 +3016,8 @@ void tst_QDataStream::status_QBitArray_data() QTest::newRow("badsize 16") << QDataStream::Qt_5_15 << QByteArray("\x00\x00\x00\x10\xff", 5) << (int) QDataStream::ReadPastEnd << QBitArray(); QTest::newRow("badsize 17") << QDataStream::Qt_5_15 << QByteArray("\x00\x00\x00\x11\xff\xff", 6) << (int) QDataStream::ReadPastEnd << QBitArray(); QTest::newRow("badsize 32") << QDataStream::Qt_5_15 << QByteArray("\x00\x00\x00\x20\xff\xff\xff", 7) << (int) QDataStream::ReadPastEnd << QBitArray(); + QTest::newRow("badsize INT_MAX") << QDataStream::Qt_5_15 << QByteArray("\x7f\xff\xff\xff\xff\xff\xff", 7) << int(QDataStream::ReadPastEnd) << QBitArray(); // size accepted + QTest::addRow("badsize INT_MAX + 1") << QDataStream::Qt_5_15 << QByteArray("\x80\x00\x00\x01" "\xff\xff\xff", 7) << int(QDataStream::ReadCorruptData) << QBitArray(); // size rejected QTest::newRow("new badsize 0") << QDataStream::Qt_6_0 << QByteArray("\x00\x00\x00\x00", 4) << (int) QDataStream::ReadPastEnd << QBitArray(); QTest::newRow("new badsize 9") << QDataStream::Qt_6_0 << QByteArray("\x00\x00\x00\x00\x00\x00\x00\x09\xff", 9) << (int) QDataStream::ReadPastEnd << QBitArray(); QTest::newRow("new badsize 0x10000") << QDataStream::Qt_6_0 << QByteArray("\x00\x00\x00\x01\x00\x00\x00\x00\x00", 9) << (int) QDataStream::ReadPastEnd << QBitArray(); @@ -3027,7 +3047,7 @@ void tst_QDataStream::status_QBitArray() QDataStream stream(&data, QIODevice::ReadOnly); stream.setVersion(version); - QBitArray str; + QBitArray str(255, true); stream >> str; if (sizeof(qsizetype) == sizeof(int)) @@ -3094,7 +3114,9 @@ void tst_QDataStream::status_QHash_QMap() hash2.insert("L", "MN"); // ok + hash = hash2; MAP_TEST(QByteArray("\x00\x00\x00\x00", 4), QDataStream::Ok, QDataStream::Ok, StringHash()); + hash = hash2; MAP_TEST(QByteArray("\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00", 12), QDataStream::Ok, QDataStream::Ok, hash1); MAP_TEST(QByteArray("\x00\x00\x00\x02\x00\x00\x00\x02\x00J\x00\x00\x00\x02\x00K" "\x00\x00\x00\x02\x00L\x00\x00\x00\x04\x00M\x00N", 30), QDataStream::Ok, QDataStream::Ok, hash2); @@ -3175,7 +3197,9 @@ void tst_QDataStream::status_QList_QVector() someList.append("J"); someList.append("MN"); + list = someList; LIST_TEST(QByteArray("\x00\x00\x00\x00", 4), QDataStream::Ok, QDataStream::Ok, List()); + list = someList; LIST_TEST(QByteArray("\x00\x00\x00\x01\x00\x00\x00\x00", 8), QDataStream::Ok, QDataStream::Ok, listWithEmptyString); LIST_TEST(QByteArray("\x00\x00\x00\x02\x00\x00\x00\x02\x00J" "\x00\x00\x00\x04\x00M\x00N", 18), QDataStream::Ok, QDataStream::Ok, someList); @@ -3243,6 +3267,13 @@ void tst_QDataStream::streamRealDataTypes() path.arcTo(4, 5, 6, 7, 8, 9); path.quadTo(1, 2, 3, 4); + QPainterPath otherPath; + otherPath.arcTo(10, 4, 5, 6, 7, 8); + otherPath.lineTo(9, 0); + otherPath.cubicTo(0, 0, 10, 10, 20, 20); + otherPath.quadTo(2, 4, 5, 6); + QCOMPARE(otherPath.elementCount(), 12); + QColor color(64, 64, 64); color.setAlphaF(0.5); QRadialGradient radialGradient(5, 6, 7, 8, 9); @@ -3274,17 +3305,17 @@ void tst_QDataStream::streamRealDataTypes() file.close(); } - QPointF point; - QRectF rect; - QPolygonF polygon; + QPointF point(1, 2); + QRectF rect(1, 2, 5, 6); + QPolygonF polygon {{3, 4}, {5, 6}}; QTransform transform; - QPainterPath p; + QPainterPath p = otherPath; QPicture pict; - QTextLength textLength; - QColor col; - QBrush rGrad; - QBrush cGrad; - QPen pen; + QTextLength textLength(QTextLength::FixedLength, 2.5); + QColor col(128, 128, 127); + QBrush rGrad(Qt::CrossPattern); + QBrush cGrad(Qt::CrossPattern); + QPen pen(conicalBrush, 10); QVERIFY(file.open(QIODevice::ReadOnly)); QDataStream stream(&file); @@ -3904,18 +3935,19 @@ void tst_QDataStream::typedefQt5Compat() // writing in Qt 6 results in the same file QTemporaryDir dir; QVERIFY(dir.isValid()); - QFile file(dir.filePath(u"typedef.q6"_qs)); - file.open(QIODevice::WriteOnly); + QFile file(dir.filePath(u"typedef.q6"_s)); + QVERIFY(file.open(QIODevice::WriteOnly)); QDataStream stream(&file); stream.setVersion(QDataStream::Qt_5_15); CustomPair p {42, 100}; stream << QVariant::fromValue(p); file.close(); - file.open(QIODevice::ReadOnly); + QVERIFY(file.open(QIODevice::ReadOnly)); QCOMPARE(file.readAll(), qt5Data); } } QTEST_MAIN(tst_QDataStream) + #include "tst_qdatastream.moc" diff --git a/tests/auto/corelib/serialization/qdatastream_core_pixmap/CMakeLists.txt b/tests/auto/corelib/serialization/qdatastream_core_pixmap/CMakeLists.txt index b77b8212fb..d0622c642c 100644 --- a/tests/auto/corelib/serialization/qdatastream_core_pixmap/CMakeLists.txt +++ b/tests/auto/corelib/serialization/qdatastream_core_pixmap/CMakeLists.txt @@ -1,12 +1,19 @@ -# Generated from qdatastream_core_pixmap.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qdatastream_core_pixmap Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdatastream_core_pixmap LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + qt_internal_add_test(tst_qdatastream_core_pixmap SOURCES tst_qdatastream_core_pixmap.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui ) diff --git a/tests/auto/corelib/serialization/qdatastream_core_pixmap/tst_qdatastream_core_pixmap.cpp b/tests/auto/corelib/serialization/qdatastream_core_pixmap/tst_qdatastream_core_pixmap.cpp index 10721cd253..f17da27f1c 100644 --- a/tests/auto/corelib/serialization/qdatastream_core_pixmap/tst_qdatastream_core_pixmap.cpp +++ b/tests/auto/corelib/serialization/qdatastream_core_pixmap/tst_qdatastream_core_pixmap.cpp @@ -1,36 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> #include <QtGui/QPixmap> #include <QtGui/QImage> -class tst_QDataStream : public QObject +class tst_QDataStreamPixmap : public QObject { Q_OBJECT @@ -38,7 +13,7 @@ private slots: void stream_with_pixmap(); }; -void tst_QDataStream::stream_with_pixmap() +void tst_QDataStreamPixmap::stream_with_pixmap() { // This is a QVariantMap with a 3x3 red QPixmap and two strings inside const QByteArray ba = QByteArray::fromBase64( @@ -62,6 +37,6 @@ void tst_QDataStream::stream_with_pixmap() QCOMPARE(map["z"].toString(), QString("there")); } -QTEST_GUILESS_MAIN(tst_QDataStream) +QTEST_GUILESS_MAIN(tst_QDataStreamPixmap) #include "tst_qdatastream_core_pixmap.moc" diff --git a/tests/auto/corelib/serialization/qtextstream/BLACKLIST b/tests/auto/corelib/serialization/qtextstream/BLACKLIST index aef7d7aa26..cb76e0454d 100644 --- a/tests/auto/corelib/serialization/qtextstream/BLACKLIST +++ b/tests/auto/corelib/serialization/qtextstream/BLACKLIST @@ -1,5 +1,3 @@ -[stillOpenWhenAtEnd] -windows-7sp1 # QTBUG-87410 [readStdin] android diff --git a/tests/auto/corelib/serialization/qtextstream/CMakeLists.txt b/tests/auto/corelib/serialization/qtextstream/CMakeLists.txt index 15798c963f..ac3dc91555 100644 --- a/tests/auto/corelib/serialization/qtextstream/CMakeLists.txt +++ b/tests/auto/corelib/serialization/qtextstream/CMakeLists.txt @@ -1,4 +1,11 @@ -# Generated from qtextstream.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtextstream LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() add_subdirectory(test) add_subdirectory(stdinProcess) diff --git a/tests/auto/corelib/serialization/qtextstream/readAllStdinProcess/CMakeLists.txt b/tests/auto/corelib/serialization/qtextstream/readAllStdinProcess/CMakeLists.txt index 785785d43a..bcfb0aaf4e 100644 --- a/tests/auto/corelib/serialization/qtextstream/readAllStdinProcess/CMakeLists.txt +++ b/tests/auto/corelib/serialization/qtextstream/readAllStdinProcess/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from readAllStdinProcess.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## readAllStdinProcess Binary: diff --git a/tests/auto/corelib/serialization/qtextstream/readAllStdinProcess/main.cpp b/tests/auto/corelib/serialization/qtextstream/readAllStdinProcess/main.cpp index 08d2bf8183..01f47d758f 100644 --- a/tests/auto/corelib/serialization/qtextstream/readAllStdinProcess/main.cpp +++ b/tests/auto/corelib/serialization/qtextstream/readAllStdinProcess/main.cpp @@ -1,33 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/QTextStream> +#include <QtCore/QString> #include <stdio.h> int main(int, char**) diff --git a/tests/auto/corelib/serialization/qtextstream/readLineStdinProcess/CMakeLists.txt b/tests/auto/corelib/serialization/qtextstream/readLineStdinProcess/CMakeLists.txt index 897444c9af..39af3a3048 100644 --- a/tests/auto/corelib/serialization/qtextstream/readLineStdinProcess/CMakeLists.txt +++ b/tests/auto/corelib/serialization/qtextstream/readLineStdinProcess/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from readLineStdinProcess.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## readLineStdinProcess Binary: diff --git a/tests/auto/corelib/serialization/qtextstream/readLineStdinProcess/main.cpp b/tests/auto/corelib/serialization/qtextstream/readLineStdinProcess/main.cpp index 2d4aa452ca..8f81f5a720 100644 --- a/tests/auto/corelib/serialization/qtextstream/readLineStdinProcess/main.cpp +++ b/tests/auto/corelib/serialization/qtextstream/readLineStdinProcess/main.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/QCoreApplication> diff --git a/tests/auto/corelib/serialization/qtextstream/stdinProcess/CMakeLists.txt b/tests/auto/corelib/serialization/qtextstream/stdinProcess/CMakeLists.txt index 73eccd1ed8..7e964bbfb2 100644 --- a/tests/auto/corelib/serialization/qtextstream/stdinProcess/CMakeLists.txt +++ b/tests/auto/corelib/serialization/qtextstream/stdinProcess/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from stdinProcess.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## stdinProcess Binary: diff --git a/tests/auto/corelib/serialization/qtextstream/stdinProcess/main.cpp b/tests/auto/corelib/serialization/qtextstream/stdinProcess/main.cpp index a3c1fc525b..b8a274ed0f 100644 --- a/tests/auto/corelib/serialization/qtextstream/stdinProcess/main.cpp +++ b/tests/auto/corelib/serialization/qtextstream/stdinProcess/main.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtCore/QTextStream> diff --git a/tests/auto/corelib/serialization/qtextstream/test/CMakeLists.txt b/tests/auto/corelib/serialization/qtextstream/test/CMakeLists.txt index cb48aed16c..588a49fcf0 100644 --- a/tests/auto/corelib/serialization/qtextstream/test/CMakeLists.txt +++ b/tests/auto/corelib/serialization/qtextstream/test/CMakeLists.txt @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + ##################################################################### ## tst_qtextstream Test: ##################################################################### @@ -15,12 +18,17 @@ qt_internal_add_test(tst_qtextstream OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../" SOURCES ../tst_qtextstream.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Network Qt::TestPrivate TESTDATA ${test_data} + QT_TEST_SERVER_LIST "cyrus" ) +if(QT_FEATURE_sanitize_address) + set_property(TEST tst_qtextstream APPEND PROPERTY ENVIRONMENT "QTEST_FUNCTION_TIMEOUT=900000") +endif() + # Resources: set(qtextstream_resource_files "../resources/big_endian/" @@ -36,28 +44,6 @@ qt_internal_add_resource(tst_qtextstream "qtextstream" ${qtextstream_resource_files} ) -if(INTEGRITY) -set(qtextstream_resource_files_integrity - "../rfc3261.txt" - "../task113817.txt" - "../tst_qtextstream.cpp" -) - -qt_internal_add_resource(tst_qtextstream "qtextstream_integrity" - PREFIX - "/" - FILES - ${qtextstream_resource_files_integrity} -) -endif() - -## Scopes: - -qt_internal_extend_target(tst_qtextstream CONDITION builtin_testdata OR INTEGRITY - DEFINES - BUILTIN_TESTDATA -) - if(QT_FEATURE_process) - add_dependencies(tst_qtextstream stdinProcess readAllStdinProcess readLineStdinProcess) + add_dependencies(tst_qtextstream stdinProcess readAllStdinProcess readLineStdinProcess) endif() diff --git a/tests/auto/corelib/serialization/qtextstream/tst_qtextstream.cpp b/tests/auto/corelib/serialization/qtextstream/tst_qtextstream.cpp index c02da8efe1..411084a36c 100644 --- a/tests/auto/corelib/serialization/qtextstream/tst_qtextstream.cpp +++ b/tests/auto/corelib/serialization/qtextstream/tst_qtextstream.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2020 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTest> @@ -37,6 +12,7 @@ #include <QDebug> #include <QElapsedTimer> #include <QFile> +#include <QTemporaryFile> #include <QStringConverter> #include <QTcpSocket> #include <QTemporaryDir> @@ -94,6 +70,8 @@ private slots: // char operators void QChar_operators_FromDevice_data(); void QChar_operators_FromDevice(); + void char16_t_operators_FromDevice_data(); + void char16_t_operators_FromDevice(); void char_operators_FromDevice_data(); void char_operators_FromDevice(); @@ -225,6 +203,9 @@ private slots: void textModeOnEmptyRead(); + void autodetectUnicode_data(); + void autodetectUnicode(); + private: void generateLineData(bool for_QString); void generateAllData(bool for_QString); @@ -569,7 +550,7 @@ void tst_QTextStream::readLineMaxlen() QFile::remove("testfile"); QFile file("testfile"); if (useDevice) { - file.open(QIODevice::ReadWrite); + QVERIFY(file.open(QIODevice::ReadWrite)); file.write(input.toUtf8()); file.seek(0); stream.setDevice(&file); @@ -954,7 +935,8 @@ void tst_QTextStream::lineCount_data() QTest::newRow("buffersize+1 line") << QByteArray(16384, '\n') << 16384; QTest::newRow("buffersize+2 line") << QByteArray(16385, '\n') << 16385; - QFile file(m_rfc3261FilePath); file.open(QFile::ReadOnly); + QFile file(m_rfc3261FilePath); + QVERIFY(file.open(QFile::ReadOnly)); QTest::newRow("rfc3261") << file.readAll() << 15067; } @@ -965,7 +947,7 @@ void tst_QTextStream::lineCount() QFETCH(int, lineCount); QFile out("out.txt"); - out.open(QFile::WriteOnly); + QVERIFY(out.open(QFile::WriteOnly)); QTextStream lineReader(data); int lines = 0; @@ -1211,11 +1193,16 @@ void tst_QTextStream::stillOpenWhenAtEnd() while (!stream.readLine().isNull()) {} QVERIFY(file.isOpen()); +#ifdef QT_TEST_SERVER + if (!QtNetworkSettings::verifyConnection(QtNetworkSettings::imapServerName(), 143)) + QSKIP("No network test server available"); +#else if (!QtNetworkSettings::verifyTestNetworkSettings()) QSKIP("No network test server available"); +#endif QTcpSocket socket; - socket.connectToHost(QtNetworkSettings::serverName(), 143); + socket.connectToHost(QtNetworkSettings::imapServerName(), 143); QVERIFY(socket.waitForReadyRead(5000)); QTextStream stream2(&socket); @@ -1422,18 +1409,18 @@ void tst_QTextStream::pos3LargeFile() { QFile file(testFileName); - file.open(QIODevice::WriteOnly | QIODevice::Text); + QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text)); QTextStream out( &file ); // NOTE: The unusual spacing is to ensure non-1-character whitespace. QString lineString = " 0 1 2\t3 4\t \t5 6 7 8 9 \n"; // Approximate 50kb text file - const int NbLines = (50*1024) / lineString.length() + 1; + const int NbLines = (50*1024) / lineString.size() + 1; for (int line = 0; line < NbLines; ++line) out << lineString; // File is automatically flushed and closed on destruction. } QFile file(testFileName); - file.open(QIODevice::ReadOnly | QIODevice::Text); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); QTextStream in( &file ); const int testValues[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; int value; @@ -1530,7 +1517,7 @@ void tst_QTextStream::read() { QFile::remove("testfile"); QFile file("testfile"); - file.open(QFile::WriteOnly); + QVERIFY(file.open(QFile::WriteOnly)); file.write("4.15 abc ole"); file.close(); @@ -1552,7 +1539,7 @@ void tst_QTextStream::read() // File larger than QTEXTSTREAM_BUFFERSIZE QFile::remove("testfile"); QFile file("testfile"); - file.open(QFile::WriteOnly); + QVERIFY(file.open(QFile::WriteOnly)); for (int i = 0; i < 16384 / 8; ++i) file.write("01234567"); file.write("0"); @@ -1608,8 +1595,8 @@ void tst_QTextStream::forceSign() // ------------------------------------------------------------------------------ void tst_QTextStream::read0d0d0a() { - QFile file("task113817.txt"); - file.open(QIODevice::ReadOnly | QIODevice::Text); + QFile file(QFINDTESTDATA("task113817.txt")); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); QTextStream stream(&file); while (!stream.atEnd()) @@ -1773,7 +1760,7 @@ void tst_QTextStream::utf8IncompleteAtBufferBoundary() "\342\200\223" "\342\200\223"); - data.open(QFile::WriteOnly | QFile::Truncate); + QVERIFY(data.open(QFile::WriteOnly | QFile::Truncate)); { QTextStream out(&data); out.setEncoding(QStringConverter::Utf8); @@ -1785,7 +1772,7 @@ void tst_QTextStream::utf8IncompleteAtBufferBoundary() } data.close(); - data.open(QFile::ReadOnly); + QVERIFY(data.open(QFile::ReadOnly)); QTextStream in(&data); QFETCH(bool, useLocale); @@ -1914,6 +1901,40 @@ void tst_QTextStream::QChar_operators_FromDevice() } // ------------------------------------------------------------------------------ +void tst_QTextStream::char16_t_operators_FromDevice_data() +{ + generateOperatorCharData(false); +} + +// ------------------------------------------------------------------------------ +void tst_QTextStream::char16_t_operators_FromDevice() +{ + QFETCH(QByteArray, input); + QFETCH(const QChar, qchar_output); + QFETCH(const QByteArray, write_output); + const char16_t char16_t_output = qchar_output.unicode(); + + QBuffer buf(&input); + buf.open(QBuffer::ReadOnly); + QTextStream stream(&buf); + stream.setEncoding(QStringConverter::Latin1); + char16_t tmp; + stream >> tmp; + QCOMPARE(tmp, qchar_output); + + QBuffer writeBuf; + writeBuf.open(QBuffer::WriteOnly); + + QTextStream writeStream(&writeBuf); + writeStream.setEncoding(QStringConverter::Latin1); + writeStream << char16_t_output; + writeStream.flush(); + + QCOMPARE(writeBuf.buffer().size(), write_output.size()); + QCOMPARE(writeBuf.buffer().constData(), write_output.constData()); +} + +// ------------------------------------------------------------------------------ void tst_QTextStream::char_operators_FromDevice_data() { generateOperatorCharData(false); @@ -2633,13 +2654,27 @@ void tst_QTextStream::manipulators_data() QTest::addColumn<QString>("textData"); QTest::addColumn<QByteArray>("result"); - QTest::newRow("no flags") << 10 << 0 << 0 << 0 << 5.0 << 5 << QString("five") << QByteArray("55five"); - QTest::newRow("rightadjust") << 10 << int(QTextStream::AlignRight) << 0 << 10 << 5.0 << 5 << QString("five") << QByteArray(" 5 5 five"); - QTest::newRow("leftadjust") << 10 << int(QTextStream::AlignLeft) << 0 << 10 << 5.0 << 5 << QString("five") << QByteArray("5 5 five "); - QTest::newRow("showpos") << 10 << int(QTextStream::AlignRight) << int(QTextStream::ForceSign) << 10 << 5.0 << 5 << QString("five") << QByteArray(" +5 +5 five"); - QTest::newRow("showpos2") << 10 << int(QTextStream::AlignRight) << int(QTextStream::ForceSign) << 5 << 3.14 << -5 << QString("five") << QByteArray("+3.14 -5 five"); - QTest::newRow("hex") << 16 << int(QTextStream::AlignRight) << int(QTextStream::ShowBase) << 5 << 3.14 << -5 << QString("five") << QByteArray(" 3.14 -0x5 five"); - QTest::newRow("hex") << 16 << int(QTextStream::AlignRight) << int(QTextStream::ShowBase | QTextStream::UppercaseBase) << 5 << 3.14 << -5 << QString("five") << QByteArray(" 3.14 -0X5 five"); + QTest::newRow("no flags") + << 10 << 0 << 0 << 0 << 5.0 << 5 << QString("five") << QByteArray("55five"); + QTest::newRow("rightadjust") + << 10 << int(QTextStream::AlignRight) << 0 << 10 << 5.0 << 5 << QString("five") + << QByteArray(" 5 5 five"); + QTest::newRow("leftadjust") + << 10 << int(QTextStream::AlignLeft) << 0 << 10 << 5.0 << 5 << QString("five") + << QByteArray("5 5 five "); + QTest::newRow("showpos-wide") + << 10 << int(QTextStream::AlignRight) << int(QTextStream::ForceSign) << 10 << 5.0 << 5 << + QString("five") << QByteArray(" +5 +5 five"); + QTest::newRow("showpos-pi") + << 10 << int(QTextStream::AlignRight) << int(QTextStream::ForceSign) << 5 << 3.14 << -5 << + QString("five") << QByteArray("+3.14 -5 five"); + QTest::newRow("hex-lower") + << 16 << int(QTextStream::AlignRight) << int(QTextStream::ShowBase) << 5 << 3.14 << -5 << + QString("five") << QByteArray(" 3.14 -0x5 five"); + QTest::newRow("hex-upper") + << 16 << int(QTextStream::AlignRight) + << int(QTextStream::ShowBase | QTextStream::UppercaseBase) + << 5 << 3.14 << -5 << QString("five") << QByteArray(" 3.14 -0X5 five"); } // ------------------------------------------------------------------------------ @@ -3027,6 +3062,57 @@ void tst_QTextStream::textModeOnEmptyRead() QVERIFY(file.isTextModeEnabled()); } +void tst_QTextStream::autodetectUnicode_data() +{ + QTest::addColumn<QStringConverter::Encoding>("encoding"); + QTest::newRow("Utf8") << QStringConverter::Utf8; + QTest::newRow("Utf16BE") << QStringConverter::Utf16BE; + QTest::newRow("Utf16LE") << QStringConverter::Utf16LE; + QTest::newRow("Utf32BE") << QStringConverter::Utf32BE; + QTest::newRow("Utf32LE") << QStringConverter::Utf32LE; +} + +void tst_QTextStream::autodetectUnicode() +{ + QFETCH(QStringConverter::Encoding, encoding); + + QTemporaryFile file; + QVERIFY(file.open()); + + QString original("HelloWorld👋"); + + { + QTextStream out(&file); + out.setGenerateByteOrderMark(true); + out.setEncoding(encoding); + out << original; + } + file.seek(0); + { + QTextStream in(&file); + QString actual; + in >> actual; + QCOMPARE(actual, original); + QCOMPARE(in.encoding(), encoding); + } + file.seek(0); + // Again, but change order of calls to QTextStream... + { + QTextStream out(&file); + out.setEncoding(encoding); + out.setGenerateByteOrderMark(true); + out << original; + } + file.seek(0); + { + QTextStream in(&file); + QString actual; + in >> actual; + QCOMPARE(actual, original); + QCOMPARE(in.encoding(), encoding); + } +} + // ------------------------------------------------------------------------------ diff --git a/tests/auto/corelib/serialization/qxmlstream/CMakeLists.txt b/tests/auto/corelib/serialization/qxmlstream/CMakeLists.txt index ab20f4d49e..30c86491ff 100644 --- a/tests/auto/corelib/serialization/qxmlstream/CMakeLists.txt +++ b/tests/auto/corelib/serialization/qxmlstream/CMakeLists.txt @@ -1,19 +1,33 @@ -# Generated from qxmlstream.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qxmlstream Test: ##################################################################### +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qxmlstream LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + # Collect test data -list(APPEND test_data "data") -list(APPEND test_data "XML-Test-Suite") +file(GLOB_RECURSE test_data + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + data/* XML-Test-Suite/*) + +file(GLOB_RECURSE tokenError + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + tokenError/*) qt_internal_add_test(tst_qxmlstream SOURCES tst_qxmlstream.cpp LIBRARIES Qt::Network - Qt::Xml - Qt::GuiPrivate - TESTDATA ${test_data} + Qt::CorePrivate + Qt::TestPrivate + TESTDATA + ${test_data} + ${tokenError} ) diff --git a/tests/auto/corelib/serialization/qxmlstream/qc14n.h b/tests/auto/corelib/serialization/qxmlstream/qc14n.h index 7fb27382aa..5ae87f1a7a 100644 --- a/tests/auto/corelib/serialization/qxmlstream/qc14n.h +++ b/tests/auto/corelib/serialization/qxmlstream/qc14n.h @@ -1,35 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -QT_FORWARD_DECLARE_CLASS(QIODevice) -QT_FORWARD_DECLARE_CLASS(QString) +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#include <QtCore/QDebug> #include <QtCore/QFlags> +#include <QtCore/QXmlStreamReader> + +#include <algorithm> class QC14N { @@ -118,18 +94,11 @@ bool QC14N::isAttributesEqual(const QXmlStreamReader &r1, const QXmlStreamAttributes &attrs1 = r1.attributes(); const QXmlStreamAttributes &attrs2 = r2.attributes(); - const int len = attrs1.size(); - - if(len != attrs2.size()) + if (attrs1.size() != attrs2.size()) return false; - for(int i = 0; i < len; ++i) - { - if(!attrs2.contains(attrs1.at(i))) - return false; - } - - return true; + auto existsInOtherList = [&attrs2](const auto &attr) { return attrs2.contains(attr); }; + return std::all_of(attrs1.cbegin(), attrs1.cend(), existsInOtherList); } bool QC14N::isDifferent(const QXmlStreamReader &r1, diff --git a/tests/auto/corelib/serialization/qxmlstream/setupSuite.sh b/tests/auto/corelib/serialization/qxmlstream/setupSuite.sh index 2bf5c5e020..3cf842c32a 100755 --- a/tests/auto/corelib/serialization/qxmlstream/setupSuite.sh +++ b/tests/auto/corelib/serialization/qxmlstream/setupSuite.sh @@ -1,31 +1,6 @@ #!/bin/sh -############################################################################# -## -## Copyright (C) 2016 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is the build configuration utility of the Qt Toolkit. -## -## $QT_BEGIN_LICENSE:GPL-EXCEPT$ -## 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 General Public License Usage -## Alternatively, this file may be used under the terms of the GNU -## General Public License version 3 as published by the Free Software -## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -## 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-3.0.html. -## -## $QT_END_LICENSE$ -## -############################################################################# +# Copyright (C) 2016 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 # # Hello! diff --git a/tests/auto/corelib/serialization/qxmlstream/tokenError/dtdInBody.xml b/tests/auto/corelib/serialization/qxmlstream/tokenError/dtdInBody.xml new file mode 100644 index 0000000000..1c3ca4e271 --- /dev/null +++ b/tests/auto/corelib/serialization/qxmlstream/tokenError/dtdInBody.xml @@ -0,0 +1,20 @@ +<!DOCTYPE TEST [ + <!ELEMENT TESTATTRIBUTE (CASE+)> + <!ELEMENT CASE (CLASS, FUNCTION)> + <!ELEMENT CLASS (#PCDATA)> + + <!-- adding random ENTITY statement, as this is typical DTD content --> + <!ENTITY unite "∪"> + + <!ATTLIST CASE CLASS CDATA #REQUIRED> +]> +<TEST> + <CASE> + <CLASS>tst_QXmlStream</CLASS> + </CASE> + <!-- invalid DTD in XML body follows --> + <!DOCTYPE DTDTEST [ + <!ELEMENT RESULT (CASE+)> + <!ATTLIST RESULT OUTPUT CDATA #REQUIRED> + ]> +</TEST> diff --git a/tests/auto/corelib/serialization/qxmlstream/tokenError/multipleDtd.xml b/tests/auto/corelib/serialization/qxmlstream/tokenError/multipleDtd.xml new file mode 100644 index 0000000000..cd398c0f9f --- /dev/null +++ b/tests/auto/corelib/serialization/qxmlstream/tokenError/multipleDtd.xml @@ -0,0 +1,20 @@ +<!DOCTYPE TEST [ + <!ELEMENT TESTATTRIBUTE (CASE+)> + <!ELEMENT CASE (CLASS, FUNCTION, DATASET, COMMENTS)> + <!ELEMENT CLASS (#PCDATA)> + + <!-- adding random ENTITY statements, as this is typical DTD content --> + <!ENTITY iff "⇔"> + + <!ATTLIST CASE CLASS CDATA #REQUIRED> +]> +<!-- invalid second DTD follows --> +<!DOCTYPE SECOND [ + <!ELEMENT SECONDATTRIBUTE (#PCDATA)> + <!ENTITY on "∘"> +]> +<TEST> + <CASE> + <CLASS>tst_QXmlStream</CLASS> + </CASE> +</TEST> diff --git a/tests/auto/corelib/serialization/qxmlstream/tokenError/wellFormed.xml b/tests/auto/corelib/serialization/qxmlstream/tokenError/wellFormed.xml new file mode 100644 index 0000000000..1b61a3f062 --- /dev/null +++ b/tests/auto/corelib/serialization/qxmlstream/tokenError/wellFormed.xml @@ -0,0 +1,15 @@ +<!DOCTYPE TEST [ + <!ELEMENT TESTATTRIBUTE (CASE+)> + <!ELEMENT CASE (CLASS, FUNCTION, DATASET, COMMENTS)> + <!ELEMENT CLASS (#PCDATA)> + + <!-- adding random ENTITY statements, as this is typical DTD content --> + <!ENTITY unite "∪"> + + <!ATTLIST CASE CLASS CDATA #REQUIRED> +]> +<TEST> + <CASE> + <CLASS>tst_QXmlStream</CLASS> + </CASE> +</TEST> diff --git a/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp b/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp index f32e044de6..b90d05b0fa 100644 --- a/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp +++ b/tests/auto/corelib/serialization/qxmlstream/tst_qxmlstream.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** 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 General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** 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-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QDirIterator> @@ -33,19 +8,23 @@ #include <QNetworkReply> #include <QNetworkRequest> #include <QTest> +#include <QtTest/private/qcomparisontesthelper_p.h> #include <QUrl> #include <QXmlStreamReader> #include <QBuffer> #include <QStack> -#include <QtGui/private/qzipreader_p.h> +#include <private/qzipreader_p.h> #include "qc14n.h" +using namespace Qt::StringLiterals; + Q_DECLARE_METATYPE(QXmlStreamReader::ReadElementTextBehaviour) static const char *const catalogFile = "XML-Test-Suite/xmlconf/finalCatalog.xml"; static const int expectedRunCount = 1646; static const int expectedSkipCount = 532; +static const char *const xmlTestsuiteDir = "XML-Test-Suite"; static const char *const xmlconfDir = "XML-Test-Suite/xmlconf/"; static const char *const xmlDatasetName = "xmltest"; static const char *const updateFilesDir = "xmltest_updates"; @@ -71,6 +50,28 @@ static inline int best(int a, int b, int c) return qMin(qMin(a, b), c); } +// copied from tst_qmake.cpp +static void copyDir(const QString &sourceDirPath, const QString &targetDirPath) +{ + QDir currentDir; + QDirIterator dit(sourceDirPath, QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden); + while (dit.hasNext()) { + dit.next(); + const QString targetPath = targetDirPath + QLatin1Char('/') + dit.fileName(); + currentDir.mkpath(targetPath); + copyDir(dit.filePath(), targetPath); + } + + QDirIterator fit(sourceDirPath, QDir::Files | QDir::Hidden); + while (fit.hasNext()) { + fit.next(); + const QString targetPath = targetDirPath + QLatin1Char('/') + fit.fileName(); + QFile::remove(targetPath); // allowed to fail + QFile src(fit.filePath()); + QVERIFY2(src.copy(targetPath), qPrintable(src.errorString())); + } +} + template <typename C> const C sorted_by_name(C c) { // return by const value so we can feed directly into range-for loops below using T = typename C::value_type; @@ -94,8 +95,8 @@ static QByteArray makeCanonical(const QString &filename, bool testIncremental = false) { QFile file(filename); - file.open(QIODevice::ReadOnly); - + if (!file.open(QIODevice::ReadOnly)) + qFatal("Could not open file %s", qPrintable(filename)); QXmlStreamReader reader; QByteArray buffer; @@ -535,13 +536,15 @@ class tst_QXmlStream: public QObject { Q_OBJECT public: - tst_QXmlStream() : m_handler(QUrl::fromLocalFile(QFINDTESTDATA(catalogFile))) + tst_QXmlStream() : m_handler(QUrl::fromLocalFile(m_tempDir.filePath(catalogFile))) { } private slots: void initTestCase(); void cleanupTestCase(); + void compareCompiles(); + void runTestSuite(); void reportFailures() const; void reportFailures_data(); void checkBaseline() const; @@ -560,6 +563,7 @@ private slots: void setEntityResolver(); void readFromQBuffer() const; void readFromQBufferInvalid() const; + void readFromLatin1String() const; void readNextStartElement() const; void readElementText() const; void readElementText_data() const; @@ -568,6 +572,14 @@ private slots: void hasAttribute() const; void writeWithUtf8Codec() const; void writeWithStandalone() const; + void writeCharacters_data() const; + void writeCharacters() const; + void writeAttribute_data() const; + void writeAttribute() const; + void writeBadCharactersUtf8_data() const; + void writeBadCharactersUtf8() const; + void writeBadCharactersUtf16_data() const; + void writeBadCharactersUtf16() const; void entitiesAndWhitespace_1() const; void entitiesAndWhitespace_2() const; void testFalsePrematureError() const; @@ -580,15 +592,24 @@ private slots: void invalidStringCharacters_data() const; void invalidStringCharacters() const; void hasError() const; + void readBack_data() const; void readBack() const; void roundTrip() const; void roundTrip_data() const; + void test_fastScanName_data() const; + void test_fastScanName() const; void entityExpansionLimit() const; + void tokenErrorHandling_data() const; + void tokenErrorHandling() const; + void checkStreamNotationDeclarations() const; + void checkStreamEntityDeclarations() const; + private: static QByteArray readFile(const QString &filename); + QTemporaryDir m_tempDir; TestSuiteHandler m_handler; }; @@ -598,8 +619,18 @@ void tst_QXmlStream::initTestCase() // suit as a zip archive. So we need to unzip it before running the tests, // and also update some files there. // We also need to remove the unzipped data during cleanup. - const QString filesDir(QFINDTESTDATA(xmlconfDir)); - QZipReader reader(filesDir + xmlDatasetName + ".zip"); + + // On Android, we cannot unzip at the resource location, so we copy + // everything to a temporary directory first. + const QString XML_Test_Suite_dir = QFINDTESTDATA(xmlTestsuiteDir); + const QString XML_Test_Suite_destDir = m_tempDir.filePath(xmlTestsuiteDir); + copyDir(XML_Test_Suite_dir, XML_Test_Suite_destDir); + + + const QString filesDir(m_tempDir.filePath(xmlconfDir)); + const QString fileName = filesDir + xmlDatasetName + ".zip"; + QVERIFY(QFile::exists(fileName)); + QZipReader reader(fileName); QVERIFY(reader.isReadable()); QVERIFY(reader.extractAll(filesDir)); // update files @@ -611,19 +642,27 @@ void tst_QXmlStream::initTestCase() QFile::remove(destinationPath); // copy will fail if file exists QVERIFY(QFile::copy(fileInfo.filePath(), destinationPath)); } +} - QFile file(QFINDTESTDATA(catalogFile)); - QVERIFY2(file.open(QIODevice::ReadOnly), - qPrintable(QString::fromLatin1("Failed to open the test suite catalog; %1").arg(file.fileName()))); +void tst_QXmlStream::cleanupTestCase() +{ +} - QVERIFY(m_handler.runTests(&file)); +void tst_QXmlStream::compareCompiles() +{ + QTestPrivate::testEqualityOperatorsCompile<QXmlStreamAttribute>(); + QTestPrivate::testEqualityOperatorsCompile<QXmlStreamNamespaceDeclaration>(); + QTestPrivate::testEqualityOperatorsCompile<QXmlStreamNotationDeclaration>(); + QTestPrivate::testEqualityOperatorsCompile<QXmlStreamEntityDeclaration>(); } -void tst_QXmlStream::cleanupTestCase() +void tst_QXmlStream::runTestSuite() { - QDir d(QFINDTESTDATA(xmlconfDir) + xmlDatasetName); - d.removeRecursively(); - QFile::remove(QLatin1String("test.xml")); + QFile file(m_tempDir.filePath(catalogFile)); + QVERIFY2(file.open(QIODevice::ReadOnly), + qPrintable(QString::fromLatin1("Failed to open the test suite catalog; %1").arg(file.fileName()))); + + QVERIFY(m_handler.runTests(&file)); } void tst_QXmlStream::reportFailures() const @@ -636,7 +675,7 @@ void tst_QXmlStream::reportFailures() const void tst_QXmlStream::reportFailures_data() { - const int len = m_handler.failures.count(); + const int len = m_handler.failures.size(); QTest::addColumn<bool>("isError"); QTest::addColumn<QString>("description"); @@ -673,7 +712,7 @@ void tst_QXmlStream::checkBaseline_data() const QTest::addColumn<QString>("expected"); QTest::addColumn<QString>("output"); - const int len = m_handler.missedBaselines.count(); + const int len = m_handler.missedBaselines.size(); for(int i = 0; i < len; ++i) { @@ -702,7 +741,7 @@ void tst_QXmlStream::reportSuccess_data() const { QTest::addColumn<bool>("isError"); - const int len = m_handler.successes.count(); + const int len = m_handler.successes.size(); for (int i = 0; i < len; ++i) { const QByteArray testName = QByteArray::number(i) + ". " + m_handler.successes.at(i).toLatin1(); @@ -716,7 +755,8 @@ void tst_QXmlStream::reportSuccess_data() const QByteArray tst_QXmlStream::readFile(const QString &filename) { QFile file(filename); - file.open(QIODevice::ReadOnly); + if (!file.open(QIODevice::ReadOnly)) + qFatal("Could not open file %s", qPrintable(filename)); QXmlStreamReader reader; @@ -867,12 +907,17 @@ void tst_QXmlStream::addExtraNamespaceDeclarations() } { QXmlStreamReader xml(data); - xml.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("undeclared", "blabla")); - xml.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("undeclared_too", "foofoo")); + QXmlStreamNamespaceDeclaration undeclared("undeclared", "blabla"); + QXmlStreamNamespaceDeclaration undeclared_too("undeclared_too", "blabla"); + xml.addExtraNamespaceDeclaration(undeclared); + xml.addExtraNamespaceDeclaration(undeclared_too); while (!xml.atEnd()) { xml.readNext(); } QVERIFY2(!xml.hasError(), xml.errorString().toLatin1().constData()); + QT_TEST_EQUALITY_OPS(undeclared, undeclared_too, false); + undeclared = undeclared_too; + QT_TEST_EQUALITY_OPS(undeclared, undeclared_too, true); } } @@ -1093,6 +1138,25 @@ void tst_QXmlStream::readFromQBufferInvalid() const QVERIFY(reader.hasError()); } +void tst_QXmlStream::readFromLatin1String() const +{ + const auto in = "<a>M\xE5rten</a>"_L1; + { + QXmlStreamReader reader(in); + QVERIFY(reader.readNextStartElement()); + QString text = reader.readElementText(); + QCOMPARE(text, "M\xE5rten"_L1); + } + // Same as above, but with addData() + { + QXmlStreamReader reader; + reader.addData(in); + QVERIFY(reader.readNextStartElement()); + QString text = reader.readElementText(); + QCOMPARE(text, "M\xE5rten"_L1); + } +} + void tst_QXmlStream::readNextStartElement() const { QLatin1String in("<?xml version=\"1.0\"?><A><!-- blah --><B><C/></B><B attr=\"value\"/>text</A>"); @@ -1109,6 +1173,10 @@ void tst_QXmlStream::readNextStartElement() const } QCOMPARE(amountOfB, 2); + + // well-formed document end follows + QVERIFY(!reader.readNextStartElement()); + QCOMPARE(reader.error(), QXmlStreamReader::NoError); } void tst_QXmlStream::readElementText() const @@ -1226,8 +1294,20 @@ void tst_QXmlStream::hasAttributeSignature() const void tst_QXmlStream::hasAttribute() const { - QXmlStreamReader reader(QLatin1String("<e xmlns:p='http://example.com/2' xmlns='http://example.com/' " - "attr1='value' attr2='value2' p:attr3='value3' emptyAttr=''><noAttributes/></e>")); + auto xml = QStringLiteral("<e" + " xmlns:p='http://example.com/2'" + " xmlns='http://example.com/'" + " attr1='value'" + " attr2='value2'" + " p:attr3='value3'" + " emptyAttr=''" + " atträbute='meep'" + " α='β'" + " >" + " <noAttributes/>" + "</e>"); + + QXmlStreamReader reader(xml); QCOMPARE(reader.readNext(), QXmlStreamReader::StartDocument); QCOMPARE(reader.readNext(), QXmlStreamReader::StartElement); @@ -1238,8 +1318,18 @@ void tst_QXmlStream::hasAttribute() const QVERIFY(atts.hasAttribute(QLatin1String("attr2"))); QVERIFY(atts.hasAttribute(QLatin1String("p:attr3"))); QVERIFY(atts.hasAttribute(QLatin1String("emptyAttr"))); + QVERIFY(atts.hasAttribute(QLatin1String("attr\xE4""bute"))); + // α is not representable in L1... QVERIFY(!atts.hasAttribute(QLatin1String("DOESNOTEXIST"))); + /* string literals (UTF-8/16) */ + QVERIFY(atts.hasAttribute(u8"atträbute")); + QVERIFY(atts.hasAttribute( u"atträbute")); + QVERIFY(atts.hasAttribute(u8"α")); + QVERIFY(atts.hasAttribute( u"α")); + QVERIFY(!atts.hasAttribute(u8"β")); + QVERIFY(!atts.hasAttribute( u"β")); + /* Test with an empty & null namespaces. */ QVERIFY(atts.hasAttribute(QString(), QLatin1String("attr2"))); /* A null string. */ QVERIFY(atts.hasAttribute(QLatin1String(""), QLatin1String("attr2"))); /* An empty string. */ @@ -1248,6 +1338,8 @@ void tst_QXmlStream::hasAttribute() const QVERIFY(atts.hasAttribute(QString::fromLatin1("attr1"))); QVERIFY(atts.hasAttribute(QString::fromLatin1("attr2"))); QVERIFY(atts.hasAttribute(QString::fromLatin1("p:attr3"))); + QVERIFY(atts.hasAttribute(QStringLiteral("atträbute"))); + QVERIFY(atts.hasAttribute(QStringLiteral("α"))); QVERIFY(atts.hasAttribute(QString::fromLatin1("emptyAttr"))); QVERIFY(!atts.hasAttribute(QString::fromLatin1("DOESNOTEXIST"))); @@ -1261,6 +1353,7 @@ void tst_QXmlStream::hasAttribute() const QVERIFY(!atts.hasAttribute(QLatin1String("WRONG_NAMESPACE"), QString::fromLatin1("attr3"))); /* Invoke on an QXmlStreamAttributes that has no attributes at all. */ + QCOMPARE(reader.readNext(), QXmlStreamReader::Characters); QCOMPARE(reader.readNext(), QXmlStreamReader::StartElement); const QXmlStreamAttributes &atts2 = reader.attributes(); @@ -1279,6 +1372,15 @@ void tst_QXmlStream::hasAttribute() const reader.readNext(); QVERIFY(!reader.hasError()); + + QXmlStreamAttribute attrValue1(QLatin1String("http://example.com/"), QString::fromLatin1("attr1")); + QXmlStreamAttribute attrValue2 = atts.at(0); + QT_TEST_EQUALITY_OPS(atts.at(0), QXmlStreamAttribute(), false); + QT_TEST_EQUALITY_OPS(atts.at(0), attrValue1, false); + QT_TEST_EQUALITY_OPS(atts.at(0), attrValue2, true); + QT_TEST_EQUALITY_OPS(attrValue1, attrValue2, false); + attrValue1 = attrValue2; + QT_TEST_EQUALITY_OPS(attrValue1, attrValue2, true); } void tst_QXmlStream::writeWithUtf8Codec() const @@ -1313,6 +1415,143 @@ void tst_QXmlStream::writeWithStandalone() const } } +static void writeCharacters_data_common() +{ + QTest::addColumn<QString>("input"); + QTest::addColumn<QString>("output"); + + QTest::newRow("empty") << QString() << QString(); + + // invalid content + QTest::newRow("null-character") << u"\0"_s << QString(); + QTest::newRow("vertical-tab") << "\v" << QString(); + QTest::newRow("form-feed") << "\f" << QString(); + QTest::newRow("esc") << "\x1f" << QString(); + QTest::newRow("U+FFFE") << u"\xfffe"_s << QString(); + QTest::newRow("U+FFFF") << u"\xffff"_s << QString(); + + // simple strings + QTest::newRow("us-ascii") << "Hello, world" << "Hello, world"; + QTest::newRow("latin1") << "Bokmål" << "Bokmål"; + QTest::newRow("nonlatin1") << "Ελληνικά" << "Ελληνικά"; + QTest::newRow("nonbmp") << u"\U00010000"_s << u"\U00010000"_s; + + // escaped content + QTest::newRow("less-than") << "<" << "<"; + QTest::newRow("greater-than") << ">" << ">"; + QTest::newRow("ampersand") << "&" << "&"; + QTest::newRow("quote") << "\"" << """; +} + +template <typename Execute, typename Transform> +static void writeCharacters_common(Execute &&exec, Transform &&transform) +{ + QFETCH(QString, input); + QFETCH(QString, output); + QStringView utf16 = input; + QByteArray utf8ba = input.toUtf8(); + QUtf8StringView utf8(utf8ba); + + // may be invalid if input is not Latin1 + QByteArray l1ba = input.toLatin1(); + QLatin1StringView l1(l1ba); + if (l1 != input) + l1 = {}; + + auto write = [&](auto input) -> std::optional<QString> { + QString result; + QXmlStreamWriter writer(&result); + writer.writeStartElement("a"); + exec(writer, input); + writer.writeEndElement(); + if (writer.hasError()) + return std::nullopt; + return result; + }; + + if (input.isNull() != output.isNull()) { + // error + QCOMPARE(write(utf16), std::nullopt); + QCOMPARE(write(utf8), std::nullopt); + if (!l1.isEmpty()) + QCOMPARE(write(l1), std::nullopt); + } else { + output = transform(output); + QCOMPARE(write(utf16), output); + QCOMPARE(write(utf8), output); + if (!l1.isEmpty()) + QCOMPARE(write(l1), output); + } +} + +void tst_QXmlStream::writeCharacters_data() const +{ + writeCharacters_data_common(); + QTest::newRow("tab") << "\t" << "\t"; + QTest::newRow("newline") << "\n" << "\n"; + QTest::newRow("carriage-return") << "\r" << "\r"; +} + +void tst_QXmlStream::writeCharacters() const +{ + auto exec = [](QXmlStreamWriter &writer, auto input) { + writer.writeCharacters(input); + }; + auto transform = [](auto output) { return "<a>" + output + "</a>"; }; + writeCharacters_common(exec, transform); +} + +void tst_QXmlStream::writeAttribute_data() const +{ + writeCharacters_data_common(); + QTest::newRow("tab") << "\t" << "	"; + QTest::newRow("newline") << "\n" << " "; + QTest::newRow("carriage-return") << "\r" << " "; +} + +void tst_QXmlStream::writeAttribute() const +{ + auto exec = [](QXmlStreamWriter &writer, auto input) { + writer.writeAttribute("b", input); + }; + auto transform = [](auto output) { return "<a b=\"" + output + "\"/>"; }; + writeCharacters_common(exec, transform); +} + +#include "../../io/qurlinternal/utf8data.cpp" +void tst_QXmlStream::writeBadCharactersUtf8_data() const +{ + QTest::addColumn<QByteArray>("input"); + loadInvalidUtf8Rows(); +} + +void tst_QXmlStream::writeBadCharactersUtf8() const +{ + QFETCH(QByteArray, input); + QString target; + QXmlStreamWriter writer(&target); + writer.writeTextElement("a", QUtf8StringView(input)); + QVERIFY(writer.hasError()); +} + +void tst_QXmlStream::writeBadCharactersUtf16_data() const +{ + QTest::addColumn<QString>("input"); + QTest::addRow("low-surrogate") << u"\xdc00"_s; + QTest::addRow("high-surrogate") << u"\xd800"_s; + QTest::addRow("inverted-surrogate-pair") << u"\xdc00\xd800"_s; + QTest::addRow("high-surrogate+non-surrogate") << u"\xd800z"_s; +} + +void tst_QXmlStream::writeBadCharactersUtf16() const +{ + QFETCH(QString, input); + QString target; + QXmlStreamWriter writer(&target); + writer.writeTextElement("a", input); + QVERIFY(writer.hasError()); +} + void tst_QXmlStream::entitiesAndWhitespace_1() const { QXmlStreamReader reader(QLatin1String("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\"><test>&extEnt;</test>")); @@ -1632,41 +1871,64 @@ void tst_QXmlStream::invalidStringCharacters_data() const // } -static bool isValidSingleTextChar(const ushort c) +static bool isValidSingleTextChar(char32_t c) { - // Conforms to https://www.w3.org/TR/REC-xml/#NT-Char - except for the high range, which is done - // with surrogates. + // Conforms to https://www.w3.org/TR/REC-xml/#NT-Char // Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] - static const QPair<ushort, ushort> validRanges[] = { - QPair<ushort, ushort>(0x9, 0xb), - QPair<ushort, ushort>(0xd, 0xe), - QPair<ushort, ushort>(0x20, 0xd800), - QPair<ushort, ushort>(0xe000, 0xfffe) + constexpr struct { char32_t lo, hi; } validRanges[] = { + {0x9, 0xA}, + {0xD, 0xD}, + {0x20, 0xD7ff}, + {0xE000, 0xFFFD}, + {0x1'0000, 0x10'FFFF}, }; - for (const QPair<ushort, ushort> &range : validRanges) { - if (c >= range.first && c < range.second) + for (const auto range : validRanges) { + if (c >= range.lo && c <= range.hi) return true; } return false; } +void tst_QXmlStream::readBack_data() const +{ + QTest::addColumn<int>("plane"); + + // Check all 17 Unicode planes. Split into separate executions lest the + // test function times out in asan builds. + + for (int i = 0; i < 17; ++i) + QTest::addRow("plane-%02d", i) << i; +} + void tst_QXmlStream::readBack() const { - for (ushort c = 0; c < std::numeric_limits<ushort>::max(); ++c) { - QBuffer buffer; + QFETCH(const int, plane); + + constexpr qsizetype MaxChunkSizeWhenEncoding = 512; // from qxmlstream.cpp + QBuffer buffer; + QString text = QString(513, 'a'); // one longer than the internal conversion buffer + + for (char16_t i = 0; i < (std::numeric_limits<char16_t>::max)(); ++i) { + + const char32_t c = (plane << 16) + i; + + // end chunk in invalid character, split surrogates: + const auto pair = QChar::fromUcs4(c); + text.resize(MaxChunkSizeWhenEncoding + 1 - pair.size()); + text += pair; - QVERIFY(buffer.open(QIODevice::WriteOnly)); + QVERIFY(buffer.open(QIODevice::WriteOnly|QIODevice::Truncate)); QXmlStreamWriter writer(&buffer); writer.writeStartDocument(); - writer.writeTextElement("a", QString(QChar(c))); + writer.writeTextElement("a", text); writer.writeEndDocument(); buffer.close(); - if (writer.hasError()) { - QVERIFY2(!isValidSingleTextChar(c), QByteArray::number(c)); + if (!isValidSingleTextChar(c)) { + QVERIFY2(writer.hasError(), QByteArray::number(c)); } else { - QVERIFY2(isValidSingleTextChar(c), QByteArray::number(c)); + QVERIFY2(!writer.hasError(), QByteArray::number(c)); QVERIFY(buffer.open(QIODevice::ReadOnly)); QXmlStreamReader reader(&buffer); do { @@ -1688,6 +1950,22 @@ void tst_QXmlStream::roundTrip_data() const "<child xmlns:unknown=\"http://mydomain\">Text</child>" "</father>" "</root>\n"; + + // When a namespace is introduced by an attribute of an element, + // that element can exercise the namespace in its tag. + // This used (QTBUG-75456) to lead to the namespace definition + // being wrongly duplicated, with a new name. + QTest::newRow("QTBUG-75456") << + "<?xml version=\"1.0\"?>" + "<abc:root xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:abc=\"ns1\">" + "<abc:parent>" + "<abc:child xmlns:unknown=\"http://mydomain\">Text</abc:child>" + "</abc:parent>" + "<def:parent xmlns:def=\"ns2\" id=\"test\">" + "<def:child id=\"Timmy\">More text</def:child>" + "<def:child id=\"Jimmy\">Even more text</def:child>" + "</def:parent>" + "</abc:root>\n"; } void tst_QXmlStream::entityExpansionLimit() const @@ -1747,5 +2025,126 @@ void tst_QXmlStream::roundTrip() const QCOMPARE(out, in); } +void tst_QXmlStream::test_fastScanName_data() const +{ + QTest::addColumn<QByteArray>("data"); + QTest::addColumn<QXmlStreamReader::Error>("errorType"); + + // 4096 is the limit in QXmlStreamReaderPrivate::fastScanName() + + QByteArray arr = "<a:" + QByteArray("b").repeated(4096 - 1); + QTest::newRow("data1") << arr << QXmlStreamReader::PrematureEndOfDocumentError; + + arr = "<a:" + QByteArray("b").repeated(4096); + QTest::newRow("data2") << arr << QXmlStreamReader::NotWellFormedError; + + arr = "<" + QByteArray("a").repeated(4000) + ":" + QByteArray("b").repeated(96); + QTest::newRow("data3") << arr << QXmlStreamReader::PrematureEndOfDocumentError; + + arr = "<" + QByteArray("a").repeated(4000) + ":" + QByteArray("b").repeated(96 + 1); + QTest::newRow("data4") << arr << QXmlStreamReader::NotWellFormedError; + + arr = "<" + QByteArray("a").repeated(4000 + 1) + ":" + QByteArray("b").repeated(96); + QTest::newRow("data5") << arr << QXmlStreamReader::NotWellFormedError; +} + +void tst_QXmlStream::test_fastScanName() const +{ + QFETCH(QByteArray, data); + QFETCH(QXmlStreamReader::Error, errorType); + + QXmlStreamReader reader(data); + QXmlStreamReader::TokenType tokenType; + while (!reader.atEnd()) + tokenType = reader.readNext(); + + QCOMPARE(tokenType, QXmlStreamReader::Invalid); + QCOMPARE(reader.error(), errorType); +} + +void tst_QXmlStream::tokenErrorHandling_data() const +{ + QTest::addColumn<QString>("fileName"); + QTest::addColumn<QXmlStreamReader::Error>("expectedError"); + QTest::addColumn<QString>("errorKeyWord"); + + constexpr auto invalid = QXmlStreamReader::Error::UnexpectedElementError; + constexpr auto valid = QXmlStreamReader::Error::NoError; + QTest::newRow("DtdInBody") << "dtdInBody.xml" << invalid << "DTD"; + QTest::newRow("multipleDTD") << "multipleDtd.xml" << invalid << "second DTD"; + QTest::newRow("wellFormed") << "wellFormed.xml" << valid << ""; +} + +void tst_QXmlStream::tokenErrorHandling() const +{ + QFETCH(const QString, fileName); + QFETCH(const QXmlStreamReader::Error, expectedError); + QFETCH(const QString, errorKeyWord); + + const QDir dir(QFINDTESTDATA("tokenError")); + QFile file(dir.absoluteFilePath(fileName)); + + // Cross-compiling: Files may not be found when running test standalone + // QSKIP in that case, because the tested functionality is platform independent. + if (!file.exists()) + QSKIP(QObject::tr("Testfile %1 not found.").arg(fileName).toUtf8().constData()); + + QVERIFY(file.open(QIODevice::ReadOnly)); + QXmlStreamReader reader(&file); + while (!reader.atEnd()) + reader.readNext(); + + QCOMPARE(reader.error(), expectedError); + if (expectedError != QXmlStreamReader::Error::NoError) + QVERIFY(reader.errorString().contains(errorKeyWord)); +} + +void tst_QXmlStream::checkStreamNotationDeclarations() const +{ + QString fileName("12.xml"); + const QDir dir(QFINDTESTDATA("data")); + QFile file(dir.absoluteFilePath(fileName)); + if (!file.exists()) + QSKIP(QObject::tr("Testfile %1 not found.").arg(fileName).toUtf8().constData()); + QVERIFY(file.open(QIODevice::ReadOnly)); + QXmlStreamReader reader(&file); + while (!reader.atEnd()) + reader.readNext(); + + QVERIFY(!reader.hasError()); + QXmlStreamNotationDeclaration notation1, notation2, notation3; + QT_TEST_EQUALITY_OPS(notation1, notation2, true); + const auto notationDeclarations = reader.notationDeclarations(); + if (notationDeclarations.count() >= 2) { + notation1 = notationDeclarations.at(0); + notation2 = notationDeclarations.at(1); + notation3 = notationDeclarations.at(1); + } + QT_TEST_EQUALITY_OPS(notation1, notation2, false); + QT_TEST_EQUALITY_OPS(notation3, notation2, true); +} + +void tst_QXmlStream::checkStreamEntityDeclarations() const +{ + QString fileName("5.xml"); + const QDir dir(QFINDTESTDATA("data")); + QFile file(dir.absoluteFilePath(fileName)); + if (!file.exists()) + QSKIP(QObject::tr("Testfile %1 not found.").arg(fileName).toUtf8().constData()); + QVERIFY(file.open(QIODevice::ReadOnly)); + QXmlStreamReader reader(&file); + while (!reader.atEnd()) + reader.readNext(); + + QVERIFY(!reader.hasError()); + QXmlStreamEntityDeclaration entity; + QT_TEST_EQUALITY_OPS(entity, QXmlStreamEntityDeclaration(), true); + + const auto entityDeclarations = reader.entityDeclarations(); + if (entityDeclarations.count() >= 2) { + entity = entityDeclarations.at(1); + QT_TEST_EQUALITY_OPS(entityDeclarations.at(0), entityDeclarations.at(1), false); + QT_TEST_EQUALITY_OPS(entity, entityDeclarations.at(1), true); + } +} #include "tst_qxmlstream.moc" -// vim: et:ts=4:sw=4:sts=4 |