summaryrefslogtreecommitdiffstats
path: root/tests/auto/qxmlquery/tst_qxmlquery.cpp
diff options
context:
space:
mode:
authoraxis <qt-info@nokia.com>2009-04-24 13:34:15 +0200
committeraxis <qt-info@nokia.com>2009-04-24 13:34:15 +0200
commit8f427b2b914d5b575a4a7c0ed65d2fb8f45acc76 (patch)
treea17e1a767a89542ab59907462206d7dcf2e504b2 /tests/auto/qxmlquery/tst_qxmlquery.cpp
Long live Qt for S60!
Diffstat (limited to 'tests/auto/qxmlquery/tst_qxmlquery.cpp')
-rw-r--r--tests/auto/qxmlquery/tst_qxmlquery.cpp3231
1 files changed, 3231 insertions, 0 deletions
diff --git a/tests/auto/qxmlquery/tst_qxmlquery.cpp b/tests/auto/qxmlquery/tst_qxmlquery.cpp
new file mode 100644
index 0000000000..986bde9fab
--- /dev/null
+++ b/tests/auto/qxmlquery/tst_qxmlquery.cpp
@@ -0,0 +1,3231 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+
+#ifdef QTEST_XMLPATTERNS
+
+#include <QAbstractMessageHandler>
+#include <QFileInfo>
+#include <QNetworkReply>
+#include <QtNetwork/QTcpServer>
+#include <QtNetwork/QTcpSocket>
+#include <QXmlFormatter>
+#include <QXmlItem>
+#include <QXmlName>
+#include <QXmlQuery>
+#include <QXmlResultItems>
+#include <QXmlSerializer>
+
+#include "MessageSilencer.h"
+#include "MessageValidator.h"
+#include "NetworkOverrider.h"
+#include "PushBaseliner.h"
+#include "../qabstracturiresolver/TestURIResolver.h"
+#include "../qsimplexmlnodemodel/TestSimpleNodeModel.h"
+#include "TestFundament.h"
+#include "../network-settings.h"
+
+/*!
+ \class tst_QXmlQuery
+ \internal
+ \since 4.4
+ \brief Tests class QXmlQuery.
+
+ This test is not intended for testing the engine, but the functionality specific
+ to the QXmlQuery class.
+
+ In other words, if you have an engine bug; don't add it here because it won't be
+ tested properly. Instead add it to the test suite.
+
+ */
+class tst_QXmlQuery : public QObject
+ , private TestFundament
+{
+ Q_OBJECT
+
+public:
+ inline tst_QXmlQuery() : m_generatedBaselines(0)
+ , m_pushTestsCount(0)
+ , m_testNetwork(true)
+ {
+ Q_SET_DEFAULT_IAP
+ }
+
+private Q_SLOTS:
+ void defaultConstructor() const;
+ void copyConstructor() const;
+ void constructorQXmlNamePool() const;
+ void constructorQXmlNamePoolQueryLanguage() const;
+ void constructorQXmlNamePoolWithinQSimpleXmlNodeModel() const;
+ void assignmentOperator() const;
+ void isValid() const;
+ void sequentialExecution() const;
+ void bindVariableQString() const;
+ void bindVariableQStringNoExternalDeclaration() const;
+ void bindVariableQXmlName() const;
+ void bindVariableQXmlNameTriggerWarnings() const;
+ void bindVariableQStringQIODevice() const;
+ void bindVariableQStringQIODeviceWithByteArray() const;
+ void bindVariableQStringQIODeviceWithString() const;
+ void bindVariableQStringQIODeviceWithQFile() const;
+ void bindVariableQXmlNameQIODevice() const;
+ void bindVariableQXmlNameQIODeviceTriggerWarnings() const;
+ void bindVariableXSLTSuccess() const;
+ void bindVariableTemporaryNode() const;
+ void setMessageHandler() const;
+ void messageHandler() const;
+ void evaluateToQAbstractXmlReceiverTriggerWarnings() const;
+ void evaluateToQXmlResultItems() const;
+ void evaluateToQXmlResultItemsTriggerWarnings() const;
+ void evaluateToQXmlResultItemsErrorAtEnd() const;
+ void evaluateToReceiver();
+ void evaluateToReceiver_data() const;
+ void evaluateToReceiverOnInvalidQuery() const;
+ void evaluateToQStringTriggerError() const;
+ void evaluateToQString() const;
+ void evaluateToQString_data() const;
+ void evaluateToQStringSignature() const;
+ void checkGeneratedBaselines() const;
+ void basicXQueryToQtTypeCheck() const;
+ void basicQtToXQueryTypeCheck() const;
+ void bindNode() const;
+ void relativeBaseURI() const;
+ void emptyBaseURI() const;
+ void roundTripDateWithinQXmlItem() const;
+ void bindingMissing() const;
+ void bindDefaultConstructedItem() const;
+ void bindDefaultConstructedItem_data() const;
+ void bindEmptyNullString() const;
+ void bindEmptyString() const;
+ void rebindVariableSameType() const;
+ void rebindVariableDifferentType() const;
+ void rebindVariableWithNullItem() const;
+ void eraseQXmlItemBinding() const;
+ void eraseDeviceBinding() const;
+ void constCorrectness() const;
+ void objectSize() const;
+ void setUriResolver() const;
+ void uriResolver() const;
+ void messageXML() const;
+ void resultItemsDeallocatedQuery() const;
+ void copyCheckMessageHandler() const;
+ void shadowedVariables() const;
+ void setFocusQXmlItem() const;
+ void setFocusQUrl() const;
+ void setFocusQIODevice() const;
+ void setFocusQIODeviceAvoidVariableClash() const;
+ void setFocusQIODeviceFailure() const;
+ void setFocusQIODeviceTriggerWarnings() const;
+ void recompilationWithEvaluateToResultFailing() const;
+ void secondEvaluationWithEvaluateToResultFailing() const;
+ void recompilationWithEvaluateToReceiver() const;
+ void fnDocOnQIODeviceTimeout() const;
+ void evaluateToQStringListOnInvalidQuery() const;
+ void evaluateToQStringList() const;
+ void evaluateToQStringListTriggerWarnings() const;
+ void evaluateToQStringList_data() const;
+ void evaluateToQStringListNoConversion() const;
+ void evaluateToQIODevice() const;
+ void evaluateToQIODeviceTriggerWarnings() const;
+ void evaluateToQIODeviceSignature() const;
+ void evaluateToQIODeviceOnInvalidQuery() const;
+ void setQueryQIODeviceQUrl() const;
+ void setQueryQIODeviceQUrlTriggerWarnings() const;
+ void setQueryQString() const;
+ void setQueryQUrlSuccess() const;
+ void setQueryQUrlSuccess_data() const;
+ void setQueryQUrlFailSucceed() const;
+ void setQueryQUrlFailure() const;
+ void setQueryQUrlFailure_data() const;
+ void setQueryQUrlBaseURI() const;
+ void setQueryQUrlBaseURI_data() const;
+ void setQueryWithNonExistentQUrlOnValidQuery() const;
+ void setQueryWithInvalidQueryFromQUrlOnValidQuery() const;
+ void retrieveNameFromQuery() const;
+ void retrieveNameFromQuery_data() const;
+ void cleanupTestCase() const;
+ void declareUnavailableExternal() const;
+ void msvcCacheIssue() const;
+ void unavailableExternalVariable() const;
+ void useUriResolver() const;
+ void queryWithFocusAndVariable() const;
+ void undefinedFocus() const;
+ void basicFocusUsage() const;
+
+ void queryLanguage() const;
+ void queryLanguageSignature() const;
+ void enumQueryLanguage() const;
+
+ void setNetworkAccessManager() const;
+ void networkAccessManagerSignature() const;
+ void networkAccessManagerDefaultValue() const;
+ void networkAccessManager() const;
+
+ void setInitialTemplateNameQXmlName() const;
+ void setInitialTemplateNameQXmlNameSignature() const;
+ void setInitialTemplateNameQString() const;
+ void setInitialTemplateNameQStringSignature() const;
+ void initialTemplateName() const;
+ void initialTemplateNameSignature() const;
+
+ void fnDocNetworkAccessSuccess() const;
+ void fnDocNetworkAccessSuccess_data() const;
+ void fnDocNetworkAccessFailure() const;
+ void fnDocNetworkAccessFailure_data() const;
+ void multipleDocsAndFocus() const;
+ void multipleEvaluationsWithDifferentFocus() const;
+ void bindVariableQXmlQuery() const;
+ void bindVariableQXmlQuery_data() const;
+ void bindVariableQStringQXmlQuerySignature() const;
+ void bindVariableQXmlNameQXmlQuerySignature() const;
+ void bindVariableQXmlNameQXmlQuery() const;
+ void bindVariableQXmlQueryInvalidate() const;
+
+ // 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.
+ // TODO bind stringlists, variant lists, both ways.
+ // TODO trigger serialization error, or any error in evaluateToushCallback().
+ // TODO let items travle between two queries, as seen in the SDK
+ // TODO what happens if the query declares local variable and external ones are provided?
+
+private:
+ enum
+ {
+ /**
+ * One excluded, since we skip static-base-uri.xq.
+ */
+ ExpectedQueryCount = 29
+ };
+
+ static void checkBaseURI(const QUrl &baseURI, const QString &candidate);
+ static QStringList queries();
+ static const char *const queriesDirectory;
+
+ int m_generatedBaselines;
+ int m_pushTestsCount;
+ const bool m_testNetwork;
+};
+
+void tst_QXmlQuery::checkBaseURI(const QUrl &baseURI, const QString &candidate)
+{
+ /* The use of QFileInfo::canonicalFilePath() takes into account that drive letters
+ * on Windows may have different cases. */
+ QVERIFY(QDir(baseURI.toLocalFile()).relativeFilePath(QFileInfo(candidate).canonicalFilePath()).startsWith("../"));
+}
+
+const char *const tst_QXmlQuery::queriesDirectory = "../xmlpatterns/queries/";
+
+QStringList tst_QXmlQuery::queries()
+{
+ QDir dir;
+ dir.cd(inputFile(QLatin1String(queriesDirectory)));
+
+ return dir.entryList(QStringList(QLatin1String("*.xq")));
+}
+
+void tst_QXmlQuery::defaultConstructor() const
+{
+ /* Allocate instance in different orders. */
+ {
+ QXmlQuery query;
+ }
+
+ {
+ QXmlQuery query1;
+ QXmlQuery query2;
+ }
+
+ {
+ QXmlQuery query1;
+ QXmlQuery query2;
+ QXmlQuery query3;
+ }
+}
+
+void tst_QXmlQuery::copyConstructor() const
+{
+ /* Verify that we can take a const reference, and simply do a copy of a default constructed object. */
+ {
+ const QXmlQuery query1;
+ QXmlQuery query2(query1);
+ }
+
+ /* Copy twice. */
+ {
+ const QXmlQuery query1;
+ QXmlQuery query2(query1);
+ QXmlQuery query3(query2);
+ }
+
+ /* Verify that copying default values works. */
+ {
+ const QXmlQuery query1;
+ const QXmlQuery query2(query1);
+ QCOMPARE(query2.messageHandler(), query1.messageHandler());
+ QCOMPARE(query2.uriResolver(), query1.uriResolver());
+ QCOMPARE(query2.queryLanguage(), query1.queryLanguage());
+ QCOMPARE(query2.initialTemplateName(), query1.initialTemplateName());
+ QCOMPARE(query2.networkAccessManager(), query1.networkAccessManager());
+ }
+
+ /* Check that the
+ *
+ * - name pool
+ * - URI resolver
+ * - message handler
+ * - query language
+ * - initial template name
+ *
+ * sticks with the copy. */
+ {
+ MessageSilencer silencer;
+ TestURIResolver resolver;
+ QNetworkAccessManager networkManager;
+ QXmlQuery query1(QXmlQuery::XSLT20);
+ QXmlNamePool np1(query1.namePool());
+
+ query1.setMessageHandler(&silencer);
+ query1.setUriResolver(&resolver);
+ query1.setNetworkAccessManager(&networkManager);
+
+ const QXmlName name(np1, QLatin1String("localName"),
+ QLatin1String("http://example.com/"),
+ QLatin1String("prefix"));
+ query1.setInitialTemplateName(name);
+
+ const QXmlQuery query2(query1);
+ QCOMPARE(query2.messageHandler(), &silencer);
+ QCOMPARE(query2.uriResolver(), &resolver);
+ QCOMPARE(query2.queryLanguage(), QXmlQuery::XSLT20);
+ QCOMPARE(query2.initialTemplateName(), name);
+ QCOMPARE(query2.networkAccessManager(), &networkManager);
+
+ QXmlNamePool np2(query2.namePool());
+
+ QCOMPARE(name.namespaceUri(np2), QString::fromLatin1("http://example.com/"));
+ QCOMPARE(name.localName(np2), QString::fromLatin1("localName"));
+ QCOMPARE(name.prefix(np2), QString::fromLatin1("prefix"));
+ }
+
+ {
+ QXmlQuery original;
+
+ original.setFocus(QXmlItem(4));
+ original.setQuery(QLatin1String("."));
+ QVERIFY(original.isValid());
+
+ const QXmlQuery copy(original);
+
+ QXmlResultItems result;
+ copy.evaluateTo(&result);
+ QCOMPARE(result.next().toAtomicValue(), QVariant(4));
+ QVERIFY(result.next().isNull());
+ QVERIFY(!result.hasError());
+ }
+
+ /* Copy, set, compare. Check that copies are independent. */
+ {
+ // TODO all members except queryLanguage().
+ }
+}
+
+void tst_QXmlQuery::constructorQXmlNamePool() const
+{
+ /* Check that the namepool we are passed, is actually used. */
+ QXmlNamePool np;
+
+ QXmlQuery query(np);
+ const QXmlName name(np, QLatin1String("localName"),
+ QLatin1String("http://example.com/"),
+ QLatin1String("prefix"));
+
+ QXmlNamePool np2(query.namePool());
+ QCOMPARE(name.namespaceUri(np2), QString::fromLatin1("http://example.com/"));
+ QCOMPARE(name.localName(np2), QString::fromLatin1("localName"));
+ QCOMPARE(name.prefix(np2), QString::fromLatin1("prefix"));
+}
+
+/*!
+ Ensure that the internal variable loading mechanisms uses the user-supplied
+ name pool.
+
+ If that is not the case, different name pools are used and the code crashes.
+
+ \since 4.5
+ */
+void tst_QXmlQuery::constructorQXmlNamePoolQueryLanguage() const
+{
+ QXmlNamePool np;
+ QXmlName name(np, QLatin1String("arbitraryName"));
+
+ QXmlQuery query(QXmlQuery::XQuery10, np);
+
+ QBuffer input;
+ input.setData("<yall/>");
+
+ QVERIFY(input.open(QIODevice::ReadOnly));
+ query.bindVariable(name, &input);
+ query.setQuery("string(doc($arbitraryName))");
+
+ QStringList result;
+ query.evaluateTo(&result);
+}
+
+void tst_QXmlQuery::constructorQXmlNamePoolWithinQSimpleXmlNodeModel() const
+{
+ class TestCTOR : public TestSimpleNodeModel
+ {
+ public:
+ TestCTOR(const QXmlNamePool &np) : TestSimpleNodeModel(np)
+ {
+ }
+
+ void checkCTOR() const
+ {
+ /* If this fails to compile, the constructor has trouble with taking
+ * a mutable reference.
+ *
+ * The reason we use the this pointer explicitly, is to avoid a compiler
+ * warnings with MSVC 2005. */
+ QXmlQuery(this->namePool());
+ }
+ };
+
+ QXmlNamePool np;
+ TestCTOR ctor(np);
+ ctor.checkCTOR();
+}
+
+void tst_QXmlQuery::assignmentOperator() const
+{
+ class ReturnURI : public QAbstractUriResolver
+ {
+ public:
+ virtual QUrl resolve(const QUrl &relative,
+ const QUrl &baseURI) const
+ {
+ return baseURI.resolved(relative);
+ }
+ };
+
+ /* Assign this to this. */
+ {
+ QXmlQuery query;
+ query = query;
+ query = query;
+ query = query;
+
+ /* Just call a couple of functions to give valgrind
+ * something to check. */
+ QVERIFY(!query.isValid());
+ query.messageHandler();
+ }
+
+ /* Assign null instances a couple of times. */
+ {
+ QXmlQuery query1;
+ QXmlQuery query2;
+ query1 = query2;
+ query1 = query2;
+ query1 = query2;
+
+ /* Just call a couple of functions to give valgrind
+ * something to check. */
+ QVERIFY(!query1.isValid());
+ query1.messageHandler();
+
+ /* Just call a couple of functions to give valgrind
+ * something to check. */
+ QVERIFY(!query2.isValid());
+ query2.messageHandler();
+ }
+
+ /* Create a query, set all the things it stores, and ensure it
+ * travels over to the new instance. */
+ {
+ MessageSilencer silencer;
+ const ReturnURI returnURI;
+ QXmlNamePool namePool;
+
+ QBuffer documentDevice;
+ documentDevice.setData(QByteArray("<e>a</e>"));
+ QVERIFY(documentDevice.open(QIODevice::ReadOnly));
+
+ QXmlQuery original(namePool);
+ QXmlName testName(namePool, QLatin1String("somethingToCheck"));
+
+ original.setMessageHandler(&silencer);
+ original.bindVariable(QLatin1String("var"), QXmlItem(1));
+ original.bindVariable(QLatin1String("device"), &documentDevice);
+ original.setUriResolver(&returnURI);
+ original.setFocus(QXmlItem(3));
+ original.setQuery(QLatin1String("$var, 1 + 1, ., string(doc($device))"));
+
+ /* Do a copy, and check that everything followed on into the copy. No modification
+ * of the copy. */
+ {
+ QXmlQuery copy;
+
+ /* We use assignment operator, not copy constructor. */
+ copy = original;
+
+ QVERIFY(copy.isValid());
+ QCOMPARE(copy.uriResolver(), static_cast<const QAbstractUriResolver *>(&returnURI));
+ QCOMPARE(copy.messageHandler(), &silencer);
+ QCOMPARE(testName.localName(copy.namePool()), QString::fromLatin1("somethingToCheck"));
+
+ QXmlResultItems result;
+ copy.evaluateTo(&result);
+ QCOMPARE(result.next().toAtomicValue(), QVariant(1));
+ QCOMPARE(result.next().toAtomicValue(), QVariant(2));
+ QCOMPARE(result.next().toAtomicValue(), QVariant(3));
+ QCOMPARE(result.next().toAtomicValue(), QVariant(QString::fromLatin1("a")));
+ QVERIFY(result.next().isNull());
+ QVERIFY(!result.hasError());
+ }
+
+ /* Copy, and change values. Things should detach. */
+ {
+ /* Evaluate the copy. */
+ {
+ MessageSilencer secondSilencer;
+ const ReturnURI secondUriResolver;
+ QBuffer documentDeviceCopy;
+ documentDeviceCopy.setData(QByteArray("<e>b</e>"));
+ QVERIFY(documentDeviceCopy.open(QIODevice::ReadOnly));
+
+ QXmlQuery copy;
+ copy = original;
+
+ copy.setMessageHandler(&secondSilencer);
+ /* Here we rebind variable values. */
+ copy.bindVariable(QLatin1String("var"), QXmlItem(4));
+ copy.bindVariable(QLatin1String("device"), &documentDeviceCopy);
+ copy.setUriResolver(&secondUriResolver);
+ copy.setFocus(QXmlItem(6));
+ copy.setQuery(QLatin1String("$var, 1 + 1, ., string(doc($device))"));
+
+ /* Check that the copy picked up the new things. */
+ QVERIFY(copy.isValid());
+ QCOMPARE(copy.uriResolver(), static_cast<const QAbstractUriResolver *>(&secondUriResolver));
+ QCOMPARE(copy.messageHandler(), &secondSilencer);
+
+ QXmlResultItems resultCopy;
+ copy.evaluateTo(&resultCopy);
+ QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(4));
+ QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(2));
+ QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(6));
+ const QString stringedDevice(resultCopy.next().toAtomicValue().toString());
+ QCOMPARE(stringedDevice, QString::fromLatin1("b"));
+ QVERIFY(resultCopy.next().isNull());
+ QVERIFY(!resultCopy.hasError());
+ }
+
+ /* Evaluate the original. */
+ {
+ /* Check that the original is unchanged. */
+ QVERIFY(original.isValid());
+ QCOMPARE(original.uriResolver(), static_cast<const QAbstractUriResolver *>(&returnURI));
+ QCOMPARE(original.messageHandler(), &silencer);
+
+ QXmlResultItems resultOriginal;
+ original.evaluateTo(&resultOriginal);
+ QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(1));
+ QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(2));
+ QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(3));
+ QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(QString::fromLatin1("a")));
+ QVERIFY(resultOriginal.next().isNull());
+ QVERIFY(!resultOriginal.hasError());
+ }
+ }
+ }
+}
+
+/*!
+ Since QXmlQuery doesn't seek devices to position 0, this code triggers a bug
+ where document caching doesn't work. Since the document caching doesn't work,
+ the device will be read twice, and the second time the device is at the end,
+ hence premature end of document.
+ */
+void tst_QXmlQuery::sequentialExecution() const
+{
+ QBuffer inBuffer;
+ inBuffer.setData(QByteArray("<input/>"));
+ QVERIFY(inBuffer.open(QIODevice::ReadOnly));
+
+ QXmlQuery query;
+ query.bindVariable("inputDocument", &inBuffer);
+
+ QByteArray outArray;
+ QBuffer outBuffer(&outArray);
+ outBuffer.open(QIODevice::WriteOnly);
+
+ const QString queryString(QLatin1String("doc($inputDocument)"));
+ query.setQuery(queryString);
+
+ QXmlFormatter formatter(query, &outBuffer);
+
+ QVERIFY(query.evaluateTo(&formatter));
+
+ /* If this line is removed, the bug isn't triggered. */
+ query.setQuery(queryString);
+
+ QVERIFY(query.evaluateTo(&formatter));
+}
+
+void tst_QXmlQuery::isValid() const
+{
+ /* Check default value. */
+ QXmlQuery query;
+ QVERIFY(!query.isValid());
+}
+
+void tst_QXmlQuery::bindVariableQString() const
+{
+ {
+ QXmlQuery query;
+ /* Bind with a null QXmlItem. */
+ query.bindVariable(QLatin1String("name"), QXmlItem());
+ }
+
+ {
+ QXmlQuery query;
+ /* Bind with a null QVariant. */
+ query.bindVariable(QLatin1String("name"), QXmlItem(QVariant()));
+ }
+
+ {
+ QXmlQuery query;
+ /* Bind with a null QXmlNodeModelIndex. */
+ query.bindVariable(QLatin1String("name"), QXmlItem(QXmlNodeModelIndex()));
+ }
+}
+
+void tst_QXmlQuery::bindVariableQStringNoExternalDeclaration() const
+{
+ QXmlQuery query;
+ query.bindVariable(QLatin1String("foo"), QXmlItem(QLatin1String("Variable Value")));
+ query.setQuery(QLatin1String("$foo"));
+
+ QVERIFY(query.isValid());
+
+ QStringList result;
+ QVERIFY(query.evaluateTo(&result));
+
+ QCOMPARE(result, QStringList() << QLatin1String("Variable Value"));
+}
+
+void tst_QXmlQuery::bindVariableQXmlName() const
+{
+ // TODO
+}
+
+void tst_QXmlQuery::bindVariableQXmlNameTriggerWarnings() const
+{
+ QXmlQuery query;
+
+ QTest::ignoreMessage(QtWarningMsg, "The variable name cannot be null.");
+ query.bindVariable(QXmlName(), QVariant());
+}
+
+void tst_QXmlQuery::bindVariableQStringQIODeviceWithByteArray() const
+{
+ QXmlQuery query;
+
+ QByteArray in("<e/>");
+ QBuffer device(&in);
+ QVERIFY(device.open(QIODevice::ReadOnly));
+
+ query.bindVariable("doc", &device);
+
+ query.setQuery(QLatin1String("declare variable $doc external; $doc"));
+
+ QVERIFY(query.isValid());
+
+ /* Check the URI corresponding to the variable. */
+ {
+ QXmlResultItems items;
+ query.evaluateTo(&items);
+
+ QCOMPARE(items.next().toAtomicValue().toString(), QString::fromLatin1("tag:trolltech.com,2007:QtXmlPatterns:QIODeviceVariable:doc"));
+ }
+
+ /* Now, actually load the document. We use the same QXmlQuery just to stress recompilation a bit. */
+ {
+ query.setQuery(QLatin1String("declare variable $doc external; doc($doc)"));
+
+ QByteArray out;
+ QBuffer outBuffer(&out);
+ QVERIFY(outBuffer.open(QIODevice::WriteOnly));
+
+ QXmlSerializer serializer(query, &outBuffer);
+
+ QVERIFY(query.evaluateTo(&serializer));
+ QCOMPARE(out, in);
+ }
+}
+
+void tst_QXmlQuery::bindVariableQStringQIODeviceWithString() const
+{
+ QXmlQuery query;
+
+ QString in("<qstring/>");
+ QByteArray inUtf8(in.toUtf8());
+ QBuffer inDevice(&inUtf8);
+
+ QVERIFY(inDevice.open(QIODevice::ReadOnly));
+
+ query.bindVariable("doc", &inDevice);
+
+ query.setQuery(QLatin1String("declare variable $doc external; doc($doc)"));
+
+ QByteArray out;
+ QBuffer outBuffer(&out);
+ QVERIFY(outBuffer.open(QIODevice::WriteOnly));
+
+ QXmlSerializer serializer(query, &outBuffer);
+ QVERIFY(query.evaluateTo(&serializer));
+
+ QCOMPARE(out, inUtf8);
+}
+
+void tst_QXmlQuery::bindVariableQStringQIODeviceWithQFile() const
+{
+ QXmlQuery query;
+ QFile inDevice(QLatin1String("input.xml"));
+
+ QVERIFY(inDevice.open(QIODevice::ReadOnly));
+
+ query.bindVariable("doc", &inDevice);
+
+ query.setQuery(QLatin1String("declare variable $doc external; doc($doc)"));
+
+ QByteArray out;
+ QBuffer outBuffer(&out);
+ QVERIFY(outBuffer.open(QIODevice::WriteOnly));
+
+ QXmlSerializer serializer(query, &outBuffer);
+ QVERIFY(query.evaluateTo(&serializer));
+ outBuffer.close();
+
+ QCOMPARE(out, QByteArray("<!-- This is just a file for testing. --><input/>"));
+}
+
+void tst_QXmlQuery::bindVariableQStringQIODevice() const
+{
+ QXmlQuery query;
+
+ /* Rebind the variable. */
+ {
+ /* First evaluation. */
+ {
+ QByteArray in1("<e1/>");
+ QBuffer inDevice1(&in1);
+ QVERIFY(inDevice1.open(QIODevice::ReadOnly));
+
+ query.bindVariable("in", &inDevice1);
+ query.setQuery(QLatin1String("doc($in)"));
+
+ QByteArray out1;
+ QBuffer outDevice1(&out1);
+ QVERIFY(outDevice1.open(QIODevice::WriteOnly));
+
+ QXmlSerializer serializer(query, &outDevice1);
+ query.evaluateTo(&serializer);
+ QCOMPARE(out1, in1);
+ }
+
+ /* Second evaluation, rebind variable. */
+ {
+ QByteArray in2("<e2/>");
+ QBuffer inDevice2(&in2);
+ QVERIFY(inDevice2.open(QIODevice::ReadOnly));
+
+ query.bindVariable(QLatin1String("in"), &inDevice2);
+
+ QByteArray out2;
+ QBuffer outDevice2(&out2);
+ QVERIFY(outDevice2.open(QIODevice::WriteOnly));
+
+ QXmlSerializer serializer(query, &outDevice2);
+ QVERIFY(query.evaluateTo(&serializer));
+ QCOMPARE(out2, in2);
+ }
+ }
+
+ // TODO trigger recompilation when setting qiodevices., and qiodevice overwritten by other type, etc.
+}
+
+void tst_QXmlQuery::bindVariableQXmlNameQIODevice() const
+{
+ // TODO
+}
+
+void tst_QXmlQuery::bindVariableQXmlNameQIODeviceTriggerWarnings() const
+{
+ QXmlNamePool np;
+ QXmlQuery query(np);
+
+ QBuffer buffer;
+ QTest::ignoreMessage(QtWarningMsg, "A null, or readable QIODevice must be passed.");
+ query.bindVariable(QXmlName(np, QLatin1String("foo")), &buffer);
+
+ QTest::ignoreMessage(QtWarningMsg, "The variable name cannot be null.");
+ query.bindVariable(QXmlName(), 0);
+}
+
+void tst_QXmlQuery::bindVariableXSLTSuccess() const
+{
+ QXmlQuery stylesheet(QXmlQuery::XSLT20);
+ stylesheet.setInitialTemplateName(QLatin1String("main"));
+
+ stylesheet.bindVariable(QLatin1String("variableNoSelectNoBodyBoundWithBindVariable"),
+ QVariant(QLatin1String("MUST NOT SHOW 1")));
+
+ stylesheet.bindVariable(QLatin1String("variableSelectBoundWithBindVariable"),
+ QVariant(QLatin1String("MUST NOT SHOW 2")));
+
+ stylesheet.bindVariable(QLatin1String("variableSelectWithTypeIntBoundWithBindVariable"),
+ QVariant(QLatin1String("MUST NOT SHOW 3")));
+
+ stylesheet.bindVariable(QLatin1String("paramNoSelectNoBodyBoundWithBindVariable"),
+ QVariant(QLatin1String("param1")));
+
+ stylesheet.bindVariable(QLatin1String("paramNoSelectNoBodyBoundWithBindVariableRequired"),
+ QVariant(QLatin1String("param1")));
+
+ stylesheet.bindVariable(QLatin1String("paramSelectBoundWithBindVariable"),
+ QVariant(QLatin1String("param2")));
+
+ stylesheet.bindVariable(QLatin1String("paramSelectBoundWithBindVariableRequired"),
+ QVariant(QLatin1String("param3")));
+
+ stylesheet.bindVariable(QLatin1String("paramSelectWithTypeIntBoundWithBindVariable"),
+ QVariant(4));
+
+ stylesheet.bindVariable(QLatin1String("paramSelectWithTypeIntBoundWithBindVariableRequired"),
+ QVariant(QLatin1String("param5")));
+
+ stylesheet.setQuery(QUrl(inputFile(QLatin1String("../xmlpatterns/stylesheets/parameters.xsl"))));
+
+ QVERIFY(stylesheet.isValid());
+
+ QBuffer deviceOut;
+ QVERIFY(deviceOut.open(QIODevice::ReadWrite));
+
+ QVERIFY(stylesheet.evaluateTo(&deviceOut));
+
+ const QString result(QString::fromUtf8(deviceOut.data().constData()));
+
+ QCOMPARE(result,
+ QString::fromLatin1("Variables: variableSelectsDefaultValue variableSelectsDefaultValue2 3 4 "
+ "Parameters: param1 param1 param2 param3 4 param5"));
+}
+
+void tst_QXmlQuery::bindVariableTemporaryNode() const
+{
+ /* First we do it with QXmlResultItems staying in scope. */;
+ {
+ QXmlQuery query1;
+ query1.setQuery("<anElement/>");
+
+ QXmlResultItems result1;
+ query1.evaluateTo(&result1);
+
+ QXmlQuery query2(query1);
+ query2.bindVariable("fromQuery1", result1.next());
+ query2.setQuery("$fromQuery1");
+
+ QString output;
+ QVERIFY(query2.evaluateTo(&output));
+
+ QCOMPARE(output, QString::fromLatin1("<anElement/>\n"));
+ }
+
+ /* And now with it deallocating, so its internal DynamicContext pointer is
+ * released. This doesn't work in Qt 4.5 and is ok. */
+ {
+ QXmlQuery query1;
+ query1.setQuery("<anElement/>");
+
+ QXmlQuery query2;
+
+ {
+ QXmlResultItems result1;
+ query1.evaluateTo(&result1);
+
+ query2.bindVariable("fromQuery1", result1.next());
+ query2.setQuery("$fromQuery1");
+ }
+
+ QString output;
+ return; // See comment above.
+ QVERIFY(query2.evaluateTo(&output));
+
+ QCOMPARE(output, QString::fromLatin1("<anElement/>\n"));
+ }
+}
+
+void tst_QXmlQuery::messageHandler() const
+{
+ {
+ /* Check default value. */
+ QXmlQuery query;
+ QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0));
+ }
+}
+
+void tst_QXmlQuery::setMessageHandler() const
+{
+ QXmlQuery query;
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+ QCOMPARE(&silencer, query.messageHandler());
+}
+
+void tst_QXmlQuery::evaluateToReceiver()
+{
+ QFETCH(QString, inputQuery);
+
+ /* This query prints a URI specific to the local system. */
+ if(inputQuery == QLatin1String("static-base-uri.xq"))
+ return;
+
+ ++m_pushTestsCount;
+ const QString queryURI(inputFile(QLatin1String(queriesDirectory) + inputQuery));
+ QFile queryFile(queryURI);
+
+ QVERIFY(queryFile.exists());
+ QVERIFY(queryFile.open(QIODevice::ReadOnly));
+
+ QXmlQuery query;
+
+ MessageSilencer receiver;
+ query.setMessageHandler(&receiver);
+ query.setQuery(&queryFile, QUrl::fromLocalFile(queryURI));
+
+ /* We read all the queries, and some of them are invalid. However, we
+ * only want those that compile. */
+ if(!query.isValid())
+ return;
+
+ QString produced;
+ QTextStream stream(&produced, QIODevice::WriteOnly);
+ PushBaseliner push(stream, query.namePool());
+ query.evaluateTo(&push);
+
+ const QString baselineName(inputFile(QLatin1String("pushBaselines/") + inputQuery.left(inputQuery.length() - 2) + QString::fromLatin1("ref")));
+ QFile baseline(baselineName);
+
+ if(baseline.exists())
+ {
+ QVERIFY(baseline.open(QIODevice::ReadOnly | QIODevice::Text));
+ const QString stringedBaseline(QString::fromUtf8(baseline.readAll()));
+ QCOMPARE(produced, stringedBaseline);
+ }
+ else
+ {
+ QVERIFY(baseline.open(QIODevice::WriteOnly));
+ /* This is intentionally a warning, don't remove it. Update the baselines instead. */
+ qWarning() << "Generated baseline for:" << baselineName;
+ ++m_generatedBaselines;
+
+ baseline.write(produced.toUtf8());
+ }
+}
+
+void tst_QXmlQuery::evaluateToReceiver_data() const
+{
+ QTest::addColumn<QString>("inputQuery");
+
+ const QStringList qs(queries());
+
+ for(int i = 0; i < qs.size(); ++i)
+ {
+ /* This outputs a URI specific to the environment, so we can't use it for this
+ * particular test. */
+ if(qs.at(i) != QLatin1String("staticBaseURI.xq"))
+ QTest::newRow(qs.at(i).toUtf8().constData()) << qs.at(i);
+ }
+}
+
+void tst_QXmlQuery::evaluateToReceiverOnInvalidQuery() const
+{
+ /* Invoke on a default constructed object. */
+ {
+ QByteArray out;
+ QBuffer buffer(&out);
+ buffer.open(QIODevice::WriteOnly);
+
+ QXmlQuery query;
+ QXmlSerializer serializer(query, &buffer);
+ QVERIFY(!query.evaluateTo(&serializer));
+ }
+
+ /* Invoke on an invalid query; compile time error. */
+ {
+ QByteArray out;
+ QBuffer buffer(&out);
+ buffer.open(QIODevice::WriteOnly);
+ MessageSilencer silencer;
+
+ QXmlQuery query;
+ query.setMessageHandler(&silencer);
+ query.setQuery(QLatin1String("1 + "));
+ QXmlSerializer serializer(query, &buffer);
+ QVERIFY(!query.evaluateTo(&serializer));
+ }
+
+ /* Invoke on an invalid query; runtime error. */
+ {
+ QByteArray out;
+ QBuffer buffer(&out);
+ buffer.open(QIODevice::WriteOnly);
+ MessageSilencer silencer;
+
+ QXmlQuery query;
+ query.setMessageHandler(&silencer);
+ query.setQuery(QLatin1String("error()"));
+ QXmlSerializer serializer(query, &buffer);
+ QVERIFY(!query.evaluateTo(&serializer));
+ }
+}
+
+void tst_QXmlQuery::evaluateToQStringTriggerError() const
+{
+ /* Invoke on a default constructed object. */
+ {
+ QXmlQuery query;
+ QString out;
+ QVERIFY(!query.evaluateTo(&out));
+ }
+
+ /* Invoke on an invalid query; compile time error. */
+ {
+ QXmlQuery query;
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+
+ query.setQuery(QLatin1String("1 + "));
+
+ QString out;
+ QVERIFY(!query.evaluateTo(&out));
+ }
+
+ /* Invoke on an invalid query; runtime error. */
+ {
+ QXmlQuery query;
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+
+ query.setQuery(QLatin1String("error()"));
+
+ QString out;
+ QVERIFY(!query.evaluateTo(&out));
+ }
+}
+
+void tst_QXmlQuery::evaluateToQString() const
+{
+ QFETCH(QString, query);
+ QFETCH(QString, expectedOutput);
+
+ QXmlQuery queryInstance;
+ queryInstance.setQuery(query);
+ QVERIFY(queryInstance.isValid());
+
+ QString result;
+ QVERIFY(queryInstance.evaluateTo(&result));
+
+ QCOMPARE(result, expectedOutput);
+}
+
+void tst_QXmlQuery::evaluateToQString_data() const
+{
+ QTest::addColumn<QString>("query");
+ QTest::addColumn<QString>("expectedOutput");
+
+ QTest::newRow("Two atomics")
+ << QString::fromLatin1("1, 'two'")
+ << QString::fromLatin1("1 two\n");
+
+ QTest::newRow("An element")
+ << QString::fromLatin1("<e>{1}</e>")
+ << QString::fromLatin1("<e>1</e>\n");
+}
+
+void tst_QXmlQuery::evaluateToQStringSignature() const
+{
+ const QXmlQuery query;
+
+ QString output;
+
+ /* evaluateTo(QString *) should be a const function. */
+ query.evaluateTo(&output);
+}
+
+void tst_QXmlQuery::evaluateToQAbstractXmlReceiverTriggerWarnings() const
+{
+ QXmlQuery query;
+
+ /* We check the return value as well as warning message here. */
+ QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed.");
+ QCOMPARE(query.evaluateTo(static_cast<QAbstractXmlReceiver *>(0)),
+ false);
+}
+
+void tst_QXmlQuery::evaluateToQXmlResultItems() const
+{
+ /* Invoke on a default constructed object. */
+ {
+ QXmlQuery query;
+ QXmlResultItems result;
+ query.evaluateTo(&result);
+ QVERIFY(result.next().isNull());
+ }
+}
+
+void tst_QXmlQuery::evaluateToQXmlResultItemsTriggerWarnings() const
+{
+ QTest::ignoreMessage(QtWarningMsg, "A null pointer cannot be passed.");
+ QXmlQuery query;
+ query.evaluateTo(static_cast<QXmlResultItems *>(0));
+}
+
+void tst_QXmlQuery::evaluateToQXmlResultItemsErrorAtEnd() const
+{
+ QXmlQuery query;
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+ query.setQuery(QLatin1String("1 to 100, fn:error()"));
+ QVERIFY(query.isValid());
+
+ QXmlResultItems it;
+ query.evaluateTo(&it);
+
+ while(!it.next().isNull())
+ {
+ }
+}
+
+/*!
+ If baselines were generated, we flag it as a failure such that it gets
+ attention, and that they are adjusted accordingly.
+ */
+void tst_QXmlQuery::checkGeneratedBaselines() const
+{
+ QCOMPARE(m_generatedBaselines, 0);
+
+ /* If this check fails, the auto test setup is misconfigured, or files have
+ * been added/removed without this number being updated. */
+ QCOMPARE(m_pushTestsCount, int(ExpectedQueryCount));
+}
+
+void tst_QXmlQuery::basicXQueryToQtTypeCheck() const
+{
+ QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("allAtomics.xq"));
+ QVERIFY(queryFile.open(QIODevice::ReadOnly));
+
+ QXmlQuery query;
+ query.setQuery(&queryFile);
+ QVERIFY(query.isValid());
+
+ QXmlResultItems it;
+ query.evaluateTo(&it);
+
+ QVariantList expectedValues;
+ expectedValues.append(QString::fromLatin1("xs:untypedAtomic"));
+ expectedValues.append(QDateTime(QDate(2002, 10, 10), QTime(23, 2, 11), Qt::UTC));
+ expectedValues.append(QDate(2002, 10, 10));
+ expectedValues.append(QVariant()); /* We currently doesn't support xs:time through the API. */
+
+ expectedValues.append(QVariant()); /* xs:duration */
+ expectedValues.append(QVariant()); /* xs:dayTimeDuration */
+ expectedValues.append(QVariant()); /* xs:yearMonthDuration */
+
+ expectedValues.append(QVariant(double(3e3))); /* xs:float */
+ expectedValues.append(QVariant(double(4e4))); /* xs:double */
+ expectedValues.append(QVariant(double(2))); /* xs:decimal */
+
+ /* xs:integer and its sub-types. */
+ expectedValues.append(QVariant(qlonglong(16)));
+ expectedValues.append(QVariant(qlonglong(-6)));
+ expectedValues.append(QVariant(qlonglong(-4)));
+ expectedValues.append(QVariant(qlonglong(5)));
+ expectedValues.append(QVariant(qlonglong(6)));
+ expectedValues.append(QVariant(qlonglong(7)));
+ expectedValues.append(QVariant(qlonglong(8)));
+ expectedValues.append(QVariant(qlonglong(9)));
+ expectedValues.append(QVariant(qulonglong(10)));
+ expectedValues.append(QVariant(qlonglong(11)));
+ expectedValues.append(QVariant(qlonglong(12)));
+ expectedValues.append(QVariant(qlonglong(13)));
+ expectedValues.append(QVariant(qlonglong(14)));
+
+ expectedValues.append(QVariant()); /* xs:gYearMonth("1976-02"), */
+ expectedValues.append(QVariant()); /* xs:gYear("2005-12:00"), */
+ expectedValues.append(QVariant()); /* xs:gMonthDay("--12-25-14:00"), */
+ expectedValues.append(QVariant()); /* xs:gDay("---25-14:00"), */
+ expectedValues.append(QVariant()); /* xs:gMonth("--12-14:00"), */
+ expectedValues.append(true); /* xs:boolean("true"), */
+ expectedValues.append(QVariant(QByteArray::fromBase64(QByteArray("aaaa")))); /* xs:base64Binary("aaaa"), */
+ expectedValues.append(QVariant(QByteArray::fromHex(QByteArray("FFFF")))); /* xs:hexBinary("FFFF"), */
+ expectedValues.append(QVariant(QString::fromLatin1("http://example.com/"))); /* xs:anyURI("http://example.com/"), */
+ QXmlNamePool np(query.namePool());
+ expectedValues.append(QVariant(qVariantFromValue(QXmlName(np, QLatin1String("localName"),
+ QLatin1String("http://example.com/2"),
+ QLatin1String("prefix")))));
+
+ expectedValues.append(QVariant(QString::fromLatin1("An xs:string")));
+ expectedValues.append(QVariant(QString::fromLatin1("normalizedString")));
+ expectedValues.append(QVariant(QString::fromLatin1("token")));
+ expectedValues.append(QVariant(QString::fromLatin1("language")));
+ expectedValues.append(QVariant(QString::fromLatin1("NMTOKEN")));
+ expectedValues.append(QVariant(QString::fromLatin1("Name")));
+ expectedValues.append(QVariant(QString::fromLatin1("NCName")));
+ expectedValues.append(QVariant(QString::fromLatin1("ID")));
+ expectedValues.append(QVariant(QString::fromLatin1("IDREF")));
+ expectedValues.append(QVariant(QString::fromLatin1("ENTITY")));
+
+ int i = 0;
+ QXmlItem item(it.next());
+
+ while(!item.isNull())
+ {
+ QVERIFY(item.isAtomicValue());
+ const QVariant produced(item.toAtomicValue());
+
+ const QVariant &expected = expectedValues.at(i);
+
+ /* For the cases where we can't represent a value in the XDM with Qt,
+ * we return an invalid QVariant. */
+ QCOMPARE(expected.isValid(), produced.isValid());
+
+ QCOMPARE(produced.type(), expected.type());
+
+ if(expected.isValid())
+ {
+ /* This is only needed for xs:decimal though, for some reason. Probably
+ * just artifacts created somewhere. */
+ if(produced.type() == QVariant::Double)
+ QVERIFY(qFuzzyCompare(produced.toDouble(), expected.toDouble()));
+ else if(qVariantCanConvert<QXmlName>(produced))
+ {
+ /* QVariant::operator==() does identity comparison, it doesn't delegate to operator==() of
+ * the contained type, unless it's hardcoded into QVariant. */
+ const QXmlName n1 = qVariantValue<QXmlName>(produced);
+ const QXmlName n2 = qVariantValue<QXmlName>(expected);
+ QCOMPARE(n1, n2);
+ }
+ else
+ QCOMPARE(produced, expected);
+ }
+
+ ++i;
+ item = it.next();
+ }
+
+ QCOMPARE(i, expectedValues.count());
+}
+
+/*!
+ Send values from Qt into XQuery.
+ */
+void tst_QXmlQuery::basicQtToXQueryTypeCheck() const
+{
+ QFile queryFile(QLatin1String(queriesDirectory) + QLatin1String("allAtomicsExternally.xq"));
+ QVERIFY(queryFile.exists());
+ QVERIFY(queryFile.open(QIODevice::ReadOnly));
+
+ QCOMPARE(QVariant(QDate(1999, 9, 10)).type(), QVariant::Date);
+
+ QXmlQuery query;
+
+ QXmlNamePool np(query.namePool());
+
+ const QXmlName name(np, QLatin1String("localname"),
+ QLatin1String("http://example.com"),
+ QLatin1String("prefix"));
+
+ query.bindVariable(QLatin1String("fromQUrl"), QXmlItem(QUrl(QString::fromLatin1("http://example.com/"))));
+ query.bindVariable(QLatin1String("fromQByteArray"), QXmlItem(QByteArray("AAAA")));
+ query.bindVariable(QLatin1String("fromBool"), QXmlItem(bool(true)));
+ query.bindVariable(QLatin1String("fromQDate"), QXmlItem(QDate(2000, 10, 11)));
+ // TODO Do with different QDateTime time specs
+ query.bindVariable(QLatin1String("fromQDateTime"), QXmlItem(QDateTime(QDate(2001, 9, 10), QTime(1, 2, 3))));
+ query.bindVariable(QLatin1String("fromDouble"), QXmlItem(double(3)));
+ query.bindVariable(QLatin1String("fromFloat"), QXmlItem(float(4)));
+ query.bindVariable(QLatin1String("integer"), QXmlItem(5));
+ query.bindVariable(QLatin1String("fromQString"), QXmlItem(QString::fromLatin1("A QString")));
+ query.bindVariable(QLatin1String("fromQChar"), QXmlItem(QChar::fromLatin1('C')));
+
+ query.bindVariable(QLatin1String("fromIntLiteral"), QXmlItem(QVariant(654)));
+
+ {
+ QVariant ui(uint(5));
+ QCOMPARE(ui.type(), QVariant::UInt);
+ query.bindVariable(QLatin1String("fromUInt"), ui);
+ }
+
+ {
+ QVariant ulnglng(qulonglong(6));
+ QCOMPARE(ulnglng.type(), QVariant::ULongLong);
+ query.bindVariable(QLatin1String("fromULongLong"), ulnglng);
+ }
+
+ {
+ QVariant qlnglng(qlonglong(7));
+ QCOMPARE(qlnglng.type(), QVariant::LongLong);
+ query.bindVariable(QLatin1String("fromLongLong"), qlnglng);
+ }
+
+ query.setQuery(&queryFile);
+
+ // TODO do queries which declares external variables with types. Tons of combos here.
+ // TODO ensure that binding with QXmlItem() doesn't make a binding available.
+ // TODO test rebinding a variable.
+
+ QVERIFY(query.isValid());
+
+ QXmlResultItems it;
+ query.evaluateTo(&it);
+ QXmlItem item(it.next());
+ QVERIFY(!item.isNull());
+ QVERIFY(item.isAtomicValue());
+
+ QCOMPARE(item.toAtomicValue().toString(),
+ QLatin1String("4 true 3 654 7 41414141 C 2000-10-11Z 2001-09-10T01:02:03 "
+ "A QString http://example.com/ 5 6 true true true true true true true true true true "
+ "true true true"));
+}
+
+void tst_QXmlQuery::bindNode() const
+{
+ QXmlQuery query;
+ TestSimpleNodeModel nodeModel(query.namePool());
+
+ query.bindVariable(QLatin1String("node"), nodeModel.root());
+ QByteArray out;
+ QBuffer buff(&out);
+ QVERIFY(buff.open(QIODevice::WriteOnly));
+
+ query.setQuery(QLatin1String("declare variable $node external; $node"));
+ QXmlSerializer serializer(query, &buff);
+
+ QVERIFY(query.evaluateTo(&serializer));
+ QCOMPARE(out, QByteArray("<nodeName/>"));
+}
+
+/*!
+ Pass in a relative URI, and make sure it is resolved against the current application directory.
+ */
+void tst_QXmlQuery::relativeBaseURI() const
+{
+ QXmlQuery query;
+ query.setQuery(QLatin1String("fn:static-base-uri()"), QUrl(QLatin1String("a/relative/uri.weirdExtension")));
+ QVERIFY(query.isValid());
+
+ QByteArray result;
+ QBuffer buffer(&result);
+ QVERIFY(buffer.open(QIODevice::ReadWrite));
+
+ QXmlSerializer serializer(query, &buffer);
+ QVERIFY(query.evaluateTo(&serializer));
+
+ const QUrl loaded(QUrl::fromEncoded(result));
+ QUrl appPath(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()));
+
+ QVERIFY(loaded.isValid());
+ QVERIFY(appPath.isValid());
+ QVERIFY(!loaded.isRelative());
+ QVERIFY(!appPath.isRelative());
+
+ QFileInfo dir(appPath.toLocalFile());
+ dir.setFile(QString());
+
+ /* We can't use QUrl::isParentOf() because it doesn't do what we want it to */
+ if(!loaded.toLocalFile().startsWith(dir.absoluteFilePath()))
+ QTextStream(stderr) << "dir.absoluteFilePath():" << dir.absoluteFilePath() << "loaded.toLocalFile():" << loaded.toLocalFile();
+
+ checkBaseURI(loaded, dir.absoluteFilePath());
+}
+
+void tst_QXmlQuery::emptyBaseURI() const
+{
+ QXmlQuery query;
+ query.setQuery(QLatin1String("fn:static-base-uri()"), QUrl());
+ QVERIFY(query.isValid());
+
+ QByteArray result;
+ QBuffer buffer(&result);
+ QVERIFY(buffer.open(QIODevice::ReadWrite));
+
+ QXmlSerializer serializer(query, &buffer);
+ QVERIFY(query.evaluateTo(&serializer));
+
+ const QUrl loaded(QUrl::fromEncoded(result));
+ QUrl appPath(QUrl::fromLocalFile(QCoreApplication::applicationFilePath()));
+
+ QVERIFY(loaded.isValid());
+ QVERIFY(appPath.isValid());
+ QVERIFY(!loaded.isRelative());
+ QVERIFY(!appPath.isRelative());
+
+ QFileInfo dir(appPath.toLocalFile());
+ dir.setFile(QString());
+
+ QCOMPARE(loaded, appPath);
+}
+
+/*!
+ Ensure that QDate comes out as QDateTime.
+ */
+void tst_QXmlQuery::roundTripDateWithinQXmlItem() const
+{
+ const QDate date(1999, 9, 10);
+ QVERIFY(date.isValid());
+
+ const QVariant variant(date);
+ QVERIFY(variant.isValid());
+ QCOMPARE(variant.type(), QVariant::Date);
+
+ const QXmlItem item(variant);
+ QVERIFY(!item.isNull());
+ QVERIFY(item.isAtomicValue());
+
+ const QVariant out(item.toAtomicValue());
+ QVERIFY(out.isValid());
+ QCOMPARE(out.type(), QVariant::Date);
+ QCOMPARE(out.toDate(), date);
+}
+
+/*!
+ Check whether a query is valid, which uses an unbound variable.
+ */
+void tst_QXmlQuery::bindingMissing() const
+{
+ QXmlQuery query;
+ MessageSilencer messageHandler;
+ query.setMessageHandler(&messageHandler);
+
+ QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
+ QVERIFY(queryFile.open(QIODevice::ReadOnly));
+ query.setQuery(&queryFile);
+
+ QVERIFY(!query.isValid());
+}
+
+void tst_QXmlQuery::bindDefaultConstructedItem() const
+{
+ QFETCH(QXmlItem, item);
+
+ QXmlQuery query;
+ MessageSilencer messageHandler;
+ query.setMessageHandler(&messageHandler);
+
+ QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
+ QVERIFY(queryFile.open(QIODevice::ReadOnly));
+ query.setQuery(&queryFile);
+ query.bindVariable(QLatin1String("externalVariable"), item);
+
+ QVERIFY(!query.isValid());
+}
+
+void tst_QXmlQuery::bindDefaultConstructedItem_data() const
+{
+ QTest::addColumn<QXmlItem>("item");
+
+ QTest::newRow("QXmlItem()") << QXmlItem();
+ QTest::newRow("QXmlItem(QVariant())") << QXmlItem(QVariant());
+ QTest::newRow("QXmlItem(QXmlNodeModelIndex())") << QXmlItem(QXmlNodeModelIndex());
+}
+
+/*!
+ Remove a binding by binding QXmlItem() with the same name.
+ */
+void tst_QXmlQuery::eraseQXmlItemBinding() const
+{
+ QXmlQuery query;
+ MessageSilencer messageHandler;
+ query.setMessageHandler(&messageHandler);
+
+ QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
+ QVERIFY(queryFile.open(QIODevice::ReadOnly));
+ query.bindVariable(QLatin1String("externalVariable"), QXmlItem(3));
+ query.setQuery(&queryFile);
+ QVERIFY(query.isValid());
+
+ QByteArray result;
+ QBuffer buffer(&result);
+ QVERIFY(buffer.open(QIODevice::ReadWrite));
+
+ QXmlSerializer serializer(query, &buffer);
+ QVERIFY(query.evaluateTo(&serializer));
+
+ QCOMPARE(result, QByteArray("3 6<e>3</e>false"));
+
+ query.bindVariable(QLatin1String("externalVariable"), QXmlItem());
+ QVERIFY(!query.isValid());
+}
+
+/*!
+ Erase a variable binding
+ */
+void tst_QXmlQuery::eraseDeviceBinding() const
+{
+ /* Erase an existing QIODevice binding with another QIODevice binding. */
+ {
+ QXmlQuery query;
+
+ QByteArray doc("<e/>");
+ QBuffer buffer(&doc);
+ QVERIFY(buffer.open(QIODevice::ReadOnly));
+
+ query.bindVariable(QLatin1String("in"), &buffer);
+ query.setQuery(QLatin1String("$in"));
+ QVERIFY(query.isValid());
+
+ query.bindVariable(QLatin1String("in"), 0);
+ QVERIFY(!query.isValid());
+ }
+
+ /* Erase an existing QXmlItem binding with another QIODevice binding. */
+ {
+ QXmlQuery query;
+
+ query.bindVariable(QLatin1String("in"), QXmlItem(5));
+ query.setQuery(QLatin1String("$in"));
+ QVERIFY(query.isValid());
+
+ query.bindVariable(QLatin1String("in"), 0);
+ QVERIFY(!query.isValid());
+ }
+}
+
+/*!
+ Bind a variable, evaluate, bind with a different value but same type, and evaluate again.
+ */
+void tst_QXmlQuery::rebindVariableSameType() const
+{
+ QXmlQuery query;
+ MessageSilencer messageHandler;
+ query.setMessageHandler(&messageHandler);
+
+ query.bindVariable(QLatin1String("externalVariable"), QXmlItem(3));
+
+ {
+ QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq"));
+ QVERIFY(queryFile.open(QIODevice::ReadOnly));
+ query.setQuery(&queryFile);
+ }
+
+ QVERIFY(query.isValid());
+
+ {
+ QByteArray result;
+ QBuffer buffer(&result);
+ QVERIFY(buffer.open(QIODevice::ReadWrite));
+
+ QXmlSerializer serializer(query, &buffer);
+ QVERIFY(query.evaluateTo(&serializer));
+
+ QCOMPARE(result, QByteArray("3 6<e>3</e>false"));
+ }
+
+ {
+ query.bindVariable(QLatin1String("externalVariable"), QXmlItem(5));
+ QByteArray result;
+ QBuffer buffer(&result);
+ QVERIFY(buffer.open(QIODevice::ReadWrite));
+
+ QXmlSerializer serializer(query, &buffer);
+ QVERIFY(query.evaluateTo(&serializer));
+
+ QCOMPARE(result, QByteArray("5 8<e>5</e>false"));
+ }
+
+}
+
+void tst_QXmlQuery::rebindVariableDifferentType() const
+{
+ /* Rebind QXmlItem variable with QXmlItem variable. */
+ {
+ QXmlQuery query;
+ query.bindVariable(QLatin1String("in"), QXmlItem(3));
+ query.setQuery(QLatin1String("$in"));
+ QVERIFY(query.isValid());
+
+ query.bindVariable(QLatin1String("in"), QXmlItem("A string"));
+ QVERIFY(!query.isValid());
+ }
+
+ /* Rebind QIODevice variable with QXmlItem variable. */
+ {
+ QXmlQuery query;
+ QBuffer buffer;
+ buffer.setData(QByteArray("<e/>"));
+ QVERIFY(buffer.open(QIODevice::ReadOnly));
+
+ query.bindVariable(QLatin1String("in"), &buffer);
+ query.setQuery(QLatin1String("$in"));
+ QVERIFY(query.isValid());
+
+ query.bindVariable(QLatin1String("in"), QXmlItem("A string"));
+ QVERIFY(!query.isValid());
+ }
+
+ /* Rebind QXmlItem variable with QIODevice variable. The type of the
+ * variable changes, so a recompile is necessary. */
+ {
+ QXmlQuery query;
+
+ query.bindVariable(QLatin1String("in"), QXmlItem(QLatin1String("A string")));
+ query.setQuery(QLatin1String("$in"));
+ QVERIFY(query.isValid());
+
+ QBuffer buffer;
+ buffer.setData(QByteArray("<e/>"));
+ QVERIFY(buffer.open(QIODevice::ReadOnly));
+ query.bindVariable(QLatin1String("in"), &buffer);
+ QVERIFY(!query.isValid());
+ }
+}
+
+void tst_QXmlQuery::rebindVariableWithNullItem() const
+{
+ QXmlQuery query;
+
+ query.bindVariable(QLatin1String("name"), QXmlItem(5));
+ query.bindVariable(QLatin1String("name"), QXmlItem());
+}
+
+void tst_QXmlQuery::constCorrectness() const
+{
+ QXmlResultItems result;
+ QXmlQuery tmp;
+ tmp.setQuery(QLatin1String("1")); /* Just so we have a valid query. */
+ const QXmlQuery query(tmp);
+
+ /* These functions should be const. */
+ query.isValid();
+ query.evaluateTo(&result);
+ query.namePool();
+ query.uriResolver();
+ query.messageHandler();
+
+ {
+ QString dummyString;
+ QTextStream dummyStream(&dummyString);
+ PushBaseliner dummy(dummyStream, query.namePool());
+ query.evaluateTo(&dummy);
+ }
+}
+
+void tst_QXmlQuery::objectSize() const
+{
+ /* We have a d pointer. */
+ QCOMPARE(sizeof(QXmlQuery), sizeof(void *));
+}
+
+void tst_QXmlQuery::setUriResolver() const
+{
+ /* Set a null resolver, and make sure it can take a const pointer. */
+ {
+ QXmlQuery query;
+ query.setUriResolver(static_cast<const QAbstractUriResolver *>(0));
+ QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0));
+ }
+
+ {
+ TestURIResolver resolver;
+ QXmlQuery query;
+ query.setUriResolver(&resolver);
+ QCOMPARE(query.uriResolver(), &resolver);
+ }
+}
+
+void tst_QXmlQuery::uriResolver() const
+{
+ /* Check default value. */
+ {
+ QXmlQuery query;
+ QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0));
+ }
+}
+
+void tst_QXmlQuery::messageXML() const
+{
+ QXmlQuery query;
+
+ MessageValidator messageValidator;
+ query.setMessageHandler(&messageValidator);
+
+ query.setQuery(QLatin1String("1basicSyntaxError"));
+
+ const QRegExp removeFilename(QLatin1String("Location: file:.*\\#"));
+ QVERIFY(removeFilename.isValid());
+
+ QVERIFY(messageValidator.success());
+ QCOMPARE(messageValidator.received().remove(removeFilename),
+ QString::fromLatin1("Type:3\n"
+ "Description: <html xmlns='http://www.w3.org/1999/xhtml/'><body><p>syntax error, unexpected unknown keyword</p></body></html>\n"
+ "Identifier: http://www.w3.org/2005/xqt-errors#XPST0003\n"
+ "1,1"));
+}
+
+/*!
+ 1. Allocate QXmlResultItems
+ 2. Allocate QXmlQuery
+ 3. evaluate to the QXmlResultItems instance
+ 4. Dellocate the QXmlQuery instance
+ 5. Ensure QXmlResultItems works
+ */
+void tst_QXmlQuery::resultItemsDeallocatedQuery() const
+{
+ QXmlResultItems result;
+
+ {
+ QXmlQuery query;
+ query.setQuery(QLatin1String("1, 2, xs:integer(<e>3</e>)"));
+ query.evaluateTo(&result);
+ }
+
+ QCOMPARE(result.next().toAtomicValue(), QVariant(1));
+ QCOMPARE(result.next().toAtomicValue(), QVariant(2));
+ QCOMPARE(result.next().toAtomicValue(), QVariant(3));
+ QVERIFY(result.next().isNull());
+ QVERIFY(!result.hasError());
+}
+
+/*!
+ 1. Bind variable with bindVariable()
+ 2. setQuery that has 'declare variable' with same name.
+ 3. Ensure the value inside the query is used. We don't guarantee this behavior
+ but that's what we lock.
+ */
+void tst_QXmlQuery::shadowedVariables() const
+{
+ QXmlQuery query;
+ query.bindVariable("varName", QXmlItem(3));
+ query.setQuery(QLatin1String("declare variable $varName := 5; $varName"));
+
+ QXmlResultItems result;
+ query.evaluateTo(&result);
+
+ QCOMPARE(result.next().toAtomicValue(), QVariant(5));
+}
+
+void tst_QXmlQuery::setFocusQXmlItem() const
+{
+ /* Make sure we can take a const reference. */
+ {
+ QXmlQuery query;
+ const QXmlItem item;
+ query.setFocus(item);
+ }
+
+ // TODO evaluate with atomic value, check type
+ // TODO evaluate with node, check type
+ // TODO ensure that setFocus() triggers query recompilation, as appropriate.
+ // TODO let the focus be undefined, call isvalid, call evaluate anyway
+ // TODO let the focus be undefined, call evaluate directly
+}
+
+void tst_QXmlQuery::setFocusQUrl() const
+{
+ /* Load a focus which isn't well-formed. */
+ {
+ QXmlQuery query;
+ MessageSilencer silencer;
+
+ query.setMessageHandler(&silencer);
+
+ QVERIFY(!query.setFocus(QUrl(QLatin1String("data/notWellformed.xml"))));
+ }
+
+ /* Ensure the same URI resolver is used. */
+ {
+ QXmlQuery query(QXmlQuery::XSLT20);
+
+ const TestURIResolver resolver(QUrl(inputFile(QLatin1String("../xmlpatterns/stylesheets/documentElement.xml"))));
+ query.setUriResolver(&resolver);
+
+ QVERIFY(query.setFocus(QUrl(QLatin1String("arbitraryURI"))));
+ query.setQuery(QUrl(inputFile(QLatin1String("../xmlpatterns/stylesheets/copyWholeDocument.xsl"))));
+ QVERIFY(query.isValid());
+
+ QBuffer result;
+ QVERIFY(result.open(QIODevice::ReadWrite));
+ QXmlSerializer serializer(query, &result);
+ query.evaluateTo(&serializer);
+
+ QCOMPARE(result.data(), QByteArray("<doc/>"));
+ }
+
+ // TODO ensure that the focus type doesn't change from XSLT20 on the main instance.
+}
+
+/*!
+ This code poses a challenge wrt. to internal caching.
+ */
+void tst_QXmlQuery::setFocusQIODevice() const
+{
+ QXmlQuery query;
+
+ {
+ QBuffer focus;
+ focus.setData(QByteArray("<e>abc</e>"));
+ QVERIFY(focus.open(QIODevice::ReadOnly));
+ query.setFocus(&focus);
+ query.setQuery(QLatin1String("string()"));
+ QVERIFY(query.isValid());
+
+ QString output;
+ query.evaluateTo(&output);
+
+ QCOMPARE(output, QString::fromLatin1("abc\n"));
+ }
+
+ /* Set a new focus, make sure it changes & works. */
+ {
+ QBuffer focus2;
+ focus2.setData(QByteArray("<e>abc2</e>"));
+ QVERIFY(focus2.open(QIODevice::ReadOnly));
+ query.setFocus(&focus2);
+ QVERIFY(query.isValid());
+
+ QString output;
+ query.evaluateTo(&output);
+
+ QCOMPARE(output, QString::fromLatin1("abc2\n"));
+ }
+}
+
+/*!
+ Since we internally use variable bindings for implementing the focus, we need
+ to make sure we don't clash in this area.
+*/
+void tst_QXmlQuery::setFocusQIODeviceAvoidVariableClash() const
+{
+ QBuffer buffer;
+ buffer.setData("<e>focus</e>");
+ QVERIFY(buffer.open(QIODevice::ReadOnly));
+
+ /* First we bind the variable name, then the focus. */
+ {
+ QXmlQuery query;
+ query.bindVariable(QString(QLatin1Char('u')), QVariant(1));
+ query.setFocus(&buffer);
+ query.setQuery(QLatin1String("string()"));
+
+ QString out;
+ query.evaluateTo(&out);
+
+ QCOMPARE(out, QString::fromLatin1("focus\n"));
+ }
+
+ /* First we bind the focus, then the variable name. */
+ {
+ QXmlQuery query;
+ QVERIFY(buffer.open(QIODevice::ReadOnly));
+ query.setFocus(&buffer);
+ query.bindVariable(QString(QLatin1Char('u')), QVariant(1));
+ query.setQuery(QLatin1String("string()"));
+
+ QString out;
+ query.evaluateTo(&out);
+
+ QCOMPARE(out, QString::fromLatin1("focus\n"));
+ }
+}
+
+void tst_QXmlQuery::setFocusQIODeviceFailure() const
+{
+ /* A not well-formed input document. */
+ {
+ QXmlQuery query;
+
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+
+ QBuffer input;
+ input.setData("<e");
+ QVERIFY(input.open(QIODevice::ReadOnly));
+
+ QCOMPARE(query.setFocus(&input), false);
+ }
+}
+
+void tst_QXmlQuery::setFocusQIODeviceTriggerWarnings() const
+{
+ /* A null pointer. */
+ {
+ QXmlQuery query;
+
+ QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed.");
+ QCOMPARE(query.setFocus(static_cast<QIODevice *>(0)), false);
+ }
+
+ /* A non opened-device. */
+ {
+ QXmlQuery query;
+
+ QBuffer notReadable;
+ QVERIFY(!notReadable.isReadable());
+
+ QTest::ignoreMessage(QtWarningMsg, "The device must be readable.");
+ QCOMPARE(query.setFocus(&notReadable), false);
+ }
+}
+
+void tst_QXmlQuery::fnDocNetworkAccessSuccess() const
+{
+#if defined(Q_OS_WINCE) && !defined(_X86_)
+ QStringList testsToSkip;
+ testsToSkip << "http scheme" << "ftp scheme";
+ if (testsToSkip.contains(QTest::currentDataTag()))
+ QSKIP("Network tests are currently unsupported on Windows CE.", SkipSingle);
+#endif
+
+ QFETCH(QUrl, uriToOpen);
+ QFETCH(QByteArray, expectedOutput);
+
+ if(!uriToOpen.isValid())
+ qDebug() << "uriToOpen:" << uriToOpen;
+
+ QVERIFY(uriToOpen.isValid());
+
+ QXmlQuery query;
+ query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen));
+ query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)"));
+ QVERIFY(query.isValid());
+
+ QByteArray result;
+ QBuffer buffer(&result);
+ QVERIFY(buffer.open(QIODevice::WriteOnly));
+
+ QXmlSerializer serializer(query, &buffer);
+ QVERIFY(query.evaluateTo(&serializer));
+
+ QCOMPARE(result, expectedOutput);
+}
+
+void tst_QXmlQuery::fnDocNetworkAccessSuccess_data() const
+{
+ QTest::addColumn<QUrl>("uriToOpen");
+ QTest::addColumn<QByteArray>("expectedOutput");
+
+ QTest::newRow("file scheme")
+ << inputFileAsURI(QLatin1String("input.xml"))
+ << QByteArray("<!-- This is just a file for testing. --><input/>");
+
+ QTest::newRow("data scheme with ASCII")
+ /* QUrl::toPercentEncoding(QLatin1String("<e/>")) yields "%3Ce%2F%3E". */
+ << QUrl::fromEncoded("data:application/xml,%3Ce%2F%3E")
+ << QByteArray("<e/>");
+
+ QTest::newRow("data scheme with ASCII no MIME type")
+ << QUrl::fromEncoded("data:,%3Ce%2F%3E")
+ << QByteArray("<e/>");
+
+ QTest::newRow("data scheme with base 64")
+ << QUrl::fromEncoded("data:application/xml;base64,PGUvPg==")
+ << QByteArray("<e/>");
+
+ QTest::newRow("qrc scheme")
+ << QUrl::fromEncoded("qrc:/QXmlQueryTestData/data/oneElement.xml")
+ << QByteArray("<oneElement/>");
+
+ if(!m_testNetwork)
+ return;
+
+ QTest::newRow("http scheme")
+ << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qxmlquery/wellFormed.xml"))
+ << QByteArray("<!-- a comment --><e from=\"http\">Some Text</e>");
+
+ QTest::newRow("ftp scheme")
+ << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/wellFormed.xml"))
+ << QByteArray("<!-- a comment --><e from=\"ftp\">Some Text</e>");
+
+}
+
+void tst_QXmlQuery::fnDocNetworkAccessFailure() const
+{
+ QFETCH(QUrl, uriToOpen);
+
+ QVERIFY(uriToOpen.isValid());
+
+ QXmlQuery query;
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+ query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen));
+ query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)"));
+ QVERIFY(query.isValid());
+
+ QXmlResultItems result;
+ query.evaluateTo(&result);
+
+ while(!result.next().isNull())
+ {
+ /* Just loop until the end. */
+ }
+
+ // TODO do something that triggers a /timeout/.
+ QVERIFY(result.hasError());
+}
+
+void tst_QXmlQuery::fnDocNetworkAccessFailure_data() const
+{
+ QTest::addColumn<QUrl>("uriToOpen");
+
+ QTest::newRow("data scheme, not-well-formed")
+ << QUrl(QLatin1String("data:application/xml;base64,PGUvg==="));
+
+ QTest::newRow("file scheme, non-existant file")
+ << QUrl(QLatin1String("file:///example.com/does/notExist.xml"));
+
+ QTest::newRow("http scheme, file not found")
+ << QUrl(QLatin1String("http://www.example.com/does/not/exist.xml"));
+
+ QTest::newRow("http scheme, nonexistent host")
+ << QUrl(QLatin1String("http://this.host.does.not.exist.I.SWear"));
+
+ QTest::newRow("qrc scheme, not well-formed")
+ << QUrl(QLatin1String("qrc:/QXmlQueryTestData/notWellformed.xml"));
+
+ QTest::newRow("'qrc:/', non-existing file")
+ << QUrl(QLatin1String(":/QXmlQueryTestData/data/thisFileDoesNotExist.xml"));
+
+ QTest::newRow("':/', this scheme is not supported")
+ << QUrl(QLatin1String(":/QXmlQueryTestData/data/notWellformed.xml"));
+
+ if(!m_testNetwork)
+ return;
+
+ QTest::newRow("http scheme, not well-formed")
+ << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qxmlquery/notWellformed.xml"));
+
+ QTest::newRow("https scheme, not well-formed")
+ << QUrl(QString("https://" + QtNetworkSettings::serverName() + "/qxmlquery/notWellformedViaHttps.xml"));
+
+ QTest::newRow("https scheme, nonexistent host")
+ << QUrl(QLatin1String("https://this.host.does.not.exist.I.SWear"));
+
+ QTest::newRow("ftp scheme, nonexistent host")
+ << QUrl(QLatin1String("ftp://this.host.does.not.exist.I.SWear"));
+
+ QTest::newRow("ftp scheme, not well-formed")
+ << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/notWellFormed.xml"));
+}
+
+/*!
+ Create a network timeout from a QIODevice binding such
+ that we ensure we don't hang infinitely.
+ */
+void tst_QXmlQuery::fnDocOnQIODeviceTimeout() const
+{
+ if(!m_testNetwork)
+ return;
+
+ QTcpServer server;
+ server.listen(QHostAddress::LocalHost, 1088);
+
+ QTcpSocket client;
+ client.connectToHost("localhost", 1088);
+ QVERIFY(client.isReadable());
+
+ QXmlQuery query;
+
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+
+ query.bindVariable(QLatin1String("inDevice"), &client);
+ query.setQuery(QLatin1String("declare variable $inDevice external;\ndoc($inDevice)"));
+ QVERIFY(query.isValid());
+
+ QXmlResultItems result;
+ query.evaluateTo(&result);
+ QXmlItem next(result.next());
+
+ while(!next.isNull())
+ {
+ next = result.next();
+ }
+
+ QVERIFY(result.hasError());
+}
+
+/*!
+ When changing query, the static context must change too, such that
+ the source locations are updated.
+ */
+void tst_QXmlQuery::recompilationWithEvaluateToResultFailing() const
+{
+ QXmlQuery query;
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+
+ query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
+ QVERIFY(query.isValid()); /* Trigger query compilation. */
+
+ query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
+ QVERIFY(query.isValid()); /* Trigger second compilation. */
+
+ QXmlResultItems items;
+ query.evaluateTo(&items);
+ items.next();
+ QVERIFY(items.hasError());
+}
+
+void tst_QXmlQuery::secondEvaluationWithEvaluateToResultFailing() const
+{
+ QXmlQuery query;
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+
+ query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
+ QVERIFY(query.isValid()); /* Trigger query compilation. */
+
+ query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
+ /* We don't call isValid(). */
+QXmlResultItems items;
+ query.evaluateTo(&items);
+ items.next();
+ QVERIFY(items.hasError());
+}
+
+/*!
+ Compilation is triggered in the evaluation function due to no call to QXmlQuery::isValid().
+ */
+void tst_QXmlQuery::recompilationWithEvaluateToReceiver() const
+{
+ QXmlQuery query;
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+
+ query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */
+ QVERIFY(query.isValid()); /* Trigger query compilation. */
+
+ query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */
+ /* We don't call isValid(). */
+
+ QByteArray dummy;
+ QBuffer buffer(&dummy);
+ buffer.open(QIODevice::WriteOnly);
+
+ QXmlSerializer serializer(query, &buffer);
+
+ QVERIFY(!query.evaluateTo(&serializer));
+}
+
+void tst_QXmlQuery::evaluateToQStringListOnInvalidQuery() const
+{
+ MessageSilencer silencer;
+
+ /* Invoke on a default constructed object. */
+ {
+ QXmlQuery query;
+ QStringList out;
+ QVERIFY(!query.evaluateTo(&out));
+ }
+
+ /* Invoke on a syntactically invalid query. */
+ {
+ QXmlQuery query;
+ QStringList out;
+ MessageSilencer silencer;
+
+ query.setMessageHandler(&silencer);
+ query.setQuery(QLatin1String("1 + "));
+
+ QVERIFY(!query.evaluateTo(&out));
+ }
+
+ /* Invoke on a query with the wrong type, one atomic. */
+ {
+ QXmlQuery query;
+ QStringList out;
+
+ query.setQuery(QLatin1String("1"));
+ query.setMessageHandler(&silencer);
+ QVERIFY(!query.evaluateTo(&out));
+ }
+
+ /* Invoke on a query with the wrong type, one element. */
+ {
+ QXmlQuery query;
+ QStringList out;
+
+ query.setQuery(QLatin1String("<e/>"));
+ QVERIFY(!query.evaluateTo(&out));
+ }
+
+ /* Invoke on a query with the wrong type, mixed nodes & atomics. */
+ {
+ QXmlQuery query;
+ QStringList out;
+
+ query.setQuery(QLatin1String("<e/>, 1, 'a string'"));
+ query.setMessageHandler(&silencer);
+ QVERIFY(!query.evaluateTo(&out));
+ }
+
+ /* Evaluate the empty sequence. */
+ {
+ QXmlQuery query;
+ QStringList out;
+
+ query.setQuery(QLatin1String("()"));
+ QVERIFY(!query.evaluateTo(&out));
+ QVERIFY(out.isEmpty());
+ }
+}
+
+void tst_QXmlQuery::evaluateToQStringList() const
+{
+ QFETCH(QString, queryString);
+ QFETCH(QStringList, expectedOutput);
+
+ QXmlQuery query;
+ query.setQuery(queryString);
+ QStringList out;
+ QVERIFY(query.isValid());
+
+ QVERIFY(query.evaluateTo(&out));
+
+ QCOMPARE(out, expectedOutput);
+}
+
+void tst_QXmlQuery::evaluateToQStringListTriggerWarnings() const
+{
+ QXmlQuery query;
+
+ QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed.");
+ QCOMPARE(query.evaluateTo(static_cast<QStringList *>(0)),
+ false);
+}
+
+void tst_QXmlQuery::evaluateToQStringList_data() const
+{
+ QTest::addColumn<QString>("queryString");
+ QTest::addColumn<QStringList>("expectedOutput");
+
+ QTest::newRow("One atomic")
+ << QString::fromLatin1("(1 + 1) cast as xs:string")
+ << QStringList(QString::fromLatin1("2"));
+
+ {
+ QStringList expected;
+ expected << QLatin1String("2");
+ expected << QLatin1String("a string");
+
+ QTest::newRow("Two atomics")
+ << QString::fromLatin1("(1 + 1) cast as xs:string, 'a string'")
+ << expected;
+ }
+
+ QTest::newRow("A query which evaluates to sub-types of xs:string.")
+ << QString::fromLatin1("xs:NCName('NCName'), xs:normalizedString(' a b c ')")
+ << (QStringList() << QString::fromLatin1("NCName")
+ << QString::fromLatin1(" a b c "));
+
+ QTest::newRow("A query which evaluates to two elements.")
+ << QString::fromLatin1("string(<e>theString1</e>), string(<e>theString2</e>)")
+ << (QStringList() << QString::fromLatin1("theString1")
+ << QString::fromLatin1("theString2"));
+}
+
+/*!
+ Ensure that we don't automatically convert non-xs:string values.
+ */
+void tst_QXmlQuery::evaluateToQStringListNoConversion() const
+{
+ QXmlQuery query;
+ query.setQuery(QString::fromLatin1("<e/>"));
+ QVERIFY(query.isValid());
+ QStringList result;
+ QVERIFY(!query.evaluateTo(&result));
+}
+
+void tst_QXmlQuery::evaluateToQIODevice() const
+{
+ /* an XQuery, check that no indentation is performed. */
+ {
+ QBuffer out;
+ QVERIFY(out.open(QIODevice::ReadWrite));
+
+ QXmlQuery query;
+ query.setQuery(QLatin1String("<a><b/></a>"));
+ QVERIFY(query.isValid());
+ QVERIFY(query.evaluateTo(&out));
+ QCOMPARE(out.data(), QByteArray("<a><b/></a>"));
+ }
+}
+
+void tst_QXmlQuery::evaluateToQIODeviceTriggerWarnings() const
+{
+ QXmlQuery query;
+
+ QTest::ignoreMessage(QtWarningMsg, "The pointer to the device cannot be null.");
+ QCOMPARE(query.evaluateTo(static_cast<QIODevice *>(0)),
+ false);
+
+ QBuffer buffer;
+
+ QTest::ignoreMessage(QtWarningMsg, "The device must be writable.");
+ QCOMPARE(query.evaluateTo(&buffer),
+ false);
+}
+
+void tst_QXmlQuery::evaluateToQIODeviceSignature() const
+{
+ /* The function should be const. */
+ {
+ QBuffer out;
+ QVERIFY(out.open(QIODevice::ReadWrite));
+
+ const QXmlQuery query;
+
+ query.evaluateTo(&out);
+ }
+}
+
+void tst_QXmlQuery::evaluateToQIODeviceOnInvalidQuery() const
+{
+ QBuffer out;
+ QVERIFY(out.open(QIODevice::WriteOnly));
+
+ /* On syntactically invalid query. */
+ {
+ QXmlQuery query;
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+ query.setQuery(QLatin1String("1 +"));
+ QVERIFY(!query.isValid());
+ QVERIFY(!query.evaluateTo(&out));
+ }
+
+ /* On null QXmlQuery instance. */
+ {
+ QXmlQuery query;
+ QVERIFY(!query.evaluateTo(&out));
+ }
+
+}
+
+void tst_QXmlQuery::setQueryQIODeviceQUrl() const
+{
+ /* Basic test. */
+ {
+ QBuffer buffer;
+ buffer.setData("1, 2, 2 + 1");
+ QVERIFY(buffer.open(QIODevice::ReadOnly));
+
+ QXmlQuery query;
+ query.setQuery(&buffer);
+ QVERIFY(query.isValid());
+
+ QXmlResultItems result;
+ query.evaluateTo(&result);
+ QCOMPARE(result.next().toAtomicValue(), QVariant(1));
+ QCOMPARE(result.next().toAtomicValue(), QVariant(2));
+ QCOMPARE(result.next().toAtomicValue(), QVariant(3));
+ QVERIFY(result.next().isNull());
+ QVERIFY(!result.hasError());
+ }
+
+ /* Set query that is invalid. */
+ {
+ QBuffer buffer;
+ buffer.setData("1, ");
+ QVERIFY(buffer.open(QIODevice::ReadOnly));
+
+ QXmlQuery query;
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+ query.setQuery(&buffer);
+ QVERIFY(!query.isValid());
+ }
+
+ /* Check that the base URI passes through. */
+ {
+ QBuffer buffer;
+ buffer.setData("string(static-base-uri())");
+ QVERIFY(buffer.open(QIODevice::ReadOnly));
+
+ QXmlQuery query;
+ query.setQuery(&buffer, QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl"));
+ QVERIFY(query.isValid());
+
+ QStringList result;
+ query.evaluateTo(&result);
+ QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
+ }
+}
+
+void tst_QXmlQuery::setQueryQIODeviceQUrlTriggerWarnings() const
+{
+ QXmlQuery query;
+ QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed.");
+ query.setQuery(0);
+
+ QBuffer buffer;
+ QTest::ignoreMessage(QtWarningMsg, "The device must be readable.");
+ query.setQuery(&buffer);
+}
+
+void tst_QXmlQuery::setQueryQString() const
+{
+ /* Basic test. */
+ {
+ QXmlQuery query;
+ query.setQuery(QLatin1String("1, 2, 2 + 1"));
+ QVERIFY(query.isValid());
+
+ QXmlResultItems result;
+ query.evaluateTo(&result);
+ QCOMPARE(result.next().toAtomicValue(), QVariant(1));
+ QCOMPARE(result.next().toAtomicValue(), QVariant(2));
+ QCOMPARE(result.next().toAtomicValue(), QVariant(3));
+ QVERIFY(result.next().isNull());
+ QVERIFY(!result.hasError());
+ }
+
+ /* Set query that is invalid. */
+ {
+ MessageSilencer silencer;
+ QXmlQuery query;
+ query.setMessageHandler(&silencer);
+ query.setQuery(QLatin1String("1, "));
+ QVERIFY(!query.isValid());
+ }
+
+ /* Check that the base URI passes through. */
+ {
+ QXmlQuery query;
+ query.setQuery(QLatin1String("string(static-base-uri())"), QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl"));
+ QVERIFY(query.isValid());
+
+ QStringList result;
+ query.evaluateTo(&result);
+ QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl")));
+ }
+}
+
+void tst_QXmlQuery::setQueryQUrlSuccess() const
+{
+#if defined(Q_OS_WINCE) && !defined(_X86_)
+ QStringList testsToSkip;
+ testsToSkip << "A valid query via the ftp scheme" << "A valid query via the http scheme";
+ if (testsToSkip.contains(QTest::currentDataTag()))
+ QSKIP("Network tests are currently unsupported on Windows CE.", SkipSingle);
+#endif
+
+ QFETCH(QUrl, queryURI);
+ QFETCH(QByteArray, expectedOutput);
+
+ QVERIFY(queryURI.isValid());
+
+ QXmlQuery query;
+
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+
+ query.setQuery(queryURI);
+ QVERIFY(query.isValid());
+
+ QByteArray out;
+ QBuffer buffer(&out);
+ QVERIFY(buffer.open(QIODevice::WriteOnly));
+ QXmlSerializer serializer(query, &buffer);
+
+ query.evaluateTo(&serializer);
+ QCOMPARE(out, expectedOutput);
+}
+
+void tst_QXmlQuery::setQueryQUrlSuccess_data() const
+{
+ QTest::addColumn<QUrl>("queryURI");
+ QTest::addColumn<QByteArray>("expectedOutput");
+
+ QTest::newRow("A valid query via the data scheme")
+ << QUrl::fromEncoded("data:application/xml,1%20%2B%201") /* "1 + 1" */
+ << QByteArray("2");
+
+ QTest::newRow("A valid query via the file scheme")
+ << QUrl::fromLocalFile(inputFile(QLatin1String(queriesDirectory) + QLatin1String("onePlusOne.xq")))
+ << QByteArray("2");
+
+ if(!m_testNetwork)
+ return;
+
+ QTest::newRow("A valid query via the ftp scheme")
+ << QUrl::fromEncoded(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/viaFtp.xq").toLatin1())
+ << QByteArray("This was received via FTP");
+
+ QTest::newRow("A valid query via the http scheme")
+ << QUrl::fromEncoded(QString("http://" + QtNetworkSettings::serverName() + "/qxmlquery/viaHttp.xq").toLatin1())
+ << QByteArray("This was received via HTTP.");
+}
+
+void tst_QXmlQuery::setQueryQUrlFailSucceed() const
+{
+ QXmlQuery query;
+ MessageSilencer silencer;
+
+ query.setMessageHandler(&silencer);
+
+ query.setQuery(QLatin1String("1 + 1"));
+ QVERIFY(query.isValid());
+
+ query.setQuery(QUrl::fromEncoded("file://example.com/does/not/exist"));
+ QVERIFY(!query.isValid());
+}
+
+void tst_QXmlQuery::setQueryQUrlFailure() const
+{
+ QFETCH(QUrl, queryURI);
+
+ MessageSilencer silencer;
+
+ QXmlQuery query;
+ query.setMessageHandler(&silencer);
+ query.setQuery(queryURI);
+ QVERIFY(!query.isValid());
+}
+
+void tst_QXmlQuery::setQueryQUrlFailure_data() const
+{
+ QTest::addColumn<QUrl>("queryURI");
+
+ QTest::newRow("Query via file:// that does not exist.")
+ << QUrl::fromEncoded("file://example.com/does/not/exist");
+
+ QTest::newRow("A query via file:// that is completely empty, but readable.")
+ << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("../xmlpatterns/queries/completelyEmptyQuery.xq"));
+
+ {
+ const QString name(QLatin1String("nonReadableFile.xq"));
+ QFile outFile(name);
+ QVERIFY(outFile.open(QIODevice::WriteOnly));
+ outFile.write(QByteArray("1"));
+ outFile.close();
+ /* On some windows versions, this fails, so we don't check that this works with QVERIFY. */
+ outFile.setPermissions(QFile::Permissions(QFile::Permissions()));
+
+ QTest::newRow("Query via file:/ that does not have read permissions.")
+ << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("nonReadableFile.xq"));
+ }
+
+ if(!m_testNetwork)
+ return;
+
+ QTest::newRow("Query via HTTP that does not exist.")
+ << QUrl::fromEncoded("http://example.com/NoQuery/ISWear");
+
+ /*
+ QTest::newRow("Query via FTP that does not exist.")
+ << QUrl::fromEncoded("ftp://example.com/NoQuery/ISWear");
+ */
+
+ QTest::newRow("A query via http:// that is completely empty, but readable.")
+ << QUrl::fromEncoded(QString(
+ "http://" + QtNetworkSettings::serverName() + "/qxmlquery/completelyEmptyQuery.xq").toLatin1());
+
+ QTest::newRow("A query via ftp:// that is completely empty, but readable.")
+ << QUrl::fromEncoded(QString(
+ "ftp://" + QtNetworkSettings::serverName() + "qxmlquery/completelyEmptyQuery.xq").toLatin1());
+
+}
+
+void tst_QXmlQuery::setQueryQUrlBaseURI() const
+{
+ QFETCH(QUrl, inputBaseURI);
+ QFETCH(QUrl, expectedBaseURI);
+
+ QXmlQuery query;
+
+ query.setQuery(QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq")), inputBaseURI);
+ QVERIFY(query.isValid());
+
+ QStringList result;
+ QVERIFY(query.evaluateTo(&result));
+ QCOMPARE(result.count(), 1);
+
+ if(qstrcmp(QTest::currentDataTag(), "Relative base URI") == 0)
+ checkBaseURI(QUrl(result.first()), QCoreApplication::applicationFilePath());
+ else
+ QCOMPARE(result.first(), expectedBaseURI.toString());
+}
+
+void tst_QXmlQuery::setQueryQUrlBaseURI_data() const
+{
+ QTest::addColumn<QUrl>("inputBaseURI");
+ QTest::addColumn<QUrl>("expectedBaseURI");
+
+ QTest::newRow("absolute HTTP")
+ << QUrl(QLatin1String("http://www.example.com/"))
+ << QUrl(QLatin1String("http://www.example.com/"));
+
+ QTest::newRow("None, so the query URI is used")
+ << QUrl()
+ << QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq"));
+
+ QTest::newRow("Relative base URI")
+ << QUrl(QLatin1String("../data/relative.uri"))
+ << QUrl();
+}
+
+/*!
+ 1. Create a valid query.
+ 2. Call setQuery(QUrl), with a query file that doesn't exist.
+ 3. Verify that the query has changed state into invalid.
+ */
+void tst_QXmlQuery::setQueryWithNonExistentQUrlOnValidQuery() const
+{
+ QXmlQuery query;
+
+ MessageSilencer messageSilencer;
+ query.setMessageHandler(&messageSilencer);
+
+ query.setQuery(QLatin1String("1 + 1"));
+ QVERIFY(query.isValid());
+
+ query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/DOESNOTEXIST.xq"));
+ QVERIFY(!query.isValid());
+}
+
+/*!
+ 1. Create a valid query.
+ 2. Call setQuery(QUrl), with a query file that is invalid.
+ 3. Verify that the query has changed state into invalid.
+ */
+void tst_QXmlQuery::setQueryWithInvalidQueryFromQUrlOnValidQuery() const
+{
+ QXmlQuery query;
+
+ MessageSilencer messageSilencer;
+ query.setMessageHandler(&messageSilencer);
+
+ query.setQuery(QLatin1String("1 + 1"));
+ QVERIFY(query.isValid());
+
+ query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/queries/syntaxError.xq"));
+ QVERIFY(!query.isValid());
+}
+
+/*!
+ This triggered two bugs:
+
+ - First, the DynamicContext wasn't assigned to QXmlResultItems, meaning it went out of
+ scope and therefore deallocated the document pool, and calls
+ to QXmlResultItems::next() would use dangling pointers.
+
+ - Conversion between QPatternist::Item and QXmlItem was incorrectly done, leading to nodes
+ being treated as atomic values, and subsequent crashes.
+
+ */
+void tst_QXmlQuery::retrieveNameFromQuery() const
+{
+ QFETCH(QString, queryString);
+ QFETCH(QString, expectedName);
+
+ QXmlQuery query;
+ query.setQuery(queryString);
+ QVERIFY(query.isValid());
+ QXmlResultItems result;
+ query.evaluateTo(&result);
+
+ QVERIFY(!result.hasError());
+
+ const QXmlItem item(result.next());
+ QVERIFY(!result.hasError());
+ QVERIFY(!item.isNull());
+ QVERIFY(item.isNode());
+
+ const QXmlNodeModelIndex node(item.toNodeModelIndex());
+ QVERIFY(!node.isNull());
+
+ QCOMPARE(node.model()->name(node).localName(query.namePool()), expectedName);
+}
+
+void tst_QXmlQuery::retrieveNameFromQuery_data() const
+{
+ QTest::addColumn<QString>("queryString");
+ QTest::addColumn<QString>("expectedName");
+
+ QTest::newRow("Document-node")
+ << QString::fromLatin1("document{<elementName/>}")
+ << QString();
+
+ QTest::newRow("Element")
+ << QString::fromLatin1("document{<elementName/>}/*")
+ << QString::fromLatin1("elementName");
+}
+
+/*!
+ Binding a null QString leads to no variable binding, but an
+ empty non-null QString is possible.
+ */
+void tst_QXmlQuery::bindEmptyNullString() const
+{
+ MessageSilencer messageHandler;
+ QXmlQuery query;
+ query.setMessageHandler(&messageHandler);
+ query.setQuery(QLatin1String("declare variable $v external; $v"));
+ /* Here, we effectively pass an invalid QVariant. */
+ query.bindVariable(QLatin1String("v"), QVariant(QString()));
+ QVERIFY(!query.isValid());
+
+ QStringList result;
+ QVERIFY(!query.evaluateTo(&result));
+}
+
+void tst_QXmlQuery::bindEmptyString() const
+{
+ QXmlQuery query;
+ query.bindVariable(QLatin1String("v"), QVariant(QString(QLatin1String(""))));
+ query.setQuery(QLatin1String("declare variable $v external; $v"));
+ QVERIFY(query.isValid());
+
+ QStringList result;
+ QVERIFY(query.evaluateTo(&result));
+ QStringList expected((QString()));
+ QCOMPARE(result, expected);
+}
+
+void tst_QXmlQuery::cleanupTestCase() const
+{
+ /* Remove a weird file we created. */
+ const QString name(QLatin1String("nonReadableFile.xq"));
+
+ if(QFile::exists(name))
+ {
+ QFile file(name);
+ QVERIFY(file.setPermissions(QFile::WriteOwner));
+ QVERIFY(file.remove());
+ }
+}
+
+void tst_QXmlQuery::declareUnavailableExternal() const
+{
+ QXmlQuery query;
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+ query.setQuery(QLatin1String("declare variable $var external;"
+ "1 + 1"));
+ /* We do not bind $var with QXmlQuery::bindVariable(). */
+ QVERIFY(!query.isValid());
+}
+
+/*!
+ This test triggers an assert in one of the cache iterator
+ with MSVC 2005 when compiled in debug mode.
+ */
+void tst_QXmlQuery::msvcCacheIssue() const
+{
+ QXmlQuery query;
+ query.bindVariable(QLatin1String("externalVariable"), QXmlItem("Variable Value"));
+ query.setQuery(QUrl::fromLocalFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariableUsedTwice.xq")));
+ QStringList result;
+ QVERIFY(query.evaluateTo(&result));
+
+ QCOMPARE(result,
+ QStringList() << QString::fromLatin1("Variable Value") << QString::fromLatin1("Variable Value"));
+}
+
+void tst_QXmlQuery::unavailableExternalVariable() const
+{
+ QXmlQuery query;
+
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+
+ query.setQuery(QLatin1String("declare variable $foo external; 1"));
+
+ QVERIFY(!query.isValid());
+}
+
+/*!
+ Ensure that setUriResolver() affects \c fn:doc() and \c fn:doc-available().
+ */
+void tst_QXmlQuery::useUriResolver() const
+{
+ class TestUriResolver : public QAbstractUriResolver
+ , private TestFundament
+ {
+ public:
+ virtual QUrl resolve(const QUrl &relative,
+ const QUrl &baseURI) const
+ {
+ Q_UNUSED(relative);
+ return baseURI.resolved(inputFile(QLatin1String(queriesDirectory) + QLatin1String("simpleDocument.xml")));
+ }
+ };
+
+ const TestUriResolver uriResolver;
+ QXmlQuery query;
+
+ query.setUriResolver(&uriResolver);
+ query.setQuery(QLatin1String("let $i := 'http://www.example.com/DoesNotExist'"
+ "return (string(doc($i)), doc-available($i))"));
+
+
+ QXmlResultItems result;
+ query.evaluateTo(&result);
+
+ QVERIFY(!result.hasError());
+ QCOMPARE(result.next().toAtomicValue().toString(), QString::fromLatin1("text text node"));
+ QCOMPARE(result.next().toAtomicValue().toBool(), true);
+ QVERIFY(result.next().isNull());
+ QVERIFY(!result.hasError());
+}
+
+void tst_QXmlQuery::queryWithFocusAndVariable() const
+{
+ QXmlQuery query;
+ query.setFocus(QXmlItem(5));
+ query.bindVariable(QLatin1String("var"), QXmlItem(2));
+
+ query.setQuery(QLatin1String("string(. * $var)"));
+
+ QStringList result;
+
+ QVERIFY(query.evaluateTo(&result));
+
+ QCOMPARE(result, QStringList(QLatin1String("10")));
+}
+
+void tst_QXmlQuery::undefinedFocus() const
+{
+ QXmlQuery query;
+
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+
+ query.setQuery(QLatin1String("."));
+ QVERIFY(!query.isValid());
+}
+
+void tst_QXmlQuery::basicFocusUsage() const
+{
+ QXmlQuery query;
+
+ MessageSilencer silencer;
+ query.setMessageHandler(&silencer);
+
+ query.setFocus(QXmlItem(5));
+ query.setQuery(QLatin1String("string(. * .)"));
+ QVERIFY(query.isValid());
+
+ QStringList result;
+ QVERIFY(query.evaluateTo(&result));
+
+ QCOMPARE(result, QStringList(QLatin1String("25")));
+}
+
+/*!
+ Triggers an ownership related crash.
+ */
+void tst_QXmlQuery::copyCheckMessageHandler() const
+{
+ QXmlQuery query;
+ QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0));
+
+ query.setQuery(QLatin1String("doc('qrc:/QXmlQueryTestData/data/oneElement.xml')"));
+ /* By now, we should have set the builtin message handler. */
+ const QAbstractMessageHandler *const messageHandler = query.messageHandler();
+ QVERIFY(messageHandler);
+
+ {
+ /* This copies QXmlQueryPrivate::m_ownerObject, and its destructor
+ * will delete it, and hence the builtin message handler attached to it. */
+ QXmlQuery copy(query);
+ }
+
+ QXmlResultItems result;
+ query.evaluateTo(&result);
+
+ while(!result.next().isNull())
+ {
+ }
+ QVERIFY(!result.hasError());
+}
+
+void tst_QXmlQuery::queryLanguage() const
+{
+ /* Check default value. */
+ {
+ const QXmlQuery query;
+ QCOMPARE(query.queryLanguage(), QXmlQuery::XQuery10);
+ }
+
+ /* Check default value of copies default instance. */
+ {
+ const QXmlQuery query1;
+ const QXmlQuery query2(query1);
+
+ QCOMPARE(query1.queryLanguage(), QXmlQuery::XQuery10);
+ QCOMPARE(query2.queryLanguage(), QXmlQuery::XQuery10);
+ }
+}
+
+void tst_QXmlQuery::queryLanguageSignature() const
+{
+ /* This getter should be const. */
+ QXmlQuery query;
+ query.queryLanguage();
+}
+
+void tst_QXmlQuery::enumQueryLanguage() const
+{
+ /* These enum values should be possible to OR for future plans. */
+ QCOMPARE(int(QXmlQuery::XQuery10), 1);
+ QCOMPARE(int(QXmlQuery::XSLT20), 2);
+}
+
+void tst_QXmlQuery::setInitialTemplateNameQXmlName() const
+{
+ QXmlQuery query(QXmlQuery::XSLT20);
+ QXmlNamePool np(query.namePool());
+ const QXmlName name(np, QLatin1String("main"));
+
+ query.setInitialTemplateName(name);
+
+ QCOMPARE(query.initialTemplateName(), name);
+
+ query.setQuery(QUrl(inputFile(QLatin1String("../xmlpatterns/stylesheets/namedTemplate.xsl"))));
+ QVERIFY(query.isValid());
+
+ QBuffer result;
+ QVERIFY(result.open(QIODevice::ReadWrite));
+ QXmlSerializer serializer(query, &result);
+ query.evaluateTo(&serializer);
+
+ QCOMPARE(result.data(), QByteArray("1 2 3 4 5"));
+
+ // TODO invoke a template which has required params.
+}
+
+void tst_QXmlQuery::setInitialTemplateNameQXmlNameSignature() const
+{
+ QXmlQuery query;
+ QXmlNamePool np(query.namePool());
+ const QXmlName name(np, QLatin1String("foo"));
+
+ /* The signature should take a const reference. */
+ query.setInitialTemplateName(name);
+}
+
+void tst_QXmlQuery::setInitialTemplateNameQString() const
+{
+ QXmlQuery query;
+ QXmlNamePool np(query.namePool());
+ query.setInitialTemplateName(QLatin1String("foo"));
+
+ QCOMPARE(query.initialTemplateName(), QXmlName(np, QLatin1String("foo")));
+}
+
+void tst_QXmlQuery::setInitialTemplateNameQStringSignature() const
+{
+ const QString name(QLatin1String("name"));
+ QXmlQuery query;
+
+ /* We should take a const reference. */
+ query.setInitialTemplateName(name);
+}
+
+void tst_QXmlQuery::initialTemplateName() const
+{
+ /* Check our default value. */
+ QXmlQuery query;
+ QCOMPARE(query.initialTemplateName(), QXmlName());
+ QVERIFY(query.initialTemplateName().isNull());
+}
+
+void tst_QXmlQuery::initialTemplateNameSignature() const
+{
+ const QXmlQuery query;
+ /* This should be a const member. */
+ query.initialTemplateName();
+}
+
+void tst_QXmlQuery::setNetworkAccessManager() const
+{
+
+ /* Ensure fn:doc() picks up the right QNetworkAccessManager. */
+ {
+ NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
+ QUrl(inputFile(QLatin1String("../xmlpatterns/queries/simpleDocument.xml"))));
+
+ QXmlQuery query;
+ query.setNetworkAccessManager(&networkOverrider);
+ query.setQuery(QLatin1String("string(doc('tag:example.com:DOESNOTEXIST'))"));
+ QVERIFY(query.isValid());
+
+ QStringList result;
+ QVERIFY(query.evaluateTo(&result));
+
+ QCOMPARE(result, QStringList(QLatin1String("text text node")));
+ }
+
+ /* Ensure setQuery() is using the right network manager. */
+ {
+ NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")),
+ QUrl(inputFile(QLatin1String("../xmlpatterns/queries/concat.xq"))));
+
+ QXmlQuery query;
+ query.setNetworkAccessManager(&networkOverrider);
+ query.setQuery(QUrl("tag:example.com:DOESNOTEXIST"));
+ QVERIFY(query.isValid());
+
+ QStringList result;
+ QVERIFY(query.evaluateTo(&result));
+
+ QCOMPARE(result, QStringList(QLatin1String("abcdef")));
+ }
+}
+void tst_QXmlQuery::networkAccessManagerSignature() const
+{
+ /* Const object. */
+ const QXmlQuery query;
+
+ /* The function should be const. */
+ query.networkAccessManager();
+}
+
+void tst_QXmlQuery::networkAccessManagerDefaultValue() const
+{
+ const QXmlQuery query;
+
+ QCOMPARE(query.networkAccessManager(), static_cast<QNetworkAccessManager *>(0));
+}
+
+void tst_QXmlQuery::networkAccessManager() const
+{
+ /* Test that we return the network manager that was set. */
+ {
+ QNetworkAccessManager manager;
+ QXmlQuery query;
+ query.setNetworkAccessManager(&manager);
+ QCOMPARE(query.networkAccessManager(), &manager);
+ }
+}
+
+/*!
+ \internal
+ \since 4.5
+
+ 1. Load a document into QXmlQuery's document cache, by executing a query which does it.
+ 2. Set a focus
+ 3. Change query, to one which uses the focus
+ 4. Evaluate
+
+ Used to crash.
+ */
+void tst_QXmlQuery::multipleDocsAndFocus() const
+{
+ QXmlQuery query;
+
+ /* We use string concatenation, since variable bindings might disturb what
+ * we're testing. */
+ query.setQuery(QLatin1String("string(doc('") +
+ inputFile(QLatin1String("../xmlpatterns/queries/simpleDocument.xml")) +
+ QLatin1String("'))"));
+ query.setFocus(QUrl(inputFile(QLatin1String("../xmlpatterns/stylesheets/documentElement.xml"))));
+ query.setQuery(QLatin1String("string(.)"));
+
+ QStringList result;
+ QVERIFY(query.evaluateTo(&result));
+}
+
+/*!
+ \internal
+ \since 4.5
+
+ 1. Set a focus
+ 2. Set a query
+ 3. Evaluate
+ 4. Change focus
+ 5. Evaluate
+
+ Used to crash.
+ */
+void tst_QXmlQuery::multipleEvaluationsWithDifferentFocus() const
+{
+ QXmlQuery query;
+ QStringList result;
+
+ query.setFocus(QUrl(inputFile(QLatin1String("../xmlpatterns/stylesheets/documentElement.xml"))));
+ query.setQuery(QLatin1String("string(.)"));
+ QVERIFY(query.evaluateTo(&result));
+
+ query.setFocus(QUrl(inputFile(QLatin1String("../xmlpatterns/stylesheets/documentElement.xml"))));
+ QVERIFY(query.evaluateTo(&result));
+}
+
+void tst_QXmlQuery::bindVariableQXmlQuery() const
+{
+ QFETCH(QString, query1);
+ QFETCH(QString, query2);
+ QFETCH(QString, expectedOutput);
+ QFETCH(bool, expectedSuccess);
+
+ MessageSilencer silencer;
+ QXmlQuery xmlQuery1;
+ xmlQuery1.setMessageHandler(&silencer);
+ xmlQuery1.setQuery(query1);
+
+ QXmlQuery xmlQuery2(xmlQuery1);
+ xmlQuery2.bindVariable("query1", xmlQuery1);
+ xmlQuery2.setQuery(query2);
+
+ QString output;
+ const bool querySuccess = xmlQuery2.evaluateTo(&output);
+
+ QCOMPARE(querySuccess, expectedSuccess);
+
+ if(querySuccess)
+ QCOMPARE(output, expectedOutput);
+}
+
+void tst_QXmlQuery::bindVariableQXmlQuery_data() const
+{
+ QTest::addColumn<QString>("query1");
+ QTest::addColumn<QString>("query2");
+ QTest::addColumn<QString>("expectedOutput");
+ QTest::addColumn<bool>("expectedSuccess");
+
+ QTest::newRow("First query has one atomic value.")
+ << "2"
+ << "1, $query1, 3"
+ << "1 2 3\n"
+ << true;
+
+ QTest::newRow("First query has two atomic values.")
+ << "2, 3"
+ << "1, $query1, 4"
+ << "1 2 3 4\n"
+ << true;
+
+ QTest::newRow("First query is a node.")
+ << "<e/>"
+ << "1, $query1, 3"
+ << "1<e/>3\n"
+ << true;
+
+ /* This is a good test, because it triggers the exception in the
+ * bindVariable() call, as supposed to when the actual evaluation is done.
+ */
+ QTest::newRow("First query has a dynamic error.")
+ << "error()"
+ << "1, $query1"
+ << QString() /* We don't care. */
+ << false;
+}
+
+void tst_QXmlQuery::bindVariableQStringQXmlQuerySignature() const
+{
+ QXmlQuery query1;
+ query1.setQuery("'dummy'");
+
+ QXmlQuery query2;
+ const QString name(QLatin1String("name"));
+
+ /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
+ * QXmlQuery, and evaluation is what we do here. */
+ query2.bindVariable(name, const_cast<const QXmlQuery &>(query1));
+}
+
+void tst_QXmlQuery::bindVariableQXmlNameQXmlQuerySignature() const
+{
+ QXmlNamePool np;
+ QXmlQuery query1(np);
+ query1.setQuery("'dummy'");
+
+ QXmlQuery query2;
+ const QXmlName name(np, QLatin1String("name"));
+
+ /* We should be able to take a const QXmlQuery reference. Evaluation never mutate
+ * QXmlQuery, and evaluation is what we do here. */
+ query2.bindVariable(name, const_cast<const QXmlQuery &>(query1));
+}
+
+/*!
+ Check that the QXmlName is handled correctly.
+ */
+void tst_QXmlQuery::bindVariableQXmlNameQXmlQuery() const
+{
+ QXmlNamePool np;
+ QXmlQuery query1;
+ query1.setQuery(QLatin1String("1"));
+
+ QXmlQuery query2(np);
+ query2.bindVariable(QXmlName(np, QLatin1String("theName")), query1);
+ query2.setQuery("$theName");
+
+ QString result;
+ query2.evaluateTo(&result);
+
+ QCOMPARE(result, QString::fromLatin1("1\n"));
+}
+
+void tst_QXmlQuery::bindVariableQXmlQueryInvalidate() const
+{
+ QXmlQuery query;
+ query.bindVariable(QLatin1String("name"), QVariant(1));
+ query.setQuery("$name");
+ QVERIFY(query.isValid());
+
+ QXmlQuery query2;
+ query2.setQuery("'query2'");
+
+ query.bindVariable(QLatin1String("name"), query);
+ QVERIFY(!query.isValid());
+}
+
+QTEST_MAIN(tst_QXmlQuery)
+
+#include "tst_qxmlquery.moc"
+#else //QTEST_XMLPATTERNS
+QTEST_NOOP_MAIN
+#endif