aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRenato Araujo Oliveira Filho <renato.filho@canonical.com>2012-10-16 07:56:41 +0000
committerTarmac <>2012-10-16 07:56:41 +0000
commit340e67d86483b2038d087b6ff2615e0d5c7cc5cc (patch)
tree5043b5daf97aa9e2bb2e87e7d5809b3a762e556e
parent5999e7a42962140b6479995d77a178186b6b2fcc (diff)
parent5c56d1288ba905ab162246c65fffbc69a831a14e (diff)
downloadqmenumodel-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--.bzrignore2
-rw-r--r--libqmenumodel/src/qmenumodel.cpp19
-rw-r--r--libqmenumodel/src/qmenumodel.h1
-rw-r--r--tests/client/CMakeLists.txt5
-rw-r--r--tests/client/loadmodel.qml33
-rw-r--r--tests/client/loadmodel2.qml22
-rw-r--r--tests/client/modeltest.cpp22
-rw-r--r--tests/client/qmlfiles.h.in3
-rw-r--r--tests/client/qmltest.cpp120
-rwxr-xr-xtests/client/script_qmltest.py12
10 files changed, 232 insertions, 7 deletions
diff --git a/.bzrignore b/.bzrignore
index 7722c4c..8dfdfff 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -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()
+