qtmobility/tests/auto/qversitreader/ut_qversitreader.cpp
changeset 4 90517678cc4f
parent 1 2b40d63a9c3d
child 5 453da2cfceef
equal deleted inserted replaced
1:2b40d63a9c3d 4:90517678cc4f
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Mobility Components.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "ut_qversitreader.h"
       
    43 #include "qversitreader.h"
       
    44 #include "qversitreader_p.h"
       
    45 #include "versitutils_p.h"
       
    46 #include <QtTest/QtTest>
       
    47 #include <QSignalSpy>
       
    48 
       
    49 // Copied from tst_qcontactmanager.cpp
       
    50 // Waits until __expr is true and fails if it doesn't happen within 5s.
       
    51 #ifndef QTRY_VERIFY
       
    52 #define QTRY_VERIFY(__expr) \
       
    53         do { \
       
    54         const int __step = 50; \
       
    55         const int __timeout = 5000; \
       
    56         if (!(__expr)) { \
       
    57             QTest::qWait(0); \
       
    58         } \
       
    59         for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \
       
    60             QTest::qWait(__step); \
       
    61         } \
       
    62         QVERIFY(__expr); \
       
    63     } while(0)
       
    64 #endif
       
    65 
       
    66 // This says "NOKIA" in Katakana encoded with UTF-8
       
    67 const QByteArray KATAKANA_NOKIA("\xe3\x83\x8e\xe3\x82\xad\xe3\x82\xa2");
       
    68 
       
    69 QTM_USE_NAMESPACE
       
    70 
       
    71 void UT_QVersitReader::init()
       
    72 {
       
    73     mInputDevice = new QBuffer;
       
    74     mInputDevice->open(QBuffer::ReadWrite);
       
    75     mReader = new QVersitReader;
       
    76     mReaderPrivate = new QVersitReaderPrivate;
       
    77     mSignalCatcher = new SignalCatcher;
       
    78     qRegisterMetaType<QVersitReader::State>("QVersitReader::State");
       
    79     connect(mReader, SIGNAL(stateChanged(QVersitReader::State)),
       
    80             mSignalCatcher, SLOT(stateChanged(QVersitReader::State)));
       
    81     connect(mReader, SIGNAL(resultsAvailable()),
       
    82             mSignalCatcher, SLOT(resultsAvailable()));
       
    83     mAsciiCodec = QTextCodec::codecForName("ISO 8859-1");
       
    84 }
       
    85 
       
    86 void UT_QVersitReader::cleanup()
       
    87 {
       
    88     delete mReaderPrivate;
       
    89     delete mReader;
       
    90     delete mInputDevice;
       
    91     delete mSignalCatcher;
       
    92 }
       
    93 
       
    94 void UT_QVersitReader::testDevice()
       
    95 {
       
    96     // No device
       
    97     QVERIFY(mReader->device() == NULL);
       
    98 
       
    99     // Device has been set
       
   100     mReader->setDevice(mInputDevice);
       
   101     QVERIFY(mReader->device() == mInputDevice);
       
   102 
       
   103     delete mInputDevice;
       
   104     QVERIFY(mReader->device() == NULL);
       
   105 
       
   106     mInputDevice = new QBuffer;
       
   107     mInputDevice->open(QBuffer::ReadWrite);
       
   108 
       
   109     QVERIFY(mReader->device() == NULL);
       
   110     mReader->setDevice(mInputDevice);
       
   111     QVERIFY(mReader->device() == mInputDevice);
       
   112 }
       
   113 
       
   114 void UT_QVersitReader::testDefaultCodec()
       
   115 {
       
   116     QVERIFY(mReader->defaultCodec() == QTextCodec::codecForName("UTF-8"));
       
   117     mReader->setDefaultCodec(QTextCodec::codecForName("UTF-16BE"));
       
   118     QVERIFY(mReader->defaultCodec() == QTextCodec::codecForName("UTF-16BE"));
       
   119 }
       
   120 
       
   121 void UT_QVersitReader::testReading()
       
   122 {
       
   123     // No I/O device set
       
   124     QVERIFY(!mReader->startReading());
       
   125     QCOMPARE(mReader->error(), QVersitReader::IOError);
       
   126 
       
   127     // Device set, no data
       
   128     mReader->setDevice(mInputDevice);
       
   129     mInputDevice->open(QBuffer::ReadOnly);
       
   130     QVERIFY(mReader->startReading());
       
   131     QVERIFY(mReader->waitForFinished());
       
   132     QList<QVersitDocument> results(mReader->results());
       
   133     QCOMPARE(mReader->state(), QVersitReader::FinishedState);
       
   134     QCOMPARE(mReader->error(), QVersitReader::NoError);
       
   135     QCOMPARE(results.count(),0);
       
   136 
       
   137     // Device set, one document
       
   138     const QByteArray& oneDocument =
       
   139         "BEGIN:VCARD\r\nVERSION:2.1\r\nFN:John\r\nEND:VCARD\r\n";
       
   140     mInputDevice->close();
       
   141     mInputDevice->setData(oneDocument);
       
   142     mInputDevice->open(QBuffer::ReadOnly);
       
   143     mInputDevice->seek(0);
       
   144     QVERIFY(mReader->startReading());
       
   145     QVERIFY(mReader->waitForFinished());
       
   146     results = mReader->results();
       
   147     QCOMPARE(mReader->state(), QVersitReader::FinishedState);
       
   148     QCOMPARE(mReader->error(), QVersitReader::NoError);
       
   149     QCOMPARE(results.count(),1);
       
   150 
       
   151     // Wide charset with no byte-order mark
       
   152     QTextCodec* codec = QTextCodec::codecForName("UTF-16BE");
       
   153     const QByteArray& wideDocument =
       
   154             VersitUtils::encode("BEGIN:VCARD\r\nVERSION:2.1\r\nFN:John\r\nEND:VCARD\r\n", codec);
       
   155     mInputDevice->close();
       
   156     mInputDevice->setData(wideDocument);
       
   157     mInputDevice->open(QBuffer::ReadOnly);
       
   158     mInputDevice->seek(0);
       
   159     mReader->setDefaultCodec(codec);
       
   160     QVERIFY(mReader->startReading());
       
   161     QVERIFY(mReader->waitForFinished());
       
   162     results = mReader->results();
       
   163     QCOMPARE(mReader->state(), QVersitReader::FinishedState);
       
   164     QCOMPARE(mReader->error(), QVersitReader::NoError);
       
   165     QCOMPARE(mReader->results().count(),1);
       
   166     mReader->setDefaultCodec(NULL);
       
   167 
       
   168     // Two documents
       
   169     const QByteArray& twoDocuments =
       
   170         " \r\n BEGIN:VCARD\r\nFN:Jenny\r\nEND:VCARD\r\nBEGIN:VCARD\r\nFN:Jake\r\nEND:VCARD\r\n";
       
   171     mInputDevice->close();
       
   172     mInputDevice->setData(twoDocuments);
       
   173     mInputDevice->open(QBuffer::ReadOnly);
       
   174     mInputDevice->seek(0);
       
   175     QVERIFY(mReader->startReading());
       
   176     QVERIFY(mReader->waitForFinished());
       
   177     results = mReader->results();
       
   178     QCOMPARE(mReader->state(), QVersitReader::FinishedState);
       
   179     QCOMPARE(mReader->error(), QVersitReader::NoError);
       
   180     QCOMPARE(results.count(),2);
       
   181 
       
   182     // Erroneous document (missing property name)
       
   183     mInputDevice->close();
       
   184     mInputDevice->setData(QByteArray(
       
   185             "BEGIN:VCARD\r\nFN:Jenny\r\n;Jenny;;;\r\nEND:VCARD\r\n"
       
   186             "BEGIN:VCARD\r\nFN:Jake\r\nEND:VCARD\r\n"));
       
   187     mInputDevice->open(QBuffer::ReadOnly);
       
   188     mInputDevice->seek(0);
       
   189     QVERIFY(mReader->startReading());
       
   190     QVERIFY(mReader->waitForFinished());
       
   191     results = mReader->results();
       
   192     QCOMPARE(mReader->state(), QVersitReader::FinishedState);
       
   193     QCOMPARE(mReader->error(), QVersitReader::ParseError);
       
   194     QCOMPARE(results.count(), 1);
       
   195 
       
   196     // Valid documents and a grouped document between them
       
   197     const QByteArray& validDocumentsAndGroupedDocument =
       
   198 "BEGIN:VCARD\r\nFN:Jenny\r\nEND:VCARD\r\n\
       
   199 BEGIN:VCARD\r\n\
       
   200 X-GROUPING:pub gang\r\n\
       
   201 BEGIN:VCARD\r\nFN:Jeremy\r\nEND:VCARD\r\n\
       
   202 BEGIN:VCARD\r\nFN:Jeffery\r\nEND:VCARD\r\n\
       
   203 END:VCARD\r\n\
       
   204 BEGIN:VCARD\r\nFN:Jake\r\nEND:VCARD\r\n\
       
   205 BEGIN:VCARD\r\nFN:James\r\nEND:VCARD\r\n\
       
   206 BEGIN:VCARD\r\nFN:Jane\r\nEND:VCARD\r\n";
       
   207     mInputDevice->close();
       
   208     mInputDevice->setData(validDocumentsAndGroupedDocument);
       
   209     mInputDevice->open(QBuffer::ReadWrite);
       
   210     mInputDevice->seek(0);
       
   211     QVERIFY(mReader->startReading());
       
   212     QVERIFY(mReader->waitForFinished());
       
   213     results = mReader->results();
       
   214     QCOMPARE(mReader->state(), QVersitReader::FinishedState);
       
   215     // An error is logged because one failed, but the rest are readable.
       
   216     QCOMPARE(mReader->error(), QVersitReader::ParseError);
       
   217     QCOMPARE(results.count(),4);
       
   218 
       
   219     // Valid documents and a grouped document between them
       
   220     const QByteArray& validDocumentsAndGroupedDocument2 =
       
   221 "BEGIN:VCARD\r\nFN:Jenny\r\nEND:VCARD\r\n\
       
   222 BEGIN:VCARD\r\n\
       
   223 X-GROUPING:pub gang\r\n\
       
   224 BEGIN:VCARD\r\nFN:Jeremy\r\nEND:VCARD\r\n\
       
   225 BEGIN:VCARD\r\nFN:Jeffery\r\nEND:VCARD\r\n\
       
   226 END:VCARD\r\n\
       
   227 BEGIN:VCARD\r\nFN:Jake\r\nEND:VCARD\r\n\
       
   228 BEGIN:VCARD\r\nFN:James\r\nEND:VCARD\r\n\
       
   229 BEGIN:VCARD\r\nFN:Jane\r\nEND:VCARD";
       
   230     mInputDevice->close();
       
   231     mInputDevice->setData(validDocumentsAndGroupedDocument2);
       
   232     mInputDevice->open(QBuffer::ReadWrite);
       
   233     mInputDevice->seek(0);
       
   234     QVERIFY(mReader->startReading());
       
   235     QVERIFY(mReader->waitForFinished());
       
   236     results = mReader->results();
       
   237     QCOMPARE(mReader->state(), QVersitReader::FinishedState);
       
   238     // An error is logged because one failed, but the rest are readable.
       
   239     QCOMPARE(mReader->error(), QVersitReader::ParseError);
       
   240     QCOMPARE(mReader->results().count(),4);
       
   241 
       
   242     // Asynchronous reading
       
   243     mInputDevice->close();
       
   244     mInputDevice->setData(twoDocuments);
       
   245     mInputDevice->open(QBuffer::ReadWrite);
       
   246     mInputDevice->seek(0);
       
   247     mSignalCatcher->mStateChanges.clear();
       
   248     mSignalCatcher->mResultsCount = 0;
       
   249     QVERIFY(mReader->startReading());
       
   250     QTRY_VERIFY(mSignalCatcher->mStateChanges.count() >= 2);
       
   251     QCOMPARE(mSignalCatcher->mStateChanges.at(0), QVersitReader::ActiveState);
       
   252     QCOMPARE(mSignalCatcher->mStateChanges.at(1), QVersitReader::FinishedState);
       
   253     QVERIFY(mSignalCatcher->mResultsCount >= 2);
       
   254     QCOMPARE(mReader->results().size(), 2);
       
   255     QCOMPARE(mReader->error(), QVersitReader::NoError);
       
   256 
       
   257     // Cancelling
       
   258     mInputDevice->close();
       
   259     mInputDevice->setData(twoDocuments);
       
   260     mInputDevice->open(QBuffer::ReadOnly);
       
   261     mInputDevice->seek(0);
       
   262     mSignalCatcher->mStateChanges.clear();
       
   263     mSignalCatcher->mResultsCount = 0;
       
   264     QVERIFY(mReader->startReading());
       
   265     mReader->cancel();
       
   266     mReader->waitForFinished();
       
   267     QTRY_VERIFY(mSignalCatcher->mStateChanges.count() >= 2);
       
   268     QCOMPARE(mSignalCatcher->mStateChanges.at(0), QVersitReader::ActiveState);
       
   269     QVersitReader::State state(mSignalCatcher->mStateChanges.at(1));
       
   270     // It's possible that it finishes before it cancels.
       
   271     QVERIFY(state == QVersitReader::CanceledState
       
   272             || state == QVersitReader::FinishedState);
       
   273 }
       
   274 
       
   275 void UT_QVersitReader::testResult()
       
   276 {
       
   277     QCOMPARE(mReader->results().count(),0);
       
   278 }
       
   279 
       
   280 void UT_QVersitReader::testSetVersionFromProperty()
       
   281 {
       
   282     QVersitDocument document;
       
   283 
       
   284     // Some other property than VERSION
       
   285     QVersitProperty property;
       
   286     property.setName(QString::fromAscii("N"));
       
   287     QVERIFY(mReaderPrivate->setVersionFromProperty(document,property));
       
   288 
       
   289     // VERSION property with 2.1
       
   290     property.setName(QString::fromAscii("VERSION"));
       
   291     property.setValue(QString::fromAscii("2.1"));
       
   292     QVERIFY(mReaderPrivate->setVersionFromProperty(document,property));
       
   293     QVERIFY(document.type() == QVersitDocument::VCard21Type);
       
   294 
       
   295     // VERSION property with 3.0
       
   296     property.setValue(QString::fromAscii("3.0"));
       
   297     QVERIFY(mReaderPrivate->setVersionFromProperty(document,property));
       
   298     QVERIFY(document.type() == QVersitDocument::VCard30Type);
       
   299 
       
   300     // VERSION property with a not supported value
       
   301     property.setValue(QString::fromAscii("4.0"));
       
   302     QVERIFY(!mReaderPrivate->setVersionFromProperty(document,property));
       
   303 
       
   304     // VERSION property with BASE64 encoded supported value
       
   305     property.setValue(QString::fromAscii(QByteArray("2.1").toBase64()));
       
   306     property.insertParameter(QString::fromAscii("ENCODING"),QString::fromAscii("BASE64"));
       
   307     QVERIFY(mReaderPrivate->setVersionFromProperty(document,property));
       
   308     QVERIFY(document.type() == QVersitDocument::VCard21Type);
       
   309 
       
   310     // VERSION property with BASE64 encoded not supported value
       
   311     property.setValue(QString::fromAscii(QByteArray("4.0").toBase64()));
       
   312     QVERIFY(!mReaderPrivate->setVersionFromProperty(document,property));
       
   313 }
       
   314 
       
   315 void UT_QVersitReader::testParseNextVersitPropertyVCard21()
       
   316 {
       
   317     QVersitDocument::VersitType type = QVersitDocument::VCard21Type;
       
   318 
       
   319     // Test a valid vCard 2.1 with properties having separate handling:
       
   320     // AGENT property, ENCODING parameters (BASE64 and QUOTED-PRINTABLE) and CHARSET parameter
       
   321     QTextCodec* codec = QTextCodec::codecForName("UTF-16BE");
       
   322     QByteArray vCard("Begin:vcard\r\n");
       
   323     vCard.append("VERSION:2.1\r\n");
       
   324     vCard.append("FN:John\r\n");
       
   325     vCard.append("ORG;CHARSET=UTF-8:");
       
   326     vCard.append(KATAKANA_NOKIA);
       
   327     vCard.append("\r\n");
       
   328     // "NOKIA" in Katakana, UTF-8 encoded, then base-64 encoded:
       
   329     vCard.append("NOTE;ENCODING=BASE64;CHARSET=UTF-8:");
       
   330     vCard.append(KATAKANA_NOKIA.toBase64());
       
   331     vCard.append("\r\n");
       
   332     // The value here is "UXQgaXMgZ3JlYXQh", which is the base64 encoding of "Qt is great!".
       
   333     vCard.append("PHOTO;ENCODING=BASE64: U\t XQgaX MgZ\t3Jl YXQh\r\n\r\n");
       
   334     // Again, but without the explicity "ENCODING" parameter
       
   335     vCard.append("PHOTO;BASE64: U\t XQgaX MgZ\t3Jl YXQh\r\n\r\n");
       
   336     vCard.append("HOME.Springfield.EMAIL;Encoding=Quoted-Printable:john.citizen=40exam=\r\nple.com\r\n");
       
   337     vCard.append("EMAIL;ENCODING=QUOTED-PRINTABLE;CHARSET=UTF-16BE:");
       
   338     vCard.append(codec->fromUnicode(QLatin1String("john.citizen=40exam=\r\nple.com")));
       
   339     vCard.append("\r\n");
       
   340     vCard.append("AGENT:\r\nBEGIN:VCARD\r\nFN:Jenny\r\nEND:VCARD\r\n\r\n");
       
   341     vCard.append("End:VCARD\r\n");
       
   342     QBuffer buffer(&vCard);
       
   343     buffer.open(QIODevice::ReadOnly);
       
   344     LineReader lineReader(&buffer, mAsciiCodec);
       
   345 
       
   346     QVersitProperty property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   347     QCOMPARE(property.name(),QString::fromAscii("BEGIN"));
       
   348     QCOMPARE(property.value(),QString::fromAscii("vcard"));
       
   349 
       
   350     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   351     QCOMPARE(property.name(),QString::fromAscii("VERSION"));
       
   352     QCOMPARE(property.value(),QString::fromAscii("2.1"));
       
   353 
       
   354     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   355     QCOMPARE(property.name(),QString::fromAscii("FN"));
       
   356     QCOMPARE(property.value(),QString::fromAscii("John"));
       
   357 
       
   358     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   359     QCOMPARE(property.name(),QString::fromAscii("ORG"));
       
   360     QCOMPARE(property.value(),QString::fromUtf8(KATAKANA_NOKIA));
       
   361 
       
   362     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   363     QCOMPARE(property.name(),QString::fromAscii("NOTE"));
       
   364     QCOMPARE(property.value(),QString::fromUtf8(KATAKANA_NOKIA));
       
   365 
       
   366     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   367     QCOMPARE(property.name(),QString::fromAscii("PHOTO"));
       
   368     // Linear whitespaces (SPACEs and TABs) removed from the value and base64 decoded:
       
   369     QCOMPARE(property.variantValue().type(), QVariant::ByteArray);
       
   370     QCOMPARE(property.value<QByteArray>(), QByteArray("Qt is great!"));
       
   371     // Ensure that base-64 encoded strings can be retrieved as strings.
       
   372     QCOMPARE(property.value(), QLatin1String("Qt is great!"));
       
   373 
       
   374     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   375     QCOMPARE(property.name(),QString::fromAscii("PHOTO"));
       
   376     QCOMPARE(property.variantValue().type(), QVariant::ByteArray);
       
   377     QCOMPARE(property.value<QByteArray>(), QByteArray("Qt is great!"));
       
   378     QCOMPARE(property.value(), QLatin1String("Qt is great!"));
       
   379 
       
   380     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   381     QStringList propertyGroup(QString::fromAscii("HOME"));
       
   382     propertyGroup.append(QString::fromAscii("Springfield"));
       
   383     QCOMPARE(property.groups(),propertyGroup);
       
   384     QCOMPARE(property.name(),QString::fromAscii("EMAIL"));
       
   385     QCOMPARE(0,property.parameters().count());
       
   386     QCOMPARE(property.value(),QString::fromAscii("john.citizen@example.com"));
       
   387 
       
   388     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   389     QCOMPARE(property.name(),QString::fromAscii("EMAIL"));
       
   390     // The encoding and charset parameters should be stripped by the reader.
       
   391     QCOMPARE(property.parameters().count(), 0);
       
   392     QCOMPARE(property.value(),QString::fromAscii("john.citizen@example.com"));
       
   393 
       
   394     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   395     QCOMPARE(property.name(),QString::fromAscii("AGENT"));
       
   396     QCOMPARE(property.value(),QString());
       
   397     QVERIFY(property.variantValue().userType() == qMetaTypeId<QVersitDocument>());
       
   398     QCOMPARE(property.value<QVersitDocument>().properties().count(), 1);
       
   399 
       
   400     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   401     QCOMPARE(property.name(),QString::fromAscii("END"));
       
   402     QCOMPARE(property.value(),QString::fromAscii("VCARD"));
       
   403 
       
   404     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   405     QCOMPARE(property.name(),QString());
       
   406     QCOMPARE(property.value(),QString());
       
   407 
       
   408     // Simulate a situation where the document nesting level is exceeded
       
   409     // In practice this would mean a big number of nested AGENT properties
       
   410     mReaderPrivate->mDocumentNestingLevel = 20;
       
   411     QByteArray agentProperty("AGENT:BEGIN:VCARD\r\nN:Jenny\r\nEND:VCARD\r\n\r\n");
       
   412     buffer.close();
       
   413     buffer.setData(agentProperty);
       
   414     buffer.open(QIODevice::ReadOnly);
       
   415     LineReader agentLineReader(&buffer, mAsciiCodec);
       
   416 
       
   417     property = mReaderPrivate->parseNextVersitProperty(type, agentLineReader);
       
   418     QVERIFY(property.isEmpty());
       
   419 }
       
   420 
       
   421 void UT_QVersitReader::testParseNextVersitPropertyVCard30()
       
   422 {
       
   423     QVersitDocument::VersitType type = QVersitDocument::VCard30Type;
       
   424 
       
   425     // Test a valid vCard 3.0 with properties having separate handling:
       
   426     // AGENT property and some other property
       
   427     QByteArray vCard("Begin:vcard\r\n");
       
   428     vCard.append("VERSION:3.0\r\n");
       
   429     vCard.append("FN:John\r\n");
       
   430     vCard.append("ORG;CHARSET=UTF-8:");
       
   431     vCard.append(KATAKANA_NOKIA);
       
   432     vCard.append("\r\n");
       
   433     // "NOKIA" in Katakana, UTF-8 encoded, then base-64 encoded:
       
   434     vCard.append("NOTE;ENCODING=B;CHARSET=UTF-8:");
       
   435     vCard.append(KATAKANA_NOKIA.toBase64());
       
   436     vCard.append("\r\n");
       
   437     vCard.append("TEL;TYPE=PREF;HOME:123\r\n");
       
   438     // The value here is "UXQgaXMgZ3JlYXQh", which is the base64 encoding of "Qt is great!".
       
   439     vCard.append("PHOTO;ENCODING=B:UXQgaXMgZ3JlYXQh\r\n");
       
   440     // Again, but without the explicity "ENCODING" parameter
       
   441     vCard.append("PHOTO;B:UXQgaXMgZ3JlYXQh\r\n");
       
   442     vCard.append("EMAIL:john.citizen@example.com\r\n");
       
   443     vCard.append("AGENT:BEGIN:VCARD\\nFN:Jenny\\nEND:VCARD\\n\r\n");
       
   444     vCard.append("End:VCARD\r\n");
       
   445     QBuffer buffer(&vCard);
       
   446     buffer.open(QIODevice::ReadOnly);
       
   447     LineReader lineReader(&buffer, mAsciiCodec);
       
   448 
       
   449     QVersitProperty property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   450     QCOMPARE(property.name(),QString::fromAscii("BEGIN"));
       
   451     QCOMPARE(property.value(),QString::fromAscii("vcard"));
       
   452 
       
   453     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   454     QCOMPARE(property.name(),QString::fromAscii("VERSION"));
       
   455     QCOMPARE(property.value(),QString::fromAscii("3.0"));
       
   456 
       
   457     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   458     QCOMPARE(property.name(),QString::fromAscii("FN"));
       
   459     QCOMPARE(property.value(),QString::fromAscii("John"));
       
   460 
       
   461     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   462     QCOMPARE(property.name(),QString::fromAscii("ORG"));
       
   463     QCOMPARE(property.value(),QString::fromUtf8(KATAKANA_NOKIA));
       
   464 
       
   465     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   466     QCOMPARE(property.name(),QString::fromAscii("NOTE"));
       
   467     QCOMPARE(property.value(),QString::fromUtf8(KATAKANA_NOKIA));
       
   468 
       
   469     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   470     QCOMPARE(property.name(),QString::fromAscii("TEL"));
       
   471     QCOMPARE(property.value(),QString::fromAscii("123"));
       
   472     QCOMPARE(property.parameters().count(), 2);
       
   473 
       
   474     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   475     QCOMPARE(property.name(),QString::fromAscii("PHOTO"));
       
   476     QCOMPARE(property.variantValue().type(), QVariant::ByteArray);
       
   477     QCOMPARE(property.value<QByteArray>(), QByteArray("Qt is great!"));
       
   478     // Ensure that base-64 encoded strings can be retrieved as strings.
       
   479     QCOMPARE(property.value(), QLatin1String("Qt is great!"));
       
   480 
       
   481     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   482     QCOMPARE(property.name(),QString::fromAscii("PHOTO"));
       
   483     QCOMPARE(property.variantValue().type(), QVariant::ByteArray);
       
   484     QCOMPARE(property.value<QByteArray>(), QByteArray("Qt is great!"));
       
   485     QCOMPARE(property.value(), QLatin1String("Qt is great!"));
       
   486 
       
   487     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   488     QCOMPARE(property.name(),QString::fromAscii("EMAIL"));
       
   489     QCOMPARE(0,property.parameters().count());
       
   490     QCOMPARE(property.value(),QString::fromAscii("john.citizen@example.com"));
       
   491 
       
   492     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   493     QCOMPARE(property.name(),QString::fromAscii("AGENT"));
       
   494     QVERIFY(property.variantValue().userType() == qMetaTypeId<QVersitDocument>());
       
   495     QCOMPARE(property.value<QVersitDocument>().properties().count(), 1);
       
   496 
       
   497     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   498     QCOMPARE(property.name(),QString::fromAscii("END"));
       
   499     QCOMPARE(property.value(),QString::fromAscii("VCARD"));
       
   500 
       
   501     property = mReaderPrivate->parseNextVersitProperty(type, lineReader);
       
   502     QCOMPARE(property.name(),QString());
       
   503     QCOMPARE(property.value(),QString());
       
   504 
       
   505     // Simulate a situation where the document nesting level is exceeded
       
   506     // In practice this would mean a big number of nested AGENT properties
       
   507     mReaderPrivate->mDocumentNestingLevel = 20;
       
   508     QByteArray agentProperty("AGENT:BEGIN\\:VCARD\\nFN\\:Jenny\\nEND\\:VCARD\\n\r\n");
       
   509     buffer.close();
       
   510     buffer.setData(agentProperty);
       
   511     buffer.open(QIODevice::ReadOnly);
       
   512     LineReader agentLineReader(&buffer, mAsciiCodec);
       
   513 
       
   514     property = mReaderPrivate->parseNextVersitProperty(type, agentLineReader);
       
   515     QVERIFY(property.isEmpty());
       
   516 }
       
   517 
       
   518 void UT_QVersitReader::testParseVersitDocument()
       
   519 {
       
   520     QFETCH(QByteArray, vCard);
       
   521     QFETCH(bool, expectedSuccess);
       
   522     QFETCH(int, expectedProperties);
       
   523 
       
   524     QBuffer buffer(&vCard);
       
   525     buffer.open(QIODevice::ReadOnly);
       
   526     LineReader lineReader(&buffer, QTextCodec::codecForName("UTF-8"));
       
   527 
       
   528     QVersitDocument document;
       
   529     QCOMPARE(mReaderPrivate->parseVersitDocument(lineReader, document), expectedSuccess);
       
   530     QCOMPARE(document.properties().count(), expectedProperties);
       
   531     QCOMPARE(mReaderPrivate->mDocumentNestingLevel, 0);
       
   532 }
       
   533 
       
   534 void UT_QVersitReader::testParseVersitDocument_data()
       
   535 {
       
   536     QTest::addColumn<QByteArray>("vCard");
       
   537     QTest::addColumn<bool>("expectedSuccess");
       
   538     QTest::addColumn<int>("expectedProperties");
       
   539 
       
   540     QTest::newRow("Basic vCard 2.1")
       
   541             << QByteArray(
       
   542                     "BEGIN:VCARD\r\n"
       
   543                     "VERSION:2.1\r\n"
       
   544                     "FN:John\r\n"
       
   545                     "END:VCARD\r\n")
       
   546             << true
       
   547             << 1;
       
   548 
       
   549     QTest::newRow("vCard 2.1 with Agent")
       
   550             << QByteArray(
       
   551                     "BEGIN:VCARD\r\n"
       
   552                     "VERSION:2.1\r\n"
       
   553                     "FN:John\r\n"
       
   554                     "AGENT:BEGIN:VCARD\r\nN:Jenny\r\nEND:VCARD\r\n\r\n"
       
   555                     "EMAIL;ENCODING=QUOTED-PRINTABLE:john.citizen=40exam=\r\nple.com\r\n"
       
   556                     "END:VCARD\r\n")
       
   557             << true
       
   558             << 3;
       
   559 
       
   560     QTest::newRow("vCard 3.0 with Agent")
       
   561             << QByteArray(
       
   562                     "BEGIN:VCARD\r\n"
       
   563                     "VERSION:3.0\r\n"
       
   564                     "FN:John\r\n"
       
   565                     "AGENT:BEGIN\\:VCARD\\nN\\:Jenny\\nEND\\:VCARD\\n\r\n"
       
   566                     "EMAIL:john.citizen@example.com\r\n"
       
   567                     "END:VCARD\r\n")
       
   568             << true
       
   569             << 3;
       
   570 
       
   571     QTest::newRow("No BEGIN found")
       
   572             << QByteArray(
       
   573                     "VCARD\r\n"
       
   574                     "VERSION:2.1\r\n"
       
   575                     "FN:Nobody\r\n"
       
   576                     "END:VCARD\r\n")
       
   577             << false
       
   578             << 0;
       
   579 
       
   580     QTest::newRow("Wrong card type")
       
   581             << QByteArray(
       
   582                     "BEGIN:VCAL\r\n"
       
   583                     "END:VCAL\r\n")
       
   584             << false
       
   585             << 0;
       
   586 
       
   587     QTest::newRow("Wrong version")
       
   588             << QByteArray(
       
   589                     "BEGIN:VCARD\r\n"
       
   590                     "VERSION:4.0\r\n"
       
   591                     "FN:Nobody\r\n"
       
   592                     "END:VCARD\r\n")
       
   593             << false
       
   594             << 0;
       
   595 
       
   596     QTest::newRow("No trailing crlf")
       
   597             << QByteArray(
       
   598                     "BEGIN:VCARD\r\n"
       
   599                     "VERSION:2.1\r\n"
       
   600                     "FN:Nobody\r\n"
       
   601                     "END:VCARD")
       
   602             << true
       
   603             << 1;
       
   604 
       
   605     QTest::newRow("No end")
       
   606             << QByteArray(
       
   607                     "BEGIN:VCARD\r\n"
       
   608                     "VERSION:2.1\r\n"
       
   609                     "FN:Nobody\r\n")
       
   610             << false
       
   611             << 0;
       
   612 
       
   613     QTest::newRow("Grouped vCards are not supported. The whole vCard will be discarded.")
       
   614             << QByteArray(
       
   615                     "BEGIN:VCARD\r\n"
       
   616                     "VERSION:2.1\r\n"
       
   617                     "X-EXAMPLES:Family vCard\r\n"
       
   618                     "BEGIN:VCARD\r\n"
       
   619                     "VERSION:2.1\r\n"
       
   620                     "N:Citizen;John\r\n"
       
   621                     "TEL;CELL:1111\r\n"
       
   622                     "EMAIL;ENCODING=QUOTED-PRINTABLE:john.citizen=40example.com\r\n"
       
   623                     "END:VCARD\r\n"
       
   624                     "BEGIN:VCARD\r\n"
       
   625                     "VERSION:2.1\r\n"
       
   626                     "N:Citizen;Jenny\r\n"
       
   627                     "TEL;CELL:7777\r\n"
       
   628                     "END:VCARD\r\n"
       
   629                     "END:VCARD")
       
   630             << false
       
   631             << 0;
       
   632 }
       
   633 
       
   634 void UT_QVersitReader::testDecodeQuotedPrintable()
       
   635 {
       
   636     // Soft line breaks
       
   637     QString encoded(QLatin1String("This=\r\n is =\r\none line."));
       
   638     QString decoded(QLatin1String("This is one line."));
       
   639     mReaderPrivate->decodeQuotedPrintable(encoded);
       
   640     QCOMPARE(encoded, decoded);
       
   641 
       
   642     // Characters recommended to be encoded according to RFC 1521:
       
   643     encoded = QLatin1String("To be decoded: =0A=0D=21=22=23=24=3D=40=5B=5C=5D=5E=60=7B=7C=7D=7E");
       
   644     decoded = QLatin1String("To be decoded: \n\r!\"#$=@[\\]^`{|}~");
       
   645     mReaderPrivate->decodeQuotedPrintable(encoded);
       
   646     QCOMPARE(encoded, decoded);
       
   647 
       
   648     // Other random characters encoded.
       
   649     // Some implementation may encode these too, as it is allowed.
       
   650     encoded = QLatin1String("=45=6E=63=6F=64=65=64 =64=61=74=61");
       
   651     decoded = QLatin1String("Encoded data");
       
   652     mReaderPrivate->decodeQuotedPrintable(encoded);
       
   653     QCOMPARE(encoded, decoded);
       
   654 }
       
   655 void UT_QVersitReader::testParamName()
       
   656 {
       
   657     // Empty value
       
   658     QByteArray param;
       
   659     QCOMPARE(mReaderPrivate->paramName(param, mAsciiCodec),QString());
       
   660 
       
   661     // Only value present
       
   662     param = "WORK";
       
   663     QCOMPARE(mReaderPrivate->paramName(param, mAsciiCodec),
       
   664              QString::fromAscii("TYPE"));
       
   665 
       
   666     // The below tests intentionally use the misspelling TIPE to avoid the default behaviour of
       
   667     // returning TYPE when the name can't be parsed.
       
   668     // Both name and value, spaces after the name
       
   669     param = "TIPE \t =WORK";
       
   670     QCOMPARE(mReaderPrivate->paramName(param, mAsciiCodec),
       
   671              QString::fromAscii("TIPE"));
       
   672 
       
   673     // Both name and value, no spaces after the name
       
   674     param = "TIPE=WORK";
       
   675     QCOMPARE(mReaderPrivate->paramName(param, mAsciiCodec),
       
   676              QString::fromAscii("TIPE"));
       
   677 
       
   678     // Test wide character support.
       
   679     QTextCodec* codec = QTextCodec::codecForName("UTF-16BE");
       
   680     param = codec->fromUnicode(QString::fromAscii("TIPE=WORK"));
       
   681     QCOMPARE(mReaderPrivate->paramName(param, codec),
       
   682              QString::fromAscii("TIPE"));
       
   683 
       
   684 }
       
   685 
       
   686 void UT_QVersitReader::testParamValue()
       
   687 {
       
   688     // Empty value
       
   689     QByteArray param;
       
   690     QCOMPARE(mReaderPrivate->paramValue(param, mAsciiCodec),QString());
       
   691 
       
   692     // Only value present
       
   693     param = "WORK";
       
   694     QCOMPARE(mReaderPrivate->paramValue(param, mAsciiCodec),
       
   695              QString::fromAscii("WORK"));
       
   696 
       
   697     // Name and equals sign, but no value
       
   698     param = "TYPE=";
       
   699     QCOMPARE(mReaderPrivate->paramValue(param, mAsciiCodec),QString());
       
   700 
       
   701     // Name and equals sign, but value has only spaces
       
   702     param = "TYPE=  \t ";
       
   703     QCOMPARE(mReaderPrivate->paramValue(param, mAsciiCodec),QString());
       
   704 
       
   705     // Both name and value, spaces before the value
       
   706     param = "TYPE= \t  WORK";
       
   707     QCOMPARE(mReaderPrivate->paramValue(param, mAsciiCodec),
       
   708              QString::fromAscii("WORK"));
       
   709 
       
   710     // Both name and value, no spaces before the value
       
   711     param = "ENCODING=QUOTED-PRINTABLE";
       
   712     QCOMPARE(mReaderPrivate->paramValue(param, mAsciiCodec),
       
   713              QString::fromAscii("QUOTED-PRINTABLE"));
       
   714 
       
   715     // Test wide character support.
       
   716     QTextCodec* codec = QTextCodec::codecForName("UTF-16BE");
       
   717     param = codec->fromUnicode(QString::fromAscii("TYPE=WORK"));
       
   718     QCOMPARE(mReaderPrivate->paramValue(param, codec),
       
   719              QString::fromAscii("WORK"));
       
   720 }
       
   721 
       
   722 void UT_QVersitReader::testExtractPart()
       
   723 {
       
   724     QByteArray originalStr;
       
   725 
       
   726     // Negative starting position
       
   727     QCOMPARE(mReaderPrivate->extractPart(originalStr,-1,1), QByteArray());
       
   728 
       
   729     // Empty original string
       
   730     QCOMPARE(mReaderPrivate->extractPart(originalStr,0,1), QByteArray());
       
   731 
       
   732     // Trimmed substring empty
       
   733     originalStr = " \t \t";
       
   734     QCOMPARE(mReaderPrivate->extractPart(originalStr,0,4), QByteArray());
       
   735 
       
   736     // The given substring length is greater than the original string length
       
   737     originalStr = "ENCODING=7BIT";
       
   738     QCOMPARE(mReaderPrivate->extractPart(originalStr,0,100), originalStr);
       
   739 
       
   740     // Non-empty substring, from the beginning
       
   741     originalStr = " TYPE=WORK ; X-PARAM=X-VALUE; ENCODING=8BIT";
       
   742     QCOMPARE(mReaderPrivate->extractPart(originalStr,0,11),
       
   743              QByteArray("TYPE=WORK"));
       
   744 
       
   745     // Non-empty substring, from the middle
       
   746     QCOMPARE(mReaderPrivate->extractPart(originalStr,12,16),
       
   747              QByteArray("X-PARAM=X-VALUE"));
       
   748 
       
   749     // Non-empty substring, from the middle to the end
       
   750     QCOMPARE(mReaderPrivate->extractPart(originalStr,29),
       
   751              QByteArray("ENCODING=8BIT"));
       
   752 }
       
   753 
       
   754 void UT_QVersitReader::testExtractParts()
       
   755 {
       
   756     QList<QByteArray> parts;
       
   757 
       
   758     // Empty value
       
   759     QByteArray text;
       
   760     parts = mReaderPrivate->extractParts(text,";", mAsciiCodec);
       
   761     QVERIFY(parts.isEmpty());
       
   762 
       
   763     // Only separator
       
   764     text = ";";
       
   765     parts = mReaderPrivate->extractParts(text,";", mAsciiCodec);
       
   766     QVERIFY(parts.isEmpty());
       
   767 
       
   768     // One part
       
   769     text = "part";
       
   770     parts = mReaderPrivate->extractParts(text,";", mAsciiCodec);
       
   771     QCOMPARE(parts.count(),1);
       
   772     QCOMPARE(QString::fromAscii(parts[0]),QString::fromAscii("part"));
       
   773 
       
   774     // Separator in the beginning, one part
       
   775     text = ";part";
       
   776     parts = mReaderPrivate->extractParts(text,";", mAsciiCodec);
       
   777     QCOMPARE(parts.count(),1);
       
   778     QCOMPARE(QString::fromAscii(parts[0]),QString::fromAscii("part"));
       
   779 
       
   780     // Separator in the end, one part
       
   781     text = "part;";
       
   782     parts = mReaderPrivate->extractParts(text,";", mAsciiCodec);
       
   783     QCOMPARE(parts.count(),1);
       
   784     QCOMPARE(QString::fromAscii(parts[0]),QString::fromAscii("part"));
       
   785 
       
   786     // One part that contains escaped separator
       
   787     text = "part\\;";
       
   788     parts = mReaderPrivate->extractParts(text,";", mAsciiCodec);
       
   789     QCOMPARE(parts.count(),1);
       
   790     QCOMPARE(QString::fromAscii(parts[0]),QString::fromAscii("part\\;"));
       
   791 
       
   792     // Two parts
       
   793     text = "part1;part2";
       
   794     parts = mReaderPrivate->extractParts(text,";", mAsciiCodec);
       
   795     QCOMPARE(parts.count(),2);
       
   796     QCOMPARE(QString::fromAscii(parts[0]),QString::fromAscii("part1"));
       
   797     QCOMPARE(QString::fromAscii(parts[1]),QString::fromAscii("part2"));
       
   798 
       
   799     // Two parts that contain escaped separators
       
   800     text = "pa\\;rt1;par\\;t2";
       
   801     parts = mReaderPrivate->extractParts(text,";", mAsciiCodec);
       
   802     QCOMPARE(parts.count(),2);
       
   803     QCOMPARE(QString::fromAscii(parts[0]),QString::fromAscii("pa\\;rt1"));
       
   804     QCOMPARE(QString::fromAscii(parts[1]),QString::fromAscii("par\\;t2"));
       
   805 
       
   806     // Test wide character support
       
   807     QTextCodec* codec = QTextCodec::codecForName("UTF-16BE");
       
   808     text = codec->fromUnicode(QString::fromAscii("part1;part2"));
       
   809     parts = mReaderPrivate->extractParts(text,";", codec);
       
   810     QCOMPARE(parts.count(),2);
       
   811     QCOMPARE(codec->toUnicode(parts[0]),QString::fromAscii("part1"));
       
   812     QCOMPARE(codec->toUnicode(parts[1]),QString::fromAscii("part2"));
       
   813 }
       
   814 
       
   815 void UT_QVersitReader::testExtractPropertyGroupsAndName()
       
   816 {
       
   817     QPair<QStringList,QString> groupsAndName;
       
   818 
       
   819     // Empty string
       
   820     VersitCursor cursor(QByteArray(" "));
       
   821     groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(cursor, mAsciiCodec);
       
   822     QCOMPARE(groupsAndName.first.count(),0);
       
   823     QCOMPARE(groupsAndName.second,QString());
       
   824 
       
   825     // No value -> returns empty string and no groups
       
   826     QByteArray property("TEL");
       
   827     cursor.setData(property);
       
   828     cursor.selection = property.size();
       
   829     groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(cursor, mAsciiCodec);
       
   830     QCOMPARE(groupsAndName.first.count(),0);
       
   831     QCOMPARE(groupsAndName.second,QString());
       
   832 
       
   833     // Simple name and value
       
   834     property = "TEL:123";
       
   835     cursor.setData(property);
       
   836     cursor.selection = property.size();
       
   837     groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(cursor, mAsciiCodec);
       
   838     QCOMPARE(groupsAndName.first.count(),0);
       
   839     QCOMPARE(groupsAndName.second,QString::fromAscii("TEL"));
       
   840 
       
   841     // One whitespace before colon
       
   842     property = "TEL :123";
       
   843     cursor.setData(property);
       
   844     cursor.selection = property.size();
       
   845     groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(cursor, mAsciiCodec);
       
   846     QCOMPARE(groupsAndName.first.count(),0);
       
   847     QCOMPARE(groupsAndName.second,QString::fromAscii("TEL"));
       
   848 
       
   849     // Several whitespaces before colon
       
   850     property = "TEL \t  :123";
       
   851     cursor.setData(property);
       
   852     cursor.selection = property.size();
       
   853     groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(cursor, mAsciiCodec);
       
   854     QCOMPARE(groupsAndName.first.count(),0);
       
   855     QCOMPARE(groupsAndName.second,QString::fromAscii("TEL"));
       
   856 
       
   857     // Name contains a group
       
   858     property = "group1.TEL:1234";
       
   859     cursor.setData(property);
       
   860     cursor.selection = property.size();
       
   861     groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(cursor, mAsciiCodec);
       
   862     QCOMPARE(groupsAndName.first.count(),1);
       
   863     QCOMPARE(groupsAndName.first.takeFirst(),QString::fromAscii("group1"));
       
   864     QCOMPARE(groupsAndName.second,QString::fromAscii("TEL"));
       
   865 
       
   866     // Name contains more than one group
       
   867     property = "group1.group2.TEL:12345";
       
   868     cursor.setData(property);
       
   869     cursor.selection = property.size();
       
   870     groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(cursor, mAsciiCodec);
       
   871     QCOMPARE(groupsAndName.first.count(),2);
       
   872     QCOMPARE(groupsAndName.first.takeFirst(),QString::fromAscii("group1"));
       
   873     QCOMPARE(groupsAndName.first.takeFirst(),QString::fromAscii("group2"));
       
   874     QCOMPARE(groupsAndName.second,QString::fromAscii("TEL"));
       
   875     QCOMPARE(cursor.position, 17);
       
   876 
       
   877     // Property contains one parameter
       
   878     property = "TEL;WORK:123";
       
   879     cursor.setData(property);
       
   880     cursor.selection = property.size();
       
   881     groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(cursor, mAsciiCodec);
       
   882     QCOMPARE(groupsAndName.first.count(),0);
       
   883     QCOMPARE(groupsAndName.second,QString::fromAscii("TEL"));
       
   884 
       
   885     // Property contains several parameters
       
   886     property = "EMAIL;INTERNET;ENCODING=QUOTED-PRINTABLE:user=40ovi.com";
       
   887     cursor.setData(property);
       
   888     cursor.selection = property.size();
       
   889     groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(cursor, mAsciiCodec);
       
   890     QCOMPARE(groupsAndName.first.count(),0);
       
   891     QCOMPARE(groupsAndName.second,QString::fromAscii("EMAIL"));
       
   892 
       
   893     // Name contains an escaped semicolon
       
   894     property = "X-proper\\;ty:value";
       
   895     cursor.setData(property);
       
   896     cursor.selection = property.size();
       
   897     groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(cursor, mAsciiCodec);
       
   898     QCOMPARE(groupsAndName.first.count(),0);
       
   899     QCOMPARE(groupsAndName.second,QString::fromAscii("X-proper\\;ty"));
       
   900 
       
   901     // Test wide character support
       
   902     QTextCodec* codec = QTextCodec::codecForName("UTF-16BE");
       
   903     property = codec->fromUnicode(QString::fromAscii("group1.group2.TEL;WORK:123"));
       
   904     cursor.setData(property);
       
   905     cursor.selection = property.size();
       
   906     groupsAndName = mReaderPrivate->extractPropertyGroupsAndName(cursor, codec);
       
   907     QCOMPARE(groupsAndName.first.count(),2);
       
   908     QCOMPARE(groupsAndName.first.takeFirst(),QString::fromAscii("group1"));
       
   909     QCOMPARE(groupsAndName.first.takeFirst(),QString::fromAscii("group2"));
       
   910     QCOMPARE(groupsAndName.second,QString::fromAscii("TEL"));
       
   911     QCOMPARE(cursor.position, 36); // 2 bytes * 17 characters + 2 byte BOM.
       
   912 
       
   913 }
       
   914 
       
   915 void UT_QVersitReader::testExtractVCard21PropertyParams()
       
   916 {
       
   917     // No parameters
       
   918     VersitCursor cursor(QByteArray(":123"));
       
   919     cursor.setSelection(cursor.data.size());
       
   920     QCOMPARE(mReaderPrivate->extractVCard21PropertyParams(cursor, mAsciiCodec).count(), 0);
       
   921 
       
   922     // "Empty" parameter
       
   923     cursor.setData(QByteArray(";:123"));
       
   924     cursor.setSelection(cursor.data.size());
       
   925     QCOMPARE(mReaderPrivate->extractVCard21PropertyParams(cursor, mAsciiCodec).count(), 0);
       
   926 
       
   927     // Semicolon found, but no value for the property
       
   928     cursor.setData(QByteArray(";TYPE=X-TYPE"));
       
   929     cursor.setSelection(cursor.data.size());
       
   930     QCOMPARE(mReaderPrivate->extractVCard21PropertyParams(cursor, mAsciiCodec).count(), 0);
       
   931 
       
   932     // The property name contains an escaped semicolon, no parameters
       
   933     cursor.setData(QByteArray(":value"));
       
   934     cursor.setSelection(cursor.data.size());
       
   935     QCOMPARE(mReaderPrivate->extractVCard21PropertyParams(cursor, mAsciiCodec).count(), 0);
       
   936 
       
   937     // The property value contains a semicolon, no parameters
       
   938     cursor.setData(QByteArray(":va;lue"));
       
   939     cursor.setSelection(cursor.data.size());
       
   940     QCOMPARE(mReaderPrivate->extractVCard21PropertyParams(cursor, mAsciiCodec).count(), 0);
       
   941 
       
   942     // One parameter
       
   943     cursor.setData(QByteArray(";HOME:123"));
       
   944     cursor.setSelection(cursor.data.size());
       
   945     QMultiHash<QString,QString> params = mReaderPrivate->extractVCard21PropertyParams(cursor,
       
   946                                                                                    mAsciiCodec);
       
   947     QCOMPARE(1, params.count());
       
   948     QCOMPARE(1, params.values(QString::fromAscii("TYPE")).count());
       
   949     QCOMPARE(params.values(QString::fromAscii("TYPE"))[0],QString::fromAscii("HOME"));
       
   950 
       
   951     // Two parameters of the same type
       
   952     cursor.setData(QByteArray(";HOME;VOICE:123"));
       
   953     cursor.setSelection(cursor.data.size());
       
   954     params = mReaderPrivate->extractVCard21PropertyParams(cursor, mAsciiCodec);
       
   955     QCOMPARE(2, params.count());
       
   956     QCOMPARE(2, params.values(QString::fromAscii("TYPE")).count());
       
   957     QCOMPARE(params.values(QString::fromAscii("TYPE"))[0],QString::fromAscii("HOME"));
       
   958     QCOMPARE(params.values(QString::fromAscii("TYPE"))[1],QString::fromAscii("VOICE"));
       
   959 
       
   960     // Two parameters, several empty parameters (extra semicolons)
       
   961     cursor.setData(QByteArray(";;;;HOME;;;;;VOICE;;;:123"));
       
   962     cursor.setSelection(cursor.data.size());
       
   963     params = mReaderPrivate->extractVCard21PropertyParams(cursor, mAsciiCodec);
       
   964     QCOMPARE(2, params.count());
       
   965     QCOMPARE(2, params.values(QString::fromAscii("TYPE")).count());
       
   966     QCOMPARE(params.values(QString::fromAscii("TYPE"))[0],QString::fromAscii("HOME"));
       
   967     QCOMPARE(params.values(QString::fromAscii("TYPE"))[1],QString::fromAscii("VOICE"));
       
   968 
       
   969     // Two parameters with different types
       
   970     cursor.setData(QByteArray(";INTERNET;ENCODING=QUOTED-PRINTABLE:user=40ovi.com"));
       
   971     cursor.setSelection(cursor.data.size());
       
   972     params.clear();
       
   973     params = mReaderPrivate->extractVCard21PropertyParams(cursor, mAsciiCodec);
       
   974     QCOMPARE(2, params.count());
       
   975     QList<QString> typeParams = params.values(QString::fromAscii("TYPE"));
       
   976     QCOMPARE(1, typeParams.count());
       
   977     QCOMPARE(typeParams[0],QString::fromAscii("INTERNET"));
       
   978     QList<QString> encodingParams = params.values(QString::fromAscii("ENCODING"));
       
   979     QCOMPARE(1, encodingParams.count());
       
   980     QCOMPARE(encodingParams[0],QString::fromAscii("QUOTED-PRINTABLE"));
       
   981 
       
   982     // Test wide character support.
       
   983     QTextCodec* codec = QTextCodec::codecForName("UTF-16BE");
       
   984     QByteArray data = VersitUtils::encode(";HOME;CHARSET=UTF-16:123", codec);
       
   985     cursor.setData(data);
       
   986     cursor.setSelection(cursor.data.size());
       
   987     params = mReaderPrivate->extractVCard21PropertyParams(cursor, codec);
       
   988     QCOMPARE(2, params.count());
       
   989     typeParams = params.values(QString::fromAscii("TYPE"));
       
   990     QCOMPARE(1, typeParams.count());
       
   991     QCOMPARE(typeParams[0],QString::fromAscii("HOME"));
       
   992     encodingParams = params.values(QString::fromAscii("CHARSET"));
       
   993     QCOMPARE(1, encodingParams.count());
       
   994     QCOMPARE(encodingParams[0],QString::fromAscii("UTF-16"));
       
   995 }
       
   996 
       
   997 void UT_QVersitReader::testExtractVCard30PropertyParams()
       
   998 {
       
   999     // No parameters
       
  1000     VersitCursor cursor(QByteArray(":123"));
       
  1001     cursor.setSelection(cursor.data.size());
       
  1002     QCOMPARE(mReaderPrivate->extractVCard30PropertyParams(cursor, mAsciiCodec).count(), 0);
       
  1003 
       
  1004     // One parameter
       
  1005     cursor.setData(QByteArray(";TYPE=HOME:123"));
       
  1006     cursor.setSelection(cursor.data.size());
       
  1007     QMultiHash<QString,QString> params = mReaderPrivate->extractVCard30PropertyParams(cursor,
       
  1008                                                                                    mAsciiCodec);
       
  1009     QCOMPARE(params.count(), 1);
       
  1010     QCOMPARE(params.values(QString::fromAscii("TYPE")).count(), 1);
       
  1011     QCOMPARE(params.values(QString::fromAscii("TYPE"))[0], QString::fromAscii("HOME"));
       
  1012 
       
  1013     // One parameter with an escaped semicolon
       
  1014     cursor.setData(QByteArray(";para\\;meter:value"));
       
  1015     cursor.setSelection(cursor.data.size());
       
  1016     params = mReaderPrivate->extractVCard30PropertyParams(cursor, mAsciiCodec);
       
  1017     QCOMPARE(params.count(), 1);
       
  1018     QCOMPARE(params.values(QString::fromAscii("TYPE")).count(), 1);
       
  1019     QCOMPARE(params.values(QString::fromAscii("TYPE"))[0], QString::fromAscii("para;meter"));
       
  1020 
       
  1021     // One parameter with and escaped comma in the name and the value
       
  1022     cursor.setData(QByteArray(";X-PA\\,RAM=VAL\\,UE:123"));
       
  1023     cursor.setSelection(cursor.data.size());
       
  1024     params = mReaderPrivate->extractVCard30PropertyParams(cursor, mAsciiCodec);
       
  1025     QCOMPARE(params.count(), 1);
       
  1026     QCOMPARE(params.values(QString::fromAscii("X-PA,RAM")).count(), 1);
       
  1027     QCOMPARE(params.values(QString::fromAscii("X-PA,RAM"))[0], QString::fromAscii("VAL,UE"));
       
  1028 
       
  1029     // Two parameters of the same type
       
  1030     cursor.setData(QByteArray(";TYPE=HOME,VOICE:123"));
       
  1031     cursor.setSelection(cursor.data.size());
       
  1032     params = mReaderPrivate->extractVCard30PropertyParams(cursor, mAsciiCodec);
       
  1033     QCOMPARE(params.count(), 2);
       
  1034     QCOMPARE(params.values(QString::fromAscii("TYPE")).count(), 2);
       
  1035     QVERIFY(params.values(QString::fromAscii("TYPE")).contains(QString::fromAscii("HOME")));
       
  1036     QVERIFY(params.values(QString::fromAscii("TYPE")).contains(QString::fromAscii("VOICE")));
       
  1037 
       
  1038     // Two parameters of the same type in separate name-values
       
  1039     cursor.setData(QByteArray(";TYPE=HOME;TYPE=VOICE:123"));
       
  1040     cursor.setSelection(cursor.data.size());
       
  1041     params = mReaderPrivate->extractVCard30PropertyParams(cursor, mAsciiCodec);
       
  1042     QCOMPARE(params.count(), 2);
       
  1043     QCOMPARE(params.values(QString::fromAscii("TYPE")).count(), 2);
       
  1044     QVERIFY(params.values(QString::fromAscii("TYPE")).contains(QString::fromAscii("HOME")));
       
  1045     QVERIFY(params.values(QString::fromAscii("TYPE")).contains(QString::fromAscii("VOICE")));
       
  1046 
       
  1047     // Three parameters of the same type
       
  1048     cursor.setData(QByteArray(";TYPE=PREF,HOME,VOICE:123"));
       
  1049     cursor.setSelection(cursor.data.size());
       
  1050     params = mReaderPrivate->extractVCard30PropertyParams(cursor, mAsciiCodec);
       
  1051     QCOMPARE(params.count(), 3);
       
  1052     QCOMPARE(params.values(QString::fromAscii("TYPE")).count(), 3);
       
  1053     QVERIFY(params.values(QString::fromAscii("TYPE")).contains(QString::fromAscii("PREF")));
       
  1054     QVERIFY(params.values(QString::fromAscii("TYPE")).contains(QString::fromAscii("HOME")));
       
  1055     QVERIFY(params.values(QString::fromAscii("TYPE")).contains(QString::fromAscii("VOICE")));
       
  1056 
       
  1057     // Two parameters with different types
       
  1058     cursor.setData(QByteArray(";TYPE=HOME;X-PARAM=X-VALUE:Home Street 1"));
       
  1059     cursor.setSelection(cursor.data.size());
       
  1060     params.clear();
       
  1061     params = mReaderPrivate->extractVCard30PropertyParams(cursor, mAsciiCodec);
       
  1062     QCOMPARE(params.count(), 2);
       
  1063     QList<QString> typeParams = params.values(QString::fromAscii("TYPE"));
       
  1064     QCOMPARE(typeParams.count(), 1);
       
  1065     QCOMPARE(typeParams[0],QString::fromAscii("HOME"));
       
  1066     QList<QString> encodingParams = params.values(QString::fromAscii("X-PARAM"));
       
  1067     QCOMPARE(encodingParams.count(), 1);
       
  1068     QCOMPARE(encodingParams[0],QString::fromAscii("X-VALUE"));
       
  1069 
       
  1070     // Test wide character support.
       
  1071     QTextCodec* codec = QTextCodec::codecForName("UTF-16BE");
       
  1072     QByteArray data = VersitUtils::encode(";TIPE=HOME,VOICE;CHARSET=UTF-16:123", codec);
       
  1073     cursor.setData(data);
       
  1074     cursor.setSelection(cursor.data.size());
       
  1075     params = mReaderPrivate->extractVCard30PropertyParams(cursor, codec);
       
  1076     QCOMPARE(params.count(), 3);
       
  1077     typeParams = params.values(QString::fromAscii("TIPE"));
       
  1078     QCOMPARE(params.values(QString::fromAscii("TIPE")).count(), 2);
       
  1079     QVERIFY(params.values(QString::fromAscii("TIPE")).contains(QString::fromAscii("HOME")));
       
  1080     QVERIFY(params.values(QString::fromAscii("TIPE")).contains(QString::fromAscii("VOICE")));
       
  1081     encodingParams = params.values(QString::fromAscii("CHARSET"));
       
  1082     QCOMPARE(1, encodingParams.count());
       
  1083     QCOMPARE(encodingParams[0],QString::fromAscii("UTF-16"));
       
  1084 }
       
  1085 
       
  1086 void UT_QVersitReader::testExtractParams()
       
  1087 {
       
  1088     VersitCursor cursor;
       
  1089     QByteArray data = ":123";
       
  1090     cursor.setData(data);
       
  1091     cursor.setPosition(0);
       
  1092     cursor.setSelection(cursor.data.size());
       
  1093     QList<QByteArray> params = mReaderPrivate->extractParams(cursor, mAsciiCodec);
       
  1094     QCOMPARE(params.size(), 0);
       
  1095     QCOMPARE(cursor.position, 1);
       
  1096 
       
  1097     data = "a;b:123";
       
  1098     cursor.setData(data);
       
  1099     cursor.setPosition(0);
       
  1100     cursor.setSelection(cursor.data.size());
       
  1101     params = mReaderPrivate->extractParams(cursor, mAsciiCodec);
       
  1102     QCOMPARE(params.size(), 2);
       
  1103     QCOMPARE(cursor.position, 4);
       
  1104     QCOMPARE(params.at(0), QByteArray("a"));
       
  1105     QCOMPARE(params.at(1), QByteArray("b"));
       
  1106 
       
  1107     QTextCodec* codec = QTextCodec::codecForName("UTF-16BE");
       
  1108     data = VersitUtils::encode(":123", codec);
       
  1109     cursor.setData(data);
       
  1110     cursor.setPosition(0);
       
  1111     cursor.setSelection(cursor.data.size());
       
  1112     params = mReaderPrivate->extractParams(cursor, codec);
       
  1113     QCOMPARE(params.size(), 0);
       
  1114     QCOMPARE(cursor.position, 2);
       
  1115 
       
  1116     data = VersitUtils::encode("a;b:123", codec);
       
  1117     cursor.setData(data);
       
  1118     cursor.setPosition(0);
       
  1119     cursor.setSelection(cursor.data.size());
       
  1120     params = mReaderPrivate->extractParams(cursor, codec);
       
  1121     QCOMPARE(params.size(), 2);
       
  1122     QCOMPARE(cursor.position, 8);
       
  1123 
       
  1124 }
       
  1125 
       
  1126 Q_DECLARE_METATYPE(QList<QString>)
       
  1127 
       
  1128 void UT_QVersitReader::testReadLine()
       
  1129 {
       
  1130     QFETCH(QByteArray, codecName);
       
  1131     QFETCH(QString, data);
       
  1132     QFETCH(QList<QString>, expectedLines);
       
  1133 
       
  1134     QTextCodec* codec = QTextCodec::codecForName(codecName);
       
  1135     QTextEncoder* encoder = codec->makeEncoder();
       
  1136     encoder->fromUnicode(QString());
       
  1137 
       
  1138     QByteArray bytes(encoder->fromUnicode(data));
       
  1139 
       
  1140     mInputDevice->close();
       
  1141     mInputDevice->setData(bytes);
       
  1142     mInputDevice->open(QIODevice::ReadWrite);
       
  1143 
       
  1144     LineReader lineReader(mInputDevice, codec, 10);
       
  1145 
       
  1146     // Check that all expected lines are read.
       
  1147     foreach (QString expectedLine, expectedLines) {
       
  1148         QByteArray expectedBytes(encoder->fromUnicode(expectedLine));
       
  1149         QVERIFY(!lineReader.atEnd());
       
  1150         VersitCursor line = lineReader.readLine();
       
  1151         QVERIFY(line.data.indexOf(expectedBytes) == line.position);
       
  1152         QCOMPARE(line.selection - line.position, expectedBytes.length());
       
  1153     }
       
  1154     // And that there are no more lines
       
  1155     VersitCursor line = lineReader.readLine();
       
  1156     QCOMPARE(line.selection, line.position);
       
  1157     QVERIFY(lineReader.atEnd());
       
  1158 
       
  1159     delete encoder;
       
  1160 }
       
  1161 
       
  1162 void UT_QVersitReader::testReadLine_data()
       
  1163 {
       
  1164     // Note: for this test, we set mLineReader to read 10 bytes at a time.  Lines of multiples of
       
  1165     // 10 bytes are hence border cases.
       
  1166     QTest::addColumn<QByteArray>("codecName");
       
  1167     QTest::addColumn<QString>("data");
       
  1168     QTest::addColumn<QList<QString> >("expectedLines");
       
  1169 
       
  1170     QList<QByteArray> codecNames;
       
  1171     codecNames << "UTF-8" << "UTF-16";
       
  1172 
       
  1173     foreach (QByteArray codecName, codecNames) {
       
  1174         QTest::newRow("empty " + codecName)
       
  1175                 << codecName
       
  1176                 << ""
       
  1177                 << QList<QString>();
       
  1178 
       
  1179         QTest::newRow("one line " + codecName)
       
  1180                 << codecName
       
  1181                 << "line"
       
  1182                 << (QList<QString>() << QLatin1String("line"));
       
  1183 
       
  1184         QTest::newRow("one ten-byte line " + codecName)
       
  1185                 << codecName
       
  1186                 << "tenletters"
       
  1187                 << (QList<QString>() << QLatin1String("tenletters"));
       
  1188 
       
  1189         QTest::newRow("one long line " + codecName)
       
  1190                 << codecName
       
  1191                 << "one line longer than ten characters"
       
  1192                 << (QList<QString>() << QLatin1String("one line longer than ten characters"));
       
  1193 
       
  1194         QTest::newRow("one terminated line " + codecName)
       
  1195                 << codecName
       
  1196                 << "one line longer than ten characters\r\n"
       
  1197                 << (QList<QString>() << QLatin1String("one line longer than ten characters"));
       
  1198 
       
  1199         QTest::newRow("two lines " + codecName)
       
  1200                 << codecName
       
  1201                 << "two\r\nlines"
       
  1202                 << (QList<QString>() << QLatin1String("two") << QLatin1String("lines"));
       
  1203 
       
  1204         QTest::newRow("two terminated lines " + codecName)
       
  1205                 << codecName
       
  1206                 << "two\r\nlines\r\n"
       
  1207                 << (QList<QString>() << QLatin1String("two") << QLatin1String("lines"));
       
  1208 
       
  1209         QTest::newRow("two long lines " + codecName)
       
  1210                 << codecName
       
  1211                 << "one line longer than ten characters\r\nanother line\r\n"
       
  1212                 << (QList<QString>() << QLatin1String("one line longer than ten characters") << QLatin1String("another line"));
       
  1213 
       
  1214         QTest::newRow("two full lines " + codecName)
       
  1215                 << codecName
       
  1216                 << "tenletters\r\n8letters\r\n"
       
  1217                 << (QList<QString>() << QLatin1String("tenletters") << QLatin1String("8letters"));
       
  1218 
       
  1219         QTest::newRow("a nine-byte line " + codecName)
       
  1220                 << codecName
       
  1221                 << "9 letters\r\nanother line\r\n"
       
  1222                 << (QList<QString>() << QLatin1String("9 letters") << QLatin1String("another line"));
       
  1223 
       
  1224         QTest::newRow("a blank line " + codecName)
       
  1225                 << codecName
       
  1226                 << "one\r\n\r\ntwo\r\n"
       
  1227                 << (QList<QString>() << QLatin1String("one") << QLatin1String("two"));
       
  1228 
       
  1229         QTest::newRow("folded lines " + codecName)
       
  1230                 << codecName
       
  1231                 << "folded\r\n  line\r\nsecond line\r\n"
       
  1232                 << (QList<QString>() << QLatin1String("folded line") << QLatin1String("second line"));
       
  1233 
       
  1234         QTest::newRow("multiply folded lines " + codecName)
       
  1235                 << codecName
       
  1236                 << "fo\r\n lded\r\n  line\r\nseco\r\n\tnd l\r\n ine\r\n"
       
  1237                 << (QList<QString>() << QLatin1String("folded line") << QLatin1String("second line"));
       
  1238 
       
  1239         QTest::newRow("fold hidden after a chunk " + codecName)
       
  1240                 << codecName
       
  1241                 << "8letters\r\n  on one line\r\n"
       
  1242                 << (QList<QString>() << QLatin1String("8letters on one line"));
       
  1243 
       
  1244         QTest::newRow("three mac lines " + codecName)
       
  1245                 << codecName
       
  1246                 << "one\rtwo\rthree\r"
       
  1247                 << (QList<QString>() << QLatin1String("one") << QLatin1String("two") << QLatin1String("three"));
       
  1248     }
       
  1249 }
       
  1250 
       
  1251 QTEST_MAIN(UT_QVersitReader)
       
  1252