From cd6204e77c4ff454030529a5c02be184965bb9e1 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 30 Dec 2013 12:42:47 +0100 Subject: Introduce EnginioModel::reload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [ChangeLog][EnginioModel] Added reload() function to EnginioModel to force a refresh of all model data. Change-Id: I7b1a3fdd6e63950f5494d4bab8511feb9f80d9fb Reviewed-by: Jędrzej Nowacki --- src/enginio_client/enginiobasemodel_p.h | 25 ++++-- src/enginio_client/enginiomodel.cpp | 20 ++++- src/enginio_client/enginiomodel.h | 2 + src/enginio_plugin/enginioqmlmodel.cpp | 6 ++ src/enginio_plugin/enginioqmlmodel_p.h | 1 + tests/auto/enginiomodel/tst_enginiomodel.cpp | 130 +++++++++++++++++++++++++++ tests/auto/qmltests/tst_model.qml | 31 +++++++ 7 files changed, 205 insertions(+), 10 deletions(-) diff --git a/src/enginio_client/enginiobasemodel_p.h b/src/enginio_client/enginiobasemodel_p.h index bd6bd2f..c615a23 100644 --- a/src/enginio_client/enginiobasemodel_p.h +++ b/src/enginio_client/enginiobasemodel_p.h @@ -586,21 +586,27 @@ public: filter.insert(EnginioString::data, objectType); _notifications.connectToBackend(this, _enginio, filter); - // send full query - QJsonObject query = queryAsJson(); - ObjectAdaptor aQuery(query); - QNetworkReply *nreply = _enginio->query(aQuery, static_cast(_operation)); - EnginioReplyState *ereply = _enginio->createReply(nreply); - if (_canFetchMore) - _latestRequestedOffset = query[EnginioString::limit].toDouble(); - FinishedFullQueryRequest finshedRequest = { this, ereply }; - QObject::connect(ereply, &EnginioReplyState::dataChanged, _replyConnectionConntext, finshedRequest); + EnginioReplyState *ereply = reload(); QObject::connect(ereply, &EnginioReplyState::dataChanged, ereply, &EnginioReplyState::deleteLater); } else { fullQueryReset(QJsonArray()); } } + EnginioReplyState *reload() + { + // send full query + QJsonObject query = queryAsJson(); + ObjectAdaptor aQuery(query); + QNetworkReply *nreply = _enginio->query(aQuery, static_cast(_operation)); + EnginioReplyState *ereply = _enginio->createReply(nreply); + if (_canFetchMore) + _latestRequestedOffset = query[EnginioString::limit].toDouble(); + FinishedFullQueryRequest finshedRequest = { this, ereply }; + QObject::connect(ereply, &EnginioReplyState::dataChanged, _replyConnectionConntext, finshedRequest); + return ereply; + } + void finishedIncrementalUpdateRequest(const EnginioReplyState *reply, const QJsonObject &query) { Q_ASSERT(_canFetchMore); @@ -993,6 +999,7 @@ struct EnginioModelPrivateT : public EnginioBaseModelPrivate Reply *append(const QJsonObject &value) { return static_cast(Base::append(value)); } Reply *remove(int row) { return static_cast(Base::remove(row)); } Reply *setValue(int row, const QString &role, const QVariant &value) { return static_cast(Base::setValue(row, role, value)); } + Reply *reload() { return static_cast(Base::reload()); } bool queryIsEmpty() const Q_DECL_OVERRIDE { diff --git a/src/enginio_client/enginiomodel.cpp b/src/enginio_client/enginiomodel.cpp index 3d5f93d..644b9a6 100644 --- a/src/enginio_client/enginiomodel.cpp +++ b/src/enginio_client/enginiomodel.cpp @@ -479,6 +479,25 @@ void EnginioModel::setOperation(Enginio::Operation operation) emit operationChanged(operation); } +/*! + Reload the model data from the server. + This is similar to reset and will emit \l modelAboutToBeReset() and \l modelReset(). + This function invalidated the internal state of the model, reloads it from the backend + and resets all views. + + \note when using this function while other requests to the server are made the result + is undefined. For example when calling append() and then reset() before append finished, + the model may or may not contain the result of the append operation. + + \return reply from backend + \since 1.1 +*/ +EnginioReply *EnginioModel::reload() +{ + Q_D(EnginioModel); + return d->reload(); +} + /*! \include model-append.qdocinc \sa EnginioClient::create() @@ -543,7 +562,6 @@ EnginioReply *EnginioModel::setData(int row, const QVariant &value, const QStrin return d->setValue(row, role, value); } - Qt::ItemFlags EnginioBaseModel::flags(const QModelIndex &index) const { return QAbstractListModel::flags(index) | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable; diff --git a/src/enginio_client/enginiomodel.h b/src/enginio_client/enginiomodel.h index 81c7eeb..f3cb0ca 100644 --- a/src/enginio_client/enginiomodel.h +++ b/src/enginio_client/enginiomodel.h @@ -82,6 +82,8 @@ public: Q_INVOKABLE EnginioReply *setData(int row, const QVariant &value, const QString &role); using EnginioBaseModel::setData; + Q_INVOKABLE EnginioReply *reload(); + Q_SIGNALS: void queryChanged(const QJsonObject &query); void clientChanged(EnginioClient *client); diff --git a/src/enginio_plugin/enginioqmlmodel.cpp b/src/enginio_plugin/enginioqmlmodel.cpp index 43d0198..05410cf 100644 --- a/src/enginio_plugin/enginioqmlmodel.cpp +++ b/src/enginio_plugin/enginioqmlmodel.cpp @@ -255,6 +255,12 @@ EnginioQmlReply *EnginioQmlModel::setProperty(int row, const QString &role, cons return d->setValue(row, role, value); } +EnginioQmlReply *EnginioQmlModel::reload() +{ + Q_D(EnginioQmlModel); + return d->reload(); +} + EnginioQmlClient *EnginioQmlModel::client() const { Q_D(const EnginioQmlModel); diff --git a/src/enginio_plugin/enginioqmlmodel_p.h b/src/enginio_plugin/enginioqmlmodel_p.h index 9b52308..c78d9af 100644 --- a/src/enginio_plugin/enginioqmlmodel_p.h +++ b/src/enginio_plugin/enginioqmlmodel_p.h @@ -79,6 +79,7 @@ public: Q_INVOKABLE EnginioQmlReply *append(const QJSValue &value); Q_INVOKABLE EnginioQmlReply *remove(int row); Q_INVOKABLE EnginioQmlReply *setProperty(int row, const QString &role, const QVariant &value); + Q_INVOKABLE EnginioQmlReply *reload(); Q_SIGNALS: void queryChanged(const QJSValue &query); diff --git a/tests/auto/enginiomodel/tst_enginiomodel.cpp b/tests/auto/enginiomodel/tst_enginiomodel.cpp index 5edc707..744ce12 100644 --- a/tests/auto/enginiomodel/tst_enginiomodel.cpp +++ b/tests/auto/enginiomodel/tst_enginiomodel.cpp @@ -98,6 +98,8 @@ private slots: void deleteModelDurringRequests(); void updatingRoles(); void setData(); + void reload(); + private: template void externallyRemovedImpl(); @@ -119,6 +121,23 @@ void tst_EnginioModel::initTestCase() // The test operates on user data. EnginioTests::prepareTestUsersAndUserGroups(_backendId); EnginioTests::prepareTestObjectType(_backendName); + + // Object types for the reload test + QJsonObject reload1; + reload1["name"] = QStringLiteral("reload1"); + QJsonObject title; + title["name"] = QStringLiteral("title"); + title["type"] = QStringLiteral("string"); + title["indexed"] = false; + QJsonArray properties; + properties.append(title); + reload1["properties"] = properties; + QVERIFY(_backendManager.createObjectType(_backendName, EnginioTests::TESTAPP_ENV, reload1)); + + QJsonObject reload2; + reload2["name"] = QStringLiteral("reload2"); + reload2["properties"] = properties; + QVERIFY(_backendManager.createObjectType(_backendName, EnginioTests::TESTAPP_ENV, reload2)); } void tst_EnginioModel::cleanupTestCase() @@ -1557,5 +1576,116 @@ void tst_EnginioModel::deleteReply() QTRY_COMPARE(counter, replies.count()); } +QJsonObject createTestObject(const QString &name, const QString &type) +{ + QJsonObject obj; + obj.insert("title", name); + obj.insert("objectType", type); + return obj; +} + +void tst_EnginioModel::reload() +{ + QString objectType = "objects.reload1"; + + EnginioClient client; + client.setBackendId(_backendId); + client.setServiceUrl(EnginioTests::TESTAPP_URL); + + QJsonObject query; + query.insert("objectType", objectType); + + EnginioModel model; + model.disableNotifications(); + model.setQuery(query); + model.setClient(&client); + + QCOMPARE(model.rowCount(), 0); + + // create an object, since notification are disabled the model cannot know about it + EnginioReply *r1(client.create(createTestObject(QString::fromLatin1("o1"), objectType))); + QTRY_VERIFY(r1->isFinished()); + CHECK_NO_ERROR(r1); + QCOMPARE(model.rowCount(), 0); + + // reload and verify that the object appears + EnginioReply *reload(model.reload()); + QTRY_VERIFY(reload->isFinished()); + CHECK_NO_ERROR(reload); + QCOMPARE(model.rowCount(), 1); + + // this test mostly checks that we don't crash + // when calling reload while other operations are on-going it is undefined + // if the result contains the data of them or not + + // create object and reload before that is finished + EnginioReply *r2(client.create(createTestObject(QString::fromLatin1("o2"), objectType))); + EnginioReply *reload2 = model.reload(); + QVERIFY(reload2); + QTRY_VERIFY(r2->isFinished()); + CHECK_NO_ERROR(r2); + QTRY_VERIFY(reload2->isFinished()); + CHECK_NO_ERROR(reload2); + + // create object bug delay it's response until reload is done + EnginioReply *r3(client.create(createTestObject(QString::fromLatin1("o3"), objectType))); + QVERIFY(r3); + r3->setDelayFinishedSignal(true); + EnginioReply *reload3(model.reload()); + QVERIFY(reload3); + QTRY_VERIFY(reload3->isFinished()); + r3->setDelayFinishedSignal(false); + QTRY_VERIFY(r3->isFinished()); + + // make sure we are in a defined state again + reload = model.reload(); + QTRY_VERIFY(reload->isFinished()); + QCOMPARE(model.rowCount(), 3); + + EnginioReply *r4(model.append(createTestObject(QString::fromLatin1("o4"), objectType))); + EnginioReply *reload4(model.reload()); + EnginioReply *r5(model.append(createTestObject(QString::fromLatin1("o5"), objectType))); + EnginioReply *reload5(model.reload()); + QTRY_VERIFY(r4->isFinished() && reload4->isFinished() && r5->isFinished() && reload5->isFinished()); + + reload = model.reload(); + QTRY_VERIFY(reload->isFinished()); + QCOMPARE(model.rowCount(), 5); + + // randomly reordered append and reset calls + EnginioReply *r6(model.append(createTestObject(QString::fromLatin1("o6"), objectType))); + r6->setDelayFinishedSignal(true); + EnginioReply *reload6(model.reload()); + reload6->setDelayFinishedSignal(true); + EnginioReply *r7(model.append(createTestObject(QString::fromLatin1("o7"), objectType))); + r7->setDelayFinishedSignal(true); + EnginioReply *reload7(model.reload()); + EnginioReply *c7(model.setData(6, QString::fromLatin1("object7"), QString::fromLatin1("title"))); + EnginioReply *c5(model.setData(4, QString::fromLatin1("object5"), QString::fromLatin1("title"))); + QTRY_VERIFY(reload7->isFinished()); + r6->setDelayFinishedSignal(false); + r7->setDelayFinishedSignal(false); + reload6->setDelayFinishedSignal(false); + QTRY_VERIFY(c5->isFinished() && c7->isFinished() && r6->isFinished() && r7->isFinished() && reload6->isFinished()); + + reload = model.reload(); + QTRY_VERIFY(reload->isFinished()); + QCOMPARE(model.rowCount(), 7); + + // completely try to mess it up by changing the query + // object type reload2 is empty + EnginioReply *reload8 = model.reload(); + reload8->setDelayFinishedSignal(true); + QJsonObject query2; + query2.insert("objectTypes", "objects.reload2"); + model.setQuery(query2); + + QSignalSpy spy(&model, SIGNAL(modelReset())); + QTRY_VERIFY(spy.count()); + reload8->setDelayFinishedSignal(false); + QTRY_VERIFY(reload8->isFinished()); + QCOMPARE(model.rowCount(), 0); +} + QTEST_MAIN(tst_EnginioModel) #include "tst_enginiomodel.moc" diff --git a/tests/auto/qmltests/tst_model.qml b/tests/auto/qmltests/tst_model.qml index e98b41a..b7535bf 100644 --- a/tests/auto/qmltests/tst_model.qml +++ b/tests/auto/qmltests/tst_model.qml @@ -511,4 +511,35 @@ Item { tryCompare(modelRowCount, "rowCountChangedCounter", initialRowCountChangedCounter + 4) } } + + TestCase { + name: "EnginioModel: reload" + + EnginioModel { + id: model + client: EnginioClient { + id: client + serviceUrl: AppConfig.backendData.serviceUrl + + property int errorCount: 0 + onError: { + ++errorCount + replyDumpDebugInfo(reply) + } + + backendId: AppConfig.backendData.id + } + query: { + "objectType": AppConfig.testObjectType, + "query": {"testCase": "EnginioModel: rowCount"} + } + } + + function test_rowCount() + { + var r = model.reload(); + tryCompare(r, "isFinished", true); + compare(client.errorCount, 0) + } + } } -- cgit v1.2.1