Initial import from the monolithic Qt.
[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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
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     query.evaluateTo(&push);
971
972     const QString baselineName(inputFile(QLatin1String(SRCDIR "pushBaselines/") + inputQuery.left(inputQuery.length() - 2) + QString::fromLatin1("ref")));
973     QFile baseline(baselineName);
974
975     if(baseline.exists())
976     {
977         QVERIFY(baseline.open(QIODevice::ReadOnly | QIODevice::Text));
978         const QString stringedBaseline(QString::fromUtf8(baseline.readAll()));
979         QCOMPARE(produced, stringedBaseline);
980     }
981     else
982     {
983         QVERIFY(baseline.open(QIODevice::WriteOnly));
984         /* This is intentionally a warning, don't remove it. Update the baselines instead. */
985         qWarning() << "Generated baseline for:" << baselineName;
986         ++m_generatedBaselines;
987
988         baseline.write(produced.toUtf8());
989     }
990 }
991
992 void tst_QXmlQuery::evaluateToReceiver_data() const
993 {
994     QTest::addColumn<QString>("inputQuery");
995
996     const QStringList qs(queries());
997
998     for(int i = 0; i < qs.size(); ++i)
999     {
1000         /* This outputs a URI specific to the environment, so we can't use it for this
1001          * particular test. */
1002         if(qs.at(i) != QLatin1String("staticBaseURI.xq"))
1003             QTest::newRow(qs.at(i).toUtf8().constData()) << qs.at(i);
1004     }
1005 }
1006
1007 void tst_QXmlQuery::evaluateToReceiverOnInvalidQuery() const
1008 {
1009     /* Invoke on a default constructed object. */
1010     {
1011         QByteArray out;
1012         QBuffer buffer(&out);
1013         buffer.open(QIODevice::WriteOnly);
1014
1015         QXmlQuery query;
1016         QXmlSerializer serializer(query, &buffer);
1017         QVERIFY(!query.evaluateTo(&serializer));
1018     }
1019
1020     /* Invoke on an invalid query; compile time error. */
1021     {
1022         QByteArray out;
1023         QBuffer buffer(&out);
1024         buffer.open(QIODevice::WriteOnly);
1025         MessageSilencer silencer;
1026
1027         QXmlQuery query;
1028         query.setMessageHandler(&silencer);
1029         query.setQuery(QLatin1String("1 + "));
1030         QXmlSerializer serializer(query, &buffer);
1031         QVERIFY(!query.evaluateTo(&serializer));
1032     }
1033
1034     /* Invoke on an invalid query; runtime error. */
1035     {
1036         QByteArray out;
1037         QBuffer buffer(&out);
1038         buffer.open(QIODevice::WriteOnly);
1039         MessageSilencer silencer;
1040
1041         QXmlQuery query;
1042         query.setMessageHandler(&silencer);
1043         query.setQuery(QLatin1String("error()"));
1044         QXmlSerializer serializer(query, &buffer);
1045         QVERIFY(!query.evaluateTo(&serializer));
1046     }
1047 }
1048
1049 void tst_QXmlQuery::evaluateToQStringTriggerError() const
1050 {
1051     /* Invoke on a default constructed object. */
1052     {
1053         QXmlQuery query;
1054         QString out;
1055         QVERIFY(!query.evaluateTo(&out));
1056     }
1057
1058     /* Invoke on an invalid query; compile time error. */
1059     {
1060         QXmlQuery query;
1061         MessageSilencer silencer;
1062         query.setMessageHandler(&silencer);
1063
1064         query.setQuery(QLatin1String("1 + "));
1065
1066         QString out;
1067         QVERIFY(!query.evaluateTo(&out));
1068     }
1069
1070     /* Invoke on an invalid query; runtime error. */
1071     {
1072         QXmlQuery query;
1073         MessageSilencer silencer;
1074         query.setMessageHandler(&silencer);
1075
1076         query.setQuery(QLatin1String("error()"));
1077
1078         QString out;
1079         QVERIFY(!query.evaluateTo(&out));
1080     }
1081 }
1082
1083 void tst_QXmlQuery::evaluateToQString() const
1084 {
1085     QFETCH(QString, query);
1086     QFETCH(QString, expectedOutput);
1087
1088     QXmlQuery queryInstance;
1089     queryInstance.setQuery(query);
1090     QVERIFY(queryInstance.isValid());
1091
1092     QString result;
1093     QVERIFY(queryInstance.evaluateTo(&result));
1094
1095     QCOMPARE(result, expectedOutput);
1096 }
1097
1098 void tst_QXmlQuery::evaluateToQString_data() const
1099 {
1100     QTest::addColumn<QString>("query");
1101     QTest::addColumn<QString>("expectedOutput");
1102
1103     QTest::newRow("Two atomics")
1104         << QString::fromLatin1("1, 'two'")
1105         << QString::fromLatin1("1 two\n");
1106
1107     QTest::newRow("An element")
1108         << QString::fromLatin1("<e>{1}</e>")
1109         << QString::fromLatin1("<e>1</e>\n");
1110 }
1111
1112 void tst_QXmlQuery::evaluateToQStringSignature() const
1113 {
1114     const QXmlQuery query;
1115
1116     QString output;
1117
1118     /* evaluateTo(QString *) should be a const function. */
1119     query.evaluateTo(&output);
1120 }
1121
1122 void tst_QXmlQuery::evaluateToQAbstractXmlReceiverTriggerWarnings() const
1123 {
1124     QXmlQuery query;
1125
1126     /* We check the return value as well as warning message here. */
1127     QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed.");
1128     QCOMPARE(query.evaluateTo(static_cast<QAbstractXmlReceiver *>(0)),
1129              false);
1130 }
1131
1132 void tst_QXmlQuery::evaluateToQXmlResultItems() const
1133 {
1134     /* Invoke on a default constructed object. */
1135     {
1136         QXmlQuery query;
1137         QXmlResultItems result;
1138         query.evaluateTo(&result);
1139         QVERIFY(result.next().isNull());
1140     }
1141 }
1142
1143 void tst_QXmlQuery::evaluateToQXmlResultItemsTriggerWarnings() const
1144 {
1145     QTest::ignoreMessage(QtWarningMsg, "A null pointer cannot be passed.");
1146     QXmlQuery query;
1147     query.evaluateTo(static_cast<QXmlResultItems *>(0));
1148 }
1149
1150 void tst_QXmlQuery::evaluateToQXmlResultItemsErrorAtEnd() const
1151 {
1152     QXmlQuery query;
1153     MessageSilencer silencer;
1154     query.setMessageHandler(&silencer);
1155     query.setQuery(QLatin1String("1 to 100, fn:error()"));
1156     QVERIFY(query.isValid());
1157
1158     QXmlResultItems it;
1159     query.evaluateTo(&it);
1160
1161     while(!it.next().isNull())
1162     {
1163     }
1164 }
1165
1166 /*!
1167   If baselines were generated, we flag it as a failure such that it gets
1168   attention, and that they are adjusted accordingly.
1169  */
1170 void tst_QXmlQuery::checkGeneratedBaselines() const
1171 {
1172     QCOMPARE(m_generatedBaselines, 0);
1173
1174     /* If this check fails, the auto test setup is misconfigured, or files have
1175      * been added/removed without this number being updated. */
1176     QCOMPARE(m_pushTestsCount, int(ExpectedQueryCount));
1177 }
1178
1179 void tst_QXmlQuery::basicXQueryToQtTypeCheck() const
1180 {
1181     QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("allAtomics.xq"));
1182     QVERIFY(queryFile.open(QIODevice::ReadOnly));
1183
1184     QXmlQuery query;
1185     query.setQuery(&queryFile);
1186     QVERIFY(query.isValid());
1187
1188     QXmlResultItems it;
1189     query.evaluateTo(&it);
1190
1191     QVariantList expectedValues;
1192     expectedValues.append(QString::fromLatin1("xs:untypedAtomic"));
1193     expectedValues.append(QDateTime(QDate(2002, 10, 10), QTime(23, 2, 11), Qt::UTC));
1194     expectedValues.append(QDate(2002, 10, 10));
1195     expectedValues.append(QVariant()); /* We currently doesn't support xs:time through the API. */
1196
1197     expectedValues.append(QVariant()); /* xs:duration */
1198     expectedValues.append(QVariant()); /* xs:dayTimeDuration */
1199     expectedValues.append(QVariant()); /* xs:yearMonthDuration */
1200
1201     if(sizeof(qreal) == sizeof(float)) {//ARM casts to Float not to double
1202         expectedValues.append(QVariant(float(3e3)));     /* xs:float */
1203         expectedValues.append(QVariant(float(4e4)));     /* xs:double */
1204         expectedValues.append(QVariant(float(2)));       /* xs:decimal */
1205     } else {
1206         expectedValues.append(QVariant(double(3e3)));     /* xs:float */
1207         expectedValues.append(QVariant(double(4e4)));     /* xs:double */
1208         expectedValues.append(QVariant(double(2)));       /* xs:decimal */
1209     }
1210
1211     /* xs:integer and its sub-types. */
1212     expectedValues.append(QVariant(qlonglong(16)));
1213     expectedValues.append(QVariant(qlonglong(-6)));
1214     expectedValues.append(QVariant(qlonglong(-4)));
1215     expectedValues.append(QVariant(qlonglong(5)));
1216     expectedValues.append(QVariant(qlonglong(6)));
1217     expectedValues.append(QVariant(qlonglong(7)));
1218     expectedValues.append(QVariant(qlonglong(8)));
1219     expectedValues.append(QVariant(qlonglong(9)));
1220     expectedValues.append(QVariant(qulonglong(10)));
1221     expectedValues.append(QVariant(qlonglong(11)));
1222     expectedValues.append(QVariant(qlonglong(12)));
1223     expectedValues.append(QVariant(qlonglong(13)));
1224     expectedValues.append(QVariant(qlonglong(14)));
1225
1226     expectedValues.append(QVariant());                                                          /* xs:gYearMonth("1976-02"), */
1227     expectedValues.append(QVariant());                                                          /* xs:gYear("2005-12:00"), */
1228     expectedValues.append(QVariant());                                                          /* xs:gMonthDay("--12-25-14:00"), */
1229     expectedValues.append(QVariant());                                                          /* xs:gDay("---25-14:00"), */
1230     expectedValues.append(QVariant());                                                          /* xs:gMonth("--12-14:00"), */
1231     expectedValues.append(true);                                                                /* xs:boolean("true"), */
1232     expectedValues.append(QVariant(QByteArray::fromBase64(QByteArray("aaaa"))));                /* xs:base64Binary("aaaa"), */
1233     expectedValues.append(QVariant(QByteArray::fromHex(QByteArray("FFFF"))));                   /* xs:hexBinary("FFFF"), */
1234     expectedValues.append(QVariant(QString::fromLatin1("http://example.com/")));                /* xs:anyURI("http://example.com/"), */
1235     QXmlNamePool np(query.namePool());
1236     expectedValues.append(QVariant(qVariantFromValue(QXmlName(np, QLatin1String("localName"),
1237                                                               QLatin1String("http://example.com/2"),
1238                                                               QLatin1String("prefix")))));
1239
1240     expectedValues.append(QVariant(QString::fromLatin1("An xs:string")));
1241     expectedValues.append(QVariant(QString::fromLatin1("normalizedString")));
1242     expectedValues.append(QVariant(QString::fromLatin1("token")));
1243     expectedValues.append(QVariant(QString::fromLatin1("language")));
1244     expectedValues.append(QVariant(QString::fromLatin1("NMTOKEN")));
1245     expectedValues.append(QVariant(QString::fromLatin1("Name")));
1246     expectedValues.append(QVariant(QString::fromLatin1("NCName")));
1247     expectedValues.append(QVariant(QString::fromLatin1("ID")));
1248     expectedValues.append(QVariant(QString::fromLatin1("IDREF")));
1249     expectedValues.append(QVariant(QString::fromLatin1("ENTITY")));
1250
1251     int i = 0;
1252     QXmlItem item(it.next());
1253
1254     while(!item.isNull())
1255     {
1256         QVERIFY(item.isAtomicValue());
1257         const QVariant produced(item.toAtomicValue());
1258
1259         const QVariant &expected = expectedValues.at(i);
1260
1261         /* For the cases where we can't represent a value in the XDM with Qt,
1262          * we return an invalid QVariant. */
1263         QCOMPARE(expected.isValid(), produced.isValid());
1264
1265         QCOMPARE(produced.type(), expected.type());
1266
1267         if(expected.isValid())
1268         {
1269             /* This is only needed for xs:decimal though, for some reason. Probably
1270              * just artifacts created somewhere. */
1271             if(produced.type() == QVariant::Double)
1272                 QVERIFY(qFuzzyCompare(produced.toDouble(), expected.toDouble()));
1273             else if(qVariantCanConvert<QXmlName>(produced))
1274             {
1275                 /* QVariant::operator==() does identity comparison, it doesn't delegate to operator==() of
1276                  * the contained type, unless it's hardcoded into QVariant. */
1277                 const QXmlName n1 = qVariantValue<QXmlName>(produced);
1278                 const QXmlName n2 = qVariantValue<QXmlName>(expected);
1279                 QCOMPARE(n1, n2);
1280             }
1281             else
1282                 QCOMPARE(produced, expected);
1283         }
1284
1285         ++i;
1286         item = it.next();
1287     }
1288
1289     QCOMPARE(i, expectedValues.count());
1290 }
1291
1292 /*!
1293   Send values from Qt into XQuery.
1294  */
1295 void tst_QXmlQuery::basicQtToXQueryTypeCheck() const
1296 {
1297     QFile queryFile(QLatin1String(queriesDirectory) + QLatin1String("allAtomicsExternally.xq"));
1298     QVERIFY(queryFile.exists());
1299     QVERIFY(queryFile.open(QIODevice::ReadOnly));
1300
1301     QCOMPARE(QVariant(QDate(1999, 9, 10)).type(), QVariant::Date);
1302
1303     QXmlQuery query;
1304
1305     QXmlNamePool np(query.namePool());
1306
1307     const QXmlName name(np, QLatin1String("localname"),
1308                             QLatin1String("http://example.com"),
1309                             QLatin1String("prefix"));
1310
1311     query.bindVariable(QLatin1String("fromQUrl"), QXmlItem(QUrl(QString::fromLatin1("http://example.com/"))));
1312     query.bindVariable(QLatin1String("fromQByteArray"), QXmlItem(QByteArray("AAAA")));
1313     query.bindVariable(QLatin1String("fromBool"), QXmlItem(bool(true)));
1314     query.bindVariable(QLatin1String("fromQDate"), QXmlItem(QDate(2000, 10, 11)));
1315     // TODO Do with different QDateTime time specs
1316     query.bindVariable(QLatin1String("fromQDateTime"), QXmlItem(QDateTime(QDate(2001, 9, 10), QTime(1, 2, 3))));
1317     query.bindVariable(QLatin1String("fromDouble"), QXmlItem(double(3)));
1318     query.bindVariable(QLatin1String("fromFloat"), QXmlItem(float(4)));
1319     query.bindVariable(QLatin1String("integer"), QXmlItem(5));
1320     query.bindVariable(QLatin1String("fromQString"), QXmlItem(QString::fromLatin1("A QString")));
1321     query.bindVariable(QLatin1String("fromQChar"), QXmlItem(QChar::fromLatin1('C')));
1322
1323     query.bindVariable(QLatin1String("fromIntLiteral"), QXmlItem(QVariant(654)));
1324
1325     {
1326         QVariant ui(uint(5));
1327         QCOMPARE(ui.type(), QVariant::UInt);
1328         query.bindVariable(QLatin1String("fromUInt"), ui);
1329     }
1330
1331     {
1332         QVariant ulnglng(qulonglong(6));
1333         QCOMPARE(ulnglng.type(), QVariant::ULongLong);
1334         query.bindVariable(QLatin1String("fromULongLong"), ulnglng);
1335     }
1336
1337     {
1338         QVariant qlnglng(qlonglong(7));
1339         QCOMPARE(qlnglng.type(), QVariant::LongLong);
1340         query.bindVariable(QLatin1String("fromLongLong"), qlnglng);
1341     }
1342
1343     query.setQuery(&queryFile);
1344
1345     // TODO do queries which declares external variables with types. Tons of combos here.
1346     // TODO ensure that binding with QXmlItem() doesn't make a binding available.
1347     // TODO test rebinding a variable.
1348
1349     QVERIFY(query.isValid());
1350
1351     QXmlResultItems it;
1352     query.evaluateTo(&it);
1353     QXmlItem item(it.next());
1354     QVERIFY(!item.isNull());
1355     QVERIFY(item.isAtomicValue());
1356
1357     if(sizeof(qreal) == sizeof(float)) //ARM casts to Float not to double
1358         QCOMPARE(item.toAtomicValue().toString(),
1359                  QLatin1String("4 true 3 654 7 41414141 C 2000-10-11Z 2001-09-10T01:02:03 "
1360                                "A QString http://example.com/ 5 6 true false false true true true true true true true "
1361                                "true true true"));
1362     else
1363         QCOMPARE(item.toAtomicValue().toString(),
1364                  QLatin1String("4 true 3 654 7 41414141 C 2000-10-11Z 2001-09-10T01:02:03 "
1365                                "A QString http://example.com/ 5 6 true true true true true true true true true true "
1366                                "true true true"));
1367
1368 }
1369
1370 void tst_QXmlQuery::bindNode() const
1371 {
1372     QXmlQuery query;
1373     TestSimpleNodeModel nodeModel(query.namePool());
1374
1375     query.bindVariable(QLatin1String("node"), nodeModel.root());
1376     QByteArray out;
1377     QBuffer buff(&out);
1378     QVERIFY(buff.open(QIODevice::WriteOnly));
1379
1380     query.setQuery(QLatin1String("declare variable $node external; $node"));
1381     QXmlSerializer serializer(query, &buff);
1382
1383     QVERIFY(query.evaluateTo(&serializer));
1384     QCOMPARE(out, QByteArray("<nodeName/>"));
1385 }
1386
1387 /*!
1388   Pass in a relative URI, and make sure it is resolved against the current application directory.
1389  */
1390 void tst_QXmlQuery::relativeBaseURI() const
1391 {
1392     QXmlQuery query;
1393     query.setQuery(QLatin1String("fn:static-base-uri()"), QUrl(QLatin1String("a/relative/uri.weirdExtension")));
1394     QVERIFY(query.isValid());
1395
1396     QByteArray result;
1397     QBuffer buffer(&result);
1398     QVERIFY(buffer.open(QIODevice::ReadWrite));
1399
1400     QXmlSerializer serializer(query, &buffer);
1401     QVERIFY(query.evaluateTo(&serializer));
1402
1403     const QUrl loaded(QUrl::fromEncoded(result));
1404     QUrl appPath(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()));
1405
1406     QVERIFY(loaded.isValid());
1407     QVERIFY(appPath.isValid());
1408     QVERIFY(!loaded.isRelative());
1409     QVERIFY(!appPath.isRelative());
1410
1411     QFileInfo dir(appPath.toLocalFile());
1412     dir.setFile(QString());
1413
1414     /* We can't use QUrl::isParentOf() because it doesn't do what we want it to */
1415     if(!loaded.toLocalFile().startsWith(dir.absoluteFilePath()))
1416         QTextStream(stderr) << "dir.absoluteFilePath():" << dir.absoluteFilePath() << "loaded.toLocalFile():" << loaded.toLocalFile();
1417
1418     checkBaseURI(loaded, dir.absoluteFilePath());
1419 }
1420
1421 void tst_QXmlQuery::emptyBaseURI() const
1422 {
1423     QXmlQuery query;
1424     query.setQuery(QLatin1String("fn:static-base-uri()"), QUrl());
1425     QVERIFY(query.isValid());
1426
1427     QByteArray result;
1428     QBuffer buffer(&result);
1429     QVERIFY(buffer.open(QIODevice::ReadWrite));
1430
1431     QXmlSerializer serializer(query, &buffer);
1432     QVERIFY(query.evaluateTo(&serializer));
1433
1434     const QUrl loaded(QUrl::fromEncoded(result));
1435     QUrl appPath(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()));
1436
1437     QVERIFY(loaded.isValid());
1438     QVERIFY(appPath.isValid());
1439     QVERIFY(!loaded.isRelative());
1440     QVERIFY(!appPath.isRelative());
1441
1442     QFileInfo dir(appPath.toLocalFile());
1443     dir.setFile(QString());
1444
1445     QCOMPARE(loaded, appPath);
1446 }
1447
1448 /*!
1449   Ensure that QDate comes out as QDateTime.
1450  */
1451 void tst_QXmlQuery::roundTripDateWithinQXmlItem() const
1452 {
1453     const QDate date(1999, 9, 10);
1454     QVERIFY(date.isValid());
1455
1456     const QVariant variant(date);
1457     QVERIFY(variant.isValid());
1458     QCOMPARE(variant.type(), QVariant::Date);
1459
1460     const QXmlItem item(variant);
1461     QVERIFY(!item.isNull());
1462     QVERIFY(item.isAtomicValue());
1463
1464     const QVariant out(item.toAtomicValue());
1465     QVERIFY(out.isValid());
1466     QCOMPARE(out.type(), QVariant::Date);
1467     QCOMPARE(out.toDate(), date);
1468 }
1469
1470 /*!
1471  Check whether a query is valid, which uses an unbound variable.
1472  */
1473 void tst_QXmlQuery::bindingMissing() const
1474 {
1475     QXmlQuery query;
1476     MessageSilencer messageHandler;
1477     query.setMessageHandler(&messageHandler);
1478
1479     QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1480     QVERIFY(queryFile.open(QIODevice::ReadOnly));
1481     query.setQuery(&queryFile);
1482
1483     QVERIFY(!query.isValid());
1484 }
1485
1486 void tst_QXmlQuery::bindDefaultConstructedItem() const
1487 {
1488     QFETCH(QXmlItem, item);
1489
1490     QXmlQuery query;
1491     MessageSilencer messageHandler;
1492     query.setMessageHandler(&messageHandler);
1493
1494     QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1495     QVERIFY(queryFile.open(QIODevice::ReadOnly));
1496     query.setQuery(&queryFile);
1497     query.bindVariable(QLatin1String("externalVariable"), item);
1498
1499     QVERIFY(!query.isValid());
1500 }
1501
1502 void tst_QXmlQuery::bindDefaultConstructedItem_data() const
1503 {
1504     QTest::addColumn<QXmlItem>("item");
1505
1506     QTest::newRow("QXmlItem()") << QXmlItem();
1507     QTest::newRow("QXmlItem(QVariant())") << QXmlItem(QVariant());
1508     QTest::newRow("QXmlItem(QXmlNodeModelIndex())") << QXmlItem(QXmlNodeModelIndex());
1509 }
1510
1511 /*!
1512   Remove a binding by binding QXmlItem() with the same name.
1513  */
1514 void tst_QXmlQuery::eraseQXmlItemBinding() const
1515 {
1516     QXmlQuery query;
1517     MessageSilencer messageHandler;
1518     query.setMessageHandler(&messageHandler);
1519
1520     QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1521     QVERIFY(queryFile.open(QIODevice::ReadOnly));
1522     query.bindVariable(QLatin1String("externalVariable"), QXmlItem(3));
1523     query.setQuery(&queryFile);
1524     QVERIFY(query.isValid());
1525
1526     QByteArray result;
1527     QBuffer buffer(&result);
1528     QVERIFY(buffer.open(QIODevice::ReadWrite));
1529
1530     QXmlSerializer serializer(query, &buffer);
1531     QVERIFY(query.evaluateTo(&serializer));
1532
1533     QCOMPARE(result, QByteArray("3 6<e>3</e>false"));
1534
1535     query.bindVariable(QLatin1String("externalVariable"), QXmlItem());
1536     QVERIFY(!query.isValid());
1537 }
1538
1539 /*!
1540  Erase a variable binding
1541  */
1542 void tst_QXmlQuery::eraseDeviceBinding() const
1543 {
1544     /* Erase an existing QIODevice binding with another QIODevice binding. */
1545     {
1546         QXmlQuery query;
1547
1548         QByteArray doc("<e/>");
1549         QBuffer buffer(&doc);
1550         QVERIFY(buffer.open(QIODevice::ReadOnly));
1551
1552         query.bindVariable(QLatin1String("in"), &buffer);
1553         query.setQuery(QLatin1String("$in"));
1554         QVERIFY(query.isValid());
1555
1556         query.bindVariable(QLatin1String("in"), 0);
1557         QVERIFY(!query.isValid());
1558     }
1559
1560     /* Erase an existing QXmlItem binding with another QIODevice binding. */
1561     {
1562         QXmlQuery query;
1563
1564         query.bindVariable(QLatin1String("in"), QXmlItem(5));
1565         query.setQuery(QLatin1String("$in"));
1566         QVERIFY(query.isValid());
1567
1568         query.bindVariable(QLatin1String("in"), 0);
1569         QVERIFY(!query.isValid());
1570     }
1571 }
1572
1573 /*!
1574  Bind a variable, evaluate, bind with a different value but same type, and evaluate again.
1575  */
1576 void tst_QXmlQuery::rebindVariableSameType() const
1577 {
1578     QXmlQuery query;
1579     MessageSilencer messageHandler;
1580     query.setMessageHandler(&messageHandler);
1581
1582     query.bindVariable(QLatin1String("externalVariable"), QXmlItem(3));
1583
1584     {
1585         QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
1586         QVERIFY(queryFile.open(QIODevice::ReadOnly));
1587         query.setQuery(&queryFile);
1588     }
1589
1590     QVERIFY(query.isValid());
1591
1592     {
1593         QByteArray result;
1594         QBuffer buffer(&result);
1595         QVERIFY(buffer.open(QIODevice::ReadWrite));
1596
1597         QXmlSerializer serializer(query, &buffer);
1598         QVERIFY(query.evaluateTo(&serializer));
1599
1600         QCOMPARE(result, QByteArray("3 6<e>3</e>false"));
1601     }
1602
1603     {
1604         query.bindVariable(QLatin1String("externalVariable"), QXmlItem(5));
1605         QByteArray result;
1606         QBuffer buffer(&result);
1607         QVERIFY(buffer.open(QIODevice::ReadWrite));
1608
1609         QXmlSerializer serializer(query, &buffer);
1610         QVERIFY(query.evaluateTo(&serializer));
1611
1612         QCOMPARE(result, QByteArray("5 8<e>5</e>false"));
1613     }
1614
1615 }
1616
1617 void tst_QXmlQuery::rebindVariableDifferentType() const
1618 {
1619     /* Rebind QXmlItem variable with QXmlItem variable. */
1620     {
1621         QXmlQuery query;
1622         query.bindVariable(QLatin1String("in"), QXmlItem(3));
1623         query.setQuery(QLatin1String("$in"));
1624         QVERIFY(query.isValid());
1625
1626         query.bindVariable(QLatin1String("in"), QXmlItem("A string"));
1627         QVERIFY(!query.isValid());
1628     }
1629
1630     /* Rebind QIODevice variable with QXmlItem variable. */
1631     {
1632         QXmlQuery query;
1633         QBuffer buffer;
1634         buffer.setData(QByteArray("<e/>"));
1635         QVERIFY(buffer.open(QIODevice::ReadOnly));
1636
1637         query.bindVariable(QLatin1String("in"), &buffer);
1638         query.setQuery(QLatin1String("$in"));
1639         QVERIFY(query.isValid());
1640
1641         query.bindVariable(QLatin1String("in"), QXmlItem("A string"));
1642         QVERIFY(!query.isValid());
1643     }
1644
1645     /* Rebind QXmlItem variable with QIODevice variable. The type of the
1646      * variable changes, so a recompile is necessary. */
1647     {
1648         QXmlQuery query;
1649
1650         query.bindVariable(QLatin1String("in"), QXmlItem(QLatin1String("A string")));
1651         query.setQuery(QLatin1String("$in"));
1652         QVERIFY(query.isValid());
1653
1654         QBuffer buffer;
1655         buffer.setData(QByteArray("<e/>"));
1656         QVERIFY(buffer.open(QIODevice::ReadOnly));
1657         query.bindVariable(QLatin1String("in"), &buffer);
1658         QVERIFY(!query.isValid());
1659     }
1660 }
1661
1662 void tst_QXmlQuery::rebindVariableWithNullItem() const
1663 {
1664     QXmlQuery query;
1665
1666     query.bindVariable(QLatin1String("name"), QXmlItem(5));
1667     query.bindVariable(QLatin1String("name"), QXmlItem());
1668 }
1669
1670 void tst_QXmlQuery::constCorrectness() const
1671 {
1672     QXmlResultItems result;
1673     QXmlQuery tmp;
1674     tmp.setQuery(QLatin1String("1")); /* Just so we have a valid query. */
1675     const QXmlQuery query(tmp);
1676
1677     /* These functions should be const. */
1678     query.isValid();
1679     query.evaluateTo(&result);
1680     query.namePool();
1681     query.uriResolver();
1682     query.messageHandler();
1683
1684     {
1685         QString dummyString;
1686         QTextStream dummyStream(&dummyString);
1687         PushBaseliner dummy(dummyStream, query.namePool());
1688         query.evaluateTo(&dummy);
1689     }
1690 }
1691
1692 void tst_QXmlQuery::objectSize() const
1693 {
1694     /* We have a d pointer. */
1695     QCOMPARE(sizeof(QXmlQuery), sizeof(void *));
1696 }
1697
1698 void tst_QXmlQuery::setUriResolver() const
1699 {
1700     /* Set a null resolver, and make sure it can take a const pointer. */
1701     {
1702         QXmlQuery query;
1703         query.setUriResolver(static_cast<const QAbstractUriResolver *>(0));
1704         QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0));
1705     }
1706
1707     {
1708         TestURIResolver resolver;
1709         QXmlQuery query;
1710         query.setUriResolver(&resolver);
1711         QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(&resolver));
1712     }
1713 }
1714
1715 void tst_QXmlQuery::uriResolver() const
1716 {
1717     /* Check default value. */
1718     {
1719         QXmlQuery query;
1720         QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0));
1721     }
1722 }
1723
1724 void tst_QXmlQuery::messageXML() const
1725 {
1726     QXmlQuery query;
1727
1728     MessageValidator messageValidator;
1729     query.setMessageHandler(&messageValidator);
1730
1731     query.setQuery(QLatin1String("1basicSyntaxError"));
1732
1733     const QRegExp removeFilename(QLatin1String("Location: file:.*\\#"));
1734     QVERIFY(removeFilename.isValid());
1735
1736     QVERIFY(messageValidator.success());
1737     QCOMPARE(messageValidator.received().remove(removeFilename),
1738              QString::fromLatin1("Type:3\n"
1739                                  "Description: <html xmlns='http://www.w3.org/1999/xhtml/'><body><p>syntax error, unexpected unknown keyword</p></body></html>\n"
1740                                  "Identifier: http://www.w3.org/2005/xqt-errors#XPST0003\n"
1741                                  "1,1"));
1742 }
1743
1744 /*!
1745   1. Allocate QXmlResultItems
1746   2. Allocate QXmlQuery
1747   3. evaluate to the QXmlResultItems instance
1748   4. Dellocate the QXmlQuery instance
1749   5. Ensure QXmlResultItems works
1750  */
1751 void tst_QXmlQuery::resultItemsDeallocatedQuery() const
1752 {
1753     QXmlResultItems result;
1754
1755     {
1756         QXmlQuery query;
1757         query.setQuery(QLatin1String("1, 2, xs:integer(<e>3</e>)"));
1758         query.evaluateTo(&result);
1759     }
1760
1761     QCOMPARE(result.next().toAtomicValue(), QVariant(1));
1762     QCOMPARE(result.next().toAtomicValue(), QVariant(2));
1763     QCOMPARE(result.next().toAtomicValue(), QVariant(3));
1764     QVERIFY(result.next().isNull());
1765     QVERIFY(!result.hasError());
1766 }
1767
1768 /*!
1769   1. Bind variable with bindVariable()
1770   2. setQuery that has 'declare variable' with same name.
1771   3. Ensure the value inside the query is used. We don't guarantee this behavior
1772      but that's what we lock.
1773  */
1774 void tst_QXmlQuery::shadowedVariables() const
1775 {
1776     QXmlQuery query;
1777     query.bindVariable("varName", QXmlItem(3));
1778     query.setQuery(QLatin1String("declare variable $varName := 5; $varName"));
1779
1780     QXmlResultItems result;
1781     query.evaluateTo(&result);
1782
1783     QCOMPARE(result.next().toAtomicValue(), QVariant(5));
1784 }
1785
1786 void tst_QXmlQuery::setFocusQXmlItem() const
1787 {
1788     /* Make sure we can take a const reference. */
1789     {
1790         QXmlQuery query;
1791         const QXmlItem item;
1792         query.setFocus(item);
1793     }
1794
1795     // TODO evaluate with atomic value, check type
1796     // TODO evaluate with node, check type
1797     // TODO ensure that setFocus() triggers query recompilation, as appropriate.
1798     // TODO let the focus be undefined, call isvalid, call evaluate anyway
1799     // TODO let the focus be undefined, call evaluate directly
1800 }
1801
1802 void tst_QXmlQuery::setFocusQUrl() const
1803 {
1804     /* Load a focus which isn't well-formed. */
1805     {
1806         QXmlQuery query;
1807         MessageSilencer silencer;
1808
1809         query.setMessageHandler(&silencer);
1810
1811         QVERIFY(!query.setFocus(QUrl(QLatin1String("data/notWellformed.xml"))));
1812     }
1813
1814     /* Ensure the same URI resolver is used. */
1815     {
1816         QXmlQuery query(QXmlQuery::XSLT20);
1817
1818         const TestURIResolver resolver(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
1819         query.setUriResolver(&resolver);
1820
1821         QVERIFY(query.setFocus(QUrl(QLatin1String("arbitraryURI"))));
1822         query.setQuery(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/copyWholeDocument.xsl"))));
1823         QVERIFY(query.isValid());
1824
1825         QBuffer result;
1826         QVERIFY(result.open(QIODevice::ReadWrite));
1827         QXmlSerializer serializer(query, &result);
1828         query.evaluateTo(&serializer);
1829
1830         QCOMPARE(result.data(), QByteArray("<doc/>"));
1831     }
1832
1833     // TODO ensure that the focus type doesn't change from XSLT20 on the main instance.
1834 }
1835
1836 /*!
1837   This code poses a challenge wrt. to internal caching.
1838  */
1839 void tst_QXmlQuery::setFocusQIODevice() const
1840 {
1841     QXmlQuery query;
1842
1843     {
1844         QBuffer focus;
1845         focus.setData(QByteArray("<e>abc</e>"));
1846         QVERIFY(focus.open(QIODevice::ReadOnly));
1847         query.setFocus(&focus);
1848         query.setQuery(QLatin1String("string()"));
1849         QVERIFY(query.isValid());
1850
1851         QString output;
1852         query.evaluateTo(&output);
1853
1854         QCOMPARE(output, QString::fromLatin1("abc\n"));
1855     }
1856
1857     /* Set a new focus, make sure it changes & works. */
1858     {
1859         QBuffer focus2;
1860         focus2.setData(QByteArray("<e>abc2</e>"));
1861         QVERIFY(focus2.open(QIODevice::ReadOnly));
1862         query.setFocus(&focus2);
1863         QVERIFY(query.isValid());
1864
1865         QString output;
1866         query.evaluateTo(&output);
1867
1868         QCOMPARE(output, QString::fromLatin1("abc2\n"));
1869     }
1870 }
1871
1872 /*!
1873  Since we internally use variable bindings for implementing the focus, we need
1874  to make sure we don't clash in this area.
1875 */
1876 void tst_QXmlQuery::setFocusQIODeviceAvoidVariableClash() const
1877 {
1878     QBuffer buffer;
1879     buffer.setData("<e>focus</e>");
1880     QVERIFY(buffer.open(QIODevice::ReadOnly));
1881
1882     /* First we bind the variable name, then the focus. */
1883     {
1884         QXmlQuery query;
1885         query.bindVariable(QString(QLatin1Char('u')), QVariant(1));
1886         query.setFocus(&buffer);
1887         query.setQuery(QLatin1String("string()"));
1888
1889         QString out;
1890         query.evaluateTo(&out);
1891
1892         QCOMPARE(out, QString::fromLatin1("focus\n"));
1893     }
1894
1895     /* First we bind the focus, then the variable name. */
1896     {
1897         QXmlQuery query;
1898         QVERIFY(buffer.open(QIODevice::ReadOnly));
1899         query.setFocus(&buffer);
1900         query.bindVariable(QString(QLatin1Char('u')), QVariant(1));
1901         query.setQuery(QLatin1String("string()"));
1902
1903         QString out;
1904         query.evaluateTo(&out);
1905
1906         QCOMPARE(out, QString::fromLatin1("focus\n"));
1907     }
1908 }
1909
1910 void tst_QXmlQuery::setFocusQIODeviceFailure() const
1911 {
1912     /* A not well-formed input document. */
1913     {
1914         QXmlQuery query;
1915
1916         MessageSilencer silencer;
1917         query.setMessageHandler(&silencer);
1918
1919         QBuffer input;
1920         input.setData("<e");
1921         QVERIFY(input.open(QIODevice::ReadOnly));
1922
1923         QCOMPARE(query.setFocus(&input), false);
1924     }
1925 }
1926
1927 void tst_QXmlQuery::setFocusQString() const
1928 {
1929     QXmlQuery query;
1930
1931     /* Basic use of focus. */
1932     {
1933         QVERIFY(query.setFocus(QLatin1String("<e>textNode</e>")));
1934         query.setQuery(QLatin1String("string()"));
1935         QVERIFY(query.isValid());
1936         QString out;
1937         query.evaluateTo(&out);
1938         QCOMPARE(out, QString::fromLatin1("textNode\n"));
1939     }
1940
1941     /* Set to a new focus, make sure it changes and works. */
1942     {
1943         QVERIFY(query.setFocus(QLatin1String("<e>newFocus</e>")));
1944         QString out;
1945         query.evaluateTo(&out);
1946         QCOMPARE(out, QString::fromLatin1("newFocus\n"));
1947     }
1948 }
1949
1950 void tst_QXmlQuery::setFocusQStringFailure() const
1951 {
1952     QXmlQuery query;
1953     MessageSilencer silencer;
1954
1955     query.setMessageHandler(&silencer);
1956     QVERIFY(!query.setFocus(QLatin1String("<notWellformed")));
1957
1958     /* Let's try the slight special case of a null string. */
1959     QVERIFY(!query.setFocus(QString()));
1960 }
1961
1962 void tst_QXmlQuery::setFocusQStringSignature() const
1963 {
1964     QXmlQuery query;
1965     MessageSilencer silencer;
1966     query.setMessageHandler(&silencer);
1967
1968     const QString argument;
1969     /* We should take a const ref. */
1970     query.setFocus(argument);
1971
1972     /* We should return a bool. */
1973     static_cast<bool>(query.setFocus(QString()));
1974 }
1975
1976 void tst_QXmlQuery::setFocusQIODeviceTriggerWarnings() const
1977 {
1978     /* A null pointer. */
1979     {
1980         QXmlQuery query;
1981
1982         QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed.");
1983         QCOMPARE(query.setFocus(static_cast<QIODevice *>(0)), false);
1984     }
1985
1986     /* A non opened-device. */
1987     {
1988         QXmlQuery query;
1989
1990         QBuffer notReadable;
1991         QVERIFY(!notReadable.isReadable());
1992
1993         QTest::ignoreMessage(QtWarningMsg, "The device must be readable.");
1994         QCOMPARE(query.setFocus(&notReadable), false);
1995     }
1996 }
1997
1998 void tst_QXmlQuery::fnDocNetworkAccessSuccess() const
1999 {
2000 #if defined(Q_OS_WINCE) && !defined(_X86_)
2001     QStringList testsToSkip;
2002     testsToSkip << "http scheme" << "ftp scheme";
2003     if (testsToSkip.contains(QTest::currentDataTag()))
2004         QSKIP("Network tests are currently unsupported on Windows CE.", SkipSingle);
2005 #endif
2006
2007     QFETCH(QUrl, uriToOpen);
2008     QFETCH(QByteArray, expectedOutput);
2009
2010     if(!uriToOpen.isValid())
2011         qDebug() << "uriToOpen:" << uriToOpen;
2012
2013     QVERIFY(uriToOpen.isValid());
2014
2015     QXmlQuery query;
2016     query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen));
2017     query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)"));
2018     QVERIFY(query.isValid());
2019
2020     QByteArray result;
2021     QBuffer buffer(&result);
2022     QVERIFY(buffer.open(QIODevice::WriteOnly));
2023
2024     QXmlSerializer serializer(query, &buffer);
2025     QVERIFY(query.evaluateTo(&serializer));
2026
2027     QCOMPARE(result, expectedOutput);
2028 }
2029
2030 void tst_QXmlQuery::fnDocNetworkAccessSuccess_data() const
2031 {
2032     QTest::addColumn<QUrl>("uriToOpen");
2033     QTest::addColumn<QByteArray>("expectedOutput");
2034
2035     QTest::newRow("file scheme")
2036         << inputFileAsURI(QLatin1String(SRCDIR "input.xml"))
2037         << QByteArray("<!-- This is just a file for testing. --><input/>");
2038
2039     QTest::newRow("data scheme with ASCII")
2040         /* QUrl::toPercentEncoding(QLatin1String("<e/>")) yields "%3Ce%2F%3E". */
2041         << QUrl::fromEncoded("data:application/xml,%3Ce%2F%3E")
2042         << QByteArray("<e/>");
2043
2044     QTest::newRow("data scheme with ASCII no MIME type")
2045         << QUrl::fromEncoded("data:,%3Ce%2F%3E")
2046         << QByteArray("<e/>");
2047
2048     QTest::newRow("data scheme with base 64")
2049         << QUrl::fromEncoded("data:application/xml;base64,PGUvPg==")
2050         << QByteArray("<e/>");
2051
2052     QTest::newRow("qrc scheme")
2053         << QUrl::fromEncoded("qrc:/QXmlQueryTestData/data/oneElement.xml")
2054         << QByteArray("<oneElement/>");
2055
2056     if(!m_testNetwork)
2057         return;
2058
2059     QTest::newRow("http scheme")
2060         << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/wellFormed.xml"))
2061         << QByteArray("<!-- a comment --><e from=\"http\">Some Text</e>");
2062
2063     QTest::newRow("ftp scheme")
2064         << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/wellFormed.xml"))
2065         << QByteArray("<!-- a comment --><e from=\"ftp\">Some Text</e>");
2066
2067 }
2068
2069 void tst_QXmlQuery::fnDocNetworkAccessFailure() const
2070 {
2071     QFETCH(QUrl, uriToOpen);
2072
2073     QVERIFY(uriToOpen.isValid());
2074
2075     QXmlQuery query;
2076     MessageSilencer silencer;
2077     query.setMessageHandler(&silencer);
2078     query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen));
2079     query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)"));
2080     QVERIFY(query.isValid());
2081
2082     QXmlResultItems result;
2083     query.evaluateTo(&result);
2084
2085     while(!result.next().isNull())
2086     {
2087         /* Just loop until the end. */
2088     }
2089
2090     // TODO do something that triggers a /timeout/.
2091     QVERIFY(result.hasError());
2092 }
2093
2094 void tst_QXmlQuery::fnDocNetworkAccessFailure_data() const
2095 {
2096     QTest::addColumn<QUrl>("uriToOpen");
2097
2098     QTest::newRow("data scheme, not-well-formed")
2099         << QUrl(QLatin1String("data:application/xml;base64,PGUvg==="));
2100
2101     QTest::newRow("file scheme, non-existant file")
2102         << QUrl(QLatin1String("file:///example.com/does/notExist.xml"));
2103
2104     QTest::newRow("http scheme, file not found")
2105         << QUrl(QLatin1String("http://www.example.com/does/not/exist.xml"));
2106
2107     QTest::newRow("http scheme, nonexistent host")
2108         << QUrl(QLatin1String("http://this.host.does.not.exist.I.SWear"));
2109
2110     QTest::newRow("qrc scheme, not well-formed")
2111         << QUrl(QLatin1String("qrc:/QXmlQueryTestData/notWellformed.xml"));
2112
2113     QTest::newRow("'qrc:/', non-existing file")
2114         << QUrl(QLatin1String(":/QXmlQueryTestData/data/thisFileDoesNotExist.xml"));
2115
2116     QTest::newRow("':/', this scheme is not supported")
2117         << QUrl(QLatin1String(":/QXmlQueryTestData/data/notWellformed.xml"));
2118
2119     if(!m_testNetwork)
2120         return;
2121
2122     QTest::newRow("http scheme, not well-formed")
2123         << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/notWellformed.xml"));
2124
2125     QTest::newRow("https scheme, not well-formed")
2126         << QUrl(QString("https://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/notWellformedViaHttps.xml"));
2127
2128     QTest::newRow("https scheme, nonexistent host")
2129         << QUrl(QLatin1String("https://this.host.does.not.exist.I.SWear"));
2130
2131     QTest::newRow("ftp scheme, nonexistent host")
2132         << QUrl(QLatin1String("ftp://this.host.does.not.exist.I.SWear"));
2133
2134     QTest::newRow("ftp scheme, not well-formed")
2135         << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/notWellFormed.xml"));
2136 }
2137
2138 /*!
2139   Create a network timeout from a QIODevice binding such
2140   that we ensure we don't hang infinitely.
2141  */
2142 void tst_QXmlQuery::fnDocOnQIODeviceTimeout() const
2143 {
2144     if(!m_testNetwork)
2145         return;
2146
2147     QTcpServer server;
2148     server.listen(QHostAddress::LocalHost, 1088);
2149
2150     QTcpSocket client;
2151     client.connectToHost("localhost", 1088);
2152     QVERIFY(client.isReadable());
2153
2154     QXmlQuery query;
2155
2156     MessageSilencer silencer;
2157     query.setMessageHandler(&silencer);
2158
2159     query.bindVariable(QLatin1String("inDevice"), &client);
2160     query.setQuery(QLatin1String("declare variable $inDevice external;\ndoc($inDevice)"));
2161     QVERIFY(query.isValid());
2162
2163     QXmlResultItems result;
2164     query.evaluateTo(&result);
2165     QXmlItem next(result.next());
2166
2167     while(!next.isNull())
2168     {
2169         next = result.next();
2170     }
2171
2172     QVERIFY(result.hasError());
2173 }
2174
2175 /*!
2176  When changing query, the static context must change too, such that
2177  the source locations are updated.
2178  */
2179 void tst_QXmlQuery::recompilationWithEvaluateToResultFailing() const
2180 {
2181     QXmlQuery query;
2182     MessageSilencer silencer;
2183     query.setMessageHandler(&silencer);
2184
2185     query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2186     QVERIFY(query.isValid()); /* Trigger query compilation. */
2187
2188     query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2189     QVERIFY(query.isValid()); /* Trigger second compilation. */
2190
2191     QXmlResultItems items;
2192     query.evaluateTo(&items);
2193     items.next();
2194     QVERIFY(items.hasError());
2195 }
2196
2197 void tst_QXmlQuery::secondEvaluationWithEvaluateToResultFailing() const
2198 {
2199     QXmlQuery query;
2200     MessageSilencer silencer;
2201     query.setMessageHandler(&silencer);
2202
2203     query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2204     QVERIFY(query.isValid()); /* Trigger query compilation. */
2205
2206     query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2207     /* We don't call isValid(). */
2208 QXmlResultItems items;
2209     query.evaluateTo(&items);
2210     items.next();
2211     QVERIFY(items.hasError());
2212 }
2213
2214 /*!
2215  Compilation is triggered in the evaluation function due to no call to QXmlQuery::isValid().
2216  */
2217 void tst_QXmlQuery::recompilationWithEvaluateToReceiver() const
2218 {
2219     QXmlQuery query;
2220     MessageSilencer silencer;
2221     query.setMessageHandler(&silencer);
2222
2223     query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
2224     QVERIFY(query.isValid()); /* Trigger query compilation. */
2225
2226     query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
2227     /* We don't call isValid(). */
2228
2229     QByteArray dummy;
2230     QBuffer buffer(&dummy);
2231     buffer.open(QIODevice::WriteOnly);
2232
2233     QXmlSerializer serializer(query, &buffer);
2234
2235     QVERIFY(!query.evaluateTo(&serializer));
2236 }
2237
2238 void tst_QXmlQuery::evaluateToQStringListOnInvalidQuery() const
2239 {
2240     MessageSilencer silencer;
2241
2242     /* Invoke on a default constructed object. */
2243     {
2244         QXmlQuery query;
2245         QStringList out;
2246         QVERIFY(!query.evaluateTo(&out));
2247     }
2248
2249     /* Invoke on a syntactically invalid query. */
2250     {
2251         QXmlQuery query;
2252         QStringList out;
2253         MessageSilencer silencer;
2254
2255         query.setMessageHandler(&silencer);
2256         query.setQuery(QLatin1String("1 + "));
2257
2258         QVERIFY(!query.evaluateTo(&out));
2259     }
2260
2261     /* Invoke on a query with the wrong type, one atomic. */
2262     {
2263         QXmlQuery query;
2264         QStringList out;
2265
2266         query.setQuery(QLatin1String("1"));
2267         query.setMessageHandler(&silencer);
2268         QVERIFY(!query.evaluateTo(&out));
2269     }
2270
2271     /* Invoke on a query with the wrong type, one element. */
2272     {
2273         QXmlQuery query;
2274         QStringList out;
2275
2276         query.setQuery(QLatin1String("<e/>"));
2277         QVERIFY(!query.evaluateTo(&out));
2278     }
2279
2280     /* Invoke on a query with the wrong type, mixed nodes & atomics. */
2281     {
2282         QXmlQuery query;
2283         QStringList out;
2284
2285         query.setQuery(QLatin1String("<e/>, 1, 'a string'"));
2286         query.setMessageHandler(&silencer);
2287         QVERIFY(!query.evaluateTo(&out));
2288     }
2289
2290     /* Evaluate the empty sequence. */
2291     {
2292         QXmlQuery query;
2293         QStringList out;
2294
2295         query.setQuery(QLatin1String("()"));
2296         QVERIFY(!query.evaluateTo(&out));
2297         QVERIFY(out.isEmpty());
2298     }
2299 }
2300
2301 void tst_QXmlQuery::evaluateToQStringList() const
2302 {
2303     QFETCH(QString, queryString);
2304     QFETCH(QStringList, expectedOutput);
2305
2306     QXmlQuery query;
2307     query.setQuery(queryString);
2308     QStringList out;
2309     QVERIFY(query.isValid());
2310
2311     QVERIFY(query.evaluateTo(&out));
2312
2313     QCOMPARE(out, expectedOutput);
2314 }
2315
2316 void tst_QXmlQuery::evaluateToQStringListTriggerWarnings() const
2317 {
2318     QXmlQuery query;
2319
2320     QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed.");
2321     QCOMPARE(query.evaluateTo(static_cast<QStringList *>(0)),
2322              false);
2323 }
2324
2325 void tst_QXmlQuery::evaluateToQStringList_data() const
2326 {
2327     QTest::addColumn<QString>("queryString");
2328     QTest::addColumn<QStringList>("expectedOutput");
2329
2330     QTest::newRow("One atomic")
2331         << QString::fromLatin1("(1 + 1) cast as xs:string")
2332         << QStringList(QString::fromLatin1("2"));
2333
2334     {
2335         QStringList expected;
2336         expected << QLatin1String("2");
2337         expected << QLatin1String("a string");
2338
2339         QTest::newRow("Two atomics")
2340             << QString::fromLatin1("(1 + 1) cast as xs:string, 'a string'")
2341             << expected;
2342     }
2343
2344     QTest::newRow("A query which evaluates to sub-types of xs:string.")
2345         << QString::fromLatin1("xs:NCName('NCName'), xs:normalizedString('  a b c   ')")
2346         << (QStringList() << QString::fromLatin1("NCName")
2347                           << QString::fromLatin1("  a b c   "));
2348
2349     QTest::newRow("A query which evaluates to two elements.")
2350         << QString::fromLatin1("string(<e>theString1</e>), string(<e>theString2</e>)")
2351         << (QStringList() << QString::fromLatin1("theString1")
2352                           << QString::fromLatin1("theString2"));
2353 }
2354
2355 /*!
2356   Ensure that we don't automatically convert non-xs:string values.
2357  */
2358 void tst_QXmlQuery::evaluateToQStringListNoConversion() const
2359 {
2360     QXmlQuery query;
2361     query.setQuery(QString::fromLatin1("<e/>"));
2362     QVERIFY(query.isValid());
2363     QStringList result;
2364     QVERIFY(!query.evaluateTo(&result));
2365 }
2366
2367 void tst_QXmlQuery::evaluateToQIODevice() const
2368 {
2369     /* an XQuery, check that no indentation is performed. */
2370     {
2371         QBuffer out;
2372         QVERIFY(out.open(QIODevice::ReadWrite));
2373
2374         QXmlQuery query;
2375         query.setQuery(QLatin1String("<a><b/></a>"));
2376         QVERIFY(query.isValid());
2377         QVERIFY(query.evaluateTo(&out));
2378         QCOMPARE(out.data(), QByteArray("<a><b/></a>"));
2379     }
2380 }
2381
2382 void tst_QXmlQuery::evaluateToQIODeviceTriggerWarnings() const
2383 {
2384     QXmlQuery query;
2385
2386     QTest::ignoreMessage(QtWarningMsg, "The pointer to the device cannot be null.");
2387     QCOMPARE(query.evaluateTo(static_cast<QIODevice *>(0)),
2388              false);
2389
2390     QBuffer buffer;
2391
2392     QTest::ignoreMessage(QtWarningMsg, "The device must be writable.");
2393     QCOMPARE(query.evaluateTo(&buffer),
2394              false);
2395 }
2396
2397 void tst_QXmlQuery::evaluateToQIODeviceSignature() const
2398 {
2399     /* The function should be const. */
2400     {
2401         QBuffer out;
2402         QVERIFY(out.open(QIODevice::ReadWrite));
2403
2404         const QXmlQuery query;
2405
2406         query.evaluateTo(&out);
2407     }
2408 }
2409
2410 void tst_QXmlQuery::evaluateToQIODeviceOnInvalidQuery() const
2411 {
2412     QBuffer out;
2413     QVERIFY(out.open(QIODevice::WriteOnly));
2414
2415     /* On syntactically invalid query. */
2416     {
2417         QXmlQuery query;
2418         MessageSilencer silencer;
2419         query.setMessageHandler(&silencer);
2420         query.setQuery(QLatin1String("1 +"));
2421         QVERIFY(!query.isValid());
2422         QVERIFY(!query.evaluateTo(&out));
2423     }
2424
2425     /* On null QXmlQuery instance. */
2426     {
2427         QXmlQuery query;
2428         QVERIFY(!query.evaluateTo(&out));
2429     }
2430
2431 }
2432
2433 void tst_QXmlQuery::setQueryQIODeviceQUrl() const
2434 {
2435     /* Basic test. */
2436     {
2437         QBuffer buffer;
2438         buffer.setData("1, 2, 2 + 1");
2439         QVERIFY(buffer.open(QIODevice::ReadOnly));
2440
2441         QXmlQuery query;
2442         query.setQuery(&buffer);
2443         QVERIFY(query.isValid());
2444
2445         QXmlResultItems result;
2446         query.evaluateTo(&result);
2447         QCOMPARE(result.next().toAtomicValue(), QVariant(1));
2448         QCOMPARE(result.next().toAtomicValue(), QVariant(2));
2449         QCOMPARE(result.next().toAtomicValue(), QVariant(3));
2450         QVERIFY(result.next().isNull());
2451         QVERIFY(!result.hasError());
2452     }
2453
2454     /* Set query that is invalid. */
2455     {
2456         QBuffer buffer;
2457         buffer.setData("1, ");
2458         QVERIFY(buffer.open(QIODevice::ReadOnly));
2459
2460         QXmlQuery query;
2461         MessageSilencer silencer;
2462         query.setMessageHandler(&silencer);
2463         query.setQuery(&buffer);
2464         QVERIFY(!query.isValid());
2465     }
2466
2467     /* Check that the base URI passes through. */
2468     {
2469         QBuffer buffer;
2470         buffer.setData("string(static-base-uri())");
2471         QVERIFY(buffer.open(QIODevice::ReadOnly));
2472
2473         QXmlQuery query;
2474         query.setQuery(&buffer, QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl"));
2475         QVERIFY(query.isValid());
2476
2477         QStringList result;
2478         query.evaluateTo(&result);
2479         QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
2480     }
2481 }
2482
2483 void tst_QXmlQuery::setQueryQIODeviceQUrlTriggerWarnings() const
2484 {
2485     QXmlQuery query;
2486     QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed.");
2487     query.setQuery(0);
2488
2489     QBuffer buffer;
2490     QTest::ignoreMessage(QtWarningMsg, "The device must be readable.");
2491     query.setQuery(&buffer);
2492 }
2493
2494 void tst_QXmlQuery::setQueryQString() const
2495 {
2496     /* Basic test. */
2497     {
2498         QXmlQuery query;
2499         query.setQuery(QLatin1String("1, 2, 2 + 1"));
2500         QVERIFY(query.isValid());
2501
2502         QXmlResultItems result;
2503         query.evaluateTo(&result);
2504         QCOMPARE(result.next().toAtomicValue(), QVariant(1));
2505         QCOMPARE(result.next().toAtomicValue(), QVariant(2));
2506         QCOMPARE(result.next().toAtomicValue(), QVariant(3));
2507         QVERIFY(result.next().isNull());
2508         QVERIFY(!result.hasError());
2509     }
2510
2511     /* Set query that is invalid. */
2512     {
2513         MessageSilencer silencer;
2514         QXmlQuery query;
2515         query.setMessageHandler(&silencer);
2516         query.setQuery(QLatin1String("1, "));
2517         QVERIFY(!query.isValid());
2518     }
2519
2520     /* Check that the base URI passes through. */
2521     {
2522         QXmlQuery query;
2523         query.setQuery(QLatin1String("string(static-base-uri())"), QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl"));
2524         QVERIFY(query.isValid());
2525
2526         QStringList result;
2527         query.evaluateTo(&result);
2528         QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
2529     }
2530 }
2531
2532 void tst_QXmlQuery::setQueryQUrlSuccess() const
2533 {
2534 #if defined(Q_OS_WINCE) && !defined(_X86_)
2535     QStringList testsToSkip;
2536     testsToSkip << "A valid query via the ftp scheme" << "A valid query via the http scheme";
2537     if (testsToSkip.contains(QTest::currentDataTag()))
2538         QSKIP("Network tests are currently unsupported on Windows CE.", SkipSingle);
2539 #endif
2540
2541     QFETCH(QUrl, queryURI);
2542     QFETCH(QByteArray, expectedOutput);
2543
2544     QVERIFY(queryURI.isValid());
2545
2546     QXmlQuery query;
2547
2548     MessageSilencer silencer;
2549     query.setMessageHandler(&silencer);
2550
2551     query.setQuery(queryURI);
2552     QVERIFY(query.isValid());
2553
2554     QByteArray out;
2555     QBuffer buffer(&out);
2556     QVERIFY(buffer.open(QIODevice::WriteOnly));
2557     QXmlSerializer serializer(query, &buffer);
2558
2559     query.evaluateTo(&serializer);
2560     QCOMPARE(out, expectedOutput);
2561 }
2562
2563 void tst_QXmlQuery::setQueryQUrlSuccess_data() const
2564 {
2565     QTest::addColumn<QUrl>("queryURI");
2566     QTest::addColumn<QByteArray>("expectedOutput");
2567
2568     QTest::newRow("A valid query via the data scheme")
2569         << QUrl::fromEncoded("data:application/xml,1%20%2B%201") /* "1 + 1" */
2570         << QByteArray("2");
2571
2572     QTest::newRow("A valid query via the file scheme")
2573         << QUrl::fromLocalFile(inputFile(QLatin1String(queriesDirectory) + QLatin1String("onePlusOne.xq")))
2574         << QByteArray("2");
2575
2576     if(!m_testNetwork)
2577         return;
2578
2579     QTest::newRow("A valid query via the ftp scheme")
2580         << QUrl::fromEncoded(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/viaFtp.xq").toLatin1())
2581         << QByteArray("This was received via FTP");
2582
2583     QTest::newRow("A valid query via the http scheme")
2584         << QUrl::fromEncoded(QString("http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/viaHttp.xq").toLatin1())
2585         << QByteArray("This was received via HTTP.");
2586 }
2587
2588 void tst_QXmlQuery::setQueryQUrlFailSucceed() const
2589 {
2590     QXmlQuery query;
2591     MessageSilencer silencer;
2592
2593     query.setMessageHandler(&silencer);
2594
2595     query.setQuery(QLatin1String("1 + 1"));
2596     QVERIFY(query.isValid());
2597
2598     query.setQuery(QUrl::fromEncoded("file://example.com/does/not/exist"));
2599     QVERIFY(!query.isValid());
2600 }
2601
2602 void tst_QXmlQuery::setQueryQUrlFailure() const
2603 {
2604     QFETCH(QUrl, queryURI);
2605
2606     MessageSilencer silencer;
2607
2608     QXmlQuery query;
2609     query.setMessageHandler(&silencer);
2610     query.setQuery(queryURI);
2611     QVERIFY(!query.isValid());
2612 }
2613
2614 void tst_QXmlQuery::setQueryQUrlFailure_data() const
2615 {
2616     QTest::addColumn<QUrl>("queryURI");
2617
2618     QTest::newRow("Query via file:// that does not exist.")
2619         << QUrl::fromEncoded("file://example.com/does/not/exist");
2620
2621     QTest::newRow("A query via file:// that is completely empty, but readable.")
2622         << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("../xmlpatterns/queries/completelyEmptyQuery.xq"));
2623
2624     {
2625         const QString name(QLatin1String("nonReadableFile.xq"));
2626         QFile outFile(name);
2627         QVERIFY(outFile.open(QIODevice::WriteOnly));
2628         outFile.write(QByteArray("1"));
2629         outFile.close();
2630         /* On some windows versions, this fails, so we don't check that this works with QVERIFY. */
2631         outFile.setPermissions(QFile::Permissions(QFile::Permissions()));
2632
2633         QTest::newRow("Query via file:/ that does not have read permissions.")
2634             << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("nonReadableFile.xq"));
2635     }
2636
2637     if(!m_testNetwork)
2638         return;
2639
2640     QTest::newRow("Query via HTTP that does not exist.")
2641         << QUrl::fromEncoded("http://example.com/NoQuery/ISWear");
2642
2643     /*
2644     QTest::newRow("Query via FTP that does not exist.")
2645         << QUrl::fromEncoded("ftp://example.com/NoQuery/ISWear");
2646         */
2647
2648     QTest::newRow("A query via http:// that is completely empty, but readable.")
2649         << QUrl::fromEncoded(QString(
2650                 "http://" + QtNetworkSettings::serverName() + "/qtest/qxmlquery/completelyEmptyQuery.xq").toLatin1());
2651
2652     QTest::newRow("A query via ftp:// that is completely empty, but readable.")
2653         << QUrl::fromEncoded(QString(
2654                 "ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/completelyEmptyQuery.xq").toLatin1());
2655
2656 }
2657
2658 void tst_QXmlQuery::setQueryQUrlBaseURI() const
2659 {
2660     QFETCH(QUrl, inputBaseURI);
2661     QFETCH(QUrl, expectedBaseURI);
2662
2663     QXmlQuery query;
2664
2665     query.setQuery(QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq")), inputBaseURI);
2666     QVERIFY(query.isValid());
2667
2668     QStringList result;
2669     QVERIFY(query.evaluateTo(&result));
2670     QCOMPARE(result.count(), 1);
2671
2672     if(qstrcmp(QTest::currentDataTag(), "Relative base URI") == 0)
2673         checkBaseURI(QUrl(result.first()), QCoreApplication::applicationFilePath());
2674     else
2675         QCOMPARE(result.first(), expectedBaseURI.toString());
2676 }
2677
2678 void tst_QXmlQuery::setQueryQUrlBaseURI_data() const
2679 {
2680     QTest::addColumn<QUrl>("inputBaseURI");
2681     QTest::addColumn<QUrl>("expectedBaseURI");
2682
2683     QTest::newRow("absolute HTTP")
2684         << QUrl(QLatin1String("http://www.example.com/"))
2685         << QUrl(QLatin1String("http://www.example.com/"));
2686
2687     QTest::newRow("None, so the query URI is used")
2688         << QUrl()
2689         << QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq"));
2690
2691     QTest::newRow("Relative base URI")
2692         << QUrl(QLatin1String("../data/relative.uri"))
2693         << QUrl();
2694 }
2695
2696 /*!
2697   1. Create a valid query.
2698   2. Call setQuery(QUrl), with a query file that doesn't exist.
2699   3. Verify that the query has changed state into invalid.
2700  */
2701 void tst_QXmlQuery::setQueryWithNonExistentQUrlOnValidQuery() const
2702 {
2703     QXmlQuery query;
2704
2705     MessageSilencer messageSilencer;
2706     query.setMessageHandler(&messageSilencer);
2707
2708     query.setQuery(QLatin1String("1 + 1"));
2709     QVERIFY(query.isValid());
2710
2711     query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/DOESNOTEXIST.xq"));
2712     QVERIFY(!query.isValid());
2713 }
2714
2715 /*!
2716   1. Create a valid query.
2717   2. Call setQuery(QUrl), with a query file that is invalid.
2718   3. Verify that the query has changed state into invalid.
2719  */
2720 void tst_QXmlQuery::setQueryWithInvalidQueryFromQUrlOnValidQuery() const
2721 {
2722     QXmlQuery query;
2723
2724     MessageSilencer messageSilencer;
2725     query.setMessageHandler(&messageSilencer);
2726
2727     query.setQuery(QLatin1String("1 + 1"));
2728     QVERIFY(query.isValid());
2729
2730     query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/queries/syntaxError.xq"));
2731     QVERIFY(!query.isValid());
2732 }
2733
2734 /*!
2735   This triggered two bugs:
2736
2737   - First, the DynamicContext wasn't assigned to QXmlResultItems, meaning it went out of
2738     scope and therefore deallocated the document pool, and calls
2739     to QXmlResultItems::next() would use dangling pointers.
2740
2741   - Conversion between QPatternist::Item and QXmlItem was incorrectly done, leading to nodes
2742     being treated as atomic values, and subsequent crashes.
2743
2744  */
2745 void tst_QXmlQuery::retrieveNameFromQuery() const
2746 {
2747     QFETCH(QString, queryString);
2748     QFETCH(QString, expectedName);
2749
2750     QXmlQuery query;
2751     query.setQuery(queryString);
2752     QVERIFY(query.isValid());
2753     QXmlResultItems result;
2754     query.evaluateTo(&result);
2755
2756     QVERIFY(!result.hasError());
2757
2758     const QXmlItem item(result.next());
2759     QVERIFY(!result.hasError());
2760     QVERIFY(!item.isNull());
2761     QVERIFY(item.isNode());
2762
2763     const QXmlNodeModelIndex node(item.toNodeModelIndex());
2764     QVERIFY(!node.isNull());
2765
2766     QCOMPARE(node.model()->name(node).localName(query.namePool()), expectedName);
2767 }
2768
2769 void tst_QXmlQuery::retrieveNameFromQuery_data() const
2770 {
2771     QTest::addColumn<QString>("queryString");
2772     QTest::addColumn<QString>("expectedName");
2773
2774     QTest::newRow("Document-node")
2775         << QString::fromLatin1("document{<elementName/>}")
2776         << QString();
2777
2778     QTest::newRow("Element")
2779         << QString::fromLatin1("document{<elementName/>}/*")
2780         << QString::fromLatin1("elementName");
2781 }
2782
2783 /*!
2784  Binding a null QString leads to no variable binding, but an
2785  empty non-null QString is possible.
2786  */
2787 void tst_QXmlQuery::bindEmptyNullString() const
2788 {
2789     MessageSilencer messageHandler;
2790     QXmlQuery query;
2791     query.setMessageHandler(&messageHandler);
2792     query.setQuery(QLatin1String("declare variable $v external; $v"));
2793     /* Here, we effectively pass an invalid QVariant. */
2794     query.bindVariable(QLatin1String("v"), QVariant(QString()));
2795     QVERIFY(!query.isValid());
2796
2797     QStringList result;
2798     QVERIFY(!query.evaluateTo(&result));
2799 }
2800
2801 void tst_QXmlQuery::bindEmptyString() const
2802 {
2803     QXmlQuery query;
2804     query.bindVariable(QLatin1String("v"), QVariant(QString(QLatin1String(""))));
2805     query.setQuery(QLatin1String("declare variable $v external; $v"));
2806     QVERIFY(query.isValid());
2807
2808     QStringList result;
2809     QVERIFY(query.evaluateTo(&result));
2810     QStringList expected((QString()));
2811     QCOMPARE(result, expected);
2812 }
2813
2814 void tst_QXmlQuery::cleanupTestCase() const
2815 {
2816     /* Remove a weird file we created. */
2817     const QString name(QLatin1String("nonReadableFile.xq"));
2818
2819     if(QFile::exists(name))
2820     {
2821         QFile file(name);
2822         QVERIFY(file.setPermissions(QFile::WriteOwner));
2823         QVERIFY(file.remove());
2824     }
2825 }
2826
2827 void tst_QXmlQuery::declareUnavailableExternal() const
2828 {
2829     QXmlQuery query;
2830     MessageSilencer silencer;
2831     query.setMessageHandler(&silencer);
2832     query.setQuery(QLatin1String("declare variable $var external;"
2833                                  "1 + 1"));
2834     /* We do not bind $var with QXmlQuery::bindVariable(). */
2835     QVERIFY(!query.isValid());
2836 }
2837
2838 /*!
2839  This test triggers an assert in one of the cache iterator
2840  with MSVC 2005 when compiled in debug mode.
2841  */
2842 void tst_QXmlQuery::msvcCacheIssue() const
2843 {
2844     QXmlQuery query;
2845     query.bindVariable(QLatin1String("externalVariable"), QXmlItem("Variable Value"));
2846     query.setQuery(QUrl::fromLocalFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariableUsedTwice.xq")));
2847     QStringList result;
2848     QVERIFY(query.evaluateTo(&result));
2849
2850     QCOMPARE(result,
2851              QStringList() << QString::fromLatin1("Variable Value") << QString::fromLatin1("Variable Value"));
2852 }
2853
2854 void tst_QXmlQuery::unavailableExternalVariable() const
2855 {
2856     QXmlQuery query;
2857
2858     MessageSilencer silencer;
2859     query.setMessageHandler(&silencer);
2860
2861     query.setQuery(QLatin1String("declare variable $foo external; 1"));
2862
2863     QVERIFY(!query.isValid());
2864 }
2865
2866 /*!
2867  Ensure that setUriResolver() affects \c fn:doc() and \c fn:doc-available().
2868  */
2869 void tst_QXmlQuery::useUriResolver() const
2870 {
2871     class TestUriResolver : public QAbstractUriResolver
2872                           , private TestFundament
2873     {
2874     public:
2875         TestUriResolver() {}
2876         virtual QUrl resolve(const QUrl &relative,
2877                              const QUrl &baseURI) const
2878         {
2879             Q_UNUSED(relative);
2880             return baseURI.resolved(inputFile(QLatin1String(queriesDirectory) + QLatin1String("simpleDocument.xml")));
2881         }
2882     };
2883
2884     const TestUriResolver uriResolver;
2885     QXmlQuery query;
2886
2887     query.setUriResolver(&uriResolver);
2888     query.setQuery(QLatin1String("let $i := 'http://www.example.com/DoesNotExist'"
2889                                  "return (string(doc($i)), doc-available($i))"));
2890
2891
2892     QXmlResultItems result;
2893     query.evaluateTo(&result);
2894
2895     QVERIFY(!result.hasError());
2896     QCOMPARE(result.next().toAtomicValue().toString(), QString::fromLatin1("text text node"));
2897     QCOMPARE(result.next().toAtomicValue().toBool(), true);
2898     QVERIFY(result.next().isNull());
2899     QVERIFY(!result.hasError());
2900 }
2901
2902 void tst_QXmlQuery::queryWithFocusAndVariable() const
2903 {
2904     QXmlQuery query;
2905     query.setFocus(QXmlItem(5));
2906     query.bindVariable(QLatin1String("var"), QXmlItem(2));
2907
2908     query.setQuery(QLatin1String("string(. * $var)"));
2909
2910     QStringList result;
2911
2912     QVERIFY(query.evaluateTo(&result));
2913
2914     QCOMPARE(result, QStringList(QLatin1String("10")));
2915 }
2916
2917 void tst_QXmlQuery::undefinedFocus() const
2918 {
2919     QXmlQuery query;
2920
2921     MessageSilencer silencer;
2922     query.setMessageHandler(&silencer);
2923
2924     query.setQuery(QLatin1String("."));
2925     QVERIFY(!query.isValid());
2926 }
2927
2928 void tst_QXmlQuery::basicFocusUsage() const
2929 {
2930     QXmlQuery query;
2931
2932     MessageSilencer silencer;
2933     query.setMessageHandler(&silencer);
2934
2935     query.setFocus(QXmlItem(5));
2936     query.setQuery(QLatin1String("string(. * .)"));
2937     QVERIFY(query.isValid());
2938
2939     QStringList result;
2940     QVERIFY(query.evaluateTo(&result));
2941
2942     QCOMPARE(result, QStringList(QLatin1String("25")));
2943 }
2944
2945 /*!
2946   Triggers an ownership related crash.
2947  */
2948 void tst_QXmlQuery::copyCheckMessageHandler() const
2949 {
2950     QXmlQuery query;
2951     QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0));
2952
2953     query.setQuery(QLatin1String("doc('qrc:/QXmlQueryTestData/data/oneElement.xml')"));
2954     /* By now, we should have set the builtin message handler. */
2955     const QAbstractMessageHandler *const messageHandler = query.messageHandler();
2956     QVERIFY(messageHandler);
2957
2958     {
2959         /* This copies QXmlQueryPrivate::m_ownerObject, and its destructor
2960          * will delete it, and hence the builtin message handler attached to it. */
2961         QXmlQuery copy(query);
2962     }
2963
2964     QXmlResultItems result;
2965     query.evaluateTo(&result);
2966
2967     while(!result.next().isNull())
2968     {
2969     }
2970     QVERIFY(!result.hasError());
2971 }
2972
2973 void tst_QXmlQuery::queryLanguage() const
2974 {
2975     /* Check default value. */
2976     {
2977         const QXmlQuery query;
2978         QCOMPARE(query.queryLanguage(), QXmlQuery::XQuery10);
2979     }
2980
2981     /* Check default value of copies default instance. */
2982     {
2983         const QXmlQuery query1;
2984         const QXmlQuery query2(query1);
2985
2986         QCOMPARE(query1.queryLanguage(), QXmlQuery::XQuery10);
2987         QCOMPARE(query2.queryLanguage(), QXmlQuery::XQuery10);
2988     }
2989 }
2990
2991 void tst_QXmlQuery::queryLanguageSignature() const
2992 {
2993     /* This getter should be const. */
2994     QXmlQuery query;
2995     query.queryLanguage();
2996 }
2997
2998 void tst_QXmlQuery::enumQueryLanguage() const
2999 {
3000     /* These enum values should be possible to OR for future plans. */
3001     QCOMPARE(int(QXmlQuery::XQuery10), 1);
3002     QCOMPARE(int(QXmlQuery::XSLT20), 2);
3003     QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintSelector), 1024);
3004     QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintField), 2048);
3005     QCOMPARE(int(QXmlQuery::XPath20), 4096);
3006 }
3007
3008 void tst_QXmlQuery::setInitialTemplateNameQXmlName() const
3009 {
3010     QXmlQuery query(QXmlQuery::XSLT20);
3011     QXmlNamePool np(query.namePool());
3012     const QXmlName name(np, QLatin1String("main"));
3013
3014     query.setInitialTemplateName(name);
3015
3016     QCOMPARE(query.initialTemplateName(), name);
3017
3018     query.setQuery(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/namedTemplate.xsl"))));
3019     QVERIFY(query.isValid());
3020
3021     QBuffer result;
3022     QVERIFY(result.open(QIODevice::ReadWrite));
3023     QXmlSerializer serializer(query, &result);
3024     query.evaluateTo(&serializer);
3025
3026     QCOMPARE(result.data(), QByteArray("1 2 3 4 5"));
3027
3028     // TODO invoke a template which has required params.
3029 }
3030
3031 void tst_QXmlQuery::setInitialTemplateNameQXmlNameSignature() const
3032 {
3033     QXmlQuery query;
3034     QXmlNamePool np(query.namePool());
3035     const QXmlName name(np, QLatin1String("foo"));
3036
3037     /* The signature should take a const reference. */
3038     query.setInitialTemplateName(name);
3039 }
3040
3041 void tst_QXmlQuery::setInitialTemplateNameQString() const
3042 {
3043     QXmlQuery query;
3044     QXmlNamePool np(query.namePool());
3045     query.setInitialTemplateName(QLatin1String("foo"));
3046
3047     QCOMPARE(query.initialTemplateName(), QXmlName(np, QLatin1String("foo")));
3048 }
3049
3050 void tst_QXmlQuery::setInitialTemplateNameQStringSignature() const
3051 {
3052     const QString name(QLatin1String("name"));
3053     QXmlQuery query;
3054
3055     /* We should take a const reference. */
3056     query.setInitialTemplateName(name);
3057 }
3058
3059 void tst_QXmlQuery::initialTemplateName() const
3060 {
3061     /* Check our default value. */
3062     QXmlQuery query;
3063     QCOMPARE(query.initialTemplateName(), QXmlName());
3064     QVERIFY(query.initialTemplateName().isNull());
3065 }
3066
3067 void tst_QXmlQuery::initialTemplateNameSignature() const
3068 {
3069     const QXmlQuery query;
3070     /* This should be a const member. */
3071     query.initialTemplateName();
3072 }
3073
3074 void tst_QXmlQuery::setNetworkAccessManager() const
3075 {
3076
3077     /* Ensure fn:doc() picks up the right QNetworkAccessManager. */
3078     {
3079         NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
3080                                           QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/queries/simpleDocument.xml"))));
3081
3082         QXmlQuery query;
3083         query.setNetworkAccessManager(&networkOverrider);
3084         query.setQuery(QLatin1String("string(doc('tag:example.com:DOESNOTEXIST'))"));
3085         QVERIFY(query.isValid());
3086
3087         QStringList result;
3088         QVERIFY(query.evaluateTo(&result));
3089
3090         QCOMPARE(result, QStringList(QLatin1String("text text node")));
3091     }
3092
3093     /* Ensure setQuery() is using the right network manager. */
3094     {
3095         NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
3096                                           QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/queries/concat.xq"))));
3097
3098         QXmlQuery query;
3099         query.setNetworkAccessManager(&networkOverrider);
3100         query.setQuery(QUrl("tag:example.com:DOESNOTEXIST"));
3101         QVERIFY(query.isValid());
3102
3103         QStringList result;
3104         QVERIFY(query.evaluateTo(&result));
3105
3106         QCOMPARE(result, QStringList(QLatin1String("abcdef")));
3107     }
3108 }
3109 void tst_QXmlQuery::networkAccessManagerSignature() const
3110 {
3111     /* Const object. */
3112     const QXmlQuery query;
3113
3114     /* The function should be const. */
3115     query.networkAccessManager();
3116 }
3117
3118 void tst_QXmlQuery::networkAccessManagerDefaultValue() const
3119 {
3120     const QXmlQuery query;
3121
3122     QCOMPARE(query.networkAccessManager(), static_cast<QNetworkAccessManager *>(0));
3123 }
3124
3125 void tst_QXmlQuery::networkAccessManager() const
3126 {
3127     /* Test that we return the network manager that was set. */
3128     {
3129         QNetworkAccessManager manager;
3130         QXmlQuery query;
3131         query.setNetworkAccessManager(&manager);
3132         QCOMPARE(query.networkAccessManager(), &manager);
3133     }
3134 }
3135
3136 /*!
3137  \internal
3138  \since 4.5
3139
3140   1. Load a document into QXmlQuery's document cache, by executing a query which does it.
3141   2. Set a focus
3142   3. Change query, to one which uses the focus
3143   4. Evaluate
3144
3145  Used to crash.
3146  */
3147 void tst_QXmlQuery::multipleDocsAndFocus() const
3148 {
3149     QXmlQuery query;
3150
3151     /* We use string concatenation, since variable bindings might disturb what
3152      * we're testing. */
3153     query.setQuery(QLatin1String("string(doc('") +
3154                    inputFile(QLatin1String(XMLPATTERNSDIR "/queries/simpleDocument.xml")) +
3155                    QLatin1String("'))"));
3156     query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3157     query.setQuery(QLatin1String("string(.)"));
3158
3159     QStringList result;
3160     QVERIFY(query.evaluateTo(&result));
3161 }
3162
3163 /*!
3164  \internal
3165  \since 4.5
3166
3167  1. Set a focus
3168  2. Set a query
3169  3. Evaluate
3170  4. Change focus
3171  5. Evaluate
3172
3173  Used to crash.
3174  */
3175 void tst_QXmlQuery::multipleEvaluationsWithDifferentFocus() const
3176 {
3177     QXmlQuery query;
3178     QStringList result;
3179
3180     query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3181     query.setQuery(QLatin1String("string(.)"));
3182     QVERIFY(query.evaluateTo(&result));
3183
3184     query.setFocus(QUrl(inputFileAsURI(QLatin1String(XMLPATTERNSDIR "/stylesheets/documentElement.xml"))));
3185     QVERIFY(query.evaluateTo(&result));
3186 }
3187
3188 void tst_QXmlQuery::bindVariableQXmlQuery() const
3189 {
3190     QFETCH(QString, query1);
3191     QFETCH(QString, query2);
3192     QFETCH(QString, expectedOutput);
3193     QFETCH(bool, expectedSuccess);
3194
3195     MessageSilencer silencer;
3196     QXmlQuery xmlQuery1;
3197     xmlQuery1.setMessageHandler(&silencer);
3198     xmlQuery1.setQuery(query1);
3199
3200     QXmlQuery xmlQuery2(xmlQuery1);
3201     xmlQuery2.bindVariable("query1", xmlQuery1);
3202     xmlQuery2.setQuery(query2);
3203
3204     QString output;
3205     const bool querySuccess = xmlQuery2.evaluateTo(&output);
3206
3207     QCOMPARE(querySuccess, expectedSuccess);
3208
3209     if(querySuccess)
3210         QCOMPARE(output, expectedOutput);
3211 }
3212
3213 void tst_QXmlQuery::bindVariableQXmlQuery_data() const
3214 {
3215     QTest::addColumn<QString>("query1");
3216     QTest::addColumn<QString>("query2");
3217     QTest::addColumn<QString>("expectedOutput");
3218     QTest::addColumn<bool>("expectedSuccess");
3219
3220     QTest::newRow("First query has one atomic value.")
3221             << "2"
3222             << "1, $query1, 3"
3223             << "1 2 3\n"
3224             << true;
3225
3226     QTest::newRow("First query has two atomic values.")
3227             << "2, 3"
3228             << "1, $query1, 4"
3229             << "1 2 3 4\n"
3230             << true;
3231
3232     QTest::newRow("First query is a node.")
3233             << "<e/>"
3234             << "1, $query1, 3"
3235             << "1<e/>3\n"
3236             << true;
3237
3238     /* This is a good test, because it triggers the exception in the
3239      * bindVariable() call, as supposed to when the actual evaluation is done.
3240      */
3241     QTest::newRow("First query has a dynamic error.")
3242             << "error()"
3243             << "1, $query1"
3244             << QString() /* We don't care. */
3245             << false;
3246 }
3247
3248 void tst_QXmlQuery::bindVariableQStringQXmlQuerySignature() const
3249 {
3250     QXmlQuery query1;
3251     query1.setQuery("'dummy'");
3252
3253     QXmlQuery query2;
3254     const QString name(QLatin1String("name"));
3255
3256     /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
3257      * QXmlQuery, and evaluation is what we do here. */
3258     query2.bindVariable(name, const_cast<const QXmlQuery &>(query1));
3259 }
3260
3261 void tst_QXmlQuery::bindVariableQXmlNameQXmlQuerySignature() const
3262 {
3263     QXmlNamePool np;
3264     QXmlQuery query1(np);
3265     query1.setQuery("'dummy'");
3266
3267     QXmlQuery query2;
3268     const QXmlName name(np, QLatin1String("name"));
3269
3270     /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
3271      * QXmlQuery, and evaluation is what we do here. */
3272     query2.bindVariable(name, const_cast<const QXmlQuery &>(query1));
3273 }
3274
3275 /*!
3276   Check that the QXmlName is handled correctly.
3277  */
3278 void tst_QXmlQuery::bindVariableQXmlNameQXmlQuery() const
3279 {
3280     QXmlNamePool np;
3281     QXmlQuery query1;
3282     query1.setQuery(QLatin1String("1"));
3283
3284     QXmlQuery query2(np);
3285     query2.bindVariable(QXmlName(np, QLatin1String("theName")), query1);
3286     query2.setQuery("$theName");
3287
3288     QString result;
3289     query2.evaluateTo(&result);
3290
3291     QCOMPARE(result, QString::fromLatin1("1\n"));
3292 }
3293
3294 void tst_QXmlQuery::bindVariableQXmlQueryInvalidate() const
3295 {
3296     QXmlQuery query;
3297     query.bindVariable(QLatin1String("name"), QVariant(1));
3298     query.setQuery("$name");
3299     QVERIFY(query.isValid());
3300
3301     QXmlQuery query2;
3302     query2.setQuery("'query2'");
3303
3304     query.bindVariable(QLatin1String("name"), query);
3305     QVERIFY(!query.isValid());
3306 }
3307
3308 void tst_QXmlQuery::unknownSourceLocation() const
3309 {
3310     QBuffer b;
3311     b.setData("<a><b/><b/></a>");
3312     b.open(QIODevice::ReadOnly);
3313
3314     MessageSilencer silencer;
3315     QXmlQuery query;
3316     query.bindVariable(QLatin1String("inputDocument"), &b);
3317     query.setMessageHandler(&silencer);
3318
3319     query.setQuery(QLatin1String("doc($inputDocument)/a/(let $v := b/string() return if ($v) then $v else ())"));
3320
3321     QString output;
3322     query.evaluateTo(&output);
3323 }
3324
3325 void tst_QXmlQuery::identityConstraintSuccess() const
3326 {
3327     QXmlQuery::QueryLanguage queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintSelector;
3328
3329     /* We run this code for Selector and Field. */
3330     for(int i = 0; i < 3; ++i)
3331     {
3332         QXmlNamePool namePool;
3333         QXmlResultItems result;
3334         QXmlItem node;
3335
3336         {
3337             QXmlQuery nodeSource(namePool);
3338             nodeSource.setQuery(QLatin1String("<e/>"));
3339
3340             nodeSource.evaluateTo(&result);
3341             node = result.next();
3342         }
3343
3344         /* Basic use:
3345          * 1. The focus is undefined, but it's still valid.
3346          * 2. We never evaluate. */
3347         {
3348             QXmlQuery query(queryLanguage);
3349             query.setQuery(QLatin1String("a"));
3350             QVERIFY(query.isValid());
3351         }
3352
3353         /* Basic use:
3354          * 1. The focus is undefined, but it's still valid.
3355          * 2. We afterwards set the focus. */
3356         {
3357             QXmlQuery query(queryLanguage, namePool);
3358             query.setQuery(QLatin1String("a"));
3359             query.setFocus(node);
3360             QVERIFY(query.isValid());
3361         }
3362
3363         /* Basic use:
3364          * 1. The focus is undefined, but it's still valid.
3365          * 2. We afterwards set the focus.
3366          * 3. We evaluate. */
3367         {
3368             QXmlQuery query(queryLanguage, namePool);
3369             query.setQuery(QString(QLatin1Char('.')));
3370             query.setFocus(node);
3371             QVERIFY(query.isValid());
3372
3373             QString result;
3374             QVERIFY(query.evaluateTo(&result));
3375             QCOMPARE(result, QString::fromLatin1("<e/>\n"));
3376         }
3377
3378         /* A slightly more complex Field. */
3379         {
3380             QXmlQuery query(queryLanguage);
3381             query.setQuery(QLatin1String("* | .//xml:*/."));
3382             QVERIFY(query.isValid());
3383         }
3384
3385         /* @ is only allowed in Field. */
3386         if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField)
3387         {
3388             QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField);
3389             query.setQuery(QLatin1String("@abc"));
3390             QVERIFY(query.isValid());
3391         }
3392
3393         /* Field allows attribute:: and child:: .*/
3394         if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField)
3395         {
3396             QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField);
3397             query.setQuery(QLatin1String("attribute::name | child::name"));
3398             QVERIFY(query.isValid());
3399         }
3400
3401         /* Selector allows only child:: .*/
3402         {
3403             QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintSelector);
3404             query.setQuery(QLatin1String("child::name"));
3405             QVERIFY(query.isValid());
3406         }
3407
3408         if(i == 0)
3409             queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintField;
3410         else if(i == 1)
3411             queryLanguage = QXmlQuery::XPath20;
3412     }
3413 }
3414
3415 Q_DECLARE_METATYPE(QXmlQuery::QueryLanguage);
3416
3417 /*!
3418  We just do some basic tests for boot strapping and sanity checking. The actual regression
3419  testing is in the Schema suite.
3420  */
3421 void tst_QXmlQuery::identityConstraintFailure() const
3422 {
3423     QFETCH(QXmlQuery::QueryLanguage, queryLanguage);
3424     QFETCH(QString, inputQuery);
3425
3426     QXmlQuery query(queryLanguage);
3427     MessageSilencer silencer;
3428     query.setMessageHandler(&silencer);
3429
3430     query.setQuery(inputQuery);
3431     QVERIFY(!query.isValid());
3432 }
3433
3434 void tst_QXmlQuery::identityConstraintFailure_data() const
3435 {
3436     QTest::addColumn<QXmlQuery::QueryLanguage>("queryLanguage");
3437     QTest::addColumn<QString>("inputQuery");
3438
3439     QTest::newRow("We don't have element constructors in identity constraint pattern, "
3440                   "it's an XQuery feature(Selector).")
3441         << QXmlQuery::XmlSchema11IdentityConstraintSelector
3442         << QString::fromLatin1("<e/>");
3443
3444     QTest::newRow("We don't have functions in identity constraint pattern, "
3445                   "it's an XPath feature(Selector).")
3446         << QXmlQuery::XmlSchema11IdentityConstraintSelector
3447         << QString::fromLatin1("current-time()");
3448
3449     QTest::newRow("We don't have element constructors in identity constraint pattern, "
3450                   "it's an XQuery feature(Field).")
3451         << QXmlQuery::XmlSchema11IdentityConstraintSelector
3452         << QString::fromLatin1("<e/>");
3453
3454     QTest::newRow("We don't have functions in identity constraint pattern, "
3455                   "it's an XPath feature(Field).")
3456         << QXmlQuery::XmlSchema11IdentityConstraintSelector
3457         << QString::fromLatin1("current-time()");
3458
3459     QTest::newRow("@attributeName is disallowed for the selector.")
3460         << QXmlQuery::XmlSchema11IdentityConstraintSelector
3461         << QString::fromLatin1("@abc");
3462
3463     QTest::newRow("attribute:: is disallowed for the selector.")
3464         << QXmlQuery::XmlSchema11IdentityConstraintSelector
3465         << QString::fromLatin1("attribute::name");
3466
3467     QTest::newRow("ancestor::name is disallowed for the selector.")
3468         << QXmlQuery::XmlSchema11IdentityConstraintSelector
3469         << QString::fromLatin1("ancestor::name");
3470
3471     QTest::newRow("ancestor::name is disallowed for the field.")
3472         << QXmlQuery::XmlSchema11IdentityConstraintField
3473         << QString::fromLatin1("ancestor::name");
3474 }
3475
3476 QTEST_MAIN(tst_QXmlQuery)
3477
3478 #include "tst_qxmlquery.moc"
3479 #else //QTEST_XMLPATTERNS
3480 QTEST_NOOP_MAIN
3481 #endif