on xenial help sign in
Merge from trunk
commitb1fc0acf9040eb5eccc1ec89f647e8193577ef04 authorMichael Terry <michael.terry@canonical.com> dateWed 18 Sep 2013, 16:59 (13 years ago) parenta4777e486d8f81
36 files changed+348−307
Components/VolumeControl.qml+5−20 Dash/GenericScopeView.qml+4−1 Dash/graphics/phone/home.png Dash/graphics/phone/music.png Dash/graphics/phone/videos.png Dash/graphics/tablet/apps.png Dash/graphics/tablet/home.png Dash/graphics/tablet/music.png Dash/graphics/tablet/people.png Dash/graphics/tablet/videos.png GreeterShell.qml+3−3 MouseTouchAdaptor.cpp+29−12 MouseTouchAdaptor.h+2−5 Panel/Indicators/DefaultIndicatorWidget.qml+1−5 Shell.qml+19−9 debian/changelog+14 main.cpp−1 plugins/SessionBroadcast/SessionBroadcast.cpp+21−19 plugins/SessionBroadcast/SessionBroadcast.h+3−1 plugins/Unity/Launcher/launchermodel.cpp+12−3 tests/autopilot/unity8/application_lifecycle/__init__.py+20 tests/autopilot/unity8/application_lifecycle/tests/__init__.py tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.py+136 tests/autopilot/unity8/shell/__init__.py+10 tests/autopilot/unity8/shell/tests/__init__.py+4−1 tests/plugins/CMakeLists.txt−1 tests/plugins/SessionBroadcast/BroadcastServer.cpp−30 tests/plugins/SessionBroadcast/BroadcastServer.h−41 tests/plugins/SessionBroadcast/CMakeLists.txt−37 tests/plugins/SessionBroadcast/client.cpp−62 tests/plugins/SessionBroadcast/interfaces.xml−12 tests/plugins/SessionBroadcast/server.cpp−39 tests/plugins/Unity/Launcher/launchermodeltest.cpp+61−2 tests/qmltests/Dash/tst_DashContent.qml+1−1 tests/qmltests/Dash/tst_GenericScopeView.qml+3 tests/uqmlscene/main.cpp−2
Components/VolumeControl.qml+5−20
@@ -21,37 +21,22 @@ Item {
2121 id: root
2222 objectName: "volumeControl"
2323 visible: false
24− property int volume
2524
2625 QDBusActionGroup {
2726 id: actionGroup
2827 busType: 1
29− busName: "com.canonical.settings.sound"
30− objectPath: "/com/canonical/settings/sound"
28+ busName: "com.canonical.indicator.sound"
29+ objectPath: "/com/canonical/indicator/sound"
3130
32− property variant actionObject: action("volume")
33− property variant serverVolume: actionObject.valid ? actionObject.state: 0
34− }
35−
36− Binding {
37− target: root
38− property: "volume"
39− value: actionGroup.serverVolume * 100
40− }
41−
42− onVolumeChanged: {
43− if (actionGroup.serverVolume != volume) {
44− var targetVolume = Math.min(1, Math.max(0, volume / 100));
45− actionGroup.actionObject.updateState(targetVolume);
46− }
31+ property variant actionObject: action("scroll")
4732 }
4833
4934 function volumeUp() {
50− actionGroup.actionObject.updateState(Math.min(1, volume/100 + 0.10));
35+ actionGroup.actionObject.activate(1);
5136 }
5237
5338 function volumeDown() {
54− actionGroup.actionObject.updateState(Math.max(0, volume/100 - 0.10));
39+ actionGroup.actionObject.activate(-1);
5540 }
5641
5742 Component.onCompleted: actionGroup.start()
Dash/GenericScopeView.qml+4−1
@@ -21,10 +21,11 @@ import "../Components/ListItems" as ListItems
2121
2222ScopeView {
2323 id: scopeView
24− property alias previewShown: previewLoader.onScreen
24+ readonly property alias previewShown: previewLoader.onScreen
2525
2626 onIsCurrentChanged: {
2727 pageHeader.resetSearch();
28+ previewLoader.open = false;
2829 }
2930
3031 onMovementStarted: categoryView.showHeader()
@@ -179,6 +180,7 @@ ScopeView {
179180 case "grid": {
180181 switch (contentType) {
181182 case "video": return "Generic/GenericFilterGridPotrait.qml";
183+ case "music": return "Music/MusicFilterGrid.qml";
182184 default: return "Generic/GenericFilterGrid.qml";
183185 }
184186 }
@@ -246,6 +248,7 @@ ScopeView {
246248 }
247249
248250 Loader {
251+ objectName: "previewLoader"
249252 id: previewLoader
250253 property var previewData
251254 height: effect.bottomGapPx - effect.topGapPx
Dash/graphics/phone/home.pngdeleted

Binary file not shown.

Dash/graphics/phone/music.pngdeleted

Binary file not shown.

Dash/graphics/phone/videos.pngdeleted

Binary file not shown.

Dash/graphics/tablet/apps.pngdeleted

Binary file not shown.

Dash/graphics/tablet/home.pngdeleted

Binary file not shown.

Dash/graphics/tablet/music.pngdeleted

Binary file not shown.

Dash/graphics/tablet/people.pngdeleted

Binary file not shown.

Dash/graphics/tablet/videos.pngdeleted

Binary file not shown.

GreeterShell.qml+3−3
@@ -31,8 +31,8 @@ import "Panel"
3131BasicShell {
3232 id: shell
3333
34− function activateApplication(desktopFile, argument) {
35− SessionBroadcast.requestApplicationStart(LightDM.Greeter.authenticationUser, desktopFile)
34+ function activateApplication(appId) {
35+ SessionBroadcast.requestApplicationStart(LightDM.Greeter.authenticationUser, appId)
3636 greeter.hide()
3737 }
3838
@@ -177,7 +177,7 @@ BasicShell {
177177 dragAreaWidth: shell.edgeSize
178178 available: greeter.narrowMode && !edgeDemo.active
179179 onLauncherApplicationSelected: {
180− shell.activateApplication(name)
180+ shell.activateApplication(appId)
181181 }
182182 onShownChanged: {
183183 if (shown) {
MouseTouchAdaptor.cpp+29−12
@@ -39,24 +39,16 @@ Qt::MouseButton translateMouseButton(xcb_button_t detail)
3939} // end of anonymous namespace
4040
4141MouseTouchAdaptor::MouseTouchAdaptor()
42− : m_targetWindow(0)
43− , m_leftButtonIsPressed(false)
42+ : m_leftButtonIsPressed(false)
4443{
4544 m_touchDevice = new QTouchDevice;
4645 m_touchDevice->setType(QTouchDevice::TouchScreen);
4746 QWindowSystemInterface::registerTouchDevice(m_touchDevice);
4847}
4948
50−void MouseTouchAdaptor::setTargetWindow(QWindow *window)
51−{
52− m_targetWindow = window;
53−}
54−
5549bool MouseTouchAdaptor::nativeEventFilter(const QByteArray & eventType,
5650 void * message, long * /*result*/)
5751{
58− Q_ASSERT(m_targetWindow);
59−
6052 if (eventType != "xcb_generic_event_t") {
6153 // wrong backend.
6254 qWarning("MouseTouchAdaptor: XCB backend not in use. Adaptor inoperative!");
@@ -91,7 +83,9 @@ bool MouseTouchAdaptor::handleButtonPress(xcb_button_press_event_t *pressEvent)
9183
9284 QPoint windowPos(pressEvent->event_x, pressEvent->event_y);
9385
94− QTouchEventSequence touchEvent = QTest::touchEvent(m_targetWindow, m_touchDevice,
86+ QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(pressEvent->event));
87+
88+ QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
9589 false /* autoCommit */);
9690 touchEvent.press(0 /* touchId */, windowPos);
9791 touchEvent.commit(false /* processEvents */);
@@ -110,7 +104,9 @@ bool MouseTouchAdaptor::handleButtonRelease(xcb_button_release_event_t *releaseE
110104
111105 QPoint windowPos(releaseEvent->event_x, releaseEvent->event_y);
112106
113− QTouchEventSequence touchEvent = QTest::touchEvent(m_targetWindow, m_touchDevice,
107+ QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(releaseEvent->event));
108+
109+ QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
114110 false /* autoCommit */);
115111 touchEvent.release(0 /* touchId */, windowPos);
116112 touchEvent.commit(false /* processEvents */);
@@ -127,10 +123,31 @@ bool MouseTouchAdaptor::handleMotionNotify(xcb_motion_notify_event_t *event)
127123
128124 QPoint windowPos(event->event_x, event->event_y);
129125
130− QTouchEventSequence touchEvent = QTest::touchEvent(m_targetWindow, m_touchDevice,
126+ QWindow *targetWindow = findQWindowWithXWindowID(static_cast<WId>(event->event));
127+
128+ QTouchEventSequence touchEvent = QTest::touchEvent(targetWindow, m_touchDevice,
131129 false /* autoCommit */);
132130 touchEvent.move(0 /* touchId */, windowPos);
133131 touchEvent.commit(false /* processEvents */);
134132
135133 return true;
136134}
135+
136+QWindow *MouseTouchAdaptor::findQWindowWithXWindowID(WId windowId)
137+{
138+ QWindowList windowList = QGuiApplication::topLevelWindows();
139+ QWindow *foundWindow = nullptr;
140+
141+ int i = 0;
142+ while (!foundWindow && i < windowList.count()) {
143+ QWindow *window = windowList[i];
144+ if (window->winId() == windowId) {
145+ foundWindow = window;
146+ } else {
147+ ++i;
148+ }
149+ }
150+
151+ Q_ASSERT(foundWindow);
152+ return foundWindow;
153+}
MouseTouchAdaptor.h+2−5
@@ -20,11 +20,11 @@
2020#define MOUSE_TOUCH_ADAPTOR_H
2121
2222#include <QtCore/QAbstractNativeEventFilter>
23+#include <QWindow>
2324#include <xcb/xcb.h>
2425
2526class QMouseEvent;
2627class QTouchDevice;
27−class QWindow;
2828
2929// Transforms QMouseEvents into single-finger QTouchEvents.
3030class MouseTouchAdaptor : public QAbstractNativeEventFilter {
@@ -32,9 +32,6 @@ class MouseTouchAdaptor : public QAbstractNativeEventFilter {
3232public:
3333 MouseTouchAdaptor();
3434
35− // The window that will receive the generated QTouchEvents
36− void setTargetWindow(QWindow *window);
37−
3835 // Filters mouse events and posts the equivalent QTouchEvents.
3936 virtual bool nativeEventFilter(const QByteArray & eventType, void *message, long *result);
4037
@@ -43,8 +40,8 @@ private:
4340 bool handleButtonPress(xcb_button_press_event_t *pressEvent);
4441 bool handleButtonRelease(xcb_button_release_event_t *releaseEvent);
4542 bool handleMotionNotify(xcb_motion_notify_event_t *event);
43+ QWindow *findQWindowWithXWindowID(WId windowId);
4644
47− QWindow *m_targetWindow;
4845 QTouchDevice *m_touchDevice;
4946 bool m_leftButtonIsPressed;
5047};
Panel/Indicators/DefaultIndicatorWidget.qml+1−5
@@ -24,6 +24,7 @@ import Unity.IndicatorsLegacy 0.1 as Indicators
2424Indicators.IndicatorWidget {
2525 id: indicatorWidget
2626
27+ enabled:true
2728 width: itemRow.width + units.gu(0.5)
2829
2930 property alias label: itemLabel.text
@@ -63,19 +64,14 @@ Indicators.IndicatorWidget {
6364
6465 onActionStateChanged: {
6566 if (action == undefined || !action.valid) {
66− enabled = false;
6767 return;
6868 }
6969
7070 if (action.state == undefined) {
71− label = "";
72− iconSource = "";
73− enabled = false;
7471 return;
7572 }
7673
7774 label = action.state[Indicators.ActionState.Label];
7875 iconSource = "image://theme/" + action.state[Indicators.ActionState.IconSource];
79− enabled = action.state[Indicators.ActionState.Visible];
8076 }
8177}
Shell.qml+19−9
@@ -22,6 +22,7 @@ import Ubuntu.Components 0.1
2222import Ubuntu.Gestures 0.1
2323import Unity.Launcher 0.1
2424import Powerd 0.1
25+import SessionBroadcast 0.1
2526import SessionManager 0.1
2627import "Dash"
2728import "Launcher"
@@ -74,6 +75,8 @@ BasicShell {
7475
7576 readonly property bool applicationFocused: !!applicationManager.mainStageFocusedApplication
7677 || !!applicationManager.sideStageFocusedApplication
78+ // Used for autopilot testing.
79+ readonly property string currentFocusedAppId: ApplicationManager.focusedApplicationId
7780
7881 readonly property bool fullscreenMode: {
7982 if (mainStage.usingScreenshots) { // Window Manager animating so want to re-evaluate fullscreen mode
@@ -397,6 +400,16 @@ BasicShell {
397400 }
398401 }
399402
403+ function showHome() {
404+ // Animate if moving between application and dash
405+ if (!stages.shown) {
406+ dash.setCurrentScope("home.scope", true, false)
407+ } else {
408+ dash.setCurrentScope("home.scope", false, false)
409+ }
410+ stages.hide()
411+ }
412+
400413 Item {
401414 id: overlay
402415
@@ -483,15 +496,7 @@ BasicShell {
483496 width: parent.width
484497 dragAreaWidth: shell.edgeSize
485498 available: edgeDemo.launcherEnabled
486− onDashItemSelected: {
487− // Animate if moving between application and dash
488− if (!stages.shown) {
489− dash.setCurrentScope("home.scope", true, false)
490− } else {
491− dash.setCurrentScope("home.scope", false, false)
492− }
493− stages.hide();
494− }
499+ onDashItemSelected: showHome()
495500 onDash: {
496501 if (stages.shown) {
497502 dash.setCurrentScope("applications.scope", true, false)
@@ -587,4 +592,9 @@ BasicShell {
587592 indicators: panel.indicators
588593 underlay: underlay
589594 }
595+
596+ Connections {
597+ target: SessionBroadcast
598+ onShowHome: showHome()
599+ }
590600}
debian/changelog+14
@@ -1,3 +1,17 @@
1+unity8 (7.81.3+13.10.20130916-0ubuntu1) saucy; urgency=low
2+
3+ [ Michael Zanetti ]
4+ * allow left edge gesture to minimize apps even when launcher is
5+ already visible.
6+ * Don't hide the launcher on changes in the stages.
7+ * ssh is now installed per default, but it's set to manual in the
8+ ssh.override.
9+
10+ [ Ubuntu daily release ]
11+ * Automatic snapshot from revision 320
12+
13+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 16 Sep 2013 11:49:28 +0000
14+
115unity8 (7.81.3+13.10.20130912-0ubuntu1) saucy; urgency=low
216
317 [ Michael Zanetti ]
main.cpp−1
@@ -145,7 +145,6 @@ int startShell(int argc, const char** argv, void* server)
145145 MouseTouchAdaptor *mouseTouchAdaptor = 0;
146146 if (args.contains(QLatin1String("-mousetouch"))) {
147147 mouseTouchAdaptor = new MouseTouchAdaptor;
148− mouseTouchAdaptor->setTargetWindow(view);
149148 application->installNativeEventFilter(mouseTouchAdaptor);
150149 }
151150
plugins/SessionBroadcast/SessionBroadcast.cpp+21−19
@@ -20,19 +20,18 @@
2020#include <QDBusConnection>
2121#include <QDBusConnectionInterface>
2222#include <QDBusInterface>
23−#include <QFileInfo>
2423
2524SessionBroadcast::SessionBroadcast(QObject* parent)
2625 : QObject(parent),
27− broadcaster(NULL)
26+ m_broadcaster(NULL)
2827{
2928 auto connection = QDBusConnection::SM_BUSNAME();
3029 auto interface = connection.interface();
3130 interface->startService("com.canonical.Unity.Greeter.Broadcast");
32− broadcaster = new QDBusInterface("com.canonical.Unity.Greeter.Broadcast",
33− "/com/canonical/Unity/Greeter/Broadcast",
34− "com.canonical.Unity.Greeter.Broadcast",
35− connection, this);
31+ m_broadcaster = new QDBusInterface("com.canonical.Unity.Greeter.Broadcast",
32+ "/com/canonical/Unity/Greeter/Broadcast",
33+ "com.canonical.Unity.Greeter.Broadcast",
34+ connection, this);
3635
3736 connection.connect("com.canonical.Unity.Greeter.Broadcast",
3837 "/com/canonical/Unity/Greeter/Broadcast",
@@ -40,23 +39,18 @@ SessionBroadcast::SessionBroadcast(QObject* parent)
4039 "StartApplication",
4140 this,
4241 SLOT(onStartApplication(const QString &, const QString &)));
42+
43+ connection.connect("com.canonical.Unity.Greeter.Broadcast",
44+ "/com/canonical/Unity/Greeter/Broadcast",
45+ "com.canonical.Unity.Greeter.Broadcast",
46+ "ShowHome",
47+ this,
48+ SLOT(onShowHome(const QString &)));
4349}
4450
4551void SessionBroadcast::requestApplicationStart(const QString &username, const QString &appId)
4652{
47− // unity-greeter-session-broadcast deals in short appIds. That is,
48− // desktop file names without the ".desktop". It doesn't really allow
49− // for arbitrary-path desktop files, for security reasons (hard to
50− // contain with apparmor and such). So we need to mangle the names a bit.
51− // If incoming appId is random path like /hello/foobar.desktop, then just
52− // do our best and ask for foobar.
53− QString desktopSuffix = ".desktop";
54− QString mangled = appId;
55− if (mangled.endsWith(desktopSuffix))
56− mangled.chop(desktopSuffix.size());
57− mangled = QFileInfo(mangled).fileName(); // chop off path
58−
59− broadcaster->asyncCall("RequestApplicationStart", username, mangled);
53+ m_broadcaster->asyncCall("RequestApplicationStart", username, appId);
6054}
6155
6256void SessionBroadcast::onStartApplication(const QString &username, const QString &appId)
@@ -68,3 +62,11 @@ void SessionBroadcast::onStartApplication(const QString &username, const QString
6862 Q_EMIT startApplication(appId);
6963 }
7064}
65+
66+void SessionBroadcast::onShowHome(const QString &username)
67+{
68+ // Only listen to requests meant for us
69+ if (username == qgetenv("USER")) {
70+ Q_EMIT showHome();
71+ }
72+}
plugins/SessionBroadcast/SessionBroadcast.h+3−1
@@ -38,12 +38,14 @@ Q_SIGNALS:
3838 // (unity-greeter-session-broadcast handles launching an app for us), but
3939 // it's useful for testing the plugin.
4040 void startApplication(const QString &appId);
41+ void showHome();
4142
4243private Q_SLOTS:
4344 void onStartApplication(const QString &username, const QString &appId);
45+ void onShowHome(const QString &username);
4446
4547private:
46− QDBusInterface *broadcaster;
48+ QDBusInterface *m_broadcaster;
4749};
4850
4951#endif
plugins/Unity/Launcher/launchermodel.cpp+12−3
@@ -115,9 +115,11 @@ void LauncherModel::move(int oldIndex, int newIndex)
115115 m_list.move(oldIndex, newIndex);
116116 endMoveRows();
117117
118− storeAppList();
119−
120− pin(m_list.at(newIndex)->appId());
118+ if (!m_list.at(newIndex)->pinned()) {
119+ pin(m_list.at(newIndex)->appId());
120+ } else {
121+ storeAppList();
122+ }
121123}
122124
123125void LauncherModel::pin(const QString &appId, int index)
@@ -146,6 +148,8 @@ void LauncherModel::pin(const QString &appId, int index)
146148 m_list.insert(index, item);
147149 endInsertRows();
148150 }
151+
152+ storeAppList();
149153}
150154
151155void LauncherModel::requestRemove(const QString &appId)
@@ -155,6 +159,11 @@ void LauncherModel::requestRemove(const QString &appId)
155159 return;
156160 }
157161
162+ if (m_appManager->findApplication(appId)) {
163+ m_list.at(index)->setPinned(false);
164+ return;
165+ }
166+
158167 beginRemoveRows(QModelIndex(), index, index);
159168 m_list.takeAt(index)->deleteLater();
160169 endRemoveRows();
tests/autopilot/unity8/application_lifecycle/__init__.pynew file+20
@@ -0,0 +1,20 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+"""Tests for the application lifecycle base package."""
tests/autopilot/unity8/application_lifecycle/tests/__init__.pynew file

No changes to the content.

tests/autopilot/unity8/application_lifecycle/tests/test_application_lifecycle.pynew file+136
@@ -0,0 +1,136 @@
1+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
2+#
3+# Unity Autopilot Test Suite
4+# Copyright (C) 2013 Canonical
5+#
6+# This program is free software: you can redistribute it and/or modify
7+# it under the terms of the GNU General Public License as published by
8+# the Free Software Foundation, either version 3 of the License, or
9+# (at your option) any later version.
10+#
11+# This program is distributed in the hope that it will be useful,
12+# but WITHOUT ANY WARRANTY; without even the implied warranty of
13+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+# GNU General Public License for more details.
15+#
16+# You should have received a copy of the GNU General Public License
17+# along with this program. If not, see <http://www.gnu.org/licenses/>.
18+#
19+
20+"""Tests for the application lifecycle."""
21+
22+from __future__ import absolute_import
23+
24+from unity8.shell import disable_qml_mocking
25+from unity8.shell.tests import UnityTestCase, _get_device_emulation_scenarios
26+
27+from autopilot.matchers import Eventually
28+from autopilot.platform import model
29+from testtools.matchers import Equals, NotEquals
30+
31+import logging
32+
33+logger = logging.getLogger(__name__)
34+
35+
36+class ApplicationLifecycleTests(UnityTestCase):
37+
38+ scenarios = _get_device_emulation_scenarios()
39+
40+ def setUp(self):
41+ super(ApplicationLifecycleTests, self).setUp()
42+ if model() == "Desktop":
43+ self.skipTest("Test cannot be run on the desktop.")
44+
45+ def swipe_screen_from_right(self):
46+ qml_view = self.main_window.get_qml_view()
47+ width = qml_view.width
48+ height = qml_view.height
49+ start_x = width
50+ start_y = int(height/2)
51+ end_x = int(width/2)
52+ end_y = start_y
53+
54+ logger.info("Swiping screen from the right edge")
55+ self.touch.drag(start_x, start_y, end_x, end_y)
56+
57+ def _get_current_focused_app_id(self):
58+ return self._proxy.select_single("Shell").currentFocusedAppId
59+
60+ def _launch_app(self, app_name):
61+ """Launches the application *app_name*
62+
63+ Assumes that the desktop file resides at:
64+ /usr/share/applications/{app_name}.desktop
65+
66+ """
67+ desktop_file = "--desktop_file_hint="\
68+ "/usr/share/applications/{app_name}.desktop".format(
69+ app_name=app_name
70+ )
71+ return self.launch_test_application(
72+ app_name,
73+ desktop_file,
74+ "--stage_hint=main_stage",
75+ app_type='qt'
76+ )
77+
78+ @disable_qml_mocking
79+ def test_can_launch_application(self):
80+ """Must be able to launch an application."""
81+ self.launch_unity()
82+ self.main_window.get_greeter().swipe()
83+
84+ app = self._launch_app("messaging-app")
85+
86+ self.assertThat(app, NotEquals(None))
87+ self.assertThat(
88+ self._get_current_focused_app_id(),
89+ Eventually(Equals("messaging-app"))
90+ )
91+
92+ @disable_qml_mocking
93+ def test_can_launch_multiple_applications(self):
94+ """A second application launched must be focused."""
95+ self.launch_unity()
96+ self.main_window.get_greeter().swipe()
97+
98+ self._launch_app("messaging-app")
99+ self.assertThat(
100+ self._get_current_focused_app_id(),
101+ Eventually(Equals("messaging-app"))
102+ )
103+
104+ self._launch_app("address-book-app")
105+ self.assertThat(
106+ self._get_current_focused_app_id(),
107+ Eventually(Equals("address-book-app"))
108+ )
109+
110+ @disable_qml_mocking
111+ def test_app_moves_from_unfocused_to_focused(self):
112+ """An application that is in the unfocused state must be able to be
113+ brought back to the focused state.
114+
115+ """
116+ self.launch_unity()
117+ self.main_window.get_greeter().swipe()
118+
119+ self._launch_app("messaging-app")
120+ self.assertThat(
121+ self._get_current_focused_app_id(),
122+ Eventually(Equals("messaging-app"))
123+ )
124+
125+ self._launch_app("address-book-app")
126+ self.assertThat(
127+ self._get_current_focused_app_id(),
128+ Eventually(Equals("address-book-app"))
129+ )
130+
131+ self.swipe_screen_from_right()
132+
133+ self.assertThat(
134+ self._get_current_focused_app_id(),
135+ Eventually(Equals("messaging-app"))
136+ )
tests/autopilot/unity8/shell/__init__.py+10
@@ -36,3 +36,13 @@ def with_lightdm_mock(mock_type):
3636 return fn(*args, **kwargs)
3737 return wrapper
3838 return with_lightdm_mock_internal
39+
40+
41+def disable_qml_mocking(fn):
42+ """Simple decorator that disables the QML mocks from being loaded."""
43+ @wraps(fn)
44+ def wrapper(*args, **kwargs):
45+ tests_self = args[0]
46+ tests_self._qml_mock_enabled = False
47+ return fn(*args, **kwargs)
48+ return wrapper
tests/autopilot/unity8/shell/tests/__init__.py+4−1
@@ -87,6 +87,7 @@ class UnityTestCase(AutopilotTestCase):
8787
8888 self._proxy = None
8989 self._lightdm_mock_type = None
90+ self._qml_mock_enabled = True
9091 self.touch = Touch.create()
9192 self._setup_display_details()
9293
@@ -160,7 +161,9 @@ class UnityTestCase(AutopilotTestCase):
160161
161162 if self._lightdm_mock_type is None:
162163 self.patch_lightdm_mock()
163− self._setup_extra_mock_environment_patch()
164+
165+ if self._qml_mock_enabled:
166+ self._setup_extra_mock_environment_patch()
164167
165168 app_proxy = self.launch_test_application(
166169 binary_path,
tests/plugins/CMakeLists.txt−1
@@ -1,6 +1,5 @@
11add_subdirectory(AccountsService)
22add_subdirectory(ListViewWithPageHeader)
3−add_subdirectory(SessionBroadcast)
43add_subdirectory(SessionManager)
54add_subdirectory(Ubuntu)
65add_subdirectory(Unity)
tests/plugins/SessionBroadcast/BroadcastServer.cppdeleted−30
@@ -1,30 +0,0 @@
1−/*
2− * Copyright 2013 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− * Authored by: Michael Terry <michael.terry@canonical.com>
18− */
19−
20−#include "BroadcastServer.h"
21−
22−BroadcastServer::BroadcastServer(QObject *parent)
23− : QObject(parent)
24−{
25−}
26−
27−void BroadcastServer::RequestApplicationStart(const QString &username, const QString &appId)
28−{
29− StartApplication(username, appId);
30−}
tests/plugins/SessionBroadcast/BroadcastServer.hdeleted−41
@@ -1,41 +0,0 @@
1−/*
2− * Copyright 2013 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− * Authored by: Michael Terry <michael.terry@canonical.com>
18− */
19−
20−#ifndef UNITY_MOCK_BROADCASTSERVER_H
21−#define UNITY_MOCK_BROADCASTSERVER_H
22−
23−#include <QDBusContext>
24−#include <QObject>
25−#include <QString>
26−
27−class BroadcastServer: public QObject, protected QDBusContext
28−{
29− Q_OBJECT
30−
31−public:
32− explicit BroadcastServer(QObject *parent = 0);
33−
34−public Q_SLOTS:
35− void RequestApplicationStart(const QString &username, const QString &appId);
36−
37−Q_SIGNALS:
38− void StartApplication(const QString &username, const QString &appId);
39−};
40−
41−#endif
tests/plugins/SessionBroadcast/CMakeLists.txtdeleted−37
@@ -1,37 +0,0 @@
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 -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−include_directories(
10− ${CMAKE_CURRENT_BINARY_DIR}
11− ${CMAKE_SOURCE_DIR}/plugins/SessionBroadcast
12− )
13−
14−add_definitions(-DSM_BUSNAME=sessionBus)
15−
16−add_executable(sessionbroadcastmock
17− ${CMAKE_CURRENT_BINARY_DIR}/BroadcastAdaptor.cpp
18− server.cpp
19− BroadcastServer.cpp
20− )
21−qt5_use_modules(sessionbroadcastmock Core DBus)
22−
23−add_executable(sessionbroadcasttest
24− ${CMAKE_SOURCE_DIR}/plugins/SessionBroadcast/SessionBroadcast.cpp
25− client.cpp
26− )
27−qt5_use_modules(sessionbroadcasttest Core DBus Test)
28−
29−add_test(NAME sessionbroadcasttest
30− COMMAND dbus-test-runner
31− --task ${CMAKE_CURRENT_BINARY_DIR}/sessionbroadcastmock
32− --task-name server
33− --ignore-return
34− --task ${CMAKE_CURRENT_BINARY_DIR}/sessionbroadcasttest
35− --task-name client
36− --wait-for com.canonical.Unity.Greeter.Broadcast
37− )
tests/plugins/SessionBroadcast/client.cppdeleted−62
@@ -1,62 +0,0 @@
1−/*
2− * Copyright 2013 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− * Authored by: Michael Terry <michael.terry@canonical.com>
18− */
19−
20−#include "SessionBroadcast.h"
21−#include <QSignalSpy>
22−#include <QTest>
23−
24−class BroadcastServiceTest : public QObject
25−{
26− Q_OBJECT
27−
28−private Q_SLOTS:
29−
30− void testRequest(const QString &requestedAppId, const QString &expectedAppId)
31− {
32− SessionBroadcast broadcaster;
33− QSignalSpy spy(&broadcaster, SIGNAL(startApplication(const QString &)));
34−
35− broadcaster.requestApplicationStart(qgetenv("USER"), requestedAppId);
36−
37− // Wait one second for the signal
38− int i = 0;
39− while (spy.count() == 0 && i++ < 10)
40− QTest::qWait(100);
41− QCOMPARE(spy.count(), 1);
42−
43− // Test the argument
44− auto arguments = spy.takeFirst();
45− QCOMPARE(arguments.at(0).toString(), QString(expectedAppId));
46− }
47−
48− void testAppId()
49− {
50− // Since our mock u-g-s-b just passes on the incoming appId, we test
51− // here that our plugin mangles the names appropriately.
52− testRequest("appid", "appid");
53− testRequest("appid.desktop", "appid");
54− testRequest("appid.info", "appid.info");
55− testRequest("/usr/share/applications/appid.desktop", "appid");
56− testRequest("/full/appid.desktop", "appid");
57− }
58−};
59−
60−QTEST_MAIN(BroadcastServiceTest)
61−
62−#include "client.moc"
tests/plugins/SessionBroadcast/interfaces.xmldeleted−12
@@ -1,12 +0,0 @@
1−<node>
2− <interface name="com.canonical.Unity.Greeter.Broadcast">
3− <method name="RequestApplicationStart">
4− <arg name="username" type="s" direction="in" />
5− <arg name="appId" type="s" direction="in" />
6− </method>
7− <signal name="StartApplication">
8− <arg name="username" type="s" direction="out" />
9− <arg name="appId" type="s" direction="out" />
10− </signal>
11− </interface>
12−</node>
tests/plugins/SessionBroadcast/server.cppdeleted−39
@@ -1,39 +0,0 @@
1−/*
2− * Copyright 2013 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− * Authored by: Michael Terry <michael.terry@canonical.com>
18− */
19−
20−#include "BroadcastAdaptor.h"
21−#include "BroadcastServer.h"
22−#include <QCoreApplication>
23−
24−int main(int argc, char *argv[])
25−{
26− QCoreApplication a(argc, argv);
27−
28− auto broadcaster = new BroadcastServer();
29− new BroadcastAdaptor(broadcaster);
30−
31− // We use the session bus for testing. The real plugin uses the system bus
32− auto connection = QDBusConnection::sessionBus();
33− if (!connection.registerObject("/com/canonical/Unity/Greeter/Broadcast", broadcaster))
34− return 1;
35− if (!connection.registerService("com.canonical.Unity.Greeter.Broadcast"))
36− return 1;
37−
38− return a.exec();
39−}
tests/plugins/Unity/Launcher/launchermodeltest.cpp+61−2
@@ -62,7 +62,14 @@ public:
6262 return QString();
6363 }
6464 unity::shell::application::ApplicationInfoInterface *get(int index) const { return m_list.at(index); }
65− unity::shell::application::ApplicationInfoInterface *findApplication(const QString &) const { return nullptr; }
65+ unity::shell::application::ApplicationInfoInterface *findApplication(const QString &appId) const {
66+ Q_FOREACH(MockApp* app, m_list) {
67+ if (app->appId() == appId) {
68+ return app;
69+ }
70+ }
71+ return nullptr;
72+ }
6673 unity::shell::application::ApplicationInfoInterface *startApplication(const QString &, const QStringList &) { return nullptr; }
6774 bool stopApplication(const QString &) { return false; }
6875 bool focusApplication(const QString &appId) {
@@ -151,9 +158,54 @@ private Q_SLOTS:
151158 QCOMPARE(launcherModel->get(1)->pinned(), false);
152159 }
153160
161+ void testRemove_data() {
162+
163+ QTest::addColumn<bool>("pinned");
164+ QTest::addColumn<bool>("running");
165+
166+ QTest::newRow("non-pinned, running") << false << true;
167+ QTest::newRow("pinned, running") << true << false;
168+ QTest::newRow("pinned, non-running") << true << false;
169+ }
170+
154171 void testRemove() {
172+ QFETCH(bool, pinned);
173+ QFETCH(bool, running);
174+
175+ // In the beginning we always have two items
155176 QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
156− launcherModel->requestRemove(launcherModel->get(0)->appId());
177+
178+ // pin one if required
179+ if (pinned) {
180+ launcherModel->pin(launcherModel->get(1)->appId());
181+ }
182+
183+ // stop it if required
184+ if (!running) {
185+ appManager->removeApplication(1);
186+ }
187+
188+ // Now remove it
189+ launcherModel->requestRemove(launcherModel->get(1)->appId());
190+
191+ if (running) {
192+ // both apps are running, both apps must still be here
193+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
194+
195+ // Item must be unpinned now
196+ QCOMPARE(launcherModel->get(1)->pinned(), false);
197+
198+ } else if (pinned) {
199+ // Item 1 must go away, item 0 is here to stay
200+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
201+ }
202+
203+ // done our checks. now stop the app if was still running
204+ if (running) {
205+ appManager->removeApplication(1);
206+ }
207+
208+ // It needs to go away in any case now
157209 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
158210 }
159211
@@ -181,6 +233,13 @@ private Q_SLOTS:
181233
182234 // quicklist needs to transform to remove item. trigger it and check it item goes away
183235 launcherModel->quickListActionInvoked(launcherModel->get(0)->appId(), pinActionIndex);
236+ QCOMPARE(launcherModel->get(0)->pinned(), false);
237+
238+ // still needs to be here as the app is still here
239+ QCOMPARE(launcherModel->rowCount(QModelIndex()), 2);
240+ // close the app
241+ appManager->removeApplication(0);
242+ // Now it needs to go away
184243 QCOMPARE(launcherModel->rowCount(QModelIndex()), 1);
185244 }
186245
tests/qmltests/Dash/tst_DashContent.qml+1−1
@@ -106,7 +106,7 @@ Item {
106106 contentEndReachedSpy.clear()
107107 clear_scope_status();
108108
109− // clear, wait for dahs to empty and load scopes.
109+ // clear, wait for dash to empty and load scopes.
110110 var dashContentList = findChild(dashContent, "dashContentList");
111111 verify(dashContentList != undefined)
112112 scopesModel.clear();
tests/qmltests/Dash/tst_GenericScopeView.qml+3
@@ -44,10 +44,13 @@ Item {
4444
4545 function test_isCurrent() {
4646 var pageHeader = findChild(genericScopeView, "pageHeader");
47+ var previewLoader = findChild(genericScopeView, "previewLoader");
4748 genericScopeView.isCurrent = true
4849 pageHeader.searchQuery = "test"
50+ previewLoader.open = true
4951 genericScopeView.isCurrent = false
5052 tryCompare(pageHeader, "searchQuery", "")
53+ tryCompare(genericScopeView, "previewShown", false);
5154 }
5255 }
5356 }
tests/uqmlscene/main.cpp−2
@@ -497,8 +497,6 @@ int main(int argc, char ** argv)
497497 }
498498
499499 if (window) {
500− mouseTouchAdaptor.setTargetWindow(window);
501−
502500 QSurfaceFormat surfaceFormat = window->requestedFormat();
503501 if (options.multisample)
504502 surfaceFormat.setSamples(16);