diff options
author | Bea Lam <bea.lam@nokia.com> | 2010-09-17 05:15:15 (GMT) |
---|---|---|
committer | Jason McDonald <jason.mcdonald@nokia.com> | 2010-09-22 04:56:37 (GMT) |
commit | ef414bc0ac9eb0feea57a578757be894abe226c0 (patch) | |
tree | baa16097014075081f640f47911a62fc44cf9d52 /tests | |
parent | 755a82fea47402be5857edf2f882e0135e02267a (diff) | |
download | Qt-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.cpp | 307 |
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) |