Revision control
Copy as Markdown
// 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"