Merge lp:~gerboland/qtmir/RTM-fix-lifecycle-exempt-keeps-wakelock into lp:qtmir/rtm-14.09

Proposed by Gerry Boland
Status: Merged
Approved by: Albert Astals Cid
Approved revision: 301
Merged at revision: 295
Proposed branch: lp:~gerboland/qtmir/RTM-fix-lifecycle-exempt-keeps-wakelock
Merge into: lp:qtmir/rtm-14.09
Diff against target: 1184 lines (+653/-249)
15 files modified
debian/control (+2/-0)
src/common/abstractdbusservicemonitor.cpp (+2/-3)
src/common/abstractdbusservicemonitor.h (+1/-7)
src/modules/Unity/Application/Application.pro (+1/-1)
src/modules/Unity/Application/application.cpp (+3/-1)
src/modules/Unity/Application/application_manager.cpp (+5/-2)
src/modules/Unity/Application/sharedwakelock.cpp (+120/-57)
src/modules/Unity/Application/sharedwakelock.h (+14/-9)
tests/modules/Application/application_test.cpp (+7/-7)
tests/modules/ApplicationManager/application_manager_test.cpp (+78/-0)
tests/modules/SharedWakelock/SharedWakelock.pro (+11/-0)
tests/modules/SharedWakelock/sharedwakelock_test.cpp (+365/-146)
tests/modules/common/common.pri (+2/-1)
tests/modules/common/mock_shared_wakelock.h (+41/-14)
tests/modules/modules.pro (+1/-1)
To merge this branch: bzr merge lp:~gerboland/qtmir/RTM-fix-lifecycle-exempt-keeps-wakelock
Reviewer Review Type Date Requested Status
Albert Astals Cid (community) Approve
Gerry Boland (community) Abstain
Review via email: mp+251131@code.launchpad.net

Commit message

Refactor wakelock handling. Lifecycle exempt apps now release wakelock when shell tries to suspend them

The previous Wakelock RAII design was faulty as it was wrapping an asynchronous service. It made it possible for wakelocks to be acquired and not be released.

This refactors SharedWakelock to hold a single instance of Wakelock, and Wakelock always holds a DBus connection.

Testing now includes testing the DBus calls are actually emitted.

Adds dependency on libqtdbusmock1-dev and libqtdbustest1-dev

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

Add test

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

2 wakelocks somehow acquired with this by the Music app, something wrong

review: Needs Fixing
297. By Gerry Boland

Refactor wakelock handling. Lifecycle exempt apps now release wakelock when shell tries to suspend them

The previous Wakelock RAII design was faulty as it was wrapping an asynchronous service. It made it possible for wakelocks to be acquired and not be released.

This refactors SharedWakelock to hold a single instance of Wakelock, and Wakelock always holds a DBus connection.

Testing now includes testing the DBus calls are actually emitted.

Adds dependency on libqtdbusmock1-dev and libqtdbustest1-dev

298. By Gerry Boland

Fix acquire(), release(), acquire() being called in quick succession, and only after do 2 cookies get sent from dbus

299. By Gerry Boland

Fix and add test for SharedWakelock acq/rel/acq not loosing a cookie

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

Is good now.

review: Abstain
300. By Gerry Boland

Delete unnecesary line

301. By Gerry Boland

Racey test not so reliable, this helps

Revision history for this message
Albert Astals Cid (aacid) wrote :

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

 * Did CI run pass?
No CI, but i compiled and ran the tests on a krillin

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/control'
2--- debian/control 2015-01-12 12:51:39 +0000
3+++ debian/control 2015-03-06 14:19:39 +0000
4@@ -16,6 +16,8 @@
5 libmirserver-dev (>= 0.8.1),
6 libmtdev-dev,
7 libprocess-cpp-dev,
8+ libqtdbusmock1-dev (>= 0.2),
9+ libqtdbustest1-dev (>= 0.2),
10 libqt5sensors5-dev,
11 libubuntu-app-launch2-dev,
12 libubuntu-application-api-dev (>= 2.1.0),
13
14=== modified file 'src/common/abstractdbusservicemonitor.cpp'
15--- src/common/abstractdbusservicemonitor.cpp 2015-01-16 09:48:21 +0000
16+++ src/common/abstractdbusservicemonitor.cpp 2015-03-06 14:19:39 +0000
17@@ -41,14 +41,13 @@
18 };
19
20 AbstractDBusServiceMonitor::AbstractDBusServiceMonitor(const QString &service, const QString &path,
21- const QString &interface, const Bus bus,
22+ const QString &interface, const QDBusConnection connection,
23 QObject *parent)
24 : QObject(parent)
25 , m_service(service)
26 , m_path(path)
27 , m_interface(interface)
28- , m_busConnection((bus == SystemBus) ? QDBusConnection::systemBus()
29- : QDBusConnection::sessionBus())
30+ , m_busConnection(connection)
31 , m_watcher(new QDBusServiceWatcher(service, m_busConnection))
32 , m_dbusInterface(nullptr)
33 {
34
35=== modified file 'src/common/abstractdbusservicemonitor.h'
36--- src/common/abstractdbusservicemonitor.h 2015-01-16 09:48:21 +0000
37+++ src/common/abstractdbusservicemonitor.h 2015-03-06 14:19:39 +0000
38@@ -31,17 +31,11 @@
39 class Q_DECL_EXPORT AbstractDBusServiceMonitor : public QObject
40 {
41 Q_OBJECT
42- Q_ENUMS(Bus)
43 Q_PROPERTY(bool serviceAvailable READ serviceAvailable NOTIFY serviceAvailableChanged)
44
45 public:
46- enum Bus {
47- SessionBus,
48- SystemBus,
49- };
50-
51 explicit AbstractDBusServiceMonitor(const QString &service, const QString &path, const QString &interface,
52- const Bus bus = SessionBus,
53+ const QDBusConnection connection = QDBusConnection::sessionBus(),
54 QObject *parent = 0);
55 ~AbstractDBusServiceMonitor();
56
57
58=== modified file 'src/modules/Unity/Application/Application.pro'
59--- src/modules/Unity/Application/Application.pro 2015-01-16 09:48:21 +0000
60+++ src/modules/Unity/Application/Application.pro 2015-03-06 14:19:39 +0000
61@@ -12,7 +12,7 @@
62 QMAKE_CXXFLAGS = -std=c++11 -Werror -Wall
63 QMAKE_LFLAGS = -std=c++11 -Wl,-no-undefined
64
65-PKGCONFIG += mirserver glib-2.0 gio-unix-2.0 process-cpp ubuntu-app-launch-2
66+PKGCONFIG += mirserver glib-2.0 gio-unix-2.0 process-cpp ubuntu-app-launch-2 libqtdbusmock-1 libqtdbustest-1
67
68 INCLUDEPATH += ../../../platforms/mirserver ../../../common
69 LIBS += -L../../../platforms/mirserver -lqpa-mirserver
70
71=== modified file 'src/modules/Unity/Application/application.cpp'
72--- src/modules/Unity/Application/application.cpp 2015-01-16 09:48:21 +0000
73+++ src/modules/Unity/Application/application.cpp 2015-03-06 14:19:39 +0000
74@@ -319,7 +319,9 @@
75 void Application::setFocused(bool focused)
76 {
77 qCDebug(QTMIR_APPLICATIONS) << "Application::setFocused - appId=" << appId() << "focused=" << focused;
78- holdWakelock(true);
79+ if (focused) {
80+ holdWakelock(true);
81+ }
82
83 if (m_focused != focused) {
84 m_focused = focused;
85
86=== modified file 'src/modules/Unity/Application/application_manager.cpp'
87--- src/modules/Unity/Application/application_manager.cpp 2015-01-16 09:48:21 +0000
88+++ src/modules/Unity/Application/application_manager.cpp 2015-03-06 14:19:39 +0000
89@@ -372,9 +372,12 @@
90 return false;
91 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::suspendApplication - appId=" << application->appId();
92
93- // Present in exceptions list, return.
94- if (!m_lifecycleExceptions.filter(application->appId().section('_',0,0)).empty())
95+ // Present in exceptions list, explicitly release wakelock and return. There's no need to keep the wakelock
96+ // as the process is never suspended and thus has no cleanup to perform when (for example) the display is blanked
97+ if (!m_lifecycleExceptions.filter(application->appId().section('_',0,0)).empty()) {
98+ m_sharedWakelock->release(application);
99 return false;
100+ }
101
102 if (m_forceDashActive && application->appId() == "unity8-dash") {
103 return false;
104
105=== modified file 'src/modules/Unity/Application/sharedwakelock.cpp'
106--- src/modules/Unity/Application/sharedwakelock.cpp 2015-01-16 09:48:21 +0000
107+++ src/modules/Unity/Application/sharedwakelock.cpp 2015-03-06 14:19:39 +0000
108@@ -29,57 +29,129 @@
109 const char cookieFile[] = "/tmp/qtmir_powerd_cookie";
110
111 /**
112- * @brief The Wakelock class - on creation acquires a system wakelock, on destruction releases it
113- * Designed in the spirit of RAII. Should the PowerD service vanish from the bus, the wakelock
114- * will be re-acquired when it re-joins the bus.
115+ * @brief The Wakelock class - wraps a single system wakelock
116+ * Should the PowerD service vanish from the bus, the wakelock will be re-acquired when it re-joins the bus.
117 */
118 class Wakelock : public AbstractDBusServiceMonitor
119 {
120 Q_OBJECT
121 public:
122- Wakelock() noexcept
123- : AbstractDBusServiceMonitor("com.canonical.powerd", "/com/canonical/powerd", "com.canonical.powerd", SystemBus)
124+ Wakelock(const QDBusConnection &connection) noexcept
125+ : AbstractDBusServiceMonitor("com.canonical.powerd", "/com/canonical/powerd", "com.canonical.powerd", connection)
126+ , m_wakelockEnabled(false)
127 {
128 // (re-)acquire wake lock when powerd (re-)appears on the bus
129 QObject::connect(this, &Wakelock::serviceAvailableChanged,
130- this, &Wakelock::acquireWakelock);
131-
132- if (!serviceAvailable()) {
133- qWarning() << "com.canonical.powerd DBus interface not available, waiting for it";
134- return;
135- }
136+ this, &Wakelock::onServiceAvailableChanged);
137
138 // WORKAROUND: if shell crashed while it held a wakelock, due to bug lp:1409722 powerd will not have released
139 // the wakelock for it. As workaround, we save the cookie to file and restore it if possible.
140- QFile cookie(cookieFile);
141- if (cookie.exists() && cookie.open(QFile::ReadOnly | QFile::Text)) {
142- m_cookie = cookie.readAll();
143- } else {
144- acquireWakelock(true);
145+ QFile cookieCache(cookieFile);
146+ if (cookieCache.exists() && cookieCache.open(QFile::ReadOnly | QFile::Text)) {
147+ m_wakelockEnabled = true;
148+ m_cookie = cookieCache.readAll();
149 }
150 }
151
152 virtual ~Wakelock() noexcept
153 {
154+ release();
155+ }
156+
157+ Q_SIGNAL void enabledChanged(bool);
158+ bool enabled() const
159+ {
160+ return m_wakelockEnabled;
161+ }
162+
163+ void acquire()
164+ {
165+ if (m_wakelockEnabled) { // wakelock already requested/set
166+ return;
167+ }
168+ m_wakelockEnabled = true;
169+
170+ acquireWakelock();
171+ }
172+
173+ void release()
174+ {
175 QFile::remove(cookieFile);
176
177+ if (!m_wakelockEnabled) { // no wakelock already requested/set
178+ return;
179+ }
180+ m_wakelockEnabled = false;
181+ Q_EMIT enabledChanged(false);
182+
183 if (!serviceAvailable()) {
184- qWarning() << "com.canonical.powerd DBus interface not available";
185+ qWarning() << "com.canonical.powerd DBus interface not available, presuming no wakelocks held";
186 return;
187 }
188
189 if (!m_cookie.isEmpty()) {
190- dbusInterface()->asyncCall("clearSysState", m_cookie);
191+ dbusInterface()->asyncCall("clearSysState", QString(m_cookie));
192+ qCDebug(QTMIR_SESSIONS) << "Wakelock released" << m_cookie;
193+ m_cookie.clear();
194 }
195- qCDebug(QTMIR_SESSIONS) << "Wakelock released";
196 }
197
198 private Q_SLOTS:
199- void acquireWakelock(bool available)
200+ void onServiceAvailableChanged(bool available)
201 {
202- if (!available) {
203- m_cookie.clear(); // clear cookie so that when powerd re-appears, new cookie will be set
204+ // Assumption is if service vanishes & reappears on the bus, it has lost its wakelock state and
205+ // we must re-acquire if necessary
206+ if (!m_wakelockEnabled) {
207+ return;
208+ }
209+
210+ if (available) {
211+ acquireWakelock();
212+ } else {
213+ m_cookie.clear();
214 QFile::remove(cookieFile);
215+ }
216+ }
217+
218+ void onWakeLockAcquired(QDBusPendingCallWatcher *call)
219+ {
220+ QDBusPendingReply<QString> reply = *call;
221+ if (reply.isError()) {
222+ qCDebug(QTMIR_SESSIONS) << "Wakelock was NOT acquired, error:"
223+ << QDBusError::errorString(reply.error().type());
224+ if (m_wakelockEnabled) {
225+ m_wakelockEnabled = false;
226+ Q_EMIT enabledChanged(false);
227+ }
228+
229+ call->deleteLater();
230+ return;
231+ }
232+ QByteArray cookie = reply.argumentAt<0>().toLatin1();
233+ call->deleteLater();
234+
235+ if (!m_wakelockEnabled || !m_cookie.isEmpty()) {
236+ // notified wakelock was created, but we either don't want it, or already have one - release it immediately
237+ dbusInterface()->asyncCall("clearSysState", QString(cookie));
238+ return;
239+ }
240+
241+ m_cookie = cookie;
242+
243+ // see WORKAROUND above for why we save cookie to disk
244+ QFile cookieCache(cookieFile);
245+ cookieCache.open(QFile::WriteOnly | QFile::Text);
246+ cookieCache.write(m_cookie);
247+
248+ qCDebug(QTMIR_SESSIONS) << "Wakelock acquired" << m_cookie;
249+ Q_EMIT enabledChanged(true);
250+ }
251+
252+private:
253+ void acquireWakelock()
254+ {
255+ if (!serviceAvailable()) {
256+ qWarning() << "com.canonical.powerd DBus interface not available, waiting for it";
257 return;
258 }
259
260@@ -90,29 +162,8 @@
261 this, &Wakelock::onWakeLockAcquired);
262 }
263
264- void onWakeLockAcquired(QDBusPendingCallWatcher *call)
265- {
266- if (m_cookie.isEmpty()) { // don't overwrite existing cookie
267- QDBusPendingReply<QString> reply = *call;
268- if (reply.isError()) {
269- qCDebug(QTMIR_SESSIONS) << "Wakelock was NOT acquired, error:"
270- << QDBusError::errorString(reply.error().type());
271- } else {
272- m_cookie = reply.argumentAt<0>();
273-
274- // see WORKAROUND above for why we save cookie to disk
275- QFile cookie(cookieFile);
276- cookie.open(QFile::WriteOnly | QFile::Text);
277- cookie.write(m_cookie.toLatin1());
278-
279- qCDebug(QTMIR_SESSIONS) << "Wakelock acquired" << m_cookie;
280- }
281- }
282- call->deleteLater();
283- }
284-
285-private:
286- QString m_cookie;
287+ QByteArray m_cookie;
288+ bool m_wakelockEnabled;
289
290 Q_DISABLE_COPY(Wakelock)
291 };
292@@ -132,36 +183,48 @@
293 * Note a caller cannot have multiple shares of the wakelock. Multiple calls to acquire are ignored.
294 */
295
296-QObject* SharedWakelock::createWakelock()
297-{
298- return new Wakelock;
299-}
300-
301+SharedWakelock::SharedWakelock(const QDBusConnection &connection)
302+ : m_wakelock(new Wakelock(connection))
303+{
304+ connect(m_wakelock.data(), &Wakelock::enabledChanged,
305+ this, &SharedWakelock::enabledChanged);
306+}
307+
308+// Define empty deconstructor here, as QScopedPointer<Wakelock> requires the destructor of the Wakelock class
309+// to be defined first.
310+SharedWakelock::~SharedWakelock()
311+{
312+}
313+
314+bool SharedWakelock::enabled() const
315+{
316+ return m_wakelock->enabled();
317+}
318
319 void SharedWakelock::acquire(const QObject *caller)
320 {
321- if (m_owners.contains(caller) || caller == nullptr) {
322+ if (caller == nullptr || m_owners.contains(caller)) {
323 return;
324 }
325
326 // register a slot to remove itself from owners list if destroyed
327 QObject::connect(caller, &QObject::destroyed, this, &SharedWakelock::release);
328
329- if (m_wakelock.isNull()) {
330- m_wakelock.reset(createWakelock());
331- }
332+ m_wakelock->acquire();
333
334 m_owners.insert(caller);
335 }
336
337 void SharedWakelock::release(const QObject *caller)
338 {
339- if (!m_owners.remove(caller) || caller == nullptr) {
340+ if (caller == nullptr || !m_owners.remove(caller)) {
341 return;
342 }
343
344- if (m_owners.empty() && m_wakelock) {
345- m_wakelock.reset();
346+ QObject::disconnect(caller, &QObject::destroyed, this, 0);
347+
348+ if (m_owners.empty()) {
349+ m_wakelock->release();
350 }
351 }
352
353
354=== modified file 'src/modules/Unity/Application/sharedwakelock.h'
355--- src/modules/Unity/Application/sharedwakelock.h 2015-01-16 09:48:21 +0000
356+++ src/modules/Unity/Application/sharedwakelock.h 2015-03-06 14:19:39 +0000
357@@ -19,26 +19,31 @@
358 #ifndef WAKELOCK_H
359 #define WAKELOCK_H
360
361-#include <QObject>
362+#include <QDBusConnection>
363 #include <QSet>
364 #include <QScopedPointer>
365
366 namespace qtmir {
367
368+class Wakelock;
369 class SharedWakelock : public QObject
370 {
371 Q_OBJECT
372+ Q_PROPERTY(bool enabled READ enabled NOTIFY enabledChanged)
373 public:
374- SharedWakelock() = default;
375- virtual ~SharedWakelock() noexcept = default;
376-
377- void acquire(const QObject *caller);
378- Q_SLOT void release(const QObject *caller);
379+ SharedWakelock(const QDBusConnection& connection = QDBusConnection::systemBus());
380+ virtual ~SharedWakelock();
381+
382+ virtual bool enabled() const;
383+
384+ virtual void acquire(const QObject *caller);
385+ Q_SLOT virtual void release(const QObject *caller);
386+
387+Q_SIGNALS:
388+ void enabledChanged(bool enabled);
389
390 protected:
391- virtual QObject* createWakelock(); // override to test
392-
393- QScopedPointer<QObject> m_wakelock;
394+ QScopedPointer<Wakelock> m_wakelock;
395 QSet<const QObject *> m_owners;
396
397 private:
398
399=== modified file 'tests/modules/Application/application_test.cpp'
400--- tests/modules/Application/application_test.cpp 2015-01-16 09:48:21 +0000
401+++ tests/modules/Application/application_test.cpp 2015-03-06 14:19:39 +0000
402@@ -34,7 +34,7 @@
403 {
404 using namespace ::testing;
405
406- EXPECT_CALL(sharedWakelock, createWakelock()).Times(1);
407+ EXPECT_CALL(sharedWakelock, acquire(_)).Times(1);
408
409 startApplication(123, "app");
410 applicationManager.focusApplication("app");
411@@ -50,14 +50,14 @@
412 applicationManager.focusApplication("app");
413
414 Q_EMIT session->suspended();
415- EXPECT_FALSE(sharedWakelock.wakelockHeld());
416+ EXPECT_FALSE(sharedWakelock.enabled());
417 }
418
419 TEST_F(ApplicationTests, checkResumeAcquiresWakeLock)
420 {
421 using namespace ::testing;
422
423- EXPECT_CALL(sharedWakelock, createWakelock()).Times(1);
424+ EXPECT_CALL(sharedWakelock, acquire(_)).Times(1);
425
426 auto app = startApplication(123, "app");
427 auto session = app->session();
428@@ -69,7 +69,7 @@
429 {
430 using namespace ::testing;
431
432- EXPECT_CALL(sharedWakelock, createWakelock()).Times(1);
433+ EXPECT_CALL(sharedWakelock, acquire(_)).Times(1);
434 const QString appId = "app";
435
436 auto app = startApplication(123, "app");
437@@ -89,7 +89,7 @@
438 {
439 using namespace ::testing;
440
441- EXPECT_CALL(sharedWakelock, createWakelock()).Times(0);
442+ EXPECT_CALL(sharedWakelock, acquire(_)).Times(0);
443
444 startApplication(123, "unity8-dash");
445 applicationManager.focusApplication("unity8-dash");
446@@ -105,14 +105,14 @@
447 applicationManager.focusApplication("unity8-dash");
448
449 Q_EMIT session->suspended();
450- EXPECT_FALSE(sharedWakelock.wakelockHeld());
451+ EXPECT_FALSE(sharedWakelock.enabled());
452 }
453
454 TEST_F(ApplicationTests, checkDashResumeDoesNotAcquireWakeLock)
455 {
456 using namespace ::testing;
457
458- EXPECT_CALL(sharedWakelock, createWakelock()).Times(0);
459+ EXPECT_CALL(sharedWakelock, acquire(_)).Times(0);
460
461 auto app = startApplication(123, "unity8-dash");
462 auto session = app->session();
463
464=== modified file 'tests/modules/ApplicationManager/application_manager_test.cpp'
465--- tests/modules/ApplicationManager/application_manager_test.cpp 2014-10-20 18:48:53 +0000
466+++ tests/modules/ApplicationManager/application_manager_test.cpp 2015-03-06 14:19:39 +0000
467@@ -2135,3 +2135,81 @@
468 cv.wait(lk, [&] { return done; } );
469 }
470 }
471+
472+/*
473+ * Test lifecycle exempt applications have their wakelocks released when shell tries to suspend them
474+ */
475+TEST_F(ApplicationManagerTests,lifecycleExemptAppsHaveWakelockReleasedOnAttemptedSuspend)
476+{
477+ using namespace ::testing;
478+
479+ const QString appId("com.ubuntu.music"); // member of lifecycle exemption list
480+ const quint64 procId = 12345;
481+
482+ // Set up Mocks & signal watcher
483+ auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
484+ ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
485+ ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
486+
487+ ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
488+
489+ EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
490+ .Times(1)
491+ .WillOnce(Return(true));
492+
493+ auto application = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
494+ applicationManager.onProcessStarting(appId);
495+ std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
496+ bool authed = true;
497+ applicationManager.authorizeSession(procId, authed);
498+ onSessionStarting(session);
499+
500+ // App creates surface, focuses it so state is running
501+ std::shared_ptr<mir::scene::Surface> surface(nullptr);
502+ applicationManager.onSessionCreatedSurface(session.get(), surface);
503+ applicationManager.focusApplication(appId);
504+
505+ applicationManager.unfocusCurrentApplication();
506+
507+ EXPECT_FALSE(sharedWakelock.enabled());
508+ EXPECT_EQ(application->state(), Application::Running);
509+}
510+
511+/*
512+ * Test lifecycle exempt applications have their wakelocks released on suspend
513+ */
514+TEST_F(ApplicationManagerTests,lifecycleExemptAppsHaveWakelockReleasedOnUnSuspend)
515+{
516+ using namespace ::testing;
517+
518+ const QString appId("com.ubuntu.music"); // member of lifecycle exemption list
519+ const quint64 procId = 12345;
520+
521+ // Set up Mocks & signal watcher
522+ auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
523+ ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
524+ ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
525+
526+ ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
527+
528+ EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
529+ .Times(1)
530+ .WillOnce(Return(true));
531+
532+ auto application = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
533+ applicationManager.onProcessStarting(appId);
534+ std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
535+ bool authed = true;
536+ applicationManager.authorizeSession(procId, authed);
537+ onSessionStarting(session);
538+
539+ // App creates surface, focuses it so state is running
540+ std::shared_ptr<mir::scene::Surface> surface(nullptr);
541+ applicationManager.onSessionCreatedSurface(session.get(), surface);
542+ applicationManager.focusApplication(appId);
543+
544+ applicationManager.setSuspended(true);
545+
546+ EXPECT_FALSE(sharedWakelock.enabled());
547+ EXPECT_EQ(application->state(), Application::Running);
548+}
549
550=== added file 'tests/modules/SharedWakelock/SharedWakelock.pro'
551--- tests/modules/SharedWakelock/SharedWakelock.pro 1970-01-01 00:00:00 +0000
552+++ tests/modules/SharedWakelock/SharedWakelock.pro 2015-03-06 14:19:39 +0000
553@@ -0,0 +1,11 @@
554+include(../../test-includes.pri)
555+include(../common/common.pri)
556+
557+TARGET = sharedwakelock_test
558+PKGCONFIG += libqtdbusmock-1 libqtdbustest-1
559+
560+INCLUDEPATH += \
561+ ../../../src/modules/Unity/Application
562+
563+SOURCES += \
564+ sharedwakelock_test.cpp
565
566=== modified file 'tests/modules/SharedWakelock/sharedwakelock_test.cpp'
567--- tests/modules/SharedWakelock/sharedwakelock_test.cpp 2015-01-16 09:48:21 +0000
568+++ tests/modules/SharedWakelock/sharedwakelock_test.cpp 2015-03-06 14:19:39 +0000
569@@ -17,154 +17,373 @@
570
571 #include <Unity/Application/sharedwakelock.h>
572
573-#include "mock_shared_wakelock.h"
574+#include <libqtdbusmock/DBusMock.h>
575
576-#include <gmock/gmock.h>
577+#include <QCoreApplication>
578+#include <QSignalSpy>
579 #include <gtest/gtest.h>
580
581 using namespace qtmir;
582-using testing::MockSharedWakelock;
583-
584-TEST(SharedWakelock, acquireCreatesAWakelock)
585-{
586- using namespace ::testing;
587-
588- testing::NiceMock<MockSharedWakelock> sharedWakelock;
589- QScopedPointer<QObject> app1(new QObject);
590-
591- EXPECT_CALL(sharedWakelock, createWakelock()).Times(1);
592- sharedWakelock.acquire(app1.data());
593-}
594-
595-TEST(SharedWakelock, acquireThenReleaseDestroysTheWakelock)
596-{
597- using namespace ::testing;
598-
599- testing::NiceMock<MockSharedWakelock> sharedWakelock;
600- QScopedPointer<QObject> app1(new QObject);
601-
602- sharedWakelock.acquire(app1.data());
603- sharedWakelock.release(app1.data());
604- EXPECT_FALSE(sharedWakelock.wakelockHeld());
605-}
606-
607-TEST(SharedWakelock, doubleAcquireBySameOwnerOnlyCreatesASingleWakelock)
608-{
609- using namespace ::testing;
610-
611- testing::NiceMock<MockSharedWakelock> sharedWakelock;
612- QScopedPointer<QObject> app1(new QObject);
613-
614- EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject));
615- sharedWakelock.acquire(app1.data());
616- sharedWakelock.acquire(app1.data());
617-}
618-
619-TEST(SharedWakelock, doubleAcquireThenReleaseBySameOwnerDestroysWakelock)
620-{
621- using namespace ::testing;
622-
623- testing::NiceMock<MockSharedWakelock> sharedWakelock;
624- QScopedPointer<QObject> app1(new QObject);
625-
626- EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject));
627- sharedWakelock.acquire(app1.data());
628- sharedWakelock.acquire(app1.data());
629- sharedWakelock.release(app1.data());
630- EXPECT_FALSE(sharedWakelock.wakelockHeld());
631-}
632-
633-TEST(SharedWakelock, acquireByDifferentOwnerOnlyCreatesASingleWakelock)
634-{
635- using namespace ::testing;
636-
637- testing::NiceMock<MockSharedWakelock> sharedWakelock;
638- QScopedPointer<QObject> app1(new QObject);
639- QScopedPointer<QObject> app2(new QObject);
640-
641- EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject));
642- sharedWakelock.acquire(app1.data());
643- sharedWakelock.acquire(app2.data());
644-}
645-
646-TEST(SharedWakelock, twoOwnersWhenOneReleasesStillHoldWakelock)
647-{
648- using namespace ::testing;
649-
650- testing::NiceMock<MockSharedWakelock> sharedWakelock;
651- QScopedPointer<QObject> app1(new QObject);
652- QScopedPointer<QObject> app2(new QObject);
653-
654- EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject));
655- sharedWakelock.acquire(app1.data());
656- sharedWakelock.acquire(app2.data());
657- sharedWakelock.release(app1.data());
658- EXPECT_TRUE(sharedWakelock.wakelockHeld());
659-}
660-
661-TEST(SharedWakelock, twoOwnersWhenBothReleaseWakelockReleased)
662-{
663- using namespace ::testing;
664-
665- testing::NiceMock<MockSharedWakelock> sharedWakelock;
666- QScopedPointer<QObject> app1(new QObject);
667- QScopedPointer<QObject> app2(new QObject);
668-
669- EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject));
670- sharedWakelock.acquire(app1.data());
671- sharedWakelock.acquire(app2.data());
672- sharedWakelock.release(app2.data());
673- sharedWakelock.release(app1.data());
674- EXPECT_FALSE(sharedWakelock.wakelockHeld());
675-}
676-
677-TEST(SharedWakelock, doubleReleaseOfSingleOwnerIgnored)
678-{
679- using namespace ::testing;
680-
681- testing::NiceMock<MockSharedWakelock> sharedWakelock;
682- QScopedPointer<QObject> app1(new QObject);
683- QScopedPointer<QObject> app2(new QObject);
684-
685- EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject));
686- sharedWakelock.acquire(app1.data());
687- sharedWakelock.acquire(app2.data());
688- sharedWakelock.release(app1.data());
689- EXPECT_TRUE(sharedWakelock.wakelockHeld());
690-
691- sharedWakelock.release(app1.data());
692- EXPECT_TRUE(sharedWakelock.wakelockHeld());
693-}
694-
695-TEST(SharedWakelock, nullOwnerAcquireIgnored)
696-{
697- using namespace ::testing;
698-
699- testing::NiceMock<MockSharedWakelock> sharedWakelock;
700-
701- EXPECT_CALL(sharedWakelock, createWakelock()).Times(0);
702- sharedWakelock.acquire(nullptr);
703-}
704-
705-TEST(SharedWakelock, nullOwnerReleaseIgnored)
706-{
707- using namespace ::testing;
708-
709- testing::NiceMock<MockSharedWakelock> sharedWakelock;
710-
711- EXPECT_CALL(sharedWakelock, createWakelock()).Times(0);
712- sharedWakelock.release(nullptr);
713-}
714-
715-TEST(SharedWakelock, ifOwnerDestroyedWakelockReleased)
716-{
717- using namespace ::testing;
718-
719- testing::NiceMock<MockSharedWakelock> sharedWakelock;
720- QScopedPointer<QObject> app1(new QObject);
721-
722- EXPECT_CALL(sharedWakelock, createWakelock()).Times(1).WillOnce(Return(new QObject));
723- sharedWakelock.acquire(app1.data());
724- app1.reset();
725- EXPECT_FALSE(sharedWakelock.wakelockHeld());
726+using namespace testing;
727+using namespace QtDBusTest;
728+using namespace QtDBusMock;
729+
730+const char POWERD_NAME[] = "com.canonical.powerd";
731+const char POWERD_PATH[] = "/com/canonical/powerd";
732+const char POWERD_INTERFACE[] = "com.canonical.powerd";
733+
734+class SharedWakelockTest: public Test
735+{
736+protected:
737+ SharedWakelockTest()
738+ : mock(dbus)
739+ {
740+ mock.registerCustomMock(POWERD_NAME,
741+ POWERD_PATH,
742+ POWERD_INTERFACE,
743+ QDBusConnection::SystemBus);
744+
745+ dbus.startServices();
746+ }
747+
748+ virtual ~SharedWakelockTest()
749+ {}
750+
751+ virtual OrgFreedesktopDBusMockInterface& powerdMockInterface()
752+ {
753+ return mock.mockInterface(POWERD_NAME,
754+ POWERD_PATH,
755+ POWERD_INTERFACE,
756+ QDBusConnection::SystemBus);
757+ }
758+
759+ void implementRequestSysState() {
760+ // Defines the mock impementation of this DBus method
761+ powerdMockInterface().AddMethod("com.canonical.powerd",
762+ "requestSysState", "si", "s", "ret = 'cookie'").waitForFinished();
763+ }
764+
765+ void implementClearSysState() {
766+ powerdMockInterface().AddMethod("com.canonical.powerd",
767+ "clearSysState", "s", "", "").waitForFinished();
768+ }
769+
770+ void EXPECT_CALL(const QList<QVariantList> &spy, int index,
771+ const QString &name, const QVariantList &args)
772+ {
773+ QVariant args2(QVariant::fromValue(args));
774+ ASSERT_LT(index, spy.size());
775+ const QVariantList &call(spy.at(index));
776+ EXPECT_EQ(name, call.at(0).toString());
777+ EXPECT_EQ(args2.toString().toStdString(), call.at(1).toString().toStdString());
778+ }
779+
780+ DBusTestRunner dbus;
781+ DBusMock mock;
782+};
783+
784+TEST_F(SharedWakelockTest, acquireCreatesAWakelock)
785+{
786+ implementRequestSysState();
787+
788+ /* Note: we pass the DBusTestRunner constructed system DBus connection instead of letting
789+ * Qt create one. This is done as Qt has a non-testing friendly way of handling the built-in
790+ * system and session connections, and as DBusTestRunner restarts the DBus daemon for each
791+ * testcase, it unfortunately means Qt would end up pointing to a dead bus after one testcase. */
792+ SharedWakelock wakelock(dbus.systemConnection());
793+
794+ // Verify the DBus method is called & wakelock
795+ QSignalSpy wakelockDBusMethodSpy(&powerdMockInterface(), SIGNAL(MethodCalled(const QString &, const QVariantList &)));
796+ QSignalSpy wakelockEnabledSpy(&wakelock, SIGNAL( enabledChanged(bool) ));
797+
798+ QScopedPointer<QObject> object(new QObject);
799+ wakelock.acquire(object.data());
800+ wakelockDBusMethodSpy.wait();
801+
802+ EXPECT_FALSE(wakelockDBusMethodSpy.empty());
803+ EXPECT_CALL(wakelockDBusMethodSpy, 0, "requestSysState",
804+ QVariantList() << QString("active") << 1);
805+
806+ // Ensure a wakelock created
807+ wakelockEnabledSpy.wait();
808+ EXPECT_FALSE(wakelockEnabledSpy.empty());
809+ EXPECT_TRUE(wakelock.enabled());
810+}
811+
812+TEST_F(SharedWakelockTest, acquireThenReleaseDestroysTheWakelock)
813+{
814+ implementRequestSysState();
815+ implementClearSysState();
816+
817+ SharedWakelock wakelock(dbus.systemConnection());
818+
819+ QSignalSpy wakelockEnabledSpy(&wakelock, SIGNAL( enabledChanged(bool) ));
820+
821+ QScopedPointer<QObject> object(new QObject);
822+ wakelock.acquire(object.data());
823+ wakelockEnabledSpy.wait();
824+
825+ // Verify the DBus method is called
826+ QSignalSpy wakelockDBusMethodSpy(&powerdMockInterface(), SIGNAL(MethodCalled(const QString &, const QVariantList &)));
827+ wakelock.release(object.data());
828+ wakelockDBusMethodSpy.wait();
829+
830+ EXPECT_FALSE(wakelockDBusMethodSpy.empty());
831+ EXPECT_CALL(wakelockDBusMethodSpy, 0, "clearSysState",
832+ QVariantList() << QString("cookie"));
833+
834+ EXPECT_FALSE(wakelock.enabled());
835+}
836+
837+TEST_F(SharedWakelockTest, doubleAcquireBySameOwnerOnlyCreatesASingleWakelock)
838+{
839+ implementRequestSysState();
840+
841+ SharedWakelock wakelock(dbus.systemConnection());
842+
843+ // Verify the DBus method is called & wakelock
844+ QSignalSpy wakelockDBusMethodSpy(&powerdMockInterface(), SIGNAL(MethodCalled(const QString &, const QVariantList &)));
845+
846+ QScopedPointer<QObject> object(new QObject);
847+ wakelock.acquire(object.data());
848+ wakelock.acquire(object.data());
849+ wakelockDBusMethodSpy.wait();
850+
851+ EXPECT_EQ(wakelockDBusMethodSpy.count(), 1);
852+}
853+
854+TEST_F(SharedWakelockTest, doubleAcquireThenReleaseBySameOwnerDestroysWakelock)
855+{
856+ implementRequestSysState();
857+ implementClearSysState();
858+
859+ SharedWakelock wakelock(dbus.systemConnection());
860+
861+ QSignalSpy wakelockEnabledSpy(&wakelock, SIGNAL( enabledChanged(bool) ));
862+ QScopedPointer<QObject> object(new QObject);
863+
864+ wakelock.acquire(object.data());
865+ wakelock.acquire(object.data());
866+ wakelock.release(object.data());
867+ wakelockEnabledSpy.wait();
868+ EXPECT_FALSE(wakelock.enabled());
869+}
870+
871+TEST_F(SharedWakelockTest, acquireByDifferentOwnerOnlyCreatesASingleWakelock)
872+{
873+ implementRequestSysState();
874+
875+ SharedWakelock wakelock(dbus.systemConnection());
876+
877+ // Verify the DBus method is called & wakelock
878+ QSignalSpy wakelockDBusMethodSpy(&powerdMockInterface(), SIGNAL(MethodCalled(const QString &, const QVariantList &)));
879+
880+ QScopedPointer<QObject> object1(new QObject);
881+ QScopedPointer<QObject> object2(new QObject);
882+ wakelock.acquire(object1.data());
883+ wakelock.acquire(object2.data());
884+
885+ wakelockDBusMethodSpy.wait();
886+ EXPECT_EQ(wakelockDBusMethodSpy.count(), 1);
887+}
888+
889+TEST_F(SharedWakelockTest, twoOwnersWhenBothReleaseWakelockReleased)
890+{
891+ implementRequestSysState();
892+
893+ SharedWakelock wakelock(dbus.systemConnection());
894+
895+ QScopedPointer<QObject> object1(new QObject);
896+ QScopedPointer<QObject> object2(new QObject);
897+ wakelock.acquire(object1.data());
898+ wakelock.acquire(object2.data());
899+
900+ QSignalSpy wakelockEnabledSpy(&wakelock, SIGNAL( enabledChanged(bool) ));
901+
902+ wakelock.release(object1.data());
903+ wakelock.release(object2.data());
904+
905+ wakelockEnabledSpy.wait();
906+ EXPECT_FALSE(wakelock.enabled());
907+}
908+
909+TEST_F(SharedWakelockTest, doubleReleaseOfSingleOwnerIgnored)
910+{
911+ implementRequestSysState();
912+
913+ SharedWakelock wakelock(dbus.systemConnection());
914+
915+ QSignalSpy wakelockEnabledSpy(&wakelock, SIGNAL( enabledChanged(bool) ));
916+
917+ QScopedPointer<QObject> object1(new QObject);
918+ QScopedPointer<QObject> object2(new QObject);
919+ wakelock.acquire(object1.data());
920+ wakelock.acquire(object2.data());
921+ wakelock.release(object1.data());
922+
923+ wakelockEnabledSpy.wait();
924+
925+ wakelock.release(object1.data());
926+
927+ EXPECT_TRUE(wakelock.enabled());
928+}
929+
930+TEST_F(SharedWakelockTest, wakelockAcquireReleaseFlood)
931+{
932+ implementRequestSysState();
933+ implementClearSysState();
934+
935+ SharedWakelock wakelock(dbus.systemConnection());
936+
937+ QSignalSpy wakelockEnabledSpy(&wakelock, SIGNAL( enabledChanged(bool) ));
938+
939+ QScopedPointer<QObject> object(new QObject);
940+ wakelock.acquire(object.data());
941+ wakelock.release(object.data());
942+ wakelock.acquire(object.data());
943+ wakelock.release(object.data());
944+ wakelock.acquire(object.data());
945+ wakelock.release(object.data());
946+ while (wakelockEnabledSpy.wait(100)) {}
947+ EXPECT_FALSE(wakelock.enabled());
948+}
949+
950+TEST_F(SharedWakelockTest, wakelockAcquireReleaseAcquireWithDelays)
951+{
952+ powerdMockInterface().AddMethod("com.canonical.powerd",
953+ "requestSysState", "si", "s",
954+ "i=100000\n" // delay the response from the mock dbus instance
955+ "while i>0:\n"
956+ " i-=1\n"
957+ "ret = 'cookie'").waitForFinished();
958+
959+ implementClearSysState();
960+
961+ SharedWakelock wakelock(dbus.systemConnection());
962+
963+ QSignalSpy wakelockDBusMethodSpy(&powerdMockInterface(), SIGNAL(MethodCalled(const QString &, const QVariantList &)));
964+
965+ QScopedPointer<QObject> object(new QObject);
966+ wakelock.acquire(object.data());
967+ wakelock.release(object.data());
968+ wakelock.acquire(object.data());
969+
970+ while (wakelockDBusMethodSpy.wait(100)) {}
971+ EXPECT_TRUE(wakelock.enabled());
972+
973+ // there must be at least one clearSysState call, but is not necessarily the second call
974+ // should the dbus response be slow enough, it may be the third call. Is hard to reliably
975+ // reproduce the racey condition for all platforms.
976+ bool found = false;
977+ for (int i=0; i<wakelockDBusMethodSpy.count(); i++) {
978+ const QVariantList &call(wakelockDBusMethodSpy.at(i));
979+
980+ if (call.at(0).toString() == "clearSysState") {
981+ found = true;
982+ }
983+ }
984+ EXPECT_TRUE(found);
985+}
986+
987+TEST_F(SharedWakelockTest, nullOwnerAcquireIgnored)
988+{
989+ implementRequestSysState();
990+
991+ SharedWakelock wakelock(dbus.systemConnection());
992+
993+ QSignalSpy wakelockEnabledSpy(&wakelock, SIGNAL( enabledChanged(bool) ));
994+
995+ wakelock.acquire(nullptr);
996+
997+ wakelockEnabledSpy.wait(200); // have to wait to see if anything happens
998+
999+ EXPECT_FALSE(wakelock.enabled());
1000+}
1001+
1002+TEST_F(SharedWakelockTest, nullReleaseAcquireIgnored)
1003+{
1004+ implementRequestSysState();
1005+
1006+ SharedWakelock wakelock(dbus.systemConnection());
1007+
1008+ wakelock.release(nullptr);
1009+
1010+ EXPECT_FALSE(wakelock.enabled());
1011+}
1012+
1013+TEST_F(SharedWakelockTest, ifOwnerDestroyedWakelockReleased)
1014+{
1015+ implementRequestSysState();
1016+ implementClearSysState();
1017+
1018+ SharedWakelock wakelock(dbus.systemConnection());
1019+
1020+ QSignalSpy wakelockEnabledSpy(&wakelock, SIGNAL( enabledChanged(bool) ));
1021+
1022+ QScopedPointer<QObject> object(new QObject);
1023+ wakelock.acquire(object.data());
1024+ wakelockEnabledSpy.wait();
1025+
1026+ // Verify the DBus method is called
1027+ QSignalSpy wakelockDBusMethodSpy(&powerdMockInterface(), SIGNAL(MethodCalled(const QString &, const QVariantList &)));
1028+
1029+ object.reset();
1030+ wakelockDBusMethodSpy.wait();
1031+
1032+ EXPECT_FALSE(wakelockDBusMethodSpy.empty());
1033+ EXPECT_CALL(wakelockDBusMethodSpy, 0, "clearSysState",
1034+ QVariantList() << QString("cookie"));
1035+
1036+ EXPECT_FALSE(wakelock.enabled());
1037+}
1038+
1039+TEST_F(SharedWakelockTest, reloadCachedWakelockCookie)
1040+{
1041+ const char cookieFile[] = "/tmp/qtmir_powerd_cookie";
1042+
1043+ // Custom Deleter for QScopedPointer to delete the qtmir cookie file no matter what
1044+ struct ScopedQFileCustomDeleter
1045+ {
1046+ static inline void cleanup(QFile *file)
1047+ {
1048+ file->remove();
1049+ delete file;
1050+ }
1051+ };
1052+ QScopedPointer<QFile, ScopedQFileCustomDeleter> cookieCache(new QFile(cookieFile));
1053+ cookieCache->open(QFile::WriteOnly | QFile::Text);
1054+ cookieCache->write("myCookie");
1055+
1056+ SharedWakelock wakelock(dbus.systemConnection());
1057+ EXPECT_TRUE(wakelock.enabled());
1058+}
1059+
1060+TEST_F(SharedWakelockTest, wakelockReleasedOnSharedWakelockDestroyed)
1061+{
1062+ implementRequestSysState();
1063+ implementClearSysState();
1064+
1065+ auto wakelock = new SharedWakelock(dbus.systemConnection());
1066+
1067+ QSignalSpy wakelockEnabledSpy(wakelock, SIGNAL( enabledChanged(bool) ));
1068+
1069+ QScopedPointer<QObject> object(new QObject);
1070+ wakelock->acquire(object.data());
1071+ wakelockEnabledSpy.wait(); // wait for wakelock to be enabled
1072+
1073+ QSignalSpy wakelockDBusMethodSpy(&powerdMockInterface(), SIGNAL(MethodCalled(const QString &, const QVariantList &)));
1074+ delete wakelock;
1075+ wakelockDBusMethodSpy.wait();
1076+
1077+ EXPECT_FALSE(wakelockDBusMethodSpy.empty());
1078+ EXPECT_CALL(wakelockDBusMethodSpy, 0, "clearSysState",
1079+ QVariantList() << QString("cookie"));
1080+}
1081+
1082+
1083+// Define custom main() as these tests rely on a QEventLoop
1084+int main(int argc, char** argv) {
1085+ QCoreApplication app(argc, argv);
1086+ ::testing::InitGoogleTest(&argc, argv);
1087+ return RUN_ALL_TESTS();
1088 }
1089
1090=== modified file 'tests/modules/common/common.pri'
1091--- tests/modules/common/common.pri 2014-10-20 18:48:53 +0000
1092+++ tests/modules/common/common.pri 2015-03-06 14:19:39 +0000
1093@@ -1,7 +1,7 @@
1094 CONFIG += no_keywords # keywords clash with ProcessC++
1095 PKGCONFIG += ubuntu-app-launch-2 process-cpp
1096
1097-QT += quick
1098+QT += quick dbus
1099
1100 HEADERS += ../common/mock_application_controller.h \
1101 ../common/mock_desktop_file_reader.h \
1102@@ -11,6 +11,7 @@
1103 ../common/mock_prompt_session.h \
1104 ../common/mock_prompt_session_manager.h \
1105 ../common/mock_renderable.h \
1106+ ../common/mock_shared_wakelock.h \
1107 ../common/mock_session.h \
1108 ../common/mock_surface.h \
1109 ../common/qtmir_test.h \
1110
1111=== modified file 'tests/modules/common/mock_shared_wakelock.h'
1112--- tests/modules/common/mock_shared_wakelock.h 2015-01-16 09:48:21 +0000
1113+++ tests/modules/common/mock_shared_wakelock.h 2015-03-06 14:19:39 +0000
1114@@ -23,21 +23,48 @@
1115
1116 namespace testing
1117 {
1118-struct MockSharedWakelock : public qtmir::SharedWakelock
1119+class MockSharedWakelock : public qtmir::SharedWakelock
1120 {
1121- MockSharedWakelock()
1122- {
1123- ON_CALL(*this, createWakelock()).WillByDefault(Invoke(this, &MockSharedWakelock::doCreateWakelock));
1124- }
1125-
1126- MOCK_METHOD0(createWakelock, QObject*());
1127- bool wakelockHeld() { return m_wakelock; }
1128-
1129-
1130- QObject* doCreateWakelock() const
1131- {
1132- return new QObject;
1133- }
1134+public:
1135+ MockSharedWakelock(const QDBusConnection& /*connection*/= QDBusConnection::systemBus())
1136+ {
1137+ ON_CALL(*this, enabled()).WillByDefault(Invoke(this, &MockSharedWakelock::doEnabled));
1138+ ON_CALL(*this, acquire(_)).WillByDefault(Invoke(this, &MockSharedWakelock::doAcquire));
1139+ ON_CALL(*this, release(_)).WillByDefault(Invoke(this, &MockSharedWakelock::doRelease));
1140+ }
1141+
1142+ MOCK_CONST_METHOD0(enabled, bool());
1143+ MOCK_METHOD1(acquire, void(const QObject *));
1144+ MOCK_METHOD1(release, void(const QObject *));
1145+
1146+ bool doEnabled()
1147+ {
1148+ return !m_owners.isEmpty();
1149+ }
1150+
1151+ void doAcquire(const QObject *object)
1152+ {
1153+ if (m_owners.contains(object)) {
1154+ return;
1155+ }
1156+ m_owners.insert(object);
1157+ if (m_owners.size() == 1) {
1158+ Q_EMIT enabledChanged(true);
1159+ }
1160+ }
1161+
1162+ void doRelease(const QObject *object)
1163+ {
1164+ if (!m_owners.remove(object)) {
1165+ return;
1166+ }
1167+ if (m_owners.isEmpty()) {
1168+ Q_EMIT enabledChanged(false);
1169+ }
1170+ }
1171+
1172+private:
1173+ QSet<const QObject *> m_owners;
1174 };
1175
1176 } // namespace testing
1177
1178=== modified file 'tests/modules/modules.pro'
1179--- tests/modules/modules.pro 2015-01-16 09:48:21 +0000
1180+++ tests/modules/modules.pro 2015-03-06 14:19:39 +0000
1181@@ -1,2 +1,2 @@
1182 TEMPLATE = subdirs
1183-SUBDIRS = Application ApplicationManager General MirSurfaceItem SessionManager TaskController DesktopFileReader
1184+SUBDIRS = Application ApplicationManager General MirSurfaceItem SessionManager SharedWakelock TaskController DesktopFileReader

Subscribers

People subscribed via source and target branches