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#include <QBuffer>
30#include <QUrl>
31#include <QXmlAttributes>
32#include <QXmlQuery>
33#include <QXmlResultItems>
34#include <QXmlSerializer>
35#include <private/qxmlquery_p.h>
36#include <algorithm>
37
38#include "DebugExpressionFactory.h"
39#include "ExternalSourceLoader.h"
40#include "Global.h"
41#include "TestSuite.h"
42#include "XMLWriter.h"
43
44#include "TestCase.h"
45
46using namespace QPatternistSDK;
47using namespace QPatternist;
48
49// STATIC DATA
50static const DebugExpressionFactory::Ptr s_exprFact(new DebugExpressionFactory());
51
52TestCase::TestCase() : m_result(0)
53{
54}
55
56TestCase::~TestCase()
57{
58 delete m_result;
59}
60
61static bool lessThan(const char *a, const char *b)
62{
63 return qstrcmp(str1: a, str2: b) < 0;
64}
65
66TestResult::List TestCase::execute(const ExecutionStage stage,
67 TestSuite *)
68{
69 ++TestCase::executions;
70
71 if ((TestCase::executions < TestCase::executeRange.first) || (TestCase::executions > TestCase::executeRange.second)) {
72 qDebug(msg: "Skipping test case #%6d", TestCase::executions);
73 return TestResult::List();
74 }
75
76 const QByteArray nm = name().toLatin1();
77
78 if(name() == QLatin1String("Constr-cont-document-3"))
79 {
80 TestResult::List result;
81 result.append(t: createTestResult(status: TestResult::Fail, comment: QLatin1String("Skipped this test, because we loop infinitely on it.")));
82 return result;
83 }
84 else if(name() == QLatin1String("Axes089"))
85 {
86 TestResult::List result;
87 result.append(t: createTestResult(status: TestResult::Fail, comment: QLatin1String("Skipped this test, we crash on it.")));
88 return result;
89 }
90 else if (name() == QLatin1String("op-numeric-unary-minus-1"))
91 {
92 TestResult::List result;
93 result.append(t: createTestResult(status: TestResult::Fail, comment: QLatin1String("Skipped this test, we crash on it.")));
94 return result;
95 }
96 else if (name() == QLatin1String("emptyorderdecl-13"))
97 {
98 TestResult::List result;
99 result.append(t: createTestResult(status: TestResult::Fail, comment: QLatin1String("Skipped this test, we crash on it.")));
100 return result;
101 }
102 else if (name() == QLatin1String("emptyorderdecl-21"))
103 {
104 TestResult::List result;
105 result.append(t: createTestResult(status: TestResult::Fail, comment: QLatin1String("Skipped this test, we crash on it.")));
106 return result;
107 }
108 else {
109 // Should be sorted in the order that std::binary_search expects
110 static const char *crashes[] = {"Constr-attr-content-4",
111 "K2-DirectConElem-12",
112 "K2-DirectConElem-50",
113 "K2-DirectConElemAttr-10",
114 "K2-DirectConElemAttr-18",
115 "K2-DirectConElemAttr-19",
116 "K2-DirectConElemAttr-20",
117 "K2-DirectConElemAttr-21"
118 };
119
120 const bool skip = std::binary_search(first: &crashes[0], last: &crashes[sizeof(crashes)/sizeof(crashes[0])], val: nm.constData(), comp: lessThan);
121 if (skip) {
122 TestResult::List result;
123 result.append(t: createTestResult(status: TestResult::Fail, comment: QLatin1String("Skipped this test, we crash on it.")));
124 return result;
125 }
126 }
127
128
129 qDebug(msg: "Running test case #%6d: %s", TestCase::executions, nm.constData());
130 return execute(stage);
131
132 Q_ASSERT(false);
133 return TestResult::List();
134}
135
136TestResult *TestCase::createTestResult(const TestResult::Status status,
137 const QString &comment) const
138{
139 TestResult *const result = new TestResult(name(),
140 status,
141 0 /* We don't have an AST. */,
142 ErrorHandler::Message::List(),
143 QPatternist::Item::List(),
144 QString());
145 result->setComment(comment);
146 return result;
147}
148
149TestResult::List TestCase::execute(const ExecutionStage stage)
150{
151 ErrorHandler errHandler;
152 ErrorHandler::installQtMessageHandler(handler: &errHandler);
153
154 pDebug() << "TestCase::execute()";
155 delete m_result;
156
157 QXmlQuery query(language(), Global::namePoolAsPublic());
158
159 query.d->setExpressionFactory(s_exprFact);
160 query.setInitialTemplateName(initialTemplateName());
161
162 QXmlQuery openDoc(query.namePool());
163
164 if(contextItemSource().isValid())
165 {
166 openDoc.setQuery(sourceCode: QString::fromLatin1(str: "doc('") + contextItemSource().toString() + QLatin1String("')"));
167 Q_ASSERT(openDoc.isValid());
168 QXmlResultItems result;
169
170 openDoc.evaluateTo(result: &result);
171 const QXmlItem item(result.next());
172 Q_ASSERT(!item.isNull());
173 query.setFocus(item);
174 }
175
176 TestResult::List retval;
177
178 const Scenario scen(scenario());
179 TestResult::Status resultStatus = TestResult::Unknown;
180
181 bool ok = false;
182 const QString queryString(sourceCode(ok));
183
184 if(!ok)
185 {
186 /* Loading the query file failed, or similar. */
187 resultStatus = TestResult::Fail;
188
189 m_result = new TestResult(name(), resultStatus, s_exprFact->astTree(),
190 errHandler.messages(), QPatternist::Item::List(), QString());
191 retval.append(t: m_result);
192 ErrorHandler::installQtMessageHandler(handler: 0);
193 changed(item: this);
194 return retval;
195 }
196
197 query.setMessageHandler(&errHandler);
198 QXmlNamePool namePool(query.namePool());
199
200 /* Bind variables. */
201 QPatternist::ExternalVariableLoader::Ptr loader(externalVariableLoader());
202 if(loader)
203 {
204 Q_ASSERT(loader);
205 const ExternalSourceLoader::VariableMap vMap(static_cast<const ExternalSourceLoader *>(loader.data())->variableMap());
206 const QStringList variables(vMap.keys());
207
208 for(int i = 0; i < variables.count(); ++i)
209 {
210 const QXmlName name(namePool, variables.at(i));
211 const QXmlItem val(QPatternist::Item::toPublic(i: loader->evaluateSingleton(name, context: QPatternist::DynamicContext::Ptr())));
212 query.bindVariable(name, value: val);
213 }
214 }
215
216 /* We pass in the testCasePath(), such that the base URI is correct fort
217 * XSL-T stylesheets. */
218 query.setQuery(sourceCode: queryString, documentURI: testCasePath());
219
220 if(!query.isValid())
221 {
222 pDebug() << "Got compilation exception.";
223 resultStatus = TestBaseLine::scanErrors(errors: errHandler.messages(), lines: baseLines());
224
225 Q_ASSERT(resultStatus != TestResult::Unknown);
226 m_result = new TestResult(name(), resultStatus, s_exprFact->astTree(),
227 errHandler.messages(), QPatternist::Item::List(), QString());
228 retval.append(t: m_result);
229 ErrorHandler::installQtMessageHandler(handler: 0);
230 changed(item: this);
231 return retval;
232 }
233
234 if(stage == CompileOnly)
235 {
236 m_result = new TestResult(name(), TestResult::Fail, s_exprFact->astTree(),
237 errHandler.messages(), QPatternist::Item::List(), QString());
238 retval.append(t: m_result);
239 return retval;
240 }
241
242 Q_ASSERT(stage == CompileAndRun);
243
244 if(scen == ParseError) /* We're supposed to have received an error
245 at this point. */
246 {
247 m_result = new TestResult(name(), TestResult::Fail, s_exprFact->astTree(),
248 errHandler.messages(), QPatternist::Item::List(), QString());
249 ErrorHandler::installQtMessageHandler(handler: 0);
250 retval.append(t: m_result);
251 changed(item: this);
252 return retval;
253 }
254
255 QPatternist::Item::List itemList;
256
257 QByteArray output;
258 QBuffer buffer(&output);
259 buffer.open(openMode: QIODevice::WriteOnly);
260
261 QXmlSerializer serializer(query, &buffer);
262
263 pDebug() << "-------------------------- evaluateToPushCallback() ---------------------------- ";
264 const bool success = query.evaluateTo(callback: &serializer);
265 pDebug() << "------------------------------------------------------------------------------------ ";
266
267 buffer.close();
268
269 const QString serialized(QString::fromUtf8(str: output.constData(), size: output.size()));
270
271 if(!success)
272 {
273 resultStatus = TestBaseLine::scanErrors(errors: errHandler.messages(), lines: baseLines());
274
275 Q_ASSERT(resultStatus != TestResult::Unknown);
276 m_result = new TestResult(name(), resultStatus, s_exprFact->astTree(),
277 errHandler.messages(), QPatternist::Item::List(), serialized);
278 retval.append(t: m_result);
279 ErrorHandler::installQtMessageHandler(handler: 0);
280 changed(item: this);
281 return retval;
282 }
283
284 /* It's a regular test. */
285 Q_ASSERT(scen == Standard || scen == RuntimeError);
286
287 resultStatus = TestBaseLine::scan(serialized, lines: baseLines());
288 Q_ASSERT(resultStatus != TestResult::Unknown);
289
290 /* Check that errHandler()->messages() at most only contains
291 * warnings, since it shouldn't have errors at this point. */
292 const ErrorHandler::Message::List errors (errHandler.messages());
293 const ErrorHandler::Message::List::const_iterator end(errors.constEnd());
294 ErrorHandler::Message::List::const_iterator it(errors.constBegin());
295
296 for(; it != end; ++it)
297 {
298 const QtMsgType type = (*it).type();
299 if(type == QtFatalMsg)
300 {
301 m_result = new TestResult(name(), TestResult::Fail, s_exprFact->astTree(),
302 errHandler.messages(), itemList, serialized);
303 retval.append(t: m_result);
304 ErrorHandler::installQtMessageHandler(handler: 0);
305 changed(item: this);
306 return retval;
307 }
308 }
309
310 m_result = new TestResult(name(), resultStatus, s_exprFact->astTree(),
311 errHandler.messages(), itemList, serialized);
312 retval.append(t: m_result);
313 ErrorHandler::installQtMessageHandler(handler: 0);
314 changed(item: this);
315 return retval;
316}
317
318TestCase::Scenario TestCase::scenarioFromString(const QString &string)
319{
320 if(string == QLatin1String("standard"))
321 return Standard;
322 else if(string == QLatin1String("parse-error"))
323 return ParseError;
324 else if(string == QLatin1String("runtime-error"))
325 return RuntimeError;
326 else if(string == QLatin1String("trivial"))
327 return Trivial;
328 else
329 {
330 Q_ASSERT_X(false, Q_FUNC_INFO,
331 qPrintable(QString::fromLatin1("Invalid string representation for the scenario-enum: %1").arg(string)));
332 return ParseError; /* Silence GCC. */
333 }
334}
335
336void TestCase::toXML(XMLWriter &receiver) const
337{
338 /* <test-case> */
339 QXmlStreamAttributes test_caseAtts;
340 test_caseAtts.append(qualifiedName: QLatin1String("is-XPath2"), value: isXPath() ? QLatin1String("true")
341 : QLatin1String("false"));
342 test_caseAtts.append(qualifiedName: QLatin1String("name"), value: name());
343 test_caseAtts.append(qualifiedName: QLatin1String("creator"), value: creator());
344 QString scen;
345 switch(scenario())
346 {
347 case Standard:
348 {
349 scen = QLatin1String("standard");
350 break;
351 }
352 case ParseError:
353 {
354 scen = QLatin1String("parse-error");
355 break;
356 }
357 case RuntimeError:
358 {
359 scen = QLatin1String("runtime-error");
360 break;
361 }
362 case Trivial:
363 {
364 scen = QLatin1String("trivial");
365 break;
366 }
367 default: /* includes 'AnyError' */
368 Q_ASSERT(false);
369 }
370 test_caseAtts.append(qualifiedName: QLatin1String("scenario"), value: scen);
371 test_caseAtts.append(qualifiedName: QLatin1String("FilePath"), value: QString());
372 receiver.startElement(qName: QLatin1String("test-case"), atts: test_caseAtts);
373
374 /* <description> */
375 receiver.startElement(qName: QLatin1String("description"), atts: test_caseAtts);
376 receiver.characters(ch: description());
377
378 /* </description> */
379 receiver.endElement(qName: QLatin1String("description"));
380
381 /* <query> */
382 QXmlStreamAttributes queryAtts;
383 queryAtts.append(qualifiedName: QLatin1String("date"), /* This date is a dummy. */
384 value: QDate::currentDate().toString(format: Qt::ISODate));
385 queryAtts.append(qualifiedName: QLatin1String("name"), value: testCasePath().toString());
386 receiver.startElement(qName: QLatin1String("query"), atts: queryAtts);
387
388 /* </query> */
389 receiver.endElement(qName: QLatin1String("query"));
390
391 /* Note: this is invalid, we don't add spec-citation. */
392 TestBaseLine::List bls(baseLines());
393 const TestBaseLine::List::const_iterator end(bls.constEnd());
394 TestBaseLine::List::const_iterator it(bls.constBegin());
395
396 for(; it != end; ++it)
397 (*it)->toXML(receiver);
398
399 /* </test-case> */
400 receiver.endElement(qName: QLatin1String("test-case"));
401}
402
403QString TestCase::displayName(const Scenario scen)
404{
405 switch(scen)
406 {
407 case Standard:
408 return QLatin1String("Standard");
409 case ParseError:
410 return QLatin1String("Parse Error");
411 case RuntimeError:
412 return QLatin1String("Runtime Error");
413 case Trivial:
414 return QLatin1String("Trivial");
415 case AnyError:
416 {
417 Q_ASSERT(false);
418 return QString();
419 }
420 }
421
422 Q_ASSERT(false);
423 return QString();
424}
425
426TestItem::ResultSummary TestCase::resultSummary() const
427{
428 if(m_result)
429 return ResultSummary(m_result->status() == TestResult::Pass ? 1 : 0,
430 1);
431
432 return ResultSummary(0, 1);
433}
434
435void TestCase::appendChild(TreeItem *)
436{
437 Q_ASSERT_X(false, Q_FUNC_INFO, "Makes no sense to call appendChild() for TestCase.");
438}
439
440TreeItem *TestCase::child(const unsigned int) const
441{
442 return 0; /* Silence GCC */
443}
444
445TreeItem::List TestCase::children() const
446{
447 return TreeItem::List();
448}
449
450unsigned int TestCase::childCount() const
451{
452 return 0;
453}
454
455TestResult *TestCase::testResult() const
456{
457 return m_result;
458}
459
460bool TestCase::isFinalNode() const
461{
462 return true;
463}
464
465QXmlQuery::QueryLanguage TestCase::language() const
466{
467 return QXmlQuery::XQuery10;
468}
469
470QXmlName TestCase::initialTemplateName() const
471{
472 return QXmlName();
473}
474
475// vim: et:ts=4:sw=4:sts=4
476
477

source code of qtxmlpatterns/tests/auto/xmlpatternssdk/TestCase.cpp