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