Merge lp:~nataliabidart/ubuntu/natty/magicicada/magicicada-0.3.0 into lp:ubuntu/natty/magicicada
- Natty (11.04)
- magicicada-0.3.0
- Merge into natty
Status: | Merged |
---|---|
Merged at revision: | 6 |
Proposed branch: | lp:~nataliabidart/ubuntu/natty/magicicada/magicicada-0.3.0 |
Merge into: | lp:ubuntu/natty/magicicada |
Diff against target: |
3391 lines (+1725/-336) 18 files modified
PKG-INFO (+4/-12) data/ui/gui.glade (+124/-6) debian/changelog (+42/-0) debian/control (+13/-13) debian/copyright (+14/-6) debian/pycompat (+0/-1) debian/rules (+2/-4) magicicada/__init__.py (+58/-29) magicicada/dbusiface.py (+95/-3) magicicada/helpers.py (+29/-17) magicicada/logger.py (+33/-9) magicicada/syncdaemon.py (+112/-17) magicicada/tests/helpers.py (+13/-4) magicicada/tests/test_dbusiface.py (+302/-35) magicicada/tests/test_logger.py (+35/-1) magicicada/tests/test_magicicada.py (+331/-144) magicicada/tests/test_syncdaemon.py (+499/-21) setup.py (+19/-14) |
To merge this branch: | bzr merge lp:~nataliabidart/ubuntu/natty/magicicada/magicicada-0.3.0 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Stefano Rivera | Approve | ||
Ubuntu Sponsors | Pending | ||
Review via email: mp+45528@code.launchpad.net |
Commit message
Description of the change
* New upstream release:
[ Natalia B. Bidart <email address hidden> ]
* Added Public Files listing to the UI (LP: #568197).
* The icon list is now set at creation time. This guarantees that proper
icon sizes will be used (LP: #669947).
* Default logging level is now INFO.
* Volumes and metadata buttons are enabled when initial_data_ready
callback is fired (LP: #612194).
[ Facundo Batista <email address hidden> ]
* Backend code to accept a share.
* Support the new "CQ changed" signal, with no data in it.
* Log all the errors that happen inside a deferred.
* Splitted "on initial data callback" in both online and offline ones.
* Handle Public Files info.
* Support GetDelta in the MQ (LP: #665680).
* Leave the "don't ask while asking" machinery ok if error while asking.
* Support an error when calling the MD (LP: #665674).
* Don't ask a lot of times for updates to Syncdaemon on changes bursts
(LP: #587020) (LP: #643195).
* debian/control
- updated depedency list
- 9. By Natalia Bidart
-
Depending on freshly released ubuntuone-client 1.5.2.
Natalia Bidart (nataliabidart) wrote : | # |
> Hi looks ok, but a couple of small things pile up:
> * changelog is a bit weirdly formatted (text indented to the same level as the
> bullets.
> * You can use this syntax: (LP: #587020, #643195)
> * I'd prefer something a little more concrete than "updated dependency list".
> It looks like: sorted depends, dropped depends-indep, depend on python-
> ubuntu-one-client (>= foo), depend on python-xdg
Fixed as per suggestion.
> * No mention of the standards version update (these are traditionally
> changelogged as "Bumped Standards-Version to X.Y.Z (no changes needed)"
Added (I bumped the version sin I got a lintian warning).
> * No mention of the maintainer change in the changelog. Should the maintainer
> not be Ubuntu Developers <email address hidden>?
I'm not sure, Elliot used to do the package maintenance but now is just Facundo Batista and me. I thought we had to put the actual maintainers on that field.
Can you please confirm?
> * No mention of the description change.
Added.
> * Copyright:
> - I assume the copyright years need a bump.
Added.
> - DEP5 has evolved quite a bit, at some point this file should be updated to
> match the current standards.
Sorry for my ignorance, what is DEP5? And how should it be updated?
> - The short GPL licence grant should be included before the "full text"
> notice.
I'm a bit lost, in which file this needs to be changed?
> * At some point, you should consider migrating to dh_python2, AFAIK doko is
> quite keen for Ubuntu-specific packages to migrate during natty.
Hum, my ignorance strikes again. I'm not using explicitly dh_python to build the package. Could you please be more specific where dh_python2 should be used?
Thanks a lot for the feedback!
- 10. By Natalia Bidart
-
* debian/control
- Sorted Depends, dropped depends-indep
- Added version depend restriction for python-ubuntuone- client (>= 1.5.2)
- Added missing depend: python-xdg
- Bumped Standards-Version to 3.9.1 (no changes needed)
- Changed the description to match the new description in the project
setup.py file* debian/copyright
- Added year 2011 to copyright statement
- Fixed email ocurrences of nataliabidart to consistently be nataliabidart
at gmail dot com
Natalia Bidart (nataliabidart) wrote : | # |
Stefano, I forgot to as about:
"changelog is a bit weirdly formatted (text indented to the same level as the bullets"
what would be a better formatting?
Stefano Rivera (stefanor) wrote : | # |
Thanks, that was quick.
> I thought we had to put the actual maintainers on that field.
> Can you please confirm?
I'm not sure on Maintainer policy either, best I can tell is that Ubuntu tends to use Ubuntu Developers for ubuntu-only packages. The reason for this is that Ubuntu packages don't have strong maintainers, but are maintained by all Ubuntu developers. However, there is no policy I know of stipulating this, so I'm happy with it as is.
> what is DEP5? And how should it be updated?
DEP5 is the machine-readable changelog format. http://
A few of the fields have changed name, and there should only be one (multi-line) copyright item.
The licence block for GPL-3 should probably be:
License: GPL-3
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, version 3 of the License.
.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
.
On Debian systems, the complete text of the GNU General Public License
version 3 can be found in the /usr/share/
> Could you please be more specific where dh_python2 should be used?
dh_python2 is a replacement for python-support and python-central. Instead of creating per-python-version symlink farms at install time, they are shipped in the package, and only byte-compilation is done at install time. Unfortunately there is little documentation on it that I can point you to (yet).
CDBS only gained support in 0.4.90 (which isn't in Ubuntu yet), so the best way to use it is probably to switch to debhelper.
Here's a trivial port, which is untested, but looks ok: http://
> what would be a better formatting?
Wrapping like this
* this is a multilne
bulleted point.
Oh, and lintian says "Splitted" isn't an english word. It's right about that :)
Natalia Bidart (nataliabidart) wrote : | # |
> > I thought we had to put the actual maintainers on that field.
> > Can you please confirm?
>
> I'm not sure on Maintainer policy either, best I can tell is that Ubuntu tends
> to use Ubuntu Developers for ubuntu-only packages. The reason for this is that
> Ubuntu packages don't have strong maintainers, but are maintained by all
> Ubuntu developers. However, there is no policy I know of stipulating this, so
> I'm happy with it as is.
Good, I'll leave it as is since I plan to maintain magicicada in the long term.
> > what is DEP5? And how should it be updated?
>
> DEP5 is the machine-readable changelog format.
> http://
> A few of the fields have changed name, and there should only be one (multi-
> line) copyright item.
Thanks for the pointer, all fixed.
> > Could you please be more specific where dh_python2 should be used?
>
> dh_python2 is a replacement for python-support and python-central. Instead of
> creating per-python-version symlink farms at install time, they are shipped in
> the package, and only byte-compilation is done at install time. Unfortunately
> there is little documentation on it that I can point you to (yet).
>
> CDBS only gained support in 0.4.90 (which isn't in Ubuntu yet), so the best
> way to use it is probably to switch to debhelper.
>
> Here's a trivial port, which is untested, but looks ok:
> http://
I've applied this patch and build and installed the packages, seems to work just fine. Thanks!
> > what would be a better formatting?
> Wrapping like this
>
> * this is a multilne
> bulleted point.
Fixed!
> Oh, and lintian says "Splitted" isn't an english word. It's right about that
> :)
Also fixed :-)
Thanks again!
- 11. By Natalia Bidart
-
* debian/copyright
- Fixed formatting to be DEP5 compatible - 12. By Natalia Bidart
-
* Switched to debhelper, thanks Stefano Rivera for the patch.
* debian/control
- fixed bullet indent formatting
- fixed typo (s/splitted/split/)
Stefano Rivera (stefanor) wrote : | # |
OK, copyright still isn't quite current DEP5, I'll tweak it on upload.
I'll also document the transition to dh_python2 in the changelog, it's worthy of a mention.
Preview Diff
1 | === modified file 'PKG-INFO' | |||
2 | --- PKG-INFO 2010-09-02 19:26:08 +0000 | |||
3 | +++ PKG-INFO 2011-01-07 23:06:32 +0000 | |||
4 | @@ -1,19 +1,11 @@ | |||
5 | 1 | Metadata-Version: 1.1 | 1 | Metadata-Version: 1.1 |
6 | 2 | Name: magicicada | 2 | Name: magicicada |
9 | 3 | Version: 0.2 | 3 | Version: 0.3.0 |
10 | 4 | Summary: A GTK+ frontend for the "Chicharra" part of Ubuntu One. | 4 | Summary: A GTK+ frontend for Ubuntu One. |
11 | 5 | Home-page: https://launchpad.net/magicicada | 5 | Home-page: https://launchpad.net/magicicada |
12 | 6 | Author: Natalia Bidart | 6 | Author: Natalia Bidart |
14 | 7 | Author-email: natalia.bidart@ubuntu.com | 7 | Author-email: nataliabidart@gmail.com |
15 | 8 | License: GPL-3 | 8 | License: GPL-3 |
17 | 9 | Description: UNKNOWN | 9 | Description: This application provides a GTK frontend to manage the file synchronisation service of Ubuntu One. |
18 | 10 | Platform: UNKNOWN | 10 | Platform: UNKNOWN |
19 | 11 | Requires: dbus | ||
20 | 12 | Requires: gobject | ||
21 | 13 | Requires: gtk | ||
22 | 14 | Requires: pango | ||
23 | 15 | Requires: twisted.internet | ||
24 | 16 | Requires: twisted.trial.unittest | ||
25 | 17 | Requires: ubuntuone.syncdaemon.tools | ||
26 | 18 | Requires: xdg.BaseDirectory | ||
27 | 19 | Provides: magicicada | 11 | Provides: magicicada |
28 | 20 | 12 | ||
29 | === added file 'data/media/logo-048.png' | |||
30 | 21 | Binary files data/media/logo-048.png 1970-01-01 00:00:00 +0000 and data/media/logo-048.png 2011-01-07 23:06:32 +0000 differ | 13 | Binary files data/media/logo-048.png 1970-01-01 00:00:00 +0000 and data/media/logo-048.png 2011-01-07 23:06:32 +0000 differ |
31 | === modified file 'data/ui/gui.glade' | |||
32 | --- data/ui/gui.glade 2010-07-24 18:46:42 +0000 | |||
33 | +++ data/ui/gui.glade 2011-01-07 23:06:32 +0000 | |||
34 | @@ -1,4 +1,4 @@ | |||
36 | 1 | <?xml version="1.0"?> | 1 | <?xml version="1.0" encoding="UTF-8"?> |
37 | 2 | <interface> | 2 | <interface> |
38 | 3 | <requires lib="gtk+" version="2.16"/> | 3 | <requires lib="gtk+" version="2.16"/> |
39 | 4 | <!-- interface-naming-policy project-wide --> | 4 | <!-- interface-naming-policy project-wide --> |
40 | @@ -84,6 +84,18 @@ | |||
41 | 84 | <column type="gchararray"/> | 84 | <column type="gchararray"/> |
42 | 85 | </columns> | 85 | </columns> |
43 | 86 | </object> | 86 | </object> |
44 | 87 | <object class="GtkListStore" id="public_files_store"> | ||
45 | 88 | <columns> | ||
46 | 89 | <!-- column-name path --> | ||
47 | 90 | <column type="gchararray"/> | ||
48 | 91 | <!-- column-name public_url --> | ||
49 | 92 | <column type="gchararray"/> | ||
50 | 93 | <!-- column-name volume --> | ||
51 | 94 | <column type="gchararray"/> | ||
52 | 95 | <!-- column-name node --> | ||
53 | 96 | <column type="gchararray"/> | ||
54 | 97 | </columns> | ||
55 | 98 | </object> | ||
56 | 87 | <object class="GtkWindow" id="main_window"> | 99 | <object class="GtkWindow" id="main_window"> |
57 | 88 | <property name="width_request">800</property> | 100 | <property name="width_request">800</property> |
58 | 89 | <property name="height_request">600</property> | 101 | <property name="height_request">600</property> |
59 | @@ -149,10 +161,12 @@ | |||
60 | 149 | <object class="GtkToolbar" id="toolbar"> | 161 | <object class="GtkToolbar" id="toolbar"> |
61 | 150 | <property name="visible">True</property> | 162 | <property name="visible">True</property> |
62 | 151 | <property name="toolbar_style">both</property> | 163 | <property name="toolbar_style">both</property> |
63 | 164 | <property name="icon_size">1</property> | ||
64 | 152 | <child> | 165 | <child> |
65 | 153 | <object class="GtkToolButton" id="start"> | 166 | <object class="GtkToolButton" id="start"> |
66 | 154 | <property name="width_request">50</property> | 167 | <property name="width_request">50</property> |
67 | 155 | <property name="visible">True</property> | 168 | <property name="visible">True</property> |
68 | 169 | <property name="is_important">True</property> | ||
69 | 156 | <property name="label" translatable="yes">Start</property> | 170 | <property name="label" translatable="yes">Start</property> |
70 | 157 | <property name="use_underline">True</property> | 171 | <property name="use_underline">True</property> |
71 | 158 | <property name="stock_id">gtk-apply</property> | 172 | <property name="stock_id">gtk-apply</property> |
72 | @@ -166,6 +180,7 @@ | |||
73 | 166 | <child> | 180 | <child> |
74 | 167 | <object class="GtkToolButton" id="stop"> | 181 | <object class="GtkToolButton" id="stop"> |
75 | 168 | <property name="width_request">50</property> | 182 | <property name="width_request">50</property> |
76 | 183 | <property name="is_important">True</property> | ||
77 | 169 | <property name="label" translatable="yes">Stop</property> | 184 | <property name="label" translatable="yes">Stop</property> |
78 | 170 | <property name="use_underline">True</property> | 185 | <property name="use_underline">True</property> |
79 | 171 | <property name="stock_id">gtk-stop</property> | 186 | <property name="stock_id">gtk-stop</property> |
80 | @@ -181,6 +196,7 @@ | |||
81 | 181 | <property name="width_request">85</property> | 196 | <property name="width_request">85</property> |
82 | 182 | <property name="visible">True</property> | 197 | <property name="visible">True</property> |
83 | 183 | <property name="sensitive">False</property> | 198 | <property name="sensitive">False</property> |
84 | 199 | <property name="is_important">True</property> | ||
85 | 184 | <property name="label" translatable="yes">Connect</property> | 200 | <property name="label" translatable="yes">Connect</property> |
86 | 185 | <property name="use_underline">True</property> | 201 | <property name="use_underline">True</property> |
87 | 186 | <property name="stock_id">gtk-connect</property> | 202 | <property name="stock_id">gtk-connect</property> |
88 | @@ -194,6 +210,7 @@ | |||
89 | 194 | <child> | 210 | <child> |
90 | 195 | <object class="GtkToolButton" id="disconnect"> | 211 | <object class="GtkToolButton" id="disconnect"> |
91 | 196 | <property name="width_request">85</property> | 212 | <property name="width_request">85</property> |
92 | 213 | <property name="is_important">True</property> | ||
93 | 197 | <property name="label" translatable="yes">Disconnect</property> | 214 | <property name="label" translatable="yes">Disconnect</property> |
94 | 198 | <property name="use_underline">True</property> | 215 | <property name="use_underline">True</property> |
95 | 199 | <property name="stock_id">gtk-disconnect</property> | 216 | <property name="stock_id">gtk-disconnect</property> |
96 | @@ -269,6 +286,20 @@ | |||
97 | 269 | <property name="homogeneous">True</property> | 286 | <property name="homogeneous">True</property> |
98 | 270 | </packing> | 287 | </packing> |
99 | 271 | </child> | 288 | </child> |
100 | 289 | <child> | ||
101 | 290 | <object class="GtkToolButton" id="public_files"> | ||
102 | 291 | <property name="visible">True</property> | ||
103 | 292 | <property name="sensitive">False</property> | ||
104 | 293 | <property name="label" translatable="yes">Public Files</property> | ||
105 | 294 | <property name="use_underline">True</property> | ||
106 | 295 | <property name="stock_id">gtk-file</property> | ||
107 | 296 | <signal name="clicked" handler="on_public_files_clicked"/> | ||
108 | 297 | </object> | ||
109 | 298 | <packing> | ||
110 | 299 | <property name="expand">False</property> | ||
111 | 300 | <property name="homogeneous">True</property> | ||
112 | 301 | </packing> | ||
113 | 302 | </child> | ||
114 | 272 | </object> | 303 | </object> |
115 | 273 | <packing> | 304 | <packing> |
116 | 274 | <property name="expand">False</property> | 305 | <property name="expand">False</property> |
117 | @@ -548,7 +579,6 @@ | |||
118 | 548 | <object class="GtkAboutDialog" id="about_dialog"> | 579 | <object class="GtkAboutDialog" id="about_dialog"> |
119 | 549 | <property name="border_width">5</property> | 580 | <property name="border_width">5</property> |
120 | 550 | <property name="type_hint">normal</property> | 581 | <property name="type_hint">normal</property> |
121 | 551 | <property name="has_separator">False</property> | ||
122 | 552 | <property name="program_name">Magicicada</property> | 582 | <property name="program_name">Magicicada</property> |
123 | 553 | <property name="copyright" translatable="yes">Copyright 2010 Chicharreros | 583 | <property name="copyright" translatable="yes">Copyright 2010 Chicharreros |
124 | 554 | Copyright 2010 Natalia Bidart <natalia.bidart@gmail.com> | 584 | Copyright 2010 Natalia Bidart <natalia.bidart@gmail.com> |
125 | @@ -598,7 +628,6 @@ | |||
126 | 598 | <property name="modal">True</property> | 628 | <property name="modal">True</property> |
127 | 599 | <property name="window_position">center</property> | 629 | <property name="window_position">center</property> |
128 | 600 | <property name="type_hint">normal</property> | 630 | <property name="type_hint">normal</property> |
129 | 601 | <property name="has_separator">False</property> | ||
130 | 602 | <child internal-child="vbox"> | 631 | <child internal-child="vbox"> |
131 | 603 | <object class="GtkVBox" id="dialog-vbox2"> | 632 | <object class="GtkVBox" id="dialog-vbox2"> |
132 | 604 | <property name="visible">True</property> | 633 | <property name="visible">True</property> |
133 | @@ -705,7 +734,6 @@ | |||
134 | 705 | <property name="modal">True</property> | 734 | <property name="modal">True</property> |
135 | 706 | <property name="window_position">center</property> | 735 | <property name="window_position">center</property> |
136 | 707 | <property name="type_hint">normal</property> | 736 | <property name="type_hint">normal</property> |
137 | 708 | <property name="has_separator">False</property> | ||
138 | 709 | <child internal-child="vbox"> | 737 | <child internal-child="vbox"> |
139 | 710 | <object class="GtkVBox" id="dialog-vbox3"> | 738 | <object class="GtkVBox" id="dialog-vbox3"> |
140 | 711 | <property name="visible">True</property> | 739 | <property name="visible">True</property> |
141 | @@ -844,7 +872,6 @@ | |||
142 | 844 | <property name="modal">True</property> | 872 | <property name="modal">True</property> |
143 | 845 | <property name="window_position">center</property> | 873 | <property name="window_position">center</property> |
144 | 846 | <property name="type_hint">normal</property> | 874 | <property name="type_hint">normal</property> |
145 | 847 | <property name="has_separator">False</property> | ||
146 | 848 | <child internal-child="vbox"> | 875 | <child internal-child="vbox"> |
147 | 849 | <object class="GtkVBox" id="dialog-vbox5"> | 876 | <object class="GtkVBox" id="dialog-vbox5"> |
148 | 850 | <property name="visible">True</property> | 877 | <property name="visible">True</property> |
149 | @@ -981,7 +1008,6 @@ | |||
150 | 981 | <property name="border_width">5</property> | 1008 | <property name="border_width">5</property> |
151 | 982 | <property name="window_position">center</property> | 1009 | <property name="window_position">center</property> |
152 | 983 | <property name="type_hint">normal</property> | 1010 | <property name="type_hint">normal</property> |
153 | 984 | <property name="has_separator">False</property> | ||
154 | 985 | <property name="create_folders">False</property> | 1011 | <property name="create_folders">False</property> |
155 | 986 | <signal name="file_activated" handler="on_file_chooser_open_clicked"/> | 1012 | <signal name="file_activated" handler="on_file_chooser_open_clicked"/> |
156 | 987 | <signal name="show" handler="on_file_chooser_show"/> | 1013 | <signal name="show" handler="on_file_chooser_show"/> |
157 | @@ -1040,4 +1066,96 @@ | |||
158 | 1040 | <action-widget response="0">file_chooser_open</action-widget> | 1066 | <action-widget response="0">file_chooser_open</action-widget> |
159 | 1041 | </action-widgets> | 1067 | </action-widgets> |
160 | 1042 | </object> | 1068 | </object> |
161 | 1069 | <object class="GtkDialog" id="public_files_dialog"> | ||
162 | 1070 | <property name="width_request">600</property> | ||
163 | 1071 | <property name="height_request">300</property> | ||
164 | 1072 | <property name="border_width">5</property> | ||
165 | 1073 | <property name="title" translatable="yes">Public files</property> | ||
166 | 1074 | <property name="modal">True</property> | ||
167 | 1075 | <property name="window_position">center</property> | ||
168 | 1076 | <property name="type_hint">normal</property> | ||
169 | 1077 | <child internal-child="vbox"> | ||
170 | 1078 | <object class="GtkVBox" id="dialog-vbox9"> | ||
171 | 1079 | <property name="visible">True</property> | ||
172 | 1080 | <property name="spacing">2</property> | ||
173 | 1081 | <child> | ||
174 | 1082 | <object class="GtkScrolledWindow" id="scrolledwindow6"> | ||
175 | 1083 | <property name="visible">True</property> | ||
176 | 1084 | <property name="can_focus">True</property> | ||
177 | 1085 | <property name="hscrollbar_policy">automatic</property> | ||
178 | 1086 | <child> | ||
179 | 1087 | <object class="GtkTreeView" id="public_files_view"> | ||
180 | 1088 | <property name="visible">True</property> | ||
181 | 1089 | <property name="can_focus">True</property> | ||
182 | 1090 | <property name="model">public_files_store</property> | ||
183 | 1091 | <property name="headers_clickable">False</property> | ||
184 | 1092 | <property name="rules_hint">True</property> | ||
185 | 1093 | <property name="search_column">0</property> | ||
186 | 1094 | <property name="enable_grid_lines">both</property> | ||
187 | 1095 | <property name="enable_tree_lines">True</property> | ||
188 | 1096 | <child> | ||
189 | 1097 | <object class="GtkTreeViewColumn" id="public_files_path"> | ||
190 | 1098 | <property name="resizable">True</property> | ||
191 | 1099 | <property name="title">Path</property> | ||
192 | 1100 | <property name="expand">True</property> | ||
193 | 1101 | <child> | ||
194 | 1102 | <object class="GtkCellRendererText" id="cellrenderertext9"/> | ||
195 | 1103 | <attributes> | ||
196 | 1104 | <attribute name="text">0</attribute> | ||
197 | 1105 | </attributes> | ||
198 | 1106 | </child> | ||
199 | 1107 | </object> | ||
200 | 1108 | </child> | ||
201 | 1109 | <child> | ||
202 | 1110 | <object class="GtkTreeViewColumn" id="public_files_public_url"> | ||
203 | 1111 | <property name="resizable">True</property> | ||
204 | 1112 | <property name="title">URL</property> | ||
205 | 1113 | <property name="expand">True</property> | ||
206 | 1114 | <child> | ||
207 | 1115 | <object class="GtkCellRendererText" id="cellrenderertext12"/> | ||
208 | 1116 | <attributes> | ||
209 | 1117 | <attribute name="text">1</attribute> | ||
210 | 1118 | </attributes> | ||
211 | 1119 | </child> | ||
212 | 1120 | </object> | ||
213 | 1121 | </child> | ||
214 | 1122 | </object> | ||
215 | 1123 | </child> | ||
216 | 1124 | </object> | ||
217 | 1125 | <packing> | ||
218 | 1126 | <property name="position">1</property> | ||
219 | 1127 | </packing> | ||
220 | 1128 | </child> | ||
221 | 1129 | <child internal-child="action_area"> | ||
222 | 1130 | <object class="GtkHButtonBox" id="dialog-action_area9"> | ||
223 | 1131 | <property name="visible">True</property> | ||
224 | 1132 | <property name="layout_style">end</property> | ||
225 | 1133 | <child> | ||
226 | 1134 | <object class="GtkButton" id="public_files_close"> | ||
227 | 1135 | <property name="label">gtk-close</property> | ||
228 | 1136 | <property name="visible">True</property> | ||
229 | 1137 | <property name="can_focus">True</property> | ||
230 | 1138 | <property name="receives_default">True</property> | ||
231 | 1139 | <property name="use_stock">True</property> | ||
232 | 1140 | <signal name="clicked" handler="on_public_files_close_clicked"/> | ||
233 | 1141 | </object> | ||
234 | 1142 | <packing> | ||
235 | 1143 | <property name="expand">False</property> | ||
236 | 1144 | <property name="fill">False</property> | ||
237 | 1145 | <property name="position">0</property> | ||
238 | 1146 | </packing> | ||
239 | 1147 | </child> | ||
240 | 1148 | </object> | ||
241 | 1149 | <packing> | ||
242 | 1150 | <property name="expand">False</property> | ||
243 | 1151 | <property name="pack_type">end</property> | ||
244 | 1152 | <property name="position">0</property> | ||
245 | 1153 | </packing> | ||
246 | 1154 | </child> | ||
247 | 1155 | </object> | ||
248 | 1156 | </child> | ||
249 | 1157 | <action-widgets> | ||
250 | 1158 | <action-widget response="0">public_files_close</action-widget> | ||
251 | 1159 | </action-widgets> | ||
252 | 1160 | </object> | ||
253 | 1043 | </interface> | 1161 | </interface> |
254 | 1044 | 1162 | ||
255 | === modified file 'debian/changelog' | |||
256 | --- debian/changelog 2010-09-23 17:13:19 +0000 | |||
257 | +++ debian/changelog 2011-01-07 23:06:32 +0000 | |||
258 | @@ -1,3 +1,45 @@ | |||
259 | 1 | magicicada (0.3.0-0ubuntu1) UNRELEASED; urgency=low | ||
260 | 2 | |||
261 | 3 | * New upstream release (0.3.0): | ||
262 | 4 | |||
263 | 5 | [ Natalia B. Bidart <natalia.bidart@canonical.com> ] | ||
264 | 6 | - Added Public Files listing to the UI (LP: #568197). | ||
265 | 7 | - The icon list is now set at creation time. This guarantees that proper | ||
266 | 8 | icon sizes will be used (LP: #669947). | ||
267 | 9 | - Default logging level is now INFO. | ||
268 | 10 | - Volumes and metadata buttons are enabled when initial_data_ready | ||
269 | 11 | callback is fired (LP: #612194). | ||
270 | 12 | |||
271 | 13 | [ Facundo Batista <facundo@taniquetil.com.ar> ] | ||
272 | 14 | - Backend code to accept a share. | ||
273 | 15 | - Support the new "CQ changed" signal, with no data in it. | ||
274 | 16 | - Log all the errors that happen inside a deferred. | ||
275 | 17 | - Split "on initial data callback" in both online and offline ones. | ||
276 | 18 | - Handle Public Files info. | ||
277 | 19 | - Support GetDelta in the MQ (LP: #665680). | ||
278 | 20 | - Leave the "don't ask while asking" machinery ok if error while asking. | ||
279 | 21 | - Support an error when calling the MD (LP: #665674). | ||
280 | 22 | - Don't ask a lot of times for updates to Syncdaemon on changes bursts | ||
281 | 23 | (LP: #587020, #643195). | ||
282 | 24 | |||
283 | 25 | * debian/control | ||
284 | 26 | - Sorted Depends, dropped depends-indep | ||
285 | 27 | - Added version depend restriction for python-ubuntuone-client (>= 1.5.2) | ||
286 | 28 | - Added missing depend: python-xdg | ||
287 | 29 | - Bumped Standards-Version to 3.9.1 (no changes needed) | ||
288 | 30 | - Changed the description to match the new description in the project | ||
289 | 31 | setup.py file | ||
290 | 32 | |||
291 | 33 | * debian/copyright | ||
292 | 34 | - Added year 2011 to copyright statement | ||
293 | 35 | - Fixed email ocurrences of nataliabidart to consistently be nataliabidart | ||
294 | 36 | at gmail dot com | ||
295 | 37 | - Fixed formatting to be DEP5 compatible | ||
296 | 38 | |||
297 | 39 | * Switched to debhelper, thanks Stefano Rivera for the patch. | ||
298 | 40 | |||
299 | 41 | -- Natalia Bidart (nessita) <nataliabidart@gmail.com> Fri, 07 Jan 2011 20:01:05 -0300 | ||
300 | 42 | |||
301 | 1 | magicicada (0.2-0ubuntu1) maverick; urgency=low | 43 | magicicada (0.2-0ubuntu1) maverick; urgency=low |
302 | 2 | 44 | ||
303 | 3 | [ Facundo Batista ] | 45 | [ Facundo Batista ] |
304 | 4 | 46 | ||
305 | === modified file 'debian/control' | |||
306 | --- debian/control 2010-06-08 10:53:02 +0000 | |||
307 | +++ debian/control 2011-01-07 23:06:32 +0000 | |||
308 | @@ -1,24 +1,24 @@ | |||
309 | 1 | Source: magicicada | 1 | Source: magicicada |
310 | 2 | Section: python | 2 | Section: python |
311 | 3 | Priority: extra | 3 | Priority: extra |
319 | 4 | Build-Depends: cdbs (>= 0.4.43), | 4 | Build-Depends: debhelper (>= 7), |
320 | 5 | debhelper (>= 6), | 5 | python-all (>= 2.6.5-13~), |
321 | 6 | python, | 6 | python-distutils-extra (>= 2.10) |
322 | 7 | python-support (>= 0.6.4) | 7 | Maintainer: Natalia Bidart <nataliabidart@gmail.com> |
323 | 8 | Build-Depends-Indep: python-distutils-extra (>= 2.10) | 8 | Standards-Version: 3.9.1 |
324 | 9 | Maintainer: Elliot Murphy <elliot@ubuntu.com> | 9 | X-Python-Version: >= 2.5 |
318 | 10 | Standards-Version: 3.8.4 | ||
325 | 11 | 10 | ||
326 | 12 | Package: magicicada | 11 | Package: magicicada |
327 | 13 | Architecture: all | 12 | Architecture: all |
328 | 14 | Depends: ${misc:Depends}, | 13 | Depends: ${misc:Depends}, |
329 | 15 | ${misc:Depends}, | ||
330 | 16 | ${python:Depends}, | 14 | ${python:Depends}, |
331 | 15 | python-dbus, | ||
332 | 17 | python-gobject, | 16 | python-gobject, |
334 | 18 | python-dbus, | 17 | python-gtk2, |
335 | 18 | python-ubuntuone-client (>= 1.5.2), | ||
336 | 19 | python-twisted-core, | 19 | python-twisted-core, |
339 | 20 | python-gtk2, | 20 | python-xdg |
340 | 21 | python-ubuntuone-client | 21 | Breaks: ${python:Breaks} |
341 | 22 | Description: A GTK+ frontend for Ubuntu One file sync. | 22 | Description: A GTK+ frontend for Ubuntu One file sync. |
344 | 23 | Displays diagnostic information about what the syncdaemon is doing | 23 | This application provides a GTK+ frontend to manage the file |
345 | 24 | on your computer. | 24 | synchronisation service of Ubuntu One. |
346 | 25 | 25 | ||
347 | === modified file 'debian/copyright' | |||
348 | --- debian/copyright 2010-06-08 10:53:02 +0000 | |||
349 | +++ debian/copyright 2011-01-07 23:06:32 +0000 | |||
350 | @@ -1,11 +1,19 @@ | |||
351 | 1 | Format-Specification: http://wiki.debian.org/Proposals/CopyrightFormat | 1 | Format-Specification: http://wiki.debian.org/Proposals/CopyrightFormat |
352 | 2 | Upstream-Name: magicicada | 2 | Upstream-Name: magicicada |
354 | 3 | Upstream-Maintainer: Natalia Bidart <natalia.bidart@ubuntu.com> | 3 | Upstream-Maintainer: Natalia Bidart <nataliabidart@gmail.com> |
355 | 4 | Upstream-Source: https://launchpad.net/magicicada | 4 | Upstream-Source: https://launchpad.net/magicicada |
356 | 5 | 5 | ||
357 | 6 | Files: * | 6 | Files: * |
363 | 7 | Copyright: (C) 2010 Facundo Batista <facundo@canonical.com> | 7 | Copyright 2010, 2011 Facundo Batista <facundo@canonical.com> Natalia Bidart <nataliabidart@gmail.com> |
364 | 8 | Copyright: (C) 2010 Natalia Bidart <natalia.bidart@gmail.com> | 8 | License: GPL-3+ |
365 | 9 | License: GPL-3 | 9 | This program is free software; you can redistribute it and/or modify |
366 | 10 | The full text of the GPL is distributed in | 10 | it under the terms of the GNU General Public License as published by |
367 | 11 | /usr/share/common-licenses/GPL-3 on Debian systems. | 11 | the Free Software Foundation, version 3 of the License. |
368 | 12 | . | ||
369 | 13 | This program is distributed in the hope that it will be useful, | ||
370 | 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
371 | 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
372 | 16 | General Public License for more details. | ||
373 | 17 | . | ||
374 | 18 | On Debian systems, the complete text of the GNU General Public License | ||
375 | 19 | version 3 can be found in the /usr/share/common-licenses/GPL-3 file. | ||
376 | 12 | 20 | ||
377 | === removed file 'debian/pycompat' | |||
378 | --- debian/pycompat 2010-06-08 10:53:02 +0000 | |||
379 | +++ debian/pycompat 1970-01-01 00:00:00 +0000 | |||
380 | @@ -1,1 +0,0 @@ | |||
381 | 1 | 2 | ||
382 | 2 | 0 | ||
383 | === modified file 'debian/rules' | |||
384 | --- debian/rules 2010-09-23 17:13:19 +0000 | |||
385 | +++ debian/rules 2011-01-07 23:06:32 +0000 | |||
386 | @@ -1,5 +1,3 @@ | |||
387 | 1 | #!/usr/bin/make -f | 1 | #!/usr/bin/make -f |
392 | 2 | DEB_PYTHON_SYSTEM := pysupport | 2 | %: |
393 | 3 | 3 | dh $@ --with python2 | |
390 | 4 | include /usr/share/cdbs/1/rules/debhelper.mk | ||
391 | 5 | include /usr/share/cdbs/1/class/python-distutils.mk | ||
394 | 6 | 4 | ||
395 | === modified file 'magicicada/__init__.py' | |||
396 | --- magicicada/__init__.py 2010-08-21 18:54:31 +0000 | |||
397 | +++ magicicada/__init__.py 2011-01-07 23:06:32 +0000 | |||
398 | @@ -16,7 +16,7 @@ | |||
399 | 16 | # You should have received a copy of the GNU General Public License along | 16 | # You should have received a copy of the GNU General Public License along |
400 | 17 | # with this program. If not, see <http://www.gnu.org/licenses/>. | 17 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
401 | 18 | 18 | ||
403 | 19 | """Magicicada.""" | 19 | """Magicicada GTK UI.""" |
404 | 20 | 20 | ||
405 | 21 | import logging | 21 | import logging |
406 | 22 | import os | 22 | import os |
407 | @@ -31,14 +31,15 @@ | |||
408 | 31 | # this shouldn't crash if not found as it is simply used for bug reporting | 31 | # this shouldn't crash if not found as it is simply used for bug reporting |
409 | 32 | try: | 32 | try: |
410 | 33 | import LaunchpadIntegration | 33 | import LaunchpadIntegration |
412 | 34 | launchpad_available = True | 34 | LAUNCHPAD_AVAILABLE = True |
413 | 35 | except ImportError: | 35 | except ImportError: |
415 | 36 | launchpad_available = False | 36 | LAUNCHPAD_AVAILABLE = False |
416 | 37 | 37 | ||
417 | 38 | from twisted.internet import gtk2reactor # for gtk-2.0 | 38 | from twisted.internet import gtk2reactor # for gtk-2.0 |
418 | 39 | gtk2reactor.install() | 39 | gtk2reactor.install() |
419 | 40 | 40 | ||
421 | 41 | from magicicada import syncdaemon, dbusiface, logger as logger_helper | 41 | from magicicada import syncdaemon, logger as logger_helper |
422 | 42 | from magicicada.dbusiface import NOT_SYNCHED_PATH | ||
423 | 42 | from magicicada.helpers import humanize_bytes, get_data_file, get_builder, \ | 43 | from magicicada.helpers import humanize_bytes, get_data_file, get_builder, \ |
424 | 43 | log, NO_OP | 44 | log, NO_OP |
425 | 44 | 45 | ||
426 | @@ -47,16 +48,11 @@ | |||
427 | 47 | UBUNTU_ONE_ROOT = os.path.expanduser('~/Ubuntu One') | 48 | UBUNTU_ONE_ROOT = os.path.expanduser('~/Ubuntu One') |
428 | 48 | 49 | ||
429 | 49 | # set up the logging for all the project | 50 | # set up the logging for all the project |
430 | 51 | # Instance of 'RootLogger' has no 'set_up' member | ||
431 | 52 | # pylint: disable=E1103 | ||
432 | 50 | logger_helper.set_up() | 53 | logger_helper.set_up() |
433 | 51 | logger = logging.getLogger('magicicada.ui') | 54 | logger = logging.getLogger('magicicada.ui') |
434 | 52 | 55 | ||
435 | 53 | DEBUG = os.getenv('DEBUG') | ||
436 | 54 | if DEBUG: | ||
437 | 55 | console = logging.StreamHandler() | ||
438 | 56 | console.setLevel(logging.DEBUG) | ||
439 | 57 | logger.addHandler(console) | ||
440 | 58 | |||
441 | 59 | |||
442 | 60 | # Instance of 'A' has no 'y' member | 56 | # Instance of 'A' has no 'y' member |
443 | 61 | # pylint: disable=E1101 | 57 | # pylint: disable=E1101 |
444 | 62 | 58 | ||
445 | @@ -81,7 +77,7 @@ | |||
446 | 81 | self.builder = get_builder('gui.glade') | 77 | self.builder = get_builder('gui.glade') |
447 | 82 | self.builder.connect_signals(self) | 78 | self.builder.connect_signals(self) |
448 | 83 | 79 | ||
450 | 84 | if launchpad_available: | 80 | if LAUNCHPAD_AVAILABLE: |
451 | 85 | # for more information about LaunchpadIntegration: | 81 | # for more information about LaunchpadIntegration: |
452 | 86 | # wiki.ubuntu.com/UbuntuDevelopment/Internationalisation/Coding | 82 | # wiki.ubuntu.com/UbuntuDevelopment/Internationalisation/Coding |
453 | 87 | helpmenu = self.builder.get_object('helpMenu') | 83 | helpmenu = self.builder.get_object('helpMenu') |
454 | @@ -105,6 +101,8 @@ | |||
455 | 105 | 'shares_to_others', 'shares_to_others_dialog', # shares_to_others | 101 | 'shares_to_others', 'shares_to_others_dialog', # shares_to_others |
456 | 106 | 'shares_to_others_view', 'shares_to_others_store', | 102 | 'shares_to_others_view', 'shares_to_others_store', |
457 | 107 | 'shares_to_others_close', 'metadata', # metadata | 103 | 'shares_to_others_close', 'metadata', # metadata |
458 | 104 | 'public_files', 'public_files_dialog', # public_files | ||
459 | 105 | 'public_files_view', 'public_files_store', 'public_files_close', | ||
460 | 108 | 'is_started', 'is_connected', 'is_online', # status bar images | 106 | 'is_started', 'is_connected', 'is_online', # status bar images |
461 | 109 | 'status_label', 'status_icon', # status label and systray icon | 107 | 'status_label', 'status_icon', # status label and systray icon |
462 | 110 | 'metaq_view', 'contentq_view', # queues tree views | 108 | 'metaq_view', 'contentq_view', # queues tree views |
463 | @@ -122,18 +120,18 @@ | |||
464 | 122 | self._make_view_sortable('folders') | 120 | self._make_view_sortable('folders') |
465 | 123 | self._make_view_sortable('shares_to_me') | 121 | self._make_view_sortable('shares_to_me') |
466 | 124 | self._make_view_sortable('shares_to_others') | 122 | self._make_view_sortable('shares_to_others') |
467 | 123 | self._make_view_sortable('public_files') | ||
468 | 125 | 124 | ||
469 | 126 | self.metadata_dialogs = {} | 125 | self.metadata_dialogs = {} |
470 | 127 | self.volumes = (self.folders, self.shares_to_me, self.shares_to_others) | 126 | self.volumes = (self.folders, self.shares_to_me, self.shares_to_others) |
471 | 128 | self.windows = (self.main_window, self.about_dialog, | ||
472 | 129 | self.folders_dialog, self.shares_to_me_dialog, | ||
473 | 130 | self.shares_to_others_dialog) | ||
474 | 131 | 127 | ||
480 | 132 | icon_filename = get_data_file('media', 'logo-016.png') | 128 | self._icons = {} |
481 | 133 | self._icon = gtk.gdk.pixbuf_new_from_file(icon_filename) | 129 | for size in (16, 32, 48, 64, 128): |
482 | 134 | self.status_icon.set_from_file(icon_filename) | 130 | icon_filename = get_data_file('media', 'logo-%.3i.png' % size) |
483 | 135 | for w in self.windows: | 131 | self._icons[size] = gtk.gdk.pixbuf_new_from_file(icon_filename) |
484 | 136 | w.set_icon(self._icon) | 132 | self.status_icon.set_from_pixbuf(self._icons[16]) |
485 | 133 | self.main_window.set_icon_list(*self._icons.values()) | ||
486 | 134 | gtk.window_set_default_icon_list(*self._icons.values()) | ||
487 | 137 | 135 | ||
488 | 138 | about_fname = get_data_file('media', 'logo-128.png') | 136 | about_fname = get_data_file('media', 'logo-128.png') |
489 | 139 | self.about_dialog.set_logo(gtk.gdk.pixbuf_new_from_file(about_fname)) | 137 | self.about_dialog.set_logo(gtk.gdk.pixbuf_new_from_file(about_fname)) |
490 | @@ -149,6 +147,9 @@ | |||
491 | 149 | self.sd.content_queue_changed_callback = self.on_content_queue_changed | 147 | self.sd.content_queue_changed_callback = self.on_content_queue_changed |
492 | 150 | self.sd.meta_queue_changed_callback = self.on_meta_queue_changed | 148 | self.sd.meta_queue_changed_callback = self.on_meta_queue_changed |
493 | 151 | self.sd.on_metadata_ready_callback = self.on_metadata_ready | 149 | self.sd.on_metadata_ready_callback = self.on_metadata_ready |
494 | 150 | self.sd.on_initial_data_ready_callback = self.on_initial_data_ready | ||
495 | 151 | self.sd.on_initial_online_data_ready_callback = \ | ||
496 | 152 | self.on_initial_online_data_ready | ||
497 | 152 | 153 | ||
498 | 153 | self.widget_is_visible = lambda w: w.get_property('visible') | 154 | self.widget_is_visible = lambda w: w.get_property('visible') |
499 | 154 | self.widget_enabled = lambda w: self.widget_is_visible(w) and \ | 155 | self.widget_enabled = lambda w: self.widget_is_visible(w) and \ |
500 | @@ -160,6 +161,8 @@ | |||
501 | 160 | store = getattr(self, '%s_store' % view_name) | 161 | store = getattr(self, '%s_store' % view_name) |
502 | 161 | view = getattr(self, '%s_view' % view_name) | 162 | view = getattr(self, '%s_view' % view_name) |
503 | 162 | self._sorting_order[store] = {} | 163 | self._sorting_order[store] = {} |
504 | 164 | # this enforces that the order that columns will be shown and sorted | ||
505 | 165 | # matches the order in the underlying model | ||
506 | 163 | for i, col in enumerate(view.get_columns()): | 166 | for i, col in enumerate(view.get_columns()): |
507 | 164 | col.set_clickable(True) | 167 | col.set_clickable(True) |
508 | 165 | col.connect('clicked', self.on_store_sort_column_changed, i, store) | 168 | col.connect('clicked', self.on_store_sort_column_changed, i, store) |
509 | @@ -175,7 +178,6 @@ | |||
510 | 175 | dialog.set_border_width(10) | 178 | dialog.set_border_width(10) |
511 | 176 | # gtk.WIN_POS_CENTER makes dialogs overlap | 179 | # gtk.WIN_POS_CENTER makes dialogs overlap |
512 | 177 | dialog.set_position(gtk.WIN_POS_MOUSE) | 180 | dialog.set_position(gtk.WIN_POS_MOUSE) |
513 | 178 | dialog.set_icon(self._icon) | ||
514 | 179 | 181 | ||
515 | 180 | close_button = dialog.action_area.get_children()[-1] | 182 | close_button = dialog.action_area.get_children()[-1] |
516 | 181 | close_button.connect('clicked', self.on_metadata_close_clicked, path) | 183 | close_button.connect('clicked', self.on_metadata_close_clicked, path) |
517 | @@ -222,9 +224,10 @@ | |||
518 | 222 | 224 | ||
519 | 223 | def on_stop_clicked(self, widget, data=None): | 225 | def on_stop_clicked(self, widget, data=None): |
520 | 224 | """Stop syncdaemon.""" | 226 | """Stop syncdaemon.""" |
523 | 225 | for v in self.volumes: | 227 | for volume in self.volumes: |
524 | 226 | v.set_sensitive(False) | 228 | volume.set_sensitive(False) |
525 | 227 | self.metadata.set_sensitive(False) | 229 | self.metadata.set_sensitive(False) |
526 | 230 | self.public_files.set_sensitive(False) | ||
527 | 228 | 231 | ||
528 | 229 | if self.widget_enabled(self.disconnect): | 232 | if self.widget_enabled(self.disconnect): |
529 | 230 | self.on_disconnect_clicked(self.disconnect) | 233 | self.on_disconnect_clicked(self.disconnect) |
530 | @@ -331,6 +334,24 @@ | |||
531 | 331 | dialog.spinner.show() | 334 | dialog.spinner.show() |
532 | 332 | dialog.show() | 335 | dialog.show() |
533 | 333 | 336 | ||
534 | 337 | def on_public_files_close_clicked(self, widget, data=None): | ||
535 | 338 | """Close the public_files dialog.""" | ||
536 | 339 | self.public_files_dialog.response(gtk.RESPONSE_CLOSE) | ||
537 | 340 | |||
538 | 341 | def on_public_files_clicked(self, widget, data=None): | ||
539 | 342 | """Show information about public files.""" | ||
540 | 343 | items = self.sd.public_files | ||
541 | 344 | if items is None: | ||
542 | 345 | items = {} | ||
543 | 346 | |||
544 | 347 | self.public_files_store.clear() | ||
545 | 348 | for item in items.itervalues(): | ||
546 | 349 | row = (item.path, item.public_url, item.volume, item.node) | ||
547 | 350 | self.public_files_store.append(row) | ||
548 | 351 | |||
549 | 352 | self.public_files_dialog.run() | ||
550 | 353 | self.public_files_dialog.hide() | ||
551 | 354 | |||
552 | 334 | def on_status_icon_activate(self, widget, data=None): | 355 | def on_status_icon_activate(self, widget, data=None): |
553 | 335 | """Systray icon was clicked.""" | 356 | """Systray icon was clicked.""" |
554 | 336 | if self.widget_is_visible(self.main_window): | 357 | if self.widget_is_visible(self.main_window): |
555 | @@ -370,10 +391,6 @@ | |||
556 | 370 | self._activate_indicator(self.is_started) | 391 | self._activate_indicator(self.is_started) |
557 | 371 | self.connect.set_sensitive(True) | 392 | self.connect.set_sensitive(True) |
558 | 372 | 393 | ||
559 | 373 | for v in self.volumes: | ||
560 | 374 | v.set_sensitive(True) | ||
561 | 375 | self.metadata.set_sensitive(True) | ||
562 | 376 | |||
563 | 377 | self._update_queues_and_status(self.sd.current_state) | 394 | self._update_queues_and_status(self.sd.current_state) |
564 | 378 | 395 | ||
565 | 379 | @log(logger) | 396 | @log(logger) |
566 | @@ -482,13 +499,25 @@ | |||
567 | 482 | dialog = self.metadata_dialogs[path] | 499 | dialog = self.metadata_dialogs[path] |
568 | 483 | dialog.spinner.hide() | 500 | dialog.spinner.hide() |
569 | 484 | dialog.view.show() | 501 | dialog.view.show() |
571 | 485 | if metadata == dbusiface.NOT_SYNCHED_PATH: | 502 | if metadata == NOT_SYNCHED_PATH: |
572 | 486 | # Metadata path doesn't exsit for syncdaemon | 503 | # Metadata path doesn't exsit for syncdaemon |
574 | 487 | text = dbusiface.NOT_SYNCHED_PATH | 504 | text = NOT_SYNCHED_PATH |
575 | 488 | else: | 505 | else: |
576 | 489 | text = '\n'.join('%s: %s' % i for i in metadata.iteritems()) | 506 | text = '\n'.join('%s: %s' % i for i in metadata.iteritems()) |
577 | 490 | dialog.view.get_buffer().set_text(text) | 507 | dialog.view.get_buffer().set_text(text) |
578 | 491 | 508 | ||
579 | 509 | @log(logger, level=logging.INFO) | ||
580 | 510 | def on_initial_data_ready(self): | ||
581 | 511 | """Initial data is now available in syncdaemon.""" | ||
582 | 512 | for volume in self.volumes: | ||
583 | 513 | volume.set_sensitive(True) | ||
584 | 514 | self.metadata.set_sensitive(True) | ||
585 | 515 | |||
586 | 516 | @log(logger, level=logging.INFO) | ||
587 | 517 | def on_initial_online_data_ready(self): | ||
588 | 518 | """Online initial data is now available in syncdaemon.""" | ||
589 | 519 | self.public_files.set_sensitive(True) | ||
590 | 520 | |||
591 | 492 | # custom | 521 | # custom |
592 | 493 | 522 | ||
593 | 494 | def _start_loading(self, what): | 523 | def _start_loading(self, what): |
594 | 495 | 524 | ||
595 | === modified file 'magicicada/dbusiface.py' | |||
596 | --- magicicada/dbusiface.py 2010-08-23 16:48:07 +0000 | |||
597 | +++ magicicada/dbusiface.py 2011-01-07 23:06:32 +0000 | |||
598 | @@ -27,7 +27,17 @@ | |||
599 | 27 | from dbus.mainloop.glib import DBusGMainLoop | 27 | from dbus.mainloop.glib import DBusGMainLoop |
600 | 28 | from twisted.internet import defer | 28 | from twisted.internet import defer |
601 | 29 | 29 | ||
603 | 30 | from ubuntuone.syncdaemon.tools import SyncDaemonTool | 30 | try: |
604 | 31 | # yes, they can be imported! pylint: disable=F0401,E0611 | ||
605 | 32 | from ubuntuone.platform.tools import SyncDaemonTool, DBusClient | ||
606 | 33 | from ubuntuone.platform.dbus_interface import DBUS_IFACE_PUBLIC_FILES_NAME | ||
607 | 34 | except ImportError: | ||
608 | 35 | # support for old structure (pre-Natty) | ||
609 | 36 | # yes, they can be imported! pylint: disable=F0401,E0611 | ||
610 | 37 | from ubuntuone.syncdaemon.tools import SyncDaemonTool, DBusClient | ||
611 | 38 | from ubuntuone.syncdaemon.dbus_interface import ( | ||
612 | 39 | DBUS_IFACE_PUBLIC_FILES_NAME, | ||
613 | 40 | ) | ||
614 | 31 | 41 | ||
615 | 32 | # log! | 42 | # log! |
616 | 33 | logger = logging.getLogger('magicicada.dbusiface') | 43 | logger = logging.getLogger('magicicada.dbusiface') |
617 | @@ -40,6 +50,8 @@ | |||
618 | 40 | ShareData = collections.namedtuple('ShareData', 'accepted access_level ' | 50 | ShareData = collections.namedtuple('ShareData', 'accepted access_level ' |
619 | 41 | 'free_bytes name node_id other_username ' | 51 | 'free_bytes name node_id other_username ' |
620 | 42 | 'other_visible_name path volume_id') | 52 | 'other_visible_name path volume_id') |
621 | 53 | PublicFilesData = collections.namedtuple('PublicFilesData', | ||
622 | 54 | 'volume node path public_url') | ||
623 | 43 | 55 | ||
624 | 44 | # regular expressions for parsing MetaQueue data | 56 | # regular expressions for parsing MetaQueue data |
625 | 45 | RE_OP_LISTDIR = re.compile("(ListDir)\(share_id=(.*?), node_id=(.*?), .*") | 57 | RE_OP_LISTDIR = re.compile("(ListDir)\(share_id=(.*?), node_id=(.*?), .*") |
626 | @@ -61,6 +73,15 @@ | |||
627 | 61 | NOT_SYNCHED_PATH = "Not a valid path!" | 73 | NOT_SYNCHED_PATH = "Not a valid path!" |
628 | 62 | 74 | ||
629 | 63 | 75 | ||
630 | 76 | # some exceptions | ||
631 | 77 | class ShareOperationError(Exception): | ||
632 | 78 | """Error on an operation on a share.""" | ||
633 | 79 | def __init__(self, share_id, error): | ||
634 | 80 | self.share_id = share_id | ||
635 | 81 | self.error = error | ||
636 | 82 | Exception.__init__(self) | ||
637 | 83 | |||
638 | 84 | |||
639 | 64 | def _is_retry_exception(err): | 85 | def _is_retry_exception(err): |
640 | 65 | """Check if the exception is a retry one.""" | 86 | """Check if the exception is a retry one.""" |
641 | 66 | if isinstance(err, dbus.exceptions.DBusException): | 87 | if isinstance(err, dbus.exceptions.DBusException): |
642 | @@ -97,6 +118,7 @@ | |||
643 | 97 | # magicicada's syncdaemon | 118 | # magicicada's syncdaemon |
644 | 98 | self.msd = msd | 119 | self.msd = msd |
645 | 99 | logger.info("DBus interface starting") | 120 | logger.info("DBus interface starting") |
646 | 121 | self._public_files_deferred = None | ||
647 | 100 | 122 | ||
648 | 101 | # set up dbus and related stuff | 123 | # set up dbus and related stuff |
649 | 102 | loop = DBusGMainLoop(set_as_default=True) | 124 | loop = DBusGMainLoop(set_as_default=True) |
650 | @@ -116,6 +138,9 @@ | |||
651 | 116 | (self._on_share_created, 'Shares', 'ShareCreated'), | 138 | (self._on_share_created, 'Shares', 'ShareCreated'), |
652 | 117 | (self._on_share_deleted, 'Shares', 'ShareDeleted'), | 139 | (self._on_share_deleted, 'Shares', 'ShareDeleted'), |
653 | 118 | (self._on_share_changed, 'Shares', 'ShareChanged'), | 140 | (self._on_share_changed, 'Shares', 'ShareChanged'), |
654 | 141 | (self._on_public_files_list, 'PublicFiles', 'PublicFilesList'), | ||
655 | 142 | (self._on_public_files_changed, 'PublicFiles', | ||
656 | 143 | 'PublicAccessChanged'), | ||
657 | 119 | ] | 144 | ] |
658 | 120 | self._dbus_matches = [] | 145 | self._dbus_matches = [] |
659 | 121 | for method, dbus_lastname, signal_name in _signals: | 146 | for method, dbus_lastname, signal_name in _signals: |
660 | @@ -164,7 +189,7 @@ | |||
661 | 164 | data = self._process_status(state) | 189 | data = self._process_status(state) |
662 | 165 | self.msd.on_sd_status_changed(*data) | 190 | self.msd.on_sd_status_changed(*data) |
663 | 166 | 191 | ||
665 | 167 | def _on_content_queue_changed(self, _): | 192 | def _on_content_queue_changed(self, _=None): |
666 | 168 | """Call the SD callback.""" | 193 | """Call the SD callback.""" |
667 | 169 | logger.info("Received Content Queue changed") | 194 | logger.info("Received Content Queue changed") |
668 | 170 | self.msd.on_sd_content_queue_changed() | 195 | self.msd.on_sd_content_queue_changed() |
669 | @@ -223,6 +248,57 @@ | |||
670 | 223 | logger.info("Received Share changed") | 248 | logger.info("Received Share changed") |
671 | 224 | self.msd.on_sd_shares_changed() | 249 | self.msd.on_sd_shares_changed() |
672 | 225 | 250 | ||
673 | 251 | def _on_public_files_changed(self, data): | ||
674 | 252 | """Call the SD callback.""" | ||
675 | 253 | logger.debug("Received Public Files changed: %s", data) | ||
676 | 254 | pf = PublicFilesData(volume=data['share_id'], node=data['node_id'], | ||
677 | 255 | path=data['path'], public_url=data['public_url']) | ||
678 | 256 | is_public = bool(data['is_public']) | ||
679 | 257 | self.msd.on_sd_public_files_changed(pf, is_public) | ||
680 | 258 | |||
681 | 259 | def _on_public_files_list(self, data): | ||
682 | 260 | """Call the SD callback.""" | ||
683 | 261 | logger.info("Received Public Files list (%d)", len(data)) | ||
684 | 262 | processed = [] | ||
685 | 263 | for d in data: | ||
686 | 264 | logger.debug(" Public Files data: %s", d) | ||
687 | 265 | p = PublicFilesData(volume=d['volume_id'], node=d['node_id'], | ||
688 | 266 | path=d['path'], public_url=d['public_url']) | ||
689 | 267 | processed.append(p) | ||
690 | 268 | |||
691 | 269 | # trigger the deferred if have one, else call the msd with the data | ||
692 | 270 | if self._public_files_deferred is None: | ||
693 | 271 | self.msd.on_sd_public_files_list(processed) | ||
694 | 272 | else: | ||
695 | 273 | d = self._public_files_deferred | ||
696 | 274 | self._public_files_deferred = None | ||
697 | 275 | d.callback(processed) | ||
698 | 276 | |||
699 | 277 | def get_public_files(self): | ||
700 | 278 | """Ask the Public Files info to syncdaemon.""" | ||
701 | 279 | client = DBusClient(self._bus, '/publicfiles', | ||
702 | 280 | DBUS_IFACE_PUBLIC_FILES_NAME) | ||
703 | 281 | |||
704 | 282 | # note that these callbacks do not come with the requested info, the | ||
705 | 283 | # method just will return None, and the real info will come later | ||
706 | 284 | # in a signal | ||
707 | 285 | |||
708 | 286 | def call_done(result): | ||
709 | 287 | """Call was succesful.""" | ||
710 | 288 | logger.debug("Public files asked ok.") | ||
711 | 289 | |||
712 | 290 | def call_error(error): | ||
713 | 291 | """Call was not succesful.""" | ||
714 | 292 | logger.error("Public files asked with error: %s", error) | ||
715 | 293 | |||
716 | 294 | client.call_method('get_public_files', | ||
717 | 295 | reply_handler=call_done, error_handler=call_error) | ||
718 | 296 | |||
719 | 297 | # return a stored deferred that will be triggered later with the signal | ||
720 | 298 | d = defer.Deferred() | ||
721 | 299 | self._public_files_deferred = d | ||
722 | 300 | return d | ||
723 | 301 | |||
724 | 226 | @retryable | 302 | @retryable |
725 | 227 | def get_content_queue(self): | 303 | def get_content_queue(self): |
726 | 228 | """Get the content queue from SDT.""" | 304 | """Get the content queue from SDT.""" |
727 | @@ -293,7 +369,7 @@ | |||
728 | 293 | """Use MetaQueue dictionary and prepare command data.""" | 369 | """Use MetaQueue dictionary and prepare command data.""" |
729 | 294 | if op in ('AccountInquiry', 'FreeSpaceInquiry', 'Query', 'ListShares', | 370 | if op in ('AccountInquiry', 'FreeSpaceInquiry', 'Query', 'ListShares', |
730 | 295 | 'GetPublicFiles', 'ListVolumes', 'ChangePublicAccess', | 371 | 'GetPublicFiles', 'ListVolumes', 'ChangePublicAccess', |
732 | 296 | 'AnswerShare'): | 372 | 'AnswerShare', 'GetDelta'): |
733 | 297 | return QueueData(operation=op, path=None, node=None, share=None) | 373 | return QueueData(operation=op, path=None, node=None, share=None) |
734 | 298 | 374 | ||
735 | 299 | if op in ('ListDir', 'Unlink'): | 375 | if op in ('ListDir', 'Unlink'): |
736 | @@ -473,3 +549,19 @@ | |||
737 | 473 | d = self.sync_daemon_tool.get_metadata(path) | 549 | d = self.sync_daemon_tool.get_metadata(path) |
738 | 474 | d.addCallbacks(process, fix_failure) | 550 | d.addCallbacks(process, fix_failure) |
739 | 475 | return d | 551 | return d |
740 | 552 | |||
741 | 553 | @retryable | ||
742 | 554 | @defer.inlineCallbacks | ||
743 | 555 | def accept_share(self, share_id): | ||
744 | 556 | """Accept a share.""" | ||
745 | 557 | logger.debug("Accept share %s started", share_id) | ||
746 | 558 | try: | ||
747 | 559 | result = yield self.sync_daemon_tool.accept_share(share_id) | ||
748 | 560 | except dbus.exceptions.DBusException, e: | ||
749 | 561 | error = str(e.args[0]) | ||
750 | 562 | logger.debug("Accept share %s crashed: %s", share_id, error) | ||
751 | 563 | raise ShareOperationError(share_id=share_id, error=error) | ||
752 | 564 | |||
753 | 565 | logger.debug("Accept share %s finished: %s", share_id, result) | ||
754 | 566 | if 'error' in result: | ||
755 | 567 | raise ShareOperationError(share_id=share_id, error=result['error']) | ||
756 | 476 | 568 | ||
757 | === modified file 'magicicada/helpers.py' | |||
758 | --- magicicada/helpers.py 2010-07-24 19:37:32 +0000 | |||
759 | +++ magicicada/helpers.py 2011-01-07 23:06:32 +0000 | |||
760 | @@ -1,21 +1,32 @@ | |||
761 | 1 | # -*- coding: utf-8 -*- | 1 | # -*- coding: utf-8 -*- |
765 | 2 | ### BEGIN LICENSE | 2 | # |
766 | 3 | # This file is in the public domain | 3 | # Author: Natalia Bidart <natalia.bidart@gmail.com> |
767 | 4 | ### END LICENSE | 4 | # |
768 | 5 | # Copyright 2010 Chicharreros | ||
769 | 6 | # | ||
770 | 7 | # This program is free software: you can redistribute it and/or modify it | ||
771 | 8 | # under the terms of the GNU General Public License version 3, as published | ||
772 | 9 | # by the Free Software Foundation. | ||
773 | 10 | # | ||
774 | 11 | # This program is distributed in the hope that it will be useful, but | ||
775 | 12 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
776 | 13 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
777 | 14 | # PURPOSE. See the GNU General Public License for more details. | ||
778 | 15 | # | ||
779 | 16 | # You should have received a copy of the GNU General Public License along | ||
780 | 17 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
781 | 5 | 18 | ||
782 | 6 | """Helpers for an Ubuntu application.""" | 19 | """Helpers for an Ubuntu application.""" |
783 | 7 | 20 | ||
784 | 8 | from __future__ import division | 21 | from __future__ import division |
785 | 9 | 22 | ||
791 | 10 | __all__ = [ | 23 | import logging |
787 | 11 | 'make_window', | ||
788 | 12 | ] | ||
789 | 13 | |||
790 | 14 | import gtk | ||
792 | 15 | import os | 24 | import os |
793 | 16 | 25 | ||
794 | 17 | from functools import wraps | 26 | from functools import wraps |
795 | 18 | 27 | ||
796 | 28 | import gtk | ||
797 | 29 | |||
798 | 19 | from magicicada.magicicadaconfig import get_data_file | 30 | from magicicada.magicicadaconfig import get_data_file |
799 | 20 | 31 | ||
800 | 21 | 32 | ||
801 | @@ -39,7 +50,7 @@ | |||
802 | 39 | return builder | 50 | return builder |
803 | 40 | 51 | ||
804 | 41 | 52 | ||
806 | 42 | def log(logger): | 53 | def log(logger, level=logging.DEBUG): |
807 | 43 | """Log input/ouput info for 'f' using 'logger'.""" | 54 | """Log input/ouput info for 'f' using 'logger'.""" |
808 | 44 | 55 | ||
809 | 45 | def decorator(f): | 56 | def decorator(f): |
810 | @@ -50,13 +61,14 @@ | |||
811 | 50 | """Wrap 'f', log input args and result using 'logger'.""" | 61 | """Wrap 'f', log input args and result using 'logger'.""" |
812 | 51 | name = f.__name__ | 62 | name = f.__name__ |
813 | 52 | result = None | 63 | result = None |
816 | 53 | logger.debug("Calling '%s' with args '%s' and kwargs '%s'.", | 64 | logger.log(level, "Calling '%s' with args '%s' and kwargs '%s'.", |
817 | 54 | name, args, kwargs) | 65 | name, args, kwargs) |
818 | 55 | try: | 66 | try: |
819 | 56 | result = f(*args, **kwargs) | 67 | result = f(*args, **kwargs) |
820 | 57 | except Exception: # pylint: disable=W0703 | 68 | except Exception: # pylint: disable=W0703 |
821 | 58 | logger.exception('%s failed with exception:', name) | 69 | logger.exception('%s failed with exception:', name) |
823 | 59 | logger.debug("Returning from '%s' with result '%s'.", name, result) | 70 | logger.log(level, "Returning from '%s' with result '%s'.", |
824 | 71 | name, result) | ||
825 | 60 | return result | 72 | return result |
826 | 61 | 73 | ||
827 | 62 | return inner | 74 | return inner |
828 | @@ -91,11 +103,11 @@ | |||
829 | 91 | '1.3 GB' | 103 | '1.3 GB' |
830 | 92 | """ | 104 | """ |
831 | 93 | abbrevs = ( | 105 | abbrevs = ( |
837 | 94 | (1<<50L, 'PB'), | 106 | (1 << 50, 'PB'), |
838 | 95 | (1<<40L, 'TB'), | 107 | (1 << 40, 'TB'), |
839 | 96 | (1<<30L, 'GB'), | 108 | (1 << 30, 'GB'), |
840 | 97 | (1<<20L, 'MB'), | 109 | (1 << 20, 'MB'), |
841 | 98 | (1<<10L, 'kB'), | 110 | (1 << 10, 'kB'), |
842 | 99 | (1, 'bytes')) | 111 | (1, 'bytes')) |
843 | 100 | 112 | ||
844 | 101 | if numbytes == 1: | 113 | if numbytes == 1: |
845 | 102 | 114 | ||
846 | === modified file 'magicicada/logger.py' | |||
847 | --- magicicada/logger.py 2010-08-18 21:17:20 +0000 | |||
848 | +++ magicicada/logger.py 2011-01-07 23:06:32 +0000 | |||
849 | @@ -26,6 +26,7 @@ | |||
850 | 26 | 26 | ||
851 | 27 | from logging.handlers import RotatingFileHandler | 27 | from logging.handlers import RotatingFileHandler |
852 | 28 | 28 | ||
853 | 29 | import twisted.python.log | ||
854 | 29 | import xdg.BaseDirectory | 30 | import xdg.BaseDirectory |
855 | 30 | 31 | ||
856 | 31 | 32 | ||
857 | @@ -37,21 +38,35 @@ | |||
858 | 37 | self.doRollover() | 38 | self.doRollover() |
859 | 38 | 39 | ||
860 | 39 | 40 | ||
861 | 41 | def deferror_handler(data): | ||
862 | 42 | """Deferred error handler. | ||
863 | 43 | |||
864 | 44 | We receive all stuff here, filter the errors and use correct info. Note | ||
865 | 45 | that we don't send to stderr as Twisted already does that. | ||
866 | 46 | """ | ||
867 | 47 | try: | ||
868 | 48 | failure = data['failure'] | ||
869 | 49 | except KeyError: | ||
870 | 50 | msg = data['message'] | ||
871 | 51 | else: | ||
872 | 52 | msg = failure.getTraceback() | ||
873 | 53 | logger = logging.getLogger('magicicada') | ||
874 | 54 | logger.error("Unhandled error in deferred!\n%s", msg) | ||
875 | 55 | |||
876 | 56 | |||
877 | 40 | def exception_handler(exc_type, exc_value, tb): | 57 | def exception_handler(exc_type, exc_value, tb): |
878 | 41 | """Handle an unhandled exception.""" | 58 | """Handle an unhandled exception.""" |
879 | 42 | # stderr | ||
880 | 43 | exception = traceback.format_exception(exc_type, exc_value, tb) | 59 | exception = traceback.format_exception(exc_type, exc_value, tb) |
883 | 44 | exception = "".join(exception) | 60 | msg = "".join(exception) |
884 | 45 | print >> sys.stderr, exception | 61 | print >> sys.stderr, msg |
885 | 46 | 62 | ||
886 | 47 | # log | 63 | # log |
887 | 48 | logger = logging.getLogger('magicicada') | 64 | logger = logging.getLogger('magicicada') |
889 | 49 | logger.error("Unhandled exception!\n%s", exception) | 65 | logger.error("Unhandled exception!\n%s", msg) |
890 | 50 | 66 | ||
891 | 51 | 67 | ||
892 | 52 | def set_up(): | 68 | def set_up(): |
893 | 53 | """Set up the logging.""" | 69 | """Set up the logging.""" |
894 | 54 | |||
895 | 55 | # choose the folder to store the logs | 70 | # choose the folder to store the logs |
896 | 56 | cache = xdg.BaseDirectory.xdg_cache_home | 71 | cache = xdg.BaseDirectory.xdg_cache_home |
897 | 57 | logfolder = os.path.join(cache, 'magicicada') | 72 | logfolder = os.path.join(cache, 'magicicada') |
898 | @@ -62,11 +77,20 @@ | |||
899 | 62 | logger = logging.getLogger('magicicada') | 77 | logger = logging.getLogger('magicicada') |
900 | 63 | handler = CustomRotatingFH(logfile, maxBytes=1e6, backupCount=10) | 78 | handler = CustomRotatingFH(logfile, maxBytes=1e6, backupCount=10) |
901 | 64 | logger.addHandler(handler) | 79 | logger.addHandler(handler) |
905 | 65 | formatter = logging.Formatter("%(asctime)s %(name)-23s" | 80 | formatter = logging.Formatter("%(asctime)s %(name)-23s" |
906 | 66 | "%(levelname)-8s %(message)s", | 81 | "%(levelname)-8s %(message)s") |
904 | 67 | '%Y-%m-%d %H:%M:%S') | ||
907 | 68 | handler.setFormatter(formatter) | 82 | handler.setFormatter(formatter) |
909 | 69 | logger.setLevel(logging.DEBUG) | 83 | logger.setLevel(logging.INFO) |
910 | 84 | |||
911 | 85 | if os.getenv('DEBUG'): | ||
912 | 86 | console = logging.StreamHandler() | ||
913 | 87 | console.setLevel(logging.DEBUG) | ||
914 | 88 | console.setFormatter(formatter) | ||
915 | 89 | logger.addHandler(console) | ||
916 | 90 | logger.setLevel(logging.DEBUG) | ||
917 | 70 | 91 | ||
918 | 71 | # hook the exception handler | 92 | # hook the exception handler |
919 | 72 | sys.excepthook = exception_handler | 93 | sys.excepthook = exception_handler |
920 | 94 | |||
921 | 95 | # hook the twisted observer | ||
922 | 96 | twisted.python.log.addObserver(deferror_handler) | ||
923 | 73 | 97 | ||
924 | === modified file 'magicicada/syncdaemon.py' | |||
925 | --- magicicada/syncdaemon.py 2010-09-02 18:23:02 +0000 | |||
926 | +++ magicicada/syncdaemon.py 2011-01-07 23:06:32 +0000 | |||
927 | @@ -23,13 +23,24 @@ | |||
928 | 23 | 23 | ||
929 | 24 | from twisted.internet import defer, reactor | 24 | from twisted.internet import defer, reactor |
930 | 25 | 25 | ||
932 | 26 | from magicicada.dbusiface import DBusInterface | 26 | from magicicada.dbusiface import DBusInterface, ShareOperationError |
933 | 27 | from magicicada.helpers import NO_OP | 27 | from magicicada.helpers import NO_OP |
934 | 28 | 28 | ||
935 | 29 | |||
936 | 30 | # log! | 29 | # log! |
937 | 31 | logger = logging.getLogger('magicicada.syncdaemon') | 30 | logger = logging.getLogger('magicicada.syncdaemon') |
938 | 32 | 31 | ||
939 | 32 | # states for MQ and CQ handling bursts | ||
940 | 33 | ASKING_IDLE, ASKING_YES, ASKING_LATER = range(3) | ||
941 | 34 | |||
942 | 35 | |||
943 | 36 | def mandatory_callback(function_name): | ||
944 | 37 | """Log that the callback was not overwritten.""" | ||
945 | 38 | def f(*a, **k): | ||
946 | 39 | """Fake callback.""" | ||
947 | 40 | logger.warning("Callback called but was not assigned! " | ||
948 | 41 | "%r called with %s %s", function_name, a, k) | ||
949 | 42 | return f | ||
950 | 43 | |||
951 | 33 | 44 | ||
952 | 34 | class State(object): | 45 | class State(object): |
953 | 35 | """Hold the state of SD.""" | 46 | """Hold the state of SD.""" |
954 | @@ -93,6 +104,7 @@ | |||
955 | 93 | self.shares_to_others = None | 104 | self.shares_to_others = None |
956 | 94 | self.content_queue = None | 105 | self.content_queue = None |
957 | 95 | self.meta_queue = None | 106 | self.meta_queue = None |
958 | 107 | self.public_files = None | ||
959 | 96 | 108 | ||
960 | 97 | # callbacks for GUI to hook in | 109 | # callbacks for GUI to hook in |
961 | 98 | self.status_changed_callback = NO_OP | 110 | self.status_changed_callback = NO_OP |
962 | @@ -107,8 +119,14 @@ | |||
963 | 107 | self.on_folders_changed_callback = NO_OP | 119 | self.on_folders_changed_callback = NO_OP |
964 | 108 | self.on_shares_to_me_changed_callback = NO_OP | 120 | self.on_shares_to_me_changed_callback = NO_OP |
965 | 109 | self.on_shares_to_others_changed_callback = NO_OP | 121 | self.on_shares_to_others_changed_callback = NO_OP |
967 | 110 | self.on_metadata_ready_callback = None # mandatory | 122 | self.on_metadata_ready_callback = mandatory_callback( |
968 | 123 | 'on_metadata_ready_callback') | ||
969 | 111 | self.on_initial_data_ready_callback = NO_OP | 124 | self.on_initial_data_ready_callback = NO_OP |
970 | 125 | self.on_initial_online_data_ready_callback = NO_OP | ||
971 | 126 | self.on_share_op_success_callback = mandatory_callback( | ||
972 | 127 | 'on_share_op_success_callback') | ||
973 | 128 | self.on_share_op_error_callback = mandatory_callback( | ||
974 | 129 | 'on_share_op_error_callback') | ||
975 | 112 | 130 | ||
976 | 113 | # mq needs to (maybe) be polled to know progress | 131 | # mq needs to (maybe) be polled to know progress |
977 | 114 | self._must_poll_mq = True | 132 | self._must_poll_mq = True |
978 | @@ -122,6 +140,10 @@ | |||
979 | 122 | else: | 140 | else: |
980 | 123 | self.current_state.set(is_started=False) | 141 | self.current_state.set(is_started=False) |
981 | 124 | 142 | ||
982 | 143 | # mq and cq burst handling | ||
983 | 144 | self._cq_asking = ASKING_IDLE | ||
984 | 145 | self._mq_asking = ASKING_IDLE | ||
985 | 146 | |||
986 | 125 | def shutdown(self): | 147 | def shutdown(self): |
987 | 126 | """Shut down the SyncDaemon.""" | 148 | """Shut down the SyncDaemon.""" |
988 | 127 | logger.info("SyncDaemon interface going down") | 149 | logger.info("SyncDaemon interface going down") |
989 | @@ -134,7 +156,7 @@ | |||
990 | 134 | @defer.inlineCallbacks | 156 | @defer.inlineCallbacks |
991 | 135 | def _get_initial_data(self): | 157 | def _get_initial_data(self): |
992 | 136 | """Get the initial SD data.""" | 158 | """Get the initial SD data.""" |
994 | 137 | logger.info("Getting initial data") | 159 | logger.info("Getting offline initial data") |
995 | 138 | 160 | ||
996 | 139 | status_data = yield self.dbus.get_status() | 161 | status_data = yield self.dbus.get_status() |
997 | 140 | self._send_status_changed(*status_data) | 162 | self._send_status_changed(*status_data) |
998 | @@ -150,10 +172,34 @@ | |||
999 | 150 | self.shares_to_me = yield self.dbus.get_shares_to_me() | 172 | self.shares_to_me = yield self.dbus.get_shares_to_me() |
1000 | 151 | self.shares_to_others = yield self.dbus.get_shares_to_others() | 173 | self.shares_to_others = yield self.dbus.get_shares_to_others() |
1001 | 152 | 174 | ||
1004 | 153 | # let frontend know that we have all the initial data | 175 | # let frontend know that we have all the initial offline data |
1005 | 154 | logger.info("All initial data is ready") | 176 | logger.info("All initial offline data is ready") |
1006 | 155 | self.on_initial_data_ready_callback() | 177 | self.on_initial_data_ready_callback() |
1007 | 156 | 178 | ||
1008 | 179 | logger.info("Getting online initial data") | ||
1009 | 180 | pf_list = yield self.dbus.get_public_files() | ||
1010 | 181 | self.on_sd_public_files_list(pf_list) | ||
1011 | 182 | |||
1012 | 183 | # let frontend know that we have all the initial online data | ||
1013 | 184 | logger.info("All initial online data is ready") | ||
1014 | 185 | self.on_initial_online_data_ready_callback() | ||
1015 | 186 | |||
1016 | 187 | def on_sd_public_files_list(self, data): | ||
1017 | 188 | """Set a new Public Files list.""" | ||
1018 | 189 | logger.info("Got new Public Files list (%d items)", len(data)) | ||
1019 | 190 | self.public_files = dict((d.node, d) for d in data) | ||
1020 | 191 | |||
1021 | 192 | def on_sd_public_files_changed(self, pf, is_public): | ||
1022 | 193 | """Update the Public Files list.""" | ||
1023 | 194 | logger.info("Change in Public Files list! is_public=%s (volume=%s " | ||
1024 | 195 | "node=%s url=%s path=%r)", is_public, pf.volume, pf.node, | ||
1025 | 196 | pf.public_url, pf.path) | ||
1026 | 197 | if is_public: | ||
1027 | 198 | self.public_files[pf.node] = pf | ||
1028 | 199 | else: | ||
1029 | 200 | # remove it if there | ||
1030 | 201 | self.public_files.pop(pf.node, None) | ||
1031 | 202 | |||
1032 | 157 | @defer.inlineCallbacks | 203 | @defer.inlineCallbacks |
1033 | 158 | def on_sd_shares_changed(self): | 204 | def on_sd_shares_changed(self): |
1034 | 159 | """Shares changed, ask for new information.""" | 205 | """Shares changed, ask for new information.""" |
1035 | @@ -234,19 +280,47 @@ | |||
1036 | 234 | @defer.inlineCallbacks | 280 | @defer.inlineCallbacks |
1037 | 235 | def on_sd_content_queue_changed(self): | 281 | def on_sd_content_queue_changed(self): |
1038 | 236 | """Content Queue changed, ask for new information.""" | 282 | """Content Queue changed, ask for new information.""" |
1045 | 237 | logger.info("SD Content Queue changed") | 283 | if self._cq_asking != ASKING_IDLE: |
1046 | 238 | new_cq = yield self.dbus.get_content_queue() | 284 | self._cq_asking = ASKING_LATER |
1047 | 239 | if new_cq != self.content_queue: | 285 | logger.info("SD Content Queue changed, but already asking") |
1048 | 240 | logger.info("Content Queue info is new! %d items", len(new_cq)) | 286 | return |
1049 | 241 | self.content_queue = new_cq | 287 | |
1050 | 242 | self.content_queue_changed_callback(new_cq) | 288 | logger.info("SD Content Queue changed, asking for info") |
1051 | 289 | self._cq_asking = ASKING_YES | ||
1052 | 290 | while self._cq_asking != ASKING_IDLE: | ||
1053 | 291 | new_cq = yield self.dbus.get_content_queue() | ||
1054 | 292 | if new_cq != self.content_queue: | ||
1055 | 293 | logger.info("Content Queue info is new! %d items", len(new_cq)) | ||
1056 | 294 | self.content_queue = new_cq | ||
1057 | 295 | self.content_queue_changed_callback(new_cq) | ||
1058 | 296 | if self._cq_asking == ASKING_LATER: | ||
1059 | 297 | self._cq_asking = ASKING_YES | ||
1060 | 298 | else: | ||
1061 | 299 | self._cq_asking = ASKING_IDLE | ||
1062 | 243 | 300 | ||
1063 | 244 | @defer.inlineCallbacks | 301 | @defer.inlineCallbacks |
1064 | 245 | def on_sd_meta_queue_changed(self): | 302 | def on_sd_meta_queue_changed(self): |
1065 | 246 | """Meta Queue changed, ask for new information.""" | 303 | """Meta Queue changed, ask for new information.""" |
1067 | 247 | logger.info("SD Meta Queue changed") | 304 | if self._mq_asking != ASKING_IDLE: |
1068 | 305 | self._mq_asking = ASKING_LATER | ||
1069 | 306 | logger.info("SD Meta Queue changed, but already asking") | ||
1070 | 307 | return | ||
1071 | 308 | |||
1072 | 309 | logger.info("SD Meta Queue changed, asking for info") | ||
1073 | 248 | self._must_poll_mq = False | 310 | self._must_poll_mq = False |
1075 | 249 | yield self._get_mq_data() | 311 | self._mq_asking = ASKING_YES |
1076 | 312 | while self._mq_asking != ASKING_IDLE: | ||
1077 | 313 | try: | ||
1078 | 314 | yield self._get_mq_data() | ||
1079 | 315 | except Exception: | ||
1080 | 316 | # on any error, leave the state sane | ||
1081 | 317 | self._mq_asking = ASKING_IDLE | ||
1082 | 318 | raise | ||
1083 | 319 | |||
1084 | 320 | if self._mq_asking == ASKING_LATER: | ||
1085 | 321 | self._mq_asking = ASKING_YES | ||
1086 | 322 | else: | ||
1087 | 323 | self._mq_asking = ASKING_IDLE | ||
1088 | 250 | 324 | ||
1089 | 251 | @defer.inlineCallbacks | 325 | @defer.inlineCallbacks |
1090 | 252 | def _get_mq_data(self): | 326 | def _get_mq_data(self): |
1091 | @@ -274,6 +348,8 @@ | |||
1092 | 274 | self._get_mq_data() | 348 | self._get_mq_data() |
1093 | 275 | 349 | ||
1094 | 276 | if self._must_poll_mq: | 350 | if self._must_poll_mq: |
1095 | 351 | # Module 'twisted.internet.reactor' has no 'callLater' member | ||
1096 | 352 | # pylint: disable=E1101 | ||
1097 | 277 | self._mqcaller = reactor.callLater(self._mq_poll_time, | 353 | self._mqcaller = reactor.callLater(self._mq_poll_time, |
1098 | 278 | self._check_mq) | 354 | self._check_mq) |
1099 | 279 | 355 | ||
1100 | @@ -300,8 +376,27 @@ | |||
1101 | 300 | 376 | ||
1102 | 301 | def get_metadata(self, path): | 377 | def get_metadata(self, path): |
1103 | 302 | """Get the metadata for given path.""" | 378 | """Get the metadata for given path.""" |
1104 | 303 | if self.on_metadata_ready_callback is None: | ||
1105 | 304 | raise ValueError("Missing the mandatory cback for get_metadata.") | ||
1106 | 305 | |||
1107 | 306 | d = self.dbus.get_metadata(os.path.realpath(path)) | 379 | d = self.dbus.get_metadata(os.path.realpath(path)) |
1108 | 307 | d.addCallback(lambda resp: self.on_metadata_ready_callback(path, resp)) | 380 | d.addCallback(lambda resp: self.on_metadata_ready_callback(path, resp)) |
1109 | 381 | |||
1110 | 382 | def accept_share(self, share_id): | ||
1111 | 383 | """Accept a share.""" | ||
1112 | 384 | def error(failure): | ||
1113 | 385 | """Operation failed.""" | ||
1114 | 386 | if failure.check(ShareOperationError): | ||
1115 | 387 | error = failure.value.error | ||
1116 | 388 | logger.info("Accepting share %s finished with error: %s", | ||
1117 | 389 | share_id, error) | ||
1118 | 390 | self.on_share_op_error_callback(share_id, error) | ||
1119 | 391 | else: | ||
1120 | 392 | logger.error("Unexpected error when accepting share %s: %s %s", | ||
1121 | 393 | share_id, failure.type, failure.value) | ||
1122 | 394 | |||
1123 | 395 | def success(_): | ||
1124 | 396 | """Operation finished ok.""" | ||
1125 | 397 | logger.info("Accepting share %s finished successfully", share_id) | ||
1126 | 398 | self.on_share_op_success_callback(share_id) | ||
1127 | 399 | |||
1128 | 400 | logger.info("Accepting share %s started", share_id) | ||
1129 | 401 | d = self.dbus.accept_share(share_id) | ||
1130 | 402 | d.addCallbacks(success, error) | ||
1131 | 308 | 403 | ||
1132 | === modified file 'magicicada/tests/helpers.py' | |||
1133 | --- magicicada/tests/helpers.py 2010-08-18 21:04:16 +0000 | |||
1134 | +++ magicicada/tests/helpers.py 2011-01-07 23:06:32 +0000 | |||
1135 | @@ -28,6 +28,7 @@ | |||
1136 | 28 | """Create the instance, and add a records attribute.""" | 28 | """Create the instance, and add a records attribute.""" |
1137 | 29 | logging.Handler.__init__(self, *args, **kwargs) | 29 | logging.Handler.__init__(self, *args, **kwargs) |
1138 | 30 | self.records = [] | 30 | self.records = [] |
1139 | 31 | self.debug = False | ||
1140 | 31 | 32 | ||
1141 | 32 | def emit(self, record): | 33 | def emit(self, record): |
1142 | 33 | """Just add the record to self.records.""" | 34 | """Just add the record to self.records.""" |
1143 | @@ -36,18 +37,26 @@ | |||
1144 | 36 | def check(self, level, *msgs): | 37 | def check(self, level, *msgs): |
1145 | 37 | """Check that something is logged.""" | 38 | """Check that something is logged.""" |
1146 | 38 | for rec in self.records: | 39 | for rec in self.records: |
1148 | 39 | if rec.levelname == level and all(m in rec.message for m in msgs): | 40 | if rec.levelno == level and all(m in rec.message for m in msgs): |
1149 | 40 | return True | 41 | return True |
1150 | 42 | if self.debug: | ||
1151 | 43 | recorded = [(logging.getLevelName(r.levelno), r.message) | ||
1152 | 44 | for r in self.records] | ||
1153 | 45 | print "Memento messages:", recorded | ||
1154 | 41 | return False | 46 | return False |
1155 | 42 | 47 | ||
1156 | 43 | def check_error(self, *msgs): | 48 | def check_error(self, *msgs): |
1157 | 44 | """Shortcut for ERROR check.""" | 49 | """Shortcut for ERROR check.""" |
1159 | 45 | return self.check('ERROR', *msgs) | 50 | return self.check(logging.ERROR, *msgs) |
1160 | 51 | |||
1161 | 52 | def check_warning(self, *msgs): | ||
1162 | 53 | """Shortcut for WARNING check.""" | ||
1163 | 54 | return self.check(logging.WARNING, *msgs) | ||
1164 | 46 | 55 | ||
1165 | 47 | def check_info(self, *msgs): | 56 | def check_info(self, *msgs): |
1166 | 48 | """Shortcut for INFO check.""" | 57 | """Shortcut for INFO check.""" |
1168 | 49 | return self.check('INFO', *msgs) | 58 | return self.check(logging.INFO, *msgs) |
1169 | 50 | 59 | ||
1170 | 51 | def check_debug(self, *msgs): | 60 | def check_debug(self, *msgs): |
1171 | 52 | """Shortcut for DEBUG check.""" | 61 | """Shortcut for DEBUG check.""" |
1173 | 53 | return self.check('DEBUG', *msgs) | 62 | return self.check(logging.DEBUG, *msgs) |
1174 | 54 | 63 | ||
1175 | === modified file 'magicicada/tests/test_dbusiface.py' | |||
1176 | --- magicicada/tests/test_dbusiface.py 2010-08-23 16:48:07 +0000 | |||
1177 | +++ magicicada/tests/test_dbusiface.py 2011-01-07 23:06:32 +0000 | |||
1178 | @@ -27,11 +27,38 @@ | |||
1179 | 27 | from magicicada import dbusiface | 27 | from magicicada import dbusiface |
1180 | 28 | from magicicada.tests.helpers import MementoHandler | 28 | from magicicada.tests.helpers import MementoHandler |
1181 | 29 | 29 | ||
1182 | 30 | try: | ||
1183 | 31 | # yes, they can be imported! pylint: disable=F0401,E0611 | ||
1184 | 32 | from ubuntuone.platform.dbus_interface import DBUS_IFACE_PUBLIC_FILES_NAME | ||
1185 | 33 | except ImportError: | ||
1186 | 34 | # support for old structure (pre-Natty) | ||
1187 | 35 | # yes, they can be imported! pylint: disable=F0401,E0611 | ||
1188 | 36 | from ubuntuone.syncdaemon.dbus_interface import ( | ||
1189 | 37 | DBUS_IFACE_PUBLIC_FILES_NAME, | ||
1190 | 38 | ) | ||
1191 | 39 | |||
1192 | 30 | 40 | ||
1193 | 31 | # It's ok to access private data in the test suite | 41 | # It's ok to access private data in the test suite |
1194 | 32 | # pylint: disable=W0212 | 42 | # pylint: disable=W0212 |
1195 | 33 | 43 | ||
1196 | 34 | 44 | ||
1197 | 45 | class FakeDBusClient(object): | ||
1198 | 46 | """Fake DBusClient, but with different behaviour at instantiation time.""" | ||
1199 | 47 | |||
1200 | 48 | def __init__(self, *args): | ||
1201 | 49 | self.init_args = None | ||
1202 | 50 | self.method_call_args = None | ||
1203 | 51 | |||
1204 | 52 | def call_method(self, *args, **kwargs): | ||
1205 | 53 | """Record the arguments of the method call.""" | ||
1206 | 54 | self.method_call_args = args, kwargs | ||
1207 | 55 | |||
1208 | 56 | def __call__(self, *args): | ||
1209 | 57 | """Simulate instantiation.""" | ||
1210 | 58 | self.init_args = args | ||
1211 | 59 | return self | ||
1212 | 60 | |||
1213 | 61 | |||
1214 | 35 | class FakeSessionBus(object): | 62 | class FakeSessionBus(object): |
1215 | 36 | """Fake Session Bus.""" | 63 | """Fake Session Bus.""" |
1216 | 37 | 64 | ||
1217 | @@ -59,6 +86,9 @@ | |||
1218 | 59 | else: | 86 | else: |
1219 | 60 | raise self.fake_name_owner | 87 | raise self.fake_name_owner |
1220 | 61 | 88 | ||
1221 | 89 | def send_message_with_reply(self, *a, **k): | ||
1222 | 90 | """Fake.""" | ||
1223 | 91 | |||
1224 | 62 | 92 | ||
1225 | 63 | class CallLoguer(object): | 93 | class CallLoguer(object): |
1226 | 64 | """Class that logs the methods called.""" | 94 | """Class that logs the methods called.""" |
1227 | @@ -104,6 +134,12 @@ | |||
1228 | 104 | 134 | ||
1229 | 105 | def setUp(self): | 135 | def setUp(self): |
1230 | 106 | """Set up.""" | 136 | """Set up.""" |
1231 | 137 | self.handler = MementoHandler() | ||
1232 | 138 | self.handler.setLevel(logging.DEBUG) | ||
1233 | 139 | logger = logging.getLogger('magicicada.dbusiface') | ||
1234 | 140 | logger.addHandler(self.handler) | ||
1235 | 141 | logger.setLevel(logging.DEBUG) | ||
1236 | 142 | |||
1237 | 107 | dbusiface.SessionBus = FakeSessionBus | 143 | dbusiface.SessionBus = FakeSessionBus |
1238 | 108 | dbusiface.SyncDaemonTool = FakeSDTool | 144 | dbusiface.SyncDaemonTool = FakeSDTool |
1239 | 109 | self.fsd = FakeSyncDaemon() | 145 | self.fsd = FakeSyncDaemon() |
1240 | @@ -199,6 +235,17 @@ | |||
1241 | 199 | self.assertEqual(self._get_hooked('Shares', 'ShareChanged'), | 235 | self.assertEqual(self._get_hooked('Shares', 'ShareChanged'), |
1242 | 200 | self.dbus._on_share_changed) | 236 | self.dbus._on_share_changed) |
1243 | 201 | 237 | ||
1244 | 238 | def test_public_files_list(self): | ||
1245 | 239 | """Test public files list callback.""" | ||
1246 | 240 | self.assertEqual(self._get_hooked('PublicFiles', 'PublicFilesList'), | ||
1247 | 241 | self.dbus._on_public_files_list) | ||
1248 | 242 | |||
1249 | 243 | def test_public_files_changed(self): | ||
1250 | 244 | """Test public files changed callback.""" | ||
1251 | 245 | self.assertEqual(self._get_hooked('PublicFiles', | ||
1252 | 246 | 'PublicAccessChanged'), | ||
1253 | 247 | self.dbus._on_public_files_changed) | ||
1254 | 248 | |||
1255 | 202 | 249 | ||
1256 | 203 | class TestSimpleCalls(SafeTests): | 250 | class TestSimpleCalls(SafeTests): |
1257 | 204 | """Tests for some simple calls.""" | 251 | """Tests for some simple calls.""" |
1258 | @@ -263,13 +310,13 @@ | |||
1259 | 263 | self.dbus._on_name_owner_changed("foo", "bar", "baz") | 310 | self.dbus._on_name_owner_changed("foo", "bar", "baz") |
1260 | 264 | self.get_msd_called(None) | 311 | self.get_msd_called(None) |
1261 | 265 | 312 | ||
1263 | 266 | def test_name_owner_changed_yes_syncdaemon_TF(self): | 313 | def test_name_owner_changed_yes_syncdaemon_true_false(self): |
1264 | 267 | """Test name owner changed callback.""" | 314 | """Test name owner changed callback.""" |
1265 | 268 | self.dbus._on_name_owner_changed("com.ubuntuone.SyncDaemon", "T", "") | 315 | self.dbus._on_name_owner_changed("com.ubuntuone.SyncDaemon", "T", "") |
1266 | 269 | rcv, = self.get_msd_called("on_sd_name_owner_changed") | 316 | rcv, = self.get_msd_called("on_sd_name_owner_changed") |
1267 | 270 | self.assertEqual(rcv, False) | 317 | self.assertEqual(rcv, False) |
1268 | 271 | 318 | ||
1270 | 272 | def test_name_owner_changed_yes_syncdaemon_FT(self): | 319 | def test_name_owner_changed_yes_syncdaemon_false_true(self): |
1271 | 273 | """Test name owner changed callback.""" | 320 | """Test name owner changed callback.""" |
1272 | 274 | self.dbus._on_name_owner_changed("com.ubuntuone.SyncDaemon", "", "T") | 321 | self.dbus._on_name_owner_changed("com.ubuntuone.SyncDaemon", "", "T") |
1273 | 275 | rcv, = self.get_msd_called("on_sd_name_owner_changed") | 322 | rcv, = self.get_msd_called("on_sd_name_owner_changed") |
1274 | @@ -279,11 +326,16 @@ | |||
1275 | 279 | class TestDataProcessingCQ(SafeTests): | 326 | class TestDataProcessingCQ(SafeTests): |
1276 | 280 | """Process CQ data before sending it to SyncDaemon.""" | 327 | """Process CQ data before sending it to SyncDaemon.""" |
1277 | 281 | 328 | ||
1280 | 282 | def test_content_queue_changed_signal(self): | 329 | def test_content_queue_changed_signal_withsomething(self): |
1281 | 283 | """Test content queue changed signal.""" | 330 | """Test content queue changed signal, old version with data.""" |
1282 | 284 | self.dbus._on_content_queue_changed(None) | 331 | self.dbus._on_content_queue_changed(None) |
1283 | 285 | self.get_msd_called("on_sd_content_queue_changed") | 332 | self.get_msd_called("on_sd_content_queue_changed") |
1284 | 286 | 333 | ||
1285 | 334 | def test_content_queue_changed_signal_nodata(self): | ||
1286 | 335 | """Test content queue changed signal new version (just the signal).""" | ||
1287 | 336 | self.dbus._on_content_queue_changed() | ||
1288 | 337 | self.get_msd_called("on_sd_content_queue_changed") | ||
1289 | 338 | |||
1290 | 287 | @defer.inlineCallbacks | 339 | @defer.inlineCallbacks |
1291 | 288 | def test_nodata(self): | 340 | def test_nodata(self): |
1292 | 289 | """Test with no data in the queue.""" | 341 | """Test with no data in the queue.""" |
1293 | @@ -371,7 +423,7 @@ | |||
1294 | 371 | self.assertEqual(data.node, None) | 423 | self.assertEqual(data.node, None) |
1295 | 372 | 424 | ||
1296 | 373 | @defer.inlineCallbacks | 425 | @defer.inlineCallbacks |
1298 | 374 | def test_GetPublicFiles_old(self): | 426 | def test_getpublicfiles_old(self): |
1299 | 375 | """Test meta with GetPublicFiles.""" | 427 | """Test meta with GetPublicFiles.""" |
1300 | 376 | cmd = 'GetPublicFiles' | 428 | cmd = 'GetPublicFiles' |
1301 | 377 | self.fake_sdt_response('waiting_metadata', [cmd]) | 429 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1302 | @@ -383,7 +435,7 @@ | |||
1303 | 383 | self.assertEqual(data.node, None) | 435 | self.assertEqual(data.node, None) |
1304 | 384 | 436 | ||
1305 | 385 | @defer.inlineCallbacks | 437 | @defer.inlineCallbacks |
1307 | 386 | def test_AccountInquiry_old(self): | 438 | def test_accountinquiry_old(self): |
1308 | 387 | """Test meta with AccountInquiry.""" | 439 | """Test meta with AccountInquiry.""" |
1309 | 388 | cmd = 'AccountInquiry' | 440 | cmd = 'AccountInquiry' |
1310 | 389 | self.fake_sdt_response('waiting_metadata', [cmd]) | 441 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1311 | @@ -395,7 +447,7 @@ | |||
1312 | 395 | self.assertEqual(data.node, None) | 447 | self.assertEqual(data.node, None) |
1313 | 396 | 448 | ||
1314 | 397 | @defer.inlineCallbacks | 449 | @defer.inlineCallbacks |
1316 | 398 | def test_FreeSpaceInquiry_old(self): | 450 | def test_freespaceinquiry_old(self): |
1317 | 399 | """Test meta with FreeSpaceInquiry.""" | 451 | """Test meta with FreeSpaceInquiry.""" |
1318 | 400 | cmd = 'FreeSpaceInquiry' | 452 | cmd = 'FreeSpaceInquiry' |
1319 | 401 | self.fake_sdt_response('waiting_metadata', [cmd]) | 453 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1320 | @@ -407,7 +459,7 @@ | |||
1321 | 407 | self.assertEqual(data.node, None) | 459 | self.assertEqual(data.node, None) |
1322 | 408 | 460 | ||
1323 | 409 | @defer.inlineCallbacks | 461 | @defer.inlineCallbacks |
1325 | 410 | def test_ListShares_old(self): | 462 | def test_listshares_old(self): |
1326 | 411 | """Test meta with ListShares.""" | 463 | """Test meta with ListShares.""" |
1327 | 412 | cmd = 'ListShares' | 464 | cmd = 'ListShares' |
1328 | 413 | self.fake_sdt_response('waiting_metadata', [cmd]) | 465 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1329 | @@ -419,7 +471,7 @@ | |||
1330 | 419 | self.assertEqual(data.node, None) | 471 | self.assertEqual(data.node, None) |
1331 | 420 | 472 | ||
1332 | 421 | @defer.inlineCallbacks | 473 | @defer.inlineCallbacks |
1334 | 422 | def test_ListVolumes_old(self): | 474 | def test_listvolumes_old(self): |
1335 | 423 | """Test meta with ListVolumes.""" | 475 | """Test meta with ListVolumes.""" |
1336 | 424 | cmd = 'ListVolumes' | 476 | cmd = 'ListVolumes' |
1337 | 425 | self.fake_sdt_response('waiting_metadata', [cmd]) | 477 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1338 | @@ -431,7 +483,7 @@ | |||
1339 | 431 | self.assertEqual(data.node, None) | 483 | self.assertEqual(data.node, None) |
1340 | 432 | 484 | ||
1341 | 433 | @defer.inlineCallbacks | 485 | @defer.inlineCallbacks |
1343 | 434 | def test_Query_old(self): | 486 | def test_query_old(self): |
1344 | 435 | """Test meta with Query.""" | 487 | """Test meta with Query.""" |
1345 | 436 | cmd = 'Query' | 488 | cmd = 'Query' |
1346 | 437 | self.fake_sdt_response('waiting_metadata', [cmd]) | 489 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1347 | @@ -443,7 +495,7 @@ | |||
1348 | 443 | self.assertEqual(data.node, None) | 495 | self.assertEqual(data.node, None) |
1349 | 444 | 496 | ||
1350 | 445 | @defer.inlineCallbacks | 497 | @defer.inlineCallbacks |
1352 | 446 | def test_ListDir_old(self): | 498 | def test_listdir_old(self): |
1353 | 447 | """Test meta with ListDir.""" | 499 | """Test meta with ListDir.""" |
1354 | 448 | cmd = 'ListDir(share_id=a, node_id=b, server_hash=c)' | 500 | cmd = 'ListDir(share_id=a, node_id=b, server_hash=c)' |
1355 | 449 | self.fake_sdt_response('waiting_metadata', [cmd]) | 501 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1356 | @@ -455,7 +507,7 @@ | |||
1357 | 455 | self.assertEqual(data.node, 'b') | 507 | self.assertEqual(data.node, 'b') |
1358 | 456 | 508 | ||
1359 | 457 | @defer.inlineCallbacks | 509 | @defer.inlineCallbacks |
1361 | 458 | def test_MakeDir_old(self): | 510 | def test_makedir_old(self): |
1362 | 459 | """Test meta with MakeDir.""" | 511 | """Test meta with MakeDir.""" |
1363 | 460 | cmd = 'MakeDir(share_id=a, parent_id=b, name=c, marker=d)' | 512 | cmd = 'MakeDir(share_id=a, parent_id=b, name=c, marker=d)' |
1364 | 461 | self.fake_sdt_response('waiting_metadata', [cmd]) | 513 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1365 | @@ -467,7 +519,7 @@ | |||
1366 | 467 | self.assertEqual(data.node, None) | 519 | self.assertEqual(data.node, None) |
1367 | 468 | 520 | ||
1368 | 469 | @defer.inlineCallbacks | 521 | @defer.inlineCallbacks |
1370 | 470 | def test_MakeFile_old(self): | 522 | def test_makefile_old(self): |
1371 | 471 | """Test meta with MakeFile.""" | 523 | """Test meta with MakeFile.""" |
1372 | 472 | cmd = 'MakeFile(share_id=a, parent_id=b, name=c, marker=d)' | 524 | cmd = 'MakeFile(share_id=a, parent_id=b, name=c, marker=d)' |
1373 | 473 | self.fake_sdt_response('waiting_metadata', [cmd]) | 525 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1374 | @@ -479,7 +531,7 @@ | |||
1375 | 479 | self.assertEqual(data.node, None) | 531 | self.assertEqual(data.node, None) |
1376 | 480 | 532 | ||
1377 | 481 | @defer.inlineCallbacks | 533 | @defer.inlineCallbacks |
1379 | 482 | def test_Unlink_old(self): | 534 | def test_unlink_old(self): |
1380 | 483 | """Test meta with Unlink.""" | 535 | """Test meta with Unlink.""" |
1381 | 484 | cmd = 'Unlink(share_id=a, node_id=b, server_hash=c)' | 536 | cmd = 'Unlink(share_id=a, node_id=b, server_hash=c)' |
1382 | 485 | self.fake_sdt_response('waiting_metadata', [cmd]) | 537 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1383 | @@ -491,7 +543,7 @@ | |||
1384 | 491 | self.assertEqual(data.node, 'b') | 543 | self.assertEqual(data.node, 'b') |
1385 | 492 | 544 | ||
1386 | 493 | @defer.inlineCallbacks | 545 | @defer.inlineCallbacks |
1388 | 494 | def test_Move_old(self): | 546 | def test_move_old(self): |
1389 | 495 | """Test meta with Move.""" | 547 | """Test meta with Move.""" |
1390 | 496 | cmd = 'Move(share_id=a, node_id=b, old_parent_id=c, '\ | 548 | cmd = 'Move(share_id=a, node_id=b, old_parent_id=c, '\ |
1391 | 497 | 'new_parent_id=d, new_name=e)' | 549 | 'new_parent_id=d, new_name=e)' |
1392 | @@ -504,7 +556,7 @@ | |||
1393 | 504 | self.assertEqual(data.node, 'b') | 556 | self.assertEqual(data.node, 'b') |
1394 | 505 | 557 | ||
1395 | 506 | @defer.inlineCallbacks | 558 | @defer.inlineCallbacks |
1397 | 507 | def test_ChangePublicAccess_old(self): | 559 | def test_changepublicaccess_old(self): |
1398 | 508 | """Test meta with ChangePublicAccess.""" | 560 | """Test meta with ChangePublicAccess.""" |
1399 | 509 | cmd = 'ChangePublicAccess' | 561 | cmd = 'ChangePublicAccess' |
1400 | 510 | self.fake_sdt_response('waiting_metadata', [cmd]) | 562 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1401 | @@ -516,7 +568,7 @@ | |||
1402 | 516 | self.assertEqual(data.node, None) | 568 | self.assertEqual(data.node, None) |
1403 | 517 | 569 | ||
1404 | 518 | @defer.inlineCallbacks | 570 | @defer.inlineCallbacks |
1406 | 519 | def test_AnswerShare_old(self): | 571 | def test_answershare_old(self): |
1407 | 520 | """Test meta with AnswerShare.""" | 572 | """Test meta with AnswerShare.""" |
1408 | 521 | cmd = 'AnswerShare' | 573 | cmd = 'AnswerShare' |
1409 | 522 | self.fake_sdt_response('waiting_metadata', [cmd]) | 574 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1410 | @@ -528,7 +580,7 @@ | |||
1411 | 528 | self.assertEqual(data.node, None) | 580 | self.assertEqual(data.node, None) |
1412 | 529 | 581 | ||
1413 | 530 | @defer.inlineCallbacks | 582 | @defer.inlineCallbacks |
1415 | 531 | def test_GetPublicFiles_dict(self): | 583 | def test_getpublicfiles_dict(self): |
1416 | 532 | """Test meta with GetPublicFiles.""" | 584 | """Test meta with GetPublicFiles.""" |
1417 | 533 | cmd = ('GetPublicFiles', {}) | 585 | cmd = ('GetPublicFiles', {}) |
1418 | 534 | self.fake_sdt_response('waiting_metadata', [cmd]) | 586 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1419 | @@ -540,7 +592,7 @@ | |||
1420 | 540 | self.assertEqual(data.node, None) | 592 | self.assertEqual(data.node, None) |
1421 | 541 | 593 | ||
1422 | 542 | @defer.inlineCallbacks | 594 | @defer.inlineCallbacks |
1424 | 543 | def test_AccountInquiry_dict(self): | 595 | def test_accountinquiry_dict(self): |
1425 | 544 | """Test meta with AccountInquiry.""" | 596 | """Test meta with AccountInquiry.""" |
1426 | 545 | cmd = ('AccountInquiry', {}) | 597 | cmd = ('AccountInquiry', {}) |
1427 | 546 | self.fake_sdt_response('waiting_metadata', [cmd]) | 598 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1428 | @@ -552,7 +604,7 @@ | |||
1429 | 552 | self.assertEqual(data.node, None) | 604 | self.assertEqual(data.node, None) |
1430 | 553 | 605 | ||
1431 | 554 | @defer.inlineCallbacks | 606 | @defer.inlineCallbacks |
1433 | 555 | def test_FreeSpaceInquiry_dict(self): | 607 | def test_freespaceinquiry_dict(self): |
1434 | 556 | """Test meta with FreeSpaceInquiry.""" | 608 | """Test meta with FreeSpaceInquiry.""" |
1435 | 557 | cmd = ('FreeSpaceInquiry', {}) | 609 | cmd = ('FreeSpaceInquiry', {}) |
1436 | 558 | self.fake_sdt_response('waiting_metadata', [cmd]) | 610 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1437 | @@ -564,7 +616,7 @@ | |||
1438 | 564 | self.assertEqual(data.node, None) | 616 | self.assertEqual(data.node, None) |
1439 | 565 | 617 | ||
1440 | 566 | @defer.inlineCallbacks | 618 | @defer.inlineCallbacks |
1442 | 567 | def test_ListShares_dict(self): | 619 | def test_listshares_dict(self): |
1443 | 568 | """Test meta with ListShares.""" | 620 | """Test meta with ListShares.""" |
1444 | 569 | cmd = ('ListShares', {}) | 621 | cmd = ('ListShares', {}) |
1445 | 570 | self.fake_sdt_response('waiting_metadata', [cmd]) | 622 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1446 | @@ -576,7 +628,7 @@ | |||
1447 | 576 | self.assertEqual(data.node, None) | 628 | self.assertEqual(data.node, None) |
1448 | 577 | 629 | ||
1449 | 578 | @defer.inlineCallbacks | 630 | @defer.inlineCallbacks |
1451 | 579 | def test_ListVolumes_dict(self): | 631 | def test_listvolumes_dict(self): |
1452 | 580 | """Test meta with ListVolumes.""" | 632 | """Test meta with ListVolumes.""" |
1453 | 581 | cmd = ('ListVolumes', {}) | 633 | cmd = ('ListVolumes', {}) |
1454 | 582 | self.fake_sdt_response('waiting_metadata', [cmd]) | 634 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1455 | @@ -588,7 +640,7 @@ | |||
1456 | 588 | self.assertEqual(data.node, None) | 640 | self.assertEqual(data.node, None) |
1457 | 589 | 641 | ||
1458 | 590 | @defer.inlineCallbacks | 642 | @defer.inlineCallbacks |
1460 | 591 | def test_Query_dict(self): | 643 | def test_query_dict(self): |
1461 | 592 | """Test meta with Query.""" | 644 | """Test meta with Query.""" |
1462 | 593 | cmd = ('Query', {}) | 645 | cmd = ('Query', {}) |
1463 | 594 | self.fake_sdt_response('waiting_metadata', [cmd]) | 646 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1464 | @@ -600,7 +652,7 @@ | |||
1465 | 600 | self.assertEqual(data.node, None) | 652 | self.assertEqual(data.node, None) |
1466 | 601 | 653 | ||
1467 | 602 | @defer.inlineCallbacks | 654 | @defer.inlineCallbacks |
1469 | 603 | def test_ListDir_dict(self): | 655 | def test_listdir_dict(self): |
1470 | 604 | """Test meta with ListDir.""" | 656 | """Test meta with ListDir.""" |
1471 | 605 | cmd = ('ListDir', dict(share_id='a', node_id='b', | 657 | cmd = ('ListDir', dict(share_id='a', node_id='b', |
1472 | 606 | server_hash='c', path='d')) | 658 | server_hash='c', path='d')) |
1473 | @@ -613,7 +665,7 @@ | |||
1474 | 613 | self.assertEqual(data.node, 'b') | 665 | self.assertEqual(data.node, 'b') |
1475 | 614 | 666 | ||
1476 | 615 | @defer.inlineCallbacks | 667 | @defer.inlineCallbacks |
1478 | 616 | def test_MakeDir_dict(self): | 668 | def test_makedir_dict(self): |
1479 | 617 | """Test meta with MakeDir.""" | 669 | """Test meta with MakeDir.""" |
1480 | 618 | cmd = ('MakeDir', dict(share_id='a', parent_id='b', | 670 | cmd = ('MakeDir', dict(share_id='a', parent_id='b', |
1481 | 619 | name='c', marker='d')) | 671 | name='c', marker='d')) |
1482 | @@ -626,7 +678,7 @@ | |||
1483 | 626 | self.assertEqual(data.node, None) | 678 | self.assertEqual(data.node, None) |
1484 | 627 | 679 | ||
1485 | 628 | @defer.inlineCallbacks | 680 | @defer.inlineCallbacks |
1487 | 629 | def test_MakeFile_dict(self): | 681 | def test_makefile_dict(self): |
1488 | 630 | """Test meta with MakeFile.""" | 682 | """Test meta with MakeFile.""" |
1489 | 631 | cmd = ('MakeFile', dict(share_id='a', parent_id='b', | 683 | cmd = ('MakeFile', dict(share_id='a', parent_id='b', |
1490 | 632 | name='c', marker='d')) | 684 | name='c', marker='d')) |
1491 | @@ -639,7 +691,7 @@ | |||
1492 | 639 | self.assertEqual(data.node, None) | 691 | self.assertEqual(data.node, None) |
1493 | 640 | 692 | ||
1494 | 641 | @defer.inlineCallbacks | 693 | @defer.inlineCallbacks |
1496 | 642 | def test_Unlink_dict(self): | 694 | def test_unlink_dict(self): |
1497 | 643 | """Test meta with Unlink.""" | 695 | """Test meta with Unlink.""" |
1498 | 644 | cmd = ('Unlink', dict(share_id='a', node_id='b', | 696 | cmd = ('Unlink', dict(share_id='a', node_id='b', |
1499 | 645 | server_hash='c', path='d')) | 697 | server_hash='c', path='d')) |
1500 | @@ -652,7 +704,7 @@ | |||
1501 | 652 | self.assertEqual(data.node, 'b') | 704 | self.assertEqual(data.node, 'b') |
1502 | 653 | 705 | ||
1503 | 654 | @defer.inlineCallbacks | 706 | @defer.inlineCallbacks |
1505 | 655 | def test_Move_dict(self): | 707 | def test_move_dict(self): |
1506 | 656 | """Test meta with Move.""" | 708 | """Test meta with Move.""" |
1507 | 657 | cmd = ('Move', dict(share_id='a', node_id='b', old_parent_id='c', | 709 | cmd = ('Move', dict(share_id='a', node_id='b', old_parent_id='c', |
1508 | 658 | new_parent_id='d', new_name='e', path='f')) | 710 | new_parent_id='d', new_name='e', path='f')) |
1509 | @@ -665,7 +717,7 @@ | |||
1510 | 665 | self.assertEqual(data.node, 'b') | 717 | self.assertEqual(data.node, 'b') |
1511 | 666 | 718 | ||
1512 | 667 | @defer.inlineCallbacks | 719 | @defer.inlineCallbacks |
1514 | 668 | def test_ChangePublicAccess_dict(self): | 720 | def test_changepublicaccess_dict(self): |
1515 | 669 | """Test meta with ChangePublicAccess.""" | 721 | """Test meta with ChangePublicAccess.""" |
1516 | 670 | cmd = ('ChangePublicAccess', {}) | 722 | cmd = ('ChangePublicAccess', {}) |
1517 | 671 | self.fake_sdt_response('waiting_metadata', [cmd]) | 723 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1518 | @@ -677,7 +729,7 @@ | |||
1519 | 677 | self.assertEqual(data.node, None) | 729 | self.assertEqual(data.node, None) |
1520 | 678 | 730 | ||
1521 | 679 | @defer.inlineCallbacks | 731 | @defer.inlineCallbacks |
1523 | 680 | def test_AnswerShare_dict(self): | 732 | def test_answershare_dict(self): |
1524 | 681 | """Test meta with AnswerShare.""" | 733 | """Test meta with AnswerShare.""" |
1525 | 682 | cmd = ('AnswerShare', {}) | 734 | cmd = ('AnswerShare', {}) |
1526 | 683 | self.fake_sdt_response('waiting_metadata', [cmd]) | 735 | self.fake_sdt_response('waiting_metadata', [cmd]) |
1527 | @@ -688,6 +740,18 @@ | |||
1528 | 688 | self.assertEqual(data.share, None) | 740 | self.assertEqual(data.share, None) |
1529 | 689 | self.assertEqual(data.node, None) | 741 | self.assertEqual(data.node, None) |
1530 | 690 | 742 | ||
1531 | 743 | @defer.inlineCallbacks | ||
1532 | 744 | def test_getdelta(self): | ||
1533 | 745 | """Test meta with AnswerShare.""" | ||
1534 | 746 | cmd = ('GetDelta', {}) | ||
1535 | 747 | self.fake_sdt_response('waiting_metadata', [cmd]) | ||
1536 | 748 | rcv = yield self.dbus.get_meta_queue() | ||
1537 | 749 | data = rcv[0] | ||
1538 | 750 | self.assertEqual(data.operation, 'GetDelta') | ||
1539 | 751 | self.assertEqual(data.path, None) | ||
1540 | 752 | self.assertEqual(data.share, None) | ||
1541 | 753 | self.assertEqual(data.node, None) | ||
1542 | 754 | |||
1543 | 691 | 755 | ||
1544 | 692 | class TestDataProcessingFolders(SafeTests): | 756 | class TestDataProcessingFolders(SafeTests): |
1545 | 693 | """Process Folders data before sending it to SyncDaemon.""" | 757 | """Process Folders data before sending it to SyncDaemon.""" |
1546 | @@ -910,6 +974,138 @@ | |||
1547 | 910 | self.assertEqual(share.volume_id, 'vol') | 974 | self.assertEqual(share.volume_id, 'vol') |
1548 | 911 | 975 | ||
1549 | 912 | 976 | ||
1550 | 977 | class TestPublicFiles(SafeTests): | ||
1551 | 978 | """PublicFiles data handling and related services.""" | ||
1552 | 979 | |||
1553 | 980 | def test_public_files_changed_yes(self): | ||
1554 | 981 | """Call the changed callback.""" | ||
1555 | 982 | d = dict(share_id='share', node_id='node', is_public='True', | ||
1556 | 983 | public_url='url', path='path') | ||
1557 | 984 | self.dbus._on_public_files_changed(d) | ||
1558 | 985 | pf, is_public = self.get_msd_called("on_sd_public_files_changed") | ||
1559 | 986 | self.assertEqual(pf.volume, 'share') | ||
1560 | 987 | self.assertEqual(pf.node, 'node') | ||
1561 | 988 | self.assertEqual(pf.public_url, 'url') | ||
1562 | 989 | self.assertEqual(pf.path, 'path') | ||
1563 | 990 | self.assertEqual(is_public, True) | ||
1564 | 991 | |||
1565 | 992 | def test_public_files_changed_no(self): | ||
1566 | 993 | """Call the changed callback.""" | ||
1567 | 994 | d = dict(share_id='share', node_id='node', is_public='', | ||
1568 | 995 | public_url='url', path='path') | ||
1569 | 996 | self.dbus._on_public_files_changed(d) | ||
1570 | 997 | pf, is_public = self.get_msd_called("on_sd_public_files_changed") | ||
1571 | 998 | self.assertEqual(pf.volume, 'share') | ||
1572 | 999 | self.assertEqual(pf.node, 'node') | ||
1573 | 1000 | self.assertEqual(pf.public_url, 'url') | ||
1574 | 1001 | self.assertEqual(pf.path, 'path') | ||
1575 | 1002 | self.assertEqual(is_public, False) | ||
1576 | 1003 | |||
1577 | 1004 | def test_public_files_empty(self): | ||
1578 | 1005 | """Call the callback without info.""" | ||
1579 | 1006 | self.dbus._on_public_files_list([]) | ||
1580 | 1007 | res, = self.get_msd_called("on_sd_public_files_list") | ||
1581 | 1008 | self.assertEqual(res, []) | ||
1582 | 1009 | |||
1583 | 1010 | def test_public_files_one(self): | ||
1584 | 1011 | """Call the callback with a public file.""" | ||
1585 | 1012 | d = dict(volume_id='volume', node_id='node', | ||
1586 | 1013 | public_url='url', path='path') | ||
1587 | 1014 | self.dbus._on_public_files_list([d]) | ||
1588 | 1015 | res, = self.get_msd_called("on_sd_public_files_list") | ||
1589 | 1016 | self.assertEqual(len(res), 1) | ||
1590 | 1017 | pf = res[0] | ||
1591 | 1018 | self.assertEqual(pf.volume, 'volume') | ||
1592 | 1019 | self.assertEqual(pf.node, 'node') | ||
1593 | 1020 | self.assertEqual(pf.public_url, 'url') | ||
1594 | 1021 | self.assertEqual(pf.path, 'path') | ||
1595 | 1022 | |||
1596 | 1023 | def test_public_files_more(self): | ||
1597 | 1024 | """Call the callback with a mixed content.""" | ||
1598 | 1025 | d1 = dict(volume_id='volume1', node_id='node1', | ||
1599 | 1026 | public_url='url1', path='path1') | ||
1600 | 1027 | d2 = dict(volume_id='volume2', node_id='node2', | ||
1601 | 1028 | public_url='url2', path='path2') | ||
1602 | 1029 | d3 = dict(volume_id='volume3', node_id='node3', | ||
1603 | 1030 | public_url='url3', path='path3') | ||
1604 | 1031 | self.dbus._on_public_files_list([d1, d2, d3]) | ||
1605 | 1032 | res, = self.get_msd_called("on_sd_public_files_list") | ||
1606 | 1033 | self.assertEqual(len(res), 3) | ||
1607 | 1034 | pf = res[0] | ||
1608 | 1035 | self.assertEqual(pf.volume, 'volume1') | ||
1609 | 1036 | self.assertEqual(pf.node, 'node1') | ||
1610 | 1037 | self.assertEqual(pf.public_url, 'url1') | ||
1611 | 1038 | self.assertEqual(pf.path, 'path1') | ||
1612 | 1039 | pf = res[1] | ||
1613 | 1040 | self.assertEqual(pf.volume, 'volume2') | ||
1614 | 1041 | self.assertEqual(pf.node, 'node2') | ||
1615 | 1042 | self.assertEqual(pf.public_url, 'url2') | ||
1616 | 1043 | self.assertEqual(pf.path, 'path2') | ||
1617 | 1044 | pf = res[2] | ||
1618 | 1045 | self.assertEqual(pf.volume, 'volume3') | ||
1619 | 1046 | self.assertEqual(pf.node, 'node3') | ||
1620 | 1047 | self.assertEqual(pf.public_url, 'url3') | ||
1621 | 1048 | self.assertEqual(pf.path, 'path3') | ||
1622 | 1049 | |||
1623 | 1050 | def test_getpublicfiles_deferred(self): | ||
1624 | 1051 | """The method should return a deferred and store it.""" | ||
1625 | 1052 | d = self.dbus.get_public_files() | ||
1626 | 1053 | self.assertTrue(isinstance(d, defer.Deferred)) | ||
1627 | 1054 | self.assertIdentical(d, self.dbus._public_files_deferred) | ||
1628 | 1055 | |||
1629 | 1056 | @defer.inlineCallbacks | ||
1630 | 1057 | def test_public_files_when_asked(self): | ||
1631 | 1058 | """If we receive the signal and data was asked, change behaviour.""" | ||
1632 | 1059 | # set up stuff | ||
1633 | 1060 | d = defer.Deferred() | ||
1634 | 1061 | self.dbus._public_files_deferred = d | ||
1635 | 1062 | |||
1636 | 1063 | # call the signal | ||
1637 | 1064 | self.dbus._on_public_files_list([]) | ||
1638 | 1065 | |||
1639 | 1066 | # check that the deferred was callbacked, and not the normal SD method | ||
1640 | 1067 | result = yield d | ||
1641 | 1068 | self.assertEqual(result, []) | ||
1642 | 1069 | self.assertTrue(self.fsd._called_method[0] is None) | ||
1643 | 1070 | |||
1644 | 1071 | def test_getpublicfiles_asktoclient_ok(self): | ||
1645 | 1072 | """The info was requested to the DBusClient, and was ok.""" | ||
1646 | 1073 | # inject a different DBusClient | ||
1647 | 1074 | fake_dbusclient = FakeDBusClient() | ||
1648 | 1075 | self.patch(dbusiface, 'DBusClient', fake_dbusclient) | ||
1649 | 1076 | |||
1650 | 1077 | # call, and check init and method args | ||
1651 | 1078 | self.dbus.get_public_files() | ||
1652 | 1079 | self.assertEqual(fake_dbusclient.init_args, (self.dbus._bus, | ||
1653 | 1080 | '/publicfiles', DBUS_IFACE_PUBLIC_FILES_NAME)) | ||
1654 | 1081 | (method_name,), kwargs = fake_dbusclient.method_call_args | ||
1655 | 1082 | done_cback = kwargs['reply_handler'] | ||
1656 | 1083 | self.assertEqual(method_name, 'get_public_files') | ||
1657 | 1084 | |||
1658 | 1085 | # trigger done callback and check logging | ||
1659 | 1086 | done_cback(None) | ||
1660 | 1087 | self.assertTrue(self.handler.check_debug("Public files asked ok.")) | ||
1661 | 1088 | |||
1662 | 1089 | def test_getpublicfiles_asktoclient_error(self): | ||
1663 | 1090 | """The info was requested to the DBusClient, and was not ok.""" | ||
1664 | 1091 | # inject a different DBusClient | ||
1665 | 1092 | fake_dbusclient = FakeDBusClient() | ||
1666 | 1093 | self.patch(dbusiface, 'DBusClient', fake_dbusclient) | ||
1667 | 1094 | |||
1668 | 1095 | # call, and check init and method args | ||
1669 | 1096 | self.dbus.get_public_files() | ||
1670 | 1097 | self.assertEqual(fake_dbusclient.init_args, (self.dbus._bus, | ||
1671 | 1098 | '/publicfiles', DBUS_IFACE_PUBLIC_FILES_NAME)) | ||
1672 | 1099 | (method_name,), kwargs = fake_dbusclient.method_call_args | ||
1673 | 1100 | error_cback = kwargs['error_handler'] | ||
1674 | 1101 | self.assertEqual(method_name, 'get_public_files') | ||
1675 | 1102 | |||
1676 | 1103 | # trigger error callback and check logging | ||
1677 | 1104 | error_cback('foo') | ||
1678 | 1105 | self.assertTrue(self.handler.check_error( | ||
1679 | 1106 | "Public files asked with error: foo")) | ||
1680 | 1107 | |||
1681 | 1108 | |||
1682 | 913 | class TestToolActions(SafeTests): | 1109 | class TestToolActions(SafeTests): |
1683 | 914 | """Actions against SD.tools. | 1110 | """Actions against SD.tools. |
1684 | 915 | 1111 | ||
1685 | @@ -944,8 +1140,10 @@ | |||
1686 | 944 | def setUp(self): | 1140 | def setUp(self): |
1687 | 945 | """Set up.""" | 1141 | """Set up.""" |
1688 | 946 | self.handler = MementoHandler() | 1142 | self.handler = MementoHandler() |
1689 | 947 | logging.getLogger('magicicada.dbusiface').addHandler(self.handler) | ||
1690 | 948 | self.handler.setLevel(logging.DEBUG) | 1143 | self.handler.setLevel(logging.DEBUG) |
1691 | 1144 | logger = logging.getLogger('magicicada.dbusiface') | ||
1692 | 1145 | logger.addHandler(self.handler) | ||
1693 | 1146 | logger.setLevel(logging.DEBUG) | ||
1694 | 949 | SafeTests.setUp(self) | 1147 | SafeTests.setUp(self) |
1695 | 950 | 1148 | ||
1696 | 951 | def test_instancing(self): | 1149 | def test_instancing(self): |
1697 | @@ -1052,13 +1250,13 @@ | |||
1698 | 1052 | self.assertTrue(self.handler.check_info("Received Name Owner changed")) | 1250 | self.assertTrue(self.handler.check_info("Received Name Owner changed")) |
1699 | 1053 | self.assertTrue(self.handler.check_debug("Name Owner data: u'' u'T'")) | 1251 | self.assertTrue(self.handler.check_debug("Name Owner data: u'' u'T'")) |
1700 | 1054 | 1252 | ||
1702 | 1055 | def test_name_owner_changed_yes_syncdaemon_TF(self): | 1253 | def test_name_owner_changed_yes_syncdaemon_true_false(self): |
1703 | 1056 | """Test name owner changed callback, SD value bad.""" | 1254 | """Test name owner changed callback, SD value bad.""" |
1704 | 1057 | self.dbus._on_name_owner_changed("com.ubuntuone.SyncDaemon", "F", "T") | 1255 | self.dbus._on_name_owner_changed("com.ubuntuone.SyncDaemon", "F", "T") |
1705 | 1058 | self.assertTrue(self.handler.check_info("Received Name Owner changed")) | 1256 | self.assertTrue(self.handler.check_info("Received Name Owner changed")) |
1706 | 1059 | self.assertTrue(self.handler.check_debug("Name Owner data: u'F' u'T'")) | 1257 | self.assertTrue(self.handler.check_debug("Name Owner data: u'F' u'T'")) |
1709 | 1060 | self.assertTrue(self.handler.check("ERROR", | 1258 | msg = "Name Owner invalid data: Same bool in old and new!" |
1710 | 1061 | "Name Owner invalid data: Same bool in old and new!")) | 1259 | self.assertTrue(self.handler.check_error(msg)) |
1711 | 1062 | 1260 | ||
1712 | 1063 | def test_folder_created_changed(self): | 1261 | def test_folder_created_changed(self): |
1713 | 1064 | """Test folder created changed callback.""" | 1262 | """Test folder created changed callback.""" |
1714 | @@ -1091,12 +1289,32 @@ | |||
1715 | 1091 | self.dbus._on_share_deleted("foo") | 1289 | self.dbus._on_share_deleted("foo") |
1716 | 1092 | self.assertTrue(self.handler.check_info("Received Share deleted")) | 1290 | self.assertTrue(self.handler.check_info("Received Share deleted")) |
1717 | 1093 | 1291 | ||
1719 | 1094 | def test_share__changed(self): | 1292 | def test_share_changed(self): |
1720 | 1095 | """Test share changed callback.""" | 1293 | """Test share changed callback.""" |
1721 | 1096 | self.dbus._on_share_changed("foo") | 1294 | self.dbus._on_share_changed("foo") |
1722 | 1097 | self.assertTrue(self.handler.check_info("Received Share changed")) | 1295 | self.assertTrue(self.handler.check_info("Received Share changed")) |
1723 | 1098 | 1296 | ||
1724 | 1099 | @defer.inlineCallbacks | 1297 | @defer.inlineCallbacks |
1725 | 1298 | def test_public_files_list(self): | ||
1726 | 1299 | """Test public files list callback.""" | ||
1727 | 1300 | d = dict(volume_id='volume', node_id='node', | ||
1728 | 1301 | public_url='url', path='path') | ||
1729 | 1302 | yield self.dbus._on_public_files_list([d]) | ||
1730 | 1303 | self.assertTrue(self.handler.check_info( | ||
1731 | 1304 | "Received Public Files list (1)")) | ||
1732 | 1305 | self.assertTrue(self.handler.check_debug( | ||
1733 | 1306 | " Public Files data: %s" % d)) | ||
1734 | 1307 | |||
1735 | 1308 | @defer.inlineCallbacks | ||
1736 | 1309 | def test_public_files_changed(self): | ||
1737 | 1310 | """Test public files changed callback.""" | ||
1738 | 1311 | d = dict(share_id='volume', node_id='node', is_public='', | ||
1739 | 1312 | public_url='url', path='path') | ||
1740 | 1313 | yield self.dbus._on_public_files_changed(d) | ||
1741 | 1314 | self.assertTrue(self.handler.check_debug( | ||
1742 | 1315 | "Received Public Files changed: %s" % d)) | ||
1743 | 1316 | |||
1744 | 1317 | @defer.inlineCallbacks | ||
1745 | 1100 | def test_content_queue_processing(self): | 1318 | def test_content_queue_processing(self): |
1746 | 1101 | """Test with one item in the queue.""" | 1319 | """Test with one item in the queue.""" |
1747 | 1102 | c = dict(operation='oper', path='path', share='share', node='node') | 1320 | c = dict(operation='oper', path='path', share='share', node='node') |
1748 | @@ -1270,3 +1488,52 @@ | |||
1749 | 1270 | d.addCallbacks(lambda _: deferred.errback(Exception()), | 1488 | d.addCallbacks(lambda _: deferred.errback(Exception()), |
1750 | 1271 | lambda _: deferred.callback(True)) | 1489 | lambda _: deferred.callback(True)) |
1751 | 1272 | return deferred | 1490 | return deferred |
1752 | 1491 | |||
1753 | 1492 | |||
1754 | 1493 | class TestHandlingShares(SafeTests): | ||
1755 | 1494 | """Handle shares.""" | ||
1756 | 1495 | |||
1757 | 1496 | @defer.inlineCallbacks | ||
1758 | 1497 | def test_accept_share_ok(self): | ||
1759 | 1498 | """Accepting share finishes ok.""" | ||
1760 | 1499 | d = dict(volume_id='foo', answer='bar') | ||
1761 | 1500 | self.fake_sdt_response('accept_share', d) | ||
1762 | 1501 | yield self.dbus.accept_share('foo') | ||
1763 | 1502 | self.assertTrue(self.handler.check_debug( | ||
1764 | 1503 | "Accept share foo started", "foo")) | ||
1765 | 1504 | self.assertTrue(self.handler.check_debug( | ||
1766 | 1505 | "Accept share foo finished", str(d))) | ||
1767 | 1506 | |||
1768 | 1507 | @defer.inlineCallbacks | ||
1769 | 1508 | def test_accept_share_bad(self): | ||
1770 | 1509 | """Accepting share finishes bad.""" | ||
1771 | 1510 | d = dict(volume_id='foo', answer='bar', error='baz') | ||
1772 | 1511 | self.fake_sdt_response('accept_share', d) | ||
1773 | 1512 | try: | ||
1774 | 1513 | yield self.dbus.accept_share('foo') | ||
1775 | 1514 | except dbusiface.ShareOperationError, e: | ||
1776 | 1515 | self.assertEqual(e.share_id, 'foo') | ||
1777 | 1516 | self.assertEqual(e.error, 'baz') | ||
1778 | 1517 | else: | ||
1779 | 1518 | raise Exception("Test should have raised an exception") | ||
1780 | 1519 | self.assertTrue(self.handler.check_debug( | ||
1781 | 1520 | "Accept share foo started", "foo")) | ||
1782 | 1521 | self.assertTrue(self.handler.check_debug( | ||
1783 | 1522 | "Accept share foo finished", str(d))) | ||
1784 | 1523 | |||
1785 | 1524 | @defer.inlineCallbacks | ||
1786 | 1525 | def test_accept_share_ugly_error(self): | ||
1787 | 1526 | """Accepting share went really bad.""" | ||
1788 | 1527 | e = dbus.exceptions.DBusException('ugly!') | ||
1789 | 1528 | self.fake_sdt_response('accept_share', e) | ||
1790 | 1529 | try: | ||
1791 | 1530 | yield self.dbus.accept_share('foo') | ||
1792 | 1531 | except dbusiface.ShareOperationError, e: | ||
1793 | 1532 | self.assertEqual(e.share_id, 'foo') | ||
1794 | 1533 | self.assertEqual(e.error, "ugly!") | ||
1795 | 1534 | else: | ||
1796 | 1535 | raise Exception("Test should have raised an exception") | ||
1797 | 1536 | self.assertTrue(self.handler.check_debug( | ||
1798 | 1537 | "Accept share foo started", "foo")) | ||
1799 | 1538 | self.assertTrue(self.handler.check_debug( | ||
1800 | 1539 | "Accept share foo crashed", "ugly!")) | ||
1801 | 1273 | 1540 | ||
1802 | === modified file 'magicicada/tests/test_logger.py' | |||
1803 | --- magicicada/tests/test_logger.py 2010-08-18 21:17:20 +0000 | |||
1804 | +++ magicicada/tests/test_logger.py 2011-01-07 23:06:32 +0000 | |||
1805 | @@ -23,7 +23,9 @@ | |||
1806 | 23 | import sys | 23 | import sys |
1807 | 24 | import unittest | 24 | import unittest |
1808 | 25 | 25 | ||
1810 | 26 | from magicicada.logger import exception_handler | 26 | from twisted.python import log, failure |
1811 | 27 | |||
1812 | 28 | from magicicada.logger import exception_handler, deferror_handler | ||
1813 | 27 | from magicicada.tests.helpers import MementoHandler | 29 | from magicicada.tests.helpers import MementoHandler |
1814 | 28 | 30 | ||
1815 | 29 | 31 | ||
1816 | @@ -81,3 +83,35 @@ | |||
1817 | 81 | shown = fh.getvalue() | 83 | shown = fh.getvalue() |
1818 | 82 | self.assertTrue("Traceback" in shown) | 84 | self.assertTrue("Traceback" in shown) |
1819 | 83 | self.assertTrue("ZeroDivisionError" in shown) | 85 | self.assertTrue("ZeroDivisionError" in shown) |
1820 | 86 | |||
1821 | 87 | |||
1822 | 88 | class DeferredTests(unittest.TestCase): | ||
1823 | 89 | """Error logging when it happened inside deferreds.""" | ||
1824 | 90 | |||
1825 | 91 | def test_observer_added(self): | ||
1826 | 92 | """Test that the observer was added to Twisted logging.""" | ||
1827 | 93 | self.assertTrue(deferror_handler in log.theLogPublisher.observers) | ||
1828 | 94 | |||
1829 | 95 | def test_noerror(self): | ||
1830 | 96 | """No error, no action.""" | ||
1831 | 97 | handler = MementoHandler() | ||
1832 | 98 | handler.setLevel(logging.DEBUG) | ||
1833 | 99 | deferror_handler(dict(isError=False, message='')) | ||
1834 | 100 | self.assertFalse(handler.check_error("error")) | ||
1835 | 101 | |||
1836 | 102 | def test_message(self): | ||
1837 | 103 | """Just a message.""" | ||
1838 | 104 | handler = MementoHandler() | ||
1839 | 105 | handler.setLevel(logging.DEBUG) | ||
1840 | 106 | deferror_handler(dict(isError=True, message="foobar")) | ||
1841 | 107 | self.assertFalse(handler.check_error("Unhandled error in deferred", | ||
1842 | 108 | "foobar")) | ||
1843 | 109 | |||
1844 | 110 | def test_failure(self): | ||
1845 | 111 | """Received a full failure.""" | ||
1846 | 112 | handler = MementoHandler() | ||
1847 | 113 | handler.setLevel(logging.DEBUG) | ||
1848 | 114 | f = failure.Failure(ValueError('foobar')) | ||
1849 | 115 | deferror_handler(dict(isError=True, failure=f, message='')) | ||
1850 | 116 | self.assertFalse(handler.check_error("Unhandled error in deferred", | ||
1851 | 117 | "ValueError", "foobar")) | ||
1852 | 84 | 118 | ||
1853 | === modified file 'magicicada/tests/test_magicicada.py' | |||
1854 | --- magicicada/tests/test_magicicada.py 2010-08-21 16:58:46 +0000 | |||
1855 | +++ magicicada/tests/test_magicicada.py 2011-01-07 23:06:32 +0000 | |||
1856 | @@ -32,9 +32,9 @@ | |||
1857 | 32 | from twisted.trial.unittest import TestCase | 32 | from twisted.trial.unittest import TestCase |
1858 | 33 | 33 | ||
1859 | 34 | from magicicada import MagicicadaUI, CONTENT_QUEUE, META_QUEUE, \ | 34 | from magicicada import MagicicadaUI, CONTENT_QUEUE, META_QUEUE, \ |
1861 | 35 | UBUNTU_ONE_ROOT, syncdaemon | 35 | NOT_SYNCHED_PATH, UBUNTU_ONE_ROOT, syncdaemon |
1862 | 36 | from magicicada.dbusiface import QueueData, FolderData, ShareData, \ | 36 | from magicicada.dbusiface import QueueData, FolderData, ShareData, \ |
1864 | 37 | NOT_SYNCHED_PATH | 37 | PublicFilesData |
1865 | 38 | from magicicada.helpers import NO_OP, humanize_bytes, get_data_file | 38 | from magicicada.helpers import NO_OP, humanize_bytes, get_data_file |
1866 | 39 | from magicicada.tests.helpers import MementoHandler | 39 | from magicicada.tests.helpers import MementoHandler |
1867 | 40 | 40 | ||
1868 | @@ -52,6 +52,25 @@ | |||
1869 | 52 | # pylint: disable=E1103 | 52 | # pylint: disable=E1103 |
1870 | 53 | 53 | ||
1871 | 54 | 54 | ||
1872 | 55 | SAMPLE_PUBLIC_FILES = { | ||
1873 | 56 | u'4d0ffa01-5375-4e67-bc47-d993b721d8af': | ||
1874 | 57 | PublicFilesData(volume=u'', | ||
1875 | 58 | node=u'4d0ffa01-5375-4e67-bc47-d993b721d8af', | ||
1876 | 59 | path=u'/home/user/Ubuntu One/test.png', | ||
1877 | 60 | public_url=u'http://ubuntuone.com/p/CUG/'), | ||
1878 | 61 | u'1be1ea29-426e-41b3-8853-5cce03b0c511': | ||
1879 | 62 | PublicFilesData(volume=u'', | ||
1880 | 63 | node=u'1be1ea29-426e-41b3-8853-5cce03b0c511', | ||
1881 | 64 | path=u'/home/user/Ubuntu One/public/text.txt', | ||
1882 | 65 | public_url=u'http://ubuntuone.com/p/6TX/'), | ||
1883 | 66 | u'8dba0484-86e9-4505-8fba-db3db7cc551d': | ||
1884 | 67 | PublicFilesData(volume=u'f5cc6b0d-85a0-4046-a72f-bd42c41538cb', | ||
1885 | 68 | node=u'8dba0484-86e9-4505-8fba-db3db7cc551d', | ||
1886 | 69 | path=u'/home/user/udf0/yadda.py', | ||
1887 | 70 | public_url=u'http://ubuntuone.com/p/U4R/'), | ||
1888 | 71 | } | ||
1889 | 72 | |||
1890 | 73 | |||
1891 | 55 | def process_gtk_pendings(): | 74 | def process_gtk_pendings(): |
1892 | 56 | """Process all gtk pending events.""" | 75 | """Process all gtk pending events.""" |
1893 | 57 | while gtk.events_pending(): | 76 | while gtk.events_pending(): |
1894 | @@ -101,8 +120,13 @@ | |||
1895 | 101 | self.content_queue_changed_callback = NO_OP | 120 | self.content_queue_changed_callback = NO_OP |
1896 | 102 | self.meta_queue_changed_callback = NO_OP | 121 | self.meta_queue_changed_callback = NO_OP |
1897 | 103 | self.on_metadata_ready_callback = None # mandatory | 122 | self.on_metadata_ready_callback = None # mandatory |
1898 | 123 | self.on_initial_data_ready_callback = NO_OP | ||
1899 | 124 | self.on_initial_online_data_ready_callback = NO_OP | ||
1900 | 104 | self.shutdown = NO_OP | 125 | self.shutdown = NO_OP |
1901 | 105 | 126 | ||
1902 | 127 | # Lambda may not be necessary | ||
1903 | 128 | # pylint: disable=W0108 | ||
1904 | 129 | |||
1905 | 106 | self.start = lambda: setattr(self.current_state, 'is_started', True) | 130 | self.start = lambda: setattr(self.current_state, 'is_started', True) |
1906 | 107 | self.quit = lambda: setattr(self.current_state, 'is_started', False) | 131 | self.quit = lambda: setattr(self.current_state, 'is_started', False) |
1907 | 108 | self.connect = lambda: setattr(self.current_state, | 132 | self.connect = lambda: setattr(self.current_state, |
1908 | @@ -116,6 +140,7 @@ | |||
1909 | 116 | """UI test cases for Magicicada UI.""" | 140 | """UI test cases for Magicicada UI.""" |
1910 | 117 | 141 | ||
1911 | 118 | TEST_FILE = get_data_file('tests', 'metadata-test.txt') | 142 | TEST_FILE = get_data_file('tests', 'metadata-test.txt') |
1912 | 143 | data_type = mapping = store = view = None | ||
1913 | 119 | 144 | ||
1914 | 120 | def setUp(self): | 145 | def setUp(self): |
1915 | 121 | """Init.""" | 146 | """Init.""" |
1916 | @@ -155,30 +180,6 @@ | |||
1917 | 155 | else: | 180 | else: |
1918 | 156 | return TestCase.__getattribute__(self, name) | 181 | return TestCase.__getattribute__(self, name) |
1919 | 157 | 182 | ||
1920 | 158 | if self._failed_test: | ||
1921 | 159 | # no, I'm not raising a bool. pylint: disable=E0702 | ||
1922 | 160 | raise self._failed_test | ||
1923 | 161 | |||
1924 | 162 | def __getattribute__(self, name): | ||
1925 | 163 | """Overwrite the assert methods with safer ones. | ||
1926 | 164 | |||
1927 | 165 | This way if a test called by gobject in the future fails, it | ||
1928 | 166 | makes the whole test suite fail. | ||
1929 | 167 | """ | ||
1930 | 168 | if name.startswith('assert') and hasattr(TestCase, name): | ||
1931 | 169 | |||
1932 | 170 | def proxy(*args, **kwargs): | ||
1933 | 171 | """Function that will call the real assert.""" | ||
1934 | 172 | real_assert = getattr(TestCase, name) | ||
1935 | 173 | try: | ||
1936 | 174 | real_assert(self, *args, **kwargs) | ||
1937 | 175 | except Exception, e: | ||
1938 | 176 | self._failed_test = e | ||
1939 | 177 | raise | ||
1940 | 178 | return proxy | ||
1941 | 179 | else: | ||
1942 | 180 | return TestCase.__getattribute__(self, name) | ||
1943 | 181 | |||
1944 | 182 | def do_start(self): | 183 | def do_start(self): |
1945 | 183 | """Simulate that start fully happened.""" | 184 | """Simulate that start fully happened.""" |
1946 | 184 | self.ui.on_start_clicked(self.ui.start) | 185 | self.ui.on_start_clicked(self.ui.start) |
1947 | @@ -190,41 +191,56 @@ | |||
1948 | 190 | self.ui.on_connect_clicked(self.ui.connect) | 191 | self.ui.on_connect_clicked(self.ui.connect) |
1949 | 191 | self.ui.on_connected() | 192 | self.ui.on_connected() |
1950 | 192 | 193 | ||
1952 | 193 | def build_some_data(self, data_type, limit=5): | 194 | def build_some_data(self, limit=5): |
1953 | 194 | """Build some data using named_tuple 'data_type'.""" | 195 | """Build some data using named_tuple 'data_type'.""" |
1955 | 195 | attrs = data_type._fields | 196 | assert self.data_type is not None, 'class muct provide a data_type' |
1956 | 197 | |||
1957 | 198 | attrs = self.data_type._fields | ||
1958 | 196 | result = [] | 199 | result = [] |
1959 | 197 | for i in xrange(limit): | 200 | for i in xrange(limit): |
1960 | 198 | kwargs = dict([(attr, '%s %i' % (attr, i)) for attr in attrs]) | 201 | kwargs = dict([(attr, '%s %i' % (attr, i)) for attr in attrs]) |
1962 | 199 | result.append(data_type(**kwargs)) | 202 | # self.data_type is not callable, pylint: disable=E1102 |
1963 | 203 | result.append(self.data_type(**kwargs)) | ||
1964 | 200 | return result | 204 | return result |
1965 | 201 | 205 | ||
1968 | 202 | def assert_store_correct(self, store, items, mapping, markup=None): | 206 | def assert_store_correct(self, items, markup=None): |
1969 | 203 | """Test that 'store' has 'items' as content.""" | 207 | """Test that 'self.store' has 'items' as content.""" |
1970 | 208 | assert self.mapping is not None, 'class must provide a mapping' | ||
1971 | 209 | assert self.store is not None, 'class must provide a store' | ||
1972 | 210 | |||
1973 | 204 | msg = 'amount of rows for %s must be %s (got %s).' | 211 | msg = 'amount of rows for %s must be %s (got %s).' |
1976 | 205 | self.assertEqual(len(store), len(items), | 212 | self.assertEqual(len(self.store), len(items), |
1977 | 206 | msg % (store, len(items), len(store))) | 213 | msg % (self.store, len(items), len(self.store))) |
1978 | 207 | 214 | ||
1979 | 208 | # assert rows content equal to items content | 215 | # assert rows content equal to items content |
1981 | 209 | tree_iter = store.get_iter_root() | 216 | tree_iter = self.store.get_iter_root() |
1982 | 210 | tmp = list(reversed(items)) | 217 | tmp = list(reversed(items)) |
1983 | 211 | do_markup = markup is not None | 218 | do_markup = markup is not None |
1984 | 212 | msg = "column %i ('%s') must be '%s' (got '%s' instead)" | 219 | msg = "column %i ('%s') must be '%s' (got '%s' instead)" |
1985 | 213 | while tree_iter is not None: | 220 | while tree_iter is not None: |
1986 | 214 | head = tmp.pop() | 221 | head = tmp.pop() |
1989 | 215 | for i, field in mapping: | 222 | for i, field in self.mapping: |
1990 | 216 | actual, = store.get(tree_iter, i) | 223 | actual, = self.store.get(tree_iter, i) |
1991 | 217 | expected = getattr(head, field) | 224 | expected = getattr(head, field) |
1993 | 218 | if store.get_column_type(i).name == 'gboolean': | 225 | if self.store.get_column_type(i).name == 'gboolean': |
1994 | 219 | expected = bool(expected) | 226 | expected = bool(expected) |
1995 | 220 | elif do_markup: | 227 | elif do_markup: |
1996 | 221 | expected = markup(expected) | 228 | expected = markup(expected) |
1997 | 222 | self.assertEqual(expected, actual, | 229 | self.assertEqual(expected, actual, |
1998 | 223 | msg % (i, field, expected, actual)) | 230 | msg % (i, field, expected, actual)) |
1999 | 224 | 231 | ||
2001 | 225 | tree_iter = store.iter_next(tree_iter) | 232 | tree_iter = self.store.iter_next(tree_iter) |
2002 | 226 | do_markup = False # only for first row | 233 | do_markup = False # only for first row |
2003 | 227 | 234 | ||
2004 | 235 | def debug_store(self): | ||
2005 | 236 | """Print the whole content of a store.""" | ||
2006 | 237 | store_iter = self.store.get_iter_root() | ||
2007 | 238 | columns = self.store.get_n_columns() | ||
2008 | 239 | print '\nShowing contents of store:', self.store | ||
2009 | 240 | while store_iter is not None: | ||
2010 | 241 | print self.store.get(store_iter, *range(columns)) | ||
2011 | 242 | store_iter = self.store.iter_next(store_iter) | ||
2012 | 243 | |||
2013 | 228 | def assert_indicator_disabled(self, indicator): | 244 | def assert_indicator_disabled(self, indicator): |
2014 | 229 | """Test that 'indicator' is not sensitive.""" | 245 | """Test that 'indicator' is not sensitive.""" |
2015 | 230 | self.assertFalse(indicator.is_sensitive(), | 246 | self.assertFalse(indicator.is_sensitive(), |
2016 | @@ -283,7 +299,41 @@ | |||
2017 | 283 | msg = '%s must not skip taskbar.' | 299 | msg = '%s must not skip taskbar.' |
2018 | 284 | self.assertFalse(dialog.get_skip_taskbar_hint(), msg % dialog_name) | 300 | self.assertFalse(dialog.get_skip_taskbar_hint(), msg % dialog_name) |
2019 | 285 | 301 | ||
2021 | 286 | self.assertEqual(dialog.get_icon(), self.ui._icon) | 302 | def assert_sort_order_correct(self, column, idx, expected_order): |
2022 | 303 | """Check that sort order is correctly set for 'self.store'.""" | ||
2023 | 304 | assert self.store is not None, 'class must provide a store' | ||
2024 | 305 | |||
2025 | 306 | msg0 = 'Store sort id must be %r (got %r instead).' | ||
2026 | 307 | msg1 = 'Store sort order must be %r (got %r instead).' | ||
2027 | 308 | msg3 = 'Column sort order must be %r (got %r instead).' | ||
2028 | 309 | |||
2029 | 310 | actual_id, actual_order = self.store.get_sort_column_id() | ||
2030 | 311 | |||
2031 | 312 | # store sort column id and order | ||
2032 | 313 | self.assertEqual(idx, actual_id, msg0 % (idx, actual_id)) | ||
2033 | 314 | self.assertEqual(expected_order, actual_order, | ||
2034 | 315 | msg1 % (expected_order, actual_order)) | ||
2035 | 316 | |||
2036 | 317 | # column sort order | ||
2037 | 318 | actual_order = column.get_sort_order() | ||
2038 | 319 | self.assertEqual(expected_order, actual_order, | ||
2039 | 320 | msg3 % (expected_order, actual_order)) | ||
2040 | 321 | |||
2041 | 322 | def assert_sort_indicator_correct(self, column): | ||
2042 | 323 | """Check that sort indicator is correctly set.""" | ||
2043 | 324 | assert self.view is not None, 'class must provide a view' | ||
2044 | 325 | |||
2045 | 326 | msg = 'Column %s must have sort indicator %s.' | ||
2046 | 327 | colname = column.get_name() | ||
2047 | 328 | # column sort indicator | ||
2048 | 329 | self.assertTrue(column.get_sort_indicator(), msg % (colname, 'on')) | ||
2049 | 330 | |||
2050 | 331 | # all the other columns must not have the sort indicator on | ||
2051 | 332 | for other_column in self.view.get_columns(): | ||
2052 | 333 | if other_column.get_name() == colname: | ||
2053 | 334 | continue | ||
2054 | 335 | self.assertFalse(other_column.get_sort_indicator(), | ||
2055 | 336 | msg % (other_column.get_name(), 'off')) | ||
2056 | 287 | 337 | ||
2057 | 288 | 338 | ||
2058 | 289 | class MagicicadaUIBasicTestCase(MagicicadaUITestCase): | 339 | class MagicicadaUIBasicTestCase(MagicicadaUITestCase): |
2059 | @@ -305,10 +355,16 @@ | |||
2060 | 305 | """UI can be created and main_window is visible.""" | 355 | """UI can be created and main_window is visible.""" |
2061 | 306 | self.assertTrue(self.ui.widget_is_visible(self.ui.main_window)) | 356 | self.assertTrue(self.ui.widget_is_visible(self.ui.main_window)) |
2062 | 307 | 357 | ||
2064 | 308 | def test_windows_have_correct_icon(self): | 358 | def test_main_window_have_correct_icon_list(self): |
2065 | 309 | """Every window has the icon set.""" | 359 | """Every window has the icon set.""" |
2068 | 310 | for w in self.ui.windows: | 360 | self.assertEqual(len(self.ui.main_window.get_icon_list()), |
2069 | 311 | self.assertEqual(w.get_icon(), self.ui._icon) | 361 | len(self.ui._icons.values())) |
2070 | 362 | |||
2071 | 363 | def test_every_window_has_correct_list(self): | ||
2072 | 364 | """The default icon list is set.""" | ||
2073 | 365 | self.patch(gtk, 'window_set_default_icon_list', self._set_called) | ||
2074 | 366 | MagicicadaUI(syncdaemon_class=FakedSyncdaemon) | ||
2075 | 367 | self.assertEqual(len(self._called[0]), len(self.ui._icons.values())) | ||
2076 | 312 | 368 | ||
2077 | 313 | def test_start_connect_are_visible(self): | 369 | def test_start_connect_are_visible(self): |
2078 | 314 | """Start and Connect buttons are visible.""" | 370 | """Start and Connect buttons are visible.""" |
2079 | @@ -456,6 +512,8 @@ | |||
2080 | 456 | """Abstratc UI test cases for queue tree views.""" | 512 | """Abstratc UI test cases for queue tree views.""" |
2081 | 457 | 513 | ||
2082 | 458 | name = None | 514 | name = None |
2083 | 515 | data_type = QueueData | ||
2084 | 516 | mapping = enumerate(['operation', 'path', 'share', 'node']) | ||
2085 | 459 | 517 | ||
2086 | 460 | def setUp(self): | 518 | def setUp(self): |
2087 | 461 | """Init.""" | 519 | """Init.""" |
2088 | @@ -468,15 +526,8 @@ | |||
2089 | 468 | 'on_%s_queue_changed' % self.name) | 526 | 'on_%s_queue_changed' % self.name) |
2090 | 469 | self.set_sd_queue = lambda q: \ | 527 | self.set_sd_queue = lambda q: \ |
2091 | 470 | setattr(self.ui.sd, '%s_queue' % self.name, q) | 528 | setattr(self.ui.sd, '%s_queue' % self.name, q) |
2101 | 471 | self.queue_store = getattr(self.ui, '%sq_store' % self.name) | 529 | self.store = getattr(self.ui, '%sq_store' % self.name) |
2102 | 472 | self.queue_view = getattr(self.ui, '%sq_view' % self.name) | 530 | self.view = getattr(self.ui, '%sq_view' % self.name) |
2094 | 473 | |||
2095 | 474 | def build_some_data(self, limit=5): | ||
2096 | 475 | """Build some data to act as queue data.""" | ||
2097 | 476 | kwargs = dict(data_type=QueueData, limit=limit) | ||
2098 | 477 | res = super(_MagicicadaUIQueueTestCase, self).build_some_data(**kwargs) | ||
2099 | 478 | # operation path share node | ||
2100 | 479 | return res | ||
2103 | 480 | 531 | ||
2104 | 481 | def expected_markup(self, value): | 532 | def expected_markup(self, value): |
2105 | 482 | """Return the markup for row at index 'i'.""" | 533 | """Return the markup for row at index 'i'.""" |
2106 | @@ -491,10 +542,9 @@ | |||
2107 | 491 | if must_highlight and value is not None else value | 542 | if must_highlight and value is not None else value |
2108 | 492 | return result | 543 | return result |
2109 | 493 | 544 | ||
2111 | 494 | def assert_store_correct(self, store, items): | 545 | def assert_store_correct(self, items): |
2112 | 495 | """Test that 'store' has 'items' as content.""" | 546 | """Test that 'store' has 'items' as content.""" |
2115 | 496 | mapping = enumerate(['operation', 'path', 'share', 'node']) | 547 | args = (items, self.expected_markup) |
2114 | 497 | args = (store, items, mapping, self.expected_markup) | ||
2116 | 498 | super(_MagicicadaUIQueueTestCase, self).assert_store_correct(*args) | 548 | super(_MagicicadaUIQueueTestCase, self).assert_store_correct(*args) |
2117 | 499 | 549 | ||
2118 | 500 | def assert_current_processing_row_is_different(self): | 550 | def assert_current_processing_row_is_different(self): |
2119 | @@ -508,8 +558,8 @@ | |||
2120 | 508 | markup = self.expected_markup | 558 | markup = self.expected_markup |
2121 | 509 | expected = tuple(markup(getattr(item, attr)) for attr in attrs) | 559 | expected = tuple(markup(getattr(item, attr)) for attr in attrs) |
2122 | 510 | 560 | ||
2125 | 511 | iter_root = self.queue_store.get_iter_root() | 561 | iter_root = self.store.get_iter_root() |
2126 | 512 | actual = self.queue_store.get(iter_root, *xrange(len(attrs))) | 562 | actual = self.store.get(iter_root, *xrange(len(attrs))) |
2127 | 513 | 563 | ||
2128 | 514 | msg = 'first row for %s queue must be %s (got %s instead)' % \ | 564 | msg = 'first row for %s queue must be %s (got %s instead)' % \ |
2129 | 515 | (self.name, expected, actual) | 565 | (self.name, expected, actual) |
2130 | @@ -524,30 +574,30 @@ | |||
2131 | 524 | @skip_abstract_class | 574 | @skip_abstract_class |
2132 | 525 | def test_model_is_binded(self): | 575 | def test_model_is_binded(self): |
2133 | 526 | """List store is binded.""" | 576 | """List store is binded.""" |
2135 | 527 | actual = self.queue_view.get_model() | 577 | actual = self.view.get_model() |
2136 | 528 | msg = 'model for view %s differs from %s' | 578 | msg = 'model for view %s differs from %s' |
2139 | 529 | self.assertEqual(self.queue_store, actual, | 579 | self.assertEqual(self.store, actual, |
2140 | 530 | msg % (self.name, self.queue_store)) | 580 | msg % (self.name, self.store)) |
2141 | 531 | 581 | ||
2142 | 532 | @skip_abstract_class | 582 | @skip_abstract_class |
2143 | 533 | def test_on_queue_changed_updates_view(self): | 583 | def test_on_queue_changed_updates_view(self): |
2144 | 534 | """On queue changed the view is updated.""" | 584 | """On queue changed the view is updated.""" |
2145 | 535 | items = self.build_some_data() | 585 | items = self.build_some_data() |
2146 | 536 | self.sd_changed(items) | 586 | self.sd_changed(items) |
2148 | 537 | self.assert_store_correct(self.queue_store, items) | 587 | self.assert_store_correct(items) |
2149 | 538 | 588 | ||
2150 | 539 | @skip_abstract_class | 589 | @skip_abstract_class |
2151 | 540 | def test_on_queue_changed_handles_none(self): | 590 | def test_on_queue_changed_handles_none(self): |
2152 | 541 | """On queue changed handles None as items.""" | 591 | """On queue changed handles None as items.""" |
2153 | 542 | self.sd_changed(None) | 592 | self.sd_changed(None) |
2155 | 543 | self.assert_store_correct(self.queue_store, []) | 593 | self.assert_store_correct([]) |
2156 | 544 | 594 | ||
2157 | 545 | @skip_abstract_class | 595 | @skip_abstract_class |
2158 | 546 | def test_on_queue_changed_handles_an_item_none(self): | 596 | def test_on_queue_changed_handles_an_item_none(self): |
2159 | 547 | """On queue changed handles None as items.""" | 597 | """On queue changed handles None as items.""" |
2160 | 548 | items = [QueueData(operation='Test', path='', share=None, node=None)] | 598 | items = [QueueData(operation='Test', path='', share=None, node=None)] |
2161 | 549 | self.sd_changed(items) | 599 | self.sd_changed(items) |
2163 | 550 | self.assert_store_correct(self.queue_store, items) | 600 | self.assert_store_correct(items) |
2164 | 551 | 601 | ||
2165 | 552 | @skip_abstract_class | 602 | @skip_abstract_class |
2166 | 553 | def test_model_is_cleared_before_updating(self): | 603 | def test_model_is_cleared_before_updating(self): |
2167 | @@ -557,17 +607,17 @@ | |||
2168 | 557 | 607 | ||
2169 | 558 | items = self.build_some_data() | 608 | items = self.build_some_data() |
2170 | 559 | self.sd_changed(items) | 609 | self.sd_changed(items) |
2172 | 560 | self.assertEqual(len(self.queue_store), len(items)) | 610 | self.assertEqual(len(self.store), len(items)) |
2173 | 561 | 611 | ||
2174 | 562 | @skip_abstract_class | 612 | @skip_abstract_class |
2175 | 563 | def test_view_is_enabled_if_disabled_on_changed(self): | 613 | def test_view_is_enabled_if_disabled_on_changed(self): |
2176 | 564 | """The tree view is enabled on changed if it was disabled.""" | 614 | """The tree view is enabled on changed if it was disabled.""" |
2178 | 565 | self.assertFalse(self.queue_view.is_sensitive(), | 615 | self.assertFalse(self.view.is_sensitive(), |
2179 | 566 | 'Tree view must be disabled by default.') | 616 | 'Tree view must be disabled by default.') |
2180 | 567 | items = self.build_some_data() | 617 | items = self.build_some_data() |
2181 | 568 | self.sd_changed(items) | 618 | self.sd_changed(items) |
2182 | 569 | 619 | ||
2184 | 570 | self.assertTrue(self.queue_view.is_sensitive(), | 620 | self.assertTrue(self.view.is_sensitive(), |
2185 | 571 | 'Tree view must be enabled on changed.') | 621 | 'Tree view must be enabled on changed.') |
2186 | 572 | 622 | ||
2187 | 573 | @skip_abstract_class | 623 | @skip_abstract_class |
2188 | @@ -578,7 +628,7 @@ | |||
2189 | 578 | 628 | ||
2190 | 579 | self.ui.update() | 629 | self.ui.update() |
2191 | 580 | 630 | ||
2193 | 581 | self.assert_store_correct(self.queue_store, data) | 631 | self.assert_store_correct(data) |
2194 | 582 | 632 | ||
2195 | 583 | @skip_abstract_class | 633 | @skip_abstract_class |
2196 | 584 | def test_on_stopped_updates_queue(self): | 634 | def test_on_stopped_updates_queue(self): |
2197 | @@ -594,7 +644,7 @@ | |||
2198 | 594 | label = '%sq_label' % self.name | 644 | label = '%sq_label' % self.name |
2199 | 595 | actual = getattr(self.ui, label).get_text() | 645 | actual = getattr(self.ui, label).get_text() |
2200 | 596 | expected = '%s Queue (%i)' % (self.name.capitalize(), | 646 | expected = '%s Queue (%i)' % (self.name.capitalize(), |
2202 | 597 | len(self.queue_store)) | 647 | len(self.store)) |
2203 | 598 | msg = '%s should be %s (got %s instead)' | 648 | msg = '%s should be %s (got %s instead)' |
2204 | 599 | self.assertEqual(expected, actual, msg % (label, expected, actual)) | 649 | self.assertEqual(expected, actual, msg % (label, expected, actual)) |
2205 | 600 | 650 | ||
2206 | @@ -608,13 +658,13 @@ | |||
2207 | 608 | items = self.build_some_data() | 658 | items = self.build_some_data() |
2208 | 609 | self.set_sd_queue(items) | 659 | self.set_sd_queue(items) |
2209 | 610 | self.do_start() | 660 | self.do_start() |
2211 | 611 | self.assert_store_correct(self.queue_store, items) | 661 | self.assert_store_correct(items) |
2212 | 612 | 662 | ||
2213 | 613 | items = items[:len(items) / 2] | 663 | items = items[:len(items) / 2] |
2214 | 614 | self.set_sd_queue(items) | 664 | self.set_sd_queue(items) |
2215 | 615 | self.ui.on_stop_clicked(self.ui.stop) | 665 | self.ui.on_stop_clicked(self.ui.stop) |
2216 | 616 | self.ui.on_stopped() | 666 | self.ui.on_stopped() |
2218 | 617 | self.assert_store_correct(self.queue_store, items) | 667 | self.assert_store_correct(items) |
2219 | 618 | 668 | ||
2220 | 619 | @skip_abstract_class | 669 | @skip_abstract_class |
2221 | 620 | def test_current_processing_row_is_different_if_online(self): | 670 | def test_current_processing_row_is_different_if_online(self): |
2222 | @@ -945,74 +995,40 @@ | |||
2223 | 945 | if self.name is None: | 995 | if self.name is None: |
2224 | 946 | return | 996 | return |
2225 | 947 | self.volume = getattr(self.ui, self.name) | 997 | self.volume = getattr(self.ui, self.name) |
2228 | 948 | self.volume_store = getattr(self.ui, '%s_store' % self.name) | 998 | self.store = getattr(self.ui, '%s_store' % self.name) |
2229 | 949 | self.volume_view = getattr(self.ui, '%s_view' % self.name) | 999 | self.view = getattr(self.ui, '%s_view' % self.name) |
2230 | 950 | self.volume_dialog_name = '%s_dialog' % self.name | 1000 | self.volume_dialog_name = '%s_dialog' % self.name |
2231 | 951 | self.volume_dialog = getattr(self.ui, self.volume_dialog_name) | 1001 | self.volume_dialog = getattr(self.ui, self.volume_dialog_name) |
2232 | 952 | self.on_volume_clicked = getattr(self.ui, 'on_%s_clicked' % self.name) | 1002 | self.on_volume_clicked = getattr(self.ui, 'on_%s_clicked' % self.name) |
2233 | 953 | self.volume_close = getattr(self.ui, '%s_close' % self.name) | 1003 | self.volume_close = getattr(self.ui, '%s_close' % self.name) |
2234 | 954 | 1004 | ||
2235 | 955 | def build_some_data(self, limit=5): | ||
2236 | 956 | """Build some data to act as volume.""" | ||
2237 | 957 | kwargs = dict(data_type=self.data_type, limit=limit) | ||
2238 | 958 | r = super(_MagicicadaUIVolumeTestCase, self).build_some_data(**kwargs) | ||
2239 | 959 | return r | ||
2240 | 960 | |||
2241 | 961 | def assert_store_correct(self, store, items): | ||
2242 | 962 | """Test that 'store' has 'items' as content.""" | ||
2243 | 963 | args = (store, items, self.mapping) | ||
2244 | 964 | super(_MagicicadaUIVolumeTestCase, self).assert_store_correct(*args) | ||
2245 | 965 | |||
2246 | 966 | def assert_widget_availability(self, enabled=True): | 1005 | def assert_widget_availability(self, enabled=True): |
2247 | 967 | """Check volume availability according to 'enabled'.""" | 1006 | """Check volume availability according to 'enabled'.""" |
2248 | 968 | s = super(_MagicicadaUIVolumeTestCase, self) | 1007 | s = super(_MagicicadaUIVolumeTestCase, self) |
2249 | 969 | s.assert_widget_availability(self.name, enabled) | 1008 | s.assert_widget_availability(self.name, enabled) |
2250 | 970 | 1009 | ||
2286 | 971 | def assert_sort_order_correct(self, column, i, expected_order): | 1010 | @skip_abstract_class |
2287 | 972 | """Check that sort order is correctly set.""" | 1011 | def test_initial_data_ready_callback_connected(self): |
2288 | 973 | msg0 = 'Store sort id must be %r (got %r instead).' | 1012 | """The callback 'on_initial_data_ready' is connected to SD.""" |
2289 | 974 | msg1 = 'Store sort order must be %r (got %r instead).' | 1013 | self.assertEqual(self.ui.sd.on_initial_data_ready_callback, |
2290 | 975 | msg3 = 'Column sort order must be %r (got %r instead).' | 1014 | self.ui.on_initial_data_ready, |
2291 | 976 | 1015 | "on_initial_data_ready should be connected.") | |
2292 | 977 | actual_id, actual_order = self.volume_store.get_sort_column_id() | 1016 | |
2293 | 978 | 1017 | @skip_abstract_class | |
2294 | 979 | # store sort column id and order | 1018 | def test_volume_are_disabled_until_initial_data_ready(self): |
2295 | 980 | self.assertEqual(i, actual_id, msg0 % (i, actual_id)) | 1019 | """Folders and shares are disabled until data ready.""" |
2261 | 981 | self.assertEqual(expected_order, actual_order, | ||
2262 | 982 | msg1 % (expected_order, actual_order)) | ||
2263 | 983 | |||
2264 | 984 | # column sort order | ||
2265 | 985 | actual_order = column.get_sort_order() | ||
2266 | 986 | self.assertEqual(expected_order, actual_order, | ||
2267 | 987 | msg3 % (expected_order, actual_order)) | ||
2268 | 988 | |||
2269 | 989 | def assert_sort_indicator_correct(self, column): | ||
2270 | 990 | """Check that sort indicator is correctly set.""" | ||
2271 | 991 | msg = 'Column %s must have sort indicator %s.' | ||
2272 | 992 | colname = column.get_name() | ||
2273 | 993 | # column sort indicator | ||
2274 | 994 | self.assertTrue(column.get_sort_indicator(), msg % (colname, 'on')) | ||
2275 | 995 | |||
2276 | 996 | # all the other columns must not have the sort indicator on | ||
2277 | 997 | for other_column in self.volume_view.get_columns(): | ||
2278 | 998 | if other_column.get_name() == colname: | ||
2279 | 999 | continue | ||
2280 | 1000 | self.assertFalse(other_column.get_sort_indicator(), | ||
2281 | 1001 | msg % (other_column.get_name(), 'off')) | ||
2282 | 1002 | |||
2283 | 1003 | @skip_abstract_class | ||
2284 | 1004 | def test_volume_are_disabled_until_started(self): | ||
2285 | 1005 | """Folders and shares are disabled until online.""" | ||
2296 | 1006 | # disabled at startup | 1020 | # disabled at startup |
2297 | 1007 | self.assert_widget_availability(enabled=False) | 1021 | self.assert_widget_availability(enabled=False) |
2298 | 1008 | 1022 | ||
2301 | 1009 | # enabled when started | 1023 | # enabled when initial data ready |
2302 | 1010 | self.do_start() | 1024 | self.ui.on_initial_data_ready() |
2303 | 1011 | self.assert_widget_availability(enabled=True) | 1025 | self.assert_widget_availability(enabled=True) |
2304 | 1012 | 1026 | ||
2305 | 1013 | @skip_abstract_class | 1027 | @skip_abstract_class |
2306 | 1014 | def test_volume_are_enabled_until_stopped(self): | 1028 | def test_volume_are_enabled_until_stopped(self): |
2307 | 1015 | """Folders and shares are enabled until offline.""" | 1029 | """Folders and shares are enabled until offline.""" |
2308 | 1030 | self.ui.on_initial_data_ready() | ||
2309 | 1031 | |||
2310 | 1016 | self.do_connect() | 1032 | self.do_connect() |
2311 | 1017 | self.assert_widget_availability(enabled=True) | 1033 | self.assert_widget_availability(enabled=True) |
2312 | 1018 | 1034 | ||
2313 | @@ -1057,7 +1073,7 @@ | |||
2314 | 1057 | """Perform the test per se before closing the dialog.""" | 1073 | """Perform the test per se before closing the dialog.""" |
2315 | 1058 | self.assertTrue(self.ui.widget_is_visible(self.volume_dialog), | 1074 | self.assertTrue(self.ui.widget_is_visible(self.volume_dialog), |
2316 | 1059 | '%s should be visible.' % self.volume_dialog_name) | 1075 | '%s should be visible.' % self.volume_dialog_name) |
2318 | 1060 | self.assert_store_correct(self.volume_store, items) | 1076 | self.assert_store_correct(items) |
2319 | 1061 | 1077 | ||
2320 | 1062 | items = self.build_some_data() | 1078 | items = self.build_some_data() |
2321 | 1063 | setattr(self.ui.sd, self.name, items) | 1079 | setattr(self.ui.sd, self.name, items) |
2322 | @@ -1077,7 +1093,7 @@ | |||
2323 | 1077 | """Perform the test per se before closing the dialog.""" | 1093 | """Perform the test per se before closing the dialog.""" |
2324 | 1078 | self.assertTrue(self.ui.widget_is_visible(self.volume_dialog), | 1094 | self.assertTrue(self.ui.widget_is_visible(self.volume_dialog), |
2325 | 1079 | '%s should be visible.' % self.volume_dialog_name) | 1095 | '%s should be visible.' % self.volume_dialog_name) |
2327 | 1080 | self.assert_store_correct(self.volume_store, items) | 1096 | self.assert_store_correct(items) |
2328 | 1081 | 1097 | ||
2329 | 1082 | items = self.build_some_data() | 1098 | items = self.build_some_data() |
2330 | 1083 | setattr(self.ui.sd, self.name, items) | 1099 | setattr(self.ui.sd, self.name, items) |
2331 | @@ -1094,7 +1110,7 @@ | |||
2332 | 1094 | def test_on_volume_clicked_handles_none(self): | 1110 | def test_on_volume_clicked_handles_none(self): |
2333 | 1095 | """On volume clicked handles None as items.""" | 1111 | """On volume clicked handles None as items.""" |
2334 | 1096 | setattr(self.ui.sd, self.name, None) | 1112 | setattr(self.ui.sd, self.name, None) |
2336 | 1097 | test = lambda: self.assert_store_correct(self.volume_store, []) | 1113 | test = lambda: self.assert_store_correct([]) |
2337 | 1098 | gobject.timeout_add(100, close_dialog, | 1114 | gobject.timeout_add(100, close_dialog, |
2338 | 1099 | (self.volume_dialog, test)) | 1115 | (self.volume_dialog, test)) |
2339 | 1100 | self.on_volume_clicked(self.volume) | 1116 | self.on_volume_clicked(self.volume) |
2340 | @@ -1110,36 +1126,36 @@ | |||
2341 | 1110 | def test_volume_columns_not_sorted_at_start(self): | 1126 | def test_volume_columns_not_sorted_at_start(self): |
2342 | 1111 | """Test volume columns are not sorted at start.""" | 1127 | """Test volume columns are not sorted at start.""" |
2343 | 1112 | msg = 'Column %s must not have the sort indicator on.' | 1128 | msg = 'Column %s must not have the sort indicator on.' |
2345 | 1113 | for col in self.volume_view.get_columns(): | 1129 | for col in self.view.get_columns(): |
2346 | 1114 | self.assertFalse(col.get_sort_indicator(), msg % col.get_name()) | 1130 | self.assertFalse(col.get_sort_indicator(), msg % col.get_name()) |
2347 | 1115 | 1131 | ||
2348 | 1116 | @skip_abstract_class | 1132 | @skip_abstract_class |
2349 | 1117 | def test_volume_columns_are_clickable(self): | 1133 | def test_volume_columns_are_clickable(self): |
2350 | 1118 | """Test volume columns are clickable.""" | 1134 | """Test volume columns are clickable.""" |
2351 | 1119 | msg = 'Column %s must be clickable.' | 1135 | msg = 'Column %s must be clickable.' |
2353 | 1120 | for col in self.volume_view.get_columns(): | 1136 | for col in self.view.get_columns(): |
2354 | 1121 | self.assertTrue(col.get_clickable(), msg % col.get_name()) | 1137 | self.assertTrue(col.get_clickable(), msg % col.get_name()) |
2355 | 1122 | 1138 | ||
2356 | 1123 | @skip_abstract_class | 1139 | @skip_abstract_class |
2357 | 1124 | def test_volume_columns_clicked_signal(self): | 1140 | def test_volume_columns_clicked_signal(self): |
2358 | 1125 | """Test volume columns clicks signal is properly connected.""" | 1141 | """Test volume columns clicks signal is properly connected.""" |
2359 | 1126 | msg = 'Column %s must be connected to on_store_sort_column_changed.' | 1142 | msg = 'Column %s must be connected to on_store_sort_column_changed.' |
2361 | 1127 | for col in self.volume_view.get_columns(): | 1143 | for col in self.view.get_columns(): |
2362 | 1128 | self.assertTrue(col.get_clickable(), msg % col.get_name()) | 1144 | self.assertTrue(col.get_clickable(), msg % col.get_name()) |
2363 | 1129 | 1145 | ||
2364 | 1130 | @skip_abstract_class | 1146 | @skip_abstract_class |
2365 | 1131 | def test_volume_sorting(self): | 1147 | def test_volume_sorting(self): |
2366 | 1132 | """Test volume panel can be re-sorted.""" | 1148 | """Test volume panel can be re-sorted.""" |
2368 | 1133 | for i, col in enumerate(self.volume_view.get_columns()): | 1149 | for idx, col in enumerate(self.view.get_columns()): |
2369 | 1134 | col.clicked() # click on the column | 1150 | col.clicked() # click on the column |
2371 | 1135 | self.assert_sort_order_correct(col, i, gtk.SORT_ASCENDING) | 1151 | self.assert_sort_order_correct(col, idx, gtk.SORT_ASCENDING) |
2372 | 1136 | self.assert_sort_indicator_correct(col) | 1152 | self.assert_sort_indicator_correct(col) |
2373 | 1137 | 1153 | ||
2374 | 1138 | col.clicked() # click on the column, sort order must change | 1154 | col.clicked() # click on the column, sort order must change |
2376 | 1139 | self.assert_sort_order_correct(col, i, gtk.SORT_DESCENDING) | 1155 | self.assert_sort_order_correct(col, idx, gtk.SORT_DESCENDING) |
2377 | 1140 | 1156 | ||
2378 | 1141 | col.clicked() # click again, sort order must be the first one | 1157 | col.clicked() # click again, sort order must be the first one |
2380 | 1142 | self.assert_sort_order_correct(col, i, gtk.SORT_ASCENDING) | 1158 | self.assert_sort_order_correct(col, idx, gtk.SORT_ASCENDING) |
2381 | 1143 | 1159 | ||
2382 | 1144 | 1160 | ||
2383 | 1145 | class MagicicadaUIFoldersTestCase(_MagicicadaUIVolumeTestCase): | 1161 | class MagicicadaUIFoldersTestCase(_MagicicadaUIVolumeTestCase): |
2384 | @@ -1173,7 +1189,7 @@ | |||
2385 | 1173 | i = int(kwargs['free_bytes']) | 1189 | i = int(kwargs['free_bytes']) |
2386 | 1174 | kwargs['free_bytes'] = humanize_bytes(i, precision=2) | 1190 | kwargs['free_bytes'] = humanize_bytes(i, precision=2) |
2387 | 1175 | item = self.data_type(**kwargs) | 1191 | item = self.data_type(**kwargs) |
2389 | 1176 | self.assert_store_correct(self.volume_store, [item]) | 1192 | self.assert_store_correct([item]) |
2390 | 1177 | 1193 | ||
2391 | 1178 | gobject.timeout_add(100, close_dialog, | 1194 | gobject.timeout_add(100, close_dialog, |
2392 | 1179 | (self.volume_dialog, test)) | 1195 | (self.volume_dialog, test)) |
2393 | @@ -1266,17 +1282,19 @@ | |||
2394 | 1266 | msg = 'buffer content must be %s (got %s instead).' | 1282 | msg = 'buffer content must be %s (got %s instead).' |
2395 | 1267 | self.assertEqual(actual, expected, msg % (expected, actual)) | 1283 | self.assertEqual(actual, expected, msg % (expected, actual)) |
2396 | 1268 | 1284 | ||
2399 | 1269 | def test_metadata_are_disabled_until_started(self): | 1285 | def test_metadata_are_disabled_until_initial_data_ready(self): |
2400 | 1270 | """Metadata button is disabled until online.""" | 1286 | """Metadata button is disabled until initial data.""" |
2401 | 1271 | # disabled at startup | 1287 | # disabled at startup |
2402 | 1272 | self.assert_widget_availability(enabled=False) | 1288 | self.assert_widget_availability(enabled=False) |
2403 | 1273 | 1289 | ||
2406 | 1274 | # enabled when started | 1290 | # enabled when initial data ready |
2407 | 1275 | self.do_start() | 1291 | self.ui.on_initial_data_ready() |
2408 | 1276 | self.assert_widget_availability(enabled=True) | 1292 | self.assert_widget_availability(enabled=True) |
2409 | 1277 | 1293 | ||
2410 | 1278 | def test_metadata_are_enabled_until_stopped(self): | 1294 | def test_metadata_are_enabled_until_stopped(self): |
2411 | 1279 | """Metadata button is enabled until offline.""" | 1295 | """Metadata button is enabled until offline.""" |
2412 | 1296 | self.ui.on_initial_data_ready() | ||
2413 | 1297 | |||
2414 | 1280 | self.do_connect() | 1298 | self.do_connect() |
2415 | 1281 | self.assert_widget_availability(enabled=True) | 1299 | self.assert_widget_availability(enabled=True) |
2416 | 1282 | 1300 | ||
2417 | @@ -1515,11 +1533,12 @@ | |||
2418 | 1515 | self.memento.setLevel(logging.DEBUG) | 1533 | self.memento.setLevel(logging.DEBUG) |
2419 | 1516 | logger = logging.getLogger('magicicada.ui') | 1534 | logger = logging.getLogger('magicicada.ui') |
2420 | 1517 | logger.addHandler(self.memento) | 1535 | logger.addHandler(self.memento) |
2421 | 1536 | logger.setLevel(logging.DEBUG) | ||
2422 | 1518 | 1537 | ||
2425 | 1519 | def assert_function_logs(self, func, *args, **kwargs): | 1538 | def assert_function_logs(self, level, func, *args, **kwargs): |
2426 | 1520 | """Check 'funcion' logs its inputs as DEBUG.""" | 1539 | """Check 'funcion' logs its inputs as 'level'.""" |
2427 | 1521 | name = func.__name__ | 1540 | name = func.__name__ |
2429 | 1522 | msg = '%s must be logged as DEBUG' | 1541 | msg = '%s must be logged with level %r' |
2430 | 1523 | try: | 1542 | try: |
2431 | 1524 | func(*args, **kwargs) | 1543 | func(*args, **kwargs) |
2432 | 1525 | except Exception: # pylint: disable=E0501, W0703 | 1544 | except Exception: # pylint: disable=E0501, W0703 |
2433 | @@ -1528,33 +1547,201 @@ | |||
2434 | 1528 | 'function (%s) must be logged as ERROR' % name) | 1547 | 'function (%s) must be logged as ERROR' % name) |
2435 | 1529 | self.assertTrue(self.memento.check_error(exc), | 1548 | self.assertTrue(self.memento.check_error(exc), |
2436 | 1530 | 'sys.exc_info (%s) must be logged as ERROR' % exc) | 1549 | 'sys.exc_info (%s) must be logged as ERROR' % exc) |
2438 | 1531 | self.assertTrue(self.memento.check_debug(name), msg % name) | 1550 | |
2439 | 1551 | memento_func = getattr(self.memento, 'check_%s' % level.lower()) | ||
2440 | 1552 | self.assertTrue(memento_func(name), msg % (name, level)) | ||
2441 | 1532 | for arg in args: | 1553 | for arg in args: |
2443 | 1533 | self.assertTrue(self.memento.check_debug(str(arg)), msg % arg) | 1554 | self.assertTrue(memento_func(str(arg)), msg % (arg, level)) |
2444 | 1534 | for key, val in kwargs.iteritems(): | 1555 | for key, val in kwargs.iteritems(): |
2445 | 1535 | arg = "'%s': %r" % (key, val) | 1556 | arg = "'%s': %r" % (key, val) |
2447 | 1536 | self.assertTrue(self.memento.check_debug(arg), msg % arg) | 1557 | self.assertTrue(memento_func(arg), msg % (arg, level)) |
2448 | 1537 | 1558 | ||
2449 | 1538 | def test_on_shares_clicked_logs(self): | 1559 | def test_on_shares_clicked_logs(self): |
2450 | 1539 | """Check _on_shares_clicked logs properly.""" | 1560 | """Check _on_shares_clicked logs properly.""" |
2451 | 1540 | args = ([0, object(), 'test', {}], object()) | 1561 | args = ([0, object(), 'test', {}], object()) |
2452 | 1541 | kwargs = dict(dialog=object()) | 1562 | kwargs = dict(dialog=object()) |
2454 | 1542 | self.assert_function_logs(self.ui._on_shares_clicked, *args, **kwargs) | 1563 | self.assert_function_logs(logging.getLevelName(logging.DEBUG), |
2455 | 1564 | self.ui._on_shares_clicked, *args, **kwargs) | ||
2456 | 1543 | 1565 | ||
2457 | 1544 | def test_on_status_changed_logs(self): | 1566 | def test_on_status_changed_logs(self): |
2458 | 1545 | """Check _on_status_changed logs properly.""" | 1567 | """Check _on_status_changed logs properly.""" |
2459 | 1546 | args = ('test status', 'status description', True, False, True) | 1568 | args = ('test status', 'status description', True, False, True) |
2460 | 1547 | kwargs = dict(queues='bla', connection=None) | 1569 | kwargs = dict(queues='bla', connection=None) |
2462 | 1548 | self.assert_function_logs(self.ui.on_status_changed, *args, **kwargs) | 1570 | self.assert_function_logs(logging.getLevelName(logging.DEBUG), |
2463 | 1571 | self.ui.on_status_changed, *args, **kwargs) | ||
2464 | 1549 | 1572 | ||
2465 | 1550 | def test_on_queue_changed_logs(self): | 1573 | def test_on_queue_changed_logs(self): |
2466 | 1551 | """Check _on_queue_changed logs properly.""" | 1574 | """Check _on_queue_changed logs properly.""" |
2467 | 1552 | args = ('meta',) | 1575 | args = ('meta',) |
2468 | 1553 | kwargs = dict(items=[0, object(), 'test', {}], must_highlight=True) | 1576 | kwargs = dict(items=[0, object(), 'test', {}], must_highlight=True) |
2470 | 1554 | self.assert_function_logs(self.ui._on_queue_changed, *args, **kwargs) | 1577 | self.assert_function_logs(logging.getLevelName(logging.DEBUG), |
2471 | 1578 | self.ui._on_queue_changed, *args, **kwargs) | ||
2472 | 1555 | 1579 | ||
2473 | 1556 | def test_on_metadata_ready_logs(self): | 1580 | def test_on_metadata_ready_logs(self): |
2474 | 1557 | """Check on_metadata_ready logs properly.""" | 1581 | """Check on_metadata_ready logs properly.""" |
2475 | 1558 | args = () | 1582 | args = () |
2476 | 1559 | kwargs = dict(path='test', metadata=True) | 1583 | kwargs = dict(path='test', metadata=True) |
2478 | 1560 | self.assert_function_logs(self.ui.on_metadata_ready, *args, **kwargs) | 1584 | self.assert_function_logs(logging.getLevelName(logging.DEBUG), |
2479 | 1585 | self.ui.on_metadata_ready, *args, **kwargs) | ||
2480 | 1586 | |||
2481 | 1587 | def test_on_initial_data_ready_logs(self): | ||
2482 | 1588 | """Check on_initial_data_ready logs properly.""" | ||
2483 | 1589 | args = () | ||
2484 | 1590 | kwargs = dict(path='test', metadata=True) | ||
2485 | 1591 | self.assert_function_logs(logging.getLevelName(logging.INFO), | ||
2486 | 1592 | self.ui.on_initial_data_ready, | ||
2487 | 1593 | *args, **kwargs) | ||
2488 | 1594 | |||
2489 | 1595 | |||
2490 | 1596 | class PublicFilesTestCase(MagicicadaUITestCase): | ||
2491 | 1597 | """UI test cases for public files.""" | ||
2492 | 1598 | |||
2493 | 1599 | name = 'public_files' | ||
2494 | 1600 | data_type = PublicFilesData | ||
2495 | 1601 | mapping = enumerate(['path', 'public_url', 'volume', 'node']) | ||
2496 | 1602 | |||
2497 | 1603 | def setUp(self): | ||
2498 | 1604 | """Init.""" | ||
2499 | 1605 | super(PublicFilesTestCase, self).setUp() | ||
2500 | 1606 | self.store = self.ui.public_files_store | ||
2501 | 1607 | self.view = self.ui.public_files_view | ||
2502 | 1608 | |||
2503 | 1609 | def assert_widget_availability(self, enabled=True, msg=None): | ||
2504 | 1610 | """Check widget availability according to 'enabled'.""" | ||
2505 | 1611 | widget = self.ui.public_files | ||
2506 | 1612 | self.assertTrue(self.ui.widget_is_visible(widget), 'must be visible') | ||
2507 | 1613 | |||
2508 | 1614 | sensitive = widget.is_sensitive() | ||
2509 | 1615 | if msg is None: | ||
2510 | 1616 | msg = 'must %sbe sensitive' % ('' if enabled else 'not ',) | ||
2511 | 1617 | self.assertTrue(sensitive if enabled else not sensitive, msg) | ||
2512 | 1618 | |||
2513 | 1619 | def test_initial_data_ready_callback_connected(self): | ||
2514 | 1620 | """The callback 'on_initial_online_data_ready' is connected to SD.""" | ||
2515 | 1621 | self.assertEqual(self.ui.sd.on_initial_online_data_ready_callback, | ||
2516 | 1622 | self.ui.on_initial_online_data_ready, | ||
2517 | 1623 | "on_initial_online_data_ready should be connected.") | ||
2518 | 1624 | |||
2519 | 1625 | def test_disabled_until_initial_online_data_ready(self): | ||
2520 | 1626 | """Widget is disabled until initial online data ready.""" | ||
2521 | 1627 | self.assert_widget_availability(enabled=False, | ||
2522 | 1628 | msg='must be disabled at startup') | ||
2523 | 1629 | |||
2524 | 1630 | self.ui.on_initial_online_data_ready() | ||
2525 | 1631 | msg = 'must be enabled when initial data ready' | ||
2526 | 1632 | self.assert_widget_availability(enabled=True, msg=msg) | ||
2527 | 1633 | |||
2528 | 1634 | def test_enabled_until_stopped(self): | ||
2529 | 1635 | """Widget is enabled until stopped.""" | ||
2530 | 1636 | self.ui.on_initial_online_data_ready() | ||
2531 | 1637 | |||
2532 | 1638 | self.do_connect() | ||
2533 | 1639 | self.assert_widget_availability(enabled=True) | ||
2534 | 1640 | |||
2535 | 1641 | self.ui.on_online() | ||
2536 | 1642 | self.assert_widget_availability(enabled=True) | ||
2537 | 1643 | |||
2538 | 1644 | self.ui.on_offline() | ||
2539 | 1645 | self.assert_widget_availability(enabled=True) | ||
2540 | 1646 | |||
2541 | 1647 | self.ui.on_stopped() | ||
2542 | 1648 | self.assert_widget_availability(enabled=False) | ||
2543 | 1649 | |||
2544 | 1650 | def test_public_files_close_emits_response_close(self): | ||
2545 | 1651 | """Test close button emits RESPONSE_CLOSE when clicked.""" | ||
2546 | 1652 | self.patch(self.ui.public_files_dialog, 'response', self._set_called) | ||
2547 | 1653 | self.ui.public_files_close.clicked() | ||
2548 | 1654 | self.assertEqual(self._called, ((gtk.RESPONSE_CLOSE,), {}), | ||
2549 | 1655 | 'close button should emit RESPONSE_CLOSE.') | ||
2550 | 1656 | |||
2551 | 1657 | def test_on_public_files_clicked_runs_the_dialog(self): | ||
2552 | 1658 | """Test on_public_files_clicked run the public_files_dialog.""" | ||
2553 | 1659 | self.patch(self.ui.public_files_dialog, 'run', self._set_called) | ||
2554 | 1660 | self.ui.sd.public_files = {} | ||
2555 | 1661 | |||
2556 | 1662 | visible = self.ui.widget_is_visible(self.ui.public_files_dialog) | ||
2557 | 1663 | self.assertFalse(visible, 'dialog should not be visible.') | ||
2558 | 1664 | |||
2559 | 1665 | self.ui.on_public_files_clicked(self.ui.public_files) | ||
2560 | 1666 | |||
2561 | 1667 | self.assertEqual(self._called, ((), {})) | ||
2562 | 1668 | |||
2563 | 1669 | def test_on_public_files_clicked_hides_the_dialog(self): | ||
2564 | 1670 | """Test on_public_files_clicked hides the public_files_dialog.""" | ||
2565 | 1671 | self.patch(self.ui.public_files_dialog, 'run', lambda: None) # no run | ||
2566 | 1672 | self.patch(self.ui.public_files_dialog, 'hide', self._set_called) | ||
2567 | 1673 | self.ui.sd.public_files = {} | ||
2568 | 1674 | |||
2569 | 1675 | self.ui.on_public_files_clicked(self.ui.public_files) | ||
2570 | 1676 | |||
2571 | 1677 | self.assertEqual(self._called, ((), {})) | ||
2572 | 1678 | |||
2573 | 1679 | def test_on_public_files_clicked_grabs_info_from_backend(self): | ||
2574 | 1680 | """Test on_public_files_clicked.""" | ||
2575 | 1681 | self.patch(self.ui.public_files_dialog, 'run', lambda: None) # no run | ||
2576 | 1682 | self.ui.sd.public_files = SAMPLE_PUBLIC_FILES | ||
2577 | 1683 | |||
2578 | 1684 | self.ui.on_public_files_clicked(self.ui.public_files) | ||
2579 | 1685 | |||
2580 | 1686 | self.assert_store_correct(SAMPLE_PUBLIC_FILES.values()) | ||
2581 | 1687 | |||
2582 | 1688 | def test_on_public_files_clicked_twice(self): | ||
2583 | 1689 | """Test on_public_files_clicked twice.""" | ||
2584 | 1690 | self.patch(self.ui.public_files_dialog, 'run', lambda: None) # no run | ||
2585 | 1691 | self.ui.sd.public_files = SAMPLE_PUBLIC_FILES | ||
2586 | 1692 | |||
2587 | 1693 | self.ui.on_public_files_clicked(self.ui.public_files) | ||
2588 | 1694 | self.ui.on_public_files_clicked(self.ui.public_files) | ||
2589 | 1695 | |||
2590 | 1696 | self.assert_store_correct(SAMPLE_PUBLIC_FILES.values()) | ||
2591 | 1697 | |||
2592 | 1698 | def test_on_public_files_clicked_handles_none(self): | ||
2593 | 1699 | """On public_files clicked handles None as items.""" | ||
2594 | 1700 | self.patch(self.ui.public_files_dialog, 'run', lambda: None) # no run | ||
2595 | 1701 | self.ui.sd.public_files = None | ||
2596 | 1702 | |||
2597 | 1703 | self.ui.on_public_files_clicked(self.ui.public_files) | ||
2598 | 1704 | |||
2599 | 1705 | self.assert_store_correct([]) | ||
2600 | 1706 | |||
2601 | 1707 | def test_dialog_properties(self): | ||
2602 | 1708 | """The dialog has correct properties.""" | ||
2603 | 1709 | title = self.name.replace('_', ' ').capitalize() | ||
2604 | 1710 | self.assert_dialog_properties(dialog_name='public_files_dialog', | ||
2605 | 1711 | title=title) | ||
2606 | 1712 | |||
2607 | 1713 | def test_columns_not_sorted_at_start(self): | ||
2608 | 1714 | """Test columns are not sorted at start.""" | ||
2609 | 1715 | msg = 'Column %s must not have the sort indicator on.' | ||
2610 | 1716 | for col in self.ui.public_files_view.get_columns(): | ||
2611 | 1717 | self.assertFalse(col.get_sort_indicator(), msg % col.get_name()) | ||
2612 | 1718 | |||
2613 | 1719 | def test_columns_are_clickable(self): | ||
2614 | 1720 | """Test columns are clickable.""" | ||
2615 | 1721 | msg = 'Column %s must be clickable.' | ||
2616 | 1722 | for col in self.ui.public_files_view.get_columns(): | ||
2617 | 1723 | self.assertTrue(col.get_clickable(), msg % col.get_name()) | ||
2618 | 1724 | |||
2619 | 1725 | def test_columns_clicked_signal(self): | ||
2620 | 1726 | """Test columns clicked signal is properly connected.""" | ||
2621 | 1727 | msg = 'Column %s must be connected to on_store_sort_column_changed.' | ||
2622 | 1728 | for col in self.ui.public_files_view.get_columns(): | ||
2623 | 1729 | self.assertTrue(col.get_clickable(), msg % col.get_name()) | ||
2624 | 1730 | |||
2625 | 1731 | def test_sorting(self): | ||
2626 | 1732 | """Test public files panel can be re-sorted.""" | ||
2627 | 1733 | self.patch(self.ui.public_files_dialog, 'run', lambda: None) # no run | ||
2628 | 1734 | self.ui.sd.public_files = SAMPLE_PUBLIC_FILES | ||
2629 | 1735 | |||
2630 | 1736 | self.ui.on_public_files_clicked(self.ui.public_files) | ||
2631 | 1737 | |||
2632 | 1738 | for idx, col in enumerate(self.ui.public_files_view.get_columns()): | ||
2633 | 1739 | col.clicked() # click on the column | ||
2634 | 1740 | self.assert_sort_order_correct(col, idx, gtk.SORT_ASCENDING) | ||
2635 | 1741 | self.assert_sort_indicator_correct(col) | ||
2636 | 1742 | |||
2637 | 1743 | col.clicked() # click on the column, sort order must change | ||
2638 | 1744 | self.assert_sort_order_correct(col, idx, gtk.SORT_DESCENDING) | ||
2639 | 1745 | |||
2640 | 1746 | col.clicked() # click again, sort order must be the first one | ||
2641 | 1747 | self.assert_sort_order_correct(col, idx, gtk.SORT_ASCENDING) | ||
2642 | 1561 | 1748 | ||
2643 | === modified file 'magicicada/tests/test_syncdaemon.py' | |||
2644 | --- magicicada/tests/test_syncdaemon.py 2010-09-02 18:23:02 +0000 | |||
2645 | +++ magicicada/tests/test_syncdaemon.py 2011-01-07 23:06:32 +0000 | |||
2646 | @@ -22,7 +22,13 @@ | |||
2647 | 22 | import os | 22 | import os |
2648 | 23 | import unittest | 23 | import unittest |
2649 | 24 | 24 | ||
2651 | 25 | from magicicada.syncdaemon import SyncDaemon, State | 25 | from magicicada.dbusiface import PublicFilesData, ShareOperationError |
2652 | 26 | from magicicada.syncdaemon import ( | ||
2653 | 27 | ASKING_IDLE, | ||
2654 | 28 | mandatory_callback, | ||
2655 | 29 | State, | ||
2656 | 30 | SyncDaemon, | ||
2657 | 31 | ) | ||
2658 | 26 | from magicicada.tests.helpers import MementoHandler | 32 | from magicicada.tests.helpers import MementoHandler |
2659 | 27 | 33 | ||
2660 | 28 | from twisted.trial.unittest import TestCase as TwistedTestCase | 34 | from twisted.trial.unittest import TestCase as TwistedTestCase |
2661 | @@ -32,18 +38,23 @@ | |||
2662 | 32 | # It's ok to access private data in the test suite | 38 | # It's ok to access private data in the test suite |
2663 | 33 | # pylint: disable=W0212 | 39 | # pylint: disable=W0212 |
2664 | 34 | 40 | ||
2665 | 41 | # Lambda may not be necessary | ||
2666 | 42 | # pylint: disable=W0108 | ||
2667 | 43 | |||
2668 | 35 | 44 | ||
2669 | 36 | class FakeDBusInterface(object): | 45 | class FakeDBusInterface(object): |
2670 | 37 | """Fake DBus Interface, for SD to not use dbus at all during tests.""" | 46 | """Fake DBus Interface, for SD to not use dbus at all during tests.""" |
2671 | 38 | 47 | ||
2672 | 39 | fake_sd_started = False | 48 | fake_sd_started = False |
2673 | 49 | fake_pf_data = PublicFilesData(volume='v', node='n', | ||
2674 | 50 | path='p', public_url='u') | ||
2675 | 51 | fake_share_response = None | ||
2676 | 40 | 52 | ||
2677 | 41 | def __init__(self, sd): | 53 | def __init__(self, sd): |
2678 | 42 | pass | 54 | pass |
2679 | 43 | 55 | ||
2680 | 44 | def shutdown(self): | 56 | def shutdown(self): |
2681 | 45 | """Fake shutdown.""" | 57 | """Fake shutdown.""" |
2682 | 46 | pass | ||
2683 | 47 | 58 | ||
2684 | 48 | def get_status(self): | 59 | def get_status(self): |
2685 | 49 | """Fake status.""" | 60 | """Fake status.""" |
2686 | @@ -58,10 +69,18 @@ | |||
2687 | 58 | start = quit = connect = disconnect = get_folders | 69 | start = quit = connect = disconnect = get_folders |
2688 | 59 | get_shares_to_me = get_shares_to_others = get_folders | 70 | get_shares_to_me = get_shares_to_others = get_folders |
2689 | 60 | 71 | ||
2690 | 72 | def get_public_files(self): | ||
2691 | 73 | """Fake public files.""" | ||
2692 | 74 | return defer.succeed([self.fake_pf_data]) | ||
2693 | 75 | |||
2694 | 61 | def is_sd_started(self): | 76 | def is_sd_started(self): |
2695 | 62 | """Fake response.""" | 77 | """Fake response.""" |
2696 | 63 | return self.fake_sd_started | 78 | return self.fake_sd_started |
2697 | 64 | 79 | ||
2698 | 80 | def accept_share(self, share_id): | ||
2699 | 81 | """Fake accept share.""" | ||
2700 | 82 | return self.fake_share_response | ||
2701 | 83 | |||
2702 | 65 | 84 | ||
2703 | 66 | class BaseTest(TwistedTestCase): | 85 | class BaseTest(TwistedTestCase): |
2704 | 67 | """Base test with a SD.""" | 86 | """Base test with a SD.""" |
2705 | @@ -70,6 +89,11 @@ | |||
2706 | 70 | 89 | ||
2707 | 71 | def setUp(self): | 90 | def setUp(self): |
2708 | 72 | """Set up.""" | 91 | """Set up.""" |
2709 | 92 | self.hdlr = MementoHandler() | ||
2710 | 93 | self.hdlr.setLevel(logging.DEBUG) | ||
2711 | 94 | logger = logging.getLogger('magicicada.syncdaemon') | ||
2712 | 95 | logger.addHandler(self.hdlr) | ||
2713 | 96 | logger.setLevel(logging.DEBUG) | ||
2714 | 73 | self.sd = SyncDaemon(FakeDBusInterface) | 97 | self.sd = SyncDaemon(FakeDBusInterface) |
2715 | 74 | 98 | ||
2716 | 75 | def tearDown(self): | 99 | def tearDown(self): |
2717 | @@ -77,9 +101,44 @@ | |||
2718 | 77 | self.sd.shutdown() | 101 | self.sd.shutdown() |
2719 | 78 | 102 | ||
2720 | 79 | 103 | ||
2721 | 104 | class MandatoryCallbackTests(BaseTest): | ||
2722 | 105 | """Tests for the mandatory callback generic function.""" | ||
2723 | 106 | |||
2724 | 107 | def test_log_function_name(self): | ||
2725 | 108 | """Log the function name.""" | ||
2726 | 109 | some_function = mandatory_callback('bar') | ||
2727 | 110 | some_function() | ||
2728 | 111 | self.assertTrue(self.hdlr.check_warning( | ||
2729 | 112 | "Callback called but was not assigned", "bar")) | ||
2730 | 113 | |||
2731 | 114 | def test_log_args(self): | ||
2732 | 115 | """Log the arguments.""" | ||
2733 | 116 | some_function = mandatory_callback('bar') | ||
2734 | 117 | some_function(1, 2, b=45) | ||
2735 | 118 | self.hdlr.debug = True | ||
2736 | 119 | self.assertTrue(self.hdlr.check_warning( | ||
2737 | 120 | "Callback called but was not assigned", | ||
2738 | 121 | "1", "2", "'b': 45")) | ||
2739 | 122 | |||
2740 | 123 | |||
2741 | 80 | class InitialDataTests(unittest.TestCase): | 124 | class InitialDataTests(unittest.TestCase): |
2742 | 81 | """Tests for initial data gathering.""" | 125 | """Tests for initial data gathering.""" |
2743 | 82 | 126 | ||
2744 | 127 | def setUp(self): | ||
2745 | 128 | """Set up the test.""" | ||
2746 | 129 | self.sd = SyncDaemon(FakeDBusInterface) | ||
2747 | 130 | |||
2748 | 131 | self.offline_called = False | ||
2749 | 132 | self.sd.on_initial_data_ready_callback = lambda: setattr(self, | ||
2750 | 133 | 'offline_called', True) | ||
2751 | 134 | self.online_called = False | ||
2752 | 135 | self.sd.on_initial_online_data_ready_callback = lambda: setattr(self, | ||
2753 | 136 | 'online_called', True) | ||
2754 | 137 | |||
2755 | 138 | def tearDown(self): | ||
2756 | 139 | """Tear down the test.""" | ||
2757 | 140 | self.sd.shutdown() | ||
2758 | 141 | |||
2759 | 83 | def test_called_by_start(self): | 142 | def test_called_by_start(self): |
2760 | 84 | """Check that start calls get initial data.""" | 143 | """Check that start calls get initial data.""" |
2761 | 85 | sd = SyncDaemon(FakeDBusInterface) | 144 | sd = SyncDaemon(FakeDBusInterface) |
2762 | @@ -135,6 +194,68 @@ | |||
2763 | 135 | sd._get_initial_data() | 194 | sd._get_initial_data() |
2764 | 136 | self.assertEqual(len(called), 3) | 195 | self.assertEqual(len(called), 3) |
2765 | 137 | 196 | ||
2766 | 197 | def test_public_files_info(self): | ||
2767 | 198 | """Check we get the public files info at start.""" | ||
2768 | 199 | sd = SyncDaemon(FakeDBusInterface) | ||
2769 | 200 | fake_data = FakeDBusInterface.fake_pf_data | ||
2770 | 201 | sd._get_initial_data() | ||
2771 | 202 | self.assertEqual(sd.public_files[fake_data.node], fake_data) | ||
2772 | 203 | |||
2773 | 204 | def test_all_ready(self): | ||
2774 | 205 | """All data is ready.""" | ||
2775 | 206 | self.sd._get_initial_data() | ||
2776 | 207 | self.assertTrue(self.offline_called) | ||
2777 | 208 | self.assertTrue(self.online_called) | ||
2778 | 209 | |||
2779 | 210 | def test_no_public_files(self): | ||
2780 | 211 | """Initial gathering is stuck in public files.""" | ||
2781 | 212 | self.sd.dbus.get_public_files = lambda: defer.Deferred() | ||
2782 | 213 | self.sd._get_initial_data() | ||
2783 | 214 | self.assertTrue(self.offline_called) | ||
2784 | 215 | self.assertFalse(self.online_called) | ||
2785 | 216 | |||
2786 | 217 | def test_no_shares_to_others(self): | ||
2787 | 218 | """Initial gathering is stuck in shares to others.""" | ||
2788 | 219 | self.sd.dbus.get_shares_to_others = lambda: defer.Deferred() | ||
2789 | 220 | self.sd._get_initial_data() | ||
2790 | 221 | self.assertFalse(self.offline_called) | ||
2791 | 222 | self.assertFalse(self.online_called) | ||
2792 | 223 | |||
2793 | 224 | def test_no_shares_to_me(self): | ||
2794 | 225 | """Initial gathering is stuck in shares to me.""" | ||
2795 | 226 | self.sd.dbus.get_shares_to_me = lambda: defer.Deferred() | ||
2796 | 227 | self.sd._get_initial_data() | ||
2797 | 228 | self.assertFalse(self.offline_called) | ||
2798 | 229 | self.assertFalse(self.online_called) | ||
2799 | 230 | |||
2800 | 231 | def test_no_folders(self): | ||
2801 | 232 | """Initial gathering is stuck in folders.""" | ||
2802 | 233 | self.sd.dbus.get_folders = lambda: defer.Deferred() | ||
2803 | 234 | self.sd._get_initial_data() | ||
2804 | 235 | self.assertFalse(self.offline_called) | ||
2805 | 236 | self.assertFalse(self.online_called) | ||
2806 | 237 | |||
2807 | 238 | def test_no_meta_queue(self): | ||
2808 | 239 | """Initial gathering is stuck in meta queue.""" | ||
2809 | 240 | self.sd.dbus.get_meta_queue = lambda: defer.Deferred() | ||
2810 | 241 | self.sd._get_initial_data() | ||
2811 | 242 | self.assertFalse(self.offline_called) | ||
2812 | 243 | self.assertFalse(self.online_called) | ||
2813 | 244 | |||
2814 | 245 | def test_no_content_queue(self): | ||
2815 | 246 | """Initial gathering is stuck in content queue.""" | ||
2816 | 247 | self.sd.dbus.get_content_queue = lambda: defer.Deferred() | ||
2817 | 248 | self.sd._get_initial_data() | ||
2818 | 249 | self.assertFalse(self.offline_called) | ||
2819 | 250 | self.assertFalse(self.online_called) | ||
2820 | 251 | |||
2821 | 252 | def test_no_status(self): | ||
2822 | 253 | """Initial gathering is stuck in status.""" | ||
2823 | 254 | self.sd.dbus.get_status = lambda: defer.Deferred() | ||
2824 | 255 | self.sd._get_initial_data() | ||
2825 | 256 | self.assertFalse(self.offline_called) | ||
2826 | 257 | self.assertFalse(self.online_called) | ||
2827 | 258 | |||
2828 | 138 | 259 | ||
2829 | 139 | class StatusChangedTests(BaseTest): | 260 | class StatusChangedTests(BaseTest): |
2830 | 140 | """Simple signals checking.""" | 261 | """Simple signals checking.""" |
2831 | @@ -175,7 +296,7 @@ | |||
2832 | 175 | False, 'queues', 'connection') | 296 | False, 'queues', 'connection') |
2833 | 176 | return deferred | 297 | return deferred |
2834 | 177 | 298 | ||
2836 | 178 | def test_status_changed_affects_cuurent_status(self): | 299 | def test_status_changed_affects_current_status(self): |
2837 | 179 | """Make changes to see how status are reflected.""" | 300 | """Make changes to see how status are reflected.""" |
2838 | 180 | # one set of values | 301 | # one set of values |
2839 | 181 | self.sd.on_sd_status_changed('name1', 'description1', False, True, | 302 | self.sd.on_sd_status_changed('name1', 'description1', False, True, |
2840 | @@ -322,24 +443,115 @@ | |||
2841 | 322 | self.sd.on_sd_content_queue_changed() | 443 | self.sd.on_sd_content_queue_changed() |
2842 | 323 | self.assertEqual(called, [['foo']]) | 444 | self.assertEqual(called, [['foo']]) |
2843 | 324 | 445 | ||
2845 | 325 | def test_CQ_state_nothing(self): | 446 | def test_cq_state_nothing(self): |
2846 | 326 | """Check the ContentQueue info, being nothing.""" | 447 | """Check the ContentQueue info, being nothing.""" |
2847 | 327 | self.sd.dbus.get_content_queue = lambda: defer.succeed([]) | 448 | self.sd.dbus.get_content_queue = lambda: defer.succeed([]) |
2848 | 328 | self.sd.on_sd_content_queue_changed() | 449 | self.sd.on_sd_content_queue_changed() |
2849 | 329 | self.assertEqual(self.sd.content_queue, []) | 450 | self.assertEqual(self.sd.content_queue, []) |
2850 | 330 | 451 | ||
2852 | 331 | def test_CQ_state_one(self): | 452 | def test_cq_state_one(self): |
2853 | 332 | """Check the ContentQueue info, being one.""" | 453 | """Check the ContentQueue info, being one.""" |
2854 | 333 | self.sd.dbus.get_content_queue = lambda: defer.succeed(['foo']) | 454 | self.sd.dbus.get_content_queue = lambda: defer.succeed(['foo']) |
2855 | 334 | self.sd.on_sd_content_queue_changed() | 455 | self.sd.on_sd_content_queue_changed() |
2856 | 335 | self.assertEqual(self.sd.content_queue, ['foo']) | 456 | self.assertEqual(self.sd.content_queue, ['foo']) |
2857 | 336 | 457 | ||
2859 | 337 | def test_CQ_state_two(self): | 458 | def test_cq_state_two(self): |
2860 | 338 | """Check the ContentQueue info, two.""" | 459 | """Check the ContentQueue info, two.""" |
2861 | 339 | self.sd.dbus.get_content_queue = lambda: defer.succeed(['foo', 'bar']) | 460 | self.sd.dbus.get_content_queue = lambda: defer.succeed(['foo', 'bar']) |
2862 | 340 | self.sd.on_sd_content_queue_changed() | 461 | self.sd.on_sd_content_queue_changed() |
2863 | 341 | self.assertEqual(self.sd.content_queue, ['foo', 'bar']) | 462 | self.assertEqual(self.sd.content_queue, ['foo', 'bar']) |
2864 | 342 | 463 | ||
2865 | 464 | def test_cq_multiple_without_response_having_two(self): | ||
2866 | 465 | """Behave correctly on two-changes burst.""" | ||
2867 | 466 | d1 = defer.Deferred() | ||
2868 | 467 | d2 = defer.Deferred() | ||
2869 | 468 | called = [] | ||
2870 | 469 | |||
2871 | 470 | def fake(): | ||
2872 | 471 | """Fake.""" | ||
2873 | 472 | d = d2 if called else d1 | ||
2874 | 473 | called.append(None) | ||
2875 | 474 | return d | ||
2876 | 475 | self.sd.dbus.get_content_queue = fake | ||
2877 | 476 | |||
2878 | 477 | # call twice | ||
2879 | 478 | self.sd.on_sd_content_queue_changed() | ||
2880 | 479 | self.sd.on_sd_content_queue_changed() | ||
2881 | 480 | |||
2882 | 481 | # dbus function should be called once | ||
2883 | 482 | self.assertEqual(len(called), 1) | ||
2884 | 483 | |||
2885 | 484 | # first call returns | ||
2886 | 485 | d1.callback(['foo']) | ||
2887 | 486 | d2.callback(['bar']) | ||
2888 | 487 | |||
2889 | 488 | # it should be called once more only | ||
2890 | 489 | self.assertEqual(len(called), 2) | ||
2891 | 490 | |||
2892 | 491 | def test_cq_multiple_without_response_having_three(self): | ||
2893 | 492 | """Behave correctly on three-changes burst.""" | ||
2894 | 493 | d1 = defer.Deferred() | ||
2895 | 494 | d2 = defer.Deferred() | ||
2896 | 495 | called = [] | ||
2897 | 496 | |||
2898 | 497 | def fake(): | ||
2899 | 498 | """Fake.""" | ||
2900 | 499 | d = d2 if called else d1 | ||
2901 | 500 | called.append(None) | ||
2902 | 501 | return d | ||
2903 | 502 | self.sd.dbus.get_content_queue = fake | ||
2904 | 503 | |||
2905 | 504 | # call thrice | ||
2906 | 505 | self.sd.on_sd_content_queue_changed() | ||
2907 | 506 | self.sd.on_sd_content_queue_changed() | ||
2908 | 507 | self.sd.on_sd_content_queue_changed() | ||
2909 | 508 | |||
2910 | 509 | # dbus function should be called once | ||
2911 | 510 | self.assertEqual(len(called), 1) | ||
2912 | 511 | |||
2913 | 512 | # first call returns | ||
2914 | 513 | d1.callback(['foo']) | ||
2915 | 514 | d2.callback(['bar']) | ||
2916 | 515 | |||
2917 | 516 | # it should be called once more only | ||
2918 | 517 | self.assertEqual(len(called), 2) | ||
2919 | 518 | |||
2920 | 519 | def test_cq_multiple_without_response_having_more_later(self): | ||
2921 | 520 | """Behave correctly on changes burst delayed.""" | ||
2922 | 521 | d1 = defer.Deferred() | ||
2923 | 522 | d2 = defer.Deferred() | ||
2924 | 523 | d3 = defer.Deferred() | ||
2925 | 524 | defs = [d1, d2, d3] | ||
2926 | 525 | called = [] | ||
2927 | 526 | |||
2928 | 527 | def fake(): | ||
2929 | 528 | """Fake.""" | ||
2930 | 529 | d = defs[len(called)] | ||
2931 | 530 | called.append(None) | ||
2932 | 531 | return d | ||
2933 | 532 | self.sd.dbus.get_content_queue = fake | ||
2934 | 533 | |||
2935 | 534 | # call some times, dbus function should be called once | ||
2936 | 535 | self.sd.on_sd_content_queue_changed() | ||
2937 | 536 | self.sd.on_sd_content_queue_changed() | ||
2938 | 537 | self.sd.on_sd_content_queue_changed() | ||
2939 | 538 | self.assertEqual(len(called), 1) | ||
2940 | 539 | |||
2941 | 540 | # first call returns | ||
2942 | 541 | d1.callback(['foo']) | ||
2943 | 542 | |||
2944 | 543 | # while calling second time, generate more requests | ||
2945 | 544 | self.sd.on_sd_content_queue_changed() | ||
2946 | 545 | self.sd.on_sd_content_queue_changed() | ||
2947 | 546 | self.sd.on_sd_content_queue_changed() | ||
2948 | 547 | |||
2949 | 548 | # second call returns | ||
2950 | 549 | d2.callback(['foo']) | ||
2951 | 550 | d3.callback(['foo']) | ||
2952 | 551 | |||
2953 | 552 | # it should be called three times total | ||
2954 | 553 | self.assertEqual(len(called), 3) | ||
2955 | 554 | |||
2956 | 343 | 555 | ||
2957 | 344 | class MetaQueueChangedTests(BaseTest): | 556 | class MetaQueueChangedTests(BaseTest): |
2958 | 345 | """Check the MetaQueueChanged handling.""" | 557 | """Check the MetaQueueChanged handling.""" |
2959 | @@ -526,6 +738,8 @@ | |||
2960 | 526 | 738 | ||
2961 | 527 | def test_mq_caller_is_reset_last_time(self): | 739 | def test_mq_caller_is_reset_last_time(self): |
2962 | 528 | """When MQ is polled last time, the caller should be back to None.""" | 740 | """When MQ is polled last time, the caller should be back to None.""" |
2963 | 741 | # Module 'twisted.internet.reactor' has no 'callLater' member | ||
2964 | 742 | # pylint: disable=E1101 | ||
2965 | 529 | self.sd._mqcaller = reactor.callLater(100, lambda: None) | 743 | self.sd._mqcaller = reactor.callLater(100, lambda: None) |
2966 | 530 | self.sd.current_state.set(name='QUEUE_MANAGER', | 744 | self.sd.current_state.set(name='QUEUE_MANAGER', |
2967 | 531 | queues='WORKING_ON_CONTENT') | 745 | queues='WORKING_ON_CONTENT') |
2968 | @@ -550,6 +764,8 @@ | |||
2969 | 550 | 'WORKING_ON_CONTENT', 'connect') | 764 | 'WORKING_ON_CONTENT', 'connect') |
2970 | 551 | 765 | ||
2971 | 552 | # allow time to see if a mistaken call happens | 766 | # allow time to see if a mistaken call happens |
2972 | 767 | # Module 'twisted.internet.reactor' has no 'callLater' member | ||
2973 | 768 | # pylint: disable=E1101 | ||
2974 | 553 | reactor.callLater(.5, deferred.callback, True) | 769 | reactor.callLater(.5, deferred.callback, True) |
2975 | 554 | elif len(calls) == 4: | 770 | elif len(calls) == 4: |
2976 | 555 | pass # last call after state changed | 771 | pass # last call after state changed |
2977 | @@ -566,24 +782,132 @@ | |||
2978 | 566 | deferred = defer.Deferred() | 782 | deferred = defer.Deferred() |
2979 | 567 | return deferred | 783 | return deferred |
2980 | 568 | 784 | ||
2982 | 569 | def test_MQ_state_nothing(self): | 785 | def test_mq_state_nothing(self): |
2983 | 570 | """Check the MetaQueue info, being nothing.""" | 786 | """Check the MetaQueue info, being nothing.""" |
2984 | 571 | self.sd.dbus.get_meta_queue = lambda: defer.succeed([]) | 787 | self.sd.dbus.get_meta_queue = lambda: defer.succeed([]) |
2985 | 572 | self.sd._check_mq() | 788 | self.sd._check_mq() |
2986 | 573 | self.assertEqual(self.sd.meta_queue, []) | 789 | self.assertEqual(self.sd.meta_queue, []) |
2987 | 574 | 790 | ||
2989 | 575 | def test_MQ_state_one(self): | 791 | def test_mq_state_one(self): |
2990 | 576 | """Check the MetaQueue info, being one.""" | 792 | """Check the MetaQueue info, being one.""" |
2991 | 577 | self.sd.dbus.get_meta_queue = lambda: defer.succeed(['foo']) | 793 | self.sd.dbus.get_meta_queue = lambda: defer.succeed(['foo']) |
2992 | 578 | self.sd._check_mq() | 794 | self.sd._check_mq() |
2993 | 579 | self.assertEqual(self.sd.meta_queue, ['foo']) | 795 | self.assertEqual(self.sd.meta_queue, ['foo']) |
2994 | 580 | 796 | ||
2996 | 581 | def test_MQ_state_two(self): | 797 | def test_mq_state_two(self): |
2997 | 582 | """Check the MetaQueue info, two.""" | 798 | """Check the MetaQueue info, two.""" |
2998 | 583 | self.sd.dbus.get_meta_queue = lambda: defer.succeed(['foo', 'bar']) | 799 | self.sd.dbus.get_meta_queue = lambda: defer.succeed(['foo', 'bar']) |
2999 | 584 | self.sd._check_mq() | 800 | self.sd._check_mq() |
3000 | 585 | self.assertEqual(self.sd.meta_queue, ['foo', 'bar']) | 801 | self.assertEqual(self.sd.meta_queue, ['foo', 'bar']) |
3001 | 586 | 802 | ||
3002 | 803 | def test_mq_multiple_without_response_having_two(self): | ||
3003 | 804 | """Behave correctly on two-changes burst.""" | ||
3004 | 805 | d1 = defer.Deferred() | ||
3005 | 806 | d2 = defer.Deferred() | ||
3006 | 807 | called = [] | ||
3007 | 808 | |||
3008 | 809 | def fake(): | ||
3009 | 810 | """Fake.""" | ||
3010 | 811 | d = d2 if called else d1 | ||
3011 | 812 | called.append(None) | ||
3012 | 813 | return d | ||
3013 | 814 | self.sd.dbus.get_meta_queue = fake | ||
3014 | 815 | |||
3015 | 816 | # call twice | ||
3016 | 817 | self.sd.on_sd_meta_queue_changed() | ||
3017 | 818 | self.sd.on_sd_meta_queue_changed() | ||
3018 | 819 | |||
3019 | 820 | # dbus function should be called once | ||
3020 | 821 | self.assertEqual(len(called), 1) | ||
3021 | 822 | |||
3022 | 823 | # first call returns | ||
3023 | 824 | d1.callback(['foo']) | ||
3024 | 825 | d2.callback(['bar']) | ||
3025 | 826 | |||
3026 | 827 | # it should be called once more only | ||
3027 | 828 | self.assertEqual(len(called), 2) | ||
3028 | 829 | |||
3029 | 830 | def test_mq_multiple_without_response_having_three(self): | ||
3030 | 831 | """Behave correctly on three-changes burst.""" | ||
3031 | 832 | d1 = defer.Deferred() | ||
3032 | 833 | d2 = defer.Deferred() | ||
3033 | 834 | called = [] | ||
3034 | 835 | |||
3035 | 836 | def fake(): | ||
3036 | 837 | """Fake.""" | ||
3037 | 838 | d = d2 if called else d1 | ||
3038 | 839 | called.append(None) | ||
3039 | 840 | return d | ||
3040 | 841 | self.sd.dbus.get_meta_queue = fake | ||
3041 | 842 | |||
3042 | 843 | # call thrice | ||
3043 | 844 | self.sd.on_sd_meta_queue_changed() | ||
3044 | 845 | self.sd.on_sd_meta_queue_changed() | ||
3045 | 846 | self.sd.on_sd_meta_queue_changed() | ||
3046 | 847 | |||
3047 | 848 | # dbus function should be called once | ||
3048 | 849 | self.assertEqual(len(called), 1) | ||
3049 | 850 | |||
3050 | 851 | # first call returns | ||
3051 | 852 | d1.callback(['foo']) | ||
3052 | 853 | d2.callback(['bar']) | ||
3053 | 854 | |||
3054 | 855 | # it should be called once more only | ||
3055 | 856 | self.assertEqual(len(called), 2) | ||
3056 | 857 | |||
3057 | 858 | def test_mq_multiple_without_response_having_more_later(self): | ||
3058 | 859 | """Behave correctly on changes burst delayed.""" | ||
3059 | 860 | d1 = defer.Deferred() | ||
3060 | 861 | d2 = defer.Deferred() | ||
3061 | 862 | d3 = defer.Deferred() | ||
3062 | 863 | defs = [d1, d2, d3] | ||
3063 | 864 | called = [] | ||
3064 | 865 | |||
3065 | 866 | def fake(): | ||
3066 | 867 | """Fake.""" | ||
3067 | 868 | d = defs[len(called)] | ||
3068 | 869 | called.append(None) | ||
3069 | 870 | return d | ||
3070 | 871 | self.sd.dbus.get_meta_queue = fake | ||
3071 | 872 | |||
3072 | 873 | # call some times, dbus function should be called once | ||
3073 | 874 | self.sd.on_sd_meta_queue_changed() | ||
3074 | 875 | self.sd.on_sd_meta_queue_changed() | ||
3075 | 876 | self.sd.on_sd_meta_queue_changed() | ||
3076 | 877 | self.assertEqual(len(called), 1) | ||
3077 | 878 | |||
3078 | 879 | # first call returns | ||
3079 | 880 | d1.callback(['foo']) | ||
3080 | 881 | |||
3081 | 882 | # while calling second time, generate more requests | ||
3082 | 883 | self.sd.on_sd_meta_queue_changed() | ||
3083 | 884 | self.sd.on_sd_meta_queue_changed() | ||
3084 | 885 | self.sd.on_sd_meta_queue_changed() | ||
3085 | 886 | |||
3086 | 887 | # second call returns | ||
3087 | 888 | d2.callback(['foo']) | ||
3088 | 889 | d3.callback(['foo']) | ||
3089 | 890 | |||
3090 | 891 | # it should be called three times total | ||
3091 | 892 | self.assertEqual(len(called), 3) | ||
3092 | 893 | |||
3093 | 894 | @defer.inlineCallbacks | ||
3094 | 895 | def test_mq_multiple_support_error(self): | ||
3095 | 896 | """Leave the state ok even with an error in the underlying call.""" | ||
3096 | 897 | def fake(): | ||
3097 | 898 | """Fake.""" | ||
3098 | 899 | raise ValueError('foo') | ||
3099 | 900 | self.sd.dbus.get_meta_queue = fake | ||
3100 | 901 | |||
3101 | 902 | # call it, and "absorb" the error we just generated | ||
3102 | 903 | try: | ||
3103 | 904 | yield self.sd.on_sd_meta_queue_changed() | ||
3104 | 905 | except ValueError: | ||
3105 | 906 | pass | ||
3106 | 907 | |||
3107 | 908 | # it should be in IDLE | ||
3108 | 909 | self.assertEqual(self.sd._mq_asking, ASKING_IDLE) | ||
3109 | 910 | |||
3110 | 587 | 911 | ||
3111 | 588 | class StateTests(unittest.TestCase): | 912 | class StateTests(unittest.TestCase): |
3112 | 589 | """Test State class.""" | 913 | """Test State class.""" |
3113 | @@ -732,13 +1056,6 @@ | |||
3114 | 732 | False, 'queues', 'connection') | 1056 | False, 'queues', 'connection') |
3115 | 733 | self.assertTrue(self.called) | 1057 | self.assertTrue(self.called) |
3116 | 734 | 1058 | ||
3117 | 735 | @defer.inlineCallbacks | ||
3118 | 736 | def test_on_initial_data_ready(self): | ||
3119 | 737 | """Called when SD gets all the initial data.""" | ||
3120 | 738 | self.flag_called(self.sd, 'on_initial_data_ready_callback') | ||
3121 | 739 | yield self.sd._get_initial_data() | ||
3122 | 740 | self.assertTrue(self.called) | ||
3123 | 741 | |||
3124 | 742 | 1059 | ||
3125 | 743 | class TestLogs(unittest.TestCase): | 1060 | class TestLogs(unittest.TestCase): |
3126 | 744 | """Test logging.""" | 1061 | """Test logging.""" |
3127 | @@ -746,8 +1063,10 @@ | |||
3128 | 746 | def setUp(self): | 1063 | def setUp(self): |
3129 | 747 | """Set up.""" | 1064 | """Set up.""" |
3130 | 748 | self.hdlr = MementoHandler() | 1065 | self.hdlr = MementoHandler() |
3131 | 749 | logging.getLogger('magicicada.syncdaemon').addHandler(self.hdlr) | ||
3132 | 750 | self.hdlr.setLevel(logging.DEBUG) | 1066 | self.hdlr.setLevel(logging.DEBUG) |
3133 | 1067 | logger = logging.getLogger('magicicada.syncdaemon') | ||
3134 | 1068 | logger.addHandler(self.hdlr) | ||
3135 | 1069 | logger.setLevel(logging.DEBUG) | ||
3136 | 751 | self.sd = SyncDaemon(FakeDBusInterface) | 1070 | self.sd = SyncDaemon(FakeDBusInterface) |
3137 | 752 | 1071 | ||
3138 | 753 | def tearDown(self): | 1072 | def tearDown(self): |
3139 | @@ -768,8 +1087,12 @@ | |||
3140 | 768 | def test_initial_value(self): | 1087 | def test_initial_value(self): |
3141 | 769 | """Log the initial filling.""" | 1088 | """Log the initial filling.""" |
3142 | 770 | yield self.sd._get_initial_data() | 1089 | yield self.sd._get_initial_data() |
3145 | 771 | self.assertTrue(self.hdlr.check_info("Getting initial data")) | 1090 | self.assertTrue(self.hdlr.check_info("Getting offline initial data")) |
3146 | 772 | self.assertTrue(self.hdlr.check_info("All initial data is ready")) | 1091 | self.assertTrue(self.hdlr.check_info( |
3147 | 1092 | "All initial offline data is ready")) | ||
3148 | 1093 | self.assertTrue(self.hdlr.check_info("Getting online initial data")) | ||
3149 | 1094 | self.assertTrue(self.hdlr.check_info( | ||
3150 | 1095 | "All initial online data is ready")) | ||
3151 | 773 | 1096 | ||
3152 | 774 | def test_start(self): | 1097 | def test_start(self): |
3153 | 775 | """Log the call to start.""" | 1098 | """Log the call to start.""" |
3154 | @@ -823,7 +1146,7 @@ | |||
3155 | 823 | self.assertTrue(self.hdlr.check_info( | 1146 | self.assertTrue(self.hdlr.check_info( |
3156 | 824 | "SD Meta Queue info is new! 1 items")) | 1147 | "SD Meta Queue info is new! 1 items")) |
3157 | 825 | 1148 | ||
3159 | 826 | def test_content_queue_changed(self): | 1149 | def test_meta_queue_changed(self): |
3160 | 827 | """Log that process_cq has new data.""" | 1150 | """Log that process_cq has new data.""" |
3161 | 828 | self.sd.dbus.get_content_queue = lambda: defer.succeed(['foo']) | 1151 | self.sd.dbus.get_content_queue = lambda: defer.succeed(['foo']) |
3162 | 829 | self.sd.on_sd_content_queue_changed() | 1152 | self.sd.on_sd_content_queue_changed() |
3163 | @@ -857,13 +1180,32 @@ | |||
3164 | 857 | self.sd.on_sd_name_owner_changed(True) | 1180 | self.sd.on_sd_name_owner_changed(True) |
3165 | 858 | self.assertTrue(self.hdlr.check_info("SD Name Owner changed: True")) | 1181 | self.assertTrue(self.hdlr.check_info("SD Name Owner changed: True")) |
3166 | 859 | 1182 | ||
3167 | 1183 | def test_on_public_files_list(self): | ||
3168 | 1184 | """Log we got a new public files list.""" | ||
3169 | 1185 | pf1 = PublicFilesData(volume='v', node='n1', path='p', public_url='u') | ||
3170 | 1186 | pf2 = PublicFilesData(volume='v', node='n2', path='p', public_url='u') | ||
3171 | 1187 | self.sd.on_sd_public_files_list([pf1, pf2]) | ||
3172 | 1188 | self.assertTrue(self.hdlr.check_info( | ||
3173 | 1189 | "Got new Public Files list (2 items)")) | ||
3174 | 1190 | |||
3175 | 1191 | def test_on_public_files_changed(self): | ||
3176 | 1192 | """Log we got a change in the public files list.""" | ||
3177 | 1193 | self.sd.public_files = {} | ||
3178 | 1194 | pf = PublicFilesData(volume='volume', node='node', | ||
3179 | 1195 | path='path', public_url='url') | ||
3180 | 1196 | self.sd.on_sd_public_files_changed(pf, True) | ||
3181 | 1197 | self.assertTrue(self.hdlr.check_info( | ||
3182 | 1198 | "Change in Public Files list! is_public=True", | ||
3183 | 1199 | "node=node", "path=u'path'")) | ||
3184 | 1200 | |||
3185 | 860 | 1201 | ||
3186 | 861 | class MetadataTests(BaseTest): | 1202 | class MetadataTests(BaseTest): |
3187 | 862 | """Get Metadata info.""" | 1203 | """Get Metadata info.""" |
3188 | 863 | 1204 | ||
3189 | 864 | def test_get_metadata_no_callback_set(self): | 1205 | def test_get_metadata_no_callback_set(self): |
3190 | 865 | """It's mandatory to set the callback for this response.""" | 1206 | """It's mandatory to set the callback for this response.""" |
3192 | 866 | self.assertRaises(ValueError, self.sd.get_metadata, 'path') | 1207 | self.sd.on_metadata_ready_callback() |
3193 | 1208 | self.assertTrue(self.hdlr.check_warning('on_metadata_ready_callback')) | ||
3194 | 867 | 1209 | ||
3195 | 868 | def test_get_metadata_ok(self): | 1210 | def test_get_metadata_ok(self): |
3196 | 869 | """Get the metadata for given path.""" | 1211 | """Get the metadata for given path.""" |
3197 | @@ -988,3 +1330,139 @@ | |||
3198 | 988 | 1330 | ||
3199 | 989 | # test | 1331 | # test |
3200 | 990 | self.assertEqual(cal, [2]) | 1332 | self.assertEqual(cal, [2]) |
3201 | 1333 | |||
3202 | 1334 | |||
3203 | 1335 | class PublicFilesTests(BaseTest): | ||
3204 | 1336 | """PublicFiles checking.""" | ||
3205 | 1337 | |||
3206 | 1338 | def test_signal_updates_value(self): | ||
3207 | 1339 | """Test that when signal arrives it updates the internal attribute.""" | ||
3208 | 1340 | pf = PublicFilesData(volume='volume', node='node', | ||
3209 | 1341 | path='path', public_url='url') | ||
3210 | 1342 | self.sd.on_sd_public_files_list([pf]) | ||
3211 | 1343 | self.assertEqual(len(self.sd.public_files), 1) | ||
3212 | 1344 | pf = self.sd.public_files['node'] | ||
3213 | 1345 | self.assertEqual(pf.volume, 'volume') | ||
3214 | 1346 | self.assertEqual(pf.node, 'node') | ||
3215 | 1347 | self.assertEqual(pf.public_url, 'url') | ||
3216 | 1348 | self.assertEqual(pf.path, 'path') | ||
3217 | 1349 | |||
3218 | 1350 | def test_public_files_changed_added_noprevious(self): | ||
3219 | 1351 | """Test that it adds the new public file.""" | ||
3220 | 1352 | # reset and add | ||
3221 | 1353 | self.sd.public_files = {} | ||
3222 | 1354 | pf = PublicFilesData(volume='volume', node='node', | ||
3223 | 1355 | path='path', public_url='url') | ||
3224 | 1356 | self.sd.on_sd_public_files_changed(pf, True) | ||
3225 | 1357 | |||
3226 | 1358 | # check is there | ||
3227 | 1359 | pf = self.sd.public_files['node'] | ||
3228 | 1360 | self.assertEqual(pf.volume, 'volume') | ||
3229 | 1361 | self.assertEqual(pf.node, 'node') | ||
3230 | 1362 | self.assertEqual(pf.public_url, 'url') | ||
3231 | 1363 | self.assertEqual(pf.path, 'path') | ||
3232 | 1364 | |||
3233 | 1365 | def test_public_files_changed_added_previous(self): | ||
3234 | 1366 | """Test that it updates the public file.""" | ||
3235 | 1367 | # add one | ||
3236 | 1368 | pf = PublicFilesData(volume='volume', node='node', | ||
3237 | 1369 | path='path', public_url='url1') | ||
3238 | 1370 | self.sd.on_sd_public_files_changed(pf, True) | ||
3239 | 1371 | |||
3240 | 1372 | # update it | ||
3241 | 1373 | pf = PublicFilesData(volume='volume', node='node', | ||
3242 | 1374 | path='path', public_url='url2') | ||
3243 | 1375 | self.sd.on_sd_public_files_changed(pf, True) | ||
3244 | 1376 | |||
3245 | 1377 | # check is updated | ||
3246 | 1378 | pf = self.sd.public_files['node'] | ||
3247 | 1379 | self.assertEqual(pf.public_url, 'url2') | ||
3248 | 1380 | |||
3249 | 1381 | def test_public_files_changed_removed_previous(self): | ||
3250 | 1382 | """Test that it deletes the public file.""" | ||
3251 | 1383 | # add one | ||
3252 | 1384 | pf = PublicFilesData(volume='volume', node='node', | ||
3253 | 1385 | path='path', public_url='url') | ||
3254 | 1386 | self.sd.on_sd_public_files_changed(pf, True) | ||
3255 | 1387 | |||
3256 | 1388 | # remove it | ||
3257 | 1389 | self.sd.on_sd_public_files_changed(pf, False) | ||
3258 | 1390 | |||
3259 | 1391 | # check is no longer there | ||
3260 | 1392 | self.assertFalse('node' in self.sd.public_files) | ||
3261 | 1393 | |||
3262 | 1394 | def test_public_files_changed_removed_noprevious(self): | ||
3263 | 1395 | """Test that support the deletion of anything.""" | ||
3264 | 1396 | # reset and remove | ||
3265 | 1397 | self.sd.public_files = {} | ||
3266 | 1398 | pf = PublicFilesData(volume='volume', node='node', | ||
3267 | 1399 | path='path', public_url='url') | ||
3268 | 1400 | self.sd.on_sd_public_files_changed(pf, False) | ||
3269 | 1401 | |||
3270 | 1402 | # check that still is not there | ||
3271 | 1403 | self.assertFalse('node' in self.sd.public_files) | ||
3272 | 1404 | |||
3273 | 1405 | |||
3274 | 1406 | class HandlingSharesTests(BaseTest): | ||
3275 | 1407 | """Handling shares checking.""" | ||
3276 | 1408 | |||
3277 | 1409 | def test_on_share_op_success_callback_set(self): | ||
3278 | 1410 | """It's mandatory to set the callback for this response.""" | ||
3279 | 1411 | self.sd.on_share_op_success_callback() | ||
3280 | 1412 | self.assertTrue(self.hdlr.check_warning( | ||
3281 | 1413 | 'on_share_op_success_callback')) | ||
3282 | 1414 | |||
3283 | 1415 | def test_on_share_op_error_callback_set(self): | ||
3284 | 1416 | """It's mandatory to set the callback for this response.""" | ||
3285 | 1417 | self.sd.on_share_op_error_callback() | ||
3286 | 1418 | self.assertTrue(self.hdlr.check_warning('on_share_op_error_callback')) | ||
3287 | 1419 | |||
3288 | 1420 | def test_accept_share_ok(self): | ||
3289 | 1421 | """Accepting a share finishes ok.""" | ||
3290 | 1422 | # monkeypatch | ||
3291 | 1423 | called = [] | ||
3292 | 1424 | self.sd.dbus.fake_share_response = defer.succeed(None) | ||
3293 | 1425 | self.sd.on_share_op_success_callback = lambda sid: called.append(sid) | ||
3294 | 1426 | self.sd.on_share_op_error_callback = lambda *a: None | ||
3295 | 1427 | |||
3296 | 1428 | # execute and test | ||
3297 | 1429 | self.sd.accept_share('share_id') | ||
3298 | 1430 | self.assertEqual(called, ['share_id']) | ||
3299 | 1431 | self.assertTrue(self.hdlr.check_info("Accepting share", "share_id", | ||
3300 | 1432 | "started")) | ||
3301 | 1433 | self.assertTrue(self.hdlr.check_info("Accepting share", "share_id", | ||
3302 | 1434 | "finished successfully")) | ||
3303 | 1435 | |||
3304 | 1436 | def test_accept_share_failure(self): | ||
3305 | 1437 | """Accepting a share finishes bad.""" | ||
3306 | 1438 | # monkeypatch | ||
3307 | 1439 | called = [] | ||
3308 | 1440 | e = ShareOperationError(share_id='foo', error='bar') | ||
3309 | 1441 | self.sd.dbus.fake_share_response = defer.fail(e) | ||
3310 | 1442 | self.sd.on_share_op_error_callback = \ | ||
3311 | 1443 | lambda sid, e: called.append((sid, e)) | ||
3312 | 1444 | self.sd.on_share_op_success_callback = lambda *a: None | ||
3313 | 1445 | |||
3314 | 1446 | # execute and test | ||
3315 | 1447 | self.sd.accept_share('share_id') | ||
3316 | 1448 | self.assertEqual(called, [('share_id', 'bar')]) | ||
3317 | 1449 | self.assertTrue(self.hdlr.check_info("Accepting share", "share_id", | ||
3318 | 1450 | "started")) | ||
3319 | 1451 | self.assertTrue(self.hdlr.check_info("Accepting share", "share_id", | ||
3320 | 1452 | "finished with error", "bar")) | ||
3321 | 1453 | |||
3322 | 1454 | def test_accept_share_error(self): | ||
3323 | 1455 | """Really bad error when accepting a share.""" | ||
3324 | 1456 | # monkeypatch | ||
3325 | 1457 | e = ValueError('unexpected failure') | ||
3326 | 1458 | self.sd.dbus.fake_share_response = defer.fail(e) | ||
3327 | 1459 | self.sd.on_share_op_error_callback = lambda *a: None | ||
3328 | 1460 | self.sd.on_share_op_success_callback = lambda *a: None | ||
3329 | 1461 | |||
3330 | 1462 | # execute and test | ||
3331 | 1463 | self.sd.accept_share('share_id') | ||
3332 | 1464 | self.assertTrue(self.hdlr.check_info("Accepting share", "share_id", | ||
3333 | 1465 | "started")) | ||
3334 | 1466 | self.assertTrue(self.hdlr.check_error( | ||
3335 | 1467 | "Unexpected error when accepting share", "share_id", | ||
3336 | 1468 | "ValueError", "unexpected failure")) | ||
3337 | 991 | 1469 | ||
3338 | === modified file 'setup.py' (properties changed: -x to +x) | |||
3339 | --- setup.py 2010-09-02 19:26:08 +0000 | |||
3340 | +++ setup.py 2011-01-07 23:06:32 +0000 | |||
3341 | @@ -1,13 +1,21 @@ | |||
3342 | 1 | #!/usr/bin/env python | 1 | #!/usr/bin/env python |
3347 | 2 | # -*- coding: utf-8 -*- | 2 | |
3348 | 3 | ### BEGIN LICENSE | 3 | # Copyright 2010 Chicharreros |
3349 | 4 | # This file is in the public domain | 4 | # |
3350 | 5 | ### END LICENSE | 5 | # This program is free software: you can redistribute it and/or modify it |
3351 | 6 | # under the terms of the GNU General Public License version 3, as published | ||
3352 | 7 | # by the Free Software Foundation. | ||
3353 | 8 | # | ||
3354 | 9 | # This program is distributed in the hope that it will be useful, but | ||
3355 | 10 | # WITHOUT ANY WARRANTY; without even the implied warranties of | ||
3356 | 11 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | ||
3357 | 12 | # PURPOSE. See the GNU General Public License for more details. | ||
3358 | 13 | # | ||
3359 | 14 | # You should have received a copy of the GNU General Public License along | ||
3360 | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. | ||
3361 | 6 | 16 | ||
3362 | 7 | """Build tar.gz and related for magicicada.""" | 17 | """Build tar.gz and related for magicicada.""" |
3363 | 8 | 18 | ||
3364 | 9 | ################# DO NOT TOUCH THIS (HEAD TO THE SECOND PART) ################# | ||
3365 | 10 | |||
3366 | 11 | import os | 19 | import os |
3367 | 12 | import sys | 20 | import sys |
3368 | 13 | 21 | ||
3369 | @@ -80,17 +88,14 @@ | |||
3370 | 80 | update_data_path(self.prefix, previous_value) | 88 | update_data_path(self.prefix, previous_value) |
3371 | 81 | 89 | ||
3372 | 82 | 90 | ||
3373 | 83 | ############################################################################## | ||
3374 | 84 | #################### YOU SHOULD MODIFY ONLY WHAT IS BELOW #################### | ||
3375 | 85 | ############################################################################## | ||
3376 | 86 | |||
3377 | 87 | DistUtilsExtra.auto.setup( | 91 | DistUtilsExtra.auto.setup( |
3378 | 88 | name='magicicada', | 92 | name='magicicada', |
3380 | 89 | version='0.2', | 93 | version='0.3.0', |
3381 | 90 | license='GPL-3', | 94 | license='GPL-3', |
3382 | 91 | author='Natalia Bidart', | 95 | author='Natalia Bidart', |
3386 | 92 | author_email='natalia.bidart@ubuntu.com', | 96 | author_email='nataliabidart@gmail.com', |
3387 | 93 | description='A GTK+ frontend for the "Chicharra" part of Ubuntu One.', | 97 | description='A GTK+ frontend for Ubuntu One.', |
3388 | 94 | #long_description='Here a longer description', | 98 | long_description='This application provides a GTK frontend to manage ' \ |
3389 | 99 | 'the file synchronisation service of Ubuntu One.', | ||
3390 | 95 | url='https://launchpad.net/magicicada', | 100 | url='https://launchpad.net/magicicada', |
3391 | 96 | cmdclass={'install': InstallAndUpdateDataDirectory}) | 101 | cmdclass={'install': InstallAndUpdateDataDirectory}) |
Hi looks ok, but a couple of small things pile up: ubuntu- one-client (>= foo), depend on python-xdg
* changelog is a bit weirdly formatted (text indented to the same level as the bullets.
* You can use this syntax: (LP: #587020, #643195)
* I'd prefer something a little more concrete than "updated dependency list".
It looks like: sorted depends, dropped depends-indep, depend on python-
* No mention of the standards version update (these are traditionally changelogged as "Bumped Standards-Version to X.Y.Z (no changes needed)"
* No mention of the maintainer change in the changelog. Should the maintainer not be Ubuntu Developers <email address hidden>?
* No mention of the description change.
* Copyright:
- I assume the copyright years need a bump.
- DEP5 has evolved quite a bit, at some point this file should be updated to match the current standards.
- The short GPL licence grant should be included before the "full text" notice.
* At some point, you should consider migrating to dh_python2, AFAIK doko is quite keen for Ubuntu-specific packages to migrate during natty.