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