|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 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 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 |
|
43 #include <QtTest/QtTest> |
|
44 |
|
45 #ifdef QTEST_XMLPATTERNS |
|
46 |
|
47 #include <QAbstractMessageHandler> |
|
48 #include <QFileInfo> |
|
49 #include <QNetworkReply> |
|
50 #include <QtNetwork/QTcpServer> |
|
51 #include <QtNetwork/QTcpSocket> |
|
52 #include <QXmlFormatter> |
|
53 #include <QXmlItem> |
|
54 #include <QXmlName> |
|
55 #include <QXmlQuery> |
|
56 #include <QXmlResultItems> |
|
57 #include <QXmlSerializer> |
|
58 |
|
59 #include "MessageSilencer.h" |
|
60 #include "MessageValidator.h" |
|
61 #include "NetworkOverrider.h" |
|
62 #include "PushBaseliner.h" |
|
63 #include "../qabstracturiresolver/TestURIResolver.h" |
|
64 #include "../qsimplexmlnodemodel/TestSimpleNodeModel.h" |
|
65 #include "TestFundament.h" |
|
66 #include "../network-settings.h" |
|
67 |
|
68 #if defined(Q_OS_SYMBIAN) |
|
69 #define SRCDIR "" |
|
70 #endif |
|
71 |
|
72 /*! |
|
73 \class tst_QXmlQuery |
|
74 \internal |
|
75 \since 4.4 |
|
76 \brief Tests class QXmlQuery. |
|
77 |
|
78 This test is not intended for testing the engine, but the functionality specific |
|
79 to the QXmlQuery class. |
|
80 |
|
81 In other words, if you have an engine bug; don't add it here because it won't be |
|
82 tested properly. Instead add it to the test suite. |
|
83 |
|
84 */ |
|
85 class tst_QXmlQuery : public QObject |
|
86 , private TestFundament |
|
87 { |
|
88 Q_OBJECT |
|
89 |
|
90 public: |
|
91 inline tst_QXmlQuery() : m_generatedBaselines(0) |
|
92 , m_pushTestsCount(0) |
|
93 , m_testNetwork(true) |
|
94 { |
|
95 Q_SET_DEFAULT_IAP |
|
96 } |
|
97 |
|
98 private Q_SLOTS: |
|
99 void defaultConstructor() const; |
|
100 void copyConstructor() const; |
|
101 void constructorQXmlNamePool() const; |
|
102 void constructorQXmlNamePoolQueryLanguage() const; |
|
103 void constructorQXmlNamePoolWithinQSimpleXmlNodeModel() const; |
|
104 void assignmentOperator() const; |
|
105 void isValid() const; |
|
106 void sequentialExecution() const; |
|
107 void bindVariableQString() const; |
|
108 void bindVariableQStringNoExternalDeclaration() const; |
|
109 void bindVariableQXmlName() const; |
|
110 void bindVariableQXmlNameTriggerWarnings() const; |
|
111 void bindVariableQStringQIODevice() const; |
|
112 void bindVariableQStringQIODeviceWithByteArray() const; |
|
113 void bindVariableQStringQIODeviceWithString() const; |
|
114 void bindVariableQStringQIODeviceWithQFile() const; |
|
115 void bindVariableQXmlNameQIODevice() const; |
|
116 void bindVariableQXmlNameQIODeviceTriggerWarnings() const; |
|
117 void bindVariableXSLTSuccess() const; |
|
118 void bindVariableTemporaryNode() const; |
|
119 void setMessageHandler() const; |
|
120 void messageHandler() const; |
|
121 void evaluateToQAbstractXmlReceiverTriggerWarnings() const; |
|
122 void evaluateToQXmlResultItems() const; |
|
123 void evaluateToQXmlResultItemsTriggerWarnings() const; |
|
124 void evaluateToQXmlResultItemsErrorAtEnd() const; |
|
125 void evaluateToReceiver(); |
|
126 void evaluateToReceiver_data() const; |
|
127 void evaluateToReceiverOnInvalidQuery() const; |
|
128 void evaluateToQStringTriggerError() const; |
|
129 void evaluateToQString() const; |
|
130 void evaluateToQString_data() const; |
|
131 void evaluateToQStringSignature() const; |
|
132 void checkGeneratedBaselines() const; |
|
133 void basicXQueryToQtTypeCheck() const; |
|
134 void basicQtToXQueryTypeCheck() const; |
|
135 void bindNode() const; |
|
136 void relativeBaseURI() const; |
|
137 void emptyBaseURI() const; |
|
138 void roundTripDateWithinQXmlItem() const; |
|
139 void bindingMissing() const; |
|
140 void bindDefaultConstructedItem() const; |
|
141 void bindDefaultConstructedItem_data() const; |
|
142 void bindEmptyNullString() const; |
|
143 void bindEmptyString() const; |
|
144 void rebindVariableSameType() const; |
|
145 void rebindVariableDifferentType() const; |
|
146 void rebindVariableWithNullItem() const; |
|
147 void eraseQXmlItemBinding() const; |
|
148 void eraseDeviceBinding() const; |
|
149 void constCorrectness() const; |
|
150 void objectSize() const; |
|
151 void setUriResolver() const; |
|
152 void uriResolver() const; |
|
153 void messageXML() const; |
|
154 void resultItemsDeallocatedQuery() const; |
|
155 void copyCheckMessageHandler() const; |
|
156 void shadowedVariables() const; |
|
157 void setFocusQXmlItem() const; |
|
158 void setFocusQUrl() const; |
|
159 void setFocusQIODevice() const; |
|
160 void setFocusQIODeviceAvoidVariableClash() const; |
|
161 void setFocusQIODeviceFailure() const; |
|
162 void setFocusQIODeviceTriggerWarnings() const; |
|
163 void setFocusQString() const; |
|
164 void setFocusQStringFailure() const; |
|
165 void setFocusQStringSignature() const; |
|
166 void recompilationWithEvaluateToResultFailing() const; |
|
167 void secondEvaluationWithEvaluateToResultFailing() const; |
|
168 void recompilationWithEvaluateToReceiver() const; |
|
169 void fnDocOnQIODeviceTimeout() const; |
|
170 void evaluateToQStringListOnInvalidQuery() const; |
|
171 void evaluateToQStringList() const; |
|
172 void evaluateToQStringListTriggerWarnings() const; |
|
173 void evaluateToQStringList_data() const; |
|
174 void evaluateToQStringListNoConversion() const; |
|
175 void evaluateToQIODevice() const; |
|
176 void evaluateToQIODeviceTriggerWarnings() const; |
|
177 void evaluateToQIODeviceSignature() const; |
|
178 void evaluateToQIODeviceOnInvalidQuery() const; |
|
179 void setQueryQIODeviceQUrl() const; |
|
180 void setQueryQIODeviceQUrlTriggerWarnings() const; |
|
181 void setQueryQString() const; |
|
182 void setQueryQUrlSuccess() const; |
|
183 void setQueryQUrlSuccess_data() const; |
|
184 void setQueryQUrlFailSucceed() const; |
|
185 void setQueryQUrlFailure() const; |
|
186 void setQueryQUrlFailure_data() const; |
|
187 void setQueryQUrlBaseURI() const; |
|
188 void setQueryQUrlBaseURI_data() const; |
|
189 void setQueryWithNonExistentQUrlOnValidQuery() const; |
|
190 void setQueryWithInvalidQueryFromQUrlOnValidQuery() const; |
|
191 void retrieveNameFromQuery() const; |
|
192 void retrieveNameFromQuery_data() const; |
|
193 void cleanupTestCase() const; |
|
194 void declareUnavailableExternal() const; |
|
195 void msvcCacheIssue() const; |
|
196 void unavailableExternalVariable() const; |
|
197 void useUriResolver() const; |
|
198 void queryWithFocusAndVariable() const; |
|
199 void undefinedFocus() const; |
|
200 void basicFocusUsage() const; |
|
201 |
|
202 void queryLanguage() const; |
|
203 void queryLanguageSignature() const; |
|
204 void enumQueryLanguage() const; |
|
205 |
|
206 void setNetworkAccessManager() const; |
|
207 void networkAccessManagerSignature() const; |
|
208 void networkAccessManagerDefaultValue() const; |
|
209 void networkAccessManager() const; |
|
210 |
|
211 void setInitialTemplateNameQXmlName() const; |
|
212 void setInitialTemplateNameQXmlNameSignature() const; |
|
213 void setInitialTemplateNameQString() const; |
|
214 void setInitialTemplateNameQStringSignature() const; |
|
215 void initialTemplateName() const; |
|
216 void initialTemplateNameSignature() const; |
|
217 |
|
218 void fnDocNetworkAccessSuccess() const; |
|
219 void fnDocNetworkAccessSuccess_data() const; |
|
220 void fnDocNetworkAccessFailure() const; |
|
221 void fnDocNetworkAccessFailure_data() const; |
|
222 void multipleDocsAndFocus() const; |
|
223 void multipleEvaluationsWithDifferentFocus() const; |
|
224 void bindVariableQXmlQuery() const; |
|
225 void bindVariableQXmlQuery_data() const; |
|
226 void bindVariableQStringQXmlQuerySignature() const; |
|
227 void bindVariableQXmlNameQXmlQuerySignature() const; |
|
228 void bindVariableQXmlNameQXmlQuery() const; |
|
229 void bindVariableQXmlQueryInvalidate() const; |
|
230 void unknownSourceLocation() const; |
|
231 |
|
232 void identityConstraintSuccess() const; |
|
233 void identityConstraintFailure() const; |
|
234 void identityConstraintFailure_data() const; |
|
235 |
|
236 // TODO call all URI resolving functions where 1) the URI resolver return a null QUrl(); 2) resolves into valid, existing URI, 3) invalid, non-existing URI. |
|
237 // TODO bind stringlists, variant lists, both ways. |
|
238 // TODO trigger serialization error, or any error in evaluateToushCallback(). |
|
239 // TODO let items travle between two queries, as seen in the SDK |
|
240 // TODO what happens if the query declares local variable and external ones are provided? |
|
241 |
|
242 private: |
|
243 enum |
|
244 { |
|
245 /** |
|
246 * One excluded, since we skip static-base-uri.xq. |
|
247 */ |
|
248 ExpectedQueryCount = 29 |
|
249 }; |
|
250 |
|
251 static void checkBaseURI(const QUrl &baseURI, const QString &candidate); |
|
252 static QStringList queries(); |
|
253 static const char *const queriesDirectory; |
|
254 |
|
255 int m_generatedBaselines; |
|
256 int m_pushTestsCount; |
|
257 const bool m_testNetwork; |
|
258 }; |
|
259 |
|
260 void tst_QXmlQuery::checkBaseURI(const QUrl &baseURI, const QString &candidate) |
|
261 { |
|
262 /* The use of QFileInfo::canonicalFilePath() takes into account that drive letters |
|
263 * on Windows may have different cases. */ |
|
264 QVERIFY(QDir(baseURI.toLocalFile()).relativeFilePath(QFileInfo(candidate).canonicalFilePath()).startsWith("../")); |
|
265 } |
|
266 |
|
267 const char *const tst_QXmlQuery::queriesDirectory = SRCDIR "../xmlpatterns/queries/"; |
|
268 |
|
269 QStringList tst_QXmlQuery::queries() |
|
270 { |
|
271 QDir dir; |
|
272 dir.cd(inputFile(QLatin1String(queriesDirectory))); |
|
273 |
|
274 return dir.entryList(QStringList(QLatin1String("*.xq"))); |
|
275 } |
|
276 |
|
277 void tst_QXmlQuery::defaultConstructor() const |
|
278 { |
|
279 /* Allocate instance in different orders. */ |
|
280 { |
|
281 QXmlQuery query; |
|
282 } |
|
283 |
|
284 { |
|
285 QXmlQuery query1; |
|
286 QXmlQuery query2; |
|
287 } |
|
288 |
|
289 { |
|
290 QXmlQuery query1; |
|
291 QXmlQuery query2; |
|
292 QXmlQuery query3; |
|
293 } |
|
294 } |
|
295 |
|
296 void tst_QXmlQuery::copyConstructor() const |
|
297 { |
|
298 /* Verify that we can take a const reference, and simply do a copy of a default constructed object. */ |
|
299 { |
|
300 const QXmlQuery query1; |
|
301 QXmlQuery query2(query1); |
|
302 } |
|
303 |
|
304 /* Copy twice. */ |
|
305 { |
|
306 const QXmlQuery query1; |
|
307 QXmlQuery query2(query1); |
|
308 QXmlQuery query3(query2); |
|
309 } |
|
310 |
|
311 /* Verify that copying default values works. */ |
|
312 { |
|
313 const QXmlQuery query1; |
|
314 const QXmlQuery query2(query1); |
|
315 QCOMPARE(query2.messageHandler(), query1.messageHandler()); |
|
316 QCOMPARE(query2.uriResolver(), query1.uriResolver()); |
|
317 QCOMPARE(query2.queryLanguage(), query1.queryLanguage()); |
|
318 QCOMPARE(query2.initialTemplateName(), query1.initialTemplateName()); |
|
319 QCOMPARE(query2.networkAccessManager(), query1.networkAccessManager()); |
|
320 } |
|
321 |
|
322 /* Check that the |
|
323 * |
|
324 * - name pool |
|
325 * - URI resolver |
|
326 * - message handler |
|
327 * - query language |
|
328 * - initial template name |
|
329 * |
|
330 * sticks with the copy. */ |
|
331 { |
|
332 MessageSilencer silencer; |
|
333 TestURIResolver resolver; |
|
334 QNetworkAccessManager networkManager; |
|
335 QXmlQuery query1(QXmlQuery::XSLT20); |
|
336 QXmlNamePool np1(query1.namePool()); |
|
337 |
|
338 query1.setMessageHandler(&silencer); |
|
339 query1.setUriResolver(&resolver); |
|
340 query1.setNetworkAccessManager(&networkManager); |
|
341 |
|
342 const QXmlName name(np1, QLatin1String("localName"), |
|
343 QLatin1String("http://example.com/"), |
|
344 QLatin1String("prefix")); |
|
345 query1.setInitialTemplateName(name); |
|
346 |
|
347 const QXmlQuery query2(query1); |
|
348 QCOMPARE(query2.messageHandler(), &silencer); |
|
349 QCOMPARE(query2.uriResolver(), &resolver); |
|
350 QCOMPARE(query2.queryLanguage(), QXmlQuery::XSLT20); |
|
351 QCOMPARE(query2.initialTemplateName(), name); |
|
352 QCOMPARE(query2.networkAccessManager(), &networkManager); |
|
353 |
|
354 QXmlNamePool np2(query2.namePool()); |
|
355 |
|
356 QCOMPARE(name.namespaceUri(np2), QString::fromLatin1("http://example.com/")); |
|
357 QCOMPARE(name.localName(np2), QString::fromLatin1("localName")); |
|
358 QCOMPARE(name.prefix(np2), QString::fromLatin1("prefix")); |
|
359 } |
|
360 |
|
361 { |
|
362 QXmlQuery original; |
|
363 |
|
364 original.setFocus(QXmlItem(4)); |
|
365 original.setQuery(QLatin1String(".")); |
|
366 QVERIFY(original.isValid()); |
|
367 |
|
368 const QXmlQuery copy(original); |
|
369 |
|
370 QXmlResultItems result; |
|
371 copy.evaluateTo(&result); |
|
372 QCOMPARE(result.next().toAtomicValue(), QVariant(4)); |
|
373 QVERIFY(result.next().isNull()); |
|
374 QVERIFY(!result.hasError()); |
|
375 } |
|
376 |
|
377 /* Copy, set, compare. Check that copies are independent. */ |
|
378 { |
|
379 // TODO all members except queryLanguage(). |
|
380 } |
|
381 } |
|
382 |
|
383 void tst_QXmlQuery::constructorQXmlNamePool() const |
|
384 { |
|
385 /* Check that the namepool we are passed, is actually used. */ |
|
386 QXmlNamePool np; |
|
387 |
|
388 QXmlQuery query(np); |
|
389 const QXmlName name(np, QLatin1String("localName"), |
|
390 QLatin1String("http://example.com/"), |
|
391 QLatin1String("prefix")); |
|
392 |
|
393 QXmlNamePool np2(query.namePool()); |
|
394 QCOMPARE(name.namespaceUri(np2), QString::fromLatin1("http://example.com/")); |
|
395 QCOMPARE(name.localName(np2), QString::fromLatin1("localName")); |
|
396 QCOMPARE(name.prefix(np2), QString::fromLatin1("prefix")); |
|
397 } |
|
398 |
|
399 /*! |
|
400 Ensure that the internal variable loading mechanisms uses the user-supplied |
|
401 name pool. |
|
402 |
|
403 If that is not the case, different name pools are used and the code crashes. |
|
404 |
|
405 \since 4.5 |
|
406 */ |
|
407 void tst_QXmlQuery::constructorQXmlNamePoolQueryLanguage() const |
|
408 { |
|
409 QXmlNamePool np; |
|
410 QXmlName name(np, QLatin1String("arbitraryName")); |
|
411 |
|
412 QXmlQuery query(QXmlQuery::XQuery10, np); |
|
413 |
|
414 QBuffer input; |
|
415 input.setData("<yall/>"); |
|
416 |
|
417 QVERIFY(input.open(QIODevice::ReadOnly)); |
|
418 query.bindVariable(name, &input); |
|
419 query.setQuery("string(doc($arbitraryName))"); |
|
420 |
|
421 QStringList result; |
|
422 query.evaluateTo(&result); |
|
423 } |
|
424 |
|
425 void tst_QXmlQuery::constructorQXmlNamePoolWithinQSimpleXmlNodeModel() const |
|
426 { |
|
427 class TestCTOR : public TestSimpleNodeModel |
|
428 { |
|
429 public: |
|
430 TestCTOR(const QXmlNamePool &np) : TestSimpleNodeModel(np) |
|
431 { |
|
432 } |
|
433 |
|
434 void checkCTOR() const |
|
435 { |
|
436 /* If this fails to compile, the constructor has trouble with taking |
|
437 * a mutable reference. |
|
438 * |
|
439 * The reason we use the this pointer explicitly, is to avoid a compiler |
|
440 * warnings with MSVC 2005. */ |
|
441 QXmlQuery(this->namePool()); |
|
442 } |
|
443 }; |
|
444 |
|
445 QXmlNamePool np; |
|
446 TestCTOR ctor(np); |
|
447 ctor.checkCTOR(); |
|
448 } |
|
449 |
|
450 void tst_QXmlQuery::assignmentOperator() const |
|
451 { |
|
452 class ReturnURI : public QAbstractUriResolver |
|
453 { |
|
454 public: |
|
455 virtual QUrl resolve(const QUrl &relative, |
|
456 const QUrl &baseURI) const |
|
457 { |
|
458 return baseURI.resolved(relative); |
|
459 } |
|
460 }; |
|
461 |
|
462 /* Assign this to this. */ |
|
463 { |
|
464 QXmlQuery query; |
|
465 query = query; |
|
466 query = query; |
|
467 query = query; |
|
468 |
|
469 /* Just call a couple of functions to give valgrind |
|
470 * something to check. */ |
|
471 QVERIFY(!query.isValid()); |
|
472 query.messageHandler(); |
|
473 } |
|
474 |
|
475 /* Assign null instances a couple of times. */ |
|
476 { |
|
477 QXmlQuery query1; |
|
478 QXmlQuery query2; |
|
479 query1 = query2; |
|
480 query1 = query2; |
|
481 query1 = query2; |
|
482 |
|
483 /* Just call a couple of functions to give valgrind |
|
484 * something to check. */ |
|
485 QVERIFY(!query1.isValid()); |
|
486 query1.messageHandler(); |
|
487 |
|
488 /* Just call a couple of functions to give valgrind |
|
489 * something to check. */ |
|
490 QVERIFY(!query2.isValid()); |
|
491 query2.messageHandler(); |
|
492 } |
|
493 |
|
494 /* Create a query, set all the things it stores, and ensure it |
|
495 * travels over to the new instance. */ |
|
496 { |
|
497 MessageSilencer silencer; |
|
498 const ReturnURI returnURI; |
|
499 QXmlNamePool namePool; |
|
500 |
|
501 QBuffer documentDevice; |
|
502 documentDevice.setData(QByteArray("<e>a</e>")); |
|
503 QVERIFY(documentDevice.open(QIODevice::ReadOnly)); |
|
504 |
|
505 QXmlQuery original(namePool); |
|
506 QXmlName testName(namePool, QLatin1String("somethingToCheck")); |
|
507 |
|
508 original.setMessageHandler(&silencer); |
|
509 original.bindVariable(QLatin1String("var"), QXmlItem(1)); |
|
510 original.bindVariable(QLatin1String("device"), &documentDevice); |
|
511 original.setUriResolver(&returnURI); |
|
512 original.setFocus(QXmlItem(3)); |
|
513 original.setQuery(QLatin1String("$var, 1 + 1, ., string(doc($device))")); |
|
514 |
|
515 /* Do a copy, and check that everything followed on into the copy. No modification |
|
516 * of the copy. */ |
|
517 { |
|
518 QXmlQuery copy; |
|
519 |
|
520 /* We use assignment operator, not copy constructor. */ |
|
521 copy = original; |
|
522 |
|
523 QVERIFY(copy.isValid()); |
|
524 QCOMPARE(copy.uriResolver(), static_cast<const QAbstractUriResolver *>(&returnURI)); |
|
525 QCOMPARE(copy.messageHandler(), &silencer); |
|
526 QCOMPARE(testName.localName(copy.namePool()), QString::fromLatin1("somethingToCheck")); |
|
527 |
|
528 QXmlResultItems result; |
|
529 copy.evaluateTo(&result); |
|
530 QCOMPARE(result.next().toAtomicValue(), QVariant(1)); |
|
531 QCOMPARE(result.next().toAtomicValue(), QVariant(2)); |
|
532 QCOMPARE(result.next().toAtomicValue(), QVariant(3)); |
|
533 QCOMPARE(result.next().toAtomicValue(), QVariant(QString::fromLatin1("a"))); |
|
534 QVERIFY(result.next().isNull()); |
|
535 QVERIFY(!result.hasError()); |
|
536 } |
|
537 |
|
538 /* Copy, and change values. Things should detach. */ |
|
539 { |
|
540 /* Evaluate the copy. */ |
|
541 { |
|
542 MessageSilencer secondSilencer; |
|
543 const ReturnURI secondUriResolver; |
|
544 QBuffer documentDeviceCopy; |
|
545 documentDeviceCopy.setData(QByteArray("<e>b</e>")); |
|
546 QVERIFY(documentDeviceCopy.open(QIODevice::ReadOnly)); |
|
547 |
|
548 QXmlQuery copy; |
|
549 copy = original; |
|
550 |
|
551 copy.setMessageHandler(&secondSilencer); |
|
552 /* Here we rebind variable values. */ |
|
553 copy.bindVariable(QLatin1String("var"), QXmlItem(4)); |
|
554 copy.bindVariable(QLatin1String("device"), &documentDeviceCopy); |
|
555 copy.setUriResolver(&secondUriResolver); |
|
556 copy.setFocus(QXmlItem(6)); |
|
557 copy.setQuery(QLatin1String("$var, 1 + 1, ., string(doc($device))")); |
|
558 |
|
559 /* Check that the copy picked up the new things. */ |
|
560 QVERIFY(copy.isValid()); |
|
561 QCOMPARE(copy.uriResolver(), static_cast<const QAbstractUriResolver *>(&secondUriResolver)); |
|
562 QCOMPARE(copy.messageHandler(), &secondSilencer); |
|
563 |
|
564 QXmlResultItems resultCopy; |
|
565 copy.evaluateTo(&resultCopy); |
|
566 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(4)); |
|
567 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(2)); |
|
568 QCOMPARE(resultCopy.next().toAtomicValue(), QVariant(6)); |
|
569 const QString stringedDevice(resultCopy.next().toAtomicValue().toString()); |
|
570 QCOMPARE(stringedDevice, QString::fromLatin1("b")); |
|
571 QVERIFY(resultCopy.next().isNull()); |
|
572 QVERIFY(!resultCopy.hasError()); |
|
573 } |
|
574 |
|
575 /* Evaluate the original. */ |
|
576 { |
|
577 /* Check that the original is unchanged. */ |
|
578 QVERIFY(original.isValid()); |
|
579 QCOMPARE(original.uriResolver(), static_cast<const QAbstractUriResolver *>(&returnURI)); |
|
580 QCOMPARE(original.messageHandler(), &silencer); |
|
581 |
|
582 QXmlResultItems resultOriginal; |
|
583 original.evaluateTo(&resultOriginal); |
|
584 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(1)); |
|
585 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(2)); |
|
586 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(3)); |
|
587 QCOMPARE(resultOriginal.next().toAtomicValue(), QVariant(QString::fromLatin1("a"))); |
|
588 QVERIFY(resultOriginal.next().isNull()); |
|
589 QVERIFY(!resultOriginal.hasError()); |
|
590 } |
|
591 } |
|
592 } |
|
593 } |
|
594 |
|
595 /*! |
|
596 Since QXmlQuery doesn't seek devices to position 0, this code triggers a bug |
|
597 where document caching doesn't work. Since the document caching doesn't work, |
|
598 the device will be read twice, and the second time the device is at the end, |
|
599 hence premature end of document. |
|
600 */ |
|
601 void tst_QXmlQuery::sequentialExecution() const |
|
602 { |
|
603 QBuffer inBuffer; |
|
604 inBuffer.setData(QByteArray("<input/>")); |
|
605 QVERIFY(inBuffer.open(QIODevice::ReadOnly)); |
|
606 |
|
607 QXmlQuery query; |
|
608 query.bindVariable("inputDocument", &inBuffer); |
|
609 |
|
610 QByteArray outArray; |
|
611 QBuffer outBuffer(&outArray); |
|
612 outBuffer.open(QIODevice::WriteOnly); |
|
613 |
|
614 const QString queryString(QLatin1String("doc($inputDocument)")); |
|
615 query.setQuery(queryString); |
|
616 |
|
617 QXmlFormatter formatter(query, &outBuffer); |
|
618 |
|
619 QVERIFY(query.evaluateTo(&formatter)); |
|
620 |
|
621 /* If this line is removed, the bug isn't triggered. */ |
|
622 query.setQuery(queryString); |
|
623 |
|
624 QVERIFY(query.evaluateTo(&formatter)); |
|
625 } |
|
626 |
|
627 void tst_QXmlQuery::isValid() const |
|
628 { |
|
629 /* Check default value. */ |
|
630 QXmlQuery query; |
|
631 QVERIFY(!query.isValid()); |
|
632 } |
|
633 |
|
634 void tst_QXmlQuery::bindVariableQString() const |
|
635 { |
|
636 { |
|
637 QXmlQuery query; |
|
638 /* Bind with a null QXmlItem. */ |
|
639 query.bindVariable(QLatin1String("name"), QXmlItem()); |
|
640 } |
|
641 |
|
642 { |
|
643 QXmlQuery query; |
|
644 /* Bind with a null QVariant. */ |
|
645 query.bindVariable(QLatin1String("name"), QXmlItem(QVariant())); |
|
646 } |
|
647 |
|
648 { |
|
649 QXmlQuery query; |
|
650 /* Bind with a null QXmlNodeModelIndex. */ |
|
651 query.bindVariable(QLatin1String("name"), QXmlItem(QXmlNodeModelIndex())); |
|
652 } |
|
653 } |
|
654 |
|
655 void tst_QXmlQuery::bindVariableQStringNoExternalDeclaration() const |
|
656 { |
|
657 QXmlQuery query; |
|
658 query.bindVariable(QLatin1String("foo"), QXmlItem(QLatin1String("Variable Value"))); |
|
659 query.setQuery(QLatin1String("$foo")); |
|
660 |
|
661 QVERIFY(query.isValid()); |
|
662 |
|
663 QStringList result; |
|
664 QVERIFY(query.evaluateTo(&result)); |
|
665 |
|
666 QCOMPARE(result, QStringList() << QLatin1String("Variable Value")); |
|
667 } |
|
668 |
|
669 void tst_QXmlQuery::bindVariableQXmlName() const |
|
670 { |
|
671 // TODO |
|
672 } |
|
673 |
|
674 void tst_QXmlQuery::bindVariableQXmlNameTriggerWarnings() const |
|
675 { |
|
676 QXmlQuery query; |
|
677 |
|
678 QTest::ignoreMessage(QtWarningMsg, "The variable name cannot be null."); |
|
679 query.bindVariable(QXmlName(), QVariant()); |
|
680 } |
|
681 |
|
682 void tst_QXmlQuery::bindVariableQStringQIODeviceWithByteArray() const |
|
683 { |
|
684 QXmlQuery query; |
|
685 |
|
686 QByteArray in("<e/>"); |
|
687 QBuffer device(&in); |
|
688 QVERIFY(device.open(QIODevice::ReadOnly)); |
|
689 |
|
690 query.bindVariable("doc", &device); |
|
691 |
|
692 query.setQuery(QLatin1String("declare variable $doc external; $doc")); |
|
693 |
|
694 QVERIFY(query.isValid()); |
|
695 |
|
696 /* Check the URI corresponding to the variable. */ |
|
697 { |
|
698 QXmlResultItems items; |
|
699 query.evaluateTo(&items); |
|
700 |
|
701 QCOMPARE(items.next().toAtomicValue().toString(), QString::fromLatin1("tag:trolltech.com,2007:QtXmlPatterns:QIODeviceVariable:doc")); |
|
702 } |
|
703 |
|
704 /* Now, actually load the document. We use the same QXmlQuery just to stress recompilation a bit. */ |
|
705 { |
|
706 query.setQuery(QLatin1String("declare variable $doc external; doc($doc)")); |
|
707 |
|
708 QByteArray out; |
|
709 QBuffer outBuffer(&out); |
|
710 QVERIFY(outBuffer.open(QIODevice::WriteOnly)); |
|
711 |
|
712 QXmlSerializer serializer(query, &outBuffer); |
|
713 |
|
714 QVERIFY(query.evaluateTo(&serializer)); |
|
715 QCOMPARE(out, in); |
|
716 } |
|
717 } |
|
718 |
|
719 void tst_QXmlQuery::bindVariableQStringQIODeviceWithString() const |
|
720 { |
|
721 QXmlQuery query; |
|
722 |
|
723 QString in("<qstring/>"); |
|
724 QByteArray inUtf8(in.toUtf8()); |
|
725 QBuffer inDevice(&inUtf8); |
|
726 |
|
727 QVERIFY(inDevice.open(QIODevice::ReadOnly)); |
|
728 |
|
729 query.bindVariable("doc", &inDevice); |
|
730 |
|
731 query.setQuery(QLatin1String("declare variable $doc external; doc($doc)")); |
|
732 |
|
733 QByteArray out; |
|
734 QBuffer outBuffer(&out); |
|
735 QVERIFY(outBuffer.open(QIODevice::WriteOnly)); |
|
736 |
|
737 QXmlSerializer serializer(query, &outBuffer); |
|
738 QVERIFY(query.evaluateTo(&serializer)); |
|
739 |
|
740 QCOMPARE(out, inUtf8); |
|
741 } |
|
742 |
|
743 void tst_QXmlQuery::bindVariableQStringQIODeviceWithQFile() const |
|
744 { |
|
745 QXmlQuery query; |
|
746 QFile inDevice(QLatin1String(SRCDIR "input.xml")); |
|
747 |
|
748 QVERIFY(inDevice.open(QIODevice::ReadOnly)); |
|
749 |
|
750 query.bindVariable("doc", &inDevice); |
|
751 |
|
752 query.setQuery(QLatin1String("declare variable $doc external; doc($doc)")); |
|
753 |
|
754 QByteArray out; |
|
755 QBuffer outBuffer(&out); |
|
756 QVERIFY(outBuffer.open(QIODevice::WriteOnly)); |
|
757 |
|
758 QXmlSerializer serializer(query, &outBuffer); |
|
759 QVERIFY(query.evaluateTo(&serializer)); |
|
760 outBuffer.close(); |
|
761 |
|
762 QCOMPARE(out, QByteArray("<!-- This is just a file for testing. --><input/>")); |
|
763 } |
|
764 |
|
765 void tst_QXmlQuery::bindVariableQStringQIODevice() const |
|
766 { |
|
767 QXmlQuery query; |
|
768 |
|
769 /* Rebind the variable. */ |
|
770 { |
|
771 /* First evaluation. */ |
|
772 { |
|
773 QByteArray in1("<e1/>"); |
|
774 QBuffer inDevice1(&in1); |
|
775 QVERIFY(inDevice1.open(QIODevice::ReadOnly)); |
|
776 |
|
777 query.bindVariable("in", &inDevice1); |
|
778 query.setQuery(QLatin1String("doc($in)")); |
|
779 |
|
780 QByteArray out1; |
|
781 QBuffer outDevice1(&out1); |
|
782 QVERIFY(outDevice1.open(QIODevice::WriteOnly)); |
|
783 |
|
784 QXmlSerializer serializer(query, &outDevice1); |
|
785 query.evaluateTo(&serializer); |
|
786 QCOMPARE(out1, in1); |
|
787 } |
|
788 |
|
789 /* Second evaluation, rebind variable. */ |
|
790 { |
|
791 QByteArray in2("<e2/>"); |
|
792 QBuffer inDevice2(&in2); |
|
793 QVERIFY(inDevice2.open(QIODevice::ReadOnly)); |
|
794 |
|
795 query.bindVariable(QLatin1String("in"), &inDevice2); |
|
796 |
|
797 QByteArray out2; |
|
798 QBuffer outDevice2(&out2); |
|
799 QVERIFY(outDevice2.open(QIODevice::WriteOnly)); |
|
800 |
|
801 QXmlSerializer serializer(query, &outDevice2); |
|
802 QVERIFY(query.evaluateTo(&serializer)); |
|
803 QCOMPARE(out2, in2); |
|
804 } |
|
805 } |
|
806 |
|
807 // TODO trigger recompilation when setting qiodevices., and qiodevice overwritten by other type, etc. |
|
808 } |
|
809 |
|
810 void tst_QXmlQuery::bindVariableQXmlNameQIODevice() const |
|
811 { |
|
812 // TODO |
|
813 } |
|
814 |
|
815 void tst_QXmlQuery::bindVariableQXmlNameQIODeviceTriggerWarnings() const |
|
816 { |
|
817 QXmlNamePool np; |
|
818 QXmlQuery query(np); |
|
819 |
|
820 QBuffer buffer; |
|
821 QTest::ignoreMessage(QtWarningMsg, "A null, or readable QIODevice must be passed."); |
|
822 query.bindVariable(QXmlName(np, QLatin1String("foo")), &buffer); |
|
823 |
|
824 QTest::ignoreMessage(QtWarningMsg, "The variable name cannot be null."); |
|
825 query.bindVariable(QXmlName(), 0); |
|
826 } |
|
827 |
|
828 void tst_QXmlQuery::bindVariableXSLTSuccess() const |
|
829 { |
|
830 QXmlQuery stylesheet(QXmlQuery::XSLT20); |
|
831 stylesheet.setInitialTemplateName(QLatin1String("main")); |
|
832 |
|
833 stylesheet.bindVariable(QLatin1String("variableNoSelectNoBodyBoundWithBindVariable"), |
|
834 QVariant(QLatin1String("MUST NOT SHOW 1"))); |
|
835 |
|
836 stylesheet.bindVariable(QLatin1String("variableSelectBoundWithBindVariable"), |
|
837 QVariant(QLatin1String("MUST NOT SHOW 2"))); |
|
838 |
|
839 stylesheet.bindVariable(QLatin1String("variableSelectWithTypeIntBoundWithBindVariable"), |
|
840 QVariant(QLatin1String("MUST NOT SHOW 3"))); |
|
841 |
|
842 stylesheet.bindVariable(QLatin1String("paramNoSelectNoBodyBoundWithBindVariable"), |
|
843 QVariant(QLatin1String("param1"))); |
|
844 |
|
845 stylesheet.bindVariable(QLatin1String("paramNoSelectNoBodyBoundWithBindVariableRequired"), |
|
846 QVariant(QLatin1String("param1"))); |
|
847 |
|
848 stylesheet.bindVariable(QLatin1String("paramSelectBoundWithBindVariable"), |
|
849 QVariant(QLatin1String("param2"))); |
|
850 |
|
851 stylesheet.bindVariable(QLatin1String("paramSelectBoundWithBindVariableRequired"), |
|
852 QVariant(QLatin1String("param3"))); |
|
853 |
|
854 stylesheet.bindVariable(QLatin1String("paramSelectWithTypeIntBoundWithBindVariable"), |
|
855 QVariant(4)); |
|
856 |
|
857 stylesheet.bindVariable(QLatin1String("paramSelectWithTypeIntBoundWithBindVariableRequired"), |
|
858 QVariant(QLatin1String("param5"))); |
|
859 |
|
860 stylesheet.setQuery(QUrl(inputFile(QLatin1String(SRCDIR "../xmlpatterns/stylesheets/parameters.xsl")))); |
|
861 |
|
862 QVERIFY(stylesheet.isValid()); |
|
863 |
|
864 QBuffer deviceOut; |
|
865 QVERIFY(deviceOut.open(QIODevice::ReadWrite)); |
|
866 |
|
867 QVERIFY(stylesheet.evaluateTo(&deviceOut)); |
|
868 |
|
869 const QString result(QString::fromUtf8(deviceOut.data().constData())); |
|
870 |
|
871 QCOMPARE(result, |
|
872 QString::fromLatin1("Variables: variableSelectsDefaultValue variableSelectsDefaultValue2 3 4 " |
|
873 "Parameters: param1 param1 param2 param3 4 param5")); |
|
874 } |
|
875 |
|
876 void tst_QXmlQuery::bindVariableTemporaryNode() const |
|
877 { |
|
878 /* First we do it with QXmlResultItems staying in scope. */; |
|
879 { |
|
880 QXmlQuery query1; |
|
881 query1.setQuery("<anElement/>"); |
|
882 |
|
883 QXmlResultItems result1; |
|
884 query1.evaluateTo(&result1); |
|
885 |
|
886 QXmlQuery query2(query1); |
|
887 query2.bindVariable("fromQuery1", result1.next()); |
|
888 query2.setQuery("$fromQuery1"); |
|
889 |
|
890 QString output; |
|
891 QVERIFY(query2.evaluateTo(&output)); |
|
892 |
|
893 QCOMPARE(output, QString::fromLatin1("<anElement/>\n")); |
|
894 } |
|
895 |
|
896 /* And now with it deallocating, so its internal DynamicContext pointer is |
|
897 * released. This doesn't work in Qt 4.5 and is ok. */ |
|
898 { |
|
899 QXmlQuery query1; |
|
900 query1.setQuery("<anElement/>"); |
|
901 |
|
902 QXmlQuery query2; |
|
903 |
|
904 { |
|
905 QXmlResultItems result1; |
|
906 query1.evaluateTo(&result1); |
|
907 |
|
908 query2.bindVariable("fromQuery1", result1.next()); |
|
909 query2.setQuery("$fromQuery1"); |
|
910 } |
|
911 |
|
912 QString output; |
|
913 return; // See comment above. |
|
914 QVERIFY(query2.evaluateTo(&output)); |
|
915 |
|
916 QCOMPARE(output, QString::fromLatin1("<anElement/>\n")); |
|
917 } |
|
918 } |
|
919 |
|
920 void tst_QXmlQuery::messageHandler() const |
|
921 { |
|
922 { |
|
923 /* Check default value. */ |
|
924 QXmlQuery query; |
|
925 QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0)); |
|
926 } |
|
927 } |
|
928 |
|
929 void tst_QXmlQuery::setMessageHandler() const |
|
930 { |
|
931 QXmlQuery query; |
|
932 MessageSilencer silencer; |
|
933 query.setMessageHandler(&silencer); |
|
934 QCOMPARE(&silencer, query.messageHandler()); |
|
935 } |
|
936 |
|
937 void tst_QXmlQuery::evaluateToReceiver() |
|
938 { |
|
939 QFETCH(QString, inputQuery); |
|
940 |
|
941 /* This query prints a URI specific to the local system. */ |
|
942 if(inputQuery == QLatin1String("static-base-uri.xq")) |
|
943 return; |
|
944 |
|
945 ++m_pushTestsCount; |
|
946 const QString queryURI(inputFile(QLatin1String(queriesDirectory) + inputQuery)); |
|
947 QFile queryFile(queryURI); |
|
948 |
|
949 QVERIFY(queryFile.exists()); |
|
950 QVERIFY(queryFile.open(QIODevice::ReadOnly)); |
|
951 |
|
952 QXmlQuery query; |
|
953 |
|
954 MessageSilencer receiver; |
|
955 query.setMessageHandler(&receiver); |
|
956 query.setQuery(&queryFile, QUrl::fromLocalFile(queryURI)); |
|
957 |
|
958 /* We read all the queries, and some of them are invalid. However, we |
|
959 * only want those that compile. */ |
|
960 if(!query.isValid()) |
|
961 return; |
|
962 |
|
963 QString produced; |
|
964 QTextStream stream(&produced, QIODevice::WriteOnly); |
|
965 PushBaseliner push(stream, query.namePool()); |
|
966 query.evaluateTo(&push); |
|
967 |
|
968 const QString baselineName(inputFile(QLatin1String(SRCDIR "pushBaselines/") + inputQuery.left(inputQuery.length() - 2) + QString::fromLatin1("ref"))); |
|
969 QFile baseline(baselineName); |
|
970 |
|
971 if(baseline.exists()) |
|
972 { |
|
973 QVERIFY(baseline.open(QIODevice::ReadOnly | QIODevice::Text)); |
|
974 const QString stringedBaseline(QString::fromUtf8(baseline.readAll())); |
|
975 QCOMPARE(produced, stringedBaseline); |
|
976 } |
|
977 else |
|
978 { |
|
979 QVERIFY(baseline.open(QIODevice::WriteOnly)); |
|
980 /* This is intentionally a warning, don't remove it. Update the baselines instead. */ |
|
981 qWarning() << "Generated baseline for:" << baselineName; |
|
982 ++m_generatedBaselines; |
|
983 |
|
984 baseline.write(produced.toUtf8()); |
|
985 } |
|
986 } |
|
987 |
|
988 void tst_QXmlQuery::evaluateToReceiver_data() const |
|
989 { |
|
990 QTest::addColumn<QString>("inputQuery"); |
|
991 |
|
992 const QStringList qs(queries()); |
|
993 |
|
994 for(int i = 0; i < qs.size(); ++i) |
|
995 { |
|
996 /* This outputs a URI specific to the environment, so we can't use it for this |
|
997 * particular test. */ |
|
998 if(qs.at(i) != QLatin1String("staticBaseURI.xq")) |
|
999 QTest::newRow(qs.at(i).toUtf8().constData()) << qs.at(i); |
|
1000 } |
|
1001 } |
|
1002 |
|
1003 void tst_QXmlQuery::evaluateToReceiverOnInvalidQuery() const |
|
1004 { |
|
1005 /* Invoke on a default constructed object. */ |
|
1006 { |
|
1007 QByteArray out; |
|
1008 QBuffer buffer(&out); |
|
1009 buffer.open(QIODevice::WriteOnly); |
|
1010 |
|
1011 QXmlQuery query; |
|
1012 QXmlSerializer serializer(query, &buffer); |
|
1013 QVERIFY(!query.evaluateTo(&serializer)); |
|
1014 } |
|
1015 |
|
1016 /* Invoke on an invalid query; compile time error. */ |
|
1017 { |
|
1018 QByteArray out; |
|
1019 QBuffer buffer(&out); |
|
1020 buffer.open(QIODevice::WriteOnly); |
|
1021 MessageSilencer silencer; |
|
1022 |
|
1023 QXmlQuery query; |
|
1024 query.setMessageHandler(&silencer); |
|
1025 query.setQuery(QLatin1String("1 + ")); |
|
1026 QXmlSerializer serializer(query, &buffer); |
|
1027 QVERIFY(!query.evaluateTo(&serializer)); |
|
1028 } |
|
1029 |
|
1030 /* Invoke on an invalid query; runtime error. */ |
|
1031 { |
|
1032 QByteArray out; |
|
1033 QBuffer buffer(&out); |
|
1034 buffer.open(QIODevice::WriteOnly); |
|
1035 MessageSilencer silencer; |
|
1036 |
|
1037 QXmlQuery query; |
|
1038 query.setMessageHandler(&silencer); |
|
1039 query.setQuery(QLatin1String("error()")); |
|
1040 QXmlSerializer serializer(query, &buffer); |
|
1041 QVERIFY(!query.evaluateTo(&serializer)); |
|
1042 } |
|
1043 } |
|
1044 |
|
1045 void tst_QXmlQuery::evaluateToQStringTriggerError() const |
|
1046 { |
|
1047 /* Invoke on a default constructed object. */ |
|
1048 { |
|
1049 QXmlQuery query; |
|
1050 QString out; |
|
1051 QVERIFY(!query.evaluateTo(&out)); |
|
1052 } |
|
1053 |
|
1054 /* Invoke on an invalid query; compile time error. */ |
|
1055 { |
|
1056 QXmlQuery query; |
|
1057 MessageSilencer silencer; |
|
1058 query.setMessageHandler(&silencer); |
|
1059 |
|
1060 query.setQuery(QLatin1String("1 + ")); |
|
1061 |
|
1062 QString out; |
|
1063 QVERIFY(!query.evaluateTo(&out)); |
|
1064 } |
|
1065 |
|
1066 /* Invoke on an invalid query; runtime error. */ |
|
1067 { |
|
1068 QXmlQuery query; |
|
1069 MessageSilencer silencer; |
|
1070 query.setMessageHandler(&silencer); |
|
1071 |
|
1072 query.setQuery(QLatin1String("error()")); |
|
1073 |
|
1074 QString out; |
|
1075 QVERIFY(!query.evaluateTo(&out)); |
|
1076 } |
|
1077 } |
|
1078 |
|
1079 void tst_QXmlQuery::evaluateToQString() const |
|
1080 { |
|
1081 QFETCH(QString, query); |
|
1082 QFETCH(QString, expectedOutput); |
|
1083 |
|
1084 QXmlQuery queryInstance; |
|
1085 queryInstance.setQuery(query); |
|
1086 QVERIFY(queryInstance.isValid()); |
|
1087 |
|
1088 QString result; |
|
1089 QVERIFY(queryInstance.evaluateTo(&result)); |
|
1090 |
|
1091 QCOMPARE(result, expectedOutput); |
|
1092 } |
|
1093 |
|
1094 void tst_QXmlQuery::evaluateToQString_data() const |
|
1095 { |
|
1096 QTest::addColumn<QString>("query"); |
|
1097 QTest::addColumn<QString>("expectedOutput"); |
|
1098 |
|
1099 QTest::newRow("Two atomics") |
|
1100 << QString::fromLatin1("1, 'two'") |
|
1101 << QString::fromLatin1("1 two\n"); |
|
1102 |
|
1103 QTest::newRow("An element") |
|
1104 << QString::fromLatin1("<e>{1}</e>") |
|
1105 << QString::fromLatin1("<e>1</e>\n"); |
|
1106 } |
|
1107 |
|
1108 void tst_QXmlQuery::evaluateToQStringSignature() const |
|
1109 { |
|
1110 const QXmlQuery query; |
|
1111 |
|
1112 QString output; |
|
1113 |
|
1114 /* evaluateTo(QString *) should be a const function. */ |
|
1115 query.evaluateTo(&output); |
|
1116 } |
|
1117 |
|
1118 void tst_QXmlQuery::evaluateToQAbstractXmlReceiverTriggerWarnings() const |
|
1119 { |
|
1120 QXmlQuery query; |
|
1121 |
|
1122 /* We check the return value as well as warning message here. */ |
|
1123 QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed."); |
|
1124 QCOMPARE(query.evaluateTo(static_cast<QAbstractXmlReceiver *>(0)), |
|
1125 false); |
|
1126 } |
|
1127 |
|
1128 void tst_QXmlQuery::evaluateToQXmlResultItems() const |
|
1129 { |
|
1130 /* Invoke on a default constructed object. */ |
|
1131 { |
|
1132 QXmlQuery query; |
|
1133 QXmlResultItems result; |
|
1134 query.evaluateTo(&result); |
|
1135 QVERIFY(result.next().isNull()); |
|
1136 } |
|
1137 } |
|
1138 |
|
1139 void tst_QXmlQuery::evaluateToQXmlResultItemsTriggerWarnings() const |
|
1140 { |
|
1141 QTest::ignoreMessage(QtWarningMsg, "A null pointer cannot be passed."); |
|
1142 QXmlQuery query; |
|
1143 query.evaluateTo(static_cast<QXmlResultItems *>(0)); |
|
1144 } |
|
1145 |
|
1146 void tst_QXmlQuery::evaluateToQXmlResultItemsErrorAtEnd() const |
|
1147 { |
|
1148 QXmlQuery query; |
|
1149 MessageSilencer silencer; |
|
1150 query.setMessageHandler(&silencer); |
|
1151 query.setQuery(QLatin1String("1 to 100, fn:error()")); |
|
1152 QVERIFY(query.isValid()); |
|
1153 |
|
1154 QXmlResultItems it; |
|
1155 query.evaluateTo(&it); |
|
1156 |
|
1157 while(!it.next().isNull()) |
|
1158 { |
|
1159 } |
|
1160 } |
|
1161 |
|
1162 /*! |
|
1163 If baselines were generated, we flag it as a failure such that it gets |
|
1164 attention, and that they are adjusted accordingly. |
|
1165 */ |
|
1166 void tst_QXmlQuery::checkGeneratedBaselines() const |
|
1167 { |
|
1168 QCOMPARE(m_generatedBaselines, 0); |
|
1169 |
|
1170 /* If this check fails, the auto test setup is misconfigured, or files have |
|
1171 * been added/removed without this number being updated. */ |
|
1172 QCOMPARE(m_pushTestsCount, int(ExpectedQueryCount)); |
|
1173 } |
|
1174 |
|
1175 void tst_QXmlQuery::basicXQueryToQtTypeCheck() const |
|
1176 { |
|
1177 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("allAtomics.xq")); |
|
1178 QVERIFY(queryFile.open(QIODevice::ReadOnly)); |
|
1179 |
|
1180 QXmlQuery query; |
|
1181 query.setQuery(&queryFile); |
|
1182 QVERIFY(query.isValid()); |
|
1183 |
|
1184 QXmlResultItems it; |
|
1185 query.evaluateTo(&it); |
|
1186 |
|
1187 QVariantList expectedValues; |
|
1188 expectedValues.append(QString::fromLatin1("xs:untypedAtomic")); |
|
1189 expectedValues.append(QDateTime(QDate(2002, 10, 10), QTime(23, 2, 11), Qt::UTC)); |
|
1190 expectedValues.append(QDate(2002, 10, 10)); |
|
1191 expectedValues.append(QVariant()); /* We currently doesn't support xs:time through the API. */ |
|
1192 |
|
1193 expectedValues.append(QVariant()); /* xs:duration */ |
|
1194 expectedValues.append(QVariant()); /* xs:dayTimeDuration */ |
|
1195 expectedValues.append(QVariant()); /* xs:yearMonthDuration */ |
|
1196 |
|
1197 expectedValues.append(QVariant(double(3e3))); /* xs:float */ |
|
1198 expectedValues.append(QVariant(double(4e4))); /* xs:double */ |
|
1199 expectedValues.append(QVariant(double(2))); /* xs:decimal */ |
|
1200 |
|
1201 /* xs:integer and its sub-types. */ |
|
1202 expectedValues.append(QVariant(qlonglong(16))); |
|
1203 expectedValues.append(QVariant(qlonglong(-6))); |
|
1204 expectedValues.append(QVariant(qlonglong(-4))); |
|
1205 expectedValues.append(QVariant(qlonglong(5))); |
|
1206 expectedValues.append(QVariant(qlonglong(6))); |
|
1207 expectedValues.append(QVariant(qlonglong(7))); |
|
1208 expectedValues.append(QVariant(qlonglong(8))); |
|
1209 expectedValues.append(QVariant(qlonglong(9))); |
|
1210 expectedValues.append(QVariant(qulonglong(10))); |
|
1211 expectedValues.append(QVariant(qlonglong(11))); |
|
1212 expectedValues.append(QVariant(qlonglong(12))); |
|
1213 expectedValues.append(QVariant(qlonglong(13))); |
|
1214 expectedValues.append(QVariant(qlonglong(14))); |
|
1215 |
|
1216 expectedValues.append(QVariant()); /* xs:gYearMonth("1976-02"), */ |
|
1217 expectedValues.append(QVariant()); /* xs:gYear("2005-12:00"), */ |
|
1218 expectedValues.append(QVariant()); /* xs:gMonthDay("--12-25-14:00"), */ |
|
1219 expectedValues.append(QVariant()); /* xs:gDay("---25-14:00"), */ |
|
1220 expectedValues.append(QVariant()); /* xs:gMonth("--12-14:00"), */ |
|
1221 expectedValues.append(true); /* xs:boolean("true"), */ |
|
1222 expectedValues.append(QVariant(QByteArray::fromBase64(QByteArray("aaaa")))); /* xs:base64Binary("aaaa"), */ |
|
1223 expectedValues.append(QVariant(QByteArray::fromHex(QByteArray("FFFF")))); /* xs:hexBinary("FFFF"), */ |
|
1224 expectedValues.append(QVariant(QString::fromLatin1("http://example.com/"))); /* xs:anyURI("http://example.com/"), */ |
|
1225 QXmlNamePool np(query.namePool()); |
|
1226 expectedValues.append(QVariant(qVariantFromValue(QXmlName(np, QLatin1String("localName"), |
|
1227 QLatin1String("http://example.com/2"), |
|
1228 QLatin1String("prefix"))))); |
|
1229 |
|
1230 expectedValues.append(QVariant(QString::fromLatin1("An xs:string"))); |
|
1231 expectedValues.append(QVariant(QString::fromLatin1("normalizedString"))); |
|
1232 expectedValues.append(QVariant(QString::fromLatin1("token"))); |
|
1233 expectedValues.append(QVariant(QString::fromLatin1("language"))); |
|
1234 expectedValues.append(QVariant(QString::fromLatin1("NMTOKEN"))); |
|
1235 expectedValues.append(QVariant(QString::fromLatin1("Name"))); |
|
1236 expectedValues.append(QVariant(QString::fromLatin1("NCName"))); |
|
1237 expectedValues.append(QVariant(QString::fromLatin1("ID"))); |
|
1238 expectedValues.append(QVariant(QString::fromLatin1("IDREF"))); |
|
1239 expectedValues.append(QVariant(QString::fromLatin1("ENTITY"))); |
|
1240 |
|
1241 int i = 0; |
|
1242 QXmlItem item(it.next()); |
|
1243 |
|
1244 while(!item.isNull()) |
|
1245 { |
|
1246 QVERIFY(item.isAtomicValue()); |
|
1247 const QVariant produced(item.toAtomicValue()); |
|
1248 |
|
1249 const QVariant &expected = expectedValues.at(i); |
|
1250 |
|
1251 /* For the cases where we can't represent a value in the XDM with Qt, |
|
1252 * we return an invalid QVariant. */ |
|
1253 QCOMPARE(expected.isValid(), produced.isValid()); |
|
1254 |
|
1255 QCOMPARE(produced.type(), expected.type()); |
|
1256 |
|
1257 if(expected.isValid()) |
|
1258 { |
|
1259 /* This is only needed for xs:decimal though, for some reason. Probably |
|
1260 * just artifacts created somewhere. */ |
|
1261 if(produced.type() == QVariant::Double) |
|
1262 QVERIFY(qFuzzyCompare(produced.toDouble(), expected.toDouble())); |
|
1263 else if(qVariantCanConvert<QXmlName>(produced)) |
|
1264 { |
|
1265 /* QVariant::operator==() does identity comparison, it doesn't delegate to operator==() of |
|
1266 * the contained type, unless it's hardcoded into QVariant. */ |
|
1267 const QXmlName n1 = qVariantValue<QXmlName>(produced); |
|
1268 const QXmlName n2 = qVariantValue<QXmlName>(expected); |
|
1269 QCOMPARE(n1, n2); |
|
1270 } |
|
1271 else |
|
1272 QCOMPARE(produced, expected); |
|
1273 } |
|
1274 |
|
1275 ++i; |
|
1276 item = it.next(); |
|
1277 } |
|
1278 |
|
1279 QCOMPARE(i, expectedValues.count()); |
|
1280 } |
|
1281 |
|
1282 /*! |
|
1283 Send values from Qt into XQuery. |
|
1284 */ |
|
1285 void tst_QXmlQuery::basicQtToXQueryTypeCheck() const |
|
1286 { |
|
1287 QFile queryFile(QLatin1String(queriesDirectory) + QLatin1String("allAtomicsExternally.xq")); |
|
1288 QVERIFY(queryFile.exists()); |
|
1289 QVERIFY(queryFile.open(QIODevice::ReadOnly)); |
|
1290 |
|
1291 QCOMPARE(QVariant(QDate(1999, 9, 10)).type(), QVariant::Date); |
|
1292 |
|
1293 QXmlQuery query; |
|
1294 |
|
1295 QXmlNamePool np(query.namePool()); |
|
1296 |
|
1297 const QXmlName name(np, QLatin1String("localname"), |
|
1298 QLatin1String("http://example.com"), |
|
1299 QLatin1String("prefix")); |
|
1300 |
|
1301 query.bindVariable(QLatin1String("fromQUrl"), QXmlItem(QUrl(QString::fromLatin1("http://example.com/")))); |
|
1302 query.bindVariable(QLatin1String("fromQByteArray"), QXmlItem(QByteArray("AAAA"))); |
|
1303 query.bindVariable(QLatin1String("fromBool"), QXmlItem(bool(true))); |
|
1304 query.bindVariable(QLatin1String("fromQDate"), QXmlItem(QDate(2000, 10, 11))); |
|
1305 // TODO Do with different QDateTime time specs |
|
1306 query.bindVariable(QLatin1String("fromQDateTime"), QXmlItem(QDateTime(QDate(2001, 9, 10), QTime(1, 2, 3)))); |
|
1307 query.bindVariable(QLatin1String("fromDouble"), QXmlItem(double(3))); |
|
1308 query.bindVariable(QLatin1String("fromFloat"), QXmlItem(float(4))); |
|
1309 query.bindVariable(QLatin1String("integer"), QXmlItem(5)); |
|
1310 query.bindVariable(QLatin1String("fromQString"), QXmlItem(QString::fromLatin1("A QString"))); |
|
1311 query.bindVariable(QLatin1String("fromQChar"), QXmlItem(QChar::fromLatin1('C'))); |
|
1312 |
|
1313 query.bindVariable(QLatin1String("fromIntLiteral"), QXmlItem(QVariant(654))); |
|
1314 |
|
1315 { |
|
1316 QVariant ui(uint(5)); |
|
1317 QCOMPARE(ui.type(), QVariant::UInt); |
|
1318 query.bindVariable(QLatin1String("fromUInt"), ui); |
|
1319 } |
|
1320 |
|
1321 { |
|
1322 QVariant ulnglng(qulonglong(6)); |
|
1323 QCOMPARE(ulnglng.type(), QVariant::ULongLong); |
|
1324 query.bindVariable(QLatin1String("fromULongLong"), ulnglng); |
|
1325 } |
|
1326 |
|
1327 { |
|
1328 QVariant qlnglng(qlonglong(7)); |
|
1329 QCOMPARE(qlnglng.type(), QVariant::LongLong); |
|
1330 query.bindVariable(QLatin1String("fromLongLong"), qlnglng); |
|
1331 } |
|
1332 |
|
1333 query.setQuery(&queryFile); |
|
1334 |
|
1335 // TODO do queries which declares external variables with types. Tons of combos here. |
|
1336 // TODO ensure that binding with QXmlItem() doesn't make a binding available. |
|
1337 // TODO test rebinding a variable. |
|
1338 |
|
1339 QVERIFY(query.isValid()); |
|
1340 |
|
1341 QXmlResultItems it; |
|
1342 query.evaluateTo(&it); |
|
1343 QXmlItem item(it.next()); |
|
1344 QVERIFY(!item.isNull()); |
|
1345 QVERIFY(item.isAtomicValue()); |
|
1346 |
|
1347 QCOMPARE(item.toAtomicValue().toString(), |
|
1348 QLatin1String("4 true 3 654 7 41414141 C 2000-10-11Z 2001-09-10T01:02:03 " |
|
1349 "A QString http://example.com/ 5 6 true true true true true true true true true true " |
|
1350 "true true true")); |
|
1351 } |
|
1352 |
|
1353 void tst_QXmlQuery::bindNode() const |
|
1354 { |
|
1355 QXmlQuery query; |
|
1356 TestSimpleNodeModel nodeModel(query.namePool()); |
|
1357 |
|
1358 query.bindVariable(QLatin1String("node"), nodeModel.root()); |
|
1359 QByteArray out; |
|
1360 QBuffer buff(&out); |
|
1361 QVERIFY(buff.open(QIODevice::WriteOnly)); |
|
1362 |
|
1363 query.setQuery(QLatin1String("declare variable $node external; $node")); |
|
1364 QXmlSerializer serializer(query, &buff); |
|
1365 |
|
1366 QVERIFY(query.evaluateTo(&serializer)); |
|
1367 QCOMPARE(out, QByteArray("<nodeName/>")); |
|
1368 } |
|
1369 |
|
1370 /*! |
|
1371 Pass in a relative URI, and make sure it is resolved against the current application directory. |
|
1372 */ |
|
1373 void tst_QXmlQuery::relativeBaseURI() const |
|
1374 { |
|
1375 QXmlQuery query; |
|
1376 query.setQuery(QLatin1String("fn:static-base-uri()"), QUrl(QLatin1String("a/relative/uri.weirdExtension"))); |
|
1377 QVERIFY(query.isValid()); |
|
1378 |
|
1379 QByteArray result; |
|
1380 QBuffer buffer(&result); |
|
1381 QVERIFY(buffer.open(QIODevice::ReadWrite)); |
|
1382 |
|
1383 QXmlSerializer serializer(query, &buffer); |
|
1384 QVERIFY(query.evaluateTo(&serializer)); |
|
1385 |
|
1386 const QUrl loaded(QUrl::fromEncoded(result)); |
|
1387 QUrl appPath(QUrl::fromLocalFile(QCoreApplication::applicationFilePath())); |
|
1388 |
|
1389 QVERIFY(loaded.isValid()); |
|
1390 QVERIFY(appPath.isValid()); |
|
1391 QVERIFY(!loaded.isRelative()); |
|
1392 QVERIFY(!appPath.isRelative()); |
|
1393 |
|
1394 QFileInfo dir(appPath.toLocalFile()); |
|
1395 dir.setFile(QString()); |
|
1396 |
|
1397 /* We can't use QUrl::isParentOf() because it doesn't do what we want it to */ |
|
1398 if(!loaded.toLocalFile().startsWith(dir.absoluteFilePath())) |
|
1399 QTextStream(stderr) << "dir.absoluteFilePath():" << dir.absoluteFilePath() << "loaded.toLocalFile():" << loaded.toLocalFile(); |
|
1400 |
|
1401 checkBaseURI(loaded, dir.absoluteFilePath()); |
|
1402 } |
|
1403 |
|
1404 void tst_QXmlQuery::emptyBaseURI() const |
|
1405 { |
|
1406 QXmlQuery query; |
|
1407 query.setQuery(QLatin1String("fn:static-base-uri()"), QUrl()); |
|
1408 QVERIFY(query.isValid()); |
|
1409 |
|
1410 QByteArray result; |
|
1411 QBuffer buffer(&result); |
|
1412 QVERIFY(buffer.open(QIODevice::ReadWrite)); |
|
1413 |
|
1414 QXmlSerializer serializer(query, &buffer); |
|
1415 QVERIFY(query.evaluateTo(&serializer)); |
|
1416 |
|
1417 const QUrl loaded(QUrl::fromEncoded(result)); |
|
1418 QUrl appPath(QUrl::fromLocalFile(QCoreApplication::applicationFilePath())); |
|
1419 |
|
1420 QVERIFY(loaded.isValid()); |
|
1421 QVERIFY(appPath.isValid()); |
|
1422 QVERIFY(!loaded.isRelative()); |
|
1423 QVERIFY(!appPath.isRelative()); |
|
1424 |
|
1425 QFileInfo dir(appPath.toLocalFile()); |
|
1426 dir.setFile(QString()); |
|
1427 |
|
1428 QCOMPARE(loaded, appPath); |
|
1429 } |
|
1430 |
|
1431 /*! |
|
1432 Ensure that QDate comes out as QDateTime. |
|
1433 */ |
|
1434 void tst_QXmlQuery::roundTripDateWithinQXmlItem() const |
|
1435 { |
|
1436 const QDate date(1999, 9, 10); |
|
1437 QVERIFY(date.isValid()); |
|
1438 |
|
1439 const QVariant variant(date); |
|
1440 QVERIFY(variant.isValid()); |
|
1441 QCOMPARE(variant.type(), QVariant::Date); |
|
1442 |
|
1443 const QXmlItem item(variant); |
|
1444 QVERIFY(!item.isNull()); |
|
1445 QVERIFY(item.isAtomicValue()); |
|
1446 |
|
1447 const QVariant out(item.toAtomicValue()); |
|
1448 QVERIFY(out.isValid()); |
|
1449 QCOMPARE(out.type(), QVariant::Date); |
|
1450 QCOMPARE(out.toDate(), date); |
|
1451 } |
|
1452 |
|
1453 /*! |
|
1454 Check whether a query is valid, which uses an unbound variable. |
|
1455 */ |
|
1456 void tst_QXmlQuery::bindingMissing() const |
|
1457 { |
|
1458 QXmlQuery query; |
|
1459 MessageSilencer messageHandler; |
|
1460 query.setMessageHandler(&messageHandler); |
|
1461 |
|
1462 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq")); |
|
1463 QVERIFY(queryFile.open(QIODevice::ReadOnly)); |
|
1464 query.setQuery(&queryFile); |
|
1465 |
|
1466 QVERIFY(!query.isValid()); |
|
1467 } |
|
1468 |
|
1469 void tst_QXmlQuery::bindDefaultConstructedItem() const |
|
1470 { |
|
1471 QFETCH(QXmlItem, item); |
|
1472 |
|
1473 QXmlQuery query; |
|
1474 MessageSilencer messageHandler; |
|
1475 query.setMessageHandler(&messageHandler); |
|
1476 |
|
1477 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq")); |
|
1478 QVERIFY(queryFile.open(QIODevice::ReadOnly)); |
|
1479 query.setQuery(&queryFile); |
|
1480 query.bindVariable(QLatin1String("externalVariable"), item); |
|
1481 |
|
1482 QVERIFY(!query.isValid()); |
|
1483 } |
|
1484 |
|
1485 void tst_QXmlQuery::bindDefaultConstructedItem_data() const |
|
1486 { |
|
1487 QTest::addColumn<QXmlItem>("item"); |
|
1488 |
|
1489 QTest::newRow("QXmlItem()") << QXmlItem(); |
|
1490 QTest::newRow("QXmlItem(QVariant())") << QXmlItem(QVariant()); |
|
1491 QTest::newRow("QXmlItem(QXmlNodeModelIndex())") << QXmlItem(QXmlNodeModelIndex()); |
|
1492 } |
|
1493 |
|
1494 /*! |
|
1495 Remove a binding by binding QXmlItem() with the same name. |
|
1496 */ |
|
1497 void tst_QXmlQuery::eraseQXmlItemBinding() const |
|
1498 { |
|
1499 QXmlQuery query; |
|
1500 MessageSilencer messageHandler; |
|
1501 query.setMessageHandler(&messageHandler); |
|
1502 |
|
1503 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq")); |
|
1504 QVERIFY(queryFile.open(QIODevice::ReadOnly)); |
|
1505 query.bindVariable(QLatin1String("externalVariable"), QXmlItem(3)); |
|
1506 query.setQuery(&queryFile); |
|
1507 QVERIFY(query.isValid()); |
|
1508 |
|
1509 QByteArray result; |
|
1510 QBuffer buffer(&result); |
|
1511 QVERIFY(buffer.open(QIODevice::ReadWrite)); |
|
1512 |
|
1513 QXmlSerializer serializer(query, &buffer); |
|
1514 QVERIFY(query.evaluateTo(&serializer)); |
|
1515 |
|
1516 QCOMPARE(result, QByteArray("3 6<e>3</e>false")); |
|
1517 |
|
1518 query.bindVariable(QLatin1String("externalVariable"), QXmlItem()); |
|
1519 QVERIFY(!query.isValid()); |
|
1520 } |
|
1521 |
|
1522 /*! |
|
1523 Erase a variable binding |
|
1524 */ |
|
1525 void tst_QXmlQuery::eraseDeviceBinding() const |
|
1526 { |
|
1527 /* Erase an existing QIODevice binding with another QIODevice binding. */ |
|
1528 { |
|
1529 QXmlQuery query; |
|
1530 |
|
1531 QByteArray doc("<e/>"); |
|
1532 QBuffer buffer(&doc); |
|
1533 QVERIFY(buffer.open(QIODevice::ReadOnly)); |
|
1534 |
|
1535 query.bindVariable(QLatin1String("in"), &buffer); |
|
1536 query.setQuery(QLatin1String("$in")); |
|
1537 QVERIFY(query.isValid()); |
|
1538 |
|
1539 query.bindVariable(QLatin1String("in"), 0); |
|
1540 QVERIFY(!query.isValid()); |
|
1541 } |
|
1542 |
|
1543 /* Erase an existing QXmlItem binding with another QIODevice binding. */ |
|
1544 { |
|
1545 QXmlQuery query; |
|
1546 |
|
1547 query.bindVariable(QLatin1String("in"), QXmlItem(5)); |
|
1548 query.setQuery(QLatin1String("$in")); |
|
1549 QVERIFY(query.isValid()); |
|
1550 |
|
1551 query.bindVariable(QLatin1String("in"), 0); |
|
1552 QVERIFY(!query.isValid()); |
|
1553 } |
|
1554 } |
|
1555 |
|
1556 /*! |
|
1557 Bind a variable, evaluate, bind with a different value but same type, and evaluate again. |
|
1558 */ |
|
1559 void tst_QXmlQuery::rebindVariableSameType() const |
|
1560 { |
|
1561 QXmlQuery query; |
|
1562 MessageSilencer messageHandler; |
|
1563 query.setMessageHandler(&messageHandler); |
|
1564 |
|
1565 query.bindVariable(QLatin1String("externalVariable"), QXmlItem(3)); |
|
1566 |
|
1567 { |
|
1568 QFile queryFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariable.xq")); |
|
1569 QVERIFY(queryFile.open(QIODevice::ReadOnly)); |
|
1570 query.setQuery(&queryFile); |
|
1571 } |
|
1572 |
|
1573 QVERIFY(query.isValid()); |
|
1574 |
|
1575 { |
|
1576 QByteArray result; |
|
1577 QBuffer buffer(&result); |
|
1578 QVERIFY(buffer.open(QIODevice::ReadWrite)); |
|
1579 |
|
1580 QXmlSerializer serializer(query, &buffer); |
|
1581 QVERIFY(query.evaluateTo(&serializer)); |
|
1582 |
|
1583 QCOMPARE(result, QByteArray("3 6<e>3</e>false")); |
|
1584 } |
|
1585 |
|
1586 { |
|
1587 query.bindVariable(QLatin1String("externalVariable"), QXmlItem(5)); |
|
1588 QByteArray result; |
|
1589 QBuffer buffer(&result); |
|
1590 QVERIFY(buffer.open(QIODevice::ReadWrite)); |
|
1591 |
|
1592 QXmlSerializer serializer(query, &buffer); |
|
1593 QVERIFY(query.evaluateTo(&serializer)); |
|
1594 |
|
1595 QCOMPARE(result, QByteArray("5 8<e>5</e>false")); |
|
1596 } |
|
1597 |
|
1598 } |
|
1599 |
|
1600 void tst_QXmlQuery::rebindVariableDifferentType() const |
|
1601 { |
|
1602 /* Rebind QXmlItem variable with QXmlItem variable. */ |
|
1603 { |
|
1604 QXmlQuery query; |
|
1605 query.bindVariable(QLatin1String("in"), QXmlItem(3)); |
|
1606 query.setQuery(QLatin1String("$in")); |
|
1607 QVERIFY(query.isValid()); |
|
1608 |
|
1609 query.bindVariable(QLatin1String("in"), QXmlItem("A string")); |
|
1610 QVERIFY(!query.isValid()); |
|
1611 } |
|
1612 |
|
1613 /* Rebind QIODevice variable with QXmlItem variable. */ |
|
1614 { |
|
1615 QXmlQuery query; |
|
1616 QBuffer buffer; |
|
1617 buffer.setData(QByteArray("<e/>")); |
|
1618 QVERIFY(buffer.open(QIODevice::ReadOnly)); |
|
1619 |
|
1620 query.bindVariable(QLatin1String("in"), &buffer); |
|
1621 query.setQuery(QLatin1String("$in")); |
|
1622 QVERIFY(query.isValid()); |
|
1623 |
|
1624 query.bindVariable(QLatin1String("in"), QXmlItem("A string")); |
|
1625 QVERIFY(!query.isValid()); |
|
1626 } |
|
1627 |
|
1628 /* Rebind QXmlItem variable with QIODevice variable. The type of the |
|
1629 * variable changes, so a recompile is necessary. */ |
|
1630 { |
|
1631 QXmlQuery query; |
|
1632 |
|
1633 query.bindVariable(QLatin1String("in"), QXmlItem(QLatin1String("A string"))); |
|
1634 query.setQuery(QLatin1String("$in")); |
|
1635 QVERIFY(query.isValid()); |
|
1636 |
|
1637 QBuffer buffer; |
|
1638 buffer.setData(QByteArray("<e/>")); |
|
1639 QVERIFY(buffer.open(QIODevice::ReadOnly)); |
|
1640 query.bindVariable(QLatin1String("in"), &buffer); |
|
1641 QVERIFY(!query.isValid()); |
|
1642 } |
|
1643 } |
|
1644 |
|
1645 void tst_QXmlQuery::rebindVariableWithNullItem() const |
|
1646 { |
|
1647 QXmlQuery query; |
|
1648 |
|
1649 query.bindVariable(QLatin1String("name"), QXmlItem(5)); |
|
1650 query.bindVariable(QLatin1String("name"), QXmlItem()); |
|
1651 } |
|
1652 |
|
1653 void tst_QXmlQuery::constCorrectness() const |
|
1654 { |
|
1655 QXmlResultItems result; |
|
1656 QXmlQuery tmp; |
|
1657 tmp.setQuery(QLatin1String("1")); /* Just so we have a valid query. */ |
|
1658 const QXmlQuery query(tmp); |
|
1659 |
|
1660 /* These functions should be const. */ |
|
1661 query.isValid(); |
|
1662 query.evaluateTo(&result); |
|
1663 query.namePool(); |
|
1664 query.uriResolver(); |
|
1665 query.messageHandler(); |
|
1666 |
|
1667 { |
|
1668 QString dummyString; |
|
1669 QTextStream dummyStream(&dummyString); |
|
1670 PushBaseliner dummy(dummyStream, query.namePool()); |
|
1671 query.evaluateTo(&dummy); |
|
1672 } |
|
1673 } |
|
1674 |
|
1675 void tst_QXmlQuery::objectSize() const |
|
1676 { |
|
1677 /* We have a d pointer. */ |
|
1678 QCOMPARE(sizeof(QXmlQuery), sizeof(void *)); |
|
1679 } |
|
1680 |
|
1681 void tst_QXmlQuery::setUriResolver() const |
|
1682 { |
|
1683 /* Set a null resolver, and make sure it can take a const pointer. */ |
|
1684 { |
|
1685 QXmlQuery query; |
|
1686 query.setUriResolver(static_cast<const QAbstractUriResolver *>(0)); |
|
1687 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0)); |
|
1688 } |
|
1689 |
|
1690 { |
|
1691 TestURIResolver resolver; |
|
1692 QXmlQuery query; |
|
1693 query.setUriResolver(&resolver); |
|
1694 QCOMPARE(query.uriResolver(), &resolver); |
|
1695 } |
|
1696 } |
|
1697 |
|
1698 void tst_QXmlQuery::uriResolver() const |
|
1699 { |
|
1700 /* Check default value. */ |
|
1701 { |
|
1702 QXmlQuery query; |
|
1703 QCOMPARE(query.uriResolver(), static_cast<const QAbstractUriResolver *>(0)); |
|
1704 } |
|
1705 } |
|
1706 |
|
1707 void tst_QXmlQuery::messageXML() const |
|
1708 { |
|
1709 QXmlQuery query; |
|
1710 |
|
1711 MessageValidator messageValidator; |
|
1712 query.setMessageHandler(&messageValidator); |
|
1713 |
|
1714 query.setQuery(QLatin1String("1basicSyntaxError")); |
|
1715 |
|
1716 const QRegExp removeFilename(QLatin1String("Location: file:.*\\#")); |
|
1717 QVERIFY(removeFilename.isValid()); |
|
1718 |
|
1719 QVERIFY(messageValidator.success()); |
|
1720 QCOMPARE(messageValidator.received().remove(removeFilename), |
|
1721 QString::fromLatin1("Type:3\n" |
|
1722 "Description: <html xmlns='http://www.w3.org/1999/xhtml/'><body><p>syntax error, unexpected unknown keyword</p></body></html>\n" |
|
1723 "Identifier: http://www.w3.org/2005/xqt-errors#XPST0003\n" |
|
1724 "1,1")); |
|
1725 } |
|
1726 |
|
1727 /*! |
|
1728 1. Allocate QXmlResultItems |
|
1729 2. Allocate QXmlQuery |
|
1730 3. evaluate to the QXmlResultItems instance |
|
1731 4. Dellocate the QXmlQuery instance |
|
1732 5. Ensure QXmlResultItems works |
|
1733 */ |
|
1734 void tst_QXmlQuery::resultItemsDeallocatedQuery() const |
|
1735 { |
|
1736 QXmlResultItems result; |
|
1737 |
|
1738 { |
|
1739 QXmlQuery query; |
|
1740 query.setQuery(QLatin1String("1, 2, xs:integer(<e>3</e>)")); |
|
1741 query.evaluateTo(&result); |
|
1742 } |
|
1743 |
|
1744 QCOMPARE(result.next().toAtomicValue(), QVariant(1)); |
|
1745 QCOMPARE(result.next().toAtomicValue(), QVariant(2)); |
|
1746 QCOMPARE(result.next().toAtomicValue(), QVariant(3)); |
|
1747 QVERIFY(result.next().isNull()); |
|
1748 QVERIFY(!result.hasError()); |
|
1749 } |
|
1750 |
|
1751 /*! |
|
1752 1. Bind variable with bindVariable() |
|
1753 2. setQuery that has 'declare variable' with same name. |
|
1754 3. Ensure the value inside the query is used. We don't guarantee this behavior |
|
1755 but that's what we lock. |
|
1756 */ |
|
1757 void tst_QXmlQuery::shadowedVariables() const |
|
1758 { |
|
1759 QXmlQuery query; |
|
1760 query.bindVariable("varName", QXmlItem(3)); |
|
1761 query.setQuery(QLatin1String("declare variable $varName := 5; $varName")); |
|
1762 |
|
1763 QXmlResultItems result; |
|
1764 query.evaluateTo(&result); |
|
1765 |
|
1766 QCOMPARE(result.next().toAtomicValue(), QVariant(5)); |
|
1767 } |
|
1768 |
|
1769 void tst_QXmlQuery::setFocusQXmlItem() const |
|
1770 { |
|
1771 /* Make sure we can take a const reference. */ |
|
1772 { |
|
1773 QXmlQuery query; |
|
1774 const QXmlItem item; |
|
1775 query.setFocus(item); |
|
1776 } |
|
1777 |
|
1778 // TODO evaluate with atomic value, check type |
|
1779 // TODO evaluate with node, check type |
|
1780 // TODO ensure that setFocus() triggers query recompilation, as appropriate. |
|
1781 // TODO let the focus be undefined, call isvalid, call evaluate anyway |
|
1782 // TODO let the focus be undefined, call evaluate directly |
|
1783 } |
|
1784 |
|
1785 void tst_QXmlQuery::setFocusQUrl() const |
|
1786 { |
|
1787 /* Load a focus which isn't well-formed. */ |
|
1788 { |
|
1789 QXmlQuery query; |
|
1790 MessageSilencer silencer; |
|
1791 |
|
1792 query.setMessageHandler(&silencer); |
|
1793 |
|
1794 QVERIFY(!query.setFocus(QUrl(QLatin1String("data/notWellformed.xml")))); |
|
1795 } |
|
1796 |
|
1797 /* Ensure the same URI resolver is used. */ |
|
1798 { |
|
1799 QXmlQuery query(QXmlQuery::XSLT20); |
|
1800 |
|
1801 const TestURIResolver resolver(QUrl(inputFile(QLatin1String(SRCDIR "../xmlpatterns/stylesheets/documentElement.xml")))); |
|
1802 query.setUriResolver(&resolver); |
|
1803 |
|
1804 QVERIFY(query.setFocus(QUrl(QLatin1String("arbitraryURI")))); |
|
1805 query.setQuery(QUrl(inputFile(QLatin1String(SRCDIR "../xmlpatterns/stylesheets/copyWholeDocument.xsl")))); |
|
1806 QVERIFY(query.isValid()); |
|
1807 |
|
1808 QBuffer result; |
|
1809 QVERIFY(result.open(QIODevice::ReadWrite)); |
|
1810 QXmlSerializer serializer(query, &result); |
|
1811 query.evaluateTo(&serializer); |
|
1812 |
|
1813 QCOMPARE(result.data(), QByteArray("<doc/>")); |
|
1814 } |
|
1815 |
|
1816 // TODO ensure that the focus type doesn't change from XSLT20 on the main instance. |
|
1817 } |
|
1818 |
|
1819 /*! |
|
1820 This code poses a challenge wrt. to internal caching. |
|
1821 */ |
|
1822 void tst_QXmlQuery::setFocusQIODevice() const |
|
1823 { |
|
1824 QXmlQuery query; |
|
1825 |
|
1826 { |
|
1827 QBuffer focus; |
|
1828 focus.setData(QByteArray("<e>abc</e>")); |
|
1829 QVERIFY(focus.open(QIODevice::ReadOnly)); |
|
1830 query.setFocus(&focus); |
|
1831 query.setQuery(QLatin1String("string()")); |
|
1832 QVERIFY(query.isValid()); |
|
1833 |
|
1834 QString output; |
|
1835 query.evaluateTo(&output); |
|
1836 |
|
1837 QCOMPARE(output, QString::fromLatin1("abc\n")); |
|
1838 } |
|
1839 |
|
1840 /* Set a new focus, make sure it changes & works. */ |
|
1841 { |
|
1842 QBuffer focus2; |
|
1843 focus2.setData(QByteArray("<e>abc2</e>")); |
|
1844 QVERIFY(focus2.open(QIODevice::ReadOnly)); |
|
1845 query.setFocus(&focus2); |
|
1846 QVERIFY(query.isValid()); |
|
1847 |
|
1848 QString output; |
|
1849 query.evaluateTo(&output); |
|
1850 |
|
1851 QCOMPARE(output, QString::fromLatin1("abc2\n")); |
|
1852 } |
|
1853 } |
|
1854 |
|
1855 /*! |
|
1856 Since we internally use variable bindings for implementing the focus, we need |
|
1857 to make sure we don't clash in this area. |
|
1858 */ |
|
1859 void tst_QXmlQuery::setFocusQIODeviceAvoidVariableClash() const |
|
1860 { |
|
1861 QBuffer buffer; |
|
1862 buffer.setData("<e>focus</e>"); |
|
1863 QVERIFY(buffer.open(QIODevice::ReadOnly)); |
|
1864 |
|
1865 /* First we bind the variable name, then the focus. */ |
|
1866 { |
|
1867 QXmlQuery query; |
|
1868 query.bindVariable(QString(QLatin1Char('u')), QVariant(1)); |
|
1869 query.setFocus(&buffer); |
|
1870 query.setQuery(QLatin1String("string()")); |
|
1871 |
|
1872 QString out; |
|
1873 query.evaluateTo(&out); |
|
1874 |
|
1875 QCOMPARE(out, QString::fromLatin1("focus\n")); |
|
1876 } |
|
1877 |
|
1878 /* First we bind the focus, then the variable name. */ |
|
1879 { |
|
1880 QXmlQuery query; |
|
1881 QVERIFY(buffer.open(QIODevice::ReadOnly)); |
|
1882 query.setFocus(&buffer); |
|
1883 query.bindVariable(QString(QLatin1Char('u')), QVariant(1)); |
|
1884 query.setQuery(QLatin1String("string()")); |
|
1885 |
|
1886 QString out; |
|
1887 query.evaluateTo(&out); |
|
1888 |
|
1889 QCOMPARE(out, QString::fromLatin1("focus\n")); |
|
1890 } |
|
1891 } |
|
1892 |
|
1893 void tst_QXmlQuery::setFocusQIODeviceFailure() const |
|
1894 { |
|
1895 /* A not well-formed input document. */ |
|
1896 { |
|
1897 QXmlQuery query; |
|
1898 |
|
1899 MessageSilencer silencer; |
|
1900 query.setMessageHandler(&silencer); |
|
1901 |
|
1902 QBuffer input; |
|
1903 input.setData("<e"); |
|
1904 QVERIFY(input.open(QIODevice::ReadOnly)); |
|
1905 |
|
1906 QCOMPARE(query.setFocus(&input), false); |
|
1907 } |
|
1908 } |
|
1909 |
|
1910 void tst_QXmlQuery::setFocusQString() const |
|
1911 { |
|
1912 QXmlQuery query; |
|
1913 |
|
1914 /* Basic use of focus. */ |
|
1915 { |
|
1916 QVERIFY(query.setFocus(QLatin1String("<e>textNode</e>"))); |
|
1917 query.setQuery(QLatin1String("string()")); |
|
1918 QVERIFY(query.isValid()); |
|
1919 QString out; |
|
1920 query.evaluateTo(&out); |
|
1921 QCOMPARE(out, QString::fromLatin1("textNode\n")); |
|
1922 } |
|
1923 |
|
1924 /* Set to a new focus, make sure it changes and works. */ |
|
1925 { |
|
1926 QVERIFY(query.setFocus(QLatin1String("<e>newFocus</e>"))); |
|
1927 QString out; |
|
1928 query.evaluateTo(&out); |
|
1929 QCOMPARE(out, QString::fromLatin1("newFocus\n")); |
|
1930 } |
|
1931 } |
|
1932 |
|
1933 void tst_QXmlQuery::setFocusQStringFailure() const |
|
1934 { |
|
1935 QXmlQuery query; |
|
1936 MessageSilencer silencer; |
|
1937 |
|
1938 query.setMessageHandler(&silencer); |
|
1939 QVERIFY(!query.setFocus(QLatin1String("<notWellformed"))); |
|
1940 |
|
1941 /* Let's try the slight special case of a null string. */ |
|
1942 QVERIFY(!query.setFocus(QString())); |
|
1943 } |
|
1944 |
|
1945 void tst_QXmlQuery::setFocusQStringSignature() const |
|
1946 { |
|
1947 QXmlQuery query; |
|
1948 MessageSilencer silencer; |
|
1949 query.setMessageHandler(&silencer); |
|
1950 |
|
1951 const QString argument; |
|
1952 /* We should take a const ref. */ |
|
1953 query.setFocus(argument); |
|
1954 |
|
1955 /* We should return a bool. */ |
|
1956 static_cast<bool>(query.setFocus(QString())); |
|
1957 } |
|
1958 |
|
1959 void tst_QXmlQuery::setFocusQIODeviceTriggerWarnings() const |
|
1960 { |
|
1961 /* A null pointer. */ |
|
1962 { |
|
1963 QXmlQuery query; |
|
1964 |
|
1965 QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed."); |
|
1966 QCOMPARE(query.setFocus(static_cast<QIODevice *>(0)), false); |
|
1967 } |
|
1968 |
|
1969 /* A non opened-device. */ |
|
1970 { |
|
1971 QXmlQuery query; |
|
1972 |
|
1973 QBuffer notReadable; |
|
1974 QVERIFY(!notReadable.isReadable()); |
|
1975 |
|
1976 QTest::ignoreMessage(QtWarningMsg, "The device must be readable."); |
|
1977 QCOMPARE(query.setFocus(¬Readable), false); |
|
1978 } |
|
1979 } |
|
1980 |
|
1981 void tst_QXmlQuery::fnDocNetworkAccessSuccess() const |
|
1982 { |
|
1983 #if defined(Q_OS_WINCE) && !defined(_X86_) |
|
1984 QStringList testsToSkip; |
|
1985 testsToSkip << "http scheme" << "ftp scheme"; |
|
1986 if (testsToSkip.contains(QTest::currentDataTag())) |
|
1987 QSKIP("Network tests are currently unsupported on Windows CE.", SkipSingle); |
|
1988 #endif |
|
1989 |
|
1990 QFETCH(QUrl, uriToOpen); |
|
1991 QFETCH(QByteArray, expectedOutput); |
|
1992 |
|
1993 if(!uriToOpen.isValid()) |
|
1994 qDebug() << "uriToOpen:" << uriToOpen; |
|
1995 |
|
1996 QVERIFY(uriToOpen.isValid()); |
|
1997 |
|
1998 QXmlQuery query; |
|
1999 query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen)); |
|
2000 query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)")); |
|
2001 QVERIFY(query.isValid()); |
|
2002 |
|
2003 QByteArray result; |
|
2004 QBuffer buffer(&result); |
|
2005 QVERIFY(buffer.open(QIODevice::WriteOnly)); |
|
2006 |
|
2007 QXmlSerializer serializer(query, &buffer); |
|
2008 QVERIFY(query.evaluateTo(&serializer)); |
|
2009 |
|
2010 QCOMPARE(result, expectedOutput); |
|
2011 } |
|
2012 |
|
2013 void tst_QXmlQuery::fnDocNetworkAccessSuccess_data() const |
|
2014 { |
|
2015 QTest::addColumn<QUrl>("uriToOpen"); |
|
2016 QTest::addColumn<QByteArray>("expectedOutput"); |
|
2017 |
|
2018 QTest::newRow("file scheme") |
|
2019 << inputFileAsURI(QLatin1String(SRCDIR "input.xml")) |
|
2020 << QByteArray("<!-- This is just a file for testing. --><input/>"); |
|
2021 |
|
2022 QTest::newRow("data scheme with ASCII") |
|
2023 /* QUrl::toPercentEncoding(QLatin1String("<e/>")) yields "%3Ce%2F%3E". */ |
|
2024 << QUrl::fromEncoded("data:application/xml,%3Ce%2F%3E") |
|
2025 << QByteArray("<e/>"); |
|
2026 |
|
2027 QTest::newRow("data scheme with ASCII no MIME type") |
|
2028 << QUrl::fromEncoded("data:,%3Ce%2F%3E") |
|
2029 << QByteArray("<e/>"); |
|
2030 |
|
2031 QTest::newRow("data scheme with base 64") |
|
2032 << QUrl::fromEncoded("data:application/xml;base64,PGUvPg==") |
|
2033 << QByteArray("<e/>"); |
|
2034 |
|
2035 QTest::newRow("qrc scheme") |
|
2036 << QUrl::fromEncoded("qrc:/QXmlQueryTestData/data/oneElement.xml") |
|
2037 << QByteArray("<oneElement/>"); |
|
2038 |
|
2039 if(!m_testNetwork) |
|
2040 return; |
|
2041 |
|
2042 QTest::newRow("http scheme") |
|
2043 << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qxmlquery/wellFormed.xml")) |
|
2044 << QByteArray("<!-- a comment --><e from=\"http\">Some Text</e>"); |
|
2045 |
|
2046 QTest::newRow("ftp scheme") |
|
2047 << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/wellFormed.xml")) |
|
2048 << QByteArray("<!-- a comment --><e from=\"ftp\">Some Text</e>"); |
|
2049 |
|
2050 } |
|
2051 |
|
2052 void tst_QXmlQuery::fnDocNetworkAccessFailure() const |
|
2053 { |
|
2054 QFETCH(QUrl, uriToOpen); |
|
2055 |
|
2056 QVERIFY(uriToOpen.isValid()); |
|
2057 |
|
2058 QXmlQuery query; |
|
2059 MessageSilencer silencer; |
|
2060 query.setMessageHandler(&silencer); |
|
2061 query.bindVariable(QLatin1String("uri"), QVariant(uriToOpen)); |
|
2062 query.setQuery(QLatin1String("declare variable $uri external;\ndoc($uri)")); |
|
2063 QVERIFY(query.isValid()); |
|
2064 |
|
2065 QXmlResultItems result; |
|
2066 query.evaluateTo(&result); |
|
2067 |
|
2068 while(!result.next().isNull()) |
|
2069 { |
|
2070 /* Just loop until the end. */ |
|
2071 } |
|
2072 |
|
2073 // TODO do something that triggers a /timeout/. |
|
2074 QVERIFY(result.hasError()); |
|
2075 } |
|
2076 |
|
2077 void tst_QXmlQuery::fnDocNetworkAccessFailure_data() const |
|
2078 { |
|
2079 QTest::addColumn<QUrl>("uriToOpen"); |
|
2080 |
|
2081 QTest::newRow("data scheme, not-well-formed") |
|
2082 << QUrl(QLatin1String("data:application/xml;base64,PGUvg===")); |
|
2083 |
|
2084 QTest::newRow("file scheme, non-existant file") |
|
2085 << QUrl(QLatin1String("file:///example.com/does/notExist.xml")); |
|
2086 |
|
2087 QTest::newRow("http scheme, file not found") |
|
2088 << QUrl(QLatin1String("http://www.example.com/does/not/exist.xml")); |
|
2089 |
|
2090 QTest::newRow("http scheme, nonexistent host") |
|
2091 << QUrl(QLatin1String("http://this.host.does.not.exist.I.SWear")); |
|
2092 |
|
2093 QTest::newRow("qrc scheme, not well-formed") |
|
2094 << QUrl(QLatin1String("qrc:/QXmlQueryTestData/notWellformed.xml")); |
|
2095 |
|
2096 QTest::newRow("'qrc:/', non-existing file") |
|
2097 << QUrl(QLatin1String(":/QXmlQueryTestData/data/thisFileDoesNotExist.xml")); |
|
2098 |
|
2099 QTest::newRow("':/', this scheme is not supported") |
|
2100 << QUrl(QLatin1String(":/QXmlQueryTestData/data/notWellformed.xml")); |
|
2101 |
|
2102 if(!m_testNetwork) |
|
2103 return; |
|
2104 |
|
2105 QTest::newRow("http scheme, not well-formed") |
|
2106 << QUrl(QString("http://" + QtNetworkSettings::serverName() + "/qxmlquery/notWellformed.xml")); |
|
2107 |
|
2108 QTest::newRow("https scheme, not well-formed") |
|
2109 << QUrl(QString("https://" + QtNetworkSettings::serverName() + "/qxmlquery/notWellformedViaHttps.xml")); |
|
2110 |
|
2111 QTest::newRow("https scheme, nonexistent host") |
|
2112 << QUrl(QLatin1String("https://this.host.does.not.exist.I.SWear")); |
|
2113 |
|
2114 QTest::newRow("ftp scheme, nonexistent host") |
|
2115 << QUrl(QLatin1String("ftp://this.host.does.not.exist.I.SWear")); |
|
2116 |
|
2117 QTest::newRow("ftp scheme, not well-formed") |
|
2118 << QUrl(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/notWellFormed.xml")); |
|
2119 } |
|
2120 |
|
2121 /*! |
|
2122 Create a network timeout from a QIODevice binding such |
|
2123 that we ensure we don't hang infinitely. |
|
2124 */ |
|
2125 void tst_QXmlQuery::fnDocOnQIODeviceTimeout() const |
|
2126 { |
|
2127 if(!m_testNetwork) |
|
2128 return; |
|
2129 |
|
2130 QTcpServer server; |
|
2131 server.listen(QHostAddress::LocalHost, 1088); |
|
2132 |
|
2133 QTcpSocket client; |
|
2134 client.connectToHost("localhost", 1088); |
|
2135 QVERIFY(client.isReadable()); |
|
2136 |
|
2137 QXmlQuery query; |
|
2138 |
|
2139 MessageSilencer silencer; |
|
2140 query.setMessageHandler(&silencer); |
|
2141 |
|
2142 query.bindVariable(QLatin1String("inDevice"), &client); |
|
2143 query.setQuery(QLatin1String("declare variable $inDevice external;\ndoc($inDevice)")); |
|
2144 QVERIFY(query.isValid()); |
|
2145 |
|
2146 QXmlResultItems result; |
|
2147 query.evaluateTo(&result); |
|
2148 QXmlItem next(result.next()); |
|
2149 |
|
2150 while(!next.isNull()) |
|
2151 { |
|
2152 next = result.next(); |
|
2153 } |
|
2154 |
|
2155 QVERIFY(result.hasError()); |
|
2156 } |
|
2157 |
|
2158 /*! |
|
2159 When changing query, the static context must change too, such that |
|
2160 the source locations are updated. |
|
2161 */ |
|
2162 void tst_QXmlQuery::recompilationWithEvaluateToResultFailing() const |
|
2163 { |
|
2164 QXmlQuery query; |
|
2165 MessageSilencer silencer; |
|
2166 query.setMessageHandler(&silencer); |
|
2167 |
|
2168 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */ |
|
2169 QVERIFY(query.isValid()); /* Trigger query compilation. */ |
|
2170 |
|
2171 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */ |
|
2172 QVERIFY(query.isValid()); /* Trigger second compilation. */ |
|
2173 |
|
2174 QXmlResultItems items; |
|
2175 query.evaluateTo(&items); |
|
2176 items.next(); |
|
2177 QVERIFY(items.hasError()); |
|
2178 } |
|
2179 |
|
2180 void tst_QXmlQuery::secondEvaluationWithEvaluateToResultFailing() const |
|
2181 { |
|
2182 QXmlQuery query; |
|
2183 MessageSilencer silencer; |
|
2184 query.setMessageHandler(&silencer); |
|
2185 |
|
2186 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */ |
|
2187 QVERIFY(query.isValid()); /* Trigger query compilation. */ |
|
2188 |
|
2189 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */ |
|
2190 /* We don't call isValid(). */ |
|
2191 QXmlResultItems items; |
|
2192 query.evaluateTo(&items); |
|
2193 items.next(); |
|
2194 QVERIFY(items.hasError()); |
|
2195 } |
|
2196 |
|
2197 /*! |
|
2198 Compilation is triggered in the evaluation function due to no call to QXmlQuery::isValid(). |
|
2199 */ |
|
2200 void tst_QXmlQuery::recompilationWithEvaluateToReceiver() const |
|
2201 { |
|
2202 QXmlQuery query; |
|
2203 MessageSilencer silencer; |
|
2204 query.setMessageHandler(&silencer); |
|
2205 |
|
2206 query.setQuery(QLatin1String("1 + 1")); /* An arbitrary valid query. */ |
|
2207 QVERIFY(query.isValid()); /* Trigger query compilation. */ |
|
2208 |
|
2209 query.setQuery(QLatin1String("fn:doc('doesNotExist.example.com.xml')")); /* An arbitrary invalid query that make use of a source location. */ |
|
2210 /* We don't call isValid(). */ |
|
2211 |
|
2212 QByteArray dummy; |
|
2213 QBuffer buffer(&dummy); |
|
2214 buffer.open(QIODevice::WriteOnly); |
|
2215 |
|
2216 QXmlSerializer serializer(query, &buffer); |
|
2217 |
|
2218 QVERIFY(!query.evaluateTo(&serializer)); |
|
2219 } |
|
2220 |
|
2221 void tst_QXmlQuery::evaluateToQStringListOnInvalidQuery() const |
|
2222 { |
|
2223 MessageSilencer silencer; |
|
2224 |
|
2225 /* Invoke on a default constructed object. */ |
|
2226 { |
|
2227 QXmlQuery query; |
|
2228 QStringList out; |
|
2229 QVERIFY(!query.evaluateTo(&out)); |
|
2230 } |
|
2231 |
|
2232 /* Invoke on a syntactically invalid query. */ |
|
2233 { |
|
2234 QXmlQuery query; |
|
2235 QStringList out; |
|
2236 MessageSilencer silencer; |
|
2237 |
|
2238 query.setMessageHandler(&silencer); |
|
2239 query.setQuery(QLatin1String("1 + ")); |
|
2240 |
|
2241 QVERIFY(!query.evaluateTo(&out)); |
|
2242 } |
|
2243 |
|
2244 /* Invoke on a query with the wrong type, one atomic. */ |
|
2245 { |
|
2246 QXmlQuery query; |
|
2247 QStringList out; |
|
2248 |
|
2249 query.setQuery(QLatin1String("1")); |
|
2250 query.setMessageHandler(&silencer); |
|
2251 QVERIFY(!query.evaluateTo(&out)); |
|
2252 } |
|
2253 |
|
2254 /* Invoke on a query with the wrong type, one element. */ |
|
2255 { |
|
2256 QXmlQuery query; |
|
2257 QStringList out; |
|
2258 |
|
2259 query.setQuery(QLatin1String("<e/>")); |
|
2260 QVERIFY(!query.evaluateTo(&out)); |
|
2261 } |
|
2262 |
|
2263 /* Invoke on a query with the wrong type, mixed nodes & atomics. */ |
|
2264 { |
|
2265 QXmlQuery query; |
|
2266 QStringList out; |
|
2267 |
|
2268 query.setQuery(QLatin1String("<e/>, 1, 'a string'")); |
|
2269 query.setMessageHandler(&silencer); |
|
2270 QVERIFY(!query.evaluateTo(&out)); |
|
2271 } |
|
2272 |
|
2273 /* Evaluate the empty sequence. */ |
|
2274 { |
|
2275 QXmlQuery query; |
|
2276 QStringList out; |
|
2277 |
|
2278 query.setQuery(QLatin1String("()")); |
|
2279 QVERIFY(!query.evaluateTo(&out)); |
|
2280 QVERIFY(out.isEmpty()); |
|
2281 } |
|
2282 } |
|
2283 |
|
2284 void tst_QXmlQuery::evaluateToQStringList() const |
|
2285 { |
|
2286 QFETCH(QString, queryString); |
|
2287 QFETCH(QStringList, expectedOutput); |
|
2288 |
|
2289 QXmlQuery query; |
|
2290 query.setQuery(queryString); |
|
2291 QStringList out; |
|
2292 QVERIFY(query.isValid()); |
|
2293 |
|
2294 QVERIFY(query.evaluateTo(&out)); |
|
2295 |
|
2296 QCOMPARE(out, expectedOutput); |
|
2297 } |
|
2298 |
|
2299 void tst_QXmlQuery::evaluateToQStringListTriggerWarnings() const |
|
2300 { |
|
2301 QXmlQuery query; |
|
2302 |
|
2303 QTest::ignoreMessage(QtWarningMsg, "A non-null callback must be passed."); |
|
2304 QCOMPARE(query.evaluateTo(static_cast<QStringList *>(0)), |
|
2305 false); |
|
2306 } |
|
2307 |
|
2308 void tst_QXmlQuery::evaluateToQStringList_data() const |
|
2309 { |
|
2310 QTest::addColumn<QString>("queryString"); |
|
2311 QTest::addColumn<QStringList>("expectedOutput"); |
|
2312 |
|
2313 QTest::newRow("One atomic") |
|
2314 << QString::fromLatin1("(1 + 1) cast as xs:string") |
|
2315 << QStringList(QString::fromLatin1("2")); |
|
2316 |
|
2317 { |
|
2318 QStringList expected; |
|
2319 expected << QLatin1String("2"); |
|
2320 expected << QLatin1String("a string"); |
|
2321 |
|
2322 QTest::newRow("Two atomics") |
|
2323 << QString::fromLatin1("(1 + 1) cast as xs:string, 'a string'") |
|
2324 << expected; |
|
2325 } |
|
2326 |
|
2327 QTest::newRow("A query which evaluates to sub-types of xs:string.") |
|
2328 << QString::fromLatin1("xs:NCName('NCName'), xs:normalizedString(' a b c ')") |
|
2329 << (QStringList() << QString::fromLatin1("NCName") |
|
2330 << QString::fromLatin1(" a b c ")); |
|
2331 |
|
2332 QTest::newRow("A query which evaluates to two elements.") |
|
2333 << QString::fromLatin1("string(<e>theString1</e>), string(<e>theString2</e>)") |
|
2334 << (QStringList() << QString::fromLatin1("theString1") |
|
2335 << QString::fromLatin1("theString2")); |
|
2336 } |
|
2337 |
|
2338 /*! |
|
2339 Ensure that we don't automatically convert non-xs:string values. |
|
2340 */ |
|
2341 void tst_QXmlQuery::evaluateToQStringListNoConversion() const |
|
2342 { |
|
2343 QXmlQuery query; |
|
2344 query.setQuery(QString::fromLatin1("<e/>")); |
|
2345 QVERIFY(query.isValid()); |
|
2346 QStringList result; |
|
2347 QVERIFY(!query.evaluateTo(&result)); |
|
2348 } |
|
2349 |
|
2350 void tst_QXmlQuery::evaluateToQIODevice() const |
|
2351 { |
|
2352 /* an XQuery, check that no indentation is performed. */ |
|
2353 { |
|
2354 QBuffer out; |
|
2355 QVERIFY(out.open(QIODevice::ReadWrite)); |
|
2356 |
|
2357 QXmlQuery query; |
|
2358 query.setQuery(QLatin1String("<a><b/></a>")); |
|
2359 QVERIFY(query.isValid()); |
|
2360 QVERIFY(query.evaluateTo(&out)); |
|
2361 QCOMPARE(out.data(), QByteArray("<a><b/></a>")); |
|
2362 } |
|
2363 } |
|
2364 |
|
2365 void tst_QXmlQuery::evaluateToQIODeviceTriggerWarnings() const |
|
2366 { |
|
2367 QXmlQuery query; |
|
2368 |
|
2369 QTest::ignoreMessage(QtWarningMsg, "The pointer to the device cannot be null."); |
|
2370 QCOMPARE(query.evaluateTo(static_cast<QIODevice *>(0)), |
|
2371 false); |
|
2372 |
|
2373 QBuffer buffer; |
|
2374 |
|
2375 QTest::ignoreMessage(QtWarningMsg, "The device must be writable."); |
|
2376 QCOMPARE(query.evaluateTo(&buffer), |
|
2377 false); |
|
2378 } |
|
2379 |
|
2380 void tst_QXmlQuery::evaluateToQIODeviceSignature() const |
|
2381 { |
|
2382 /* The function should be const. */ |
|
2383 { |
|
2384 QBuffer out; |
|
2385 QVERIFY(out.open(QIODevice::ReadWrite)); |
|
2386 |
|
2387 const QXmlQuery query; |
|
2388 |
|
2389 query.evaluateTo(&out); |
|
2390 } |
|
2391 } |
|
2392 |
|
2393 void tst_QXmlQuery::evaluateToQIODeviceOnInvalidQuery() const |
|
2394 { |
|
2395 QBuffer out; |
|
2396 QVERIFY(out.open(QIODevice::WriteOnly)); |
|
2397 |
|
2398 /* On syntactically invalid query. */ |
|
2399 { |
|
2400 QXmlQuery query; |
|
2401 MessageSilencer silencer; |
|
2402 query.setMessageHandler(&silencer); |
|
2403 query.setQuery(QLatin1String("1 +")); |
|
2404 QVERIFY(!query.isValid()); |
|
2405 QVERIFY(!query.evaluateTo(&out)); |
|
2406 } |
|
2407 |
|
2408 /* On null QXmlQuery instance. */ |
|
2409 { |
|
2410 QXmlQuery query; |
|
2411 QVERIFY(!query.evaluateTo(&out)); |
|
2412 } |
|
2413 |
|
2414 } |
|
2415 |
|
2416 void tst_QXmlQuery::setQueryQIODeviceQUrl() const |
|
2417 { |
|
2418 /* Basic test. */ |
|
2419 { |
|
2420 QBuffer buffer; |
|
2421 buffer.setData("1, 2, 2 + 1"); |
|
2422 QVERIFY(buffer.open(QIODevice::ReadOnly)); |
|
2423 |
|
2424 QXmlQuery query; |
|
2425 query.setQuery(&buffer); |
|
2426 QVERIFY(query.isValid()); |
|
2427 |
|
2428 QXmlResultItems result; |
|
2429 query.evaluateTo(&result); |
|
2430 QCOMPARE(result.next().toAtomicValue(), QVariant(1)); |
|
2431 QCOMPARE(result.next().toAtomicValue(), QVariant(2)); |
|
2432 QCOMPARE(result.next().toAtomicValue(), QVariant(3)); |
|
2433 QVERIFY(result.next().isNull()); |
|
2434 QVERIFY(!result.hasError()); |
|
2435 } |
|
2436 |
|
2437 /* Set query that is invalid. */ |
|
2438 { |
|
2439 QBuffer buffer; |
|
2440 buffer.setData("1, "); |
|
2441 QVERIFY(buffer.open(QIODevice::ReadOnly)); |
|
2442 |
|
2443 QXmlQuery query; |
|
2444 MessageSilencer silencer; |
|
2445 query.setMessageHandler(&silencer); |
|
2446 query.setQuery(&buffer); |
|
2447 QVERIFY(!query.isValid()); |
|
2448 } |
|
2449 |
|
2450 /* Check that the base URI passes through. */ |
|
2451 { |
|
2452 QBuffer buffer; |
|
2453 buffer.setData("string(static-base-uri())"); |
|
2454 QVERIFY(buffer.open(QIODevice::ReadOnly)); |
|
2455 |
|
2456 QXmlQuery query; |
|
2457 query.setQuery(&buffer, QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl")); |
|
2458 QVERIFY(query.isValid()); |
|
2459 |
|
2460 QStringList result; |
|
2461 query.evaluateTo(&result); |
|
2462 QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl"))); |
|
2463 } |
|
2464 } |
|
2465 |
|
2466 void tst_QXmlQuery::setQueryQIODeviceQUrlTriggerWarnings() const |
|
2467 { |
|
2468 QXmlQuery query; |
|
2469 QTest::ignoreMessage(QtWarningMsg, "A null QIODevice pointer cannot be passed."); |
|
2470 query.setQuery(0); |
|
2471 |
|
2472 QBuffer buffer; |
|
2473 QTest::ignoreMessage(QtWarningMsg, "The device must be readable."); |
|
2474 query.setQuery(&buffer); |
|
2475 } |
|
2476 |
|
2477 void tst_QXmlQuery::setQueryQString() const |
|
2478 { |
|
2479 /* Basic test. */ |
|
2480 { |
|
2481 QXmlQuery query; |
|
2482 query.setQuery(QLatin1String("1, 2, 2 + 1")); |
|
2483 QVERIFY(query.isValid()); |
|
2484 |
|
2485 QXmlResultItems result; |
|
2486 query.evaluateTo(&result); |
|
2487 QCOMPARE(result.next().toAtomicValue(), QVariant(1)); |
|
2488 QCOMPARE(result.next().toAtomicValue(), QVariant(2)); |
|
2489 QCOMPARE(result.next().toAtomicValue(), QVariant(3)); |
|
2490 QVERIFY(result.next().isNull()); |
|
2491 QVERIFY(!result.hasError()); |
|
2492 } |
|
2493 |
|
2494 /* Set query that is invalid. */ |
|
2495 { |
|
2496 MessageSilencer silencer; |
|
2497 QXmlQuery query; |
|
2498 query.setMessageHandler(&silencer); |
|
2499 query.setQuery(QLatin1String("1, ")); |
|
2500 QVERIFY(!query.isValid()); |
|
2501 } |
|
2502 |
|
2503 /* Check that the base URI passes through. */ |
|
2504 { |
|
2505 QXmlQuery query; |
|
2506 query.setQuery(QLatin1String("string(static-base-uri())"), QUrl::fromEncoded("http://www.example.com/QIODeviceQUrl")); |
|
2507 QVERIFY(query.isValid()); |
|
2508 |
|
2509 QStringList result; |
|
2510 query.evaluateTo(&result); |
|
2511 QCOMPARE(result, QStringList(QLatin1String("http://www.example.com/QIODeviceQUrl"))); |
|
2512 } |
|
2513 } |
|
2514 |
|
2515 void tst_QXmlQuery::setQueryQUrlSuccess() const |
|
2516 { |
|
2517 #if defined(Q_OS_WINCE) && !defined(_X86_) |
|
2518 QStringList testsToSkip; |
|
2519 testsToSkip << "A valid query via the ftp scheme" << "A valid query via the http scheme"; |
|
2520 if (testsToSkip.contains(QTest::currentDataTag())) |
|
2521 QSKIP("Network tests are currently unsupported on Windows CE.", SkipSingle); |
|
2522 #endif |
|
2523 |
|
2524 QFETCH(QUrl, queryURI); |
|
2525 QFETCH(QByteArray, expectedOutput); |
|
2526 |
|
2527 QVERIFY(queryURI.isValid()); |
|
2528 |
|
2529 QXmlQuery query; |
|
2530 |
|
2531 MessageSilencer silencer; |
|
2532 query.setMessageHandler(&silencer); |
|
2533 |
|
2534 query.setQuery(queryURI); |
|
2535 QVERIFY(query.isValid()); |
|
2536 |
|
2537 QByteArray out; |
|
2538 QBuffer buffer(&out); |
|
2539 QVERIFY(buffer.open(QIODevice::WriteOnly)); |
|
2540 QXmlSerializer serializer(query, &buffer); |
|
2541 |
|
2542 query.evaluateTo(&serializer); |
|
2543 QCOMPARE(out, expectedOutput); |
|
2544 } |
|
2545 |
|
2546 void tst_QXmlQuery::setQueryQUrlSuccess_data() const |
|
2547 { |
|
2548 QTest::addColumn<QUrl>("queryURI"); |
|
2549 QTest::addColumn<QByteArray>("expectedOutput"); |
|
2550 |
|
2551 QTest::newRow("A valid query via the data scheme") |
|
2552 << QUrl::fromEncoded("data:application/xml,1%20%2B%201") /* "1 + 1" */ |
|
2553 << QByteArray("2"); |
|
2554 |
|
2555 QTest::newRow("A valid query via the file scheme") |
|
2556 << QUrl::fromLocalFile(inputFile(QLatin1String(queriesDirectory) + QLatin1String("onePlusOne.xq"))) |
|
2557 << QByteArray("2"); |
|
2558 |
|
2559 if(!m_testNetwork) |
|
2560 return; |
|
2561 |
|
2562 QTest::newRow("A valid query via the ftp scheme") |
|
2563 << QUrl::fromEncoded(QString("ftp://" + QtNetworkSettings::serverName() + "/pub/qxmlquery/viaFtp.xq").toLatin1()) |
|
2564 << QByteArray("This was received via FTP"); |
|
2565 |
|
2566 QTest::newRow("A valid query via the http scheme") |
|
2567 << QUrl::fromEncoded(QString("http://" + QtNetworkSettings::serverName() + "/qxmlquery/viaHttp.xq").toLatin1()) |
|
2568 << QByteArray("This was received via HTTP."); |
|
2569 } |
|
2570 |
|
2571 void tst_QXmlQuery::setQueryQUrlFailSucceed() const |
|
2572 { |
|
2573 QXmlQuery query; |
|
2574 MessageSilencer silencer; |
|
2575 |
|
2576 query.setMessageHandler(&silencer); |
|
2577 |
|
2578 query.setQuery(QLatin1String("1 + 1")); |
|
2579 QVERIFY(query.isValid()); |
|
2580 |
|
2581 query.setQuery(QUrl::fromEncoded("file://example.com/does/not/exist")); |
|
2582 QVERIFY(!query.isValid()); |
|
2583 } |
|
2584 |
|
2585 void tst_QXmlQuery::setQueryQUrlFailure() const |
|
2586 { |
|
2587 QFETCH(QUrl, queryURI); |
|
2588 |
|
2589 MessageSilencer silencer; |
|
2590 |
|
2591 QXmlQuery query; |
|
2592 query.setMessageHandler(&silencer); |
|
2593 query.setQuery(queryURI); |
|
2594 QVERIFY(!query.isValid()); |
|
2595 } |
|
2596 |
|
2597 void tst_QXmlQuery::setQueryQUrlFailure_data() const |
|
2598 { |
|
2599 QTest::addColumn<QUrl>("queryURI"); |
|
2600 |
|
2601 QTest::newRow("Query via file:// that does not exist.") |
|
2602 << QUrl::fromEncoded("file://example.com/does/not/exist"); |
|
2603 |
|
2604 QTest::newRow("A query via file:// that is completely empty, but readable.") |
|
2605 << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("../xmlpatterns/queries/completelyEmptyQuery.xq")); |
|
2606 |
|
2607 { |
|
2608 const QString name(QLatin1String("nonReadableFile.xq")); |
|
2609 QFile outFile(name); |
|
2610 QVERIFY(outFile.open(QIODevice::WriteOnly)); |
|
2611 outFile.write(QByteArray("1")); |
|
2612 outFile.close(); |
|
2613 /* On some windows versions, this fails, so we don't check that this works with QVERIFY. */ |
|
2614 outFile.setPermissions(QFile::Permissions(QFile::Permissions())); |
|
2615 |
|
2616 QTest::newRow("Query via file:/ that does not have read permissions.") |
|
2617 << QUrl::fromLocalFile(QCoreApplication::applicationFilePath()).resolved(QUrl("nonReadableFile.xq")); |
|
2618 } |
|
2619 |
|
2620 if(!m_testNetwork) |
|
2621 return; |
|
2622 |
|
2623 QTest::newRow("Query via HTTP that does not exist.") |
|
2624 << QUrl::fromEncoded("http://example.com/NoQuery/ISWear"); |
|
2625 |
|
2626 /* |
|
2627 QTest::newRow("Query via FTP that does not exist.") |
|
2628 << QUrl::fromEncoded("ftp://example.com/NoQuery/ISWear"); |
|
2629 */ |
|
2630 |
|
2631 QTest::newRow("A query via http:// that is completely empty, but readable.") |
|
2632 << QUrl::fromEncoded(QString( |
|
2633 "http://" + QtNetworkSettings::serverName() + "/qxmlquery/completelyEmptyQuery.xq").toLatin1()); |
|
2634 |
|
2635 QTest::newRow("A query via ftp:// that is completely empty, but readable.") |
|
2636 << QUrl::fromEncoded(QString( |
|
2637 "ftp://" + QtNetworkSettings::serverName() + "qxmlquery/completelyEmptyQuery.xq").toLatin1()); |
|
2638 |
|
2639 } |
|
2640 |
|
2641 void tst_QXmlQuery::setQueryQUrlBaseURI() const |
|
2642 { |
|
2643 QFETCH(QUrl, inputBaseURI); |
|
2644 QFETCH(QUrl, expectedBaseURI); |
|
2645 |
|
2646 QXmlQuery query; |
|
2647 |
|
2648 query.setQuery(QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq")), inputBaseURI); |
|
2649 QVERIFY(query.isValid()); |
|
2650 |
|
2651 QStringList result; |
|
2652 QVERIFY(query.evaluateTo(&result)); |
|
2653 QCOMPARE(result.count(), 1); |
|
2654 |
|
2655 if(qstrcmp(QTest::currentDataTag(), "Relative base URI") == 0) |
|
2656 checkBaseURI(QUrl(result.first()), QCoreApplication::applicationFilePath()); |
|
2657 else |
|
2658 QCOMPARE(result.first(), expectedBaseURI.toString()); |
|
2659 } |
|
2660 |
|
2661 void tst_QXmlQuery::setQueryQUrlBaseURI_data() const |
|
2662 { |
|
2663 QTest::addColumn<QUrl>("inputBaseURI"); |
|
2664 QTest::addColumn<QUrl>("expectedBaseURI"); |
|
2665 |
|
2666 QTest::newRow("absolute HTTP") |
|
2667 << QUrl(QLatin1String("http://www.example.com/")) |
|
2668 << QUrl(QLatin1String("http://www.example.com/")); |
|
2669 |
|
2670 QTest::newRow("None, so the query URI is used") |
|
2671 << QUrl() |
|
2672 << QUrl(QLatin1String("qrc:/QXmlQueryTestData/queries/staticBaseURI.xq")); |
|
2673 |
|
2674 QTest::newRow("Relative base URI") |
|
2675 << QUrl(QLatin1String("../data/relative.uri")) |
|
2676 << QUrl(); |
|
2677 } |
|
2678 |
|
2679 /*! |
|
2680 1. Create a valid query. |
|
2681 2. Call setQuery(QUrl), with a query file that doesn't exist. |
|
2682 3. Verify that the query has changed state into invalid. |
|
2683 */ |
|
2684 void tst_QXmlQuery::setQueryWithNonExistentQUrlOnValidQuery() const |
|
2685 { |
|
2686 QXmlQuery query; |
|
2687 |
|
2688 MessageSilencer messageSilencer; |
|
2689 query.setMessageHandler(&messageSilencer); |
|
2690 |
|
2691 query.setQuery(QLatin1String("1 + 1")); |
|
2692 QVERIFY(query.isValid()); |
|
2693 |
|
2694 query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/DOESNOTEXIST.xq")); |
|
2695 QVERIFY(!query.isValid()); |
|
2696 } |
|
2697 |
|
2698 /*! |
|
2699 1. Create a valid query. |
|
2700 2. Call setQuery(QUrl), with a query file that is invalid. |
|
2701 3. Verify that the query has changed state into invalid. |
|
2702 */ |
|
2703 void tst_QXmlQuery::setQueryWithInvalidQueryFromQUrlOnValidQuery() const |
|
2704 { |
|
2705 QXmlQuery query; |
|
2706 |
|
2707 MessageSilencer messageSilencer; |
|
2708 query.setMessageHandler(&messageSilencer); |
|
2709 |
|
2710 query.setQuery(QLatin1String("1 + 1")); |
|
2711 QVERIFY(query.isValid()); |
|
2712 |
|
2713 query.setQuery(QUrl::fromEncoded("qrc:/QXmlQueryTestData/queries/syntaxError.xq")); |
|
2714 QVERIFY(!query.isValid()); |
|
2715 } |
|
2716 |
|
2717 /*! |
|
2718 This triggered two bugs: |
|
2719 |
|
2720 - First, the DynamicContext wasn't assigned to QXmlResultItems, meaning it went out of |
|
2721 scope and therefore deallocated the document pool, and calls |
|
2722 to QXmlResultItems::next() would use dangling pointers. |
|
2723 |
|
2724 - Conversion between QPatternist::Item and QXmlItem was incorrectly done, leading to nodes |
|
2725 being treated as atomic values, and subsequent crashes. |
|
2726 |
|
2727 */ |
|
2728 void tst_QXmlQuery::retrieveNameFromQuery() const |
|
2729 { |
|
2730 QFETCH(QString, queryString); |
|
2731 QFETCH(QString, expectedName); |
|
2732 |
|
2733 QXmlQuery query; |
|
2734 query.setQuery(queryString); |
|
2735 QVERIFY(query.isValid()); |
|
2736 QXmlResultItems result; |
|
2737 query.evaluateTo(&result); |
|
2738 |
|
2739 QVERIFY(!result.hasError()); |
|
2740 |
|
2741 const QXmlItem item(result.next()); |
|
2742 QVERIFY(!result.hasError()); |
|
2743 QVERIFY(!item.isNull()); |
|
2744 QVERIFY(item.isNode()); |
|
2745 |
|
2746 const QXmlNodeModelIndex node(item.toNodeModelIndex()); |
|
2747 QVERIFY(!node.isNull()); |
|
2748 |
|
2749 QCOMPARE(node.model()->name(node).localName(query.namePool()), expectedName); |
|
2750 } |
|
2751 |
|
2752 void tst_QXmlQuery::retrieveNameFromQuery_data() const |
|
2753 { |
|
2754 QTest::addColumn<QString>("queryString"); |
|
2755 QTest::addColumn<QString>("expectedName"); |
|
2756 |
|
2757 QTest::newRow("Document-node") |
|
2758 << QString::fromLatin1("document{<elementName/>}") |
|
2759 << QString(); |
|
2760 |
|
2761 QTest::newRow("Element") |
|
2762 << QString::fromLatin1("document{<elementName/>}/*") |
|
2763 << QString::fromLatin1("elementName"); |
|
2764 } |
|
2765 |
|
2766 /*! |
|
2767 Binding a null QString leads to no variable binding, but an |
|
2768 empty non-null QString is possible. |
|
2769 */ |
|
2770 void tst_QXmlQuery::bindEmptyNullString() const |
|
2771 { |
|
2772 MessageSilencer messageHandler; |
|
2773 QXmlQuery query; |
|
2774 query.setMessageHandler(&messageHandler); |
|
2775 query.setQuery(QLatin1String("declare variable $v external; $v")); |
|
2776 /* Here, we effectively pass an invalid QVariant. */ |
|
2777 query.bindVariable(QLatin1String("v"), QVariant(QString())); |
|
2778 QVERIFY(!query.isValid()); |
|
2779 |
|
2780 QStringList result; |
|
2781 QVERIFY(!query.evaluateTo(&result)); |
|
2782 } |
|
2783 |
|
2784 void tst_QXmlQuery::bindEmptyString() const |
|
2785 { |
|
2786 QXmlQuery query; |
|
2787 query.bindVariable(QLatin1String("v"), QVariant(QString(QLatin1String("")))); |
|
2788 query.setQuery(QLatin1String("declare variable $v external; $v")); |
|
2789 QVERIFY(query.isValid()); |
|
2790 |
|
2791 QStringList result; |
|
2792 QVERIFY(query.evaluateTo(&result)); |
|
2793 QStringList expected((QString())); |
|
2794 QCOMPARE(result, expected); |
|
2795 } |
|
2796 |
|
2797 void tst_QXmlQuery::cleanupTestCase() const |
|
2798 { |
|
2799 /* Remove a weird file we created. */ |
|
2800 const QString name(QLatin1String("nonReadableFile.xq")); |
|
2801 |
|
2802 if(QFile::exists(name)) |
|
2803 { |
|
2804 QFile file(name); |
|
2805 QVERIFY(file.setPermissions(QFile::WriteOwner)); |
|
2806 QVERIFY(file.remove()); |
|
2807 } |
|
2808 } |
|
2809 |
|
2810 void tst_QXmlQuery::declareUnavailableExternal() const |
|
2811 { |
|
2812 QXmlQuery query; |
|
2813 MessageSilencer silencer; |
|
2814 query.setMessageHandler(&silencer); |
|
2815 query.setQuery(QLatin1String("declare variable $var external;" |
|
2816 "1 + 1")); |
|
2817 /* We do not bind $var with QXmlQuery::bindVariable(). */ |
|
2818 QVERIFY(!query.isValid()); |
|
2819 } |
|
2820 |
|
2821 /*! |
|
2822 This test triggers an assert in one of the cache iterator |
|
2823 with MSVC 2005 when compiled in debug mode. |
|
2824 */ |
|
2825 void tst_QXmlQuery::msvcCacheIssue() const |
|
2826 { |
|
2827 QXmlQuery query; |
|
2828 query.bindVariable(QLatin1String("externalVariable"), QXmlItem("Variable Value")); |
|
2829 query.setQuery(QUrl::fromLocalFile(QLatin1String(queriesDirectory) + QString::fromLatin1("externalVariableUsedTwice.xq"))); |
|
2830 QStringList result; |
|
2831 QVERIFY(query.evaluateTo(&result)); |
|
2832 |
|
2833 QCOMPARE(result, |
|
2834 QStringList() << QString::fromLatin1("Variable Value") << QString::fromLatin1("Variable Value")); |
|
2835 } |
|
2836 |
|
2837 void tst_QXmlQuery::unavailableExternalVariable() const |
|
2838 { |
|
2839 QXmlQuery query; |
|
2840 |
|
2841 MessageSilencer silencer; |
|
2842 query.setMessageHandler(&silencer); |
|
2843 |
|
2844 query.setQuery(QLatin1String("declare variable $foo external; 1")); |
|
2845 |
|
2846 QVERIFY(!query.isValid()); |
|
2847 } |
|
2848 |
|
2849 /*! |
|
2850 Ensure that setUriResolver() affects \c fn:doc() and \c fn:doc-available(). |
|
2851 */ |
|
2852 void tst_QXmlQuery::useUriResolver() const |
|
2853 { |
|
2854 class TestUriResolver : public QAbstractUriResolver |
|
2855 , private TestFundament |
|
2856 { |
|
2857 public: |
|
2858 virtual QUrl resolve(const QUrl &relative, |
|
2859 const QUrl &baseURI) const |
|
2860 { |
|
2861 Q_UNUSED(relative); |
|
2862 return baseURI.resolved(inputFile(QLatin1String(queriesDirectory) + QLatin1String("simpleDocument.xml"))); |
|
2863 } |
|
2864 }; |
|
2865 |
|
2866 const TestUriResolver uriResolver; |
|
2867 QXmlQuery query; |
|
2868 |
|
2869 query.setUriResolver(&uriResolver); |
|
2870 query.setQuery(QLatin1String("let $i := 'http://www.example.com/DoesNotExist'" |
|
2871 "return (string(doc($i)), doc-available($i))")); |
|
2872 |
|
2873 |
|
2874 QXmlResultItems result; |
|
2875 query.evaluateTo(&result); |
|
2876 |
|
2877 QVERIFY(!result.hasError()); |
|
2878 QCOMPARE(result.next().toAtomicValue().toString(), QString::fromLatin1("text text node")); |
|
2879 QCOMPARE(result.next().toAtomicValue().toBool(), true); |
|
2880 QVERIFY(result.next().isNull()); |
|
2881 QVERIFY(!result.hasError()); |
|
2882 } |
|
2883 |
|
2884 void tst_QXmlQuery::queryWithFocusAndVariable() const |
|
2885 { |
|
2886 QXmlQuery query; |
|
2887 query.setFocus(QXmlItem(5)); |
|
2888 query.bindVariable(QLatin1String("var"), QXmlItem(2)); |
|
2889 |
|
2890 query.setQuery(QLatin1String("string(. * $var)")); |
|
2891 |
|
2892 QStringList result; |
|
2893 |
|
2894 QVERIFY(query.evaluateTo(&result)); |
|
2895 |
|
2896 QCOMPARE(result, QStringList(QLatin1String("10"))); |
|
2897 } |
|
2898 |
|
2899 void tst_QXmlQuery::undefinedFocus() const |
|
2900 { |
|
2901 QXmlQuery query; |
|
2902 |
|
2903 MessageSilencer silencer; |
|
2904 query.setMessageHandler(&silencer); |
|
2905 |
|
2906 query.setQuery(QLatin1String(".")); |
|
2907 QVERIFY(!query.isValid()); |
|
2908 } |
|
2909 |
|
2910 void tst_QXmlQuery::basicFocusUsage() const |
|
2911 { |
|
2912 QXmlQuery query; |
|
2913 |
|
2914 MessageSilencer silencer; |
|
2915 query.setMessageHandler(&silencer); |
|
2916 |
|
2917 query.setFocus(QXmlItem(5)); |
|
2918 query.setQuery(QLatin1String("string(. * .)")); |
|
2919 QVERIFY(query.isValid()); |
|
2920 |
|
2921 QStringList result; |
|
2922 QVERIFY(query.evaluateTo(&result)); |
|
2923 |
|
2924 QCOMPARE(result, QStringList(QLatin1String("25"))); |
|
2925 } |
|
2926 |
|
2927 /*! |
|
2928 Triggers an ownership related crash. |
|
2929 */ |
|
2930 void tst_QXmlQuery::copyCheckMessageHandler() const |
|
2931 { |
|
2932 QXmlQuery query; |
|
2933 QCOMPARE(query.messageHandler(), static_cast<QAbstractMessageHandler *>(0)); |
|
2934 |
|
2935 query.setQuery(QLatin1String("doc('qrc:/QXmlQueryTestData/data/oneElement.xml')")); |
|
2936 /* By now, we should have set the builtin message handler. */ |
|
2937 const QAbstractMessageHandler *const messageHandler = query.messageHandler(); |
|
2938 QVERIFY(messageHandler); |
|
2939 |
|
2940 { |
|
2941 /* This copies QXmlQueryPrivate::m_ownerObject, and its destructor |
|
2942 * will delete it, and hence the builtin message handler attached to it. */ |
|
2943 QXmlQuery copy(query); |
|
2944 } |
|
2945 |
|
2946 QXmlResultItems result; |
|
2947 query.evaluateTo(&result); |
|
2948 |
|
2949 while(!result.next().isNull()) |
|
2950 { |
|
2951 } |
|
2952 QVERIFY(!result.hasError()); |
|
2953 } |
|
2954 |
|
2955 void tst_QXmlQuery::queryLanguage() const |
|
2956 { |
|
2957 /* Check default value. */ |
|
2958 { |
|
2959 const QXmlQuery query; |
|
2960 QCOMPARE(query.queryLanguage(), QXmlQuery::XQuery10); |
|
2961 } |
|
2962 |
|
2963 /* Check default value of copies default instance. */ |
|
2964 { |
|
2965 const QXmlQuery query1; |
|
2966 const QXmlQuery query2(query1); |
|
2967 |
|
2968 QCOMPARE(query1.queryLanguage(), QXmlQuery::XQuery10); |
|
2969 QCOMPARE(query2.queryLanguage(), QXmlQuery::XQuery10); |
|
2970 } |
|
2971 } |
|
2972 |
|
2973 void tst_QXmlQuery::queryLanguageSignature() const |
|
2974 { |
|
2975 /* This getter should be const. */ |
|
2976 QXmlQuery query; |
|
2977 query.queryLanguage(); |
|
2978 } |
|
2979 |
|
2980 void tst_QXmlQuery::enumQueryLanguage() const |
|
2981 { |
|
2982 /* These enum values should be possible to OR for future plans. */ |
|
2983 QCOMPARE(int(QXmlQuery::XQuery10), 1); |
|
2984 QCOMPARE(int(QXmlQuery::XSLT20), 2); |
|
2985 QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintSelector), 1024); |
|
2986 QCOMPARE(int(QXmlQuery::XmlSchema11IdentityConstraintField), 2048); |
|
2987 QCOMPARE(int(QXmlQuery::XPath20), 4096); |
|
2988 } |
|
2989 |
|
2990 void tst_QXmlQuery::setInitialTemplateNameQXmlName() const |
|
2991 { |
|
2992 QXmlQuery query(QXmlQuery::XSLT20); |
|
2993 QXmlNamePool np(query.namePool()); |
|
2994 const QXmlName name(np, QLatin1String("main")); |
|
2995 |
|
2996 query.setInitialTemplateName(name); |
|
2997 |
|
2998 QCOMPARE(query.initialTemplateName(), name); |
|
2999 |
|
3000 query.setQuery(QUrl(inputFile(QLatin1String(SRCDIR "../xmlpatterns/stylesheets/namedTemplate.xsl")))); |
|
3001 QVERIFY(query.isValid()); |
|
3002 |
|
3003 QBuffer result; |
|
3004 QVERIFY(result.open(QIODevice::ReadWrite)); |
|
3005 QXmlSerializer serializer(query, &result); |
|
3006 query.evaluateTo(&serializer); |
|
3007 |
|
3008 QCOMPARE(result.data(), QByteArray("1 2 3 4 5")); |
|
3009 |
|
3010 // TODO invoke a template which has required params. |
|
3011 } |
|
3012 |
|
3013 void tst_QXmlQuery::setInitialTemplateNameQXmlNameSignature() const |
|
3014 { |
|
3015 QXmlQuery query; |
|
3016 QXmlNamePool np(query.namePool()); |
|
3017 const QXmlName name(np, QLatin1String("foo")); |
|
3018 |
|
3019 /* The signature should take a const reference. */ |
|
3020 query.setInitialTemplateName(name); |
|
3021 } |
|
3022 |
|
3023 void tst_QXmlQuery::setInitialTemplateNameQString() const |
|
3024 { |
|
3025 QXmlQuery query; |
|
3026 QXmlNamePool np(query.namePool()); |
|
3027 query.setInitialTemplateName(QLatin1String("foo")); |
|
3028 |
|
3029 QCOMPARE(query.initialTemplateName(), QXmlName(np, QLatin1String("foo"))); |
|
3030 } |
|
3031 |
|
3032 void tst_QXmlQuery::setInitialTemplateNameQStringSignature() const |
|
3033 { |
|
3034 const QString name(QLatin1String("name")); |
|
3035 QXmlQuery query; |
|
3036 |
|
3037 /* We should take a const reference. */ |
|
3038 query.setInitialTemplateName(name); |
|
3039 } |
|
3040 |
|
3041 void tst_QXmlQuery::initialTemplateName() const |
|
3042 { |
|
3043 /* Check our default value. */ |
|
3044 QXmlQuery query; |
|
3045 QCOMPARE(query.initialTemplateName(), QXmlName()); |
|
3046 QVERIFY(query.initialTemplateName().isNull()); |
|
3047 } |
|
3048 |
|
3049 void tst_QXmlQuery::initialTemplateNameSignature() const |
|
3050 { |
|
3051 const QXmlQuery query; |
|
3052 /* This should be a const member. */ |
|
3053 query.initialTemplateName(); |
|
3054 } |
|
3055 |
|
3056 void tst_QXmlQuery::setNetworkAccessManager() const |
|
3057 { |
|
3058 |
|
3059 /* Ensure fn:doc() picks up the right QNetworkAccessManager. */ |
|
3060 { |
|
3061 NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")), |
|
3062 QUrl(inputFile(QLatin1String(SRCDIR "../xmlpatterns/queries/simpleDocument.xml")))); |
|
3063 |
|
3064 QXmlQuery query; |
|
3065 query.setNetworkAccessManager(&networkOverrider); |
|
3066 query.setQuery(QLatin1String("string(doc('tag:example.com:DOESNOTEXIST'))")); |
|
3067 QVERIFY(query.isValid()); |
|
3068 |
|
3069 QStringList result; |
|
3070 QVERIFY(query.evaluateTo(&result)); |
|
3071 |
|
3072 QCOMPARE(result, QStringList(QLatin1String("text text node"))); |
|
3073 } |
|
3074 |
|
3075 /* Ensure setQuery() is using the right network manager. */ |
|
3076 { |
|
3077 NetworkOverrider networkOverrider(QUrl(QLatin1String("tag:example.com:DOESNOTEXIST")), |
|
3078 QUrl(inputFile(QLatin1String(SRCDIR "../xmlpatterns/queries/concat.xq")))); |
|
3079 |
|
3080 QXmlQuery query; |
|
3081 query.setNetworkAccessManager(&networkOverrider); |
|
3082 query.setQuery(QUrl("tag:example.com:DOESNOTEXIST")); |
|
3083 QVERIFY(query.isValid()); |
|
3084 |
|
3085 QStringList result; |
|
3086 QVERIFY(query.evaluateTo(&result)); |
|
3087 |
|
3088 QCOMPARE(result, QStringList(QLatin1String("abcdef"))); |
|
3089 } |
|
3090 } |
|
3091 void tst_QXmlQuery::networkAccessManagerSignature() const |
|
3092 { |
|
3093 /* Const object. */ |
|
3094 const QXmlQuery query; |
|
3095 |
|
3096 /* The function should be const. */ |
|
3097 query.networkAccessManager(); |
|
3098 } |
|
3099 |
|
3100 void tst_QXmlQuery::networkAccessManagerDefaultValue() const |
|
3101 { |
|
3102 const QXmlQuery query; |
|
3103 |
|
3104 QCOMPARE(query.networkAccessManager(), static_cast<QNetworkAccessManager *>(0)); |
|
3105 } |
|
3106 |
|
3107 void tst_QXmlQuery::networkAccessManager() const |
|
3108 { |
|
3109 /* Test that we return the network manager that was set. */ |
|
3110 { |
|
3111 QNetworkAccessManager manager; |
|
3112 QXmlQuery query; |
|
3113 query.setNetworkAccessManager(&manager); |
|
3114 QCOMPARE(query.networkAccessManager(), &manager); |
|
3115 } |
|
3116 } |
|
3117 |
|
3118 /*! |
|
3119 \internal |
|
3120 \since 4.5 |
|
3121 |
|
3122 1. Load a document into QXmlQuery's document cache, by executing a query which does it. |
|
3123 2. Set a focus |
|
3124 3. Change query, to one which uses the focus |
|
3125 4. Evaluate |
|
3126 |
|
3127 Used to crash. |
|
3128 */ |
|
3129 void tst_QXmlQuery::multipleDocsAndFocus() const |
|
3130 { |
|
3131 QXmlQuery query; |
|
3132 |
|
3133 /* We use string concatenation, since variable bindings might disturb what |
|
3134 * we're testing. */ |
|
3135 query.setQuery(QLatin1String("string(doc('") + |
|
3136 inputFile(QLatin1String(SRCDIR "../xmlpatterns/queries/simpleDocument.xml")) + |
|
3137 QLatin1String("'))")); |
|
3138 query.setFocus(QUrl(inputFile(QLatin1String(SRCDIR "../xmlpatterns/stylesheets/documentElement.xml")))); |
|
3139 query.setQuery(QLatin1String("string(.)")); |
|
3140 |
|
3141 QStringList result; |
|
3142 QVERIFY(query.evaluateTo(&result)); |
|
3143 } |
|
3144 |
|
3145 /*! |
|
3146 \internal |
|
3147 \since 4.5 |
|
3148 |
|
3149 1. Set a focus |
|
3150 2. Set a query |
|
3151 3. Evaluate |
|
3152 4. Change focus |
|
3153 5. Evaluate |
|
3154 |
|
3155 Used to crash. |
|
3156 */ |
|
3157 void tst_QXmlQuery::multipleEvaluationsWithDifferentFocus() const |
|
3158 { |
|
3159 QXmlQuery query; |
|
3160 QStringList result; |
|
3161 |
|
3162 query.setFocus(QUrl(inputFile(QLatin1String(SRCDIR "../xmlpatterns/stylesheets/documentElement.xml")))); |
|
3163 query.setQuery(QLatin1String("string(.)")); |
|
3164 QVERIFY(query.evaluateTo(&result)); |
|
3165 |
|
3166 query.setFocus(QUrl(inputFile(QLatin1String(SRCDIR "../xmlpatterns/stylesheets/documentElement.xml")))); |
|
3167 QVERIFY(query.evaluateTo(&result)); |
|
3168 } |
|
3169 |
|
3170 void tst_QXmlQuery::bindVariableQXmlQuery() const |
|
3171 { |
|
3172 QFETCH(QString, query1); |
|
3173 QFETCH(QString, query2); |
|
3174 QFETCH(QString, expectedOutput); |
|
3175 QFETCH(bool, expectedSuccess); |
|
3176 |
|
3177 MessageSilencer silencer; |
|
3178 QXmlQuery xmlQuery1; |
|
3179 xmlQuery1.setMessageHandler(&silencer); |
|
3180 xmlQuery1.setQuery(query1); |
|
3181 |
|
3182 QXmlQuery xmlQuery2(xmlQuery1); |
|
3183 xmlQuery2.bindVariable("query1", xmlQuery1); |
|
3184 xmlQuery2.setQuery(query2); |
|
3185 |
|
3186 QString output; |
|
3187 const bool querySuccess = xmlQuery2.evaluateTo(&output); |
|
3188 |
|
3189 QCOMPARE(querySuccess, expectedSuccess); |
|
3190 |
|
3191 if(querySuccess) |
|
3192 QCOMPARE(output, expectedOutput); |
|
3193 } |
|
3194 |
|
3195 void tst_QXmlQuery::bindVariableQXmlQuery_data() const |
|
3196 { |
|
3197 QTest::addColumn<QString>("query1"); |
|
3198 QTest::addColumn<QString>("query2"); |
|
3199 QTest::addColumn<QString>("expectedOutput"); |
|
3200 QTest::addColumn<bool>("expectedSuccess"); |
|
3201 |
|
3202 QTest::newRow("First query has one atomic value.") |
|
3203 << "2" |
|
3204 << "1, $query1, 3" |
|
3205 << "1 2 3\n" |
|
3206 << true; |
|
3207 |
|
3208 QTest::newRow("First query has two atomic values.") |
|
3209 << "2, 3" |
|
3210 << "1, $query1, 4" |
|
3211 << "1 2 3 4\n" |
|
3212 << true; |
|
3213 |
|
3214 QTest::newRow("First query is a node.") |
|
3215 << "<e/>" |
|
3216 << "1, $query1, 3" |
|
3217 << "1<e/>3\n" |
|
3218 << true; |
|
3219 |
|
3220 /* This is a good test, because it triggers the exception in the |
|
3221 * bindVariable() call, as supposed to when the actual evaluation is done. |
|
3222 */ |
|
3223 QTest::newRow("First query has a dynamic error.") |
|
3224 << "error()" |
|
3225 << "1, $query1" |
|
3226 << QString() /* We don't care. */ |
|
3227 << false; |
|
3228 } |
|
3229 |
|
3230 void tst_QXmlQuery::bindVariableQStringQXmlQuerySignature() const |
|
3231 { |
|
3232 QXmlQuery query1; |
|
3233 query1.setQuery("'dummy'"); |
|
3234 |
|
3235 QXmlQuery query2; |
|
3236 const QString name(QLatin1String("name")); |
|
3237 |
|
3238 /* We should be able to take a const QXmlQuery reference. Evaluation never mutate |
|
3239 * QXmlQuery, and evaluation is what we do here. */ |
|
3240 query2.bindVariable(name, const_cast<const QXmlQuery &>(query1)); |
|
3241 } |
|
3242 |
|
3243 void tst_QXmlQuery::bindVariableQXmlNameQXmlQuerySignature() const |
|
3244 { |
|
3245 QXmlNamePool np; |
|
3246 QXmlQuery query1(np); |
|
3247 query1.setQuery("'dummy'"); |
|
3248 |
|
3249 QXmlQuery query2; |
|
3250 const QXmlName name(np, QLatin1String("name")); |
|
3251 |
|
3252 /* We should be able to take a const QXmlQuery reference. Evaluation never mutate |
|
3253 * QXmlQuery, and evaluation is what we do here. */ |
|
3254 query2.bindVariable(name, const_cast<const QXmlQuery &>(query1)); |
|
3255 } |
|
3256 |
|
3257 /*! |
|
3258 Check that the QXmlName is handled correctly. |
|
3259 */ |
|
3260 void tst_QXmlQuery::bindVariableQXmlNameQXmlQuery() const |
|
3261 { |
|
3262 QXmlNamePool np; |
|
3263 QXmlQuery query1; |
|
3264 query1.setQuery(QLatin1String("1")); |
|
3265 |
|
3266 QXmlQuery query2(np); |
|
3267 query2.bindVariable(QXmlName(np, QLatin1String("theName")), query1); |
|
3268 query2.setQuery("$theName"); |
|
3269 |
|
3270 QString result; |
|
3271 query2.evaluateTo(&result); |
|
3272 |
|
3273 QCOMPARE(result, QString::fromLatin1("1\n")); |
|
3274 } |
|
3275 |
|
3276 void tst_QXmlQuery::bindVariableQXmlQueryInvalidate() const |
|
3277 { |
|
3278 QXmlQuery query; |
|
3279 query.bindVariable(QLatin1String("name"), QVariant(1)); |
|
3280 query.setQuery("$name"); |
|
3281 QVERIFY(query.isValid()); |
|
3282 |
|
3283 QXmlQuery query2; |
|
3284 query2.setQuery("'query2'"); |
|
3285 |
|
3286 query.bindVariable(QLatin1String("name"), query); |
|
3287 QVERIFY(!query.isValid()); |
|
3288 } |
|
3289 |
|
3290 void tst_QXmlQuery::unknownSourceLocation() const |
|
3291 { |
|
3292 QBuffer b; |
|
3293 b.setData("<a><b/><b/></a>"); |
|
3294 b.open(QIODevice::ReadOnly); |
|
3295 |
|
3296 MessageSilencer silencer; |
|
3297 QXmlQuery query; |
|
3298 query.bindVariable(QLatin1String("inputDocument"), &b); |
|
3299 query.setMessageHandler(&silencer); |
|
3300 |
|
3301 query.setQuery(QLatin1String("doc($inputDocument)/a/(let $v := b/string() return if ($v) then $v else ())")); |
|
3302 |
|
3303 QString output; |
|
3304 query.evaluateTo(&output); |
|
3305 } |
|
3306 |
|
3307 void tst_QXmlQuery::identityConstraintSuccess() const |
|
3308 { |
|
3309 QXmlQuery::QueryLanguage queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintSelector; |
|
3310 |
|
3311 /* We run this code for Selector and Field. */ |
|
3312 for(int i = 0; i < 3; ++i) |
|
3313 { |
|
3314 QXmlNamePool namePool; |
|
3315 QXmlResultItems result; |
|
3316 QXmlItem node; |
|
3317 |
|
3318 { |
|
3319 QXmlQuery nodeSource(namePool); |
|
3320 nodeSource.setQuery(QLatin1String("<e/>")); |
|
3321 |
|
3322 nodeSource.evaluateTo(&result); |
|
3323 node = result.next(); |
|
3324 } |
|
3325 |
|
3326 /* Basic use: |
|
3327 * 1. The focus is undefined, but it's still valid. |
|
3328 * 2. We never evaluate. */ |
|
3329 { |
|
3330 QXmlQuery query(queryLanguage); |
|
3331 query.setQuery(QLatin1String("a")); |
|
3332 QVERIFY(query.isValid()); |
|
3333 } |
|
3334 |
|
3335 /* Basic use: |
|
3336 * 1. The focus is undefined, but it's still valid. |
|
3337 * 2. We afterwards set the focus. */ |
|
3338 { |
|
3339 QXmlQuery query(queryLanguage, namePool); |
|
3340 query.setQuery(QLatin1String("a")); |
|
3341 query.setFocus(node); |
|
3342 QVERIFY(query.isValid()); |
|
3343 } |
|
3344 |
|
3345 /* Basic use: |
|
3346 * 1. The focus is undefined, but it's still valid. |
|
3347 * 2. We afterwards set the focus. |
|
3348 * 3. We evaluate. */ |
|
3349 { |
|
3350 QXmlQuery query(queryLanguage, namePool); |
|
3351 query.setQuery(QString(QLatin1Char('.'))); |
|
3352 query.setFocus(node); |
|
3353 QVERIFY(query.isValid()); |
|
3354 |
|
3355 QString result; |
|
3356 QVERIFY(query.evaluateTo(&result)); |
|
3357 QCOMPARE(result, QString::fromLatin1("<e/>\n")); |
|
3358 } |
|
3359 |
|
3360 /* A slightly more complex Field. */ |
|
3361 { |
|
3362 QXmlQuery query(queryLanguage); |
|
3363 query.setQuery(QLatin1String("* | .//xml:*/.")); |
|
3364 QVERIFY(query.isValid()); |
|
3365 } |
|
3366 |
|
3367 /* @ is only allowed in Field. */ |
|
3368 if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField) |
|
3369 { |
|
3370 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField); |
|
3371 query.setQuery(QLatin1String("@abc")); |
|
3372 QVERIFY(query.isValid()); |
|
3373 } |
|
3374 |
|
3375 /* Field allows attribute:: and child:: .*/ |
|
3376 if(queryLanguage == QXmlQuery::XmlSchema11IdentityConstraintField) |
|
3377 { |
|
3378 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintField); |
|
3379 query.setQuery(QLatin1String("attribute::name | child::name")); |
|
3380 QVERIFY(query.isValid()); |
|
3381 } |
|
3382 |
|
3383 /* Selector allows only child:: .*/ |
|
3384 { |
|
3385 QXmlQuery query(QXmlQuery::XmlSchema11IdentityConstraintSelector); |
|
3386 query.setQuery(QLatin1String("child::name")); |
|
3387 QVERIFY(query.isValid()); |
|
3388 } |
|
3389 |
|
3390 if(i == 0) |
|
3391 queryLanguage = QXmlQuery::XmlSchema11IdentityConstraintField; |
|
3392 else if(i == 1) |
|
3393 queryLanguage = QXmlQuery::XPath20; |
|
3394 } |
|
3395 } |
|
3396 |
|
3397 Q_DECLARE_METATYPE(QXmlQuery::QueryLanguage); |
|
3398 |
|
3399 /*! |
|
3400 We just do some basic tests for boot strapping and sanity checking. The actual regression |
|
3401 testing is in the Schema suite. |
|
3402 */ |
|
3403 void tst_QXmlQuery::identityConstraintFailure() const |
|
3404 { |
|
3405 QFETCH(QXmlQuery::QueryLanguage, queryLanguage); |
|
3406 QFETCH(QString, inputQuery); |
|
3407 |
|
3408 QXmlQuery query(queryLanguage); |
|
3409 MessageSilencer silencer; |
|
3410 query.setMessageHandler(&silencer); |
|
3411 |
|
3412 query.setQuery(inputQuery); |
|
3413 QVERIFY(!query.isValid()); |
|
3414 } |
|
3415 |
|
3416 void tst_QXmlQuery::identityConstraintFailure_data() const |
|
3417 { |
|
3418 QTest::addColumn<QXmlQuery::QueryLanguage>("queryLanguage"); |
|
3419 QTest::addColumn<QString>("inputQuery"); |
|
3420 |
|
3421 QTest::newRow("We don't have element constructors in identity constraint pattern, " |
|
3422 "it's an XQuery feature(Selector).") |
|
3423 << QXmlQuery::XmlSchema11IdentityConstraintSelector |
|
3424 << QString::fromLatin1("<e/>"); |
|
3425 |
|
3426 QTest::newRow("We don't have functions in identity constraint pattern, " |
|
3427 "it's an XPath feature(Selector).") |
|
3428 << QXmlQuery::XmlSchema11IdentityConstraintSelector |
|
3429 << QString::fromLatin1("current-time()"); |
|
3430 |
|
3431 QTest::newRow("We don't have element constructors in identity constraint pattern, " |
|
3432 "it's an XQuery feature(Field).") |
|
3433 << QXmlQuery::XmlSchema11IdentityConstraintSelector |
|
3434 << QString::fromLatin1("<e/>"); |
|
3435 |
|
3436 QTest::newRow("We don't have functions in identity constraint pattern, " |
|
3437 "it's an XPath feature(Field).") |
|
3438 << QXmlQuery::XmlSchema11IdentityConstraintSelector |
|
3439 << QString::fromLatin1("current-time()"); |
|
3440 |
|
3441 QTest::newRow("@attributeName is disallowed for the selector.") |
|
3442 << QXmlQuery::XmlSchema11IdentityConstraintSelector |
|
3443 << QString::fromLatin1("@abc"); |
|
3444 |
|
3445 QTest::newRow("attribute:: is disallowed for the selector.") |
|
3446 << QXmlQuery::XmlSchema11IdentityConstraintSelector |
|
3447 << QString::fromLatin1("attribute::name"); |
|
3448 |
|
3449 QTest::newRow("ancestor::name is disallowed for the selector.") |
|
3450 << QXmlQuery::XmlSchema11IdentityConstraintSelector |
|
3451 << QString::fromLatin1("ancestor::name"); |
|
3452 |
|
3453 QTest::newRow("ancestor::name is disallowed for the field.") |
|
3454 << QXmlQuery::XmlSchema11IdentityConstraintField |
|
3455 << QString::fromLatin1("ancestor::name"); |
|
3456 } |
|
3457 |
|
3458 QTEST_MAIN(tst_QXmlQuery) |
|
3459 |
|
3460 #include "tst_qxmlquery.moc" |
|
3461 #else //QTEST_XMLPATTERNS |
|
3462 QTEST_NOOP_MAIN |
|
3463 #endif |