tests/auto/qxmlstream/tst_qxmlstream.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     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 test suite of the Qt Toolkit.
       
     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 
       
    43 #include <QDirIterator>
       
    44 #include <QEventLoop>
       
    45 #include <QNetworkAccessManager>
       
    46 #include <QNetworkReply>
       
    47 #include <QNetworkRequest>
       
    48 #include <QtTest/QtTest>
       
    49 #include <QUrl>
       
    50 #include <QXmlDefaultHandler>
       
    51 #include <QXmlStreamReader>
       
    52 
       
    53 #include "qc14n.h"
       
    54 
       
    55 //TESTED_CLASS=QXmlStreamReader QXmlStreamWriter
       
    56 //TESTED_FILES=corelib/xml/stream/qxmlutils.cpp corelib/xml/stream/qxmlstream.cpp corelib/xml/stream/qxmlstream_p.h
       
    57 
       
    58 Q_DECLARE_METATYPE(QXmlStreamReader::ReadElementTextBehaviour)
       
    59 
       
    60 static const char *const catalogFile = "XML-Test-Suite/xmlconf/finalCatalog.xml";
       
    61 static const int expectedRunCount = 1646;
       
    62 static const int expectedSkipCount = 532;
       
    63 
       
    64 static inline int best(int a, int b)
       
    65 {
       
    66     if (a < 0)
       
    67         return b;
       
    68     if (b < 0)
       
    69         return a;
       
    70     return qMin(a, b);
       
    71 }
       
    72 
       
    73 static inline int best(int a, int b, int c)
       
    74 {
       
    75     if (a < 0)
       
    76         return best(b, c);
       
    77     if (b < 0)
       
    78         return best(a, c);
       
    79     if (c < 0)
       
    80         return best(a, b);
       
    81     return qMin(qMin(a, b), c);
       
    82 }
       
    83 
       
    84 /**
       
    85  *  Opens @p filename and returns content produced as per
       
    86  *  xmlconf/xmltest/canonxml.html.
       
    87  *
       
    88  *  @p docType is the DOCTYPE name that the returned output should
       
    89  *  have, if it doesn't already have one.
       
    90  */
       
    91 static QByteArray makeCanonical(const QString &filename,
       
    92                                 const QString &docType,
       
    93                                 bool &hasError,
       
    94                                 bool testIncremental = false)
       
    95 {
       
    96     QFile file(filename);
       
    97     file.open(QIODevice::ReadOnly);
       
    98 
       
    99     QXmlStreamReader reader;
       
   100 
       
   101     QByteArray buffer;
       
   102     int bufferPos = 0;
       
   103 
       
   104     if (testIncremental)
       
   105         buffer = file.readAll();
       
   106     else
       
   107         reader.setDevice(&file);
       
   108 
       
   109     QByteArray outarray;
       
   110     QXmlStreamWriter writer(&outarray);
       
   111 
       
   112     forever {
       
   113         while (!reader.atEnd()) {
       
   114             reader.readNext();
       
   115             if (reader.isDTD()) {
       
   116                 if (!reader.notationDeclarations().isEmpty()) {
       
   117                     QString dtd;
       
   118                     QTextStream writeDtd(&dtd);
       
   119 
       
   120                     writeDtd << "<!DOCTYPE ";
       
   121                     writeDtd << docType;
       
   122                     writeDtd << " [";
       
   123                     writeDtd << endl;
       
   124                     QMap<QString, QXmlStreamNotationDeclaration> sortedNotationDeclarations;
       
   125                     foreach (QXmlStreamNotationDeclaration notation, reader.notationDeclarations())
       
   126                         sortedNotationDeclarations.insert(notation.name().toString(), notation);
       
   127                     foreach (QXmlStreamNotationDeclaration notation, sortedNotationDeclarations.values()) {
       
   128                         writeDtd << "<!NOTATION ";
       
   129                         writeDtd << notation.name().toString();
       
   130                         if (notation.publicId().isEmpty()) {
       
   131                             writeDtd << " SYSTEM \'";
       
   132                             writeDtd << notation.systemId().toString();
       
   133                             writeDtd << "\'";
       
   134                         } else {
       
   135                             writeDtd << " PUBLIC \'";
       
   136                             writeDtd << notation.publicId().toString();
       
   137                             writeDtd << "\'";
       
   138                             if (!notation.systemId().isEmpty() ) {
       
   139                                 writeDtd << " \'";
       
   140                                 writeDtd << notation.systemId().toString();
       
   141                                 writeDtd << "\'";
       
   142                             }
       
   143                         }
       
   144                         writeDtd << ">";
       
   145                         writeDtd << endl;
       
   146                     }
       
   147 
       
   148                     writeDtd << "]>";
       
   149                     writeDtd << endl;
       
   150                     writer.writeDTD(dtd);
       
   151                 }
       
   152             } else if (reader.isStartElement()) {
       
   153                 writer.writeStartElement(reader.namespaceUri().toString(), reader.name().toString());
       
   154 
       
   155                 QMap<QString, QXmlStreamAttribute> sortedAttributes;
       
   156                 foreach(QXmlStreamAttribute attribute, reader.attributes())
       
   157                     sortedAttributes.insert(attribute.name().toString(), attribute);
       
   158                 foreach(QXmlStreamAttribute attribute, sortedAttributes.values())
       
   159                     writer.writeAttribute(attribute);
       
   160                 writer.writeCharacters(QString()); // write empty string to avoid having empty xml tags
       
   161             } else if (reader.isCharacters()) {
       
   162                 // make canonical
       
   163 
       
   164                 QString text = reader.text().toString();
       
   165                 int i = 0;
       
   166                 int p = 0;
       
   167                 while ((i = best(text.indexOf(QLatin1Char(10), p),
       
   168                                  text.indexOf(QLatin1Char(13), p),
       
   169                                  text.indexOf(QLatin1Char(9), p))) >= 0) {
       
   170                     writer.writeCharacters(text.mid(p, i - p));
       
   171                     writer.writeEntityReference(QString("#%1").arg(text.at(i).unicode()));
       
   172                     p = i + 1;
       
   173                 }
       
   174                 writer.writeCharacters(text.mid(p));
       
   175             } else if (reader.isStartDocument() || reader.isEndDocument() || reader.isComment()){
       
   176                 // canonical does not want any of those
       
   177             } else if (reader.isProcessingInstruction() && reader.processingInstructionData().isEmpty()) {
       
   178                 // for some reason canonical wants a space
       
   179                 writer.writeProcessingInstruction(reader.processingInstructionTarget().toString(), QLatin1String(""));
       
   180             } else if (!reader.hasError()){
       
   181                 writer.writeCurrentToken(reader);
       
   182             }
       
   183         }
       
   184         if (testIncremental && bufferPos < buffer.size()) {
       
   185             reader.addData(QByteArray(buffer.data() + (bufferPos++), 1));
       
   186         } else {
       
   187             break;
       
   188         }
       
   189     }
       
   190 
       
   191     if (reader.hasError()) {
       
   192         hasError = true;
       
   193         outarray += "ERROR:";
       
   194         outarray += reader.errorString().toLatin1();
       
   195     }
       
   196     else
       
   197         hasError = false;
       
   198 
       
   199     return outarray;
       
   200 }
       
   201 
       
   202 /**
       
   203  * @short Returns the lexical QName of the document element in
       
   204  * @p document.
       
   205  *
       
   206  * It is assumed that @p document is a well-formed XML document.
       
   207  */
       
   208 static QString documentElement(const QByteArray &document)
       
   209 {
       
   210     QXmlStreamReader reader(document);
       
   211 
       
   212     while(!reader.atEnd())
       
   213     {
       
   214         if(reader.isStartElement())
       
   215             return reader.qualifiedName().toString();
       
   216 
       
   217         reader.readNext();
       
   218     }
       
   219 
       
   220     Q_ASSERT_X(false, Q_FUNC_INFO,
       
   221                qPrintable(QString::fromLatin1("The input %1 didn't contain an element.").arg(QString::fromUtf8(document.constData()))));
       
   222     return QString();
       
   223 }
       
   224 
       
   225 /**
       
   226  * @short Loads W3C's XML conformance test suite and runs it on QXmlStreamReader.
       
   227  *
       
   228  * Since this suite is fairly large, it runs the tests sequentially in order to not
       
   229  * have them all loaded into memory at once. In this way, the maximum memory usage stays
       
   230  * low, which means one can run valgrind on this test. However, the drawback is that
       
   231  * QTestLib's usual error reporting and testing mechanisms are slightly bypassed.
       
   232  *
       
   233  * Part of this code is a manual, ad-hoc implementation of xml:base.
       
   234  *
       
   235  * @see <a href="http://www.w3.org/XML/Test/">Extensible
       
   236  * Markup Language (XML) Conformance Test Suites</a>
       
   237  */
       
   238 class TestSuiteHandler : public QXmlDefaultHandler
       
   239 {
       
   240 public:
       
   241     /**
       
   242      * The first string is the test ID, the second is
       
   243      * a description of what went wrong.
       
   244      */
       
   245     typedef QPair<QString, QString> GeneralFailure;
       
   246 
       
   247     /**
       
   248      * The string is the test ID.
       
   249      */
       
   250     QStringList successes;
       
   251 
       
   252     /**
       
   253      * The first value is the baseline, while the se
       
   254      */
       
   255     class MissedBaseline
       
   256     {
       
   257     public:
       
   258         MissedBaseline(const QString &aId,
       
   259                        const QByteArray &aExpected,
       
   260                        const QByteArray &aOutput) : id(aId),
       
   261                                                     expected(aExpected),
       
   262                                                     output(aOutput)
       
   263         {
       
   264             Q_ASSERT(!aId.isEmpty());
       
   265         }
       
   266 
       
   267         QString     id;
       
   268         QByteArray  expected;
       
   269         QByteArray  output;
       
   270     };
       
   271 
       
   272     QList<GeneralFailure> failures;
       
   273     QList<MissedBaseline> missedBaselines;
       
   274 
       
   275     /**
       
   276      * The count of how many tests that were run.
       
   277      */
       
   278     int runCount;
       
   279 
       
   280     int skipCount;
       
   281 
       
   282     /**
       
   283      * @p baseURI is the the URI of where the catalog file resides.
       
   284      */
       
   285     TestSuiteHandler(const QUrl &baseURI) : runCount(0),
       
   286                                             skipCount(0)
       
   287     {
       
   288         Q_ASSERT(baseURI.isValid());
       
   289         m_baseURI.push(baseURI);
       
   290     }
       
   291 
       
   292     virtual bool characters(const QString &chars)
       
   293     {
       
   294         m_ch = chars;
       
   295         return true;
       
   296     }
       
   297 
       
   298     virtual bool startElement(const QString &,
       
   299                               const QString &,
       
   300                               const QString &,
       
   301                               const QXmlAttributes &atts)
       
   302     {
       
   303         m_atts.push(atts);
       
   304         const int i = atts.index(QLatin1String("xml:base"));
       
   305 
       
   306         if(i != -1)
       
   307             m_baseURI.push(m_baseURI.top().resolved(atts.value(i)));
       
   308 
       
   309         return true;
       
   310     }
       
   311 
       
   312     virtual bool endElement(const QString &,
       
   313                             const QString &localName,
       
   314                             const QString &)
       
   315     {
       
   316         if(localName == QLatin1String("TEST"))
       
   317         {
       
   318             /* We don't want tests for XML 1.1.0, in fact). */
       
   319             if(m_atts.top().value(QString(), QLatin1String("VERSION")) == QLatin1String("1.1"))
       
   320             {
       
   321                 ++skipCount;
       
   322                 m_atts.pop();
       
   323                 return true;
       
   324             }
       
   325 
       
   326             /* We don't want tests that conflict with the namespaces spec. Our parser is a
       
   327              * namespace-aware parser. */
       
   328             else if(m_atts.top().value(QString(), QLatin1String("NAMESPACE")) == QLatin1String("no"))
       
   329             {
       
   330                 ++skipCount;
       
   331                 m_atts.pop();
       
   332                 return true;
       
   333             }
       
   334 
       
   335             const QString inputFilePath(m_baseURI.top().resolved(m_atts.top().value(QString(), QLatin1String("URI")))
       
   336                                                                 .toLocalFile());
       
   337             const QString id(m_atts.top().value(QString(), QLatin1String("ID")));
       
   338             const QString type(m_atts.top().value(QString(), QLatin1String("TYPE")));
       
   339 
       
   340             QString expectedFilePath;
       
   341             const int index = m_atts.top().index(QString(), QLatin1String("OUTPUT"));
       
   342 
       
   343             //qDebug() << "Running test case:" << id;
       
   344 
       
   345             if(index != -1)
       
   346             {
       
   347                 expectedFilePath = m_baseURI.top().resolved(m_atts.top().value(QString(),
       
   348                                                             QLatin1String("OUTPUT"))).toLocalFile();
       
   349             }
       
   350 
       
   351             /* testcases.dtd: 'No parser should accept a "not-wf" testcase
       
   352              * unless it's a nonvalidating parser and the test contains
       
   353              * external entities that the parser doesn't read.'
       
   354              *
       
   355              * We also let this apply to "valid", "invalid" and "error" tests, although
       
   356              * I'm not fully sure this is correct. */
       
   357             const QString ents(m_atts.top().value(QString(), QLatin1String("ENTITIES")));
       
   358             m_atts.pop();
       
   359 
       
   360             if(ents == QLatin1String("both") ||
       
   361                ents == QLatin1String("general") ||
       
   362                ents == QLatin1String("parameter"))
       
   363             {
       
   364                 ++skipCount;
       
   365                 return true;
       
   366             }
       
   367 
       
   368             ++runCount;
       
   369 
       
   370             QFile inputFile(inputFilePath);
       
   371             if(!inputFile.open(QIODevice::ReadOnly))
       
   372             {
       
   373                 failures.append(qMakePair(id, QString::fromLatin1("Failed to open input file %1").arg(inputFilePath)));
       
   374                 return true;
       
   375             }
       
   376 
       
   377             if(type == QLatin1String("not-wf"))
       
   378             {
       
   379                 if(isWellformed(&inputFile, ParseSinglePass))
       
   380                 {
       
   381                      failures.append(qMakePair(id, QString::fromLatin1("Failed to flag %1 as not well-formed.")
       
   382                                                    .arg(inputFilePath)));
       
   383 
       
   384                      /* Exit, the incremental test will fail as well, no need to flood the output. */
       
   385                      return true;
       
   386                 }
       
   387                 else
       
   388                     successes.append(id);
       
   389 
       
   390                 if(isWellformed(&inputFile, ParseIncrementally))
       
   391                 {
       
   392                      failures.append(qMakePair(id, QString::fromLatin1("Failed to flag %1 as not well-formed with incremental parsing.")
       
   393                                                    .arg(inputFilePath)));
       
   394                 }
       
   395                 else
       
   396                     successes.append(id);
       
   397 
       
   398                 return true;
       
   399             }
       
   400 
       
   401             QXmlStreamReader reader(&inputFile);
       
   402 
       
   403             /* See testcases.dtd which reads: 'Nonvalidating parsers
       
   404              * must also accept "invalid" testcases, but validating ones must reject them.' */
       
   405             if(type == QLatin1String("invalid") || type == QLatin1String("valid"))
       
   406             {
       
   407                 QByteArray expected;
       
   408                 QString docType;
       
   409 
       
   410                 /* We only want to compare against a baseline when we have
       
   411                  * one. Some "invalid"-tests, for instance, doesn't have baselines. */
       
   412                 if(!expectedFilePath.isEmpty())
       
   413                 {
       
   414                     QFile expectedFile(expectedFilePath);
       
   415 
       
   416                     if(!expectedFile.open(QIODevice::ReadOnly))
       
   417                     {
       
   418                         failures.append(qMakePair(id, QString::fromLatin1("Failed to open baseline %1").arg(expectedFilePath)));
       
   419                         return true;
       
   420                     }
       
   421 
       
   422                     expected = expectedFile.readAll();
       
   423                     docType = documentElement(expected);
       
   424                 }
       
   425                 else
       
   426                     docType = QLatin1String("dummy");
       
   427 
       
   428                 bool hasError = true;
       
   429                 bool incremental = false;
       
   430 
       
   431                 QByteArray input(makeCanonical(inputFilePath, docType, hasError, incremental));
       
   432 
       
   433                 if (!hasError && !expectedFilePath.isEmpty() && input == expected)
       
   434                     input = makeCanonical(inputFilePath, docType, hasError, (incremental = true));
       
   435 
       
   436                 if(hasError)
       
   437                     failures.append(qMakePair(id, QString::fromLatin1("Failed to parse %1%2")
       
   438                                               .arg(incremental?"(incremental run only) ":"")
       
   439                                               .arg(inputFilePath)));
       
   440 
       
   441                 if(!expectedFilePath.isEmpty() && input != expected)
       
   442                 {
       
   443                     missedBaselines.append(MissedBaseline(id, expected, input));
       
   444                     return true;
       
   445                 }
       
   446                 else
       
   447                 {
       
   448                     successes.append(id);
       
   449                     return true;
       
   450                 }
       
   451             }
       
   452             else if(type == QLatin1String("error"))
       
   453             {
       
   454                 /* Not yet sure about this one. */
       
   455                 // TODO
       
   456                 return true;
       
   457             }
       
   458             else
       
   459             {
       
   460                 Q_ASSERT_X(false, Q_FUNC_INFO, "The input catalog is invalid.");
       
   461                 return false;
       
   462             }
       
   463         }
       
   464         else if(localName == QLatin1String("TESTCASES") && m_atts.top().index(QLatin1String("xml:base")) != -1)
       
   465             m_baseURI.pop();
       
   466 
       
   467         m_atts.pop();
       
   468 
       
   469         return true;
       
   470     }
       
   471 
       
   472     enum ParseMode
       
   473     {
       
   474         ParseIncrementally,
       
   475         ParseSinglePass
       
   476     };
       
   477 
       
   478     static bool isWellformed(QIODevice *const inputFile, const ParseMode mode)
       
   479     {
       
   480         Q_ASSERT(inputFile);
       
   481         Q_ASSERT_X(inputFile->isOpen(), Q_FUNC_INFO, "The caller is responsible for opening the device.");
       
   482         Q_ASSERT(mode == ParseIncrementally || mode == ParseSinglePass);
       
   483 
       
   484         if(mode == ParseIncrementally)
       
   485         {
       
   486             QXmlStreamReader reader;
       
   487             QByteArray buffer;
       
   488             int bufferPos = 0;
       
   489 
       
   490             buffer = inputFile->readAll();
       
   491 
       
   492             while(true)
       
   493             {
       
   494                 while(!reader.atEnd())
       
   495                     reader.readNext();
       
   496 
       
   497                 if(bufferPos < buffer.size())
       
   498                 {
       
   499                     ++bufferPos;
       
   500                     reader.addData(QByteArray(buffer.data() + bufferPos, 1));
       
   501                 }
       
   502                 else
       
   503                     break;
       
   504             }
       
   505 
       
   506             return !reader.hasError();
       
   507         }
       
   508         else
       
   509         {
       
   510             QXmlStreamReader reader;
       
   511             reader.setDevice(inputFile);
       
   512 
       
   513             while(!reader.atEnd())
       
   514                 reader.readNext();
       
   515 
       
   516             return !reader.hasError();
       
   517         }
       
   518     }
       
   519 
       
   520 private:
       
   521     QStack<QXmlAttributes>  m_atts;
       
   522     QString                 m_ch;
       
   523     QStack<QUrl>            m_baseURI;
       
   524 };
       
   525 
       
   526 class tst_QXmlStream: public QObject
       
   527 {
       
   528     Q_OBJECT
       
   529 public:
       
   530     tst_QXmlStream() : m_handler(QUrl::fromLocalFile(QDir::currentPath()  + QLatin1Char('/'))
       
   531                                 .resolved(QUrl(QLatin1String(catalogFile))))
       
   532     {
       
   533     }
       
   534 
       
   535 private slots:
       
   536     void initTestCase();
       
   537     void reportFailures() const;
       
   538     void reportFailures_data();
       
   539     void checkBaseline() const;
       
   540     void checkBaseline_data() const;
       
   541     void testReader() const;
       
   542     void testReader_data() const;
       
   543     void reportSuccess() const;
       
   544     void reportSuccess_data() const;
       
   545     void parseXSLTTestSuite() const;
       
   546     void writerHangs() const;
       
   547     void writerAutoFormattingWithComments() const;
       
   548     void writerAutoFormattingWithTabs() const;
       
   549     void writerAutoEmptyTags() const;
       
   550     void writeAttributesWithSpace() const;
       
   551     void addExtraNamespaceDeclarations();
       
   552     void setEntityResolver();
       
   553     void readFromQBuffer() const;
       
   554     void readFromQBufferInvalid() const;
       
   555     void readNextStartElement() const;
       
   556     void readElementText() const;
       
   557     void readElementText_data() const;
       
   558     void crashInUTF16Codec() const;
       
   559     void hasAttributeSignature() const;
       
   560     void hasAttribute() const;
       
   561     void writeWithCodec() const;
       
   562     void writeWithStandalone() const;
       
   563     void entitiesAndWhitespace_1() const;
       
   564     void entitiesAndWhitespace_2() const;
       
   565     void testFalsePrematureError() const;
       
   566     void garbageInXMLPrologDefaultCodec() const;
       
   567     void garbageInXMLPrologUTF8Explicitly() const;
       
   568     void clear() const;
       
   569     void checkCommentIndentation() const;
       
   570     void checkCommentIndentation_data() const;
       
   571 
       
   572 private:
       
   573     static QByteArray readFile(const QString &filename);
       
   574 
       
   575     TestSuiteHandler m_handler;
       
   576 };
       
   577 
       
   578 void tst_QXmlStream::initTestCase()
       
   579 {
       
   580     QFile file(QString::fromLatin1(catalogFile));
       
   581     QVERIFY2(file.open(QIODevice::ReadOnly),
       
   582              qPrintable(QString::fromLatin1("Failed to open the test suite catalog; %1").arg(file.fileName())));
       
   583 
       
   584     QXmlInputSource source(&file);
       
   585     QXmlSimpleReader reader;
       
   586     reader.setContentHandler(&m_handler);
       
   587 
       
   588     QVERIFY(reader.parse(&source, false));
       
   589 }
       
   590 
       
   591 void tst_QXmlStream::reportFailures() const
       
   592 {
       
   593     QFETCH(bool, isError);
       
   594     QFETCH(QString, description);
       
   595 
       
   596     QVERIFY2(!isError, qPrintable(description));
       
   597 }
       
   598 
       
   599 void tst_QXmlStream::reportFailures_data()
       
   600 {
       
   601     const int len = m_handler.failures.count();
       
   602 
       
   603     QTest::addColumn<bool>("isError");
       
   604     QTest::addColumn<QString>("description");
       
   605 
       
   606     /* We loop over all our failures(if any!), and output them such
       
   607      * that they appear in the QTestLib log. */
       
   608     for(int i = 0; i < len; ++i)
       
   609         QTest::newRow(m_handler.failures.at(i).first.toLatin1().constData()) << true << m_handler.failures.at(i).second;
       
   610 
       
   611     /* We need to add at least one column of test data, otherwise QTestLib complains. */
       
   612     if(len == 0)
       
   613         QTest::newRow("Whole test suite passed") << false << QString();
       
   614 
       
   615     /* We compare the test case counts to ensure that we've actually run test cases, that
       
   616      * the driver hasn't been broken or changed without updating the expected count, and
       
   617      * similar reasons. */
       
   618     QCOMPARE(m_handler.runCount, expectedRunCount);
       
   619     QCOMPARE(m_handler.skipCount, expectedSkipCount);
       
   620 }
       
   621 
       
   622 void tst_QXmlStream::checkBaseline() const
       
   623 {
       
   624     QFETCH(bool, isError);
       
   625     QFETCH(QString, expected);
       
   626     QFETCH(QString, output);
       
   627 
       
   628     if(isError)
       
   629         QCOMPARE(output, expected);
       
   630 }
       
   631 
       
   632 void tst_QXmlStream::checkBaseline_data() const
       
   633 {
       
   634     QTest::addColumn<bool>("isError");
       
   635     QTest::addColumn<QString>("expected");
       
   636     QTest::addColumn<QString>("output");
       
   637 
       
   638     const int len = m_handler.missedBaselines.count();
       
   639 
       
   640     for(int i = 0; i < len; ++i)
       
   641     {
       
   642         const TestSuiteHandler::MissedBaseline &b = m_handler.missedBaselines.at(i);
       
   643 
       
   644         /* We indeed don't know what encoding the content is in so in some cases fromUtf8
       
   645          * is all wrong, but it's an acceptable guess for error reporting. */
       
   646         QTest::newRow(b.id.toLatin1().constData())
       
   647                 << true
       
   648                 << QString::fromUtf8(b.expected.constData())
       
   649                 << QString::fromUtf8(b.output.constData());
       
   650     }
       
   651 
       
   652     if(len == 0)
       
   653         QTest::newRow("dummy") << false << QString() << QString();
       
   654 }
       
   655 
       
   656 void tst_QXmlStream::reportSuccess() const
       
   657 {
       
   658     QFETCH(bool, isError);
       
   659 
       
   660     QVERIFY(!isError);
       
   661 }
       
   662 
       
   663 void tst_QXmlStream::reportSuccess_data() const
       
   664 {
       
   665     QTest::addColumn<bool>("isError");
       
   666 
       
   667     const int len = m_handler.successes.count();
       
   668 
       
   669     for(int i = 0; i < len; ++i)
       
   670         QTest::newRow(m_handler.successes.at(i).toLatin1().constData()) << false;
       
   671 
       
   672     if(len == 0)
       
   673         QTest::newRow("No test cases succeeded.") << true;
       
   674 }
       
   675 
       
   676 QByteArray tst_QXmlStream::readFile(const QString &filename)
       
   677 {
       
   678     QFile file(filename);
       
   679     file.open(QIODevice::ReadOnly);
       
   680 
       
   681     QXmlStreamReader reader;
       
   682 
       
   683     reader.setDevice(&file);
       
   684     QByteArray outarray;
       
   685     QTextStream writer(&outarray);
       
   686 	// We always want UTF-8, and not what the system picks up.
       
   687 	writer.setCodec("UTF-8");
       
   688 
       
   689     while (!reader.atEnd()) {
       
   690         reader.readNext();
       
   691         writer << reader.tokenString() << "(";
       
   692         if (reader.isWhitespace())
       
   693             writer << " whitespace";
       
   694         if (reader.isCDATA())
       
   695             writer << " CDATA";
       
   696         if (reader.isStartDocument() && reader.isStandaloneDocument())
       
   697             writer << " standalone";
       
   698         if (!reader.text().isEmpty())
       
   699             writer << " text=\"" << reader.text().toString() << "\"";
       
   700         if (!reader.processingInstructionTarget().isEmpty())
       
   701             writer << " processingInstructionTarget=\"" << reader.processingInstructionTarget().toString() << "\"";
       
   702         if (!reader.processingInstructionData().isEmpty())
       
   703             writer << " processingInstructionData=\"" << reader.processingInstructionData().toString() << "\"";
       
   704         if (!reader.dtdName().isEmpty())
       
   705             writer << " dtdName=\"" << reader.dtdName().toString() << "\"";
       
   706         if (!reader.dtdPublicId().isEmpty())
       
   707             writer << " dtdPublicId=\"" << reader.dtdPublicId().toString() << "\"";
       
   708         if (!reader.dtdSystemId().isEmpty())
       
   709             writer << " dtdSystemId=\"" << reader.dtdSystemId().toString() << "\"";
       
   710         if (!reader.documentVersion().isEmpty())
       
   711             writer << " documentVersion=\"" << reader.documentVersion().toString() << "\"";
       
   712         if (!reader.documentEncoding().isEmpty())
       
   713             writer << " documentEncoding=\"" << reader.documentEncoding().toString() << "\"";
       
   714         if (!reader.name().isEmpty())
       
   715             writer << " name=\"" << reader.name().toString() << "\"";
       
   716         if (!reader.namespaceUri().isEmpty())
       
   717             writer << " namespaceUri=\"" << reader.namespaceUri().toString() << "\"";
       
   718         if (!reader.qualifiedName().isEmpty())
       
   719             writer << " qualifiedName=\"" << reader.qualifiedName().toString() << "\"";
       
   720         if (!reader.prefix().isEmpty())
       
   721             writer << " prefix=\"" << reader.prefix().toString() << "\"";
       
   722         if (reader.attributes().size()) {
       
   723             foreach(QXmlStreamAttribute attribute, reader.attributes()) {
       
   724                 writer << endl << "    Attribute(";
       
   725                 if (!attribute.name().isEmpty())
       
   726                     writer << " name=\"" << attribute.name().toString() << "\"";
       
   727                 if (!attribute.namespaceUri().isEmpty())
       
   728                     writer << " namespaceUri=\"" << attribute.namespaceUri().toString() << "\"";
       
   729                 if (!attribute.qualifiedName().isEmpty())
       
   730                     writer << " qualifiedName=\"" << attribute.qualifiedName().toString() << "\"";
       
   731                 if (!attribute.prefix().isEmpty())
       
   732                     writer << " prefix=\"" << attribute.prefix().toString() << "\"";
       
   733                 if (!attribute.value().isEmpty())
       
   734                     writer << " value=\"" << attribute.value().toString() << "\"";
       
   735                 writer << " )" << endl;
       
   736             }
       
   737         }
       
   738         if (reader.namespaceDeclarations().size()) {
       
   739             foreach(QXmlStreamNamespaceDeclaration namespaceDeclaration, reader.namespaceDeclarations()) {
       
   740                 writer << endl << "    NamespaceDeclaration(";
       
   741                 if (!namespaceDeclaration.prefix().isEmpty())
       
   742                     writer << " prefix=\"" << namespaceDeclaration.prefix().toString() << "\"";
       
   743                 if (!namespaceDeclaration.namespaceUri().isEmpty())
       
   744                     writer << " namespaceUri=\"" << namespaceDeclaration.namespaceUri().toString() << "\"";
       
   745                 writer << " )" << endl;
       
   746             }
       
   747         }
       
   748         if (reader.notationDeclarations().size()) {
       
   749             foreach(QXmlStreamNotationDeclaration notationDeclaration, reader.notationDeclarations()) {
       
   750                 writer << endl << "    NotationDeclaration(";
       
   751                 if (!notationDeclaration.name().isEmpty())
       
   752                     writer << " name=\"" << notationDeclaration.name().toString() << "\"";
       
   753                 if (!notationDeclaration.systemId().isEmpty())
       
   754                     writer << " systemId=\"" << notationDeclaration.systemId().toString() << "\"";
       
   755                 if (!notationDeclaration.publicId().isEmpty())
       
   756                     writer << " publicId=\"" << notationDeclaration.publicId().toString() << "\"";
       
   757                 writer << " )" << endl;
       
   758             }
       
   759         }
       
   760         if (reader.entityDeclarations().size()) {
       
   761             foreach(QXmlStreamEntityDeclaration entityDeclaration, reader.entityDeclarations()) {
       
   762                 writer << endl << "    EntityDeclaration(";
       
   763                 if (!entityDeclaration.name().isEmpty())
       
   764                     writer << " name=\"" << entityDeclaration.name().toString() << "\"";
       
   765                 if (!entityDeclaration.notationName().isEmpty())
       
   766                     writer << " notationName=\"" << entityDeclaration.notationName().toString() << "\"";
       
   767                 if (!entityDeclaration.systemId().isEmpty())
       
   768                     writer << " systemId=\"" << entityDeclaration.systemId().toString() << "\"";
       
   769                 if (!entityDeclaration.publicId().isEmpty())
       
   770                     writer << " publicId=\"" << entityDeclaration.publicId().toString() << "\"";
       
   771                 if (!entityDeclaration.value().isEmpty())
       
   772                     writer << " value=\"" << entityDeclaration.value().toString() << "\"";
       
   773                 writer << " )" << endl;
       
   774             }
       
   775         }
       
   776         writer << " )" << endl;
       
   777     }
       
   778     if (reader.hasError())
       
   779         writer << "ERROR: " << reader.errorString() << endl;
       
   780     return outarray;
       
   781 }
       
   782 
       
   783 void tst_QXmlStream::testReader() const
       
   784 {
       
   785     QFETCH(QString, xml);
       
   786     QFETCH(QString, ref);
       
   787     QFile file(ref);
       
   788     if (!file.exists()) {
       
   789         QByteArray reference = readFile(xml);
       
   790         QVERIFY(file.open(QIODevice::WriteOnly));
       
   791         file.write(reference);
       
   792         file.close();
       
   793     } else {
       
   794         QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text));
       
   795         QString reference = QString::fromUtf8(file.readAll());
       
   796         QString qxmlstream = QString::fromUtf8(readFile(xml));
       
   797         QCOMPARE(qxmlstream, reference);
       
   798     }
       
   799 }
       
   800 
       
   801 void tst_QXmlStream::testReader_data() const
       
   802 {
       
   803     QTest::addColumn<QString>("xml");
       
   804     QTest::addColumn<QString>("ref");
       
   805     QDir dir;
       
   806     dir.cd("data/");
       
   807     foreach(QString filename , dir.entryList(QStringList() << "*.xml")) {
       
   808         QString reference =  QFileInfo(filename).baseName() + ".ref";
       
   809         QTest::newRow(dir.filePath(filename).toLatin1().data()) << dir.filePath(filename) << dir.filePath(reference);
       
   810     }
       
   811 }
       
   812 
       
   813 void tst_QXmlStream::parseXSLTTestSuite() const
       
   814 {
       
   815     /* We disable this test for now, so it doesn't show up as an XFAIL. */
       
   816 #if 0
       
   817     QEXPECT_FAIL("", "Two problems needs to be solved in order to enable this test: \n"
       
   818                      "* The XSLT suite is 69 MB large, which is quite a lot compared to the existing XML suite on 2 mb.\n"
       
   819                      "* We need a c14n-like implementation in order to compare the outputs.", Abort);
       
   820     QVERIFY(false);
       
   821 
       
   822     /* We don't yet know this. TODO */
       
   823     int xsltExpectedRunCount = -1;
       
   824 
       
   825     QStringList nameFilters;
       
   826     nameFilters.append("*.xsl");
       
   827     nameFilters.append("*.xml");
       
   828 
       
   829     QDirIterator dirIterator("XSLT-Test-Suite/", nameFilters,
       
   830                              QDir::AllEntries, QDirIterator::Subdirectories);
       
   831 
       
   832     int filesParsed = 0;
       
   833 
       
   834     while(dirIterator.hasNext())
       
   835     {
       
   836         dirIterator.next();
       
   837 
       
   838         const QString fp(dirIterator.filePath());
       
   839         qDebug() << "Found" << fp;
       
   840 
       
   841         QFile inputFile(fp);
       
   842         QVERIFY(inputFile.open(QIODevice::ReadOnly));
       
   843 
       
   844         /* Read in and write out to the QByteArray. */
       
   845         QByteArray outputArray;
       
   846         {
       
   847             QXmlStreamReader reader(&inputFile);
       
   848 
       
   849             QXmlStreamWriter writer(&outputArray);
       
   850 
       
   851             while(!reader.atEnd())
       
   852             {
       
   853                 writer.writeCurrentToken(reader);
       
   854                 reader.readNext();
       
   855 
       
   856                 QVERIFY2(!reader.hasError(), qPrintable(reader.errorString()));
       
   857             }
       
   858             /* Might be we got an error here, but we don't care. */
       
   859         }
       
   860 
       
   861         /* Read in the two files, and compare them. */
       
   862         {
       
   863             QBuffer outputBuffer(&outputArray);
       
   864             outputBuffer.open(QIODevice::ReadOnly);
       
   865             inputFile.close();
       
   866             inputFile.open(QIODevice::ReadOnly);
       
   867 
       
   868             QString message;
       
   869             const bool isEqual = QC14N::isEqual(&inputFile, &outputBuffer, &message);
       
   870 
       
   871             QVERIFY2(isEqual, message.toLatin1().constData());
       
   872 
       
   873             ++filesParsed;
       
   874         }
       
   875     }
       
   876 
       
   877     QCOMPARE(xsltExpectedRunCount, filesParsed);
       
   878 #endif
       
   879 }
       
   880 
       
   881 void tst_QXmlStream::addExtraNamespaceDeclarations()
       
   882 {
       
   883     const char *data = "<bla><undeclared:foo/><undeclared_too:foo/></bla>";
       
   884     {
       
   885         QXmlStreamReader xml(data);
       
   886         while (!xml.atEnd()) {
       
   887             xml.readNext();
       
   888         }
       
   889         QVERIFY2(xml.hasError(), "namespaces undeclared");
       
   890     }
       
   891     {
       
   892         QXmlStreamReader xml(data);
       
   893         xml.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("undeclared", "blabla"));
       
   894         xml.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("undeclared_too", "foofoo"));
       
   895         while (!xml.atEnd()) {
       
   896             xml.readNext();
       
   897         }
       
   898         QVERIFY2(!xml.hasError(), xml.errorString().toLatin1().constData());
       
   899     }
       
   900 }
       
   901 
       
   902 
       
   903 class EntityResolver : public QXmlStreamEntityResolver {
       
   904 public:
       
   905     QString resolveUndeclaredEntity(const QString &name) {
       
   906         static int count = 0;
       
   907         return name.toUpper() + QString::number(++count);
       
   908     }
       
   909 };
       
   910 void tst_QXmlStream::setEntityResolver()
       
   911 {
       
   912     const char *data = "<bla foo=\"&undeclared;\">&undeclared_too;</bla>";
       
   913     {
       
   914         QXmlStreamReader xml(data);
       
   915         while (!xml.atEnd()) {
       
   916             xml.readNext();
       
   917         }
       
   918         QVERIFY2(xml.hasError(), "undeclared entities");
       
   919     }
       
   920     {
       
   921         QString foo;
       
   922         QString bla_text;
       
   923         QXmlStreamReader xml(data);
       
   924         EntityResolver resolver;
       
   925         xml.setEntityResolver(&resolver);
       
   926         while (!xml.atEnd()) {
       
   927             xml.readNext();
       
   928             if (xml.isStartElement())
       
   929                 foo = xml.attributes().value("foo").toString();
       
   930             if (xml.isCharacters())
       
   931                 bla_text += xml.text().toString();
       
   932         }
       
   933         QVERIFY2(!xml.hasError(), xml.errorString().toLatin1().constData());
       
   934         QCOMPARE(foo, QLatin1String("UNDECLARED1"));
       
   935         QCOMPARE(bla_text, QLatin1String("UNDECLARED_TOO2"));
       
   936     }
       
   937 }
       
   938 
       
   939 void tst_QXmlStream::testFalsePrematureError() const // task 179320
       
   940 {
       
   941     const char *illegal_start = "illegal<sta";
       
   942     const char *legal_start = "<sta";
       
   943     const char* end = "rt/>";
       
   944     {
       
   945         QXmlStreamReader xml("");
       
   946         while (!xml.atEnd()) {
       
   947             xml.readNext();
       
   948         }
       
   949         QVERIFY(xml.error() == QXmlStreamReader::PrematureEndOfDocumentError);
       
   950         QCOMPARE(xml.errorString(), QLatin1String("Premature end of document."));
       
   951         xml.addData(legal_start);
       
   952         while (!xml.atEnd()) {
       
   953             xml.readNext();
       
   954         }
       
   955         QVERIFY(xml.error() == QXmlStreamReader::PrematureEndOfDocumentError);
       
   956         QCOMPARE(xml.errorString(), QLatin1String("Premature end of document."));
       
   957         xml.addData(end);
       
   958         while (!xml.atEnd()) {
       
   959             xml.readNext();
       
   960         }
       
   961         QVERIFY(!xml.hasError());
       
   962     }
       
   963     {
       
   964         QXmlStreamReader xml(illegal_start);
       
   965         while (!xml.atEnd()) {
       
   966             xml.readNext();
       
   967         }
       
   968         QVERIFY(xml.hasError());
       
   969         QCOMPARE(xml.errorString(), QLatin1String("Start tag expected."));
       
   970         QVERIFY(xml.error() == QXmlStreamReader::NotWellFormedError);
       
   971     }
       
   972 }
       
   973 
       
   974 /*!
       
   975  See task 188737. Crash due to using empty QStack.
       
   976  */
       
   977 void tst_QXmlStream::writerHangs() const
       
   978 {
       
   979     QFile file("test.xml");
       
   980 
       
   981     QVERIFY(file.open(QIODevice::WriteOnly));
       
   982 
       
   983     QXmlStreamWriter  writer(&file);
       
   984     double radius = 4.0;
       
   985     writer.setAutoFormatting(true);
       
   986     writer.writeStartDocument();
       
   987     writer.writeEmptyElement("circle");
       
   988     writer.writeAttribute("radius", QString::number(radius));
       
   989     writer.writeEndElement();
       
   990     writer.writeEndDocument();
       
   991 }
       
   992 /*!
       
   993   Task 189611
       
   994 */
       
   995 void tst_QXmlStream::writerAutoFormattingWithComments() const
       
   996 {
       
   997     QBuffer buffer;
       
   998     buffer.open(QIODevice::WriteOnly);
       
   999 
       
  1000     QXmlStreamWriter writer(&buffer);
       
  1001     writer.setAutoFormatting(true);
       
  1002     writer.writeStartDocument();
       
  1003     writer.writeComment("This is a comment");
       
  1004     writer.writeEndDocument();
       
  1005     const char *str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--This is a comment-->\n";
       
  1006     QCOMPARE(buffer.buffer().data(), str);
       
  1007 }
       
  1008 
       
  1009 
       
  1010 /*!
       
  1011   Task 206782
       
  1012 */
       
  1013 void tst_QXmlStream::writerAutoFormattingWithTabs() const
       
  1014 {
       
  1015     QBuffer buffer;
       
  1016     buffer.open(QIODevice::WriteOnly);
       
  1017 
       
  1018 
       
  1019     QXmlStreamWriter writer(&buffer);
       
  1020     writer.setAutoFormatting(true);
       
  1021     writer.setAutoFormattingIndent(-1);
       
  1022     QCOMPARE(writer.autoFormattingIndent(), -1);
       
  1023     writer.writeStartDocument();
       
  1024     writer.writeStartElement("A");
       
  1025     writer.writeStartElement("B");
       
  1026     writer.writeEndDocument();
       
  1027     const char *str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<A>\n\t<B/>\n</A>\n";
       
  1028     QCOMPARE(buffer.buffer().data(), str);
       
  1029 }
       
  1030 
       
  1031 /*!
       
  1032   Task 204822
       
  1033 */
       
  1034 void tst_QXmlStream::writeAttributesWithSpace() const
       
  1035 {
       
  1036     QBuffer buffer;
       
  1037     buffer.open(QIODevice::WriteOnly);
       
  1038 
       
  1039 
       
  1040     QXmlStreamWriter writer(&buffer);
       
  1041     writer.writeStartDocument();
       
  1042     writer.writeEmptyElement("A");
       
  1043     writer.writeAttribute("attribute", QString("value")+QChar::Nbsp);
       
  1044     writer.writeEndDocument();
       
  1045     QString s = QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><A attribute=\"value%1\"/>\n").arg(QChar(QChar::Nbsp));
       
  1046     QCOMPARE(buffer.buffer().data(), s.toUtf8().data());
       
  1047 }
       
  1048 
       
  1049 /*!
       
  1050   Task 209340
       
  1051 */
       
  1052 void tst_QXmlStream::writerAutoEmptyTags() const
       
  1053 {
       
  1054     QBuffer buffer;
       
  1055     buffer.open(QIODevice::WriteOnly);
       
  1056 
       
  1057 
       
  1058     QXmlStreamWriter writer(&buffer);
       
  1059 
       
  1060     writer.writeStartDocument();
       
  1061 
       
  1062     writer.writeStartElement("Hans");
       
  1063     writer.writeAttribute("key", "value");
       
  1064     writer.writeEndElement();
       
  1065 
       
  1066     writer.writeStartElement("Hans");
       
  1067     writer.writeAttribute("key", "value");
       
  1068     writer.writeEmptyElement("Leer");
       
  1069     writer.writeAttribute("key", "value");
       
  1070     writer.writeEndElement();
       
  1071 
       
  1072     writer.writeStartElement("Hans");
       
  1073     writer.writeAttribute("key", "value");
       
  1074     writer.writeCharacters("stuff");
       
  1075     writer.writeEndElement();
       
  1076 
       
  1077     writer.writeEndDocument();
       
  1078 
       
  1079     QString s = QString("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Hans key=\"value\"/><Hans key=\"value\"><Leer key=\"value\"/></Hans><Hans key=\"value\">stuff</Hans>\n");
       
  1080     QCOMPARE(buffer.buffer().data(), s.toUtf8().data());
       
  1081 }
       
  1082 
       
  1083 void tst_QXmlStream::readFromQBuffer() const
       
  1084 {
       
  1085     QByteArray in("<e/>");
       
  1086     QBuffer buffer(&in);
       
  1087     QVERIFY(buffer.open(QIODevice::ReadOnly));
       
  1088 
       
  1089     QXmlStreamReader reader(&buffer);
       
  1090 
       
  1091     while(!reader.atEnd())
       
  1092     {
       
  1093         reader.readNext();
       
  1094     }
       
  1095 
       
  1096     QVERIFY(!reader.hasError());
       
  1097 }
       
  1098 
       
  1099 void tst_QXmlStream::readFromQBufferInvalid() const
       
  1100 {
       
  1101     QByteArray in("<e/><e/>");
       
  1102     QBuffer buffer(&in);
       
  1103     QVERIFY(buffer.open(QIODevice::ReadOnly));
       
  1104 
       
  1105     QXmlStreamReader reader(&buffer);
       
  1106 
       
  1107     while(!reader.atEnd())
       
  1108     {
       
  1109         reader.readNext();
       
  1110     }
       
  1111 
       
  1112     QVERIFY(reader.hasError());
       
  1113 }
       
  1114 
       
  1115 void tst_QXmlStream::readNextStartElement() const
       
  1116 {
       
  1117     QLatin1String in("<?xml version=\"1.0\"?><A><!-- blah --><B><C/></B><B attr=\"value\"/>text</A>");
       
  1118     QXmlStreamReader reader(in);
       
  1119 
       
  1120     QVERIFY(reader.readNextStartElement());
       
  1121     QVERIFY(reader.isStartElement() && reader.name() == "A");
       
  1122 
       
  1123     int amountOfB = 0;
       
  1124     while (reader.readNextStartElement()) {
       
  1125         QVERIFY(reader.isStartElement() && reader.name() == "B");
       
  1126         ++amountOfB;
       
  1127         reader.skipCurrentElement();
       
  1128     }
       
  1129 
       
  1130     QCOMPARE(amountOfB, 2);
       
  1131 }
       
  1132 
       
  1133 void tst_QXmlStream::readElementText() const
       
  1134 {
       
  1135     QFETCH(QXmlStreamReader::ReadElementTextBehaviour, behaviour);
       
  1136     QFETCH(QString, input);
       
  1137     QFETCH(QString, expected);
       
  1138 
       
  1139     QXmlStreamReader reader(input);
       
  1140 
       
  1141     QVERIFY(reader.readNextStartElement());
       
  1142     QCOMPARE(reader.readElementText(behaviour), expected);
       
  1143 }
       
  1144 
       
  1145 void tst_QXmlStream::readElementText_data() const
       
  1146 {
       
  1147     QTest::addColumn<QXmlStreamReader::ReadElementTextBehaviour>("behaviour");
       
  1148     QTest::addColumn<QString>("input");
       
  1149     QTest::addColumn<QString>("expected");
       
  1150 
       
  1151     QString validInput("<p>He was <em>never</em> going to admit<!-- TODO: rephrase --> his mistake.</p>");
       
  1152     QString invalidInput("<p>invalid...<p>");
       
  1153     QString invalidOutput("invalid...");
       
  1154 
       
  1155     QTest::newRow("ErrorOnUnexpectedElement")
       
  1156             << QXmlStreamReader::ErrorOnUnexpectedElement
       
  1157             << validInput << QString("He was ");
       
  1158 
       
  1159     QTest::newRow("IncludeChildElements")
       
  1160             << QXmlStreamReader::IncludeChildElements
       
  1161             << validInput << QString("He was never going to admit his mistake.");
       
  1162 
       
  1163     QTest::newRow("SkipChildElements")
       
  1164             << QXmlStreamReader::SkipChildElements
       
  1165             << validInput << QString("He was  going to admit his mistake.");
       
  1166 
       
  1167     QTest::newRow("ErrorOnUnexpectedElement Invalid")
       
  1168             << QXmlStreamReader::ErrorOnUnexpectedElement
       
  1169             << invalidInput << invalidOutput;
       
  1170 
       
  1171     QTest::newRow("IncludeChildElements Invalid")
       
  1172             << QXmlStreamReader::IncludeChildElements
       
  1173             << invalidInput << invalidOutput;
       
  1174 
       
  1175     QTest::newRow("SkipChildElements Invalid")
       
  1176             << QXmlStreamReader::SkipChildElements
       
  1177             << invalidInput << invalidOutput;
       
  1178 }
       
  1179 
       
  1180 void tst_QXmlStream::crashInUTF16Codec() const
       
  1181 {
       
  1182     QEventLoop eventLoop;
       
  1183 
       
  1184     QNetworkAccessManager networkManager;
       
  1185     QNetworkRequest request(QUrl::fromLocalFile(QLatin1String("data/051reduced.xml")));
       
  1186     QNetworkReply *const reply = networkManager.get(request);
       
  1187     eventLoop.connect(reply, SIGNAL(finished()), SLOT(quit()));
       
  1188 
       
  1189     QCOMPARE(eventLoop.exec(), 0);
       
  1190 
       
  1191     QXmlStreamReader reader(reply);
       
  1192     while(!reader.atEnd())
       
  1193     {
       
  1194         reader.readNext();
       
  1195         continue;
       
  1196     }
       
  1197 
       
  1198     QVERIFY(!reader.hasError());
       
  1199 }
       
  1200 
       
  1201 /*
       
  1202   In addition to QTestLib's flags, one can specify "-c <filename>" and have that file output in its canonical form.
       
  1203 */
       
  1204 int main(int argc, char *argv[])
       
  1205 {
       
  1206     QCoreApplication app(argc, argv);
       
  1207 
       
  1208     if (argc == 3 && QByteArray(argv[1]).startsWith("-c")) {
       
  1209         // output canonical only
       
  1210         bool error = false;
       
  1211         QByteArray canonical = makeCanonical(argv[2], "doc", error);
       
  1212         QTextStream myStdOut(stdout);
       
  1213         myStdOut << canonical << endl;
       
  1214         exit(0);
       
  1215     }
       
  1216 
       
  1217     tst_QXmlStream tc;
       
  1218     return QTest::qExec(&tc, argc, argv);
       
  1219 }
       
  1220 
       
  1221 void tst_QXmlStream::hasAttributeSignature() const
       
  1222 {
       
  1223     /* These functions should be const so invoke all
       
  1224      * of them on a const object. */
       
  1225     const QXmlStreamAttributes atts;
       
  1226     atts.hasAttribute(QLatin1String("localName"));
       
  1227     atts.hasAttribute(QString::fromLatin1("localName"));
       
  1228     atts.hasAttribute(QString::fromLatin1("http://example.com/"), QLatin1String("localName"));
       
  1229 
       
  1230     /* The input arguments should be const references, not mutable references
       
  1231      * so pass const references. */
       
  1232     const QLatin1String latin1StringLocalName(QLatin1String("localName"));
       
  1233     const QString qStringLocalname(QLatin1String("localName"));
       
  1234     const QString namespaceURI(QLatin1String("http://example.com/"));
       
  1235 
       
  1236     /* QLatin1String overload. */
       
  1237     atts.hasAttribute(latin1StringLocalName);
       
  1238 
       
  1239     /* QString overload. */
       
  1240     atts.hasAttribute(latin1StringLocalName);
       
  1241 
       
  1242     /* namespace/local name overload. */
       
  1243     atts.hasAttribute(namespaceURI, qStringLocalname);
       
  1244 }
       
  1245 
       
  1246 void tst_QXmlStream::hasAttribute() const
       
  1247 {
       
  1248     QXmlStreamReader reader(QLatin1String("<e xmlns:p='http://example.com/2' xmlns='http://example.com/' "
       
  1249                                           "attr1='value' attr2='value2' p:attr3='value3' emptyAttr=''><noAttributes/></e>"));
       
  1250 
       
  1251     QCOMPARE(reader.readNext(), QXmlStreamReader::StartDocument);
       
  1252     QCOMPARE(reader.readNext(), QXmlStreamReader::StartElement);
       
  1253     const QXmlStreamAttributes &atts = reader.attributes();
       
  1254 
       
  1255     /* QLatin1String overload. */
       
  1256     QVERIFY(atts.hasAttribute(QLatin1String("attr1")));
       
  1257     QVERIFY(atts.hasAttribute(QLatin1String("attr2")));
       
  1258     QVERIFY(atts.hasAttribute(QLatin1String("p:attr3")));
       
  1259     QVERIFY(atts.hasAttribute(QLatin1String("emptyAttr")));
       
  1260     QVERIFY(!atts.hasAttribute(QLatin1String("DOESNOTEXIST")));
       
  1261 
       
  1262     /* Test with an empty & null namespaces. */
       
  1263     QVERIFY(atts.hasAttribute(QString(), QLatin1String("attr2"))); /* A null string. */
       
  1264     QVERIFY(atts.hasAttribute(QLatin1String(""), QLatin1String("attr2"))); /* An empty string. */
       
  1265 
       
  1266     /* QString overload. */
       
  1267     QVERIFY(atts.hasAttribute(QString::fromLatin1("attr1")));
       
  1268     QVERIFY(atts.hasAttribute(QString::fromLatin1("attr2")));
       
  1269     QVERIFY(atts.hasAttribute(QString::fromLatin1("p:attr3")));
       
  1270     QVERIFY(atts.hasAttribute(QString::fromLatin1("emptyAttr")));
       
  1271     QVERIFY(!atts.hasAttribute(QString::fromLatin1("DOESNOTEXIST")));
       
  1272 
       
  1273     /* namespace/local name overload. */
       
  1274     QVERIFY(atts.hasAttribute(QString(), QString::fromLatin1("attr1")));
       
  1275     /* Attributes do not pick up the default namespace. */
       
  1276     QVERIFY(!atts.hasAttribute(QLatin1String("http://example.com/"), QString::fromLatin1("attr1")));
       
  1277     QVERIFY(atts.hasAttribute(QLatin1String("http://example.com/2"), QString::fromLatin1("attr3")));
       
  1278     QVERIFY(atts.hasAttribute(QString(), QString::fromLatin1("emptyAttr")));
       
  1279     QVERIFY(!atts.hasAttribute(QLatin1String("http://example.com/2"), QString::fromLatin1("DOESNOTEXIST")));
       
  1280     QVERIFY(!atts.hasAttribute(QLatin1String("WRONG_NAMESPACE"), QString::fromLatin1("attr3")));
       
  1281 
       
  1282     /* Invoke on an QXmlStreamAttributes that has no attributes at all. */
       
  1283     QCOMPARE(reader.readNext(), QXmlStreamReader::StartElement);
       
  1284 
       
  1285     const QXmlStreamAttributes &atts2 = reader.attributes();
       
  1286     QVERIFY(atts2.isEmpty());
       
  1287 
       
  1288     /* QLatin1String overload. */
       
  1289     QVERIFY(!atts.hasAttribute(QLatin1String("arbitraryName")));
       
  1290 
       
  1291     /* QString overload. */
       
  1292     QVERIFY(!atts.hasAttribute(QString::fromLatin1("arbitraryName")));
       
  1293 
       
  1294     /* namespace/local name overload. */
       
  1295     QVERIFY(!atts.hasAttribute(QLatin1String("http://example.com/"), QString::fromLatin1("arbitraryName")));
       
  1296 
       
  1297     while(!reader.atEnd())
       
  1298         reader.readNext();
       
  1299 
       
  1300     QVERIFY(!reader.hasError());
       
  1301 }
       
  1302 
       
  1303 
       
  1304 void tst_QXmlStream::writeWithCodec() const
       
  1305 {
       
  1306 
       
  1307     QByteArray outarray;
       
  1308     QXmlStreamWriter writer(&outarray);
       
  1309     writer.setAutoFormatting(true);
       
  1310 
       
  1311     QTextCodec *codec = QTextCodec::codecForName("ISO 8859-15");
       
  1312     QVERIFY(codec);
       
  1313     writer.setCodec(codec);
       
  1314 
       
  1315     const char *latin2 = "hé hé";
       
  1316     const QString string = codec->toUnicode(latin2);
       
  1317 
       
  1318 
       
  1319     writer.writeStartDocument("1.0");
       
  1320 
       
  1321     writer.writeTextElement("foo", string);
       
  1322     writer.writeEndElement();
       
  1323     writer.writeEndDocument();
       
  1324 
       
  1325     QVERIFY(outarray.contains(latin2));
       
  1326     QVERIFY(outarray.contains(codec->name()));
       
  1327 }
       
  1328 
       
  1329 void tst_QXmlStream::writeWithStandalone() const
       
  1330 {
       
  1331     {
       
  1332         QByteArray outarray;
       
  1333         QXmlStreamWriter writer(&outarray);
       
  1334         writer.setAutoFormatting(true);
       
  1335         writer.writeStartDocument("1.0", true);
       
  1336         writer.writeEndDocument();
       
  1337         const char *ref = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n";
       
  1338         QCOMPARE(outarray.constData(), ref);
       
  1339     }
       
  1340     {
       
  1341         QByteArray outarray;
       
  1342         QXmlStreamWriter writer(&outarray);
       
  1343         writer.setAutoFormatting(true);
       
  1344         writer.writeStartDocument("1.0", false);
       
  1345         writer.writeEndDocument();
       
  1346         const char *ref = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n";
       
  1347         QCOMPARE(outarray.constData(), ref);
       
  1348     }
       
  1349 }
       
  1350 
       
  1351 void tst_QXmlStream::entitiesAndWhitespace_1() const
       
  1352 {
       
  1353     QXmlStreamReader reader(QLatin1String("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\"><test>&extEnt;</test>"));
       
  1354 
       
  1355     int entityCount = 0;
       
  1356     int characterCount = 0;
       
  1357     while(!reader.atEnd())
       
  1358     {
       
  1359         QXmlStreamReader::TokenType token = reader.readNext();
       
  1360         switch(token)
       
  1361         {
       
  1362             case QXmlStreamReader::Characters:
       
  1363                 characterCount++;
       
  1364                 break;
       
  1365             case QXmlStreamReader::EntityReference:
       
  1366                 entityCount++;
       
  1367                 break;
       
  1368             default:
       
  1369                 ;
       
  1370         }
       
  1371     }
       
  1372 
       
  1373     QCOMPARE(entityCount, 1);
       
  1374     QCOMPARE(characterCount, 0);
       
  1375     QVERIFY(!reader.hasError());
       
  1376 }
       
  1377 
       
  1378 void tst_QXmlStream::entitiesAndWhitespace_2() const
       
  1379 {
       
  1380     QXmlStreamReader reader(QLatin1String("<test>&extEnt;</test>"));
       
  1381 
       
  1382     int entityCount = 0;
       
  1383     int characterCount = 0;
       
  1384     while(!reader.atEnd())
       
  1385     {
       
  1386         QXmlStreamReader::TokenType token = reader.readNext();
       
  1387         switch(token)
       
  1388         {
       
  1389             case QXmlStreamReader::Characters:
       
  1390                 characterCount++;
       
  1391                 break;
       
  1392             case QXmlStreamReader::EntityReference:
       
  1393                 entityCount++;
       
  1394                 break;
       
  1395             default:
       
  1396                 ;
       
  1397         }
       
  1398     }
       
  1399 
       
  1400     QCOMPARE(entityCount, 0);
       
  1401     QCOMPARE(characterCount, 0);
       
  1402     QVERIFY(reader.hasError());
       
  1403 }
       
  1404 
       
  1405 void tst_QXmlStream::garbageInXMLPrologDefaultCodec() const
       
  1406 {
       
  1407     QBuffer out;
       
  1408     QVERIFY(out.open(QIODevice::ReadWrite));
       
  1409 
       
  1410     QXmlStreamWriter writer (&out);
       
  1411     writer.writeStartDocument();
       
  1412     writer.writeEmptyElement("Foo");
       
  1413     writer.writeEndDocument();
       
  1414 
       
  1415     QCOMPARE(out.data(), QByteArray("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Foo/>\n"));
       
  1416 }
       
  1417 
       
  1418 void tst_QXmlStream::garbageInXMLPrologUTF8Explicitly() const
       
  1419 {
       
  1420     QBuffer out;
       
  1421     QVERIFY(out.open(QIODevice::ReadWrite));
       
  1422 
       
  1423     QXmlStreamWriter writer (&out);
       
  1424     writer.setCodec("UTF-8");
       
  1425     writer.writeStartDocument();
       
  1426     writer.writeEmptyElement("Foo");
       
  1427     writer.writeEndDocument();
       
  1428 
       
  1429     QCOMPARE(out.data(), QByteArray("<?xml version=\"1.0\" encoding=\"UTF-8\"?><Foo/>\n"));
       
  1430 }
       
  1431 
       
  1432 void tst_QXmlStream::clear() const // task 228768
       
  1433 {
       
  1434     QString xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><body></body>";
       
  1435     QXmlStreamReader reader;
       
  1436 
       
  1437     reader.addData(xml);
       
  1438     while (!reader.atEnd()) {
       
  1439         reader.readNext();
       
  1440     }
       
  1441     QCOMPARE(reader.tokenType(), QXmlStreamReader::EndDocument);
       
  1442 
       
  1443     reader.clear();
       
  1444     reader.addData(xml);
       
  1445     while (!reader.atEnd()) {
       
  1446         reader.readNext();
       
  1447     }
       
  1448     QCOMPARE(reader.tokenType(), QXmlStreamReader::EndDocument);
       
  1449 
       
  1450 
       
  1451     // now we stop in the middle to check whether clear really works
       
  1452     reader.clear();
       
  1453     reader.addData(xml);
       
  1454     reader.readNext();
       
  1455     reader.readNext();
       
  1456     QCOMPARE(reader.tokenType(), QXmlStreamReader::StartElement);
       
  1457 
       
  1458     // and here the final read
       
  1459     reader.clear();
       
  1460     reader.addData(xml);
       
  1461     while (!reader.atEnd()) {
       
  1462         reader.readNext();
       
  1463     }
       
  1464     QCOMPARE(reader.tokenType(), QXmlStreamReader::EndDocument);
       
  1465 }
       
  1466 
       
  1467 void tst_QXmlStream::checkCommentIndentation_data() const
       
  1468 {
       
  1469 
       
  1470     QTest::addColumn<QString>("input");
       
  1471     QTest::addColumn<QString>("expectedOutput");
       
  1472 
       
  1473     QString simpleInput = "<a><!-- bla --></a>";
       
  1474     QString simpleOutput = "<?xml version=\"1.0\"?>\n"
       
  1475                            "<a>\n"
       
  1476                            "   <!-- bla -->\n"
       
  1477                            "</a>\n";
       
  1478     QTest::newRow("simple-comment") << simpleInput << simpleOutput;
       
  1479 
       
  1480     QString advancedInput = "<a><!-- bla --><!-- bla --><b><!-- bla --><c><!-- bla --></c><!-- bla --></b></a>";
       
  1481     QString advancedOutput = "<?xml version=\"1.0\"?>\n"
       
  1482                            "<a>\n"
       
  1483                            "   <!-- bla -->\n"
       
  1484                            "   <!-- bla -->\n"
       
  1485                            "   <b>\n"
       
  1486                            "      <!-- bla -->\n"
       
  1487                            "      <c>\n"
       
  1488                            "         <!-- bla -->\n"
       
  1489                            "      </c>\n"
       
  1490                            "      <!-- bla -->\n"
       
  1491                            "   </b>\n"
       
  1492                            "</a>\n";
       
  1493     QTest::newRow("advanced-comment") << advancedInput << advancedOutput;
       
  1494 }
       
  1495 
       
  1496 void tst_QXmlStream::checkCommentIndentation() const // task 256468
       
  1497 {
       
  1498     QFETCH(QString, input);
       
  1499     QFETCH(QString, expectedOutput);
       
  1500     QString output;
       
  1501     QXmlStreamReader reader(input);
       
  1502     QXmlStreamWriter writer(&output);
       
  1503     writer.setAutoFormatting(true);
       
  1504     writer.setAutoFormattingIndent(3);
       
  1505 
       
  1506     while (!reader.atEnd()) {
       
  1507         reader.readNext();
       
  1508         if (reader.error()) {
       
  1509             QFAIL("error reading XML input");
       
  1510         } else {
       
  1511             writer.writeCurrentToken(reader);
       
  1512         }
       
  1513     }
       
  1514     QCOMPARE(output, expectedOutput);
       
  1515 }
       
  1516 
       
  1517 #include "tst_qxmlstream.moc"
       
  1518 // vim: et:ts=4:sw=4:sts=4