Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / tests / auto / qtquick2 / qquickrepeater / tst_qquickrepeater.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtTest/QtTest>
43 #include <QtTest/QSignalSpy>
44 #include <private/qlistmodelinterface_p.h>
45 #include <QtDeclarative/qdeclarativeengine.h>
46 #include <QtQuick/qquickview.h>
47 #include <QtDeclarative/qdeclarativecontext.h>
48 #include <QtDeclarative/qdeclarativeexpression.h>
49 #include <QtDeclarative/qdeclarativeincubator.h>
50 #include <private/qquickrepeater_p.h>
51 #include <QtQuick/private/qquicktext_p.h>
52
53 #include "../../shared/util.h"
54
55 class tst_QQuickRepeater : public QDeclarativeDataTest
56 {
57     Q_OBJECT
58 public:
59     tst_QQuickRepeater();
60
61 private slots:
62     void numberModel();
63     void objectList();
64     void stringList();
65     void dataModel_adding();
66     void dataModel_removing();
67     void dataModel_changes();
68     void itemModel();
69     void resetModel();
70     void modelChanged();
71     void properties();
72     void asynchronous();
73     void initParent();
74
75 private:
76     QQuickView *createView();
77     template<typename T>
78     T *findItem(QObject *parent, const QString &objectName, int index);
79     template<typename T>
80     T *findItem(QObject *parent, const QString &id);
81 };
82
83 class TestObject : public QObject
84 {
85     Q_OBJECT
86
87     Q_PROPERTY(bool error READ error WRITE setError)
88     Q_PROPERTY(bool useModel READ useModel NOTIFY useModelChanged)
89
90 public:
91     TestObject() : QObject(), mError(true), mUseModel(false) {}
92
93     bool error() const { return mError; }
94     void setError(bool err) { mError = err; }
95
96     bool useModel() const { return mUseModel; }
97     void setUseModel(bool use) { mUseModel = use; emit useModelChanged(); }
98
99 signals:
100     void useModelChanged();
101
102 private:
103     bool mError;
104     bool mUseModel;
105 };
106
107 class TestModel : public QAbstractListModel
108 {
109 public:
110     enum Roles { Name = Qt::UserRole+1, Number = Qt::UserRole+2 };
111
112     TestModel(QObject *parent=0) : QAbstractListModel(parent) {
113         QHash<int, QByteArray> roles;
114         roles[Name] = "name";
115         roles[Number] = "number";
116         setRoleNames(roles);
117     }
118
119     int rowCount(const QModelIndex &parent=QModelIndex()) const { Q_UNUSED(parent); return list.count(); }
120     QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const {
121         QVariant rv;
122         if (role == Name)
123             rv = list.at(index.row()).first;
124         else if (role == Number)
125             rv = list.at(index.row()).second;
126
127         return rv;
128     }
129
130     int count() const { return rowCount(); }
131     QString name(int index) const { return list.at(index).first; }
132     QString number(int index) const { return list.at(index).second; }
133
134     void addItem(const QString &name, const QString &number) {
135         emit beginInsertRows(QModelIndex(), list.count(), list.count());
136         list.append(QPair<QString,QString>(name, number));
137         emit endInsertRows();
138     }
139
140     void insertItem(int index, const QString &name, const QString &number) {
141         emit beginInsertRows(QModelIndex(), index, index);
142         list.insert(index, QPair<QString,QString>(name, number));
143         emit endInsertRows();
144     }
145
146     void removeItem(int index) {
147         emit beginRemoveRows(QModelIndex(), index, index);
148         list.removeAt(index);
149         emit endRemoveRows();
150     }
151
152     void moveItem(int from, int to) {
153         emit beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
154         list.move(from, to);
155         emit endMoveRows();
156     }
157
158     void modifyItem(int idx, const QString &name, const QString &number) {
159         list[idx] = QPair<QString,QString>(name, number);
160         emit dataChanged(index(idx,0), index(idx,0));
161     }
162
163 private:
164     QList<QPair<QString,QString> > list;
165 };
166
167
168 tst_QQuickRepeater::tst_QQuickRepeater()
169 {
170 }
171
172 void tst_QQuickRepeater::numberModel()
173 {
174     QQuickView *canvas = createView();
175
176     QDeclarativeContext *ctxt = canvas->rootContext();
177     ctxt->setContextProperty("testData", 5);
178     TestObject *testObject = new TestObject;
179     ctxt->setContextProperty("testObject", testObject);
180
181     canvas->setSource(testFileUrl("intmodel.qml"));
182     qApp->processEvents();
183
184     QQuickRepeater *repeater = findItem<QQuickRepeater>(canvas->rootObject(), "repeater");
185     QVERIFY(repeater != 0);
186     QCOMPARE(repeater->parentItem()->childItems().count(), 5+1);
187
188     QVERIFY(!repeater->itemAt(-1));
189     for (int i=0; i<repeater->count(); i++)
190         QCOMPARE(repeater->itemAt(i), repeater->parentItem()->childItems().at(i));
191     QVERIFY(!repeater->itemAt(repeater->count()));
192
193     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
194     QVERIFY(testObject->error() == false);
195
196     delete testObject;
197     delete canvas;
198 }
199
200 class MyObject : public QObject
201 {
202     Q_OBJECT
203     Q_PROPERTY(int idx READ idx CONSTANT)
204 public:
205     MyObject(int i) : QObject(), m_idx(i) {}
206
207     int idx() const { return m_idx; }
208
209     int m_idx;
210 };
211
212 void tst_QQuickRepeater::objectList()
213 {
214     QQuickView *canvas = createView();
215     QObjectList data;
216     for (int i=0; i<100; i++)
217         data << new MyObject(i);
218
219     QDeclarativeContext *ctxt = canvas->rootContext();
220     ctxt->setContextProperty("testData", QVariant::fromValue(data));
221
222     canvas->setSource(testFileUrl("objlist.qml"));
223     qApp->processEvents();
224
225     QQuickRepeater *repeater = findItem<QQuickRepeater>(canvas->rootObject(), "repeater");
226     QVERIFY(repeater != 0);
227     QCOMPARE(repeater->property("errors").toInt(), 0);//If this fails either they are out of order or can't find the object's data
228     QCOMPARE(repeater->property("instantiated").toInt(), 100);
229
230     QVERIFY(!repeater->itemAt(-1));
231     for (int i=0; i<data.count(); i++)
232         QCOMPARE(repeater->itemAt(i), repeater->parentItem()->childItems().at(i));
233     QVERIFY(!repeater->itemAt(data.count()));
234
235     QSignalSpy addedSpy(repeater, SIGNAL(itemAdded(int,QQuickItem*)));
236     QSignalSpy removedSpy(repeater, SIGNAL(itemRemoved(int,QQuickItem*)));
237     ctxt->setContextProperty("testData", QVariant::fromValue(data));
238     QCOMPARE(addedSpy.count(), data.count());
239     QCOMPARE(removedSpy.count(), data.count());
240
241     qDeleteAll(data);
242     delete canvas;
243 }
244
245 /*
246 The Repeater element creates children at its own position in its parent's
247 stacking order.  In this test we insert a repeater between two other Text
248 elements to test this.
249 */
250 void tst_QQuickRepeater::stringList()
251 {
252     QQuickView *canvas = createView();
253
254     QStringList data;
255     data << "One";
256     data << "Two";
257     data << "Three";
258     data << "Four";
259
260     QDeclarativeContext *ctxt = canvas->rootContext();
261     ctxt->setContextProperty("testData", data);
262
263     canvas->setSource(testFileUrl("repeater1.qml"));
264     qApp->processEvents();
265
266     QQuickRepeater *repeater = findItem<QQuickRepeater>(canvas->rootObject(), "repeater");
267     QVERIFY(repeater != 0);
268
269     QQuickItem *container = findItem<QQuickItem>(canvas->rootObject(), "container");
270     QVERIFY(container != 0);
271
272     QCOMPARE(container->childItems().count(), data.count() + 3);
273
274     bool saw_repeater = false;
275     for (int i = 0; i < container->childItems().count(); ++i) {
276
277         if (i == 0) {
278             QQuickText *name = qobject_cast<QQuickText*>(container->childItems().at(i));
279             QVERIFY(name != 0);
280             QCOMPARE(name->text(), QLatin1String("Zero"));
281         } else if (i == container->childItems().count() - 2) {
282             // The repeater itself
283             QQuickRepeater *rep = qobject_cast<QQuickRepeater*>(container->childItems().at(i));
284             QCOMPARE(rep, repeater);
285             saw_repeater = true;
286             continue;
287         } else if (i == container->childItems().count() - 1) {
288             QQuickText *name = qobject_cast<QQuickText*>(container->childItems().at(i));
289             QVERIFY(name != 0);
290             QCOMPARE(name->text(), QLatin1String("Last"));
291         } else {
292             QQuickText *name = qobject_cast<QQuickText*>(container->childItems().at(i));
293             QVERIFY(name != 0);
294             QCOMPARE(name->text(), data.at(i-1));
295         }
296     }
297     QVERIFY(saw_repeater);
298
299     delete canvas;
300 }
301
302 void tst_QQuickRepeater::dataModel_adding()
303 {
304     QQuickView *canvas = createView();
305     QDeclarativeContext *ctxt = canvas->rootContext();
306     TestObject *testObject = new TestObject;
307     ctxt->setContextProperty("testObject", testObject);
308
309     TestModel testModel;
310     ctxt->setContextProperty("testData", &testModel);
311     canvas->setSource(testFileUrl("repeater2.qml"));
312     qApp->processEvents();
313
314     QQuickRepeater *repeater = findItem<QQuickRepeater>(canvas->rootObject(), "repeater");
315     QVERIFY(repeater != 0);
316     QQuickItem *container = findItem<QQuickItem>(canvas->rootObject(), "container");
317     QVERIFY(container != 0);
318
319     QVERIFY(!repeater->itemAt(0));
320
321     QSignalSpy countSpy(repeater, SIGNAL(countChanged()));
322     QSignalSpy addedSpy(repeater, SIGNAL(itemAdded(int,QQuickItem*)));
323
324     // add to empty model
325     testModel.addItem("two", "2");
326     QCOMPARE(repeater->itemAt(0), container->childItems().at(0));
327     QCOMPARE(countSpy.count(), 1); countSpy.clear();
328     QCOMPARE(addedSpy.count(), 1);
329     QCOMPARE(addedSpy.at(0).at(0).toInt(), 0);
330     QCOMPARE(addedSpy.at(0).at(1).value<QQuickItem*>(), container->childItems().at(0));
331     addedSpy.clear();
332
333     // insert at start
334     testModel.insertItem(0, "one", "1");
335     QCOMPARE(repeater->itemAt(0), container->childItems().at(0));
336     QCOMPARE(countSpy.count(), 1); countSpy.clear();
337     QCOMPARE(addedSpy.count(), 1);
338     QCOMPARE(addedSpy.at(0).at(0).toInt(), 0);
339     QCOMPARE(addedSpy.at(0).at(1).value<QQuickItem*>(), container->childItems().at(0));
340     addedSpy.clear();
341
342     // insert at end
343     testModel.insertItem(2, "four", "4");
344     QCOMPARE(repeater->itemAt(2), container->childItems().at(2));
345     QCOMPARE(countSpy.count(), 1); countSpy.clear();
346     QCOMPARE(addedSpy.count(), 1);
347     QCOMPARE(addedSpy.at(0).at(0).toInt(), 2);
348     QCOMPARE(addedSpy.at(0).at(1).value<QQuickItem*>(), container->childItems().at(2));
349     addedSpy.clear();
350
351     // insert in middle
352     testModel.insertItem(2, "three", "3");
353     QCOMPARE(repeater->itemAt(2), container->childItems().at(2));
354     QCOMPARE(countSpy.count(), 1); countSpy.clear();
355     QCOMPARE(addedSpy.count(), 1);
356     QCOMPARE(addedSpy.at(0).at(0).toInt(), 2);
357     QCOMPARE(addedSpy.at(0).at(1).value<QQuickItem*>(), container->childItems().at(2));
358     addedSpy.clear();
359
360     delete testObject;
361     addedSpy.clear();
362     countSpy.clear();
363     delete canvas;
364 }
365
366 void tst_QQuickRepeater::dataModel_removing()
367 {
368     QQuickView *canvas = createView();
369     QDeclarativeContext *ctxt = canvas->rootContext();
370     TestObject *testObject = new TestObject;
371     ctxt->setContextProperty("testObject", testObject);
372
373     TestModel testModel;
374     testModel.addItem("one", "1");
375     testModel.addItem("two", "2");
376     testModel.addItem("three", "3");
377     testModel.addItem("four", "4");
378     testModel.addItem("five", "5");
379
380     ctxt->setContextProperty("testData", &testModel);
381     canvas->setSource(testFileUrl("repeater2.qml"));
382     qApp->processEvents();
383
384     QQuickRepeater *repeater = findItem<QQuickRepeater>(canvas->rootObject(), "repeater");
385     QVERIFY(repeater != 0);
386     QQuickItem *container = findItem<QQuickItem>(canvas->rootObject(), "container");
387     QVERIFY(container != 0);
388     QCOMPARE(container->childItems().count(), repeater->count()+1);
389
390     QSignalSpy countSpy(repeater, SIGNAL(countChanged()));
391     QSignalSpy removedSpy(repeater, SIGNAL(itemRemoved(int,QQuickItem*)));
392
393     // remove at start
394     QQuickItem *item = repeater->itemAt(0);
395     QCOMPARE(item, container->childItems().at(0));
396
397     testModel.removeItem(0);
398     QVERIFY(repeater->itemAt(0) != item);
399     QCOMPARE(countSpy.count(), 1); countSpy.clear();
400     QCOMPARE(removedSpy.count(), 1);
401     QCOMPARE(removedSpy.at(0).at(0).toInt(), 0);
402     QCOMPARE(removedSpy.at(0).at(1).value<QQuickItem*>(), item);
403     removedSpy.clear();
404
405     // remove at end
406     int lastIndex = testModel.count()-1;
407     item = repeater->itemAt(lastIndex);
408     QCOMPARE(item, container->childItems().at(lastIndex));
409
410     testModel.removeItem(lastIndex);
411     QVERIFY(repeater->itemAt(lastIndex) != item);
412     QCOMPARE(countSpy.count(), 1); countSpy.clear();
413     QCOMPARE(removedSpy.count(), 1);
414     QCOMPARE(removedSpy.at(0).at(0).toInt(), lastIndex);
415     QCOMPARE(removedSpy.at(0).at(1).value<QQuickItem*>(), item);
416     removedSpy.clear();
417
418     // remove from middle
419     item = repeater->itemAt(1);
420     QCOMPARE(item, container->childItems().at(1));
421
422     testModel.removeItem(1);
423     QVERIFY(repeater->itemAt(lastIndex) != item);
424     QCOMPARE(countSpy.count(), 1); countSpy.clear();
425     QCOMPARE(removedSpy.count(), 1);
426     QCOMPARE(removedSpy.at(0).at(0).toInt(), 1);
427     QCOMPARE(removedSpy.at(0).at(1).value<QQuickItem*>(), item);
428     removedSpy.clear();
429
430     delete testObject;
431     delete canvas;
432 }
433
434 void tst_QQuickRepeater::dataModel_changes()
435 {
436     QQuickView *canvas = createView();
437     QDeclarativeContext *ctxt = canvas->rootContext();
438     TestObject *testObject = new TestObject;
439     ctxt->setContextProperty("testObject", testObject);
440
441     TestModel testModel;
442     testModel.addItem("one", "1");
443     testModel.addItem("two", "2");
444     testModel.addItem("three", "3");
445
446     ctxt->setContextProperty("testData", &testModel);
447     canvas->setSource(testFileUrl("repeater2.qml"));
448     qApp->processEvents();
449
450     QQuickRepeater *repeater = findItem<QQuickRepeater>(canvas->rootObject(), "repeater");
451     QVERIFY(repeater != 0);
452     QQuickItem *container = findItem<QQuickItem>(canvas->rootObject(), "container");
453     QVERIFY(container != 0);
454     QCOMPARE(container->childItems().count(), repeater->count()+1);
455
456     // Check that model changes are propagated
457     QQuickText *text = findItem<QQuickText>(canvas->rootObject(), "myName", 1);
458     QVERIFY(text);
459     QCOMPARE(text->text(), QString("two"));
460
461     testModel.modifyItem(1, "Item two", "_2");
462     text = findItem<QQuickText>(canvas->rootObject(), "myName", 1);
463     QVERIFY(text);
464     QCOMPARE(text->text(), QString("Item two"));
465
466     text = findItem<QQuickText>(canvas->rootObject(), "myNumber", 1);
467     QVERIFY(text);
468     QCOMPARE(text->text(), QString("_2"));
469
470     delete testObject;
471     delete canvas;
472 }
473
474 void tst_QQuickRepeater::itemModel()
475 {
476     QQuickView *canvas = createView();
477     QDeclarativeContext *ctxt = canvas->rootContext();
478     TestObject *testObject = new TestObject;
479     ctxt->setContextProperty("testObject", testObject);
480
481     canvas->setSource(testFileUrl("itemlist.qml"));
482     qApp->processEvents();
483
484     QQuickRepeater *repeater = findItem<QQuickRepeater>(canvas->rootObject(), "repeater");
485     QVERIFY(repeater != 0);
486
487     QQuickItem *container = findItem<QQuickItem>(canvas->rootObject(), "container");
488     QVERIFY(container != 0);
489
490     QCOMPARE(container->childItems().count(), 1);
491
492     testObject->setUseModel(true);
493     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
494     QVERIFY(testObject->error() == false);
495
496     QCOMPARE(container->childItems().count(), 4);
497     QVERIFY(qobject_cast<QObject*>(container->childItems().at(0))->objectName() == "item1");
498     QVERIFY(qobject_cast<QObject*>(container->childItems().at(1))->objectName() == "item2");
499     QVERIFY(qobject_cast<QObject*>(container->childItems().at(2))->objectName() == "item3");
500     QVERIFY(container->childItems().at(3) == repeater);
501
502     QMetaObject::invokeMethod(canvas->rootObject(), "switchModel");
503     QCOMPARE(container->childItems().count(), 3);
504     QVERIFY(qobject_cast<QObject*>(container->childItems().at(0))->objectName() == "item4");
505     QVERIFY(qobject_cast<QObject*>(container->childItems().at(1))->objectName() == "item5");
506     QVERIFY(container->childItems().at(2) == repeater);
507
508     testObject->setUseModel(false);
509     QCOMPARE(container->childItems().count(), 1);
510
511     delete testObject;
512     delete canvas;
513 }
514
515 void tst_QQuickRepeater::resetModel()
516 {
517     QQuickView *canvas = createView();
518
519     QStringList dataA;
520     for (int i=0; i<10; i++)
521         dataA << QString::number(i);
522
523     QDeclarativeContext *ctxt = canvas->rootContext();
524     ctxt->setContextProperty("testData", dataA);
525     canvas->setSource(testFileUrl("repeater1.qml"));
526     qApp->processEvents();
527     QQuickRepeater *repeater = findItem<QQuickRepeater>(canvas->rootObject(), "repeater");
528     QVERIFY(repeater != 0);
529     QQuickItem *container = findItem<QQuickItem>(canvas->rootObject(), "container");
530     QVERIFY(container != 0);
531
532     QCOMPARE(repeater->count(), dataA.count());
533     for (int i=0; i<repeater->count(); i++)
534         QCOMPARE(repeater->itemAt(i), container->childItems().at(i+1)); // +1 to skip first Text object
535
536     QSignalSpy modelChangedSpy(repeater, SIGNAL(modelChanged()));
537     QSignalSpy countSpy(repeater, SIGNAL(countChanged()));
538     QSignalSpy addedSpy(repeater, SIGNAL(itemAdded(int,QQuickItem*)));
539     QSignalSpy removedSpy(repeater, SIGNAL(itemRemoved(int,QQuickItem*)));
540
541     QStringList dataB;
542     for (int i=0; i<20; i++)
543         dataB << QString::number(i);
544
545     // reset context property
546     ctxt->setContextProperty("testData", dataB);
547     QCOMPARE(repeater->count(), dataB.count());
548
549     QCOMPARE(modelChangedSpy.count(), 1);
550     QCOMPARE(countSpy.count(), 1);
551     QCOMPARE(removedSpy.count(), dataA.count());
552     QCOMPARE(addedSpy.count(), dataB.count());
553     for (int i=0; i<dataB.count(); i++) {
554         QCOMPARE(addedSpy.at(i).at(0).toInt(), i);
555         QCOMPARE(addedSpy.at(i).at(1).value<QQuickItem*>(), repeater->itemAt(i));
556     }
557     modelChangedSpy.clear();
558     countSpy.clear();
559     removedSpy.clear();
560     addedSpy.clear();
561
562     // reset via setModel()
563     repeater->setModel(dataA);
564     QCOMPARE(repeater->count(), dataA.count());
565
566     QCOMPARE(modelChangedSpy.count(), 1);
567     QCOMPARE(countSpy.count(), 1);
568     QCOMPARE(removedSpy.count(), dataB.count());
569     QCOMPARE(addedSpy.count(), dataA.count());
570     for (int i=0; i<dataA.count(); i++) {
571         QCOMPARE(addedSpy.at(i).at(0).toInt(), i);
572         QCOMPARE(addedSpy.at(i).at(1).value<QQuickItem*>(), repeater->itemAt(i));
573     }
574
575     modelChangedSpy.clear();
576     countSpy.clear();
577     removedSpy.clear();
578     addedSpy.clear();
579
580     delete canvas;
581 }
582
583 // QTBUG-17156
584 void tst_QQuickRepeater::modelChanged()
585 {
586     QDeclarativeEngine engine;
587     QDeclarativeComponent component(&engine, testFileUrl("modelChanged.qml"));
588
589     QQuickItem *rootObject = qobject_cast<QQuickItem*>(component.create());
590     QVERIFY(rootObject);
591     QQuickRepeater *repeater = findItem<QQuickRepeater>(rootObject, "repeater");
592     QVERIFY(repeater);
593
594     repeater->setModel(4);
595     QCOMPARE(repeater->count(), 4);
596     QCOMPARE(repeater->property("itemsCount").toInt(), 4);
597     QCOMPARE(repeater->property("itemsFound").toList().count(), 4);
598
599     repeater->setModel(10);
600     QCOMPARE(repeater->count(), 10);
601     QCOMPARE(repeater->property("itemsCount").toInt(), 10);
602     QCOMPARE(repeater->property("itemsFound").toList().count(), 10);
603
604     delete rootObject;
605 }
606
607 void tst_QQuickRepeater::properties()
608 {
609     QDeclarativeEngine engine;
610     QDeclarativeComponent component(&engine, testFileUrl("properties.qml"));
611
612     QQuickItem *rootObject = qobject_cast<QQuickItem*>(component.create());
613     QVERIFY(rootObject);
614
615     QQuickRepeater *repeater = findItem<QQuickRepeater>(rootObject, "repeater");
616     QVERIFY(repeater);
617
618     QSignalSpy modelSpy(repeater, SIGNAL(modelChanged()));
619     repeater->setModel(3);
620     QCOMPARE(modelSpy.count(),1);
621     repeater->setModel(3);
622     QCOMPARE(modelSpy.count(),1);
623
624     QSignalSpy delegateSpy(repeater, SIGNAL(delegateChanged()));
625
626     QDeclarativeComponent rectComponent(&engine);
627     rectComponent.setData("import QtQuick 2.0; Rectangle {}", QUrl::fromLocalFile(""));
628
629     repeater->setDelegate(&rectComponent);
630     QCOMPARE(delegateSpy.count(),1);
631     repeater->setDelegate(&rectComponent);
632     QCOMPARE(delegateSpy.count(),1);
633
634     delete rootObject;
635 }
636
637 void tst_QQuickRepeater::asynchronous()
638 {
639     QQuickView *canvas = createView();
640     canvas->show();
641     QDeclarativeIncubationController controller;
642     canvas->engine()->setIncubationController(&controller);
643
644     canvas->setSource(testFileUrl("asyncloader.qml"));
645
646     QQuickItem *rootObject = qobject_cast<QQuickItem*>(canvas->rootObject());
647     QVERIFY(rootObject);
648
649     QQuickItem *container = findItem<QQuickItem>(rootObject, "container");
650     QVERIFY(!container);
651     while (!container) {
652         bool b = false;
653         controller.incubateWhile(&b);
654         container = findItem<QQuickItem>(rootObject, "container");
655     }
656
657     QQuickRepeater *repeater = 0;
658     while (!repeater) {
659         bool b = false;
660         controller.incubateWhile(&b);
661         repeater = findItem<QQuickRepeater>(rootObject, "repeater");
662     }
663
664     // items will be created one at a time
665     for (int i = 0; i < 10; ++i) {
666         QString name("delegate");
667         name += QString::number(i);
668         QVERIFY(findItem<QQuickItem>(container, name) == 0);
669         QQuickItem *item = 0;
670         while (!item) {
671             bool b = false;
672             controller.incubateWhile(&b);
673             item = findItem<QQuickItem>(container, name);
674         }
675     }
676
677     {
678         bool b = true;
679         controller.incubateWhile(&b);
680     }
681
682     // verify positioning
683     for (int i = 0; i < 10; ++i) {
684         QString name("delegate");
685         name += QString::number(i);
686         QQuickItem *item = findItem<QQuickItem>(container, name);
687         QTRY_COMPARE(item->y(), i * 50.0);
688     }
689
690     delete canvas;
691 }
692
693 void tst_QQuickRepeater::initParent()
694 {
695     QDeclarativeEngine engine;
696     QDeclarativeComponent component(&engine, testFileUrl("initparent.qml"));
697
698     QQuickItem *rootObject = qobject_cast<QQuickItem*>(component.create());
699     QVERIFY(rootObject);
700
701     QCOMPARE(qvariant_cast<QQuickItem*>(rootObject->property("parentItem")), rootObject);
702 }
703
704 QQuickView *tst_QQuickRepeater::createView()
705 {
706     QQuickView *canvas = new QQuickView(0);
707     canvas->setGeometry(0,0,240,320);
708
709     return canvas;
710 }
711
712 template<typename T>
713 T *tst_QQuickRepeater::findItem(QObject *parent, const QString &objectName, int index)
714 {
715     const QMetaObject &mo = T::staticMetaObject;
716     //qDebug() << parent->children().count() << "children";
717     for (int i = 0; i < parent->children().count(); ++i) {
718         QQuickItem *item = qobject_cast<QQuickItem*>(parent->children().at(i));
719         if (!item)
720             continue;
721         //qDebug() << "try" << item;
722         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
723             if (index != -1) {
724                 QDeclarativeExpression e(qmlContext(item), item, "index");
725                 if (e.evaluate().toInt() == index)
726                     return static_cast<T*>(item);
727             } else {
728                 return static_cast<T*>(item);
729             }
730         }
731         item = findItem<T>(item, objectName, index);
732         if (item)
733             return static_cast<T*>(item);
734     }
735
736     return 0;
737 }
738
739 template<typename T>
740 T *tst_QQuickRepeater::findItem(QObject *parent, const QString &objectName)
741 {
742     const QMetaObject &mo = T::staticMetaObject;
743     if (mo.cast(parent) && (objectName.isEmpty() || parent->objectName() == objectName))
744         return static_cast<T*>(parent);
745     for (int i = 0; i < parent->children().count(); ++i) {
746         QQuickItem *child = qobject_cast<QQuickItem*>(parent->children().at(i));
747         if (!child)
748             continue;
749         QQuickItem *item = findItem<T>(child, objectName);
750         if (item)
751             return static_cast<T*>(item);
752     }
753
754     return 0;
755 }
756
757 QTEST_MAIN(tst_QQuickRepeater)
758
759 #include "tst_qquickrepeater.moc"