tests/auto/xmlpatterns/tst_xmlpatterns.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/auto/xmlpatterns/tst_xmlpatterns.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1013 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QFile>
+#include <QtTest/QtTest>
+
+#ifdef QTEST_XMLPATTERNS
+
+#include "../qxmlquery/TestFundament.h"
+#include "../network-settings.h"
+
+/*!
+ \class tst_XmlPatterns
+ \internal
+ \since 4.4
+ \brief Tests the command line interface, \c xmlpatterns, for the XQuery code.
+
+ This test is not intended for testing the engine, but all the gluing the
+ command line interface do: error reporting, query output, variable bindings, exit
+ codes, and so on.
+
+ 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_XmlPatterns : public QObject
+                      , private TestFundament
+{
+    Q_OBJECT
+
+public:
+    tst_XmlPatterns();
+
+private Q_SLOTS:
+    void initTestCase();
+    void xquerySupport();
+    void xquerySupport_data() const;
+    void xsltSupport();
+    void xsltSupport_data() const;
+    void stdoutFailure() const;
+    void cleanupTestCase() const;
+
+private:
+    static void createNonWritable(const QString &name);
+    static void removeNonWritable(QFile &outFile);
+
+    int             m_generatedTests;
+    /**
+     * Get rid of characters that complicates on various file systems.
+     */
+    const QRegExp   m_normalizeTestName;
+    /**
+     * @note Perforce disallows wildcards in the name.
+     */
+    const QRegExp   m_filenameInStderr;
+    const QString   m_command;
+    bool            m_dontRun;
+};
+
+tst_XmlPatterns::tst_XmlPatterns() : m_generatedTests(0)
+                                   , m_normalizeTestName(QLatin1String("[\\*\\?#\\-\\/:; ()',&]"))
+                                   , m_filenameInStderr(QLatin1String("file:\\/\\/.*(\\.xq|\\.gccxml|\\.xml|\\.xsl|-)(,|:)"))
+                                   , m_command(QLatin1String("xmlpatterns"))
+                                   , m_dontRun(false)
+{
+    Q_SET_DEFAULT_IAP
+
+    Q_ASSERT(m_normalizeTestName.isValid());
+    Q_ASSERT(m_filenameInStderr.isValid());
+}
+
+void tst_XmlPatterns::initTestCase()
+{
+    QProcess process;
+    process.start(m_command);
+
+    if(!process.waitForFinished())
+    {
+        m_dontRun = true;
+        QEXPECT_FAIL("", "The command line tool is not in the path, most likely because Qt "
+                         "has been partically built, such as only the sub-src rule. No tests will be run.", Abort);
+        QVERIFY(false);
+    }
+
+}
+
+void tst_XmlPatterns::xquerySupport()
+{
+    if(m_dontRun)
+        QSKIP("The command line utility is not in the path.", SkipAll);
+
+#ifdef Q_OS_WINCE
+    QSKIP("WinCE: This test uses unsupported WinCE functionality", SkipAll);
+#endif
+
+    QFETCH(int,         expectedExitCode);
+    QFETCH(QByteArray,  expectedQueryOutput);
+    QFETCH(QStringList, arguments);
+    QFETCH(QString,     cwd);
+    QFETCH(QString,     outputFile);
+
+    QProcess process;
+
+    if(!cwd.isEmpty())
+        process.setWorkingDirectory(inputFile(cwd));
+
+    process.start(m_command, arguments);
+
+    QCOMPARE(process.exitStatus(), QProcess::NormalExit);
+    QVERIFY(process.waitForFinished());
+
+    if(process.exitCode() != expectedExitCode)
+        QTextStream(stderr) << "stderr:" << process.readAllStandardError();
+
+    QCOMPARE(process.exitCode(), expectedExitCode);
+
+    const QByteArray rawProducedStderr((process.readAllStandardError()));
+    const QString fixedStderr(QString::fromLocal8Bit(rawProducedStderr).remove(m_filenameInStderr));
+
+    const QString errorFileName(inputFile(QLatin1String(SRCDIR "stderrBaselines/") +
+                                          QString::fromUtf8(QTest::currentDataTag()).remove(m_normalizeTestName) +
+                                          QLatin1String(".txt")));
+
+    QFile writeErr(errorFileName);
+
+    if(writeErr.exists())
+    {
+        QVERIFY(writeErr.open(QIODevice::ReadOnly));
+        QString rawExpectedStdErr(QString::fromLocal8Bit(writeErr.readAll()));
+
+        /* On Windows, at least MinGW, this differs. */
+        if(qstrcmp(QTest::currentDataTag(), "-output with a non-writable file") == 0)
+        {
+            QVERIFY(fixedStderr == rawExpectedStdErr.remove(m_filenameInStderr) ||
+                    fixedStderr.trimmed() == "Failed to open file notWritable.out for writing: Access is denied.");
+        }
+        else if(qstrcmp(QTest::currentDataTag(), "Invoke -version") == 0)
+        {
+            /* There's a wide range of different version strings used. For
+             * instance, "4.4.0-rc1". */
+            const QRegExp removeVersion(QLatin1String(" Qt \\d\\.\\d.*"));
+            QVERIFY(removeVersion.isValid());
+            QCOMPARE(QString(fixedStderr).remove(removeVersion) + QChar('|'), rawExpectedStdErr + QChar('|'));
+        }
+        else
+            QCOMPARE(fixedStderr, rawExpectedStdErr.remove(m_filenameInStderr));
+    }
+    else
+    {
+        QFile writeErr(errorFileName);
+        QVERIFY(writeErr.open(QIODevice::WriteOnly));
+
+        QCOMPARE(writeErr.write(rawProducedStderr), qint64(rawProducedStderr.count()));
+        QTextStream(stderr) << "creating file " << errorFileName;
+        ++m_generatedTests;
+    }
+
+    const QByteArray actual(process.readAllStandardOutput());
+
+    if(outputFile.isEmpty())
+    {
+        QCOMPARE(actual, expectedQueryOutput);
+        return; /* We're done, this test was not creating any output file. */
+    }
+    else
+    {
+        QVERIFY(actual.isEmpty());
+
+        QFile outFile(outputFile);
+
+        QVERIFY(outFile.exists());
+        QVERIFY(outFile.open(QIODevice::ReadOnly));
+
+        QCOMPARE(outFile.readAll(), expectedQueryOutput);
+
+        removeNonWritable(outFile);
+    }
+}
+
+void tst_XmlPatterns::xquerySupport_data() const
+{
+#ifdef Q_OS_WINCE
+    return;
+#endif
+
+    /* Check one file for existence, to avoid possible false positives. */
+    QVERIFY(QFile::exists(inputFile(QLatin1String(SRCDIR "queries/onePlusOne.xq"))));
+
+    QTest::addColumn<int>("expectedExitCode");
+    QTest::addColumn<QByteArray>("expectedQueryOutput");
+    QTest::addColumn<QStringList>("arguments");
+    QTest::addColumn<QString>("cwd");
+    QTest::addColumn<QString>("outputFile");
+
+    QTest::newRow("A simple math query")
+        << 0
+        << QByteArray("2\n")
+        << QStringList((QLatin1String(SRCDIR "queries/onePlusOne.xq")))
+        << QString()
+        << QString();
+
+    QTest::newRow("An unbound external variable")
+        << 2
+        << QByteArray()
+        << QStringList(QLatin1String(SRCDIR "queries/externalVariable.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Bind an external variable")
+        << 0
+        << QByteArray("1 4<e>1</e>true\n")
+        << (QStringList() << QLatin1String(SRCDIR "queries/externalVariable.xq")
+                          << QLatin1String("-param")
+                          << QLatin1String("externalVariable=1"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Bind an external variable, query appearing last")
+        << 0
+        << QByteArray("1 4<e>1</e>true\n")
+        << (QStringList() << QLatin1String("-param")
+                          << QLatin1String("externalVariable=1")
+                          << QLatin1String(SRCDIR "queries/externalVariable.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Use fn:doc")
+        << 0
+        << QByteArray("<e xmlns=\"http://example.com\" xmlns:p=\"http://example.com/P\" attr=\"1\" p:attr=\"\">\n    <?target data?>\n    <!-- a comment -->\n    <e/>text <f/>text node</e>\n")
+        << QStringList(QLatin1String(SRCDIR "queries/openDocument.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Use fn:doc, together with -no-format, last")
+        << 0
+        << QByteArray("<e xmlns=\"http://example.com\" xmlns:p=\"http://example.com/P\" attr=\"1\" p:attr=\"\"><?target data?><!-- a comment --><e/>text <f/>text node</e>")
+        << (QStringList() << QLatin1String(SRCDIR "queries/openDocument.xq")
+                          << QLatin1String("-no-format"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Use fn:doc, together with -no-format, first")
+        << 0
+        << QByteArray("<e xmlns=\"http://example.com\" xmlns:p=\"http://example.com/P\" attr=\"1\" p:attr=\"\"><?target data?><!-- a comment --><e/>text <f/>text node</e>")
+        << (QStringList() << QLatin1String("-no-format")
+                          << QLatin1String(SRCDIR "queries/openDocument.xq"))
+        << QString()
+        << QString();
+
+    /* This is true for the command line utility, but not QXmlQuery::setQuery(). */
+    QTest::newRow("Make sure query paths are resolved against CWD, not the location of the executable.")
+        << 0
+        << QByteArray("2\n")
+        << QStringList(QLatin1String(SRCDIR "queries/onePlusOne.xq"))
+        << QString::fromLatin1("queries")
+        << QString();
+
+    QTest::newRow("Call fn:error()")
+        << 2
+        << QByteArray()
+        << QStringList(QLatin1String(SRCDIR "queries/errorFunction.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Evaluate a library module")
+        << 2
+        << QByteArray()
+        << QStringList(QLatin1String(SRCDIR "queries/simpleLibraryModule.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Trigger a static error.")
+        << 2
+        << QByteArray()
+        << QStringList(QLatin1String(SRCDIR "queries/staticError.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Pass -help")
+        << 0
+        << QByteArray()
+        << QStringList(QLatin1String("-help"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Open an nonexistent file")
+        << 2
+        << QByteArray()
+        << QStringList(QLatin1String(SRCDIR "queries/ThisFileDoesNotExist.xq"))
+        << QString()
+        << QString();
+
+    /* The following five tests exists to test the various
+     * markup classes in the message. */
+    QTest::newRow("XQuery-function message markups")
+        << 2
+        << QByteArray()
+        << QStringList(QLatin1String(SRCDIR "queries/wrongArity.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("XQuery-type message markups")
+        << 2
+        << QByteArray()
+        << QStringList(QLatin1String(SRCDIR "queries/typeError.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("XQuery-data & XQuery-keyword message markups")
+        << 2
+        << QByteArray()
+        << QStringList(QLatin1String(SRCDIR "queries/zeroDivision.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("XQuery-uri message markups")
+        << 2
+        << QByteArray()
+        << QStringList(QLatin1String(SRCDIR "queries/unsupportedCollation.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("XQuery-expression message markups")
+        << 2
+        << QByteArray()
+        << QStringList(QLatin1String(SRCDIR "queries/invalidRegexp.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Print a list of available regexp flags(The available flags are formatted in a complex way.)")
+        << 2
+        << QByteArray()
+        << QStringList(QLatin1String(SRCDIR "queries/invalidRegexpFlag.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Trigger an assert in QPatternist::ColorOutput. The query naturally contains an error; XPTY0004.")
+        << 2
+        << QByteArray()
+        << QStringList(QLatin1String(SRCDIR "queries/flwor.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Trigger a second assert in QPatternist::ColorOutput. The query naturally contains XPST0003.")
+        << 2
+        << QByteArray()
+        << QStringList(QLatin1String(SRCDIR "queries/syntaxError.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("-param is missing so multiple queries appear")
+        << 2
+        << QByteArray()
+        << (QStringList() << QLatin1String(SRCDIR "queries/reportGlobals.xq")
+                          << QLatin1String("fileToOpen=globals.gccxml"))
+        << QString()
+        << QString();
+
+    QTest::newRow("only -no-format")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String("-no-format"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Basic use of -output, query first")
+        << 0
+        << QByteArray("2\n")
+        << (QStringList() << QLatin1String(SRCDIR "queries/onePlusOne.xq")
+                          << QLatin1String("-output")
+                          << QLatin1String("basicOutput.out"))
+        << QString()
+        << QString::fromLatin1("basicOutput.out");
+
+    QTest::newRow("Basic use of -output, query last")
+        << 0
+        << QByteArray("<e/>\n")
+        << (QStringList() << QLatin1String("-output")
+                          << QLatin1String("basicOutput2.out")
+                          << QLatin1String(SRCDIR "queries/oneElement.xq"))
+        << QString()
+        << QString::fromLatin1("basicOutput2.out");
+
+    QTest::newRow("A single query, that does not exist")
+        << 2
+        << QByteArray()
+        << (QStringList() << QLatin1String(SRCDIR "doesNotExist.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Specify two identical query names")
+        << 2
+        << QByteArray()
+        << (QStringList() << QLatin1String(SRCDIR "query.xq")
+                          << QLatin1String(SRCDIR "query.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Specify no arguments at all.")
+        << 1
+        << QByteArray()
+        << QStringList()
+        << QString()
+        << QString();
+
+    QTest::newRow("Use -output twice")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String("-output")
+                          << QLatin1String("output1")
+                          << QLatin1String("-output")
+                          << QLatin1String("output2"))
+        << QString()
+        << QString();
+
+    {
+        const QString filename(QString::fromLatin1("notWritable.out"));
+        createNonWritable(filename);
+
+        QTest::newRow("-output with a non-writable file")
+            << 1
+            << QByteArray()
+            << (QStringList() << QLatin1String("-output")
+                              << filename
+                              << QLatin1String(SRCDIR "queries/onePlusOne.xq"))
+            << QString()
+            << filename;
+    }
+
+    {
+        const QString outName(QString::fromLatin1("existingContent.out"));
+        QFile outFile(outName);
+        QVERIFY(outFile.open(QIODevice::WriteOnly));
+        QCOMPARE(outFile.write("Existing content\n"), qint64(17));
+        outFile.close();
+
+        QTest::newRow("Use -output on a file with existing content, to ensure we truncate, not append the content we produce.")
+            << 0
+            << QByteArray("2\n")
+            << (QStringList() << QLatin1String("-output")
+                              << outName
+                              << QLatin1String(SRCDIR "queries/onePlusOne.xq"))
+            << QString()
+            << outName;
+    }
+
+    QTest::newRow("one query, and a terminating dash at the end")
+        << 0
+        << QByteArray("2\n")
+        << (QStringList() << QLatin1String(SRCDIR "queries/onePlusOne.xq")
+                          << QLatin1String("-"))
+        << QString()
+        << QString();
+
+    QTest::newRow("one query, with a preceding dash")
+        << 0
+        << QByteArray("2\n")
+        << (QStringList() << QLatin1String("-")
+                          << QLatin1String(SRCDIR "queries/onePlusOne.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("A single dash, that's invalid")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String("-"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Invoke -version")
+        << 0
+        << QByteArray()
+        << (QStringList() << QLatin1String("-version"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Unknown switch; -unknown-switch")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String("-unknown-switch"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Unknown switch; -d")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String("-d"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Passing a single dash is insufficient")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String("-"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Passing two dashes, the last is interpreted as a file name")
+        << 2
+        << QByteArray()
+        << (QStringList() << QLatin1String("-")
+                          << QLatin1String("-"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Pass three dashes, the two last gets interpreted as two query arguments")
+        << 2
+        << QByteArray()
+        << (QStringList() << QLatin1String("-")
+                          << QLatin1String("-")
+                          << QLatin1String("-"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Load query via data: scheme")
+        << 0
+        << QByteArray("<e/>\n")
+        << (QStringList() << QLatin1String("-is-uri") << QLatin1String("data:application/xml,%3Ce%2F%3E"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Load query via FTP")
+        << 0
+        << QByteArray("This was received via FTP\n")
+        << (QStringList() << QLatin1String("-is-uri") << QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/viaFtp.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Load query via HTTP")
+        << 0
+        << QByteArray("This was received via HTTP.\n")
+        << (QStringList() << QLatin1String("-is-uri") << QString("http://" + QtNetworkSettings::serverName() + "/qxmlquery/viaHttp.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("We don't support -format any longer")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String("-format"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Run a query which evaluates to the empty sequence.")
+        << 0
+        << QByteArray("\n")
+        << (QStringList() << QLatin1String(SRCDIR "queries/emptySequence.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Run a query which evaluates to a single document node with no children.")
+        << 0
+        << QByteArray("\n")
+        << (QStringList() << QLatin1String(SRCDIR "queries/onlyDocumentNode.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Invoke with invalid -param value.")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String(SRCDIR "queries/externalVariable.xq")
+                          << QLatin1String("-param")
+                          << QLatin1String("EqualSignIsMissing"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Invoke with colon in variable name.")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String(SRCDIR "queries/externalVariable.xq")
+                          << QLatin1String("-param")
+                          << QLatin1String("xs:name=value"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Invoke with missing name in -param arg.")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String(SRCDIR "queries/externalVariable.xq")
+                          << QLatin1String("-param")
+                          << QLatin1String("=value"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Invoke with -param that has two adjacent equal signs.")
+        << 0
+        << QByteArray("START =text END\n")
+        << (QStringList() << QLatin1String(SRCDIR "queries/externalStringVariable.xq")
+                          << QLatin1String("-param")
+                          << QLatin1String("externalString==text"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Pass in an external variable, but the query doesn't use it.")
+        << 0
+        << QByteArray("2\n")
+        << (QStringList() << QLatin1String(SRCDIR "queries/onePlusOne.xq")
+                          << QLatin1String("-param")
+                          << QLatin1String("externalString==text"))
+        << QString()
+        << QString();
+
+    /* This is how an empty string would have been passed in. */
+    QTest::newRow("Invoke with -param that has no value.")
+        << 0
+        << QByteArray("START  END\n")
+        << (QStringList() << QLatin1String(SRCDIR "queries/externalStringVariable.xq")
+                          << QLatin1String("-param")
+                          << QLatin1String("externalString="))
+        << QString()
+        << QString();
+
+    QTest::newRow("Ensure -is-uri can appear after the query filename")
+        << 0
+        << QByteArray("<e/>\n")
+        << (QStringList() << QLatin1String("data:application/xml,%3Ce%2F%3E") << QLatin1String("-is-uri"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Use a native path")
+        << 0
+        << QByteArray("2\n")
+        << (QStringList() << QDir::toNativeSeparators(QLatin1String(SRCDIR "queries/onePlusOne.xq")))
+        << QString()
+        << QString();
+
+    QTest::newRow("Pass in invalid URI")
+        << 2
+        << QByteArray()
+        << (QStringList() << QLatin1String("-is-uri") << QLatin1String("data:application/xml;base64,PGUvg==="))
+        << QString()
+        << QString();
+
+    /* Not relevant anymore.
+    QTest::newRow("A valid, existing query, followed by a bogus one")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String(SRCDIR "queries/onePlusOne.xq")
+                          << QLatin1String(SRCDIR "doesNotExist.xq"))
+        << QString()
+        << QString();
+        */
+
+    /* Not relevant anymore.
+    QTest::newRow("Specify two different query names")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String(SRCDIR "query1.xq")
+                          << QLatin1String(SRCDIR "query2.xq"))
+        << QString()
+        << QString();
+        */
+
+    // TODO use focus with xquery
+    // TODO fail to load focus with xquery
+    // TODO focus with URI with xquery
+    // TODO focus via FTP or so with xquery
+
+
+    QTest::newRow("Use -param twice")
+        << 0
+        << QByteArray("param1 param2\n")
+        << (QStringList() << QLatin1String(SRCDIR "queries/twoVariables.xq")
+                          << QLatin1String("-param")
+                          << QLatin1String("var1=param1")
+                          << QLatin1String("-param")
+                          << QLatin1String("var2=param2"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Use -param thrice")
+        << 0
+        << QByteArray("param1 param2 third\n")
+        << (QStringList() << QLatin1String(SRCDIR "queries/threeVariables.xq")
+                          << QLatin1String("-param")
+                          << QLatin1String("var1=param1")
+                          << QLatin1String("-param")
+                          << QLatin1String("var2=param2")
+                          << QLatin1String("-param")
+                          << QLatin1String("var3=third"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Specify the same parameter twice, different values")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String(SRCDIR "queries/onePlusOne.xq")
+                          << QLatin1String("-param")
+                          << QLatin1String("duplicated=param1")
+                          << QLatin1String("-param")
+                          << QLatin1String("duplicated=param2"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Specify the same parameter twice, same values")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String(SRCDIR "queries/onePlusOne.xq")
+                          << QLatin1String("-param")
+                          << QLatin1String("duplicated=param1")
+                          << QLatin1String("-param")
+                          << QLatin1String("duplicated=param2"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Open a non-existing collection.")
+        << 2
+        << QByteArray()
+        << (QStringList() << QLatin1String(SRCDIR "queries/nonexistingCollection.xq"))
+        << QString()
+        << QString();
+
+    // TODO https?
+    // TODO pass external variables that allows space around the equal sign.
+    // TODO run fn:trace()
+    // TODO Trigger warning
+    // TODO what can we do with queries/nodeSequence.xq?
+    // TODO trigger serialization error
+    // TODO "xmlpatterns e.xq x" gives "binding must equal .."
+    //
+    // TODO use stdout where it's connected to a non-writable file.
+    // TODO specify -format twice, or whatever it's called.
+    // TODO query name that starts with "-".
+    //
+    // TODO Consider what we should do with paths on windows. Stuff like path\filename.xml fails.
+    // TODO use invalid URI in query name, xmlpatterns -is-uri 'as1/#(¤/¤)("#'
+
+    // TODO add xmlpatterns file1 file2 file3
+    // TODO add xmlpatterns -is-uri file1 file2 file3
+}
+
+void tst_XmlPatterns::createNonWritable(const QString &name)
+{
+    /* Create an existing, empty, non-writable file. */
+    QFile outFile(name);
+    QVERIFY(outFile.open(QIODevice::ReadWrite));
+    outFile.write(QByteArray("1"));
+    QVERIFY(outFile.resize(0));
+    outFile.close();
+    QVERIFY(outFile.setPermissions(QFile::Permissions(QFile::ReadOwner)));
+}
+
+void tst_XmlPatterns::removeNonWritable(QFile &outFile)
+{
+    /* Kill off temporary files. */
+    if(!outFile.remove())
+    {
+        /* Since one file is used for testing that we can handle non-writable file by
+         * changing the permissions, we need to revert it such that we can remove it. */
+        outFile.setPermissions(QFile::WriteOwner);
+        outFile.remove();
+    }
+}
+
+/*!
+ Check that we gracefully handle writing out to stdout
+ when the latter is not writable.
+ */
+void tst_XmlPatterns::stdoutFailure() const
+{
+    return; // TODO It's really hard to write testing code for this.
+
+    const QString outName(QLatin1String("stdoutFailure.out"));
+    createNonWritable(outName);
+
+    QProcess process;
+    // If we enable this line, waitForFinished() fails.
+    //process.setStandardOutputFile(outName);
+
+    process.setWorkingDirectory(QDir::current().absoluteFilePath(QString()));
+    process.start(m_command, QStringList(SRCDIR "queries/onePlusOne.xq"));
+
+    QCOMPARE(process.exitStatus(), QProcess::NormalExit);
+    QVERIFY(process.waitForFinished());
+
+    QFile outFile(outName);
+    QVERIFY(outFile.open(QIODevice::ReadOnly));
+    QCOMPARE(outFile.readAll(), QByteArray());
+
+    QCOMPARE(process.exitCode(), 1);
+
+    removeNonWritable(outFile);
+}
+
+void tst_XmlPatterns::cleanupTestCase() const
+{
+    /* Remove temporaries that we create. */
+    QStringList files;
+    files << QLatin1String("existingContent.out")
+          << QLatin1String("notWritable.out")
+          << QLatin1String("output1");
+
+    for(int i = 0; i < files.count(); ++i)
+    {
+        QFile file(files.at(i));
+        removeNonWritable(file);
+    }
+
+    QCOMPARE(m_generatedTests, 0);
+}
+
+void tst_XmlPatterns::xsltSupport()
+{
+    xquerySupport();
+}
+
+void tst_XmlPatterns::xsltSupport_data() const
+{
+    if(m_dontRun)
+        QSKIP("The command line utility is not in the path.", SkipAll);
+
+#ifdef Q_OS_WINCE
+    QSKIP("WinCE: This test uses unsupported WinCE functionality", SkipAll);
+#endif
+
+    QTest::addColumn<int>("expectedExitCode");
+    QTest::addColumn<QByteArray>("expectedQueryOutput");
+    QTest::addColumn<QStringList>("arguments");
+    QTest::addColumn<QString>("cwd");
+    QTest::addColumn<QString>("outputFile");
+
+    QTest::newRow("Evaluate a stylesheet, with no context document")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String("stylesheets/onlyRootTemplate.xsl"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Pass in a stylesheet file which contains an XQuery query")
+        << 2
+        << QByteArray()
+        << (QStringList() << QLatin1String(SRCDIR "stylesheets/queryAsStylesheet.xsl")
+                          << QLatin1String(SRCDIR "queries/simpleDocument.xml"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Pass in a stylesheet file and a focus file which doesn't exist")
+        << 2
+        << QByteArray()
+        << (QStringList() << QLatin1String("stylesheets/onlyRootTemplate.xsl")
+                          << QLatin1String("doesNotExist.Nope.xml"))
+        << QString()
+        << QString();
+
+    QTest::newRow("-initial-template doesn't work with XQueries.")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String("-initial-template")
+                          << QLatin1String("name")
+                          << QLatin1String(SRCDIR "queries/onePlusOne.xq"))
+        << QString()
+        << QString();
+
+    QTest::newRow("-initial-template must be followed by a value")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String("-initial-template")
+                          << QLatin1String("stylesheets/onlyRootTemplate.xsl"))
+        << QString()
+        << QString();
+
+    QTest::newRow("-initial-template must be followed by a value(#2)")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String("stylesheets/onlyRootTemplate.xsl")
+                          << QLatin1String("-initial-template"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Invalid template name")
+        << 1
+        << QByteArray()
+        << (QStringList() << QLatin1String("-initial-template")
+                          << QLatin1String("abc:def")
+                          << QLatin1String("stylesheets/onlyRootTemplate.xsl"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Specify a named template, that exists")
+        << 0
+        << QByteArray("named-template")
+        << (QStringList() << QLatin1String("-no-format")
+                          << QLatin1String("-initial-template")
+                          << QLatin1String("main")
+                          << QLatin1String(SRCDIR "stylesheets/namedAndRootTemplate.xsl")
+                          << QLatin1String(SRCDIR "stylesheets/documentElement.xml"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Specify a named template, that does not exists")
+        << 0
+        << QByteArray("root-template")
+        << (QStringList() << QLatin1String("-no-format")
+                          << QLatin1String("-initial-template")
+                          << QLatin1String("no-template-by-this-name")
+                          << QLatin1String(SRCDIR "stylesheets/namedAndRootTemplate.xsl")
+                          << QLatin1String(SRCDIR "stylesheets/documentElement.xml"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Call a named template, and use no focus.")
+        << 0
+        << QByteArray("named-template")
+        << (QStringList() << QLatin1String("-no-format")
+                          << QLatin1String("-initial-template")
+                          << QLatin1String("main")
+                          << QLatin1String(SRCDIR "stylesheets/namedAndRootTemplate.xsl"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Call a named template, and use no focus.")
+        << 0
+        << QByteArray("namespaced-template")
+        << (QStringList() << QLatin1String("-no-format")
+                          << QLatin1String("-initial-template")
+                          << QLatin1String("{http://example.com/NS}main")
+                          << QLatin1String(SRCDIR "stylesheets/namedAndRootTemplate.xsl"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Invoke a template, and use/pass parameters.")
+        << 0
+        << QByteArray("defParam overridedDefaultedParam implicitlyRequiredValue\n")
+        << (QStringList() << QLatin1String("-initial-template")
+                          << QLatin1String("main")
+                          << QLatin1String(SRCDIR "stylesheets/useParameters.xsl")
+                          << QLatin1String("-param")
+                          << QLatin1String("overridedDefaultedParam=overridedDefaultedParam")
+                          << QLatin1String("-param")
+                          << QLatin1String("implicitlyRequiredValue=implicitlyRequiredValue"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Use a simplified stylesheet module")
+        << 0
+        << QByteArray("<output>some text</output>\n")
+        << (QStringList() << QLatin1String(SRCDIR "stylesheets/simplifiedStylesheetModule.xsl")
+                          << QLatin1String(SRCDIR "stylesheets/simplifiedStylesheetModule.xml"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Not well-formed stylesheet, causes crash in coloring code.")
+        << 2
+        << QByteArray()
+        << (QStringList() << QLatin1String(SRCDIR "stylesheets/notWellformed.xsl")
+                          << QLatin1String(SRCDIR "queries/simpleDocument.xml"))
+        << QString()
+        << QString();
+
+    QTest::newRow("Not well-formed instance document, causes crash in coloring code.")
+        << 2
+        << QByteArray()
+        << (QStringList() << QLatin1String(SRCDIR "stylesheets/bool070.xsl")
+                          << QLatin1String(SRCDIR "stylesheets/bool070.xml"))
+        << QString()
+        << QString();
+
+    // TODO test -is-uris with context
+    // TODO fail to load focus document when using XSL-T
+    // TODO fail to load focus document when using XQuery
+    // TODO focus via FTP or so with xquery
+    // TODO use URI in focus
+    // TODO use invalid URI in focus
+
+    // TODO invoke a template which has required params.
+}
+
+QTEST_MAIN(tst_XmlPatterns)
+
+#include "tst_xmlpatterns.moc"
+#else
+QTEST_NOOP_MAIN
+#endif
+
+// vim: et:ts=4:sw=4:sts=4