Merge lp:unity-mir/devel-mir-next into lp:unity-mir
- devel-mir-next
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Michał Sawicz |
Approved revision: | no longer in the source branch. |
Merged at revision: | 222 |
Proposed branch: | lp:unity-mir/devel-mir-next |
Merge into: | lp:unity-mir |
Diff against target: |
1928 lines (+1441/-142) 10 files modified
src/modules/Unity/Application/application.cpp (+11/-0) src/modules/Unity/Application/application.h (+4/-1) src/modules/Unity/Application/application_manager.cpp (+80/-85) src/modules/Unity/Application/application_manager.h (+5/-5) src/modules/Unity/Application/taskcontroller.cpp (+4/-25) src/modules/Unity/Application/taskcontroller.h (+3/-4) src/unity-mir/qmirserver.cpp (+22/-3) tests/CMakeLists.txt (+13/-0) tests/application_manager_test.cpp (+1296/-16) tests/mock_session.h (+3/-3) |
To merge this branch: | bzr merge lp:unity-mir/devel-mir-next |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Michał Sawicz | Approve | ||
Review via email: mp+220249@code.launchpad.net |
Commit message
Merge devel at rev 222. (Fix crash on shell shutdown; Refactor app shutdown)
2014-05-06 Gerry Boland <email address hidden>
[220] Fix crash on Mir-initiated shutdown, where stop() was being
called on an already shutting-down server.
Mir initiates shutdown on SIGINT & SIGTERM, need to distinguish that
shutdown from a client-initiated shutdown. Do this by installing a
custom signal handler that is run after Mir's initiate-shutdown
handler is called, so that we only call server.stop() on a client-
initiated shutdown. Fixes: https:/
Approved by Michał Sawicz, PS Jenkins bot.
2014-05-20 Gerry Boland <email address hidden>
[222] Refactoring to have app shutdown handled correctly.
Involves adding a canBeResumed flag to Application and using it to
determine if AppMan should remove the application from the lists.
Also add a big bunch of unit tests for AppMan. Fixes:
https:/
Approved by PS Jenkins bot, Michał Sawicz.
Description of the change
Refactoring to have app shutdown handled correctly.
Involves adding a canBeResumed flag to Application and using it to determine if AppMan should remove the application from the lists. Also add a big bunch of unit tests for AppMan. Fixes: https:/
Checklist:
* Are there any related MPs required for this MP to build/function as expected? Please list.
N
* Did you perform an exploratory manual test run of your code change and any related functionality?
Y
* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:222
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 222. By Tarmac
-
Merge devel at rev 222. (Fix crash on shell shutdown; Refactor app shutdown)
2014-05-06 Gerry Boland <email address hidden>
[220] Fix crash on Mir-initiated shutdown, where stop() was being
called on an already shutting-down server.Mir initiates shutdown on SIGINT & SIGTERM, need to distinguish that
shutdown from a client-initiated shutdown. Do this by installing a
custom signal handler that is run after Mir's initiate-shutdown
handler is called, so that we only call server.stop() on a client-
initiated shutdown. Fixes: https://bugs.launchpad .net/bugs/ 1315251. Approved by Michał Sawicz, PS Jenkins bot.
2014-05-20 Gerry Boland <email address hidden>
[222] Refactoring to have app shutdown handled correctly.
Involves adding a canBeResumed flag to Application and using it to
determine if AppMan should remove the application from the lists.
Also add a big bunch of unit tests for AppMan. Fixes:
https://bugs.launchpad .net/bugs/ 1305128. Approved by PS Jenkins bot, Michał Sawicz.
Preview Diff
1 | === modified file 'src/modules/Unity/Application/application.cpp' |
2 | --- src/modules/Unity/Application/application.cpp 2014-05-14 16:44:55 +0000 |
3 | +++ src/modules/Unity/Application/application.cpp 2014-05-20 12:42:58 +0000 |
4 | @@ -42,6 +42,7 @@ |
5 | , m_stage((m_desktopData->stageHint() == "SideStage") ? Application::SideStage : Application::MainStage) |
6 | , m_state(state) |
7 | , m_focused(false) |
8 | + , m_canBeResumed(true) |
9 | , m_fullscreen(false) |
10 | , m_visible(false) |
11 | , m_arguments(arguments) |
12 | @@ -142,6 +143,16 @@ |
13 | return m_visible; |
14 | } |
15 | |
16 | +bool Application::canBeResumed() const |
17 | +{ |
18 | + return m_canBeResumed; |
19 | +} |
20 | + |
21 | +void Application::setCanBeResumed(const bool resume) |
22 | +{ |
23 | + m_canBeResumed = resume; |
24 | +} |
25 | + |
26 | pid_t Application::pid() const |
27 | { |
28 | return m_pid; |
29 | |
30 | === modified file 'src/modules/Unity/Application/application.h' |
31 | --- src/modules/Unity/Application/application.h 2014-05-14 16:44:55 +0000 |
32 | +++ src/modules/Unity/Application/application.h 2014-05-20 12:42:58 +0000 |
33 | @@ -71,6 +71,9 @@ |
34 | QImage screenshotImage() const; |
35 | void updateScreenshot(); |
36 | |
37 | + bool canBeResumed() const; |
38 | + void setCanBeResumed(const bool); |
39 | + |
40 | bool isValid() const; |
41 | QString desktopFile() const; |
42 | QString exec() const; |
43 | @@ -106,6 +109,7 @@ |
44 | bool m_focused; |
45 | QUrl m_screenshot; |
46 | QImage m_screenshotImage; |
47 | + bool m_canBeResumed; |
48 | bool m_fullscreen; |
49 | bool m_visible; // duplicating internal Mir data :( |
50 | std::shared_ptr<::mir::scene::Session> m_session; |
51 | @@ -114,7 +118,6 @@ |
52 | QTimer* m_suspendTimer; |
53 | |
54 | friend class ApplicationManager; |
55 | - friend class ApplicationListModel; |
56 | friend class MirSurfaceManager; |
57 | }; |
58 | |
59 | |
60 | === modified file 'src/modules/Unity/Application/application_manager.cpp' |
61 | --- src/modules/Unity/Application/application_manager.cpp 2014-05-16 12:19:52 +0000 |
62 | +++ src/modules/Unity/Application/application_manager.cpp 2014-05-20 12:42:58 +0000 |
63 | @@ -113,22 +113,22 @@ |
64 | { |
65 | QObject::connect(strategy, &InitialSurfacePlacementStrategy::requestPlacementForSession, |
66 | manager, &ApplicationManager::placeSession, Qt::DirectConnection); |
67 | - |
68 | } |
69 | |
70 | void connectToTaskController(ApplicationManager * manager, TaskController * controller) |
71 | { |
72 | - QObject::connect(controller, &TaskController::processStartReport, |
73 | - manager, &ApplicationManager::onProcessStartReportReceived); |
74 | + QObject::connect(controller, &TaskController::processStarting, |
75 | + manager, &ApplicationManager::onProcessStarting); |
76 | QObject::connect(controller, &TaskController::processStopped, |
77 | manager, &ApplicationManager::onProcessStopped); |
78 | + QObject::connect(controller, &TaskController::processFailed, |
79 | + manager, &ApplicationManager::onProcessFailed); |
80 | QObject::connect(controller, &TaskController::requestFocus, |
81 | manager, &ApplicationManager::onFocusRequested); |
82 | QObject::connect(controller, &TaskController::requestResume, |
83 | manager, &ApplicationManager::onResumeRequested); |
84 | - |
85 | -} |
86 | -} |
87 | +} |
88 | +} // namespace |
89 | |
90 | ApplicationManager* ApplicationManager::Factory::Factory::create() |
91 | { |
92 | @@ -485,20 +485,12 @@ |
93 | return application; |
94 | } |
95 | |
96 | -void ApplicationManager::onProcessStartReportReceived(const QString &appId, const bool failure) |
97 | +void ApplicationManager::onProcessStarting(const QString &appId) |
98 | { |
99 | - DLOG("ApplicationManager::onProcessStartReportReceived (this=%p, appId=%s, failure=%c)", |
100 | - this, qPrintable(appId), (failure) ? 'Y' : 'N'); |
101 | - |
102 | - if (failure) { |
103 | - DLOG("ApplicationManager::onProcessStartReportReceived handling failure:"); |
104 | - stopStartingApplication(appId); |
105 | - return; |
106 | - } |
107 | - |
108 | + DLOG("ApplicationManager::onProcessStarting (this=%p, appId=%s)", this, qPrintable(appId)); |
109 | Application *application = findApplication(appId); |
110 | |
111 | - if (!application) { // if shell did not start this application, but upstart did |
112 | + if (!application) { // then shell did not start this application, so upstart must have - add to list |
113 | application = new Application( |
114 | m_taskController, |
115 | m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)), |
116 | @@ -519,7 +511,37 @@ |
117 | Q_EMIT focusRequested(appId); |
118 | } |
119 | else { |
120 | - DLOG("ApplicationManager::onProcessStartReportReceived application already found: (app=%p, appId=%s)", application, qPrintable(appId)); |
121 | + DLOG("ApplicationManager::onProcessStarting application already found: (app=%p, appId=%s)", application, qPrintable(appId)); |
122 | + } |
123 | +} |
124 | + |
125 | +void ApplicationManager::onProcessFailed(const QString &appId, const bool duringStartup) |
126 | +{ |
127 | + /* Applications fail if they fail to launch, crash or are killed. If failed to start, must |
128 | + * immediately remove from list of applications. If crash or kill, instead we set flag on the |
129 | + * Application to indicate it can be resumed. |
130 | + */ |
131 | + |
132 | + DLOG("ApplicationManager::onProcessFailed (this=%p, appId=%s, duringStartup=%c)", |
133 | + this, qPrintable(appId), (duringStartup) ? 'Y' : 'N'); |
134 | + |
135 | + Application *application = findApplication(appId); |
136 | + if (!application) { |
137 | + LOG("ApplicationManager::onProcessFailed - upstart reports failure of application AppManager is not managing"); |
138 | + return; |
139 | + } |
140 | + |
141 | + Q_UNUSED(duringStartup); // FIXME(greyback) upstart reports app that fully started up & crashes as failing during startup?? |
142 | + if (application->state() == Application::Starting) { |
143 | + checkFocusOnRemovedApplication(application); |
144 | + remove(application); |
145 | + m_dbusWindowStack->WindowDestroyed(0, application->appId()); |
146 | + delete application; |
147 | + } else { |
148 | + // We need to set flags on the Application to say the app can be resumed, and thus should not be removed |
149 | + // from the list by onProcessStopped. |
150 | + application->setCanBeResumed(true); |
151 | + application->setPid(0); |
152 | } |
153 | } |
154 | |
155 | @@ -574,55 +596,31 @@ |
156 | return true; |
157 | } |
158 | |
159 | -void ApplicationManager::stopStartingApplication(const QString &appId) |
160 | -{ |
161 | - Application *application = findApplication(appId); |
162 | - |
163 | - if (application && application->state() == Application::Starting) { |
164 | - shutdownApplication(application); |
165 | - } |
166 | - else if (application) { |
167 | - DLOG("ApplicationManager::stopStartingApplication start failure report received - but application=%p, appId=%s is not in Starting state",application, qPrintable(appId)); |
168 | - } |
169 | -} |
170 | - |
171 | -void ApplicationManager::onProcessStopped(const QString &appId, const bool unexpected) |
172 | -{ |
173 | - Application *application = findApplication(appId); |
174 | +void ApplicationManager::onProcessStopped(const QString &appId) |
175 | +{ |
176 | + Application *application = findApplication(appId); |
177 | + if (!application) { |
178 | + DLOG("ApplicationManager::onProcessStopped reports stop of appId='%s' which AppMan is not managing, ignoring the event"); |
179 | + return; |
180 | + } |
181 | DLOG("ApplicationManager::onProcessStopped (this=%p, application=%p, appId=%s)", this, application, qPrintable(appId)); |
182 | |
183 | - // if shell did not stop the application, but upstart says it died, we assume the process has been |
184 | - // killed, so it can be respawned later. Only exception is if that application is focused or running |
185 | - // as then it most likely crashed. Update this logic when upstart gives some failure info. |
186 | - if (application) { |
187 | - shutdownApplication(application); |
188 | - } |
189 | - |
190 | - if (unexpected) { |
191 | - // TODO: pop up a message box/notification? |
192 | - LOG("ApplicationManager: application '%s' died unexpectedly!", qPrintable(appId)); |
193 | - } |
194 | -} |
195 | - |
196 | -void ApplicationManager::shutdownApplication(Application* application) |
197 | -{ |
198 | - bool removeApplication = checkFocusOnRemovedApplication(application); |
199 | - |
200 | - if (application->state() == Application::Running || application->state() == Application::Starting) { |
201 | - // Application probably crashed, else OOM killer struck. Either way state wasn't saved |
202 | - // so just remove application |
203 | - removeApplication = true; |
204 | - } else if (application->state() == Application::Suspended) { |
205 | - application->setState(Application::Stopped); |
206 | - application->setSession(nullptr); |
207 | + bool removeApplication = true; |
208 | + |
209 | + // The following scenario is the only time that we do NOT remove the application from the app list: |
210 | + if ((application->state() == Application::Suspended || application->state() == Application::Stopped) |
211 | + && application->pid() == 0 // i.e. onProcessFailed was called, which resets the PID of this application |
212 | + && application->canBeResumed()) { |
213 | + removeApplication = false; |
214 | } |
215 | |
216 | if (removeApplication) { |
217 | + DLOG("ApplicationManager::onProcessStopped - removing appId='%s' from the application list", qPrintable(appId)); |
218 | + checkFocusOnRemovedApplication(application); |
219 | remove(application); |
220 | m_dbusWindowStack->WindowDestroyed(0, application->appId()); |
221 | delete application; |
222 | } |
223 | - |
224 | } |
225 | |
226 | void ApplicationManager::onFocusRequested(const QString& appId) |
227 | @@ -644,7 +642,7 @@ |
228 | } |
229 | |
230 | // If app Stopped, trust that upstart-app-launch respawns it itself, and AppManager will |
231 | - // be notified of that through the onProcessStartReportReceived slot. Else resume. |
232 | + // be notified of that through the onProcessStarting slot. Else resume. |
233 | if (application->state() == Application::Suspended) { |
234 | application->setState(Application::Running); |
235 | } |
236 | @@ -670,8 +668,6 @@ |
237 | } |
238 | } |
239 | |
240 | -/************************************* Mir-side methods *************************************/ |
241 | - |
242 | void ApplicationManager::authorizeSession(const quint64 pid, bool &authorized) |
243 | { |
244 | authorized = false; //to be proven wrong |
245 | @@ -761,6 +757,7 @@ |
246 | application = new Application(m_taskController, desktopData, Application::Starting, arguments, this); |
247 | application->setPid(pid); |
248 | application->setStage(stage); |
249 | + application->setCanBeResumed(false); |
250 | add(application); |
251 | authorized = true; |
252 | } |
253 | @@ -768,8 +765,8 @@ |
254 | void ApplicationManager::placeSession(ms::Session const* session, uint32_t &x, uint32_t &y) |
255 | { |
256 | Application* application = findApplicationWithSession(session); |
257 | - DLOG("ApplicationManager::placeSession (this=%p, application=%p, session=%p, name=%s)", this, application, session, session?(session->name().c_str()):"null"); |
258 | - |
259 | + DLOG("ApplicationManager::placeSession (this=%p, application=%p, session=%p, name=%s)", this, application, session, |
260 | + session?(session->name().c_str()):"null"); |
261 | |
262 | // Application defaults |
263 | x = 0; |
264 | @@ -818,32 +815,29 @@ |
265 | // in case application closed not by hand of shell, check again here: |
266 | Application* application = findApplicationWithSession(session); |
267 | |
268 | - 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"); |
269 | + DLOG("ApplicationManager::onSessionStopping (this=%p, application=%p, appId=%s, session name=%s)", this, application, |
270 | + application?qPrintable(application->appId()):"null", session?session->name().c_str():"null"); |
271 | |
272 | if (application) { |
273 | - bool removeApplication = true; |
274 | - |
275 | - if (application->state() != Application::Starting) { |
276 | - application->setState(Application::Stopped); |
277 | - application->setSession(nullptr); |
278 | + /* Can remove the application from the running apps list immediately in these curcumstances: |
279 | + * 1. application is not managed by upstart (this message from Mir is only notice the app has stopped, must do |
280 | + * it here) |
281 | + * 2. application is managed by upstart, but has stopped before it managed to create a surface, we can assume |
282 | + * it crashed on startup, and thus cannot be resumed - so remove it. |
283 | + * 3. application is managed by upstart and is in foreground (i.e. has Running state), if Mir reports the |
284 | + * application disconnects, it either crashed or stopped itself. Either case, remove it. |
285 | + */ |
286 | + if (!application->canBeResumed() |
287 | + || application->state() == Application::Starting |
288 | + || application->state() == Application::Running) { |
289 | + checkFocusOnRemovedApplication(application); |
290 | m_dbusWindowStack->WindowDestroyed(0, application->appId()); |
291 | - if (application != m_focusedApplication) { |
292 | - removeApplication = false; |
293 | - } |
294 | - } |
295 | - |
296 | - if (m_mainStageApplication == application) |
297 | - m_mainStageApplication = nullptr; |
298 | - |
299 | - if (m_sideStageApplication == application) |
300 | - m_sideStageApplication = nullptr; |
301 | - |
302 | - if (removeApplication) { |
303 | - // TODO(greyback) What to do?? Focus next app, or unfocus everything?? |
304 | - m_focusedApplication = nullptr; |
305 | remove(application); |
306 | delete application; |
307 | - Q_EMIT focusedApplicationIdChanged(); |
308 | + } else { |
309 | + // otherwise, we do not have enough information to make any changes to the model, so await events from |
310 | + // upstart to go further, but set the app state |
311 | + application->setState(Application::Stopped); |
312 | } |
313 | } |
314 | } |
315 | @@ -851,7 +845,8 @@ |
316 | void ApplicationManager::onSessionFocused(const std::shared_ptr<ms::Session>& session) |
317 | { |
318 | Application* application = findApplicationWithSession(session); |
319 | - 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"); |
320 | + DLOG("ApplicationManager::onSessionFocused (this=%p, application=%p, appId=%s, session name=%s)", this, application, |
321 | + application?qPrintable(application->appId()):"null", session?session->name().c_str():"null"); |
322 | |
323 | // Don't give application focus until it has created it's surface, when it is set as state "Running" |
324 | // and only notify shell of focus changes that it actually expects |
325 | |
326 | === modified file 'src/modules/Unity/Application/application_manager.h' |
327 | --- src/modules/Unity/Application/application_manager.h 2014-04-15 14:31:02 +0000 |
328 | +++ src/modules/Unity/Application/application_manager.h 2014-05-20 12:42:58 +0000 |
329 | @@ -125,8 +125,9 @@ |
330 | |
331 | void onSessionCreatedSurface(::mir::scene::Session const*, std::shared_ptr<::mir::scene::Surface> const&); |
332 | |
333 | - void onProcessStartReportReceived(const QString& appId, const bool failure); |
334 | - void onProcessStopped(const QString& appId, const bool unexpected); |
335 | + void onProcessFailed(const QString& appId, const bool duringStartup); |
336 | + void onProcessStarting(const QString& appId); |
337 | + void onProcessStopped(const QString& appId); |
338 | void onFocusRequested(const QString& appId); |
339 | void onResumeRequested(const QString& appId); |
340 | |
341 | @@ -144,9 +145,8 @@ |
342 | Application* findApplicationWithSession(const ::mir::scene::Session *session); |
343 | Application* applicationForStage(Application::Stage stage); |
344 | QModelIndex findIndex(Application* application); |
345 | - bool checkFocusOnRemovedApplication(Application* application); |
346 | - void shutdownApplication(Application* application); |
347 | - void stopStartingApplication(const QString &appId); |
348 | + bool isFocused(Application* application); |
349 | + bool checkFocusOnRemovedApplication(Application *application); |
350 | |
351 | QList<Application*> m_applications; |
352 | Application* m_focusedApplication; // remove as Mir has API for this |
353 | |
354 | === modified file 'src/modules/Unity/Application/taskcontroller.cpp' |
355 | --- src/modules/Unity/Application/taskcontroller.cpp 2014-05-12 11:09:42 +0000 |
356 | +++ src/modules/Unity/Application/taskcontroller.cpp 2014-05-20 12:42:58 +0000 |
357 | @@ -34,11 +34,8 @@ |
358 | // STL |
359 | #include <mutex> |
360 | |
361 | -// glib |
362 | -#include <glib.h> |
363 | - |
364 | // std |
365 | -#include <signal.h> |
366 | +#include <csignal> |
367 | #include <unistd.h> |
368 | |
369 | namespace unitymir |
370 | @@ -55,7 +52,7 @@ |
371 | connect(m_appController.data(), |
372 | &ApplicationController::applicationAboutToBeStarted, |
373 | this, |
374 | - &TaskController::onApplicationAboutToBeStarted); |
375 | + &TaskController::processStarting); |
376 | |
377 | connect(m_appController.data(), |
378 | &ApplicationController::applicationStarted, |
379 | @@ -65,7 +62,7 @@ |
380 | connect(m_appController.data(), |
381 | &ApplicationController::applicationStopped, |
382 | this, |
383 | - &TaskController::onApplicationStopped); |
384 | + &TaskController::processStopped); |
385 | |
386 | connect(m_appController.data(), |
387 | &ApplicationController::applicationFocusRequest, |
388 | @@ -146,22 +143,12 @@ |
389 | } |
390 | } |
391 | |
392 | -void TaskController::onApplicationAboutToBeStarted(const QString& id) |
393 | -{ |
394 | - Q_EMIT processStartReport(id, false); |
395 | -} |
396 | - |
397 | void TaskController::onApplicationStarted(const QString& id) |
398 | { |
399 | pid_t pid = m_appController->primaryPidForAppId(id); |
400 | m_processController->oomController()->ensureProcessUnlikelyToBeKilled(pid); |
401 | } |
402 | |
403 | -void TaskController::onApplicationStopped(const QString& id) |
404 | -{ |
405 | - Q_EMIT processStopped(id, false); |
406 | -} |
407 | - |
408 | void TaskController::onApplicationFocusRequest(const QString& id) |
409 | { |
410 | pid_t pid = m_appController->primaryPidForAppId(id); |
411 | @@ -176,15 +163,7 @@ |
412 | |
413 | void TaskController::onApplicationError(const QString& id, ApplicationController::Error error) |
414 | { |
415 | - switch(error) |
416 | - { |
417 | - case ApplicationController::Error::APPLICATION_CRASHED: |
418 | - Q_EMIT processStopped(id, true); |
419 | - break; |
420 | - case ApplicationController::Error::APPLICATION_FAILED_TO_START: |
421 | - Q_EMIT processStartReport(id, true); |
422 | - break; |
423 | - } |
424 | + Q_EMIT processFailed(id, (error == ApplicationController::Error::APPLICATION_FAILED_TO_START) ); |
425 | |
426 | // Is this really the signal we want to emit? |
427 | Q_EMIT requestResume(id); |
428 | |
429 | === modified file 'src/modules/Unity/Application/taskcontroller.h' |
430 | --- src/modules/Unity/Application/taskcontroller.h 2014-05-12 11:09:42 +0000 |
431 | +++ src/modules/Unity/Application/taskcontroller.h 2014-05-20 12:42:58 +0000 |
432 | @@ -48,15 +48,14 @@ |
433 | QFileInfo findDesktopFileForAppId(const QString &appId) const; |
434 | |
435 | Q_SIGNALS: |
436 | - void processStartReport(const QString& appId, const bool failure); |
437 | - void processStopped(const QString& appId, const bool unexpectedly); |
438 | + void processStarting(const QString& appId); |
439 | + void processStopped(const QString& appId); |
440 | + void processFailed(const QString& appId, const bool duringStartup); |
441 | void requestFocus(const QString& appId); |
442 | void requestResume(const QString& appId); |
443 | |
444 | private Q_SLOTS: |
445 | - void onApplicationAboutToBeStarted(const QString& id); |
446 | void onApplicationStarted(const QString& id); |
447 | - void onApplicationStopped(const QString& id); |
448 | void onApplicationFocusRequest(const QString& id); |
449 | void onApplicationResumeRequest(const QString& id); |
450 | |
451 | |
452 | === modified file 'src/unity-mir/qmirserver.cpp' |
453 | --- src/unity-mir/qmirserver.cpp 2014-04-09 15:47:05 +0000 |
454 | +++ src/unity-mir/qmirserver.cpp 2014-05-20 12:42:58 +0000 |
455 | @@ -19,6 +19,7 @@ |
456 | #include <mir/abnormal_exit.h> |
457 | #include <mir/default_server_configuration.h> |
458 | #include <mir/display_server.h> |
459 | +#include <mir/main_loop.h> |
460 | |
461 | // Platform API |
462 | #include <application/ubuntu_application_api_mirserver_priv.h> |
463 | @@ -30,9 +31,11 @@ |
464 | |
465 | // Std |
466 | #include <chrono> |
467 | +#include <csignal> |
468 | #include <thread> |
469 | |
470 | // local |
471 | +#include "logging.h" |
472 | #include "qmirserver.h" |
473 | |
474 | QMirServer::QMirServer(int argc, const char* argv[], QObject *parent) |
475 | @@ -53,19 +56,35 @@ |
476 | auto argv = m_argv; |
477 | auto config = new ShellServerConfiguration(m_argc, m_argv); |
478 | std::thread *t; |
479 | + bool mirServerShutDown = false; |
480 | |
481 | - mir::run_mir(*config, [config, &client, &argc, &argv, &t](mir::DisplayServer& server) { |
482 | + mir::run_mir(*config, [&](mir::DisplayServer &server) { |
483 | ua_ui_mirserver_init(*config); |
484 | |
485 | + // Mir initiates shutdown on SIGINT & SIGTERM, need to distinguish that shutdown from a |
486 | + // client-initiated shutdown. Do this by installing a custom signal handler that is run |
487 | + // after Mir's initiate-shutdown handler is called. |
488 | + config->the_main_loop()->register_signal_handler( |
489 | + {SIGINT, SIGTERM}, |
490 | + [&](int) |
491 | + { |
492 | + DLOG("Mir-initiated shut down in progress"); |
493 | + mirServerShutDown = true; |
494 | + }); |
495 | + |
496 | try { |
497 | t = new std::thread([&]() { |
498 | client (argc, argv, config); |
499 | - server.stop(); |
500 | + if (!mirServerShutDown) { |
501 | + DLOG("Qt-initiated shut down in progress"); |
502 | + server.stop(); |
503 | + } |
504 | }); |
505 | } catch (...) { |
506 | - qDebug() << "Exception caught, quitting"; |
507 | + LOG("Exception caught, quitting"); |
508 | } |
509 | }); |
510 | + |
511 | if (QCoreApplication::instance()) { |
512 | bool aboutToQuitSignaled = false; |
513 | QObject::connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, [&]() { |
514 | |
515 | === modified file 'tests/CMakeLists.txt' |
516 | --- tests/CMakeLists.txt 2014-03-25 11:05:42 +0000 |
517 | +++ tests/CMakeLists.txt 2014-05-20 12:42:58 +0000 |
518 | @@ -24,14 +24,27 @@ |
519 | ${QT_TEST} |
520 | ) |
521 | |
522 | +set( |
523 | + MOCK_HEADERS |
524 | + mock_application_controller.h |
525 | + mock_desktop_file_reader.h |
526 | + mock_focus_controller.h |
527 | + mock_oom_controller.h |
528 | + mock_process_controller.h |
529 | + mock_proc_info.h |
530 | + mock_session.h |
531 | +) |
532 | + |
533 | add_executable( |
534 | application_manager_test |
535 | application_manager_test.cpp |
536 | + ${MOCK_HEADERS} |
537 | ) |
538 | |
539 | add_executable( |
540 | taskcontroller_test |
541 | taskcontroller_test.cpp |
542 | + ${MOCK_HEADERS} |
543 | ) |
544 | |
545 | # We should not need this line according to the Qt5/CMake docs. |
546 | |
547 | === modified file 'tests/application_manager_test.cpp' |
548 | --- tests/application_manager_test.cpp 2014-05-13 17:23:22 +0000 |
549 | +++ tests/application_manager_test.cpp 2014-05-20 12:42:58 +0000 |
550 | @@ -45,18 +45,22 @@ |
551 | QSharedPointer<ProcessController::OomController> ( |
552 | &oomController, |
553 | [](ProcessController::OomController*){}) |
554 | - }, |
555 | - applicationManager{ |
556 | - QSharedPointer<TaskController>{ |
557 | - new TaskController( |
558 | - nullptr, |
559 | - QSharedPointer<ApplicationController>( |
560 | - &appController, |
561 | - [](ApplicationController*){}), |
562 | - QSharedPointer<ProcessController>( |
563 | - &processController, |
564 | - [](ProcessController*){}) |
565 | - )}, |
566 | + } |
567 | + , taskController{ |
568 | + QSharedPointer<TaskController> ( |
569 | + new TaskController( |
570 | + nullptr, |
571 | + QSharedPointer<ApplicationController>( |
572 | + &appController, |
573 | + [](ApplicationController*){}), |
574 | + QSharedPointer<ProcessController>( |
575 | + &processController, |
576 | + [](ProcessController*){}) |
577 | + ) |
578 | + ) |
579 | + } |
580 | + , applicationManager{ |
581 | + taskController, |
582 | QSharedPointer<DesktopFileReader::Factory>( |
583 | &desktopFileReaderFactory, |
584 | [](DesktopFileReader::Factory*){}), |
585 | @@ -72,6 +76,7 @@ |
586 | testing::NiceMock<testing::MockProcInfo> procInfo; |
587 | testing::NiceMock<testing::MockDesktopFileReaderFactory> desktopFileReaderFactory; |
588 | testing::NiceMock<testing::MockFocusController> focusController; |
589 | + QSharedPointer<TaskController> taskController; |
590 | ApplicationManager applicationManager; |
591 | }; |
592 | |
593 | @@ -167,7 +172,7 @@ |
594 | |
595 | // now a second session without desktop file is launched: |
596 | applicationManager.authorizeSession(secondProcId, authed); |
597 | - applicationManager.onProcessStartReportReceived(dialer_app_id, true); |
598 | + applicationManager.onProcessStarting(dialer_app_id); |
599 | |
600 | EXPECT_EQ(false,authed); |
601 | EXPECT_EQ(app,applicationManager.findApplication(dialer_app_id)); |
602 | @@ -191,7 +196,8 @@ |
603 | applicationManager.authorizeSession(procId, authed); |
604 | applicationManager.onSessionStarting(mirSession); |
605 | Application * beforeFailure = applicationManager.findApplication(app_id); |
606 | - applicationManager.onProcessStartReportReceived(app_id,true); |
607 | + applicationManager.onProcessStarting(app_id); |
608 | + applicationManager.onProcessFailed(app_id, true); |
609 | Application * afterFailure = applicationManager.findApplication(app_id); |
610 | |
611 | EXPECT_EQ(true, authed); |
612 | @@ -218,7 +224,8 @@ |
613 | applicationManager.onSessionStarting(mirSession); |
614 | Application * beforeFailure = applicationManager.findApplication(app_id); |
615 | applicationManager.onSessionCreatedSurface(mirSession.get(), aSurface); |
616 | - applicationManager.onProcessStartReportReceived(app_id, true); |
617 | + applicationManager.onProcessStarting(app_id); |
618 | + applicationManager.onProcessFailed(app_id, false); |
619 | Application * afterFailure = applicationManager.findApplication(app_id); |
620 | |
621 | EXPECT_EQ(true, authed); |
622 | @@ -394,7 +401,7 @@ |
623 | ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
624 | |
625 | // mock upstart launching an app which reports itself as sidestage, but we're on phone |
626 | - applicationManager.onProcessStartReportReceived(appId, false); |
627 | + applicationManager.onProcessStarting(appId); |
628 | |
629 | // ensure the app stage is overridden to be main stage |
630 | Application* theApp = applicationManager.findApplication(appId); |
631 | @@ -513,3 +520,1276 @@ |
632 | QList<QVariant> arguments = spy.takeFirst(); // take the first signal |
633 | EXPECT_EQ(arguments.at(0).toString(), "app3"); |
634 | } |
635 | + |
636 | +/* |
637 | + * Test that an application launched by shell itself creates the correct Application instance and |
638 | + * emits signals indicating the model updated |
639 | + */ |
640 | +TEST_F(ApplicationManagerTests,appStartedByShell) |
641 | +{ |
642 | + using namespace ::testing; |
643 | + const QString appId("testAppId"); |
644 | + const QString name("Test App"); |
645 | + |
646 | + // Set up Mocks & signal watcher |
647 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
648 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
649 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
650 | + ON_CALL(*mockDesktopFileReader, name()).WillByDefault(Return(name)); |
651 | + |
652 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
653 | + |
654 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
655 | + .Times(1) |
656 | + .WillOnce(Return(true)); |
657 | + |
658 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
659 | + QSignalSpy addedSpy(&applicationManager, SIGNAL(applicationAdded(const QString &))); |
660 | + |
661 | + // start the application |
662 | + Application *theApp = applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
663 | + |
664 | + // check application data |
665 | + EXPECT_EQ(theApp->state(), Application::Starting); |
666 | + EXPECT_EQ(theApp->appId(), appId); |
667 | + EXPECT_EQ(theApp->name(), name); |
668 | + EXPECT_EQ(theApp->canBeResumed(), true); |
669 | + |
670 | + // check signals were emitted |
671 | + EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) |
672 | + EXPECT_EQ(applicationManager.count(), 1); |
673 | + EXPECT_EQ(addedSpy.count(), 1); |
674 | + EXPECT_EQ(addedSpy.takeFirst().at(0).toString(), appId); |
675 | + |
676 | + // check application in list of apps |
677 | + Application *theAppAgain = applicationManager.findApplication(appId); |
678 | + EXPECT_EQ(theAppAgain, theApp); |
679 | +} |
680 | + |
681 | +/* |
682 | + * Test that an application launched upstart (i.e. not by shell itself) creates the correct Application |
683 | + * instance and emits signals indicating the model updated |
684 | + */ |
685 | +TEST_F(ApplicationManagerTests,appStartedByUpstart) |
686 | +{ |
687 | + using namespace ::testing; |
688 | + const QString appId("testAppId"); |
689 | + const QString name("Test App"); |
690 | + |
691 | + // Set up Mocks & signal watcher |
692 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
693 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
694 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
695 | + ON_CALL(*mockDesktopFileReader, name()).WillByDefault(Return(name)); |
696 | + |
697 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
698 | + |
699 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
700 | + QSignalSpy addedSpy(&applicationManager, SIGNAL(applicationAdded(const QString &))); |
701 | + QSignalSpy focusSpy(&applicationManager, SIGNAL(focusRequested(const QString &))); |
702 | + |
703 | + // upstart sends notification that the application was started |
704 | + applicationManager.onProcessStarting(appId); |
705 | + |
706 | + Application *theApp = applicationManager.findApplication(appId); |
707 | + |
708 | + // check application data |
709 | + EXPECT_EQ(theApp->state(), Application::Starting); |
710 | + EXPECT_EQ(theApp->appId(), appId); |
711 | + EXPECT_EQ(theApp->name(), name); |
712 | + EXPECT_EQ(theApp->canBeResumed(), true); |
713 | + |
714 | + // check signals were emitted |
715 | + EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) |
716 | + EXPECT_EQ(applicationManager.count(), 1); |
717 | + EXPECT_EQ(addedSpy.count(), 1); |
718 | + EXPECT_EQ(addedSpy.takeFirst().at(0).toString(), appId); |
719 | + EXPECT_EQ(focusSpy.count(), 1); |
720 | + EXPECT_EQ(focusSpy.takeFirst().at(0).toString(), appId); |
721 | +} |
722 | + |
723 | +/* |
724 | + * Test that an application launched via the command line with a correct --desktop_file_hint is accepted, |
725 | + * creates the correct Application instance and emits signals indicating the model updated |
726 | + */ |
727 | +TEST_F(ApplicationManagerTests,appStartedUsingCorrectDesktopFileHintSwitch) |
728 | +{ |
729 | + using namespace ::testing; |
730 | + const QString appId("testAppId"); |
731 | + const QString name("Test App"); |
732 | + quint64 procId = 5551; |
733 | + QByteArray cmdLine("/usr/bin/testApp --desktop_file_hint="); |
734 | + cmdLine = cmdLine.append(appId); |
735 | + |
736 | + // Set up Mocks & signal watcher |
737 | + EXPECT_CALL(procInfo,command_line(procId)) |
738 | + .Times(1) |
739 | + .WillOnce(Return(cmdLine)); |
740 | + |
741 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
742 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
743 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
744 | + ON_CALL(*mockDesktopFileReader, name()).WillByDefault(Return(name)); |
745 | + |
746 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
747 | + |
748 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
749 | + QSignalSpy addedSpy(&applicationManager, SIGNAL(applicationAdded(const QString &))); |
750 | + |
751 | + // Mir requests authentication for an application that was started |
752 | + bool authed = false; |
753 | + applicationManager.authorizeSession(procId, authed); |
754 | + EXPECT_EQ(authed, true); |
755 | + |
756 | + Application *theApp = applicationManager.findApplication(appId); |
757 | + |
758 | + // check application data |
759 | + EXPECT_EQ(theApp->state(), Application::Starting); |
760 | + EXPECT_EQ(theApp->appId(), appId); |
761 | + EXPECT_EQ(theApp->name(), name); |
762 | + EXPECT_EQ(theApp->canBeResumed(), false); |
763 | + |
764 | + // check signals were emitted |
765 | + EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) |
766 | + EXPECT_EQ(applicationManager.count(), 1); |
767 | + EXPECT_EQ(addedSpy.count(), 1); |
768 | + EXPECT_EQ(addedSpy.takeFirst().at(0).toString(), appId); |
769 | +} |
770 | + |
771 | +/* |
772 | + * Test that an application launched via the command line without the correct --desktop_file_hint is rejected |
773 | + */ |
774 | +TEST_F(ApplicationManagerTests,appDoesNotStartWhenUsingBadDesktopFileHintSwitch) |
775 | +{ |
776 | + using namespace ::testing; |
777 | + const QString appId("testAppId"); |
778 | + const QString name("Test App"); |
779 | + quint64 procId = 5551; |
780 | + QByteArray cmdLine("/usr/bin/testApp"); |
781 | + |
782 | + // Set up Mocks & signal watcher |
783 | + EXPECT_CALL(procInfo,command_line(procId)) |
784 | + .Times(1) |
785 | + .WillOnce(Return(cmdLine)); |
786 | + |
787 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
788 | + QSignalSpy addedSpy(&applicationManager, SIGNAL(applicationAdded(const QString &))); |
789 | + |
790 | + // Mir requests authentication for an application that was started |
791 | + bool authed = true; |
792 | + applicationManager.authorizeSession(procId, authed); |
793 | + EXPECT_EQ(authed, false); |
794 | + |
795 | + Application *theApp = applicationManager.findApplication(appId); |
796 | + |
797 | + EXPECT_EQ(theApp, nullptr); |
798 | + |
799 | + // check no new signals were emitted |
800 | + EXPECT_EQ(countSpy.count(), 0); |
801 | + EXPECT_EQ(applicationManager.count(), 0); |
802 | + EXPECT_EQ(addedSpy.count(), 0); |
803 | +} |
804 | + |
805 | +/* |
806 | + * Test that an application launched via the command line with the --desktop_file_hint but an incorrect |
807 | + * desktop file specified is rejected |
808 | + */ |
809 | +TEST_F(ApplicationManagerTests,appDoesNotStartWhenUsingBadDesktopFileHintFile) |
810 | +{ |
811 | + using namespace ::testing; |
812 | + const QString appId("testAppId"); |
813 | + const QString badDesktopFile = QString("%1.desktop").arg(appId); |
814 | + quint64 procId = 5551; |
815 | + QByteArray cmdLine("/usr/bin/testApp --desktop_file_hint="); |
816 | + cmdLine = cmdLine.append(badDesktopFile); |
817 | + |
818 | + // Set up Mocks & signal watcher |
819 | + EXPECT_CALL(procInfo,command_line(procId)) |
820 | + .Times(1) |
821 | + .WillOnce(Return(cmdLine)); |
822 | + |
823 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
824 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(false)); |
825 | + |
826 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
827 | + |
828 | + // Mir requests authentication for an application that was started, should fail |
829 | + bool authed = true; |
830 | + applicationManager.authorizeSession(procId, authed); |
831 | + EXPECT_EQ(authed, false); |
832 | +} |
833 | + |
834 | +/* |
835 | + * Test that the child sessions of a webapp process are accepted |
836 | + */ |
837 | +TEST_F(ApplicationManagerTests,webAppSecondarySessionsAccepted) |
838 | +{ |
839 | + using namespace ::testing; |
840 | + quint64 procId = 5551; |
841 | + QByteArray cmdLine("/usr/bin/qt5/libexec/QtWebProcess"); |
842 | + |
843 | + // Set up Mocks & signal watcher |
844 | + EXPECT_CALL(procInfo,command_line(procId)) |
845 | + .Times(1) |
846 | + .WillOnce(Return(cmdLine)); |
847 | + |
848 | + bool authed = false; |
849 | + applicationManager.authorizeSession(procId, authed); |
850 | + EXPECT_EQ(authed, true); |
851 | +} |
852 | + |
853 | +/* |
854 | + * Test that signon-ui sessions are accepted |
855 | + */ |
856 | +TEST_F(ApplicationManagerTests,signonUiSessionsAccepted) |
857 | +{ |
858 | + using namespace ::testing; |
859 | + quint64 procId = 5151; |
860 | + QByteArray cmdLine("/usr/bin/signon-ui --blah=http://signon-ui"); |
861 | + |
862 | + // Set up Mocks & signal watcher |
863 | + EXPECT_CALL(procInfo,command_line(procId)) |
864 | + .Times(1) |
865 | + .WillOnce(Return(cmdLine)); |
866 | + |
867 | + bool authed = false; |
868 | + applicationManager.authorizeSession(procId, authed); |
869 | + EXPECT_EQ(authed, true); |
870 | +} |
871 | + |
872 | +/* |
873 | + * Test that maliit sessions are accepted |
874 | + */ |
875 | +TEST_F(ApplicationManagerTests,maliitSessionsAccepted) |
876 | +{ |
877 | + using namespace ::testing; |
878 | + quint64 procId = 151; |
879 | + QByteArray cmdLine("maliit-server --blah"); |
880 | + |
881 | + // Set up Mocks & signal watcher |
882 | + EXPECT_CALL(procInfo,command_line(procId)) |
883 | + .Times(1) |
884 | + .WillOnce(Return(cmdLine)); |
885 | + |
886 | + bool authed = false; |
887 | + applicationManager.authorizeSession(procId, authed); |
888 | + EXPECT_EQ(authed, true); |
889 | +} |
890 | + |
891 | +/* |
892 | + * Test that an application in the Starting state is not impacted by the upstart "Starting" message |
893 | + * for that application (i.e. the upstart message is effectively useless) |
894 | + */ |
895 | +TEST_F(ApplicationManagerTests,onceAppAddedToApplicationLists_upstartStartingEventIgnored) |
896 | +{ |
897 | + using namespace ::testing; |
898 | + const QString appId("testAppId"); |
899 | + |
900 | + // Set up Mocks & signal watcher |
901 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
902 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
903 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
904 | + |
905 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
906 | + |
907 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
908 | + .Times(1) |
909 | + .WillOnce(Return(true)); |
910 | + |
911 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
912 | + |
913 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
914 | + QSignalSpy addedSpy(&applicationManager, SIGNAL(applicationAdded(const QString &))); |
915 | + |
916 | + // upstart sends notification that the application was started |
917 | + applicationManager.onProcessStarting(appId); |
918 | + |
919 | + // check no new signals were emitted and application state unchanged |
920 | + EXPECT_EQ(countSpy.count(), 0); |
921 | + EXPECT_EQ(applicationManager.count(), 1); |
922 | + EXPECT_EQ(addedSpy.count(), 0); |
923 | + |
924 | + Application *theApp = applicationManager.findApplication(appId); |
925 | + EXPECT_EQ(Application::Starting, theApp->state()); |
926 | +} |
927 | + |
928 | +/* |
929 | + * Test that an application in the Starting state reacts correctly to the Mir sessionStarted |
930 | + * event for that application (i.e. the Session is associated) |
931 | + */ |
932 | +TEST_F(ApplicationManagerTests,onceAppAddedToApplicationLists_mirSessionStartingEventHandled) |
933 | +{ |
934 | + using namespace ::testing; |
935 | + const QString appId("testAppId"); |
936 | + quint64 procId = 5551; |
937 | + |
938 | + // Set up Mocks & signal watcher |
939 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
940 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
941 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
942 | + |
943 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
944 | + |
945 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
946 | + .Times(1) |
947 | + .WillOnce(Return(true)); |
948 | + |
949 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
950 | + applicationManager.onProcessStarting(appId); |
951 | + |
952 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
953 | + QSignalSpy addedSpy(&applicationManager, SIGNAL(applicationAdded(const QString &))); |
954 | + |
955 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
956 | + |
957 | + // Authorize session and emit Mir sessionStarting event |
958 | + bool authed = true; |
959 | + applicationManager.authorizeSession(procId, authed); |
960 | + applicationManager.onSessionStarting(session); |
961 | + |
962 | + EXPECT_EQ(countSpy.count(), 0); |
963 | + EXPECT_EQ(applicationManager.count(), 1); |
964 | + EXPECT_EQ(addedSpy.count(), 0); |
965 | + |
966 | + // Check application state and session are correctly set |
967 | + Application *theApp = applicationManager.findApplication(appId); |
968 | + EXPECT_EQ(theApp->session(), session); |
969 | + EXPECT_EQ(theApp->focused(), false); |
970 | +} |
971 | + |
972 | +/* |
973 | + * Test that an application in the Starting state reacts correctly to the Mir surfaceCreated |
974 | + * event for that application (i.e. the Surface is associated and state set to Running) |
975 | + */ |
976 | +TEST_F(ApplicationManagerTests,onceAppAddedToApplicationLists_mirSurfaceCreatedEventHandled) |
977 | +{ |
978 | + using namespace ::testing; |
979 | + const QString appId("testAppId"); |
980 | + quint64 procId = 5551; |
981 | + |
982 | + // Set up Mocks & signal watcher |
983 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
984 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
985 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
986 | + |
987 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
988 | + |
989 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
990 | + .Times(1) |
991 | + .WillOnce(Return(true)); |
992 | + |
993 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
994 | + applicationManager.onProcessStarting(appId); |
995 | + |
996 | + QSignalSpy dataSpy(&applicationManager, SIGNAL(dataChanged(QModelIndex,QModelIndex))); |
997 | + QSignalSpy focusSpy(&applicationManager, SIGNAL(focusedApplicationIdChanged())); |
998 | + |
999 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1000 | + |
1001 | + bool authed = true; |
1002 | + applicationManager.authorizeSession(procId, authed); |
1003 | + applicationManager.onSessionStarting(session); |
1004 | + |
1005 | + std::shared_ptr<mir::scene::Surface> surface(nullptr); |
1006 | + |
1007 | + applicationManager.onSessionCreatedSurface(session.get(), surface); |
1008 | + |
1009 | + EXPECT_EQ(dataSpy.count(), 1); |
1010 | + EXPECT_EQ(focusSpy.count(), 1); |
1011 | + |
1012 | + // Check application state is correctly set |
1013 | + Application *theApp = applicationManager.findApplication(appId); |
1014 | + EXPECT_EQ(theApp->state(), Application::Running); |
1015 | + EXPECT_EQ(theApp->focused(), true); |
1016 | + |
1017 | + EXPECT_EQ(applicationManager.focusedApplicationId(), appId); |
1018 | +} |
1019 | + |
1020 | +/* |
1021 | + * Test that an application is stopped correctly, if it has not yet created a surface (still in Starting state) |
1022 | + */ |
1023 | +TEST_F(ApplicationManagerTests,shellStopsAppCorrectlyBeforeSurfaceCreated) |
1024 | +{ |
1025 | + using namespace ::testing; |
1026 | + const QString appId("testAppId"); |
1027 | + quint64 procId = 5551; |
1028 | + |
1029 | + // Set up Mocks & signal watcher |
1030 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1031 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1032 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1033 | + |
1034 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1035 | + |
1036 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1037 | + .Times(1) |
1038 | + .WillOnce(Return(true)); |
1039 | + |
1040 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1041 | + applicationManager.onProcessStarting(appId); |
1042 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1043 | + bool authed = true; |
1044 | + applicationManager.authorizeSession(procId, authed); |
1045 | + applicationManager.onSessionStarting(session); |
1046 | + |
1047 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1048 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1049 | + |
1050 | + // Stop app |
1051 | + applicationManager.stopApplication(appId); |
1052 | + |
1053 | + EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) |
1054 | + EXPECT_EQ(applicationManager.count(), 0); |
1055 | + EXPECT_EQ(removedSpy.count(), 1); |
1056 | + EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId); |
1057 | +} |
1058 | + |
1059 | +/* |
1060 | + * Test that the foreground application is stopped correctly (is in Running state, has surface) |
1061 | + */ |
1062 | +TEST_F(ApplicationManagerTests,shellStopsForegroundAppCorrectly) |
1063 | +{ |
1064 | + using namespace ::testing; |
1065 | + const QString appId("testAppId"); |
1066 | + quint64 procId = 5551; |
1067 | + |
1068 | + // Set up Mocks & signal watcher |
1069 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1070 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1071 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1072 | + |
1073 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1074 | + |
1075 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1076 | + .Times(1) |
1077 | + .WillOnce(Return(true)); |
1078 | + |
1079 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1080 | + applicationManager.onProcessStarting(appId); |
1081 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1082 | + bool authed = true; |
1083 | + applicationManager.authorizeSession(procId, authed); |
1084 | + applicationManager.onSessionStarting(session); |
1085 | + |
1086 | + std::shared_ptr<mir::scene::Surface> surface(nullptr); |
1087 | + applicationManager.onSessionCreatedSurface(session.get(), surface); |
1088 | + EXPECT_EQ(applicationManager.focusedApplicationId(), appId); |
1089 | + |
1090 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1091 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1092 | + |
1093 | + // Stop app |
1094 | + applicationManager.stopApplication(appId); |
1095 | + |
1096 | + EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) |
1097 | + EXPECT_EQ(applicationManager.count(), 0); |
1098 | + EXPECT_EQ(removedSpy.count(), 1); |
1099 | + EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId); |
1100 | +} |
1101 | + |
1102 | +/* |
1103 | + * Test that the background application is stopped correctly |
1104 | + */ |
1105 | +TEST_F(ApplicationManagerTests,shellStopsBackgroundAppCorrectly) |
1106 | +{ |
1107 | + using namespace ::testing; |
1108 | + const QString appId("testAppId"); |
1109 | + quint64 procId = 5551; |
1110 | + |
1111 | + // Set up Mocks & signal watcher |
1112 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1113 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1114 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1115 | + |
1116 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1117 | + |
1118 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1119 | + .Times(1) |
1120 | + .WillOnce(Return(true)); |
1121 | + |
1122 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1123 | + applicationManager.onProcessStarting(appId); |
1124 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1125 | + bool authed = true; |
1126 | + applicationManager.authorizeSession(procId, authed); |
1127 | + applicationManager.onSessionStarting(session); |
1128 | + |
1129 | + std::shared_ptr<mir::scene::Surface> surface(nullptr); |
1130 | + applicationManager.onSessionCreatedSurface(session.get(), surface); |
1131 | + applicationManager.onSessionUnfocused(); |
1132 | + |
1133 | + EXPECT_EQ(applicationManager.focusedApplicationId(), QString()); |
1134 | + |
1135 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1136 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1137 | + |
1138 | + // Stop app |
1139 | + applicationManager.stopApplication(appId); |
1140 | + |
1141 | + EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) |
1142 | + EXPECT_EQ(applicationManager.count(), 0); |
1143 | + EXPECT_EQ(removedSpy.count(), 1); |
1144 | + EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId); |
1145 | +} |
1146 | + |
1147 | +/* |
1148 | + * Test that if an application is stopped by upstart, before it has created a surface, AppMan cleans up after it ok |
1149 | + */ |
1150 | +TEST_F(ApplicationManagerTests,upstartNotificationOfStartingAppBeingStopped) |
1151 | +{ |
1152 | + using namespace ::testing; |
1153 | + const QString appId("testAppId"); |
1154 | + quint64 procId = 5551; |
1155 | + |
1156 | + // Set up Mocks & signal watcher |
1157 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1158 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1159 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1160 | + |
1161 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1162 | + |
1163 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1164 | + .Times(1) |
1165 | + .WillOnce(Return(true)); |
1166 | + |
1167 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1168 | + applicationManager.onProcessStarting(appId); |
1169 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1170 | + bool authed = true; |
1171 | + applicationManager.authorizeSession(procId, authed); |
1172 | + applicationManager.onSessionStarting(session); |
1173 | + |
1174 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1175 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1176 | + |
1177 | + // Upstart notifies of stopping app |
1178 | + applicationManager.onProcessStopped(appId); |
1179 | + |
1180 | + EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) |
1181 | + EXPECT_EQ(applicationManager.count(), 0); |
1182 | + EXPECT_EQ(removedSpy.count(), 1); |
1183 | + EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId); |
1184 | +} |
1185 | + |
1186 | +/* |
1187 | + * Test that if the foreground Running application is stopped by upstart, AppMan cleans up after it ok |
1188 | + */ |
1189 | +TEST_F(ApplicationManagerTests,upstartNotifiesOfStoppingForegroundApp) |
1190 | +{ |
1191 | + using namespace ::testing; |
1192 | + const QString appId("testAppId"); |
1193 | + quint64 procId = 5551; |
1194 | + |
1195 | + // Set up Mocks & signal watcher |
1196 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1197 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1198 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1199 | + |
1200 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1201 | + |
1202 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1203 | + .Times(1) |
1204 | + .WillOnce(Return(true)); |
1205 | + |
1206 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1207 | + applicationManager.onProcessStarting(appId); |
1208 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1209 | + bool authed = true; |
1210 | + applicationManager.authorizeSession(procId, authed); |
1211 | + applicationManager.onSessionStarting(session); |
1212 | + |
1213 | + std::shared_ptr<mir::scene::Surface> surface(nullptr); |
1214 | + applicationManager.onSessionCreatedSurface(session.get(), surface); |
1215 | + EXPECT_EQ(applicationManager.focusedApplicationId(), appId); |
1216 | + |
1217 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1218 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1219 | + |
1220 | + // Upstart notifies of stopping app |
1221 | + applicationManager.onProcessStopped(appId); |
1222 | + |
1223 | + EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) |
1224 | + EXPECT_EQ(applicationManager.count(), 0); |
1225 | + EXPECT_EQ(removedSpy.count(), 1); |
1226 | + EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId); |
1227 | +} |
1228 | + |
1229 | +/* |
1230 | + * Test that if the foreground Running application is reported to unexpectedly stop by upstart, AppMan |
1231 | + * cleans up after it ok (as was not in background, had not lifecycle saved its state, so cannot be resumed) |
1232 | + */ |
1233 | +TEST_F(ApplicationManagerTests,upstartNotifiesOfUnexpectedStopOfForegroundApp) |
1234 | +{ |
1235 | + using namespace ::testing; |
1236 | + const QString appId("testAppId"); |
1237 | + quint64 procId = 5551; |
1238 | + |
1239 | + // Set up Mocks & signal watcher |
1240 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1241 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1242 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1243 | + |
1244 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1245 | + |
1246 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1247 | + .Times(1) |
1248 | + .WillOnce(Return(true)); |
1249 | + |
1250 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1251 | + applicationManager.onProcessStarting(appId); |
1252 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1253 | + bool authed = true; |
1254 | + applicationManager.authorizeSession(procId, authed); |
1255 | + applicationManager.onSessionStarting(session); |
1256 | + |
1257 | + std::shared_ptr<mir::scene::Surface> surface(nullptr); |
1258 | + applicationManager.onSessionCreatedSurface(session.get(), surface); |
1259 | + EXPECT_EQ(applicationManager.focusedApplicationId(), appId); |
1260 | + |
1261 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1262 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1263 | + |
1264 | + // Upstart notifies of crashing / OOM killed app |
1265 | + applicationManager.onProcessFailed(appId, false); |
1266 | + |
1267 | + // Upstart finally notifies the app stopped |
1268 | + applicationManager.onProcessStopped(appId); |
1269 | + |
1270 | + EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) |
1271 | + EXPECT_EQ(applicationManager.count(), 0); |
1272 | + EXPECT_EQ(removedSpy.count(), 1); |
1273 | + EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId); |
1274 | +} |
1275 | + |
1276 | +/* |
1277 | + * Test that if a background application is stopped by upstart, AppMan removes it from the app list |
1278 | + * as the event is a result of direct user interaction |
1279 | + */ |
1280 | +TEST_F(ApplicationManagerTests,upstartNotifiesOfStoppingBackgroundApp) |
1281 | +{ |
1282 | + using namespace ::testing; |
1283 | + const QString appId("testAppId"); |
1284 | + quint64 procId = 5551; |
1285 | + |
1286 | + // Set up Mocks & signal watcher |
1287 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1288 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1289 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1290 | + |
1291 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1292 | + |
1293 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1294 | + .Times(1) |
1295 | + .WillOnce(Return(true)); |
1296 | + |
1297 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1298 | + applicationManager.onProcessStarting(appId); |
1299 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1300 | + bool authed = true; |
1301 | + applicationManager.authorizeSession(procId, authed); |
1302 | + applicationManager.onSessionStarting(session); |
1303 | + |
1304 | + std::shared_ptr<mir::scene::Surface> surface(nullptr); |
1305 | + applicationManager.onSessionCreatedSurface(session.get(), surface); |
1306 | + applicationManager.onSessionUnfocused(); |
1307 | + EXPECT_EQ(applicationManager.focusedApplicationId(), QString()); |
1308 | + |
1309 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1310 | + QSignalSpy focusSpy(&applicationManager, SIGNAL(focusedApplicationIdChanged())); |
1311 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1312 | + |
1313 | + // Upstart notifies of stopping app |
1314 | + applicationManager.onProcessStopped(appId); |
1315 | + |
1316 | + EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) |
1317 | + EXPECT_EQ(applicationManager.count(), 0); |
1318 | + EXPECT_EQ(focusSpy.count(), 0); |
1319 | + EXPECT_EQ(removedSpy.count(), 1); |
1320 | + EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId); |
1321 | +} |
1322 | + |
1323 | +/* |
1324 | + * Test that if a background application is reported to unexpectedly stop by upstart, AppMan does not remove |
1325 | + * it from the app lists but instead considers it Stopped, ready to be resumed. This is due to the fact the |
1326 | + * app should have saved its state, so can be resumed. This situation can occur due to the OOM killer, or |
1327 | + * a dodgy app crashing. |
1328 | + */ |
1329 | +TEST_F(ApplicationManagerTests,unexpectedStopOfBackgroundApp) |
1330 | +{ |
1331 | + using namespace ::testing; |
1332 | + const QString appId("testAppId"); |
1333 | + quint64 procId = 5551; |
1334 | + |
1335 | + // Set up Mocks & signal watcher |
1336 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1337 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1338 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1339 | + |
1340 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1341 | + |
1342 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1343 | + .Times(1) |
1344 | + .WillOnce(Return(true)); |
1345 | + |
1346 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1347 | + applicationManager.onProcessStarting(appId); |
1348 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1349 | + bool authed = true; |
1350 | + applicationManager.authorizeSession(procId, authed); |
1351 | + applicationManager.onSessionStarting(session); |
1352 | + |
1353 | + std::shared_ptr<mir::scene::Surface> surface(nullptr); |
1354 | + applicationManager.onSessionCreatedSurface(session.get(), surface); |
1355 | + applicationManager.onSessionUnfocused(); |
1356 | + EXPECT_EQ(applicationManager.focusedApplicationId(), QString()); |
1357 | + |
1358 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1359 | + QSignalSpy focusSpy(&applicationManager, SIGNAL(focusedApplicationIdChanged())); |
1360 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1361 | + |
1362 | + // Mir reports disconnection |
1363 | + applicationManager.onSessionStopping(session); |
1364 | + |
1365 | + // Upstart notifies of crashing / OOM-killed app |
1366 | + applicationManager.onProcessFailed(appId, false); |
1367 | + |
1368 | + EXPECT_EQ(focusSpy.count(), 0); |
1369 | + |
1370 | + // Upstart finally notifies the app stopped |
1371 | + applicationManager.onProcessStopped(appId); |
1372 | + |
1373 | + EXPECT_EQ(countSpy.count(), 0); |
1374 | + EXPECT_EQ(applicationManager.count(), 1); |
1375 | + EXPECT_EQ(removedSpy.count(), 0); |
1376 | +} |
1377 | + |
1378 | +/* |
1379 | + * Test that if a background application is reported to have stopped on startup by upstart, that it |
1380 | + * is kept in the application model, as the app can be lifecycle resumed. |
1381 | + * |
1382 | + * Note that upstart reports this "stopped on startup" even for applications which are running. |
1383 | + * This may be an upstart bug, or AppMan mis-understanding upstart's intentions. But need to check |
1384 | + * we're doing the right thing in either case. CHECKME(greyback)! |
1385 | + */ |
1386 | +TEST_F(ApplicationManagerTests,unexpectedStopOfBackgroundAppCheckingUpstartBug) |
1387 | +{ |
1388 | + using namespace ::testing; |
1389 | + const QString appId("testAppId"); |
1390 | + quint64 procId = 5551; |
1391 | + |
1392 | + // Set up Mocks & signal watcher |
1393 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1394 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1395 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1396 | + |
1397 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1398 | + |
1399 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1400 | + .Times(1) |
1401 | + .WillOnce(Return(true)); |
1402 | + |
1403 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1404 | + applicationManager.onProcessStarting(appId); |
1405 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1406 | + bool authed = true; |
1407 | + applicationManager.authorizeSession(procId, authed); |
1408 | + applicationManager.onSessionStarting(session); |
1409 | + |
1410 | + std::shared_ptr<mir::scene::Surface> surface(nullptr); |
1411 | + applicationManager.onSessionCreatedSurface(session.get(), surface); |
1412 | + applicationManager.onSessionUnfocused(); |
1413 | + EXPECT_EQ(applicationManager.focusedApplicationId(), QString()); |
1414 | + |
1415 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1416 | + QSignalSpy focusSpy(&applicationManager, SIGNAL(focusedApplicationIdChanged())); |
1417 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1418 | + |
1419 | + // Mir reports disconnection |
1420 | + applicationManager.onSessionStopping(session); |
1421 | + |
1422 | + // Upstart notifies of crashing app |
1423 | + applicationManager.onProcessFailed(appId, true); |
1424 | + |
1425 | + EXPECT_EQ(focusSpy.count(), 0); |
1426 | + |
1427 | + // Upstart finally notifies the app stopped |
1428 | + applicationManager.onProcessStopped(appId); |
1429 | + |
1430 | + EXPECT_EQ(countSpy.count(), 0); |
1431 | + EXPECT_EQ(applicationManager.count(), 1); |
1432 | + EXPECT_EQ(removedSpy.count(), 0); |
1433 | +} |
1434 | + |
1435 | +/* |
1436 | + * Test that if a Starting application is then reported to be stopping by Mir, AppMan cleans up after it ok |
1437 | + */ |
1438 | +TEST_F(ApplicationManagerTests,mirNotifiesStartingAppIsNowStopping) |
1439 | +{ |
1440 | + using namespace ::testing; |
1441 | + const QString appId("testAppId"); |
1442 | + quint64 procId = 5551; |
1443 | + |
1444 | + // Set up Mocks & signal watcher |
1445 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1446 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1447 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1448 | + |
1449 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1450 | + |
1451 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1452 | + .Times(1) |
1453 | + .WillOnce(Return(true)); |
1454 | + |
1455 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1456 | + applicationManager.onProcessStarting(appId); |
1457 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1458 | + bool authed = true; |
1459 | + applicationManager.authorizeSession(procId, authed); |
1460 | + applicationManager.onSessionStarting(session); |
1461 | + |
1462 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1463 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1464 | + |
1465 | + // Mir notifies of stopping app |
1466 | + applicationManager.onSessionStopping(session); |
1467 | + |
1468 | + EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) |
1469 | + EXPECT_EQ(applicationManager.count(), 0); |
1470 | + EXPECT_EQ(removedSpy.count(), 1); |
1471 | + EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId); |
1472 | +} |
1473 | + |
1474 | +/* |
1475 | + * Test that if a Running foreground application is reported to be stopping by Mir, AppMan cleans up after it ok |
1476 | + */ |
1477 | +TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingForegroundApp) |
1478 | +{ |
1479 | + using namespace ::testing; |
1480 | + const QString appId("testAppId"); |
1481 | + quint64 procId = 5551; |
1482 | + |
1483 | + // Set up Mocks & signal watcher |
1484 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1485 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1486 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1487 | + |
1488 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1489 | + |
1490 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1491 | + .Times(1) |
1492 | + .WillOnce(Return(true)); |
1493 | + |
1494 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1495 | + applicationManager.onProcessStarting(appId); |
1496 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1497 | + bool authed = true; |
1498 | + applicationManager.authorizeSession(procId, authed); |
1499 | + applicationManager.onSessionStarting(session); |
1500 | + |
1501 | + // Associate a surface so AppMan considers app Running, check focused |
1502 | + std::shared_ptr<mir::scene::Surface> surface(nullptr); |
1503 | + applicationManager.onSessionCreatedSurface(session.get(), surface); |
1504 | + EXPECT_EQ(applicationManager.focusedApplicationId(), appId); |
1505 | + |
1506 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1507 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1508 | + |
1509 | + // Mir notifies of stopping app |
1510 | + applicationManager.onSessionStopping(session); |
1511 | + |
1512 | + EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) |
1513 | + EXPECT_EQ(applicationManager.count(), 0); |
1514 | + EXPECT_EQ(removedSpy.count(), 1); |
1515 | + EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId); |
1516 | +} |
1517 | + |
1518 | +/* |
1519 | + * Test that if a foreground application (one launched via desktop_file_hint) is reported to be stopping by |
1520 | + * Mir, AppMan removes it from the model immediately |
1521 | + */ |
1522 | +TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingForegroundAppLaunchedWithDesktopFileHint) |
1523 | +{ |
1524 | + using namespace ::testing; |
1525 | + const QString appId("testAppId"); |
1526 | + const QString name("Test App"); |
1527 | + quint64 procId = 5551; |
1528 | + QByteArray cmdLine("/usr/bin/testApp --desktop_file_hint="); |
1529 | + cmdLine = cmdLine.append(appId); |
1530 | + |
1531 | + // Set up Mocks & signal watcher |
1532 | + EXPECT_CALL(procInfo,command_line(procId)) |
1533 | + .Times(1) |
1534 | + .WillOnce(Return(cmdLine)); |
1535 | + |
1536 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1537 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1538 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1539 | + ON_CALL(*mockDesktopFileReader, name()).WillByDefault(Return(name)); |
1540 | + |
1541 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1542 | + |
1543 | + // Mir requests authentication for an application that was started |
1544 | + bool authed = true; |
1545 | + applicationManager.authorizeSession(procId, authed); |
1546 | + EXPECT_EQ(authed, true); |
1547 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1548 | + applicationManager.onSessionStarting(session); |
1549 | + |
1550 | + // Associate a surface so AppMan considers app Running, check focused |
1551 | + std::shared_ptr<mir::scene::Surface> surface(nullptr); |
1552 | + applicationManager.onSessionCreatedSurface(session.get(), surface); |
1553 | + EXPECT_EQ(applicationManager.focusedApplicationId(), appId); |
1554 | + |
1555 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1556 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1557 | + |
1558 | + // Mir notifies of stopping app |
1559 | + applicationManager.onSessionStopping(session); |
1560 | + |
1561 | + EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) |
1562 | + EXPECT_EQ(applicationManager.count(), 0); |
1563 | + EXPECT_EQ(removedSpy.count(), 1); |
1564 | + |
1565 | + Application *app = applicationManager.findApplication(appId); |
1566 | + EXPECT_EQ(nullptr, app); |
1567 | +} |
1568 | + |
1569 | +/* |
1570 | + * Test that if a background application is reported to be stopping by Mir, AppMan sets its state to Stopped |
1571 | + * but does not remove it from the model |
1572 | + */ |
1573 | +TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingBackgroundApp) |
1574 | +{ |
1575 | + using namespace ::testing; |
1576 | + const QString appId("testAppId"); |
1577 | + quint64 procId = 5551; |
1578 | + |
1579 | + // Set up Mocks & signal watcher |
1580 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1581 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1582 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1583 | + |
1584 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1585 | + |
1586 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1587 | + .Times(1) |
1588 | + .WillOnce(Return(true)); |
1589 | + |
1590 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1591 | + applicationManager.onProcessStarting(appId); |
1592 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1593 | + bool authed = true; |
1594 | + applicationManager.authorizeSession(procId, authed); |
1595 | + applicationManager.onSessionStarting(session); |
1596 | + |
1597 | + // Associate a surface so AppMan considers app Running, check in background |
1598 | + std::shared_ptr<mir::scene::Surface> surface(nullptr); |
1599 | + applicationManager.onSessionCreatedSurface(session.get(), surface); |
1600 | + applicationManager.onSessionUnfocused(); |
1601 | + EXPECT_EQ(applicationManager.focusedApplicationId(), QString()); |
1602 | + |
1603 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1604 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1605 | + |
1606 | + // Mir notifies of stopping app |
1607 | + applicationManager.onSessionStopping(session); |
1608 | + |
1609 | + EXPECT_EQ(countSpy.count(), 0); |
1610 | + EXPECT_EQ(applicationManager.count(), 1); |
1611 | + EXPECT_EQ(removedSpy.count(), 0); |
1612 | + |
1613 | + Application * app = applicationManager.findApplication(appId); |
1614 | + EXPECT_NE(nullptr,app); |
1615 | + EXPECT_EQ(app->state(), Application::Stopped); |
1616 | +} |
1617 | + |
1618 | +/* |
1619 | + * Test that if a background application (one launched via desktop_file_hint) is reported to be stopping by |
1620 | + * Mir, AppMan removes it from the model immediately |
1621 | + */ |
1622 | +TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingBackgroundAppLaunchedWithDesktopFileHint) |
1623 | +{ |
1624 | + using namespace ::testing; |
1625 | + const QString appId("testAppId"); |
1626 | + const QString name("Test App"); |
1627 | + quint64 procId = 5551; |
1628 | + QByteArray cmdLine("/usr/bin/testApp --desktop_file_hint="); |
1629 | + cmdLine = cmdLine.append(appId); |
1630 | + |
1631 | + // Set up Mocks & signal watcher |
1632 | + EXPECT_CALL(procInfo,command_line(procId)) |
1633 | + .Times(1) |
1634 | + .WillOnce(Return(cmdLine)); |
1635 | + |
1636 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1637 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1638 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1639 | + ON_CALL(*mockDesktopFileReader, name()).WillByDefault(Return(name)); |
1640 | + |
1641 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1642 | + |
1643 | + // Mir requests authentication for an application that was started |
1644 | + bool authed = true; |
1645 | + applicationManager.authorizeSession(procId, authed); |
1646 | + EXPECT_EQ(authed, true); |
1647 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1648 | + applicationManager.onSessionStarting(session); |
1649 | + |
1650 | + // Associate a surface so AppMan considers app Running, check in background |
1651 | + std::shared_ptr<mir::scene::Surface> surface(nullptr); |
1652 | + applicationManager.onSessionCreatedSurface(session.get(), surface); |
1653 | + applicationManager.onSessionUnfocused(); |
1654 | + EXPECT_EQ(applicationManager.focusedApplicationId(), QString()); |
1655 | + |
1656 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1657 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1658 | + |
1659 | + // Mir notifies of stopping app |
1660 | + applicationManager.onSessionStopping(session); |
1661 | + |
1662 | + EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) |
1663 | + EXPECT_EQ(applicationManager.count(), 0); |
1664 | + EXPECT_EQ(removedSpy.count(), 1); |
1665 | + |
1666 | + Application * app = applicationManager.findApplication(appId); |
1667 | + EXPECT_EQ(nullptr,app); |
1668 | +} |
1669 | + |
1670 | +/* |
1671 | + * Test that when an application is stopped correctly by shell, the upstart stopping event is ignored |
1672 | + */ |
1673 | +TEST_F(ApplicationManagerTests,shellStoppedApp_upstartStoppingEventIgnored) |
1674 | +{ |
1675 | + using namespace ::testing; |
1676 | + const QString appId("testAppId"); |
1677 | + quint64 procId = 5551; |
1678 | + |
1679 | + // Set up Mocks & signal watcher |
1680 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1681 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1682 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1683 | + |
1684 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1685 | + |
1686 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1687 | + .Times(1) |
1688 | + .WillOnce(Return(true)); |
1689 | + |
1690 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1691 | + applicationManager.onProcessStarting(appId); |
1692 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1693 | + bool authed = true; |
1694 | + applicationManager.authorizeSession(procId, authed); |
1695 | + applicationManager.onSessionStarting(session); |
1696 | + |
1697 | + applicationManager.stopApplication(appId); |
1698 | + |
1699 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1700 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1701 | + |
1702 | + // Upstart notifies of stopping app |
1703 | + applicationManager.onProcessStopped(appId); |
1704 | + |
1705 | + EXPECT_EQ(countSpy.count(), 0); |
1706 | + EXPECT_EQ(removedSpy.count(), 0); |
1707 | +} |
1708 | + |
1709 | +/* |
1710 | + * Test that when an application is stopped correctly by shell, the Mir Session stopped event is ignored |
1711 | + */ |
1712 | +TEST_F(ApplicationManagerTests,shellStoppedApp_mirSessionStoppingEventIgnored) |
1713 | +{ |
1714 | + using namespace ::testing; |
1715 | + const QString appId("testAppId"); |
1716 | + quint64 procId = 5551; |
1717 | + |
1718 | + // Set up Mocks & signal watcher |
1719 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1720 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1721 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1722 | + |
1723 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1724 | + |
1725 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1726 | + .Times(1) |
1727 | + .WillOnce(Return(true)); |
1728 | + |
1729 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1730 | + applicationManager.onProcessStarting(appId); |
1731 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1732 | + bool authed = true; |
1733 | + applicationManager.authorizeSession(procId, authed); |
1734 | + applicationManager.onSessionStarting(session); |
1735 | + |
1736 | + applicationManager.stopApplication(appId); |
1737 | + |
1738 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1739 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1740 | + |
1741 | + // Mir notifies of stopping app/Session |
1742 | + applicationManager.onSessionStopping(session); |
1743 | + |
1744 | + EXPECT_EQ(countSpy.count(), 0); |
1745 | + EXPECT_EQ(removedSpy.count(), 0); |
1746 | +} |
1747 | + |
1748 | +/* |
1749 | + * Test that if an application is stopped by upstart, the Mir stopping event is ignored |
1750 | + */ |
1751 | +TEST_F(ApplicationManagerTests,appStoppedByUpstart_mirSessionStoppingEventIgnored) |
1752 | +{ |
1753 | + using namespace ::testing; |
1754 | + const QString appId("testAppId"); |
1755 | + quint64 procId = 5551; |
1756 | + |
1757 | + // Set up Mocks & signal watcher |
1758 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1759 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1760 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1761 | + |
1762 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1763 | + |
1764 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1765 | + .Times(1) |
1766 | + .WillOnce(Return(true)); |
1767 | + |
1768 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1769 | + applicationManager.onProcessStarting(appId); |
1770 | + std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId); |
1771 | + bool authed = true; |
1772 | + applicationManager.authorizeSession(procId, authed); |
1773 | + applicationManager.onSessionStarting(session); |
1774 | + |
1775 | + applicationManager.onProcessStopped(appId); |
1776 | + |
1777 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1778 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1779 | + |
1780 | + // Mir notifies of stopping app |
1781 | + applicationManager.onSessionStopping(session); |
1782 | + |
1783 | + EXPECT_EQ(countSpy.count(), 0); |
1784 | + EXPECT_EQ(removedSpy.count(), 0); |
1785 | +} |
1786 | + |
1787 | +/* |
1788 | + * Webapps have multiple sessions, but only one is linked to the application (other is considered a hidden session). |
1789 | + * If webapp in foreground stops unexpectedly, remove it and it alone from app list |
1790 | + */ |
1791 | +TEST_F(ApplicationManagerTests,unexpectedStopOfForegroundWebapp) |
1792 | +{ |
1793 | + using namespace ::testing; |
1794 | + const QString appId("webapp"); |
1795 | + quint64 procId1 = 5551; |
1796 | + quint64 procId2 = 5564; |
1797 | + QByteArray cmdLine("/usr/bin/qt5/libexec/QtWebProcess"); |
1798 | + |
1799 | + // Set up Mocks & signal watcher |
1800 | + EXPECT_CALL(procInfo,command_line(procId2)) |
1801 | + .Times(1) |
1802 | + .WillOnce(Return(cmdLine)); |
1803 | + |
1804 | + ON_CALL(appController,appIdHasProcessId(procId1, appId)).WillByDefault(Return(true)); |
1805 | + ON_CALL(appController,appIdHasProcessId(procId2, _)).WillByDefault(Return(false)); |
1806 | + |
1807 | + // Set up Mocks & signal watcher |
1808 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1809 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1810 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1811 | + |
1812 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1813 | + |
1814 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1815 | + .Times(1) |
1816 | + .WillOnce(Return(true)); |
1817 | + |
1818 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1819 | + applicationManager.onProcessStarting(appId); |
1820 | + std::shared_ptr<mir::scene::Session> session1 = std::make_shared<MockSession>("", procId1); |
1821 | + std::shared_ptr<mir::scene::Session> session2 = std::make_shared<MockSession>("", procId2); |
1822 | + |
1823 | + bool authed = false; |
1824 | + applicationManager.authorizeSession(procId1, authed); |
1825 | + applicationManager.onSessionStarting(session1); |
1826 | + EXPECT_EQ(authed, true); |
1827 | + applicationManager.authorizeSession(procId2, authed); |
1828 | + applicationManager.onSessionStarting(session2); |
1829 | + EXPECT_EQ(authed, true); |
1830 | + std::shared_ptr<mir::scene::Surface> surface(nullptr); |
1831 | + applicationManager.onSessionCreatedSurface(session2.get(), surface); |
1832 | + |
1833 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1834 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1835 | + |
1836 | + // Mir notifies of stopping app/Session |
1837 | + applicationManager.onSessionStopping(session2); |
1838 | + applicationManager.onSessionStopping(session1); |
1839 | + |
1840 | + EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback) |
1841 | + EXPECT_EQ(applicationManager.count(), 0); |
1842 | + EXPECT_EQ(removedSpy.count(), 1); |
1843 | + EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId); |
1844 | +} |
1845 | + |
1846 | +/* |
1847 | + * Webapps have multiple sessions, but only one is linked to the application (other is considered a hidden session). |
1848 | + * If webapp in background stops unexpectedly, do not remove it from app list |
1849 | + */ |
1850 | +TEST_F(ApplicationManagerTests,unexpectedStopOfBackgroundWebapp) |
1851 | +{ |
1852 | + using namespace ::testing; |
1853 | + const QString appId("webapp"); |
1854 | + quint64 procId1 = 5551; |
1855 | + quint64 procId2 = 5564; |
1856 | + QByteArray cmdLine("/usr/bin/qt5/libexec/QtWebProcess"); |
1857 | + |
1858 | + // Set up Mocks & signal watcher |
1859 | + EXPECT_CALL(procInfo,command_line(procId2)) |
1860 | + .Times(1) |
1861 | + .WillOnce(Return(cmdLine)); |
1862 | + |
1863 | + ON_CALL(appController,appIdHasProcessId(procId1, appId)).WillByDefault(Return(true)); |
1864 | + ON_CALL(appController,appIdHasProcessId(procId2, _)).WillByDefault(Return(false)); |
1865 | + |
1866 | + // Set up Mocks & signal watcher |
1867 | + auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo()); |
1868 | + ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true)); |
1869 | + ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId)); |
1870 | + |
1871 | + ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader)); |
1872 | + |
1873 | + EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _)) |
1874 | + .Times(1) |
1875 | + .WillOnce(Return(true)); |
1876 | + |
1877 | + applicationManager.startApplication(appId, ApplicationManager::NoFlag); |
1878 | + applicationManager.onProcessStarting(appId); |
1879 | + std::shared_ptr<mir::scene::Session> session1 = std::make_shared<MockSession>("", procId1); |
1880 | + std::shared_ptr<mir::scene::Session> session2 = std::make_shared<MockSession>("", procId2); |
1881 | + |
1882 | + bool authed = false; |
1883 | + applicationManager.authorizeSession(procId1, authed); |
1884 | + applicationManager.onSessionStarting(session1); |
1885 | + EXPECT_EQ(authed, true); |
1886 | + applicationManager.authorizeSession(procId2, authed); |
1887 | + applicationManager.onSessionStarting(session2); |
1888 | + EXPECT_EQ(authed, true); |
1889 | + |
1890 | + // both sessions create surfaces, then unfocus everything. |
1891 | + std::shared_ptr<mir::scene::Surface> surface1(nullptr); |
1892 | + applicationManager.onSessionCreatedSurface(session1.get(), surface1); |
1893 | + std::shared_ptr<mir::scene::Surface> surface2(nullptr); |
1894 | + applicationManager.onSessionCreatedSurface(session2.get(), surface2); |
1895 | + applicationManager.onSessionUnfocused(); |
1896 | + EXPECT_EQ(applicationManager.focusedApplicationId(), QString()); |
1897 | + |
1898 | + QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged())); |
1899 | + QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &))); |
1900 | + |
1901 | + // Mir notifies of stopping app/Session |
1902 | + applicationManager.onSessionStopping(session2); |
1903 | + applicationManager.onSessionStopping(session1); |
1904 | + |
1905 | + EXPECT_EQ(countSpy.count(), 0); |
1906 | + EXPECT_EQ(removedSpy.count(), 0); |
1907 | +} |
1908 | |
1909 | === modified file 'tests/mock_session.h' |
1910 | --- tests/mock_session.h 2014-04-15 14:31:02 +0000 |
1911 | +++ tests/mock_session.h 2014-05-20 12:42:58 +0000 |
1912 | @@ -15,8 +15,8 @@ |
1913 | * |
1914 | */ |
1915 | |
1916 | -#ifndef MOCK_MIR_SHELL_SESSION_H |
1917 | -#define MOCK_MIR_SHELL_SESSION_H |
1918 | +#ifndef MOCK_MIR_SCENE_SESSION_H |
1919 | +#define MOCK_MIR_SCENE_SESSION_H |
1920 | |
1921 | #include <mir/scene/session.h> |
1922 | #include <mir/graphics/display_configuration.h> |
1923 | @@ -66,4 +66,4 @@ |
1924 | }; |
1925 | } |
1926 | |
1927 | -#endif // MOCK_MIR_SHELL_SESSION_H |
1928 | +#endif // MOCK_MIR_SCENE_SESSION_H |
Yup.