Merge lp:~dobey/ubuntu/oneiric/ubuntuone-control-panel/release-113 into lp:ubuntu/oneiric/ubuntuone-control-panel

Proposed by dobey
Status: Merged
Merged at revision: 29
Proposed branch: lp:~dobey/ubuntu/oneiric/ubuntuone-control-panel/release-113
Merge into: lp:ubuntu/oneiric/ubuntuone-control-panel
Diff against target: 5305 lines (+1530/-1352)
42 files modified
PKG-INFO (+2/-2)
bin/ubuntuone-control-panel-gtk (+3/-3)
bin/ubuntuone-control-panel-qt (+3/-3)
data/qt/account.ui (+2/-2)
data/qt/controlpanel.ui (+42/-39)
data/qt/device.ui (+3/-0)
data/qt/filesyncstatus.ui (+4/-2)
data/qt/folders.ui (+8/-5)
data/qt/images.qrc (+2/-0)
data/qt/mainwindow.ui (+1/-1)
data/qt/preferences.ui (+4/-1)
data/qt/ubuntuone.qss (+63/-46)
debian/changelog (+7/-0)
po/ubuntuone-control-panel.pot (+1/-1)
run-tests (+4/-2)
run-tests.bat (+1/-1)
setup.py (+13/-7)
ubuntuone/controlpanel/backend.py (+65/-57)
ubuntuone/controlpanel/dbus_service.py (+12/-4)
ubuntuone/controlpanel/dbustests/test_dbus_service.py (+1/-1)
ubuntuone/controlpanel/dbustests/test_sd_client/test_linux.py (+2/-1)
ubuntuone/controlpanel/gui/gtk/gui.py (+48/-77)
ubuntuone/controlpanel/gui/gtk/tests/__init__.py (+12/-14)
ubuntuone/controlpanel/gui/gtk/tests/test_gui.py (+24/-23)
ubuntuone/controlpanel/gui/gtk/tests/test_gui_basic.py (+126/-81)
ubuntuone/controlpanel/gui/qt/filesyncstatus.py (+8/-0)
ubuntuone/controlpanel/gui/qt/tests/test_filesyncstatus.py (+12/-0)
ubuntuone/controlpanel/gui/qt/ui/account_ui.py (+3/-3)
ubuntuone/controlpanel/gui/qt/ui/controlpanel_ui.py (+24/-12)
ubuntuone/controlpanel/gui/qt/ui/device_ui.py (+2/-1)
ubuntuone/controlpanel/gui/qt/ui/devices_ui.py (+1/-1)
ubuntuone/controlpanel/gui/qt/ui/filesyncstatus_ui.py (+2/-1)
ubuntuone/controlpanel/gui/qt/ui/folders_ui.py (+3/-4)
ubuntuone/controlpanel/gui/qt/ui/images_rc.py (+459/-447)
ubuntuone/controlpanel/gui/qt/ui/loadingoverlay_ui.py (+1/-1)
ubuntuone/controlpanel/gui/qt/ui/mainwindow_ui.py (+2/-2)
ubuntuone/controlpanel/gui/qt/ui/preferences_ui.py (+3/-2)
ubuntuone/controlpanel/login_client.py (+18/-5)
ubuntuone/controlpanel/sd_client/__init__.py (+176/-211)
ubuntuone/controlpanel/tests/test_backend.py (+205/-168)
ubuntuone/controlpanel/tests/test_login_client.py (+48/-6)
ubuntuone/controlpanel/tests/test_sd_client.py (+110/-115)
To merge this branch: bzr merge lp:~dobey/ubuntu/oneiric/ubuntuone-control-panel/release-113
Reviewer Review Type Date Requested Status
Ubuntu branches Pending
Review via email: mp+72945@code.launchpad.net
To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'PKG-INFO'
--- PKG-INFO 2011-08-12 19:12:08 +0000
+++ PKG-INFO 2011-08-25 19:40:18 +0000
@@ -1,12 +1,12 @@
1Metadata-Version: 1.11Metadata-Version: 1.1
2Name: ubuntuone-control-panel2Name: ubuntuone-control-panel
3Version: 1.1.23Version: 1.1.3
4Summary: Ubuntu One Control Panel4Summary: Ubuntu One Control Panel
5Home-page: https://launchpad.net/ubuntuone-control-panel5Home-page: https://launchpad.net/ubuntuone-control-panel
6Author: Natalia Bidart6Author: Natalia Bidart
7Author-email: natalia.bidart@canonical.com7Author-email: natalia.bidart@canonical.com
8License: GPL v38License: GPL v3
9Description: Application to manage a Ubuntu One account. Provides aDBus service to query/modify all the Ubuntu One bits.9Description: Application to manage a Ubuntu One account. Provides a DBus service to query/modify all the Ubuntu One bits.
10Platform: UNKNOWN10Platform: UNKNOWN
11Requires: PyQt411Requires: PyQt4
12Requires: apt12Requires: apt
1313
=== modified file 'bin/ubuntuone-control-panel-gtk'
--- bin/ubuntuone-control-panel-gtk 2011-07-22 21:26:48 +0000
+++ bin/ubuntuone-control-panel-gtk 2011-08-25 19:40:18 +0000
@@ -39,12 +39,12 @@
39 result = OptionParser(usage=usage)39 result = OptionParser(usage=usage)
40 result.add_option("", "--switch-to", dest="switch_to", type="string",40 result.add_option("", "--switch-to", dest="switch_to", type="string",
41 metavar="PANEL_NAME", default="",41 metavar="PANEL_NAME", default="",
42 help="Start the Ubuntu One Control Panel (GTK) in the "42 help="Start Ubuntu One in the "
43 "PANEL_NAME tab. Possible values are: "43 "PANEL_NAME tab. Possible values are: "
44 "dashboard, volumes, devices, applications")44 "dashboard, volumes, devices, applications")
45 result.add_option("-a", "--alert", dest="alert", action="store_true",45 result.add_option("-a", "--alert", dest="alert", action="store_true",
46 default=False, help="Start the Ubuntu One Control Panel "46 default=False, help="Start Ubuntu One "
47 "(GTK) alerting the user to its presence.")47 "alerting the user to its presence.")
48 return result48 return result
4949
5050
5151
=== modified file 'bin/ubuntuone-control-panel-qt'
--- bin/ubuntuone-control-panel-qt 2011-07-22 21:26:48 +0000
+++ bin/ubuntuone-control-panel-qt 2011-08-25 19:40:18 +0000
@@ -39,12 +39,12 @@
39 result = OptionParser(usage=usage)39 result = OptionParser(usage=usage)
40 result.add_option("", "--switch-to", dest="switch_to", type="string",40 result.add_option("", "--switch-to", dest="switch_to", type="string",
41 metavar="PANEL_NAME", default="",41 metavar="PANEL_NAME", default="",
42 help="Start the Ubuntu One Control Panel (QT) in the "42 help="Start Ubuntu One in the "
43 "PANEL_NAME tab. Possible values are: "43 "PANEL_NAME tab. Possible values are: "
44 "dashboard, volumes, devices, applications")44 "dashboard, volumes, devices, applications")
45 result.add_option("-a", "--alert", dest="alert", action="store_true",45 result.add_option("-a", "--alert", dest="alert", action="store_true",
46 default=False, help="Start the Ubuntu One Control Panel "46 default=False, help="Start Ubuntu One "
47 "(QT) alerting the user to its presence.")47 "alerting the user to its presence.")
48 return result48 return result
4949
5050
5151
=== modified file 'data/facebook.png'
52Binary files data/facebook.png 2011-01-25 19:08:59 +0000 and data/facebook.png 2011-08-25 19:40:18 +0000 differ52Binary files data/facebook.png 2011-01-25 19:08:59 +0000 and data/facebook.png 2011-08-25 19:40:18 +0000 differ
=== modified file 'data/qt/account.ui'
--- data/qt/account.ui 2011-08-12 19:12:08 +0000
+++ data/qt/account.ui 2011-08-25 19:40:18 +0000
@@ -22,7 +22,7 @@
22 <property name="verticalSpacing">22 <property name="verticalSpacing">
23 <number>30</number>23 <number>30</number>
24 </property>24 </property>
25 <item row="2" column="2">25 <item row="0" column="2">
26 <widget class="GoToWebButton" name="edit_profile_button">26 <widget class="GoToWebButton" name="edit_profile_button">
27 <property name="text">27 <property name="text">
28 <string>Edit personal details online</string>28 <string>Edit personal details online</string>
@@ -85,7 +85,7 @@
85 </layout>85 </layout>
86 </widget>86 </widget>
87 </item>87 </item>
88 <item row="0" column="2">88 <item row="2" column="2">
89 <widget class="GoToWebButton" name="edit_services_button">89 <widget class="GoToWebButton" name="edit_services_button">
90 <property name="text">90 <property name="text">
91 <string>Edit your services online</string>91 <string>Edit your services online</string>
9292
=== modified file 'data/qt/controlpanel.ui'
--- data/qt/controlpanel.ui 2011-08-12 19:12:08 +0000
+++ data/qt/controlpanel.ui 2011-08-25 19:40:18 +0000
@@ -6,8 +6,8 @@
6 <rect>6 <rect>
7 <x>0</x>7 <x>0</x>
8 <y>0</y>8 <y>0</y>
9 <width>387</width>9 <width>367</width>
10 <height>203</height>10 <height>142</height>
11 </rect>11 </rect>
12 </property>12 </property>
13 <property name="sizePolicy">13 <property name="sizePolicy">
@@ -221,7 +221,7 @@
221 </widget>221 </widget>
222 <widget class="PreferencesPanel" name="preferences_tab">222 <widget class="PreferencesPanel" name="preferences_tab">
223 <attribute name="title">223 <attribute name="title">
224 <string>Preferences</string>224 <string>Settings</string>
225 </attribute>225 </attribute>
226 </widget>226 </widget>
227 <widget class="AccountPanel" name="account_tab">227 <widget class="AccountPanel" name="account_tab">
@@ -233,22 +233,25 @@
233 </item>233 </item>
234 <item>234 <item>
235 <widget class="QFrame" name="frame_footer">235 <widget class="QFrame" name="frame_footer">
236 <property name="sizePolicy">
237 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
238 <horstretch>0</horstretch>
239 <verstretch>0</verstretch>
240 </sizepolicy>
241 </property>
242 <property name="maximumSize">
243 <size>
244 <width>16777215</width>
245 <height>30</height>
246 </size>
247 </property>
236 <layout class="QHBoxLayout" name="horizontalLayout">248 <layout class="QHBoxLayout" name="horizontalLayout">
237 <property name="spacing">249 <property name="spacing">
238 <number>5</number>250 <number>5</number>
239 </property>251 </property>
240 <property name="leftMargin">252 <property name="margin">
241 <number>3</number>
242 </property>
243 <property name="topMargin">
244 <number>0</number>253 <number>0</number>
245 </property>254 </property>
246 <property name="rightMargin">
247 <number>3</number>
248 </property>
249 <property name="bottomMargin">
250 <number>3</number>
251 </property>
252 <item>255 <item>
253 <widget class="GoToWebButton" name="help_button">256 <widget class="GoToWebButton" name="help_button">
254 <property name="text">257 <property name="text">
@@ -282,49 +285,49 @@
282 </widget>285 </widget>
283 </item>286 </item>
284 <item>287 <item>
285 <widget class="QToolButton" name="twitter_button">288 <widget class="QPushButton" name="twitter_button">
289 <property name="sizePolicy">
290 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
291 <horstretch>0</horstretch>
292 <verstretch>0</verstretch>
293 </sizepolicy>
294 </property>
295 <property name="maximumSize">
296 <size>
297 <width>16</width>
298 <height>16</height>
299 </size>
300 </property>
286 <property name="cursor">301 <property name="cursor">
287 <cursorShape>PointingHandCursor</cursorShape>302 <cursorShape>PointingHandCursor</cursorShape>
288 </property>303 </property>
289 <property name="styleSheet">
290 <string notr="true">border: 0;</string>
291 </property>
292 <property name="text">
293 <string/>
294 </property>
295 <property name="icon">304 <property name="icon">
296 <iconset resource="images.qrc">305 <iconset resource="images.qrc">
297 <normaloff>:/twitter.png</normaloff>:/twitter.png</iconset>306 <normaloff>:/twitter.png</normaloff>:/twitter.png</iconset>
298 </property>307 </property>
299 <property name="iconSize">
300 <size>
301 <width>22</width>
302 <height>22</height>
303 </size>
304 </property>
305 </widget>308 </widget>
306 </item>309 </item>
307 <item>310 <item>
308 <widget class="QToolButton" name="facebook_button">311 <widget class="QPushButton" name="facebook_button">
312 <property name="sizePolicy">
313 <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
314 <horstretch>0</horstretch>
315 <verstretch>0</verstretch>
316 </sizepolicy>
317 </property>
318 <property name="maximumSize">
319 <size>
320 <width>16</width>
321 <height>16</height>
322 </size>
323 </property>
309 <property name="cursor">324 <property name="cursor">
310 <cursorShape>PointingHandCursor</cursorShape>325 <cursorShape>PointingHandCursor</cursorShape>
311 </property>326 </property>
312 <property name="styleSheet">
313 <string notr="true">border: 0;</string>
314 </property>
315 <property name="text">
316 <string/>
317 </property>
318 <property name="icon">327 <property name="icon">
319 <iconset resource="images.qrc">328 <iconset resource="images.qrc">
320 <normaloff>:/facebook.png</normaloff>:/facebook.png</iconset>329 <normaloff>:/facebook.png</normaloff>:/facebook.png</iconset>
321 </property>330 </property>
322 <property name="iconSize">
323 <size>
324 <width>22</width>
325 <height>22</height>
326 </size>
327 </property>
328 </widget>331 </widget>
329 </item>332 </item>
330 </layout>333 </layout>
331334
=== modified file 'data/qt/device.ui'
--- data/qt/device.ui 2011-08-12 19:12:08 +0000
+++ data/qt/device.ui 2011-08-25 19:40:18 +0000
@@ -46,6 +46,9 @@
46 <property name="text">46 <property name="text">
47 <string>Remove</string>47 <string>Remove</string>
48 </property>48 </property>
49 <property name="secondary" stdset="0">
50 <bool>true</bool>
51 </property>
49 </widget>52 </widget>
50 </item>53 </item>
51 </layout>54 </layout>
5255
=== modified file 'data/qt/filesyncstatus.ui'
--- data/qt/filesyncstatus.ui 2011-08-12 19:12:08 +0000
+++ data/qt/filesyncstatus.ui 2011-08-25 19:40:18 +0000
@@ -20,8 +20,7 @@
20 <item>20 <item>
21 <layout class="QHBoxLayout" name="horizontalLayout">21 <layout class="QHBoxLayout" name="horizontalLayout">
22 <item>22 <item>
23 <widget class="QLabel" name="sync_status_icon">23 <widget class="QLabel" name="sync_status_icon"/>
24 </widget>
25 </item>24 </item>
26 <item>25 <item>
27 <widget class="QLabel" name="sync_status_label">26 <widget class="QLabel" name="sync_status_label">
@@ -40,6 +39,9 @@
40 </item>39 </item>
41 <item>40 <item>
42 <widget class="QPushButton" name="sync_status_button">41 <widget class="QPushButton" name="sync_status_button">
42 <property name="secondary" stdset="0">
43 <bool>true</bool>
44 </property>
43 </widget>45 </widget>
44 </item>46 </item>
45 </layout>47 </layout>
4648
=== modified file 'data/qt/folders.ui'
--- data/qt/folders.ui 2011-08-12 19:12:08 +0000
+++ data/qt/folders.ui 2011-08-25 19:40:18 +0000
@@ -13,9 +13,6 @@
13 <property name="windowTitle">13 <property name="windowTitle">
14 <string notr="true">Form</string>14 <string notr="true">Form</string>
15 </property>15 </property>
16 <property name="styleSheet">
17 <string notr="true">padding: 0px;</string>
18 </property>
19 <layout class="QVBoxLayout" name="verticalLayout">16 <layout class="QVBoxLayout" name="verticalLayout">
20 <property name="spacing">17 <property name="spacing">
21 <number>0</number>18 <number>0</number>
@@ -38,7 +35,7 @@
38 <item>35 <item>
39 <widget class="GoToWebButton" name="share_publish_button">36 <widget class="GoToWebButton" name="share_publish_button">
40 <property name="sizePolicy">37 <property name="sizePolicy">
41 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">38 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
42 <horstretch>0</horstretch>39 <horstretch>0</horstretch>
43 <verstretch>0</verstretch>40 <verstretch>0</verstretch>
44 </sizepolicy>41 </sizepolicy>
@@ -136,6 +133,9 @@
136 <property name="orientation">133 <property name="orientation">
137 <enum>Qt::Horizontal</enum>134 <enum>Qt::Horizontal</enum>
138 </property>135 </property>
136 <property name="sizeType">
137 <enum>QSizePolicy::Expanding</enum>
138 </property>
139 <property name="sizeHint" stdset="0">139 <property name="sizeHint" stdset="0">
140 <size>140 <size>
141 <width>53</width>141 <width>53</width>
@@ -147,7 +147,7 @@
147 <item>147 <item>
148 <widget class="AddFolderButton" name="add_folder_button">148 <widget class="AddFolderButton" name="add_folder_button">
149 <property name="sizePolicy">149 <property name="sizePolicy">
150 <sizepolicy hsizetype="Preferred" vsizetype="Preferred">150 <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
151 <horstretch>0</horstretch>151 <horstretch>0</horstretch>
152 <verstretch>0</verstretch>152 <verstretch>0</verstretch>
153 </sizepolicy>153 </sizepolicy>
@@ -162,6 +162,9 @@
162 <property name="orientation">162 <property name="orientation">
163 <enum>Qt::Horizontal</enum>163 <enum>Qt::Horizontal</enum>
164 </property>164 </property>
165 <property name="sizeType">
166 <enum>QSizePolicy::Expanding</enum>
167 </property>
165 <property name="sizeHint" stdset="0">168 <property name="sizeHint" stdset="0">
166 <size>169 <size>
167 <width>40</width>170 <width>40</width>
168171
=== modified file 'data/qt/images.qrc'
--- data/qt/images.qrc 2011-07-22 21:26:48 +0000
+++ data/qt/images.qrc 2011-08-25 19:40:18 +0000
@@ -10,6 +10,8 @@
10 <file>../computer.png</file>10 <file>../computer.png</file>
11 <file>../phone.png</file>11 <file>../phone.png</file>
12 <file>../twitter.png</file>12 <file>../twitter.png</file>
13 <file>../twitter.png</file>
14 <file>../facebook.png</file>
13 <file>../facebook.png</file>15 <file>../facebook.png</file>
14 <file>../external_icon_white.png</file>16 <file>../external_icon_white.png</file>
15 <file>../Ubuntu-R.ttf</file>17 <file>../Ubuntu-R.ttf</file>
1618
=== modified file 'data/qt/mainwindow.ui'
--- data/qt/mainwindow.ui 2011-07-22 21:26:48 +0000
+++ data/qt/mainwindow.ui 2011-08-25 19:40:18 +0000
@@ -23,7 +23,7 @@
23 </size>23 </size>
24 </property>24 </property>
25 <property name="windowTitle">25 <property name="windowTitle">
26 <string>Ubuntu One Control Panel</string>26 <string>Ubuntu One</string>
27 </property>27 </property>
28 <property name="windowIcon">28 <property name="windowIcon">
29 <iconset resource="images.qrc">29 <iconset resource="images.qrc">
3030
=== modified file 'data/qt/preferences.ui'
--- data/qt/preferences.ui 2011-08-12 19:12:08 +0000
+++ data/qt/preferences.ui 2011-08-25 19:40:18 +0000
@@ -23,7 +23,7 @@
23 <item>23 <item>
24 <widget class="QGroupBox" name="bandwidth_settings">24 <widget class="QGroupBox" name="bandwidth_settings">
25 <property name="title">25 <property name="title">
26 <string>Bandwidth settings</string>26 <string>Bandwidth Settings</string>
27 </property>27 </property>
28 <layout class="QGridLayout" name="gridLayout">28 <layout class="QGridLayout" name="gridLayout">
29 <property name="margin">29 <property name="margin">
@@ -202,6 +202,9 @@
202 <property name="text">202 <property name="text">
203 <string>Default settings</string>203 <string>Default settings</string>
204 </property>204 </property>
205 <property name="secondary" stdset="0">
206 <bool>true</bool>
207 </property>
205 </widget>208 </widget>
206 </item>209 </item>
207 </layout>210 </layout>
208211
=== modified file 'data/qt/ubuntuone.qss'
--- data/qt/ubuntuone.qss 2011-08-12 19:12:08 +0000
+++ data/qt/ubuntuone.qss 2011-08-25 19:40:18 +0000
@@ -4,6 +4,8 @@
44
5QWidget {5QWidget {
6 font-family: "Ubuntu";6 font-family: "Ubuntu";
7 font-size: 13px;
8 color: #333333;
7}9}
810
9QFrame {11QFrame {
@@ -42,59 +44,82 @@
42 border-bottom-width: 1px;44 border-bottom-width: 1px;
43}45}
4446
47QFrame#frm_box { /*The Loading Overlay frame.*/
48 background: #ffffff;
49 border-radius: 5px;
50 border-style: solid;
51 border-color: #939389;
52 border-width: 1px;
53 color: white;
54 min-height: 100px;
55}
56
57QFrame#frm_box > QLabel {
58 font-size: 24px;
59}
60
61QPushButton {
62 border-radius: 5px;
63 border-style: solid;
64 padding: 6px;
65 padding-left: 20px;
66 padding-right: 20px;
67 border-width: 1px;
68}
69
45QPushButton[enabled="true"] {70QPushButton[enabled="true"] {
46 background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,71 background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
47 stop: 0 #fecfc2, stop: 1.0 #e44e19);72 stop: 0 #fe9e84,stop: 1.0 #dd4814);
48 border-radius: 5px;
49 border-style: solid;
50 padding: 6px;
51 color: white;73 color: white;
52 border-color: #939389;74 border-color: #999999;
53 border-width: 1px;
54 height: 14px;
55}75}
5676
57QPushButton:hover[enabled="true"] {77QPushButton:hover[enabled="true"] {
58 background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,78 background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
59 stop: 0 #fedad1,stop: 1.0 #e47a55);79 stop: 0 #ffb19c,stop: 1.0 #dd4814);
60 border-radius: 5px;
61 border-style: solid;
62 padding: 6px;
63 color: white;80 color: white;
64 border-color: #939389;81 border-color: #999999;
65 border-width: 1px;
66 height: 12px;
67}82}
6883
69QPushButton:pressed[enabled="true"] {84QPushButton:pressed[enabled="true"] {
70 background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,85 background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
71 stop: 0 #e44e19,stop: 1.0 #fecfc2);86 stop: 0 #b93f14,stop: 1.0 #dd4814);
72 border-radius: 5px;
73 border-style: solid;
74 padding: 6px;
75 color: white;87 color: white;
76 border-color: #939389;88 border-color: #999999;
77 border-width: 1px;89}
78 height: 12px;90
91QPushButton[secondary="true"] {
92 background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
93 stop: 0 #ffffff,stop: 1.0 #e6e6e6);
94 color: #333333;
95 border-color: #999999;
96}
97
98QPushButton:hover[secondary="true"] {
99 background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
100 stop: 0 #ffffff,stop: 1.0 #ededed);
101 color: #333333;
102 border-color: #999999;
103}
104
105QPushButton:pressed[secondary="true"] {
106 background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
107 stop: 0 #d9d9d9,stop: 1.0 #fefefe);
108 color: #333333;
109 border-color: #999999;
79}110}
80111
81QPushButton[enabled="false"] {112QPushButton[enabled="false"] {
82 background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,113 background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,
83 stop: 0 #eaeaea, stop: 1.0 #cacaca);114 stop: 0 #eaeaea, stop: 1.0 #cacaca);
84 border-radius: 5px;
85 border-style: solid;
86 padding: 6px;
87 color: #595959;115 color: #595959;
88 border-color: #939389;116 border-color: #939389;
89 border-width: 1px;
90 height: 12px;
91}117}
92118
93QPushButton#help_button {119QPushButton#help_button {
94 background: transparent;120 background: transparent;
95 border: none;121 border: none;
96 color: white;122 color: white;
97 height: 20px;
98 text-decoration: underline;123 text-decoration: underline;
99 padding: 0px;124 padding: 0px;
100}125}
@@ -103,10 +128,13 @@
103 border: none;128 border: none;
104 background: none;129 background: none;
105 color: #595959;130 color: #595959;
131 padding-left: 10px;
132 padding-right: 10px;
106}133}
107134
108QPushButton#add_folder_button {135QPushButton#twitter_button,
109 padding: 5px;136QPushButton#facebook_button {
137 border: none;
110}138}
111139
112GoToWebButton#share_publish_button {140GoToWebButton#share_publish_button {
@@ -158,9 +186,7 @@
158}186}
159187
160QTabBar::tab:hover {188QTabBar::tab:hover {
161 background: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1,189 background: #f6f6f6;
162 stop: 0 #fafafa, stop: 0.4 #f4f4f4,
163 stop: 0.5 #e7e7e7, stop: 1.0 #fafafa);
164 text-decoration: underline;190 text-decoration: underline;
165}191}
166192
@@ -223,20 +249,11 @@
223 color: white;249 color: white;
224}250}
225251
226QFrame#frm_box { /* the loadingoverlay frame */
227 background: #ffffff;
228 border-radius: 5px;
229 border-style: solid;
230 border-color: #939389;
231 border-width: 1px;
232 color: white;
233 min-height: 100px;
234}
235
236QAbstractItemView {252QAbstractItemView {
237 border-style: solid;253 border-style: solid;
238 border-color: #333333;254 border-color: #898989;
239 border-width: 1px;255 border-top-width: 1px;
240 alternate-background-color: #efedec;256 border-bottom-width: 1px;
241 background: #f7f6f5;257 alternate-background-color: #f7f6f5;
258 background: #efedec;
242}259}
243260
=== modified file 'data/twitter.png'
244Binary files data/twitter.png 2011-01-25 19:08:59 +0000 and data/twitter.png 2011-08-25 19:40:18 +0000 differ261Binary files data/twitter.png 2011-01-25 19:08:59 +0000 and data/twitter.png 2011-08-25 19:40:18 +0000 differ
=== modified file 'debian/changelog'
--- debian/changelog 2011-08-12 19:13:51 +0000
+++ debian/changelog 2011-08-25 19:40:18 +0000
@@ -1,3 +1,10 @@
1ubuntuone-control-panel (1.1.3-0ubuntu1) oneiric; urgency=low
2
3 * New upstream release.
4 - Work correctly with static and GI bindings of gobject (LP: #829186)
5
6 -- Rodney Dawes <rodney.dawes@ubuntu.com> Thu, 25 Aug 2011 15:37:15 -0400
7
1ubuntuone-control-panel (1.1.2-0ubuntu1) oneiric; urgency=low8ubuntuone-control-panel (1.1.2-0ubuntu1) oneiric; urgency=low
29
3 * New upstream release.10 * New upstream release.
411
=== modified file 'po/ubuntuone-control-panel.pot'
--- po/ubuntuone-control-panel.pot 2011-08-12 19:12:08 +0000
+++ po/ubuntuone-control-panel.pot 2011-08-25 19:40:18 +0000
@@ -8,7 +8,7 @@
8msgstr ""8msgstr ""
9"Project-Id-Version: PACKAGE VERSION\n"9"Project-Id-Version: PACKAGE VERSION\n"
10"Report-Msgid-Bugs-To: \n"10"Report-Msgid-Bugs-To: \n"
11"POT-Creation-Date: 2011-08-12 15:07-0400\n"11"POT-Creation-Date: 2011-08-25 15:26-0400\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"14"Language-Team: LANGUAGE <LL@li.org>\n"
1515
=== modified file 'run-tests'
--- run-tests 2011-07-22 21:26:48 +0000
+++ run-tests 2011-08-25 19:40:18 +0000
@@ -37,14 +37,16 @@
37fi37fi
3838
39style_check() {39style_check() {
40 u1lint --ignore ubuntuone/controlpanel/gui/qt/ui ubuntuone/40 u1lint --ignore ubuntuone/controlpanel/gui/qt/ui
41 if [ -x `which pep8` ]; then41 if [ -x `which pep8` ]; then
42 pep8 --exclude '.svn,CVS,.bzr,.hg,.git,*_ui.py,*_rc.py' --repeat bin/ $MODULE42 pep8 --exclude '.svn,CVS,.bzr,.hg,.git,*_ui.py,*_rc.py' --repeat .
43 else43 else
44 echo "Please install the 'pep8' package."44 echo "Please install the 'pep8' package."
45 fi45 fi
46}46}
4747
48unset GTK_MODULES
49
48./setup.py build50./setup.py build
49echo "Running test suite for ""$MODULE"51echo "Running test suite for ""$MODULE"
50if [ "$USE_QT" -eq 0 ]; then52if [ "$USE_QT" -eq 0 ]; then
5153
=== modified file 'run-tests.bat'
--- run-tests.bat 2011-07-22 21:26:48 +0000
+++ run-tests.bat 2011-08-25 19:40:18 +0000
@@ -30,7 +30,7 @@
30ECHO Cleaning the generated code before running the style checks...30ECHO Cleaning the generated code before running the style checks...
31"%PYTHONEXEPATH%\python.exe" setup.py clean31"%PYTHONEXEPATH%\python.exe" setup.py clean
32ECHO Performing style checks...32ECHO Performing style checks...
33"%PYTHONEXEPATH%\Scripts\u1lint.exe" --ignore ubuntuone\controlpanel\gui\qt\ui "%MODULE%"33"%PYTHONEXEPATH%\python.exe" "%PYTHONEXEPATH%\Scripts\u1lint" --ignore ubuntuone\controlpanel\gui\qt\ui "%MODULE%"
34"%PYTHONEXEPATH%\Scripts\pep8.exe" --exclude ".svn,CVS,.bzr,.hg,.git,*_ui.py,*_rc.py" --repeat .34"%PYTHONEXEPATH%\Scripts\pep8.exe" --exclude ".svn,CVS,.bzr,.hg,.git,*_ui.py,*_rc.py" --repeat .
35:: Delete the temp folders35:: Delete the temp folders
36RMDIR /s /q _trial_temp36RMDIR /s /q _trial_temp
3737
=== modified file 'setup.py'
--- setup.py 2011-08-12 19:12:08 +0000
+++ setup.py 2011-08-25 19:40:18 +0000
@@ -46,6 +46,7 @@
46CLEANFILES = [46CLEANFILES = [
47 SERVICE_FILE, GUI_SERVICE_FILE, MESSAGE_ENTRY, CONSTANTS, POT_FILE,47 SERVICE_FILE, GUI_SERVICE_FILE, MESSAGE_ENTRY, CONSTANTS, POT_FILE,
48 'MANIFEST']48 'MANIFEST']
49QT_UI_DIR = os.path.join('ubuntuone', 'controlpanel', 'gui', 'qt', 'ui')
4950
5051
51def replace_prefix(prefix):52def replace_prefix(prefix):
@@ -64,7 +65,7 @@
64 def run(self):65 def run(self):
65 """Do the install.66 """Do the install.
6667
67 Read from *.service.in and generate .service files with reeplacing68 Read from *.service.in and generate .service files by replacing
68 @prefix@ by self.prefix.69 @prefix@ by self.prefix.
6970
70 """71 """
@@ -75,7 +76,6 @@
75class ControlPanelBuild(build_extra.build_extra):76class ControlPanelBuild(build_extra.build_extra):
76 """Build PyQt (.ui) files and resources."""77 """Build PyQt (.ui) files and resources."""
7778
78 QT_UI_DIR = os.path.join('ubuntuone', 'controlpanel', 'gui', 'qt', 'ui')
79 description = "build PyQt GUIs (.ui) and resources (.qrc)"79 description = "build PyQt GUIs (.ui) and resources (.qrc)"
8080
81 def compile_ui(self, ui_file, py_file=None):81 def compile_ui(self, ui_file, py_file=None):
@@ -86,7 +86,7 @@
86 # python file in the qt moodule86 # python file in the qt moodule
87 py_file = os.path.split(ui_file)[1]87 py_file = os.path.split(ui_file)[1]
88 py_file = os.path.splitext(py_file)[0] + '_ui.py'88 py_file = os.path.splitext(py_file)[0] + '_ui.py'
89 py_file = os.path.join(self.QT_UI_DIR, py_file)89 py_file = os.path.join(QT_UI_DIR, py_file)
90 # we indeed want to catch Exception, is ugly but we need it90 # we indeed want to catch Exception, is ugly but we need it
91 # pylint: disable=W070391 # pylint: disable=W0703
92 try:92 try:
@@ -112,7 +112,7 @@
112 if py_file is None:112 if py_file is None:
113 py_file = os.path.split(qrc_file)[1]113 py_file = os.path.split(qrc_file)[1]
114 py_file = os.path.splitext(py_file)[0] + '_rc.py'114 py_file = os.path.splitext(py_file)[0] + '_rc.py'
115 py_file = os.path.join(self.QT_UI_DIR, py_file)115 py_file = os.path.join(QT_UI_DIR, py_file)
116 path = os.getenv('PATH')116 path = os.getenv('PATH')
117 os.putenv('PATH', path + os.path.pathsep + os.path.join(117 os.putenv('PATH', path + os.path.pathsep + os.path.join(
118 os.path.dirname(PyQt4.__file__), 'bin'))118 os.path.dirname(PyQt4.__file__), 'bin'))
@@ -218,18 +218,24 @@
218 if os.path.exists(built_file):218 if os.path.exists(built_file):
219 os.unlink(built_file)219 os.unlink(built_file)
220220
221 for dirpath, _, filenames in os.walk(os.path.join(QT_UI_DIR)):
222 for current_file in filenames:
223 if current_file.endswith('_ui.py') or\
224 current_file.endswith('_rc.py'):
225 os.unlink(os.path.join(dirpath, current_file))
226
221 DistUtilsExtra.auto.clean_build_tree.run(self)227 DistUtilsExtra.auto.clean_build_tree.run(self)
222228
223229
224DistUtilsExtra.auto.setup(230DistUtilsExtra.auto.setup(
225 name='ubuntuone-control-panel',231 name='ubuntuone-control-panel',
226 version='1.1.2',232 version='1.1.3',
227 license='GPL v3',233 license='GPL v3',
228 author='Natalia Bidart',234 author='Natalia Bidart',
229 author_email='natalia.bidart@canonical.com',235 author_email='natalia.bidart@canonical.com',
230 description='Ubuntu One Control Panel',236 description='Ubuntu One Control Panel',
231 long_description='Application to manage a Ubuntu One account. Provides a'\237 long_description='Application to manage a Ubuntu One account. Provides' \
232 'DBus service to query/modify all the Ubuntu One bits.',238 ' a DBus service to query/modify all the Ubuntu One bits.',
233 url='https://launchpad.net/ubuntuone-control-panel',239 url='https://launchpad.net/ubuntuone-control-panel',
234 packages=[240 packages=[
235 'ubuntuone', 'ubuntuone.controlpanel', 'ubuntuone.controlpanel.gui',241 'ubuntuone', 'ubuntuone.controlpanel', 'ubuntuone.controlpanel.gui',
236242
=== modified file 'ubuntuone/controlpanel/backend.py'
--- ubuntuone/controlpanel/backend.py 2011-08-12 19:12:08 +0000
+++ ubuntuone/controlpanel/backend.py 2011-08-25 19:40:18 +0000
@@ -27,11 +27,12 @@
2727
28from twisted.internet.defer import inlineCallbacks, returnValue28from twisted.internet.defer import inlineCallbacks, returnValue
29# No name 'is_link' in module 'ubuntuone.platform'29# No name 'is_link' in module 'ubuntuone.platform'
30# pylint: disable=E061130# pylint: disable=E0611, F0401
31from ubuntuone.platform import is_link31from ubuntuone.platform import is_link
32# pylint: enable=E061132from ubuntuone.platform.credentials import CredentialsManagementTool
33# pylint: enable=E0611, F0401
3334
34from ubuntuone.controlpanel import login_client, sd_client, replication_client35from ubuntuone.controlpanel import sd_client, replication_client
35from ubuntuone.controlpanel.logger import setup_logging, log_call36from ubuntuone.controlpanel.logger import setup_logging, log_call
36# pylint: disable=W061137# pylint: disable=W0611
37from ubuntuone.controlpanel.web_client import (UnauthorizedError,38from ubuntuone.controlpanel.web_client import (UnauthorizedError,
@@ -92,13 +93,13 @@
9293
93 @inlineCallbacks94 @inlineCallbacks
94 @wraps(f)95 @wraps(f)
95 def inner(*args, **kwargs):96 def inner(instance, *args, **kwargs):
96 """Handle UnauthorizedError and clear credentials."""97 """Handle UnauthorizedError and clear credentials."""
97 try:98 try:
98 result = yield f(*args, **kwargs)99 result = yield f(instance, *args, **kwargs)
99 except UnauthorizedError, e:100 except UnauthorizedError, e:
100 logger.exception('process_unauthorized (clearing credentials):')101 logger.exception('process_unauthorized (clearing credentials):')
101 yield login_client.clear_credentials()102 yield instance.login_client.clear_credentials()
102 raise e103 raise e
103104
104 returnValue(result)105 returnValue(result)
@@ -126,13 +127,17 @@
126127
127 def __init__(self, shutdown_func=None):128 def __init__(self, shutdown_func=None):
128 """Initialize the web_client."""129 """Initialize the web_client."""
129 self.shutdown_func = shutdown_func
130 self.wc = web_client_factory(login_client.get_credentials)
131 self._status_changed_handler = None130 self._status_changed_handler = None
132131 self._credentials = None
133 self._volumes = {} # cache last known volume info132 self._volumes = {} # cache last known volume info
133
134 self.shutdown_func = shutdown_func
134 self.file_sync_disabled = False135 self.file_sync_disabled = False
135136
137 self.login_client = CredentialsManagementTool()
138 self.sd_client = sd_client.SyncDaemonClient()
139 self.wc = web_client_factory(self.get_credentials)
140
136 def _process_file_sync_status(self, status):141 def _process_file_sync_status(self, status):
137 """Process raw file sync status into custom format.142 """Process raw file sync status into custom format.
138143
@@ -194,11 +199,7 @@
194 handler(result)199 handler(result)
195200
196 self._status_changed_handler = handler201 self._status_changed_handler = handler
197202 self.sd_client.set_status_changed_handler(process_and_callback)
198 # XXX: since windows version of sd_client still don't have a way of
199 # setting a status changed handler, we'll be robust by doing this check
200 if getattr(sd_client, 'set_status_changed_handler', None) is not None:
201 sd_client.set_status_changed_handler(process_and_callback)
202203
203 def _get_status_changed_handler(self):204 def _get_status_changed_handler(self):
204 """Return the handler to be called when file sync status changes."""205 """Return the handler to be called when file sync status changes."""
@@ -263,7 +264,7 @@
263 If all the file sync settings are None, do not attach that info.264 If all the file sync settings are None, do not attach that info.
264265
265 """266 """
266 credentials = yield login_client.get_credentials()267 credentials = yield self.get_credentials()
267268
268 local_device = {}269 local_device = {}
269 local_device["type"] = DEVICE_TYPE_COMPUTER270 local_device["type"] = DEVICE_TYPE_COMPUTER
@@ -294,9 +295,16 @@
294 return result295 return result
295296
296 @inlineCallbacks297 @inlineCallbacks
298 def get_credentials(self):
299 """Find credentials."""
300 if self._credentials is None:
301 self._credentials = yield self.login_client.find_credentials()
302 returnValue(self._credentials)
303
304 @inlineCallbacks
297 def get_token(self):305 def get_token(self):
298 """Return the token from the credentials."""306 """Return the token from the credentials."""
299 credentials = yield login_client.get_credentials()307 credentials = yield self.get_credentials()
300 returnValue(credentials["token"])308 returnValue(credentials["token"])
301309
302 @inlineCallbacks310 @inlineCallbacks
@@ -344,25 +352,25 @@
344 autoconnect = show_notifs = None352 autoconnect = show_notifs = None
345 share_autosubscribe = udf_autosubscribe = None353 share_autosubscribe = udf_autosubscribe = None
346354
347 enabled = yield sd_client.files_sync_enabled()355 enabled = yield self.sd_client.files_sync_enabled()
348 enabled = bool(enabled)356 enabled = bool(enabled)
349 if enabled:357 if enabled:
350 sd_res = yield sd_client.autoconnect_enabled()358 sd_res = yield self.sd_client.autoconnect_enabled()
351 autoconnect = bool(sd_res)359 autoconnect = bool(sd_res)
352360
353 sd_res = yield sd_client.show_all_notifications_enabled()361 sd_res = yield self.sd_client.show_all_notifications_enabled()
354 show_notifs = bool(sd_res)362 show_notifs = bool(sd_res)
355363
356 sd_res = yield sd_client.share_autosubscribe_enabled()364 sd_res = yield self.sd_client.share_autosubscribe_enabled()
357 share_autosubscribe = bool(sd_res)365 share_autosubscribe = bool(sd_res)
358366
359 sd_res = yield sd_client.udf_autosubscribe_enabled()367 sd_res = yield self.sd_client.udf_autosubscribe_enabled()
360 udf_autosubscribe = bool(sd_res)368 udf_autosubscribe = bool(sd_res)
361369
362 sd_res = yield sd_client.bandwidth_throttling_enabled()370 sd_res = yield self.sd_client.bandwidth_throttling_enabled()
363 limit_bw = bool(sd_res)371 limit_bw = bool(sd_res)
364372
365 limits = yield sd_client.get_throttling_limits()373 limits = yield self.sd_client.get_throttling_limits()
366374
367 logger.debug('devices_info: file sync enabled? %s limit_bw %s, limits '375 logger.debug('devices_info: file sync enabled? %s limit_bw %s, limits '
368 '%s, autoconnect %s, show_notifs %s, '376 '%s, autoconnect %s, show_notifs %s, '
@@ -432,19 +440,19 @@
432440
433 if is_local and SHOW_ALL_NOTIFICATIONS_KEY in settings:441 if is_local and SHOW_ALL_NOTIFICATIONS_KEY in settings:
434 if not settings[SHOW_ALL_NOTIFICATIONS_KEY]:442 if not settings[SHOW_ALL_NOTIFICATIONS_KEY]:
435 yield sd_client.disable_show_all_notifications()443 yield self.sd_client.disable_show_all_notifications()
436 else:444 else:
437 yield sd_client.enable_show_all_notifications()445 yield self.sd_client.enable_show_all_notifications()
438446
439 if is_local and LIMIT_BW_KEY in settings:447 if is_local and LIMIT_BW_KEY in settings:
440 if not settings[LIMIT_BW_KEY]:448 if not settings[LIMIT_BW_KEY]:
441 yield sd_client.disable_bandwidth_throttling()449 yield self.sd_client.disable_bandwidth_throttling()
442 else:450 else:
443 yield sd_client.enable_bandwidth_throttling()451 yield self.sd_client.enable_bandwidth_throttling()
444452
445 if is_local and (UPLOAD_KEY in settings or453 if is_local and (UPLOAD_KEY in settings or
446 DOWNLOAD_KEY in settings):454 DOWNLOAD_KEY in settings):
447 current_limits = yield sd_client.get_throttling_limits()455 current_limits = yield self.sd_client.get_throttling_limits()
448 limits = {456 limits = {
449 "download": current_limits["download"],457 "download": current_limits["download"],
450 "upload": current_limits["upload"],458 "upload": current_limits["upload"],
@@ -453,7 +461,7 @@
453 limits["upload"] = settings[UPLOAD_KEY]461 limits["upload"] = settings[UPLOAD_KEY]
454 if DOWNLOAD_KEY in settings:462 if DOWNLOAD_KEY in settings:
455 limits["download"] = settings[DOWNLOAD_KEY]463 limits["download"] = settings[DOWNLOAD_KEY]
456 sd_client.set_throttling_limits(limits)464 self.sd_client.set_throttling_limits(limits)
457465
458 # still pending: more work on the settings dict (LP: #673674)466 # still pending: more work on the settings dict (LP: #673674)
459 returnValue(device_id)467 returnValue(device_id)
@@ -472,7 +480,7 @@
472 if is_local:480 if is_local:
473 logger.warning('remove_device: device is local! removing and '481 logger.warning('remove_device: device is local! removing and '
474 'clearing credentials.')482 'clearing credentials.')
475 yield login_client.clear_credentials()483 yield self.login_client.clear_credentials()
476484
477 returnValue(device_id)485 returnValue(device_id)
478486
@@ -480,9 +488,9 @@
480 @inlineCallbacks488 @inlineCallbacks
481 def file_sync_status(self):489 def file_sync_status(self):
482 """Return the status of the file sync service."""490 """Return the status of the file sync service."""
483 enabled = yield sd_client.files_sync_enabled()491 enabled = yield self.sd_client.files_sync_enabled()
484 if enabled:492 if enabled:
485 status = yield sd_client.get_current_status()493 status = yield self.sd_client.get_current_status()
486 else:494 else:
487 status = {}495 status = {}
488 returnValue(self._process_file_sync_status(status))496 returnValue(self._process_file_sync_status(status))
@@ -491,46 +499,46 @@
491 @inlineCallbacks499 @inlineCallbacks
492 def enable_files(self):500 def enable_files(self):
493 """Enable the files service."""501 """Enable the files service."""
494 yield sd_client.set_files_sync_enabled(True)502 yield self.sd_client.set_files_sync_enabled(True)
495 self.file_sync_disabled = False503 self.file_sync_disabled = False
496504
497 @log_call(logger.debug)505 @log_call(logger.debug)
498 @inlineCallbacks506 @inlineCallbacks
499 def disable_files(self):507 def disable_files(self):
500 """Enable the files service."""508 """Enable the files service."""
501 yield sd_client.set_files_sync_enabled(False)509 yield self.sd_client.set_files_sync_enabled(False)
502 self.file_sync_disabled = True510 self.file_sync_disabled = True
503511
504 @log_call(logger.debug)512 @log_call(logger.debug)
505 @inlineCallbacks513 @inlineCallbacks
506 def connect_files(self):514 def connect_files(self):
507 """Connect the files service."""515 """Connect the files service."""
508 yield sd_client.connect_file_sync()516 yield self.sd_client.connect_file_sync()
509517
510 @log_call(logger.debug)518 @log_call(logger.debug)
511 @inlineCallbacks519 @inlineCallbacks
512 def disconnect_files(self):520 def disconnect_files(self):
513 """Disconnect the files service."""521 """Disconnect the files service."""
514 yield sd_client.disconnect_file_sync()522 yield self.sd_client.disconnect_file_sync()
515523
516 @log_call(logger.debug)524 @log_call(logger.debug)
517 @inlineCallbacks525 @inlineCallbacks
518 def restart_files(self):526 def restart_files(self):
519 """restart the files service."""527 """restart the files service."""
520 yield sd_client.stop_file_sync()528 yield self.sd_client.stop_file_sync()
521 yield sd_client.start_file_sync()529 yield self.sd_client.start_file_sync()
522530
523 @log_call(logger.debug)531 @log_call(logger.debug)
524 @inlineCallbacks532 @inlineCallbacks
525 def start_files(self):533 def start_files(self):
526 """start the files service."""534 """start the files service."""
527 yield sd_client.start_file_sync()535 yield self.sd_client.start_file_sync()
528536
529 @log_call(logger.debug)537 @log_call(logger.debug)
530 @inlineCallbacks538 @inlineCallbacks
531 def stop_files(self):539 def stop_files(self):
532 """stop the files service."""540 """stop the files service."""
533 yield sd_client.stop_file_sync()541 yield self.sd_client.stop_file_sync()
534542
535 @log_call(logger.debug)543 @log_call(logger.debug)
536 @inlineCallbacks544 @inlineCallbacks
@@ -547,11 +555,11 @@
547 else:555 else:
548 free_bytes = account['quota_total'] - account['quota_used']556 free_bytes = account['quota_total'] - account['quota_used']
549557
550 root_dir = yield sd_client.get_root_dir()558 root_dir = yield self.sd_client.get_root_dir()
551 shares_dir = yield sd_client.get_shares_dir()559 shares_dir = yield self.sd_client.get_shares_dir()
552 shares_dir_link = yield sd_client.get_shares_dir_link()560 shares_dir_link = yield self.sd_client.get_shares_dir_link()
553 folders = yield sd_client.get_folders()561 folders = yield self.sd_client.get_folders()
554 shares = yield sd_client.get_shares()562 shares = yield self.sd_client.get_shares()
555563
556 root_volume = {u'volume_id': u'', u'path': root_dir,564 root_volume = {u'volume_id': u'', u'path': root_dir,
557 u'subscribed': True, u'type': self.ROOT_TYPE,565 u'subscribed': True, u'type': self.ROOT_TYPE,
@@ -630,23 +638,23 @@
630 def subscribe_volume(self, volume_id):638 def subscribe_volume(self, volume_id):
631 """Subscribe to 'volume_id'."""639 """Subscribe to 'volume_id'."""
632 if self._volumes[volume_id][u'type'] == self.FOLDER_TYPE:640 if self._volumes[volume_id][u'type'] == self.FOLDER_TYPE:
633 yield sd_client.subscribe_folder(volume_id)641 yield self.sd_client.subscribe_folder(volume_id)
634 elif self._volumes[volume_id][u'type'] == self.SHARE_TYPE:642 elif self._volumes[volume_id][u'type'] == self.SHARE_TYPE:
635 yield sd_client.subscribe_share(volume_id)643 yield self.sd_client.subscribe_share(volume_id)
636644
637 @inlineCallbacks645 @inlineCallbacks
638 def unsubscribe_volume(self, volume_id):646 def unsubscribe_volume(self, volume_id):
639 """Unsubscribe from 'volume_id'."""647 """Unsubscribe from 'volume_id'."""
640 if self._volumes[volume_id][u'type'] == self.FOLDER_TYPE:648 if self._volumes[volume_id][u'type'] == self.FOLDER_TYPE:
641 yield sd_client.unsubscribe_folder(volume_id)649 yield self.sd_client.unsubscribe_folder(volume_id)
642 elif self._volumes[volume_id][u'type'] == self.SHARE_TYPE:650 elif self._volumes[volume_id][u'type'] == self.SHARE_TYPE:
643 yield sd_client.unsubscribe_share(volume_id)651 yield self.sd_client.unsubscribe_share(volume_id)
644652
645 @log_call(logger.debug)653 @log_call(logger.debug)
646 @inlineCallbacks654 @inlineCallbacks
647 def create_folder(self, folder_path):655 def create_folder(self, folder_path):
648 """Create a new User Defined Folder pointing to 'folder_path'."""656 """Create a new User Defined Folder pointing to 'folder_path'."""
649 yield sd_client.create_folder(path=folder_path)657 yield self.sd_client.create_folder(path=folder_path)
650658
651 @log_call(logger.debug)659 @log_call(logger.debug)
652 @inlineCallbacks660 @inlineCallbacks
@@ -728,11 +736,11 @@
728736
729 for name in (AUTOCONNECT_KEY, SHOW_ALL_NOTIFICATIONS_KEY,737 for name in (AUTOCONNECT_KEY, SHOW_ALL_NOTIFICATIONS_KEY,
730 SHARE_AUTOSUBSCRIBE_KEY, UDF_AUTOSUBSCRIBE_KEY):738 SHARE_AUTOSUBSCRIBE_KEY, UDF_AUTOSUBSCRIBE_KEY):
731 sd_method = getattr(sd_client, '%s_enabled' % name)739 sd_method = getattr(self.sd_client, '%s_enabled' % name)
732 value = yield sd_method()740 value = yield sd_method()
733 result[name] = bool(value)741 result[name] = bool(value)
734742
735 limits = yield sd_client.get_throttling_limits()743 limits = yield self.sd_client.get_throttling_limits()
736 result[DOWNLOAD_KEY] = limits['download']744 result[DOWNLOAD_KEY] = limits['download']
737 result[UPLOAD_KEY] = limits['upload']745 result[UPLOAD_KEY] = limits['upload']
738746
@@ -744,7 +752,7 @@
744 if setting_name in settings:752 if setting_name in settings:
745 new_value = settings[setting_name]753 new_value = settings[setting_name]
746 sd_method_name = 'enable_%s' if new_value else 'disable_%s'754 sd_method_name = 'enable_%s' if new_value else 'disable_%s'
747 sd_method = getattr(sd_client, sd_method_name % setting_name)755 sd_method = getattr(self.sd_client, sd_method_name % setting_name)
748 yield sd_method()756 yield sd_method()
749757
750 @log_call(logger.info)758 @log_call(logger.info)
@@ -756,7 +764,7 @@
756 yield self._change_boolean_file_sync_setting(name, settings)764 yield self._change_boolean_file_sync_setting(name, settings)
757765
758 if DOWNLOAD_KEY in settings or UPLOAD_KEY in settings:766 if DOWNLOAD_KEY in settings or UPLOAD_KEY in settings:
759 current_limits = yield sd_client.get_throttling_limits()767 current_limits = yield self.sd_client.get_throttling_limits()
760 limits = {768 limits = {
761 "download": current_limits["download"],769 "download": current_limits["download"],
762 "upload": current_limits["upload"],770 "upload": current_limits["upload"],
@@ -765,13 +773,13 @@
765 limits["upload"] = settings[UPLOAD_KEY]773 limits["upload"] = settings[UPLOAD_KEY]
766 if DOWNLOAD_KEY in settings:774 if DOWNLOAD_KEY in settings:
767 limits["download"] = settings[DOWNLOAD_KEY]775 limits["download"] = settings[DOWNLOAD_KEY]
768 yield sd_client.set_throttling_limits(limits)776 yield self.sd_client.set_throttling_limits(limits)
769777
770 throttling_disabled = sum(limits.itervalues()) == -2778 throttling_disabled = sum(limits.itervalues()) == -2
771 if throttling_disabled:779 if throttling_disabled:
772 yield sd_client.disable_bandwidth_throttling()780 yield self.sd_client.disable_bandwidth_throttling()
773 else:781 else:
774 yield sd_client.enable_bandwidth_throttling()782 yield self.sd_client.enable_bandwidth_throttling()
775783
776 @log_call(logger.info)784 @log_call(logger.info)
777 @inlineCallbacks785 @inlineCallbacks
778786
=== modified file 'ubuntuone/controlpanel/dbus_service.py'
--- ubuntuone/controlpanel/dbus_service.py 2011-07-22 21:26:48 +0000
+++ ubuntuone/controlpanel/dbus_service.py 2011-08-25 19:40:18 +0000
@@ -20,9 +20,17 @@
20"""Export the control backend thru DBus."""20"""Export the control backend thru DBus."""
2121
22from functools import wraps22from functools import wraps
23import sys
2324
24import dbus.service25import dbus.service
25import gobject26# pylint: disable=E0611
27# pylint: disable=W0404
28if 'gobject' in sys.modules:
29 import gobject as GObject
30else:
31 from gi.repository import GObject
32# pylint: enable=W0404
33# pylint: enable=E0611
2634
27from dbus.mainloop.glib import DBusGMainLoop35from dbus.mainloop.glib import DBusGMainLoop
28from dbus.service import method, signal36from dbus.service import method, signal
@@ -608,9 +616,9 @@
608616
609617
610def run_mainloop(loop=None):618def run_mainloop(loop=None):
611 """Run the gobject main loop."""619 """Run the GObject main loop."""
612 if loop is None:620 if loop is None:
613 loop = gobject.MainLoop()621 loop = GObject.MainLoop()
614 loop.run()622 loop.run()
615623
616624
@@ -643,7 +651,7 @@
643 """Hook the DBus listeners and start the main loop."""651 """Hook the DBus listeners and start the main loop."""
644 init_mainloop()652 init_mainloop()
645 if register_service():653 if register_service():
646 loop = gobject.MainLoop()654 loop = GObject.MainLoop()
647 publish_backend(shutdown_func=loop.quit)655 publish_backend(shutdown_func=loop.quit)
648 run_mainloop(loop=loop)656 run_mainloop(loop=loop)
649 else:657 else:
650658
=== modified file 'ubuntuone/controlpanel/dbustests/test_dbus_service.py'
--- ubuntuone/controlpanel/dbustests/test_dbus_service.py 2011-07-22 21:26:48 +0000
+++ ubuntuone/controlpanel/dbustests/test_dbus_service.py 2011-08-25 19:40:18 +0000
@@ -107,7 +107,7 @@
107 rs()107 rs()
108 self.mocker.result(True)108 self.mocker.result(True)
109109
110 mainloop = "ubuntuone.controlpanel.dbus_service.gobject.MainLoop"110 mainloop = "ubuntuone.controlpanel.dbus_service.GObject.MainLoop"
111 mainloop = self.mocker.replace(mainloop)111 mainloop = self.mocker.replace(mainloop)
112 mainloop()112 mainloop()
113 loop = self.mocker.mock()113 loop = self.mocker.mock()
114114
=== modified file 'ubuntuone/controlpanel/dbustests/test_sd_client/test_linux.py'
--- ubuntuone/controlpanel/dbustests/test_sd_client/test_linux.py 2011-07-22 21:26:48 +0000
+++ ubuntuone/controlpanel/dbustests/test_sd_client/test_linux.py 2011-08-25 19:40:18 +0000
@@ -82,7 +82,8 @@
8282
83 def test_set_status_changed_handler(self):83 def test_set_status_changed_handler(self):
84 """A proper callback can be connected to StatusChanged signal."""84 """A proper callback can be connected to StatusChanged signal."""
85 _, sig = sd_client.set_status_changed_handler(self._set_called)85 client = sd_client.SyncDaemonClient()
86 _, sig = client.set_status_changed_handler(self._set_called)
8687
87 self.assertEqual(sig._handler, self._set_called)88 self.assertEqual(sig._handler, self._set_called)
88 self.assertEqual(sig._member, 'StatusChanged')89 self.assertEqual(sig._member, 'StatusChanged')
8990
=== modified file 'ubuntuone/controlpanel/gui/gtk/gui.py'
--- ubuntuone/controlpanel/gui/gtk/gui.py 2011-07-22 21:26:48 +0000
+++ ubuntuone/controlpanel/gui/gtk/gui.py 2011-08-25 19:40:18 +0000
@@ -28,17 +28,14 @@
28import dbus28import dbus
29import gtk29import gtk
30import gobject30import gobject
31import ubuntu_sso
3231
33from dbus.mainloop.glib import DBusGMainLoop32from dbus.mainloop.glib import DBusGMainLoop
34from ubuntu_sso import networkstate33from ubuntu_sso import networkstate
35from ubuntu_sso.credentials import (TC_URL_KEY, HELP_TEXT_KEY, WINDOW_ID_KEY,
36 PING_URL_KEY)
37# No name 'clientdefs' in module 'ubuntuone'
38# pylint: disable=E0611,F040134# pylint: disable=E0611,F0401
39from gi.repository import GLib35from ubuntuone.platform.credentials import (
40from ubuntuone.clientdefs import (APP_NAME as U1_APP_NAME, TC_URL as U1_TC_URL,36 APP_NAME as U1_APP_NAME,
41 PING_URL as U1_PING_URL, DESCRIPTION as U1_DESCRIPTION)37 CredentialsManagementTool,
38)
42# pylint: enable=E0611,F040139# pylint: enable=E0611,F0401
4340
44# Wildcard import ubuntuone.controlpanel.gui41# Wildcard import ubuntuone.controlpanel.gui
@@ -123,21 +120,6 @@
123 gtk.main()120 gtk.main()
124121
125122
126def filter_by_app_name(f):
127 """Excecute 'f' filtering by app_name."""
128
129 @wraps(f)
130 def filter_by_app_name_inner(instance, app_name, *args, **kwargs):
131 """Execute 'f' only if 'app_name' matches 'U1_APP_NAME'."""
132 if app_name == U1_APP_NAME:
133 return f(instance, app_name, *args, **kwargs)
134 else:
135 logger.info('%s: ignoring call since received app_name '\
136 '"%s" (expected "%s")',
137 f.__name__, app_name, U1_APP_NAME)
138 return filter_by_app_name_inner
139
140
141def on_size_allocate(widget, allocation, label):123def on_size_allocate(widget, allocation, label):
142 """Resize labels according to who 'widget' is being resized."""124 """Resize labels according to who 'widget' is being resized."""
143 label.set_size_request(allocation.width - 2, -1)125 label.set_size_request(allocation.width - 2, -1)
@@ -188,10 +170,6 @@
188170
189 logger.debug('%s: started.', self.__class__.__name__)171 logger.debug('%s: started.', self.__class__.__name__)
190172
191 def humanize(self, int_bytes):
192 """Return a human readble string of 'int_bytes'."""
193 return GLib.format_size_for_display(int_bytes)
194
195 def _set_warning(self, message, label):173 def _set_warning(self, message, label):
196 """Set 'message' as warning in 'label'."""174 """Set 'message' as warning in 'label'."""
197 label.set_markup(WARNING_MARKUP % message)175 label.set_markup(WARNING_MARKUP % message)
@@ -272,23 +250,9 @@
272250
273 def __init__(self, main_window):251 def __init__(self, main_window):
274 GreyableBin.__init__(self)252 GreyableBin.__init__(self)
275253 creds_backend = CredentialsManagementTool()
276 sso_backend = None
277 bus = dbus.SessionBus()
278 try:
279 obj = bus.get_object(ubuntu_sso.DBUS_BUS_NAME,
280 ubuntu_sso.DBUS_CREDENTIALS_PATH,
281 follow_name_owner_changes=True)
282 iface = ubuntu_sso.DBUS_CREDENTIALS_IFACE
283 sso_backend = dbus.Interface(obj, dbus_interface=iface)
284 except dbus.exceptions.DBusException:
285 logger.exception('Can not connect to DBus at %r',
286 (ubuntu_sso.DBUS_BUS_NAME,
287 ubuntu_sso.DBUS_CREDENTIALS_PATH))
288 raise
289
290 ControlPanelMixin.__init__(self, filename='overview.ui',254 ControlPanelMixin.__init__(self, filename='overview.ui',
291 backend_instance=sso_backend)255 backend_instance=creds_backend)
292 self.add(self.itself)256 self.add(self.itself)
293 self.banner.set_from_file(get_data_file(OVERVIEW_BANNER))257 self.banner.set_from_file(get_data_file(OVERVIEW_BANNER))
294 self.files_icon.set_from_file(get_data_file(FILES_ICON))258 self.files_icon.set_from_file(get_data_file(FILES_ICON))
@@ -305,15 +269,6 @@
305 self._credentials_are_new = False269 self._credentials_are_new = False
306 self.show()270 self.show()
307271
308 self.backend.connect_to_signal('CredentialsFound',
309 self.on_credentials_found)
310 self.backend.connect_to_signal('CredentialsNotFound',
311 self.on_credentials_not_found)
312 self.backend.connect_to_signal('CredentialsError',
313 self.on_credentials_error)
314 self.backend.connect_to_signal('AuthorizationDenied',
315 self.on_authorization_denied)
316
317 kw = dict(result_cb=self.on_network_state_changed)272 kw = dict(result_cb=self.on_network_state_changed)
318 self.network_manager_state = networkstate.NetworkManagerState(**kw)273 self.network_manager_state = networkstate.NetworkManagerState(**kw)
319 self.network_manager_state.find_online_state()274 self.network_manager_state.find_online_state()
@@ -323,6 +278,14 @@
323 ControlPanelMixin._set_warning(self, message,278 ControlPanelMixin._set_warning(self, message,
324 label=self.warning_label)279 label=self.warning_label)
325280
281 def _window_xid(self):
282 """Return settings for credentials backend."""
283 if self.main_window.window is not None:
284 settings = {'window_id': str(self.main_window.window.xid)}
285 else:
286 settings = {}
287 return settings
288
326 def set_property(self, prop_name, new_value):289 def set_property(self, prop_name, new_value):
327 """Override 'set_property' to disable buttons if prop is 'greyed'."""290 """Override 'set_property' to disable buttons if prop is 'greyed'."""
328 if prop_name == 'greyed':291 if prop_name == 'greyed':
@@ -342,21 +305,17 @@
342305
343 def on_join_now_button_clicked(self, *a, **kw):306 def on_join_now_button_clicked(self, *a, **kw):
344 """User wants to join now."""307 """User wants to join now."""
345 settings = {TC_URL_KEY: U1_TC_URL, HELP_TEXT_KEY: U1_DESCRIPTION,308 d = self.backend.register(**self._window_xid())
346 WINDOW_ID_KEY: str(self.main_window.window.xid),309 d.addCallback(self.on_credentials_result)
347 PING_URL_KEY: U1_PING_URL}310 d.addErrback(self.on_credentials_error)
348 self.backend.register(U1_APP_NAME, settings,
349 reply_handler=NO_OP, error_handler=error_handler)
350 self.set_property('greyed', True)311 self.set_property('greyed', True)
351 self.warning_label.set_text('')312 self.warning_label.set_text('')
352313
353 def on_connect_button_clicked(self, *a, **kw):314 def on_connect_button_clicked(self, *a, **kw):
354 """User wants to connect now."""315 """User wants to connect now."""
355 settings = {TC_URL_KEY: U1_TC_URL, HELP_TEXT_KEY: U1_DESCRIPTION,316 d = self.backend.login(**self._window_xid())
356 WINDOW_ID_KEY: str(self.main_window.window.xid),317 d.addCallback(self.on_credentials_result)
357 PING_URL_KEY: U1_PING_URL}318 d.addErrback(self.on_credentials_error)
358 self.backend.login(U1_APP_NAME, settings,
359 reply_handler=NO_OP, error_handler=error_handler)
360 self.set_property('greyed', True)319 self.set_property('greyed', True)
361 self.warning_label.set_text('')320 self.warning_label.set_text('')
362321
@@ -364,31 +323,42 @@
364 """User wants to learn more."""323 """User wants to learn more."""
365 uri_hook(self.learn_more_button, LEARN_MORE_LINK)324 uri_hook(self.learn_more_button, LEARN_MORE_LINK)
366325
367 @filter_by_app_name326 def on_credentials_result(self, result):
327 """Process the credentials response.
328
329 If 'result' is a non empty dict, they were found.
330 If 'result' is an empty dict, they were not found.
331 If 'result' is None, the user cancelled the process.
332
333 """
334 if result is None:
335 self.on_authorization_denied()
336 elif result == {}:
337 self.on_credentials_not_found()
338 else:
339 self.on_credentials_found(result)
340
368 @log_call(logger.info, with_args=False)341 @log_call(logger.info, with_args=False)
369 def on_credentials_found(self, app_name, credentials):342 def on_credentials_found(self, credentials):
370 """SSO backend notifies of credentials found."""343 """Credentials backend notifies of credentials found."""
371 self.set_property('greyed', False)344 self.set_property('greyed', False)
372 self.emit('credentials-found', self._credentials_are_new, credentials)345 self.emit('credentials-found', self._credentials_are_new, credentials)
373346
374 @filter_by_app_name
375 @log_call(logger.info)347 @log_call(logger.info)
376 def on_credentials_not_found(self, app_name):348 def on_credentials_not_found(self):
377 """SSO backend notifies of credentials not found."""349 """Creds backend notifies of credentials not found."""
378 self._credentials_are_new = True350 self._credentials_are_new = True
379 self.set_property('greyed', False)351 self.set_property('greyed', False)
380352
381 @filter_by_app_name
382 @log_call(logger.error)353 @log_call(logger.error)
383 def on_credentials_error(self, app_name, error_dict):354 def on_credentials_error(self, error_dict):
384 """SSO backend notifies of an error when fetching credentials."""355 """Creds backend notifies of an error when fetching credentials."""
385 self.set_property('greyed', False)356 self.set_property('greyed', False)
386 self._set_warning(CREDENTIALS_ERROR)357 self._set_warning(CREDENTIALS_ERROR)
387358
388 @filter_by_app_name
389 @log_call(logger.info)359 @log_call(logger.info)
390 def on_authorization_denied(self, app_name):360 def on_authorization_denied(self):
391 """SSO backend notifies that user refused auth for 'app_name'."""361 """Creds backend notifies that user refused auth for 'app_name'."""
392 self.set_property('greyed', False)362 self.set_property('greyed', False)
393363
394 @log_call(logger.info)364 @log_call(logger.info)
@@ -402,8 +372,9 @@
402 else:372 else:
403 self.set_sensitive(True)373 self.set_sensitive(True)
404 self.warning_label.set_text(msg)374 self.warning_label.set_text(msg)
405 self.backend.find_credentials(U1_APP_NAME, {},375 d = self.backend.find_credentials()
406 reply_handler=NO_OP, error_handler=error_handler)376 d.addCallback(self.on_credentials_result)
377 d.addErrback(self.on_credentials_error)
407378
408379
409class DashboardPanel(UbuntuOneBin, ControlPanelMixin):380class DashboardPanel(UbuntuOneBin, ControlPanelMixin):
@@ -520,7 +491,7 @@
520 scroll_to_cell = True491 scroll_to_cell = True
521 else:492 else:
522 free_bytes_str = self.FREE_SPACE493 free_bytes_str = self.FREE_SPACE
523 free_bytes_args = {'free_space': self.humanize(free_bytes)}494 free_bytes_args = {'free_space': humanize(free_bytes)}
524 free_bytes = free_bytes_str % free_bytes_args495 free_bytes = free_bytes_str % free_bytes_args
525496
526 row = (self.ROW_HEADER % (name, free_bytes),497 row = (self.ROW_HEADER % (name, free_bytes),
@@ -1542,7 +1513,7 @@
1542 """Backend notifies of account info."""1513 """Backend notifies of account info."""
1543 used = int(info['quota_used'])1514 used = int(info['quota_used'])
1544 total = int(info['quota_total'])1515 total = int(info['quota_total'])
1545 data = {'used': self.humanize(used), 'total': self.humanize(total),1516 data = {'used': humanize(used), 'total': humanize(total),
1546 'percentage': (used / total) * 100}1517 'percentage': (used / total) * 100}
1547 self._update_quota(QUOTA_LABEL % data, data)1518 self._update_quota(QUOTA_LABEL % data, data)
15481519
15491520
=== modified file 'ubuntuone/controlpanel/gui/gtk/tests/__init__.py'
--- ubuntuone/controlpanel/gui/gtk/tests/__init__.py 2011-07-22 21:26:48 +0000
+++ ubuntuone/controlpanel/gui/gtk/tests/__init__.py 2011-08-25 19:40:18 +0000
@@ -22,6 +22,7 @@
2222
23from collections import defaultdict23from collections import defaultdict
2424
25from twisted.internet import defer
25from ubuntuone.devtools.handlers import MementoHandler26from ubuntuone.devtools.handlers import MementoHandler
2627
27from ubuntuone.controlpanel.gui.gtk import gui28from ubuntuone.controlpanel.gui.gtk import gui
@@ -83,14 +84,12 @@
83 self._signals[signal].append(handler)84 self._signals[signal].append(handler)
8485
8586
86class FakedSSOBackend(FakedDBusBackend):87class FakedCredentialsBackend(FakedObject):
87 """Fake a SSO Backend, act as a dbus.Interface."""88 """Fake a credentials backend."""
8889
89 bus_name = gui.ubuntu_sso.DBUS_BUS_NAME
90 object_path = gui.ubuntu_sso.DBUS_CREDENTIALS_PATH
91 iface = gui.ubuntu_sso.DBUS_CREDENTIALS_IFACE
92 exposed_methods = ['find_credentials', 'clear_credentials',90 exposed_methods = ['find_credentials', 'clear_credentials',
93 'login', 'register']91 'login', 'register']
92 next_result = defer.succeed(None)
9493
9594
96class FakedControlPanelBackend(FakedDBusBackend):95class FakedControlPanelBackend(FakedDBusBackend):
@@ -135,8 +134,6 @@
135 if dbus_interface == gui.DBUS_PREFERENCES_IFACE:134 if dbus_interface == gui.DBUS_PREFERENCES_IFACE:
136 return FakedControlPanelBackend(obj, dbus_interface,135 return FakedControlPanelBackend(obj, dbus_interface,
137 *args, **kwargs)136 *args, **kwargs)
138 if dbus_interface == gui.ubuntu_sso.DBUS_CREDENTIALS_IFACE:
139 return FakedSSOBackend(obj, dbus_interface, *args, **kwargs)
140 if dbus_interface == gui.DBUS_IFACE_GUI:137 if dbus_interface == gui.DBUS_IFACE_GUI:
141 return FakeControlPanelBackend(138 return FakeControlPanelBackend(
142 obj, dbus_interface, *args, **kwargs)139 obj, dbus_interface, *args, **kwargs)
@@ -188,9 +185,11 @@
188 # pylint: disable=E1102185 # pylint: disable=E1102
189 klass = None186 klass = None
190 kwargs = {}187 kwargs = {}
188 backend_is_dbus = True
191189
192 def setUp(self):190 def setUp(self):
193 super(BaseTestCase, self).setUp()191 super(BaseTestCase, self).setUp()
192 self.patch(gui, 'CredentialsManagementTool', FakedCredentialsBackend)
194 self.patch(gui.os.path, 'expanduser',193 self.patch(gui.os.path, 'expanduser',
195 lambda path: path.replace('~', USER_HOME))194 lambda path: path.replace('~', USER_HOME))
196 self.patch(gui.gtk, 'main', lambda: None)195 self.patch(gui.gtk, 'main', lambda: None)
@@ -221,14 +220,13 @@
221 pb = gui.gtk.gdk.pixbuf_new_from_file(gui.get_data_file(filename))220 pb = gui.gtk.gdk.pixbuf_new_from_file(gui.get_data_file(filename))
222 self.assertEqual(image.get_pixbuf().get_pixels(), pb.get_pixels())221 self.assertEqual(image.get_pixbuf().get_pixels(), pb.get_pixels())
223222
224 def assert_backend_called(self, method_name, args, backend=None):223 def assert_backend_called(self, method_name, *args, **kwargs):
225 """Check that the control panel backend 'method_name' was called."""224 """Check that the control panel backend 'method_name' was called."""
226 if backend is None:225 if self.backend_is_dbus:
227 backend = self.ui.backend226 kwargs = {'reply_handler': gui.NO_OP,
228 self.assertIn(method_name, backend._called)227 'error_handler': gui.error_handler}
229 kwargs = {'reply_handler': gui.NO_OP,228 self.assertIn(method_name, self.ui.backend._called)
230 'error_handler': gui.error_handler}229 self.assertEqual(self.ui.backend._called[method_name], (args, kwargs))
231 self.assertEqual(backend._called[method_name], (args, kwargs))
232230
233 def assert_warning_correct(self, warning, text):231 def assert_warning_correct(self, warning, text):
234 """Check that 'warning' is visible, showing 'text'."""232 """Check that 'warning' is visible, showing 'text'."""
235233
=== modified file 'ubuntuone/controlpanel/gui/gtk/tests/test_gui.py'
--- ubuntuone/controlpanel/gui/gtk/tests/test_gui.py 2011-07-22 21:26:48 +0000
+++ ubuntuone/controlpanel/gui/gtk/tests/test_gui.py 2011-08-25 19:40:18 +0000
@@ -42,6 +42,7 @@
42)42)
43from ubuntuone.controlpanel.gui.gtk.tests.test_package_manager import (43from ubuntuone.controlpanel.gui.gtk.tests.test_package_manager import (
44 SUCCESS, FAILURE)44 SUCCESS, FAILURE)
45from ubuntuone.controlpanel.gui import humanize
4546
4647
47# Attribute 'yyy' defined outside __init__, access to a protected member48# Attribute 'yyy' defined outside __init__, access to a protected member
@@ -159,7 +160,7 @@
159 self.ui.backend._called.pop('volumes_info', None)160 self.ui.backend._called.pop('volumes_info', None)
160 self.ui.load()161 self.ui.load()
161162
162 self.assert_backend_called('volumes_info', ())163 self.assert_backend_called('volumes_info')
163164
164 def test_is_processing_after_load(self):165 def test_is_processing_after_load(self):
165 """The ui is processing when contents are load."""166 """The ui is processing when contents are load."""
@@ -196,7 +197,7 @@
196 treeiter = self.ui.volumes_store.get_iter_root()197 treeiter = self.ui.volumes_store.get_iter_root()
197 for name, free_bytes, volumes in FAKE_VOLUMES_INFO:198 for name, free_bytes, volumes in FAKE_VOLUMES_INFO:
198 name = "%s's" % name if name else gui.MY_FOLDERS199 name = "%s's" % name if name else gui.MY_FOLDERS
199 free_bytes = self.ui.humanize(int(free_bytes))200 free_bytes = humanize(int(free_bytes))
200 header = (name, self.ui.FREE_SPACE % {'free_space': free_bytes})201 header = (name, self.ui.FREE_SPACE % {'free_space': free_bytes})
201202
202 # check parent row203 # check parent row
@@ -277,7 +278,7 @@
277 treeiter = self.ui.volumes_store.get_iter_root()278 treeiter = self.ui.volumes_store.get_iter_root()
278 for name, free_bytes, volumes in FAKE_VOLUMES_NO_FREE_SPACE_INFO:279 for name, free_bytes, volumes in FAKE_VOLUMES_NO_FREE_SPACE_INFO:
279 name = "%s's" % name if name else gui.MY_FOLDERS280 name = "%s's" % name if name else gui.MY_FOLDERS
280 free_bytes = self.ui.humanize(int(free_bytes))281 free_bytes = humanize(int(free_bytes))
281 free_bytes = self.ui.NO_FREE_SPACE % {'free_space': free_bytes}282 free_bytes = self.ui.NO_FREE_SPACE % {'free_space': free_bytes}
282283
283 # check parent row284 # check parent row
@@ -440,7 +441,7 @@
440 subscribed = gui.bool_str(not bool(volume['subscribed']))441 subscribed = gui.bool_str(not bool(volume['subscribed']))
441 # backend was called442 # backend was called
442 self.assert_backend_called('change_volume_settings',443 self.assert_backend_called('change_volume_settings',
443 (fid, {'subscribed': subscribed}))444 fid, {'subscribed': subscribed})
444 # store was updated445 # store was updated
445 it = self.ui.volumes_store.get_iter(path)446 it = self.ui.volumes_store.get_iter(path)
446 value = self.ui.volumes_store.get_value(it, 1)447 value = self.ui.volumes_store.get_value(it, 1)
@@ -572,7 +573,7 @@
572 """Changing throttling settings updates the backend properly."""573 """Changing throttling settings updates the backend properly."""
573 expected = self.ui.__dict__574 expected = self.ui.__dict__
574 self.assert_backend_called('change_device_settings',575 self.assert_backend_called('change_device_settings',
575 (self.ui.id, expected))576 self.ui.id, expected)
576 self.assertEqual(self.ui.warning_label.get_text(), '')577 self.assertEqual(self.ui.warning_label.get_text(), '')
577578
578 limit_enabled = self.ui.throttling_limits.get_sensitive()579 limit_enabled = self.ui.throttling_limits.get_sensitive()
@@ -806,7 +807,7 @@
806 self.ui.is_local = False807 self.ui.is_local = False
807 self.ui.remove.clicked()808 self.ui.remove.clicked()
808809
809 self.assert_backend_called('remove_device', (self.ui.id,))810 self.assert_backend_called('remove_device', self.ui.id)
810 self.assertFalse(self.ui.get_sensitive(),811 self.assertFalse(self.ui.get_sensitive(),
811 'Must be disabled while removing.')812 'Must be disabled while removing.')
812813
@@ -936,7 +937,7 @@
936 self.ui.backend._called.pop('devices_info', None)937 self.ui.backend._called.pop('devices_info', None)
937 self.ui.load()938 self.ui.load()
938939
939 self.assert_backend_called('devices_info', ())940 self.assert_backend_called('devices_info')
940941
941 def test_is_processing_after_load(self):942 def test_is_processing_after_load(self):
942 """The ui is processing when contents are load."""943 """The ui is processing when contents are load."""
@@ -1246,7 +1247,7 @@
12461247
1247 def test_file_sync_status_is_requested(self):1248 def test_file_sync_status_is_requested(self):
1248 """The file sync status is requested to the backend."""1249 """The file sync status is requested to the backend."""
1249 self.assert_backend_called('file_sync_status', ())1250 self.assert_backend_called('file_sync_status')
12501251
1251 def test_is_disabled(self):1252 def test_is_disabled(self):
1252 """Until file sync status is given, the widget is disabled."""1253 """Until file sync status is given, the widget is disabled."""
@@ -1273,10 +1274,10 @@
1273 assert self.ui.button.get_active()1274 assert self.ui.button.get_active()
12741275
1275 self.ui.button.set_active(not self.ui.button.get_active())1276 self.ui.button.set_active(not self.ui.button.get_active())
1276 self.assert_backend_called('disable_files', ())1277 self.assert_backend_called('disable_files')
12771278
1278 self.ui.button.set_active(not self.ui.button.get_active())1279 self.ui.button.set_active(not self.ui.button.get_active())
1279 self.assert_backend_called('enable_files', ())1280 self.assert_backend_called('enable_files')
12801281
1281 def test_on_file_sync_enabled(self):1282 def test_on_file_sync_enabled(self):
1282 """When file sync is enabled, the button is active."""1283 """When file sync is enabled, the button is active."""
@@ -1330,7 +1331,7 @@
13301331
1331 args = (self.service_id,1332 args = (self.service_id,
1332 {'enabled': gui.bool_str(self.ui.button.get_active())})1333 {'enabled': gui.bool_str(self.ui.button.get_active())})
1333 self.assert_backend_called('change_replication_settings', args)1334 self.assert_backend_called('change_replication_settings', *args)
13341335
1335 def test_dependency(self):1336 def test_dependency(self):
1336 """The dependency box is None."""1337 """The dependency box is None."""
@@ -1543,7 +1544,7 @@
1543 self.ui.load_replications()1544 self.ui.load_replications()
15441545
1545 self.assertTrue(self.ui.message.active)1546 self.assertTrue(self.ui.message.active)
1546 self.assert_backend_called('replications_info', ())1547 self.assert_backend_called('replications_info')
15471548
15481549
1549class ServicesWithDesktopcouchTestCase(ServicesTestCase):1550class ServicesWithDesktopcouchTestCase(ServicesTestCase):
@@ -1750,7 +1751,7 @@
1750 def test_file_sync_status_is_requested_on_load(self):1751 def test_file_sync_status_is_requested_on_load(self):
1751 """The file sync status is requested to the backend."""1752 """The file sync status is requested to the backend."""
1752 self.ui.load()1753 self.ui.load()
1753 self.assert_backend_called('file_sync_status', ())1754 self.assert_backend_called('file_sync_status')
17541755
1755 def test_on_file_sync_status_disabled(self):1756 def test_on_file_sync_status_disabled(self):
1756 """The file sync is disabled.1757 """The file sync is disabled.
@@ -1862,43 +1863,43 @@
1862 self.ui.backend._called.clear()1863 self.ui.backend._called.clear()
1863 self.ui.on_files_start_error({'error_msg': 'error msg'})1864 self.ui.on_files_start_error({'error_msg': 'error msg'})
18641865
1865 self.assert_backend_called('file_sync_status', ())1866 self.assert_backend_called('file_sync_status')
18661867
1867 def test_on_connect_clicked(self):1868 def test_on_connect_clicked(self):
1868 """User requested connection."""1869 """User requested connection."""
1869 self.ui.on_connect_clicked(self.ui.button)1870 self.ui.on_connect_clicked(self.ui.button)
18701871
1871 self.assert_backend_called('connect_files', ())1872 self.assert_backend_called('connect_files')
18721873
1873 def test_on_disconnect_clicked(self):1874 def test_on_disconnect_clicked(self):
1874 """User requested disconnection."""1875 """User requested disconnection."""
1875 self.ui.on_disconnect_clicked(self.ui.button)1876 self.ui.on_disconnect_clicked(self.ui.button)
18761877
1877 self.assert_backend_called('disconnect_files', ())1878 self.assert_backend_called('disconnect_files')
18781879
1879 def test_on_enable_clicked(self):1880 def test_on_enable_clicked(self):
1880 """User requested enable the service."""1881 """User requested enable the service."""
1881 self.ui.on_enable_clicked(self.ui.button)1882 self.ui.on_enable_clicked(self.ui.button)
18821883
1883 self.assert_backend_called('enable_files', ())1884 self.assert_backend_called('enable_files')
18841885
1885 def test_on_restart_clicked(self):1886 def test_on_restart_clicked(self):
1886 """User requested restart the service."""1887 """User requested restart the service."""
1887 self.ui.on_restart_clicked(self.ui.button)1888 self.ui.on_restart_clicked(self.ui.button)
18881889
1889 self.assert_backend_called('restart_files', ())1890 self.assert_backend_called('restart_files')
18901891
1891 def test_on_start_clicked(self):1892 def test_on_start_clicked(self):
1892 """User requested start the service."""1893 """User requested start the service."""
1893 self.ui.on_start_clicked(self.ui.button)1894 self.ui.on_start_clicked(self.ui.button)
18941895
1895 self.assert_backend_called('start_files', ())1896 self.assert_backend_called('start_files')
18961897
1897 def test_on_stop_clicked(self):1898 def test_on_stop_clicked(self):
1898 """User requested stop the service."""1899 """User requested stop the service."""
1899 self.ui.on_stop_clicked(self.ui.button)1900 self.ui.on_stop_clicked(self.ui.button)
19001901
1901 self.assert_backend_called('stop_files', ())1902 self.assert_backend_called('stop_files')
19021903
19031904
1904class ManagementPanelTestCase(ControlPanelMixinTestCase):1905class ManagementPanelTestCase(ControlPanelMixinTestCase):
@@ -1912,8 +1913,8 @@
1912 used = int(info['quota_used'])1913 used = int(info['quota_used'])
1913 total = int(info['quota_total'])1914 total = int(info['quota_total'])
1914 percentage = round((used / total) * 100, 2)1915 percentage = round((used / total) * 100, 2)
1915 expected = {'used': self.ui.humanize(used),1916 expected = {'used': humanize(used),
1916 'total': self.ui.humanize(total),1917 'total': humanize(total),
1917 'percentage': percentage}1918 'percentage': percentage}
1918 msg = gui.QUOTA_LABEL % expected1919 msg = gui.QUOTA_LABEL % expected
1919 self.assertEqual(self.ui.quota_label.get_text(), msg)1920 self.assertEqual(self.ui.quota_label.get_text(), msg)
@@ -1992,7 +1993,7 @@
1992 def test_account_info_is_requested_on_load(self):1993 def test_account_info_is_requested_on_load(self):
1993 """The account info is requested to the backend."""1994 """The account info is requested to the backend."""
1994 self.ui.load()1995 self.ui.load()
1995 self.assert_backend_called('account_info', ())1996 self.assert_backend_called('account_info')
19961997
1997 def test_file_sync_status_info_is_requested_on_load(self):1998 def test_file_sync_status_info_is_requested_on_load(self):
1998 """The file sync status info is requested to the backend."""1999 """The file sync status info is requested to the backend."""
19992000
=== modified file 'ubuntuone/controlpanel/gui/gtk/tests/test_gui_basic.py'
--- ubuntuone/controlpanel/gui/gtk/tests/test_gui_basic.py 2011-07-22 21:26:48 +0000
+++ ubuntuone/controlpanel/gui/gtk/tests/test_gui_basic.py 2011-08-25 19:40:18 +0000
@@ -20,9 +20,14 @@
2020
21from __future__ import division21from __future__ import division
2222
23from twisted.internet import defer
24from twisted.python.failure import Failure
2325
24from ubuntuone.controlpanel.gui.gtk import gui26from ubuntuone.controlpanel.gui.gtk import gui
25from ubuntuone.controlpanel.gui.gtk.tests import BaseTestCase, FakedSSOBackend27from ubuntuone.controlpanel.gui.gtk.tests import (
28 BaseTestCase,
29 FakedCredentialsBackend,
30)
26from ubuntuone.controlpanel.tests import TOKEN31from ubuntuone.controlpanel.tests import TOKEN
2732
28from ubuntuone.devtools.testcase import skipIf33from ubuntuone.devtools.testcase import skipIf
@@ -287,7 +292,7 @@
287 """On 'credentials-found' signal, ask syncdaemon to connect."""292 """On 'credentials-found' signal, ask syncdaemon to connect."""
288 # credentials are new293 # credentials are new
289 self.ui.overview.emit('credentials-found', True, object())294 self.ui.overview.emit('credentials-found', True, object())
290 self.assert_backend_called('connect_files', ())295 self.assert_backend_called('connect_files')
291296
292 def test_local_device_removed_shows_overview_panel(self):297 def test_local_device_removed_shows_overview_panel(self):
293 """On 'local-device-removed' signal, the overview panel is shown."""298 """On 'local-device-removed' signal, the overview panel is shown."""
@@ -306,7 +311,7 @@
306 def test_backend_is_shutdown_on_close(self):311 def test_backend_is_shutdown_on_close(self):
307 """When the control panel is closed, the backend is shutdown."""312 """When the control panel is closed, the backend is shutdown."""
308 self.ui.emit('destroy')313 self.ui.emit('destroy')
309 self.assert_backend_called('shutdown', ())314 self.assert_backend_called('shutdown')
310315
311316
312class UbuntuOneBinTestCase(BaseTestCase):317class UbuntuOneBinTestCase(BaseTestCase):
@@ -437,6 +442,11 @@
437 klass = gui.OverviewPanel442 klass = gui.OverviewPanel
438 kwargs = {'main_window': gui.gtk.Window()}443 kwargs = {'main_window': gui.gtk.Window()}
439 ui_filename = 'overview.ui'444 ui_filename = 'overview.ui'
445 backend_is_dbus = False
446
447 def setUp(self):
448 super(OverwiewPanelTestCase, self).setUp()
449 gui.gtk.link_button_set_uri_hook(lambda *a: None)
440450
441 def test_is_a_greyable_bin(self):451 def test_is_a_greyable_bin(self):
442 """Inherits from GreyableBin."""452 """Inherits from GreyableBin."""
@@ -450,20 +460,102 @@
450 """The 'join_now' button is the default widget."""460 """The 'join_now' button is the default widget."""
451 self.assertTrue(self.ui.join_now_button.get_property('can-default'))461 self.assertTrue(self.ui.join_now_button.get_property('can-default'))
452462
453 def test_sso_backend(self):463 def test_backend(self):
454 """Has a correct SSO backend."""464 """Has a correct backend."""
455 self.assertIsInstance(self.ui.backend, FakedSSOBackend)465 self.assertIsInstance(self.ui.backend, FakedCredentialsBackend)
456466
457 def test_sso_backend_signals(self):467
458 """The proper signals are connected to the backend."""468class OverwiewPanelBackendCallbacksTestCase(OverwiewPanelTestCase):
459 self.assertEqual(self.ui.backend._signals['CredentialsFound'],469 """Proper callbacks are chained to the credentials backend methods."""
460 [self.ui.on_credentials_found])470
461 self.assertEqual(self.ui.backend._signals['CredentialsNotFound'],471 failure = Exception()
462 [self.ui.on_credentials_not_found])472
463 self.assertEqual(self.ui.backend._signals['CredentialsError'],473 def test_find_credentials_fired_with_credentials(self):
464 [self.ui.on_credentials_error])474 """Test that on_credentials_found is called."""
465 self.assertEqual(self.ui.backend._signals['AuthorizationDenied'],475 self.ui.backend.next_result = defer.succeed(TOKEN)
466 [self.ui.on_authorization_denied])476 self.patch(self.ui, 'on_credentials_found', self._set_called)
477
478 self.ui.on_network_state_changed(gui.networkstate.ONLINE)
479
480 self.assertEqual(self._called, ((TOKEN,), {}))
481
482 def test_find_credentials_fired_without_credentials(self):
483 """Test that on_credentials_not_found is called."""
484 self.ui.backend.next_result = defer.succeed({})
485 self.patch(self.ui, 'on_credentials_not_found', self._set_called)
486
487 self.ui.on_network_state_changed(gui.networkstate.ONLINE)
488
489 self.assertEqual(self._called, ((), {}))
490
491 def test_find_credentials_errback(self):
492 """Test that on_credentials_error is called."""
493 self.ui.backend.next_result = defer.fail(self.failure)
494 self.patch(self.ui, 'on_credentials_error', self._set_called)
495
496 self.ui.on_network_state_changed(gui.networkstate.ONLINE)
497
498 failure = self._called[0][0]
499 self.assertIsInstance(failure, Failure)
500 self.assertEqual(failure.value, self.failure)
501
502 def test_register_fired_with_credentials(self):
503 """Test that on_credentials_found is called."""
504 self.ui.backend.next_result = defer.succeed(TOKEN)
505 self.patch(self.ui, 'on_credentials_found', self._set_called)
506
507 self.ui.join_now_button.clicked()
508
509 self.assertEqual(self._called, ((TOKEN,), {}))
510
511 def test_register_fired_with_credentials_none(self):
512 """Test that on_authorization_denied is called."""
513 self.ui.backend.next_result = defer.succeed(None)
514 self.patch(self.ui, 'on_authorization_denied', self._set_called)
515
516 self.ui.join_now_button.clicked()
517
518 self.assertEqual(self._called, ((), {}))
519
520 def test_register_errback(self):
521 """Test that on_credentials_error is called."""
522 self.ui.backend.next_result = defer.fail(self.failure)
523 self.patch(self.ui, 'on_credentials_error', self._set_called)
524
525 self.ui.join_now_button.clicked()
526
527 failure = self._called[0][0]
528 self.assertIsInstance(failure, Failure)
529 self.assertEqual(failure.value, self.failure)
530
531 def test_login_fired_with_credentials(self):
532 """Test that on_credentials_found is called."""
533 self.ui.backend.next_result = defer.succeed(TOKEN)
534 self.patch(self.ui, 'on_credentials_found', self._set_called)
535
536 self.ui.connect_button.clicked()
537
538 self.assertEqual(self._called, ((TOKEN,), {}))
539
540 def test_login_fired_with_credentials_none(self):
541 """Test that on_authorization_denied is called."""
542 self.ui.backend.next_result = defer.succeed(None)
543 self.patch(self.ui, 'on_authorization_denied', self._set_called)
544
545 self.ui.connect_button.clicked()
546
547 self.assertEqual(self._called, ((), {}))
548
549 def test_login_errback(self):
550 """Test that on_credentials_error is called."""
551 self.ui.backend.next_result = defer.fail(self.failure)
552 self.patch(self.ui, 'on_credentials_error', self._set_called)
553
554 self.ui.connect_button.clicked()
555
556 failure = self._called[0][0]
557 self.assertIsInstance(failure, Failure)
558 self.assertEqual(failure.value, self.failure)
467559
468560
469class OverwiewNetworkStatePanelTestCase(OverwiewPanelTestCase):561class OverwiewNetworkStatePanelTestCase(OverwiewPanelTestCase):
@@ -514,13 +606,13 @@
514 def test_find_credentials_is_called(self):606 def test_find_credentials_is_called(self):
515 """Credentials are asked to SSO backend."""607 """Credentials are asked to SSO backend."""
516 self.assertFalse(self.ui._credentials_are_new)608 self.assertFalse(self.ui._credentials_are_new)
517 self.assert_backend_called('find_credentials', (gui.U1_APP_NAME, {}))609 self.assert_backend_called('find_credentials')
518610
519 def test_on_credentials_found(self):611 def test_on_credentials_found(self):
520 """Callback 'on_credentials_found' is correct."""612 """Callback 'on_credentials_found' is correct."""
521 self.ui.connect('credentials-found', self._set_called)613 self.ui.connect('credentials-found', self._set_called)
522614
523 self.ui.on_credentials_found(gui.U1_APP_NAME, TOKEN)615 self.ui.on_credentials_found(TOKEN)
524616
525 self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')617 self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
526 # assume credentials were in local keyring618 # assume credentials were in local keyring
@@ -531,9 +623,9 @@
531 self.ui.connect('credentials-found', self._set_called)623 self.ui.connect('credentials-found', self._set_called)
532624
533 # credentials weren't in the system625 # credentials weren't in the system
534 self.ui.on_credentials_not_found(gui.U1_APP_NAME)626 self.ui.on_credentials_not_found()
535 # now they are!627 # now they are!
536 self.ui.on_credentials_found(gui.U1_APP_NAME, TOKEN)628 self.ui.on_credentials_found(TOKEN)
537629
538 self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')630 self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
539 # assume credentials were not in local keyring631 # assume credentials were not in local keyring
@@ -541,70 +633,31 @@
541633
542 def test_on_credentials_not_found(self):634 def test_on_credentials_not_found(self):
543 """Callback 'on_credentials_not_found' is correct."""635 """Callback 'on_credentials_not_found' is correct."""
544 self.ui.on_credentials_not_found(gui.U1_APP_NAME)636 self.ui.on_credentials_not_found()
545 self.assertTrue(self.ui.get_visible())637 self.assertTrue(self.ui.get_visible())
546 self.assertTrue(self.ui._credentials_are_new)638 self.assertTrue(self.ui._credentials_are_new)
547639
548 def test_on_credentials_error(self):640 def test_on_credentials_error(self):
549 """Callback 'on_credentials_error' is correct."""641 """Callback 'on_credentials_error' is correct."""
550 self.ui.on_credentials_error(gui.U1_APP_NAME, {})642 self.ui.on_credentials_error({})
551 self.assertTrue(self.ui.get_visible())643 self.assertTrue(self.ui.get_visible())
552 self.assert_warning_correct(self.ui.warning_label,644 self.assert_warning_correct(self.ui.warning_label,
553 gui.CREDENTIALS_ERROR)645 gui.CREDENTIALS_ERROR)
554646
555 def test_on_authorization_denied(self):647 def test_on_authorization_denied(self):
556 """Callback 'on_authorization_denied' is correct."""648 """Callback 'on_authorization_denied' is correct."""
557 self.ui.on_authorization_denied(gui.U1_APP_NAME)649 self.ui.on_authorization_denied()
558 self.assertTrue(self.ui.get_visible())650 self.assertTrue(self.ui.get_visible())
559 self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')651 self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
560 self.assertEqual(self.ui.warning_label.get_text(), '')652 self.assertEqual(self.ui.warning_label.get_text(), '')
561653
562654
563class OverwiewPanelAppNameMismatchTestCase(OverwiewPanelTestCase):
564 """The test suite for the overview panel when the app_name won't match."""
565
566 NOT_U1_APP = 'Not ' + gui.U1_APP_NAME
567
568 def test_filter_by_app_name(self):
569 """The filter_by_app_name decorator is correct."""
570 f = gui.filter_by_app_name(self._set_called)
571 f(self.ui, self.NOT_U1_APP)
572 self.assertFalse(self._called)
573 self.assertTrue(self.memento.check_info('ignoring', self.NOT_U1_APP))
574
575 args = ('test', object())
576 kwargs = {'really': 'AWESOME'}
577 f(self.ui, gui.U1_APP_NAME, *args, **kwargs)
578 self.assertEqual(self._called,
579 ((self.ui, gui.U1_APP_NAME,) + args, kwargs))
580
581 def test_on_credentials_found(self):
582 """Callback 'on_credentials_found' is not executed."""
583 self.assert_function_decorated(gui.filter_by_app_name,
584 self.ui.on_credentials_found)
585
586 def test_on_credentials_not_found(self):
587 """Callback 'on_credentials_not_found' is not executed."""
588 self.assert_function_decorated(gui.filter_by_app_name,
589 self.ui.on_credentials_not_found)
590
591 def test_on_credentials_error(self):
592 """Callback 'on_credentials_error' is not executed."""
593 self.assert_function_decorated(gui.filter_by_app_name,
594 self.ui.on_credentials_error)
595
596 def test_on_authorization_denied(self):
597 """Callback 'on_authorization_denied' is not executed."""
598 self.assert_function_decorated(gui.filter_by_app_name,
599 self.ui.on_authorization_denied)
600
601
602class OverwiewPanelNoCredsTestCase(OverwiewPanelTestCase):655class OverwiewPanelNoCredsTestCase(OverwiewPanelTestCase):
603 """The test suite for the overview panel when no credentials are found."""656 """The test suite for the overview panel when no credentials are found."""
604657
605 def setUp(self):658 def setUp(self):
606 super(OverwiewPanelNoCredsTestCase, self).setUp()659 super(OverwiewPanelNoCredsTestCase, self).setUp()
607 self.ui.on_credentials_not_found(gui.U1_APP_NAME)660 self.ui.on_credentials_not_found()
608661
609 def test_startup_visibility(self):662 def test_startup_visibility(self):
610 """The widget is visible at startup."""663 """The widget is visible at startup."""
@@ -631,12 +684,8 @@
631 self.ui.join_now_button.clicked()684 self.ui.join_now_button.clicked()
632685
633 window_id = self.kwargs['main_window'].window.xid686 window_id = self.kwargs['main_window'].window.xid
634 args = (gui.U1_APP_NAME,687 kwargs = {'window_id': str(window_id)}
635 {gui.TC_URL_KEY: gui.U1_TC_URL,688 self.assert_backend_called('register', **kwargs)
636 gui.HELP_TEXT_KEY: gui.U1_DESCRIPTION,
637 gui.WINDOW_ID_KEY: str(window_id),
638 gui.PING_URL_KEY: gui.U1_PING_URL})
639 self.assert_backend_called('register', args)
640689
641 def test_connect_button_clicked(self):690 def test_connect_button_clicked(self):
642 """Test the 'join now' button callback."""691 """Test the 'join now' button callback."""
@@ -646,12 +695,8 @@
646 self.ui.connect_button.clicked()695 self.ui.connect_button.clicked()
647696
648 window_id = self.kwargs['main_window'].window.xid697 window_id = self.kwargs['main_window'].window.xid
649 args = (gui.U1_APP_NAME,698 kwargs = {'window_id': str(window_id)}
650 {gui.TC_URL_KEY: gui.U1_TC_URL,699 self.assert_backend_called('login', **kwargs)
651 gui.HELP_TEXT_KEY: gui.U1_DESCRIPTION,
652 gui.WINDOW_ID_KEY: str(window_id),
653 gui.PING_URL_KEY: gui.U1_PING_URL})
654 self.assert_backend_called('login', args)
655700
656 def test_join_now_button_clicked_set_greyed(self):701 def test_join_now_button_clicked_set_greyed(self):
657 """Clicking on 'join_now' self is greyed."""702 """Clicking on 'join_now' self is greyed."""
@@ -660,7 +705,7 @@
660705
661 def test_join_now_button_clicked_removes_warning(self):706 def test_join_now_button_clicked_removes_warning(self):
662 """Clicking on 'join_now' the warnings are removed."""707 """Clicking on 'join_now' the warnings are removed."""
663 self.ui.on_authorization_denied(gui.U1_APP_NAME) # show warning708 self.ui.on_authorization_denied() # show warning
664 self.ui.join_now_button.clicked()709 self.ui.join_now_button.clicked()
665710
666 self.assertEqual(self.ui.warning_label.get_text(), '')711 self.assertEqual(self.ui.warning_label.get_text(), '')
@@ -672,7 +717,7 @@
672717
673 def test_connect_button_clicked_removes_warning(self):718 def test_connect_button_clicked_removes_warning(self):
674 """Clicking on 'connect' the warnings are removed."""719 """Clicking on 'connect' the warnings are removed."""
675 self.ui.on_authorization_denied(gui.U1_APP_NAME) # show warning720 self.ui.on_authorization_denied() # show warning
676 self.ui.connect_button.clicked()721 self.ui.connect_button.clicked()
677722
678 self.assertEqual(self.ui.warning_label.get_text(), '')723 self.assertEqual(self.ui.warning_label.get_text(), '')
@@ -688,21 +733,21 @@
688 def test_on_credentials_not_found_unset_greyed(self):733 def test_on_credentials_not_found_unset_greyed(self):
689 """Callback 'on_credentials_not_found' unsets the 'greyed' prop."""734 """Callback 'on_credentials_not_found' unsets the 'greyed' prop."""
690 self.ui.connect_button.clicked()735 self.ui.connect_button.clicked()
691 self.ui.on_credentials_not_found(gui.U1_APP_NAME)736 self.ui.on_credentials_not_found()
692737
693 self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')738 self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
694739
695 def test_on_credentials_error_unset_greyed(self):740 def test_on_credentials_error_unset_greyed(self):
696 """Callback 'on_credentials_error' unsets the 'greyed' prop."""741 """Callback 'on_credentials_error' unsets the 'greyed' prop."""
697 self.ui.connect_button.clicked()742 self.ui.connect_button.clicked()
698 self.ui.on_credentials_error(gui.U1_APP_NAME, {})743 self.ui.on_credentials_error({})
699744
700 self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')745 self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
701746
702 def test_on_authorization_denied_unset_greyed(self):747 def test_on_authorization_denied_unset_greyed(self):
703 """Callback 'on_authorization_denied' unsets the 'greyed' prop."""748 """Callback 'on_authorization_denied' unsets the 'greyed' prop."""
704 self.ui.connect_button.clicked()749 self.ui.connect_button.clicked()
705 self.ui.on_authorization_denied(gui.U1_APP_NAME)750 self.ui.on_authorization_denied()
706751
707 self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')752 self.assertFalse(self.ui.get_property('greyed'), 'Must not be greyed.')
708753
709754
=== modified file 'ubuntuone/controlpanel/gui/qt/filesyncstatus.py'
--- ubuntuone/controlpanel/gui/qt/filesyncstatus.py 2011-07-22 21:26:48 +0000
+++ ubuntuone/controlpanel/gui/qt/filesyncstatus.py 2011-08-25 19:40:18 +0000
@@ -165,6 +165,14 @@
165 self.ui.sync_status_button.setText(action)165 self.ui.sync_status_button.setText(action)
166 self.ui.sync_status_button.setEnabled(True)166 self.ui.sync_status_button.setEnabled(True)
167 self.ui.sync_status_button.show()167 self.ui.sync_status_button.show()
168 if status_key == backend.FILE_SYNC_DISCONNECTED:
169 self.ui.sync_status_button.setProperty("secondary", False)
170 else:
171 self.ui.sync_status_button.setProperty("secondary", True)
172 self.ui.sync_status_button.style().unpolish(
173 self.ui.sync_status_button)
174 self.ui.sync_status_button.style().polish(
175 self.ui.sync_status_button)
168 else:176 else:
169 self.ui.sync_status_button.hide()177 self.ui.sync_status_button.hide()
170178
171179
=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_filesyncstatus.py'
--- ubuntuone/controlpanel/gui/qt/tests/test_filesyncstatus.py 2011-07-22 21:26:48 +0000
+++ ubuntuone/controlpanel/gui/qt/tests/test_filesyncstatus.py 2011-08-25 19:40:18 +0000
@@ -65,10 +65,22 @@
6565
66 self.assertTrue(self.ui.ui.sync_status_button.isEnabled())66 self.assertTrue(self.ui.ui.sync_status_button.isEnabled())
67 self.assertEqual(self.ui.ui.sync_status_button.text(), action)67 self.assertEqual(self.ui.ui.sync_status_button.text(), action)
68 if action == gui.FILE_SYNC_DISCONNECTED:
69 self.assertFalse(
70 self.ui.ui.sync_status_button.property("secondary"))
71 else:
72 self.assertTrue(
73 self.ui.ui.sync_status_button.property("secondary"))
6874
69 self.ui.ui.sync_status_button.click()75 self.ui.ui.sync_status_button.click()
70 self.assertFalse(self.ui.ui.sync_status_button.isEnabled())76 self.assertFalse(self.ui.ui.sync_status_button.isEnabled())
71 self.assert_backend_called(callback)77 self.assert_backend_called(callback)
78 if action == gui.FILE_SYNC_DISCONNECTED:
79 self.assertFalse(
80 self.ui.ui.sync_status_button.property("secondary"))
81 else:
82 self.assertTrue(
83 self.ui.ui.sync_status_button.property("secondary"))
7284
73 if tooltip is not None:85 if tooltip is not None:
74 self.assertEqual(self.ui.ui.sync_status_button.toolTip(), tooltip)86 self.assertEqual(self.ui.ui.sync_status_button.toolTip(), tooltip)
7587
=== modified file 'ubuntuone/controlpanel/gui/qt/ui/account_ui.py'
--- ubuntuone/controlpanel/gui/qt/ui/account_ui.py 2011-08-12 19:12:08 +0000
+++ ubuntuone/controlpanel/gui/qt/ui/account_ui.py 2011-08-25 19:40:18 +0000
@@ -2,7 +2,7 @@
22
3# Form implementation generated from reading ui file 'data/qt/account.ui'3# Form implementation generated from reading ui file 'data/qt/account.ui'
4#4#
5# Created: Fri Aug 12 15:07:18 20115# Created: Thu Aug 25 15:26:50 2011
6# by: PyQt4 UI code generator 4.8.36# by: PyQt4 UI code generator 4.8.3
7#7#
8# WARNING! All changes made in this file will be lost!8# WARNING! All changes made in this file will be lost!
@@ -27,7 +27,7 @@
27 self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))27 self.gridLayout_2.setObjectName(_fromUtf8("gridLayout_2"))
28 self.edit_profile_button = GoToWebButton(Form)28 self.edit_profile_button = GoToWebButton(Form)
29 self.edit_profile_button.setObjectName(_fromUtf8("edit_profile_button"))29 self.edit_profile_button.setObjectName(_fromUtf8("edit_profile_button"))
30 self.gridLayout_2.addWidget(self.edit_profile_button, 2, 2, 1, 1)30 self.gridLayout_2.addWidget(self.edit_profile_button, 0, 2, 1, 1)
31 self.services = QtGui.QGroupBox(Form)31 self.services = QtGui.QGroupBox(Form)
32 self.services.setObjectName(_fromUtf8("services"))32 self.services.setObjectName(_fromUtf8("services"))
33 self.verticalLayout_3 = QtGui.QVBoxLayout(self.services)33 self.verticalLayout_3 = QtGui.QVBoxLayout(self.services)
@@ -62,7 +62,7 @@
62 self.gridLayout_2.addWidget(self.profile_info, 0, 0, 1, 1)62 self.gridLayout_2.addWidget(self.profile_info, 0, 0, 1, 1)
63 self.edit_services_button = GoToWebButton(Form)63 self.edit_services_button = GoToWebButton(Form)
64 self.edit_services_button.setObjectName(_fromUtf8("edit_services_button"))64 self.edit_services_button.setObjectName(_fromUtf8("edit_services_button"))
65 self.gridLayout_2.addWidget(self.edit_services_button, 0, 2, 1, 1)65 self.gridLayout_2.addWidget(self.edit_services_button, 2, 2, 1, 1)
66 spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)66 spacerItem = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
67 self.gridLayout_2.addItem(spacerItem, 0, 3, 1, 1)67 self.gridLayout_2.addItem(spacerItem, 0, 3, 1, 1)
68 spacerItem1 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)68 spacerItem1 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
6969
=== modified file 'ubuntuone/controlpanel/gui/qt/ui/controlpanel_ui.py'
--- ubuntuone/controlpanel/gui/qt/ui/controlpanel_ui.py 2011-08-12 19:12:08 +0000
+++ ubuntuone/controlpanel/gui/qt/ui/controlpanel_ui.py 2011-08-25 19:40:18 +0000
@@ -2,7 +2,7 @@
22
3# Form implementation generated from reading ui file 'data/qt/controlpanel.ui'3# Form implementation generated from reading ui file 'data/qt/controlpanel.ui'
4#4#
5# Created: Fri Aug 12 15:07:18 20115# Created: Thu Aug 25 15:26:50 2011
6# by: PyQt4 UI code generator 4.8.36# by: PyQt4 UI code generator 4.8.3
7#7#
8# WARNING! All changes made in this file will be lost!8# WARNING! All changes made in this file will be lost!
@@ -18,7 +18,7 @@
18class Ui_Form(object):18class Ui_Form(object):
19 def setupUi(self, Form):19 def setupUi(self, Form):
20 Form.setObjectName(_fromUtf8("Form"))20 Form.setObjectName(_fromUtf8("Form"))
21 Form.resize(387, 203)21 Form.resize(367, 142)
22 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)22 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
23 sizePolicy.setHorizontalStretch(0)23 sizePolicy.setHorizontalStretch(0)
24 sizePolicy.setVerticalStretch(0)24 sizePolicy.setVerticalStretch(0)
@@ -133,10 +133,16 @@
133 self.tab_widget.addTab(self.account_tab, _fromUtf8(""))133 self.tab_widget.addTab(self.account_tab, _fromUtf8(""))
134 self.verticalLayout.addWidget(self.tab_widget)134 self.verticalLayout.addWidget(self.tab_widget)
135 self.frame_footer = QtGui.QFrame(Form)135 self.frame_footer = QtGui.QFrame(Form)
136 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)
137 sizePolicy.setHorizontalStretch(0)
138 sizePolicy.setVerticalStretch(0)
139 sizePolicy.setHeightForWidth(self.frame_footer.sizePolicy().hasHeightForWidth())
140 self.frame_footer.setSizePolicy(sizePolicy)
141 self.frame_footer.setMaximumSize(QtCore.QSize(16777215, 30))
136 self.frame_footer.setObjectName(_fromUtf8("frame_footer"))142 self.frame_footer.setObjectName(_fromUtf8("frame_footer"))
137 self.horizontalLayout = QtGui.QHBoxLayout(self.frame_footer)143 self.horizontalLayout = QtGui.QHBoxLayout(self.frame_footer)
138 self.horizontalLayout.setSpacing(5)144 self.horizontalLayout.setSpacing(5)
139 self.horizontalLayout.setContentsMargins(3, 0, 3, 3)145 self.horizontalLayout.setMargin(0)
140 self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))146 self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
141 self.help_button = GoToWebButton(self.frame_footer)147 self.help_button = GoToWebButton(self.frame_footer)
142 self.help_button.setObjectName(_fromUtf8("help_button"))148 self.help_button.setObjectName(_fromUtf8("help_button"))
@@ -149,24 +155,30 @@
149 self.follow_us_label.setFont(font)155 self.follow_us_label.setFont(font)
150 self.follow_us_label.setObjectName(_fromUtf8("follow_us_label"))156 self.follow_us_label.setObjectName(_fromUtf8("follow_us_label"))
151 self.horizontalLayout.addWidget(self.follow_us_label)157 self.horizontalLayout.addWidget(self.follow_us_label)
152 self.twitter_button = QtGui.QToolButton(self.frame_footer)158 self.twitter_button = QtGui.QPushButton(self.frame_footer)
159 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
160 sizePolicy.setHorizontalStretch(0)
161 sizePolicy.setVerticalStretch(0)
162 sizePolicy.setHeightForWidth(self.twitter_button.sizePolicy().hasHeightForWidth())
163 self.twitter_button.setSizePolicy(sizePolicy)
164 self.twitter_button.setMaximumSize(QtCore.QSize(16, 16))
153 self.twitter_button.setCursor(QtCore.Qt.PointingHandCursor)165 self.twitter_button.setCursor(QtCore.Qt.PointingHandCursor)
154 self.twitter_button.setStyleSheet(_fromUtf8("border: 0;"))
155 self.twitter_button.setText(_fromUtf8(""))
156 icon = QtGui.QIcon()166 icon = QtGui.QIcon()
157 icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/twitter.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)167 icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/twitter.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
158 self.twitter_button.setIcon(icon)168 self.twitter_button.setIcon(icon)
159 self.twitter_button.setIconSize(QtCore.QSize(22, 22))
160 self.twitter_button.setObjectName(_fromUtf8("twitter_button"))169 self.twitter_button.setObjectName(_fromUtf8("twitter_button"))
161 self.horizontalLayout.addWidget(self.twitter_button)170 self.horizontalLayout.addWidget(self.twitter_button)
162 self.facebook_button = QtGui.QToolButton(self.frame_footer)171 self.facebook_button = QtGui.QPushButton(self.frame_footer)
172 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
173 sizePolicy.setHorizontalStretch(0)
174 sizePolicy.setVerticalStretch(0)
175 sizePolicy.setHeightForWidth(self.facebook_button.sizePolicy().hasHeightForWidth())
176 self.facebook_button.setSizePolicy(sizePolicy)
177 self.facebook_button.setMaximumSize(QtCore.QSize(16, 16))
163 self.facebook_button.setCursor(QtCore.Qt.PointingHandCursor)178 self.facebook_button.setCursor(QtCore.Qt.PointingHandCursor)
164 self.facebook_button.setStyleSheet(_fromUtf8("border: 0;"))
165 self.facebook_button.setText(_fromUtf8(""))
166 icon1 = QtGui.QIcon()179 icon1 = QtGui.QIcon()
167 icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/facebook.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)180 icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/facebook.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off)
168 self.facebook_button.setIcon(icon1)181 self.facebook_button.setIcon(icon1)
169 self.facebook_button.setIconSize(QtCore.QSize(22, 22))
170 self.facebook_button.setObjectName(_fromUtf8("facebook_button"))182 self.facebook_button.setObjectName(_fromUtf8("facebook_button"))
171 self.horizontalLayout.addWidget(self.facebook_button)183 self.horizontalLayout.addWidget(self.facebook_button)
172 self.verticalLayout.addWidget(self.frame_footer)184 self.verticalLayout.addWidget(self.frame_footer)
@@ -182,7 +194,7 @@
182 self.get_more_space_button.setText(_('Get more storage'))194 self.get_more_space_button.setText(_('Get more storage'))
183 self.tab_widget.setTabText(self.tab_widget.indexOf(self.folders_tab), _('Folders'))195 self.tab_widget.setTabText(self.tab_widget.indexOf(self.folders_tab), _('Folders'))
184 self.tab_widget.setTabText(self.tab_widget.indexOf(self.devices_tab), _('Devices'))196 self.tab_widget.setTabText(self.tab_widget.indexOf(self.devices_tab), _('Devices'))
185 self.tab_widget.setTabText(self.tab_widget.indexOf(self.preferences_tab), _('Preferences'))197 self.tab_widget.setTabText(self.tab_widget.indexOf(self.preferences_tab), _('Settings'))
186 self.tab_widget.setTabText(self.tab_widget.indexOf(self.account_tab), _('Account information'))198 self.tab_widget.setTabText(self.tab_widget.indexOf(self.account_tab), _('Account information'))
187 self.help_button.setText(_('Get help online'))199 self.help_button.setText(_('Get help online'))
188 self.follow_us_label.setText(_('Talk to us'))200 self.follow_us_label.setText(_('Talk to us'))
189201
=== modified file 'ubuntuone/controlpanel/gui/qt/ui/device_ui.py'
--- ubuntuone/controlpanel/gui/qt/ui/device_ui.py 2011-08-12 19:12:08 +0000
+++ ubuntuone/controlpanel/gui/qt/ui/device_ui.py 2011-08-25 19:40:18 +0000
@@ -2,7 +2,7 @@
22
3# Form implementation generated from reading ui file 'data/qt/device.ui'3# Form implementation generated from reading ui file 'data/qt/device.ui'
4#4#
5# Created: Fri Aug 12 15:07:18 20115# Created: Thu Aug 25 15:26:50 2011
6# by: PyQt4 UI code generator 4.8.36# by: PyQt4 UI code generator 4.8.3
7#7#
8# WARNING! All changes made in this file will be lost!8# WARNING! All changes made in this file will be lost!
@@ -32,6 +32,7 @@
32 spacerItem = QtGui.QSpacerItem(217, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)32 spacerItem = QtGui.QSpacerItem(217, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
33 self.horizontalLayout.addItem(spacerItem)33 self.horizontalLayout.addItem(spacerItem)
34 self.remove_device_button = QtGui.QPushButton(Form)34 self.remove_device_button = QtGui.QPushButton(Form)
35 self.remove_device_button.setProperty(_fromUtf8("secondary"), True)
35 self.remove_device_button.setObjectName(_fromUtf8("remove_device_button"))36 self.remove_device_button.setObjectName(_fromUtf8("remove_device_button"))
36 self.horizontalLayout.addWidget(self.remove_device_button)37 self.horizontalLayout.addWidget(self.remove_device_button)
3738
3839
=== modified file 'ubuntuone/controlpanel/gui/qt/ui/devices_ui.py'
--- ubuntuone/controlpanel/gui/qt/ui/devices_ui.py 2011-08-12 19:12:08 +0000
+++ ubuntuone/controlpanel/gui/qt/ui/devices_ui.py 2011-08-25 19:40:18 +0000
@@ -2,7 +2,7 @@
22
3# Form implementation generated from reading ui file 'data/qt/devices.ui'3# Form implementation generated from reading ui file 'data/qt/devices.ui'
4#4#
5# Created: Fri Aug 12 15:07:18 20115# Created: Thu Aug 25 15:26:50 2011
6# by: PyQt4 UI code generator 4.8.36# by: PyQt4 UI code generator 4.8.3
7#7#
8# WARNING! All changes made in this file will be lost!8# WARNING! All changes made in this file will be lost!
99
=== modified file 'ubuntuone/controlpanel/gui/qt/ui/filesyncstatus_ui.py'
--- ubuntuone/controlpanel/gui/qt/ui/filesyncstatus_ui.py 2011-08-12 19:12:08 +0000
+++ ubuntuone/controlpanel/gui/qt/ui/filesyncstatus_ui.py 2011-08-25 19:40:18 +0000
@@ -2,7 +2,7 @@
22
3# Form implementation generated from reading ui file 'data/qt/filesyncstatus.ui'3# Form implementation generated from reading ui file 'data/qt/filesyncstatus.ui'
4#4#
5# Created: Fri Aug 12 15:07:18 20115# Created: Thu Aug 25 15:26:50 2011
6# by: PyQt4 UI code generator 4.8.36# by: PyQt4 UI code generator 4.8.3
7#7#
8# WARNING! All changes made in this file will be lost!8# WARNING! All changes made in this file will be lost!
@@ -35,6 +35,7 @@
35 self.horizontalLayout.addWidget(self.sync_status_label)35 self.horizontalLayout.addWidget(self.sync_status_label)
36 self.verticalLayout.addLayout(self.horizontalLayout)36 self.verticalLayout.addLayout(self.horizontalLayout)
37 self.sync_status_button = QtGui.QPushButton(Form)37 self.sync_status_button = QtGui.QPushButton(Form)
38 self.sync_status_button.setProperty(_fromUtf8("secondary"), True)
38 self.sync_status_button.setObjectName(_fromUtf8("sync_status_button"))39 self.sync_status_button.setObjectName(_fromUtf8("sync_status_button"))
39 self.verticalLayout.addWidget(self.sync_status_button)40 self.verticalLayout.addWidget(self.sync_status_button)
40 self.sync_status_label.setBuddy(self.sync_status_button)41 self.sync_status_label.setBuddy(self.sync_status_button)
4142
=== modified file 'ubuntuone/controlpanel/gui/qt/ui/folders_ui.py'
--- ubuntuone/controlpanel/gui/qt/ui/folders_ui.py 2011-08-12 19:12:08 +0000
+++ ubuntuone/controlpanel/gui/qt/ui/folders_ui.py 2011-08-25 19:40:18 +0000
@@ -2,7 +2,7 @@
22
3# Form implementation generated from reading ui file 'data/qt/folders.ui'3# Form implementation generated from reading ui file 'data/qt/folders.ui'
4#4#
5# Created: Fri Aug 12 15:07:18 20115# Created: Thu Aug 25 15:26:50 2011
6# by: PyQt4 UI code generator 4.8.36# by: PyQt4 UI code generator 4.8.3
7#7#
8# WARNING! All changes made in this file will be lost!8# WARNING! All changes made in this file will be lost!
@@ -20,7 +20,6 @@
20 Form.setObjectName(_fromUtf8("Form"))20 Form.setObjectName(_fromUtf8("Form"))
21 Form.resize(345, 279)21 Form.resize(345, 279)
22 Form.setWindowTitle(_fromUtf8("Form"))22 Form.setWindowTitle(_fromUtf8("Form"))
23 Form.setStyleSheet(_fromUtf8("padding: 0px;"))
24 self.verticalLayout = QtGui.QVBoxLayout(Form)23 self.verticalLayout = QtGui.QVBoxLayout(Form)
25 self.verticalLayout.setSpacing(0)24 self.verticalLayout.setSpacing(0)
26 self.verticalLayout.setMargin(0)25 self.verticalLayout.setMargin(0)
@@ -32,7 +31,7 @@
32 self.horizontalLayout_2.setMargin(3)31 self.horizontalLayout_2.setMargin(3)
33 self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2"))32 self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2"))
34 self.share_publish_button = GoToWebButton(self.frame_top)33 self.share_publish_button = GoToWebButton(self.frame_top)
35 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)34 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)
36 sizePolicy.setHorizontalStretch(0)35 sizePolicy.setHorizontalStretch(0)
37 sizePolicy.setVerticalStretch(0)36 sizePolicy.setVerticalStretch(0)
38 sizePolicy.setHeightForWidth(self.share_publish_button.sizePolicy().hasHeightForWidth())37 sizePolicy.setHeightForWidth(self.share_publish_button.sizePolicy().hasHeightForWidth())
@@ -66,7 +65,7 @@
66 spacerItem1 = QtGui.QSpacerItem(53, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)65 spacerItem1 = QtGui.QSpacerItem(53, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
67 self.horizontalLayout.addItem(spacerItem1)66 self.horizontalLayout.addItem(spacerItem1)
68 self.add_folder_button = AddFolderButton(self.frame_bottom)67 self.add_folder_button = AddFolderButton(self.frame_bottom)
69 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred)68 sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)
70 sizePolicy.setHorizontalStretch(0)69 sizePolicy.setHorizontalStretch(0)
71 sizePolicy.setVerticalStretch(0)70 sizePolicy.setVerticalStretch(0)
72 sizePolicy.setHeightForWidth(self.add_folder_button.sizePolicy().hasHeightForWidth())71 sizePolicy.setHeightForWidth(self.add_folder_button.sizePolicy().hasHeightForWidth())
7372
=== modified file 'ubuntuone/controlpanel/gui/qt/ui/images_rc.py'
--- ubuntuone/controlpanel/gui/qt/ui/images_rc.py 2011-08-12 19:12:08 +0000
+++ ubuntuone/controlpanel/gui/qt/ui/images_rc.py 2011-08-25 19:40:18 +0000
@@ -2,7 +2,7 @@
22
3# Resource object code3# Resource object code
4#4#
5# Created: Fri Aug 12 15:07:18 20115# Created: Thu Aug 25 15:26:50 2011
6# by: The Resource Compiler for PyQt (Qt v4.7.2)6# by: The Resource Compiler for PyQt (Qt v4.7.2)
7#7#
8# WARNING! All changes made in this file will be lost!8# WARNING! All changes made in this file will be lost!
@@ -307,90 +307,84 @@
307\xf8\xb9\x66\x02\x5d\x38\x49\x38\xe7\x8e\x81\x63\xe0\x88\x7f\x33\307\xf8\xb9\x66\x02\x5d\x38\x49\x38\xe7\x8e\x81\x63\xe0\x88\x7f\x33\
308\x0f\xac\x24\xad\x7e\x03\xdb\x11\x28\xad\xbd\x6a\x0e\xf8\x00\x00\308\x0f\xac\x24\xad\x7e\x03\xdb\x11\x28\xad\xbd\x6a\x0e\xf8\x00\x00\
309\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\309\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
310\x00\x00\x05\x1d\310\x00\x00\x02\x50\
311\x89\311\x89\
312\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\312\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
313\x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\313\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
314\x00\x00\x04\xe4\x49\x44\x41\x54\x78\xda\xbd\x97\xd9\x4f\xdc\x55\314\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\
315\x14\xc7\xfd\x37\x7c\xb1\x16\xda\x29\x05\x5a\xa8\xec\x20\x52\x95\315\x01\x00\x9a\x9c\x18\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
316\xae\x94\x62\x23\x89\x21\x46\x4d\x7c\x33\xbe\xa8\x2f\x8d\x7d\xe9\316\x74\x77\x61\x72\x65\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\
317\x43\xe3\xd2\x5a\x6b\x95\x61\x93\xd2\x05\x81\xd6\x16\x0a\x96\x75\317\x65\x52\x65\x61\x64\x79\x71\xc9\x65\x3c\x00\x00\x01\xdd\x49\x44\
318\x98\x8d\x6d\xa6\x82\x0c\xdb\x0c\xfb\xb0\x15\x04\xa4\x44\x91\x52\318\x41\x54\x78\xda\x94\x53\xbb\x6e\x13\x41\x14\x3d\x33\x3b\x5e\x7b\
319\xa9\x9d\xb1\xc7\x73\x6e\x38\xbf\xfb\x63\x7e\x3f\x48\xe0\xa1\x93\319\x63\x3b\x0f\x04\x29\x42\x91\x2d\x03\x55\x0a\xa4\xd4\x28\xe9\x23\
320\x7c\x72\x96\x7b\xee\xf9\x7e\x73\x5b\xc8\xf0\x82\xf8\xa4\x9e\x8e\320\x37\x94\xf9\x01\xc4\x27\x50\x52\xd2\x13\x41\xc3\x17\xa4\xa3\xa3\
321\x44\x0a\x11\xfb\x73\xa2\x90\x34\xd5\xe2\xf6\xac\x32\xe7\xfc\xd9\321\xa0\x41\xe9\x40\x02\x04\x54\xd9\x14\x11\x49\x94\x28\xb1\x1d\xef\
322\xae\x59\xff\x97\x9e\x05\x38\xd7\x37\xaf\x0b\x9e\xe9\xc6\x2f\xdc\322\x63\x66\x76\x86\x3b\xb3\x8e\xbd\x11\x44\x8a\xef\xea\xee\xce\xeb\
323\x7f\xa8\x67\x36\xbd\x77\xb6\x7b\xd6\x4f\x5a\xa4\x29\xb4\xc9\x4d\323\x9c\x3d\xf7\xcc\x8c\x78\xf9\xf1\xcb\xfa\x72\xb7\xbb\x77\xbf\xd3\
324\x66\x89\x63\xee\xb4\x6b\x06\xde\x77\x3c\x80\x93\x36\x2f\xa4\x5b\324\x8e\xe7\x5b\x11\xee\x12\x83\x3c\xc3\xd9\xd5\x28\x39\x1d\x0e\x7b\
325\xd7\xf3\x56\xd3\x04\xa3\xf4\x32\xec\xe3\x9c\xeb\xcd\xeb\xf6\x4e\325\x78\xfd\xf9\xd7\xc1\xd1\x95\xb2\xca\xd8\x99\xd2\x61\x1c\x56\x74\
326\x21\xef\xb6\x4c\xc0\x99\xae\x39\xc8\x2c\x69\x9b\x13\x2f\x41\x4e\326\x9a\x61\xcc\x60\xf1\x67\x24\x31\x4b\x08\xc6\xb0\x18\xb5\x62\x11\
327\x3e\x75\x4e\xf8\xb3\x9a\xc6\x21\xc3\x36\x0e\x27\xcc\xa3\x1c\x35\327\x70\x86\x81\xd4\x44\x51\x0f\x36\xfe\xda\x5b\x09\x98\x9f\xb5\x10\
328\x04\x9c\x73\x4e\x6c\x32\xe3\xd5\x9c\x67\xb5\x4c\x02\x69\x92\xb6\328\xb9\x92\xc8\x4b\x33\x99\x48\x8b\x12\xcf\xde\x7e\xf5\xed\x0f\x2f\
329\x30\xf0\xc9\xaf\x53\x70\xd2\xe2\x85\xb4\xc6\x51\x38\x6e\x1a\x16\329\x9e\x80\xd4\xde\x4a\xe2\xb0\x22\x97\x0a\xb2\x46\x50\x12\x40\xdb\
330\xa4\x63\x4d\xf1\x94\x65\x04\xd2\xee\x76\x43\x98\xd1\x02\xa1\xd9\330\x4a\x81\xf5\xe0\x29\x01\xd5\x8e\x3a\x9f\xc3\x8a\x42\x2b\xa8\xb2\
331\x66\x38\x5e\xee\x82\x4c\xdb\x18\x9f\xe3\x9d\x11\xca\x05\x69\xa6\331\x9c\x0c\x46\x21\x47\x2b\x6c\xf8\xf6\xf6\xee\xb7\xc9\x78\xbb\xc9\
332\x11\xea\x31\xdc\xa3\xa8\x46\x68\x7d\xd6\x31\x0d\x8a\x81\x8f\x9d\332\xf1\x7e\x67\xed\xc6\xcf\x1c\xd6\x13\x48\x33\x25\x68\x18\x06\xd1\
333\x53\x70\xb4\x7e\x48\x18\x60\xb8\x7e\xbb\xb2\x07\x2a\xfa\x67\x60\333\x14\xff\x9a\x16\x06\x30\xa4\xa0\xbe\xb6\x22\x50\x9a\xea\x04\xb4\
334\xf9\x89\x4f\x50\x8e\x79\x66\x55\x2f\x9d\x33\x3c\xaf\xad\x79\xa7\334\xa9\x98\xa3\xc0\xc2\x34\x2b\x05\xaf\x36\x57\xb0\xba\x34\x25\xbb\
335\x69\x14\x8e\x35\x0c\x53\x8d\xc6\x46\x44\xfc\xc8\x31\x29\x0d\x7c\335\x24\xc9\x76\x5c\x83\xe0\x1c\x0e\xcb\x25\xb1\x38\xa3\x24\x11\xb8\
336\xd8\xe4\x15\x03\x87\x6b\x07\x38\x32\x42\x3c\xf0\x53\xee\x99\x81\336\x54\xd6\x60\x79\x29\xf2\x24\xef\xbe\xf7\xd1\x2f\x2c\x39\x5e\x3d\
337\x23\xb5\x83\x3c\xab\x81\xf7\x30\xb2\x96\xa0\xa6\x34\xf0\x9e\x7d\337\x6a\xbc\xc6\xa5\xc7\x78\x05\xa5\xae\x3a\xe3\xda\xce\x33\x89\xde\
338\x0c\x8e\xd4\x0d\x09\x52\xab\xfb\x45\x64\x36\xf8\xd0\x9c\x06\xd5\338\xda\x02\x76\x7f\x0c\x70\x90\x59\x3c\xff\x74\xee\xc7\x3b\x0d\x8e\
339\x3d\xae\x39\x47\x13\xeb\xcf\x50\x53\x1a\xc8\x6c\x18\x84\xc3\x35\339\x37\x4f\xef\xd1\x79\xc9\x7d\x3f\x74\x0a\x08\xcb\x0b\xad\xfd\x7e\
340\x03\xf0\xe6\x2f\x6e\x38\x84\x87\x9c\x13\xbe\x67\xa0\x07\x9f\xd3\340\x4a\x32\xd2\x65\x46\xfd\xf5\x07\x0c\x3b\x8f\x17\x30\xdf\x0e\xc1\
341\x2c\x83\xb5\x47\x90\x7a\xcf\x13\xd0\x77\x6b\x72\xd4\x94\x06\x32\341\x49\x89\xcf\x50\x78\xf9\xd7\xeb\x1c\xc6\x61\x85\x7b\xb9\x90\x66\
342\xea\x07\xe9\x12\xd2\x0f\xaf\x57\xf6\x11\x94\x0b\x56\xff\x03\x5d\342\xea\xee\x71\x9a\xe3\xd1\x62\x80\xad\x87\x9d\x1b\x46\x26\xc3\x11\
343\x76\x7e\x53\x0b\x3b\x2e\x08\x30\xaf\x13\xb5\xba\x97\x52\xec\x84\343\x79\x65\x6b\x26\x12\x81\xab\x63\x90\x66\x08\x18\x47\xaa\x6b\x0e\
344\x34\x7c\x7a\xda\xc1\xbb\xd5\x1a\x27\x6a\xfa\xa5\x81\x43\x55\x7d\344\x53\x49\xfd\x42\xfd\xf7\x00\xcd\x89\xc0\x63\x2a\x0f\xb4\x4e\xf6\
345\x90\x52\xd1\x0b\x6f\x54\xb9\xd7\xf0\x28\xf5\x23\x3f\x6c\x8b\x7b\345\x93\x24\xde\x88\x57\x11\x77\xe7\xee\x74\x0f\x2e\xd2\x14\xfb\xc9\
346\xc3\xf3\x90\x76\xb3\x83\x77\x11\x28\xec\x56\x72\xd4\x94\x06\x0e\346\xa1\x53\x90\x08\x5b\x96\xbd\xdf\xc7\x27\x7b\x3f\x4f\x4e\xe3\x59\
347\xde\xed\x83\xe4\x3b\xdd\x0c\x0e\xf4\x00\xf7\x96\x7c\xb0\x6d\x2a\347\x2e\x13\xb7\x36\xa1\x1d\xef\xfd\x15\x60\x00\xe8\x15\x51\x25\x42\
348\x87\xe6\x69\x0f\xc3\x3b\x39\x97\x06\x52\xca\x7b\x81\x79\xf5\xe7\348\x1c\xe5\x09\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
349\x2e\x46\xd4\x8b\x4f\x61\xdb\xfc\xed\x03\xde\xab\xb7\x5f\x1a\x48\349\x00\x00\x02\x50\
350\xbc\xd9\x09\x4c\xf2\xed\x6e\x85\xa4\x5b\x2e\x98\xff\x17\xb6\xc5\350\x89\
351\x9f\x28\xde\x3c\xbd\x44\x7b\xe4\x5e\x99\xa3\x01\x97\x34\x10\x5f\351\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
352\xda\x01\x04\x1d\x92\x68\x12\xc7\x5b\x9d\x30\xf3\x04\x74\x79\xfa\352\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
353\x6c\x73\xec\xd3\x7f\x41\xea\x1d\x17\xed\x5d\xdb\xa5\xe4\x8c\x34\353\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\
354\x90\x50\xd6\x09\xb1\x3f\xb5\x03\x45\xce\xb9\x9e\x5c\x05\x3d\x34\354\x01\x00\x9a\x9c\x18\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
355\x73\xaa\x1c\x85\x7e\xe3\x5d\x7a\x73\x8c\x34\x10\x57\xd2\x01\xd1\355\x74\x77\x61\x72\x65\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\
356\x37\xee\x0b\x28\x67\xa8\xf6\xfe\x03\xba\xf0\xbc\x44\xde\xe5\x3c\356\x65\x52\x65\x61\x64\x79\x71\xc9\x65\x3c\x00\x00\x01\xdd\x49\x44\
357\xa6\x58\xb3\x4f\x9d\x4b\x03\xb1\xc5\xed\x40\x44\x5d\x73\x8a\xa8\357\x41\x54\x78\xda\x94\x53\xbb\x6e\x13\x41\x14\x3d\x33\x3b\x5e\x7b\
358\x66\x0c\xc5\x86\x1e\x6b\x39\x76\xdb\xa5\xb9\xc3\xb9\x6e\x2d\x7b\358\x63\x3b\x0f\x04\x29\x42\x91\x2d\x03\x55\x0a\xa4\xd4\x28\xe9\x23\
359\xdc\x97\x06\x0e\x14\xb5\x01\x11\x75\xd5\x01\x9c\x47\x5f\xbf\x2f\359\x37\x94\xf9\x01\xc4\x27\x50\x52\xd2\x13\x41\xc3\x17\xa4\xa3\xa3\
360\xa2\x79\x6a\x09\x3c\x2b\xa0\x21\xbd\xd8\x01\x47\xcb\xda\x21\x09\360\xa0\x41\xe9\x40\x02\x04\x54\xd9\x14\x11\x49\x94\x28\xb1\x1d\xef\
361\x9f\x52\x7d\x47\xe2\xe4\x1d\x0c\x6b\xf0\x8c\x34\xf0\xca\x55\x27\361\x63\x66\x76\x86\x3b\xb3\x8e\xbd\x11\x44\x8a\xef\xea\xee\xce\xeb\
362\xa8\x89\x28\x6c\x55\xf2\xd2\xfe\x39\xe8\x7d\x04\x1a\x9c\x8b\x3e\362\x9c\x3d\xf7\xcc\x8c\x78\xf9\xf1\xcb\xfa\x72\xb7\xbb\x77\xbf\xd3\
363\xf8\xa0\xc2\x05\xbb\xce\x57\xc3\x8b\xe7\xaa\xd6\xa8\x54\xf2\x98\363\x8e\xe7\x5b\x11\xee\x12\x83\x3c\xc3\xd9\xd5\x28\x39\x1d\x0e\x7b\
364\x5c\x2b\xc4\x5d\x73\xa8\x77\x71\xce\x51\x1a\x88\xbc\xd2\x06\x91\364\x78\xfd\xf9\xd7\xc1\xd1\x95\xb2\xca\xd8\x99\xd2\x61\x1c\x56\x74\
365\x57\x5a\x61\x5f\x41\xb3\x80\x6a\x82\xf2\x77\xf0\xbb\xc0\x30\x3e\365\x9a\x61\xcc\x60\xf1\x67\x24\x31\x4b\x08\xc6\xb0\x18\xb5\x62\x11\
366\xb9\x6b\x79\x6b\xe4\xf4\xcc\x42\x7c\x9e\x95\x77\x29\xec\xc7\x9d\366\x70\x86\x81\xd4\x44\x51\x0f\x36\xfe\xda\x5b\x09\x98\x9f\xb5\x10\
367\x08\xed\x96\x06\xf6\xff\xd8\x02\x44\x58\x9e\x5d\xc4\xf0\x7c\x3b\367\xb9\x92\xc8\x4b\x33\x99\x48\x8b\x12\xcf\xde\x7e\xf5\xed\x0f\x2f\
368\xd7\x82\x86\xb1\x45\x70\xe3\xb3\x77\x2e\x6f\x8d\xeb\x03\x0f\x71\368\x9e\x80\xd4\xde\x4a\xe2\xb0\x22\x97\x0a\xb2\x46\x50\x12\x40\xdb\
369\x4f\x33\x84\xe3\x0e\xde\xad\xd6\x92\x2f\x80\x8d\xd0\x1c\x1b\xec\369\x4a\x81\xf5\xe0\x29\x01\xd5\x8e\x3a\x9f\xc3\x8a\x42\x2b\xa8\xb2\
370\xcb\x6f\x86\x30\x8c\xa1\x01\x24\xe2\xbf\x5b\xcf\xc2\x0a\xbe\xc4\370\x9c\x0c\x46\x21\x47\x2b\x6c\xf8\xf6\xf6\xee\xb7\xc9\x78\xbb\xc9\
371\xd6\x0c\x0c\xac\x00\xed\xe3\xdd\x02\xce\x51\x53\x1a\x88\xc2\xe7\371\xf1\x7e\x67\xed\xc6\xcf\x1c\xd6\x13\x48\x33\x25\x68\x18\x06\xd1\
372\xd8\x6b\xb4\xc0\xde\x6c\x0b\x84\xe7\xda\x09\x1c\xb4\x52\x14\xbd\372\x14\xff\x9a\x16\x06\x30\xa4\xa0\xbe\xb6\x22\x50\x9a\xea\x04\xb4\
373\x30\x8c\x09\x45\xad\x50\x3f\xf6\x50\xfc\x8e\x1f\x5f\xc5\xe5\x9b\373\xa9\x98\xa3\xc0\xc2\x34\x2b\x05\xaf\x36\x57\xb0\xba\x34\x25\xbb\
374\x9b\x11\x66\xdb\x66\x96\xf8\x3e\x45\xde\x27\x88\xcc\xb5\x49\x03\374\x24\xc9\x76\x5c\x83\xe0\x1c\x0e\xcb\x25\xb1\x38\xa3\x24\x11\xb8\
375\xd1\x68\x20\x04\x9b\x7b\xbe\x37\x13\x22\x0f\x35\xda\xd4\x3d\xaa\375\x54\xd6\x60\x79\x29\xf2\x24\xef\xbe\xf7\xd1\x2f\x2c\x39\x5e\x3d\
376\x45\x4c\xbd\xe1\x84\xa2\xae\x07\xd0\x8a\xbf\xe9\x1e\xfb\x61\x43\376\x6a\xbc\xc6\xa5\xc7\x78\x05\xa5\xae\x3a\xe3\xda\xce\x33\x89\xde\
377\xea\xbc\x0b\x10\x9d\x8f\x82\x46\xab\xb8\xcb\xf7\x99\x03\x6a\x03\377\xda\x02\x76\x7f\x0c\x70\x90\x59\x3c\xff\x74\xee\xc7\x3b\x0d\x8e\
378\x31\x05\x4d\xb0\xfb\x3b\x93\x42\xc8\x0f\x16\x42\xd3\xe3\x68\xc0\378\x37\x4f\xef\xd1\x79\xc9\x7d\x3f\x74\x0a\x08\xcb\x0b\xad\xfd\x7e\
379\x05\x5c\x6f\x00\x8a\x34\xf2\xbc\xee\xbe\xa8\x5c\xab\xca\x00\x16\379\x4a\x32\xd2\x65\x46\xfd\xf5\x07\x0c\x3b\x8f\x17\x30\xdf\x0e\xc1\
380\xbb\xbe\xad\x87\x3d\x97\x1b\x15\x0c\x97\x4d\xdc\xa3\xc8\x35\x81\380\x49\x89\xcf\x50\x78\xf9\xd7\xeb\x1c\xc6\x61\x85\x7b\xb9\x90\x66\
381\x0b\x1a\x44\x5f\xd6\xf2\x4c\x75\x47\xce\x5c\x92\xbb\x79\x26\x46\381\xea\xee\x71\x9a\xe3\xd1\x62\x80\xad\x87\x9d\x1b\x46\x26\xc3\x11\
382\x6d\x20\x0e\x8b\x60\xfc\x56\x13\x14\xc0\xee\x4b\x26\x08\xba\x28\382\x79\x65\x6b\x26\x12\x81\xab\x63\x90\x66\x08\x18\x47\xaa\x6b\x0e\
383\xeb\x60\x12\xc3\x1e\x42\xf5\x66\x91\x73\xcd\x5d\xca\x0d\x18\x13\383\x53\x49\xfd\x42\xfd\xf7\x00\xcd\x89\xc0\x63\x2a\x0f\xb4\x4e\xf6\
384\x0a\x54\x3f\x05\xb1\xc6\x46\x9f\x01\x85\x5e\x3e\x5f\x03\xc1\x17\384\x93\x24\xde\x88\x57\x11\x77\xe7\xee\x74\x0f\x2e\xd2\x14\xfb\xc9\
385\xeb\x75\xa1\x33\x26\x48\x53\x8b\xbb\x0c\x7d\x35\x53\xdf\xd1\xdc\385\xa1\x53\x90\x08\x5b\x96\xbd\xdf\xc7\x27\x7b\x3f\x4f\x4e\xe3\x59\
386\x0f\x41\x33\xf1\x46\x8b\x8f\x0d\x14\x1a\x3e\x2f\xfd\x3d\x31\x0f\386\x2e\x13\xb7\x36\xa1\x1d\xef\xfd\x15\x60\x00\xe8\x15\x51\x25\x42\
387\xff\xa3\xe0\x53\xee\xbc\x50\x27\x78\xe9\xab\x6a\x06\x2f\xd5\x8a\387\x1c\xe5\x09\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
388\xc8\x67\x3b\xbe\xae\xe1\x9a\xa2\x44\xce\x68\x72\x51\xe3\x1e\xd2\
389\x20\x2d\xd2\xe4\x3f\xcd\x22\xc8\x89\xe1\x0c\x9a\xc8\x36\xf9\x0f\
390\xe2\xd3\xbc\x96\x67\x01\x8a\x0c\xf7\x38\xea\x91\x92\x6f\xe5\x99\
391\x80\x68\x5b\xd7\x4b\xce\x31\xfb\x49\x8b\x34\x85\x36\x00\xb0\x89\
392\xe7\xfd\xe7\x79\x04\x69\xff\x0f\xc3\x78\xed\x1a\xcc\x42\xc6\x50\
393\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
394\x00\x00\x01\x27\388\x00\x00\x01\x27\
395\x89\389\x89\
396\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\390\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@@ -412,308 +406,329 @@
412\x04\xc3\x06\xd0\xb4\xf6\x1c\xaa\xf8\x39\x94\x8f\x12\xe1\x00\x01\406\x04\xc3\x06\xd0\xb4\xf6\x1c\xaa\xf8\x39\x94\x8f\x12\xe1\x00\x01\
413\x06\x00\x00\x4e\x7e\x7b\x48\x93\x95\x27\x00\x00\x00\x00\x49\x45\407\x06\x00\x00\x4e\x7e\x7b\x48\x93\x95\x27\x00\x00\x00\x00\x49\x45\
414\x4e\x44\xae\x42\x60\x82\408\x4e\x44\xae\x42\x60\x82\
415\x00\x00\x12\xb1\409\x00\x00\x14\x10\
416\x51\410\x51\
417\x4d\x61\x69\x6e\x57\x69\x6e\x64\x6f\x77\x20\x7b\x0a\x20\x20\x20\411\x4d\x61\x69\x6e\x57\x69\x6e\x64\x6f\x77\x20\x7b\x0a\x20\x20\x20\
418\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x2d\x63\x6f\x6c\x6f\412\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x2d\x63\x6f\x6c\x6f\
419\x72\x3a\x20\x23\x64\x64\x34\x38\x31\x34\x3b\x0a\x7d\x0a\x0a\x51\413\x72\x3a\x20\x23\x64\x64\x34\x38\x31\x34\x3b\x0a\x7d\x0a\x0a\x51\
420\x57\x69\x64\x67\x65\x74\x20\x7b\x0a\x20\x20\x20\x20\x66\x6f\x6e\414\x57\x69\x64\x67\x65\x74\x20\x7b\x0a\x20\x20\x20\x20\x66\x6f\x6e\
421\x74\x2d\x66\x61\x6d\x69\x6c\x79\x3a\x20\x22\x55\x62\x75\x6e\x74\415\x74\x2d\x66\x61\x6d\x69\x6c\x79\x3a\x20\x22\x55\x62\x75\x6e\x74\
422\x75\x22\x3b\x0a\x7d\x0a\x0a\x51\x46\x72\x61\x6d\x65\x20\x7b\x0a\416\x75\x22\x3b\x0a\x20\x20\x20\x20\x66\x6f\x6e\x74\x2d\x73\x69\x7a\
423\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x3a\x20\x6e\x6f\x6e\x65\417\x65\x3a\x20\x31\x33\x70\x78\x3b\x0a\x20\x20\x20\x20\x63\x6f\x6c\
424\x3b\x0a\x7d\x0a\x0a\x51\x46\x72\x61\x6d\x65\x23\x66\x72\x61\x6d\418\x6f\x72\x3a\x20\x23\x33\x33\x33\x33\x33\x33\x3b\x0a\x7d\x0a\x0a\
425\x65\x5f\x68\x65\x61\x64\x65\x72\x20\x7b\x0a\x20\x20\x20\x20\x62\419\x51\x46\x72\x61\x6d\x65\x20\x7b\x0a\x20\x20\x20\x20\x62\x6f\x72\
426\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\x23\x66\x66\x66\x66\420\x64\x65\x72\x3a\x20\x6e\x6f\x6e\x65\x3b\x0a\x7d\x0a\x0a\x51\x46\
427\x66\x66\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x72\421\x72\x61\x6d\x65\x23\x66\x72\x61\x6d\x65\x5f\x68\x65\x61\x64\x65\
428\x61\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\422\x72\x20\x7b\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\
429\x62\x6f\x72\x64\x65\x72\x2d\x73\x74\x79\x6c\x65\x3a\x20\x73\x6f\423\x6e\x64\x3a\x20\x23\x66\x66\x66\x66\x66\x66\x3b\x0a\x20\x20\x20\
430\x6c\x69\x64\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\
431\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\
432\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x77\x69\x64\x74\x68\
433\x3a\x20\x31\x70\x78\x3b\x0a\x20\x20\x20\x20\x6d\x69\x6e\x2d\x68\
434\x65\x69\x67\x68\x74\x3a\x20\x39\x30\x70\x78\x3b\x0a\x7d\x0a\x0a\
435\x51\x46\x72\x61\x6d\x65\x23\x66\x72\x61\x6d\x65\x5f\x67\x72\x65\
436\x65\x74\x69\x6e\x67\x20\x7b\x0a\x20\x20\x20\x20\x6d\x61\x72\x67\
437\x69\x6e\x20\x30\x70\x78\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\
438\x69\x6e\x67\x2d\x6c\x65\x66\x74\x3a\x20\x31\x35\x70\x78\x3b\x0a\
439\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\x67\x2d\x74\x6f\x70\x3a\
440\x20\x31\x30\x70\x78\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\
441\x6e\x67\x2d\x72\x69\x67\x68\x74\x3a\x20\x31\x30\x70\x78\x3b\x0a\
442\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\x67\x2d\x62\x6f\x74\x74\
443\x6f\x6d\x3a\x20\x31\x30\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x46\x72\
444\x61\x6d\x65\x23\x66\x72\x61\x6d\x65\x5f\x73\x74\x61\x74\x75\x73\
445\x2c\x0a\x51\x46\x72\x61\x6d\x65\x23\x66\x72\x61\x6d\x65\x5f\x73\
446\x74\x6f\x72\x61\x67\x65\x20\x7b\x0a\x20\x20\x20\x20\x62\x6f\x72\
447\x64\x65\x72\x2d\x73\x74\x79\x6c\x65\x3a\x20\x64\x6f\x74\x74\x65\
448\x64\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x63\x6f\
449\x6c\x6f\x72\x3a\x20\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\
450\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x6c\x65\x66\x74\x2d\x77\x69\
451\x64\x74\x68\x3a\x20\x31\x70\x78\x3b\x0a\x20\x20\x20\x20\x70\x61\
452\x64\x64\x69\x6e\x67\x3a\x20\x31\x30\x70\x78\x3b\x0a\x20\x20\x20\
453\x20\x6d\x69\x6e\x2d\x77\x69\x64\x74\x68\x3a\x20\x34\x30\x70\x78\
454\x3b\x0a\x7d\x0a\x0a\x51\x46\x72\x61\x6d\x65\x23\x61\x63\x63\x6f\
455\x75\x6e\x74\x5f\x73\x65\x70\x61\x72\x61\x74\x6f\x72\x20\x7b\x0a\
456\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x73\x74\x79\x6c\x65\
457\x3a\x20\x64\x6f\x74\x74\x65\x64\x3b\x0a\x20\x20\x20\x20\x62\x6f\
458\x72\x64\x65\x72\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x39\x33\x39\
459\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\
460\x62\x6f\x74\x74\x6f\x6d\x2d\x77\x69\x64\x74\x68\x3a\x20\x31\x70\
461\x78\x3b\x0a\x7d\x0a\x0a\x51\x50\x75\x73\x68\x42\x75\x74\x74\x6f\
462\x6e\x5b\x65\x6e\x61\x62\x6c\x65\x64\x3d\x22\x74\x72\x75\x65\x22\
463\x5d\x20\x7b\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\
464\x6e\x64\x3a\x20\x71\x6c\x69\x6e\x65\x61\x72\x67\x72\x61\x64\x69\
465\x65\x6e\x74\x28\x78\x31\x3a\x20\x30\x2c\x20\x79\x31\x3a\x20\x30\
466\x2c\x20\x78\x32\x3a\x20\x30\x2c\x20\x79\x32\x3a\x20\x31\x2c\x0a\
467\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x6f\x70\x3a\x20\x30\x20\
468\x23\x66\x65\x63\x66\x63\x32\x2c\x20\x73\x74\x6f\x70\x3a\x20\x31\
469\x2e\x30\x20\x23\x65\x34\x34\x65\x31\x39\x29\x3b\x0a\x20\x20\x20\
470\x20\x62\x6f\x72\x64\x65\x72\x2d\x72\x61\x64\x69\x75\x73\x3a\x20\424\x20\x62\x6f\x72\x64\x65\x72\x2d\x72\x61\x64\x69\x75\x73\x3a\x20\
471\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\425\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\
472\x73\x74\x79\x6c\x65\x3a\x20\x73\x6f\x6c\x69\x64\x3b\x0a\x20\x20\426\x73\x74\x79\x6c\x65\x3a\x20\x73\x6f\x6c\x69\x64\x3b\x0a\x20\x20\
473\x20\x20\x70\x61\x64\x64\x69\x6e\x67\x3a\x20\x36\x70\x78\x3b\x0a\427\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\
474\x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x77\x68\x69\x74\x65\428\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\
475\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x63\x6f\x6c\429\x64\x65\x72\x2d\x77\x69\x64\x74\x68\x3a\x20\x31\x70\x78\x3b\x0a\
476\x6f\x72\x3a\x20\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\x20\430\x20\x20\x20\x20\x6d\x69\x6e\x2d\x68\x65\x69\x67\x68\x74\x3a\x20\
477\x20\x62\x6f\x72\x64\x65\x72\x2d\x77\x69\x64\x74\x68\x3a\x20\x31\431\x39\x30\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x46\x72\x61\x6d\x65\x23\
478\x70\x78\x3b\x0a\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3a\x20\432\x66\x72\x61\x6d\x65\x5f\x67\x72\x65\x65\x74\x69\x6e\x67\x20\x7b\
479\x31\x34\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x50\x75\x73\x68\x42\x75\433\x0a\x20\x20\x20\x20\x6d\x61\x72\x67\x69\x6e\x20\x30\x70\x78\x3b\
480\x74\x74\x6f\x6e\x3a\x68\x6f\x76\x65\x72\x5b\x65\x6e\x61\x62\x6c\434\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\x67\x2d\x6c\x65\x66\
435\x74\x3a\x20\x31\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\
436\x64\x69\x6e\x67\x2d\x74\x6f\x70\x3a\x20\x31\x30\x70\x78\x3b\x0a\
437\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\x67\x2d\x72\x69\x67\x68\
438\x74\x3a\x20\x31\x30\x70\x78\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\
439\x64\x69\x6e\x67\x2d\x62\x6f\x74\x74\x6f\x6d\x3a\x20\x31\x30\x70\
440\x78\x3b\x0a\x7d\x0a\x0a\x51\x46\x72\x61\x6d\x65\x23\x66\x72\x61\
441\x6d\x65\x5f\x73\x74\x61\x74\x75\x73\x2c\x0a\x51\x46\x72\x61\x6d\
442\x65\x23\x66\x72\x61\x6d\x65\x5f\x73\x74\x6f\x72\x61\x67\x65\x20\
443\x7b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x73\x74\x79\
444\x6c\x65\x3a\x20\x64\x6f\x74\x74\x65\x64\x3b\x0a\x20\x20\x20\x20\
445\x62\x6f\x72\x64\x65\x72\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x39\
446\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\
447\x72\x2d\x6c\x65\x66\x74\x2d\x77\x69\x64\x74\x68\x3a\x20\x31\x70\
448\x78\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\x67\x3a\x20\
449\x31\x30\x70\x78\x3b\x0a\x20\x20\x20\x20\x6d\x69\x6e\x2d\x77\x69\
450\x64\x74\x68\x3a\x20\x34\x30\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x46\
451\x72\x61\x6d\x65\x23\x61\x63\x63\x6f\x75\x6e\x74\x5f\x73\x65\x70\
452\x61\x72\x61\x74\x6f\x72\x20\x7b\x0a\x20\x20\x20\x20\x62\x6f\x72\
453\x64\x65\x72\x2d\x73\x74\x79\x6c\x65\x3a\x20\x64\x6f\x74\x74\x65\
454\x64\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x63\x6f\
455\x6c\x6f\x72\x3a\x20\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\
456\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x62\x6f\x74\x74\x6f\x6d\x2d\
457\x77\x69\x64\x74\x68\x3a\x20\x31\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\
458\x46\x72\x61\x6d\x65\x23\x66\x72\x6d\x5f\x62\x6f\x78\x20\x7b\x20\
459\x2f\x2a\x54\x68\x65\x20\x4c\x6f\x61\x64\x69\x6e\x67\x20\x4f\x76\
460\x65\x72\x6c\x61\x79\x20\x66\x72\x61\x6d\x65\x2e\x2a\x2f\x0a\x20\
461\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\x23\
462\x66\x66\x66\x66\x66\x66\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\
463\x65\x72\x2d\x72\x61\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\
464\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x73\x74\x79\x6c\x65\
465\x3a\x20\x73\x6f\x6c\x69\x64\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\
466\x64\x65\x72\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x39\x33\x39\x33\
467\x38\x39\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x77\
468\x69\x64\x74\x68\x3a\x20\x31\x70\x78\x3b\x0a\x20\x20\x20\x20\x63\
469\x6f\x6c\x6f\x72\x3a\x20\x77\x68\x69\x74\x65\x3b\x0a\x20\x20\x20\
470\x20\x6d\x69\x6e\x2d\x68\x65\x69\x67\x68\x74\x3a\x20\x31\x30\x30\
471\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x46\x72\x61\x6d\x65\x23\x66\x72\
472\x6d\x5f\x62\x6f\x78\x20\x3e\x20\x51\x4c\x61\x62\x65\x6c\x20\x7b\
473\x0a\x20\x20\x20\x20\x66\x6f\x6e\x74\x2d\x73\x69\x7a\x65\x3a\x20\
474\x32\x34\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x50\x75\x73\x68\x42\x75\
475\x74\x74\x6f\x6e\x20\x7b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\
476\x72\x2d\x72\x61\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\x20\
477\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x73\x74\x79\x6c\x65\x3a\
478\x20\x73\x6f\x6c\x69\x64\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\
479\x69\x6e\x67\x3a\x20\x36\x70\x78\x3b\x0a\x20\x20\x20\x20\x70\x61\
480\x64\x64\x69\x6e\x67\x2d\x6c\x65\x66\x74\x3a\x20\x32\x30\x70\x78\
481\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\x67\x2d\x72\x69\
482\x67\x68\x74\x3a\x20\x32\x30\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\
483\x6f\x72\x64\x65\x72\x2d\x77\x69\x64\x74\x68\x3a\x20\x31\x70\x78\
484\x3b\x0a\x7d\x0a\x0a\x51\x50\x75\x73\x68\x42\x75\x74\x74\x6f\x6e\
485\x5b\x65\x6e\x61\x62\x6c\x65\x64\x3d\x22\x74\x72\x75\x65\x22\x5d\
486\x20\x7b\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\
487\x64\x3a\x20\x71\x6c\x69\x6e\x65\x61\x72\x67\x72\x61\x64\x69\x65\
488\x6e\x74\x28\x78\x31\x3a\x20\x30\x2c\x20\x79\x31\x3a\x20\x30\x2c\
489\x20\x78\x32\x3a\x20\x30\x2c\x20\x79\x32\x3a\x20\x31\x2c\x0a\x20\
490\x20\x20\x20\x20\x20\x20\x20\x73\x74\x6f\x70\x3a\x20\x30\x20\x23\
491\x66\x65\x39\x65\x38\x34\x2c\x73\x74\x6f\x70\x3a\x20\x31\x2e\x30\
492\x20\x23\x64\x64\x34\x38\x31\x34\x29\x3b\x0a\x20\x20\x20\x20\x63\
493\x6f\x6c\x6f\x72\x3a\x20\x77\x68\x69\x74\x65\x3b\x0a\x20\x20\x20\
494\x20\x62\x6f\x72\x64\x65\x72\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\
495\x39\x39\x39\x39\x39\x39\x3b\x0a\x7d\x0a\x0a\x51\x50\x75\x73\x68\
496\x42\x75\x74\x74\x6f\x6e\x3a\x68\x6f\x76\x65\x72\x5b\x65\x6e\x61\
497\x62\x6c\x65\x64\x3d\x22\x74\x72\x75\x65\x22\x5d\x20\x7b\x0a\x20\
498\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\x71\
499\x6c\x69\x6e\x65\x61\x72\x67\x72\x61\x64\x69\x65\x6e\x74\x28\x78\
500\x31\x3a\x20\x30\x2c\x20\x79\x31\x3a\x20\x30\x2c\x20\x78\x32\x3a\
501\x20\x30\x2c\x20\x79\x32\x3a\x20\x31\x2c\x0a\x20\x20\x20\x20\x20\
502\x20\x20\x20\x73\x74\x6f\x70\x3a\x20\x30\x20\x23\x66\x66\x62\x31\
503\x39\x63\x2c\x73\x74\x6f\x70\x3a\x20\x31\x2e\x30\x20\x23\x64\x64\
504\x34\x38\x31\x34\x29\x3b\x0a\x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\
505\x3a\x20\x77\x68\x69\x74\x65\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\
506\x64\x65\x72\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x39\x39\x39\x39\
507\x39\x39\x3b\x0a\x7d\x0a\x0a\x51\x50\x75\x73\x68\x42\x75\x74\x74\
508\x6f\x6e\x3a\x70\x72\x65\x73\x73\x65\x64\x5b\x65\x6e\x61\x62\x6c\
481\x65\x64\x3d\x22\x74\x72\x75\x65\x22\x5d\x20\x7b\x0a\x20\x20\x20\509\x65\x64\x3d\x22\x74\x72\x75\x65\x22\x5d\x20\x7b\x0a\x20\x20\x20\
482\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\x71\x6c\x69\510\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\x71\x6c\x69\
483\x6e\x65\x61\x72\x67\x72\x61\x64\x69\x65\x6e\x74\x28\x78\x31\x3a\511\x6e\x65\x61\x72\x67\x72\x61\x64\x69\x65\x6e\x74\x28\x78\x31\x3a\
484\x20\x30\x2c\x20\x79\x31\x3a\x20\x30\x2c\x20\x78\x32\x3a\x20\x30\512\x20\x30\x2c\x20\x79\x31\x3a\x20\x30\x2c\x20\x78\x32\x3a\x20\x30\
485\x2c\x20\x79\x32\x3a\x20\x31\x2c\x0a\x20\x20\x20\x20\x20\x20\x20\513\x2c\x20\x79\x32\x3a\x20\x31\x2c\x0a\x20\x20\x20\x20\x20\x20\x20\
486\x20\x73\x74\x6f\x70\x3a\x20\x30\x20\x23\x66\x65\x64\x61\x64\x31\514\x20\x73\x74\x6f\x70\x3a\x20\x30\x20\x23\x62\x39\x33\x66\x31\x34\
487\x2c\x73\x74\x6f\x70\x3a\x20\x31\x2e\x30\x20\x23\x65\x34\x37\x61\515\x2c\x73\x74\x6f\x70\x3a\x20\x31\x2e\x30\x20\x23\x64\x64\x34\x38\
488\x35\x35\x29\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\516\x31\x34\x29\x3b\x0a\x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\
489\x72\x61\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\517\x77\x68\x69\x74\x65\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\
490\x20\x62\x6f\x72\x64\x65\x72\x2d\x73\x74\x79\x6c\x65\x3a\x20\x73\518\x72\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x39\x39\x39\x39\x39\x39\
491\x6f\x6c\x69\x64\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\519\x3b\x0a\x7d\x0a\x0a\x51\x50\x75\x73\x68\x42\x75\x74\x74\x6f\x6e\
492\x67\x3a\x20\x36\x70\x78\x3b\x0a\x20\x20\x20\x20\x63\x6f\x6c\x6f\520\x5b\x73\x65\x63\x6f\x6e\x64\x61\x72\x79\x3d\x22\x74\x72\x75\x65\
493\x72\x3a\x20\x77\x68\x69\x74\x65\x3b\x0a\x20\x20\x20\x20\x62\x6f\521\x22\x5d\x20\x7b\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\
494\x72\x64\x65\x72\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x39\x33\x39\522\x75\x6e\x64\x3a\x20\x71\x6c\x69\x6e\x65\x61\x72\x67\x72\x61\x64\
495\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\523\x69\x65\x6e\x74\x28\x78\x31\x3a\x20\x30\x2c\x20\x79\x31\x3a\x20\
496\x77\x69\x64\x74\x68\x3a\x20\x31\x70\x78\x3b\x0a\x20\x20\x20\x20\524\x30\x2c\x20\x78\x32\x3a\x20\x30\x2c\x20\x79\x32\x3a\x20\x31\x2c\
497\x68\x65\x69\x67\x68\x74\x3a\x20\x31\x32\x70\x78\x3b\x0a\x7d\x0a\525\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x6f\x70\x3a\x20\x30\
498\x0a\x51\x50\x75\x73\x68\x42\x75\x74\x74\x6f\x6e\x3a\x70\x72\x65\526\x20\x23\x66\x66\x66\x66\x66\x66\x2c\x73\x74\x6f\x70\x3a\x20\x31\
499\x73\x73\x65\x64\x5b\x65\x6e\x61\x62\x6c\x65\x64\x3d\x22\x74\x72\527\x2e\x30\x20\x23\x65\x36\x65\x36\x65\x36\x29\x3b\x0a\x20\x20\x20\
528\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x33\x33\x33\x33\x33\x33\x3b\
529\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x63\x6f\x6c\x6f\
530\x72\x3a\x20\x23\x39\x39\x39\x39\x39\x39\x3b\x0a\x7d\x0a\x0a\x51\
531\x50\x75\x73\x68\x42\x75\x74\x74\x6f\x6e\x3a\x68\x6f\x76\x65\x72\
532\x5b\x73\x65\x63\x6f\x6e\x64\x61\x72\x79\x3d\x22\x74\x72\x75\x65\
533\x22\x5d\x20\x7b\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\
534\x75\x6e\x64\x3a\x20\x71\x6c\x69\x6e\x65\x61\x72\x67\x72\x61\x64\
535\x69\x65\x6e\x74\x28\x78\x31\x3a\x20\x30\x2c\x20\x79\x31\x3a\x20\
536\x30\x2c\x20\x78\x32\x3a\x20\x30\x2c\x20\x79\x32\x3a\x20\x31\x2c\
537\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x6f\x70\x3a\x20\x30\
538\x20\x23\x66\x66\x66\x66\x66\x66\x2c\x73\x74\x6f\x70\x3a\x20\x31\
539\x2e\x30\x20\x23\x65\x64\x65\x64\x65\x64\x29\x3b\x0a\x20\x20\x20\
540\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x33\x33\x33\x33\x33\x33\x3b\
541\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x63\x6f\x6c\x6f\
542\x72\x3a\x20\x23\x39\x39\x39\x39\x39\x39\x3b\x0a\x7d\x0a\x0a\x51\
543\x50\x75\x73\x68\x42\x75\x74\x74\x6f\x6e\x3a\x70\x72\x65\x73\x73\
544\x65\x64\x5b\x73\x65\x63\x6f\x6e\x64\x61\x72\x79\x3d\x22\x74\x72\
500\x75\x65\x22\x5d\x20\x7b\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\x67\545\x75\x65\x22\x5d\x20\x7b\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\x67\
501\x72\x6f\x75\x6e\x64\x3a\x20\x71\x6c\x69\x6e\x65\x61\x72\x67\x72\546\x72\x6f\x75\x6e\x64\x3a\x20\x71\x6c\x69\x6e\x65\x61\x72\x67\x72\
502\x61\x64\x69\x65\x6e\x74\x28\x78\x31\x3a\x20\x30\x2c\x20\x79\x31\547\x61\x64\x69\x65\x6e\x74\x28\x78\x31\x3a\x20\x30\x2c\x20\x79\x31\
503\x3a\x20\x30\x2c\x20\x78\x32\x3a\x20\x30\x2c\x20\x79\x32\x3a\x20\548\x3a\x20\x30\x2c\x20\x78\x32\x3a\x20\x30\x2c\x20\x79\x32\x3a\x20\
504\x31\x2c\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x6f\x70\x3a\549\x31\x2c\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x6f\x70\x3a\
505\x20\x30\x20\x23\x65\x34\x34\x65\x31\x39\x2c\x73\x74\x6f\x70\x3a\550\x20\x30\x20\x23\x64\x39\x64\x39\x64\x39\x2c\x73\x74\x6f\x70\x3a\
506\x20\x31\x2e\x30\x20\x23\x66\x65\x63\x66\x63\x32\x29\x3b\x0a\x20\551\x20\x31\x2e\x30\x20\x23\x66\x65\x66\x65\x66\x65\x29\x3b\x0a\x20\
507\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x72\x61\x64\x69\x75\x73\552\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x33\x33\x33\x33\x33\
508\x3a\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\553\x33\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x63\x6f\
509\x72\x2d\x73\x74\x79\x6c\x65\x3a\x20\x73\x6f\x6c\x69\x64\x3b\x0a\554\x6c\x6f\x72\x3a\x20\x23\x39\x39\x39\x39\x39\x39\x3b\x0a\x7d\x0a\
510\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\x67\x3a\x20\x36\x70\x78\555\x0a\x51\x50\x75\x73\x68\x42\x75\x74\x74\x6f\x6e\x5b\x65\x6e\x61\
511\x3b\x0a\x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x77\x68\x69\556\x62\x6c\x65\x64\x3d\x22\x66\x61\x6c\x73\x65\x22\x5d\x20\x7b\x0a\
512\x74\x65\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x63\
513\x6f\x6c\x6f\x72\x3a\x20\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x20\
514\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x77\x69\x64\x74\x68\x3a\
515\x20\x31\x70\x78\x3b\x0a\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\
516\x3a\x20\x31\x32\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x50\x75\x73\x68\
517\x42\x75\x74\x74\x6f\x6e\x5b\x65\x6e\x61\x62\x6c\x65\x64\x3d\x22\
518\x66\x61\x6c\x73\x65\x22\x5d\x20\x7b\x0a\x20\x20\x20\x20\x62\x61\
519\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\x71\x6c\x69\x6e\x65\x61\
520\x72\x67\x72\x61\x64\x69\x65\x6e\x74\x28\x78\x31\x3a\x20\x30\x2c\
521\x20\x79\x31\x3a\x20\x30\x2c\x20\x78\x32\x3a\x20\x30\x2c\x20\x79\
522\x32\x3a\x20\x31\x2c\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\
523\x6f\x70\x3a\x20\x30\x20\x23\x65\x61\x65\x61\x65\x61\x2c\x20\x73\
524\x74\x6f\x70\x3a\x20\x31\x2e\x30\x20\x23\x63\x61\x63\x61\x63\x61\
525\x29\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x72\x61\
526\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\
527\x6f\x72\x64\x65\x72\x2d\x73\x74\x79\x6c\x65\x3a\x20\x73\x6f\x6c\
528\x69\x64\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\x67\x3a\
529\x20\x36\x70\x78\x3b\x0a\x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\
530\x20\x23\x35\x39\x35\x39\x35\x39\x3b\x0a\x20\x20\x20\x20\x62\x6f\
531\x72\x64\x65\x72\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x39\x33\x39\
532\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\
533\x77\x69\x64\x74\x68\x3a\x20\x31\x70\x78\x3b\x0a\x20\x20\x20\x20\
534\x68\x65\x69\x67\x68\x74\x3a\x20\x31\x32\x70\x78\x3b\x0a\x7d\x0a\
535\x0a\x51\x50\x75\x73\x68\x42\x75\x74\x74\x6f\x6e\x23\x68\x65\x6c\
536\x70\x5f\x62\x75\x74\x74\x6f\x6e\x20\x7b\x0a\x20\x20\x20\x20\x62\
537\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\x74\x72\x61\x6e\x73\
538\x70\x61\x72\x65\x6e\x74\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\
539\x65\x72\x3a\x20\x6e\x6f\x6e\x65\x3b\x0a\x20\x20\x20\x20\x63\x6f\
540\x6c\x6f\x72\x3a\x20\x77\x68\x69\x74\x65\x3b\x0a\x20\x20\x20\x20\
541\x68\x65\x69\x67\x68\x74\x3a\x20\x32\x30\x70\x78\x3b\x0a\x20\x20\
542\x20\x20\x74\x65\x78\x74\x2d\x64\x65\x63\x6f\x72\x61\x74\x69\x6f\
543\x6e\x3a\x20\x75\x6e\x64\x65\x72\x6c\x69\x6e\x65\x3b\x0a\x20\x20\
544\x20\x20\x70\x61\x64\x64\x69\x6e\x67\x3a\x20\x30\x70\x78\x3b\x0a\
545\x7d\x0a\x0a\x51\x50\x75\x73\x68\x42\x75\x74\x74\x6f\x6e\x23\x65\
546\x78\x70\x6c\x6f\x72\x65\x5f\x66\x6f\x6c\x64\x65\x72\x5f\x62\x75\
547\x74\x74\x6f\x6e\x20\x7b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\
548\x72\x3a\x20\x6e\x6f\x6e\x65\x3b\x0a\x20\x20\x20\x20\x62\x61\x63\
549\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\x6e\x6f\x6e\x65\x3b\x0a\x20\
550\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x35\x39\x35\x39\x35\
551\x39\x3b\x0a\x7d\x0a\x0a\x51\x50\x75\x73\x68\x42\x75\x74\x74\x6f\
552\x6e\x23\x61\x64\x64\x5f\x66\x6f\x6c\x64\x65\x72\x5f\x62\x75\x74\
553\x74\x6f\x6e\x20\x7b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\
554\x67\x3a\x20\x35\x70\x78\x3b\x0a\x7d\x0a\x0a\x47\x6f\x54\x6f\x57\
555\x65\x62\x42\x75\x74\x74\x6f\x6e\x23\x73\x68\x61\x72\x65\x5f\x70\
556\x75\x62\x6c\x69\x73\x68\x5f\x62\x75\x74\x74\x6f\x6e\x20\x7b\x0a\
557\x20\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\557\x20\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\
558\x74\x72\x61\x6e\x73\x70\x61\x72\x65\x6e\x74\x3b\x0a\x20\x20\x20\558\x71\x6c\x69\x6e\x65\x61\x72\x67\x72\x61\x64\x69\x65\x6e\x74\x28\
559\x20\x62\x6f\x72\x64\x65\x72\x3a\x20\x6e\x6f\x6e\x65\x3b\x0a\x20\559\x78\x31\x3a\x20\x30\x2c\x20\x79\x31\x3a\x20\x30\x2c\x20\x78\x32\
560\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x64\x64\x34\x38\x31\560\x3a\x20\x30\x2c\x20\x79\x32\x3a\x20\x31\x2c\x0a\x20\x20\x20\x20\
561\x34\x3b\x0a\x20\x20\x20\x20\x74\x65\x78\x74\x2d\x64\x65\x63\x6f\561\x20\x20\x20\x20\x73\x74\x6f\x70\x3a\x20\x30\x20\x23\x65\x61\x65\
562\x72\x61\x74\x69\x6f\x6e\x3a\x20\x75\x6e\x64\x65\x72\x6c\x69\x6e\562\x61\x65\x61\x2c\x20\x73\x74\x6f\x70\x3a\x20\x31\x2e\x30\x20\x23\
563\x65\x3b\x0a\x7d\x0a\x0a\x51\x54\x61\x62\x42\x61\x72\x3a\x3a\x74\563\x63\x61\x63\x61\x63\x61\x29\x3b\x0a\x20\x20\x20\x20\x63\x6f\x6c\
564\x61\x62\x20\x7b\x0a\x20\x20\x20\x20\x68\x65\x69\x67\x68\x74\x3a\564\x6f\x72\x3a\x20\x23\x35\x39\x35\x39\x35\x39\x3b\x0a\x20\x20\x20\
565\x20\x31\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\565\x20\x62\x6f\x72\x64\x65\x72\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\
566\x3a\x20\x23\x33\x33\x33\x33\x33\x33\x3b\x0a\x20\x20\x20\x20\x62\566\x39\x33\x39\x33\x38\x39\x3b\x0a\x7d\x0a\x0a\x51\x50\x75\x73\x68\
567\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x2d\x63\x6f\x6c\x6f\x72\x3a\567\x42\x75\x74\x74\x6f\x6e\x23\x68\x65\x6c\x70\x5f\x62\x75\x74\x74\
568\x20\x23\x65\x34\x65\x30\x64\x64\x3b\x0a\x20\x20\x20\x20\x62\x6f\568\x6f\x6e\x20\x7b\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\
569\x72\x64\x65\x72\x2d\x74\x6f\x70\x2d\x6c\x65\x66\x74\x2d\x72\x61\569\x75\x6e\x64\x3a\x20\x74\x72\x61\x6e\x73\x70\x61\x72\x65\x6e\x74\
570\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\570\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x3a\x20\x6e\x6f\
571\x6f\x72\x64\x65\x72\x2d\x74\x6f\x70\x2d\x72\x69\x67\x68\x74\x2d\571\x6e\x65\x3b\x0a\x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x77\
572\x72\x61\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\572\x68\x69\x74\x65\x3b\x0a\x20\x20\x20\x20\x74\x65\x78\x74\x2d\x64\
573\x20\x62\x6f\x72\x64\x65\x72\x2d\x73\x74\x79\x6c\x65\x3a\x20\x73\573\x65\x63\x6f\x72\x61\x74\x69\x6f\x6e\x3a\x20\x75\x6e\x64\x65\x72\
574\x6f\x6c\x69\x64\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\574\x6c\x69\x6e\x65\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\
575\x67\x3a\x20\x31\x32\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\575\x67\x3a\x20\x30\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x50\x75\x73\x68\
576\x64\x65\x72\x2d\x74\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\576\x42\x75\x74\x74\x6f\x6e\x23\x65\x78\x70\x6c\x6f\x72\x65\x5f\x66\
577\x39\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\577\x6f\x6c\x64\x65\x72\x5f\x62\x75\x74\x74\x6f\x6e\x20\x7b\x0a\x20\
578\x65\x72\x2d\x72\x69\x67\x68\x74\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\578\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x3a\x20\x6e\x6f\x6e\x65\x3b\
579\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\579\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\
580\x64\x65\x72\x2d\x6c\x65\x66\x74\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\580\x20\x6e\x6f\x6e\x65\x3b\x0a\x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\
581\x77\x68\x69\x74\x65\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\581\x3a\x20\x23\x35\x39\x35\x39\x35\x39\x3b\x0a\x20\x20\x20\x20\x70\
582\x72\x2d\x62\x6f\x74\x74\x6f\x6d\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\582\x61\x64\x64\x69\x6e\x67\x2d\x6c\x65\x66\x74\x3a\x20\x31\x30\x70\
583\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\583\x78\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\x67\x2d\x72\
584\x64\x65\x72\x2d\x77\x69\x64\x74\x68\x3a\x20\x31\x70\x78\x3b\x0a\584\x69\x67\x68\x74\x3a\x20\x31\x30\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\
585\x7d\x0a\x0a\x51\x54\x61\x62\x42\x61\x72\x3a\x3a\x74\x61\x62\x3a\585\x50\x75\x73\x68\x42\x75\x74\x74\x6f\x6e\x23\x74\x77\x69\x74\x74\
586\x73\x65\x6c\x65\x63\x74\x65\x64\x20\x7b\x0a\x20\x20\x20\x20\x62\586\x65\x72\x5f\x62\x75\x74\x74\x6f\x6e\x2c\x0a\x51\x50\x75\x73\x68\
587\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x2d\x63\x6f\x6c\x6f\x72\x3a\587\x42\x75\x74\x74\x6f\x6e\x23\x66\x61\x63\x65\x62\x6f\x6f\x6b\x5f\
588\x20\x77\x68\x69\x74\x65\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\588\x62\x75\x74\x74\x6f\x6e\x20\x7b\x0a\x20\x20\x20\x20\x62\x6f\x72\
589\x65\x72\x2d\x74\x6f\x70\x2d\x6c\x65\x66\x74\x2d\x72\x61\x64\x69\589\x64\x65\x72\x3a\x20\x6e\x6f\x6e\x65\x3b\x0a\x7d\x0a\x0a\x47\x6f\
590\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\590\x54\x6f\x57\x65\x62\x42\x75\x74\x74\x6f\x6e\x23\x73\x68\x61\x72\
591\x64\x65\x72\x2d\x74\x6f\x70\x2d\x72\x69\x67\x68\x74\x2d\x72\x61\591\x65\x5f\x70\x75\x62\x6c\x69\x73\x68\x5f\x62\x75\x74\x74\x6f\x6e\
592\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\592\x20\x7b\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\
593\x6f\x72\x64\x65\x72\x2d\x73\x74\x79\x6c\x65\x3a\x20\x73\x6f\x6c\593\x64\x3a\x20\x74\x72\x61\x6e\x73\x70\x61\x72\x65\x6e\x74\x3b\x0a\
594\x69\x64\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\x67\x3a\594\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x3a\x20\x6e\x6f\x6e\x65\
595\x20\x31\x32\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\595\x3b\x0a\x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x64\x64\
596\x72\x2d\x74\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x39\x33\596\x34\x38\x31\x34\x3b\x0a\x20\x20\x20\x20\x74\x65\x78\x74\x2d\x64\
597\x39\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\597\x65\x63\x6f\x72\x61\x74\x69\x6f\x6e\x3a\x20\x75\x6e\x64\x65\x72\
598\x2d\x72\x69\x67\x68\x74\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x39\598\x6c\x69\x6e\x65\x3b\x0a\x7d\x0a\x0a\x51\x54\x61\x62\x42\x61\x72\
599\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\599\x3a\x3a\x74\x61\x62\x20\x7b\x0a\x20\x20\x20\x20\x68\x65\x69\x67\
600\x72\x2d\x6c\x65\x66\x74\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x77\x68\600\x68\x74\x3a\x20\x31\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x63\x6f\
601\x69\x74\x65\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\601\x6c\x6f\x72\x3a\x20\x23\x33\x33\x33\x33\x33\x33\x3b\x0a\x20\x20\
602\x62\x6f\x74\x74\x6f\x6d\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x77\x68\602\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x2d\x63\x6f\x6c\
603\x69\x74\x65\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\603\x6f\x72\x3a\x20\x23\x65\x34\x65\x30\x64\x64\x3b\x0a\x20\x20\x20\
604\x77\x69\x64\x74\x68\x3a\x20\x31\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\604\x20\x62\x6f\x72\x64\x65\x72\x2d\x74\x6f\x70\x2d\x6c\x65\x66\x74\
605\x54\x61\x62\x42\x61\x72\x3a\x3a\x74\x61\x62\x3a\x66\x69\x72\x73\605\x2d\x72\x61\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\x20\x20\
606\x74\x3a\x21\x73\x65\x6c\x65\x63\x74\x65\x64\x20\x7b\x0a\x20\x20\606\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x74\x6f\x70\x2d\x72\x69\x67\
607\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x6c\x65\x66\x74\x2d\x63\x6f\607\x68\x74\x2d\x72\x61\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\
608\x6c\x6f\x72\x3a\x20\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\608\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x73\x74\x79\x6c\x65\
609\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x6c\x65\x66\x74\x2d\x63\x6f\609\x3a\x20\x73\x6f\x6c\x69\x64\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\
610\x6c\x6f\x72\x3a\x20\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x7d\x0a\610\x64\x69\x6e\x67\x3a\x20\x31\x32\x70\x78\x3b\x0a\x20\x20\x20\x20\
611\x0a\x51\x54\x61\x62\x42\x61\x72\x3a\x3a\x74\x61\x62\x3a\x66\x69\611\x62\x6f\x72\x64\x65\x72\x2d\x74\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\
612\x72\x73\x74\x3a\x73\x65\x6c\x65\x63\x74\x65\x64\x20\x7b\x0a\x20\612\x3a\x20\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\x62\
613\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x6c\x65\x66\x74\x2d\x63\613\x6f\x72\x64\x65\x72\x2d\x72\x69\x67\x68\x74\x2d\x63\x6f\x6c\x6f\
614\x6f\x6c\x6f\x72\x3a\x20\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x7d\614\x72\x3a\x20\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\
615\x0a\x0a\x51\x54\x61\x62\x42\x61\x72\x3a\x3a\x74\x61\x62\x3a\x6d\615\x62\x6f\x72\x64\x65\x72\x2d\x6c\x65\x66\x74\x2d\x63\x6f\x6c\x6f\
616\x69\x64\x64\x6c\x65\x3a\x21\x73\x65\x6c\x65\x63\x74\x65\x64\x20\616\x72\x3a\x20\x77\x68\x69\x74\x65\x3b\x0a\x20\x20\x20\x20\x62\x6f\
617\x72\x64\x65\x72\x2d\x62\x6f\x74\x74\x6f\x6d\x2d\x63\x6f\x6c\x6f\
618\x72\x3a\x20\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\
619\x62\x6f\x72\x64\x65\x72\x2d\x77\x69\x64\x74\x68\x3a\x20\x31\x70\
620\x78\x3b\x0a\x7d\x0a\x0a\x51\x54\x61\x62\x42\x61\x72\x3a\x3a\x74\
621\x61\x62\x3a\x73\x65\x6c\x65\x63\x74\x65\x64\x20\x7b\x0a\x20\x20\
622\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x2d\x63\x6f\x6c\
623\x6f\x72\x3a\x20\x77\x68\x69\x74\x65\x3b\x0a\x20\x20\x20\x20\x62\
624\x6f\x72\x64\x65\x72\x2d\x74\x6f\x70\x2d\x6c\x65\x66\x74\x2d\x72\
625\x61\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\
626\x62\x6f\x72\x64\x65\x72\x2d\x74\x6f\x70\x2d\x72\x69\x67\x68\x74\
627\x2d\x72\x61\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\x20\x20\
628\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x73\x74\x79\x6c\x65\x3a\x20\
629\x73\x6f\x6c\x69\x64\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\
630\x6e\x67\x3a\x20\x31\x32\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\x6f\
631\x72\x64\x65\x72\x2d\x74\x6f\x70\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\
632\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\
633\x64\x65\x72\x2d\x72\x69\x67\x68\x74\x2d\x63\x6f\x6c\x6f\x72\x3a\
634\x20\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\x62\x6f\
635\x72\x64\x65\x72\x2d\x6c\x65\x66\x74\x2d\x63\x6f\x6c\x6f\x72\x3a\
636\x20\x77\x68\x69\x74\x65\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\
637\x65\x72\x2d\x62\x6f\x74\x74\x6f\x6d\x2d\x63\x6f\x6c\x6f\x72\x3a\
638\x20\x77\x68\x69\x74\x65\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\
639\x65\x72\x2d\x77\x69\x64\x74\x68\x3a\x20\x31\x70\x78\x3b\x0a\x7d\
640\x0a\x0a\x51\x54\x61\x62\x42\x61\x72\x3a\x3a\x74\x61\x62\x3a\x66\
641\x69\x72\x73\x74\x3a\x21\x73\x65\x6c\x65\x63\x74\x65\x64\x20\x7b\
642\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x6c\x65\x66\x74\
643\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x39\x33\x39\x33\x38\x39\x3b\
644\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x6c\x65\x66\x74\
645\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x39\x33\x39\x33\x38\x39\x3b\
646\x0a\x7d\x0a\x0a\x51\x54\x61\x62\x42\x61\x72\x3a\x3a\x74\x61\x62\
647\x3a\x66\x69\x72\x73\x74\x3a\x73\x65\x6c\x65\x63\x74\x65\x64\x20\
617\x7b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x6c\x65\x66\648\x7b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x6c\x65\x66\
618\x74\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x65\x34\x65\x30\x64\x64\649\x74\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x39\x33\x39\x33\x38\x39\
619\x3b\x0a\x7d\x0a\x0a\x51\x54\x61\x62\x42\x61\x72\x3a\x3a\x74\x61\650\x3b\x0a\x7d\x0a\x0a\x51\x54\x61\x62\x42\x61\x72\x3a\x3a\x74\x61\
620\x62\x3a\x68\x6f\x76\x65\x72\x20\x7b\x0a\x20\x20\x20\x20\x62\x61\651\x62\x3a\x6d\x69\x64\x64\x6c\x65\x3a\x21\x73\x65\x6c\x65\x63\x74\
621\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\x71\x6c\x69\x6e\x65\x61\652\x65\x64\x20\x7b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\
622\x72\x67\x72\x61\x64\x69\x65\x6e\x74\x28\x78\x31\x3a\x20\x30\x2c\653\x6c\x65\x66\x74\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x65\x34\x65\
623\x20\x79\x31\x3a\x20\x30\x2c\x20\x78\x32\x3a\x20\x30\x2c\x20\x79\654\x30\x64\x64\x3b\x0a\x7d\x0a\x0a\x51\x54\x61\x62\x42\x61\x72\x3a\
624\x32\x3a\x20\x31\x2c\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\655\x3a\x74\x61\x62\x3a\x68\x6f\x76\x65\x72\x20\x7b\x0a\x20\x20\x20\
625\x6f\x70\x3a\x20\x30\x20\x23\x66\x61\x66\x61\x66\x61\x2c\x20\x73\656\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\x23\x66\x36\
626\x74\x6f\x70\x3a\x20\x30\x2e\x34\x20\x23\x66\x34\x66\x34\x66\x34\657\x66\x36\x66\x36\x3b\x0a\x20\x20\x20\x20\x74\x65\x78\x74\x2d\x64\
627\x2c\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x73\x74\x6f\x70\x3a\x20\658\x65\x63\x6f\x72\x61\x74\x69\x6f\x6e\x3a\x20\x75\x6e\x64\x65\x72\
628\x30\x2e\x35\x20\x23\x65\x37\x65\x37\x65\x37\x2c\x20\x73\x74\x6f\659\x6c\x69\x6e\x65\x3b\x0a\x7d\x0a\x0a\x51\x54\x61\x62\x42\x61\x72\
629\x70\x3a\x20\x31\x2e\x30\x20\x23\x66\x61\x66\x61\x66\x61\x29\x3b\660\x3a\x3a\x74\x61\x62\x3a\x6c\x61\x73\x74\x3a\x21\x73\x65\x6c\x65\
630\x0a\x20\x20\x20\x20\x74\x65\x78\x74\x2d\x64\x65\x63\x6f\x72\x61\661\x63\x74\x65\x64\x20\x7b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\
631\x74\x69\x6f\x6e\x3a\x20\x75\x6e\x64\x65\x72\x6c\x69\x6e\x65\x3b\662\x72\x2d\x6c\x65\x66\x74\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x65\
632\x0a\x7d\x0a\x0a\x51\x54\x61\x62\x42\x61\x72\x3a\x3a\x74\x61\x62\663\x34\x65\x30\x64\x64\x3b\x0a\x7d\x0a\x0a\x51\x54\x61\x62\x57\x69\
633\x3a\x6c\x61\x73\x74\x3a\x21\x73\x65\x6c\x65\x63\x74\x65\x64\x20\664\x64\x67\x65\x74\x20\x7b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\
634\x7b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x6c\x65\x66\665\x72\x2d\x62\x6f\x74\x74\x6f\x6d\x2d\x6c\x65\x66\x74\x2d\x72\x61\
635\x74\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x65\x34\x65\x30\x64\x64\666\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\
636\x3b\x0a\x7d\x0a\x0a\x51\x54\x61\x62\x57\x69\x64\x67\x65\x74\x20\667\x6f\x72\x64\x65\x72\x2d\x62\x6f\x74\x74\x6f\x6d\x2d\x72\x69\x67\
668\x68\x74\x2d\x72\x61\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\
669\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x73\x74\x79\x6c\x65\
670\x3a\x20\x73\x6f\x6c\x69\x64\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\
671\x64\x69\x6e\x67\x3a\x20\x31\x30\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\
672\x54\x61\x62\x57\x69\x64\x67\x65\x74\x3a\x3a\x70\x61\x6e\x65\x20\
637\x7b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x62\x6f\x74\673\x7b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x62\x6f\x74\
638\x74\x6f\x6d\x2d\x6c\x65\x66\x74\x2d\x72\x61\x64\x69\x75\x73\x3a\674\x74\x6f\x6d\x2d\x6c\x65\x66\x74\x2d\x72\x61\x64\x69\x75\x73\x3a\
639\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\675\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\
640\x2d\x62\x6f\x74\x74\x6f\x6d\x2d\x72\x69\x67\x68\x74\x2d\x72\x61\676\x2d\x62\x6f\x74\x74\x6f\x6d\x2d\x72\x69\x67\x68\x74\x2d\x72\x61\
641\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\677\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\
642\x6f\x72\x64\x65\x72\x2d\x73\x74\x79\x6c\x65\x3a\x20\x73\x6f\x6c\678\x6f\x72\x64\x65\x72\x2d\x74\x6f\x70\x2d\x72\x69\x67\x68\x74\x2d\
643\x69\x64\x3b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\x67\x3a\679\x72\x61\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\
644\x20\x31\x30\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x54\x61\x62\x57\x69\680\x20\x62\x6f\x72\x64\x65\x72\x2d\x73\x74\x79\x6c\x65\x3a\x20\x73\
645\x64\x67\x65\x74\x3a\x3a\x70\x61\x6e\x65\x20\x7b\x0a\x20\x20\x20\681\x6f\x6c\x69\x64\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\
646\x20\x62\x6f\x72\x64\x65\x72\x2d\x62\x6f\x74\x74\x6f\x6d\x2d\x6c\682\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x39\x33\x39\x33\x38\x39\x3b\
647\x65\x66\x74\x2d\x72\x61\x64\x69\x75\x73\x3a\x20\x35\x70\x78\x3b\683\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x77\x69\x64\x74\
648\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x62\x6f\x74\x74\684\x68\x3a\x20\x31\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\
649\x6f\x6d\x2d\x72\x69\x67\x68\x74\x2d\x72\x61\x64\x69\x75\x73\x3a\685\x67\x72\x6f\x75\x6e\x64\x3a\x20\x77\x68\x69\x74\x65\x3b\x0a\x20\
650\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\686\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x74\x6f\x70\x3a\x20\x32\
651\x2d\x74\x6f\x70\x2d\x72\x69\x67\x68\x74\x2d\x72\x61\x64\x69\x75\687\x70\x78\x20\x73\x6f\x6c\x69\x64\x20\x77\x68\x69\x74\x65\x3b\x0a\
652\x73\x3a\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\688\x7d\x0a\x0a\x51\x47\x72\x6f\x75\x70\x42\x6f\x78\x20\x7b\x0a\x20\
653\x65\x72\x2d\x73\x74\x79\x6c\x65\x3a\x20\x73\x6f\x6c\x69\x64\x3b\689\x20\x20\x20\x70\x61\x64\x64\x69\x6e\x67\x2d\x74\x6f\x70\x3a\x20\
654\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x63\x6f\x6c\x6f\690\x33\x30\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\
655\x72\x3a\x20\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\691\x3a\x20\x6e\x6f\x6e\x65\x3b\x0a\x20\x20\x20\x20\x6d\x61\x72\x67\
656\x62\x6f\x72\x64\x65\x72\x2d\x77\x69\x64\x74\x68\x3a\x20\x31\x70\692\x69\x6e\x2d\x74\x6f\x70\x3a\x20\x31\x65\x78\x3b\x0a\x7d\x0a\x0a\
657\x78\x3b\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\693\x51\x47\x72\x6f\x75\x70\x42\x6f\x78\x3a\x3a\x74\x69\x74\x6c\x65\
658\x64\x3a\x20\x77\x68\x69\x74\x65\x3b\x0a\x20\x20\x20\x20\x62\x6f\694\x20\x7b\x0a\x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x33\
659\x72\x64\x65\x72\x2d\x74\x6f\x70\x3a\x20\x32\x70\x78\x20\x73\x6f\695\x33\x33\x33\x33\x33\x3b\x0a\x20\x20\x20\x20\x66\x6f\x6e\x74\x3a\
660\x6c\x69\x64\x20\x77\x68\x69\x74\x65\x3b\x0a\x7d\x0a\x0a\x51\x47\696\x20\x62\x6f\x6c\x64\x20\x31\x35\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\
661\x72\x6f\x75\x70\x42\x6f\x78\x20\x7b\x0a\x20\x20\x20\x20\x70\x61\697\x47\x72\x6f\x75\x70\x42\x6f\x78\x23\x70\x72\x6f\x66\x69\x6c\x65\
662\x64\x64\x69\x6e\x67\x2d\x74\x6f\x70\x3a\x20\x33\x30\x70\x78\x3b\698\x2c\x0a\x51\x47\x72\x6f\x75\x70\x42\x6f\x78\x23\x73\x65\x72\x76\
663\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x3a\x20\x6e\x6f\x6e\699\x69\x63\x65\x73\x20\x7b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\
664\x65\x3b\x0a\x20\x20\x20\x20\x6d\x61\x72\x67\x69\x6e\x2d\x74\x6f\700\x6e\x67\x2d\x6c\x65\x66\x74\x3a\x20\x35\x70\x78\x3b\x0a\x7d\x0a\
665\x70\x3a\x20\x31\x65\x78\x3b\x0a\x7d\x0a\x0a\x51\x47\x72\x6f\x75\701\x0a\x51\x4c\x69\x73\x74\x57\x69\x64\x67\x65\x74\x20\x7b\x0a\x20\
666\x70\x42\x6f\x78\x3a\x3a\x74\x69\x74\x6c\x65\x20\x7b\x0a\x20\x20\702\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\x23\
667\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x33\x33\x33\x33\x33\x33\703\x66\x37\x66\x36\x66\x35\x3b\x0a\x20\x20\x20\x20\x61\x6c\x74\x65\
668\x3b\x0a\x20\x20\x20\x20\x66\x6f\x6e\x74\x3a\x20\x62\x6f\x6c\x64\704\x72\x6e\x61\x74\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\
669\x20\x31\x35\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x47\x72\x6f\x75\x70\705\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x65\x66\x65\x64\x65\x63\x3b\
670\x42\x6f\x78\x23\x70\x72\x6f\x66\x69\x6c\x65\x2c\x0a\x51\x47\x72\706\x0a\x7d\x0a\x0a\x51\x4c\x69\x73\x74\x57\x69\x64\x67\x65\x74\x23\
671\x6f\x75\x70\x42\x6f\x78\x23\x73\x65\x72\x76\x69\x63\x65\x73\x20\707\x6c\x69\x73\x74\x5f\x64\x65\x76\x69\x63\x65\x73\x3a\x3a\x69\x74\
672\x7b\x0a\x20\x20\x20\x20\x70\x61\x64\x64\x69\x6e\x67\x2d\x6c\x65\708\x65\x6d\x20\x7b\x0a\x20\x20\x20\x20\x6d\x69\x6e\x2d\x68\x65\x69\
673\x66\x74\x3a\x20\x35\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x4c\x69\x73\709\x67\x68\x74\x3a\x20\x34\x38\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x4c\
674\x74\x57\x69\x64\x67\x65\x74\x20\x7b\x0a\x20\x20\x20\x20\x62\x61\710\x61\x62\x65\x6c\x23\x6f\x74\x68\x65\x72\x5f\x64\x65\x76\x69\x63\
675\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x20\x23\x66\x37\x66\x36\x66\711\x65\x73\x5f\x6c\x61\x62\x65\x6c\x20\x7b\x0a\x20\x20\x20\x20\x66\
676\x35\x3b\x0a\x20\x20\x20\x20\x61\x6c\x74\x65\x72\x6e\x61\x74\x65\712\x6f\x6e\x74\x3a\x20\x62\x6f\x6c\x64\x20\x31\x36\x70\x78\x3b\x0a\
677\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x2d\x63\x6f\x6c\x6f\713\x7d\x0a\x0a\x51\x4c\x61\x62\x65\x6c\x23\x70\x65\x72\x63\x65\x6e\
678\x72\x3a\x20\x23\x65\x66\x65\x64\x65\x63\x3b\x0a\x7d\x0a\x0a\x51\714\x74\x61\x67\x65\x5f\x75\x73\x61\x67\x65\x5f\x6c\x61\x62\x65\x6c\
679\x4c\x69\x73\x74\x57\x69\x64\x67\x65\x74\x23\x6c\x69\x73\x74\x5f\715\x20\x7b\x0a\x20\x20\x20\x20\x6d\x61\x72\x67\x69\x6e\x2d\x74\x6f\
680\x64\x65\x76\x69\x63\x65\x73\x3a\x3a\x69\x74\x65\x6d\x20\x7b\x0a\716\x70\x3a\x20\x2d\x34\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x4c\x61\x62\
681\x20\x20\x20\x20\x6d\x69\x6e\x2d\x68\x65\x69\x67\x68\x74\x3a\x20\717\x65\x6c\x23\x66\x6f\x6c\x6c\x6f\x77\x5f\x75\x73\x5f\x6c\x61\x62\
682\x34\x38\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x4c\x61\x62\x65\x6c\x23\718\x65\x6c\x20\x7b\x0a\x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\
683\x6f\x74\x68\x65\x72\x5f\x64\x65\x76\x69\x63\x65\x73\x5f\x6c\x61\719\x77\x68\x69\x74\x65\x3b\x0a\x7d\x0a\x0a\x51\x41\x62\x73\x74\x72\
684\x62\x65\x6c\x20\x7b\x0a\x20\x20\x20\x20\x66\x6f\x6e\x74\x3a\x20\720\x61\x63\x74\x49\x74\x65\x6d\x56\x69\x65\x77\x20\x7b\x0a\x20\x20\
685\x62\x6f\x6c\x64\x20\x31\x36\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x4c\721\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x73\x74\x79\x6c\x65\x3a\x20\
686\x61\x62\x65\x6c\x23\x70\x65\x72\x63\x65\x6e\x74\x61\x67\x65\x5f\722\x73\x6f\x6c\x69\x64\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\
687\x75\x73\x61\x67\x65\x5f\x6c\x61\x62\x65\x6c\x20\x7b\x0a\x20\x20\723\x72\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x38\x39\x38\x39\x38\x39\
688\x20\x20\x6d\x61\x72\x67\x69\x6e\x2d\x74\x6f\x70\x3a\x20\x2d\x34\724\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x74\x6f\x70\
689\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x4c\x61\x62\x65\x6c\x23\x66\x6f\725\x2d\x77\x69\x64\x74\x68\x3a\x20\x31\x70\x78\x3b\x0a\x20\x20\x20\
690\x6c\x6c\x6f\x77\x5f\x75\x73\x5f\x6c\x61\x62\x65\x6c\x20\x7b\x0a\726\x20\x62\x6f\x72\x64\x65\x72\x2d\x62\x6f\x74\x74\x6f\x6d\x2d\x77\
691\x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x77\x68\x69\x74\x65\727\x69\x64\x74\x68\x3a\x20\x31\x70\x78\x3b\x0a\x20\x20\x20\x20\x61\
692\x3b\x0a\x7d\x0a\x0a\x51\x46\x72\x61\x6d\x65\x23\x66\x72\x6d\x5f\728\x6c\x74\x65\x72\x6e\x61\x74\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\
693\x62\x6f\x78\x20\x7b\x20\x2f\x2a\x20\x74\x68\x65\x20\x6c\x6f\x61\729\x75\x6e\x64\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x66\x37\x66\x36\
694\x64\x69\x6e\x67\x6f\x76\x65\x72\x6c\x61\x79\x20\x66\x72\x61\x6d\730\x66\x35\x3b\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\x75\
695\x65\x20\x2a\x2f\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\731\x6e\x64\x3a\x20\x23\x65\x66\x65\x64\x65\x63\x3b\x0a\x7d\x0a\
696\x75\x6e\x64\x3a\x20\x23\x66\x66\x66\x66\x66\x66\x3b\x0a\x20\x20\
697\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x72\x61\x64\x69\x75\x73\x3a\
698\x20\x35\x70\x78\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\
699\x2d\x73\x74\x79\x6c\x65\x3a\x20\x73\x6f\x6c\x69\x64\x3b\x0a\x20\
700\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x63\x6f\x6c\x6f\x72\x3a\
701\x20\x23\x39\x33\x39\x33\x38\x39\x3b\x0a\x20\x20\x20\x20\x62\x6f\
702\x72\x64\x65\x72\x2d\x77\x69\x64\x74\x68\x3a\x20\x31\x70\x78\x3b\
703\x0a\x20\x20\x20\x20\x63\x6f\x6c\x6f\x72\x3a\x20\x77\x68\x69\x74\
704\x65\x3b\x0a\x20\x20\x20\x20\x6d\x69\x6e\x2d\x68\x65\x69\x67\x68\
705\x74\x3a\x20\x31\x30\x30\x70\x78\x3b\x0a\x7d\x0a\x0a\x51\x41\x62\
706\x73\x74\x72\x61\x63\x74\x49\x74\x65\x6d\x56\x69\x65\x77\x20\x7b\
707\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\x73\x74\x79\x6c\
708\x65\x3a\x20\x73\x6f\x6c\x69\x64\x3b\x0a\x20\x20\x20\x20\x62\x6f\
709\x72\x64\x65\x72\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x33\x33\x33\
710\x33\x33\x33\x3b\x0a\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\x2d\
711\x77\x69\x64\x74\x68\x3a\x20\x31\x70\x78\x3b\x0a\x20\x20\x20\x20\
712\x61\x6c\x74\x65\x72\x6e\x61\x74\x65\x2d\x62\x61\x63\x6b\x67\x72\
713\x6f\x75\x6e\x64\x2d\x63\x6f\x6c\x6f\x72\x3a\x20\x23\x65\x66\x65\
714\x64\x65\x63\x3b\x0a\x20\x20\x20\x20\x62\x61\x63\x6b\x67\x72\x6f\
715\x75\x6e\x64\x3a\x20\x23\x66\x37\x66\x36\x66\x35\x3b\x0a\x7d\x0a\
716\
717\x00\x00\x02\x6f\732\x00\x00\x02\x6f\
718\x89\733\x89\
719\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\734\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@@ -23296,85 +23311,80 @@
23296\xee\xee\xee\x93\x74\xd0\xf8\xf8\x78\xff\xac\x9e\xfb\xdf\x96\xbf\23311\xee\xee\xee\x93\x74\xd0\xf8\xf8\x78\xff\xac\x9e\xfb\xdf\x96\xbf\
23297\x02\x0c\x00\x57\x70\xa3\x5d\x63\x1a\xb1\xc1\x00\x00\x00\x00\x49\23312\x02\x0c\x00\x57\x70\xa3\x5d\x63\x1a\xb1\xc1\x00\x00\x00\x00\x49\
23298\x45\x4e\x44\xae\x42\x60\x82\23313\x45\x4e\x44\xae\x42\x60\x82\
23299\x00\x00\x04\xc5\23314\x00\x00\x02\x2a\
23300\x89\23315\x89\
23301\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\23316\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
23302\x00\x00\x20\x00\x00\x00\x20\x08\x06\x00\x00\x00\x73\x7a\x7a\xf4\23317\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
23303\x00\x00\x04\x8c\x49\x44\x41\x54\x78\xda\xbd\x97\xfb\x53\xd4\x65\23318\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\
23304\x14\xc6\xf9\x17\xfa\x4b\x6c\xa6\xdf\xba\x89\x9a\x19\x65\x99\x95\23319\x01\x00\x9a\x9c\x18\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
23305\x20\x97\xbd\x21\xec\x02\x8a\x28\x77\x58\x60\xc1\x26\xe9\x66\x4a\23320\x74\x77\x61\x72\x65\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\
23306\xa5\x21\x2c\xe2\x28\xb0\x0b\xbb\x2c\x2c\x94\x94\x46\x54\xde\xa6\23321\x65\x52\x65\x61\x64\x79\x71\xc9\x65\x3c\x00\x00\x01\xb7\x49\x44\
23307\x71\x72\xaa\xd1\x9a\x32\xa6\xa6\x7b\x43\x22\x0a\x22\x5e\xb6\xd3\23322\x41\x54\x78\xda\x94\x93\x4d\x2f\x03\x41\x18\xc7\x9f\x5d\xd3\xdd\
23308\x7b\x5e\xe6\xd9\xf7\xec\xee\x77\x1b\x7e\x92\x99\xcf\x9c\x73\x9e\23323\xb6\x0e\x5e\x83\x34\xc1\x88\x38\x38\x78\x8d\x44\x38\x38\x78\xb9\
23309\xf7\x5c\x1e\x96\x1f\x80\x0c\xfe\x7a\xe0\xa1\xba\x07\x15\xdd\x8a\23324\x8a\xc6\x55\x22\x1c\x5c\xc4\x37\x70\xf3\x2d\x48\x70\x90\x38\x96\
23310\xa9\xfb\x44\x37\xdf\x94\xc7\xa7\x8a\x9a\x22\x33\x7b\xbb\xce\xc4\23325\xc4\x89\xe8\xcd\x57\xd0\x92\x58\x2b\x95\x4a\x43\xd5\x5b\xed\xdb\
23311\xf6\xf5\x9e\xa7\xd7\x7b\xce\x5a\x82\xb7\xe4\xf8\xe6\x11\xcc\x40\23326\xec\xac\x99\xd9\x54\xad\x96\xf0\x5c\x9e\xcc\xf3\x3c\xff\xdf\xfc\
23312\x4b\x3f\xd7\xde\x7d\x36\xc6\xb7\xf8\xa6\x36\xc1\x6e\x0a\x1b\x87\23327\x67\x76\x47\x1a\x9b\x5b\x1f\x0c\x87\xa3\x09\x35\x1c\xc1\x8a\xa2\
23313\x67\xf6\x1c\xfc\x8c\x2a\x5f\x3d\x49\x1e\xdf\xfb\xe4\x6e\x19\x37\23328\xc2\x5f\xc2\xb6\x2d\xb0\x4c\x43\x37\xcd\xf7\x38\x52\xd5\x48\x62\
23314\xf8\xc6\xa9\xb4\xed\x04\xe0\x5a\x53\xd2\xfa\x01\x7a\x64\x2f\x47\23329\x76\x7a\x1c\x77\xb7\xb7\x82\x24\x49\x7f\x02\x78\x9e\x07\x57\x99\
23315\xdd\x27\x75\x68\x65\x8a\x8a\xf6\x09\x7a\xb9\xf3\x73\xe2\x9b\x7c\23330\x1c\x3e\x3c\x39\x4b\x20\x14\x52\x70\xbe\x60\x41\x4a\x4f\xc3\x7f\
23316\x3b\x83\x9d\x78\xdf\xfe\x24\x56\xbe\x77\x82\x3c\x6a\x69\x51\xcb\23331\xa2\xa5\x2e\x0a\x21\x25\x8c\x11\x80\x0c\xe9\x4c\x1e\x5c\xea\xfd\
23317\x18\x62\x0a\x9e\xb6\x84\x77\xe4\x8c\xec\x91\xd1\x32\xdf\xd9\xfe\23332\x0b\xf0\xf8\x62\x30\x27\x00\x88\x50\x0a\x96\xe3\xfe\x38\x38\x39\
23318\x21\x79\x0f\x4c\xc6\xf8\xf6\xb2\x81\x8e\x49\x72\xb7\x8e\xd3\xb6\23333\xda\x05\xcb\xf3\xc3\x50\x1b\x51\xc4\x3a\xbe\xb6\x2f\x32\x61\x12\
23319\xe6\x28\x15\x36\x8d\x6a\x8a\x7d\xe3\x32\x4f\x5b\xf3\x0c\x6a\x47\23334\xae\x45\xae\xeb\xb2\x05\xfd\x11\xf0\x55\xec\x0b\xcb\xb3\x5c\xcb\
23320\x7d\x98\x36\x97\xf9\x29\xcb\xd5\x41\x1b\x1c\xfb\xe9\x29\xa7\x89\23335\x00\xf4\x57\x40\x49\xbc\xba\x71\x04\xda\x6d\x21\xd0\xe3\x5a\xe1\
23321\xfc\x0e\xf8\x56\xf3\x3b\x53\x14\x37\x50\xbf\x7f\x92\x9c\x8d\x23\23336\x80\x1f\x86\xdb\xf9\x2d\x2e\x6f\x1e\x02\x6b\x24\xcb\x65\x07\x8a\
23322\x6a\xd9\x18\x88\xd7\x2e\xef\xe8\x72\x6c\x1c\xd5\x9a\xd6\x9b\xf4\23337\x2c\x81\xe9\x04\x01\xc9\xed\xa5\xcf\x4f\xc6\xe3\x78\x6b\xd1\xbf\
23323\xbb\x06\x7d\xb6\xda\x10\xd9\xab\x7b\xe9\xe4\xe9\xcb\x34\x7f\x73\23338\x93\xe5\x1d\x91\xa3\xa8\xa6\xe4\x80\x82\x2a\x68\x41\x80\xe3\x38\
23324\x89\x92\xbe\x30\xaf\x4c\x47\x75\xac\xdb\x77\xca\x18\xd8\xad\x7e\23339\x55\x9d\x94\xe6\x4a\x1a\x44\x98\x0d\x85\xd1\xec\x6f\x80\x89\xa5\
23325\x2e\x85\xde\xa8\xfa\x0e\x22\x88\x20\x9d\x9e\xa2\xb9\xbd\x41\x9a\23340\x5d\x91\x4f\x37\x17\x44\x9e\x5a\xd9\x0b\xf4\xb9\x86\x6b\x91\x69\
23326\x9b\xbf\x45\xf7\x62\xff\x5a\xc2\xbd\x12\x75\xd3\x18\x28\x7f\xe5\23341\x19\x0c\x6b\x40\xb1\xf8\x52\x75\x47\x87\xf8\x4e\x2a\xfa\xf5\x21\
23327\x04\x39\x1a\x46\x34\x05\xb5\x61\xe4\x29\x1a\x72\xbb\x3a\xc8\x51\23342\xe0\x5a\xd4\xd8\xde\x09\xb8\x7f\x08\x0a\xcd\x4f\xd5\x01\x0e\x11\
23328\x12\x9a\xb8\x48\xb7\xef\xc6\xd2\xc2\x33\x72\x87\xba\x69\x0c\x78\23343\xb9\x63\x60\x24\x50\xc7\xb1\x7a\xb8\x36\x18\x80\x58\x36\xb8\x66\
23329\x7c\x51\xdd\x90\x5f\x13\x52\x1f\xe5\x30\xf2\x15\xc1\xbd\xcc\xe5\23344\x11\xa2\x1e\x85\x7c\xd1\xaa\x00\x10\xe2\x03\x8c\x37\xe3\xb3\xd6\
23330\x2b\x7f\xd2\xe2\xd2\xbd\xb4\xa0\x0f\x33\xee\x96\xa8\x31\x50\xd4\23345\x54\xab\x0a\x0d\xe5\x77\x50\x03\x54\xbf\xbb\xcf\xe1\xbe\x58\x8c\
23331\x14\x55\xae\x86\x35\xb9\x55\x43\x0c\x6a\x35\x10\x96\x1a\x62\x4a\23346\xfd\xdb\x0d\x15\x00\x55\xf5\x5f\xe8\x4c\x6f\x5b\xd9\x15\x7b\x8d\
23332\xef\xc2\xe2\xdd\x04\x3e\x3a\xf3\x23\x15\xb7\x8c\xe9\x37\x20\xe7\23347\x5a\x36\x0b\x12\xb1\x75\x44\x8d\xe7\xf8\x85\xa6\x25\x52\x9a\x8e\
23333\x5c\x8d\x11\x63\x20\xaf\x7a\x90\xb2\x77\x07\x28\xb7\x7a\x08\xa4\23348\xab\x1d\xa1\xa7\x35\x27\xf2\x41\xf2\x3c\x50\x97\xd9\xc6\x9e\xf9\
23334\xab\x11\x53\xb8\xbe\x70\x27\x01\x47\x43\x58\xce\x6a\xb6\x56\x0d\23349\x1a\xff\x10\x60\x00\x7c\x3b\xda\x5b\x6c\x8a\x7e\xa0\x00\x00\x00\
23335\xc6\x73\x75\xd3\x18\xc8\xa9\x1c\xa4\x97\x2a\x06\xe2\x64\xef\x0a\23350\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
23336\x92\xd4\x38\x67\x64\xbe\xa5\x22\x80\x5a\xc7\xd9\x1b\x77\x24\xe8\23351\x00\x00\x02\x2a\
23337\x03\x16\x3b\x84\x81\x2d\xbb\x02\x04\x5e\x28\xef\x03\xa8\xd5\xb1\23352\x89\
23338\x81\x84\x77\xe4\x99\xb9\x6f\x68\x56\xe7\xbc\x46\x33\xd7\x6e\x4b\23353\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
23339\x58\xe3\x37\x60\xb5\xdf\x18\x78\x7e\xfb\x71\x02\x2f\xee\x1c\x88\23354\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\
23340\xb3\x79\x47\x1f\x34\xe4\xa8\x75\xfc\xeb\xea\xd2\x4a\xc1\x4e\xcc\23355\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\x0b\x13\
23341\x2b\x03\xfd\xc6\xc0\x73\xa5\xc7\x48\xa1\x1e\xfb\xd4\xa1\x7e\xc4\23356\x01\x00\x9a\x9c\x18\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\
23342\x04\x36\x95\x1d\xe7\x1e\xd4\x3a\xff\x7d\xe6\xd6\x8a\xf8\xf8\xfc\23357\x74\x77\x61\x72\x65\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\
23343\x34\x66\xc4\x4e\x61\x60\x93\x72\xb5\xb1\xe4\x28\x71\x44\xce\xc4\23358\x65\x52\x65\x61\x64\x79\x71\xc9\x65\x3c\x00\x00\x01\xb7\x49\x44\
23344\xf5\xb2\x63\x96\x6f\xbf\xfe\xbd\xb8\x22\x4e\x9d\x9b\x96\xfb\x80\23359\x41\x54\x78\xda\x94\x93\x4d\x2f\x03\x41\x18\xc7\x9f\x5d\xd3\xdd\
23345\x31\xf0\xac\x72\xf6\xb4\xbb\x57\xc3\x39\x40\x8d\xb7\xe4\xfa\xe7\23360\xb6\x0e\x5e\x83\x34\xc1\x88\x38\x38\x78\x8d\x44\x38\x38\x78\xb9\
23346\x3f\x6e\xae\x88\x43\x81\x0b\x72\x1f\x72\x63\x60\xa3\xe7\x28\x31\23361\x8a\xc6\x55\x22\x1c\x5c\xc4\x37\x70\xf3\x2d\x48\x70\x90\x38\x96\
23347\x59\x45\x3d\x1c\x01\x34\x19\xd3\xe6\xd3\xbf\x2d\x48\xfe\x6f\x1f\23362\xc4\x89\xe8\xcd\x57\xd0\x92\x58\x2b\x95\x4a\x43\xd5\x5b\xed\xdb\
23348\x74\x63\xe0\xc9\x42\x3f\x31\x1b\xb6\xf5\x10\xf2\xac\xe2\x5e\xe4\23363\xec\xac\x99\xd9\x54\xad\x96\xf0\x5c\x9e\xcc\xf3\x3c\xff\xdf\xfc\
23349\x96\x35\x34\xe8\x3f\xfc\x32\x2f\x51\x47\x8e\x24\xcf\xe0\x06\xe6\23364\x67\x76\x47\x1a\x9b\x5b\x1f\x0c\x87\xa3\x09\x35\x1c\xc1\x8a\xa2\
23350\x8c\x01\x16\x25\x4f\x38\xbb\x64\x8e\x88\x05\x96\xef\xdf\xfd\x74\23365\xc2\x5f\xc2\xb6\x2d\xb0\x4c\x43\x37\xcd\xf7\x38\x52\xd5\x48\x62\
23351\x43\xc2\x3a\x90\xbd\x32\x1a\x03\xeb\x5d\x7e\x5a\xef\xf4\xd3\x5a\23366\x76\x7a\x1c\x77\xb7\xb7\x82\x24\x49\x7f\x02\x78\x9e\x07\x57\x99\
23352\xfb\x61\x0d\xd7\x0c\x72\x51\x5b\x46\xe6\xd2\xf4\x75\x89\xd5\x3c\23367\x1c\x3e\x3c\x39\x4b\x20\x14\x52\x70\xbe\x60\x41\x4a\x4f\xc3\x7f\
23353\x34\x60\x0c\xac\x73\x74\x11\x93\x59\xf0\x9e\x8e\x6b\x0a\x3a\x51\23368\xa2\xa5\x2e\x0a\x21\x25\x8c\x11\x80\x0c\xe9\x4c\x1e\x5c\xea\xfd\
23354\xa7\x65\x9d\xdd\xf4\x33\x5f\x5f\x99\x93\xe0\x0d\x7d\xa8\x01\xd7\23369\x0b\xf0\xf8\x62\x30\x27\x00\x88\x50\x0a\x96\xe3\xfe\x38\x38\x39\
23355\xd2\xc0\x61\x7a\x3c\xef\x20\xad\xb1\x75\x72\x94\x40\xd7\xc8\x9e\23370\xda\x05\xcb\xf3\xc3\x50\x1b\x51\xc4\x3a\xbe\xb6\x2f\x32\x61\x12\
23356\xcc\xfc\x43\x1a\xd4\x17\xbf\x9f\x93\xa0\x37\xed\x0e\x75\xd3\x18\23371\xae\x45\xae\xeb\xb2\x05\xfd\x11\xf0\x55\xec\x0b\xcb\xb3\x5c\xcb\
23357\x58\x6b\x7b\x97\x1e\xc9\x7e\x8b\x1e\x56\x3c\x96\xdb\xa1\x79\x34\23372\x00\xf4\x57\x40\x49\xbc\xba\x71\x04\xda\x6d\x21\xd0\xe3\x5a\xe1\
23358\xe7\x80\x8e\xd0\x00\x34\x44\xe4\x17\xbe\xbd\x26\x91\xbd\x96\x33\23373\x80\x1f\x86\xdb\xf9\x2d\x2e\x6f\x1e\x02\x6b\x24\xcb\x65\x07\x8a\
23359\xab\xf3\x3a\x8c\x81\x12\xdf\x08\x39\xeb\x87\xc8\x51\x37\xc8\xe8\23374\x2c\x81\xe9\x04\x01\xc9\xed\xa5\xcf\x4f\xc6\xe3\x78\x6b\xd1\xbf\
23360\xbc\xb0\x31\x2c\x35\x5d\x23\xba\x94\x8e\x1a\x7c\x71\x69\x56\xa2\23375\x93\xe5\x1d\x91\xa3\xa8\xa6\xe4\x80\x82\x2a\x68\x41\x80\xe3\x38\
23361\x7a\x42\x5a\x77\x35\x84\xb8\x37\xa5\xbf\xb8\x29\x9c\x68\xc0\x5e\23376\x55\x9d\x94\xe6\x4a\x1a\x44\x98\x0d\x85\xd1\xec\x6f\x80\x89\xa5\
23362\x1b\x04\x3c\xc4\xa4\x68\x88\x6c\x0c\x35\x38\xf7\xcd\x55\x09\xf7\23377\x5d\x91\x4f\x37\x17\x44\x9e\x5a\xd9\x0b\xf4\xb9\x86\x6b\x91\x69\
23363\xa0\xdf\x72\x9f\x5b\x1a\x70\x37\x87\xa9\xa0\x26\x40\x0e\x35\x04\23378\x19\x0c\x6b\x40\xb1\xf8\x52\x75\x47\x87\xf8\x4e\x2a\xfa\xf5\x21\
23364\xd8\x25\x34\x8e\xf6\xe5\x5a\x63\x53\x0b\xa0\xe3\xed\xf4\x57\xff\23379\xe0\x5a\xd4\xd8\xde\x09\xb8\x7f\x08\x0a\xcd\x4f\xd5\x01\x0e\x11\
23365\x48\xe4\x2c\xf2\x84\x9a\x6f\x9a\x3f\xc9\xd8\x40\xf5\x00\xe5\x57\23380\xb9\x63\x60\x24\x50\xc7\xb1\x7a\xb8\x36\x18\x80\x58\x36\xb8\x66\
23366\x25\xa0\x9c\x0e\x26\xd4\xb6\xea\xa0\xd6\xa0\xcb\xf8\xe9\x97\x33\23381\x11\xa2\x1e\x85\x7c\xd1\xaa\x00\x10\xe2\x03\x8c\x37\xe3\xb3\xd6\
23367\x92\xe4\x59\xb5\x3f\x00\x4d\x7f\x73\xa5\xbe\x88\xf8\x04\xbc\xe1\23382\x54\xab\x0a\x0d\xe5\x77\x50\x03\x54\xbf\xbb\xcf\xe1\xbe\x58\x8c\
23368\x18\x7f\x2c\x79\x7c\x44\x35\x59\xc1\x6f\xc0\x56\x93\x5c\x07\x13\23383\xfd\xdb\x0d\x15\x00\x55\xf5\x5f\xe8\x4c\x6f\x5b\xd9\x15\x7b\x8d\
23369\xea\x7c\x75\x4c\xcc\x20\x07\xca\x40\x90\x3c\xcd\xc3\x31\x18\xe8\23384\x5a\x36\x0b\x12\xb1\x75\x44\x8d\xe7\xf8\x85\xa6\x25\x52\x9a\x8e\
23370\x7e\xc6\xed\x9f\x2d\x6d\x1b\x25\x67\xc3\x10\xe5\xab\x65\xcc\xd6\23385\xab\x1d\xa1\xa7\x35\x27\xf2\x41\xf2\x3c\x50\x97\xd9\xc6\x9e\xf9\
23371\xca\x7e\xa0\x86\x02\x3a\xe2\x2d\x57\x2d\x41\x2d\xfa\x64\x8f\xcc\23386\x1a\xff\x10\x60\x00\x7c\x3b\xda\x5b\x6c\x8a\x7e\xa0\x00\x00\x00\
23372\xc5\x5b\x40\xdf\xe0\x5b\x7c\x13\xff\x9a\xad\xd2\xbf\x11\x3d\xfe\23387\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
23373\xd9\xe2\xe6\x50\x6c\xc7\x9e\x28\x95\xb6\x46\x88\x23\x80\x86\x68\
23374\x45\x59\xdb\x88\xe9\x11\x71\xbb\x3a\x26\xb5\x12\x5f\x24\xc6\xb7\
23375\xf8\x26\xdf\xce\x20\x22\x98\xb8\xdf\xff\x9e\xaf\xe2\xdb\xff\x01\
23376\x95\x7c\xeb\x7f\xbd\xd5\xeb\x77\x00\x00\x00\x00\x49\x45\x4e\x44\
23377\xae\x42\x60\x82\
23378\x00\x00\x01\x68\23388\x00\x00\x01\x68\
23379\x89\23389\x89\
23380\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\23390\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@@ -23468,21 +23478,23 @@
23468"23478"
2346923479
23470qt_resource_struct = "\23480qt_resource_struct = "\
23471\x00\x00\x00\x00\x00\x02\x00\x00\x00\x0e\x00\x00\x00\x01\23481\x00\x00\x00\x00\x00\x02\x00\x00\x00\x10\x00\x00\x00\x01\
23472\x00\x00\x01\xf0\x00\x00\x00\x00\x00\x01\x00\x05\xb2\x73\23482\x00\x00\x01\xf0\x00\x00\x00\x00\x00\x01\x00\x05\xb2\xec\
23473\x00\x00\x00\x96\x00\x00\x00\x00\x00\x01\x00\x00\x0c\xe5\23483\x00\x00\x00\x96\x00\x00\x00\x00\x00\x01\x00\x00\x0c\xe5\
23474\x00\x00\x01\x3e\x00\x00\x00\x00\x00\x01\x00\x00\x17\xfe\23484\x00\x00\x01\x3e\x00\x00\x00\x00\x00\x01\x00\x00\x17\x85\
23475\x00\x00\x00\x7e\x00\x00\x00\x00\x00\x01\x00\x00\x09\xc8\23485\x00\x00\x00\x7e\x00\x00\x00\x00\x00\x01\x00\x00\x09\xc8\
23476\x00\x00\x01\xb4\x00\x00\x00\x00\x00\x01\x00\x05\xaa\x1e\23486\x00\x00\x01\xb4\x00\x00\x00\x00\x00\x01\x00\x05\xab\x04\
23477\x00\x00\x00\x4a\x00\x00\x00\x00\x00\x01\x00\x00\x04\x92\23487\x00\x00\x00\x4a\x00\x00\x00\x00\x00\x01\x00\x00\x04\x92\
23478\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\23488\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
23479\x00\x00\x00\xee\x00\x00\x00\x00\x00\x01\x00\x00\x11\xb2\23489\x00\x00\x00\xee\x00\x00\x00\x00\x00\x01\x00\x00\x11\xb2\
23490\x00\x00\x00\xee\x00\x00\x00\x00\x00\x01\x00\x00\x14\x06\
23480\x00\x00\x00\x30\x00\x00\x00\x00\x00\x01\x00\x00\x01\xd5\23491\x00\x00\x00\x30\x00\x00\x00\x00\x00\x01\x00\x00\x01\xd5\
23481\x00\x00\x00\xd4\x00\x00\x00\x00\x00\x01\x00\x00\x0f\x83\23492\x00\x00\x00\xd4\x00\x00\x00\x00\x00\x01\x00\x00\x0f\x83\
23482\x00\x00\x01\x0a\x00\x00\x00\x00\x00\x01\x00\x00\x16\xd3\23493\x00\x00\x01\x0a\x00\x00\x00\x00\x00\x01\x00\x00\x16\x5a\
23483\x00\x00\x01\xd2\x00\x00\x00\x00\x00\x01\x00\x05\xad\xaa\23494\x00\x00\x01\xd2\x00\x00\x00\x00\x00\x01\x00\x05\xb0\xbe\
23484\x00\x00\x01\x5e\x00\x00\x00\x00\x00\x01\x00\x00\x2a\xb3\23495\x00\x00\x01\xd2\x00\x00\x00\x00\x00\x01\x00\x05\xae\x90\
23485\x00\x00\x01\x96\x00\x00\x00\x00\x00\x01\x00\x00\x2d\x26\23496\x00\x00\x01\x5e\x00\x00\x00\x00\x00\x01\x00\x00\x2b\x99\
23497\x00\x00\x01\x96\x00\x00\x00\x00\x00\x01\x00\x00\x2e\x0c\
23486"23498"
2348723499
23488def qInitResources():23500def qInitResources():
2348923501
=== modified file 'ubuntuone/controlpanel/gui/qt/ui/loadingoverlay_ui.py'
--- ubuntuone/controlpanel/gui/qt/ui/loadingoverlay_ui.py 2011-08-12 19:12:08 +0000
+++ ubuntuone/controlpanel/gui/qt/ui/loadingoverlay_ui.py 2011-08-25 19:40:18 +0000
@@ -2,7 +2,7 @@
22
3# Form implementation generated from reading ui file 'data/qt/loadingoverlay.ui'3# Form implementation generated from reading ui file 'data/qt/loadingoverlay.ui'
4#4#
5# Created: Fri Aug 12 15:07:18 20115# Created: Thu Aug 25 15:26:50 2011
6# by: PyQt4 UI code generator 4.8.36# by: PyQt4 UI code generator 4.8.3
7#7#
8# WARNING! All changes made in this file will be lost!8# WARNING! All changes made in this file will be lost!
99
=== modified file 'ubuntuone/controlpanel/gui/qt/ui/mainwindow_ui.py'
--- ubuntuone/controlpanel/gui/qt/ui/mainwindow_ui.py 2011-08-12 19:12:08 +0000
+++ ubuntuone/controlpanel/gui/qt/ui/mainwindow_ui.py 2011-08-25 19:40:18 +0000
@@ -2,7 +2,7 @@
22
3# Form implementation generated from reading ui file 'data/qt/mainwindow.ui'3# Form implementation generated from reading ui file 'data/qt/mainwindow.ui'
4#4#
5# Created: Fri Aug 12 15:07:18 20115# Created: Thu Aug 25 15:26:50 2011
6# by: PyQt4 UI code generator 4.8.36# by: PyQt4 UI code generator 4.8.3
7#7#
8# WARNING! All changes made in this file will be lost!8# WARNING! All changes made in this file will be lost!
@@ -48,7 +48,7 @@
48 QtCore.QMetaObject.connectSlotsByName(MainWindow)48 QtCore.QMetaObject.connectSlotsByName(MainWindow)
4949
50 def retranslateUi(self, MainWindow):50 def retranslateUi(self, MainWindow):
51 MainWindow.setWindowTitle(_('Ubuntu One Control Panel'))51 MainWindow.setWindowTitle(_('Ubuntu One'))
5252
53from ubuntuone.controlpanel.gui.qt.controlpanel import ControlPanel53from ubuntuone.controlpanel.gui.qt.controlpanel import ControlPanel
54import images_rc54import images_rc
5555
=== modified file 'ubuntuone/controlpanel/gui/qt/ui/preferences_ui.py'
--- ubuntuone/controlpanel/gui/qt/ui/preferences_ui.py 2011-08-12 19:12:08 +0000
+++ ubuntuone/controlpanel/gui/qt/ui/preferences_ui.py 2011-08-25 19:40:18 +0000
@@ -2,7 +2,7 @@
22
3# Form implementation generated from reading ui file 'data/qt/preferences.ui'3# Form implementation generated from reading ui file 'data/qt/preferences.ui'
4#4#
5# Created: Fri Aug 12 15:07:18 20115# Created: Thu Aug 25 15:26:50 2011
6# by: PyQt4 UI code generator 4.8.36# by: PyQt4 UI code generator 4.8.3
7#7#
8# WARNING! All changes made in this file will be lost!8# WARNING! All changes made in this file will be lost!
@@ -90,6 +90,7 @@
90 self.apply_changes_button.setObjectName(_fromUtf8("apply_changes_button"))90 self.apply_changes_button.setObjectName(_fromUtf8("apply_changes_button"))
91 self.horizontalLayout.addWidget(self.apply_changes_button)91 self.horizontalLayout.addWidget(self.apply_changes_button)
92 self.restore_defaults_button = QtGui.QPushButton(Form)92 self.restore_defaults_button = QtGui.QPushButton(Form)
93 self.restore_defaults_button.setProperty(_fromUtf8("secondary"), True)
93 self.restore_defaults_button.setObjectName(_fromUtf8("restore_defaults_button"))94 self.restore_defaults_button.setObjectName(_fromUtf8("restore_defaults_button"))
94 self.horizontalLayout.addWidget(self.restore_defaults_button)95 self.horizontalLayout.addWidget(self.restore_defaults_button)
95 self.verticalLayout.addLayout(self.horizontalLayout)96 self.verticalLayout.addLayout(self.horizontalLayout)
@@ -98,7 +99,7 @@
98 QtCore.QMetaObject.connectSlotsByName(Form)99 QtCore.QMetaObject.connectSlotsByName(Form)
99100
100 def retranslateUi(self, Form):101 def retranslateUi(self, Form):
101 self.bandwidth_settings.setTitle(_('Bandwidth settings'))102 self.bandwidth_settings.setTitle(_('Bandwidth Settings'))
102 self.limit_uploads_checkbox.setText(_('Limit upload speed to'))103 self.limit_uploads_checkbox.setText(_('Limit upload speed to'))
103 self.kbps_label_1.setText(_('Kilobits per second'))104 self.kbps_label_1.setText(_('Kilobits per second'))
104 self.limit_downloads_checkbox.setText(_('Limit download speed to'))105 self.limit_downloads_checkbox.setText(_('Limit download speed to'))
105106
=== modified file 'ubuntuone/controlpanel/login_client.py'
--- ubuntuone/controlpanel/login_client.py 2011-07-22 21:26:48 +0000
+++ ubuntuone/controlpanel/login_client.py 2011-08-25 19:40:18 +0000
@@ -18,14 +18,15 @@
1818
19"""Client to access Ubuntu One credentials."""19"""Client to access Ubuntu One credentials."""
2020
21# pylint: disable=E0611, F0401
22from ubuntuone.platform.credentials import CredentialsManagementTool
23# pylint: enable=E0611, F0401
24
2125
22def get_sso_proxy():26def get_sso_proxy():
23 """Return a login client."""27 """Return a login client."""
24 # No name 'credentials' in module 'ubuntuone.platform'28 result = CredentialsManagementTool()
25 # Reimport 'credentials' (imported line 22)29 return result
26 # pylint: disable=E0611,W0404
27 from ubuntuone.platform import credentials
28 return credentials.CredentialsManagementTool()
2930
3031
31def get_credentials():32def get_credentials():
@@ -38,3 +39,15 @@
38 """Clear the credentials for Ubuntu One."""39 """Clear the credentials for Ubuntu One."""
39 proxy = get_sso_proxy()40 proxy = get_sso_proxy()
40 return proxy.clear_credentials()41 return proxy.clear_credentials()
42
43
44def login(*args, **kwargs):
45 """Get the credentials for Ubuntu One offering the user to login."""
46 proxy = get_sso_proxy()
47 return proxy.login(*args, **kwargs)
48
49
50def register(*args, **kwargs):
51 """Get the credentials for Ubuntu One offering the user to register."""
52 proxy = get_sso_proxy()
53 return proxy.register(*args, **kwargs)
4154
=== modified file 'ubuntuone/controlpanel/sd_client/__init__.py'
--- ubuntuone/controlpanel/sd_client/__init__.py 2011-07-22 21:26:48 +0000
+++ ubuntuone/controlpanel/sd_client/__init__.py 2011-08-25 19:40:18 +0000
@@ -21,221 +21,186 @@
21import sys21import sys
22import warnings22import warnings
2323
24# pylint: disable=E0611
25from ubuntuone.platform import tools
26# pylint: enable=E0611
24from ubuntuone.controlpanel.logger import setup_logging27from ubuntuone.controlpanel.logger import setup_logging
2528
26# pylint: disable=W0611
27
2829
29logger = setup_logging('sd_client')30logger = setup_logging('sd_client')
3031
3132
32def get_syncdaemon_tool():33class SyncDaemonClient(object):
33 """Get a proxy for the SyncDaemonTool."""34 """An abstraction to SyncDaemonTool."""
34 # Reimport 'SyncDaemonTool'35
35 # No name 'tools' in module 'ubuntuone.platform'36 def __init__(self):
36 # pylint: disable=W0404, E061137 """Get a proxy for the SyncDaemonTool."""
37 from ubuntuone.platform import tools38 self.status_changed_handler = None
38 proxy = tools.SyncDaemonTool()39 self.proxy = tools.SyncDaemonTool()
39 return proxy40
4041 def get_throttling_limits(self):
4142 """Get the speed limits from the syncdaemon."""
42def get_throttling_limits():43 return self.proxy.get_throttling_limits()
43 """Get the speed limits from the syncdaemon."""44
44 return get_syncdaemon_tool().get_throttling_limits()45 def set_throttling_limits(self, limits):
4546 """Set the speed limits on the syncdaemon."""
4647 dload = int(limits["download"])
47def set_throttling_limits(limits):48 uload = int(limits["upload"])
48 """Set the speed limits on the syncdaemon."""49 return self.proxy.set_throttling_limits(dload, uload)
49 dload = int(limits["download"])50
50 uload = int(limits["upload"])51 def bandwidth_throttling_enabled(self):
51 return get_syncdaemon_tool().set_throttling_limits(dload, uload)52 """Get the state of throttling in the syncdaemon."""
5253 return self.proxy.is_throttling_enabled()
5354
54def bandwidth_throttling_enabled():55 def enable_bandwidth_throttling(self):
55 """Get the state of throttling in the syncdaemon."""56 """Enable the speed limits in the syncdaemon."""
56 return get_syncdaemon_tool().is_throttling_enabled()57 return self.proxy.enable_throttling(True)
5758
5859 def disable_bandwidth_throttling(self):
59def enable_bandwidth_throttling():60 """Disable the speed limits in the syncdaemon."""
60 """Enable the speed limits in the syncdaemon."""61 return self.proxy.enable_throttling(False)
61 return get_syncdaemon_tool().enable_throttling(True)62
6263 def autoconnect_enabled(self):
6364 """Get the state of autoconnect in the syncdaemon."""
64def disable_bandwidth_throttling():65 return self.proxy.is_autoconnect_enabled()
65 """Disable the speed limits in the syncdaemon."""66
66 return get_syncdaemon_tool().enable_throttling(False)67 def enable_autoconnect(self):
6768 """Enable autoconnect in the syncdaemon."""
6869 return self.proxy.enable_autoconnect(True)
69def autoconnect_enabled():70
70 """Get the state of autoconnect in the syncdaemon."""71 def disable_autoconnect(self):
71 return get_syncdaemon_tool().is_autoconnect_enabled()72 """Disable autoconnect in the syncdaemon."""
7273 return self.proxy.enable_autoconnect(False)
7374
74def enable_autoconnect():75 def show_all_notifications_enabled(self):
75 """Enable autoconnect in the syncdaemon."""76 """Get the state of show_all_notifications in the syncdaemon."""
76 return get_syncdaemon_tool().enable_autoconnect(True)77 return self.proxy.is_show_all_notifications_enabled()
7778
7879 def enable_show_all_notifications(self):
79def disable_autoconnect():80 """Enable show_all_notifications in the syncdaemon."""
80 """Disable autoconnect in the syncdaemon."""81 return self.proxy.enable_show_all_notifications(True)
81 return get_syncdaemon_tool().enable_autoconnect(False)82
8283 def disable_show_all_notifications(self):
8384 """Disable show_all_notifications in the syncdaemon."""
84def show_all_notifications_enabled():85 return self.proxy.enable_show_all_notifications(False)
85 """Get the state of show_all_notifications in the syncdaemon."""86
86 return get_syncdaemon_tool().is_show_all_notifications_enabled()87 def share_autosubscribe_enabled(self):
8788 """Get the state of share_autosubscribe in the syncdaemon."""
8889 return self.proxy.is_share_autosubscribe_enabled()
89def enable_show_all_notifications():90
90 """Enable show_all_notifications in the syncdaemon."""91 def enable_share_autosubscribe(self):
91 return get_syncdaemon_tool().enable_show_all_notifications(True)92 """Enable share_autosubscribe in the syncdaemon."""
9293 return self.proxy.enable_share_autosubscribe(True)
9394
94def disable_show_all_notifications():95 def disable_share_autosubscribe(self):
95 """Disable show_all_notifications in the syncdaemon."""96 """Disable share_autosubscribe in the syncdaemon."""
96 return get_syncdaemon_tool().enable_show_all_notifications(False)97 return self.proxy.enable_share_autosubscribe(False)
9798
9899 def udf_autosubscribe_enabled(self):
99def share_autosubscribe_enabled():100 """Get the state of udf_autosubscribe in the syncdaemon."""
100 """Get the state of share_autosubscribe in the syncdaemon."""101 return self.proxy.is_udf_autosubscribe_enabled()
101 return get_syncdaemon_tool().is_share_autosubscribe_enabled()102
102103 def enable_udf_autosubscribe(self):
103104 """Enable udf_autosubscribe in the syncdaemon."""
104def enable_share_autosubscribe():105 return self.proxy.enable_udf_autosubscribe(True)
105 """Enable share_autosubscribe in the syncdaemon."""106
106 return get_syncdaemon_tool().enable_share_autosubscribe(True)107 def disable_udf_autosubscribe(self):
107108 """Disable udf_autosubscribe in the syncdaemon."""
108109 return self.proxy.enable_udf_autosubscribe(False)
109def disable_share_autosubscribe():110
110 """Disable share_autosubscribe in the syncdaemon."""111 def get_root_dir(self):
111 return get_syncdaemon_tool().enable_share_autosubscribe(False)112 """Retrieve the root information from syncdaemon."""
112113 return self.proxy.get_root_dir()
113114
114def udf_autosubscribe_enabled():115 def get_shares_dir(self):
115 """Get the state of udf_autosubscribe in the syncdaemon."""116 """Retrieve the shares information from syncdaemon."""
116 return get_syncdaemon_tool().is_udf_autosubscribe_enabled()117 return self.proxy.get_shares_dir()
117118
118119 def get_shares_dir_link(self):
119def enable_udf_autosubscribe():120 """Retrieve the shares information from syncdaemon."""
120 """Enable udf_autosubscribe in the syncdaemon."""121 return self.proxy.get_shares_dir_link()
121 return get_syncdaemon_tool().enable_udf_autosubscribe(True)122
122123 def get_folders(self):
123124 """Retrieve the folders information from syncdaemon."""
124def disable_udf_autosubscribe():125 return self.proxy.get_folders()
125 """Disable udf_autosubscribe in the syncdaemon."""126
126 return get_syncdaemon_tool().enable_udf_autosubscribe(False)127 def create_folder(self, path):
127128 """Create a new folder through syncdaemon."""
128129 return self.proxy.create_folder(path)
129def get_root_dir():130
130 """Retrieve the root information from syncdaemon."""131 def subscribe_folder(self, folder_id):
131 return get_syncdaemon_tool().get_root_dir()132 """Subscribe to 'folder_id'."""
132133 return self.proxy.subscribe_folder(folder_id)
133134
134def get_shares_dir():135 def unsubscribe_folder(self, folder_id):
135 """Retrieve the shares information from syncdaemon."""136 """Unsubscribe 'folder_id'."""
136 return get_syncdaemon_tool().get_shares_dir()137 return self.proxy.unsubscribe_folder(folder_id)
137138
138139 def get_shares(self):
139def get_shares_dir_link():140 """Retrieve the shares information from syncdaemon."""
140 """Retrieve the shares information from syncdaemon."""141 return self.proxy.get_shares()
141 return get_syncdaemon_tool().get_shares_dir_link()142
142143 def subscribe_share(self, share_id):
143144 """Subscribe to 'share_id'."""
144def get_folders():145 return self.proxy.subscribe_share(share_id)
145 """Retrieve the folders information from syncdaemon."""146
146 return get_syncdaemon_tool().get_folders()147 def unsubscribe_share(self, share_id):
147148 """Unsubscribe 'share_id'."""
148149 return self.proxy.unsubscribe_share(share_id)
149def create_folder(path):150
150 """Create a new folder through syncdaemon."""151 def get_current_status(self):
151 return get_syncdaemon_tool().create_folder(path)152 """Retrieve the current status from syncdaemon."""
152153 return self.proxy.get_status()
153154
154def subscribe_folder(folder_id):155 def file_sync_enabled(self):
155 """Subscribe to 'folder_id'."""156 """Get if file sync service is enabled."""
156 return get_syncdaemon_tool().subscribe_folder(folder_id)157 return self.proxy.is_files_sync_enabled()
157158
158159 def enable_file_sync(self):
159def unsubscribe_folder(folder_id):160 """Enable the file sync service."""
160 """Unsubscribe 'folder_id'."""161 return self.proxy.enable_files_sync(True)
161 return get_syncdaemon_tool().unsubscribe_folder(folder_id)162
162163 def disable_file_sync(self):
163164 """Enable the file sync service."""
164def get_shares():165 return self.proxy.enable_files_sync(False)
165 """Retrieve the shares information from syncdaemon."""166
166 return get_syncdaemon_tool().get_shares()167 def files_sync_enabled(self):
167168 """Get if file sync service is enabled."""
168169 warnings.warn('use file_sync_enabled instead', DeprecationWarning)
169def subscribe_share(share_id):170 return self.file_sync_enabled()
170 """Subscribe to 'share_id'."""171
171 return get_syncdaemon_tool().subscribe_share(share_id)172 def set_files_sync_enabled(self, enabled):
172173 """Set the file sync service to be 'enabled'."""
173174 warnings.warn('use {enable/disable}_file_sync instead',
174def unsubscribe_share(share_id):175 DeprecationWarning)
175 """Unsubscribe 'share_id'."""176 if enabled:
176 return get_syncdaemon_tool().unsubscribe_share(share_id)177 return self.enable_file_sync()
177178 else:
178179 return self.disable_file_sync()
179def get_current_status():180
180 """Retrieve the current status from syncdaemon."""181 def connect_file_sync(self):
181 return get_syncdaemon_tool().get_status()182 """Connect the file sync service."""
182183 return self.proxy.connect()
183184
184def file_sync_enabled():185 def disconnect_file_sync(self):
185 """Get if file sync service is enabled."""186 """Disconnect the file sync service."""
186 return get_syncdaemon_tool().is_files_sync_enabled()187 return self.proxy.disconnect()
187188
188189 def start_file_sync(self):
189def enable_file_sync():190 """Start the file sync service."""
190 """Enable the file sync service."""191 return self.proxy.start()
191 return get_syncdaemon_tool().enable_files_sync(True)192
192193 def stop_file_sync(self):
193194 """Stop the file sync service."""
194def disable_file_sync():195 return self.proxy.quit()
195 """Enable the file sync service."""196
196 return get_syncdaemon_tool().enable_files_sync(False)197 def set_status_changed_handler(self, handler):
197
198
199def files_sync_enabled():
200 """Get if file sync service is enabled."""
201 warnings.warn('use file_sync_enabled instead', DeprecationWarning)
202 return file_sync_enabled()
203
204
205def set_files_sync_enabled(enabled):
206 """Set the file sync service to be 'enabled'."""
207 warnings.warn('use {enable/disable}_file_sync instead', DeprecationWarning)
208 if enabled:
209 return enable_file_sync()
210 else:
211 return disable_file_sync()
212
213
214def connect_file_sync():
215 """Connect the file sync service."""
216 return get_syncdaemon_tool().connect()
217
218
219def disconnect_file_sync():
220 """Disconnect the file sync service."""
221 return get_syncdaemon_tool().disconnect()
222
223
224def start_file_sync():
225 """Start the file sync service."""
226 return get_syncdaemon_tool().start()
227
228
229def stop_file_sync():
230 """Stop the file sync service."""
231 return get_syncdaemon_tool().quit()
232
233
234if sys.platform.startswith("linux"):
235 from ubuntuone.controlpanel.sd_client.linux \
236 import set_status_changed_handler
237else:
238
239 def set_status_changed_handler(handler):
240 """Set the status handler function."""198 """Set the status handler function."""
241 return get_syncdaemon_tool().set_status_changed_handler(handler)199 self.status_changed_handler = handler
200 if sys.platform.startswith("linux"):
201 # pylint: disable=W0404
202 from ubuntuone.controlpanel.sd_client import linux
203 result = linux.set_status_changed_handler(handler)
204 else:
205 result = self.proxy.set_status_changed_handler(handler)
206 return result
242207
=== modified file 'ubuntuone/controlpanel/tests/test_backend.py'
--- ubuntuone/controlpanel/tests/test_backend.py 2011-07-22 21:26:48 +0000
+++ ubuntuone/controlpanel/tests/test_backend.py 2011-08-25 19:40:18 +0000
@@ -66,18 +66,33 @@
66)66)
6767
6868
69# pylint: disable=E110169# pylint: disable=E1101, W0201, W0212
7070
7171
72class MockWebClient(object):72class CallRecorder(object):
73 """A class that records every call clients made to it."""
74
75 def __init__(self):
76 self._called = defaultdict(int)
77
78 def __getattribute__(self, attr_name):
79 """Override to we can record calls to members."""
80 result = super(CallRecorder, self).__getattribute__(attr_name)
81 if attr_name != '_called':
82 called = super(CallRecorder, self).__getattribute__('_called')
83 called[attr_name] += 1
84 return result
85
86
87class MockWebClient(CallRecorder):
73 """A mock webclient."""88 """A mock webclient."""
7489
75 failure = False
76 results = {}
77
78 def __init__(self, get_credentials):90 def __init__(self, get_credentials):
79 """Initialize this mock instance."""91 """Initialize this mock instance."""
92 super(MockWebClient, self).__init__()
80 self.get_credentials = get_credentials93 self.get_credentials = get_credentials
94 self.failure = False
95 self.results = {}
8196
82 def call_api(self, method):97 def call_api(self, method):
83 """Get a given url from the webservice."""98 """Get a given url from the webservice."""
@@ -90,42 +105,48 @@
90 return defer.succeed(result)105 return defer.succeed(result)
91106
92107
93class MockLoginClient(object):108class MockLoginClient(CallRecorder):
94 """A mock login_client module."""109 """A mock login_client module."""
95110
96 creds = TOKEN111 def __init__(self):
112 """Initialize this mock instance."""
113 super(MockLoginClient, self).__init__()
114 self.creds = TOKEN
97115
98 def get_credentials(self):116 def find_credentials(self):
99 """Return the mock credentials."""117 """Return the mock credentials."""
100 return defer.succeed(self.creds)118 return defer.succeed(self.creds)
101119
102 def clear_credentials(self):120 def clear_credentials(self):
103 """Clear the mock credentials."""121 """Clear the mock credentials."""
104 MockLoginClient.creds = None122 self.creds = None
105 return defer.succeed(None)123 return defer.succeed(None)
106124
107125
108class MockSDClient(object):126class MockSDClient(CallRecorder):
109 """A mock sd_client module."""127 """A mock sd_client module."""
110128
111 throttling = False129 def __init__(self):
112 show_all_notifications = True130 """Initialize this mock instance."""
113 autoconnect = True131 super(MockSDClient, self).__init__()
114 udf_autosubscribe = False132 self.throttling = False
115 share_autosubscribe = False133 self.show_all_notifications = True
116 limits = {"download": -1, "upload": -1}134 self.autoconnect = True
117 file_sync = True135 self.udf_autosubscribe = False
118 status = {136 self.share_autosubscribe = False
119 'name': 'TEST', 'queues': 'GORGEOUS', 'connection': '',137 self.limits = {"download": -1, "upload": -1}
120 'description': 'Some test state, nothing else.',138 self.file_sync = True
121 'is_error': '', 'is_connected': 'True', 'is_online': '',139 self.status = {
122 }140 'name': 'TEST', 'queues': 'GORGEOUS', 'connection': '',
123 status_changed_handler = None141 'description': 'Some test state, nothing else.',
124 subscribed_folders = []142 'is_error': '', 'is_connected': 'True', 'is_online': '',
125 subscribed_shares = []143 }
126 actions = []144 self.status_changed_handler = None
127 shares = []145 self.subscribed_folders = []
128 folders = []146 self.subscribed_shares = []
147 self.actions = []
148 self.shares = []
149 self.folders = []
129150
130 def get_throttling_limits(self):151 def get_throttling_limits(self):
131 """Return the sample speed limits."""152 """Return the sample speed limits."""
@@ -198,27 +219,27 @@
198219
199 def files_sync_enabled(self):220 def files_sync_enabled(self):
200 """Get if file sync service is enabled."""221 """Get if file sync service is enabled."""
201 return MockSDClient.file_sync222 return self.file_sync
202223
203 def set_files_sync_enabled(self, enabled):224 def set_files_sync_enabled(self, enabled):
204 """Set the file sync service to be 'enabled'."""225 """Set the file sync service to be 'enabled'."""
205 MockSDClient.file_sync = enabled226 self.file_sync = enabled
206227
207 def connect_file_sync(self):228 def connect_file_sync(self):
208 """Connect files service."""229 """Connect files service."""
209 MockSDClient.actions.append('connect')230 self.actions.append('connect')
210231
211 def disconnect_file_sync(self):232 def disconnect_file_sync(self):
212 """Disconnect file_sync service."""233 """Disconnect file_sync service."""
213 MockSDClient.actions.append('disconnect')234 self.actions.append('disconnect')
214235
215 def start_file_sync(self):236 def start_file_sync(self):
216 """Start the file_sync service."""237 """Start the file_sync service."""
217 MockSDClient.actions.append('start')238 self.actions.append('start')
218239
219 def stop_file_sync(self):240 def stop_file_sync(self):
220 """Stop the file_sync service."""241 """Stop the file_sync service."""
221 MockSDClient.actions.append('stop')242 self.actions.append('stop')
222243
223 def get_root_dir(self):244 def get_root_dir(self):
224 """Grab the root dir."""245 """Grab the root dir."""
@@ -234,31 +255,31 @@
234255
235 def get_folders(self):256 def get_folders(self):
236 """Grab list of folders."""257 """Grab list of folders."""
237 return MockSDClient.folders258 return self.folders
238259
239 def subscribe_folder(self, volume_id):260 def subscribe_folder(self, volume_id):
240 """Subcribe to 'volume_id'."""261 """Subcribe to 'volume_id'."""
241 MockSDClient.subscribed_folders.append(volume_id)262 self.subscribed_folders.append(volume_id)
242263
243 def unsubscribe_folder(self, volume_id):264 def unsubscribe_folder(self, volume_id):
244 """Unsubcribe from 'volume_id'."""265 """Unsubcribe from 'volume_id'."""
245 MockSDClient.subscribed_folders.remove(volume_id)266 self.subscribed_folders.remove(volume_id)
246267
247 def create_folder(self, path):268 def create_folder(self, path):
248 """Grab list of folders."""269 """Grab list of folders."""
249 MockSDClient.folders.append(path)270 self.folders.append(path)
250271
251 def get_shares(self):272 def get_shares(self):
252 """Grab list of shares."""273 """Grab list of shares."""
253 return MockSDClient.shares274 return self.shares
254275
255 def subscribe_share(self, volume_id):276 def subscribe_share(self, volume_id):
256 """Subcribe to 'volume_id'."""277 """Subcribe to 'volume_id'."""
257 MockSDClient.subscribed_shares.append(volume_id)278 self.subscribed_shares.append(volume_id)
258279
259 def unsubscribe_share(self, volume_id):280 def unsubscribe_share(self, volume_id):
260 """Unsubcribe from 'volume_id'."""281 """Unsubcribe from 'volume_id'."""
261 MockSDClient.subscribed_shares.remove(volume_id)282 self.subscribed_shares.remove(volume_id)
262283
263 def get_current_status(self):284 def get_current_status(self):
264 """Grab syncdaemon status."""285 """Grab syncdaemon status."""
@@ -273,7 +294,7 @@
273 return SAMPLE_SHARED294 return SAMPLE_SHARED
274295
275296
276class MockReplicationClient(object):297class MockReplicationClient(CallRecorder):
277 """A mock replication_client module."""298 """A mock replication_client module."""
278299
279 BOOKMARKS = 'awesome'300 BOOKMARKS = 'awesome'
@@ -311,21 +332,22 @@
311 def setUp(self):332 def setUp(self):
312 super(BackendBasicTestCase, self).setUp()333 super(BackendBasicTestCase, self).setUp()
313 self.patch(backend, "web_client_factory", MockWebClient)334 self.patch(backend, "web_client_factory", MockWebClient)
314 self.patch(backend, "login_client", MockLoginClient())335 self.patch(backend, "CredentialsManagementTool", MockLoginClient)
315 self.patch(backend, "sd_client", MockSDClient())336 self.patch(backend.sd_client, "SyncDaemonClient", MockSDClient)
316 self.patch(backend, "replication_client", MockReplicationClient())337 self.patch(backend, "replication_client", MockReplicationClient())
338
317 self.local_token = DEVICE_TYPE_COMPUTER + TOKEN["token"]339 self.local_token = DEVICE_TYPE_COMPUTER + TOKEN["token"]
318 self.be = backend.ControlBackend()340 self.be = backend.ControlBackend()
341 self.be.wc.failure = False
319342
320 self.memento = MementoHandler()343 self.memento = MementoHandler()
321 backend.logger.addHandler(self.memento)344 backend.logger.addHandler(self.memento)
322345
323 MockLoginClient.creds = TOKEN
324 MockWebClient.failure = None
325
326 def test_backend_creation(self):346 def test_backend_creation(self):
327 """The backend instance is successfully created."""347 """The backend instance is successfully created."""
328 self.assertEqual(self.be.wc.__class__, MockWebClient)348 self.assertIsInstance(self.be.wc, MockWebClient)
349 self.assertIsInstance(self.be.login_client, MockLoginClient)
350 self.assertIsInstance(self.be.sd_client, MockSDClient)
329351
330 @inlineCallbacks352 @inlineCallbacks
331 def test_get_token(self):353 def test_get_token(self):
@@ -361,6 +383,22 @@
361 self.be.shutdown()383 self.be.shutdown()
362 # nothing explodes384 # nothing explodes
363385
386 @inlineCallbacks
387 def test_credentials_are_cached(self):
388 """The credentials are cached."""
389 creds1 = yield self.be.get_credentials()
390 creds2 = yield self.be.get_credentials()
391 self.assertEqual(creds1, creds2)
392 self.assertEqual(self.be.login_client._called['find_credentials'], 1)
393
394 def test_login_client_is_cached(self):
395 """The login_client instance is cached."""
396 self.assertIs(self.be.login_client, self.be.login_client)
397
398 def test_sd_client_is_cached(self):
399 """The sd_client instance is cached."""
400 self.assertIs(self.be.sd_client, self.be.sd_client)
401
364402
365class BackendAccountTestCase(BackendBasicTestCase):403class BackendAccountTestCase(BackendBasicTestCase):
366 """Account tests for the backend."""404 """Account tests for the backend."""
@@ -384,16 +422,16 @@
384 @inlineCallbacks422 @inlineCallbacks
385 def test_account_info_fails(self):423 def test_account_info_fails(self):
386 """The account_info method exercises its errback."""424 """The account_info method exercises its errback."""
387 MockWebClient.failure = 404425 self.be.wc.failure = 404
388 yield self.assertFailure(self.be.account_info(),426 yield self.assertFailure(self.be.account_info(),
389 backend.WebClientError)427 backend.WebClientError)
390428
391 @inlineCallbacks429 @inlineCallbacks
392 def test_account_info_fails_with_unauthorized(self):430 def test_account_info_fails_with_unauthorized(self):
393 """The account_info clears the credentials on unauthorized."""431 """The account_info clears the credentials on unauthorized."""
394 MockWebClient.failure = 401432 self.be.wc.failure = 401
395 d = defer.Deferred()433 d = defer.Deferred()
396 self.patch(backend.login_client, 'clear_credentials',434 self.patch(self.be.login_client, 'clear_credentials',
397 lambda: d.callback('called'))435 lambda: d.callback('called'))
398 yield self.assertFailure(self.be.account_info(),436 yield self.assertFailure(self.be.account_info(),
399 backend.UnauthorizedError)437 backend.UnauthorizedError)
@@ -423,7 +461,7 @@
423 @inlineCallbacks461 @inlineCallbacks
424 def test_devices_info_with_webclient_error(self):462 def test_devices_info_with_webclient_error(self):
425 """The devices_info returns local info if webclient error."""463 """The devices_info returns local info if webclient error."""
426 MockWebClient.failure = 404464 self.be.wc.failure = 404
427 result = yield self.be.devices_info()465 result = yield self.be.devices_info()
428466
429 self.assertEqual(result, [LOCAL_DEVICE])467 self.assertEqual(result, [LOCAL_DEVICE])
@@ -433,9 +471,9 @@
433 @inlineCallbacks471 @inlineCallbacks
434 def test_devices_info_fails_with_unauthorized(self):472 def test_devices_info_fails_with_unauthorized(self):
435 """The devices_info clears the credentials on unauthorized."""473 """The devices_info clears the credentials on unauthorized."""
436 MockWebClient.failure = 401474 self.be.wc.failure = 401
437 d = defer.Deferred()475 d = defer.Deferred()
438 self.patch(backend.login_client, 'clear_credentials',476 self.patch(self.be.login_client, 'clear_credentials',
439 lambda: d.callback('called'))477 lambda: d.callback('called'))
440 yield self.assertFailure(self.be.devices_info(),478 yield self.assertFailure(self.be.devices_info(),
441 backend.UnauthorizedError)479 backend.UnauthorizedError)
@@ -446,13 +484,14 @@
446 """The devices_info returns device only info if files is disabled."""484 """The devices_info returns device only info if files is disabled."""
447 yield self.be.disable_files()485 yield self.be.disable_files()
448 status = yield self.be.file_sync_status()486 status = yield self.be.file_sync_status()
449 assert status['status'] == backend.FILE_SYNC_DISABLED, status487 assert status['status'] == backend.FILE_SYNC_DISABLED
450488
451 self.be.wc.results[DEVICES_API] = SAMPLE_DEVICES_JSON489 self.be.wc.results[DEVICES_API] = SAMPLE_DEVICES_JSON
452 result = yield self.be.devices_info()490 result = yield self.be.devices_info()
453491
454 expected = EXPECTED_DEVICES_INFO[:]492 expected = []
455 for device in expected:493 for device in EXPECTED_DEVICES_INFO:
494 device = device.copy()
456 device.pop('limit_bandwidth', None)495 device.pop('limit_bandwidth', None)
457 device.pop(backend.DOWNLOAD_KEY, None)496 device.pop(backend.DOWNLOAD_KEY, None)
458 device.pop(backend.UPLOAD_KEY, None)497 device.pop(backend.UPLOAD_KEY, None)
@@ -461,6 +500,7 @@
461 device.pop(backend.SHARE_AUTOSUBSCRIBE_KEY, None)500 device.pop(backend.SHARE_AUTOSUBSCRIBE_KEY, None)
462 device.pop(backend.UDF_AUTOSUBSCRIBE_KEY, None)501 device.pop(backend.UDF_AUTOSUBSCRIBE_KEY, None)
463 device['configurable'] = False502 device['configurable'] = False
503 expected.append(device)
464 self.assertEqual(result, expected)504 self.assertEqual(result, expected)
465505
466 @inlineCallbacks506 @inlineCallbacks
@@ -506,7 +546,7 @@
506 @inlineCallbacks546 @inlineCallbacks
507 def test_device_names_info_with_webclient_error(self):547 def test_device_names_info_with_webclient_error(self):
508 """The device_names_info returns local info if webclient error."""548 """The device_names_info returns local info if webclient error."""
509 MockWebClient.failure = 404549 self.be.wc.failure = 404
510 result = yield self.be.device_names_info()550 result = yield self.be.device_names_info()
511551
512 device = LOCAL_DEVICE.copy()552 device = LOCAL_DEVICE.copy()
@@ -525,9 +565,9 @@
525 @inlineCallbacks565 @inlineCallbacks
526 def test_device_names_info_fails_with_unauthorized(self):566 def test_device_names_info_fails_with_unauthorized(self):
527 """The device_names_info clears the credentials on unauthorized."""567 """The device_names_info clears the credentials on unauthorized."""
528 MockWebClient.failure = 401568 self.be.wc.failure = 401
529 d = defer.Deferred()569 d = defer.Deferred()
530 self.patch(backend.login_client, 'clear_credentials',570 self.patch(self.be.login_client, 'clear_credentials',
531 lambda: d.callback('called'))571 lambda: d.callback('called'))
532 yield self.assertFailure(self.be.device_names_info(),572 yield self.assertFailure(self.be.device_names_info(),
533 backend.UnauthorizedError)573 backend.UnauthorizedError)
@@ -565,7 +605,7 @@
565 result = yield self.be.remove_device(device_id)605 result = yield self.be.remove_device(device_id)
566 self.assertEqual(result, device_id)606 self.assertEqual(result, device_id)
567 # credentials were not cleared607 # credentials were not cleared
568 self.assertEqual(MockLoginClient.creds, TOKEN)608 self.assertEqual(self.be.login_client.creds, TOKEN)
569609
570 @inlineCallbacks610 @inlineCallbacks
571 def test_remove_device_clear_credentials_if_local_device(self):611 def test_remove_device_clear_credentials_if_local_device(self):
@@ -574,21 +614,21 @@
574 self.be.wc.results[apiurl] = SAMPLE_DEVICES_JSON614 self.be.wc.results[apiurl] = SAMPLE_DEVICES_JSON
575 yield self.be.remove_device(self.local_token)615 yield self.be.remove_device(self.local_token)
576 # credentials were cleared616 # credentials were cleared
577 self.assertEqual(MockLoginClient.creds, None)617 self.assertEqual(self.be.login_client.creds, None)
578618
579 @inlineCallbacks619 @inlineCallbacks
580 def test_remove_device_fails(self):620 def test_remove_device_fails(self):
581 """The remove_device method fails as expected."""621 """The remove_device method fails as expected."""
582 MockWebClient.failure = 404622 self.be.wc.failure = 404
583 yield self.assertFailure(self.be.remove_device(self.local_token),623 yield self.assertFailure(self.be.remove_device(self.local_token),
584 backend.WebClientError)624 backend.WebClientError)
585625
586 @inlineCallbacks626 @inlineCallbacks
587 def test_remove_device_fails_with_unauthorized(self):627 def test_remove_device_fails_with_unauthorized(self):
588 """The remove_device clears the credentials on unauthorized."""628 """The remove_device clears the credentials on unauthorized."""
589 MockWebClient.failure = 401629 self.be.wc.failure = 401
590 d = defer.Deferred()630 d = defer.Deferred()
591 self.patch(backend.login_client, 'clear_credentials',631 self.patch(self.be.login_client, 'clear_credentials',
592 lambda: d.callback('called'))632 lambda: d.callback('called'))
593 yield self.assertFailure(self.be.remove_device(self.local_token),633 yield self.assertFailure(self.be.remove_device(self.local_token),
594 backend.UnauthorizedError)634 backend.UnauthorizedError)
@@ -598,6 +638,8 @@
598 def test_remove_device_does_not_log_device_id(self):638 def test_remove_device_does_not_log_device_id(self):
599 """The remove_device does not log the device_id."""639 """The remove_device does not log the device_id."""
600 device_id = DEVICE_TYPE_COMPUTER + TOKEN['token']640 device_id = DEVICE_TYPE_COMPUTER + TOKEN['token']
641 apiurl = DEVICE_REMOVE_API % ('computer', TOKEN['token'])
642 self.be.wc.results[apiurl] = SAMPLE_DEVICES_JSON
601 yield self.be.remove_device(device_id)643 yield self.be.remove_device(device_id)
602644
603 device_id_logged = all(device_id not in r.getMessage()645 device_id_logged = all(device_id not in r.getMessage()
@@ -607,57 +649,57 @@
607 @inlineCallbacks649 @inlineCallbacks
608 def test_change_show_all_notifications(self):650 def test_change_show_all_notifications(self):
609 """The device settings are updated."""651 """The device settings are updated."""
610 backend.sd_client.show_all_notifications = False652 self.be.sd_client.show_all_notifications = False
611 yield self.be.change_device_settings(self.local_token,653 yield self.be.change_device_settings(self.local_token,
612 {backend.SHOW_ALL_NOTIFICATIONS_KEY: True})654 {backend.SHOW_ALL_NOTIFICATIONS_KEY: True})
613 self.assertEqual(backend.sd_client.show_all_notifications, True)655 self.assertEqual(self.be.sd_client.show_all_notifications, True)
614 yield self.be.change_device_settings(self.local_token,656 yield self.be.change_device_settings(self.local_token,
615 {backend.SHOW_ALL_NOTIFICATIONS_KEY: False})657 {backend.SHOW_ALL_NOTIFICATIONS_KEY: False})
616 self.assertEqual(backend.sd_client.show_all_notifications, False)658 self.assertEqual(self.be.sd_client.show_all_notifications, False)
617659
618 @inlineCallbacks660 @inlineCallbacks
619 def test_change_limit_bandwidth(self):661 def test_change_limit_bandwidth(self):
620 """The device settings are updated."""662 """The device settings are updated."""
621 backend.sd_client.throttling = False663 self.be.sd_client.throttling = False
622 yield self.be.change_device_settings(self.local_token,664 yield self.be.change_device_settings(self.local_token,
623 {backend.LIMIT_BW_KEY: True})665 {backend.LIMIT_BW_KEY: True})
624 self.assertEqual(backend.sd_client.throttling, True)666 self.assertEqual(self.be.sd_client.throttling, True)
625 yield self.be.change_device_settings(self.local_token,667 yield self.be.change_device_settings(self.local_token,
626 {backend.LIMIT_BW_KEY: False})668 {backend.LIMIT_BW_KEY: False})
627 self.assertEqual(backend.sd_client.throttling, False)669 self.assertEqual(self.be.sd_client.throttling, False)
628670
629 @inlineCallbacks671 @inlineCallbacks
630 def test_change_upload_speed_limit(self):672 def test_change_upload_speed_limit(self):
631 """The device settings are updated."""673 """The device settings are updated."""
632 backend.sd_client.limits = {"download": -1, "upload": -1}674 self.be.sd_client.limits = {"download": -1, "upload": -1}
633 yield self.be.change_device_settings(self.local_token,675 yield self.be.change_device_settings(self.local_token,
634 {backend.UPLOAD_KEY: 1111})676 {backend.UPLOAD_KEY: 1111})
635 self.assertEqual(backend.sd_client.limits["upload"], 1111)677 self.assertEqual(self.be.sd_client.limits["upload"], 1111)
636 self.assertEqual(backend.sd_client.limits["download"], -1)678 self.assertEqual(self.be.sd_client.limits["download"], -1)
637679
638 @inlineCallbacks680 @inlineCallbacks
639 def test_change_download_speed_limit(self):681 def test_change_download_speed_limit(self):
640 """The device settings are updated."""682 """The device settings are updated."""
641 backend.sd_client.limits = {"download": -1, "upload": -1}683 self.be.sd_client.limits = {"download": -1, "upload": -1}
642 yield self.be.change_device_settings(self.local_token,684 yield self.be.change_device_settings(self.local_token,
643 {backend.DOWNLOAD_KEY: 99})685 {backend.DOWNLOAD_KEY: 99})
644 self.assertEqual(backend.sd_client.limits["upload"], -1)686 self.assertEqual(self.be.sd_client.limits["upload"], -1)
645 self.assertEqual(backend.sd_client.limits["download"], 99)687 self.assertEqual(self.be.sd_client.limits["download"], 99)
646688
647 @inlineCallbacks689 @inlineCallbacks
648 def test_changing_settings_for_wrong_id_has_no_effect(self):690 def test_changing_settings_for_wrong_id_has_no_effect(self):
649 """If the id is wrong, no settings are changed."""691 """If the id is wrong, no settings are changed."""
650 backend.sd_client.throttling = False692 self.be.sd_client.throttling = False
651 backend.sd_client.limits = {"download": -1, "upload": -1}693 self.be.sd_client.limits = {"download": -1, "upload": -1}
652 new_settings = {694 new_settings = {
653 backend.DOWNLOAD_KEY: 99,695 backend.DOWNLOAD_KEY: 99,
654 backend.UPLOAD_KEY: 99,696 backend.UPLOAD_KEY: 99,
655 backend.LIMIT_BW_KEY: True,697 backend.LIMIT_BW_KEY: True,
656 }698 }
657 yield self.be.change_device_settings("wrong token!", new_settings)699 yield self.be.change_device_settings("wrong token!", new_settings)
658 self.assertEqual(backend.sd_client.throttling, False)700 self.assertEqual(self.be.sd_client.throttling, False)
659 self.assertEqual(backend.sd_client.limits["upload"], -1)701 self.assertEqual(self.be.sd_client.limits["upload"], -1)
660 self.assertEqual(backend.sd_client.limits["download"], -1)702 self.assertEqual(self.be.sd_client.limits["download"], -1)
661703
662 @inlineCallbacks704 @inlineCallbacks
663 def test_changing_settings_does_not_log_device_id(self):705 def test_changing_settings_does_not_log_device_id(self):
@@ -674,7 +716,6 @@
674 """Volumes tests for the backend."""716 """Volumes tests for the backend."""
675717
676 # Access to a protected member of a client class718 # Access to a protected member of a client class
677 # pylint: disable=W0212
678719
679 def setUp(self):720 def setUp(self):
680 super(BackendVolumesTestCase, self).setUp()721 super(BackendVolumesTestCase, self).setUp()
@@ -753,8 +794,8 @@
753 @inlineCallbacks794 @inlineCallbacks
754 def test_volumes_info(self):795 def test_volumes_info(self):
755 """The volumes_info method exercises its callback."""796 """The volumes_info method exercises its callback."""
756 self.patch(MockSDClient, 'shares', SAMPLE_SHARES)797 self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES)
757 self.patch(MockSDClient, 'folders', SAMPLE_FOLDERS)798 self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS)
758799
759 expected = yield self.expected_volumes(SAMPLE_SHARES, SAMPLE_FOLDERS)800 expected = yield self.expected_volumes(SAMPLE_SHARES, SAMPLE_FOLDERS)
760 result = yield self.be.volumes_info()801 result = yield self.be.volumes_info()
@@ -763,16 +804,14 @@
763 @inlineCallbacks804 @inlineCallbacks
764 def test_volumes_info_without_storage_info(self):805 def test_volumes_info_without_storage_info(self):
765 """The volumes_info method exercises its callback."""806 """The volumes_info method exercises its callback."""
766 self.patch(MockSDClient, 'shares', SAMPLE_SHARES)807 self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES)
767 self.patch(MockSDClient, 'folders', SAMPLE_FOLDERS)808 self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS)
768809
769 expected = yield self.expected_volumes(SAMPLE_SHARES, SAMPLE_FOLDERS,810 expected = yield self.expected_volumes(SAMPLE_SHARES, SAMPLE_FOLDERS,
770 with_storage_info=False)811 with_storage_info=False)
771 result = yield self.be.volumes_info(with_storage_info=False)812 result = yield self.be.volumes_info(with_storage_info=False)
772 self.assertEqual(result, expected)813 self.assertEqual(result, expected)
773814
774 # pylint: disable=W0212
775
776 def test_cached_volumes_are_initially_empty(self):815 def test_cached_volumes_are_initially_empty(self):
777 """The cached volume list is empty."""816 """The cached volume list is empty."""
778 self.assertEqual(self.be._volumes, {})817 self.assertEqual(self.be._volumes, {})
@@ -780,8 +819,8 @@
780 @inlineCallbacks819 @inlineCallbacks
781 def test_volumes_are_cached(self):820 def test_volumes_are_cached(self):
782 """The volume list is cached."""821 """The volume list is cached."""
783 self.patch(MockSDClient, 'shares', SAMPLE_SHARES)822 self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES)
784 self.patch(MockSDClient, 'folders', SAMPLE_FOLDERS)823 self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS)
785824
786 expected = {}825 expected = {}
787 info = yield self.expected_volumes(SAMPLE_SHARES, SAMPLE_FOLDERS)826 info = yield self.expected_volumes(SAMPLE_SHARES, SAMPLE_FOLDERS)
@@ -832,8 +871,8 @@
832 u'volume_id': u'7d130dfe-98b2-4bd5-8708-9eeba9838ac0'},871 u'volume_id': u'7d130dfe-98b2-4bd5-8708-9eeba9838ac0'},
833 ]872 ]
834873
835 self.patch(MockSDClient, 'shares', read_only_shares)874 self.patch(self.be.sd_client, 'shares', read_only_shares)
836 self.patch(MockSDClient, 'folders', SAMPLE_FOLDERS)875 self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS)
837876
838 expected = yield self.expected_volumes(read_only_shares,877 expected = yield self.expected_volumes(read_only_shares,
839 SAMPLE_FOLDERS)878 SAMPLE_FOLDERS)
@@ -843,9 +882,9 @@
843 @inlineCallbacks882 @inlineCallbacks
844 def test_volumes_info_no_quota_for_root(self):883 def test_volumes_info_no_quota_for_root(self):
845 """The volumes_info returns info even if quota call fails."""884 """The volumes_info returns info even if quota call fails."""
846 MockWebClient.failure = 500885 self.be.wc.failure = 500
847 self.patch(MockSDClient, 'shares', SAMPLE_SHARES)886 self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES)
848 self.patch(MockSDClient, 'folders', SAMPLE_FOLDERS)887 self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS)
849888
850 expected = yield self.expected_volumes(SAMPLE_SHARES, SAMPLE_FOLDERS)889 expected = yield self.expected_volumes(SAMPLE_SHARES, SAMPLE_FOLDERS)
851 result = yield self.be.volumes_info()890 result = yield self.be.volumes_info()
@@ -860,9 +899,9 @@
860 self.be._volumes[fid] = {u'type': self.be.FOLDER_TYPE}899 self.be._volumes[fid] = {u'type': self.be.FOLDER_TYPE}
861900
862 yield self.be.subscribe_volume(volume_id=fid)901 yield self.be.subscribe_volume(volume_id=fid)
863 self.addCleanup(lambda: MockSDClient.subscribed_folders.remove(fid))902 self.addCleanup(self.be.sd_client.subscribed_folders.remove, fid)
864903
865 self.assertEqual(MockSDClient.subscribed_folders, [fid])904 self.assertEqual(self.be.sd_client.subscribed_folders, [fid])
866905
867 @inlineCallbacks906 @inlineCallbacks
868 def test_unsubscribe_volume_folder(self):907 def test_unsubscribe_volume_folder(self):
@@ -871,11 +910,11 @@
871 self.be._volumes[fid] = {u'type': self.be.FOLDER_TYPE}910 self.be._volumes[fid] = {u'type': self.be.FOLDER_TYPE}
872911
873 yield self.be.subscribe_volume(volume_id=fid)912 yield self.be.subscribe_volume(volume_id=fid)
874 self.assertEqual(MockSDClient.subscribed_folders, [fid])913 self.assertEqual(self.be.sd_client.subscribed_folders, [fid])
875914
876 yield self.be.unsubscribe_volume(volume_id=fid)915 yield self.be.unsubscribe_volume(volume_id=fid)
877916
878 self.assertEqual(MockSDClient.subscribed_folders, [])917 self.assertEqual(self.be.sd_client.subscribed_folders, [])
879918
880 @inlineCallbacks919 @inlineCallbacks
881 def test_subscribe_volume_share(self):920 def test_subscribe_volume_share(self):
@@ -884,9 +923,9 @@
884 self.be._volumes[sid] = {u'type': self.be.SHARE_TYPE}923 self.be._volumes[sid] = {u'type': self.be.SHARE_TYPE}
885924
886 yield self.be.subscribe_volume(volume_id=sid)925 yield self.be.subscribe_volume(volume_id=sid)
887 self.addCleanup(lambda: MockSDClient.subscribed_shares.remove(sid))926 self.addCleanup(self.be.sd_client.subscribed_shares.remove, sid)
888927
889 self.assertEqual(MockSDClient.subscribed_shares, [sid])928 self.assertEqual(self.be.sd_client.subscribed_shares, [sid])
890929
891 @inlineCallbacks930 @inlineCallbacks
892 def test_unsubscribe_volume_share(self):931 def test_unsubscribe_volume_share(self):
@@ -895,11 +934,11 @@
895 self.be._volumes[sid] = {u'type': self.be.SHARE_TYPE}934 self.be._volumes[sid] = {u'type': self.be.SHARE_TYPE}
896935
897 yield self.be.subscribe_volume(volume_id=sid)936 yield self.be.subscribe_volume(volume_id=sid)
898 self.assertEqual(MockSDClient.subscribed_shares, [sid])937 self.assertEqual(self.be.sd_client.subscribed_shares, [sid])
899938
900 yield self.be.unsubscribe_volume(volume_id=sid)939 yield self.be.unsubscribe_volume(volume_id=sid)
901940
902 self.assertEqual(MockSDClient.subscribed_shares, [])941 self.assertEqual(self.be.sd_client.subscribed_shares, [])
903942
904 @inlineCallbacks943 @inlineCallbacks
905 def test_change_volume_settings_folder(self):944 def test_change_volume_settings_folder(self):
@@ -908,10 +947,10 @@
908 self.be._volumes[fid] = {u'type': self.be.FOLDER_TYPE}947 self.be._volumes[fid] = {u'type': self.be.FOLDER_TYPE}
909948
910 yield self.be.change_volume_settings(fid, {'subscribed': True})949 yield self.be.change_volume_settings(fid, {'subscribed': True})
911 self.assertEqual(MockSDClient.subscribed_folders, [fid])950 self.assertEqual(self.be.sd_client.subscribed_folders, [fid])
912951
913 yield self.be.change_volume_settings(fid, {'subscribed': False})952 yield self.be.change_volume_settings(fid, {'subscribed': False})
914 self.assertEqual(MockSDClient.subscribed_folders, [])953 self.assertEqual(self.be.sd_client.subscribed_folders, [])
915954
916 @inlineCallbacks955 @inlineCallbacks
917 def test_change_volume_settings_share(self):956 def test_change_volume_settings_share(self):
@@ -920,17 +959,17 @@
920 self.be._volumes[sid] = {u'type': self.be.SHARE_TYPE}959 self.be._volumes[sid] = {u'type': self.be.SHARE_TYPE}
921960
922 yield self.be.change_volume_settings(sid, {'subscribed': True})961 yield self.be.change_volume_settings(sid, {'subscribed': True})
923 self.assertEqual(MockSDClient.subscribed_shares, [sid])962 self.assertEqual(self.be.sd_client.subscribed_shares, [sid])
924963
925 yield self.be.change_volume_settings(sid, {'subscribed': False})964 yield self.be.change_volume_settings(sid, {'subscribed': False})
926 self.assertEqual(MockSDClient.subscribed_shares, [])965 self.assertEqual(self.be.sd_client.subscribed_shares, [])
927966
928 @inlineCallbacks967 @inlineCallbacks
929 def test_change_volume_settings_no_setting(self):968 def test_change_volume_settings_no_setting(self):
930 """The change volume settings does not fail on empty settings."""969 """The change volume settings does not fail on empty settings."""
931 yield self.be.change_volume_settings('test', {})970 yield self.be.change_volume_settings('test', {})
932 self.assertEqual(MockSDClient.subscribed_folders, [])971 self.assertEqual(self.be.sd_client.subscribed_folders, [])
933 self.assertEqual(MockSDClient.subscribed_shares, [])972 self.assertEqual(self.be.sd_client.subscribed_shares, [])
934973
935 @inlineCallbacks974 @inlineCallbacks
936 def test_create_folder(self):975 def test_create_folder(self):
@@ -938,7 +977,7 @@
938 folder_path = os.path.join(USER_HOME, 'Test Me')977 folder_path = os.path.join(USER_HOME, 'Test Me')
939 yield self.be.create_folder(folder_path=folder_path)978 yield self.be.create_folder(folder_path=folder_path)
940979
941 self.assertEqual(MockSDClient.folders, [folder_path])980 self.assertEqual(self.be.sd_client.folders, [folder_path])
942981
943982
944class BackendValidatePathForFolderTestCase(BackendBasicTestCase):983class BackendValidatePathForFolderTestCase(BackendBasicTestCase):
@@ -950,9 +989,9 @@
950 super(BackendValidatePathForFolderTestCase, self).setUp()989 super(BackendValidatePathForFolderTestCase, self).setUp()
951 old_home = os.environ['HOME']990 old_home = os.environ['HOME']
952 os.environ['HOME'] = USER_HOME991 os.environ['HOME'] = USER_HOME
953 self.addCleanup(lambda: os.environ.__setitem__('HOME', old_home))992 self.addCleanup(os.environ.__setitem__, 'HOME', old_home)
954 self.patch(MockSDClient, 'shares', SAMPLE_SHARES)993 self.patch(self.be.sd_client, 'shares', SAMPLE_SHARES)
955 self.patch(MockSDClient, 'folders', SAMPLE_FOLDERS)994 self.patch(self.be.sd_client, 'folders', SAMPLE_FOLDERS)
956 self.patch(backend, 'is_link', lambda p: False)995 self.patch(backend, 'is_link', lambda p: False)
957996
958 @defer.inlineCallbacks997 @defer.inlineCallbacks
@@ -1040,8 +1079,8 @@
10401079
1041 def _build_msg(self):1080 def _build_msg(self):
1042 """Build expected message regarding file sync status."""1081 """Build expected message regarding file sync status."""
1043 return '%s (%s)' % (MockSDClient.status['description'],1082 return '%s (%s)' % (self.be.sd_client.status['description'],
1044 MockSDClient.status['name'])1083 self.be.sd_client.status['name'])
10451084
1046 @inlineCallbacks1085 @inlineCallbacks
1047 def assert_correct_status(self, status, msg=None):1086 def assert_correct_status(self, status, msg=None):
@@ -1054,14 +1093,14 @@
1054 @inlineCallbacks1093 @inlineCallbacks
1055 def test_disabled(self):1094 def test_disabled(self):
1056 """The syncdaemon status is processed and emitted."""1095 """The syncdaemon status is processed and emitted."""
1057 self.patch(MockSDClient, 'file_sync', False)1096 self.patch(self.be.sd_client, 'file_sync', False)
1058 yield self.assert_correct_status(FILE_SYNC_DISABLED, msg='')1097 yield self.assert_correct_status(FILE_SYNC_DISABLED, msg='')
1059 self.assertTrue(self.be.file_sync_disabled)1098 self.assertTrue(self.be.file_sync_disabled)
10601099
1061 @inlineCallbacks1100 @inlineCallbacks
1062 def test_error(self):1101 def test_error(self):
1063 """The syncdaemon status is processed and emitted."""1102 """The syncdaemon status is processed and emitted."""
1064 MockSDClient.status = {1103 self.be.sd_client.status = {
1065 'is_error': 'True', # nothing else matters1104 'is_error': 'True', # nothing else matters
1066 'is_online': '', 'is_connected': '',1105 'is_online': '', 'is_connected': '',
1067 'name': 'AUTH_FAILED', 'connection': '', 'queues': '',1106 'name': 'AUTH_FAILED', 'connection': '', 'queues': '',
@@ -1074,7 +1113,7 @@
1074 @inlineCallbacks1113 @inlineCallbacks
1075 def test_starting_when_init_not_user(self):1114 def test_starting_when_init_not_user(self):
1076 """The syncdaemon status is processed and emitted."""1115 """The syncdaemon status is processed and emitted."""
1077 MockSDClient.status = {1116 self.be.sd_client.status = {
1078 'is_error': '', 'is_online': '', 'is_connected': '',1117 'is_error': '', 'is_online': '', 'is_connected': '',
1079 'connection': 'Not User With Network', 'queues': '',1118 'connection': 'Not User With Network', 'queues': '',
1080 'name': 'INIT', 'description': 'something new',1119 'name': 'INIT', 'description': 'something new',
@@ -1085,7 +1124,7 @@
1085 @inlineCallbacks1124 @inlineCallbacks
1086 def test_starting_when_init_with_user(self):1125 def test_starting_when_init_with_user(self):
1087 """The syncdaemon status is processed and emitted."""1126 """The syncdaemon status is processed and emitted."""
1088 MockSDClient.status = {1127 self.be.sd_client.status = {
1089 'is_error': '', 'is_online': '', 'is_connected': '',1128 'is_error': '', 'is_online': '', 'is_connected': '',
1090 'connection': 'With User With Network', 'queues': '',1129 'connection': 'With User With Network', 'queues': '',
1091 'name': 'INIT', 'description': 'something new',1130 'name': 'INIT', 'description': 'something new',
@@ -1096,7 +1135,7 @@
1096 @inlineCallbacks1135 @inlineCallbacks
1097 def test_starting_when_local_rescan_not_user(self):1136 def test_starting_when_local_rescan_not_user(self):
1098 """The syncdaemon status is processed and emitted."""1137 """The syncdaemon status is processed and emitted."""
1099 MockSDClient.status = {1138 self.be.sd_client.status = {
1100 'is_error': '', 'is_online': '', 'is_connected': '',1139 'is_error': '', 'is_online': '', 'is_connected': '',
1101 'connection': 'Not User With Network', 'queues': '',1140 'connection': 'Not User With Network', 'queues': '',
1102 'name': 'LOCAL_RESCAN', 'description': 'something new',1141 'name': 'LOCAL_RESCAN', 'description': 'something new',
@@ -1107,7 +1146,7 @@
1107 @inlineCallbacks1146 @inlineCallbacks
1108 def test_starting_when_local_rescan_with_user(self):1147 def test_starting_when_local_rescan_with_user(self):
1109 """The syncdaemon status is processed and emitted."""1148 """The syncdaemon status is processed and emitted."""
1110 MockSDClient.status = {1149 self.be.sd_client.status = {
1111 'is_error': '', 'is_online': '', 'is_connected': '',1150 'is_error': '', 'is_online': '', 'is_connected': '',
1112 'connection': 'With User With Network', 'queues': '',1151 'connection': 'With User With Network', 'queues': '',
1113 'name': 'LOCAL_RESCAN', 'description': 'something new',1152 'name': 'LOCAL_RESCAN', 'description': 'something new',
@@ -1118,7 +1157,7 @@
1118 @inlineCallbacks1157 @inlineCallbacks
1119 def test_starting_when_ready_with_user(self):1158 def test_starting_when_ready_with_user(self):
1120 """The syncdaemon status is processed and emitted."""1159 """The syncdaemon status is processed and emitted."""
1121 MockSDClient.status = {1160 self.be.sd_client.status = {
1122 'is_error': '', 'is_online': '', 'is_connected': '',1161 'is_error': '', 'is_online': '', 'is_connected': '',
1123 'connection': 'With User With Network', 'queues': '',1162 'connection': 'With User With Network', 'queues': '',
1124 'name': 'READY', 'description': 'something nicer',1163 'name': 'READY', 'description': 'something nicer',
@@ -1129,7 +1168,7 @@
1129 @inlineCallbacks1168 @inlineCallbacks
1130 def test_disconnected(self):1169 def test_disconnected(self):
1131 """The syncdaemon status is processed and emitted."""1170 """The syncdaemon status is processed and emitted."""
1132 MockSDClient.status = {1171 self.be.sd_client.status = {
1133 'is_error': '', 'is_online': '', 'is_connected': '',1172 'is_error': '', 'is_online': '', 'is_connected': '',
1134 'queues': '', 'description': 'something new',1173 'queues': '', 'description': 'something new',
1135 'connection': 'Not User With Network', # user didn't connect1174 'connection': 'Not User With Network', # user didn't connect
@@ -1143,7 +1182,7 @@
1143 @inlineCallbacks1182 @inlineCallbacks
1144 def test_disconnected_when_waiting(self):1183 def test_disconnected_when_waiting(self):
1145 """The syncdaemon status is processed and emitted."""1184 """The syncdaemon status is processed and emitted."""
1146 MockSDClient.status = {1185 self.be.sd_client.status = {
1147 'is_error': '', 'is_online': '', 'is_connected': '',1186 'is_error': '', 'is_online': '', 'is_connected': '',
1148 'connection': 'With User With Network', 'queues': '',1187 'connection': 'With User With Network', 'queues': '',
1149 'name': 'WAITING', 'description': 'what a long wait!',1188 'name': 'WAITING', 'description': 'what a long wait!',
@@ -1156,7 +1195,7 @@
1156 @inlineCallbacks1195 @inlineCallbacks
1157 def test_syncing_if_online(self):1196 def test_syncing_if_online(self):
1158 """The syncdaemon status is processed and emitted."""1197 """The syncdaemon status is processed and emitted."""
1159 MockSDClient.status = {1198 self.be.sd_client.status = {
1160 'is_error': '', 'is_online': 'True', 'is_connected': 'True',1199 'is_error': '', 'is_online': 'True', 'is_connected': 'True',
1161 'name': 'QUEUE_MANAGER', 'connection': '',1200 'name': 'QUEUE_MANAGER', 'connection': '',
1162 'queues': 'WORKING_ON_CONTENT', # anything but IDLE1201 'queues': 'WORKING_ON_CONTENT', # anything but IDLE
@@ -1170,7 +1209,7 @@
1170 @inlineCallbacks1209 @inlineCallbacks
1171 def test_syncing_even_if_not_online(self):1210 def test_syncing_even_if_not_online(self):
1172 """The syncdaemon status is processed and emitted."""1211 """The syncdaemon status is processed and emitted."""
1173 MockSDClient.status = {1212 self.be.sd_client.status = {
1174 'is_error': '', 'is_online': '', 'is_connected': 'True',1213 'is_error': '', 'is_online': '', 'is_connected': 'True',
1175 'name': 'CHECK_VERSION', 'connection': '',1214 'name': 'CHECK_VERSION', 'connection': '',
1176 'queues': 'WORKING_ON_CONTENT',1215 'queues': 'WORKING_ON_CONTENT',
@@ -1184,7 +1223,7 @@
1184 @inlineCallbacks1223 @inlineCallbacks
1185 def test_idle(self):1224 def test_idle(self):
1186 """The syncdaemon status is processed and emitted."""1225 """The syncdaemon status is processed and emitted."""
1187 MockSDClient.status = {1226 self.be.sd_client.status = {
1188 'is_error': '', 'is_online': 'True', 'is_connected': 'True',1227 'is_error': '', 'is_online': 'True', 'is_connected': 'True',
1189 'name': 'QUEUE_MANAGER', 'connection': '',1228 'name': 'QUEUE_MANAGER', 'connection': '',
1190 'queues': 'IDLE',1229 'queues': 'IDLE',
@@ -1198,7 +1237,7 @@
1198 @inlineCallbacks1237 @inlineCallbacks
1199 def test_stopped(self):1238 def test_stopped(self):
1200 """The syncdaemon status is processed and emitted."""1239 """The syncdaemon status is processed and emitted."""
1201 MockSDClient.status = {1240 self.be.sd_client.status = {
1202 'is_error': '', 'is_online': '', 'is_connected': '',1241 'is_error': '', 'is_online': '', 'is_connected': '',
1203 'name': 'SHUTDOWN', 'connection': '',1242 'name': 'SHUTDOWN', 'connection': '',
1204 'queues': 'IDLE',1243 'queues': 'IDLE',
@@ -1212,7 +1251,7 @@
1212 @inlineCallbacks1251 @inlineCallbacks
1213 def test_unknown(self):1252 def test_unknown(self):
1214 """The syncdaemon status is processed and emitted."""1253 """The syncdaemon status is processed and emitted."""
1215 MockSDClient.status = {1254 self.be.sd_client.status = status = {
1216 'is_error': '', 'is_online': '', 'is_connected': '',1255 'is_error': '', 'is_online': '', 'is_connected': '',
1217 'name': '', 'connection': '', 'queues': '',1256 'name': '', 'connection': '', 'queues': '',
1218 'description': '',1257 'description': '',
@@ -1220,7 +1259,7 @@
1220 yield self.assert_correct_status(FILE_SYNC_UNKNOWN)1259 yield self.assert_correct_status(FILE_SYNC_UNKNOWN)
12211260
1222 has_warning = self.memento.check_warning('file_sync_status: unknown',1261 has_warning = self.memento.check_warning('file_sync_status: unknown',
1223 repr(MockSDClient.status))1262 repr(status))
1224 self.assertTrue(has_warning)1263 self.assertTrue(has_warning)
12251264
1226 # self.be.file_sync_disabled does not change1265 # self.be.file_sync_disabled does not change
@@ -1231,10 +1270,8 @@
1231 self.be.status_changed_handler = self._set_called1270 self.be.status_changed_handler = self._set_called
1232 status = {'name': 'foo', 'description': 'bar', 'is_error': '',1271 status = {'name': 'foo', 'description': 'bar', 'is_error': '',
1233 'is_connected': '', 'is_online': '', 'queues': ''}1272 'is_connected': '', 'is_online': '', 'queues': ''}
1234 backend.sd_client.status_changed_handler(status)1273 self.be.sd_client.status_changed_handler(status)
12351274
1236 # pylint: disable=W0212
1237 # Access to a protected member _process_file_sync_status
1238 expected_status = self.be._process_file_sync_status(status)1275 expected_status = self.be._process_file_sync_status(status)
1239 self.assertEqual(self._called, ((expected_status,), {}))1276 self.assertEqual(self._called, ((expected_status,), {}))
12401277
@@ -1259,7 +1296,7 @@
12591296
1260 def setUp(self):1297 def setUp(self):
1261 super(BackendFileSyncOpsTestCase, self).setUp()1298 super(BackendFileSyncOpsTestCase, self).setUp()
1262 MockSDClient.actions = []1299 self.be.sd_client.actions = []
12631300
1264 @inlineCallbacks1301 @inlineCallbacks
1265 def test_enable_files(self):1302 def test_enable_files(self):
@@ -1267,7 +1304,7 @@
1267 yield self.be.disable_files()1304 yield self.be.disable_files()
12681305
1269 yield self.be.enable_files()1306 yield self.be.enable_files()
1270 self.assertTrue(MockSDClient.file_sync)1307 self.assertTrue(self.be.sd_client.file_sync)
1271 self.assertFalse(self.be.file_sync_disabled)1308 self.assertFalse(self.be.file_sync_disabled)
12721309
1273 @inlineCallbacks1310 @inlineCallbacks
@@ -1276,7 +1313,7 @@
1276 yield self.be.enable_files()1313 yield self.be.enable_files()
12771314
1278 yield self.be.disable_files()1315 yield self.be.disable_files()
1279 self.assertFalse(MockSDClient.file_sync)1316 self.assertFalse(self.be.sd_client.file_sync)
1280 self.assertTrue(self.be.file_sync_disabled)1317 self.assertTrue(self.be.file_sync_disabled)
12811318
1282 @inlineCallbacks1319 @inlineCallbacks
@@ -1284,7 +1321,7 @@
1284 """Connect files service."""1321 """Connect files service."""
1285 yield self.be.connect_files()1322 yield self.be.connect_files()
12861323
1287 self.assertEqual(MockSDClient.actions, ['connect'])1324 self.assertEqual(self.be.sd_client.actions, ['connect'])
1288 self.assertFalse(self.be.file_sync_disabled)1325 self.assertFalse(self.be.file_sync_disabled)
12891326
1290 @inlineCallbacks1327 @inlineCallbacks
@@ -1292,7 +1329,7 @@
1292 """Disconnect files service."""1329 """Disconnect files service."""
1293 yield self.be.disconnect_files()1330 yield self.be.disconnect_files()
12941331
1295 self.assertEqual(MockSDClient.actions, ['disconnect'])1332 self.assertEqual(self.be.sd_client.actions, ['disconnect'])
1296 self.assertFalse(self.be.file_sync_disabled)1333 self.assertFalse(self.be.file_sync_disabled)
12971334
1298 @inlineCallbacks1335 @inlineCallbacks
@@ -1300,7 +1337,7 @@
1300 """Restart the files service."""1337 """Restart the files service."""
1301 yield self.be.restart_files()1338 yield self.be.restart_files()
13021339
1303 self.assertEqual(MockSDClient.actions, ['stop', 'start'])1340 self.assertEqual(self.be.sd_client.actions, ['stop', 'start'])
1304 self.assertFalse(self.be.file_sync_disabled)1341 self.assertFalse(self.be.file_sync_disabled)
13051342
1306 @inlineCallbacks1343 @inlineCallbacks
@@ -1308,7 +1345,7 @@
1308 """Start the files service."""1345 """Start the files service."""
1309 yield self.be.start_files()1346 yield self.be.start_files()
13101347
1311 self.assertEqual(MockSDClient.actions, ['start'])1348 self.assertEqual(self.be.sd_client.actions, ['start'])
1312 self.assertFalse(self.be.file_sync_disabled)1349 self.assertFalse(self.be.file_sync_disabled)
13131350
1314 @inlineCallbacks1351 @inlineCallbacks
@@ -1316,7 +1353,7 @@
1316 """Stop the files service."""1353 """Stop the files service."""
1317 yield self.be.stop_files()1354 yield self.be.stop_files()
13181355
1319 self.assertEqual(MockSDClient.actions, ['stop'])1356 self.assertEqual(self.be.sd_client.actions, ['stop'])
1320 self.assertFalse(self.be.file_sync_disabled)1357 self.assertFalse(self.be.file_sync_disabled)
13211358
13221359
@@ -1348,7 +1385,7 @@
1348 """The replication settings can be changed."""1385 """The replication settings can be changed."""
1349 rid = '0123-4567'1386 rid = '0123-4567'
1350 MockReplicationClient.replications.add(rid)1387 MockReplicationClient.replications.add(rid)
1351 self.addCleanup(lambda: MockReplicationClient.replications.remove(rid))1388 self.addCleanup(MockReplicationClient.replications.remove, rid)
13521389
1353 yield self.be.change_replication_settings(rid, {'enabled': False})1390 yield self.be.change_replication_settings(rid, {'enabled': False})
1354 self.assertIn(rid, MockReplicationClient.exclusions)1391 self.assertIn(rid, MockReplicationClient.exclusions)
@@ -1373,7 +1410,7 @@
1373 """The change replication settings does not fail on empty settings."""1410 """The change replication settings does not fail on empty settings."""
1374 rid = '0123-4567'1411 rid = '0123-4567'
1375 MockReplicationClient.replications.add(rid)1412 MockReplicationClient.replications.add(rid)
1376 self.addCleanup(lambda: MockReplicationClient.replications.remove(rid))1413 self.addCleanup(MockReplicationClient.replications.remove, rid)
13771414
1378 prior = MockReplicationClient.exclusions.copy()1415 prior = MockReplicationClient.exclusions.copy()
1379 yield self.be.change_replication_settings(rid, {})1416 yield self.be.change_replication_settings(rid, {})
@@ -1398,7 +1435,7 @@
1398 new_value = not self.default_settings[setting_name]1435 new_value = not self.default_settings[setting_name]
1399 yield self.be.change_file_sync_settings({setting_name: new_value})1436 yield self.be.change_file_sync_settings({setting_name: new_value})
14001437
1401 self.assertEqual(getattr(backend.sd_client, setting_name), new_value)1438 self.assertEqual(getattr(self.be.sd_client, setting_name), new_value)
14021439
1403 actual = yield self.be.file_sync_settings_info()1440 actual = yield self.be.file_sync_settings_info()
1404 expected = self.default_settings.copy()1441 expected = self.default_settings.copy()
@@ -1408,17 +1445,17 @@
1408 @inlineCallbacks1445 @inlineCallbacks
1409 def test_file_sync_settings_info(self):1446 def test_file_sync_settings_info(self):
1410 """The settings_info method exercises its callback."""1447 """The settings_info method exercises its callback."""
1411 backend.sd_client.limits = {"download": 1000, "upload": 100}1448 self.be.sd_client.limits = {"download": 1000, "upload": 100}
1412 expected = {1449 expected = {
1413 backend.AUTOCONNECT_KEY: backend.sd_client.autoconnect,1450 backend.AUTOCONNECT_KEY: self.be.sd_client.autoconnect,
1414 backend.SHOW_ALL_NOTIFICATIONS_KEY:1451 backend.SHOW_ALL_NOTIFICATIONS_KEY:
1415 backend.sd_client.show_all_notifications,1452 self.be.sd_client.show_all_notifications,
1416 backend.SHARE_AUTOSUBSCRIBE_KEY:1453 backend.SHARE_AUTOSUBSCRIBE_KEY:
1417 backend.sd_client.share_autosubscribe,1454 self.be.sd_client.share_autosubscribe,
1418 backend.UDF_AUTOSUBSCRIBE_KEY:1455 backend.UDF_AUTOSUBSCRIBE_KEY:
1419 backend.sd_client.udf_autosubscribe,1456 self.be.sd_client.udf_autosubscribe,
1420 backend.DOWNLOAD_KEY: backend.sd_client.limits['download'],1457 backend.DOWNLOAD_KEY: self.be.sd_client.limits['download'],
1421 backend.UPLOAD_KEY: backend.sd_client.limits['upload'],1458 backend.UPLOAD_KEY: self.be.sd_client.limits['upload'],
1422 }1459 }
1423 result = yield self.be.file_sync_settings_info()1460 result = yield self.be.file_sync_settings_info()
1424 self.assertEqual(expected, result)1461 self.assertEqual(expected, result)
@@ -1453,8 +1490,8 @@
1453 setting_name = backend.DOWNLOAD_KEY1490 setting_name = backend.DOWNLOAD_KEY
1454 yield self.be.change_file_sync_settings({setting_name: new_value})1491 yield self.be.change_file_sync_settings({setting_name: new_value})
14551492
1456 self.assertEqual(backend.sd_client.throttling, True)1493 self.assertEqual(self.be.sd_client.throttling, True)
1457 self.assertEqual(backend.sd_client.limits,1494 self.assertEqual(self.be.sd_client.limits,
1458 {'download': new_value, 'upload': -1})1495 {'download': new_value, 'upload': -1})
14591496
1460 @inlineCallbacks1497 @inlineCallbacks
@@ -1464,8 +1501,8 @@
1464 setting_name = backend.UPLOAD_KEY1501 setting_name = backend.UPLOAD_KEY
1465 yield self.be.change_file_sync_settings({setting_name: new_value})1502 yield self.be.change_file_sync_settings({setting_name: new_value})
14661503
1467 self.assertEqual(backend.sd_client.throttling, True)1504 self.assertEqual(self.be.sd_client.throttling, True)
1468 self.assertEqual(backend.sd_client.limits,1505 self.assertEqual(self.be.sd_client.limits,
1469 {'download': -1, 'upload': new_value})1506 {'download': -1, 'upload': new_value})
14701507
1471 @inlineCallbacks1508 @inlineCallbacks
@@ -1486,8 +1523,8 @@
1486 setting_name = backend.UPLOAD_KEY1523 setting_name = backend.UPLOAD_KEY
1487 yield self.be.change_file_sync_settings({setting_name: -1})1524 yield self.be.change_file_sync_settings({setting_name: -1})
14881525
1489 self.assertEqual(backend.sd_client.throttling, False)1526 self.assertEqual(self.be.sd_client.throttling, False)
1490 self.assertEqual(backend.sd_client.limits,1527 self.assertEqual(self.be.sd_client.limits,
1491 {'download': -1, 'upload': -1})1528 {'download': -1, 'upload': -1})
14921529
1493 @inlineCallbacks1530 @inlineCallbacks
@@ -1508,8 +1545,8 @@
1508 setting_name = backend.DOWNLOAD_KEY1545 setting_name = backend.DOWNLOAD_KEY
1509 yield self.be.change_file_sync_settings({setting_name: -1})1546 yield self.be.change_file_sync_settings({setting_name: -1})
15101547
1511 self.assertEqual(backend.sd_client.throttling, False)1548 self.assertEqual(self.be.sd_client.throttling, False)
1512 self.assertEqual(backend.sd_client.limits,1549 self.assertEqual(self.be.sd_client.limits,
1513 {'download': -1, 'upload': -1})1550 {'download': -1, 'upload': -1})
15141551
1515 @inlineCallbacks1552 @inlineCallbacks
@@ -1528,12 +1565,12 @@
1528 # the call we want to test1565 # the call we want to test
1529 yield self.be.restore_file_sync_settings()1566 yield self.be.restore_file_sync_settings()
15301567
1531 self.assertEqual(backend.sd_client.autoconnect, True)1568 self.assertEqual(self.be.sd_client.autoconnect, True)
1532 self.assertEqual(backend.sd_client.show_all_notifications, True)1569 self.assertEqual(self.be.sd_client.show_all_notifications, True)
1533 self.assertEqual(backend.sd_client.share_autosubscribe, False)1570 self.assertEqual(self.be.sd_client.share_autosubscribe, False)
1534 self.assertEqual(backend.sd_client.udf_autosubscribe, False)1571 self.assertEqual(self.be.sd_client.udf_autosubscribe, False)
1535 self.assertEqual(backend.sd_client.throttling, False)1572 self.assertEqual(self.be.sd_client.throttling, False)
1536 self.assertEqual(backend.sd_client.limits,1573 self.assertEqual(self.be.sd_client.limits,
1537 {'download': -1, 'upload': -1})1574 {'download': -1, 'upload': -1})
15381575
1539 result = yield self.be.file_sync_settings_info()1576 result = yield self.be.file_sync_settings_info()
15401577
=== modified file 'ubuntuone/controlpanel/tests/test_login_client.py'
--- ubuntuone/controlpanel/tests/test_login_client.py 2011-07-22 21:26:48 +0000
+++ ubuntuone/controlpanel/tests/test_login_client.py 2011-08-25 19:40:18 +0000
@@ -19,10 +19,6 @@
19"""Tests for the service when accessing the login client."""19"""Tests for the service when accessing the login client."""
2020
21from twisted.internet import defer21from twisted.internet import defer
22# No name 'credentials' in module 'ubuntuone.platform'
23# pylint: disable=E0611
24from ubuntuone.platform import credentials
25# pylint: enable=E0611
2622
27from ubuntuone.controlpanel import login_client23from ubuntuone.controlpanel import login_client
28from ubuntuone.controlpanel.tests import TestCase, TOKEN24from ubuntuone.controlpanel.tests import TestCase, TOKEN
@@ -49,13 +45,23 @@
49 FakedCredentialsManagementTool.credentials = None45 FakedCredentialsManagementTool.credentials = None
50 yield46 yield
5147
48 @defer.inlineCallbacks
49 def login(self):
50 """Create credentials for Ubuntu One by logging in."""
51 yield defer.succeed(FakedCredentialsManagementTool.credentials)
52
53 @defer.inlineCallbacks
54 def register(self):
55 """Create credentials for Ubuntu One by registering."""
56 yield defer.succeed(FakedCredentialsManagementTool.credentials)
57
5258
53class BaseTestCase(TestCase):59class BaseTestCase(TestCase):
54 """Base TestCase for the login client methods."""60 """Base TestCase for the login client methods."""
5561
56 def setUp(self):62 def setUp(self):
57 super(BaseTestCase, self).setUp()63 super(BaseTestCase, self).setUp()
58 self.patch(credentials, 'CredentialsManagementTool',64 self.patch(login_client, 'CredentialsManagementTool',
59 FakedCredentialsManagementTool)65 FakedCredentialsManagementTool)
60 FakedCredentialsManagementTool.credentials = TOKEN66 FakedCredentialsManagementTool.credentials = TOKEN
6167
@@ -87,7 +93,7 @@
8793
88 @defer.inlineCallbacks94 @defer.inlineCallbacks
89 def test_clear_credentials(self):95 def test_clear_credentials(self):
90 """The credentials are properly retrieved."""96 """The credentials are properly cleared."""
91 yield login_client.clear_credentials()97 yield login_client.clear_credentials()
92 self.assertEqual(None, FakedCredentialsManagementTool.credentials)98 self.assertEqual(None, FakedCredentialsManagementTool.credentials)
9399
@@ -98,3 +104,39 @@
98 self.fake_fail)104 self.fake_fail)
99 yield self.assertFailure(login_client.clear_credentials(),105 yield self.assertFailure(login_client.clear_credentials(),
100 CustomError)106 CustomError)
107
108
109class LoginTestCase(BaseTestCase):
110 """Test for the login method."""
111
112 @defer.inlineCallbacks
113 def test_login(self):
114 """The credentials are properly retrieved."""
115 yield login_client.login()
116 self.assertEqual(TOKEN, FakedCredentialsManagementTool.credentials)
117
118 @defer.inlineCallbacks
119 def test_login_throws_an_error(self):
120 """If login fails, the error is propagated."""
121 self.patch(FakedCredentialsManagementTool, 'login',
122 self.fake_fail)
123 yield self.assertFailure(login_client.login(),
124 CustomError)
125
126
127class RegisterTestCase(BaseTestCase):
128 """Test for the register method."""
129
130 @defer.inlineCallbacks
131 def test_register(self):
132 """The credentials are properly retrieved."""
133 yield login_client.register()
134 self.assertEqual(TOKEN, FakedCredentialsManagementTool.credentials)
135
136 @defer.inlineCallbacks
137 def test_register_throws_an_error(self):
138 """If register fails, the error is propagated."""
139 self.patch(FakedCredentialsManagementTool, 'register',
140 self.fake_fail)
141 yield self.assertFailure(login_client.register(),
142 CustomError)
101143
=== modified file 'ubuntuone/controlpanel/tests/test_sd_client.py'
--- ubuntuone/controlpanel/tests/test_sd_client.py 2011-07-22 21:26:48 +0000
+++ ubuntuone/controlpanel/tests/test_sd_client.py 2011-08-25 19:40:18 +0000
@@ -149,7 +149,7 @@
149 @inlineCallbacks149 @inlineCallbacks
150 def get_shares(self):150 def get_shares(self):
151 """Get the list of shares (accepted or not)."""151 """Get the list of shares (accepted or not)."""
152 result = yield FakedSyncDaemonTool.shares.values()152 result = yield self.shares.values()
153 returnValue(sorted(result))153 returnValue(sorted(result))
154154
155 def refresh_shares(self):155 def refresh_shares(self):
@@ -169,38 +169,38 @@
169 if path == '': # simulate an error169 if path == '': # simulate an error
170 raise CustomError(path)170 raise CustomError(path)
171171
172 folder = FakedSyncDaemonTool.create_udf(path)172 folder = self.create_udf(path)
173 return folder173 return folder
174174
175 def delete_folder(self, folder_id):175 def delete_folder(self, folder_id):
176 """Delete a user defined folder given its id."""176 """Delete a user defined folder given its id."""
177 if folder_id not in FakedSyncDaemonTool.folders:177 if folder_id not in self.folders:
178 raise CustomError(folder_id)178 raise CustomError(folder_id)
179179
180 folder = FakedSyncDaemonTool.folders.pop(folder_id)180 folder = self.folders.pop(folder_id)
181 return folder181 return folder
182182
183 @inlineCallbacks183 @inlineCallbacks
184 def subscribe_folder(self, folder_id):184 def subscribe_folder(self, folder_id):
185 """Subscribe to a user defined folder given its id."""185 """Subscribe to a user defined folder given its id."""
186 yield FakedSyncDaemonTool._set_folder_attr(folder_id,186 yield self._set_folder_attr(folder_id,
187 u'subscribed', True)187 u'subscribed', True)
188188
189 @inlineCallbacks189 @inlineCallbacks
190 def unsubscribe_folder(self, folder_id):190 def unsubscribe_folder(self, folder_id):
191 """Unsubscribe from a user defined folder given its id."""191 """Unsubscribe from a user defined folder given its id."""
192 yield FakedSyncDaemonTool._set_folder_attr(folder_id,192 yield self._set_folder_attr(folder_id,
193 u'subscribed', False)193 u'subscribed', False)
194194
195 @inlineCallbacks195 @inlineCallbacks
196 def get_folders(self):196 def get_folders(self):
197 """Return the list of folders (a list of dicts)."""197 """Return the list of folders (a list of dicts)."""
198 result = yield FakedSyncDaemonTool.folders.values()198 result = yield self.folders.values()
199 returnValue(sorted(result))199 returnValue(sorted(result))
200200
201 def get_folder_info(self, path):201 def get_folder_info(self, path):
202 """Call the get_info method for a UDF path."""202 """Call the get_info method for a UDF path."""
203 for _, content in FakedSyncDaemonTool.folders.iteritems():203 for _, content in self.folders.iteritems():
204 if content['path'] == path:204 if content['path'] == path:
205 return content205 return content
206 else:206 else:
@@ -322,9 +322,10 @@
322 def setUp(self):322 def setUp(self):
323 super(BaseTestCase, self).setUp()323 super(BaseTestCase, self).setUp()
324 self.patch(tools, 'SyncDaemonTool', FakedSyncDaemonTool)324 self.patch(tools, 'SyncDaemonTool', FakedSyncDaemonTool)
325 FakedSyncDaemonTool.shares.clear()325 self.sd = sd_client.SyncDaemonClient()
326 FakedSyncDaemonTool.folders.clear()326 self.sd.proxy.shares.clear()
327 FakedSyncDaemonTool.called.clear()327 self.sd.proxy.folders.clear()
328 self.sd.proxy.called.clear()
328329
329 def fake_fail(self, *a):330 def fake_fail(self, *a):
330 """Fake a failure."""331 """Fake a failure."""
@@ -337,79 +338,79 @@
337 @inlineCallbacks338 @inlineCallbacks
338 def test_throttle_limits_returned(self):339 def test_throttle_limits_returned(self):
339 """When SD returns the limits, they are handled."""340 """When SD returns the limits, they are handled."""
340 limits = yield sd_client.get_throttling_limits()341 limits = yield self.sd.get_throttling_limits()
341 self.assertEqual(limits, FakedSyncDaemonTool.settings['limits'])342 self.assertEqual(limits, self.sd.proxy.settings['limits'])
342343
343 @inlineCallbacks344 @inlineCallbacks
344 def test_getting_throttle_limits_throws_an_error(self):345 def test_getting_throttle_limits_throws_an_error(self):
345 """Handle error when getting the limits."""346 """Handle error when getting the limits."""
346 self.patch(FakedSyncDaemonTool, 'get_throttling_limits',347 self.patch(self.sd.proxy, 'get_throttling_limits',
347 self.fake_fail)348 self.fake_fail)
348 yield self.assertFailure(sd_client.get_throttling_limits(),349 yield self.assertFailure(self.sd.get_throttling_limits(),
349 CustomError)350 CustomError)
350351
351 @inlineCallbacks352 @inlineCallbacks
352 def test_throttle_limits_set(self):353 def test_throttle_limits_set(self):
353 """Setting the limits."""354 """Setting the limits."""
354 yield sd_client.set_throttling_limits(SAMPLE_LIMITS)355 yield self.sd.set_throttling_limits(SAMPLE_LIMITS)
355 actual = sd_client.get_throttling_limits()356 actual = self.sd.get_throttling_limits()
356 self.assertEqual(actual, SAMPLE_LIMITS)357 self.assertEqual(actual, SAMPLE_LIMITS)
357358
358 @inlineCallbacks359 @inlineCallbacks
359 def test_setting_throttle_limits_throws_an_error(self):360 def test_setting_throttle_limits_throws_an_error(self):
360 """Handle error when setting the limits."""361 """Handle error when setting the limits."""
361 self.patch(FakedSyncDaemonTool, 'set_throttling_limits',362 self.patch(self.sd.proxy, 'set_throttling_limits',
362 self.fake_fail)363 self.fake_fail)
363 d = sd_client.set_throttling_limits(SAMPLE_LIMITS)364 d = self.sd.set_throttling_limits(SAMPLE_LIMITS)
364 yield self.assertFailure(d, CustomError)365 yield self.assertFailure(d, CustomError)
365366
366 @inlineCallbacks367 @inlineCallbacks
367 def test_throttle_get(self):368 def test_throttle_get(self):
368 """Getting the throttling state."""369 """Getting the throttling state."""
369 FakedSyncDaemonTool.settings['throttling_enabled'] = False370 self.sd.proxy.settings['throttling_enabled'] = False
370 throttling = yield sd_client.bandwidth_throttling_enabled()371 throttling = yield self.sd.bandwidth_throttling_enabled()
371 self.assertEqual(throttling, False)372 self.assertEqual(throttling, False)
372373
373 FakedSyncDaemonTool.settings['throttling_enabled'] = True374 self.sd.proxy.settings['throttling_enabled'] = True
374 throttling = yield sd_client.bandwidth_throttling_enabled()375 throttling = yield self.sd.bandwidth_throttling_enabled()
375 self.assertEqual(throttling, True)376 self.assertEqual(throttling, True)
376377
377 @inlineCallbacks378 @inlineCallbacks
378 def test_throttle_get_throws_an_error(self):379 def test_throttle_get_throws_an_error(self):
379 """Handle error when getting the throttling state."""380 """Handle error when getting the throttling state."""
380 self.patch(FakedSyncDaemonTool, 'is_throttling_enabled',381 self.patch(self.sd.proxy, 'is_throttling_enabled',
381 self.fake_fail)382 self.fake_fail)
382 yield self.assertFailure(sd_client.bandwidth_throttling_enabled(),383 yield self.assertFailure(self.sd.bandwidth_throttling_enabled(),
383 CustomError)384 CustomError)
384385
385 @inlineCallbacks386 @inlineCallbacks
386 def test_throttle_enable(self):387 def test_throttle_enable(self):
387 """Enabling bw throttling."""388 """Enabling bw throttling."""
388 FakedSyncDaemonTool.settings['throttling_enabled'] = False389 self.sd.proxy.settings['throttling_enabled'] = False
389 yield sd_client.enable_bandwidth_throttling()390 yield self.sd.enable_bandwidth_throttling()
390 self.assertTrue(FakedSyncDaemonTool.settings['throttling_enabled'])391 self.assertTrue(self.sd.proxy.settings['throttling_enabled'])
391392
392 @inlineCallbacks393 @inlineCallbacks
393 def test_throttle_enable_throws_an_error(self):394 def test_throttle_enable_throws_an_error(self):
394 """Handle error when enabling throttling."""395 """Handle error when enabling throttling."""
395 self.patch(FakedSyncDaemonTool, 'enable_throttling',396 self.patch(self.sd.proxy, 'enable_throttling',
396 self.fake_fail)397 self.fake_fail)
397 yield self.assertFailure(sd_client.enable_bandwidth_throttling(),398 yield self.assertFailure(self.sd.enable_bandwidth_throttling(),
398 CustomError)399 CustomError)
399400
400 @inlineCallbacks401 @inlineCallbacks
401 def test_throttle_disable(self):402 def test_throttle_disable(self):
402 """Disabling bw throttling."""403 """Disabling bw throttling."""
403 FakedSyncDaemonTool.settings['throttling_enabled'] = True404 self.sd.proxy.settings['throttling_enabled'] = True
404 yield sd_client.disable_bandwidth_throttling()405 yield self.sd.disable_bandwidth_throttling()
405 self.assertFalse(FakedSyncDaemonTool.settings['throttling_enabled'])406 self.assertFalse(self.sd.proxy.settings['throttling_enabled'])
406407
407 @inlineCallbacks408 @inlineCallbacks
408 def test_throttle_disable_throws_an_error(self):409 def test_throttle_disable_throws_an_error(self):
409 """Handle error when disabling throttling."""410 """Handle error when disabling throttling."""
410 self.patch(FakedSyncDaemonTool, 'enable_throttling',411 self.patch(self.sd.proxy, 'enable_throttling',
411 self.fake_fail)412 self.fake_fail)
412 yield self.assertFailure(sd_client.disable_bandwidth_throttling(),413 yield self.assertFailure(self.sd.disable_bandwidth_throttling(),
413 CustomError)414 CustomError)
414415
415416
@@ -420,53 +421,56 @@
420 # pylint: disable=E1120421 # pylint: disable=E1120
421422
422 name = 'show_all_notifications'423 name = 'show_all_notifications'
423 getter = lambda _: sd_client.show_all_notifications_enabled()424
424 enabler = lambda _: sd_client.enable_show_all_notifications()425 def setUp(self):
425 disabler = lambda _: sd_client.disable_show_all_notifications()426 super(NotificationsTestCase, self).setUp()
427 self.getter = getattr(self.sd, '%s_enabled' % self.name)
428 self.enabler = getattr(self.sd, 'enable_%s' % self.name)
429 self.disabler = getattr(self.sd, 'disable_%s' % self.name)
426430
427 @inlineCallbacks431 @inlineCallbacks
428 def test_get_value(self):432 def test_get_value(self):
429 """Getting the show_all_notifications state."""433 """Getting the show_all_notifications state."""
430 FakedSyncDaemonTool.settings[self.name] = False434 self.sd.proxy.settings[self.name] = False
431 state = yield self.getter()435 state = yield self.getter()
432 self.assertEqual(state, False)436 self.assertEqual(state, False)
433 FakedSyncDaemonTool.settings[self.name] = True437 self.sd.proxy.settings[self.name] = True
434 state = yield self.getter()438 state = yield self.getter()
435 self.assertEqual(state, True)439 self.assertEqual(state, True)
436440
437 @inlineCallbacks441 @inlineCallbacks
438 def test_get_value_throws_an_error(self):442 def test_get_value_throws_an_error(self):
439 """Handle error when getting the show_all_notifications state."""443 """Handle error when getting the show_all_notifications state."""
440 self.patch(FakedSyncDaemonTool, 'is_%s_enabled' % self.name,444 self.patch(self.sd.proxy, 'is_%s_enabled' % self.name,
441 self.fake_fail)445 self.fake_fail)
442 yield self.assertFailure(self.getter(), CustomError)446 yield self.assertFailure(self.getter(), CustomError)
443447
444 @inlineCallbacks448 @inlineCallbacks
445 def test_enable(self):449 def test_enable(self):
446 """Enabling show_all_notifications."""450 """Enabling show_all_notifications."""
447 FakedSyncDaemonTool.settings[self.name] = False451 self.sd.proxy.settings[self.name] = False
448 yield self.enabler()452 yield self.enabler()
449 self.assertTrue(FakedSyncDaemonTool.settings[self.name])453 self.assertTrue(self.sd.proxy.settings[self.name])
450454
451 @inlineCallbacks455 @inlineCallbacks
452 def test_enable_throws_an_error(self):456 def test_enable_throws_an_error(self):
453 """Handle error when enabling show_all_notifications."""457 """Handle error when enabling show_all_notifications."""
454 self.patch(FakedSyncDaemonTool, 'enable_%s' % self.name,458 self.patch(self.sd.proxy, 'enable_%s' % self.name,
455 self.fake_fail)459 self.fake_fail)
456 yield self.assertFailure(self.enabler(), CustomError)460 yield self.assertFailure(self.enabler(), CustomError)
457461
458 @inlineCallbacks462 @inlineCallbacks
459 def test_disable(self):463 def test_disable(self):
460 """Disabling show_all_notifications."""464 """Disabling show_all_notifications."""
461 FakedSyncDaemonTool.settings[self.name] = True465 self.sd.proxy.settings[self.name] = True
462 yield self.disabler()466 yield self.disabler()
463 value = FakedSyncDaemonTool.settings[self.name]467 value = self.sd.proxy.settings[self.name]
464 self.assertFalse(value)468 self.assertFalse(value)
465469
466 @inlineCallbacks470 @inlineCallbacks
467 def test_disable_throws_an_error(self):471 def test_disable_throws_an_error(self):
468 """Handle error when disabling show_all_notifications."""472 """Handle error when disabling show_all_notifications."""
469 self.patch(FakedSyncDaemonTool, 'enable_%s' % self.name,473 self.patch(self.sd.proxy, 'enable_%s' % self.name,
470 self.fake_fail)474 self.fake_fail)
471 yield self.assertFailure(self.disabler(), CustomError)475 yield self.assertFailure(self.disabler(), CustomError)
472476
@@ -475,27 +479,18 @@
475 """Test for the autoconnect syncdaemon client methods."""479 """Test for the autoconnect syncdaemon client methods."""
476480
477 name = 'autoconnect'481 name = 'autoconnect'
478 getter = lambda _: sd_client.autoconnect_enabled()
479 enabler = lambda _: sd_client.enable_autoconnect()
480 disabler = lambda _: sd_client.disable_autoconnect()
481482
482483
483class ShareAutosubscribeTestCase(NotificationsTestCase):484class ShareAutosubscribeTestCase(NotificationsTestCase):
484 """Test for the share_autosubscribe syncdaemon client methods."""485 """Test for the share_autosubscribe syncdaemon client methods."""
485486
486 name = 'share_autosubscribe'487 name = 'share_autosubscribe'
487 getter = lambda _: sd_client.share_autosubscribe_enabled()
488 enabler = lambda _: sd_client.enable_share_autosubscribe()
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: