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 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);
2023 QVERIFY(query.evaluateTo(&serializer));
2025 QCOMPARE(result, expectedOutput);
2028 void tst_QXmlQuery::fnDocNetworkAccessSuccess_data() const
2030 QTest::addColumn<QUrl>("uriToOpen");
2031 QTest::addColumn<QByteArray>("expectedOutput");
2033 QTest::newRow("file scheme")
2034 << inputFileAsURI(QLatin1String(SRCDIR "input.xml"))
2035 << QByteArray("<!-- This is just a file for testing. --><input/>");
2037 QTest::newRow("data scheme with ASCII")
2038 /* QUrl::toPercentEncoding(QLatin1String("<e/>")) yields "%3Ce%2F%3E". */
2039 << QUrl::fromEncoded("data:application/xml,%3Ce%2F%3E")
2040 << QByteArray("<e/>");
2042 QTest::newRow("data scheme with ASCII no MIME type")
2043 << QUrl::fromEncoded("data:,%3Ce%2F%3E")
2044 << QByteArray("<e/>");
2046 QTest::newRow("data scheme with base 64")
2047 << QUrl::fromEncoded("data:application/xml;base64,PGUvPg==")
2048 << QByteArray("<e/>");
2050 QTest::newRow("qrc scheme")
2051 << QUrl::fromEncoded("qrc:/QXmlQueryTestData/data/oneElement.xml")
2052 << QByteArray("<oneElement/>");
2057 QTest::newRow("http scheme")
2058 << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/wellFormed.xml"))
2059 << QByteArray("<!-- a comment --><e from=\"http\">Some Text</e>");
2061 QTest::newRow("ftp scheme")
2062 << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/wellFormed.xml"))
2063 << QByteArray("<!-- a comment --><e from=\"ftp\">Some Text</e>");
2067 void tst_QXmlQuery::fnDocNetworkAccessFailure() const
2069 QFETCH(QUrl, uriToOpen);
2071 QVERIFY(uriToOpen.isValid());
2074 MessageSilencer silencer;
2075 query.setMessageHandler(&silencer);
2076 query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen));
2077 query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)"));
2078 QVERIFY(query.isValid());
2080 QXmlResultItems result;
2081 query.evaluateTo(&result);
2083 while(!result.next().isNull())
2085 /* Just loop until the end. */
2088 // TODO do something that triggers a /timeout/.
2089 QVERIFY(result.hasError());
2092 void tst_QXmlQuery::fnDocNetworkAccessFailure_data() const
2094 QTest::addColumn<QUrl>("uriToOpen");
2096 QTest::newRow("data scheme, not-well-formed")
2097 << QUrl(QLatin1String("data:application/xml;base64,PGUvg==="));
2099 QTest::newRow("file scheme, non-existant file")
2100 << QUrl(QLatin1String("file:///example.com/does/notExist.xml"));
2102 QTest::newRow("http scheme, file not found")
2103 << QUrl(QLatin1String("http://www.example.com/does/not/exist.xml"));
2105 QTest::newRow("http scheme, nonexistent host")
2106 << QUrl(QLatin1String("http://this.host.does.not.exist.I.SWear"));
2108 QTest::newRow("qrc scheme, not well-formed")
2109 << QUrl(QLatin1String("qrc:/QXmlQueryTestData/notWellformed.xml"));
2111 QTest::newRow("'qrc:/', non-existing file")
2112 << QUrl(QLatin1String(":/QXmlQueryTestData/data/thisFileDoesNotExist.xml"));
2114 QTest::newRow("':/', this scheme is not supported")
2115 << QUrl(QLatin1String(":/QXmlQueryTestData/data/notWellformed.xml"));
2120 QTest::newRow("http scheme, not well-formed")
2121 << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/notWellformed.xml"));
2123 QTest::newRow("https scheme, not well-formed")
2124 << QUrl(QString("https://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/notWellformedViaHttps.xml"));
2126 QTest::newRow("https scheme, nonexistent host")
2127 << QUrl(QLatin1String("https://this.host.does.not.exist.I.SWear"));
2129 QTest::newRow("ftp scheme, nonexistent host")
2130 << QUrl(QLatin1String("ftp://this.host.does.not.exist.I.SWear"));
2132 QTest::newRow("ftp scheme, not well-formed")
2133 << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/notWellFormed.xml"));
2137 Create a network timeout from a QIODevice binding such
2138 that we ensure we don't hang infinitely.
2140 void tst_QXmlQuery::fnDocOnQIODeviceTimeout() const
2146 server.listen(QHostAddress::LocalHost, 1088);
2149 client.connectToHost("localhost", 1088);
2150 QVERIFY(client.isReadable());
2154 MessageSilencer silencer;
2155 query.setMessageHandler(&silencer);
2157 query.bindVariable(QLatin1String("inDevice"), &client);
2158 query.setQuery(QLatin1String("declare variable $inDevice external;\ndoc($inDevice)"));
2159 QVERIFY(query.isValid());
2161 QXmlResultItems result;
2162 query.evaluateTo(&result);
2163 QXmlItem next(result.next());
2165 while(!next.isNull())
2167 next = result.next();
2170 QVERIFY(result.hasError());
2174 When changing query, the static context must change too, such that
2175 the source locations are updated.
2177 void tst_QXmlQuery::recompilationWithEvaluateToResultFailing() const
2180 MessageSilencer silencer;
2181 query.setMessageHandler(&silencer);
2183 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2184 QVERIFY(query.isValid()); /* Trigger query compilation. */
2186 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2187 QVERIFY(query.isValid()); /* Trigger second compilation. */
2189 QXmlResultItems items;
2190 query.evaluateTo(&items);
2192 QVERIFY(items.hasError());
2195 void tst_QXmlQuery::secondEvaluationWithEvaluateToResultFailing() const
2198 MessageSilencer silencer;
2199 query.setMessageHandler(&silencer);
2201 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2202 QVERIFY(query.isValid()); /* Trigger query compilation. */
2204 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2205 /* We don't call isValid(). */
2206 QXmlResultItems items;
2207 query.evaluateTo(&items);
2209 QVERIFY(items.hasError());
2213 Compilation is triggered in the evaluation function due to no call to QXmlQuery::isValid().
2215 void tst_QXmlQuery::recompilationWithEvaluateToReceiver() const
2218 MessageSilencer silencer;
2219 query.setMessageHandler(&silencer);
2221 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2222 QVERIFY(query.isValid()); /* Trigger query compilation. */
2224 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2225 /* We don't call isValid(). */
2228 QBuffer buffer(&dummy);
2229 buffer.open(QIODevice::WriteOnly);
2231 QXmlSerializer serializer(query, &buffer);
2233 QVERIFY(!query.evaluateTo(&serializer));
2236 void tst_QXmlQuery::evaluateToQStringListOnInvalidQuery() const
2238 MessageSilencer silencer;
2240 /* Invoke on a default constructed object. */
2244 QVERIFY(!query.evaluateTo(&out));
2247 /* Invoke on a syntactically invalid query. */
2251 MessageSilencer silencer;
2253 query.setMessageHandler(&silencer);
2254 query.setQuery(QLatin1String("1 + "));
2256 QVERIFY(!query.evaluateTo(&out));
2259 /* Invoke on a query with the wrong type, one atomic. */
2264 query.setQuery(QLatin1String("1"));
2265 query.setMessageHandler(&silencer);
2266 QVERIFY(!query.evaluateTo(&out));
2269 /* Invoke on a query with the wrong type, one element. */
2274 query.setQuery(QLatin1String("<e/>"));
2275 QVERIFY(!query.evaluateTo(&out));
2278 /* Invoke on a query with the wrong type, mixed nodes & atomics. */
2283 query.setQuery(QLatin1String("<e/>, 1, 'a string'"));
2284 query.setMessageHandler(&silencer);
2285 QVERIFY(!query.evaluateTo(&out));
2288 /* Evaluate the empty sequence. */
2293 query.setQuery(QLatin1String("()"));
2294 QVERIFY(!query.evaluateTo(&out));
2295 QVERIFY(out.isEmpty());
2299 void tst_QXmlQuery::evaluateToQStringList() const
2301 QFETCH(QString, queryString);
2302 QFETCH(QStringList, expectedOutput);
2305 query.setQuery(queryString);
2307 QVERIFY(query.isValid());
2309 QVERIFY(query.evaluateTo(&out));
2311 QCOMPARE(out, expectedOutput);
2314 void tst_QXmlQuery::evaluateToQStringListTriggerWarnings() const
2318 QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed.");
2319 QCOMPARE(query.evaluateTo(static_cast<QStringList *>(0)),
2323 void tst_QXmlQuery::evaluateToQStringList_data() const
2325 QTest::addColumn<QString>("queryString");
2326 QTest::addColumn<QStringList>("expectedOutput");
2328 QTest::newRow("One atomic")
2329 << QString::fromLatin1("(1 + 1) cast as xs:string")
2330 << QStringList(QString::fromLatin1("2"));
2333 QStringList expected;
2334 expected << QLatin1String("2");
2335 expected << QLatin1String("a string");
2337 QTest::newRow("Two atomics")
2338 << QString::fromLatin1("(1 + 1) cast as xs:string, 'a string'")
2342 QTest::newRow("A query which evaluates to sub-types of xs:string.")
2343 << QString::fromLatin1("xs:NCName('NCName'), xs:normalizedString(' a b c ')")
2344 << (QStringList() << QString::fromLatin1("NCName")
2345 << QString::fromLatin1(" a b c "));
2347 QTest::newRow("A query which evaluates to two elements.")
2348 << QString::fromLatin1("string(<e>theString1</e>), string(<e>theString2</e>)")
2349 << (QStringList() << QString::fromLatin1("theString1")
2350 << QString::fromLatin1("theString2"));
2354 Ensure that we don't automatically convert non-xs:string values.
2356 void tst_QXmlQuery::evaluateToQStringListNoConversion() const
2359 query.setQuery(QString::fromLatin1("<e/>"));
2360 QVERIFY(query.isValid());
2362 QVERIFY(!query.evaluateTo(&result));
2365 void tst_QXmlQuery::evaluateToQIODevice() const
2367 /* an XQuery, check that no indentation is performed. */
2370 QVERIFY(out.open(QIODevice::ReadWrite));
2373 query.setQuery(QLatin1String("<a><b/></a>"));
2374 QVERIFY(query.isValid());
2375 QVERIFY(query.evaluateTo(&out));
2376 QCOMPARE(out.data(), QByteArray("<a><b/></a>"));
2380 void tst_QXmlQuery::evaluateToQIODeviceTriggerWarnings() const
2384 QTest::ignoreMessage(QtWarningMsg, "The pointer to the device cannot be null.");
2385 QCOMPARE(query.evaluateTo(static_cast<QIODevice *>(0)),
2390 QTest::ignoreMessage(QtWarningMsg, "The device must be writable.");
2391 QCOMPARE(query.evaluateTo(&buffer),
2395 void tst_QXmlQuery::evaluateToQIODeviceSignature() const
2397 /* The function should be const. */
2400 QVERIFY(out.open(QIODevice::ReadWrite));
2402 const QXmlQuery query;
2404 query.evaluateTo(&out);
2408 void tst_QXmlQuery::evaluateToQIODeviceOnInvalidQuery() const
2411 QVERIFY(out.open(QIODevice::WriteOnly));
2413 /* On syntactically invalid query. */
2416 MessageSilencer silencer;
2417 query.setMessageHandler(&silencer);
2418 query.setQuery(QLatin1String("1 +"));
2419 QVERIFY(!query.isValid());
2420 QVERIFY(!query.evaluateTo(&out));
2423 /* On null QXmlQuery instance. */
2426 QVERIFY(!query.evaluateTo(&out));
2431 void tst_QXmlQuery::setQueryQIODeviceQUrl() const
2436 buffer.setData("1, 2, 2 + 1");
2437 QVERIFY(buffer.open(QIODevice::ReadOnly));
2440 query.setQuery(&buffer);
2441 QVERIFY(query.isValid());
2443 QXmlResultItems result;
2444 query.evaluateTo(&result);
2445 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
2446 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
2447 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
2448 QVERIFY(result.next().isNull());
2449 QVERIFY(!result.hasError());
2452 /* Set query that is invalid. */
2455 buffer.setData("1, ");
2456 QVERIFY(buffer.open(QIODevice::ReadOnly));
2459 MessageSilencer silencer;
2460 query.setMessageHandler(&silencer);
2461 query.setQuery(&buffer);
2462 QVERIFY(!query.isValid());
2465 /* Check that the base URI passes through. */
2468 buffer.setData("string(static-base-uri())");
2469 QVERIFY(buffer.open(QIODevice::ReadOnly));
2472 query.setQuery(&buffer, QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl"));
2473 QVERIFY(query.isValid());
2476 query.evaluateTo(&result);
2477 QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
2481 void tst_QXmlQuery::setQueryQIODeviceQUrlTriggerWarnings() const
2484 QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed.");
2488 QTest::ignoreMessage(QtWarningMsg, "The device must be readable.");
2489 query.setQuery(&buffer);
2492 void tst_QXmlQuery::setQueryQString() const
2497 query.setQuery(QLatin1String("1, 2, 2 + 1"));
2498 QVERIFY(query.isValid());
2500 QXmlResultItems result;
2501 query.evaluateTo(&result);
2502 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
2503 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
2504 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
2505 QVERIFY(result.next().isNull());
2506 QVERIFY(!result.hasError());
2509 /* Set query that is invalid. */
2511 MessageSilencer silencer;
2513 query.setMessageHandler(&silencer);
2514 query.setQuery(QLatin1String("1, "));
2515 QVERIFY(!query.isValid());
2518 /* Check that the base URI passes through. */
2521 query.setQuery(QLatin1String("string(static-base-uri())"), QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl"));
2522 QVERIFY(query.isValid());
2525 query.evaluateTo(&result);
2526 QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
2530 void tst_QXmlQuery::setQueryQUrlSuccess() const
2532 #if defined(Q_OS_WINCE) && !defined(_X86_)
2533 QStringList testsToSkip;
2534 testsToSkip << "A valid query via the ftp scheme" << "A valid query via the http scheme";
2535 if (testsToSkip.contains(QTest::currentDataTag()))
2536 QSKIP("Network tests are currently unsupported on Windows CE.");
2539 QFETCH(QUrl, queryURI);
2540 QFETCH(QByteArray, expectedOutput);
2542 QVERIFY(queryURI.isValid());
2546 MessageSilencer silencer;
2547 query.setMessageHandler(&silencer);
2549 query.setQuery(queryURI);
2550 QVERIFY(query.isValid());
2553 QBuffer buffer(&out);
2554 QVERIFY(buffer.open(QIODevice::WriteOnly));
2555 QXmlSerializer serializer(query, &buffer);
2557 query.evaluateTo(&serializer);
2558 QCOMPARE(out, expectedOutput);
2561 void tst_QXmlQuery::setQueryQUrlSuccess_data() const
2563 QTest::addColumn<QUrl>("queryURI");
2564 QTest::addColumn<QByteArray>("expectedOutput");
2566 QTest::newRow("A valid query via the data scheme")
2567 << QUrl::fromEncoded("data:application/xml,1%20%2B%201") /* "1 + 1" */
2570 QTest::newRow("A valid query via the file scheme")
2571 << QUrl::fromLocalFile(inputFile(QLatin1String(queriesDirectory) + QLatin1String("onePlusOne.xq")))
2577 QTest::newRow("A valid query via the ftp scheme")
2578 << QUrl::fromEncoded(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/viaFtp.xq").toLatin1())
2579 << QByteArray("This was received via FTP");
2581 QTest::newRow("A valid query via the http scheme")
2582 << QUrl::fromEncoded(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/viaHttp.xq").toLatin1())
2583 << QByteArray("This was received via HTTP.");
2586 void tst_QXmlQuery::setQueryQUrlFailSucceed() const
2589 MessageSilencer silencer;
2591 query.setMessageHandler(&silencer);
2593 query.setQuery(QLatin1String("1 + 1"));
2594 QVERIFY(query.isValid());
2596 query.setQuery(QUrl::fromEncoded("file://example.com/does/not/exist"));
2597 QVERIFY(!query.isValid());
2600 void tst_QXmlQuery::setQueryQUrlFailure() const
2602 QFETCH(QUrl, queryURI);
2604 MessageSilencer silencer;
2607 query.setMessageHandler(&silencer);
2608 query.setQuery(queryURI);
2609 QVERIFY(!query.isValid());
2612 void tst_QXmlQuery::setQueryQUrlFailure_data() const
2614 QTest::addColumn<QUrl>("queryURI");
2616 QTest::newRow("Query via file:// that does not exist.")
2617 << QUrl::fromEncoded("file://example.com/does/not/exist");
2619 QTest::newRow("A query via file:// that is completely empty, but readable.")
2620 << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("../xmlpatterns/queries/completelyEmptyQuery.xq"));
2623 const QString name(QLatin1String("nonReadableFile.xq"));
2624 QFile outFile(name);
2625 QVERIFY(outFile.open(QIODevice::WriteOnly));
2626 outFile.write(QByteArray("1"));
2628 /* On some windows versions, this fails, so we don't check that this works with QVERIFY. */
2629 outFile.setPermissions(QFile::Permissions(QFile::Permissions()));
2631 QTest::newRow("Query via file:/ that does not have read permissions.")
2632 << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("nonReadableFile.xq"));
2638 QTest::newRow("Query via HTTP that does not exist.")
2639 << QUrl::fromEncoded("http://example.com/NoQuery/ISWear");
2642 QTest::newRow("Query via FTP that does not exist.")
2643 << QUrl::fromEncoded("ftp://example.com/NoQuery/ISWear");
2646 QTest::newRow("A query via http:// that is completely empty, but readable.")
2647 << QUrl::fromEncoded(QString(
2648 "http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/completelyEmptyQuery.xq").toLatin1());
2650 QTest::newRow("A query via ftp:// that is completely empty, but readable.")
2651 << QUrl::fromEncoded(QString(
2652 "ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/completelyEmptyQuery.xq").toLatin1());
2656 void tst_QXmlQuery::setQueryQUrlBaseURI() const
2658 QFETCH(QUrl, inputBaseURI);
2659 QFETCH(QUrl, expectedBaseURI);
2663 query.setQuery(QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq")), inputBaseURI);
2664 QVERIFY(query.isValid());
2667 QVERIFY(query.evaluateTo(&result));
2668 QCOMPARE(result.count(), 1);
2670 if(qstrcmp(QTest::currentDataTag(), "Relative base URI") == 0)
2671 checkBaseURI(QUrl(result.first()), QCoreApplication::applicationFilePath());
2673 QCOMPARE(result.first(), expectedBaseURI.toString());
2676 void tst_QXmlQuery::setQueryQUrlBaseURI_data() const
2678 QTest::addColumn<QUrl>("inputBaseURI");
2679 QTest::addColumn<QUrl>("expectedBaseURI");
2681 QTest::newRow("absolute HTTP")
2682 << QUrl(QLatin1String("http://www.example.com/"))
2683 << QUrl(QLatin1String("http://www.example.com/"));
2685 QTest::newRow("None, so the query URI is used")
2687 << QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq"));
2689 QTest::newRow("Relative base URI")
2690 << QUrl(QLatin1String("../data/relative.uri"))
2695 1. Create a valid query.
2696 2. Call setQuery(QUrl), with a query file that doesn't exist.
2697 3. Verify that the query has changed state into invalid.
2699 void tst_QXmlQuery::setQueryWithNonExistentQUrlOnValidQuery() const
2703 MessageSilencer messageSilencer;
2704 query.setMessageHandler(&messageSilencer);
2706 query.setQuery(QLatin1String("1 + 1"));
2707 QVERIFY(query.isValid());
2709 query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/DOESNOTEXIST.xq"));
2710 QVERIFY(!query.isValid());
2714 1. Create a valid query.
2715 2. Call setQuery(QUrl), with a query file that is invalid.
2716 3. Verify that the query has changed state into invalid.
2718 void tst_QXmlQuery::setQueryWithInvalidQueryFromQUrlOnValidQuery() const
2722 MessageSilencer messageSilencer;
2723 query.setMessageHandler(&messageSilencer);
2725 query.setQuery(QLatin1String("1 + 1"));
2726 QVERIFY(query.isValid());
2728 query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/queries/syntaxError.xq"));
2729 QVERIFY(!query.isValid());
2733 This triggered two bugs:
2735 - First, the DynamicContext wasn't assigned to QXmlResultItems, meaning it went out of
2736 scope and therefore deallocated the document pool, and calls
2737 to QXmlResultItems::next() would use dangling pointers.
2739 - Conversion between QPatternist::Item and QXmlItem was incorrectly done, leading to nodes
2740 being treated as atomic values, and subsequent crashes.
2743 void tst_QXmlQuery::retrieveNameFromQuery() const
2745 QFETCH(QString, queryString);
2746 QFETCH(QString, expectedName);
2749 query.setQuery(queryString);
2750 QVERIFY(query.isValid());
2751 QXmlResultItems result;
2752 query.evaluateTo(&result);
2754 QVERIFY(!result.hasError());
2756 const QXmlItem item(result.next());
2757 QVERIFY(!result.hasError());
2758 QVERIFY(!item.isNull());
2759 QVERIFY(item.isNode());
2761 const QXmlNodeModelIndex node(item.toNodeModelIndex());
2762 QVERIFY(!node.isNull());
2764 QCOMPARE(node.model()->name(node).localName(query.namePool()), expectedName);
2767 void tst_QXmlQuery::retrieveNameFromQuery_data() const
2769 QTest::addColumn<QString>("queryString");
2770 QTest::addColumn<QString>("expectedName");
2772 QTest::newRow("Document-node")
2773 << QString::fromLatin1("document{<elementName/>}")
2776 QTest::newRow("Element")
2777 << QString::fromLatin1("document{<elementName/>}/*")
2778 << QString::fromLatin1("elementName");
2782 Binding a null QString leads to no variable binding, but an
2783 empty non-null QString is possible.
2785 void tst_QXmlQuery::bindEmptyNullString() const
2787 MessageSilencer messageHandler;
2789 query.setMessageHandler(&messageHandler);
2790 query.setQuery(QLatin1String("declare variable $v external; $v"));
2791 /* Here, we effectively pass an invalid QVariant. */
2792 query.bindVariable(QLatin1String("v"), QVariant(QString()));
2793 QVERIFY(!query.isValid());
2796 QVERIFY(!query.evaluateTo(&result));
2799 void tst_QXmlQuery::bindEmptyString() const
2802 query.bindVariable(QLatin1String("v"), QVariant(QString(QLatin1String(""))));
2803 query.setQuery(QLatin1String("declare variable $v external; $v"));
2804 QVERIFY(query.isValid());
2807 QVERIFY(query.evaluateTo(&result));
2808 QStringList expected((QString()));
2809 QCOMPARE(result, expected);
2812 void tst_QXmlQuery::cleanupTestCase() const
2814 /* Remove a weird file we created. */
2815 const QString name(QLatin1String("nonReadableFile.xq"));
2817 if(QFile::exists(name))
2820 QVERIFY(file.setPermissions(QFile::WriteOwner));
2821 QVERIFY(file.remove());
2825 void tst_QXmlQuery::declareUnavailableExternal() const
2828 MessageSilencer silencer;
2829 query.setMessageHandler(&silencer);
2830 query.setQuery(QLatin1String("declare variable $var external;"
2832 /* We do not bind $var with QXmlQuery::bindVariable(). */
2833 QVERIFY(!query.isValid());
2837 This test triggers an assert in one of the cache iterator
2838 with MSVC 2005 when compiled in debug mode.
2840 void tst_QXmlQuery::msvcCacheIssue() const
2843 query.bindVariable(QLatin1String("externalVariable"), QXmlItem("Variable Value"));
2844 query.setQuery(QUrl::fromLocalFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariableUsedTwice.xq")));
2846 QVERIFY(query.evaluateTo(&result));
2849 QStringList() << QString::fromLatin1("Variable Value") << QString::fromLatin1("Variable Value"));
2852 void tst_QXmlQuery::unavailableExternalVariable() const
2856 MessageSilencer silencer;
2857 query.setMessageHandler(&silencer);
2859 query.setQuery(QLatin1String("declare variable $foo external; 1"));
2861 QVERIFY(!query.isValid());
2865 Ensure that setUriResolver() affects \c fn:doc() and \c fn:doc-available().
2867 void tst_QXmlQuery::useUriResolver() const
2869 class TestUriResolver : public QAbstractUriResolver
2870 , private TestFundament
2873 TestUriResolver() {}
2874 virtual QUrl resolve(const QUrl &relative,
2875 const QUrl &baseURI) const
2878 QString fixedInputFile = inputFile(QLatin1String(queriesDirectory) + QLatin1String("simpleDocument.xml"));
2880 // A file path with drive letter is not a valid relative URI, so remove the drive letter.
2881 // Note that can't just use inputFileAsURI() instead of inputFile() as that doesn't
2882 // produce a relative URI either.
2883 if (fixedInputFile.size() > 1 && fixedInputFile.at(1) == QLatin1Char(':'))
2884 fixedInputFile.remove(0, 2);
2886 return baseURI.resolved(fixedInputFile);
2890 const TestUriResolver uriResolver;
2893 query.setUriResolver(&uriResolver);
2894 query.setQuery(QLatin1String("let $i := 'http://www.example.com/DoesNotExist'"
2895 "return (string(doc($i)), doc-available($i))"));
2898 QXmlResultItems result;
2899 query.evaluateTo(&result);
2901 QVERIFY(!result.hasError());
2902 QCOMPARE(result.next().toAtomicValue().toString(), QString::fromLatin1("text text node"));
2903 QCOMPARE(result.next().toAtomicValue().toBool(), true);
2904 QVERIFY(result.next().isNull());
2905 QVERIFY(!result.hasError());
2908 void tst_QXmlQuery::queryWithFocusAndVariable() const
2911 query.setFocus(QXmlItem(5));
2912 query.bindVariable(QLatin1String("var"), QXmlItem(2));
2914 query.setQuery(QLatin1String("string(. * $var)"));
2918 QVERIFY(query.evaluateTo(&result));
2920 QCOMPARE(result, QStringList(QLatin1String("10")));
2923 void tst_QXmlQuery::undefinedFocus() const
2927 MessageSilencer silencer;
2928 query.setMessageHandler(&silencer);
2930 query.setQuery(QLatin1String("."));
2931 QVERIFY(!query.isValid());
2934 void tst_QXmlQuery::basicFocusUsage() const
2938 MessageSilencer silencer;
2939 query.setMessageHandler(&silencer);
2941 query.setFocus(QXmlItem(5));
2942 query.setQuery(QLatin1String("string(. * .)"));
2943 QVERIFY(query.isValid());
2946 QVERIFY(query.evaluateTo(&result));
2948 QCOMPARE(result, QStringList(QLatin1String("25")));
2952 Triggers an ownership related crash.
2954 void tst_QXmlQuery::copyCheckMessageHandler() const
2957 QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0));
2959 query.setQuery(QLatin1String("doc('qrc:/QXmlQueryTestData/data/oneElement.xml')"));
2960 /* By now, we should have set the builtin message handler. */
2961 const QAbstractMessageHandler *const messageHandler = query.messageHandler();
2962 QVERIFY(messageHandler);
2965 /* This copies QXmlQueryPrivate::m_ownerObject, and its destructor
2966 * will delete it, and hence the builtin message handler attached to it. */
2967 QXmlQuery copy(query);
2970 QXmlResultItems result;
2971 query.evaluateTo(&result);
2973 while(!result.next().isNull())
2976 QVERIFY(!result.hasError());
2979 void tst_QXmlQuery::queryLanguage() const
2981 /* Check default value. */
2983 const QXmlQuery query;
2984 QCOMPARE(query.queryLanguage(), QXmlQuery::XQuery10);
2987 /* Check default value of copies default instance. */
2989 const QXmlQuery query1;
2990 const QXmlQuery query2(query1);
2992 QCOMPARE(query1.queryLanguage(), QXmlQuery::XQuery10);
2993 QCOMPARE(query2.queryLanguage(), QXmlQuery::XQuery10);
2997 void tst_QXmlQuery::queryLanguageSignature() const
2999 /* This getter should be const. */
3001 query.queryLanguage();
3004 void tst_QXmlQuery::enumQueryLanguage() const
3006 /* These enum values should be possible to OR for future plans. */
3007 QCOMPARE(int(QXmlQuery::XQuery10), 1);
3008 QCOMPARE(int(QXmlQuery::XSLT20), 2);
3009 QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintSelector), 1024);
3010 QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintField), 2048);
3011 QCOMPARE(int(QXmlQuery::XPath20), 4096);
3014 void tst_QXmlQuery::setInitialTemplateNameQXmlName() const
3016 QXmlQuery query(QXmlQuery::XSLT20);
3017 QXmlNamePool np(query.namePool());
3018 const QXmlName name(np, QLatin1String("main"));
3020 query.setInitialTemplateName(name);
3022 QCOMPARE(query.initialTemplateName(), name);
3024 query.setQuery(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/namedTemplate.xsl"))));
3025 QVERIFY(query.isValid());
3028 QVERIFY(result.open(QIODevice::ReadWrite));
3029 QXmlSerializer serializer(query, &result);
3030 query.evaluateTo(&serializer);
3032 QCOMPARE(result.data(), QByteArray("1 2 3 4 5"));
3034 // TODO invoke a template which has required params.
3037 void tst_QXmlQuery::setInitialTemplateNameQXmlNameSignature() const
3040 QXmlNamePool np(query.namePool());
3041 const QXmlName name(np, QLatin1String("foo"));
3043 /* The signature should take a const reference. */
3044 query.setInitialTemplateName(name);
3047 void tst_QXmlQuery::setInitialTemplateNameQString() const
3050 QXmlNamePool np(query.namePool());
3051 query.setInitialTemplateName(QLatin1String("foo"));
3053 QCOMPARE(query.initialTemplateName(), QXmlName(np, QLatin1String("foo")));
3056 void tst_QXmlQuery::setInitialTemplateNameQStringSignature() const
3058 const QString name(QLatin1String("name"));
3061 /* We should take a const reference. */
3062 query.setInitialTemplateName(name);
3065 void tst_QXmlQuery::initialTemplateName() const
3067 /* Check our default value. */
3069 QCOMPARE(query.initialTemplateName(), QXmlName());
3070 QVERIFY(query.initialTemplateName().isNull());
3073 void tst_QXmlQuery::initialTemplateNameSignature() const
3075 const QXmlQuery query;
3076 /* This should be a const member. */
3077 query.initialTemplateName();
3080 void tst_QXmlQuery::setNetworkAccessManager() const
3083 /* Ensure fn:doc() picks up the right QNetworkAccessManager. */
3085 NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
3086 QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/queries/simpleDocument.xml"))));
3087 QVERIFY(networkOverrider.isValid());
3090 query.setNetworkAccessManager(&networkOverrider);
3091 query.setQuery(QLatin1String("string(doc('tag:example.com:DOESNOTEXIST'))"));
3092 QVERIFY(query.isValid());
3095 QVERIFY(query.evaluateTo(&result));
3097 QCOMPARE(result, QStringList(QLatin1String("text text node")));
3100 /* Ensure setQuery() is using the right network manager. */
3102 NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
3103 QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/queries/concat.xq"))));
3104 QVERIFY(networkOverrider.isValid());
3107 query.setNetworkAccessManager(&networkOverrider);
3108 query.setQuery(QUrl("tag:example.com:DOESNOTEXIST"));
3109 QVERIFY(query.isValid());
3112 QVERIFY(query.evaluateTo(&result));
3114 QCOMPARE(result, QStringList(QLatin1String("abcdef")));
3117 void tst_QXmlQuery::networkAccessManagerSignature() const
3120 const QXmlQuery query;
3122 /* The function should be const. */
3123 query.networkAccessManager();
3126 void tst_QXmlQuery::networkAccessManagerDefaultValue() const
3128 const QXmlQuery query;
3130 QCOMPARE(query.networkAccessManager(), static_cast<QNetworkAccessManager *>(0));
3133 void tst_QXmlQuery::networkAccessManager() const
3135 /* Test that we return the network manager that was set. */
3137 QNetworkAccessManager manager;
3139 query.setNetworkAccessManager(&manager);
3140 QCOMPARE(query.networkAccessManager(), &manager);
3148 1. Load a document into QXmlQuery's document cache, by executing a query which does it.
3150 3. Change query, to one which uses the focus
3155 void tst_QXmlQuery::multipleDocsAndFocus() const
3159 /* We use string concatenation, since variable bindings might disturb what
3161 query.setQuery(QLatin1String("string(doc('") +
3162 inputFile(QLatin1String(XMLPATTERNSDIR "/queries/simpleDocument.xml")) +
3163 QLatin1String("'))"));
3164 query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3165 query.setQuery(QLatin1String("string(.)"));
3168 QVERIFY(query.evaluateTo(&result));
3183 void tst_QXmlQuery::multipleEvaluationsWithDifferentFocus() const
3188 query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3189 query.setQuery(QLatin1String("string(.)"));
3190 QVERIFY(query.evaluateTo(&result));
3192 query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3193 QVERIFY(query.evaluateTo(&result));
3196 void tst_QXmlQuery::bindVariableQXmlQuery() const
3198 QFETCH(QString, query1);
3199 QFETCH(QString, query2);
3200 QFETCH(QString, expectedOutput);
3201 QFETCH(bool, expectedSuccess);
3203 MessageSilencer silencer;
3204 QXmlQuery xmlQuery1;
3205 xmlQuery1.setMessageHandler(&silencer);
3206 xmlQuery1.setQuery(query1);
3208 QXmlQuery xmlQuery2(xmlQuery1);
3209 xmlQuery2.bindVariable("query1", xmlQuery1);
3210 xmlQuery2.setQuery(query2);
3213 const bool querySuccess = xmlQuery2.evaluateTo(&output);
3215 QCOMPARE(querySuccess, expectedSuccess);
3218 QCOMPARE(output, expectedOutput);
3221 void tst_QXmlQuery::bindVariableQXmlQuery_data() const
3223 QTest::addColumn<QString>("query1");
3224 QTest::addColumn<QString>("query2");
3225 QTest::addColumn<QString>("expectedOutput");
3226 QTest::addColumn<bool>("expectedSuccess");
3228 QTest::newRow("First query has one atomic value.")
3234 QTest::newRow("First query has two atomic values.")
3240 QTest::newRow("First query is a node.")
3246 /* This is a good test, because it triggers the exception in the
3247 * bindVariable() call, as supposed to when the actual evaluation is done.
3249 QTest::newRow("First query has a dynamic error.")
3252 << QString() /* We don't care. */
3256 void tst_QXmlQuery::bindVariableQStringQXmlQuerySignature() const
3259 query1.setQuery("'dummy'");
3262 const QString name(QLatin1String("name"));
3264 /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
3265 * QXmlQuery, and evaluation is what we do here. */
3266 query2.bindVariable(name, const_cast<const QXmlQuery &>(query1));
3269 void tst_QXmlQuery::bindVariableQXmlNameQXmlQuerySignature() const
3272 QXmlQuery query1(np);
3273 query1.setQuery("'dummy'");
3276 const QXmlName name(np, QLatin1String("name"));
3278 /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
3279 * QXmlQuery, and evaluation is what we do here. */
3280 query2.bindVariable(name, const_cast<const QXmlQuery &>(query1));
3284 Check that the QXmlName is handled correctly.
3286 void tst_QXmlQuery::bindVariableQXmlNameQXmlQuery() const
3290 query1.setQuery(QLatin1String("1"));
3292 QXmlQuery query2(np);
3293 query2.bindVariable(QXmlName(np, QLatin1String("theName")), query1);
3294 query2.setQuery("$theName");
3297 query2.evaluateTo(&result);
3299 QCOMPARE(result, QString::fromLatin1("1\n"));
3302 void tst_QXmlQuery::bindVariableQXmlQueryInvalidate() const
3305 query.bindVariable(QLatin1String("name"), QVariant(1));
3306 query.setQuery("$name");
3307 QVERIFY(query.isValid());
3310 query2.setQuery("'query2'");
3312 query.bindVariable(QLatin1String("name"), query);
3313 QVERIFY(!query.isValid());
3316 void tst_QXmlQuery::unknownSourceLocation() const
3319 b.setData("<a><b/><b/></a>");
3320 b.open(QIODevice::ReadOnly);
3322 MessageSilencer silencer;
3324 query.bindVariable(QLatin1String("inputDocument"), &b);
3325 query.setMessageHandler(&silencer);
3327 query.setQuery(QLatin1String("doc($inputDocument)/a/(let $v := b/string() return if ($v) then $v else ())"));
3330 query.evaluateTo(&output);
3333 void tst_QXmlQuery::identityConstraintSuccess() const
3335 QXmlQuery::QueryLanguage queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintSelector;
3337 /* We run this code for Selector and Field. */
3338 for(int i = 0; i < 3; ++i)
3340 QXmlNamePool namePool;
3341 QXmlResultItems result;
3345 QXmlQuery nodeSource(namePool);
3346 nodeSource.setQuery(QLatin1String("<e/>"));
3348 nodeSource.evaluateTo(&result);
3349 node = result.next();
3353 * 1. The focus is undefined, but it's still valid.
3354 * 2. We never evaluate. */
3356 QXmlQuery query(queryLanguage);
3357 query.setQuery(QLatin1String("a"));
3358 QVERIFY(query.isValid());
3362 * 1. The focus is undefined, but it's still valid.
3363 * 2. We afterwards set the focus. */
3365 QXmlQuery query(queryLanguage, namePool);
3366 query.setQuery(QLatin1String("a"));
3367 query.setFocus(node);
3368 QVERIFY(query.isValid());
3372 * 1. The focus is undefined, but it's still valid.
3373 * 2. We afterwards set the focus.
3374 * 3. We evaluate. */
3376 QXmlQuery query(queryLanguage, namePool);
3377 query.setQuery(QString(QLatin1Char('.')));
3378 query.setFocus(node);
3379 QVERIFY(query.isValid());
3382 QVERIFY(query.evaluateTo(&result));
3383 QCOMPARE(result, QString::fromLatin1("<e/>\n"));
3386 /* A slightly more complex Field. */
3388 QXmlQuery query(queryLanguage);
3389 query.setQuery(QLatin1String("* | .//xml:*/."));
3390 QVERIFY(query.isValid());
3393 /* @ is only allowed in Field. */
3394 if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField)
3396 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField);
3397 query.setQuery(QLatin1String("@abc"));
3398 QVERIFY(query.isValid());
3401 /* Field allows attribute:: and child:: .*/
3402 if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField)
3404 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField);
3405 query.setQuery(QLatin1String("attribute::name | child::name"));
3406 QVERIFY(query.isValid());
3409 /* Selector allows only child:: .*/
3411 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintSelector);
3412 query.setQuery(QLatin1String("child::name"));
3413 QVERIFY(query.isValid());
3417 queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintField;
3419 queryLanguage = QXmlQuery::XPath20;
3423 Q_DECLARE_METATYPE(QXmlQuery::QueryLanguage);
3426 We just do some basic tests for boot strapping and sanity checking. The actual regression
3427 testing is in the Schema suite.
3429 void tst_QXmlQuery::identityConstraintFailure() const
3431 QFETCH(QXmlQuery::QueryLanguage, queryLanguage);
3432 QFETCH(QString, inputQuery);
3434 QXmlQuery query(queryLanguage);
3435 MessageSilencer silencer;
3436 query.setMessageHandler(&silencer);
3438 query.setQuery(inputQuery);
3439 QVERIFY(!query.isValid());
3442 void tst_QXmlQuery::identityConstraintFailure_data() const
3444 QTest::addColumn<QXmlQuery::QueryLanguage>("queryLanguage");
3445 QTest::addColumn<QString>("inputQuery");
3447 QTest::newRow("We don't have element constructors in identity constraint pattern, "
3448 "it's an XQuery feature(Selector).")
3449 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3450 << QString::fromLatin1("<e/>");
3452 QTest::newRow("We don't have functions in identity constraint pattern, "
3453 "it's an XPath feature(Selector).")
3454 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3455 << QString::fromLatin1("current-time()");
3457 QTest::newRow("We don't have element constructors in identity constraint pattern, "
3458 "it's an XQuery feature(Field).")
3459 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3460 << QString::fromLatin1("<e/>");
3462 QTest::newRow("We don't have functions in identity constraint pattern, "
3463 "it's an XPath feature(Field).")
3464 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3465 << QString::fromLatin1("current-time()");
3467 QTest::newRow("@attributeName is disallowed for the selector.")
3468 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3469 << QString::fromLatin1("@abc");
3471 QTest::newRow("attribute:: is disallowed for the selector.")
3472 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3473 << QString::fromLatin1("attribute::name");
3475 QTest::newRow("ancestor::name is disallowed for the selector.")
3476 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3477 << QString::fromLatin1("ancestor::name");
3479 QTest::newRow("ancestor::name is disallowed for the field.")
3480 << QXmlQuery::XmlSchema11IdentityConstraintField
3481 << QString::fromLatin1("ancestor::name");
3484 QTEST_MAIN(tst_QXmlQuery)
3486 #include "tst_qxmlquery.moc"