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