Merge lp:~gerboland/unity-mir/shortAppIds-0.1.9 into lp:unity-mir

Proposed by Gerry Boland
Status: Merged
Merged at revision: 220
Proposed branch: lp:~gerboland/unity-mir/shortAppIds-0.1.9
Merge into: lp:unity-mir
Diff against target: 903 lines (+312/-156)
14 files modified
src/modules/Unity/Application/application.cpp (+10/-2)
src/modules/Unity/Application/application.h (+2/-0)
src/modules/Unity/Application/application_manager.cpp (+55/-14)
src/modules/Unity/Application/applicationcontroller.h (+3/-0)
src/modules/Unity/Application/desktopfilereader.cpp (+6/-47)
src/modules/Unity/Application/desktopfilereader.h (+2/-5)
src/modules/Unity/Application/taskcontroller.cpp (+5/-0)
src/modules/Unity/Application/taskcontroller.h (+1/-0)
src/modules/Unity/Application/upstart/applicationcontroller.cpp (+92/-10)
src/modules/Unity/Application/upstart/applicationcontroller.h (+7/-5)
tests/application_manager_test.cpp (+85/-3)
tests/mock_application_controller.h (+11/-0)
tests/mock_desktop_file_reader.h (+31/-70)
tests/taskcontroller_test.cpp (+2/-0)
To merge this branch: bzr merge lp:~gerboland/unity-mir/shortAppIds-0.1.9
Reviewer Review Type Date Requested Status
Michael Zanetti (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+219377@code.launchpad.net

Commit message

Add support for short appIds

Shells should use only short AppIds (i.e. appId without the version string). In order to support this, needed to
- slightly decouple appId from the desktop file path, now need to ask the ApplicationController implementation for the path to the desktop file
- DesktopFileReader now just a dumb file reader
- in the upstart ApplicationController interface & implementation, convert any long appIds to short appIds
- in ApplicationManager, to ease transition, have startApplication support both long & short appIds. But otherwise it uses short appIds only.

Description of the change

Add support for short appIds

Shells should use only short AppIds (i.e. appId without the version string). In order to support this, needed to
- slightly decouple appId from the desktop file path, now need to ask the ApplicationController implementation for the path to the desktop file
- DesktopFileReader now just a dumb file reader
- in the upstart ApplicationController interface & implementation, convert any long appIds to short appIds
- in ApplicationManager, to ease transition, have startApplication support both long & short appIds. But otherwise it uses short appIds only.

To post a comment you must log in.
228. By Gerry Boland

Remove unused header

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

changelog please

review: Needs Fixing
Revision history for this message
Michael Zanetti (mzanetti) wrote :

err... checklist... sorry

Revision history for this message
Gerry Boland (gerboland) wrote :

Checklist:
 * Are there any related MPs required for this MP to build/function as expected? Please list.
lp:~mzanetti/unity8/support-short-appid-in-gsettings
 * Did you perform an exploratory manual test run of your code change and any related functionality?
Yes
 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A

Revision history for this message
Michael Zanetti (mzanetti) wrote :

74 + if (desktopFileName.get().endsWith(".desktop")) {
75 + appId = toShortAppIdIfPossible(desktopFileName.get().left( desktopFileName.get().length() - 8));
76 + } else {
77 + appId = toShortAppIdIfPossible(desktopFileName.get());
78 + }

can there be .desktop files not ending with .desktop?

desktopFileName.remove(QRegExp(".desktop$")) perhaps?

review: Needs Information
Revision history for this message
Gerry Boland (gerboland) wrote :

> can there be .desktop files not ending with .desktop?
Really old legacy support for --desktop_file_hint=appId <- very naughty, maybe I should just remove that support entirely

> desktopFileName.remove(QRegExp(".desktop$")) perhaps?
Oh that's nicer, thanks!

Revision history for this message
Michael Zanetti (mzanetti) wrote :

 * Did you perform an exploratory manual test run of the code change and any related functionality?

Yes

 * Did CI run pass? If not, please explain why.

Yes

review: Approve
229. By Gerry Boland

Correction to "appId guess from desktop file name" code (thanks for suggestion mzanetti)

230. By Gerry Boland

Test appId guess from desktop file name

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
231. By Gerry Boland

Extra test for long appId, fix bug

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

after installing this from silo 005, launching settings through the indicators seems broken.

Revision history for this message
Michael Zanetti (mzanetti) wrote :

go to music scope, open a song's preview and click on "play in music app". doesn't seem to work any more either.

review: Needs Fixing
232. By Gerry Boland

Possible fix for bug where if app updated while running, need to refer to it by old long-appId, not short appId

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

Launching accounts from system settings app seems broken:

ApplicationManager REJECTED connection from app with pid 4025 as desktop_file_hint file not found

review: Needs Fixing
233. By Gerry Boland

Fix shortening of appId - no need to deref pointer

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
234. By Gerry Boland

Fix typo breaking apps launching via command line

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
235. By Gerry Boland

Have AppMan invokable methods gracefully handle long appIds. Do not re-create new Application for existing app

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michael Zanetti (mzanetti) wrote :

Ok. looking good. all test plans passing.

 * Did you perform an exploratory manual test run of the code change and any related functionality?

Yes

 * Did CI run pass? If not, please explain why.

it did!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/modules/Unity/Application/application.cpp'
2--- src/modules/Unity/Application/application.cpp 2014-04-15 14:31:02 +0000
3+++ src/modules/Unity/Application/application.cpp 2014-05-16 12:20:01 +0000
4@@ -52,6 +52,9 @@
5
6 m_suspendTimer->setSingleShot(true);
7 connect(m_suspendTimer, SIGNAL(timeout()), this, SLOT(suspend()));
8+
9+ // FIXME(greyback) need to save long appId internally until upstart-app-launch can hide it from us
10+ m_longAppId = desktopFileReader->file().remove(QRegExp(".desktop$")).split('/').last();
11 }
12
13 Application::~Application()
14@@ -276,13 +279,13 @@
15 void Application::suspend()
16 {
17 DLOG("Application::suspend (this=%p)", this);
18- m_taskController->suspend(appId());
19+ m_taskController->suspend(longAppId());
20 }
21
22 void Application::resume()
23 {
24 DLOG("Application::resume (this=%p)", this);
25- m_taskController->resume(appId());
26+ m_taskController->resume(longAppId());
27 }
28
29 void Application::respawn()
30@@ -291,4 +294,9 @@
31 m_taskController->start(appId(), m_arguments);
32 }
33
34+QString Application::longAppId() const
35+{
36+ return m_longAppId;
37+}
38+
39 } // namespace unitymir
40
41=== modified file 'src/modules/Unity/Application/application.h'
42--- src/modules/Unity/Application/application.h 2014-04-15 14:31:02 +0000
43+++ src/modules/Unity/Application/application.h 2014-05-16 12:20:01 +0000
44@@ -89,6 +89,7 @@
45 void visibleChanged();
46
47 private:
48+ QString longAppId() const;
49 void setPid(pid_t pid);
50 void setState(State state);
51 void setFocused(bool focus);
52@@ -98,6 +99,7 @@
53
54 QSharedPointer<TaskController> m_taskController;
55 DesktopFileReader* m_desktopData;
56+ QString m_longAppId;
57 qint64 m_pid;
58 Stage m_stage;
59 State m_state;
60
61=== modified file 'src/modules/Unity/Application/application_manager.cpp'
62--- src/modules/Unity/Application/application_manager.cpp 2014-04-15 14:31:02 +0000
63+++ src/modules/Unity/Application/application_manager.cpp 2014-05-16 12:20:01 +0000
64@@ -76,6 +76,17 @@
65 );
66 }
67
68+// FIXME: To be removed once shell has fully adopted short appIds!!
69+QString toShortAppIdIfPossible(const QString &appId) {
70+ QRegExp longAppIdMask("[a-z0-9][a-z0-9+.-]+_[a-zA-Z0-9+.-]+_[0-9][a-zA-Z0-9.+:~-]*");
71+ if (longAppIdMask.exactMatch(appId)) {
72+ LOG("WARNING: long App ID encountered: %s", qPrintable(appId));
73+ // input string a long AppId, chop the version string off the end
74+ QStringList parts = appId.split("_");
75+ return QString("%1_%2").arg(parts.first()).arg(parts.at(1));
76+ }
77+ return appId;
78+}
79
80 void connectToSessionListener(ApplicationManager * manager, SessionListener * listener)
81 {
82@@ -262,8 +273,10 @@
83 return m_applications.at(index);
84 }
85
86-Application* ApplicationManager::findApplication(const QString &appId) const
87+Application* ApplicationManager::findApplication(const QString &inputAppId) const
88 {
89+ const QString appId = toShortAppIdIfPossible(inputAppId);
90+
91 for (Application *app : m_applications) {
92 if (app->appId() == appId) {
93 return app;
94@@ -272,8 +285,10 @@
95 return nullptr;
96 }
97
98-bool ApplicationManager::requestFocusApplication(const QString &appId)
99+bool ApplicationManager::requestFocusApplication(const QString &inputAppId)
100 {
101+ const QString appId = toShortAppIdIfPossible(inputAppId);
102+
103 DLOG("ApplicationManager::requestFocusApplication (this=%p, appId=%s)", this, qPrintable(appId));
104 Application *application = findApplication(appId);
105
106@@ -355,8 +370,10 @@
107 application->setState(Application::Running);
108 }
109
110-bool ApplicationManager::focusApplication(const QString &appId)
111+bool ApplicationManager::focusApplication(const QString &inputAppId)
112 {
113+ const QString appId = toShortAppIdIfPossible(inputAppId);
114+
115 Application *application = findApplication(appId);
116 DLOG("ApplicationManager::focusApplication (this=%p, application=%p, appId=%s)", this, application, qPrintable(appId));
117
118@@ -412,9 +429,26 @@
119 return startApplication(appId, NoFlag, arguments);
120 }
121
122-Application *ApplicationManager::startApplication(const QString &appId, ExecFlags flags,
123+/**
124+ * @brief ApplicationManager::startApplication launches an application identified by an "application id" or appId.
125+ *
126+ * Note: due to an implementation detail, appIds come in two forms:
127+ * * long appId: $(click_package)_$(application)_$(version)
128+ * * short appId: $(click_package)_$(application)
129+ * It is expected that the shell uses _only_ short appIds (but long appIds are accepted by this method for legacy
130+ * reasons - but be warned, this ability will be removed)
131+ *
132+ * Unless stated otherwise, we always use short appIds in this API.
133+ *
134+ * @param inputAppId AppId of application to launch (long appId supported)
135+ * @param flags Shell specific flags to modify the initial state of the application
136+ * @param arguments Command line arguments to pass to the application to be launched
137+ * @return Pointer to Application object representing the launched process
138+ */
139+Application *ApplicationManager::startApplication(const QString &inputAppId, ExecFlags flags,
140 const QStringList &arguments)
141 {
142+ QString appId = toShortAppIdIfPossible(inputAppId);
143 DLOG("ApplicationManager::startApplication (this=%p, appId=%s)", this, qPrintable(appId));
144
145 if (!m_taskController->start(appId, arguments)) {
146@@ -425,15 +459,15 @@
147 {
148 Application * application = findApplication(appId);
149 if (application) {
150- DLOG("ApplicationManager::startApplication - application already "
151- "exists: (this=%p, app=%p, appId=%s)",
152+ DLOG("ApplicationManager::startApplication - application already exists: (this=%p, app=%p, appId=%s)",
153 this, application, qPrintable(appId));
154+ return application;
155 }
156 }
157
158 Application* application = new Application(
159 m_taskController,
160- m_desktopFileReaderFactory->createInstanceForAppId(appId),
161+ m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)),
162 Application::Starting,
163 arguments,
164 this);
165@@ -467,7 +501,7 @@
166 if (!application) { // if shell did not start this application, but upstart did
167 application = new Application(
168 m_taskController,
169- m_desktopFileReaderFactory->createInstanceForAppId(appId),
170+ m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)),
171 Application::Starting,
172 QStringList(), this);
173 if (!application->isValid()) {
174@@ -489,8 +523,10 @@
175 }
176 }
177
178-bool ApplicationManager::stopApplication(const QString &appId)
179+bool ApplicationManager::stopApplication(const QString &inputAppId)
180 {
181+ const QString appId = toShortAppIdIfPossible(inputAppId);
182+
183 Application *application = findApplication(appId);
184 DLOG("ApplicationManager::stopApplication (this=%p, application=%p, appId=%s)", this, application, qPrintable(appId));
185
186@@ -504,7 +540,7 @@
187 remove(application);
188 m_dbusWindowStack->WindowDestroyed(0, application->appId());
189
190- bool result = m_taskController->stop(application->appId());
191+ bool result = m_taskController->stop(application->longAppId());
192
193 LOG_IF(result == false, "FAILED to ask Upstart to stop application '%s'", qPrintable(application->appId()));
194
195@@ -521,8 +557,10 @@
196 return result;
197 }
198
199-bool ApplicationManager::updateScreenshot(const QString &appId)
200+bool ApplicationManager::updateScreenshot(const QString &inputAppId)
201 {
202+ const QString appId = toShortAppIdIfPossible(inputAppId);
203+
204 Application *application = findApplication(appId);
205
206 if (!application) {
207@@ -642,7 +680,7 @@
208
209 for (Application *app : m_applications) {
210 if (app->state() == Application::Starting
211- && m_taskController->appIdHasProcessId(app->appId(), pid)) {
212+ && m_taskController->appIdHasProcessId(app->longAppId(), pid)) {
213 app->setPid(pid);
214 DLOG("ApplicationManager::authorizeSession - connecting: application=%p and pid=%lld", app, pid);
215 authorized = true;
216@@ -678,13 +716,16 @@
217
218 DLOG("Process supplied desktop_file_hint, loading '%s'", desktopFileName.get().toLatin1().data());
219
220+ // Guess appId from the desktop file hint
221+ QString appId = toShortAppIdIfPossible(desktopFileName.get().remove(QRegExp(".desktop$")).split('/').last());
222+
223 // FIXME: right now we support --desktop_file_hint=appId for historical reasons. So let's try that in
224 // case we didn't get an existing .desktop file path
225 DesktopFileReader* desktopData;
226 if (QFileInfo(desktopFileName.get()).exists()) {
227- desktopData = m_desktopFileReaderFactory->createInstanceForDesktopFile(QFileInfo(desktopFileName.get()));
228+ desktopData = m_desktopFileReaderFactory->createInstance(appId, QFileInfo(desktopFileName.get()));
229 } else {
230- desktopData = m_desktopFileReaderFactory->createInstanceForAppId(desktopFileName.get());
231+ desktopData = m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId));
232 }
233
234 if (!desktopData->loaded()) {
235
236=== modified file 'src/modules/Unity/Application/applicationcontroller.h'
237--- src/modules/Unity/Application/applicationcontroller.h 2014-02-28 16:51:31 +0000
238+++ src/modules/Unity/Application/applicationcontroller.h 2014-05-16 12:20:01 +0000
239@@ -21,6 +21,7 @@
240 #include <QObject>
241 #include <QString>
242 #include <QStringList>
243+#include <QFileInfo>
244
245 namespace unitymir
246 {
247@@ -47,6 +48,8 @@
248 virtual bool stopApplicationWithAppId(const QString& appId) = 0;
249 virtual bool startApplicationWithAppIdAndArgs(const QString& appId, const QStringList& arguments) = 0;
250
251+ virtual QFileInfo findDesktopFileForAppId(const QString &appId) const = 0;
252+
253 Q_SIGNALS:
254 void applicationAboutToBeStarted(QString id);
255 void applicationStarted(QString id);
256
257=== modified file 'src/modules/Unity/Application/desktopfilereader.cpp'
258--- src/modules/Unity/Application/desktopfilereader.cpp 2014-02-28 16:51:31 +0000
259+++ src/modules/Unity/Application/desktopfilereader.cpp 2014-05-16 12:20:01 +0000
260@@ -22,8 +22,6 @@
261
262 // Qt
263 #include <QFile>
264-#include <QDir>
265-#include <QStandardPaths>
266
267 namespace unitymir
268 {
269@@ -36,38 +34,23 @@
270 {
271 }
272
273-DesktopFileReader* DesktopFileReader::Factory::createInstanceForAppId(const QString& appId)
274-{
275- return new DesktopFileReader(appId);
276-}
277-
278-DesktopFileReader* DesktopFileReader::Factory::createInstanceForDesktopFile(const QFileInfo& fi)
279-{
280- return new DesktopFileReader(fi);
281+DesktopFileReader* DesktopFileReader::Factory::createInstance(const QString &appId, const QFileInfo& fi)
282+{
283+ return new DesktopFileReader(appId, fi);
284 }
285
286 // Retrieves the size of an array at compile time.
287 #define ARRAY_SIZE(a) \
288 ((sizeof(a) / sizeof(*(a))) / static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
289
290-DesktopFileReader::DesktopFileReader(const QString &appId)
291+DesktopFileReader::DesktopFileReader(const QString &appId, const QFileInfo &desktopFile)
292 : appId_(appId)
293 , entries_(DesktopFileReader::kNumberOfEntries, "")
294 {
295- DLOG("DesktopFileReader::DesktopFileReader (this=%p, appId='%s')", this, appId.toLatin1().data());
296- DASSERT(appId != NULL);
297-
298- file_ = findDesktopFile(appId);
299- loaded_ = loadDesktopFile(file_);
300-}
301-
302-DesktopFileReader::DesktopFileReader(const QFileInfo &desktopFile)
303- : entries_(DesktopFileReader::kNumberOfEntries, "")
304-{
305- DLOG("DesktopFileReader::DesktopFileReader (this=%p, desktopFile='%s')", this, desktopFile.absoluteFilePath().toLatin1().data());
306+ DLOG("DesktopFileReader::DesktopFileReader (this=%p, appId='%s', desktopFile='%s')", this,
307+ qPrintable(appId), qPrintable(desktopFile.absoluteFilePath()));
308 DASSERT(desktopFile.exists());
309
310- appId_ = desktopFile.completeBaseName();
311 file_ = desktopFile.absoluteFilePath();
312 loaded_ = loadDesktopFile(file_);
313 }
314@@ -78,30 +61,6 @@
315 entries_.clear();
316 }
317
318-
319-QString DesktopFileReader::findDesktopFile(const QString &appId) const
320-{
321- DLOG("DesktopFileReader::findDesktopFile (appId='%s')", appId.toLatin1().data());
322-
323- int dashPos = -1;
324- QString helper = appId;
325- QString desktopFile;
326-
327- do {
328- if (dashPos != -1) {
329- helper = helper.replace(dashPos, 1, '/');
330- }
331-
332- desktopFile = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, QString("%1.desktop").arg(helper));
333- if (!desktopFile.isEmpty()) return desktopFile;
334-
335- dashPos = helper.indexOf("-");
336- } while (dashPos != -1);
337-
338- return QString();
339-}
340-
341-
342 bool DesktopFileReader::loadDesktopFile(QString desktopFile)
343 {
344 DLOG("DesktopFileReader::loadDesktopFile (this=%p, desktopFile='%s')",
345
346=== modified file 'src/modules/Unity/Application/desktopfilereader.h'
347--- src/modules/Unity/Application/desktopfilereader.h 2014-02-28 16:51:31 +0000
348+++ src/modules/Unity/Application/desktopfilereader.h 2014-05-16 12:20:01 +0000
349@@ -35,8 +35,7 @@
350
351 Factory& operator=(const Factory&) = delete;
352
353- virtual DesktopFileReader* createInstanceForAppId(const QString& appId);
354- virtual DesktopFileReader* createInstanceForDesktopFile(const QFileInfo& fi);
355+ virtual DesktopFileReader* createInstance(const QString &appId, const QFileInfo& fi);
356 };
357
358 virtual ~DesktopFileReader();
359@@ -50,13 +49,11 @@
360 virtual QString path() const { return entries_[kPathIndex]; }
361 virtual QString stageHint() const { return entries_[kStageHintIndex]; }
362 virtual bool loaded() const { return loaded_; }
363- virtual QString findDesktopFile(const QString &appId) const;
364
365 protected:
366 friend class DesktopFileReaderFactory;
367
368- DesktopFileReader(const QString &appId);
369- DesktopFileReader(const QFileInfo &desktopFile);
370+ DesktopFileReader(const QString &appId, const QFileInfo &desktopFile);
371
372 private:
373 static const int kNameIndex = 0,
374
375=== modified file 'src/modules/Unity/Application/taskcontroller.cpp'
376--- src/modules/Unity/Application/taskcontroller.cpp 2014-02-28 16:51:31 +0000
377+++ src/modules/Unity/Application/taskcontroller.cpp 2014-05-16 12:20:01 +0000
378@@ -107,6 +107,11 @@
379 return m_appController->appIdHasProcessId(pid, appId);
380 }
381
382+QFileInfo TaskController::findDesktopFileForAppId(const QString &appId) const
383+{
384+ return m_appController->findDesktopFileForAppId(appId);
385+}
386+
387 bool TaskController::suspend(const QString& appId)
388 {
389 DLOG("TaskController::suspend (this=%p, application=%p)", this, qPrintable(appId));
390
391=== modified file 'src/modules/Unity/Application/taskcontroller.h'
392--- src/modules/Unity/Application/taskcontroller.h 2014-02-28 16:51:31 +0000
393+++ src/modules/Unity/Application/taskcontroller.h 2014-05-16 12:20:01 +0000
394@@ -45,6 +45,7 @@
395 bool resume(const QString& appId);
396
397 bool appIdHasProcessId(const QString& appId, const quint64 pid);
398+ QFileInfo findDesktopFileForAppId(const QString &appId) const;
399
400 Q_SIGNALS:
401 void processStartReport(const QString& appId, const bool failure);
402
403=== modified file 'src/modules/Unity/Application/upstart/applicationcontroller.cpp'
404--- src/modules/Unity/Application/upstart/applicationcontroller.cpp 2014-04-16 17:45:59 +0000
405+++ src/modules/Unity/Application/upstart/applicationcontroller.cpp 2014-05-16 12:20:01 +0000
406@@ -20,6 +20,9 @@
407 // unity-mir
408 #include <logging.h>
409
410+// Qt
411+#include <QStandardPaths>
412+
413 // upstart
414 extern "C" {
415 #include "upstart-app-launch.h"
416@@ -40,33 +43,89 @@
417 upstart_app_launch_app_failed_observer_t failureCallback = nullptr;
418 };
419
420+namespace {
421+/**
422+ * @brief toShortAppIdIfPossible
423+ * @param appId - any string that you think is an appId
424+ * @return if a valid appId was input, a shortened appId is returned, else returns the input string unaltered
425+ */
426+QString toShortAppIdIfPossible(const QString &appId) {
427+ gchar *package, *application;
428+ if (upstart_app_launch_app_id_parse(appId.toLatin1().constData(), &package, &application, nullptr)) {
429+ // is long appId, so assemble its short appId
430+ QString shortAppId = QString("%1_%2").arg(package).arg(application);
431+ g_free(package);
432+ g_free(application);
433+ return shortAppId;
434+ } else {
435+ return appId;
436+ }
437+}
438+
439+/**
440+ * @brief toLongAppIdIfPossible
441+ * @param shortAppId - any string that you think is a short appId
442+ * @return if valid short appId was input, the corresponding long appId is returned. If a long appId was
443+ * entered, it is returned unchanged. Anything else is also returned unchanged.
444+ */
445+QString toLongAppIdIfPossible(const QString &shortAppId) {
446+ if (upstart_app_launch_app_id_parse(shortAppId.toLatin1().constData(), nullptr, nullptr, nullptr)) {
447+ // then we got a long appId after all, just return it
448+ return shortAppId;
449+ } else {
450+ // try to parse the string in the form "$package_$application"
451+ QRegExp shortAppIdMask("[a-z0-9][a-z0-9+.-]+_[a-zA-Z0-9+.-]+");
452+ if (!shortAppIdMask.exactMatch(shortAppId)) {
453+ // input string not a short appId, so just return it unchanged
454+ return shortAppId;
455+ }
456+
457+ // ask upstart for the long appId corresponding to this short appId
458+ QStringList parts = shortAppId.split("_");
459+ gchar *longAppId;
460+ longAppId = upstart_app_launch_triplet_to_app_id(parts.first().toLatin1().constData(),
461+ parts.last().toLatin1().constData(),
462+ nullptr);
463+ if (longAppId == nullptr) {
464+ // was unable to construct a long appId from the short appId, return input unchanged
465+ return shortAppId;
466+ } else {
467+ QString appId(longAppId);
468+ g_free(longAppId);
469+ return appId;
470+ }
471+ }
472+}
473+
474+} // namespace
475+
476 ApplicationController::ApplicationController()
477 : unitymir::ApplicationController(),
478 impl(new Private())
479 {
480 impl->preStartCallback = [](const gchar * appId, gpointer userData) {
481 auto thiz = static_cast<ApplicationController*>(userData);
482- Q_EMIT(thiz->applicationAboutToBeStarted(appId));
483+ Q_EMIT(thiz->applicationAboutToBeStarted(toShortAppIdIfPossible(appId)));
484 };
485
486 impl->startedCallback = [](const gchar * appId, gpointer userData) {
487 auto thiz = static_cast<ApplicationController*>(userData);
488- Q_EMIT(thiz->applicationStarted(appId));
489+ Q_EMIT(thiz->applicationStarted(toShortAppIdIfPossible(appId)));
490 };
491
492 impl->stopCallback = [](const gchar * appId, gpointer userData) {
493 auto thiz = static_cast<ApplicationController*>(userData);
494- Q_EMIT(thiz->applicationStopped(appId));
495+ Q_EMIT(thiz->applicationStopped(toShortAppIdIfPossible(appId)));
496 };
497
498 impl->focusCallback = [](const gchar * appId, gpointer userData) {
499 auto thiz = static_cast<ApplicationController*>(userData);
500- Q_EMIT(thiz->applicationFocusRequest(appId));
501+ Q_EMIT(thiz->applicationFocusRequest(toShortAppIdIfPossible(appId)));
502 };
503
504 impl->resumeCallback = [](const gchar * appId, gpointer userData) {
505 auto thiz = static_cast<ApplicationController*>(userData);
506- Q_EMIT(thiz->applicationResumeRequest(appId));
507+ Q_EMIT(thiz->applicationResumeRequest(toShortAppIdIfPossible(appId)));
508 };
509
510 impl->failureCallback = [](const gchar * appId, upstart_app_launch_app_failed_t failureType, gpointer userData) {
511@@ -78,7 +137,7 @@
512 }
513
514 auto thiz = static_cast<ApplicationController*>(userData);
515- Q_EMIT(thiz->applicationError(appId, error));
516+ Q_EMIT(thiz->applicationError(toShortAppIdIfPossible(appId), error));
517 };
518
519 upstart_app_launch_observer_add_app_starting(impl->preStartCallback, this);
520@@ -101,7 +160,7 @@
521
522 pid_t ApplicationController::primaryPidForAppId(const QString& appId)
523 {
524- GPid pid = upstart_app_launch_get_primary_pid(appId.toLatin1().constData());
525+ GPid pid = upstart_app_launch_get_primary_pid(toLongAppIdIfPossible(appId).toLatin1().constData());
526 DLOG_IF(!pid, "ApplicationController::stopApplication appId='%s' FAILED", qPrintable(appId));
527
528 return pid;
529@@ -109,12 +168,12 @@
530
531 bool ApplicationController::appIdHasProcessId(pid_t pid, const QString& appId)
532 {
533- return upstart_app_launch_pid_in_app_id(pid, appId.toLatin1().constData());
534+ return upstart_app_launch_pid_in_app_id(pid, toLongAppIdIfPossible(appId).toLatin1().constData());
535 }
536
537 bool ApplicationController::stopApplicationWithAppId(const QString& appId)
538 {
539- auto result = upstart_app_launch_stop_application(appId.toLatin1().constData());
540+ auto result = upstart_app_launch_stop_application(toLongAppIdIfPossible(appId).toLatin1().constData());
541 DLOG_IF(!result, "ApplicationController::stopApplicationWithAppId appId='%s' FAILED", qPrintable(appId));
542 return result;
543 }
544@@ -130,7 +189,7 @@
545 }
546
547 auto result = upstart_app_launch_start_application(
548- appId.toLatin1().constData(),
549+ toLongAppIdIfPossible(appId).toLatin1().constData(),
550 static_cast<const gchar * const *>(upstartArgs));
551
552 g_strfreev(upstartArgs);
553@@ -139,5 +198,28 @@
554 return result;
555 }
556
557+QFileInfo ApplicationController::findDesktopFileForAppId(const QString &appId) const
558+{
559+ DLOG("ApplicationController::desktopFilePathForAppId (appId='%s')", qPrintable(appId));
560+
561+ // Search for the correct desktop file using a simple heuristic
562+ int dashPos = -1;
563+ QString helper = toLongAppIdIfPossible(appId);
564+ QString desktopFile;
565+
566+ do {
567+ if (dashPos != -1) {
568+ helper = helper.replace(dashPos, 1, '/');
569+ }
570+
571+ desktopFile = QStandardPaths::locate(QStandardPaths::ApplicationsLocation, QString("%1.desktop").arg(helper));
572+ if (!desktopFile.isEmpty()) return desktopFile;
573+
574+ dashPos = helper.indexOf("-");
575+ } while (dashPos != -1);
576+
577+ return QFileInfo();
578+}
579+
580 } // namespace upstart
581 } // namespace unitymir
582
583=== modified file 'src/modules/Unity/Application/upstart/applicationcontroller.h'
584--- src/modules/Unity/Application/upstart/applicationcontroller.h 2014-02-28 16:51:31 +0000
585+++ src/modules/Unity/Application/upstart/applicationcontroller.h 2014-05-16 12:20:01 +0000
586@@ -31,11 +31,13 @@
587 ApplicationController();
588 ~ApplicationController();
589
590- pid_t primaryPidForAppId(const QString& appId);
591- bool appIdHasProcessId(pid_t pid, const QString& appId);
592-
593- bool stopApplicationWithAppId(const QString& appId);
594- bool startApplicationWithAppIdAndArgs(const QString& appId, const QStringList& arguments);
595+ pid_t primaryPidForAppId(const QString& appId) override;
596+ bool appIdHasProcessId(pid_t pid, const QString& appId) override;
597+
598+ bool stopApplicationWithAppId(const QString& appId) override;
599+ bool startApplicationWithAppIdAndArgs(const QString& appId, const QStringList& arguments) override;
600+
601+ QFileInfo findDesktopFileForAppId(const QString &appId) const override;
602
603 private:
604 struct Private;
605
606=== modified file 'tests/application_manager_test.cpp'
607--- tests/application_manager_test.cpp 2014-04-15 14:31:02 +0000
608+++ tests/application_manager_test.cpp 2014-05-16 12:20:01 +0000
609@@ -82,8 +82,9 @@
610 const QString appId("com.canonical.does.not.exist");
611
612 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(_, _)).Times(1);
613+ EXPECT_CALL(appController, findDesktopFileForAppId(appId)).Times(1);
614
615- EXPECT_CALL(desktopFileReaderFactory, createInstanceForAppId(appId)).Times(1);
616+ EXPECT_CALL(desktopFileReaderFactory, createInstance(_, _)).Times(1);
617
618 EXPECT_CALL(processController, sigStopProcessGroupForPid(_)).Times(1);
619 EXPECT_CALL(processController, sigContinueProcessGroupForPid(_)).Times(1);
620@@ -225,6 +226,85 @@
621 EXPECT_EQ(beforeFailure, afterFailure);
622 }
623
624+TEST_F(ApplicationManagerTests,startApplicationSupportsShortAppId)
625+{
626+ using namespace ::testing;
627+
628+ const QString shortAppId("com.canonical.test_test");
629+
630+ EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(_, _)).Times(1);
631+ EXPECT_CALL(appController, findDesktopFileForAppId(shortAppId)).Times(1);
632+
633+ EXPECT_CALL(desktopFileReaderFactory, createInstance(_, _)).Times(1);
634+
635+ auto application = applicationManager.startApplication(
636+ shortAppId,
637+ ApplicationManager::NoFlag,
638+ QStringList());
639+
640+ EXPECT_EQ(shortAppId, application->appId());
641+}
642+
643+TEST_F(ApplicationManagerTests,startApplicationSupportsLongAppId)
644+{
645+ using namespace ::testing;
646+
647+ const QString longAppId("com.canonical.test_test_0.1.235");
648+ const QString shortAppId("com.canonical.test_test");
649+
650+ EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(_, _)).Times(1);
651+ EXPECT_CALL(appController, findDesktopFileForAppId(shortAppId)).Times(1);
652+
653+ EXPECT_CALL(desktopFileReaderFactory, createInstance(_, _)).Times(1);
654+
655+ auto application = applicationManager.startApplication(
656+ longAppId,
657+ ApplicationManager::NoFlag,
658+ QStringList());
659+
660+ EXPECT_EQ(shortAppId, application->appId());
661+}
662+
663+TEST_F(ApplicationManagerTests,testAppIdGuessFromDesktopFileName)
664+{
665+ using namespace ::testing;
666+ quint64 procId = 5921;
667+ QString appId("sudoku-app");
668+ QString cmdLine = QString("/usr/bin/my-app --desktop_file_hint=/usr/share/click/preinstalled/com.ubuntu.sudoku/1.0.180/%1.desktop").arg(appId);
669+
670+ EXPECT_CALL(procInfo,command_line(procId))
671+ .Times(1)
672+ .WillOnce(Return(qPrintable(cmdLine)));
673+
674+ bool authed = true;
675+ applicationManager.authorizeSession(procId, authed);
676+ Application *app = applicationManager.findApplication(appId);
677+
678+ EXPECT_EQ(true, authed);
679+ EXPECT_NE(app, nullptr);
680+ EXPECT_EQ(appId, app->appId());
681+}
682+
683+TEST_F(ApplicationManagerTests,testAppIdGuessFromDesktopFileNameWithLongAppId)
684+{
685+ using namespace ::testing;
686+ quint64 procId = 5921;
687+ QString shortAppId("com.ubuntu.desktop_desktop");
688+ QString cmdLine = QString("/usr/bin/my-app --desktop_file_hint=/usr/share/applications/%1_1.0.180.desktop").arg(shortAppId);
689+
690+ EXPECT_CALL(procInfo,command_line(procId))
691+ .Times(1)
692+ .WillOnce(Return(qPrintable(cmdLine)));
693+
694+ bool authed = true;
695+ applicationManager.authorizeSession(procId, authed);
696+ Application *app = applicationManager.findApplication(shortAppId);
697+
698+ EXPECT_EQ(true, authed);
699+ EXPECT_NE(app, nullptr);
700+ EXPECT_EQ(shortAppId, app->appId());
701+}
702+
703 TEST_F(ApplicationManagerTests,bug_case_1281075_session_ptrs_always_distributed_to_last_started_app)
704 {
705 using namespace ::testing;
706@@ -305,11 +385,13 @@
707 using namespace ::testing;
708 QString appId("sideStage");
709
710- auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId);
711+ EXPECT_CALL(appController, findDesktopFileForAppId(appId)).Times(1);
712+
713+ auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
714 ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
715 ON_CALL(*mockDesktopFileReader, stageHint()).WillByDefault(Return("SideStage"));
716
717- ON_CALL(desktopFileReaderFactory, createInstanceForAppId(appId)).WillByDefault(Return(mockDesktopFileReader));
718+ ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
719
720 // mock upstart launching an app which reports itself as sidestage, but we're on phone
721 applicationManager.onProcessStartReportReceived(appId, false);
722
723=== modified file 'tests/mock_application_controller.h'
724--- tests/mock_application_controller.h 2014-02-28 16:51:31 +0000
725+++ tests/mock_application_controller.h 2014-05-16 12:20:01 +0000
726@@ -30,6 +30,7 @@
727 {
728 MOCK_METHOD1(primaryPidForAppId, pid_t(const QString& appId));
729 MOCK_METHOD2(appIdHasProcessId, bool(pid_t, const QString&));
730+ MOCK_CONST_METHOD1(findDesktopFileForAppId, QFileInfo(const QString &appId));
731
732 MOCK_METHOD1(stopApplicationWithAppId, bool(const QString&));
733 MOCK_METHOD2(startApplicationWithAppIdAndArgs, bool(const QString&, const QStringList&));
734@@ -45,6 +46,10 @@
735 .WillByDefault(
736 Invoke(this, &MockApplicationController::doAppIdHasProcessId));
737
738+ ON_CALL(*this, findDesktopFileForAppId(_))
739+ .WillByDefault(
740+ Invoke(this, &MockApplicationController::doFindDesktopFileForAppId));
741+
742 ON_CALL(*this, stopApplicationWithAppId(_))
743 .WillByDefault(
744 Invoke(this, &MockApplicationController::doStopApplicationWithAppId));
745@@ -72,6 +77,12 @@
746 return it->pid() == pid;
747 }
748
749+ QFileInfo doFindDesktopFileForAppId(const QString& appId) const
750+ {
751+ QString path = QString("/usr/share/applications/%1.desktop").arg(appId);
752+ return QFileInfo(path);
753+ }
754+
755 bool doStopApplicationWithAppId(const QString& appId)
756 {
757 (void) appId;
758
759=== modified file 'tests/mock_desktop_file_reader.h'
760--- tests/mock_desktop_file_reader.h 2014-02-28 16:51:31 +0000
761+++ tests/mock_desktop_file_reader.h 2014-05-16 12:20:01 +0000
762@@ -26,38 +26,20 @@
763 {
764 struct MockDesktopFileReader : public unitymir::DesktopFileReader
765 {
766- MockDesktopFileReader(const QString& appId)
767- : unitymir::DesktopFileReader(appId)
768- {
769- using namespace ::testing;
770-
771- ON_CALL(*this, file()).WillByDefault(Invoke(this, &MockDesktopFileReader::doFile));
772- ON_CALL(*this, appId()).WillByDefault(Invoke(this, &MockDesktopFileReader::doAppId));
773- ON_CALL(*this, name()).WillByDefault(Invoke(this, &MockDesktopFileReader::doName));
774- ON_CALL(*this, comment()).WillByDefault(Invoke(this, &MockDesktopFileReader::doComment));
775- ON_CALL(*this, icon()).WillByDefault(Invoke(this, &MockDesktopFileReader::doIcon));
776- ON_CALL(*this, exec()).WillByDefault(Invoke(this, &MockDesktopFileReader::doExec));
777- ON_CALL(*this, path()).WillByDefault(Invoke(this, &MockDesktopFileReader::doPath));
778- ON_CALL(*this, stageHint()).WillByDefault(Invoke(this, &MockDesktopFileReader::doStageHint));
779- ON_CALL(*this, loaded()).WillByDefault(Invoke(this, &MockDesktopFileReader::doLoaded));
780- ON_CALL(*this, findDesktopFile(_)).WillByDefault(Invoke(this, &MockDesktopFileReader::doFindDesktopFile));
781- }
782-
783- MockDesktopFileReader(const QFileInfo& fileInfo)
784- : DesktopFileReader(fileInfo)
785- {
786- using namespace ::testing;
787-
788- ON_CALL(*this, file()).WillByDefault(Invoke(this, &MockDesktopFileReader::doFile));
789- ON_CALL(*this, appId()).WillByDefault(Invoke(this, &MockDesktopFileReader::doAppId));
790- ON_CALL(*this, name()).WillByDefault(Invoke(this, &MockDesktopFileReader::doName));
791- ON_CALL(*this, comment()).WillByDefault(Invoke(this, &MockDesktopFileReader::doComment));
792- ON_CALL(*this, icon()).WillByDefault(Invoke(this, &MockDesktopFileReader::doIcon));
793- ON_CALL(*this, exec()).WillByDefault(Invoke(this, &MockDesktopFileReader::doExec));
794- ON_CALL(*this, path()).WillByDefault(Invoke(this, &MockDesktopFileReader::doPath));
795- ON_CALL(*this, stageHint()).WillByDefault(Invoke(this, &MockDesktopFileReader::doStageHint));
796- ON_CALL(*this, loaded()).WillByDefault(Invoke(this, &MockDesktopFileReader::doLoaded));
797- ON_CALL(*this, findDesktopFile(_)).WillByDefault(Invoke(this, &MockDesktopFileReader::doFindDesktopFile));
798+ MockDesktopFileReader(const QString &appId, const QFileInfo& fileInfo)
799+ : DesktopFileReader(appId, fileInfo)
800+ {
801+ using namespace ::testing;
802+
803+ ON_CALL(*this, file()).WillByDefault(Invoke(this, &MockDesktopFileReader::doFile));
804+ ON_CALL(*this, appId()).WillByDefault(Invoke(this, &MockDesktopFileReader::doAppId));
805+ ON_CALL(*this, name()).WillByDefault(Invoke(this, &MockDesktopFileReader::doName));
806+ ON_CALL(*this, comment()).WillByDefault(Invoke(this, &MockDesktopFileReader::doComment));
807+ ON_CALL(*this, icon()).WillByDefault(Invoke(this, &MockDesktopFileReader::doIcon));
808+ ON_CALL(*this, exec()).WillByDefault(Invoke(this, &MockDesktopFileReader::doExec));
809+ ON_CALL(*this, path()).WillByDefault(Invoke(this, &MockDesktopFileReader::doPath));
810+ ON_CALL(*this, stageHint()).WillByDefault(Invoke(this, &MockDesktopFileReader::doStageHint));
811+ ON_CALL(*this, loaded()).WillByDefault(Invoke(this, &MockDesktopFileReader::doLoaded));
812 }
813
814 MOCK_CONST_METHOD0(file, QString());
815@@ -69,7 +51,6 @@
816 MOCK_CONST_METHOD0(path, QString());
817 MOCK_CONST_METHOD0(stageHint, QString());
818 MOCK_CONST_METHOD0(loaded, bool());
819- MOCK_CONST_METHOD1(findDesktopFile, QString(const QString&));
820
821 QString doFile() const
822 {
823@@ -115,11 +96,6 @@
824 {
825 return DesktopFileReader::loaded();
826 }
827-
828- QString doFindDesktopFile(const QString& appId) const
829- {
830- return DesktopFileReader::findDesktopFile(appId);
831- }
832 };
833
834 struct MockDesktopFileReaderFactory : public unitymir::DesktopFileReader::Factory
835@@ -127,38 +103,23 @@
836 MockDesktopFileReaderFactory()
837 {
838 using namespace ::testing;
839- ON_CALL(*this, createInstanceForAppId(_))
840- .WillByDefault(
841- Invoke(
842- this,
843- &MockDesktopFileReaderFactory::doCreateInstanceForAppId));
844- ON_CALL(*this, createInstanceForDesktopFile(_))
845- .WillByDefault(
846- Invoke(
847- this,
848- &MockDesktopFileReaderFactory::doCreateInstanceForDesktopFile));
849- }
850-
851- virtual unitymir::DesktopFileReader* doCreateInstanceForAppId(const QString& appId)
852- {
853- using namespace ::testing;
854- auto instance = new NiceMock<MockDesktopFileReader>(appId);
855- ON_CALL(*instance, loaded()).WillByDefault(Return(true));
856-
857- return instance;
858- }
859-
860- virtual unitymir::DesktopFileReader* doCreateInstanceForDesktopFile(const QFileInfo& fi)
861- {
862- using namespace ::testing;
863- auto instance = new NiceMock<MockDesktopFileReader>(fi);
864- ON_CALL(*instance, loaded()).WillByDefault(Return(true));
865-
866- return instance;
867- }
868-
869- MOCK_METHOD1(createInstanceForAppId, unitymir::DesktopFileReader*(const QString&));
870- MOCK_METHOD1(createInstanceForDesktopFile, unitymir::DesktopFileReader*(const QFileInfo&));
871+ ON_CALL(*this, createInstance(_, _))
872+ .WillByDefault(
873+ Invoke(
874+ this,
875+ &MockDesktopFileReaderFactory::doCreateInstance));
876+ }
877+
878+ virtual unitymir::DesktopFileReader* doCreateInstance(const QString &appId, const QFileInfo &fi)
879+ {
880+ using namespace ::testing;
881+ auto instance = new NiceMock<MockDesktopFileReader>(appId, fi);
882+ ON_CALL(*instance, loaded()).WillByDefault(Return(true));
883+
884+ return instance;
885+ }
886+
887+ MOCK_METHOD2(createInstance, unitymir::DesktopFileReader*(const QString &appId, const QFileInfo &fi));
888 };
889 }
890
891
892=== modified file 'tests/taskcontroller_test.cpp'
893--- tests/taskcontroller_test.cpp 2014-02-28 16:51:31 +0000
894+++ tests/taskcontroller_test.cpp 2014-05-16 12:20:01 +0000
895@@ -49,6 +49,8 @@
896
897 MOCK_METHOD1(primaryPidForAppId, pid_t (const QString&));
898 MOCK_METHOD2(appIdHasProcessId, bool(pid_t, const QString&));
899+ MOCK_CONST_METHOD1(findDesktopFileForAppId, QFileInfo(const QString &appId));
900+
901 MOCK_METHOD1(stopApplicationWithAppId, bool(const QString&));
902 MOCK_METHOD2(startApplicationWithAppIdAndArgs, bool(const QString&, const QStringList&));
903 };

Subscribers

People subscribed via source and target branches