Update copyright year in license headers.
[profile/ivi/qtxmlpatterns.git] / tests / auto / xmlpatterns / tst_xmlpatterns.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the test suite of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42
43 #include <QFile>
44 #include <QtTest/QtTest>
45
46 #include "../qxmlquery/TestFundament.h"
47 #include "../network-settings.h"
48
49 #if defined(Q_OS_SYMBIAN)
50 #define SRCDIR ""
51 #endif
52
53 /*!
54  \class tst_XmlPatterns
55  \internal
56  \since 4.4
57  \brief Tests the command line interface, \c xmlpatterns, for the XQuery code.
58
59  This test is not intended for testing the engine, but all the gluing the
60  command line interface do: error reporting, query output, variable bindings, exit
61  codes, and so on.
62
63  In other words, if you have an engine bug; don't add it here because it won't be
64  tested properly. Instead add it to the test suite.
65
66  */
67 class tst_XmlPatterns : public QObject
68                       , private TestFundament
69 {
70     Q_OBJECT
71
72 public:
73     tst_XmlPatterns();
74
75 private Q_SLOTS:
76     void initTestCase();
77     void xquerySupport();
78     void xquerySupport_data() const;
79     void xsltSupport();
80     void xsltSupport_data() const;
81     void stdoutFailure() const;
82     void cleanupTestCase() const;
83
84 private:
85     static void createNonWritable(const QString &name);
86     static void removeNonWritable(QFile &outFile);
87
88     int             m_generatedTests;
89     /**
90      * Get rid of characters that complicates on various file systems.
91      */
92     const QRegExp   m_normalizeTestName;
93     /**
94      * @note Perforce disallows wildcards in the name.
95      */
96     const QRegExp   m_filenameInStderr;
97     const QString   m_command;
98     bool            m_dontRun;
99 };
100
101 tst_XmlPatterns::tst_XmlPatterns() : m_generatedTests(0)
102                                    , m_normalizeTestName(QLatin1String("[\\*\\?#\\-\\/:; ()',&]"))
103                                    , m_filenameInStderr(QLatin1String("file:\\/\\/.*(\\.xq|\\.gccxml|\\.xml|\\.xsl|-)(,|:)"))
104                                    , m_command(QLibraryInfo::location(QLibraryInfo::BinariesPath) + QLatin1String("/xmlpatterns"))
105                                    , m_dontRun(false)
106 {
107 }
108
109 void tst_XmlPatterns::initTestCase()
110 {
111     QVERIFY(QtNetworkSettings::verifyTestNetworkSettings());
112
113     QVERIFY(m_normalizeTestName.isValid());
114     QVERIFY(m_filenameInStderr.isValid());
115
116     QProcess process;
117     process.start(m_command);
118
119     if(!process.waitForFinished())
120     {
121         m_dontRun = true;
122         QSKIP(
123             qPrintable(QString(
124                 "The command line tool (%1) could not be run, possibly because Qt was "
125                 "incompletely built or installed. No tests will be run."
126             ).arg(m_command))
127         );
128     }
129
130 }
131
132 void tst_XmlPatterns::xquerySupport()
133 {
134     if(m_dontRun)
135         QSKIP("The command line utility is not in the path.");
136
137 #ifdef Q_OS_WINCE
138     QSKIP("WinCE: This test uses unsupported WinCE functionality");
139 #elif defined(Q_OS_SYMBIAN)
140     QSKIP("Symbian: This test uses unsupported Symbian functionality (QProcess with std streams)");
141 #elif defined(Q_OS_MAC)
142     QSKIP("MacOs: Failed on Mac. Will be fixed by bug fix for QTBUG-22556");
143 #endif
144
145     QFETCH(int,         expectedExitCode);
146     QFETCH(QByteArray,  expectedQueryOutput);
147     QFETCH(QStringList, arguments);
148     QFETCH(QString,     cwd);
149     QFETCH(QString,     outputFile);
150
151     QProcess process;
152
153     if(!cwd.isEmpty())
154         process.setWorkingDirectory(inputFile(cwd));
155
156     process.start(m_command, arguments);
157
158     QCOMPARE(process.exitStatus(), QProcess::NormalExit);
159     QVERIFY(process.waitForFinished());
160
161     if(process.exitCode() != expectedExitCode)
162         QTextStream(stderr) << "stderr:" << process.readAllStandardError();
163
164     QCOMPARE(process.exitCode(), expectedExitCode);
165
166     const QByteArray rawProducedStderr((process.readAllStandardError()));
167     QString fixedStderr(QString::fromLocal8Bit(rawProducedStderr).remove(m_filenameInStderr));
168     // convert Windows line endings to Unix ones
169     fixedStderr.replace("\r\n", "\n");
170
171     const QString errorFileName(inputFile(QLatin1String(SRCDIR "stderrBaselines/") +
172                                           QString::fromUtf8(QTest::currentDataTag()).remove(m_normalizeTestName) +
173                                           QLatin1String(".txt")));
174
175     QFile writeErr(errorFileName);
176
177     if(writeErr.exists())
178     {
179         QVERIFY(writeErr.open(QIODevice::ReadOnly));
180         QString rawExpectedStdErr(QString::fromLocal8Bit(writeErr.readAll()));
181
182         /* On Windows, at least MinGW, this differs. */
183         if(qstrcmp(QTest::currentDataTag(), "-output with a non-writable file") == 0)
184         {
185             QVERIFY(fixedStderr == rawExpectedStdErr.remove(m_filenameInStderr) ||
186                     fixedStderr.trimmed() == "Failed to open file notWritable.out for writing: Access is denied.");
187         }
188         else if(qstrcmp(QTest::currentDataTag(), "Invoke -version") == 0)
189         {
190             /* There's a wide range of different version strings used. For
191              * instance, "4.4.0-rc1". */
192             const QRegExp removeVersion(QLatin1String(" Qt \\d\\.\\d.*"));
193             QVERIFY(removeVersion.isValid());
194             QCOMPARE(QString(fixedStderr).remove(removeVersion) + QChar('|'), rawExpectedStdErr + QChar('|'));
195         }
196         else
197             QCOMPARE(fixedStderr, rawExpectedStdErr.remove(m_filenameInStderr));
198     }
199     else
200     {
201         QFile writeErr(errorFileName);
202         QVERIFY(writeErr.open(QIODevice::WriteOnly));
203
204         QCOMPARE(writeErr.write(rawProducedStderr), qint64(rawProducedStderr.count()));
205         QTextStream(stderr) << "creating file " << errorFileName;
206         ++m_generatedTests;
207     }
208
209     const QByteArray actual(process.readAllStandardOutput());
210
211     if(outputFile.isEmpty())
212     {
213         QCOMPARE(actual, expectedQueryOutput);
214         return; /* We're done, this test was not creating any output file. */
215     }
216     else
217     {
218         QVERIFY(actual.isEmpty());
219
220         QFile outFile(outputFile);
221
222         QVERIFY(outFile.exists());
223         QVERIFY(outFile.open(QIODevice::ReadOnly));
224
225         QCOMPARE(outFile.readAll(), expectedQueryOutput);
226
227         removeNonWritable(outFile);
228     }
229 }
230
231 void tst_XmlPatterns::xquerySupport_data() const
232 {
233 #if defined(Q_OS_WINCE) || defined(Q_OS_SYMBIAN)
234     return;
235 #endif
236
237     /* Check one file for existence, to avoid possible false positives. */
238     QVERIFY(QFile::exists(inputFile(QLatin1String(SRCDIR "queries/onePlusOne.xq"))));
239
240     QTest::addColumn<int>("expectedExitCode");
241     QTest::addColumn<QByteArray>("expectedQueryOutput");
242     QTest::addColumn<QStringList>("arguments");
243     QTest::addColumn<QString>("cwd");
244     QTest::addColumn<QString>("outputFile");
245
246     QTest::newRow("A simple math query")
247         << 0
248         << QByteArray("2\n")
249         << QStringList((QLatin1String(SRCDIR "queries/onePlusOne.xq")))
250         << QString()
251         << QString();
252
253     QTest::newRow("An unbound external variable")
254         << 2
255         << QByteArray()
256         << QStringList(QLatin1String(SRCDIR "queries/externalVariable.xq"))
257         << QString()
258         << QString();
259
260     QTest::newRow("Bind an external variable")
261         << 0
262         << QByteArray("1 4<e>1</e>true\n")
263         << (QStringList() << QLatin1String(SRCDIR "queries/externalVariable.xq")
264                           << QLatin1String("-param")
265                           << QLatin1String("externalVariable=1"))
266         << QString()
267         << QString();
268
269     QTest::newRow("Bind an external variable, query appearing last")
270         << 0
271         << QByteArray("1 4<e>1</e>true\n")
272         << (QStringList() << QLatin1String("-param")
273                           << QLatin1String("externalVariable=1")
274                           << QLatin1String(SRCDIR "queries/externalVariable.xq"))
275         << QString()
276         << QString();
277
278     QTest::newRow("Use fn:doc")
279         << 0
280         << 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")
281         << QStringList(QLatin1String(SRCDIR "queries/openDocument.xq"))
282         << QString()
283         << QString();
284
285     QTest::newRow("Use fn:doc, together with -no-format, last")
286         << 0
287         << 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>")
288         << (QStringList() << QLatin1String(SRCDIR "queries/openDocument.xq")
289                           << QLatin1String("-no-format"))
290         << QString()
291         << QString();
292
293     QTest::newRow("Use fn:doc, together with -no-format, first")
294         << 0
295         << 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>")
296         << (QStringList() << QLatin1String("-no-format")
297                           << QLatin1String(SRCDIR "queries/openDocument.xq"))
298         << QString()
299         << QString();
300
301     /* This is true for the command line utility, but not QXmlQuery::setQuery(). */
302     QTest::newRow("Make sure query paths are resolved against CWD, not the location of the executable.")
303         << 0
304         << QByteArray("2\n")
305         << QStringList(QLatin1String(SRCDIR "queries/onePlusOne.xq"))
306         << QString::fromLatin1("queries")
307         << QString();
308
309     QTest::newRow("Call fn:error()")
310         << 2
311         << QByteArray()
312         << QStringList(QLatin1String(SRCDIR "queries/errorFunction.xq"))
313         << QString()
314         << QString();
315
316     QTest::newRow("Evaluate a library module")
317         << 2
318         << QByteArray()
319         << QStringList(QLatin1String(SRCDIR "queries/simpleLibraryModule.xq"))
320         << QString()
321         << QString();
322
323     QTest::newRow("Trigger a static error.")
324         << 2
325         << QByteArray()
326         << QStringList(QLatin1String(SRCDIR "queries/staticError.xq"))
327         << QString()
328         << QString();
329
330     QTest::newRow("Pass -help")
331         << 0
332         << QByteArray()
333         << QStringList(QLatin1String("-help"))
334         << QString()
335         << QString();
336
337     QTest::newRow("Open an nonexistent file")
338         << 2
339         << QByteArray()
340         << QStringList(QLatin1String(SRCDIR "queries/ThisFileDoesNotExist.xq"))
341         << QString()
342         << QString();
343
344     /* The following five tests exists to test the various
345      * markup classes in the message. */
346     QTest::newRow("XQuery-function message markups")
347         << 2
348         << QByteArray()
349         << QStringList(QLatin1String(SRCDIR "queries/wrongArity.xq"))
350         << QString()
351         << QString();
352
353     QTest::newRow("XQuery-type message markups")
354         << 2
355         << QByteArray()
356         << QStringList(QLatin1String(SRCDIR "queries/typeError.xq"))
357         << QString()
358         << QString();
359
360     QTest::newRow("XQuery-data & XQuery-keyword message markups")
361         << 2
362         << QByteArray()
363         << QStringList(QLatin1String(SRCDIR "queries/zeroDivision.xq"))
364         << QString()
365         << QString();
366
367     QTest::newRow("XQuery-uri message markups")
368         << 2
369         << QByteArray()
370         << QStringList(QLatin1String(SRCDIR "queries/unsupportedCollation.xq"))
371         << QString()
372         << QString();
373
374     QTest::newRow("XQuery-expression message markups")
375         << 2
376         << QByteArray()
377         << QStringList(QLatin1String(SRCDIR "queries/invalidRegexp.xq"))
378         << QString()
379         << QString();
380
381     QTest::newRow("Print a list of available regexp flags(The available flags are formatted in a complex way.)")
382         << 2
383         << QByteArray()
384         << QStringList(QLatin1String(SRCDIR "queries/invalidRegexpFlag.xq"))
385         << QString()
386         << QString();
387
388     QTest::newRow("Trigger an assert in QPatternist::ColorOutput. The query naturally contains an error; XPTY0004.")
389         << 2
390         << QByteArray()
391         << QStringList(QLatin1String(SRCDIR "queries/flwor.xq"))
392         << QString()
393         << QString();
394
395     QTest::newRow("Trigger a second assert in QPatternist::ColorOutput. The query naturally contains XPST0003.")
396         << 2
397         << QByteArray()
398         << QStringList(QLatin1String(SRCDIR "queries/syntaxError.xq"))
399         << QString()
400         << QString();
401
402     QTest::newRow("-param is missing so multiple queries appear")
403         << 2
404         << QByteArray()
405         << (QStringList() << QLatin1String(SRCDIR "queries/reportGlobals.xq")
406                           << QLatin1String("fileToOpen=globals.gccxml"))
407         << QString()
408         << QString();
409
410     QTest::newRow("only -no-format")
411         << 1
412         << QByteArray()
413         << (QStringList() << QLatin1String("-no-format"))
414         << QString()
415         << QString();
416
417     QTest::newRow("Basic use of -output, query first")
418         << 0
419         << QByteArray("2\n")
420         << (QStringList() << QLatin1String(SRCDIR "queries/onePlusOne.xq")
421                           << QLatin1String("-output")
422                           << QLatin1String("basicOutput.out"))
423         << QString()
424         << QString::fromLatin1("basicOutput.out");
425
426     QTest::newRow("Basic use of -output, query last")
427         << 0
428         << QByteArray("<e/>\n")
429         << (QStringList() << QLatin1String("-output")
430                           << QLatin1String("basicOutput2.out")
431                           << QLatin1String(SRCDIR "queries/oneElement.xq"))
432         << QString()
433         << QString::fromLatin1("basicOutput2.out");
434
435     QTest::newRow("A single query, that does not exist")
436         << 2
437         << QByteArray()
438         << (QStringList() << QLatin1String(SRCDIR "doesNotExist.xq"))
439         << QString()
440         << QString();
441
442     QTest::newRow("Specify two identical query names")
443         << 2
444         << QByteArray()
445         << (QStringList() << QLatin1String(SRCDIR "query.xq")
446                           << QLatin1String(SRCDIR "query.xq"))
447         << QString()
448         << QString();
449
450     QTest::newRow("Specify no arguments at all.")
451         << 1
452         << QByteArray()
453         << QStringList()
454         << QString()
455         << QString();
456
457     QTest::newRow("Use -output twice")
458         << 1
459         << QByteArray()
460         << (QStringList() << QLatin1String("-output")
461                           << QLatin1String("output1")
462                           << QLatin1String("-output")
463                           << QLatin1String("output2"))
464         << QString()
465         << QString();
466
467     {
468         const QString filename(QString::fromLatin1("notWritable.out"));
469         createNonWritable(filename);
470
471         QTest::newRow("-output with a non-writable file")
472             << 1
473             << QByteArray()
474             << (QStringList() << QLatin1String("-output")
475                               << filename
476                               << QLatin1String(SRCDIR "queries/onePlusOne.xq"))
477             << QString()
478             << filename;
479     }
480
481     {
482         const QString outName(QString::fromLatin1("existingContent.out"));
483         QFile outFile(outName);
484         QVERIFY(outFile.open(QIODevice::WriteOnly));
485         QCOMPARE(outFile.write("Existing content\n"), qint64(17));
486         outFile.close();
487
488         QTest::newRow("Use -output on a file with existing content, to ensure we truncate, not append the content we produce.")
489             << 0
490             << QByteArray("2\n")
491             << (QStringList() << QLatin1String("-output")
492                               << outName
493                               << QLatin1String(SRCDIR "queries/onePlusOne.xq"))
494             << QString()
495             << outName;
496     }
497
498     QTest::newRow("one query, and a terminating dash at the end")
499         << 0
500         << QByteArray("2\n")
501         << (QStringList() << QLatin1String(SRCDIR "queries/onePlusOne.xq")
502                           << QLatin1String("-"))
503         << QString()
504         << QString();
505
506     QTest::newRow("one query, with a preceding dash")
507         << 0
508         << QByteArray("2\n")
509         << (QStringList() << QLatin1String("-")
510                           << QLatin1String(SRCDIR "queries/onePlusOne.xq"))
511         << QString()
512         << QString();
513
514     QTest::newRow("A single dash, that's invalid")
515         << 1
516         << QByteArray()
517         << (QStringList() << QLatin1String("-"))
518         << QString()
519         << QString();
520
521     QTest::newRow("Invoke -version")
522         << 0
523         << QByteArray()
524         << (QStringList() << QLatin1String("-version"))
525         << QString()
526         << QString();
527
528     QTest::newRow("Unknown switch; -unknown-switch")
529         << 1
530         << QByteArray()
531         << (QStringList() << QLatin1String("-unknown-switch"))
532         << QString()
533         << QString();
534
535     QTest::newRow("Unknown switch; -d")
536         << 1
537         << QByteArray()
538         << (QStringList() << QLatin1String("-d"))
539         << QString()
540         << QString();
541
542     QTest::newRow("Passing a single dash is insufficient")
543         << 1
544         << QByteArray()
545         << (QStringList() << QLatin1String("-"))
546         << QString()
547         << QString();
548
549     QTest::newRow("Passing two dashes, the last is interpreted as a file name")
550         << 2
551         << QByteArray()
552         << (QStringList() << QLatin1String("-")
553                           << QLatin1String("-"))
554         << QString()
555         << QString();
556
557     QTest::newRow("Pass three dashes, the two last gets interpreted as two query arguments")
558         << 2
559         << QByteArray()
560         << (QStringList() << QLatin1String("-")
561                           << QLatin1String("-")
562                           << QLatin1String("-"))
563         << QString()
564         << QString();
565
566     QTest::newRow("Load query via data: scheme")
567         << 0
568         << QByteArray("<e/>\n")
569         << (QStringList() << QLatin1String("-is-uri") << QLatin1String("data:application/xml,%3Ce%2F%3E"))
570         << QString()
571         << QString();
572
573     QTest::newRow("Load query via FTP")
574         << 0
575         << QByteArray("This was received via FTP\n")
576         << (QStringList() << QLatin1String("-is-uri") << QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/viaFtp.xq"))
577         << QString()
578         << QString();
579
580     QTest::newRow("Load query via HTTP")
581         << 0
582         << QByteArray("This was received via HTTP.\n")
583         << (QStringList() << QLatin1String("-is-uri") << QString("http://" + QtNetworkSettings::serverName() + "/qxmlquery/viaHttp.xq"))
584         << QString()
585         << QString();
586
587     QTest::newRow("We don't support -format any longer")
588         << 1
589         << QByteArray()
590         << (QStringList() << QLatin1String("-format"))
591         << QString()
592         << QString();
593
594     QTest::newRow("Run a query which evaluates to the empty sequence.")
595         << 0
596         << QByteArray("\n")
597         << (QStringList() << QLatin1String(SRCDIR "queries/emptySequence.xq"))
598         << QString()
599         << QString();
600
601     QTest::newRow("Run a query which evaluates to a single document node with no children.")
602         << 0
603         << QByteArray("\n")
604         << (QStringList() << QLatin1String(SRCDIR "queries/onlyDocumentNode.xq"))
605         << QString()
606         << QString();
607
608     QTest::newRow("Invoke with invalid -param value.")
609         << 1
610         << QByteArray()
611         << (QStringList() << QLatin1String(SRCDIR "queries/externalVariable.xq")
612                           << QLatin1String("-param")
613                           << QLatin1String("EqualSignIsMissing"))
614         << QString()
615         << QString();
616
617     QTest::newRow("Invoke with colon in variable name.")
618         << 1
619         << QByteArray()
620         << (QStringList() << QLatin1String(SRCDIR "queries/externalVariable.xq")
621                           << QLatin1String("-param")
622                           << QLatin1String("xs:name=value"))
623         << QString()
624         << QString();
625
626     QTest::newRow("Invoke with missing name in -param arg.")
627         << 1
628         << QByteArray()
629         << (QStringList() << QLatin1String(SRCDIR "queries/externalVariable.xq")
630                           << QLatin1String("-param")
631                           << QLatin1String("=value"))
632         << QString()
633         << QString();
634
635     QTest::newRow("Invoke with -param that has two adjacent equal signs.")
636         << 0
637         << QByteArray("START =text END\n")
638         << (QStringList() << QLatin1String(SRCDIR "queries/externalStringVariable.xq")
639                           << QLatin1String("-param")
640                           << QLatin1String("externalString==text"))
641         << QString()
642         << QString();
643
644     QTest::newRow("Pass in an external variable, but the query doesn't use it.")
645         << 0
646         << QByteArray("2\n")
647         << (QStringList() << QLatin1String(SRCDIR "queries/onePlusOne.xq")
648                           << QLatin1String("-param")
649                           << QLatin1String("externalString==text"))
650         << QString()
651         << QString();
652
653     /* This is how an empty string would have been passed in. */
654     QTest::newRow("Invoke with -param that has no value.")
655         << 0
656         << QByteArray("START  END\n")
657         << (QStringList() << QLatin1String(SRCDIR "queries/externalStringVariable.xq")
658                           << QLatin1String("-param")
659                           << QLatin1String("externalString="))
660         << QString()
661         << QString();
662
663     QTest::newRow("Ensure -is-uri can appear after the query filename")
664         << 0
665         << QByteArray("<e/>\n")
666         << (QStringList() << QLatin1String("data:application/xml,%3Ce%2F%3E") << QLatin1String("-is-uri"))
667         << QString()
668         << QString();
669
670     QTest::newRow("Use a native path")
671         << 0
672         << QByteArray("2\n")
673         << (QStringList() << QDir::toNativeSeparators(QLatin1String(SRCDIR "queries/onePlusOne.xq")))
674         << QString()
675         << QString();
676
677     QTest::newRow("Pass in invalid URI")
678         << 2
679         << QByteArray()
680         << (QStringList() << QLatin1String("-is-uri") << QLatin1String("data:application/xml;base64,PGUvg==="))
681         << QString()
682         << QString();
683
684     /* Not relevant anymore.
685     QTest::newRow("A valid, existing query, followed by a bogus one")
686         << 1
687         << QByteArray()
688         << (QStringList() << QLatin1String(SRCDIR "queries/onePlusOne.xq")
689                           << QLatin1String(SRCDIR "doesNotExist.xq"))
690         << QString()
691         << QString();
692         */
693
694     /* Not relevant anymore.
695     QTest::newRow("Specify two different query names")
696         << 1
697         << QByteArray()
698         << (QStringList() << QLatin1String(SRCDIR "query1.xq")
699                           << QLatin1String(SRCDIR "query2.xq"))
700         << QString()
701         << QString();
702         */
703
704     // TODO use focus with xquery
705     // TODO fail to load focus with xquery
706     // TODO focus with URI with xquery
707     // TODO focus via FTP or so with xquery
708
709
710     QTest::newRow("Use -param twice")
711         << 0
712         << QByteArray("param1 param2\n")
713         << (QStringList() << QLatin1String(SRCDIR "queries/twoVariables.xq")
714                           << QLatin1String("-param")
715                           << QLatin1String("var1=param1")
716                           << QLatin1String("-param")
717                           << QLatin1String("var2=param2"))
718         << QString()
719         << QString();
720
721     QTest::newRow("Use -param thrice")
722         << 0
723         << QByteArray("param1 param2 third\n")
724         << (QStringList() << QLatin1String(SRCDIR "queries/threeVariables.xq")
725                           << QLatin1String("-param")
726                           << QLatin1String("var1=param1")
727                           << QLatin1String("-param")
728                           << QLatin1String("var2=param2")
729                           << QLatin1String("-param")
730                           << QLatin1String("var3=third"))
731         << QString()
732         << QString();
733
734     QTest::newRow("Specify the same parameter twice, different values")
735         << 1
736         << QByteArray()
737         << (QStringList() << QLatin1String(SRCDIR "queries/onePlusOne.xq")
738                           << QLatin1String("-param")
739                           << QLatin1String("duplicated=param1")
740                           << QLatin1String("-param")
741                           << QLatin1String("duplicated=param2"))
742         << QString()
743         << QString();
744
745     QTest::newRow("Specify the same parameter twice, same values")
746         << 1
747         << QByteArray()
748         << (QStringList() << QLatin1String(SRCDIR "queries/onePlusOne.xq")
749                           << QLatin1String("-param")
750                           << QLatin1String("duplicated=param1")
751                           << QLatin1String("-param")
752                           << QLatin1String("duplicated=param2"))
753         << QString()
754         << QString();
755
756     QTest::newRow("Open a non-existing collection.")
757         << 2
758         << QByteArray()
759         << (QStringList() << QLatin1String(SRCDIR "queries/nonexistingCollection.xq"))
760         << QString()
761         << QString();
762
763     // TODO https?
764     // TODO pass external variables that allows space around the equal sign.
765     // TODO run fn:trace()
766     // TODO Trigger warning
767     // TODO what can we do with queries/nodeSequence.xq?
768     // TODO trigger serialization error
769     // TODO "xmlpatterns e.xq x" gives "binding must equal .."
770     //
771     // TODO use stdout where it's connected to a non-writable file.
772     // TODO specify -format twice, or whatever it's called.
773     // TODO query name that starts with "-".
774     //
775     // TODO Consider what we should do with paths on windows. Stuff like path\filename.xml fails.
776     // TODO use invalid URI in query name, xmlpatterns -is-uri 'as1/#(¤/¤)("#'
777
778     // TODO add xmlpatterns file1 file2 file3
779     // TODO add xmlpatterns -is-uri file1 file2 file3
780 }
781
782 void tst_XmlPatterns::createNonWritable(const QString &name)
783 {
784     /* Create an existing, empty, non-writable file. */
785     QFile outFile(name);
786     QVERIFY(outFile.open(QIODevice::ReadWrite));
787     outFile.write(QByteArray("1"));
788     QVERIFY(outFile.resize(0));
789     outFile.close();
790     QVERIFY(outFile.setPermissions(QFile::Permissions(QFile::ReadOwner)));
791 }
792
793 void tst_XmlPatterns::removeNonWritable(QFile &outFile)
794 {
795     /* Kill off temporary files. */
796     if(!outFile.remove())
797     {
798         /* Since one file is used for testing that we can handle non-writable file by
799          * changing the permissions, we need to revert it such that we can remove it. */
800         outFile.setPermissions(QFile::WriteOwner);
801         outFile.remove();
802     }
803 }
804
805 /*!
806  Check that we gracefully handle writing out to stdout
807  when the latter is not writable.
808  */
809 void tst_XmlPatterns::stdoutFailure() const
810 {
811     return; // TODO It's really hard to write testing code for this.
812
813     const QString outName(QLatin1String("stdoutFailure.out"));
814     createNonWritable(outName);
815
816     QProcess process;
817     // If we enable this line, waitForFinished() fails.
818     //process.setStandardOutputFile(outName);
819
820     process.setWorkingDirectory(QDir::current().absoluteFilePath(QString()));
821     process.start(m_command, QStringList(SRCDIR "queries/onePlusOne.xq"));
822
823     QCOMPARE(process.exitStatus(), QProcess::NormalExit);
824     QVERIFY(process.waitForFinished());
825
826     QFile outFile(outName);
827     QVERIFY(outFile.open(QIODevice::ReadOnly));
828     QCOMPARE(outFile.readAll(), QByteArray());
829
830     QCOMPARE(process.exitCode(), 1);
831
832     removeNonWritable(outFile);
833 }
834
835 void tst_XmlPatterns::cleanupTestCase() const
836 {
837     /* Remove temporaries that we create. */
838     QStringList files;
839     files << QLatin1String("existingContent.out")
840           << QLatin1String("notWritable.out")
841           << QLatin1String("output1");
842
843     for(int i = 0; i < files.count(); ++i)
844     {
845         QFile file(files.at(i));
846         removeNonWritable(file);
847     }
848
849     QCOMPARE(m_generatedTests, 0);
850 }
851
852 void tst_XmlPatterns::xsltSupport()
853 {
854     xquerySupport();
855 }
856
857 void tst_XmlPatterns::xsltSupport_data() const
858 {
859     if(m_dontRun)
860         QSKIP("The command line utility is not in the path.");
861
862 #ifdef Q_OS_WINCE
863     QSKIP("WinCE: This test uses unsupported WinCE functionality");
864 #elif defined(Q_OS_SYMBIAN)
865     QSKIP("Symbian: This test uses unsupported Symbian functionality (QProcess with std streams)");
866 #endif
867
868     QTest::addColumn<int>("expectedExitCode");
869     QTest::addColumn<QByteArray>("expectedQueryOutput");
870     QTest::addColumn<QStringList>("arguments");
871     QTest::addColumn<QString>("cwd");
872     QTest::addColumn<QString>("outputFile");
873
874     QTest::newRow("Evaluate a stylesheet, with no context document")
875         << 1
876         << QByteArray()
877         << (QStringList() << QLatin1String("stylesheets/onlyRootTemplate.xsl"))
878         << QString()
879         << QString();
880
881     QTest::newRow("Pass in a stylesheet file which contains an XQuery query")
882         << 2
883         << QByteArray()
884         << (QStringList() << QLatin1String(SRCDIR "stylesheets/queryAsStylesheet.xsl")
885                           << QLatin1String(SRCDIR "queries/simpleDocument.xml"))
886         << QString()
887         << QString();
888
889     QTest::newRow("Pass in a stylesheet file and a focus file which doesn't exist")
890         << 2
891         << QByteArray()
892         << (QStringList() << QLatin1String("stylesheets/onlyRootTemplate.xsl")
893                           << QLatin1String("doesNotExist.Nope.xml"))
894         << QString()
895         << QString();
896
897     QTest::newRow("-initial-template doesn't work with XQueries.")
898         << 1
899         << QByteArray()
900         << (QStringList() << QLatin1String("-initial-template")
901                           << QLatin1String("name")
902                           << QLatin1String(SRCDIR "queries/onePlusOne.xq"))
903         << QString()
904         << QString();
905
906     QTest::newRow("-initial-template must be followed by a value")
907         << 1
908         << QByteArray()
909         << (QStringList() << QLatin1String("-initial-template")
910                           << QLatin1String("stylesheets/onlyRootTemplate.xsl"))
911         << QString()
912         << QString();
913
914     QTest::newRow("-initial-template must be followed by a value(#2)")
915         << 1
916         << QByteArray()
917         << (QStringList() << QLatin1String("stylesheets/onlyRootTemplate.xsl")
918                           << QLatin1String("-initial-template"))
919         << QString()
920         << QString();
921
922     QTest::newRow("Invalid template name")
923         << 1
924         << QByteArray()
925         << (QStringList() << QLatin1String("-initial-template")
926                           << QLatin1String("abc:def")
927                           << QLatin1String("stylesheets/onlyRootTemplate.xsl"))
928         << QString()
929         << QString();
930
931     QTest::newRow("Specify a named template, that exists")
932         << 0
933         << QByteArray("named-template")
934         << (QStringList() << QLatin1String("-no-format")
935                           << QLatin1String("-initial-template")
936                           << QLatin1String("main")
937                           << QLatin1String(SRCDIR "stylesheets/namedAndRootTemplate.xsl")
938                           << QLatin1String(SRCDIR "stylesheets/documentElement.xml"))
939         << QString()
940         << QString();
941
942     QTest::newRow("Specify a named template, that does not exists")
943         << 0
944         << QByteArray("root-template")
945         << (QStringList() << QLatin1String("-no-format")
946                           << QLatin1String("-initial-template")
947                           << QLatin1String("no-template-by-this-name")
948                           << QLatin1String(SRCDIR "stylesheets/namedAndRootTemplate.xsl")
949                           << QLatin1String(SRCDIR "stylesheets/documentElement.xml"))
950         << QString()
951         << QString();
952
953     QTest::newRow("Call a named template, and use no focus.")
954         << 0
955         << QByteArray("named-template")
956         << (QStringList() << QLatin1String("-no-format")
957                           << QLatin1String("-initial-template")
958                           << QLatin1String("main")
959                           << QLatin1String(SRCDIR "stylesheets/namedAndRootTemplate.xsl"))
960         << QString()
961         << QString();
962
963     QTest::newRow("Call a named template, and use no focus.")
964         << 0
965         << QByteArray("namespaced-template")
966         << (QStringList() << QLatin1String("-no-format")
967                           << QLatin1String("-initial-template")
968                           << QLatin1String("{http://example.com/NS}main")
969                           << QLatin1String(SRCDIR "stylesheets/namedAndRootTemplate.xsl"))
970         << QString()
971         << QString();
972
973     QTest::newRow("Invoke a template, and use/pass parameters.")
974         << 0
975         << QByteArray("defParam overridedDefaultedParam implicitlyRequiredValue\n")
976         << (QStringList() << QLatin1String("-initial-template")
977                           << QLatin1String("main")
978                           << QLatin1String(SRCDIR "stylesheets/useParameters.xsl")
979                           << QLatin1String("-param")
980                           << QLatin1String("overridedDefaultedParam=overridedDefaultedParam")
981                           << QLatin1String("-param")
982                           << QLatin1String("implicitlyRequiredValue=implicitlyRequiredValue"))
983         << QString()
984         << QString();
985
986     QTest::newRow("Use a simplified stylesheet module")
987         << 0
988         << QByteArray("<output>some text</output>\n")
989         << (QStringList() << QLatin1String(SRCDIR "stylesheets/simplifiedStylesheetModule.xsl")
990                           << QLatin1String(SRCDIR "stylesheets/simplifiedStylesheetModule.xml"))
991         << QString()
992         << QString();
993
994     QTest::newRow("Not well-formed stylesheet, causes crash in coloring code.")
995         << 2
996         << QByteArray()
997         << (QStringList() << QLatin1String(SRCDIR "stylesheets/notWellformed.xsl")
998                           << QLatin1String(SRCDIR "queries/simpleDocument.xml"))
999         << QString()
1000         << QString();
1001
1002     QTest::newRow("Not well-formed instance document, causes crash in coloring code.")
1003         << 2
1004         << QByteArray()
1005         << (QStringList() << QLatin1String(SRCDIR "stylesheets/bool070.xsl")
1006                           << QLatin1String(SRCDIR "stylesheets/bool070.xml"))
1007         << QString()
1008         << QString();
1009
1010     // TODO test -is-uris with context
1011     // TODO fail to load focus document when using XSL-T
1012     // TODO fail to load focus document when using XQuery
1013     // TODO focus via FTP or so with xquery
1014     // TODO use URI in focus
1015     // TODO use invalid URI in focus
1016
1017     // TODO invoke a template which has required params.
1018 }
1019
1020 QTEST_MAIN(tst_XmlPatterns)
1021
1022 #include "tst_xmlpatterns.moc"
1023
1024 // vim: et:ts=4:sw=4:sts=4