1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QDomDocument>
31#include <qthread.h>
32#include <qtcpserver.h>
33#include <qtcpsocket.h>
34#include <QtTest/QtTest>
35#include <QtCore/qatomic.h>
36#include <QtCore/qsemaphore.h>
37#include <qfile.h>
38#include <qstring.h>
39#include <qdir.h>
40#include <qbuffer.h>
41#include "parser/parser.h"
42
43static const char *const inputString = "<!DOCTYPE inferno [<!ELEMENT inferno (circle+)><!ELEMENT circle (#PCDATA)>]><inferno><circle /><circle /></inferno>";
44static const char *const refString = "setDocumentLocator(locator={columnNumber=1, lineNumber=1})\nstartDocument()\nstartDTD(name=\"inferno\", publicId=\"\", systemId=\"\")\nendDTD()\nstartElement(namespaceURI=\"\", localName=\"inferno\", qName=\"inferno\", atts=[])\nstartElement(namespaceURI=\"\", localName=\"circle\", qName=\"circle\", atts=[])\nendElement(namespaceURI=\"\", localName=\"circle\", qName=\"circle\")\nstartElement(namespaceURI=\"\", localName=\"circle\", qName=\"circle\", atts=[])\nendElement(namespaceURI=\"\", localName=\"circle\", qName=\"circle\")\nendElement(namespaceURI=\"\", localName=\"inferno\", qName=\"inferno\")\nendDocument()\n";
45
46#define TEST_PORT 1088
47
48class XmlServer : public QThread
49{
50 Q_OBJECT
51public:
52 XmlServer(QObject *parent = 0) : QThread(parent) {}
53
54 QSemaphore threadStarted;
55 bool listening = false;
56 QAtomicInt quitSoon;
57
58protected:
59 virtual void run();
60};
61
62#define CHUNK_SIZE 2048
63
64void XmlServer::run()
65{
66 QTcpServer srv;
67
68 listening = srv.listen(address: QHostAddress::Any, TEST_PORT);
69 threadStarted.release();
70
71 if (!listening) {
72 qWarning() << "Failed to listen on" << TEST_PORT << srv.errorString();
73 return;
74 }
75
76 for (;;) {
77 srv.waitForNewConnection(msec: 100);
78
79 if (QTcpSocket *sock = srv.nextPendingConnection()) {
80 QByteArray fileName;
81 for (;;) {
82 char c;
83 if (sock->getChar(c: &c)) {
84 if (c == '\n')
85 break;
86 fileName.append(c);
87 } else {
88 if (!sock->waitForReadyRead(msecs: -1))
89 break;
90 }
91 }
92
93 QFile file(QString::fromLocal8Bit(str: fileName));
94 if (!file.open(flags: QIODevice::ReadOnly)) {
95 qWarning() << "XmlServer::run(): could not open" << fileName;
96 sock->abort();
97 delete sock;
98 continue;
99 }
100
101 QByteArray data = file.readAll();
102 for (int i = 0; i < data.size();) {
103 int cnt = qMin(CHUNK_SIZE, b: data.size() - i);
104 sock->write(data: data.constData() + i, len: cnt);
105 i += cnt;
106 sock->flush();
107 QTest::qSleep(ms: 1);
108
109 if (quitSoon.loadAcquire()) {
110 sock->abort();
111 break;
112 }
113 }
114
115 sock->disconnectFromHost();
116 delete sock;
117 }
118
119 if (quitSoon.loadAcquire())
120 break;
121 }
122
123 srv.close();
124}
125
126class tst_QXmlSimpleReader : public QObject
127{
128 Q_OBJECT
129
130#if QT_DEPRECATED_SINCE(5, 15)
131 public:
132 tst_QXmlSimpleReader();
133 ~tst_QXmlSimpleReader();
134
135 private slots:
136 void initTestCase();
137 void testGoodXmlFile();
138 void testGoodXmlFile_data();
139 void testBadXmlFile();
140 void testBadXmlFile_data();
141 void testIncrementalParsing();
142 void testIncrementalParsing_data();
143 void setDataQString();
144 void inputFromQIODevice();
145 void inputFromString();
146 void inputFromSocket_data();
147 void inputFromSocket();
148
149 void idsInParseException1();
150 void idsInParseException2();
151 void preserveCharacterReferences() const;
152 void reportNamespace() const;
153 void reportNamespace_data() const;
154 void roundtripWithNamespaces() const;
155 void dtdRecursionLimit();
156
157 private:
158 static QDomDocument fromByteArray(const QString &title, const QByteArray &ba, bool *ok);
159 XmlServer *server;
160 QString prefix;
161#endif // QT_DEPRECATED_SINCE(5, 15)
162};
163
164#if QT_DEPRECATED_SINCE(5, 15)
165QT_WARNING_PUSH
166QT_WARNING_DISABLE_DEPRECATED
167
168tst_QXmlSimpleReader::tst_QXmlSimpleReader() : server(new XmlServer(this))
169{
170 server->start();
171}
172
173tst_QXmlSimpleReader::~tst_QXmlSimpleReader()
174{
175 server->quitSoon.storeRelease(newValue: 1);
176 server->wait();
177}
178
179class MyErrorHandler : public QXmlErrorHandler
180{
181public:
182 QString publicId;
183 QString systemId;
184
185 virtual bool error(const QXmlParseException &)
186 {
187 return false;
188 }
189
190 virtual QString errorString() const
191 {
192 return QString();
193 }
194
195 virtual bool fatalError(const QXmlParseException &exception)
196 {
197 publicId = exception.publicId();
198 systemId = exception.systemId();
199 return true;
200 }
201
202 virtual bool warning(const QXmlParseException &)
203 {
204 return true;
205 }
206
207};
208
209void tst_QXmlSimpleReader::initTestCase()
210{
211 prefix = QFileInfo(QFINDTESTDATA("xmldocs")).absolutePath();
212 if (prefix.isEmpty())
213 QFAIL("Cannot find xmldocs testdata!");
214 QDir::setCurrent(prefix);
215}
216
217void tst_QXmlSimpleReader::idsInParseException1()
218{
219 MyErrorHandler handler;
220 QXmlSimpleReader reader;
221
222 reader.setErrorHandler(&handler);
223
224 /* A non-wellformed XML document with PUBLIC and SYSTEM. */
225 QByteArray input("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" "
226 "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
227 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">"
228 "<head>"
229 "<a/><a/><a/>"
230 "<head/>");
231
232 QBuffer buff(&input);
233 QXmlInputSource source(&buff);
234
235 /* Yes, parsing should be reported as a failure. */
236 QVERIFY(!reader.parse(source));
237
238 QCOMPARE(handler.publicId, QString::fromLatin1("-//W3C//DTD XHTML 1.0 Strict//EN"));
239 QCOMPARE(handler.systemId, QString::fromLatin1("http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"));
240}
241
242void tst_QXmlSimpleReader::idsInParseException2()
243{
244 MyErrorHandler handler;
245 QXmlSimpleReader reader;
246
247 reader.setErrorHandler(&handler);
248
249 /* A non-wellformed XML document with only SYSTEM. */
250 QByteArray input("<!DOCTYPE html SYSTEM \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">"
251 "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">"
252 "<head>"
253 "<a/><a/><a/>"
254 "<head/>");
255
256 QBuffer buff(&input);
257 QXmlInputSource source(&buff);
258
259 /* Yes, parsing should be reported as a failure. */
260 QVERIFY(!reader.parse(source));
261
262 QCOMPARE(handler.publicId, QString());
263 QCOMPARE(handler.systemId, QString::fromLatin1("http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"));
264}
265
266static QStringList findXmlFiles(QString dir_name)
267{
268 QStringList result;
269
270 dir_name = QFINDTESTDATA(dir_name);
271 QDir dir(dir_name);
272 QFileInfoList file_list = dir.entryInfoList(nameFilters: QStringList("*.xml"), filters: QDir::Files, sort: QDir::Name);
273
274 QFileInfoList::const_iterator it = file_list.begin();
275 for (; it != file_list.end(); ++it) {
276 const QFileInfo &file_info = *it;
277 result.append(t: file_info.filePath());
278 }
279
280 return result;
281}
282
283
284void tst_QXmlSimpleReader::testGoodXmlFile_data()
285{
286 const char * const good_data_dirs[] = {
287 "xmldocs/valid/sa",
288 "xmldocs/valid/not-sa",
289 "xmldocs/valid/ext-sa",
290 0
291 };
292 const char * const *d = good_data_dirs;
293
294 QStringList good_file_list;
295 for (; *d != 0; ++d)
296 good_file_list += findXmlFiles(dir_name: *d);
297
298 QTest::addColumn<QString>(name: "file_name");
299 QStringList::const_iterator it = good_file_list.begin();
300 for (; it != good_file_list.end(); ++it)
301 QTest::newRow(dataTag: (*it).toLatin1()) << *it;
302}
303
304void tst_QXmlSimpleReader::testGoodXmlFile()
305{
306 QFETCH(QString, file_name);
307 QFile file(file_name);
308 QVERIFY(file.open(QIODevice::ReadOnly));
309 QString content = file.readAll();
310 file.close();
311 QVERIFY(file.open(QIODevice::ReadOnly));
312 Parser parser;
313
314 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/valid/sa/089.xml").toLocal8Bit().constData(), "a form feed character is not accepted in XML", Continue);
315 QVERIFY(parser.parseFile(&file));
316
317 QFile ref_file(file_name + ".ref");
318 QVERIFY(ref_file.open(QIODevice::ReadOnly | QIODevice::Text));
319 QTextStream ref_stream(&ref_file);
320 ref_stream.setCodec("UTF-8");
321 QString ref_file_contents = ref_stream.readAll();
322
323 QCOMPARE(parser.result(), ref_file_contents);
324}
325
326void tst_QXmlSimpleReader::testBadXmlFile_data()
327{
328 const char * const bad_data_dirs[] = {
329 "xmldocs/not-wf/sa",
330 0
331 };
332 const char * const *d = bad_data_dirs;
333
334 QStringList bad_file_list;
335 for (; *d != 0; ++d)
336 bad_file_list += findXmlFiles(dir_name: *d);
337
338 QTest::addColumn<QString>(name: "file_name");
339 QStringList::const_iterator it = bad_file_list.begin();
340 for (; it != bad_file_list.end(); ++it)
341 QTest::newRow(dataTag: (*it).toLatin1()) << *it;
342}
343
344void tst_QXmlSimpleReader::testBadXmlFile()
345{
346 QFETCH(QString, file_name);
347 QFile file(file_name);
348 QVERIFY(file.open(QIODevice::ReadOnly));
349 Parser parser;
350
351 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/030.xml").toLocal8Bit().constData(), "a form feed character is not accepted in XML", Continue);
352 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/031.xml").toLocal8Bit().constData(), "a form feed character is not accepted in a processing instruction", Continue);
353 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/032.xml").toLocal8Bit().constData(), "a form feed character is not accepted in a comment", Continue);
354 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/033.xml").toLocal8Bit().constData(), "overlong sequence - small latin letter d should be rejected", Continue);
355 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/038.xml").toLocal8Bit().constData(), "attribute x redefined; should be rejected", Continue);
356 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/072.xml").toLocal8Bit().constData(), "entity foo not defined", Continue);
357 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/073.xml").toLocal8Bit().constData(), "entity f not defined", Continue);
358 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/074.xml").toLocal8Bit().constData(), "entity e is not well-formed (</foo><foo>)", Continue);
359 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/076.xml").toLocal8Bit().constData(), "entity foo is not defined", Continue);
360 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/077.xml").toLocal8Bit().constData(), "entity bar is not defined within the definition of entity foo", Continue);
361 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/078.xml").toLocal8Bit().constData(), "entity foo not defined", Continue);
362 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/085.xml").toLocal8Bit().constData(), "Unfinished Public or System Id", Continue);
363 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/086.xml").toLocal8Bit().constData(), "Unfinished Public or System Id", Continue);
364 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/087.xml").toLocal8Bit().constData(), "Unfinished Public or System Id", Continue);
365 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/101.xml").toLocal8Bit().constData(), "Invalid XML encoding name (space before utf-8)", Continue);
366 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/102.xml").toLocal8Bit().constData(), "Invalid version specification (1.0 followed by space)", Continue);
367 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/104.xml").toLocal8Bit().constData(), "Premature end of data in tag foo", Continue);
368 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/116.xml").toLocal8Bit().constData(), "Invalid decimal value", Continue);
369 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/117.xml").toLocal8Bit().constData(), "No name", Continue);
370 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/119.xml").toLocal8Bit().constData(), "No name", Continue);
371 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/122.xml").toLocal8Bit().constData(), "; expected in declaration of element", Continue);
372 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/132.xml").toLocal8Bit().constData(), "; expected in declaration of element", Continue);
373 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/142.xml").toLocal8Bit().constData(), "Invalid value '0'", Continue);
374 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/143.xml").toLocal8Bit().constData(), "Invalid value '31'", Continue);
375
376 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/144.xml").toLocal8Bit().constData(), "noncharacter code 0xFFFF should be rejected", Continue);
377 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/145.xml").toLocal8Bit().constData(), "surrogate code point 0xD800 should be rejected", Continue);
378 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/146.xml").toLocal8Bit().constData(), "code point out-of-range 0x110000 (must be < 0x10FFFE)", Abort);
379 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/160.xml").toLocal8Bit().constData(), "Parameter references forbidden in internal subset", Continue);
380 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/162.xml").toLocal8Bit().constData(), "Parameter references forbidden in internal subset", Continue);
381
382 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/168.xml").toLocal8Bit().constData(), "Surrogate code point 0xEDA080 should be rejected", Continue);
383 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/169.xml").toLocal8Bit().constData(), "Surrogate code point 0xEDB080 should be rejected", Continue);
384 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/170.xml").toLocal8Bit().constData(), "Code point 0xF7808080 should be rejected", Continue);
385
386 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/180.xml").toLocal8Bit().constData(), "Entity e is not defined", Continue);
387 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/181.xml").toLocal8Bit().constData(), "Unregistered error message", Continue);
388 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/182.xml").toLocal8Bit().constData(), "Comment not terminated", Continue);
389 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/185.xml").toLocal8Bit().constData(), "Entity e not defined", Continue);
390 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/186.xml").toLocal8Bit().constData(), "Attributes constructs error", Continue);
391
392 QVERIFY(!parser.parseFile(&file));
393
394 QFile ref_file(file_name + ".ref");
395 QVERIFY(ref_file.open(QIODevice::ReadOnly | QIODevice::Text));
396 QTextStream ref_stream(&ref_file);
397 ref_stream.setCodec("UTF-8");
398 QString ref_file_contents = ref_stream.readAll();
399
400 QEXPECT_FAIL(QFINDTESTDATA("xmldocs/not-wf/sa/145.xml").toLocal8Bit().constData(), "Surrogate code point 0xD800 should be rejected", Continue);
401
402 QCOMPARE(parser.result(), ref_file_contents);
403}
404
405void tst_QXmlSimpleReader::testIncrementalParsing_data()
406{
407 QTest::addColumn<QString>(name: "file_name");
408 QTest::addColumn<int>(name: "chunkSize");
409
410 const char * const good_data_dirs[] = {
411 "xmldocs/valid/sa",
412 "xmldocs/valid/not-sa",
413 "xmldocs/valid/ext-sa",
414 0
415 };
416 const char * const *d = good_data_dirs;
417
418 QStringList good_file_list;
419 for (; *d != 0; ++d)
420 good_file_list += findXmlFiles(dir_name: *d);
421
422 for (int i=1; i<10; ++i) {
423 QStringList::const_iterator it = good_file_list.begin();
424 const QString skip49 = QFINDTESTDATA("xmldocs/valid/sa/049.xml");
425 const QString skip50 = QFINDTESTDATA("xmldocs/valid/sa/050.xml");
426 const QString skip51 = QFINDTESTDATA("xmldocs/valid/sa/051.xml");
427 const QString skip52 = QFINDTESTDATA("xmldocs/valid/sa/052.xml");
428 const QString skip89 = QFINDTESTDATA("xmldocs/valid/sa/089.xml");
429
430 for (; it != good_file_list.end(); ++it) {
431 if ( *it == skip89 )
432 continue;// TODO: fails at the moment -- don't bother
433 if ( i==1 && (
434 *it == skip49 ||
435 *it == skip50 ||
436 *it == skip51 ||
437 *it == skip52 ) ) {
438 continue; // TODO: fails at the moment -- don't bother
439 }
440 QTest::newRow(dataTag: QString("%1 %2").arg(a: *it).arg(a: i).toLatin1()) << *it << i;
441 }
442 }
443}
444
445void tst_QXmlSimpleReader::testIncrementalParsing()
446{
447 QFETCH(QString, file_name);
448 QFETCH(int, chunkSize);
449
450 QFile file(file_name);
451 QVERIFY(file.open(QIODevice::ReadOnly));
452
453 Parser parser;
454 QXmlInputSource source;
455 bool first = true;
456 while (!file.atEnd()) {
457 source.setData(file.read(maxlen: chunkSize));
458 if(first) {
459 QVERIFY(parser.parse(&source, true));
460 first = false;
461 } else {
462 QVERIFY(parser.parseContinue());
463 }
464 }
465 // detect end of document
466 QVERIFY(parser.parseContinue());
467 // parsing should fail after the end of the document was reached
468 QVERIFY(!parser.parseContinue());
469
470 QFile ref_file(file_name + ".ref");
471 QVERIFY(ref_file.open(QIODevice::ReadOnly | QIODevice::Text));
472 QTextStream ref_stream(&ref_file);
473 ref_stream.setCodec("UTF-8");
474 QString ref_file_contents = ref_stream.readAll();
475
476 QCOMPARE(parser.result(), ref_file_contents);
477}
478
479void tst_QXmlSimpleReader::setDataQString()
480{
481 QString input = inputString;
482 QString ref = refString;
483
484 QXmlInputSource source;
485 Parser parser;
486
487 source.setData(input);
488 QVERIFY(parser.parse(&source,false));
489
490 QBuffer resultBuffer;
491 resultBuffer.setData(parser.result().toLatin1());
492
493 QBuffer refBuffer;
494 refBuffer.setData(ref.toLatin1());
495
496 resultBuffer.open(openMode: QIODevice::ReadOnly);
497 refBuffer.open(openMode: QIODevice::ReadOnly);
498
499 bool success = true;
500 while (resultBuffer.canReadLine()) {
501 if (!refBuffer.canReadLine()) {
502 success = false; break ;
503 }
504 if (resultBuffer.readLine().simplified() != refBuffer.readLine().simplified()) {
505 success = false; break ;
506 }
507 }
508 QVERIFY(success);
509}
510
511void tst_QXmlSimpleReader::inputFromQIODevice()
512{
513 QBuffer inputBuffer;
514 inputBuffer.setData(inputString);
515
516 QXmlInputSource source(&inputBuffer);
517 Parser parser;
518
519 QVERIFY(parser.parse(&source,false));
520
521 QBuffer resultBuffer;
522 resultBuffer.setData(parser.result().toLatin1());
523
524 QBuffer refBuffer;
525 refBuffer.setData(refString);
526
527 resultBuffer.open(openMode: QIODevice::ReadOnly);
528 refBuffer.open(openMode: QIODevice::ReadOnly);
529
530 bool success = true;
531 while (resultBuffer.canReadLine()) {
532 if (!refBuffer.canReadLine()) {
533 success = false; break ;
534 }
535 if (resultBuffer.readLine().simplified() != refBuffer.readLine().simplified()) {
536 success = false; break ;
537 }
538 }
539 QVERIFY(success);
540}
541
542void tst_QXmlSimpleReader::inputFromString()
543{
544 QString str = "<foo><bar>kake</bar><bar>ja</bar></foo>";
545 QBuffer buff;
546 buff.setData(adata: (char*)str.utf16(), alen: str.size()*sizeof(ushort));
547
548 QXmlInputSource input(&buff);
549
550 QXmlSimpleReader reader;
551 QXmlDefaultHandler handler;
552 reader.setContentHandler(&handler);
553
554 QVERIFY(reader.parse(&input));
555}
556
557void tst_QXmlSimpleReader::inputFromSocket_data()
558{
559 QStringList files = findXmlFiles(dir_name: QLatin1String("encodings"));
560 QVERIFY(files.count() > 0);
561
562 QTest::addColumn<QString>(name: "file_name");
563
564 foreach (const QString &file_name, files)
565 QTest::newRow(dataTag: file_name.toLatin1()) << file_name;
566}
567
568void tst_QXmlSimpleReader::inputFromSocket()
569{
570 QFETCH(QString, file_name);
571#ifdef Q_OS_WINRT
572 QSKIP("WinRT does not support connecting to localhost");
573#endif
574
575 if (!server->threadStarted.tryAcquire(n: 1, timeout: 15000)) {
576 // If something is wrong with QThreads, it's not a reason to fail
577 // XML-test, we are not testing QThread here after all!
578 QSKIP("XmlServer/thread has not started yet");
579 }
580
581 // Subsequent runs should be able to acquire the semaphore.
582 server->threadStarted.release(n: 1);
583
584 if (!server->listening) {
585 // Again, QTcpServer is not the subject of this test!
586 QSKIP("QTcpServer::listen failed, bailing out");
587 }
588
589 QTcpSocket sock;
590 sock.connectToHost(address: QHostAddress::LocalHost, TEST_PORT);
591 QVERIFY2(sock.waitForConnected(),
592 qPrintable(QStringLiteral("Cannot connect on port ") + QString::number(TEST_PORT)
593 + QStringLiteral(": ") + sock.errorString()));
594
595 sock.write(data: file_name.toLocal8Bit() + "\n");
596 QVERIFY(sock.waitForBytesWritten());
597
598 QXmlInputSource input(&sock);
599
600 QXmlSimpleReader reader;
601 QXmlDefaultHandler handler;
602 reader.setContentHandler(&handler);
603
604 QVERIFY(reader.parse(&input));
605
606// qDebug() << "tst_QXmlSimpleReader::inputFromSocket(): success" << file_name;
607}
608
609void tst_QXmlSimpleReader::preserveCharacterReferences() const
610{
611 class Handler : public QXmlDefaultHandler
612 {
613 public:
614 virtual bool characters(const QString &chars)
615 {
616 received = chars;
617 return true;
618 }
619
620 QString received;
621 };
622
623 {
624 QByteArray input("<e>A&#160;&#160;&#160;&#160;A</e>");
625
626 QBuffer buff(&input);
627 QXmlInputSource source(&buff);
628
629 Handler h;
630 QXmlSimpleReader reader;
631 reader.setContentHandler(&h);
632 QVERIFY(reader.parse(&source, false));
633
634 QCOMPARE(h.received, QLatin1Char('A') + QString(4, QChar(160)) + QLatin1Char('A'));
635 }
636
637 {
638 QByteArray input("<e>&#160;&#160;&#160;&#160;</e>");
639
640 QBuffer buff(&input);
641 QXmlInputSource source(&buff);
642
643 Handler h;
644 QXmlSimpleReader reader;
645 reader.setContentHandler(&h);
646 QVERIFY(reader.parse(&source, false));
647
648 QCOMPARE(h.received, QString(4, QChar(160)));
649 }
650}
651
652void tst_QXmlSimpleReader::reportNamespace() const
653{
654 class Handler : public QXmlDefaultHandler
655 {
656 public:
657 virtual bool startElement(const QString &namespaceURI,
658 const QString &localName,
659 const QString &qName,
660 const QXmlAttributes &)
661 {
662 startNamespaceURI = namespaceURI;
663 startLocalName = localName;
664 startQName = qName;
665
666 return true;
667 }
668
669 virtual bool endElement(const QString &namespaceURI,
670 const QString &localName,
671 const QString &qName)
672 {
673 endNamespaceURI = namespaceURI;
674 endLocalName = localName;
675 endQName = qName;
676
677 return true;
678 }
679
680 QString startLocalName;
681 QString startQName;
682 QString startNamespaceURI;
683 QString endLocalName;
684 QString endQName;
685 QString endNamespaceURI;
686 };
687
688 QXmlSimpleReader reader;
689 Handler handler;
690 reader.setContentHandler(&handler);
691
692 QFETCH(QByteArray, input);
693
694 QBuffer buffer(&input);
695 QVERIFY(buffer.open(QIODevice::ReadOnly));
696
697 QXmlInputSource source(&buffer);
698 QVERIFY(reader.parse(source));
699
700 QFETCH(QString, expectedQName);
701 QFETCH(QString, expectedLocalName);
702 QFETCH(QString, expectedNamespace);
703
704 QCOMPARE(handler.startNamespaceURI, expectedNamespace);
705 QCOMPARE(handler.startLocalName, expectedLocalName);
706 QCOMPARE(handler.startQName, expectedQName);
707
708 QCOMPARE(handler.endNamespaceURI, expectedNamespace);
709 QCOMPARE(handler.endLocalName, expectedLocalName);
710 QCOMPARE(handler.endQName, expectedQName);
711}
712
713void tst_QXmlSimpleReader::reportNamespace_data() const
714{
715 QTest::addColumn<QByteArray>(name: "input");
716 QTest::addColumn<QString>(name: "expectedQName");
717 QTest::addColumn<QString>(name: "expectedLocalName");
718 QTest::addColumn<QString>(name: "expectedNamespace");
719
720 QTest::newRow(dataTag: "default ns") << QByteArray("<element xmlns='http://example.com/'/>")
721 << QString("element")
722 << QString("element")
723 << QString("http://example.com/");
724
725 QTest::newRow(dataTag: "with prefix") << QByteArray("<p:element xmlns:p='http://example.com/'/>")
726 << QString("p:element")
727 << QString("element")
728 << QString("http://example.com/");
729}
730
731QDomDocument tst_QXmlSimpleReader::fromByteArray(const QString &title, const QByteArray &ba, bool *ok)
732{
733 QDomDocument doc(title);
734 *ok = doc.setContent(text: ba, namespaceProcessing: true);
735 return doc;
736}
737
738void tst_QXmlSimpleReader::roundtripWithNamespaces() const
739{
740 const char *const expected = "<element b:attr=\"value\" xmlns:a=\"http://www.example.com/A\" xmlns:b=\"http://www.example.com/B\" />\n";
741 bool ok;
742
743 {
744 const char *const xml = "<element xmlns:b=\"http://www.example.com/B\" b:attr=\"value\" xmlns:a=\"http://www.example.com/A\"/>";
745
746 const QDomDocument one(fromByteArray(title: "document", ba: xml, ok: &ok));
747 QVERIFY(ok);
748 const QDomDocument two(fromByteArray(title: "document2", ba: one.toByteArray(2), ok: &ok));
749 QVERIFY(ok);
750
751 QEXPECT_FAIL("", "Known problem, see 154573. The fix happens to break uic.", Abort);
752
753 QCOMPARE(expected, one.toByteArray().constData());
754 QCOMPARE(one.toByteArray(2).constData(), two.toByteArray(2).constData());
755 QCOMPARE(two.toByteArray(2).constData(), two.toByteArray(2).constData());
756 }
757
758 {
759 const char *const xml = "<element b:attr=\"value\" xmlns:b=\"http://www.example.com/B\" xmlns:a=\"http://www.example.com/A\"/>";
760
761 const QDomDocument one(fromByteArray(title: "document", ba: xml, ok: &ok));
762 QVERIFY(ok);
763 const QDomDocument two(fromByteArray(title: "document2", ba: one.toByteArray(2), ok: &ok));
764 QVERIFY(ok);
765
766 QCOMPARE(expected, one.toByteArray().constData());
767 QCOMPARE(one.toByteArray(2).constData(), two.toByteArray(2).constData());
768 QCOMPARE(two.toByteArray(2).constData(), two.toByteArray(2).constData());
769 }
770}
771
772class TestHandler : public QXmlDefaultHandler
773{
774public:
775 TestHandler() :
776 recursionCount(0)
777 {
778 }
779
780 bool internalEntityDecl(const QString &name, const QString &value)
781 {
782 ++recursionCount;
783 return QXmlDefaultHandler::internalEntityDecl(name, value);
784 }
785
786 int recursionCount;
787};
788
789void tst_QXmlSimpleReader::dtdRecursionLimit()
790{
791 QFile file(QFINDTESTDATA("xmldocs/2-levels-nested-dtd.xml"));
792 QVERIFY(file.open(QIODevice::ReadOnly));
793 QXmlSimpleReader xmlReader;
794 {
795 QXmlInputSource source(&file);
796 TestHandler handler;
797 xmlReader.setDeclHandler(&handler);
798 xmlReader.setErrorHandler(&handler);
799 QVERIFY(!xmlReader.parse(&source));
800 }
801
802 file.close();
803 file.setFileName(QFINDTESTDATA("xmldocs/1-levels-nested-dtd.xml"));
804 QVERIFY(file.open(QIODevice::ReadOnly));
805 {
806 QXmlInputSource source(&file);
807 TestHandler handler;
808 xmlReader.setDeclHandler(&handler);
809 xmlReader.setErrorHandler(&handler);
810 QVERIFY(!xmlReader.parse(&source));
811 // The error wasn't because of the recursion limit being reached,
812 // it was because the document is not valid.
813 QVERIFY(handler.recursionCount < 2);
814 }
815
816 file.close();
817 file.setFileName(QFINDTESTDATA("xmldocs/internal-entity-polynomial-attribute.xml"));
818 QVERIFY(file.open(QIODevice::ReadOnly));
819 {
820 QXmlInputSource source(&file);
821 TestHandler handler;
822 xmlReader.setDeclHandler(&handler);
823 xmlReader.setErrorHandler(&handler);
824 QVERIFY(!xmlReader.parse(&source));
825 QCOMPARE(handler.recursionCount, 2);
826 }
827}
828
829QT_WARNING_POP
830#endif // QT_DEPRECATED_SINCE(5, 15)
831
832QTEST_MAIN(tst_QXmlSimpleReader)
833#include "tst_qxmlsimplereader.moc"
834

source code of qtbase/tests/auto/xml/sax/qxmlsimplereader/tst_qxmlsimplereader.cpp