|
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 |