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