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