on xenial help sign in
Support launching apps in the greeter, either as locked apps or passing them on to the session via SessionBroadcast
commitd039c648a59a1bfbd0624a33bf1005233d869916 authorMichael Terry <michael.terry@canonical.com> dateTue 28 Jun 2016, 16:38 (10 years ago) parentfc1b69d
33 files changed+900−28
data/unity8-greeter-wrapper+5 debian/control+1 plugins/SessionBroadcast/SessionBroadcast.cpp+35−9 plugins/SessionBroadcast/SessionBroadcast.h+10−3 plugins/Utils/CMakeLists.txt+1 plugins/Utils/URLDispatcher.cpp+75 plugins/Utils/URLDispatcher.h+47 plugins/Utils/plugin.cpp+2 qml/Greeter/Greeter.qml+1−1 qml/Shell.qml+35−10 tests/CMakeLists.txt+6 tests/mocks/CMakeLists.txt+1 tests/mocks/SessionBroadcast/CMakeLists.txt+8 tests/mocks/SessionBroadcast/SessionBroadcast.cpp+34 tests/mocks/SessionBroadcast/SessionBroadcast.h+39 tests/mocks/SessionBroadcast/plugin.cpp+31 tests/mocks/SessionBroadcast/plugin.h+32 tests/mocks/SessionBroadcast/qmldir+2 tests/mocks/Utils/CMakeLists.txt+1 tests/mocks/Utils/URLDispatcher.cpp+38 tests/mocks/Utils/URLDispatcher.h+47 tests/mocks/Utils/plugin.cpp+2 tests/plugins/AccountsService/CMakeLists.txt−5 tests/plugins/CMakeLists.txt+1 tests/plugins/SessionBroadcast/BroadcastServer.cpp+33 tests/plugins/SessionBroadcast/BroadcastServer.h+40 tests/plugins/SessionBroadcast/CMakeLists.txt+45 tests/plugins/SessionBroadcast/interfaces.xml+18 tests/plugins/SessionBroadcast/server.cpp+37 tests/plugins/SessionBroadcast/sessionbroadcasttest.cpp+111 tests/plugins/Utils/CMakeLists.txt+9 tests/plugins/Utils/URLDispatcherTest.cpp+78 tests/qmltests/tst_Shell.qml+75
data/unity8-greeter-wrapper+5
@@ -67,6 +67,11 @@ INIT_PID=$!
6767while [ ! -e "$XDG_RUNTIME_DIR/upstart/sessions/$INIT_PID.session" ]; do sleep 0.1; done
6868export UPSTART_SESSION=$(/sbin/initctl list-sessions | grep "^$INIT_PID " | cut -d' ' -f2)
6969
70+# Connect upstart to system.
71+echo "DBUS_SESSION_BUS_ADDRESS=${DBUS_SESSION_BUS_ADDRESS}" >"$XDG_RUNTIME_DIR/dbus-session"
72+/sbin/initctl notify-dbus-address "$DBUS_SESSION_BUS_ADDRESS"
73+/sbin/initctl start notify-cgmanager
74+
7075# Start any pre-greeter tasks if needed (like wizard)
7176/sbin/initctl emit unity8-greeter-starting
7277
debian/control+1
@@ -129,6 +129,7 @@ Depends: dmz-cursor-theme,
129129 ${misc:Depends},
130130 ${shlibs:Depends},
131131Recommends: indicator-session,
132+ unity-greeter-session-broadcast,
132133 unity-scope-click,
133134 unity-scope-mediascanner2,
134135Breaks: indicator-network (<< 0.5.1+14.10.20141014),
plugins/SessionBroadcast/SessionBroadcast.cpp+35−9
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -12,12 +12,12 @@
1212 *
1313 * You should have received a copy of the GNU General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
1715 */
1816
1917#include "SessionBroadcast.h"
2018#include <QDBusConnection>
19+#include <QDBusConnectionInterface>
20+#include <QDBusInterface>
2121
2222#include <glib.h>
2323
@@ -27,13 +27,39 @@ SessionBroadcast::SessionBroadcast(QObject* parent)
2727 m_username = QString::fromUtf8(g_get_user_name());
2828
2929 auto connection = QDBusConnection::SM_BUSNAME();
30+ auto interface = connection.interface();
31+ interface->startService(QStringLiteral("com.canonical.Unity.Greeter.Broadcast"));
32+
33+ m_broadcaster = new QDBusInterface(QStringLiteral("com.canonical.Unity.Greeter.Broadcast"),
34+ QStringLiteral("/com/canonical/Unity/Greeter/Broadcast"),
35+ QStringLiteral("com.canonical.Unity.Greeter.Broadcast"),
36+ connection, this);
37+
38+ connect(m_broadcaster, SIGNAL(StartUrl(const QString &, const QString &)),
39+ this, SLOT(onStartUrl(const QString &, const QString &)));
40+
41+ connect(m_broadcaster, SIGNAL(ShowHome(const QString &)),
42+ this, SLOT(onShowHome(const QString &)));
43+}
44+
45+void SessionBroadcast::requestUrlStart(const QString &username, const QString &url)
46+{
47+ m_broadcaster->asyncCall(QStringLiteral("RequestUrlStart"), username, url);
48+}
49+
50+void SessionBroadcast::requestHomeShown(const QString &username)
51+{
52+ m_broadcaster->asyncCall(QStringLiteral("RequestHomeShown"), username);
53+}
3054
31− connection.connect(QStringLiteral("com.canonical.Unity.Greeter.Broadcast"),
32− QStringLiteral("/com/canonical/Unity/Greeter/Broadcast"),
33− QStringLiteral("com.canonical.Unity.Greeter.Broadcast"),
34− QStringLiteral("ShowHome"),
35− this,
36− SLOT(onShowHome(const QString &)));
55+void SessionBroadcast::onStartUrl(const QString &username, const QString &url)
56+{
57+ // Since this signal is just used for testing, we don't *really* care if
58+ // username matches, but just in case we do eventually use the signal, we
59+ // should only listen to our own requests.
60+ if (username == m_username) {
61+ Q_EMIT startUrl(url);
62+ }
3763}
3864
3965void SessionBroadcast::onShowHome(const QString &username)
plugins/SessionBroadcast/SessionBroadcast.h+10−3
@@ -1,5 +1,5 @@
11/*
2− * Copyright (C) 2013 Canonical, Ltd.
2+ * Copyright (C) 2013,2016 Canonical, Ltd.
33 *
44 * This program is free software; you can redistribute it and/or modify
55 * it under the terms of the GNU General Public License as published by
@@ -12,8 +12,6 @@
1212 *
1313 * You should have received a copy of the GNU General Public License
1414 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Authors: Michael Terry <michael.terry@canonical.com>
1715 */
1816
1917#ifndef UNITY_SESSIONBROADCAST_H
@@ -31,14 +29,23 @@ class SessionBroadcast: public QObject
3129public:
3230 explicit SessionBroadcast(QObject *parent = 0);
3331
32+ Q_INVOKABLE void requestUrlStart(const QString &username, const QString &url);
33+ Q_INVOKABLE void requestHomeShown(const QString &username);
34+
3435Q_SIGNALS:
36+ // This signal isn't actually used by the shell
37+ // (unity-greeter-session-broadcast handles launching an app for us), but
38+ // it's useful for testing the plugin.
39+ void startUrl(const QString &url);
3540 void showHome();
3641
3742private Q_SLOTS:
43+ void onStartUrl(const QString &username, const QString &url);
3844 void onShowHome(const QString &username);
3945
4046private:
4147 QString m_username;
48+ QDBusInterface *m_broadcaster;
4249};
4350
4451#endif
plugins/Utils/CMakeLists.txt+1
@@ -24,6 +24,7 @@ set(QMLPLUGIN_SRC
2424 inputeventgenerator.cpp
2525 deviceconfigparser.cpp
2626 globalfunctions.cpp
27+ URLDispatcher.cpp
2728 plugin.cpp
2829 )
2930
plugins/Utils/URLDispatcher.cppnew file+75
@@ -0,0 +1,75 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "URLDispatcher.h"
18+
19+#include <QDBusConnection>
20+
21+class URLDispatcherInterface : public QObject
22+{
23+ Q_OBJECT
24+ Q_CLASSINFO("D-Bus Interface", "com.canonical.URLDispatcher")
25+
26+public:
27+ explicit URLDispatcherInterface(URLDispatcher *parent);
28+
29+ Q_SCRIPTABLE void DispatchURL(const QString &url, const QString &package);
30+};
31+
32+URLDispatcherInterface::URLDispatcherInterface(URLDispatcher *parent)
33+ : QObject(parent)
34+{
35+}
36+
37+void URLDispatcherInterface::DispatchURL(const QString &url, const QString &package)
38+{
39+ Q_UNUSED(package);
40+ Q_EMIT static_cast<URLDispatcher *>(parent())->dispatchURL(url);
41+}
42+
43+URLDispatcher::URLDispatcher(QObject *parent)
44+ : QObject(parent)
45+ , m_dispatcher(nullptr)
46+{
47+}
48+
49+bool URLDispatcher::active() const
50+{
51+ return m_dispatcher != nullptr;
52+}
53+
54+void URLDispatcher::setActive(bool value)
55+{
56+ if (value == active())
57+ return;
58+
59+ if (value) {
60+ QDBusConnection connection = QDBusConnection::sessionBus();
61+ URLDispatcherInterface *dispatcher = new URLDispatcherInterface(this);
62+ connection.registerObject(QStringLiteral("/com/canonical/URLDispatcher"),
63+ dispatcher,
64+ QDBusConnection::ExportScriptableContents);
65+ connection.registerService(QStringLiteral("com.canonical.URLDispatcher"));
66+ m_dispatcher = dispatcher;
67+ } else {
68+ delete m_dispatcher;
69+ m_dispatcher = nullptr;
70+ }
71+
72+ Q_EMIT activeChanged();
73+}
74+
75+#include "URLDispatcher.moc"
plugins/Utils/URLDispatcher.hnew file+47
@@ -0,0 +1,47 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef UNITY_URLDISPATCHER_H
18+#define UNITY_URLDISPATCHER_H
19+
20+#include <QObject>
21+#include <QString>
22+
23+// This class manages our url-dispatcher interception. We intercept
24+// url-dispatcher because rather than spawning the handler for the URL
25+// in our own session, we want to notify the user session to do it for us
26+// (and start an unlock in the process).
27+
28+class URLDispatcher : public QObject
29+{
30+ Q_OBJECT
31+ Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
32+
33+public:
34+ explicit URLDispatcher(QObject *parent=0);
35+
36+ bool active() const;
37+ void setActive(bool active);
38+
39+Q_SIGNALS:
40+ void dispatchURL(const QString &url);
41+ void activeChanged();
42+
43+private:
44+ QObject *m_dispatcher;
45+};
46+
47+#endif
plugins/Utils/plugin.cpp+2
@@ -38,6 +38,7 @@
3838#include "inputeventgenerator.h"
3939#include "deviceconfigparser.h"
4040#include "globalfunctions.h"
41+#include "URLDispatcher.h"
4142
4243static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine)
4344{
@@ -80,4 +81,5 @@ void UtilsPlugin::registerTypes(const char *uri)
8081 qmlRegisterType<InputEventGenerator>(uri, 0, 1, "InputEventGenerator");
8182 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
8283 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
84+ qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
8385}
qml/Greeter/Greeter.qml+1−1
@@ -88,7 +88,7 @@ Showable {
8888 }
8989
9090 // Notify that the user has explicitly requested the given app through unity8 GUI.
91− function notifyUserRequestedApp(appId) {
91+ function notifyUserRequestedApp() {
9292 if (!active) {
9393 return;
9494 }
qml/Shell.qml+35−10
@@ -151,6 +151,16 @@ StyledItem {
151151 readonly property alias greeter: greeterLoader.item
152152
153153 function activateApplication(appId) {
154+ // Either open the app in our own session, or -- if we're acting as a
155+ // greeter -- ask the user's session to open it for us.
156+ if (shell.mode === "greeter") {
157+ urlDispatcher.activateURL("application:///" + appId + ".desktop");
158+ } else {
159+ startApp(appId);
160+ }
161+ }
162+
163+ function startApp(appId) {
154164 if (ApplicationManager.findApplication(appId)) {
155165 ApplicationManager.requestFocusApplication(appId);
156166 } else {
@@ -162,7 +172,7 @@ StyledItem {
162172 if (greeter.locked) {
163173 greeter.lockedApp = app;
164174 }
165− shell.activateApplication(app);
175+ startApp(app); // locked apps are always in our same session
166176 }
167177
168178 Binding {
@@ -259,9 +269,7 @@ StyledItem {
259269 ? "phone"
260270 : shell.usageScenario
261271 readonly property string qmlComponent: {
262− if (shell.mode === "greeter") {
263− return "Stages/AbstractStage.qml"
264− } else if (applicationsDisplayLoader.usageScenario === "phone") {
272+ if (applicationsDisplayLoader.usageScenario === "phone") {
265273 return "Stages/PhoneStage.qml";
266274 } else if (applicationsDisplayLoader.usageScenario === "tablet") {
267275 return "Stages/TabletStage.qml";
@@ -491,11 +499,15 @@ StyledItem {
491499 }
492500
493501 function showHome() {
494− greeter.notifyUserRequestedApp("unity8-dash");
502+ greeter.notifyUserRequestedApp();
495503
496− var animate = !LightDMService.greeter.active && !stages.shown
497− dash.setCurrentScope(0, animate, false)
498− ApplicationManager.requestFocusApplication("unity8-dash")
504+ if (shell.mode === "greeter") {
505+ SessionBroadcast.requestHomeShown(AccountsService.user);
506+ } else {
507+ var animate = !LightDMService.greeter.active && !stages.shown;
508+ dash.setCurrentScope(0, animate, false);
509+ ApplicationManager.requestFocusApplication("unity8-dash");
510+ }
499511 }
500512
501513 function showDash() {
@@ -589,7 +601,7 @@ StyledItem {
589601 }
590602 }
591603 onLauncherApplicationSelected: {
592− greeter.notifyUserRequestedApp(appId);
604+ greeter.notifyUserRequestedApp();
593605 shell.activateApplication(appId);
594606 }
595607 onShownChanged: {
@@ -735,7 +747,20 @@ StyledItem {
735747
736748 Connections {
737749 target: SessionBroadcast
738− onShowHome: showHome()
750+ onShowHome: if (shell.mode !== "greeter") showHome()
751+ }
752+
753+ URLDispatcher {
754+ id: urlDispatcher
755+ objectName: "urlDispatcher"
756+ active: shell.mode === "greeter"
757+ onDispatchURL: activateURL(url)
758+
759+ function activateURL(url) {
760+ SessionBroadcast.requestUrlStart(AccountsService.user, url);
761+ greeter.notifyUserRequestedApp();
762+ panel.indicators.hide();
763+ }
739764 }
740765
741766 ItemGrabber {
tests/CMakeLists.txt+6
@@ -126,6 +126,12 @@ function(add_unity8_qmlbenchmark PATH COMPONENT_NAME ITERATIONS)
126126 add_unity8_qmltest(${PATH} ${COMPONENT_NAME} ITERATIONS ${ITERATIONS} ${ARGN})
127127endfunction()
128128
129+# used to make dbus adaptor classes from xml to mock system services
130+if(DEFINED ENV{DEB_BUILD_MULTIARCH})
131+ set(QDBUSXML2CPP_EXECUTABLE "/usr/lib/$ENV{DEB_BUILD_MULTIARCH}/qt5/bin/qdbusxml2cpp")
132+else()
133+ FIND_PROGRAM(QDBUSXML2CPP_EXECUTABLE qdbusxml2cpp)
134+endif()
129135
130136# Actual test definitions
131137add_subdirectory(plugins)
tests/mocks/CMakeLists.txt+1
@@ -38,6 +38,7 @@ add_subdirectory(Lights)
3838add_subdirectory(MeeGo)
3939add_subdirectory(Powerd)
4040add_subdirectory(QMenuModel)
41+add_subdirectory(SessionBroadcast)
4142add_subdirectory(Ubuntu)
4243add_subdirectory(Unity)
4344add_subdirectory(QtMultimedia)
tests/mocks/SessionBroadcast/CMakeLists.txtnew file+8
@@ -0,0 +1,8 @@
1+add_library(MockSessionBroadcast-qml MODULE
2+ plugin.cpp
3+ SessionBroadcast.cpp
4+ )
5+
6+qt5_use_modules(MockSessionBroadcast-qml Qml)
7+
8+add_unity8_mock(SessionBroadcast 0.1 SessionBroadcast TARGETS MockSessionBroadcast-qml)
tests/mocks/SessionBroadcast/SessionBroadcast.cppnew file+34
@@ -0,0 +1,34 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "SessionBroadcast.h"
18+
19+SessionBroadcast::SessionBroadcast(QObject* parent)
20+ : QObject(parent)
21+{
22+}
23+
24+void SessionBroadcast::requestUrlStart(const QString &, const QString &url)
25+{
26+ // No user name guards, we won't worry about that aspect of the plugin here
27+ Q_EMIT startUrl(url);
28+}
29+
30+void SessionBroadcast::requestHomeShown(const QString &)
31+{
32+ // No user name guards, we won't worry about that aspect of the plugin here
33+ Q_EMIT showHome();
34+}
tests/mocks/SessionBroadcast/SessionBroadcast.hnew file+39
@@ -0,0 +1,39 @@
1+/*
2+ * Copyright (C) 2012,2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ *
16+ * Authors: Michael Terry <michael.terry@canonical.com>
17+ */
18+
19+#ifndef UNITY_MOCK_SESSIONBROADCAST_H
20+#define UNITY_MOCK_SESSIONBROADCAST_H
21+
22+#include <QObject>
23+
24+class SessionBroadcast: public QObject
25+{
26+ Q_OBJECT
27+
28+public:
29+ explicit SessionBroadcast(QObject *parent = 0);
30+
31+ Q_INVOKABLE void requestUrlStart(const QString &username, const QString &url);
32+ Q_INVOKABLE void requestHomeShown(const QString &username);
33+
34+Q_SIGNALS:
35+ void startUrl(const QString &url);
36+ void showHome();
37+};
38+
39+#endif
tests/mocks/SessionBroadcast/plugin.cppnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "plugin.h"
18+#include "SessionBroadcast.h"
19+
20+#include <QtQml/qqml.h>
21+
22+static QObject *broadcast_provider(QQmlEngine *, QJSEngine *)
23+{
24+ return new SessionBroadcast();
25+}
26+
27+void PowerdPlugin::registerTypes(const char *uri)
28+{
29+ Q_ASSERT(uri == QLatin1String("SessionBroadcast"));
30+ qmlRegisterSingletonType<SessionBroadcast>(uri, 0, 1, "SessionBroadcast", broadcast_provider);
31+}
tests/mocks/SessionBroadcast/plugin.hnew file+32
@@ -0,0 +1,32 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef MOCK_SESSIONBROADCAST_PLUGIN_H
18+#define MOCK_SESSIONBROADCAST_PLUGIN_H
19+
20+#include <QtQml/QQmlEngine>
21+#include <QtQml/QQmlExtensionPlugin>
22+
23+class PowerdPlugin : public QQmlExtensionPlugin
24+{
25+ Q_OBJECT
26+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
27+
28+public:
29+ void registerTypes(const char *uri) override;
30+};
31+
32+#endif
tests/mocks/SessionBroadcast/qmldirnew file+2
@@ -0,0 +1,2 @@
1+module SessionBroadcast
2+plugin MockSessionBroadcast-qml
tests/mocks/Utils/CMakeLists.txt+1
@@ -28,6 +28,7 @@ set(QMLPLUGIN_SRC
2828 constants.cpp
2929 plugin.cpp
3030 windowstatestorage.cpp
31+ URLDispatcher.cpp
3132 )
3233
3334add_library(FakeUtils-qml SHARED
tests/mocks/Utils/URLDispatcher.cppnew file+38
@@ -0,0 +1,38 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "URLDispatcher.h"
18+
19+#include <QDBusConnection>
20+
21+URLDispatcher::URLDispatcher(QObject *parent)
22+ : QObject(parent)
23+ , m_active(false)
24+{
25+}
26+
27+bool URLDispatcher::active() const
28+{
29+ return m_active;
30+}
31+
32+void URLDispatcher::setActive(bool value)
33+{
34+ if (m_active != value) {
35+ m_active = value;
36+ Q_EMIT activeChanged();
37+ }
38+}
tests/mocks/Utils/URLDispatcher.hnew file+47
@@ -0,0 +1,47 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef UNITY_URLDISPATCHER_H
18+#define UNITY_URLDISPATCHER_H
19+
20+#include <QObject>
21+#include <QString>
22+
23+// This class manages our url-dispatcher interception. We intercept
24+// url-dispatcher because rather than spawning the handler for the URL
25+// in our own session, we want to notify the user session to do it for us
26+// (and start an unlock in the process).
27+
28+class URLDispatcher : public QObject
29+{
30+ Q_OBJECT
31+ Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
32+
33+public:
34+ explicit URLDispatcher(QObject *parent=0);
35+
36+ bool active() const;
37+ void setActive(bool active);
38+
39+Q_SIGNALS:
40+ void dispatchURL(const QString &url);
41+ void activeChanged();
42+
43+private:
44+ bool m_active;
45+};
46+
47+#endif
tests/mocks/Utils/plugin.cpp+2
@@ -25,6 +25,7 @@
2525#include "plugin.h"
2626#include "windowstatestorage.h"
2727#include "constants.h"
28+#include "URLDispatcher.h"
2829
2930// plugin
3031#include <activefocuslogger.h>
@@ -80,4 +81,5 @@ void FakeUtilsPlugin::registerTypes(const char *uri)
8081 qmlRegisterType<InputEventGenerator>(uri, 0, 1, "InputEventGenerator");
8182 qmlRegisterType<DeviceConfigParser>(uri, 0, 1, "DeviceConfigParser");
8283 qmlRegisterSingletonType<GlobalFunctions>(uri, 0, 1, "Functions", createGlobalFunctions);
84+ qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher");
8385}
tests/plugins/AccountsService/CMakeLists.txt−5
@@ -1,8 +1,3 @@
1−if(DEFINED ENV{DEB_BUILD_MULTIARCH})
2− set(QDBUSXML2CPP_EXECUTABLE "/usr/lib/$ENV{DEB_BUILD_MULTIARCH}/qt5/bin/qdbusxml2cpp")
3−else()
4− FIND_PROGRAM(QDBUSXML2CPP_EXECUTABLE qdbusxml2cpp)
5−endif()
61macro(make_dbus_class NAME INTERFACE)
72 if(${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${NAME}Adaptor.h)
83 execute_process(COMMAND ${QDBUSXML2CPP_EXECUTABLE} -c ${NAME}Adaptor -i types.h -a ${CMAKE_CURRENT_BINARY_DIR}/${NAME}Adaptor ${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml ${INTERFACE})
tests/plugins/CMakeLists.txt+1
@@ -4,6 +4,7 @@ add_subdirectory(Dash)
44add_subdirectory(GlobalShortcut)
55add_subdirectory(Greeter)
66add_subdirectory(LightDM)
7+add_subdirectory(SessionBroadcast)
78add_subdirectory(Ubuntu)
89add_subdirectory(Unity)
910add_subdirectory(Utils)
tests/plugins/SessionBroadcast/BroadcastServer.cppnew file+33
@@ -0,0 +1,33 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ */
17+
18+#include "BroadcastServer.h"
19+
20+BroadcastServer::BroadcastServer(QObject *parent)
21+ : QObject(parent)
22+{
23+}
24+
25+void BroadcastServer::RequestUrlStart(const QString &username, const QString &url)
26+{
27+ Q_EMIT StartUrl(username, url);
28+}
29+
30+void BroadcastServer::RequestHomeShown(const QString &username)
31+{
32+ Q_EMIT ShowHome(username);
33+}
tests/plugins/SessionBroadcast/BroadcastServer.hnew file+40
@@ -0,0 +1,40 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ */
17+
18+#ifndef UNITY_BROADCASTSERVER_H
19+#define UNITY_BROADCASTSERVER_H
20+
21+#include <QDBusContext>
22+#include <QObject>
23+
24+class BroadcastServer: public QObject, protected QDBusContext
25+{
26+ Q_OBJECT
27+
28+public:
29+ explicit BroadcastServer(QObject *parent = nullptr);
30+
31+public Q_SLOTS:
32+ void RequestUrlStart(const QString &username, const QString &url);
33+ void RequestHomeShown(const QString &username);
34+
35+Q_SIGNALS:
36+ void StartUrl(const QString &username, const QString &url);
37+ void ShowHome(const QString &username);
38+};
39+
40+#endif
tests/plugins/SessionBroadcast/CMakeLists.txtnew file+45
@@ -0,0 +1,45 @@
1+macro(make_dbus_class NAME INTERFACE)
2+ if(${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml IS_NEWER_THAN ${CMAKE_CURRENT_BINARY_DIR}/${NAME}Adaptor.h)
3+ execute_process(COMMAND ${QDBUSXML2CPP_EXECUTABLE} -c ${NAME}Adaptor -a ${CMAKE_CURRENT_BINARY_DIR}/${NAME}Adaptor ${CMAKE_CURRENT_SOURCE_DIR}/interfaces.xml ${INTERFACE})
4+ endif()
5+endmacro(make_dbus_class)
6+
7+make_dbus_class(Broadcast com.canonical.Unity.Greeter.Broadcast)
8+
9+add_definitions(-DSM_BUSNAME=sessionBus)
10+
11+include_directories(
12+ ${GLIB_INCLUDE_DIRS}
13+ ${CMAKE_CURRENT_BINARY_DIR}
14+ ${CMAKE_CURRENT_SOURCE_DIR}
15+ ${CMAKE_SOURCE_DIR}/plugins/SessionBroadcast
16+)
17+
18+link_libraries(
19+ ${GLIB_LIBRARIES}
20+)
21+
22+add_executable(MockBroadcastServer
23+ ${CMAKE_CURRENT_BINARY_DIR}/BroadcastAdaptor.cpp
24+ server.cpp
25+ BroadcastServer.cpp
26+)
27+qt5_use_modules(MockBroadcastServer Core DBus)
28+
29+### SessionBroadcastTest
30+add_executable(sessionbroadcasttestExec
31+ ${CMAKE_SOURCE_DIR}/plugins/SessionBroadcast/SessionBroadcast.cpp
32+ sessionbroadcasttest.cpp
33+)
34+qt5_use_modules(sessionbroadcasttestExec Test Core Qml DBus)
35+
36+add_unity8_unittest(SessionBroadcast dbus-test-runner
37+ ARG_PREFIX "--parameter"
38+ ARGS
39+ --task $<TARGET_FILE:MockBroadcastServer>
40+ --task-name server
41+ --ignore-return
42+ --task $<TARGET_FILE:sessionbroadcasttestExec>
43+ --task-name client
44+ --wait-for com.canonical.Unity.Greeter.Broadcast
45+)
tests/plugins/SessionBroadcast/interfaces.xmlnew file+18
@@ -0,0 +1,18 @@
1+<node>
2+ <interface name="com.canonical.Unity.Greeter.Broadcast">
3+ <method name="RequestUrlStart">
4+ <arg name="username" type="s" direction="in" />
5+ <arg name="url" type="s" direction="in" />
6+ </method>
7+ <method name="RequestHomeShown">
8+ <arg name="username" type="s" direction="in" />
9+ </method>
10+ <signal name="StartUrl">
11+ <arg name="username" type="s" direction="out" />
12+ <arg name="url" type="s" direction="out" />
13+ </signal>
14+ <signal name="ShowHome">
15+ <arg name="username" type="s" direction="out" />
16+ </signal>
17+ </interface>
18+</node>
tests/plugins/SessionBroadcast/server.cppnew file+37
@@ -0,0 +1,37 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
3+ *
4+ * This program is free software: you can redistribute it and/or modify it
5+ * under the terms of the GNU General Public License version 3, as published
6+ * by the Free Software Foundation.
7+ *
8+ * This program is distributed in the hope that it will be useful, but
9+ * WITHOUT ANY WARRANTY; without even the implied warranties of
10+ * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
11+ * PURPOSE. See the GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * version 3 along with this program. If not, see
15+ * <http://www.gnu.org/licenses/>
16+ */
17+
18+#include "BroadcastAdaptor.h"
19+#include "BroadcastServer.h"
20+#include <QCoreApplication>
21+
22+int main(int argc, char *argv[])
23+{
24+ QCoreApplication a(argc, argv);
25+
26+ auto broadcast = new BroadcastServer(&a);
27+ new BroadcastAdaptor(broadcast);
28+
29+ // We use the session bus for testing. The real plugin uses the system bus
30+ auto connection = QDBusConnection::sessionBus();
31+ if (!connection.registerObject("/com/canonical/Unity/Greeter/Broadcast", broadcast))
32+ return 1;
33+ if (!connection.registerService("com.canonical.Unity.Greeter.Broadcast"))
34+ return 1;
35+
36+ return a.exec();
37+}
tests/plugins/SessionBroadcast/sessionbroadcasttest.cppnew file+111
@@ -0,0 +1,111 @@
1+/*
2+ * Copyright 2016 Canonical Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU Lesser General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU Lesser General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU Lesser General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include <QtTest>
18+#include <QDBusInterface>
19+#include <QSignalSpy>
20+
21+#include <glib.h>
22+
23+#include "SessionBroadcast.h"
24+
25+class SessionBroadcastTest : public QObject
26+{
27+ Q_OBJECT
28+
29+private Q_SLOTS:
30+
31+ void initTestCase() {
32+ broadcastServer = new QDBusInterface("com.canonical.Unity.Greeter.Broadcast",
33+ "/com/canonical/Unity/Greeter/Broadcast",
34+ "com.canonical.Unity.Greeter.Broadcast",
35+ QDBusConnection::sessionBus(), this);
36+ serverStartUrlSpy = new QSignalSpy(broadcastServer,
37+ SIGNAL(StartUrl(const QString &, const QString &)));
38+ serverShowHomeSpy = new QSignalSpy(broadcastServer,
39+ SIGNAL(ShowHome(const QString &)));
40+ user = QString(g_get_user_name());
41+ otherUser = user + "2";
42+ }
43+
44+ void init()
45+ {
46+ serverStartUrlSpy->clear();
47+ serverShowHomeSpy->clear();
48+
49+ broadcastPlugin = new SessionBroadcast(this);
50+ pluginStartUrlSpy = new QSignalSpy(broadcastPlugin,
51+ SIGNAL(startUrl(const QString &)));
52+ pluginShowHomeSpy = new QSignalSpy(broadcastPlugin,
53+ SIGNAL(showHome()));
54+ }
55+
56+ void cleanup()
57+ {
58+ delete broadcastPlugin;
59+ }
60+
61+ void testReceiveUrlStart() {
62+ broadcastServer->call("RequestUrlStart", user, "test:");
63+ QTRY_COMPARE(serverStartUrlSpy->count(), 1);
64+ QCOMPARE(pluginStartUrlSpy->count(), 1);
65+ QCOMPARE((*pluginStartUrlSpy)[0][0], QVariant("test:"));
66+ }
67+
68+ void testReceiveUrlStartOtherUser() {
69+ broadcastServer->call("RequestUrlStart", otherUser, "test:");
70+ QTRY_COMPARE(serverStartUrlSpy->count(), 1);
71+ QCOMPARE(pluginStartUrlSpy->count(), 0);
72+ }
73+
74+ void testReceiveShowHome() {
75+ broadcastServer->call("RequestHomeShown", user);
76+ QTRY_COMPARE(serverShowHomeSpy->count(), 1);
77+ QCOMPARE(pluginShowHomeSpy->count(), 1);
78+ }
79+
80+ void testReceiveShowHomeOtherUser() {
81+ broadcastServer->call("RequestHomeShown", otherUser);
82+ QTRY_COMPARE(serverShowHomeSpy->count(), 1);
83+ QCOMPARE(pluginShowHomeSpy->count(), 0);
84+ }
85+
86+ void testSendUrlStart() {
87+ broadcastPlugin->requestUrlStart(otherUser, "test:");
88+ QTRY_COMPARE(serverStartUrlSpy->count(), 1);
89+ QCOMPARE((*serverStartUrlSpy)[0][0], QVariant(otherUser));
90+ QCOMPARE((*serverStartUrlSpy)[0][1], QVariant("test:"));
91+ }
92+
93+ void testSendShowHome() {
94+ broadcastPlugin->requestHomeShown(otherUser);
95+ QTRY_COMPARE(serverShowHomeSpy->count(), 1);
96+ QCOMPARE((*serverShowHomeSpy)[0][0], QVariant(otherUser));
97+ }
98+
99+private:
100+ QDBusInterface *broadcastServer;
101+ SessionBroadcast *broadcastPlugin;
102+ QSignalSpy *serverStartUrlSpy;
103+ QSignalSpy *serverShowHomeSpy;
104+ QSignalSpy *pluginStartUrlSpy;
105+ QSignalSpy *pluginShowHomeSpy;
106+ QString user;
107+ QString otherUser;
108+};
109+
110+QTEST_GUILESS_MAIN(SessionBroadcastTest)
111+#include "sessionbroadcasttest.moc"
tests/plugins/Utils/CMakeLists.txt+9
@@ -23,3 +23,12 @@ endforeach()
2323
2424# plain qml test
2525add_unity8_qmlunittest(. UtilsStyle)
26+
27+# URLDispatcher test needs dbus-test-runner because it claims a dbus name
28+add_executable(URLDispatcherTestExec URLDispatcherTest.cpp)
29+qt5_use_modules(URLDispatcherTestExec Test Core Qml)
30+target_link_libraries(URLDispatcherTestExec Utils-qml)
31+add_unity8_unittest(URLDispatcher dbus-test-runner
32+ ARG_PREFIX "--parameter"
33+ ARGS --task $<TARGET_FILE:URLDispatcherTestExec>
34+)
tests/plugins/Utils/URLDispatcherTest.cppnew file+78
@@ -0,0 +1,78 @@
1+/*
2+ * Copyright (C) 2016 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
8+ * This program is distributed in the hope that it will be useful,
9+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+ * GNU General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#include "URLDispatcher.h"
18+
19+#include <QDBusInterface>
20+#include <QSignalSpy>
21+#include <QTest>
22+
23+class URLDispatcherTest : public QObject
24+{
25+ Q_OBJECT
26+
27+private Q_SLOTS:
28+
29+ void initTestCase() {
30+ dispatchServer = new QDBusInterface("com.canonical.URLDispatcher",
31+ "/com/canonical/URLDispatcher",
32+ "com.canonical.URLDispatcher",
33+ QDBusConnection::sessionBus(), this);
34+ }
35+
36+ void init()
37+ {
38+ dispatcher = new URLDispatcher(this);
39+ dispatchSpy = new QSignalSpy(dispatcher,
40+ SIGNAL(dispatchURL(const QString &)));
41+ }
42+
43+ void cleanup()
44+ {
45+ delete dispatcher;
46+ }
47+
48+ void testInactiveByDefault() {
49+ QVERIFY(!dispatcher->active());
50+ }
51+
52+ void testActiveRequest() {
53+ dispatcher->setActive(true);
54+ dispatchServer->call("DispatchURL", "test:", "package");
55+ QCOMPARE(dispatchSpy->count(), 1);
56+ QCOMPARE((*dispatchSpy)[0][0], QVariant("test:"));
57+ }
58+
59+ void testInactiveRequest() {
60+ dispatchServer->call("DispatchURL", "test:", "package");
61+ QCOMPARE(dispatchSpy->count(), 0);
62+ }
63+
64+ void testInactiveAfterActiveRequest() {
65+ dispatcher->setActive(true);
66+ dispatcher->setActive(false);
67+ dispatchServer->call("DispatchURL", "test:", "package");
68+ QCOMPARE(dispatchSpy->count(), 0);
69+ }
70+
71+private:
72+ QDBusInterface *dispatchServer;
73+ URLDispatcher *dispatcher;
74+ QSignalSpy *dispatchSpy;
75+};
76+
77+QTEST_GUILESS_MAIN(URLDispatcherTest)
78+#include "URLDispatcherTest.moc"
tests/qmltests/tst_Shell.qml+75
@@ -19,6 +19,7 @@ import QtTest 1.0
1919import AccountsService 0.1
2020import GSettings 1.0
2121import LightDM.IntegratedLightDM 0.1 as LightDM
22+import SessionBroadcast 0.1
2223import Ubuntu.Components 1.3
2324import Ubuntu.Components.ListItems 1.3 as ListItem
2425import Ubuntu.Telephony 0.1 as Telephony
@@ -378,6 +379,18 @@ Rectangle {
378379 signalName: "setCurrentScopeCalled"
379380 }
380381
382+ SignalSpy {
383+ id: broadcastUrlSpy
384+ target: SessionBroadcast
385+ signalName: "startUrl"
386+ }
387+
388+ SignalSpy {
389+ id: broadcastHomeSpy
390+ target: SessionBroadcast
391+ signalName: "showHome"
392+ }
393+
381394 SignalSpy {
382395 id: unlockAllModemsSpy
383396 target: Connectivity
@@ -519,6 +532,8 @@ Rectangle {
519532 LightDM.Greeter.authenticate(""); // reset greeter
520533
521534 sessionSpy.clear();
535+ broadcastUrlSpy.clear();
536+ broadcastHomeSpy.clear();
522537
523538 GSettingsController.setLifecycleExemptAppids([]);
524539
@@ -2468,5 +2483,65 @@ Rectangle {
24682483 verify(greeter.shown);
24692484 verify(greeter.locked);
24702485 }
2486+
2487+ function test_greeterModeBroadcastsApp() {
2488+ setLightDMMockMode("single-pin");
2489+ shellLoader.mode = "greeter";
2490+ loadShell("phone");
2491+ shell.usageScenario = "phone";
2492+ waitForRendering(shell);
2493+
2494+ dragLauncherIntoView();
2495+ var appIcon = findChild(shell, "launcherDelegate0")
2496+ tap(appIcon);
2497+
2498+ tryCompare(broadcastUrlSpy, "count", 1);
2499+ compare(broadcastUrlSpy.signalArguments[0][0], "application:///" + appIcon.appId + ".desktop");
2500+ compare(ApplicationManager.count, 1); // confirm only dash is open, we didn't start new app
2501+
2502+ var coverPage = findChild(shell, "coverPage");
2503+ tryCompare(coverPage, "showProgress", 0);
2504+ }
2505+
2506+ function test_greeterModeBroadcastsHome() {
2507+ setLightDMMockMode("single-pin");
2508+ shellLoader.mode = "greeter";
2509+ loadShell("phone");
2510+ shell.usageScenario = "phone";
2511+ waitForRendering(shell);
2512+
2513+ var gallerySurfaceId = topLevelSurfaceList.nextId;
2514+ var galleryApp = ApplicationManager.startApplication("gallery-app");
2515+ waitUntilAppWindowIsFullyLoaded(gallerySurfaceId);
2516+ compare(ApplicationManager.focusedApplicationId, "gallery-app");
2517+
2518+ dragLauncherIntoView();
2519+ tap(findChild(shell, "buttonShowDashHome"));
2520+
2521+ tryCompare(broadcastHomeSpy, "count", 1);
2522+ compare(ApplicationManager.focusedApplicationId, "gallery-app"); // confirm we didn't focus dash
2523+
2524+ var coverPage = findChild(shell, "coverPage");
2525+ tryCompare(coverPage, "showProgress", 0);
2526+ }
2527+
2528+ function test_greeterModeDispatchesURL() {
2529+ setLightDMMockMode("single-pin");
2530+ shellLoader.mode = "greeter";
2531+ loadShell("phone");
2532+ shell.usageScenario = "phone";
2533+ waitForRendering(shell);
2534+
2535+ var urlDispatcher = findInvisibleChild(shell, "urlDispatcher");
2536+ verify(urlDispatcher.active);
2537+ urlDispatcher.dispatchURL("test:"); // force signal emission
2538+
2539+ tryCompare(broadcastUrlSpy, "count", 1);
2540+ compare(broadcastUrlSpy.signalArguments[0][0], "test:");
2541+ compare(ApplicationManager.count, 1); // confirm only dash is open, we didn't start new app
2542+
2543+ var coverPage = findChild(shell, "coverPage");
2544+ tryCompare(coverPage, "showProgress", 0);
2545+ }
24712546 }
24722547}