Merge lp:~andreas-pokorny/unity-mir/fix-1240400 into lp:unity-mir

Proposed by Andreas Pokorny
Status: Superseded
Proposed branch: lp:~andreas-pokorny/unity-mir/fix-1240400
Merge into: lp:unity-mir
Diff against target: 1548 lines (+834/-218)
20 files modified
CMakeLists.txt (+1/-0)
cmake/LinuxCrossCompile.cmake (+39/-0)
cross-compile-chroot.sh (+85/-0)
debian/control (+1/-0)
scripts/fix-qt-cmake.sh (+10/-0)
scripts/setup-partial-armhf-chroot.sh (+67/-0)
src/modules/Unity/Application/CMakeLists.txt (+5/-6)
src/modules/Unity/Application/application.cpp (+1/-1)
src/modules/Unity/Application/application_manager.cpp (+219/-132)
src/modules/Unity/Application/application_manager.h (+15/-2)
src/modules/Unity/Application/proc_info.cpp (+52/-0)
src/modules/Unity/Application/proc_info.h (+48/-0)
src/unity-mir/CMakeLists.txt (+3/-5)
tests/CMakeLists.txt (+1/-0)
tests/application_manager_test.cpp (+137/-64)
tests/auto/modules/Unity/Application/CMakeLists.txt (+2/-2)
tests/auto/modules/Unity/Application/main.cpp (+5/-6)
tests/mock_focus_controller.h (+37/-0)
tests/mock_proc_info.h (+37/-0)
tests/mock_session.h (+69/-0)
To merge this branch: bzr merge lp:~andreas-pokorny/unity-mir/fix-1240400
Reviewer Review Type Date Requested Status
Mir development team Pending
Review via email: mp+207301@code.launchpad.net

This proposal has been superseded by a proposal from 2014-02-19.

Commit message

Bugfix for unity-mir crash when multiple application instances with the same appId get started & stopped.

This change also introduces further mocks for mir and unity-mir classes and a refactored ApplicationManager. The log traces where extended with more information on the touched application objects.

Description of the change

Bugfix for unity-mir crash when multiple application instances with the same appId get started & stopped.

This change solves the issue to some degree - but cannot solve it entirely - since there is no API yet to get something like an instance id. So closing one application may still close the wrong application.

ApplicationManager had to be refactored. The constructor no longer accesses the QApplication object or connects itself to all the signal sources. This is now encapsulated into a factory class, which also reduces the dependency on mir structures to the FocusController.

To post a comment you must log in.
181. By Alberto Aguirre

[cmake] Use XXX_LDFLAGS for libraries found with pkg_check_modules.

Using the pkg given LDFLAGS resolves linking issues when cross-compiling

182. By Alberto Aguirre

Add dev scripts to support cross-compilation

These scripts are adapted from the mir project.
The build dependencies are parsed from debian/control and
given to debootstrap to setup a basic armhf chroot environment for cross-compilation.

These scripts are intended for development purposes only

183. By Andreas Pokorny

more log traces

184. By Andreas Pokorny

Adding Proc Info and Mock and a Mir Session Mock for unit testing

185. By Andreas Pokorny

Fixes Unity8 crash when two instances of the same application are launched
and the second one stops.

There is still misbehavior that cannot be resolved without further changes in
upstart-app-launch. I.e. when the second instance is shutdown the existing one
will be closed too, since they use the same appId.

The changes in the construction of ApplicationManager where neceessary to
replace the external parts of ApplicationManager with mock objects.

186. By Andreas Pokorny

review findings address

187. By Andreas Pokorny

Code style changes were not reflected in MockProcInfo

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2014-02-14 15:42:49 +0000
3+++ CMakeLists.txt 2014-02-19 21:10:08 +0000
4@@ -59,6 +59,7 @@
5 find_package(Qt5Core REQUIRED)
6 find_package(Qt5Quick REQUIRED)
7 find_package(Qt5DBus REQUIRED)
8+find_package(Boost 1.50 COMPONENTS system REQUIRED)
9
10 find_package(Protobuf REQUIRED)
11 if(PROTOBUF_PROTOC_EXECUTABLE STREQUAL "PROTOBUF_PROTOC_EXECUTABLE-NOTFOUND")
12
13=== added directory 'cmake'
14=== added file 'cmake/LinuxCrossCompile.cmake'
15--- cmake/LinuxCrossCompile.cmake 1970-01-01 00:00:00 +0000
16+++ cmake/LinuxCrossCompile.cmake 2014-02-19 21:10:08 +0000
17@@ -0,0 +1,39 @@
18+set(CMAKE_SYSTEM_NAME Linux)
19+set(CMAKE_SYSTEM_VERSION 1)
20+
21+set(UNITYMIR_CHROOT_DIR $ENV{UNITYMIR_CHROOT_DIR} CACHE STRING "directory containing partial chroot for unity-mir cross-compilation")
22+set(UNITYMIR_ARM_EABI "arm-linux-gnueabihf")
23+
24+set(CMAKE_C_COMPILER /usr/bin/${UNITYMIR_ARM_EABI}-gcc)
25+set(CMAKE_CXX_COMPILER /usr/bin/${UNITYMIR_ARM_EABI}-g++)
26+
27+# where to look to find dependencies in the target environment
28+set(CMAKE_FIND_ROOT_PATH "${UNITYMIR_CHROOT_DIR}")
29+
30+#treat the chroot's includes as system includes
31+include_directories(SYSTEM "${UNITYMIR_CHROOT_DIR}/usr/include" "${UNITYMIR_CHROOT_DIR}/usr/include/${UNITYMIR_ARM_EABI}")
32+
33+list(APPEND CMAKE_SYSTEM_INCLUDE_PATH "${UNITYMIR_CHROOT_DIR}/usr/include" "${UNITYMIR_CHROOT_DIR}/usr/include/${UNITYMIR_ARM_EABI}" )
34+
35+# Add the chroot libraries as system libraries
36+list(APPEND CMAKE_SYSTEM_LIBRARY_PATH
37+ "${UNITYMIR_CHROOT_DIR}/lib"
38+ "${UNITYMIR_CHROOT_DIR}/lib/${UNITYMIR_ARM_EABI}"
39+ "${UNITYMIR_CHROOT_DIR}/usr/lib"
40+ "${UNITYMIR_CHROOT_DIR}/usr/lib/${UNITYMIR_ARM_EABI}"
41+)
42+
43+set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
44+set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
45+set(CMAKE_EXECUTABLE_RUNTIME_C_FLAG "-Wl,-rpath-link,")
46+set(CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG "-Wl,-rpath-link,")
47+set(CMAKE_INSTALL_RPATH "${UNITYMIR_CHROOT_DIR}/lib:${UNITYMIR_CHROOT_DIR}/lib/${UNITYMIR_ARM_EABI}:${UNITYMIR_CHROOT_DIR}/usr/lib:${UNITYMIR_CHROOT_DIR}/usr/lib/${UNITYMIR_ARM_EABI}:${UNITYMIR_CHROOT_DIR}/usr/lib/${UNITYMIR_ARM_EABI}/mesa-egl")
48+
49+set(ENV{PKG_CONFIG_PATH} "${UNITYMIR_CHROOT_DIR}/usr/lib/pkgconfig:${UNITYMIR_CHROOT_DIR}/usr/lib/${UNITYMIR_ARM_EABI}/pkgconfig")
50+set(ENV{PKG_CONFIG_SYSROOT_DIR} "${UNITYMIR_CHROOT_DIR}")
51+
52+#use only the cross compile system
53+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
54+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
55+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
56+
57
58=== added file 'cross-compile-chroot.sh'
59--- cross-compile-chroot.sh 1970-01-01 00:00:00 +0000
60+++ cross-compile-chroot.sh 2014-02-19 21:10:08 +0000
61@@ -0,0 +1,85 @@
62+#!/bin/bash
63+# build script to compile unity-mir for armhf devices
64+
65+set -e
66+
67+usage() {
68+ echo "usage: $(basename $0) [-c] [-u]"
69+ echo "-c clean before building"
70+ echo "-u update partial chroot directory"
71+ echo "-h this message"
72+}
73+
74+clean_build_dir() {
75+ rm -rf ${1}
76+ mkdir ${1}
77+}
78+
79+BUILD_DIR=build-android-arm
80+NUM_JOBS=$(( $(grep -c ^processor /proc/cpuinfo) + 1 ))
81+_do_update_chroot=0
82+
83+while getopts "cuh" OPTNAME
84+do
85+ case $OPTNAME in
86+ c )
87+ clean_build_dir ${BUILD_DIR}
88+ ;;
89+ u )
90+ _do_update_chroot=1
91+ ;;
92+ h )
93+ usage
94+ exit 0
95+ ;;
96+ * )
97+ echo "invalid option specified"
98+ usage
99+ exit 1
100+ ;;
101+ esac
102+done
103+
104+
105+if [ "${UNITYMIR_CHROOT_DIR}" = "" ]; then
106+ export UNITYMIR_CHROOT_DIR=$(pwd)/partial-armhf-chroot
107+fi
108+
109+if [ ! -d ${UNITYMIR_CHROOT_DIR} ]; then
110+ echo "no partial chroot dir detected. attempting to create one"
111+ _do_update_chroot=1
112+fi
113+
114+if [ ! -d ${BUILD_DIR} ]; then
115+ mkdir ${BUILD_DIR}
116+fi
117+
118+if [ ${_do_update_chroot} -eq 1 ] ; then
119+ pushd scripts > /dev/null
120+ ./setup-partial-armhf-chroot.sh ${UNITYMIR_CHROOT_DIR}
121+ popd > /dev/null
122+ # force a clean build after an update, since CMake cache maybe out of date
123+ clean_build_dir ${BUILD_DIR}
124+fi
125+
126+echo "Using UNITYMIR_CHROOT_DIR: ${UNITYMIR_CHROOT_DIR}"
127+
128+pushd ${BUILD_DIR} > /dev/null
129+
130+ export CMAKE_PREFIX_PATH=${UNITYMIR_CHROOT_DIR}/usr/lib/arm-linux-gnueabihf/cmake
131+ export PKG_CONFIG_PATH="${UNITYMIR_CHROOT_DIR}/usr/lib/pkgconfig:${UNITYMIR_CHROOT_DIR}/usr/lib/arm-linux-gnueabihf/pkgconfig"
132+ export PKG_CONFIG_ALLOW_SYSTEM_CFLAGS=1
133+ export PKG_CONFIG_ALLOW_SYSTEM_LIBS=1
134+ export PKG_CONFIG_SYSROOT_DIR=${UNITYMIR_CHROOT_DIR}
135+ export PKG_CONFIG_EXECUTABLE=`which pkg-config`
136+ echo "Using PKG_CONFIG_PATH: $PKG_CONFIG_PATH"
137+ echo "Using PKG_CONFIG_EXECUTABLE: $PKG_CONFIG_EXECUTABLE"
138+
139+ # These are used to make cmake select the host machine QT MOC compiler in the AutoMocInfo module
140+ export DEB_HOST_MULTIARCH=arm-linux-gnueabihf
141+ export DEB_BUILD_MULTIARCH=$(gcc -dumpmachine)
142+
143+ cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/LinuxCrossCompile.cmake ..
144+ make -j${NUM_JOBS}
145+
146+popd ${BUILD_DIR} > /dev/null
147
148=== modified file 'debian/control'
149--- debian/control 2014-02-14 15:42:49 +0000
150+++ debian/control 2014-02-19 21:10:08 +0000
151@@ -6,6 +6,7 @@
152 cmake,
153 google-mock (>= 1.6.0+svn437),
154 pkg-config,
155+ libboost-dev,
156 libboost-system-dev,
157 libplatform-api1-dev,
158 libmirserver-dev (>= 0.1.5),
159
160=== added directory 'scripts'
161=== added file 'scripts/fix-qt-cmake.sh'
162--- scripts/fix-qt-cmake.sh 1970-01-01 00:00:00 +0000
163+++ scripts/fix-qt-cmake.sh 2014-02-19 21:10:08 +0000
164@@ -0,0 +1,10 @@
165+#!/bin/bash
166+
167+# $1 must contain path to QT cmake files
168+# Given that path, all hardcoded root paths are fixed up
169+for file in $(find ${1} -type f -name \*.cmake)
170+do
171+ echo "fixing $file"
172+ sed -i 's/NO_DEFAULT_PATH/ONLY_CMAKE_FIND_ROOT_PATH/g' $file
173+ sed -i 's/\/usr/${CMAKE_FIND_ROOT_PATH}\/usr/g' $file
174+done
175
176=== added file 'scripts/setup-partial-armhf-chroot.sh'
177--- scripts/setup-partial-armhf-chroot.sh 1970-01-01 00:00:00 +0000
178+++ scripts/setup-partial-armhf-chroot.sh 2014-02-19 21:10:08 +0000
179@@ -0,0 +1,67 @@
180+#!/bin/bash
181+
182+set -e
183+
184+if [ -z ${1} ]; then
185+ echo "please supply directory to create partial chroot in. (eg, ./setup-partial-armhf-chroot.sh mychroot-dir)"
186+ exit
187+fi
188+
189+echo "creating phablet-compatible armhf partial chroot for unity-mir compilation in directory ${1}"
190+
191+if [ ! -d ${1} ]; then
192+ mkdir -p ${1}
193+fi
194+
195+DEBCONTROL=$(pwd)/../debian/control
196+
197+pushd ${1} > /dev/null
198+
199+# Empty dpkg status file, so that ALL dependencies are listed with dpkg-checkbuilddeps
200+echo "" > status
201+
202+# Manual error code checking is needed for dpkg-checkbuilddeps
203+set +e
204+
205+# Parse dependencies from debian/control
206+# dpkg-checkbuilddeps returns 1 when dependencies are not met and the list is sent to stderr
207+builddeps=$(dpkg-checkbuilddeps -a armhf --admindir=. ${DEBCONTROL} 2>&1 )
208+if [ $? -ne 1 ] ; then
209+ echo "${builddeps}"
210+ exit 2
211+fi
212+
213+# now turn exit on error option
214+set -e
215+
216+# Sanitize dependencies list for submission to debootstrap
217+# build-essential is not needed as we are cross-compiling
218+builddeps=$(echo ${builddeps} | sed -e 's/dpkg-checkbuilddeps://g' -e 's/Unmet build dependencies://g' -e 's/build-essential:native//g')
219+builddeps=$(echo ${builddeps} | sed 's/([^)]*)//g')
220+# TODO: figure out why debootstrap is not finding libunity-api-dev package - it doesn't seem to be needed for cross-compilation - is it needed in debian/control?
221+builddeps=$(echo ${builddeps} | sed 's/libunity-api-dev//g')
222+builddeps=$(echo ${builddeps} | sed 's/ /,/g')
223+
224+fakeroot debootstrap --include=${builddeps} --arch=armhf --download-only --variant=buildd trusty .
225+
226+# Remove libc libraries that confuse the cross-compiler
227+rm var/cache/apt/archives/libc-dev*.deb
228+rm var/cache/apt/archives/libc6*.deb
229+
230+for deb in var/cache/apt/archives/* ; do
231+if [ ! -d ${deb} ] ; then
232+ echo "unpacking: ${deb}"
233+ dpkg -x ${deb} .
234+fi
235+done
236+
237+# Fix up symlinks which asssumed the usual root path
238+for broken_symlink in $(find . -name \*.so -type l -xtype l) ; do
239+ ln -sf $(pwd)$(readlink ${broken_symlink}) ${broken_symlink}
240+done
241+
242+popd > /dev/null
243+
244+# QT CMake files have hardcoded root paths - fix them up
245+./fix-qt-cmake.sh ${1}/usr/lib/arm-linux-gnueabihf/cmake/
246+
247
248=== modified file 'src/modules/Unity/Application/CMakeLists.txt'
249--- src/modules/Unity/Application/CMakeLists.txt 2014-01-27 11:29:44 +0000
250+++ src/modules/Unity/Application/CMakeLists.txt 2014-02-19 21:10:08 +0000
251@@ -31,6 +31,7 @@
252 inputfilterarea.cpp
253 processcontroller.h
254 processcontroller.cpp
255+ proc_info.cpp
256 shellinputarea.cpp
257 ubuntukeyboardinfo.cpp
258
259@@ -74,12 +75,10 @@
260 ${CMAKE_THREAD_LIBS_INIT}
261
262 ${GLIB_LDFLAGS}
263- ${UBUNTU_PLATFORM_API_LIBRARIES}
264- ${MIRCOMMON_LIBRARIES}
265- ${MIRSERVER_LIBRARIES}
266- ${PROCESS_CPP_LIBRARIES}
267- ${UBUNTU_PLATFORM_API_LIBRARIES}
268- ${UPSTART_APP_LAUNCH_LIBRARIES}
269+ ${UBUNTU_PLATFORM_API_LDFLAGS}
270+ ${MIRSERVER_LDFLAGS}
271+ ${PROCESS_CPP_LDFLAGS}
272+ ${UPSTART_APP_LAUNCH_LDFLAGS}
273
274 ubuntu_application_api_mirserver
275 )
276
277=== modified file 'src/modules/Unity/Application/application.cpp'
278--- src/modules/Unity/Application/application.cpp 2014-01-27 11:29:44 +0000
279+++ src/modules/Unity/Application/application.cpp 2014-02-19 21:10:08 +0000
280@@ -52,7 +52,7 @@
281
282 Application::~Application()
283 {
284- DLOG("Application::~Application");
285+ DLOG("Application::~Application (this=%p)", this);
286 delete m_desktopData;
287 }
288
289
290=== modified file 'src/modules/Unity/Application/application_manager.cpp'
291--- src/modules/Unity/Application/application_manager.cpp 2014-02-11 09:47:56 +0000
292+++ src/modules/Unity/Application/application_manager.cpp 2014-02-19 21:10:08 +0000
293@@ -16,6 +16,7 @@
294
295 // local
296 #include "application_manager.h"
297+#include "proc_info.h"
298 #include "application.h"
299 #include "desktopfilereader.h"
300 #include "dbuswindowstack.h"
301@@ -51,26 +52,130 @@
302
303 using namespace unity::shell::application;
304
305-ApplicationManager *ApplicationManager::the_application_manager = nullptr;
306+namespace
307+{
308+
309+QSize get_display_size(std::shared_ptr<mir::graphics::Display> const& display) {
310+ // Obtain display size
311+ mir::geometry::Rectangles view_area;
312+ display->for_each_display_buffer(
313+ [&view_area](mir::graphics::DisplayBuffer const& db)
314+ {
315+ view_area.add(db.view_area());
316+ });
317+
318+ return QSize(
319+ view_area.bounding_rectangle().size.width.as_uint32_t(),
320+ view_area.bounding_rectangle().size.height.as_uint32_t()
321+ );
322+}
323+
324+
325+void connectToSessionListener(ApplicationManager * manager, SessionListener * listener)
326+{
327+
328+ QObject::connect(listener, &SessionListener::sessionStarting,
329+ manager, &ApplicationManager::onSessionStarting);
330+ QObject::connect(listener, &SessionListener::sessionStopping,
331+ manager, &ApplicationManager::onSessionStopping);
332+ QObject::connect(listener, &SessionListener::sessionFocused,
333+ manager, &ApplicationManager::onSessionFocused, Qt::QueuedConnection);
334+ QObject::connect(listener, &SessionListener::sessionUnfocused,
335+ manager, &ApplicationManager::onSessionUnfocused);
336+ QObject::connect(listener, &SessionListener::sessionCreatedSurface,
337+ manager, &ApplicationManager::onSessionCreatedSurface);
338+ QObject::connect(listener, &SessionListener::sessionStarting,
339+ manager, &ApplicationManager::onSessionStarting);
340+ QObject::connect(listener, &SessionListener::sessionStopping,
341+ manager, &ApplicationManager::onSessionStopping);
342+ QObject::connect(listener, &SessionListener::sessionFocused,
343+ manager, &ApplicationManager::onSessionFocused, Qt::QueuedConnection);
344+ QObject::connect(listener, &SessionListener::sessionUnfocused,
345+ manager, &ApplicationManager::onSessionUnfocused);
346+ QObject::connect(listener, &SessionListener::sessionCreatedSurface,
347+ manager, &ApplicationManager::onSessionCreatedSurface);
348+}
349+
350+void connectToSessionAuthorizer(ApplicationManager * manager, SessionAuthorizer * authorizer)
351+{
352+ QObject::connect(authorizer, &SessionAuthorizer::requestAuthorizationForSession,
353+ manager, &ApplicationManager::authorizeSession, Qt::BlockingQueuedConnection);
354+}
355+
356+
357+void connectToPlacementStrategy(ApplicationManager * manager, InitialSurfacePlacementStrategy * strategy)
358+{
359+ QObject::connect(strategy, &InitialSurfacePlacementStrategy::requestPlacementForSession,
360+ manager, &ApplicationManager::placeSession, Qt::DirectConnection);
361+
362+}
363+
364+void connectToTaskController(ApplicationManager * manager, TaskController * controller)
365+{
366+ QObject::connect(controller, &TaskController::processStartReport,
367+ manager, &ApplicationManager::onProcessStartReportReceived);
368+ QObject::connect(controller, &TaskController::processStopped,
369+ manager, &ApplicationManager::onProcessStopped);
370+ QObject::connect(controller, &TaskController::requestFocus,
371+ manager, &ApplicationManager::onFocusRequested);
372+ QObject::connect(controller, &TaskController::requestResume,
373+ manager, &ApplicationManager::onResumeRequested);
374+
375+}
376+}
377+
378+QSharedPointer<ApplicationManager> ApplicationManager::Factory::Factory::create()
379+{
380+ QMirServerApplication* mirServerApplication = dynamic_cast<QMirServerApplication*>(QCoreApplication::instance());
381+ if (mirServerApplication == NULL) {
382+ LOG("Need to use QMirServerApplication");
383+ QCoreApplication::quit();
384+ return QSharedPointer<ApplicationManager>(nullptr);
385+ }
386+
387+ ShellServerConfiguration * mirServer = mirServerApplication->server();
388+
389+ QSize displaySize{get_display_size(mirServer->the_display())};
390+
391+ QSharedPointer<upstart::ApplicationController> appController(new upstart::ApplicationController());
392+ QSharedPointer<TaskController> taskController(new TaskController(nullptr, appController));
393+ QSharedPointer<DesktopFileReader::Factory> fileReaderFactory(new DesktopFileReader::Factory());
394+ QSharedPointer<ProcInfo> procInfo(new ProcInfo());
395+ QSharedPointer<ApplicationManager> appManager(
396+ new ApplicationManager(
397+ taskController,
398+ fileReaderFactory,
399+ procInfo,
400+ mirServer->the_focus_controller(),
401+ displaySize
402+ )
403+ );
404+
405+
406+ connectToSessionListener(appManager.data(), mirServer->sessionListener());
407+ connectToSessionAuthorizer(appManager.data(), mirServer->sessionAuthorizer());
408+ connectToPlacementStrategy(appManager.data(), mirServer->placementStrategy());
409+ connectToTaskController(appManager.data(), taskController.data());
410+
411+ return appManager;
412+}
413
414 ApplicationManager* ApplicationManager::singleton()
415 {
416- if (!the_application_manager) {
417- the_application_manager = new ApplicationManager(
418- QSharedPointer<TaskController>(
419- new TaskController(
420- nullptr,
421- QSharedPointer<ApplicationController>(
422- new upstart::ApplicationController()))),
423- QSharedPointer<DesktopFileReader::Factory>(
424- new DesktopFileReader::Factory()));
425+ static QSharedPointer<ApplicationManager> instance;
426+ if (!instance) {
427+ Factory appFactory;
428+ instance = appFactory.create();
429 }
430- return the_application_manager;
431+ return instance.data();
432 }
433
434 ApplicationManager::ApplicationManager(
435 const QSharedPointer<TaskController>& taskController,
436 const QSharedPointer<DesktopFileReader::Factory>& desktopFileReaderFactory,
437+ const QSharedPointer<ProcInfo>& procInfo,
438+ const std::shared_ptr<mir::shell::FocusController> & controller,
439+ const QSize & displaySize,
440 QObject *parent)
441 : ApplicationManagerInterface(parent)
442 , m_focusedApplication(nullptr)
443@@ -79,63 +184,18 @@
444 , m_msApplicationToBeFocused(nullptr)
445 , m_ssApplicationToBeFocused(nullptr)
446 , m_lifecycleExceptions(QStringList() << "com.ubuntu.music")
447+ , m_focusController(controller)
448+ , m_dbusWindowStack(new DBusWindowStack(this))
449 , m_taskController(taskController)
450 , m_desktopFileReaderFactory(desktopFileReaderFactory)
451+ , m_procInfo(procInfo)
452 , m_gridUnitPx(8)
453 , m_fenceNext(false)
454+ , m_displaySize(displaySize)
455 , m_panelHeight(54)
456 {
457 DLOG("ApplicationManager::ApplicationManager (this=%p)", this);
458
459- QMirServerApplication* mirServerApplication = dynamic_cast<QMirServerApplication*>(QCoreApplication::instance());
460- if (mirServerApplication == NULL) {
461- LOG("Need to use QMirServerApplication");
462- QCoreApplication::quit();
463- return;
464- }
465- m_mirServer = mirServerApplication->server();
466-
467- QObject::connect(m_mirServer->sessionListener(), &SessionListener::sessionStarting,
468- this, &ApplicationManager::onSessionStarting);
469- QObject::connect(m_mirServer->sessionListener(), &SessionListener::sessionStopping,
470- this, &ApplicationManager::onSessionStopping);
471- QObject::connect(m_mirServer->sessionListener(), &SessionListener::sessionFocused,
472- this, &ApplicationManager::onSessionFocused, Qt::QueuedConnection);
473- QObject::connect(m_mirServer->sessionListener(), &SessionListener::sessionUnfocused,
474- this, &ApplicationManager::onSessionUnfocused);
475- QObject::connect(m_mirServer->sessionListener(), &SessionListener::sessionCreatedSurface,
476- this, &ApplicationManager::onSessionCreatedSurface);
477- QObject::connect(m_mirServer->sessionAuthorizer(), &SessionAuthorizer::requestAuthorizationForSession,
478- this, &ApplicationManager::authorizeSession, Qt::BlockingQueuedConnection);
479- QObject::connect(m_mirServer->placementStrategy(), &InitialSurfacePlacementStrategy::requestPlacementForSession,
480- this, &ApplicationManager::placeSession, Qt::DirectConnection);
481-
482- QObject::connect(m_taskController.data(), &TaskController::processStartReport,
483- this, &ApplicationManager::onProcessStartReportReceived);
484- QObject::connect(m_taskController.data(), &TaskController::processStopped,
485- this, &ApplicationManager::onProcessStopped);
486- QObject::connect(m_taskController.data(), &TaskController::requestFocus,
487- this, &ApplicationManager::onFocusRequested);
488- QObject::connect(m_taskController.data(), &TaskController::requestResume,
489- this, &ApplicationManager::onResumeRequested);
490-
491- m_dbusWindowStack = new DBusWindowStack(this);
492-
493- std::shared_ptr<mir::graphics::Display> mirDisplay = m_mirServer->the_display();
494-
495- // Obtain display size
496- mir::geometry::Rectangles view_area;
497- mirDisplay->for_each_display_buffer(
498- [&view_area](mir::graphics::DisplayBuffer const& db)
499- {
500- view_area.add(db.view_area());
501- });
502-
503- m_displaySize = QSize(
504- view_area.bounding_rectangle().size.width.as_uint32_t(),
505- view_area.bounding_rectangle().size.height.as_uint32_t()
506- );
507-
508 // Setup panel height
509 QByteArray gridUnitString = qgetenv("GRID_UNIT_PX");
510 if (!gridUnitString.isEmpty()) {
511@@ -222,6 +282,7 @@
512 if (application == nullptr)
513 return;
514
515+ DLOG("ApplicationManager::suspend(this=%p, application(%p)->appId(%s) )",this, application, qPrintable(application->appId()));
516 // Present in exceptions list, return.
517 if (!m_lifecycleExceptions.filter(application->appId().section('_',0,0)).empty())
518 return;
519@@ -232,8 +293,8 @@
520
521 bool ApplicationManager::focusApplication(const QString &appId)
522 {
523- DLOG("ApplicationManager::focusApplication (this=%p, appId=%s)", this, qPrintable(appId));
524 Application *application = findApplication(appId);
525+ DLOG("ApplicationManager::focusApplication (this=%p, application=%p, appId=%s)", this, application, qPrintable(appId));
526
527 if (!application) {
528 DLOG("No such running application '%s'", qPrintable(appId));
529@@ -256,7 +317,7 @@
530 move(from, m_applications.length()-1);
531 } else {
532 if (application->session())
533- m_mirServer->the_focus_controller()->set_focus_to(application->session());
534+ m_focusController->set_focus_to(application->session());
535 }
536
537 // FIXME(dandrader): lying here. The operation is async. So we will only know whether
538@@ -275,7 +336,7 @@
539 // Clear both stages
540 m_msApplicationToBeFocused = nullptr;
541 m_ssApplicationToBeFocused = nullptr;
542- m_mirServer->the_focus_controller()->set_focus_to(NULL); //FIXME(greyback)
543+ m_focusController->set_focus_to(NULL); //FIXME(greyback)
544 }
545
546 Application* ApplicationManager::startApplication(const QString &appId,
547@@ -294,6 +355,16 @@
548 return nullptr;
549 }
550
551+ {
552+ Application * application = findApplication(appId);
553+ if (application)
554+ {
555+ DLOG("ApplicationManager::startApplication - application already "
556+ "exists: (this=%p, app=%p, appId=%s)",
557+ this, application, qPrintable(appId));
558+ }
559+ }
560+
561 Application* application = new Application(
562 m_taskController,
563 m_desktopFileReaderFactory->createInstanceForAppId(appId),
564@@ -320,7 +391,9 @@
565 this, qPrintable(appId), (failure) ? 'Y' : 'N');
566
567 if (failure) {
568- onProcessStopped(appId, true);
569+ DLOG("ApplicationManager::onProcessStartReportReceived handling failure:");
570+ stopStartingApplication(appId);
571+ return;
572 }
573
574 Application *application = findApplication(appId);
575@@ -338,30 +411,23 @@
576 add(application);
577 Q_EMIT focusRequested(appId);
578 }
579+ else {
580+ DLOG("ApplicationManager::onProcessStartReportReceived application already found: (app=%p, appId=%s)", application, qPrintable(appId));
581+ }
582 }
583
584 bool ApplicationManager::stopApplication(const QString &appId)
585 {
586- DLOG("ApplicationManager::stopApplication (this=%p, appId=%s)", this, qPrintable(appId));
587-
588 Application *application = findApplication(appId);
589+ DLOG("ApplicationManager::stopApplication (this=%p, application=%p, appId=%s)", this, application, qPrintable(appId));
590
591 if (!application) {
592 DLOG("No such running application '%s'", qPrintable(appId));
593 return false;
594 }
595
596- if (application == m_focusedApplication) {
597- // TODO(greyback) What to do?? Focus next app, or unfocus everything??
598- m_focusedApplication = NULL;
599- Q_EMIT focusedApplicationIdChanged();
600- }
601+ checkFocusOnRemovedApplication(application);
602
603- if (application == m_mainStageApplication)
604- m_mainStageApplication = nullptr;
605- if (application == m_sideStageApplication)
606- m_sideStageApplication = nullptr;
607-
608 remove(application);
609 m_dbusWindowStack->WindowDestroyed(0, application->appId());
610
611@@ -376,37 +442,28 @@
612 return result;
613 }
614
615+void ApplicationManager::stopStartingApplication(const QString &appId)
616+{
617+ Application *application = findApplication(appId);
618+
619+ if (application && application->state() == Application::Starting) {
620+ shutdownApplication(application);
621+ }
622+ else if (application) {
623+ DLOG("onProcessStartReportReceived failure - but application=%p, appId=%s is not in Starting state",application, qPrintable(appId));
624+ }
625+}
626+
627 void ApplicationManager::onProcessStopped(const QString &appId, const bool unexpected)
628 {
629 Application *application = findApplication(appId);
630+ DLOG("ApplicationManager::onProcessStopped (this=%p, application=%p, appId=%s)", this, application, qPrintable(appId));
631
632 // if shell did not stop the application, but upstart says it died, we assume the process has been
633 // killed, so it can be respawned later. Only exception is if that application is focused or running
634 // as then it most likely crashed. Update this logic when upstart gives some failure info.
635 if (application) {
636- bool removeApplication = false;
637-
638- if (application == m_focusedApplication) {
639- // Very bad case where focused application dies. Remove from list. Should give error message
640- m_focusedApplication = nullptr;
641- Q_EMIT focusedApplicationIdChanged();
642- removeApplication = true;
643- }
644-
645- if (application->state() == Application::Running || application->state() == Application::Starting) {
646- // Application probably crashed, else OOM killer struck. Either way state wasn't saved
647- // so just remove application
648- removeApplication = true;
649- } else if (application->state() == Application::Suspended) {
650- application->setState(Application::Stopped);
651- application->setSession(nullptr);
652- }
653-
654- if (removeApplication) {
655- remove(application);
656- m_dbusWindowStack->WindowDestroyed(0, application->appId());
657- delete application;
658- }
659+ shutdownApplication(application);
660 }
661
662 if (unexpected) {
663@@ -415,6 +472,27 @@
664 }
665 }
666
667+void ApplicationManager::shutdownApplication(Application* application)
668+{
669+ bool removeApplication = checkFocusOnRemovedApplication(application);
670+
671+ if (application->state() == Application::Running || application->state() == Application::Starting) {
672+ // Application probably crashed, else OOM killer struck. Either way state wasn't saved
673+ // so just remove application
674+ removeApplication = true;
675+ } else if (application->state() == Application::Suspended) {
676+ application->setState(Application::Stopped);
677+ application->setSession(nullptr);
678+ }
679+
680+ if (removeApplication) {
681+ remove(application);
682+ m_dbusWindowStack->WindowDestroyed(0, application->appId());
683+ delete application;
684+ }
685+
686+}
687+
688 void ApplicationManager::onFocusRequested(const QString& appId)
689 {
690 DLOG("ApplicationManager::onFocusRequested (this=%p, appId=%s)", this, qPrintable(appId));
691@@ -424,9 +502,9 @@
692
693 void ApplicationManager::onResumeRequested(const QString& appId)
694 {
695- DLOG("ApplicationManager::onResumeRequested (this=%p, appId=%s)", this, qPrintable(appId));
696-
697 Application *application = findApplication(appId);
698+ DLOG("ApplicationManager::onResumeRequested (this=%p, application=%p, appId=%s)", this, application, qPrintable(appId));
699+
700
701 if (!application) {
702 DLOG("ApplicationManager::onResumeRequested: No such running application '%s'", qPrintable(appId));
703@@ -452,6 +530,7 @@
704 if (app->state() == Application::Starting
705 && m_taskController->appIdHasProcessId(app->appId(), pid)) {
706 app->setPid(pid);
707+ DLOG("ApplicationManager::authorizeSession - connecting: application=%p and pid=%lld", app, pid);
708 authorized = true;
709 return;
710 }
711@@ -463,41 +542,35 @@
712 * notify shell it is starting an application and so shell should allow it. Also reads
713 * the --stage parameter to determine the desired stage
714 */
715- QFile cmdline(QString("/proc/%1/cmdline").arg(pid));
716- if (!cmdline.open(QIODevice::ReadOnly | QIODevice::Text)) {
717+ std::unique_ptr<ProcInfo::CommandLine> info = m_procInfo->command_line(pid);
718+ if (!info) {
719 DLOG("ApplicationManager REJECTED connection from app with pid %lld as unable to read process command", pid);
720 return;
721 }
722
723- QByteArray command = cmdline.readLine().replace('\0', ' ');
724-
725- // FIXME: special exception for the OSK - maliit-server - not very secure
726- if (command.startsWith("maliit-server") || command.startsWith("/usr/lib/arm-linux-gnueabihf/qt5/libexec/QtWebProcess")
727- || command.startsWith("/usr/bin/signon-ui")) {
728+ if (info->starts_with("maliit-server") || info->starts_with("/usr/lib/arm-linux-gnueabihf/qt5/libexec/QtWebProcess")
729+ || info->starts_with("/usr/bin/signon-ui")) {
730 authorized = true;
731 m_fenceNext = true;
732 return;
733 }
734
735- QString pattern = QRegularExpression::escape("--desktop_file_hint=") + "(\\S+)";
736- QRegularExpression regExp(pattern);
737- QRegularExpressionMatch regExpMatch = regExp.match(command);
738+ boost::optional<QString> desktopFileName{ info->get_parameter("--desktop_file_hint=") };
739
740- if (!regExpMatch.hasMatch()) {
741+ if (!desktopFileName) {
742 LOG("ApplicationManager REJECTED connection from app with pid %lld as no desktop_file_hint specified", pid);
743 return;
744 }
745
746- QString desktopFileName = regExpMatch.captured(1);
747- DLOG("Process supplied desktop_file_hint, loading '%s'", desktopFileName.toLatin1().data());
748+ DLOG("Process supplied desktop_file_hint, loading '%s'", desktopFileName.get().toLatin1().data());
749
750 // FIXME: right now we support --desktop_file_hint=appId for historical reasons. So let's try that in
751 // case we didn't get an existing .desktop file path
752 DesktopFileReader* desktopData;
753- if (QFileInfo(desktopFileName).exists()) {
754- desktopData = m_desktopFileReaderFactory->createInstanceForDesktopFile(QFileInfo(desktopFileName));
755+ if (QFileInfo(desktopFileName.get()).exists()) {
756+ desktopData = m_desktopFileReaderFactory->createInstanceForDesktopFile(QFileInfo(desktopFileName.get()));
757 } else {
758- desktopData = m_desktopFileReaderFactory->createInstanceForAppId(desktopFileName);
759+ desktopData = m_desktopFileReaderFactory->createInstanceForAppId(desktopFileName.get());
760 }
761
762 if (!desktopData->loaded()) {
763@@ -521,18 +594,15 @@
764
765 // if stage supplied in CLI, fetch that
766 Application::Stage stage = Application::MainStage;
767- pattern = QRegularExpression::escape("--stage_hint=") + "(\\S+)";
768- regExp.setPattern(pattern);
769- regExpMatch = regExp.match(command);
770+ boost::optional<QString> stageParam = info->get_parameter("--stage_hint=");
771
772- if (regExpMatch.hasMatch() && regExpMatch.captured(1) == "side_stage") {
773+ if (stageParam && stageParam.get() == "side_stage") {
774 stage = Application::SideStage;
775 }
776
777 DLOG("Existing process with pid %lld appeared, adding '%s' to application lists", pid, desktopData->name().toLatin1().data());
778
779- QString argStr(command.data());
780- QStringList arguments(argStr.split(' '));
781+ QStringList arguments(info->as_string_list());
782 application = new Application(m_taskController, desktopData, Application::Starting, arguments, this);
783 application->setPid(pid);
784 application->setStage(stage);
785@@ -542,9 +612,9 @@
786
787 void ApplicationManager::placeSession(msh::Session const* session, uint32_t &x, uint32_t &y)
788 {
789- DLOG("ApplicationManager::placeSession (this=%p, session=%p)", this, session);
790+ Application* application = findApplicationWithSession(session);
791+ DLOG("ApplicationManager::placeSession (this=%p, application=%p, session=%p, name=%s)", this, application, session, session?(session->name().c_str()):"null");
792
793- Application* application = findApplicationWithSession(session);
794
795 // Application defaults
796 x = 0;
797@@ -569,7 +639,7 @@
798
799 void ApplicationManager::onSessionStarting(std::shared_ptr<msh::Session> const& session)
800 {
801- DLOG("ApplicationManager::onSessionStarting (this=%p, application=%s)", this, session->name().c_str());
802+ DLOG("ApplicationManager::onSessionStarting (this=%p, application=%s)", this, session?session->name().c_str():"null");
803
804 if (m_fenceNext) {
805 m_fenceNext = false;
806@@ -592,10 +662,11 @@
807
808 void ApplicationManager::onSessionStopping(std::shared_ptr<msh::Session> const& session)
809 {
810- DLOG("ApplicationManager::onSessionStopping (this=%p, application=%s)", this, session->name().c_str());
811-
812 // in case application closed not by hand of shell, check again here:
813 Application* application = findApplicationWithSession(session);
814+
815+ DLOG("ApplicationManager::onSessionStopping (this=%p, application=%p, appId=%s, session name=%s)", this, application, application?qPrintable(application->appId()):"null", session?session->name().c_str():"null");
816+
817 if (application) {
818 bool removeApplication = true;
819
820@@ -604,7 +675,7 @@
821 application->setSession(nullptr);
822 m_dbusWindowStack->WindowDestroyed(0, application->appId());
823 if (application != m_focusedApplication) {
824- removeApplication = false;
825+ removeApplication = false;
826 }
827 }
828
829@@ -626,8 +697,8 @@
830
831 void ApplicationManager::onSessionFocused(std::shared_ptr<msh::Session> const& session)
832 {
833- DLOG("ApplicationManager::onSessionFocused (this=%p, session=%p)", this, session.get());
834 Application* application = findApplicationWithSession(session);
835+ DLOG("ApplicationManager::onSessionFocused (this=%p, application=%p, appId=%s, session name=%s)", this, application, application?qPrintable(application->appId()):"null", session?session->name().c_str():"null");
836
837 // Don't give application focus until it has created it's surface, when it is set as state "Running"
838 // and only notify shell of focus changes that it actually expects
839@@ -649,7 +720,7 @@
840
841 void ApplicationManager::onSessionUnfocused()
842 {
843- DLOG("ApplicationManager::onSessionUnfocused (this=%p)", this);
844+ DLOG("ApplicationManager::onSessionUnfocused (this=%p, application=%p)", this, m_focusedApplication);
845 if (NULL != m_focusedApplication) {
846 Q_ASSERT(m_focusedApplication->focused());
847 m_focusedApplication->setFocused(false);
848@@ -683,7 +754,7 @@
849
850 void ApplicationManager::setFocused(Application *application)
851 {
852- DLOG("ApplicationManager::setFocused (appId=%s)", qPrintable(application->appId()));
853+ DLOG("ApplicationManager::setFocused (application=%p, appId=%s)", application, qPrintable(application->appId()));
854
855 if (application == m_focusedApplication)
856 return;
857@@ -769,6 +840,11 @@
858 DASSERT(application != NULL);
859 DLOG("ApplicationManager::remove (this=%p, application='%s')", this, qPrintable(application->name()));
860
861+ if (application == m_sideStageApplication)
862+ m_sideStageApplication = nullptr;
863+ if (application == m_mainStageApplication)
864+ m_mainStageApplication = nullptr;
865+
866 int i = m_applications.indexOf(application);
867 if (i != -1) {
868 beginRemoveRows(QModelIndex(), i, i);
869@@ -803,3 +879,14 @@
870
871 return QModelIndex();
872 }
873+
874+bool ApplicationManager::checkFocusOnRemovedApplication(Application * application)
875+{
876+ if (application == m_focusedApplication) {
877+ // TODO(greyback) What to do?? Focus next app, or unfocus everything??
878+ m_focusedApplication = nullptr;
879+ Q_EMIT focusedApplicationIdChanged();
880+ return true;
881+ }
882+ return false;
883+}
884
885=== modified file 'src/modules/Unity/Application/application_manager.h'
886--- src/modules/Unity/Application/application_manager.h 2014-01-27 11:29:44 +0000
887+++ src/modules/Unity/Application/application_manager.h 2014-02-19 21:10:08 +0000
888@@ -38,6 +38,7 @@
889 class DBusWindowStack;
890 class MirSurfaceManager;
891 class TaskController;
892+class ProcInfo;
893
894 namespace mir {
895 namespace geometry {
896@@ -46,6 +47,7 @@
897 namespace shell {
898 class Session;
899 class Surface;
900+ class FocusController;
901 }
902 }
903
904@@ -55,6 +57,11 @@
905 Q_FLAGS(ExecFlags)
906
907 public:
908+ class Factory
909+ {
910+ public:
911+ QSharedPointer<ApplicationManager> create();
912+ };
913 // Mapping enums to Ubuntu Platform API enums.
914 enum Flag {
915 NoFlag = 0x0,
916@@ -66,6 +73,9 @@
917
918 explicit ApplicationManager(const QSharedPointer<TaskController>& taskController,
919 const QSharedPointer<DesktopFileReader::Factory>& desktopFileReaderFactory,
920+ const QSharedPointer<ProcInfo>& processInfo,
921+ std::shared_ptr<mir::shell::FocusController> const& controller,
922+ QSize const& displaySize,
923 QObject *parent = 0);
924 virtual ~ApplicationManager();
925
926@@ -122,6 +132,9 @@
927 Application* findLastExecutedApplication();
928 Application* applicationForStage(Application::Stage stage);
929 QModelIndex findIndex(Application* application);
930+ bool checkFocusOnRemovedApplication(Application* application);
931+ void shutdownApplication(Application* application);
932+ void stopStartingApplication(const QString &appId);
933
934 QList<Application*> m_applications;
935 Application* m_focusedApplication; // remove as Mir has API for this
936@@ -130,11 +143,11 @@
937 Application* m_msApplicationToBeFocused; // placeholder store for async focusing
938 Application* m_ssApplicationToBeFocused; // placeholder store for async focusing
939 QStringList m_lifecycleExceptions;
940- ShellServerConfiguration* m_mirServer;
941+ std::shared_ptr<mir::shell::FocusController> m_focusController;
942 DBusWindowStack* m_dbusWindowStack;
943 QSharedPointer<TaskController> m_taskController;
944 QSharedPointer<DesktopFileReader::Factory> m_desktopFileReaderFactory;
945- static ApplicationManager* the_application_manager;
946+ QSharedPointer<ProcInfo> m_procInfo;
947 int m_gridUnitPx;
948 bool m_fenceNext;
949 QSize m_displaySize;
950
951=== added file 'src/modules/Unity/Application/proc_info.cpp'
952--- src/modules/Unity/Application/proc_info.cpp 1970-01-01 00:00:00 +0000
953+++ src/modules/Unity/Application/proc_info.cpp 2014-02-19 21:10:08 +0000
954@@ -0,0 +1,52 @@
955+/*
956+ * Copyright (C) 2014 Canonical, Ltd.
957+ *
958+ * This program is free software: you can redistribute it and/or modify it under
959+ * the terms of the GNU Lesser General Public License version 3, as published by
960+ * the Free Software Foundation.
961+ *
962+ * This program is distributed in the hope that it will be useful, but WITHOUT
963+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
964+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
965+ * Lesser General Public License for more details.
966+ *
967+ * You should have received a copy of the GNU Lesser General Public License
968+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
969+ */
970+
971+#include "proc_info.h"
972+
973+#include <QFile>
974+#include <QRegularExpression>
975+
976+ProcInfo::~ProcInfo() {
977+}
978+
979+std::unique_ptr<ProcInfo::CommandLine> ProcInfo::command_line(quint64 pid) {
980+ QFile cmdline(QString("/proc/%1/cmdline").arg(pid));
981+ if (!cmdline.open(QIODevice::ReadOnly | QIODevice::Text)) {
982+ return nullptr;
983+ }
984+
985+ return std::unique_ptr<CommandLine>(new CommandLine{ cmdline.readLine().replace('\0', ' ') });
986+}
987+QStringList ProcInfo::CommandLine::as_string_list() const {
988+ return QString(command.data()).split(' ');
989+}
990+
991+bool ProcInfo::CommandLine::starts_with(char const* prefix) const {
992+ return command.startsWith(prefix);
993+}
994+
995+boost::optional<QString> ProcInfo::CommandLine::get_parameter(const char* name) const {
996+ QString pattern = QRegularExpression::escape(name) + "(\\S+)";
997+ QRegularExpression regExp(pattern);
998+ QRegularExpressionMatch regExpMatch = regExp.match(command);
999+
1000+ if (!regExpMatch.hasMatch()) {
1001+ return boost::optional<QString>{};
1002+ }
1003+
1004+ return boost::optional<QString>{regExpMatch.captured(1)};
1005+}
1006+
1007
1008=== added file 'src/modules/Unity/Application/proc_info.h'
1009--- src/modules/Unity/Application/proc_info.h 1970-01-01 00:00:00 +0000
1010+++ src/modules/Unity/Application/proc_info.h 2014-02-19 21:10:08 +0000
1011@@ -0,0 +1,48 @@
1012+/*
1013+ * Copyright (C) 2014 Canonical, Ltd.
1014+ *
1015+ * This program is free software: you can redistribute it and/or modify it under
1016+ * the terms of the GNU Lesser General Public License version 3, as published by
1017+ * the Free Software Foundation.
1018+ *
1019+ * This program is distributed in the hope that it will be useful, but WITHOUT
1020+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1021+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1022+ * Lesser General Public License for more details.
1023+ *
1024+ * You should have received a copy of the GNU Lesser General Public License
1025+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1026+ */
1027+
1028+// Process Information
1029+
1030+#ifndef PROC_INFO_H
1031+#define PROC_INFO_H
1032+
1033+#include <memory>
1034+
1035+#include <boost/optional.hpp>
1036+
1037+#include <QByteArray>
1038+#include <QStringList>
1039+
1040+class QString;
1041+
1042+class ProcInfo
1043+{
1044+public:
1045+ class CommandLine
1046+ {
1047+ public:
1048+ QByteArray command;
1049+
1050+ bool starts_with(const char* prefix) const;
1051+ boost::optional<QString> get_parameter(const char* name) const;
1052+ QStringList as_string_list() const;
1053+ };
1054+ virtual std::unique_ptr<CommandLine> command_line(quint64 pid);
1055+ virtual ~ProcInfo();
1056+};
1057+
1058+#endif
1059+
1060
1061=== modified file 'src/unity-mir/CMakeLists.txt'
1062--- src/unity-mir/CMakeLists.txt 2014-02-07 16:10:12 +0000
1063+++ src/unity-mir/CMakeLists.txt 2014-02-19 21:10:08 +0000
1064@@ -74,12 +74,10 @@
1065
1066 ${CMAKE_THREAD_LIBS_INIT}
1067
1068- ${UBUNTU_PLATFORM_API_LIBRARIES}
1069- ${MIRCOMMON_LIBRARIES}
1070- ${MIRSERVER_LIBRARIES}
1071+ ${UBUNTU_PLATFORM_API_LDFLAGS}
1072+ ${MIRSERVER_LDFLAGS}
1073 ${PROTOBUF_LIBRARIES}
1074-
1075- boost_system
1076+ ${Boost_SYSTEM_LIBRARY_RELEASE}
1077
1078 ubuntu_application_api_mirserver)
1079
1080
1081=== modified file 'tests/CMakeLists.txt'
1082--- tests/CMakeLists.txt 2014-01-27 11:29:44 +0000
1083+++ tests/CMakeLists.txt 2014-02-19 21:10:08 +0000
1084@@ -20,6 +20,7 @@
1085 ${CMAKE_SOURCE_DIR}/src/modules
1086 ${GMOCK_INCLUDE_DIR}
1087 ${GTEST_INCLUDE_DIR}
1088+ ${MIRSERVER_INCLUDE_DIRS}
1089 )
1090
1091 add_executable(
1092
1093=== modified file 'tests/application_manager_test.cpp'
1094--- tests/application_manager_test.cpp 2014-01-27 11:29:44 +0000
1095+++ tests/application_manager_test.cpp 2014-02-19 21:10:08 +0000
1096@@ -19,6 +19,7 @@
1097
1098 #include <Unity/Application/applicationcontroller.h>
1099 #include <Unity/Application/taskcontroller.h>
1100+#include <Unity/Application/proc_info.h>
1101
1102 #include <core/posix/linux/proc/process/oom_score_adj.h>
1103
1104@@ -29,43 +30,54 @@
1105 #include "mock_desktop_file_reader.h"
1106 #include "mock_oom_controller.h"
1107 #include "mock_process_controller.h"
1108-
1109-TEST(ApplicationManager, SuspendingAndResumingARunningApplicationResultsInOomScoreAdjustment)
1110+#include "mock_proc_info.h"
1111+#include "mock_session.h"
1112+#include "mock_focus_controller.h"
1113+
1114+
1115+class ApplicationManagerTests : public ::testing::Test
1116 {
1117- using namespace ::testing;
1118-
1119- const QString appId("com.canonical.does.not.exist");
1120-
1121- NiceMock<testing::MockOomController> oomController;
1122- QSharedPointer<ProcessController::OomController> oomControllerPtr(
1123+public:
1124+ ApplicationManagerTests()
1125+ : processController{
1126+ QSharedPointer<ProcessController::OomController> (
1127 &oomController,
1128- [](ProcessController::OomController*){});
1129-
1130- NiceMock<testing::MockProcessController> processController(oomControllerPtr);
1131- QSharedPointer<ProcessController> processControllerPtr(
1132- &processController,
1133- [](ProcessController*){});
1134-
1135- NiceMock<testing::MockApplicationController> appController;
1136- QSharedPointer<ApplicationController> appControllerPtr(
1137- &appController,
1138- [](ApplicationController*){});
1139-
1140- QSharedPointer<TaskController> taskController(
1141+ [](ProcessController::OomController*){})
1142+ },
1143+ applicationManager{
1144+ QSharedPointer<TaskController>{
1145 new TaskController(
1146 nullptr,
1147- appControllerPtr,
1148- processControllerPtr
1149- ));
1150-
1151- NiceMock<MockDesktopFileReaderFactory> desktopFileReaderFactory;
1152- QSharedPointer<DesktopFileReader::Factory> desktopFileReaderFactoryPtr(
1153+ QSharedPointer<ApplicationController>(
1154+ &appController,
1155+ [](ApplicationController*){}),
1156+ QSharedPointer<ProcessController>(
1157+ &processController,
1158+ [](ProcessController*){})
1159+ )},
1160+ QSharedPointer<DesktopFileReader::Factory>(
1161 &desktopFileReaderFactory,
1162- [](DesktopFileReader::Factory*){});
1163-
1164- ApplicationManager applicationManager(
1165- taskController,
1166- desktopFileReaderFactoryPtr);
1167+ [](DesktopFileReader::Factory*){}),
1168+ QSharedPointer<ProcInfo>(&procInfo,[](ProcInfo *){}),
1169+ std::shared_ptr<mir::shell::FocusController>(&focusController, [](void*){}),
1170+ QSize(400,400)
1171+ }
1172+ {
1173+ }
1174+ testing::NiceMock<testing::MockOomController> oomController;
1175+ testing::NiceMock<testing::MockProcessController> processController;
1176+ testing::NiceMock<testing::MockApplicationController> appController;
1177+ testing::NiceMock<testing::MockProcInfo> procInfo;
1178+ testing::NiceMock<testing::MockDesktopFileReaderFactory> desktopFileReaderFactory;
1179+ testing::NiceMock<testing::MockFocusController> focusController;
1180+ ApplicationManager applicationManager;
1181+};
1182+
1183+TEST_F(ApplicationManagerTests, SuspendingAndResumingARunningApplicationResultsInOomScoreAdjustment)
1184+{
1185+ using namespace ::testing;
1186+
1187+ const QString appId("com.canonical.does.not.exist");
1188
1189 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(_, _)).Times(1);
1190
1191@@ -86,43 +98,12 @@
1192 }
1193
1194 // Currently disabled as we need to make sure that we have a corresponding mir session, too.
1195-TEST(ApplicationManager, DISABLED_FocusingRunningApplicationResultsInOomScoreAdjustment)
1196+TEST_F(ApplicationManagerTests, DISABLED_FocusingRunningApplicationResultsInOomScoreAdjustment)
1197 {
1198 using namespace ::testing;
1199
1200 const QString appId("com.canonical.does.not.exist");
1201
1202- NiceMock<testing::MockOomController> oomController;
1203- QSharedPointer<ProcessController::OomController> oomControllerPtr(
1204- &oomController,
1205- [](ProcessController::OomController*){});
1206-
1207- NiceMock<testing::MockProcessController> processController(oomControllerPtr);
1208- QSharedPointer<ProcessController> processControllerPtr(
1209- &processController,
1210- [](ProcessController*){});
1211-
1212- NiceMock<testing::MockApplicationController> appController;
1213- QSharedPointer<ApplicationController> appControllerPtr(
1214- &appController,
1215- [](ApplicationController*){});
1216-
1217- QSharedPointer<TaskController> taskController(
1218- new TaskController(
1219- nullptr,
1220- appControllerPtr,
1221- processControllerPtr
1222- ));
1223-
1224- NiceMock<MockDesktopFileReaderFactory> desktopFileReaderFactory;
1225- QSharedPointer<DesktopFileReader::Factory> desktopFileReaderFactoryPtr(
1226- &desktopFileReaderFactory,
1227- [](DesktopFileReader::Factory*){});
1228-
1229- ApplicationManager applicationManager(
1230- taskController,
1231- desktopFileReaderFactoryPtr);
1232-
1233 QSet<QString> appIds;
1234
1235 for (unsigned int i = 0; i < 50; i++)
1236@@ -135,6 +116,9 @@
1237 ApplicationManager::NoFlag,
1238 QStringList());
1239
1240+ std::shared_ptr<mir::shell::Session> mirSession = std::make_shared<MockSession>(appIdFormat.toStdString(), i);
1241+ applicationManager.onSessionStarting( mirSession );
1242+
1243 EXPECT_NE(nullptr, application);
1244
1245 appIds.insert(appId);
1246@@ -149,3 +133,92 @@
1247 applicationManager.focusApplication(appId);
1248 }
1249 }
1250+
1251+
1252+TEST_F(ApplicationManagerTests,bug_case_1240400_second_dialer_app_fails_to_authorize_and_gets_mixed_up_with_first_one)
1253+{
1254+ using namespace ::testing;
1255+ std::shared_ptr<mir::shell::Surface> aSurface(nullptr);
1256+ quint64 firstProcId = 5921;
1257+ quint64 secondProcId = 5922;
1258+ const char dialer_app_id[] = "dialer-app";
1259+ QByteArray cmdLine( "/usr/bin/dialer-app --desktop_file_hint=dialer-app");
1260+ QByteArray secondcmdLine( "/usr/bin/dialer-app");
1261+
1262+ EXPECT_CALL(procInfo,command_line_(firstProcId))
1263+ .Times(1)
1264+ .WillOnce(Return(cmdLine));
1265+ EXPECT_CALL(procInfo,command_line_(secondProcId))
1266+ .Times(1)
1267+ .WillOnce(Return(secondcmdLine));
1268+
1269+ bool authed = true;
1270+
1271+ std::shared_ptr<mir::shell::Session> mirSession = std::make_shared<MockSession>(dialer_app_id, firstProcId);
1272+ applicationManager.authorizeSession(firstProcId, authed);
1273+ EXPECT_EQ(true, authed);
1274+ applicationManager.onSessionStarting(mirSession);
1275+ applicationManager.onSessionCreatedSurface(mirSession.get(),aSurface);
1276+ Application * app = applicationManager.findApplication(dialer_app_id);
1277+ EXPECT_NE(nullptr,app);
1278+
1279+ // now a second session without desktop file is launched:
1280+ applicationManager.authorizeSession(secondProcId, authed);
1281+ applicationManager.onProcessStartReportReceived(dialer_app_id, true);
1282+
1283+ EXPECT_EQ(false,authed);
1284+ EXPECT_EQ(app,applicationManager.findApplication(dialer_app_id));
1285+ EXPECT_EQ(QString(dialer_app_id),applicationManager.focusedApplicationId());
1286+}
1287+
1288+TEST_F(ApplicationManagerTests,application_dies_while_starting)
1289+{
1290+ using namespace ::testing;
1291+ quint64 procId = 5921;
1292+ const char app_id[] = "my-app";
1293+ QByteArray cmdLine( "/usr/bin/my-app --desktop_file_hint=my-app");
1294+
1295+ EXPECT_CALL(procInfo,command_line_(procId))
1296+ .Times(1)
1297+ .WillOnce(Return(cmdLine));
1298+
1299+ bool authed = true;
1300+
1301+ std::shared_ptr<mir::shell::Session> mirSession = std::make_shared<MockSession>(app_id, procId);
1302+ applicationManager.authorizeSession(procId, authed);
1303+ applicationManager.onSessionStarting(mirSession);
1304+ Application * beforeFailure = applicationManager.findApplication(app_id);
1305+ applicationManager.onProcessStartReportReceived(app_id,true);
1306+ Application * afterFailure = applicationManager.findApplication(app_id);
1307+
1308+ EXPECT_EQ(true, authed);
1309+ EXPECT_NE(nullptr, beforeFailure);
1310+ EXPECT_EQ(nullptr, afterFailure);
1311+}
1312+
1313+TEST_F(ApplicationManagerTests,application_start_failure_after_starting)
1314+{
1315+ using namespace ::testing;
1316+ quint64 procId = 5921;
1317+ std::shared_ptr<mir::shell::Surface> aSurface(nullptr);
1318+ const char app_id[] = "my-app";
1319+ QByteArray cmdLine( "/usr/bin/my-app --desktop_file_hint=my-app");
1320+
1321+ EXPECT_CALL(procInfo,command_line_(procId))
1322+ .Times(1)
1323+ .WillOnce(Return(cmdLine));
1324+
1325+ bool authed = true;
1326+
1327+ std::shared_ptr<mir::shell::Session> mirSession = std::make_shared<MockSession>(app_id, procId);
1328+ applicationManager.authorizeSession(procId, authed);
1329+ applicationManager.onSessionStarting(mirSession);
1330+ Application * beforeFailure = applicationManager.findApplication(app_id);
1331+ applicationManager.onSessionCreatedSurface(mirSession.get(), aSurface);
1332+ applicationManager.onProcessStartReportReceived(app_id, true);
1333+ Application * afterFailure = applicationManager.findApplication(app_id);
1334+
1335+ EXPECT_EQ(true, authed);
1336+ EXPECT_NE(nullptr, beforeFailure);
1337+ EXPECT_EQ(beforeFailure, afterFailure);
1338+}
1339
1340=== modified file 'tests/auto/modules/Unity/Application/CMakeLists.txt'
1341--- tests/auto/modules/Unity/Application/CMakeLists.txt 2014-01-27 11:29:44 +0000
1342+++ tests/auto/modules/Unity/Application/CMakeLists.txt 2014-02-19 21:10:08 +0000
1343@@ -19,8 +19,8 @@
1344 unity-mir
1345 unityapplicationplugin
1346
1347- ${MIRSERVER_LIBRARIES}
1348- ${UPSTART_APP_LAUNCH_LIBRARIES})
1349+ ${MIRSERVER_LDFLAGS}
1350+ ${UPSTART_APP_LAUNCH_LDFLAGS})
1351
1352 install(
1353 TARGETS unity-mir-test-app
1354
1355=== modified file 'tests/auto/modules/Unity/Application/main.cpp'
1356--- tests/auto/modules/Unity/Application/main.cpp 2014-01-27 11:29:44 +0000
1357+++ tests/auto/modules/Unity/Application/main.cpp 2014-02-19 21:10:08 +0000
1358@@ -18,6 +18,7 @@
1359
1360 #include "application_manager.h"
1361 #include "processcontroller.h"
1362+#include "proc_info.h"
1363 #include "taskcontroller.h"
1364 #include "upstart/applicationcontroller.h"
1365
1366@@ -44,12 +45,10 @@
1367
1368 void ApplicationManagerTests::testStartStop()
1369 {
1370- QSharedPointer<upstart::ApplicationController> appController(new upstart::ApplicationController());
1371- QSharedPointer<TaskController> taskController(new TaskController(nullptr, appController));
1372- QSharedPointer<DesktopFileReader::Factory> fileReaderFactory(new DesktopFileReader::Factory());
1373+ ApplicationManager::Factory appFactory;
1374+ QSharedPointer<ApplicationManager> manager{appFactory.create()};
1375
1376- ApplicationManager manager(taskController, fileReaderFactory);
1377- Application *app = manager.startApplication("unity-mir-test-helper-app", QStringList());
1378+ Application *app = manager->startApplication("unity-mir-test-helper-app", QStringList());
1379 QVERIFY(app);
1380 QCOMPARE(app->desktopFile(), QString("/usr/share/applications/unity-mir-test-helper-app.desktop"));
1381 QCOMPARE(app->name(), QString("My Fake App"));
1382@@ -61,7 +60,7 @@
1383 QString simplifiedCommand = pidCommandLine(app->pid());
1384 QCOMPARE(simplifiedCommand, QString("unity-mir-test-helper-app"));
1385
1386- manager.stopApplication(app->appId());
1387+ manager->stopApplication(app->appId());
1388
1389 QDir d;
1390 QTRY_VERIFY(!d.exists(QString("/proc/%1").arg(app->pid())));
1391
1392=== added file 'tests/mock_focus_controller.h'
1393--- tests/mock_focus_controller.h 1970-01-01 00:00:00 +0000
1394+++ tests/mock_focus_controller.h 2014-02-19 21:10:08 +0000
1395@@ -0,0 +1,37 @@
1396+/*
1397+ * Copyright (C) 2014 Canonical, Ltd.
1398+ *
1399+ * This program is free software: you can redistribute it and/or modify it under
1400+ * the terms of the GNU Lesser General Public License version 3, as published by
1401+ * the Free Software Foundation.
1402+ *
1403+ * This program is distributed in the hope that it will be useful, but WITHOUT
1404+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1405+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1406+ * Lesser General Public License for more details.
1407+ *
1408+ * You should have received a copy of the GNU Lesser General Public License
1409+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1410+ *
1411+ */
1412+
1413+#ifndef MOCK_MIR_SHELL_FOCUS_CONTROLLER_H
1414+#define MOCK_MIR_SHELL_FOCUS_CONTROLLER_H
1415+
1416+#include <mir/shell/focus_controller.h>
1417+#include <gmock/gmock.h>
1418+
1419+#include <string>
1420+
1421+namespace testing
1422+{
1423+class MockFocusController : public mir::shell::FocusController
1424+{
1425+public:
1426+ MOCK_METHOD0(focus_next, void());
1427+ MOCK_CONST_METHOD0(focussed_application, std::weak_ptr<mir::shell::Session>());
1428+ MOCK_METHOD1(set_focus_to, void(std::shared_ptr<mir::shell::Session>const&));
1429+};
1430+}
1431+
1432+#endif // MOCK_MIR_SHELL_FOCUS_CONTROLLER_H_
1433
1434=== added file 'tests/mock_proc_info.h'
1435--- tests/mock_proc_info.h 1970-01-01 00:00:00 +0000
1436+++ tests/mock_proc_info.h 2014-02-19 21:10:08 +0000
1437@@ -0,0 +1,37 @@
1438+/*
1439+ * Copyright (C) 2014 Canonical, Ltd.
1440+ *
1441+ * This program is free software: you can redistribute it and/or modify it under
1442+ * the terms of the GNU Lesser General Public License version 3, as published by
1443+ * the Free Software Foundation.
1444+ *
1445+ * This program is distributed in the hope that it will be useful, but WITHOUT
1446+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1447+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1448+ * Lesser General Public License for more details.
1449+ *
1450+ * You should have received a copy of the GNU Lesser General Public License
1451+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1452+ *
1453+ */
1454+
1455+#ifndef MOCK_PROC_INFO_H
1456+#define MOCK_PROC_INFO_H
1457+
1458+#include <Unity/Application/proc_info.h>
1459+
1460+#include <gmock/gmock.h>
1461+
1462+namespace testing
1463+{
1464+struct MockProcInfo : public ProcInfo
1465+{
1466+ MOCK_METHOD1(command_line_, QByteArray(quint64));
1467+ std::unique_ptr<CommandLine> command_line(quint64 pid)
1468+ {
1469+ return std::unique_ptr<CommandLine>(new CommandLine{command_line_(pid)});
1470+ }
1471+};
1472+}
1473+
1474+#endif // MOCK_OOM_CONTROLLER_H
1475
1476=== added file 'tests/mock_session.h'
1477--- tests/mock_session.h 1970-01-01 00:00:00 +0000
1478+++ tests/mock_session.h 2014-02-19 21:10:08 +0000
1479@@ -0,0 +1,69 @@
1480+/*
1481+ * Copyright (C) 2014 Canonical, Ltd.
1482+ *
1483+ * This program is free software: you can redistribute it and/or modify it under
1484+ * the terms of the GNU Lesser General Public License version 3, as published by
1485+ * the Free Software Foundation.
1486+ *
1487+ * This program is distributed in the hope that it will be useful, but WITHOUT
1488+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1489+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1490+ * Lesser General Public License for more details.
1491+ *
1492+ * You should have received a copy of the GNU Lesser General Public License
1493+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1494+ *
1495+ */
1496+
1497+#ifndef MOCK_MIR_SHELL_SESSION_H
1498+#define MOCK_MIR_SHELL_SESSION_H
1499+
1500+#include <mir/shell/session.h>
1501+#include <mir/graphics/display_configuration.h>
1502+#include <mir/shell/surface_creation_parameters.h>
1503+#include <gmock/gmock.h>
1504+
1505+#include <string>
1506+
1507+namespace testing
1508+{
1509+struct MockSession : public mir::shell::Session
1510+{
1511+ MockSession() {}
1512+ MockSession(std::string const& sessionName, pid_t processId)
1513+ : m_sessionName(sessionName), m_sessionId(processId)
1514+ {}
1515+
1516+ std::string name() const override
1517+ {
1518+ return m_sessionName;
1519+ }
1520+
1521+ pid_t process_id() const override
1522+ {
1523+ return m_sessionId;
1524+ }
1525+
1526+ typedef mir::frontend::SurfaceId SurfaceId;
1527+
1528+ MOCK_METHOD0(force_requests_to_complete, void());
1529+
1530+ MOCK_CONST_METHOD0(default_surface, std::shared_ptr<mir::shell::Surface>());
1531+ MOCK_CONST_METHOD1(get_surface, std::shared_ptr<mir::frontend::Surface>(SurfaceId));
1532+
1533+ MOCK_METHOD1(take_snapshot, void(mir::shell::SnapshotCallback const&));
1534+ MOCK_METHOD1(set_lifecycle_state, void(MirLifecycleState));
1535+ MOCK_METHOD1(create_surface, SurfaceId(mir::shell::SurfaceCreationParameters const&));
1536+ MOCK_METHOD1(destroy_surface, void (SurfaceId));
1537+
1538+ MOCK_METHOD0(hide, void());
1539+ MOCK_METHOD0(show, void());
1540+ MOCK_METHOD1(send_display_config, void(mir::graphics::DisplayConfiguration const&));
1541+ MOCK_METHOD3(configure_surface, int(SurfaceId, MirSurfaceAttrib, int));
1542+private:
1543+ std::string m_sessionName;
1544+ pid_t m_sessionId;
1545+};
1546+}
1547+
1548+#endif // MOCK_MIR_SHELL_SESSION_H

Subscribers

People subscribed via source and target branches