summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorBea Lam <bea.lam@nokia.com>2010-09-17 05:15:15 (GMT)
committerJason McDonald <jason.mcdonald@nokia.com>2010-09-22 04:56:37 (GMT)
commitef414bc0ac9eb0feea57a578757be894abe226c0 (patch)
treebaa16097014075081f640f47911a62fc44cf9d52 /tests
parent755a82fea47402be5857edf2f882e0135e02267a (diff)
downloadQt-ef414bc0ac9eb0feea57a578757be894abe226c0.zip
Qt-ef414bc0ac9eb0feea57a578757be894abe226c0.tar.gz
Qt-ef414bc0ac9eb0feea57a578757be894abe226c0.tar.bz2
Changes to objects returned from get() should emit itemsChanged() so
that the view is updated (currently the model data changes but the view does not). In flat-model mode, get() now returns a QScriptClass-type object so that changes to the returned object are received, and for nested-model mode, the fix adds a QDeclarativeOpenMetaObject subclass and ModelNode::listIndex so that itemsChanged() can be emitted when the node data changes. Task-number: QTBUG-12363 (cherry picked from commit c2bbef949126826f0330c69dff1a8c96919f69a5)
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/declarative/qdeclarativelistmodel/tst_qdeclarativelistmodel.cpp307
1 files changed, 287 insertions, 20 deletions
diff --git a/tests/auto/declarative/qdeclarativelistmodel/tst_qdeclarativelistmodel.cpp b/tests/auto/declarative/qdeclarativelistmodel/tst_qdeclarativelistmodel.cpp
index 10805b4..70ec416 100644
--- a/tests/auto/declarative/qdeclarativelistmodel/tst_qdeclarativelistmodel.cpp
+++ b/tests/auto/declarative/qdeclarativelistmodel/tst_qdeclarativelistmodel.cpp
@@ -49,6 +49,7 @@
#include <QtCore/qtimer.h>
#include <QtCore/qdebug.h>
#include <QtCore/qtranslator.h>
+#include <QSignalSpy>
#include "../../../shared/util.h"
@@ -57,6 +58,8 @@
#define SRCDIR "."
#endif
+Q_DECLARE_METATYPE(QList<int>)
+
class tst_qdeclarativelistmodel : public QObject
{
Q_OBJECT
@@ -64,6 +67,7 @@ public:
tst_qdeclarativelistmodel() {}
private:
+ int roleFromName(const QDeclarativeListModel *model, const QString &roleName);
QScriptValue nestedListValue(QScriptEngine *eng) const;
QDeclarativeItem *createWorkerTest(QDeclarativeEngine *eng, QDeclarativeComponent *component, QDeclarativeListModel *model);
void waitForWorker(QDeclarativeItem *item);
@@ -78,6 +82,8 @@ private slots:
void dynamic();
void dynamic_worker_data();
void dynamic_worker();
+ void dynamic_worker_sync_data();
+ void dynamic_worker_sync();
void convertNestedToFlat_fail();
void convertNestedToFlat_fail_data();
void convertNestedToFlat_ok();
@@ -86,7 +92,23 @@ private slots:
void error_data();
void error();
void set();
+ void get();
+ void get_data();
+ void get_worker();
+ void get_worker_data();
+ void get_nested();
+ void get_nested_data();
};
+int tst_qdeclarativelistmodel::roleFromName(const QDeclarativeListModel *model, const QString &roleName)
+{
+ QList<int> roles = model->roles();
+ for (int i=0; i<roles.count(); i++) {
+ if (model->toString(roles[i]) == roleName)
+ return roles[i];
+ }
+ Q_ASSERT(false);
+ return -1;
+}
QScriptValue tst_qdeclarativelistmodel::nestedListValue(QScriptEngine *eng) const
{
@@ -196,6 +218,10 @@ void tst_qdeclarativelistmodel::dynamic_data()
QTest::newRow("get1") << "{get(0) === undefined}" << 1 << "";
QTest::newRow("get2") << "{get(-1) === undefined}" << 1 << "";
QTest::newRow("get3") << "{append({'foo':123});get(0) != undefined}" << 1 << "";
+ QTest::newRow("get4") << "{append({'foo':123});get(0).foo}" << 123 << "";
+
+ QTest::newRow("get-modify1") << "{append({'foo':123,'bar':456});get(0).foo = 333;get(0).foo}" << 333 << "";
+ QTest::newRow("get-modify2") << "{append({'z':1});append({'foo':123,'bar':456});get(1).bar = 999;get(1).bar}" << 999 << "";
QTest::newRow("append1") << "{append({'foo':123});count}" << 1 << "";
QTest::newRow("append2") << "{append({'foo':123,'bar':456});count}" << 1 << "";
@@ -310,8 +336,12 @@ void tst_qdeclarativelistmodel::dynamic_worker()
QFETCH(int, result);
QFETCH(QString, warning);
+ if (QByteArray(QTest::currentDataTag()).startsWith("nested"))
+ return;
+
// This is same as dynamic() except it applies the test to a ListModel called
- // from a WorkerScript (i.e. testing the internal NestedListModel class)
+ // from a WorkerScript (i.e. testing the internal FlatListModel that is created
+ // by the WorkerListModelAgent)
QDeclarativeListModel model;
QDeclarativeEngine eng;
@@ -330,27 +360,62 @@ void tst_qdeclarativelistmodel::dynamic_worker()
if (!warning.isEmpty())
QTest::ignoreMessage(QtWarningMsg, warning.toLatin1());
- if (operations.count() == 1) {
- // test count(), get() return the correct default values in the worker list model
- QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker",
- Q_ARG(QVariant, operations)));
- waitForWorker(item);
- QCOMPARE(QDeclarativeProperty(item, "result").read().toInt(), result);
- } else {
- // execute a set of commands on the worker list model, then check the
- // changes are reflected in the list model in the main thread
- if (QByteArray(QTest::currentDataTag()).startsWith("nested"))
- QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML ListModel: Cannot add nested list values when modifying or after modification from a worker script");
-
- QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker",
- Q_ARG(QVariant, operations.mid(0, operations.length()-1))));
- waitForWorker(item);
-
- QDeclarativeExpression e(eng.rootContext(), &model, operations.last().toString());
- if (!QByteArray(QTest::currentDataTag()).startsWith("nested"))
- QCOMPARE(e.evaluate().toInt(), result);
+ QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker",
+ Q_ARG(QVariant, operations)));
+ waitForWorker(item);
+ QCOMPARE(QDeclarativeProperty(item, "result").read().toInt(), result);
+
+ delete item;
+ qApp->processEvents();
+}
+
+
+
+void tst_qdeclarativelistmodel::dynamic_worker_sync_data()
+{
+ dynamic_data();
+}
+
+void tst_qdeclarativelistmodel::dynamic_worker_sync()
+{
+ QFETCH(QString, script);
+ QFETCH(int, result);
+ QFETCH(QString, warning);
+
+ // This is the same as dynamic_worker() except that it executes a set of list operations
+ // from the worker script, calls sync(), and tests the changes are reflected in the
+ // list in the main thread
+
+ QDeclarativeListModel model;
+ QDeclarativeEngine eng;
+ QDeclarativeComponent component(&eng, QUrl::fromLocalFile(SRCDIR "/data/model.qml"));
+ QDeclarativeItem *item = createWorkerTest(&eng, &component, &model);
+ QVERIFY(item != 0);
+
+ if (script[0] == QLatin1Char('{') && script[script.length()-1] == QLatin1Char('}'))
+ script = script.mid(1, script.length() - 2);
+ QVariantList operations;
+ foreach (const QString &s, script.split(';')) {
+ if (!s.isEmpty())
+ operations << s;
}
+ if (!warning.isEmpty())
+ QTest::ignoreMessage(QtWarningMsg, warning.toLatin1());
+
+ // execute a set of commands on the worker list model, then check the
+ // changes are reflected in the list model in the main thread
+ if (QByteArray(QTest::currentDataTag()).startsWith("nested"))
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML ListModel: Cannot add nested list values when modifying or after modification from a worker script");
+
+ QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker",
+ Q_ARG(QVariant, operations.mid(0, operations.length()-1))));
+ waitForWorker(item);
+
+ QDeclarativeExpression e(eng.rootContext(), &model, operations.last().toString());
+ if (!QByteArray(QTest::currentDataTag()).startsWith("nested"))
+ QCOMPARE(e.evaluate().toInt(), result);
+
delete item;
qApp->processEvents();
}
@@ -595,6 +660,9 @@ void tst_qdeclarativelistmodel::error()
}
}
+/*
+ Test model changes from set() are available to the view
+*/
void tst_qdeclarativelistmodel::set()
{
QDeclarativeEngine engine;
@@ -618,6 +686,205 @@ void tst_qdeclarativelistmodel::set()
QCOMPARE(model.data(0, model.roles()[0]), qVariantFromValue(false));
}
+/*
+ Test model changes on values returned by get() are available to the view
+*/
+void tst_qdeclarativelistmodel::get()
+{
+ QFETCH(QString, expression);
+ QFETCH(int, index);
+ QFETCH(QString, roleName);
+ QFETCH(QVariant, roleValue);
+
+ QDeclarativeEngine eng;
+ QDeclarativeComponent component(&eng);
+ component.setData(
+ "import Qt 4.7\n"
+ "ListModel { \n"
+ "ListElement { roleA: 100 }\n"
+ "ListElement { roleA: 200; roleB: 400 } \n"
+ "ListElement { roleA: 200; roleB: 400 } \n"
+ "}", QUrl());
+ QDeclarativeListModel *model = qobject_cast<QDeclarativeListModel*>(component.create());
+ int role = roleFromName(model, roleName);
+
+ QSignalSpy spy(model, SIGNAL(itemsChanged(int, int, QList<int>)));
+ QDeclarativeExpression expr(eng.rootContext(), model, expression);
+ expr.evaluate();
+ QVERIFY(!expr.hasError());
+
+ QCOMPARE(model->data(index, role), roleValue);
+ QCOMPARE(spy.count(), 1);
+
+ QList<QVariant> spyResult = spy.takeFirst();
+ QCOMPARE(spyResult.at(0).toInt(), index);
+ QCOMPARE(spyResult.at(1).toInt(), 1); // only 1 item is modified at a time
+ QCOMPARE(spyResult.at(2).value<QList<int> >(), (QList<int>() << role));
+}
+
+void tst_qdeclarativelistmodel::get_data()
+{
+ QTest::addColumn<QString>("expression");
+ QTest::addColumn<int>("index");
+ QTest::addColumn<QString>("roleName");
+ QTest::addColumn<QVariant>("roleValue");
+
+ QTest::newRow("simple value") << "get(0).roleA = 500" << 0 << "roleA" << QVariant(500);
+ QTest::newRow("simple value 2") << "get(1).roleB = 500" << 1 << "roleB" << QVariant(500);
+
+ QVariantMap map;
+ map["zzz"] = 123;
+ QTest::newRow("object value") << "get(1).roleB = {'zzz':123}" << 1 << "roleB" << QVariant::fromValue(map);
+
+ QVariantList list;
+ map.clear(); map["a"] = 50; map["b"] = 500;
+ list << map;
+ map.clear(); map["c"] = 1000;
+ list << map;
+ QTest::newRow("list of objects") << "get(2).roleB = [{'a': 50, 'b': 500}, {'c': 1000}]" << 2 << "roleB" << QVariant::fromValue(list);
+}
+
+void tst_qdeclarativelistmodel::get_worker()
+{
+ QFETCH(QString, expression);
+ QFETCH(int, index);
+ QFETCH(QString, roleName);
+ QFETCH(QVariant, roleValue);
+
+ QDeclarativeListModel model;
+ QDeclarativeEngine eng;
+ QDeclarativeComponent component(&eng, QUrl::fromLocalFile(SRCDIR "/data/model.qml"));
+ QDeclarativeItem *item = createWorkerTest(&eng, &component, &model);
+ QVERIFY(item != 0);
+ QScriptEngine *seng = QDeclarativeEnginePrivate::getScriptEngine(&eng);
+
+ // Add some values like get() test
+ QScriptValue sv = seng->newObject();
+ sv.setProperty(QLatin1String("roleA"), seng->newVariant(QVariant::fromValue(100)));
+ model.append(sv);
+ sv = seng->newObject();
+ sv.setProperty(QLatin1String("roleA"), seng->newVariant(QVariant::fromValue(200)));
+ sv.setProperty(QLatin1String("roleB"), seng->newVariant(QVariant::fromValue(400)));
+ model.append(sv);
+ model.append(sv);
+ int role = roleFromName(&model, roleName);
+
+ const char *warning = "<Unknown File>: QML ListModel: Cannot add nested list values when modifying or after modification from a worker script";
+ if (roleValue.type() == QVariant::List || roleValue.type() == QVariant::Map)
+ QTest::ignoreMessage(QtWarningMsg, warning);
+ QSignalSpy spy(&model, SIGNAL(itemsChanged(int, int, QList<int>)));
+
+ // in the worker thread, change the model data and call sync()
+ QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker",
+ Q_ARG(QVariant, QStringList(expression))));
+ waitForWorker(item);
+
+ // see if we receive the model changes in the main thread's model
+ if (roleValue.type() == QVariant::List || roleValue.type() == QVariant::Map) {
+ QVERIFY(model.data(index, role) != roleValue);
+ QCOMPARE(spy.count(), 0);
+ } else {
+ QCOMPARE(model.data(index, role), roleValue);
+ QCOMPARE(spy.count(), 1);
+
+ QList<QVariant> spyResult = spy.takeFirst();
+ QCOMPARE(spyResult.at(0).toInt(), index);
+ QCOMPARE(spyResult.at(1).toInt(), 1); // only 1 item is modified at a time
+ QVERIFY(spyResult.at(2).value<QList<int> >().contains(role));
+ }
+}
+
+void tst_qdeclarativelistmodel::get_worker_data()
+{
+ get_data();
+}
+
+/*
+ Test that the tests run in get() also work for nested list data
+*/
+void tst_qdeclarativelistmodel::get_nested()
+{
+ QFETCH(QString, expression);
+ QFETCH(int, index);
+ QFETCH(QString, roleName);
+ QFETCH(QVariant, roleValue);
+
+ QDeclarativeEngine eng;
+ QDeclarativeComponent component(&eng);
+ component.setData(
+ "import Qt 4.7\n"
+ "ListModel { \n"
+ "ListElement {\n"
+ "listRoleA: [\n"
+ "ListElement { roleA: 100 },\n"
+ "ListElement { roleA: 200; roleB: 400 },\n"
+ "ListElement { roleA: 200; roleB: 400 } \n"
+ "]\n"
+ "}\n"
+ "ListElement {\n"
+ "listRoleA: [\n"
+ "ListElement { roleA: 100 },\n"
+ "ListElement { roleA: 200; roleB: 400 },\n"
+ "ListElement { roleA: 200; roleB: 400 } \n"
+ "]\n"
+ "listRoleB: [\n"
+ "ListElement { roleA: 100 },\n"
+ "ListElement { roleA: 200; roleB: 400 },\n"
+ "ListElement { roleA: 200; roleB: 400 } \n"
+ "]\n"
+ "listRoleC: [\n"
+ "ListElement { roleA: 100 },\n"
+ "ListElement { roleA: 200; roleB: 400 },\n"
+ "ListElement { roleA: 200; roleB: 400 } \n"
+ "]\n"
+ "}\n"
+ "}", QUrl());
+ QDeclarativeListModel *model = qobject_cast<QDeclarativeListModel*>(component.create());
+ QVERIFY(component.errorString().isEmpty());
+ QDeclarativeListModel *childModel;
+
+ // Test setting the inner list data for:
+ // get(0).listRoleA
+ // get(1).listRoleA
+ // get(1).listRoleB
+ // get(1).listRoleC
+
+ QList<QPair<int, QString> > testData;
+ testData << qMakePair(0, QString("listRoleA"));
+ testData << qMakePair(1, QString("listRoleA"));
+ testData << qMakePair(1, QString("listRoleB"));
+ testData << qMakePair(1, QString("listRoleC"));
+
+ for (int i=0; i<testData.count(); i++) {
+ int outerListIndex = testData[i].first;
+ QString outerListRoleName = testData[i].second;
+ int outerListRole = roleFromName(model, outerListRoleName);
+
+ childModel = qobject_cast<QDeclarativeListModel*>(model->data(outerListIndex, outerListRole).value<QObject*>());
+ QVERIFY(childModel);
+
+ QString extendedExpression = QString("get(%1).%2.%3").arg(outerListIndex).arg(outerListRoleName).arg(expression);
+ QDeclarativeExpression expr(eng.rootContext(), model, extendedExpression);
+
+ QSignalSpy spy(childModel, SIGNAL(itemsChanged(int, int, QList<int>)));
+ expr.evaluate();
+ QVERIFY(!expr.hasError());
+
+ int role = roleFromName(childModel, roleName);
+ QCOMPARE(childModel->data(index, role), roleValue);
+ QCOMPARE(spy.count(), 1);
+
+ QList<QVariant> spyResult = spy.takeFirst();
+ QCOMPARE(spyResult.at(0).toInt(), index);
+ QCOMPARE(spyResult.at(1).toInt(), 1); // only 1 item is modified at a time
+ QCOMPARE(spyResult.at(2).value<QList<int> >(), (QList<int>() << role));
+ }
+}
+
+void tst_qdeclarativelistmodel::get_nested_data()
+{
+ get_data();
+}
QTEST_MAIN(tst_qdeclarativelistmodel)