Revision control
++ // Copyright (C) 2022 The Qt Company Ltd.
++ // Copyright (C) 2022 Alexey Edelev <semlanik@gmail.com>
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #include <QtTest/qtest.h>
++
++ #include <QtCore/qhash.h>
++ #include <QtProtobuf/qprotobufserializer.h>
++
++ #include "unknownfield.qpb.h"
++
++ // NOTE: all test data in this file is generated by generate_unknownfield_data.py
++ // located in the ./proto folder!
++
++ class tst_protobuf_unknown_field : public QObject
++ {
++ Q_OBJECT
++ private Q_SLOTS:
++ void unknownStringField();
++ void unknownIntField();
++ void unknownMapField();
++ void unknownRepeatedField();
++ void unknownRepeatedFieldNonPacked();
++ void nonPreserveUnknownFieldsSerializer();
++ };
++
++ // Deserialize a full known message into a type for which one of the fields is
++ // unknown, then serialize the message back to bytes.
++ void partialMessageRoundtrip_common(QByteArrayView data, QByteArray *out, QByteArray *leftovers)
++ {
++ Q_ASSERT(out);
++ Q_ASSERT(leftovers);
++
++ qtproto::tests::PartialMessage pm;
++ QProtobufSerializer serializer;
++ pm.deserialize(&serializer, data);
++
++ for (const auto &num : pm.unknownFieldNumbers())
++ *leftovers += pm.unknownFieldData(num).join();
++
++ QCOMPARE(pm.aaa(), 2);
++ QCOMPARE(pm.timestamp(), 42);
++ pm.setTimestamp(64); // update one field, will not affect other fields
++ *out = pm.serialize(&serializer);
++ }
++
++ template<typename TargetType>
++ void partialMessageRoundtrip(QByteArrayView known, QByteArrayView unknown, TargetType *targetMessage)
++ {
++ Q_ASSERT(targetMessage);
++ QByteArray roundtrippedProto;
++ QByteArray leftovers;
++
++ partialMessageRoundtrip_common(known.toByteArray().append(unknown), &roundtrippedProto, &leftovers);
++
++ QProtobufSerializer serializer;
++ targetMessage->deserialize(&serializer, roundtrippedProto);
++ QCOMPARE(targetMessage->aaa(), 2);
++ QCOMPARE(targetMessage->timestamp(), 64);
++
++ QCOMPARE(unknown.toByteArray().toHex(), leftovers.toHex());
++ }
++
++ void tst_protobuf_unknown_field::unknownStringField()
++ {
++ // "2, 42, Hello World"
++ QByteArray knownPayload = QByteArray::fromHex("0802102a");
++ QByteArray unknownPayload = QByteArray::fromHex("1a0b48656c6c6f20576f726c64");
++ qtproto::tests::StringMessage sm;
++ partialMessageRoundtrip<>(knownPayload, unknownPayload, &sm);
++
++ QCOMPARE(sm.extraField(), u"Hello World");
++
++ // "2, 42, Hello World"
++ // Same as above but the field number of the string is huge
++ knownPayload = QByteArray::fromHex("0802102a");
++ unknownPayload = QByteArray::fromHex("a2ffffff0f0b48656c6c6f20576f726c64");
++ qtproto::tests::LargeIndexStringMessage lsm;
++ partialMessageRoundtrip<>(knownPayload, unknownPayload, &lsm);
++
++ QCOMPARE(lsm.extraField(), u"Hello World");
++
++ }
++
++ void tst_protobuf_unknown_field::unknownIntField()
++ {
++ // "2, 42, 242"
++ QByteArray knownPayload = QByteArray::fromHex("0802102a");
++ QByteArray unknownPayload = QByteArray::fromHex("18f201");
++
++ qtproto::tests::IntMessage im;
++ partialMessageRoundtrip<>(knownPayload, unknownPayload, &im);
++
++ QCOMPARE(im.extraField(), 242);
++ }
++
++ void tst_protobuf_unknown_field::unknownMapField()
++ {
++ // "2, 42, {1: 2, 2: 4, 3: 6}"
++ QByteArray knownPayload = QByteArray::fromHex("0802102a");
++ QByteArray unknownPayload = QByteArray::fromHex("1a04080110021a04080210041a0408031006");
++ qtproto::tests::MapMessage mm;
++ partialMessageRoundtrip<>(knownPayload, unknownPayload, &mm);
++
++ QHash<QtProtobuf::int32, QtProtobuf::int32> expected{
++ { 1, 2 },
++ { 2, 4 },
++ { 3, 6 },
++ };
++ QCOMPARE(mm.extraField(), expected);
++ }
++
++ void tst_protobuf_unknown_field::unknownRepeatedField()
++ {
++ // "2, 42, [1, 1, 2, 3, 5, -1, -5, -3, -2, -1]"
++ QByteArray knownPayload = QByteArray::fromHex("0802102a");
++ QByteArray unknownPayload = QByteArray::fromHex("1a370101020305ffffffffffffffffff01"
++ "fbffffffffffffffff01fdffffffffffffffff01"
++ "feffffffffffffffff01ffffffffffffffffff01");
++ qtproto::tests::RepeatedMessage mm;
++ partialMessageRoundtrip<>(knownPayload, unknownPayload, &mm);
++
++ QList<QtProtobuf::int32> expected{ 1, 1, 2, 3, 5, -1, -5, -3, -2, -1 };
++ QCOMPARE(mm.extraField(), expected);
++ }
++
++ void tst_protobuf_unknown_field::unknownRepeatedFieldNonPacked()
++ {
++ // "2, 42, [1, 1, 2, 3, 5, -1, -5, -3, -2, -1]"
++ QByteArray knowPayload = QByteArray::fromHex("0802102a");
++ QByteArray unknownPayload = QByteArray::fromHex("1801180118021803180518ffffffffffffffffff01"
++ "18fbffffffffffffffff0118fdffffffffffffffff01"
++ "18feffffffffffffffff0118ffffffffffffffffff01");
++ qtproto::tests::RepeatedMessageNonPacked mm;
++ partialMessageRoundtrip<>(knowPayload, unknownPayload, &mm);
++
++ QList<QtProtobuf::int32> expected{ 1, 1, 2, 3, 5, -1, -5, -3, -2, -1 };
++ QCOMPARE(mm.extraField(), expected);
++ }
++
++ void tst_protobuf_unknown_field::nonPreserveUnknownFieldsSerializer()
++ {
++ QProtobufSerializer serializer;
++ serializer.shouldPreserveUnknownFields(false);
++ qtproto::tests::PartialMessage pm;
++ pm.deserialize(&serializer, QByteArray::fromHex("0802102a18f201"));
++
++ QVERIFY(pm.unknownFieldNumbers().isEmpty());
++ QCOMPARE(pm.serialize(&serializer).toHex(), "0802102a");
++
++ serializer.shouldPreserveUnknownFields(true);
++ pm.deserialize(&serializer, QByteArray::fromHex("0802102a18f201"));
++
++ serializer.shouldPreserveUnknownFields(false);
++ QVERIFY(!pm.unknownFieldNumbers().isEmpty());
++ QCOMPARE(pm.serialize(&serializer).toHex(), "0802102a");
++ }
++
++
++ QTEST_MAIN(tst_protobuf_unknown_field)
++ #include "tst_protobuf_unknown_field.moc"