Merge lp:~gerboland/qtmir/acquire-wakelock into lp:qtmir

Proposed by Gerry Boland on 2015-01-09
Status: Merged
Approved by: Michael Zanetti on 2015-01-14
Approved revision: 319
Merged at revision: 308
Proposed branch: lp:~gerboland/qtmir/acquire-wakelock
Merge into: lp:qtmir
Diff against target: 1098 lines (+814/-2)
16 files modified
src/common/abstractdbusservicemonitor.cpp (+101/-0)
src/common/abstractdbusservicemonitor.h (+68/-0)
src/modules/Unity/Application/CMakeLists.txt (+2/-0)
src/modules/Unity/Application/application.cpp (+20/-0)
src/modules/Unity/Application/application.h (+4/-0)
src/modules/Unity/Application/application_manager.cpp (+8/-0)
src/modules/Unity/Application/application_manager.h (+3/-0)
src/modules/Unity/Application/sharedwakelock.cpp (+168/-0)
src/modules/Unity/Application/sharedwakelock.h (+50/-0)
tests/modules/Application/CMakeLists.txt (+25/-0)
tests/modules/Application/application_test.cpp (+122/-0)
tests/modules/CMakeLists.txt (+3/-1)
tests/modules/SharedWakelock/CMakeLists.txt (+22/-0)
tests/modules/SharedWakelock/sharedwakelock_test.cpp (+170/-0)
tests/modules/common/mock_shared_wakelock.h (+44/-0)
tests/modules/common/qtmir_test.h (+4/-1)
To merge this branch: bzr merge lp:~gerboland/qtmir/acquire-wakelock
Reviewer Review Type Date Requested Status
Michael Zanetti (community) 2015-01-09 Approve on 2015-01-14
PS Jenkins bot continuous-integration Approve on 2015-01-12
Review via email: mp+245942@code.launchpad.net

Commit Message

Add Wakelock support - ensures device drops to deep-sleep mode only when all AppMan suspend tasks have completed

Implemented like so:
 - SharedWakelock manages a single wakelock instance, allowing multiple owners to keep the wakelock enabled, only releasing it when all owners have released it.
- Wakelock operates by communicating with PowerD
- the Application class decides whether a wakelock is necessary, based on the process' lifecycle state.
- added a mighty bunch of tests

Description of the Change

Add Wakelock support - ensures device drops to deep-sleep mode only when all AppMan suspend tasks has completed

 * 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

To post a comment you must log in.
Gerry Boland (gerboland) wrote :

To test, this command is handy:
sudo powerd-cli list
it lists the various wakelocks enabled:

Example output with system (grabbed by unity8 here) and display wakelock:
  Name: com.canonical.Unity.Screen, Owner: :1.16, State: 1
  Name: active, Owner: :1.48, State: 1

Example output with display wakelock alone:
  Name: com.canonical.Unity.Screen, Owner: :1.16, State: 1

Example output with display off, but system wakelock still held by unity8/qtmir - is held for 3 seconds after display is turned off, to give apps time to save state before suspend:
  Name: active, Owner: :1.48, State: 1

3 seconds after screen blank:
  None

Gerry Boland (gerboland) wrote :

Note also that I made it so that unity8-dash does not ever hold a wakelock.

lp:~gerboland/qtmir/acquire-wakelock updated on 2015-01-09
316. By Gerry Boland on 2015-01-09

No need for the lambda, just use the existing method as a slot

lp:~gerboland/qtmir/acquire-wakelock updated on 2015-01-09
317. By Gerry Boland on 2015-01-09

Fix typos

Michael Zanetti (mzanetti) wrote :

Ok. I think I fully understand the code. Looks ok except I'm not comfortable with the TODO in there (see inline comment)... Please explain if I'm wrong (or resolve it).

Still have to do testing.

review: Needs Information
Gerry Boland (gerboland) wrote :

> Ok. I think I fully understand the code. Looks ok except I'm not comfortable
> with the TODO in there (see inline comment)... Please explain if I'm wrong (or
> resolve it).

You're not wrong, wasn't sure how crashy unity8 is these days. Quick chat with ricmm, decided to save cookie to file and check for cookie file existence on startup

Michael Zanetti (mzanetti) wrote :

Ok, tried to test this:

* I cannot reproduce the app weirdness as described in the linked bug in vivid any more (I can in rtm) so I'm having problems verifying this fix.

* After installing the packages from this branch, the very first suspend after booting the phone seems to fail. Couldn't repro that with vivid trunk, so seems to be caused by this.

review: Needs Fixing
lp:~gerboland/qtmir/acquire-wakelock updated on 2015-01-12
318. By Gerry Boland on 2015-01-12

Merge trunk

319. By Gerry Boland on 2015-01-12

Cache cookie to disk so can restore it if qtmir crashes and fails to clean up

Gerry Boland (gerboland) wrote :

> * After installing the packages from this branch, the very first
> suspend after booting the phone seems to fail. Couldn't repro that
> with vivid trunk, so seems to be caused by this.

And naturally I'm unable to reproduce this. I followed your exact instructions on Mako:
<mzanetti> ubuntu-device-flash --device mako --channel devel-proposed
<mzanetti> phablet-shell
<mzanetti> wget jenkins:/output.zip
<mzanetti> unzip & dpkg -i qtmir-android qtdeclarative-qtmir
<mzanetti> *unplug* usb
<mzanetti> reboot phone
<mzanetti> open browser -> youtube.de
<mzanetti> play first video, press power button
<mzanetti> continues to play
<mzanetti> following retries make it stop after 5 secs.
<mzanetti> just the very first time the screen turns off after booting
<greyback> you let the screen turn itself off? Or you hit power key yourself?
<mzanetti> I press power

Can you have another look please?

kevin gunn (kgunn72) wrote :

I just tried with vivid/mako, installed the debs from jenkins.
For me, 1st try and every other....it always stops playing video after ~5 seconds.

> > * After installing the packages from this branch, the very first
> > suspend after booting the phone seems to fail. Couldn't repro that
> > with vivid trunk, so seems to be caused by this.
>
> And naturally I'm unable to reproduce this. I followed your exact instructions
> on Mako:
> <mzanetti> ubuntu-device-flash --device mako --channel devel-proposed
> <mzanetti> phablet-shell
> <mzanetti> wget jenkins:/output.zip
> <mzanetti> unzip & dpkg -i qtmir-android qtdeclarative-qtmir
> <mzanetti> *unplug* usb
> <mzanetti> reboot phone
> <mzanetti> open browser -> youtube.de
> <mzanetti> play first video, press power button
> <mzanetti> continues to play
> <mzanetti> following retries make it stop after 5 secs.
> <mzanetti> just the very first time the screen turns off after booting
> <greyback> you let the screen turn itself off? Or you hit power key yourself?
> <mzanetti> I press power
>
> Can you have another look please?

kevin gunn (kgunn72) wrote :

testing with the powerd-cli list
playing video with youtube, list is
System State Requests:
  Name: com.canonical.Unity.Screen, Owner: :1.17, State: 1
  Name: active, Owner: :1.43, State: 1
hit power button, screen off while youtube is playing list changes to
System State Requests:
  Name: active, Owner: :1.43, State: 1
after ~5 sec, youtube audio is halted (app suspended presumably)
System State Requests:
  None

on the very first attempt of monitoring list, i thot i saw the
    Name: active, Owner: :1.43, State: 1
hang around and be returned for powerd-cli list after striking the power button, and never go away for well over 10 seconds. Even without an app being launched. Retried many tries, even reflashed thinking that might be related. Never saw it again...

kevin gunn (kgunn72) wrote :

after even more testing, i am now convinced i must have not waited long enough.
This seems to be working solidly

> testing with the powerd-cli list
> playing video with youtube, list is
> System State Requests:
> Name: com.canonical.Unity.Screen, Owner: :1.17, State: 1
> Name: active, Owner: :1.43, State: 1
> hit power button, screen off while youtube is playing list changes to
> System State Requests:
> Name: active, Owner: :1.43, State: 1
> after ~5 sec, youtube audio is halted (app suspended presumably)
> System State Requests:
> None
>
> on the very first attempt of monitoring list, i thot i saw the
> Name: active, Owner: :1.43, State: 1
> hang around and be returned for powerd-cli list after striking the power
> button, and never go away for well over 10 seconds. Even without an app being
> launched. Retried many tries, even reflashed thinking that might be related.
> Never saw it again...

Michael Zanetti (mzanetti) wrote :

> * After installing the packages from this branch, the very first suspend after booting the phone
> seems to fail. Couldn't repro that with vivid trunk, so seems to be caused by this.

Installed the latest packages from jenkins and can't repro this any more either...

Code looks still good to me, seems to behave well. Thanks Kevin for the testing help.

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

yes

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

yip yip

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'src/common/abstractdbusservicemonitor.cpp'
2--- src/common/abstractdbusservicemonitor.cpp 1970-01-01 00:00:00 +0000
3+++ src/common/abstractdbusservicemonitor.cpp 2015-01-12 16:51:13 +0000
4@@ -0,0 +1,101 @@
5+/*
6+ * Copyright (C) 2011-2015 Canonical, Ltd.
7+ *
8+ * Authors:
9+ * Ugo Riboni <ugo.riboni@canonical.com>
10+ * Gerry Boland <gerry.boland@canonical.com>
11+ *
12+ * This program is free software; you can redistribute it and/or modify
13+ * it under the terms of the GNU General Public License as published by
14+ * the Free Software Foundation; version 3.
15+ *
16+ * This program is distributed in the hope that it will be useful,
17+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
18+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+ * GNU General Public License for more details.
20+ *
21+ * You should have received a copy of the GNU General Public License
22+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
23+ */
24+
25+#include "abstractdbusservicemonitor.h"
26+
27+#include <QDBusInterface>
28+#include <QDBusServiceWatcher>
29+#include <QDBusConnection>
30+#include <QDBusConnectionInterface>
31+#include <QDBusReply>
32+
33+// On construction QDBusInterface synchronously introspects the service, which will block the GUI
34+// thread if the service is busy. QDBusAbstractInterface does not perform this introspection, so
35+// let's subclass that and avoid the blocking scenario.
36+class AsyncDBusInterface : public QDBusAbstractInterface
37+{
38+public:
39+ AsyncDBusInterface(const QString &service, const QString &path,
40+ const QString &interface, const QDBusConnection &connection,
41+ QObject *parent = 0)
42+ : QDBusAbstractInterface(service, path, interface.toLatin1().data(), connection, parent)
43+ {}
44+ ~AsyncDBusInterface() = default;
45+};
46+
47+AbstractDBusServiceMonitor::AbstractDBusServiceMonitor(const QString &service, const QString &path,
48+ const QString &interface, const Bus bus,
49+ QObject *parent)
50+ : QObject(parent)
51+ , m_service(service)
52+ , m_path(path)
53+ , m_interface(interface)
54+ , m_busConnection((bus == SystemBus) ? QDBusConnection::systemBus()
55+ : QDBusConnection::sessionBus())
56+ , m_watcher(new QDBusServiceWatcher(service, m_busConnection))
57+ , m_dbusInterface(nullptr)
58+{
59+ connect(m_watcher, &QDBusServiceWatcher::serviceRegistered, this, &AbstractDBusServiceMonitor::createInterface);
60+ connect(m_watcher, &QDBusServiceWatcher::serviceUnregistered, this, &AbstractDBusServiceMonitor::destroyInterface);
61+
62+ // Connect to the service if it's up already
63+ QDBusConnectionInterface* sessionBus = m_busConnection.interface();
64+ QDBusReply<bool> reply = sessionBus->isServiceRegistered(m_service);
65+ if (reply.isValid() && reply.value()) {
66+ createInterface(m_service);
67+ }
68+}
69+
70+AbstractDBusServiceMonitor::~AbstractDBusServiceMonitor()
71+{
72+ delete m_watcher;
73+ delete m_dbusInterface;
74+}
75+
76+void AbstractDBusServiceMonitor::createInterface(const QString &)
77+{
78+ if (m_dbusInterface != nullptr) {
79+ delete m_dbusInterface;
80+ m_dbusInterface = nullptr;
81+ }
82+
83+ m_dbusInterface = new AsyncDBusInterface(m_service, m_path, m_interface, m_busConnection);
84+ Q_EMIT serviceAvailableChanged(true);
85+}
86+
87+void AbstractDBusServiceMonitor::destroyInterface(const QString &)
88+{
89+ if (m_dbusInterface != nullptr) {
90+ delete m_dbusInterface;
91+ m_dbusInterface = nullptr;
92+ }
93+
94+ Q_EMIT serviceAvailableChanged(false);
95+}
96+
97+QDBusAbstractInterface* AbstractDBusServiceMonitor::dbusInterface() const
98+{
99+ return m_dbusInterface;
100+}
101+
102+bool AbstractDBusServiceMonitor::serviceAvailable() const
103+{
104+ return m_dbusInterface != nullptr;
105+}
106
107=== added file 'src/common/abstractdbusservicemonitor.h'
108--- src/common/abstractdbusservicemonitor.h 1970-01-01 00:00:00 +0000
109+++ src/common/abstractdbusservicemonitor.h 2015-01-12 16:51:13 +0000
110@@ -0,0 +1,68 @@
111+/*
112+ * Copyright (C) 2011-2015 Canonical, Ltd.
113+ *
114+ * Authors:
115+ * Ugo Riboni <ugo.riboni@canonical.com>
116+ * Gerry Boland <gerry.boland@canonical.com>
117+ *
118+ * This program is free software; you can redistribute it and/or modify
119+ * it under the terms of the GNU General Public License as published by
120+ * the Free Software Foundation; version 3.
121+ *
122+ * This program is distributed in the hope that it will be useful,
123+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
124+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
125+ * GNU General Public License for more details.
126+ *
127+ * You should have received a copy of the GNU General Public License
128+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
129+ */
130+
131+#ifndef ABSTRACTDBUSSERVICEMONITOR_H
132+#define ABSTRACTDBUSSERVICEMONITOR_H
133+
134+#include <QObject>
135+#include <QString>
136+#include <QDBusConnection>
137+
138+class QDBusAbstractInterface;
139+class QDBusServiceWatcher;
140+
141+class Q_DECL_EXPORT AbstractDBusServiceMonitor : public QObject
142+{
143+ Q_OBJECT
144+ Q_ENUMS(Bus)
145+ Q_PROPERTY(bool serviceAvailable READ serviceAvailable NOTIFY serviceAvailableChanged)
146+
147+public:
148+ enum Bus {
149+ SessionBus,
150+ SystemBus,
151+ };
152+
153+ explicit AbstractDBusServiceMonitor(const QString &service, const QString &path, const QString &interface,
154+ const Bus bus = SessionBus,
155+ QObject *parent = 0);
156+ ~AbstractDBusServiceMonitor();
157+
158+ QDBusAbstractInterface* dbusInterface() const;
159+
160+ bool serviceAvailable() const;
161+
162+Q_SIGNALS:
163+ void serviceAvailableChanged(bool available);
164+
165+private Q_SLOTS:
166+ void createInterface(const QString &service);
167+ void destroyInterface(const QString &service);
168+
169+protected:
170+ const QString m_service;
171+ const QString m_path;
172+ const QString m_interface;
173+ const QDBusConnection m_busConnection;
174+ QDBusServiceWatcher* m_watcher;
175+ QDBusAbstractInterface* m_dbusInterface;
176+};
177+
178+#endif // ABSTRACTDBUSSERVICEMONITOR_H
179
180=== modified file 'src/modules/Unity/Application/CMakeLists.txt'
181--- src/modules/Unity/Application/CMakeLists.txt 2014-12-01 13:00:39 +0000
182+++ src/modules/Unity/Application/CMakeLists.txt 2015-01-12 16:51:13 +0000
183@@ -24,6 +24,7 @@
184 set(QMLMIRPLUGIN_SRC
185 application_manager.cpp
186 application.cpp
187+ ../../../common/abstractdbusservicemonitor.cpp
188 ../../../common/debughelpers.cpp
189 desktopfilereader.cpp
190 plugin.cpp
191@@ -37,6 +38,7 @@
192 proc_info.cpp
193 session.cpp
194 sessionmanager.cpp
195+ sharedwakelock.cpp
196 upstart/applicationcontroller.cpp
197 tracepoints.c
198 # We need to run moc on these headers
199
200=== modified file 'src/modules/Unity/Application/application.cpp'
201--- src/modules/Unity/Application/application.cpp 2014-11-27 16:43:44 +0000
202+++ src/modules/Unity/Application/application.cpp 2015-01-12 16:51:13 +0000
203@@ -19,6 +19,7 @@
204 #include "application_manager.h"
205 #include "desktopfilereader.h"
206 #include "session.h"
207+#include "sharedwakelock.h"
208 #include "taskcontroller.h"
209
210 // common
211@@ -37,12 +38,14 @@
212 {
213
214 Application::Application(const QSharedPointer<TaskController>& taskController,
215+ const QSharedPointer<SharedWakelock>& sharedWakelock,
216 DesktopFileReader *desktopFileReader,
217 State state,
218 const QStringList &arguments,
219 ApplicationManager *parent)
220 : ApplicationInfoInterface(desktopFileReader->appId(), parent)
221 , m_taskController(taskController)
222+ , m_sharedWakelock(sharedWakelock)
223 , m_desktopData(desktopFileReader)
224 , m_pid(0)
225 , m_stage((m_desktopData->stageHint() == "SideStage") ? Application::SideStage : Application::MainStage)
226@@ -316,6 +319,8 @@
227 void Application::setFocused(bool focused)
228 {
229 qCDebug(QTMIR_APPLICATIONS) << "Application::setFocused - appId=" << appId() << "focused=" << focused;
230+ holdWakelock(true);
231+
232 if (m_focused != focused) {
233 m_focused = focused;
234 Q_EMIT focusedChanged(focused);
235@@ -326,17 +331,20 @@
236 {
237 qCDebug(QTMIR_APPLICATIONS) << "Application::onSessionSuspended - appId=" << appId();
238 m_taskController->suspend(longAppId());
239+ holdWakelock(false);
240 }
241
242 void Application::onSessionResumed()
243 {
244 qCDebug(QTMIR_APPLICATIONS) << "Application::onSessionResumed - appId=" << appId();
245+ holdWakelock(true);
246 m_taskController->resume(longAppId());
247 }
248
249 void Application::respawn()
250 {
251 qCDebug(QTMIR_APPLICATIONS) << "Application::respawn - appId=" << appId();
252+ holdWakelock(true);
253 m_taskController->start(appId(), m_arguments);
254 }
255
256@@ -355,4 +363,16 @@
257 return m_session;
258 }
259
260+void Application::holdWakelock(bool enable) const
261+{
262+ if (appId() == "unity8-dash")
263+ return;
264+
265+ if (enable) {
266+ m_sharedWakelock->acquire(this);
267+ } else {
268+ m_sharedWakelock->release(this);
269+ }
270+}
271+
272 } // namespace qtmir
273
274=== modified file 'src/modules/Unity/Application/application.h'
275--- src/modules/Unity/Application/application.h 2014-10-13 10:07:10 +0000
276+++ src/modules/Unity/Application/application.h 2015-01-12 16:51:13 +0000
277@@ -41,6 +41,7 @@
278 class DesktopFileReader;
279 class TaskController;
280 class Session;
281+class SharedWakelock;
282
283 class Application : public unity::shell::application::ApplicationInfoInterface
284 {
285@@ -68,6 +69,7 @@
286 Q_DECLARE_FLAGS(SupportedOrientations, Orientation)
287
288 Application(const QSharedPointer<TaskController>& taskController,
289+ const QSharedPointer<SharedWakelock>& sharedWakelock,
290 DesktopFileReader *desktopFileReader,
291 State state,
292 const QStringList &arguments,
293@@ -120,6 +122,7 @@
294
295 private:
296 QString longAppId() const;
297+ void holdWakelock(bool enable) const;
298 void setPid(pid_t pid);
299 void setArguments(const QStringList arguments);
300 void setFocused(bool focus);
301@@ -127,6 +130,7 @@
302 QColor colorFromString(const QString &colorString, const char *colorName) const;
303
304 QSharedPointer<TaskController> m_taskController;
305+ QSharedPointer<SharedWakelock> m_sharedWakelock;
306 DesktopFileReader* m_desktopData;
307 QString m_longAppId;
308 qint64 m_pid;
309
310=== modified file 'src/modules/Unity/Application/application_manager.cpp'
311--- src/modules/Unity/Application/application_manager.cpp 2014-12-15 08:27:32 +0000
312+++ src/modules/Unity/Application/application_manager.cpp 2015-01-12 16:51:13 +0000
313@@ -20,6 +20,7 @@
314 #include "desktopfilereader.h"
315 #include "dbuswindowstack.h"
316 #include "session.h"
317+#include "sharedwakelock.h"
318 #include "proc_info.h"
319 #include "taskcontroller.h"
320 #include "upstart/applicationcontroller.h"
321@@ -150,6 +151,7 @@
322 QSharedPointer<TaskController> taskController(new TaskController(nullptr, appController));
323 QSharedPointer<DesktopFileReader::Factory> fileReaderFactory(new DesktopFileReader::Factory());
324 QSharedPointer<ProcInfo> procInfo(new ProcInfo());
325+ QSharedPointer<SharedWakelock> sharedWakelock(new SharedWakelock);
326
327 // FIXME: We should use a QSharedPointer to wrap this ApplicationManager object, which requires us
328 // to use the data() method to pass the raw pointer to the QML engine. However the QML engine appears
329@@ -159,6 +161,7 @@
330 ApplicationManager* appManager = new ApplicationManager(
331 mirServer,
332 taskController,
333+ sharedWakelock,
334 fileReaderFactory,
335 procInfo
336 );
337@@ -193,6 +196,7 @@
338 ApplicationManager::ApplicationManager(
339 const QSharedPointer<MirServer>& mirServer,
340 const QSharedPointer<TaskController>& taskController,
341+ const QSharedPointer<SharedWakelock>& sharedWakelock,
342 const QSharedPointer<DesktopFileReader::Factory>& desktopFileReaderFactory,
343 const QSharedPointer<ProcInfo>& procInfo,
344 QObject *parent)
345@@ -206,6 +210,7 @@
346 , m_taskController(taskController)
347 , m_desktopFileReaderFactory(desktopFileReaderFactory)
348 , m_procInfo(procInfo)
349+ , m_sharedWakelock(sharedWakelock)
350 , m_suspended(false)
351 , m_forceDashActive(false)
352 {
353@@ -493,6 +498,7 @@
354 } else {
355 application = new Application(
356 m_taskController,
357+ m_sharedWakelock,
358 m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)),
359 Application::Starting,
360 arguments,
361@@ -522,6 +528,7 @@
362 if (!application) { // then shell did not start this application, so ubuntu-app-launch must have - add to list
363 application = new Application(
364 m_taskController,
365+ m_sharedWakelock,
366 m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)),
367 Application::Starting,
368 QStringList(),
369@@ -796,6 +803,7 @@
370 QStringList arguments(info->asStringList());
371 application = new Application(
372 m_taskController,
373+ m_sharedWakelock,
374 desktopData,
375 Application::Starting,
376 arguments,
377
378=== modified file 'src/modules/Unity/Application/application_manager.h'
379--- src/modules/Unity/Application/application_manager.h 2014-12-01 11:05:01 +0000
380+++ src/modules/Unity/Application/application_manager.h 2015-01-12 16:51:13 +0000
381@@ -46,6 +46,7 @@
382 class DBusWindowStack;
383 class MirSurfaceManager;
384 class ProcInfo;
385+class SharedWakelock;
386 class TaskController;
387
388 class ApplicationManager : public unity::shell::application::ApplicationManagerInterface
389@@ -82,6 +83,7 @@
390 explicit ApplicationManager(
391 const QSharedPointer<MirServer> &mirServer,
392 const QSharedPointer<TaskController> &taskController,
393+ const QSharedPointer<SharedWakelock> &sharedWakelock,
394 const QSharedPointer<DesktopFileReader::Factory> &desktopFileReaderFactory,
395 const QSharedPointer<ProcInfo> &processInfo,
396 QObject *parent = 0);
397@@ -160,6 +162,7 @@
398 QSharedPointer<TaskController> m_taskController;
399 QSharedPointer<DesktopFileReader::Factory> m_desktopFileReaderFactory;
400 QSharedPointer<ProcInfo> m_procInfo;
401+ QSharedPointer<SharedWakelock> m_sharedWakelock;
402 static ApplicationManager* the_application_manager;
403 QList<pid_t> m_hiddenPIDs;
404 bool m_suspended;
405
406=== added file 'src/modules/Unity/Application/sharedwakelock.cpp'
407--- src/modules/Unity/Application/sharedwakelock.cpp 1970-01-01 00:00:00 +0000
408+++ src/modules/Unity/Application/sharedwakelock.cpp 2015-01-12 16:51:13 +0000
409@@ -0,0 +1,168 @@
410+/*
411+ * Copyright (C) 2015 Canonical, Ltd.
412+ *
413+ * This program is free software: you can redistribute it and/or modify it under
414+ * the terms of the GNU Lesser General Public License version 3, as published by
415+ * the Free Software Foundation.
416+ *
417+ * This program is distributed in the hope that it will be useful, but WITHOUT
418+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
419+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
420+ * Lesser General Public License for more details.
421+ *
422+ * You should have received a copy of the GNU Lesser General Public License
423+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
424+ */
425+
426+#include "sharedwakelock.h"
427+#include "abstractdbusservicemonitor.h"
428+#include "logging.h"
429+
430+#include <QDBusAbstractInterface>
431+#include <QDBusPendingCallWatcher>
432+#include <QDBusPendingReply>
433+#include <QFile>
434+
435+namespace qtmir {
436+
437+const int POWERD_SYS_STATE_ACTIVE = 1; // copied from private header file powerd.h
438+const char cookieFile[] = "/tmp/qtmir_powerd_cookie";
439+
440+/**
441+ * @brief The Wakelock class - on creation acquires a system wakelock, on destruction releases it
442+ * Designed in the spirit of RAII. Should the PowerD service vanish from the bus, the wakelock
443+ * will be re-acquired when it re-joins the bus.
444+ */
445+class Wakelock : public AbstractDBusServiceMonitor
446+{
447+ Q_OBJECT
448+public:
449+ Wakelock() noexcept
450+ : AbstractDBusServiceMonitor("com.canonical.powerd", "/com/canonical/powerd", "com.canonical.powerd", SystemBus)
451+ {
452+ // (re-)acquire wake lock when powerd (re-)appears on the bus
453+ QObject::connect(this, &Wakelock::serviceAvailableChanged,
454+ this, &Wakelock::acquireWakelock);
455+
456+ if (!serviceAvailable()) {
457+ qWarning() << "com.canonical.powerd DBus interface not available, waiting for it";
458+ return;
459+ }
460+
461+ // WORKAROUND: if shell crashed while it held a wakelock, due to bug lp:1409722 powerd will not have released
462+ // the wakelock for it. As workaround, we save the cookie to file and restore it if possible.
463+ QFile cookie(cookieFile);
464+ if (cookie.exists() && cookie.open(QFile::ReadOnly | QFile::Text)) {
465+ m_cookie = cookie.readAll();
466+ } else {
467+ acquireWakelock(true);
468+ }
469+ }
470+
471+ virtual ~Wakelock() noexcept
472+ {
473+ QFile::remove(cookieFile);
474+
475+ if (!serviceAvailable()) {
476+ qWarning() << "com.canonical.powerd DBus interface not available";
477+ return;
478+ }
479+
480+ if (!m_cookie.isEmpty()) {
481+ dbusInterface()->asyncCall("clearSysState", m_cookie);
482+ }
483+ qCDebug(QTMIR_SESSIONS) << "Wakelock released";
484+ }
485+
486+private Q_SLOTS:
487+ void acquireWakelock(bool available)
488+ {
489+ if (!available) {
490+ m_cookie.clear(); // clear cookie so that when powerd re-appears, new cookie will be set
491+ QFile::remove(cookieFile);
492+ return;
493+ }
494+
495+ QDBusPendingCall pcall = dbusInterface()->asyncCall("requestSysState", "active", POWERD_SYS_STATE_ACTIVE);
496+
497+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this);
498+ QObject::connect(watcher, &QDBusPendingCallWatcher::finished,
499+ this, &Wakelock::onWakeLockAcquired);
500+ }
501+
502+ void onWakeLockAcquired(QDBusPendingCallWatcher *call)
503+ {
504+ if (m_cookie.isEmpty()) { // don't overwrite existing cookie
505+ QDBusPendingReply<QString> reply = *call;
506+ if (reply.isError()) {
507+ qCDebug(QTMIR_SESSIONS) << "Wakelock was NOT acquired, error:"
508+ << QDBusError::errorString(reply.error().type());
509+ } else {
510+ m_cookie = reply.argumentAt<0>();
511+
512+ // see WORKAROUND above for why we save cookie to disk
513+ QFile cookie(cookieFile);
514+ cookie.open(QFile::WriteOnly | QFile::Text);
515+ cookie.write(m_cookie.toLatin1());
516+
517+ qCDebug(QTMIR_SESSIONS) << "Wakelock acquired" << m_cookie;
518+ }
519+ }
520+ call->deleteLater();
521+ }
522+
523+private:
524+ QString m_cookie;
525+
526+ Q_DISABLE_COPY(Wakelock)
527+};
528+
529+#include "sharedwakelock.moc"
530+
531+/**
532+ * @brief SharedWakelock - allow a single wakelock instance to be shared between multiple owners
533+ *
534+ * QtMir has application management duties to perform even if display is off. To prevent device
535+ * going to deep sleep before QtMir is ready, have QtMir register a system wakelock when it needs to.
536+ *
537+ * This class allows multiple objects to own the wakelock simultaneously. The wakelock is first
538+ * registered when acquire has been called by one caller. Multiple callers may then share the
539+ * wakelock. The wakelock is only destroyed when all callers have called release.
540+ *
541+ * Note a caller cannot have multiple shares of the wakelock. Multiple calls to acquire are ignored.
542+ */
543+
544+QObject* SharedWakelock::createWakelock()
545+{
546+ return new Wakelock;
547+}
548+
549+
550+void SharedWakelock::acquire(const QObject *caller)
551+{
552+ if (m_owners.contains(caller) || caller == nullptr) {
553+ return;
554+ }
555+
556+ // register a slot to remove itself from owners list if destroyed
557+ QObject::connect(caller, &QObject::destroyed, this, &SharedWakelock::release);
558+
559+ if (m_wakelock.isNull()) {
560+ m_wakelock.reset(createWakelock());
561+ }
562+
563+ m_owners.insert(caller);
564+}
565+
566+void SharedWakelock::release(const QObject *caller)
567+{
568+ if (!m_owners.remove(caller) || caller == nullptr) {
569+ return;
570+ }
571+
572+ if (m_owners.empty() && m_wakelock) {
573+ m_wakelock.reset();
574+ }
575+}
576+
577+} // namespace qtmir
578
579=== added file 'src/modules/Unity/Application/sharedwakelock.h'
580--- src/modules/Unity/Application/sharedwakelock.h 1970-01-01 00:00:00 +0000
581+++ src/modules/Unity/Application/sharedwakelock.h 2015-01-12 16:51:13 +0000
582@@ -0,0 +1,50 @@
583+/*
584+ * Copyright (C) 2015 Canonical, Ltd.
585+ *
586+ * This program is free software: you can redistribute it and/or modify it under
587+ * the terms of the GNU Lesser General Public License version 3, as published by
588+ * the Free Software Foundation.
589+ *
590+ * This program is distributed in the hope that it will be useful, but WITHOUT
591+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
592+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
593+ * Lesser General Public License for more details.
594+ *
595+ * You should have received a copy of the GNU Lesser General Public License
596+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
597+ *
598+ * Author: Gerry Boland <gerry.boland@canonical.com>
599+ */
600+
601+#ifndef WAKELOCK_H
602+#define WAKELOCK_H
603+
604+#include <QObject>
605+#include <QSet>
606+#include <QScopedPointer>
607+
608+namespace qtmir {
609+
610+class SharedWakelock : public QObject
611+{
612+ Q_OBJECT
613+public:
614+ SharedWakelock() = default;
615+ virtual ~SharedWakelock() noexcept = default;
616+
617+ void acquire(const QObject *caller);
618+ Q_SLOT void release(const QObject *caller);
619+
620+protected:
621+ virtual QObject* createWakelock(); // override to test
622+
623+ QScopedPointer<QObject> m_wakelock;
624+ QSet<const QObject *> m_owners;
625+
626+private:
627+ Q_DISABLE_COPY(SharedWakelock)
628+};
629+
630+} // namespace qtmir
631+
632+#endif // WAKELOCK_H
633
634=== added directory 'tests/modules/Application'
635=== added file 'tests/modules/Application/CMakeLists.txt'
636--- tests/modules/Application/CMakeLists.txt 1970-01-01 00:00:00 +0000
637+++ tests/modules/Application/CMakeLists.txt 2015-01-12 16:51:13 +0000
638@@ -0,0 +1,25 @@
639+set(
640+ APPLICATION_TEST_SOURCES
641+ application_test.cpp
642+)
643+
644+include_directories(
645+ ${CMAKE_SOURCE_DIR}/src/platforms/mirserver
646+ ${CMAKE_SOURCE_DIR}/src/modules
647+ ${CMAKE_SOURCE_DIR}/tests/modules/common
648+ ${MIRSERVER_INCLUDE_DIRS}
649+)
650+
651+add_executable(application_test ${APPLICATION_TEST_SOURCES})
652+
653+target_link_libraries(
654+ application_test
655+
656+ unityapplicationplugin
657+ qpa-mirserver
658+
659+ ${GTEST_BOTH_LIBRARIES}
660+ ${GMOCK_LIBRARIES}
661+)
662+
663+add_test(Application, application_test)
664
665=== added file 'tests/modules/Application/application_test.cpp'
666--- tests/modules/Application/application_test.cpp 1970-01-01 00:00:00 +0000
667+++ tests/modules/Application/application_test.cpp 2015-01-12 16:51:13 +0000
668@@ -0,0 +1,122 @@
669+/*
670+ * Copyright (C) 2015 Canonical, Ltd.
671+ *
672+ * This program is free software: you can redistribute it and/or modify it under
673+ * the terms of the GNU Lesser General Public License version 3, as published by
674+ * the Free Software Foundation.
675+ *
676+ * This program is distributed in the hope that it will be useful, but WITHOUT
677+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
678+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
679+ * Lesser General Public License for more details.
680+ *
681+ * You should have received a copy of the GNU Lesser General Public License
682+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
683+ *
684+ */
685+
686+#include <gmock/gmock.h>
687+#include <gtest/gtest.h>
688+
689+#include "qtmir_test.h"
690+
691+
692+using namespace qtmir;
693+
694+class ApplicationTests : public ::testing::QtMirTest
695+{
696+public:
697+ ApplicationTests()
698+ {}
699+};
700+
701+TEST_F(ApplicationTests, checkFocusAcquiresWakeLock)
702+{
703+ using namespace ::testing;
704+
705+ EXPECT_CALL(sharedWakelock, createWakelock()).Times(1);
706+
707+ startApplication(123, "app");
708+ applicationManager.focusApplication("app");
709+}
710+
711+TEST_F(ApplicationTests, checkSuspendReleasesWakeLock)
712+{
713+ using namespace ::testing;
714+
715+ auto app = startApplication(123, "app");
716+ auto session = app->session();
717+
718+ applicationManager.focusApplication("app");
719+
720+ Q_EMIT session->suspended();
721+ EXPECT_FALSE(sharedWakelock.wakelockHeld());
722+}
723+
724+TEST_F(ApplicationTests, checkResumeAcquiresWakeLock)
725+{
726+ using namespace ::testing;
727+
728+ EXPECT_CALL(sharedWakelock, createWakelock()).Times(1);
729+
730+ auto app = startApplication(123, "app");
731+ auto session = app->session();
732+
733+ Q_EMIT session->resumed();
734+}
735+
736+TEST_F(ApplicationTests, checkRespawnAcquiresWakeLock)
737+{
738+ using namespace ::testing;
739+
740+ EXPECT_CALL(sharedWakelock, createWakelock()).Times(1);
741+ const QString appId = "app";
742+
743+ auto app = startApplication(123, "app");
744+
745+ // as respawn fires startApplicationWithAppIdAndArgs again, keep gmock quiet about another call
746+ EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
747+ .Times(1)
748+ .WillRepeatedly(Return(true));
749+
750+ // respawn by setting app state as Stopped, delete the Session associated, then set to Running state
751+ app->setState(Session::State::Stopped);
752+ delete app->session();
753+ app->setState(Session::State::Running);
754+}
755+
756+TEST_F(ApplicationTests, checkDashFocusDoesNotAcquireWakeLock)
757+{
758+ using namespace ::testing;
759+
760+ EXPECT_CALL(sharedWakelock, createWakelock()).Times(0);
761+
762+ startApplication(123, "unity8-dash");
763+ applicationManager.focusApplication("unity8-dash");
764+}
765+
766+TEST_F(ApplicationTests, checkDashSuspendDoesNotImpactWakeLock)
767+{
768+ using namespace ::testing;
769+
770+ auto app = startApplication(123, "unity8-dash");
771+ auto session = app->session();
772+
773+ applicationManager.focusApplication("unity8-dash");
774+
775+ Q_EMIT session->suspended();
776+ EXPECT_FALSE(sharedWakelock.wakelockHeld());
777+}
778+
779+TEST_F(ApplicationTests, checkDashResumeDoesNotAcquireWakeLock)
780+{
781+ using namespace ::testing;
782+
783+ EXPECT_CALL(sharedWakelock, createWakelock()).Times(0);
784+
785+ auto app = startApplication(123, "unity8-dash");
786+ auto session = app->session();
787+
788+ Q_EMIT session->resumed();
789+}
790+
791
792=== modified file 'tests/modules/CMakeLists.txt'
793--- tests/modules/CMakeLists.txt 2014-10-14 19:39:43 +0000
794+++ tests/modules/CMakeLists.txt 2015-01-12 16:51:13 +0000
795@@ -1,6 +1,8 @@
796+add_subdirectory(Application)
797 add_subdirectory(ApplicationManager)
798+add_subdirectory(DesktopFileReader)
799 add_subdirectory(General)
800 add_subdirectory(MirSurfaceItem)
801 add_subdirectory(SessionManager)
802+add_subdirectory(SharedWakelock)
803 add_subdirectory(TaskController)
804-add_subdirectory(DesktopFileReader)
805
806=== added directory 'tests/modules/SharedWakelock'
807=== added file 'tests/modules/SharedWakelock/CMakeLists.txt'
808--- tests/modules/SharedWakelock/CMakeLists.txt 1970-01-01 00:00:00 +0000
809+++ tests/modules/SharedWakelock/CMakeLists.txt 2015-01-12 16:51:13 +0000
810@@ -0,0 +1,22 @@
811+set(
812+ SHARED_WAKELOCK_TEST_SOURCES
813+ sharedwakelock_test.cpp
814+)
815+
816+include_directories(
817+ ${CMAKE_SOURCE_DIR}/src/modules
818+ ${CMAKE_SOURCE_DIR}/tests/modules/common
819+)
820+
821+add_executable(sharedwakelock_test ${SHARED_WAKELOCK_TEST_SOURCES})
822+
823+target_link_libraries(
824+ sharedwakelock_test
825+
826+ unityapplicationplugin
827+
828+ ${GTEST_BOTH_LIBRARIES}
829+ ${GMOCK_LIBRARIES}
830+)
831+
832+add_test(SharedWakelock, sharedwakelock_test)
833
834=== added file 'tests/modules/SharedWakelock/sharedwakelock_test.cpp'
835--- tests/modules/SharedWakelock/sharedwakelock_test.cpp 1970-01-01 00:00:00 +0000
836+++ tests/modules/SharedWakelock/sharedwakelock_test.cpp 2015-01-12 16:51:13 +0000
837@@ -0,0 +1,170 @@
838+/*
839+ * Copyright (C) 2015 Canonical, Ltd.
840+ *
841+ * This program is free software: you can redistribute it and/or modify it under
842+ * the terms of the GNU Lesser General Public License version 3, as published by
843+ * the Free Software Foundation.
844+ *
845+ * This program is distributed in the hope that it will be useful, but WITHOUT
846+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
847+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
848+ * Lesser General Public License for more details.
849+ *
850+ * You should have received a copy of the GNU Lesser General Public License
851+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
852+ *
853+ */
854+
855+#include <Unity/Application/sharedwakelock.h>
856+
857+#include "mock_shared_wakelock.h"
858+
859+#include <gmock/gmock.h>
860+#include <gtest/gtest.h>
861+
862+using namespace qtmir;
863+using testing::MockSharedWakelock;
864+
865+TEST(SharedWakelock, acquireCreatesAWakelock)
866+{
867+ using namespace ::testing;
868+
869+ testing::NiceMock<MockSharedWakelock> sharedWakelock;
870+ QScopedPointer<QObject> app1(new QObject);
871+
872+ EXPECT_CALL(sharedWakelock, createWakelock()).Times(1);
873+ sharedWakelock.acquire(app1.data());
874+}
875+
876+TEST(SharedWakelock, acquireThenReleaseDestroysTheWakelock)
877+{
878+ using namespace ::testing;
879+
880+ testing::NiceMock<MockSharedWakelock> sharedWakelock;
881+ QScopedPointer<QObject> app1(new QObject);
882+
883+ sharedWakelock.acquire(app1.data());
884+ sharedWakelock.release(app1.data());
885+ EXPECT_FALSE(sharedWakelock.wakelockHeld());
886+}
887+
888+TEST(SharedWakelock, doubleAcquireBySameOwnerOnlyCreatesASingleWakelock)
889+{
890+ using namespace ::testing;
891+
892+ testing::NiceMock<MockSharedWakelock> sharedWakelock;
893+ QScopedPointer<QObject> app1(new QObject);
894+
895+ EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject));
896+ sharedWakelock.acquire(app1.data());
897+ sharedWakelock.acquire(app1.data());
898+}
899+
900+TEST(SharedWakelock, doubleAcquireThenReleaseBySameOwnerDestroysWakelock)
901+{
902+ using namespace ::testing;
903+
904+ testing::NiceMock<MockSharedWakelock> sharedWakelock;
905+ QScopedPointer<QObject> app1(new QObject);
906+
907+ EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject));
908+ sharedWakelock.acquire(app1.data());
909+ sharedWakelock.acquire(app1.data());
910+ sharedWakelock.release(app1.data());
911+ EXPECT_FALSE(sharedWakelock.wakelockHeld());
912+}
913+
914+TEST(SharedWakelock, acquireByDifferentOwnerOnlyCreatesASingleWakelock)
915+{
916+ using namespace ::testing;
917+
918+ testing::NiceMock<MockSharedWakelock> sharedWakelock;
919+ QScopedPointer<QObject> app1(new QObject);
920+ QScopedPointer<QObject> app2(new QObject);
921+
922+ EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject));
923+ sharedWakelock.acquire(app1.data());
924+ sharedWakelock.acquire(app2.data());
925+}
926+
927+TEST(SharedWakelock, twoOwnersWhenOneReleasesStillHoldWakelock)
928+{
929+ using namespace ::testing;
930+
931+ testing::NiceMock<MockSharedWakelock> sharedWakelock;
932+ QScopedPointer<QObject> app1(new QObject);
933+ QScopedPointer<QObject> app2(new QObject);
934+
935+ EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject));
936+ sharedWakelock.acquire(app1.data());
937+ sharedWakelock.acquire(app2.data());
938+ sharedWakelock.release(app1.data());
939+ EXPECT_TRUE(sharedWakelock.wakelockHeld());
940+}
941+
942+TEST(SharedWakelock, twoOwnersWhenBothReleaseWakelockReleased)
943+{
944+ using namespace ::testing;
945+
946+ testing::NiceMock<MockSharedWakelock> sharedWakelock;
947+ QScopedPointer<QObject> app1(new QObject);
948+ QScopedPointer<QObject> app2(new QObject);
949+
950+ EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject));
951+ sharedWakelock.acquire(app1.data());
952+ sharedWakelock.acquire(app2.data());
953+ sharedWakelock.release(app2.data());
954+ sharedWakelock.release(app1.data());
955+ EXPECT_FALSE(sharedWakelock.wakelockHeld());
956+}
957+
958+TEST(SharedWakelock, doubleReleaseOfSingleOwnerIgnored)
959+{
960+ using namespace ::testing;
961+
962+ testing::NiceMock<MockSharedWakelock> sharedWakelock;
963+ QScopedPointer<QObject> app1(new QObject);
964+ QScopedPointer<QObject> app2(new QObject);
965+
966+ EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject));
967+ sharedWakelock.acquire(app1.data());
968+ sharedWakelock.acquire(app2.data());
969+ sharedWakelock.release(app1.data());
970+ EXPECT_TRUE(sharedWakelock.wakelockHeld());
971+
972+ sharedWakelock.release(app1.data());
973+ EXPECT_TRUE(sharedWakelock.wakelockHeld());
974+}
975+
976+TEST(SharedWakelock, nullOwnerAcquireIgnored)
977+{
978+ using namespace ::testing;
979+
980+ testing::NiceMock<MockSharedWakelock> sharedWakelock;
981+
982+ EXPECT_CALL(sharedWakelock, createWakelock()).Times(0);
983+ sharedWakelock.acquire(nullptr);
984+}
985+
986+TEST(SharedWakelock, nullOwnerReleaseIgnored)
987+{
988+ using namespace ::testing;
989+
990+ testing::NiceMock<MockSharedWakelock> sharedWakelock;
991+
992+ EXPECT_CALL(sharedWakelock, createWakelock()).Times(0);
993+ sharedWakelock.release(nullptr);
994+}
995+
996+TEST(SharedWakelock, ifOwnerDestroyedWakelockReleased)
997+{
998+ using namespace ::testing;
999+
1000+ testing::NiceMock<MockSharedWakelock> sharedWakelock;
1001+ QScopedPointer<QObject> app1(new QObject);
1002+
1003+ EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject));
1004+ sharedWakelock.acquire(app1.data());
1005+ app1.reset();
1006+ EXPECT_FALSE(sharedWakelock.wakelockHeld());
1007+}
1008
1009=== added file 'tests/modules/common/mock_shared_wakelock.h'
1010--- tests/modules/common/mock_shared_wakelock.h 1970-01-01 00:00:00 +0000
1011+++ tests/modules/common/mock_shared_wakelock.h 2015-01-12 16:51:13 +0000
1012@@ -0,0 +1,44 @@
1013+/*
1014+ * Copyright (C) 2015 Canonical, Ltd.
1015+ *
1016+ * This program is free software: you can redistribute it and/or modify it under
1017+ * the terms of the GNU Lesser General Public License version 3, as published by
1018+ * the Free Software Foundation.
1019+ *
1020+ * This program is distributed in the hope that it will be useful, but WITHOUT
1021+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1022+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1023+ * Lesser General Public License for more details.
1024+ *
1025+ * You should have received a copy of the GNU Lesser General Public License
1026+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1027+ */
1028+
1029+#ifndef MOCK_SHARED_WAKELOCK_H
1030+#define MOCK_SHARED_WAKELOCK_H
1031+
1032+#include <Unity/Application/sharedwakelock.h>
1033+
1034+#include <gmock/gmock.h>
1035+
1036+namespace testing
1037+{
1038+struct MockSharedWakelock : public qtmir::SharedWakelock
1039+{
1040+ MockSharedWakelock()
1041+ {
1042+ ON_CALL(*this, createWakelock()).WillByDefault(Invoke(this, &MockSharedWakelock::doCreateWakelock));
1043+ }
1044+
1045+ MOCK_METHOD0(createWakelock, QObject*());
1046+ bool wakelockHeld() { return m_wakelock; }
1047+
1048+
1049+ QObject* doCreateWakelock() const
1050+ {
1051+ return new QObject;
1052+ }
1053+};
1054+
1055+} // namespace testing
1056+#endif // MOCK_SHARED_WAKELOCK_H
1057
1058=== modified file 'tests/modules/common/qtmir_test.h'
1059--- tests/modules/common/qtmir_test.h 2014-12-01 11:05:01 +0000
1060+++ tests/modules/common/qtmir_test.h 2015-01-12 16:51:13 +0000
1061@@ -24,6 +24,7 @@
1062 #include <Unity/Application/applicationcontroller.h>
1063 #include <Unity/Application/mirsurfacemanager.h>
1064 #include <Unity/Application/sessionmanager.h>
1065+#include <Unity/Application/sharedwakelock.h>
1066 #include <Unity/Application/taskcontroller.h>
1067 #include <Unity/Application/proc_info.h>
1068 #include <mirserver.h>
1069@@ -35,6 +36,7 @@
1070 #include "mock_focus_controller.h"
1071 #include "mock_prompt_session_manager.h"
1072 #include "mock_prompt_session.h"
1073+#include "mock_shared_wakelock.h"
1074
1075 namespace ms = mir::scene;
1076 using namespace qtmir;
1077@@ -97,6 +99,7 @@
1078 , applicationManager{
1079 mirServer,
1080 taskController,
1081+ QSharedPointer<MockSharedWakelock>(&sharedWakelock, [](MockSharedWakelock *){}),
1082 QSharedPointer<DesktopFileReader::Factory>(
1083 &desktopFileReaderFactory,
1084 [](DesktopFileReader::Factory*){}),
1085@@ -140,12 +143,12 @@
1086 auto appSession = std::make_shared<mir::scene::MockSession>(appId.toStdString(), procId);
1087 sessionManager.onSessionStarting(appSession);
1088 return application;
1089- return nullptr;
1090 }
1091
1092 testing::NiceMock<testing::MockApplicationController> appController;
1093 testing::NiceMock<testing::MockProcInfo> procInfo;
1094 testing::NiceMock<testing::MockDesktopFileReaderFactory> desktopFileReaderFactory;
1095+ testing::NiceMock<testing::MockSharedWakelock> sharedWakelock;
1096 QSharedPointer<FakeMirServer> mirServer;
1097 QSharedPointer<TaskController> taskController;
1098 ApplicationManager applicationManager;

Subscribers

People subscribed via source and target branches