1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
43 #include <QtTest/QtTest>
45 #include <QAbstractMessageHandler>
47 #include <QNetworkReply>
48 #include <QtNetwork/QTcpServer>
49 #include <QtNetwork/QTcpSocket>
50 #include <QXmlFormatter>
54 #include <QXmlResultItems>
55 #include <QXmlSerializer>
57 #include "MessageSilencer.h"
58 #include "MessageValidator.h"
59 #include "NetworkOverrider.h"
60 #include "PushBaseliner.h"
61 #include "../qabstracturiresolver/TestURIResolver.h"
62 #include "../qsimplexmlnodemodel/TestSimpleNodeModel.h"
63 #include "TestFundament.h"
64 #include "../network-settings.h"
70 \brief Tests class QXmlQuery.
72 This test is not intended for testing the engine, but the functionality specific
73 to the QXmlQuery class.
75 In other words, if you have an engine bug; don't add it here because it won't be
76 tested properly. Instead add it to the test suite.
79 class tst_QXmlQuery : public QObject
80 , private TestFundament
85 inline tst_QXmlQuery() : m_generatedBaselines(0)
93 void defaultConstructor() const;
94 void copyConstructor() const;
95 void constructorQXmlNamePool() const;
96 void constructorQXmlNamePoolQueryLanguage() const;
97 void constructorQXmlNamePoolWithinQSimpleXmlNodeModel() const;
98 void assignmentOperator() const;
100 void sequentialExecution() const;
101 void bindVariableQString() const;
102 void bindVariableQStringNoExternalDeclaration() const;
103 void bindVariableQXmlName() const;
104 void bindVariableQXmlNameTriggerWarnings() const;
105 void bindVariableQStringQIODevice() const;
106 void bindVariableQStringQIODeviceWithByteArray() const;
107 void bindVariableQStringQIODeviceWithString() const;
108 void bindVariableQStringQIODeviceWithQFile() const;
109 void bindVariableQXmlNameQIODevice() const;
110 void bindVariableQXmlNameQIODeviceTriggerWarnings() const;
111 void bindVariableXSLTSuccess() const;
112 void bindVariableTemporaryNode() const;
113 void setMessageHandler() const;
114 void messageHandler() const;
115 void evaluateToQAbstractXmlReceiverTriggerWarnings() const;
116 void evaluateToQXmlResultItems() const;
117 void evaluateToQXmlResultItemsTriggerWarnings() const;
118 void evaluateToQXmlResultItemsErrorAtEnd() const;
119 void evaluateToReceiver();
120 void evaluateToReceiver_data() const;
121 void evaluateToReceiverOnInvalidQuery() const;
122 void evaluateToQStringTriggerError() const;
123 void evaluateToQString() const;
124 void evaluateToQString_data() const;
125 void evaluateToQStringSignature() const;
126 void checkGeneratedBaselines() const;
127 void basicXQueryToQtTypeCheck() const;
128 void basicQtToXQueryTypeCheck() const;
129 void bindNode() const;
130 void relativeBaseURI() const;
131 void emptyBaseURI() const;
132 void roundTripDateWithinQXmlItem() const;
133 void bindingMissing() const;
134 void bindDefaultConstructedItem() const;
135 void bindDefaultConstructedItem_data() const;
136 void bindEmptyNullString() const;
137 void bindEmptyString() const;
138 void rebindVariableSameType() const;
139 void rebindVariableDifferentType() const;
140 void rebindVariableWithNullItem() const;
141 void eraseQXmlItemBinding() const;
142 void eraseDeviceBinding() const;
143 void constCorrectness() const;
144 void objectSize() const;
145 void setUriResolver() const;
146 void uriResolver() const;
147 void messageXML() const;
148 void resultItemsDeallocatedQuery() const;
149 void copyCheckMessageHandler() const;
150 void shadowedVariables() const;
151 void setFocusQXmlItem() const;
152 void setFocusQUrl() const;
153 void setFocusQIODevice() const;
154 void setFocusQIODeviceAvoidVariableClash() const;
155 void setFocusQIODeviceFailure() const;
156 void setFocusQIODeviceTriggerWarnings() const;
157 void setFocusQString() const;
158 void setFocusQStringFailure() const;
159 void setFocusQStringSignature() const;
160 void recompilationWithEvaluateToResultFailing() const;
161 void secondEvaluationWithEvaluateToResultFailing() const;
162 void recompilationWithEvaluateToReceiver() const;
163 void fnDocOnQIODeviceTimeout() const;
164 void evaluateToQStringListOnInvalidQuery() const;
165 void evaluateToQStringList() const;
166 void evaluateToQStringListTriggerWarnings() const;
167 void evaluateToQStringList_data() const;
168 void evaluateToQStringListNoConversion() const;
169 void evaluateToQIODevice() const;
170 void evaluateToQIODeviceTriggerWarnings() const;
171 void evaluateToQIODeviceSignature() const;
172 void evaluateToQIODeviceOnInvalidQuery() const;
173 void setQueryQIODeviceQUrl() const;
174 void setQueryQIODeviceQUrlTriggerWarnings() const;
175 void setQueryQString() const;
176 void setQueryQUrlSuccess() const;
177 void setQueryQUrlSuccess_data() const;
178 void setQueryQUrlFailSucceed() const;
179 void setQueryQUrlFailure() const;
180 void setQueryQUrlFailure_data() const;
181 void setQueryQUrlBaseURI() const;
182 void setQueryQUrlBaseURI_data() const;
183 void setQueryWithNonExistentQUrlOnValidQuery() const;
184 void setQueryWithInvalidQueryFromQUrlOnValidQuery() const;
185 void retrieveNameFromQuery() const;
186 void retrieveNameFromQuery_data() const;
187 void cleanupTestCase() const;
188 void declareUnavailableExternal() const;
189 void msvcCacheIssue() const;
190 void unavailableExternalVariable() const;
191 void useUriResolver() const;
192 void queryWithFocusAndVariable() const;
193 void undefinedFocus() const;
194 void basicFocusUsage() const;
196 void queryLanguage() const;
197 void queryLanguageSignature() const;
198 void enumQueryLanguage() const;
200 void setNetworkAccessManager() const;
201 void networkAccessManagerSignature() const;
202 void networkAccessManagerDefaultValue() const;
203 void networkAccessManager() const;
205 void setInitialTemplateNameQXmlName() const;
206 void setInitialTemplateNameQXmlNameSignature() const;
207 void setInitialTemplateNameQString() const;
208 void setInitialTemplateNameQStringSignature() const;
209 void initialTemplateName() const;
210 void initialTemplateNameSignature() const;
212 void fnDocNetworkAccessSuccess() const;
213 void fnDocNetworkAccessSuccess_data() const;
214 void fnDocNetworkAccessFailure() const;
215 void fnDocNetworkAccessFailure_data() const;
216 void multipleDocsAndFocus() const;
217 void multipleEvaluationsWithDifferentFocus() const;
218 void bindVariableQXmlQuery() const;
219 void bindVariableQXmlQuery_data() const;
220 void bindVariableQStringQXmlQuerySignature() const;
221 void bindVariableQXmlNameQXmlQuerySignature() const;
222 void bindVariableQXmlNameQXmlQuery() const;
223 void bindVariableQXmlQueryInvalidate() const;
224 void unknownSourceLocation() const;
226 void identityConstraintSuccess() const;
227 void identityConstraintFailure() const;
228 void identityConstraintFailure_data() const;
230 // 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.
231 // TODO bind stringlists, variant lists, both ways.
232 // TODO trigger serialization error, or any error in evaluateToushCallback().
233 // TODO let items travle between two queries, as seen in the SDK
234 // TODO what happens if the query declares local variable and external ones are provided?
240 * One excluded, since we skip static-base-uri.xq.
242 ExpectedQueryCount = 29
245 static void checkBaseURI(const QUrl &baseURI, const QString &candidate);
246 static QStringList queries();
247 static const QString m_xmlPatternsDir;
249 int m_generatedBaselines;
250 int m_pushTestsCount;
251 const bool m_testNetwork;
254 const QString tst_QXmlQuery::m_xmlPatternsDir = QFINDTESTDATA("../xmlpatterns");
256 void tst_QXmlQuery::initTestCase()
258 QVERIFY2(!m_xmlPatternsDir.isEmpty(), qPrintable(QString::fromLatin1("Cannot locate '../xmlpatterns' starting from %1").arg(QDir::currentPath())));
259 QVERIFY(QtNetworkSettings::verifyTestNetworkSettings());
262 void tst_QXmlQuery::checkBaseURI(const QUrl &baseURI, const QString &candidate)
264 /* The use of QFileInfo::canonicalFilePath() takes into account that drive letters
265 * on Windows may have different cases. */
266 QVERIFY(QDir(baseURI.toLocalFile()).relativeFilePath(QFileInfo(candidate).canonicalFilePath()).startsWith("../"));
269 QStringList tst_QXmlQuery::queries()
272 dir.cd(inputFile(m_xmlPatternsDir + QLatin1String("/queries/")));
274 return dir.entryList(QStringList(QLatin1String("*.xq")));
277 void tst_QXmlQuery::defaultConstructor() const
279 /* Allocate instance in different orders. */
296 void tst_QXmlQuery::copyConstructor() const
298 /* Verify that we can take a const reference, and simply do a copy of a default constructed object. */
300 const QXmlQuery query1;
301 QXmlQuery query2(query1);
306 const QXmlQuery query1;
307 QXmlQuery query2(query1);
308 QXmlQuery query3(query2);
311 /* Verify that copying default values works. */
313 const QXmlQuery query1;
314 const QXmlQuery query2(query1);
315 QCOMPARE(query2.messageHandler(), query1.messageHandler());
316 QCOMPARE(query2.uriResolver(), query1.uriResolver());
317 QCOMPARE(query2.queryLanguage(), query1.queryLanguage());
318 QCOMPARE(query2.initialTemplateName(), query1.initialTemplateName());
319 QCOMPARE(query2.networkAccessManager(), query1.networkAccessManager());
328 * - initial template name
330 * sticks with the copy. */
332 MessageSilencer silencer;
333 TestURIResolver resolver;
334 QNetworkAccessManager networkManager;
335 QXmlQuery query1(QXmlQuery::XSLT20);
336 QXmlNamePool np1(query1.namePool());
338 query1.setMessageHandler(&silencer);
339 query1.setUriResolver(&resolver);
340 query1.setNetworkAccessManager(&networkManager);
342 const QXmlName name(np1, QLatin1String("localName"),
343 QLatin1String("http://example.com/"),
344 QLatin1String("prefix"));
345 query1.setInitialTemplateName(name);
347 const QXmlQuery query2(query1);
348 QCOMPARE(query2.messageHandler(), static_cast<QAbstractMessageHandler *>(&silencer));
349 QCOMPARE(query2.uriResolver(), static_cast<const QAbstractUriResolver *>(&resolver));
350 QCOMPARE(query2.queryLanguage(), QXmlQuery::XSLT20);
351 QCOMPARE(query2.initialTemplateName(), name);
352 QCOMPARE(query2.networkAccessManager(), &networkManager);
354 QXmlNamePool np2(query2.namePool());
356 QCOMPARE(name.namespaceUri(np2), QString::fromLatin1("http://example.com/"));
357 QCOMPARE(name.localName(np2), QString::fromLatin1("localName"));
358 QCOMPARE(name.prefix(np2), QString::fromLatin1("prefix"));
364 original.setFocus(QXmlItem(4));
365 original.setQuery(QLatin1String("."));
366 QVERIFY(original.isValid());
368 const QXmlQuery copy(original);
370 QXmlResultItems result;
371 copy.evaluateTo(&result);
372 QCOMPARE(result.next().toAtomicValue(), QVariant(4));
373 QVERIFY(result.next().isNull());
374 QVERIFY(!result.hasError());
377 /* Copy, set, compare. Check that copies are independent. */
379 // TODO all members except queryLanguage().
383 void tst_QXmlQuery::constructorQXmlNamePool() const
385 /* Check that the namepool we are passed, is actually used. */
389 const QXmlName name(np, QLatin1String("localName"),
390 QLatin1String("http://example.com/"),
391 QLatin1String("prefix"));
393 QXmlNamePool np2(query.namePool());
394 QCOMPARE(name.namespaceUri(np2), QString::fromLatin1("http://example.com/"));
395 QCOMPARE(name.localName(np2), QString::fromLatin1("localName"));
396 QCOMPARE(name.prefix(np2), QString::fromLatin1("prefix"));
400 Ensure that the internal variable loading mechanisms uses the user-supplied
403 If that is not the case, different name pools are used and the code crashes.
407 void tst_QXmlQuery::constructorQXmlNamePoolQueryLanguage() const
410 QXmlName name(np, QLatin1String("arbitraryName"));
412 QXmlQuery query(QXmlQuery::XQuery10, np);
415 input.setData("<yall/>");
417 QVERIFY(input.open(QIODevice::ReadOnly));
418 query.bindVariable(name, &input);
419 query.setQuery("string(doc($arbitraryName))");
422 query.evaluateTo(&result);
425 void tst_QXmlQuery::constructorQXmlNamePoolWithinQSimpleXmlNodeModel() const
427 class TestCTOR : public TestSimpleNodeModel
430 TestCTOR(const QXmlNamePool &np) : TestSimpleNodeModel(np)
434 void checkCTOR() const
436 /* If this fails to compile, the constructor has trouble with taking
437 * a mutable reference.
439 * The reason we use the this pointer explicitly, is to avoid a compiler
440 * warnings with MSVC 2005. */
441 QXmlQuery(this->namePool());
450 void tst_QXmlQuery::assignmentOperator() const
452 class ReturnURI : public QAbstractUriResolver
456 virtual QUrl resolve(const QUrl &relative,
457 const QUrl &baseURI) const
459 return baseURI.resolved(relative);
463 /* Assign this to this. */
470 /* Just call a couple of functions to give valgrind
471 * something to check. */
472 QVERIFY(!query.isValid());
473 query.messageHandler();
476 /* Assign null instances a couple of times. */
484 /* Just call a couple of functions to give valgrind
485 * something to check. */
486 QVERIFY(!query1.isValid());
487 query1.messageHandler();
489 /* Just call a couple of functions to give valgrind
490 * something to check. */
491 QVERIFY(!query2.isValid());
492 query2.messageHandler();
495 /* Create a query, set all the things it stores, and ensure it
496 * travels over to the new instance. */
498 MessageSilencer silencer;
499 const ReturnURI returnURI;
500 QXmlNamePool namePool;
502 QBuffer documentDevice;
503 documentDevice.setData(QByteArray("<e>a</e>"));
504 QVERIFY(documentDevice.open(QIODevice::ReadOnly));
506 QXmlQuery original(namePool);
507 QXmlName testName(namePool, QLatin1String("somethingToCheck"));
509 original.setMessageHandler(&silencer);
510 original.bindVariable(QLatin1String("var"), QXmlItem(1));
511 original.bindVariable(QLatin1String("device"), &documentDevice);
512 original.setUriResolver(&returnURI);
513 original.setFocus(QXmlItem(3));
514 original.setQuery(QLatin1String("$var, 1 + 1, ., string(doc($device))"));
516 /* Do a copy, and check that everything followed on into the copy. No modification
521 /* We use assignment operator, not copy constructor. */
524 QVERIFY(copy.isValid());
525 QCOMPARE(copy.uriResolver(), static_cast<const QAbstractUriResolver *>(&returnURI));
526 QCOMPARE(copy.messageHandler(), static_cast<QAbstractMessageHandler *>(&silencer));
527 QCOMPARE(testName.localName(copy.namePool()), QString::fromLatin1("somethingToCheck"));
529 QXmlResultItems result;
530 copy.evaluateTo(&result);
531 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
532 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
533 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
534 QCOMPARE(result.next().toAtomicValue(), QVariant(QString::fromLatin1("a")));
535 QVERIFY(result.next().isNull());
536 QVERIFY(!result.hasError());
539 /* Copy, and change values. Things should detach. */
541 /* Evaluate the copy. */
543 MessageSilencer secondSilencer;
544 const ReturnURI secondUriResolver;
545 QBuffer documentDeviceCopy;
546 documentDeviceCopy.setData(QByteArray("<e>b</e>"));
547 QVERIFY(documentDeviceCopy.open(QIODevice::ReadOnly));
552 copy.setMessageHandler(&secondSilencer);
553 /* Here we rebind variable values. */
554 copy.bindVariable(QLatin1String("var"), QXmlItem(4));
555 copy.bindVariable(QLatin1String("device"), &documentDeviceCopy);
556 copy.setUriResolver(&secondUriResolver);
557 copy.setFocus(QXmlItem(6));
558 copy.setQuery(QLatin1String("$var, 1 + 1, ., string(doc($device))"));
560 /* Check that the copy picked up the new things. */
561 QVERIFY(copy.isValid());
562 QCOMPARE(copy.uriResolver(), static_cast<const QAbstractUriResolver *>(&secondUriResolver));
563 QCOMPARE(copy.messageHandler(), static_cast<QAbstractMessageHandler *>(&secondSilencer));
565 QXmlResultItems resultCopy;
566 copy.evaluateTo(&resultCopy);
567 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(4));
568 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(2));
569 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(6));
570 const QString stringedDevice(resultCopy.next().toAtomicValue().toString());
571 QCOMPARE(stringedDevice, QString::fromLatin1("b"));
572 QVERIFY(resultCopy.next().isNull());
573 QVERIFY(!resultCopy.hasError());
576 /* Evaluate the original. */
578 /* Check that the original is unchanged. */
579 QVERIFY(original.isValid());
580 QCOMPARE(original.uriResolver(), static_cast<const QAbstractUriResolver *>(&returnURI));
581 QCOMPARE(original.messageHandler(), static_cast<QAbstractMessageHandler *>(&silencer));
583 QXmlResultItems resultOriginal;
584 original.evaluateTo(&resultOriginal);
585 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(1));
586 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(2));
587 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(3));
588 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(QString::fromLatin1("a")));
589 QVERIFY(resultOriginal.next().isNull());
590 QVERIFY(!resultOriginal.hasError());
597 Since QXmlQuery doesn't seek devices to position 0, this code triggers a bug
598 where document caching doesn't work. Since the document caching doesn't work,
599 the device will be read twice, and the second time the device is at the end,
600 hence premature end of document.
602 void tst_QXmlQuery::sequentialExecution() const
605 inBuffer.setData(QByteArray("<input/>"));
606 QVERIFY(inBuffer.open(QIODevice::ReadOnly));
609 query.bindVariable("inputDocument", &inBuffer);
612 QBuffer outBuffer(&outArray);
613 outBuffer.open(QIODevice::WriteOnly);
615 const QString queryString(QLatin1String("doc($inputDocument)"));
616 query.setQuery(queryString);
618 QXmlFormatter formatter(query, &outBuffer);
620 QVERIFY(query.evaluateTo(&formatter));
622 /* If this line is removed, the bug isn't triggered. */
623 query.setQuery(queryString);
625 QVERIFY(query.evaluateTo(&formatter));
628 void tst_QXmlQuery::isValid() const
630 /* Check default value. */
632 QVERIFY(!query.isValid());
635 void tst_QXmlQuery::bindVariableQString() const
639 /* Bind with a null QXmlItem. */
640 query.bindVariable(QLatin1String("name"), QXmlItem());
645 /* Bind with a null QVariant. */
646 query.bindVariable(QLatin1String("name"), QXmlItem(QVariant()));
651 /* Bind with a null QXmlNodeModelIndex. */
652 query.bindVariable(QLatin1String("name"), QXmlItem(QXmlNodeModelIndex()));
656 void tst_QXmlQuery::bindVariableQStringNoExternalDeclaration() const
659 query.bindVariable(QLatin1String("foo"), QXmlItem(QLatin1String("Variable Value")));
660 query.setQuery(QLatin1String("$foo"));
662 QVERIFY(query.isValid());
665 QVERIFY(query.evaluateTo(&result));
667 QCOMPARE(result, QStringList() << QLatin1String("Variable Value"));
670 void tst_QXmlQuery::bindVariableQXmlName() const
675 void tst_QXmlQuery::bindVariableQXmlNameTriggerWarnings() const
679 QTest::ignoreMessage(QtWarningMsg, "The variable name cannot be null.");
680 query.bindVariable(QXmlName(), QVariant());
683 void tst_QXmlQuery::bindVariableQStringQIODeviceWithByteArray() const
687 QByteArray in("<e/>");
689 QVERIFY(device.open(QIODevice::ReadOnly));
691 query.bindVariable("doc", &device);
693 query.setQuery(QLatin1String("declare variable $doc external; $doc"));
695 QVERIFY(query.isValid());
697 /* Check the URI corresponding to the variable. */
699 QXmlResultItems items;
700 query.evaluateTo(&items);
702 QCOMPARE(items.next().toAtomicValue().toString(), QString::fromLatin1("tag:trolltech.com,2007:QtXmlPatterns:QIODeviceVariable:doc"));
705 /* Now, actually load the document. We use the same QXmlQuery just to stress recompilation a bit. */
707 query.setQuery(QLatin1String("declare variable $doc external; doc($doc)"));
710 QBuffer outBuffer(&out);
711 QVERIFY(outBuffer.open(QIODevice::WriteOnly));
713 QXmlSerializer serializer(query, &outBuffer);
715 QVERIFY(query.evaluateTo(&serializer));
720 void tst_QXmlQuery::bindVariableQStringQIODeviceWithString() const
724 QString in("<qstring/>");
725 QByteArray inUtf8(in.toUtf8());
726 QBuffer inDevice(&inUtf8);
728 QVERIFY(inDevice.open(QIODevice::ReadOnly));
730 query.bindVariable("doc", &inDevice);
732 query.setQuery(QLatin1String("declare variable $doc external; doc($doc)"));
735 QBuffer outBuffer(&out);
736 QVERIFY(outBuffer.open(QIODevice::WriteOnly));
738 QXmlSerializer serializer(query, &outBuffer);
739 QVERIFY(query.evaluateTo(&serializer));
741 QCOMPARE(out, inUtf8);
744 void tst_QXmlQuery::bindVariableQStringQIODeviceWithQFile() const
747 QFile inDevice(QLatin1String(SRCDIR "input.xml"));
749 QVERIFY(inDevice.open(QIODevice::ReadOnly));
751 query.bindVariable("doc", &inDevice);
753 query.setQuery(QLatin1String("declare variable $doc external; doc($doc)"));
756 QBuffer outBuffer(&out);
757 QVERIFY(outBuffer.open(QIODevice::WriteOnly));
759 QXmlSerializer serializer(query, &outBuffer);
760 QVERIFY(query.evaluateTo(&serializer));
763 QCOMPARE(out, QByteArray("<!-- This is just a file for testing. --><input/>"));
766 void tst_QXmlQuery::bindVariableQStringQIODevice() const
770 /* Rebind the variable. */
772 /* First evaluation. */
774 QByteArray in1("<e1/>");
775 QBuffer inDevice1(&in1);
776 QVERIFY(inDevice1.open(QIODevice::ReadOnly));
778 query.bindVariable("in", &inDevice1);
779 query.setQuery(QLatin1String("doc($in)"));
782 QBuffer outDevice1(&out1);
783 QVERIFY(outDevice1.open(QIODevice::WriteOnly));
785 QXmlSerializer serializer(query, &outDevice1);
786 query.evaluateTo(&serializer);
790 /* Second evaluation, rebind variable. */
792 QByteArray in2("<e2/>");
793 QBuffer inDevice2(&in2);
794 QVERIFY(inDevice2.open(QIODevice::ReadOnly));
796 query.bindVariable(QLatin1String("in"), &inDevice2);
799 QBuffer outDevice2(&out2);
800 QVERIFY(outDevice2.open(QIODevice::WriteOnly));
802 QXmlSerializer serializer(query, &outDevice2);
803 QVERIFY(query.evaluateTo(&serializer));
808 // TODO trigger recompilation when setting qiodevices., and qiodevice overwritten by other type, etc.
811 void tst_QXmlQuery::bindVariableQXmlNameQIODevice() const
816 void tst_QXmlQuery::bindVariableQXmlNameQIODeviceTriggerWarnings() const
822 QTest::ignoreMessage(QtWarningMsg, "A null, or readable QIODevice must be passed.");
823 query.bindVariable(QXmlName(np, QLatin1String("foo")), &buffer);
825 QTest::ignoreMessage(QtWarningMsg, "The variable name cannot be null.");
826 query.bindVariable(QXmlName(), 0);
829 void tst_QXmlQuery::bindVariableXSLTSuccess() const
831 QXmlQuery stylesheet(QXmlQuery::XSLT20);
832 stylesheet.setInitialTemplateName(QLatin1String("main"));
834 stylesheet.bindVariable(QLatin1String("variableNoSelectNoBodyBoundWithBindVariable"),
835 QVariant(QLatin1String("MUST NOT SHOW 1")));
837 stylesheet.bindVariable(QLatin1String("variableSelectBoundWithBindVariable"),
838 QVariant(QLatin1String("MUST NOT SHOW 2")));
840 stylesheet.bindVariable(QLatin1String("variableSelectWithTypeIntBoundWithBindVariable"),
841 QVariant(QLatin1String("MUST NOT SHOW 3")));
843 stylesheet.bindVariable(QLatin1String("paramNoSelectNoBodyBoundWithBindVariable"),
844 QVariant(QLatin1String("param1")));
846 stylesheet.bindVariable(QLatin1String("paramNoSelectNoBodyBoundWithBindVariableRequired"),
847 QVariant(QLatin1String("param1")));
849 stylesheet.bindVariable(QLatin1String("paramSelectBoundWithBindVariable"),
850 QVariant(QLatin1String("param2")));
852 stylesheet.bindVariable(QLatin1String("paramSelectBoundWithBindVariableRequired"),
853 QVariant(QLatin1String("param3")));
855 stylesheet.bindVariable(QLatin1String("paramSelectWithTypeIntBoundWithBindVariable"),
858 stylesheet.bindVariable(QLatin1String("paramSelectWithTypeIntBoundWithBindVariableRequired"),
859 QVariant(QLatin1String("param5")));
861 stylesheet.setQuery(QUrl(inputFileAsURI(m_xmlPatternsDir + QLatin1String("/stylesheets/parameters.xsl"))));
863 QVERIFY(stylesheet.isValid());
866 QVERIFY(deviceOut.open(QIODevice::ReadWrite));
868 QVERIFY(stylesheet.evaluateTo(&deviceOut));
870 const QString result(QString::fromUtf8(deviceOut.data().constData()));
873 QString::fromLatin1("Variables: variableSelectsDefaultValue variableSelectsDefaultValue2 3 4 "
874 "Parameters: param1 param1 param2 param3 4 param5"));
877 void tst_QXmlQuery::bindVariableTemporaryNode() const
879 /* First we do it with QXmlResultItems staying in scope. */;
882 query1.setQuery("<anElement/>");
884 QXmlResultItems result1;
885 query1.evaluateTo(&result1);
887 QXmlQuery query2(query1);
888 query2.bindVariable("fromQuery1", result1.next());
889 query2.setQuery("$fromQuery1");
892 QVERIFY(query2.evaluateTo(&output));
894 QCOMPARE(output, QString::fromLatin1("<anElement/>\n"));
897 /* And now with it deallocating, so its internal DynamicContext pointer is
898 * released. This doesn't work in Qt 4.5 and is ok. */
901 query1.setQuery("<anElement/>");
906 QXmlResultItems result1;
907 query1.evaluateTo(&result1);
909 query2.bindVariable("fromQuery1", result1.next());
910 query2.setQuery("$fromQuery1");
914 return; // See comment above.
915 QVERIFY(query2.evaluateTo(&output));
917 QCOMPARE(output, QString::fromLatin1("<anElement/>\n"));
921 void tst_QXmlQuery::messageHandler() const
924 /* Check default value. */
926 QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0));
930 void tst_QXmlQuery::setMessageHandler() const
933 MessageSilencer silencer;
934 query.setMessageHandler(&silencer);
935 QCOMPARE(static_cast<QAbstractMessageHandler *>(&silencer), query.messageHandler());
938 void tst_QXmlQuery::evaluateToReceiver()
940 QFETCH(QString, inputQuery);
942 /* This query prints a URI specific to the local system. */
943 if(inputQuery == QLatin1String("static-base-uri.xq"))
947 const QString queryURI(inputFile(m_xmlPatternsDir + QLatin1String("/queries/") + inputQuery));
948 QFile queryFile(queryURI);
950 QVERIFY(queryFile.exists());
951 QVERIFY(queryFile.open(QIODevice::ReadOnly));
955 MessageSilencer receiver;
956 query.setMessageHandler(&receiver);
957 query.setQuery(&queryFile, QUrl::fromLocalFile(queryURI));
959 /* We read all the queries, and some of them are invalid. However, we
960 * only want those that compile. */
965 QTextStream stream(&produced, QIODevice::WriteOnly);
966 PushBaseliner push(stream, query.namePool());
967 QVERIFY(push.isValid());
968 query.evaluateTo(&push);
970 const QString baselineName(inputFile(QLatin1String(SRCDIR "pushBaselines/") + inputQuery.left(inputQuery.length() - 2) + QString::fromLatin1("ref")));
971 QFile baseline(baselineName);
973 if(baseline.exists())
975 QVERIFY(baseline.open(QIODevice::ReadOnly | QIODevice::Text));
976 const QString stringedBaseline(QString::fromUtf8(baseline.readAll()));
977 QCOMPARE(produced, stringedBaseline);
981 QVERIFY(baseline.open(QIODevice::WriteOnly));
982 /* This is intentionally a warning, don't remove it. Update the baselines instead. */
983 qWarning() << "Generated baseline for:" << baselineName;
984 ++m_generatedBaselines;
986 baseline.write(produced.toUtf8());
990 void tst_QXmlQuery::evaluateToReceiver_data() const
992 QTest::addColumn<QString>("inputQuery");
994 foreach (QString const& query, queries())
996 /* This outputs a URI specific to the environment, so we can't use it for this
997 * particular test. */
998 if (query != QLatin1String("staticBaseURI.xq"))
999 QTest::newRow(query.toUtf8().constData()) << query;
1003 void tst_QXmlQuery::evaluateToReceiverOnInvalidQuery() const
1005 /* Invoke on a default constructed object. */
1008 QBuffer buffer(&out);
1009 buffer.open(QIODevice::WriteOnly);
1012 QXmlSerializer serializer(query, &buffer);
1013 QVERIFY(!query.evaluateTo(&serializer));
1016 /* Invoke on an invalid query; compile time error. */
1019 QBuffer buffer(&out);
1020 buffer.open(QIODevice::WriteOnly);
1021 MessageSilencer silencer;
1024 query.setMessageHandler(&silencer);
1025 query.setQuery(QLatin1String("1 + "));
1026 QXmlSerializer serializer(query, &buffer);
1027 QVERIFY(!query.evaluateTo(&serializer));
1030 /* Invoke on an invalid query; runtime error. */
1033 QBuffer buffer(&out);
1034 buffer.open(QIODevice::WriteOnly);
1035 MessageSilencer silencer;
1038 query.setMessageHandler(&silencer);
1039 query.setQuery(QLatin1String("error()"));
1040 QXmlSerializer serializer(query, &buffer);
1041 QVERIFY(!query.evaluateTo(&serializer));
1045 void tst_QXmlQuery::evaluateToQStringTriggerError() const
1047 /* Invoke on a default constructed object. */
1051 QVERIFY(!query.evaluateTo(&out));
1054 /* Invoke on an invalid query; compile time error. */
1057 MessageSilencer silencer;
1058 query.setMessageHandler(&silencer);
1060 query.setQuery(QLatin1String("1 + "));
1063 QVERIFY(!query.evaluateTo(&out));
1066 /* Invoke on an invalid query; runtime error. */
1069 MessageSilencer silencer;
1070 query.setMessageHandler(&silencer);
1072 query.setQuery(QLatin1String("error()"));
1075 QVERIFY(!query.evaluateTo(&out));
1079 void tst_QXmlQuery::evaluateToQString() const
1081 QFETCH(QString, query);
1082 QFETCH(QString, expectedOutput);
1084 QXmlQuery queryInstance;
1085 queryInstance.setQuery(query);
1086 QVERIFY(queryInstance.isValid());
1089 QVERIFY(queryInstance.evaluateTo(&result));
1091 QCOMPARE(result, expectedOutput);
1094 void tst_QXmlQuery::evaluateToQString_data() const
1096 QTest::addColumn<QString>("query");
1097 QTest::addColumn<QString>("expectedOutput");
1099 QTest::newRow("Two atomics")
1100 << QString::fromLatin1("1, 'two'")
1101 << QString::fromLatin1("1 two\n");
1103 QTest::newRow("An element")
1104 << QString::fromLatin1("<e>{1}</e>")
1105 << QString::fromLatin1("<e>1</e>\n");
1108 void tst_QXmlQuery::evaluateToQStringSignature() const
1110 const QXmlQuery query;
1114 /* evaluateTo(QString *) should be a const function. */
1115 query.evaluateTo(&output);
1118 void tst_QXmlQuery::evaluateToQAbstractXmlReceiverTriggerWarnings() const
1122 /* We check the return value as well as warning message here. */
1123 QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed.");
1124 QCOMPARE(query.evaluateTo(static_cast<QAbstractXmlReceiver *>(0)),
1128 void tst_QXmlQuery::evaluateToQXmlResultItems() const
1130 /* Invoke on a default constructed object. */
1133 QXmlResultItems result;
1134 query.evaluateTo(&result);
1135 QVERIFY(result.next().isNull());
1139 void tst_QXmlQuery::evaluateToQXmlResultItemsTriggerWarnings() const
1141 QTest::ignoreMessage(QtWarningMsg, "A null pointer cannot be passed.");
1143 query.evaluateTo(static_cast<QXmlResultItems *>(0));
1146 void tst_QXmlQuery::evaluateToQXmlResultItemsErrorAtEnd() const
1149 MessageSilencer silencer;
1150 query.setMessageHandler(&silencer);
1151 query.setQuery(QLatin1String("1 to 100, fn:error()"));
1152 QVERIFY(query.isValid());
1155 query.evaluateTo(&it);
1157 while(!it.next().isNull())
1163 If baselines were generated, we flag it as a failure such that it gets
1164 attention, and that they are adjusted accordingly.
1166 void tst_QXmlQuery::checkGeneratedBaselines() const
1168 QCOMPARE(m_generatedBaselines, 0);
1170 /* If this check fails, the auto test setup is misconfigured, or files have
1171 * been added/removed without this number being updated. */
1172 QCOMPARE(m_pushTestsCount, int(ExpectedQueryCount));
1175 void tst_QXmlQuery::basicXQueryToQtTypeCheck() const
1177 QFile queryFile(m_xmlPatternsDir + QLatin1String("/queries/") + QString::fromLatin1("allAtomics.xq"));
1178 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1181 query.setQuery(&queryFile);
1182 QVERIFY(query.isValid());
1185 query.evaluateTo(&it);
1187 QVariantList expectedValues;
1188 expectedValues.append(QString::fromLatin1("xs:untypedAtomic"));
1189 expectedValues.append(QDateTime(QDate(2002, 10, 10), QTime(23, 2, 11), Qt::UTC));
1190 expectedValues.append(QDate(2002, 10, 10));
1191 expectedValues.append(QVariant()); /* We currently doesn't support xs:time through the API. */
1193 expectedValues.append(QVariant()); /* xs:duration */
1194 expectedValues.append(QVariant()); /* xs:dayTimeDuration */
1195 expectedValues.append(QVariant()); /* xs:yearMonthDuration */
1197 if(sizeof(qreal) == sizeof(float)) {//ARM casts to Float not to double
1198 expectedValues.append(QVariant(float(3e3))); /* xs:float */
1199 expectedValues.append(QVariant(float(4e4))); /* xs:double */
1200 expectedValues.append(QVariant(float(2))); /* xs:decimal */
1202 expectedValues.append(QVariant(double(3e3))); /* xs:float */
1203 expectedValues.append(QVariant(double(4e4))); /* xs:double */
1204 expectedValues.append(QVariant(double(2))); /* xs:decimal */
1207 /* xs:integer and its sub-types. */
1208 expectedValues.append(QVariant(qlonglong(16)));
1209 expectedValues.append(QVariant(qlonglong(-6)));
1210 expectedValues.append(QVariant(qlonglong(-4)));
1211 expectedValues.append(QVariant(qlonglong(5)));
1212 expectedValues.append(QVariant(qlonglong(6)));
1213 expectedValues.append(QVariant(qlonglong(7)));
1214 expectedValues.append(QVariant(qlonglong(8)));
1215 expectedValues.append(QVariant(qlonglong(9)));
1216 expectedValues.append(QVariant(qulonglong(10)));
1217 expectedValues.append(QVariant(qlonglong(11)));
1218 expectedValues.append(QVariant(qlonglong(12)));
1219 expectedValues.append(QVariant(qlonglong(13)));
1220 expectedValues.append(QVariant(qlonglong(14)));
1222 expectedValues.append(QVariant()); /* xs:gYearMonth("1976-02"), */
1223 expectedValues.append(QVariant()); /* xs:gYear("2005-12:00"), */
1224 expectedValues.append(QVariant()); /* xs:gMonthDay("--12-25-14:00"), */
1225 expectedValues.append(QVariant()); /* xs:gDay("---25-14:00"), */
1226 expectedValues.append(QVariant()); /* xs:gMonth("--12-14:00"), */
1227 expectedValues.append(true); /* xs:boolean("true"), */
1228 expectedValues.append(QVariant(QByteArray::fromBase64(QByteArray("aaaa")))); /* xs:base64Binary("aaaa"), */
1229 expectedValues.append(QVariant(QByteArray::fromHex(QByteArray("FFFF")))); /* xs:hexBinary("FFFF"), */
1230 expectedValues.append(QVariant(QString::fromLatin1("http://example.com/"))); /* xs:anyURI("http://example.com/"), */
1231 QXmlNamePool np(query.namePool());
1232 expectedValues.append(QVariant(qVariantFromValue(QXmlName(np, QLatin1String("localName"),
1233 QLatin1String("http://example.com/2"),
1234 QLatin1String("prefix")))));
1236 expectedValues.append(QVariant(QString::fromLatin1("An xs:string")));
1237 expectedValues.append(QVariant(QString::fromLatin1("normalizedString")));
1238 expectedValues.append(QVariant(QString::fromLatin1("token")));
1239 expectedValues.append(QVariant(QString::fromLatin1("language")));
1240 expectedValues.append(QVariant(QString::fromLatin1("NMTOKEN")));
1241 expectedValues.append(QVariant(QString::fromLatin1("Name")));
1242 expectedValues.append(QVariant(QString::fromLatin1("NCName")));
1243 expectedValues.append(QVariant(QString::fromLatin1("ID")));
1244 expectedValues.append(QVariant(QString::fromLatin1("IDREF")));
1245 expectedValues.append(QVariant(QString::fromLatin1("ENTITY")));
1248 QXmlItem item(it.next());
1250 while(!item.isNull())
1252 QVERIFY(item.isAtomicValue());
1253 const QVariant produced(item.toAtomicValue());
1255 const QVariant &expected = expectedValues.at(i);
1257 /* For the cases where we can't represent a value in the XDM with Qt,
1258 * we return an invalid QVariant. */
1259 QCOMPARE(expected.isValid(), produced.isValid());
1261 QCOMPARE(produced.type(), expected.type());
1263 if(expected.isValid())
1265 /* This is only needed for xs:decimal though, for some reason. Probably
1266 * just artifacts created somewhere. */
1267 if(produced.type() == QVariant::Double)
1268 QVERIFY(qFuzzyCompare(produced.toDouble(), expected.toDouble()));
1269 else if(qVariantCanConvert<QXmlName>(produced))
1271 /* QVariant::operator==() does identity comparison, it doesn't delegate to operator==() of
1272 * the contained type, unless it's hardcoded into QVariant. */
1273 const QXmlName n1 = qVariantValue<QXmlName>(produced);
1274 const QXmlName n2 = qVariantValue<QXmlName>(expected);
1278 QCOMPARE(produced, expected);
1285 QCOMPARE(i, expectedValues.count());
1289 Send values from Qt into XQuery.
1291 void tst_QXmlQuery::basicQtToXQueryTypeCheck() const
1293 QFile queryFile(m_xmlPatternsDir + QLatin1String("/queries/") + QLatin1String("allAtomicsExternally.xq"));
1294 QVERIFY(queryFile.exists());
1295 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1297 QCOMPARE(QVariant(QDate(1999, 9, 10)).type(), QVariant::Date);
1301 QXmlNamePool np(query.namePool());
1303 const QXmlName name(np, QLatin1String("localname"),
1304 QLatin1String("http://example.com"),
1305 QLatin1String("prefix"));
1307 query.bindVariable(QLatin1String("fromQUrl"), QXmlItem(QUrl(QString::fromLatin1("http://example.com/"))));
1308 query.bindVariable(QLatin1String("fromQByteArray"), QXmlItem(QByteArray("AAAA")));
1309 query.bindVariable(QLatin1String("fromBool"), QXmlItem(bool(true)));
1310 query.bindVariable(QLatin1String("fromQDate"), QXmlItem(QDate(2000, 10, 11)));
1311 // TODO Do with different QDateTime time specs
1312 query.bindVariable(QLatin1String("fromQDateTime"), QXmlItem(QDateTime(QDate(2001, 9, 10), QTime(1, 2, 3))));
1313 query.bindVariable(QLatin1String("fromDouble"), QXmlItem(double(3)));
1314 query.bindVariable(QLatin1String("fromFloat"), QXmlItem(float(4)));
1315 query.bindVariable(QLatin1String("integer"), QXmlItem(5));
1316 query.bindVariable(QLatin1String("fromQString"), QXmlItem(QString::fromLatin1("A QString")));
1317 query.bindVariable(QLatin1String("fromQChar"), QXmlItem(QChar::fromLatin1('C')));
1319 query.bindVariable(QLatin1String("fromIntLiteral"), QXmlItem(QVariant(654)));
1322 QVariant ui(uint(5));
1323 QCOMPARE(ui.type(), QVariant::UInt);
1324 query.bindVariable(QLatin1String("fromUInt"), ui);
1328 QVariant ulnglng(qulonglong(6));
1329 QCOMPARE(ulnglng.type(), QVariant::ULongLong);
1330 query.bindVariable(QLatin1String("fromULongLong"), ulnglng);
1334 QVariant qlnglng(qlonglong(7));
1335 QCOMPARE(qlnglng.type(), QVariant::LongLong);
1336 query.bindVariable(QLatin1String("fromLongLong"), qlnglng);
1339 query.setQuery(&queryFile);
1341 // TODO do queries which declares external variables with types. Tons of combos here.
1342 // TODO ensure that binding with QXmlItem() doesn't make a binding available.
1343 // TODO test rebinding a variable.
1345 QVERIFY(query.isValid());
1348 query.evaluateTo(&it);
1349 QXmlItem item(it.next());
1350 QVERIFY(!item.isNull());
1351 QVERIFY(item.isAtomicValue());
1353 if(sizeof(qreal) == sizeof(float)) //ARM casts to Float not to double
1354 QCOMPARE(item.toAtomicValue().toString(),
1355 QLatin1String("4 true 3 654 7 41414141 C 2000-10-11Z 2001-09-10T01:02:03 "
1356 "A QString http://example.com/ 5 6 true false false true true true true true true true "
1359 QCOMPARE(item.toAtomicValue().toString(),
1360 QLatin1String("4 true 3 654 7 41414141 C 2000-10-11Z 2001-09-10T01:02:03 "
1361 "A QString http://example.com/ 5 6 true true true true true true true true true true "
1366 void tst_QXmlQuery::bindNode() const
1369 TestSimpleNodeModel nodeModel(query.namePool());
1371 query.bindVariable(QLatin1String("node"), nodeModel.root());
1374 QVERIFY(buff.open(QIODevice::WriteOnly));
1376 query.setQuery(QLatin1String("declare variable $node external; $node"));
1377 QXmlSerializer serializer(query, &buff);
1379 QVERIFY(query.evaluateTo(&serializer));
1380 QCOMPARE(out, QByteArray("<nodeName/>"));
1384 Pass in a relative URI, and make sure it is resolved against the current application directory.
1386 void tst_QXmlQuery::relativeBaseURI() const
1389 query.setQuery(QLatin1String("fn:static-base-uri()"), QUrl(QLatin1String("a/relative/uri.weirdExtension")));
1390 QVERIFY(query.isValid());
1393 QBuffer buffer(&result);
1394 QVERIFY(buffer.open(QIODevice::ReadWrite));
1396 QXmlSerializer serializer(query, &buffer);
1397 QVERIFY(query.evaluateTo(&serializer));
1399 const QUrl loaded(QUrl::fromEncoded(result));
1400 QUrl appPath(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()));
1402 QVERIFY(loaded.isValid());
1403 QVERIFY(appPath.isValid());
1404 QVERIFY(!loaded.isRelative());
1405 QVERIFY(!appPath.isRelative());
1407 QFileInfo dir(appPath.toLocalFile());
1408 dir.setFile(QString());
1410 /* We can't use QUrl::isParentOf() because it doesn't do what we want it to */
1411 if(!loaded.toLocalFile().startsWith(dir.absoluteFilePath()))
1412 QTextStream(stderr) << "dir.absoluteFilePath():" << dir.absoluteFilePath() << "loaded.toLocalFile():" << loaded.toLocalFile();
1414 checkBaseURI(loaded, dir.absoluteFilePath());
1417 void tst_QXmlQuery::emptyBaseURI() const
1420 query.setQuery(QLatin1String("fn:static-base-uri()"), QUrl());
1421 QVERIFY(query.isValid());
1424 QBuffer buffer(&result);
1425 QVERIFY(buffer.open(QIODevice::ReadWrite));
1427 QXmlSerializer serializer(query, &buffer);
1428 QVERIFY(query.evaluateTo(&serializer));
1430 const QUrl loaded(QUrl::fromEncoded(result));
1431 QUrl appPath(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()));
1433 QVERIFY(loaded.isValid());
1434 QVERIFY(appPath.isValid());
1435 QVERIFY(!loaded.isRelative());
1436 QVERIFY(!appPath.isRelative());
1438 QFileInfo dir(appPath.toLocalFile());
1439 dir.setFile(QString());
1441 QCOMPARE(loaded, appPath);
1445 Ensure that QDate comes out as QDateTime.
1447 void tst_QXmlQuery::roundTripDateWithinQXmlItem() const
1449 const QDate date(1999, 9, 10);
1450 QVERIFY(date.isValid());
1452 const QVariant variant(date);
1453 QVERIFY(variant.isValid());
1454 QCOMPARE(variant.type(), QVariant::Date);
1456 const QXmlItem item(variant);
1457 QVERIFY(!item.isNull());
1458 QVERIFY(item.isAtomicValue());
1460 const QVariant out(item.toAtomicValue());
1461 QVERIFY(out.isValid());
1462 QCOMPARE(out.type(), QVariant::Date);
1463 QCOMPARE(out.toDate(), date);
1467 Check whether a query is valid, which uses an unbound variable.
1469 void tst_QXmlQuery::bindingMissing() const
1472 MessageSilencer messageHandler;
1473 query.setMessageHandler(&messageHandler);
1475 QFile queryFile(m_xmlPatternsDir + QLatin1String("/queries/") + QString::fromLatin1("externalVariable.xq"));
1476 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1477 query.setQuery(&queryFile);
1479 QVERIFY(!query.isValid());
1482 void tst_QXmlQuery::bindDefaultConstructedItem() const
1484 QFETCH(QXmlItem, item);
1487 MessageSilencer messageHandler;
1488 query.setMessageHandler(&messageHandler);
1490 QFile queryFile(m_xmlPatternsDir + QLatin1String("/queries/") + QString::fromLatin1("externalVariable.xq"));
1491 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1492 query.setQuery(&queryFile);
1493 query.bindVariable(QLatin1String("externalVariable"), item);
1495 QVERIFY(!query.isValid());
1498 void tst_QXmlQuery::bindDefaultConstructedItem_data() const
1500 QTest::addColumn<QXmlItem>("item");
1502 QTest::newRow("QXmlItem()") << QXmlItem();
1503 QTest::newRow("QXmlItem(QVariant())") << QXmlItem(QVariant());
1504 QTest::newRow("QXmlItem(QXmlNodeModelIndex())") << QXmlItem(QXmlNodeModelIndex());
1508 Remove a binding by binding QXmlItem() with the same name.
1510 void tst_QXmlQuery::eraseQXmlItemBinding() const
1513 MessageSilencer messageHandler;
1514 query.setMessageHandler(&messageHandler);
1516 QFile queryFile(m_xmlPatternsDir + QLatin1String("/queries/") + QString::fromLatin1("externalVariable.xq"));
1517 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1518 query.bindVariable(QLatin1String("externalVariable"), QXmlItem(3));
1519 query.setQuery(&queryFile);
1520 QVERIFY(query.isValid());
1523 QBuffer buffer(&result);
1524 QVERIFY(buffer.open(QIODevice::ReadWrite));
1526 QXmlSerializer serializer(query, &buffer);
1527 QVERIFY(query.evaluateTo(&serializer));
1529 QCOMPARE(result, QByteArray("3 6<e>3</e>false"));
1531 query.bindVariable(QLatin1String("externalVariable"), QXmlItem());
1532 QVERIFY(!query.isValid());
1536 Erase a variable binding
1538 void tst_QXmlQuery::eraseDeviceBinding() const
1540 /* Erase an existing QIODevice binding with another QIODevice binding. */
1544 QByteArray doc("<e/>");
1545 QBuffer buffer(&doc);
1546 QVERIFY(buffer.open(QIODevice::ReadOnly));
1548 query.bindVariable(QLatin1String("in"), &buffer);
1549 query.setQuery(QLatin1String("$in"));
1550 QVERIFY(query.isValid());
1552 query.bindVariable(QLatin1String("in"), 0);
1553 QVERIFY(!query.isValid());
1556 /* Erase an existing QXmlItem binding with another QIODevice binding. */
1560 query.bindVariable(QLatin1String("in"), QXmlItem(5));
1561 query.setQuery(QLatin1String("$in"));
1562 QVERIFY(query.isValid());
1564 query.bindVariable(QLatin1String("in"), 0);
1565 QVERIFY(!query.isValid());
1570 Bind a variable, evaluate, bind with a different value but same type, and evaluate again.
1572 void tst_QXmlQuery::rebindVariableSameType() const
1575 MessageSilencer messageHandler;
1576 query.setMessageHandler(&messageHandler);
1578 query.bindVariable(QLatin1String("externalVariable"), QXmlItem(3));
1581 QFile queryFile(m_xmlPatternsDir + QLatin1String("/queries/") + QString::fromLatin1("externalVariable.xq"));
1582 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1583 query.setQuery(&queryFile);
1586 QVERIFY(query.isValid());
1590 QBuffer buffer(&result);
1591 QVERIFY(buffer.open(QIODevice::ReadWrite));
1593 QXmlSerializer serializer(query, &buffer);
1594 QVERIFY(query.evaluateTo(&serializer));
1596 QCOMPARE(result, QByteArray("3 6<e>3</e>false"));
1600 query.bindVariable(QLatin1String("externalVariable"), QXmlItem(5));
1602 QBuffer buffer(&result);
1603 QVERIFY(buffer.open(QIODevice::ReadWrite));
1605 QXmlSerializer serializer(query, &buffer);
1606 QVERIFY(query.evaluateTo(&serializer));
1608 QCOMPARE(result, QByteArray("5 8<e>5</e>false"));
1613 void tst_QXmlQuery::rebindVariableDifferentType() const
1615 /* Rebind QXmlItem variable with QXmlItem variable. */
1618 query.bindVariable(QLatin1String("in"), QXmlItem(3));
1619 query.setQuery(QLatin1String("$in"));
1620 QVERIFY(query.isValid());
1622 query.bindVariable(QLatin1String("in"), QXmlItem("A string"));
1623 QVERIFY(!query.isValid());
1626 /* Rebind QIODevice variable with QXmlItem variable. */
1630 buffer.setData(QByteArray("<e/>"));
1631 QVERIFY(buffer.open(QIODevice::ReadOnly));
1633 query.bindVariable(QLatin1String("in"), &buffer);
1634 query.setQuery(QLatin1String("$in"));
1635 QVERIFY(query.isValid());
1637 query.bindVariable(QLatin1String("in"), QXmlItem("A string"));
1638 QVERIFY(!query.isValid());
1641 /* Rebind QXmlItem variable with QIODevice variable. The type of the
1642 * variable changes, so a recompile is necessary. */
1646 query.bindVariable(QLatin1String("in"), QXmlItem(QLatin1String("A string")));
1647 query.setQuery(QLatin1String("$in"));
1648 QVERIFY(query.isValid());
1651 buffer.setData(QByteArray("<e/>"));
1652 QVERIFY(buffer.open(QIODevice::ReadOnly));
1653 query.bindVariable(QLatin1String("in"), &buffer);
1654 QVERIFY(!query.isValid());
1658 void tst_QXmlQuery::rebindVariableWithNullItem() const
1662 query.bindVariable(QLatin1String("name"), QXmlItem(5));
1663 query.bindVariable(QLatin1String("name"), QXmlItem());
1666 void tst_QXmlQuery::constCorrectness() const
1668 QXmlResultItems result;
1670 tmp.setQuery(QLatin1String("1")); /* Just so we have a valid query. */
1671 const QXmlQuery query(tmp);
1673 /* These functions should be const. */
1675 query.evaluateTo(&result);
1677 query.uriResolver();
1678 query.messageHandler();
1681 QString dummyString;
1682 QTextStream dummyStream(&dummyString);
1683 PushBaseliner dummy(dummyStream, query.namePool());
1684 QVERIFY(dummy.isValid());
1685 query.evaluateTo(&dummy);
1689 void tst_QXmlQuery::objectSize() const
1691 /* We have a d pointer. */
1692 QCOMPARE(sizeof(QXmlQuery), sizeof(void *));
1695 void tst_QXmlQuery::setUriResolver() const
1697 /* Set a null resolver, and make sure it can take a const pointer. */
1700 query.setUriResolver(static_cast<const QAbstractUriResolver *>(0));
1701 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0));
1705 TestURIResolver resolver;
1707 query.setUriResolver(&resolver);
1708 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(&resolver));
1712 void tst_QXmlQuery::uriResolver() const
1714 /* Check default value. */
1717 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0));
1721 void tst_QXmlQuery::messageXML() const
1725 MessageValidator messageValidator;
1726 query.setMessageHandler(&messageValidator);
1728 query.setQuery(QLatin1String("1basicSyntaxError"));
1730 QRegExp removeFilename(QLatin1String("Location: file:.*\\#"));
1731 QVERIFY(removeFilename.isValid());
1733 QVERIFY(messageValidator.success());
1734 QCOMPARE(messageValidator.received().remove(removeFilename),
1735 QString::fromLatin1("Type:3\n"
1736 "Description: <html xmlns='http://www.w3.org/1999/xhtml/'><body><p>syntax error, unexpected unknown keyword</p></body></html>\n"
1737 "Identifier: http://www.w3.org/2005/xqt-errors#XPST0003\n"
1742 1. Allocate QXmlResultItems
1743 2. Allocate QXmlQuery
1744 3. evaluate to the QXmlResultItems instance
1745 4. Dellocate the QXmlQuery instance
1746 5. Ensure QXmlResultItems works
1748 void tst_QXmlQuery::resultItemsDeallocatedQuery() const
1750 QXmlResultItems result;
1754 query.setQuery(QLatin1String("1, 2, xs:integer(<e>3</e>)"));
1755 query.evaluateTo(&result);
1758 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
1759 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
1760 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
1761 QVERIFY(result.next().isNull());
1762 QVERIFY(!result.hasError());
1766 1. Bind variable with bindVariable()
1767 2. setQuery that has 'declare variable' with same name.
1768 3. Ensure the value inside the query is used. We don't guarantee this behavior
1769 but that's what we lock.
1771 void tst_QXmlQuery::shadowedVariables() const
1774 query.bindVariable("varName", QXmlItem(3));
1775 query.setQuery(QLatin1String("declare variable $varName := 5; $varName"));
1777 QXmlResultItems result;
1778 query.evaluateTo(&result);
1780 QCOMPARE(result.next().toAtomicValue(), QVariant(5));
1783 void tst_QXmlQuery::setFocusQXmlItem() const
1785 /* Make sure we can take a const reference. */
1788 const QXmlItem item;
1789 query.setFocus(item);
1792 // TODO evaluate with atomic value, check type
1793 // TODO evaluate with node, check type
1794 // TODO ensure that setFocus() triggers query recompilation, as appropriate.
1795 // TODO let the focus be undefined, call isvalid, call evaluate anyway
1796 // TODO let the focus be undefined, call evaluate directly
1799 void tst_QXmlQuery::setFocusQUrl() const
1801 /* Load a focus which isn't well-formed. */
1804 MessageSilencer silencer;
1806 query.setMessageHandler(&silencer);
1808 QVERIFY(!query.setFocus(QUrl(QLatin1String("data/notWellformed.xml"))));
1811 /* Ensure the same URI resolver is used. */
1813 QXmlQuery query(QXmlQuery::XSLT20);
1815 const TestURIResolver resolver(QUrl(inputFileAsURI(m_xmlPatternsDir + QLatin1String("/stylesheets/documentElement.xml"))));
1816 query.setUriResolver(&resolver);
1818 QVERIFY(query.setFocus(QUrl(QLatin1String("arbitraryURI"))));
1819 query.setQuery(QUrl(inputFileAsURI(m_xmlPatternsDir + QLatin1String("/stylesheets/copyWholeDocument.xsl"))));
1820 QVERIFY(query.isValid());
1823 QVERIFY(result.open(QIODevice::ReadWrite));
1824 QXmlSerializer serializer(query, &result);
1825 query.evaluateTo(&serializer);
1827 QCOMPARE(result.data(), QByteArray("<doc/>"));
1830 // TODO ensure that the focus type doesn't change from XSLT20 on the main instance.
1834 This code poses a challenge wrt. to internal caching.
1836 void tst_QXmlQuery::setFocusQIODevice() const
1842 focus.setData(QByteArray("<e>abc</e>"));
1843 QVERIFY(focus.open(QIODevice::ReadOnly));
1844 query.setFocus(&focus);
1845 query.setQuery(QLatin1String("string()"));
1846 QVERIFY(query.isValid());
1849 query.evaluateTo(&output);
1851 QCOMPARE(output, QString::fromLatin1("abc\n"));
1854 /* Set a new focus, make sure it changes & works. */
1857 focus2.setData(QByteArray("<e>abc2</e>"));
1858 QVERIFY(focus2.open(QIODevice::ReadOnly));
1859 query.setFocus(&focus2);
1860 QVERIFY(query.isValid());
1863 query.evaluateTo(&output);
1865 QCOMPARE(output, QString::fromLatin1("abc2\n"));
1870 Since we internally use variable bindings for implementing the focus, we need
1871 to make sure we don't clash in this area.
1873 void tst_QXmlQuery::setFocusQIODeviceAvoidVariableClash() const
1876 buffer.setData("<e>focus</e>");
1877 QVERIFY(buffer.open(QIODevice::ReadOnly));
1879 /* First we bind the variable name, then the focus. */
1882 query.bindVariable(QString(QLatin1Char('u')), QVariant(1));
1883 query.setFocus(&buffer);
1884 query.setQuery(QLatin1String("string()"));
1887 query.evaluateTo(&out);
1889 QCOMPARE(out, QString::fromLatin1("focus\n"));
1892 /* First we bind the focus, then the variable name. */
1895 QVERIFY(buffer.open(QIODevice::ReadOnly));
1896 query.setFocus(&buffer);
1897 query.bindVariable(QString(QLatin1Char('u')), QVariant(1));
1898 query.setQuery(QLatin1String("string()"));
1901 query.evaluateTo(&out);
1903 QCOMPARE(out, QString::fromLatin1("focus\n"));
1907 void tst_QXmlQuery::setFocusQIODeviceFailure() const
1909 /* A not well-formed input document. */
1913 MessageSilencer silencer;
1914 query.setMessageHandler(&silencer);
1917 input.setData("<e");
1918 QVERIFY(input.open(QIODevice::ReadOnly));
1920 QCOMPARE(query.setFocus(&input), false);
1924 void tst_QXmlQuery::setFocusQString() const
1928 /* Basic use of focus. */
1930 QVERIFY(query.setFocus(QLatin1String("<e>textNode</e>")));
1931 query.setQuery(QLatin1String("string()"));
1932 QVERIFY(query.isValid());
1934 query.evaluateTo(&out);
1935 QCOMPARE(out, QString::fromLatin1("textNode\n"));
1938 /* Set to a new focus, make sure it changes and works. */
1940 QVERIFY(query.setFocus(QLatin1String("<e>newFocus</e>")));
1942 query.evaluateTo(&out);
1943 QCOMPARE(out, QString::fromLatin1("newFocus\n"));
1947 void tst_QXmlQuery::setFocusQStringFailure() const
1950 MessageSilencer silencer;
1952 query.setMessageHandler(&silencer);
1953 QVERIFY(!query.setFocus(QLatin1String("<notWellformed")));
1955 /* Let's try the slight special case of a null string. */
1956 QVERIFY(!query.setFocus(QString()));
1959 void tst_QXmlQuery::setFocusQStringSignature() const
1962 MessageSilencer silencer;
1963 query.setMessageHandler(&silencer);
1965 const QString argument;
1966 /* We should take a const ref. */
1967 query.setFocus(argument);
1969 /* We should return a bool. */
1970 static_cast<bool>(query.setFocus(QString()));
1973 void tst_QXmlQuery::setFocusQIODeviceTriggerWarnings() const
1975 /* A null pointer. */
1979 QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed.");
1980 QCOMPARE(query.setFocus(static_cast<QIODevice *>(0)), false);
1983 /* A non opened-device. */
1987 QBuffer notReadable;
1988 QVERIFY(!notReadable.isReadable());
1990 QTest::ignoreMessage(QtWarningMsg, "The device must be readable.");
1991 QCOMPARE(query.setFocus(¬Readable), false);
1995 void tst_QXmlQuery::fnDocNetworkAccessSuccess() const
1997 #if defined(Q_OS_WINCE) && !defined(_X86_)
1998 QStringList testsToSkip;
1999 testsToSkip << "http scheme" << "ftp scheme";
2000 if (testsToSkip.contains(QTest::currentDataTag()))
2001 QSKIP("Network tests are currently unsupported on Windows CE.");
2004 QFETCH(QUrl, uriToOpen);
2005 QFETCH(QByteArray, expectedOutput);
2007 if(!uriToOpen.isValid())
2008 qDebug() << "uriToOpen:" << uriToOpen;
2010 QVERIFY(uriToOpen.isValid());
2013 query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen));
2014 query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)"));
2015 QVERIFY(query.isValid());
2018 QBuffer buffer(&result);
2019 QVERIFY(buffer.open(QIODevice::WriteOnly));
2021 QXmlSerializer serializer(query, &buffer);
2022 QVERIFY(query.evaluateTo(&serializer));
2024 QCOMPARE(result, expectedOutput);
2027 void tst_QXmlQuery::fnDocNetworkAccessSuccess_data() const
2029 QTest::addColumn<QUrl>("uriToOpen");
2030 QTest::addColumn<QByteArray>("expectedOutput");
2032 QTest::newRow("file scheme")
2033 << inputFileAsURI(QLatin1String(SRCDIR "input.xml"))
2034 << QByteArray("<!-- This is just a file for testing. --><input/>");
2036 QTest::newRow("data scheme with ASCII")
2037 /* QUrl::toPercentEncoding(QLatin1String("<e/>")) yields "%3Ce%2F%3E". */
2038 << QUrl::fromEncoded("data:application/xml,%3Ce%2F%3E")
2039 << QByteArray("<e/>");
2041 QTest::newRow("data scheme with ASCII no MIME type")
2042 << QUrl::fromEncoded("data:,%3Ce%2F%3E")
2043 << QByteArray("<e/>");
2045 QTest::newRow("data scheme with base 64")
2046 << QUrl::fromEncoded("data:application/xml;base64,PGUvPg==")
2047 << QByteArray("<e/>");
2049 QTest::newRow("qrc scheme")
2050 << QUrl::fromEncoded("qrc:/QXmlQueryTestData/data/oneElement.xml")
2051 << QByteArray("<oneElement/>");
2056 QTest::newRow("http scheme")
2057 << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/wellFormed.xml"))
2058 << QByteArray("<!-- a comment --><e from=\"http\">Some Text</e>");
2060 QTest::newRow("ftp scheme")
2061 << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/wellFormed.xml"))
2062 << QByteArray("<!-- a comment --><e from=\"ftp\">Some Text</e>");
2066 void tst_QXmlQuery::fnDocNetworkAccessFailure() const
2068 QFETCH(QUrl, uriToOpen);
2070 QVERIFY(uriToOpen.isValid());
2073 MessageSilencer silencer;
2074 query.setMessageHandler(&silencer);
2075 query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen));
2076 query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)"));
2077 QVERIFY(query.isValid());
2079 QXmlResultItems result;
2080 query.evaluateTo(&result);
2082 while(!result.next().isNull())
2084 /* Just loop until the end. */
2087 // TODO do something that triggers a /timeout/.
2088 QVERIFY(result.hasError());
2091 void tst_QXmlQuery::fnDocNetworkAccessFailure_data() const
2093 QTest::addColumn<QUrl>("uriToOpen");
2095 QTest::newRow("data scheme, not-well-formed")
2096 << QUrl(QLatin1String("data:application/xml;base64,PGUvg==="));
2098 QTest::newRow("file scheme, non-existant file")
2099 << QUrl(QLatin1String("file:///example.com/does/notExist.xml"));
2101 QTest::newRow("http scheme, file not found")
2102 << QUrl(QLatin1String("http://www.example.com/does/not/exist.xml"));
2104 QTest::newRow("http scheme, nonexistent host")
2105 << QUrl(QLatin1String("http://this.host.does.not.exist.I.SWear"));
2107 QTest::newRow("qrc scheme, not well-formed")
2108 << QUrl(QLatin1String("qrc:/QXmlQueryTestData/notWellformed.xml"));
2110 QTest::newRow("'qrc:/', non-existing file")
2111 << QUrl(QLatin1String(":/QXmlQueryTestData/data/thisFileDoesNotExist.xml"));
2113 QTest::newRow("':/', this scheme is not supported")
2114 << QUrl(QLatin1String(":/QXmlQueryTestData/data/notWellformed.xml"));
2119 QTest::newRow("http scheme, not well-formed")
2120 << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/notWellformed.xml"));
2122 QTest::newRow("https scheme, not well-formed")
2123 << QUrl(QString("https://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/notWellformedViaHttps.xml"));
2125 QTest::newRow("https scheme, nonexistent host")
2126 << QUrl(QLatin1String("https://this.host.does.not.exist.I.SWear"));
2128 QTest::newRow("ftp scheme, nonexistent host")
2129 << QUrl(QLatin1String("ftp://this.host.does.not.exist.I.SWear"));
2131 QTest::newRow("ftp scheme, not well-formed")
2132 << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/notWellFormed.xml"));
2136 Create a network timeout from a QIODevice binding such
2137 that we ensure we don't hang infinitely.
2139 void tst_QXmlQuery::fnDocOnQIODeviceTimeout() const
2145 server.listen(QHostAddress::LocalHost, 1088);
2148 client.connectToHost("localhost", 1088);
2149 QVERIFY(client.isReadable());
2153 MessageSilencer silencer;
2154 query.setMessageHandler(&silencer);
2156 query.bindVariable(QLatin1String("inDevice"), &client);
2157 query.setQuery(QLatin1String("declare variable $inDevice external;\ndoc($inDevice)"));
2158 QVERIFY(query.isValid());
2160 QXmlResultItems result;
2161 query.evaluateTo(&result);
2162 QXmlItem next(result.next());
2164 while(!next.isNull())
2166 next = result.next();
2169 QVERIFY(result.hasError());
2173 When changing query, the static context must change too, such that
2174 the source locations are updated.
2176 void tst_QXmlQuery::recompilationWithEvaluateToResultFailing() const
2179 MessageSilencer silencer;
2180 query.setMessageHandler(&silencer);
2182 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2183 QVERIFY(query.isValid()); /* Trigger query compilation. */
2185 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2186 QVERIFY(query.isValid()); /* Trigger second compilation. */
2188 QXmlResultItems items;
2189 query.evaluateTo(&items);
2191 QVERIFY(items.hasError());
2194 void tst_QXmlQuery::secondEvaluationWithEvaluateToResultFailing() const
2197 MessageSilencer silencer;
2198 query.setMessageHandler(&silencer);
2200 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2201 QVERIFY(query.isValid()); /* Trigger query compilation. */
2203 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2204 /* We don't call isValid(). */
2205 QXmlResultItems items;
2206 query.evaluateTo(&items);
2208 QVERIFY(items.hasError());
2212 Compilation is triggered in the evaluation function due to no call to QXmlQuery::isValid().
2214 void tst_QXmlQuery::recompilationWithEvaluateToReceiver() const
2217 MessageSilencer silencer;
2218 query.setMessageHandler(&silencer);
2220 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2221 QVERIFY(query.isValid()); /* Trigger query compilation. */
2223 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2224 /* We don't call isValid(). */
2227 QBuffer buffer(&dummy);
2228 buffer.open(QIODevice::WriteOnly);
2230 QXmlSerializer serializer(query, &buffer);
2232 QVERIFY(!query.evaluateTo(&serializer));
2235 void tst_QXmlQuery::evaluateToQStringListOnInvalidQuery() const
2237 MessageSilencer silencer;
2239 /* Invoke on a default constructed object. */
2243 QVERIFY(!query.evaluateTo(&out));
2246 /* Invoke on a syntactically invalid query. */
2250 MessageSilencer silencer;
2252 query.setMessageHandler(&silencer);
2253 query.setQuery(QLatin1String("1 + "));
2255 QVERIFY(!query.evaluateTo(&out));
2258 /* Invoke on a query with the wrong type, one atomic. */
2263 query.setQuery(QLatin1String("1"));
2264 query.setMessageHandler(&silencer);
2265 QVERIFY(!query.evaluateTo(&out));
2268 /* Invoke on a query with the wrong type, one element. */
2273 query.setQuery(QLatin1String("<e/>"));
2274 QVERIFY(!query.evaluateTo(&out));
2277 /* Invoke on a query with the wrong type, mixed nodes & atomics. */
2282 query.setQuery(QLatin1String("<e/>, 1, 'a string'"));
2283 query.setMessageHandler(&silencer);
2284 QVERIFY(!query.evaluateTo(&out));
2287 /* Evaluate the empty sequence. */
2292 query.setQuery(QLatin1String("()"));
2293 QVERIFY(!query.evaluateTo(&out));
2294 QVERIFY(out.isEmpty());
2298 void tst_QXmlQuery::evaluateToQStringList() const
2300 QFETCH(QString, queryString);
2301 QFETCH(QStringList, expectedOutput);
2304 query.setQuery(queryString);
2306 QVERIFY(query.isValid());
2308 QVERIFY(query.evaluateTo(&out));
2310 QCOMPARE(out, expectedOutput);
2313 void tst_QXmlQuery::evaluateToQStringListTriggerWarnings() const
2317 QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed.");
2318 QCOMPARE(query.evaluateTo(static_cast<QStringList *>(0)),
2322 void tst_QXmlQuery::evaluateToQStringList_data() const
2324 QTest::addColumn<QString>("queryString");
2325 QTest::addColumn<QStringList>("expectedOutput");
2327 QTest::newRow("One atomic")
2328 << QString::fromLatin1("(1 + 1) cast as xs:string")
2329 << QStringList(QString::fromLatin1("2"));
2332 QStringList expected;
2333 expected << QLatin1String("2");
2334 expected << QLatin1String("a string");
2336 QTest::newRow("Two atomics")
2337 << QString::fromLatin1("(1 + 1) cast as xs:string, 'a string'")
2341 QTest::newRow("A query which evaluates to sub-types of xs:string.")
2342 << QString::fromLatin1("xs:NCName('NCName'), xs:normalizedString(' a b c ')")
2343 << (QStringList() << QString::fromLatin1("NCName")
2344 << QString::fromLatin1(" a b c "));
2346 QTest::newRow("A query which evaluates to two elements.")
2347 << QString::fromLatin1("string(<e>theString1</e>), string(<e>theString2</e>)")
2348 << (QStringList() << QString::fromLatin1("theString1")
2349 << QString::fromLatin1("theString2"));
2353 Ensure that we don't automatically convert non-xs:string values.
2355 void tst_QXmlQuery::evaluateToQStringListNoConversion() const
2358 query.setQuery(QString::fromLatin1("<e/>"));
2359 QVERIFY(query.isValid());
2361 QVERIFY(!query.evaluateTo(&result));
2364 void tst_QXmlQuery::evaluateToQIODevice() const
2366 /* an XQuery, check that no indentation is performed. */
2369 QVERIFY(out.open(QIODevice::ReadWrite));
2372 query.setQuery(QLatin1String("<a><b/></a>"));
2373 QVERIFY(query.isValid());
2374 QVERIFY(query.evaluateTo(&out));
2375 QCOMPARE(out.data(), QByteArray("<a><b/></a>"));
2379 void tst_QXmlQuery::evaluateToQIODeviceTriggerWarnings() const
2383 QTest::ignoreMessage(QtWarningMsg, "The pointer to the device cannot be null.");
2384 QCOMPARE(query.evaluateTo(static_cast<QIODevice *>(0)),
2389 QTest::ignoreMessage(QtWarningMsg, "The device must be writable.");
2390 QCOMPARE(query.evaluateTo(&buffer),
2394 void tst_QXmlQuery::evaluateToQIODeviceSignature() const
2396 /* The function should be const. */
2399 QVERIFY(out.open(QIODevice::ReadWrite));
2401 const QXmlQuery query;
2403 query.evaluateTo(&out);
2407 void tst_QXmlQuery::evaluateToQIODeviceOnInvalidQuery() const
2410 QVERIFY(out.open(QIODevice::WriteOnly));
2412 /* On syntactically invalid query. */
2415 MessageSilencer silencer;
2416 query.setMessageHandler(&silencer);
2417 query.setQuery(QLatin1String("1 +"));
2418 QVERIFY(!query.isValid());
2419 QVERIFY(!query.evaluateTo(&out));
2422 /* On null QXmlQuery instance. */
2425 QVERIFY(!query.evaluateTo(&out));
2430 void tst_QXmlQuery::setQueryQIODeviceQUrl() const
2435 buffer.setData("1, 2, 2 + 1");
2436 QVERIFY(buffer.open(QIODevice::ReadOnly));
2439 query.setQuery(&buffer);
2440 QVERIFY(query.isValid());
2442 QXmlResultItems result;
2443 query.evaluateTo(&result);
2444 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
2445 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
2446 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
2447 QVERIFY(result.next().isNull());
2448 QVERIFY(!result.hasError());
2451 /* Set query that is invalid. */
2454 buffer.setData("1, ");
2455 QVERIFY(buffer.open(QIODevice::ReadOnly));
2458 MessageSilencer silencer;
2459 query.setMessageHandler(&silencer);
2460 query.setQuery(&buffer);
2461 QVERIFY(!query.isValid());
2464 /* Check that the base URI passes through. */
2467 buffer.setData("string(static-base-uri())");
2468 QVERIFY(buffer.open(QIODevice::ReadOnly));
2471 query.setQuery(&buffer, QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl"));
2472 QVERIFY(query.isValid());
2475 query.evaluateTo(&result);
2476 QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
2480 void tst_QXmlQuery::setQueryQIODeviceQUrlTriggerWarnings() const
2483 QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed.");
2487 QTest::ignoreMessage(QtWarningMsg, "The device must be readable.");
2488 query.setQuery(&buffer);
2491 void tst_QXmlQuery::setQueryQString() const
2496 query.setQuery(QLatin1String("1, 2, 2 + 1"));
2497 QVERIFY(query.isValid());
2499 QXmlResultItems result;
2500 query.evaluateTo(&result);
2501 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
2502 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
2503 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
2504 QVERIFY(result.next().isNull());
2505 QVERIFY(!result.hasError());
2508 /* Set query that is invalid. */
2510 MessageSilencer silencer;
2512 query.setMessageHandler(&silencer);
2513 query.setQuery(QLatin1String("1, "));
2514 QVERIFY(!query.isValid());
2517 /* Check that the base URI passes through. */
2520 query.setQuery(QLatin1String("string(static-base-uri())"), QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl"));
2521 QVERIFY(query.isValid());
2524 query.evaluateTo(&result);
2525 QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
2529 void tst_QXmlQuery::setQueryQUrlSuccess() const
2531 #if defined(Q_OS_WINCE) && !defined(_X86_)
2532 QStringList testsToSkip;
2533 testsToSkip << "A valid query via the ftp scheme" << "A valid query via the http scheme";
2534 if (testsToSkip.contains(QTest::currentDataTag()))
2535 QSKIP("Network tests are currently unsupported on Windows CE.");
2538 QFETCH(QUrl, queryURI);
2539 QFETCH(QByteArray, expectedOutput);
2541 QVERIFY(queryURI.isValid());
2545 MessageSilencer silencer;
2546 query.setMessageHandler(&silencer);
2548 query.setQuery(queryURI);
2549 QVERIFY(query.isValid());
2552 QBuffer buffer(&out);
2553 QVERIFY(buffer.open(QIODevice::WriteOnly));
2554 QXmlSerializer serializer(query, &buffer);
2556 query.evaluateTo(&serializer);
2557 QCOMPARE(out, expectedOutput);
2560 void tst_QXmlQuery::setQueryQUrlSuccess_data() const
2562 QTest::addColumn<QUrl>("queryURI");
2563 QTest::addColumn<QByteArray>("expectedOutput");
2565 QTest::newRow("A valid query via the data scheme")
2566 << QUrl::fromEncoded("data:application/xml,1%20%2B%201") /* "1 + 1" */
2569 QTest::newRow("A valid query via the file scheme")
2570 << QUrl::fromLocalFile(inputFile(m_xmlPatternsDir + QLatin1String("/queries/") + QLatin1String("onePlusOne.xq")))
2576 QTest::newRow("A valid query via the ftp scheme")
2577 << QUrl::fromEncoded(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/viaFtp.xq").toLatin1())
2578 << QByteArray("This was received via FTP");
2580 QTest::newRow("A valid query via the http scheme")
2581 << QUrl::fromEncoded(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/viaHttp.xq").toLatin1())
2582 << QByteArray("This was received via HTTP.");
2585 void tst_QXmlQuery::setQueryQUrlFailSucceed() const
2588 MessageSilencer silencer;
2590 query.setMessageHandler(&silencer);
2592 query.setQuery(QLatin1String("1 + 1"));
2593 QVERIFY(query.isValid());
2595 query.setQuery(QUrl::fromEncoded("file://example.com/does/not/exist"));
2596 QVERIFY(!query.isValid());
2599 void tst_QXmlQuery::setQueryQUrlFailure() const
2601 QFETCH(QUrl, queryURI);
2603 MessageSilencer silencer;
2606 query.setMessageHandler(&silencer);
2607 query.setQuery(queryURI);
2608 QVERIFY(!query.isValid());
2611 void tst_QXmlQuery::setQueryQUrlFailure_data() const
2613 QTest::addColumn<QUrl>("queryURI");
2615 QTest::newRow("Query via file:// that does not exist.")
2616 << QUrl::fromEncoded("file://example.com/does/not/exist");
2618 QTest::newRow("A query via file:// that is completely empty, but readable.")
2619 << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("../xmlpatterns/queries/completelyEmptyQuery.xq"));
2622 const QString name(QLatin1String("nonReadableFile.xq"));
2623 QFile outFile(name);
2624 QVERIFY(outFile.open(QIODevice::WriteOnly));
2625 outFile.write(QByteArray("1"));
2627 /* On some windows versions, this fails, so we don't check that this works with QVERIFY. */
2628 outFile.setPermissions(QFile::Permissions(QFile::Permissions()));
2630 QTest::newRow("Query via file:/ that does not have read permissions.")
2631 << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("nonReadableFile.xq"));
2637 QTest::newRow("Query via HTTP that does not exist.")
2638 << QUrl::fromEncoded("http://example.com/NoQuery/ISWear");
2641 QTest::newRow("Query via FTP that does not exist.")
2642 << QUrl::fromEncoded("ftp://example.com/NoQuery/ISWear");
2645 QTest::newRow("A query via http:// that is completely empty, but readable.")
2646 << QUrl::fromEncoded(QString(
2647 "http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/completelyEmptyQuery.xq").toLatin1());
2649 QTest::newRow("A query via ftp:// that is completely empty, but readable.")
2650 << QUrl::fromEncoded(QString(
2651 "ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/completelyEmptyQuery.xq").toLatin1());
2655 void tst_QXmlQuery::setQueryQUrlBaseURI() const
2657 QFETCH(QUrl, inputBaseURI);
2658 QFETCH(QUrl, expectedBaseURI);
2662 query.setQuery(QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq")), inputBaseURI);
2663 QVERIFY(query.isValid());
2666 QVERIFY(query.evaluateTo(&result));
2667 QCOMPARE(result.count(), 1);
2669 if(qstrcmp(QTest::currentDataTag(), "Relative base URI") == 0)
2670 checkBaseURI(QUrl(result.first()), QCoreApplication::applicationFilePath());
2672 QCOMPARE(result.first(), expectedBaseURI.toString());
2675 void tst_QXmlQuery::setQueryQUrlBaseURI_data() const
2677 QTest::addColumn<QUrl>("inputBaseURI");
2678 QTest::addColumn<QUrl>("expectedBaseURI");
2680 QTest::newRow("absolute HTTP")
2681 << QUrl(QLatin1String("http://www.example.com/"))
2682 << QUrl(QLatin1String("http://www.example.com/"));
2684 QTest::newRow("None, so the query URI is used")
2686 << QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq"));
2688 QTest::newRow("Relative base URI")
2689 << QUrl(QLatin1String("../data/relative.uri"))
2694 1. Create a valid query.
2695 2. Call setQuery(QUrl), with a query file that doesn't exist.
2696 3. Verify that the query has changed state into invalid.
2698 void tst_QXmlQuery::setQueryWithNonExistentQUrlOnValidQuery() const
2702 MessageSilencer messageSilencer;
2703 query.setMessageHandler(&messageSilencer);
2705 query.setQuery(QLatin1String("1 + 1"));
2706 QVERIFY(query.isValid());
2708 query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/DOESNOTEXIST.xq"));
2709 QVERIFY(!query.isValid());
2713 1. Create a valid query.
2714 2. Call setQuery(QUrl), with a query file that is invalid.
2715 3. Verify that the query has changed state into invalid.
2717 void tst_QXmlQuery::setQueryWithInvalidQueryFromQUrlOnValidQuery() const
2721 MessageSilencer messageSilencer;
2722 query.setMessageHandler(&messageSilencer);
2724 query.setQuery(QLatin1String("1 + 1"));
2725 QVERIFY(query.isValid());
2727 query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/queries/syntaxError.xq"));
2728 QVERIFY(!query.isValid());
2732 This triggered two bugs:
2734 - First, the DynamicContext wasn't assigned to QXmlResultItems, meaning it went out of
2735 scope and therefore deallocated the document pool, and calls
2736 to QXmlResultItems::next() would use dangling pointers.
2738 - Conversion between QPatternist::Item and QXmlItem was incorrectly done, leading to nodes
2739 being treated as atomic values, and subsequent crashes.
2742 void tst_QXmlQuery::retrieveNameFromQuery() const
2744 QFETCH(QString, queryString);
2745 QFETCH(QString, expectedName);
2748 query.setQuery(queryString);
2749 QVERIFY(query.isValid());
2750 QXmlResultItems result;
2751 query.evaluateTo(&result);
2753 QVERIFY(!result.hasError());
2755 const QXmlItem item(result.next());
2756 QVERIFY(!result.hasError());
2757 QVERIFY(!item.isNull());
2758 QVERIFY(item.isNode());
2760 const QXmlNodeModelIndex node(item.toNodeModelIndex());
2761 QVERIFY(!node.isNull());
2763 QCOMPARE(node.model()->name(node).localName(query.namePool()), expectedName);
2766 void tst_QXmlQuery::retrieveNameFromQuery_data() const
2768 QTest::addColumn<QString>("queryString");
2769 QTest::addColumn<QString>("expectedName");
2771 QTest::newRow("Document-node")
2772 << QString::fromLatin1("document{<elementName/>}")
2775 QTest::newRow("Element")
2776 << QString::fromLatin1("document{<elementName/>}/*")
2777 << QString::fromLatin1("elementName");
2781 Binding a null QString leads to no variable binding, but an
2782 empty non-null QString is possible.
2784 void tst_QXmlQuery::bindEmptyNullString() const
2786 MessageSilencer messageHandler;
2788 query.setMessageHandler(&messageHandler);
2789 query.setQuery(QLatin1String("declare variable $v external; $v"));
2790 /* Here, we effectively pass an invalid QVariant. */
2791 query.bindVariable(QLatin1String("v"), QVariant(QString()));
2792 QVERIFY(!query.isValid());
2795 QVERIFY(!query.evaluateTo(&result));
2798 void tst_QXmlQuery::bindEmptyString() const
2801 query.bindVariable(QLatin1String("v"), QVariant(QString(QLatin1String(""))));
2802 query.setQuery(QLatin1String("declare variable $v external; $v"));
2803 QVERIFY(query.isValid());
2806 QVERIFY(query.evaluateTo(&result));
2807 QStringList expected((QString()));
2808 QCOMPARE(result, expected);
2811 void tst_QXmlQuery::cleanupTestCase() const
2813 /* Remove a weird file we created. */
2814 const QString name(QLatin1String("nonReadableFile.xq"));
2816 if(QFile::exists(name))
2819 QVERIFY(file.setPermissions(QFile::WriteOwner));
2820 QVERIFY(file.remove());
2824 void tst_QXmlQuery::declareUnavailableExternal() const
2827 MessageSilencer silencer;
2828 query.setMessageHandler(&silencer);
2829 query.setQuery(QLatin1String("declare variable $var external;"
2831 /* We do not bind $var with QXmlQuery::bindVariable(). */
2832 QVERIFY(!query.isValid());
2836 This test triggers an assert in one of the cache iterator
2837 with MSVC 2005 when compiled in debug mode.
2839 void tst_QXmlQuery::msvcCacheIssue() const
2842 query.bindVariable(QLatin1String("externalVariable"), QXmlItem("Variable Value"));
2843 query.setQuery(QUrl::fromLocalFile(m_xmlPatternsDir + QLatin1String("/queries/") + QString::fromLatin1("externalVariableUsedTwice.xq")));
2845 QVERIFY(query.evaluateTo(&result));
2848 QStringList() << QString::fromLatin1("Variable Value") << QString::fromLatin1("Variable Value"));
2851 void tst_QXmlQuery::unavailableExternalVariable() const
2855 MessageSilencer silencer;
2856 query.setMessageHandler(&silencer);
2858 query.setQuery(QLatin1String("declare variable $foo external; 1"));
2860 QVERIFY(!query.isValid());
2864 Ensure that setUriResolver() affects \c fn:doc() and \c fn:doc-available().
2866 void tst_QXmlQuery::useUriResolver() const
2868 class TestUriResolver : public QAbstractUriResolver
2869 , private TestFundament
2872 TestUriResolver() {}
2873 virtual QUrl resolve(const QUrl &relative,
2874 const QUrl &baseURI) const
2877 QString fixedInputFile = inputFile(m_xmlPatternsDir + QLatin1String("/queries/") + QLatin1String("simpleDocument.xml"));
2879 // A file path with drive letter is not a valid relative URI, so remove the drive letter.
2880 // Note that can't just use inputFileAsURI() instead of inputFile() as that doesn't
2881 // produce a relative URI either.
2882 if (fixedInputFile.size() > 1 && fixedInputFile.at(1) == QLatin1Char(':'))
2883 fixedInputFile.remove(0, 2);
2885 return baseURI.resolved(fixedInputFile);
2889 const TestUriResolver uriResolver;
2892 query.setUriResolver(&uriResolver);
2893 query.setQuery(QLatin1String("let $i := 'http://www.example.com/DoesNotExist'"
2894 "return (string(doc($i)), doc-available($i))"));
2897 QXmlResultItems result;
2898 query.evaluateTo(&result);
2900 QVERIFY(!result.hasError());
2901 QCOMPARE(result.next().toAtomicValue().toString(), QString::fromLatin1("text text node"));
2902 QCOMPARE(result.next().toAtomicValue().toBool(), true);
2903 QVERIFY(result.next().isNull());
2904 QVERIFY(!result.hasError());
2907 void tst_QXmlQuery::queryWithFocusAndVariable() const
2910 query.setFocus(QXmlItem(5));
2911 query.bindVariable(QLatin1String("var"), QXmlItem(2));
2913 query.setQuery(QLatin1String("string(. * $var)"));
2917 QVERIFY(query.evaluateTo(&result));
2919 QCOMPARE(result, QStringList(QLatin1String("10")));
2922 void tst_QXmlQuery::undefinedFocus() const
2926 MessageSilencer silencer;
2927 query.setMessageHandler(&silencer);
2929 query.setQuery(QLatin1String("."));
2930 QVERIFY(!query.isValid());
2933 void tst_QXmlQuery::basicFocusUsage() const
2937 MessageSilencer silencer;
2938 query.setMessageHandler(&silencer);
2940 query.setFocus(QXmlItem(5));
2941 query.setQuery(QLatin1String("string(. * .)"));
2942 QVERIFY(query.isValid());
2945 QVERIFY(query.evaluateTo(&result));
2947 QCOMPARE(result, QStringList(QLatin1String("25")));
2951 Triggers an ownership related crash.
2953 void tst_QXmlQuery::copyCheckMessageHandler() const
2956 QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0));
2958 query.setQuery(QLatin1String("doc('qrc:/QXmlQueryTestData/data/oneElement.xml')"));
2959 /* By now, we should have set the builtin message handler. */
2960 const QAbstractMessageHandler *const messageHandler = query.messageHandler();
2961 QVERIFY(messageHandler);
2964 /* This copies QXmlQueryPrivate::m_ownerObject, and its destructor
2965 * will delete it, and hence the builtin message handler attached to it. */
2966 QXmlQuery copy(query);
2969 QXmlResultItems result;
2970 query.evaluateTo(&result);
2972 while(!result.next().isNull())
2975 QVERIFY(!result.hasError());
2978 void tst_QXmlQuery::queryLanguage() const
2980 /* Check default value. */
2982 const QXmlQuery query;
2983 QCOMPARE(query.queryLanguage(), QXmlQuery::XQuery10);
2986 /* Check default value of copies default instance. */
2988 const QXmlQuery query1;
2989 const QXmlQuery query2(query1);
2991 QCOMPARE(query1.queryLanguage(), QXmlQuery::XQuery10);
2992 QCOMPARE(query2.queryLanguage(), QXmlQuery::XQuery10);
2996 void tst_QXmlQuery::queryLanguageSignature() const
2998 /* This getter should be const. */
3000 query.queryLanguage();
3003 void tst_QXmlQuery::enumQueryLanguage() const
3005 /* These enum values should be possible to OR for future plans. */
3006 QCOMPARE(int(QXmlQuery::XQuery10), 1);
3007 QCOMPARE(int(QXmlQuery::XSLT20), 2);
3008 QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintSelector), 1024);
3009 QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintField), 2048);
3010 QCOMPARE(int(QXmlQuery::XPath20), 4096);
3013 void tst_QXmlQuery::setInitialTemplateNameQXmlName() const
3015 QXmlQuery query(QXmlQuery::XSLT20);
3016 QXmlNamePool np(query.namePool());
3017 const QXmlName name(np, QLatin1String("main"));
3019 query.setInitialTemplateName(name);
3021 QCOMPARE(query.initialTemplateName(), name);
3023 query.setQuery(QUrl(inputFileAsURI(m_xmlPatternsDir + QLatin1String("/stylesheets/namedTemplate.xsl"))));
3024 QVERIFY(query.isValid());
3027 QVERIFY(result.open(QIODevice::ReadWrite));
3028 QXmlSerializer serializer(query, &result);
3029 query.evaluateTo(&serializer);
3031 QCOMPARE(result.data(), QByteArray("1 2 3 4 5"));
3033 // TODO invoke a template which has required params.
3036 void tst_QXmlQuery::setInitialTemplateNameQXmlNameSignature() const
3039 QXmlNamePool np(query.namePool());
3040 const QXmlName name(np, QLatin1String("foo"));
3042 /* The signature should take a const reference. */
3043 query.setInitialTemplateName(name);
3046 void tst_QXmlQuery::setInitialTemplateNameQString() const
3049 QXmlNamePool np(query.namePool());
3050 query.setInitialTemplateName(QLatin1String("foo"));
3052 QCOMPARE(query.initialTemplateName(), QXmlName(np, QLatin1String("foo")));
3055 void tst_QXmlQuery::setInitialTemplateNameQStringSignature() const
3057 const QString name(QLatin1String("name"));
3060 /* We should take a const reference. */
3061 query.setInitialTemplateName(name);
3064 void tst_QXmlQuery::initialTemplateName() const
3066 /* Check our default value. */
3068 QCOMPARE(query.initialTemplateName(), QXmlName());
3069 QVERIFY(query.initialTemplateName().isNull());
3072 void tst_QXmlQuery::initialTemplateNameSignature() const
3074 const QXmlQuery query;
3075 /* This should be a const member. */
3076 query.initialTemplateName();
3079 void tst_QXmlQuery::setNetworkAccessManager() const
3082 /* Ensure fn:doc() picks up the right QNetworkAccessManager. */
3084 NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
3085 QUrl(inputFileAsURI(m_xmlPatternsDir + QLatin1String("/queries/simpleDocument.xml"))));
3086 QVERIFY(networkOverrider.isValid());
3089 query.setNetworkAccessManager(&networkOverrider);
3090 query.setQuery(QLatin1String("string(doc('tag:example.com:DOESNOTEXIST'))"));
3091 QVERIFY(query.isValid());
3094 QVERIFY(query.evaluateTo(&result));
3096 QCOMPARE(result, QStringList(QLatin1String("text text node")));
3099 /* Ensure setQuery() is using the right network manager. */
3101 NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
3102 QUrl(inputFileAsURI(m_xmlPatternsDir + QLatin1String("/queries/concat.xq"))));
3103 QVERIFY(networkOverrider.isValid());
3106 query.setNetworkAccessManager(&networkOverrider);
3107 query.setQuery(QUrl("tag:example.com:DOESNOTEXIST"));
3108 QVERIFY(query.isValid());
3111 QVERIFY(query.evaluateTo(&result));
3113 QCOMPARE(result, QStringList(QLatin1String("abcdef")));
3116 void tst_QXmlQuery::networkAccessManagerSignature() const
3119 const QXmlQuery query;
3121 /* The function should be const. */
3122 query.networkAccessManager();
3125 void tst_QXmlQuery::networkAccessManagerDefaultValue() const
3127 const QXmlQuery query;
3129 QCOMPARE(query.networkAccessManager(), static_cast<QNetworkAccessManager *>(0));
3132 void tst_QXmlQuery::networkAccessManager() const
3134 /* Test that we return the network manager that was set. */
3136 QNetworkAccessManager manager;
3138 query.setNetworkAccessManager(&manager);
3139 QCOMPARE(query.networkAccessManager(), &manager);
3147 1. Load a document into QXmlQuery's document cache, by executing a query which does it.
3149 3. Change query, to one which uses the focus
3154 void tst_QXmlQuery::multipleDocsAndFocus() const
3158 /* We use string concatenation, since variable bindings might disturb what
3160 query.setQuery(QLatin1String("string(doc('") +
3161 inputFile(m_xmlPatternsDir + QLatin1String("/queries/simpleDocument.xml")) +
3162 QLatin1String("'))"));
3163 query.setFocus(QUrl(inputFileAsURI(m_xmlPatternsDir + QLatin1String("/stylesheets/documentElement.xml"))));
3164 query.setQuery(QLatin1String("string(.)"));
3167 QVERIFY(query.evaluateTo(&result));
3182 void tst_QXmlQuery::multipleEvaluationsWithDifferentFocus() const
3187 query.setFocus(QUrl(inputFileAsURI(m_xmlPatternsDir + QLatin1String("/stylesheets/documentElement.xml"))));
3188 query.setQuery(QLatin1String("string(.)"));
3189 QVERIFY(query.evaluateTo(&result));
3191 query.setFocus(QUrl(inputFileAsURI(m_xmlPatternsDir + QLatin1String("/stylesheets/documentElement.xml"))));
3192 QVERIFY(query.evaluateTo(&result));
3195 void tst_QXmlQuery::bindVariableQXmlQuery() const
3197 QFETCH(QString, query1);
3198 QFETCH(QString, query2);
3199 QFETCH(QString, expectedOutput);
3200 QFETCH(bool, expectedSuccess);
3202 MessageSilencer silencer;
3203 QXmlQuery xmlQuery1;
3204 xmlQuery1.setMessageHandler(&silencer);
3205 xmlQuery1.setQuery(query1);
3207 QXmlQuery xmlQuery2(xmlQuery1);
3208 xmlQuery2.bindVariable("query1", xmlQuery1);
3209 xmlQuery2.setQuery(query2);
3212 const bool querySuccess = xmlQuery2.evaluateTo(&output);
3214 QCOMPARE(querySuccess, expectedSuccess);
3217 QCOMPARE(output, expectedOutput);
3220 void tst_QXmlQuery::bindVariableQXmlQuery_data() const
3222 QTest::addColumn<QString>("query1");
3223 QTest::addColumn<QString>("query2");
3224 QTest::addColumn<QString>("expectedOutput");
3225 QTest::addColumn<bool>("expectedSuccess");
3227 QTest::newRow("First query has one atomic value.")
3233 QTest::newRow("First query has two atomic values.")
3239 QTest::newRow("First query is a node.")
3245 /* This is a good test, because it triggers the exception in the
3246 * bindVariable() call, as supposed to when the actual evaluation is done.
3248 QTest::newRow("First query has a dynamic error.")
3251 << QString() /* We don't care. */
3255 void tst_QXmlQuery::bindVariableQStringQXmlQuerySignature() const
3258 query1.setQuery("'dummy'");
3261 const QString name(QLatin1String("name"));
3263 /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
3264 * QXmlQuery, and evaluation is what we do here. */
3265 query2.bindVariable(name, const_cast<const QXmlQuery &>(query1));
3268 void tst_QXmlQuery::bindVariableQXmlNameQXmlQuerySignature() const
3271 QXmlQuery query1(np);
3272 query1.setQuery("'dummy'");
3275 const QXmlName name(np, QLatin1String("name"));
3277 /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
3278 * QXmlQuery, and evaluation is what we do here. */
3279 query2.bindVariable(name, const_cast<const QXmlQuery &>(query1));
3283 Check that the QXmlName is handled correctly.
3285 void tst_QXmlQuery::bindVariableQXmlNameQXmlQuery() const
3289 query1.setQuery(QLatin1String("1"));
3291 QXmlQuery query2(np);
3292 query2.bindVariable(QXmlName(np, QLatin1String("theName")), query1);
3293 query2.setQuery("$theName");
3296 query2.evaluateTo(&result);
3298 QCOMPARE(result, QString::fromLatin1("1\n"));
3301 void tst_QXmlQuery::bindVariableQXmlQueryInvalidate() const
3304 query.bindVariable(QLatin1String("name"), QVariant(1));
3305 query.setQuery("$name");
3306 QVERIFY(query.isValid());
3309 query2.setQuery("'query2'");
3311 query.bindVariable(QLatin1String("name"), query);
3312 QVERIFY(!query.isValid());
3315 void tst_QXmlQuery::unknownSourceLocation() const
3318 b.setData("<a><b/><b/></a>");
3319 b.open(QIODevice::ReadOnly);
3321 MessageSilencer silencer;
3323 query.bindVariable(QLatin1String("inputDocument"), &b);
3324 query.setMessageHandler(&silencer);
3326 query.setQuery(QLatin1String("doc($inputDocument)/a/(let $v := b/string() return if ($v) then $v else ())"));
3329 query.evaluateTo(&output);
3332 void tst_QXmlQuery::identityConstraintSuccess() const
3334 QXmlQuery::QueryLanguage queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintSelector;
3336 /* We run this code for Selector and Field. */
3337 for(int i = 0; i < 3; ++i)
3339 QXmlNamePool namePool;
3340 QXmlResultItems result;
3344 QXmlQuery nodeSource(namePool);
3345 nodeSource.setQuery(QLatin1String("<e/>"));
3347 nodeSource.evaluateTo(&result);
3348 node = result.next();
3352 * 1. The focus is undefined, but it's still valid.
3353 * 2. We never evaluate. */
3355 QXmlQuery query(queryLanguage);
3356 query.setQuery(QLatin1String("a"));
3357 QVERIFY(query.isValid());
3361 * 1. The focus is undefined, but it's still valid.
3362 * 2. We afterwards set the focus. */
3364 QXmlQuery query(queryLanguage, namePool);
3365 query.setQuery(QLatin1String("a"));
3366 query.setFocus(node);
3367 QVERIFY(query.isValid());
3371 * 1. The focus is undefined, but it's still valid.
3372 * 2. We afterwards set the focus.
3373 * 3. We evaluate. */
3375 QXmlQuery query(queryLanguage, namePool);
3376 query.setQuery(QString(QLatin1Char('.')));
3377 query.setFocus(node);
3378 QVERIFY(query.isValid());
3381 QVERIFY(query.evaluateTo(&result));
3382 QCOMPARE(result, QString::fromLatin1("<e/>\n"));
3385 /* A slightly more complex Field. */
3387 QXmlQuery query(queryLanguage);
3388 query.setQuery(QLatin1String("* | .//xml:*/."));
3389 QVERIFY(query.isValid());
3392 /* @ is only allowed in Field. */
3393 if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField)
3395 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField);
3396 query.setQuery(QLatin1String("@abc"));
3397 QVERIFY(query.isValid());
3400 /* Field allows attribute:: and child:: .*/
3401 if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField)
3403 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField);
3404 query.setQuery(QLatin1String("attribute::name | child::name"));
3405 QVERIFY(query.isValid());
3408 /* Selector allows only child:: .*/
3410 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintSelector);
3411 query.setQuery(QLatin1String("child::name"));
3412 QVERIFY(query.isValid());
3416 queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintField;
3418 queryLanguage = QXmlQuery::XPath20;
3422 Q_DECLARE_METATYPE(QXmlQuery::QueryLanguage);
3425 We just do some basic tests for boot strapping and sanity checking. The actual regression
3426 testing is in the Schema suite.
3428 void tst_QXmlQuery::identityConstraintFailure() const
3430 QFETCH(QXmlQuery::QueryLanguage, queryLanguage);
3431 QFETCH(QString, inputQuery);
3433 QXmlQuery query(queryLanguage);
3434 MessageSilencer silencer;
3435 query.setMessageHandler(&silencer);
3437 query.setQuery(inputQuery);
3438 QVERIFY(!query.isValid());
3441 void tst_QXmlQuery::identityConstraintFailure_data() const
3443 QTest::addColumn<QXmlQuery::QueryLanguage>("queryLanguage");
3444 QTest::addColumn<QString>("inputQuery");
3446 QTest::newRow("We don't have element constructors in identity constraint pattern, "
3447 "it's an XQuery feature(Selector).")
3448 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3449 << QString::fromLatin1("<e/>");
3451 QTest::newRow("We don't have functions in identity constraint pattern, "
3452 "it's an XPath feature(Selector).")
3453 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3454 << QString::fromLatin1("current-time()");
3456 QTest::newRow("We don't have element constructors in identity constraint pattern, "
3457 "it's an XQuery feature(Field).")
3458 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3459 << QString::fromLatin1("<e/>");
3461 QTest::newRow("We don't have functions in identity constraint pattern, "
3462 "it's an XPath feature(Field).")
3463 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3464 << QString::fromLatin1("current-time()");
3466 QTest::newRow("@attributeName is disallowed for the selector.")
3467 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3468 << QString::fromLatin1("@abc");
3470 QTest::newRow("attribute:: is disallowed for the selector.")
3471 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3472 << QString::fromLatin1("attribute::name");
3474 QTest::newRow("ancestor::name is disallowed for the selector.")
3475 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3476 << QString::fromLatin1("ancestor::name");
3478 QTest::newRow("ancestor::name is disallowed for the field.")
3479 << QXmlQuery::XmlSchema11IdentityConstraintField
3480 << QString::fromLatin1("ancestor::name");
3483 QTEST_MAIN(tst_QXmlQuery)
3485 #include "tst_qxmlquery.moc"