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