Fix test fails related to QTBUG-22237
[profile/ivi/qtdeclarative.git] / tests / auto / declarative / qquickpathview / tst_qquickpathview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 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 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtTest/QtTest>
43 #include <QtDeclarative/qquickview.h>
44 #include <QtDeclarative/qdeclarativeengine.h>
45 #include <QtDeclarative/qdeclarativecomponent.h>
46 #include <QtDeclarative/qdeclarativecontext.h>
47 #include <QtDeclarative/qdeclarativeexpression.h>
48 #include <QtDeclarative/private/qquickpathview_p.h>
49 #include <QtDeclarative/private/qdeclarativepath_p.h>
50 #include <QtDeclarative/private/qquicktext_p.h>
51 #include <QtDeclarative/private/qquickrectangle_p.h>
52 #include <QtDeclarative/private/qdeclarativelistmodel_p.h>
53 #include <QtDeclarative/private/qdeclarativevaluetype_p.h>
54 #include <QAbstractListModel>
55 #include <QStringListModel>
56 #include <QStandardItemModel>
57 #include <QFile>
58
59 #include "../shared/util.h"
60
61 static void initStandardTreeModel(QStandardItemModel *model)
62 {
63     QStandardItem *item;
64     item = new QStandardItem(QLatin1String("Row 1 Item"));
65     model->insertRow(0, item);
66
67     item = new QStandardItem(QLatin1String("Row 2 Item"));
68     item->setCheckable(true);
69     model->insertRow(1, item);
70
71     QStandardItem *childItem = new QStandardItem(QLatin1String("Row 2 Child Item"));
72     item->setChild(0, childItem);
73
74     item = new QStandardItem(QLatin1String("Row 3 Item"));
75     item->setIcon(QIcon());
76     model->insertRow(2, item);
77 }
78
79
80 class tst_QQuickPathView : public QObject
81 {
82     Q_OBJECT
83 public:
84     tst_QQuickPathView();
85
86 private slots:
87     void initTestCase();
88     void cleanupTestCase();
89     void initValues();
90     void items();
91     void dataModel();
92     void pathview2();
93     void pathview3();
94     void path();
95     void pathMoved();
96     void setCurrentIndex();
97     void resetModel();
98     void propertyChanges();
99     void pathChanges();
100     void componentChanges();
101     void modelChanges();
102     void pathUpdateOnStartChanged();
103     void package();
104     void emptyModel();
105     void closed();
106     void pathUpdate();
107     void visualDataModel();
108     void undefinedPath();
109     void mouseDrag();
110     void treeModel();
111     void changePreferredHighlight();
112     void missingPercent();
113     void creationContext();
114
115 private:
116     QQuickView *createView();
117     template<typename T>
118     T *findItem(QQuickItem *parent, const QString &objectName, int index=-1);
119     template<typename T>
120     QList<T*> findItems(QQuickItem *parent, const QString &objectName);
121 };
122
123 void tst_QQuickPathView::initTestCase()
124 {
125 }
126
127 void tst_QQuickPathView::cleanupTestCase()
128 {
129
130 }
131
132 class TestObject : public QObject
133 {
134     Q_OBJECT
135
136     Q_PROPERTY(bool error READ error WRITE setError)
137     Q_PROPERTY(bool useModel READ useModel NOTIFY useModelChanged)
138     Q_PROPERTY(int pathItemCount READ pathItemCount NOTIFY pathItemCountChanged)
139
140 public:
141     TestObject() : QObject(), mError(true), mUseModel(true), mPathItemCount(-1) {}
142
143     bool error() const { return mError; }
144     void setError(bool err) { mError = err; }
145
146     bool useModel() const { return mUseModel; }
147     void setUseModel(bool use) { mUseModel = use; emit useModelChanged(); }
148
149     int pathItemCount() const { return mPathItemCount; }
150     void setPathItemCount(int count) { mPathItemCount = count; emit pathItemCountChanged(); }
151
152 signals:
153     void useModelChanged();
154     void pathItemCountChanged();
155
156 private:
157     bool mError;
158     bool mUseModel;
159     int mPathItemCount;
160 };
161
162 class TestModel : public QAbstractListModel
163 {
164 public:
165     enum Roles { Name = Qt::UserRole+1, Number = Qt::UserRole+2 };
166
167     TestModel(QObject *parent=0) : QAbstractListModel(parent) {
168         QHash<int, QByteArray> roles;
169         roles[Name] = "name";
170         roles[Number] = "number";
171         setRoleNames(roles);
172     }
173
174     int rowCount(const QModelIndex &parent=QModelIndex()) const { Q_UNUSED(parent); return list.count(); }
175     QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const {
176         QVariant rv;
177         if (role == Name)
178             rv = list.at(index.row()).first;
179         else if (role == Number)
180             rv = list.at(index.row()).second;
181
182         return rv;
183     }
184
185     int count() const { return rowCount(); }
186     QString name(int index) const { return list.at(index).first; }
187     QString number(int index) const { return list.at(index).second; }
188
189     void addItem(const QString &name, const QString &number) {
190         beginInsertRows(QModelIndex(), list.count(), list.count());
191         list.append(QPair<QString,QString>(name, number));
192         endInsertRows();
193     }
194
195     void insertItem(int index, const QString &name, const QString &number) {
196         beginInsertRows(QModelIndex(), index, index);
197         list.insert(index, QPair<QString,QString>(name, number));
198         endInsertRows();
199     }
200
201     void removeItem(int index) {
202         beginRemoveRows(QModelIndex(), index, index);
203         list.removeAt(index);
204         endRemoveRows();
205     }
206
207     void moveItem(int from, int to) {
208         beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
209         list.move(from, to);
210         endMoveRows();
211     }
212
213     void modifyItem(int idx, const QString &name, const QString &number) {
214         list[idx] = QPair<QString,QString>(name, number);
215         emit dataChanged(index(idx,0), index(idx,0));
216     }
217
218 private:
219     QList<QPair<QString,QString> > list;
220 };
221
222
223 tst_QQuickPathView::tst_QQuickPathView()
224 {
225 }
226
227 void tst_QQuickPathView::initValues()
228 {
229     QDeclarativeEngine engine;
230     QDeclarativeComponent c(&engine, QUrl::fromLocalFile(TESTDATA("pathview1.qml")));
231     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
232
233     QVERIFY(obj != 0);
234     QVERIFY(obj->path() == 0);
235     QVERIFY(obj->delegate() == 0);
236     QCOMPARE(obj->model(), QVariant());
237     QCOMPARE(obj->currentIndex(), 0);
238     QCOMPARE(obj->offset(), 0.);
239     QCOMPARE(obj->preferredHighlightBegin(), 0.);
240     QCOMPARE(obj->dragMargin(), 0.);
241     QCOMPARE(obj->count(), 0);
242     QCOMPARE(obj->pathItemCount(), -1);
243 }
244
245 void tst_QQuickPathView::items()
246 {
247     QQuickView *canvas = createView();
248
249     TestModel model;
250     model.addItem("Fred", "12345");
251     model.addItem("John", "2345");
252     model.addItem("Bob", "54321");
253     model.addItem("Bill", "4321");
254
255     QDeclarativeContext *ctxt = canvas->rootContext();
256     ctxt->setContextProperty("testModel", &model);
257
258     canvas->setSource(QUrl::fromLocalFile(TESTDATA("pathview0.qml")));
259     qApp->processEvents();
260
261     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
262     QVERIFY(pathview != 0);
263
264     QCOMPARE(pathview->count(), model.count());
265     QCOMPARE(canvas->rootObject()->property("count").toInt(), model.count());
266     QCOMPARE(pathview->childItems().count(), model.count()+1); // assumes all are visible, including highlight
267
268     for (int i = 0; i < model.count(); ++i) {
269         QQuickText *name = findItem<QQuickText>(pathview, "textName", i);
270         QVERIFY(name != 0);
271         QCOMPARE(name->text(), model.name(i));
272         QQuickText *number = findItem<QQuickText>(pathview, "textNumber", i);
273         QVERIFY(number != 0);
274         QCOMPARE(number->text(), model.number(i));
275     }
276
277     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
278     QVERIFY(path);
279
280     QVERIFY(pathview->highlightItem());
281     QPointF start = path->pointAt(0.0);
282     QPointF offset;
283     offset.setX(pathview->highlightItem()->width()/2);
284     offset.setY(pathview->highlightItem()->height()/2);
285     QCOMPARE(pathview->highlightItem()->pos() + offset, start);
286
287     delete canvas;
288 }
289
290 void tst_QQuickPathView::pathview2()
291 {
292     QDeclarativeEngine engine;
293     QDeclarativeComponent c(&engine, QUrl::fromLocalFile(TESTDATA("pathview2.qml")));
294     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
295
296     QVERIFY(obj != 0);
297     QVERIFY(obj->path() != 0);
298     QVERIFY(obj->delegate() != 0);
299     QVERIFY(obj->model() != QVariant());
300     QCOMPARE(obj->currentIndex(), 0);
301     QCOMPARE(obj->offset(), 0.);
302     QCOMPARE(obj->preferredHighlightBegin(), 0.);
303     QCOMPARE(obj->dragMargin(), 0.);
304     QCOMPARE(obj->count(), 8);
305     QCOMPARE(obj->pathItemCount(), 10);
306 }
307
308 void tst_QQuickPathView::pathview3()
309 {
310     QDeclarativeEngine engine;
311     QDeclarativeComponent c(&engine, QUrl::fromLocalFile(TESTDATA("pathview3.qml")));
312     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
313
314     QVERIFY(obj != 0);
315     QVERIFY(obj->path() != 0);
316     QVERIFY(obj->delegate() != 0);
317     QVERIFY(obj->model() != QVariant());
318     QCOMPARE(obj->currentIndex(), 0);
319     QCOMPARE(obj->offset(), 1.0);
320     QCOMPARE(obj->preferredHighlightBegin(), 0.5);
321     QCOMPARE(obj->dragMargin(), 24.);
322     QCOMPARE(obj->count(), 8);
323     QCOMPARE(obj->pathItemCount(), 4);
324 }
325
326 void tst_QQuickPathView::path()
327 {
328     QDeclarativeEngine engine;
329     QDeclarativeComponent c(&engine, QUrl::fromLocalFile(TESTDATA("pathtest.qml")));
330     QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
331
332     QVERIFY(obj != 0);
333     QCOMPARE(obj->startX(), 120.);
334     QCOMPARE(obj->startY(), 100.);
335     QVERIFY(obj->path() != QPainterPath());
336
337     QDeclarativeListReference list(obj, "pathElements");
338     QCOMPARE(list.count(), 5);
339
340     QDeclarativePathAttribute* attr = qobject_cast<QDeclarativePathAttribute*>(list.at(0));
341     QVERIFY(attr != 0);
342     QCOMPARE(attr->name(), QString("scale"));
343     QCOMPARE(attr->value(), 1.0);
344
345     QDeclarativePathQuad* quad = qobject_cast<QDeclarativePathQuad*>(list.at(1));
346     QVERIFY(quad != 0);
347     QCOMPARE(quad->x(), 120.);
348     QCOMPARE(quad->y(), 25.);
349     QCOMPARE(quad->controlX(), 260.);
350     QCOMPARE(quad->controlY(), 75.);
351
352     QDeclarativePathPercent* perc = qobject_cast<QDeclarativePathPercent*>(list.at(2));
353     QVERIFY(perc != 0);
354     QCOMPARE(perc->value(), 0.3);
355
356     QDeclarativePathLine* line = qobject_cast<QDeclarativePathLine*>(list.at(3));
357     QVERIFY(line != 0);
358     QCOMPARE(line->x(), 120.);
359     QCOMPARE(line->y(), 100.);
360
361     QDeclarativePathCubic* cubic = qobject_cast<QDeclarativePathCubic*>(list.at(4));
362     QVERIFY(cubic != 0);
363     QCOMPARE(cubic->x(), 180.);
364     QCOMPARE(cubic->y(), 0.);
365     QCOMPARE(cubic->control1X(), -10.);
366     QCOMPARE(cubic->control1Y(), 90.);
367     QCOMPARE(cubic->control2X(), 210.);
368     QCOMPARE(cubic->control2Y(), 90.);
369 }
370
371 void tst_QQuickPathView::dataModel()
372 {
373     QQuickView *canvas = createView();
374     canvas->show();
375
376     QDeclarativeContext *ctxt = canvas->rootContext();
377     TestObject *testObject = new TestObject;
378     ctxt->setContextProperty("testObject", testObject);
379
380     TestModel model;
381     model.addItem("red", "1");
382     model.addItem("green", "2");
383     model.addItem("blue", "3");
384     model.addItem("purple", "4");
385     model.addItem("gray", "5");
386     model.addItem("brown", "6");
387     model.addItem("yellow", "7");
388     model.addItem("thistle", "8");
389     model.addItem("cyan", "9");
390     model.addItem("peachpuff", "10");
391     model.addItem("powderblue", "11");
392     model.addItem("gold", "12");
393     model.addItem("sandybrown", "13");
394
395     ctxt->setContextProperty("testData", &model);
396
397     canvas->setSource(QUrl::fromLocalFile(TESTDATA("datamodel.qml")));
398     qApp->processEvents();
399
400     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
401     QVERIFY(pathview != 0);
402
403     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
404     QVERIFY(testObject->error() == false);
405
406     QQuickItem *item = findItem<QQuickItem>(pathview, "wrapper", 0);
407     QVERIFY(item);
408     QCOMPARE(item->x(), 110.0);
409     QCOMPARE(item->y(), 10.0);
410
411     model.insertItem(4, "orange", "10");
412     QTest::qWait(100);
413
414     QCOMPARE(canvas->rootObject()->property("viewCount").toInt(), model.count());
415     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 14);
416
417     QVERIFY(pathview->currentIndex() == 0);
418     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 0));
419
420     QQuickText *text = findItem<QQuickText>(pathview, "myText", 4);
421     QVERIFY(text);
422     QCOMPARE(text->text(), model.name(4));
423
424     model.removeItem(2);
425     QCOMPARE(canvas->rootObject()->property("viewCount").toInt(), model.count());
426     text = findItem<QQuickText>(pathview, "myText", 2);
427     QVERIFY(text);
428     QCOMPARE(text->text(), model.name(2));
429     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 0));
430
431     testObject->setPathItemCount(5);
432     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
433     QVERIFY(testObject->error() == false);
434
435     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
436
437     QQuickRectangle *testItem = findItem<QQuickRectangle>(pathview, "wrapper", 4);
438     QVERIFY(testItem != 0);
439     testItem = findItem<QQuickRectangle>(pathview, "wrapper", 5);
440     QVERIFY(testItem == 0);
441
442     pathview->setCurrentIndex(1);
443     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
444     QTest::qWait(100);
445
446     model.insertItem(2, "pink", "2");
447     QTest::qWait(100);
448
449     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
450     QVERIFY(pathview->currentIndex() == 1);
451     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
452
453     text = findItem<QQuickText>(pathview, "myText", 2);
454     QVERIFY(text);
455     QCOMPARE(text->text(), model.name(2));
456
457     model.removeItem(3);
458     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
459     text = findItem<QQuickText>(pathview, "myText", 3);
460     QVERIFY(text);
461     QCOMPARE(text->text(), model.name(3));
462     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
463
464     model.moveItem(3, 5);
465     QTRY_COMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
466     QList<QQuickItem*> items = findItems<QQuickItem>(pathview, "wrapper");
467     foreach (QQuickItem *item, items) {
468         QVERIFY(item->property("onPath").toBool());
469     }
470     QCOMPARE(pathview->currentItem(), findItem<QQuickItem>(pathview, "wrapper", 1));
471
472     // QTBUG-14199
473     pathview->setOffset(7);
474     pathview->setOffset(0);
475     QCOMPARE(findItems<QQuickItem>(pathview, "wrapper").count(), 5);
476
477     pathview->setCurrentIndex(model.count()-1);
478     model.removeItem(model.count()-1);
479     QCOMPARE(pathview->currentIndex(), model.count()-1);
480
481     delete canvas;
482     delete testObject;
483 }
484
485 void tst_QQuickPathView::pathMoved()
486 {
487     QQuickView *canvas = createView();
488     canvas->show();
489
490     TestModel model;
491     model.addItem("Ben", "12345");
492     model.addItem("Bohn", "2345");
493     model.addItem("Bob", "54321");
494     model.addItem("Bill", "4321");
495
496     QDeclarativeContext *ctxt = canvas->rootContext();
497     ctxt->setContextProperty("testModel", &model);
498
499     canvas->setSource(QUrl::fromLocalFile(TESTDATA("pathview0.qml")));
500     qApp->processEvents();
501
502     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
503     QVERIFY(pathview != 0);
504
505     QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
506     QVERIFY(firstItem);
507     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
508     QVERIFY(path);
509     QPointF start = path->pointAt(0.0);
510     QPointF offset;//Center of item is at point, but pos is from corner
511     offset.setX(firstItem->width()/2);
512     offset.setY(firstItem->height()/2);
513     QCOMPARE(firstItem->pos() + offset, start);
514     pathview->setOffset(1.0);
515
516     for (int i=0; i<model.count(); i++) {
517         QQuickRectangle *curItem = findItem<QQuickRectangle>(pathview, "wrapper", i);
518         QPointF itemPos(path->pointAt(0.25 + i*0.25));
519         QCOMPARE(curItem->pos() + offset, QPointF(qRound(itemPos.x()), qRound(itemPos.y())));
520     }
521
522     pathview->setOffset(0.0);
523     QCOMPARE(firstItem->pos() + offset, start);
524
525     // Change delegate size
526     pathview->setOffset(0.1);
527     pathview->setOffset(0.0);
528     canvas->rootObject()->setProperty("delegateWidth", 30);
529     QCOMPARE(firstItem->width(), 30.0);
530     offset.setX(firstItem->width()/2);
531     QTRY_COMPARE(firstItem->pos() + offset, start);
532
533     // Change delegate scale
534     pathview->setOffset(0.1);
535     pathview->setOffset(0.0);
536     canvas->rootObject()->setProperty("delegateScale", 1.2);
537     QTRY_COMPARE(firstItem->pos() + offset, start);
538
539     delete canvas;
540 }
541
542 void tst_QQuickPathView::setCurrentIndex()
543 {
544     QQuickView *canvas = createView();
545     canvas->show();
546
547     TestModel model;
548     model.addItem("Ben", "12345");
549     model.addItem("Bohn", "2345");
550     model.addItem("Bob", "54321");
551     model.addItem("Bill", "4321");
552
553     QDeclarativeContext *ctxt = canvas->rootContext();
554     ctxt->setContextProperty("testModel", &model);
555
556     canvas->setSource(QUrl::fromLocalFile(TESTDATA("pathview0.qml")));
557     qApp->processEvents();
558
559     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
560     QVERIFY(pathview != 0);
561
562     QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
563     QVERIFY(firstItem);
564     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
565     QVERIFY(path);
566     QPointF start = path->pointAt(0.0);
567     QPointF offset;//Center of item is at point, but pos is from corner
568     offset.setX(firstItem->width()/2);
569     offset.setY(firstItem->height()/2);
570     QCOMPARE(firstItem->pos() + offset, start);
571     QCOMPARE(canvas->rootObject()->property("currentA").toInt(), 0);
572     QCOMPARE(canvas->rootObject()->property("currentB").toInt(), 0);
573
574     pathview->setCurrentIndex(2);
575
576     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 2);
577     QTRY_COMPARE(firstItem->pos() + offset, start);
578     QCOMPARE(canvas->rootObject()->property("currentA").toInt(), 2);
579     QCOMPARE(canvas->rootObject()->property("currentB").toInt(), 2);
580     QCOMPARE(pathview->currentItem(), firstItem);
581     QCOMPARE(firstItem->property("onPath"), QVariant(true));
582
583     pathview->decrementCurrentIndex();
584     QTRY_COMPARE(pathview->currentIndex(), 1);
585     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 1);
586     QVERIFY(firstItem);
587     QTRY_COMPARE(firstItem->pos() + offset, start);
588     QCOMPARE(pathview->currentItem(), firstItem);
589     QCOMPARE(firstItem->property("onPath"), QVariant(true));
590
591     pathview->decrementCurrentIndex();
592     QTRY_COMPARE(pathview->currentIndex(), 0);
593     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
594     QVERIFY(firstItem);
595     QTRY_COMPARE(firstItem->pos() + offset, start);
596     QCOMPARE(pathview->currentItem(), firstItem);
597     QCOMPARE(firstItem->property("onPath"), QVariant(true));
598
599     pathview->decrementCurrentIndex();
600     QTRY_COMPARE(pathview->currentIndex(), 3);
601     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 3);
602     QVERIFY(firstItem);
603     QTRY_COMPARE(firstItem->pos() + offset, start);
604     QCOMPARE(pathview->currentItem(), firstItem);
605     QCOMPARE(firstItem->property("onPath"), QVariant(true));
606
607     pathview->incrementCurrentIndex();
608     QTRY_COMPARE(pathview->currentIndex(), 0);
609     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
610     QVERIFY(firstItem);
611     QTRY_COMPARE(firstItem->pos() + offset, start);
612     QCOMPARE(pathview->currentItem(), firstItem);
613     QCOMPARE(firstItem->property("onPath"), QVariant(true));
614
615     // Check the current item is still created when outside the bounds of pathItemCount.
616     pathview->setPathItemCount(2);
617     pathview->setHighlightRangeMode(QQuickPathView::NoHighlightRange);
618     QVERIFY(findItem<QQuickRectangle>(pathview, "wrapper", 0));
619     QVERIFY(findItem<QQuickRectangle>(pathview, "wrapper", 1));
620     QVERIFY(!findItem<QQuickRectangle>(pathview, "wrapper", 2));
621     QVERIFY(!findItem<QQuickRectangle>(pathview, "wrapper", 3));
622
623     pathview->setCurrentIndex(2);
624     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 2);
625     QCOMPARE(pathview->currentItem(), firstItem);
626     QCOMPARE(firstItem->property("onPath"), QVariant(false));
627
628     pathview->decrementCurrentIndex();
629     QTRY_COMPARE(pathview->currentIndex(), 1);
630     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 1);
631     QVERIFY(firstItem);
632     QCOMPARE(pathview->currentItem(), firstItem);
633     QCOMPARE(firstItem->property("onPath"), QVariant(true));
634
635     pathview->decrementCurrentIndex();
636     QTRY_COMPARE(pathview->currentIndex(), 0);
637     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
638     QVERIFY(firstItem);
639     QCOMPARE(pathview->currentItem(), firstItem);
640     QCOMPARE(firstItem->property("onPath"), QVariant(true));
641
642     pathview->decrementCurrentIndex();
643     QTRY_COMPARE(pathview->currentIndex(), 3);
644     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 3);
645     QVERIFY(firstItem);
646     QCOMPARE(pathview->currentItem(), firstItem);
647     QCOMPARE(firstItem->property("onPath"), QVariant(false));
648
649     pathview->incrementCurrentIndex();
650     QTRY_COMPARE(pathview->currentIndex(), 0);
651     firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
652     QVERIFY(firstItem);
653     QCOMPARE(pathview->currentItem(), firstItem);
654     QCOMPARE(firstItem->property("onPath"), QVariant(true));
655
656     delete canvas;
657 }
658
659 void tst_QQuickPathView::resetModel()
660 {
661     QQuickView *canvas = createView();
662
663     QStringList strings;
664     strings << "one" << "two" << "three";
665     QStringListModel model(strings);
666
667     QDeclarativeContext *ctxt = canvas->rootContext();
668     ctxt->setContextProperty("testModel", &model);
669
670     canvas->setSource(QUrl::fromLocalFile(TESTDATA("displaypath.qml")));
671     qApp->processEvents();
672
673     QQuickPathView *pathview = findItem<QQuickPathView>(canvas->rootObject(), "view");
674     QVERIFY(pathview != 0);
675
676     QCOMPARE(pathview->count(), model.rowCount());
677
678     for (int i = 0; i < model.rowCount(); ++i) {
679         QQuickText *display = findItem<QQuickText>(pathview, "displayText", i);
680         QVERIFY(display != 0);
681         QCOMPARE(display->text(), strings.at(i));
682     }
683
684     strings.clear();
685     strings << "four" << "five" << "six" << "seven";
686     model.setStringList(strings);
687
688     QCOMPARE(pathview->count(), model.rowCount());
689
690     for (int i = 0; i < model.rowCount(); ++i) {
691         QQuickText *display = findItem<QQuickText>(pathview, "displayText", i);
692         QVERIFY(display != 0);
693         QCOMPARE(display->text(), strings.at(i));
694     }
695
696     delete canvas;
697 }
698
699 void tst_QQuickPathView::propertyChanges()
700 {
701     QQuickView *canvas = createView();
702     QVERIFY(canvas);
703     canvas->setSource(QUrl::fromLocalFile(TESTDATA("propertychanges.qml")));
704
705     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
706     QVERIFY(pathView);
707
708     QSignalSpy snapPositionSpy(pathView, SIGNAL(preferredHighlightBeginChanged()));
709     QSignalSpy dragMarginSpy(pathView, SIGNAL(dragMarginChanged()));
710
711     QCOMPARE(pathView->preferredHighlightBegin(), 0.1);
712     QCOMPARE(pathView->dragMargin(), 5.0);
713
714     pathView->setPreferredHighlightBegin(0.4);
715     pathView->setPreferredHighlightEnd(0.4);
716     pathView->setDragMargin(20.0);
717
718     QCOMPARE(pathView->preferredHighlightBegin(), 0.4);
719     QCOMPARE(pathView->preferredHighlightEnd(), 0.4);
720     QCOMPARE(pathView->dragMargin(), 20.0);
721
722     QCOMPARE(snapPositionSpy.count(), 1);
723     QCOMPARE(dragMarginSpy.count(), 1);
724
725     pathView->setPreferredHighlightBegin(0.4);
726     pathView->setPreferredHighlightEnd(0.4);
727     pathView->setDragMargin(20.0);
728
729     QCOMPARE(snapPositionSpy.count(), 1);
730     QCOMPARE(dragMarginSpy.count(), 1);
731     delete canvas;
732 }
733
734 void tst_QQuickPathView::pathChanges()
735 {
736     QQuickView *canvas = createView();
737     QVERIFY(canvas);
738     canvas->setSource(QUrl::fromLocalFile(TESTDATA("propertychanges.qml")));
739
740     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
741     QVERIFY(pathView);
742
743     QDeclarativePath *path = canvas->rootObject()->findChild<QDeclarativePath*>("path");
744     QVERIFY(path);
745
746     QSignalSpy startXSpy(path, SIGNAL(startXChanged()));
747     QSignalSpy startYSpy(path, SIGNAL(startYChanged()));
748
749     QCOMPARE(path->startX(), 220.0);
750     QCOMPARE(path->startY(), 200.0);
751
752     path->setStartX(240.0);
753     path->setStartY(220.0);
754
755     QCOMPARE(path->startX(), 240.0);
756     QCOMPARE(path->startY(), 220.0);
757
758     QCOMPARE(startXSpy.count(),1);
759     QCOMPARE(startYSpy.count(),1);
760
761     path->setStartX(240);
762     path->setStartY(220);
763
764     QCOMPARE(startXSpy.count(),1);
765     QCOMPARE(startYSpy.count(),1);
766
767     QDeclarativePath *alternatePath = canvas->rootObject()->findChild<QDeclarativePath*>("alternatePath");
768     QVERIFY(alternatePath);
769
770     QSignalSpy pathSpy(pathView, SIGNAL(pathChanged()));
771
772     QCOMPARE(pathView->path(), path);
773
774     pathView->setPath(alternatePath);
775     QCOMPARE(pathView->path(), alternatePath);
776     QCOMPARE(pathSpy.count(),1);
777
778     pathView->setPath(alternatePath);
779     QCOMPARE(pathSpy.count(),1);
780
781     QDeclarativePathAttribute *pathAttribute = canvas->rootObject()->findChild<QDeclarativePathAttribute*>("pathAttribute");
782     QVERIFY(pathAttribute);
783
784     QSignalSpy nameSpy(pathAttribute, SIGNAL(nameChanged()));
785     QCOMPARE(pathAttribute->name(), QString("opacity"));
786
787     pathAttribute->setName("scale");
788     QCOMPARE(pathAttribute->name(), QString("scale"));
789     QCOMPARE(nameSpy.count(),1);
790
791     pathAttribute->setName("scale");
792     QCOMPARE(nameSpy.count(),1);
793     delete canvas;
794 }
795
796 void tst_QQuickPathView::componentChanges()
797 {
798     QQuickView *canvas = createView();
799     QVERIFY(canvas);
800     canvas->setSource(QUrl::fromLocalFile(TESTDATA("propertychanges.qml")));
801
802     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
803     QVERIFY(pathView);
804
805     QDeclarativeComponent delegateComponent(canvas->engine());
806     delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
807
808     QSignalSpy delegateSpy(pathView, SIGNAL(delegateChanged()));
809
810     pathView->setDelegate(&delegateComponent);
811     QCOMPARE(pathView->delegate(), &delegateComponent);
812     QCOMPARE(delegateSpy.count(),1);
813
814     pathView->setDelegate(&delegateComponent);
815     QCOMPARE(delegateSpy.count(),1);
816     delete canvas;
817 }
818
819 void tst_QQuickPathView::modelChanges()
820 {
821     QQuickView *canvas = createView();
822     QVERIFY(canvas);
823     canvas->setSource(QUrl::fromLocalFile(TESTDATA("propertychanges.qml")));
824
825     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
826     QVERIFY(pathView);
827
828     QDeclarativeListModel *alternateModel = canvas->rootObject()->findChild<QDeclarativeListModel*>("alternateModel");
829     QVERIFY(alternateModel);
830     QVariant modelVariant = QVariant::fromValue<QObject *>(alternateModel);
831     QSignalSpy modelSpy(pathView, SIGNAL(modelChanged()));
832
833     pathView->setModel(modelVariant);
834     QCOMPARE(pathView->model(), modelVariant);
835     QCOMPARE(modelSpy.count(),1);
836
837     pathView->setModel(modelVariant);
838     QCOMPARE(modelSpy.count(),1);
839
840     pathView->setModel(QVariant());
841     QCOMPARE(modelSpy.count(),2);
842
843     delete canvas;
844 }
845
846 void tst_QQuickPathView::pathUpdateOnStartChanged()
847 {
848     QQuickView *canvas = createView();
849     QVERIFY(canvas);
850     canvas->setSource(QUrl::fromLocalFile(TESTDATA("pathUpdateOnStartChanged.qml")));
851
852     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
853     QVERIFY(pathView);
854
855     QDeclarativePath *path = canvas->rootObject()->findChild<QDeclarativePath*>("path");
856     QVERIFY(path);
857     QCOMPARE(path->startX(), 400.0);
858     QCOMPARE(path->startY(), 300.0);
859
860     QQuickItem *item = findItem<QQuickItem>(pathView, "wrapper", 0);
861     QVERIFY(item);
862     QCOMPARE(item->x(), path->startX() - item->width() / 2.0);
863     QCOMPARE(item->y(), path->startY() - item->height() / 2.0);
864
865     delete canvas;
866 }
867
868 void tst_QQuickPathView::package()
869 {
870     QQuickView *canvas = createView();
871     QVERIFY(canvas);
872     canvas->setSource(QUrl::fromLocalFile(TESTDATA("pathview_package.qml")));
873
874     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("photoPathView");
875     QVERIFY(pathView);
876
877     QQuickItem *item = findItem<QQuickItem>(pathView, "pathItem");
878     QVERIFY(item);
879     QVERIFY(item->scale() != 1.0);
880
881     delete canvas;
882 }
883
884 //QTBUG-13017
885 void tst_QQuickPathView::emptyModel()
886 {
887     QQuickView *canvas = createView();
888
889     QStringListModel model;
890
891     QDeclarativeContext *ctxt = canvas->rootContext();
892     ctxt->setContextProperty("emptyModel", &model);
893
894     canvas->setSource(QUrl::fromLocalFile(TESTDATA("emptymodel.qml")));
895     qApp->processEvents();
896
897     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
898     QVERIFY(pathview != 0);
899
900     QCOMPARE(pathview->offset(), qreal(0.0));
901
902     delete canvas;
903 }
904
905 void tst_QQuickPathView::closed()
906 {
907     QDeclarativeEngine engine;
908
909     {
910         QDeclarativeComponent c(&engine, QUrl::fromLocalFile(TESTDATA("openPath.qml")));
911         QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
912         QVERIFY(obj);
913         QCOMPARE(obj->isClosed(), false);
914         delete obj;
915     }
916
917     {
918         QDeclarativeComponent c(&engine, QUrl::fromLocalFile(TESTDATA("closedPath.qml")));
919         QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
920         QVERIFY(obj);
921         QCOMPARE(obj->isClosed(), true);
922         delete obj;
923     }
924 }
925
926 // QTBUG-14239
927 void tst_QQuickPathView::pathUpdate()
928 {
929     QQuickView *canvas = createView();
930     QVERIFY(canvas);
931     canvas->setSource(QUrl::fromLocalFile(TESTDATA("pathUpdate.qml")));
932
933     QQuickPathView *pathView = canvas->rootObject()->findChild<QQuickPathView*>("pathView");
934     QVERIFY(pathView);
935
936     QQuickItem *item = findItem<QQuickItem>(pathView, "wrapper", 0);
937     QVERIFY(item);
938     QCOMPARE(item->x(), 150.0);
939
940     delete canvas;
941 }
942
943 void tst_QQuickPathView::visualDataModel()
944 {
945     QDeclarativeEngine engine;
946     QDeclarativeComponent c(&engine, QUrl::fromLocalFile(TESTDATA("vdm.qml")));
947
948     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
949     QVERIFY(obj != 0);
950
951     QCOMPARE(obj->count(), 3);
952
953     delete obj;
954 }
955
956 void tst_QQuickPathView::undefinedPath()
957 {
958     QDeclarativeEngine engine;
959     QDeclarativeComponent c(&engine, QUrl::fromLocalFile(TESTDATA("undefinedpath.qml")));
960
961     QQuickPathView *obj = qobject_cast<QQuickPathView*>(c.create());
962     QVERIFY(obj != 0);
963
964     QCOMPARE(obj->count(), 3);
965
966     delete obj;
967 }
968
969 void tst_QQuickPathView::mouseDrag()
970 {
971     QQuickView *canvas = createView();
972     canvas->setSource(QUrl::fromLocalFile(TESTDATA("dragpath.qml")));
973     canvas->show();
974     canvas->requestActivateWindow();
975     QTest::qWaitForWindowShown(canvas);
976     QTRY_COMPARE(canvas, qGuiApp->focusWindow());
977
978     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
979     QVERIFY(pathview != 0);
980
981     int current = pathview->currentIndex();
982
983     QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(10,100));
984     QTest::qWait(100);
985
986     {
987         QMouseEvent mv(QEvent::MouseMove, QPoint(30,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
988         QApplication::sendEvent(canvas, &mv);
989     }
990     {
991         QMouseEvent mv(QEvent::MouseMove, QPoint(90,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
992         QApplication::sendEvent(canvas, &mv);
993     }
994
995     QVERIFY(pathview->currentIndex() != current);
996
997     QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(40,100));
998
999     delete canvas;
1000 }
1001
1002 void tst_QQuickPathView::treeModel()
1003 {
1004     QQuickView *canvas = createView();
1005     canvas->show();
1006
1007     QStandardItemModel model;
1008     initStandardTreeModel(&model);
1009     canvas->engine()->rootContext()->setContextProperty("myModel", &model);
1010
1011     canvas->setSource(QUrl::fromLocalFile(TESTDATA("treemodel.qml")));
1012
1013     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1014     QVERIFY(pathview != 0);
1015     QCOMPARE(pathview->count(), 3);
1016
1017     QQuickText *item = findItem<QQuickText>(pathview, "wrapper", 0);
1018     QVERIFY(item);
1019     QCOMPARE(item->text(), QLatin1String("Row 1 Item"));
1020
1021     QVERIFY(QMetaObject::invokeMethod(pathview, "setRoot", Q_ARG(QVariant, 1)));
1022     QCOMPARE(pathview->count(), 1);
1023
1024     QTRY_VERIFY(item = findItem<QQuickText>(pathview, "wrapper", 0));
1025     QTRY_COMPARE(item->text(), QLatin1String("Row 2 Child Item"));
1026
1027     delete canvas;
1028 }
1029
1030 void tst_QQuickPathView::changePreferredHighlight()
1031 {
1032     QQuickView *canvas = createView();
1033     canvas->setGeometry(0,0,400,200);
1034     canvas->setSource(QUrl::fromLocalFile(TESTDATA("dragpath.qml")));
1035     canvas->show();
1036     canvas->requestActivateWindow();
1037     QTest::qWaitForWindowShown(canvas);
1038     QTRY_COMPARE(canvas, qGuiApp->focusWindow());
1039
1040     QQuickPathView *pathview = qobject_cast<QQuickPathView*>(canvas->rootObject());
1041     QVERIFY(pathview != 0);
1042
1043     int current = pathview->currentIndex();
1044     QCOMPARE(current, 0);
1045
1046     QQuickRectangle *firstItem = findItem<QQuickRectangle>(pathview, "wrapper", 0);
1047     QVERIFY(firstItem);
1048     QDeclarativePath *path = qobject_cast<QDeclarativePath*>(pathview->path());
1049     QVERIFY(path);
1050     QPointF start = path->pointAt(0.5);
1051     start.setX(qRound(start.x()));
1052     start.setY(qRound(start.y()));
1053     QPointF offset;//Center of item is at point, but pos is from corner
1054     offset.setX(firstItem->width()/2);
1055     offset.setY(firstItem->height()/2);
1056     QTRY_COMPARE(firstItem->pos() + offset, start);
1057
1058     pathview->setPreferredHighlightBegin(0.8);
1059     pathview->setPreferredHighlightEnd(0.8);
1060     start = path->pointAt(0.8);
1061     start.setX(qRound(start.x()));
1062     start.setY(qRound(start.y()));
1063     QTRY_COMPARE(firstItem->pos() + offset, start);
1064     QCOMPARE(pathview->currentIndex(), 0);
1065
1066     delete canvas;
1067 }
1068
1069 void tst_QQuickPathView::creationContext()
1070 {
1071     QQuickView canvas;
1072     canvas.setGeometry(0,0,240,320);
1073     canvas.setSource(QUrl::fromLocalFile(TESTDATA("creationContext.qml")));
1074
1075     QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
1076     QVERIFY(rootItem);
1077     QVERIFY(rootItem->property("count").toInt() > 0);
1078
1079     QQuickItem *item;
1080     QVERIFY(item = findItem<QQuickItem>(rootItem, "listItem", 0));
1081     QCOMPARE(item->property("text").toString(), QString("Hello!"));
1082 }
1083
1084 QQuickView *tst_QQuickPathView::createView()
1085 {
1086     QQuickView *canvas = new QQuickView(0);
1087     canvas->setGeometry(0,0,240,320);
1088
1089     return canvas;
1090 }
1091
1092 /*
1093    Find an item with the specified objectName.  If index is supplied then the
1094    item must also evaluate the {index} expression equal to index
1095  */
1096 template<typename T>
1097 T *tst_QQuickPathView::findItem(QQuickItem *parent, const QString &objectName, int index)
1098 {
1099     const QMetaObject &mo = T::staticMetaObject;
1100     //qDebug() << parent->childItems().count() << "children";
1101     for (int i = 0; i < parent->childItems().count(); ++i) {
1102         QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
1103         if (!item)
1104             continue;
1105         //qDebug() << "try" << item;
1106         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
1107             if (index != -1) {
1108                 QDeclarativeExpression e(qmlContext(item), item, "index");
1109                 if (e.evaluate().toInt() == index)
1110                     return static_cast<T*>(item);
1111             } else {
1112                 return static_cast<T*>(item);
1113             }
1114         }
1115         item = findItem<T>(item, objectName, index);
1116         if (item)
1117             return static_cast<T*>(item);
1118     }
1119
1120     return 0;
1121 }
1122
1123 template<typename T>
1124 QList<T*> tst_QQuickPathView::findItems(QQuickItem *parent, const QString &objectName)
1125 {
1126     QList<T*> items;
1127     const QMetaObject &mo = T::staticMetaObject;
1128     //qDebug() << parent->QQuickItem::children().count() << "children";
1129     for (int i = 0; i < parent->childItems().count(); ++i) {
1130         QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
1131         if (!item)
1132             continue;
1133         //qDebug() << "try" << item;
1134         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName))
1135             items.append(static_cast<T*>(item));
1136         items += findItems<T>(item, objectName);
1137     }
1138
1139     return items;
1140 }
1141
1142 void tst_QQuickPathView::missingPercent()
1143 {
1144     QDeclarativeEngine engine;
1145     QDeclarativeComponent c(&engine, QUrl::fromLocalFile(TESTDATA("missingPercent.qml")));
1146     QDeclarativePath *obj = qobject_cast<QDeclarativePath*>(c.create());
1147     QVERIFY(obj);
1148     QCOMPARE(obj->attributeAt("_qfx_percent", 1.0), qreal(1.0));
1149     delete obj;
1150 }
1151
1152
1153 QTEST_MAIN(tst_QQuickPathView)
1154
1155 #include "tst_qquickpathview.moc"