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 | |
42 | class tst_QCborStreamWriter : public QObject |
43 | { |
44 | Q_OBJECT |
45 | |
46 | private 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) |
65 | typedef quint64 CborTag; |
66 | #include "data.cpp" |
67 | |
68 | void 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 | |
144 | void 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 | |
162 | void 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 | |
169 | void tst_QCborStreamWriter::fixed_data() |
170 | { |
171 | addColumns(); |
172 | addFixedData(); |
173 | } |
174 | |
175 | void tst_QCborStreamWriter::fixed() |
176 | { |
177 | QFETCH(QVariant, input); |
178 | QFETCH(QByteArray, output); |
179 | compare(input, output); |
180 | } |
181 | |
182 | void tst_QCborStreamWriter::strings_data() |
183 | { |
184 | addColumns(); |
185 | addStringsData(); |
186 | } |
187 | |
188 | void 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 | |
213 | void 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 | |
256 | void tst_QCborStreamWriter::arraysAndMaps_data() |
257 | { |
258 | addColumns(); |
259 | addArraysAndMaps(); |
260 | } |
261 | |
262 | void tst_QCborStreamWriter::tags_data() |
263 | { |
264 | addColumns(); |
265 | addFixedData(); |
266 | addStringsData(); |
267 | addArraysAndMaps(); |
268 | } |
269 | |
270 | void 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 | |
278 | void 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 | |
299 | void 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 | |
311 | QTEST_MAIN(tst_QCborStreamWriter) |
312 | |
313 | #include "tst_qcborstreamwriter.moc" |
314 | |