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