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