Remove Symbian-specific code from tests.
[profile/ivi/qtxmlpatterns.git] / tests / auto / qxmlquery / tst_qxmlquery.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the test suite of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42
43 #include <QtTest/QtTest>
44
45 #ifdef QTEST_XMLPATTERNS
46
47 #include <QAbstractMessageHandler>
48 #include <QFileInfo>
49 #include <QNetworkReply>
50 #include <QtNetwork/QTcpServer>
51 #include <QtNetwork/QTcpSocket>
52 #include <QXmlFormatter>
53 #include <QXmlItem>
54 #include <QXmlName>
55 #include <QXmlQuery>
56 #include <QXmlResultItems>
57 #include <QXmlSerializer>
58
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"
67
68 #if defined(Q_OS_SYMBIAN)
69 #define SRCDIR ""
70 #define XMLPATTERNSDIR "xmlpatterns"
71 #else
72 #define XMLPATTERNSDIR SRCDIR "../xmlpatterns"
73 #endif
74
75 /*!
76  \class tst_QXmlQuery
77  \internal
78  \since 4.4
79  \brief Tests class QXmlQuery.
80
81  This test is not intended for testing the engine, but the functionality specific
82  to the QXmlQuery class.
83
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.
86
87  */
88 class tst_QXmlQuery : public QObject
89                     , private TestFundament
90 {
91     Q_OBJECT
92
93 public:
94     inline tst_QXmlQuery() : m_generatedBaselines(0)
95                            , m_pushTestsCount(0)
96                            , m_testNetwork(true)
97     {
98     }
99
100 private Q_SLOTS:
101     void defaultConstructor() const;
102     void copyConstructor() const;
103     void constructorQXmlNamePool() const;
104     void constructorQXmlNamePoolQueryLanguage() const;
105     void constructorQXmlNamePoolWithinQSimpleXmlNodeModel() const;
106     void assignmentOperator() const;
107     void isValid() const;
108     void sequentialExecution() const;
109     void bindVariableQString() const;
110     void bindVariableQStringNoExternalDeclaration() const;
111     void bindVariableQXmlName() const;
112     void bindVariableQXmlNameTriggerWarnings() const;
113     void bindVariableQStringQIODevice() const;
114     void bindVariableQStringQIODeviceWithByteArray() const;
115     void bindVariableQStringQIODeviceWithString() const;
116     void bindVariableQStringQIODeviceWithQFile() const;
117     void bindVariableQXmlNameQIODevice() const;
118     void bindVariableQXmlNameQIODeviceTriggerWarnings() const;
119     void bindVariableXSLTSuccess() const;
120     void bindVariableTemporaryNode() const;
121     void setMessageHandler() const;
122     void messageHandler() const;
123     void evaluateToQAbstractXmlReceiverTriggerWarnings() const;
124     void evaluateToQXmlResultItems() const;
125     void evaluateToQXmlResultItemsTriggerWarnings() const;
126     void evaluateToQXmlResultItemsErrorAtEnd() const;
127     void evaluateToReceiver();
128     void evaluateToReceiver_data() const;
129     void evaluateToReceiverOnInvalidQuery() const;
130     void evaluateToQStringTriggerError() const;
131     void evaluateToQString() const;
132     void evaluateToQString_data() const;
133     void evaluateToQStringSignature() const;
134     void checkGeneratedBaselines() const;
135     void basicXQueryToQtTypeCheck() const;
136     void basicQtToXQueryTypeCheck() const;
137     void bindNode() const;
138     void relativeBaseURI() const;
139     void emptyBaseURI() const;
140     void roundTripDateWithinQXmlItem() const;
141     void bindingMissing() const;
142     void bindDefaultConstructedItem() const;
143     void bindDefaultConstructedItem_data() const;
144     void bindEmptyNullString() const;
145     void bindEmptyString() const;
146     void rebindVariableSameType() const;
147     void rebindVariableDifferentType() const;
148     void rebindVariableWithNullItem() const;
149     void eraseQXmlItemBinding() const;
150     void eraseDeviceBinding() const;
151     void constCorrectness() const;
152     void objectSize() const;
153     void setUriResolver() const;
154     void uriResolver() const;
155     void messageXML() const;
156     void resultItemsDeallocatedQuery() const;
157     void copyCheckMessageHandler() const;
158     void shadowedVariables() const;
159     void setFocusQXmlItem() const;
160     void setFocusQUrl() const;
161     void setFocusQIODevice() const;
162     void setFocusQIODeviceAvoidVariableClash() const;
163     void setFocusQIODeviceFailure() const;
164     void setFocusQIODeviceTriggerWarnings() const;
165     void setFocusQString() const;
166     void setFocusQStringFailure() const;
167     void setFocusQStringSignature() const;
168     void recompilationWithEvaluateToResultFailing() const;
169     void secondEvaluationWithEvaluateToResultFailing() const;
170     void recompilationWithEvaluateToReceiver() const;
171     void fnDocOnQIODeviceTimeout() const;
172     void evaluateToQStringListOnInvalidQuery() const;
173     void evaluateToQStringList() const;
174     void evaluateToQStringListTriggerWarnings() const;
175     void evaluateToQStringList_data() const;
176     void evaluateToQStringListNoConversion() const;
177     void evaluateToQIODevice() const;
178     void evaluateToQIODeviceTriggerWarnings() const;
179     void evaluateToQIODeviceSignature() const;
180     void evaluateToQIODeviceOnInvalidQuery() const;
181     void setQueryQIODeviceQUrl() const;
182     void setQueryQIODeviceQUrlTriggerWarnings() const;
183     void setQueryQString() const;
184     void setQueryQUrlSuccess() const;
185     void setQueryQUrlSuccess_data() const;
186     void setQueryQUrlFailSucceed() const;
187     void setQueryQUrlFailure() const;
188     void setQueryQUrlFailure_data() const;
189     void setQueryQUrlBaseURI() const;
190     void setQueryQUrlBaseURI_data() const;
191     void setQueryWithNonExistentQUrlOnValidQuery() const;
192     void setQueryWithInvalidQueryFromQUrlOnValidQuery() const;
193     void retrieveNameFromQuery() const;
194     void retrieveNameFromQuery_data() const;
195     void cleanupTestCase() const;
196     void declareUnavailableExternal() const;
197     void msvcCacheIssue() const;
198     void unavailableExternalVariable() const;
199     void useUriResolver() const;
200     void queryWithFocusAndVariable() const;
201     void undefinedFocus() const;
202     void basicFocusUsage() const;
203
204     void queryLanguage() const;
205     void queryLanguageSignature() const;
206     void enumQueryLanguage() const;
207
208     void setNetworkAccessManager() const;
209     void networkAccessManagerSignature() const;
210     void networkAccessManagerDefaultValue() const;
211     void networkAccessManager() const;
212
213     void setInitialTemplateNameQXmlName() const;
214     void setInitialTemplateNameQXmlNameSignature() const;
215     void setInitialTemplateNameQString() const;
216     void setInitialTemplateNameQStringSignature() const;
217     void initialTemplateName() const;
218     void initialTemplateNameSignature() const;
219
220     void fnDocNetworkAccessSuccess() const;
221     void fnDocNetworkAccessSuccess_data() const;
222     void fnDocNetworkAccessFailure() const;
223     void fnDocNetworkAccessFailure_data() const;
224     void multipleDocsAndFocus() const;
225     void multipleEvaluationsWithDifferentFocus() const;
226     void bindVariableQXmlQuery() const;
227     void bindVariableQXmlQuery_data() const;
228     void bindVariableQStringQXmlQuerySignature() const;
229     void bindVariableQXmlNameQXmlQuerySignature() const;
230     void bindVariableQXmlNameQXmlQuery() const;
231     void bindVariableQXmlQueryInvalidate() const;
232     void unknownSourceLocation() const;
233
234     void identityConstraintSuccess() const;
235     void identityConstraintFailure() const;
236     void identityConstraintFailure_data() const;
237
238     // 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.
239     // TODO bind stringlists, variant lists, both ways.
240     // TODO trigger serialization error, or any error in evaluateToushCallback().
241     // TODO let items travle between two queries, as seen in the SDK
242     // TODO what happens if the query declares local variable and external ones are provided?
243
244 private:
245     enum
246     {
247         /**
248          * One excluded, since we skip static-base-uri.xq.
249          */
250         ExpectedQueryCount = 29
251     };
252
253     static void checkBaseURI(const QUrl &baseURI, const QString &candidate);
254     static QStringList queries();
255     static const char *const queriesDirectory;
256
257     int m_generatedBaselines;
258     int m_pushTestsCount;
259     const bool m_testNetwork;
260 };
261
262 void tst_QXmlQuery::checkBaseURI(const QUrl &baseURI, const QString &candidate)
263 {
264     /* The use of QFileInfo::canonicalFilePath() takes into account that drive letters
265      * on Windows may have different cases. */
266     QVERIFY(QDir(baseURI.toLocalFile()).relativeFilePath(QFileInfo(candidate).canonicalFilePath()).startsWith("../"));
267 }
268
269 const char *const tst_QXmlQuery::queriesDirectory = XMLPATTERNSDIR "/queries/";
270
271 QStringList tst_QXmlQuery::queries()
272 {
273     QDir dir;
274     dir.cd(inputFile(QLatin1String(queriesDirectory)));
275
276     return dir.entryList(QStringList(QLatin1String("*.xq")));
277 }
278
279 void tst_QXmlQuery::defaultConstructor() const
280 {
281     /* Allocate instance in different orders. */
282     {
283         QXmlQuery query;
284     }
285
286     {
287         QXmlQuery query1;
288         QXmlQuery query2;
289     }
290
291     {
292         QXmlQuery query1;
293         QXmlQuery query2;
294         QXmlQuery query3;
295     }
296 }
297
298 void tst_QXmlQuery::copyConstructor() const
299 {
300     /* Verify that we can take a const reference, and simply do a copy of a default constructed object. */
301     {
302         const QXmlQuery query1;
303         QXmlQuery query2(query1);
304     }
305
306     /* Copy twice. */
307     {
308         const QXmlQuery query1;
309         QXmlQuery query2(query1);
310         QXmlQuery query3(query2);
311     }
312
313     /* Verify that copying default values works. */
314     {
315         const QXmlQuery query1;
316         const QXmlQuery query2(query1);
317         QCOMPARE(query2.messageHandler(), query1.messageHandler());
318         QCOMPARE(query2.uriResolver(), query1.uriResolver());
319         QCOMPARE(query2.queryLanguage(), query1.queryLanguage());
320         QCOMPARE(query2.initialTemplateName(), query1.initialTemplateName());
321         QCOMPARE(query2.networkAccessManager(), query1.networkAccessManager());
322     }
323
324     /* Check that the
325      *
326      * - name pool
327      * - URI resolver
328      * - message handler
329      * - query language
330      * - initial template name
331      *
332      *   sticks with the copy. */
333     {
334         MessageSilencer silencer;
335         TestURIResolver resolver;
336         QNetworkAccessManager networkManager;
337         QXmlQuery query1(QXmlQuery::XSLT20);
338         QXmlNamePool np1(query1.namePool());
339
340         query1.setMessageHandler(&silencer);
341         query1.setUriResolver(&resolver);
342         query1.setNetworkAccessManager(&networkManager);
343
344         const QXmlName name(np1, QLatin1String("localName"),
345                                  QLatin1String("http://example.com/"),
346                                  QLatin1String("prefix"));
347         query1.setInitialTemplateName(name);
348
349         const QXmlQuery query2(query1);
350         QCOMPARE(query2.messageHandler(), static_cast<QAbstractMessageHandler *>(&silencer));
351         QCOMPARE(query2.uriResolver(), static_cast<const QAbstractUriResolver *>(&resolver));
352         QCOMPARE(query2.queryLanguage(), QXmlQuery::XSLT20);
353         QCOMPARE(query2.initialTemplateName(), name);
354         QCOMPARE(query2.networkAccessManager(), &networkManager);
355
356         QXmlNamePool np2(query2.namePool());
357
358         QCOMPARE(name.namespaceUri(np2), QString::fromLatin1("http://example.com/"));
359         QCOMPARE(name.localName(np2), QString::fromLatin1("localName"));
360         QCOMPARE(name.prefix(np2), QString::fromLatin1("prefix"));
361     }
362
363     {
364         QXmlQuery original;
365
366         original.setFocus(QXmlItem(4));
367         original.setQuery(QLatin1String("."));
368         QVERIFY(original.isValid());
369
370         const QXmlQuery copy(original);
371
372         QXmlResultItems result;
373         copy.evaluateTo(&result);
374         QCOMPARE(result.next().toAtomicValue(), QVariant(4));
375         QVERIFY(result.next().isNull());
376         QVERIFY(!result.hasError());
377     }
378
379     /* Copy, set, compare. Check that copies are independent. */
380     {
381         // TODO all members except queryLanguage().
382     }
383 }
384
385 void tst_QXmlQuery::constructorQXmlNamePool() const
386 {
387     /* Check that the namepool we are passed, is actually used. */
388     QXmlNamePool np;
389
390     QXmlQuery query(np);
391     const QXmlName name(np, QLatin1String("localName"),
392                             QLatin1String("http://example.com/"),
393                             QLatin1String("prefix"));
394
395     QXmlNamePool np2(query.namePool());
396     QCOMPARE(name.namespaceUri(np2), QString::fromLatin1("http://example.com/"));
397     QCOMPARE(name.localName(np2), QString::fromLatin1("localName"));
398     QCOMPARE(name.prefix(np2), QString::fromLatin1("prefix"));
399 }
400
401 /*!
402   Ensure that the internal variable loading mechanisms uses the user-supplied
403   name pool.
404
405   If that is not the case, different name pools are used and the code crashes.
406
407   \since 4.5
408  */
409 void tst_QXmlQuery::constructorQXmlNamePoolQueryLanguage() const
410 {
411     QXmlNamePool np;
412     QXmlName name(np, QLatin1String("arbitraryName"));
413
414     QXmlQuery query(QXmlQuery::XQuery10, np);
415
416     QBuffer input;
417     input.setData("<yall/>");
418
419     QVERIFY(input.open(QIODevice::ReadOnly));
420     query.bindVariable(name, &input);
421     query.setQuery("string(doc($arbitraryName))");
422
423     QStringList result;
424     query.evaluateTo(&result);
425 }
426
427 void tst_QXmlQuery::constructorQXmlNamePoolWithinQSimpleXmlNodeModel() const
428 {
429     class TestCTOR : public TestSimpleNodeModel
430     {
431     public:
432         TestCTOR(const QXmlNamePool &np) : TestSimpleNodeModel(np)
433         {
434         }
435
436         void checkCTOR() const
437         {
438             /* If this fails to compile, the constructor has trouble with taking
439              * a mutable reference.
440              *
441              * The reason we use the this pointer explicitly, is to avoid a compiler
442              * warnings with MSVC 2005. */
443             QXmlQuery(this->namePool());
444         }
445     };
446
447     QXmlNamePool np;
448     TestCTOR ctor(np);
449     ctor.checkCTOR();
450 }
451
452 void tst_QXmlQuery::assignmentOperator() const
453 {
454     class ReturnURI : public QAbstractUriResolver
455     {
456     public:
457         ReturnURI() {}
458         virtual QUrl resolve(const QUrl &relative,
459                              const QUrl &baseURI) const
460         {
461             return baseURI.resolved(relative);
462         }
463     };
464
465     /* Assign this to this. */
466     {
467         QXmlQuery query;
468         query = query;
469         query = query;
470         query = query;
471
472         /* Just call a couple of functions to give valgrind
473          * something to check. */
474         QVERIFY(!query.isValid());
475         query.messageHandler();
476     }
477
478     /* Assign null instances a couple of times. */
479     {
480         QXmlQuery query1;
481         QXmlQuery query2;
482         query1 = query2;
483         query1 = query2;
484         query1 = query2;
485
486         /* Just call a couple of functions to give valgrind
487          * something to check. */
488         QVERIFY(!query1.isValid());
489         query1.messageHandler();
490
491         /* Just call a couple of functions to give valgrind
492          * something to check. */
493         QVERIFY(!query2.isValid());
494         query2.messageHandler();
495     }
496
497     /* Create a query, set all the things it stores, and ensure it
498      * travels over to the new instance. */
499     {
500         MessageSilencer silencer;
501         const ReturnURI returnURI;
502         QXmlNamePool namePool;
503
504         QBuffer documentDevice;
505         documentDevice.setData(QByteArray("<e>a</e>"));
506         QVERIFY(documentDevice.open(QIODevice::ReadOnly));
507
508         QXmlQuery original(namePool);
509         QXmlName testName(namePool, QLatin1String("somethingToCheck"));
510
511         original.setMessageHandler(&silencer);
512         original.bindVariable(QLatin1String("var"), QXmlItem(1));
513         original.bindVariable(QLatin1String("device"), &documentDevice);
514         original.setUriResolver(&returnURI);
515         original.setFocus(QXmlItem(3));
516         original.setQuery(QLatin1String("$var, 1 + 1, ., string(doc($device))"));
517
518         /* Do a copy, and check that everything followed on into the copy. No modification
519          * of the copy. */
520         {
521             QXmlQuery copy;
522
523             /* We use assignment operator, not copy constructor. */
524             copy = original;
525
526             QVERIFY(copy.isValid());
527             QCOMPARE(copy.uriResolver(), static_cast<const QAbstractUriResolver *>(&returnURI));
528             QCOMPARE(copy.messageHandler(), static_cast<QAbstractMessageHandler *>(&silencer));
529             QCOMPARE(testName.localName(copy.namePool()), QString::fromLatin1("somethingToCheck"));
530
531             QXmlResultItems result;
532             copy.evaluateTo(&result);
533             QCOMPARE(result.next().toAtomicValue(), QVariant(1));
534             QCOMPARE(result.next().toAtomicValue(), QVariant(2));
535             QCOMPARE(result.next().toAtomicValue(), QVariant(3));
536             QCOMPARE(result.next().toAtomicValue(), QVariant(QString::fromLatin1("a")));
537             QVERIFY(result.next().isNull());
538             QVERIFY(!result.hasError());
539         }
540
541         /* Copy, and change values. Things should detach. */
542         {
543             /* Evaluate the copy. */
544             {
545                 MessageSilencer secondSilencer;
546                 const ReturnURI secondUriResolver;
547                 QBuffer documentDeviceCopy;
548                 documentDeviceCopy.setData(QByteArray("<e>b</e>"));
549                 QVERIFY(documentDeviceCopy.open(QIODevice::ReadOnly));
550
551                 QXmlQuery copy;
552                 copy = original;
553
554                 copy.setMessageHandler(&secondSilencer);
555                 /* Here we rebind variable values. */
556                 copy.bindVariable(QLatin1String("var"), QXmlItem(4));
557                 copy.bindVariable(QLatin1String("device"), &documentDeviceCopy);
558                 copy.setUriResolver(&secondUriResolver);
559                 copy.setFocus(QXmlItem(6));
560                 copy.setQuery(QLatin1String("$var, 1 + 1, ., string(doc($device))"));
561
562                 /* Check that the copy picked up the new things. */
563                 QVERIFY(copy.isValid());
564                 QCOMPARE(copy.uriResolver(), static_cast<const QAbstractUriResolver *>(&secondUriResolver));
565                 QCOMPARE(copy.messageHandler(), static_cast<QAbstractMessageHandler *>(&secondSilencer));
566
567                 QXmlResultItems resultCopy;
568                 copy.evaluateTo(&resultCopy);
569                 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(4));
570                 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(2));
571                 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(6));
572                 const QString stringedDevice(resultCopy.next().toAtomicValue().toString());
573                 QCOMPARE(stringedDevice, QString::fromLatin1("b"));
574                 QVERIFY(resultCopy.next().isNull());
575                 QVERIFY(!resultCopy.hasError());
576             }
577
578             /* Evaluate the original. */
579             {
580                 /* Check that the original is unchanged. */
581                 QVERIFY(original.isValid());
582                 QCOMPARE(original.uriResolver(), static_cast<const QAbstractUriResolver *>(&returnURI));
583                 QCOMPARE(original.messageHandler(), static_cast<QAbstractMessageHandler *>(&silencer));
584
585                 QXmlResultItems resultOriginal;
586                 original.evaluateTo(&resultOriginal);
587                 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(1));
588                 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(2));
589                 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(3));
590                 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(QString::fromLatin1("a")));
591                 QVERIFY(resultOriginal.next().isNull());
592                 QVERIFY(!resultOriginal.hasError());
593             }
594         }
595     }
596 }
597
598 /*!
599   Since QXmlQuery doesn't seek devices to position 0, this code triggers a bug
600   where document caching doesn't work. Since the document caching doesn't work,
601   the device will be read twice, and the second time the device is at the end,
602   hence premature end of document.
603  */
604 void tst_QXmlQuery::sequentialExecution() const
605 {
606     QBuffer inBuffer;
607     inBuffer.setData(QByteArray("<input/>"));
608     QVERIFY(inBuffer.open(QIODevice::ReadOnly));
609
610     QXmlQuery query;
611     query.bindVariable("inputDocument", &inBuffer);
612
613     QByteArray outArray;
614     QBuffer outBuffer(&outArray);
615     outBuffer.open(QIODevice::WriteOnly);
616
617     const QString queryString(QLatin1String("doc($inputDocument)"));
618     query.setQuery(queryString);
619
620     QXmlFormatter formatter(query, &outBuffer);
621
622     QVERIFY(query.evaluateTo(&formatter));
623
624     /* If this line is removed, the bug isn't triggered. */
625     query.setQuery(queryString);
626
627     QVERIFY(query.evaluateTo(&formatter));
628 }
629
630 void tst_QXmlQuery::isValid() const
631 {
632     /* Check default value. */
633     QXmlQuery query;
634     QVERIFY(!query.isValid());
635 }
636
637 void tst_QXmlQuery::bindVariableQString() const
638 {
639     {
640         QXmlQuery query;
641         /* Bind with a null QXmlItem. */
642         query.bindVariable(QLatin1String("name"), QXmlItem());
643     }
644
645     {
646         QXmlQuery query;
647         /* Bind with a null QVariant. */
648         query.bindVariable(QLatin1String("name"), QXmlItem(QVariant()));
649     }
650
651     {
652         QXmlQuery query;
653         /* Bind with a null QXmlNodeModelIndex. */
654         query.bindVariable(QLatin1String("name"), QXmlItem(QXmlNodeModelIndex()));
655     }
656 }
657
658 void tst_QXmlQuery::bindVariableQStringNoExternalDeclaration() const
659 {
660     QXmlQuery query;
661     query.bindVariable(QLatin1String("foo"), QXmlItem(QLatin1String("Variable Value")));
662     query.setQuery(QLatin1String("$foo"));
663
664     QVERIFY(query.isValid());
665
666     QStringList result;
667     QVERIFY(query.evaluateTo(&result));
668
669     QCOMPARE(result, QStringList() << QLatin1String("Variable Value"));
670 }
671
672 void tst_QXmlQuery::bindVariableQXmlName() const
673 {
674     // TODO
675 }
676
677 void tst_QXmlQuery::bindVariableQXmlNameTriggerWarnings() const
678 {
679     QXmlQuery query;
680
681     QTest::ignoreMessage(QtWarningMsg, "The variable name cannot be null.");
682     query.bindVariable(QXmlName(), QVariant());
683 }
684
685 void tst_QXmlQuery::bindVariableQStringQIODeviceWithByteArray() const
686 {
687     QXmlQuery query;
688
689     QByteArray in("<e/>");
690     QBuffer device(&in);
691     QVERIFY(device.open(QIODevice::ReadOnly));
692
693     query.bindVariable("doc", &device);
694
695     query.setQuery(QLatin1String("declare variable $doc external; $doc"));
696
697     QVERIFY(query.isValid());
698
699     /* Check the URI corresponding to the variable. */
700     {
701         QXmlResultItems items;
702         query.evaluateTo(&items);
703
704         QCOMPARE(items.next().toAtomicValue().toString(), QString::fromLatin1("tag:trolltech.com,2007:QtXmlPatterns:QIODeviceVariable:doc"));
705     }
706
707     /* Now, actually load the document. We use the same QXmlQuery just to stress recompilation a bit. */
708     {
709         query.setQuery(QLatin1String("declare variable $doc external; doc($doc)"));
710
711         QByteArray out;
712         QBuffer outBuffer(&out);
713         QVERIFY(outBuffer.open(QIODevice::WriteOnly));
714
715         QXmlSerializer serializer(query, &outBuffer);
716
717         QVERIFY(query.evaluateTo(&serializer));
718         QCOMPARE(out, in);
719     }
720 }
721
722 void tst_QXmlQuery::bindVariableQStringQIODeviceWithString() const
723 {
724     QXmlQuery query;
725
726     QString in("<qstring/>");
727     QByteArray inUtf8(in.toUtf8());
728     QBuffer inDevice(&inUtf8);
729
730     QVERIFY(inDevice.open(QIODevice::ReadOnly));
731
732     query.bindVariable("doc", &inDevice);
733
734     query.setQuery(QLatin1String("declare variable $doc external; doc($doc)"));
735
736     QByteArray out;
737     QBuffer outBuffer(&out);
738     QVERIFY(outBuffer.open(QIODevice::WriteOnly));
739
740     QXmlSerializer serializer(query, &outBuffer);
741     QVERIFY(query.evaluateTo(&serializer));
742
743     QCOMPARE(out, inUtf8);
744 }
745
746 void tst_QXmlQuery::bindVariableQStringQIODeviceWithQFile() const
747 {
748     QXmlQuery query;
749     QFile inDevice(QLatin1String(SRCDIR "input.xml"));
750
751     QVERIFY(inDevice.open(QIODevice::ReadOnly));
752
753     query.bindVariable("doc", &inDevice);
754
755     query.setQuery(QLatin1String("declare variable $doc external; doc($doc)"));
756
757     QByteArray out;
758     QBuffer outBuffer(&out);
759     QVERIFY(outBuffer.open(QIODevice::WriteOnly));
760
761     QXmlSerializer serializer(query, &outBuffer);
762     QVERIFY(query.evaluateTo(&serializer));
763     outBuffer.close();
764
765     QCOMPARE(out, QByteArray("<!-- This is just a file for testing. --><input/>"));
766 }
767
768 void tst_QXmlQuery::bindVariableQStringQIODevice() const
769 {
770     QXmlQuery query;
771
772     /* Rebind the variable. */
773     {
774         /* First evaluation. */
775         {
776             QByteArray in1("<e1/>");
777             QBuffer inDevice1(&in1);
778             QVERIFY(inDevice1.open(QIODevice::ReadOnly));
779
780             query.bindVariable("in", &inDevice1);
781             query.setQuery(QLatin1String("doc($in)"));
782
783             QByteArray out1;
784             QBuffer outDevice1(&out1);
785             QVERIFY(outDevice1.open(QIODevice::WriteOnly));
786
787             QXmlSerializer serializer(query, &outDevice1);
788             query.evaluateTo(&serializer);
789             QCOMPARE(out1, in1);
790         }
791
792         /* Second evaluation, rebind variable. */
793         {
794             QByteArray in2("<e2/>");
795             QBuffer inDevice2(&in2);
796             QVERIFY(inDevice2.open(QIODevice::ReadOnly));
797
798             query.bindVariable(QLatin1String("in"), &inDevice2);
799
800             QByteArray out2;
801             QBuffer outDevice2(&out2);
802             QVERIFY(outDevice2.open(QIODevice::WriteOnly));
803
804             QXmlSerializer serializer(query, &outDevice2);
805             QVERIFY(query.evaluateTo(&serializer));
806             QCOMPARE(out2, in2);
807         }
808     }
809
810     // TODO trigger recompilation when setting qiodevices., and qiodevice overwritten by other type, etc.
811 }
812
813 void tst_QXmlQuery::bindVariableQXmlNameQIODevice() const
814 {
815     // TODO
816 }
817
818 void tst_QXmlQuery::bindVariableQXmlNameQIODeviceTriggerWarnings() const
819 {
820     QXmlNamePool np;
821     QXmlQuery query(np);
822
823     QBuffer buffer;
824     QTest::ignoreMessage(QtWarningMsg, "A null, or readable QIODevice must be passed.");
825     query.bindVariable(QXmlName(np, QLatin1String("foo")), &buffer);
826
827     QTest::ignoreMessage(QtWarningMsg, "The variable name cannot be null.");
828     query.bindVariable(QXmlName(), 0);
829 }
830
831 void tst_QXmlQuery::bindVariableXSLTSuccess() const
832 {
833     QXmlQuery stylesheet(QXmlQuery::XSLT20);
834     stylesheet.setInitialTemplateName(QLatin1String("main"));
835
836     stylesheet.bindVariable(QLatin1String("variableNoSelectNoBodyBoundWithBindVariable"),
837                                           QVariant(QLatin1String("MUST NOT SHOW 1")));
838
839     stylesheet.bindVariable(QLatin1String("variableSelectBoundWithBindVariable"),
840                                           QVariant(QLatin1String("MUST NOT SHOW 2")));
841
842     stylesheet.bindVariable(QLatin1String("variableSelectWithTypeIntBoundWithBindVariable"),
843                                           QVariant(QLatin1String("MUST NOT SHOW 3")));
844
845     stylesheet.bindVariable(QLatin1String("paramNoSelectNoBodyBoundWithBindVariable"),
846                                           QVariant(QLatin1String("param1")));
847
848     stylesheet.bindVariable(QLatin1String("paramNoSelectNoBodyBoundWithBindVariableRequired"),
849                                           QVariant(QLatin1String("param1")));
850
851     stylesheet.bindVariable(QLatin1String("paramSelectBoundWithBindVariable"),
852                                           QVariant(QLatin1String("param2")));
853
854     stylesheet.bindVariable(QLatin1String("paramSelectBoundWithBindVariableRequired"),
855                                           QVariant(QLatin1String("param3")));
856
857     stylesheet.bindVariable(QLatin1String("paramSelectWithTypeIntBoundWithBindVariable"),
858                                           QVariant(4));
859
860     stylesheet.bindVariable(QLatin1String("paramSelectWithTypeIntBoundWithBindVariableRequired"),
861                                           QVariant(QLatin1String("param5")));
862
863     stylesheet.setQuery(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/parameters.xsl"))));
864
865     QVERIFY(stylesheet.isValid());
866
867     QBuffer deviceOut;
868     QVERIFY(deviceOut.open(QIODevice::ReadWrite));
869
870     QVERIFY(stylesheet.evaluateTo(&deviceOut));
871
872     const QString result(QString::fromUtf8(deviceOut.data().constData()));
873
874     QCOMPARE(result,
875              QString::fromLatin1("Variables:   variableSelectsDefaultValue variableSelectsDefaultValue2 3 4 "
876                                  "Parameters: param1 param1 param2 param3 4 param5"));
877 }
878
879 void tst_QXmlQuery::bindVariableTemporaryNode() const
880 {
881     /* First we do it with QXmlResultItems staying in scope. */;
882     {
883         QXmlQuery query1;
884         query1.setQuery("<anElement/>");
885
886         QXmlResultItems result1;
887         query1.evaluateTo(&result1);
888
889         QXmlQuery query2(query1);
890         query2.bindVariable("fromQuery1", result1.next());
891         query2.setQuery("$fromQuery1");
892
893         QString output;
894         QVERIFY(query2.evaluateTo(&output));
895
896         QCOMPARE(output, QString::fromLatin1("<anElement/>\n"));
897     }
898
899     /* And now with it deallocating, so its internal DynamicContext pointer is
900      * released. This doesn't work in Qt 4.5 and is ok. */
901     {
902         QXmlQuery query1;
903         query1.setQuery("<anElement/>");
904
905         QXmlQuery query2;
906
907         {
908             QXmlResultItems result1;
909             query1.evaluateTo(&result1);
910
911             query2.bindVariable("fromQuery1", result1.next());
912             query2.setQuery("$fromQuery1");
913         }
914
915         QString output;
916         return; // See comment above.
917         QVERIFY(query2.evaluateTo(&output));
918
919         QCOMPARE(output, QString::fromLatin1("<anElement/>\n"));
920     }
921 }
922
923 void tst_QXmlQuery::messageHandler() const
924 {
925     {
926         /* Check default value. */
927         QXmlQuery query;
928         QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0));
929     }
930 }
931
932 void tst_QXmlQuery::setMessageHandler() const
933 {
934     QXmlQuery query;
935     MessageSilencer silencer;
936     query.setMessageHandler(&silencer);
937     QCOMPARE(static_cast<QAbstractMessageHandler *>(&silencer), query.messageHandler());
938 }
939
940 void tst_QXmlQuery::evaluateToReceiver()
941 {
942     QFETCH(QString, inputQuery);
943
944     /* This query prints a URI specific to the local system. */
945     if(inputQuery == QLatin1String("static-base-uri.xq"))
946         return;
947
948     ++m_pushTestsCount;
949     const QString queryURI(inputFile(QLatin1String(queriesDirectory) + inputQuery));
950     QFile queryFile(queryURI);
951
952     QVERIFY(queryFile.exists());
953     QVERIFY(queryFile.open(QIODevice::ReadOnly));
954
955     QXmlQuery query;
956
957     MessageSilencer receiver;
958     query.setMessageHandler(&receiver);
959     query.setQuery(&queryFile, QUrl::fromLocalFile(queryURI));
960
961     /* We read all the queries, and some of them are invalid. However, we
962      * only want those that compile. */
963     if(!query.isValid())
964         return;
965
966     QString produced;
967     QTextStream stream(&produced, QIODevice::WriteOnly);
968     PushBaseliner push(stream, query.namePool());
969     QVERIFY(push.isValid());
970     query.evaluateTo(&push);
971
972     const QString baselineName(inputFile(QLatin1String(SRCDIR "pushBaselines/") + inputQuery.left(inputQuery.length() - 2) + QString::fromLatin1("ref")));
973     QFile baseline(baselineName);
974
975     if(baseline.exists())
976     {
977         QVERIFY(baseline.open(QIODevice::ReadOnly | QIODevice::Text));
978         const QString stringedBaseline(QString::fromUtf8(baseline.readAll()));
979         QCOMPARE(produced, stringedBaseline);
980     }
981     else
982     {
983         QVERIFY(baseline.open(QIODevice::WriteOnly));
984         /* This is intentionally a warning, don't remove it. Update the baselines instead. */
985         qWarning() << "Generated baseline for:" << baselineName;
986         ++m_generatedBaselines;
987
988         baseline.write(produced.toUtf8());
989     }
990 }
991
992 void tst_QXmlQuery::evaluateToReceiver_data() const
993 {
994     QTest::addColumn<QString>("inputQuery");
995
996     foreach (QString const& query, queries())
997     {
998         /* This outputs a URI specific to the environment, so we can't use it for this
999          * particular test. */
1000         if (query != QLatin1String("staticBaseURI.xq"))
1001             QTest::newRow(query.toUtf8().constData()) << query;
1002     }
1003 }
1004
1005 void tst_QXmlQuery::evaluateToReceiverOnInvalidQuery() const
1006 {
1007     /* Invoke on a default constructed object. */
1008     {
1009         QByteArray out;
1010         QBuffer buffer(&out);
1011         buffer.open(QIODevice::WriteOnly);
1012
1013         QXmlQuery query;
1014         QXmlSerializer serializer(query, &buffer);
1015         QVERIFY(!query.evaluateTo(&serializer));
1016     }
1017
1018     /* Invoke on an invalid query; compile time error. */
1019     {
1020         QByteArray out;
1021         QBuffer buffer(&out);
1022         buffer.open(QIODevice::WriteOnly);
1023         MessageSilencer silencer;
1024
1025         QXmlQuery query;
1026         query.setMessageHandler(&silencer);
1027         query.setQuery(QLatin1String("1 + "));
1028         QXmlSerializer serializer(query, &buffer);
1029         QVERIFY(!query.evaluateTo(&serializer));
1030     }
1031
1032     /* Invoke on an invalid query; runtime error. */
1033     {
1034         QByteArray out;
1035         QBuffer buffer(&out);
1036         buffer.open(QIODevice::WriteOnly);
1037         MessageSilencer silencer;
1038
1039         QXmlQuery query;
1040         query.setMessageHandler(&silencer);
1041         query.setQuery(QLatin1String("error()"));
1042         QXmlSerializer serializer(query, &buffer);
1043         QVERIFY(!query.evaluateTo(&serializer));
1044     }
1045 }
1046
1047 void tst_QXmlQuery::evaluateToQStringTriggerError() const
1048 {
1049     /* Invoke on a default constructed object. */
1050     {
1051         QXmlQuery query;
1052         QString out;
1053         QVERIFY(!query.evaluateTo(&out));
1054     }
1055
1056     /* Invoke on an invalid query; compile time error. */
1057     {
1058         QXmlQuery query;
1059         MessageSilencer silencer;
1060         query.setMessageHandler(&silencer);
1061
1062         query.setQuery(QLatin1String("1 + "));
1063
1064         QString out;
1065         QVERIFY(!query.evaluateTo(&out));
1066     }
1067
1068     /* Invoke on an invalid query; runtime error. */
1069     {
1070         QXmlQuery query;
1071         MessageSilencer silencer;
1072         query.setMessageHandler(&silencer);
1073
1074         query.setQuery(QLatin1String("error()"));
1075
1076         QString out;
1077         QVERIFY(!query.evaluateTo(&out));
1078     }
1079 }
1080
1081 void tst_QXmlQuery::evaluateToQString() const
1082 {
1083     QFETCH(QString, query);
1084     QFETCH(QString, expectedOutput);
1085
1086     QXmlQuery queryInstance;
1087     queryInstance.setQuery(query);
1088     QVERIFY(queryInstance.isValid());
1089
1090     QString result;
1091     QVERIFY(queryInstance.evaluateTo(&result));
1092
1093     QCOMPARE(result, expectedOutput);
1094 }
1095
1096 void tst_QXmlQuery::evaluateToQString_data() const
1097 {
1098     QTest::addColumn<QString>("query");
1099     QTest::addColumn<QString>("expectedOutput");
1100
1101     QTest::newRow("Two atomics")
1102         << QString::fromLatin1("1, 'two'")
1103         << QString::fromLatin1("1 two\n");
1104
1105     QTest::newRow("An element")
1106         << QString::fromLatin1("<e>{1}</e>")
1107         << QString::fromLatin1("<e>1</e>\n");
1108 }
1109
1110 void tst_QXmlQuery::evaluateToQStringSignature() const
1111 {
1112     const QXmlQuery query;
1113
1114     QString output;
1115
1116     /* evaluateTo(QString *) should be a const function. */
1117     query.evaluateTo(&output);
1118 }
1119
1120 void tst_QXmlQuery::evaluateToQAbstractXmlReceiverTriggerWarnings() const
1121 {
1122     QXmlQuery query;
1123
1124     /* We check the return value as well as warning message here. */
1125     QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed.");
1126     QCOMPARE(query.evaluateTo(static_cast<QAbstractXmlReceiver *>(0)),
1127              false);
1128 }
1129
1130 void tst_QXmlQuery::evaluateToQXmlResultItems() const
1131 {
1132     /* Invoke on a default constructed object. */
1133     {
1134         QXmlQuery query;
1135         QXmlResultItems result;
1136         query.evaluateTo(&result);
1137         QVERIFY(result.next().isNull());
1138     }
1139 }
1140
1141 void tst_QXmlQuery::evaluateToQXmlResultItemsTriggerWarnings() const
1142 {
1143     QTest::ignoreMessage(QtWarningMsg, "A null pointer cannot be passed.");
1144     QXmlQuery query;
1145     query.evaluateTo(static_cast<QXmlResultItems *>(0));
1146 }
1147
1148 void tst_QXmlQuery::evaluateToQXmlResultItemsErrorAtEnd() const
1149 {
1150     QXmlQuery query;
1151     MessageSilencer silencer;
1152     query.setMessageHandler(&silencer);
1153     query.setQuery(QLatin1String("1 to 100, fn:error()"));
1154     QVERIFY(query.isValid());
1155
1156     QXmlResultItems it;
1157     query.evaluateTo(&it);
1158
1159     while(!it.next().isNull())
1160     {
1161     }
1162 }
1163
1164 /*!
1165   If baselines were generated, we flag it as a failure such that it gets
1166   attention, and that they are adjusted accordingly.
1167  */
1168 void tst_QXmlQuery::checkGeneratedBaselines() const
1169 {
1170     QCOMPARE(m_generatedBaselines, 0);
1171
1172     /* If this check fails, the auto test setup is misconfigured, or files have
1173      * been added/removed without this number being updated. */
1174     QCOMPARE(m_pushTestsCount, int(ExpectedQueryCount));
1175 }
1176
1177 void tst_QXmlQuery::basicXQueryToQtTypeCheck() const
1178 {
1179     QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("allAtomics.xq"));
1180     QVERIFY(queryFile.open(QIODevice::ReadOnly));
1181
1182     QXmlQuery query;
1183     query.setQuery(&queryFile);
1184     QVERIFY(query.isValid());
1185
1186     QXmlResultItems it;
1187     query.evaluateTo(&it);
1188
1189     QVariantList expectedValues;
1190     expectedValues.append(QString::fromLatin1("xs:untypedAtomic"));
1191     expectedValues.append(QDateTime(QDate(2002, 10, 10), QTime(23, 2, 11), Qt::UTC));
1192     expectedValues.append(QDate(2002, 10, 10));
1193     expectedValues.append(QVariant()); /* We currently doesn't support xs:time through the API. */
1194
1195     expectedValues.append(QVariant()); /* xs:duration */
1196     expectedValues.append(QVariant()); /* xs:dayTimeDuration */
1197     expectedValues.append(QVariant()); /* xs:yearMonthDuration */
1198
1199     if(sizeof(qreal) == sizeof(float)) {//ARM casts to Float not to double
1200         expectedValues.append(QVariant(float(3e3)));     /* xs:float */
1201         expectedValues.append(QVariant(float(4e4)));     /* xs:double */
1202         expectedValues.append(QVariant(float(2)));       /* xs:decimal */
1203     } else {
1204         expectedValues.append(QVariant(double(3e3)));     /* xs:float */
1205         expectedValues.append(QVariant(double(4e4)));     /* xs:double */
1206         expectedValues.append(QVariant(double(2)));       /* xs:decimal */
1207     }
1208
1209     /* xs:integer and its sub-types. */
1210     expectedValues.append(QVariant(qlonglong(16)));
1211     expectedValues.append(QVariant(qlonglong(-6)));
1212     expectedValues.append(QVariant(qlonglong(-4)));
1213     expectedValues.append(QVariant(qlonglong(5)));
1214     expectedValues.append(QVariant(qlonglong(6)));
1215     expectedValues.append(QVariant(qlonglong(7)));
1216     expectedValues.append(QVariant(qlonglong(8)));
1217     expectedValues.append(QVariant(qlonglong(9)));
1218     expectedValues.append(QVariant(qulonglong(10)));
1219     expectedValues.append(QVariant(qlonglong(11)));
1220     expectedValues.append(QVariant(qlonglong(12)));
1221     expectedValues.append(QVariant(qlonglong(13)));
1222     expectedValues.append(QVariant(qlonglong(14)));
1223
1224     expectedValues.append(QVariant());                                                          /* xs:gYearMonth("1976-02"), */
1225     expectedValues.append(QVariant());                                                          /* xs:gYear("2005-12:00"), */
1226     expectedValues.append(QVariant());                                                          /* xs:gMonthDay("--12-25-14:00"), */
1227     expectedValues.append(QVariant());                                                          /* xs:gDay("---25-14:00"), */
1228     expectedValues.append(QVariant());                                                          /* xs:gMonth("--12-14:00"), */
1229     expectedValues.append(true);                                                                /* xs:boolean("true"), */
1230     expectedValues.append(QVariant(QByteArray::fromBase64(QByteArray("aaaa"))));                /* xs:base64Binary("aaaa"), */
1231     expectedValues.append(QVariant(QByteArray::fromHex(QByteArray("FFFF"))));                   /* xs:hexBinary("FFFF"), */
1232     expectedValues.append(QVariant(QString::fromLatin1("http://example.com/")));                /* xs:anyURI("http://example.com/"), */
1233     QXmlNamePool np(query.namePool());
1234     expectedValues.append(QVariant(qVariantFromValue(QXmlName(np, QLatin1String("localName"),
1235                                                               QLatin1String("http://example.com/2"),
1236                                                               QLatin1String("prefix")))));
1237
1238     expectedValues.append(QVariant(QString::fromLatin1("An xs:string")));
1239     expectedValues.append(QVariant(QString::fromLatin1("normalizedString")));
1240     expectedValues.append(QVariant(QString::fromLatin1("token")));
1241     expectedValues.append(QVariant(QString::fromLatin1("language")));
1242     expectedValues.append(QVariant(QString::fromLatin1("NMTOKEN")));
1243     expectedValues.append(QVariant(QString::fromLatin1("Name")));
1244     expectedValues.append(QVariant(QString::fromLatin1("NCName")));
1245     expectedValues.append(QVariant(QString::fromLatin1("ID")));
1246     expectedValues.append(QVariant(QString::fromLatin1("IDREF")));
1247     expectedValues.append(QVariant(QString::fromLatin1("ENTITY")));
1248
1249     int i = 0;
1250     QXmlItem item(it.next());
1251
1252     while(!item.isNull())
1253     {
1254         QVERIFY(item.isAtomicValue());
1255         const QVariant produced(item.toAtomicValue());
1256
1257         const QVariant &expected = expectedValues.at(i);
1258
1259         /* For the cases where we can't represent a value in the XDM with Qt,
1260          * we return an invalid QVariant. */
1261         QCOMPARE(expected.isValid(), produced.isValid());
1262
1263         QCOMPARE(produced.type(), expected.type());
1264
1265         if(expected.isValid())
1266         {
1267             /* This is only needed for xs:decimal though, for some reason. Probably
1268              * just artifacts created somewhere. */
1269             if(produced.type() == QVariant::Double)
1270                 QVERIFY(qFuzzyCompare(produced.toDouble(), expected.toDouble()));
1271             else if(qVariantCanConvert<QXmlName>(produced))
1272             {
1273                 /* QVariant::operator==() does identity comparison, it doesn't delegate to operator==() of
1274                  * the contained type, unless it's hardcoded into QVariant. */
1275                 const QXmlName n1 = qVariantValue<QXmlName>(produced);
1276                 const QXmlName n2 = qVariantValue<QXmlName>(expected);
1277                 QCOMPARE(n1, n2);
1278             }
1279             else
1280                 QCOMPARE(produced, expected);
1281         }
1282
1283         ++i;
1284         item = it.next();
1285     }
1286
1287     QCOMPARE(i, expectedValues.count());
1288 }
1289
1290 /*!
1291   Send values from Qt into XQuery.
1292  */
1293 void tst_QXmlQuery::basicQtToXQueryTypeCheck() const
1294 {
1295     QFile queryFile(QLatin1String(queriesDirectory) + QLatin1String("allAtomicsExternally.xq"));
1296     QVERIFY(queryFile.exists());
1297     QVERIFY(queryFile.open(QIODevice::ReadOnly));
1298
1299     QCOMPARE(QVariant(QDate(1999, 9, 10)).type(), QVariant::Date);
1300
1301     QXmlQuery query;
1302
1303     QXmlNamePool np(query.namePool());
1304
1305     const QXmlName name(np, QLatin1String("localname"),
1306                             QLatin1String("http://example.com"),
1307                             QLatin1String("prefix"));
1308
1309     query.bindVariable(QLatin1String("fromQUrl"), QXmlItem(QUrl(QString::fromLatin1("http://example.com/"))));
1310     query.bindVariable(QLatin1String("fromQByteArray"), QXmlItem(QByteArray("AAAA")));
1311     query.bindVariable(QLatin1String("fromBool"), QXmlItem(bool(true)));
1312     query.bindVariable(QLatin1String("fromQDate"), QXmlItem(QDate(2000, 10, 11)));
1313     // TODO Do with different QDateTime time specs
1314     query.bindVariable(QLatin1String("fromQDateTime"), QXmlItem(QDateTime(QDate(2001, 9, 10), QTime(1, 2, 3))));
1315     query.bindVariable(QLatin1String("fromDouble"), QXmlItem(double(3)));
1316     query.bindVariable(QLatin1String("fromFloat"), QXmlItem(float(4)));
1317     query.bindVariable(QLatin1String("integer"), QXmlItem(5));
1318     query.bindVariable(QLatin1String("fromQString"), QXmlItem(QString::fromLatin1("A QString")));
1319     query.bindVariable(QLatin1String("fromQChar"), QXmlItem(QChar::fromLatin1('C')));
1320
1321     query.bindVariable(QLatin1String("fromIntLiteral"), QXmlItem(QVariant(654)));
1322
1323     {
1324         QVariant ui(uint(5));
1325         QCOMPARE(ui.type(), QVariant::UInt);
1326         query.bindVariable(QLatin1String("fromUInt"), ui);
1327     }
1328
1329     {
1330         QVariant ulnglng(qulonglong(6));
1331         QCOMPARE(ulnglng.type(), QVariant::ULongLong);
1332         query.bindVariable(QLatin1String("fromULongLong"), ulnglng);
1333     }
1334
1335     {
1336         QVariant qlnglng(qlonglong(7));
1337         QCOMPARE(qlnglng.type(), QVariant::LongLong);
1338         query.bindVariable(QLatin1String("fromLongLong"), qlnglng);
1339     }
1340
1341     query.setQuery(&queryFile);
1342
1343     // TODO do queries which declares external variables with types. Tons of combos here.
1344     // TODO ensure that binding with QXmlItem() doesn't make a binding available.
1345     // TODO test rebinding a variable.
1346
1347     QVERIFY(query.isValid());
1348
1349     QXmlResultItems it;
1350     query.evaluateTo(&it);
1351     QXmlItem item(it.next());
1352     QVERIFY(!item.isNull());
1353     QVERIFY(item.isAtomicValue());
1354
1355     if(sizeof(qreal) == sizeof(float)) //ARM casts to Float not to double
1356         QCOMPARE(item.toAtomicValue().toString(),
1357                  QLatin1String("4 true 3 654 7 41414141 C 2000-10-11Z 2001-09-10T01:02:03 "
1358                                "A QString http://example.com/ 5 6 true false false true true true true true true true "
1359                                "true true true"));
1360     else
1361         QCOMPARE(item.toAtomicValue().toString(),
1362                  QLatin1String("4 true 3 654 7 41414141 C 2000-10-11Z 2001-09-10T01:02:03 "
1363                                "A QString http://example.com/ 5 6 true true true true true true true true true true "
1364                                "true true true"));
1365
1366 }
1367
1368 void tst_QXmlQuery::bindNode() const
1369 {
1370     QXmlQuery query;
1371     TestSimpleNodeModel nodeModel(query.namePool());
1372
1373     query.bindVariable(QLatin1String("node"), nodeModel.root());
1374     QByteArray out;
1375     QBuffer buff(&out);
1376     QVERIFY(buff.open(QIODevice::WriteOnly));
1377
1378     query.setQuery(QLatin1String("declare variable $node external; $node"));
1379     QXmlSerializer serializer(query, &buff);
1380
1381     QVERIFY(query.evaluateTo(&serializer));
1382     QCOMPARE(out, QByteArray("<nodeName/>"));
1383 }
1384
1385 /*!
1386   Pass in a relative URI, and make sure it is resolved against the current application directory.
1387  */
1388 void tst_QXmlQuery::relativeBaseURI() const
1389 {
1390     QXmlQuery query;
1391     query.setQuery(QLatin1String("fn:static-base-uri()"), QUrl(QLatin1String("a/relative/uri.weirdExtension")));
1392     QVERIFY(query.isValid());
1393
1394     QByteArray result;
1395     QBuffer buffer(&result);
1396     QVERIFY(buffer.open(QIODevice::ReadWrite));
1397
1398     QXmlSerializer serializer(query, &buffer);
1399     QVERIFY(query.evaluateTo(&serializer));
1400
1401     const QUrl loaded(QUrl::fromEncoded(result));
1402     QUrl appPath(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()));
1403
1404     QVERIFY(loaded.isValid());
1405     QVERIFY(appPath.isValid());
1406     QVERIFY(!loaded.isRelative());
1407     QVERIFY(!appPath.isRelative());
1408
1409     QFileInfo dir(appPath.toLocalFile());
1410     dir.setFile(QString());
1411
1412     /* We can't use QUrl::isParentOf() because it doesn't do what we want it to */
1413     if(!loaded.toLocalFile().startsWith(dir.absoluteFilePath()))
1414         QTextStream(stderr) << "dir.absoluteFilePath():" << dir.absoluteFilePath() << "loaded.toLocalFile():" << loaded.toLocalFile();
1415
1416     checkBaseURI(loaded, dir.absoluteFilePath());
1417 }
1418
1419 void tst_QXmlQuery::emptyBaseURI() const
1420 {
1421     QXmlQuery query;
1422     query.setQuery(QLatin1String("fn:static-base-uri()"), QUrl());
1423     QVERIFY(query.isValid());
1424
1425     QByteArray result;
1426     QBuffer buffer(&result);
1427     QVERIFY(buffer.open(QIODevice::ReadWrite));
1428
1429     QXmlSerializer serializer(query, &buffer);
1430     QVERIFY(query.evaluateTo(&serializer));
1431
1432     const QUrl loaded(QUrl::fromEncoded(result));
1433     QUrl appPath(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()));
1434
1435     QVERIFY(loaded.isValid());
1436     QVERIFY(appPath.isValid());
1437     QVERIFY(!loaded.isRelative());
1438     QVERIFY(!appPath.isRelative());
1439
1440     QFileInfo dir(appPath.toLocalFile());
1441     dir.setFile(QString());
1442
1443     QCOMPARE(loaded, appPath);
1444 }
1445
1446 /*!
1447   Ensure that QDate comes out as QDateTime.
1448  */
1449 void tst_QXmlQuery::roundTripDateWithinQXmlItem() const
1450 {
1451     const QDate date(1999, 9, 10);
1452     QVERIFY(date.isValid());
1453
1454     const QVariant variant(date);
1455     QVERIFY(variant.isValid());
1456     QCOMPARE(variant.type(), QVariant::Date);
1457
1458     const QXmlItem item(variant);
1459     QVERIFY(!item.isNull());
1460     QVERIFY(item.isAtomicValue());
1461
1462     const QVariant out(item.toAtomicValue());
1463     QVERIFY(out.isValid());
1464     QCOMPARE(out.type(), QVariant::Date);
1465     QCOMPARE(out.toDate(), date);
1466 }
1467
1468 /*!
1469  Check whether a query is valid, which uses an unbound variable.
1470  */
1471 void tst_QXmlQuery::bindingMissing() const
1472 {
1473     QXmlQuery query;
1474     MessageSilencer messageHandler;
1475     query.setMessageHandler(&messageHandler);
1476
1477     QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1478     QVERIFY(queryFile.open(QIODevice::ReadOnly));
1479     query.setQuery(&queryFile);
1480
1481     QVERIFY(!query.isValid());
1482 }
1483
1484 void tst_QXmlQuery::bindDefaultConstructedItem() const
1485 {
1486     QFETCH(QXmlItem, item);
1487
1488     QXmlQuery query;
1489     MessageSilencer messageHandler;
1490     query.setMessageHandler(&messageHandler);
1491
1492     QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1493     QVERIFY(queryFile.open(QIODevice::ReadOnly));
1494     query.setQuery(&queryFile);
1495     query.bindVariable(QLatin1String("externalVariable"), item);
1496
1497     QVERIFY(!query.isValid());
1498 }
1499
1500 void tst_QXmlQuery::bindDefaultConstructedItem_data() const
1501 {
1502     QTest::addColumn<QXmlItem>("item");
1503
1504     QTest::newRow("QXmlItem()") << QXmlItem();
1505     QTest::newRow("QXmlItem(QVariant())") << QXmlItem(QVariant());
1506     QTest::newRow("QXmlItem(QXmlNodeModelIndex())") << QXmlItem(QXmlNodeModelIndex());
1507 }
1508
1509 /*!
1510   Remove a binding by binding QXmlItem() with the same name.
1511  */
1512 void tst_QXmlQuery::eraseQXmlItemBinding() const
1513 {
1514     QXmlQuery query;
1515     MessageSilencer messageHandler;
1516     query.setMessageHandler(&messageHandler);
1517
1518     QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1519     QVERIFY(queryFile.open(QIODevice::ReadOnly));
1520     query.bindVariable(QLatin1String("externalVariable"), QXmlItem(3));
1521     query.setQuery(&queryFile);
1522     QVERIFY(query.isValid());
1523
1524     QByteArray result;
1525     QBuffer buffer(&result);
1526     QVERIFY(buffer.open(QIODevice::ReadWrite));
1527
1528     QXmlSerializer serializer(query, &buffer);
1529     QVERIFY(query.evaluateTo(&serializer));
1530
1531     QCOMPARE(result, QByteArray("3 6<e>3</e>false"));
1532
1533     query.bindVariable(QLatin1String("externalVariable"), QXmlItem());
1534     QVERIFY(!query.isValid());
1535 }
1536
1537 /*!
1538  Erase a variable binding
1539  */
1540 void tst_QXmlQuery::eraseDeviceBinding() const
1541 {
1542     /* Erase an existing QIODevice binding with another QIODevice binding. */
1543     {
1544         QXmlQuery query;
1545
1546         QByteArray doc("<e/>");
1547         QBuffer buffer(&doc);
1548         QVERIFY(buffer.open(QIODevice::ReadOnly));
1549
1550         query.bindVariable(QLatin1String("in"), &buffer);
1551         query.setQuery(QLatin1String("$in"));
1552         QVERIFY(query.isValid());
1553
1554         query.bindVariable(QLatin1String("in"), 0);
1555         QVERIFY(!query.isValid());
1556     }
1557
1558     /* Erase an existing QXmlItem binding with another QIODevice binding. */
1559     {
1560         QXmlQuery query;
1561
1562         query.bindVariable(QLatin1String("in"), QXmlItem(5));
1563         query.setQuery(QLatin1String("$in"));
1564         QVERIFY(query.isValid());
1565
1566         query.bindVariable(QLatin1String("in"), 0);
1567         QVERIFY(!query.isValid());
1568     }
1569 }
1570
1571 /*!
1572  Bind a variable, evaluate, bind with a different value but same type, and evaluate again.
1573  */
1574 void tst_QXmlQuery::rebindVariableSameType() const
1575 {
1576     QXmlQuery query;
1577     MessageSilencer messageHandler;
1578     query.setMessageHandler(&messageHandler);
1579
1580     query.bindVariable(QLatin1String("externalVariable"), QXmlItem(3));
1581
1582     {
1583         QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1584         QVERIFY(queryFile.open(QIODevice::ReadOnly));
1585         query.setQuery(&queryFile);
1586     }
1587
1588     QVERIFY(query.isValid());
1589
1590     {
1591         QByteArray result;
1592         QBuffer buffer(&result);
1593         QVERIFY(buffer.open(QIODevice::ReadWrite));
1594
1595         QXmlSerializer serializer(query, &buffer);
1596         QVERIFY(query.evaluateTo(&serializer));
1597
1598         QCOMPARE(result, QByteArray("3 6<e>3</e>false"));
1599     }
1600
1601     {
1602         query.bindVariable(QLatin1String("externalVariable"), QXmlItem(5));
1603         QByteArray result;
1604         QBuffer buffer(&result);
1605         QVERIFY(buffer.open(QIODevice::ReadWrite));
1606
1607         QXmlSerializer serializer(query, &buffer);
1608         QVERIFY(query.evaluateTo(&serializer));
1609
1610         QCOMPARE(result, QByteArray("5 8<e>5</e>false"));
1611     }
1612
1613 }
1614
1615 void tst_QXmlQuery::rebindVariableDifferentType() const
1616 {
1617     /* Rebind QXmlItem variable with QXmlItem variable. */
1618     {
1619         QXmlQuery query;
1620         query.bindVariable(QLatin1String("in"), QXmlItem(3));
1621         query.setQuery(QLatin1String("$in"));
1622         QVERIFY(query.isValid());
1623
1624         query.bindVariable(QLatin1String("in"), QXmlItem("A string"));
1625         QVERIFY(!query.isValid());
1626     }
1627
1628     /* Rebind QIODevice variable with QXmlItem variable. */
1629     {
1630         QXmlQuery query;
1631         QBuffer buffer;
1632         buffer.setData(QByteArray("<e/>"));
1633         QVERIFY(buffer.open(QIODevice::ReadOnly));
1634
1635         query.bindVariable(QLatin1String("in"), &buffer);
1636         query.setQuery(QLatin1String("$in"));
1637         QVERIFY(query.isValid());
1638
1639         query.bindVariable(QLatin1String("in"), QXmlItem("A string"));
1640         QVERIFY(!query.isValid());
1641     }
1642
1643     /* Rebind QXmlItem variable with QIODevice variable. The type of the
1644      * variable changes, so a recompile is necessary. */
1645     {
1646         QXmlQuery query;
1647
1648         query.bindVariable(QLatin1String("in"), QXmlItem(QLatin1String("A string")));
1649         query.setQuery(QLatin1String("$in"));
1650         QVERIFY(query.isValid());
1651
1652         QBuffer buffer;
1653         buffer.setData(QByteArray("<e/>"));
1654         QVERIFY(buffer.open(QIODevice::ReadOnly));
1655         query.bindVariable(QLatin1String("in"), &buffer);
1656         QVERIFY(!query.isValid());
1657     }
1658 }
1659
1660 void tst_QXmlQuery::rebindVariableWithNullItem() const
1661 {
1662     QXmlQuery query;
1663
1664     query.bindVariable(QLatin1String("name"), QXmlItem(5));
1665     query.bindVariable(QLatin1String("name"), QXmlItem());
1666 }
1667
1668 void tst_QXmlQuery::constCorrectness() const
1669 {
1670     QXmlResultItems result;
1671     QXmlQuery tmp;
1672     tmp.setQuery(QLatin1String("1")); /* Just so we have a valid query. */
1673     const QXmlQuery query(tmp);
1674
1675     /* These functions should be const. */
1676     query.isValid();
1677     query.evaluateTo(&result);
1678     query.namePool();
1679     query.uriResolver();
1680     query.messageHandler();
1681
1682     {
1683         QString dummyString;
1684         QTextStream dummyStream(&dummyString);
1685         PushBaseliner dummy(dummyStream, query.namePool());
1686         QVERIFY(dummy.isValid());
1687         query.evaluateTo(&dummy);
1688     }
1689 }
1690
1691 void tst_QXmlQuery::objectSize() const
1692 {
1693     /* We have a d pointer. */
1694     QCOMPARE(sizeof(QXmlQuery), sizeof(void *));
1695 }
1696
1697 void tst_QXmlQuery::setUriResolver() const
1698 {
1699     /* Set a null resolver, and make sure it can take a const pointer. */
1700     {
1701         QXmlQuery query;
1702         query.setUriResolver(static_cast<const QAbstractUriResolver *>(0));
1703         QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0));
1704     }
1705
1706     {
1707         TestURIResolver resolver;
1708         QXmlQuery query;
1709         query.setUriResolver(&resolver);
1710         QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(&resolver));
1711     }
1712 }
1713
1714 void tst_QXmlQuery::uriResolver() const
1715 {
1716     /* Check default value. */
1717     {
1718         QXmlQuery query;
1719         QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0));
1720     }
1721 }
1722
1723 void tst_QXmlQuery::messageXML() const
1724 {
1725     QXmlQuery query;
1726
1727     MessageValidator messageValidator;
1728     query.setMessageHandler(&messageValidator);
1729
1730     query.setQuery(QLatin1String("1basicSyntaxError"));
1731
1732     const QRegExp removeFilename(QLatin1String("Location: file:.*\\#"));
1733     QVERIFY(removeFilename.isValid());
1734
1735     QVERIFY(messageValidator.success());
1736     QCOMPARE(messageValidator.received().remove(removeFilename),
1737              QString::fromLatin1("Type:3\n"
1738                                  "Description: <html xmlns='http://www.w3.org/1999/xhtml/'><body><p>syntax error, unexpected unknown keyword</p></body></html>\n"
1739                                  "Identifier: http://www.w3.org/2005/xqt-errors#XPST0003\n"
1740                                  "1,1"));
1741 }
1742
1743 /*!
1744   1. Allocate QXmlResultItems
1745   2. Allocate QXmlQuery
1746   3. evaluate to the QXmlResultItems instance
1747   4. Dellocate the QXmlQuery instance
1748   5. Ensure QXmlResultItems works
1749  */
1750 void tst_QXmlQuery::resultItemsDeallocatedQuery() const
1751 {
1752     QXmlResultItems result;
1753
1754     {
1755         QXmlQuery query;
1756         query.setQuery(QLatin1String("1, 2, xs:integer(<e>3</e>)"));
1757         query.evaluateTo(&result);
1758     }
1759
1760     QCOMPARE(result.next().toAtomicValue(), QVariant(1));
1761     QCOMPARE(result.next().toAtomicValue(), QVariant(2));
1762     QCOMPARE(result.next().toAtomicValue(), QVariant(3));
1763     QVERIFY(result.next().isNull());
1764     QVERIFY(!result.hasError());
1765 }
1766
1767 /*!
1768   1. Bind variable with bindVariable()
1769   2. setQuery that has 'declare variable' with same name.
1770   3. Ensure the value inside the query is used. We don't guarantee this behavior
1771      but that's what we lock.
1772  */
1773 void tst_QXmlQuery::shadowedVariables() const
1774 {
1775     QXmlQuery query;
1776     query.bindVariable("varName", QXmlItem(3));
1777     query.setQuery(QLatin1String("declare variable $varName := 5; $varName"));
1778
1779     QXmlResultItems result;
1780     query.evaluateTo(&result);
1781
1782     QCOMPARE(result.next().toAtomicValue(), QVariant(5));
1783 }
1784
1785 void tst_QXmlQuery::setFocusQXmlItem() const
1786 {
1787     /* Make sure we can take a const reference. */
1788     {
1789         QXmlQuery query;
1790         const QXmlItem item;
1791         query.setFocus(item);
1792     }
1793
1794     // TODO evaluate with atomic value, check type
1795     // TODO evaluate with node, check type
1796     // TODO ensure that setFocus() triggers query recompilation, as appropriate.
1797     // TODO let the focus be undefined, call isvalid, call evaluate anyway
1798     // TODO let the focus be undefined, call evaluate directly
1799 }
1800
1801 void tst_QXmlQuery::setFocusQUrl() const
1802 {
1803     /* Load a focus which isn't well-formed. */
1804     {
1805         QXmlQuery query;
1806         MessageSilencer silencer;
1807
1808         query.setMessageHandler(&silencer);
1809
1810         QVERIFY(!query.setFocus(QUrl(QLatin1String("data/notWellformed.xml"))));
1811     }
1812
1813     /* Ensure the same URI resolver is used. */
1814     {
1815         QXmlQuery query(QXmlQuery::XSLT20);
1816
1817         const TestURIResolver resolver(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
1818         query.setUriResolver(&resolver);
1819
1820         QVERIFY(query.setFocus(QUrl(QLatin1String("arbitraryURI"))));
1821         query.setQuery(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/copyWholeDocument.xsl"))));
1822         QVERIFY(query.isValid());
1823
1824         QBuffer result;
1825         QVERIFY(result.open(QIODevice::ReadWrite));
1826         QXmlSerializer serializer(query, &result);
1827         query.evaluateTo(&serializer);
1828
1829         QCOMPARE(result.data(), QByteArray("<doc/>"));
1830     }
1831
1832     // TODO ensure that the focus type doesn't change from XSLT20 on the main instance.
1833 }
1834
1835 /*!
1836   This code poses a challenge wrt. to internal caching.
1837  */
1838 void tst_QXmlQuery::setFocusQIODevice() const
1839 {
1840     QXmlQuery query;
1841
1842     {
1843         QBuffer focus;
1844         focus.setData(QByteArray("<e>abc</e>"));
1845         QVERIFY(focus.open(QIODevice::ReadOnly));
1846         query.setFocus(&focus);
1847         query.setQuery(QLatin1String("string()"));
1848         QVERIFY(query.isValid());
1849
1850         QString output;
1851         query.evaluateTo(&output);
1852
1853         QCOMPARE(output, QString::fromLatin1("abc\n"));
1854     }
1855
1856     /* Set a new focus, make sure it changes & works. */
1857     {
1858         QBuffer focus2;
1859         focus2.setData(QByteArray("<e>abc2</e>"));
1860         QVERIFY(focus2.open(QIODevice::ReadOnly));
1861         query.setFocus(&focus2);
1862         QVERIFY(query.isValid());
1863
1864         QString output;
1865         query.evaluateTo(&output);
1866
1867         QCOMPARE(output, QString::fromLatin1("abc2\n"));
1868     }
1869 }
1870
1871 /*!
1872  Since we internally use variable bindings for implementing the focus, we need
1873  to make sure we don't clash in this area.
1874 */
1875 void tst_QXmlQuery::setFocusQIODeviceAvoidVariableClash() const
1876 {
1877     QBuffer buffer;
1878     buffer.setData("<e>focus</e>");
1879     QVERIFY(buffer.open(QIODevice::ReadOnly));
1880
1881     /* First we bind the variable name, then the focus. */
1882     {
1883         QXmlQuery query;
1884         query.bindVariable(QString(QLatin1Char('u')), QVariant(1));
1885         query.setFocus(&buffer);
1886         query.setQuery(QLatin1String("string()"));
1887
1888         QString out;
1889         query.evaluateTo(&out);
1890
1891         QCOMPARE(out, QString::fromLatin1("focus\n"));
1892     }
1893
1894     /* First we bind the focus, then the variable name. */
1895     {
1896         QXmlQuery query;
1897         QVERIFY(buffer.open(QIODevice::ReadOnly));
1898         query.setFocus(&buffer);
1899         query.bindVariable(QString(QLatin1Char('u')), QVariant(1));
1900         query.setQuery(QLatin1String("string()"));
1901
1902         QString out;
1903         query.evaluateTo(&out);
1904
1905         QCOMPARE(out, QString::fromLatin1("focus\n"));
1906     }
1907 }
1908
1909 void tst_QXmlQuery::setFocusQIODeviceFailure() const
1910 {
1911     /* A not well-formed input document. */
1912     {
1913         QXmlQuery query;
1914
1915         MessageSilencer silencer;
1916         query.setMessageHandler(&silencer);
1917
1918         QBuffer input;
1919         input.setData("<e");
1920         QVERIFY(input.open(QIODevice::ReadOnly));
1921
1922         QCOMPARE(query.setFocus(&input), false);
1923     }
1924 }
1925
1926 void tst_QXmlQuery::setFocusQString() const
1927 {
1928     QXmlQuery query;
1929
1930     /* Basic use of focus. */
1931     {
1932         QVERIFY(query.setFocus(QLatin1String("<e>textNode</e>")));
1933         query.setQuery(QLatin1String("string()"));
1934         QVERIFY(query.isValid());
1935         QString out;
1936         query.evaluateTo(&out);
1937         QCOMPARE(out, QString::fromLatin1("textNode\n"));
1938     }
1939
1940     /* Set to a new focus, make sure it changes and works. */
1941     {
1942         QVERIFY(query.setFocus(QLatin1String("<e>newFocus</e>")));
1943         QString out;
1944         query.evaluateTo(&out);
1945         QCOMPARE(out, QString::fromLatin1("newFocus\n"));
1946     }
1947 }
1948
1949 void tst_QXmlQuery::setFocusQStringFailure() const
1950 {
1951     QXmlQuery query;
1952     MessageSilencer silencer;
1953
1954     query.setMessageHandler(&silencer);
1955     QVERIFY(!query.setFocus(QLatin1String("<notWellformed")));
1956
1957     /* Let's try the slight special case of a null string. */
1958     QVERIFY(!query.setFocus(QString()));
1959 }
1960
1961 void tst_QXmlQuery::setFocusQStringSignature() const
1962 {
1963     QXmlQuery query;
1964     MessageSilencer silencer;
1965     query.setMessageHandler(&silencer);
1966
1967     const QString argument;
1968     /* We should take a const ref. */
1969     query.setFocus(argument);
1970
1971     /* We should return a bool. */
1972     static_cast<bool>(query.setFocus(QString()));
1973 }
1974
1975 void tst_QXmlQuery::setFocusQIODeviceTriggerWarnings() const
1976 {
1977     /* A null pointer. */
1978     {
1979         QXmlQuery query;
1980
1981         QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed.");
1982         QCOMPARE(query.setFocus(static_cast<QIODevice *>(0)), false);
1983     }
1984
1985     /* A non opened-device. */
1986     {
1987         QXmlQuery query;
1988
1989         QBuffer notReadable;
1990         QVERIFY(!notReadable.isReadable());
1991
1992         QTest::ignoreMessage(QtWarningMsg, "The device must be readable.");
1993         QCOMPARE(query.setFocus(&notReadable), false);
1994     }
1995 }
1996
1997 void tst_QXmlQuery::fnDocNetworkAccessSuccess() const
1998 {
1999 #if defined(Q_OS_WINCE) && !defined(_X86_)
2000     QStringList testsToSkip;
2001     testsToSkip << "http scheme" << "ftp scheme";
2002     if (testsToSkip.contains(QTest::currentDataTag()))
2003         QSKIP("Network tests are currently unsupported on Windows CE.", SkipSingle);
2004 #endif
2005
2006     QFETCH(QUrl, uriToOpen);
2007     QFETCH(QByteArray, expectedOutput);
2008
2009     if(!uriToOpen.isValid())
2010         qDebug() << "uriToOpen:" << uriToOpen;
2011
2012     QVERIFY(uriToOpen.isValid());
2013
2014     QXmlQuery query;
2015     query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen));
2016     query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)"));
2017     QVERIFY(query.isValid());
2018
2019     QByteArray result;
2020     QBuffer buffer(&result);
2021     QVERIFY(buffer.open(QIODevice::WriteOnly));
2022
2023     QXmlSerializer serializer(query, &buffer);
2024     QVERIFY(query.evaluateTo(&serializer));
2025
2026     QCOMPARE(result, expectedOutput);
2027 }
2028
2029 void tst_QXmlQuery::fnDocNetworkAccessSuccess_data() const
2030 {
2031     QTest::addColumn<QUrl>("uriToOpen");
2032     QTest::addColumn<QByteArray>("expectedOutput");
2033
2034     QTest::newRow("file scheme")
2035         << inputFileAsURI(QLatin1String(SRCDIR "input.xml"))
2036         << QByteArray("<!-- This is just a file for testing. --><input/>");
2037
2038     QTest::newRow("data scheme with ASCII")
2039         /* QUrl::toPercentEncoding(QLatin1String("<e/>")) yields "%3Ce%2F%3E". */
2040         << QUrl::fromEncoded("data:application/xml,%3Ce%2F%3E")
2041         << QByteArray("<e/>");
2042
2043     QTest::newRow("data scheme with ASCII no MIME type")
2044         << QUrl::fromEncoded("data:,%3Ce%2F%3E")
2045         << QByteArray("<e/>");
2046
2047     QTest::newRow("data scheme with base 64")
2048         << QUrl::fromEncoded("data:application/xml;base64,PGUvPg==")
2049         << QByteArray("<e/>");
2050
2051     QTest::newRow("qrc scheme")
2052         << QUrl::fromEncoded("qrc:/QXmlQueryTestData/data/oneElement.xml")
2053         << QByteArray("<oneElement/>");
2054
2055     if(!m_testNetwork)
2056         return;
2057
2058     QTest::newRow("http scheme")
2059         << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/wellFormed.xml"))
2060         << QByteArray("<!-- a comment --><e from=\"http\">Some Text</e>");
2061
2062     QTest::newRow("ftp scheme")
2063         << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/wellFormed.xml"))
2064         << QByteArray("<!-- a comment --><e from=\"ftp\">Some Text</e>");
2065
2066 }
2067
2068 void tst_QXmlQuery::fnDocNetworkAccessFailure() const
2069 {
2070     QFETCH(QUrl, uriToOpen);
2071
2072     QVERIFY(uriToOpen.isValid());
2073
2074     QXmlQuery query;
2075     MessageSilencer silencer;
2076     query.setMessageHandler(&silencer);
2077     query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen));
2078     query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)"));
2079     QVERIFY(query.isValid());
2080
2081     QXmlResultItems result;
2082     query.evaluateTo(&result);
2083
2084     while(!result.next().isNull())
2085     {
2086         /* Just loop until the end. */
2087     }
2088
2089     // TODO do something that triggers a /timeout/.
2090     QVERIFY(result.hasError());
2091 }
2092
2093 void tst_QXmlQuery::fnDocNetworkAccessFailure_data() const
2094 {
2095     QTest::addColumn<QUrl>("uriToOpen");
2096
2097     QTest::newRow("data scheme, not-well-formed")
2098         << QUrl(QLatin1String("data:application/xml;base64,PGUvg==="));
2099
2100     QTest::newRow("file scheme, non-existant file")
2101         << QUrl(QLatin1String("file:///example.com/does/notExist.xml"));
2102
2103     QTest::newRow("http scheme, file not found")
2104         << QUrl(QLatin1String("http://www.example.com/does/not/exist.xml"));
2105
2106     QTest::newRow("http scheme, nonexistent host")
2107         << QUrl(QLatin1String("http://this.host.does.not.exist.I.SWear"));
2108
2109     QTest::newRow("qrc scheme, not well-formed")
2110         << QUrl(QLatin1String("qrc:/QXmlQueryTestData/notWellformed.xml"));
2111
2112     QTest::newRow("'qrc:/', non-existing file")
2113         << QUrl(QLatin1String(":/QXmlQueryTestData/data/thisFileDoesNotExist.xml"));
2114
2115     QTest::newRow("':/', this scheme is not supported")
2116         << QUrl(QLatin1String(":/QXmlQueryTestData/data/notWellformed.xml"));
2117
2118     if(!m_testNetwork)
2119         return;
2120
2121     QTest::newRow("http scheme, not well-formed")
2122         << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/notWellformed.xml"));
2123
2124     QTest::newRow("https scheme, not well-formed")
2125         << QUrl(QString("https://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/notWellformedViaHttps.xml"));
2126
2127     QTest::newRow("https scheme, nonexistent host")
2128         << QUrl(QLatin1String("https://this.host.does.not.exist.I.SWear"));
2129
2130     QTest::newRow("ftp scheme, nonexistent host")
2131         << QUrl(QLatin1String("ftp://this.host.does.not.exist.I.SWear"));
2132
2133     QTest::newRow("ftp scheme, not well-formed")
2134         << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/notWellFormed.xml"));
2135 }
2136
2137 /*!
2138   Create a network timeout from a QIODevice binding such
2139   that we ensure we don't hang infinitely.
2140  */
2141 void tst_QXmlQuery::fnDocOnQIODeviceTimeout() const
2142 {
2143     if(!m_testNetwork)
2144         return;
2145
2146     QTcpServer server;
2147     server.listen(QHostAddress::LocalHost, 1088);
2148
2149     QTcpSocket client;
2150     client.connectToHost("localhost", 1088);
2151     QVERIFY(client.isReadable());
2152
2153     QXmlQuery query;
2154
2155     MessageSilencer silencer;
2156     query.setMessageHandler(&silencer);
2157
2158     query.bindVariable(QLatin1String("inDevice"), &client);
2159     query.setQuery(QLatin1String("declare variable $inDevice external;\ndoc($inDevice)"));
2160     QVERIFY(query.isValid());
2161
2162     QXmlResultItems result;
2163     query.evaluateTo(&result);
2164     QXmlItem next(result.next());
2165
2166     while(!next.isNull())
2167     {
2168         next = result.next();
2169     }
2170
2171     QVERIFY(result.hasError());
2172 }
2173
2174 /*!
2175  When changing query, the static context must change too, such that
2176  the source locations are updated.
2177  */
2178 void tst_QXmlQuery::recompilationWithEvaluateToResultFailing() const
2179 {
2180     QXmlQuery query;
2181     MessageSilencer silencer;
2182     query.setMessageHandler(&silencer);
2183
2184     query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2185     QVERIFY(query.isValid()); /* Trigger query compilation. */
2186
2187     query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2188     QVERIFY(query.isValid()); /* Trigger second compilation. */
2189
2190     QXmlResultItems items;
2191     query.evaluateTo(&items);
2192     items.next();
2193     QVERIFY(items.hasError());
2194 }
2195
2196 void tst_QXmlQuery::secondEvaluationWithEvaluateToResultFailing() const
2197 {
2198     QXmlQuery query;
2199     MessageSilencer silencer;
2200     query.setMessageHandler(&silencer);
2201
2202     query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2203     QVERIFY(query.isValid()); /* Trigger query compilation. */
2204
2205     query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2206     /* We don't call isValid(). */
2207 QXmlResultItems items;
2208     query.evaluateTo(&items);
2209     items.next();
2210     QVERIFY(items.hasError());
2211 }
2212
2213 /*!
2214  Compilation is triggered in the evaluation function due to no call to QXmlQuery::isValid().
2215  */
2216 void tst_QXmlQuery::recompilationWithEvaluateToReceiver() const
2217 {
2218     QXmlQuery query;
2219     MessageSilencer silencer;
2220     query.setMessageHandler(&silencer);
2221
2222     query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2223     QVERIFY(query.isValid()); /* Trigger query compilation. */
2224
2225     query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2226     /* We don't call isValid(). */
2227
2228     QByteArray dummy;
2229     QBuffer buffer(&dummy);
2230     buffer.open(QIODevice::WriteOnly);
2231
2232     QXmlSerializer serializer(query, &buffer);
2233
2234     QVERIFY(!query.evaluateTo(&serializer));
2235 }
2236
2237 void tst_QXmlQuery::evaluateToQStringListOnInvalidQuery() const
2238 {
2239     MessageSilencer silencer;
2240
2241     /* Invoke on a default constructed object. */
2242     {
2243         QXmlQuery query;
2244         QStringList out;
2245         QVERIFY(!query.evaluateTo(&out));
2246     }
2247
2248     /* Invoke on a syntactically invalid query. */
2249     {
2250         QXmlQuery query;
2251         QStringList out;
2252         MessageSilencer silencer;
2253
2254         query.setMessageHandler(&silencer);
2255         query.setQuery(QLatin1String("1 + "));
2256
2257         QVERIFY(!query.evaluateTo(&out));
2258     }
2259
2260     /* Invoke on a query with the wrong type, one atomic. */
2261     {
2262         QXmlQuery query;
2263         QStringList out;
2264
2265         query.setQuery(QLatin1String("1"));
2266         query.setMessageHandler(&silencer);
2267         QVERIFY(!query.evaluateTo(&out));
2268     }
2269
2270     /* Invoke on a query with the wrong type, one element. */
2271     {
2272         QXmlQuery query;
2273         QStringList out;
2274
2275         query.setQuery(QLatin1String("<e/>"));
2276         QVERIFY(!query.evaluateTo(&out));
2277     }
2278
2279     /* Invoke on a query with the wrong type, mixed nodes & atomics. */
2280     {
2281         QXmlQuery query;
2282         QStringList out;
2283
2284         query.setQuery(QLatin1String("<e/>, 1, 'a string'"));
2285         query.setMessageHandler(&silencer);
2286         QVERIFY(!query.evaluateTo(&out));
2287     }
2288
2289     /* Evaluate the empty sequence. */
2290     {
2291         QXmlQuery query;
2292         QStringList out;
2293
2294         query.setQuery(QLatin1String("()"));
2295         QVERIFY(!query.evaluateTo(&out));
2296         QVERIFY(out.isEmpty());
2297     }
2298 }
2299
2300 void tst_QXmlQuery::evaluateToQStringList() const
2301 {
2302     QFETCH(QString, queryString);
2303     QFETCH(QStringList, expectedOutput);
2304
2305     QXmlQuery query;
2306     query.setQuery(queryString);
2307     QStringList out;
2308     QVERIFY(query.isValid());
2309
2310     QVERIFY(query.evaluateTo(&out));
2311
2312     QCOMPARE(out, expectedOutput);
2313 }
2314
2315 void tst_QXmlQuery::evaluateToQStringListTriggerWarnings() const
2316 {
2317     QXmlQuery query;
2318
2319     QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed.");
2320     QCOMPARE(query.evaluateTo(static_cast<QStringList *>(0)),
2321              false);
2322 }
2323
2324 void tst_QXmlQuery::evaluateToQStringList_data() const
2325 {
2326     QTest::addColumn<QString>("queryString");
2327     QTest::addColumn<QStringList>("expectedOutput");
2328
2329     QTest::newRow("One atomic")
2330         << QString::fromLatin1("(1 + 1) cast as xs:string")
2331         << QStringList(QString::fromLatin1("2"));
2332
2333     {
2334         QStringList expected;
2335         expected << QLatin1String("2");
2336         expected << QLatin1String("a string");
2337
2338         QTest::newRow("Two atomics")
2339             << QString::fromLatin1("(1 + 1) cast as xs:string, 'a string'")
2340             << expected;
2341     }
2342
2343     QTest::newRow("A query which evaluates to sub-types of xs:string.")
2344         << QString::fromLatin1("xs:NCName('NCName'), xs:normalizedString('  a b c   ')")
2345         << (QStringList() << QString::fromLatin1("NCName")
2346                           << QString::fromLatin1("  a b c   "));
2347
2348     QTest::newRow("A query which evaluates to two elements.")
2349         << QString::fromLatin1("string(<e>theString1</e>), string(<e>theString2</e>)")
2350         << (QStringList() << QString::fromLatin1("theString1")
2351                           << QString::fromLatin1("theString2"));
2352 }
2353
2354 /*!
2355   Ensure that we don't automatically convert non-xs:string values.
2356  */
2357 void tst_QXmlQuery::evaluateToQStringListNoConversion() const
2358 {
2359     QXmlQuery query;
2360     query.setQuery(QString::fromLatin1("<e/>"));
2361     QVERIFY(query.isValid());
2362     QStringList result;
2363     QVERIFY(!query.evaluateTo(&result));
2364 }
2365
2366 void tst_QXmlQuery::evaluateToQIODevice() const
2367 {
2368     /* an XQuery, check that no indentation is performed. */
2369     {
2370         QBuffer out;
2371         QVERIFY(out.open(QIODevice::ReadWrite));
2372
2373         QXmlQuery query;
2374         query.setQuery(QLatin1String("<a><b/></a>"));
2375         QVERIFY(query.isValid());
2376         QVERIFY(query.evaluateTo(&out));
2377         QCOMPARE(out.data(), QByteArray("<a><b/></a>"));
2378     }
2379 }
2380
2381 void tst_QXmlQuery::evaluateToQIODeviceTriggerWarnings() const
2382 {
2383     QXmlQuery query;
2384
2385     QTest::ignoreMessage(QtWarningMsg, "The pointer to the device cannot be null.");
2386     QCOMPARE(query.evaluateTo(static_cast<QIODevice *>(0)),
2387              false);
2388
2389     QBuffer buffer;
2390
2391     QTest::ignoreMessage(QtWarningMsg, "The device must be writable.");
2392     QCOMPARE(query.evaluateTo(&buffer),
2393              false);
2394 }
2395
2396 void tst_QXmlQuery::evaluateToQIODeviceSignature() const
2397 {
2398     /* The function should be const. */
2399     {
2400         QBuffer out;
2401         QVERIFY(out.open(QIODevice::ReadWrite));
2402
2403         const QXmlQuery query;
2404
2405         query.evaluateTo(&out);
2406     }
2407 }
2408
2409 void tst_QXmlQuery::evaluateToQIODeviceOnInvalidQuery() const
2410 {
2411     QBuffer out;
2412     QVERIFY(out.open(QIODevice::WriteOnly));
2413
2414     /* On syntactically invalid query. */
2415     {
2416         QXmlQuery query;
2417         MessageSilencer silencer;
2418         query.setMessageHandler(&silencer);
2419         query.setQuery(QLatin1String("1 +"));
2420         QVERIFY(!query.isValid());
2421         QVERIFY(!query.evaluateTo(&out));
2422     }
2423
2424     /* On null QXmlQuery instance. */
2425     {
2426         QXmlQuery query;
2427         QVERIFY(!query.evaluateTo(&out));
2428     }
2429
2430 }
2431
2432 void tst_QXmlQuery::setQueryQIODeviceQUrl() const
2433 {
2434     /* Basic test. */
2435     {
2436         QBuffer buffer;
2437         buffer.setData("1, 2, 2 + 1");
2438         QVERIFY(buffer.open(QIODevice::ReadOnly));
2439
2440         QXmlQuery query;
2441         query.setQuery(&buffer);
2442         QVERIFY(query.isValid());
2443
2444         QXmlResultItems result;
2445         query.evaluateTo(&result);
2446         QCOMPARE(result.next().toAtomicValue(), QVariant(1));
2447         QCOMPARE(result.next().toAtomicValue(), QVariant(2));
2448         QCOMPARE(result.next().toAtomicValue(), QVariant(3));
2449         QVERIFY(result.next().isNull());
2450         QVERIFY(!result.hasError());
2451     }
2452
2453     /* Set query that is invalid. */
2454     {
2455         QBuffer buffer;
2456         buffer.setData("1, ");
2457         QVERIFY(buffer.open(QIODevice::ReadOnly));
2458
2459         QXmlQuery query;
2460         MessageSilencer silencer;
2461         query.setMessageHandler(&silencer);
2462         query.setQuery(&buffer);
2463         QVERIFY(!query.isValid());
2464     }
2465
2466     /* Check that the base URI passes through. */
2467     {
2468         QBuffer buffer;
2469         buffer.setData("string(static-base-uri())");
2470         QVERIFY(buffer.open(QIODevice::ReadOnly));
2471
2472         QXmlQuery query;
2473         query.setQuery(&buffer, QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl"));
2474         QVERIFY(query.isValid());
2475
2476         QStringList result;
2477         query.evaluateTo(&result);
2478         QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
2479     }
2480 }
2481
2482 void tst_QXmlQuery::setQueryQIODeviceQUrlTriggerWarnings() const
2483 {
2484     QXmlQuery query;
2485     QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed.");
2486     query.setQuery(0);
2487
2488     QBuffer buffer;
2489     QTest::ignoreMessage(QtWarningMsg, "The device must be readable.");
2490     query.setQuery(&buffer);
2491 }
2492
2493 void tst_QXmlQuery::setQueryQString() const
2494 {
2495     /* Basic test. */
2496     {
2497         QXmlQuery query;
2498         query.setQuery(QLatin1String("1, 2, 2 + 1"));
2499         QVERIFY(query.isValid());
2500
2501         QXmlResultItems result;
2502         query.evaluateTo(&result);
2503         QCOMPARE(result.next().toAtomicValue(), QVariant(1));
2504         QCOMPARE(result.next().toAtomicValue(), QVariant(2));
2505         QCOMPARE(result.next().toAtomicValue(), QVariant(3));
2506         QVERIFY(result.next().isNull());
2507         QVERIFY(!result.hasError());
2508     }
2509
2510     /* Set query that is invalid. */
2511     {
2512         MessageSilencer silencer;
2513         QXmlQuery query;
2514         query.setMessageHandler(&silencer);
2515         query.setQuery(QLatin1String("1, "));
2516         QVERIFY(!query.isValid());
2517     }
2518
2519     /* Check that the base URI passes through. */
2520     {
2521         QXmlQuery query;
2522         query.setQuery(QLatin1String("string(static-base-uri())"), QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl"));
2523         QVERIFY(query.isValid());
2524
2525         QStringList result;
2526         query.evaluateTo(&result);
2527         QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
2528     }
2529 }
2530
2531 void tst_QXmlQuery::setQueryQUrlSuccess() const
2532 {
2533 #if defined(Q_OS_WINCE) && !defined(_X86_)
2534     QStringList testsToSkip;
2535     testsToSkip << "A valid query via the ftp scheme" << "A valid query via the http scheme";
2536     if (testsToSkip.contains(QTest::currentDataTag()))
2537         QSKIP("Network tests are currently unsupported on Windows CE.", SkipSingle);
2538 #endif
2539
2540     QFETCH(QUrl, queryURI);
2541     QFETCH(QByteArray, expectedOutput);
2542
2543     QVERIFY(queryURI.isValid());
2544
2545     QXmlQuery query;
2546
2547     MessageSilencer silencer;
2548     query.setMessageHandler(&silencer);
2549
2550     query.setQuery(queryURI);
2551     QVERIFY(query.isValid());
2552
2553     QByteArray out;
2554     QBuffer buffer(&out);
2555     QVERIFY(buffer.open(QIODevice::WriteOnly));
2556     QXmlSerializer serializer(query, &buffer);
2557
2558     query.evaluateTo(&serializer);
2559     QCOMPARE(out, expectedOutput);
2560 }
2561
2562 void tst_QXmlQuery::setQueryQUrlSuccess_data() const
2563 {
2564     QTest::addColumn<QUrl>("queryURI");
2565     QTest::addColumn<QByteArray>("expectedOutput");
2566
2567     QTest::newRow("A valid query via the data scheme")
2568         << QUrl::fromEncoded("data:application/xml,1%20%2B%201") /* "1 + 1" */
2569         << QByteArray("2");
2570
2571     QTest::newRow("A valid query via the file scheme")
2572         << QUrl::fromLocalFile(inputFile(QLatin1String(queriesDirectory) + QLatin1String("onePlusOne.xq")))
2573         << QByteArray("2");
2574
2575     if(!m_testNetwork)
2576         return;
2577
2578     QTest::newRow("A valid query via the ftp scheme")
2579         << QUrl::fromEncoded(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/viaFtp.xq").toLatin1())
2580         << QByteArray("This was received via FTP");
2581
2582     QTest::newRow("A valid query via the http scheme")
2583         << QUrl::fromEncoded(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/viaHttp.xq").toLatin1())
2584         << QByteArray("This was received via HTTP.");
2585 }
2586
2587 void tst_QXmlQuery::setQueryQUrlFailSucceed() const
2588 {
2589     QXmlQuery query;
2590     MessageSilencer silencer;
2591
2592     query.setMessageHandler(&silencer);
2593
2594     query.setQuery(QLatin1String("1 + 1"));
2595     QVERIFY(query.isValid());
2596
2597     query.setQuery(QUrl::fromEncoded("file://example.com/does/not/exist"));
2598     QVERIFY(!query.isValid());
2599 }
2600
2601 void tst_QXmlQuery::setQueryQUrlFailure() const
2602 {
2603     QFETCH(QUrl, queryURI);
2604
2605     MessageSilencer silencer;
2606
2607     QXmlQuery query;
2608     query.setMessageHandler(&silencer);
2609     query.setQuery(queryURI);
2610     QVERIFY(!query.isValid());
2611 }
2612
2613 void tst_QXmlQuery::setQueryQUrlFailure_data() const
2614 {
2615     QTest::addColumn<QUrl>("queryURI");
2616
2617     QTest::newRow("Query via file:// that does not exist.")
2618         << QUrl::fromEncoded("file://example.com/does/not/exist");
2619
2620     QTest::newRow("A query via file:// that is completely empty, but readable.")
2621         << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("../xmlpatterns/queries/completelyEmptyQuery.xq"));
2622
2623     {
2624         const QString name(QLatin1String("nonReadableFile.xq"));
2625         QFile outFile(name);
2626         QVERIFY(outFile.open(QIODevice::WriteOnly));
2627         outFile.write(QByteArray("1"));
2628         outFile.close();
2629         /* On some windows versions, this fails, so we don't check that this works with QVERIFY. */
2630         outFile.setPermissions(QFile::Permissions(QFile::Permissions()));
2631
2632         QTest::newRow("Query via file:/ that does not have read permissions.")
2633             << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("nonReadableFile.xq"));
2634     }
2635
2636     if(!m_testNetwork)
2637         return;
2638
2639     QTest::newRow("Query via HTTP that does not exist.")
2640         << QUrl::fromEncoded("http://example.com/NoQuery/ISWear");
2641
2642     /*
2643     QTest::newRow("Query via FTP that does not exist.")
2644         << QUrl::fromEncoded("ftp://example.com/NoQuery/ISWear");
2645         */
2646
2647     QTest::newRow("A query via http:// that is completely empty, but readable.")
2648         << QUrl::fromEncoded(QString(
2649                 "http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/completelyEmptyQuery.xq").toLatin1());
2650
2651     QTest::newRow("A query via ftp:// that is completely empty, but readable.")
2652         << QUrl::fromEncoded(QString(
2653                 "ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/completelyEmptyQuery.xq").toLatin1());
2654
2655 }
2656
2657 void tst_QXmlQuery::setQueryQUrlBaseURI() const
2658 {
2659     QFETCH(QUrl, inputBaseURI);
2660     QFETCH(QUrl, expectedBaseURI);
2661
2662     QXmlQuery query;
2663
2664     query.setQuery(QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq")), inputBaseURI);
2665     QVERIFY(query.isValid());
2666
2667     QStringList result;
2668     QVERIFY(query.evaluateTo(&result));
2669     QCOMPARE(result.count(), 1);
2670
2671     if(qstrcmp(QTest::currentDataTag(), "Relative base URI") == 0)
2672         checkBaseURI(QUrl(result.first()), QCoreApplication::applicationFilePath());
2673     else
2674         QCOMPARE(result.first(), expectedBaseURI.toString());
2675 }
2676
2677 void tst_QXmlQuery::setQueryQUrlBaseURI_data() const
2678 {
2679     QTest::addColumn<QUrl>("inputBaseURI");
2680     QTest::addColumn<QUrl>("expectedBaseURI");
2681
2682     QTest::newRow("absolute HTTP")
2683         << QUrl(QLatin1String("http://www.example.com/"))
2684         << QUrl(QLatin1String("http://www.example.com/"));
2685
2686     QTest::newRow("None, so the query URI is used")
2687         << QUrl()
2688         << QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq"));
2689
2690     QTest::newRow("Relative base URI")
2691         << QUrl(QLatin1String("../data/relative.uri"))
2692         << QUrl();
2693 }
2694
2695 /*!
2696   1. Create a valid query.
2697   2. Call setQuery(QUrl), with a query file that doesn't exist.
2698   3. Verify that the query has changed state into invalid.
2699  */
2700 void tst_QXmlQuery::setQueryWithNonExistentQUrlOnValidQuery() const
2701 {
2702     QXmlQuery query;
2703
2704     MessageSilencer messageSilencer;
2705     query.setMessageHandler(&messageSilencer);
2706
2707     query.setQuery(QLatin1String("1 + 1"));
2708     QVERIFY(query.isValid());
2709
2710     query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/DOESNOTEXIST.xq"));
2711     QVERIFY(!query.isValid());
2712 }
2713
2714 /*!
2715   1. Create a valid query.
2716   2. Call setQuery(QUrl), with a query file that is invalid.
2717   3. Verify that the query has changed state into invalid.
2718  */
2719 void tst_QXmlQuery::setQueryWithInvalidQueryFromQUrlOnValidQuery() const
2720 {
2721     QXmlQuery query;
2722
2723     MessageSilencer messageSilencer;
2724     query.setMessageHandler(&messageSilencer);
2725
2726     query.setQuery(QLatin1String("1 + 1"));
2727     QVERIFY(query.isValid());
2728
2729     query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/queries/syntaxError.xq"));
2730     QVERIFY(!query.isValid());
2731 }
2732
2733 /*!
2734   This triggered two bugs:
2735
2736   - First, the DynamicContext wasn't assigned to QXmlResultItems, meaning it went out of
2737     scope and therefore deallocated the document pool, and calls
2738     to QXmlResultItems::next() would use dangling pointers.
2739
2740   - Conversion between QPatternist::Item and QXmlItem was incorrectly done, leading to nodes
2741     being treated as atomic values, and subsequent crashes.
2742
2743  */
2744 void tst_QXmlQuery::retrieveNameFromQuery() const
2745 {
2746     QFETCH(QString, queryString);
2747     QFETCH(QString, expectedName);
2748
2749     QXmlQuery query;
2750     query.setQuery(queryString);
2751     QVERIFY(query.isValid());
2752     QXmlResultItems result;
2753     query.evaluateTo(&result);
2754
2755     QVERIFY(!result.hasError());
2756
2757     const QXmlItem item(result.next());
2758     QVERIFY(!result.hasError());
2759     QVERIFY(!item.isNull());
2760     QVERIFY(item.isNode());
2761
2762     const QXmlNodeModelIndex node(item.toNodeModelIndex());
2763     QVERIFY(!node.isNull());
2764
2765     QCOMPARE(node.model()->name(node).localName(query.namePool()), expectedName);
2766 }
2767
2768 void tst_QXmlQuery::retrieveNameFromQuery_data() const
2769 {
2770     QTest::addColumn<QString>("queryString");
2771     QTest::addColumn<QString>("expectedName");
2772
2773     QTest::newRow("Document-node")
2774         << QString::fromLatin1("document{<elementName/>}")
2775         << QString();
2776
2777     QTest::newRow("Element")
2778         << QString::fromLatin1("document{<elementName/>}/*")
2779         << QString::fromLatin1("elementName");
2780 }
2781
2782 /*!
2783  Binding a null QString leads to no variable binding, but an
2784  empty non-null QString is possible.
2785  */
2786 void tst_QXmlQuery::bindEmptyNullString() const
2787 {
2788     MessageSilencer messageHandler;
2789     QXmlQuery query;
2790     query.setMessageHandler(&messageHandler);
2791     query.setQuery(QLatin1String("declare variable $v external; $v"));
2792     /* Here, we effectively pass an invalid QVariant. */
2793     query.bindVariable(QLatin1String("v"), QVariant(QString()));
2794     QVERIFY(!query.isValid());
2795
2796     QStringList result;
2797     QVERIFY(!query.evaluateTo(&result));
2798 }
2799
2800 void tst_QXmlQuery::bindEmptyString() const
2801 {
2802     QXmlQuery query;
2803     query.bindVariable(QLatin1String("v"), QVariant(QString(QLatin1String(""))));
2804     query.setQuery(QLatin1String("declare variable $v external; $v"));
2805     QVERIFY(query.isValid());
2806
2807     QStringList result;
2808     QVERIFY(query.evaluateTo(&result));
2809     QStringList expected((QString()));
2810     QCOMPARE(result, expected);
2811 }
2812
2813 void tst_QXmlQuery::cleanupTestCase() const
2814 {
2815     /* Remove a weird file we created. */
2816     const QString name(QLatin1String("nonReadableFile.xq"));
2817
2818     if(QFile::exists(name))
2819     {
2820         QFile file(name);
2821         QVERIFY(file.setPermissions(QFile::WriteOwner));
2822         QVERIFY(file.remove());
2823     }
2824 }
2825
2826 void tst_QXmlQuery::declareUnavailableExternal() const
2827 {
2828     QXmlQuery query;
2829     MessageSilencer silencer;
2830     query.setMessageHandler(&silencer);
2831     query.setQuery(QLatin1String("declare variable $var external;"
2832                                  "1 + 1"));
2833     /* We do not bind $var with QXmlQuery::bindVariable(). */
2834     QVERIFY(!query.isValid());
2835 }
2836
2837 /*!
2838  This test triggers an assert in one of the cache iterator
2839  with MSVC 2005 when compiled in debug mode.
2840  */
2841 void tst_QXmlQuery::msvcCacheIssue() const
2842 {
2843     QXmlQuery query;
2844     query.bindVariable(QLatin1String("externalVariable"), QXmlItem("Variable Value"));
2845     query.setQuery(QUrl::fromLocalFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariableUsedTwice.xq")));
2846     QStringList result;
2847     QVERIFY(query.evaluateTo(&result));
2848
2849     QCOMPARE(result,
2850              QStringList() << QString::fromLatin1("Variable Value") << QString::fromLatin1("Variable Value"));
2851 }
2852
2853 void tst_QXmlQuery::unavailableExternalVariable() const
2854 {
2855     QXmlQuery query;
2856
2857     MessageSilencer silencer;
2858     query.setMessageHandler(&silencer);
2859
2860     query.setQuery(QLatin1String("declare variable $foo external; 1"));
2861
2862     QVERIFY(!query.isValid());
2863 }
2864
2865 /*!
2866  Ensure that setUriResolver() affects \c fn:doc() and \c fn:doc-available().
2867  */
2868 void tst_QXmlQuery::useUriResolver() const
2869 {
2870     class TestUriResolver : public QAbstractUriResolver
2871                           , private TestFundament
2872     {
2873     public:
2874         TestUriResolver() {}
2875         virtual QUrl resolve(const QUrl &relative,
2876                              const QUrl &baseURI) const
2877         {
2878             Q_UNUSED(relative);
2879             return baseURI.resolved(inputFile(QLatin1String(queriesDirectory) + QLatin1String("simpleDocument.xml")));
2880         }
2881     };
2882
2883     const TestUriResolver uriResolver;
2884     QXmlQuery query;
2885
2886     query.setUriResolver(&uriResolver);
2887     query.setQuery(QLatin1String("let $i := 'http://www.example.com/DoesNotExist'"
2888                                  "return (string(doc($i)), doc-available($i))"));
2889
2890
2891     QXmlResultItems result;
2892     query.evaluateTo(&result);
2893
2894     QVERIFY(!result.hasError());
2895     QCOMPARE(result.next().toAtomicValue().toString(), QString::fromLatin1("text text node"));
2896     QCOMPARE(result.next().toAtomicValue().toBool(), true);
2897     QVERIFY(result.next().isNull());
2898     QVERIFY(!result.hasError());
2899 }
2900
2901 void tst_QXmlQuery::queryWithFocusAndVariable() const
2902 {
2903     QXmlQuery query;
2904     query.setFocus(QXmlItem(5));
2905     query.bindVariable(QLatin1String("var"), QXmlItem(2));
2906
2907     query.setQuery(QLatin1String("string(. * $var)"));
2908
2909     QStringList result;
2910
2911     QVERIFY(query.evaluateTo(&result));
2912
2913     QCOMPARE(result, QStringList(QLatin1String("10")));
2914 }
2915
2916 void tst_QXmlQuery::undefinedFocus() const
2917 {
2918     QXmlQuery query;
2919
2920     MessageSilencer silencer;
2921     query.setMessageHandler(&silencer);
2922
2923     query.setQuery(QLatin1String("."));
2924     QVERIFY(!query.isValid());
2925 }
2926
2927 void tst_QXmlQuery::basicFocusUsage() const
2928 {
2929     QXmlQuery query;
2930
2931     MessageSilencer silencer;
2932     query.setMessageHandler(&silencer);
2933
2934     query.setFocus(QXmlItem(5));
2935     query.setQuery(QLatin1String("string(. * .)"));
2936     QVERIFY(query.isValid());
2937
2938     QStringList result;
2939     QVERIFY(query.evaluateTo(&result));
2940
2941     QCOMPARE(result, QStringList(QLatin1String("25")));
2942 }
2943
2944 /*!
2945   Triggers an ownership related crash.
2946  */
2947 void tst_QXmlQuery::copyCheckMessageHandler() const
2948 {
2949     QXmlQuery query;
2950     QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0));
2951
2952     query.setQuery(QLatin1String("doc('qrc:/QXmlQueryTestData/data/oneElement.xml')"));
2953     /* By now, we should have set the builtin message handler. */
2954     const QAbstractMessageHandler *const messageHandler = query.messageHandler();
2955     QVERIFY(messageHandler);
2956
2957     {
2958         /* This copies QXmlQueryPrivate::m_ownerObject, and its destructor
2959          * will delete it, and hence the builtin message handler attached to it. */
2960         QXmlQuery copy(query);
2961     }
2962
2963     QXmlResultItems result;
2964     query.evaluateTo(&result);
2965
2966     while(!result.next().isNull())
2967     {
2968     }
2969     QVERIFY(!result.hasError());
2970 }
2971
2972 void tst_QXmlQuery::queryLanguage() const
2973 {
2974     /* Check default value. */
2975     {
2976         const QXmlQuery query;
2977         QCOMPARE(query.queryLanguage(), QXmlQuery::XQuery10);
2978     }
2979
2980     /* Check default value of copies default instance. */
2981     {
2982         const QXmlQuery query1;
2983         const QXmlQuery query2(query1);
2984
2985         QCOMPARE(query1.queryLanguage(), QXmlQuery::XQuery10);
2986         QCOMPARE(query2.queryLanguage(), QXmlQuery::XQuery10);
2987     }
2988 }
2989
2990 void tst_QXmlQuery::queryLanguageSignature() const
2991 {
2992     /* This getter should be const. */
2993     QXmlQuery query;
2994     query.queryLanguage();
2995 }
2996
2997 void tst_QXmlQuery::enumQueryLanguage() const
2998 {
2999     /* These enum values should be possible to OR for future plans. */
3000     QCOMPARE(int(QXmlQuery::XQuery10), 1);
3001     QCOMPARE(int(QXmlQuery::XSLT20), 2);
3002     QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintSelector), 1024);
3003     QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintField), 2048);
3004     QCOMPARE(int(QXmlQuery::XPath20), 4096);
3005 }
3006
3007 void tst_QXmlQuery::setInitialTemplateNameQXmlName() const
3008 {
3009     QXmlQuery query(QXmlQuery::XSLT20);
3010     QXmlNamePool np(query.namePool());
3011     const QXmlName name(np, QLatin1String("main"));
3012
3013     query.setInitialTemplateName(name);
3014
3015     QCOMPARE(query.initialTemplateName(), name);
3016
3017     query.setQuery(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/namedTemplate.xsl"))));
3018     QVERIFY(query.isValid());
3019
3020     QBuffer result;
3021     QVERIFY(result.open(QIODevice::ReadWrite));
3022     QXmlSerializer serializer(query, &result);
3023     query.evaluateTo(&serializer);
3024
3025     QCOMPARE(result.data(), QByteArray("1 2 3 4 5"));
3026
3027     // TODO invoke a template which has required params.
3028 }
3029
3030 void tst_QXmlQuery::setInitialTemplateNameQXmlNameSignature() const
3031 {
3032     QXmlQuery query;
3033     QXmlNamePool np(query.namePool());
3034     const QXmlName name(np, QLatin1String("foo"));
3035
3036     /* The signature should take a const reference. */
3037     query.setInitialTemplateName(name);
3038 }
3039
3040 void tst_QXmlQuery::setInitialTemplateNameQString() const
3041 {
3042     QXmlQuery query;
3043     QXmlNamePool np(query.namePool());
3044     query.setInitialTemplateName(QLatin1String("foo"));
3045
3046     QCOMPARE(query.initialTemplateName(), QXmlName(np, QLatin1String("foo")));
3047 }
3048
3049 void tst_QXmlQuery::setInitialTemplateNameQStringSignature() const
3050 {
3051     const QString name(QLatin1String("name"));
3052     QXmlQuery query;
3053
3054     /* We should take a const reference. */
3055     query.setInitialTemplateName(name);
3056 }
3057
3058 void tst_QXmlQuery::initialTemplateName() const
3059 {
3060     /* Check our default value. */
3061     QXmlQuery query;
3062     QCOMPARE(query.initialTemplateName(), QXmlName());
3063     QVERIFY(query.initialTemplateName().isNull());
3064 }
3065
3066 void tst_QXmlQuery::initialTemplateNameSignature() const
3067 {
3068     const QXmlQuery query;
3069     /* This should be a const member. */
3070     query.initialTemplateName();
3071 }
3072
3073 void tst_QXmlQuery::setNetworkAccessManager() const
3074 {
3075
3076     /* Ensure fn:doc() picks up the right QNetworkAccessManager. */
3077     {
3078         NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
3079                                           QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/queries/simpleDocument.xml"))));
3080         QVERIFY(networkOverrider.isValid());
3081
3082         QXmlQuery query;
3083         query.setNetworkAccessManager(&networkOverrider);
3084         query.setQuery(QLatin1String("string(doc('tag:example.com:DOESNOTEXIST'))"));
3085         QVERIFY(query.isValid());
3086
3087         QStringList result;
3088         QVERIFY(query.evaluateTo(&result));
3089
3090         QCOMPARE(result, QStringList(QLatin1String("text text node")));
3091     }
3092
3093     /* Ensure setQuery() is using the right network manager. */
3094     {
3095         NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
3096                                           QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/queries/concat.xq"))));
3097         QVERIFY(networkOverrider.isValid());
3098
3099         QXmlQuery query;
3100         query.setNetworkAccessManager(&networkOverrider);
3101         query.setQuery(QUrl("tag:example.com:DOESNOTEXIST"));
3102         QVERIFY(query.isValid());
3103
3104         QStringList result;
3105         QVERIFY(query.evaluateTo(&result));
3106
3107         QCOMPARE(result, QStringList(QLatin1String("abcdef")));
3108     }
3109 }
3110 void tst_QXmlQuery::networkAccessManagerSignature() const
3111 {
3112     /* Const object. */
3113     const QXmlQuery query;
3114
3115     /* The function should be const. */
3116     query.networkAccessManager();
3117 }
3118
3119 void tst_QXmlQuery::networkAccessManagerDefaultValue() const
3120 {
3121     const QXmlQuery query;
3122
3123     QCOMPARE(query.networkAccessManager(), static_cast<QNetworkAccessManager *>(0));
3124 }
3125
3126 void tst_QXmlQuery::networkAccessManager() const
3127 {
3128     /* Test that we return the network manager that was set. */
3129     {
3130         QNetworkAccessManager manager;
3131         QXmlQuery query;
3132         query.setNetworkAccessManager(&manager);
3133         QCOMPARE(query.networkAccessManager(), &manager);
3134     }
3135 }
3136
3137 /*!
3138  \internal
3139  \since 4.5
3140
3141   1. Load a document into QXmlQuery's document cache, by executing a query which does it.
3142   2. Set a focus
3143   3. Change query, to one which uses the focus
3144   4. Evaluate
3145
3146  Used to crash.
3147  */
3148 void tst_QXmlQuery::multipleDocsAndFocus() const
3149 {
3150     QXmlQuery query;
3151
3152     /* We use string concatenation, since variable bindings might disturb what
3153      * we're testing. */
3154     query.setQuery(QLatin1String("string(doc('") +
3155                    inputFile(QLatin1String(XMLPATTERNSDIR "/queries/simpleDocument.xml")) +
3156                    QLatin1String("'))"));
3157     query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3158     query.setQuery(QLatin1String("string(.)"));
3159
3160     QStringList result;
3161     QVERIFY(query.evaluateTo(&result));
3162 }
3163
3164 /*!
3165  \internal
3166  \since 4.5
3167
3168  1. Set a focus
3169  2. Set a query
3170  3. Evaluate
3171  4. Change focus
3172  5. Evaluate
3173
3174  Used to crash.
3175  */
3176 void tst_QXmlQuery::multipleEvaluationsWithDifferentFocus() const
3177 {
3178     QXmlQuery query;
3179     QStringList result;
3180
3181     query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3182     query.setQuery(QLatin1String("string(.)"));
3183     QVERIFY(query.evaluateTo(&result));
3184
3185     query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3186     QVERIFY(query.evaluateTo(&result));
3187 }
3188
3189 void tst_QXmlQuery::bindVariableQXmlQuery() const
3190 {
3191     QFETCH(QString, query1);
3192     QFETCH(QString, query2);
3193     QFETCH(QString, expectedOutput);
3194     QFETCH(bool, expectedSuccess);
3195
3196     MessageSilencer silencer;
3197     QXmlQuery xmlQuery1;
3198     xmlQuery1.setMessageHandler(&silencer);
3199     xmlQuery1.setQuery(query1);
3200
3201     QXmlQuery xmlQuery2(xmlQuery1);
3202     xmlQuery2.bindVariable("query1", xmlQuery1);
3203     xmlQuery2.setQuery(query2);
3204
3205     QString output;
3206     const bool querySuccess = xmlQuery2.evaluateTo(&output);
3207
3208     QCOMPARE(querySuccess, expectedSuccess);
3209
3210     if(querySuccess)
3211         QCOMPARE(output, expectedOutput);
3212 }
3213
3214 void tst_QXmlQuery::bindVariableQXmlQuery_data() const
3215 {
3216     QTest::addColumn<QString>("query1");
3217     QTest::addColumn<QString>("query2");
3218     QTest::addColumn<QString>("expectedOutput");
3219     QTest::addColumn<bool>("expectedSuccess");
3220
3221     QTest::newRow("First query has one atomic value.")
3222             << "2"
3223             << "1, $query1, 3"
3224             << "1 2 3\n"
3225             << true;
3226
3227     QTest::newRow("First query has two atomic values.")
3228             << "2, 3"
3229             << "1, $query1, 4"
3230             << "1 2 3 4\n"
3231             << true;
3232
3233     QTest::newRow("First query is a node.")
3234             << "<e/>"
3235             << "1, $query1, 3"
3236             << "1<e/>3\n"
3237             << true;
3238
3239     /* This is a good test, because it triggers the exception in the
3240      * bindVariable() call, as supposed to when the actual evaluation is done.
3241      */
3242     QTest::newRow("First query has a dynamic error.")
3243             << "error()"
3244             << "1, $query1"
3245             << QString() /* We don't care. */
3246             << false;
3247 }
3248
3249 void tst_QXmlQuery::bindVariableQStringQXmlQuerySignature() const
3250 {
3251     QXmlQuery query1;
3252     query1.setQuery("'dummy'");
3253
3254     QXmlQuery query2;
3255     const QString name(QLatin1String("name"));
3256
3257     /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
3258      * QXmlQuery, and evaluation is what we do here. */
3259     query2.bindVariable(name, const_cast<const QXmlQuery &>(query1));
3260 }
3261
3262 void tst_QXmlQuery::bindVariableQXmlNameQXmlQuerySignature() const
3263 {
3264     QXmlNamePool np;
3265     QXmlQuery query1(np);
3266     query1.setQuery("'dummy'");
3267
3268     QXmlQuery query2;
3269     const QXmlName name(np, QLatin1String("name"));
3270
3271     /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
3272      * QXmlQuery, and evaluation is what we do here. */
3273     query2.bindVariable(name, const_cast<const QXmlQuery &>(query1));
3274 }
3275
3276 /*!
3277   Check that the QXmlName is handled correctly.
3278  */
3279 void tst_QXmlQuery::bindVariableQXmlNameQXmlQuery() const
3280 {
3281     QXmlNamePool np;
3282     QXmlQuery query1;
3283     query1.setQuery(QLatin1String("1"));
3284
3285     QXmlQuery query2(np);
3286     query2.bindVariable(QXmlName(np, QLatin1String("theName")), query1);
3287     query2.setQuery("$theName");
3288
3289     QString result;
3290     query2.evaluateTo(&result);
3291
3292     QCOMPARE(result, QString::fromLatin1("1\n"));
3293 }
3294
3295 void tst_QXmlQuery::bindVariableQXmlQueryInvalidate() const
3296 {
3297     QXmlQuery query;
3298     query.bindVariable(QLatin1String("name"), QVariant(1));
3299     query.setQuery("$name");
3300     QVERIFY(query.isValid());
3301
3302     QXmlQuery query2;
3303     query2.setQuery("'query2'");
3304
3305     query.bindVariable(QLatin1String("name"), query);
3306     QVERIFY(!query.isValid());
3307 }
3308
3309 void tst_QXmlQuery::unknownSourceLocation() const
3310 {
3311     QBuffer b;
3312     b.setData("<a><b/><b/></a>");
3313     b.open(QIODevice::ReadOnly);
3314
3315     MessageSilencer silencer;
3316     QXmlQuery query;
3317     query.bindVariable(QLatin1String("inputDocument"), &b);
3318     query.setMessageHandler(&silencer);
3319
3320     query.setQuery(QLatin1String("doc($inputDocument)/a/(let $v := b/string() return if ($v) then $v else ())"));
3321
3322     QString output;
3323     query.evaluateTo(&output);
3324 }
3325
3326 void tst_QXmlQuery::identityConstraintSuccess() const
3327 {
3328     QXmlQuery::QueryLanguage queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintSelector;
3329
3330     /* We run this code for Selector and Field. */
3331     for(int i = 0; i < 3; ++i)
3332     {
3333         QXmlNamePool namePool;
3334         QXmlResultItems result;
3335         QXmlItem node;
3336
3337         {
3338             QXmlQuery nodeSource(namePool);
3339             nodeSource.setQuery(QLatin1String("<e/>"));
3340
3341             nodeSource.evaluateTo(&result);
3342             node = result.next();
3343         }
3344
3345         /* Basic use:
3346          * 1. The focus is undefined, but it's still valid.
3347          * 2. We never evaluate. */
3348         {
3349             QXmlQuery query(queryLanguage);
3350             query.setQuery(QLatin1String("a"));
3351             QVERIFY(query.isValid());
3352         }
3353
3354         /* Basic use:
3355          * 1. The focus is undefined, but it's still valid.
3356          * 2. We afterwards set the focus. */
3357         {
3358             QXmlQuery query(queryLanguage, namePool);
3359             query.setQuery(QLatin1String("a"));
3360             query.setFocus(node);
3361             QVERIFY(query.isValid());
3362         }
3363
3364         /* Basic use:
3365          * 1. The focus is undefined, but it's still valid.
3366          * 2. We afterwards set the focus.
3367          * 3. We evaluate. */
3368         {
3369             QXmlQuery query(queryLanguage, namePool);
3370             query.setQuery(QString(QLatin1Char('.')));
3371             query.setFocus(node);
3372             QVERIFY(query.isValid());
3373
3374             QString result;
3375             QVERIFY(query.evaluateTo(&result));
3376             QCOMPARE(result, QString::fromLatin1("<e/>\n"));
3377         }
3378
3379         /* A slightly more complex Field. */
3380         {
3381             QXmlQuery query(queryLanguage);
3382             query.setQuery(QLatin1String("* | .//xml:*/."));
3383             QVERIFY(query.isValid());
3384         }
3385
3386         /* @ is only allowed in Field. */
3387         if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField)
3388         {
3389             QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField);
3390             query.setQuery(QLatin1String("@abc"));
3391             QVERIFY(query.isValid());
3392         }
3393
3394         /* Field allows attribute:: and child:: .*/
3395         if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField)
3396         {
3397             QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField);
3398             query.setQuery(QLatin1String("attribute::name | child::name"));
3399             QVERIFY(query.isValid());
3400         }
3401
3402         /* Selector allows only child:: .*/
3403         {
3404             QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintSelector);
3405             query.setQuery(QLatin1String("child::name"));
3406             QVERIFY(query.isValid());
3407         }
3408
3409         if(i == 0)
3410             queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintField;
3411         else if(i == 1)
3412             queryLanguage = QXmlQuery::XPath20;
3413     }
3414 }
3415
3416 Q_DECLARE_METATYPE(QXmlQuery::QueryLanguage);
3417
3418 /*!
3419  We just do some basic tests for boot strapping and sanity checking. The actual regression
3420  testing is in the Schema suite.
3421  */
3422 void tst_QXmlQuery::identityConstraintFailure() const
3423 {
3424     QFETCH(QXmlQuery::QueryLanguage, queryLanguage);
3425     QFETCH(QString, inputQuery);
3426
3427     QXmlQuery query(queryLanguage);
3428     MessageSilencer silencer;
3429     query.setMessageHandler(&silencer);
3430
3431     query.setQuery(inputQuery);
3432     QVERIFY(!query.isValid());
3433 }
3434
3435 void tst_QXmlQuery::identityConstraintFailure_data() const
3436 {
3437     QTest::addColumn<QXmlQuery::QueryLanguage>("queryLanguage");
3438     QTest::addColumn<QString>("inputQuery");
3439
3440     QTest::newRow("We don't have element constructors in identity constraint pattern, "
3441                   "it's an XQuery feature(Selector).")
3442         << QXmlQuery::XmlSchema11IdentityConstraintSelector
3443         << QString::fromLatin1("<e/>");
3444
3445     QTest::newRow("We don't have functions in identity constraint pattern, "
3446                   "it's an XPath feature(Selector).")
3447         << QXmlQuery::XmlSchema11IdentityConstraintSelector
3448         << QString::fromLatin1("current-time()");
3449
3450     QTest::newRow("We don't have element constructors in identity constraint pattern, "
3451                   "it's an XQuery feature(Field).")
3452         << QXmlQuery::XmlSchema11IdentityConstraintSelector
3453         << QString::fromLatin1("<e/>");
3454
3455     QTest::newRow("We don't have functions in identity constraint pattern, "
3456                   "it's an XPath feature(Field).")
3457         << QXmlQuery::XmlSchema11IdentityConstraintSelector
3458         << QString::fromLatin1("current-time()");
3459
3460     QTest::newRow("@attributeName is disallowed for the selector.")
3461         << QXmlQuery::XmlSchema11IdentityConstraintSelector
3462         << QString::fromLatin1("@abc");
3463
3464     QTest::newRow("attribute:: is disallowed for the selector.")
3465         << QXmlQuery::XmlSchema11IdentityConstraintSelector
3466         << QString::fromLatin1("attribute::name");
3467
3468     QTest::newRow("ancestor::name is disallowed for the selector.")
3469         << QXmlQuery::XmlSchema11IdentityConstraintSelector
3470         << QString::fromLatin1("ancestor::name");
3471
3472     QTest::newRow("ancestor::name is disallowed for the field.")
3473         << QXmlQuery::XmlSchema11IdentityConstraintField
3474         << QString::fromLatin1("ancestor::name");
3475 }
3476
3477 QTEST_MAIN(tst_QXmlQuery)
3478
3479 #include "tst_qxmlquery.moc"
3480 #else //QTEST_XMLPATTERNS
3481 QTEST_NOOP_MAIN
3482 #endif