1/****************************************************************************
2**
3** Copyright (C) 2018 Intel Corporation.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <QtTest>
41
42class tst_QCborStreamWriter : public QObject
43{
44 Q_OBJECT
45
46private Q_SLOTS:
47 void initTestCase_data();
48 void fixed_data();
49 void fixed();
50 void strings_data();
51 void strings() { fixed(); }
52 void nonAsciiStrings_data();
53 void nonAsciiStrings();
54 void arraysAndMaps_data();
55 void arraysAndMaps() { fixed(); }
56 void tags_data();
57 void tags();
58 void arrays_data() { tags_data(); }
59 void arrays();
60 void maps_data() { tags_data(); }
61 void maps();
62};
63
64// Get the data from TinyCBOR (see src/3rdparty/tinycbor/tests/encoder/data.cpp)
65typedef quint64 CborTag;
66#include "data.cpp"
67
68void encodeVariant(QCborStreamWriter &writer, const QVariant &v)
69{
70 int type = v.userType();
71 switch (type) {
72 case QVariant::Int:
73 case QVariant::LongLong:
74 return writer.append(i: v.toLongLong());
75
76 case QVariant::UInt:
77 case QVariant::ULongLong:
78 return writer.append(u: v.toULongLong());
79
80 case QVariant::Bool:
81 return writer.append(b: v.toBool());
82
83 case QVariant::Invalid:
84 return writer.appendUndefined();
85
86 case QMetaType::VoidStar:
87 return writer.append(nullptr);
88
89 case QVariant::Double:
90 return writer.append(d: v.toDouble());
91
92 case QMetaType::Float:
93 return writer.append(f: v.toFloat());
94
95 case QVariant::String:
96 return writer.append(str: v.toString());
97
98 case QVariant::ByteArray:
99 return writer.append(ba: v.toByteArray());
100
101 default:
102 if (type == qMetaTypeId<NegativeInteger>())
103 return writer.append(n: QCborNegativeInteger(v.value<NegativeInteger>().abs));
104 if (type == qMetaTypeId<SimpleType>())
105 return writer.append(st: QCborSimpleType(v.value<SimpleType>().type));
106 if (type == qMetaTypeId<qfloat16>())
107 return writer.append(f: v.value<qfloat16>());
108 if (type == qMetaTypeId<Tag>()) {
109 writer.append(tag: QCborTag(v.value<Tag>().tag));
110 return encodeVariant(writer, v: v.value<Tag>().tagged);
111 }
112 if (type == QVariant::List || type == qMetaTypeId<IndeterminateLengthArray>()) {
113 QVariantList list = v.toList();
114 if (type == qMetaTypeId<IndeterminateLengthArray>()) {
115 list = v.value<IndeterminateLengthArray>();
116 writer.startArray();
117 } else {
118 writer.startArray(count: list.length());
119 }
120 for (const QVariant &v2 : qAsConst(t&: list))
121 encodeVariant(writer, v: v2);
122 QVERIFY(writer.endArray());
123 return;
124 }
125 if (type == qMetaTypeId<Map>() || type == qMetaTypeId<IndeterminateLengthMap>()) {
126 Map map = v.value<Map>();
127 if (type == qMetaTypeId<IndeterminateLengthMap>()) {
128 map = v.value<IndeterminateLengthMap>();
129 writer.startMap();
130 } else {
131 writer.startMap(count: map.length());
132 }
133 for (auto pair : qAsConst(t&: map)) {
134 encodeVariant(writer, v: pair.first);
135 encodeVariant(writer, v: pair.second);
136 }
137 QVERIFY(writer.endMap());
138 return;
139 }
140 }
141 QFAIL("Shouldn't have got here");
142}
143
144void compare(const QVariant &input, const QByteArray &output)
145{
146 QFETCH_GLOBAL(bool, useDevice);
147
148 if (useDevice) {
149 QBuffer buffer;
150 buffer.open(openMode: QIODevice::WriteOnly);
151 QCborStreamWriter writer(&buffer);
152 encodeVariant(writer, v: input);
153 QCOMPARE(buffer.data(), output);
154 } else {
155 QByteArray buffer;
156 QCborStreamWriter writer(&buffer);
157 encodeVariant(writer, v: input);
158 QCOMPARE(buffer, output);
159 }
160}
161
162void tst_QCborStreamWriter::initTestCase_data()
163{
164 QTest::addColumn<bool>(name: "useDevice");
165 QTest::newRow(dataTag: "QByteArray") << false;
166 QTest::newRow(dataTag: "QIODevice") << true;
167}
168
169void tst_QCborStreamWriter::fixed_data()
170{
171 addColumns();
172 addFixedData();
173}
174
175void tst_QCborStreamWriter::fixed()
176{
177 QFETCH(QVariant, input);
178 QFETCH(QByteArray, output);
179 compare(input, output);
180}
181
182void tst_QCborStreamWriter::strings_data()
183{
184 addColumns();
185 addStringsData();
186}
187
188void tst_QCborStreamWriter::nonAsciiStrings_data()
189{
190 QTest::addColumn<QByteArray>(name: "output");
191 QTest::addColumn<QString>(name: "input");
192 QTest::addColumn<bool>(name: "isLatin1");
193
194 QByteArray latin1 = u8"Résumé";
195 QTest::newRow(dataTag: "shortlatin1")
196 << ("\x68" + latin1) << QString::fromUtf8(str: latin1) << true;
197
198 // replicate it 5 times (total 40 bytes)
199 latin1 += latin1 + latin1 + latin1 + latin1;
200 QTest::newRow(dataTag: "longlatin1")
201 << ("\x78\x28" + latin1) << QString::fromUtf8(str: latin1) << true;
202
203 QByteArray nonlatin1 = u8"Χαίρετε";
204 QTest::newRow(dataTag: "shortnonlatin1")
205 << ("\x6e" + nonlatin1) << QString::fromUtf8(str: nonlatin1) << false;
206
207 // replicate it 4 times (total 56 bytes)
208 nonlatin1 = nonlatin1 + nonlatin1 + nonlatin1 + nonlatin1;
209 QTest::newRow(dataTag: "longnonlatin1")
210 << ("\x78\x38" + nonlatin1) << QString::fromUtf8(str: nonlatin1) << false;
211}
212
213void tst_QCborStreamWriter::nonAsciiStrings()
214{
215 QFETCH(QByteArray, output);
216 QFETCH(QString, input);
217 QFETCH(bool, isLatin1);
218 QFETCH_GLOBAL(bool, useDevice);
219
220 // will be wrong if !isLatin1
221 QByteArray latin1 = input.toLatin1();
222
223 if (useDevice) {
224 {
225 QBuffer buffer;
226 buffer.open(openMode: QIODevice::WriteOnly);
227 QCborStreamWriter writer(&buffer);
228 writer.append(str: input);
229 QCOMPARE(buffer.data(), output);
230 }
231
232 if (isLatin1) {
233 QBuffer buffer;
234 buffer.open(openMode: QIODevice::WriteOnly);
235 QCborStreamWriter writer(&buffer);
236 writer.append(str: QLatin1String(latin1.constData(), latin1.size()));
237 QCOMPARE(buffer.data(), output);
238 }
239 } else {
240 {
241 QByteArray buffer;
242 QCborStreamWriter writer(&buffer);
243 encodeVariant(writer, v: input);
244 QCOMPARE(buffer, output);
245 }
246
247 if (isLatin1) {
248 QByteArray buffer;
249 QCborStreamWriter writer(&buffer);
250 writer.append(str: QLatin1String(latin1.constData(), latin1.size()));
251 QCOMPARE(buffer, output);
252 }
253 }
254}
255
256void tst_QCborStreamWriter::arraysAndMaps_data()
257{
258 addColumns();
259 addArraysAndMaps();
260}
261
262void tst_QCborStreamWriter::tags_data()
263{
264 addColumns();
265 addFixedData();
266 addStringsData();
267 addArraysAndMaps();
268}
269
270void tst_QCborStreamWriter::tags()
271{
272 QFETCH(QVariant, input);
273 QFETCH(QByteArray, output);
274
275 compare(input: QVariant::fromValue(value: Tag{.tag: 1, .tagged: input}), output: "\xc1" + output);
276}
277
278void tst_QCborStreamWriter::arrays()
279{
280 QFETCH(QVariant, input);
281 QFETCH(QByteArray, output);
282
283 compare(input: make_list(args: input), output: "\x81" + output);
284 if (QTest::currentTestFailed())
285 return;
286
287 compare(input: make_list(args: input, args: input), output: "\x82" + output + output);
288 if (QTest::currentTestFailed())
289 return;
290
291 // nested lists
292 compare(input: make_list(args: make_list(args: input)), output: "\x81\x81" + output);
293 if (QTest::currentTestFailed())
294 return;
295
296 compare(input: make_list(args: make_list(args: input), args: make_list(args: input)), output: "\x82\x81" + output + "\x81" + output);
297}
298
299void tst_QCborStreamWriter::maps()
300{
301 QFETCH(QVariant, input);
302 QFETCH(QByteArray, output);
303
304 compare(input: make_map(list: {{1, input}}), output: "\xa1\1" + output);
305 if (QTest::currentTestFailed())
306 return;
307
308 compare(input: make_map(list: {{1, input}, {input, 24}}), output: "\xa2\1" + output + output + "\x18\x18");
309}
310
311QTEST_MAIN(tst_QCborStreamWriter)
312
313#include "tst_qcborstreamwriter.moc"
314

source code of qtbase/tests/auto/corelib/serialization/qcborstreamwriter/tst_qcborstreamwriter.cpp