Merge lp:~statik/ubuntu/maverick/magicicada/zero-one-two into lp:ubuntu/maverick/magicicada

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
Reviewer Review Type Date Requested Status
Ubuntu branches Pending
Review via email: mp+29596@code.launchpad.net

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
=== modified file 'PKG-INFO'
--- PKG-INFO 2010-06-08 10:53:02 +0000
+++ PKG-INFO 2010-07-09 18:33:43 +0000
@@ -1,7 +1,7 @@
1Metadata-Version: 1.11Metadata-Version: 1.1
2Name: magicicada2Name: magicicada
3Version: 0.1.13Version: 0.1.2
4Summary: A GTK+ frontend for the "Chicharra" part of Ubuntu One client.4Summary: A GTK+ frontend for the "Chicharra" part of Ubuntu One.
5Home-page: https://launchpad.net/magicicada5Home-page: https://launchpad.net/magicicada
6Author: Natalia Bidart6Author: Natalia Bidart
7Author-email: natalia.bidart@ubuntu.com7Author-email: natalia.bidart@ubuntu.com
@@ -14,6 +14,6 @@
14Requires: pango14Requires: pango
15Requires: twisted.internet15Requires: twisted.internet
16Requires: twisted.trial.unittest16Requires: twisted.trial.unittest
17Requires: ubuntuone.syncdaemon17Requires: ubuntuone.syncdaemon.tools
18Requires: xdg.BaseDirectory18Requires: xdg.BaseDirectory
19Provides: magicicada19Provides: magicicada
2020
=== modified file 'README.txt'
--- README.txt 2010-06-08 10:53:02 +0000
+++ README.txt 2010-07-09 18:33:43 +0000
@@ -1,3 +1,16 @@
1This is Magicicada!1This is Magicicada!
22
3A GTK+ frontend for the "Chicharra" part of Ubuntu One client.3A GTK+ frontend for the "Chicharra" part of Ubuntu One client.
4
5-----------
6HOWTO do a source release:
7
8 * edit setup.py and increment the version number.
9 * 'python setup.py sdist'
10 * look at the contents of the tarball created in dist/ to be sure they are ok
11 * sign the tarball by a command like:
12 gpg -a --detach-sign magicicada-0.2.tar.gz
13 this should create a file like magicicada-0.2.tar.gz.asc
14 * Upload the new release to launchpad with a command like:
15 lp-project-upload magicicada 0.2 magicicada-0.2.tar.gz
16 * Announce the release, ping someone to build updated packages for the PPA and Ubuntu.
417
=== modified file 'bin/magicicada'
--- bin/magicicada 2010-06-08 10:53:02 +0000
+++ bin/magicicada 2010-07-09 18:33:43 +0000
@@ -1,8 +1,21 @@
1#!/usr/bin/python1#!/usr/bin/python
2# -*- coding: utf-8 -*-2# -*- coding: utf-8 -*-
3### BEGIN LICENSE3#
4# This file is in the public domain4# Copyright 2010 Chicharreros
5### END LICENSE5#
6# This program is free software: you can redistribute it and/or modify it
7# under the terms of the GNU General Public License version 3, as published
8# by the Free Software Foundation.
9#
10# This program is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranties of
12# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13# PURPOSE. See the GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program. If not, see <http://www.gnu.org/licenses/>.
17
18"""Script to run Magicicada."""
619
7import sys20import sys
8import os21import os
@@ -11,14 +24,6 @@
11from gettext import gettext as _24from gettext import gettext as _
12gettext.textdomain('magicicada')25gettext.textdomain('magicicada')
1326
14# optional Launchpad integration
15# this shouldn't crash if not found as it is simply used for bug reporting
16try:
17 import LaunchpadIntegration
18 launchpad_available = True
19except:
20 launchpad_available = False
21
22# Add project root directory (enable symlink, and trunk execution).27# Add project root directory (enable symlink, and trunk execution).
23PROJECT_ROOT_DIRECTORY = os.path.abspath(28PROJECT_ROOT_DIRECTORY = os.path.abspath(
24 os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0]))))29 os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0]))))
@@ -48,6 +53,5 @@
48 from twisted.internet import reactor53 from twisted.internet import reactor
4954
50 # Run the application.55 # Run the application.
51 window = MagicicadaUI(launchpad_available=launchpad_available,56 window = MagicicadaUI(on_destroy=lambda *a, **kw: reactor.stop())
52 on_destroy=lambda *a, **kw: reactor.stop())
53 reactor.run()57 reactor.run()
5458
=== added symlink 'data/media/icon.png'
=== target is u'logo-032.png'
=== added directory 'data/tests'
=== added file 'data/tests/metadata-test.txt'
=== modified file 'data/ui/gui.glade'
--- data/ui/gui.glade 2010-06-08 10:53:02 +0000
+++ data/ui/gui.glade 2010-07-09 18:33:43 +0000
@@ -257,6 +257,7 @@
257 </child>257 </child>
258 <child>258 <child>
259 <object class="GtkToolButton" id="raw_metadata">259 <object class="GtkToolButton" id="raw_metadata">
260 <property name="visible">True</property>
260 <property name="sensitive">False</property>261 <property name="sensitive">False</property>
261 <property name="label" translatable="yes">Metadata</property>262 <property name="label" translatable="yes">Metadata</property>
262 <property name="use_underline">True</property>263 <property name="use_underline">True</property>
@@ -384,7 +385,7 @@
384 <child>385 <child>
385 <object class="GtkCellRendererText" id="cellrenderertext1"/>386 <object class="GtkCellRendererText" id="cellrenderertext1"/>
386 <attributes>387 <attributes>
387 <attribute name="text">0</attribute>388 <attribute name="markup">0</attribute>
388 </attributes>389 </attributes>
389 </child>390 </child>
390 </object>391 </object>
@@ -396,7 +397,7 @@
396 <child>397 <child>
397 <object class="GtkCellRendererText" id="cellrenderertext2"/>398 <object class="GtkCellRendererText" id="cellrenderertext2"/>
398 <attributes>399 <attributes>
399 <attribute name="text">1</attribute>400 <attribute name="markup">1</attribute>
400 </attributes>401 </attributes>
401 </child>402 </child>
402 </object>403 </object>
@@ -408,7 +409,7 @@
408 <child>409 <child>
409 <object class="GtkCellRendererText" id="cellrenderertext4"/>410 <object class="GtkCellRendererText" id="cellrenderertext4"/>
410 <attributes>411 <attributes>
411 <attribute name="text">2</attribute>412 <attribute name="markup">2</attribute>
412 </attributes>413 </attributes>
413 </child>414 </child>
414 </object>415 </object>
@@ -420,7 +421,7 @@
420 <child>421 <child>
421 <object class="GtkCellRendererText" id="cellrenderertext3"/>422 <object class="GtkCellRendererText" id="cellrenderertext3"/>
422 <attributes>423 <attributes>
423 <attribute name="text">3</attribute>424 <attribute name="markup">3</attribute>
424 </attributes>425 </attributes>
425 </child>426 </child>
426 </object>427 </object>
@@ -476,7 +477,7 @@
476 <child>477 <child>
477 <object class="GtkCellRendererText" id="cellrenderertext5"/>478 <object class="GtkCellRendererText" id="cellrenderertext5"/>
478 <attributes>479 <attributes>
479 <attribute name="text">0</attribute>480 <attribute name="markup">0</attribute>
480 </attributes>481 </attributes>
481 </child>482 </child>
482 </object>483 </object>
@@ -488,7 +489,7 @@
488 <child>489 <child>
489 <object class="GtkCellRendererText" id="cellrenderertext6"/>490 <object class="GtkCellRendererText" id="cellrenderertext6"/>
490 <attributes>491 <attributes>
491 <attribute name="text">1</attribute>492 <attribute name="markup">1</attribute>
492 </attributes>493 </attributes>
493 </child>494 </child>
494 </object>495 </object>
@@ -500,7 +501,7 @@
500 <child>501 <child>
501 <object class="GtkCellRendererText" id="cellrenderertext8"/>502 <object class="GtkCellRendererText" id="cellrenderertext8"/>
502 <attributes>503 <attributes>
503 <attribute name="text">2</attribute>504 <attribute name="markup">2</attribute>
504 </attributes>505 </attributes>
505 </child>506 </child>
506 </object>507 </object>
@@ -512,7 +513,7 @@
512 <child>513 <child>
513 <object class="GtkCellRendererText" id="cellrenderertext7"/>514 <object class="GtkCellRendererText" id="cellrenderertext7"/>
514 <attributes>515 <attributes>
515 <attribute name="text">3</attribute>516 <attribute name="markup">3</attribute>
516 </attributes>517 </attributes>
517 </child>518 </child>
518 </object>519 </object>
@@ -1051,4 +1052,65 @@
1051 <action-widget response="0">shares_to_others_close</action-widget>1052 <action-widget response="0">shares_to_others_close</action-widget>
1052 </action-widgets>1053 </action-widgets>
1053 </object>1054 </object>
1055 <object class="GtkFileChooserDialog" id="file_chooser">
1056 <property name="border_width">5</property>
1057 <property name="type_hint">normal</property>
1058 <property name="has_separator">False</property>
1059 <property name="create_folders">False</property>
1060 <signal name="file_activated" handler="on_file_chooser_open_clicked"/>
1061 <child internal-child="vbox">
1062 <object class="GtkVBox" id="dialog-vbox7">
1063 <property name="visible">True</property>
1064 <property name="spacing">2</property>
1065 <child>
1066 <placeholder/>
1067 </child>
1068 <child internal-child="action_area">
1069 <object class="GtkHButtonBox" id="dialog-action_area7">
1070 <property name="visible">True</property>
1071 <property name="layout_style">end</property>
1072 <child>
1073 <object class="GtkButton" id="file_chooser_cancel">
1074 <property name="label">gtk-cancel</property>
1075 <property name="visible">True</property>
1076 <property name="can_focus">True</property>
1077 <property name="receives_default">True</property>
1078 <property name="use_stock">True</property>
1079 </object>
1080 <packing>
1081 <property name="expand">False</property>
1082 <property name="fill">False</property>
1083 <property name="position">0</property>
1084 </packing>
1085 </child>
1086 <child>
1087 <object class="GtkButton" id="file_chooser_open">
1088 <property name="label">gtk-open</property>
1089 <property name="visible">True</property>
1090 <property name="can_focus">True</property>
1091 <property name="receives_default">True</property>
1092 <property name="use_stock">True</property>
1093 <signal name="clicked" handler="on_file_chooser_open_clicked"/>
1094 <signal name="activate" handler="on_file_chooser_open_clicked"/>
1095 </object>
1096 <packing>
1097 <property name="expand">False</property>
1098 <property name="fill">False</property>
1099 <property name="position">1</property>
1100 </packing>
1101 </child>
1102 </object>
1103 <packing>
1104 <property name="expand">False</property>
1105 <property name="pack_type">end</property>
1106 <property name="position">0</property>
1107 </packing>
1108 </child>
1109 </object>
1110 </child>
1111 <action-widgets>
1112 <action-widget response="-6">file_chooser_cancel</action-widget>
1113 <action-widget response="0">file_chooser_open</action-widget>
1114 </action-widgets>
1115 </object>
1054</interface>1116</interface>
10551117
=== modified file 'debian/changelog'
--- debian/changelog 2010-06-08 10:53:02 +0000
+++ debian/changelog 2010-07-09 18:33:43 +0000
@@ -1,3 +1,9 @@
1magicicada (0.1.2-0ubuntu1) maverick; urgency=low
2
3 * New upstream release.
4
5 -- Elliot Murphy <elliot@ubuntu.com> Fri, 09 Jul 2010 14:14:53 -0400
6
1magicicada (0.1.1-0ubuntu1) maverick; urgency=low7magicicada (0.1.1-0ubuntu1) maverick; urgency=low
28
3 [ Elliot Murphy ]9 [ Elliot Murphy ]
410
=== modified file 'magicicada/__init__.py'
--- magicicada/__init__.py 2010-06-08 10:53:02 +0000
+++ magicicada/__init__.py 2010-07-09 18:33:43 +0000
@@ -18,33 +18,52 @@
1818
19"""Magicicada."""19"""Magicicada."""
2020
21import gtk21import logging
22import sys22import os
2323
24import gettext24import gettext
25from gettext import gettext as _25_ = gettext.gettext
26gettext.textdomain('magicicada')26gettext.textdomain('magicicada')
2727
28from twisted.internet import gtk2reactor # for gtk-2.028import gtk
29
30# optional Launchpad integration
31# this shouldn't crash if not found as it is simply used for bug reporting
32try:
33 import LaunchpadIntegration
34 launchpad_available = True
35except ImportError:
36 launchpad_available = False
37
38from twisted.internet import gtk2reactor # for gtk-2.0
29gtk2reactor.install()39gtk2reactor.install()
3040
31from magicicada import syncdaemon, logger41from magicicada import syncdaemon, logger as logger_helper
32from magicicada.helpers import humanize_bytes, get_data_file, get_builder, NO_OP42from magicicada.helpers import humanize_bytes, get_data_file, get_builder, \
43 log, NO_OP
3344
34CONTENT_QUEUE = 'content'45CONTENT_QUEUE = 'content'
35META_QUEUE = 'meta'46META_QUEUE = 'meta'
47UBUNTU_ONE_ROOT = os.path.expanduser('~/Ubuntu One')
3648
37# set up the logging for all the project49# set up the logging for all the project
38logger.set_up()50logger_helper.set_up()
51logger = logging.getLogger('magicicada.ui')
52console = logging.StreamHandler()
53console.setLevel(logging.DEBUG)
54#logger.addHandler(console)
55
3956
40class MagicicadaUI(object):57class MagicicadaUI(object):
58 """Magicicada GUI main class."""
4159
42 STATUS_JOINER = " - "60 CURRENT_ROW = '<b><span foreground="#000099">%s</span></b>'
61 STATUS_JOINER = ' - '
43 STATUS = {62 STATUS = {
44 'initial': _('Service is not started, click Start to continue.'),63 'initial': _('Service is not started, click Start to continue.'),
45 }64 }
4665
47 def __init__(self, launchpad_available=False, on_destroy=NO_OP,66 def __init__(self, on_destroy=NO_OP,
48 syncdaemon_class=syncdaemon.SyncDaemon):67 syncdaemon_class=syncdaemon.SyncDaemon):
49 """Init."""68 """Init."""
50 self.builder = get_builder('gui.glade')69 self.builder = get_builder('gui.glade')
@@ -66,27 +85,28 @@
66 self.active_indicator = gtk.gdk.pixbuf_new_from_file(active_filename)85 self.active_indicator = gtk.gdk.pixbuf_new_from_file(active_filename)
6786
68 widgets = (87 widgets = (
69 'start', 'stop', 'connect', 'disconnect', # toolbar buttons88 'start', 'stop', 'connect', 'disconnect', # toolbar buttons
70 'folders', 'folders_dialog', # folders89 'folders', 'folders_dialog', # folders
71 'folders_store', 'folders_close',90 'folders_store', 'folders_close',
72 'shares_to_me', 'shares_to_me_dialog', # shares_to_me91 'shares_to_me', 'shares_to_me_dialog', # shares_to_me
73 'shares_to_me_store', 'shares_to_me_close',92 'shares_to_me_store', 'shares_to_me_close',
74 'shares_to_others', 'shares_to_others_dialog', # shares_to_others93 'shares_to_others', 'shares_to_others_dialog', # shares_to_others
75 'shares_to_others_store', 'shares_to_others_close',94 'shares_to_others_store', 'shares_to_others_close',
76 'is_started', 'is_connected', 'is_online', # status bar images95 'raw_metadata', # raw metadata
77 'status_label', 'status_icon', # status label and systray icon96 'is_started', 'is_connected', 'is_online', # status bar images
78 'metaq_view', 'contentq_view', # queues tree views97 'status_label', 'status_icon', # status label and systray icon
79 'metaq_store', 'contentq_store', # queues list stores98 'metaq_view', 'contentq_view', # queues tree views
80 'metaq_label', 'contentq_label', # queues labels99 'metaq_store', 'contentq_store', # queues list stores
81 'raw_metadata', # raw metadata100 'metaq_label', 'contentq_label', # queues labels
82 'about_dialog', # dialogs101 'file_chooser', 'file_chooser_open', 'file_chooser_cancel',
83 'main_window'102 'about_dialog', 'main_window')
84 )103
85 for widget in widgets:104 for widget in widgets:
86 obj = self.builder.get_object(widget)105 obj = self.builder.get_object(widget)
87 setattr(self, widget, obj)106 setattr(self, widget, obj)
88 assert obj is not None, '%s must not be None' % widget107 assert obj is not None, '%s must not be None' % widget
89108
109 self.raw_metadata_dialog = self._new_metadata_dialog()
90 self.volumes = (self.folders, self.shares_to_me, self.shares_to_others)110 self.volumes = (self.folders, self.shares_to_me, self.shares_to_others)
91 self.windows = (self.main_window, self.about_dialog,111 self.windows = (self.main_window, self.about_dialog,
92 self.folders_dialog)112 self.folders_dialog)
@@ -97,8 +117,8 @@
97 for w in self.windows:117 for w in self.windows:
98 w.set_icon(self._icon)118 w.set_icon(self._icon)
99119
100 about_filename = get_data_file('media', 'logo-128.png')120 about_fname = get_data_file('media', 'logo-128.png')
101 self.about_dialog.set_logo(gtk.gdk.pixbuf_new_from_file(about_filename))121 self.about_dialog.set_logo(gtk.gdk.pixbuf_new_from_file(about_fname))
102122
103 self.sd = syncdaemon_class()123 self.sd = syncdaemon_class()
104 self.sd.on_started_callback = self.on_started124 self.sd.on_started_callback = self.on_started
@@ -110,13 +130,44 @@
110 self.sd.status_changed_callback = self.on_status_changed130 self.sd.status_changed_callback = self.on_status_changed
111 self.sd.content_queue_changed_callback = self.on_content_queue_changed131 self.sd.content_queue_changed_callback = self.on_content_queue_changed
112 self.sd.meta_queue_changed_callback = self.on_meta_queue_changed132 self.sd.meta_queue_changed_callback = self.on_meta_queue_changed
133 self.sd.on_metadata_ready_callback = self.on_metadata_ready
113134
114 self.widget_is_visible = lambda w: w.get_property('visible')135 self.widget_is_visible = lambda w: w.get_property('visible')
115 self.widget_enabled = lambda w: self.widget_is_visible(w) and \136 self.widget_enabled = lambda w: self.widget_is_visible(w) and \
116 w.is_sensitive()137 w.is_sensitive()
117138 self.file_chooser.set_current_folder(UBUNTU_ONE_ROOT)
139 self.last_metadata_path = None
118 self.update()140 self.update()
119141
142 def _new_metadata_dialog(self):
143 """Return a new metadata dialog."""
144 dialog = gtk.Dialog(title='Raw metadata', parent=self.main_window,
145 flags=gtk.DIALOG_NO_SEPARATOR,
146 buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
147 dialog.set_size_request(600, 300)
148 dialog.set_position(gtk.WIN_POS_CENTER)
149 setattr(self, 'raw_metadata_dialog', dialog)
150
151 close_button = dialog.action_area.get_children()[-1]
152 close_button.connect('clicked', self.on_raw_metadata_close_clicked)
153 close_button.connect('activate', self.on_raw_metadata_close_clicked)
154 setattr(self, 'raw_metadata_close', close_button)
155
156 image = gtk.Image()
157 image.set_from_animation(self.loading_animation)
158 setattr(self, 'raw_metadata_image', image)
159
160 dialog.get_child().add(image)
161
162 text_view = gtk.TextView()
163 text_view.set_editable(False)
164 text_view.set_wrap_mode(gtk.WRAP_WORD)
165 dialog.get_child().add(text_view)
166 setattr(self, 'raw_metadata_view', text_view)
167
168 dialog.hide() # XXX to be fixed later
169 return dialog
170
120 # GTK callbacks171 # GTK callbacks
121172
122 def on_main_window_destroy(self, widget, data=None):173 def on_main_window_destroy(self, widget, data=None):
@@ -131,7 +182,7 @@
131182
132 def on_about_activate(self, widget, data=None):183 def on_about_activate(self, widget, data=None):
133 """Display the about box."""184 """Display the about box."""
134 response = self.about_dialog.run()185 self.about_dialog.run()
135 self.about_dialog.hide()186 self.about_dialog.hide()
136187
137 def on_start_clicked(self, widget, data=None):188 def on_start_clicked(self, widget, data=None):
@@ -144,6 +195,7 @@
144 """Stop syncdaemon."""195 """Stop syncdaemon."""
145 for v in self.volumes:196 for v in self.volumes:
146 v.set_sensitive(False)197 v.set_sensitive(False)
198 self.raw_metadata.set_sensitive(False)
147199
148 if self.widget_enabled(self.disconnect):200 if self.widget_enabled(self.disconnect):
149 self.on_disconnect_clicked(self.disconnect)201 self.on_disconnect_clicked(self.disconnect)
@@ -179,13 +231,14 @@
179 item.subscribed, item.volume)231 item.subscribed, item.volume)
180 self.folders_store.append(row)232 self.folders_store.append(row)
181233
182 res = self.folders_dialog.run()234 self.folders_dialog.run()
183 self.folders_dialog.hide()235 self.folders_dialog.hide()
184236
185 def on_shares_to_me_close_clicked(self, widget, data=None):237 def on_shares_to_me_close_clicked(self, widget, data=None):
186 """Close the shares_to_me dialog."""238 """Close the shares_to_me dialog."""
187 self.shares_to_me_dialog.response(gtk.RESPONSE_CLOSE)239 self.shares_to_me_dialog.response(gtk.RESPONSE_CLOSE)
188240
241 @log(logger)
189 def _on_shares_clicked(self, items, store, dialog):242 def _on_shares_clicked(self, items, store, dialog):
190 """List shares to the user or to others."""243 """List shares to the user or to others."""
191 if items is None:244 if items is None:
@@ -201,7 +254,7 @@
201 item.path, item.volume_id)254 item.path, item.volume_id)
202 store.append(row)255 store.append(row)
203256
204 res = dialog.run()257 dialog.run()
205 dialog.hide()258 dialog.hide()
206259
207 def on_shares_to_me_clicked(self, widget, data=None):260 def on_shares_to_me_clicked(self, widget, data=None):
@@ -220,8 +273,27 @@
220 self.shares_to_others_store,273 self.shares_to_others_store,
221 self.shares_to_others_dialog)274 self.shares_to_others_dialog)
222275
276 def on_file_chooser_open_clicked(self, widget, data=None):
277 """Close the file_chooser dialog."""
278 self.file_chooser.response(gtk.FILE_CHOOSER_ACTION_OPEN)
279
280 def on_raw_metadata_close_clicked(self, widget, data=None):
281 """Close the raw_metadata dialog."""
282 self.raw_metadata_dialog.hide()
283
223 def on_raw_metadata_clicked(self, widget, data=None):284 def on_raw_metadata_clicked(self, widget, data=None):
224 """Show raw metadata for a path choosen by the user."""285 """Show raw metadata for a path choosen by the user."""
286 res = self.file_chooser.run()
287 self.file_chooser.hide()
288 if res != gtk.FILE_CHOOSER_ACTION_OPEN:
289 return
290
291 self.last_metadata_path = self.file_chooser.get_filename()
292 self.sd.get_metadata(path=self.last_metadata_path)
293 self.raw_metadata_view.hide()
294 self.raw_metadata_image.show()
295 self._start_loading(self.raw_metadata_image)
296 self.raw_metadata_dialog.show()
225297
226 def on_status_icon_activate(self, widget, data=None):298 def on_status_icon_activate(self, widget, data=None):
227 """Systray icon was clicked."""299 """Systray icon was clicked."""
@@ -242,6 +314,7 @@
242314
243 for v in self.volumes:315 for v in self.volumes:
244 v.set_sensitive(True)316 v.set_sensitive(True)
317 self.raw_metadata.set_sensitive(True)
245318
246 self._update_queues_and_status(self.sd.current_state)319 self._update_queues_and_status(self.sd.current_state)
247320
@@ -285,6 +358,7 @@
285 """Callback'ed when syncadaemon is offline."""358 """Callback'ed when syncadaemon is offline."""
286 self._activate_indicator(self.is_online, sensitive=False)359 self._activate_indicator(self.is_online, sensitive=False)
287360
361 @log(logger)
288 def on_status_changed(self, name=None, description=None,362 def on_status_changed(self, name=None, description=None,
289 is_error=False, is_connected=True, is_online=True,363 is_error=False, is_connected=True, is_online=True,
290 queues=None, connection=None):364 queues=None, connection=None):
@@ -295,17 +369,22 @@
295 text = self.STATUS['initial']369 text = self.STATUS['initial']
296 self.status_label.set_text(text)370 self.status_label.set_text(text)
297371
298 def _on_queue_changed(self, queue_name, items, *args, **kwargs):372 @log(logger)
373 def _on_queue_changed(self, queue_name, items, must_highlight):
299 """Callback'ed when a queue changed."""374 """Callback'ed when a queue changed."""
300 if items is None:375 if items is None:
301 items = []376 items = []
302377
378 markup = lambda value: self.CURRENT_ROW % value \
379 if must_highlight and value is not None else value
303 queue_label = getattr(self, '%sq_label' % queue_name)380 queue_label = getattr(self, '%sq_label' % queue_name)
304 queue_view = getattr(self, '%sq_view' % queue_name)381 queue_view = getattr(self, '%sq_view' % queue_name)
305 queue_store = getattr(self, '%sq_store' % queue_name)382 queue_store = getattr(self, '%sq_store' % queue_name)
306 queue_store.clear()383 queue_store.clear()
307 for item in items:384 for i, item in enumerate(items):
308 row = (item.operation, item.path, item.share, item.node)385 row = (item.operation, item.path, item.share, item.node)
386 if i == 0:
387 row = map(markup, row)
309 queue_store.append(row)388 queue_store.append(row)
310389
311 items_len = len(items)390 items_len = len(items)
@@ -317,11 +396,27 @@
317396
318 def on_content_queue_changed(self, items, *args, **kwargs):397 def on_content_queue_changed(self, items, *args, **kwargs):
319 """Callback'ed when syncadaemon's content queue changed."""398 """Callback'ed when syncadaemon's content queue changed."""
320 self._on_queue_changed(CONTENT_QUEUE, items, args, kwargs)399 state = self.sd.current_state
400 highlight = state.processing_content and state.is_online
401 self._on_queue_changed(CONTENT_QUEUE, items, highlight)
321402
322 def on_meta_queue_changed(self, items, *args, **kwargs):403 def on_meta_queue_changed(self, items, *args, **kwargs):
323 """Callback'ed when syncadaemon's meta queue changed."""404 """Callback'ed when syncadaemon's meta queue changed."""
324 self._on_queue_changed(META_QUEUE, items, args, kwargs)405 state = self.sd.current_state
406 highlight = state.processing_meta and state.is_online
407 self._on_queue_changed(META_QUEUE, items, highlight)
408
409 @log(logger)
410 def on_metadata_ready(self, path, metadata):
411 """Lower layer has the requested metadata for 'path'."""
412 logger.debug('on_metadata_ready: path: %r, last_metadata_path: %r',
413 path, self.last_metadata_path)
414 if path != self.last_metadata_path:
415 return
416 self.raw_metadata_image.hide()
417 self.raw_metadata_view.show()
418 text = '\n'.join('%s: %s' % i for i in metadata.iteritems())
419 self.raw_metadata_view.get_buffer().set_text(text)
325420
326 # custom421 # custom
327422
@@ -340,7 +435,8 @@
340 self.on_meta_queue_changed(self.sd.meta_queue)435 self.on_meta_queue_changed(self.sd.meta_queue)
341 self.on_content_queue_changed(self.sd.content_queue)436 self.on_content_queue_changed(self.sd.content_queue)
342 self.on_status_changed(name=state.name, description=state.description,437 self.on_status_changed(name=state.name, description=state.description,
343 queues=state.queues, connection=state.connection)438 queues=state.queues,
439 connection=state.connection)
344440
345 def update(self):441 def update(self):
346 """Update UI based on SD current state."""442 """Update UI based on SD current state."""
347443
=== removed file 'magicicada/cmd_pof.py'
--- magicicada/cmd_pof.py 2010-06-08 10:53:02 +0000
+++ magicicada/cmd_pof.py 1970-01-01 00:00:00 +0000
@@ -1,190 +0,0 @@
1# DISCLAIMER: this is a proof of concept, we need tests for this!
2# Author: Facundo Batista
3
4# this always first
5from twisted.internet import glib2reactor
6glib2reactor.install()
7
8import functools
9import re
10import time
11
12import dbus
13from dbus.mainloop.glib import DBusGMainLoop
14from twisted.internet import reactor
15
16from ubuntuone.syncdaemon import tools
17
18# main connected stuff
19loop = DBusGMainLoop(set_as_default=True)
20bus = dbus.SessionBus(mainloop=loop)
21sync_daemon_tool = tools.SyncDaemonTool(bus)
22
23DATE_FMT = "%Y-%m-%d"
24TIME_FMT = "%H:%M:%S"
25
26RE_OP_LISTDIR = re.compile("ListDir\(share_id=(.*?), node_id=(.*?), .*")
27RE_OP_UNLINK = re.compile("Unlink\(share_id=(.*?), node_id=(.*?), .*")
28RE_OP_MAKEFILE = re.compile(
29 "MakeFile\(share_id=(.*?), parent_id=(.*?), name=(.*?), .*")
30
31class DBusHandler(object):
32 def __init__(self):
33 # status changed
34 bus.add_signal_receiver(self.on_status_changed,
35 dbus_interface='com.ubuntuone.SyncDaemon.Status',
36 signal_name='StatusChanged')
37 # content queue changed
38 bus.add_signal_receiver(self.on_CQ_changed,
39 dbus_interface='com.ubuntuone.SyncDaemon.Status',
40 signal_name='ContentQueueChanged')
41
42 # upload or download
43 for s in ('DownloadStarted', 'DownloadFinished',
44 'UploadStarted', 'UploadFinished'):
45 bus.add_signal_receiver(functools.partial(self.on_updown_activ, s),
46 signal_name=s)
47
48 # get the first one
49 d = sync_daemon_tool.get_status()
50 d.addCallback(self.on_status_changed)
51
52 self._date = None
53 self._last_CQ_data = None
54 self._last_MQ_data = None
55 self._state = None
56 self._mqcaller = None
57
58 def on_updown_activ(self, signal, *data):
59 """Upload or download activity."""
60 path = data[0]
61 if signal in ('UploadStarted', 'DownloadStarted'):
62 self._show("{0}: {1}".format(signal, path))
63 else:
64 self._show("{0}: {1}".format(signal, path))
65
66 def on_CQ_changed(self, data):
67 """Content Queue changed, update it."""
68 def show_cq(data):
69 """Show the CONTENT queue."""
70 if data == self._last_CQ_data:
71 return
72
73 # it changed!
74 self._last_CQ_data = data
75 self._show("Content Queue:")
76 if data:
77 for d in data:
78 print " ", self._format_CQ_data(d)
79 else:
80 print " (empty)"
81
82 d = sync_daemon_tool.waiting_content()
83 d.addCallback(show_cq)
84
85 def _show(self, message):
86 date = time.strftime(DATE_FMT)
87 if date != self._date:
88 self._date = date
89 print "-- {0} --".format(self._date)
90
91 now = time.strftime(TIME_FMT)
92 print "{0} {1}".format(now, message)
93
94 def _format_MQ_data(self, data):
95 """Format the meta queue information."""
96 if data.startswith("ListDir"):
97 m = RE_OP_LISTDIR.match(data)
98 if not m:
99 m = "Got a ListDir, but failed to match: %r" % (data,)
100 raise ValueError(m)
101 share_id, node_id = m.groups()
102 name = self._find_out_path(share_id, node_id)
103 return "ListDir: {0} ({1}:{2})".format(name, share_id, node_id)
104
105 if data.startswith("Unlink"):
106 m = RE_OP_UNLINK.match(data)
107 if not m:
108 m = "Got an Unlink, but failed to match: %r" % (data,)
109 raise ValueError(m)
110 share_id, node_id = m.groups()
111 name = self._find_out_path(share_id, node_id)
112 return "Unlink: {0} ({1}:{2})".format(name, share_id, node_id)
113
114 if data.startswith("MakeFile"):
115 m = RE_OP_MAKEFILE.match(data)
116 if not m:
117 m = "Got a Makefile, but failed to match: %r" % (data,)
118 raise ValueError(m)
119 share_id, parent_id, name = m.groups()
120 parent = self._find_out_path(share_id, parent_id)
121 return "MakeFile: {0} (in {1})".format(name, parent)
122
123 # these operations are very simple
124 if data in ('GetPublicFiles', 'AccountInquiry', 'FreeSpaceInquiry',
125 'ListVolumes', 'ListShares'):
126 return data
127
128 return "Op? {0}".format(data)
129
130 def _format_CQ_data(self, data):
131 """Format the content queue information."""
132 return "{operation}: {path} ({share}:{node})".format(**data)
133
134 def _find_out_path(self, share_id, node_id):
135 """Curse the destiny!"""
136 return "?"
137
138 def on_status_changed(self, state):
139 """Show the state nicely."""
140 self._state = state
141 if state["is_error"]:
142 print "State: Error!"
143 return
144
145 # normal
146 self._show("State: {name} Q: {queues} C: {connection}".format(
147 **state))
148 self._check_MQ()
149
150 def _check_MQ(self):
151 """Check MQ if we should."""
152 # check if we have something to show in MQ!
153 if self._state['name'] != 'QUEUE_MANAGER' or self._state['queues'] \
154 not in ('WORKING_ON_METADATA', 'WORKING_ON_BOTH'):
155 return
156
157 # we have a previous call later running?
158 if self._mqcaller is not None and self._mqcaller.active():
159 self._mqcaller.cancel()
160
161 def show_mq(data):
162 """Show the META queue."""
163 if data == self._last_MQ_data:
164 return
165
166 # it changed!
167 self._last_MQ_data = data
168 self._show("Meta Queue:")
169 if data:
170 for d in data:
171 print " ", self._format_MQ_data(d)
172 else:
173 print " (empty)"
174
175 d = sync_daemon_tool.waiting_metadata()
176 d.addCallback(show_mq)
177
178 self._mqcaller = reactor.callLater(1, self._check_MQ)
179
180def main():
181 print "Check if it's running..."
182 if not tools.is_running():
183 print "ERROR: SD is not running!"
184 reactor.stop()
185
186 sh = DBusHandler()
187
188if __name__ == "__main__":
189 reactor.callWhenRunning(main)
190 reactor.run()
1910
=== modified file 'magicicada/dbusiface.py'
--- magicicada/dbusiface.py 2010-06-08 10:53:02 +0000
+++ magicicada/dbusiface.py 2010-07-09 18:33:43 +0000
@@ -32,6 +32,8 @@
32# log!32# log!
33logger = logging.getLogger('magicicada.dbusiface')33logger = logging.getLogger('magicicada.dbusiface')
3434
35# we use here camel case names, because this variables are used later as
36# classes, so pylint: disable-msg=C0103
35QueueData = collections.namedtuple('QueueData', 'operation path share node')37QueueData = collections.namedtuple('QueueData', 'operation path share node')
36FolderData = collections.namedtuple('FolderData',38FolderData = collections.namedtuple('FolderData',
37 'node path suggested_path subscribed volume')39 'node path suggested_path subscribed volume')
@@ -50,14 +52,23 @@
50 "(Move)\(share_id=(.*?), node_id=(.*?), old_parent_id=(.*?), "52 "(Move)\(share_id=(.*?), node_id=(.*?), old_parent_id=(.*?), "
51 "new_parent_id=(.*?), new_name=(.*?)\)")53 "new_parent_id=(.*?), new_name=(.*?)\)")
5254
55# DBus exceptions store the type inside, as a string :|
56DBUSERR_NOREPLY = 'org.freedesktop.DBus.Error.NoReply'
57DBUSERR_NAMENOOWNER = 'org.freedesktop.DBus.Error.NameHasNoOwner'
58DBUSERR_PYKEYERROR = 'org.freedesktop.DBus.Python.KeyError'
59
60# some constants
61NOT_SYNCHED_PATH = "Not a valid path!"
62
5363
54def _is_retry_exception(err):64def _is_retry_exception(err):
55 """Check if the exception is a retry one."""65 """Check if the exception is a retry one."""
56 if isinstance(err, dbus.exceptions.DBusException):66 if isinstance(err, dbus.exceptions.DBusException):
57 if err.get_dbus_name() == 'org.freedesktop.DBus.Error.NoReply':67 if err.get_dbus_name() == DBUSERR_NOREPLY:
58 return True68 return True
59 return False69 return False
6070
71
61def retryable(func):72def retryable(func):
62 """Call the function until its deferred succeed (max 5 times)."""73 """Call the function until its deferred succeed (max 5 times)."""
6374
@@ -68,7 +79,7 @@
68 while opportunities:79 while opportunities:
69 try:80 try:
70 res = yield func(*a, **k)81 res = yield func(*a, **k)
71 except Exception, err:82 except Exception, err: # pylint: disable-msg=W0703
72 opportunities -= 183 opportunities -= 1
73 if opportunities == 0 or not _is_retry_exception(err):84 if opportunities == 0 or not _is_retry_exception(err):
74 raise85 raise
@@ -99,6 +110,8 @@
99 (self._on_name_owner_changed, None, 'NameOwnerChanged'),110 (self._on_name_owner_changed, None, 'NameOwnerChanged'),
100 (self._on_folder_created, 'Folders', 'FolderCreated'),111 (self._on_folder_created, 'Folders', 'FolderCreated'),
101 (self._on_folder_deleted, 'Folders', 'FolderDeleted'),112 (self._on_folder_deleted, 'Folders', 'FolderDeleted'),
113 (self._on_folder_subscribed, 'Folders', 'FolderSubscribed'),
114 (self._on_folder_unsubscribed, 'Folders', 'FolderUnSubscribed'),
102 (self._on_share_created, 'Shares', 'ShareCreated'),115 (self._on_share_created, 'Shares', 'ShareCreated'),
103 (self._on_share_deleted, 'Shares', 'ShareDeleted'),116 (self._on_share_deleted, 'Shares', 'ShareDeleted'),
104 (self._on_share_changed, 'Shares', 'ShareChanged'),117 (self._on_share_changed, 'Shares', 'ShareChanged'),
@@ -114,7 +127,6 @@
114 signal_name=signal_name)127 signal_name=signal_name)
115 self._dbus_matches.append((match, dbus_interface, signal_name))128 self._dbus_matches.append((match, dbus_interface, signal_name))
116129
117
118 def shutdown(self):130 def shutdown(self):
119 """Shut down the SyncDaemon."""131 """Shut down the SyncDaemon."""
120 logger.info("DBus interface going down")132 logger.info("DBus interface going down")
@@ -180,6 +192,16 @@
180 logger.info("Received Folder deleted")192 logger.info("Received Folder deleted")
181 self.msd.on_sd_folders_changed()193 self.msd.on_sd_folders_changed()
182194
195 def _on_folder_subscribed(self, _):
196 """Call the SD callback."""
197 logger.info("Received Folder subscribed")
198 self.msd.on_sd_folders_changed()
199
200 def _on_folder_unsubscribed(self, _):
201 """Call the SD callback."""
202 logger.info("Received Folder unsubscribed")
203 self.msd.on_sd_folders_changed()
204
183 def _on_share_created(self, _):205 def _on_share_created(self, _):
184 """Call the SD callback."""206 """Call the SD callback."""
185 logger.info("Received Share created")207 logger.info("Received Share created")
@@ -198,6 +220,7 @@
198 @retryable220 @retryable
199 def get_content_queue(self):221 def get_content_queue(self):
200 """Get the content queue from SDT."""222 """Get the content queue from SDT."""
223
201 def process(data):224 def process(data):
202 """Enhance data format."""225 """Enhance data format."""
203 logger.info("Processing Content Queue items (%d)", len(data))226 logger.info("Processing Content Queue items (%d)", len(data))
@@ -217,31 +240,32 @@
217 def _parse_mq(self, data):240 def _parse_mq(self, data):
218 """Parse MetaQueue string to extract its data."""241 """Parse MetaQueue string to extract its data."""
219 if data in ('AccountInquiry', 'FreeSpaceInquiry', 'GetPublicFiles',242 if data in ('AccountInquiry', 'FreeSpaceInquiry', 'GetPublicFiles',
220 'ListShares', 'ListVolumes', 'Query'):243 'ListShares', 'ListVolumes', 'Query',
244 'ChangePublicAccess', 'AnswerShare'):
221 return QueueData(operation=data, path=None, node=None, share=None)245 return QueueData(operation=data, path=None, node=None, share=None)
222246
223 m = RE_OP_LISTDIR.match(data)247 m = RE_OP_LISTDIR.match(data)
224 if m:248 if m:
225 op, share, node = m.groups()249 op, share, node = m.groups()
226 path = '?' # we should get the real path, no API now250 path = '?' # we should get the real path, no API now
227 return QueueData(operation=op, path=path, node=node, share=share)251 return QueueData(operation=op, path=path, node=node, share=share)
228252
229 m = RE_OP_MAKEFILE.match(data)253 m = RE_OP_MAKEFILE.match(data)
230 if m:254 if m:
231 op, share, parent, name = m.groups()255 op, share, parent, name = m.groups()
232 path = '/?.../' + name # we should get the real path, no API now256 path = '/?.../' + name # we should get the real path, no API now
233 return QueueData(operation=op, path=path, node=None, share=share)257 return QueueData(operation=op, path=path, node=None, share=share)
234258
235 m = RE_OP_MAKEDIR.match(data)259 m = RE_OP_MAKEDIR.match(data)
236 if m:260 if m:
237 op, share, parent, name = m.groups()261 op, share, parent, name = m.groups()
238 path = '/?.../' + name # we should get the real path, no API now262 path = '/?.../' + name # we should get the real path, no API now
239 return QueueData(operation=op, path=path, node=None, share=share)263 return QueueData(operation=op, path=path, node=None, share=share)
240264
241 m = RE_OP_UNLINK.match(data)265 m = RE_OP_UNLINK.match(data)
242 if m:266 if m:
243 op, share, node, = m.groups()267 op, share, node, = m.groups()
244 path = '?' # we should get the real path, no API now268 path = '?' # we should get the real path, no API now
245 return QueueData(operation=op, path=path, node=node, share=share)269 return QueueData(operation=op, path=path, node=node, share=share)
246270
247 m = RE_OP_MOVE.match(data)271 m = RE_OP_MOVE.match(data)
@@ -262,6 +286,7 @@
262 @retryable286 @retryable
263 def get_meta_queue(self):287 def get_meta_queue(self):
264 """Get the meta queue from SDT."""288 """Get the meta queue from SDT."""
289
265 def process(data):290 def process(data):
266 """Enhance data format."""291 """Enhance data format."""
267 logger.info("Processing Meta Queue items (%d)", len(data))292 logger.info("Processing Meta Queue items (%d)", len(data))
@@ -280,6 +305,7 @@
280 @retryable305 @retryable
281 def get_folders(self):306 def get_folders(self):
282 """Get the folders info from SDT."""307 """Get the folders info from SDT."""
308
283 def process(data):309 def process(data):
284 """Enhance data format."""310 """Enhance data format."""
285 logger.info("Processing Folders items (%d)", len(data))311 logger.info("Processing Folders items (%d)", len(data))
@@ -323,8 +349,7 @@
323 try:349 try:
324 self._bus.get_name_owner('com.ubuntuone.SyncDaemon')350 self._bus.get_name_owner('com.ubuntuone.SyncDaemon')
325 except dbus.exceptions.DBusException, err:351 except dbus.exceptions.DBusException, err:
326 if err.get_dbus_name() != \352 if err.get_dbus_name() != DBUSERR_NAMENOOWNER:
327 'org.freedesktop.DBus.Error.NameHasNoOwner':
328 raise353 raise
329 started = False354 started = False
330 else:355 else:
@@ -359,6 +384,7 @@
359 @retryable384 @retryable
360 def get_shares_to_me(self):385 def get_shares_to_me(self):
361 """Get the shares to me ('shares') info from SDT."""386 """Get the shares to me ('shares') info from SDT."""
387
362 def process(data):388 def process(data):
363 """Enhance data format."""389 """Enhance data format."""
364 logger.info("Processing Shares To Me items (%d)", len(data))390 logger.info("Processing Shares To Me items (%d)", len(data))
@@ -372,6 +398,7 @@
372 @retryable398 @retryable
373 def get_shares_to_others(self):399 def get_shares_to_others(self):
374 """Get the shares to others ('shared') info from SDT."""400 """Get the shares to others ('shared') info from SDT."""
401
375 def process(data):402 def process(data):
376 """Enhance data format."""403 """Enhance data format."""
377 logger.info("Processing Shares To Others items (%d)", len(data))404 logger.info("Processing Shares To Others items (%d)", len(data))
@@ -381,3 +408,24 @@
381 d = self.sync_daemon_tool.list_shared()408 d = self.sync_daemon_tool.list_shared()
382 d.addCallback(process)409 d.addCallback(process)
383 return d410 return d
411
412 @retryable
413 def get_metadata(self, path):
414 """Return the raw metadata."""
415 logger.info("Getting metadata for %r", path)
416
417 def fix_failure(failure):
418 """Get the failure and return a nice message."""
419 if failure.check(dbus.exceptions.DBusException):
420 if failure.value.get_dbus_name() == DBUSERR_PYKEYERROR:
421 return NOT_SYNCHED_PATH
422 return failure
423
424 def process(metadata):
425 """Process the metadata."""
426 logger.debug("Got metadata for path %r: %r", path, metadata)
427 return dict(metadata)
428
429 d = self.sync_daemon_tool.get_metadata(path)
430 d.addCallbacks(process, fix_failure)
431 return d
384432
=== modified file 'magicicada/helpers.py'
--- magicicada/helpers.py 2010-06-08 10:53:02 +0000
+++ magicicada/helpers.py 2010-07-09 18:33:43 +0000
@@ -13,18 +13,15 @@
1313
14import gtk14import gtk
15import os15import os
16import sys
1716
18from functools import wraps17from functools import wraps
1918
20from magicicada.magicicadaconfig import get_data_file19from magicicada.magicicadaconfig import get_data_file
2120
22import gettext
23from gettext import gettext as _
24gettext.textdomain('magicicada')
2521
26NO_OP = lambda *a, **kw: None22NO_OP = lambda *a, **kw: None
2723
24
28def get_builder(builder_file_name):25def get_builder(builder_file_name):
29 """Return a fully-instantiated gtk.Builder instance from specified ui file.26 """Return a fully-instantiated gtk.Builder instance from specified ui file.
3027
@@ -41,21 +38,37 @@
41 builder.add_from_file(ui_filename)38 builder.add_from_file(ui_filename)
42 return builder39 return builder
4340
44def print_debug(f):41
45 """Print debug info for 'f'."""42def log(logger):
4643 """Log input/ouput info for 'f' using 'logger'."""
47 @wraps(f)44
48 def inner(*args, **kwargs):45 def decorator(f):
49 """Wrap f."""46 """The decorator per se."""
50 sys.stderr.write('Calling %s %s %s\n' % (f.__name__, args, kwargs))47
51 result = f(*args, **kwargs)48 @wraps(f)
52 return result49 def inner(*args, **kwargs):
5350 """Wrap 'f', log input args and result using 'logger'."""
54 return inner51 name = f.__name__
5552 result = None
56# from 53 logger.debug("Calling '%s' with args '%s' and kwargs '%s'.",
57# http://code.activestate.com/recipes/577081-humanized-representation-of-a-number-of-bytes/54 name, args, kwargs)
58def humanize_bytes(bytes, precision=1):55 try:
56 result = f(*args, **kwargs)
57 except Exception: # pylint: disable-msg=W0703
58 logger.exception('%s failed with exception:', name)
59 logger.debug("Returning from '%s' with result '%s'.", name, result)
60 return result
61
62 return inner
63
64 return decorator
65
66
67# from http://code.activestate.com/recipes/
68# 577081-humanized-representation-of-a-number-of-bytes/
69
70
71def humanize_bytes(numbytes, precision=1):
59 """Return a humanized string representation of a number of bytes.72 """Return a humanized string representation of a number of bytes.
6073
61 Assumes `from __future__ import division`.74 Assumes `from __future__ import division`.
@@ -83,11 +96,12 @@
83 (1<<30L, 'GB'),96 (1<<30L, 'GB'),
84 (1<<20L, 'MB'),97 (1<<20L, 'MB'),
85 (1<<10L, 'kB'),98 (1<<10L, 'kB'),
86 (1, 'bytes')99 (1, 'bytes'))
87 )100
88 if bytes == 1:101 if numbytes == 1:
89 return '1 byte'102 return '1 byte'
90 for factor, suffix in abbrevs:103 for factor, suffix in abbrevs:
91 if bytes >= factor:104 if numbytes >= factor:
92 break105 break
93 return '%.*f %s' % (precision, bytes / factor, suffix)106 # pylint: disable-msg=W0631
107 return '%.*f %s' % (precision, numbytes / factor, suffix)
94108
=== modified file 'magicicada/logger.py'
--- magicicada/logger.py 2010-06-08 10:53:02 +0000
+++ magicicada/logger.py 2010-07-09 18:33:43 +0000
@@ -53,4 +53,3 @@
53 '%Y-%m-%d %H:%M:%S')53 '%Y-%m-%d %H:%M:%S')
54 handler.setFormatter(formatter)54 handler.setFormatter(formatter)
55 logger.setLevel(logging.DEBUG)55 logger.setLevel(logging.DEBUG)
56
5756
=== modified file 'magicicada/magicicadaconfig.py'
--- magicicada/magicicadaconfig.py 2010-06-08 10:53:02 +0000
+++ magicicada/magicicadaconfig.py 2010-07-09 18:33:43 +0000
@@ -3,13 +3,15 @@
3# This file is in the public domain3# This file is in the public domain
4### END LICENSE4### END LICENSE
55
6"""Magicicada configuration file."""
7
6# THIS IS Magicicada CONFIGURATION FILE8# THIS IS Magicicada CONFIGURATION FILE
7# YOU CAN PUT THERE SOME GLOBAL VALUE9# YOU CAN PUT THERE SOME GLOBAL VALUE
8# Do not touch unless you know what you're doing.10# Do not touch unless you know what you're doing.
9# you're warned :)11# you're warned :)
1012
11__all__ = [13__all__ = [
12 'project_path_not_found',14 'ProjectPathNotFound',
13 'get_data_file',15 'get_data_file',
14 'get_data_path',16 'get_data_path',
15 ]17 ]
@@ -21,11 +23,8 @@
2123
22import os24import os
2325
24import gettext
25from gettext import gettext as _
26gettext.textdomain('magicicada')
2726
28class project_path_not_found(Exception):27class ProjectPathNotFound(Exception):
29 """Raised when we can't find the project directory."""28 """Raised when we can't find the project directory."""
3029
3130
@@ -53,6 +52,6 @@
5352
54 abs_data_path = os.path.abspath(path)53 abs_data_path = os.path.abspath(path)
55 if not os.path.exists(abs_data_path):54 if not os.path.exists(abs_data_path):
56 raise project_path_not_found55 raise ProjectPathNotFound
5756
58 return abs_data_path57 return abs_data_path
5958
=== modified file 'magicicada/syncdaemon.py'
--- magicicada/syncdaemon.py 2010-06-08 10:53:02 +0000
+++ magicicada/syncdaemon.py 2010-07-09 18:33:43 +0000
@@ -25,14 +25,19 @@
25from magicicada.dbusiface import DBusInterface25from magicicada.dbusiface import DBusInterface
26from magicicada.helpers import NO_OP26from magicicada.helpers import NO_OP
2727
28
28# log!29# log!
29logger = logging.getLogger('magicicada.syncdaemon')30logger = logging.getLogger('magicicada.syncdaemon')
3031
3132
32class State(object):33class State(object):
33 """Hold the state of SD."""34 """Hold the state of SD."""
34 _attrs = ['name', 'description', 'is_error', 'is_connected',35
35 'is_online', 'queues', 'connection', 'is_started']36 _attrs = ['name', 'description', 'is_error', 'is_connected',
37 'is_online', 'queues', 'connection', 'is_started']
38
39 _meta = ('WORKING_ON_METADATA', 'WORKING_ON_BOTH')
40 _content = ('WORKING_ON_CONTENT', 'WORKING_ON_BOTH')
3641
37 def __init__(self):42 def __init__(self):
38 # starting defaults43 # starting defaults
@@ -47,12 +52,16 @@
4752
48 def __getattribute__(self, name):53 def __getattribute__(self, name):
49 """Return the value if there."""54 """Return the value if there."""
50 if name[0] == "_":55 if name[0] == "_" or name == 'set':
51 return object.__getattribute__(self, name)56 return object.__getattribute__(self, name)
57 elif name == 'processing_meta':
58 return self.__dict__['queues'] in self._meta
59 elif name == 'processing_content':
60 return self.__dict__['queues'] in self._content
52 else:61 else:
53 return self.__dict__[name]62 return self.__dict__[name]
5463
55 def _set(self, **data):64 def set(self, **data):
56 """Set the attributes from data, if allowed."""65 """Set the attributes from data, if allowed."""
57 for name, value in data.iteritems():66 for name, value in data.iteritems():
58 if name not in self._attrs:67 if name not in self._attrs:
@@ -97,6 +106,7 @@
97 self.on_folders_changed_callback = NO_OP106 self.on_folders_changed_callback = NO_OP
98 self.on_shares_to_me_changed_callback = NO_OP107 self.on_shares_to_me_changed_callback = NO_OP
99 self.on_shares_to_others_changed_callback = NO_OP108 self.on_shares_to_others_changed_callback = NO_OP
109 self.on_metadata_ready_callback = None # mandatory
100110
101 # mq needs to be polled to know progress111 # mq needs to be polled to know progress
102 self._mqcaller = None112 self._mqcaller = None
@@ -104,11 +114,10 @@
104114
105 # load initial data if ubuntuone-client already started115 # load initial data if ubuntuone-client already started
106 if self.dbus.is_sd_started():116 if self.dbus.is_sd_started():
107 self.current_state._set(is_started=True)117 self.current_state.set(is_started=True)
108 self._get_initial_data()118 self._get_initial_data()
109 else:119 else:
110 self.current_state._set(is_started=False)120 self.current_state.set(is_started=False)
111
112121
113 def shutdown(self):122 def shutdown(self):
114 """Shut down the SyncDaemon."""123 """Shut down the SyncDaemon."""
@@ -165,14 +174,14 @@
165 def on_sd_name_owner_changed(self, now_active):174 def on_sd_name_owner_changed(self, now_active):
166 """SyncDaemon name owner changed."""175 """SyncDaemon name owner changed."""
167 logger.info("SD Name Owner changed: %s", now_active)176 logger.info("SD Name Owner changed: %s", now_active)
168 self.current_state._set(is_started=now_active)177 self.current_state.set(is_started=now_active)
169178
170 def set_status(name, description):179 def set_status(name, description):
171 """Set status after the name owner change."""180 """Set status after the name owner change."""
172 d = dict(name=name, description=description, is_error=False,181 d = dict(name=name, description=description, is_error=False,
173 is_connected=False, is_online=False, queues='',182 is_connected=False, is_online=False, queues='',
174 connection='')183 connection='')
175 self.current_state._set(**d)184 self.current_state.set(**d)
176185
177 if now_active:186 if now_active:
178 set_status('STARTED', 'ubuntuone-client just started')187 set_status('STARTED', 'ubuntuone-client just started')
@@ -189,6 +198,7 @@
189198
190 def _send_status_changed(self, name, description, is_error, is_connected,199 def _send_status_changed(self, name, description, is_error, is_connected,
191 is_online, queues, connection):200 is_online, queues, connection):
201 """Send status changed signal."""
192 logger.debug(" new status: name=%r, description=%r, is_error=%s, "202 logger.debug(" new status: name=%r, description=%r, is_error=%s, "
193 "is_connected=%s, is_online=%s, queues=%r, connection=%r",203 "is_connected=%s, is_online=%s, queues=%r, connection=%r",
194 name, description, is_error, is_connected, is_online,204 name, description, is_error, is_connected, is_online,
@@ -205,7 +215,7 @@
205 self.on_offline_callback()215 self.on_offline_callback()
206216
207 # set current state to new values and call status changed cb217 # set current state to new values and call status changed cb
208 self.current_state._set(name=name, description=description,218 self.current_state.set(name=name, description=description,
209 is_error=is_error, is_connected=is_connected,219 is_error=is_error, is_connected=is_connected,
210 is_online=is_online, queues=queues,220 is_online=is_online, queues=queues,
211 connection=connection)221 connection=connection)
@@ -213,7 +223,8 @@
213 is_online, queues, connection)223 is_online, queues, connection)
214224
215 # if corresponds, supervise MQ225 # if corresponds, supervise MQ
216 self._check_mq()226 if self._mqcaller is None:
227 self._check_mq()
217228
218 @defer.inlineCallbacks229 @defer.inlineCallbacks
219 def on_sd_content_queue_changed(self):230 def on_sd_content_queue_changed(self):
@@ -226,27 +237,30 @@
226 self.content_queue_changed_callback(new_cq)237 self.content_queue_changed_callback(new_cq)
227238
228 @defer.inlineCallbacks239 @defer.inlineCallbacks
240 def _get_mq_data(self):
241 """Get MQ info and call back if needed."""
242 new_mq = yield self.dbus.get_meta_queue()
243 if new_mq != self.meta_queue:
244 logger.info("SD Meta Queue changed: %d items", len(new_mq))
245 self.meta_queue = new_mq
246 self.meta_queue_changed_callback(new_mq)
247
229 def _check_mq(self):248 def _check_mq(self):
230 """Check MQ if we should."""249 """Check MQ if we should."""
231 state = self.current_state250 # cancel previous (if any) and check again later
232 if state.queues not in ('WORKING_ON_METADATA', 'WORKING_ON_BOTH'):251 if self._mqcaller is not None and self._mqcaller.active():
233 logger.info("Check MQ called but States not in MQ")252 self._mqcaller.cancel()
253
254 if not self.current_state.processing_meta:
255 logger.info("Check MQ called, States not in MQ, call a last time")
256 self._mqcaller = None
257 self._get_mq_data()
234 else:258 else:
235 logger.info("Asking for MQ information")259 logger.info("Asking for MQ information")
236260
237 # have we a previous call later still running?
238 if self._mqcaller is not None and self._mqcaller.active():
239 self._mqcaller.cancel()
240
241 # get the info261 # get the info
242 new_mq = yield self.dbus.get_meta_queue()262 self._get_mq_data()
243263
244 if new_mq != self.meta_queue:
245 logger.info("SD Meta Queue changed: %d items", len(new_mq))
246 self.meta_queue = new_mq
247 self.meta_queue_changed_callback(new_mq)
248
249 # check again later
250 self._mqcaller = reactor.callLater(self._mq_poll_time,264 self._mqcaller = reactor.callLater(self._mq_poll_time,
251 self._check_mq)265 self._check_mq)
252266
@@ -270,3 +284,11 @@
270 """Tell the SyncDaemon that the user wants it to disconnect."""284 """Tell the SyncDaemon that the user wants it to disconnect."""
271 logger.info("Telling u1.SD to disconnect")285 logger.info("Telling u1.SD to disconnect")
272 self.dbus.disconnect()286 self.dbus.disconnect()
287
288 def get_metadata(self, path):
289 """Get the metadata for given path."""
290 if self.on_metadata_ready_callback is None:
291 raise ValueError("Missing the mandatory cback for get_metadata.")
292
293 d = self.dbus.get_metadata(path)
294 d.addCallback(lambda resp: self.on_metadata_ready_callback(path, resp))
273295
=== modified file 'magicicada/tests/helpers.py'
--- magicicada/tests/helpers.py 2010-06-08 10:53:02 +0000
+++ magicicada/tests/helpers.py 2010-07-09 18:33:43 +0000
@@ -36,14 +36,18 @@
36 def check(self, level, msg):36 def check(self, level, msg):
37 """Check that something is logged."""37 """Check that something is logged."""
38 for rec in self.records:38 for rec in self.records:
39 if rec.levelname == level and rec.message == msg:39 if rec.levelname == level and str(msg) in rec.message:
40 return True40 return True
41 return False41 return False
4242
43 def check_inf(self, msg):43 def check_error(self, msg):
44 """Shortcut for ERROR check."""
45 return self.check('ERROR', msg)
46
47 def check_info(self, msg):
44 """Shortcut for INFO check."""48 """Shortcut for INFO check."""
45 return self.check('INFO', msg)49 return self.check('INFO', msg)
4650
47 def check_dbg(self, msg):51 def check_debug(self, msg):
48 """Shortcut for DEBUG check."""52 """Shortcut for DEBUG check."""
49 return self.check('DEBUG', msg)53 return self.check('DEBUG', msg)
5054
=== modified file 'magicicada/tests/test_dbusiface.py'
--- magicicada/tests/test_dbusiface.py 2010-06-08 10:53:02 +0000
+++ magicicada/tests/test_dbusiface.py 2010-07-09 18:33:43 +0000
@@ -28,8 +28,13 @@
28from magicicada.tests.helpers import MementoHandler28from magicicada.tests.helpers import MementoHandler
2929
3030
31# It's ok to access private data in the test suite
32# pylint: disable-msg=W0212
33
34
31class FakeSessionBus(object):35class FakeSessionBus(object):
32 """Fake Session Bus."""36 """Fake Session Bus."""
37
33 def __init__(self, **kwargs):38 def __init__(self, **kwargs):
34 self._callbacks = {}39 self._callbacks = {}
35 self.fake_name_owner = "foo"40 self.fake_name_owner = "foo"
@@ -43,8 +48,12 @@
43 del self._callbacks[(dbus_interface, signal_name)]48 del self._callbacks[(dbus_interface, signal_name)]
4449
45 def get_name_owner(self, name):50 def get_name_owner(self, name):
46 """Fakes the response of the method."""51 """Fake the response of the method."""
47 assert name == 'com.ubuntuone.SyncDaemon'52 assert name == 'com.ubuntuone.SyncDaemon'
53
54 # will return a string, or raise an exception instance, never
55 # raise a string
56 # pylint: disable-msg=W0701
48 if isinstance(self.fake_name_owner, str):57 if isinstance(self.fake_name_owner, str):
49 return self.fake_name_owner58 return self.fake_name_owner
50 else:59 else:
@@ -53,6 +62,7 @@
5362
54class CallLoguer(object):63class CallLoguer(object):
55 """Class that logs the methods called."""64 """Class that logs the methods called."""
65
56 def __init__(self):66 def __init__(self):
57 self._called_method = None, ()67 self._called_method = None, ()
58 self._fake_response = None68 self._fake_response = None
@@ -62,19 +72,25 @@
62 if name[0] == "_":72 if name[0] == "_":
63 return object.__getattribute__(self, name)73 return object.__getattribute__(self, name)
64 else:74 else:
75
65 def f(*args):76 def f(*args):
77 """Fake function."""
66 setattr(self, "_called_method", (name, args))78 setattr(self, "_called_method", (name, args))
67 if self._fake_response is None:79 if self._fake_response is None:
68 # no hurt in returning a deferred, it may be needed80 # no hurt in returning a deferred, it may be needed
69 return defer.Deferred()81 return defer.Deferred()
70 methname, response = self._fake_response82 methname, response = self._fake_response
71 assert methname == name83 assert methname == name
72 return response84 if isinstance(response, Exception):
85 return defer.fail(response)
86 else:
87 return defer.succeed(response)
73 return f88 return f
7489
7590
76class FakeSDTool(CallLoguer):91class FakeSDTool(CallLoguer):
77 """Fake real SyncDaemonTool."""92 """Fake real SyncDaemonTool."""
93
78 def __init__(self, _):94 def __init__(self, _):
79 CallLoguer.__init__(self)95 CallLoguer.__init__(self)
8096
@@ -104,9 +120,9 @@
104 return called_args120 return called_args
105121
106 def fake_sdt_response(self, method_name, response):122 def fake_sdt_response(self, method_name, response):
107 """Fakes SDT answer in deferred mode."""123 """Fake SDT answer in deferred mode."""
108 self.dbus.sync_daemon_tool._fake_response = (method_name,124 self.dbus.sync_daemon_tool._fake_response = (method_name, response)
109 defer.succeed(response))125
110126
111class TestSignalHooking(SafeTests):127class TestSignalHooking(SafeTests):
112 """Signal hooking tests.128 """Signal hooking tests.
@@ -114,6 +130,7 @@
114 We can not check if the methods are really called, because DBus holds the130 We can not check if the methods are really called, because DBus holds the
115 method object itself, so no chance in monkeypatching.131 method object itself, so no chance in monkeypatching.
116 """132 """
133
117 def _get_hooked(self, iface, signal):134 def _get_hooked(self, iface, signal):
118 """Return the hooked method if any."""135 """Return the hooked method if any."""
119 if iface is None:136 if iface is None:
@@ -152,6 +169,16 @@
152 self.assertEqual(self._get_hooked('Folders', 'FolderDeleted'),169 self.assertEqual(self._get_hooked('Folders', 'FolderDeleted'),
153 self.dbus._on_folder_deleted)170 self.dbus._on_folder_deleted)
154171
172 def test_folder_subscribed_changed(self):
173 """Test folder subscribed changed callback."""
174 self.assertEqual(self._get_hooked('Folders', 'FolderSubscribed'),
175 self.dbus._on_folder_subscribed)
176
177 def test_folder_unsubscribed_changed(self):
178 """Test folder unsubscribed changed callback."""
179 self.assertEqual(self._get_hooked('Folders', 'FolderUnSubscribed'),
180 self.dbus._on_folder_unsubscribed)
181
155 def test_share_created(self):182 def test_share_created(self):
156 """Test share created callback."""183 """Test share created callback."""
157 self.assertEqual(self._get_hooked('Shares', 'ShareCreated'),184 self.assertEqual(self._get_hooked('Shares', 'ShareCreated'),
@@ -188,7 +215,7 @@
188215
189216
190class TestDataProcessingStatus(SafeTests):217class TestDataProcessingStatus(SafeTests):
191 """Processes Status before sending it to SyncDaemon."""218 """Process Status before sending it to SyncDaemon."""
192219
193 @defer.inlineCallbacks220 @defer.inlineCallbacks
194 def test_get_status(self):221 def test_get_status(self):
@@ -224,7 +251,7 @@
224251
225252
226class TestDataProcessingNameOwner(SafeTests):253class TestDataProcessingNameOwner(SafeTests):
227 """Processes Name Owner data before sending it to SyncDaemon."""254 """Process Name Owner data before sending it to SyncDaemon."""
228255
229 def test_name_owner_changed_no_syncdaemon(self):256 def test_name_owner_changed_no_syncdaemon(self):
230 """Test name owner changed callback."""257 """Test name owner changed callback."""
@@ -245,7 +272,7 @@
245272
246273
247class TestDataProcessingCQ(SafeTests):274class TestDataProcessingCQ(SafeTests):
248 """Processes CQ data before sending it to SyncDaemon."""275 """Process CQ data before sending it to SyncDaemon."""
249276
250 @defer.inlineCallbacks277 @defer.inlineCallbacks
251 def test_nodata(self):278 def test_nodata(self):
@@ -288,7 +315,7 @@
288315
289316
290class TestDataProcessingMQ(SafeTests):317class TestDataProcessingMQ(SafeTests):
291 """Processes MQ data before sending it to SyncDaemon."""318 """Process MQ data before sending it to SyncDaemon."""
292319
293 @defer.inlineCallbacks320 @defer.inlineCallbacks
294 def test_nodata(self):321 def test_nodata(self):
@@ -461,9 +488,33 @@
461 self.assertEqual(data.share, 'a')488 self.assertEqual(data.share, 'a')
462 self.assertEqual(data.node, 'b')489 self.assertEqual(data.node, 'b')
463490
491 @defer.inlineCallbacks
492 def test_ChangePublicAccess(self):
493 """Test meta with ChangePublicAccess."""
494 cmd = 'ChangePublicAccess'
495 self.fake_sdt_response('waiting_metadata', [cmd])
496 rcv = yield self.dbus.get_meta_queue()
497 data = rcv[0]
498 self.assertEqual(data.operation, 'ChangePublicAccess')
499 self.assertEqual(data.path, None)
500 self.assertEqual(data.share, None)
501 self.assertEqual(data.node, None)
502
503 @defer.inlineCallbacks
504 def test_AnswerShare(self):
505 """Test meta with AnswerShare."""
506 cmd = 'AnswerShare'
507 self.fake_sdt_response('waiting_metadata', [cmd])
508 rcv = yield self.dbus.get_meta_queue()
509 data = rcv[0]
510 self.assertEqual(data.operation, 'AnswerShare')
511 self.assertEqual(data.path, None)
512 self.assertEqual(data.share, None)
513 self.assertEqual(data.node, None)
514
464515
465class TestDataProcessingFolders(SafeTests):516class TestDataProcessingFolders(SafeTests):
466 """Processes Folders data before sending it to SyncDaemon."""517 """Process Folders data before sending it to SyncDaemon."""
467518
468 @defer.inlineCallbacks519 @defer.inlineCallbacks
469 def test_nodata(self):520 def test_nodata(self):
@@ -520,10 +571,40 @@
520 self.dbus._on_folder_deleted(None)571 self.dbus._on_folder_deleted(None)
521 self.get_msd_called("on_sd_folders_changed")572 self.get_msd_called("on_sd_folders_changed")
522573
574 def test_folders_changed_from_subscribed(self):
575 """Test folders changed callback from subscribed."""
576 self.dbus._on_folder_subscribed(None)
577 self.get_msd_called("on_sd_folders_changed")
578
579 def test_folders_changed_from_unsubscribed(self):
580 """Test folders changed callback from unsubscribed."""
581 self.dbus._on_folder_unsubscribed(None)
582 self.get_msd_called("on_sd_folders_changed")
583
584
585class TestDataProcessingMetadata(SafeTests):
586 """Process Metadata data before sending it to SyncDaemon."""
587
588 @defer.inlineCallbacks
589 def test_info_ok(self):
590 """Test get metadata and see response."""
591 md = dbus.Dictionary({'a': 3, 'c': 4}, signature=dbus.Signature('ss'))
592 self.fake_sdt_response('get_metadata', md)
593 rcv = yield self.dbus.get_metadata('path')
594 self.assertEqual(rcv, dict(a=3, c=4))
595
596 @defer.inlineCallbacks
597 def test_info_bad(self):
598 """Test get metadata and get the error."""
599 exc = dbus.exceptions.DBusException(
600 name='org.freedesktop.DBus.Python.KeyError')
601 self.fake_sdt_response('get_metadata', exc)
602 rcv = yield self.dbus.get_metadata('not a real path')
603 self.assertEqual(rcv, dbusiface.NOT_SYNCHED_PATH)
523604
524605
525class TestDataProcessingShares(SafeTests):606class TestDataProcessingShares(SafeTests):
526 """Processes Shares data before sending it to SyncDaemon."""607 """Process Shares data before sending it to SyncDaemon."""
527608
528 @defer.inlineCallbacks609 @defer.inlineCallbacks
529 def test_sharestome_nodata(self):610 def test_sharestome_nodata(self):
@@ -693,68 +774,74 @@
693774
694 def test_instancing(self):775 def test_instancing(self):
695 """Just logged SD instancing."""776 """Just logged SD instancing."""
696 self.assertTrue(self.handler.check_inf("DBus interface starting"))777 self.assertTrue(self.handler.check_info("DBus interface starting"))
697778
698 def test_shutdown(self):779 def test_shutdown(self):
699 """Log when SD shutdowns."""780 """Log when SD shutdowns."""
700 self.dbus.shutdown()781 self.dbus.shutdown()
701 self.assertTrue(self.handler.check_inf("DBus interface going down"))782 self.assertTrue(self.handler.check_info("DBus interface going down"))
702783
703 def test_waiting_content(self):784 def test_waiting_content(self):
704 """Test call to waiting content."""785 """Test call to waiting content."""
705 self.dbus.get_content_queue()786 self.dbus.get_content_queue()
706 self.assertTrue(self.handler.check_inf("Getting content queue"))787 self.assertTrue(self.handler.check_info("Getting content queue"))
707788
708 def test_waiting_meta(self):789 def test_waiting_meta(self):
709 """Test call to waiting meta."""790 """Test call to waiting meta."""
710 self.dbus.get_meta_queue()791 self.dbus.get_meta_queue()
711 self.assertTrue(self.handler.check_inf("Getting meta queue"))792 self.assertTrue(self.handler.check_info("Getting meta queue"))
712793
713 def test_get_status(self):794 def test_get_status(self):
714 """Test call to status."""795 """Test call to status."""
715 self.dbus.get_status()796 self.dbus.get_status()
716 self.assertTrue(self.handler.check_inf("Getting status"))797 self.assertTrue(self.handler.check_info("Getting status"))
717798
718 def test_get_folders(self):799 def test_get_folders(self):
719 """Test call to folders."""800 """Test call to folders."""
720 self.dbus.get_folders()801 self.dbus.get_folders()
721 self.assertTrue(self.handler.check_inf("Getting folders"))802 self.assertTrue(self.handler.check_info("Getting folders"))
803
804 def test_get_metadata(self):
805 """Test call to metadata."""
806 self.dbus.get_metadata('path')
807 msg = "Getting metadata for u'path'"
808 self.assertTrue(self.handler.check_info(msg))
722809
723 def test_get_shares_to_me(self):810 def test_get_shares_to_me(self):
724 """Test call to shares to me."""811 """Test call to shares to me."""
725 self.dbus.get_shares_to_me()812 self.dbus.get_shares_to_me()
726 self.assertTrue(self.handler.check_inf("Getting shares to me"))813 self.assertTrue(self.handler.check_info("Getting shares to me"))
727814
728 def test_get_shares_to_other(self):815 def test_get_shares_to_other(self):
729 """Test call to shares to others."""816 """Test call to shares to others."""
730 self.dbus.get_shares_to_others()817 self.dbus.get_shares_to_others()
731 self.assertTrue(self.handler.check_inf("Getting shares to others"))818 self.assertTrue(self.handler.check_info("Getting shares to others"))
732819
733 def test_is_sd_started(self):820 def test_is_sd_started(self):
734 """Test call to is_sd_started."""821 """Test call to is_sd_started."""
735 self.dbus.is_sd_started()822 self.dbus.is_sd_started()
736 self.assertTrue(self.handler.check_inf(823 self.assertTrue(self.handler.check_info(
737 "Checking if SD is started: True"))824 "Checking if SD is started: True"))
738825
739 def test_start(self):826 def test_start(self):
740 """Test call to start."""827 """Test call to start."""
741 self.dbus.start()828 self.dbus.start()
742 self.assertTrue(self.handler.check_inf("Calling start"))829 self.assertTrue(self.handler.check_info("Calling start"))
743830
744 def test_quit(self):831 def test_quit(self):
745 """Test call to quit."""832 """Test call to quit."""
746 self.dbus.quit()833 self.dbus.quit()
747 self.assertTrue(self.handler.check_inf("Calling quit"))834 self.assertTrue(self.handler.check_info("Calling quit"))
748835
749 def test_connect(self):836 def test_connect(self):
750 """Test call to connect."""837 """Test call to connect."""
751 self.dbus.connect()838 self.dbus.connect()
752 self.assertTrue(self.handler.check_inf("Calling connect"))839 self.assertTrue(self.handler.check_info("Calling connect"))
753840
754 def test_disconnect(self):841 def test_disconnect(self):
755 """Test call to disconnect."""842 """Test call to disconnect."""
756 self.dbus.disconnect()843 self.dbus.disconnect()
757 self.assertTrue(self.handler.check_inf("Calling disconnect"))844 self.assertTrue(self.handler.check_info("Calling disconnect"))
758845
759 def test_status_changed(self):846 def test_status_changed(self):
760 """Test status changed callback."""847 """Test status changed callback."""
@@ -762,58 +849,71 @@
762 is_connected='True', is_online='', queues='queues',849 is_connected='True', is_online='', queues='queues',
763 connection='connection')850 connection='connection')
764 self.dbus._on_status_changed(d)851 self.dbus._on_status_changed(d)
765 self.assertTrue(self.handler.check_inf("Received Status changed"))852 self.assertTrue(self.handler.check_info("Received Status changed"))
766 self.assertTrue(self.handler.check_dbg("Status changed data: %r" % d))853 msg = "Status changed data: %r" % d
854 self.assertTrue(self.handler.check_debug(msg))
767855
768 def test_content_queue_changed(self):856 def test_content_queue_changed(self):
769 """Test content queue changed callback."""857 """Test content queue changed callback."""
770 self.dbus._on_content_queue_changed("foo")858 self.dbus._on_content_queue_changed("foo")
771 self.assertTrue(self.handler.check_inf(859 self.assertTrue(self.handler.check_info(
772 "Received Content Queue changed"))860 "Received Content Queue changed"))
773861
774 def test_name_owner_changed_other(self):862 def test_name_owner_changed_other(self):
775 """Test name owner changed callback, no SD."""863 """Test name owner changed callback, no SD."""
776 self.dbus._on_name_owner_changed("other", "", "T")864 self.dbus._on_name_owner_changed("other", "", "T")
777 self.assertFalse(self.handler.check_inf("Received Name Owner changed"))865 msg = "Received Name Owner changed"
866 self.assertFalse(self.handler.check_info(msg))
778867
779 def test_name_owner_changed_syncdaemon(self):868 def test_name_owner_changed_syncdaemon(self):
780 """Test name owner changed callback, SD value ok."""869 """Test name owner changed callback, SD value ok."""
781 self.dbus._on_name_owner_changed("com.ubuntuone.SyncDaemon", "", "T")870 self.dbus._on_name_owner_changed("com.ubuntuone.SyncDaemon", "", "T")
782 self.assertTrue(self.handler.check_inf("Received Name Owner changed"))871 self.assertTrue(self.handler.check_info("Received Name Owner changed"))
783 self.assertTrue(self.handler.check_dbg("Name Owner data: u'' u'T'"))872 self.assertTrue(self.handler.check_debug("Name Owner data: u'' u'T'"))
784873
785 def test_name_owner_changed_yes_syncdaemon_TF(self):874 def test_name_owner_changed_yes_syncdaemon_TF(self):
786 """Test name owner changed callback, SD value bad."""875 """Test name owner changed callback, SD value bad."""
787 self.dbus._on_name_owner_changed("com.ubuntuone.SyncDaemon", "F", "T")876 self.dbus._on_name_owner_changed("com.ubuntuone.SyncDaemon", "F", "T")
788 self.assertTrue(self.handler.check_inf("Received Name Owner changed"))877 self.assertTrue(self.handler.check_info("Received Name Owner changed"))
789 self.assertTrue(self.handler.check_dbg("Name Owner data: u'F' u'T'"))878 self.assertTrue(self.handler.check_debug("Name Owner data: u'F' u'T'"))
790 self.assertTrue(self.handler.check("ERROR",879 self.assertTrue(self.handler.check("ERROR",
791 "Name Owner invalid data: Same bool in old and new!"))880 "Name Owner invalid data: Same bool in old and new!"))
792881
793 def test_folder_created_changed(self):882 def test_folder_created_changed(self):
794 """Test folder created changed callback."""883 """Test folder created changed callback."""
795 self.dbus._on_folder_created("foo")884 self.dbus._on_folder_created("foo")
796 self.assertTrue(self.handler.check_inf("Received Folder created"))885 self.assertTrue(self.handler.check_info("Received Folder created"))
797886
798 def test_folder_deleted_changed(self):887 def test_folder_deleted_changed(self):
799 """Test folder deleted changed callback."""888 """Test folder deleted changed callback."""
800 self.dbus._on_folder_deleted("foo")889 self.dbus._on_folder_deleted("foo")
801 self.assertTrue(self.handler.check_inf("Received Folder deleted"))890 self.assertTrue(self.handler.check_info("Received Folder deleted"))
891
892 def test_folder_subscribed_changed(self):
893 """Test folder subscribed changed callback."""
894 self.dbus._on_folder_subscribed("foo")
895 self.assertTrue(self.handler.check_info("Received Folder subscribed"))
896
897 def test_folder_unsubscribed_changed(self):
898 """Test folder unsubscribed changed callback."""
899 self.dbus._on_folder_unsubscribed("foo")
900 self.assertTrue(self.handler.check_info(
901 "Received Folder unsubscribed"))
802902
803 def test_share_created(self):903 def test_share_created(self):
804 """Test share created callback."""904 """Test share created callback."""
805 self.dbus._on_share_created("foo")905 self.dbus._on_share_created("foo")
806 self.assertTrue(self.handler.check_inf("Received Share created"))906 self.assertTrue(self.handler.check_info("Received Share created"))
807907
808 def test_share_deleted(self):908 def test_share_deleted(self):
809 """Test share deleted callback."""909 """Test share deleted callback."""
810 self.dbus._on_share_deleted("foo")910 self.dbus._on_share_deleted("foo")
811 self.assertTrue(self.handler.check_inf("Received Share deleted"))911 self.assertTrue(self.handler.check_info("Received Share deleted"))
812912
813 def test_share__changed(self):913 def test_share__changed(self):
814 """Test share changed callback."""914 """Test share changed callback."""
815 self.dbus._on_share_changed("foo")915 self.dbus._on_share_changed("foo")
816 self.assertTrue(self.handler.check_inf("Received Share changed"))916 self.assertTrue(self.handler.check_info("Received Share changed"))
817917
818 @defer.inlineCallbacks918 @defer.inlineCallbacks
819 def test_content_queue_processing(self):919 def test_content_queue_processing(self):
@@ -821,9 +921,9 @@
821 c = dict(operation='oper', path='path', share='share', node='node')921 c = dict(operation='oper', path='path', share='share', node='node')
822 self.fake_sdt_response('waiting_content', [c])922 self.fake_sdt_response('waiting_content', [c])
823 yield self.dbus.get_content_queue()923 yield self.dbus.get_content_queue()
824 self.assertTrue(self.handler.check_inf(924 self.assertTrue(self.handler.check_info(
825 "Processing Content Queue items (1)"))925 "Processing Content Queue items (1)"))
826 self.assertTrue(self.handler.check_dbg(926 self.assertTrue(self.handler.check_debug(
827 " Content Queue data: %s" % c))927 " Content Queue data: %s" % c))
828928
829 @defer.inlineCallbacks929 @defer.inlineCallbacks
@@ -831,9 +931,9 @@
831 """Test with one item in the queue."""931 """Test with one item in the queue."""
832 self.fake_sdt_response('waiting_metadata', ['ListShares'])932 self.fake_sdt_response('waiting_metadata', ['ListShares'])
833 yield self.dbus.get_meta_queue()933 yield self.dbus.get_meta_queue()
834 self.assertTrue(self.handler.check_inf(934 self.assertTrue(self.handler.check_info(
835 "Processing Meta Queue items (1)"))935 "Processing Meta Queue items (1)"))
836 self.assertTrue(self.handler.check_dbg(936 self.assertTrue(self.handler.check_debug(
837 " Meta Queue data: u'ListShares'"))937 " Meta Queue data: u'ListShares'"))
838938
839 @defer.inlineCallbacks939 @defer.inlineCallbacks
@@ -843,8 +943,18 @@
843 suggested_path=u'sgp', type='UDF', volume_id='vid')943 suggested_path=u'sgp', type='UDF', volume_id='vid')
844 self.fake_sdt_response('get_folders', [d])944 self.fake_sdt_response('get_folders', [d])
845 yield self.dbus.get_folders()945 yield self.dbus.get_folders()
846 self.assertTrue(self.handler.check_inf("Processing Folders items (1)"))946 msg = "Processing Folders items (1)"
847 self.assertTrue(self.handler.check_dbg(" Folders data: %r" % d))947 self.assertTrue(self.handler.check_info(msg))
948 self.assertTrue(self.handler.check_debug(" Folders data: %r" % d))
949
950 @defer.inlineCallbacks
951 def test_metadata_processing(self):
952 """Test get metadata."""
953 d = dict(lot_of_data="I don't care")
954 self.fake_sdt_response('get_metadata', d)
955 yield self.dbus.get_metadata('path')
956 self.assertTrue(self.handler.check_debug(
957 "Got metadata for path u'path': %r" % d))
848958
849 @defer.inlineCallbacks959 @defer.inlineCallbacks
850 def test_sharestome_processing(self):960 def test_sharestome_processing(self):
@@ -855,9 +965,9 @@
855 volume_id=u'vol', type=u'Share')965 volume_id=u'vol', type=u'Share')
856 self.fake_sdt_response('get_shares', [d])966 self.fake_sdt_response('get_shares', [d])
857 yield self.dbus.get_shares_to_me()967 yield self.dbus.get_shares_to_me()
858 self.assertTrue(self.handler.check_inf(968 self.assertTrue(self.handler.check_info(
859 "Processing Shares To Me items (1)"))969 "Processing Shares To Me items (1)"))
860 self.assertTrue(self.handler.check_dbg(" Share data: %r" % d))970 self.assertTrue(self.handler.check_debug(" Share data: %r" % d))
861971
862 @defer.inlineCallbacks972 @defer.inlineCallbacks
863 def test_sharestoothers_processing(self):973 def test_sharestoothers_processing(self):
@@ -868,16 +978,17 @@
868 volume_id=u'vol', type=u'Shared')978 volume_id=u'vol', type=u'Shared')
869 self.fake_sdt_response('list_shared', [d])979 self.fake_sdt_response('list_shared', [d])
870 yield self.dbus.get_shares_to_others()980 yield self.dbus.get_shares_to_others()
871 self.assertTrue(self.handler.check_inf(981 self.assertTrue(self.handler.check_info(
872 "Processing Shares To Others items (1)"))982 "Processing Shares To Others items (1)"))
873 self.assertTrue(self.handler.check_dbg(" Share data: %r" % d))983 self.assertTrue(self.handler.check_debug(" Share data: %r" % d))
874984
875985
876class RetryDecoratorTests(TwistedTestCase):986class RetryDecoratorTests(TwistedTestCase):
877 """Test the retry decorator."""987 """Test the retry decorator."""
878988
879 class Helper(object):989 class Helper(object):
880 """Fails some times, finally succeeds."""990 """Fail some times, finally succeed."""
991
881 def __init__(self, limit, excep=None):992 def __init__(self, limit, excep=None):
882 self.cant = 0993 self.cant = 0
883 self.limit = limit994 self.limit = limit
@@ -915,7 +1026,7 @@
915 self.assertTrue(dbusiface._is_retry_exception(err))1026 self.assertTrue(dbusiface._is_retry_exception(err))
9161027
917 def get_decorated_func(self, func):1028 def get_decorated_func(self, func):
918 """Executes the test calling the received function."""1029 """Execute the test calling the received function."""
9191030
920 @dbusiface.retryable1031 @dbusiface.retryable
921 def f():1032 def f():
9221033
=== modified file 'magicicada/tests/test_magicicada.py'
--- magicicada/tests/test_magicicada.py 2010-06-08 10:53:02 +0000
+++ magicicada/tests/test_magicicada.py 2010-07-09 18:33:43 +0000
@@ -18,6 +18,9 @@
1818
19"""Tests for magicicada."""19"""Tests for magicicada."""
2020
21import logging
22import sys
23
21from functools import wraps24from functools import wraps
2225
23import gobject26import gobject
@@ -26,33 +29,57 @@
2629
27from twisted.trial.unittest import TestCase30from twisted.trial.unittest import TestCase
2831
29from magicicada import MagicicadaUI, CONTENT_QUEUE, META_QUEUE, syncdaemon32from magicicada import MagicicadaUI, CONTENT_QUEUE, META_QUEUE, \
33 UBUNTU_ONE_ROOT, syncdaemon
30from magicicada.dbusiface import QueueData, FolderData, ShareData34from magicicada.dbusiface import QueueData, FolderData, ShareData
31from magicicada.helpers import NO_OP, humanize_bytes35from magicicada.helpers import NO_OP, humanize_bytes, get_data_file
36from magicicada.tests.helpers import MementoHandler
37
38
39# It's ok to access private data in the test suite
40# pylint: disable-msg=W0212
41# Arguments number differs from overridden method
42# pylint: disable-msg=W0221
43
3244
33def process_gtk_pendings():45def process_gtk_pendings():
34 while gtk.events_pending(): gtk.main_iteration()46 """Process all gtk pending events."""
47 while gtk.events_pending():
48 gtk.main_iteration()
49
3550
36def close_dialog((dialog, test)):51def close_dialog((dialog, test)):
37 """Call the 'test', close 'dialog'."""52 """Call the 'test', close 'dialog'."""
38 try:53 response = gtk.RESPONSE_CLOSE
39 process_gtk_pendings()54 try:
40 test()55 process_gtk_pendings()
41 process_gtk_pendings()56 test()
42 finally:57 finally:
43 dialog.response(gtk.RESPONSE_CLOSE)58 dialog.response(response)
44 process_gtk_pendings()59 process_gtk_pendings()
45 return False # do not be called again60 return False # do not be called again
61
62
63def test_and_click((button, test)):
64 """Call the 'test', and click 'button'."""
65 try:
66 test()
67 finally:
68 button.clicked()
69 return False # do not be called again
4670
4771
48class FakedSyncdaemon(object):72class FakedSyncdaemon(object):
49 """A faked syncdaemon."""73 """A faked syncdaemon."""
5074
51 def __init__(self):75 def __init__(self):
76 self._meta_path = None
77
52 self.current_state = syncdaemon.State()78 self.current_state = syncdaemon.State()
53 self.meta_queue = []79 self.meta_queue = []
54 self.content_queue = []80 self.content_queue = []
55 self.folders = []81 self.folders = []
82 self.processing_meta = False
5683
57 self.on_started_callback = NO_OP84 self.on_started_callback = NO_OP
58 self.on_stopped_callback = NO_OP85 self.on_stopped_callback = NO_OP
@@ -63,13 +90,16 @@
63 self.status_changed_callback = NO_OP90 self.status_changed_callback = NO_OP
64 self.content_queue_changed_callback = NO_OP91 self.content_queue_changed_callback = NO_OP
65 self.meta_queue_changed_callback = NO_OP92 self.meta_queue_changed_callback = NO_OP
93 self.on_metadata_ready_callback = None # mandatory
66 self.shutdown = NO_OP94 self.shutdown = NO_OP
6795
68 self.start = lambda: setattr(self.current_state, 'is_started', True)96 self.start = lambda: setattr(self.current_state, 'is_started', True)
69 self.quit = lambda: setattr(self.current_state, 'is_started', False)97 self.quit = lambda: setattr(self.current_state, 'is_started', False)
70 self.connect = lambda: setattr(self.current_state, 'is_connected', True)98 self.connect = lambda: setattr(self.current_state,
71 self.disconnect = \99 'is_connected', True)
72 lambda: setattr(self.current_state, 'is_connected', False)100 self.disconnect = lambda: \
101 setattr(self.current_state, 'is_connected', False)
102 self.get_metadata = lambda path: setattr(self, '_meta_path', path)
73103
74104
75class MagicicadaUITestCase(TestCase):105class MagicicadaUITestCase(TestCase):
@@ -78,13 +108,14 @@
78 def setUp(self):108 def setUp(self):
79 """Init."""109 """Init."""
80 self.ui = MagicicadaUI(syncdaemon_class=FakedSyncdaemon)110 self.ui = MagicicadaUI(syncdaemon_class=FakedSyncdaemon)
81 self._called = False111 self.called = False
82 self.set_called = lambda *args, **kwargs: setattr(self, '_called', True)112 self.response = None
113 self.set_called = lambda *args, **kwargs: setattr(self, 'called', True)
83114
84 def tearDown(self):115 def tearDown(self):
85 """Cleanup."""116 """Cleanup."""
86 self.ui.on_main_window_destroy(self.ui.main_window)117 self.ui.on_main_window_destroy(self.ui.main_window)
87 self._called = False118 self.called = False
88119
89 def do_start(self):120 def do_start(self):
90 """Simulate that start fully happened."""121 """Simulate that start fully happened."""
@@ -106,14 +137,16 @@
106 result.append(data_type(**kwargs))137 result.append(data_type(**kwargs))
107 return result138 return result
108139
109 def assert_store_correct(self, store, items):140 def assert_store_correct(self, store, items, markup=None):
110 """Test that 'store' has 'items' as content."""141 """Test that 'store' has 'items' as content."""
111 msg = 'amount of rows for %s must be %s (got %s).'142 msg = 'amount of rows for %s must be %s (got %s).'
112 self.assertEqual(len(store), len(items),143 self.assertEqual(len(store), len(items),
113 msg % (store, len(items), len(store)))144 msg % (store, len(items), len(store)))
145
114 # assert rows content equal to items content146 # assert rows content equal to items content
115 tree_iter = store.get_iter_root()147 tree_iter = store.get_iter_root()
116 tmp = list(reversed(items))148 tmp = list(reversed(items))
149 do_markup = markup is not None
117 msg = "column %i ('%s') must be '%s' (got '%s' instead)"150 msg = "column %i ('%s') must be '%s' (got '%s' instead)"
118 while tree_iter is not None:151 while tree_iter is not None:
119 head = tmp.pop()152 head = tmp.pop()
@@ -122,27 +155,68 @@
122 expected = getattr(head, field)155 expected = getattr(head, field)
123 if store.get_column_type(i).name == 'gboolean':156 if store.get_column_type(i).name == 'gboolean':
124 expected = bool(expected)157 expected = bool(expected)
125 self.assertEqual(expected, actual, msg % (i, field, expected, actual))158 elif do_markup:
159 expected = markup(expected)
160 self.assertEqual(expected, actual,
161 msg % (i, field, expected, actual))
126162
127 tree_iter = store.iter_next(tree_iter)163 tree_iter = store.iter_next(tree_iter)
164 do_markup = False # only for first row
128165
129 def assert_indicator_disabled(self, indicator):166 def assert_indicator_disabled(self, indicator):
130 """Test that 'indicator' is not sensitive."""167 """Test that 'indicator' is not sensitive."""
131 self.assertFalse(indicator.is_sensitive(), 'indicator is not sensitive')168 self.assertFalse(indicator.is_sensitive(),
169 'indicator must not be sensitive.')
132170
133 def assert_indicator_ready(self, indicator):171 def assert_indicator_ready(self, indicator):
134 """Test that 'indicator' is sensitive and green."""172 """Test that 'indicator' is sensitive and green."""
135 self.assertTrue(indicator.is_sensitive(), 'indicator is sensitive')173 self.assertTrue(indicator.is_sensitive(),
136 expected = indicator.get_pixbuf() # a test on its own174 'indicator must be sensitive.')
175 expected = indicator.get_pixbuf() # a test on its own
137 self.assertEqual(self.ui.active_indicator, expected,176 self.assertEqual(self.ui.active_indicator, expected,
138 'indicator is the correct pixbuf')177 'indicator must have the correct pixbuf.')
139178
140 def assert_indicator_loading(self, indicator):179 def assert_indicator_loading(self, indicator):
141 """Test that 'indicator' is sensitive and loading."""180 """Test that 'indicator' is sensitive and loading."""
142 self.assertTrue(indicator.is_sensitive(), 'indicator is sensitive')181 self.assertTrue(indicator.is_sensitive(),
143 expected = indicator.get_animation() # a test on its own182 'indicator must be sensitive.')
183 expected = indicator.get_animation() # a test on its own
144 self.assertEqual(self.ui.loading_animation, expected,184 self.assertEqual(self.ui.loading_animation, expected,
145 'indicator is the correct animation')185 'indicator must have the correct animation.')
186
187 def assert_widget_availability(self, widget_name, enabled=True):
188 """Check button availability according to 'enabled'."""
189 widget = getattr(self.ui, widget_name)
190 self.assertTrue(self.ui.widget_is_visible(widget),
191 '%s should be visible' % widget_name)
192 sensitive = widget.is_sensitive()
193 msg = '%s should %sbe sensitive'
194 self.assertTrue(sensitive if enabled else not sensitive,
195 msg % (widget_name, '' if enabled else 'not '))
196
197 def assert_dialog_properties(self, dialog_name, title, size=(600, 300),
198 modal=True):
199 """The dialog 'dialog_name' has correct properties."""
200 dialog = getattr(self.ui, dialog_name)
201 actual = dialog.size_request()
202 msg = 'size must be %s (got %s instead).'
203 self.assertEquals(size, actual, msg % (size, actual))
204
205 msg = '%s must %sbe modal.'
206 self.assertEqual(modal, dialog.get_modal(),
207 msg % (dialog_name, '' if modal else 'not '))
208
209 position = dialog.get_property('window-position')
210 self.assertEqual(gtk.WIN_POS_CENTER, position,
211 '%s must be centered.' % dialog_name)
212
213 actual = dialog.get_title()
214 msg = '%s title must be %s (got %s instead)'
215 self.assertEqual(title, actual, msg % (dialog_name, title, actual))
216
217 msg = '%s must have main_window as parent.'
218 #self.assertTrue(dialog.parent is self.ui.main_window,
219 # msg % dialog_name)
146220
147221
148class MagicicadaUIBasicTestCase(MagicicadaUITestCase):222class MagicicadaUIBasicTestCase(MagicicadaUITestCase):
@@ -157,7 +231,7 @@
157 """SyncDaemon instance is shutdown at destroy time."""231 """SyncDaemon instance is shutdown at destroy time."""
158 self.patch(self.ui.sd, 'shutdown', self.set_called)232 self.patch(self.ui.sd, 'shutdown', self.set_called)
159 self.ui.on_main_window_destroy(self.ui.main_window)233 self.ui.on_main_window_destroy(self.ui.main_window)
160 self.assertTrue(self._called,234 self.assertTrue(self.called,
161 'syncdaemon.shutdown must be called at destroy time.')235 'syncdaemon.shutdown must be called at destroy time.')
162236
163 def test_main_window_is_visible(self):237 def test_main_window_is_visible(self):
@@ -192,7 +266,7 @@
192 """Update is called at startup."""266 """Update is called at startup."""
193 self.patch(MagicicadaUI, 'update', self.set_called)267 self.patch(MagicicadaUI, 'update', self.set_called)
194 self.ui = MagicicadaUI(syncdaemon_class=FakedSyncdaemon)268 self.ui = MagicicadaUI(syncdaemon_class=FakedSyncdaemon)
195 self.assertTrue(self._called,269 self.assertTrue(self.called,
196 'update was called at startup.')270 'update was called at startup.')
197271
198272
@@ -215,11 +289,11 @@
215 """Test on_start_clicked."""289 """Test on_start_clicked."""
216 self.patch(self.ui.sd, 'start', self.set_called)290 self.patch(self.ui.sd, 'start', self.set_called)
217 self.ui.on_start_clicked(self.ui.start)291 self.ui.on_start_clicked(self.ui.start)
218 self.assertTrue(self._called, 'syncdaemon.start was called.')292 self.assertTrue(self.called, 'syncdaemon.start was called.')
219293
220 def test_on_connect_clicked(self):294 def test_on_connect_clicked(self):
221 """Test on_connect_clicked."""295 """Test on_connect_clicked."""
222 self.do_start() # need to be started296 self.do_start() # need to be started
223 self.ui.on_connect_clicked(self.ui.connect)297 self.ui.on_connect_clicked(self.ui.connect)
224298
225 self.assertTrue(self.ui.widget_is_visible(self.ui.connect))299 self.assertTrue(self.ui.widget_is_visible(self.ui.connect))
@@ -234,7 +308,7 @@
234 """Test on_connect_clicked."""308 """Test on_connect_clicked."""
235 self.patch(self.ui.sd, 'connect', self.set_called)309 self.patch(self.ui.sd, 'connect', self.set_called)
236 self.ui.on_connect_clicked(self.ui.connect)310 self.ui.on_connect_clicked(self.ui.connect)
237 self.assertTrue(self._called, 'syncdaemon.connect was called.')311 self.assertTrue(self.called, 'syncdaemon.connect was called.')
238312
239 def test_on_stop_clicked(self):313 def test_on_stop_clicked(self):
240 """Test on_stop_clicked."""314 """Test on_stop_clicked."""
@@ -243,7 +317,7 @@
243 self.patch(self.ui, 'on_disconnect_clicked', self.set_called)317 self.patch(self.ui, 'on_disconnect_clicked', self.set_called)
244 self.ui.on_stop_clicked(self.ui.stop)318 self.ui.on_stop_clicked(self.ui.stop)
245319
246 self.assertFalse(self._called, 'on_disconnect_clicked was not called.')320 self.assertFalse(self.called, 'on_disconnect_clicked was not called.')
247321
248 self.assertFalse(self.ui.widget_is_visible(self.ui.start))322 self.assertFalse(self.ui.widget_is_visible(self.ui.start))
249 self.assertTrue(self.ui.widget_is_visible(self.ui.stop))323 self.assertTrue(self.ui.widget_is_visible(self.ui.stop))
@@ -259,13 +333,13 @@
259 self.patch(self.ui, 'on_disconnect_clicked', self.set_called)333 self.patch(self.ui, 'on_disconnect_clicked', self.set_called)
260 self.ui.on_stop_clicked(self.ui.stop)334 self.ui.on_stop_clicked(self.ui.stop)
261335
262 self.assertTrue(self._called, 'on_disconnect_clicked was called.')336 self.assertTrue(self.called, 'on_disconnect_clicked was called.')
263337
264 def test_on_stop_clicked_stops_syncdaemon(self):338 def test_on_stop_clicked_stops_syncdaemon(self):
265 """Test on_stop_clicked."""339 """Test on_stop_clicked."""
266 self.patch(self.ui.sd, 'quit', self.set_called)340 self.patch(self.ui.sd, 'quit', self.set_called)
267 self.ui.on_stop_clicked(self.ui.stop)341 self.ui.on_stop_clicked(self.ui.stop)
268 self.assertTrue(self._called, 'syncdaemon.quit was called.')342 self.assertTrue(self.called, 'syncdaemon.quit was called.')
269343
270 def test_on_disconnect_clicked(self):344 def test_on_disconnect_clicked(self):
271 """Test on_disconnect_clicked."""345 """Test on_disconnect_clicked."""
@@ -280,7 +354,7 @@
280 """Test on_disconnect_clicked."""354 """Test on_disconnect_clicked."""
281 self.patch(self.ui.sd, 'disconnect', self.set_called)355 self.patch(self.ui.sd, 'disconnect', self.set_called)
282 self.ui.on_disconnect_clicked(self.ui.disconnect)356 self.ui.on_disconnect_clicked(self.ui.disconnect)
283 self.assertTrue(self._called, 'syncdaemon.disconnect was called.')357 self.assertTrue(self.called, 'syncdaemon.disconnect was called.')
284358
285359
286class MagicicadaUISystrayIconTestCase(MagicicadaUITestCase):360class MagicicadaUISystrayIconTestCase(MagicicadaUITestCase):
@@ -294,14 +368,15 @@
294368
295 def test_main_window_is_shown_when_clicked_after_hidden(self):369 def test_main_window_is_shown_when_clicked_after_hidden(self):
296 """Main window is shown when the icon is clicked after hidden."""370 """Main window is shown when the icon is clicked after hidden."""
297 self.ui.on_status_icon_activate(self.ui.status_icon) # hide371 self.ui.on_status_icon_activate(self.ui.status_icon) # hide
298 self.ui.on_status_icon_activate(self.ui.status_icon) # show372 self.ui.on_status_icon_activate(self.ui.status_icon) # show
299 msg = 'main_window should be visible when icon clicked after hidden.'373 msg = 'main_window should be visible when icon clicked after hidden.'
300 self.assertTrue(self.ui.widget_is_visible(self.ui.main_window), msg)374 self.assertTrue(self.ui.widget_is_visible(self.ui.main_window), msg)
301375
302376
303def skip_abstract_class(test):377def skip_abstract_class(test):
304 """If 'test' belongs to an abstract class, don't run it."""378 """If 'test' belongs to an abstract class, don't run it."""
379
305 @wraps(test)380 @wraps(test)
306 def inner(klass):381 def inner(klass):
307 """Execute 'test' only if not in an abstract class."""382 """Execute 'test' only if not in an abstract class."""
@@ -336,6 +411,24 @@
336 # operation path share node411 # operation path share node
337 return res412 return res
338413
414 def expected_markup(self, value):
415 """Return the markup for row at index 'i'."""
416 processing_meta = self.name == META_QUEUE and \
417 self.ui.sd.current_state.processing_meta
418 processing_content = self.name == CONTENT_QUEUE and \
419 self.ui.sd.current_state.processing_content
420 assert not (processing_meta and processing_content)
421 must_highlight = self.ui.sd.current_state.is_online and \
422 (processing_meta or processing_content)
423 result = self.ui.CURRENT_ROW % value \
424 if must_highlight and value is not None else value
425 return result
426
427 def assert_store_correct(self, store, items):
428 """Test that 'store' has 'items' as content."""
429 args = (store, items, self.expected_markup)
430 super(_MagicicadaUIQueueTestCase, self).assert_store_correct(*args)
431
339 @skip_abstract_class432 @skip_abstract_class
340 def test_callback_is_connected(self):433 def test_callback_is_connected(self):
341 """Queue changed callback is connected."""434 """Queue changed callback is connected."""
@@ -364,6 +457,13 @@
364 self.assert_store_correct(self.queue_store, [])457 self.assert_store_correct(self.queue_store, [])
365458
366 @skip_abstract_class459 @skip_abstract_class
460 def test_on_queue_changed_handles_an_item_none(self):
461 """On queue changed handles None as items."""
462 items = [QueueData(operation='Test', path='', share=None, node=None)]
463 self.sd_changed(items)
464 self.assert_store_correct(self.queue_store, items)
465
466 @skip_abstract_class
367 def test_model_is_cleared_before_updating(self):467 def test_model_is_cleared_before_updating(self):
368 """The model is cleared before upadting with a new set of data."""468 """The model is cleared before upadting with a new set of data."""
369 items = self.build_some_data()469 items = self.build_some_data()
@@ -400,7 +500,7 @@
400 cb = 'on_%s_queue_changed' % self.name500 cb = 'on_%s_queue_changed' % self.name
401 self.patch(self.ui, cb, self.set_called)501 self.patch(self.ui, cb, self.set_called)
402 self.ui.on_stopped()502 self.ui.on_stopped()
403 self.assertTrue(self._called,503 self.assertTrue(self.called,
404 '%s was called on_stopped.' % cb)504 '%s was called on_stopped.' % cb)
405505
406 @skip_abstract_class506 @skip_abstract_class
@@ -425,12 +525,64 @@
425 self.do_start()525 self.do_start()
426 self.assert_store_correct(self.queue_store, items)526 self.assert_store_correct(self.queue_store, items)
427527
428 items = items[:len(items)/2]528 items = items[:len(items) / 2]
429 self.set_sd_queue(items)529 self.set_sd_queue(items)
430 self.ui.on_stop_clicked(self.ui.stop)530 self.ui.on_stop_clicked(self.ui.stop)
431 self.ui.on_stopped()531 self.ui.on_stopped()
432 self.assert_store_correct(self.queue_store, items)532 self.assert_store_correct(self.queue_store, items)
433533
534 def assert_current_processing_row_is_different(self):
535 """Row being processed is highlighted."""
536 items = self.build_some_data()
537 self.sd_changed(items)
538
539 item = items[0]
540 attrs = type(item)._fields
541
542 markup = self.expected_markup
543 expected = tuple(markup(getattr(item, attr)) for attr in attrs)
544
545 iter_root = self.queue_store.get_iter_root()
546 actual = self.queue_store.get(iter_root, *xrange(len(attrs)))
547
548 msg = 'first row for %s queue must be %s (got %s instead)' % \
549 (self.name, expected, actual)
550 self.assertEqual(expected, actual, msg)
551
552 @skip_abstract_class
553 def test_current_processing_row_is_different_if_online(self):
554 """Row being processed is highlighted."""
555 self.ui.sd.current_state.set(is_online=True)
556
557 self.ui.sd.current_state.set(queues='')
558 self.assert_current_processing_row_is_different()
559
560 self.ui.sd.current_state.set(queues='WORKING_ON_METADATA')
561 self.assert_current_processing_row_is_different()
562
563 self.ui.sd.current_state.set(queues='WORKING_ON_CONTENT')
564 self.assert_current_processing_row_is_different()
565
566 self.ui.sd.current_state.set(queues='WORKING_ON_BOTH')
567 self.assert_current_processing_row_is_different()
568
569 @skip_abstract_class
570 def test_current_processing_row_is_not_different_if_offline(self):
571 """Row being processed is highlighted."""
572 self.ui.sd.current_state.set(is_online=False)
573
574 self.ui.sd.current_state.set(queues='')
575 self.assert_current_processing_row_is_different()
576
577 self.ui.sd.current_state.set(queues='WORKING_ON_METADATA')
578 self.assert_current_processing_row_is_different()
579
580 self.ui.sd.current_state.set(queues='WORKING_ON_CONTENT')
581 self.assert_current_processing_row_is_different()
582
583 self.ui.sd.current_state.set(queues='WORKING_ON_BOTH')
584 self.assert_current_processing_row_is_different()
585
434586
435class MagicicadaUIContentQueueTestCase(_MagicicadaUIQueueTestCase):587class MagicicadaUIContentQueueTestCase(_MagicicadaUIQueueTestCase):
436 """UI test cases for content queue view."""588 """UI test cases for content queue view."""
@@ -477,13 +629,14 @@
477 """Status callback is connected."""629 """Status callback is connected."""
478 self.assertEqual(self.ui.sd.status_changed_callback,630 self.assertEqual(self.ui.sd.status_changed_callback,
479 self.ui.on_status_changed,631 self.ui.on_status_changed,
480 'status_changed callback must be set')632 'status_changed callback must be set.')
481633
482 def test_status_label_ellipsizes(self):634 def test_status_label_ellipsizes(self):
483 """The status label ellipsizes."""635 """The status label ellipsizes."""
484 expected = pango.ELLIPSIZE_END636 expected = pango.ELLIPSIZE_END
485 actual = self.ui.status_label.get_ellipsize()637 actual = self.ui.status_label.get_ellipsize()
486 self.assertEqual(expected, actual, 'label ellipsizes is ELLIPSIZE_END.')638 self.assertEqual(expected, actual,
639 'label ellipsizes is ELLIPSIZE_END.')
487640
488 def test_on_status_changed_updates_status_label(self):641 def test_on_status_changed_updates_status_label(self):
489 """On status changed, the status label is updated."""642 """On status changed, the status label is updated."""
@@ -493,7 +646,7 @@
493646
494 def test_on_status_changed_updates_status_label_even_on_weird_cases(self):647 def test_on_status_changed_updates_status_label_even_on_weird_cases(self):
495 """On status changed, the status label is updated."""648 """On status changed, the status label is updated."""
496 keywords = ('name', 'description', 'queues', 'connection') # need order649 keywords = ('name', 'description', 'queues', 'connection') # order
497 for attr in keywords:650 for attr in keywords:
498 old_value = self.kwargs[attr]651 old_value = self.kwargs[attr]
499652
@@ -509,7 +662,7 @@
509662
510 def test_update_is_correct_for_status_label(self):663 def test_update_is_correct_for_status_label(self):
511 """Correctly updates the status label."""664 """Correctly updates the status label."""
512 self.ui.sd.current_state._set(**self.kwargs)665 self.ui.sd.current_state.set(**self.kwargs)
513 self.ui.update()666 self.ui.update()
514 self.assert_status_label_correct(**self.kwargs)667 self.assert_status_label_correct(**self.kwargs)
515668
@@ -517,7 +670,7 @@
517 """On SD stoppped, the UI updates the status label."""670 """On SD stoppped, the UI updates the status label."""
518 self.patch(self.ui, 'on_status_changed', self.set_called)671 self.patch(self.ui, 'on_status_changed', self.set_called)
519 self.ui.on_stopped()672 self.ui.on_stopped()
520 self.assertTrue(self._called,673 self.assertTrue(self.called,
521 'on_status_changed was called on_stopped.')674 'on_status_changed was called on_stopped.')
522675
523 def test_status_label_default_if_not_started(self):676 def test_status_label_default_if_not_started(self):
@@ -532,12 +685,12 @@
532685
533 def test_status_label_is_updated_on_started_and_on_stopped(self):686 def test_status_label_is_updated_on_started_and_on_stopped(self):
534 """Status label is updated on_started."""687 """Status label is updated on_started."""
535 self.ui.sd.current_state._set(**self.kwargs)688 self.ui.sd.current_state.set(**self.kwargs)
536 self.do_start()689 self.do_start()
537 self.assert_status_label_correct(**self.kwargs)690 self.assert_status_label_correct(**self.kwargs)
538691
539 self.kwargs['name'] = 'CHANGED'692 self.kwargs['name'] = 'CHANGED'
540 self.ui.sd.current_state._set(**self.kwargs)693 self.ui.sd.current_state.set(**self.kwargs)
541694
542 self.ui.on_stop_clicked(self.ui.stop)695 self.ui.on_stop_clicked(self.ui.stop)
543 self.ui.on_stopped()696 self.ui.on_stopped()
@@ -551,7 +704,7 @@
551 """Test that correctly updates the 'indicator'."""704 """Test that correctly updates the 'indicator'."""
552 cs = self.ui.sd.current_state705 cs = self.ui.sd.current_state
553 for expected in (True, False):706 for expected in (True, False):
554 cs._set(**{indicator: expected})707 cs.set(**{indicator: expected})
555708
556 self.ui.update()709 self.ui.update()
557710
@@ -684,67 +837,64 @@
684 self.volume_dialog_name = '%s_dialog' % self.name837 self.volume_dialog_name = '%s_dialog' % self.name
685 self.volume_dialog = getattr(self.ui, self.volume_dialog_name)838 self.volume_dialog = getattr(self.ui, self.volume_dialog_name)
686 self.on_volume_clicked = getattr(self.ui, 'on_%s_clicked' % self.name)839 self.on_volume_clicked = getattr(self.ui, 'on_%s_clicked' % self.name)
840 self.volume_close = getattr(self.ui, '%s_close' % self.name)
687841
688 def build_some_data(self, limit=5):842 def build_some_data(self, limit=5):
689 """Build some data to act as volume."""843 """Build some data to act as volume."""
690 kwargs = dict(data_type=self.data_type, limit=limit)844 kwargs = dict(data_type=self.data_type, limit=limit)
691 res = super(_MagicicadaUIVolumeTestCase, self).build_some_data(**kwargs)845 r = super(_MagicicadaUIVolumeTestCase, self).build_some_data(**kwargs)
692 return res846 return r
693847
694 def assert_volume_availability(self, enabled):848 def assert_widget_availability(self, enabled=True):
695 """Check volume availability according to 'enabled'."""849 """Check volume availability according to 'enabled'."""
696 self.assertTrue(self.ui.widget_is_visible(self.volume),850 s = super(_MagicicadaUIVolumeTestCase, self)
697 '%s should be visible' % self.name)851 s.assert_widget_availability(self.name, enabled)
698 sensitive = self.volume.is_sensitive()
699 msg = '%s should %sbe sensitive'
700 self.assertTrue(sensitive if enabled else not sensitive,
701 msg % (self.name, '' if enabled else 'not '))
702852
703 @skip_abstract_class853 @skip_abstract_class
704 def test_volume_are_disabled_until_started(self):854 def test_volume_are_disabled_until_started(self):
705 """Folders and shares are disabled until online."""855 """Folders and shares are disabled until online."""
706 # disabled at startup856 # disabled at startup
707 self.assert_volume_availability(enabled=False)857 self.assert_widget_availability(enabled=False)
708858
709 # enabled when started859 # enabled when started
710 self.do_start()860 self.do_start()
711 self.assert_volume_availability(enabled=True)861 self.assert_widget_availability(enabled=True)
712862
713 @skip_abstract_class863 @skip_abstract_class
714 def test_volume_are_enabled_until_stopped(self):864 def test_volume_are_enabled_until_stopped(self):
715 """Folders and shares are enabled until offline."""865 """Folders and shares are enabled until offline."""
716 self.do_connect()866 self.do_connect()
717 self.assert_volume_availability(enabled=True)867 self.assert_widget_availability(enabled=True)
718868
719 self.ui.on_online()869 self.ui.on_online()
720 self.assert_volume_availability(enabled=True)870 self.assert_widget_availability(enabled=True)
721871
722 self.ui.on_offline()872 self.ui.on_offline()
723 self.assert_volume_availability(enabled=True)873 self.assert_widget_availability(enabled=True)
724874
725 self.ui.on_disconnect_clicked(self.ui.disconnect)875 self.ui.on_disconnect_clicked(self.ui.disconnect)
726 self.ui.on_disconnected()876 self.ui.on_disconnected()
727 self.assert_volume_availability(enabled=True)877 self.assert_widget_availability(enabled=True)
728878
729 # disabled when stopped879 # disabled when stopped
730 self.ui.on_stop_clicked(self.ui.stop)880 self.ui.on_stop_clicked(self.ui.stop)
731 self.assert_volume_availability(enabled=False)881 self.assert_widget_availability(enabled=False)
732 self.ui.on_stopped()882 self.ui.on_stopped()
733 self.assert_volume_availability(enabled=False)883 self.assert_widget_availability(enabled=False)
734884
735 @skip_abstract_class885 @skip_abstract_class
736 def test_volume_close_emits_response_close(self):886 def test_volume_close_emits_response_close(self):
737 """Test volume close button emits RESPONSE_CLOSE when clicked."""887 """Test volume close button emits RESPONSE_CLOSE when clicked."""
738 self.response = None888
739 def record_response(value):889 def record_response(value):
740 """Record the response received."""890 """Record the response received."""
741 self.response = value891 self.response = value
892
742 self.patch(self.volume_dialog, 'response', record_response)893 self.patch(self.volume_dialog, 'response', record_response)
743894
744 volume_close = '%s_close' % self.name895 self.volume_close.clicked()
745 getattr(self.ui, volume_close).clicked()
746 self.assertEqual(gtk.RESPONSE_CLOSE, self.response,896 self.assertEqual(gtk.RESPONSE_CLOSE, self.response,
747 '%s should emit RESPONSE_CLOSE.' % volume_close)897 'volume close button should emit RESPONSE_CLOSE.')
748898
749 @skip_abstract_class899 @skip_abstract_class
750 def test_on_volume_clicked(self):900 def test_on_volume_clicked(self):
@@ -799,36 +949,25 @@
799 self.on_volume_clicked(self.volume)949 self.on_volume_clicked(self.volume)
800950
801 @skip_abstract_class951 @skip_abstract_class
802 def test_volume_dialog_props(self):952 def test_volume_dialog_properties(self):
803 """The volume dialog has correct properties."""953 """The volume dialog has correct properties."""
804 size = self.volume_dialog.size_request()954 title = self.name.replace('_', ' ').capitalize()
805 self.assertEquals((600, 300), size)955 self.assert_dialog_properties(dialog_name=self.volume_dialog_name,
806956 title=title)
807 self.assertTrue(self.volume_dialog.get_modal(),957
808 '%s must be modal.' % self.volume_dialog_name)
809
810 position = self.volume_dialog.get_property('window-position')
811 self.assertEqual(gtk.WIN_POS_CENTER, position,
812 '%s must be centered.' % self.volume_dialog_name)
813
814 actual = self.volume_dialog.get_title()
815 expected = self.name.replace('_', ' ').capitalize()
816 msg = '%s title must be %s (got %s instead)'
817 self.assertEqual(expected, actual,
818 msg % (self.volume_dialog_name, expected, actual))
819958
820class MagicicadaUIFoldersTestCase(_MagicicadaUIVolumeTestCase):959class MagicicadaUIFoldersTestCase(_MagicicadaUIVolumeTestCase):
821 """UI test cases for folders."""960 """UI test cases for folders."""
822961
823 name = 'folders'962 name = 'folders'
824 data_type = FolderData # node path suggested_path subscribed volume963 data_type = FolderData # node path suggested_path subscribed volume
825964
826965
827class _MagicicadaUISharesTestCase(_MagicicadaUIVolumeTestCase):966class _MagicicadaUISharesTestCase(_MagicicadaUIVolumeTestCase):
828 """UI test cases for shares_to_me."""967 """UI test cases for shares_to_me."""
829968
830 data_type = ShareData # accepted access_level free_bytes name node_id969 data_type = ShareData # accepted access_level free_bytes name node_id
831 # other_username other_visible_name path volume_id970 # other_username other_visible_name path volume_id
832971
833 @skip_abstract_class972 @skip_abstract_class
834 def test_bytes_are_humanized(self):973 def test_bytes_are_humanized(self):
@@ -862,3 +1001,279 @@
8621001
863 name = 'shares_to_others'1002 name = 'shares_to_others'
8641003
1004
1005class MagicicadaUIMetadataTestCase(MagicicadaUITestCase):
1006 """UI test cases for metadata display."""
1007
1008 name = 'raw_metadata'
1009
1010 def setUp(self):
1011 """Init."""
1012 super(MagicicadaUIMetadataTestCase, self).setUp()
1013 self.path = get_data_file('tests', 'metadata-test.txt')
1014 self.metadata = dict(bla='ble', foo='bar')
1015
1016 def assert_widget_availability(self, enabled=True):
1017 """Check button availability according to 'enabled'."""
1018 s = super(MagicicadaUIMetadataTestCase, self)
1019 s.assert_widget_availability(self.name, enabled)
1020
1021 def assert_dialog_visibility(self, dialog, text_view, image):
1022 """Check the visibility for dialog, text_view and image."""
1023 msg = '%s visibility should be %s (got %s instead).'
1024 visible = self.ui.widget_is_visible(self.ui.raw_metadata_dialog)
1025 self.assertEqual(dialog, visible,
1026 msg % ('raw_metadata_dialog', dialog, visible))
1027
1028 visible = self.ui.widget_is_visible(self.ui.raw_metadata_view)
1029 self.assertEqual(text_view, visible,
1030 msg % ('raw_metadata_view', text_view, visible))
1031
1032 visible = self.ui.widget_is_visible(self.ui.raw_metadata_image)
1033 self.assertEqual(image, visible,
1034 msg % ('raw_metadata_image', image, visible))
1035
1036 def test_raw_metadata_are_disabled_until_started(self):
1037 """Raw metadata button is disabled until online."""
1038 # disabled at startup
1039 self.assert_widget_availability(enabled=False)
1040
1041 # enabled when started
1042 self.do_start()
1043 self.assert_widget_availability(enabled=True)
1044
1045 def test_raw_metadata_are_enabled_until_stopped(self):
1046 """Raw metadata button is enabled until offline."""
1047 self.do_connect()
1048 self.assert_widget_availability(enabled=True)
1049
1050 self.ui.on_online()
1051 self.assert_widget_availability(enabled=True)
1052
1053 self.ui.on_offline()
1054 self.assert_widget_availability(enabled=True)
1055
1056 self.ui.on_disconnect_clicked(self.ui.disconnect)
1057 self.ui.on_disconnected()
1058 self.assert_widget_availability(enabled=True)
1059
1060 # disabled when stopped
1061 self.ui.on_stop_clicked(self.ui.stop)
1062 self.assert_widget_availability(enabled=False)
1063 self.ui.on_stopped()
1064 self.assert_widget_availability(enabled=False)
1065
1066 def test_raw_metadata_close_hides_the_dialog(self):
1067 """Test raw_metadata close button emits RESPONSE_CLOSE when clicked."""
1068 self.ui.raw_metadata_close.clicked()
1069 self.assertFalse(self.ui.widget_is_visible(
1070 self.ui.raw_metadata_dialog),
1071 'raw_metadata_dialog should not be visible.')
1072
1073 def test_file_chooser_open_emits_response_ok(self):
1074 """Test volume close button emits RESPONSE_CLOSE when clicked."""
1075
1076 def record_response(value):
1077 """Record the response received."""
1078 self.response = value
1079
1080 self.patch(self.ui.file_chooser, 'response', record_response)
1081
1082 self.ui.file_chooser_open.clicked()
1083 self.assertEqual(gtk.FILE_CHOOSER_ACTION_OPEN, self.response,
1084 'open button should emit FILE_CHOOSER_ACTION_OPEN.')
1085
1086 def test_on_raw_metadata_clicked(self):
1087 """Test on_raw_metadata_clicked."""
1088 self.assertFalse(self.ui.widget_is_visible(
1089 self.ui.raw_metadata_dialog),
1090 'raw_metadata_dialog should not be visible.')
1091
1092 self.ui.file_chooser.set_filename(self.path)
1093
1094 def test_file_chooser():
1095 """Auxiliar to assert over the file_chooser."""
1096 self.assertTrue(self.ui.widget_is_visible(self.ui.file_chooser),
1097 'file_chooser must be visible on metadata clicked.')
1098
1099 gobject.timeout_add(100, test_and_click,
1100 (self.ui.file_chooser_open, test_file_chooser))
1101 self.ui.on_raw_metadata_clicked(self.ui.raw_metadata)
1102
1103 self.assertFalse(self.ui.widget_is_visible(self.ui.file_chooser),
1104 'file_chooser must be visible after metadata clicked.')
1105 self.assertEqual(self.path, self.ui.file_chooser.get_filename(),
1106 'filename returned by file chooser must be correct.')
1107
1108 # raw_metadata_dialog is enabled and shows the loading animation
1109 self.assert_dialog_visibility(dialog=True, text_view=False, image=True)
1110 expected = self.ui.raw_metadata_image.get_animation()
1111 self.assertEqual(self.ui.loading_animation, expected,
1112 'raw_metadata_image must have the correct animation.')
1113
1114 # Check that the metadata was asked to the SD
1115 self.assertEqual(self.ui.sd._meta_path, self.path)
1116 # SD will eventually callback us with the metadata
1117 self.ui.on_metadata_ready(self.path, self.metadata)
1118
1119 # raw_metadata_dialog is enabled and shows the metadata
1120 self.assert_dialog_visibility(dialog=True, text_view=True, image=False)
1121
1122 # user closes the dialog
1123 self.ui.raw_metadata_close.clicked()
1124
1125 # dialog was closed already
1126 self.assertFalse(self.ui.widget_is_visible(
1127 self.ui.raw_metadata_dialog),
1128 'raw_metadata_dialog should not be visible.')
1129
1130 def test_raw_metadata_dialog_properties(self):
1131 """The raw_metadata dialog has correct properties."""
1132 title = self.name.replace('_', ' ').capitalize()
1133 self.assert_dialog_properties(dialog_name='raw_metadata_dialog',
1134 title=title, modal=False)
1135
1136 actual = self.ui.raw_metadata_view.get_wrap_mode()
1137 msg = 'wrap mode for view must be gtk.WRAP_WORD (got %s instead).'
1138 self.assertEqual(gtk.WRAP_WORD, actual, msg % actual)
1139
1140 def test_callback_is_connected(self):
1141 """Metadata ready callback is connected."""
1142 self.assertEqual(self.ui.sd.on_metadata_ready_callback,
1143 self.ui.on_metadata_ready,
1144 'on_metadata_ready_callback callback must be set.')
1145
1146 def test_file_chooser_is_hidden_at_startup(self):
1147 """File chooser exists but is not visible."""
1148 self.assertFalse(self.ui.widget_is_visible(self.ui.file_chooser),
1149 'file_chooser must be hidden by default.')
1150
1151 def test_file_chooser_current_folder_is_ubuntu_one_root(self):
1152 """File chooser default folder is ~/Ubuntu One."""
1153 process_gtk_pendings() # WOW! Needed to get proper value below
1154 actual = self.ui.file_chooser.get_current_folder()
1155 msg = 'file_chooser default folder must be %s (got %s instead).'
1156 self.assertEqual(actual, UBUNTU_ONE_ROOT,
1157 msg % (UBUNTU_ONE_ROOT, actual))
1158
1159 def test_filename_is_used_only_if_open_clicked(self):
1160 """Filename is used only if user clicked open."""
1161 self.patch(self.ui.sd, 'get_metadata', self.set_called)
1162 gobject.timeout_add(100, test_and_click,
1163 (self.ui.file_chooser_cancel, NO_OP))
1164 self.ui.on_raw_metadata_clicked(self.ui.raw_metadata)
1165
1166 self.assertFalse(self.called,
1167 'get_metadata should not be called if no file chosen.')
1168
1169 def test_filename_is_stored_if_open_clicked(self):
1170 """Filename is stored as 'last_metadata_path' if user clicked open."""
1171 self.assertTrue(self.ui.last_metadata_path is None,
1172 'last_metadata_path must be None.')
1173 self.ui.file_chooser.set_filename(self.path)
1174 gobject.timeout_add(100, test_and_click,
1175 (self.ui.file_chooser_open, NO_OP))
1176 self.ui.on_raw_metadata_clicked(self.ui.raw_metadata)
1177
1178 self.assertEqual(self.path, self.ui.last_metadata_path,
1179 'last_metadata_path should be what the user choose.')
1180
1181 def test_on_metadata_ready(self):
1182 """Callback on_metadata_ready updates the raw_metadata_view."""
1183 path = 'bla'
1184 self.ui.last_metadata_path = path
1185 self.ui.on_metadata_ready(path, self.metadata)
1186
1187 buff = self.ui.raw_metadata_view.get_buffer()
1188 self.assertTrue(buff is not None,
1189 'buffer for raw_metadata_view must not be None.')
1190
1191 expected = '\n'.join('%s: %s' % i for i in self.metadata.iteritems())
1192 actual = buff.get_text(*buff.get_bounds())
1193 msg = 'buffer content must be %s (got %s instead).'
1194 self.assertEqual(actual, expected,
1195 msg % (expected, actual))
1196
1197 def test_on_metadata_ready_doesnt_update_if_last_path_doesnt_match(self):
1198 """Callback on_metadata_ready updates the raw_metadata_view."""
1199 self.patch(self.ui.raw_metadata_view.get_buffer(),
1200 'set_text', self.set_called)
1201 path = 'bla'
1202 self.ui.last_metadata_path = path + path
1203 self.ui.on_metadata_ready(path, self.metadata)
1204
1205 self.assertFalse(self.called,
1206 'view should not be updated if key is not last one.')
1207
1208
1209def override_input_output(input_args, output_args):
1210 """Call 'f' but receive fixed input and return fixed output."""
1211
1212 def decorator(f):
1213 """The decorator per se."""
1214
1215 @wraps(f)
1216 def inner(*args, **kwargs):
1217 """Feed 'f' with 'input_args' and return 'output_args'."""
1218 f(input_args)
1219 return output_args
1220
1221 return inner
1222
1223 return decorator
1224
1225
1226class MagicicadaLoggingTestCase(MagicicadaUITestCase):
1227 """UI test cases for logging."""
1228
1229 def setUp(self):
1230 """Init."""
1231 super(MagicicadaLoggingTestCase, self).setUp()
1232
1233 self.memento = MementoHandler()
1234 self.memento.setLevel(logging.DEBUG)
1235 logger = logging.getLogger('magicicada.ui')
1236 logger.addHandler(self.memento)
1237
1238 def assert_function_logs(self, func, *args, **kwargs):
1239 """Check 'funcion' logs its inputs as DEBUG."""
1240 name = func.__name__
1241 msg = '%s must be logged as DEBUG'
1242 try:
1243 func(*args, **kwargs)
1244 except Exception: # pylint: disable-msg=E0501, W0703
1245 exc = sys.exc_info()
1246 self.assertTrue(self.memento.check_error(name),
1247 'function (%s) must be logged as ERROR' % name)
1248 self.assertTrue(self.memento.check_error(exc),
1249 'sys.exc_info (%s) must be logged as ERROR' % exc)
1250 self.assertTrue(self.memento.check_debug(name), msg % name)
1251 for arg in args:
1252 self.assertTrue(self.memento.check_debug(str(arg)), msg % arg)
1253 for key, val in kwargs.iteritems():
1254 arg = "'%s': %r" % (key, val)
1255 self.assertTrue(self.memento.check_debug(arg), msg % arg)
1256
1257 def test_on_shares_clicked_logs(self):
1258 """Check _on_shares_clicked logs properly."""
1259 args = ([0, object(), 'test', {}], object())
1260 kwargs = dict(dialog=object())
1261 self.assert_function_logs(self.ui._on_shares_clicked, *args, **kwargs)
1262
1263 def test_on_status_changed_logs(self):
1264 """Check _on_status_changed logs properly."""
1265 args = ('test status', 'status description', True, False, True)
1266 kwargs = dict(queues='bla', connection=None)
1267 self.assert_function_logs(self.ui.on_status_changed, *args, **kwargs)
1268
1269 def test_on_queue_changed_logs(self):
1270 """Check _on_queue_changed logs properly."""
1271 args = ('meta',)
1272 kwargs = dict(items=[0, object(), 'test', {}], must_highlight=True)
1273 self.assert_function_logs(self.ui._on_queue_changed, *args, **kwargs)
1274
1275 def test_on_metadata_ready_logs(self):
1276 """Check on_metadata_ready logs properly."""
1277 args = ()
1278 kwargs = dict(path='test', metadata=True)
1279 self.assert_function_logs(self.ui.on_metadata_ready, *args, **kwargs)
8651280
=== modified file 'magicicada/tests/test_syncdaemon.py'
--- magicicada/tests/test_syncdaemon.py 2010-06-08 10:53:02 +0000
+++ magicicada/tests/test_syncdaemon.py 2010-07-09 18:33:43 +0000
@@ -28,6 +28,10 @@
28from twisted.internet import defer, reactor28from twisted.internet import defer, reactor
2929
3030
31# It's ok to access private data in the test suite
32# pylint: disable-msg=W0212
33
34
31class FakeDBusInterface(object):35class FakeDBusInterface(object):
32 """Fake DBus Interface, for SD to not use dbus at all during tests."""36 """Fake DBus Interface, for SD to not use dbus at all during tests."""
3337
@@ -37,15 +41,18 @@
37 pass41 pass
3842
39 def shutdown(self):43 def shutdown(self):
44 """Fake shutdown."""
40 pass45 pass
4146
42 def get_status(self):47 def get_status(self):
43 """Fake status."""48 """Fake status."""
44 return defer.succeed(('fakename', 'fakedescrip', False, True,49 return defer.succeed(('fakename', 'fakedescrip', False, True,
45 False, 'fakequeues', 'fakeconnection'))50 False, 'fakequeues', 'fakeconnection'))
51
46 def get_folders(self):52 def get_folders(self):
47 """Fake folders."""53 """Fake folders."""
48 return defer.succeed('fakedata')54 return defer.succeed('fakedata')
55
49 get_content_queue = get_meta_queue = get_folders56 get_content_queue = get_meta_queue = get_folders
50 start = quit = connect = disconnect = get_folders57 start = quit = connect = disconnect = get_folders
51 get_shares_to_me = get_shares_to_others = get_folders58 get_shares_to_me = get_shares_to_others = get_folders
@@ -133,8 +140,9 @@
133140
134 @defer.inlineCallbacks141 @defer.inlineCallbacks
135 def test_initial_value(self):142 def test_initial_value(self):
136 """Fills the status info initially."""143 """Fill the status info initially."""
137 called = []144 called = []
145
138 def fake():146 def fake():
139 """Fake method."""147 """Fake method."""
140 called.append(True)148 called.append(True)
@@ -167,7 +175,7 @@
167 return deferred175 return deferred
168176
169 def test_status_changed_affects_cuurent_status(self):177 def test_status_changed_affects_cuurent_status(self):
170 """Makes changes to see how status are reflected."""178 """Make changes to see how status are reflected."""
171 # one set of values179 # one set of values
172 self.sd.on_sd_status_changed('name1', 'description1', False, True,180 self.sd.on_sd_status_changed('name1', 'description1', False, True,
173 False, 'queues1', 'connection1')181 False, 'queues1', 'connection1')
@@ -229,13 +237,42 @@
229 self.assertEqual(self.sd.current_state.queues, '')237 self.assertEqual(self.sd.current_state.queues, '')
230 self.assertEqual(self.sd.current_state.connection, '')238 self.assertEqual(self.sd.current_state.connection, '')
231239
240 def test_processing_meta_if_working_on_meta_or_both(self):
241 """Status.processing_meta is True when WORKING_ON_{METADATA,BOTH}."""
242
243 msg = 'processing_meta must be False when %s.'
244 for state in ('WORKING_ON_CONTENT', ''):
245 self.sd.current_state.set(queues=state)
246 self.assertFalse(self.sd.current_state.processing_meta,
247 msg % state)
248
249 msg = 'processing_meta must be True when %s.'
250 for state in ('WORKING_ON_METADATA', 'WORKING_ON_BOTH'):
251 self.sd.current_state.set(queues=state)
252 self.assertTrue(self.sd.current_state.processing_meta, msg % state)
253
254 def test_processing_content_if_working_on_content_or_both(self):
255 """Status.processing_content is True when WORKING_ON_{CONTENT,BOTH}."""
256
257 msg = 'processing_content must be False when %s.'
258 for state in ('WORKING_ON_METADATA', ''):
259 self.sd.current_state.set(queues=state)
260 self.assertFalse(self.sd.current_state.processing_content,
261 msg % state)
262
263 msg = 'processing_content must be True when %s.'
264 for state in ('WORKING_ON_CONTENT', 'WORKING_ON_BOTH'):
265 self.sd.current_state.set(queues=state)
266 self.assertTrue(self.sd.current_state.processing_content,
267 msg % state)
268
232269
233class ContentQueueChangedTests(BaseTest):270class ContentQueueChangedTests(BaseTest):
234 """Check the ContenQueueChanged handling."""271 """Check the ContenQueueChanged handling."""
235272
236 @defer.inlineCallbacks273 @defer.inlineCallbacks
237 def test_initial_value(self):274 def test_initial_value(self):
238 """Fills the content queue info initially."""275 """Fill the content queue info initially."""
239 called = []276 called = []
240 self.sd.dbus.get_content_queue = lambda: called.append(True)277 self.sd.dbus.get_content_queue = lambda: called.append(True)
241 yield self.sd._get_initial_data()278 yield self.sd._get_initial_data()
@@ -311,13 +348,19 @@
311 def setUp(self):348 def setUp(self):
312 """Set up."""349 """Set up."""
313 BaseTest.setUp(self)350 BaseTest.setUp(self)
314 self.sd.current_state._set(queues='WORKING_ON_METADATA')351 self.sd.current_state.set(queues='WORKING_ON_METADATA')
315352
316 @defer.inlineCallbacks353 @defer.inlineCallbacks
317 def test_initial_value(self):354 def test_initial_value(self):
318 """Fills the meta queue info initially."""355 """Fill the meta queue info initially."""
319 called = []356 called = []
320 self.sd.dbus.get_meta_queue = lambda: called.append(True)357
358 def f():
359 """Helper function."""
360 called.append(True)
361 return []
362
363 self.sd.dbus.get_meta_queue = f
321 yield self.sd._get_initial_data()364 yield self.sd._get_initial_data()
322 self.assertTrue(called)365 self.assertTrue(called)
323366
@@ -373,10 +416,12 @@
373 """Check that it polls mq while working in metadata not being in QM."""416 """Check that it polls mq while working in metadata not being in QM."""
374 # set the callback417 # set the callback
375 deferred = defer.Deferred()418 deferred = defer.Deferred()
419
376 def fake():420 def fake():
377 """Fake."""421 """Fake."""
378 deferred.callback(True)422 deferred.callback(True)
379 return defer.succeed("foo")423 return defer.succeed("foo")
424
380 self.sd.dbus.get_meta_queue = fake425 self.sd.dbus.get_meta_queue = fake
381426
382 # send status changed to working in metadata427 # send status changed to working in metadata
@@ -389,10 +434,12 @@
389 """Check that it polls mq while working in metadata being in QM."""434 """Check that it polls mq while working in metadata being in QM."""
390 # set the callback435 # set the callback
391 deferred = defer.Deferred()436 deferred = defer.Deferred()
437
392 def fake():438 def fake():
393 """Fake."""439 """Fake."""
394 deferred.callback(True)440 deferred.callback(True)
395 return defer.succeed("foo")441 return defer.succeed("foo")
442
396 self.sd.dbus.get_meta_queue = fake443 self.sd.dbus.get_meta_queue = fake
397444
398 # send status changed to working in metadata445 # send status changed to working in metadata
@@ -405,10 +452,12 @@
405 """Check that it polls mq while working in both."""452 """Check that it polls mq while working in both."""
406 # set the callback453 # set the callback
407 deferred = defer.Deferred()454 deferred = defer.Deferred()
455
408 def fake():456 def fake():
409 """Fake."""457 """Fake."""
410 deferred.callback(True)458 deferred.callback(True)
411 return defer.succeed("foo")459 return defer.succeed("foo")
460
412 self.sd.dbus.get_meta_queue = fake461 self.sd.dbus.get_meta_queue = fake
413462
414 # send status changed to working in metadata463 # send status changed to working in metadata
@@ -417,14 +466,47 @@
417 'connection')466 'connection')
418 return deferred467 return deferred
419468
469 def test_mq_polls_last_time(self):
470 """Was polling, state changed, it needs to poll a last time."""
471 # set the callback
472 deferred = defer.Deferred()
473 changed = self.sd.on_sd_status_changed
474 called = []
475
476 def fake():
477 """Fake."""
478 called.append(None)
479 if len(called) == 1:
480 changed('QUEUE_MANAGER', 'description', False, True, False,
481 'WORKING_ON_CONTENT', 'connection')
482 elif len(called) == 2:
483 # check that the caller is set back to None
484 self.assertTrue(self.sd._mqcaller is None)
485 deferred.callback(True)
486 return defer.succeed("foo")
487
488 self.sd.dbus.get_meta_queue = fake
489
490 # send status changed to working in metadata
491 changed('QUEUE_MANAGER', 'description', False, True, False,
492 'WORKING_ON_BOTH', 'connection')
493 return deferred
494
495 def test_mq_caller_is_reset_last_time(self):
496 """When MQ is polled last time, the caller should be back to None."""
497 self.sd._mqcaller = reactor.callLater(100, lambda: None)
498 self.sd.current_state.set(name='QUEUE_MANAGER',
499 queues='WORKING_ON_CONTENT')
500
501 # call the method and check
502 self.sd._check_mq()
503 self.assertTrue(self.sd._mqcaller is None)
504
420 def test_mq_polling_untilfinish(self):505 def test_mq_polling_untilfinish(self):
421 """Check that it polls mq until no more is needed."""506 """Check that it polls mq until no more is needed."""
422 d2 = dict(name='QUEUE_MANAGER', queues='WORKING_ON_CONTENT',
423 description='description', is_error='', is_connected='True',
424 is_online='', connection='conn')
425
426 # set the callback, and adjust the polling time to faster507 # set the callback, and adjust the polling time to faster
427 calls = []508 calls = []
509
428 def fake_get(*a):510 def fake_get(*a):
429 """Fake get."""511 """Fake get."""
430 calls.append(None)512 calls.append(None)
@@ -437,6 +519,8 @@
437519
438 # allow time to see if a mistaken call happens520 # allow time to see if a mistaken call happens
439 reactor.callLater(.5, deferred.callback, True)521 reactor.callLater(.5, deferred.callback, True)
522 elif len(calls) == 4:
523 pass # last call after state changed
440 else:524 else:
441 deferred.errback(ValueError("Too many calls"))525 deferred.errback(ValueError("Too many calls"))
442 return defer.succeed("foo")526 return defer.succeed("foo")
@@ -480,7 +564,7 @@
480 def test_set_one_value(self):564 def test_set_one_value(self):
481 """Set one value."""565 """Set one value."""
482 st = State()566 st = State()
483 st._set(name=55)567 st.set(name=55)
484568
485 # check the one is set, the rest not569 # check the one is set, the rest not
486 self.assertEqual(st.name, 55)570 self.assertEqual(st.name, 55)
@@ -489,7 +573,7 @@
489 def test_set_two_values(self):573 def test_set_two_values(self):
490 """Set two values."""574 """Set two values."""
491 st = State()575 st = State()
492 st._set(name=55, description=77)576 st.set(name=55, description=77)
493577
494 # check those two are set, the rest not578 # check those two are set, the rest not
495 self.assertEqual(st.name, 55)579 self.assertEqual(st.name, 55)
@@ -499,7 +583,7 @@
499 def test_bad_value(self):583 def test_bad_value(self):
500 """Set a value that should not."""584 """Set a value that should not."""
501 st = State()585 st = State()
502 self.assertRaises(AttributeError, st._set, not_really_allowed=44)586 self.assertRaises(AttributeError, st.set, not_really_allowed=44)
503587
504588
505class APITests(unittest.TestCase):589class APITests(unittest.TestCase):
@@ -617,7 +701,6 @@
617 self.assertTrue(self.called)701 self.assertTrue(self.called)
618702
619703
620
621class TestLogs(unittest.TestCase):704class TestLogs(unittest.TestCase):
622 """Test logging."""705 """Test logging."""
623706
@@ -634,100 +717,128 @@
634717
635 def test_instancing(self):718 def test_instancing(self):
636 """Just logged SD instancing."""719 """Just logged SD instancing."""
637 self.assertTrue(self.hdlr.check_inf("SyncDaemon interface started!"))720 self.assertTrue(self.hdlr.check_info("SyncDaemon interface started!"))
638721
639 def test_shutdown(self):722 def test_shutdown(self):
640 """Log when SD shutdowns."""723 """Log when SD shutdowns."""
641 self.sd.shutdown()724 self.sd.shutdown()
642 self.assertTrue(self.hdlr.check_inf("SyncDaemon interface going down"))725 msg = "SyncDaemon interface going down"
726 self.assertTrue(self.hdlr.check_info(msg))
643727
644 @defer.inlineCallbacks728 @defer.inlineCallbacks
645 def test_initial_value(self):729 def test_initial_value(self):
646 """Log the initial filling."""730 """Log the initial filling."""
647 yield self.sd._get_initial_data()731 yield self.sd._get_initial_data()
648 self.assertTrue(self.hdlr.check_inf("Getting initial data"))732 self.assertTrue(self.hdlr.check_info("Getting initial data"))
649733
650 def test_start(self):734 def test_start(self):
651 """Log the call to start."""735 """Log the call to start."""
652 self.sd.start()736 self.sd.start()
653 self.assertTrue(self.hdlr.check_inf("Starting u1.SD"))737 self.assertTrue(self.hdlr.check_info("Starting u1.SD"))
654738
655 def test_quit(self):739 def test_quit(self):
656 """Log the call to quit."""740 """Log the call to quit."""
657 self.sd.quit()741 self.sd.quit()
658 self.assertTrue(self.hdlr.check_inf("Stopping u1.SD"))742 self.assertTrue(self.hdlr.check_info("Stopping u1.SD"))
659743
660 def test_connect(self):744 def test_connect(self):
661 """Log the call to connect."""745 """Log the call to connect."""
662 self.sd.connect()746 self.sd.connect()
663 self.assertTrue(self.hdlr.check_inf("Telling u1.SD to connect"))747 self.assertTrue(self.hdlr.check_info("Telling u1.SD to connect"))
664748
665 def test_disconnect(self):749 def test_disconnect(self):
666 """Log the call to disconnect."""750 """Log the call to disconnect."""
667 self.sd.disconnect()751 self.sd.disconnect()
668 self.assertTrue(self.hdlr.check_inf("Telling u1.SD to disconnect"))752 self.assertTrue(self.hdlr.check_info("Telling u1.SD to disconnect"))
669753
670 def test_check_mq_true(self):754 def test_check_mq_true(self):
671 """Log the MQ check when it asks for info."""755 """Log the MQ check when it asks for info."""
672 self.sd.current_state._set(name='QUEUE_MANAGER',756 self.sd.current_state.set(name='QUEUE_MANAGER',
673 queues='WORKING_ON_METADATA')757 queues='WORKING_ON_METADATA')
674 self.sd._check_mq()758 self.sd._check_mq()
675 self.assertTrue(self.hdlr.check_inf("Asking for MQ information"))759 self.assertTrue(self.hdlr.check_info("Asking for MQ information"))
676760
677 def test_check_mq_noreally(self):761 def test_check_mq_noreally(self):
678 """Log the MQ check when it should not work."""762 """Log the MQ check when it should not work."""
679 self.sd.current_state._set(name='QUEUE_MANAGER',763 self.sd.current_state.set(name='QUEUE_MANAGER',
680 queues='WORKING_ON_CONTENT')764 queues='WORKING_ON_CONTENT')
681 self.sd._check_mq()765 self.sd._check_mq()
682 self.assertTrue(self.hdlr.check_inf(766 self.assertTrue(self.hdlr.check_info(
683 "Check MQ called but States not in MQ"))767 "Check MQ called, States not in MQ, call a last time"))
684768
685 def test_meta_queue_changed(self):769 def test_meta_queue_changed(self):
686 """Log that MQ has new data."""770 """Log that MQ has new data."""
687 self.sd.dbus.get_meta_queue = lambda: defer.succeed(['foo'])771 self.sd.dbus.get_meta_queue = lambda: defer.succeed(['foo'])
688 self.sd.current_state._set(name='QUEUE_MANAGER',772 self.sd.current_state.set(name='QUEUE_MANAGER',
689 queues='WORKING_ON_METADATA')773 queues='WORKING_ON_METADATA')
690 self.sd._check_mq()774 self.sd._check_mq()
691 self.assertTrue(self.hdlr.check_inf("SD Meta Queue changed: 1 items"))775 self.assertTrue(self.hdlr.check_info("SD Meta Queue changed: 1 items"))
692776
693 def test_content_queue_changed(self):777 def test_content_queue_changed(self):
694 """Log that process_cq has new data."""778 """Log that process_cq has new data."""
695 self.sd.dbus.get_content_queue = lambda: defer.succeed(['foo'])779 self.sd.dbus.get_content_queue = lambda: defer.succeed(['foo'])
696 self.sd.on_sd_content_queue_changed()780 self.sd.on_sd_content_queue_changed()
697 self.assertTrue(self.hdlr.check_inf("SD Content Queue changed"))781 self.assertTrue(self.hdlr.check_info("SD Content Queue changed"))
698 self.assertTrue(self.hdlr.check_inf(782 self.assertTrue(self.hdlr.check_info(
699 "Content Queue info is new! 1 items"))783 "Content Queue info is new! 1 items"))
700784
701 def test_on_status_changed(self):785 def test_on_status_changed(self):
702 """Log status changed."""786 """Log status changed."""
703 self.sd.on_sd_status_changed('name', 'description', False, True,787 self.sd.on_sd_status_changed('name', 'description', False, True,
704 False, 'queues', 'connection')788 False, 'queues', 'connection')
705 self.assertTrue(self.hdlr.check_inf("SD Status changed"))789 self.assertTrue(self.hdlr.check_info("SD Status changed"))
706 self.assertTrue(self.hdlr.check_dbg(" new status: name=u'name', "790 self.assertTrue(self.hdlr.check_debug(" new status: name=u'name', "
707 "description=u'description', is_error=False, is_connected=True, "791 "description=u'description', is_error=False, is_connected=True, "
708 "is_online=False, queues=u'queues', connection=u'connection'"))792 "is_online=False, queues=u'queues', connection=u'connection'"))
709793
710 def test_folders_changed(self):794 def test_folders_changed(self):
711 """Log when folders changed."""795 """Log when folders changed."""
712 self.sd.on_sd_folders_changed()796 self.sd.on_sd_folders_changed()
713 self.assertTrue(self.hdlr.check_inf("SD Folders changed"))797 self.assertTrue(self.hdlr.check_info("SD Folders changed"))
714798
715 def test_shares_changed(self):799 def test_shares_changed(self):
716 """Log when shares changed."""800 """Log when shares changed."""
717 self.sd.on_sd_shares_changed()801 self.sd.on_sd_shares_changed()
718 self.assertTrue(self.hdlr.check_inf("SD Shares changed"))802 self.assertTrue(self.hdlr.check_info("SD Shares changed"))
719803
720 def test_on_name_owner_changed(self):804 def test_on_name_owner_changed(self):
721 """Log name owner changed."""805 """Log name owner changed."""
722 self.sd.on_sd_name_owner_changed(True)806 self.sd.on_sd_name_owner_changed(True)
723 self.assertTrue(self.hdlr.check_inf("SD Name Owner changed: True"))807 self.assertTrue(self.hdlr.check_info("SD Name Owner changed: True"))
808
809
810class MetadataTests(BaseTest):
811 """Get Metadata info."""
812
813 def test_get_metadata_no_callback_set(self):
814 """It's mandatory to set the callback for this response."""
815 self.assertRaises(ValueError, self.sd.get_metadata, 'path')
816
817 def test_get_metadata_ok(self):
818 """Get the metadata for given path."""
819 called = []
820 self.sd.dbus.get_metadata = lambda p: defer.succeed('foo')
821 self.sd.on_metadata_ready_callback = lambda *a: called.extend(a)
822 self.sd.get_metadata('path')
823 self.assertEqual(called, ['path', 'foo'])
824
825 def test_get_metadata_double(self):
826 """Get the metadata twice."""
827 called = []
828 fake_md = {'path1': 'foo', 'path2': 'bar'}
829 self.sd.dbus.get_metadata = lambda p: defer.succeed(fake_md[p])
830 self.sd.on_metadata_ready_callback = lambda *a: called.append(a)
831 self.sd.get_metadata('path1')
832 self.sd.get_metadata('path2')
833 self.assertEqual(called[0], ('path1', 'foo'))
834 self.assertEqual(called[1], ('path2', 'bar'))
724835
725836
726class FoldersTests(BaseTest):837class FoldersTests(BaseTest):
727 """Folders checking."""838 """Folders checking."""
728839
729 def test_foldercreated_callback(self):840 def test_foldercreated_callback(self):
730 """Gets the new data after the folders changed."""841 """Get the new data after the folders changed."""
731 # set the callback842 # set the callback
732 called = []843 called = []
733 self.sd.dbus.get_folders = lambda: called.append(True)844 self.sd.dbus.get_folders = lambda: called.append(True)
@@ -740,7 +851,7 @@
740851
741 @defer.inlineCallbacks852 @defer.inlineCallbacks
742 def test_initial_value(self):853 def test_initial_value(self):
743 """Fills the folder info initially."""854 """Fill the folder info initially."""
744 called = []855 called = []
745 self.sd.dbus.get_folders = lambda: called.append(True)856 self.sd.dbus.get_folders = lambda: called.append(True)
746 yield self.sd._get_initial_data()857 yield self.sd._get_initial_data()
@@ -763,7 +874,7 @@
763 """Shares checking."""874 """Shares checking."""
764875
765 def test_shares_changed_callback(self):876 def test_shares_changed_callback(self):
766 """Gets the new data after the shares changed."""877 """Get the new data after the shares changed."""
767 # set the callback878 # set the callback
768 called = []879 called = []
769 self.sd.dbus.get_shares_to_me = lambda: called.append(True)880 self.sd.dbus.get_shares_to_me = lambda: called.append(True)
@@ -777,7 +888,7 @@
777888
778 @defer.inlineCallbacks889 @defer.inlineCallbacks
779 def test_initial_value(self):890 def test_initial_value(self):
780 """Fills the folder info initially."""891 """Fill the folder info initially."""
781 called = []892 called = []
782 self.sd.dbus.get_shares_to_me = lambda: called.append(True)893 self.sd.dbus.get_shares_to_me = lambda: called.append(True)
783 self.sd.dbus.get_shares_to_others = lambda: called.append(True)894 self.sd.dbus.get_shares_to_others = lambda: called.append(True)
@@ -793,7 +904,7 @@
793904
794 # they changed!905 # they changed!
795 self.sd.shares_to_me = 'foo'906 self.sd.shares_to_me = 'foo'
796 self.sd.shares_to_others = 'fakedata' # what fake dbus will return907 self.sd.shares_to_others = 'fakedata' # what fake dbus will return
797 self.sd.on_sd_shares_changed()908 self.sd.on_sd_shares_changed()
798909
799 # test910 # test
@@ -808,7 +919,7 @@
808919
809 # they changed!920 # they changed!
810 self.sd.shares_to_others = 'foo'921 self.sd.shares_to_others = 'foo'
811 self.sd.shares_to_me = 'fakedata' # what fake dbus will return922 self.sd.shares_to_me = 'fakedata' # what fake dbus will return
812 self.sd.on_sd_shares_changed()923 self.sd.on_sd_shares_changed()
813924
814 # test925 # test
815926
=== modified file 'setup.py'
--- setup.py 2010-06-08 10:53:02 +0000
+++ setup.py 2010-07-09 18:33:43 +0000
@@ -4,7 +4,9 @@
4# This file is in the public domain4# This file is in the public domain
5### END LICENSE5### END LICENSE
66
7###################### DO NOT TOUCH THIS (HEAD TO THE SECOND PART) ######################7"""Build tar.gz and related for magicicada."""
8
9################# DO NOT TOUCH THIS (HEAD TO THE SECOND PART) #################
810
9import os11import os
10import sys12import sys
@@ -12,24 +14,28 @@
12try:14try:
13 import DistUtilsExtra.auto15 import DistUtilsExtra.auto
14except ImportError:16except ImportError:
15 print >> sys.stderr, 'To build magicicada you need https://launchpad.net/python-distutils-extra'17 url = 'https://launchpad.net/python-distutils-extra'
18 print >> sys.stderr, 'To build magicicada you need', url
16 sys.exit(1)19 sys.exit(1)
17assert DistUtilsExtra.auto.__version__ >= '2.18', 'needs DistUtilsExtra.auto >= 2.18'20assert DistUtilsExtra.auto.__version__ >= '2.18', \
21 'needs DistUtilsExtra.auto >= 2.18'
22
1823
19def update_data_path(prefix, oldvalue=None):24def update_data_path(prefix, oldvalue=None):
25 """Update data path."""
2026
21 try:27 try:
22 fin = file('magicicada/magicicadaconfig.py', 'r')28 fin = file('magicicada/magicicadaconfig.py', 'r')
23 fout = file(fin.name + '.new', 'w')29 fout = file(fin.name + '.new', 'w')
2430
25 for line in fin: 31 for line in fin:
26 fields = line.split(' = ') # Separate variable from value32 fields = line.split(' = ') # Separate variable from value
27 if fields[0] == '__magicicada_data_directory__':33 if fields[0] == '__magicicada_data_directory__':
28 # update to prefix, store oldvalue34 # update to prefix, store oldvalue
29 if not oldvalue:35 if not oldvalue:
30 oldvalue = fields[1]36 oldvalue = fields[1]
31 line = "%s = '%s'\n" % (fields[0], prefix)37 line = "%s = '%s'\n" % (fields[0], prefix)
32 else: # restore oldvalue38 else: # restore oldvalue
33 line = "%s = %s" % (fields[0], oldvalue)39 line = "%s = %s" % (fields[0], oldvalue)
34 fout.write(line)40 fout.write(line)
3541
@@ -37,19 +43,20 @@
37 fout.close()43 fout.close()
38 fin.close()44 fin.close()
39 os.rename(fout.name, fin.name)45 os.rename(fout.name, fin.name)
40 except (OSError, IOError), e:46 except (OSError, IOError):
41 print ("ERROR: Can't find magicicada/magicicadaconfig.py")47 print ("ERROR: Can't find magicicada/magicicadaconfig.py")
42 sys.exit(1)48 sys.exit(1)
43 return oldvalue49 return oldvalue
4450
4551
46def update_desktop_file(datadir):52def update_desktop_file(datadir):
53 """Update desktop file."""
4754
48 try:55 try:
49 fin = file('magicicada.desktop.in', 'r')56 fin = file('magicicada.desktop.in', 'r')
50 fout = file(fin.name + '.new', 'w')57 fout = file(fin.name + '.new', 'w')
5158
52 for line in fin: 59 for line in fin:
53 if 'Icon=' in line:60 if 'Icon=' in line:
54 line = "Icon=%s\n" % (datadir + 'media/icon.png')61 line = "Icon=%s\n" % (datadir + 'media/icon.png')
55 fout.write(line)62 fout.write(line)
@@ -57,33 +64,33 @@
57 fout.close()64 fout.close()
58 fin.close()65 fin.close()
59 os.rename(fout.name, fin.name)66 os.rename(fout.name, fin.name)
60 except (OSError, IOError), e:67 except (OSError, IOError):
61 print ("ERROR: Can't find magicicada.desktop.in")68 print ("ERROR: Can't find magicicada.desktop.in")
62 sys.exit(1)69 sys.exit(1)
6370
6471
65class InstallAndUpdateDataDirectory(DistUtilsExtra.auto.install_auto):72class InstallAndUpdateDataDirectory(DistUtilsExtra.auto.install_auto):
73 """Install and update data dir."""
74
66 def run(self):75 def run(self):
76 """Run."""
67 previous_value = update_data_path(self.prefix + '/share/magicicada/')77 previous_value = update_data_path(self.prefix + '/share/magicicada/')
68 update_desktop_file(self.prefix + '/share/magicicada/')78 update_desktop_file(self.prefix + '/share/magicicada/')
69 DistUtilsExtra.auto.install_auto.run(self)79 DistUtilsExtra.auto.install_auto.run(self)
70 update_data_path(self.prefix, previous_value)80 update_data_path(self.prefix, previous_value)
7181
7282
73 83##############################################################################
74##################################################################################84#################### YOU SHOULD MODIFY ONLY WHAT IS BELOW ####################
75###################### YOU SHOULD MODIFY ONLY WHAT IS BELOW ######################85##############################################################################
76##################################################################################
7786
78DistUtilsExtra.auto.setup(87DistUtilsExtra.auto.setup(
79 name='magicicada',88 name='magicicada',
80 version='0.1.1',89 version='0.1.2',
81 license='GPL-3',90 license='GPL-3',
82 author='Natalia Bidart',91 author='Natalia Bidart',
83 author_email='natalia.bidart@ubuntu.com',92 author_email='natalia.bidart@ubuntu.com',
84 description='A GTK+ frontend for the "Chicharra" part of Ubuntu One client.',93 description='A GTK+ frontend for the "Chicharra" part of Ubuntu One.',
85 #long_description='Here a longer description',94 #long_description='Here a longer description',
86 url='https://launchpad.net/magicicada',95 url='https://launchpad.net/magicicada',
87 cmdclass={'install': InstallAndUpdateDataDirectory}96 cmdclass={'install': InstallAndUpdateDataDirectory})
88 )
89

Subscribers

People subscribed via source and target branches

to all changes: