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 <QtTest/QtTest>
31
32#include <QAbstractMessageHandler>
33#include <QFileInfo>
34#include <QNetworkReply>
35#include <QtNetwork/QTcpServer>
36#include <QtNetwork/QTcpSocket>
37#include <QXmlFormatter>
38#include <QXmlItem>
39#include <QXmlName>
40#include <QXmlQuery>
41#include <QXmlResultItems>
42#include <QXmlSerializer>
43
44#include "MessageSilencer.h"
45#include "MessageValidator.h"
46#include "NetworkOverrider.h"
47#include "PushBaseliner.h"
48#include "../qabstracturiresolver/TestURIResolver.h"
49#include "../qsimplexmlnodemodel/TestSimpleNodeModel.h"
50#include "TestFundament.h"
51#include "../network-settings.h"
52
53/*!
54 \class tst_QXmlQuery
55 \internal
56 \since 4.4
57 \brief Tests class QXmlQuery.
58
59 This test is not intended for testing the engine, but the functionality specific
60 to the QXmlQuery class.
61
62 In other words, if you have an engine bug; don't add it here because it won't be
63 tested properly. Instead add it to the test suite.
64
65 */
66class tst_QXmlQuery : public QObject
67 , private TestFundament
68{
69 Q_OBJECT
70
71public:
72 inline tst_QXmlQuery() : m_generatedBaselines(0)
73 , m_pushTestsCount(0)
74 , m_testNetwork(true)
75 {
76 }
77
78private Q_SLOTS:
79 void initTestCase();
80 void defaultConstructor() const;
81 void copyConstructor() const;
82 void constructorQXmlNamePool() const;
83 void constructorQXmlNamePoolQueryLanguage() const;
84 void constructorQXmlNamePoolWithinQSimpleXmlNodeModel() const;
85 void assignmentOperator() const;
86 void isValid() const;
87 void sequentialExecution() const;
88 void bindVariableQString() const;
89 void bindVariableQStringNoExternalDeclaration() const;
90 void bindVariableQXmlName() const;
91 void bindVariableQXmlNameTriggerWarnings() const;
92 void bindVariableQStringQIODevice() const;
93 void bindVariableQStringQIODeviceWithByteArray() const;
94 void bindVariableQStringQIODeviceWithString() const;
95 void bindVariableQStringQIODeviceWithQFile() const;
96 void bindVariableQXmlNameQIODevice() const;
97 void bindVariableQXmlNameQIODeviceTriggerWarnings() const;
98 void bindVariableXSLTSuccess() const;
99 void bindVariableTemporaryNode() const;
100 void setMessageHandler() const;
101 void messageHandler() const;
102 void evaluateToQAbstractXmlReceiverTriggerWarnings() const;
103 void evaluateToQXmlResultItems() const;
104 void evaluateToQXmlResultItemsTriggerWarnings() const;
105 void evaluateToQXmlResultItemsErrorAtEnd() const;
106 void evaluateToReceiver();
107 void evaluateToReceiver_data() const;
108 void evaluateToReceiverOnInvalidQuery() const;
109 void evaluateToQStringTriggerError() const;
110 void evaluateToQString() const;
111 void evaluateToQString_data() const;
112 void evaluateToQStringSignature() const;
113 void checkGeneratedBaselines() const;
114 void basicXQueryToQtTypeCheck() const;
115 void basicQtToXQueryTypeCheck() const;
116 void bindNode() const;
117 void relativeBaseURI() const;
118 void emptyBaseURI() const;
119 void roundTripDateWithinQXmlItem() const;
120 void bindingMissing() const;
121 void bindDefaultConstructedItem() const;
122 void bindDefaultConstructedItem_data() const;
123 void bindEmptyNullString() const;
124 void bindEmptyString() const;
125 void rebindVariableSameType() const;
126 void rebindVariableDifferentType() const;
127 void rebindVariableWithNullItem() const;
128 void eraseQXmlItemBinding() const;
129 void eraseDeviceBinding() const;
130 void constCorrectness() const;
131 void objectSize() const;
132 void setUriResolver() const;
133 void uriResolver() const;
134 void messageXML() const;
135 void resultItemsDeallocatedQuery() const;
136 void copyCheckMessageHandler() const;
137 void shadowedVariables() const;
138 void setFocusQXmlItem() const;
139 void setFocusQUrl() const;
140 void setFocusQIODevice() const;
141 void setFocusQIODeviceAvoidVariableClash() const;
142 void setFocusQIODeviceFailure() const;
143 void setFocusQIODeviceTriggerWarnings() const;
144 void setFocusQString() const;
145 void setFocusQStringFailure() const;
146 void setFocusQStringSignature() const;
147 void recompilationWithEvaluateToResultFailing() const;
148 void secondEvaluationWithEvaluateToResultFailing() const;
149 void recompilationWithEvaluateToReceiver() const;
150 void fnDocOnQIODeviceTimeout() const;
151 void evaluateToQStringListOnInvalidQuery() const;
152 void evaluateToQStringList() const;
153 void evaluateToQStringListTriggerWarnings() const;
154 void evaluateToQStringList_data() const;
155 void evaluateToQStringListNoConversion() const;
156 void evaluateToQIODevice() const;
157 void evaluateToQIODeviceTriggerWarnings() const;
158 void evaluateToQIODeviceSignature() const;
159 void evaluateToQIODeviceOnInvalidQuery() const;
160 void setQueryQIODeviceQUrl() const;
161 void setQueryQIODeviceQUrlTriggerWarnings() const;
162 void setQueryQString() const;
163 void setQueryQUrlSuccess() const;
164 void setQueryQUrlSuccess_data() const;
165 void setQueryQUrlFailSucceed() const;
166 void setQueryQUrlFailure() const;
167 void setQueryQUrlFailure_data() const;
168 void setQueryQUrlBaseURI() const;
169 void setQueryQUrlBaseURI_data() const;
170 void setQueryWithNonExistentQUrlOnValidQuery() const;
171 void setQueryWithInvalidQueryFromQUrlOnValidQuery() const;
172 void retrieveNameFromQuery() const;
173 void retrieveNameFromQuery_data() const;
174 void cleanupTestCase() const;
175 void declareUnavailableExternal() const;
176 void msvcCacheIssue() const;
177 void unavailableExternalVariable() const;
178 void useUriResolver() const;
179 void queryWithFocusAndVariable() const;
180 void undefinedFocus() const;
181 void basicFocusUsage() const;
182
183 void queryLanguage() const;
184 void queryLanguageSignature() const;
185 void enumQueryLanguage() const;
186
187 void setNetworkAccessManager() const;
188 void networkAccessManagerSignature() const;
189 void networkAccessManagerDefaultValue() const;
190 void networkAccessManager() const;
191
192 void setInitialTemplateNameQXmlName() const;
193 void setInitialTemplateNameQXmlNameSignature() const;
194 void setInitialTemplateNameQString() const;
195 void setInitialTemplateNameQStringSignature() const;
196 void initialTemplateName() const;
197 void initialTemplateNameSignature() const;
198
199 void fnDocNetworkAccessSuccess() const;
200 void fnDocNetworkAccessSuccess_data() const;
201 void fnDocNetworkAccessFailure() const;
202 void fnDocNetworkAccessFailure_data() const;
203 void multipleDocsAndFocus() const;
204 void multipleEvaluationsWithDifferentFocus() const;
205 void bindVariableQXmlQuery() const;
206 void bindVariableQXmlQuery_data() const;
207 void bindVariableQStringQXmlQuerySignature() const;
208 void bindVariableQXmlNameQXmlQuerySignature() const;
209 void bindVariableQXmlNameQXmlQuery() const;
210 void bindVariableQXmlQueryInvalidate() const;
211 void unknownSourceLocation() const;
212
213 void identityConstraintSuccess() const;
214 void identityConstraintFailure() const;
215 void identityConstraintFailure_data() const;
216
217 // TODO call all URI resolving functions where 1) the URI resolver return a null QUrl(); 2) resolves into valid, existing URI, 3) invalid, non-existing URI.
218 // TODO bind stringlists, variant lists, both ways.
219 // TODO trigger serialization error, or any error in evaluateToushCallback().
220 // TODO let items travle between two queries, as seen in the SDK
221 // TODO what happens if the query declares local variable and external ones are provided?
222
223private:
224 enum
225 {
226 /**
227 * One excluded, since we skip static-base-uri.xq.
228 */
229 ExpectedQueryCount = 30
230 };
231
232 static void checkBaseURI(const QUrl &baseURI, const QString &candidate);
233 static QStringList queries();
234 static const QString m_xmlPatternsDir;
235
236 int m_generatedBaselines;
237 int m_pushTestsCount;
238 const bool m_testNetwork;
239};
240
241const QString tst_QXmlQuery::m_xmlPatternsDir = QFINDTESTDATA("../xmlpatterns");
242
243void tst_QXmlQuery::initTestCase()
244{
245 QVERIFY2(!m_xmlPatternsDir.isEmpty(), qPrintable(QString::fromLatin1("Cannot locate '../xmlpatterns' starting from %1").arg(QDir::currentPath())));
246}
247
248void tst_QXmlQuery::checkBaseURI(const QUrl &baseURI, const QString &candidate)
249{
250 /* The use of QFileInfo::canonicalFilePath() takes into account that drive letters
251 * on Windows may have different cases. */
252 QVERIFY(QDir(baseURI.toLocalFile()).relativeFilePath(QFileInfo(candidate).canonicalFilePath()).startsWith("../"));
253}
254
255QStringList tst_QXmlQuery::queries()
256{
257 QDir dir;
258 dir.cd(dirName: inputFile(file: m_xmlPatternsDir + QLatin1String("/queries/")));
259
260 return dir.entryList(nameFilters: QStringList(QLatin1String("*.xq")));
261}
262
263void tst_QXmlQuery::defaultConstructor() const
264{
265 /* Allocate instance in different orders. */
266 {
267 QXmlQuery query;
268 }
269
270 {
271 QXmlQuery query1;
272 QXmlQuery query2;
273 }
274
275 {
276 QXmlQuery query1;
277 QXmlQuery query2;
278 QXmlQuery query3;
279 }
280}
281
282void tst_QXmlQuery::copyConstructor() const
283{
284 /* Verify that we can take a const reference, and simply do a copy of a default constructed object. */
285 {
286 const QXmlQuery query1;
287 QXmlQuery query2(query1);
288 }
289
290 /* Copy twice. */
291 {
292 const QXmlQuery query1;
293 QXmlQuery query2(query1);
294 QXmlQuery query3(query2);
295 }
296
297 /* Verify that copying default values works. */
298 {
299 const QXmlQuery query1;
300 const QXmlQuery query2(query1);
301 QCOMPARE(query2.messageHandler(), query1.messageHandler());
302 QCOMPARE(query2.uriResolver(), query1.uriResolver());
303 QCOMPARE(query2.queryLanguage(), query1.queryLanguage());
304 QCOMPARE(query2.initialTemplateName(), query1.initialTemplateName());
305 QCOMPARE(query2.networkAccessManager(), query1.networkAccessManager());
306 }
307
308 /* Check that the
309 *
310 * - name pool
311 * - URI resolver
312 * - message handler
313 * - query language
314 * - initial template name
315 *
316 * sticks with the copy. */
317 {
318 MessageSilencer silencer;
319 TestURIResolver resolver;
320 QNetworkAccessManager networkManager;
321 QXmlQuery query1(QXmlQuery::XSLT20);
322 QXmlNamePool np1(query1.namePool());
323
324 query1.setMessageHandler(&silencer);
325 query1.setUriResolver(&resolver);
326 query1.setNetworkAccessManager(&networkManager);
327
328 const QXmlName name(np1, QLatin1String("localName"),
329 QLatin1String("http://example.com/"),
330 QLatin1String("prefix"));
331 query1.setInitialTemplateName(name);
332
333 const QXmlQuery query2(query1);
334 QCOMPARE(query2.messageHandler(), static_cast<QAbstractMessageHandler *>(&silencer));
335 QCOMPARE(query2.uriResolver(), static_cast<const QAbstractUriResolver *>(&resolver));
336 QCOMPARE(query2.queryLanguage(), QXmlQuery::XSLT20);
337 QCOMPARE(query2.initialTemplateName(), name);
338 QCOMPARE(query2.networkAccessManager(), &networkManager);
339
340 QXmlNamePool np2(query2.namePool());
341
342 QCOMPARE(name.namespaceUri(np2), QString::fromLatin1("http://example.com/"));
343 QCOMPARE(name.localName(np2), QString::fromLatin1("localName"));
344 QCOMPARE(name.prefix(np2), QString::fromLatin1("prefix"));
345 }
346
347 {
348 QXmlQuery original;
349
350 original.setFocus(QXmlItem(4));
351 original.setQuery(sourceCode: QLatin1String("."));
352 QVERIFY(original.isValid());
353
354 const QXmlQuery copy(original);
355
356 QXmlResultItems result;
357 copy.evaluateTo(result: &result);
358 QCOMPARE(result.next().toAtomicValue(), QVariant(4));
359 QVERIFY(result.next().isNull());
360 QVERIFY(!result.hasError());
361 }
362
363 /* Copy, set, compare. Check that copies are independent. */
364 {
365 // TODO all members except queryLanguage().
366 }
367}
368
369void tst_QXmlQuery::constructorQXmlNamePool() const
370{
371 /* Check that the namepool we are passed, is actually used. */
372 QXmlNamePool np;
373
374 QXmlQuery query(np);
375 const QXmlName name(np, QLatin1String("localName"),
376 QLatin1String("http://example.com/"),
377 QLatin1String("prefix"));
378
379 QXmlNamePool np2(query.namePool());
380 QCOMPARE(name.namespaceUri(np2), QString::fromLatin1("http://example.com/"));
381 QCOMPARE(name.localName(np2), QString::fromLatin1("localName"));
382 QCOMPARE(name.prefix(np2), QString::fromLatin1("prefix"));
383}
384
385/*!
386 Ensure that the internal variable loading mechanisms uses the user-supplied
387 name pool.
388
389 If that is not the case, different name pools are used and the code crashes.
390
391 \since 4.5
392 */
393void tst_QXmlQuery::constructorQXmlNamePoolQueryLanguage() const
394{
395 QXmlNamePool np;
396 QXmlName name(np, QLatin1String("arbitraryName"));
397
398 QXmlQuery query(QXmlQuery::XQuery10, np);
399
400 QBuffer input;
401 input.setData("<yall/>");
402
403 QVERIFY(input.open(QIODevice::ReadOnly));
404 query.bindVariable(name, &input);
405 query.setQuery(sourceCode: "string(doc($arbitraryName))");
406
407 QStringList result;
408 query.evaluateTo(target: &result);
409}
410
411void tst_QXmlQuery::constructorQXmlNamePoolWithinQSimpleXmlNodeModel() const
412{
413 class TestCTOR : public TestSimpleNodeModel
414 {
415 public:
416 TestCTOR(const QXmlNamePool &np) : TestSimpleNodeModel(np)
417 {
418 }
419
420 void checkCTOR() const
421 {
422 /* If this fails to compile, the constructor has trouble with taking
423 * a mutable reference.
424 *
425 * The reason we use the this pointer explicitly, is to avoid a compiler
426 * warnings with MSVC 2005. */
427 QXmlQuery(this->namePool());
428 }
429 };
430
431 QXmlNamePool np;
432 TestCTOR ctor(np);
433 ctor.checkCTOR();
434}
435
436void tst_QXmlQuery::assignmentOperator() const
437{
438 class ReturnURI : public QAbstractUriResolver
439 {
440 public:
441 ReturnURI() {}
442 virtual QUrl resolve(const QUrl &relative,
443 const QUrl &baseURI) const
444 {
445 return baseURI.resolved(relative);
446 }
447 };
448
449 /* Assign this to this. */
450 {
451 QXmlQuery query;
452 query = query;
453 query = query;
454 query = query;
455
456 /* Just call a couple of functions to give valgrind
457 * something to check. */
458 QVERIFY(!query.isValid());
459 query.messageHandler();
460 }
461
462 /* Assign null instances a couple of times. */
463 {
464 QXmlQuery query1;
465 QXmlQuery query2;
466 query1 = query2;
467 query1 = query2;
468 query1 = query2;
469
470 /* Just call a couple of functions to give valgrind
471 * something to check. */
472 QVERIFY(!query1.isValid());
473 query1.messageHandler();
474
475 /* Just call a couple of functions to give valgrind
476 * something to check. */
477 QVERIFY(!query2.isValid());
478 query2.messageHandler();
479 }
480
481 /* Create a query, set all the things it stores, and ensure it
482 * travels over to the new instance. */
483 {
484 MessageSilencer silencer;
485 const ReturnURI returnURI;
486 QXmlNamePool namePool;
487
488 QBuffer documentDevice;
489 documentDevice.setData(QByteArray("<e>a</e>"));
490 QVERIFY(documentDevice.open(QIODevice::ReadOnly));
491
492 QXmlQuery original(namePool);
493 QXmlName testName(namePool, QLatin1String("somethingToCheck"));
494
495 original.setMessageHandler(&silencer);
496 original.bindVariable(localName: QLatin1String("var"), value: QXmlItem(1));
497 original.bindVariable(localName: QLatin1String("device"), &documentDevice);
498 original.setUriResolver(&returnURI);
499 original.setFocus(QXmlItem(3));
500 original.setQuery(sourceCode: QLatin1String("$var, 1 + 1, ., string(doc($device))"));
501
502 /* Do a copy, and check that everything followed on into the copy. No modification
503 * of the copy. */
504 {
505 QXmlQuery copy;
506
507 /* We use assignment operator, not copy constructor. */
508 copy = original;
509
510 QVERIFY(copy.isValid());
511 QCOMPARE(copy.uriResolver(), static_cast<const QAbstractUriResolver *>(&returnURI));
512 QCOMPARE(copy.messageHandler(), static_cast<QAbstractMessageHandler *>(&silencer));
513 QCOMPARE(testName.localName(copy.namePool()), QString::fromLatin1("somethingToCheck"));
514
515 QXmlResultItems result;
516 copy.evaluateTo(result: &result);
517 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
518 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
519 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
520 QCOMPARE(result.next().toAtomicValue(), QVariant(QString::fromLatin1("a")));
521 QVERIFY(result.next().isNull());
522 QVERIFY(!result.hasError());
523 }
524
525 /* Copy, and change values. Things should detach. */
526 {
527 /* Evaluate the copy. */
528 {
529 MessageSilencer secondSilencer;
530 const ReturnURI secondUriResolver;
531 QBuffer documentDeviceCopy;
532 documentDeviceCopy.setData(QByteArray("<e>b</e>"));
533 QVERIFY(documentDeviceCopy.open(QIODevice::ReadOnly));
534
535 QXmlQuery copy;
536 copy = original;
537
538 copy.setMessageHandler(&secondSilencer);
539 /* Here we rebind variable values. */
540 copy.bindVariable(localName: QLatin1String("var"), value: QXmlItem(4));
541 copy.bindVariable(localName: QLatin1String("device"), &documentDeviceCopy);
542 copy.setUriResolver(&secondUriResolver);
543 copy.setFocus(QXmlItem(6));
544 copy.setQuery(sourceCode: QLatin1String("$var, 1 + 1, ., string(doc($device))"));
545
546 /* Check that the copy picked up the new things. */
547 QVERIFY(copy.isValid());
548 QCOMPARE(copy.uriResolver(), static_cast<const QAbstractUriResolver *>(&secondUriResolver));
549 QCOMPARE(copy.messageHandler(), static_cast<QAbstractMessageHandler *>(&secondSilencer));
550
551 QXmlResultItems resultCopy;
552 copy.evaluateTo(result: &resultCopy);
553 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(4));
554 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(2));
555 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(6));
556 const QString stringedDevice(resultCopy.next().toAtomicValue().toString());
557 QCOMPARE(stringedDevice, QString::fromLatin1("b"));
558 QVERIFY(resultCopy.next().isNull());
559 QVERIFY(!resultCopy.hasError());
560 }
561
562 /* Evaluate the original. */
563 {
564 /* Check that the original is unchanged. */
565 QVERIFY(original.isValid());
566 QCOMPARE(original.uriResolver(), static_cast<const QAbstractUriResolver *>(&returnURI));
567 QCOMPARE(original.messageHandler(), static_cast<QAbstractMessageHandler *>(&silencer));
568
569 QXmlResultItems resultOriginal;
570 original.evaluateTo(result: &resultOriginal);
571 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(1));
572 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(2));
573 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(3));
574 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(QString::fromLatin1("a")));
575 QVERIFY(resultOriginal.next().isNull());
576 QVERIFY(!resultOriginal.hasError());
577 }
578 }
579 }
580}
581
582/*!
583 Since QXmlQuery doesn't seek devices to position 0, this code triggers a bug
584 where document caching doesn't work. Since the document caching doesn't work,
585 the device will be read twice, and the second time the device is at the end,
586 hence premature end of document.
587 */
588void tst_QXmlQuery::sequentialExecution() const
589{
590 QBuffer inBuffer;
591 inBuffer.setData(QByteArray("<input/>"));
592 QVERIFY(inBuffer.open(QIODevice::ReadOnly));
593
594 QXmlQuery query;
595 query.bindVariable(localName: "inputDocument", &inBuffer);
596
597 QByteArray outArray;
598 QBuffer outBuffer(&outArray);
599 outBuffer.open(openMode: QIODevice::WriteOnly);
600
601 const QString queryString(QLatin1String("doc($inputDocument)"));
602 query.setQuery(sourceCode: queryString);
603
604 QXmlFormatter formatter(query, &outBuffer);
605
606 QVERIFY(query.evaluateTo(&formatter));
607
608 /* If this line is removed, the bug isn't triggered. */
609 query.setQuery(sourceCode: queryString);
610
611 QVERIFY(query.evaluateTo(&formatter));
612}
613
614void tst_QXmlQuery::isValid() const
615{
616 /* Check default value. */
617 QXmlQuery query;
618 QVERIFY(!query.isValid());
619}
620
621void tst_QXmlQuery::bindVariableQString() const
622{
623 {
624 QXmlQuery query;
625 /* Bind with a null QXmlItem. */
626 query.bindVariable(localName: QLatin1String("name"), value: QXmlItem());
627 }
628
629 {
630 QXmlQuery query;
631 /* Bind with a null QVariant. */
632 query.bindVariable(localName: QLatin1String("name"), value: QXmlItem(QVariant()));
633 }
634
635 {
636 QXmlQuery query;
637 /* Bind with a null QXmlNodeModelIndex. */
638 query.bindVariable(localName: QLatin1String("name"), value: QXmlItem(QXmlNodeModelIndex()));
639 }
640}
641
642void tst_QXmlQuery::bindVariableQStringNoExternalDeclaration() const
643{
644 QXmlQuery query;
645 query.bindVariable(localName: QLatin1String("foo"), value: QXmlItem(QLatin1String("Variable Value")));
646 query.setQuery(sourceCode: QLatin1String("$foo"));
647
648 QVERIFY(query.isValid());
649
650 QStringList result;
651 QVERIFY(query.evaluateTo(&result));
652
653 QCOMPARE(result, QStringList() << QLatin1String("Variable Value"));
654}
655
656void tst_QXmlQuery::bindVariableQXmlName() const
657{
658 // TODO
659}
660
661void tst_QXmlQuery::bindVariableQXmlNameTriggerWarnings() const
662{
663 QXmlQuery query;
664
665 QTest::ignoreMessage(type: QtWarningMsg, message: "The variable name cannot be null.");
666 query.bindVariable(name: QXmlName(), value: QVariant());
667}
668
669void tst_QXmlQuery::bindVariableQStringQIODeviceWithByteArray() const
670{
671 QXmlQuery query;
672
673 QByteArray in("<e/>");
674 QBuffer device(&in);
675 QVERIFY(device.open(QIODevice::ReadOnly));
676
677 query.bindVariable(localName: "doc", &device);
678
679 query.setQuery(sourceCode: QLatin1String("declare variable $doc external; $doc"));
680
681 QVERIFY(query.isValid());
682
683 /* Check the URI corresponding to the variable. */
684 {
685 QXmlResultItems items;
686 query.evaluateTo(result: &items);
687
688 QCOMPARE(items.next().toAtomicValue().toString(), QString::fromLatin1("tag:trolltech.com,2007:QtXmlPatterns:QIODeviceVariable:doc"));
689 }
690
691 /* Now, actually load the document. We use the same QXmlQuery just to stress recompilation a bit. */
692 {
693 query.setQuery(sourceCode: QLatin1String("declare variable $doc external; doc($doc)"));
694
695 QByteArray out;
696 QBuffer outBuffer(&out);
697 QVERIFY(outBuffer.open(QIODevice::WriteOnly));
698
699 QXmlSerializer serializer(query, &outBuffer);
700
701 QVERIFY(query.evaluateTo(&serializer));
702 QCOMPARE(out, in);
703 }
704}
705
706void tst_QXmlQuery::bindVariableQStringQIODeviceWithString() const
707{
708 QXmlQuery query;
709
710 QString in("<qstring/>");
711 QByteArray inUtf8(in.toUtf8());
712 QBuffer inDevice(&inUtf8);
713
714 QVERIFY(inDevice.open(QIODevice::ReadOnly));
715
716 query.bindVariable(localName: "doc", &inDevice);
717
718 query.setQuery(sourceCode: QLatin1String("declare variable $doc external; doc($doc)"));
719
720 QByteArray out;
721 QBuffer outBuffer(&out);
722 QVERIFY(outBuffer.open(QIODevice::WriteOnly));
723
724 QXmlSerializer serializer(query, &outBuffer);
725 QVERIFY(query.evaluateTo(&serializer));
726
727 QCOMPARE(out, inUtf8);
728}
729
730void tst_QXmlQuery::bindVariableQStringQIODeviceWithQFile() const
731{
732 QXmlQuery query;
733 QFile inDevice(QFINDTESTDATA("input.xml"));
734
735 QVERIFY(inDevice.open(QIODevice::ReadOnly));
736
737 query.bindVariable(localName: "doc", &inDevice);
738
739 query.setQuery(sourceCode: QLatin1String("declare variable $doc external; doc($doc)"));
740
741 QByteArray out;
742 QBuffer outBuffer(&out);
743 QVERIFY(outBuffer.open(QIODevice::WriteOnly));
744
745 QXmlSerializer serializer(query, &outBuffer);
746 QVERIFY(query.evaluateTo(&serializer));
747 outBuffer.close();
748
749 QCOMPARE(out, QByteArray("<!-- This is just a file for testing. --><input/>"));
750}
751
752void tst_QXmlQuery::bindVariableQStringQIODevice() const
753{
754 QXmlQuery query;
755
756 /* Rebind the variable. */
757 {
758 /* First evaluation. */
759 {
760 QByteArray in1("<e1/>");
761 QBuffer inDevice1(&in1);
762 QVERIFY(inDevice1.open(QIODevice::ReadOnly));
763
764 query.bindVariable(localName: "in", &inDevice1);
765 query.setQuery(sourceCode: QLatin1String("doc($in)"));
766
767 QByteArray out1;
768 QBuffer outDevice1(&out1);
769 QVERIFY(outDevice1.open(QIODevice::WriteOnly));
770
771 QXmlSerializer serializer(query, &outDevice1);
772 query.evaluateTo(callback: &serializer);
773 QCOMPARE(out1, in1);
774 }
775
776 /* Second evaluation, rebind variable. */
777 {
778 QByteArray in2("<e2/>");
779 QBuffer inDevice2(&in2);
780 QVERIFY(inDevice2.open(QIODevice::ReadOnly));
781
782 query.bindVariable(localName: QLatin1String("in"), &inDevice2);
783
784 QByteArray out2;
785 QBuffer outDevice2(&out2);
786 QVERIFY(outDevice2.open(QIODevice::WriteOnly));
787
788 QXmlSerializer serializer(query, &outDevice2);
789 QVERIFY(query.evaluateTo(&serializer));
790 QCOMPARE(out2, in2);
791 }
792 }
793
794 // TODO trigger recompilation when setting qiodevices., and qiodevice overwritten by other type, etc.
795}
796
797void tst_QXmlQuery::bindVariableQXmlNameQIODevice() const
798{
799 // TODO
800}
801
802void tst_QXmlQuery::bindVariableQXmlNameQIODeviceTriggerWarnings() const
803{
804 QXmlNamePool np;
805 QXmlQuery query(np);
806
807 QBuffer buffer;
808 QTest::ignoreMessage(type: QtWarningMsg, message: "A null, or readable QIODevice must be passed.");
809 query.bindVariable(name: QXmlName(np, QLatin1String("foo")), &buffer);
810
811 QTest::ignoreMessage(type: QtWarningMsg, message: "The variable name cannot be null.");
812 query.bindVariable(name: QXmlName(), 0);
813}
814
815void tst_QXmlQuery::bindVariableXSLTSuccess() const
816{
817 QXmlQuery stylesheet(QXmlQuery::XSLT20);
818 stylesheet.setInitialTemplateName(QLatin1String("main"));
819
820 stylesheet.bindVariable(localName: QLatin1String("variableNoSelectNoBodyBoundWithBindVariable"),
821 value: QVariant(QLatin1String("MUST NOT SHOW 1")));
822
823 stylesheet.bindVariable(localName: QLatin1String("variableSelectBoundWithBindVariable"),
824 value: QVariant(QLatin1String("MUST NOT SHOW 2")));
825
826 stylesheet.bindVariable(localName: QLatin1String("variableSelectWithTypeIntBoundWithBindVariable"),
827 value: QVariant(QLatin1String("MUST NOT SHOW 3")));
828
829 stylesheet.bindVariable(localName: QLatin1String("paramNoSelectNoBodyBoundWithBindVariable"),
830 value: QVariant(QLatin1String("param1")));
831
832 stylesheet.bindVariable(localName: QLatin1String("paramNoSelectNoBodyBoundWithBindVariableRequired"),
833 value: QVariant(QLatin1String("param1")));
834
835 stylesheet.bindVariable(localName: QLatin1String("paramSelectBoundWithBindVariable"),
836 value: QVariant(QLatin1String("param2")));
837
838 stylesheet.bindVariable(localName: QLatin1String("paramSelectBoundWithBindVariableRequired"),
839 value: QVariant(QLatin1String("param3")));
840
841 stylesheet.bindVariable(localName: QLatin1String("paramSelectWithTypeIntBoundWithBindVariable"),
842 value: QVariant(4));
843
844 stylesheet.bindVariable(localName: QLatin1String("paramSelectWithTypeIntBoundWithBindVariableRequired"),
845 value: QVariant(QLatin1String("param5")));
846
847 stylesheet.setQuery(queryURI: QUrl(inputFileAsURI(file: m_xmlPatternsDir + QLatin1String("/stylesheets/parameters.xsl"))));
848
849 QVERIFY(stylesheet.isValid());
850
851 QBuffer deviceOut;
852 QVERIFY(deviceOut.open(QIODevice::ReadWrite));
853
854 QVERIFY(stylesheet.evaluateTo(&deviceOut));
855
856 const QString result(QString::fromUtf8(str: deviceOut.data().constData()));
857
858 QCOMPARE(result,
859 QString::fromLatin1("Variables: variableSelectsDefaultValue variableSelectsDefaultValue2 3 4 "
860 "Parameters: param1 param1 param2 param3 4 param5"));
861}
862
863void tst_QXmlQuery::bindVariableTemporaryNode() const
864{
865 /* First we do it with QXmlResultItems staying in scope. */;
866 {
867 QXmlQuery query1;
868 query1.setQuery(sourceCode: "<anElement/>");
869
870 QXmlResultItems result1;
871 query1.evaluateTo(result: &result1);
872
873 QXmlQuery query2(query1);
874 query2.bindVariable(localName: "fromQuery1", value: result1.next());
875 query2.setQuery(sourceCode: "$fromQuery1");
876
877 QString output;
878 QVERIFY(query2.evaluateTo(&output));
879
880 QCOMPARE(output, QString::fromLatin1("<anElement/>\n"));
881 }
882
883 /* And now with it deallocating, so its internal DynamicContext pointer is
884 * released. This doesn't work in Qt 4.5 and is ok. */
885 {
886 QXmlQuery query1;
887 query1.setQuery(sourceCode: "<anElement/>");
888
889 QXmlQuery query2;
890
891 {
892 QXmlResultItems result1;
893 query1.evaluateTo(result: &result1);
894
895 query2.bindVariable(localName: "fromQuery1", value: result1.next());
896 query2.setQuery(sourceCode: "$fromQuery1");
897 }
898
899 QString output;
900 return; // See comment above.
901 QVERIFY(query2.evaluateTo(&output));
902
903 QCOMPARE(output, QString::fromLatin1("<anElement/>\n"));
904 }
905}
906
907void tst_QXmlQuery::messageHandler() const
908{
909 {
910 /* Check default value. */
911 QXmlQuery query;
912 QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0));
913 }
914}
915
916void tst_QXmlQuery::setMessageHandler() const
917{
918 QXmlQuery query;
919 MessageSilencer silencer;
920 query.setMessageHandler(&silencer);
921 QCOMPARE(static_cast<QAbstractMessageHandler *>(&silencer), query.messageHandler());
922}
923
924void tst_QXmlQuery::evaluateToReceiver()
925{
926 QFETCH(QString, inputQuery);
927
928 /* This query prints a URI specific to the local system. */
929 if(inputQuery == QLatin1String("static-base-uri.xq"))
930 return;
931
932 ++m_pushTestsCount;
933 const QString queryURI(inputFile(file: m_xmlPatternsDir + QLatin1String("/queries/") + inputQuery));
934 QFile queryFile(queryURI);
935
936 QVERIFY(queryFile.exists());
937 QVERIFY(queryFile.open(QIODevice::ReadOnly));
938
939 QXmlQuery query;
940
941 MessageSilencer receiver;
942 query.setMessageHandler(&receiver);
943 query.setQuery(sourceCode: &queryFile, documentURI: QUrl::fromLocalFile(localfile: queryURI));
944
945 /* We read all the queries, and some of them are invalid. However, we
946 * only want those that compile. */
947 if(!query.isValid())
948 return;
949
950 QString produced;
951 QTextStream stream(&produced, QIODevice::WriteOnly);
952 PushBaseliner push(stream, query.namePool());
953 QVERIFY(push.isValid());
954 query.evaluateTo(callback: &push);
955
956 const QString baselineName(QFINDTESTDATA("pushBaselines/") + inputQuery.left(n: inputQuery.length() - 2) + QString::fromLatin1(str: "ref"));
957 QFile baseline(baselineName);
958
959 if(baseline.exists())
960 {
961 QVERIFY(baseline.open(QIODevice::ReadOnly | QIODevice::Text));
962 const QString stringedBaseline(QString::fromUtf8(str: baseline.readAll()));
963 QCOMPARE(produced, stringedBaseline);
964 }
965 else
966 {
967 QVERIFY(baseline.open(QIODevice::WriteOnly));
968 /* This is intentionally a warning, don't remove it. Update the baselines instead. */
969 qWarning() << "Generated baseline for:" << baselineName;
970 ++m_generatedBaselines;
971
972 baseline.write(data: produced.toUtf8());
973 }
974}
975
976void tst_QXmlQuery::evaluateToReceiver_data() const
977{
978 QTest::addColumn<QString>(name: "inputQuery");
979
980 const auto queries_ = queries();
981 for (QString const& query : queries_) {
982 /* This outputs a URI specific to the environment, so we can't use it for this
983 * particular test. */
984 if (query != QLatin1String("staticBaseURI.xq"))
985 QTest::newRow(dataTag: query.toUtf8().constData()) << query;
986 }
987}
988
989void tst_QXmlQuery::evaluateToReceiverOnInvalidQuery() const
990{
991 /* Invoke on a default constructed object. */
992 {
993 QByteArray out;
994 QBuffer buffer(&out);
995 buffer.open(openMode: QIODevice::WriteOnly);
996
997 QXmlQuery query;
998 QXmlSerializer serializer(query, &buffer);
999 QVERIFY(!query.evaluateTo(&serializer));
1000 }
1001
1002 /* Invoke on an invalid query; compile time error. */
1003 {
1004 QByteArray out;
1005 QBuffer buffer(&out);
1006 buffer.open(openMode: QIODevice::WriteOnly);
1007 MessageSilencer silencer;
1008
1009 QXmlQuery query;
1010 query.setMessageHandler(&silencer);
1011 query.setQuery(sourceCode: QLatin1String("1 + "));
1012 QXmlSerializer serializer(query, &buffer);
1013 QVERIFY(!query.evaluateTo(&serializer));
1014 }
1015
1016 /* Invoke on an invalid query; runtime error. */
1017 {
1018 QByteArray out;
1019 QBuffer buffer(&out);
1020 buffer.open(openMode: QIODevice::WriteOnly);
1021 MessageSilencer silencer;
1022
1023 QXmlQuery query;
1024 query.setMessageHandler(&silencer);
1025 query.setQuery(sourceCode: QLatin1String("error()"));
1026 QXmlSerializer serializer(query, &buffer);
1027 QVERIFY(!query.evaluateTo(&serializer));
1028 }
1029}
1030
1031void tst_QXmlQuery::evaluateToQStringTriggerError() const
1032{
1033 /* Invoke on a default constructed object. */
1034 {
1035 QXmlQuery query;
1036 QString out;
1037 QVERIFY(!query.evaluateTo(&out));
1038 }
1039
1040 /* Invoke on an invalid query; compile time error. */
1041 {
1042 QXmlQuery query;
1043 MessageSilencer silencer;
1044 query.setMessageHandler(&silencer);
1045
1046 query.setQuery(sourceCode: QLatin1String("1 + "));
1047
1048 QString out;
1049 QVERIFY(!query.evaluateTo(&out));
1050 }
1051
1052 /* Invoke on an invalid query; runtime error. */
1053 {
1054 QXmlQuery query;
1055 MessageSilencer silencer;
1056 query.setMessageHandler(&silencer);
1057
1058 query.setQuery(sourceCode: QLatin1String("error()"));
1059
1060 QString out;
1061 QVERIFY(!query.evaluateTo(&out));
1062 }
1063}
1064
1065void tst_QXmlQuery::evaluateToQString() const
1066{
1067 QFETCH(QString, query);
1068 QFETCH(QString, expectedOutput);
1069
1070 QXmlQuery queryInstance;
1071 queryInstance.setQuery(sourceCode: query);
1072 QVERIFY(queryInstance.isValid());
1073
1074 QString result;
1075 QVERIFY(queryInstance.evaluateTo(&result));
1076
1077 QCOMPARE(result, expectedOutput);
1078}
1079
1080void tst_QXmlQuery::evaluateToQString_data() const
1081{
1082 QTest::addColumn<QString>(name: "query");
1083 QTest::addColumn<QString>(name: "expectedOutput");
1084
1085 QTest::newRow(dataTag: "Two atomics")
1086 << QString::fromLatin1(str: "1, 'two'")
1087 << QString::fromLatin1(str: "1 two\n");
1088
1089 QTest::newRow(dataTag: "An element")
1090 << QString::fromLatin1(str: "<e>{1}</e>")
1091 << QString::fromLatin1(str: "<e>1</e>\n");
1092}
1093
1094void tst_QXmlQuery::evaluateToQStringSignature() const
1095{
1096 const QXmlQuery query;
1097
1098 QString output;
1099
1100 /* evaluateTo(QString *) should be a const function. */
1101 query.evaluateTo(output: &output);
1102}
1103
1104void tst_QXmlQuery::evaluateToQAbstractXmlReceiverTriggerWarnings() const
1105{
1106 QXmlQuery query;
1107
1108 /* We check the return value as well as warning message here. */
1109 QTest::ignoreMessage(type: QtWarningMsg, message: "A non-null callback must be passed.");
1110 QCOMPARE(query.evaluateTo(static_cast<QAbstractXmlReceiver *>(0)),
1111 false);
1112}
1113
1114void tst_QXmlQuery::evaluateToQXmlResultItems() const
1115{
1116 /* Invoke on a default constructed object. */
1117 {
1118 QXmlQuery query;
1119 QXmlResultItems result;
1120 query.evaluateTo(result: &result);
1121 QVERIFY(result.next().isNull());
1122 }
1123}
1124
1125void tst_QXmlQuery::evaluateToQXmlResultItemsTriggerWarnings() const
1126{
1127 QTest::ignoreMessage(type: QtWarningMsg, message: "A null pointer cannot be passed.");
1128 QXmlQuery query;
1129 query.evaluateTo(result: static_cast<QXmlResultItems *>(0));
1130}
1131
1132void tst_QXmlQuery::evaluateToQXmlResultItemsErrorAtEnd() const
1133{
1134 QXmlQuery query;
1135 MessageSilencer silencer;
1136 query.setMessageHandler(&silencer);
1137 query.setQuery(sourceCode: QLatin1String("1 to 100, fn:error()"));
1138 QVERIFY(query.isValid());
1139
1140 QXmlResultItems it;
1141 query.evaluateTo(result: &it);
1142
1143 while(!it.next().isNull())
1144 {
1145 }
1146}
1147
1148/*!
1149 If baselines were generated, we flag it as a failure such that it gets
1150 attention, and that they are adjusted accordingly.
1151 */
1152void tst_QXmlQuery::checkGeneratedBaselines() const
1153{
1154 QCOMPARE(m_generatedBaselines, 0);
1155
1156 /* If this check fails, the auto test setup is misconfigured, or files have
1157 * been added/removed without this number being updated. */
1158 QCOMPARE(m_pushTestsCount, int(ExpectedQueryCount));
1159}
1160
1161void tst_QXmlQuery::basicXQueryToQtTypeCheck() const
1162{
1163 QFile queryFile(m_xmlPatternsDir + QLatin1String("/queries/") + QString::fromLatin1(str: "allAtomics.xq"));
1164 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1165
1166 QXmlQuery query;
1167 query.setQuery(sourceCode: &queryFile);
1168 QVERIFY(query.isValid());
1169
1170 QXmlResultItems it;
1171 query.evaluateTo(result: &it);
1172
1173 QVariantList expectedValues;
1174 expectedValues.append(t: QString::fromLatin1(str: "xs:untypedAtomic"));
1175 expectedValues.append(t: QDateTime(QDate(2002, 10, 10), QTime(23, 2, 11), Qt::UTC));
1176 expectedValues.append(t: QDate(2002, 10, 10));
1177 expectedValues.append(t: QVariant()); /* We currently doesn't support xs:time through the API. */
1178
1179 expectedValues.append(t: QVariant()); /* xs:duration */
1180 expectedValues.append(t: QVariant()); /* xs:dayTimeDuration */
1181 expectedValues.append(t: QVariant()); /* xs:yearMonthDuration */
1182
1183 if(sizeof(qreal) == sizeof(float)) {//ARM casts to Float not to double
1184 expectedValues.append(t: QVariant(float(3e3))); /* xs:float */
1185 expectedValues.append(t: QVariant(float(4e4))); /* xs:double */
1186 expectedValues.append(t: QVariant(float(2))); /* xs:decimal */
1187 } else {
1188 expectedValues.append(t: QVariant(double(3e3))); /* xs:float */
1189 expectedValues.append(t: QVariant(double(4e4))); /* xs:double */
1190 expectedValues.append(t: QVariant(double(2))); /* xs:decimal */
1191 }
1192
1193 /* xs:integer and its sub-types. */
1194 expectedValues.append(t: QVariant(qlonglong(16)));
1195 expectedValues.append(t: QVariant(qlonglong(-6)));
1196 expectedValues.append(t: QVariant(qlonglong(-4)));
1197 expectedValues.append(t: QVariant(qlonglong(5)));
1198 expectedValues.append(t: QVariant(qlonglong(6)));
1199 expectedValues.append(t: QVariant(qlonglong(7)));
1200 expectedValues.append(t: QVariant(qlonglong(8)));
1201 expectedValues.append(t: QVariant(qlonglong(9)));
1202 expectedValues.append(t: QVariant(qulonglong(10)));
1203 expectedValues.append(t: QVariant(qlonglong(11)));
1204 expectedValues.append(t: QVariant(qlonglong(12)));
1205 expectedValues.append(t: QVariant(qlonglong(13)));
1206 expectedValues.append(t: QVariant(qlonglong(14)));
1207
1208 expectedValues.append(t: QVariant()); /* xs:gYearMonth("1976-02"), */
1209 expectedValues.append(t: QVariant()); /* xs:gYear("2005-12:00"), */
1210 expectedValues.append(t: QVariant()); /* xs:gMonthDay("--12-25-14:00"), */
1211 expectedValues.append(t: QVariant()); /* xs:gDay("---25-14:00"), */
1212 expectedValues.append(t: QVariant()); /* xs:gMonth("--12-14:00"), */
1213 expectedValues.append(t: true); /* xs:boolean("true"), */
1214 expectedValues.append(t: QVariant(QByteArray::fromBase64(base64: QByteArray("aaaa")))); /* xs:base64Binary("aaaa"), */
1215 expectedValues.append(t: QVariant(QByteArray::fromHex(hexEncoded: QByteArray("FFFF")))); /* xs:hexBinary("FFFF"), */
1216 expectedValues.append(t: QVariant(QString::fromLatin1(str: "http://example.com/"))); /* xs:anyURI("http://example.com/"), */
1217 QXmlNamePool np(query.namePool());
1218 expectedValues.append(t: QVariant::fromValue(value: QXmlName(np, QLatin1String("localName"),
1219 QLatin1String("http://example.com/2"),
1220 QLatin1String("prefix"))));
1221
1222 expectedValues.append(t: QVariant(QString::fromLatin1(str: "An xs:string")));
1223 expectedValues.append(t: QVariant(QString::fromLatin1(str: "normalizedString")));
1224 expectedValues.append(t: QVariant(QString::fromLatin1(str: "token")));
1225 expectedValues.append(t: QVariant(QString::fromLatin1(str: "language")));
1226 expectedValues.append(t: QVariant(QString::fromLatin1(str: "NMTOKEN")));
1227 expectedValues.append(t: QVariant(QString::fromLatin1(str: "Name")));
1228 expectedValues.append(t: QVariant(QString::fromLatin1(str: "NCName")));
1229 expectedValues.append(t: QVariant(QString::fromLatin1(str: "ID")));
1230 expectedValues.append(t: QVariant(QString::fromLatin1(str: "IDREF")));
1231 expectedValues.append(t: QVariant(QString::fromLatin1(str: "ENTITY")));
1232
1233 int i = 0;
1234 QXmlItem item(it.next());
1235
1236 while(!item.isNull())
1237 {
1238 QVERIFY(item.isAtomicValue());
1239 const QVariant produced(item.toAtomicValue());
1240
1241 const QVariant &expected = expectedValues.at(i);
1242
1243 /* For the cases where we can't represent a value in the XDM with Qt,
1244 * we return an invalid QVariant. */
1245 QCOMPARE(expected.isValid(), produced.isValid());
1246
1247 QCOMPARE(produced.type(), expected.type());
1248
1249 if(expected.isValid())
1250 {
1251 /* This is only needed for xs:decimal though, for some reason. Probably
1252 * just artifacts created somewhere. */
1253 if(produced.type() == QVariant::Double)
1254 QVERIFY(qFuzzyCompare(produced.toDouble(), expected.toDouble()));
1255 else if (produced.canConvert<QXmlName>())
1256 {
1257 /* QVariant::operator==() does identity comparison, it doesn't delegate to operator==() of
1258 * the contained type, unless it's hardcoded into QVariant. */
1259 const QXmlName n1 = qvariant_cast<QXmlName>(v: produced);
1260 const QXmlName n2 = qvariant_cast<QXmlName>(v: expected);
1261 QCOMPARE(n1, n2);
1262 }
1263 else
1264 QCOMPARE(produced, expected);
1265 }
1266
1267 ++i;
1268 item = it.next();
1269 }
1270
1271 QCOMPARE(i, expectedValues.count());
1272}
1273
1274/*!
1275 Send values from Qt into XQuery.
1276 */
1277void tst_QXmlQuery::basicQtToXQueryTypeCheck() const
1278{
1279 QFile queryFile(m_xmlPatternsDir + QLatin1String("/queries/") + QLatin1String("allAtomicsExternally.xq"));
1280 QVERIFY(queryFile.exists());
1281 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1282
1283 QCOMPARE(QVariant(QDate(1999, 9, 10)).type(), QVariant::Date);
1284
1285 QXmlQuery query;
1286
1287 QXmlNamePool np(query.namePool());
1288
1289 const QXmlName name(np, QLatin1String("localname"),
1290 QLatin1String("http://example.com"),
1291 QLatin1String("prefix"));
1292
1293 query.bindVariable(localName: QLatin1String("fromQUrl"), value: QXmlItem(QUrl(QString::fromLatin1(str: "http://example.com/"))));
1294 query.bindVariable(localName: QLatin1String("fromQByteArray"), value: QXmlItem(QByteArray("AAAA")));
1295 query.bindVariable(localName: QLatin1String("fromBool"), value: QXmlItem(bool(true)));
1296 query.bindVariable(localName: QLatin1String("fromQDate"), value: QXmlItem(QDate(2000, 10, 11)));
1297 // TODO Do with different QDateTime time specs
1298 query.bindVariable(localName: QLatin1String("fromQDateTime"), value: QXmlItem(QDateTime(QDate(2001, 9, 10), QTime(1, 2, 3))));
1299 query.bindVariable(localName: QLatin1String("fromDouble"), value: QXmlItem(double(3)));
1300 query.bindVariable(localName: QLatin1String("fromFloat"), value: QXmlItem(float(4)));
1301 query.bindVariable(localName: QLatin1String("integer"), value: QXmlItem(5));
1302 query.bindVariable(localName: QLatin1String("fromQString"), value: QXmlItem(QString::fromLatin1(str: "A QString")));
1303 query.bindVariable(localName: QLatin1String("fromQChar"), value: QXmlItem(QChar::fromLatin1(c: 'C')));
1304
1305 query.bindVariable(localName: QLatin1String("fromIntLiteral"), value: QXmlItem(QVariant(654)));
1306
1307 {
1308 QVariant ui(uint(5));
1309 QCOMPARE(ui.type(), QVariant::UInt);
1310 query.bindVariable(localName: QLatin1String("fromUInt"), value: ui);
1311 }
1312
1313 {
1314 QVariant ulnglng(qulonglong(6));
1315 QCOMPARE(ulnglng.type(), QVariant::ULongLong);
1316 query.bindVariable(localName: QLatin1String("fromULongLong"), value: ulnglng);
1317 }
1318
1319 {
1320 QVariant qlnglng(qlonglong(7));
1321 QCOMPARE(qlnglng.type(), QVariant::LongLong);
1322 query.bindVariable(localName: QLatin1String("fromLongLong"), value: qlnglng);
1323 }
1324
1325 query.setQuery(sourceCode: &queryFile);
1326
1327 // TODO do queries which declares external variables with types. Tons of combos here.
1328 // TODO ensure that binding with QXmlItem() doesn't make a binding available.
1329 // TODO test rebinding a variable.
1330
1331 QVERIFY(query.isValid());
1332
1333 QXmlResultItems it;
1334 query.evaluateTo(result: &it);
1335 QXmlItem item(it.next());
1336 QVERIFY(!item.isNull());
1337 QVERIFY(item.isAtomicValue());
1338
1339 if(sizeof(qreal) == sizeof(float)) //ARM casts to Float not to double
1340 QCOMPARE(item.toAtomicValue().toString(),
1341 QLatin1String("4 true 3 654 7 41414141 C 2000-10-11Z 2001-09-10T01:02:03 "
1342 "A QString http://example.com/ 5 6 true false false true true true true true true true "
1343 "true true true"));
1344 else
1345 QCOMPARE(item.toAtomicValue().toString(),
1346 QLatin1String("4 true 3 654 7 41414141 C 2000-10-11Z 2001-09-10T01:02:03 "
1347 "A QString http://example.com/ 5 6 true true true true true true true true true true "
1348 "true true true"));
1349
1350}
1351
1352void tst_QXmlQuery::bindNode() const
1353{
1354 QXmlQuery query;
1355 TestSimpleNodeModel nodeModel(query.namePool());
1356
1357 query.bindVariable(localName: QLatin1String("node"), value: nodeModel.root());
1358 QByteArray out;
1359 QBuffer buff(&out);
1360 QVERIFY(buff.open(QIODevice::WriteOnly));
1361
1362 query.setQuery(sourceCode: QLatin1String("declare variable $node external; $node"));
1363 QXmlSerializer serializer(query, &buff);
1364
1365 QVERIFY(query.evaluateTo(&serializer));
1366 QCOMPARE(out, QByteArray("<nodeName/>"));
1367}
1368
1369/*!
1370 Pass in a relative URI, and make sure it is resolved against the current application directory.
1371 */
1372void tst_QXmlQuery::relativeBaseURI() const
1373{
1374 QXmlQuery query;
1375 query.setQuery(sourceCode: QLatin1String("fn:static-base-uri()"), documentURI: QUrl(QLatin1String("a/relative/uri.weirdExtension")));
1376 QVERIFY(query.isValid());
1377
1378 QByteArray result;
1379 QBuffer buffer(&result);
1380 QVERIFY(buffer.open(QIODevice::ReadWrite));
1381
1382 QXmlSerializer serializer(query, &buffer);
1383 QVERIFY(query.evaluateTo(&serializer));
1384
1385 const QUrl loaded(QUrl::fromEncoded(url: result));
1386 QUrl appPath(QUrl::fromLocalFile(localfile: QCoreApplication::applicationFilePath()));
1387
1388 QVERIFY(loaded.isValid());
1389 QVERIFY(appPath.isValid());
1390 QVERIFY(!loaded.isRelative());
1391 QVERIFY(!appPath.isRelative());
1392
1393 QFileInfo dir(appPath.toLocalFile());
1394
1395 /* We can't use QUrl::isParentOf() because it doesn't do what we want it to */
1396 if (!loaded.toLocalFile().startsWith(s: dir.absolutePath()))
1397 QTextStream(stderr) << "dir.absolutePath():" << dir.absolutePath() << "loaded.toLocalFile():" << loaded.toLocalFile();
1398
1399 checkBaseURI(baseURI: loaded, candidate: dir.absolutePath());
1400}
1401
1402void tst_QXmlQuery::emptyBaseURI() const
1403{
1404 QXmlQuery query;
1405 query.setQuery(sourceCode: QLatin1String("fn:static-base-uri()"), documentURI: QUrl());
1406 QVERIFY(query.isValid());
1407
1408 QByteArray result;
1409 QBuffer buffer(&result);
1410 QVERIFY(buffer.open(QIODevice::ReadWrite));
1411
1412 QXmlSerializer serializer(query, &buffer);
1413 QVERIFY(query.evaluateTo(&serializer));
1414
1415 const QUrl loaded(QUrl::fromEncoded(url: result));
1416 QUrl appPath(QUrl::fromLocalFile(localfile: QCoreApplication::applicationFilePath()));
1417
1418 QVERIFY(loaded.isValid());
1419 QVERIFY(appPath.isValid());
1420 QVERIFY(!loaded.isRelative());
1421 QVERIFY(!appPath.isRelative());
1422
1423 QFileInfo dir(appPath.toLocalFile());
1424 dir.setFile(QString());
1425
1426 QCOMPARE(loaded, appPath);
1427}
1428
1429/*!
1430 Ensure that QDate comes out as QDateTime.
1431 */
1432void tst_QXmlQuery::roundTripDateWithinQXmlItem() const
1433{
1434 const QDate date(1999, 9, 10);
1435 QVERIFY(date.isValid());
1436
1437 const QVariant variant(date);
1438 QVERIFY(variant.isValid());
1439 QCOMPARE(variant.type(), QVariant::Date);
1440
1441 const QXmlItem item(variant);
1442 QVERIFY(!item.isNull());
1443 QVERIFY(item.isAtomicValue());
1444
1445 const QVariant out(item.toAtomicValue());
1446 QVERIFY(out.isValid());
1447 QCOMPARE(out.type(), QVariant::Date);
1448 QCOMPARE(out.toDate(), date);
1449}
1450
1451/*!
1452 Check whether a query is valid, which uses an unbound variable.
1453 */
1454void tst_QXmlQuery::bindingMissing() const
1455{
1456 QXmlQuery query;
1457 MessageSilencer messageHandler;
1458 query.setMessageHandler(&messageHandler);
1459
1460 QFile queryFile(m_xmlPatternsDir + QLatin1String("/queries/") + QString::fromLatin1(str: "externalVariable.xq"));
1461 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1462 query.setQuery(sourceCode: &queryFile);
1463
1464 QVERIFY(!query.isValid());
1465}
1466
1467void tst_QXmlQuery::bindDefaultConstructedItem() const
1468{
1469 QFETCH(QXmlItem, item);
1470
1471 QXmlQuery query;
1472 MessageSilencer messageHandler;
1473 query.setMessageHandler(&messageHandler);
1474
1475 QFile queryFile(m_xmlPatternsDir + QLatin1String("/queries/") + QString::fromLatin1(str: "externalVariable.xq"));
1476 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1477 query.setQuery(sourceCode: &queryFile);
1478 query.bindVariable(localName: QLatin1String("externalVariable"), value: item);
1479
1480 QVERIFY(!query.isValid());
1481}
1482
1483void tst_QXmlQuery::bindDefaultConstructedItem_data() const
1484{
1485 QTest::addColumn<QXmlItem>(name: "item");
1486
1487 QTest::newRow(dataTag: "QXmlItem()") << QXmlItem();
1488 QTest::newRow(dataTag: "QXmlItem(QVariant())") << QXmlItem(QVariant());
1489 QTest::newRow(dataTag: "QXmlItem(QXmlNodeModelIndex())") << QXmlItem(QXmlNodeModelIndex());
1490}
1491
1492/*!
1493 Remove a binding by binding QXmlItem() with the same name.
1494 */
1495void tst_QXmlQuery::eraseQXmlItemBinding() const
1496{
1497 QXmlQuery query;
1498 MessageSilencer messageHandler;
1499 query.setMessageHandler(&messageHandler);
1500
1501 QFile queryFile(m_xmlPatternsDir + QLatin1String("/queries/") + QString::fromLatin1(str: "externalVariable.xq"));
1502 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1503 query.bindVariable(localName: QLatin1String("externalVariable"), value: QXmlItem(3));
1504 query.setQuery(sourceCode: &queryFile);
1505 QVERIFY(query.isValid());
1506
1507 QByteArray result;
1508 QBuffer buffer(&result);
1509 QVERIFY(buffer.open(QIODevice::ReadWrite));
1510
1511 QXmlSerializer serializer(query, &buffer);
1512 QVERIFY(query.evaluateTo(&serializer));
1513
1514 QCOMPARE(result, QByteArray("3 6<e>3</e>false"));
1515
1516 query.bindVariable(localName: QLatin1String("externalVariable"), value: QXmlItem());
1517 QVERIFY(!query.isValid());
1518}
1519
1520/*!
1521 Erase a variable binding
1522 */
1523void tst_QXmlQuery::eraseDeviceBinding() const
1524{
1525 /* Erase an existing QIODevice binding with another QIODevice binding. */
1526 {
1527 QXmlQuery query;
1528
1529 QByteArray doc("<e/>");
1530 QBuffer buffer(&doc);
1531 QVERIFY(buffer.open(QIODevice::ReadOnly));
1532
1533 query.bindVariable(localName: QLatin1String("in"), &buffer);
1534 query.setQuery(sourceCode: QLatin1String("$in"));
1535 QVERIFY(query.isValid());
1536
1537 query.bindVariable(localName: QLatin1String("in"), 0);
1538 QVERIFY(!query.isValid());
1539 }
1540
1541 /* Erase an existing QXmlItem binding with another QIODevice binding. */
1542 {
1543 QXmlQuery query;
1544
1545 query.bindVariable(localName: QLatin1String("in"), value: QXmlItem(5));
1546 query.setQuery(sourceCode: QLatin1String("$in"));
1547 QVERIFY(query.isValid());
1548
1549 query.bindVariable(localName: QLatin1String("in"), 0);
1550 QVERIFY(!query.isValid());
1551 }
1552}
1553
1554/*!
1555 Bind a variable, evaluate, bind with a different value but same type, and evaluate again.
1556 */
1557void tst_QXmlQuery::rebindVariableSameType() const
1558{
1559 QXmlQuery query;
1560 MessageSilencer messageHandler;
1561 query.setMessageHandler(&messageHandler);
1562
1563 query.bindVariable(localName: QLatin1String("externalVariable"), value: QXmlItem(3));
1564
1565 {
1566 QFile queryFile(m_xmlPatternsDir + QLatin1String("/queries/") + QString::fromLatin1(str: "externalVariable.xq"));
1567 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1568 query.setQuery(sourceCode: &queryFile);
1569 }
1570
1571 QVERIFY(query.isValid());
1572
1573 {
1574 QByteArray result;
1575 QBuffer buffer(&result);
1576 QVERIFY(buffer.open(QIODevice::ReadWrite));
1577
1578 QXmlSerializer serializer(query, &buffer);
1579 QVERIFY(query.evaluateTo(&serializer));
1580
1581 QCOMPARE(result, QByteArray("3 6<e>3</e>false"));
1582 }
1583
1584 {
1585 query.bindVariable(localName: QLatin1String("externalVariable"), value: QXmlItem(5));
1586 QByteArray result;
1587 QBuffer buffer(&result);
1588 QVERIFY(buffer.open(QIODevice::ReadWrite));
1589
1590 QXmlSerializer serializer(query, &buffer);
1591 QVERIFY(query.evaluateTo(&serializer));
1592
1593 QCOMPARE(result, QByteArray("5 8<e>5</e>false"));
1594 }
1595
1596}
1597
1598void tst_QXmlQuery::rebindVariableDifferentType() const
1599{
1600 /* Rebind QXmlItem variable with QXmlItem variable. */
1601 {
1602 QXmlQuery query;
1603 query.bindVariable(localName: QLatin1String("in"), value: QXmlItem(3));
1604 query.setQuery(sourceCode: QLatin1String("$in"));
1605 QVERIFY(query.isValid());
1606
1607 query.bindVariable(localName: QLatin1String("in"), value: QXmlItem("A string"));
1608 QVERIFY(!query.isValid());
1609 }
1610
1611 /* Rebind QIODevice variable with QXmlItem variable. */
1612 {
1613 QXmlQuery query;
1614 QBuffer buffer;
1615 buffer.setData(QByteArray("<e/>"));
1616 QVERIFY(buffer.open(QIODevice::ReadOnly));
1617
1618 query.bindVariable(localName: QLatin1String("in"), &buffer);
1619 query.setQuery(sourceCode: QLatin1String("$in"));
1620 QVERIFY(query.isValid());
1621
1622 query.bindVariable(localName: QLatin1String("in"), value: QXmlItem("A string"));
1623 QVERIFY(!query.isValid());
1624 }
1625
1626 /* Rebind QXmlItem variable with QIODevice variable. The type of the
1627 * variable changes, so a recompile is necessary. */
1628 {
1629 QXmlQuery query;
1630
1631 query.bindVariable(localName: QLatin1String("in"), value: QXmlItem(QLatin1String("A string")));
1632 query.setQuery(sourceCode: QLatin1String("$in"));
1633 QVERIFY(query.isValid());
1634
1635 QBuffer buffer;
1636 buffer.setData(QByteArray("<e/>"));
1637 QVERIFY(buffer.open(QIODevice::ReadOnly));
1638 query.bindVariable(localName: QLatin1String("in"), &buffer);
1639 QVERIFY(!query.isValid());
1640 }
1641}
1642
1643void tst_QXmlQuery::rebindVariableWithNullItem() const
1644{
1645 QXmlQuery query;
1646
1647 query.bindVariable(localName: QLatin1String("name"), value: QXmlItem(5));
1648 query.bindVariable(localName: QLatin1String("name"), value: QXmlItem());
1649}
1650
1651void tst_QXmlQuery::constCorrectness() const
1652{
1653 QXmlResultItems result;
1654 QXmlQuery tmp;
1655 tmp.setQuery(sourceCode: QLatin1String("1")); /* Just so we have a valid query. */
1656 const QXmlQuery query(tmp);
1657
1658 /* These functions should be const. */
1659 query.isValid();
1660 query.evaluateTo(result: &result);
1661 query.namePool();
1662 query.uriResolver();
1663 query.messageHandler();
1664
1665 {
1666 QString dummyString;
1667 QTextStream dummyStream(&dummyString);
1668 PushBaseliner dummy(dummyStream, query.namePool());
1669 QVERIFY(dummy.isValid());
1670 query.evaluateTo(callback: &dummy);
1671 }
1672}
1673
1674void tst_QXmlQuery::objectSize() const
1675{
1676 /* We have a d pointer. */
1677 QCOMPARE(sizeof(QXmlQuery), sizeof(void *));
1678}
1679
1680void tst_QXmlQuery::setUriResolver() const
1681{
1682 /* Set a null resolver, and make sure it can take a const pointer. */
1683 {
1684 QXmlQuery query;
1685 query.setUriResolver(static_cast<const QAbstractUriResolver *>(0));
1686 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0));
1687 }
1688
1689 {
1690 TestURIResolver resolver;
1691 QXmlQuery query;
1692 query.setUriResolver(&resolver);
1693 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(&resolver));
1694 }
1695}
1696
1697void tst_QXmlQuery::uriResolver() const
1698{
1699 /* Check default value. */
1700 {
1701 QXmlQuery query;
1702 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0));
1703 }
1704}
1705
1706void tst_QXmlQuery::messageXML() const
1707{
1708 QXmlQuery query;
1709
1710 MessageValidator messageValidator;
1711 query.setMessageHandler(&messageValidator);
1712
1713 query.setQuery(sourceCode: QLatin1String("1basicSyntaxError"));
1714
1715 QRegExp removeFilename(QLatin1String("Location: file:.*\\#"));
1716 QVERIFY(removeFilename.isValid());
1717
1718 QVERIFY(messageValidator.success());
1719 QCOMPARE(messageValidator.received().remove(removeFilename),
1720 QString::fromLatin1("Type:3\n"
1721 "Description: <html xmlns='http://www.w3.org/1999/xhtml/'><body><p>syntax error, unexpected unknown keyword</p></body></html>\n"
1722 "Identifier: http://www.w3.org/2005/xqt-errors#XPST0003\n"
1723 "1,1"));
1724}
1725
1726/*!
1727 1. Allocate QXmlResultItems
1728 2. Allocate QXmlQuery
1729 3. evaluate to the QXmlResultItems instance
1730 4. Dellocate the QXmlQuery instance
1731 5. Ensure QXmlResultItems works
1732 */
1733void tst_QXmlQuery::resultItemsDeallocatedQuery() const
1734{
1735 QXmlResultItems result;
1736
1737 {
1738 QXmlQuery query;
1739 query.setQuery(sourceCode: QLatin1String("1, 2, xs:integer(<e>3</e>)"));
1740 query.evaluateTo(result: &result);
1741 }
1742
1743 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
1744 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
1745 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
1746 QVERIFY(result.next().isNull());
1747 QVERIFY(!result.hasError());
1748}
1749
1750/*!
1751 1. Bind variable with bindVariable()
1752 2. setQuery that has 'declare variable' with same name.
1753 3. Ensure the value inside the query is used. We don't guarantee this behavior
1754 but that's what we lock.
1755 */
1756void tst_QXmlQuery::shadowedVariables() const
1757{
1758 QXmlQuery query;
1759 query.bindVariable(localName: "varName", value: QXmlItem(3));
1760 query.setQuery(sourceCode: QLatin1String("declare variable $varName := 5; $varName"));
1761
1762 QXmlResultItems result;
1763 query.evaluateTo(result: &result);
1764
1765 QCOMPARE(result.next().toAtomicValue(), QVariant(5));
1766}
1767
1768void tst_QXmlQuery::setFocusQXmlItem() const
1769{
1770 /* Make sure we can take a const reference. */
1771 {
1772 QXmlQuery query;
1773 const QXmlItem item;
1774 query.setFocus(item);
1775 }
1776
1777 // TODO evaluate with atomic value, check type
1778 // TODO evaluate with node, check type
1779 // TODO ensure that setFocus() triggers query recompilation, as appropriate.
1780 // TODO let the focus be undefined, call isvalid, call evaluate anyway
1781 // TODO let the focus be undefined, call evaluate directly
1782}
1783
1784void tst_QXmlQuery::setFocusQUrl() const
1785{
1786 /* Load a focus which isn't well-formed. */
1787 {
1788 QXmlQuery query;
1789 MessageSilencer silencer;
1790
1791 query.setMessageHandler(&silencer);
1792
1793 QVERIFY(!query.setFocus(QUrl(QLatin1String("data/notWellformed.xml"))));
1794 }
1795
1796 /* Ensure the same URI resolver is used. */
1797 {
1798 QXmlQuery query(QXmlQuery::XSLT20);
1799
1800 const TestURIResolver resolver(QUrl(inputFileAsURI(file: m_xmlPatternsDir + QLatin1String("/stylesheets/documentElement.xml"))));
1801 query.setUriResolver(&resolver);
1802
1803 QVERIFY(query.setFocus(QUrl(QLatin1String("arbitraryURI"))));
1804 query.setQuery(queryURI: QUrl(inputFileAsURI(file: m_xmlPatternsDir + QLatin1String("/stylesheets/copyWholeDocument.xsl"))));
1805 QVERIFY(query.isValid());
1806
1807 QBuffer result;
1808 QVERIFY(result.open(QIODevice::ReadWrite));
1809 QXmlSerializer serializer(query, &result);
1810 query.evaluateTo(callback: &serializer);
1811
1812 QCOMPARE(result.data(), QByteArray("<doc/>"));
1813 }
1814
1815 // TODO ensure that the focus type doesn't change from XSLT20 on the main instance.
1816}
1817
1818/*!
1819 This code poses a challenge wrt. to internal caching.
1820 */
1821void tst_QXmlQuery::setFocusQIODevice() const
1822{
1823 QXmlQuery query;
1824
1825 {
1826 QBuffer focus;
1827 focus.setData(QByteArray("<e>abc</e>"));
1828 QVERIFY(focus.open(QIODevice::ReadOnly));
1829 query.setFocus(&focus);
1830 query.setQuery(sourceCode: QLatin1String("string()"));
1831 QVERIFY(query.isValid());
1832
1833 QString output;
1834 query.evaluateTo(output: &output);
1835
1836 QCOMPARE(output, QString::fromLatin1("abc\n"));
1837 }
1838
1839 /* Set a new focus, make sure it changes & works. */
1840 {
1841 QBuffer focus2;
1842 focus2.setData(QByteArray("<e>abc2</e>"));
1843 QVERIFY(focus2.open(QIODevice::ReadOnly));
1844 query.setFocus(&focus2);
1845 QVERIFY(query.isValid());
1846
1847 QString output;
1848 query.evaluateTo(output: &output);
1849
1850 QCOMPARE(output, QString::fromLatin1("abc2\n"));
1851 }
1852}
1853
1854/*!
1855 Since we internally use variable bindings for implementing the focus, we need
1856 to make sure we don't clash in this area.
1857*/
1858void tst_QXmlQuery::setFocusQIODeviceAvoidVariableClash() const
1859{
1860 QBuffer buffer;
1861 buffer.setData("<e>focus</e>");
1862 QVERIFY(buffer.open(QIODevice::ReadOnly));
1863
1864 /* First we bind the variable name, then the focus. */
1865 {
1866 QXmlQuery query;
1867 query.bindVariable(localName: QString(QLatin1Char('u')), value: QVariant(1));
1868 query.setFocus(&buffer);
1869 query.setQuery(sourceCode: QLatin1String("string()"));
1870
1871 QString out;
1872 query.evaluateTo(output: &out);
1873
1874 QCOMPARE(out, QString::fromLatin1("focus\n"));
1875 }
1876
1877 /* First we bind the focus, then the variable name. */
1878 {
1879 QXmlQuery query;
1880 QVERIFY(buffer.open(QIODevice::ReadOnly));
1881 query.setFocus(&buffer);
1882 query.bindVariable(localName: QString(QLatin1Char('u')), value: QVariant(1));
1883 query.setQuery(sourceCode: QLatin1String("string()"));
1884
1885 QString out;
1886 query.evaluateTo(output: &out);
1887
1888 QCOMPARE(out, QString::fromLatin1("focus\n"));
1889 }
1890}
1891
1892void tst_QXmlQuery::setFocusQIODeviceFailure() const
1893{
1894 /* A not well-formed input document. */
1895 {
1896 QXmlQuery query;
1897
1898 MessageSilencer silencer;
1899 query.setMessageHandler(&silencer);
1900
1901 QBuffer input;
1902 input.setData("<e");
1903 QVERIFY(input.open(QIODevice::ReadOnly));
1904
1905 QCOMPARE(query.setFocus(&input), false);
1906 }
1907}
1908
1909void tst_QXmlQuery::setFocusQString() const
1910{
1911 QXmlQuery query;
1912
1913 /* Basic use of focus. */
1914 {
1915 QVERIFY(query.setFocus(QLatin1String("<e>textNode</e>")));
1916 query.setQuery(sourceCode: QLatin1String("string()"));
1917 QVERIFY(query.isValid());
1918 QString out;
1919 query.evaluateTo(output: &out);
1920 QCOMPARE(out, QString::fromLatin1("textNode\n"));
1921 }
1922
1923 /* Set to a new focus, make sure it changes and works. */
1924 {
1925 QVERIFY(query.setFocus(QLatin1String("<e>newFocus</e>")));
1926 QString out;
1927 query.evaluateTo(output: &out);
1928 QCOMPARE(out, QString::fromLatin1("newFocus\n"));
1929 }
1930}
1931
1932void tst_QXmlQuery::setFocusQStringFailure() const
1933{
1934 QXmlQuery query;
1935 MessageSilencer silencer;
1936
1937 query.setMessageHandler(&silencer);
1938 QVERIFY(!query.setFocus(QLatin1String("<notWellformed")));
1939
1940 /* Let's try the slight special case of a null string. */
1941 QVERIFY(!query.setFocus(QString()));
1942}
1943
1944void tst_QXmlQuery::setFocusQStringSignature() const
1945{
1946 QXmlQuery query;
1947 MessageSilencer silencer;
1948 query.setMessageHandler(&silencer);
1949
1950 const QString argument;
1951 /* We should take a const ref. */
1952 query.setFocus(argument);
1953
1954 /* We should return a bool. */
1955 static_cast<bool>(query.setFocus(QString()));
1956}
1957
1958void tst_QXmlQuery::setFocusQIODeviceTriggerWarnings() const
1959{
1960 /* A null pointer. */
1961 {
1962 QXmlQuery query;
1963
1964 QTest::ignoreMessage(type: QtWarningMsg, message: "A null QIODevice pointer cannot be passed.");
1965 QCOMPARE(query.setFocus(static_cast<QIODevice *>(0)), false);
1966 }
1967
1968 /* A non opened-device. */
1969 {
1970 QXmlQuery query;
1971
1972 QBuffer notReadable;
1973 QVERIFY(!notReadable.isReadable());
1974
1975 QTest::ignoreMessage(type: QtWarningMsg, message: "The device must be readable.");
1976 QCOMPARE(query.setFocus(&notReadable), false);
1977 }
1978}
1979
1980void tst_QXmlQuery::fnDocNetworkAccessSuccess() const
1981{
1982 if (QTest::currentDataTag() == QByteArray("http scheme")
1983 || QTest::currentDataTag() == QByteArray("ftp scheme"))
1984 QVERIFY(QtNetworkSettings::verifyTestNetworkSettings());
1985
1986 QFETCH(QUrl, uriToOpen);
1987 QFETCH(QByteArray, expectedOutput);
1988
1989 if(!uriToOpen.isValid())
1990 qDebug() << "uriToOpen:" << uriToOpen;
1991
1992 QVERIFY(uriToOpen.isValid());
1993
1994 QXmlQuery query;
1995 query.bindVariable(localName: QLatin1String("uri"), value: QVariant(uriToOpen));
1996 query.setQuery(sourceCode: QLatin1String("declare variable $uri external;\ndoc($uri)"));
1997 QVERIFY(query.isValid());
1998
1999 QByteArray result;
2000 QBuffer buffer(&result);
2001 QVERIFY(buffer.open(QIODevice::WriteOnly));
2002
2003 QXmlSerializer serializer(query, &buffer);
2004 QVERIFY(query.evaluateTo(&serializer));
2005
2006 QCOMPARE(result, expectedOutput);
2007}
2008
2009void tst_QXmlQuery::fnDocNetworkAccessSuccess_data() const
2010{
2011 QTest::addColumn<QUrl>(name: "uriToOpen");
2012 QTest::addColumn<QByteArray>(name: "expectedOutput");
2013
2014 QTest::newRow(dataTag: "file scheme")
2015 << inputFileAsURI(QFINDTESTDATA("input.xml"))
2016 << QByteArray("<!-- This is just a file for testing. --><input/>");
2017
2018 QTest::newRow(dataTag: "data scheme with ASCII")
2019 /* QUrl::toPercentEncoding(QLatin1String("<e/>")) yields "%3Ce%2F%3E". */
2020 << QUrl::fromEncoded(url: "data:application/xml,%3Ce%2F%3E")
2021 << QByteArray("<e/>");
2022
2023 QTest::newRow(dataTag: "data scheme with ASCII no MIME type")
2024 << QUrl::fromEncoded(url: "data:,%3Ce%2F%3E")
2025 << QByteArray("<e/>");
2026
2027 QTest::newRow(dataTag: "data scheme with base 64")
2028 << QUrl::fromEncoded(url: "data:application/xml;base64,PGUvPg==")
2029 << QByteArray("<e/>");
2030
2031 QTest::newRow(dataTag: "qrc scheme")
2032 << QUrl::fromEncoded(url: "qrc:/QXmlQueryTestData/data/oneElement.xml")
2033 << QByteArray("<oneElement/>");
2034
2035 if(!m_testNetwork)
2036 return;
2037
2038 QTest::newRow(dataTag: "http scheme")
2039 << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/wellFormed.xml"))
2040 << QByteArray("<!-- a comment --><e from=\"http\">Some Text</e>");
2041
2042 QTest::newRow(dataTag: "ftp scheme")
2043 << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/wellFormed.xml"))
2044 << QByteArray("<!-- a comment --><e from=\"ftp\">Some Text</e>");
2045
2046}
2047
2048void tst_QXmlQuery::fnDocNetworkAccessFailure() const
2049{
2050 if (QTest::currentDataTag() == QByteArray("http scheme, not well-formed")
2051 || QTest::currentDataTag() == QByteArray("https scheme, not well-formed")
2052 || QTest::currentDataTag() == QByteArray("ftp scheme, not well-formed"))
2053 QVERIFY(QtNetworkSettings::verifyTestNetworkSettings());
2054
2055 QFETCH(QUrl, uriToOpen);
2056
2057 QVERIFY(uriToOpen.isValid());
2058
2059 QXmlQuery query;
2060 MessageSilencer silencer;
2061 query.setMessageHandler(&silencer);
2062 query.bindVariable(localName: QLatin1String("uri"), value: QVariant(uriToOpen));
2063 query.setQuery(sourceCode: QLatin1String("declare variable $uri external;\ndoc($uri)"));
2064 QVERIFY(query.isValid());
2065
2066 QXmlResultItems result;
2067 query.evaluateTo(result: &result);
2068
2069 while(!result.next().isNull())
2070 {
2071 /* Just loop until the end. */
2072 }
2073
2074 // TODO do something that triggers a /timeout/.
2075 QVERIFY(result.hasError());
2076}
2077
2078void tst_QXmlQuery::fnDocNetworkAccessFailure_data() const
2079{
2080 QTest::addColumn<QUrl>(name: "uriToOpen");
2081
2082 QTest::newRow(dataTag: "data scheme, not-well-formed")
2083 << QUrl(QLatin1String("data:application/xml;base64,PGUvg==="));
2084
2085 QTest::newRow(dataTag: "file scheme, non-existent file")
2086 << QUrl(QLatin1String("file:///example.com/does/notExist.xml"));
2087
2088 QTest::newRow(dataTag: "http scheme, file not found")
2089 << QUrl(QLatin1String("http://www.example.com/does/not/exist.xml"));
2090
2091 QTest::newRow(dataTag: "http scheme, nonexistent host")
2092 << QUrl(QLatin1String("http://this.host.does.not.exist.I.SWear"));
2093
2094 QTest::newRow(dataTag: "qrc scheme, not well-formed")
2095 << QUrl(QLatin1String("qrc:/QXmlQueryTestData/notWellformed.xml"));
2096
2097 QTest::newRow(dataTag: "'qrc:/', non-existing file")
2098 << QUrl(QLatin1String("qrc:/QXmlQueryTestData/data/thisFileDoesNotExist.xml"));
2099
2100 if(!m_testNetwork)
2101 return;
2102
2103 QTest::newRow(dataTag: "http scheme, not well-formed")
2104 << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/notWellformed.xml"));
2105
2106 QTest::newRow(dataTag: "https scheme, not well-formed")
2107 << QUrl(QString("https://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/notWellformedViaHttps.xml"));
2108
2109 QTest::newRow(dataTag: "https scheme, nonexistent host")
2110 << QUrl(QLatin1String("https://this.host.does.not.exist.I.SWear"));
2111
2112 QTest::newRow(dataTag: "ftp scheme, nonexistent host")
2113 << QUrl(QLatin1String("ftp://this.host.does.not.exist.I.SWear"));
2114
2115 QTest::newRow(dataTag: "ftp scheme, not well-formed")
2116 << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/notWellFormed.xml"));
2117}
2118
2119/*!
2120 Create a network timeout from a QIODevice binding such
2121 that we ensure we don't hang infinitely.
2122 */
2123void tst_QXmlQuery::fnDocOnQIODeviceTimeout() const
2124{
2125 if(!m_testNetwork)
2126 return;
2127
2128 QTcpServer server;
2129 server.listen(address: QHostAddress::LocalHost, port: 1088);
2130
2131 QTcpSocket client;
2132 client.connectToHost(hostName: "localhost", port: 1088);
2133 QVERIFY(client.isReadable());
2134
2135 QXmlQuery query;
2136
2137 MessageSilencer silencer;
2138 query.setMessageHandler(&silencer);
2139
2140 query.bindVariable(localName: QLatin1String("inDevice"), &client);
2141 query.setQuery(sourceCode: QLatin1String("declare variable $inDevice external;\ndoc($inDevice)"));
2142 QVERIFY(query.isValid());
2143
2144 QXmlResultItems result;
2145 query.evaluateTo(result: &result);
2146 QXmlItem next(result.next());
2147
2148 while(!next.isNull())
2149 {
2150 next = result.next();
2151 }
2152
2153 QVERIFY(result.hasError());
2154}
2155
2156/*!
2157 When changing query, the static context must change too, such that
2158 the source locations are updated.
2159 */
2160void tst_QXmlQuery::recompilationWithEvaluateToResultFailing() const
2161{
2162 QXmlQuery query;
2163 MessageSilencer silencer;
2164 query.setMessageHandler(&silencer);
2165
2166 query.setQuery(sourceCode: QLatin1String("1 + 1")); /* An arbitrary valid query. */
2167 QVERIFY(query.isValid()); /* Trigger query compilation. */
2168
2169 query.setQuery(sourceCode: QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2170 QVERIFY(query.isValid()); /* Trigger second compilation. */
2171
2172 QXmlResultItems items;
2173 query.evaluateTo(result: &items);
2174 items.next();
2175 QVERIFY(items.hasError());
2176}
2177
2178void tst_QXmlQuery::secondEvaluationWithEvaluateToResultFailing() const
2179{
2180 QXmlQuery query;
2181 MessageSilencer silencer;
2182 query.setMessageHandler(&silencer);
2183
2184 query.setQuery(sourceCode: QLatin1String("1 + 1")); /* An arbitrary valid query. */
2185 QVERIFY(query.isValid()); /* Trigger query compilation. */
2186
2187 query.setQuery(sourceCode: QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2188 /* We don't call isValid(). */
2189QXmlResultItems items;
2190 query.evaluateTo(result: &items);
2191 items.next();
2192 QVERIFY(items.hasError());
2193}
2194
2195/*!
2196 Compilation is triggered in the evaluation function due to no call to QXmlQuery::isValid().
2197 */
2198void tst_QXmlQuery::recompilationWithEvaluateToReceiver() const
2199{
2200 QXmlQuery query;
2201 MessageSilencer silencer;
2202 query.setMessageHandler(&silencer);
2203
2204 query.setQuery(sourceCode: QLatin1String("1 + 1")); /* An arbitrary valid query. */
2205 QVERIFY(query.isValid()); /* Trigger query compilation. */
2206
2207 query.setQuery(sourceCode: QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2208 /* We don't call isValid(). */
2209
2210 QByteArray dummy;
2211 QBuffer buffer(&dummy);
2212 buffer.open(openMode: QIODevice::WriteOnly);
2213
2214 QXmlSerializer serializer(query, &buffer);
2215
2216 QVERIFY(!query.evaluateTo(&serializer));
2217}
2218
2219void tst_QXmlQuery::evaluateToQStringListOnInvalidQuery() const
2220{
2221 MessageSilencer silencer;
2222
2223 /* Invoke on a default constructed object. */
2224 {
2225 QXmlQuery query;
2226 QStringList out;
2227 QVERIFY(!query.evaluateTo(&out));
2228 }
2229
2230 /* Invoke on a syntactically invalid query. */
2231 {
2232 QXmlQuery query;
2233 QStringList out;
2234 MessageSilencer silencer;
2235
2236 query.setMessageHandler(&silencer);
2237 query.setQuery(sourceCode: QLatin1String("1 + "));
2238
2239 QVERIFY(!query.evaluateTo(&out));
2240 }
2241
2242 /* Invoke on a query with the wrong type, one atomic. */
2243 {
2244 QXmlQuery query;
2245 QStringList out;
2246
2247 query.setQuery(sourceCode: QLatin1String("1"));
2248 query.setMessageHandler(&silencer);
2249 QVERIFY(!query.evaluateTo(&out));
2250 }
2251
2252 /* Invoke on a query with the wrong type, one element. */
2253 {
2254 QXmlQuery query;
2255 QStringList out;
2256
2257 query.setQuery(sourceCode: QLatin1String("<e/>"));
2258 QVERIFY(!query.evaluateTo(&out));
2259 }
2260
2261 /* Invoke on a query with the wrong type, mixed nodes & atomics. */
2262 {
2263 QXmlQuery query;
2264 QStringList out;
2265
2266 query.setQuery(sourceCode: QLatin1String("<e/>, 1, 'a string'"));
2267 query.setMessageHandler(&silencer);
2268 QVERIFY(!query.evaluateTo(&out));
2269 }
2270
2271 /* Evaluate the empty sequence. */
2272 {
2273 QXmlQuery query;
2274 QStringList out;
2275
2276 query.setQuery(sourceCode: QLatin1String("()"));
2277 QVERIFY(!query.evaluateTo(&out));
2278 QVERIFY(out.isEmpty());
2279 }
2280}
2281
2282void tst_QXmlQuery::evaluateToQStringList() const
2283{
2284 QFETCH(QString, queryString);
2285 QFETCH(QStringList, expectedOutput);
2286
2287 QXmlQuery query;
2288 query.setQuery(sourceCode: queryString);
2289 QStringList out;
2290 QVERIFY(query.isValid());
2291
2292 QVERIFY(query.evaluateTo(&out));
2293
2294 QCOMPARE(out, expectedOutput);
2295}
2296
2297void tst_QXmlQuery::evaluateToQStringListTriggerWarnings() const
2298{
2299 QXmlQuery query;
2300
2301 QTest::ignoreMessage(type: QtWarningMsg, message: "A non-null callback must be passed.");
2302 QCOMPARE(query.evaluateTo(static_cast<QStringList *>(0)),
2303 false);
2304}
2305
2306void tst_QXmlQuery::evaluateToQStringList_data() const
2307{
2308 QTest::addColumn<QString>(name: "queryString");
2309 QTest::addColumn<QStringList>(name: "expectedOutput");
2310
2311 QTest::newRow(dataTag: "One atomic")
2312 << QString::fromLatin1(str: "(1 + 1) cast as xs:string")
2313 << QStringList(QString::fromLatin1(str: "2"));
2314
2315 {
2316 QStringList expected;
2317 expected << QLatin1String("2");
2318 expected << QLatin1String("a string");
2319
2320 QTest::newRow(dataTag: "Two atomics")
2321 << QString::fromLatin1(str: "(1 + 1) cast as xs:string, 'a string'")
2322 << expected;
2323 }
2324
2325 QTest::newRow(dataTag: "A query which evaluates to sub-types of xs:string.")
2326 << QString::fromLatin1(str: "xs:NCName('NCName'), xs:normalizedString(' a b c ')")
2327 << (QStringList() << QString::fromLatin1(str: "NCName")
2328 << QString::fromLatin1(str: " a b c "));
2329
2330 QTest::newRow(dataTag: "A query which evaluates to two elements.")
2331 << QString::fromLatin1(str: "string(<e>theString1</e>), string(<e>theString2</e>)")
2332 << (QStringList() << QString::fromLatin1(str: "theString1")
2333 << QString::fromLatin1(str: "theString2"));
2334}
2335
2336/*!
2337 Ensure that we don't automatically convert non-xs:string values.
2338 */
2339void tst_QXmlQuery::evaluateToQStringListNoConversion() const
2340{
2341 QXmlQuery query;
2342 query.setQuery(sourceCode: QString::fromLatin1(str: "<e/>"));
2343 QVERIFY(query.isValid());
2344 QStringList result;
2345 QVERIFY(!query.evaluateTo(&result));
2346}
2347
2348void tst_QXmlQuery::evaluateToQIODevice() const
2349{
2350 /* an XQuery, check that no indentation is performed. */
2351 {
2352 QBuffer out;
2353 QVERIFY(out.open(QIODevice::ReadWrite));
2354
2355 QXmlQuery query;
2356 query.setQuery(sourceCode: QLatin1String("<a><b/></a>"));
2357 QVERIFY(query.isValid());
2358 QVERIFY(query.evaluateTo(&out));
2359 QCOMPARE(out.data(), QByteArray("<a><b/></a>"));
2360 }
2361}
2362
2363void tst_QXmlQuery::evaluateToQIODeviceTriggerWarnings() const
2364{
2365 QXmlQuery query;
2366
2367 QTest::ignoreMessage(type: QtWarningMsg, message: "The pointer to the device cannot be null.");
2368 QCOMPARE(query.evaluateTo(static_cast<QIODevice *>(0)),
2369 false);
2370
2371 QBuffer buffer;
2372
2373 QTest::ignoreMessage(type: QtWarningMsg, message: "The device must be writable.");
2374 QCOMPARE(query.evaluateTo(&buffer),
2375 false);
2376}
2377
2378void tst_QXmlQuery::evaluateToQIODeviceSignature() const
2379{
2380 /* The function should be const. */
2381 {
2382 QBuffer out;
2383 QVERIFY(out.open(QIODevice::ReadWrite));
2384
2385 const QXmlQuery query;
2386
2387 query.evaluateTo(target: &out);
2388 }
2389}
2390
2391void tst_QXmlQuery::evaluateToQIODeviceOnInvalidQuery() const
2392{
2393 QBuffer out;
2394 QVERIFY(out.open(QIODevice::WriteOnly));
2395
2396 /* On syntactically invalid query. */
2397 {
2398 QXmlQuery query;
2399 MessageSilencer silencer;
2400 query.setMessageHandler(&silencer);
2401 query.setQuery(sourceCode: QLatin1String("1 +"));
2402 QVERIFY(!query.isValid());
2403 QVERIFY(!query.evaluateTo(&out));
2404 }
2405
2406 /* On null QXmlQuery instance. */
2407 {
2408 QXmlQuery query;
2409 QVERIFY(!query.evaluateTo(&out));
2410 }
2411
2412}
2413
2414void tst_QXmlQuery::setQueryQIODeviceQUrl() const
2415{
2416 /* Basic test. */
2417 {
2418 QBuffer buffer;
2419 buffer.setData("1, 2, 2 + 1");
2420 QVERIFY(buffer.open(QIODevice::ReadOnly));
2421
2422 QXmlQuery query;
2423 query.setQuery(sourceCode: &buffer);
2424 QVERIFY(query.isValid());
2425
2426 QXmlResultItems result;
2427 query.evaluateTo(result: &result);
2428 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
2429 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
2430 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
2431 QVERIFY(result.next().isNull());
2432 QVERIFY(!result.hasError());
2433 }
2434
2435 /* Set query that is invalid. */
2436 {
2437 QBuffer buffer;
2438 buffer.setData("1, ");
2439 QVERIFY(buffer.open(QIODevice::ReadOnly));
2440
2441 QXmlQuery query;
2442 MessageSilencer silencer;
2443 query.setMessageHandler(&silencer);
2444 query.setQuery(sourceCode: &buffer);
2445 QVERIFY(!query.isValid());
2446 }
2447
2448 /* Check that the base URI passes through. */
2449 {
2450 QBuffer buffer;
2451 buffer.setData("string(static-base-uri())");
2452 QVERIFY(buffer.open(QIODevice::ReadOnly));
2453
2454 QXmlQuery query;
2455 query.setQuery(sourceCode: &buffer, documentURI: QUrl::fromEncoded(url: "http://www.example.com/QIODeviceQUrl"));
2456 QVERIFY(query.isValid());
2457
2458 QStringList result;
2459 query.evaluateTo(target: &result);
2460 QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
2461 }
2462}
2463
2464void tst_QXmlQuery::setQueryQIODeviceQUrlTriggerWarnings() const
2465{
2466 QXmlQuery query;
2467 QTest::ignoreMessage(type: QtWarningMsg, message: "A null QIODevice pointer cannot be passed.");
2468 query.setQuery(sourceCode: 0);
2469
2470 QBuffer buffer;
2471 QTest::ignoreMessage(type: QtWarningMsg, message: "The device must be readable.");
2472 query.setQuery(sourceCode: &buffer);
2473}
2474
2475void tst_QXmlQuery::setQueryQString() const
2476{
2477 /* Basic test. */
2478 {
2479 QXmlQuery query;
2480 query.setQuery(sourceCode: QLatin1String("1, 2, 2 + 1"));
2481 QVERIFY(query.isValid());
2482
2483 QXmlResultItems result;
2484 query.evaluateTo(result: &result);
2485 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
2486 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
2487 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
2488 QVERIFY(result.next().isNull());
2489 QVERIFY(!result.hasError());
2490 }
2491
2492 /* Set query that is invalid. */
2493 {
2494 MessageSilencer silencer;
2495 QXmlQuery query;
2496 query.setMessageHandler(&silencer);
2497 query.setQuery(sourceCode: QLatin1String("1, "));
2498 QVERIFY(!query.isValid());
2499 }
2500
2501 /* Check that the base URI passes through. */
2502 {
2503 QXmlQuery query;
2504 query.setQuery(sourceCode: QLatin1String("string(static-base-uri())"), documentURI: QUrl::fromEncoded(url: "http://www.example.com/QIODeviceQUrl"));
2505 QVERIFY(query.isValid());
2506
2507 QStringList result;
2508 query.evaluateTo(target: &result);
2509 QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
2510 }
2511}
2512
2513void tst_QXmlQuery::setQueryQUrlSuccess() const
2514{
2515 if (QTest::currentDataTag() == QByteArray("A valid query via the ftp scheme")
2516 || QTest::currentDataTag() == QByteArray("A valid query via the http scheme"))
2517 QVERIFY(QtNetworkSettings::verifyTestNetworkSettings());
2518
2519 QFETCH(QUrl, queryURI);
2520 QFETCH(QByteArray, expectedOutput);
2521
2522 QVERIFY(queryURI.isValid());
2523
2524 QXmlQuery query;
2525
2526 MessageSilencer silencer;
2527 query.setMessageHandler(&silencer);
2528
2529 query.setQuery(queryURI);
2530 QVERIFY(query.isValid());
2531
2532 QByteArray out;
2533 QBuffer buffer(&out);
2534 QVERIFY(buffer.open(QIODevice::WriteOnly));
2535 QXmlSerializer serializer(query, &buffer);
2536
2537 query.evaluateTo(callback: &serializer);
2538 QCOMPARE(out, expectedOutput);
2539}
2540
2541void tst_QXmlQuery::setQueryQUrlSuccess_data() const
2542{
2543 QTest::addColumn<QUrl>(name: "queryURI");
2544 QTest::addColumn<QByteArray>(name: "expectedOutput");
2545
2546 QTest::newRow(dataTag: "A valid query via the data scheme")
2547 << QUrl::fromEncoded(url: "data:application/xml,1%20%2B%201") /* "1 + 1" */
2548 << QByteArray("2");
2549
2550 QTest::newRow(dataTag: "A valid query via the file scheme")
2551 << QUrl::fromLocalFile(localfile: inputFile(file: m_xmlPatternsDir + QLatin1String("/queries/") + QLatin1String("onePlusOne.xq")))
2552 << QByteArray("2");
2553
2554 if(!m_testNetwork)
2555 return;
2556
2557 QTest::newRow(dataTag: "A valid query via the ftp scheme")
2558 << QUrl::fromEncoded(url: QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/viaFtp.xq").toLatin1())
2559 << QByteArray("This was received via FTP");
2560
2561 QTest::newRow(dataTag: "A valid query via the http scheme")
2562 << QUrl::fromEncoded(url: QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/viaHttp.xq").toLatin1())
2563 << QByteArray("This was received via HTTP.");
2564}
2565
2566void tst_QXmlQuery::setQueryQUrlFailSucceed() const
2567{
2568 QXmlQuery query;
2569 MessageSilencer silencer;
2570
2571 query.setMessageHandler(&silencer);
2572
2573 query.setQuery(sourceCode: QLatin1String("1 + 1"));
2574 QVERIFY(query.isValid());
2575
2576 query.setQuery(queryURI: QUrl::fromEncoded(url: "file://example.com/does/not/exist"));
2577 QVERIFY(!query.isValid());
2578}
2579
2580void tst_QXmlQuery::setQueryQUrlFailure() const
2581{
2582 if (QTest::currentDataTag() == QByteArray("A query via http:// that is completely empty, but readable.")
2583 || QTest::currentDataTag() == QByteArray("A query via ftp:// that is completely empty, but readable."))
2584 QVERIFY(QtNetworkSettings::verifyTestNetworkSettings());
2585
2586 QFETCH(QUrl, queryURI);
2587
2588 MessageSilencer silencer;
2589
2590 QXmlQuery query;
2591 query.setMessageHandler(&silencer);
2592 query.setQuery(queryURI);
2593 QVERIFY(!query.isValid());
2594}
2595
2596void tst_QXmlQuery::setQueryQUrlFailure_data() const
2597{
2598 const auto localFileUrl = [](const QString &relPath) {
2599 return QUrl::fromLocalFile(localfile: QCoreApplication::applicationFilePath()).resolved(relative: QUrl(relPath));
2600 };
2601 QTest::addColumn<QUrl>(name: "queryURI");
2602
2603 QTest::newRow(dataTag: "Query via file:// that does not exist.")
2604 << QUrl::fromEncoded(url: "file://example.com/does/not/exist");
2605
2606 QTest::newRow(dataTag: "A query via file:// that is completely empty, but readable.")
2607 << localFileUrl(QStringLiteral("../xmlpatterns/queries/completelyEmptyQuery.xq"));
2608
2609 {
2610 const QString name(QLatin1String("nonReadableFile.xq"));
2611 QFile outFile(name);
2612 QVERIFY(outFile.open(QIODevice::WriteOnly));
2613 outFile.write(data: QByteArray("1"));
2614 outFile.close();
2615 /* On some windows versions, this fails, so we don't check that this works with QVERIFY. */
2616 if (outFile.setPermissions(QFile::Permissions())) {
2617 QTest::newRow(dataTag: "Query via file:/ that does not have read permissions.")
2618 << localFileUrl(name);
2619 }
2620 }
2621
2622 if(!m_testNetwork)
2623 return;
2624
2625 QTest::newRow(dataTag: "Query via HTTP that does not exist.")
2626 << QUrl::fromEncoded(url: "http://example.com/NoQuery/ISWear");
2627
2628 /*
2629 QTest::newRow("Query via FTP that does not exist.")
2630 << QUrl::fromEncoded("ftp://example.com/NoQuery/ISWear");
2631 */
2632
2633 QTest::newRow(dataTag: "A query via http:// that is completely empty, but readable.")
2634 << QUrl::fromEncoded(url: QString(
2635 "http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/completelyEmptyQuery.xq").toLatin1());
2636
2637 QTest::newRow(dataTag: "A query via ftp:// that is completely empty, but readable.")
2638 << QUrl::fromEncoded(url: QString(
2639 "ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/completelyEmptyQuery.xq").toLatin1());
2640
2641}
2642
2643void tst_QXmlQuery::setQueryQUrlBaseURI() const
2644{
2645 QFETCH(QUrl, inputBaseURI);
2646 QFETCH(QUrl, expectedBaseURI);
2647
2648 QXmlQuery query;
2649
2650 query.setQuery(queryURI: QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq")), baseURI: inputBaseURI);
2651 QVERIFY(query.isValid());
2652
2653 QStringList result;
2654 QVERIFY(query.evaluateTo(&result));
2655 QCOMPARE(result.count(), 1);
2656
2657 if(qstrcmp(str1: QTest::currentDataTag(), str2: "Relative base URI") == 0)
2658 checkBaseURI(baseURI: QUrl(result.first()), candidate: QCoreApplication::applicationFilePath());
2659 else
2660 QCOMPARE(result.first(), expectedBaseURI.toString());
2661}
2662
2663void tst_QXmlQuery::setQueryQUrlBaseURI_data() const
2664{
2665 QTest::addColumn<QUrl>(name: "inputBaseURI");
2666 QTest::addColumn<QUrl>(name: "expectedBaseURI");
2667
2668 QTest::newRow(dataTag: "absolute HTTP")
2669 << QUrl(QLatin1String("http://www.example.com/"))
2670 << QUrl(QLatin1String("http://www.example.com/"));
2671
2672 QTest::newRow(dataTag: "None, so the query URI is used")
2673 << QUrl()
2674 << QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq"));
2675
2676 QTest::newRow(dataTag: "Relative base URI")
2677 << QUrl(QLatin1String("../data/relative.uri"))
2678 << QUrl();
2679}
2680
2681/*!
2682 1. Create a valid query.
2683 2. Call setQuery(QUrl), with a query file that doesn't exist.
2684 3. Verify that the query has changed state into invalid.
2685 */
2686void tst_QXmlQuery::setQueryWithNonExistentQUrlOnValidQuery() const
2687{
2688 QXmlQuery query;
2689
2690 MessageSilencer messageSilencer;
2691 query.setMessageHandler(&messageSilencer);
2692
2693 query.setQuery(sourceCode: QLatin1String("1 + 1"));
2694 QVERIFY(query.isValid());
2695
2696 query.setQuery(queryURI: QUrl::fromEncoded(url: "qrc:/QXmlQueryTestData/DOESNOTEXIST.xq"));
2697 QVERIFY(!query.isValid());
2698}
2699
2700/*!
2701 1. Create a valid query.
2702 2. Call setQuery(QUrl), with a query file that is invalid.
2703 3. Verify that the query has changed state into invalid.
2704 */
2705void tst_QXmlQuery::setQueryWithInvalidQueryFromQUrlOnValidQuery() const
2706{
2707 QXmlQuery query;
2708
2709 MessageSilencer messageSilencer;
2710 query.setMessageHandler(&messageSilencer);
2711
2712 query.setQuery(sourceCode: QLatin1String("1 + 1"));
2713 QVERIFY(query.isValid());
2714
2715 query.setQuery(queryURI: QUrl::fromEncoded(url: "qrc:/QXmlQueryTestData/queries/syntaxError.xq"));
2716 QVERIFY(!query.isValid());
2717}
2718
2719/*!
2720 This triggered two bugs:
2721
2722 - First, the DynamicContext wasn't assigned to QXmlResultItems, meaning it went out of
2723 scope and therefore deallocated the document pool, and calls
2724 to QXmlResultItems::next() would use dangling pointers.
2725
2726 - Conversion between QPatternist::Item and QXmlItem was incorrectly done, leading to nodes
2727 being treated as atomic values, and subsequent crashes.
2728
2729 */
2730void tst_QXmlQuery::retrieveNameFromQuery() const
2731{
2732 QFETCH(QString, queryString);
2733 QFETCH(QString, expectedName);
2734
2735 QXmlQuery query;
2736 query.setQuery(sourceCode: queryString);
2737 QVERIFY(query.isValid());
2738 QXmlResultItems result;
2739 query.evaluateTo(result: &result);
2740
2741 QVERIFY(!result.hasError());
2742
2743 const QXmlItem item(result.next());
2744 QVERIFY(!result.hasError());
2745 QVERIFY(!item.isNull());
2746 QVERIFY(item.isNode());
2747
2748 const QXmlNodeModelIndex node(item.toNodeModelIndex());
2749 QVERIFY(!node.isNull());
2750
2751 QCOMPARE(node.model()->name(node).localName(query.namePool()), expectedName);
2752}
2753
2754void tst_QXmlQuery::retrieveNameFromQuery_data() const
2755{
2756 QTest::addColumn<QString>(name: "queryString");
2757 QTest::addColumn<QString>(name: "expectedName");
2758
2759 QTest::newRow(dataTag: "Document-node")
2760 << QString::fromLatin1(str: "document{<elementName/>}")
2761 << QString();
2762
2763 QTest::newRow(dataTag: "Element")
2764 << QString::fromLatin1(str: "document{<elementName/>}/*")
2765 << QString::fromLatin1(str: "elementName");
2766}
2767
2768/*!
2769 Binding a null QString leads to no variable binding, but an
2770 empty non-null QString is possible.
2771 */
2772void tst_QXmlQuery::bindEmptyNullString() const
2773{
2774 MessageSilencer messageHandler;
2775 QXmlQuery query;
2776 query.setMessageHandler(&messageHandler);
2777 query.setQuery(sourceCode: QLatin1String("declare variable $v external; $v"));
2778 /* Here, we effectively pass an invalid QVariant. */
2779 query.bindVariable(localName: QLatin1String("v"), value: QVariant(QString()));
2780 QVERIFY(!query.isValid());
2781
2782 QStringList result;
2783 QVERIFY(!query.evaluateTo(&result));
2784}
2785
2786void tst_QXmlQuery::bindEmptyString() const
2787{
2788 QXmlQuery query;
2789 query.bindVariable(localName: QLatin1String("v"), value: QVariant(QString(QLatin1String(""))));
2790 query.setQuery(sourceCode: QLatin1String("declare variable $v external; $v"));
2791 QVERIFY(query.isValid());
2792
2793 QStringList result;
2794 QVERIFY(query.evaluateTo(&result));
2795 QStringList expected((QString()));
2796 QCOMPARE(result, expected);
2797}
2798
2799void tst_QXmlQuery::cleanupTestCase() const
2800{
2801 /* Remove a weird file we created. */
2802 const QString name(QLatin1String("nonReadableFile.xq"));
2803
2804 if(QFile::exists(fileName: name))
2805 {
2806 QFile file(name);
2807 QVERIFY(file.setPermissions(QFile::WriteOwner));
2808 QVERIFY(file.remove());
2809 }
2810}
2811
2812void tst_QXmlQuery::declareUnavailableExternal() const
2813{
2814 QXmlQuery query;
2815 MessageSilencer silencer;
2816 query.setMessageHandler(&silencer);
2817 query.setQuery(sourceCode: QLatin1String("declare variable $var external;"
2818 "1 + 1"));
2819 /* We do not bind $var with QXmlQuery::bindVariable(). */
2820 QVERIFY(!query.isValid());
2821}
2822
2823/*!
2824 This test triggers an assert in one of the cache iterator
2825 with MSVC 2005 when compiled in debug mode.
2826 */
2827void tst_QXmlQuery::msvcCacheIssue() const
2828{
2829 QXmlQuery query;
2830 query.bindVariable(localName: QLatin1String("externalVariable"), value: QXmlItem("Variable Value"));
2831 query.setQuery(queryURI: QUrl::fromLocalFile(localfile: m_xmlPatternsDir + QLatin1String("/queries/") + QString::fromLatin1(str: "externalVariableUsedTwice.xq")));
2832 QStringList result;
2833 QVERIFY(query.evaluateTo(&result));
2834
2835 QCOMPARE(result,
2836 QStringList() << QString::fromLatin1("Variable Value") << QString::fromLatin1("Variable Value"));
2837}
2838
2839void tst_QXmlQuery::unavailableExternalVariable() const
2840{
2841 QXmlQuery query;
2842
2843 MessageSilencer silencer;
2844 query.setMessageHandler(&silencer);
2845
2846 query.setQuery(sourceCode: QLatin1String("declare variable $foo external; 1"));
2847
2848 QVERIFY(!query.isValid());
2849}
2850
2851/*!
2852 Ensure that setUriResolver() affects \c fn:doc() and \c fn:doc-available().
2853 */
2854void tst_QXmlQuery::useUriResolver() const
2855{
2856 class TestUriResolver : public QAbstractUriResolver
2857 , private TestFundament
2858 {
2859 public:
2860 TestUriResolver() {}
2861 virtual QUrl resolve(const QUrl &relative,
2862 const QUrl &baseURI) const
2863 {
2864 Q_UNUSED(relative);
2865 QString fixedInputFile = inputFile(file: m_xmlPatternsDir + QLatin1String("/queries/") + QLatin1String("simpleDocument.xml"));
2866#ifdef Q_OS_WIN
2867 // A file path with drive letter is not a valid relative URI, so remove the drive letter.
2868 // Note that can't just use inputFileAsURI() instead of inputFile() as that doesn't
2869 // produce a relative URI either.
2870 if (fixedInputFile.size() > 1 && fixedInputFile.at(1) == QLatin1Char(':'))
2871 fixedInputFile.remove(0, 2);
2872#endif
2873 return baseURI.resolved(relative: fixedInputFile);
2874 }
2875 };
2876
2877 const TestUriResolver uriResolver;
2878 QXmlQuery query;
2879
2880 query.setUriResolver(&uriResolver);
2881 query.setQuery(sourceCode: QLatin1String("let $i := 'http://www.example.com/DoesNotExist'"
2882 "return (string(doc($i)), doc-available($i))"));
2883
2884
2885 QXmlResultItems result;
2886 query.evaluateTo(result: &result);
2887
2888 QVERIFY(!result.hasError());
2889 QCOMPARE(result.next().toAtomicValue().toString(), QString::fromLatin1("text text node"));
2890 QCOMPARE(result.next().toAtomicValue().toBool(), true);
2891 QVERIFY(result.next().isNull());
2892 QVERIFY(!result.hasError());
2893}
2894
2895void tst_QXmlQuery::queryWithFocusAndVariable() const
2896{
2897 QXmlQuery query;
2898 query.setFocus(QXmlItem(5));
2899 query.bindVariable(localName: QLatin1String("var"), value: QXmlItem(2));
2900
2901 query.setQuery(sourceCode: QLatin1String("string(. * $var)"));
2902
2903 QStringList result;
2904
2905 QVERIFY(query.evaluateTo(&result));
2906
2907 QCOMPARE(result, QStringList(QLatin1String("10")));
2908}
2909
2910void tst_QXmlQuery::undefinedFocus() const
2911{
2912 QXmlQuery query;
2913
2914 MessageSilencer silencer;
2915 query.setMessageHandler(&silencer);
2916
2917 query.setQuery(sourceCode: QLatin1String("."));
2918 QVERIFY(!query.isValid());
2919}
2920
2921void tst_QXmlQuery::basicFocusUsage() const
2922{
2923 QXmlQuery query;
2924
2925 MessageSilencer silencer;
2926 query.setMessageHandler(&silencer);
2927
2928 query.setFocus(QXmlItem(5));
2929 query.setQuery(sourceCode: QLatin1String("string(. * .)"));
2930 QVERIFY(query.isValid());
2931
2932 QStringList result;
2933 QVERIFY(query.evaluateTo(&result));
2934
2935 QCOMPARE(result, QStringList(QLatin1String("25")));
2936}
2937
2938/*!
2939 Triggers an ownership related crash.
2940 */
2941void tst_QXmlQuery::copyCheckMessageHandler() const
2942{
2943 QXmlQuery query;
2944 QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0));
2945
2946 query.setQuery(sourceCode: QLatin1String("doc('qrc:/QXmlQueryTestData/data/oneElement.xml')"));
2947 /* By now, we should have set the builtin message handler. */
2948 const QAbstractMessageHandler *const messageHandler = query.messageHandler();
2949 QVERIFY(messageHandler);
2950
2951 {
2952 /* This copies QXmlQueryPrivate::m_ownerObject, and its destructor
2953 * will delete it, and hence the builtin message handler attached to it. */
2954 QXmlQuery copy(query);
2955 }
2956
2957 QXmlResultItems result;
2958 query.evaluateTo(result: &result);
2959
2960 while(!result.next().isNull())
2961 {
2962 }
2963 QVERIFY(!result.hasError());
2964}
2965
2966void tst_QXmlQuery::queryLanguage() const
2967{
2968 /* Check default value. */
2969 {
2970 const QXmlQuery query;
2971 QCOMPARE(query.queryLanguage(), QXmlQuery::XQuery10);
2972 }
2973
2974 /* Check default value of copies default instance. */
2975 {
2976 const QXmlQuery query1;
2977 const QXmlQuery query2(query1);
2978
2979 QCOMPARE(query1.queryLanguage(), QXmlQuery::XQuery10);
2980 QCOMPARE(query2.queryLanguage(), QXmlQuery::XQuery10);
2981 }
2982}
2983
2984void tst_QXmlQuery::queryLanguageSignature() const
2985{
2986 /* This getter should be const. */
2987 QXmlQuery query;
2988 query.queryLanguage();
2989}
2990
2991void tst_QXmlQuery::enumQueryLanguage() const
2992{
2993 /* These enum values should be possible to OR for future plans. */
2994 QCOMPARE(int(QXmlQuery::XQuery10), 1);
2995 QCOMPARE(int(QXmlQuery::XSLT20), 2);
2996 QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintSelector), 1024);
2997 QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintField), 2048);
2998 QCOMPARE(int(QXmlQuery::XPath20), 4096);
2999}
3000
3001void tst_QXmlQuery::setInitialTemplateNameQXmlName() const
3002{
3003 QXmlQuery query(QXmlQuery::XSLT20);
3004 QXmlNamePool np(query.namePool());
3005 const QXmlName name(np, QLatin1String("main"));
3006
3007 query.setInitialTemplateName(name);
3008
3009 QCOMPARE(query.initialTemplateName(), name);
3010
3011 query.setQuery(queryURI: QUrl(inputFileAsURI(file: m_xmlPatternsDir + QLatin1String("/stylesheets/namedTemplate.xsl"))));
3012 QVERIFY(query.isValid());
3013
3014 QBuffer result;
3015 QVERIFY(result.open(QIODevice::ReadWrite));
3016 QXmlSerializer serializer(query, &result);
3017 query.evaluateTo(callback: &serializer);
3018
3019 QCOMPARE(result.data(), QByteArray("1 2 3 4 5"));
3020
3021 // TODO invoke a template which has required params.
3022}
3023
3024void tst_QXmlQuery::setInitialTemplateNameQXmlNameSignature() const
3025{
3026 QXmlQuery query;
3027 QXmlNamePool np(query.namePool());
3028 const QXmlName name(np, QLatin1String("foo"));
3029
3030 /* The signature should take a const reference. */
3031 query.setInitialTemplateName(name);
3032}
3033
3034void tst_QXmlQuery::setInitialTemplateNameQString() const
3035{
3036 QXmlQuery query;
3037 QXmlNamePool np(query.namePool());
3038 query.setInitialTemplateName(QLatin1String("foo"));
3039
3040 QCOMPARE(query.initialTemplateName(), QXmlName(np, QLatin1String("foo")));
3041}
3042
3043void tst_QXmlQuery::setInitialTemplateNameQStringSignature() const
3044{
3045 const QString name(QLatin1String("name"));
3046 QXmlQuery query;
3047
3048 /* We should take a const reference. */
3049 query.setInitialTemplateName(name);
3050}
3051
3052void tst_QXmlQuery::initialTemplateName() const
3053{
3054 /* Check our default value. */
3055 QXmlQuery query;
3056 QCOMPARE(query.initialTemplateName(), QXmlName());
3057 QVERIFY(query.initialTemplateName().isNull());
3058}
3059
3060void tst_QXmlQuery::initialTemplateNameSignature() const
3061{
3062 const QXmlQuery query;
3063 /* This should be a const member. */
3064 query.initialTemplateName();
3065}
3066
3067void tst_QXmlQuery::setNetworkAccessManager() const
3068{
3069
3070 /* Ensure fn:doc() picks up the right QNetworkAccessManager. */
3071 {
3072 NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
3073 QUrl(inputFileAsURI(file: m_xmlPatternsDir + QLatin1String("/queries/simpleDocument.xml"))));
3074 QVERIFY(networkOverrider.isValid());
3075
3076 QXmlQuery query;
3077 query.setNetworkAccessManager(&networkOverrider);
3078 query.setQuery(sourceCode: QLatin1String("string(doc('tag:example.com:DOESNOTEXIST'))"));
3079 QVERIFY(query.isValid());
3080
3081 QStringList result;
3082 QVERIFY(query.evaluateTo(&result));
3083
3084 QCOMPARE(result, QStringList(QLatin1String("text text node")));
3085 }
3086
3087 /* Ensure setQuery() is using the right network manager. */
3088 {
3089 NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
3090 QUrl(inputFileAsURI(file: m_xmlPatternsDir + QLatin1String("/queries/concat.xq"))));
3091 QVERIFY(networkOverrider.isValid());
3092
3093 QXmlQuery query;
3094 query.setNetworkAccessManager(&networkOverrider);
3095 query.setQuery(queryURI: QUrl("tag:example.com:DOESNOTEXIST"));
3096 QVERIFY(query.isValid());
3097
3098 QStringList result;
3099 QVERIFY(query.evaluateTo(&result));
3100
3101 QCOMPARE(result, QStringList(QLatin1String("abcdef")));
3102 }
3103}
3104void tst_QXmlQuery::networkAccessManagerSignature() const
3105{
3106 /* Const object. */
3107 const QXmlQuery query;
3108
3109 /* The function should be const. */
3110 query.networkAccessManager();
3111}
3112
3113void tst_QXmlQuery::networkAccessManagerDefaultValue() const
3114{
3115 const QXmlQuery query;
3116
3117 QCOMPARE(query.networkAccessManager(), static_cast<QNetworkAccessManager *>(0));
3118}
3119
3120void tst_QXmlQuery::networkAccessManager() const
3121{
3122 /* Test that we return the network manager that was set. */
3123 {
3124 QNetworkAccessManager manager;
3125 QXmlQuery query;
3126 query.setNetworkAccessManager(&manager);
3127 QCOMPARE(query.networkAccessManager(), &manager);
3128 }
3129}
3130
3131/*!
3132 \internal
3133 \since 4.5
3134
3135 1. Load a document into QXmlQuery's document cache, by executing a query which does it.
3136 2. Set a focus
3137 3. Change query, to one which uses the focus
3138 4. Evaluate
3139
3140 Used to crash.
3141 */
3142void tst_QXmlQuery::multipleDocsAndFocus() const
3143{
3144 QXmlQuery query;
3145
3146 /* We use string concatenation, since variable bindings might disturb what
3147 * we're testing. */
3148 query.setQuery(sourceCode: QLatin1String("string(doc('") +
3149 inputFile(file: m_xmlPatternsDir + QLatin1String("/queries/simpleDocument.xml")) +
3150 QLatin1String("'))"));
3151 query.setFocus(QUrl(inputFileAsURI(file: m_xmlPatternsDir + QLatin1String("/stylesheets/documentElement.xml"))));
3152 query.setQuery(sourceCode: QLatin1String("string(.)"));
3153
3154 QStringList result;
3155 QVERIFY(query.evaluateTo(&result));
3156}
3157
3158/*!
3159 \internal
3160 \since 4.5
3161
3162 1. Set a focus
3163 2. Set a query
3164 3. Evaluate
3165 4. Change focus
3166 5. Evaluate
3167
3168 Used to crash.
3169 */
3170void tst_QXmlQuery::multipleEvaluationsWithDifferentFocus() const
3171{
3172 QXmlQuery query;
3173 QStringList result;
3174
3175 query.setFocus(QUrl(inputFileAsURI(file: m_xmlPatternsDir + QLatin1String("/stylesheets/documentElement.xml"))));
3176 query.setQuery(sourceCode: QLatin1String("string(.)"));
3177 QVERIFY(query.evaluateTo(&result));
3178
3179 query.setFocus(QUrl(inputFileAsURI(file: m_xmlPatternsDir + QLatin1String("/stylesheets/documentElement.xml"))));
3180 QVERIFY(query.evaluateTo(&result));
3181}
3182
3183void tst_QXmlQuery::bindVariableQXmlQuery() const
3184{
3185 QFETCH(QString, query1);
3186 QFETCH(QString, query2);
3187 QFETCH(QString, expectedOutput);
3188 QFETCH(bool, expectedSuccess);
3189
3190 MessageSilencer silencer;
3191 QXmlQuery xmlQuery1;
3192 xmlQuery1.setMessageHandler(&silencer);
3193 xmlQuery1.setQuery(sourceCode: query1);
3194
3195 QXmlQuery xmlQuery2(xmlQuery1);
3196 xmlQuery2.bindVariable(localName: "query1", query: xmlQuery1);
3197 xmlQuery2.setQuery(sourceCode: query2);
3198
3199 QString output;
3200 const bool querySuccess = xmlQuery2.evaluateTo(output: &output);
3201
3202 QCOMPARE(querySuccess, expectedSuccess);
3203
3204 if(querySuccess)
3205 QCOMPARE(output, expectedOutput);
3206}
3207
3208void tst_QXmlQuery::bindVariableQXmlQuery_data() const
3209{
3210 QTest::addColumn<QString>(name: "query1");
3211 QTest::addColumn<QString>(name: "query2");
3212 QTest::addColumn<QString>(name: "expectedOutput");
3213 QTest::addColumn<bool>(name: "expectedSuccess");
3214
3215 QTest::newRow(dataTag: "First query has one atomic value.")
3216 << "2"
3217 << "1, $query1, 3"
3218 << "1 2 3\n"
3219 << true;
3220
3221 QTest::newRow(dataTag: "First query has two atomic values.")
3222 << "2, 3"
3223 << "1, $query1, 4"
3224 << "1 2 3 4\n"
3225 << true;
3226
3227 QTest::newRow(dataTag: "First query is a node.")
3228 << "<e/>"
3229 << "1, $query1, 3"
3230 << "1<e/>3\n"
3231 << true;
3232
3233 /* This is a good test, because it triggers the exception in the
3234 * bindVariable() call, as supposed to when the actual evaluation is done.
3235 */
3236 QTest::newRow(dataTag: "First query has a dynamic error.")
3237 << "error()"
3238 << "1, $query1"
3239 << QString() /* We don't care. */
3240 << false;
3241}
3242
3243void tst_QXmlQuery::bindVariableQStringQXmlQuerySignature() const
3244{
3245 QXmlQuery query1;
3246 query1.setQuery(sourceCode: "'dummy'");
3247
3248 QXmlQuery query2;
3249 const QString name(QLatin1String("name"));
3250
3251 /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
3252 * QXmlQuery, and evaluation is what we do here. */
3253 query2.bindVariable(localName: name, query: const_cast<const QXmlQuery &>(query1));
3254}
3255
3256void tst_QXmlQuery::bindVariableQXmlNameQXmlQuerySignature() const
3257{
3258 QXmlNamePool np;
3259 QXmlQuery query1(np);
3260 query1.setQuery(sourceCode: "'dummy'");
3261
3262 QXmlQuery query2;
3263 const QXmlName name(np, QLatin1String("name"));
3264
3265 /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
3266 * QXmlQuery, and evaluation is what we do here. */
3267 query2.bindVariable(name, query: const_cast<const QXmlQuery &>(query1));
3268}
3269
3270/*!
3271 Check that the QXmlName is handled correctly.
3272 */
3273void tst_QXmlQuery::bindVariableQXmlNameQXmlQuery() const
3274{
3275 QXmlNamePool np;
3276 QXmlQuery query1;
3277 query1.setQuery(sourceCode: QLatin1String("1"));
3278
3279 QXmlQuery query2(np);
3280 query2.bindVariable(name: QXmlName(np, QLatin1String("theName")), query: query1);
3281 query2.setQuery(sourceCode: "$theName");
3282
3283 QString result;
3284 query2.evaluateTo(output: &result);
3285
3286 QCOMPARE(result, QString::fromLatin1("1\n"));
3287}
3288
3289void tst_QXmlQuery::bindVariableQXmlQueryInvalidate() const
3290{
3291 QXmlQuery query;
3292 query.bindVariable(localName: QLatin1String("name"), value: QVariant(1));
3293 query.setQuery(sourceCode: "$name");
3294 QVERIFY(query.isValid());
3295
3296 QXmlQuery query2;
3297 query2.setQuery(sourceCode: "'query2'");
3298
3299 query.bindVariable(localName: QLatin1String("name"), query: query2);
3300 QVERIFY(!query.isValid());
3301}
3302
3303void tst_QXmlQuery::unknownSourceLocation() const
3304{
3305 QBuffer b;
3306 b.setData("<a><b/><b/></a>");
3307 b.open(openMode: QIODevice::ReadOnly);
3308
3309 MessageSilencer silencer;
3310 QXmlQuery query;
3311 query.bindVariable(localName: QLatin1String("inputDocument"), &b);
3312 query.setMessageHandler(&silencer);
3313
3314 query.setQuery(sourceCode: QLatin1String("doc($inputDocument)/a/(let $v := b/string() return if ($v) then $v else ())"));
3315
3316 QString output;
3317 query.evaluateTo(output: &output);
3318}
3319
3320void tst_QXmlQuery::identityConstraintSuccess() const
3321{
3322 QXmlQuery::QueryLanguage queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintSelector;
3323
3324 /* We run this code for Selector and Field. */
3325 for(int i = 0; i < 3; ++i)
3326 {
3327 QXmlNamePool namePool;
3328 QXmlResultItems result;
3329 QXmlItem node;
3330
3331 {
3332 QXmlQuery nodeSource(namePool);
3333 nodeSource.setQuery(sourceCode: QLatin1String("<e/>"));
3334
3335 nodeSource.evaluateTo(result: &result);
3336 node = result.next();
3337 }
3338
3339 /* Basic use:
3340 * 1. The focus is undefined, but it's still valid.
3341 * 2. We never evaluate. */
3342 {
3343 QXmlQuery query(queryLanguage);
3344 query.setQuery(sourceCode: QLatin1String("a"));
3345 QVERIFY(query.isValid());
3346 }
3347
3348 /* Basic use:
3349 * 1. The focus is undefined, but it's still valid.
3350 * 2. We afterwards set the focus. */
3351 {
3352 QXmlQuery query(queryLanguage, namePool);
3353 query.setQuery(sourceCode: QLatin1String("a"));
3354 query.setFocus(node);
3355 QVERIFY(query.isValid());
3356 }
3357
3358 /* Basic use:
3359 * 1. The focus is undefined, but it's still valid.
3360 * 2. We afterwards set the focus.
3361 * 3. We evaluate. */
3362 {
3363 QXmlQuery query(queryLanguage, namePool);
3364 query.setQuery(sourceCode: QString(QLatin1Char('.')));
3365 query.setFocus(node);
3366 QVERIFY(query.isValid());
3367
3368 QString result;
3369 QVERIFY(query.evaluateTo(&result));
3370 QCOMPARE(result, QString::fromLatin1("<e/>\n"));
3371 }
3372
3373 /* A slightly more complex Field. */
3374 {
3375 QXmlQuery query(queryLanguage);
3376 query.setQuery(sourceCode: QLatin1String("* | .//xml:*/."));
3377 QVERIFY(query.isValid());
3378 }
3379
3380 /* @ is only allowed in Field. */
3381 if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField)
3382 {
3383 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField);
3384 query.setQuery(sourceCode: QLatin1String("@abc"));
3385 QVERIFY(query.isValid());
3386 }
3387
3388 /* Field allows attribute:: and child:: .*/
3389 if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField)
3390 {
3391 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField);
3392 query.setQuery(sourceCode: QLatin1String("attribute::name | child::name"));
3393 QVERIFY(query.isValid());
3394 }
3395
3396 /* Selector allows only child:: .*/
3397 {
3398 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintSelector);
3399 query.setQuery(sourceCode: QLatin1String("child::name"));
3400 QVERIFY(query.isValid());
3401 }
3402
3403 if(i == 0)
3404 queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintField;
3405 else if(i == 1)
3406 queryLanguage = QXmlQuery::XPath20;
3407 }
3408}
3409
3410Q_DECLARE_METATYPE(QXmlQuery::QueryLanguage);
3411
3412/*!
3413 We just do some basic tests for boot strapping and sanity checking. The actual regression
3414 testing is in the Schema suite.
3415 */
3416void tst_QXmlQuery::identityConstraintFailure() const
3417{
3418 QFETCH(QXmlQuery::QueryLanguage, queryLanguage);
3419 QFETCH(QString, inputQuery);
3420
3421 QXmlQuery query(queryLanguage);
3422 MessageSilencer silencer;
3423 query.setMessageHandler(&silencer);
3424
3425 query.setQuery(sourceCode: inputQuery);
3426 QVERIFY(!query.isValid());
3427}
3428
3429void tst_QXmlQuery::identityConstraintFailure_data() const
3430{
3431 QTest::addColumn<QXmlQuery::QueryLanguage>(name: "queryLanguage");
3432 QTest::addColumn<QString>(name: "inputQuery");
3433
3434 QTest::newRow(dataTag: "We don't have element constructors in identity constraint pattern, "
3435 "it's an XQuery feature(Selector).")
3436 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3437 << QString::fromLatin1(str: "<e/>");
3438
3439 QTest::newRow(dataTag: "We don't have functions in identity constraint pattern, "
3440 "it's an XPath feature(Selector).")
3441 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3442 << QString::fromLatin1(str: "current-time()");
3443
3444 QTest::newRow(dataTag: "We don't have element constructors in identity constraint pattern, "
3445 "it's an XQuery feature(Field).")
3446 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3447 << QString::fromLatin1(str: "<e/>");
3448
3449 QTest::newRow(dataTag: "We don't have functions in identity constraint pattern, "
3450 "it's an XPath feature(Field).")
3451 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3452 << QString::fromLatin1(str: "current-time()");
3453
3454 QTest::newRow(dataTag: "@attributeName is disallowed for the selector.")
3455 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3456 << QString::fromLatin1(str: "@abc");
3457
3458 QTest::newRow(dataTag: "attribute:: is disallowed for the selector.")
3459 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3460 << QString::fromLatin1(str: "attribute::name");
3461
3462 QTest::newRow(dataTag: "ancestor::name is disallowed for the selector.")
3463 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3464 << QString::fromLatin1(str: "ancestor::name");
3465
3466 QTest::newRow(dataTag: "ancestor::name is disallowed for the field.")
3467 << QXmlQuery::XmlSchema11IdentityConstraintField
3468 << QString::fromLatin1(str: "ancestor::name");
3469}
3470
3471QTEST_MAIN(tst_QXmlQuery)
3472
3473#include "tst_qxmlquery.moc"
3474

source code of qtxmlpatterns/tests/auto/qxmlquery/tst_qxmlquery.cpp