Merge lp:~statik/ubuntu/maverick/magicicada/zero-one-two into lp:ubuntu/maverick/magicicada
- Maverick (10.10)
- zero-one-two
- Merge into maverick
Proposed by
Elliot Murphy
Status: | Merged |
---|---|
Merged at revision: | 3 |
Proposed branch: | lp:~statik/ubuntu/maverick/magicicada/zero-one-two |
Merge into: | lp:ubuntu/maverick/magicicada |
Diff against target: |
3256 lines (+1233/-512) 17 files modified
PKG-INFO (+3/-3) README.txt (+13/-0) bin/magicicada (+17/-13) data/ui/gui.glade (+70/-8) debian/changelog (+6/-0) magicicada/__init__.py (+129/-33) magicicada/cmd_pof.py (+0/-190) magicicada/dbusiface.py (+58/-10) magicicada/helpers.py (+38/-24) magicicada/logger.py (+0/-1) magicicada/magicicadaconfig.py (+5/-6) magicicada/syncdaemon.py (+48/-26) magicicada/tests/helpers.py (+7/-3) magicicada/tests/test_dbusiface.py (+160/-49) magicicada/tests/test_magicicada.py (+502/-87) magicicada/tests/test_syncdaemon.py (+152/-41) setup.py (+25/-18) |
To merge this branch: | bzr merge lp:~statik/ubuntu/maverick/magicicada/zero-one-two |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu branches | Pending | ||
Review via email: mp+29596@code.launchpad.net |
Commit message
Description of the change
New upstream release of Magicicada. I've testbuilt and installed, and it upgrades and runs just fine.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'PKG-INFO' |
2 | --- PKG-INFO 2010-06-08 10:53:02 +0000 |
3 | +++ PKG-INFO 2010-07-09 18:33:43 +0000 |
4 | @@ -1,7 +1,7 @@ |
5 | Metadata-Version: 1.1 |
6 | Name: magicicada |
7 | -Version: 0.1.1 |
8 | -Summary: A GTK+ frontend for the "Chicharra" part of Ubuntu One client. |
9 | +Version: 0.1.2 |
10 | +Summary: A GTK+ frontend for the "Chicharra" part of Ubuntu One. |
11 | Home-page: https://launchpad.net/magicicada |
12 | Author: Natalia Bidart |
13 | Author-email: natalia.bidart@ubuntu.com |
14 | @@ -14,6 +14,6 @@ |
15 | Requires: pango |
16 | Requires: twisted.internet |
17 | Requires: twisted.trial.unittest |
18 | -Requires: ubuntuone.syncdaemon |
19 | +Requires: ubuntuone.syncdaemon.tools |
20 | Requires: xdg.BaseDirectory |
21 | Provides: magicicada |
22 | |
23 | === modified file 'README.txt' |
24 | --- README.txt 2010-06-08 10:53:02 +0000 |
25 | +++ README.txt 2010-07-09 18:33:43 +0000 |
26 | @@ -1,3 +1,16 @@ |
27 | This is Magicicada! |
28 | |
29 | A GTK+ frontend for the "Chicharra" part of Ubuntu One client. |
30 | + |
31 | +----------- |
32 | +HOWTO do a source release: |
33 | + |
34 | + * edit setup.py and increment the version number. |
35 | + * 'python setup.py sdist' |
36 | + * look at the contents of the tarball created in dist/ to be sure they are ok |
37 | + * sign the tarball by a command like: |
38 | + gpg -a --detach-sign magicicada-0.2.tar.gz |
39 | + this should create a file like magicicada-0.2.tar.gz.asc |
40 | + * Upload the new release to launchpad with a command like: |
41 | + lp-project-upload magicicada 0.2 magicicada-0.2.tar.gz |
42 | + * Announce the release, ping someone to build updated packages for the PPA and Ubuntu. |
43 | |
44 | === modified file 'bin/magicicada' |
45 | --- bin/magicicada 2010-06-08 10:53:02 +0000 |
46 | +++ bin/magicicada 2010-07-09 18:33:43 +0000 |
47 | @@ -1,8 +1,21 @@ |
48 | #!/usr/bin/python |
49 | # -*- coding: utf-8 -*- |
50 | -### BEGIN LICENSE |
51 | -# This file is in the public domain |
52 | -### END LICENSE |
53 | +# |
54 | +# Copyright 2010 Chicharreros |
55 | +# |
56 | +# This program is free software: you can redistribute it and/or modify it |
57 | +# under the terms of the GNU General Public License version 3, as published |
58 | +# by the Free Software Foundation. |
59 | +# |
60 | +# This program is distributed in the hope that it will be useful, but |
61 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
62 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
63 | +# PURPOSE. See the GNU General Public License for more details. |
64 | +# |
65 | +# You should have received a copy of the GNU General Public License along |
66 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
67 | + |
68 | +"""Script to run Magicicada.""" |
69 | |
70 | import sys |
71 | import os |
72 | @@ -11,14 +24,6 @@ |
73 | from gettext import gettext as _ |
74 | gettext.textdomain('magicicada') |
75 | |
76 | -# optional Launchpad integration |
77 | -# this shouldn't crash if not found as it is simply used for bug reporting |
78 | -try: |
79 | - import LaunchpadIntegration |
80 | - launchpad_available = True |
81 | -except: |
82 | - launchpad_available = False |
83 | - |
84 | # Add project root directory (enable symlink, and trunk execution). |
85 | PROJECT_ROOT_DIRECTORY = os.path.abspath( |
86 | os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0])))) |
87 | @@ -48,6 +53,5 @@ |
88 | from twisted.internet import reactor |
89 | |
90 | # Run the application. |
91 | - window = MagicicadaUI(launchpad_available=launchpad_available, |
92 | - on_destroy=lambda *a, **kw: reactor.stop()) |
93 | + window = MagicicadaUI(on_destroy=lambda *a, **kw: reactor.stop()) |
94 | reactor.run() |
95 | |
96 | === added symlink 'data/media/icon.png' |
97 | === target is u'logo-032.png' |
98 | === added directory 'data/tests' |
99 | === added file 'data/tests/metadata-test.txt' |
100 | === modified file 'data/ui/gui.glade' |
101 | --- data/ui/gui.glade 2010-06-08 10:53:02 +0000 |
102 | +++ data/ui/gui.glade 2010-07-09 18:33:43 +0000 |
103 | @@ -257,6 +257,7 @@ |
104 | </child> |
105 | <child> |
106 | <object class="GtkToolButton" id="raw_metadata"> |
107 | + <property name="visible">True</property> |
108 | <property name="sensitive">False</property> |
109 | <property name="label" translatable="yes">Metadata</property> |
110 | <property name="use_underline">True</property> |
111 | @@ -384,7 +385,7 @@ |
112 | <child> |
113 | <object class="GtkCellRendererText" id="cellrenderertext1"/> |
114 | <attributes> |
115 | - <attribute name="text">0</attribute> |
116 | + <attribute name="markup">0</attribute> |
117 | </attributes> |
118 | </child> |
119 | </object> |
120 | @@ -396,7 +397,7 @@ |
121 | <child> |
122 | <object class="GtkCellRendererText" id="cellrenderertext2"/> |
123 | <attributes> |
124 | - <attribute name="text">1</attribute> |
125 | + <attribute name="markup">1</attribute> |
126 | </attributes> |
127 | </child> |
128 | </object> |
129 | @@ -408,7 +409,7 @@ |
130 | <child> |
131 | <object class="GtkCellRendererText" id="cellrenderertext4"/> |
132 | <attributes> |
133 | - <attribute name="text">2</attribute> |
134 | + <attribute name="markup">2</attribute> |
135 | </attributes> |
136 | </child> |
137 | </object> |
138 | @@ -420,7 +421,7 @@ |
139 | <child> |
140 | <object class="GtkCellRendererText" id="cellrenderertext3"/> |
141 | <attributes> |
142 | - <attribute name="text">3</attribute> |
143 | + <attribute name="markup">3</attribute> |
144 | </attributes> |
145 | </child> |
146 | </object> |
147 | @@ -476,7 +477,7 @@ |
148 | <child> |
149 | <object class="GtkCellRendererText" id="cellrenderertext5"/> |
150 | <attributes> |
151 | - <attribute name="text">0</attribute> |
152 | + <attribute name="markup">0</attribute> |
153 | </attributes> |
154 | </child> |
155 | </object> |
156 | @@ -488,7 +489,7 @@ |
157 | <child> |
158 | <object class="GtkCellRendererText" id="cellrenderertext6"/> |
159 | <attributes> |
160 | - <attribute name="text">1</attribute> |
161 | + <attribute name="markup">1</attribute> |
162 | </attributes> |
163 | </child> |
164 | </object> |
165 | @@ -500,7 +501,7 @@ |
166 | <child> |
167 | <object class="GtkCellRendererText" id="cellrenderertext8"/> |
168 | <attributes> |
169 | - <attribute name="text">2</attribute> |
170 | + <attribute name="markup">2</attribute> |
171 | </attributes> |
172 | </child> |
173 | </object> |
174 | @@ -512,7 +513,7 @@ |
175 | <child> |
176 | <object class="GtkCellRendererText" id="cellrenderertext7"/> |
177 | <attributes> |
178 | - <attribute name="text">3</attribute> |
179 | + <attribute name="markup">3</attribute> |
180 | </attributes> |
181 | </child> |
182 | </object> |
183 | @@ -1051,4 +1052,65 @@ |
184 | <action-widget response="0">shares_to_others_close</action-widget> |
185 | </action-widgets> |
186 | </object> |
187 | + <object class="GtkFileChooserDialog" id="file_chooser"> |
188 | + <property name="border_width">5</property> |
189 | + <property name="type_hint">normal</property> |
190 | + <property name="has_separator">False</property> |
191 | + <property name="create_folders">False</property> |
192 | + <signal name="file_activated" handler="on_file_chooser_open_clicked"/> |
193 | + <child internal-child="vbox"> |
194 | + <object class="GtkVBox" id="dialog-vbox7"> |
195 | + <property name="visible">True</property> |
196 | + <property name="spacing">2</property> |
197 | + <child> |
198 | + <placeholder/> |
199 | + </child> |
200 | + <child internal-child="action_area"> |
201 | + <object class="GtkHButtonBox" id="dialog-action_area7"> |
202 | + <property name="visible">True</property> |
203 | + <property name="layout_style">end</property> |
204 | + <child> |
205 | + <object class="GtkButton" id="file_chooser_cancel"> |
206 | + <property name="label">gtk-cancel</property> |
207 | + <property name="visible">True</property> |
208 | + <property name="can_focus">True</property> |
209 | + <property name="receives_default">True</property> |
210 | + <property name="use_stock">True</property> |
211 | + </object> |
212 | + <packing> |
213 | + <property name="expand">False</property> |
214 | + <property name="fill">False</property> |
215 | + <property name="position">0</property> |
216 | + </packing> |
217 | + </child> |
218 | + <child> |
219 | + <object class="GtkButton" id="file_chooser_open"> |
220 | + <property name="label">gtk-open</property> |
221 | + <property name="visible">True</property> |
222 | + <property name="can_focus">True</property> |
223 | + <property name="receives_default">True</property> |
224 | + <property name="use_stock">True</property> |
225 | + <signal name="clicked" handler="on_file_chooser_open_clicked"/> |
226 | + <signal name="activate" handler="on_file_chooser_open_clicked"/> |
227 | + </object> |
228 | + <packing> |
229 | + <property name="expand">False</property> |
230 | + <property name="fill">False</property> |
231 | + <property name="position">1</property> |
232 | + </packing> |
233 | + </child> |
234 | + </object> |
235 | + <packing> |
236 | + <property name="expand">False</property> |
237 | + <property name="pack_type">end</property> |
238 | + <property name="position">0</property> |
239 | + </packing> |
240 | + </child> |
241 | + </object> |
242 | + </child> |
243 | + <action-widgets> |
244 | + <action-widget response="-6">file_chooser_cancel</action-widget> |
245 | + <action-widget response="0">file_chooser_open</action-widget> |
246 | + </action-widgets> |
247 | + </object> |
248 | </interface> |
249 | |
250 | === modified file 'debian/changelog' |
251 | --- debian/changelog 2010-06-08 10:53:02 +0000 |
252 | +++ debian/changelog 2010-07-09 18:33:43 +0000 |
253 | @@ -1,3 +1,9 @@ |
254 | +magicicada (0.1.2-0ubuntu1) maverick; urgency=low |
255 | + |
256 | + * New upstream release. |
257 | + |
258 | + -- Elliot Murphy <elliot@ubuntu.com> Fri, 09 Jul 2010 14:14:53 -0400 |
259 | + |
260 | magicicada (0.1.1-0ubuntu1) maverick; urgency=low |
261 | |
262 | [ Elliot Murphy ] |
263 | |
264 | === modified file 'magicicada/__init__.py' |
265 | --- magicicada/__init__.py 2010-06-08 10:53:02 +0000 |
266 | +++ magicicada/__init__.py 2010-07-09 18:33:43 +0000 |
267 | @@ -18,33 +18,52 @@ |
268 | |
269 | """Magicicada.""" |
270 | |
271 | -import gtk |
272 | -import sys |
273 | +import logging |
274 | +import os |
275 | |
276 | import gettext |
277 | -from gettext import gettext as _ |
278 | +_ = gettext.gettext |
279 | gettext.textdomain('magicicada') |
280 | |
281 | -from twisted.internet import gtk2reactor # for gtk-2.0 |
282 | +import gtk |
283 | + |
284 | +# optional Launchpad integration |
285 | +# this shouldn't crash if not found as it is simply used for bug reporting |
286 | +try: |
287 | + import LaunchpadIntegration |
288 | + launchpad_available = True |
289 | +except ImportError: |
290 | + launchpad_available = False |
291 | + |
292 | +from twisted.internet import gtk2reactor # for gtk-2.0 |
293 | gtk2reactor.install() |
294 | |
295 | -from magicicada import syncdaemon, logger |
296 | -from magicicada.helpers import humanize_bytes, get_data_file, get_builder, NO_OP |
297 | +from magicicada import syncdaemon, logger as logger_helper |
298 | +from magicicada.helpers import humanize_bytes, get_data_file, get_builder, \ |
299 | + log, NO_OP |
300 | |
301 | CONTENT_QUEUE = 'content' |
302 | META_QUEUE = 'meta' |
303 | +UBUNTU_ONE_ROOT = os.path.expanduser('~/Ubuntu One') |
304 | |
305 | # set up the logging for all the project |
306 | -logger.set_up() |
307 | +logger_helper.set_up() |
308 | +logger = logging.getLogger('magicicada.ui') |
309 | +console = logging.StreamHandler() |
310 | +console.setLevel(logging.DEBUG) |
311 | +#logger.addHandler(console) |
312 | + |
313 | |
314 | class MagicicadaUI(object): |
315 | + """Magicicada GUI main class.""" |
316 | |
317 | - STATUS_JOINER = " - " |
318 | + CURRENT_ROW = '<b><span foreground="#000099">%s</span></b>' |
319 | + STATUS_JOINER = ' - ' |
320 | STATUS = { |
321 | 'initial': _('Service is not started, click Start to continue.'), |
322 | } |
323 | |
324 | - def __init__(self, launchpad_available=False, on_destroy=NO_OP, |
325 | + def __init__(self, on_destroy=NO_OP, |
326 | syncdaemon_class=syncdaemon.SyncDaemon): |
327 | """Init.""" |
328 | self.builder = get_builder('gui.glade') |
329 | @@ -66,27 +85,28 @@ |
330 | self.active_indicator = gtk.gdk.pixbuf_new_from_file(active_filename) |
331 | |
332 | widgets = ( |
333 | - 'start', 'stop', 'connect', 'disconnect', # toolbar buttons |
334 | - 'folders', 'folders_dialog', # folders |
335 | + 'start', 'stop', 'connect', 'disconnect', # toolbar buttons |
336 | + 'folders', 'folders_dialog', # folders |
337 | 'folders_store', 'folders_close', |
338 | - 'shares_to_me', 'shares_to_me_dialog', # shares_to_me |
339 | + 'shares_to_me', 'shares_to_me_dialog', # shares_to_me |
340 | 'shares_to_me_store', 'shares_to_me_close', |
341 | - 'shares_to_others', 'shares_to_others_dialog', # shares_to_others |
342 | + 'shares_to_others', 'shares_to_others_dialog', # shares_to_others |
343 | 'shares_to_others_store', 'shares_to_others_close', |
344 | - 'is_started', 'is_connected', 'is_online', # status bar images |
345 | - 'status_label', 'status_icon', # status label and systray icon |
346 | - 'metaq_view', 'contentq_view', # queues tree views |
347 | - 'metaq_store', 'contentq_store', # queues list stores |
348 | - 'metaq_label', 'contentq_label', # queues labels |
349 | - 'raw_metadata', # raw metadata |
350 | - 'about_dialog', # dialogs |
351 | - 'main_window' |
352 | - ) |
353 | + 'raw_metadata', # raw metadata |
354 | + 'is_started', 'is_connected', 'is_online', # status bar images |
355 | + 'status_label', 'status_icon', # status label and systray icon |
356 | + 'metaq_view', 'contentq_view', # queues tree views |
357 | + 'metaq_store', 'contentq_store', # queues list stores |
358 | + 'metaq_label', 'contentq_label', # queues labels |
359 | + 'file_chooser', 'file_chooser_open', 'file_chooser_cancel', |
360 | + 'about_dialog', 'main_window') |
361 | + |
362 | for widget in widgets: |
363 | obj = self.builder.get_object(widget) |
364 | setattr(self, widget, obj) |
365 | assert obj is not None, '%s must not be None' % widget |
366 | |
367 | + self.raw_metadata_dialog = self._new_metadata_dialog() |
368 | self.volumes = (self.folders, self.shares_to_me, self.shares_to_others) |
369 | self.windows = (self.main_window, self.about_dialog, |
370 | self.folders_dialog) |
371 | @@ -97,8 +117,8 @@ |
372 | for w in self.windows: |
373 | w.set_icon(self._icon) |
374 | |
375 | - about_filename = get_data_file('media', 'logo-128.png') |
376 | - self.about_dialog.set_logo(gtk.gdk.pixbuf_new_from_file(about_filename)) |
377 | + about_fname = get_data_file('media', 'logo-128.png') |
378 | + self.about_dialog.set_logo(gtk.gdk.pixbuf_new_from_file(about_fname)) |
379 | |
380 | self.sd = syncdaemon_class() |
381 | self.sd.on_started_callback = self.on_started |
382 | @@ -110,13 +130,44 @@ |
383 | self.sd.status_changed_callback = self.on_status_changed |
384 | self.sd.content_queue_changed_callback = self.on_content_queue_changed |
385 | self.sd.meta_queue_changed_callback = self.on_meta_queue_changed |
386 | + self.sd.on_metadata_ready_callback = self.on_metadata_ready |
387 | |
388 | self.widget_is_visible = lambda w: w.get_property('visible') |
389 | self.widget_enabled = lambda w: self.widget_is_visible(w) and \ |
390 | w.is_sensitive() |
391 | - |
392 | + self.file_chooser.set_current_folder(UBUNTU_ONE_ROOT) |
393 | + self.last_metadata_path = None |
394 | self.update() |
395 | |
396 | + def _new_metadata_dialog(self): |
397 | + """Return a new metadata dialog.""" |
398 | + dialog = gtk.Dialog(title='Raw metadata', parent=self.main_window, |
399 | + flags=gtk.DIALOG_NO_SEPARATOR, |
400 | + buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)) |
401 | + dialog.set_size_request(600, 300) |
402 | + dialog.set_position(gtk.WIN_POS_CENTER) |
403 | + setattr(self, 'raw_metadata_dialog', dialog) |
404 | + |
405 | + close_button = dialog.action_area.get_children()[-1] |
406 | + close_button.connect('clicked', self.on_raw_metadata_close_clicked) |
407 | + close_button.connect('activate', self.on_raw_metadata_close_clicked) |
408 | + setattr(self, 'raw_metadata_close', close_button) |
409 | + |
410 | + image = gtk.Image() |
411 | + image.set_from_animation(self.loading_animation) |
412 | + setattr(self, 'raw_metadata_image', image) |
413 | + |
414 | + dialog.get_child().add(image) |
415 | + |
416 | + text_view = gtk.TextView() |
417 | + text_view.set_editable(False) |
418 | + text_view.set_wrap_mode(gtk.WRAP_WORD) |
419 | + dialog.get_child().add(text_view) |
420 | + setattr(self, 'raw_metadata_view', text_view) |
421 | + |
422 | + dialog.hide() # XXX to be fixed later |
423 | + return dialog |
424 | + |
425 | # GTK callbacks |
426 | |
427 | def on_main_window_destroy(self, widget, data=None): |
428 | @@ -131,7 +182,7 @@ |
429 | |
430 | def on_about_activate(self, widget, data=None): |
431 | """Display the about box.""" |
432 | - response = self.about_dialog.run() |
433 | + self.about_dialog.run() |
434 | self.about_dialog.hide() |
435 | |
436 | def on_start_clicked(self, widget, data=None): |
437 | @@ -144,6 +195,7 @@ |
438 | """Stop syncdaemon.""" |
439 | for v in self.volumes: |
440 | v.set_sensitive(False) |
441 | + self.raw_metadata.set_sensitive(False) |
442 | |
443 | if self.widget_enabled(self.disconnect): |
444 | self.on_disconnect_clicked(self.disconnect) |
445 | @@ -179,13 +231,14 @@ |
446 | item.subscribed, item.volume) |
447 | self.folders_store.append(row) |
448 | |
449 | - res = self.folders_dialog.run() |
450 | + self.folders_dialog.run() |
451 | self.folders_dialog.hide() |
452 | |
453 | def on_shares_to_me_close_clicked(self, widget, data=None): |
454 | """Close the shares_to_me dialog.""" |
455 | self.shares_to_me_dialog.response(gtk.RESPONSE_CLOSE) |
456 | |
457 | + @log(logger) |
458 | def _on_shares_clicked(self, items, store, dialog): |
459 | """List shares to the user or to others.""" |
460 | if items is None: |
461 | @@ -201,7 +254,7 @@ |
462 | item.path, item.volume_id) |
463 | store.append(row) |
464 | |
465 | - res = dialog.run() |
466 | + dialog.run() |
467 | dialog.hide() |
468 | |
469 | def on_shares_to_me_clicked(self, widget, data=None): |
470 | @@ -220,8 +273,27 @@ |
471 | self.shares_to_others_store, |
472 | self.shares_to_others_dialog) |
473 | |
474 | + def on_file_chooser_open_clicked(self, widget, data=None): |
475 | + """Close the file_chooser dialog.""" |
476 | + self.file_chooser.response(gtk.FILE_CHOOSER_ACTION_OPEN) |
477 | + |
478 | + def on_raw_metadata_close_clicked(self, widget, data=None): |
479 | + """Close the raw_metadata dialog.""" |
480 | + self.raw_metadata_dialog.hide() |
481 | + |
482 | def on_raw_metadata_clicked(self, widget, data=None): |
483 | """Show raw metadata for a path choosen by the user.""" |
484 | + res = self.file_chooser.run() |
485 | + self.file_chooser.hide() |
486 | + if res != gtk.FILE_CHOOSER_ACTION_OPEN: |
487 | + return |
488 | + |
489 | + self.last_metadata_path = self.file_chooser.get_filename() |
490 | + self.sd.get_metadata(path=self.last_metadata_path) |
491 | + self.raw_metadata_view.hide() |
492 | + self.raw_metadata_image.show() |
493 | + self._start_loading(self.raw_metadata_image) |
494 | + self.raw_metadata_dialog.show() |
495 | |
496 | def on_status_icon_activate(self, widget, data=None): |
497 | """Systray icon was clicked.""" |
498 | @@ -242,6 +314,7 @@ |
499 | |
500 | for v in self.volumes: |
501 | v.set_sensitive(True) |
502 | + self.raw_metadata.set_sensitive(True) |
503 | |
504 | self._update_queues_and_status(self.sd.current_state) |
505 | |
506 | @@ -285,6 +358,7 @@ |
507 | """Callback'ed when syncadaemon is offline.""" |
508 | self._activate_indicator(self.is_online, sensitive=False) |
509 | |
510 | + @log(logger) |
511 | def on_status_changed(self, name=None, description=None, |
512 | is_error=False, is_connected=True, is_online=True, |
513 | queues=None, connection=None): |
514 | @@ -295,17 +369,22 @@ |
515 | text = self.STATUS['initial'] |
516 | self.status_label.set_text(text) |
517 | |
518 | - def _on_queue_changed(self, queue_name, items, *args, **kwargs): |
519 | + @log(logger) |
520 | + def _on_queue_changed(self, queue_name, items, must_highlight): |
521 | """Callback'ed when a queue changed.""" |
522 | if items is None: |
523 | items = [] |
524 | |
525 | + markup = lambda value: self.CURRENT_ROW % value \ |
526 | + if must_highlight and value is not None else value |
527 | queue_label = getattr(self, '%sq_label' % queue_name) |
528 | queue_view = getattr(self, '%sq_view' % queue_name) |
529 | queue_store = getattr(self, '%sq_store' % queue_name) |
530 | queue_store.clear() |
531 | - for item in items: |
532 | + for i, item in enumerate(items): |
533 | row = (item.operation, item.path, item.share, item.node) |
534 | + if i == 0: |
535 | + row = map(markup, row) |
536 | queue_store.append(row) |
537 | |
538 | items_len = len(items) |
539 | @@ -317,11 +396,27 @@ |
540 | |
541 | def on_content_queue_changed(self, items, *args, **kwargs): |
542 | """Callback'ed when syncadaemon's content queue changed.""" |
543 | - self._on_queue_changed(CONTENT_QUEUE, items, args, kwargs) |
544 | + state = self.sd.current_state |
545 | + highlight = state.processing_content and state.is_online |
546 | + self._on_queue_changed(CONTENT_QUEUE, items, highlight) |
547 | |
548 | def on_meta_queue_changed(self, items, *args, **kwargs): |
549 | """Callback'ed when syncadaemon's meta queue changed.""" |
550 | - self._on_queue_changed(META_QUEUE, items, args, kwargs) |
551 | + state = self.sd.current_state |
552 | + highlight = state.processing_meta and state.is_online |
553 | + self._on_queue_changed(META_QUEUE, items, highlight) |
554 | + |
555 | + @log(logger) |
556 | + def on_metadata_ready(self, path, metadata): |
557 | + """Lower layer has the requested metadata for 'path'.""" |
558 | + logger.debug('on_metadata_ready: path: %r, last_metadata_path: %r', |
559 | + path, self.last_metadata_path) |
560 | + if path != self.last_metadata_path: |
561 | + return |
562 | + self.raw_metadata_image.hide() |
563 | + self.raw_metadata_view.show() |
564 | + text = '\n'.join('%s: %s' % i for i in metadata.iteritems()) |
565 | + self.raw_metadata_view.get_buffer().set_text(text) |
566 | |
567 | # custom |
568 | |
569 | @@ -340,7 +435,8 @@ |
570 | self.on_meta_queue_changed(self.sd.meta_queue) |
571 | self.on_content_queue_changed(self.sd.content_queue) |
572 | self.on_status_changed(name=state.name, description=state.description, |
573 | - queues=state.queues, connection=state.connection) |
574 | + queues=state.queues, |
575 | + connection=state.connection) |
576 | |
577 | def update(self): |
578 | """Update UI based on SD current state.""" |
579 | |
580 | === removed file 'magicicada/cmd_pof.py' |
581 | --- magicicada/cmd_pof.py 2010-06-08 10:53:02 +0000 |
582 | +++ magicicada/cmd_pof.py 1970-01-01 00:00:00 +0000 |
583 | @@ -1,190 +0,0 @@ |
584 | -# DISCLAIMER: this is a proof of concept, we need tests for this! |
585 | -# Author: Facundo Batista |
586 | - |
587 | -# this always first |
588 | -from twisted.internet import glib2reactor |
589 | -glib2reactor.install() |
590 | - |
591 | -import functools |
592 | -import re |
593 | -import time |
594 | - |
595 | -import dbus |
596 | -from dbus.mainloop.glib import DBusGMainLoop |
597 | -from twisted.internet import reactor |
598 | - |
599 | -from ubuntuone.syncdaemon import tools |
600 | - |
601 | -# main connected stuff |
602 | -loop = DBusGMainLoop(set_as_default=True) |
603 | -bus = dbus.SessionBus(mainloop=loop) |
604 | -sync_daemon_tool = tools.SyncDaemonTool(bus) |
605 | - |
606 | -DATE_FMT = "%Y-%m-%d" |
607 | -TIME_FMT = "%H:%M:%S" |
608 | - |
609 | -RE_OP_LISTDIR = re.compile("ListDir\(share_id=(.*?), node_id=(.*?), .*") |
610 | -RE_OP_UNLINK = re.compile("Unlink\(share_id=(.*?), node_id=(.*?), .*") |
611 | -RE_OP_MAKEFILE = re.compile( |
612 | - "MakeFile\(share_id=(.*?), parent_id=(.*?), name=(.*?), .*") |
613 | - |
614 | -class DBusHandler(object): |
615 | - def __init__(self): |
616 | - # status changed |
617 | - bus.add_signal_receiver(self.on_status_changed, |
618 | - dbus_interface='com.ubuntuone.SyncDaemon.Status', |
619 | - signal_name='StatusChanged') |
620 | - # content queue changed |
621 | - bus.add_signal_receiver(self.on_CQ_changed, |
622 | - dbus_interface='com.ubuntuone.SyncDaemon.Status', |
623 | - signal_name='ContentQueueChanged') |
624 | - |
625 | - # upload or download |
626 | - for s in ('DownloadStarted', 'DownloadFinished', |
627 | - 'UploadStarted', 'UploadFinished'): |
628 | - bus.add_signal_receiver(functools.partial(self.on_updown_activ, s), |
629 | - signal_name=s) |
630 | - |
631 | - # get the first one |
632 | - d = sync_daemon_tool.get_status() |
633 | - d.addCallback(self.on_status_changed) |
634 | - |
635 | - self._date = None |
636 | - self._last_CQ_data = None |
637 | - self._last_MQ_data = None |
638 | - self._state = None |
639 | - self._mqcaller = None |
640 | - |
641 | - def on_updown_activ(self, signal, *data): |
642 | - """Upload or download activity.""" |
643 | - path = data[0] |
644 | - if signal in ('UploadStarted', 'DownloadStarted'): |
645 | - self._show("{0}: {1}".format(signal, path)) |
646 | - else: |
647 | - self._show("{0}: {1}".format(signal, path)) |
648 | - |
649 | - def on_CQ_changed(self, data): |
650 | - """Content Queue changed, update it.""" |
651 | - def show_cq(data): |
652 | - """Show the CONTENT queue.""" |
653 | - if data == self._last_CQ_data: |
654 | - return |
655 | - |
656 | - # it changed! |
657 | - self._last_CQ_data = data |
658 | - self._show("Content Queue:") |
659 | - if data: |
660 | - for d in data: |
661 | - print " ", self._format_CQ_data(d) |
662 | - else: |
663 | - print " (empty)" |
664 | - |
665 | - d = sync_daemon_tool.waiting_content() |
666 | - d.addCallback(show_cq) |
667 | - |
668 | - def _show(self, message): |
669 | - date = time.strftime(DATE_FMT) |
670 | - if date != self._date: |
671 | - self._date = date |
672 | - print "-- {0} --".format(self._date) |
673 | - |
674 | - now = time.strftime(TIME_FMT) |
675 | - print "{0} {1}".format(now, message) |
676 | - |
677 | - def _format_MQ_data(self, data): |
678 | - """Format the meta queue information.""" |
679 | - if data.startswith("ListDir"): |
680 | - m = RE_OP_LISTDIR.match(data) |
681 | - if not m: |
682 | - m = "Got a ListDir, but failed to match: %r" % (data,) |
683 | - raise ValueError(m) |
684 | - share_id, node_id = m.groups() |
685 | - name = self._find_out_path(share_id, node_id) |
686 | - return "ListDir: {0} ({1}:{2})".format(name, share_id, node_id) |
687 | - |
688 | - if data.startswith("Unlink"): |
689 | - m = RE_OP_UNLINK.match(data) |
690 | - if not m: |
691 | - m = "Got an Unlink, but failed to match: %r" % (data,) |
692 | - raise ValueError(m) |
693 | - share_id, node_id = m.groups() |
694 | - name = self._find_out_path(share_id, node_id) |
695 | - return "Unlink: {0} ({1}:{2})".format(name, share_id, node_id) |
696 | - |
697 | - if data.startswith("MakeFile"): |
698 | - m = RE_OP_MAKEFILE.match(data) |
699 | - if not m: |
700 | - m = "Got a Makefile, but failed to match: %r" % (data,) |
701 | - raise ValueError(m) |
702 | - share_id, parent_id, name = m.groups() |
703 | - parent = self._find_out_path(share_id, parent_id) |
704 | - return "MakeFile: {0} (in {1})".format(name, parent) |
705 | - |
706 | - # these operations are very simple |
707 | - if data in ('GetPublicFiles', 'AccountInquiry', 'FreeSpaceInquiry', |
708 | - 'ListVolumes', 'ListShares'): |
709 | - return data |
710 | - |
711 | - return "Op? {0}".format(data) |
712 | - |
713 | - def _format_CQ_data(self, data): |
714 | - """Format the content queue information.""" |
715 | - return "{operation}: {path} ({share}:{node})".format(**data) |
716 | - |
717 | - def _find_out_path(self, share_id, node_id): |
718 | - """Curse the destiny!""" |
719 | - return "?" |
720 | - |
721 | - def on_status_changed(self, state): |
722 | - """Show the state nicely.""" |
723 | - self._state = state |
724 | - if state["is_error"]: |
725 | - print "State: Error!" |
726 | - return |
727 | - |
728 | - # normal |
729 | - self._show("State: {name} Q: {queues} C: {connection}".format( |
730 | - **state)) |
731 | - self._check_MQ() |
732 | - |
733 | - def _check_MQ(self): |
734 | - """Check MQ if we should.""" |
735 | - # check if we have something to show in MQ! |
736 | - if self._state['name'] != 'QUEUE_MANAGER' or self._state['queues'] \ |
737 | - not in ('WORKING_ON_METADATA', 'WORKING_ON_BOTH'): |
738 | - return |
739 | - |
740 | - # we have a previous call later running? |
741 | - if self._mqcaller is not None and self._mqcaller.active(): |
742 | - self._mqcaller.cancel() |
743 | - |
744 | - def show_mq(data): |
745 | - """Show the META queue.""" |
746 | - if data == self._last_MQ_data: |
747 | - return |
748 | - |
749 | - # it changed! |
750 | - self._last_MQ_data = data |
751 | - self._show("Meta Queue:") |
752 | - if data: |
753 | - for d in data: |
754 | - print " ", self._format_MQ_data(d) |
755 | - else: |
756 | - print " (empty)" |
757 | - |
758 | - d = sync_daemon_tool.waiting_metadata() |
759 | - d.addCallback(show_mq) |
760 | - |
761 | - self._mqcaller = reactor.callLater(1, self._check_MQ) |
762 | - |
763 | -def main(): |
764 | - print "Check if it's running..." |
765 | - if not tools.is_running(): |
766 | - print "ERROR: SD is not running!" |
767 | - reactor.stop() |
768 | - |
769 | - sh = DBusHandler() |
770 | - |
771 | -if __name__ == "__main__": |
772 | - reactor.callWhenRunning(main) |
773 | - reactor.run() |
774 | |
775 | === modified file 'magicicada/dbusiface.py' |
776 | --- magicicada/dbusiface.py 2010-06-08 10:53:02 +0000 |
777 | +++ magicicada/dbusiface.py 2010-07-09 18:33:43 +0000 |
778 | @@ -32,6 +32,8 @@ |
779 | # log! |
780 | logger = logging.getLogger('magicicada.dbusiface') |
781 | |
782 | +# we use here camel case names, because this variables are used later as |
783 | +# classes, so pylint: disable-msg=C0103 |
784 | QueueData = collections.namedtuple('QueueData', 'operation path share node') |
785 | FolderData = collections.namedtuple('FolderData', |
786 | 'node path suggested_path subscribed volume') |
787 | @@ -50,14 +52,23 @@ |
788 | "(Move)\(share_id=(.*?), node_id=(.*?), old_parent_id=(.*?), " |
789 | "new_parent_id=(.*?), new_name=(.*?)\)") |
790 | |
791 | +# DBus exceptions store the type inside, as a string :| |
792 | +DBUSERR_NOREPLY = 'org.freedesktop.DBus.Error.NoReply' |
793 | +DBUSERR_NAMENOOWNER = 'org.freedesktop.DBus.Error.NameHasNoOwner' |
794 | +DBUSERR_PYKEYERROR = 'org.freedesktop.DBus.Python.KeyError' |
795 | + |
796 | +# some constants |
797 | +NOT_SYNCHED_PATH = "Not a valid path!" |
798 | + |
799 | |
800 | def _is_retry_exception(err): |
801 | """Check if the exception is a retry one.""" |
802 | if isinstance(err, dbus.exceptions.DBusException): |
803 | - if err.get_dbus_name() == 'org.freedesktop.DBus.Error.NoReply': |
804 | + if err.get_dbus_name() == DBUSERR_NOREPLY: |
805 | return True |
806 | return False |
807 | |
808 | + |
809 | def retryable(func): |
810 | """Call the function until its deferred succeed (max 5 times).""" |
811 | |
812 | @@ -68,7 +79,7 @@ |
813 | while opportunities: |
814 | try: |
815 | res = yield func(*a, **k) |
816 | - except Exception, err: |
817 | + except Exception, err: # pylint: disable-msg=W0703 |
818 | opportunities -= 1 |
819 | if opportunities == 0 or not _is_retry_exception(err): |
820 | raise |
821 | @@ -99,6 +110,8 @@ |
822 | (self._on_name_owner_changed, None, 'NameOwnerChanged'), |
823 | (self._on_folder_created, 'Folders', 'FolderCreated'), |
824 | (self._on_folder_deleted, 'Folders', 'FolderDeleted'), |
825 | + (self._on_folder_subscribed, 'Folders', 'FolderSubscribed'), |
826 | + (self._on_folder_unsubscribed, 'Folders', 'FolderUnSubscribed'), |
827 | (self._on_share_created, 'Shares', 'ShareCreated'), |
828 | (self._on_share_deleted, 'Shares', 'ShareDeleted'), |
829 | (self._on_share_changed, 'Shares', 'ShareChanged'), |
830 | @@ -114,7 +127,6 @@ |
831 | signal_name=signal_name) |
832 | self._dbus_matches.append((match, dbus_interface, signal_name)) |
833 | |
834 | - |
835 | def shutdown(self): |
836 | """Shut down the SyncDaemon.""" |
837 | logger.info("DBus interface going down") |
838 | @@ -180,6 +192,16 @@ |
839 | logger.info("Received Folder deleted") |
840 | self.msd.on_sd_folders_changed() |
841 | |
842 | + def _on_folder_subscribed(self, _): |
843 | + """Call the SD callback.""" |
844 | + logger.info("Received Folder subscribed") |
845 | + self.msd.on_sd_folders_changed() |
846 | + |
847 | + def _on_folder_unsubscribed(self, _): |
848 | + """Call the SD callback.""" |
849 | + logger.info("Received Folder unsubscribed") |
850 | + self.msd.on_sd_folders_changed() |
851 | + |
852 | def _on_share_created(self, _): |
853 | """Call the SD callback.""" |
854 | logger.info("Received Share created") |
855 | @@ -198,6 +220,7 @@ |
856 | @retryable |
857 | def get_content_queue(self): |
858 | """Get the content queue from SDT.""" |
859 | + |
860 | def process(data): |
861 | """Enhance data format.""" |
862 | logger.info("Processing Content Queue items (%d)", len(data)) |
863 | @@ -217,31 +240,32 @@ |
864 | def _parse_mq(self, data): |
865 | """Parse MetaQueue string to extract its data.""" |
866 | if data in ('AccountInquiry', 'FreeSpaceInquiry', 'GetPublicFiles', |
867 | - 'ListShares', 'ListVolumes', 'Query'): |
868 | + 'ListShares', 'ListVolumes', 'Query', |
869 | + 'ChangePublicAccess', 'AnswerShare'): |
870 | return QueueData(operation=data, path=None, node=None, share=None) |
871 | |
872 | m = RE_OP_LISTDIR.match(data) |
873 | if m: |
874 | op, share, node = m.groups() |
875 | - path = '?' # we should get the real path, no API now |
876 | + path = '?' # we should get the real path, no API now |
877 | return QueueData(operation=op, path=path, node=node, share=share) |
878 | |
879 | m = RE_OP_MAKEFILE.match(data) |
880 | if m: |
881 | op, share, parent, name = m.groups() |
882 | - path = '/?.../' + name # we should get the real path, no API now |
883 | + path = '/?.../' + name # we should get the real path, no API now |
884 | return QueueData(operation=op, path=path, node=None, share=share) |
885 | |
886 | m = RE_OP_MAKEDIR.match(data) |
887 | if m: |
888 | op, share, parent, name = m.groups() |
889 | - path = '/?.../' + name # we should get the real path, no API now |
890 | + path = '/?.../' + name # we should get the real path, no API now |
891 | return QueueData(operation=op, path=path, node=None, share=share) |
892 | |
893 | m = RE_OP_UNLINK.match(data) |
894 | if m: |
895 | op, share, node, = m.groups() |
896 | - path = '?' # we should get the real path, no API now |
897 | + path = '?' # we should get the real path, no API now |
898 | return QueueData(operation=op, path=path, node=node, share=share) |
899 | |
900 | m = RE_OP_MOVE.match(data) |
901 | @@ -262,6 +286,7 @@ |
902 | @retryable |
903 | def get_meta_queue(self): |
904 | """Get the meta queue from SDT.""" |
905 | + |
906 | def process(data): |
907 | """Enhance data format.""" |
908 | logger.info("Processing Meta Queue items (%d)", len(data)) |
909 | @@ -280,6 +305,7 @@ |
910 | @retryable |
911 | def get_folders(self): |
912 | """Get the folders info from SDT.""" |
913 | + |
914 | def process(data): |
915 | """Enhance data format.""" |
916 | logger.info("Processing Folders items (%d)", len(data)) |
917 | @@ -323,8 +349,7 @@ |
918 | try: |
919 | self._bus.get_name_owner('com.ubuntuone.SyncDaemon') |
920 | except dbus.exceptions.DBusException, err: |
921 | - if err.get_dbus_name() != \ |
922 | - 'org.freedesktop.DBus.Error.NameHasNoOwner': |
923 | + if err.get_dbus_name() != DBUSERR_NAMENOOWNER: |
924 | raise |
925 | started = False |
926 | else: |
927 | @@ -359,6 +384,7 @@ |
928 | @retryable |
929 | def get_shares_to_me(self): |
930 | """Get the shares to me ('shares') info from SDT.""" |
931 | + |
932 | def process(data): |
933 | """Enhance data format.""" |
934 | logger.info("Processing Shares To Me items (%d)", len(data)) |
935 | @@ -372,6 +398,7 @@ |
936 | @retryable |
937 | def get_shares_to_others(self): |
938 | """Get the shares to others ('shared') info from SDT.""" |
939 | + |
940 | def process(data): |
941 | """Enhance data format.""" |
942 | logger.info("Processing Shares To Others items (%d)", len(data)) |
943 | @@ -381,3 +408,24 @@ |
944 | d = self.sync_daemon_tool.list_shared() |
945 | d.addCallback(process) |
946 | return d |
947 | + |
948 | + @retryable |
949 | + def get_metadata(self, path): |
950 | + """Return the raw metadata.""" |
951 | + logger.info("Getting metadata for %r", path) |
952 | + |
953 | + def fix_failure(failure): |
954 | + """Get the failure and return a nice message.""" |
955 | + if failure.check(dbus.exceptions.DBusException): |
956 | + if failure.value.get_dbus_name() == DBUSERR_PYKEYERROR: |
957 | + return NOT_SYNCHED_PATH |
958 | + return failure |
959 | + |
960 | + def process(metadata): |
961 | + """Process the metadata.""" |
962 | + logger.debug("Got metadata for path %r: %r", path, metadata) |
963 | + return dict(metadata) |
964 | + |
965 | + d = self.sync_daemon_tool.get_metadata(path) |
966 | + d.addCallbacks(process, fix_failure) |
967 | + return d |
968 | |
969 | === modified file 'magicicada/helpers.py' |
970 | --- magicicada/helpers.py 2010-06-08 10:53:02 +0000 |
971 | +++ magicicada/helpers.py 2010-07-09 18:33:43 +0000 |
972 | @@ -13,18 +13,15 @@ |
973 | |
974 | import gtk |
975 | import os |
976 | -import sys |
977 | |
978 | from functools import wraps |
979 | |
980 | from magicicada.magicicadaconfig import get_data_file |
981 | |
982 | -import gettext |
983 | -from gettext import gettext as _ |
984 | -gettext.textdomain('magicicada') |
985 | |
986 | NO_OP = lambda *a, **kw: None |
987 | |
988 | + |
989 | def get_builder(builder_file_name): |
990 | """Return a fully-instantiated gtk.Builder instance from specified ui file. |
991 | |
992 | @@ -41,21 +38,37 @@ |
993 | builder.add_from_file(ui_filename) |
994 | return builder |
995 | |
996 | -def print_debug(f): |
997 | - """Print debug info for 'f'.""" |
998 | - |
999 | - @wraps(f) |
1000 | - def inner(*args, **kwargs): |
1001 | - """Wrap f.""" |
1002 | - sys.stderr.write('Calling %s %s %s\n' % (f.__name__, args, kwargs)) |
1003 | - result = f(*args, **kwargs) |
1004 | - return result |
1005 | - |
1006 | - return inner |
1007 | - |
1008 | -# from |
1009 | -# http://code.activestate.com/recipes/577081-humanized-representation-of-a-number-of-bytes/ |
1010 | -def humanize_bytes(bytes, precision=1): |
1011 | + |
1012 | +def log(logger): |
1013 | + """Log input/ouput info for 'f' using 'logger'.""" |
1014 | + |
1015 | + def decorator(f): |
1016 | + """The decorator per se.""" |
1017 | + |
1018 | + @wraps(f) |
1019 | + def inner(*args, **kwargs): |
1020 | + """Wrap 'f', log input args and result using 'logger'.""" |
1021 | + name = f.__name__ |
1022 | + result = None |
1023 | + logger.debug("Calling '%s' with args '%s' and kwargs '%s'.", |
1024 | + name, args, kwargs) |
1025 | + try: |
1026 | + result = f(*args, **kwargs) |
1027 | + except Exception: # pylint: disable-msg=W0703 |
1028 | + logger.exception('%s failed with exception:', name) |
1029 | + logger.debug("Returning from '%s' with result '%s'.", name, result) |
1030 | + return result |
1031 | + |
1032 | + return inner |
1033 | + |
1034 | + return decorator |
1035 | + |
1036 | + |
1037 | +# from http://code.activestate.com/recipes/ |
1038 | +# 577081-humanized-representation-of-a-number-of-bytes/ |
1039 | + |
1040 | + |
1041 | +def humanize_bytes(numbytes, precision=1): |
1042 | """Return a humanized string representation of a number of bytes. |
1043 | |
1044 | Assumes `from __future__ import division`. |
1045 | @@ -83,11 +96,12 @@ |
1046 | (1<<30L, 'GB'), |
1047 | (1<<20L, 'MB'), |
1048 | (1<<10L, 'kB'), |
1049 | - (1, 'bytes') |
1050 | - ) |
1051 | - if bytes == 1: |
1052 | + (1, 'bytes')) |
1053 | + |
1054 | + if numbytes == 1: |
1055 | return '1 byte' |
1056 | for factor, suffix in abbrevs: |
1057 | - if bytes >= factor: |
1058 | + if numbytes >= factor: |
1059 | break |
1060 | - return '%.*f %s' % (precision, bytes / factor, suffix) |
1061 | + # pylint: disable-msg=W0631 |
1062 | + return '%.*f %s' % (precision, numbytes / factor, suffix) |
1063 | |
1064 | === modified file 'magicicada/logger.py' |
1065 | --- magicicada/logger.py 2010-06-08 10:53:02 +0000 |
1066 | +++ magicicada/logger.py 2010-07-09 18:33:43 +0000 |
1067 | @@ -53,4 +53,3 @@ |
1068 | '%Y-%m-%d %H:%M:%S') |
1069 | handler.setFormatter(formatter) |
1070 | logger.setLevel(logging.DEBUG) |
1071 | - |
1072 | |
1073 | === modified file 'magicicada/magicicadaconfig.py' |
1074 | --- magicicada/magicicadaconfig.py 2010-06-08 10:53:02 +0000 |
1075 | +++ magicicada/magicicadaconfig.py 2010-07-09 18:33:43 +0000 |
1076 | @@ -3,13 +3,15 @@ |
1077 | # This file is in the public domain |
1078 | ### END LICENSE |
1079 | |
1080 | +"""Magicicada configuration file.""" |
1081 | + |
1082 | # THIS IS Magicicada CONFIGURATION FILE |
1083 | # YOU CAN PUT THERE SOME GLOBAL VALUE |
1084 | # Do not touch unless you know what you're doing. |
1085 | # you're warned :) |
1086 | |
1087 | __all__ = [ |
1088 | - 'project_path_not_found', |
1089 | + 'ProjectPathNotFound', |
1090 | 'get_data_file', |
1091 | 'get_data_path', |
1092 | ] |
1093 | @@ -21,11 +23,8 @@ |
1094 | |
1095 | import os |
1096 | |
1097 | -import gettext |
1098 | -from gettext import gettext as _ |
1099 | -gettext.textdomain('magicicada') |
1100 | |
1101 | -class project_path_not_found(Exception): |
1102 | +class ProjectPathNotFound(Exception): |
1103 | """Raised when we can't find the project directory.""" |
1104 | |
1105 | |
1106 | @@ -53,6 +52,6 @@ |
1107 | |
1108 | abs_data_path = os.path.abspath(path) |
1109 | if not os.path.exists(abs_data_path): |
1110 | - raise project_path_not_found |
1111 | + raise ProjectPathNotFound |
1112 | |
1113 | return abs_data_path |
1114 | |
1115 | === modified file 'magicicada/syncdaemon.py' |
1116 | --- magicicada/syncdaemon.py 2010-06-08 10:53:02 +0000 |
1117 | +++ magicicada/syncdaemon.py 2010-07-09 18:33:43 +0000 |
1118 | @@ -25,14 +25,19 @@ |
1119 | from magicicada.dbusiface import DBusInterface |
1120 | from magicicada.helpers import NO_OP |
1121 | |
1122 | + |
1123 | # log! |
1124 | logger = logging.getLogger('magicicada.syncdaemon') |
1125 | |
1126 | |
1127 | class State(object): |
1128 | """Hold the state of SD.""" |
1129 | - _attrs = ['name', 'description', 'is_error', 'is_connected', |
1130 | - 'is_online', 'queues', 'connection', 'is_started'] |
1131 | + |
1132 | + _attrs = ['name', 'description', 'is_error', 'is_connected', |
1133 | + 'is_online', 'queues', 'connection', 'is_started'] |
1134 | + |
1135 | + _meta = ('WORKING_ON_METADATA', 'WORKING_ON_BOTH') |
1136 | + _content = ('WORKING_ON_CONTENT', 'WORKING_ON_BOTH') |
1137 | |
1138 | def __init__(self): |
1139 | # starting defaults |
1140 | @@ -47,12 +52,16 @@ |
1141 | |
1142 | def __getattribute__(self, name): |
1143 | """Return the value if there.""" |
1144 | - if name[0] == "_": |
1145 | + if name[0] == "_" or name == 'set': |
1146 | return object.__getattribute__(self, name) |
1147 | + elif name == 'processing_meta': |
1148 | + return self.__dict__['queues'] in self._meta |
1149 | + elif name == 'processing_content': |
1150 | + return self.__dict__['queues'] in self._content |
1151 | else: |
1152 | return self.__dict__[name] |
1153 | |
1154 | - def _set(self, **data): |
1155 | + def set(self, **data): |
1156 | """Set the attributes from data, if allowed.""" |
1157 | for name, value in data.iteritems(): |
1158 | if name not in self._attrs: |
1159 | @@ -97,6 +106,7 @@ |
1160 | self.on_folders_changed_callback = NO_OP |
1161 | self.on_shares_to_me_changed_callback = NO_OP |
1162 | self.on_shares_to_others_changed_callback = NO_OP |
1163 | + self.on_metadata_ready_callback = None # mandatory |
1164 | |
1165 | # mq needs to be polled to know progress |
1166 | self._mqcaller = None |
1167 | @@ -104,11 +114,10 @@ |
1168 | |
1169 | # load initial data if ubuntuone-client already started |
1170 | if self.dbus.is_sd_started(): |
1171 | - self.current_state._set(is_started=True) |
1172 | + self.current_state.set(is_started=True) |
1173 | self._get_initial_data() |
1174 | else: |
1175 | - self.current_state._set(is_started=False) |
1176 | - |
1177 | + self.current_state.set(is_started=False) |
1178 | |
1179 | def shutdown(self): |
1180 | """Shut down the SyncDaemon.""" |
1181 | @@ -165,14 +174,14 @@ |
1182 | def on_sd_name_owner_changed(self, now_active): |
1183 | """SyncDaemon name owner changed.""" |
1184 | logger.info("SD Name Owner changed: %s", now_active) |
1185 | - self.current_state._set(is_started=now_active) |
1186 | + self.current_state.set(is_started=now_active) |
1187 | |
1188 | def set_status(name, description): |
1189 | """Set status after the name owner change.""" |
1190 | d = dict(name=name, description=description, is_error=False, |
1191 | is_connected=False, is_online=False, queues='', |
1192 | connection='') |
1193 | - self.current_state._set(**d) |
1194 | + self.current_state.set(**d) |
1195 | |
1196 | if now_active: |
1197 | set_status('STARTED', 'ubuntuone-client just started') |
1198 | @@ -189,6 +198,7 @@ |
1199 | |
1200 | def _send_status_changed(self, name, description, is_error, is_connected, |
1201 | is_online, queues, connection): |
1202 | + """Send status changed signal.""" |
1203 | logger.debug(" new status: name=%r, description=%r, is_error=%s, " |
1204 | "is_connected=%s, is_online=%s, queues=%r, connection=%r", |
1205 | name, description, is_error, is_connected, is_online, |
1206 | @@ -205,7 +215,7 @@ |
1207 | self.on_offline_callback() |
1208 | |
1209 | # set current state to new values and call status changed cb |
1210 | - self.current_state._set(name=name, description=description, |
1211 | + self.current_state.set(name=name, description=description, |
1212 | is_error=is_error, is_connected=is_connected, |
1213 | is_online=is_online, queues=queues, |
1214 | connection=connection) |
1215 | @@ -213,7 +223,8 @@ |
1216 | is_online, queues, connection) |
1217 | |
1218 | # if corresponds, supervise MQ |
1219 | - self._check_mq() |
1220 | + if self._mqcaller is None: |
1221 | + self._check_mq() |
1222 | |
1223 | @defer.inlineCallbacks |
1224 | def on_sd_content_queue_changed(self): |
1225 | @@ -226,27 +237,30 @@ |
1226 | self.content_queue_changed_callback(new_cq) |
1227 | |
1228 | @defer.inlineCallbacks |
1229 | + def _get_mq_data(self): |
1230 | + """Get MQ info and call back if needed.""" |
1231 | + new_mq = yield self.dbus.get_meta_queue() |
1232 | + if new_mq != self.meta_queue: |
1233 | + logger.info("SD Meta Queue changed: %d items", len(new_mq)) |
1234 | + self.meta_queue = new_mq |
1235 | + self.meta_queue_changed_callback(new_mq) |
1236 | + |
1237 | def _check_mq(self): |
1238 | """Check MQ if we should.""" |
1239 | - state = self.current_state |
1240 | - if state.queues not in ('WORKING_ON_METADATA', 'WORKING_ON_BOTH'): |
1241 | - logger.info("Check MQ called but States not in MQ") |
1242 | + # cancel previous (if any) and check again later |
1243 | + if self._mqcaller is not None and self._mqcaller.active(): |
1244 | + self._mqcaller.cancel() |
1245 | + |
1246 | + if not self.current_state.processing_meta: |
1247 | + logger.info("Check MQ called, States not in MQ, call a last time") |
1248 | + self._mqcaller = None |
1249 | + self._get_mq_data() |
1250 | else: |
1251 | logger.info("Asking for MQ information") |
1252 | |
1253 | - # have we a previous call later still running? |
1254 | - if self._mqcaller is not None and self._mqcaller.active(): |
1255 | - self._mqcaller.cancel() |
1256 | - |
1257 | # get the info |
1258 | - new_mq = yield self.dbus.get_meta_queue() |
1259 | - |
1260 | - if new_mq != self.meta_queue: |
1261 | - logger.info("SD Meta Queue changed: %d items", len(new_mq)) |
1262 | - self.meta_queue = new_mq |
1263 | - self.meta_queue_changed_callback(new_mq) |
1264 | - |
1265 | - # check again later |
1266 | + self._get_mq_data() |
1267 | + |
1268 | self._mqcaller = reactor.callLater(self._mq_poll_time, |
1269 | self._check_mq) |
1270 | |
1271 | @@ -270,3 +284,11 @@ |
1272 | """Tell the SyncDaemon that the user wants it to disconnect.""" |
1273 | logger.info("Telling u1.SD to disconnect") |
1274 | self.dbus.disconnect() |
1275 | + |
1276 | + def get_metadata(self, path): |
1277 | + """Get the metadata for given path.""" |
1278 | + if self.on_metadata_ready_callback is None: |
1279 | + raise ValueError("Missing the mandatory cback for get_metadata.") |
1280 | + |
1281 | + d = self.dbus.get_metadata(path) |
1282 | + d.addCallback(lambda resp: self.on_metadata_ready_callback(path, resp)) |
1283 | |
1284 | === modified file 'magicicada/tests/helpers.py' |
1285 | --- magicicada/tests/helpers.py 2010-06-08 10:53:02 +0000 |
1286 | +++ magicicada/tests/helpers.py 2010-07-09 18:33:43 +0000 |
1287 | @@ -36,14 +36,18 @@ |
1288 | def check(self, level, msg): |
1289 | """Check that something is logged.""" |
1290 | for rec in self.records: |
1291 | - if rec.levelname == level and rec.message == msg: |
1292 | + if rec.levelname == level and str(msg) in rec.message: |
1293 | return True |
1294 | return False |
1295 | |
1296 | - def check_inf(self, msg): |
1297 | + def check_error(self, msg): |
1298 | + """Shortcut for ERROR check.""" |
1299 | + return self.check('ERROR', msg) |
1300 | + |
1301 | + def check_info(self, msg): |
1302 | """Shortcut for INFO check.""" |
1303 | return self.check('INFO', msg) |
1304 | |
1305 | - def check_dbg(self, msg): |
1306 | + def check_debug(self, msg): |
1307 | """Shortcut for DEBUG check.""" |
1308 | return self.check('DEBUG', msg) |
1309 | |
1310 | === modified file 'magicicada/tests/test_dbusiface.py' |
1311 | --- magicicada/tests/test_dbusiface.py 2010-06-08 10:53:02 +0000 |
1312 | +++ magicicada/tests/test_dbusiface.py 2010-07-09 18:33:43 +0000 |
1313 | @@ -28,8 +28,13 @@ |
1314 | from magicicada.tests.helpers import MementoHandler |
1315 | |
1316 | |
1317 | +# It's ok to access private data in the test suite |
1318 | +# pylint: disable-msg=W0212 |
1319 | + |
1320 | + |
1321 | class FakeSessionBus(object): |
1322 | """Fake Session Bus.""" |
1323 | + |
1324 | def __init__(self, **kwargs): |
1325 | self._callbacks = {} |
1326 | self.fake_name_owner = "foo" |
1327 | @@ -43,8 +48,12 @@ |
1328 | del self._callbacks[(dbus_interface, signal_name)] |
1329 | |
1330 | def get_name_owner(self, name): |
1331 | - """Fakes the response of the method.""" |
1332 | + """Fake the response of the method.""" |
1333 | assert name == 'com.ubuntuone.SyncDaemon' |
1334 | + |
1335 | + # will return a string, or raise an exception instance, never |
1336 | + # raise a string |
1337 | + # pylint: disable-msg=W0701 |
1338 | if isinstance(self.fake_name_owner, str): |
1339 | return self.fake_name_owner |
1340 | else: |
1341 | @@ -53,6 +62,7 @@ |
1342 | |
1343 | class CallLoguer(object): |
1344 | """Class that logs the methods called.""" |
1345 | + |
1346 | def __init__(self): |
1347 | self._called_method = None, () |
1348 | self._fake_response = None |
1349 | @@ -62,19 +72,25 @@ |
1350 | if name[0] == "_": |
1351 | return object.__getattribute__(self, name) |
1352 | else: |
1353 | + |
1354 | def f(*args): |
1355 | + """Fake function.""" |
1356 | setattr(self, "_called_method", (name, args)) |
1357 | if self._fake_response is None: |
1358 | # no hurt in returning a deferred, it may be needed |
1359 | return defer.Deferred() |
1360 | methname, response = self._fake_response |
1361 | assert methname == name |
1362 | - return response |
1363 | + if isinstance(response, Exception): |
1364 | + return defer.fail(response) |
1365 | + else: |
1366 | + return defer.succeed(response) |
1367 | return f |
1368 | |
1369 | |
1370 | class FakeSDTool(CallLoguer): |
1371 | """Fake real SyncDaemonTool.""" |
1372 | + |
1373 | def __init__(self, _): |
1374 | CallLoguer.__init__(self) |
1375 | |
1376 | @@ -104,9 +120,9 @@ |
1377 | return called_args |
1378 | |
1379 | def fake_sdt_response(self, method_name, response): |
1380 | - """Fakes SDT answer in deferred mode.""" |
1381 | - self.dbus.sync_daemon_tool._fake_response = (method_name, |
1382 | - defer.succeed(response)) |
1383 | + """Fake SDT answer in deferred mode.""" |
1384 | + self.dbus.sync_daemon_tool._fake_response = (method_name, response) |
1385 | + |
1386 | |
1387 | class TestSignalHooking(SafeTests): |
1388 | """Signal hooking tests. |
1389 | @@ -114,6 +130,7 @@ |
1390 | We can not check if the methods are really called, because DBus holds the |
1391 | method object itself, so no chance in monkeypatching. |
1392 | """ |
1393 | + |
1394 | def _get_hooked(self, iface, signal): |
1395 | """Return the hooked method if any.""" |
1396 | if iface is None: |
1397 | @@ -152,6 +169,16 @@ |
1398 | self.assertEqual(self._get_hooked('Folders', 'FolderDeleted'), |
1399 | self.dbus._on_folder_deleted) |
1400 | |
1401 | + def test_folder_subscribed_changed(self): |
1402 | + """Test folder subscribed changed callback.""" |
1403 | + self.assertEqual(self._get_hooked('Folders', 'FolderSubscribed'), |
1404 | + self.dbus._on_folder_subscribed) |
1405 | + |
1406 | + def test_folder_unsubscribed_changed(self): |
1407 | + """Test folder unsubscribed changed callback.""" |
1408 | + self.assertEqual(self._get_hooked('Folders', 'FolderUnSubscribed'), |
1409 | + self.dbus._on_folder_unsubscribed) |
1410 | + |
1411 | def test_share_created(self): |
1412 | """Test share created callback.""" |
1413 | self.assertEqual(self._get_hooked('Shares', 'ShareCreated'), |
1414 | @@ -188,7 +215,7 @@ |
1415 | |
1416 | |
1417 | class TestDataProcessingStatus(SafeTests): |
1418 | - """Processes Status before sending it to SyncDaemon.""" |
1419 | + """Process Status before sending it to SyncDaemon.""" |
1420 | |
1421 | @defer.inlineCallbacks |
1422 | def test_get_status(self): |
1423 | @@ -224,7 +251,7 @@ |
1424 | |
1425 | |
1426 | class TestDataProcessingNameOwner(SafeTests): |
1427 | - """Processes Name Owner data before sending it to SyncDaemon.""" |
1428 | + """Process Name Owner data before sending it to SyncDaemon.""" |
1429 | |
1430 | def test_name_owner_changed_no_syncdaemon(self): |
1431 | """Test name owner changed callback.""" |
1432 | @@ -245,7 +272,7 @@ |
1433 | |
1434 | |
1435 | class TestDataProcessingCQ(SafeTests): |
1436 | - """Processes CQ data before sending it to SyncDaemon.""" |
1437 | + """Process CQ data before sending it to SyncDaemon.""" |
1438 | |
1439 | @defer.inlineCallbacks |
1440 | def test_nodata(self): |
1441 | @@ -288,7 +315,7 @@ |
1442 | |
1443 | |
1444 | class TestDataProcessingMQ(SafeTests): |
1445 | - """Processes MQ data before sending it to SyncDaemon.""" |
1446 | + """Process MQ data before sending it to SyncDaemon.""" |
1447 | |
1448 | @defer.inlineCallbacks |
1449 | def test_nodata(self): |
1450 | @@ -461,9 +488,33 @@ |
1451 | self.assertEqual(data.share, 'a') |
1452 | self.assertEqual(data.node, 'b') |
1453 | |
1454 | + @defer.inlineCallbacks |
1455 | + def test_ChangePublicAccess(self): |
1456 | + """Test meta with ChangePublicAccess.""" |
1457 | + cmd = 'ChangePublicAccess' |
1458 | + self.fake_sdt_response('waiting_metadata', [cmd]) |
1459 | + rcv = yield self.dbus.get_meta_queue() |
1460 | + data = rcv[0] |
1461 | + self.assertEqual(data.operation, 'ChangePublicAccess') |
1462 | + self.assertEqual(data.path, None) |
1463 | + self.assertEqual(data.share, None) |
1464 | + self.assertEqual(data.node, None) |
1465 | + |
1466 | + @defer.inlineCallbacks |
1467 | + def test_AnswerShare(self): |
1468 | + """Test meta with AnswerShare.""" |
1469 | + cmd = 'AnswerShare' |
1470 | + self.fake_sdt_response('waiting_metadata', [cmd]) |
1471 | + rcv = yield self.dbus.get_meta_queue() |
1472 | + data = rcv[0] |
1473 | + self.assertEqual(data.operation, 'AnswerShare') |
1474 | + self.assertEqual(data.path, None) |
1475 | + self.assertEqual(data.share, None) |
1476 | + self.assertEqual(data.node, None) |
1477 | + |
1478 | |
1479 | class TestDataProcessingFolders(SafeTests): |
1480 | - """Processes Folders data before sending it to SyncDaemon.""" |
1481 | + """Process Folders data before sending it to SyncDaemon.""" |
1482 | |
1483 | @defer.inlineCallbacks |
1484 | def test_nodata(self): |
1485 | @@ -520,10 +571,40 @@ |
1486 | self.dbus._on_folder_deleted(None) |
1487 | self.get_msd_called("on_sd_folders_changed") |
1488 | |
1489 | + def test_folders_changed_from_subscribed(self): |
1490 | + """Test folders changed callback from subscribed.""" |
1491 | + self.dbus._on_folder_subscribed(None) |
1492 | + self.get_msd_called("on_sd_folders_changed") |
1493 | + |
1494 | + def test_folders_changed_from_unsubscribed(self): |
1495 | + """Test folders changed callback from unsubscribed.""" |
1496 | + self.dbus._on_folder_unsubscribed(None) |
1497 | + self.get_msd_called("on_sd_folders_changed") |
1498 | + |
1499 | + |
1500 | +class TestDataProcessingMetadata(SafeTests): |
1501 | + """Process Metadata data before sending it to SyncDaemon.""" |
1502 | + |
1503 | + @defer.inlineCallbacks |
1504 | + def test_info_ok(self): |
1505 | + """Test get metadata and see response.""" |
1506 | + md = dbus.Dictionary({'a': 3, 'c': 4}, signature=dbus.Signature('ss')) |
1507 | + self.fake_sdt_response('get_metadata', md) |
1508 | + rcv = yield self.dbus.get_metadata('path') |
1509 | + self.assertEqual(rcv, dict(a=3, c=4)) |
1510 | + |
1511 | + @defer.inlineCallbacks |
1512 | + def test_info_bad(self): |
1513 | + """Test get metadata and get the error.""" |
1514 | + exc = dbus.exceptions.DBusException( |
1515 | + name='org.freedesktop.DBus.Python.KeyError') |
1516 | + self.fake_sdt_response('get_metadata', exc) |
1517 | + rcv = yield self.dbus.get_metadata('not a real path') |
1518 | + self.assertEqual(rcv, dbusiface.NOT_SYNCHED_PATH) |
1519 | |
1520 | |
1521 | class TestDataProcessingShares(SafeTests): |
1522 | - """Processes Shares data before sending it to SyncDaemon.""" |
1523 | + """Process Shares data before sending it to SyncDaemon.""" |
1524 | |
1525 | @defer.inlineCallbacks |
1526 | def test_sharestome_nodata(self): |
1527 | @@ -693,68 +774,74 @@ |
1528 | |
1529 | def test_instancing(self): |
1530 | """Just logged SD instancing.""" |
1531 | - self.assertTrue(self.handler.check_inf("DBus interface starting")) |
1532 | + self.assertTrue(self.handler.check_info("DBus interface starting")) |
1533 | |
1534 | def test_shutdown(self): |
1535 | """Log when SD shutdowns.""" |
1536 | self.dbus.shutdown() |
1537 | - self.assertTrue(self.handler.check_inf("DBus interface going down")) |
1538 | + self.assertTrue(self.handler.check_info("DBus interface going down")) |
1539 | |
1540 | def test_waiting_content(self): |
1541 | """Test call to waiting content.""" |
1542 | self.dbus.get_content_queue() |
1543 | - self.assertTrue(self.handler.check_inf("Getting content queue")) |
1544 | + self.assertTrue(self.handler.check_info("Getting content queue")) |
1545 | |
1546 | def test_waiting_meta(self): |
1547 | """Test call to waiting meta.""" |
1548 | self.dbus.get_meta_queue() |
1549 | - self.assertTrue(self.handler.check_inf("Getting meta queue")) |
1550 | + self.assertTrue(self.handler.check_info("Getting meta queue")) |
1551 | |
1552 | def test_get_status(self): |
1553 | """Test call to status.""" |
1554 | self.dbus.get_status() |
1555 | - self.assertTrue(self.handler.check_inf("Getting status")) |
1556 | + self.assertTrue(self.handler.check_info("Getting status")) |
1557 | |
1558 | def test_get_folders(self): |
1559 | """Test call to folders.""" |
1560 | self.dbus.get_folders() |
1561 | - self.assertTrue(self.handler.check_inf("Getting folders")) |
1562 | + self.assertTrue(self.handler.check_info("Getting folders")) |
1563 | + |
1564 | + def test_get_metadata(self): |
1565 | + """Test call to metadata.""" |
1566 | + self.dbus.get_metadata('path') |
1567 | + msg = "Getting metadata for u'path'" |
1568 | + self.assertTrue(self.handler.check_info(msg)) |
1569 | |
1570 | def test_get_shares_to_me(self): |
1571 | """Test call to shares to me.""" |
1572 | self.dbus.get_shares_to_me() |
1573 | - self.assertTrue(self.handler.check_inf("Getting shares to me")) |
1574 | + self.assertTrue(self.handler.check_info("Getting shares to me")) |
1575 | |
1576 | def test_get_shares_to_other(self): |
1577 | """Test call to shares to others.""" |
1578 | self.dbus.get_shares_to_others() |
1579 | - self.assertTrue(self.handler.check_inf("Getting shares to others")) |
1580 | + self.assertTrue(self.handler.check_info("Getting shares to others")) |
1581 | |
1582 | def test_is_sd_started(self): |
1583 | """Test call to is_sd_started.""" |
1584 | self.dbus.is_sd_started() |
1585 | - self.assertTrue(self.handler.check_inf( |
1586 | + self.assertTrue(self.handler.check_info( |
1587 | "Checking if SD is started: True")) |
1588 | |
1589 | def test_start(self): |
1590 | """Test call to start.""" |
1591 | self.dbus.start() |
1592 | - self.assertTrue(self.handler.check_inf("Calling start")) |
1593 | + self.assertTrue(self.handler.check_info("Calling start")) |
1594 | |
1595 | def test_quit(self): |
1596 | """Test call to quit.""" |
1597 | self.dbus.quit() |
1598 | - self.assertTrue(self.handler.check_inf("Calling quit")) |
1599 | + self.assertTrue(self.handler.check_info("Calling quit")) |
1600 | |
1601 | def test_connect(self): |
1602 | """Test call to connect.""" |
1603 | self.dbus.connect() |
1604 | - self.assertTrue(self.handler.check_inf("Calling connect")) |
1605 | + self.assertTrue(self.handler.check_info("Calling connect")) |
1606 | |
1607 | def test_disconnect(self): |
1608 | """Test call to disconnect.""" |
1609 | self.dbus.disconnect() |
1610 | - self.assertTrue(self.handler.check_inf("Calling disconnect")) |
1611 | + self.assertTrue(self.handler.check_info("Calling disconnect")) |
1612 | |
1613 | def test_status_changed(self): |
1614 | """Test status changed callback.""" |
1615 | @@ -762,58 +849,71 @@ |
1616 | is_connected='True', is_online='', queues='queues', |
1617 | connection='connection') |
1618 | self.dbus._on_status_changed(d) |
1619 | - self.assertTrue(self.handler.check_inf("Received Status changed")) |
1620 | - self.assertTrue(self.handler.check_dbg("Status changed data: %r" % d)) |
1621 | + self.assertTrue(self.handler.check_info("Received Status changed")) |
1622 | + msg = "Status changed data: %r" % d |
1623 | + self.assertTrue(self.handler.check_debug(msg)) |
1624 | |
1625 | def test_content_queue_changed(self): |
1626 | """Test content queue changed callback.""" |
1627 | self.dbus._on_content_queue_changed("foo") |
1628 | - self.assertTrue(self.handler.check_inf( |
1629 | + self.assertTrue(self.handler.check_info( |
1630 | "Received Content Queue changed")) |
1631 | |
1632 | def test_name_owner_changed_other(self): |
1633 | """Test name owner changed callback, no SD.""" |
1634 | self.dbus._on_name_owner_changed("other", "", "T") |
1635 | - self.assertFalse(self.handler.check_inf("Received Name Owner changed")) |
1636 | + msg = "Received Name Owner changed" |
1637 | + self.assertFalse(self.handler.check_info(msg)) |
1638 | |
1639 | def test_name_owner_changed_syncdaemon(self): |
1640 | """Test name owner changed callback, SD value ok.""" |
1641 | self.dbus._on_name_owner_changed("com.ubuntuone.SyncDaemon", "", "T") |
1642 | - self.assertTrue(self.handler.check_inf("Received Name Owner changed")) |
1643 | - self.assertTrue(self.handler.check_dbg("Name Owner data: u'' u'T'")) |
1644 | + self.assertTrue(self.handler.check_info("Received Name Owner changed")) |
1645 | + self.assertTrue(self.handler.check_debug("Name Owner data: u'' u'T'")) |
1646 | |
1647 | def test_name_owner_changed_yes_syncdaemon_TF(self): |
1648 | """Test name owner changed callback, SD value bad.""" |
1649 | self.dbus._on_name_owner_changed("com.ubuntuone.SyncDaemon", "F", "T") |
1650 | - self.assertTrue(self.handler.check_inf("Received Name Owner changed")) |
1651 | - self.assertTrue(self.handler.check_dbg("Name Owner data: u'F' u'T'")) |
1652 | + self.assertTrue(self.handler.check_info("Received Name Owner changed")) |
1653 | + self.assertTrue(self.handler.check_debug("Name Owner data: u'F' u'T'")) |
1654 | self.assertTrue(self.handler.check("ERROR", |
1655 | "Name Owner invalid data: Same bool in old and new!")) |
1656 | |
1657 | def test_folder_created_changed(self): |
1658 | """Test folder created changed callback.""" |
1659 | self.dbus._on_folder_created("foo") |
1660 | - self.assertTrue(self.handler.check_inf("Received Folder created")) |
1661 | + self.assertTrue(self.handler.check_info("Received Folder created")) |
1662 | |
1663 | def test_folder_deleted_changed(self): |
1664 | """Test folder deleted changed callback.""" |
1665 | self.dbus._on_folder_deleted("foo") |
1666 | - self.assertTrue(self.handler.check_inf("Received Folder deleted")) |
1667 | + self.assertTrue(self.handler.check_info("Received Folder deleted")) |
1668 | + |
1669 | + def test_folder_subscribed_changed(self): |
1670 | + """Test folder subscribed changed callback.""" |
1671 | + self.dbus._on_folder_subscribed("foo") |
1672 | + self.assertTrue(self.handler.check_info("Received Folder subscribed")) |
1673 | + |
1674 | + def test_folder_unsubscribed_changed(self): |
1675 | + """Test folder unsubscribed changed callback.""" |
1676 | + self.dbus._on_folder_unsubscribed("foo") |
1677 | + self.assertTrue(self.handler.check_info( |
1678 | + "Received Folder unsubscribed")) |
1679 | |
1680 | def test_share_created(self): |
1681 | """Test share created callback.""" |
1682 | self.dbus._on_share_created("foo") |
1683 | - self.assertTrue(self.handler.check_inf("Received Share created")) |
1684 | + self.assertTrue(self.handler.check_info("Received Share created")) |
1685 | |
1686 | def test_share_deleted(self): |
1687 | """Test share deleted callback.""" |
1688 | self.dbus._on_share_deleted("foo") |
1689 | - self.assertTrue(self.handler.check_inf("Received Share deleted")) |
1690 | + self.assertTrue(self.handler.check_info("Received Share deleted")) |
1691 | |
1692 | def test_share__changed(self): |
1693 | """Test share changed callback.""" |
1694 | self.dbus._on_share_changed("foo") |
1695 | - self.assertTrue(self.handler.check_inf("Received Share changed")) |
1696 | + self.assertTrue(self.handler.check_info("Received Share changed")) |
1697 | |
1698 | @defer.inlineCallbacks |
1699 | def test_content_queue_processing(self): |
1700 | @@ -821,9 +921,9 @@ |
1701 | c = dict(operation='oper', path='path', share='share', node='node') |
1702 | self.fake_sdt_response('waiting_content', [c]) |
1703 | yield self.dbus.get_content_queue() |
1704 | - self.assertTrue(self.handler.check_inf( |
1705 | + self.assertTrue(self.handler.check_info( |
1706 | "Processing Content Queue items (1)")) |
1707 | - self.assertTrue(self.handler.check_dbg( |
1708 | + self.assertTrue(self.handler.check_debug( |
1709 | " Content Queue data: %s" % c)) |
1710 | |
1711 | @defer.inlineCallbacks |
1712 | @@ -831,9 +931,9 @@ |
1713 | """Test with one item in the queue.""" |
1714 | self.fake_sdt_response('waiting_metadata', ['ListShares']) |
1715 | yield self.dbus.get_meta_queue() |
1716 | - self.assertTrue(self.handler.check_inf( |
1717 | + self.assertTrue(self.handler.check_info( |
1718 | "Processing Meta Queue items (1)")) |
1719 | - self.assertTrue(self.handler.check_dbg( |
1720 | + self.assertTrue(self.handler.check_debug( |
1721 | " Meta Queue data: u'ListShares'")) |
1722 | |
1723 | @defer.inlineCallbacks |
1724 | @@ -843,8 +943,18 @@ |
1725 | suggested_path=u'sgp', type='UDF', volume_id='vid') |
1726 | self.fake_sdt_response('get_folders', [d]) |
1727 | yield self.dbus.get_folders() |
1728 | - self.assertTrue(self.handler.check_inf("Processing Folders items (1)")) |
1729 | - self.assertTrue(self.handler.check_dbg(" Folders data: %r" % d)) |
1730 | + msg = "Processing Folders items (1)" |
1731 | + self.assertTrue(self.handler.check_info(msg)) |
1732 | + self.assertTrue(self.handler.check_debug(" Folders data: %r" % d)) |
1733 | + |
1734 | + @defer.inlineCallbacks |
1735 | + def test_metadata_processing(self): |
1736 | + """Test get metadata.""" |
1737 | + d = dict(lot_of_data="I don't care") |
1738 | + self.fake_sdt_response('get_metadata', d) |
1739 | + yield self.dbus.get_metadata('path') |
1740 | + self.assertTrue(self.handler.check_debug( |
1741 | + "Got metadata for path u'path': %r" % d)) |
1742 | |
1743 | @defer.inlineCallbacks |
1744 | def test_sharestome_processing(self): |
1745 | @@ -855,9 +965,9 @@ |
1746 | volume_id=u'vol', type=u'Share') |
1747 | self.fake_sdt_response('get_shares', [d]) |
1748 | yield self.dbus.get_shares_to_me() |
1749 | - self.assertTrue(self.handler.check_inf( |
1750 | + self.assertTrue(self.handler.check_info( |
1751 | "Processing Shares To Me items (1)")) |
1752 | - self.assertTrue(self.handler.check_dbg(" Share data: %r" % d)) |
1753 | + self.assertTrue(self.handler.check_debug(" Share data: %r" % d)) |
1754 | |
1755 | @defer.inlineCallbacks |
1756 | def test_sharestoothers_processing(self): |
1757 | @@ -868,16 +978,17 @@ |
1758 | volume_id=u'vol', type=u'Shared') |
1759 | self.fake_sdt_response('list_shared', [d]) |
1760 | yield self.dbus.get_shares_to_others() |
1761 | - self.assertTrue(self.handler.check_inf( |
1762 | + self.assertTrue(self.handler.check_info( |
1763 | "Processing Shares To Others items (1)")) |
1764 | - self.assertTrue(self.handler.check_dbg(" Share data: %r" % d)) |
1765 | + self.assertTrue(self.handler.check_debug(" Share data: %r" % d)) |
1766 | |
1767 | |
1768 | class RetryDecoratorTests(TwistedTestCase): |
1769 | """Test the retry decorator.""" |
1770 | |
1771 | class Helper(object): |
1772 | - """Fails some times, finally succeeds.""" |
1773 | + """Fail some times, finally succeed.""" |
1774 | + |
1775 | def __init__(self, limit, excep=None): |
1776 | self.cant = 0 |
1777 | self.limit = limit |
1778 | @@ -915,7 +1026,7 @@ |
1779 | self.assertTrue(dbusiface._is_retry_exception(err)) |
1780 | |
1781 | def get_decorated_func(self, func): |
1782 | - """Executes the test calling the received function.""" |
1783 | + """Execute the test calling the received function.""" |
1784 | |
1785 | @dbusiface.retryable |
1786 | def f(): |
1787 | |
1788 | === modified file 'magicicada/tests/test_magicicada.py' |
1789 | --- magicicada/tests/test_magicicada.py 2010-06-08 10:53:02 +0000 |
1790 | +++ magicicada/tests/test_magicicada.py 2010-07-09 18:33:43 +0000 |
1791 | @@ -18,6 +18,9 @@ |
1792 | |
1793 | """Tests for magicicada.""" |
1794 | |
1795 | +import logging |
1796 | +import sys |
1797 | + |
1798 | from functools import wraps |
1799 | |
1800 | import gobject |
1801 | @@ -26,33 +29,57 @@ |
1802 | |
1803 | from twisted.trial.unittest import TestCase |
1804 | |
1805 | -from magicicada import MagicicadaUI, CONTENT_QUEUE, META_QUEUE, syncdaemon |
1806 | +from magicicada import MagicicadaUI, CONTENT_QUEUE, META_QUEUE, \ |
1807 | + UBUNTU_ONE_ROOT, syncdaemon |
1808 | from magicicada.dbusiface import QueueData, FolderData, ShareData |
1809 | -from magicicada.helpers import NO_OP, humanize_bytes |
1810 | +from magicicada.helpers import NO_OP, humanize_bytes, get_data_file |
1811 | +from magicicada.tests.helpers import MementoHandler |
1812 | + |
1813 | + |
1814 | +# It's ok to access private data in the test suite |
1815 | +# pylint: disable-msg=W0212 |
1816 | +# Arguments number differs from overridden method |
1817 | +# pylint: disable-msg=W0221 |
1818 | + |
1819 | |
1820 | def process_gtk_pendings(): |
1821 | - while gtk.events_pending(): gtk.main_iteration() |
1822 | + """Process all gtk pending events.""" |
1823 | + while gtk.events_pending(): |
1824 | + gtk.main_iteration() |
1825 | + |
1826 | |
1827 | def close_dialog((dialog, test)): |
1828 | """Call the 'test', close 'dialog'.""" |
1829 | - try: |
1830 | - process_gtk_pendings() |
1831 | - test() |
1832 | - process_gtk_pendings() |
1833 | - finally: |
1834 | - dialog.response(gtk.RESPONSE_CLOSE) |
1835 | - process_gtk_pendings() |
1836 | - return False # do not be called again |
1837 | + response = gtk.RESPONSE_CLOSE |
1838 | + try: |
1839 | + process_gtk_pendings() |
1840 | + test() |
1841 | + finally: |
1842 | + dialog.response(response) |
1843 | + process_gtk_pendings() |
1844 | + return False # do not be called again |
1845 | + |
1846 | + |
1847 | +def test_and_click((button, test)): |
1848 | + """Call the 'test', and click 'button'.""" |
1849 | + try: |
1850 | + test() |
1851 | + finally: |
1852 | + button.clicked() |
1853 | + return False # do not be called again |
1854 | |
1855 | |
1856 | class FakedSyncdaemon(object): |
1857 | """A faked syncdaemon.""" |
1858 | |
1859 | def __init__(self): |
1860 | + self._meta_path = None |
1861 | + |
1862 | self.current_state = syncdaemon.State() |
1863 | self.meta_queue = [] |
1864 | self.content_queue = [] |
1865 | self.folders = [] |
1866 | + self.processing_meta = False |
1867 | |
1868 | self.on_started_callback = NO_OP |
1869 | self.on_stopped_callback = NO_OP |
1870 | @@ -63,13 +90,16 @@ |
1871 | self.status_changed_callback = NO_OP |
1872 | self.content_queue_changed_callback = NO_OP |
1873 | self.meta_queue_changed_callback = NO_OP |
1874 | + self.on_metadata_ready_callback = None # mandatory |
1875 | self.shutdown = NO_OP |
1876 | |
1877 | self.start = lambda: setattr(self.current_state, 'is_started', True) |
1878 | self.quit = lambda: setattr(self.current_state, 'is_started', False) |
1879 | - self.connect = lambda: setattr(self.current_state, 'is_connected', True) |
1880 | - self.disconnect = \ |
1881 | - lambda: setattr(self.current_state, 'is_connected', False) |
1882 | + self.connect = lambda: setattr(self.current_state, |
1883 | + 'is_connected', True) |
1884 | + self.disconnect = lambda: \ |
1885 | + setattr(self.current_state, 'is_connected', False) |
1886 | + self.get_metadata = lambda path: setattr(self, '_meta_path', path) |
1887 | |
1888 | |
1889 | class MagicicadaUITestCase(TestCase): |
1890 | @@ -78,13 +108,14 @@ |
1891 | def setUp(self): |
1892 | """Init.""" |
1893 | self.ui = MagicicadaUI(syncdaemon_class=FakedSyncdaemon) |
1894 | - self._called = False |
1895 | - self.set_called = lambda *args, **kwargs: setattr(self, '_called', True) |
1896 | + self.called = False |
1897 | + self.response = None |
1898 | + self.set_called = lambda *args, **kwargs: setattr(self, 'called', True) |
1899 | |
1900 | def tearDown(self): |
1901 | """Cleanup.""" |
1902 | self.ui.on_main_window_destroy(self.ui.main_window) |
1903 | - self._called = False |
1904 | + self.called = False |
1905 | |
1906 | def do_start(self): |
1907 | """Simulate that start fully happened.""" |
1908 | @@ -106,14 +137,16 @@ |
1909 | result.append(data_type(**kwargs)) |
1910 | return result |
1911 | |
1912 | - def assert_store_correct(self, store, items): |
1913 | + def assert_store_correct(self, store, items, markup=None): |
1914 | """Test that 'store' has 'items' as content.""" |
1915 | msg = 'amount of rows for %s must be %s (got %s).' |
1916 | self.assertEqual(len(store), len(items), |
1917 | msg % (store, len(items), len(store))) |
1918 | + |
1919 | # assert rows content equal to items content |
1920 | tree_iter = store.get_iter_root() |
1921 | tmp = list(reversed(items)) |
1922 | + do_markup = markup is not None |
1923 | msg = "column %i ('%s') must be '%s' (got '%s' instead)" |
1924 | while tree_iter is not None: |
1925 | head = tmp.pop() |
1926 | @@ -122,27 +155,68 @@ |
1927 | expected = getattr(head, field) |
1928 | if store.get_column_type(i).name == 'gboolean': |
1929 | expected = bool(expected) |
1930 | - self.assertEqual(expected, actual, msg % (i, field, expected, actual)) |
1931 | + elif do_markup: |
1932 | + expected = markup(expected) |
1933 | + self.assertEqual(expected, actual, |
1934 | + msg % (i, field, expected, actual)) |
1935 | |
1936 | tree_iter = store.iter_next(tree_iter) |
1937 | + do_markup = False # only for first row |
1938 | |
1939 | def assert_indicator_disabled(self, indicator): |
1940 | """Test that 'indicator' is not sensitive.""" |
1941 | - self.assertFalse(indicator.is_sensitive(), 'indicator is not sensitive') |
1942 | + self.assertFalse(indicator.is_sensitive(), |
1943 | + 'indicator must not be sensitive.') |
1944 | |
1945 | def assert_indicator_ready(self, indicator): |
1946 | """Test that 'indicator' is sensitive and green.""" |
1947 | - self.assertTrue(indicator.is_sensitive(), 'indicator is sensitive') |
1948 | - expected = indicator.get_pixbuf() # a test on its own |
1949 | + self.assertTrue(indicator.is_sensitive(), |
1950 | + 'indicator must be sensitive.') |
1951 | + expected = indicator.get_pixbuf() # a test on its own |
1952 | self.assertEqual(self.ui.active_indicator, expected, |
1953 | - 'indicator is the correct pixbuf') |
1954 | + 'indicator must have the correct pixbuf.') |
1955 | |
1956 | def assert_indicator_loading(self, indicator): |
1957 | """Test that 'indicator' is sensitive and loading.""" |
1958 | - self.assertTrue(indicator.is_sensitive(), 'indicator is sensitive') |
1959 | - expected = indicator.get_animation() # a test on its own |
1960 | + self.assertTrue(indicator.is_sensitive(), |
1961 | + 'indicator must be sensitive.') |
1962 | + expected = indicator.get_animation() # a test on its own |
1963 | self.assertEqual(self.ui.loading_animation, expected, |
1964 | - 'indicator is the correct animation') |
1965 | + 'indicator must have the correct animation.') |
1966 | + |
1967 | + def assert_widget_availability(self, widget_name, enabled=True): |
1968 | + """Check button availability according to 'enabled'.""" |
1969 | + widget = getattr(self.ui, widget_name) |
1970 | + self.assertTrue(self.ui.widget_is_visible(widget), |
1971 | + '%s should be visible' % widget_name) |
1972 | + sensitive = widget.is_sensitive() |
1973 | + msg = '%s should %sbe sensitive' |
1974 | + self.assertTrue(sensitive if enabled else not sensitive, |
1975 | + msg % (widget_name, '' if enabled else 'not ')) |
1976 | + |
1977 | + def assert_dialog_properties(self, dialog_name, title, size=(600, 300), |
1978 | + modal=True): |
1979 | + """The dialog 'dialog_name' has correct properties.""" |
1980 | + dialog = getattr(self.ui, dialog_name) |
1981 | + actual = dialog.size_request() |
1982 | + msg = 'size must be %s (got %s instead).' |
1983 | + self.assertEquals(size, actual, msg % (size, actual)) |
1984 | + |
1985 | + msg = '%s must %sbe modal.' |
1986 | + self.assertEqual(modal, dialog.get_modal(), |
1987 | + msg % (dialog_name, '' if modal else 'not ')) |
1988 | + |
1989 | + position = dialog.get_property('window-position') |
1990 | + self.assertEqual(gtk.WIN_POS_CENTER, position, |
1991 | + '%s must be centered.' % dialog_name) |
1992 | + |
1993 | + actual = dialog.get_title() |
1994 | + msg = '%s title must be %s (got %s instead)' |
1995 | + self.assertEqual(title, actual, msg % (dialog_name, title, actual)) |
1996 | + |
1997 | + msg = '%s must have main_window as parent.' |
1998 | + #self.assertTrue(dialog.parent is self.ui.main_window, |
1999 | + # msg % dialog_name) |
2000 | |
2001 | |
2002 | class MagicicadaUIBasicTestCase(MagicicadaUITestCase): |
2003 | @@ -157,7 +231,7 @@ |
2004 | """SyncDaemon instance is shutdown at destroy time.""" |
2005 | self.patch(self.ui.sd, 'shutdown', self.set_called) |
2006 | self.ui.on_main_window_destroy(self.ui.main_window) |
2007 | - self.assertTrue(self._called, |
2008 | + self.assertTrue(self.called, |
2009 | 'syncdaemon.shutdown must be called at destroy time.') |
2010 | |
2011 | def test_main_window_is_visible(self): |
2012 | @@ -192,7 +266,7 @@ |
2013 | """Update is called at startup.""" |
2014 | self.patch(MagicicadaUI, 'update', self.set_called) |
2015 | self.ui = MagicicadaUI(syncdaemon_class=FakedSyncdaemon) |
2016 | - self.assertTrue(self._called, |
2017 | + self.assertTrue(self.called, |
2018 | 'update was called at startup.') |
2019 | |
2020 | |
2021 | @@ -215,11 +289,11 @@ |
2022 | """Test on_start_clicked.""" |
2023 | self.patch(self.ui.sd, 'start', self.set_called) |
2024 | self.ui.on_start_clicked(self.ui.start) |
2025 | - self.assertTrue(self._called, 'syncdaemon.start was called.') |
2026 | + self.assertTrue(self.called, 'syncdaemon.start was called.') |
2027 | |
2028 | def test_on_connect_clicked(self): |
2029 | """Test on_connect_clicked.""" |
2030 | - self.do_start() # need to be started |
2031 | + self.do_start() # need to be started |
2032 | self.ui.on_connect_clicked(self.ui.connect) |
2033 | |
2034 | self.assertTrue(self.ui.widget_is_visible(self.ui.connect)) |
2035 | @@ -234,7 +308,7 @@ |
2036 | """Test on_connect_clicked.""" |
2037 | self.patch(self.ui.sd, 'connect', self.set_called) |
2038 | self.ui.on_connect_clicked(self.ui.connect) |
2039 | - self.assertTrue(self._called, 'syncdaemon.connect was called.') |
2040 | + self.assertTrue(self.called, 'syncdaemon.connect was called.') |
2041 | |
2042 | def test_on_stop_clicked(self): |
2043 | """Test on_stop_clicked.""" |
2044 | @@ -243,7 +317,7 @@ |
2045 | self.patch(self.ui, 'on_disconnect_clicked', self.set_called) |
2046 | self.ui.on_stop_clicked(self.ui.stop) |
2047 | |
2048 | - self.assertFalse(self._called, 'on_disconnect_clicked was not called.') |
2049 | + self.assertFalse(self.called, 'on_disconnect_clicked was not called.') |
2050 | |
2051 | self.assertFalse(self.ui.widget_is_visible(self.ui.start)) |
2052 | self.assertTrue(self.ui.widget_is_visible(self.ui.stop)) |
2053 | @@ -259,13 +333,13 @@ |
2054 | self.patch(self.ui, 'on_disconnect_clicked', self.set_called) |
2055 | self.ui.on_stop_clicked(self.ui.stop) |
2056 | |
2057 | - self.assertTrue(self._called, 'on_disconnect_clicked was called.') |
2058 | + self.assertTrue(self.called, 'on_disconnect_clicked was called.') |
2059 | |
2060 | def test_on_stop_clicked_stops_syncdaemon(self): |
2061 | """Test on_stop_clicked.""" |
2062 | self.patch(self.ui.sd, 'quit', self.set_called) |
2063 | self.ui.on_stop_clicked(self.ui.stop) |
2064 | - self.assertTrue(self._called, 'syncdaemon.quit was called.') |
2065 | + self.assertTrue(self.called, 'syncdaemon.quit was called.') |
2066 | |
2067 | def test_on_disconnect_clicked(self): |
2068 | """Test on_disconnect_clicked.""" |
2069 | @@ -280,7 +354,7 @@ |
2070 | """Test on_disconnect_clicked.""" |
2071 | self.patch(self.ui.sd, 'disconnect', self.set_called) |
2072 | self.ui.on_disconnect_clicked(self.ui.disconnect) |
2073 | - self.assertTrue(self._called, 'syncdaemon.disconnect was called.') |
2074 | + self.assertTrue(self.called, 'syncdaemon.disconnect was called.') |
2075 | |
2076 | |
2077 | class MagicicadaUISystrayIconTestCase(MagicicadaUITestCase): |
2078 | @@ -294,14 +368,15 @@ |
2079 | |
2080 | def test_main_window_is_shown_when_clicked_after_hidden(self): |
2081 | """Main window is shown when the icon is clicked after hidden.""" |
2082 | - self.ui.on_status_icon_activate(self.ui.status_icon) # hide |
2083 | - self.ui.on_status_icon_activate(self.ui.status_icon) # show |
2084 | + self.ui.on_status_icon_activate(self.ui.status_icon) # hide |
2085 | + self.ui.on_status_icon_activate(self.ui.status_icon) # show |
2086 | msg = 'main_window should be visible when icon clicked after hidden.' |
2087 | self.assertTrue(self.ui.widget_is_visible(self.ui.main_window), msg) |
2088 | |
2089 | |
2090 | def skip_abstract_class(test): |
2091 | """If 'test' belongs to an abstract class, don't run it.""" |
2092 | + |
2093 | @wraps(test) |
2094 | def inner(klass): |
2095 | """Execute 'test' only if not in an abstract class.""" |
2096 | @@ -336,6 +411,24 @@ |
2097 | # operation path share node |
2098 | return res |
2099 | |
2100 | + def expected_markup(self, value): |
2101 | + """Return the markup for row at index 'i'.""" |
2102 | + processing_meta = self.name == META_QUEUE and \ |
2103 | + self.ui.sd.current_state.processing_meta |
2104 | + processing_content = self.name == CONTENT_QUEUE and \ |
2105 | + self.ui.sd.current_state.processing_content |
2106 | + assert not (processing_meta and processing_content) |
2107 | + must_highlight = self.ui.sd.current_state.is_online and \ |
2108 | + (processing_meta or processing_content) |
2109 | + result = self.ui.CURRENT_ROW % value \ |
2110 | + if must_highlight and value is not None else value |
2111 | + return result |
2112 | + |
2113 | + def assert_store_correct(self, store, items): |
2114 | + """Test that 'store' has 'items' as content.""" |
2115 | + args = (store, items, self.expected_markup) |
2116 | + super(_MagicicadaUIQueueTestCase, self).assert_store_correct(*args) |
2117 | + |
2118 | @skip_abstract_class |
2119 | def test_callback_is_connected(self): |
2120 | """Queue changed callback is connected.""" |
2121 | @@ -364,6 +457,13 @@ |
2122 | self.assert_store_correct(self.queue_store, []) |
2123 | |
2124 | @skip_abstract_class |
2125 | + def test_on_queue_changed_handles_an_item_none(self): |
2126 | + """On queue changed handles None as items.""" |
2127 | + items = [QueueData(operation='Test', path='', share=None, node=None)] |
2128 | + self.sd_changed(items) |
2129 | + self.assert_store_correct(self.queue_store, items) |
2130 | + |
2131 | + @skip_abstract_class |
2132 | def test_model_is_cleared_before_updating(self): |
2133 | """The model is cleared before upadting with a new set of data.""" |
2134 | items = self.build_some_data() |
2135 | @@ -400,7 +500,7 @@ |
2136 | cb = 'on_%s_queue_changed' % self.name |
2137 | self.patch(self.ui, cb, self.set_called) |
2138 | self.ui.on_stopped() |
2139 | - self.assertTrue(self._called, |
2140 | + self.assertTrue(self.called, |
2141 | '%s was called on_stopped.' % cb) |
2142 | |
2143 | @skip_abstract_class |
2144 | @@ -425,12 +525,64 @@ |
2145 | self.do_start() |
2146 | self.assert_store_correct(self.queue_store, items) |
2147 | |
2148 | - items = items[:len(items)/2] |
2149 | + items = items[:len(items) / 2] |
2150 | self.set_sd_queue(items) |
2151 | self.ui.on_stop_clicked(self.ui.stop) |
2152 | self.ui.on_stopped() |
2153 | self.assert_store_correct(self.queue_store, items) |
2154 | |
2155 | + def assert_current_processing_row_is_different(self): |
2156 | + """Row being processed is highlighted.""" |
2157 | + items = self.build_some_data() |
2158 | + self.sd_changed(items) |
2159 | + |
2160 | + item = items[0] |
2161 | + attrs = type(item)._fields |
2162 | + |
2163 | + markup = self.expected_markup |
2164 | + expected = tuple(markup(getattr(item, attr)) for attr in attrs) |
2165 | + |
2166 | + iter_root = self.queue_store.get_iter_root() |
2167 | + actual = self.queue_store.get(iter_root, *xrange(len(attrs))) |
2168 | + |
2169 | + msg = 'first row for %s queue must be %s (got %s instead)' % \ |
2170 | + (self.name, expected, actual) |
2171 | + self.assertEqual(expected, actual, msg) |
2172 | + |
2173 | + @skip_abstract_class |
2174 | + def test_current_processing_row_is_different_if_online(self): |
2175 | + """Row being processed is highlighted.""" |
2176 | + self.ui.sd.current_state.set(is_online=True) |
2177 | + |
2178 | + self.ui.sd.current_state.set(queues='') |
2179 | + self.assert_current_processing_row_is_different() |
2180 | + |
2181 | + self.ui.sd.current_state.set(queues='WORKING_ON_METADATA') |
2182 | + self.assert_current_processing_row_is_different() |
2183 | + |
2184 | + self.ui.sd.current_state.set(queues='WORKING_ON_CONTENT') |
2185 | + self.assert_current_processing_row_is_different() |
2186 | + |
2187 | + self.ui.sd.current_state.set(queues='WORKING_ON_BOTH') |
2188 | + self.assert_current_processing_row_is_different() |
2189 | + |
2190 | + @skip_abstract_class |
2191 | + def test_current_processing_row_is_not_different_if_offline(self): |
2192 | + """Row being processed is highlighted.""" |
2193 | + self.ui.sd.current_state.set(is_online=False) |
2194 | + |
2195 | + self.ui.sd.current_state.set(queues='') |
2196 | + self.assert_current_processing_row_is_different() |
2197 | + |
2198 | + self.ui.sd.current_state.set(queues='WORKING_ON_METADATA') |
2199 | + self.assert_current_processing_row_is_different() |
2200 | + |
2201 | + self.ui.sd.current_state.set(queues='WORKING_ON_CONTENT') |
2202 | + self.assert_current_processing_row_is_different() |
2203 | + |
2204 | + self.ui.sd.current_state.set(queues='WORKING_ON_BOTH') |
2205 | + self.assert_current_processing_row_is_different() |
2206 | + |
2207 | |
2208 | class MagicicadaUIContentQueueTestCase(_MagicicadaUIQueueTestCase): |
2209 | """UI test cases for content queue view.""" |
2210 | @@ -477,13 +629,14 @@ |
2211 | """Status callback is connected.""" |
2212 | self.assertEqual(self.ui.sd.status_changed_callback, |
2213 | self.ui.on_status_changed, |
2214 | - 'status_changed callback must be set') |
2215 | + 'status_changed callback must be set.') |
2216 | |
2217 | def test_status_label_ellipsizes(self): |
2218 | """The status label ellipsizes.""" |
2219 | expected = pango.ELLIPSIZE_END |
2220 | actual = self.ui.status_label.get_ellipsize() |
2221 | - self.assertEqual(expected, actual, 'label ellipsizes is ELLIPSIZE_END.') |
2222 | + self.assertEqual(expected, actual, |
2223 | + 'label ellipsizes is ELLIPSIZE_END.') |
2224 | |
2225 | def test_on_status_changed_updates_status_label(self): |
2226 | """On status changed, the status label is updated.""" |
2227 | @@ -493,7 +646,7 @@ |
2228 | |
2229 | def test_on_status_changed_updates_status_label_even_on_weird_cases(self): |
2230 | """On status changed, the status label is updated.""" |
2231 | - keywords = ('name', 'description', 'queues', 'connection') # need order |
2232 | + keywords = ('name', 'description', 'queues', 'connection') # order |
2233 | for attr in keywords: |
2234 | old_value = self.kwargs[attr] |
2235 | |
2236 | @@ -509,7 +662,7 @@ |
2237 | |
2238 | def test_update_is_correct_for_status_label(self): |
2239 | """Correctly updates the status label.""" |
2240 | - self.ui.sd.current_state._set(**self.kwargs) |
2241 | + self.ui.sd.current_state.set(**self.kwargs) |
2242 | self.ui.update() |
2243 | self.assert_status_label_correct(**self.kwargs) |
2244 | |
2245 | @@ -517,7 +670,7 @@ |
2246 | """On SD stoppped, the UI updates the status label.""" |
2247 | self.patch(self.ui, 'on_status_changed', self.set_called) |
2248 | self.ui.on_stopped() |
2249 | - self.assertTrue(self._called, |
2250 | + self.assertTrue(self.called, |
2251 | 'on_status_changed was called on_stopped.') |
2252 | |
2253 | def test_status_label_default_if_not_started(self): |
2254 | @@ -532,12 +685,12 @@ |
2255 | |
2256 | def test_status_label_is_updated_on_started_and_on_stopped(self): |
2257 | """Status label is updated on_started.""" |
2258 | - self.ui.sd.current_state._set(**self.kwargs) |
2259 | + self.ui.sd.current_state.set(**self.kwargs) |
2260 | self.do_start() |
2261 | self.assert_status_label_correct(**self.kwargs) |
2262 | |
2263 | self.kwargs['name'] = 'CHANGED' |
2264 | - self.ui.sd.current_state._set(**self.kwargs) |
2265 | + self.ui.sd.current_state.set(**self.kwargs) |
2266 | |
2267 | self.ui.on_stop_clicked(self.ui.stop) |
2268 | self.ui.on_stopped() |
2269 | @@ -551,7 +704,7 @@ |
2270 | """Test that correctly updates the 'indicator'.""" |
2271 | cs = self.ui.sd.current_state |
2272 | for expected in (True, False): |
2273 | - cs._set(**{indicator: expected}) |
2274 | + cs.set(**{indicator: expected}) |
2275 | |
2276 | self.ui.update() |
2277 | |
2278 | @@ -684,67 +837,64 @@ |
2279 | self.volume_dialog_name = '%s_dialog' % self.name |
2280 | self.volume_dialog = getattr(self.ui, self.volume_dialog_name) |
2281 | self.on_volume_clicked = getattr(self.ui, 'on_%s_clicked' % self.name) |
2282 | + self.volume_close = getattr(self.ui, '%s_close' % self.name) |
2283 | |
2284 | def build_some_data(self, limit=5): |
2285 | """Build some data to act as volume.""" |
2286 | kwargs = dict(data_type=self.data_type, limit=limit) |
2287 | - res = super(_MagicicadaUIVolumeTestCase, self).build_some_data(**kwargs) |
2288 | - return res |
2289 | + r = super(_MagicicadaUIVolumeTestCase, self).build_some_data(**kwargs) |
2290 | + return r |
2291 | |
2292 | - def assert_volume_availability(self, enabled): |
2293 | + def assert_widget_availability(self, enabled=True): |
2294 | """Check volume availability according to 'enabled'.""" |
2295 | - self.assertTrue(self.ui.widget_is_visible(self.volume), |
2296 | - '%s should be visible' % self.name) |
2297 | - sensitive = self.volume.is_sensitive() |
2298 | - msg = '%s should %sbe sensitive' |
2299 | - self.assertTrue(sensitive if enabled else not sensitive, |
2300 | - msg % (self.name, '' if enabled else 'not ')) |
2301 | + s = super(_MagicicadaUIVolumeTestCase, self) |
2302 | + s.assert_widget_availability(self.name, enabled) |
2303 | |
2304 | @skip_abstract_class |
2305 | def test_volume_are_disabled_until_started(self): |
2306 | """Folders and shares are disabled until online.""" |
2307 | # disabled at startup |
2308 | - self.assert_volume_availability(enabled=False) |
2309 | + self.assert_widget_availability(enabled=False) |
2310 | |
2311 | # enabled when started |
2312 | self.do_start() |
2313 | - self.assert_volume_availability(enabled=True) |
2314 | + self.assert_widget_availability(enabled=True) |
2315 | |
2316 | @skip_abstract_class |
2317 | def test_volume_are_enabled_until_stopped(self): |
2318 | """Folders and shares are enabled until offline.""" |
2319 | self.do_connect() |
2320 | - self.assert_volume_availability(enabled=True) |
2321 | + self.assert_widget_availability(enabled=True) |
2322 | |
2323 | self.ui.on_online() |
2324 | - self.assert_volume_availability(enabled=True) |
2325 | + self.assert_widget_availability(enabled=True) |
2326 | |
2327 | self.ui.on_offline() |
2328 | - self.assert_volume_availability(enabled=True) |
2329 | + self.assert_widget_availability(enabled=True) |
2330 | |
2331 | self.ui.on_disconnect_clicked(self.ui.disconnect) |
2332 | self.ui.on_disconnected() |
2333 | - self.assert_volume_availability(enabled=True) |
2334 | + self.assert_widget_availability(enabled=True) |
2335 | |
2336 | # disabled when stopped |
2337 | self.ui.on_stop_clicked(self.ui.stop) |
2338 | - self.assert_volume_availability(enabled=False) |
2339 | + self.assert_widget_availability(enabled=False) |
2340 | self.ui.on_stopped() |
2341 | - self.assert_volume_availability(enabled=False) |
2342 | + self.assert_widget_availability(enabled=False) |
2343 | |
2344 | @skip_abstract_class |
2345 | def test_volume_close_emits_response_close(self): |
2346 | """Test volume close button emits RESPONSE_CLOSE when clicked.""" |
2347 | - self.response = None |
2348 | + |
2349 | def record_response(value): |
2350 | """Record the response received.""" |
2351 | self.response = value |
2352 | + |
2353 | self.patch(self.volume_dialog, 'response', record_response) |
2354 | |
2355 | - volume_close = '%s_close' % self.name |
2356 | - getattr(self.ui, volume_close).clicked() |
2357 | + self.volume_close.clicked() |
2358 | self.assertEqual(gtk.RESPONSE_CLOSE, self.response, |
2359 | - '%s should emit RESPONSE_CLOSE.' % volume_close) |
2360 | + 'volume close button should emit RESPONSE_CLOSE.') |
2361 | |
2362 | @skip_abstract_class |
2363 | def test_on_volume_clicked(self): |
2364 | @@ -799,36 +949,25 @@ |
2365 | self.on_volume_clicked(self.volume) |
2366 | |
2367 | @skip_abstract_class |
2368 | - def test_volume_dialog_props(self): |
2369 | + def test_volume_dialog_properties(self): |
2370 | """The volume dialog has correct properties.""" |
2371 | - size = self.volume_dialog.size_request() |
2372 | - self.assertEquals((600, 300), size) |
2373 | - |
2374 | - self.assertTrue(self.volume_dialog.get_modal(), |
2375 | - '%s must be modal.' % self.volume_dialog_name) |
2376 | - |
2377 | - position = self.volume_dialog.get_property('window-position') |
2378 | - self.assertEqual(gtk.WIN_POS_CENTER, position, |
2379 | - '%s must be centered.' % self.volume_dialog_name) |
2380 | - |
2381 | - actual = self.volume_dialog.get_title() |
2382 | - expected = self.name.replace('_', ' ').capitalize() |
2383 | - msg = '%s title must be %s (got %s instead)' |
2384 | - self.assertEqual(expected, actual, |
2385 | - msg % (self.volume_dialog_name, expected, actual)) |
2386 | + title = self.name.replace('_', ' ').capitalize() |
2387 | + self.assert_dialog_properties(dialog_name=self.volume_dialog_name, |
2388 | + title=title) |
2389 | + |
2390 | |
2391 | class MagicicadaUIFoldersTestCase(_MagicicadaUIVolumeTestCase): |
2392 | """UI test cases for folders.""" |
2393 | |
2394 | name = 'folders' |
2395 | - data_type = FolderData # node path suggested_path subscribed volume |
2396 | + data_type = FolderData # node path suggested_path subscribed volume |
2397 | |
2398 | |
2399 | class _MagicicadaUISharesTestCase(_MagicicadaUIVolumeTestCase): |
2400 | """UI test cases for shares_to_me.""" |
2401 | |
2402 | - data_type = ShareData # accepted access_level free_bytes name node_id |
2403 | - # other_username other_visible_name path volume_id |
2404 | + data_type = ShareData # accepted access_level free_bytes name node_id |
2405 | + # other_username other_visible_name path volume_id |
2406 | |
2407 | @skip_abstract_class |
2408 | def test_bytes_are_humanized(self): |
2409 | @@ -862,3 +1001,279 @@ |
2410 | |
2411 | name = 'shares_to_others' |
2412 | |
2413 | + |
2414 | +class MagicicadaUIMetadataTestCase(MagicicadaUITestCase): |
2415 | + """UI test cases for metadata display.""" |
2416 | + |
2417 | + name = 'raw_metadata' |
2418 | + |
2419 | + def setUp(self): |
2420 | + """Init.""" |
2421 | + super(MagicicadaUIMetadataTestCase, self).setUp() |
2422 | + self.path = get_data_file('tests', 'metadata-test.txt') |
2423 | + self.metadata = dict(bla='ble', foo='bar') |
2424 | + |
2425 | + def assert_widget_availability(self, enabled=True): |
2426 | + """Check button availability according to 'enabled'.""" |
2427 | + s = super(MagicicadaUIMetadataTestCase, self) |
2428 | + s.assert_widget_availability(self.name, enabled) |
2429 | + |
2430 | + def assert_dialog_visibility(self, dialog, text_view, image): |
2431 | + """Check the visibility for dialog, text_view and image.""" |
2432 | + msg = '%s visibility should be %s (got %s instead).' |
2433 | + visible = self.ui.widget_is_visible(self.ui.raw_metadata_dialog) |
2434 | + self.assertEqual(dialog, visible, |
2435 | + msg % ('raw_metadata_dialog', dialog, visible)) |
2436 | + |
2437 | + visible = self.ui.widget_is_visible(self.ui.raw_metadata_view) |
2438 | + self.assertEqual(text_view, visible, |
2439 | + msg % ('raw_metadata_view', text_view, visible)) |
2440 | + |
2441 | + visible = self.ui.widget_is_visible(self.ui.raw_metadata_image) |
2442 | + self.assertEqual(image, visible, |
2443 | + msg % ('raw_metadata_image', image, visible)) |
2444 | + |
2445 | + def test_raw_metadata_are_disabled_until_started(self): |
2446 | + """Raw metadata button is disabled until online.""" |
2447 | + # disabled at startup |
2448 | + self.assert_widget_availability(enabled=False) |
2449 | + |
2450 | + # enabled when started |
2451 | + self.do_start() |
2452 | + self.assert_widget_availability(enabled=True) |
2453 | + |
2454 | + def test_raw_metadata_are_enabled_until_stopped(self): |
2455 | + """Raw metadata button is enabled until offline.""" |
2456 | + self.do_connect() |
2457 | + self.assert_widget_availability(enabled=True) |
2458 | + |
2459 | + self.ui.on_online() |
2460 | + self.assert_widget_availability(enabled=True) |
2461 | + |
2462 | + self.ui.on_offline() |
2463 | + self.assert_widget_availability(enabled=True) |
2464 | + |
2465 | + self.ui.on_disconnect_clicked(self.ui.disconnect) |
2466 | + self.ui.on_disconnected() |
2467 | + self.assert_widget_availability(enabled=True) |
2468 | + |
2469 | + # disabled when stopped |
2470 | + self.ui.on_stop_clicked(self.ui.stop) |
2471 | + self.assert_widget_availability(enabled=False) |
2472 | + self.ui.on_stopped() |
2473 | + self.assert_widget_availability(enabled=False) |
2474 | + |
2475 | + def test_raw_metadata_close_hides_the_dialog(self): |
2476 | + """Test raw_metadata close button emits RESPONSE_CLOSE when clicked.""" |
2477 | + self.ui.raw_metadata_close.clicked() |
2478 | + self.assertFalse(self.ui.widget_is_visible( |
2479 | + self.ui.raw_metadata_dialog), |
2480 | + 'raw_metadata_dialog should not be visible.') |
2481 | + |
2482 | + def test_file_chooser_open_emits_response_ok(self): |
2483 | + """Test volume close button emits RESPONSE_CLOSE when clicked.""" |
2484 | + |
2485 | + def record_response(value): |
2486 | + """Record the response received.""" |
2487 | + self.response = value |
2488 | + |
2489 | + self.patch(self.ui.file_chooser, 'response', record_response) |
2490 | + |
2491 | + self.ui.file_chooser_open.clicked() |
2492 | + self.assertEqual(gtk.FILE_CHOOSER_ACTION_OPEN, self.response, |
2493 | + 'open button should emit FILE_CHOOSER_ACTION_OPEN.') |
2494 | + |
2495 | + def test_on_raw_metadata_clicked(self): |
2496 | + """Test on_raw_metadata_clicked.""" |
2497 | + self.assertFalse(self.ui.widget_is_visible( |
2498 | + self.ui.raw_metadata_dialog), |
2499 | + 'raw_metadata_dialog should not be visible.') |
2500 | + |
2501 | + self.ui.file_chooser.set_filename(self.path) |
2502 | + |
2503 | + def test_file_chooser(): |
2504 | + """Auxiliar to assert over the file_chooser.""" |
2505 | + self.assertTrue(self.ui.widget_is_visible(self.ui.file_chooser), |
2506 | + 'file_chooser must be visible on metadata clicked.') |
2507 | + |
2508 | + gobject.timeout_add(100, test_and_click, |
2509 | + (self.ui.file_chooser_open, test_file_chooser)) |
2510 | + self.ui.on_raw_metadata_clicked(self.ui.raw_metadata) |
2511 | + |
2512 | + self.assertFalse(self.ui.widget_is_visible(self.ui.file_chooser), |
2513 | + 'file_chooser must be visible after metadata clicked.') |
2514 | + self.assertEqual(self.path, self.ui.file_chooser.get_filename(), |
2515 | + 'filename returned by file chooser must be correct.') |
2516 | + |
2517 | + # raw_metadata_dialog is enabled and shows the loading animation |
2518 | + self.assert_dialog_visibility(dialog=True, text_view=False, image=True) |
2519 | + expected = self.ui.raw_metadata_image.get_animation() |
2520 | + self.assertEqual(self.ui.loading_animation, expected, |
2521 | + 'raw_metadata_image must have the correct animation.') |
2522 | + |
2523 | + # Check that the metadata was asked to the SD |
2524 | + self.assertEqual(self.ui.sd._meta_path, self.path) |
2525 | + # SD will eventually callback us with the metadata |
2526 | + self.ui.on_metadata_ready(self.path, self.metadata) |
2527 | + |
2528 | + # raw_metadata_dialog is enabled and shows the metadata |
2529 | + self.assert_dialog_visibility(dialog=True, text_view=True, image=False) |
2530 | + |
2531 | + # user closes the dialog |
2532 | + self.ui.raw_metadata_close.clicked() |
2533 | + |
2534 | + # dialog was closed already |
2535 | + self.assertFalse(self.ui.widget_is_visible( |
2536 | + self.ui.raw_metadata_dialog), |
2537 | + 'raw_metadata_dialog should not be visible.') |
2538 | + |
2539 | + def test_raw_metadata_dialog_properties(self): |
2540 | + """The raw_metadata dialog has correct properties.""" |
2541 | + title = self.name.replace('_', ' ').capitalize() |
2542 | + self.assert_dialog_properties(dialog_name='raw_metadata_dialog', |
2543 | + title=title, modal=False) |
2544 | + |
2545 | + actual = self.ui.raw_metadata_view.get_wrap_mode() |
2546 | + msg = 'wrap mode for view must be gtk.WRAP_WORD (got %s instead).' |
2547 | + self.assertEqual(gtk.WRAP_WORD, actual, msg % actual) |
2548 | + |
2549 | + def test_callback_is_connected(self): |
2550 | + """Metadata ready callback is connected.""" |
2551 | + self.assertEqual(self.ui.sd.on_metadata_ready_callback, |
2552 | + self.ui.on_metadata_ready, |
2553 | + 'on_metadata_ready_callback callback must be set.') |
2554 | + |
2555 | + def test_file_chooser_is_hidden_at_startup(self): |
2556 | + """File chooser exists but is not visible.""" |
2557 | + self.assertFalse(self.ui.widget_is_visible(self.ui.file_chooser), |
2558 | + 'file_chooser must be hidden by default.') |
2559 | + |
2560 | + def test_file_chooser_current_folder_is_ubuntu_one_root(self): |
2561 | + """File chooser default folder is ~/Ubuntu One.""" |
2562 | + process_gtk_pendings() # WOW! Needed to get proper value below |
2563 | + actual = self.ui.file_chooser.get_current_folder() |
2564 | + msg = 'file_chooser default folder must be %s (got %s instead).' |
2565 | + self.assertEqual(actual, UBUNTU_ONE_ROOT, |
2566 | + msg % (UBUNTU_ONE_ROOT, actual)) |
2567 | + |
2568 | + def test_filename_is_used_only_if_open_clicked(self): |
2569 | + """Filename is used only if user clicked open.""" |
2570 | + self.patch(self.ui.sd, 'get_metadata', self.set_called) |
2571 | + gobject.timeout_add(100, test_and_click, |
2572 | + (self.ui.file_chooser_cancel, NO_OP)) |
2573 | + self.ui.on_raw_metadata_clicked(self.ui.raw_metadata) |
2574 | + |
2575 | + self.assertFalse(self.called, |
2576 | + 'get_metadata should not be called if no file chosen.') |
2577 | + |
2578 | + def test_filename_is_stored_if_open_clicked(self): |
2579 | + """Filename is stored as 'last_metadata_path' if user clicked open.""" |
2580 | + self.assertTrue(self.ui.last_metadata_path is None, |
2581 | + 'last_metadata_path must be None.') |
2582 | + self.ui.file_chooser.set_filename(self.path) |
2583 | + gobject.timeout_add(100, test_and_click, |
2584 | + (self.ui.file_chooser_open, NO_OP)) |
2585 | + self.ui.on_raw_metadata_clicked(self.ui.raw_metadata) |
2586 | + |
2587 | + self.assertEqual(self.path, self.ui.last_metadata_path, |
2588 | + 'last_metadata_path should be what the user choose.') |
2589 | + |
2590 | + def test_on_metadata_ready(self): |
2591 | + """Callback on_metadata_ready updates the raw_metadata_view.""" |
2592 | + path = 'bla' |
2593 | + self.ui.last_metadata_path = path |
2594 | + self.ui.on_metadata_ready(path, self.metadata) |
2595 | + |
2596 | + buff = self.ui.raw_metadata_view.get_buffer() |
2597 | + self.assertTrue(buff is not None, |
2598 | + 'buffer for raw_metadata_view must not be None.') |
2599 | + |
2600 | + expected = '\n'.join('%s: %s' % i for i in self.metadata.iteritems()) |
2601 | + actual = buff.get_text(*buff.get_bounds()) |
2602 | + msg = 'buffer content must be %s (got %s instead).' |
2603 | + self.assertEqual(actual, expected, |
2604 | + msg % (expected, actual)) |
2605 | + |
2606 | + def test_on_metadata_ready_doesnt_update_if_last_path_doesnt_match(self): |
2607 | + """Callback on_metadata_ready updates the raw_metadata_view.""" |
2608 | + self.patch(self.ui.raw_metadata_view.get_buffer(), |
2609 | + 'set_text', self.set_called) |
2610 | + path = 'bla' |
2611 | + self.ui.last_metadata_path = path + path |
2612 | + self.ui.on_metadata_ready(path, self.metadata) |
2613 | + |
2614 | + self.assertFalse(self.called, |
2615 | + 'view should not be updated if key is not last one.') |
2616 | + |
2617 | + |
2618 | +def override_input_output(input_args, output_args): |
2619 | + """Call 'f' but receive fixed input and return fixed output.""" |
2620 | + |
2621 | + def decorator(f): |
2622 | + """The decorator per se.""" |
2623 | + |
2624 | + @wraps(f) |
2625 | + def inner(*args, **kwargs): |
2626 | + """Feed 'f' with 'input_args' and return 'output_args'.""" |
2627 | + f(input_args) |
2628 | + return output_args |
2629 | + |
2630 | + return inner |
2631 | + |
2632 | + return decorator |
2633 | + |
2634 | + |
2635 | +class MagicicadaLoggingTestCase(MagicicadaUITestCase): |
2636 | + """UI test cases for logging.""" |
2637 | + |
2638 | + def setUp(self): |
2639 | + """Init.""" |
2640 | + super(MagicicadaLoggingTestCase, self).setUp() |
2641 | + |
2642 | + self.memento = MementoHandler() |
2643 | + self.memento.setLevel(logging.DEBUG) |
2644 | + logger = logging.getLogger('magicicada.ui') |
2645 | + logger.addHandler(self.memento) |
2646 | + |
2647 | + def assert_function_logs(self, func, *args, **kwargs): |
2648 | + """Check 'funcion' logs its inputs as DEBUG.""" |
2649 | + name = func.__name__ |
2650 | + msg = '%s must be logged as DEBUG' |
2651 | + try: |
2652 | + func(*args, **kwargs) |
2653 | + except Exception: # pylint: disable-msg=E0501, W0703 |
2654 | + exc = sys.exc_info() |
2655 | + self.assertTrue(self.memento.check_error(name), |
2656 | + 'function (%s) must be logged as ERROR' % name) |
2657 | + self.assertTrue(self.memento.check_error(exc), |
2658 | + 'sys.exc_info (%s) must be logged as ERROR' % exc) |
2659 | + self.assertTrue(self.memento.check_debug(name), msg % name) |
2660 | + for arg in args: |
2661 | + self.assertTrue(self.memento.check_debug(str(arg)), msg % arg) |
2662 | + for key, val in kwargs.iteritems(): |
2663 | + arg = "'%s': %r" % (key, val) |
2664 | + self.assertTrue(self.memento.check_debug(arg), msg % arg) |
2665 | + |
2666 | + def test_on_shares_clicked_logs(self): |
2667 | + """Check _on_shares_clicked logs properly.""" |
2668 | + args = ([0, object(), 'test', {}], object()) |
2669 | + kwargs = dict(dialog=object()) |
2670 | + self.assert_function_logs(self.ui._on_shares_clicked, *args, **kwargs) |
2671 | + |
2672 | + def test_on_status_changed_logs(self): |
2673 | + """Check _on_status_changed logs properly.""" |
2674 | + args = ('test status', 'status description', True, False, True) |
2675 | + kwargs = dict(queues='bla', connection=None) |
2676 | + self.assert_function_logs(self.ui.on_status_changed, *args, **kwargs) |
2677 | + |
2678 | + def test_on_queue_changed_logs(self): |
2679 | + """Check _on_queue_changed logs properly.""" |
2680 | + args = ('meta',) |
2681 | + kwargs = dict(items=[0, object(), 'test', {}], must_highlight=True) |
2682 | + self.assert_function_logs(self.ui._on_queue_changed, *args, **kwargs) |
2683 | + |
2684 | + def test_on_metadata_ready_logs(self): |
2685 | + """Check on_metadata_ready logs properly.""" |
2686 | + args = () |
2687 | + kwargs = dict(path='test', metadata=True) |
2688 | + self.assert_function_logs(self.ui.on_metadata_ready, *args, **kwargs) |
2689 | |
2690 | === modified file 'magicicada/tests/test_syncdaemon.py' |
2691 | --- magicicada/tests/test_syncdaemon.py 2010-06-08 10:53:02 +0000 |
2692 | +++ magicicada/tests/test_syncdaemon.py 2010-07-09 18:33:43 +0000 |
2693 | @@ -28,6 +28,10 @@ |
2694 | from twisted.internet import defer, reactor |
2695 | |
2696 | |
2697 | +# It's ok to access private data in the test suite |
2698 | +# pylint: disable-msg=W0212 |
2699 | + |
2700 | + |
2701 | class FakeDBusInterface(object): |
2702 | """Fake DBus Interface, for SD to not use dbus at all during tests.""" |
2703 | |
2704 | @@ -37,15 +41,18 @@ |
2705 | pass |
2706 | |
2707 | def shutdown(self): |
2708 | + """Fake shutdown.""" |
2709 | pass |
2710 | |
2711 | def get_status(self): |
2712 | """Fake status.""" |
2713 | return defer.succeed(('fakename', 'fakedescrip', False, True, |
2714 | False, 'fakequeues', 'fakeconnection')) |
2715 | + |
2716 | def get_folders(self): |
2717 | """Fake folders.""" |
2718 | return defer.succeed('fakedata') |
2719 | + |
2720 | get_content_queue = get_meta_queue = get_folders |
2721 | start = quit = connect = disconnect = get_folders |
2722 | get_shares_to_me = get_shares_to_others = get_folders |
2723 | @@ -133,8 +140,9 @@ |
2724 | |
2725 | @defer.inlineCallbacks |
2726 | def test_initial_value(self): |
2727 | - """Fills the status info initially.""" |
2728 | + """Fill the status info initially.""" |
2729 | called = [] |
2730 | + |
2731 | def fake(): |
2732 | """Fake method.""" |
2733 | called.append(True) |
2734 | @@ -167,7 +175,7 @@ |
2735 | return deferred |
2736 | |
2737 | def test_status_changed_affects_cuurent_status(self): |
2738 | - """Makes changes to see how status are reflected.""" |
2739 | + """Make changes to see how status are reflected.""" |
2740 | # one set of values |
2741 | self.sd.on_sd_status_changed('name1', 'description1', False, True, |
2742 | False, 'queues1', 'connection1') |
2743 | @@ -229,13 +237,42 @@ |
2744 | self.assertEqual(self.sd.current_state.queues, '') |
2745 | self.assertEqual(self.sd.current_state.connection, '') |
2746 | |
2747 | + def test_processing_meta_if_working_on_meta_or_both(self): |
2748 | + """Status.processing_meta is True when WORKING_ON_{METADATA,BOTH}.""" |
2749 | + |
2750 | + msg = 'processing_meta must be False when %s.' |
2751 | + for state in ('WORKING_ON_CONTENT', ''): |
2752 | + self.sd.current_state.set(queues=state) |
2753 | + self.assertFalse(self.sd.current_state.processing_meta, |
2754 | + msg % state) |
2755 | + |
2756 | + msg = 'processing_meta must be True when %s.' |
2757 | + for state in ('WORKING_ON_METADATA', 'WORKING_ON_BOTH'): |
2758 | + self.sd.current_state.set(queues=state) |
2759 | + self.assertTrue(self.sd.current_state.processing_meta, msg % state) |
2760 | + |
2761 | + def test_processing_content_if_working_on_content_or_both(self): |
2762 | + """Status.processing_content is True when WORKING_ON_{CONTENT,BOTH}.""" |
2763 | + |
2764 | + msg = 'processing_content must be False when %s.' |
2765 | + for state in ('WORKING_ON_METADATA', ''): |
2766 | + self.sd.current_state.set(queues=state) |
2767 | + self.assertFalse(self.sd.current_state.processing_content, |
2768 | + msg % state) |
2769 | + |
2770 | + msg = 'processing_content must be True when %s.' |
2771 | + for state in ('WORKING_ON_CONTENT', 'WORKING_ON_BOTH'): |
2772 | + self.sd.current_state.set(queues=state) |
2773 | + self.assertTrue(self.sd.current_state.processing_content, |
2774 | + msg % state) |
2775 | + |
2776 | |
2777 | class ContentQueueChangedTests(BaseTest): |
2778 | """Check the ContenQueueChanged handling.""" |
2779 | |
2780 | @defer.inlineCallbacks |
2781 | def test_initial_value(self): |
2782 | - """Fills the content queue info initially.""" |
2783 | + """Fill the content queue info initially.""" |
2784 | called = [] |
2785 | self.sd.dbus.get_content_queue = lambda: called.append(True) |
2786 | yield self.sd._get_initial_data() |
2787 | @@ -311,13 +348,19 @@ |
2788 | def setUp(self): |
2789 | """Set up.""" |
2790 | BaseTest.setUp(self) |
2791 | - self.sd.current_state._set(queues='WORKING_ON_METADATA') |
2792 | + self.sd.current_state.set(queues='WORKING_ON_METADATA') |
2793 | |
2794 | @defer.inlineCallbacks |
2795 | def test_initial_value(self): |
2796 | - """Fills the meta queue info initially.""" |
2797 | + """Fill the meta queue info initially.""" |
2798 | called = [] |
2799 | - self.sd.dbus.get_meta_queue = lambda: called.append(True) |
2800 | + |
2801 | + def f(): |
2802 | + """Helper function.""" |
2803 | + called.append(True) |
2804 | + return [] |
2805 | + |
2806 | + self.sd.dbus.get_meta_queue = f |
2807 | yield self.sd._get_initial_data() |
2808 | self.assertTrue(called) |
2809 | |
2810 | @@ -373,10 +416,12 @@ |
2811 | """Check that it polls mq while working in metadata not being in QM.""" |
2812 | # set the callback |
2813 | deferred = defer.Deferred() |
2814 | + |
2815 | def fake(): |
2816 | """Fake.""" |
2817 | deferred.callback(True) |
2818 | return defer.succeed("foo") |
2819 | + |
2820 | self.sd.dbus.get_meta_queue = fake |
2821 | |
2822 | # send status changed to working in metadata |
2823 | @@ -389,10 +434,12 @@ |
2824 | """Check that it polls mq while working in metadata being in QM.""" |
2825 | # set the callback |
2826 | deferred = defer.Deferred() |
2827 | + |
2828 | def fake(): |
2829 | """Fake.""" |
2830 | deferred.callback(True) |
2831 | return defer.succeed("foo") |
2832 | + |
2833 | self.sd.dbus.get_meta_queue = fake |
2834 | |
2835 | # send status changed to working in metadata |
2836 | @@ -405,10 +452,12 @@ |
2837 | """Check that it polls mq while working in both.""" |
2838 | # set the callback |
2839 | deferred = defer.Deferred() |
2840 | + |
2841 | def fake(): |
2842 | """Fake.""" |
2843 | deferred.callback(True) |
2844 | return defer.succeed("foo") |
2845 | + |
2846 | self.sd.dbus.get_meta_queue = fake |
2847 | |
2848 | # send status changed to working in metadata |
2849 | @@ -417,14 +466,47 @@ |
2850 | 'connection') |
2851 | return deferred |
2852 | |
2853 | + def test_mq_polls_last_time(self): |
2854 | + """Was polling, state changed, it needs to poll a last time.""" |
2855 | + # set the callback |
2856 | + deferred = defer.Deferred() |
2857 | + changed = self.sd.on_sd_status_changed |
2858 | + called = [] |
2859 | + |
2860 | + def fake(): |
2861 | + """Fake.""" |
2862 | + called.append(None) |
2863 | + if len(called) == 1: |
2864 | + changed('QUEUE_MANAGER', 'description', False, True, False, |
2865 | + 'WORKING_ON_CONTENT', 'connection') |
2866 | + elif len(called) == 2: |
2867 | + # check that the caller is set back to None |
2868 | + self.assertTrue(self.sd._mqcaller is None) |
2869 | + deferred.callback(True) |
2870 | + return defer.succeed("foo") |
2871 | + |
2872 | + self.sd.dbus.get_meta_queue = fake |
2873 | + |
2874 | + # send status changed to working in metadata |
2875 | + changed('QUEUE_MANAGER', 'description', False, True, False, |
2876 | + 'WORKING_ON_BOTH', 'connection') |
2877 | + return deferred |
2878 | + |
2879 | + def test_mq_caller_is_reset_last_time(self): |
2880 | + """When MQ is polled last time, the caller should be back to None.""" |
2881 | + self.sd._mqcaller = reactor.callLater(100, lambda: None) |
2882 | + self.sd.current_state.set(name='QUEUE_MANAGER', |
2883 | + queues='WORKING_ON_CONTENT') |
2884 | + |
2885 | + # call the method and check |
2886 | + self.sd._check_mq() |
2887 | + self.assertTrue(self.sd._mqcaller is None) |
2888 | + |
2889 | def test_mq_polling_untilfinish(self): |
2890 | """Check that it polls mq until no more is needed.""" |
2891 | - d2 = dict(name='QUEUE_MANAGER', queues='WORKING_ON_CONTENT', |
2892 | - description='description', is_error='', is_connected='True', |
2893 | - is_online='', connection='conn') |
2894 | - |
2895 | # set the callback, and adjust the polling time to faster |
2896 | calls = [] |
2897 | + |
2898 | def fake_get(*a): |
2899 | """Fake get.""" |
2900 | calls.append(None) |
2901 | @@ -437,6 +519,8 @@ |
2902 | |
2903 | # allow time to see if a mistaken call happens |
2904 | reactor.callLater(.5, deferred.callback, True) |
2905 | + elif len(calls) == 4: |
2906 | + pass # last call after state changed |
2907 | else: |
2908 | deferred.errback(ValueError("Too many calls")) |
2909 | return defer.succeed("foo") |
2910 | @@ -480,7 +564,7 @@ |
2911 | def test_set_one_value(self): |
2912 | """Set one value.""" |
2913 | st = State() |
2914 | - st._set(name=55) |
2915 | + st.set(name=55) |
2916 | |
2917 | # check the one is set, the rest not |
2918 | self.assertEqual(st.name, 55) |
2919 | @@ -489,7 +573,7 @@ |
2920 | def test_set_two_values(self): |
2921 | """Set two values.""" |
2922 | st = State() |
2923 | - st._set(name=55, description=77) |
2924 | + st.set(name=55, description=77) |
2925 | |
2926 | # check those two are set, the rest not |
2927 | self.assertEqual(st.name, 55) |
2928 | @@ -499,7 +583,7 @@ |
2929 | def test_bad_value(self): |
2930 | """Set a value that should not.""" |
2931 | st = State() |
2932 | - self.assertRaises(AttributeError, st._set, not_really_allowed=44) |
2933 | + self.assertRaises(AttributeError, st.set, not_really_allowed=44) |
2934 | |
2935 | |
2936 | class APITests(unittest.TestCase): |
2937 | @@ -617,7 +701,6 @@ |
2938 | self.assertTrue(self.called) |
2939 | |
2940 | |
2941 | - |
2942 | class TestLogs(unittest.TestCase): |
2943 | """Test logging.""" |
2944 | |
2945 | @@ -634,100 +717,128 @@ |
2946 | |
2947 | def test_instancing(self): |
2948 | """Just logged SD instancing.""" |
2949 | - self.assertTrue(self.hdlr.check_inf("SyncDaemon interface started!")) |
2950 | + self.assertTrue(self.hdlr.check_info("SyncDaemon interface started!")) |
2951 | |
2952 | def test_shutdown(self): |
2953 | """Log when SD shutdowns.""" |
2954 | self.sd.shutdown() |
2955 | - self.assertTrue(self.hdlr.check_inf("SyncDaemon interface going down")) |
2956 | + msg = "SyncDaemon interface going down" |
2957 | + self.assertTrue(self.hdlr.check_info(msg)) |
2958 | |
2959 | @defer.inlineCallbacks |
2960 | def test_initial_value(self): |
2961 | """Log the initial filling.""" |
2962 | yield self.sd._get_initial_data() |
2963 | - self.assertTrue(self.hdlr.check_inf("Getting initial data")) |
2964 | + self.assertTrue(self.hdlr.check_info("Getting initial data")) |
2965 | |
2966 | def test_start(self): |
2967 | """Log the call to start.""" |
2968 | self.sd.start() |
2969 | - self.assertTrue(self.hdlr.check_inf("Starting u1.SD")) |
2970 | + self.assertTrue(self.hdlr.check_info("Starting u1.SD")) |
2971 | |
2972 | def test_quit(self): |
2973 | """Log the call to quit.""" |
2974 | self.sd.quit() |
2975 | - self.assertTrue(self.hdlr.check_inf("Stopping u1.SD")) |
2976 | + self.assertTrue(self.hdlr.check_info("Stopping u1.SD")) |
2977 | |
2978 | def test_connect(self): |
2979 | """Log the call to connect.""" |
2980 | self.sd.connect() |
2981 | - self.assertTrue(self.hdlr.check_inf("Telling u1.SD to connect")) |
2982 | + self.assertTrue(self.hdlr.check_info("Telling u1.SD to connect")) |
2983 | |
2984 | def test_disconnect(self): |
2985 | """Log the call to disconnect.""" |
2986 | self.sd.disconnect() |
2987 | - self.assertTrue(self.hdlr.check_inf("Telling u1.SD to disconnect")) |
2988 | + self.assertTrue(self.hdlr.check_info("Telling u1.SD to disconnect")) |
2989 | |
2990 | def test_check_mq_true(self): |
2991 | """Log the MQ check when it asks for info.""" |
2992 | - self.sd.current_state._set(name='QUEUE_MANAGER', |
2993 | + self.sd.current_state.set(name='QUEUE_MANAGER', |
2994 | queues='WORKING_ON_METADATA') |
2995 | self.sd._check_mq() |
2996 | - self.assertTrue(self.hdlr.check_inf("Asking for MQ information")) |
2997 | + self.assertTrue(self.hdlr.check_info("Asking for MQ information")) |
2998 | |
2999 | def test_check_mq_noreally(self): |
3000 | """Log the MQ check when it should not work.""" |
3001 | - self.sd.current_state._set(name='QUEUE_MANAGER', |
3002 | + self.sd.current_state.set(name='QUEUE_MANAGER', |
3003 | queues='WORKING_ON_CONTENT') |
3004 | self.sd._check_mq() |
3005 | - self.assertTrue(self.hdlr.check_inf( |
3006 | - "Check MQ called but States not in MQ")) |
3007 | + self.assertTrue(self.hdlr.check_info( |
3008 | + "Check MQ called, States not in MQ, call a last time")) |
3009 | |
3010 | def test_meta_queue_changed(self): |
3011 | """Log that MQ has new data.""" |
3012 | self.sd.dbus.get_meta_queue = lambda: defer.succeed(['foo']) |
3013 | - self.sd.current_state._set(name='QUEUE_MANAGER', |
3014 | + self.sd.current_state.set(name='QUEUE_MANAGER', |
3015 | queues='WORKING_ON_METADATA') |
3016 | self.sd._check_mq() |
3017 | - self.assertTrue(self.hdlr.check_inf("SD Meta Queue changed: 1 items")) |
3018 | + self.assertTrue(self.hdlr.check_info("SD Meta Queue changed: 1 items")) |
3019 | |
3020 | def test_content_queue_changed(self): |
3021 | """Log that process_cq has new data.""" |
3022 | self.sd.dbus.get_content_queue = lambda: defer.succeed(['foo']) |
3023 | self.sd.on_sd_content_queue_changed() |
3024 | - self.assertTrue(self.hdlr.check_inf("SD Content Queue changed")) |
3025 | - self.assertTrue(self.hdlr.check_inf( |
3026 | + self.assertTrue(self.hdlr.check_info("SD Content Queue changed")) |
3027 | + self.assertTrue(self.hdlr.check_info( |
3028 | "Content Queue info is new! 1 items")) |
3029 | |
3030 | def test_on_status_changed(self): |
3031 | """Log status changed.""" |
3032 | self.sd.on_sd_status_changed('name', 'description', False, True, |
3033 | False, 'queues', 'connection') |
3034 | - self.assertTrue(self.hdlr.check_inf("SD Status changed")) |
3035 | - self.assertTrue(self.hdlr.check_dbg(" new status: name=u'name', " |
3036 | + self.assertTrue(self.hdlr.check_info("SD Status changed")) |
3037 | + self.assertTrue(self.hdlr.check_debug(" new status: name=u'name', " |
3038 | "description=u'description', is_error=False, is_connected=True, " |
3039 | "is_online=False, queues=u'queues', connection=u'connection'")) |
3040 | |
3041 | def test_folders_changed(self): |
3042 | """Log when folders changed.""" |
3043 | self.sd.on_sd_folders_changed() |
3044 | - self.assertTrue(self.hdlr.check_inf("SD Folders changed")) |
3045 | + self.assertTrue(self.hdlr.check_info("SD Folders changed")) |
3046 | |
3047 | def test_shares_changed(self): |
3048 | """Log when shares changed.""" |
3049 | self.sd.on_sd_shares_changed() |
3050 | - self.assertTrue(self.hdlr.check_inf("SD Shares changed")) |
3051 | + self.assertTrue(self.hdlr.check_info("SD Shares changed")) |
3052 | |
3053 | def test_on_name_owner_changed(self): |
3054 | """Log name owner changed.""" |
3055 | self.sd.on_sd_name_owner_changed(True) |
3056 | - self.assertTrue(self.hdlr.check_inf("SD Name Owner changed: True")) |
3057 | + self.assertTrue(self.hdlr.check_info("SD Name Owner changed: True")) |
3058 | + |
3059 | + |
3060 | +class MetadataTests(BaseTest): |
3061 | + """Get Metadata info.""" |
3062 | + |
3063 | + def test_get_metadata_no_callback_set(self): |
3064 | + """It's mandatory to set the callback for this response.""" |
3065 | + self.assertRaises(ValueError, self.sd.get_metadata, 'path') |
3066 | + |
3067 | + def test_get_metadata_ok(self): |
3068 | + """Get the metadata for given path.""" |
3069 | + called = [] |
3070 | + self.sd.dbus.get_metadata = lambda p: defer.succeed('foo') |
3071 | + self.sd.on_metadata_ready_callback = lambda *a: called.extend(a) |
3072 | + self.sd.get_metadata('path') |
3073 | + self.assertEqual(called, ['path', 'foo']) |
3074 | + |
3075 | + def test_get_metadata_double(self): |
3076 | + """Get the metadata twice.""" |
3077 | + called = [] |
3078 | + fake_md = {'path1': 'foo', 'path2': 'bar'} |
3079 | + self.sd.dbus.get_metadata = lambda p: defer.succeed(fake_md[p]) |
3080 | + self.sd.on_metadata_ready_callback = lambda *a: called.append(a) |
3081 | + self.sd.get_metadata('path1') |
3082 | + self.sd.get_metadata('path2') |
3083 | + self.assertEqual(called[0], ('path1', 'foo')) |
3084 | + self.assertEqual(called[1], ('path2', 'bar')) |
3085 | |
3086 | |
3087 | class FoldersTests(BaseTest): |
3088 | """Folders checking.""" |
3089 | |
3090 | def test_foldercreated_callback(self): |
3091 | - """Gets the new data after the folders changed.""" |
3092 | + """Get the new data after the folders changed.""" |
3093 | # set the callback |
3094 | called = [] |
3095 | self.sd.dbus.get_folders = lambda: called.append(True) |
3096 | @@ -740,7 +851,7 @@ |
3097 | |
3098 | @defer.inlineCallbacks |
3099 | def test_initial_value(self): |
3100 | - """Fills the folder info initially.""" |
3101 | + """Fill the folder info initially.""" |
3102 | called = [] |
3103 | self.sd.dbus.get_folders = lambda: called.append(True) |
3104 | yield self.sd._get_initial_data() |
3105 | @@ -763,7 +874,7 @@ |
3106 | """Shares checking.""" |
3107 | |
3108 | def test_shares_changed_callback(self): |
3109 | - """Gets the new data after the shares changed.""" |
3110 | + """Get the new data after the shares changed.""" |
3111 | # set the callback |
3112 | called = [] |
3113 | self.sd.dbus.get_shares_to_me = lambda: called.append(True) |
3114 | @@ -777,7 +888,7 @@ |
3115 | |
3116 | @defer.inlineCallbacks |
3117 | def test_initial_value(self): |
3118 | - """Fills the folder info initially.""" |
3119 | + """Fill the folder info initially.""" |
3120 | called = [] |
3121 | self.sd.dbus.get_shares_to_me = lambda: called.append(True) |
3122 | self.sd.dbus.get_shares_to_others = lambda: called.append(True) |
3123 | @@ -793,7 +904,7 @@ |
3124 | |
3125 | # they changed! |
3126 | self.sd.shares_to_me = 'foo' |
3127 | - self.sd.shares_to_others = 'fakedata' # what fake dbus will return |
3128 | + self.sd.shares_to_others = 'fakedata' # what fake dbus will return |
3129 | self.sd.on_sd_shares_changed() |
3130 | |
3131 | # test |
3132 | @@ -808,7 +919,7 @@ |
3133 | |
3134 | # they changed! |
3135 | self.sd.shares_to_others = 'foo' |
3136 | - self.sd.shares_to_me = 'fakedata' # what fake dbus will return |
3137 | + self.sd.shares_to_me = 'fakedata' # what fake dbus will return |
3138 | self.sd.on_sd_shares_changed() |
3139 | |
3140 | # test |
3141 | |
3142 | === modified file 'setup.py' |
3143 | --- setup.py 2010-06-08 10:53:02 +0000 |
3144 | +++ setup.py 2010-07-09 18:33:43 +0000 |
3145 | @@ -4,7 +4,9 @@ |
3146 | # This file is in the public domain |
3147 | ### END LICENSE |
3148 | |
3149 | -###################### DO NOT TOUCH THIS (HEAD TO THE SECOND PART) ###################### |
3150 | +"""Build tar.gz and related for magicicada.""" |
3151 | + |
3152 | +################# DO NOT TOUCH THIS (HEAD TO THE SECOND PART) ################# |
3153 | |
3154 | import os |
3155 | import sys |
3156 | @@ -12,24 +14,28 @@ |
3157 | try: |
3158 | import DistUtilsExtra.auto |
3159 | except ImportError: |
3160 | - print >> sys.stderr, 'To build magicicada you need https://launchpad.net/python-distutils-extra' |
3161 | + url = 'https://launchpad.net/python-distutils-extra' |
3162 | + print >> sys.stderr, 'To build magicicada you need', url |
3163 | sys.exit(1) |
3164 | -assert DistUtilsExtra.auto.__version__ >= '2.18', 'needs DistUtilsExtra.auto >= 2.18' |
3165 | +assert DistUtilsExtra.auto.__version__ >= '2.18', \ |
3166 | + 'needs DistUtilsExtra.auto >= 2.18' |
3167 | + |
3168 | |
3169 | def update_data_path(prefix, oldvalue=None): |
3170 | + """Update data path.""" |
3171 | |
3172 | try: |
3173 | fin = file('magicicada/magicicadaconfig.py', 'r') |
3174 | fout = file(fin.name + '.new', 'w') |
3175 | |
3176 | - for line in fin: |
3177 | - fields = line.split(' = ') # Separate variable from value |
3178 | + for line in fin: |
3179 | + fields = line.split(' = ') # Separate variable from value |
3180 | if fields[0] == '__magicicada_data_directory__': |
3181 | # update to prefix, store oldvalue |
3182 | if not oldvalue: |
3183 | oldvalue = fields[1] |
3184 | line = "%s = '%s'\n" % (fields[0], prefix) |
3185 | - else: # restore oldvalue |
3186 | + else: # restore oldvalue |
3187 | line = "%s = %s" % (fields[0], oldvalue) |
3188 | fout.write(line) |
3189 | |
3190 | @@ -37,19 +43,20 @@ |
3191 | fout.close() |
3192 | fin.close() |
3193 | os.rename(fout.name, fin.name) |
3194 | - except (OSError, IOError), e: |
3195 | + except (OSError, IOError): |
3196 | print ("ERROR: Can't find magicicada/magicicadaconfig.py") |
3197 | sys.exit(1) |
3198 | return oldvalue |
3199 | |
3200 | |
3201 | def update_desktop_file(datadir): |
3202 | + """Update desktop file.""" |
3203 | |
3204 | try: |
3205 | fin = file('magicicada.desktop.in', 'r') |
3206 | fout = file(fin.name + '.new', 'w') |
3207 | |
3208 | - for line in fin: |
3209 | + for line in fin: |
3210 | if 'Icon=' in line: |
3211 | line = "Icon=%s\n" % (datadir + 'media/icon.png') |
3212 | fout.write(line) |
3213 | @@ -57,33 +64,33 @@ |
3214 | fout.close() |
3215 | fin.close() |
3216 | os.rename(fout.name, fin.name) |
3217 | - except (OSError, IOError), e: |
3218 | + except (OSError, IOError): |
3219 | print ("ERROR: Can't find magicicada.desktop.in") |
3220 | sys.exit(1) |
3221 | |
3222 | |
3223 | class InstallAndUpdateDataDirectory(DistUtilsExtra.auto.install_auto): |
3224 | + """Install and update data dir.""" |
3225 | + |
3226 | def run(self): |
3227 | + """Run.""" |
3228 | previous_value = update_data_path(self.prefix + '/share/magicicada/') |
3229 | update_desktop_file(self.prefix + '/share/magicicada/') |
3230 | DistUtilsExtra.auto.install_auto.run(self) |
3231 | update_data_path(self.prefix, previous_value) |
3232 | |
3233 | |
3234 | - |
3235 | -################################################################################## |
3236 | -###################### YOU SHOULD MODIFY ONLY WHAT IS BELOW ###################### |
3237 | -################################################################################## |
3238 | +############################################################################## |
3239 | +#################### YOU SHOULD MODIFY ONLY WHAT IS BELOW #################### |
3240 | +############################################################################## |
3241 | |
3242 | DistUtilsExtra.auto.setup( |
3243 | name='magicicada', |
3244 | - version='0.1.1', |
3245 | + version='0.1.2', |
3246 | license='GPL-3', |
3247 | author='Natalia Bidart', |
3248 | author_email='natalia.bidart@ubuntu.com', |
3249 | - description='A GTK+ frontend for the "Chicharra" part of Ubuntu One client.', |
3250 | + description='A GTK+ frontend for the "Chicharra" part of Ubuntu One.', |
3251 | #long_description='Here a longer description', |
3252 | url='https://launchpad.net/magicicada', |
3253 | - cmdclass={'install': InstallAndUpdateDataDirectory} |
3254 | - ) |
3255 | - |
3256 | + cmdclass={'install': InstallAndUpdateDataDirectory}) |