1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QFile>
31#include <QtTest/QtTest>
32
33#include <QSourceLocation>
34#include <QXmlFormatter>
35#include <QXmlNamePool>
36#include <QXmlQuery>
37#include <QXmlResultItems>
38#include <QXmlSerializer>
39#include <QFileInfo>
40#include <QDir>
41
42#include "TestNodeModel.h"
43#include "LoadingModel.h"
44#include "../qxmlquery/TestFundament.h"
45
46/*!
47 \class tst_QAbstractXmlNodeModel
48 \internal
49 \since 4.4
50 \brief Tests the QAbstractXmlNodeModel class.
51 */
52class tst_QAbstractXmlNodeModel : public QObject
53 , private TestFundament
54{
55 Q_OBJECT
56
57private Q_SLOTS:
58 // TODO lots of tests missing
59 void initTestCase();
60 void constructor() const;
61 void objectSize() const;
62 void nextFromSimpleAxis();
63 void nextFromSimpleAxis_data() const;
64 void constCorrectness() const;
65 void createData() const;
66 void createPointerAdditionalData() const;
67 void createDataAdditionalData() const;
68 void id() const;
69 void idref() const;
70 void typedValue() const;
71 void sourceLocation() const;
72
73private:
74 QAbstractXmlNodeModel::Ptr m_nodeModel;
75 QXmlNamePool m_namePool;
76 QXmlNodeModelIndex m_rootNode;
77};
78
79const char testFileName[] = "tree.xml";
80
81void tst_QAbstractXmlNodeModel::initTestCase()
82{
83 const QString testFilePath = QFINDTESTDATA(testFileName);
84 QVERIFY2(!testFilePath.isEmpty(), "tree.xml not found");
85 const QString testDirectory = QFileInfo(testFilePath).absolutePath();
86 QVERIFY2(QDir::setCurrent(testDirectory), qPrintable(QStringLiteral("Could not chdir to ") + testDirectory));
87
88 m_nodeModel = LoadingModel::create(np: m_namePool);
89 QVERIFY(m_nodeModel);
90 m_rootNode = m_nodeModel->root(n: QXmlNodeModelIndex());
91 QVERIFY(!m_rootNode.isNull());
92}
93
94void tst_QAbstractXmlNodeModel::constructor() const
95{
96 /* Allocate instance. */
97 {
98 TestNodeModel instance;
99 }
100
101 {
102 TestNodeModel instance1;
103 TestNodeModel instance2;
104 }
105
106 {
107 TestNodeModel instance1;
108 TestNodeModel instance2;
109 TestNodeModel instance3;
110 }
111}
112
113void tst_QAbstractXmlNodeModel::objectSize() const
114{
115 /* We can't currently test this in portable way,
116 * so disable it. */
117 return;
118
119 const int pointerSize = sizeof(void *);
120 // adjust for pointer alignment
121 const int sharedDataSize = ((sizeof(QSharedData) + pointerSize) / pointerSize) * pointerSize;
122 const int modelSize = sizeof(QAbstractXmlNodeModel);
123
124 /* A d pointer plus a vtable pointer. */
125 QCOMPARE(modelSize, sharedDataSize + pointerSize * 2);
126}
127
128/*!
129 Tests nextFromSimpleAxis(). More exactly that all the logic in
130 QAbstractXmlNodeModel::iterate() is as we expect to. Subsequently, a lot
131 of testing code is in LoadingModel(.cpp).
132
133 Approach:
134
135 1. In initTestCase() we loaded tree.xml into LoadingModel and
136 stored the root node in m_rootNode.
137 2. We execute a query that navigates from m_rootNode and write out
138 the result using QXmlFormatter.
139 3. We execute the exact same query, but this time use the built in node backend,
140 and write out the result in the same way. This is our baseline.
141 4. Compare the two.
142
143 Hence we check QAbstractXmlNodeModel::iterate() and friends against our XQuery
144 code, which in turn is (mainly) checked by the XQTS. This means safer testing
145 since we don't create baselines manually, and it also means that we can modify
146 the input file, tree.xml, without having to update static baselines.
147 */
148void tst_QAbstractXmlNodeModel::nextFromSimpleAxis()
149{
150 QFETCH(QString, queryString);
151
152 QBuffer out;
153
154 /* Fill out, using LoadingModel. */
155 {
156 QXmlQuery query(m_namePool);
157 query.bindVariable(localName: QLatin1String("node"), value: m_rootNode);
158 query.setQuery(sourceCode: queryString);
159 QVERIFY(query.isValid());
160
161 QVERIFY(out.open(QIODevice::WriteOnly));
162 QXmlFormatter formatter(query, &out);
163
164 QVERIFY(query.evaluateTo(&formatter));
165 }
166
167 QBuffer baseline;
168
169 /* Create the baseline. */
170 {
171 QXmlQuery openDoc(m_namePool);
172 const QString testFilePath = QDir::currentPath() + QLatin1Char('/') + QLatin1String(testFileName);
173 openDoc.bindVariable(localName: QLatin1String("docURI"), value: QVariant(QUrl::fromLocalFile(localfile: testFilePath)));
174 openDoc.setQuery(sourceCode: QLatin1String("doc($docURI)"));
175 QXmlResultItems doc;
176 QVERIFY(openDoc.isValid());
177 openDoc.evaluateTo(result: &doc);
178
179 QXmlQuery produceBaseline(m_namePool);
180 produceBaseline.bindVariable(localName: QLatin1String("node"), value: doc.next());
181 produceBaseline.setQuery(sourceCode: queryString);
182 QVERIFY(produceBaseline.isValid());
183 QVERIFY(baseline.open(QIODevice::WriteOnly));
184
185 QXmlFormatter baselineFormatter(produceBaseline, &baseline);
186 QVERIFY(produceBaseline.evaluateTo(&baselineFormatter));
187 }
188
189 if(out.data() != baseline.data())
190 {
191 QTextStream(stderr) << "ACTUAL:" << QString::fromUtf8(str: out.data().constData())
192 << "EXPECTED:" << QString::fromUtf8(str: baseline.data().constData());
193 }
194
195 QCOMPARE(out.data(), baseline.data());
196}
197
198void tst_QAbstractXmlNodeModel::nextFromSimpleAxis_data() const
199{
200 QTest::addColumn<QString>(name: "queryString");
201
202 QTest::newRow(dataTag: "The whole tree")
203 << "$node";
204
205 QTest::newRow(dataTag: "::descendant-or-self from $node, all nodes.")
206 << "$node/descendant-or-self::node()";
207
208 QTest::newRow(dataTag: "::descendant from $node, all nodes.")
209 << "$node/descendant::node()";
210
211 QTest::newRow(dataTag: "::descendant from node with no descendants.")
212 << "$node/descendant::node()";
213
214 QTest::newRow(dataTag: "following-sibling on $root.")
215 << "$node/text()[1]/following-sibling::node()";
216
217 QTest::newRow(dataTag: "following-sibling from section1.")
218 << "$node//section1/following-sibling::node()";
219
220 QTest::newRow(dataTag: "preceding-sibling-sibling from section1.")
221 << "$node//section1/preceding-sibling::node()";
222
223 QTest::newRow(dataTag: "following-sibling from oneTextChild.")
224 << "$node//oneTextChild/following-sibling::node()";
225
226 QTest::newRow(dataTag: "preceding-sibling-sibling from oneTextChild.")
227 << "$node//oneTextChild/preceding-sibling::node()";
228
229 QTest::newRow(dataTag: "preceding-sibling on $root.")
230 << "$node/preceding-sibling::node()";
231
232 QTest::newRow(dataTag: "::ancestor from node at the end")
233 << "$node//node()/ancestor::node()";
234
235 QTest::newRow(dataTag: "::ancestor-or-self from node at the end")
236 << "$node//node()/ancestor-or-self::node()";
237
238 QTest::newRow(dataTag: "Copy attributes from all nodes.")
239 << "<e>{for $i in $node//node()/@* order by $i return $i}</e>";
240
241 QTest::newRow(dataTag: "::preceding from node at the end")
242 << "($node//node())[last()]/preceding::node()";
243
244 QTest::newRow(dataTag: "::preceding from $node")
245 << "$node/preceding::node()";
246
247 QTest::newRow(dataTag: "::following from node at the end")
248 << "($node//node())[last()]/following::node()";
249
250 QTest::newRow(dataTag: "::following from $node")
251 << "$node//following::node()";
252
253 QTest::newRow(dataTag: "::following from $node")
254 << "$node/following::node()";
255
256 QTest::newRow(dataTag: "::descendant from text() nodes.")
257 << "$node/descendant-or-self::text()/descendant::node()";
258
259 QTest::newRow(dataTag: "::descendant-or-self from text() nodes.")
260 << "$node/descendant-or-self::text()/descendant-or-self::node()";
261
262 QTest::newRow(dataTag: "descendant-or-self::node() from section1.")
263 << "$node//section1/descendant-or-self::node()";
264
265 QTest::newRow(dataTag: "descendant::node() from section1.")
266 << "$node//section1/descendant::node()";
267
268 /* Checks for axis order. */
269
270 QTest::newRow(dataTag: "::descendant from text() nodes with last(), checking axis order.")
271 << "$node/descendant-or-self::text()/(descendant::node()[last()])";
272
273 QTest::newRow(dataTag: "::descendant-or-self from text() nodes with last(), checking axis order.")
274 << "$node/descendant-or-self::text()/(descendant-or-self::node()[last()])";
275
276 QTest::newRow(dataTag: "::descendant from text() nodes with predicate, checking axis order.")
277 << "$node/descendant-or-self::text()/(descendant::node()[2])";
278
279 QTest::newRow(dataTag: "::descendant-or-self from text() nodes with predicate, checking axis order.")
280 << "$node/descendant-or-self::text()/(descendant-or-self::node()[2])";
281
282 QTest::newRow(dataTag: "::following from node at the end with predicate, checking axis order.")
283 << "($node//node())[last()]/(following::node()[2])";
284
285 QTest::newRow(dataTag: "::following from node at the end with last(), checking axis order.")
286 << "($node//node())[last()]/(following::node()[last()])";
287
288 QTest::newRow(dataTag: "ancestor:: from node at the end with predicate, checking axis order.")
289 << "($node//node())[last()]/(ancestor::node()[2])";
290
291 QTest::newRow(dataTag: "ancestor:: from node at the end with last(), checking axis order.")
292 << "($node//node())[last()]/(ancestor::node()[last()])";
293
294 QTest::newRow(dataTag: "ancestor-or-self:: from node at the end with predicate, checking axis order.")
295 << "($node//node())[last()]/(ancestor::node()[2])";
296
297 QTest::newRow(dataTag: "ancestor-or-self:: from node at the end with last(), checking axis order.")
298 << "($node//node())[last()]/(ancestor::node()[last()])";
299
300 QTest::newRow(dataTag: "::preceding from node at the end with predicate, checking axis order.")
301 << "($node//node())[last()]/(preceding::node()[2])";
302
303 QTest::newRow(dataTag: "descendant-or-self in two levels, with last()")
304 << "$node/descendant-or-self::text()/(descendant-or-self::node()[last()])";
305
306 QTest::newRow(dataTag: "descendant-or-self with last()")
307 << "$node/descendant-or-self::node()[last()]";
308
309 QTest::newRow(dataTag: "::preceding from node at the end with last(), checking axis order.")
310 << "$node/preceding::node()[last()]";
311
312 QTest::newRow(dataTag: "::preceding combined with descendant-or-self, from node at the end with last(), checking axis order.")
313 << "$node//preceding::node()[last()]";
314
315 QTest::newRow(dataTag: "::preceding combined with descendant-or-self, from node at the end with last(), checking axis order.")
316 << "($node//node())[last()]/(preceding::node()[last()])";
317}
318
319void tst_QAbstractXmlNodeModel::constCorrectness() const
320{
321 // TODO
322}
323
324void tst_QAbstractXmlNodeModel::createData() const
325{
326 // TODO
327 // Verify that the argument is qint64
328}
329
330void tst_QAbstractXmlNodeModel::createPointerAdditionalData() const
331{
332 // TODO
333 // Verify that the second argument is qint64
334}
335
336void tst_QAbstractXmlNodeModel::createDataAdditionalData() const
337{
338 // TODO
339}
340
341void tst_QAbstractXmlNodeModel::id() const
342{
343 // TODO verify that invalid NCNames are not sent to the model.
344}
345
346void tst_QAbstractXmlNodeModel::idref() const
347{
348 // TODO verify that invalid NCNames are not sent to the model.
349}
350
351/*!
352 Verify that if QAbstractXmlNodeModel::typedValue() return a null
353 QVariant, it is treated as that the node has no typed value.
354 */
355void tst_QAbstractXmlNodeModel::typedValue() const
356{
357 class TypedModel : public TestNodeModel
358 {
359 public:
360 virtual QVariant typedValue(const QXmlNodeModelIndex &) const
361 {
362 return QVariant();
363 }
364
365 QXmlNodeModelIndex rootIndex() const
366 {
367 return createIndex(data: qint64(1));
368 }
369 };
370
371 TypedModel model;
372
373 QXmlQuery query;
374 query.bindVariable(localName: QLatin1String("node"), value: model.rootIndex());
375 query.setQuery(sourceCode: QLatin1String("declare variable $node external;"
376 "string($node), data($node)"));
377
378 QByteArray output;
379 QBuffer buffer(&output);
380 QVERIFY(buffer.open(QIODevice::WriteOnly));
381 QVERIFY(query.isValid());
382
383 QXmlSerializer serializer(query, &buffer);
384 QVERIFY(query.evaluateTo(&serializer));
385
386 QVERIFY(output.isEmpty());
387}
388
389void tst_QAbstractXmlNodeModel::sourceLocation() const
390{
391 const QAbstractXmlNodeModel* const constModel = m_nodeModel.data();
392 const QSourceLocation location = constModel->sourceLocation(index: m_rootNode);
393}
394
395QTEST_MAIN(tst_QAbstractXmlNodeModel)
396
397#include "tst_qabstractxmlnodemodel.moc"
398
399// vim: et:ts=4:sw=4:sts=4
400

source code of qtxmlpatterns/tests/auto/qabstractxmlnodemodel/tst_qabstractxmlnodemodel.cpp