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

source code of qtxmlpatterns/tests/auto/xmlpatterns/tst_xmlpatterns.cpp