Merge lp:~nataliabidart/ubuntuone-control-panel/start-dc-on-backend into lp:ubuntuone-control-panel
- start-dc-on-backend
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Natalia Bidart |
Approved revision: | 56 |
Merged at revision: | 45 |
Proposed branch: | lp:~nataliabidart/ubuntuone-control-panel/start-dc-on-backend |
Merge into: | lp:ubuntuone-control-panel |
Prerequisite: | lp:~nataliabidart/ubuntuone-control-panel/replication-to-the-backend |
Diff against target: |
932 lines (+326/-242) 7 files modified
data/install.ui (+7/-2) po/POTFILES.in (+2/-1) pylintrc (+1/-1) ubuntuone/controlpanel/gtk/gui.py (+114/-81) ubuntuone/controlpanel/gtk/tests/__init__.py (+14/-13) ubuntuone/controlpanel/gtk/tests/test_gui.py (+187/-140) ubuntuone/controlpanel/logger.py (+1/-4) |
To merge this branch: | bzr merge lp:~nataliabidart/ubuntuone-control-panel/start-dc-on-backend |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Roberto Alsina (community) | Approve | ||
Eric Casteleijn (community) | Approve | ||
Review via email: mp+45448@code.launchpad.net |
This proposal supersedes a proposal from 2011-01-06.
Commit message
Desktopcouch replication service is accessed through the backend using its DBus service (LP: #696782).
Description of the change
To test, first check that all the desktopcouch related packages are uninstalled, and that no desktopcouch nor couchdb process is running. This meaning:
sudo apt-cache search couch
should show no package installed, and:
ps aux | grep couch
should return no results. Then, from this branch, run:
terminal 1: DEBUG=True PYTHONPATH=. ./bin/ubuntuone
terminal 2: DEBUG=True PYTHONPATH=. ./bin/ubuntuone
And go to the third tab and install dc-u1 from there, and keep playing. You should get no hangs at all in the UI.
- 55. By Natalia Bidart
-
Avoiding conflicts.
- 56. By Natalia Bidart
-
Merged dependency branch in.
Roberto Alsina (ralsina) wrote : | # |
Does what it says. +1
Preview Diff
1 | === modified file 'data/install.ui' |
2 | --- data/install.ui 2010-12-24 14:53:03 +0000 |
3 | +++ data/install.ui 2011-01-06 21:03:06 +0000 |
4 | @@ -23,11 +23,12 @@ |
5 | <property name="visible">True</property> |
6 | <child> |
7 | <object class="GtkButton" id="install_button"> |
8 | - <property name="label">gtk-ok</property> |
9 | + <property name="label" translatable="yes">_Install now</property> |
10 | <property name="visible">True</property> |
11 | <property name="can_focus">True</property> |
12 | <property name="receives_default">True</property> |
13 | - <property name="use_stock">True</property> |
14 | + <property name="image">image1</property> |
15 | + <property name="use_underline">True</property> |
16 | <signal name="clicked" handler="on_install_button_clicked"/> |
17 | </object> |
18 | <packing> |
19 | @@ -43,4 +44,8 @@ |
20 | </packing> |
21 | </child> |
22 | </object> |
23 | + <object class="GtkImage" id="image1"> |
24 | + <property name="visible">True</property> |
25 | + <property name="stock">gtk-ok</property> |
26 | + </object> |
27 | </interface> |
28 | |
29 | === modified file 'po/POTFILES.in' |
30 | --- po/POTFILES.in 2011-01-04 16:12:56 +0000 |
31 | +++ po/POTFILES.in 2011-01-06 21:03:06 +0000 |
32 | @@ -1,8 +1,9 @@ |
33 | ubuntuone-control-panel-gtk.desktop.in |
34 | ubuntuone/controlpanel/gtk/gui.py |
35 | [type: gettext/glade] data/dashboard.ui |
36 | -[type: gettext/glade] data/services.ui |
37 | [type: gettext/glade] data/device.ui |
38 | [type: gettext/glade] data/devices.ui |
39 | +[type: gettext/glade] data/install.ui |
40 | [type: gettext/glade] data/management.ui |
41 | [type: gettext/glade] data/overview.ui |
42 | +[type: gettext/glade] data/services.ui |
43 | |
44 | === modified file 'pylintrc' |
45 | --- pylintrc 2010-10-13 18:55:23 +0000 |
46 | +++ pylintrc 2011-01-06 21:03:06 +0000 |
47 | @@ -272,7 +272,7 @@ |
48 | max-line-length=79 |
49 | |
50 | # Maximum number of lines in a module |
51 | -max-module-lines=2000 |
52 | +max-module-lines=2500 |
53 | |
54 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 |
55 | # tab). |
56 | |
57 | === modified file 'ubuntuone/controlpanel/gtk/gui.py' |
58 | --- ubuntuone/controlpanel/gtk/gui.py 2010-12-26 15:02:52 +0000 |
59 | +++ ubuntuone/controlpanel/gtk/gui.py 2011-01-06 21:03:06 +0000 |
60 | @@ -78,6 +78,12 @@ |
61 | VALUE_ERROR = _('Value could not be retrieved.') |
62 | WARNING_MARKUP = '<span foreground="%s"><b>%%s</b></span>' % ORANGE |
63 | KILOBYTES = 1024 |
64 | +NO_OP = lambda *a, **kw: None |
65 | + |
66 | + |
67 | +def error_handler(*args, **kwargs): |
68 | + """Log errors when calling D-Bus methods in a async way.""" |
69 | + logger.error('Error handler received: %r, %r', args, kwargs) |
70 | |
71 | |
72 | def filter_by_app_name(f): |
73 | @@ -352,7 +358,8 @@ |
74 | settings = {TC_URL_KEY: U1_TC_URL, HELP_TEXT_KEY: U1_DESCRIPTION, |
75 | WINDOW_ID_KEY: str(self._window_id), |
76 | PING_URL_KEY: U1_PING_URL} |
77 | - self.sso_backend.register(U1_APP_NAME, settings) |
78 | + self.sso_backend.register(U1_APP_NAME, settings, |
79 | + reply_handler=NO_OP, error_handler=error_handler) |
80 | self.set_property('greyed', True) |
81 | self.warning_label.set_text('') |
82 | |
83 | @@ -361,7 +368,8 @@ |
84 | settings = {TC_URL_KEY: U1_TC_URL, HELP_TEXT_KEY: U1_DESCRIPTION, |
85 | WINDOW_ID_KEY: str(self._window_id), |
86 | PING_URL_KEY: U1_PING_URL} |
87 | - self.sso_backend.login(U1_APP_NAME, settings) |
88 | + self.sso_backend.login(U1_APP_NAME, settings, |
89 | + reply_handler=NO_OP, error_handler=error_handler) |
90 | self.set_property('greyed', True) |
91 | self.warning_label.set_text('') |
92 | |
93 | @@ -408,7 +416,8 @@ |
94 | else: |
95 | self.set_sensitive(True) |
96 | self.warning_label.set_text('') |
97 | - self.sso_backend.find_credentials(U1_APP_NAME, {}) |
98 | + self.sso_backend.find_credentials(U1_APP_NAME, {}, |
99 | + reply_handler=NO_OP, error_handler=error_handler) |
100 | |
101 | |
102 | class DashboardPanel(UbuntuOneBin, ControlPanelMixin): |
103 | @@ -516,11 +525,13 @@ |
104 | volume_id = checkbutton.get_label() |
105 | subscribed = bool_str(checkbutton.get_active()) |
106 | self.backend.change_volume_settings(volume_id, |
107 | - {'subscribed': subscribed}) |
108 | + {'subscribed': subscribed}, |
109 | + reply_handler=NO_OP, error_handler=error_handler) |
110 | |
111 | def load(self): |
112 | """Load the volume list.""" |
113 | - self.backend.volumes_info() |
114 | + self.backend.volumes_info(reply_handler=NO_OP, |
115 | + error_handler=error_handler) |
116 | self.message.start() |
117 | |
118 | |
119 | @@ -565,7 +576,8 @@ |
120 | # Not disabling the GUI to avoid annyong twitchings |
121 | #self.set_sensitive(False) |
122 | self.warning_label.set_text('') |
123 | - self.backend.change_device_settings(self.id, self.__dict__) |
124 | + self.backend.change_device_settings(self.id, self.__dict__, |
125 | + reply_handler=NO_OP, error_handler=error_handler) |
126 | |
127 | def _block_signals(f): |
128 | """Execute 'f' while having the _updating flag set.""" |
129 | @@ -591,7 +603,8 @@ |
130 | |
131 | def on_remove_clicked(self, widget): |
132 | """Remove button was clicked or activated.""" |
133 | - self.backend.remove_device(self.id) |
134 | + self.backend.remove_device(self.id, |
135 | + reply_handler=NO_OP, error_handler=error_handler) |
136 | self.set_sensitive(False) |
137 | |
138 | @_block_signals |
139 | @@ -747,7 +760,8 @@ |
140 | |
141 | def load(self): |
142 | """Load the device list.""" |
143 | - self.backend.devices_info() |
144 | + self.backend.devices_info(reply_handler=NO_OP, |
145 | + error_handler=error_handler) |
146 | self.message.start() |
147 | |
148 | |
149 | @@ -818,12 +832,18 @@ |
150 | class Service(gtk.VBox, ControlPanelMixin): |
151 | """A service.""" |
152 | |
153 | - def __init__(self, name, localized_name, *args, **kwargs): |
154 | + CHANGE_ERROR = _('The settings could not be changed,\n' |
155 | + 'previous values were restored.') |
156 | + |
157 | + def __init__(self, service_id, name, *args, **kwargs): |
158 | gtk.VBox.__init__(self) |
159 | ControlPanelMixin.__init__(self) |
160 | - self.service_name = name |
161 | - |
162 | - self.button = gtk.CheckButton(label=localized_name) |
163 | + self.id = service_id |
164 | + |
165 | + self.warning_label = gtk.Label() |
166 | + self.pack_start(self.warning_label, expand=False) |
167 | + |
168 | + self.button = gtk.CheckButton(label=name) |
169 | self.pack_start(self.button, expand=False) |
170 | |
171 | self.show_all() |
172 | @@ -835,15 +855,18 @@ |
173 | FILES_SERVICE_NAME = _('Files') |
174 | |
175 | def __init__(self): |
176 | - Service.__init__(self, name='files', |
177 | - localized_name=self.FILES_SERVICE_NAME) |
178 | + Service.__init__(self, service_id='files', |
179 | + name=self.FILES_SERVICE_NAME) |
180 | |
181 | self.set_sensitive(False) |
182 | + |
183 | self.backend.connect_to_signal('FileSyncStatusChanged', |
184 | self.on_file_sync_status_changed) |
185 | self.backend.connect_to_signal('FilesEnabled', self.on_files_enabled) |
186 | self.backend.connect_to_signal('FilesDisabled', self.on_files_disabled) |
187 | - self.backend.file_sync_status() |
188 | + |
189 | + self.backend.file_sync_status(reply_handler=NO_OP, |
190 | + error_handler=error_handler) |
191 | |
192 | @log_call(logger.debug) |
193 | def on_file_sync_status_changed(self, status): |
194 | @@ -869,19 +892,24 @@ |
195 | """Button was toggled, exclude/replicate the service properly.""" |
196 | logger.info('File sync enabled? %r', self.button.get_active()) |
197 | if self.button.get_active(): |
198 | - self.backend.enable_files() |
199 | + self.backend.enable_files(reply_handler=NO_OP, |
200 | + error_handler=error_handler) |
201 | else: |
202 | - self.backend.disable_files() |
203 | + self.backend.disable_files(reply_handler=NO_OP, |
204 | + error_handler=error_handler) |
205 | |
206 | |
207 | class DesktopcouchService(Service): |
208 | """A desktopcouch service.""" |
209 | |
210 | - def __init__(self, name, localized_name, |
211 | - replication_service, dependency=None): |
212 | - Service.__init__(self, name, localized_name) |
213 | - self.replication_service = replication_service |
214 | - enabled = name not in self.replication_service.all_exclusions() |
215 | + def __init__(self, service_id, name, enabled, dependency=None): |
216 | + Service.__init__(self, service_id, name) |
217 | + |
218 | + self.backend.connect_to_signal('ReplicationSettingsChanged', |
219 | + self.on_replication_settings_changed) |
220 | + self.backend.connect_to_signal('ReplicationSettingsChangeError', |
221 | + self.on_replication_settings_change_error) |
222 | + |
223 | self.button.set_active(enabled) |
224 | |
225 | self.dependency = None |
226 | @@ -903,11 +931,27 @@ |
227 | def on_button_toggled(self, button): |
228 | """Button was toggled, exclude/replicate the service properly.""" |
229 | logger.info('Starting replication for %r? %r', |
230 | - self.service_name, self.button.get_active()) |
231 | - if self.button.get_active(): |
232 | - self.replication_service.replicate(self.service_name) |
233 | - else: |
234 | - self.replication_service.exclude(self.service_name) |
235 | + self.id, self.button.get_active()) |
236 | + |
237 | + args = {'enabled': bool_str(self.button.get_active())} |
238 | + self.backend.change_replication_settings(self.id, args, |
239 | + reply_handler=NO_OP, error_handler=error_handler) |
240 | + |
241 | + @log_call(logger.info) |
242 | + def on_replication_settings_changed(self, replication_id): |
243 | + """The change of settings for this replication succeded.""" |
244 | + if replication_id != self.id: |
245 | + return |
246 | + self.warning_label.set_text('') |
247 | + |
248 | + @log_call(logger.error) |
249 | + def on_replication_settings_change_error(self, replication_id, |
250 | + error_dict=None): |
251 | + """The change of settings for this replication failed.""" |
252 | + if replication_id != self.id: |
253 | + return |
254 | + self.button.set_active(not self.button.get_active()) |
255 | + self._set_warning(self.CHANGE_ERROR, self.warning_label) |
256 | |
257 | |
258 | class ServicesPanel(UbuntuOneBin, ControlPanelMixin): |
259 | @@ -916,32 +960,33 @@ |
260 | TITLE = _('Ubuntu One services including data sync are enabled for the ' |
261 | 'data types and services listed below.') |
262 | CHOOSE_SERVICES = _('Choose services to synchronize with this computer:') |
263 | - DESKTOPCOUCH_PKG = 'desktopcouch' |
264 | - BINDWOOD_PKG = 'xul-ext-bindwood' |
265 | - EVOCOUCH_PKG = 'evolution-couchdb' |
266 | + DESKTOPCOUCH_PKG = 'desktopcouch-ubuntuone' |
267 | BOOKMARKS = _('Bookmarks (Firefox)') |
268 | CONTACTS = _('Contacts (Evolution)') |
269 | NO_PAIRING_RECORD = _('There is no Ubuntu One pairing record.') |
270 | |
271 | - def __init__(self, replication_exclusion_class=None): |
272 | + def __init__(self): |
273 | UbuntuOneBin.__init__(self) |
274 | ControlPanelMixin.__init__(self, filename='services.ui') |
275 | self.add(self.itself) |
276 | |
277 | - self.replication_exclusion_class = replication_exclusion_class |
278 | - self.replication_service = None |
279 | - self.has_desktopcouch = False |
280 | - self.has_bindwood = False |
281 | - self.has_evocouch = False |
282 | self.package_manager = package_manager.PackageManager() |
283 | self.install_box = None |
284 | - self.bookmarks = None |
285 | - self.contacts = None |
286 | + |
287 | + self.backend.connect_to_signal('ReplicationsInfoReady', |
288 | + self.on_replications_info_ready) |
289 | + self.backend.connect_to_signal('ReplicationsInfoError', |
290 | + self.on_replications_info_error) |
291 | |
292 | self.files.pack_start(FilesService(), expand=False) |
293 | |
294 | self.show() |
295 | |
296 | + @property |
297 | + def has_desktopcouch(self): |
298 | + """Is desktopcouch installed?""" |
299 | + return self.package_manager.is_installed(self.DESKTOPCOUCH_PKG) |
300 | + |
301 | @log_call(logger.debug) |
302 | def load(self): |
303 | """Load info.""" |
304 | @@ -949,16 +994,7 @@ |
305 | self.itself.remove(self.install_box) |
306 | self.install_box = None |
307 | |
308 | - self.has_desktopcouch = \ |
309 | - self.package_manager.is_installed(self.DESKTOPCOUCH_PKG) |
310 | - self.has_bindwood = \ |
311 | - self.package_manager.is_installed(self.BINDWOOD_PKG) |
312 | - self.has_evocouch = \ |
313 | - self.package_manager.is_installed(self.EVOCOUCH_PKG) |
314 | - |
315 | - logger.info('load: has_desktopcouch? %r has_bindwood? %s ' |
316 | - 'has_evocouch? %s', self.has_desktopcouch, |
317 | - self.has_bindwood, self.has_evocouch) |
318 | + logger.info('load: has_desktopcouch? %r', self.has_desktopcouch) |
319 | if not self.has_desktopcouch: |
320 | self.message.set_text('') |
321 | self.replications.hide() |
322 | @@ -975,46 +1011,41 @@ |
323 | @log_call(logger.debug) |
324 | def load_replications(self, *args): |
325 | """Load replications info.""" |
326 | + # ask replications to the backend |
327 | + self.message.start() |
328 | + self.backend.replications_info(reply_handler=NO_OP, |
329 | + error_handler=error_handler) |
330 | + |
331 | + @log_call(logger.debug) |
332 | + def on_replications_info_ready(self, info): |
333 | + """The replication info is ready.""" |
334 | + self.on_success(self.CHOOSE_SERVICES) |
335 | + |
336 | self.replications.show() |
337 | |
338 | if self.install_box is not None: |
339 | self.itself.remove(self.install_box) |
340 | self.install_box = None |
341 | |
342 | - self.message.set_text(self.CHOOSE_SERVICES) |
343 | for child in self.replications.get_children(): |
344 | self.replications.remove(child) |
345 | |
346 | - # Unable to import 'desktopcouch.application.replication_services' |
347 | - # pylint: disable=F0401 |
348 | - if self.replication_exclusion_class is None: |
349 | - from desktopcouch.application.replication_services import \ |
350 | - ubuntuone as u1rep |
351 | - self.replication_exclusion_class = u1rep.ReplicationExclusion |
352 | - |
353 | - if self.replication_service is None: |
354 | - try: |
355 | - self.replication_service = self.replication_exclusion_class() |
356 | - except ValueError: |
357 | - logger.exception('Can not load replications:') |
358 | - self._set_warning(self.NO_PAIRING_RECORD, self.message) |
359 | - return |
360 | - |
361 | - pkg = None |
362 | - if not self.has_bindwood: |
363 | - pkg = self.BINDWOOD_PKG |
364 | - self.bookmarks = DesktopcouchService('bookmarks', self.BOOKMARKS, |
365 | - self.replication_service, |
366 | - dependency=pkg) |
367 | - self.replications.pack_start(self.bookmarks, expand=False) |
368 | - |
369 | - pkg = None |
370 | - if not self.has_evocouch: |
371 | - pkg = self.EVOCOUCH_PKG |
372 | - self.contacts = DesktopcouchService('contacts', self.CONTACTS, |
373 | - self.replication_service, |
374 | - dependency=pkg) |
375 | - self.replications.pack_start(self.contacts, expand=False) |
376 | + for item in info: |
377 | + pkg = item['dependency'] |
378 | + child = DesktopcouchService(service_id=item['replication_id'], |
379 | + name=item['name'], # self.BOOKMARKS, |
380 | + enabled=bool(item['enabled']), |
381 | + dependency=pkg if pkg else None) |
382 | + self.replications.pack_start(child, expand=False) |
383 | + |
384 | + @log_call(logger.error) |
385 | + def on_replications_info_error(self, error_dict=None): |
386 | + """The replication info can not be retrieved.""" |
387 | + if error_dict is not None and \ |
388 | + error_dict.get('error_type', None) == 'NoPairingRecord': |
389 | + self.on_error(self.NO_PAIRING_RECORD) |
390 | + else: |
391 | + self.on_error() |
392 | |
393 | |
394 | class ManagementPanel(gtk.VBox, ControlPanelMixin): |
395 | @@ -1107,8 +1138,10 @@ |
396 | |
397 | def load(self): |
398 | """Load the account info and file sync status list.""" |
399 | - self.backend.account_info() |
400 | - self.backend.file_sync_status() |
401 | + self.backend.account_info(reply_handler=NO_OP, |
402 | + error_handler=error_handler) |
403 | + self.backend.file_sync_status(reply_handler=NO_OP, |
404 | + error_handler=error_handler) |
405 | self.dashboard_button.clicked() |
406 | |
407 | @log_call(logger.debug) |
408 | |
409 | === modified file 'ubuntuone/controlpanel/gtk/tests/__init__.py' |
410 | --- ubuntuone/controlpanel/gtk/tests/__init__.py 2010-12-23 19:17:53 +0000 |
411 | +++ ubuntuone/controlpanel/gtk/tests/__init__.py 2011-01-06 21:03:06 +0000 |
412 | @@ -53,6 +53,15 @@ |
413 | 'max_upload_speed': '1000', 'max_download_speed': '72548'}, # local |
414 | ] |
415 | |
416 | +FAKE_REPLICATIONS_INFO = [ |
417 | + {'replication_id': 'foo', 'name': 'Bar', |
418 | + 'enabled': 'True', 'dependency': ''}, |
419 | + {'replication_id': 'yadda', 'name': 'Foo', |
420 | + 'enabled': '', 'dependency': 'a very weird one'}, |
421 | + {'replication_id': 'yoda', 'name': 'Figthers', |
422 | + 'enabled': 'True', 'dependency': 'other dep'}, |
423 | +] |
424 | + |
425 | |
426 | class FakedObject(object): |
427 | """Fake an object, record every call.""" |
428 | @@ -117,9 +126,11 @@ |
429 | object_path = gui.DBUS_PREFERENCES_PATH |
430 | iface = gui.DBUS_PREFERENCES_IFACE |
431 | exposed_methods = [ |
432 | - 'account_info', 'devices_info', 'change_device_settings', |
433 | - 'volumes_info', 'change_volume_settings', 'file_sync_status', |
434 | - 'remove_device', 'enable_files', 'disable_files', |
435 | + 'account_info', # account |
436 | + 'devices_info', 'change_device_settings', 'remove_device', # devices |
437 | + 'volumes_info', 'change_volume_settings', # volumes |
438 | + 'replications_info', 'change_replication_settings', # replications |
439 | + 'file_sync_status', 'enable_files', 'disable_files', # files |
440 | ] |
441 | |
442 | |
443 | @@ -157,13 +168,3 @@ |
444 | yield |
445 | self._installed[package_name] = True |
446 | gui.package_manager.return_value(FakedTransaction([package_name])) |
447 | - |
448 | - |
449 | -class FakedReplication(object): |
450 | - """Faked a DC replication exclusion.""" |
451 | - |
452 | - def __init__(self): |
453 | - self._exclusions = set() |
454 | - self.all_exclusions = lambda: self._exclusions |
455 | - self.replicate = self._exclusions.remove |
456 | - self.exclude = self._exclusions.add |
457 | |
458 | === modified file 'ubuntuone/controlpanel/gtk/tests/test_gui.py' |
459 | --- ubuntuone/controlpanel/gtk/tests/test_gui.py 2010-12-26 15:02:52 +0000 |
460 | +++ ubuntuone/controlpanel/gtk/tests/test_gui.py 2011-01-06 21:03:06 +0000 |
461 | @@ -26,9 +26,10 @@ |
462 | |
463 | from ubuntuone.controlpanel.gtk import gui |
464 | from ubuntuone.controlpanel.gtk.tests import (FAKE_ACCOUNT_INFO, |
465 | - FAKE_VOLUMES_INFO, FAKE_DEVICE_INFO, FAKE_DEVICES_INFO, |
466 | + FAKE_DEVICE_INFO, FAKE_DEVICES_INFO, |
467 | + FAKE_VOLUMES_INFO, FAKE_REPLICATIONS_INFO, |
468 | FakedNMState, FakedSSOBackend, FakedSessionBus, FakedInterface, |
469 | - FakedPackageManager, FakedReplication, |
470 | + FakedPackageManager, |
471 | ) |
472 | from ubuntuone.controlpanel.tests import TOKEN, TestCase |
473 | from ubuntuone.controlpanel.gtk.tests.test_package_manager import ( |
474 | @@ -37,6 +38,8 @@ |
475 | |
476 | # Attribute 'yyy' defined outside __init__, access to a protected member |
477 | # pylint: disable=W0201, W0212 |
478 | +# Too many lines in module |
479 | +# pylint: disable=C0302 |
480 | |
481 | |
482 | class BaseTestCase(TestCase): |
483 | @@ -96,7 +99,9 @@ |
484 | if backend is None: |
485 | backend = self.ui.backend |
486 | self.assertIn(method_name, backend._called) |
487 | - self.assertEqual(backend._called[method_name], (args, {})) |
488 | + kwargs = {'reply_handler': gui.NO_OP, |
489 | + 'error_handler': gui.error_handler} |
490 | + self.assertEqual(backend._called[method_name], (args, kwargs)) |
491 | |
492 | def assert_warning_correct(self, warning, text): |
493 | """Check that 'warning' is visible, showing 'text'.""" |
494 | @@ -1361,9 +1366,9 @@ |
495 | """The test suite for a service.""" |
496 | |
497 | klass = gui.Service |
498 | - name = 'dc_test' |
499 | - localized_name = u'Qué lindo test!' |
500 | - kwargs = {'name': 'dc_test', 'localized_name': u'Qué lindo test!'} |
501 | + service_id = 'dc_test' |
502 | + name = u'Qué lindo test!' |
503 | + kwargs = {'service_id': service_id, 'name': name} |
504 | |
505 | def test_is_an_box(self): |
506 | """Inherits from gtk.VBox.""" |
507 | @@ -1373,30 +1378,35 @@ |
508 | """Is visible.""" |
509 | self.assertTrue(self.ui.get_visible()) |
510 | |
511 | + def test_warning_label_is_cleared(self): |
512 | + """The warning label is cleared.""" |
513 | + self.assertEqual(self.ui.warning_label.get_text(), '') |
514 | + |
515 | + def test_warning_label_packed(self): |
516 | + """The warning label is packed as child.""" |
517 | + self.assertIn(self.ui.warning_label, self.ui.get_children()) |
518 | + |
519 | def test_check_button_packed(self): |
520 | - """A check button is packed as only child.""" |
521 | + """A check button is packed as child.""" |
522 | self.assertIn(self.ui.button, self.ui.get_children()) |
523 | |
524 | def test_label(self): |
525 | """The label is set.""" |
526 | - self.assertEqual(self.localized_name, self.ui.button.get_label()) |
527 | + self.assertEqual(self.name, self.ui.button.get_label()) |
528 | |
529 | - def test_service_name(self): |
530 | - """The service_name is set.""" |
531 | - self.assertEqual(self.name, self.ui.service_name) |
532 | + def test_service_id(self): |
533 | + """The service id is set.""" |
534 | + self.assertEqual(self.service_id, self.ui.id) |
535 | |
536 | |
537 | class FilesServiceTestCase(ServiceTestCase): |
538 | """The test suite for the file sync service.""" |
539 | |
540 | klass = gui.FilesService |
541 | + service_id = 'files' |
542 | + name = gui.FilesService.FILES_SERVICE_NAME |
543 | kwargs = {} |
544 | |
545 | - def setUp(self): |
546 | - self.name = 'files' |
547 | - self.localized_name = gui.FilesService.FILES_SERVICE_NAME |
548 | - super(FilesServiceTestCase, self).setUp() |
549 | - |
550 | def test_backend_account_signals(self): |
551 | """The proper signals are connected to the backend.""" |
552 | self.assertEqual(self.ui.backend._signals['FileSyncStatusChanged'], |
553 | @@ -1461,35 +1471,36 @@ |
554 | """The test suite for a desktopcouch service.""" |
555 | |
556 | klass = gui.DesktopcouchService |
557 | + enabled = True |
558 | |
559 | def setUp(self): |
560 | - self.replication = FakedReplication() |
561 | - self.name = self.kwargs['name'] |
562 | - self.kwargs['replication_service'] = self.replication |
563 | + self.kwargs['enabled'] = self.enabled |
564 | super(DesktopcouchServiceTestCase, self).setUp() |
565 | |
566 | + def modify_settings(self): |
567 | + """Modify settings so values actually change.""" |
568 | + self.ui.button.set_active(not self.ui.button.get_active()) |
569 | + |
570 | + def test_backend_account_signals(self): |
571 | + """The proper signals are connected to the backend.""" |
572 | + self.assertEqual( |
573 | + self.ui.backend._signals['ReplicationSettingsChanged'], |
574 | + [self.ui.on_replication_settings_changed]) |
575 | + self.assertEqual( |
576 | + self.ui.backend._signals['ReplicationSettingsChangeError'], |
577 | + [self.ui.on_replication_settings_change_error]) |
578 | + |
579 | def test_active(self): |
580 | - """Is active since replication has an empty database.""" |
581 | - self.assertTrue(self.ui.button.get_active()) |
582 | - |
583 | - def test_not_active(self): |
584 | - """Is not active since 'name' is excluded on replication database.""" |
585 | - self.replication.exclude(self.name) |
586 | - self.ui = self.klass(**self.kwargs) |
587 | - self.assertFalse(self.ui.button.get_active()) |
588 | + """Is active if enabled.""" |
589 | + self.assertEqual(self.enabled, self.ui.button.get_active()) |
590 | |
591 | def test_on_button_toggled(self): |
592 | """When toggling the button, the DC exclude list is updated.""" |
593 | - assert self.ui.button.get_active() |
594 | self.ui.button.set_active(not self.ui.button.get_active()) |
595 | - self.assertEqual(set([self.name]), self.replication.all_exclusions()) |
596 | |
597 | - def test_on_button_toggled_twice(self): |
598 | - """When toggling the button twice, the DC exclude list is updated.""" |
599 | - assert self.ui.button.get_active() |
600 | - self.ui.button.set_active(not self.ui.button.get_active()) |
601 | - self.ui.button.set_active(not self.ui.button.get_active()) |
602 | - self.assertEqual(set(), self.replication.all_exclusions()) |
603 | + args = (self.service_id, |
604 | + {'enabled': gui.bool_str(self.ui.button.get_active())}) |
605 | + self.assert_backend_called('change_replication_settings', args) |
606 | |
607 | def test_dependency(self): |
608 | """The dependency box is None.""" |
609 | @@ -1499,6 +1510,72 @@ |
610 | """The check button is sensitive.""" |
611 | self.assertTrue(self.ui.button.get_sensitive()) |
612 | |
613 | + def test_on_replication_settings_changed(self): |
614 | + """When settings were changed for this replication, enable it.""" |
615 | + new_val = not self.ui.button.get_active() |
616 | + self.ui.button.set_active(new_val) |
617 | + |
618 | + self.ui.on_replication_settings_changed(replication_id=self.ui.id) |
619 | + |
620 | + self.assertEqual(self.ui.warning_label.get_text(), '') |
621 | + self.assertEqual(new_val, self.ui.button.get_active()) |
622 | + |
623 | + def test_on_replication_settings_changed_after_error(self): |
624 | + """Change success after error.""" |
625 | + self.ui.button.set_active(not self.ui.button.get_active()) |
626 | + self.ui.on_replication_settings_change_error(replication_id=self.ui.id) |
627 | + |
628 | + self.test_on_replication_settings_changed() |
629 | + |
630 | + def test_on_replication_settings_changed_different_id(self): |
631 | + """When settings were changed for other rep, nothing changes.""" |
632 | + self.ui.button.set_active(not self.ui.button.get_active()) |
633 | + self.ui.on_replication_settings_changed(replication_id='yadda') |
634 | + |
635 | + self.assertEqual(self.ui.warning_label.get_text(), '') |
636 | + |
637 | + def test_on_replication_settings_changed_different_id_after_error(self): |
638 | + """When settings were changed for other + error, nothing changes.""" |
639 | + self.ui.on_replication_settings_change_error(replication_id=self.ui.id) |
640 | + self.ui.on_replication_settings_changed(replication_id='yadda') |
641 | + |
642 | + self.assert_warning_correct(self.ui.warning_label, |
643 | + self.ui.CHANGE_ERROR) |
644 | + |
645 | + def test_on_replication_settings_change_error(self): |
646 | + """When settings were not changed, notify the user. |
647 | + |
648 | + Also, confirm that old value was restored. |
649 | + |
650 | + """ |
651 | + old_val = self.ui.button.get_active() |
652 | + self.ui.button.set_active(not old_val) |
653 | + self.ui.on_replication_settings_change_error(replication_id=self.ui.id) |
654 | + |
655 | + self.assert_warning_correct(self.ui.warning_label, |
656 | + self.ui.CHANGE_ERROR) |
657 | + self.assertEqual(old_val, self.ui.button.get_active()) |
658 | + |
659 | + def test_on_replication_settings_change_error_after_success(self): |
660 | + """Change error after success.""" |
661 | + self.ui.button.set_active(not self.ui.button.get_active()) |
662 | + self.ui.on_replication_settings_changed(replication_id=self.ui.id) |
663 | + |
664 | + self.test_on_replication_settings_change_error() |
665 | + |
666 | + def test_on_replication_settings_change_error_different_id(self): |
667 | + """When settings were not changed for other replication, do nothing.""" |
668 | + self.ui.button.set_active(not self.ui.button.get_active()) |
669 | + self.ui.on_replication_settings_change_error(replication_id='yudo') |
670 | + |
671 | + self.assertEqual(self.ui.warning_label.get_text(), '') |
672 | + |
673 | + |
674 | +class DesktopcouchServiceDisabledAtStartupTestCase(ServiceTestCase): |
675 | + """The test suite for a desktopcouch service when enabled=False.""" |
676 | + |
677 | + enabled = False |
678 | + |
679 | |
680 | class DesktopcouchServiceWithDependencyTestCase(DesktopcouchServiceTestCase): |
681 | """The test suite for a desktopcouch service when it needs a dependency.""" |
682 | @@ -1532,7 +1609,8 @@ |
683 | self.ui.dependency.emit('finished') |
684 | |
685 | self.assertTrue(self.ui.dependency is None) |
686 | - self.assertEqual(self.ui.get_children(), [self.ui.button]) |
687 | + self.assertEqual(sorted(self.ui.get_children()), |
688 | + sorted([self.ui.button, self.ui.warning_label])) |
689 | |
690 | |
691 | class ServicesTestCase(ControlPanelMixinTestCase): |
692 | @@ -1562,6 +1640,13 @@ |
693 | """The install box is None.""" |
694 | self.assertTrue(self.ui.install_box is None) |
695 | |
696 | + def test_backend_signals(self): |
697 | + """The proper signals are connected to the backend.""" |
698 | + self.assertEqual(self.ui.backend._signals['ReplicationsInfoReady'], |
699 | + [self.ui.on_replications_info_ready]) |
700 | + self.assertEqual(self.ui.backend._signals['ReplicationsInfoError'], |
701 | + [self.ui.on_replications_info_error]) |
702 | + |
703 | |
704 | class ServicesFilesTestCase(ServicesTestCase): |
705 | """The test suite for the services panel (files section).""" |
706 | @@ -1589,22 +1674,10 @@ |
707 | self.assertFalse(self.ui.message.active) |
708 | self.assertEqual(self.ui.message.get_text(), '') |
709 | |
710 | - def test_replication_service(self): |
711 | - """Has a replication service.""" |
712 | - self.assertEqual(self.ui.replication_service, None) |
713 | - |
714 | def test_has_desktopcouch(self): |
715 | """Has desktopcouch installed?""" |
716 | self.assertFalse(self.ui.has_desktopcouch) |
717 | |
718 | - def test_has_bindwood(self): |
719 | - """Has bindwood installed?""" |
720 | - self.assertFalse(self.ui.has_bindwood) |
721 | - |
722 | - def test_has_evocouch(self): |
723 | - """Has evocouch installed?""" |
724 | - self.assertFalse(self.ui.has_evocouch) |
725 | - |
726 | def test_install_box_is_hidden(self): |
727 | """The install box is not hidden.""" |
728 | self.assertTrue(self.ui.install_box.get_visible()) |
729 | @@ -1629,41 +1702,27 @@ |
730 | |
731 | self.assertEqual(self._called, ((self.ui.install_box,), {})) |
732 | |
733 | + def test_load_replications(self): |
734 | + """The load_replications starts the spinner and calls the backend.""" |
735 | + self.ui.load_replications() |
736 | + |
737 | + self.assertTrue(self.ui.message.active) |
738 | + self.assert_backend_called('replications_info', ()) |
739 | + |
740 | |
741 | class ServicesWithDesktopcouchTestCase(ServicesTestCase): |
742 | """The test suite for the services panel.""" |
743 | |
744 | - kwargs = {'replication_exclusion_class': FakedReplication} |
745 | - |
746 | def setUp(self): |
747 | super(ServicesWithDesktopcouchTestCase, self).setUp() |
748 | self.ui.package_manager._installed[self.ui.DESKTOPCOUCH_PKG] = True |
749 | - self.ui.load() |
750 | + self.ui.on_replications_info_ready(info=FAKE_REPLICATIONS_INFO) |
751 | |
752 | def test_message(self): |
753 | """Global load message is stopped and proper test is shown.""" |
754 | self.assertFalse(self.ui.message.active) |
755 | self.assertEqual(self.ui.message.get_text(), self.ui.CHOOSE_SERVICES) |
756 | |
757 | - def test_replication_service(self): |
758 | - """Has a replication service.""" |
759 | - self.assertIsInstance(self.ui.replication_service, FakedReplication) |
760 | - |
761 | - def test_no_pairing_record(self): |
762 | - """The pairing record is not in place.""" |
763 | - |
764 | - def no_pairing_record(*a): |
765 | - """Fake a ReplicationExclusion with no pairing record.""" |
766 | - raise ValueError("No pairing record for ubuntuone.") |
767 | - |
768 | - self.ui.replication_exclusion_class = no_pairing_record |
769 | - self.ui.replication_service = None |
770 | - self.ui.load() |
771 | - |
772 | - self.assertEqual(self.ui.replications.get_children(), []) |
773 | - self.assertFalse(self.ui.message.active) |
774 | - self.assert_warning_correct(self.ui.message, self.ui.NO_PAIRING_RECORD) |
775 | - |
776 | def test_has_desktopcouch(self): |
777 | """Has desktopcouch installed?""" |
778 | self.assertTrue(self.ui.has_desktopcouch) |
779 | @@ -1673,79 +1732,67 @@ |
780 | self.assertTrue(self.ui.replications.get_visible()) |
781 | |
782 | children = self.ui.replications.get_children() |
783 | - self.assertEqual(len(children), 2) |
784 | - for child in children: |
785 | + self.assertEqual(len(children), len(FAKE_REPLICATIONS_INFO)) |
786 | + for expected, child in zip(FAKE_REPLICATIONS_INFO, children): |
787 | self.assertIsInstance(child, gui.DesktopcouchService) |
788 | - |
789 | - self.assertTrue(self.ui.bookmarks is children[0]) |
790 | - self.assertTrue(self.ui.contacts is children[1]) |
791 | - |
792 | - def test_replications_after_loading_twice(self): |
793 | - """Has proper child after loading twice.""" |
794 | - self.ui.load() |
795 | + self.assertEqual(expected['replication_id'], child.id) |
796 | + self.assertEqual(expected['name'], child.button.get_label()) |
797 | + self.assertEqual(bool(expected['enabled']), |
798 | + child.button.get_active()) |
799 | + |
800 | + if expected['dependency']: |
801 | + self.assertTrue(child.dependency is not None) |
802 | + self.assertEqual(expected['dependency'], |
803 | + child.dependency.package_name) |
804 | + else: |
805 | + self.assertTrue(child.dependency is None) |
806 | + |
807 | + def test_replications_after_getting_info_twice(self): |
808 | + """Has proper child after getting backend info twice.""" |
809 | + self.ui.on_replications_info_ready(info=FAKE_REPLICATIONS_INFO) |
810 | self.test_replications() |
811 | |
812 | - def test_bookmarks(self): |
813 | - """The bookmarks is correct.""" |
814 | - self.assertEqual(self.ui.bookmarks.service_name, 'bookmarks') |
815 | - self.assertEqual(self.ui.bookmarks.button.get_label(), |
816 | - self.ui.BOOKMARKS) |
817 | - self.assertTrue(self.ui.bookmarks.replication_service is |
818 | - self.ui.replication_service) |
819 | - |
820 | - def test_bookmarks_dependency(self): |
821 | - """The bookmarks dependency is correct.""" |
822 | - self.assertTrue(self.ui.bookmarks.dependency is not None) |
823 | - self.assertEqual(self.ui.bookmarks.dependency.package_name, |
824 | - self.ui.BINDWOOD_PKG) |
825 | - |
826 | - def test_contacts(self): |
827 | - """The contacts is correct.""" |
828 | - self.assertEqual(self.ui.contacts.service_name, 'contacts') |
829 | - self.assertEqual(self.ui.contacts.button.get_label(), |
830 | - self.ui.CONTACTS) |
831 | - self.assertTrue(self.ui.contacts.replication_service is |
832 | - self.ui.replication_service) |
833 | - |
834 | - def test_contacts_dependency(self): |
835 | - """The contacts dependency is correct.""" |
836 | - self.assertTrue(self.ui.contacts.dependency is not None) |
837 | - self.assertEqual(self.ui.contacts.dependency.package_name, |
838 | - self.ui.EVOCOUCH_PKG) |
839 | - |
840 | - |
841 | -class ServicesWithDCAndBindwoodTestCase(ServicesWithDesktopcouchTestCase): |
842 | - """The test suite for the services panel.""" |
843 | - |
844 | - def setUp(self): |
845 | - super(ServicesWithDCAndBindwoodTestCase, self).setUp() |
846 | - self.ui.package_manager._installed[self.ui.BINDWOOD_PKG] = True |
847 | - self.ui.load() |
848 | - |
849 | - def test_has_bindwood(self): |
850 | - """Has bindwood installed?""" |
851 | - self.assertTrue(self.ui.has_bindwood) |
852 | - |
853 | - def test_bookmarks_dependency(self): |
854 | - """The bookmarks dependency is correct.""" |
855 | - self.assertTrue(self.ui.bookmarks.dependency is None) |
856 | - |
857 | - |
858 | -class ServicesWithDCAndEvocouchTestCase(ServicesWithDesktopcouchTestCase): |
859 | - """The test suite for the services panel.""" |
860 | - |
861 | - def setUp(self): |
862 | - super(ServicesWithDCAndEvocouchTestCase, self).setUp() |
863 | - self.ui.package_manager._installed[self.ui.EVOCOUCH_PKG] = True |
864 | - self.ui.load() |
865 | - |
866 | - def test_has_evocouch(self): |
867 | - """Has evocoucg installed?""" |
868 | - self.assertTrue(self.ui.has_evocouch) |
869 | - |
870 | - def test_contacts_dependency(self): |
871 | - """The bookmarks dependency is correct.""" |
872 | - self.assertTrue(self.ui.contacts.dependency is None) |
873 | + |
874 | +class ServicesWithDesktopcouchErrorTestCase(ServicesTestCase): |
875 | + """The test suite for the services panel.""" |
876 | + |
877 | + def setUp(self): |
878 | + super(ServicesWithDesktopcouchErrorTestCase, self).setUp() |
879 | + self.ui.package_manager._installed[self.ui.DESKTOPCOUCH_PKG] = True |
880 | + |
881 | + def test_no_pairing_record(self): |
882 | + """The pairing record is not in place.""" |
883 | + error_dict = {'error_type': 'NoPairingRecord'} |
884 | + self.ui.on_replications_info_error(error_dict) |
885 | + |
886 | + self.assertEqual(self.ui.replications.get_children(), []) |
887 | + self.assertFalse(self.ui.message.active) |
888 | + self.assert_warning_correct(self.ui.message, self.ui.NO_PAIRING_RECORD) |
889 | + |
890 | + def test_other_error(self): |
891 | + """There was an error other than no pairing record.""" |
892 | + error_dict = {'error_type': 'OtherError'} |
893 | + self.ui.on_replications_info_error(error_dict) |
894 | + |
895 | + self.assertEqual(self.ui.replications.get_children(), []) |
896 | + self.assertFalse(self.ui.message.active) |
897 | + self.assert_warning_correct(self.ui.message, gui.VALUE_ERROR) |
898 | + |
899 | + def test_empty_dict(self): |
900 | + """Handle empty dicts errors.""" |
901 | + self.ui.on_replications_info_error(error_dict={}) |
902 | + |
903 | + self.assertEqual(self.ui.replications.get_children(), []) |
904 | + self.assertFalse(self.ui.message.active) |
905 | + self.assert_warning_correct(self.ui.message, gui.VALUE_ERROR) |
906 | + |
907 | + def test_error_dict_none(self): |
908 | + """HGandle empty dicts errors.""" |
909 | + self.ui.on_replications_info_error(error_dict=None) |
910 | + |
911 | + self.assertEqual(self.ui.replications.get_children(), []) |
912 | + self.assertFalse(self.ui.message.active) |
913 | + self.assert_warning_correct(self.ui.message, gui.VALUE_ERROR) |
914 | |
915 | |
916 | class ManagementPanelTestCase(ControlPanelMixinTestCase): |
917 | |
918 | === modified file 'ubuntuone/controlpanel/logger.py' |
919 | --- ubuntuone/controlpanel/logger.py 2010-12-23 18:20:56 +0000 |
920 | +++ ubuntuone/controlpanel/logger.py 2011-01-06 21:03:06 +0000 |
921 | @@ -53,10 +53,7 @@ |
922 | logger.addHandler(MAIN_HANDLER) |
923 | if os.environ.get('DEBUG'): |
924 | debug_handler = logging.StreamHandler(sys.stderr) |
925 | - if prefix is not None: |
926 | - fmt = prefix + "%(name)s - %(levelname)s\n%(message)s\n" |
927 | - formatter = logging.Formatter(fmt) |
928 | - debug_handler.setFormatter(formatter) |
929 | + debug_handler.setFormatter(basic_formatter) |
930 | logger.addHandler(debug_handler) |
931 | |
932 | return logger |
Works as advertised, code looks good. I don't like the desktopcouch pairing functionality as it stands, but that's in the design, so we'll discuss elsewhere.