1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the test suite of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file. Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights. These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
43 #include <QtTest/QtTest>
45 #ifdef QTEST_XMLPATTERNS
47 #include <QAbstractMessageHandler>
49 #include <QNetworkReply>
50 #include <QtNetwork/QTcpServer>
51 #include <QtNetwork/QTcpSocket>
52 #include <QXmlFormatter>
56 #include <QXmlResultItems>
57 #include <QXmlSerializer>
59 #include "MessageSilencer.h"
60 #include "MessageValidator.h"
61 #include "NetworkOverrider.h"
62 #include "PushBaseliner.h"
63 #include "../qabstracturiresolver/TestURIResolver.h"
64 #include "../qsimplexmlnodemodel/TestSimpleNodeModel.h"
65 #include "TestFundament.h"
66 #include "../network-settings.h"
68 #if defined(Q_OS_SYMBIAN)
70 #define XMLPATTERNSDIR "xmlpatterns"
72 #define XMLPATTERNSDIR SRCDIR "../xmlpatterns"
79 \brief Tests class QXmlQuery.
81 This test is not intended for testing the engine, but the functionality specific
82 to the QXmlQuery class.
84 In other words, if you have an engine bug; don't add it here because it won't be
85 tested properly. Instead add it to the test suite.
88 class tst_QXmlQuery : public QObject
89 , private TestFundament
94 inline tst_QXmlQuery() : m_generatedBaselines(0)
102 void defaultConstructor() const;
103 void copyConstructor() const;
104 void constructorQXmlNamePool() const;
105 void constructorQXmlNamePoolQueryLanguage() const;
106 void constructorQXmlNamePoolWithinQSimpleXmlNodeModel() const;
107 void assignmentOperator() const;
108 void isValid() const;
109 void sequentialExecution() const;
110 void bindVariableQString() const;
111 void bindVariableQStringNoExternalDeclaration() const;
112 void bindVariableQXmlName() const;
113 void bindVariableQXmlNameTriggerWarnings() const;
114 void bindVariableQStringQIODevice() const;
115 void bindVariableQStringQIODeviceWithByteArray() const;
116 void bindVariableQStringQIODeviceWithString() const;
117 void bindVariableQStringQIODeviceWithQFile() const;
118 void bindVariableQXmlNameQIODevice() const;
119 void bindVariableQXmlNameQIODeviceTriggerWarnings() const;
120 void bindVariableXSLTSuccess() const;
121 void bindVariableTemporaryNode() const;
122 void setMessageHandler() const;
123 void messageHandler() const;
124 void evaluateToQAbstractXmlReceiverTriggerWarnings() const;
125 void evaluateToQXmlResultItems() const;
126 void evaluateToQXmlResultItemsTriggerWarnings() const;
127 void evaluateToQXmlResultItemsErrorAtEnd() const;
128 void evaluateToReceiver();
129 void evaluateToReceiver_data() const;
130 void evaluateToReceiverOnInvalidQuery() const;
131 void evaluateToQStringTriggerError() const;
132 void evaluateToQString() const;
133 void evaluateToQString_data() const;
134 void evaluateToQStringSignature() const;
135 void checkGeneratedBaselines() const;
136 void basicXQueryToQtTypeCheck() const;
137 void basicQtToXQueryTypeCheck() const;
138 void bindNode() const;
139 void relativeBaseURI() const;
140 void emptyBaseURI() const;
141 void roundTripDateWithinQXmlItem() const;
142 void bindingMissing() const;
143 void bindDefaultConstructedItem() const;
144 void bindDefaultConstructedItem_data() const;
145 void bindEmptyNullString() const;
146 void bindEmptyString() const;
147 void rebindVariableSameType() const;
148 void rebindVariableDifferentType() const;
149 void rebindVariableWithNullItem() const;
150 void eraseQXmlItemBinding() const;
151 void eraseDeviceBinding() const;
152 void constCorrectness() const;
153 void objectSize() const;
154 void setUriResolver() const;
155 void uriResolver() const;
156 void messageXML() const;
157 void resultItemsDeallocatedQuery() const;
158 void copyCheckMessageHandler() const;
159 void shadowedVariables() const;
160 void setFocusQXmlItem() const;
161 void setFocusQUrl() const;
162 void setFocusQIODevice() const;
163 void setFocusQIODeviceAvoidVariableClash() const;
164 void setFocusQIODeviceFailure() const;
165 void setFocusQIODeviceTriggerWarnings() const;
166 void setFocusQString() const;
167 void setFocusQStringFailure() const;
168 void setFocusQStringSignature() const;
169 void recompilationWithEvaluateToResultFailing() const;
170 void secondEvaluationWithEvaluateToResultFailing() const;
171 void recompilationWithEvaluateToReceiver() const;
172 void fnDocOnQIODeviceTimeout() const;
173 void evaluateToQStringListOnInvalidQuery() const;
174 void evaluateToQStringList() const;
175 void evaluateToQStringListTriggerWarnings() const;
176 void evaluateToQStringList_data() const;
177 void evaluateToQStringListNoConversion() const;
178 void evaluateToQIODevice() const;
179 void evaluateToQIODeviceTriggerWarnings() const;
180 void evaluateToQIODeviceSignature() const;
181 void evaluateToQIODeviceOnInvalidQuery() const;
182 void setQueryQIODeviceQUrl() const;
183 void setQueryQIODeviceQUrlTriggerWarnings() const;
184 void setQueryQString() const;
185 void setQueryQUrlSuccess() const;
186 void setQueryQUrlSuccess_data() const;
187 void setQueryQUrlFailSucceed() const;
188 void setQueryQUrlFailure() const;
189 void setQueryQUrlFailure_data() const;
190 void setQueryQUrlBaseURI() const;
191 void setQueryQUrlBaseURI_data() const;
192 void setQueryWithNonExistentQUrlOnValidQuery() const;
193 void setQueryWithInvalidQueryFromQUrlOnValidQuery() const;
194 void retrieveNameFromQuery() const;
195 void retrieveNameFromQuery_data() const;
196 void cleanupTestCase() const;
197 void declareUnavailableExternal() const;
198 void msvcCacheIssue() const;
199 void unavailableExternalVariable() const;
200 void useUriResolver() const;
201 void queryWithFocusAndVariable() const;
202 void undefinedFocus() const;
203 void basicFocusUsage() const;
205 void queryLanguage() const;
206 void queryLanguageSignature() const;
207 void enumQueryLanguage() const;
209 void setNetworkAccessManager() const;
210 void networkAccessManagerSignature() const;
211 void networkAccessManagerDefaultValue() const;
212 void networkAccessManager() const;
214 void setInitialTemplateNameQXmlName() const;
215 void setInitialTemplateNameQXmlNameSignature() const;
216 void setInitialTemplateNameQString() const;
217 void setInitialTemplateNameQStringSignature() const;
218 void initialTemplateName() const;
219 void initialTemplateNameSignature() const;
221 void fnDocNetworkAccessSuccess() const;
222 void fnDocNetworkAccessSuccess_data() const;
223 void fnDocNetworkAccessFailure() const;
224 void fnDocNetworkAccessFailure_data() const;
225 void multipleDocsAndFocus() const;
226 void multipleEvaluationsWithDifferentFocus() const;
227 void bindVariableQXmlQuery() const;
228 void bindVariableQXmlQuery_data() const;
229 void bindVariableQStringQXmlQuerySignature() const;
230 void bindVariableQXmlNameQXmlQuerySignature() const;
231 void bindVariableQXmlNameQXmlQuery() const;
232 void bindVariableQXmlQueryInvalidate() const;
233 void unknownSourceLocation() const;
235 void identityConstraintSuccess() const;
236 void identityConstraintFailure() const;
237 void identityConstraintFailure_data() const;
239 // 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.
240 // TODO bind stringlists, variant lists, both ways.
241 // TODO trigger serialization error, or any error in evaluateToushCallback().
242 // TODO let items travle between two queries, as seen in the SDK
243 // TODO what happens if the query declares local variable and external ones are provided?
249 * One excluded, since we skip static-base-uri.xq.
251 ExpectedQueryCount = 29
254 static void checkBaseURI(const QUrl &baseURI, const QString &candidate);
255 static QStringList queries();
256 static const char *const queriesDirectory;
258 int m_generatedBaselines;
259 int m_pushTestsCount;
260 const bool m_testNetwork;
263 void tst_QXmlQuery::checkBaseURI(const QUrl &baseURI, const QString &candidate)
265 /* The use of QFileInfo::canonicalFilePath() takes into account that drive letters
266 * on Windows may have different cases. */
267 QVERIFY(QDir(baseURI.toLocalFile()).relativeFilePath(QFileInfo(candidate).canonicalFilePath()).startsWith("../"));
270 const char *const tst_QXmlQuery::queriesDirectory = XMLPATTERNSDIR "/queries/";
272 QStringList tst_QXmlQuery::queries()
275 dir.cd(inputFile(QLatin1String(queriesDirectory)));
277 return dir.entryList(QStringList(QLatin1String("*.xq")));
280 void tst_QXmlQuery::defaultConstructor() const
282 /* Allocate instance in different orders. */
299 void tst_QXmlQuery::copyConstructor() const
301 /* Verify that we can take a const reference, and simply do a copy of a default constructed object. */
303 const QXmlQuery query1;
304 QXmlQuery query2(query1);
309 const QXmlQuery query1;
310 QXmlQuery query2(query1);
311 QXmlQuery query3(query2);
314 /* Verify that copying default values works. */
316 const QXmlQuery query1;
317 const QXmlQuery query2(query1);
318 QCOMPARE(query2.messageHandler(), query1.messageHandler());
319 QCOMPARE(query2.uriResolver(), query1.uriResolver());
320 QCOMPARE(query2.queryLanguage(), query1.queryLanguage());
321 QCOMPARE(query2.initialTemplateName(), query1.initialTemplateName());
322 QCOMPARE(query2.networkAccessManager(), query1.networkAccessManager());
331 * - initial template name
333 * sticks with the copy. */
335 MessageSilencer silencer;
336 TestURIResolver resolver;
337 QNetworkAccessManager networkManager;
338 QXmlQuery query1(QXmlQuery::XSLT20);
339 QXmlNamePool np1(query1.namePool());
341 query1.setMessageHandler(&silencer);
342 query1.setUriResolver(&resolver);
343 query1.setNetworkAccessManager(&networkManager);
345 const QXmlName name(np1, QLatin1String("localName"),
346 QLatin1String("http://example.com/"),
347 QLatin1String("prefix"));
348 query1.setInitialTemplateName(name);
350 const QXmlQuery query2(query1);
351 QCOMPARE(query2.messageHandler(), static_cast<QAbstractMessageHandler *>(&silencer));
352 QCOMPARE(query2.uriResolver(), static_cast<const QAbstractUriResolver *>(&resolver));
353 QCOMPARE(query2.queryLanguage(), QXmlQuery::XSLT20);
354 QCOMPARE(query2.initialTemplateName(), name);
355 QCOMPARE(query2.networkAccessManager(), &networkManager);
357 QXmlNamePool np2(query2.namePool());
359 QCOMPARE(name.namespaceUri(np2), QString::fromLatin1("http://example.com/"));
360 QCOMPARE(name.localName(np2), QString::fromLatin1("localName"));
361 QCOMPARE(name.prefix(np2), QString::fromLatin1("prefix"));
367 original.setFocus(QXmlItem(4));
368 original.setQuery(QLatin1String("."));
369 QVERIFY(original.isValid());
371 const QXmlQuery copy(original);
373 QXmlResultItems result;
374 copy.evaluateTo(&result);
375 QCOMPARE(result.next().toAtomicValue(), QVariant(4));
376 QVERIFY(result.next().isNull());
377 QVERIFY(!result.hasError());
380 /* Copy, set, compare. Check that copies are independent. */
382 // TODO all members except queryLanguage().
386 void tst_QXmlQuery::constructorQXmlNamePool() const
388 /* Check that the namepool we are passed, is actually used. */
392 const QXmlName name(np, QLatin1String("localName"),
393 QLatin1String("http://example.com/"),
394 QLatin1String("prefix"));
396 QXmlNamePool np2(query.namePool());
397 QCOMPARE(name.namespaceUri(np2), QString::fromLatin1("http://example.com/"));
398 QCOMPARE(name.localName(np2), QString::fromLatin1("localName"));
399 QCOMPARE(name.prefix(np2), QString::fromLatin1("prefix"));
403 Ensure that the internal variable loading mechanisms uses the user-supplied
406 If that is not the case, different name pools are used and the code crashes.
410 void tst_QXmlQuery::constructorQXmlNamePoolQueryLanguage() const
413 QXmlName name(np, QLatin1String("arbitraryName"));
415 QXmlQuery query(QXmlQuery::XQuery10, np);
418 input.setData("<yall/>");
420 QVERIFY(input.open(QIODevice::ReadOnly));
421 query.bindVariable(name, &input);
422 query.setQuery("string(doc($arbitraryName))");
425 query.evaluateTo(&result);
428 void tst_QXmlQuery::constructorQXmlNamePoolWithinQSimpleXmlNodeModel() const
430 class TestCTOR : public TestSimpleNodeModel
433 TestCTOR(const QXmlNamePool &np) : TestSimpleNodeModel(np)
437 void checkCTOR() const
439 /* If this fails to compile, the constructor has trouble with taking
440 * a mutable reference.
442 * The reason we use the this pointer explicitly, is to avoid a compiler
443 * warnings with MSVC 2005. */
444 QXmlQuery(this->namePool());
453 void tst_QXmlQuery::assignmentOperator() const
455 class ReturnURI : public QAbstractUriResolver
459 virtual QUrl resolve(const QUrl &relative,
460 const QUrl &baseURI) const
462 return baseURI.resolved(relative);
466 /* Assign this to this. */
473 /* Just call a couple of functions to give valgrind
474 * something to check. */
475 QVERIFY(!query.isValid());
476 query.messageHandler();
479 /* Assign null instances a couple of times. */
487 /* Just call a couple of functions to give valgrind
488 * something to check. */
489 QVERIFY(!query1.isValid());
490 query1.messageHandler();
492 /* Just call a couple of functions to give valgrind
493 * something to check. */
494 QVERIFY(!query2.isValid());
495 query2.messageHandler();
498 /* Create a query, set all the things it stores, and ensure it
499 * travels over to the new instance. */
501 MessageSilencer silencer;
502 const ReturnURI returnURI;
503 QXmlNamePool namePool;
505 QBuffer documentDevice;
506 documentDevice.setData(QByteArray("<e>a</e>"));
507 QVERIFY(documentDevice.open(QIODevice::ReadOnly));
509 QXmlQuery original(namePool);
510 QXmlName testName(namePool, QLatin1String("somethingToCheck"));
512 original.setMessageHandler(&silencer);
513 original.bindVariable(QLatin1String("var"), QXmlItem(1));
514 original.bindVariable(QLatin1String("device"), &documentDevice);
515 original.setUriResolver(&returnURI);
516 original.setFocus(QXmlItem(3));
517 original.setQuery(QLatin1String("$var, 1 + 1, ., string(doc($device))"));
519 /* Do a copy, and check that everything followed on into the copy. No modification
524 /* We use assignment operator, not copy constructor. */
527 QVERIFY(copy.isValid());
528 QCOMPARE(copy.uriResolver(), static_cast<const QAbstractUriResolver *>(&returnURI));
529 QCOMPARE(copy.messageHandler(), static_cast<QAbstractMessageHandler *>(&silencer));
530 QCOMPARE(testName.localName(copy.namePool()), QString::fromLatin1("somethingToCheck"));
532 QXmlResultItems result;
533 copy.evaluateTo(&result);
534 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
535 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
536 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
537 QCOMPARE(result.next().toAtomicValue(), QVariant(QString::fromLatin1("a")));
538 QVERIFY(result.next().isNull());
539 QVERIFY(!result.hasError());
542 /* Copy, and change values. Things should detach. */
544 /* Evaluate the copy. */
546 MessageSilencer secondSilencer;
547 const ReturnURI secondUriResolver;
548 QBuffer documentDeviceCopy;
549 documentDeviceCopy.setData(QByteArray("<e>b</e>"));
550 QVERIFY(documentDeviceCopy.open(QIODevice::ReadOnly));
555 copy.setMessageHandler(&secondSilencer);
556 /* Here we rebind variable values. */
557 copy.bindVariable(QLatin1String("var"), QXmlItem(4));
558 copy.bindVariable(QLatin1String("device"), &documentDeviceCopy);
559 copy.setUriResolver(&secondUriResolver);
560 copy.setFocus(QXmlItem(6));
561 copy.setQuery(QLatin1String("$var, 1 + 1, ., string(doc($device))"));
563 /* Check that the copy picked up the new things. */
564 QVERIFY(copy.isValid());
565 QCOMPARE(copy.uriResolver(), static_cast<const QAbstractUriResolver *>(&secondUriResolver));
566 QCOMPARE(copy.messageHandler(), static_cast<QAbstractMessageHandler *>(&secondSilencer));
568 QXmlResultItems resultCopy;
569 copy.evaluateTo(&resultCopy);
570 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(4));
571 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(2));
572 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(6));
573 const QString stringedDevice(resultCopy.next().toAtomicValue().toString());
574 QCOMPARE(stringedDevice, QString::fromLatin1("b"));
575 QVERIFY(resultCopy.next().isNull());
576 QVERIFY(!resultCopy.hasError());
579 /* Evaluate the original. */
581 /* Check that the original is unchanged. */
582 QVERIFY(original.isValid());
583 QCOMPARE(original.uriResolver(), static_cast<const QAbstractUriResolver *>(&returnURI));
584 QCOMPARE(original.messageHandler(), static_cast<QAbstractMessageHandler *>(&silencer));
586 QXmlResultItems resultOriginal;
587 original.evaluateTo(&resultOriginal);
588 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(1));
589 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(2));
590 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(3));
591 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(QString::fromLatin1("a")));
592 QVERIFY(resultOriginal.next().isNull());
593 QVERIFY(!resultOriginal.hasError());
600 Since QXmlQuery doesn't seek devices to position 0, this code triggers a bug
601 where document caching doesn't work. Since the document caching doesn't work,
602 the device will be read twice, and the second time the device is at the end,
603 hence premature end of document.
605 void tst_QXmlQuery::sequentialExecution() const
608 inBuffer.setData(QByteArray("<input/>"));
609 QVERIFY(inBuffer.open(QIODevice::ReadOnly));
612 query.bindVariable("inputDocument", &inBuffer);
615 QBuffer outBuffer(&outArray);
616 outBuffer.open(QIODevice::WriteOnly);
618 const QString queryString(QLatin1String("doc($inputDocument)"));
619 query.setQuery(queryString);
621 QXmlFormatter formatter(query, &outBuffer);
623 QVERIFY(query.evaluateTo(&formatter));
625 /* If this line is removed, the bug isn't triggered. */
626 query.setQuery(queryString);
628 QVERIFY(query.evaluateTo(&formatter));
631 void tst_QXmlQuery::isValid() const
633 /* Check default value. */
635 QVERIFY(!query.isValid());
638 void tst_QXmlQuery::bindVariableQString() const
642 /* Bind with a null QXmlItem. */
643 query.bindVariable(QLatin1String("name"), QXmlItem());
648 /* Bind with a null QVariant. */
649 query.bindVariable(QLatin1String("name"), QXmlItem(QVariant()));
654 /* Bind with a null QXmlNodeModelIndex. */
655 query.bindVariable(QLatin1String("name"), QXmlItem(QXmlNodeModelIndex()));
659 void tst_QXmlQuery::bindVariableQStringNoExternalDeclaration() const
662 query.bindVariable(QLatin1String("foo"), QXmlItem(QLatin1String("Variable Value")));
663 query.setQuery(QLatin1String("$foo"));
665 QVERIFY(query.isValid());
668 QVERIFY(query.evaluateTo(&result));
670 QCOMPARE(result, QStringList() << QLatin1String("Variable Value"));
673 void tst_QXmlQuery::bindVariableQXmlName() const
678 void tst_QXmlQuery::bindVariableQXmlNameTriggerWarnings() const
682 QTest::ignoreMessage(QtWarningMsg, "The variable name cannot be null.");
683 query.bindVariable(QXmlName(), QVariant());
686 void tst_QXmlQuery::bindVariableQStringQIODeviceWithByteArray() const
690 QByteArray in("<e/>");
692 QVERIFY(device.open(QIODevice::ReadOnly));
694 query.bindVariable("doc", &device);
696 query.setQuery(QLatin1String("declare variable $doc external; $doc"));
698 QVERIFY(query.isValid());
700 /* Check the URI corresponding to the variable. */
702 QXmlResultItems items;
703 query.evaluateTo(&items);
705 QCOMPARE(items.next().toAtomicValue().toString(), QString::fromLatin1("tag:trolltech.com,2007:QtXmlPatterns:QIODeviceVariable:doc"));
708 /* Now, actually load the document. We use the same QXmlQuery just to stress recompilation a bit. */
710 query.setQuery(QLatin1String("declare variable $doc external; doc($doc)"));
713 QBuffer outBuffer(&out);
714 QVERIFY(outBuffer.open(QIODevice::WriteOnly));
716 QXmlSerializer serializer(query, &outBuffer);
718 QVERIFY(query.evaluateTo(&serializer));
723 void tst_QXmlQuery::bindVariableQStringQIODeviceWithString() const
727 QString in("<qstring/>");
728 QByteArray inUtf8(in.toUtf8());
729 QBuffer inDevice(&inUtf8);
731 QVERIFY(inDevice.open(QIODevice::ReadOnly));
733 query.bindVariable("doc", &inDevice);
735 query.setQuery(QLatin1String("declare variable $doc external; doc($doc)"));
738 QBuffer outBuffer(&out);
739 QVERIFY(outBuffer.open(QIODevice::WriteOnly));
741 QXmlSerializer serializer(query, &outBuffer);
742 QVERIFY(query.evaluateTo(&serializer));
744 QCOMPARE(out, inUtf8);
747 void tst_QXmlQuery::bindVariableQStringQIODeviceWithQFile() const
750 QFile inDevice(QLatin1String(SRCDIR "input.xml"));
752 QVERIFY(inDevice.open(QIODevice::ReadOnly));
754 query.bindVariable("doc", &inDevice);
756 query.setQuery(QLatin1String("declare variable $doc external; doc($doc)"));
759 QBuffer outBuffer(&out);
760 QVERIFY(outBuffer.open(QIODevice::WriteOnly));
762 QXmlSerializer serializer(query, &outBuffer);
763 QVERIFY(query.evaluateTo(&serializer));
766 QCOMPARE(out, QByteArray("<!-- This is just a file for testing. --><input/>"));
769 void tst_QXmlQuery::bindVariableQStringQIODevice() const
773 /* Rebind the variable. */
775 /* First evaluation. */
777 QByteArray in1("<e1/>");
778 QBuffer inDevice1(&in1);
779 QVERIFY(inDevice1.open(QIODevice::ReadOnly));
781 query.bindVariable("in", &inDevice1);
782 query.setQuery(QLatin1String("doc($in)"));
785 QBuffer outDevice1(&out1);
786 QVERIFY(outDevice1.open(QIODevice::WriteOnly));
788 QXmlSerializer serializer(query, &outDevice1);
789 query.evaluateTo(&serializer);
793 /* Second evaluation, rebind variable. */
795 QByteArray in2("<e2/>");
796 QBuffer inDevice2(&in2);
797 QVERIFY(inDevice2.open(QIODevice::ReadOnly));
799 query.bindVariable(QLatin1String("in"), &inDevice2);
802 QBuffer outDevice2(&out2);
803 QVERIFY(outDevice2.open(QIODevice::WriteOnly));
805 QXmlSerializer serializer(query, &outDevice2);
806 QVERIFY(query.evaluateTo(&serializer));
811 // TODO trigger recompilation when setting qiodevices., and qiodevice overwritten by other type, etc.
814 void tst_QXmlQuery::bindVariableQXmlNameQIODevice() const
819 void tst_QXmlQuery::bindVariableQXmlNameQIODeviceTriggerWarnings() const
825 QTest::ignoreMessage(QtWarningMsg, "A null, or readable QIODevice must be passed.");
826 query.bindVariable(QXmlName(np, QLatin1String("foo")), &buffer);
828 QTest::ignoreMessage(QtWarningMsg, "The variable name cannot be null.");
829 query.bindVariable(QXmlName(), 0);
832 void tst_QXmlQuery::bindVariableXSLTSuccess() const
834 QXmlQuery stylesheet(QXmlQuery::XSLT20);
835 stylesheet.setInitialTemplateName(QLatin1String("main"));
837 stylesheet.bindVariable(QLatin1String("variableNoSelectNoBodyBoundWithBindVariable"),
838 QVariant(QLatin1String("MUST NOT SHOW 1")));
840 stylesheet.bindVariable(QLatin1String("variableSelectBoundWithBindVariable"),
841 QVariant(QLatin1String("MUST NOT SHOW 2")));
843 stylesheet.bindVariable(QLatin1String("variableSelectWithTypeIntBoundWithBindVariable"),
844 QVariant(QLatin1String("MUST NOT SHOW 3")));
846 stylesheet.bindVariable(QLatin1String("paramNoSelectNoBodyBoundWithBindVariable"),
847 QVariant(QLatin1String("param1")));
849 stylesheet.bindVariable(QLatin1String("paramNoSelectNoBodyBoundWithBindVariableRequired"),
850 QVariant(QLatin1String("param1")));
852 stylesheet.bindVariable(QLatin1String("paramSelectBoundWithBindVariable"),
853 QVariant(QLatin1String("param2")));
855 stylesheet.bindVariable(QLatin1String("paramSelectBoundWithBindVariableRequired"),
856 QVariant(QLatin1String("param3")));
858 stylesheet.bindVariable(QLatin1String("paramSelectWithTypeIntBoundWithBindVariable"),
861 stylesheet.bindVariable(QLatin1String("paramSelectWithTypeIntBoundWithBindVariableRequired"),
862 QVariant(QLatin1String("param5")));
864 stylesheet.setQuery(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/parameters.xsl"))));
866 QVERIFY(stylesheet.isValid());
869 QVERIFY(deviceOut.open(QIODevice::ReadWrite));
871 QVERIFY(stylesheet.evaluateTo(&deviceOut));
873 const QString result(QString::fromUtf8(deviceOut.data().constData()));
876 QString::fromLatin1("Variables: variableSelectsDefaultValue variableSelectsDefaultValue2 3 4 "
877 "Parameters: param1 param1 param2 param3 4 param5"));
880 void tst_QXmlQuery::bindVariableTemporaryNode() const
882 /* First we do it with QXmlResultItems staying in scope. */;
885 query1.setQuery("<anElement/>");
887 QXmlResultItems result1;
888 query1.evaluateTo(&result1);
890 QXmlQuery query2(query1);
891 query2.bindVariable("fromQuery1", result1.next());
892 query2.setQuery("$fromQuery1");
895 QVERIFY(query2.evaluateTo(&output));
897 QCOMPARE(output, QString::fromLatin1("<anElement/>\n"));
900 /* And now with it deallocating, so its internal DynamicContext pointer is
901 * released. This doesn't work in Qt 4.5 and is ok. */
904 query1.setQuery("<anElement/>");
909 QXmlResultItems result1;
910 query1.evaluateTo(&result1);
912 query2.bindVariable("fromQuery1", result1.next());
913 query2.setQuery("$fromQuery1");
917 return; // See comment above.
918 QVERIFY(query2.evaluateTo(&output));
920 QCOMPARE(output, QString::fromLatin1("<anElement/>\n"));
924 void tst_QXmlQuery::messageHandler() const
927 /* Check default value. */
929 QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0));
933 void tst_QXmlQuery::setMessageHandler() const
936 MessageSilencer silencer;
937 query.setMessageHandler(&silencer);
938 QCOMPARE(static_cast<QAbstractMessageHandler *>(&silencer), query.messageHandler());
941 void tst_QXmlQuery::evaluateToReceiver()
943 QFETCH(QString, inputQuery);
945 /* This query prints a URI specific to the local system. */
946 if(inputQuery == QLatin1String("static-base-uri.xq"))
950 const QString queryURI(inputFile(QLatin1String(queriesDirectory) + inputQuery));
951 QFile queryFile(queryURI);
953 QVERIFY(queryFile.exists());
954 QVERIFY(queryFile.open(QIODevice::ReadOnly));
958 MessageSilencer receiver;
959 query.setMessageHandler(&receiver);
960 query.setQuery(&queryFile, QUrl::fromLocalFile(queryURI));
962 /* We read all the queries, and some of them are invalid. However, we
963 * only want those that compile. */
968 QTextStream stream(&produced, QIODevice::WriteOnly);
969 PushBaseliner push(stream, query.namePool());
970 query.evaluateTo(&push);
972 const QString baselineName(inputFile(QLatin1String(SRCDIR "pushBaselines/") + inputQuery.left(inputQuery.length() - 2) + QString::fromLatin1("ref")));
973 QFile baseline(baselineName);
975 if(baseline.exists())
977 QVERIFY(baseline.open(QIODevice::ReadOnly | QIODevice::Text));
978 const QString stringedBaseline(QString::fromUtf8(baseline.readAll()));
979 QCOMPARE(produced, stringedBaseline);
983 QVERIFY(baseline.open(QIODevice::WriteOnly));
984 /* This is intentionally a warning, don't remove it. Update the baselines instead. */
985 qWarning() << "Generated baseline for:" << baselineName;
986 ++m_generatedBaselines;
988 baseline.write(produced.toUtf8());
992 void tst_QXmlQuery::evaluateToReceiver_data() const
994 QTest::addColumn<QString>("inputQuery");
996 const QStringList qs(queries());
998 for(int i = 0; i < qs.size(); ++i)
1000 /* This outputs a URI specific to the environment, so we can't use it for this
1001 * particular test. */
1002 if(qs.at(i) != QLatin1String("staticBaseURI.xq"))
1003 QTest::newRow(qs.at(i).toUtf8().constData()) << qs.at(i);
1007 void tst_QXmlQuery::evaluateToReceiverOnInvalidQuery() const
1009 /* Invoke on a default constructed object. */
1012 QBuffer buffer(&out);
1013 buffer.open(QIODevice::WriteOnly);
1016 QXmlSerializer serializer(query, &buffer);
1017 QVERIFY(!query.evaluateTo(&serializer));
1020 /* Invoke on an invalid query; compile time error. */
1023 QBuffer buffer(&out);
1024 buffer.open(QIODevice::WriteOnly);
1025 MessageSilencer silencer;
1028 query.setMessageHandler(&silencer);
1029 query.setQuery(QLatin1String("1 + "));
1030 QXmlSerializer serializer(query, &buffer);
1031 QVERIFY(!query.evaluateTo(&serializer));
1034 /* Invoke on an invalid query; runtime error. */
1037 QBuffer buffer(&out);
1038 buffer.open(QIODevice::WriteOnly);
1039 MessageSilencer silencer;
1042 query.setMessageHandler(&silencer);
1043 query.setQuery(QLatin1String("error()"));
1044 QXmlSerializer serializer(query, &buffer);
1045 QVERIFY(!query.evaluateTo(&serializer));
1049 void tst_QXmlQuery::evaluateToQStringTriggerError() const
1051 /* Invoke on a default constructed object. */
1055 QVERIFY(!query.evaluateTo(&out));
1058 /* Invoke on an invalid query; compile time error. */
1061 MessageSilencer silencer;
1062 query.setMessageHandler(&silencer);
1064 query.setQuery(QLatin1String("1 + "));
1067 QVERIFY(!query.evaluateTo(&out));
1070 /* Invoke on an invalid query; runtime error. */
1073 MessageSilencer silencer;
1074 query.setMessageHandler(&silencer);
1076 query.setQuery(QLatin1String("error()"));
1079 QVERIFY(!query.evaluateTo(&out));
1083 void tst_QXmlQuery::evaluateToQString() const
1085 QFETCH(QString, query);
1086 QFETCH(QString, expectedOutput);
1088 QXmlQuery queryInstance;
1089 queryInstance.setQuery(query);
1090 QVERIFY(queryInstance.isValid());
1093 QVERIFY(queryInstance.evaluateTo(&result));
1095 QCOMPARE(result, expectedOutput);
1098 void tst_QXmlQuery::evaluateToQString_data() const
1100 QTest::addColumn<QString>("query");
1101 QTest::addColumn<QString>("expectedOutput");
1103 QTest::newRow("Two atomics")
1104 << QString::fromLatin1("1, 'two'")
1105 << QString::fromLatin1("1 two\n");
1107 QTest::newRow("An element")
1108 << QString::fromLatin1("<e>{1}</e>")
1109 << QString::fromLatin1("<e>1</e>\n");
1112 void tst_QXmlQuery::evaluateToQStringSignature() const
1114 const QXmlQuery query;
1118 /* evaluateTo(QString *) should be a const function. */
1119 query.evaluateTo(&output);
1122 void tst_QXmlQuery::evaluateToQAbstractXmlReceiverTriggerWarnings() const
1126 /* We check the return value as well as warning message here. */
1127 QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed.");
1128 QCOMPARE(query.evaluateTo(static_cast<QAbstractXmlReceiver *>(0)),
1132 void tst_QXmlQuery::evaluateToQXmlResultItems() const
1134 /* Invoke on a default constructed object. */
1137 QXmlResultItems result;
1138 query.evaluateTo(&result);
1139 QVERIFY(result.next().isNull());
1143 void tst_QXmlQuery::evaluateToQXmlResultItemsTriggerWarnings() const
1145 QTest::ignoreMessage(QtWarningMsg, "A null pointer cannot be passed.");
1147 query.evaluateTo(static_cast<QXmlResultItems *>(0));
1150 void tst_QXmlQuery::evaluateToQXmlResultItemsErrorAtEnd() const
1153 MessageSilencer silencer;
1154 query.setMessageHandler(&silencer);
1155 query.setQuery(QLatin1String("1 to 100, fn:error()"));
1156 QVERIFY(query.isValid());
1159 query.evaluateTo(&it);
1161 while(!it.next().isNull())
1167 If baselines were generated, we flag it as a failure such that it gets
1168 attention, and that they are adjusted accordingly.
1170 void tst_QXmlQuery::checkGeneratedBaselines() const
1172 QCOMPARE(m_generatedBaselines, 0);
1174 /* If this check fails, the auto test setup is misconfigured, or files have
1175 * been added/removed without this number being updated. */
1176 QCOMPARE(m_pushTestsCount, int(ExpectedQueryCount));
1179 void tst_QXmlQuery::basicXQueryToQtTypeCheck() const
1181 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("allAtomics.xq"));
1182 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1185 query.setQuery(&queryFile);
1186 QVERIFY(query.isValid());
1189 query.evaluateTo(&it);
1191 QVariantList expectedValues;
1192 expectedValues.append(QString::fromLatin1("xs:untypedAtomic"));
1193 expectedValues.append(QDateTime(QDate(2002, 10, 10), QTime(23, 2, 11), Qt::UTC));
1194 expectedValues.append(QDate(2002, 10, 10));
1195 expectedValues.append(QVariant()); /* We currently doesn't support xs:time through the API. */
1197 expectedValues.append(QVariant()); /* xs:duration */
1198 expectedValues.append(QVariant()); /* xs:dayTimeDuration */
1199 expectedValues.append(QVariant()); /* xs:yearMonthDuration */
1201 if(sizeof(qreal) == sizeof(float)) {//ARM casts to Float not to double
1202 expectedValues.append(QVariant(float(3e3))); /* xs:float */
1203 expectedValues.append(QVariant(float(4e4))); /* xs:double */
1204 expectedValues.append(QVariant(float(2))); /* xs:decimal */
1206 expectedValues.append(QVariant(double(3e3))); /* xs:float */
1207 expectedValues.append(QVariant(double(4e4))); /* xs:double */
1208 expectedValues.append(QVariant(double(2))); /* xs:decimal */
1211 /* xs:integer and its sub-types. */
1212 expectedValues.append(QVariant(qlonglong(16)));
1213 expectedValues.append(QVariant(qlonglong(-6)));
1214 expectedValues.append(QVariant(qlonglong(-4)));
1215 expectedValues.append(QVariant(qlonglong(5)));
1216 expectedValues.append(QVariant(qlonglong(6)));
1217 expectedValues.append(QVariant(qlonglong(7)));
1218 expectedValues.append(QVariant(qlonglong(8)));
1219 expectedValues.append(QVariant(qlonglong(9)));
1220 expectedValues.append(QVariant(qulonglong(10)));
1221 expectedValues.append(QVariant(qlonglong(11)));
1222 expectedValues.append(QVariant(qlonglong(12)));
1223 expectedValues.append(QVariant(qlonglong(13)));
1224 expectedValues.append(QVariant(qlonglong(14)));
1226 expectedValues.append(QVariant()); /* xs:gYearMonth("1976-02"), */
1227 expectedValues.append(QVariant()); /* xs:gYear("2005-12:00"), */
1228 expectedValues.append(QVariant()); /* xs:gMonthDay("--12-25-14:00"), */
1229 expectedValues.append(QVariant()); /* xs:gDay("---25-14:00"), */
1230 expectedValues.append(QVariant()); /* xs:gMonth("--12-14:00"), */
1231 expectedValues.append(true); /* xs:boolean("true"), */
1232 expectedValues.append(QVariant(QByteArray::fromBase64(QByteArray("aaaa")))); /* xs:base64Binary("aaaa"), */
1233 expectedValues.append(QVariant(QByteArray::fromHex(QByteArray("FFFF")))); /* xs:hexBinary("FFFF"), */
1234 expectedValues.append(QVariant(QString::fromLatin1("http://example.com/"))); /* xs:anyURI("http://example.com/"), */
1235 QXmlNamePool np(query.namePool());
1236 expectedValues.append(QVariant(qVariantFromValue(QXmlName(np, QLatin1String("localName"),
1237 QLatin1String("http://example.com/2"),
1238 QLatin1String("prefix")))));
1240 expectedValues.append(QVariant(QString::fromLatin1("An xs:string")));
1241 expectedValues.append(QVariant(QString::fromLatin1("normalizedString")));
1242 expectedValues.append(QVariant(QString::fromLatin1("token")));
1243 expectedValues.append(QVariant(QString::fromLatin1("language")));
1244 expectedValues.append(QVariant(QString::fromLatin1("NMTOKEN")));
1245 expectedValues.append(QVariant(QString::fromLatin1("Name")));
1246 expectedValues.append(QVariant(QString::fromLatin1("NCName")));
1247 expectedValues.append(QVariant(QString::fromLatin1("ID")));
1248 expectedValues.append(QVariant(QString::fromLatin1("IDREF")));
1249 expectedValues.append(QVariant(QString::fromLatin1("ENTITY")));
1252 QXmlItem item(it.next());
1254 while(!item.isNull())
1256 QVERIFY(item.isAtomicValue());
1257 const QVariant produced(item.toAtomicValue());
1259 const QVariant &expected = expectedValues.at(i);
1261 /* For the cases where we can't represent a value in the XDM with Qt,
1262 * we return an invalid QVariant. */
1263 QCOMPARE(expected.isValid(), produced.isValid());
1265 QCOMPARE(produced.type(), expected.type());
1267 if(expected.isValid())
1269 /* This is only needed for xs:decimal though, for some reason. Probably
1270 * just artifacts created somewhere. */
1271 if(produced.type() == QVariant::Double)
1272 QVERIFY(qFuzzyCompare(produced.toDouble(), expected.toDouble()));
1273 else if(qVariantCanConvert<QXmlName>(produced))
1275 /* QVariant::operator==() does identity comparison, it doesn't delegate to operator==() of
1276 * the contained type, unless it's hardcoded into QVariant. */
1277 const QXmlName n1 = qVariantValue<QXmlName>(produced);
1278 const QXmlName n2 = qVariantValue<QXmlName>(expected);
1282 QCOMPARE(produced, expected);
1289 QCOMPARE(i, expectedValues.count());
1293 Send values from Qt into XQuery.
1295 void tst_QXmlQuery::basicQtToXQueryTypeCheck() const
1297 QFile queryFile(QLatin1String(queriesDirectory) + QLatin1String("allAtomicsExternally.xq"));
1298 QVERIFY(queryFile.exists());
1299 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1301 QCOMPARE(QVariant(QDate(1999, 9, 10)).type(), QVariant::Date);
1305 QXmlNamePool np(query.namePool());
1307 const QXmlName name(np, QLatin1String("localname"),
1308 QLatin1String("http://example.com"),
1309 QLatin1String("prefix"));
1311 query.bindVariable(QLatin1String("fromQUrl"), QXmlItem(QUrl(QString::fromLatin1("http://example.com/"))));
1312 query.bindVariable(QLatin1String("fromQByteArray"), QXmlItem(QByteArray("AAAA")));
1313 query.bindVariable(QLatin1String("fromBool"), QXmlItem(bool(true)));
1314 query.bindVariable(QLatin1String("fromQDate"), QXmlItem(QDate(2000, 10, 11)));
1315 // TODO Do with different QDateTime time specs
1316 query.bindVariable(QLatin1String("fromQDateTime"), QXmlItem(QDateTime(QDate(2001, 9, 10), QTime(1, 2, 3))));
1317 query.bindVariable(QLatin1String("fromDouble"), QXmlItem(double(3)));
1318 query.bindVariable(QLatin1String("fromFloat"), QXmlItem(float(4)));
1319 query.bindVariable(QLatin1String("integer"), QXmlItem(5));
1320 query.bindVariable(QLatin1String("fromQString"), QXmlItem(QString::fromLatin1("A QString")));
1321 query.bindVariable(QLatin1String("fromQChar"), QXmlItem(QChar::fromLatin1('C')));
1323 query.bindVariable(QLatin1String("fromIntLiteral"), QXmlItem(QVariant(654)));
1326 QVariant ui(uint(5));
1327 QCOMPARE(ui.type(), QVariant::UInt);
1328 query.bindVariable(QLatin1String("fromUInt"), ui);
1332 QVariant ulnglng(qulonglong(6));
1333 QCOMPARE(ulnglng.type(), QVariant::ULongLong);
1334 query.bindVariable(QLatin1String("fromULongLong"), ulnglng);
1338 QVariant qlnglng(qlonglong(7));
1339 QCOMPARE(qlnglng.type(), QVariant::LongLong);
1340 query.bindVariable(QLatin1String("fromLongLong"), qlnglng);
1343 query.setQuery(&queryFile);
1345 // TODO do queries which declares external variables with types. Tons of combos here.
1346 // TODO ensure that binding with QXmlItem() doesn't make a binding available.
1347 // TODO test rebinding a variable.
1349 QVERIFY(query.isValid());
1352 query.evaluateTo(&it);
1353 QXmlItem item(it.next());
1354 QVERIFY(!item.isNull());
1355 QVERIFY(item.isAtomicValue());
1357 if(sizeof(qreal) == sizeof(float)) //ARM casts to Float not to double
1358 QCOMPARE(item.toAtomicValue().toString(),
1359 QLatin1String("4 true 3 654 7 41414141 C 2000-10-11Z 2001-09-10T01:02:03 "
1360 "A QString http://example.com/ 5 6 true false false true true true true true true true "
1363 QCOMPARE(item.toAtomicValue().toString(),
1364 QLatin1String("4 true 3 654 7 41414141 C 2000-10-11Z 2001-09-10T01:02:03 "
1365 "A QString http://example.com/ 5 6 true true true true true true true true true true "
1370 void tst_QXmlQuery::bindNode() const
1373 TestSimpleNodeModel nodeModel(query.namePool());
1375 query.bindVariable(QLatin1String("node"), nodeModel.root());
1378 QVERIFY(buff.open(QIODevice::WriteOnly));
1380 query.setQuery(QLatin1String("declare variable $node external; $node"));
1381 QXmlSerializer serializer(query, &buff);
1383 QVERIFY(query.evaluateTo(&serializer));
1384 QCOMPARE(out, QByteArray("<nodeName/>"));
1388 Pass in a relative URI, and make sure it is resolved against the current application directory.
1390 void tst_QXmlQuery::relativeBaseURI() const
1393 query.setQuery(QLatin1String("fn:static-base-uri()"), QUrl(QLatin1String("a/relative/uri.weirdExtension")));
1394 QVERIFY(query.isValid());
1397 QBuffer buffer(&result);
1398 QVERIFY(buffer.open(QIODevice::ReadWrite));
1400 QXmlSerializer serializer(query, &buffer);
1401 QVERIFY(query.evaluateTo(&serializer));
1403 const QUrl loaded(QUrl::fromEncoded(result));
1404 QUrl appPath(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()));
1406 QVERIFY(loaded.isValid());
1407 QVERIFY(appPath.isValid());
1408 QVERIFY(!loaded.isRelative());
1409 QVERIFY(!appPath.isRelative());
1411 QFileInfo dir(appPath.toLocalFile());
1412 dir.setFile(QString());
1414 /* We can't use QUrl::isParentOf() because it doesn't do what we want it to */
1415 if(!loaded.toLocalFile().startsWith(dir.absoluteFilePath()))
1416 QTextStream(stderr) << "dir.absoluteFilePath():" << dir.absoluteFilePath() << "loaded.toLocalFile():" << loaded.toLocalFile();
1418 checkBaseURI(loaded, dir.absoluteFilePath());
1421 void tst_QXmlQuery::emptyBaseURI() const
1424 query.setQuery(QLatin1String("fn:static-base-uri()"), QUrl());
1425 QVERIFY(query.isValid());
1428 QBuffer buffer(&result);
1429 QVERIFY(buffer.open(QIODevice::ReadWrite));
1431 QXmlSerializer serializer(query, &buffer);
1432 QVERIFY(query.evaluateTo(&serializer));
1434 const QUrl loaded(QUrl::fromEncoded(result));
1435 QUrl appPath(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()));
1437 QVERIFY(loaded.isValid());
1438 QVERIFY(appPath.isValid());
1439 QVERIFY(!loaded.isRelative());
1440 QVERIFY(!appPath.isRelative());
1442 QFileInfo dir(appPath.toLocalFile());
1443 dir.setFile(QString());
1445 QCOMPARE(loaded, appPath);
1449 Ensure that QDate comes out as QDateTime.
1451 void tst_QXmlQuery::roundTripDateWithinQXmlItem() const
1453 const QDate date(1999, 9, 10);
1454 QVERIFY(date.isValid());
1456 const QVariant variant(date);
1457 QVERIFY(variant.isValid());
1458 QCOMPARE(variant.type(), QVariant::Date);
1460 const QXmlItem item(variant);
1461 QVERIFY(!item.isNull());
1462 QVERIFY(item.isAtomicValue());
1464 const QVariant out(item.toAtomicValue());
1465 QVERIFY(out.isValid());
1466 QCOMPARE(out.type(), QVariant::Date);
1467 QCOMPARE(out.toDate(), date);
1471 Check whether a query is valid, which uses an unbound variable.
1473 void tst_QXmlQuery::bindingMissing() const
1476 MessageSilencer messageHandler;
1477 query.setMessageHandler(&messageHandler);
1479 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1480 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1481 query.setQuery(&queryFile);
1483 QVERIFY(!query.isValid());
1486 void tst_QXmlQuery::bindDefaultConstructedItem() const
1488 QFETCH(QXmlItem, item);
1491 MessageSilencer messageHandler;
1492 query.setMessageHandler(&messageHandler);
1494 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1495 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1496 query.setQuery(&queryFile);
1497 query.bindVariable(QLatin1String("externalVariable"), item);
1499 QVERIFY(!query.isValid());
1502 void tst_QXmlQuery::bindDefaultConstructedItem_data() const
1504 QTest::addColumn<QXmlItem>("item");
1506 QTest::newRow("QXmlItem()") << QXmlItem();
1507 QTest::newRow("QXmlItem(QVariant())") << QXmlItem(QVariant());
1508 QTest::newRow("QXmlItem(QXmlNodeModelIndex())") << QXmlItem(QXmlNodeModelIndex());
1512 Remove a binding by binding QXmlItem() with the same name.
1514 void tst_QXmlQuery::eraseQXmlItemBinding() const
1517 MessageSilencer messageHandler;
1518 query.setMessageHandler(&messageHandler);
1520 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1521 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1522 query.bindVariable(QLatin1String("externalVariable"), QXmlItem(3));
1523 query.setQuery(&queryFile);
1524 QVERIFY(query.isValid());
1527 QBuffer buffer(&result);
1528 QVERIFY(buffer.open(QIODevice::ReadWrite));
1530 QXmlSerializer serializer(query, &buffer);
1531 QVERIFY(query.evaluateTo(&serializer));
1533 QCOMPARE(result, QByteArray("3 6<e>3</e>false"));
1535 query.bindVariable(QLatin1String("externalVariable"), QXmlItem());
1536 QVERIFY(!query.isValid());
1540 Erase a variable binding
1542 void tst_QXmlQuery::eraseDeviceBinding() const
1544 /* Erase an existing QIODevice binding with another QIODevice binding. */
1548 QByteArray doc("<e/>");
1549 QBuffer buffer(&doc);
1550 QVERIFY(buffer.open(QIODevice::ReadOnly));
1552 query.bindVariable(QLatin1String("in"), &buffer);
1553 query.setQuery(QLatin1String("$in"));
1554 QVERIFY(query.isValid());
1556 query.bindVariable(QLatin1String("in"), 0);
1557 QVERIFY(!query.isValid());
1560 /* Erase an existing QXmlItem binding with another QIODevice binding. */
1564 query.bindVariable(QLatin1String("in"), QXmlItem(5));
1565 query.setQuery(QLatin1String("$in"));
1566 QVERIFY(query.isValid());
1568 query.bindVariable(QLatin1String("in"), 0);
1569 QVERIFY(!query.isValid());
1574 Bind a variable, evaluate, bind with a different value but same type, and evaluate again.
1576 void tst_QXmlQuery::rebindVariableSameType() const
1579 MessageSilencer messageHandler;
1580 query.setMessageHandler(&messageHandler);
1582 query.bindVariable(QLatin1String("externalVariable"), QXmlItem(3));
1585 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1586 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1587 query.setQuery(&queryFile);
1590 QVERIFY(query.isValid());
1594 QBuffer buffer(&result);
1595 QVERIFY(buffer.open(QIODevice::ReadWrite));
1597 QXmlSerializer serializer(query, &buffer);
1598 QVERIFY(query.evaluateTo(&serializer));
1600 QCOMPARE(result, QByteArray("3 6<e>3</e>false"));
1604 query.bindVariable(QLatin1String("externalVariable"), QXmlItem(5));
1606 QBuffer buffer(&result);
1607 QVERIFY(buffer.open(QIODevice::ReadWrite));
1609 QXmlSerializer serializer(query, &buffer);
1610 QVERIFY(query.evaluateTo(&serializer));
1612 QCOMPARE(result, QByteArray("5 8<e>5</e>false"));
1617 void tst_QXmlQuery::rebindVariableDifferentType() const
1619 /* Rebind QXmlItem variable with QXmlItem variable. */
1622 query.bindVariable(QLatin1String("in"), QXmlItem(3));
1623 query.setQuery(QLatin1String("$in"));
1624 QVERIFY(query.isValid());
1626 query.bindVariable(QLatin1String("in"), QXmlItem("A string"));
1627 QVERIFY(!query.isValid());
1630 /* Rebind QIODevice variable with QXmlItem variable. */
1634 buffer.setData(QByteArray("<e/>"));
1635 QVERIFY(buffer.open(QIODevice::ReadOnly));
1637 query.bindVariable(QLatin1String("in"), &buffer);
1638 query.setQuery(QLatin1String("$in"));
1639 QVERIFY(query.isValid());
1641 query.bindVariable(QLatin1String("in"), QXmlItem("A string"));
1642 QVERIFY(!query.isValid());
1645 /* Rebind QXmlItem variable with QIODevice variable. The type of the
1646 * variable changes, so a recompile is necessary. */
1650 query.bindVariable(QLatin1String("in"), QXmlItem(QLatin1String("A string")));
1651 query.setQuery(QLatin1String("$in"));
1652 QVERIFY(query.isValid());
1655 buffer.setData(QByteArray("<e/>"));
1656 QVERIFY(buffer.open(QIODevice::ReadOnly));
1657 query.bindVariable(QLatin1String("in"), &buffer);
1658 QVERIFY(!query.isValid());
1662 void tst_QXmlQuery::rebindVariableWithNullItem() const
1666 query.bindVariable(QLatin1String("name"), QXmlItem(5));
1667 query.bindVariable(QLatin1String("name"), QXmlItem());
1670 void tst_QXmlQuery::constCorrectness() const
1672 QXmlResultItems result;
1674 tmp.setQuery(QLatin1String("1")); /* Just so we have a valid query. */
1675 const QXmlQuery query(tmp);
1677 /* These functions should be const. */
1679 query.evaluateTo(&result);
1681 query.uriResolver();
1682 query.messageHandler();
1685 QString dummyString;
1686 QTextStream dummyStream(&dummyString);
1687 PushBaseliner dummy(dummyStream, query.namePool());
1688 query.evaluateTo(&dummy);
1692 void tst_QXmlQuery::objectSize() const
1694 /* We have a d pointer. */
1695 QCOMPARE(sizeof(QXmlQuery), sizeof(void *));
1698 void tst_QXmlQuery::setUriResolver() const
1700 /* Set a null resolver, and make sure it can take a const pointer. */
1703 query.setUriResolver(static_cast<const QAbstractUriResolver *>(0));
1704 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0));
1708 TestURIResolver resolver;
1710 query.setUriResolver(&resolver);
1711 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(&resolver));
1715 void tst_QXmlQuery::uriResolver() const
1717 /* Check default value. */
1720 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0));
1724 void tst_QXmlQuery::messageXML() const
1728 MessageValidator messageValidator;
1729 query.setMessageHandler(&messageValidator);
1731 query.setQuery(QLatin1String("1basicSyntaxError"));
1733 const QRegExp removeFilename(QLatin1String("Location: file:.*\\#"));
1734 QVERIFY(removeFilename.isValid());
1736 QVERIFY(messageValidator.success());
1737 QCOMPARE(messageValidator.received().remove(removeFilename),
1738 QString::fromLatin1("Type:3\n"
1739 "Description: <html xmlns='http://www.w3.org/1999/xhtml/'><body><p>syntax error, unexpected unknown keyword</p></body></html>\n"
1740 "Identifier: http://www.w3.org/2005/xqt-errors#XPST0003\n"
1745 1. Allocate QXmlResultItems
1746 2. Allocate QXmlQuery
1747 3. evaluate to the QXmlResultItems instance
1748 4. Dellocate the QXmlQuery instance
1749 5. Ensure QXmlResultItems works
1751 void tst_QXmlQuery::resultItemsDeallocatedQuery() const
1753 QXmlResultItems result;
1757 query.setQuery(QLatin1String("1, 2, xs:integer(<e>3</e>)"));
1758 query.evaluateTo(&result);
1761 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
1762 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
1763 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
1764 QVERIFY(result.next().isNull());
1765 QVERIFY(!result.hasError());
1769 1. Bind variable with bindVariable()
1770 2. setQuery that has 'declare variable' with same name.
1771 3. Ensure the value inside the query is used. We don't guarantee this behavior
1772 but that's what we lock.
1774 void tst_QXmlQuery::shadowedVariables() const
1777 query.bindVariable("varName", QXmlItem(3));
1778 query.setQuery(QLatin1String("declare variable $varName := 5; $varName"));
1780 QXmlResultItems result;
1781 query.evaluateTo(&result);
1783 QCOMPARE(result.next().toAtomicValue(), QVariant(5));
1786 void tst_QXmlQuery::setFocusQXmlItem() const
1788 /* Make sure we can take a const reference. */
1791 const QXmlItem item;
1792 query.setFocus(item);
1795 // TODO evaluate with atomic value, check type
1796 // TODO evaluate with node, check type
1797 // TODO ensure that setFocus() triggers query recompilation, as appropriate.
1798 // TODO let the focus be undefined, call isvalid, call evaluate anyway
1799 // TODO let the focus be undefined, call evaluate directly
1802 void tst_QXmlQuery::setFocusQUrl() const
1804 /* Load a focus which isn't well-formed. */
1807 MessageSilencer silencer;
1809 query.setMessageHandler(&silencer);
1811 QVERIFY(!query.setFocus(QUrl(QLatin1String("data/notWellformed.xml"))));
1814 /* Ensure the same URI resolver is used. */
1816 QXmlQuery query(QXmlQuery::XSLT20);
1818 const TestURIResolver resolver(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
1819 query.setUriResolver(&resolver);
1821 QVERIFY(query.setFocus(QUrl(QLatin1String("arbitraryURI"))));
1822 query.setQuery(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/copyWholeDocument.xsl"))));
1823 QVERIFY(query.isValid());
1826 QVERIFY(result.open(QIODevice::ReadWrite));
1827 QXmlSerializer serializer(query, &result);
1828 query.evaluateTo(&serializer);
1830 QCOMPARE(result.data(), QByteArray("<doc/>"));
1833 // TODO ensure that the focus type doesn't change from XSLT20 on the main instance.
1837 This code poses a challenge wrt. to internal caching.
1839 void tst_QXmlQuery::setFocusQIODevice() const
1845 focus.setData(QByteArray("<e>abc</e>"));
1846 QVERIFY(focus.open(QIODevice::ReadOnly));
1847 query.setFocus(&focus);
1848 query.setQuery(QLatin1String("string()"));
1849 QVERIFY(query.isValid());
1852 query.evaluateTo(&output);
1854 QCOMPARE(output, QString::fromLatin1("abc\n"));
1857 /* Set a new focus, make sure it changes & works. */
1860 focus2.setData(QByteArray("<e>abc2</e>"));
1861 QVERIFY(focus2.open(QIODevice::ReadOnly));
1862 query.setFocus(&focus2);
1863 QVERIFY(query.isValid());
1866 query.evaluateTo(&output);
1868 QCOMPARE(output, QString::fromLatin1("abc2\n"));
1873 Since we internally use variable bindings for implementing the focus, we need
1874 to make sure we don't clash in this area.
1876 void tst_QXmlQuery::setFocusQIODeviceAvoidVariableClash() const
1879 buffer.setData("<e>focus</e>");
1880 QVERIFY(buffer.open(QIODevice::ReadOnly));
1882 /* First we bind the variable name, then the focus. */
1885 query.bindVariable(QString(QLatin1Char('u')), QVariant(1));
1886 query.setFocus(&buffer);
1887 query.setQuery(QLatin1String("string()"));
1890 query.evaluateTo(&out);
1892 QCOMPARE(out, QString::fromLatin1("focus\n"));
1895 /* First we bind the focus, then the variable name. */
1898 QVERIFY(buffer.open(QIODevice::ReadOnly));
1899 query.setFocus(&buffer);
1900 query.bindVariable(QString(QLatin1Char('u')), QVariant(1));
1901 query.setQuery(QLatin1String("string()"));
1904 query.evaluateTo(&out);
1906 QCOMPARE(out, QString::fromLatin1("focus\n"));
1910 void tst_QXmlQuery::setFocusQIODeviceFailure() const
1912 /* A not well-formed input document. */
1916 MessageSilencer silencer;
1917 query.setMessageHandler(&silencer);
1920 input.setData("<e");
1921 QVERIFY(input.open(QIODevice::ReadOnly));
1923 QCOMPARE(query.setFocus(&input), false);
1927 void tst_QXmlQuery::setFocusQString() const
1931 /* Basic use of focus. */
1933 QVERIFY(query.setFocus(QLatin1String("<e>textNode</e>")));
1934 query.setQuery(QLatin1String("string()"));
1935 QVERIFY(query.isValid());
1937 query.evaluateTo(&out);
1938 QCOMPARE(out, QString::fromLatin1("textNode\n"));
1941 /* Set to a new focus, make sure it changes and works. */
1943 QVERIFY(query.setFocus(QLatin1String("<e>newFocus</e>")));
1945 query.evaluateTo(&out);
1946 QCOMPARE(out, QString::fromLatin1("newFocus\n"));
1950 void tst_QXmlQuery::setFocusQStringFailure() const
1953 MessageSilencer silencer;
1955 query.setMessageHandler(&silencer);
1956 QVERIFY(!query.setFocus(QLatin1String("<notWellformed")));
1958 /* Let's try the slight special case of a null string. */
1959 QVERIFY(!query.setFocus(QString()));
1962 void tst_QXmlQuery::setFocusQStringSignature() const
1965 MessageSilencer silencer;
1966 query.setMessageHandler(&silencer);
1968 const QString argument;
1969 /* We should take a const ref. */
1970 query.setFocus(argument);
1972 /* We should return a bool. */
1973 static_cast<bool>(query.setFocus(QString()));
1976 void tst_QXmlQuery::setFocusQIODeviceTriggerWarnings() const
1978 /* A null pointer. */
1982 QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed.");
1983 QCOMPARE(query.setFocus(static_cast<QIODevice *>(0)), false);
1986 /* A non opened-device. */
1990 QBuffer notReadable;
1991 QVERIFY(!notReadable.isReadable());
1993 QTest::ignoreMessage(QtWarningMsg, "The device must be readable.");
1994 QCOMPARE(query.setFocus(¬Readable), false);
1998 void tst_QXmlQuery::fnDocNetworkAccessSuccess() const
2000 #if defined(Q_OS_WINCE) && !defined(_X86_)
2001 QStringList testsToSkip;
2002 testsToSkip << "http scheme" << "ftp scheme";
2003 if (testsToSkip.contains(QTest::currentDataTag()))
2004 QSKIP("Network tests are currently unsupported on Windows CE.", SkipSingle);
2007 QFETCH(QUrl, uriToOpen);
2008 QFETCH(QByteArray, expectedOutput);
2010 if(!uriToOpen.isValid())
2011 qDebug() << "uriToOpen:" << uriToOpen;
2013 QVERIFY(uriToOpen.isValid());
2016 query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen));
2017 query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)"));
2018 QVERIFY(query.isValid());
2021 QBuffer buffer(&result);
2022 QVERIFY(buffer.open(QIODevice::WriteOnly));
2024 QXmlSerializer serializer(query, &buffer);
2025 QVERIFY(query.evaluateTo(&serializer));
2027 QCOMPARE(result, expectedOutput);
2030 void tst_QXmlQuery::fnDocNetworkAccessSuccess_data() const
2032 QTest::addColumn<QUrl>("uriToOpen");
2033 QTest::addColumn<QByteArray>("expectedOutput");
2035 QTest::newRow("file scheme")
2036 << inputFileAsURI(QLatin1String(SRCDIR "input.xml"))
2037 << QByteArray("<!-- This is just a file for testing. --><input/>");
2039 QTest::newRow("data scheme with ASCII")
2040 /* QUrl::toPercentEncoding(QLatin1String("<e/>")) yields "%3Ce%2F%3E". */
2041 << QUrl::fromEncoded("data:application/xml,%3Ce%2F%3E")
2042 << QByteArray("<e/>");
2044 QTest::newRow("data scheme with ASCII no MIME type")
2045 << QUrl::fromEncoded("data:,%3Ce%2F%3E")
2046 << QByteArray("<e/>");
2048 QTest::newRow("data scheme with base 64")
2049 << QUrl::fromEncoded("data:application/xml;base64,PGUvPg==")
2050 << QByteArray("<e/>");
2052 QTest::newRow("qrc scheme")
2053 << QUrl::fromEncoded("qrc:/QXmlQueryTestData/data/oneElement.xml")
2054 << QByteArray("<oneElement/>");
2059 QTest::newRow("http scheme")
2060 << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/wellFormed.xml"))
2061 << QByteArray("<!-- a comment --><e from=\"http\">Some Text</e>");
2063 QTest::newRow("ftp scheme")
2064 << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/wellFormed.xml"))
2065 << QByteArray("<!-- a comment --><e from=\"ftp\">Some Text</e>");
2069 void tst_QXmlQuery::fnDocNetworkAccessFailure() const
2071 QFETCH(QUrl, uriToOpen);
2073 QVERIFY(uriToOpen.isValid());
2076 MessageSilencer silencer;
2077 query.setMessageHandler(&silencer);
2078 query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen));
2079 query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)"));
2080 QVERIFY(query.isValid());
2082 QXmlResultItems result;
2083 query.evaluateTo(&result);
2085 while(!result.next().isNull())
2087 /* Just loop until the end. */
2090 // TODO do something that triggers a /timeout/.
2091 QVERIFY(result.hasError());
2094 void tst_QXmlQuery::fnDocNetworkAccessFailure_data() const
2096 QTest::addColumn<QUrl>("uriToOpen");
2098 QTest::newRow("data scheme, not-well-formed")
2099 << QUrl(QLatin1String("data:application/xml;base64,PGUvg==="));
2101 QTest::newRow("file scheme, non-existant file")
2102 << QUrl(QLatin1String("file:///example.com/does/notExist.xml"));
2104 QTest::newRow("http scheme, file not found")
2105 << QUrl(QLatin1String("http://www.example.com/does/not/exist.xml"));
2107 QTest::newRow("http scheme, nonexistent host")
2108 << QUrl(QLatin1String("http://this.host.does.not.exist.I.SWear"));
2110 QTest::newRow("qrc scheme, not well-formed")
2111 << QUrl(QLatin1String("qrc:/QXmlQueryTestData/notWellformed.xml"));
2113 QTest::newRow("'qrc:/', non-existing file")
2114 << QUrl(QLatin1String(":/QXmlQueryTestData/data/thisFileDoesNotExist.xml"));
2116 QTest::newRow("':/', this scheme is not supported")
2117 << QUrl(QLatin1String(":/QXmlQueryTestData/data/notWellformed.xml"));
2122 QTest::newRow("http scheme, not well-formed")
2123 << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/notWellformed.xml"));
2125 QTest::newRow("https scheme, not well-formed")
2126 << QUrl(QString("https://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/notWellformedViaHttps.xml"));
2128 QTest::newRow("https scheme, nonexistent host")
2129 << QUrl(QLatin1String("https://this.host.does.not.exist.I.SWear"));
2131 QTest::newRow("ftp scheme, nonexistent host")
2132 << QUrl(QLatin1String("ftp://this.host.does.not.exist.I.SWear"));
2134 QTest::newRow("ftp scheme, not well-formed")
2135 << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/notWellFormed.xml"));
2139 Create a network timeout from a QIODevice binding such
2140 that we ensure we don't hang infinitely.
2142 void tst_QXmlQuery::fnDocOnQIODeviceTimeout() const
2148 server.listen(QHostAddress::LocalHost, 1088);
2151 client.connectToHost("localhost", 1088);
2152 QVERIFY(client.isReadable());
2156 MessageSilencer silencer;
2157 query.setMessageHandler(&silencer);
2159 query.bindVariable(QLatin1String("inDevice"), &client);
2160 query.setQuery(QLatin1String("declare variable $inDevice external;\ndoc($inDevice)"));
2161 QVERIFY(query.isValid());
2163 QXmlResultItems result;
2164 query.evaluateTo(&result);
2165 QXmlItem next(result.next());
2167 while(!next.isNull())
2169 next = result.next();
2172 QVERIFY(result.hasError());
2176 When changing query, the static context must change too, such that
2177 the source locations are updated.
2179 void tst_QXmlQuery::recompilationWithEvaluateToResultFailing() const
2182 MessageSilencer silencer;
2183 query.setMessageHandler(&silencer);
2185 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2186 QVERIFY(query.isValid()); /* Trigger query compilation. */
2188 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2189 QVERIFY(query.isValid()); /* Trigger second compilation. */
2191 QXmlResultItems items;
2192 query.evaluateTo(&items);
2194 QVERIFY(items.hasError());
2197 void tst_QXmlQuery::secondEvaluationWithEvaluateToResultFailing() const
2200 MessageSilencer silencer;
2201 query.setMessageHandler(&silencer);
2203 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2204 QVERIFY(query.isValid()); /* Trigger query compilation. */
2206 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2207 /* We don't call isValid(). */
2208 QXmlResultItems items;
2209 query.evaluateTo(&items);
2211 QVERIFY(items.hasError());
2215 Compilation is triggered in the evaluation function due to no call to QXmlQuery::isValid().
2217 void tst_QXmlQuery::recompilationWithEvaluateToReceiver() const
2220 MessageSilencer silencer;
2221 query.setMessageHandler(&silencer);
2223 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2224 QVERIFY(query.isValid()); /* Trigger query compilation. */
2226 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2227 /* We don't call isValid(). */
2230 QBuffer buffer(&dummy);
2231 buffer.open(QIODevice::WriteOnly);
2233 QXmlSerializer serializer(query, &buffer);
2235 QVERIFY(!query.evaluateTo(&serializer));
2238 void tst_QXmlQuery::evaluateToQStringListOnInvalidQuery() const
2240 MessageSilencer silencer;
2242 /* Invoke on a default constructed object. */
2246 QVERIFY(!query.evaluateTo(&out));
2249 /* Invoke on a syntactically invalid query. */
2253 MessageSilencer silencer;
2255 query.setMessageHandler(&silencer);
2256 query.setQuery(QLatin1String("1 + "));
2258 QVERIFY(!query.evaluateTo(&out));
2261 /* Invoke on a query with the wrong type, one atomic. */
2266 query.setQuery(QLatin1String("1"));
2267 query.setMessageHandler(&silencer);
2268 QVERIFY(!query.evaluateTo(&out));
2271 /* Invoke on a query with the wrong type, one element. */
2276 query.setQuery(QLatin1String("<e/>"));
2277 QVERIFY(!query.evaluateTo(&out));
2280 /* Invoke on a query with the wrong type, mixed nodes & atomics. */
2285 query.setQuery(QLatin1String("<e/>, 1, 'a string'"));
2286 query.setMessageHandler(&silencer);
2287 QVERIFY(!query.evaluateTo(&out));
2290 /* Evaluate the empty sequence. */
2295 query.setQuery(QLatin1String("()"));
2296 QVERIFY(!query.evaluateTo(&out));
2297 QVERIFY(out.isEmpty());
2301 void tst_QXmlQuery::evaluateToQStringList() const
2303 QFETCH(QString, queryString);
2304 QFETCH(QStringList, expectedOutput);
2307 query.setQuery(queryString);
2309 QVERIFY(query.isValid());
2311 QVERIFY(query.evaluateTo(&out));
2313 QCOMPARE(out, expectedOutput);
2316 void tst_QXmlQuery::evaluateToQStringListTriggerWarnings() const
2320 QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed.");
2321 QCOMPARE(query.evaluateTo(static_cast<QStringList *>(0)),
2325 void tst_QXmlQuery::evaluateToQStringList_data() const
2327 QTest::addColumn<QString>("queryString");
2328 QTest::addColumn<QStringList>("expectedOutput");
2330 QTest::newRow("One atomic")
2331 << QString::fromLatin1("(1 + 1) cast as xs:string")
2332 << QStringList(QString::fromLatin1("2"));
2335 QStringList expected;
2336 expected << QLatin1String("2");
2337 expected << QLatin1String("a string");
2339 QTest::newRow("Two atomics")
2340 << QString::fromLatin1("(1 + 1) cast as xs:string, 'a string'")
2344 QTest::newRow("A query which evaluates to sub-types of xs:string.")
2345 << QString::fromLatin1("xs:NCName('NCName'), xs:normalizedString(' a b c ')")
2346 << (QStringList() << QString::fromLatin1("NCName")
2347 << QString::fromLatin1(" a b c "));
2349 QTest::newRow("A query which evaluates to two elements.")
2350 << QString::fromLatin1("string(<e>theString1</e>), string(<e>theString2</e>)")
2351 << (QStringList() << QString::fromLatin1("theString1")
2352 << QString::fromLatin1("theString2"));
2356 Ensure that we don't automatically convert non-xs:string values.
2358 void tst_QXmlQuery::evaluateToQStringListNoConversion() const
2361 query.setQuery(QString::fromLatin1("<e/>"));
2362 QVERIFY(query.isValid());
2364 QVERIFY(!query.evaluateTo(&result));
2367 void tst_QXmlQuery::evaluateToQIODevice() const
2369 /* an XQuery, check that no indentation is performed. */
2372 QVERIFY(out.open(QIODevice::ReadWrite));
2375 query.setQuery(QLatin1String("<a><b/></a>"));
2376 QVERIFY(query.isValid());
2377 QVERIFY(query.evaluateTo(&out));
2378 QCOMPARE(out.data(), QByteArray("<a><b/></a>"));
2382 void tst_QXmlQuery::evaluateToQIODeviceTriggerWarnings() const
2386 QTest::ignoreMessage(QtWarningMsg, "The pointer to the device cannot be null.");
2387 QCOMPARE(query.evaluateTo(static_cast<QIODevice *>(0)),
2392 QTest::ignoreMessage(QtWarningMsg, "The device must be writable.");
2393 QCOMPARE(query.evaluateTo(&buffer),
2397 void tst_QXmlQuery::evaluateToQIODeviceSignature() const
2399 /* The function should be const. */
2402 QVERIFY(out.open(QIODevice::ReadWrite));
2404 const QXmlQuery query;
2406 query.evaluateTo(&out);
2410 void tst_QXmlQuery::evaluateToQIODeviceOnInvalidQuery() const
2413 QVERIFY(out.open(QIODevice::WriteOnly));
2415 /* On syntactically invalid query. */
2418 MessageSilencer silencer;
2419 query.setMessageHandler(&silencer);
2420 query.setQuery(QLatin1String("1 +"));
2421 QVERIFY(!query.isValid());
2422 QVERIFY(!query.evaluateTo(&out));
2425 /* On null QXmlQuery instance. */
2428 QVERIFY(!query.evaluateTo(&out));
2433 void tst_QXmlQuery::setQueryQIODeviceQUrl() const
2438 buffer.setData("1, 2, 2 + 1");
2439 QVERIFY(buffer.open(QIODevice::ReadOnly));
2442 query.setQuery(&buffer);
2443 QVERIFY(query.isValid());
2445 QXmlResultItems result;
2446 query.evaluateTo(&result);
2447 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
2448 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
2449 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
2450 QVERIFY(result.next().isNull());
2451 QVERIFY(!result.hasError());
2454 /* Set query that is invalid. */
2457 buffer.setData("1, ");
2458 QVERIFY(buffer.open(QIODevice::ReadOnly));
2461 MessageSilencer silencer;
2462 query.setMessageHandler(&silencer);
2463 query.setQuery(&buffer);
2464 QVERIFY(!query.isValid());
2467 /* Check that the base URI passes through. */
2470 buffer.setData("string(static-base-uri())");
2471 QVERIFY(buffer.open(QIODevice::ReadOnly));
2474 query.setQuery(&buffer, QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl"));
2475 QVERIFY(query.isValid());
2478 query.evaluateTo(&result);
2479 QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
2483 void tst_QXmlQuery::setQueryQIODeviceQUrlTriggerWarnings() const
2486 QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed.");
2490 QTest::ignoreMessage(QtWarningMsg, "The device must be readable.");
2491 query.setQuery(&buffer);
2494 void tst_QXmlQuery::setQueryQString() const
2499 query.setQuery(QLatin1String("1, 2, 2 + 1"));
2500 QVERIFY(query.isValid());
2502 QXmlResultItems result;
2503 query.evaluateTo(&result);
2504 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
2505 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
2506 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
2507 QVERIFY(result.next().isNull());
2508 QVERIFY(!result.hasError());
2511 /* Set query that is invalid. */
2513 MessageSilencer silencer;
2515 query.setMessageHandler(&silencer);
2516 query.setQuery(QLatin1String("1, "));
2517 QVERIFY(!query.isValid());
2520 /* Check that the base URI passes through. */
2523 query.setQuery(QLatin1String("string(static-base-uri())"), QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl"));
2524 QVERIFY(query.isValid());
2527 query.evaluateTo(&result);
2528 QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
2532 void tst_QXmlQuery::setQueryQUrlSuccess() const
2534 #if defined(Q_OS_WINCE) && !defined(_X86_)
2535 QStringList testsToSkip;
2536 testsToSkip << "A valid query via the ftp scheme" << "A valid query via the http scheme";
2537 if (testsToSkip.contains(QTest::currentDataTag()))
2538 QSKIP("Network tests are currently unsupported on Windows CE.", SkipSingle);
2541 QFETCH(QUrl, queryURI);
2542 QFETCH(QByteArray, expectedOutput);
2544 QVERIFY(queryURI.isValid());
2548 MessageSilencer silencer;
2549 query.setMessageHandler(&silencer);
2551 query.setQuery(queryURI);
2552 QVERIFY(query.isValid());
2555 QBuffer buffer(&out);
2556 QVERIFY(buffer.open(QIODevice::WriteOnly));
2557 QXmlSerializer serializer(query, &buffer);
2559 query.evaluateTo(&serializer);
2560 QCOMPARE(out, expectedOutput);
2563 void tst_QXmlQuery::setQueryQUrlSuccess_data() const
2565 QTest::addColumn<QUrl>("queryURI");
2566 QTest::addColumn<QByteArray>("expectedOutput");
2568 QTest::newRow("A valid query via the data scheme")
2569 << QUrl::fromEncoded("data:application/xml,1%20%2B%201") /* "1 + 1" */
2572 QTest::newRow("A valid query via the file scheme")
2573 << QUrl::fromLocalFile(inputFile(QLatin1String(queriesDirectory) + QLatin1String("onePlusOne.xq")))
2579 QTest::newRow("A valid query via the ftp scheme")
2580 << QUrl::fromEncoded(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/viaFtp.xq").toLatin1())
2581 << QByteArray("This was received via FTP");
2583 QTest::newRow("A valid query via the http scheme")
2584 << QUrl::fromEncoded(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/viaHttp.xq").toLatin1())
2585 << QByteArray("This was received via HTTP.");
2588 void tst_QXmlQuery::setQueryQUrlFailSucceed() const
2591 MessageSilencer silencer;
2593 query.setMessageHandler(&silencer);
2595 query.setQuery(QLatin1String("1 + 1"));
2596 QVERIFY(query.isValid());
2598 query.setQuery(QUrl::fromEncoded("file://example.com/does/not/exist"));
2599 QVERIFY(!query.isValid());
2602 void tst_QXmlQuery::setQueryQUrlFailure() const
2604 QFETCH(QUrl, queryURI);
2606 MessageSilencer silencer;
2609 query.setMessageHandler(&silencer);
2610 query.setQuery(queryURI);
2611 QVERIFY(!query.isValid());
2614 void tst_QXmlQuery::setQueryQUrlFailure_data() const
2616 QTest::addColumn<QUrl>("queryURI");
2618 QTest::newRow("Query via file:// that does not exist.")
2619 << QUrl::fromEncoded("file://example.com/does/not/exist");
2621 QTest::newRow("A query via file:// that is completely empty, but readable.")
2622 << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("../xmlpatterns/queries/completelyEmptyQuery.xq"));
2625 const QString name(QLatin1String("nonReadableFile.xq"));
2626 QFile outFile(name);
2627 QVERIFY(outFile.open(QIODevice::WriteOnly));
2628 outFile.write(QByteArray("1"));
2630 /* On some windows versions, this fails, so we don't check that this works with QVERIFY. */
2631 outFile.setPermissions(QFile::Permissions(QFile::Permissions()));
2633 QTest::newRow("Query via file:/ that does not have read permissions.")
2634 << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("nonReadableFile.xq"));
2640 QTest::newRow("Query via HTTP that does not exist.")
2641 << QUrl::fromEncoded("http://example.com/NoQuery/ISWear");
2644 QTest::newRow("Query via FTP that does not exist.")
2645 << QUrl::fromEncoded("ftp://example.com/NoQuery/ISWear");
2648 QTest::newRow("A query via http:// that is completely empty, but readable.")
2649 << QUrl::fromEncoded(QString(
2650 "http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/completelyEmptyQuery.xq").toLatin1());
2652 QTest::newRow("A query via ftp:// that is completely empty, but readable.")
2653 << QUrl::fromEncoded(QString(
2654 "ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/completelyEmptyQuery.xq").toLatin1());
2658 void tst_QXmlQuery::setQueryQUrlBaseURI() const
2660 QFETCH(QUrl, inputBaseURI);
2661 QFETCH(QUrl, expectedBaseURI);
2665 query.setQuery(QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq")), inputBaseURI);
2666 QVERIFY(query.isValid());
2669 QVERIFY(query.evaluateTo(&result));
2670 QCOMPARE(result.count(), 1);
2672 if(qstrcmp(QTest::currentDataTag(), "Relative base URI") == 0)
2673 checkBaseURI(QUrl(result.first()), QCoreApplication::applicationFilePath());
2675 QCOMPARE(result.first(), expectedBaseURI.toString());
2678 void tst_QXmlQuery::setQueryQUrlBaseURI_data() const
2680 QTest::addColumn<QUrl>("inputBaseURI");
2681 QTest::addColumn<QUrl>("expectedBaseURI");
2683 QTest::newRow("absolute HTTP")
2684 << QUrl(QLatin1String("http://www.example.com/"))
2685 << QUrl(QLatin1String("http://www.example.com/"));
2687 QTest::newRow("None, so the query URI is used")
2689 << QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq"));
2691 QTest::newRow("Relative base URI")
2692 << QUrl(QLatin1String("../data/relative.uri"))
2697 1. Create a valid query.
2698 2. Call setQuery(QUrl), with a query file that doesn't exist.
2699 3. Verify that the query has changed state into invalid.
2701 void tst_QXmlQuery::setQueryWithNonExistentQUrlOnValidQuery() const
2705 MessageSilencer messageSilencer;
2706 query.setMessageHandler(&messageSilencer);
2708 query.setQuery(QLatin1String("1 + 1"));
2709 QVERIFY(query.isValid());
2711 query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/DOESNOTEXIST.xq"));
2712 QVERIFY(!query.isValid());
2716 1. Create a valid query.
2717 2. Call setQuery(QUrl), with a query file that is invalid.
2718 3. Verify that the query has changed state into invalid.
2720 void tst_QXmlQuery::setQueryWithInvalidQueryFromQUrlOnValidQuery() const
2724 MessageSilencer messageSilencer;
2725 query.setMessageHandler(&messageSilencer);
2727 query.setQuery(QLatin1String("1 + 1"));
2728 QVERIFY(query.isValid());
2730 query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/queries/syntaxError.xq"));
2731 QVERIFY(!query.isValid());
2735 This triggered two bugs:
2737 - First, the DynamicContext wasn't assigned to QXmlResultItems, meaning it went out of
2738 scope and therefore deallocated the document pool, and calls
2739 to QXmlResultItems::next() would use dangling pointers.
2741 - Conversion between QPatternist::Item and QXmlItem was incorrectly done, leading to nodes
2742 being treated as atomic values, and subsequent crashes.
2745 void tst_QXmlQuery::retrieveNameFromQuery() const
2747 QFETCH(QString, queryString);
2748 QFETCH(QString, expectedName);
2751 query.setQuery(queryString);
2752 QVERIFY(query.isValid());
2753 QXmlResultItems result;
2754 query.evaluateTo(&result);
2756 QVERIFY(!result.hasError());
2758 const QXmlItem item(result.next());
2759 QVERIFY(!result.hasError());
2760 QVERIFY(!item.isNull());
2761 QVERIFY(item.isNode());
2763 const QXmlNodeModelIndex node(item.toNodeModelIndex());
2764 QVERIFY(!node.isNull());
2766 QCOMPARE(node.model()->name(node).localName(query.namePool()), expectedName);
2769 void tst_QXmlQuery::retrieveNameFromQuery_data() const
2771 QTest::addColumn<QString>("queryString");
2772 QTest::addColumn<QString>("expectedName");
2774 QTest::newRow("Document-node")
2775 << QString::fromLatin1("document{<elementName/>}")
2778 QTest::newRow("Element")
2779 << QString::fromLatin1("document{<elementName/>}/*")
2780 << QString::fromLatin1("elementName");
2784 Binding a null QString leads to no variable binding, but an
2785 empty non-null QString is possible.
2787 void tst_QXmlQuery::bindEmptyNullString() const
2789 MessageSilencer messageHandler;
2791 query.setMessageHandler(&messageHandler);
2792 query.setQuery(QLatin1String("declare variable $v external; $v"));
2793 /* Here, we effectively pass an invalid QVariant. */
2794 query.bindVariable(QLatin1String("v"), QVariant(QString()));
2795 QVERIFY(!query.isValid());
2798 QVERIFY(!query.evaluateTo(&result));
2801 void tst_QXmlQuery::bindEmptyString() const
2804 query.bindVariable(QLatin1String("v"), QVariant(QString(QLatin1String(""))));
2805 query.setQuery(QLatin1String("declare variable $v external; $v"));
2806 QVERIFY(query.isValid());
2809 QVERIFY(query.evaluateTo(&result));
2810 QStringList expected((QString()));
2811 QCOMPARE(result, expected);
2814 void tst_QXmlQuery::cleanupTestCase() const
2816 /* Remove a weird file we created. */
2817 const QString name(QLatin1String("nonReadableFile.xq"));
2819 if(QFile::exists(name))
2822 QVERIFY(file.setPermissions(QFile::WriteOwner));
2823 QVERIFY(file.remove());
2827 void tst_QXmlQuery::declareUnavailableExternal() const
2830 MessageSilencer silencer;
2831 query.setMessageHandler(&silencer);
2832 query.setQuery(QLatin1String("declare variable $var external;"
2834 /* We do not bind $var with QXmlQuery::bindVariable(). */
2835 QVERIFY(!query.isValid());
2839 This test triggers an assert in one of the cache iterator
2840 with MSVC 2005 when compiled in debug mode.
2842 void tst_QXmlQuery::msvcCacheIssue() const
2845 query.bindVariable(QLatin1String("externalVariable"), QXmlItem("Variable Value"));
2846 query.setQuery(QUrl::fromLocalFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariableUsedTwice.xq")));
2848 QVERIFY(query.evaluateTo(&result));
2851 QStringList() << QString::fromLatin1("Variable Value") << QString::fromLatin1("Variable Value"));
2854 void tst_QXmlQuery::unavailableExternalVariable() const
2858 MessageSilencer silencer;
2859 query.setMessageHandler(&silencer);
2861 query.setQuery(QLatin1String("declare variable $foo external; 1"));
2863 QVERIFY(!query.isValid());
2867 Ensure that setUriResolver() affects \c fn:doc() and \c fn:doc-available().
2869 void tst_QXmlQuery::useUriResolver() const
2871 class TestUriResolver : public QAbstractUriResolver
2872 , private TestFundament
2875 TestUriResolver() {}
2876 virtual QUrl resolve(const QUrl &relative,
2877 const QUrl &baseURI) const
2880 return baseURI.resolved(inputFile(QLatin1String(queriesDirectory) + QLatin1String("simpleDocument.xml")));
2884 const TestUriResolver uriResolver;
2887 query.setUriResolver(&uriResolver);
2888 query.setQuery(QLatin1String("let $i := 'http://www.example.com/DoesNotExist'"
2889 "return (string(doc($i)), doc-available($i))"));
2892 QXmlResultItems result;
2893 query.evaluateTo(&result);
2895 QVERIFY(!result.hasError());
2896 QCOMPARE(result.next().toAtomicValue().toString(), QString::fromLatin1("text text node"));
2897 QCOMPARE(result.next().toAtomicValue().toBool(), true);
2898 QVERIFY(result.next().isNull());
2899 QVERIFY(!result.hasError());
2902 void tst_QXmlQuery::queryWithFocusAndVariable() const
2905 query.setFocus(QXmlItem(5));
2906 query.bindVariable(QLatin1String("var"), QXmlItem(2));
2908 query.setQuery(QLatin1String("string(. * $var)"));
2912 QVERIFY(query.evaluateTo(&result));
2914 QCOMPARE(result, QStringList(QLatin1String("10")));
2917 void tst_QXmlQuery::undefinedFocus() const
2921 MessageSilencer silencer;
2922 query.setMessageHandler(&silencer);
2924 query.setQuery(QLatin1String("."));
2925 QVERIFY(!query.isValid());
2928 void tst_QXmlQuery::basicFocusUsage() const
2932 MessageSilencer silencer;
2933 query.setMessageHandler(&silencer);
2935 query.setFocus(QXmlItem(5));
2936 query.setQuery(QLatin1String("string(. * .)"));
2937 QVERIFY(query.isValid());
2940 QVERIFY(query.evaluateTo(&result));
2942 QCOMPARE(result, QStringList(QLatin1String("25")));
2946 Triggers an ownership related crash.
2948 void tst_QXmlQuery::copyCheckMessageHandler() const
2951 QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0));
2953 query.setQuery(QLatin1String("doc('qrc:/QXmlQueryTestData/data/oneElement.xml')"));
2954 /* By now, we should have set the builtin message handler. */
2955 const QAbstractMessageHandler *const messageHandler = query.messageHandler();
2956 QVERIFY(messageHandler);
2959 /* This copies QXmlQueryPrivate::m_ownerObject, and its destructor
2960 * will delete it, and hence the builtin message handler attached to it. */
2961 QXmlQuery copy(query);
2964 QXmlResultItems result;
2965 query.evaluateTo(&result);
2967 while(!result.next().isNull())
2970 QVERIFY(!result.hasError());
2973 void tst_QXmlQuery::queryLanguage() const
2975 /* Check default value. */
2977 const QXmlQuery query;
2978 QCOMPARE(query.queryLanguage(), QXmlQuery::XQuery10);
2981 /* Check default value of copies default instance. */
2983 const QXmlQuery query1;
2984 const QXmlQuery query2(query1);
2986 QCOMPARE(query1.queryLanguage(), QXmlQuery::XQuery10);
2987 QCOMPARE(query2.queryLanguage(), QXmlQuery::XQuery10);
2991 void tst_QXmlQuery::queryLanguageSignature() const
2993 /* This getter should be const. */
2995 query.queryLanguage();
2998 void tst_QXmlQuery::enumQueryLanguage() const
3000 /* These enum values should be possible to OR for future plans. */
3001 QCOMPARE(int(QXmlQuery::XQuery10), 1);
3002 QCOMPARE(int(QXmlQuery::XSLT20), 2);
3003 QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintSelector), 1024);
3004 QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintField), 2048);
3005 QCOMPARE(int(QXmlQuery::XPath20), 4096);
3008 void tst_QXmlQuery::setInitialTemplateNameQXmlName() const
3010 QXmlQuery query(QXmlQuery::XSLT20);
3011 QXmlNamePool np(query.namePool());
3012 const QXmlName name(np, QLatin1String("main"));
3014 query.setInitialTemplateName(name);
3016 QCOMPARE(query.initialTemplateName(), name);
3018 query.setQuery(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/namedTemplate.xsl"))));
3019 QVERIFY(query.isValid());
3022 QVERIFY(result.open(QIODevice::ReadWrite));
3023 QXmlSerializer serializer(query, &result);
3024 query.evaluateTo(&serializer);
3026 QCOMPARE(result.data(), QByteArray("1 2 3 4 5"));
3028 // TODO invoke a template which has required params.
3031 void tst_QXmlQuery::setInitialTemplateNameQXmlNameSignature() const
3034 QXmlNamePool np(query.namePool());
3035 const QXmlName name(np, QLatin1String("foo"));
3037 /* The signature should take a const reference. */
3038 query.setInitialTemplateName(name);
3041 void tst_QXmlQuery::setInitialTemplateNameQString() const
3044 QXmlNamePool np(query.namePool());
3045 query.setInitialTemplateName(QLatin1String("foo"));
3047 QCOMPARE(query.initialTemplateName(), QXmlName(np, QLatin1String("foo")));
3050 void tst_QXmlQuery::setInitialTemplateNameQStringSignature() const
3052 const QString name(QLatin1String("name"));
3055 /* We should take a const reference. */
3056 query.setInitialTemplateName(name);
3059 void tst_QXmlQuery::initialTemplateName() const
3061 /* Check our default value. */
3063 QCOMPARE(query.initialTemplateName(), QXmlName());
3064 QVERIFY(query.initialTemplateName().isNull());
3067 void tst_QXmlQuery::initialTemplateNameSignature() const
3069 const QXmlQuery query;
3070 /* This should be a const member. */
3071 query.initialTemplateName();
3074 void tst_QXmlQuery::setNetworkAccessManager() const
3077 /* Ensure fn:doc() picks up the right QNetworkAccessManager. */
3079 NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
3080 QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/queries/simpleDocument.xml"))));
3083 query.setNetworkAccessManager(&networkOverrider);
3084 query.setQuery(QLatin1String("string(doc('tag:example.com:DOESNOTEXIST'))"));
3085 QVERIFY(query.isValid());
3088 QVERIFY(query.evaluateTo(&result));
3090 QCOMPARE(result, QStringList(QLatin1String("text text node")));
3093 /* Ensure setQuery() is using the right network manager. */
3095 NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
3096 QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/queries/concat.xq"))));
3099 query.setNetworkAccessManager(&networkOverrider);
3100 query.setQuery(QUrl("tag:example.com:DOESNOTEXIST"));
3101 QVERIFY(query.isValid());
3104 QVERIFY(query.evaluateTo(&result));
3106 QCOMPARE(result, QStringList(QLatin1String("abcdef")));
3109 void tst_QXmlQuery::networkAccessManagerSignature() const
3112 const QXmlQuery query;
3114 /* The function should be const. */
3115 query.networkAccessManager();
3118 void tst_QXmlQuery::networkAccessManagerDefaultValue() const
3120 const QXmlQuery query;
3122 QCOMPARE(query.networkAccessManager(), static_cast<QNetworkAccessManager *>(0));
3125 void tst_QXmlQuery::networkAccessManager() const
3127 /* Test that we return the network manager that was set. */
3129 QNetworkAccessManager manager;
3131 query.setNetworkAccessManager(&manager);
3132 QCOMPARE(query.networkAccessManager(), &manager);
3140 1. Load a document into QXmlQuery's document cache, by executing a query which does it.
3142 3. Change query, to one which uses the focus
3147 void tst_QXmlQuery::multipleDocsAndFocus() const
3151 /* We use string concatenation, since variable bindings might disturb what
3153 query.setQuery(QLatin1String("string(doc('") +
3154 inputFile(QLatin1String(XMLPATTERNSDIR "/queries/simpleDocument.xml")) +
3155 QLatin1String("'))"));
3156 query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3157 query.setQuery(QLatin1String("string(.)"));
3160 QVERIFY(query.evaluateTo(&result));
3175 void tst_QXmlQuery::multipleEvaluationsWithDifferentFocus() const
3180 query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3181 query.setQuery(QLatin1String("string(.)"));
3182 QVERIFY(query.evaluateTo(&result));
3184 query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3185 QVERIFY(query.evaluateTo(&result));
3188 void tst_QXmlQuery::bindVariableQXmlQuery() const
3190 QFETCH(QString, query1);
3191 QFETCH(QString, query2);
3192 QFETCH(QString, expectedOutput);
3193 QFETCH(bool, expectedSuccess);
3195 MessageSilencer silencer;
3196 QXmlQuery xmlQuery1;
3197 xmlQuery1.setMessageHandler(&silencer);
3198 xmlQuery1.setQuery(query1);
3200 QXmlQuery xmlQuery2(xmlQuery1);
3201 xmlQuery2.bindVariable("query1", xmlQuery1);
3202 xmlQuery2.setQuery(query2);
3205 const bool querySuccess = xmlQuery2.evaluateTo(&output);
3207 QCOMPARE(querySuccess, expectedSuccess);
3210 QCOMPARE(output, expectedOutput);
3213 void tst_QXmlQuery::bindVariableQXmlQuery_data() const
3215 QTest::addColumn<QString>("query1");
3216 QTest::addColumn<QString>("query2");
3217 QTest::addColumn<QString>("expectedOutput");
3218 QTest::addColumn<bool>("expectedSuccess");
3220 QTest::newRow("First query has one atomic value.")
3226 QTest::newRow("First query has two atomic values.")
3232 QTest::newRow("First query is a node.")
3238 /* This is a good test, because it triggers the exception in the
3239 * bindVariable() call, as supposed to when the actual evaluation is done.
3241 QTest::newRow("First query has a dynamic error.")
3244 << QString() /* We don't care. */
3248 void tst_QXmlQuery::bindVariableQStringQXmlQuerySignature() const
3251 query1.setQuery("'dummy'");
3254 const QString name(QLatin1String("name"));
3256 /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
3257 * QXmlQuery, and evaluation is what we do here. */
3258 query2.bindVariable(name, const_cast<const QXmlQuery &>(query1));
3261 void tst_QXmlQuery::bindVariableQXmlNameQXmlQuerySignature() const
3264 QXmlQuery query1(np);
3265 query1.setQuery("'dummy'");
3268 const QXmlName name(np, QLatin1String("name"));
3270 /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
3271 * QXmlQuery, and evaluation is what we do here. */
3272 query2.bindVariable(name, const_cast<const QXmlQuery &>(query1));
3276 Check that the QXmlName is handled correctly.
3278 void tst_QXmlQuery::bindVariableQXmlNameQXmlQuery() const
3282 query1.setQuery(QLatin1String("1"));
3284 QXmlQuery query2(np);
3285 query2.bindVariable(QXmlName(np, QLatin1String("theName")), query1);
3286 query2.setQuery("$theName");
3289 query2.evaluateTo(&result);
3291 QCOMPARE(result, QString::fromLatin1("1\n"));
3294 void tst_QXmlQuery::bindVariableQXmlQueryInvalidate() const
3297 query.bindVariable(QLatin1String("name"), QVariant(1));
3298 query.setQuery("$name");
3299 QVERIFY(query.isValid());
3302 query2.setQuery("'query2'");
3304 query.bindVariable(QLatin1String("name"), query);
3305 QVERIFY(!query.isValid());
3308 void tst_QXmlQuery::unknownSourceLocation() const
3311 b.setData("<a><b/><b/></a>");
3312 b.open(QIODevice::ReadOnly);
3314 MessageSilencer silencer;
3316 query.bindVariable(QLatin1String("inputDocument"), &b);
3317 query.setMessageHandler(&silencer);
3319 query.setQuery(QLatin1String("doc($inputDocument)/a/(let $v := b/string() return if ($v) then $v else ())"));
3322 query.evaluateTo(&output);
3325 void tst_QXmlQuery::identityConstraintSuccess() const
3327 QXmlQuery::QueryLanguage queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintSelector;
3329 /* We run this code for Selector and Field. */
3330 for(int i = 0; i < 3; ++i)
3332 QXmlNamePool namePool;
3333 QXmlResultItems result;
3337 QXmlQuery nodeSource(namePool);
3338 nodeSource.setQuery(QLatin1String("<e/>"));
3340 nodeSource.evaluateTo(&result);
3341 node = result.next();
3345 * 1. The focus is undefined, but it's still valid.
3346 * 2. We never evaluate. */
3348 QXmlQuery query(queryLanguage);
3349 query.setQuery(QLatin1String("a"));
3350 QVERIFY(query.isValid());
3354 * 1. The focus is undefined, but it's still valid.
3355 * 2. We afterwards set the focus. */
3357 QXmlQuery query(queryLanguage, namePool);
3358 query.setQuery(QLatin1String("a"));
3359 query.setFocus(node);
3360 QVERIFY(query.isValid());
3364 * 1. The focus is undefined, but it's still valid.
3365 * 2. We afterwards set the focus.
3366 * 3. We evaluate. */
3368 QXmlQuery query(queryLanguage, namePool);
3369 query.setQuery(QString(QLatin1Char('.')));
3370 query.setFocus(node);
3371 QVERIFY(query.isValid());
3374 QVERIFY(query.evaluateTo(&result));
3375 QCOMPARE(result, QString::fromLatin1("<e/>\n"));
3378 /* A slightly more complex Field. */
3380 QXmlQuery query(queryLanguage);
3381 query.setQuery(QLatin1String("* | .//xml:*/."));
3382 QVERIFY(query.isValid());
3385 /* @ is only allowed in Field. */
3386 if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField)
3388 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField);
3389 query.setQuery(QLatin1String("@abc"));
3390 QVERIFY(query.isValid());
3393 /* Field allows attribute:: and child:: .*/
3394 if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField)
3396 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField);
3397 query.setQuery(QLatin1String("attribute::name | child::name"));
3398 QVERIFY(query.isValid());
3401 /* Selector allows only child:: .*/
3403 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintSelector);
3404 query.setQuery(QLatin1String("child::name"));
3405 QVERIFY(query.isValid());
3409 queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintField;
3411 queryLanguage = QXmlQuery::XPath20;
3415 Q_DECLARE_METATYPE(QXmlQuery::QueryLanguage);
3418 We just do some basic tests for boot strapping and sanity checking. The actual regression
3419 testing is in the Schema suite.
3421 void tst_QXmlQuery::identityConstraintFailure() const
3423 QFETCH(QXmlQuery::QueryLanguage, queryLanguage);
3424 QFETCH(QString, inputQuery);
3426 QXmlQuery query(queryLanguage);
3427 MessageSilencer silencer;
3428 query.setMessageHandler(&silencer);
3430 query.setQuery(inputQuery);
3431 QVERIFY(!query.isValid());
3434 void tst_QXmlQuery::identityConstraintFailure_data() const
3436 QTest::addColumn<QXmlQuery::QueryLanguage>("queryLanguage");
3437 QTest::addColumn<QString>("inputQuery");
3439 QTest::newRow("We don't have element constructors in identity constraint pattern, "
3440 "it's an XQuery feature(Selector).")
3441 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3442 << QString::fromLatin1("<e/>");
3444 QTest::newRow("We don't have functions in identity constraint pattern, "
3445 "it's an XPath feature(Selector).")
3446 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3447 << QString::fromLatin1("current-time()");
3449 QTest::newRow("We don't have element constructors in identity constraint pattern, "
3450 "it's an XQuery feature(Field).")
3451 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3452 << QString::fromLatin1("<e/>");
3454 QTest::newRow("We don't have functions in identity constraint pattern, "
3455 "it's an XPath feature(Field).")
3456 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3457 << QString::fromLatin1("current-time()");
3459 QTest::newRow("@attributeName is disallowed for the selector.")
3460 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3461 << QString::fromLatin1("@abc");
3463 QTest::newRow("attribute:: is disallowed for the selector.")
3464 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3465 << QString::fromLatin1("attribute::name");
3467 QTest::newRow("ancestor::name is disallowed for the selector.")
3468 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3469 << QString::fromLatin1("ancestor::name");
3471 QTest::newRow("ancestor::name is disallowed for the field.")
3472 << QXmlQuery::XmlSchema11IdentityConstraintField
3473 << QString::fromLatin1("ancestor::name");
3476 QTEST_MAIN(tst_QXmlQuery)
3478 #include "tst_qxmlquery.moc"
3479 #else //QTEST_XMLPATTERNS