diff options
author | Renato Araujo Oliveira Filho <renato.filho@canonical.com> | 2012-10-16 07:56:41 +0000 |
---|---|---|
committer | Tarmac <> | 2012-10-16 07:56:41 +0000 |
commit | 340e67d86483b2038d087b6ff2615e0d5c7cc5cc (patch) | |
tree | 5043b5daf97aa9e2bb2e87e7d5809b3a762e556e | |
parent | 5999e7a42962140b6479995d77a178186b6b2fcc (diff) | |
parent | 5c56d1288ba905ab162246c65fffbc69a831a14e (diff) | |
download | qmenumodel-340e67d86483b2038d087b6ff2615e0d5c7cc5cc.tar.gz qmenumodel-340e67d86483b2038d087b6ff2615e0d5c7cc5cc.tar.bz2 qmenumodel-340e67d86483b2038d087b6ff2615e0d5c7cc5cc.zip |
Avoid emit model reset during QMenuModel destructor, to avoid crash in QML engine.. Approved by Olivier Tilloy, PS Jenkins bot, jenkins.
-rw-r--r-- | .bzrignore | 2 | ||||
-rw-r--r-- | libqmenumodel/src/qmenumodel.cpp | 19 | ||||
-rw-r--r-- | libqmenumodel/src/qmenumodel.h | 1 | ||||
-rw-r--r-- | tests/client/CMakeLists.txt | 5 | ||||
-rw-r--r-- | tests/client/loadmodel.qml | 33 | ||||
-rw-r--r-- | tests/client/loadmodel2.qml | 22 | ||||
-rw-r--r-- | tests/client/modeltest.cpp | 22 | ||||
-rw-r--r-- | tests/client/qmlfiles.h.in | 3 | ||||
-rw-r--r-- | tests/client/qmltest.cpp | 120 | ||||
-rwxr-xr-x | tests/client/script_qmltest.py | 12 |
10 files changed, 232 insertions, 7 deletions
@@ -19,6 +19,8 @@ tests/client/actiongrouptest tests/client/convertertest tests/client/menuchangestest tests/client/modeltest +tests/client/qmlfiles.h +tests/client/qmltest tests/client/servicetest doc/html/ diff --git a/libqmenumodel/src/qmenumodel.cpp b/libqmenumodel/src/qmenumodel.cpp index 1aaf210..a45e5ff 100644 --- a/libqmenumodel/src/qmenumodel.cpp +++ b/libqmenumodel/src/qmenumodel.cpp @@ -56,7 +56,7 @@ QMenuModel::QMenuModel(GMenuModel *other, QObject *parent) /*! \internal */ QMenuModel::~QMenuModel() { - setMenuModel(NULL); + clearModel(); } /*! \internal */ @@ -68,11 +68,7 @@ void QMenuModel::setMenuModel(GMenuModel *other) beginResetModel(); - if (m_menuModel) { - g_signal_handler_disconnect(m_menuModel, m_signalChangedId); - m_signalChangedId = 0; - g_object_unref(m_menuModel); - } + clearModel(); m_menuModel = other; @@ -95,6 +91,17 @@ GMenuModel *QMenuModel::menuModel() const } /*! \internal */ +void QMenuModel::clearModel() +{ + if (m_menuModel) { + g_signal_handler_disconnect(m_menuModel, m_signalChangedId); + m_signalChangedId = 0; + g_object_unref(m_menuModel); + m_menuModel = NULL; + } +} + +/*! \internal */ QVariant QMenuModel::data(const QModelIndex &index, int role) const { QVariant attribute; diff --git a/libqmenumodel/src/qmenumodel.h b/libqmenumodel/src/qmenumodel.h index b5c76c0..22c30df 100644 --- a/libqmenumodel/src/qmenumodel.h +++ b/libqmenumodel/src/qmenumodel.h @@ -60,6 +60,7 @@ private: QVariant getLink(const QModelIndex &index, const QString &linkName) const; QVariant getExtraProperties(const QModelIndex &index) const; QString parseExtraPropertyName(const QString &name) const; + void clearModel(); static void onItemsChanged(GMenuModel *model, gint position, gint removed, gint added, gpointer data); }; diff --git a/tests/client/CMakeLists.txt b/tests/client/CMakeLists.txt index 7455b16..9bce370 100644 --- a/tests/client/CMakeLists.txt +++ b/tests/client/CMakeLists.txt @@ -1,6 +1,6 @@ macro(declare_test testname)
add_executable(${testname} ${testname}.cpp)
- qt5_use_modules(${testname} Core DBus Widgets Test)
+ qt5_use_modules(${testname} Core DBus Widgets Test Qml Quick)
target_link_libraries(${testname}
qmenumodel
dbusmenuscript
@@ -50,5 +50,8 @@ declare_test(servicetest) declare_test(menuchangestest)
declare_test(modeltest)
declare_test(actiongrouptest)
+declare_test(qmltest)
declare_simple_test(convertertest)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/qmlfiles.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/qmlfiles.h)
diff --git a/tests/client/loadmodel.qml b/tests/client/loadmodel.qml new file mode 100644 index 0000000..7a1f045 --- /dev/null +++ b/tests/client/loadmodel.qml @@ -0,0 +1,33 @@ +import QtQuick 2.0 +import QMenuModel 0.1 + +Item { + id: root + width: 100 + height: 100 + + property bool reset: resetModel + + onResetChanged: { + if (reset) { + // destroy the current model and check if it will not crash the QML engine + view.model.destroy(); + } + } + + ListView { + id: view + anchors.fill: parent + delegate: Text { + text: label + } + } + + Component.onCompleted: { + // dynamically create the model to destroy it later + var model = Qt.createQmlObject("import QMenuModel 0.1; QDBusMenuModel { id: menuModel; busType: globalBusType; busName: globalBusName; objectPath: globalObjectPath; }", view, ""); + model.start(); + view.model = model; + } +} + diff --git a/tests/client/loadmodel2.qml b/tests/client/loadmodel2.qml new file mode 100644 index 0000000..dfe265d --- /dev/null +++ b/tests/client/loadmodel2.qml @@ -0,0 +1,22 @@ +import QtQuick 2.0 +import QMenuModel 0.1 + +Item { + width: 100 + height: 100 + + QDBusMenuModel { + id: menuModel + busType: globalBusType + busName: globalBusName + objectPath: globalObjectPath + } + + ListView { + model: menuModel + delegate: Item {} + } + + Component.onCompleted: menuModel.start() +} + diff --git a/tests/client/modeltest.cpp b/tests/client/modeltest.cpp index 9f6ee76..03629ca 100644 --- a/tests/client/modeltest.cpp +++ b/tests/client/modeltest.cpp @@ -204,6 +204,28 @@ private Q_SLOTS: QCOMPARE(v.toString(), QString("42")); } + /* + * Test if model is destroyed without crash + */ + void testDestroyModel() + { + // Make menu available + m_script.publishMenu(); + m_script.run(); + + // create a new model + QDBusMenuModel *model = new QDBusMenuModel(); + model->setBusType(DBusEnums::SessionBus); + model->setBusName(MENU_SERVICE_NAME); + model->setObjectPath(MENU_OBJECT_PATH); + model->start(); + + // Wait for dbus sync + QTest::qWait(500); + + delete model; + } + }; QTEST_MAIN(ModelTest) diff --git a/tests/client/qmlfiles.h.in b/tests/client/qmlfiles.h.in new file mode 100644 index 0000000..876b78f --- /dev/null +++ b/tests/client/qmlfiles.h.in @@ -0,0 +1,3 @@ +const char* QML_BASE_DIR = "@libqmenumodel_BINARY_DIR@"; +const char* LOADMODEL_QML = "@CMAKE_CURRENT_SOURCE_DIR@/loadmodel.qml"; +const char* LOADMODEL2_QML = "@CMAKE_CURRENT_SOURCE_DIR@/loadmodel2.qml"; diff --git a/tests/client/qmltest.cpp b/tests/client/qmltest.cpp new file mode 100644 index 0000000..4e6f288 --- /dev/null +++ b/tests/client/qmltest.cpp @@ -0,0 +1,120 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authors: + * Renato Araujo Oliveira Filho <renato@canonical.com> + */ + +extern "C" { +#include <glib-object.h> +} + +#include "qdbusmenumodel.h" +#include "dbusmenuscript.h" +#include "qmlfiles.h" + +#include <QObject> +#include <QSignalSpy> +#include <QtTestGui> +#include <QDebug> + +#include <QQmlContext> +#include <QQmlEngine> +#include <QQuickView> + + +class QMLTest : public QObject +{ + Q_OBJECT +private: + DBusMenuScript m_script; + +private Q_SLOTS: + void initTestCase() + { + g_type_init(); + Q_ASSERT(m_script.connect()); + } + + void cleanupTestCase() + { + m_script.quit(); + } + + void init() + { + } + + void cleanup() + { + m_script.unpublishMenu(); + } + + /* + * Test if model is destroyed without crash + */ + void destroyModel() + { + m_script.publishMenu(); + m_script.run(); + QTest::qWait(500); + + QQuickView *view = new QQuickView; + view->engine()->addImportPath(QML_BASE_DIR); + view->engine()->rootContext()->setContextProperty("resetModel", QVariant(false)); + view->engine()->rootContext()->setContextProperty("globalBusType", DBusEnums::SessionBus); + view->engine()->rootContext()->setContextProperty("globalBusName", MENU_SERVICE_NAME); + view->engine()->rootContext()->setContextProperty("globalObjectPath", MENU_OBJECT_PATH); + view->setSource(QUrl::fromLocalFile(LOADMODEL_QML)); + view->show(); + QTest::qWait(500); + view->engine()->rootContext()->setContextProperty("resetModel", true); + QTest::qWait(500); + } + + /* + * Test the menu model disappearing from the bus and reappearing + * while the QML application is running. + */ + void testServiceDisappear() + { + m_script.publishMenu(); + m_script.run(); + QTest::qWait(500); + + QQuickView *view = new QQuickView; + view->engine()->addImportPath(QML_BASE_DIR); + view->engine()->rootContext()->setContextProperty("globalBusType", DBusEnums::SessionBus); + view->engine()->rootContext()->setContextProperty("globalBusName", MENU_SERVICE_NAME); + view->engine()->rootContext()->setContextProperty("globalObjectPath", MENU_OBJECT_PATH); + view->setSource(QUrl::fromLocalFile(LOADMODEL2_QML)); + view->show(); + QTest::qWait(500); + + m_script.unpublishMenu(); + QTest::qWait(500); + + m_script.publishMenu(); + m_script.run(); + QTest::qWait(500); + + delete view; + QTest::qWait(1000); + } +}; + +QTEST_MAIN(QMLTest) + +#include "qmltest.moc" diff --git a/tests/client/script_qmltest.py b/tests/client/script_qmltest.py new file mode 100755 index 0000000..385256c --- /dev/null +++ b/tests/client/script_qmltest.py @@ -0,0 +1,12 @@ +#!/usr/bin/python2.7 + +import time +from menuscript import Script, ActionList, MENU_OBJECT_PATH + +al = ActionList(MENU_OBJECT_PATH) +al.appendItem("Menu0", "Menu0") +al.appendItem("Menu1", "Menu1") + +t = Script.create(al) +t.run() + |