Merge lp:~jonas-drange/ubuntu-system-settings/1219793-reset into lp:ubuntu-system-settings
- 1219793-reset
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Ken VanDine | ||||
Approved revision: | 851 | ||||
Merged at revision: | 875 | ||||
Proposed branch: | lp:~jonas-drange/ubuntu-system-settings/1219793-reset | ||||
Merge into: | lp:ubuntu-system-settings | ||||
Diff against target: |
640 lines (+350/-62) 11 files modified
plugins/reset/CMakeLists.txt (+2/-0) plugins/reset/EntryComponent.qml (+1/-1) plugins/reset/EraseEverything.qml (+22/-4) plugins/reset/PageComponent.qml (+21/-8) plugins/reset/ResetLauncherHome.qml (+27/-6) plugins/reset/reset.cpp (+39/-33) plugins/reset/reset.h (+5/-2) plugins/reset/reset.settings (+1/-2) tests/autopilot/ubuntu_system_settings/__init__.py (+118/-3) tests/autopilot/ubuntu_system_settings/tests/__init__.py (+64/-3) tests/autopilot/ubuntu_system_settings/tests/test_reset.py (+50/-0) |
||||
To merge this branch: | bzr merge lp:~jonas-drange/ubuntu-system-settings/1219793-reset | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ken VanDine | Approve | ||
PS Jenkins bot | continuous-integration | Approve | |
Leo Arias (community) | Needs Fixing | ||
Review via email: mp+228954@code.launchpad.net |
Commit message
[reset] bring back reset and hook it up to the backend. Added happy path tests.
Description of the change
Hi,
this branch makes reset visible by default and exposes "Reset Launcher" and "Erase Everything…" to the user.
The ux has been altered slightly to conform to the new spec.
Binding for factory reset was also written.
Thanks
Omer Akram (om26er) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:842
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Leo Arias (elopio) wrote : | # |
408 + def test_reset_
This is not a good autopilot tests because it doesn't represent a user story. We are not testing there the reason why the user opens the app. It would be better as a QML test, but as the project currently has no QML tests, it's ok for now.
308 + sleep(2)
334 + sleep(2)
Sleeps should be avoided. If there's no way to avoid them, you should explain why in a comment.
In this case, why do we need it? According to the spawn_server docs:
"This function blocks until the spawned DBusMockObject is ready and listening on the bus."
387 + def reset_launcher(
392 + def reset_launcher_
398 + def factory_
403 + def factory_
We have found that we need to follow the page object pattern in order to make the tests maintainable and to be able to cope with design changes. That amongst other things mean that the UI components need to be hidden in objects that model their parents, and to be called from public methods that represent user actions.
It's explained in more detail here: http://
It would be awesome if you can read through it. I'm preparing a branch to show you what I mean. I'll ask for your review when it's ready.
Thanks for adding tests Jonas!
Leo Arias (elopio) : | # |
Leo Arias (elopio) wrote : | # |
Oh, well, I won't be able to do the branch tonight because it's failing to build. So I'm not able to test my changes.
Jonas G. Drange (jonas-drange) wrote : | # |
Thank you for your comments. I think I have addressed all of them in r844.
The reset launcher functionality on the phone does not work, so putting it back in to WIP.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:843
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:848
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:843
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
Ken VanDine (ken-vandine) wrote : | # |
Should the launcher reset restart the shell/device or at least tell they user that it's needed?
Michael Terry (mterry) wrote : | # |
Me coming here from a unity8 POV -- personally, I'd like unity8 updated to notice when the setting changes to the default again, so it doesn't have to be restarted.
Failing that, we should probably tell the user to turn off and on their phone, but that's a very gross message to receive.
Ken VanDine (ken-vandine) wrote : | # |
Ok, we should at least tell the user they need to do something.
- 849. By Jonas G. Drange
-
merge trunk
- 850. By Jonas G. Drange
-
note up front re: restart
Jonas G. Drange (jonas-drange) wrote : | # |
Added note re: restart. Hopefully this will be handled by Unity at some point.
Ken VanDine (ken-vandine) wrote : | # |
Please remove the debug output in reset.cpp and see inline comment.
- 851. By Jonas G. Drange
-
removing hide-by-default and debug output
Ken VanDine (ken-vandine) wrote : | # |
Looks good, thanks!
Preview Diff
1 | === modified file 'plugins/reset/CMakeLists.txt' |
2 | --- plugins/reset/CMakeLists.txt 2013-10-22 14:44:44 +0000 |
3 | +++ plugins/reset/CMakeLists.txt 2014-08-07 14:21:27 +0000 |
4 | @@ -14,6 +14,8 @@ |
5 | ) |
6 | qt5_use_modules(UbuntuResetPanel Qml Quick DBus) |
7 | |
8 | +target_link_libraries(UbuntuResetPanel uss-accountsservice) |
9 | + |
10 | set(PLUG_DIR ${PLUGIN_PRIVATE_MODULE_DIR}/Ubuntu/SystemSettings/Reset) |
11 | install(TARGETS UbuntuResetPanel DESTINATION ${PLUG_DIR}) |
12 | install(FILES qmldir DESTINATION ${PLUG_DIR}) |
13 | |
14 | === modified file 'plugins/reset/EntryComponent.qml' |
15 | --- plugins/reset/EntryComponent.qml 2013-11-28 15:09:15 +0000 |
16 | +++ plugins/reset/EntryComponent.qml 2014-08-07 14:21:27 +0000 |
17 | @@ -24,7 +24,7 @@ |
18 | |
19 | ListItem.Standard { |
20 | id: root |
21 | - |
22 | + objectName: "entryComponent-reset" |
23 | iconSource: model.icon |
24 | iconFrame: false |
25 | text: i18n.tr(model.displayName) |
26 | |
27 | === modified file 'plugins/reset/EraseEverything.qml' |
28 | --- plugins/reset/EraseEverything.qml 2013-08-23 14:05:39 +0000 |
29 | +++ plugins/reset/EraseEverything.qml 2014-08-07 14:21:27 +0000 |
30 | @@ -26,15 +26,33 @@ |
31 | id: eraseEverything |
32 | Dialog { |
33 | id: dialog |
34 | + states: State { |
35 | + name: "clicked" |
36 | + PropertyChanges { |
37 | + target: action |
38 | + enabled: false |
39 | + } |
40 | + PropertyChanges { |
41 | + target: cancel |
42 | + enabled: false |
43 | + } |
44 | + } |
45 | + objectName: "factoryResetDialog" |
46 | text: i18n.tr("All documents, saved games, settings, and other items will be permanently deleted from this phone.") |
47 | Button { |
48 | - text: i18n.tr("Erase & reset everything") |
49 | - enabled: false /* TODO: enable when there is a backend */ |
50 | - onClicked: PopupUtils.close(dialog) |
51 | + id: action |
52 | + text: i18n.tr("Erase & Reset Everything") |
53 | + objectName: "factoryResetAction" |
54 | + onClicked: { |
55 | + dialog.state = "clicked"; |
56 | + resetBackend.factoryReset(); |
57 | + root.done(); |
58 | + } |
59 | } |
60 | Button { |
61 | + id: cancel |
62 | text: i18n.tr("Cancel") |
63 | - onClicked: PopupUtils.close(dialog) |
64 | + onClicked: PopupUtils.close(dialog); |
65 | } |
66 | } |
67 | } |
68 | |
69 | === modified file 'plugins/reset/PageComponent.qml' |
70 | --- plugins/reset/PageComponent.qml 2013-09-06 13:12:46 +0000 |
71 | +++ plugins/reset/PageComponent.qml 2014-08-07 14:21:27 +0000 |
72 | @@ -30,8 +30,18 @@ |
73 | id: root |
74 | |
75 | title: i18n.tr("Reset phone") |
76 | + objectName: "resetPage" |
77 | flickable: scrollWidget |
78 | |
79 | + // workaround for #1231729 |
80 | + // delay destroying popup until pageStack has been popped |
81 | + property var popup |
82 | + function done () { |
83 | + popup.opacity = 0; |
84 | + pageStack.pop(); |
85 | + popup.destroy(1000); |
86 | + } |
87 | + |
88 | Loader { |
89 | id: buttonActions |
90 | asynchronous: false |
91 | @@ -59,24 +69,26 @@ |
92 | ListItem.SingleControl { |
93 | control: Button { |
94 | id: resetLauncherHomeButton |
95 | - text: i18n.tr("Reset launcher & home screen…") |
96 | + objectName: "resetLauncher" |
97 | + text: i18n.tr("Reset Launcher") |
98 | width: parent.width - units.gu(4) |
99 | onClicked: { |
100 | - buttonActions.source = "ResetLauncherHome.qml" |
101 | - PopupUtils.open(buttonActions.item) |
102 | + buttonActions.source = "ResetLauncherHome.qml"; |
103 | + root.popup = PopupUtils.open(buttonActions.item); |
104 | } |
105 | } |
106 | showDivider: false |
107 | } |
108 | |
109 | ListItem.SingleControl { |
110 | + visible: false // enabled when backend is ready/useful |
111 | control: Button { |
112 | id: resetAllSettingsButton |
113 | text: i18n.tr("Reset all system settings…") |
114 | width: parent.width - units.gu(4) |
115 | onClicked: { |
116 | - buttonActions.source = "ResetAllSettings.qml" |
117 | - PopupUtils.open(buttonActions.item) |
118 | + buttonActions.source = "ResetAllSettings.qml"; |
119 | + root.popup = PopupUtils.open(buttonActions.item); |
120 | } |
121 | } |
122 | showDivider: false |
123 | @@ -85,11 +97,12 @@ |
124 | ListItem.SingleControl { |
125 | control: Button { |
126 | id: eraseEverythingButton |
127 | - text: i18n.tr("Erase & reset everything…") |
128 | + objectName: "factoryReset" |
129 | + text: i18n.tr("Erase & Reset Everything…") |
130 | width: parent.width - units.gu(4) |
131 | onClicked: { |
132 | - buttonActions.source = "EraseEverything.qml" |
133 | - PopupUtils.open(buttonActions.item) |
134 | + buttonActions.source = "EraseEverything.qml"; |
135 | + root.popup = PopupUtils.open(buttonActions.item); |
136 | } |
137 | } |
138 | showDivider: false |
139 | |
140 | === modified file 'plugins/reset/ResetLauncherHome.qml' |
141 | --- plugins/reset/ResetLauncherHome.qml 2013-09-06 13:12:46 +0000 |
142 | +++ plugins/reset/ResetLauncherHome.qml 2014-08-07 14:21:27 +0000 |
143 | @@ -24,21 +24,42 @@ |
144 | |
145 | Component { |
146 | id: resetLauncherHome |
147 | - |
148 | Dialog { |
149 | id: dialog |
150 | - text: i18n.tr("The contents and layout of the launcher, and the filters in the home screen will be returned to their original settings.") |
151 | + states: State { |
152 | + name: "clicked" |
153 | + PropertyChanges { |
154 | + target: action |
155 | + enabled: false |
156 | + } |
157 | + PropertyChanges { |
158 | + target: cancel |
159 | + enabled: false |
160 | + } |
161 | + } |
162 | + text: i18n.tr("The Launcher will be returned to its original contents.") |
163 | + objectName: "resetLauncherDialog" |
164 | Button { |
165 | - text: i18n.tr("Reset launcher & home screen") |
166 | + id: action |
167 | + text: i18n.tr("Reset Launcher") |
168 | + objectName: "resetLauncherAction" |
169 | onClicked: { |
170 | - unitySettings.schema.reset("favorites") |
171 | - resetBackend.resetLauncher() |
172 | - PopupUtils.close(dialog) |
173 | + dialog.state = "clicked"; |
174 | + unitySettings.schema.reset("favorites"); |
175 | + resetBackend.resetLauncher(); |
176 | + root.done(); |
177 | } |
178 | } |
179 | Button { |
180 | + id: cancel |
181 | text: i18n.tr("Cancel") |
182 | onClicked: PopupUtils.close(dialog) |
183 | } |
184 | + Label { |
185 | + text: i18n.tr("The phone needs to restart for changes to take effect.") |
186 | + width: parent.width |
187 | + wrapMode: Text.Wrap |
188 | + horizontalAlignment: Text.AlignHCenter |
189 | + } |
190 | } |
191 | } |
192 | |
193 | === modified file 'plugins/reset/reset.cpp' |
194 | --- plugins/reset/reset.cpp 2013-09-06 13:12:46 +0000 |
195 | +++ plugins/reset/reset.cpp 2014-08-07 14:21:27 +0000 |
196 | @@ -22,49 +22,55 @@ |
197 | #include <QEvent> |
198 | #include <QDBusReply> |
199 | #include <unistd.h> |
200 | -#include <QtCore/QDebug> |
201 | - |
202 | -Reset::Reset(QObject *parent) : |
203 | - QObject(parent), |
204 | - m_systemBusConnection (QDBusConnection::systemBus()), |
205 | - m_accountsserviceIface ("org.freedesktop.Accounts", |
206 | - "/org/freedesktop/Accounts", |
207 | - "org.freedesktop.Accounts", |
208 | - m_systemBusConnection) |
209 | +#include <QDBusMetaType> |
210 | + |
211 | +typedef QList<QVariantMap> resetLauncherItemsArg; |
212 | +Q_DECLARE_METATYPE(resetLauncherItemsArg) |
213 | + |
214 | +Reset::Reset(QObject *parent) |
215 | + : QObject(parent), |
216 | + m_systemBusConnection(QDBusConnection::systemBus()) |
217 | { |
218 | - if (!m_accountsserviceIface.isValid()) { |
219 | - return; |
220 | - } |
221 | - |
222 | - QDBusReply<QDBusObjectPath> qObjectPath = m_accountsserviceIface.call( |
223 | - "FindUserById", qlonglong(getuid())); |
224 | - |
225 | - if (qObjectPath.isValid()) { |
226 | - m_objectPath = qObjectPath.value().path(); |
227 | + static bool isRegistered = false; |
228 | + if(!isRegistered) { |
229 | + qDBusRegisterMetaType<resetLauncherItemsArg>(); |
230 | + isRegistered = true; |
231 | } |
232 | } |
233 | |
234 | bool Reset::resetLauncher() |
235 | { |
236 | - QDBusInterface userInterface ( |
237 | - "org.freedesktop.Accounts", |
238 | - m_objectPath, |
239 | - "org.freedesktop.DBus.Properties.Set", |
240 | - m_systemBusConnection, |
241 | - this); |
242 | - |
243 | - if (!userInterface.isValid()) |
244 | - return false; |
245 | - |
246 | QList<QVariantMap> items; |
247 | QVariantMap defaults; |
248 | defaults.insert("defaults", true); |
249 | items << defaults; |
250 | - /* TODO: test again-enable once the unity side lands |
251 | - userInterface.call("Set", |
252 | - "com.canonical.unity.AccountsService", |
253 | - "launcher-items", |
254 | - QVariant::fromValue(items));*/ |
255 | + QVariant answer = m_accountsService.setUserProperty( |
256 | + "com.canonical.unity.AccountsService", |
257 | + "launcher-items", |
258 | + QVariant::fromValue(items)); |
259 | + |
260 | + if (answer.isValid()) |
261 | + return true; |
262 | + |
263 | + return false; |
264 | +} |
265 | + |
266 | +bool Reset::factoryReset() |
267 | +{ |
268 | + QDBusInterface systemServiceInterface ( |
269 | + "com.canonical.SystemImage", |
270 | + "/Service", |
271 | + "com.canonical.SystemImage", |
272 | + m_systemBusConnection, |
273 | + this); |
274 | + |
275 | + if (!systemServiceInterface.isValid()) |
276 | + return false; |
277 | + |
278 | + QDBusReply<QString> reply = systemServiceInterface.call("FactoryReset"); |
279 | + if (!reply.isValid()) |
280 | + return false; |
281 | + |
282 | return true; |
283 | } |
284 | |
285 | |
286 | === modified file 'plugins/reset/reset.h' |
287 | --- plugins/reset/reset.h 2013-09-06 13:12:46 +0000 |
288 | +++ plugins/reset/reset.h 2014-08-07 14:21:27 +0000 |
289 | @@ -21,7 +21,10 @@ |
290 | #ifndef RESET_H |
291 | #define RESET_H |
292 | |
293 | +#include "accountsservice.h" |
294 | + |
295 | #include <QDBusInterface> |
296 | +#include <QDBusServiceWatcher> |
297 | #include <QObject> |
298 | #include <QProcess> |
299 | #include <QUrl> |
300 | @@ -34,11 +37,11 @@ |
301 | explicit Reset(QObject *parent = 0); |
302 | ~Reset(); |
303 | Q_INVOKABLE bool resetLauncher(void); |
304 | + Q_INVOKABLE bool factoryReset(void); |
305 | |
306 | private: |
307 | + AccountsService m_accountsService; |
308 | QDBusConnection m_systemBusConnection; |
309 | - QString m_objectPath; |
310 | - QDBusInterface m_accountsserviceIface; |
311 | }; |
312 | |
313 | #endif // RESET_H |
314 | |
315 | === modified file 'plugins/reset/reset.settings' |
316 | --- plugins/reset/reset.settings 2014-04-09 13:38:34 +0000 |
317 | +++ plugins/reset/reset.settings 2014-08-07 14:21:27 +0000 |
318 | @@ -11,6 +11,5 @@ |
319 | "has-dynamic-keywords": false, |
320 | "has-dynamic-visibility": false, |
321 | "entry-component": "EntryComponent.qml", |
322 | - "page-component": "PageComponent.qml", |
323 | - "hide-by-default": true |
324 | + "page-component": "PageComponent.qml" |
325 | } |
326 | |
327 | === modified file 'tests/autopilot/ubuntu_system_settings/__init__.py' |
328 | --- tests/autopilot/ubuntu_system_settings/__init__.py 2014-07-22 18:29:46 +0000 |
329 | +++ tests/autopilot/ubuntu_system_settings/__init__.py 2014-08-07 14:21:27 +0000 |
330 | @@ -101,6 +101,10 @@ |
331 | item.swipe_into_view() |
332 | self.pointing_device.click_object(item) |
333 | |
334 | + @autopilot.logging.log_action(logger.debug) |
335 | + def go_to_reset_phone(self): |
336 | + return self._go_to_page('entryComponent-reset', 'resetPage') |
337 | + |
338 | def _go_to_page(self, item_object_name, page_object_name): |
339 | self.click_item(item_object_name) |
340 | page = self.wait_select_single(objectName=page_object_name) |
341 | @@ -108,7 +112,7 @@ |
342 | return page |
343 | |
344 | def scroll_to(self, obj): |
345 | - page = self.select_single(objectName='systemSettingsPage') |
346 | + page = self.system_settings_page |
347 | page_right = page.globalRect[0] + page.globalRect[2] |
348 | page_bottom = page.globalRect[1] + page.globalRect[3] |
349 | page_center_x = int(page_right / 2) |
350 | @@ -128,6 +132,10 @@ |
351 | self.pointing_device.click_object(obj) |
352 | |
353 | @property |
354 | + def system_settings_page(self): |
355 | + return self.select_single(objectName='systemSettingsPage') |
356 | + |
357 | + @property |
358 | def cellular_page(self): |
359 | """ Return 'Cellular' page """ |
360 | return self.select_single(objectName='cellularPage') |
361 | @@ -165,12 +173,25 @@ |
362 | @classmethod |
363 | def validate_dbus_object(cls, path, state): |
364 | name = introspection.get_classname_from_path(path) |
365 | - if name == b'ItemPage': |
366 | + if name == b'PageComponent': |
367 | if state['objectName'][1] == 'cellularPage': |
368 | return True |
369 | return False |
370 | |
371 | |
372 | +class TimeAndDatePage(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase): |
373 | + |
374 | + """Autopilot helper for the Sound page.""" |
375 | + |
376 | + @classmethod |
377 | + def validate_dbus_object(cls, path, state): |
378 | + name = introspection.get_classname_from_path(path) |
379 | + if name == b'PageComponent': |
380 | + if state['objectName'][1] == 'timeDatePage': |
381 | + return True |
382 | + return False |
383 | + |
384 | + |
385 | class SoundPage(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase): |
386 | |
387 | """Autopilot helper for the Sound page.""" |
388 | @@ -272,7 +293,101 @@ |
389 | @classmethod |
390 | def validate_dbus_object(cls, path, state): |
391 | name = introspection.get_classname_from_path(path) |
392 | - if name == b'ItemPage': |
393 | + if name == b'PageComponent': |
394 | if state['objectName'][1] == 'systemUpdatesPage': |
395 | return True |
396 | return False |
397 | + |
398 | + |
399 | +class ResetPage(ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase): |
400 | + |
401 | + """Autopilot helper for the Reset page.""" |
402 | + |
403 | + @classmethod |
404 | + def validate_dbus_object(cls, path, state): |
405 | + name = introspection.get_classname_from_path(path) |
406 | + if name == b'PageComponent': |
407 | + if state['objectName'][1] == 'resetPage': |
408 | + return True |
409 | + return False |
410 | + |
411 | + @autopilot.logging.log_action(logger.info) |
412 | + def reset_launcher(self): |
413 | + """Reset the launcher. |
414 | + |
415 | + :returns: The main system settings page object, that will be visible |
416 | + after the reset is complete. |
417 | + |
418 | + """ |
419 | + confirm_dialog = self._click_reset_launcher() |
420 | + confirm_dialog.confirm_reset() |
421 | + return self._wait_and_return_main_system_settins_page() |
422 | + |
423 | + @autopilot.logging.log_action(logger.debug) |
424 | + def _click_reset_launcher(self): |
425 | + button = self.select_single(objectName='resetLauncher') |
426 | + self.pointing_device.click_object(button) |
427 | + return self.get_root_instance().select_single( |
428 | + objectName='resetLauncherDialog') |
429 | + |
430 | + def _wait_and_return_main_system_settins_page(self): |
431 | + main_view = self.get_root_instance().select_single(MainWindow) |
432 | + main_view.system_settings_page.active.wait_for(True) |
433 | + return main_view.system_settings_page |
434 | + |
435 | + @autopilot.logging.log_action(logger.info) |
436 | + def erase_and_reset_everything(self): |
437 | + """Reset to factory settings. |
438 | + |
439 | + :returns: The main system settings page object, that will be visible |
440 | + after the reset is complete. |
441 | + |
442 | + """ |
443 | + confirm_dialog = self._click_factory_reset() |
444 | + confirm_dialog.confirm_reset() |
445 | + return self._wait_and_return_main_system_settins_page() |
446 | + |
447 | + @autopilot.logging.log_action(logger.debug) |
448 | + def _click_factory_reset(self): |
449 | + button = self.select_single(objectName='factoryReset') |
450 | + self.pointing_device.click_object(button) |
451 | + return self.get_root_instance().select_single( |
452 | + objectName='factoryResetDialog') |
453 | + |
454 | + |
455 | +class ResetLauncherConfirmationDialog( |
456 | + ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase): |
457 | + |
458 | + """Autopilot helper for the Reset Launcher Confirmation dialog.""" |
459 | + |
460 | + @classmethod |
461 | + def validate_dbus_object(cls, path, state): |
462 | + name = introspection.get_classname_from_path(path) |
463 | + if name == b'Dialog': |
464 | + if state['objectName'][1] == 'resetLauncherDialog': |
465 | + return True |
466 | + return False |
467 | + |
468 | + @autopilot.logging.log_action(logger.debug) |
469 | + def confirm_reset(self): |
470 | + button = self.select_single('Button', objectName='resetLauncherAction') |
471 | + self.pointing_device.click_object(button) |
472 | + |
473 | + |
474 | +class FactoryResetConfirmationDialog( |
475 | + ubuntuuitoolkit.UbuntuUIToolkitCustomProxyObjectBase): |
476 | + |
477 | + """Autopilot helper for the Reset Launcher Confirmation dialog.""" |
478 | + |
479 | + @classmethod |
480 | + def validate_dbus_object(cls, path, state): |
481 | + name = introspection.get_classname_from_path(path) |
482 | + if name == b'Dialog': |
483 | + if state['objectName'][1] == 'factoryResetDialog': |
484 | + return True |
485 | + return False |
486 | + |
487 | + @autopilot.logging.log_action(logger.debug) |
488 | + def confirm_reset(self): |
489 | + button = self.select_single('Button', objectName='factoryResetAction') |
490 | + self.pointing_device.click_object(button) |
491 | |
492 | === modified file 'tests/autopilot/ubuntu_system_settings/tests/__init__.py' |
493 | --- tests/autopilot/ubuntu_system_settings/tests/__init__.py 2014-08-01 17:10:10 +0000 |
494 | +++ tests/autopilot/ubuntu_system_settings/tests/__init__.py 2014-08-07 14:21:27 +0000 |
495 | @@ -27,13 +27,15 @@ |
496 | ACCOUNTS_IFACE = 'org.freedesktop.Accounts' |
497 | ACCOUNTS_USER_IFACE = 'org.freedesktop.Accounts.User' |
498 | ACCOUNTS_OBJ = '/org/freedesktop/Accounts' |
499 | - |
500 | +ACCOUNTS_SERVICE = 'com.canonical.unity.AccountsService' |
501 | ACCOUNTS_SOUND_IFACE = 'com.ubuntu.touch.AccountsService.Sound' |
502 | MODEM_IFACE = 'org.ofono.Modem' |
503 | CONNMAN_IFACE = 'org.ofono.ConnectionManager' |
504 | RDO_IFACE = 'org.ofono.RadioSettings' |
505 | SIM_IFACE = 'org.ofono.SimManager' |
506 | NETREG_IFACE = 'org.ofono.NetworkRegistration' |
507 | +SYSTEM_IFACE = 'com.canonical.SystemImage' |
508 | +SYSTEM_SERVICE_OBJ = '/Service' |
509 | |
510 | |
511 | class UbuntuSystemSettingsTestCase( |
512 | @@ -479,8 +481,7 @@ |
513 | 'IncomingMessageVibrate': dbus.Boolean(False, |
514 | variant_level=1), |
515 | 'IncomingMessageVibrateSilentMode': dbus.Boolean(False, |
516 | - variant_level=1) |
517 | - } |
518 | + variant_level=1)} |
519 | |
520 | # start dbus system bus |
521 | self.mock_server = self.spawn_server(ACCOUNTS_IFACE, ACCOUNTS_OBJ, |
522 | @@ -555,3 +556,63 @@ |
523 | self.mock_server.terminate() |
524 | self.mock_server.wait() |
525 | super(SoundBaseTestCase, self).tearDown() |
526 | + |
527 | + |
528 | +class ResetBaseTestCase(UbuntuSystemSettingsTestCase, |
529 | + dbusmock.DBusTestCase): |
530 | + """ Base class for reset settings tests""" |
531 | + |
532 | + def mock_for_launcher_reset(self): |
533 | + user_obj = '/user/foo' |
534 | + # start dbus system bus |
535 | + self.mock_server = self.spawn_server(ACCOUNTS_IFACE, ACCOUNTS_OBJ, |
536 | + ACCOUNTS_IFACE, system_bus=True, |
537 | + stdout=subprocess.PIPE) |
538 | + |
539 | + # spawn_server does not wait properly |
540 | + # Reported as bug here: http://pad.lv/1350833 |
541 | + sleep(2) |
542 | + self.acc_proxy = dbus.Interface(self.dbus_con.get_object( |
543 | + ACCOUNTS_IFACE, ACCOUNTS_OBJ), dbusmock.MOCK_IFACE) |
544 | + |
545 | + self.acc_proxy.AddMethod(ACCOUNTS_IFACE, 'FindUserById', 'x', 'o', |
546 | + 'ret = "%s"' % user_obj) |
547 | + |
548 | + self.acc_proxy.AddObject( |
549 | + user_obj, ACCOUNTS_USER_IFACE, {}, []) |
550 | + |
551 | + self.user_mock = dbus.Interface(self.dbus_con.get_object( |
552 | + ACCOUNTS_IFACE, user_obj), |
553 | + dbusmock.MOCK_IFACE) |
554 | + |
555 | + self.user_mock.AddMethod( |
556 | + 'org.freedesktop.DBus.Properties', 'Set', 'ssaa{sv}', '', '') |
557 | + |
558 | + def mock_for_factory_reset(self): |
559 | + self.mock_server = self.spawn_server(SYSTEM_IFACE, SYSTEM_SERVICE_OBJ, |
560 | + SYSTEM_IFACE, system_bus=True, |
561 | + stdout=subprocess.PIPE) |
562 | + # spawn_server does not wait properly |
563 | + # Reported as bug here: http://pad.lv/1350833 |
564 | + sleep(2) |
565 | + self.sys_mock = dbus.Interface(self.dbus_con.get_object( |
566 | + SYSTEM_IFACE, SYSTEM_SERVICE_OBJ), dbusmock.MOCK_IFACE) |
567 | + |
568 | + self.sys_mock.AddMethod(SYSTEM_IFACE, 'FactoryReset', '', '', '') |
569 | + |
570 | + @classmethod |
571 | + def setUpClass(klass): |
572 | + klass.start_system_bus() |
573 | + klass.dbus_con = klass.get_dbus(True) |
574 | + |
575 | + def setUp(self): |
576 | + self.mock_for_launcher_reset() |
577 | + self.mock_for_factory_reset() |
578 | + |
579 | + super(ResetBaseTestCase, self).setUp() |
580 | + self.reset_page = self.system_settings.main_view.go_to_reset_phone() |
581 | + |
582 | + def tearDown(self): |
583 | + self.mock_server.terminate() |
584 | + self.mock_server.wait() |
585 | + super(ResetBaseTestCase, self).tearDown() |
586 | |
587 | === added file 'tests/autopilot/ubuntu_system_settings/tests/test_reset.py' |
588 | --- tests/autopilot/ubuntu_system_settings/tests/test_reset.py 1970-01-01 00:00:00 +0000 |
589 | +++ tests/autopilot/ubuntu_system_settings/tests/test_reset.py 2014-08-07 14:21:27 +0000 |
590 | @@ -0,0 +1,50 @@ |
591 | +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- |
592 | +# Copyright 2014 Canonical |
593 | +# |
594 | +# This program is free software: you can redistribute it and/or modify it |
595 | +# under the terms of the GNU General Public License version 3, as published |
596 | +# by the Free Software Foundation. |
597 | + |
598 | +from __future__ import absolute_import |
599 | + |
600 | +from time import sleep |
601 | + |
602 | +from autopilot.matchers import Eventually |
603 | +from gi.repository import Gio |
604 | +from testtools.matchers import Contains, Equals |
605 | + |
606 | +from ubuntu_system_settings.tests import ResetBaseTestCase |
607 | +from ubuntu_system_settings.utils.i18n import ugettext as _ |
608 | + |
609 | + |
610 | +class ResetTestCase(ResetBaseTestCase): |
611 | + """Tests for Reset Page""" |
612 | + |
613 | + def set_unity_launcher_favorites(self, gsettings, favorites): |
614 | + gsettings.set_value('favorites', favorites) |
615 | + # wait for gsettings |
616 | + sleep(1) |
617 | + |
618 | + def test_reset_page_title_is_correct(self): |
619 | + """Checks whether Reset page is available""" |
620 | + self.assertThat( |
621 | + self.reset_page.title, |
622 | + Equals(_('Reset phone'))) |
623 | + |
624 | + def test_reset_launcher(self): |
625 | + gsettings = Gio.Settings.new('com.canonical.Unity.Launcher') |
626 | + favorites = gsettings.get_value('favorites') |
627 | + self.addCleanup( |
628 | + self.set_unity_launcher_favorites, gsettings, favorites) |
629 | + |
630 | + self.reset_page.reset_launcher() |
631 | + |
632 | + self.assertThat( |
633 | + lambda: str(self.user_mock.GetCalls()), |
634 | + Eventually(Contains('com.canonical.unity.AccountsService'))) |
635 | + |
636 | + def test_factory_reset(self): |
637 | + self.reset_page.erase_and_reset_everything() |
638 | + self.assertThat( |
639 | + lambda: str(self.sys_mock.GetCalls()), |
640 | + Eventually(Contains('FactoryReset'))) |
You should make sure to backup launcher icons before running tests and restore them at the end.