Merge lp:~nataliabidart/ubuntu/natty/ubuntuone-control-panel/ubuntuone-control-panel-0.9.5 into lp:ubuntu/natty/ubuntuone-control-panel
- Natty (11.04)
- ubuntuone-control-panel-0.9.5
- Merge into natty
Status: | Merged |
---|---|
Merge reported by: | Sebastien Bacher |
Merged at revision: | not available |
Proposed branch: | lp:~nataliabidart/ubuntu/natty/ubuntuone-control-panel/ubuntuone-control-panel-0.9.5 |
Merge into: | lp:ubuntu/natty/ubuntuone-control-panel |
Diff against target: |
2658 lines (+1102/-207) 26 files modified
PKG-INFO (+1/-1) bin/ubuntuone-control-panel-gtk (+8/-20) debian/changelog (+42/-0) po/POTFILES.in (+1/-0) run-tests (+5/-15) setup.py (+1/-1) ubuntuone-control-panel-gtk.desktop.in (+0/-6) ubuntuone/controlpanel/backend.py (+154/-49) ubuntuone/controlpanel/dbus_client.py (+0/-1) ubuntuone/controlpanel/dbus_service.py (+55/-34) ubuntuone/controlpanel/gtk/__init__.py (+4/-2) ubuntuone/controlpanel/gtk/gui.py (+184/-35) ubuntuone/controlpanel/gtk/package_manager.py (+3/-2) ubuntuone/controlpanel/gtk/tests/__init__.py (+13/-1) ubuntuone/controlpanel/gtk/tests/test_gui.py (+76/-1) ubuntuone/controlpanel/gtk/tests/test_gui_basic.py (+129/-5) ubuntuone/controlpanel/gtk/tests/test_package_manager.py (+2/-2) ubuntuone/controlpanel/integrationtests/__init__.py (+1/-2) ubuntuone/controlpanel/integrationtests/test_dbus_service.py (+67/-3) ubuntuone/controlpanel/integrationtests/test_gui_service.py (+98/-0) ubuntuone/controlpanel/integrationtests/test_webclient.py (+11/-1) ubuntuone/controlpanel/replication_client.py (+1/-1) ubuntuone/controlpanel/tests/__init__.py (+24/-12) ubuntuone/controlpanel/tests/test_backend.py (+205/-8) ubuntuone/controlpanel/utils.py (+1/-1) ubuntuone/controlpanel/webclient.py (+16/-4) |
To merge this branch: | bzr merge lp:~nataliabidart/ubuntu/natty/ubuntuone-control-panel/ubuntuone-control-panel-0.9.5 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Sponsors | Pending | ||
Review via email: mp+56995@code.launchpad.net |
Commit message
Description of the change
* New upstream release:
[ <email address hidden> ]
- Now that we set the launcher urgency from ubuntuone-client, the control
panel needs to remove it when its window receives focus (LP: #747677).
- changed default value for switch_to to empty string, and now don't call
switch_to method when the value is empty string (or anything else falsy)
(LP: #752943).
- Added proper defaults to the command line arguments (LP: #746489).
- Fixed issue where closing the panel resulted in a runtime error
(LP: #745987).
- This adds a method to the dbus service that allows switching between
panels, and/or drawing attention to the control panel (LP: #742008).
- Removed the shortcut group that causes two Ubuntu One entries to appear
in the messaging menu when syncdaemon is not running (LP: #721525).
[ Natalia B. Bidart <email address hidden> ]
- If servers reply with a 401, clear credentials and ask user to
authenticate (LP: #726612).
- Moving style_check down so the exit code from u1trial is not hidden by
&& operator.
- Unify disable/enable file sync functionality among Services tab and
global file sync status (LP: #729301).
- Cloud Folders tab is now disabled when the file sync service is
(LP: #747482).
- Improving legend for plugin installation to ease translations
(LP: #746374).
- Added volumes.ui to the translation list (LP: #746370).
- Small improvement to show something else besides the generic "Value can
not be retrieved." error (LP: #722485).
- Made the backend robust against possible None values (or any non
basestring instance) sent from the API server (LP: #745790).
- Decoupled device list retrieved from the web from the local settings
retrieved from syncdaemon (LP: #720704).
- Stop the control panel backend once the UI is done
(LP: #704434).
- After initial computer adding, syncdameon is asked to connect
(LP: #715873).
- 21. By Natalia Bidart
-
Fixing editor's email address.
Preview Diff
1 | === modified file 'PKG-INFO' | |||
2 | --- PKG-INFO 2011-03-23 20:33:42 +0000 | |||
3 | +++ PKG-INFO 2011-04-08 19:43:13 +0000 | |||
4 | @@ -1,6 +1,6 @@ | |||
5 | 1 | Metadata-Version: 1.1 | 1 | Metadata-Version: 1.1 |
6 | 2 | Name: ubuntuone-control-panel | 2 | Name: ubuntuone-control-panel |
8 | 3 | Version: 0.9.4 | 3 | Version: 0.9.5 |
9 | 4 | Summary: Ubuntu One Control Panel | 4 | Summary: Ubuntu One Control Panel |
10 | 5 | Home-page: https://launchpad.net/ubuntuone-control-panel | 5 | Home-page: https://launchpad.net/ubuntuone-control-panel |
11 | 6 | Author: Natalia Bidart | 6 | Author: Natalia Bidart |
12 | 7 | 7 | ||
13 | === modified file 'bin/ubuntuone-control-panel-gtk' | |||
14 | --- bin/ubuntuone-control-panel-gtk 2011-03-23 16:06:41 +0000 | |||
15 | +++ bin/ubuntuone-control-panel-gtk 2011-04-08 19:43:13 +0000 | |||
16 | @@ -2,6 +2,7 @@ | |||
17 | 2 | # -*- coding: utf-8 -*- | 2 | # -*- coding: utf-8 -*- |
18 | 3 | 3 | ||
19 | 4 | # Authors: Natalia B Bidart <natalia.bidart@canonical.com> | 4 | # Authors: Natalia B Bidart <natalia.bidart@canonical.com> |
20 | 5 | # Eric Casteleijn <eric.casteleijn@canonical.com> | ||
21 | 5 | # | 6 | # |
22 | 6 | # Copyright 2010 Canonical Ltd. | 7 | # Copyright 2010 Canonical Ltd. |
23 | 7 | # | 8 | # |
24 | @@ -20,20 +21,16 @@ | |||
25 | 20 | 21 | ||
26 | 21 | # Invalid name "ubuntuone-control-panel-gtk", pylint: disable=C0103 | 22 | # Invalid name "ubuntuone-control-panel-gtk", pylint: disable=C0103 |
27 | 22 | 23 | ||
28 | 24 | import gettext | ||
29 | 23 | import sys | 25 | import sys |
30 | 24 | 26 | ||
31 | 25 | import dbus.mainloop.glib | ||
32 | 26 | import gettext | ||
33 | 27 | |||
34 | 28 | from optparse import OptionParser | 27 | from optparse import OptionParser |
35 | 29 | 28 | ||
37 | 30 | from ubuntuone.controlpanel.gtk import DBUS_BUS_NAME, TRANSLATION_DOMAIN | 29 | from ubuntuone.controlpanel.gtk import TRANSLATION_DOMAIN |
38 | 31 | 30 | ||
39 | 32 | dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) | ||
40 | 33 | gettext.textdomain(TRANSLATION_DOMAIN) | 31 | gettext.textdomain(TRANSLATION_DOMAIN) |
41 | 34 | |||
42 | 35 | # import the GUI after the translation domain has been set | 32 | # import the GUI after the translation domain has been set |
44 | 36 | from ubuntuone.controlpanel.gtk.gui import ControlPanelWindow | 33 | from ubuntuone.controlpanel.gtk.gui import main |
45 | 37 | 34 | ||
46 | 38 | 35 | ||
47 | 39 | def parser_options(): | 36 | def parser_options(): |
48 | @@ -41,26 +38,17 @@ | |||
49 | 41 | usage = "Usage: %prog [option]" | 38 | usage = "Usage: %prog [option]" |
50 | 42 | result = OptionParser(usage=usage) | 39 | result = OptionParser(usage=usage) |
51 | 43 | result.add_option("", "--switch-to", dest="switch_to", type="string", | 40 | result.add_option("", "--switch-to", dest="switch_to", type="string", |
53 | 44 | metavar="PANEL_NAME", | 41 | metavar="PANEL_NAME", default="", |
54 | 45 | help="Start the Ubuntu One Control Panel (GTK) in the " | 42 | help="Start the Ubuntu One Control Panel (GTK) in the " |
55 | 46 | "PANEL_NAME tab. Possible values are: " | 43 | "PANEL_NAME tab. Possible values are: " |
56 | 47 | "dashboard, volumes, devices, applications") | 44 | "dashboard, volumes, devices, applications") |
57 | 48 | result.add_option("-a", "--alert", dest="alert", action="store_true", | 45 | result.add_option("-a", "--alert", dest="alert", action="store_true", |
60 | 49 | help="Start the Ubuntu One Control Panel (GTK) alerting " | 46 | default=False, help="Start the Ubuntu One Control Panel " |
61 | 50 | "the user to its presence.") | 47 | "(GTK) alerting the user to its presence.") |
62 | 51 | return result | 48 | return result |
63 | 52 | 49 | ||
64 | 53 | 50 | ||
65 | 54 | if __name__ == "__main__": | 51 | if __name__ == "__main__": |
66 | 55 | bus = dbus.SessionBus() | ||
67 | 56 | name = bus.request_name(DBUS_BUS_NAME, | ||
68 | 57 | dbus.bus.NAME_FLAG_DO_NOT_QUEUE) | ||
69 | 58 | if name == dbus.bus.REQUEST_NAME_REPLY_EXISTS: | ||
70 | 59 | sys.exit(0) | ||
71 | 60 | |||
72 | 61 | bus_name = dbus.service.BusName(DBUS_BUS_NAME, bus=dbus.SessionBus()) | ||
73 | 62 | parser = parser_options() | 52 | parser = parser_options() |
74 | 63 | (options, args) = parser.parse_args(sys.argv) | 53 | (options, args) = parser.parse_args(sys.argv) |
78 | 64 | gui = ControlPanelWindow( | 54 | main(switch_to=options.switch_to, alert=options.alert) |
76 | 65 | switch_to=options.switch_to, alert=options.alert) | ||
77 | 66 | gui.main() | ||
79 | 67 | 55 | ||
80 | === modified file 'debian/changelog' | |||
81 | --- debian/changelog 2011-03-23 20:44:08 +0000 | |||
82 | +++ debian/changelog 2011-04-08 19:43:13 +0000 | |||
83 | @@ -1,3 +1,45 @@ | |||
84 | 1 | ubuntuone-control-panel (0.9.5-0ubuntu1) UNRELEASED; urgency=low | ||
85 | 2 | |||
86 | 3 | * New upstream release: | ||
87 | 4 | |||
88 | 5 | [ eric.casteleijn@canonical.com ] | ||
89 | 6 | - Now that we set the launcher urgency from ubuntuone-client, the control | ||
90 | 7 | panel needs to remove it when its window receives focus (LP: #747677). | ||
91 | 8 | - changed default value for switch_to to empty string, and now don't call | ||
92 | 9 | switch_to method when the value is empty string (or anything else falsy) | ||
93 | 10 | (LP: #752943). | ||
94 | 11 | - Added proper defaults to the command line arguments (LP: #746489). | ||
95 | 12 | - Fixed issue where closing the panel resulted in a runtime error | ||
96 | 13 | (LP: #745987). | ||
97 | 14 | - This adds a method to the dbus service that allows switching between | ||
98 | 15 | panels, and/or drawing attention to the control panel (LP: #742008). | ||
99 | 16 | - Removed the shortcut group that causes two Ubuntu One entries to appear | ||
100 | 17 | in the messaging menu when syncdaemon is not running (LP: #721525). | ||
101 | 18 | [ Natalia B. Bidart <natalia.bidart@canonical.com> ] | ||
102 | 19 | - If servers reply with a 401, clear credentials and ask user to | ||
103 | 20 | authenticate (LP: #726612). | ||
104 | 21 | - Moving style_check down so the exit code from u1trial is not hidden by | ||
105 | 22 | && operator. | ||
106 | 23 | - Unify disable/enable file sync functionality among Services tab and | ||
107 | 24 | global file sync status (LP: #729301). | ||
108 | 25 | - Cloud Folders tab is now disabled when the file sync service is | ||
109 | 26 | (LP: #747482). | ||
110 | 27 | - Improving legend for plugin installation to ease translations | ||
111 | 28 | (LP: #746374). | ||
112 | 29 | - Added volumes.ui to the translation list (LP: #746370). | ||
113 | 30 | - Small improvement to show something else besides the generic "Value can | ||
114 | 31 | not be retrieved." error (LP: #722485). | ||
115 | 32 | - Made the backend robust against possible None values (or any non | ||
116 | 33 | basestring instance) sent from the API server (LP: #745790). | ||
117 | 34 | - Decoupled device list retrieved from the web from the local settings | ||
118 | 35 | retrieved from syncdaemon (LP: #720704). | ||
119 | 36 | - Stop the control panel backend once the UI is done | ||
120 | 37 | (LP: #704434). | ||
121 | 38 | - After initial computer adding, syncdameon is asked to connect | ||
122 | 39 | (LP: #715873). | ||
123 | 40 | |||
124 | 41 | -- Natalia Bidart (nessita) <nataliabidart@gmail.com> Fri, 08 Apr 2011 15:53:02 -0300 | ||
125 | 42 | |||
126 | 1 | ubuntuone-control-panel (0.9.4-0ubuntu1) natty; urgency=low | 43 | ubuntuone-control-panel (0.9.4-0ubuntu1) natty; urgency=low |
127 | 2 | 44 | ||
128 | 3 | * New upstream release: | 45 | * New upstream release: |
129 | 4 | 46 | ||
130 | === modified file 'po/POTFILES.in' | |||
131 | --- po/POTFILES.in 2011-01-07 20:07:39 +0000 | |||
132 | +++ po/POTFILES.in 2011-04-08 19:43:13 +0000 | |||
133 | @@ -7,3 +7,4 @@ | |||
134 | 7 | [type: gettext/glade] data/management.ui | 7 | [type: gettext/glade] data/management.ui |
135 | 8 | [type: gettext/glade] data/overview.ui | 8 | [type: gettext/glade] data/overview.ui |
136 | 9 | [type: gettext/glade] data/services.ui | 9 | [type: gettext/glade] data/services.ui |
137 | 10 | [type: gettext/glade] data/volumes.ui | ||
138 | 10 | 11 | ||
139 | === modified file 'run-tests' | |||
140 | --- run-tests 2010-12-06 12:27:11 +0000 | |||
141 | +++ run-tests 2011-04-08 19:43:13 +0000 | |||
142 | @@ -19,19 +19,8 @@ | |||
143 | 19 | set -e | 19 | set -e |
144 | 20 | 20 | ||
145 | 21 | if [ $# -ne 0 ]; then | 21 | if [ $# -ne 0 ]; then |
159 | 22 | # an extra argument was given | 22 | # run specific module given by the caller |
160 | 23 | if [ $1 == "--integration" ]; then | 23 | MODULE="$@" |
148 | 24 | # run only integration tests | ||
149 | 25 | MODULE="ubuntuone/controlpanel/integrationtests" | ||
150 | 26 | else | ||
151 | 27 | if [ $1 == "--unittests" ]; then | ||
152 | 28 | # run only non-integration tests (unittests) | ||
153 | 29 | MODULE="ubuntuone/controlpanel/tests" | ||
154 | 30 | else | ||
155 | 31 | # run specific module given by the caller | ||
156 | 32 | MODULE="$@" | ||
157 | 33 | fi | ||
158 | 34 | fi | ||
161 | 35 | else | 24 | else |
162 | 36 | # run all tests, useful for tarmac and reviews | 25 | # run all tests, useful for tarmac and reviews |
163 | 37 | MODULE="ubuntuone/controlpanel" | 26 | MODULE="ubuntuone/controlpanel" |
164 | @@ -40,12 +29,13 @@ | |||
165 | 40 | style_check() { | 29 | style_check() { |
166 | 41 | pylint ubuntuone/ | 30 | pylint ubuntuone/ |
167 | 42 | if [ -x `which pep8` ]; then | 31 | if [ -x `which pep8` ]; then |
169 | 43 | pep8 --repeat ubuntuone/ | 32 | pep8 --repeat bin/ $MODULE |
170 | 44 | else | 33 | else |
171 | 45 | echo "Please install the 'pep8' package." | 34 | echo "Please install the 'pep8' package." |
172 | 46 | fi | 35 | fi |
173 | 47 | } | 36 | } |
174 | 48 | 37 | ||
175 | 49 | echo "Running test suite for ""$MODULE" | 38 | echo "Running test suite for ""$MODULE" |
177 | 50 | `which xvfb-run` u1trial "$MODULE" && style_check | 39 | `which xvfb-run` u1trial "$MODULE" |
178 | 40 | style_check | ||
179 | 51 | rm -rf _trial_temp | 41 | rm -rf _trial_temp |
180 | 52 | 42 | ||
181 | === modified file 'setup.py' | |||
182 | --- setup.py 2011-03-23 20:33:42 +0000 | |||
183 | +++ setup.py 2011-04-08 19:43:13 +0000 | |||
184 | @@ -79,7 +79,7 @@ | |||
185 | 79 | 79 | ||
186 | 80 | DistUtilsExtra.auto.setup( | 80 | DistUtilsExtra.auto.setup( |
187 | 81 | name='ubuntuone-control-panel', | 81 | name='ubuntuone-control-panel', |
189 | 82 | version='0.9.4', | 82 | version='0.9.5', |
190 | 83 | license='GPL v3', | 83 | license='GPL v3', |
191 | 84 | author='Natalia Bidart', | 84 | author='Natalia Bidart', |
192 | 85 | author_email='natalia.bidart@canonical.com', | 85 | author_email='natalia.bidart@canonical.com', |
193 | 86 | 86 | ||
194 | === modified file 'ubuntuone-control-panel-gtk.desktop.in' | |||
195 | --- ubuntuone-control-panel-gtk.desktop.in 2011-02-23 13:57:42 +0000 | |||
196 | +++ ubuntuone-control-panel-gtk.desktop.in 2011-04-08 19:43:13 +0000 | |||
197 | @@ -7,10 +7,4 @@ | |||
198 | 7 | Type=Application | 7 | Type=Application |
199 | 8 | Categories=GNOME;GTK;Settings; | 8 | Categories=GNOME;GTK;Settings; |
200 | 9 | StartupNotify=true | 9 | StartupNotify=true |
201 | 10 | X-Ayatana-Desktop-Shortcuts=U1 | ||
202 | 11 | X-Ayatana-Appmenu-Show-Stubs=False | 10 | X-Ayatana-Appmenu-Show-Stubs=False |
203 | 12 | |||
204 | 13 | [U1 Shortcut Group] | ||
205 | 14 | Name=Ubuntu One | ||
206 | 15 | Exec=ubuntuone-control-panel-gtk | ||
207 | 16 | OnlyShowIn=Messaging Menu | ||
208 | 17 | 11 | ||
209 | === modified file 'ubuntuone/controlpanel/backend.py' | |||
210 | --- ubuntuone/controlpanel/backend.py 2011-03-10 02:59:44 +0000 | |||
211 | +++ ubuntuone/controlpanel/backend.py 2011-04-08 19:43:13 +0000 | |||
212 | @@ -20,14 +20,17 @@ | |||
213 | 20 | """A backend for the Ubuntu One Control Panel.""" | 20 | """A backend for the Ubuntu One Control Panel.""" |
214 | 21 | 21 | ||
215 | 22 | from collections import defaultdict | 22 | from collections import defaultdict |
216 | 23 | from functools import wraps | ||
217 | 23 | 24 | ||
218 | 24 | from twisted.internet.defer import inlineCallbacks, returnValue | 25 | from twisted.internet.defer import inlineCallbacks, returnValue |
219 | 25 | 26 | ||
220 | 26 | from ubuntuone.controlpanel import dbus_client | 27 | from ubuntuone.controlpanel import dbus_client |
221 | 27 | from ubuntuone.controlpanel import replication_client | 28 | from ubuntuone.controlpanel import replication_client |
222 | 28 | from ubuntuone.controlpanel.logger import setup_logging, log_call | 29 | from ubuntuone.controlpanel.logger import setup_logging, log_call |
225 | 29 | from ubuntuone.controlpanel.webclient import WebClient | 30 | # pylint: disable=W0611 |
226 | 30 | 31 | from ubuntuone.controlpanel.webclient import (UnauthorizedError, | |
227 | 32 | WebClient, WebClientError) | ||
228 | 33 | # pylint: enable=W0611 | ||
229 | 31 | 34 | ||
230 | 32 | logger = setup_logging('backend') | 35 | logger = setup_logging('backend') |
231 | 33 | 36 | ||
232 | @@ -61,6 +64,35 @@ | |||
233 | 61 | return 'True' if value else '' | 64 | return 'True' if value else '' |
234 | 62 | 65 | ||
235 | 63 | 66 | ||
236 | 67 | def filter_field(info, field): | ||
237 | 68 | """Return a copy of 'info' where each item has 'field' hidden.""" | ||
238 | 69 | result = [] | ||
239 | 70 | for item in info: | ||
240 | 71 | item = item.copy() | ||
241 | 72 | item[field] = '<hidden>' | ||
242 | 73 | result.append(item) | ||
243 | 74 | return result | ||
244 | 75 | |||
245 | 76 | |||
246 | 77 | def process_unauthorized(f): | ||
247 | 78 | """Decorator to catch UnauthorizedError from the webclient and act upon.""" | ||
248 | 79 | |||
249 | 80 | @inlineCallbacks | ||
250 | 81 | @wraps(f) | ||
251 | 82 | def inner(*args, **kwargs): | ||
252 | 83 | """Handle UnauthorizedError and clear credentials.""" | ||
253 | 84 | try: | ||
254 | 85 | result = yield f(*args, **kwargs) | ||
255 | 86 | except UnauthorizedError, e: | ||
256 | 87 | logger.exception('process_unauthorized (clearing credentials):') | ||
257 | 88 | yield dbus_client.clear_credentials() | ||
258 | 89 | raise e | ||
259 | 90 | |||
260 | 91 | returnValue(result) | ||
261 | 92 | |||
262 | 93 | return inner | ||
263 | 94 | |||
264 | 95 | |||
265 | 64 | class ControlBackend(object): | 96 | class ControlBackend(object): |
266 | 65 | """The control panel backend.""" | 97 | """The control panel backend.""" |
267 | 66 | 98 | ||
268 | @@ -68,14 +100,17 @@ | |||
269 | 68 | FOLDER_TYPE = u'UDF' | 100 | FOLDER_TYPE = u'UDF' |
270 | 69 | SHARE_TYPE = u'SHARE' | 101 | SHARE_TYPE = u'SHARE' |
271 | 70 | NAME_NOT_SET = u'ENAMENOTSET' | 102 | NAME_NOT_SET = u'ENAMENOTSET' |
272 | 103 | STATUS_DISABLED = {MSG_KEY: '', STATUS_KEY: FILE_SYNC_DISABLED} | ||
273 | 71 | 104 | ||
275 | 72 | def __init__(self): | 105 | def __init__(self, shutdown_func=None): |
276 | 73 | """Initialize the webclient.""" | 106 | """Initialize the webclient.""" |
277 | 107 | self.shutdown_func = shutdown_func | ||
278 | 74 | self.wc = WebClient(dbus_client.get_credentials) | 108 | self.wc = WebClient(dbus_client.get_credentials) |
279 | 75 | self._status_changed_handler = None | 109 | self._status_changed_handler = None |
280 | 76 | self.status_changed_handler = lambda *a: None | 110 | self.status_changed_handler = lambda *a: None |
281 | 77 | 111 | ||
282 | 78 | self._volumes = {} # cache last known volume info | 112 | self._volumes = {} # cache last known volume info |
283 | 113 | self.file_sync_disabled = False | ||
284 | 79 | 114 | ||
285 | 80 | def _process_file_sync_status(self, status): | 115 | def _process_file_sync_status(self, status): |
286 | 81 | """Process raw file sync status into custom format. | 116 | """Process raw file sync status into custom format. |
287 | @@ -89,7 +124,8 @@ | |||
288 | 89 | 124 | ||
289 | 90 | """ | 125 | """ |
290 | 91 | if not status: | 126 | if not status: |
292 | 92 | return {MSG_KEY: '', STATUS_KEY: FILE_SYNC_DISABLED} | 127 | self.file_sync_disabled = True |
293 | 128 | return self.STATUS_DISABLED | ||
294 | 93 | 129 | ||
295 | 94 | msg = '%s (%s)' % (status['description'], status['name']) | 130 | msg = '%s (%s)' % (status['description'], status['name']) |
296 | 95 | result = {MSG_KEY: msg} | 131 | result = {MSG_KEY: msg} |
297 | @@ -113,6 +149,7 @@ | |||
298 | 113 | elif is_disconnected: | 149 | elif is_disconnected: |
299 | 114 | result[STATUS_KEY] = FILE_SYNC_DISCONNECTED | 150 | result[STATUS_KEY] = FILE_SYNC_DISCONNECTED |
300 | 115 | elif is_starting: | 151 | elif is_starting: |
301 | 152 | self.file_sync_disabled = False | ||
302 | 116 | result[STATUS_KEY] = FILE_SYNC_STARTING | 153 | result[STATUS_KEY] = FILE_SYNC_STARTING |
303 | 117 | elif is_stopped: | 154 | elif is_stopped: |
304 | 118 | result[STATUS_KEY] = FILE_SYNC_STOPPED | 155 | result[STATUS_KEY] = FILE_SYNC_STOPPED |
305 | @@ -120,7 +157,10 @@ | |||
306 | 120 | logger.warning('file_sync_status: unknown (got %r)', status) | 157 | logger.warning('file_sync_status: unknown (got %r)', status) |
307 | 121 | result[STATUS_KEY] = FILE_SYNC_UNKNOWN | 158 | result[STATUS_KEY] = FILE_SYNC_UNKNOWN |
308 | 122 | 159 | ||
310 | 123 | return result | 160 | if self.file_sync_disabled: |
311 | 161 | return self.STATUS_DISABLED | ||
312 | 162 | else: | ||
313 | 163 | return result | ||
314 | 124 | 164 | ||
315 | 125 | def _set_status_changed_handler(self, handler): | 165 | def _set_status_changed_handler(self, handler): |
316 | 126 | """Set 'handler' to be called when file sync status changes.""" | 166 | """Set 'handler' to be called when file sync status changes.""" |
317 | @@ -142,6 +182,56 @@ | |||
318 | 142 | _set_status_changed_handler) | 182 | _set_status_changed_handler) |
319 | 143 | 183 | ||
320 | 144 | @inlineCallbacks | 184 | @inlineCallbacks |
321 | 185 | def _process_device_web_info(self, devices, enabled, limit_bw, limits, | ||
322 | 186 | show_notifs): | ||
323 | 187 | """Return a lis of processed devices.""" | ||
324 | 188 | result = [] | ||
325 | 189 | for d in devices: | ||
326 | 190 | di = {} | ||
327 | 191 | di["type"] = d["kind"] | ||
328 | 192 | di["name"] = d["description"] | ||
329 | 193 | di["configurable"] = '' | ||
330 | 194 | if di["type"] == DEVICE_TYPE_COMPUTER: | ||
331 | 195 | di["device_id"] = di["type"] + d["token"] | ||
332 | 196 | if di["type"] == DEVICE_TYPE_PHONE: | ||
333 | 197 | di["device_id"] = di["type"] + str(d["id"]) | ||
334 | 198 | |||
335 | 199 | is_local = yield self.device_is_local(di["device_id"]) | ||
336 | 200 | di["is_local"] = bool_str(is_local) | ||
337 | 201 | # currently, only local devices are configurable. | ||
338 | 202 | # eventually, more devices will be configurable. | ||
339 | 203 | di["configurable"] = bool_str(is_local and enabled) | ||
340 | 204 | |||
341 | 205 | if bool(di["configurable"]): | ||
342 | 206 | di["limit_bandwidth"] = bool_str(limit_bw) | ||
343 | 207 | di["show_all_notifications"] = bool_str(show_notifs) | ||
344 | 208 | upload = limits["upload"] | ||
345 | 209 | download = limits["download"] | ||
346 | 210 | di[UPLOAD_KEY] = str(upload) | ||
347 | 211 | di[DOWNLOAD_KEY] = str(download) | ||
348 | 212 | |||
349 | 213 | # date_added is not in the webservice yet (LP: #673668) | ||
350 | 214 | # di["date_added"] = "" | ||
351 | 215 | |||
352 | 216 | # missing values (LP: #673668) | ||
353 | 217 | # di["available_services"] = "" | ||
354 | 218 | # di["enabled_services"] = "" | ||
355 | 219 | |||
356 | 220 | # make a sanity check | ||
357 | 221 | for key, val in di.iteritems(): | ||
358 | 222 | if not isinstance(val, basestring): | ||
359 | 223 | logger.warning('_process_device_web_info: (key %r), ' | ||
360 | 224 | 'val %r is not a basestring.', key, val) | ||
361 | 225 | di[key] = repr(val) | ||
362 | 226 | |||
363 | 227 | if is_local: # prepend the local device! | ||
364 | 228 | result.insert(0, di) | ||
365 | 229 | else: | ||
366 | 230 | result.append(di) | ||
367 | 231 | |||
368 | 232 | returnValue(result) | ||
369 | 233 | |||
370 | 234 | @inlineCallbacks | ||
371 | 145 | def get_token(self): | 235 | def get_token(self): |
372 | 146 | """Return the token from the credentials.""" | 236 | """Return the token from the credentials.""" |
373 | 147 | credentials = yield dbus_client.get_credentials() | 237 | credentials = yield dbus_client.get_credentials() |
374 | @@ -153,10 +243,10 @@ | |||
375 | 153 | dtype, did = self.type_n_id(device_id) | 243 | dtype, did = self.type_n_id(device_id) |
376 | 154 | local_token = yield self.get_token() | 244 | local_token = yield self.get_token() |
377 | 155 | is_local = (dtype == DEVICE_TYPE_COMPUTER and did == local_token) | 245 | is_local = (dtype == DEVICE_TYPE_COMPUTER and did == local_token) |
378 | 156 | logger.info('device_is_local: result %r, ', is_local) | ||
379 | 157 | returnValue(is_local) | 246 | returnValue(is_local) |
380 | 158 | 247 | ||
381 | 159 | @log_call(logger.debug) | 248 | @log_call(logger.debug) |
382 | 249 | @process_unauthorized | ||
383 | 160 | @inlineCallbacks | 250 | @inlineCallbacks |
384 | 161 | def account_info(self): | 251 | def account_info(self): |
385 | 162 | """Get the user account info.""" | 252 | """Get the user account info.""" |
386 | @@ -184,50 +274,55 @@ | |||
387 | 184 | returnValue(result) | 274 | returnValue(result) |
388 | 185 | 275 | ||
389 | 186 | @log_call(logger.debug) | 276 | @log_call(logger.debug) |
390 | 277 | @process_unauthorized | ||
391 | 187 | @inlineCallbacks | 278 | @inlineCallbacks |
392 | 188 | def devices_info(self): | 279 | def devices_info(self): |
393 | 189 | """Get the user devices info.""" | 280 | """Get the user devices info.""" |
419 | 190 | result = [] | 281 | result = limit_bw = limits = show_notifs = None |
420 | 191 | limit_bw = yield dbus_client.bandwidth_throttling_enabled() | 282 | enabled = yield dbus_client.files_sync_enabled() |
421 | 192 | show_all_notif = yield dbus_client.show_all_notifications_enabled() | 283 | if enabled: |
422 | 193 | limits = yield dbus_client.get_throttling_limits() | 284 | limit_bw = yield dbus_client.bandwidth_throttling_enabled() |
423 | 194 | 285 | show_notifs = yield dbus_client.show_all_notifications_enabled() | |
424 | 195 | devices = yield self.wc.call_api(DEVICES_API) | 286 | limits = yield dbus_client.get_throttling_limits() |
425 | 196 | for d in devices: | 287 | |
426 | 197 | di = {} | 288 | logger.debug('devices_info: file sync enabled? %s limit_bw %s, limits ' |
427 | 198 | di["type"] = d["kind"] | 289 | '%s, show_notifs %s', |
428 | 199 | di["name"] = d["description"] | 290 | enabled, limit_bw, limits, show_notifs) |
429 | 200 | di["configurable"] = '' | 291 | |
430 | 201 | if di["type"] == DEVICE_TYPE_COMPUTER: | 292 | try: |
431 | 202 | di["device_id"] = di["type"] + d["token"] | 293 | devices = yield self.wc.call_api(DEVICES_API) |
432 | 203 | if di["type"] == DEVICE_TYPE_PHONE: | 294 | except UnauthorizedError: |
433 | 204 | di["device_id"] = di["type"] + str(d["id"]) | 295 | raise |
434 | 205 | 296 | except WebClientError: | |
435 | 206 | is_local = yield self.device_is_local(di["device_id"]) | 297 | logger.exception('devices_info: web client failure:') |
436 | 207 | di["is_local"] = bool_str(is_local) | 298 | else: |
437 | 208 | # currently, only local devices are configurable. | 299 | result = yield self._process_device_web_info(devices, enabled, |
438 | 209 | # eventually, more the devices will be configurable. | 300 | limit_bw, limits, |
439 | 210 | di["configurable"] = bool_str(is_local) | 301 | show_notifs) |
440 | 211 | 302 | if result is None: | |
441 | 212 | if bool(di["configurable"]): | 303 | logger.info('devices_info: result is None after calling ' |
442 | 213 | di["limit_bandwidth"] = bool_str(limit_bw) | 304 | 'devices/ API, building the local device.') |
443 | 214 | di["show_all_notifications"] = bool_str(show_all_notif) | 305 | credentials = yield dbus_client.get_credentials() |
444 | 306 | local_device = {} | ||
445 | 307 | local_device["type"] = DEVICE_TYPE_COMPUTER | ||
446 | 308 | local_device["name"] = credentials['name'] | ||
447 | 309 | device_id = local_device["type"] + credentials["token"] | ||
448 | 310 | local_device["device_id"] = device_id | ||
449 | 311 | local_device["is_local"] = bool_str(True) | ||
450 | 312 | local_device["configurable"] = bool_str(enabled) | ||
451 | 313 | if bool(local_device["configurable"]): | ||
452 | 314 | local_device["limit_bandwidth"] = bool_str(limit_bw) | ||
453 | 315 | show_notifs = bool_str(show_notifs) | ||
454 | 316 | local_device["show_all_notifications"] = show_notifs | ||
455 | 215 | upload = limits["upload"] | 317 | upload = limits["upload"] |
456 | 216 | download = limits["download"] | 318 | download = limits["download"] |
471 | 217 | di[UPLOAD_KEY] = str(upload) | 319 | local_device[UPLOAD_KEY] = str(upload) |
472 | 218 | di[DOWNLOAD_KEY] = str(download) | 320 | local_device[DOWNLOAD_KEY] = str(download) |
473 | 219 | 321 | result = [local_device] | |
474 | 220 | # date_added is not in the webservice yet (LP: #673668) | 322 | else: |
475 | 221 | # di["date_added"] = "" | 323 | logger.info('devices_info: result is not None after calling ' |
476 | 222 | 324 | 'devices/ API: %r', | |
477 | 223 | # missing values (LP: #673668) | 325 | filter_field(result, field='device_id')) |
464 | 224 | # di["available_services"] = "" | ||
465 | 225 | # di["enabled_services"] = "" | ||
466 | 226 | |||
467 | 227 | if is_local: # prepend the local device! | ||
468 | 228 | result.insert(0, di) | ||
469 | 229 | else: | ||
470 | 230 | result.append(di) | ||
478 | 231 | 326 | ||
479 | 232 | returnValue(result) | 327 | returnValue(result) |
480 | 233 | 328 | ||
481 | @@ -239,7 +334,7 @@ | |||
482 | 239 | return DEVICE_TYPE_PHONE, device_id[5:] | 334 | return DEVICE_TYPE_PHONE, device_id[5:] |
483 | 240 | return "No device", device_id | 335 | return "No device", device_id |
484 | 241 | 336 | ||
486 | 242 | @log_call(logger.info) | 337 | @log_call(logger.info, with_args=False) |
487 | 243 | @inlineCallbacks | 338 | @inlineCallbacks |
488 | 244 | def change_device_settings(self, device_id, settings): | 339 | def change_device_settings(self, device_id, settings): |
489 | 245 | """Change the settings for the given device.""" | 340 | """Change the settings for the given device.""" |
490 | @@ -275,7 +370,8 @@ | |||
491 | 275 | # still pending: more work on the settings dict (LP: #673674) | 370 | # still pending: more work on the settings dict (LP: #673674) |
492 | 276 | returnValue(device_id) | 371 | returnValue(device_id) |
493 | 277 | 372 | ||
495 | 278 | @log_call(logger.warning) | 373 | @log_call(logger.warning, with_args=False) |
496 | 374 | @process_unauthorized | ||
497 | 279 | @inlineCallbacks | 375 | @inlineCallbacks |
498 | 280 | def remove_device(self, device_id): | 376 | def remove_device(self, device_id): |
499 | 281 | """Remove a device's tokens from the sso server.""" | 377 | """Remove a device's tokens from the sso server.""" |
500 | @@ -286,8 +382,8 @@ | |||
501 | 286 | yield self.wc.call_api(api) | 382 | yield self.wc.call_api(api) |
502 | 287 | 383 | ||
503 | 288 | if is_local: | 384 | if is_local: |
506 | 289 | logger.warning('remove_device: device is local, id %r, ' | 385 | logger.warning('remove_device: device is local! removing and ' |
507 | 290 | 'clearing credentials.', device_id) | 386 | 'clearing credentials.') |
508 | 291 | yield dbus_client.clear_credentials() | 387 | yield dbus_client.clear_credentials() |
509 | 292 | 388 | ||
510 | 293 | returnValue(device_id) | 389 | returnValue(device_id) |
511 | @@ -308,12 +404,14 @@ | |||
512 | 308 | def enable_files(self): | 404 | def enable_files(self): |
513 | 309 | """Enable the files service.""" | 405 | """Enable the files service.""" |
514 | 310 | yield dbus_client.set_files_sync_enabled(True) | 406 | yield dbus_client.set_files_sync_enabled(True) |
515 | 407 | self.file_sync_disabled = False | ||
516 | 311 | 408 | ||
517 | 312 | @log_call(logger.debug) | 409 | @log_call(logger.debug) |
518 | 313 | @inlineCallbacks | 410 | @inlineCallbacks |
519 | 314 | def disable_files(self): | 411 | def disable_files(self): |
520 | 315 | """Enable the files service.""" | 412 | """Enable the files service.""" |
521 | 316 | yield dbus_client.set_files_sync_enabled(False) | 413 | yield dbus_client.set_files_sync_enabled(False) |
522 | 414 | self.file_sync_disabled = True | ||
523 | 317 | 415 | ||
524 | 318 | @log_call(logger.debug) | 416 | @log_call(logger.debug) |
525 | 319 | @inlineCallbacks | 417 | @inlineCallbacks |
526 | @@ -478,3 +576,10 @@ | |||
527 | 478 | """Install the extension to sync bookmarks.""" | 576 | """Install the extension to sync bookmarks.""" |
528 | 479 | # still pending (LP: #673673) | 577 | # still pending (LP: #673673) |
529 | 480 | returnValue(None) | 578 | returnValue(None) |
530 | 579 | |||
531 | 580 | @log_call(logger.info) | ||
532 | 581 | def shutdown(self): | ||
533 | 582 | """Stop this service.""" | ||
534 | 583 | # do any other needed cleanup | ||
535 | 584 | if self.shutdown_func is not None: | ||
536 | 585 | self.shutdown_func() | ||
537 | 481 | 586 | ||
538 | === modified file 'ubuntuone/controlpanel/dbus_client.py' | |||
539 | --- ubuntuone/controlpanel/dbus_client.py 2011-02-23 13:57:42 +0000 | |||
540 | +++ ubuntuone/controlpanel/dbus_client.py 2011-04-08 19:43:13 +0000 | |||
541 | @@ -64,7 +64,6 @@ | |||
542 | 64 | 64 | ||
543 | 65 | def found_credentials(app_name, creds): | 65 | def found_credentials(app_name, creds): |
544 | 66 | """Credentials have been found.""" | 66 | """Credentials have been found.""" |
545 | 67 | logger.debug('credentials were found for app_name %r.', app_name) | ||
546 | 68 | if app_name == APP_NAME: | 67 | if app_name == APP_NAME: |
547 | 69 | logger.info('credentials were found! (%r).', APP_NAME) | 68 | logger.info('credentials were found! (%r).', APP_NAME) |
548 | 70 | d.callback(creds) | 69 | d.callback(creds) |
549 | 71 | 70 | ||
550 | === modified file 'ubuntuone/controlpanel/dbus_service.py' | |||
551 | --- ubuntuone/controlpanel/dbus_service.py 2011-01-25 19:08:59 +0000 | |||
552 | +++ ubuntuone/controlpanel/dbus_service.py 2011-04-08 19:43:13 +0000 | |||
553 | @@ -32,7 +32,8 @@ | |||
554 | 32 | from ubuntuone.controlpanel import (DBUS_BUS_NAME, DBUS_PREFERENCES_PATH, | 32 | from ubuntuone.controlpanel import (DBUS_BUS_NAME, DBUS_PREFERENCES_PATH, |
555 | 33 | DBUS_PREFERENCES_IFACE) | 33 | DBUS_PREFERENCES_IFACE) |
556 | 34 | from ubuntuone.controlpanel.backend import ( | 34 | from ubuntuone.controlpanel.backend import ( |
558 | 35 | ControlBackend, FILE_SYNC_DISABLED, FILE_SYNC_DISCONNECTED, | 35 | ControlBackend, filter_field, UnauthorizedError, |
559 | 36 | FILE_SYNC_DISABLED, FILE_SYNC_DISCONNECTED, | ||
560 | 36 | FILE_SYNC_ERROR, FILE_SYNC_IDLE, FILE_SYNC_STARTING, FILE_SYNC_STOPPED, | 37 | FILE_SYNC_ERROR, FILE_SYNC_IDLE, FILE_SYNC_STARTING, FILE_SYNC_STOPPED, |
561 | 37 | FILE_SYNC_SYNCING, | 38 | FILE_SYNC_SYNCING, |
562 | 38 | MSG_KEY, STATUS_KEY, | 39 | MSG_KEY, STATUS_KEY, |
563 | @@ -77,7 +78,7 @@ | |||
564 | 77 | return result | 78 | return result |
565 | 78 | 79 | ||
566 | 79 | 80 | ||
568 | 80 | def transform_failure(f): | 81 | def transform_failure(f, auth_error=None): |
569 | 81 | """Decorator to apply to DBus error signals. | 82 | """Decorator to apply to DBus error signals. |
570 | 82 | 83 | ||
571 | 83 | With this call, a Failure is transformed into a string-string dict. | 84 | With this call, a Failure is transformed into a string-string dict. |
572 | @@ -85,8 +86,11 @@ | |||
573 | 85 | """ | 86 | """ |
574 | 86 | def inner(error, _=None): | 87 | def inner(error, _=None): |
575 | 87 | """Do the Failure transformation.""" | 88 | """Do the Failure transformation.""" |
576 | 89 | logger.error('processing failure: %r', error.printTraceback()) | ||
577 | 88 | error_dict = error_handler(error) | 90 | error_dict = error_handler(error) |
579 | 89 | if _ is not None: | 91 | if auth_error is not None and error.check(UnauthorizedError): |
580 | 92 | result = auth_error(error_dict) | ||
581 | 93 | elif _ is not None: | ||
582 | 90 | result = f(_, error_dict) | 94 | result = f(_, error_dict) |
583 | 91 | else: | 95 | else: |
584 | 92 | result = f(error_dict) | 96 | result = f(error_dict) |
585 | @@ -115,19 +119,25 @@ | |||
586 | 115 | super(ControlPanelBackend, self).__init__(*args, **kwargs) | 119 | super(ControlPanelBackend, self).__init__(*args, **kwargs) |
587 | 116 | self.backend = backend | 120 | self.backend = backend |
588 | 117 | self.backend.status_changed_handler = self.process_status | 121 | self.backend.status_changed_handler = self.process_status |
589 | 122 | self.transform = lambda f: transform_failure(f, self.UnauthorizedError) | ||
590 | 118 | logger.debug('ControlPanelBackend: created with %r, %r.\n' | 123 | logger.debug('ControlPanelBackend: created with %r, %r.\n' |
591 | 119 | 'status_changed_handler is %r.', | 124 | 'status_changed_handler is %r.', |
592 | 120 | args, kwargs, self.process_status) | 125 | args, kwargs, self.process_status) |
593 | 121 | 126 | ||
594 | 122 | # pylint: disable=C0103 | 127 | # pylint: disable=C0103 |
595 | 123 | 128 | ||
596 | 129 | @log_call(logger.error) | ||
597 | 130 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}") | ||
598 | 131 | def UnauthorizedError(self, error): | ||
599 | 132 | """The credentials are not valid.""" | ||
600 | 133 | |||
601 | 124 | @log_call(logger.debug) | 134 | @log_call(logger.debug) |
602 | 125 | @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="") | 135 | @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="") |
603 | 126 | def account_info(self): | 136 | def account_info(self): |
604 | 127 | """Find out the account info for the current logged in user.""" | 137 | """Find out the account info for the current logged in user.""" |
605 | 128 | d = self.backend.account_info() | 138 | d = self.backend.account_info() |
606 | 129 | d.addCallback(self.AccountInfoReady) | 139 | d.addCallback(self.AccountInfoReady) |
608 | 130 | d.addErrback(transform_failure(self.AccountInfoError)) | 140 | d.addErrback(self.transform(self.AccountInfoError)) |
609 | 131 | 141 | ||
610 | 132 | @log_call(logger.debug) | 142 | @log_call(logger.debug) |
611 | 133 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}") | 143 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}") |
612 | @@ -147,12 +157,13 @@ | |||
613 | 147 | """Find out the devices info for the logged in user.""" | 157 | """Find out the devices info for the logged in user.""" |
614 | 148 | d = self.backend.devices_info() | 158 | d = self.backend.devices_info() |
615 | 149 | d.addCallback(self.DevicesInfoReady) | 159 | d.addCallback(self.DevicesInfoReady) |
617 | 150 | d.addErrback(transform_failure(self.DevicesInfoError)) | 160 | d.addErrback(self.transform(self.DevicesInfoError)) |
618 | 151 | 161 | ||
619 | 152 | @log_call(logger.debug) | ||
620 | 153 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="aa{ss}") | 162 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="aa{ss}") |
621 | 154 | def DevicesInfoReady(self, info): | 163 | def DevicesInfoReady(self, info): |
622 | 155 | """The info for the devices is available right now.""" | 164 | """The info for the devices is available right now.""" |
623 | 165 | logger.debug('DevicesInfoReady: args %r', | ||
624 | 166 | filter_field(info, field='device_id')) | ||
625 | 156 | 167 | ||
626 | 157 | @log_call(logger.error) | 168 | @log_call(logger.error) |
627 | 158 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}") | 169 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a{ss}") |
628 | @@ -161,41 +172,41 @@ | |||
629 | 161 | 172 | ||
630 | 162 | #--- | 173 | #--- |
631 | 163 | 174 | ||
633 | 164 | @log_call(logger.info) | 175 | @log_call(logger.info, with_args=False) |
634 | 165 | @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="sa{ss}") | 176 | @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="sa{ss}") |
635 | 166 | def change_device_settings(self, device_id, settings): | 177 | def change_device_settings(self, device_id, settings): |
636 | 167 | """Configure a given device.""" | 178 | """Configure a given device.""" |
637 | 168 | d = self.backend.change_device_settings(device_id, settings) | 179 | d = self.backend.change_device_settings(device_id, settings) |
638 | 169 | d.addCallback(self.DeviceSettingsChanged) | 180 | d.addCallback(self.DeviceSettingsChanged) |
640 | 170 | d.addErrback(transform_failure(self.DeviceSettingsChangeError), | 181 | d.addErrback(self.transform(self.DeviceSettingsChangeError), |
641 | 171 | device_id) | 182 | device_id) |
642 | 172 | 183 | ||
644 | 173 | @log_call(logger.info) | 184 | @log_call(logger.info, with_args=False) |
645 | 174 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s") | 185 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s") |
646 | 175 | def DeviceSettingsChanged(self, device_id): | 186 | def DeviceSettingsChanged(self, device_id): |
647 | 176 | """The settings for the device were changed.""" | 187 | """The settings for the device were changed.""" |
648 | 177 | 188 | ||
650 | 178 | @log_call(logger.error) | 189 | @log_call(logger.error, with_args=False) |
651 | 179 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="sa{ss}") | 190 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="sa{ss}") |
652 | 180 | def DeviceSettingsChangeError(self, device_id, error): | 191 | def DeviceSettingsChangeError(self, device_id, error): |
653 | 181 | """Problem changing settings for the device.""" | 192 | """Problem changing settings for the device.""" |
654 | 182 | 193 | ||
655 | 183 | #--- | 194 | #--- |
656 | 184 | 195 | ||
658 | 185 | @log_call(logger.warning) | 196 | @log_call(logger.warning, with_args=False) |
659 | 186 | @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="s") | 197 | @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="s") |
660 | 187 | def remove_device(self, device_id): | 198 | def remove_device(self, device_id): |
661 | 188 | """Remove a given device.""" | 199 | """Remove a given device.""" |
662 | 189 | d = self.backend.remove_device(device_id) | 200 | d = self.backend.remove_device(device_id) |
663 | 190 | d.addCallback(self.DeviceRemoved) | 201 | d.addCallback(self.DeviceRemoved) |
665 | 191 | d.addErrback(transform_failure(self.DeviceRemovalError), device_id) | 202 | d.addErrback(self.transform(self.DeviceRemovalError), device_id) |
666 | 192 | 203 | ||
668 | 193 | @log_call(logger.warning) | 204 | @log_call(logger.warning, with_args=False) |
669 | 194 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s") | 205 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s") |
670 | 195 | def DeviceRemoved(self, device_id): | 206 | def DeviceRemoved(self, device_id): |
671 | 196 | """The removal for the device was completed.""" | 207 | """The removal for the device was completed.""" |
672 | 197 | 208 | ||
674 | 198 | @log_call(logger.error) | 209 | @log_call(logger.error, with_args=False) |
675 | 199 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="sa{ss}") | 210 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="sa{ss}") |
676 | 200 | def DeviceRemovalError(self, device_id, error): | 211 | def DeviceRemovalError(self, device_id, error): |
677 | 201 | """Problem removing the device.""" | 212 | """Problem removing the device.""" |
678 | @@ -234,7 +245,7 @@ | |||
679 | 234 | """Get the status of the file sync service.""" | 245 | """Get the status of the file sync service.""" |
680 | 235 | d = self.backend.file_sync_status() | 246 | d = self.backend.file_sync_status() |
681 | 236 | d.addCallback(self.process_status) | 247 | d.addCallback(self.process_status) |
683 | 237 | d.addErrback(transform_failure(self.FileSyncStatusError)) | 248 | d.addErrback(self.transform(self.FileSyncStatusError)) |
684 | 238 | 249 | ||
685 | 239 | @log_call(logger.debug) | 250 | @log_call(logger.debug) |
686 | 240 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s") | 251 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="s") |
687 | @@ -284,7 +295,7 @@ | |||
688 | 284 | """Enable the files service.""" | 295 | """Enable the files service.""" |
689 | 285 | d = self.backend.enable_files() | 296 | d = self.backend.enable_files() |
690 | 286 | d.addCallback(lambda _: self.FilesEnabled()) | 297 | d.addCallback(lambda _: self.FilesEnabled()) |
692 | 287 | d.addErrback(transform_failure(self.FilesEnableError)) | 298 | d.addErrback(self.transform(self.FilesEnableError)) |
693 | 288 | 299 | ||
694 | 289 | @log_call(logger.debug) | 300 | @log_call(logger.debug) |
695 | 290 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE) | 301 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE) |
696 | @@ -304,7 +315,7 @@ | |||
697 | 304 | """Disable the files service.""" | 315 | """Disable the files service.""" |
698 | 305 | d = self.backend.disable_files() | 316 | d = self.backend.disable_files() |
699 | 306 | d.addCallback(lambda _: self.FilesDisabled()) | 317 | d.addCallback(lambda _: self.FilesDisabled()) |
701 | 307 | d.addErrback(transform_failure(self.FilesDisableError)) | 318 | d.addErrback(self.transform(self.FilesDisableError)) |
702 | 308 | 319 | ||
703 | 309 | @log_call(logger.debug) | 320 | @log_call(logger.debug) |
704 | 310 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE) | 321 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE) |
705 | @@ -324,7 +335,7 @@ | |||
706 | 324 | """Connect the files service.""" | 335 | """Connect the files service.""" |
707 | 325 | d = self.backend.connect_files() | 336 | d = self.backend.connect_files() |
708 | 326 | d.addCallback(lambda _: self.FilesConnected()) | 337 | d.addCallback(lambda _: self.FilesConnected()) |
710 | 327 | d.addErrback(transform_failure(self.FilesConnectError)) | 338 | d.addErrback(self.transform(self.FilesConnectError)) |
711 | 328 | 339 | ||
712 | 329 | @log_call(logger.debug) | 340 | @log_call(logger.debug) |
713 | 330 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE) | 341 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE) |
714 | @@ -344,7 +355,7 @@ | |||
715 | 344 | """Disconnect the files service.""" | 355 | """Disconnect the files service.""" |
716 | 345 | d = self.backend.disconnect_files() | 356 | d = self.backend.disconnect_files() |
717 | 346 | d.addCallback(lambda _: self.FilesDisconnected()) | 357 | d.addCallback(lambda _: self.FilesDisconnected()) |
719 | 347 | d.addErrback(transform_failure(self.FilesDisconnectError)) | 358 | d.addErrback(self.transform(self.FilesDisconnectError)) |
720 | 348 | 359 | ||
721 | 349 | @log_call(logger.debug) | 360 | @log_call(logger.debug) |
722 | 350 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE) | 361 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE) |
723 | @@ -364,7 +375,7 @@ | |||
724 | 364 | """Restart the files service.""" | 375 | """Restart the files service.""" |
725 | 365 | d = self.backend.restart_files() | 376 | d = self.backend.restart_files() |
726 | 366 | d.addCallback(lambda _: self.FilesRestarted()) | 377 | d.addCallback(lambda _: self.FilesRestarted()) |
728 | 367 | d.addErrback(transform_failure(self.FilesRestartError)) | 378 | d.addErrback(self.transform(self.FilesRestartError)) |
729 | 368 | 379 | ||
730 | 369 | @log_call(logger.debug) | 380 | @log_call(logger.debug) |
731 | 370 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE) | 381 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE) |
732 | @@ -384,7 +395,7 @@ | |||
733 | 384 | """Start the files service.""" | 395 | """Start the files service.""" |
734 | 385 | d = self.backend.start_files() | 396 | d = self.backend.start_files() |
735 | 386 | d.addCallback(lambda _: self.FilesStarted()) | 397 | d.addCallback(lambda _: self.FilesStarted()) |
737 | 387 | d.addErrback(transform_failure(self.FilesStartError)) | 398 | d.addErrback(self.transform(self.FilesStartError)) |
738 | 388 | 399 | ||
739 | 389 | @log_call(logger.debug) | 400 | @log_call(logger.debug) |
740 | 390 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE) | 401 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE) |
741 | @@ -404,7 +415,7 @@ | |||
742 | 404 | """Stop the files service.""" | 415 | """Stop the files service.""" |
743 | 405 | d = self.backend.stop_files() | 416 | d = self.backend.stop_files() |
744 | 406 | d.addCallback(lambda _: self.FilesStopped()) | 417 | d.addCallback(lambda _: self.FilesStopped()) |
746 | 407 | d.addErrback(transform_failure(self.FilesStopError)) | 418 | d.addErrback(self.transform(self.FilesStopError)) |
747 | 408 | 419 | ||
748 | 409 | @log_call(logger.debug) | 420 | @log_call(logger.debug) |
749 | 410 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE) | 421 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE) |
750 | @@ -424,7 +435,7 @@ | |||
751 | 424 | """Find out the volumes info for the logged in user.""" | 435 | """Find out the volumes info for the logged in user.""" |
752 | 425 | d = self.backend.volumes_info() | 436 | d = self.backend.volumes_info() |
753 | 426 | d.addCallback(self.VolumesInfoReady) | 437 | d.addCallback(self.VolumesInfoReady) |
755 | 427 | d.addErrback(transform_failure(self.VolumesInfoError)) | 438 | d.addErrback(self.transform(self.VolumesInfoError)) |
756 | 428 | 439 | ||
757 | 429 | @log_call(logger.debug) | 440 | @log_call(logger.debug) |
758 | 430 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a(ssaa{ss})") | 441 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="a(ssaa{ss})") |
759 | @@ -444,7 +455,7 @@ | |||
760 | 444 | """Configure a given volume.""" | 455 | """Configure a given volume.""" |
761 | 445 | d = self.backend.change_volume_settings(volume_id, settings) | 456 | d = self.backend.change_volume_settings(volume_id, settings) |
762 | 446 | d.addCallback(self.VolumeSettingsChanged) | 457 | d.addCallback(self.VolumeSettingsChanged) |
764 | 447 | d.addErrback(transform_failure(self.VolumeSettingsChangeError), | 458 | d.addErrback(self.transform(self.VolumeSettingsChangeError), |
765 | 448 | volume_id) | 459 | volume_id) |
766 | 449 | 460 | ||
767 | 450 | @log_call(logger.info) | 461 | @log_call(logger.info) |
768 | @@ -465,7 +476,7 @@ | |||
769 | 465 | """Return the replications info.""" | 476 | """Return the replications info.""" |
770 | 466 | d = self.backend.replications_info() | 477 | d = self.backend.replications_info() |
771 | 467 | d.addCallback(self.ReplicationsInfoReady) | 478 | d.addCallback(self.ReplicationsInfoReady) |
773 | 468 | d.addErrback(transform_failure(self.ReplicationsInfoError)) | 479 | d.addErrback(self.transform(self.ReplicationsInfoError)) |
774 | 469 | 480 | ||
775 | 470 | @log_call(logger.debug) | 481 | @log_call(logger.debug) |
776 | 471 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="aa{ss}") | 482 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="aa{ss}") |
777 | @@ -485,7 +496,7 @@ | |||
778 | 485 | """Configure a given replication.""" | 496 | """Configure a given replication.""" |
779 | 486 | d = self.backend.change_replication_settings(replication_id, settings) | 497 | d = self.backend.change_replication_settings(replication_id, settings) |
780 | 487 | d.addCallback(self.ReplicationSettingsChanged) | 498 | d.addCallback(self.ReplicationSettingsChanged) |
782 | 488 | d.addErrback(transform_failure(self.ReplicationSettingsChangeError), | 499 | d.addErrback(self.transform(self.ReplicationSettingsChangeError), |
783 | 489 | replication_id) | 500 | replication_id) |
784 | 490 | 501 | ||
785 | 491 | @log_call(logger.info) | 502 | @log_call(logger.info) |
786 | @@ -506,7 +517,7 @@ | |||
787 | 506 | """Check if the extension to sync bookmarks is installed.""" | 517 | """Check if the extension to sync bookmarks is installed.""" |
788 | 507 | d = self.backend.query_bookmark_extension() | 518 | d = self.backend.query_bookmark_extension() |
789 | 508 | d.addCallback(self.QueryBookmarksResult) | 519 | d.addCallback(self.QueryBookmarksResult) |
791 | 509 | d.addErrback(transform_failure(self.QueryBookmarksError)) | 520 | d.addErrback(self.transform(self.QueryBookmarksError)) |
792 | 510 | 521 | ||
793 | 511 | @log_call(logger.debug) | 522 | @log_call(logger.debug) |
794 | 512 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="b") | 523 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="b") |
795 | @@ -526,7 +537,7 @@ | |||
796 | 526 | """Install the extension to sync bookmarks.""" | 537 | """Install the extension to sync bookmarks.""" |
797 | 527 | d = self.backend.install_bookmarks_extension() | 538 | d = self.backend.install_bookmarks_extension() |
798 | 528 | d.addCallback(lambda _: self.InstallBookmarksSuccess()) | 539 | d.addCallback(lambda _: self.InstallBookmarksSuccess()) |
800 | 529 | d.addErrback(transform_failure(self.InstallBookmarksError)) | 540 | d.addErrback(self.transform(self.InstallBookmarksError)) |
801 | 530 | 541 | ||
802 | 531 | @log_call(logger.info) | 542 | @log_call(logger.info) |
803 | 532 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="") | 543 | @signal(dbus_interface=DBUS_PREFERENCES_IFACE, signature="") |
804 | @@ -538,15 +549,24 @@ | |||
805 | 538 | def InstallBookmarksError(self, error): | 549 | def InstallBookmarksError(self, error): |
806 | 539 | """Problem installing the extension to sync bookmarks.""" | 550 | """Problem installing the extension to sync bookmarks.""" |
807 | 540 | 551 | ||
808 | 552 | #--- | ||
809 | 553 | |||
810 | 554 | @log_call(logger.info) | ||
811 | 555 | @method(dbus_interface=DBUS_PREFERENCES_IFACE, in_signature="") | ||
812 | 556 | def shutdown(self): | ||
813 | 557 | """Shutdown this service.""" | ||
814 | 558 | self.backend.shutdown() | ||
815 | 559 | |||
816 | 541 | 560 | ||
817 | 542 | def init_mainloop(): | 561 | def init_mainloop(): |
818 | 543 | """Start the DBus mainloop.""" | 562 | """Start the DBus mainloop.""" |
819 | 544 | DBusGMainLoop(set_as_default=True) | 563 | DBusGMainLoop(set_as_default=True) |
820 | 545 | 564 | ||
821 | 546 | 565 | ||
823 | 547 | def run_mainloop(): | 566 | def run_mainloop(loop=None): |
824 | 548 | """Run the gobject main loop.""" | 567 | """Run the gobject main loop.""" |
826 | 549 | loop = gobject.MainLoop() | 568 | if loop is None: |
827 | 569 | loop = gobject.MainLoop() | ||
828 | 550 | loop.run() | 570 | loop.run() |
829 | 551 | 571 | ||
830 | 552 | 572 | ||
831 | @@ -566,10 +586,10 @@ | |||
832 | 566 | return dbus.service.BusName(DBUS_BUS_NAME, bus=dbus.SessionBus()) | 586 | return dbus.service.BusName(DBUS_BUS_NAME, bus=dbus.SessionBus()) |
833 | 567 | 587 | ||
834 | 568 | 588 | ||
836 | 569 | def publish_backend(backend=None): | 589 | def publish_backend(backend=None, shutdown_func=None): |
837 | 570 | """Publish the backend on the DBus.""" | 590 | """Publish the backend on the DBus.""" |
838 | 571 | if backend is None: | 591 | if backend is None: |
840 | 572 | backend = ControlBackend() | 592 | backend = ControlBackend(shutdown_func=shutdown_func) |
841 | 573 | return ControlPanelBackend(backend=backend, | 593 | return ControlPanelBackend(backend=backend, |
842 | 574 | object_path=DBUS_PREFERENCES_PATH, | 594 | object_path=DBUS_PREFERENCES_PATH, |
843 | 575 | bus_name=get_busname()) | 595 | bus_name=get_busname()) |
844 | @@ -579,7 +599,8 @@ | |||
845 | 579 | """Hook the DBus listeners and start the main loop.""" | 599 | """Hook the DBus listeners and start the main loop.""" |
846 | 580 | init_mainloop() | 600 | init_mainloop() |
847 | 581 | if register_service(): | 601 | if register_service(): |
850 | 582 | publish_backend() | 602 | loop = gobject.MainLoop() |
851 | 583 | run_mainloop() | 603 | publish_backend(shutdown_func=loop.quit) |
852 | 604 | run_mainloop(loop=loop) | ||
853 | 584 | else: | 605 | else: |
854 | 585 | print "Control panel backend already running." | 606 | print "Control panel backend already running." |
855 | 586 | 607 | ||
856 | === modified file 'ubuntuone/controlpanel/gtk/__init__.py' | |||
857 | --- ubuntuone/controlpanel/gtk/__init__.py 2011-03-23 16:06:41 +0000 | |||
858 | +++ ubuntuone/controlpanel/gtk/__init__.py 2011-04-08 19:43:13 +0000 | |||
859 | @@ -18,5 +18,7 @@ | |||
860 | 18 | 18 | ||
861 | 19 | """The GTK graphical interface for the control panel for Ubuntu One.""" | 19 | """The GTK graphical interface for the control panel for Ubuntu One.""" |
862 | 20 | 20 | ||
865 | 21 | DBUS_BUS_NAME = "com.ubuntuone.controlpanel.gui" | 21 | DBUS_BUS_NAME = 'com.ubuntuone.controlpanel.gui' |
866 | 22 | TRANSLATION_DOMAIN = "ubuntuone-control-panel" | 22 | DBUS_PATH = '/gui' |
867 | 23 | DBUS_IFACE_GUI = 'com.ubuntuone.controlpanel.gui' | ||
868 | 24 | TRANSLATION_DOMAIN = 'ubuntuone-control-panel' | ||
869 | 23 | 25 | ||
870 | === modified file 'ubuntuone/controlpanel/gtk/gui.py' | |||
871 | --- ubuntuone/controlpanel/gtk/gui.py 2011-03-23 16:06:41 +0000 | |||
872 | +++ ubuntuone/controlpanel/gtk/gui.py 2011-04-08 19:43:13 +0000 | |||
873 | @@ -1,6 +1,7 @@ | |||
874 | 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
875 | 2 | 2 | ||
876 | 3 | # Authors: Natalia B Bidart <natalia.bidart@canonical.com> | 3 | # Authors: Natalia B Bidart <natalia.bidart@canonical.com> |
877 | 4 | # Eric Casteleijn <eric.casteleijn@canonical.com> | ||
878 | 4 | # | 5 | # |
879 | 5 | # Copyright 2010 Canonical Ltd. | 6 | # Copyright 2010 Canonical Ltd. |
880 | 6 | # | 7 | # |
881 | @@ -31,6 +32,7 @@ | |||
882 | 31 | import gobject | 32 | import gobject |
883 | 32 | import ubuntu_sso | 33 | import ubuntu_sso |
884 | 33 | 34 | ||
885 | 35 | from dbus.mainloop.glib import DBusGMainLoop | ||
886 | 34 | from ubuntu_sso import networkstate | 36 | from ubuntu_sso import networkstate |
887 | 35 | from ubuntu_sso.credentials import (TC_URL_KEY, HELP_TEXT_KEY, WINDOW_ID_KEY, | 37 | from ubuntu_sso.credentials import (TC_URL_KEY, HELP_TEXT_KEY, WINDOW_ID_KEY, |
888 | 36 | PING_URL_KEY) | 38 | PING_URL_KEY) |
889 | @@ -41,6 +43,9 @@ | |||
890 | 41 | PING_URL as U1_PING_URL, DESCRIPTION as U1_DESCRIPTION) | 43 | PING_URL as U1_PING_URL, DESCRIPTION as U1_DESCRIPTION) |
891 | 42 | # pylint: enable=E0611,F0401 | 44 | # pylint: enable=E0611,F0401 |
892 | 43 | 45 | ||
893 | 46 | from ubuntuone.controlpanel.gtk import ( | ||
894 | 47 | DBUS_IFACE_GUI, DBUS_BUS_NAME as DBUS_BUS_NAME_GUI, | ||
895 | 48 | DBUS_PATH as DBUS_PATH_GUI) | ||
896 | 44 | from ubuntuone.controlpanel.gtk.widgets import LabelLoading, PanelTitle | 49 | from ubuntuone.controlpanel.gtk.widgets import LabelLoading, PanelTitle |
897 | 45 | # Use ubiquity package when ready (LP: #673665) | 50 | # Use ubiquity package when ready (LP: #673665) |
898 | 46 | from ubuntuone.controlpanel.gtk.widgets import GreyableBin | 51 | from ubuntuone.controlpanel.gtk.widgets import GreyableBin |
899 | @@ -50,10 +55,18 @@ | |||
900 | 50 | from ubuntuone.controlpanel.backend import (DEVICE_TYPE_PHONE, | 55 | from ubuntuone.controlpanel.backend import (DEVICE_TYPE_PHONE, |
901 | 51 | DEVICE_TYPE_COMPUTER, bool_str) | 56 | DEVICE_TYPE_COMPUTER, bool_str) |
902 | 52 | from ubuntuone.controlpanel.logger import setup_logging, log_call | 57 | from ubuntuone.controlpanel.logger import setup_logging, log_call |
904 | 53 | from ubuntuone.controlpanel.utils import get_data_file | 58 | from ubuntuone.controlpanel.utils import (get_data_file, |
905 | 59 | ERROR_TYPE, ERROR_MESSAGE) | ||
906 | 54 | 60 | ||
907 | 55 | from ubuntuone.controlpanel.gtk import package_manager, TRANSLATION_DOMAIN | 61 | from ubuntuone.controlpanel.gtk import package_manager, TRANSLATION_DOMAIN |
908 | 56 | 62 | ||
909 | 63 | try: | ||
910 | 64 | from gi.repository import Unity # pylint: disable=E0611 | ||
911 | 65 | USE_LIBUNITY = True | ||
912 | 66 | U1_DOTDESKTOP = "ubuntuone-control-panel-gtk.desktop" | ||
913 | 67 | except ImportError: | ||
914 | 68 | USE_LIBUNITY = False | ||
915 | 69 | |||
916 | 57 | logger = setup_logging('gtk.gui') | 70 | logger = setup_logging('gtk.gui') |
917 | 58 | _ = gettext.gettext | 71 | _ = gettext.gettext |
918 | 59 | 72 | ||
919 | @@ -63,6 +76,7 @@ | |||
920 | 63 | ERROR_COLOR = 'red' | 76 | ERROR_COLOR = 'red' |
921 | 64 | LOADING = _('Loading...') | 77 | LOADING = _('Loading...') |
922 | 65 | VALUE_ERROR = _('Value could not be retrieved.') | 78 | VALUE_ERROR = _('Value could not be retrieved.') |
923 | 79 | UNKNOWN_ERROR = _('Unknown error') | ||
924 | 66 | WARNING_MARKUP = '<span foreground="%s"><b>%%s</b></span>' % ERROR_COLOR | 80 | WARNING_MARKUP = '<span foreground="%s"><b>%%s</b></span>' % ERROR_COLOR |
925 | 67 | KILOBYTES = 1024 | 81 | KILOBYTES = 1024 |
926 | 68 | NO_OP = lambda *a, **kw: None | 82 | NO_OP = lambda *a, **kw: None |
927 | @@ -74,6 +88,49 @@ | |||
928 | 74 | logger.error('Error handler received: %r, %r', args, kwargs) | 88 | logger.error('Error handler received: %r, %r', args, kwargs) |
929 | 75 | 89 | ||
930 | 76 | 90 | ||
931 | 91 | def register_service(bus): | ||
932 | 92 | """Try to register DBus service for making sure we run only one instance. | ||
933 | 93 | |||
934 | 94 | Return True if succesfully registered, False if already running. | ||
935 | 95 | """ | ||
936 | 96 | name = bus.request_name(DBUS_BUS_NAME_GUI, | ||
937 | 97 | dbus.bus.NAME_FLAG_DO_NOT_QUEUE) | ||
938 | 98 | return name != dbus.bus.REQUEST_NAME_REPLY_EXISTS | ||
939 | 99 | |||
940 | 100 | |||
941 | 101 | def publish_service(window=None, switch_to='', alert=False): | ||
942 | 102 | """Publish the service on DBus.""" | ||
943 | 103 | if window is None: | ||
944 | 104 | window = ControlPanelWindow(switch_to=switch_to, alert=alert) | ||
945 | 105 | return ControlPanelService(window) | ||
946 | 106 | |||
947 | 107 | |||
948 | 108 | def main(switch_to='', alert=False): | ||
949 | 109 | """Hook the DBus listeners and start the main loop.""" | ||
950 | 110 | DBusGMainLoop(set_as_default=True) | ||
951 | 111 | bus = dbus.SessionBus() | ||
952 | 112 | if register_service(bus): | ||
953 | 113 | publish_service(switch_to=switch_to, alert=alert) | ||
954 | 114 | else: | ||
955 | 115 | obj = bus.get_object(DBUS_BUS_NAME_GUI, DBUS_PATH_GUI) | ||
956 | 116 | service = dbus.Interface(obj, dbus_interface=DBUS_IFACE_GUI) | ||
957 | 117 | |||
958 | 118 | def gui_error_handler(*args, **kwargs): | ||
959 | 119 | """Log errors when calling D-Bus methods in a async way.""" | ||
960 | 120 | logger.error('Error handler received: %r, %r', args, kwargs) | ||
961 | 121 | gtk.main_quit() | ||
962 | 122 | |||
963 | 123 | def gui_reply_handler(*args, **kwargs): | ||
964 | 124 | """Exit when done.""" | ||
965 | 125 | gtk.main_quit() | ||
966 | 126 | |||
967 | 127 | service.switch_to_alert( | ||
968 | 128 | switch_to, alert, reply_handler=gui_reply_handler, | ||
969 | 129 | error_handler=gui_error_handler) | ||
970 | 130 | |||
971 | 131 | gtk.main() | ||
972 | 132 | |||
973 | 133 | |||
974 | 77 | def filter_by_app_name(f): | 134 | def filter_by_app_name(f): |
975 | 78 | """Excecute 'f' filtering by app_name.""" | 135 | """Excecute 'f' filtering by app_name.""" |
976 | 79 | 136 | ||
977 | @@ -192,10 +249,19 @@ | |||
978 | 192 | self.message.set_markup(message) | 249 | self.message.set_markup(message) |
979 | 193 | 250 | ||
980 | 194 | @log_call(logger.error) | 251 | @log_call(logger.error) |
982 | 195 | def on_error(self, message=None): | 252 | def on_error(self, message=None, error_dict=None): |
983 | 196 | """Use this callback to stop the Loading and set a warning message.""" | 253 | """Use this callback to stop the Loading and set a warning message.""" |
985 | 197 | if message == None: | 254 | if message is None and error_dict is None: |
986 | 198 | message = VALUE_ERROR | 255 | message = VALUE_ERROR |
987 | 256 | elif message is None and error_dict is not None: | ||
988 | 257 | error_type = error_dict.get(ERROR_TYPE, UNKNOWN_ERROR) | ||
989 | 258 | error_msg = error_dict.get(ERROR_MESSAGE) | ||
990 | 259 | if error_msg: | ||
991 | 260 | message = "%s (%s: %s)" % (VALUE_ERROR, error_type, error_msg) | ||
992 | 261 | else: | ||
993 | 262 | message = "%s (%s)" % (VALUE_ERROR, error_type) | ||
994 | 263 | |||
995 | 264 | assert message is not None | ||
996 | 199 | 265 | ||
997 | 200 | self.message.stop() | 266 | self.message.stop() |
998 | 201 | self.message.set_markup(WARNING_MARKUP % message) | 267 | self.message.set_markup(WARNING_MARKUP % message) |
999 | @@ -492,6 +558,10 @@ | |||
1000 | 492 | elif name == self.MUSIC_DISPLAY_NAME: | 558 | elif name == self.MUSIC_DISPLAY_NAME: |
1001 | 493 | icon_name = self.MUSIC_ICON_NAME | 559 | icon_name = self.MUSIC_ICON_NAME |
1002 | 494 | 560 | ||
1003 | 561 | if volume[u'path'] is None: | ||
1004 | 562 | logger.warning('on_volumes_info_ready: about to store a ' | ||
1005 | 563 | 'volume with None path: %r', volume) | ||
1006 | 564 | |||
1007 | 495 | row = (name, bool(volume[u'subscribed']), icon_name, True, | 565 | row = (name, bool(volume[u'subscribed']), icon_name, True, |
1008 | 496 | sensitive, gtk.ICON_SIZE_MENU, volume['volume_id'], | 566 | sensitive, gtk.ICON_SIZE_MENU, volume['volume_id'], |
1009 | 497 | volume[u'path']) | 567 | volume[u'path']) |
1010 | @@ -509,7 +579,7 @@ | |||
1011 | 509 | @log_call(logger.error) | 579 | @log_call(logger.error) |
1012 | 510 | def on_volumes_info_error(self, error_dict=None): | 580 | def on_volumes_info_error(self, error_dict=None): |
1013 | 511 | """Backend notifies of an error when fetching volumes info.""" | 581 | """Backend notifies of an error when fetching volumes info.""" |
1015 | 512 | self.on_error() | 582 | self.on_error(error_dict=error_dict) |
1016 | 513 | 583 | ||
1017 | 514 | @log_call(logger.info) | 584 | @log_call(logger.info) |
1018 | 515 | def on_volume_settings_changed(self, volume_id): | 585 | def on_volume_settings_changed(self, volume_id): |
1019 | @@ -548,7 +618,14 @@ | |||
1020 | 548 | """The user double clicked on a row.""" | 618 | """The user double clicked on a row.""" |
1021 | 549 | treeiter = self.volumes_store.get_iter(path) | 619 | treeiter = self.volumes_store.get_iter(path) |
1022 | 550 | volume_path = self.volumes_store.get_value(treeiter, 7) | 620 | volume_path = self.volumes_store.get_value(treeiter, 7) |
1024 | 551 | uri_hook(None, FILE_URI_PREFIX + volume_path) | 621 | if volume_path is None: |
1025 | 622 | logger.warning('on_volumes_view_row_activated: volume_path for ' | ||
1026 | 623 | 'tree_path %r is None', path) | ||
1027 | 624 | elif not os.path.exists(volume_path): | ||
1028 | 625 | logger.warning('on_volumes_view_row_activated: path %r ' | ||
1029 | 626 | 'does not exist', volume_path) | ||
1030 | 627 | else: | ||
1031 | 628 | uri_hook(None, FILE_URI_PREFIX + volume_path) | ||
1032 | 552 | 629 | ||
1033 | 553 | def load(self): | 630 | def load(self): |
1034 | 554 | """Load the volume list.""" | 631 | """Load the volume list.""" |
1035 | @@ -827,7 +904,7 @@ | |||
1036 | 827 | @log_call(logger.error) | 904 | @log_call(logger.error) |
1037 | 828 | def on_devices_info_error(self, error_dict=None): | 905 | def on_devices_info_error(self, error_dict=None): |
1038 | 829 | """Backend notifies of an error when fetching volumes info.""" | 906 | """Backend notifies of an error when fetching volumes info.""" |
1040 | 830 | self.on_error() | 907 | self.on_error(error_dict=error_dict) |
1041 | 831 | self.is_processing = False | 908 | self.is_processing = False |
1042 | 832 | 909 | ||
1043 | 833 | @log_call(logger.warning) | 910 | @log_call(logger.warning) |
1044 | @@ -992,7 +1069,8 @@ | |||
1045 | 992 | def on_file_sync_status_changed(self, status): | 1069 | def on_file_sync_status_changed(self, status): |
1046 | 993 | """File Sync status changed.""" | 1070 | """File Sync status changed.""" |
1047 | 994 | enabled = status != backend.FILE_SYNC_DISABLED | 1071 | enabled = status != backend.FILE_SYNC_DISABLED |
1049 | 995 | logger.info('FileSyncService: enabled? %r', enabled) | 1072 | logger.info('FileSyncService: on_file_sync_status_changed: ' |
1050 | 1073 | 'status %r, enabled? %r', status, enabled) | ||
1051 | 996 | self.check_button.set_active(enabled) | 1074 | self.check_button.set_active(enabled) |
1052 | 997 | # if service is disabled, disable the action_button | 1075 | # if service is disabled, disable the action_button |
1053 | 998 | self.action_button.set_sensitive(enabled) | 1076 | self.action_button.set_sensitive(enabled) |
1054 | @@ -1025,8 +1103,8 @@ | |||
1055 | 1025 | class DesktopcouchService(Service): | 1103 | class DesktopcouchService(Service): |
1056 | 1026 | """A desktopcouch service.""" | 1104 | """A desktopcouch service.""" |
1057 | 1027 | 1105 | ||
1060 | 1028 | INSTALL_PACKAGE = _('Install the %(plugin_name)s ' | 1106 | INSTALL_PACKAGE = _('Install the %(plugin_name)s for the sync service: ' |
1061 | 1029 | 'for %(service_name)s sync') | 1107 | '%(service_name)s') |
1062 | 1030 | 1108 | ||
1063 | 1031 | def __init__(self, service_id, name, enabled, | 1109 | def __init__(self, service_id, name, enabled, |
1064 | 1032 | container, check_button, | 1110 | container, check_button, |
1065 | @@ -1152,6 +1230,7 @@ | |||
1066 | 1152 | @log_call(logger.debug) | 1230 | @log_call(logger.debug) |
1067 | 1153 | def load(self): | 1231 | def load(self): |
1068 | 1154 | """Load info.""" | 1232 | """Load info.""" |
1069 | 1233 | self.replications.hide() | ||
1070 | 1155 | if self.install_box is not None: | 1234 | if self.install_box is not None: |
1071 | 1156 | self.itself.remove(self.install_box) | 1235 | self.itself.remove(self.install_box) |
1072 | 1157 | self.install_box = None | 1236 | self.install_box = None |
1073 | @@ -1159,7 +1238,6 @@ | |||
1074 | 1159 | logger.info('load: has_desktopcouch? %r', self.has_desktopcouch) | 1238 | logger.info('load: has_desktopcouch? %r', self.has_desktopcouch) |
1075 | 1160 | if not self.has_desktopcouch: | 1239 | if not self.has_desktopcouch: |
1076 | 1161 | self.message.set_text('') | 1240 | self.message.set_text('') |
1077 | 1162 | self.replications.hide() | ||
1078 | 1163 | 1241 | ||
1079 | 1164 | self.install_box = InstallPackage(self.DESKTOPCOUCH_PKG) | 1242 | self.install_box = InstallPackage(self.DESKTOPCOUCH_PKG) |
1080 | 1165 | self.install_box.connect('finished', self.load_replications) | 1243 | self.install_box.connect('finished', self.load_replications) |
1081 | @@ -1211,7 +1289,7 @@ | |||
1082 | 1211 | error_dict.get('error_type', None) == 'NoPairingRecord': | 1289 | error_dict.get('error_type', None) == 'NoPairingRecord': |
1083 | 1212 | self.on_error(self.NO_PAIRING_RECORD) | 1290 | self.on_error(self.NO_PAIRING_RECORD) |
1084 | 1213 | else: | 1291 | else: |
1086 | 1214 | self.on_error() | 1292 | self.on_error(error_dict=error_dict) |
1087 | 1215 | 1293 | ||
1088 | 1216 | 1294 | ||
1089 | 1217 | class FileSyncStatus(gtk.HBox, ControlPanelMixin): | 1295 | class FileSyncStatus(gtk.HBox, ControlPanelMixin): |
1090 | @@ -1252,6 +1330,8 @@ | |||
1091 | 1252 | self.button.connect('clicked', self._on_button_clicked) | 1330 | self.button.connect('clicked', self._on_button_clicked) |
1092 | 1253 | self.pack_start(self.button, expand=False) | 1331 | self.pack_start(self.button, expand=False) |
1093 | 1254 | 1332 | ||
1094 | 1333 | self.show_all() | ||
1095 | 1334 | |||
1096 | 1255 | self.backend.connect_to_signal('FileSyncStatusDisabled', | 1335 | self.backend.connect_to_signal('FileSyncStatusDisabled', |
1097 | 1256 | self.on_file_sync_status_disabled) | 1336 | self.on_file_sync_status_disabled) |
1098 | 1257 | self.backend.connect_to_signal('FileSyncStatusStarting', | 1337 | self.backend.connect_to_signal('FileSyncStatusStarting', |
1099 | @@ -1268,10 +1348,10 @@ | |||
1100 | 1268 | self.on_file_sync_status_error) | 1348 | self.on_file_sync_status_error) |
1101 | 1269 | self.backend.connect_to_signal('FilesStartError', | 1349 | self.backend.connect_to_signal('FilesStartError', |
1102 | 1270 | self.on_files_start_error) | 1350 | self.on_files_start_error) |
1107 | 1271 | 1351 | self.backend.connect_to_signal('FilesEnabled', | |
1108 | 1272 | self.backend.file_sync_status(reply_handler=NO_OP, | 1352 | self.on_file_sync_status_starting) |
1109 | 1273 | error_handler=error_handler) | 1353 | self.backend.connect_to_signal('FilesDisabled', |
1110 | 1274 | self.show_all() | 1354 | self.on_file_sync_status_disabled) |
1111 | 1275 | 1355 | ||
1112 | 1276 | def _update_status(self, msg, action, callback, | 1356 | def _update_status(self, msg, action, callback, |
1113 | 1277 | icon=None, color=None, tooltip=None): | 1357 | icon=None, color=None, tooltip=None): |
1114 | @@ -1296,42 +1376,42 @@ | |||
1115 | 1296 | button.get_data('callback')(button) | 1376 | button.get_data('callback')(button) |
1116 | 1297 | 1377 | ||
1117 | 1298 | @log_call(logger.info) | 1378 | @log_call(logger.info) |
1119 | 1299 | def on_file_sync_status_disabled(self, msg): | 1379 | def on_file_sync_status_disabled(self, msg=None): |
1120 | 1300 | """Backend notifies of file sync status being disabled.""" | 1380 | """Backend notifies of file sync status being disabled.""" |
1121 | 1301 | self._update_status(self.FILE_SYNC_DISABLED, | 1381 | self._update_status(self.FILE_SYNC_DISABLED, |
1122 | 1302 | self.ENABLE, self.on_enable_clicked, | 1382 | self.ENABLE, self.on_enable_clicked, |
1123 | 1303 | '✘', 'red', self.ENABLE_TOOLTIP) | 1383 | '✘', 'red', self.ENABLE_TOOLTIP) |
1124 | 1304 | 1384 | ||
1125 | 1305 | @log_call(logger.info) | 1385 | @log_call(logger.info) |
1127 | 1306 | def on_file_sync_status_starting(self, msg): | 1386 | def on_file_sync_status_starting(self, msg=None): |
1128 | 1307 | """Backend notifies of file sync status being starting.""" | 1387 | """Backend notifies of file sync status being starting.""" |
1129 | 1308 | self._update_status(self.FILE_SYNC_STARTING, | 1388 | self._update_status(self.FILE_SYNC_STARTING, |
1130 | 1309 | self.STOP, self.on_stop_clicked, | 1389 | self.STOP, self.on_stop_clicked, |
1131 | 1310 | '⇅', ORANGE, self.STOP_TOOLTIP) | 1390 | '⇅', ORANGE, self.STOP_TOOLTIP) |
1132 | 1311 | 1391 | ||
1133 | 1312 | @log_call(logger.info) | 1392 | @log_call(logger.info) |
1135 | 1313 | def on_file_sync_status_stopped(self, msg): | 1393 | def on_file_sync_status_stopped(self, msg=None): |
1136 | 1314 | """Backend notifies of file sync being stopped.""" | 1394 | """Backend notifies of file sync being stopped.""" |
1137 | 1315 | self._update_status(self.FILE_SYNC_STOPPED, | 1395 | self._update_status(self.FILE_SYNC_STOPPED, |
1138 | 1316 | self.START, self.on_start_clicked, | 1396 | self.START, self.on_start_clicked, |
1139 | 1317 | '✘', 'red', self.START_TOOLTIP) | 1397 | '✘', 'red', self.START_TOOLTIP) |
1140 | 1318 | 1398 | ||
1141 | 1319 | @log_call(logger.info) | 1399 | @log_call(logger.info) |
1143 | 1320 | def on_file_sync_status_disconnected(self, msg): | 1400 | def on_file_sync_status_disconnected(self, msg=None): |
1144 | 1321 | """Backend notifies of file sync status being ready.""" | 1401 | """Backend notifies of file sync status being ready.""" |
1145 | 1322 | self._update_status(self.FILE_SYNC_DISCONNECTED, | 1402 | self._update_status(self.FILE_SYNC_DISCONNECTED, |
1146 | 1323 | self.CONNECT, self.on_connect_clicked, | 1403 | self.CONNECT, self.on_connect_clicked, |
1147 | 1324 | '✘', 'red', self.CONNECT_TOOLTIP,) | 1404 | '✘', 'red', self.CONNECT_TOOLTIP,) |
1148 | 1325 | 1405 | ||
1149 | 1326 | @log_call(logger.info) | 1406 | @log_call(logger.info) |
1151 | 1327 | def on_file_sync_status_syncing(self, msg): | 1407 | def on_file_sync_status_syncing(self, msg=None): |
1152 | 1328 | """Backend notifies of file sync status being syncing.""" | 1408 | """Backend notifies of file sync status being syncing.""" |
1153 | 1329 | self._update_status(self.FILE_SYNC_SYNCING, | 1409 | self._update_status(self.FILE_SYNC_SYNCING, |
1154 | 1330 | self.DISCONNECT, self.on_disconnect_clicked, | 1410 | self.DISCONNECT, self.on_disconnect_clicked, |
1155 | 1331 | '⇅', ORANGE, self.DISCONNECT_TOOLTIP) | 1411 | '⇅', ORANGE, self.DISCONNECT_TOOLTIP) |
1156 | 1332 | 1412 | ||
1157 | 1333 | @log_call(logger.info) | 1413 | @log_call(logger.info) |
1159 | 1334 | def on_file_sync_status_idle(self, msg): | 1414 | def on_file_sync_status_idle(self, msg=None): |
1160 | 1335 | """Backend notifies of file sync status being idle.""" | 1415 | """Backend notifies of file sync status being idle.""" |
1161 | 1336 | self._update_status(self.FILE_SYNC_IDLE, | 1416 | self._update_status(self.FILE_SYNC_IDLE, |
1162 | 1337 | self.DISCONNECT, self.on_disconnect_clicked, | 1417 | self.DISCONNECT, self.on_disconnect_clicked, |
1163 | @@ -1385,6 +1465,11 @@ | |||
1164 | 1385 | self.backend.stop_files(reply_handler=NO_OP, | 1465 | self.backend.stop_files(reply_handler=NO_OP, |
1165 | 1386 | error_handler=error_handler) | 1466 | error_handler=error_handler) |
1166 | 1387 | 1467 | ||
1167 | 1468 | def load(self): | ||
1168 | 1469 | """Load the information.""" | ||
1169 | 1470 | self.backend.file_sync_status(reply_handler=NO_OP, | ||
1170 | 1471 | error_handler=error_handler) | ||
1171 | 1472 | |||
1172 | 1388 | 1473 | ||
1173 | 1389 | class ManagementPanel(gtk.VBox, ControlPanelMixin): | 1474 | class ManagementPanel(gtk.VBox, ControlPanelMixin): |
1174 | 1390 | """The management panel. | 1475 | """The management panel. |
1175 | @@ -1396,6 +1481,7 @@ | |||
1176 | 1396 | __gsignals__ = { | 1481 | __gsignals__ = { |
1177 | 1397 | 'local-device-removed': (gobject.SIGNAL_RUN_FIRST, | 1482 | 'local-device-removed': (gobject.SIGNAL_RUN_FIRST, |
1178 | 1398 | gobject.TYPE_NONE, ()), | 1483 | gobject.TYPE_NONE, ()), |
1179 | 1484 | 'unauthorized': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()), | ||
1180 | 1399 | } | 1485 | } |
1181 | 1400 | 1486 | ||
1182 | 1401 | QUOTA_LABEL = _('Using %(used)s of %(total)s (%(percentage).0f%%)') | 1487 | QUOTA_LABEL = _('Using %(used)s of %(total)s (%(percentage).0f%%)') |
1183 | @@ -1421,6 +1507,8 @@ | |||
1184 | 1421 | self.on_account_info_ready) | 1507 | self.on_account_info_ready) |
1185 | 1422 | self.backend.connect_to_signal('AccountInfoError', | 1508 | self.backend.connect_to_signal('AccountInfoError', |
1186 | 1423 | self.on_account_info_error) | 1509 | self.on_account_info_error) |
1187 | 1510 | self.backend.connect_to_signal('UnauthorizedError', | ||
1188 | 1511 | self.on_unauthorized_error) | ||
1189 | 1424 | 1512 | ||
1190 | 1425 | self.quota_progressbar.set_sensitive(False) | 1513 | self.quota_progressbar.set_sensitive(False) |
1191 | 1426 | 1514 | ||
1192 | @@ -1461,7 +1549,11 @@ | |||
1193 | 1461 | 1549 | ||
1194 | 1462 | self.services_button.set_name(self.SERVICES_BUTTON_NAME) | 1550 | self.services_button.set_name(self.SERVICES_BUTTON_NAME) |
1195 | 1463 | self.services_button.set_tooltip_text(self.SERVICES_BUTTON_TOOLTIP) | 1551 | self.services_button.set_tooltip_text(self.SERVICES_BUTTON_TOOLTIP) |
1197 | 1464 | self.services.load() | 1552 | |
1198 | 1553 | self.enable_volumes = lambda: self.volumes_button.set_sensitive(True) | ||
1199 | 1554 | self.disable_volumes = lambda: self.volumes_button.set_sensitive(False) | ||
1200 | 1555 | self.backend.connect_to_signal('FilesEnabled', self.enable_volumes) | ||
1201 | 1556 | self.backend.connect_to_signal('FilesDisabled', self.disable_volumes) | ||
1202 | 1465 | 1557 | ||
1203 | 1466 | def _update_quota(self, msg, data=None): | 1558 | def _update_quota(self, msg, data=None): |
1204 | 1467 | """Update the quota info.""" | 1559 | """Update the quota info.""" |
1205 | @@ -1491,6 +1583,8 @@ | |||
1206 | 1491 | """Load the account info and file sync status list.""" | 1583 | """Load the account info and file sync status list.""" |
1207 | 1492 | self.backend.account_info(reply_handler=NO_OP, | 1584 | self.backend.account_info(reply_handler=NO_OP, |
1208 | 1493 | error_handler=error_handler) | 1585 | error_handler=error_handler) |
1209 | 1586 | self.status_label.load() | ||
1210 | 1587 | self.services.load() | ||
1211 | 1494 | 1588 | ||
1212 | 1495 | @log_call(logger.debug) | 1589 | @log_call(logger.debug) |
1213 | 1496 | def on_account_info_ready(self, info): | 1590 | def on_account_info_ready(self, info): |
1214 | @@ -1506,15 +1600,22 @@ | |||
1215 | 1506 | """Backend notifies of an error when fetching account info.""" | 1600 | """Backend notifies of an error when fetching account info.""" |
1216 | 1507 | self._update_quota(msg='') | 1601 | self._update_quota(msg='') |
1217 | 1508 | 1602 | ||
1220 | 1509 | 1603 | @log_call(logger.error) | |
1221 | 1510 | class ControlPanel(gtk.Notebook): | 1604 | def on_unauthorized_error(self, error_dict=None): |
1222 | 1605 | """Backend notifies that credentials are not valid.""" | ||
1223 | 1606 | self.emit('unauthorized') | ||
1224 | 1607 | |||
1225 | 1608 | |||
1226 | 1609 | class ControlPanel(gtk.Notebook, ControlPanelMixin): | ||
1227 | 1511 | """The control panel per se, can be added into any other widget.""" | 1610 | """The control panel per se, can be added into any other widget.""" |
1228 | 1512 | 1611 | ||
1229 | 1513 | # should not be any larger than 736x525 | 1612 | # should not be any larger than 736x525 |
1230 | 1514 | 1613 | ||
1231 | 1515 | def __init__(self, main_window): | 1614 | def __init__(self, main_window): |
1232 | 1516 | gtk.Notebook.__init__(self) | 1615 | gtk.Notebook.__init__(self) |
1233 | 1616 | ControlPanelMixin.__init__(self) | ||
1234 | 1517 | gtk.link_button_set_uri_hook(uri_hook) | 1617 | gtk.link_button_set_uri_hook(uri_hook) |
1235 | 1618 | self.connect('destroy', self.shutdown) | ||
1236 | 1518 | 1619 | ||
1237 | 1519 | self.main_window = main_window | 1620 | self.main_window = main_window |
1238 | 1520 | 1621 | ||
1239 | @@ -1531,6 +1632,8 @@ | |||
1240 | 1531 | self.on_show_management_panel) | 1632 | self.on_show_management_panel) |
1241 | 1532 | self.management.connect('local-device-removed', | 1633 | self.management.connect('local-device-removed', |
1242 | 1533 | self.on_show_overview_panel) | 1634 | self.on_show_overview_panel) |
1243 | 1635 | self.management.connect('unauthorized', | ||
1244 | 1636 | self.on_show_overview_panel) | ||
1245 | 1534 | 1637 | ||
1246 | 1535 | self.show() | 1638 | self.show() |
1247 | 1536 | self.on_show_overview_panel() | 1639 | self.on_show_overview_panel() |
1248 | @@ -1538,6 +1641,12 @@ | |||
1249 | 1538 | logger.debug('%s: started (window size %r).', | 1641 | logger.debug('%s: started (window size %r).', |
1250 | 1539 | self.__class__.__name__, self.get_size_request()) | 1642 | self.__class__.__name__, self.get_size_request()) |
1251 | 1540 | 1643 | ||
1252 | 1644 | def shutdown(self, *args, **kwargs): | ||
1253 | 1645 | """Shutdown backend.""" | ||
1254 | 1646 | logger.info('Shutting down...') | ||
1255 | 1647 | self.backend.shutdown(reply_handler=NO_OP, | ||
1256 | 1648 | error_handler=error_handler) | ||
1257 | 1649 | |||
1258 | 1541 | def on_show_overview_panel(self, widget=None): | 1650 | def on_show_overview_panel(self, widget=None): |
1259 | 1542 | """Show the overview panel.""" | 1651 | """Show the overview panel.""" |
1260 | 1543 | self.set_current_page(0) | 1652 | self.set_current_page(0) |
1261 | @@ -1550,18 +1659,42 @@ | |||
1262 | 1550 | if credentials_are_new: | 1659 | if credentials_are_new: |
1263 | 1551 | # redirect user to services page to start using Ubuntu One | 1660 | # redirect user to services page to start using Ubuntu One |
1264 | 1552 | self.management.services_button.clicked() | 1661 | self.management.services_button.clicked() |
1265 | 1662 | # instruct syncdaemon to connect | ||
1266 | 1663 | self.backend.connect_files(reply_handler=NO_OP, | ||
1267 | 1664 | error_handler=error_handler) | ||
1268 | 1553 | 1665 | ||
1269 | 1554 | self.next_page() | 1666 | self.next_page() |
1270 | 1555 | 1667 | ||
1271 | 1556 | 1668 | ||
1272 | 1669 | class ControlPanelService(dbus.service.Object): | ||
1273 | 1670 | """DBUS service that exposes some of the window's methods.""" | ||
1274 | 1671 | |||
1275 | 1672 | def __init__(self, window): | ||
1276 | 1673 | self.window = window | ||
1277 | 1674 | bus_name = dbus.service.BusName( | ||
1278 | 1675 | DBUS_BUS_NAME_GUI, bus=dbus.SessionBus()) | ||
1279 | 1676 | dbus.service.Object.__init__( | ||
1280 | 1677 | self, bus_name=bus_name, object_path=DBUS_PATH_GUI) | ||
1281 | 1678 | |||
1282 | 1679 | @log_call(logger.debug) | ||
1283 | 1680 | @dbus.service.method(dbus_interface=DBUS_IFACE_GUI, in_signature='sb') | ||
1284 | 1681 | def switch_to_alert(self, panel='', alert=False): | ||
1285 | 1682 | """Switch to named panel.""" | ||
1286 | 1683 | if panel: | ||
1287 | 1684 | self.window.switch_to(panel) | ||
1288 | 1685 | if alert: | ||
1289 | 1686 | self.window.draw_attention() | ||
1290 | 1687 | |||
1291 | 1688 | |||
1292 | 1557 | class ControlPanelWindow(gtk.Window): | 1689 | class ControlPanelWindow(gtk.Window): |
1293 | 1558 | """The main window for the Ubuntu One control panel.""" | 1690 | """The main window for the Ubuntu One control panel.""" |
1294 | 1559 | 1691 | ||
1295 | 1560 | TITLE = _('%(app_name)s Control Panel') | 1692 | TITLE = _('%(app_name)s Control Panel') |
1296 | 1561 | 1693 | ||
1298 | 1562 | def __init__(self, switch_to=None, alert=False): | 1694 | def __init__(self, switch_to='', alert=False): |
1299 | 1563 | super(ControlPanelWindow, self).__init__() | 1695 | super(ControlPanelWindow, self).__init__() |
1300 | 1564 | 1696 | ||
1301 | 1697 | self.connect('focus-in-event', self.remove_urgency) | ||
1302 | 1565 | self.set_title(self.TITLE % {'app_name': U1_APP_NAME}) | 1698 | self.set_title(self.TITLE % {'app_name': U1_APP_NAME}) |
1303 | 1566 | self.set_position(gtk.WIN_POS_CENTER_ALWAYS) | 1699 | self.set_position(gtk.WIN_POS_CENTER_ALWAYS) |
1304 | 1567 | self.set_icon_name('ubuntuone') | 1700 | self.set_icon_name('ubuntuone') |
1305 | @@ -1569,9 +1702,7 @@ | |||
1306 | 1569 | 1702 | ||
1307 | 1570 | self.connect('delete-event', lambda w, e: gtk.main_quit()) | 1703 | self.connect('delete-event', lambda w, e: gtk.main_quit()) |
1308 | 1571 | if alert: | 1704 | if alert: |
1312 | 1572 | # NOTE this should prevent focus stealing but it does not :( | 1705 | self.draw_attention() |
1310 | 1573 | self.present_with_time(1) | ||
1311 | 1574 | self.set_urgency_hint(True) | ||
1313 | 1575 | else: | 1706 | else: |
1314 | 1576 | self.present() | 1707 | self.present() |
1315 | 1577 | 1708 | ||
1316 | @@ -1580,17 +1711,35 @@ | |||
1317 | 1580 | 1711 | ||
1318 | 1581 | logger.info('Starting %s pointing at panel: %r.', | 1712 | logger.info('Starting %s pointing at panel: %r.', |
1319 | 1582 | self.__class__.__name__, switch_to) | 1713 | self.__class__.__name__, switch_to) |
1327 | 1583 | if switch_to is not None: | 1714 | if switch_to: |
1328 | 1584 | button = getattr(self.control_panel.management, | 1715 | self.switch_to(switch_to) |
1322 | 1585 | '%s_button' % switch_to, None) | ||
1323 | 1586 | if button is not None: | ||
1324 | 1587 | button.clicked() | ||
1325 | 1588 | else: | ||
1326 | 1589 | logger.warning('Could not start at panel: %r.', switch_to) | ||
1329 | 1590 | 1716 | ||
1330 | 1591 | logger.debug('%s: started (window size %r).', | 1717 | logger.debug('%s: started (window size %r).', |
1331 | 1592 | self.__class__.__name__, self.get_size_request()) | 1718 | self.__class__.__name__, self.get_size_request()) |
1332 | 1593 | 1719 | ||
1333 | 1720 | def remove_urgency(self, *args, **kwargs): | ||
1334 | 1721 | """Remove urgency from the launcher entry.""" | ||
1335 | 1722 | if not USE_LIBUNITY: | ||
1336 | 1723 | return | ||
1337 | 1724 | entry = Unity.LauncherEntry.get_for_desktop_id(U1_DOTDESKTOP) | ||
1338 | 1725 | if entry.props.urgent: | ||
1339 | 1726 | self.switch_to('volumes') | ||
1340 | 1727 | entry.props.urgent = False | ||
1341 | 1728 | |||
1342 | 1729 | def draw_attention(self): | ||
1343 | 1730 | """Draw attention to the control panel.""" | ||
1344 | 1731 | self.present_with_time(1) | ||
1345 | 1732 | self.set_urgency_hint(True) | ||
1346 | 1733 | |||
1347 | 1734 | def switch_to(self, panel): | ||
1348 | 1735 | """Switch to named panel.""" | ||
1349 | 1736 | button = getattr( | ||
1350 | 1737 | self.control_panel.management, '%s_button' % panel, None) | ||
1351 | 1738 | if button is not None: | ||
1352 | 1739 | button.clicked() | ||
1353 | 1740 | else: | ||
1354 | 1741 | logger.warning('Could not start at panel: %r.', panel) | ||
1355 | 1742 | |||
1356 | 1594 | def main(self): | 1743 | def main(self): |
1357 | 1595 | """Run the main loop of the widget toolkit.""" | 1744 | """Run the main loop of the widget toolkit.""" |
1358 | 1596 | logger.debug('Starting GTK main loop.') | 1745 | logger.debug('Starting GTK main loop.') |
1359 | 1597 | 1746 | ||
1360 | === modified file 'ubuntuone/controlpanel/gtk/package_manager.py' | |||
1361 | --- ubuntuone/controlpanel/gtk/package_manager.py 2011-02-23 13:57:42 +0000 | |||
1362 | +++ ubuntuone/controlpanel/gtk/package_manager.py 2011-04-08 19:43:13 +0000 | |||
1363 | @@ -20,13 +20,14 @@ | |||
1364 | 20 | 20 | ||
1365 | 21 | import apt | 21 | import apt |
1366 | 22 | import aptdaemon.client | 22 | import aptdaemon.client |
1367 | 23 | # pylint: disable=W0404 | ||
1368 | 23 | import aptdaemon.enums | 24 | import aptdaemon.enums |
1369 | 24 | 25 | ||
1370 | 25 | try: | 26 | try: |
1372 | 26 | # Unable to import 'defer', pylint: disable=F0401,E0611 | 27 | # Unable to import 'defer', pylint: disable=F0401,E0611,W0404 |
1373 | 27 | from aptdaemon.defer import inline_callbacks, return_value | 28 | from aptdaemon.defer import inline_callbacks, return_value |
1374 | 28 | except ImportError: | 29 | except ImportError: |
1376 | 29 | # Unable to import 'defer', pylint: disable=F0401,E0611 | 30 | # Unable to import 'defer', pylint: disable=F0401,E0611,W0404 |
1377 | 30 | from defer import inline_callbacks, return_value | 31 | from defer import inline_callbacks, return_value |
1378 | 31 | from aptdaemon.gtkwidgets import AptProgressBar | 32 | from aptdaemon.gtkwidgets import AptProgressBar |
1379 | 32 | 33 | ||
1380 | 33 | 34 | ||
1381 | === modified file 'ubuntuone/controlpanel/gtk/tests/__init__.py' | |||
1382 | --- ubuntuone/controlpanel/gtk/tests/__init__.py 2011-03-10 02:59:44 +0000 | |||
1383 | +++ ubuntuone/controlpanel/gtk/tests/__init__.py 2011-04-08 19:43:13 +0000 | |||
1384 | @@ -180,10 +180,19 @@ | |||
1385 | 180 | 'replications_info', 'change_replication_settings', # replications | 180 | 'replications_info', 'change_replication_settings', # replications |
1386 | 181 | 'file_sync_status', 'enable_files', 'disable_files', # files | 181 | 'file_sync_status', 'enable_files', 'disable_files', # files |
1387 | 182 | 'connect_files', 'disconnect_files', | 182 | 'connect_files', 'disconnect_files', |
1389 | 183 | 'restart_files', 'start_files', 'stop_files', | 183 | 'restart_files', 'start_files', 'stop_files', 'shutdown', |
1390 | 184 | ] | 184 | ] |
1391 | 185 | 185 | ||
1392 | 186 | 186 | ||
1393 | 187 | class FakeControlPanelBackend(FakedDBusBackend): | ||
1394 | 188 | """Fake a Control Panel Service, act as a dbus.Interface.""" | ||
1395 | 189 | |||
1396 | 190 | bus_name = gui.DBUS_BUS_NAME_GUI | ||
1397 | 191 | object_path = gui.DBUS_PATH_GUI | ||
1398 | 192 | iface = gui.DBUS_IFACE_GUI | ||
1399 | 193 | exposed_methods = ['draw_attention', 'switch_to'] | ||
1400 | 194 | |||
1401 | 195 | |||
1402 | 187 | class FakedSessionBus(object): | 196 | class FakedSessionBus(object): |
1403 | 188 | """Fake a session bus.""" | 197 | """Fake a session bus.""" |
1404 | 189 | 198 | ||
1405 | @@ -202,6 +211,9 @@ | |||
1406 | 202 | *args, **kwargs) | 211 | *args, **kwargs) |
1407 | 203 | if dbus_interface == gui.ubuntu_sso.DBUS_CREDENTIALS_IFACE: | 212 | if dbus_interface == gui.ubuntu_sso.DBUS_CREDENTIALS_IFACE: |
1408 | 204 | return FakedSSOBackend(obj, dbus_interface, *args, **kwargs) | 213 | return FakedSSOBackend(obj, dbus_interface, *args, **kwargs) |
1409 | 214 | if dbus_interface == gui.DBUS_IFACE_GUI: | ||
1410 | 215 | return FakeControlPanelBackend( | ||
1411 | 216 | obj, dbus_interface, *args, **kwargs) | ||
1412 | 205 | 217 | ||
1413 | 206 | 218 | ||
1414 | 207 | class FakedPackageManager(object): | 219 | class FakedPackageManager(object): |
1415 | 208 | 220 | ||
1416 | === modified file 'ubuntuone/controlpanel/gtk/tests/test_gui.py' | |||
1417 | --- ubuntuone/controlpanel/gtk/tests/test_gui.py 2011-03-10 02:59:44 +0000 | |||
1418 | +++ ubuntuone/controlpanel/gtk/tests/test_gui.py 2011-04-08 19:43:13 +0000 | |||
1419 | @@ -278,6 +278,7 @@ | |||
1420 | 278 | 278 | ||
1421 | 279 | def test_clicking_on_row_opens_folder(self): | 279 | def test_clicking_on_row_opens_folder(self): |
1422 | 280 | """The folder activated is opened.""" | 280 | """The folder activated is opened.""" |
1423 | 281 | self.patch(gui.os.path, 'exists', lambda *a: True) | ||
1424 | 281 | self.patch(gui, 'uri_hook', self._set_called) | 282 | self.patch(gui, 'uri_hook', self._set_called) |
1425 | 282 | self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO) | 283 | self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO) |
1426 | 283 | 284 | ||
1427 | @@ -287,6 +288,33 @@ | |||
1428 | 287 | self.assertEqual(self._called, | 288 | self.assertEqual(self._called, |
1429 | 288 | ((None, gui.FILE_URI_PREFIX + ROOT['path']), {})) | 289 | ((None, gui.FILE_URI_PREFIX + ROOT['path']), {})) |
1430 | 289 | 290 | ||
1431 | 291 | def test_clicking_on_row_handles_path_none(self): | ||
1432 | 292 | """None paths are properly handled.""" | ||
1433 | 293 | self.patch(gui, 'uri_hook', self._set_called) | ||
1434 | 294 | self.patch(self.ui.volumes_store, 'get_value', lambda *a: None) | ||
1435 | 295 | self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO) | ||
1436 | 296 | |||
1437 | 297 | self.ui.volumes_view.row_activated('0:0', | ||
1438 | 298 | self.ui.volumes_view.get_column(0)) | ||
1439 | 299 | |||
1440 | 300 | self.assertTrue(self.memento.check_warning('tree_path (0, 0)', | ||
1441 | 301 | 'volume_path', 'is None')) | ||
1442 | 302 | self.assertEqual(self._called, False) | ||
1443 | 303 | |||
1444 | 304 | def test_clicking_on_row_handles_path_non_existent(self): | ||
1445 | 305 | """Not-existent paths are properly handled.""" | ||
1446 | 306 | self.patch(gui.os.path, 'exists', lambda *a: False) | ||
1447 | 307 | self.patch(gui, 'uri_hook', self._set_called) | ||
1448 | 308 | path = 'not-in-disk' | ||
1449 | 309 | self.patch(self.ui.volumes_store, 'get_value', lambda *a: path) | ||
1450 | 310 | self.ui.on_volumes_info_ready(FAKE_VOLUMES_INFO) | ||
1451 | 311 | |||
1452 | 312 | self.ui.volumes_view.row_activated('0:0', | ||
1453 | 313 | self.ui.volumes_view.get_column(0)) | ||
1454 | 314 | |||
1455 | 315 | self.assertTrue(self.memento.check_warning(path, 'does not exist')) | ||
1456 | 316 | self.assertEqual(self._called, False) | ||
1457 | 317 | |||
1458 | 290 | def test_on_volumes_info_ready_with_music_folder(self): | 318 | def test_on_volumes_info_ready_with_music_folder(self): |
1459 | 291 | """The volumes info is processed when ready.""" | 319 | """The volumes info is processed when ready.""" |
1460 | 292 | info = [(u'', u'147852369', [ROOT] + [MUSIC_FOLDER])] | 320 | info = [(u'', u'147852369', [ROOT] + [MUSIC_FOLDER])] |
1461 | @@ -1646,12 +1674,15 @@ | |||
1462 | 1646 | ('FileSyncStatusIdle', [self.ui.on_file_sync_status_idle]), | 1674 | ('FileSyncStatusIdle', [self.ui.on_file_sync_status_idle]), |
1463 | 1647 | ('FileSyncStatusError', [self.ui.on_file_sync_status_error]), | 1675 | ('FileSyncStatusError', [self.ui.on_file_sync_status_error]), |
1464 | 1648 | ('FilesStartError', [self.ui.on_files_start_error]), | 1676 | ('FilesStartError', [self.ui.on_files_start_error]), |
1465 | 1677 | ('FilesDisabled', [self.ui.on_file_sync_status_disabled]), | ||
1466 | 1678 | ('FilesEnabled', [self.ui.on_file_sync_status_starting]), | ||
1467 | 1649 | ) | 1679 | ) |
1468 | 1650 | for sig, handlers in matches: | 1680 | for sig, handlers in matches: |
1469 | 1651 | self.assertEqual(self.ui.backend._signals[sig], handlers) | 1681 | self.assertEqual(self.ui.backend._signals[sig], handlers) |
1470 | 1652 | 1682 | ||
1472 | 1653 | def test_file_sync_status_is_requested(self): | 1683 | def test_file_sync_status_is_requested_on_load(self): |
1473 | 1654 | """The file sync status is requested to the backend.""" | 1684 | """The file sync status is requested to the backend.""" |
1474 | 1685 | self.ui.load() | ||
1475 | 1655 | self.assert_backend_called('file_sync_status', ()) | 1686 | self.assert_backend_called('file_sync_status', ()) |
1476 | 1656 | 1687 | ||
1477 | 1657 | def test_on_file_sync_status_disabled(self): | 1688 | def test_on_file_sync_status_disabled(self): |
1478 | @@ -1882,11 +1913,32 @@ | |||
1479 | 1882 | self.assertEqual(self.ui.backend._signals['AccountInfoError'], | 1913 | self.assertEqual(self.ui.backend._signals['AccountInfoError'], |
1480 | 1883 | [self.ui.on_account_info_error]) | 1914 | [self.ui.on_account_info_error]) |
1481 | 1884 | 1915 | ||
1482 | 1916 | def test_backend_unauthorized_signal(self): | ||
1483 | 1917 | """The proper signals are connected to the backend.""" | ||
1484 | 1918 | self.assertEqual(self.ui.backend._signals['UnauthorizedError'], | ||
1485 | 1919 | [self.ui.on_unauthorized_error]) | ||
1486 | 1920 | |||
1487 | 1921 | def test_no_backend_calls_before_load(self): | ||
1488 | 1922 | """No calls are made to the backend before load() is called.""" | ||
1489 | 1923 | self.assertEqual(self.ui.backend._called, {}) | ||
1490 | 1924 | |||
1491 | 1885 | def test_account_info_is_requested_on_load(self): | 1925 | def test_account_info_is_requested_on_load(self): |
1492 | 1886 | """The account info is requested to the backend.""" | 1926 | """The account info is requested to the backend.""" |
1493 | 1887 | self.ui.load() | 1927 | self.ui.load() |
1494 | 1888 | self.assert_backend_called('account_info', ()) | 1928 | self.assert_backend_called('account_info', ()) |
1495 | 1889 | 1929 | ||
1496 | 1930 | def test_file_sync_status_info_is_requested_on_load(self): | ||
1497 | 1931 | """The file sync status info is requested to the backend.""" | ||
1498 | 1932 | self.patch(self.ui.status_label, 'load', self._set_called) | ||
1499 | 1933 | self.ui.load() | ||
1500 | 1934 | self.assertEqual(self._called, ((), {})) | ||
1501 | 1935 | |||
1502 | 1936 | def test_replications_info_is_requested_on_load(self): | ||
1503 | 1937 | """The replications info is requested to the backend.""" | ||
1504 | 1938 | self.patch(self.ui.services, 'load', self._set_called) | ||
1505 | 1939 | self.ui.load() | ||
1506 | 1940 | self.assertEqual(self._called, ((), {})) | ||
1507 | 1941 | |||
1508 | 1890 | def test_dashboard_panel_is_packed(self): | 1942 | def test_dashboard_panel_is_packed(self): |
1509 | 1891 | """The dashboard panel is packed.""" | 1943 | """The dashboard panel is packed.""" |
1510 | 1892 | self.assertIsInstance(self.ui.dashboard, gui.DashboardPanel) | 1944 | self.assertIsInstance(self.ui.dashboard, gui.DashboardPanel) |
1511 | @@ -1999,6 +2051,23 @@ | |||
1512 | 1999 | self.assertIsInstance(self.ui.status_label, gui.FileSyncStatus) | 2051 | self.assertIsInstance(self.ui.status_label, gui.FileSyncStatus) |
1513 | 2000 | self.assertIn(self.ui.status_label, self.ui.status_box.get_children()) | 2052 | self.assertIn(self.ui.status_label, self.ui.status_box.get_children()) |
1514 | 2001 | 2053 | ||
1515 | 2054 | def test_backend_file_sync_signals(self): | ||
1516 | 2055 | """The proper signals are connected to the backend.""" | ||
1517 | 2056 | self.assertEqual(self.ui.backend._signals['FilesEnabled'], | ||
1518 | 2057 | [self.ui.enable_volumes]) | ||
1519 | 2058 | self.assertEqual(self.ui.backend._signals['FilesDisabled'], | ||
1520 | 2059 | [self.ui.disable_volumes]) | ||
1521 | 2060 | |||
1522 | 2061 | def test_enable_volumes(self): | ||
1523 | 2062 | """The volumes tab is properly enabled.""" | ||
1524 | 2063 | self.ui.enable_volumes() | ||
1525 | 2064 | self.assertTrue(self.ui.volumes_button.get_sensitive()) | ||
1526 | 2065 | |||
1527 | 2066 | def test_disable_volumes(self): | ||
1528 | 2067 | """The volumes tab is properly disabled.""" | ||
1529 | 2068 | self.ui.disable_volumes() | ||
1530 | 2069 | self.assertFalse(self.ui.volumes_button.get_sensitive()) | ||
1531 | 2070 | |||
1532 | 2002 | def test_local_device_removed_is_emitted(self): | 2071 | def test_local_device_removed_is_emitted(self): |
1533 | 2003 | """Signal local-device-removed is sent when DevicesPanel emits it.""" | 2072 | """Signal local-device-removed is sent when DevicesPanel emits it.""" |
1534 | 2004 | self.ui.connect('local-device-removed', self._set_called) | 2073 | self.ui.connect('local-device-removed', self._set_called) |
1535 | @@ -2024,3 +2093,9 @@ | |||
1536 | 2024 | actual = getattr(self.ui, '%s_button' % tab).get_tooltip_text() | 2093 | actual = getattr(self.ui, '%s_button' % tab).get_tooltip_text() |
1537 | 2025 | expected = getattr(self.ui, '%s_BUTTON_TOOLTIP' % tab.upper()) | 2094 | expected = getattr(self.ui, '%s_BUTTON_TOOLTIP' % tab.upper()) |
1538 | 2026 | self.assertEqual(actual, expected) | 2095 | self.assertEqual(actual, expected) |
1539 | 2096 | |||
1540 | 2097 | def test_on_unauthorized_error(self): | ||
1541 | 2098 | """On invalid credentials, proper signal is sent.""" | ||
1542 | 2099 | self.ui.connect('unauthorized', self._set_called) | ||
1543 | 2100 | self.ui.on_unauthorized_error() | ||
1544 | 2101 | self.assertEqual(self._called, ((self.ui,), {})) | ||
1545 | 2027 | 2102 | ||
1546 | === modified file 'ubuntuone/controlpanel/gtk/tests/test_gui_basic.py' | |||
1547 | --- ubuntuone/controlpanel/gtk/tests/test_gui_basic.py 2011-03-23 16:06:41 +0000 | |||
1548 | +++ ubuntuone/controlpanel/gtk/tests/test_gui_basic.py 2011-04-08 19:43:13 +0000 | |||
1549 | @@ -29,6 +29,43 @@ | |||
1550 | 29 | # pylint: disable=W0201, W0212 | 29 | # pylint: disable=W0201, W0212 |
1551 | 30 | 30 | ||
1552 | 31 | 31 | ||
1553 | 32 | class FakeLauncherEntryProps(object): | ||
1554 | 33 | """A fake Unity.LauncherEntry.props""" | ||
1555 | 34 | |||
1556 | 35 | urgent = True | ||
1557 | 36 | |||
1558 | 37 | |||
1559 | 38 | THE_FLEP = FakeLauncherEntryProps() | ||
1560 | 39 | |||
1561 | 40 | |||
1562 | 41 | class FakeLauncherEntry(object): | ||
1563 | 42 | """A fake Unity.LauncherEntry""" | ||
1564 | 43 | |||
1565 | 44 | def __init__(self): | ||
1566 | 45 | """Initialize this fake instance.""" | ||
1567 | 46 | self.props = THE_FLEP | ||
1568 | 47 | |||
1569 | 48 | @staticmethod | ||
1570 | 49 | def get_for_desktop_id(dotdesktop): | ||
1571 | 50 | """Find the LauncherEntry for a given dotdesktop.""" | ||
1572 | 51 | return FakeLauncherEntry() | ||
1573 | 52 | |||
1574 | 53 | |||
1575 | 54 | class FakeControlPanelService(object): | ||
1576 | 55 | """Fake service.""" | ||
1577 | 56 | def __init__(self, window): | ||
1578 | 57 | self.window = window | ||
1579 | 58 | self.called = [] | ||
1580 | 59 | |||
1581 | 60 | def draw_attention(self): | ||
1582 | 61 | """Draw attention to the control panel.""" | ||
1583 | 62 | self.called.append('draw_attention') | ||
1584 | 63 | |||
1585 | 64 | def switch_to(self, panel): | ||
1586 | 65 | """Switch to named panel.""" | ||
1587 | 66 | self.called.append(('switch_to', panel)) | ||
1588 | 67 | |||
1589 | 68 | |||
1590 | 32 | class ControlPanelMixinTestCase(BaseTestCase): | 69 | class ControlPanelMixinTestCase(BaseTestCase): |
1591 | 33 | """The test suite for the control panel widget.""" | 70 | """The test suite for the control panel widget.""" |
1592 | 34 | 71 | ||
1593 | @@ -49,25 +86,30 @@ | |||
1594 | 49 | 86 | ||
1595 | 50 | klass = gui.ControlPanelWindow | 87 | klass = gui.ControlPanelWindow |
1596 | 51 | 88 | ||
1597 | 89 | def setUp(self): | ||
1598 | 90 | self.patch(gui, 'ControlPanelService', FakeControlPanelService) | ||
1599 | 91 | super(ControlPanelWindowTestCase, self).setUp() | ||
1600 | 92 | |||
1601 | 52 | def test_is_a_window(self): | 93 | def test_is_a_window(self): |
1602 | 53 | """Inherits from gtk.Window.""" | 94 | """Inherits from gtk.Window.""" |
1603 | 54 | self.assertIsInstance(self.ui, gui.gtk.Window) | 95 | self.assertIsInstance(self.ui, gui.gtk.Window) |
1604 | 55 | 96 | ||
1605 | 56 | def test_startup_visibility(self): | 97 | def test_startup_visibility(self): |
1606 | 57 | """The widget is visible at startup.""" | 98 | """The widget is visible at startup.""" |
1608 | 58 | self.assertTrue(self.ui.get_visible(), 'must be visible at startup.') | 99 | self.assertTrue(self.ui.get_visible(), 'was not visible at startup.') |
1609 | 59 | 100 | ||
1610 | 60 | def test_main_start_gtk_main_loop(self): | 101 | def test_main_start_gtk_main_loop(self): |
1611 | 61 | """The GTK main loop is started when calling main().""" | 102 | """The GTK main loop is started when calling main().""" |
1612 | 62 | self.patch(gui.gtk, 'main', self._set_called) | 103 | self.patch(gui.gtk, 'main', self._set_called) |
1613 | 63 | self.ui.main() | 104 | self.ui.main() |
1615 | 64 | self.assertEqual(self._called, ((), {}), 'gtk.main was called.') | 105 | self.assertEqual(self._called, ((), {}), 'gtk.main was not called.') |
1616 | 65 | 106 | ||
1617 | 66 | def test_closing_stops_the_main_lopp(self): | 107 | def test_closing_stops_the_main_lopp(self): |
1618 | 67 | """The GTK main loop is stopped when closing the window.""" | 108 | """The GTK main loop is stopped when closing the window.""" |
1619 | 68 | self.patch(gui.gtk, 'main_quit', self._set_called) | 109 | self.patch(gui.gtk, 'main_quit', self._set_called) |
1620 | 69 | self.ui.emit('delete-event', None) | 110 | self.ui.emit('delete-event', None) |
1622 | 70 | self.assertEqual(self._called, ((), {}), 'gtk.main_quit was called.') | 111 | self.assertEqual( |
1623 | 112 | self._called, ((), {}), 'gtk.main_quit was not called.') | ||
1624 | 71 | 113 | ||
1625 | 72 | def test_title_is_correct(self): | 114 | def test_title_is_correct(self): |
1626 | 73 | """The window title is correct.""" | 115 | """The window title is correct.""" |
1627 | @@ -96,6 +138,32 @@ | |||
1628 | 96 | """Max size is not bigger than 736x525 (LP: #645526, LP: #683164).""" | 138 | """Max size is not bigger than 736x525 (LP: #645526, LP: #683164).""" |
1629 | 97 | self.assertTrue(self.ui.get_size_request() <= (736, 525)) | 139 | self.assertTrue(self.ui.get_size_request() <= (736, 525)) |
1630 | 98 | 140 | ||
1631 | 141 | def test_focus_handler(self): | ||
1632 | 142 | """When the window receives focus, the handler is called.""" | ||
1633 | 143 | THE_FLEP.urgent = True | ||
1634 | 144 | self.patch(gui.Unity, "LauncherEntry", FakeLauncherEntry) | ||
1635 | 145 | cp = gui.ControlPanelWindow() | ||
1636 | 146 | cp.emit('focus-in-event', None) | ||
1637 | 147 | self.assertEqual( | ||
1638 | 148 | False, THE_FLEP.urgent, 'remove_urgency should have been called.') | ||
1639 | 149 | |||
1640 | 150 | |||
1641 | 151 | class ControlPanelWindowAlertParamTestCase(BaseTestCase): | ||
1642 | 152 | """The test suite for the control panel window when passing params.""" | ||
1643 | 153 | |||
1644 | 154 | klass = gui.ControlPanelWindow | ||
1645 | 155 | kwargs = {'alert': True} | ||
1646 | 156 | |||
1647 | 157 | def setUp(self): | ||
1648 | 158 | self.patch(gui, 'ControlPanelService', FakeControlPanelService) | ||
1649 | 159 | self.patch(gui.ControlPanelWindow, 'draw_attention', self._set_called) | ||
1650 | 160 | super(ControlPanelWindowAlertParamTestCase, self).setUp() | ||
1651 | 161 | |||
1652 | 162 | def test_alert(self): | ||
1653 | 163 | """Can pass a 'alert' parameter to draw attention to the window.""" | ||
1654 | 164 | self.assertEqual( | ||
1655 | 165 | ((), {}), self._called, 'draw_attention should have been called.') | ||
1656 | 166 | |||
1657 | 99 | 167 | ||
1658 | 100 | class ControlPanelWindowParamsTestCase(ControlPanelWindowTestCase): | 168 | class ControlPanelWindowParamsTestCase(ControlPanelWindowTestCase): |
1659 | 101 | """The test suite for the control panel window when passing params.""" | 169 | """The test suite for the control panel window when passing params.""" |
1660 | @@ -169,8 +237,10 @@ | |||
1661 | 169 | 237 | ||
1662 | 170 | def test_on_show_management_panel(self): | 238 | def test_on_show_management_panel(self): |
1663 | 171 | """A ManagementPanel is shown when the callback is executed.""" | 239 | """A ManagementPanel is shown when the callback is executed.""" |
1664 | 240 | self.patch(self.ui.management, 'load', self._set_called) | ||
1665 | 172 | self.ui.on_show_management_panel() | 241 | self.ui.on_show_management_panel() |
1666 | 173 | self.assert_current_tab_correct(self.ui.management) | 242 | self.assert_current_tab_correct(self.ui.management) |
1667 | 243 | self.assertEqual(self._called, ((), {})) | ||
1668 | 174 | 244 | ||
1669 | 175 | def test_on_show_management_panel_is_idempotent(self): | 245 | def test_on_show_management_panel_is_idempotent(self): |
1670 | 176 | """Only one ManagementPanel is shown.""" | 246 | """Only one ManagementPanel is shown.""" |
1671 | @@ -179,7 +249,7 @@ | |||
1672 | 179 | 249 | ||
1673 | 180 | self.assert_current_tab_correct(self.ui.management) | 250 | self.assert_current_tab_correct(self.ui.management) |
1674 | 181 | 251 | ||
1676 | 182 | def test_credentials_found_shows_dashboard_management_panel(self): | 252 | def test_credentials_found_shows_dashboard_panel(self): |
1677 | 183 | """On 'credentials-found' signal, the management panel is shown. | 253 | """On 'credentials-found' signal, the management panel is shown. |
1678 | 184 | 254 | ||
1679 | 185 | If first signal parameter is False, visible tab should be dashboard. | 255 | If first signal parameter is False, visible tab should be dashboard. |
1680 | @@ -193,7 +263,7 @@ | |||
1681 | 193 | self.ui.management.DASHBOARD_PAGE) | 263 | self.ui.management.DASHBOARD_PAGE) |
1682 | 194 | self.assertEqual(self._called, ((), {})) | 264 | self.assertEqual(self._called, ((), {})) |
1683 | 195 | 265 | ||
1685 | 196 | def test_credentials_found_shows_volumes_management_panel(self): | 266 | def test_credentials_found_shows_services_panel(self): |
1686 | 197 | """On 'credentials-found' signal, the management panel is shown. | 267 | """On 'credentials-found' signal, the management panel is shown. |
1687 | 198 | 268 | ||
1688 | 199 | If first signal parameter is True, visible tab should be services. | 269 | If first signal parameter is True, visible tab should be services. |
1689 | @@ -206,6 +276,12 @@ | |||
1690 | 206 | self.assertEqual(self.ui.management.notebook.get_current_page(), | 276 | self.assertEqual(self.ui.management.notebook.get_current_page(), |
1691 | 207 | self.ui.management.SERVICES_PAGE) | 277 | self.ui.management.SERVICES_PAGE) |
1692 | 208 | 278 | ||
1693 | 279 | def test_credentials_found_connects_syncdaemon(self): | ||
1694 | 280 | """On 'credentials-found' signal, ask syncdaemon to connect.""" | ||
1695 | 281 | # credentials are new | ||
1696 | 282 | self.ui.overview.emit('credentials-found', True, object()) | ||
1697 | 283 | self.assert_backend_called('connect_files', ()) | ||
1698 | 284 | |||
1699 | 209 | def test_local_device_removed_shows_overview_panel(self): | 285 | def test_local_device_removed_shows_overview_panel(self): |
1700 | 210 | """On 'local-device-removed' signal, the overview panel is shown.""" | 286 | """On 'local-device-removed' signal, the overview panel is shown.""" |
1701 | 211 | self.ui.overview.emit('credentials-found', True, object()) | 287 | self.ui.overview.emit('credentials-found', True, object()) |
1702 | @@ -213,6 +289,18 @@ | |||
1703 | 213 | 289 | ||
1704 | 214 | self.assert_current_tab_correct(self.ui.overview) | 290 | self.assert_current_tab_correct(self.ui.overview) |
1705 | 215 | 291 | ||
1706 | 292 | def test_unauthorized_shows_overview_panel(self): | ||
1707 | 293 | """On 'unauthorized' signal, the overview panel is shown.""" | ||
1708 | 294 | self.ui.overview.emit('credentials-found', True, object()) | ||
1709 | 295 | self.ui.management.emit('unauthorized') | ||
1710 | 296 | |||
1711 | 297 | self.assert_current_tab_correct(self.ui.overview) | ||
1712 | 298 | |||
1713 | 299 | def test_backend_is_shutdown_on_close(self): | ||
1714 | 300 | """When the control panel is closed, the backend is shutdown.""" | ||
1715 | 301 | self.ui.emit('destroy') | ||
1716 | 302 | self.assert_backend_called('shutdown', ()) | ||
1717 | 303 | |||
1718 | 216 | 304 | ||
1719 | 217 | class UbuntuOneBinTestCase(BaseTestCase): | 305 | class UbuntuOneBinTestCase(BaseTestCase): |
1720 | 218 | """The test suite for a Ubuntu One panel.""" | 306 | """The test suite for a Ubuntu One panel.""" |
1721 | @@ -276,6 +364,42 @@ | |||
1722 | 276 | self.assert_warning_correct(self.ui.message, msg) | 364 | self.assert_warning_correct(self.ui.message, msg) |
1723 | 277 | self.assertFalse(self.ui.message.active) | 365 | self.assertFalse(self.ui.message.active) |
1724 | 278 | 366 | ||
1725 | 367 | def test_on_error_with_error_dict(self): | ||
1726 | 368 | """Callback to stop the Loading and show the error from error_dict.""" | ||
1727 | 369 | msg = u'Qué mala suerte! <i>this does not rock</i>' | ||
1728 | 370 | error_dict = {gui.ERROR_TYPE: 'YaddaError', gui.ERROR_MESSAGE: msg} | ||
1729 | 371 | self.ui.on_error(error_dict=error_dict) | ||
1730 | 372 | |||
1731 | 373 | expected_msg = "%s (%s: %s)" % (gui.VALUE_ERROR, 'YaddaError', msg) | ||
1732 | 374 | self.assert_warning_correct(self.ui.message, expected_msg) | ||
1733 | 375 | self.assertFalse(self.ui.message.active) | ||
1734 | 376 | |||
1735 | 377 | def test_on_error_with_error_dict_without_error_type(self): | ||
1736 | 378 | """Callback to stop the Loading and show the error from error_dict.""" | ||
1737 | 379 | error_dict = {} | ||
1738 | 380 | self.ui.on_error(error_dict=error_dict) | ||
1739 | 381 | |||
1740 | 382 | expected_msg = "%s (%s)" % (gui.VALUE_ERROR, gui.UNKNOWN_ERROR) | ||
1741 | 383 | self.assert_warning_correct(self.ui.message, expected_msg) | ||
1742 | 384 | self.assertFalse(self.ui.message.active) | ||
1743 | 385 | |||
1744 | 386 | def test_on_error_with_error_dict_without_error_message(self): | ||
1745 | 387 | """Callback to stop the Loading and show the error from error_dict.""" | ||
1746 | 388 | error_dict = {gui.ERROR_TYPE: 'YaddaError'} | ||
1747 | 389 | self.ui.on_error(error_dict=error_dict) | ||
1748 | 390 | |||
1749 | 391 | expected_msg = "%s (%s)" % (gui.VALUE_ERROR, 'YaddaError') | ||
1750 | 392 | self.assert_warning_correct(self.ui.message, expected_msg) | ||
1751 | 393 | self.assertFalse(self.ui.message.active) | ||
1752 | 394 | |||
1753 | 395 | def test_on_error_with_message_and_error_dict(self): | ||
1754 | 396 | """Callback to stop the Loading and show a info message.""" | ||
1755 | 397 | error_dict = {gui.ERROR_TYPE: 'YaddaError', gui.ERROR_MESSAGE: 'test'} | ||
1756 | 398 | msg = 'WOW! <i>this does not rock</i> :-/' | ||
1757 | 399 | self.ui.on_error(message=msg, error_dict=error_dict) | ||
1758 | 400 | self.assert_warning_correct(self.ui.message, msg) | ||
1759 | 401 | self.assertFalse(self.ui.message.active) | ||
1760 | 402 | |||
1761 | 279 | def test_is_processing(self): | 403 | def test_is_processing(self): |
1762 | 280 | """The flag 'is_processing' is False on start.""" | 404 | """The flag 'is_processing' is False on start.""" |
1763 | 281 | self.assertFalse(self.ui.is_processing) | 405 | self.assertFalse(self.ui.is_processing) |
1764 | 282 | 406 | ||
1765 | === modified file 'ubuntuone/controlpanel/gtk/tests/test_package_manager.py' | |||
1766 | --- ubuntuone/controlpanel/gtk/tests/test_package_manager.py 2011-01-07 20:07:39 +0000 | |||
1767 | +++ ubuntuone/controlpanel/gtk/tests/test_package_manager.py 2011-04-08 19:43:13 +0000 | |||
1768 | @@ -21,10 +21,10 @@ | |||
1769 | 21 | import collections | 21 | import collections |
1770 | 22 | 22 | ||
1771 | 23 | try: | 23 | try: |
1773 | 24 | # Unable to import 'defer', pylint: disable=F0401,E0611 | 24 | # Unable to import 'defer', pylint: disable=F0401,E0611,W0404 |
1774 | 25 | from aptdaemon import defer | 25 | from aptdaemon import defer |
1775 | 26 | except ImportError: | 26 | except ImportError: |
1777 | 27 | # Unable to import 'defer', pylint: disable=F0401,E0611 | 27 | # Unable to import 'defer', pylint: disable=F0401,E0611,W0404 |
1778 | 28 | import defer | 28 | import defer |
1779 | 29 | 29 | ||
1780 | 30 | from ubuntuone.controlpanel.gtk import package_manager | 30 | from ubuntuone.controlpanel.gtk import package_manager |
1781 | 31 | 31 | ||
1782 | === modified file 'ubuntuone/controlpanel/integrationtests/__init__.py' | |||
1783 | --- ubuntuone/controlpanel/integrationtests/__init__.py 2010-12-22 13:33:25 +0000 | |||
1784 | +++ ubuntuone/controlpanel/integrationtests/__init__.py 2011-04-08 19:43:13 +0000 | |||
1785 | @@ -20,7 +20,6 @@ | |||
1786 | 20 | """Integration tests for the Ubuntu One Control Panel.""" | 20 | """Integration tests for the Ubuntu One Control Panel.""" |
1787 | 21 | 21 | ||
1788 | 22 | import dbus | 22 | import dbus |
1789 | 23 | import dbus.service | ||
1790 | 24 | 23 | ||
1791 | 25 | from ubuntuone.devtools.testcase import DBusTestCase as TestCase | 24 | from ubuntuone.devtools.testcase import DBusTestCase as TestCase |
1792 | 26 | 25 | ||
1793 | @@ -31,7 +30,7 @@ | |||
1794 | 31 | """A DBus exception to be used in tests.""" | 30 | """A DBus exception to be used in tests.""" |
1795 | 32 | 31 | ||
1796 | 33 | 32 | ||
1798 | 34 | class MockDBusNoMethods(dbus.service.Object): | 33 | class MockDBusNoMethods(dbus_service.dbus.service.Object): |
1799 | 35 | """A mock that fails at the DBus layer (because it's got no methods!).""" | 34 | """A mock that fails at the DBus layer (because it's got no methods!).""" |
1800 | 36 | 35 | ||
1801 | 37 | 36 | ||
1802 | 38 | 37 | ||
1803 | === modified file 'ubuntuone/controlpanel/integrationtests/test_dbus_service.py' | |||
1804 | --- ubuntuone/controlpanel/integrationtests/test_dbus_service.py 2011-02-23 13:57:42 +0000 | |||
1805 | +++ ubuntuone/controlpanel/integrationtests/test_dbus_service.py 2011-04-08 19:43:13 +0000 | |||
1806 | @@ -84,12 +84,23 @@ | |||
1807 | 84 | rs = self.mocker.replace(rs_name) | 84 | rs = self.mocker.replace(rs_name) |
1808 | 85 | rs() | 85 | rs() |
1809 | 86 | self.mocker.result(True) | 86 | self.mocker.result(True) |
1810 | 87 | |||
1811 | 88 | mainloop = "ubuntuone.controlpanel.dbus_service.gobject.MainLoop" | ||
1812 | 89 | mainloop = self.mocker.replace(mainloop) | ||
1813 | 90 | mainloop() | ||
1814 | 91 | loop = self.mocker.mock() | ||
1815 | 92 | self.mocker.result(loop) | ||
1816 | 93 | |||
1817 | 94 | shutdown_func = self.mocker.mock() | ||
1818 | 95 | loop.quit # pylint: disable=W0104 | ||
1819 | 96 | self.mocker.result(shutdown_func) | ||
1820 | 97 | |||
1821 | 87 | rml_name = "ubuntuone.controlpanel.dbus_service.run_mainloop" | 98 | rml_name = "ubuntuone.controlpanel.dbus_service.run_mainloop" |
1822 | 88 | rml = self.mocker.replace(rml_name) | 99 | rml = self.mocker.replace(rml_name) |
1824 | 89 | rml() | 100 | rml(loop=loop) |
1825 | 90 | pb_name = "ubuntuone.controlpanel.dbus_service.publish_backend" | 101 | pb_name = "ubuntuone.controlpanel.dbus_service.publish_backend" |
1826 | 91 | pb = self.mocker.replace(pb_name) | 102 | pb = self.mocker.replace(pb_name) |
1828 | 92 | pb() | 103 | pb(shutdown_func=shutdown_func) |
1829 | 93 | self.mocker.replay() | 104 | self.mocker.replay() |
1830 | 94 | dbus_service.main() | 105 | dbus_service.main() |
1831 | 95 | 106 | ||
1832 | @@ -112,6 +123,10 @@ | |||
1833 | 112 | dbus_service.STATUS_KEY: dbus_service.FILE_SYNC_IDLE, | 123 | dbus_service.STATUS_KEY: dbus_service.FILE_SYNC_IDLE, |
1834 | 113 | } | 124 | } |
1835 | 114 | status_changed_handler = None | 125 | status_changed_handler = None |
1836 | 126 | shutdown_func = None | ||
1837 | 127 | |||
1838 | 128 | def __init__(self, shutdown_func=None): | ||
1839 | 129 | MockBackend.shutdown_func = shutdown_func | ||
1840 | 115 | 130 | ||
1841 | 116 | def _process(self, result): | 131 | def _process(self, result): |
1842 | 117 | """Process the request with the given result.""" | 132 | """Process the request with the given result.""" |
1843 | @@ -197,6 +212,10 @@ | |||
1844 | 197 | """Install the extension to sync bookmarks.""" | 212 | """Install the extension to sync bookmarks.""" |
1845 | 198 | return self._process(None) | 213 | return self._process(None) |
1846 | 199 | 214 | ||
1847 | 215 | def shutdown(self): | ||
1848 | 216 | """Stop this service.""" | ||
1849 | 217 | self.shutdown_func() | ||
1850 | 218 | |||
1851 | 200 | 219 | ||
1852 | 201 | class DBusServiceTestCase(TestCase): | 220 | class DBusServiceTestCase(TestCase): |
1853 | 202 | """Test for the DBus service.""" | 221 | """Test for the DBus service.""" |
1854 | @@ -289,7 +308,8 @@ | |||
1855 | 289 | def setUp(self): | 308 | def setUp(self): |
1856 | 290 | super(BaseTestCase, self).setUp() | 309 | super(BaseTestCase, self).setUp() |
1857 | 291 | dbus_service.init_mainloop() | 310 | dbus_service.init_mainloop() |
1859 | 292 | be = dbus_service.publish_backend(MockBackend()) | 311 | self.patch(dbus_service, 'ControlBackend', MockBackend) |
1860 | 312 | be = dbus_service.publish_backend() | ||
1861 | 293 | self.addCleanup(be.remove_from_connection) | 313 | self.addCleanup(be.remove_from_connection) |
1862 | 294 | bus = dbus.SessionBus() | 314 | bus = dbus.SessionBus() |
1863 | 295 | obj = bus.get_object(bus_name=DBUS_BUS_NAME, | 315 | obj = bus.get_object(bus_name=DBUS_BUS_NAME, |
1864 | @@ -604,6 +624,35 @@ | |||
1865 | 604 | error_sig, success_sig, got_error_signal, method, *args) | 624 | error_sig, success_sig, got_error_signal, method, *args) |
1866 | 605 | 625 | ||
1867 | 606 | 626 | ||
1868 | 627 | class OperationsAuthErrorTestCase(OperationsTestCase): | ||
1869 | 628 | """Test for the DBus service operations when UnauthorizedError happens.""" | ||
1870 | 629 | |||
1871 | 630 | def setUp(self): | ||
1872 | 631 | super(OperationsAuthErrorTestCase, self).setUp() | ||
1873 | 632 | self.patch(MockBackend, 'exception', | ||
1874 | 633 | dbus_service.UnauthorizedError) | ||
1875 | 634 | |||
1876 | 635 | def assert_correct_method_call(self, success_sig, error_sig, success_cb, | ||
1877 | 636 | method, *args): | ||
1878 | 637 | """Call parent instance expecting UnauthorizedError signal.""" | ||
1879 | 638 | |||
1880 | 639 | def inner_success_cb(*a): | ||
1881 | 640 | """The success signal was received.""" | ||
1882 | 641 | if len(a) == 1: | ||
1883 | 642 | error_dict = a[0] | ||
1884 | 643 | else: | ||
1885 | 644 | an_id, error_dict = a | ||
1886 | 645 | self.assertEqual(an_id, args[0]) | ||
1887 | 646 | |||
1888 | 647 | self.assertEqual(error_dict[dbus_service.ERROR_TYPE], | ||
1889 | 648 | 'UnauthorizedError') | ||
1890 | 649 | self.deferred.callback('success') | ||
1891 | 650 | |||
1892 | 651 | parent = super(OperationsAuthErrorTestCase, self) | ||
1893 | 652 | return parent.assert_correct_method_call( | ||
1894 | 653 | "UnauthorizedError", error_sig, inner_success_cb, method, *args) | ||
1895 | 654 | |||
1896 | 655 | |||
1897 | 607 | class FileSyncTestCase(BaseTestCase): | 656 | class FileSyncTestCase(BaseTestCase): |
1898 | 608 | """Test for the DBus service when requesting file sync status.""" | 657 | """Test for the DBus service when requesting file sync status.""" |
1899 | 609 | 658 | ||
1900 | @@ -691,3 +740,18 @@ | |||
1901 | 691 | cpbe = dbus_service.ControlPanelBackend(backend=be) | 740 | cpbe = dbus_service.ControlPanelBackend(backend=be) |
1902 | 692 | 741 | ||
1903 | 693 | self.assertEqual(be.status_changed_handler, cpbe.process_status) | 742 | self.assertEqual(be.status_changed_handler, cpbe.process_status) |
1904 | 743 | |||
1905 | 744 | |||
1906 | 745 | class ShutdownTestCase(BaseTestCase): | ||
1907 | 746 | """Test for the DBus service shurdown.""" | ||
1908 | 747 | |||
1909 | 748 | @defer.inlineCallbacks | ||
1910 | 749 | def test_shutdown(self): | ||
1911 | 750 | """The service can be shutdown.""" | ||
1912 | 751 | called = [] | ||
1913 | 752 | MockBackend.shutdown_func = lambda *a: called.append('shutdown') | ||
1914 | 753 | self.backend.shutdown(reply_handler=lambda: self.deferred.callback(1), | ||
1915 | 754 | error_handler=self.got_error) | ||
1916 | 755 | yield self.deferred | ||
1917 | 756 | |||
1918 | 757 | self.assertEqual(called, ['shutdown']) | ||
1919 | 694 | 758 | ||
1920 | === added file 'ubuntuone/controlpanel/integrationtests/test_gui_service.py' | |||
1921 | --- ubuntuone/controlpanel/integrationtests/test_gui_service.py 1970-01-01 00:00:00 +0000 | |||
1922 | +++ ubuntuone/controlpanel/integrationtests/test_gui_service.py 2011-04-08 19:43:13 +0000 | |||
1923 | @@ -0,0 +1,98 @@ | |||
1924 | 1 | # -*- coding: utf-8 -*- | ||
1925 | 2 | |||
1926 | 3 | # Authors: Alejandro J. Cura <alecu@canonical.com> | ||
1927 | 4 | # Natalia B. Bidart <nataliabidart@canonical.com> | ||
1928 | 5 | # Eric Casteleijn <eric.casteleijn@canonical.com> | ||
1929 | 6 | # | ||
1930 | 7 | # Copyright 2011 Canonical Ltd. | ||
1931 | 8 | # | ||
1932 | 9 | # This program is free software: you can redistribute it and/or modify it | ||
1933 | 10 | # under the terms of the GNU General Public License version 3, as published | ||
1934 | 11 | # by the Free Software Foundation. | ||
1935 | 12 | # | ||
1936 | 13 | # This program is distributed in the hope that it will be useful, but | ||
1937 | 14 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
1938 | 15 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
1939 | 16 | # PURPOSE. See the GNU General Public License for more details. | ||
1940 | 17 | # | ||
1941 | 18 | # You should have received a copy of the GNU General Public License along | ||
1942 | 19 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1943 | 20 | |||
1944 | 21 | """Tests for the control panel backend DBus service.""" | ||
1945 | 22 | |||
1946 | 23 | import dbus | ||
1947 | 24 | import mocker | ||
1948 | 25 | |||
1949 | 26 | from dbus.mainloop.glib import DBusGMainLoop | ||
1950 | 27 | |||
1951 | 28 | from ubuntuone.controlpanel.gtk import gui | ||
1952 | 29 | from ubuntuone.devtools.testcase import DBusTestCase | ||
1953 | 30 | from twisted.trial.unittest import TestCase | ||
1954 | 31 | |||
1955 | 32 | |||
1956 | 33 | class MockWindow(object): | ||
1957 | 34 | """A mock backend.""" | ||
1958 | 35 | |||
1959 | 36 | exception = None | ||
1960 | 37 | |||
1961 | 38 | def __init__(self, switch_to=None, alert=False): | ||
1962 | 39 | self.called = [] | ||
1963 | 40 | |||
1964 | 41 | def draw_attention(self): | ||
1965 | 42 | """Draw attention to the control panel.""" | ||
1966 | 43 | self.called.append('draw_attention') | ||
1967 | 44 | |||
1968 | 45 | def switch_to(self, panel): | ||
1969 | 46 | """Switch to named panel.""" | ||
1970 | 47 | self.called.append(('switch_to', panel)) | ||
1971 | 48 | |||
1972 | 49 | |||
1973 | 50 | class DBusServiceMockTestCase(TestCase): | ||
1974 | 51 | """Tests for the main function.""" | ||
1975 | 52 | |||
1976 | 53 | def setUp(self): | ||
1977 | 54 | self.mocker = mocker.Mocker() | ||
1978 | 55 | |||
1979 | 56 | def tearDown(self): | ||
1980 | 57 | self.mocker.restore() | ||
1981 | 58 | self.mocker.verify() | ||
1982 | 59 | |||
1983 | 60 | def test_dbus_service_main(self): | ||
1984 | 61 | """The main method starts the loop and hooks up to DBus.""" | ||
1985 | 62 | self.patch(gui, 'ControlPanelWindow', MockWindow) | ||
1986 | 63 | dbus_gmain_loop = self.mocker.replace( | ||
1987 | 64 | "dbus.mainloop.glib.DBusGMainLoop") | ||
1988 | 65 | register_service = self.mocker.replace( | ||
1989 | 66 | "ubuntuone.controlpanel.gtk.gui.register_service") | ||
1990 | 67 | publish_service = self.mocker.replace( | ||
1991 | 68 | "ubuntuone.controlpanel.gtk.gui.publish_service") | ||
1992 | 69 | main = self.mocker.replace("gtk.main") | ||
1993 | 70 | dbus_gmain_loop(set_as_default=True) | ||
1994 | 71 | loop = self.mocker.mock() | ||
1995 | 72 | self.mocker.result(loop) | ||
1996 | 73 | register_service(mocker.ANY) | ||
1997 | 74 | self.mocker.result(True) | ||
1998 | 75 | publish_service(switch_to='', alert=False) | ||
1999 | 76 | main() | ||
2000 | 77 | self.mocker.replay() | ||
2001 | 78 | gui.main() | ||
2002 | 79 | |||
2003 | 80 | |||
2004 | 81 | class DBusServiceTestCase(DBusTestCase): | ||
2005 | 82 | """Test for the DBus service.""" | ||
2006 | 83 | |||
2007 | 84 | def _set_called(self, *args, **kwargs): | ||
2008 | 85 | """Keep track of function calls, useful for monkeypatching.""" | ||
2009 | 86 | self._called = (args, kwargs) | ||
2010 | 87 | |||
2011 | 88 | def setUp(self): | ||
2012 | 89 | """Initialize each test run.""" | ||
2013 | 90 | super(DBusServiceTestCase, self).setUp() | ||
2014 | 91 | DBusGMainLoop(set_as_default=True) | ||
2015 | 92 | self._called = False | ||
2016 | 93 | |||
2017 | 94 | def test_register_service(self): | ||
2018 | 95 | """The DBus service is successfully registered.""" | ||
2019 | 96 | bus = dbus.SessionBus() | ||
2020 | 97 | ret = gui.register_service(bus) | ||
2021 | 98 | self.assertTrue(ret) | ||
2022 | 0 | 99 | ||
2023 | === modified file 'ubuntuone/controlpanel/integrationtests/test_webclient.py' | |||
2024 | --- ubuntuone/controlpanel/integrationtests/test_webclient.py 2010-12-06 12:27:11 +0000 | |||
2025 | +++ ubuntuone/controlpanel/integrationtests/test_webclient.py 2011-04-08 19:43:13 +0000 | |||
2026 | @@ -73,6 +73,10 @@ | |||
2027 | 73 | devices_resource.contents = SAMPLE_RESOURCE | 73 | devices_resource.contents = SAMPLE_RESOURCE |
2028 | 74 | root.putChild("devices", devices_resource) | 74 | root.putChild("devices", devices_resource) |
2029 | 75 | root.putChild("throwerror", resource.NoResource()) | 75 | root.putChild("throwerror", resource.NoResource()) |
2030 | 76 | unauthorized = resource.ErrorPage(resource.http.UNAUTHORIZED, | ||
2031 | 77 | "Unauthrorized", "Unauthrorized") | ||
2032 | 78 | root.putChild("unauthorized", unauthorized) | ||
2033 | 79 | |||
2034 | 76 | site = server.Site(root) | 80 | site = server.Site(root) |
2035 | 77 | application = service.Application('web') | 81 | application = service.Application('web') |
2036 | 78 | self.service_collection = service.IServiceCollection(application) | 82 | self.service_collection = service.IServiceCollection(application) |
2037 | @@ -96,7 +100,7 @@ | |||
2038 | 96 | class WebClientTestCase(TestCase): | 100 | class WebClientTestCase(TestCase): |
2039 | 97 | """Test for the webservice client.""" | 101 | """Test for the webservice client.""" |
2040 | 98 | 102 | ||
2042 | 99 | timeout = 5 | 103 | timeout = 8 |
2043 | 100 | 104 | ||
2044 | 101 | def setUp(self): | 105 | def setUp(self): |
2045 | 102 | super(WebClientTestCase, self).setUp() | 106 | super(WebClientTestCase, self).setUp() |
2046 | @@ -123,6 +127,12 @@ | |||
2047 | 123 | yield self.assertFailure(self.wc.call_api("throwerror"), | 127 | yield self.assertFailure(self.wc.call_api("throwerror"), |
2048 | 124 | webclient.WebClientError) | 128 | webclient.WebClientError) |
2049 | 125 | 129 | ||
2050 | 130 | @inlineCallbacks | ||
2051 | 131 | def test_unauthorized(self): | ||
2052 | 132 | """Detect when a request failed with UNAUTHORIZED.""" | ||
2053 | 133 | yield self.assertFailure(self.wc.call_api("unauthorized"), | ||
2054 | 134 | webclient.UnauthorizedError) | ||
2055 | 135 | |||
2056 | 126 | 136 | ||
2057 | 127 | class OAuthTestCase(TestCase): | 137 | class OAuthTestCase(TestCase): |
2058 | 128 | """Test for the oauth signing code.""" | 138 | """Test for the oauth signing code.""" |
2059 | 129 | 139 | ||
2060 | === modified file 'ubuntuone/controlpanel/replication_client.py' | |||
2061 | --- ubuntuone/controlpanel/replication_client.py 2011-01-07 20:07:39 +0000 | |||
2062 | +++ ubuntuone/controlpanel/replication_client.py 2011-04-08 19:43:13 +0000 | |||
2063 | @@ -57,7 +57,7 @@ | |||
2064 | 57 | if replication_module is None: | 57 | if replication_module is None: |
2065 | 58 | # delay import in case DC is not installed at module import time | 58 | # delay import in case DC is not installed at module import time |
2066 | 59 | # Unable to import 'desktopcouch.application.replication_services' | 59 | # Unable to import 'desktopcouch.application.replication_services' |
2068 | 60 | # pylint: disable=F0401 | 60 | # pylint: disable=W0404 |
2069 | 61 | from desktopcouch.application.replication_services \ | 61 | from desktopcouch.application.replication_services \ |
2070 | 62 | import ubuntuone as replication_module | 62 | import ubuntuone as replication_module |
2071 | 63 | try: | 63 | try: |
2072 | 64 | 64 | ||
2073 | === modified file 'ubuntuone/controlpanel/tests/__init__.py' | |||
2074 | --- ubuntuone/controlpanel/tests/__init__.py 2011-03-10 02:59:44 +0000 | |||
2075 | +++ ubuntuone/controlpanel/tests/__init__.py 2011-04-08 19:43:13 +0000 | |||
2076 | @@ -23,7 +23,7 @@ | |||
2077 | 23 | 23 | ||
2078 | 24 | TOKEN = {u'consumer_key': u'xQ7xDAz', | 24 | TOKEN = {u'consumer_key': u'xQ7xDAz', |
2079 | 25 | u'consumer_secret': u'KzCJWCTNbbntwfyCKKjomJDzlgqxLy', | 25 | u'consumer_secret': u'KzCJWCTNbbntwfyCKKjomJDzlgqxLy', |
2081 | 26 | u'token_name': u'test', | 26 | u'name': u'Ubuntu One @ localhost', |
2082 | 27 | u'token': u'ABCDEF01234-localtoken', | 27 | u'token': u'ABCDEF01234-localtoken', |
2083 | 28 | u'token_secret': u'qFYImEtlczPbsCnYyuwLoPDlPEnvNcIktZphPQklAWrvyfFMV'} | 28 | u'token_secret': u'qFYImEtlczPbsCnYyuwLoPDlPEnvNcIktZphPQklAWrvyfFMV'} |
2084 | 29 | 29 | ||
2085 | @@ -110,19 +110,31 @@ | |||
2086 | 110 | ] | 110 | ] |
2087 | 111 | """ | 111 | """ |
2088 | 112 | 112 | ||
2089 | 113 | EMPTY_DESCRIPTION_JSON = """ | ||
2090 | 114 | [ | ||
2091 | 115 | { | ||
2092 | 116 | "token": "ABCDEF01234token", | ||
2093 | 117 | "description": null, | ||
2094 | 118 | "kind": "Computer" | ||
2095 | 119 | } | ||
2096 | 120 | ] | ||
2097 | 121 | """ | ||
2098 | 122 | |||
2099 | 123 | LOCAL_DEVICE = { | ||
2100 | 124 | 'is_local': 'True', | ||
2101 | 125 | 'configurable': 'True', | ||
2102 | 126 | 'device_id': 'ComputerABCDEF01234-localtoken', | ||
2103 | 127 | 'limit_bandwidth': '', | ||
2104 | 128 | 'show_all_notifications': 'True', | ||
2105 | 129 | 'max_download_speed': '-1', | ||
2106 | 130 | 'max_upload_speed': '-1', | ||
2107 | 131 | 'name': 'Ubuntu One @ localhost', | ||
2108 | 132 | 'type': 'Computer', | ||
2109 | 133 | } | ||
2110 | 134 | |||
2111 | 113 | # note that local computer should be first, do not change! | 135 | # note that local computer should be first, do not change! |
2112 | 114 | EXPECTED_DEVICES_INFO = [ | 136 | EXPECTED_DEVICES_INFO = [ |
2124 | 115 | { | 137 | LOCAL_DEVICE, |
2114 | 116 | 'is_local': 'True', | ||
2115 | 117 | 'configurable': 'True', | ||
2116 | 118 | 'device_id': 'ComputerABCDEF01234-localtoken', | ||
2117 | 119 | 'limit_bandwidth': '', | ||
2118 | 120 | 'show_all_notifications': 'True', | ||
2119 | 121 | 'max_download_speed': '-1', | ||
2120 | 122 | 'max_upload_speed': '-1', | ||
2121 | 123 | 'name': 'Ubuntu One @ localhost', | ||
2122 | 124 | 'type': 'Computer', | ||
2123 | 125 | }, | ||
2125 | 126 | { | 138 | { |
2126 | 127 | "device_id": "ComputerABCDEF01234token", | 139 | "device_id": "ComputerABCDEF01234token", |
2127 | 128 | "name": "Ubuntu One @ darkstar", | 140 | "name": "Ubuntu One @ darkstar", |
2128 | 129 | 141 | ||
2129 | === modified file 'ubuntuone/controlpanel/tests/test_backend.py' | |||
2130 | --- ubuntuone/controlpanel/tests/test_backend.py 2011-03-10 02:59:44 +0000 | |||
2131 | +++ ubuntuone/controlpanel/tests/test_backend.py 2011-04-08 19:43:13 +0000 | |||
2132 | @@ -30,6 +30,7 @@ | |||
2133 | 30 | from ubuntuone.controlpanel import backend, replication_client | 30 | from ubuntuone.controlpanel import backend, replication_client |
2134 | 31 | from ubuntuone.controlpanel.backend import (bool_str, | 31 | from ubuntuone.controlpanel.backend import (bool_str, |
2135 | 32 | ACCOUNT_API, DEVICES_API, DEVICE_REMOVE_API, QUOTA_API, | 32 | ACCOUNT_API, DEVICES_API, DEVICE_REMOVE_API, QUOTA_API, |
2136 | 33 | DEVICE_TYPE_COMPUTER, | ||
2137 | 33 | FILE_SYNC_DISABLED, | 34 | FILE_SYNC_DISABLED, |
2138 | 34 | FILE_SYNC_DISCONNECTED, | 35 | FILE_SYNC_DISCONNECTED, |
2139 | 35 | FILE_SYNC_ERROR, | 36 | FILE_SYNC_ERROR, |
2140 | @@ -41,9 +42,11 @@ | |||
2141 | 41 | MSG_KEY, STATUS_KEY, | 42 | MSG_KEY, STATUS_KEY, |
2142 | 42 | ) | 43 | ) |
2143 | 43 | from ubuntuone.controlpanel.tests import (TestCase, | 44 | from ubuntuone.controlpanel.tests import (TestCase, |
2144 | 45 | EMPTY_DESCRIPTION_JSON, | ||
2145 | 44 | EXPECTED_ACCOUNT_INFO, | 46 | EXPECTED_ACCOUNT_INFO, |
2146 | 45 | EXPECTED_ACCOUNT_INFO_WITH_CURRENT_PLAN, | 47 | EXPECTED_ACCOUNT_INFO_WITH_CURRENT_PLAN, |
2147 | 46 | EXPECTED_DEVICES_INFO, | 48 | EXPECTED_DEVICES_INFO, |
2148 | 49 | LOCAL_DEVICE, | ||
2149 | 47 | ROOT_PATH, | 50 | ROOT_PATH, |
2150 | 48 | SAMPLE_ACCOUNT_NO_CURRENT_PLAN, | 51 | SAMPLE_ACCOUNT_NO_CURRENT_PLAN, |
2151 | 49 | SAMPLE_ACCOUNT_WITH_CURRENT_PLAN, | 52 | SAMPLE_ACCOUNT_WITH_CURRENT_PLAN, |
2152 | @@ -56,7 +59,6 @@ | |||
2153 | 56 | SHARES_PATH_LINK, | 59 | SHARES_PATH_LINK, |
2154 | 57 | TOKEN, | 60 | TOKEN, |
2155 | 58 | ) | 61 | ) |
2156 | 59 | from ubuntuone.controlpanel.webclient import WebClientError | ||
2157 | 60 | 62 | ||
2158 | 61 | 63 | ||
2159 | 62 | class MockWebClient(object): | 64 | class MockWebClient(object): |
2160 | @@ -70,8 +72,10 @@ | |||
2161 | 70 | 72 | ||
2162 | 71 | def call_api(self, method): | 73 | def call_api(self, method): |
2163 | 72 | """Get a given url from the webservice.""" | 74 | """Get a given url from the webservice.""" |
2166 | 73 | if self.failure: | 75 | if self.failure == 401: |
2167 | 74 | return defer.fail(WebClientError(self.failure)) | 76 | return defer.fail(backend.UnauthorizedError(self.failure)) |
2168 | 77 | elif self.failure: | ||
2169 | 78 | return defer.fail(backend.WebClientError(self.failure)) | ||
2170 | 75 | else: | 79 | else: |
2171 | 76 | result = simplejson.loads(self.results[method]) | 80 | result = simplejson.loads(self.results[method]) |
2172 | 77 | return defer.succeed(result) | 81 | return defer.succeed(result) |
2173 | @@ -250,7 +254,7 @@ | |||
2174 | 250 | self.patch(backend, "WebClient", MockWebClient) | 254 | self.patch(backend, "WebClient", MockWebClient) |
2175 | 251 | self.patch(backend, "dbus_client", MockDBusClient()) | 255 | self.patch(backend, "dbus_client", MockDBusClient()) |
2176 | 252 | self.patch(backend, "replication_client", MockReplicationClient()) | 256 | self.patch(backend, "replication_client", MockReplicationClient()) |
2178 | 253 | self.local_token = "Computer" + TOKEN["token"] | 257 | self.local_token = DEVICE_TYPE_COMPUTER + TOKEN["token"] |
2179 | 254 | self.be = backend.ControlBackend() | 258 | self.be = backend.ControlBackend() |
2180 | 255 | 259 | ||
2181 | 256 | self.memento = MementoHandler() | 260 | self.memento = MementoHandler() |
2182 | @@ -278,6 +282,24 @@ | |||
2183 | 278 | result = yield self.be.device_is_local(did) | 282 | result = yield self.be.device_is_local(did) |
2184 | 279 | self.assertFalse(result) | 283 | self.assertFalse(result) |
2185 | 280 | 284 | ||
2186 | 285 | def test_shutdown_func(self): | ||
2187 | 286 | """A shutdown_func can be passed as creation parameter.""" | ||
2188 | 287 | f = lambda: None | ||
2189 | 288 | be = backend.ControlBackend(shutdown_func=f) | ||
2190 | 289 | self.assertEqual(be.shutdown_func, f) | ||
2191 | 290 | |||
2192 | 291 | def test_shutdown_func_is_called_on_shutdown(self): | ||
2193 | 292 | """The shutdown_func is called on shutdown.""" | ||
2194 | 293 | self.be.shutdown_func = self._set_called | ||
2195 | 294 | self.be.shutdown() | ||
2196 | 295 | self.assertEqual(self._called, ((), {})) | ||
2197 | 296 | |||
2198 | 297 | def test_shutdown_func_when_none(self): | ||
2199 | 298 | """The shutdown_func can be None.""" | ||
2200 | 299 | self.be.shutdown_func = None | ||
2201 | 300 | self.be.shutdown() | ||
2202 | 301 | # nothing explodes | ||
2203 | 302 | |||
2204 | 281 | 303 | ||
2205 | 282 | class BackendAccountTestCase(BackendBasicTestCase): | 304 | class BackendAccountTestCase(BackendBasicTestCase): |
2206 | 283 | """Account tests for the backend.""" | 305 | """Account tests for the backend.""" |
2207 | @@ -305,7 +327,20 @@ | |||
2208 | 305 | """The account_info method exercises its errback.""" | 327 | """The account_info method exercises its errback.""" |
2209 | 306 | # pylint: disable=E1101 | 328 | # pylint: disable=E1101 |
2210 | 307 | self.be.wc.failure = 404 | 329 | self.be.wc.failure = 404 |
2212 | 308 | yield self.assertFailure(self.be.account_info(), WebClientError) | 330 | yield self.assertFailure(self.be.account_info(), |
2213 | 331 | backend.WebClientError) | ||
2214 | 332 | |||
2215 | 333 | @inlineCallbacks | ||
2216 | 334 | def test_account_info_fails_with_unauthorized(self): | ||
2217 | 335 | """The account_info clears the credentials on unauthorized.""" | ||
2218 | 336 | # pylint: disable=E1101 | ||
2219 | 337 | self.be.wc.failure = 401 | ||
2220 | 338 | d = defer.Deferred() | ||
2221 | 339 | self.patch(backend.dbus_client, 'clear_credentials', | ||
2222 | 340 | lambda: d.callback('called')) | ||
2223 | 341 | yield self.assertFailure(self.be.account_info(), | ||
2224 | 342 | backend.UnauthorizedError) | ||
2225 | 343 | yield d | ||
2226 | 309 | 344 | ||
2227 | 310 | 345 | ||
2228 | 311 | class BackendDevicesTestCase(BackendBasicTestCase): | 346 | class BackendDevicesTestCase(BackendBasicTestCase): |
2229 | @@ -322,14 +357,86 @@ | |||
2230 | 322 | @inlineCallbacks | 357 | @inlineCallbacks |
2231 | 323 | def test_devices_info_fails(self): | 358 | def test_devices_info_fails(self): |
2232 | 324 | """The devices_info method exercises its errback.""" | 359 | """The devices_info method exercises its errback.""" |
2233 | 360 | def fail(*args, **kwargs): | ||
2234 | 361 | """Raise any error other than WebClientError.""" | ||
2235 | 362 | raise ValueError(args) | ||
2236 | 363 | |||
2237 | 364 | self.patch(self.be.wc, 'call_api', fail) | ||
2238 | 365 | yield self.assertFailure(self.be.devices_info(), ValueError) | ||
2239 | 366 | |||
2240 | 367 | @inlineCallbacks | ||
2241 | 368 | def test_devices_info_with_webclient_error(self): | ||
2242 | 369 | """The devices_info returns local info if webclient error.""" | ||
2243 | 325 | # pylint: disable=E1101 | 370 | # pylint: disable=E1101 |
2244 | 326 | self.be.wc.failure = 404 | 371 | self.be.wc.failure = 404 |
2246 | 327 | yield self.assertFailure(self.be.devices_info(), WebClientError) | 372 | result = yield self.be.devices_info() |
2247 | 373 | |||
2248 | 374 | self.assertEqual(result, [LOCAL_DEVICE]) | ||
2249 | 375 | self.assertTrue(self.memento.check_error('devices_info', | ||
2250 | 376 | 'web client failure')) | ||
2251 | 377 | |||
2252 | 378 | @inlineCallbacks | ||
2253 | 379 | def test_devices_info_fails_with_unauthorized(self): | ||
2254 | 380 | """The devices_info clears the credentials on unauthorized.""" | ||
2255 | 381 | # pylint: disable=E1101 | ||
2256 | 382 | self.be.wc.failure = 401 | ||
2257 | 383 | d = defer.Deferred() | ||
2258 | 384 | self.patch(backend.dbus_client, 'clear_credentials', | ||
2259 | 385 | lambda: d.callback('called')) | ||
2260 | 386 | yield self.assertFailure(self.be.devices_info(), | ||
2261 | 387 | backend.UnauthorizedError) | ||
2262 | 388 | yield d | ||
2263 | 389 | |||
2264 | 390 | @inlineCallbacks | ||
2265 | 391 | def test_devices_info_if_files_disable(self): | ||
2266 | 392 | """The devices_info returns device only info if files is disabled.""" | ||
2267 | 393 | yield self.be.disable_files() | ||
2268 | 394 | status = yield self.be.file_sync_status() | ||
2269 | 395 | assert status['status'] == backend.FILE_SYNC_DISABLED, status | ||
2270 | 396 | |||
2271 | 397 | # pylint: disable=E1101 | ||
2272 | 398 | self.be.wc.results[DEVICES_API] = SAMPLE_DEVICES_JSON | ||
2273 | 399 | result = yield self.be.devices_info() | ||
2274 | 400 | |||
2275 | 401 | expected = EXPECTED_DEVICES_INFO[:] | ||
2276 | 402 | for device in expected: | ||
2277 | 403 | device.pop('limit_bandwidth', None) | ||
2278 | 404 | device.pop('max_download_speed', None) | ||
2279 | 405 | device.pop('max_upload_speed', None) | ||
2280 | 406 | device.pop('show_all_notifications', None) | ||
2281 | 407 | device['configurable'] = '' | ||
2282 | 408 | self.assertEqual(result, expected) | ||
2283 | 409 | |||
2284 | 410 | @inlineCallbacks | ||
2285 | 411 | def test_devices_info_when_token_name_is_empty(self): | ||
2286 | 412 | """The devices_info can handle empty token names.""" | ||
2287 | 413 | # pylint: disable=E1101 | ||
2288 | 414 | self.be.wc.results[DEVICES_API] = EMPTY_DESCRIPTION_JSON | ||
2289 | 415 | result = yield self.be.devices_info() | ||
2290 | 416 | expected = {'configurable': '', | ||
2291 | 417 | 'device_id': 'ComputerABCDEF01234token', | ||
2292 | 418 | 'is_local': '', 'name': 'None', | ||
2293 | 419 | 'type': DEVICE_TYPE_COMPUTER} | ||
2294 | 420 | self.assertEqual(result, [expected]) | ||
2295 | 421 | self.assertTrue(self.memento.check_warning('name', 'None')) | ||
2296 | 422 | |||
2297 | 423 | @inlineCallbacks | ||
2298 | 424 | def test_devices_info_does_not_log_device_id(self): | ||
2299 | 425 | """The devices_info does not log the device_id.""" | ||
2300 | 426 | # pylint: disable=E1101 | ||
2301 | 427 | self.be.wc.results[DEVICES_API] = SAMPLE_DEVICES_JSON | ||
2302 | 428 | yield self.be.devices_info() | ||
2303 | 429 | |||
2304 | 430 | dids = (d['device_id'] for d in EXPECTED_DEVICES_INFO) | ||
2305 | 431 | device_id_logged = all(all(did not in r.getMessage() | ||
2306 | 432 | for r in self.memento.records) | ||
2307 | 433 | for did in dids) | ||
2308 | 434 | self.assertTrue(device_id_logged) | ||
2309 | 328 | 435 | ||
2310 | 329 | @inlineCallbacks | 436 | @inlineCallbacks |
2311 | 330 | def test_remove_device(self): | 437 | def test_remove_device(self): |
2312 | 331 | """The remove_device method calls the right api.""" | 438 | """The remove_device method calls the right api.""" |
2314 | 332 | dtype, did = "Computer", "SAMPLE-TOKEN" | 439 | dtype, did = DEVICE_TYPE_COMPUTER, "SAMPLE-TOKEN" |
2315 | 333 | device_id = dtype + did | 440 | device_id = dtype + did |
2316 | 334 | apiurl = DEVICE_REMOVE_API % (dtype.lower(), did) | 441 | apiurl = DEVICE_REMOVE_API % (dtype.lower(), did) |
2317 | 335 | # pylint: disable=E1101 | 442 | # pylint: disable=E1101 |
2318 | @@ -354,7 +461,30 @@ | |||
2319 | 354 | """The remove_device method fails as expected.""" | 461 | """The remove_device method fails as expected.""" |
2320 | 355 | # pylint: disable=E1101 | 462 | # pylint: disable=E1101 |
2321 | 356 | self.be.wc.failure = 404 | 463 | self.be.wc.failure = 404 |
2323 | 357 | yield self.assertFailure(self.be.devices_info(), WebClientError) | 464 | yield self.assertFailure(self.be.remove_device(self.local_token), |
2324 | 465 | backend.WebClientError) | ||
2325 | 466 | |||
2326 | 467 | @inlineCallbacks | ||
2327 | 468 | def test_remove_device_fails_with_unauthorized(self): | ||
2328 | 469 | """The remove_device clears the credentials on unauthorized.""" | ||
2329 | 470 | # pylint: disable=E1101 | ||
2330 | 471 | self.be.wc.failure = 401 | ||
2331 | 472 | d = defer.Deferred() | ||
2332 | 473 | self.patch(backend.dbus_client, 'clear_credentials', | ||
2333 | 474 | lambda: d.callback('called')) | ||
2334 | 475 | yield self.assertFailure(self.be.remove_device(self.local_token), | ||
2335 | 476 | backend.UnauthorizedError) | ||
2336 | 477 | yield d | ||
2337 | 478 | |||
2338 | 479 | @inlineCallbacks | ||
2339 | 480 | def test_remove_device_does_not_log_device_id(self): | ||
2340 | 481 | """The remove_device does not log the device_id.""" | ||
2341 | 482 | device_id = DEVICE_TYPE_COMPUTER + TOKEN['token'] | ||
2342 | 483 | yield self.be.remove_device(device_id) | ||
2343 | 484 | |||
2344 | 485 | device_id_logged = all(device_id not in r.getMessage() | ||
2345 | 486 | for r in self.memento.records) | ||
2346 | 487 | self.assertTrue(device_id_logged) | ||
2347 | 358 | 488 | ||
2348 | 359 | @inlineCallbacks | 489 | @inlineCallbacks |
2349 | 360 | def test_change_show_all_notifications(self): | 490 | def test_change_show_all_notifications(self): |
2350 | @@ -411,6 +541,16 @@ | |||
2351 | 411 | self.assertEqual(backend.dbus_client.limits["upload"], -1) | 541 | self.assertEqual(backend.dbus_client.limits["upload"], -1) |
2352 | 412 | self.assertEqual(backend.dbus_client.limits["download"], -1) | 542 | self.assertEqual(backend.dbus_client.limits["download"], -1) |
2353 | 413 | 543 | ||
2354 | 544 | @inlineCallbacks | ||
2355 | 545 | def test_changing_settings_does_not_log_device_id(self): | ||
2356 | 546 | """The change_device_settings does not log the device_id.""" | ||
2357 | 547 | device_id = 'yadda-yadda' | ||
2358 | 548 | yield self.be.change_device_settings(device_id, {}) | ||
2359 | 549 | |||
2360 | 550 | device_id_logged = all(device_id not in r.getMessage() | ||
2361 | 551 | for r in self.memento.records) | ||
2362 | 552 | self.assertTrue(device_id_logged) | ||
2363 | 553 | |||
2364 | 414 | 554 | ||
2365 | 415 | class BackendVolumesTestCase(BackendBasicTestCase): | 555 | class BackendVolumesTestCase(BackendBasicTestCase): |
2366 | 416 | """Volumes tests for the backend.""" | 556 | """Volumes tests for the backend.""" |
2367 | @@ -581,6 +721,12 @@ | |||
2368 | 581 | class BackendSyncStatusTestCase(BackendBasicTestCase): | 721 | class BackendSyncStatusTestCase(BackendBasicTestCase): |
2369 | 582 | """Syncdaemon state for the backend.""" | 722 | """Syncdaemon state for the backend.""" |
2370 | 583 | 723 | ||
2371 | 724 | was_disabled = False | ||
2372 | 725 | |||
2373 | 726 | def setUp(self): | ||
2374 | 727 | super(BackendSyncStatusTestCase, self).setUp() | ||
2375 | 728 | self.be.file_sync_disabled = self.was_disabled | ||
2376 | 729 | |||
2377 | 584 | def _build_msg(self): | 730 | def _build_msg(self): |
2378 | 585 | """Build expected message regarding file sync status.""" | 731 | """Build expected message regarding file sync status.""" |
2379 | 586 | return '%s (%s)' % (MockDBusClient.status['description'], | 732 | return '%s (%s)' % (MockDBusClient.status['description'], |
2380 | @@ -599,6 +745,7 @@ | |||
2381 | 599 | """The syncdaemon status is processed and emitted.""" | 745 | """The syncdaemon status is processed and emitted.""" |
2382 | 600 | self.patch(MockDBusClient, 'file_sync', False) | 746 | self.patch(MockDBusClient, 'file_sync', False) |
2383 | 601 | yield self.assert_correct_status(FILE_SYNC_DISABLED, msg='') | 747 | yield self.assert_correct_status(FILE_SYNC_DISABLED, msg='') |
2384 | 748 | self.assertTrue(self.be.file_sync_disabled) | ||
2385 | 602 | 749 | ||
2386 | 603 | @inlineCallbacks | 750 | @inlineCallbacks |
2387 | 604 | def test_error(self): | 751 | def test_error(self): |
2388 | @@ -610,6 +757,8 @@ | |||
2389 | 610 | 'description': 'auth failed', | 757 | 'description': 'auth failed', |
2390 | 611 | } | 758 | } |
2391 | 612 | yield self.assert_correct_status(FILE_SYNC_ERROR) | 759 | yield self.assert_correct_status(FILE_SYNC_ERROR) |
2392 | 760 | # self.be.file_sync_disabled does not change | ||
2393 | 761 | self.assertEqual(self.was_disabled, self.be.file_sync_disabled) | ||
2394 | 613 | 762 | ||
2395 | 614 | @inlineCallbacks | 763 | @inlineCallbacks |
2396 | 615 | def test_starting_when_init_not_user(self): | 764 | def test_starting_when_init_not_user(self): |
2397 | @@ -620,6 +769,7 @@ | |||
2398 | 620 | 'name': 'INIT', 'description': 'something new', | 769 | 'name': 'INIT', 'description': 'something new', |
2399 | 621 | } | 770 | } |
2400 | 622 | yield self.assert_correct_status(FILE_SYNC_STARTING) | 771 | yield self.assert_correct_status(FILE_SYNC_STARTING) |
2401 | 772 | self.assertFalse(self.be.file_sync_disabled) | ||
2402 | 623 | 773 | ||
2403 | 624 | @inlineCallbacks | 774 | @inlineCallbacks |
2404 | 625 | def test_starting_when_init_with_user(self): | 775 | def test_starting_when_init_with_user(self): |
2405 | @@ -630,6 +780,7 @@ | |||
2406 | 630 | 'name': 'INIT', 'description': 'something new', | 780 | 'name': 'INIT', 'description': 'something new', |
2407 | 631 | } | 781 | } |
2408 | 632 | yield self.assert_correct_status(FILE_SYNC_STARTING) | 782 | yield self.assert_correct_status(FILE_SYNC_STARTING) |
2409 | 783 | self.assertFalse(self.be.file_sync_disabled) | ||
2410 | 633 | 784 | ||
2411 | 634 | @inlineCallbacks | 785 | @inlineCallbacks |
2412 | 635 | def test_starting_when_local_rescan_not_user(self): | 786 | def test_starting_when_local_rescan_not_user(self): |
2413 | @@ -640,6 +791,7 @@ | |||
2414 | 640 | 'name': 'LOCAL_RESCAN', 'description': 'something new', | 791 | 'name': 'LOCAL_RESCAN', 'description': 'something new', |
2415 | 641 | } | 792 | } |
2416 | 642 | yield self.assert_correct_status(FILE_SYNC_STARTING) | 793 | yield self.assert_correct_status(FILE_SYNC_STARTING) |
2417 | 794 | self.assertFalse(self.be.file_sync_disabled) | ||
2418 | 643 | 795 | ||
2419 | 644 | @inlineCallbacks | 796 | @inlineCallbacks |
2420 | 645 | def test_starting_when_local_rescan_with_user(self): | 797 | def test_starting_when_local_rescan_with_user(self): |
2421 | @@ -650,6 +802,7 @@ | |||
2422 | 650 | 'name': 'LOCAL_RESCAN', 'description': 'something new', | 802 | 'name': 'LOCAL_RESCAN', 'description': 'something new', |
2423 | 651 | } | 803 | } |
2424 | 652 | yield self.assert_correct_status(FILE_SYNC_STARTING) | 804 | yield self.assert_correct_status(FILE_SYNC_STARTING) |
2425 | 805 | self.assertFalse(self.be.file_sync_disabled) | ||
2426 | 653 | 806 | ||
2427 | 654 | @inlineCallbacks | 807 | @inlineCallbacks |
2428 | 655 | def test_starting_when_ready_with_user(self): | 808 | def test_starting_when_ready_with_user(self): |
2429 | @@ -660,6 +813,7 @@ | |||
2430 | 660 | 'name': 'READY', 'description': 'something nicer', | 813 | 'name': 'READY', 'description': 'something nicer', |
2431 | 661 | } | 814 | } |
2432 | 662 | yield self.assert_correct_status(FILE_SYNC_STARTING) | 815 | yield self.assert_correct_status(FILE_SYNC_STARTING) |
2433 | 816 | self.assertFalse(self.be.file_sync_disabled) | ||
2434 | 663 | 817 | ||
2435 | 664 | @inlineCallbacks | 818 | @inlineCallbacks |
2436 | 665 | def test_disconnected(self): | 819 | def test_disconnected(self): |
2437 | @@ -672,6 +826,9 @@ | |||
2438 | 672 | } | 826 | } |
2439 | 673 | yield self.assert_correct_status(FILE_SYNC_DISCONNECTED) | 827 | yield self.assert_correct_status(FILE_SYNC_DISCONNECTED) |
2440 | 674 | 828 | ||
2441 | 829 | # self.be.file_sync_disabled does not change | ||
2442 | 830 | self.assertEqual(self.was_disabled, self.be.file_sync_disabled) | ||
2443 | 831 | |||
2444 | 675 | @inlineCallbacks | 832 | @inlineCallbacks |
2445 | 676 | def test_disconnected_when_waiting(self): | 833 | def test_disconnected_when_waiting(self): |
2446 | 677 | """The syncdaemon status is processed and emitted.""" | 834 | """The syncdaemon status is processed and emitted.""" |
2447 | @@ -682,6 +839,9 @@ | |||
2448 | 682 | } | 839 | } |
2449 | 683 | yield self.assert_correct_status(FILE_SYNC_DISCONNECTED) | 840 | yield self.assert_correct_status(FILE_SYNC_DISCONNECTED) |
2450 | 684 | 841 | ||
2451 | 842 | # self.be.file_sync_disabled does not change | ||
2452 | 843 | self.assertEqual(self.was_disabled, self.be.file_sync_disabled) | ||
2453 | 844 | |||
2454 | 685 | @inlineCallbacks | 845 | @inlineCallbacks |
2455 | 686 | def test_syncing_if_online(self): | 846 | def test_syncing_if_online(self): |
2456 | 687 | """The syncdaemon status is processed and emitted.""" | 847 | """The syncdaemon status is processed and emitted.""" |
2457 | @@ -693,6 +853,9 @@ | |||
2458 | 693 | } | 853 | } |
2459 | 694 | yield self.assert_correct_status(FILE_SYNC_SYNCING) | 854 | yield self.assert_correct_status(FILE_SYNC_SYNCING) |
2460 | 695 | 855 | ||
2461 | 856 | # self.be.file_sync_disabled does not change | ||
2462 | 857 | self.assertEqual(self.was_disabled, self.be.file_sync_disabled) | ||
2463 | 858 | |||
2464 | 696 | @inlineCallbacks | 859 | @inlineCallbacks |
2465 | 697 | def test_syncing_even_if_not_online(self): | 860 | def test_syncing_even_if_not_online(self): |
2466 | 698 | """The syncdaemon status is processed and emitted.""" | 861 | """The syncdaemon status is processed and emitted.""" |
2467 | @@ -704,6 +867,9 @@ | |||
2468 | 704 | } | 867 | } |
2469 | 705 | yield self.assert_correct_status(FILE_SYNC_SYNCING) | 868 | yield self.assert_correct_status(FILE_SYNC_SYNCING) |
2470 | 706 | 869 | ||
2471 | 870 | # self.be.file_sync_disabled does not change | ||
2472 | 871 | self.assertEqual(self.was_disabled, self.be.file_sync_disabled) | ||
2473 | 872 | |||
2474 | 707 | @inlineCallbacks | 873 | @inlineCallbacks |
2475 | 708 | def test_idle(self): | 874 | def test_idle(self): |
2476 | 709 | """The syncdaemon status is processed and emitted.""" | 875 | """The syncdaemon status is processed and emitted.""" |
2477 | @@ -715,6 +881,9 @@ | |||
2478 | 715 | } | 881 | } |
2479 | 716 | yield self.assert_correct_status(FILE_SYNC_IDLE) | 882 | yield self.assert_correct_status(FILE_SYNC_IDLE) |
2480 | 717 | 883 | ||
2481 | 884 | # self.be.file_sync_disabled does not change | ||
2482 | 885 | self.assertEqual(self.was_disabled, self.be.file_sync_disabled) | ||
2483 | 886 | |||
2484 | 718 | @inlineCallbacks | 887 | @inlineCallbacks |
2485 | 719 | def test_stopped(self): | 888 | def test_stopped(self): |
2486 | 720 | """The syncdaemon status is processed and emitted.""" | 889 | """The syncdaemon status is processed and emitted.""" |
2487 | @@ -726,6 +895,9 @@ | |||
2488 | 726 | } | 895 | } |
2489 | 727 | yield self.assert_correct_status(FILE_SYNC_STOPPED) | 896 | yield self.assert_correct_status(FILE_SYNC_STOPPED) |
2490 | 728 | 897 | ||
2491 | 898 | # self.be.file_sync_disabled does not change | ||
2492 | 899 | self.assertEqual(self.was_disabled, self.be.file_sync_disabled) | ||
2493 | 900 | |||
2494 | 729 | @inlineCallbacks | 901 | @inlineCallbacks |
2495 | 730 | def test_unknown(self): | 902 | def test_unknown(self): |
2496 | 731 | """The syncdaemon status is processed and emitted.""" | 903 | """The syncdaemon status is processed and emitted.""" |
2497 | @@ -740,6 +912,9 @@ | |||
2498 | 740 | repr(MockDBusClient.status)) | 912 | repr(MockDBusClient.status)) |
2499 | 741 | self.assertTrue(has_warning) | 913 | self.assertTrue(has_warning) |
2500 | 742 | 914 | ||
2501 | 915 | # self.be.file_sync_disabled does not change | ||
2502 | 916 | self.assertEqual(self.was_disabled, self.be.file_sync_disabled) | ||
2503 | 917 | |||
2504 | 743 | def test_status_changed(self): | 918 | def test_status_changed(self): |
2505 | 744 | """The file_sync_status is the status changed handler.""" | 919 | """The file_sync_status is the status changed handler.""" |
2506 | 745 | self.be.status_changed_handler = self._set_called | 920 | self.be.status_changed_handler = self._set_called |
2507 | @@ -754,6 +929,21 @@ | |||
2508 | 754 | self.assertEqual(self._called, ((expected_status,), {})) | 929 | self.assertEqual(self._called, ((expected_status,), {})) |
2509 | 755 | 930 | ||
2510 | 756 | 931 | ||
2511 | 932 | class BackendSyncStatusIfDisabledTestCase(BackendSyncStatusTestCase): | ||
2512 | 933 | """Syncdaemon state for the backend when file sync is disabled.""" | ||
2513 | 934 | |||
2514 | 935 | was_disabled = True | ||
2515 | 936 | |||
2516 | 937 | @inlineCallbacks | ||
2517 | 938 | def assert_correct_status(self, status, msg=None): | ||
2518 | 939 | """Check that the resulting status is correct.""" | ||
2519 | 940 | sup = super(BackendSyncStatusIfDisabledTestCase, self) | ||
2520 | 941 | if status != FILE_SYNC_STARTING: | ||
2521 | 942 | yield sup.assert_correct_status(FILE_SYNC_DISABLED, msg='') | ||
2522 | 943 | else: | ||
2523 | 944 | yield sup.assert_correct_status(status, msg=msg) | ||
2524 | 945 | |||
2525 | 946 | |||
2526 | 757 | class BackendFileSyncOpsTestCase(BackendBasicTestCase): | 947 | class BackendFileSyncOpsTestCase(BackendBasicTestCase): |
2527 | 758 | """Syncdaemon operations for the backend.""" | 948 | """Syncdaemon operations for the backend.""" |
2528 | 759 | 949 | ||
2529 | @@ -768,6 +958,7 @@ | |||
2530 | 768 | 958 | ||
2531 | 769 | yield self.be.enable_files() | 959 | yield self.be.enable_files() |
2532 | 770 | self.assertTrue(MockDBusClient.file_sync) | 960 | self.assertTrue(MockDBusClient.file_sync) |
2533 | 961 | self.assertFalse(self.be.file_sync_disabled) | ||
2534 | 771 | 962 | ||
2535 | 772 | @inlineCallbacks | 963 | @inlineCallbacks |
2536 | 773 | def test_disable_files(self): | 964 | def test_disable_files(self): |
2537 | @@ -776,6 +967,7 @@ | |||
2538 | 776 | 967 | ||
2539 | 777 | yield self.be.disable_files() | 968 | yield self.be.disable_files() |
2540 | 778 | self.assertFalse(MockDBusClient.file_sync) | 969 | self.assertFalse(MockDBusClient.file_sync) |
2541 | 970 | self.assertTrue(self.be.file_sync_disabled) | ||
2542 | 779 | 971 | ||
2543 | 780 | @inlineCallbacks | 972 | @inlineCallbacks |
2544 | 781 | def test_connect_files(self): | 973 | def test_connect_files(self): |
2545 | @@ -783,6 +975,7 @@ | |||
2546 | 783 | yield self.be.connect_files() | 975 | yield self.be.connect_files() |
2547 | 784 | 976 | ||
2548 | 785 | self.assertEqual(MockDBusClient.actions, ['connect']) | 977 | self.assertEqual(MockDBusClient.actions, ['connect']) |
2549 | 978 | self.assertFalse(self.be.file_sync_disabled) | ||
2550 | 786 | 979 | ||
2551 | 787 | @inlineCallbacks | 980 | @inlineCallbacks |
2552 | 788 | def test_disconnect_files(self): | 981 | def test_disconnect_files(self): |
2553 | @@ -790,6 +983,7 @@ | |||
2554 | 790 | yield self.be.disconnect_files() | 983 | yield self.be.disconnect_files() |
2555 | 791 | 984 | ||
2556 | 792 | self.assertEqual(MockDBusClient.actions, ['disconnect']) | 985 | self.assertEqual(MockDBusClient.actions, ['disconnect']) |
2557 | 986 | self.assertFalse(self.be.file_sync_disabled) | ||
2558 | 793 | 987 | ||
2559 | 794 | @inlineCallbacks | 988 | @inlineCallbacks |
2560 | 795 | def test_restart_files(self): | 989 | def test_restart_files(self): |
2561 | @@ -797,6 +991,7 @@ | |||
2562 | 797 | yield self.be.restart_files() | 991 | yield self.be.restart_files() |
2563 | 798 | 992 | ||
2564 | 799 | self.assertEqual(MockDBusClient.actions, ['stop', 'start']) | 993 | self.assertEqual(MockDBusClient.actions, ['stop', 'start']) |
2565 | 994 | self.assertFalse(self.be.file_sync_disabled) | ||
2566 | 800 | 995 | ||
2567 | 801 | @inlineCallbacks | 996 | @inlineCallbacks |
2568 | 802 | def test_start_files(self): | 997 | def test_start_files(self): |
2569 | @@ -804,6 +999,7 @@ | |||
2570 | 804 | yield self.be.start_files() | 999 | yield self.be.start_files() |
2571 | 805 | 1000 | ||
2572 | 806 | self.assertEqual(MockDBusClient.actions, ['start']) | 1001 | self.assertEqual(MockDBusClient.actions, ['start']) |
2573 | 1002 | self.assertFalse(self.be.file_sync_disabled) | ||
2574 | 807 | 1003 | ||
2575 | 808 | @inlineCallbacks | 1004 | @inlineCallbacks |
2576 | 809 | def test_stop_files(self): | 1005 | def test_stop_files(self): |
2577 | @@ -811,6 +1007,7 @@ | |||
2578 | 811 | yield self.be.stop_files() | 1007 | yield self.be.stop_files() |
2579 | 812 | 1008 | ||
2580 | 813 | self.assertEqual(MockDBusClient.actions, ['stop']) | 1009 | self.assertEqual(MockDBusClient.actions, ['stop']) |
2581 | 1010 | self.assertFalse(self.be.file_sync_disabled) | ||
2582 | 814 | 1011 | ||
2583 | 815 | 1012 | ||
2584 | 816 | class BackendReplicationsTestCase(BackendBasicTestCase): | 1013 | class BackendReplicationsTestCase(BackendBasicTestCase): |
2585 | 817 | 1014 | ||
2586 | === modified file 'ubuntuone/controlpanel/utils.py' | |||
2587 | --- ubuntuone/controlpanel/utils.py 2010-12-22 13:33:25 +0000 | |||
2588 | +++ ubuntuone/controlpanel/utils.py 2011-04-08 19:43:13 +0000 | |||
2589 | @@ -52,7 +52,7 @@ | |||
2590 | 52 | 52 | ||
2591 | 53 | # otherwise, try to load PROJECT_DIR from installation path | 53 | # otherwise, try to load PROJECT_DIR from installation path |
2592 | 54 | try: | 54 | try: |
2594 | 55 | # pylint: disable=F0401, E0611 | 55 | # pylint: disable=F0401, E0611, W0404 |
2595 | 56 | from ubuntuone.controlpanel.constants import PROJECT_DIR | 56 | from ubuntuone.controlpanel.constants import PROJECT_DIR |
2596 | 57 | return PROJECT_DIR | 57 | return PROJECT_DIR |
2597 | 58 | except ImportError: | 58 | except ImportError: |
2598 | 59 | 59 | ||
2599 | === modified file 'ubuntuone/controlpanel/webclient.py' | |||
2600 | --- ubuntuone/controlpanel/webclient.py 2010-12-22 13:33:25 +0000 | |||
2601 | +++ ubuntuone/controlpanel/webclient.py 2011-04-08 19:43:13 +0000 | |||
2602 | @@ -32,10 +32,18 @@ | |||
2603 | 32 | logger = setup_logging('webclient') | 32 | logger = setup_logging('webclient') |
2604 | 33 | 33 | ||
2605 | 34 | 34 | ||
2606 | 35 | # full list of status codes | ||
2607 | 36 | # http://library.gnome.org/devel/libsoup/stable/libsoup-2.4-soup-status.html | ||
2608 | 37 | |||
2609 | 38 | |||
2610 | 35 | class WebClientError(Exception): | 39 | class WebClientError(Exception): |
2611 | 36 | """An http error happened while calling the webservice.""" | 40 | """An http error happened while calling the webservice.""" |
2612 | 37 | 41 | ||
2613 | 38 | 42 | ||
2614 | 43 | class UnauthorizedError(WebClientError): | ||
2615 | 44 | """The request ended with bad_request, unauthorized or forbidden.""" | ||
2616 | 45 | |||
2617 | 46 | |||
2618 | 39 | class WebClient(object): | 47 | class WebClient(object): |
2619 | 40 | """A client for the u1 webservice.""" | 48 | """A client for the u1 webservice.""" |
2620 | 41 | 49 | ||
2621 | @@ -48,21 +56,24 @@ | |||
2622 | 48 | 56 | ||
2623 | 49 | def _handler(self, session, msg, d): | 57 | def _handler(self, session, msg, d): |
2624 | 50 | """Handle the result of an http message.""" | 58 | """Handle the result of an http message.""" |
2626 | 51 | logger.debug("WebClient: got http response %d for uri %r", | 59 | logger.debug("got http response %d for uri %r", |
2627 | 52 | msg.status_code, msg.get_uri().to_string(False)) | 60 | msg.status_code, msg.get_uri().to_string(False)) |
2628 | 53 | data = msg.response_body.data | 61 | data = msg.response_body.data |
2629 | 54 | if msg.status_code == 200: | 62 | if msg.status_code == 200: |
2630 | 55 | result = simplejson.loads(data) | 63 | result = simplejson.loads(data) |
2631 | 56 | d.callback(result) | 64 | d.callback(result) |
2632 | 57 | else: | 65 | else: |
2634 | 58 | e = WebClientError(msg.status_code, data) | 66 | if msg.status_code in (401,): |
2635 | 67 | e = UnauthorizedError(msg.status_code, data) | ||
2636 | 68 | else: | ||
2637 | 69 | e = WebClientError(msg.status_code, data) | ||
2638 | 59 | d.errback(e) | 70 | d.errback(e) |
2639 | 60 | 71 | ||
2640 | 61 | def _call_api_with_creds(self, credentials, api_name): | 72 | def _call_api_with_creds(self, credentials, api_name): |
2641 | 62 | """Get a given url from the webservice with credentials.""" | 73 | """Get a given url from the webservice with credentials.""" |
2642 | 63 | url = (self.base_url + api_name).encode('utf-8') | 74 | url = (self.base_url + api_name).encode('utf-8') |
2643 | 64 | method = "GET" | 75 | method = "GET" |
2645 | 65 | logger.debug("WebClient: getting url: %s, %s", method, url) | 76 | logger.debug("getting url: %s, %s", method, url) |
2646 | 66 | msg = Soup.Message.new(method, url) | 77 | msg = Soup.Message.new(method, url) |
2647 | 67 | add_oauth_headers(msg.request_headers.append, method, url, credentials) | 78 | add_oauth_headers(msg.request_headers.append, method, url, credentials) |
2648 | 68 | d = defer.Deferred() | 79 | d = defer.Deferred() |
2649 | @@ -71,7 +82,8 @@ | |||
2650 | 71 | 82 | ||
2651 | 72 | def call_api(self, api_name): | 83 | def call_api(self, api_name): |
2652 | 73 | """Get a given url from the webservice.""" | 84 | """Get a given url from the webservice.""" |
2654 | 74 | logger.debug("WebClient: calling api: %s", api_name) | 85 | # this may log device ID's, but only for removals, which is OK |
2655 | 86 | logger.debug("calling api: %s", api_name) | ||
2656 | 75 | d = self.get_credentials() | 87 | d = self.get_credentials() |
2657 | 76 | d.addCallback(self._call_api_with_creds, api_name) | 88 | d.addCallback(self._call_api_with_creds, api_name) |
2658 | 77 | return d | 89 | return d |