Merge lp:~nataliabidart/magicicada-gui/get-metadata-ui into lp:magicicada-gui
- get-metadata-ui
- Merge into trunk
Proposed by
Natalia Bidart
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Facundo Batista | ||||
Approved revision: | 49 | ||||
Merged at revision: | 48 | ||||
Proposed branch: | lp:~nataliabidart/magicicada-gui/get-metadata-ui | ||||
Merge into: | lp:magicicada-gui | ||||
Diff against target: |
771 lines (+436/-50) 3 files modified
data/ui/gui.glade (+127/-0) magicicada/__init__.py (+39/-2) magicicada/tests/test_magicicada.py (+270/-48) |
||||
To merge this branch: | bzr merge lp:~nataliabidart/magicicada-gui/get-metadata-ui | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Facundo Batista | Approve | ||
Review via email: mp+27462@code.launchpad.net |
Commit message
Description of the change
Get raw metadata functionality for the GUI.
Depends on lp:~facundo/magicicada/show-md for running the binary in real life, not for tests (since those use a faked syncdaemon).
This branch may be tweaked if we decide that the "code" for the SD will be the same as the path to which metadata is requested.
To post a comment you must log in.
Revision history for this message
Natalia Bidart (nataliabidart) wrote : | # |
I opened a bug for the requested behavior: https:/
- 50. By Natalia Bidart
-
API no longuer uses a dedicated code gor getting metadata but the path.
- 51. By Natalia Bidart
-
Stringifying the metadata dict.
- 52. By Natalia Bidart
-
Merged trunk in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory 'data/tests' |
2 | === added file 'data/tests/metadata-test.txt' |
3 | === modified file 'data/ui/gui.glade' |
4 | --- data/ui/gui.glade 2010-06-05 19:46:04 +0000 |
5 | +++ data/ui/gui.glade 2010-06-14 23:37:28 +0000 |
6 | @@ -257,6 +257,7 @@ |
7 | </child> |
8 | <child> |
9 | <object class="GtkToolButton" id="raw_metadata"> |
10 | + <property name="visible">True</property> |
11 | <property name="sensitive">False</property> |
12 | <property name="label" translatable="yes">Metadata</property> |
13 | <property name="use_underline">True</property> |
14 | @@ -1051,4 +1052,130 @@ |
15 | <action-widget response="0">shares_to_others_close</action-widget> |
16 | </action-widgets> |
17 | </object> |
18 | + <object class="GtkDialog" id="raw_metadata_dialog"> |
19 | + <property name="width_request">600</property> |
20 | + <property name="height_request">300</property> |
21 | + <property name="border_width">5</property> |
22 | + <property name="title" translatable="yes">Raw metadata</property> |
23 | + <property name="window_position">center</property> |
24 | + <property name="type_hint">normal</property> |
25 | + <property name="has_separator">False</property> |
26 | + <signal name="close" handler="on_raw_metadata_close_clicked"/> |
27 | + <child internal-child="vbox"> |
28 | + <object class="GtkVBox" id="dialog-vbox9"> |
29 | + <property name="visible">True</property> |
30 | + <property name="spacing">2</property> |
31 | + <child> |
32 | + <object class="GtkTextView" id="raw_metadata_view"> |
33 | + <property name="visible">True</property> |
34 | + <property name="can_focus">True</property> |
35 | + <property name="editable">False</property> |
36 | + <property name="wrap_mode">word</property> |
37 | + </object> |
38 | + <packing> |
39 | + <property name="position">1</property> |
40 | + </packing> |
41 | + </child> |
42 | + <child> |
43 | + <object class="GtkImage" id="raw_metadata_image"> |
44 | + <property name="visible">True</property> |
45 | + <property name="stock">gtk-missing-image</property> |
46 | + </object> |
47 | + <packing> |
48 | + <property name="position">2</property> |
49 | + </packing> |
50 | + </child> |
51 | + <child internal-child="action_area"> |
52 | + <object class="GtkHButtonBox" id="dialog-action_area9"> |
53 | + <property name="visible">True</property> |
54 | + <property name="layout_style">end</property> |
55 | + <child> |
56 | + <object class="GtkButton" id="raw_metadata_close"> |
57 | + <property name="label">gtk-close</property> |
58 | + <property name="visible">True</property> |
59 | + <property name="can_focus">True</property> |
60 | + <property name="receives_default">True</property> |
61 | + <property name="use_stock">True</property> |
62 | + <signal name="clicked" handler="on_raw_metadata_close_clicked"/> |
63 | + <signal name="activate" handler="on_raw_metadata_close_clicked"/> |
64 | + </object> |
65 | + <packing> |
66 | + <property name="expand">False</property> |
67 | + <property name="fill">False</property> |
68 | + <property name="position">0</property> |
69 | + </packing> |
70 | + </child> |
71 | + </object> |
72 | + <packing> |
73 | + <property name="expand">False</property> |
74 | + <property name="pack_type">end</property> |
75 | + <property name="position">0</property> |
76 | + </packing> |
77 | + </child> |
78 | + </object> |
79 | + </child> |
80 | + <action-widgets> |
81 | + <action-widget response="0">raw_metadata_close</action-widget> |
82 | + </action-widgets> |
83 | + </object> |
84 | + <object class="GtkFileChooserDialog" id="file_chooser"> |
85 | + <property name="border_width">5</property> |
86 | + <property name="type_hint">normal</property> |
87 | + <property name="has_separator">False</property> |
88 | + <signal name="file_activated" handler="on_file_chooser_open_clicked"/> |
89 | + <child internal-child="vbox"> |
90 | + <object class="GtkVBox" id="dialog-vbox7"> |
91 | + <property name="visible">True</property> |
92 | + <property name="spacing">2</property> |
93 | + <child> |
94 | + <placeholder/> |
95 | + </child> |
96 | + <child internal-child="action_area"> |
97 | + <object class="GtkHButtonBox" id="dialog-action_area7"> |
98 | + <property name="visible">True</property> |
99 | + <property name="layout_style">end</property> |
100 | + <child> |
101 | + <object class="GtkButton" id="file_chooser_cancel"> |
102 | + <property name="label">gtk-cancel</property> |
103 | + <property name="visible">True</property> |
104 | + <property name="can_focus">True</property> |
105 | + <property name="receives_default">True</property> |
106 | + <property name="use_stock">True</property> |
107 | + </object> |
108 | + <packing> |
109 | + <property name="expand">False</property> |
110 | + <property name="fill">False</property> |
111 | + <property name="position">0</property> |
112 | + </packing> |
113 | + </child> |
114 | + <child> |
115 | + <object class="GtkButton" id="file_chooser_open"> |
116 | + <property name="label">gtk-open</property> |
117 | + <property name="visible">True</property> |
118 | + <property name="can_focus">True</property> |
119 | + <property name="receives_default">True</property> |
120 | + <property name="use_stock">True</property> |
121 | + <signal name="clicked" handler="on_file_chooser_open_clicked"/> |
122 | + <signal name="activate" handler="on_file_chooser_open_clicked"/> |
123 | + </object> |
124 | + <packing> |
125 | + <property name="expand">False</property> |
126 | + <property name="fill">False</property> |
127 | + <property name="position">1</property> |
128 | + </packing> |
129 | + </child> |
130 | + </object> |
131 | + <packing> |
132 | + <property name="expand">False</property> |
133 | + <property name="pack_type">end</property> |
134 | + <property name="position">0</property> |
135 | + </packing> |
136 | + </child> |
137 | + </object> |
138 | + </child> |
139 | + <action-widgets> |
140 | + <action-widget response="-6">file_chooser_cancel</action-widget> |
141 | + <action-widget response="0">file_chooser_open</action-widget> |
142 | + </action-widgets> |
143 | + </object> |
144 | </interface> |
145 | |
146 | === modified file 'magicicada/__init__.py' |
147 | --- magicicada/__init__.py 2010-06-08 21:14:28 +0000 |
148 | +++ magicicada/__init__.py 2010-06-14 23:37:28 +0000 |
149 | @@ -68,19 +68,26 @@ |
150 | |
151 | widgets = ( |
152 | 'start', 'stop', 'connect', 'disconnect', # toolbar buttons |
153 | + |
154 | 'folders', 'folders_dialog', # folders |
155 | 'folders_store', 'folders_close', |
156 | + |
157 | 'shares_to_me', 'shares_to_me_dialog', # shares_to_me |
158 | 'shares_to_me_store', 'shares_to_me_close', |
159 | + |
160 | 'shares_to_others', 'shares_to_others_dialog', # shares_to_others |
161 | 'shares_to_others_store', 'shares_to_others_close', |
162 | + |
163 | + 'raw_metadata', 'raw_metadata_dialog', # raw metadata |
164 | + 'raw_metadata_close', 'raw_metadata_view', 'raw_metadata_image', |
165 | + |
166 | 'is_started', 'is_connected', 'is_online', # status bar images |
167 | 'status_label', 'status_icon', # status label and systray icon |
168 | 'metaq_view', 'contentq_view', # queues tree views |
169 | 'metaq_store', 'contentq_store', # queues list stores |
170 | 'metaq_label', 'contentq_label', # queues labels |
171 | - 'raw_metadata', # raw metadata |
172 | 'about_dialog', # dialogs |
173 | + 'file_chooser', 'file_chooser_open', 'file_chooser_cancel', |
174 | 'main_window' |
175 | ) |
176 | for widget in widgets: |
177 | @@ -111,11 +118,12 @@ |
178 | self.sd.status_changed_callback = self.on_status_changed |
179 | self.sd.content_queue_changed_callback = self.on_content_queue_changed |
180 | self.sd.meta_queue_changed_callback = self.on_meta_queue_changed |
181 | + self.sd.on_metadata_ready_callback = self.on_metadata_ready |
182 | |
183 | self.widget_is_visible = lambda w: w.get_property('visible') |
184 | self.widget_enabled = lambda w: self.widget_is_visible(w) and \ |
185 | w.is_sensitive() |
186 | - |
187 | + self.last_metadata_path = None |
188 | self.update() |
189 | |
190 | # GTK callbacks |
191 | @@ -145,6 +153,7 @@ |
192 | """Stop syncdaemon.""" |
193 | for v in self.volumes: |
194 | v.set_sensitive(False) |
195 | + self.raw_metadata.set_sensitive(False) |
196 | |
197 | if self.widget_enabled(self.disconnect): |
198 | self.on_disconnect_clicked(self.disconnect) |
199 | @@ -221,8 +230,27 @@ |
200 | self.shares_to_others_store, |
201 | self.shares_to_others_dialog) |
202 | |
203 | + def on_file_chooser_open_clicked(self, widget, data=None): |
204 | + """Close the file_chooser dialog.""" |
205 | + self.file_chooser.response(gtk.FILE_CHOOSER_ACTION_OPEN) |
206 | + |
207 | + def on_raw_metadata_close_clicked(self, widget, data=None): |
208 | + """Close the raw_metadata dialog.""" |
209 | + self.raw_metadata_dialog.hide() |
210 | + |
211 | def on_raw_metadata_clicked(self, widget, data=None): |
212 | """Show raw metadata for a path choosen by the user.""" |
213 | + res = self.file_chooser.run() |
214 | + self.file_chooser.hide() |
215 | + if res != gtk.FILE_CHOOSER_ACTION_OPEN: |
216 | + return |
217 | + |
218 | + self.last_metadata_path = self.file_chooser.get_filename() |
219 | + self.sd.get_metadata(path=self.last_metadata_path) |
220 | + self.raw_metadata_view.hide() |
221 | + self.raw_metadata_image.show() |
222 | + self._start_loading(self.raw_metadata_image) |
223 | + self.raw_metadata_dialog.show() |
224 | |
225 | def on_status_icon_activate(self, widget, data=None): |
226 | """Systray icon was clicked.""" |
227 | @@ -243,6 +271,7 @@ |
228 | |
229 | for v in self.volumes: |
230 | v.set_sensitive(True) |
231 | + self.raw_metadata.set_sensitive(True) |
232 | |
233 | self._update_queues_and_status(self.sd.current_state) |
234 | |
235 | @@ -332,6 +361,14 @@ |
236 | highlight = state.processing_meta and state.is_online |
237 | self._on_queue_changed(META_QUEUE, items, highlight) |
238 | |
239 | + def on_metadata_ready(self, path, metadata): |
240 | + """Lower layer has the requested metadata for 'path'.""" |
241 | + if path != self.last_metadata_path: |
242 | + return |
243 | + self.raw_metadata_image.hide() |
244 | + self.raw_metadata_view.show() |
245 | + self.raw_metadata_view.get_buffer().set_text(str(metadata)) |
246 | + |
247 | # custom |
248 | |
249 | def _start_loading(self, what): |
250 | |
251 | === modified file 'magicicada/tests/test_magicicada.py' |
252 | --- magicicada/tests/test_magicicada.py 2010-06-08 21:14:28 +0000 |
253 | +++ magicicada/tests/test_magicicada.py 2010-06-14 23:37:28 +0000 |
254 | @@ -28,20 +28,28 @@ |
255 | |
256 | from magicicada import MagicicadaUI, CONTENT_QUEUE, META_QUEUE, syncdaemon |
257 | from magicicada.dbusiface import QueueData, FolderData, ShareData |
258 | -from magicicada.helpers import NO_OP, humanize_bytes |
259 | +from magicicada.helpers import NO_OP, humanize_bytes, get_data_file |
260 | |
261 | def process_gtk_pendings(): |
262 | while gtk.events_pending(): gtk.main_iteration() |
263 | |
264 | def close_dialog((dialog, test)): |
265 | """Call the 'test', close 'dialog'.""" |
266 | - try: |
267 | - process_gtk_pendings() |
268 | - test() |
269 | - process_gtk_pendings() |
270 | - finally: |
271 | - dialog.response(gtk.RESPONSE_CLOSE) |
272 | - process_gtk_pendings() |
273 | + response = gtk.RESPONSE_CLOSE |
274 | + try: |
275 | + process_gtk_pendings() |
276 | + test() |
277 | + finally: |
278 | + dialog.response(response) |
279 | + process_gtk_pendings() |
280 | + return False # do not be called again |
281 | + |
282 | +def test_and_click((button, test)): |
283 | + """Call the 'test', and click 'button'.""" |
284 | + try: |
285 | + test() |
286 | + finally: |
287 | + button.clicked() |
288 | return False # do not be called again |
289 | |
290 | |
291 | @@ -49,6 +57,8 @@ |
292 | """A faked syncdaemon.""" |
293 | |
294 | def __init__(self): |
295 | + self._meta_path = None |
296 | + |
297 | self.current_state = syncdaemon.State() |
298 | self.meta_queue = [] |
299 | self.content_queue = [] |
300 | @@ -64,13 +74,15 @@ |
301 | self.status_changed_callback = NO_OP |
302 | self.content_queue_changed_callback = NO_OP |
303 | self.meta_queue_changed_callback = NO_OP |
304 | + self.on_metadata_ready_callback = None # mandatory |
305 | self.shutdown = NO_OP |
306 | |
307 | self.start = lambda: setattr(self.current_state, 'is_started', True) |
308 | self.quit = lambda: setattr(self.current_state, 'is_started', False) |
309 | self.connect = lambda: setattr(self.current_state, 'is_connected', True) |
310 | - self.disconnect = \ |
311 | - lambda: setattr(self.current_state, 'is_connected', False) |
312 | + self.disconnect = lambda: \ |
313 | + setattr(self.current_state, 'is_connected', False) |
314 | + self.get_metadata = lambda path: setattr(self, '_meta_path', path) |
315 | |
316 | |
317 | class MagicicadaUITestCase(TestCase): |
318 | @@ -79,13 +91,13 @@ |
319 | def setUp(self): |
320 | """Init.""" |
321 | self.ui = MagicicadaUI(syncdaemon_class=FakedSyncdaemon) |
322 | - self._called = False |
323 | - self.set_called = lambda *args, **kwargs: setattr(self, '_called', True) |
324 | + self.called = False |
325 | + self.set_called = lambda *args, **kwargs: setattr(self, 'called', True) |
326 | |
327 | def tearDown(self): |
328 | """Cleanup.""" |
329 | self.ui.on_main_window_destroy(self.ui.main_window) |
330 | - self._called = False |
331 | + self.called = False |
332 | |
333 | def do_start(self): |
334 | """Simulate that start fully happened.""" |
335 | @@ -134,21 +146,34 @@ |
336 | |
337 | def assert_indicator_disabled(self, indicator): |
338 | """Test that 'indicator' is not sensitive.""" |
339 | - self.assertFalse(indicator.is_sensitive(), 'indicator is not sensitive') |
340 | + self.assertFalse(indicator.is_sensitive(), |
341 | + 'indicator must not be sensitive.') |
342 | |
343 | def assert_indicator_ready(self, indicator): |
344 | """Test that 'indicator' is sensitive and green.""" |
345 | - self.assertTrue(indicator.is_sensitive(), 'indicator is sensitive') |
346 | + self.assertTrue(indicator.is_sensitive(), |
347 | + 'indicator must be sensitive.') |
348 | expected = indicator.get_pixbuf() # a test on its own |
349 | self.assertEqual(self.ui.active_indicator, expected, |
350 | - 'indicator is the correct pixbuf') |
351 | + 'indicator must have the correct pixbuf.') |
352 | |
353 | def assert_indicator_loading(self, indicator): |
354 | """Test that 'indicator' is sensitive and loading.""" |
355 | - self.assertTrue(indicator.is_sensitive(), 'indicator is sensitive') |
356 | + self.assertTrue(indicator.is_sensitive(), |
357 | + 'indicator must be sensitive.') |
358 | expected = indicator.get_animation() # a test on its own |
359 | self.assertEqual(self.ui.loading_animation, expected, |
360 | - 'indicator is the correct animation') |
361 | + 'indicator must have the correct animation.') |
362 | + |
363 | + def assert_widget_availability(self, widget_name, enabled=True): |
364 | + """Check button availability according to 'enabled'.""" |
365 | + widget = getattr(self.ui, widget_name) |
366 | + self.assertTrue(self.ui.widget_is_visible(widget), |
367 | + '%s should be visible' % widget_name) |
368 | + sensitive = widget.is_sensitive() |
369 | + msg = '%s should %sbe sensitive' |
370 | + self.assertTrue(sensitive if enabled else not sensitive, |
371 | + msg % (widget_name, '' if enabled else 'not ')) |
372 | |
373 | |
374 | class MagicicadaUIBasicTestCase(MagicicadaUITestCase): |
375 | @@ -163,7 +188,7 @@ |
376 | """SyncDaemon instance is shutdown at destroy time.""" |
377 | self.patch(self.ui.sd, 'shutdown', self.set_called) |
378 | self.ui.on_main_window_destroy(self.ui.main_window) |
379 | - self.assertTrue(self._called, |
380 | + self.assertTrue(self.called, |
381 | 'syncdaemon.shutdown must be called at destroy time.') |
382 | |
383 | def test_main_window_is_visible(self): |
384 | @@ -198,7 +223,7 @@ |
385 | """Update is called at startup.""" |
386 | self.patch(MagicicadaUI, 'update', self.set_called) |
387 | self.ui = MagicicadaUI(syncdaemon_class=FakedSyncdaemon) |
388 | - self.assertTrue(self._called, |
389 | + self.assertTrue(self.called, |
390 | 'update was called at startup.') |
391 | |
392 | |
393 | @@ -221,7 +246,7 @@ |
394 | """Test on_start_clicked.""" |
395 | self.patch(self.ui.sd, 'start', self.set_called) |
396 | self.ui.on_start_clicked(self.ui.start) |
397 | - self.assertTrue(self._called, 'syncdaemon.start was called.') |
398 | + self.assertTrue(self.called, 'syncdaemon.start was called.') |
399 | |
400 | def test_on_connect_clicked(self): |
401 | """Test on_connect_clicked.""" |
402 | @@ -240,7 +265,7 @@ |
403 | """Test on_connect_clicked.""" |
404 | self.patch(self.ui.sd, 'connect', self.set_called) |
405 | self.ui.on_connect_clicked(self.ui.connect) |
406 | - self.assertTrue(self._called, 'syncdaemon.connect was called.') |
407 | + self.assertTrue(self.called, 'syncdaemon.connect was called.') |
408 | |
409 | def test_on_stop_clicked(self): |
410 | """Test on_stop_clicked.""" |
411 | @@ -249,7 +274,7 @@ |
412 | self.patch(self.ui, 'on_disconnect_clicked', self.set_called) |
413 | self.ui.on_stop_clicked(self.ui.stop) |
414 | |
415 | - self.assertFalse(self._called, 'on_disconnect_clicked was not called.') |
416 | + self.assertFalse(self.called, 'on_disconnect_clicked was not called.') |
417 | |
418 | self.assertFalse(self.ui.widget_is_visible(self.ui.start)) |
419 | self.assertTrue(self.ui.widget_is_visible(self.ui.stop)) |
420 | @@ -265,13 +290,13 @@ |
421 | self.patch(self.ui, 'on_disconnect_clicked', self.set_called) |
422 | self.ui.on_stop_clicked(self.ui.stop) |
423 | |
424 | - self.assertTrue(self._called, 'on_disconnect_clicked was called.') |
425 | + self.assertTrue(self.called, 'on_disconnect_clicked was called.') |
426 | |
427 | def test_on_stop_clicked_stops_syncdaemon(self): |
428 | """Test on_stop_clicked.""" |
429 | self.patch(self.ui.sd, 'quit', self.set_called) |
430 | self.ui.on_stop_clicked(self.ui.stop) |
431 | - self.assertTrue(self._called, 'syncdaemon.quit was called.') |
432 | + self.assertTrue(self.called, 'syncdaemon.quit was called.') |
433 | |
434 | def test_on_disconnect_clicked(self): |
435 | """Test on_disconnect_clicked.""" |
436 | @@ -286,7 +311,7 @@ |
437 | """Test on_disconnect_clicked.""" |
438 | self.patch(self.ui.sd, 'disconnect', self.set_called) |
439 | self.ui.on_disconnect_clicked(self.ui.disconnect) |
440 | - self.assertTrue(self._called, 'syncdaemon.disconnect was called.') |
441 | + self.assertTrue(self.called, 'syncdaemon.disconnect was called.') |
442 | |
443 | |
444 | class MagicicadaUISystrayIconTestCase(MagicicadaUITestCase): |
445 | @@ -431,7 +456,7 @@ |
446 | cb = 'on_%s_queue_changed' % self.name |
447 | self.patch(self.ui, cb, self.set_called) |
448 | self.ui.on_stopped() |
449 | - self.assertTrue(self._called, |
450 | + self.assertTrue(self.called, |
451 | '%s was called on_stopped.' % cb) |
452 | |
453 | @skip_abstract_class |
454 | @@ -560,7 +585,7 @@ |
455 | """Status callback is connected.""" |
456 | self.assertEqual(self.ui.sd.status_changed_callback, |
457 | self.ui.on_status_changed, |
458 | - 'status_changed callback must be set') |
459 | + 'status_changed callback must be set.') |
460 | |
461 | def test_status_label_ellipsizes(self): |
462 | """The status label ellipsizes.""" |
463 | @@ -600,7 +625,7 @@ |
464 | """On SD stoppped, the UI updates the status label.""" |
465 | self.patch(self.ui, 'on_status_changed', self.set_called) |
466 | self.ui.on_stopped() |
467 | - self.assertTrue(self._called, |
468 | + self.assertTrue(self.called, |
469 | 'on_status_changed was called on_stopped.') |
470 | |
471 | def test_status_label_default_if_not_started(self): |
472 | @@ -767,6 +792,7 @@ |
473 | self.volume_dialog_name = '%s_dialog' % self.name |
474 | self.volume_dialog = getattr(self.ui, self.volume_dialog_name) |
475 | self.on_volume_clicked = getattr(self.ui, 'on_%s_clicked' % self.name) |
476 | + self.volume_close = getattr(self.ui, '%s_close' % self.name) |
477 | |
478 | def build_some_data(self, limit=5): |
479 | """Build some data to act as volume.""" |
480 | @@ -774,46 +800,42 @@ |
481 | res = super(_MagicicadaUIVolumeTestCase, self).build_some_data(**kwargs) |
482 | return res |
483 | |
484 | - def assert_volume_availability(self, enabled): |
485 | + def assert_widget_availability(self, enabled=True): |
486 | """Check volume availability according to 'enabled'.""" |
487 | - self.assertTrue(self.ui.widget_is_visible(self.volume), |
488 | - '%s should be visible' % self.name) |
489 | - sensitive = self.volume.is_sensitive() |
490 | - msg = '%s should %sbe sensitive' |
491 | - self.assertTrue(sensitive if enabled else not sensitive, |
492 | - msg % (self.name, '' if enabled else 'not ')) |
493 | + s = super(_MagicicadaUIVolumeTestCase, self) |
494 | + s.assert_widget_availability(self.name, enabled) |
495 | |
496 | @skip_abstract_class |
497 | def test_volume_are_disabled_until_started(self): |
498 | """Folders and shares are disabled until online.""" |
499 | # disabled at startup |
500 | - self.assert_volume_availability(enabled=False) |
501 | + self.assert_widget_availability(enabled=False) |
502 | |
503 | # enabled when started |
504 | self.do_start() |
505 | - self.assert_volume_availability(enabled=True) |
506 | + self.assert_widget_availability(enabled=True) |
507 | |
508 | @skip_abstract_class |
509 | def test_volume_are_enabled_until_stopped(self): |
510 | """Folders and shares are enabled until offline.""" |
511 | self.do_connect() |
512 | - self.assert_volume_availability(enabled=True) |
513 | + self.assert_widget_availability(enabled=True) |
514 | |
515 | self.ui.on_online() |
516 | - self.assert_volume_availability(enabled=True) |
517 | + self.assert_widget_availability(enabled=True) |
518 | |
519 | self.ui.on_offline() |
520 | - self.assert_volume_availability(enabled=True) |
521 | + self.assert_widget_availability(enabled=True) |
522 | |
523 | self.ui.on_disconnect_clicked(self.ui.disconnect) |
524 | self.ui.on_disconnected() |
525 | - self.assert_volume_availability(enabled=True) |
526 | + self.assert_widget_availability(enabled=True) |
527 | |
528 | # disabled when stopped |
529 | self.ui.on_stop_clicked(self.ui.stop) |
530 | - self.assert_volume_availability(enabled=False) |
531 | + self.assert_widget_availability(enabled=False) |
532 | self.ui.on_stopped() |
533 | - self.assert_volume_availability(enabled=False) |
534 | + self.assert_widget_availability(enabled=False) |
535 | |
536 | @skip_abstract_class |
537 | def test_volume_close_emits_response_close(self): |
538 | @@ -824,10 +846,9 @@ |
539 | self.response = value |
540 | self.patch(self.volume_dialog, 'response', record_response) |
541 | |
542 | - volume_close = '%s_close' % self.name |
543 | - getattr(self.ui, volume_close).clicked() |
544 | + self.volume_close.clicked() |
545 | self.assertEqual(gtk.RESPONSE_CLOSE, self.response, |
546 | - '%s should emit RESPONSE_CLOSE.' % volume_close) |
547 | + 'volume close button should emit RESPONSE_CLOSE.') |
548 | |
549 | @skip_abstract_class |
550 | def test_on_volume_clicked(self): |
551 | @@ -882,7 +903,7 @@ |
552 | self.on_volume_clicked(self.volume) |
553 | |
554 | @skip_abstract_class |
555 | - def test_volume_dialog_props(self): |
556 | + def test_volume_dialog_properties(self): |
557 | """The volume dialog has correct properties.""" |
558 | size = self.volume_dialog.size_request() |
559 | self.assertEquals((600, 300), size) |
560 | @@ -900,6 +921,7 @@ |
561 | self.assertEqual(expected, actual, |
562 | msg % (self.volume_dialog_name, expected, actual)) |
563 | |
564 | + |
565 | class MagicicadaUIFoldersTestCase(_MagicicadaUIVolumeTestCase): |
566 | """UI test cases for folders.""" |
567 | |
568 | @@ -945,3 +967,203 @@ |
569 | |
570 | name = 'shares_to_others' |
571 | |
572 | + |
573 | +class MagicicadaUIMetadataTestCase(MagicicadaUITestCase): |
574 | + """UI test cases for metadata display.""" |
575 | + |
576 | + name = 'raw_metadata' |
577 | + |
578 | + def setUp(self): |
579 | + """Init.""" |
580 | + super(MagicicadaUIMetadataTestCase, self).setUp() |
581 | + self.path = get_data_file('tests', 'metadata-test.txt') |
582 | + self.metadata = dict(bla='ble', foo='bar') |
583 | + |
584 | + def assert_widget_availability(self, enabled=True): |
585 | + """Check button availability according to 'enabled'.""" |
586 | + s = super(MagicicadaUIMetadataTestCase, self) |
587 | + s.assert_widget_availability(self.name, enabled) |
588 | + |
589 | + def assert_dialog_visibility(self, dialog, text_view, image): |
590 | + """Check the visibility for dialog, text_view and image.""" |
591 | + msg = '%s visibility should be %s (got %s instead).' |
592 | + visible = self.ui.widget_is_visible(self.ui.raw_metadata_dialog) |
593 | + self.assertEqual(dialog, visible, |
594 | + msg % ('raw_metadata_dialog', dialog, visible)) |
595 | + |
596 | + visible = self.ui.widget_is_visible(self.ui.raw_metadata_view) |
597 | + self.assertEqual(text_view, visible, |
598 | + msg % ('raw_metadata_view', text_view, visible)) |
599 | + |
600 | + visible = self.ui.widget_is_visible(self.ui.raw_metadata_image) |
601 | + self.assertEqual(image, visible, |
602 | + msg % ('raw_metadata_image', image, visible)) |
603 | + |
604 | + def test_raw_metadata_are_disabled_until_started(self): |
605 | + """Raw metadata button is disabled until online.""" |
606 | + # disabled at startup |
607 | + self.assert_widget_availability(enabled=False) |
608 | + |
609 | + # enabled when started |
610 | + self.do_start() |
611 | + self.assert_widget_availability(enabled=True) |
612 | + |
613 | + def test_raw_metadata_are_enabled_until_stopped(self): |
614 | + """Raw metadata button is enabled until offline.""" |
615 | + self.do_connect() |
616 | + self.assert_widget_availability(enabled=True) |
617 | + |
618 | + self.ui.on_online() |
619 | + self.assert_widget_availability(enabled=True) |
620 | + |
621 | + self.ui.on_offline() |
622 | + self.assert_widget_availability(enabled=True) |
623 | + |
624 | + self.ui.on_disconnect_clicked(self.ui.disconnect) |
625 | + self.ui.on_disconnected() |
626 | + self.assert_widget_availability(enabled=True) |
627 | + |
628 | + # disabled when stopped |
629 | + self.ui.on_stop_clicked(self.ui.stop) |
630 | + self.assert_widget_availability(enabled=False) |
631 | + self.ui.on_stopped() |
632 | + self.assert_widget_availability(enabled=False) |
633 | + |
634 | + def test_raw_metadata_close_hides_the_dialog(self): |
635 | + """Test raw_metadata close button emits RESPONSE_CLOSE when clicked.""" |
636 | + self.ui.raw_metadata_close.clicked() |
637 | + self.assertFalse(self.ui.widget_is_visible(self.ui.raw_metadata_dialog), |
638 | + 'raw_metadata_dialog should not be visible.') |
639 | + |
640 | + def test_file_chooser_open_emits_response_ok(self): |
641 | + """Test volume close button emits RESPONSE_CLOSE when clicked.""" |
642 | + self.response = None |
643 | + def record_response(value): |
644 | + """Record the response received.""" |
645 | + self.response = value |
646 | + self.patch(self.ui.file_chooser, 'response', record_response) |
647 | + |
648 | + self.ui.file_chooser_open.clicked() |
649 | + self.assertEqual(gtk.FILE_CHOOSER_ACTION_OPEN, self.response, |
650 | + 'open button should emit FILE_CHOOSER_ACTION_OPEN.') |
651 | + |
652 | + def test_on_raw_metadata_clicked(self): |
653 | + """Test on_raw_metadata_clicked.""" |
654 | + self.assertFalse(self.ui.widget_is_visible(self.ui.raw_metadata_dialog), |
655 | + 'raw_metadata_dialog should not be visible.') |
656 | + |
657 | + self.ui.file_chooser.set_filename(self.path) |
658 | + def test_file_chooser(): |
659 | + """Auxiliar to assert over the file_chooser.""" |
660 | + self.assertTrue(self.ui.widget_is_visible(self.ui.file_chooser), |
661 | + 'file_chooser must be visible on metadata clicked.') |
662 | + |
663 | + gobject.timeout_add(100, test_and_click, |
664 | + (self.ui.file_chooser_open, test_file_chooser)) |
665 | + self.ui.on_raw_metadata_clicked(self.ui.raw_metadata) |
666 | + |
667 | + self.assertFalse(self.ui.widget_is_visible(self.ui.file_chooser), |
668 | + 'file_chooser must be visible after metadata clicked.') |
669 | + self.assertEqual(self.path, self.ui.file_chooser.get_filename(), |
670 | + 'filename returned by file chooser must be correct.') |
671 | + |
672 | + # raw_metadata_dialog is enabled and shows the loading animation |
673 | + self.assert_dialog_visibility(dialog=True, text_view=False, image=True) |
674 | + expected = self.ui.raw_metadata_image.get_animation() |
675 | + self.assertEqual(self.ui.loading_animation, expected, |
676 | + 'raw_metadata_image must have the correct animation.') |
677 | + |
678 | + # Check that the metadata was asked to the SD |
679 | + self.assertEqual(self.ui.sd._meta_path, self.path) |
680 | + # SD will eventually callback us with the metadata |
681 | + self.ui.on_metadata_ready(self.path, self.metadata) |
682 | + |
683 | + # raw_metadata_dialog is enabled and shows the metadata |
684 | + self.assert_dialog_visibility(dialog=True, text_view=True, image=False) |
685 | + |
686 | + # user closes the dialog |
687 | + self.ui.raw_metadata_close.clicked() |
688 | + |
689 | + # dialog was closed already |
690 | + self.assertFalse(self.ui.widget_is_visible(self.ui.raw_metadata_dialog), |
691 | + 'raw_metadata_dialog should not be visible.') |
692 | + |
693 | + def test_raw_metadata_dialog_properties(self): |
694 | + """The raw_metadata dialog has correct properties.""" |
695 | + dialog = 'raw_metadata_dialog' |
696 | + size = self.ui.raw_metadata_dialog.size_request() |
697 | + self.assertEquals((600, 300), size) |
698 | + |
699 | + self.assertFalse(self.ui.raw_metadata_dialog.get_modal(), |
700 | + '%s must not be modal.' % dialog) |
701 | + |
702 | + position = self.ui.raw_metadata_dialog.get_property('window-position') |
703 | + self.assertEqual(gtk.WIN_POS_CENTER, position, |
704 | + '%s must be centered.' % dialog) |
705 | + |
706 | + actual = self.ui.raw_metadata_dialog.get_title() |
707 | + expected = self.name.replace('_', ' ').capitalize() |
708 | + msg = '%s title must be %s (got %s instead)' |
709 | + self.assertEqual(expected, actual, msg % (dialog, expected, actual)) |
710 | + |
711 | + actual = self.ui.raw_metadata_view.get_wrap_mode() |
712 | + msg = 'wrap mode for view must be gtk.WRAP_WORD (got %s instead).' |
713 | + self.assertEqual(gtk.WRAP_WORD, actual, msg % actual) |
714 | + |
715 | + def test_callback_is_connected(self): |
716 | + """Metadata ready callback is connected.""" |
717 | + self.assertEqual(self.ui.sd.on_metadata_ready_callback, |
718 | + self.ui.on_metadata_ready, |
719 | + 'on_metadata_ready_callback callback must be set.') |
720 | + |
721 | + def test_file_chooser_is_hidden_at_startup(self): |
722 | + """File chooser exists but is not visible.""" |
723 | + self.assertFalse(self.ui.widget_is_visible(self.ui.file_chooser), |
724 | + 'file_chooser must be hidden by default.') |
725 | + |
726 | + def test_filename_is_used_only_if_open_clicked(self): |
727 | + """Filename is used only if user clicked open.""" |
728 | + self.patch(self.ui.sd, 'get_metadata', self.set_called) |
729 | + gobject.timeout_add(100, test_and_click, |
730 | + (self.ui.file_chooser_cancel, NO_OP)) |
731 | + self.ui.on_raw_metadata_clicked(self.ui.raw_metadata) |
732 | + |
733 | + self.assertFalse(self.called, |
734 | + 'get_metadata should not be called if no file chosen.') |
735 | + |
736 | + def test_filename_is_stored_if_open_clicked(self): |
737 | + """Filename is stored as 'last_metadata_path' if user clicked open.""" |
738 | + self.assertTrue(self.ui.last_metadata_path is None, |
739 | + 'last_metadata_path must be None.') |
740 | + self.ui.file_chooser.set_filename(self.path) |
741 | + gobject.timeout_add(100, test_and_click, |
742 | + (self.ui.file_chooser_open, NO_OP)) |
743 | + self.ui.on_raw_metadata_clicked(self.ui.raw_metadata) |
744 | + |
745 | + self.assertEqual(self.path, self.ui.last_metadata_path, |
746 | + 'last_metadata_path should be what the user choose.') |
747 | + |
748 | + def test_on_metadata_ready(self): |
749 | + """Callback on_metadata_ready updates the raw_metadata_view.""" |
750 | + path = 'bla' |
751 | + self.ui.last_metadata_path = path |
752 | + self.ui.on_metadata_ready(path, self.metadata) |
753 | + |
754 | + buffer = self.ui.raw_metadata_view.get_buffer() |
755 | + self.assertTrue(buffer is not None, |
756 | + 'buffer for raw_metadata_view must not be None.') |
757 | + actual = buffer.get_text(*buffer.get_bounds()) |
758 | + msg = 'buffer content must be %s (got %s instead).' |
759 | + self.assertEqual(actual, str(self.metadata), |
760 | + msg % (self.metadata, actual)) |
761 | + |
762 | + def test_on_metadata_ready_doesnt_update_if_last_path_doesnt_match(self): |
763 | + """Callback on_metadata_ready updates the raw_metadata_view.""" |
764 | + self.patch(self.ui.raw_metadata_view.get_buffer(), |
765 | + 'set_text', self.set_called) |
766 | + path = 'bla' |
767 | + self.ui.last_metadata_path = path+path |
768 | + self.ui.on_metadata_ready(path, self.metadata) |
769 | + |
770 | + self.assertFalse(self.called, |
771 | + 'view should not be updated if key is not last one.') |
User should be able to open two "metadata" popups (this is specially useful to compare information).
Right now, if you click again on metada to see other file, the previous content is lost.