1/****************************************************************************
2**
3** Copyright (C) 2015 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL21$
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 http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://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 2.1 or version 3 as published by the Free
20** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22** following information to ensure the GNU Lesser General Public License
23** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** As a special exception, The Qt Company gives you certain additional
27** rights. These rights are described in The Qt Company LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29**
30** $QT_END_LICENSE$
31**
32****************************************************************************/
33
34//TESTED_COMPONENT=src/versit
35
36#include "tst_qvcard21writer.h"
37#ifdef QT_BUILD_INTERNAL
38#include <QtVersit/private/qvcard21writer_p.h>
39#endif
40#include <QtVersit/qversitproperty.h>
41#include <QtVersit/qversitdocument.h>
42#include <QtTest/QtTest>
43#include <QByteArray>
44#include <QVariant>
45
46// This says "NOKIA" in Katakana
47const QString KATAKANA_NOKIA(QString::fromUtf8(str: "\xe3\x83\x8e\xe3\x82\xad\xe3\x82\xa2"));
48
49QTVERSIT_USE_NAMESPACE
50
51Q_DECLARE_METATYPE(QVersitProperty)
52
53// Because the QFETCH macro balks on the comma in QMultiHash<QString,QString>
54typedef QMultiHash<QString,QString> StringHash;
55Q_DECLARE_METATYPE(StringHash)
56#ifdef QT_BUILD_INTERNAL
57void tst_QVCard21Writer::init()
58{
59 mWriter = new QVCard21Writer(QVersitDocument::VCard21Type);
60 mWriter->setCodec(QTextCodec::codecForName(name: "ISO_8859-1"));
61}
62
63void tst_QVCard21Writer::cleanup()
64{
65 delete mWriter;
66}
67
68void tst_QVCard21Writer::testEncodeVersitProperty()
69{
70 QFETCH(QVersitProperty, property);
71 QFETCH(QByteArray, expectedResult);
72 QFETCH(QByteArray, codec);
73 QTextCodec* textCodec = QTextCodec::codecForName(name: codec);
74 QByteArray encodedProperty;
75 QBuffer buffer(&encodedProperty);
76 mWriter->setDevice(&buffer);
77 mWriter->setCodec(textCodec);
78 buffer.open(openMode: QIODevice::WriteOnly);
79
80 mWriter->encodeVersitProperty(property);
81 if (encodedProperty != expectedResult) {
82 qDebug() << "Encoded: " << encodedProperty;
83 qDebug() << "Expected: " << expectedResult;
84 QVERIFY(false);
85 }
86}
87
88void tst_QVCard21Writer::testEncodeVersitProperty_data()
89{
90 QTest::addColumn<QVersitProperty>(name: "property");
91 QTest::addColumn<QByteArray>(name: "expectedResult");
92 QTest::addColumn<QByteArray>(name: "codec");
93
94 QVersitProperty property;
95 QByteArray expectedResult;
96 QByteArray codec("ISO-8859_1");
97
98 // normal case
99 property.setName(QString::fromLatin1(str: "FN"));
100 property.setValue(QString::fromLatin1(str: "John Citizen"));
101 property.setValueType(QVersitProperty::PlainType);
102 expectedResult = "FN:John Citizen\r\n";
103 QTest::newRow(dataTag: "No parameters") << property << expectedResult << codec;
104
105 // Structured N - escaping should happen for semicolons, not for commas
106 property.setName(QStringLiteral("N"));
107 property.setValue(QStringList()
108 << QStringLiteral("La;st") // needs to be backslash escaped
109 << QStringLiteral("Fi,rst")
110 << QStringLiteral("Mi:ddle")
111 << QStringLiteral("Pr\\efix") // needs to be QP encoded
112 << QStringLiteral("Suffix"));
113 property.setValueType(QVersitProperty::CompoundType);
114 expectedResult = "N;ENCODING=QUOTED-PRINTABLE:La\\;st;Fi,rst;Mi:ddle;Pr=5Cefix;Suffix\r\n";
115 QTest::newRow(dataTag: "N property") << property << expectedResult << codec;
116
117 // Structured N - there was a bug where if two fields had to be escaped,
118 // two ENCODING parameters were added
119 property.setName(QStringLiteral("N"));
120 property.setValue(QStringList()
121 << QStringLiteral("La\\st")
122 << QStringLiteral("Fi\\rst")
123 << QString()
124 << QString()
125 << QString());
126 property.setValueType(QVersitProperty::CompoundType);
127 expectedResult = "N;ENCODING=QUOTED-PRINTABLE:La=5Cst;Fi=5Crst;;;\r\n";
128 QTest::newRow(dataTag: "N property, double-encoded") << property << expectedResult << codec;
129
130 // Structured N - one field needs to be encoded in UTF-8 while the other doesn't
131 // correct behaviour is to encode the whole thing in UTF-8
132 property.setName(QStringLiteral("N"));
133 property.setValue(QStringList()
134 << QString::fromUtf8(str: "\xE2\x82\xAC") // euro sign
135 << QString::fromLatin1(str: "\xA3") // pound sign (upper Latin-1)
136 << QString()
137 << QString()
138 << QString());
139 property.setValueType(QVersitProperty::CompoundType);
140 expectedResult = "N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E2=82=AC;=C2=A3;;;\r\n";
141 QTest::newRow(dataTag: "N property, double-encoded") << property << expectedResult << codec;
142
143 // Structured CATEGORIES - escaping should happen for commas, not semicolons
144 property.setName(QStringLiteral("CATEGORIES"));
145 property.setValue(QStringList()
146 << QStringLiteral("re;d")
147 << QStringLiteral("gr,een")
148 << QStringLiteral("bl:ue"));
149 property.setValueType(QVersitProperty::ListType);
150 expectedResult = "CATEGORIES:re;d,gr\\,een,bl:ue\r\n";
151 QTest::newRow(dataTag: "CATEGORIES property") << property << expectedResult << codec;
152
153 // With parameter(s). No special characters in the value.
154 // -> No need to Quoted-Printable encode the value.
155 expectedResult = "TEL;HOME:123\r\n";
156 property.setName(QString::fromLatin1(str: "TEL"));
157 property.setValue(QString::fromLatin1(str: "123"));
158 property.insertParameter(name: QString::fromLatin1(str: "TYPE"),value: QString::fromLatin1(str: "HOME"));
159 QTest::newRow(dataTag: "With parameters, plain value") << property << expectedResult << codec;
160
161 expectedResult = "EMAIL;ENCODING=QUOTED-PRINTABLE;HOME:john.citizen=40example.com\r\n";
162 property.setName(QString::fromLatin1(str: "EMAIL"));
163 property.setValue(QString::fromLatin1(str: "john.citizen@example.com"));
164 QTest::newRow(dataTag: "With parameters, special value") << property << expectedResult << codec;
165
166 // AGENT property with parameter
167 expectedResult =
168"AGENT;X-PARAMETER=VALUE:\r\n\
169BEGIN:VCARD\r\n\
170VERSION:2.1\r\n\
171FN:Secret Agent\r\n\
172END:VCARD\r\n\
173\r\n";
174 property.setParameters(QMultiHash<QString,QString>());
175 property.setName(QString::fromLatin1(str: "AGENT"));
176 property.setValue(QString());
177 property.insertParameter(name: QString::fromLatin1(str: "X-PARAMETER"),value: QString::fromLatin1(str: "VALUE"));
178 QVersitDocument document(QVersitDocument::VCard21Type);
179 document.setComponentType(QStringLiteral("VCARD"));
180 QVersitProperty embeddedProperty;
181 embeddedProperty.setName(QString(QString::fromLatin1(str: "FN")));
182 embeddedProperty.setValue(QString::fromLatin1(str: "Secret Agent"));
183 document.addProperty(property: embeddedProperty);
184 property.setValue(QVariant::fromValue(value: document));
185 QTest::newRow(dataTag: "AGENT property") << property << expectedResult << codec;
186
187 // Value is base64 encoded.
188 // Check that the extra folding and the line break are added
189 QByteArray value("value");
190 expectedResult = "Springfield.HOUSE.PHOTO;ENCODING=BASE64:\r\n " + value.toBase64() + "\r\n\r\n";
191 QStringList groups(QString::fromLatin1(str: "Springfield"));
192 groups.append(t: QString::fromLatin1(str: "HOUSE"));
193 property.setGroups(groups);
194 property.setParameters(QMultiHash<QString,QString>());
195 property.setName(QString::fromLatin1(str: "PHOTO"));
196 property.setValue(value);
197 QTest::newRow(dataTag: "base64 encoded") << property << expectedResult << codec;
198
199 // Characters other than ASCII:
200 // Note: KATAKANA_NOKIA is defined as: QString::fromUtf8("\xe3\x83\x8e\xe3\x82\xad\xe3\x82\xa2")
201 // The expected behaviour is to convert to UTF8, then encode with quoted-printable.
202 // Because the result overflows one line, it should be split onto two lines using a
203 // quoted-printable soft line break (EQUALS-CR-LF). (Note: Versit soft line breaks
204 // (CR-LF-SPACE) are not supported by the native Symbian vCard importers).
205 expectedResult = "ORG;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:=E3=83=8E=E3=82=AD=E3=82=A2=E3=\r\n"
206 "=83=8E=E3=82=AD=E3=82=A2\r\n";
207 property = QVersitProperty();
208 property.setName(QStringLiteral("ORG"));
209 property.setValue(KATAKANA_NOKIA + KATAKANA_NOKIA);
210 QTest::newRow(dataTag: "non-ASCII 1") << property << expectedResult << codec;
211
212 expectedResult = "ORG;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:a=E3=83=8E=E3=82=AD=E3=82=A2=E3=\r\n"
213 "=83=8E=E3=82=AD=E3=82=A2\r\n";
214 property = QVersitProperty();
215 property.setName(QStringLiteral("ORG"));
216 property.setValue("a" + KATAKANA_NOKIA + KATAKANA_NOKIA);
217 QTest::newRow(dataTag: "non-ASCII 2") << property << expectedResult << codec;
218
219 expectedResult = "ORG;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:aa=E3=83=8E=E3=82=AD=E3=82=A2=\r\n"
220 "=E3=83=8E=E3=82=AD=E3=82=A2\r\n";
221 property = QVersitProperty();
222 property.setName(QStringLiteral("ORG"));
223 property.setValue("aa" + KATAKANA_NOKIA + KATAKANA_NOKIA);
224 QTest::newRow(dataTag: "non-ASCII 3") << property << expectedResult << codec;
225
226 // In Shift-JIS codec.
227 QTextCodec* jisCodec = QTextCodec::codecForName(name: "Shift-JIS");
228 expectedResult = jisCodec->fromUnicode(
229 QStringLiteral("ORG:") + KATAKANA_NOKIA + QStringLiteral("\r\n"));
230 property = QVersitProperty();
231 property.setName(QStringLiteral("ORG"));
232 property.setValue(KATAKANA_NOKIA);
233 QTest::newRow(dataTag: "JIS codec") << property << expectedResult << QByteArray("Shift-JIS");
234}
235
236void tst_QVCard21Writer::testEncodeParameters()
237{
238 QFETCH(StringHash, parameters);
239 QFETCH(QByteArray, expected);
240
241 QByteArray encodedParameters;
242 QBuffer buffer(&encodedParameters);
243 mWriter->setDevice(&buffer);
244 buffer.open(openMode: QIODevice::WriteOnly);
245
246 // No parameters
247 mWriter->encodeParameters(parameters);
248 if (encodedParameters != expected) {
249 qDebug() << "Encoded: " << encodedParameters;
250 qDebug() << "Expected: " << expected;
251 QVERIFY(false);
252 }
253}
254
255void tst_QVCard21Writer::testEncodeParameters_data()
256{
257 QTest::addColumn< QMultiHash<QString, QString> >(name: "parameters");
258 QTest::addColumn<QByteArray>(name: "expected");
259
260 QMultiHash<QString,QString> parameters;
261
262 QTest::newRow(dataTag: "No parameters") << parameters << QByteArray("");
263
264 parameters.insert(QStringLiteral("TYPE"), avalue: QString::fromLatin1(str: "HOME"));
265 QTest::newRow(dataTag: "One TYPE parameter") << parameters << QByteArray(";HOME");
266
267 // HOME should appear before VOICE because it is more "important" and some vCard
268 // parsers may ignore everything after the first TYPE
269 parameters.insert(QStringLiteral("TYPE"), avalue: QString::fromLatin1(str: "VOICE"));
270 QTest::newRow(dataTag: "Two TYPE parameters") << parameters << QByteArray(";HOME;VOICE");
271
272 parameters.clear();
273 parameters.insert(QStringLiteral("ENCODING"), avalue: QString::fromLatin1(str: "8BIT"));
274 QTest::newRow(dataTag: "One ENCODING parameter") << parameters << QByteArray(";ENCODING=8BIT");
275
276 parameters.insert(akey: QString::fromLatin1(str: "X-PARAM"),avalue: QString::fromLatin1(str: "VALUE"));
277 QTest::newRow(dataTag: "Two parameters") << parameters << QByteArray(";ENCODING=8BIT;X-PARAM=VALUE");
278
279 parameters.clear();
280 parameters.insert(QStringLiteral("TYPE"), QStringLiteral("VOICE"));
281 parameters.insert(QStringLiteral("TYPE"), QStringLiteral("CELL"));
282 parameters.insert(QStringLiteral("TYPE"), QStringLiteral("MODEM"));
283 parameters.insert(QStringLiteral("TYPE"), QStringLiteral("CAR"));
284 parameters.insert(QStringLiteral("TYPE"), QStringLiteral("VIDEO"));
285 parameters.insert(QStringLiteral("TYPE"), QStringLiteral("FAX"));
286 parameters.insert(QStringLiteral("TYPE"), QStringLiteral("BBS"));
287 parameters.insert(QStringLiteral("TYPE"), QStringLiteral("PAGER"));
288 parameters.insert(QStringLiteral("TYPE"), QStringLiteral("HOME"));
289 parameters.insert(QStringLiteral("TYPE"), QStringLiteral("WORK"));
290 // Ensure CELL and FAX are at the front because they are "more important" and some vCard
291 // parsers may ignore everything after the first TYPE
292 // Ensure WORK and HOME come next.
293 // Besides these conditions, there are no other ordering constraints. The data here is simply
294 // what the writer produces (as dictated by its internal data structures).
295 QTest::newRow(dataTag: "TYPE parameters order") << parameters
296 << QByteArray(";FAX;CELL;WORK;HOME;PAGER;BBS;VIDEO;CAR;MODEM;VOICE");
297}
298
299void tst_QVCard21Writer::testEncodeGroupsAndName()
300{
301 QVersitProperty property;
302 QByteArray result;
303 QBuffer buffer(&result);
304 mWriter->setDevice(&buffer);
305 buffer.open(openMode: QIODevice::WriteOnly);
306
307 // No groups
308
309 property.setName(QString::fromLatin1(str: "name"));
310 QByteArray expected("NAME");
311 mWriter->encodeGroupsAndName(property);
312 QCOMPARE(result, expected);
313
314 // One group
315 mWriter->writeCrlf(); // so it doesn't start folding
316 buffer.close();
317 result.clear();
318 buffer.open(openMode: QIODevice::WriteOnly);
319 property.setGroups(QStringList(QString::fromLatin1(str: "group")));
320 expected = "group.NAME";
321 mWriter->encodeGroupsAndName(property);
322 QCOMPARE(result, expected);
323
324 // Two groups
325 mWriter->writeCrlf(); // so it doesn't start folding
326 buffer.close();
327 result.clear();
328 buffer.open(openMode: QIODevice::WriteOnly);
329 QStringList groups(QString::fromLatin1(str: "group1"));
330 groups.append(t: QString::fromLatin1(str: "group2"));
331 property.setGroups(groups);
332 expected = "group1.group2.NAME";
333 mWriter->encodeGroupsAndName(property);
334 QCOMPARE(result, expected);
335}
336
337
338void tst_QVCard21Writer::testQuotedPrintableEncode()
339{
340 QByteArray encodedBytes;
341
342 // Nothing to encode
343 QString nothingToEncode(QStringLiteral("nothing to encode"));
344 QVERIFY(!mWriter->quotedPrintableEncode(nothingToEncode));
345
346 // Special characters
347 QString inputOutput(QStringLiteral("\n"));
348 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
349 QCOMPARE(inputOutput, QStringLiteral("=0A"));
350 inputOutput = QStringLiteral("\r");
351 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
352 QCOMPARE(inputOutput, QStringLiteral("=0D"));
353 inputOutput = QStringLiteral("!");
354 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
355 QCOMPARE(inputOutput, QStringLiteral("=21"));
356 inputOutput = QStringLiteral("\"");
357 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
358 QCOMPARE(inputOutput, QStringLiteral("=22"));
359 inputOutput = QStringLiteral("#");
360 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
361 QCOMPARE(inputOutput, QStringLiteral("=23"));
362 inputOutput = QStringLiteral("$");
363 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
364 QCOMPARE(inputOutput, QStringLiteral("=24"));
365 inputOutput = QStringLiteral("=");
366 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
367 QCOMPARE(inputOutput, QStringLiteral("=3D"));
368 inputOutput = QStringLiteral("@");
369 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
370 QCOMPARE(inputOutput, QStringLiteral("=40"));
371 inputOutput = QStringLiteral("[");
372 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
373 QCOMPARE(inputOutput, QStringLiteral("=5B"));
374 inputOutput = QStringLiteral("\\");
375 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
376 QCOMPARE(inputOutput, QStringLiteral("=5C"));
377 inputOutput = QStringLiteral("]");
378 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
379 QCOMPARE(inputOutput, QStringLiteral("=5D"));
380 inputOutput = QStringLiteral("^");
381 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
382 QCOMPARE(inputOutput, QStringLiteral("=5E"));
383 inputOutput = QStringLiteral("`");
384 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
385 QCOMPARE(inputOutput, QStringLiteral("=60"));
386 inputOutput = QStringLiteral("{");
387 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
388 QCOMPARE(inputOutput, QStringLiteral("=7B"));
389 inputOutput = QStringLiteral("|");
390 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
391 QCOMPARE(inputOutput, QStringLiteral("=7C"));
392 inputOutput = QStringLiteral("}");
393 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
394 QCOMPARE(inputOutput, QStringLiteral("=7D"));
395 inputOutput = QStringLiteral("~");
396 QVERIFY(mWriter->quotedPrintableEncode(inputOutput));
397 QCOMPARE(inputOutput, QStringLiteral("=7E"));
398}
399#endif
400
401QTEST_MAIN(tst_QVCard21Writer)
402

source code of qtpim/tests/auto/versit/qvcard21writer/tst_qvcard21writer.cpp