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"
66 #define XMLPATTERNSDIR SRCDIR "../xmlpatterns"
72 \brief Tests class QXmlQuery.
74 This test is not intended for testing the engine, but the functionality specific
75 to the QXmlQuery class.
77 In other words, if you have an engine bug; don't add it here because it won't be
78 tested properly. Instead add it to the test suite.
81 class tst_QXmlQuery : public QObject
82 , private TestFundament
87 inline tst_QXmlQuery() : m_generatedBaselines(0)
95 void defaultConstructor() const;
96 void copyConstructor() const;
97 void constructorQXmlNamePool() const;
98 void constructorQXmlNamePoolQueryLanguage() const;
99 void constructorQXmlNamePoolWithinQSimpleXmlNodeModel() const;
100 void assignmentOperator() const;
101 void isValid() const;
102 void sequentialExecution() const;
103 void bindVariableQString() const;
104 void bindVariableQStringNoExternalDeclaration() const;
105 void bindVariableQXmlName() const;
106 void bindVariableQXmlNameTriggerWarnings() const;
107 void bindVariableQStringQIODevice() const;
108 void bindVariableQStringQIODeviceWithByteArray() const;
109 void bindVariableQStringQIODeviceWithString() const;
110 void bindVariableQStringQIODeviceWithQFile() const;
111 void bindVariableQXmlNameQIODevice() const;
112 void bindVariableQXmlNameQIODeviceTriggerWarnings() const;
113 void bindVariableXSLTSuccess() const;
114 void bindVariableTemporaryNode() const;
115 void setMessageHandler() const;
116 void messageHandler() const;
117 void evaluateToQAbstractXmlReceiverTriggerWarnings() const;
118 void evaluateToQXmlResultItems() const;
119 void evaluateToQXmlResultItemsTriggerWarnings() const;
120 void evaluateToQXmlResultItemsErrorAtEnd() const;
121 void evaluateToReceiver();
122 void evaluateToReceiver_data() const;
123 void evaluateToReceiverOnInvalidQuery() const;
124 void evaluateToQStringTriggerError() const;
125 void evaluateToQString() const;
126 void evaluateToQString_data() const;
127 void evaluateToQStringSignature() const;
128 void checkGeneratedBaselines() const;
129 void basicXQueryToQtTypeCheck() const;
130 void basicQtToXQueryTypeCheck() const;
131 void bindNode() const;
132 void relativeBaseURI() const;
133 void emptyBaseURI() const;
134 void roundTripDateWithinQXmlItem() const;
135 void bindingMissing() const;
136 void bindDefaultConstructedItem() const;
137 void bindDefaultConstructedItem_data() const;
138 void bindEmptyNullString() const;
139 void bindEmptyString() const;
140 void rebindVariableSameType() const;
141 void rebindVariableDifferentType() const;
142 void rebindVariableWithNullItem() const;
143 void eraseQXmlItemBinding() const;
144 void eraseDeviceBinding() const;
145 void constCorrectness() const;
146 void objectSize() const;
147 void setUriResolver() const;
148 void uriResolver() const;
149 void messageXML() const;
150 void resultItemsDeallocatedQuery() const;
151 void copyCheckMessageHandler() const;
152 void shadowedVariables() const;
153 void setFocusQXmlItem() const;
154 void setFocusQUrl() const;
155 void setFocusQIODevice() const;
156 void setFocusQIODeviceAvoidVariableClash() const;
157 void setFocusQIODeviceFailure() const;
158 void setFocusQIODeviceTriggerWarnings() const;
159 void setFocusQString() const;
160 void setFocusQStringFailure() const;
161 void setFocusQStringSignature() const;
162 void recompilationWithEvaluateToResultFailing() const;
163 void secondEvaluationWithEvaluateToResultFailing() const;
164 void recompilationWithEvaluateToReceiver() const;
165 void fnDocOnQIODeviceTimeout() const;
166 void evaluateToQStringListOnInvalidQuery() const;
167 void evaluateToQStringList() const;
168 void evaluateToQStringListTriggerWarnings() const;
169 void evaluateToQStringList_data() const;
170 void evaluateToQStringListNoConversion() const;
171 void evaluateToQIODevice() const;
172 void evaluateToQIODeviceTriggerWarnings() const;
173 void evaluateToQIODeviceSignature() const;
174 void evaluateToQIODeviceOnInvalidQuery() const;
175 void setQueryQIODeviceQUrl() const;
176 void setQueryQIODeviceQUrlTriggerWarnings() const;
177 void setQueryQString() const;
178 void setQueryQUrlSuccess() const;
179 void setQueryQUrlSuccess_data() const;
180 void setQueryQUrlFailSucceed() const;
181 void setQueryQUrlFailure() const;
182 void setQueryQUrlFailure_data() const;
183 void setQueryQUrlBaseURI() const;
184 void setQueryQUrlBaseURI_data() const;
185 void setQueryWithNonExistentQUrlOnValidQuery() const;
186 void setQueryWithInvalidQueryFromQUrlOnValidQuery() const;
187 void retrieveNameFromQuery() const;
188 void retrieveNameFromQuery_data() const;
189 void cleanupTestCase() const;
190 void declareUnavailableExternal() const;
191 void msvcCacheIssue() const;
192 void unavailableExternalVariable() const;
193 void useUriResolver() const;
194 void queryWithFocusAndVariable() const;
195 void undefinedFocus() const;
196 void basicFocusUsage() const;
198 void queryLanguage() const;
199 void queryLanguageSignature() const;
200 void enumQueryLanguage() const;
202 void setNetworkAccessManager() const;
203 void networkAccessManagerSignature() const;
204 void networkAccessManagerDefaultValue() const;
205 void networkAccessManager() const;
207 void setInitialTemplateNameQXmlName() const;
208 void setInitialTemplateNameQXmlNameSignature() const;
209 void setInitialTemplateNameQString() const;
210 void setInitialTemplateNameQStringSignature() const;
211 void initialTemplateName() const;
212 void initialTemplateNameSignature() const;
214 void fnDocNetworkAccessSuccess() const;
215 void fnDocNetworkAccessSuccess_data() const;
216 void fnDocNetworkAccessFailure() const;
217 void fnDocNetworkAccessFailure_data() const;
218 void multipleDocsAndFocus() const;
219 void multipleEvaluationsWithDifferentFocus() const;
220 void bindVariableQXmlQuery() const;
221 void bindVariableQXmlQuery_data() const;
222 void bindVariableQStringQXmlQuerySignature() const;
223 void bindVariableQXmlNameQXmlQuerySignature() const;
224 void bindVariableQXmlNameQXmlQuery() const;
225 void bindVariableQXmlQueryInvalidate() const;
226 void unknownSourceLocation() const;
228 void identityConstraintSuccess() const;
229 void identityConstraintFailure() const;
230 void identityConstraintFailure_data() const;
232 // 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.
233 // TODO bind stringlists, variant lists, both ways.
234 // TODO trigger serialization error, or any error in evaluateToushCallback().
235 // TODO let items travle between two queries, as seen in the SDK
236 // TODO what happens if the query declares local variable and external ones are provided?
242 * One excluded, since we skip static-base-uri.xq.
244 ExpectedQueryCount = 29
247 static void checkBaseURI(const QUrl &baseURI, const QString &candidate);
248 static QStringList queries();
249 static const char *const queriesDirectory;
251 int m_generatedBaselines;
252 int m_pushTestsCount;
253 const bool m_testNetwork;
256 void tst_QXmlQuery::initTestCase()
258 QVERIFY(QtNetworkSettings::verifyTestNetworkSettings());
261 void tst_QXmlQuery::checkBaseURI(const QUrl &baseURI, const QString &candidate)
263 /* The use of QFileInfo::canonicalFilePath() takes into account that drive letters
264 * on Windows may have different cases. */
265 QVERIFY(QDir(baseURI.toLocalFile()).relativeFilePath(QFileInfo(candidate).canonicalFilePath()).startsWith("../"));
268 const char *const tst_QXmlQuery::queriesDirectory = XMLPATTERNSDIR "/queries/";
270 QStringList tst_QXmlQuery::queries()
273 dir.cd(inputFile(QLatin1String(queriesDirectory)));
275 return dir.entryList(QStringList(QLatin1String("*.xq")));
278 void tst_QXmlQuery::defaultConstructor() const
280 /* Allocate instance in different orders. */
297 void tst_QXmlQuery::copyConstructor() const
299 /* Verify that we can take a const reference, and simply do a copy of a default constructed object. */
301 const QXmlQuery query1;
302 QXmlQuery query2(query1);
307 const QXmlQuery query1;
308 QXmlQuery query2(query1);
309 QXmlQuery query3(query2);
312 /* Verify that copying default values works. */
314 const QXmlQuery query1;
315 const QXmlQuery query2(query1);
316 QCOMPARE(query2.messageHandler(), query1.messageHandler());
317 QCOMPARE(query2.uriResolver(), query1.uriResolver());
318 QCOMPARE(query2.queryLanguage(), query1.queryLanguage());
319 QCOMPARE(query2.initialTemplateName(), query1.initialTemplateName());
320 QCOMPARE(query2.networkAccessManager(), query1.networkAccessManager());
329 * - initial template name
331 * sticks with the copy. */
333 MessageSilencer silencer;
334 TestURIResolver resolver;
335 QNetworkAccessManager networkManager;
336 QXmlQuery query1(QXmlQuery::XSLT20);
337 QXmlNamePool np1(query1.namePool());
339 query1.setMessageHandler(&silencer);
340 query1.setUriResolver(&resolver);
341 query1.setNetworkAccessManager(&networkManager);
343 const QXmlName name(np1, QLatin1String("localName"),
344 QLatin1String("http://example.com/"),
345 QLatin1String("prefix"));
346 query1.setInitialTemplateName(name);
348 const QXmlQuery query2(query1);
349 QCOMPARE(query2.messageHandler(), static_cast<QAbstractMessageHandler *>(&silencer));
350 QCOMPARE(query2.uriResolver(), static_cast<const QAbstractUriResolver *>(&resolver));
351 QCOMPARE(query2.queryLanguage(), QXmlQuery::XSLT20);
352 QCOMPARE(query2.initialTemplateName(), name);
353 QCOMPARE(query2.networkAccessManager(), &networkManager);
355 QXmlNamePool np2(query2.namePool());
357 QCOMPARE(name.namespaceUri(np2), QString::fromLatin1("http://example.com/"));
358 QCOMPARE(name.localName(np2), QString::fromLatin1("localName"));
359 QCOMPARE(name.prefix(np2), QString::fromLatin1("prefix"));
365 original.setFocus(QXmlItem(4));
366 original.setQuery(QLatin1String("."));
367 QVERIFY(original.isValid());
369 const QXmlQuery copy(original);
371 QXmlResultItems result;
372 copy.evaluateTo(&result);
373 QCOMPARE(result.next().toAtomicValue(), QVariant(4));
374 QVERIFY(result.next().isNull());
375 QVERIFY(!result.hasError());
378 /* Copy, set, compare. Check that copies are independent. */
380 // TODO all members except queryLanguage().
384 void tst_QXmlQuery::constructorQXmlNamePool() const
386 /* Check that the namepool we are passed, is actually used. */
390 const QXmlName name(np, QLatin1String("localName"),
391 QLatin1String("http://example.com/"),
392 QLatin1String("prefix"));
394 QXmlNamePool np2(query.namePool());
395 QCOMPARE(name.namespaceUri(np2), QString::fromLatin1("http://example.com/"));
396 QCOMPARE(name.localName(np2), QString::fromLatin1("localName"));
397 QCOMPARE(name.prefix(np2), QString::fromLatin1("prefix"));
401 Ensure that the internal variable loading mechanisms uses the user-supplied
404 If that is not the case, different name pools are used and the code crashes.
408 void tst_QXmlQuery::constructorQXmlNamePoolQueryLanguage() const
411 QXmlName name(np, QLatin1String("arbitraryName"));
413 QXmlQuery query(QXmlQuery::XQuery10, np);
416 input.setData("<yall/>");
418 QVERIFY(input.open(QIODevice::ReadOnly));
419 query.bindVariable(name, &input);
420 query.setQuery("string(doc($arbitraryName))");
423 query.evaluateTo(&result);
426 void tst_QXmlQuery::constructorQXmlNamePoolWithinQSimpleXmlNodeModel() const
428 class TestCTOR : public TestSimpleNodeModel
431 TestCTOR(const QXmlNamePool &np) : TestSimpleNodeModel(np)
435 void checkCTOR() const
437 /* If this fails to compile, the constructor has trouble with taking
438 * a mutable reference.
440 * The reason we use the this pointer explicitly, is to avoid a compiler
441 * warnings with MSVC 2005. */
442 QXmlQuery(this->namePool());
451 void tst_QXmlQuery::assignmentOperator() const
453 class ReturnURI : public QAbstractUriResolver
457 virtual QUrl resolve(const QUrl &relative,
458 const QUrl &baseURI) const
460 return baseURI.resolved(relative);
464 /* Assign this to this. */
471 /* Just call a couple of functions to give valgrind
472 * something to check. */
473 QVERIFY(!query.isValid());
474 query.messageHandler();
477 /* Assign null instances a couple of times. */
485 /* Just call a couple of functions to give valgrind
486 * something to check. */
487 QVERIFY(!query1.isValid());
488 query1.messageHandler();
490 /* Just call a couple of functions to give valgrind
491 * something to check. */
492 QVERIFY(!query2.isValid());
493 query2.messageHandler();
496 /* Create a query, set all the things it stores, and ensure it
497 * travels over to the new instance. */
499 MessageSilencer silencer;
500 const ReturnURI returnURI;
501 QXmlNamePool namePool;
503 QBuffer documentDevice;
504 documentDevice.setData(QByteArray("<e>a</e>"));
505 QVERIFY(documentDevice.open(QIODevice::ReadOnly));
507 QXmlQuery original(namePool);
508 QXmlName testName(namePool, QLatin1String("somethingToCheck"));
510 original.setMessageHandler(&silencer);
511 original.bindVariable(QLatin1String("var"), QXmlItem(1));
512 original.bindVariable(QLatin1String("device"), &documentDevice);
513 original.setUriResolver(&returnURI);
514 original.setFocus(QXmlItem(3));
515 original.setQuery(QLatin1String("$var, 1 + 1, ., string(doc($device))"));
517 /* Do a copy, and check that everything followed on into the copy. No modification
522 /* We use assignment operator, not copy constructor. */
525 QVERIFY(copy.isValid());
526 QCOMPARE(copy.uriResolver(), static_cast<const QAbstractUriResolver *>(&returnURI));
527 QCOMPARE(copy.messageHandler(), static_cast<QAbstractMessageHandler *>(&silencer));
528 QCOMPARE(testName.localName(copy.namePool()), QString::fromLatin1("somethingToCheck"));
530 QXmlResultItems result;
531 copy.evaluateTo(&result);
532 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
533 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
534 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
535 QCOMPARE(result.next().toAtomicValue(), QVariant(QString::fromLatin1("a")));
536 QVERIFY(result.next().isNull());
537 QVERIFY(!result.hasError());
540 /* Copy, and change values. Things should detach. */
542 /* Evaluate the copy. */
544 MessageSilencer secondSilencer;
545 const ReturnURI secondUriResolver;
546 QBuffer documentDeviceCopy;
547 documentDeviceCopy.setData(QByteArray("<e>b</e>"));
548 QVERIFY(documentDeviceCopy.open(QIODevice::ReadOnly));
553 copy.setMessageHandler(&secondSilencer);
554 /* Here we rebind variable values. */
555 copy.bindVariable(QLatin1String("var"), QXmlItem(4));
556 copy.bindVariable(QLatin1String("device"), &documentDeviceCopy);
557 copy.setUriResolver(&secondUriResolver);
558 copy.setFocus(QXmlItem(6));
559 copy.setQuery(QLatin1String("$var, 1 + 1, ., string(doc($device))"));
561 /* Check that the copy picked up the new things. */
562 QVERIFY(copy.isValid());
563 QCOMPARE(copy.uriResolver(), static_cast<const QAbstractUriResolver *>(&secondUriResolver));
564 QCOMPARE(copy.messageHandler(), static_cast<QAbstractMessageHandler *>(&secondSilencer));
566 QXmlResultItems resultCopy;
567 copy.evaluateTo(&resultCopy);
568 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(4));
569 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(2));
570 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(6));
571 const QString stringedDevice(resultCopy.next().toAtomicValue().toString());
572 QCOMPARE(stringedDevice, QString::fromLatin1("b"));
573 QVERIFY(resultCopy.next().isNull());
574 QVERIFY(!resultCopy.hasError());
577 /* Evaluate the original. */
579 /* Check that the original is unchanged. */
580 QVERIFY(original.isValid());
581 QCOMPARE(original.uriResolver(), static_cast<const QAbstractUriResolver *>(&returnURI));
582 QCOMPARE(original.messageHandler(), static_cast<QAbstractMessageHandler *>(&silencer));
584 QXmlResultItems resultOriginal;
585 original.evaluateTo(&resultOriginal);
586 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(1));
587 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(2));
588 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(3));
589 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(QString::fromLatin1("a")));
590 QVERIFY(resultOriginal.next().isNull());
591 QVERIFY(!resultOriginal.hasError());
598 Since QXmlQuery doesn't seek devices to position 0, this code triggers a bug
599 where document caching doesn't work. Since the document caching doesn't work,
600 the device will be read twice, and the second time the device is at the end,
601 hence premature end of document.
603 void tst_QXmlQuery::sequentialExecution() const
606 inBuffer.setData(QByteArray("<input/>"));
607 QVERIFY(inBuffer.open(QIODevice::ReadOnly));
610 query.bindVariable("inputDocument", &inBuffer);
613 QBuffer outBuffer(&outArray);
614 outBuffer.open(QIODevice::WriteOnly);
616 const QString queryString(QLatin1String("doc($inputDocument)"));
617 query.setQuery(queryString);
619 QXmlFormatter formatter(query, &outBuffer);
621 QVERIFY(query.evaluateTo(&formatter));
623 /* If this line is removed, the bug isn't triggered. */
624 query.setQuery(queryString);
626 QVERIFY(query.evaluateTo(&formatter));
629 void tst_QXmlQuery::isValid() const
631 /* Check default value. */
633 QVERIFY(!query.isValid());
636 void tst_QXmlQuery::bindVariableQString() const
640 /* Bind with a null QXmlItem. */
641 query.bindVariable(QLatin1String("name"), QXmlItem());
646 /* Bind with a null QVariant. */
647 query.bindVariable(QLatin1String("name"), QXmlItem(QVariant()));
652 /* Bind with a null QXmlNodeModelIndex. */
653 query.bindVariable(QLatin1String("name"), QXmlItem(QXmlNodeModelIndex()));
657 void tst_QXmlQuery::bindVariableQStringNoExternalDeclaration() const
660 query.bindVariable(QLatin1String("foo"), QXmlItem(QLatin1String("Variable Value")));
661 query.setQuery(QLatin1String("$foo"));
663 QVERIFY(query.isValid());
666 QVERIFY(query.evaluateTo(&result));
668 QCOMPARE(result, QStringList() << QLatin1String("Variable Value"));
671 void tst_QXmlQuery::bindVariableQXmlName() const
676 void tst_QXmlQuery::bindVariableQXmlNameTriggerWarnings() const
680 QTest::ignoreMessage(QtWarningMsg, "The variable name cannot be null.");
681 query.bindVariable(QXmlName(), QVariant());
684 void tst_QXmlQuery::bindVariableQStringQIODeviceWithByteArray() const
688 QByteArray in("<e/>");
690 QVERIFY(device.open(QIODevice::ReadOnly));
692 query.bindVariable("doc", &device);
694 query.setQuery(QLatin1String("declare variable $doc external; $doc"));
696 QVERIFY(query.isValid());
698 /* Check the URI corresponding to the variable. */
700 QXmlResultItems items;
701 query.evaluateTo(&items);
703 QCOMPARE(items.next().toAtomicValue().toString(), QString::fromLatin1("tag:trolltech.com,2007:QtXmlPatterns:QIODeviceVariable:doc"));
706 /* Now, actually load the document. We use the same QXmlQuery just to stress recompilation a bit. */
708 query.setQuery(QLatin1String("declare variable $doc external; doc($doc)"));
711 QBuffer outBuffer(&out);
712 QVERIFY(outBuffer.open(QIODevice::WriteOnly));
714 QXmlSerializer serializer(query, &outBuffer);
716 QVERIFY(query.evaluateTo(&serializer));
721 void tst_QXmlQuery::bindVariableQStringQIODeviceWithString() const
725 QString in("<qstring/>");
726 QByteArray inUtf8(in.toUtf8());
727 QBuffer inDevice(&inUtf8);
729 QVERIFY(inDevice.open(QIODevice::ReadOnly));
731 query.bindVariable("doc", &inDevice);
733 query.setQuery(QLatin1String("declare variable $doc external; doc($doc)"));
736 QBuffer outBuffer(&out);
737 QVERIFY(outBuffer.open(QIODevice::WriteOnly));
739 QXmlSerializer serializer(query, &outBuffer);
740 QVERIFY(query.evaluateTo(&serializer));
742 QCOMPARE(out, inUtf8);
745 void tst_QXmlQuery::bindVariableQStringQIODeviceWithQFile() const
748 QFile inDevice(QLatin1String(SRCDIR "input.xml"));
750 QVERIFY(inDevice.open(QIODevice::ReadOnly));
752 query.bindVariable("doc", &inDevice);
754 query.setQuery(QLatin1String("declare variable $doc external; doc($doc)"));
757 QBuffer outBuffer(&out);
758 QVERIFY(outBuffer.open(QIODevice::WriteOnly));
760 QXmlSerializer serializer(query, &outBuffer);
761 QVERIFY(query.evaluateTo(&serializer));
764 QCOMPARE(out, QByteArray("<!-- This is just a file for testing. --><input/>"));
767 void tst_QXmlQuery::bindVariableQStringQIODevice() const
771 /* Rebind the variable. */
773 /* First evaluation. */
775 QByteArray in1("<e1/>");
776 QBuffer inDevice1(&in1);
777 QVERIFY(inDevice1.open(QIODevice::ReadOnly));
779 query.bindVariable("in", &inDevice1);
780 query.setQuery(QLatin1String("doc($in)"));
783 QBuffer outDevice1(&out1);
784 QVERIFY(outDevice1.open(QIODevice::WriteOnly));
786 QXmlSerializer serializer(query, &outDevice1);
787 query.evaluateTo(&serializer);
791 /* Second evaluation, rebind variable. */
793 QByteArray in2("<e2/>");
794 QBuffer inDevice2(&in2);
795 QVERIFY(inDevice2.open(QIODevice::ReadOnly));
797 query.bindVariable(QLatin1String("in"), &inDevice2);
800 QBuffer outDevice2(&out2);
801 QVERIFY(outDevice2.open(QIODevice::WriteOnly));
803 QXmlSerializer serializer(query, &outDevice2);
804 QVERIFY(query.evaluateTo(&serializer));
809 // TODO trigger recompilation when setting qiodevices., and qiodevice overwritten by other type, etc.
812 void tst_QXmlQuery::bindVariableQXmlNameQIODevice() const
817 void tst_QXmlQuery::bindVariableQXmlNameQIODeviceTriggerWarnings() const
823 QTest::ignoreMessage(QtWarningMsg, "A null, or readable QIODevice must be passed.");
824 query.bindVariable(QXmlName(np, QLatin1String("foo")), &buffer);
826 QTest::ignoreMessage(QtWarningMsg, "The variable name cannot be null.");
827 query.bindVariable(QXmlName(), 0);
830 void tst_QXmlQuery::bindVariableXSLTSuccess() const
832 QXmlQuery stylesheet(QXmlQuery::XSLT20);
833 stylesheet.setInitialTemplateName(QLatin1String("main"));
835 stylesheet.bindVariable(QLatin1String("variableNoSelectNoBodyBoundWithBindVariable"),
836 QVariant(QLatin1String("MUST NOT SHOW 1")));
838 stylesheet.bindVariable(QLatin1String("variableSelectBoundWithBindVariable"),
839 QVariant(QLatin1String("MUST NOT SHOW 2")));
841 stylesheet.bindVariable(QLatin1String("variableSelectWithTypeIntBoundWithBindVariable"),
842 QVariant(QLatin1String("MUST NOT SHOW 3")));
844 stylesheet.bindVariable(QLatin1String("paramNoSelectNoBodyBoundWithBindVariable"),
845 QVariant(QLatin1String("param1")));
847 stylesheet.bindVariable(QLatin1String("paramNoSelectNoBodyBoundWithBindVariableRequired"),
848 QVariant(QLatin1String("param1")));
850 stylesheet.bindVariable(QLatin1String("paramSelectBoundWithBindVariable"),
851 QVariant(QLatin1String("param2")));
853 stylesheet.bindVariable(QLatin1String("paramSelectBoundWithBindVariableRequired"),
854 QVariant(QLatin1String("param3")));
856 stylesheet.bindVariable(QLatin1String("paramSelectWithTypeIntBoundWithBindVariable"),
859 stylesheet.bindVariable(QLatin1String("paramSelectWithTypeIntBoundWithBindVariableRequired"),
860 QVariant(QLatin1String("param5")));
862 stylesheet.setQuery(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/parameters.xsl"))));
864 QVERIFY(stylesheet.isValid());
867 QVERIFY(deviceOut.open(QIODevice::ReadWrite));
869 QVERIFY(stylesheet.evaluateTo(&deviceOut));
871 const QString result(QString::fromUtf8(deviceOut.data().constData()));
874 QString::fromLatin1("Variables: variableSelectsDefaultValue variableSelectsDefaultValue2 3 4 "
875 "Parameters: param1 param1 param2 param3 4 param5"));
878 void tst_QXmlQuery::bindVariableTemporaryNode() const
880 /* First we do it with QXmlResultItems staying in scope. */;
883 query1.setQuery("<anElement/>");
885 QXmlResultItems result1;
886 query1.evaluateTo(&result1);
888 QXmlQuery query2(query1);
889 query2.bindVariable("fromQuery1", result1.next());
890 query2.setQuery("$fromQuery1");
893 QVERIFY(query2.evaluateTo(&output));
895 QCOMPARE(output, QString::fromLatin1("<anElement/>\n"));
898 /* And now with it deallocating, so its internal DynamicContext pointer is
899 * released. This doesn't work in Qt 4.5 and is ok. */
902 query1.setQuery("<anElement/>");
907 QXmlResultItems result1;
908 query1.evaluateTo(&result1);
910 query2.bindVariable("fromQuery1", result1.next());
911 query2.setQuery("$fromQuery1");
915 return; // See comment above.
916 QVERIFY(query2.evaluateTo(&output));
918 QCOMPARE(output, QString::fromLatin1("<anElement/>\n"));
922 void tst_QXmlQuery::messageHandler() const
925 /* Check default value. */
927 QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0));
931 void tst_QXmlQuery::setMessageHandler() const
934 MessageSilencer silencer;
935 query.setMessageHandler(&silencer);
936 QCOMPARE(static_cast<QAbstractMessageHandler *>(&silencer), query.messageHandler());
939 void tst_QXmlQuery::evaluateToReceiver()
941 QFETCH(QString, inputQuery);
943 /* This query prints a URI specific to the local system. */
944 if(inputQuery == QLatin1String("static-base-uri.xq"))
948 const QString queryURI(inputFile(QLatin1String(queriesDirectory) + inputQuery));
949 QFile queryFile(queryURI);
951 QVERIFY(queryFile.exists());
952 QVERIFY(queryFile.open(QIODevice::ReadOnly));
956 MessageSilencer receiver;
957 query.setMessageHandler(&receiver);
958 query.setQuery(&queryFile, QUrl::fromLocalFile(queryURI));
960 /* We read all the queries, and some of them are invalid. However, we
961 * only want those that compile. */
966 QTextStream stream(&produced, QIODevice::WriteOnly);
967 PushBaseliner push(stream, query.namePool());
968 QVERIFY(push.isValid());
969 query.evaluateTo(&push);
971 const QString baselineName(inputFile(QLatin1String(SRCDIR "pushBaselines/") + inputQuery.left(inputQuery.length() - 2) + QString::fromLatin1("ref")));
972 QFile baseline(baselineName);
974 if(baseline.exists())
976 QVERIFY(baseline.open(QIODevice::ReadOnly | QIODevice::Text));
977 const QString stringedBaseline(QString::fromUtf8(baseline.readAll()));
978 QCOMPARE(produced, stringedBaseline);
982 QVERIFY(baseline.open(QIODevice::WriteOnly));
983 /* This is intentionally a warning, don't remove it. Update the baselines instead. */
984 qWarning() << "Generated baseline for:" << baselineName;
985 ++m_generatedBaselines;
987 baseline.write(produced.toUtf8());
991 void tst_QXmlQuery::evaluateToReceiver_data() const
993 QTest::addColumn<QString>("inputQuery");
995 foreach (QString const& query, queries())
997 /* This outputs a URI specific to the environment, so we can't use it for this
998 * particular test. */
999 if (query != QLatin1String("staticBaseURI.xq"))
1000 QTest::newRow(query.toUtf8().constData()) << query;
1004 void tst_QXmlQuery::evaluateToReceiverOnInvalidQuery() const
1006 /* Invoke on a default constructed object. */
1009 QBuffer buffer(&out);
1010 buffer.open(QIODevice::WriteOnly);
1013 QXmlSerializer serializer(query, &buffer);
1014 QVERIFY(!query.evaluateTo(&serializer));
1017 /* Invoke on an invalid query; compile time error. */
1020 QBuffer buffer(&out);
1021 buffer.open(QIODevice::WriteOnly);
1022 MessageSilencer silencer;
1025 query.setMessageHandler(&silencer);
1026 query.setQuery(QLatin1String("1 + "));
1027 QXmlSerializer serializer(query, &buffer);
1028 QVERIFY(!query.evaluateTo(&serializer));
1031 /* Invoke on an invalid query; runtime error. */
1034 QBuffer buffer(&out);
1035 buffer.open(QIODevice::WriteOnly);
1036 MessageSilencer silencer;
1039 query.setMessageHandler(&silencer);
1040 query.setQuery(QLatin1String("error()"));
1041 QXmlSerializer serializer(query, &buffer);
1042 QVERIFY(!query.evaluateTo(&serializer));
1046 void tst_QXmlQuery::evaluateToQStringTriggerError() const
1048 /* Invoke on a default constructed object. */
1052 QVERIFY(!query.evaluateTo(&out));
1055 /* Invoke on an invalid query; compile time error. */
1058 MessageSilencer silencer;
1059 query.setMessageHandler(&silencer);
1061 query.setQuery(QLatin1String("1 + "));
1064 QVERIFY(!query.evaluateTo(&out));
1067 /* Invoke on an invalid query; runtime error. */
1070 MessageSilencer silencer;
1071 query.setMessageHandler(&silencer);
1073 query.setQuery(QLatin1String("error()"));
1076 QVERIFY(!query.evaluateTo(&out));
1080 void tst_QXmlQuery::evaluateToQString() const
1082 QFETCH(QString, query);
1083 QFETCH(QString, expectedOutput);
1085 QXmlQuery queryInstance;
1086 queryInstance.setQuery(query);
1087 QVERIFY(queryInstance.isValid());
1090 QVERIFY(queryInstance.evaluateTo(&result));
1092 QCOMPARE(result, expectedOutput);
1095 void tst_QXmlQuery::evaluateToQString_data() const
1097 QTest::addColumn<QString>("query");
1098 QTest::addColumn<QString>("expectedOutput");
1100 QTest::newRow("Two atomics")
1101 << QString::fromLatin1("1, 'two'")
1102 << QString::fromLatin1("1 two\n");
1104 QTest::newRow("An element")
1105 << QString::fromLatin1("<e>{1}</e>")
1106 << QString::fromLatin1("<e>1</e>\n");
1109 void tst_QXmlQuery::evaluateToQStringSignature() const
1111 const QXmlQuery query;
1115 /* evaluateTo(QString *) should be a const function. */
1116 query.evaluateTo(&output);
1119 void tst_QXmlQuery::evaluateToQAbstractXmlReceiverTriggerWarnings() const
1123 /* We check the return value as well as warning message here. */
1124 QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed.");
1125 QCOMPARE(query.evaluateTo(static_cast<QAbstractXmlReceiver *>(0)),
1129 void tst_QXmlQuery::evaluateToQXmlResultItems() const
1131 /* Invoke on a default constructed object. */
1134 QXmlResultItems result;
1135 query.evaluateTo(&result);
1136 QVERIFY(result.next().isNull());
1140 void tst_QXmlQuery::evaluateToQXmlResultItemsTriggerWarnings() const
1142 QTest::ignoreMessage(QtWarningMsg, "A null pointer cannot be passed.");
1144 query.evaluateTo(static_cast<QXmlResultItems *>(0));
1147 void tst_QXmlQuery::evaluateToQXmlResultItemsErrorAtEnd() const
1150 MessageSilencer silencer;
1151 query.setMessageHandler(&silencer);
1152 query.setQuery(QLatin1String("1 to 100, fn:error()"));
1153 QVERIFY(query.isValid());
1156 query.evaluateTo(&it);
1158 while(!it.next().isNull())
1164 If baselines were generated, we flag it as a failure such that it gets
1165 attention, and that they are adjusted accordingly.
1167 void tst_QXmlQuery::checkGeneratedBaselines() const
1169 QCOMPARE(m_generatedBaselines, 0);
1171 /* If this check fails, the auto test setup is misconfigured, or files have
1172 * been added/removed without this number being updated. */
1173 QCOMPARE(m_pushTestsCount, int(ExpectedQueryCount));
1176 void tst_QXmlQuery::basicXQueryToQtTypeCheck() const
1178 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("allAtomics.xq"));
1179 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1182 query.setQuery(&queryFile);
1183 QVERIFY(query.isValid());
1186 query.evaluateTo(&it);
1188 QVariantList expectedValues;
1189 expectedValues.append(QString::fromLatin1("xs:untypedAtomic"));
1190 expectedValues.append(QDateTime(QDate(2002, 10, 10), QTime(23, 2, 11), Qt::UTC));
1191 expectedValues.append(QDate(2002, 10, 10));
1192 expectedValues.append(QVariant()); /* We currently doesn't support xs:time through the API. */
1194 expectedValues.append(QVariant()); /* xs:duration */
1195 expectedValues.append(QVariant()); /* xs:dayTimeDuration */
1196 expectedValues.append(QVariant()); /* xs:yearMonthDuration */
1198 if(sizeof(qreal) == sizeof(float)) {//ARM casts to Float not to double
1199 expectedValues.append(QVariant(float(3e3))); /* xs:float */
1200 expectedValues.append(QVariant(float(4e4))); /* xs:double */
1201 expectedValues.append(QVariant(float(2))); /* xs:decimal */
1203 expectedValues.append(QVariant(double(3e3))); /* xs:float */
1204 expectedValues.append(QVariant(double(4e4))); /* xs:double */
1205 expectedValues.append(QVariant(double(2))); /* xs:decimal */
1208 /* xs:integer and its sub-types. */
1209 expectedValues.append(QVariant(qlonglong(16)));
1210 expectedValues.append(QVariant(qlonglong(-6)));
1211 expectedValues.append(QVariant(qlonglong(-4)));
1212 expectedValues.append(QVariant(qlonglong(5)));
1213 expectedValues.append(QVariant(qlonglong(6)));
1214 expectedValues.append(QVariant(qlonglong(7)));
1215 expectedValues.append(QVariant(qlonglong(8)));
1216 expectedValues.append(QVariant(qlonglong(9)));
1217 expectedValues.append(QVariant(qulonglong(10)));
1218 expectedValues.append(QVariant(qlonglong(11)));
1219 expectedValues.append(QVariant(qlonglong(12)));
1220 expectedValues.append(QVariant(qlonglong(13)));
1221 expectedValues.append(QVariant(qlonglong(14)));
1223 expectedValues.append(QVariant()); /* xs:gYearMonth("1976-02"), */
1224 expectedValues.append(QVariant()); /* xs:gYear("2005-12:00"), */
1225 expectedValues.append(QVariant()); /* xs:gMonthDay("--12-25-14:00"), */
1226 expectedValues.append(QVariant()); /* xs:gDay("---25-14:00"), */
1227 expectedValues.append(QVariant()); /* xs:gMonth("--12-14:00"), */
1228 expectedValues.append(true); /* xs:boolean("true"), */
1229 expectedValues.append(QVariant(QByteArray::fromBase64(QByteArray("aaaa")))); /* xs:base64Binary("aaaa"), */
1230 expectedValues.append(QVariant(QByteArray::fromHex(QByteArray("FFFF")))); /* xs:hexBinary("FFFF"), */
1231 expectedValues.append(QVariant(QString::fromLatin1("http://example.com/"))); /* xs:anyURI("http://example.com/"), */
1232 QXmlNamePool np(query.namePool());
1233 expectedValues.append(QVariant(qVariantFromValue(QXmlName(np, QLatin1String("localName"),
1234 QLatin1String("http://example.com/2"),
1235 QLatin1String("prefix")))));
1237 expectedValues.append(QVariant(QString::fromLatin1("An xs:string")));
1238 expectedValues.append(QVariant(QString::fromLatin1("normalizedString")));
1239 expectedValues.append(QVariant(QString::fromLatin1("token")));
1240 expectedValues.append(QVariant(QString::fromLatin1("language")));
1241 expectedValues.append(QVariant(QString::fromLatin1("NMTOKEN")));
1242 expectedValues.append(QVariant(QString::fromLatin1("Name")));
1243 expectedValues.append(QVariant(QString::fromLatin1("NCName")));
1244 expectedValues.append(QVariant(QString::fromLatin1("ID")));
1245 expectedValues.append(QVariant(QString::fromLatin1("IDREF")));
1246 expectedValues.append(QVariant(QString::fromLatin1("ENTITY")));
1249 QXmlItem item(it.next());
1251 while(!item.isNull())
1253 QVERIFY(item.isAtomicValue());
1254 const QVariant produced(item.toAtomicValue());
1256 const QVariant &expected = expectedValues.at(i);
1258 /* For the cases where we can't represent a value in the XDM with Qt,
1259 * we return an invalid QVariant. */
1260 QCOMPARE(expected.isValid(), produced.isValid());
1262 QCOMPARE(produced.type(), expected.type());
1264 if(expected.isValid())
1266 /* This is only needed for xs:decimal though, for some reason. Probably
1267 * just artifacts created somewhere. */
1268 if(produced.type() == QVariant::Double)
1269 QVERIFY(qFuzzyCompare(produced.toDouble(), expected.toDouble()));
1270 else if(qVariantCanConvert<QXmlName>(produced))
1272 /* QVariant::operator==() does identity comparison, it doesn't delegate to operator==() of
1273 * the contained type, unless it's hardcoded into QVariant. */
1274 const QXmlName n1 = qVariantValue<QXmlName>(produced);
1275 const QXmlName n2 = qVariantValue<QXmlName>(expected);
1279 QCOMPARE(produced, expected);
1286 QCOMPARE(i, expectedValues.count());
1290 Send values from Qt into XQuery.
1292 void tst_QXmlQuery::basicQtToXQueryTypeCheck() const
1294 QFile queryFile(QLatin1String(queriesDirectory) + QLatin1String("allAtomicsExternally.xq"));
1295 QVERIFY(queryFile.exists());
1296 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1298 QCOMPARE(QVariant(QDate(1999, 9, 10)).type(), QVariant::Date);
1302 QXmlNamePool np(query.namePool());
1304 const QXmlName name(np, QLatin1String("localname"),
1305 QLatin1String("http://example.com"),
1306 QLatin1String("prefix"));
1308 query.bindVariable(QLatin1String("fromQUrl"), QXmlItem(QUrl(QString::fromLatin1("http://example.com/"))));
1309 query.bindVariable(QLatin1String("fromQByteArray"), QXmlItem(QByteArray("AAAA")));
1310 query.bindVariable(QLatin1String("fromBool"), QXmlItem(bool(true)));
1311 query.bindVariable(QLatin1String("fromQDate"), QXmlItem(QDate(2000, 10, 11)));
1312 // TODO Do with different QDateTime time specs
1313 query.bindVariable(QLatin1String("fromQDateTime"), QXmlItem(QDateTime(QDate(2001, 9, 10), QTime(1, 2, 3))));
1314 query.bindVariable(QLatin1String("fromDouble"), QXmlItem(double(3)));
1315 query.bindVariable(QLatin1String("fromFloat"), QXmlItem(float(4)));
1316 query.bindVariable(QLatin1String("integer"), QXmlItem(5));
1317 query.bindVariable(QLatin1String("fromQString"), QXmlItem(QString::fromLatin1("A QString")));
1318 query.bindVariable(QLatin1String("fromQChar"), QXmlItem(QChar::fromLatin1('C')));
1320 query.bindVariable(QLatin1String("fromIntLiteral"), QXmlItem(QVariant(654)));
1323 QVariant ui(uint(5));
1324 QCOMPARE(ui.type(), QVariant::UInt);
1325 query.bindVariable(QLatin1String("fromUInt"), ui);
1329 QVariant ulnglng(qulonglong(6));
1330 QCOMPARE(ulnglng.type(), QVariant::ULongLong);
1331 query.bindVariable(QLatin1String("fromULongLong"), ulnglng);
1335 QVariant qlnglng(qlonglong(7));
1336 QCOMPARE(qlnglng.type(), QVariant::LongLong);
1337 query.bindVariable(QLatin1String("fromLongLong"), qlnglng);
1340 query.setQuery(&queryFile);
1342 // TODO do queries which declares external variables with types. Tons of combos here.
1343 // TODO ensure that binding with QXmlItem() doesn't make a binding available.
1344 // TODO test rebinding a variable.
1346 QVERIFY(query.isValid());
1349 query.evaluateTo(&it);
1350 QXmlItem item(it.next());
1351 QVERIFY(!item.isNull());
1352 QVERIFY(item.isAtomicValue());
1354 if(sizeof(qreal) == sizeof(float)) //ARM casts to Float not to double
1355 QCOMPARE(item.toAtomicValue().toString(),
1356 QLatin1String("4 true 3 654 7 41414141 C 2000-10-11Z 2001-09-10T01:02:03 "
1357 "A QString http://example.com/ 5 6 true false false true true true true true true true "
1360 QCOMPARE(item.toAtomicValue().toString(),
1361 QLatin1String("4 true 3 654 7 41414141 C 2000-10-11Z 2001-09-10T01:02:03 "
1362 "A QString http://example.com/ 5 6 true true true true true true true true true true "
1367 void tst_QXmlQuery::bindNode() const
1370 TestSimpleNodeModel nodeModel(query.namePool());
1372 query.bindVariable(QLatin1String("node"), nodeModel.root());
1375 QVERIFY(buff.open(QIODevice::WriteOnly));
1377 query.setQuery(QLatin1String("declare variable $node external; $node"));
1378 QXmlSerializer serializer(query, &buff);
1380 QVERIFY(query.evaluateTo(&serializer));
1381 QCOMPARE(out, QByteArray("<nodeName/>"));
1385 Pass in a relative URI, and make sure it is resolved against the current application directory.
1387 void tst_QXmlQuery::relativeBaseURI() const
1390 query.setQuery(QLatin1String("fn:static-base-uri()"), QUrl(QLatin1String("a/relative/uri.weirdExtension")));
1391 QVERIFY(query.isValid());
1394 QBuffer buffer(&result);
1395 QVERIFY(buffer.open(QIODevice::ReadWrite));
1397 QXmlSerializer serializer(query, &buffer);
1398 QVERIFY(query.evaluateTo(&serializer));
1400 const QUrl loaded(QUrl::fromEncoded(result));
1401 QUrl appPath(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()));
1403 QVERIFY(loaded.isValid());
1404 QVERIFY(appPath.isValid());
1405 QVERIFY(!loaded.isRelative());
1406 QVERIFY(!appPath.isRelative());
1408 QFileInfo dir(appPath.toLocalFile());
1409 dir.setFile(QString());
1411 /* We can't use QUrl::isParentOf() because it doesn't do what we want it to */
1412 if(!loaded.toLocalFile().startsWith(dir.absoluteFilePath()))
1413 QTextStream(stderr) << "dir.absoluteFilePath():" << dir.absoluteFilePath() << "loaded.toLocalFile():" << loaded.toLocalFile();
1415 checkBaseURI(loaded, dir.absoluteFilePath());
1418 void tst_QXmlQuery::emptyBaseURI() const
1421 query.setQuery(QLatin1String("fn:static-base-uri()"), QUrl());
1422 QVERIFY(query.isValid());
1425 QBuffer buffer(&result);
1426 QVERIFY(buffer.open(QIODevice::ReadWrite));
1428 QXmlSerializer serializer(query, &buffer);
1429 QVERIFY(query.evaluateTo(&serializer));
1431 const QUrl loaded(QUrl::fromEncoded(result));
1432 QUrl appPath(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()));
1434 QVERIFY(loaded.isValid());
1435 QVERIFY(appPath.isValid());
1436 QVERIFY(!loaded.isRelative());
1437 QVERIFY(!appPath.isRelative());
1439 QFileInfo dir(appPath.toLocalFile());
1440 dir.setFile(QString());
1442 QCOMPARE(loaded, appPath);
1446 Ensure that QDate comes out as QDateTime.
1448 void tst_QXmlQuery::roundTripDateWithinQXmlItem() const
1450 const QDate date(1999, 9, 10);
1451 QVERIFY(date.isValid());
1453 const QVariant variant(date);
1454 QVERIFY(variant.isValid());
1455 QCOMPARE(variant.type(), QVariant::Date);
1457 const QXmlItem item(variant);
1458 QVERIFY(!item.isNull());
1459 QVERIFY(item.isAtomicValue());
1461 const QVariant out(item.toAtomicValue());
1462 QVERIFY(out.isValid());
1463 QCOMPARE(out.type(), QVariant::Date);
1464 QCOMPARE(out.toDate(), date);
1468 Check whether a query is valid, which uses an unbound variable.
1470 void tst_QXmlQuery::bindingMissing() const
1473 MessageSilencer messageHandler;
1474 query.setMessageHandler(&messageHandler);
1476 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1477 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1478 query.setQuery(&queryFile);
1480 QVERIFY(!query.isValid());
1483 void tst_QXmlQuery::bindDefaultConstructedItem() const
1485 QFETCH(QXmlItem, item);
1488 MessageSilencer messageHandler;
1489 query.setMessageHandler(&messageHandler);
1491 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1492 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1493 query.setQuery(&queryFile);
1494 query.bindVariable(QLatin1String("externalVariable"), item);
1496 QVERIFY(!query.isValid());
1499 void tst_QXmlQuery::bindDefaultConstructedItem_data() const
1501 QTest::addColumn<QXmlItem>("item");
1503 QTest::newRow("QXmlItem()") << QXmlItem();
1504 QTest::newRow("QXmlItem(QVariant())") << QXmlItem(QVariant());
1505 QTest::newRow("QXmlItem(QXmlNodeModelIndex())") << QXmlItem(QXmlNodeModelIndex());
1509 Remove a binding by binding QXmlItem() with the same name.
1511 void tst_QXmlQuery::eraseQXmlItemBinding() const
1514 MessageSilencer messageHandler;
1515 query.setMessageHandler(&messageHandler);
1517 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1518 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1519 query.bindVariable(QLatin1String("externalVariable"), QXmlItem(3));
1520 query.setQuery(&queryFile);
1521 QVERIFY(query.isValid());
1524 QBuffer buffer(&result);
1525 QVERIFY(buffer.open(QIODevice::ReadWrite));
1527 QXmlSerializer serializer(query, &buffer);
1528 QVERIFY(query.evaluateTo(&serializer));
1530 QCOMPARE(result, QByteArray("3 6<e>3</e>false"));
1532 query.bindVariable(QLatin1String("externalVariable"), QXmlItem());
1533 QVERIFY(!query.isValid());
1537 Erase a variable binding
1539 void tst_QXmlQuery::eraseDeviceBinding() const
1541 /* Erase an existing QIODevice binding with another QIODevice binding. */
1545 QByteArray doc("<e/>");
1546 QBuffer buffer(&doc);
1547 QVERIFY(buffer.open(QIODevice::ReadOnly));
1549 query.bindVariable(QLatin1String("in"), &buffer);
1550 query.setQuery(QLatin1String("$in"));
1551 QVERIFY(query.isValid());
1553 query.bindVariable(QLatin1String("in"), 0);
1554 QVERIFY(!query.isValid());
1557 /* Erase an existing QXmlItem binding with another QIODevice binding. */
1561 query.bindVariable(QLatin1String("in"), QXmlItem(5));
1562 query.setQuery(QLatin1String("$in"));
1563 QVERIFY(query.isValid());
1565 query.bindVariable(QLatin1String("in"), 0);
1566 QVERIFY(!query.isValid());
1571 Bind a variable, evaluate, bind with a different value but same type, and evaluate again.
1573 void tst_QXmlQuery::rebindVariableSameType() const
1576 MessageSilencer messageHandler;
1577 query.setMessageHandler(&messageHandler);
1579 query.bindVariable(QLatin1String("externalVariable"), QXmlItem(3));
1582 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1583 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1584 query.setQuery(&queryFile);
1587 QVERIFY(query.isValid());
1591 QBuffer buffer(&result);
1592 QVERIFY(buffer.open(QIODevice::ReadWrite));
1594 QXmlSerializer serializer(query, &buffer);
1595 QVERIFY(query.evaluateTo(&serializer));
1597 QCOMPARE(result, QByteArray("3 6<e>3</e>false"));
1601 query.bindVariable(QLatin1String("externalVariable"), QXmlItem(5));
1603 QBuffer buffer(&result);
1604 QVERIFY(buffer.open(QIODevice::ReadWrite));
1606 QXmlSerializer serializer(query, &buffer);
1607 QVERIFY(query.evaluateTo(&serializer));
1609 QCOMPARE(result, QByteArray("5 8<e>5</e>false"));
1614 void tst_QXmlQuery::rebindVariableDifferentType() const
1616 /* Rebind QXmlItem variable with QXmlItem variable. */
1619 query.bindVariable(QLatin1String("in"), QXmlItem(3));
1620 query.setQuery(QLatin1String("$in"));
1621 QVERIFY(query.isValid());
1623 query.bindVariable(QLatin1String("in"), QXmlItem("A string"));
1624 QVERIFY(!query.isValid());
1627 /* Rebind QIODevice variable with QXmlItem variable. */
1631 buffer.setData(QByteArray("<e/>"));
1632 QVERIFY(buffer.open(QIODevice::ReadOnly));
1634 query.bindVariable(QLatin1String("in"), &buffer);
1635 query.setQuery(QLatin1String("$in"));
1636 QVERIFY(query.isValid());
1638 query.bindVariable(QLatin1String("in"), QXmlItem("A string"));
1639 QVERIFY(!query.isValid());
1642 /* Rebind QXmlItem variable with QIODevice variable. The type of the
1643 * variable changes, so a recompile is necessary. */
1647 query.bindVariable(QLatin1String("in"), QXmlItem(QLatin1String("A string")));
1648 query.setQuery(QLatin1String("$in"));
1649 QVERIFY(query.isValid());
1652 buffer.setData(QByteArray("<e/>"));
1653 QVERIFY(buffer.open(QIODevice::ReadOnly));
1654 query.bindVariable(QLatin1String("in"), &buffer);
1655 QVERIFY(!query.isValid());
1659 void tst_QXmlQuery::rebindVariableWithNullItem() const
1663 query.bindVariable(QLatin1String("name"), QXmlItem(5));
1664 query.bindVariable(QLatin1String("name"), QXmlItem());
1667 void tst_QXmlQuery::constCorrectness() const
1669 QXmlResultItems result;
1671 tmp.setQuery(QLatin1String("1")); /* Just so we have a valid query. */
1672 const QXmlQuery query(tmp);
1674 /* These functions should be const. */
1676 query.evaluateTo(&result);
1678 query.uriResolver();
1679 query.messageHandler();
1682 QString dummyString;
1683 QTextStream dummyStream(&dummyString);
1684 PushBaseliner dummy(dummyStream, query.namePool());
1685 QVERIFY(dummy.isValid());
1686 query.evaluateTo(&dummy);
1690 void tst_QXmlQuery::objectSize() const
1692 /* We have a d pointer. */
1693 QCOMPARE(sizeof(QXmlQuery), sizeof(void *));
1696 void tst_QXmlQuery::setUriResolver() const
1698 /* Set a null resolver, and make sure it can take a const pointer. */
1701 query.setUriResolver(static_cast<const QAbstractUriResolver *>(0));
1702 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0));
1706 TestURIResolver resolver;
1708 query.setUriResolver(&resolver);
1709 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(&resolver));
1713 void tst_QXmlQuery::uriResolver() const
1715 /* Check default value. */
1718 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0));
1722 void tst_QXmlQuery::messageXML() const
1726 MessageValidator messageValidator;
1727 query.setMessageHandler(&messageValidator);
1729 query.setQuery(QLatin1String("1basicSyntaxError"));
1731 const QRegExp removeFilename(QLatin1String("Location: file:.*\\#"));
1732 QVERIFY(removeFilename.isValid());
1734 QVERIFY(messageValidator.success());
1735 QCOMPARE(messageValidator.received().remove(removeFilename),
1736 QString::fromLatin1("Type:3\n"
1737 "Description: <html xmlns='http://www.w3.org/1999/xhtml/'><body><p>syntax error, unexpected unknown keyword</p></body></html>\n"
1738 "Identifier: http://www.w3.org/2005/xqt-errors#XPST0003\n"
1743 1. Allocate QXmlResultItems
1744 2. Allocate QXmlQuery
1745 3. evaluate to the QXmlResultItems instance
1746 4. Dellocate the QXmlQuery instance
1747 5. Ensure QXmlResultItems works
1749 void tst_QXmlQuery::resultItemsDeallocatedQuery() const
1751 QXmlResultItems result;
1755 query.setQuery(QLatin1String("1, 2, xs:integer(<e>3</e>)"));
1756 query.evaluateTo(&result);
1759 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
1760 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
1761 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
1762 QVERIFY(result.next().isNull());
1763 QVERIFY(!result.hasError());
1767 1. Bind variable with bindVariable()
1768 2. setQuery that has 'declare variable' with same name.
1769 3. Ensure the value inside the query is used. We don't guarantee this behavior
1770 but that's what we lock.
1772 void tst_QXmlQuery::shadowedVariables() const
1775 query.bindVariable("varName", QXmlItem(3));
1776 query.setQuery(QLatin1String("declare variable $varName := 5; $varName"));
1778 QXmlResultItems result;
1779 query.evaluateTo(&result);
1781 QCOMPARE(result.next().toAtomicValue(), QVariant(5));
1784 void tst_QXmlQuery::setFocusQXmlItem() const
1786 /* Make sure we can take a const reference. */
1789 const QXmlItem item;
1790 query.setFocus(item);
1793 // TODO evaluate with atomic value, check type
1794 // TODO evaluate with node, check type
1795 // TODO ensure that setFocus() triggers query recompilation, as appropriate.
1796 // TODO let the focus be undefined, call isvalid, call evaluate anyway
1797 // TODO let the focus be undefined, call evaluate directly
1800 void tst_QXmlQuery::setFocusQUrl() const
1802 /* Load a focus which isn't well-formed. */
1805 MessageSilencer silencer;
1807 query.setMessageHandler(&silencer);
1809 QVERIFY(!query.setFocus(QUrl(QLatin1String("data/notWellformed.xml"))));
1812 /* Ensure the same URI resolver is used. */
1814 QXmlQuery query(QXmlQuery::XSLT20);
1816 const TestURIResolver resolver(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
1817 query.setUriResolver(&resolver);
1819 QVERIFY(query.setFocus(QUrl(QLatin1String("arbitraryURI"))));
1820 query.setQuery(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/copyWholeDocument.xsl"))));
1821 QVERIFY(query.isValid());
1824 QVERIFY(result.open(QIODevice::ReadWrite));
1825 QXmlSerializer serializer(query, &result);
1826 query.evaluateTo(&serializer);
1828 QCOMPARE(result.data(), QByteArray("<doc/>"));
1831 // TODO ensure that the focus type doesn't change from XSLT20 on the main instance.
1835 This code poses a challenge wrt. to internal caching.
1837 void tst_QXmlQuery::setFocusQIODevice() const
1843 focus.setData(QByteArray("<e>abc</e>"));
1844 QVERIFY(focus.open(QIODevice::ReadOnly));
1845 query.setFocus(&focus);
1846 query.setQuery(QLatin1String("string()"));
1847 QVERIFY(query.isValid());
1850 query.evaluateTo(&output);
1852 QCOMPARE(output, QString::fromLatin1("abc\n"));
1855 /* Set a new focus, make sure it changes & works. */
1858 focus2.setData(QByteArray("<e>abc2</e>"));
1859 QVERIFY(focus2.open(QIODevice::ReadOnly));
1860 query.setFocus(&focus2);
1861 QVERIFY(query.isValid());
1864 query.evaluateTo(&output);
1866 QCOMPARE(output, QString::fromLatin1("abc2\n"));
1871 Since we internally use variable bindings for implementing the focus, we need
1872 to make sure we don't clash in this area.
1874 void tst_QXmlQuery::setFocusQIODeviceAvoidVariableClash() const
1877 buffer.setData("<e>focus</e>");
1878 QVERIFY(buffer.open(QIODevice::ReadOnly));
1880 /* First we bind the variable name, then the focus. */
1883 query.bindVariable(QString(QLatin1Char('u')), QVariant(1));
1884 query.setFocus(&buffer);
1885 query.setQuery(QLatin1String("string()"));
1888 query.evaluateTo(&out);
1890 QCOMPARE(out, QString::fromLatin1("focus\n"));
1893 /* First we bind the focus, then the variable name. */
1896 QVERIFY(buffer.open(QIODevice::ReadOnly));
1897 query.setFocus(&buffer);
1898 query.bindVariable(QString(QLatin1Char('u')), QVariant(1));
1899 query.setQuery(QLatin1String("string()"));
1902 query.evaluateTo(&out);
1904 QCOMPARE(out, QString::fromLatin1("focus\n"));
1908 void tst_QXmlQuery::setFocusQIODeviceFailure() const
1910 /* A not well-formed input document. */
1914 MessageSilencer silencer;
1915 query.setMessageHandler(&silencer);
1918 input.setData("<e");
1919 QVERIFY(input.open(QIODevice::ReadOnly));
1921 QCOMPARE(query.setFocus(&input), false);
1925 void tst_QXmlQuery::setFocusQString() const
1929 /* Basic use of focus. */
1931 QVERIFY(query.setFocus(QLatin1String("<e>textNode</e>")));
1932 query.setQuery(QLatin1String("string()"));
1933 QVERIFY(query.isValid());
1935 query.evaluateTo(&out);
1936 QCOMPARE(out, QString::fromLatin1("textNode\n"));
1939 /* Set to a new focus, make sure it changes and works. */
1941 QVERIFY(query.setFocus(QLatin1String("<e>newFocus</e>")));
1943 query.evaluateTo(&out);
1944 QCOMPARE(out, QString::fromLatin1("newFocus\n"));
1948 void tst_QXmlQuery::setFocusQStringFailure() const
1951 MessageSilencer silencer;
1953 query.setMessageHandler(&silencer);
1954 QVERIFY(!query.setFocus(QLatin1String("<notWellformed")));
1956 /* Let's try the slight special case of a null string. */
1957 QVERIFY(!query.setFocus(QString()));
1960 void tst_QXmlQuery::setFocusQStringSignature() const
1963 MessageSilencer silencer;
1964 query.setMessageHandler(&silencer);
1966 const QString argument;
1967 /* We should take a const ref. */
1968 query.setFocus(argument);
1970 /* We should return a bool. */
1971 static_cast<bool>(query.setFocus(QString()));
1974 void tst_QXmlQuery::setFocusQIODeviceTriggerWarnings() const
1976 /* A null pointer. */
1980 QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed.");
1981 QCOMPARE(query.setFocus(static_cast<QIODevice *>(0)), false);
1984 /* A non opened-device. */
1988 QBuffer notReadable;
1989 QVERIFY(!notReadable.isReadable());
1991 QTest::ignoreMessage(QtWarningMsg, "The device must be readable.");
1992 QCOMPARE(query.setFocus(¬Readable), false);
1996 void tst_QXmlQuery::fnDocNetworkAccessSuccess() const
1998 #if defined(Q_OS_WINCE) && !defined(_X86_)
1999 QStringList testsToSkip;
2000 testsToSkip << "http scheme" << "ftp scheme";
2001 if (testsToSkip.contains(QTest::currentDataTag()))
2002 QSKIP("Network tests are currently unsupported on Windows CE.");
2005 QFETCH(QUrl, uriToOpen);
2006 QFETCH(QByteArray, expectedOutput);
2008 if(!uriToOpen.isValid())
2009 qDebug() << "uriToOpen:" << uriToOpen;
2011 QVERIFY(uriToOpen.isValid());
2014 query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen));
2015 query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)"));
2016 QVERIFY(query.isValid());
2019 QBuffer buffer(&result);
2020 QVERIFY(buffer.open(QIODevice::WriteOnly));
2022 QXmlSerializer serializer(query, &buffer);
2024 // these tests with data: URL fail since QUrl change e650dd3b6d8212d2c54ddb4a50558b508d0bf2b9
2025 QEXPECT_FAIL("data scheme with ASCII", "QTBUG-23953, fails", Abort);
2026 QEXPECT_FAIL("data scheme with ASCII no MIME type", "QTBUG-23953, fails", Abort);
2027 QVERIFY(query.evaluateTo(&serializer));
2029 QCOMPARE(result, expectedOutput);
2032 void tst_QXmlQuery::fnDocNetworkAccessSuccess_data() const
2034 QTest::addColumn<QUrl>("uriToOpen");
2035 QTest::addColumn<QByteArray>("expectedOutput");
2037 QTest::newRow("file scheme")
2038 << inputFileAsURI(QLatin1String(SRCDIR "input.xml"))
2039 << QByteArray("<!-- This is just a file for testing. --><input/>");
2041 QTest::newRow("data scheme with ASCII")
2042 /* QUrl::toPercentEncoding(QLatin1String("<e/>")) yields "%3Ce%2F%3E". */
2043 << QUrl::fromEncoded("data:application/xml,%3Ce%2F%3E")
2044 << QByteArray("<e/>");
2046 QTest::newRow("data scheme with ASCII no MIME type")
2047 << QUrl::fromEncoded("data:,%3Ce%2F%3E")
2048 << QByteArray("<e/>");
2050 QTest::newRow("data scheme with base 64")
2051 << QUrl::fromEncoded("data:application/xml;base64,PGUvPg==")
2052 << QByteArray("<e/>");
2054 QTest::newRow("qrc scheme")
2055 << QUrl::fromEncoded("qrc:/QXmlQueryTestData/data/oneElement.xml")
2056 << QByteArray("<oneElement/>");
2061 QTest::newRow("http scheme")
2062 << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/wellFormed.xml"))
2063 << QByteArray("<!-- a comment --><e from=\"http\">Some Text</e>");
2065 QTest::newRow("ftp scheme")
2066 << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/wellFormed.xml"))
2067 << QByteArray("<!-- a comment --><e from=\"ftp\">Some Text</e>");
2071 void tst_QXmlQuery::fnDocNetworkAccessFailure() const
2073 QFETCH(QUrl, uriToOpen);
2075 QVERIFY(uriToOpen.isValid());
2078 MessageSilencer silencer;
2079 query.setMessageHandler(&silencer);
2080 query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen));
2081 query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)"));
2082 QVERIFY(query.isValid());
2084 QXmlResultItems result;
2085 query.evaluateTo(&result);
2087 while(!result.next().isNull())
2089 /* Just loop until the end. */
2092 // TODO do something that triggers a /timeout/.
2093 QVERIFY(result.hasError());
2096 void tst_QXmlQuery::fnDocNetworkAccessFailure_data() const
2098 QTest::addColumn<QUrl>("uriToOpen");
2100 QTest::newRow("data scheme, not-well-formed")
2101 << QUrl(QLatin1String("data:application/xml;base64,PGUvg==="));
2103 QTest::newRow("file scheme, non-existant file")
2104 << QUrl(QLatin1String("file:///example.com/does/notExist.xml"));
2106 QTest::newRow("http scheme, file not found")
2107 << QUrl(QLatin1String("http://www.example.com/does/not/exist.xml"));
2109 QTest::newRow("http scheme, nonexistent host")
2110 << QUrl(QLatin1String("http://this.host.does.not.exist.I.SWear"));
2112 QTest::newRow("qrc scheme, not well-formed")
2113 << QUrl(QLatin1String("qrc:/QXmlQueryTestData/notWellformed.xml"));
2115 QTest::newRow("'qrc:/', non-existing file")
2116 << QUrl(QLatin1String(":/QXmlQueryTestData/data/thisFileDoesNotExist.xml"));
2118 QTest::newRow("':/', this scheme is not supported")
2119 << QUrl(QLatin1String(":/QXmlQueryTestData/data/notWellformed.xml"));
2124 QTest::newRow("http scheme, not well-formed")
2125 << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/notWellformed.xml"));
2127 QTest::newRow("https scheme, not well-formed")
2128 << QUrl(QString("https://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/notWellformedViaHttps.xml"));
2130 QTest::newRow("https scheme, nonexistent host")
2131 << QUrl(QLatin1String("https://this.host.does.not.exist.I.SWear"));
2133 QTest::newRow("ftp scheme, nonexistent host")
2134 << QUrl(QLatin1String("ftp://this.host.does.not.exist.I.SWear"));
2136 QTest::newRow("ftp scheme, not well-formed")
2137 << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/notWellFormed.xml"));
2141 Create a network timeout from a QIODevice binding such
2142 that we ensure we don't hang infinitely.
2144 void tst_QXmlQuery::fnDocOnQIODeviceTimeout() const
2150 server.listen(QHostAddress::LocalHost, 1088);
2153 client.connectToHost("localhost", 1088);
2154 QVERIFY(client.isReadable());
2158 MessageSilencer silencer;
2159 query.setMessageHandler(&silencer);
2161 query.bindVariable(QLatin1String("inDevice"), &client);
2162 query.setQuery(QLatin1String("declare variable $inDevice external;\ndoc($inDevice)"));
2163 QVERIFY(query.isValid());
2165 QXmlResultItems result;
2166 query.evaluateTo(&result);
2167 QXmlItem next(result.next());
2169 while(!next.isNull())
2171 next = result.next();
2174 QVERIFY(result.hasError());
2178 When changing query, the static context must change too, such that
2179 the source locations are updated.
2181 void tst_QXmlQuery::recompilationWithEvaluateToResultFailing() const
2184 MessageSilencer silencer;
2185 query.setMessageHandler(&silencer);
2187 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2188 QVERIFY(query.isValid()); /* Trigger query compilation. */
2190 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2191 QVERIFY(query.isValid()); /* Trigger second compilation. */
2193 QXmlResultItems items;
2194 query.evaluateTo(&items);
2196 QVERIFY(items.hasError());
2199 void tst_QXmlQuery::secondEvaluationWithEvaluateToResultFailing() const
2202 MessageSilencer silencer;
2203 query.setMessageHandler(&silencer);
2205 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2206 QVERIFY(query.isValid()); /* Trigger query compilation. */
2208 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2209 /* We don't call isValid(). */
2210 QXmlResultItems items;
2211 query.evaluateTo(&items);
2213 QVERIFY(items.hasError());
2217 Compilation is triggered in the evaluation function due to no call to QXmlQuery::isValid().
2219 void tst_QXmlQuery::recompilationWithEvaluateToReceiver() const
2222 MessageSilencer silencer;
2223 query.setMessageHandler(&silencer);
2225 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2226 QVERIFY(query.isValid()); /* Trigger query compilation. */
2228 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2229 /* We don't call isValid(). */
2232 QBuffer buffer(&dummy);
2233 buffer.open(QIODevice::WriteOnly);
2235 QXmlSerializer serializer(query, &buffer);
2237 QVERIFY(!query.evaluateTo(&serializer));
2240 void tst_QXmlQuery::evaluateToQStringListOnInvalidQuery() const
2242 MessageSilencer silencer;
2244 /* Invoke on a default constructed object. */
2248 QVERIFY(!query.evaluateTo(&out));
2251 /* Invoke on a syntactically invalid query. */
2255 MessageSilencer silencer;
2257 query.setMessageHandler(&silencer);
2258 query.setQuery(QLatin1String("1 + "));
2260 QVERIFY(!query.evaluateTo(&out));
2263 /* Invoke on a query with the wrong type, one atomic. */
2268 query.setQuery(QLatin1String("1"));
2269 query.setMessageHandler(&silencer);
2270 QVERIFY(!query.evaluateTo(&out));
2273 /* Invoke on a query with the wrong type, one element. */
2278 query.setQuery(QLatin1String("<e/>"));
2279 QVERIFY(!query.evaluateTo(&out));
2282 /* Invoke on a query with the wrong type, mixed nodes & atomics. */
2287 query.setQuery(QLatin1String("<e/>, 1, 'a string'"));
2288 query.setMessageHandler(&silencer);
2289 QVERIFY(!query.evaluateTo(&out));
2292 /* Evaluate the empty sequence. */
2297 query.setQuery(QLatin1String("()"));
2298 QVERIFY(!query.evaluateTo(&out));
2299 QVERIFY(out.isEmpty());
2303 void tst_QXmlQuery::evaluateToQStringList() const
2305 QFETCH(QString, queryString);
2306 QFETCH(QStringList, expectedOutput);
2309 query.setQuery(queryString);
2311 QVERIFY(query.isValid());
2313 QVERIFY(query.evaluateTo(&out));
2315 QCOMPARE(out, expectedOutput);
2318 void tst_QXmlQuery::evaluateToQStringListTriggerWarnings() const
2322 QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed.");
2323 QCOMPARE(query.evaluateTo(static_cast<QStringList *>(0)),
2327 void tst_QXmlQuery::evaluateToQStringList_data() const
2329 QTest::addColumn<QString>("queryString");
2330 QTest::addColumn<QStringList>("expectedOutput");
2332 QTest::newRow("One atomic")
2333 << QString::fromLatin1("(1 + 1) cast as xs:string")
2334 << QStringList(QString::fromLatin1("2"));
2337 QStringList expected;
2338 expected << QLatin1String("2");
2339 expected << QLatin1String("a string");
2341 QTest::newRow("Two atomics")
2342 << QString::fromLatin1("(1 + 1) cast as xs:string, 'a string'")
2346 QTest::newRow("A query which evaluates to sub-types of xs:string.")
2347 << QString::fromLatin1("xs:NCName('NCName'), xs:normalizedString(' a b c ')")
2348 << (QStringList() << QString::fromLatin1("NCName")
2349 << QString::fromLatin1(" a b c "));
2351 QTest::newRow("A query which evaluates to two elements.")
2352 << QString::fromLatin1("string(<e>theString1</e>), string(<e>theString2</e>)")
2353 << (QStringList() << QString::fromLatin1("theString1")
2354 << QString::fromLatin1("theString2"));
2358 Ensure that we don't automatically convert non-xs:string values.
2360 void tst_QXmlQuery::evaluateToQStringListNoConversion() const
2363 query.setQuery(QString::fromLatin1("<e/>"));
2364 QVERIFY(query.isValid());
2366 QVERIFY(!query.evaluateTo(&result));
2369 void tst_QXmlQuery::evaluateToQIODevice() const
2371 /* an XQuery, check that no indentation is performed. */
2374 QVERIFY(out.open(QIODevice::ReadWrite));
2377 query.setQuery(QLatin1String("<a><b/></a>"));
2378 QVERIFY(query.isValid());
2379 QVERIFY(query.evaluateTo(&out));
2380 QCOMPARE(out.data(), QByteArray("<a><b/></a>"));
2384 void tst_QXmlQuery::evaluateToQIODeviceTriggerWarnings() const
2388 QTest::ignoreMessage(QtWarningMsg, "The pointer to the device cannot be null.");
2389 QCOMPARE(query.evaluateTo(static_cast<QIODevice *>(0)),
2394 QTest::ignoreMessage(QtWarningMsg, "The device must be writable.");
2395 QCOMPARE(query.evaluateTo(&buffer),
2399 void tst_QXmlQuery::evaluateToQIODeviceSignature() const
2401 /* The function should be const. */
2404 QVERIFY(out.open(QIODevice::ReadWrite));
2406 const QXmlQuery query;
2408 query.evaluateTo(&out);
2412 void tst_QXmlQuery::evaluateToQIODeviceOnInvalidQuery() const
2415 QVERIFY(out.open(QIODevice::WriteOnly));
2417 /* On syntactically invalid query. */
2420 MessageSilencer silencer;
2421 query.setMessageHandler(&silencer);
2422 query.setQuery(QLatin1String("1 +"));
2423 QVERIFY(!query.isValid());
2424 QVERIFY(!query.evaluateTo(&out));
2427 /* On null QXmlQuery instance. */
2430 QVERIFY(!query.evaluateTo(&out));
2435 void tst_QXmlQuery::setQueryQIODeviceQUrl() const
2440 buffer.setData("1, 2, 2 + 1");
2441 QVERIFY(buffer.open(QIODevice::ReadOnly));
2444 query.setQuery(&buffer);
2445 QVERIFY(query.isValid());
2447 QXmlResultItems result;
2448 query.evaluateTo(&result);
2449 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
2450 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
2451 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
2452 QVERIFY(result.next().isNull());
2453 QVERIFY(!result.hasError());
2456 /* Set query that is invalid. */
2459 buffer.setData("1, ");
2460 QVERIFY(buffer.open(QIODevice::ReadOnly));
2463 MessageSilencer silencer;
2464 query.setMessageHandler(&silencer);
2465 query.setQuery(&buffer);
2466 QVERIFY(!query.isValid());
2469 /* Check that the base URI passes through. */
2472 buffer.setData("string(static-base-uri())");
2473 QVERIFY(buffer.open(QIODevice::ReadOnly));
2476 query.setQuery(&buffer, QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl"));
2477 QVERIFY(query.isValid());
2480 query.evaluateTo(&result);
2481 QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
2485 void tst_QXmlQuery::setQueryQIODeviceQUrlTriggerWarnings() const
2488 QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed.");
2492 QTest::ignoreMessage(QtWarningMsg, "The device must be readable.");
2493 query.setQuery(&buffer);
2496 void tst_QXmlQuery::setQueryQString() const
2501 query.setQuery(QLatin1String("1, 2, 2 + 1"));
2502 QVERIFY(query.isValid());
2504 QXmlResultItems result;
2505 query.evaluateTo(&result);
2506 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
2507 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
2508 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
2509 QVERIFY(result.next().isNull());
2510 QVERIFY(!result.hasError());
2513 /* Set query that is invalid. */
2515 MessageSilencer silencer;
2517 query.setMessageHandler(&silencer);
2518 query.setQuery(QLatin1String("1, "));
2519 QVERIFY(!query.isValid());
2522 /* Check that the base URI passes through. */
2525 query.setQuery(QLatin1String("string(static-base-uri())"), QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl"));
2526 QVERIFY(query.isValid());
2529 query.evaluateTo(&result);
2530 QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
2534 void tst_QXmlQuery::setQueryQUrlSuccess() const
2536 #if defined(Q_OS_WINCE) && !defined(_X86_)
2537 QStringList testsToSkip;
2538 testsToSkip << "A valid query via the ftp scheme" << "A valid query via the http scheme";
2539 if (testsToSkip.contains(QTest::currentDataTag()))
2540 QSKIP("Network tests are currently unsupported on Windows CE.");
2543 QFETCH(QUrl, queryURI);
2544 QFETCH(QByteArray, expectedOutput);
2546 QVERIFY(queryURI.isValid());
2550 MessageSilencer silencer;
2551 query.setMessageHandler(&silencer);
2553 query.setQuery(queryURI);
2554 QVERIFY(query.isValid());
2557 QBuffer buffer(&out);
2558 QVERIFY(buffer.open(QIODevice::WriteOnly));
2559 QXmlSerializer serializer(query, &buffer);
2561 query.evaluateTo(&serializer);
2562 QCOMPARE(out, expectedOutput);
2565 void tst_QXmlQuery::setQueryQUrlSuccess_data() const
2567 QTest::addColumn<QUrl>("queryURI");
2568 QTest::addColumn<QByteArray>("expectedOutput");
2570 QTest::newRow("A valid query via the data scheme")
2571 << QUrl::fromEncoded("data:application/xml,1%20%2B%201") /* "1 + 1" */
2574 QTest::newRow("A valid query via the file scheme")
2575 << QUrl::fromLocalFile(inputFile(QLatin1String(queriesDirectory) + QLatin1String("onePlusOne.xq")))
2581 QTest::newRow("A valid query via the ftp scheme")
2582 << QUrl::fromEncoded(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/viaFtp.xq").toLatin1())
2583 << QByteArray("This was received via FTP");
2585 QTest::newRow("A valid query via the http scheme")
2586 << QUrl::fromEncoded(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/viaHttp.xq").toLatin1())
2587 << QByteArray("This was received via HTTP.");
2590 void tst_QXmlQuery::setQueryQUrlFailSucceed() const
2593 MessageSilencer silencer;
2595 query.setMessageHandler(&silencer);
2597 query.setQuery(QLatin1String("1 + 1"));
2598 QVERIFY(query.isValid());
2600 query.setQuery(QUrl::fromEncoded("file://example.com/does/not/exist"));
2601 QVERIFY(!query.isValid());
2604 void tst_QXmlQuery::setQueryQUrlFailure() const
2606 QFETCH(QUrl, queryURI);
2608 MessageSilencer silencer;
2611 query.setMessageHandler(&silencer);
2612 query.setQuery(queryURI);
2613 QVERIFY(!query.isValid());
2616 void tst_QXmlQuery::setQueryQUrlFailure_data() const
2618 QTest::addColumn<QUrl>("queryURI");
2620 QTest::newRow("Query via file:// that does not exist.")
2621 << QUrl::fromEncoded("file://example.com/does/not/exist");
2623 QTest::newRow("A query via file:// that is completely empty, but readable.")
2624 << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("../xmlpatterns/queries/completelyEmptyQuery.xq"));
2627 const QString name(QLatin1String("nonReadableFile.xq"));
2628 QFile outFile(name);
2629 QVERIFY(outFile.open(QIODevice::WriteOnly));
2630 outFile.write(QByteArray("1"));
2632 /* On some windows versions, this fails, so we don't check that this works with QVERIFY. */
2633 outFile.setPermissions(QFile::Permissions(QFile::Permissions()));
2635 QTest::newRow("Query via file:/ that does not have read permissions.")
2636 << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("nonReadableFile.xq"));
2642 QTest::newRow("Query via HTTP that does not exist.")
2643 << QUrl::fromEncoded("http://example.com/NoQuery/ISWear");
2646 QTest::newRow("Query via FTP that does not exist.")
2647 << QUrl::fromEncoded("ftp://example.com/NoQuery/ISWear");
2650 QTest::newRow("A query via http:// that is completely empty, but readable.")
2651 << QUrl::fromEncoded(QString(
2652 "http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/completelyEmptyQuery.xq").toLatin1());
2654 QTest::newRow("A query via ftp:// that is completely empty, but readable.")
2655 << QUrl::fromEncoded(QString(
2656 "ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/completelyEmptyQuery.xq").toLatin1());
2660 void tst_QXmlQuery::setQueryQUrlBaseURI() const
2662 QFETCH(QUrl, inputBaseURI);
2663 QFETCH(QUrl, expectedBaseURI);
2667 query.setQuery(QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq")), inputBaseURI);
2668 QVERIFY(query.isValid());
2671 QVERIFY(query.evaluateTo(&result));
2672 QCOMPARE(result.count(), 1);
2674 if(qstrcmp(QTest::currentDataTag(), "Relative base URI") == 0)
2675 checkBaseURI(QUrl(result.first()), QCoreApplication::applicationFilePath());
2677 QCOMPARE(result.first(), expectedBaseURI.toString());
2680 void tst_QXmlQuery::setQueryQUrlBaseURI_data() const
2682 QTest::addColumn<QUrl>("inputBaseURI");
2683 QTest::addColumn<QUrl>("expectedBaseURI");
2685 QTest::newRow("absolute HTTP")
2686 << QUrl(QLatin1String("http://www.example.com/"))
2687 << QUrl(QLatin1String("http://www.example.com/"));
2689 QTest::newRow("None, so the query URI is used")
2691 << QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq"));
2693 QTest::newRow("Relative base URI")
2694 << QUrl(QLatin1String("../data/relative.uri"))
2699 1. Create a valid query.
2700 2. Call setQuery(QUrl), with a query file that doesn't exist.
2701 3. Verify that the query has changed state into invalid.
2703 void tst_QXmlQuery::setQueryWithNonExistentQUrlOnValidQuery() const
2707 MessageSilencer messageSilencer;
2708 query.setMessageHandler(&messageSilencer);
2710 query.setQuery(QLatin1String("1 + 1"));
2711 QVERIFY(query.isValid());
2713 query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/DOESNOTEXIST.xq"));
2714 QVERIFY(!query.isValid());
2718 1. Create a valid query.
2719 2. Call setQuery(QUrl), with a query file that is invalid.
2720 3. Verify that the query has changed state into invalid.
2722 void tst_QXmlQuery::setQueryWithInvalidQueryFromQUrlOnValidQuery() const
2726 MessageSilencer messageSilencer;
2727 query.setMessageHandler(&messageSilencer);
2729 query.setQuery(QLatin1String("1 + 1"));
2730 QVERIFY(query.isValid());
2732 query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/queries/syntaxError.xq"));
2733 QVERIFY(!query.isValid());
2737 This triggered two bugs:
2739 - First, the DynamicContext wasn't assigned to QXmlResultItems, meaning it went out of
2740 scope and therefore deallocated the document pool, and calls
2741 to QXmlResultItems::next() would use dangling pointers.
2743 - Conversion between QPatternist::Item and QXmlItem was incorrectly done, leading to nodes
2744 being treated as atomic values, and subsequent crashes.
2747 void tst_QXmlQuery::retrieveNameFromQuery() const
2749 QFETCH(QString, queryString);
2750 QFETCH(QString, expectedName);
2753 query.setQuery(queryString);
2754 QVERIFY(query.isValid());
2755 QXmlResultItems result;
2756 query.evaluateTo(&result);
2758 QVERIFY(!result.hasError());
2760 const QXmlItem item(result.next());
2761 QVERIFY(!result.hasError());
2762 QVERIFY(!item.isNull());
2763 QVERIFY(item.isNode());
2765 const QXmlNodeModelIndex node(item.toNodeModelIndex());
2766 QVERIFY(!node.isNull());
2768 QCOMPARE(node.model()->name(node).localName(query.namePool()), expectedName);
2771 void tst_QXmlQuery::retrieveNameFromQuery_data() const
2773 QTest::addColumn<QString>("queryString");
2774 QTest::addColumn<QString>("expectedName");
2776 QTest::newRow("Document-node")
2777 << QString::fromLatin1("document{<elementName/>}")
2780 QTest::newRow("Element")
2781 << QString::fromLatin1("document{<elementName/>}/*")
2782 << QString::fromLatin1("elementName");
2786 Binding a null QString leads to no variable binding, but an
2787 empty non-null QString is possible.
2789 void tst_QXmlQuery::bindEmptyNullString() const
2791 MessageSilencer messageHandler;
2793 query.setMessageHandler(&messageHandler);
2794 query.setQuery(QLatin1String("declare variable $v external; $v"));
2795 /* Here, we effectively pass an invalid QVariant. */
2796 query.bindVariable(QLatin1String("v"), QVariant(QString()));
2797 QVERIFY(!query.isValid());
2800 QVERIFY(!query.evaluateTo(&result));
2803 void tst_QXmlQuery::bindEmptyString() const
2806 query.bindVariable(QLatin1String("v"), QVariant(QString(QLatin1String(""))));
2807 query.setQuery(QLatin1String("declare variable $v external; $v"));
2808 QVERIFY(query.isValid());
2811 QVERIFY(query.evaluateTo(&result));
2812 QStringList expected((QString()));
2813 QCOMPARE(result, expected);
2816 void tst_QXmlQuery::cleanupTestCase() const
2818 /* Remove a weird file we created. */
2819 const QString name(QLatin1String("nonReadableFile.xq"));
2821 if(QFile::exists(name))
2824 QVERIFY(file.setPermissions(QFile::WriteOwner));
2825 QVERIFY(file.remove());
2829 void tst_QXmlQuery::declareUnavailableExternal() const
2832 MessageSilencer silencer;
2833 query.setMessageHandler(&silencer);
2834 query.setQuery(QLatin1String("declare variable $var external;"
2836 /* We do not bind $var with QXmlQuery::bindVariable(). */
2837 QVERIFY(!query.isValid());
2841 This test triggers an assert in one of the cache iterator
2842 with MSVC 2005 when compiled in debug mode.
2844 void tst_QXmlQuery::msvcCacheIssue() const
2847 query.bindVariable(QLatin1String("externalVariable"), QXmlItem("Variable Value"));
2848 query.setQuery(QUrl::fromLocalFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariableUsedTwice.xq")));
2850 QVERIFY(query.evaluateTo(&result));
2853 QStringList() << QString::fromLatin1("Variable Value") << QString::fromLatin1("Variable Value"));
2856 void tst_QXmlQuery::unavailableExternalVariable() const
2860 MessageSilencer silencer;
2861 query.setMessageHandler(&silencer);
2863 query.setQuery(QLatin1String("declare variable $foo external; 1"));
2865 QVERIFY(!query.isValid());
2869 Ensure that setUriResolver() affects \c fn:doc() and \c fn:doc-available().
2871 void tst_QXmlQuery::useUriResolver() const
2873 class TestUriResolver : public QAbstractUriResolver
2874 , private TestFundament
2877 TestUriResolver() {}
2878 virtual QUrl resolve(const QUrl &relative,
2879 const QUrl &baseURI) const
2882 return baseURI.resolved(inputFile(QLatin1String(queriesDirectory) + QLatin1String("simpleDocument.xml")));
2886 const TestUriResolver uriResolver;
2889 query.setUriResolver(&uriResolver);
2890 query.setQuery(QLatin1String("let $i := 'http://www.example.com/DoesNotExist'"
2891 "return (string(doc($i)), doc-available($i))"));
2894 QXmlResultItems result;
2895 query.evaluateTo(&result);
2897 QVERIFY(!result.hasError());
2898 QCOMPARE(result.next().toAtomicValue().toString(), QString::fromLatin1("text text node"));
2899 QCOMPARE(result.next().toAtomicValue().toBool(), true);
2900 QVERIFY(result.next().isNull());
2901 QVERIFY(!result.hasError());
2904 void tst_QXmlQuery::queryWithFocusAndVariable() const
2907 query.setFocus(QXmlItem(5));
2908 query.bindVariable(QLatin1String("var"), QXmlItem(2));
2910 query.setQuery(QLatin1String("string(. * $var)"));
2914 QVERIFY(query.evaluateTo(&result));
2916 QCOMPARE(result, QStringList(QLatin1String("10")));
2919 void tst_QXmlQuery::undefinedFocus() const
2923 MessageSilencer silencer;
2924 query.setMessageHandler(&silencer);
2926 query.setQuery(QLatin1String("."));
2927 QVERIFY(!query.isValid());
2930 void tst_QXmlQuery::basicFocusUsage() const
2934 MessageSilencer silencer;
2935 query.setMessageHandler(&silencer);
2937 query.setFocus(QXmlItem(5));
2938 query.setQuery(QLatin1String("string(. * .)"));
2939 QVERIFY(query.isValid());
2942 QVERIFY(query.evaluateTo(&result));
2944 QCOMPARE(result, QStringList(QLatin1String("25")));
2948 Triggers an ownership related crash.
2950 void tst_QXmlQuery::copyCheckMessageHandler() const
2953 QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0));
2955 query.setQuery(QLatin1String("doc('qrc:/QXmlQueryTestData/data/oneElement.xml')"));
2956 /* By now, we should have set the builtin message handler. */
2957 const QAbstractMessageHandler *const messageHandler = query.messageHandler();
2958 QVERIFY(messageHandler);
2961 /* This copies QXmlQueryPrivate::m_ownerObject, and its destructor
2962 * will delete it, and hence the builtin message handler attached to it. */
2963 QXmlQuery copy(query);
2966 QXmlResultItems result;
2967 query.evaluateTo(&result);
2969 while(!result.next().isNull())
2972 QVERIFY(!result.hasError());
2975 void tst_QXmlQuery::queryLanguage() const
2977 /* Check default value. */
2979 const QXmlQuery query;
2980 QCOMPARE(query.queryLanguage(), QXmlQuery::XQuery10);
2983 /* Check default value of copies default instance. */
2985 const QXmlQuery query1;
2986 const QXmlQuery query2(query1);
2988 QCOMPARE(query1.queryLanguage(), QXmlQuery::XQuery10);
2989 QCOMPARE(query2.queryLanguage(), QXmlQuery::XQuery10);
2993 void tst_QXmlQuery::queryLanguageSignature() const
2995 /* This getter should be const. */
2997 query.queryLanguage();
3000 void tst_QXmlQuery::enumQueryLanguage() const
3002 /* These enum values should be possible to OR for future plans. */
3003 QCOMPARE(int(QXmlQuery::XQuery10), 1);
3004 QCOMPARE(int(QXmlQuery::XSLT20), 2);
3005 QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintSelector), 1024);
3006 QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintField), 2048);
3007 QCOMPARE(int(QXmlQuery::XPath20), 4096);
3010 void tst_QXmlQuery::setInitialTemplateNameQXmlName() const
3012 QXmlQuery query(QXmlQuery::XSLT20);
3013 QXmlNamePool np(query.namePool());
3014 const QXmlName name(np, QLatin1String("main"));
3016 query.setInitialTemplateName(name);
3018 QCOMPARE(query.initialTemplateName(), name);
3020 query.setQuery(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/namedTemplate.xsl"))));
3021 QVERIFY(query.isValid());
3024 QVERIFY(result.open(QIODevice::ReadWrite));
3025 QXmlSerializer serializer(query, &result);
3026 query.evaluateTo(&serializer);
3028 QCOMPARE(result.data(), QByteArray("1 2 3 4 5"));
3030 // TODO invoke a template which has required params.
3033 void tst_QXmlQuery::setInitialTemplateNameQXmlNameSignature() const
3036 QXmlNamePool np(query.namePool());
3037 const QXmlName name(np, QLatin1String("foo"));
3039 /* The signature should take a const reference. */
3040 query.setInitialTemplateName(name);
3043 void tst_QXmlQuery::setInitialTemplateNameQString() const
3046 QXmlNamePool np(query.namePool());
3047 query.setInitialTemplateName(QLatin1String("foo"));
3049 QCOMPARE(query.initialTemplateName(), QXmlName(np, QLatin1String("foo")));
3052 void tst_QXmlQuery::setInitialTemplateNameQStringSignature() const
3054 const QString name(QLatin1String("name"));
3057 /* We should take a const reference. */
3058 query.setInitialTemplateName(name);
3061 void tst_QXmlQuery::initialTemplateName() const
3063 /* Check our default value. */
3065 QCOMPARE(query.initialTemplateName(), QXmlName());
3066 QVERIFY(query.initialTemplateName().isNull());
3069 void tst_QXmlQuery::initialTemplateNameSignature() const
3071 const QXmlQuery query;
3072 /* This should be a const member. */
3073 query.initialTemplateName();
3076 void tst_QXmlQuery::setNetworkAccessManager() const
3079 /* Ensure fn:doc() picks up the right QNetworkAccessManager. */
3081 NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
3082 QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/queries/simpleDocument.xml"))));
3083 QVERIFY(networkOverrider.isValid());
3086 query.setNetworkAccessManager(&networkOverrider);
3087 query.setQuery(QLatin1String("string(doc('tag:example.com:DOESNOTEXIST'))"));
3088 QVERIFY(query.isValid());
3091 QVERIFY(query.evaluateTo(&result));
3093 QCOMPARE(result, QStringList(QLatin1String("text text node")));
3096 /* Ensure setQuery() is using the right network manager. */
3098 NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
3099 QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/queries/concat.xq"))));
3100 QVERIFY(networkOverrider.isValid());
3103 query.setNetworkAccessManager(&networkOverrider);
3104 query.setQuery(QUrl("tag:example.com:DOESNOTEXIST"));
3105 QVERIFY(query.isValid());
3108 QVERIFY(query.evaluateTo(&result));
3110 QCOMPARE(result, QStringList(QLatin1String("abcdef")));
3113 void tst_QXmlQuery::networkAccessManagerSignature() const
3116 const QXmlQuery query;
3118 /* The function should be const. */
3119 query.networkAccessManager();
3122 void tst_QXmlQuery::networkAccessManagerDefaultValue() const
3124 const QXmlQuery query;
3126 QCOMPARE(query.networkAccessManager(), static_cast<QNetworkAccessManager *>(0));
3129 void tst_QXmlQuery::networkAccessManager() const
3131 /* Test that we return the network manager that was set. */
3133 QNetworkAccessManager manager;
3135 query.setNetworkAccessManager(&manager);
3136 QCOMPARE(query.networkAccessManager(), &manager);
3144 1. Load a document into QXmlQuery's document cache, by executing a query which does it.
3146 3. Change query, to one which uses the focus
3151 void tst_QXmlQuery::multipleDocsAndFocus() const
3155 /* We use string concatenation, since variable bindings might disturb what
3157 query.setQuery(QLatin1String("string(doc('") +
3158 inputFile(QLatin1String(XMLPATTERNSDIR "/queries/simpleDocument.xml")) +
3159 QLatin1String("'))"));
3160 query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3161 query.setQuery(QLatin1String("string(.)"));
3164 QVERIFY(query.evaluateTo(&result));
3179 void tst_QXmlQuery::multipleEvaluationsWithDifferentFocus() const
3184 query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3185 query.setQuery(QLatin1String("string(.)"));
3186 QVERIFY(query.evaluateTo(&result));
3188 query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3189 QVERIFY(query.evaluateTo(&result));
3192 void tst_QXmlQuery::bindVariableQXmlQuery() const
3194 QFETCH(QString, query1);
3195 QFETCH(QString, query2);
3196 QFETCH(QString, expectedOutput);
3197 QFETCH(bool, expectedSuccess);
3199 MessageSilencer silencer;
3200 QXmlQuery xmlQuery1;
3201 xmlQuery1.setMessageHandler(&silencer);
3202 xmlQuery1.setQuery(query1);
3204 QXmlQuery xmlQuery2(xmlQuery1);
3205 xmlQuery2.bindVariable("query1", xmlQuery1);
3206 xmlQuery2.setQuery(query2);
3209 const bool querySuccess = xmlQuery2.evaluateTo(&output);
3211 QCOMPARE(querySuccess, expectedSuccess);
3214 QCOMPARE(output, expectedOutput);
3217 void tst_QXmlQuery::bindVariableQXmlQuery_data() const
3219 QTest::addColumn<QString>("query1");
3220 QTest::addColumn<QString>("query2");
3221 QTest::addColumn<QString>("expectedOutput");
3222 QTest::addColumn<bool>("expectedSuccess");
3224 QTest::newRow("First query has one atomic value.")
3230 QTest::newRow("First query has two atomic values.")
3236 QTest::newRow("First query is a node.")
3242 /* This is a good test, because it triggers the exception in the
3243 * bindVariable() call, as supposed to when the actual evaluation is done.
3245 QTest::newRow("First query has a dynamic error.")
3248 << QString() /* We don't care. */
3252 void tst_QXmlQuery::bindVariableQStringQXmlQuerySignature() const
3255 query1.setQuery("'dummy'");
3258 const QString name(QLatin1String("name"));
3260 /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
3261 * QXmlQuery, and evaluation is what we do here. */
3262 query2.bindVariable(name, const_cast<const QXmlQuery &>(query1));
3265 void tst_QXmlQuery::bindVariableQXmlNameQXmlQuerySignature() const
3268 QXmlQuery query1(np);
3269 query1.setQuery("'dummy'");
3272 const QXmlName name(np, QLatin1String("name"));
3274 /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
3275 * QXmlQuery, and evaluation is what we do here. */
3276 query2.bindVariable(name, const_cast<const QXmlQuery &>(query1));
3280 Check that the QXmlName is handled correctly.
3282 void tst_QXmlQuery::bindVariableQXmlNameQXmlQuery() const
3286 query1.setQuery(QLatin1String("1"));
3288 QXmlQuery query2(np);
3289 query2.bindVariable(QXmlName(np, QLatin1String("theName")), query1);
3290 query2.setQuery("$theName");
3293 query2.evaluateTo(&result);
3295 QCOMPARE(result, QString::fromLatin1("1\n"));
3298 void tst_QXmlQuery::bindVariableQXmlQueryInvalidate() const
3301 query.bindVariable(QLatin1String("name"), QVariant(1));
3302 query.setQuery("$name");
3303 QVERIFY(query.isValid());
3306 query2.setQuery("'query2'");
3308 query.bindVariable(QLatin1String("name"), query);
3309 QVERIFY(!query.isValid());
3312 void tst_QXmlQuery::unknownSourceLocation() const
3315 b.setData("<a><b/><b/></a>");
3316 b.open(QIODevice::ReadOnly);
3318 MessageSilencer silencer;
3320 query.bindVariable(QLatin1String("inputDocument"), &b);
3321 query.setMessageHandler(&silencer);
3323 query.setQuery(QLatin1String("doc($inputDocument)/a/(let $v := b/string() return if ($v) then $v else ())"));
3326 query.evaluateTo(&output);
3329 void tst_QXmlQuery::identityConstraintSuccess() const
3331 QXmlQuery::QueryLanguage queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintSelector;
3333 /* We run this code for Selector and Field. */
3334 for(int i = 0; i < 3; ++i)
3336 QXmlNamePool namePool;
3337 QXmlResultItems result;
3341 QXmlQuery nodeSource(namePool);
3342 nodeSource.setQuery(QLatin1String("<e/>"));
3344 nodeSource.evaluateTo(&result);
3345 node = result.next();
3349 * 1. The focus is undefined, but it's still valid.
3350 * 2. We never evaluate. */
3352 QXmlQuery query(queryLanguage);
3353 query.setQuery(QLatin1String("a"));
3354 QVERIFY(query.isValid());
3358 * 1. The focus is undefined, but it's still valid.
3359 * 2. We afterwards set the focus. */
3361 QXmlQuery query(queryLanguage, namePool);
3362 query.setQuery(QLatin1String("a"));
3363 query.setFocus(node);
3364 QVERIFY(query.isValid());
3368 * 1. The focus is undefined, but it's still valid.
3369 * 2. We afterwards set the focus.
3370 * 3. We evaluate. */
3372 QXmlQuery query(queryLanguage, namePool);
3373 query.setQuery(QString(QLatin1Char('.')));
3374 query.setFocus(node);
3375 QVERIFY(query.isValid());
3378 QVERIFY(query.evaluateTo(&result));
3379 QCOMPARE(result, QString::fromLatin1("<e/>\n"));
3382 /* A slightly more complex Field. */
3384 QXmlQuery query(queryLanguage);
3385 query.setQuery(QLatin1String("* | .//xml:*/."));
3386 QVERIFY(query.isValid());
3389 /* @ is only allowed in Field. */
3390 if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField)
3392 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField);
3393 query.setQuery(QLatin1String("@abc"));
3394 QVERIFY(query.isValid());
3397 /* Field allows attribute:: and child:: .*/
3398 if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField)
3400 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField);
3401 query.setQuery(QLatin1String("attribute::name | child::name"));
3402 QVERIFY(query.isValid());
3405 /* Selector allows only child:: .*/
3407 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintSelector);
3408 query.setQuery(QLatin1String("child::name"));
3409 QVERIFY(query.isValid());
3413 queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintField;
3415 queryLanguage = QXmlQuery::XPath20;
3419 Q_DECLARE_METATYPE(QXmlQuery::QueryLanguage);
3422 We just do some basic tests for boot strapping and sanity checking. The actual regression
3423 testing is in the Schema suite.
3425 void tst_QXmlQuery::identityConstraintFailure() const
3427 QFETCH(QXmlQuery::QueryLanguage, queryLanguage);
3428 QFETCH(QString, inputQuery);
3430 QXmlQuery query(queryLanguage);
3431 MessageSilencer silencer;
3432 query.setMessageHandler(&silencer);
3434 query.setQuery(inputQuery);
3435 QVERIFY(!query.isValid());
3438 void tst_QXmlQuery::identityConstraintFailure_data() const
3440 QTest::addColumn<QXmlQuery::QueryLanguage>("queryLanguage");
3441 QTest::addColumn<QString>("inputQuery");
3443 QTest::newRow("We don't have element constructors in identity constraint pattern, "
3444 "it's an XQuery feature(Selector).")
3445 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3446 << QString::fromLatin1("<e/>");
3448 QTest::newRow("We don't have functions in identity constraint pattern, "
3449 "it's an XPath feature(Selector).")
3450 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3451 << QString::fromLatin1("current-time()");
3453 QTest::newRow("We don't have element constructors in identity constraint pattern, "
3454 "it's an XQuery feature(Field).")
3455 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3456 << QString::fromLatin1("<e/>");
3458 QTest::newRow("We don't have functions in identity constraint pattern, "
3459 "it's an XPath feature(Field).")
3460 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3461 << QString::fromLatin1("current-time()");
3463 QTest::newRow("@attributeName is disallowed for the selector.")
3464 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3465 << QString::fromLatin1("@abc");
3467 QTest::newRow("attribute:: is disallowed for the selector.")
3468 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3469 << QString::fromLatin1("attribute::name");
3471 QTest::newRow("ancestor::name is disallowed for the selector.")
3472 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3473 << QString::fromLatin1("ancestor::name");
3475 QTest::newRow("ancestor::name is disallowed for the field.")
3476 << QXmlQuery::XmlSchema11IdentityConstraintField
3477 << QString::fromLatin1("ancestor::name");
3480 QTEST_MAIN(tst_QXmlQuery)
3482 #include "tst_qxmlquery.moc"