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