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 QVERIFY(push.isValid());
971 query.evaluateTo(&push);
973 const QString baselineName(inputFile(QLatin1String(SRCDIR "pushBaselines/") + inputQuery.left(inputQuery.length() - 2) + QString::fromLatin1("ref")));
974 QFile baseline(baselineName);
976 if(baseline.exists())
978 QVERIFY(baseline.open(QIODevice::ReadOnly | QIODevice::Text));
979 const QString stringedBaseline(QString::fromUtf8(baseline.readAll()));
980 QCOMPARE(produced, stringedBaseline);
984 QVERIFY(baseline.open(QIODevice::WriteOnly));
985 /* This is intentionally a warning, don't remove it. Update the baselines instead. */
986 qWarning() << "Generated baseline for:" << baselineName;
987 ++m_generatedBaselines;
989 baseline.write(produced.toUtf8());
993 void tst_QXmlQuery::evaluateToReceiver_data() const
995 QTest::addColumn<QString>("inputQuery");
997 const QStringList qs(queries());
999 for(int i = 0; i < qs.size(); ++i)
1001 /* This outputs a URI specific to the environment, so we can't use it for this
1002 * particular test. */
1003 if(qs.at(i) != QLatin1String("staticBaseURI.xq"))
1004 QTest::newRow(qs.at(i).toUtf8().constData()) << qs.at(i);
1008 void tst_QXmlQuery::evaluateToReceiverOnInvalidQuery() const
1010 /* Invoke on a default constructed object. */
1013 QBuffer buffer(&out);
1014 buffer.open(QIODevice::WriteOnly);
1017 QXmlSerializer serializer(query, &buffer);
1018 QVERIFY(!query.evaluateTo(&serializer));
1021 /* Invoke on an invalid query; compile time error. */
1024 QBuffer buffer(&out);
1025 buffer.open(QIODevice::WriteOnly);
1026 MessageSilencer silencer;
1029 query.setMessageHandler(&silencer);
1030 query.setQuery(QLatin1String("1 + "));
1031 QXmlSerializer serializer(query, &buffer);
1032 QVERIFY(!query.evaluateTo(&serializer));
1035 /* Invoke on an invalid query; runtime error. */
1038 QBuffer buffer(&out);
1039 buffer.open(QIODevice::WriteOnly);
1040 MessageSilencer silencer;
1043 query.setMessageHandler(&silencer);
1044 query.setQuery(QLatin1String("error()"));
1045 QXmlSerializer serializer(query, &buffer);
1046 QVERIFY(!query.evaluateTo(&serializer));
1050 void tst_QXmlQuery::evaluateToQStringTriggerError() const
1052 /* Invoke on a default constructed object. */
1056 QVERIFY(!query.evaluateTo(&out));
1059 /* Invoke on an invalid query; compile time error. */
1062 MessageSilencer silencer;
1063 query.setMessageHandler(&silencer);
1065 query.setQuery(QLatin1String("1 + "));
1068 QVERIFY(!query.evaluateTo(&out));
1071 /* Invoke on an invalid query; runtime error. */
1074 MessageSilencer silencer;
1075 query.setMessageHandler(&silencer);
1077 query.setQuery(QLatin1String("error()"));
1080 QVERIFY(!query.evaluateTo(&out));
1084 void tst_QXmlQuery::evaluateToQString() const
1086 QFETCH(QString, query);
1087 QFETCH(QString, expectedOutput);
1089 QXmlQuery queryInstance;
1090 queryInstance.setQuery(query);
1091 QVERIFY(queryInstance.isValid());
1094 QVERIFY(queryInstance.evaluateTo(&result));
1096 QCOMPARE(result, expectedOutput);
1099 void tst_QXmlQuery::evaluateToQString_data() const
1101 QTest::addColumn<QString>("query");
1102 QTest::addColumn<QString>("expectedOutput");
1104 QTest::newRow("Two atomics")
1105 << QString::fromLatin1("1, 'two'")
1106 << QString::fromLatin1("1 two\n");
1108 QTest::newRow("An element")
1109 << QString::fromLatin1("<e>{1}</e>")
1110 << QString::fromLatin1("<e>1</e>\n");
1113 void tst_QXmlQuery::evaluateToQStringSignature() const
1115 const QXmlQuery query;
1119 /* evaluateTo(QString *) should be a const function. */
1120 query.evaluateTo(&output);
1123 void tst_QXmlQuery::evaluateToQAbstractXmlReceiverTriggerWarnings() const
1127 /* We check the return value as well as warning message here. */
1128 QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed.");
1129 QCOMPARE(query.evaluateTo(static_cast<QAbstractXmlReceiver *>(0)),
1133 void tst_QXmlQuery::evaluateToQXmlResultItems() const
1135 /* Invoke on a default constructed object. */
1138 QXmlResultItems result;
1139 query.evaluateTo(&result);
1140 QVERIFY(result.next().isNull());
1144 void tst_QXmlQuery::evaluateToQXmlResultItemsTriggerWarnings() const
1146 QTest::ignoreMessage(QtWarningMsg, "A null pointer cannot be passed.");
1148 query.evaluateTo(static_cast<QXmlResultItems *>(0));
1151 void tst_QXmlQuery::evaluateToQXmlResultItemsErrorAtEnd() const
1154 MessageSilencer silencer;
1155 query.setMessageHandler(&silencer);
1156 query.setQuery(QLatin1String("1 to 100, fn:error()"));
1157 QVERIFY(query.isValid());
1160 query.evaluateTo(&it);
1162 while(!it.next().isNull())
1168 If baselines were generated, we flag it as a failure such that it gets
1169 attention, and that they are adjusted accordingly.
1171 void tst_QXmlQuery::checkGeneratedBaselines() const
1173 QCOMPARE(m_generatedBaselines, 0);
1175 /* If this check fails, the auto test setup is misconfigured, or files have
1176 * been added/removed without this number being updated. */
1177 QCOMPARE(m_pushTestsCount, int(ExpectedQueryCount));
1180 void tst_QXmlQuery::basicXQueryToQtTypeCheck() const
1182 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("allAtomics.xq"));
1183 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1186 query.setQuery(&queryFile);
1187 QVERIFY(query.isValid());
1190 query.evaluateTo(&it);
1192 QVariantList expectedValues;
1193 expectedValues.append(QString::fromLatin1("xs:untypedAtomic"));
1194 expectedValues.append(QDateTime(QDate(2002, 10, 10), QTime(23, 2, 11), Qt::UTC));
1195 expectedValues.append(QDate(2002, 10, 10));
1196 expectedValues.append(QVariant()); /* We currently doesn't support xs:time through the API. */
1198 expectedValues.append(QVariant()); /* xs:duration */
1199 expectedValues.append(QVariant()); /* xs:dayTimeDuration */
1200 expectedValues.append(QVariant()); /* xs:yearMonthDuration */
1202 if(sizeof(qreal) == sizeof(float)) {//ARM casts to Float not to double
1203 expectedValues.append(QVariant(float(3e3))); /* xs:float */
1204 expectedValues.append(QVariant(float(4e4))); /* xs:double */
1205 expectedValues.append(QVariant(float(2))); /* xs:decimal */
1207 expectedValues.append(QVariant(double(3e3))); /* xs:float */
1208 expectedValues.append(QVariant(double(4e4))); /* xs:double */
1209 expectedValues.append(QVariant(double(2))); /* xs:decimal */
1212 /* xs:integer and its sub-types. */
1213 expectedValues.append(QVariant(qlonglong(16)));
1214 expectedValues.append(QVariant(qlonglong(-6)));
1215 expectedValues.append(QVariant(qlonglong(-4)));
1216 expectedValues.append(QVariant(qlonglong(5)));
1217 expectedValues.append(QVariant(qlonglong(6)));
1218 expectedValues.append(QVariant(qlonglong(7)));
1219 expectedValues.append(QVariant(qlonglong(8)));
1220 expectedValues.append(QVariant(qlonglong(9)));
1221 expectedValues.append(QVariant(qulonglong(10)));
1222 expectedValues.append(QVariant(qlonglong(11)));
1223 expectedValues.append(QVariant(qlonglong(12)));
1224 expectedValues.append(QVariant(qlonglong(13)));
1225 expectedValues.append(QVariant(qlonglong(14)));
1227 expectedValues.append(QVariant()); /* xs:gYearMonth("1976-02"), */
1228 expectedValues.append(QVariant()); /* xs:gYear("2005-12:00"), */
1229 expectedValues.append(QVariant()); /* xs:gMonthDay("--12-25-14:00"), */
1230 expectedValues.append(QVariant()); /* xs:gDay("---25-14:00"), */
1231 expectedValues.append(QVariant()); /* xs:gMonth("--12-14:00"), */
1232 expectedValues.append(true); /* xs:boolean("true"), */
1233 expectedValues.append(QVariant(QByteArray::fromBase64(QByteArray("aaaa")))); /* xs:base64Binary("aaaa"), */
1234 expectedValues.append(QVariant(QByteArray::fromHex(QByteArray("FFFF")))); /* xs:hexBinary("FFFF"), */
1235 expectedValues.append(QVariant(QString::fromLatin1("http://example.com/"))); /* xs:anyURI("http://example.com/"), */
1236 QXmlNamePool np(query.namePool());
1237 expectedValues.append(QVariant(qVariantFromValue(QXmlName(np, QLatin1String("localName"),
1238 QLatin1String("http://example.com/2"),
1239 QLatin1String("prefix")))));
1241 expectedValues.append(QVariant(QString::fromLatin1("An xs:string")));
1242 expectedValues.append(QVariant(QString::fromLatin1("normalizedString")));
1243 expectedValues.append(QVariant(QString::fromLatin1("token")));
1244 expectedValues.append(QVariant(QString::fromLatin1("language")));
1245 expectedValues.append(QVariant(QString::fromLatin1("NMTOKEN")));
1246 expectedValues.append(QVariant(QString::fromLatin1("Name")));
1247 expectedValues.append(QVariant(QString::fromLatin1("NCName")));
1248 expectedValues.append(QVariant(QString::fromLatin1("ID")));
1249 expectedValues.append(QVariant(QString::fromLatin1("IDREF")));
1250 expectedValues.append(QVariant(QString::fromLatin1("ENTITY")));
1253 QXmlItem item(it.next());
1255 while(!item.isNull())
1257 QVERIFY(item.isAtomicValue());
1258 const QVariant produced(item.toAtomicValue());
1260 const QVariant &expected = expectedValues.at(i);
1262 /* For the cases where we can't represent a value in the XDM with Qt,
1263 * we return an invalid QVariant. */
1264 QCOMPARE(expected.isValid(), produced.isValid());
1266 QCOMPARE(produced.type(), expected.type());
1268 if(expected.isValid())
1270 /* This is only needed for xs:decimal though, for some reason. Probably
1271 * just artifacts created somewhere. */
1272 if(produced.type() == QVariant::Double)
1273 QVERIFY(qFuzzyCompare(produced.toDouble(), expected.toDouble()));
1274 else if(qVariantCanConvert<QXmlName>(produced))
1276 /* QVariant::operator==() does identity comparison, it doesn't delegate to operator==() of
1277 * the contained type, unless it's hardcoded into QVariant. */
1278 const QXmlName n1 = qVariantValue<QXmlName>(produced);
1279 const QXmlName n2 = qVariantValue<QXmlName>(expected);
1283 QCOMPARE(produced, expected);
1290 QCOMPARE(i, expectedValues.count());
1294 Send values from Qt into XQuery.
1296 void tst_QXmlQuery::basicQtToXQueryTypeCheck() const
1298 QFile queryFile(QLatin1String(queriesDirectory) + QLatin1String("allAtomicsExternally.xq"));
1299 QVERIFY(queryFile.exists());
1300 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1302 QCOMPARE(QVariant(QDate(1999, 9, 10)).type(), QVariant::Date);
1306 QXmlNamePool np(query.namePool());
1308 const QXmlName name(np, QLatin1String("localname"),
1309 QLatin1String("http://example.com"),
1310 QLatin1String("prefix"));
1312 query.bindVariable(QLatin1String("fromQUrl"), QXmlItem(QUrl(QString::fromLatin1("http://example.com/"))));
1313 query.bindVariable(QLatin1String("fromQByteArray"), QXmlItem(QByteArray("AAAA")));
1314 query.bindVariable(QLatin1String("fromBool"), QXmlItem(bool(true)));
1315 query.bindVariable(QLatin1String("fromQDate"), QXmlItem(QDate(2000, 10, 11)));
1316 // TODO Do with different QDateTime time specs
1317 query.bindVariable(QLatin1String("fromQDateTime"), QXmlItem(QDateTime(QDate(2001, 9, 10), QTime(1, 2, 3))));
1318 query.bindVariable(QLatin1String("fromDouble"), QXmlItem(double(3)));
1319 query.bindVariable(QLatin1String("fromFloat"), QXmlItem(float(4)));
1320 query.bindVariable(QLatin1String("integer"), QXmlItem(5));
1321 query.bindVariable(QLatin1String("fromQString"), QXmlItem(QString::fromLatin1("A QString")));
1322 query.bindVariable(QLatin1String("fromQChar"), QXmlItem(QChar::fromLatin1('C')));
1324 query.bindVariable(QLatin1String("fromIntLiteral"), QXmlItem(QVariant(654)));
1327 QVariant ui(uint(5));
1328 QCOMPARE(ui.type(), QVariant::UInt);
1329 query.bindVariable(QLatin1String("fromUInt"), ui);
1333 QVariant ulnglng(qulonglong(6));
1334 QCOMPARE(ulnglng.type(), QVariant::ULongLong);
1335 query.bindVariable(QLatin1String("fromULongLong"), ulnglng);
1339 QVariant qlnglng(qlonglong(7));
1340 QCOMPARE(qlnglng.type(), QVariant::LongLong);
1341 query.bindVariable(QLatin1String("fromLongLong"), qlnglng);
1344 query.setQuery(&queryFile);
1346 // TODO do queries which declares external variables with types. Tons of combos here.
1347 // TODO ensure that binding with QXmlItem() doesn't make a binding available.
1348 // TODO test rebinding a variable.
1350 QVERIFY(query.isValid());
1353 query.evaluateTo(&it);
1354 QXmlItem item(it.next());
1355 QVERIFY(!item.isNull());
1356 QVERIFY(item.isAtomicValue());
1358 if(sizeof(qreal) == sizeof(float)) //ARM casts to Float not to double
1359 QCOMPARE(item.toAtomicValue().toString(),
1360 QLatin1String("4 true 3 654 7 41414141 C 2000-10-11Z 2001-09-10T01:02:03 "
1361 "A QString http://example.com/ 5 6 true false false true true true true true true true "
1364 QCOMPARE(item.toAtomicValue().toString(),
1365 QLatin1String("4 true 3 654 7 41414141 C 2000-10-11Z 2001-09-10T01:02:03 "
1366 "A QString http://example.com/ 5 6 true true true true true true true true true true "
1371 void tst_QXmlQuery::bindNode() const
1374 TestSimpleNodeModel nodeModel(query.namePool());
1376 query.bindVariable(QLatin1String("node"), nodeModel.root());
1379 QVERIFY(buff.open(QIODevice::WriteOnly));
1381 query.setQuery(QLatin1String("declare variable $node external; $node"));
1382 QXmlSerializer serializer(query, &buff);
1384 QVERIFY(query.evaluateTo(&serializer));
1385 QCOMPARE(out, QByteArray("<nodeName/>"));
1389 Pass in a relative URI, and make sure it is resolved against the current application directory.
1391 void tst_QXmlQuery::relativeBaseURI() const
1394 query.setQuery(QLatin1String("fn:static-base-uri()"), QUrl(QLatin1String("a/relative/uri.weirdExtension")));
1395 QVERIFY(query.isValid());
1398 QBuffer buffer(&result);
1399 QVERIFY(buffer.open(QIODevice::ReadWrite));
1401 QXmlSerializer serializer(query, &buffer);
1402 QVERIFY(query.evaluateTo(&serializer));
1404 const QUrl loaded(QUrl::fromEncoded(result));
1405 QUrl appPath(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()));
1407 QVERIFY(loaded.isValid());
1408 QVERIFY(appPath.isValid());
1409 QVERIFY(!loaded.isRelative());
1410 QVERIFY(!appPath.isRelative());
1412 QFileInfo dir(appPath.toLocalFile());
1413 dir.setFile(QString());
1415 /* We can't use QUrl::isParentOf() because it doesn't do what we want it to */
1416 if(!loaded.toLocalFile().startsWith(dir.absoluteFilePath()))
1417 QTextStream(stderr) << "dir.absoluteFilePath():" << dir.absoluteFilePath() << "loaded.toLocalFile():" << loaded.toLocalFile();
1419 checkBaseURI(loaded, dir.absoluteFilePath());
1422 void tst_QXmlQuery::emptyBaseURI() const
1425 query.setQuery(QLatin1String("fn:static-base-uri()"), QUrl());
1426 QVERIFY(query.isValid());
1429 QBuffer buffer(&result);
1430 QVERIFY(buffer.open(QIODevice::ReadWrite));
1432 QXmlSerializer serializer(query, &buffer);
1433 QVERIFY(query.evaluateTo(&serializer));
1435 const QUrl loaded(QUrl::fromEncoded(result));
1436 QUrl appPath(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()));
1438 QVERIFY(loaded.isValid());
1439 QVERIFY(appPath.isValid());
1440 QVERIFY(!loaded.isRelative());
1441 QVERIFY(!appPath.isRelative());
1443 QFileInfo dir(appPath.toLocalFile());
1444 dir.setFile(QString());
1446 QCOMPARE(loaded, appPath);
1450 Ensure that QDate comes out as QDateTime.
1452 void tst_QXmlQuery::roundTripDateWithinQXmlItem() const
1454 const QDate date(1999, 9, 10);
1455 QVERIFY(date.isValid());
1457 const QVariant variant(date);
1458 QVERIFY(variant.isValid());
1459 QCOMPARE(variant.type(), QVariant::Date);
1461 const QXmlItem item(variant);
1462 QVERIFY(!item.isNull());
1463 QVERIFY(item.isAtomicValue());
1465 const QVariant out(item.toAtomicValue());
1466 QVERIFY(out.isValid());
1467 QCOMPARE(out.type(), QVariant::Date);
1468 QCOMPARE(out.toDate(), date);
1472 Check whether a query is valid, which uses an unbound variable.
1474 void tst_QXmlQuery::bindingMissing() const
1477 MessageSilencer messageHandler;
1478 query.setMessageHandler(&messageHandler);
1480 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1481 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1482 query.setQuery(&queryFile);
1484 QVERIFY(!query.isValid());
1487 void tst_QXmlQuery::bindDefaultConstructedItem() const
1489 QFETCH(QXmlItem, item);
1492 MessageSilencer messageHandler;
1493 query.setMessageHandler(&messageHandler);
1495 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1496 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1497 query.setQuery(&queryFile);
1498 query.bindVariable(QLatin1String("externalVariable"), item);
1500 QVERIFY(!query.isValid());
1503 void tst_QXmlQuery::bindDefaultConstructedItem_data() const
1505 QTest::addColumn<QXmlItem>("item");
1507 QTest::newRow("QXmlItem()") << QXmlItem();
1508 QTest::newRow("QXmlItem(QVariant())") << QXmlItem(QVariant());
1509 QTest::newRow("QXmlItem(QXmlNodeModelIndex())") << QXmlItem(QXmlNodeModelIndex());
1513 Remove a binding by binding QXmlItem() with the same name.
1515 void tst_QXmlQuery::eraseQXmlItemBinding() const
1518 MessageSilencer messageHandler;
1519 query.setMessageHandler(&messageHandler);
1521 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1522 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1523 query.bindVariable(QLatin1String("externalVariable"), QXmlItem(3));
1524 query.setQuery(&queryFile);
1525 QVERIFY(query.isValid());
1528 QBuffer buffer(&result);
1529 QVERIFY(buffer.open(QIODevice::ReadWrite));
1531 QXmlSerializer serializer(query, &buffer);
1532 QVERIFY(query.evaluateTo(&serializer));
1534 QCOMPARE(result, QByteArray("3 6<e>3</e>false"));
1536 query.bindVariable(QLatin1String("externalVariable"), QXmlItem());
1537 QVERIFY(!query.isValid());
1541 Erase a variable binding
1543 void tst_QXmlQuery::eraseDeviceBinding() const
1545 /* Erase an existing QIODevice binding with another QIODevice binding. */
1549 QByteArray doc("<e/>");
1550 QBuffer buffer(&doc);
1551 QVERIFY(buffer.open(QIODevice::ReadOnly));
1553 query.bindVariable(QLatin1String("in"), &buffer);
1554 query.setQuery(QLatin1String("$in"));
1555 QVERIFY(query.isValid());
1557 query.bindVariable(QLatin1String("in"), 0);
1558 QVERIFY(!query.isValid());
1561 /* Erase an existing QXmlItem binding with another QIODevice binding. */
1565 query.bindVariable(QLatin1String("in"), QXmlItem(5));
1566 query.setQuery(QLatin1String("$in"));
1567 QVERIFY(query.isValid());
1569 query.bindVariable(QLatin1String("in"), 0);
1570 QVERIFY(!query.isValid());
1575 Bind a variable, evaluate, bind with a different value but same type, and evaluate again.
1577 void tst_QXmlQuery::rebindVariableSameType() const
1580 MessageSilencer messageHandler;
1581 query.setMessageHandler(&messageHandler);
1583 query.bindVariable(QLatin1String("externalVariable"), QXmlItem(3));
1586 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1587 QVERIFY(queryFile.open(QIODevice::ReadOnly));
1588 query.setQuery(&queryFile);
1591 QVERIFY(query.isValid());
1595 QBuffer buffer(&result);
1596 QVERIFY(buffer.open(QIODevice::ReadWrite));
1598 QXmlSerializer serializer(query, &buffer);
1599 QVERIFY(query.evaluateTo(&serializer));
1601 QCOMPARE(result, QByteArray("3 6<e>3</e>false"));
1605 query.bindVariable(QLatin1String("externalVariable"), QXmlItem(5));
1607 QBuffer buffer(&result);
1608 QVERIFY(buffer.open(QIODevice::ReadWrite));
1610 QXmlSerializer serializer(query, &buffer);
1611 QVERIFY(query.evaluateTo(&serializer));
1613 QCOMPARE(result, QByteArray("5 8<e>5</e>false"));
1618 void tst_QXmlQuery::rebindVariableDifferentType() const
1620 /* Rebind QXmlItem variable with QXmlItem variable. */
1623 query.bindVariable(QLatin1String("in"), QXmlItem(3));
1624 query.setQuery(QLatin1String("$in"));
1625 QVERIFY(query.isValid());
1627 query.bindVariable(QLatin1String("in"), QXmlItem("A string"));
1628 QVERIFY(!query.isValid());
1631 /* Rebind QIODevice variable with QXmlItem variable. */
1635 buffer.setData(QByteArray("<e/>"));
1636 QVERIFY(buffer.open(QIODevice::ReadOnly));
1638 query.bindVariable(QLatin1String("in"), &buffer);
1639 query.setQuery(QLatin1String("$in"));
1640 QVERIFY(query.isValid());
1642 query.bindVariable(QLatin1String("in"), QXmlItem("A string"));
1643 QVERIFY(!query.isValid());
1646 /* Rebind QXmlItem variable with QIODevice variable. The type of the
1647 * variable changes, so a recompile is necessary. */
1651 query.bindVariable(QLatin1String("in"), QXmlItem(QLatin1String("A string")));
1652 query.setQuery(QLatin1String("$in"));
1653 QVERIFY(query.isValid());
1656 buffer.setData(QByteArray("<e/>"));
1657 QVERIFY(buffer.open(QIODevice::ReadOnly));
1658 query.bindVariable(QLatin1String("in"), &buffer);
1659 QVERIFY(!query.isValid());
1663 void tst_QXmlQuery::rebindVariableWithNullItem() const
1667 query.bindVariable(QLatin1String("name"), QXmlItem(5));
1668 query.bindVariable(QLatin1String("name"), QXmlItem());
1671 void tst_QXmlQuery::constCorrectness() const
1673 QXmlResultItems result;
1675 tmp.setQuery(QLatin1String("1")); /* Just so we have a valid query. */
1676 const QXmlQuery query(tmp);
1678 /* These functions should be const. */
1680 query.evaluateTo(&result);
1682 query.uriResolver();
1683 query.messageHandler();
1686 QString dummyString;
1687 QTextStream dummyStream(&dummyString);
1688 PushBaseliner dummy(dummyStream, query.namePool());
1689 QVERIFY(dummy.isValid());
1690 query.evaluateTo(&dummy);
1694 void tst_QXmlQuery::objectSize() const
1696 /* We have a d pointer. */
1697 QCOMPARE(sizeof(QXmlQuery), sizeof(void *));
1700 void tst_QXmlQuery::setUriResolver() const
1702 /* Set a null resolver, and make sure it can take a const pointer. */
1705 query.setUriResolver(static_cast<const QAbstractUriResolver *>(0));
1706 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0));
1710 TestURIResolver resolver;
1712 query.setUriResolver(&resolver);
1713 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(&resolver));
1717 void tst_QXmlQuery::uriResolver() const
1719 /* Check default value. */
1722 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0));
1726 void tst_QXmlQuery::messageXML() const
1730 MessageValidator messageValidator;
1731 query.setMessageHandler(&messageValidator);
1733 query.setQuery(QLatin1String("1basicSyntaxError"));
1735 const QRegExp removeFilename(QLatin1String("Location: file:.*\\#"));
1736 QVERIFY(removeFilename.isValid());
1738 QVERIFY(messageValidator.success());
1739 QCOMPARE(messageValidator.received().remove(removeFilename),
1740 QString::fromLatin1("Type:3\n"
1741 "Description: <html xmlns='http://www.w3.org/1999/xhtml/'><body><p>syntax error, unexpected unknown keyword</p></body></html>\n"
1742 "Identifier: http://www.w3.org/2005/xqt-errors#XPST0003\n"
1747 1. Allocate QXmlResultItems
1748 2. Allocate QXmlQuery
1749 3. evaluate to the QXmlResultItems instance
1750 4. Dellocate the QXmlQuery instance
1751 5. Ensure QXmlResultItems works
1753 void tst_QXmlQuery::resultItemsDeallocatedQuery() const
1755 QXmlResultItems result;
1759 query.setQuery(QLatin1String("1, 2, xs:integer(<e>3</e>)"));
1760 query.evaluateTo(&result);
1763 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
1764 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
1765 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
1766 QVERIFY(result.next().isNull());
1767 QVERIFY(!result.hasError());
1771 1. Bind variable with bindVariable()
1772 2. setQuery that has 'declare variable' with same name.
1773 3. Ensure the value inside the query is used. We don't guarantee this behavior
1774 but that's what we lock.
1776 void tst_QXmlQuery::shadowedVariables() const
1779 query.bindVariable("varName", QXmlItem(3));
1780 query.setQuery(QLatin1String("declare variable $varName := 5; $varName"));
1782 QXmlResultItems result;
1783 query.evaluateTo(&result);
1785 QCOMPARE(result.next().toAtomicValue(), QVariant(5));
1788 void tst_QXmlQuery::setFocusQXmlItem() const
1790 /* Make sure we can take a const reference. */
1793 const QXmlItem item;
1794 query.setFocus(item);
1797 // TODO evaluate with atomic value, check type
1798 // TODO evaluate with node, check type
1799 // TODO ensure that setFocus() triggers query recompilation, as appropriate.
1800 // TODO let the focus be undefined, call isvalid, call evaluate anyway
1801 // TODO let the focus be undefined, call evaluate directly
1804 void tst_QXmlQuery::setFocusQUrl() const
1806 /* Load a focus which isn't well-formed. */
1809 MessageSilencer silencer;
1811 query.setMessageHandler(&silencer);
1813 QVERIFY(!query.setFocus(QUrl(QLatin1String("data/notWellformed.xml"))));
1816 /* Ensure the same URI resolver is used. */
1818 QXmlQuery query(QXmlQuery::XSLT20);
1820 const TestURIResolver resolver(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
1821 query.setUriResolver(&resolver);
1823 QVERIFY(query.setFocus(QUrl(QLatin1String("arbitraryURI"))));
1824 query.setQuery(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/copyWholeDocument.xsl"))));
1825 QVERIFY(query.isValid());
1828 QVERIFY(result.open(QIODevice::ReadWrite));
1829 QXmlSerializer serializer(query, &result);
1830 query.evaluateTo(&serializer);
1832 QCOMPARE(result.data(), QByteArray("<doc/>"));
1835 // TODO ensure that the focus type doesn't change from XSLT20 on the main instance.
1839 This code poses a challenge wrt. to internal caching.
1841 void tst_QXmlQuery::setFocusQIODevice() const
1847 focus.setData(QByteArray("<e>abc</e>"));
1848 QVERIFY(focus.open(QIODevice::ReadOnly));
1849 query.setFocus(&focus);
1850 query.setQuery(QLatin1String("string()"));
1851 QVERIFY(query.isValid());
1854 query.evaluateTo(&output);
1856 QCOMPARE(output, QString::fromLatin1("abc\n"));
1859 /* Set a new focus, make sure it changes & works. */
1862 focus2.setData(QByteArray("<e>abc2</e>"));
1863 QVERIFY(focus2.open(QIODevice::ReadOnly));
1864 query.setFocus(&focus2);
1865 QVERIFY(query.isValid());
1868 query.evaluateTo(&output);
1870 QCOMPARE(output, QString::fromLatin1("abc2\n"));
1875 Since we internally use variable bindings for implementing the focus, we need
1876 to make sure we don't clash in this area.
1878 void tst_QXmlQuery::setFocusQIODeviceAvoidVariableClash() const
1881 buffer.setData("<e>focus</e>");
1882 QVERIFY(buffer.open(QIODevice::ReadOnly));
1884 /* First we bind the variable name, then the focus. */
1887 query.bindVariable(QString(QLatin1Char('u')), QVariant(1));
1888 query.setFocus(&buffer);
1889 query.setQuery(QLatin1String("string()"));
1892 query.evaluateTo(&out);
1894 QCOMPARE(out, QString::fromLatin1("focus\n"));
1897 /* First we bind the focus, then the variable name. */
1900 QVERIFY(buffer.open(QIODevice::ReadOnly));
1901 query.setFocus(&buffer);
1902 query.bindVariable(QString(QLatin1Char('u')), QVariant(1));
1903 query.setQuery(QLatin1String("string()"));
1906 query.evaluateTo(&out);
1908 QCOMPARE(out, QString::fromLatin1("focus\n"));
1912 void tst_QXmlQuery::setFocusQIODeviceFailure() const
1914 /* A not well-formed input document. */
1918 MessageSilencer silencer;
1919 query.setMessageHandler(&silencer);
1922 input.setData("<e");
1923 QVERIFY(input.open(QIODevice::ReadOnly));
1925 QCOMPARE(query.setFocus(&input), false);
1929 void tst_QXmlQuery::setFocusQString() const
1933 /* Basic use of focus. */
1935 QVERIFY(query.setFocus(QLatin1String("<e>textNode</e>")));
1936 query.setQuery(QLatin1String("string()"));
1937 QVERIFY(query.isValid());
1939 query.evaluateTo(&out);
1940 QCOMPARE(out, QString::fromLatin1("textNode\n"));
1943 /* Set to a new focus, make sure it changes and works. */
1945 QVERIFY(query.setFocus(QLatin1String("<e>newFocus</e>")));
1947 query.evaluateTo(&out);
1948 QCOMPARE(out, QString::fromLatin1("newFocus\n"));
1952 void tst_QXmlQuery::setFocusQStringFailure() const
1955 MessageSilencer silencer;
1957 query.setMessageHandler(&silencer);
1958 QVERIFY(!query.setFocus(QLatin1String("<notWellformed")));
1960 /* Let's try the slight special case of a null string. */
1961 QVERIFY(!query.setFocus(QString()));
1964 void tst_QXmlQuery::setFocusQStringSignature() const
1967 MessageSilencer silencer;
1968 query.setMessageHandler(&silencer);
1970 const QString argument;
1971 /* We should take a const ref. */
1972 query.setFocus(argument);
1974 /* We should return a bool. */
1975 static_cast<bool>(query.setFocus(QString()));
1978 void tst_QXmlQuery::setFocusQIODeviceTriggerWarnings() const
1980 /* A null pointer. */
1984 QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed.");
1985 QCOMPARE(query.setFocus(static_cast<QIODevice *>(0)), false);
1988 /* A non opened-device. */
1992 QBuffer notReadable;
1993 QVERIFY(!notReadable.isReadable());
1995 QTest::ignoreMessage(QtWarningMsg, "The device must be readable.");
1996 QCOMPARE(query.setFocus(¬Readable), false);
2000 void tst_QXmlQuery::fnDocNetworkAccessSuccess() const
2002 #if defined(Q_OS_WINCE) && !defined(_X86_)
2003 QStringList testsToSkip;
2004 testsToSkip << "http scheme" << "ftp scheme";
2005 if (testsToSkip.contains(QTest::currentDataTag()))
2006 QSKIP("Network tests are currently unsupported on Windows CE.", SkipSingle);
2009 QFETCH(QUrl, uriToOpen);
2010 QFETCH(QByteArray, expectedOutput);
2012 if(!uriToOpen.isValid())
2013 qDebug() << "uriToOpen:" << uriToOpen;
2015 QVERIFY(uriToOpen.isValid());
2018 query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen));
2019 query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)"));
2020 QVERIFY(query.isValid());
2023 QBuffer buffer(&result);
2024 QVERIFY(buffer.open(QIODevice::WriteOnly));
2026 QXmlSerializer serializer(query, &buffer);
2027 QVERIFY(query.evaluateTo(&serializer));
2029 QCOMPARE(result, expectedOutput);
2032 void tst_QXmlQuery::fnDocNetworkAccessSuccess_data() const
2034 QTest::addColumn<QUrl>("uriToOpen");
2035 QTest::addColumn<QByteArray>("expectedOutput");
2037 QTest::newRow("file scheme")
2038 << inputFileAsURI(QLatin1String(SRCDIR "input.xml"))
2039 << QByteArray("<!-- This is just a file for testing. --><input/>");
2041 QTest::newRow("data scheme with ASCII")
2042 /* QUrl::toPercentEncoding(QLatin1String("<e/>")) yields "%3Ce%2F%3E". */
2043 << QUrl::fromEncoded("data:application/xml,%3Ce%2F%3E")
2044 << QByteArray("<e/>");
2046 QTest::newRow("data scheme with ASCII no MIME type")
2047 << QUrl::fromEncoded("data:,%3Ce%2F%3E")
2048 << QByteArray("<e/>");
2050 QTest::newRow("data scheme with base 64")
2051 << QUrl::fromEncoded("data:application/xml;base64,PGUvPg==")
2052 << QByteArray("<e/>");
2054 QTest::newRow("qrc scheme")
2055 << QUrl::fromEncoded("qrc:/QXmlQueryTestData/data/oneElement.xml")
2056 << QByteArray("<oneElement/>");
2061 QTest::newRow("http scheme")
2062 << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/wellFormed.xml"))
2063 << QByteArray("<!-- a comment --><e from=\"http\">Some Text</e>");
2065 QTest::newRow("ftp scheme")
2066 << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/wellFormed.xml"))
2067 << QByteArray("<!-- a comment --><e from=\"ftp\">Some Text</e>");
2071 void tst_QXmlQuery::fnDocNetworkAccessFailure() const
2073 QFETCH(QUrl, uriToOpen);
2075 QVERIFY(uriToOpen.isValid());
2078 MessageSilencer silencer;
2079 query.setMessageHandler(&silencer);
2080 query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen));
2081 query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)"));
2082 QVERIFY(query.isValid());
2084 QXmlResultItems result;
2085 query.evaluateTo(&result);
2087 while(!result.next().isNull())
2089 /* Just loop until the end. */
2092 // TODO do something that triggers a /timeout/.
2093 QVERIFY(result.hasError());
2096 void tst_QXmlQuery::fnDocNetworkAccessFailure_data() const
2098 QTest::addColumn<QUrl>("uriToOpen");
2100 QTest::newRow("data scheme, not-well-formed")
2101 << QUrl(QLatin1String("data:application/xml;base64,PGUvg==="));
2103 QTest::newRow("file scheme, non-existant file")
2104 << QUrl(QLatin1String("file:///example.com/does/notExist.xml"));
2106 QTest::newRow("http scheme, file not found")
2107 << QUrl(QLatin1String("http://www.example.com/does/not/exist.xml"));
2109 QTest::newRow("http scheme, nonexistent host")
2110 << QUrl(QLatin1String("http://this.host.does.not.exist.I.SWear"));
2112 QTest::newRow("qrc scheme, not well-formed")
2113 << QUrl(QLatin1String("qrc:/QXmlQueryTestData/notWellformed.xml"));
2115 QTest::newRow("'qrc:/', non-existing file")
2116 << QUrl(QLatin1String(":/QXmlQueryTestData/data/thisFileDoesNotExist.xml"));
2118 QTest::newRow("':/', this scheme is not supported")
2119 << QUrl(QLatin1String(":/QXmlQueryTestData/data/notWellformed.xml"));
2124 QTest::newRow("http scheme, not well-formed")
2125 << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/notWellformed.xml"));
2127 QTest::newRow("https scheme, not well-formed")
2128 << QUrl(QString("https://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/notWellformedViaHttps.xml"));
2130 QTest::newRow("https scheme, nonexistent host")
2131 << QUrl(QLatin1String("https://this.host.does.not.exist.I.SWear"));
2133 QTest::newRow("ftp scheme, nonexistent host")
2134 << QUrl(QLatin1String("ftp://this.host.does.not.exist.I.SWear"));
2136 QTest::newRow("ftp scheme, not well-formed")
2137 << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/notWellFormed.xml"));
2141 Create a network timeout from a QIODevice binding such
2142 that we ensure we don't hang infinitely.
2144 void tst_QXmlQuery::fnDocOnQIODeviceTimeout() const
2150 server.listen(QHostAddress::LocalHost, 1088);
2153 client.connectToHost("localhost", 1088);
2154 QVERIFY(client.isReadable());
2158 MessageSilencer silencer;
2159 query.setMessageHandler(&silencer);
2161 query.bindVariable(QLatin1String("inDevice"), &client);
2162 query.setQuery(QLatin1String("declare variable $inDevice external;\ndoc($inDevice)"));
2163 QVERIFY(query.isValid());
2165 QXmlResultItems result;
2166 query.evaluateTo(&result);
2167 QXmlItem next(result.next());
2169 while(!next.isNull())
2171 next = result.next();
2174 QVERIFY(result.hasError());
2178 When changing query, the static context must change too, such that
2179 the source locations are updated.
2181 void tst_QXmlQuery::recompilationWithEvaluateToResultFailing() const
2184 MessageSilencer silencer;
2185 query.setMessageHandler(&silencer);
2187 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2188 QVERIFY(query.isValid()); /* Trigger query compilation. */
2190 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2191 QVERIFY(query.isValid()); /* Trigger second compilation. */
2193 QXmlResultItems items;
2194 query.evaluateTo(&items);
2196 QVERIFY(items.hasError());
2199 void tst_QXmlQuery::secondEvaluationWithEvaluateToResultFailing() const
2202 MessageSilencer silencer;
2203 query.setMessageHandler(&silencer);
2205 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2206 QVERIFY(query.isValid()); /* Trigger query compilation. */
2208 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2209 /* We don't call isValid(). */
2210 QXmlResultItems items;
2211 query.evaluateTo(&items);
2213 QVERIFY(items.hasError());
2217 Compilation is triggered in the evaluation function due to no call to QXmlQuery::isValid().
2219 void tst_QXmlQuery::recompilationWithEvaluateToReceiver() const
2222 MessageSilencer silencer;
2223 query.setMessageHandler(&silencer);
2225 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2226 QVERIFY(query.isValid()); /* Trigger query compilation. */
2228 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2229 /* We don't call isValid(). */
2232 QBuffer buffer(&dummy);
2233 buffer.open(QIODevice::WriteOnly);
2235 QXmlSerializer serializer(query, &buffer);
2237 QVERIFY(!query.evaluateTo(&serializer));
2240 void tst_QXmlQuery::evaluateToQStringListOnInvalidQuery() const
2242 MessageSilencer silencer;
2244 /* Invoke on a default constructed object. */
2248 QVERIFY(!query.evaluateTo(&out));
2251 /* Invoke on a syntactically invalid query. */
2255 MessageSilencer silencer;
2257 query.setMessageHandler(&silencer);
2258 query.setQuery(QLatin1String("1 + "));
2260 QVERIFY(!query.evaluateTo(&out));
2263 /* Invoke on a query with the wrong type, one atomic. */
2268 query.setQuery(QLatin1String("1"));
2269 query.setMessageHandler(&silencer);
2270 QVERIFY(!query.evaluateTo(&out));
2273 /* Invoke on a query with the wrong type, one element. */
2278 query.setQuery(QLatin1String("<e/>"));
2279 QVERIFY(!query.evaluateTo(&out));
2282 /* Invoke on a query with the wrong type, mixed nodes & atomics. */
2287 query.setQuery(QLatin1String("<e/>, 1, 'a string'"));
2288 query.setMessageHandler(&silencer);
2289 QVERIFY(!query.evaluateTo(&out));
2292 /* Evaluate the empty sequence. */
2297 query.setQuery(QLatin1String("()"));
2298 QVERIFY(!query.evaluateTo(&out));
2299 QVERIFY(out.isEmpty());
2303 void tst_QXmlQuery::evaluateToQStringList() const
2305 QFETCH(QString, queryString);
2306 QFETCH(QStringList, expectedOutput);
2309 query.setQuery(queryString);
2311 QVERIFY(query.isValid());
2313 QVERIFY(query.evaluateTo(&out));
2315 QCOMPARE(out, expectedOutput);
2318 void tst_QXmlQuery::evaluateToQStringListTriggerWarnings() const
2322 QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed.");
2323 QCOMPARE(query.evaluateTo(static_cast<QStringList *>(0)),
2327 void tst_QXmlQuery::evaluateToQStringList_data() const
2329 QTest::addColumn<QString>("queryString");
2330 QTest::addColumn<QStringList>("expectedOutput");
2332 QTest::newRow("One atomic")
2333 << QString::fromLatin1("(1 + 1) cast as xs:string")
2334 << QStringList(QString::fromLatin1("2"));
2337 QStringList expected;
2338 expected << QLatin1String("2");
2339 expected << QLatin1String("a string");
2341 QTest::newRow("Two atomics")
2342 << QString::fromLatin1("(1 + 1) cast as xs:string, 'a string'")
2346 QTest::newRow("A query which evaluates to sub-types of xs:string.")
2347 << QString::fromLatin1("xs:NCName('NCName'), xs:normalizedString(' a b c ')")
2348 << (QStringList() << QString::fromLatin1("NCName")
2349 << QString::fromLatin1(" a b c "));
2351 QTest::newRow("A query which evaluates to two elements.")
2352 << QString::fromLatin1("string(<e>theString1</e>), string(<e>theString2</e>)")
2353 << (QStringList() << QString::fromLatin1("theString1")
2354 << QString::fromLatin1("theString2"));
2358 Ensure that we don't automatically convert non-xs:string values.
2360 void tst_QXmlQuery::evaluateToQStringListNoConversion() const
2363 query.setQuery(QString::fromLatin1("<e/>"));
2364 QVERIFY(query.isValid());
2366 QVERIFY(!query.evaluateTo(&result));
2369 void tst_QXmlQuery::evaluateToQIODevice() const
2371 /* an XQuery, check that no indentation is performed. */
2374 QVERIFY(out.open(QIODevice::ReadWrite));
2377 query.setQuery(QLatin1String("<a><b/></a>"));
2378 QVERIFY(query.isValid());
2379 QVERIFY(query.evaluateTo(&out));
2380 QCOMPARE(out.data(), QByteArray("<a><b/></a>"));
2384 void tst_QXmlQuery::evaluateToQIODeviceTriggerWarnings() const
2388 QTest::ignoreMessage(QtWarningMsg, "The pointer to the device cannot be null.");
2389 QCOMPARE(query.evaluateTo(static_cast<QIODevice *>(0)),
2394 QTest::ignoreMessage(QtWarningMsg, "The device must be writable.");
2395 QCOMPARE(query.evaluateTo(&buffer),
2399 void tst_QXmlQuery::evaluateToQIODeviceSignature() const
2401 /* The function should be const. */
2404 QVERIFY(out.open(QIODevice::ReadWrite));
2406 const QXmlQuery query;
2408 query.evaluateTo(&out);
2412 void tst_QXmlQuery::evaluateToQIODeviceOnInvalidQuery() const
2415 QVERIFY(out.open(QIODevice::WriteOnly));
2417 /* On syntactically invalid query. */
2420 MessageSilencer silencer;
2421 query.setMessageHandler(&silencer);
2422 query.setQuery(QLatin1String("1 +"));
2423 QVERIFY(!query.isValid());
2424 QVERIFY(!query.evaluateTo(&out));
2427 /* On null QXmlQuery instance. */
2430 QVERIFY(!query.evaluateTo(&out));
2435 void tst_QXmlQuery::setQueryQIODeviceQUrl() const
2440 buffer.setData("1, 2, 2 + 1");
2441 QVERIFY(buffer.open(QIODevice::ReadOnly));
2444 query.setQuery(&buffer);
2445 QVERIFY(query.isValid());
2447 QXmlResultItems result;
2448 query.evaluateTo(&result);
2449 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
2450 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
2451 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
2452 QVERIFY(result.next().isNull());
2453 QVERIFY(!result.hasError());
2456 /* Set query that is invalid. */
2459 buffer.setData("1, ");
2460 QVERIFY(buffer.open(QIODevice::ReadOnly));
2463 MessageSilencer silencer;
2464 query.setMessageHandler(&silencer);
2465 query.setQuery(&buffer);
2466 QVERIFY(!query.isValid());
2469 /* Check that the base URI passes through. */
2472 buffer.setData("string(static-base-uri())");
2473 QVERIFY(buffer.open(QIODevice::ReadOnly));
2476 query.setQuery(&buffer, QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl"));
2477 QVERIFY(query.isValid());
2480 query.evaluateTo(&result);
2481 QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
2485 void tst_QXmlQuery::setQueryQIODeviceQUrlTriggerWarnings() const
2488 QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed.");
2492 QTest::ignoreMessage(QtWarningMsg, "The device must be readable.");
2493 query.setQuery(&buffer);
2496 void tst_QXmlQuery::setQueryQString() const
2501 query.setQuery(QLatin1String("1, 2, 2 + 1"));
2502 QVERIFY(query.isValid());
2504 QXmlResultItems result;
2505 query.evaluateTo(&result);
2506 QCOMPARE(result.next().toAtomicValue(), QVariant(1));
2507 QCOMPARE(result.next().toAtomicValue(), QVariant(2));
2508 QCOMPARE(result.next().toAtomicValue(), QVariant(3));
2509 QVERIFY(result.next().isNull());
2510 QVERIFY(!result.hasError());
2513 /* Set query that is invalid. */
2515 MessageSilencer silencer;
2517 query.setMessageHandler(&silencer);
2518 query.setQuery(QLatin1String("1, "));
2519 QVERIFY(!query.isValid());
2522 /* Check that the base URI passes through. */
2525 query.setQuery(QLatin1String("string(static-base-uri())"), QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl"));
2526 QVERIFY(query.isValid());
2529 query.evaluateTo(&result);
2530 QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
2534 void tst_QXmlQuery::setQueryQUrlSuccess() const
2536 #if defined(Q_OS_WINCE) && !defined(_X86_)
2537 QStringList testsToSkip;
2538 testsToSkip << "A valid query via the ftp scheme" << "A valid query via the http scheme";
2539 if (testsToSkip.contains(QTest::currentDataTag()))
2540 QSKIP("Network tests are currently unsupported on Windows CE.", SkipSingle);
2543 QFETCH(QUrl, queryURI);
2544 QFETCH(QByteArray, expectedOutput);
2546 QVERIFY(queryURI.isValid());
2550 MessageSilencer silencer;
2551 query.setMessageHandler(&silencer);
2553 query.setQuery(queryURI);
2554 QVERIFY(query.isValid());
2557 QBuffer buffer(&out);
2558 QVERIFY(buffer.open(QIODevice::WriteOnly));
2559 QXmlSerializer serializer(query, &buffer);
2561 query.evaluateTo(&serializer);
2562 QCOMPARE(out, expectedOutput);
2565 void tst_QXmlQuery::setQueryQUrlSuccess_data() const
2567 QTest::addColumn<QUrl>("queryURI");
2568 QTest::addColumn<QByteArray>("expectedOutput");
2570 QTest::newRow("A valid query via the data scheme")
2571 << QUrl::fromEncoded("data:application/xml,1%20%2B%201") /* "1 + 1" */
2574 QTest::newRow("A valid query via the file scheme")
2575 << QUrl::fromLocalFile(inputFile(QLatin1String(queriesDirectory) + QLatin1String("onePlusOne.xq")))
2581 QTest::newRow("A valid query via the ftp scheme")
2582 << QUrl::fromEncoded(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/viaFtp.xq").toLatin1())
2583 << QByteArray("This was received via FTP");
2585 QTest::newRow("A valid query via the http scheme")
2586 << QUrl::fromEncoded(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/viaHttp.xq").toLatin1())
2587 << QByteArray("This was received via HTTP.");
2590 void tst_QXmlQuery::setQueryQUrlFailSucceed() const
2593 MessageSilencer silencer;
2595 query.setMessageHandler(&silencer);
2597 query.setQuery(QLatin1String("1 + 1"));
2598 QVERIFY(query.isValid());
2600 query.setQuery(QUrl::fromEncoded("file://example.com/does/not/exist"));
2601 QVERIFY(!query.isValid());
2604 void tst_QXmlQuery::setQueryQUrlFailure() const
2606 QFETCH(QUrl, queryURI);
2608 MessageSilencer silencer;
2611 query.setMessageHandler(&silencer);
2612 query.setQuery(queryURI);
2613 QVERIFY(!query.isValid());
2616 void tst_QXmlQuery::setQueryQUrlFailure_data() const
2618 QTest::addColumn<QUrl>("queryURI");
2620 QTest::newRow("Query via file:// that does not exist.")
2621 << QUrl::fromEncoded("file://example.com/does/not/exist");
2623 QTest::newRow("A query via file:// that is completely empty, but readable.")
2624 << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("../xmlpatterns/queries/completelyEmptyQuery.xq"));
2627 const QString name(QLatin1String("nonReadableFile.xq"));
2628 QFile outFile(name);
2629 QVERIFY(outFile.open(QIODevice::WriteOnly));
2630 outFile.write(QByteArray("1"));
2632 /* On some windows versions, this fails, so we don't check that this works with QVERIFY. */
2633 outFile.setPermissions(QFile::Permissions(QFile::Permissions()));
2635 QTest::newRow("Query via file:/ that does not have read permissions.")
2636 << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("nonReadableFile.xq"));
2642 QTest::newRow("Query via HTTP that does not exist.")
2643 << QUrl::fromEncoded("http://example.com/NoQuery/ISWear");
2646 QTest::newRow("Query via FTP that does not exist.")
2647 << QUrl::fromEncoded("ftp://example.com/NoQuery/ISWear");
2650 QTest::newRow("A query via http:// that is completely empty, but readable.")
2651 << QUrl::fromEncoded(QString(
2652 "http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/completelyEmptyQuery.xq").toLatin1());
2654 QTest::newRow("A query via ftp:// that is completely empty, but readable.")
2655 << QUrl::fromEncoded(QString(
2656 "ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/completelyEmptyQuery.xq").toLatin1());
2660 void tst_QXmlQuery::setQueryQUrlBaseURI() const
2662 QFETCH(QUrl, inputBaseURI);
2663 QFETCH(QUrl, expectedBaseURI);
2667 query.setQuery(QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq")), inputBaseURI);
2668 QVERIFY(query.isValid());
2671 QVERIFY(query.evaluateTo(&result));
2672 QCOMPARE(result.count(), 1);
2674 if(qstrcmp(QTest::currentDataTag(), "Relative base URI") == 0)
2675 checkBaseURI(QUrl(result.first()), QCoreApplication::applicationFilePath());
2677 QCOMPARE(result.first(), expectedBaseURI.toString());
2680 void tst_QXmlQuery::setQueryQUrlBaseURI_data() const
2682 QTest::addColumn<QUrl>("inputBaseURI");
2683 QTest::addColumn<QUrl>("expectedBaseURI");
2685 QTest::newRow("absolute HTTP")
2686 << QUrl(QLatin1String("http://www.example.com/"))
2687 << QUrl(QLatin1String("http://www.example.com/"));
2689 QTest::newRow("None, so the query URI is used")
2691 << QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq"));
2693 QTest::newRow("Relative base URI")
2694 << QUrl(QLatin1String("../data/relative.uri"))
2699 1. Create a valid query.
2700 2. Call setQuery(QUrl), with a query file that doesn't exist.
2701 3. Verify that the query has changed state into invalid.
2703 void tst_QXmlQuery::setQueryWithNonExistentQUrlOnValidQuery() const
2707 MessageSilencer messageSilencer;
2708 query.setMessageHandler(&messageSilencer);
2710 query.setQuery(QLatin1String("1 + 1"));
2711 QVERIFY(query.isValid());
2713 query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/DOESNOTEXIST.xq"));
2714 QVERIFY(!query.isValid());
2718 1. Create a valid query.
2719 2. Call setQuery(QUrl), with a query file that is invalid.
2720 3. Verify that the query has changed state into invalid.
2722 void tst_QXmlQuery::setQueryWithInvalidQueryFromQUrlOnValidQuery() const
2726 MessageSilencer messageSilencer;
2727 query.setMessageHandler(&messageSilencer);
2729 query.setQuery(QLatin1String("1 + 1"));
2730 QVERIFY(query.isValid());
2732 query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/queries/syntaxError.xq"));
2733 QVERIFY(!query.isValid());
2737 This triggered two bugs:
2739 - First, the DynamicContext wasn't assigned to QXmlResultItems, meaning it went out of
2740 scope and therefore deallocated the document pool, and calls
2741 to QXmlResultItems::next() would use dangling pointers.
2743 - Conversion between QPatternist::Item and QXmlItem was incorrectly done, leading to nodes
2744 being treated as atomic values, and subsequent crashes.
2747 void tst_QXmlQuery::retrieveNameFromQuery() const
2749 QFETCH(QString, queryString);
2750 QFETCH(QString, expectedName);
2753 query.setQuery(queryString);
2754 QVERIFY(query.isValid());
2755 QXmlResultItems result;
2756 query.evaluateTo(&result);
2758 QVERIFY(!result.hasError());
2760 const QXmlItem item(result.next());
2761 QVERIFY(!result.hasError());
2762 QVERIFY(!item.isNull());
2763 QVERIFY(item.isNode());
2765 const QXmlNodeModelIndex node(item.toNodeModelIndex());
2766 QVERIFY(!node.isNull());
2768 QCOMPARE(node.model()->name(node).localName(query.namePool()), expectedName);
2771 void tst_QXmlQuery::retrieveNameFromQuery_data() const
2773 QTest::addColumn<QString>("queryString");
2774 QTest::addColumn<QString>("expectedName");
2776 QTest::newRow("Document-node")
2777 << QString::fromLatin1("document{<elementName/>}")
2780 QTest::newRow("Element")
2781 << QString::fromLatin1("document{<elementName/>}/*")
2782 << QString::fromLatin1("elementName");
2786 Binding a null QString leads to no variable binding, but an
2787 empty non-null QString is possible.
2789 void tst_QXmlQuery::bindEmptyNullString() const
2791 MessageSilencer messageHandler;
2793 query.setMessageHandler(&messageHandler);
2794 query.setQuery(QLatin1String("declare variable $v external; $v"));
2795 /* Here, we effectively pass an invalid QVariant. */
2796 query.bindVariable(QLatin1String("v"), QVariant(QString()));
2797 QVERIFY(!query.isValid());
2800 QVERIFY(!query.evaluateTo(&result));
2803 void tst_QXmlQuery::bindEmptyString() const
2806 query.bindVariable(QLatin1String("v"), QVariant(QString(QLatin1String(""))));
2807 query.setQuery(QLatin1String("declare variable $v external; $v"));
2808 QVERIFY(query.isValid());
2811 QVERIFY(query.evaluateTo(&result));
2812 QStringList expected((QString()));
2813 QCOMPARE(result, expected);
2816 void tst_QXmlQuery::cleanupTestCase() const
2818 /* Remove a weird file we created. */
2819 const QString name(QLatin1String("nonReadableFile.xq"));
2821 if(QFile::exists(name))
2824 QVERIFY(file.setPermissions(QFile::WriteOwner));
2825 QVERIFY(file.remove());
2829 void tst_QXmlQuery::declareUnavailableExternal() const
2832 MessageSilencer silencer;
2833 query.setMessageHandler(&silencer);
2834 query.setQuery(QLatin1String("declare variable $var external;"
2836 /* We do not bind $var with QXmlQuery::bindVariable(). */
2837 QVERIFY(!query.isValid());
2841 This test triggers an assert in one of the cache iterator
2842 with MSVC 2005 when compiled in debug mode.
2844 void tst_QXmlQuery::msvcCacheIssue() const
2847 query.bindVariable(QLatin1String("externalVariable"), QXmlItem("Variable Value"));
2848 query.setQuery(QUrl::fromLocalFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariableUsedTwice.xq")));
2850 QVERIFY(query.evaluateTo(&result));
2853 QStringList() << QString::fromLatin1("Variable Value") << QString::fromLatin1("Variable Value"));
2856 void tst_QXmlQuery::unavailableExternalVariable() const
2860 MessageSilencer silencer;
2861 query.setMessageHandler(&silencer);
2863 query.setQuery(QLatin1String("declare variable $foo external; 1"));
2865 QVERIFY(!query.isValid());
2869 Ensure that setUriResolver() affects \c fn:doc() and \c fn:doc-available().
2871 void tst_QXmlQuery::useUriResolver() const
2873 class TestUriResolver : public QAbstractUriResolver
2874 , private TestFundament
2877 TestUriResolver() {}
2878 virtual QUrl resolve(const QUrl &relative,
2879 const QUrl &baseURI) const
2882 return baseURI.resolved(inputFile(QLatin1String(queriesDirectory) + QLatin1String("simpleDocument.xml")));
2886 const TestUriResolver uriResolver;
2889 query.setUriResolver(&uriResolver);
2890 query.setQuery(QLatin1String("let $i := 'http://www.example.com/DoesNotExist'"
2891 "return (string(doc($i)), doc-available($i))"));
2894 QXmlResultItems result;
2895 query.evaluateTo(&result);
2897 QVERIFY(!result.hasError());
2898 QCOMPARE(result.next().toAtomicValue().toString(), QString::fromLatin1("text text node"));
2899 QCOMPARE(result.next().toAtomicValue().toBool(), true);
2900 QVERIFY(result.next().isNull());
2901 QVERIFY(!result.hasError());
2904 void tst_QXmlQuery::queryWithFocusAndVariable() const
2907 query.setFocus(QXmlItem(5));
2908 query.bindVariable(QLatin1String("var"), QXmlItem(2));
2910 query.setQuery(QLatin1String("string(. * $var)"));
2914 QVERIFY(query.evaluateTo(&result));
2916 QCOMPARE(result, QStringList(QLatin1String("10")));
2919 void tst_QXmlQuery::undefinedFocus() const
2923 MessageSilencer silencer;
2924 query.setMessageHandler(&silencer);
2926 query.setQuery(QLatin1String("."));
2927 QVERIFY(!query.isValid());
2930 void tst_QXmlQuery::basicFocusUsage() const
2934 MessageSilencer silencer;
2935 query.setMessageHandler(&silencer);
2937 query.setFocus(QXmlItem(5));
2938 query.setQuery(QLatin1String("string(. * .)"));
2939 QVERIFY(query.isValid());
2942 QVERIFY(query.evaluateTo(&result));
2944 QCOMPARE(result, QStringList(QLatin1String("25")));
2948 Triggers an ownership related crash.
2950 void tst_QXmlQuery::copyCheckMessageHandler() const
2953 QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0));
2955 query.setQuery(QLatin1String("doc('qrc:/QXmlQueryTestData/data/oneElement.xml')"));
2956 /* By now, we should have set the builtin message handler. */
2957 const QAbstractMessageHandler *const messageHandler = query.messageHandler();
2958 QVERIFY(messageHandler);
2961 /* This copies QXmlQueryPrivate::m_ownerObject, and its destructor
2962 * will delete it, and hence the builtin message handler attached to it. */
2963 QXmlQuery copy(query);
2966 QXmlResultItems result;
2967 query.evaluateTo(&result);
2969 while(!result.next().isNull())
2972 QVERIFY(!result.hasError());
2975 void tst_QXmlQuery::queryLanguage() const
2977 /* Check default value. */
2979 const QXmlQuery query;
2980 QCOMPARE(query.queryLanguage(), QXmlQuery::XQuery10);
2983 /* Check default value of copies default instance. */
2985 const QXmlQuery query1;
2986 const QXmlQuery query2(query1);
2988 QCOMPARE(query1.queryLanguage(), QXmlQuery::XQuery10);
2989 QCOMPARE(query2.queryLanguage(), QXmlQuery::XQuery10);
2993 void tst_QXmlQuery::queryLanguageSignature() const
2995 /* This getter should be const. */
2997 query.queryLanguage();
3000 void tst_QXmlQuery::enumQueryLanguage() const
3002 /* These enum values should be possible to OR for future plans. */
3003 QCOMPARE(int(QXmlQuery::XQuery10), 1);
3004 QCOMPARE(int(QXmlQuery::XSLT20), 2);
3005 QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintSelector), 1024);
3006 QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintField), 2048);
3007 QCOMPARE(int(QXmlQuery::XPath20), 4096);
3010 void tst_QXmlQuery::setInitialTemplateNameQXmlName() const
3012 QXmlQuery query(QXmlQuery::XSLT20);
3013 QXmlNamePool np(query.namePool());
3014 const QXmlName name(np, QLatin1String("main"));
3016 query.setInitialTemplateName(name);
3018 QCOMPARE(query.initialTemplateName(), name);
3020 query.setQuery(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/namedTemplate.xsl"))));
3021 QVERIFY(query.isValid());
3024 QVERIFY(result.open(QIODevice::ReadWrite));
3025 QXmlSerializer serializer(query, &result);
3026 query.evaluateTo(&serializer);
3028 QCOMPARE(result.data(), QByteArray("1 2 3 4 5"));
3030 // TODO invoke a template which has required params.
3033 void tst_QXmlQuery::setInitialTemplateNameQXmlNameSignature() const
3036 QXmlNamePool np(query.namePool());
3037 const QXmlName name(np, QLatin1String("foo"));
3039 /* The signature should take a const reference. */
3040 query.setInitialTemplateName(name);
3043 void tst_QXmlQuery::setInitialTemplateNameQString() const
3046 QXmlNamePool np(query.namePool());
3047 query.setInitialTemplateName(QLatin1String("foo"));
3049 QCOMPARE(query.initialTemplateName(), QXmlName(np, QLatin1String("foo")));
3052 void tst_QXmlQuery::setInitialTemplateNameQStringSignature() const
3054 const QString name(QLatin1String("name"));
3057 /* We should take a const reference. */
3058 query.setInitialTemplateName(name);
3061 void tst_QXmlQuery::initialTemplateName() const
3063 /* Check our default value. */
3065 QCOMPARE(query.initialTemplateName(), QXmlName());
3066 QVERIFY(query.initialTemplateName().isNull());
3069 void tst_QXmlQuery::initialTemplateNameSignature() const
3071 const QXmlQuery query;
3072 /* This should be a const member. */
3073 query.initialTemplateName();
3076 void tst_QXmlQuery::setNetworkAccessManager() const
3079 /* Ensure fn:doc() picks up the right QNetworkAccessManager. */
3081 NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
3082 QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/queries/simpleDocument.xml"))));
3085 query.setNetworkAccessManager(&networkOverrider);
3086 query.setQuery(QLatin1String("string(doc('tag:example.com:DOESNOTEXIST'))"));
3087 QVERIFY(query.isValid());
3090 QVERIFY(query.evaluateTo(&result));
3092 QCOMPARE(result, QStringList(QLatin1String("text text node")));
3095 /* Ensure setQuery() is using the right network manager. */
3097 NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
3098 QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/queries/concat.xq"))));
3101 query.setNetworkAccessManager(&networkOverrider);
3102 query.setQuery(QUrl("tag:example.com:DOESNOTEXIST"));
3103 QVERIFY(query.isValid());
3106 QVERIFY(query.evaluateTo(&result));
3108 QCOMPARE(result, QStringList(QLatin1String("abcdef")));
3111 void tst_QXmlQuery::networkAccessManagerSignature() const
3114 const QXmlQuery query;
3116 /* The function should be const. */
3117 query.networkAccessManager();
3120 void tst_QXmlQuery::networkAccessManagerDefaultValue() const
3122 const QXmlQuery query;
3124 QCOMPARE(query.networkAccessManager(), static_cast<QNetworkAccessManager *>(0));
3127 void tst_QXmlQuery::networkAccessManager() const
3129 /* Test that we return the network manager that was set. */
3131 QNetworkAccessManager manager;
3133 query.setNetworkAccessManager(&manager);
3134 QCOMPARE(query.networkAccessManager(), &manager);
3142 1. Load a document into QXmlQuery's document cache, by executing a query which does it.
3144 3. Change query, to one which uses the focus
3149 void tst_QXmlQuery::multipleDocsAndFocus() const
3153 /* We use string concatenation, since variable bindings might disturb what
3155 query.setQuery(QLatin1String("string(doc('") +
3156 inputFile(QLatin1String(XMLPATTERNSDIR "/queries/simpleDocument.xml")) +
3157 QLatin1String("'))"));
3158 query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3159 query.setQuery(QLatin1String("string(.)"));
3162 QVERIFY(query.evaluateTo(&result));
3177 void tst_QXmlQuery::multipleEvaluationsWithDifferentFocus() const
3182 query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3183 query.setQuery(QLatin1String("string(.)"));
3184 QVERIFY(query.evaluateTo(&result));
3186 query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3187 QVERIFY(query.evaluateTo(&result));
3190 void tst_QXmlQuery::bindVariableQXmlQuery() const
3192 QFETCH(QString, query1);
3193 QFETCH(QString, query2);
3194 QFETCH(QString, expectedOutput);
3195 QFETCH(bool, expectedSuccess);
3197 MessageSilencer silencer;
3198 QXmlQuery xmlQuery1;
3199 xmlQuery1.setMessageHandler(&silencer);
3200 xmlQuery1.setQuery(query1);
3202 QXmlQuery xmlQuery2(xmlQuery1);
3203 xmlQuery2.bindVariable("query1", xmlQuery1);
3204 xmlQuery2.setQuery(query2);
3207 const bool querySuccess = xmlQuery2.evaluateTo(&output);
3209 QCOMPARE(querySuccess, expectedSuccess);
3212 QCOMPARE(output, expectedOutput);
3215 void tst_QXmlQuery::bindVariableQXmlQuery_data() const
3217 QTest::addColumn<QString>("query1");
3218 QTest::addColumn<QString>("query2");
3219 QTest::addColumn<QString>("expectedOutput");
3220 QTest::addColumn<bool>("expectedSuccess");
3222 QTest::newRow("First query has one atomic value.")
3228 QTest::newRow("First query has two atomic values.")
3234 QTest::newRow("First query is a node.")
3240 /* This is a good test, because it triggers the exception in the
3241 * bindVariable() call, as supposed to when the actual evaluation is done.
3243 QTest::newRow("First query has a dynamic error.")
3246 << QString() /* We don't care. */
3250 void tst_QXmlQuery::bindVariableQStringQXmlQuerySignature() const
3253 query1.setQuery("'dummy'");
3256 const QString name(QLatin1String("name"));
3258 /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
3259 * QXmlQuery, and evaluation is what we do here. */
3260 query2.bindVariable(name, const_cast<const QXmlQuery &>(query1));
3263 void tst_QXmlQuery::bindVariableQXmlNameQXmlQuerySignature() const
3266 QXmlQuery query1(np);
3267 query1.setQuery("'dummy'");
3270 const QXmlName name(np, QLatin1String("name"));
3272 /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
3273 * QXmlQuery, and evaluation is what we do here. */
3274 query2.bindVariable(name, const_cast<const QXmlQuery &>(query1));
3278 Check that the QXmlName is handled correctly.
3280 void tst_QXmlQuery::bindVariableQXmlNameQXmlQuery() const
3284 query1.setQuery(QLatin1String("1"));
3286 QXmlQuery query2(np);
3287 query2.bindVariable(QXmlName(np, QLatin1String("theName")), query1);
3288 query2.setQuery("$theName");
3291 query2.evaluateTo(&result);
3293 QCOMPARE(result, QString::fromLatin1("1\n"));
3296 void tst_QXmlQuery::bindVariableQXmlQueryInvalidate() const
3299 query.bindVariable(QLatin1String("name"), QVariant(1));
3300 query.setQuery("$name");
3301 QVERIFY(query.isValid());
3304 query2.setQuery("'query2'");
3306 query.bindVariable(QLatin1String("name"), query);
3307 QVERIFY(!query.isValid());
3310 void tst_QXmlQuery::unknownSourceLocation() const
3313 b.setData("<a><b/><b/></a>");
3314 b.open(QIODevice::ReadOnly);
3316 MessageSilencer silencer;
3318 query.bindVariable(QLatin1String("inputDocument"), &b);
3319 query.setMessageHandler(&silencer);
3321 query.setQuery(QLatin1String("doc($inputDocument)/a/(let $v := b/string() return if ($v) then $v else ())"));
3324 query.evaluateTo(&output);
3327 void tst_QXmlQuery::identityConstraintSuccess() const
3329 QXmlQuery::QueryLanguage queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintSelector;
3331 /* We run this code for Selector and Field. */
3332 for(int i = 0; i < 3; ++i)
3334 QXmlNamePool namePool;
3335 QXmlResultItems result;
3339 QXmlQuery nodeSource(namePool);
3340 nodeSource.setQuery(QLatin1String("<e/>"));
3342 nodeSource.evaluateTo(&result);
3343 node = result.next();
3347 * 1. The focus is undefined, but it's still valid.
3348 * 2. We never evaluate. */
3350 QXmlQuery query(queryLanguage);
3351 query.setQuery(QLatin1String("a"));
3352 QVERIFY(query.isValid());
3356 * 1. The focus is undefined, but it's still valid.
3357 * 2. We afterwards set the focus. */
3359 QXmlQuery query(queryLanguage, namePool);
3360 query.setQuery(QLatin1String("a"));
3361 query.setFocus(node);
3362 QVERIFY(query.isValid());
3366 * 1. The focus is undefined, but it's still valid.
3367 * 2. We afterwards set the focus.
3368 * 3. We evaluate. */
3370 QXmlQuery query(queryLanguage, namePool);
3371 query.setQuery(QString(QLatin1Char('.')));
3372 query.setFocus(node);
3373 QVERIFY(query.isValid());
3376 QVERIFY(query.evaluateTo(&result));
3377 QCOMPARE(result, QString::fromLatin1("<e/>\n"));
3380 /* A slightly more complex Field. */
3382 QXmlQuery query(queryLanguage);
3383 query.setQuery(QLatin1String("* | .//xml:*/."));
3384 QVERIFY(query.isValid());
3387 /* @ is only allowed in Field. */
3388 if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField)
3390 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField);
3391 query.setQuery(QLatin1String("@abc"));
3392 QVERIFY(query.isValid());
3395 /* Field allows attribute:: and child:: .*/
3396 if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField)
3398 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField);
3399 query.setQuery(QLatin1String("attribute::name | child::name"));
3400 QVERIFY(query.isValid());
3403 /* Selector allows only child:: .*/
3405 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintSelector);
3406 query.setQuery(QLatin1String("child::name"));
3407 QVERIFY(query.isValid());
3411 queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintField;
3413 queryLanguage = QXmlQuery::XPath20;
3417 Q_DECLARE_METATYPE(QXmlQuery::QueryLanguage);
3420 We just do some basic tests for boot strapping and sanity checking. The actual regression
3421 testing is in the Schema suite.
3423 void tst_QXmlQuery::identityConstraintFailure() const
3425 QFETCH(QXmlQuery::QueryLanguage, queryLanguage);
3426 QFETCH(QString, inputQuery);
3428 QXmlQuery query(queryLanguage);
3429 MessageSilencer silencer;
3430 query.setMessageHandler(&silencer);
3432 query.setQuery(inputQuery);
3433 QVERIFY(!query.isValid());
3436 void tst_QXmlQuery::identityConstraintFailure_data() const
3438 QTest::addColumn<QXmlQuery::QueryLanguage>("queryLanguage");
3439 QTest::addColumn<QString>("inputQuery");
3441 QTest::newRow("We don't have element constructors in identity constraint pattern, "
3442 "it's an XQuery feature(Selector).")
3443 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3444 << QString::fromLatin1("<e/>");
3446 QTest::newRow("We don't have functions in identity constraint pattern, "
3447 "it's an XPath feature(Selector).")
3448 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3449 << QString::fromLatin1("current-time()");
3451 QTest::newRow("We don't have element constructors in identity constraint pattern, "
3452 "it's an XQuery feature(Field).")
3453 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3454 << QString::fromLatin1("<e/>");
3456 QTest::newRow("We don't have functions in identity constraint pattern, "
3457 "it's an XPath feature(Field).")
3458 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3459 << QString::fromLatin1("current-time()");
3461 QTest::newRow("@attributeName is disallowed for the selector.")
3462 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3463 << QString::fromLatin1("@abc");
3465 QTest::newRow("attribute:: is disallowed for the selector.")
3466 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3467 << QString::fromLatin1("attribute::name");
3469 QTest::newRow("ancestor::name is disallowed for the selector.")
3470 << QXmlQuery::XmlSchema11IdentityConstraintSelector
3471 << QString::fromLatin1("ancestor::name");
3473 QTest::newRow("ancestor::name is disallowed for the field.")
3474 << QXmlQuery::XmlSchema11IdentityConstraintField
3475 << QString::fromLatin1("ancestor::name");
3478 QTEST_MAIN(tst_QXmlQuery)
3480 #include "tst_qxmlquery.moc"
3481 #else //QTEST_XMLPATTERNS