Merge lp:~nataliabidart/ubuntuone-control-panel/update-file-sync-status into lp:ubuntuone-control-panel

Proposed by Natalia Bidart
Status: Merged
Approved by: Natalia Bidart
Approved revision: 159
Merged at revision: 155
Proposed branch: lp:~nataliabidart/ubuntuone-control-panel/update-file-sync-status
Merge into: lp:ubuntuone-control-panel
Prerequisite: lp:~nataliabidart/ubuntuone-control-panel/use-sdtool
Diff against target: 1187 lines (+647/-246)
17 files modified
.bzrignore (+1/-0)
data/qt/controlpanel.ui (+41/-32)
data/qt/filesyncstatus.ui (+45/-0)
data/qt/images.qrc (+6/-0)
data/qt/mainwindow.ui (+1/-1)
run-tests (+2/-1)
ubuntuone/controlpanel/backend.py (+2/-0)
ubuntuone/controlpanel/gui/__init__.py (+5/-0)
ubuntuone/controlpanel/gui/gtk/gui.py (+8/-6)
ubuntuone/controlpanel/gui/gtk/tests/__init__.py (+15/-108)
ubuntuone/controlpanel/gui/qt/controlpanel.py (+6/-41)
ubuntuone/controlpanel/gui/qt/filesyncstatus.py (+151/-0)
ubuntuone/controlpanel/gui/qt/gui.py (+34/-0)
ubuntuone/controlpanel/gui/qt/tests/__init__.py (+38/-15)
ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py (+0/-42)
ubuntuone/controlpanel/gui/qt/tests/test_filesyncstatus.py (+159/-0)
ubuntuone/controlpanel/gui/tests/__init__.py (+133/-0)
To merge this branch: bzr merge lp:~nataliabidart/ubuntuone-control-panel/update-file-sync-status
Reviewer Review Type Date Requested Status
Manuel de la Peña (community) Approve
Alejandro J. Cura (community) Approve
Shane Fagan (community) Approve
Review via email: mp+62575@code.launchpad.net

Commit message

- File Sync status is now updated using symbols and colours, and the action button is properly updated as well.

Description of the change

To test this branch, you need to have latest nightlies installed and updated.

To run the test suites, do:

./run-tests
./run-tests -qt

To test IRL, open the QT UI with:

DEBUG=True PYTHONPATH=. ./bin/ubuntuone-control-panel-qt

and modify syncdaemon "state on the side" (u1sdtool -d on the terminal). You will see how the file sync status is updated, and how the button next to the status offers different actions according to the status. You can click on the buttons and modify syncdaemon status.

To post a comment you must log in.
Revision history for this message
Shane Fagan (shanepatrickfagan) :
review: Approve
Revision history for this message
Manuel de la Peña (mandel) wrote :
Download full text (9.7 KiB)

As with the other branch I get the following when running the tests:

E1101: 82:show_all_notifications_enabled: Instance of 'SyncDaemonTool' has no 'is_show_all_notifications_enabled' member
E1101: 87:enable_show_all_notifications: Instance of 'SyncDaemonTool' has no 'enable_show_all_notifications' member
E1101: 92:disable_show_all_notifications: Instance of 'SyncDaemonTool' has no 'enable_show_all_notifications' member
E1101: 97:get_root_dir: Instance of 'SyncDaemonTool' has no 'get_root_dir' member
E1101:102:get_shares_dir: Instance of 'SyncDaemonTool' has no 'get_shares_dir' member
E1101:107:get_shares_dir_link: Instance of 'SyncDaemonTool' has no 'get_shares_dir_link' member

when not running the ui tests. With the ui tests I get the following:

Must run 2nd check in another process.
===============================================================================
[ERROR]: ubuntuone.controlpanel.gui.qt.tests.test_gui.MainWindowTestCase.test_close_callback_can_be_none

Traceback (most recent call last):
  File "/home/mandel/Projects/ubuntuone-control-panel/update-file-sync-status/ubuntuone/controlpanel/gui/qt/tests/__init__.py", line 39, in inner
    result = test(instance)
  File "/home/mandel/Projects/ubuntuone-control-panel/update-file-sync-status/ubuntuone/controlpanel/gui/qt/tests/__init__.py", line 80, in setUp
    self.ui = self.class_ui(**self.kwargs)
  File "/home/mandel/Projects/ubuntuone-control-panel/update-file-sync-status/ubuntuone/controlpanel/gui/qt/gui.py", line 41, in __init__
    self.ui.setupUi(self)
  File "/home/mandel/Projects/ubuntuone-control-panel/update-file-sync-status/ubuntuone/controlpanel/gui/qt/ui/mainwindow_ui.py", line 32, in setupUi
    self.control_panel = ControlPanel(self.centralwidget)
  File "/home/mandel/Projects/ubuntuone-control-panel/update-file-sync-status/ubuntuone/controlpanel/gui/qt/controlpanel.py", line 39, in __init__
    self.ui.setupUi(self)
  File "/home/mandel/Projects/ubuntuone-control-panel/update-file-sync-status/ubuntuone/controlpanel/gui/qt/ui/controlpanel_ui.py", line 68, in setupUi
    self.profile_tab = ProfilePanel()
  File "/home/mandel/Projects/ubuntuone-control-panel/update-file-sync-status/ubuntuone/controlpanel/gui/qt/profile.py", line 33, in __init__
    self.ui.setupUi(self)
  File "/home/mandel/Projects/ubuntuone-control-panel/update-file-sync-status/ubuntuone/controlpanel/gui/qt/ui/profile_ui.py", line 77, in setupUi
    self.retranslateUi(Form)
  File "/home/mandel/Projects/ubuntuone-control-panel/update-file-sync-status/ubuntuone/controlpanel/gui/qt/ui/profile_ui.py", line 91, in retranslateUi
    self.email_lineedit.setPlaceholderText(_('<email address hidden>'))
exceptions.AttributeError: 'QLineEdit' object has no attribute 'setPlaceholderText'
===============================================================================
[ERROR]: ubuntuone.controlpanel.gui.qt.tests.test_gui.MainWindowTestCase.test_close_event_calls_custom_close_callback

Traceback (most recent call last):
  File "/home/mandel/Projects/ubuntuone-control-panel/update-file-sync-status/ubuntuone/controlpanel/gui/qt/tests/__init__.py", line 39, in inner
    result = test(instance)
  File "/home/mandel/P...

Read more...

review: Needs Fixing
Revision history for this message
Alejandro J. Cura (alecu) wrote :

Running on Natty, I'm only getting the REQUEST_NAME_REPLY_EXISTS error due to the u1-dev-tools bug; the code looks fine otherwise.

review: Approve
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

Manuel,

I'm guessing you have an old version of QT? The files where you're having errors on are those generated by QT, for example:

File "/home/mandel/Projects/ubuntuone-control-panel/update-file-sync-status/ubuntuone/controlpanel/gui/qt/ui/profile_ui.py"

We're working with code for QT 4.4 or higher, what version are you running?

Revision history for this message
Manuel de la Peña (mandel) wrote :

I'm using Qt 4.7.4 I really don't understand why would that be happening, I'll take a look at the Qt state of my test vm.

Revision history for this message
Manuel de la Peña (mandel) :
review: Approve
Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :

The attempt to merge lp:~nataliabidart/ubuntuone-control-panel/update-file-sync-status into lp:ubuntuone-control-panel failed. Below is the output from the failed tests.

running build
Compiled data/qt/mainwindow.ui into ubuntuone/controlpanel/gui/qt/ui/mainwindow_ui.py
Compiled data/qt/filesyncstatus.ui into ubuntuone/controlpanel/gui/qt/ui/filesyncstatus_ui.py
Compiled data/qt/folders.ui into ubuntuone/controlpanel/gui/qt/ui/folders_ui.py
Compiled data/qt/devices.ui into ubuntuone/controlpanel/gui/qt/ui/devices_ui.py
Compiled data/qt/preferences.ui into ubuntuone/controlpanel/gui/qt/ui/preferences_ui.py

/usr/lib/pymodules/python2.7/gtk-2.0/gtk/__init__.py:57: GtkWarning: could not open display
  warnings.warn(str(e), _gtk.Warning)
ERROR: Python module aptdaemon.defer not found
ERROR: Python module ubuntuone.controlpanel.gui.qt.ui not found
ERROR: Python module ubuntuone.controlpanel.gui.qt.ui not found
ERROR: Python module ubuntuone.controlpanel.gui.qt.ui not found
ERROR: Python module ubuntuone.controlpanel.gui.qt.ui not found
ERROR: Python module ubuntuone.controlpanel.constants not found
ERROR: Python module ubuntuone.controlpanel.gui.qt.ui not found
ERROR: Python module ubuntuone.controlpanel.gui.qt.ui not found
ERROR: Python module qtreactor not found
ERROR: Python module ubuntuone.controlpanel.gui.qt.gui not found
ERROR: Python module ubuntuone.controlpanel.gui.qt not found
ERROR: Python module ubuntuone.controlpanel.gui.qt.tests not found
ERROR: Python module ubuntuone.controlpanel.gui.qt not found
ERROR: Python module ubuntuone.controlpanel.gui.qt.tests not found
ERROR: Python module ubuntuone.controlpanel.gui.qt.ui not found
ERROR: Python module ubuntuone.controlpanel.gui.qt not found
ERROR: Python module ubuntuone.controlpanel.gui.qt.tests not found
ERROR: Python module ubuntuone.controlpanel.gui.qt.ui not found
ERROR: Python module ubuntuone.controlpanel.gui.qt not found
/home/otto/tarmac-builds/ubuntuone-control-panel/trunk/ubuntuone/controlpanel/gui/gtk/tests/test_gui_basic.py:438: Warning: invalid (NULL) pointer instance
  kwargs = {'main_window': gui.gtk.Window()}
/home/otto/tarmac-builds/ubuntuone-control-panel/trunk/ubuntuone/controlpanel/gui/gtk/tests/test_gui_basic.py:438: Warning: g_signal_connect_data: assertion `G_TYPE_CHECK_INSTANCE (instance)' failed
  kwargs = {'main_window': gui.gtk.Window()}
sh: pyrcc4: not found
Traceback (most recent call last):
  File "./setup.py", line 246, in <module>
    'clean': ControlPanelClean,
  File "/usr/lib/python2.7/dist-packages/DistUtilsExtra/auto.py", line 98, in setup
    distutils.core.setup(**attrs)
  File "/usr/lib/python2.7/distutils/core.py", line 152, in setup
    dist.run_commands()
  File "/usr/lib/python2.7/distutils/dist.py", line 953, in run_commands
    self.run_command(cmd)
  File "/usr/lib/python2.7/distutils/dist.py", line 972, in run_command
    cmd_obj.run()
  File "./setup.py", line 169, in run
    self.compile_rc(os.path.join(dirpath, filename))
  File "./setup.py", line 119, in compile_rc
    + 'for resource file %s', py_file, qrc_file)
TypeError: warn() takes exactly 2 arguments (4 given)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2011-05-24 14:20:18 +0000
3+++ .bzrignore 2011-05-31 18:28:26 +0000
4@@ -6,4 +6,5 @@
5 po/ubuntuone-control-panel.pot
6 ubuntuone/controlpanel/constants.py
7 ubuntuone/controlpanel/gui/qt/ui/*_ui.py
8+ubuntuone/controlpanel/gui/qt/ui/*_rc.py
9 qtreactor/
10\ No newline at end of file
11
12=== modified file 'data/qt/controlpanel.ui'
13--- data/qt/controlpanel.ui 2011-05-24 14:20:18 +0000
14+++ data/qt/controlpanel.ui 2011-05-31 18:28:26 +0000
15@@ -7,7 +7,7 @@
16 <x>0</x>
17 <y>0</y>
18 <width>646</width>
19- <height>421</height>
20+ <height>487</height>
21 </rect>
22 </property>
23 <property name="sizePolicy">
24@@ -74,21 +74,7 @@
25 </spacer>
26 </item>
27 <item>
28- <widget class="QLabel" name="sync_status_label">
29- <property name="text">
30- <string>Sync in progress...</string>
31- </property>
32- <property name="buddy">
33- <cstring>change_sync_status_button</cstring>
34- </property>
35- </widget>
36- </item>
37- <item>
38- <widget class="QPushButton" name="change_sync_status_button">
39- <property name="text">
40- <string>Stop Syncing</string>
41- </property>
42- </widget>
43+ <widget class="FileSyncStatus" name="file_sync_status" native="true"/>
44 </item>
45 <item>
46 <spacer name="horizontalSpacer_2">
47@@ -178,30 +164,46 @@
48 <item>
49 <widget class="QPushButton" name="twitter_button">
50 <property name="styleSheet">
51- <string notr="true">color: #fff;
52-background-color: rgb(0, 170, 255);
53-width: 1em;
54-height: 1em;
55-spacing: 0px;
56-font: 18pt &quot;Ubuntu&quot; bold;</string>
57+ <string notr="true"/>
58 </property>
59 <property name="text">
60- <string>t</string>
61+ <string/>
62+ </property>
63+ <property name="icon">
64+ <iconset resource="images.qrc">
65+ <normaloff>:/twitter.png</normaloff>:/twitter.png</iconset>
66+ </property>
67+ <property name="iconSize">
68+ <size>
69+ <width>32</width>
70+ <height>32</height>
71+ </size>
72+ </property>
73+ <property name="flat">
74+ <bool>true</bool>
75 </property>
76 </widget>
77 </item>
78 <item>
79 <widget class="QPushButton" name="facebook_button">
80 <property name="styleSheet">
81- <string notr="true">color: #fff;
82-background-color: rgb(0, 0, 128);
83-width: 1em;
84-height: 1em;
85-spacing: 0px;
86-font: 18pt &quot;Ubuntu&quot; bold;</string>
87+ <string notr="true"/>
88 </property>
89 <property name="text">
90- <string>f</string>
91+ <string/>
92+ </property>
93+ <property name="icon">
94+ <iconset resource="images.qrc">
95+ <normaloff>:/facebook.png</normaloff>:/facebook.png</iconset>
96+ </property>
97+ <property name="iconSize">
98+ <size>
99+ <width>32</width>
100+ <height>32</height>
101+ </size>
102+ </property>
103+ <property name="flat">
104+ <bool>true</bool>
105 </property>
106 </widget>
107 </item>
108@@ -240,15 +242,22 @@
109 <header>ubuntuone.controlpanel.gui.qt.profile</header>
110 <container>1</container>
111 </customwidget>
112+ <customwidget>
113+ <class>FileSyncStatus</class>
114+ <extends>QWidget</extends>
115+ <header>ubuntuone.controlpanel.gui.qt.filesyncstatus</header>
116+ <container>1</container>
117+ </customwidget>
118 </customwidgets>
119 <tabstops>
120 <tabstop>tab_widget</tabstop>
121 <tabstop>help_button</tabstop>
122 <tabstop>twitter_button</tabstop>
123 <tabstop>facebook_button</tabstop>
124- <tabstop>change_sync_status_button</tabstop>
125 <tabstop>get_more_space_button</tabstop>
126 </tabstops>
127- <resources/>
128+ <resources>
129+ <include location="images.qrc"/>
130+ </resources>
131 <connections/>
132 </ui>
133
134=== added file 'data/qt/filesyncstatus.ui'
135--- data/qt/filesyncstatus.ui 1970-01-01 00:00:00 +0000
136+++ data/qt/filesyncstatus.ui 2011-05-31 18:28:26 +0000
137@@ -0,0 +1,45 @@
138+<?xml version="1.0" encoding="UTF-8"?>
139+<ui version="4.0">
140+ <class>Form</class>
141+ <widget class="QWidget" name="Form">
142+ <property name="geometry">
143+ <rect>
144+ <x>0</x>
145+ <y>0</y>
146+ <width>241</width>
147+ <height>47</height>
148+ </rect>
149+ </property>
150+ <property name="windowTitle">
151+ <string>Form</string>
152+ </property>
153+ <layout class="QHBoxLayout" name="horizontalLayout_2">
154+ <item>
155+ <layout class="QHBoxLayout" name="horizontalLayout">
156+ <item>
157+ <widget class="QLabel" name="sync_status_label">
158+ <property name="text">
159+ <string>Status unknown</string>
160+ </property>
161+ <property name="buddy">
162+ <cstring>sync_status_button</cstring>
163+ </property>
164+ </widget>
165+ </item>
166+ <item>
167+ <widget class="QPushButton" name="sync_status_button">
168+ <property name="toolTip">
169+ <string>test</string>
170+ </property>
171+ <property name="text">
172+ <string>Action unknown</string>
173+ </property>
174+ </widget>
175+ </item>
176+ </layout>
177+ </item>
178+ </layout>
179+ </widget>
180+ <resources/>
181+ <connections/>
182+</ui>
183
184=== added file 'data/qt/images.qrc'
185--- data/qt/images.qrc 1970-01-01 00:00:00 +0000
186+++ data/qt/images.qrc 2011-05-31 18:28:26 +0000
187@@ -0,0 +1,6 @@
188+<RCC>
189+ <qresource prefix="/">
190+ <file>../twitter.png</file>
191+ <file>../facebook.png</file>
192+ </qresource>
193+</RCC>
194
195=== modified file 'data/qt/mainwindow.ui'
196--- data/qt/mainwindow.ui 2011-05-24 14:20:18 +0000
197+++ data/qt/mainwindow.ui 2011-05-31 18:28:26 +0000
198@@ -34,7 +34,7 @@
199 </property>
200 <layout class="QVBoxLayout" name="verticalLayout">
201 <item>
202- <widget class="ControlPanel" name="widget" native="true"/>
203+ <widget class="ControlPanel" name="control_panel" native="true"/>
204 </item>
205 </layout>
206 </widget>
207
208=== modified file 'run-tests'
209--- run-tests 2011-05-26 19:56:29 +0000
210+++ run-tests 2011-05-31 18:28:26 +0000
211@@ -39,7 +39,7 @@
212 style_check() {
213 pylint --ignore ui ubuntuone/
214 if [ -x `which pep8` ]; then
215- pep8 --exclude '.svn,CVS,.bzr,.hg,.git,*_ui.py' --repeat bin/ $MODULE
216+ pep8 --exclude '.svn,CVS,.bzr,.hg,.git,*_ui.py,*_rc.py' --repeat bin/ $MODULE
217 else
218 echo "Please install the 'pep8' package."
219 fi
220@@ -54,3 +54,4 @@
221 fi
222 style_check
223 rm -rf _trial_temp
224+rm -rf build
225
226=== modified file 'ubuntuone/controlpanel/backend.py'
227--- ubuntuone/controlpanel/backend.py 2011-05-24 18:56:16 +0000
228+++ ubuntuone/controlpanel/backend.py 2011-05-31 18:28:26 +0000
229@@ -531,6 +531,8 @@
230 else:
231 yield self.unsubscribe_volume(volume_id)
232
233+ returnValue(volume_id)
234+
235 @inlineCallbacks
236 def subscribe_volume(self, volume_id):
237 """Subscribe to 'volume_id'."""
238
239=== modified file 'ubuntuone/controlpanel/gui/__init__.py'
240--- ubuntuone/controlpanel/gui/__init__.py 2011-05-25 13:46:14 +0000
241+++ ubuntuone/controlpanel/gui/__init__.py 2011-05-31 18:28:26 +0000
242@@ -30,6 +30,11 @@
243 ORANGE = '#DD4814'
244 QUOTA_THRESHOLD = 0.95
245 SHARES_MIN_SIZE_FULL = 1048576
246+SUCCESS_COLOR = 'green'
247+
248+ERROR_ICON = u'✘'
249+SYNCING_ICON = u'⇅'
250+IDLE_ICON = u'✔'
251
252 CONTACTS_ICON = 'contacts.png'
253 FACEBOOK_LOGO = 'facebook.png'
254
255=== modified file 'ubuntuone/controlpanel/gui/gtk/gui.py'
256--- ubuntuone/controlpanel/gui/gtk/gui.py 2011-05-25 14:34:17 +0000
257+++ ubuntuone/controlpanel/gui/gtk/gui.py 2011-05-31 18:28:26 +0000
258@@ -1339,42 +1339,44 @@
259 """Backend notifies of file sync status being disabled."""
260 self._update_status(FILE_SYNC_DISABLED,
261 FILE_SYNC_ENABLE, self.on_enable_clicked,
262- '✘', 'red', FILE_SYNC_ENABLE_TOOLTIP)
263+ ERROR_ICON, ERROR_COLOR, FILE_SYNC_ENABLE_TOOLTIP)
264
265 @log_call(logger.info)
266 def on_file_sync_status_starting(self, msg=None):
267 """Backend notifies of file sync status being starting."""
268 self._update_status(FILE_SYNC_STARTING,
269 FILE_SYNC_STOP, self.on_stop_clicked,
270- '⇅', ORANGE, FILE_SYNC_STOP_TOOLTIP)
271+ SYNCING_ICON, ORANGE, FILE_SYNC_STOP_TOOLTIP)
272
273 @log_call(logger.info)
274 def on_file_sync_status_stopped(self, msg=None):
275 """Backend notifies of file sync being stopped."""
276 self._update_status(FILE_SYNC_STOPPED,
277 FILE_SYNC_START, self.on_start_clicked,
278- '✘', 'red', FILE_SYNC_START_TOOLTIP)
279+ ERROR_ICON, ERROR_COLOR, FILE_SYNC_START_TOOLTIP)
280
281 @log_call(logger.info)
282 def on_file_sync_status_disconnected(self, msg=None):
283 """Backend notifies of file sync status being ready."""
284 self._update_status(FILE_SYNC_DISCONNECTED,
285 FILE_SYNC_CONNECT, self.on_connect_clicked,
286- '✘', 'red', FILE_SYNC_CONNECT_TOOLTIP,)
287+ ERROR_ICON, ERROR_COLOR,
288+ FILE_SYNC_CONNECT_TOOLTIP,)
289
290 @log_call(logger.info)
291 def on_file_sync_status_syncing(self, msg=None):
292 """Backend notifies of file sync status being syncing."""
293 self._update_status(FILE_SYNC_SYNCING,
294 FILE_SYNC_DISCONNECT, self.on_disconnect_clicked,
295- '⇅', ORANGE, FILE_SYNC_DISCONNECT_TOOLTIP)
296+ SYNCING_ICON, ORANGE, FILE_SYNC_DISCONNECT_TOOLTIP)
297
298 @log_call(logger.info)
299 def on_file_sync_status_idle(self, msg=None):
300 """Backend notifies of file sync status being idle."""
301 self._update_status(FILE_SYNC_IDLE,
302 FILE_SYNC_DISCONNECT, self.on_disconnect_clicked,
303- '✔', 'green', FILE_SYNC_DISCONNECT_TOOLTIP)
304+ IDLE_ICON, SUCCESS_COLOR,
305+ FILE_SYNC_DISCONNECT_TOOLTIP)
306
307 @log_call(logger.error)
308 def on_file_sync_status_error(self, error_dict=None):
309
310=== modified file 'ubuntuone/controlpanel/gui/gtk/tests/__init__.py'
311--- ubuntuone/controlpanel/gui/gtk/tests/__init__.py 2011-05-24 16:05:30 +0000
312+++ ubuntuone/controlpanel/gui/gtk/tests/__init__.py 2011-05-31 18:28:26 +0000
313@@ -24,10 +24,24 @@
314
315 from ubuntuone.devtools.handlers import MementoHandler
316
317-from ubuntuone.controlpanel.backend import ControlBackend
318 from ubuntuone.controlpanel.gui.gtk import gui
319 from ubuntuone.controlpanel.gui.gtk.tests.test_package_manager import (
320 FakedTransaction)
321+# Unused imports, they are here to maintain old module API
322+# pylint: disable=W0611
323+from ubuntuone.controlpanel.gui.tests import (FakedObject,
324+ FAKE_ACCOUNT_INFO,
325+ FAKE_DEVICE_INFO,
326+ FAKE_DEVICES_INFO,
327+ FAKE_FOLDERS_INFO,
328+ FAKE_SHARES_INFO,
329+ FAKE_VOLUMES_INFO,
330+ FAKE_VOLUMES_NO_FREE_SPACE_INFO,
331+ MUSIC_FOLDER,
332+ ROOT,
333+ USER_HOME,
334+)
335+# pylint: enable=W0611
336 from ubuntuone.controlpanel.tests import TestCase
337
338
339@@ -35,91 +49,6 @@
340 # pylint: disable=W0201, W0212
341
342
343-FAKE_ACCOUNT_INFO = {'type': 'Payed', 'name': 'Test me',
344- 'email': 'test.com', 'quota_total': '12345', 'quota_used': '9999'}
345-
346-USER_HOME = '/home/tester'
347-
348-ROOT = {
349- u'volume_id': '', u'path': '/home/tester/My Ubuntu',
350- u'subscribed': 'True', u'type': ControlBackend.ROOT_TYPE,
351- u'display_name': u'My Ubuntu',
352-}
353-
354-MUSIC_FOLDER = {
355- u'volume_id': u'58236', u'subscribed': u'True',
356- u'type': ControlBackend.FOLDER_TYPE,
357- u'path': u'/home/tester/.ubuntuone/Purchased from Ubuntu One',
358- u'suggested_path': u'~/.ubuntuone/Purchased from Ubuntu One',
359- u'display_name': u'.ubuntuone/Purchased from Ubuntu One',
360-}
361-
362-FAKE_FOLDERS_INFO = [
363- # backend send this ordered by path
364- {u'volume_id': u'1', u'path': u'/home/tester/bar',
365- u'suggested_path': u'~/bar', u'subscribed': u'True',
366- u'type': ControlBackend.FOLDER_TYPE, u'display_name': u'bar'},
367- {u'volume_id': u'2', u'path': u'/home/tester/baz',
368- u'suggested_path': u'~/baz', u'subscribed': u'True',
369- u'type': ControlBackend.FOLDER_TYPE, u'display_name': u'baz'},
370- {u'volume_id': u'0', u'path': u'/home/tester/foo',
371- u'suggested_path': u'~/foo', u'subscribed': u'',
372- u'type': ControlBackend.FOLDER_TYPE, u'display_name': u'foo'},
373-]
374-
375-FAKE_SHARES_INFO = [
376- # backend send this ordered by path
377- {u'volume_id': u'1234', u'name': u'do',
378- u'path': u'/home/tester/.local/share/ubuntuone/shares/do from Other User',
379- u'subscribed': u'', u'type': ControlBackend.SHARE_TYPE,
380- u'display_name': u'do'},
381- {u'volume_id': u'5678', u'name': u're',
382- u'path': u'/home/tester/.local/share/ubuntuone/shares/re from Other User',
383- u'subscribed': u'True', u'type': ControlBackend.SHARE_TYPE,
384- u'display_name': u're'},
385-]
386-
387-FAKE_VOLUMES_INFO = [
388- (u'', u'147852369', [ROOT] + FAKE_FOLDERS_INFO),
389- (u'Other User', gui.SHARES_MIN_SIZE_FULL, FAKE_SHARES_INFO),
390-]
391-
392-FAKE_VOLUMES_NO_FREE_SPACE_INFO = [
393- (u'', u'500', [ROOT]),
394- (u'No free space', u'0',
395- [{u'volume_id': u'0', u'name': u'full', u'path': u'full-share',
396- u'subscribed': u'', u'type': ControlBackend.SHARE_TYPE,
397- u'display_name': u'something',
398- }]),
399- (u'Almost no free space', gui.SHARES_MIN_SIZE_FULL - 1,
400- [{u'volume_id': u'1', u'name': u'almostfull', u'path': u'almost-full',
401- u'subscribed': u'', u'type': ControlBackend.SHARE_TYPE,
402- u'display_name': u'yadda',
403- }]),
404-]
405-
406-FAKE_DEVICE_INFO = {
407- 'device_id': '1258-6854', 'device_name': 'Baz', 'device_type': 'Computer',
408- 'is_local': 'True', 'configurable': 'True', 'limit_bandwidth': 'True',
409- 'max_upload_speed': '1000', 'max_download_speed': '72548',
410- 'show_all_notifications': 'True',
411-}
412-
413-FAKE_DEVICES_INFO = [
414- {'device_id': '0', 'name': 'Ubuntu One @ Foo', 'type': 'Computer',
415- 'is_local': '', 'configurable': ''},
416- {'device_id': '1', 'name': 'Ubuntu One @ Bar', 'type': 'Phone',
417- 'is_local': '', 'configurable': ''},
418- {'device_id': '2', 'name': 'Ubuntu One @ Z', 'type': 'Computer',
419- 'is_local': '', 'configurable': 'True', 'limit_bandwidth': '',
420- 'max_upload_speed': '0', 'max_download_speed': '0',
421- 'show_all_notifications': ''},
422- {'device_id': '1258-6854', 'name': 'Ubuntu One @ Baz', 'type': 'Computer',
423- 'is_local': 'True', 'configurable': 'True', 'limit_bandwidth': 'True',
424- 'max_upload_speed': '1000', 'max_download_speed': '72548',
425- 'show_all_notifications': 'True'}, # local
426-]
427-
428 FAKE_REPLICATIONS_INFO = [
429 {'replication_id': 'foo', 'name': 'Bar',
430 'enabled': 'True', 'dependency': ''},
431@@ -130,28 +59,6 @@
432 ]
433
434
435-class FakedObject(object):
436- """Fake an object, record every call."""
437-
438- exposed_methods = []
439-
440- def __init__(self, *args, **kwargs):
441- self._args = args
442- self._kwargs = kwargs
443- self._called = {}
444- for i in self.exposed_methods:
445- setattr(self, i, self._record_call(i))
446-
447- def _record_call(self, func_name):
448- """Store values when calling 'func_name'."""
449-
450- def inner(*args, **kwargs):
451- """Fake 'func_name'."""
452- self._called[func_name] = (args, kwargs)
453-
454- return inner
455-
456-
457 class FakedNMState(FakedObject):
458 """Fake a NetworkManagerState."""
459
460
461=== modified file 'ubuntuone/controlpanel/gui/qt/controlpanel.py'
462--- ubuntuone/controlpanel/gui/qt/controlpanel.py 2011-05-24 18:56:16 +0000
463+++ ubuntuone/controlpanel/gui/qt/controlpanel.py 2011-05-31 18:28:26 +0000
464@@ -1,6 +1,7 @@
465 # -*- coding: utf-8 -*-
466
467 # Authors: Alejandro J. Cura <alecu@canonical.com>
468+# Authors: Natalia B Bidart <natalia.bidart@canonical.com>
469 #
470 # Copyright 2011 Canonical Ltd.
471 #
472@@ -18,33 +19,16 @@
473
474 """The user interface for the control panel for Ubuntu One."""
475
476-from PyQt4 import QtGui, QtCore
477+from PyQt4 import QtGui
478
479 from ubuntuone.controlpanel import backend
480-from ubuntuone.controlpanel.logger import setup_logging, log_call
481-# Wildcard import ubuntuone.controlpanel.gui
482-# pylint: disable=W0401, W0614
483-from ubuntuone.controlpanel.gui import *
484-# pylint: enable=W0401, W0614
485+from ubuntuone.controlpanel.logger import setup_logging
486 from ubuntuone.controlpanel.gui.qt.ui import controlpanel_ui
487
488
489 logger = setup_logging('qt.controlpanel')
490
491
492-FILE_SYNC_STATUS = {
493- backend.FILE_SYNC_DISCONNECTED: FILE_SYNC_DISCONNECTED,
494- backend.FILE_SYNC_DISABLED: FILE_SYNC_DISABLED,
495- backend.FILE_SYNC_ERROR: FILE_SYNC_ERROR,
496- backend.FILE_SYNC_IDLE: FILE_SYNC_IDLE,
497- backend.FILE_SYNC_STARTING: FILE_SYNC_STARTING,
498- backend.FILE_SYNC_STOPPED: FILE_SYNC_STOPPED,
499- backend.FILE_SYNC_SYNCING: FILE_SYNC_SYNCING,
500-}
501-
502-STATUS_MARKUP = u'<b>⭐</b> %s'
503-
504-
505 class ControlPanel(QtGui.QWidget):
506 """The Control Panel widget"""
507
508@@ -55,28 +39,9 @@
509 self.ui.setupUi(self)
510
511 self.backend = backend.ControlBackend()
512- self.backend.status_changed_handler = self.process_status
513-
514- # this call is crashing with MemoryError as per
515- # http://pastebin.ubuntu.com/612270/
516- #self.backend.file_sync_status()
517
518 logger.debug('%s: started.', self.__class__.__name__)
519
520- @log_call(logger.debug)
521- def process_status(self, status):
522- """Match status with signals."""
523- logger.debug('process_status: new status received %r', status)
524-
525- try:
526- status = FILE_SYNC_STATUS[status[backend.STATUS_KEY]]
527- except KeyError:
528- logger.exception('process_status: received unknown status dict %r',
529- status)
530- else:
531- msg = STATUS_MARKUP % (status,)
532- self.ui.sync_status_label.setText(msg)
533-
534- @QtCore.pyqtSlot()
535- def on_change_sync_status_button_clicked(self, *a, **kw):
536- """The change sync status button was clicked."""
537+ def load(self):
538+ """Load info."""
539+ self.ui.file_sync_status.load()
540
541=== added file 'ubuntuone/controlpanel/gui/qt/filesyncstatus.py'
542--- ubuntuone/controlpanel/gui/qt/filesyncstatus.py 1970-01-01 00:00:00 +0000
543+++ ubuntuone/controlpanel/gui/qt/filesyncstatus.py 2011-05-31 18:28:26 +0000
544@@ -0,0 +1,151 @@
545+# -*- coding: utf-8 -*-
546+
547+# Authors: Natalia B Bidart <natalia.bidart@canonical.com>
548+#
549+# Copyright 2011 Canonical Ltd.
550+#
551+# This program is free software: you can redistribute it and/or modify it
552+# under the terms of the GNU General Public License version 3, as published
553+# by the Free Software Foundation.
554+#
555+# This program is distributed in the hope that it will be useful, but
556+# WITHOUT ANY WARRANTY; without even the implied warranties of
557+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
558+# PURPOSE. See the GNU General Public License for more details.
559+#
560+# You should have received a copy of the GNU General Public License along
561+# with this program. If not, see <http://www.gnu.org/licenses/>.
562+
563+"""The file sync status widget."""
564+
565+from PyQt4 import QtGui, QtCore
566+from twisted.internet import defer
567+
568+from ubuntuone.controlpanel import backend
569+from ubuntuone.controlpanel.logger import setup_logging, log_call
570+# Wildcard import ubuntuone.controlpanel.gui
571+# pylint: disable=W0401, W0614
572+from ubuntuone.controlpanel.gui import *
573+# pylint: enable=W0401, W0614
574+from ubuntuone.controlpanel.gui.qt.ui import filesyncstatus_ui
575+
576+
577+logger = setup_logging('qt.filesyncstatus')
578+
579+
580+WARNING_MARKUP = '<font color="%s"><b>%%s</b></font>' % ERROR_COLOR
581+
582+FILE_SYNC_STATUS = {
583+ backend.FILE_SYNC_DISABLED:
584+ {'msg': FILE_SYNC_DISABLED, 'action': FILE_SYNC_ENABLE,
585+ 'backend_method': 'enable_files',
586+ 'icon': ERROR_ICON, 'color': ERROR_COLOR,
587+ 'tooltip': FILE_SYNC_ENABLE_TOOLTIP},
588+ backend.FILE_SYNC_DISCONNECTED:
589+ {'msg': FILE_SYNC_DISCONNECTED, 'action': FILE_SYNC_CONNECT,
590+ 'backend_method': 'connect_files',
591+ 'icon': ERROR_ICON, 'color': ERROR_COLOR,
592+ 'tooltip': FILE_SYNC_CONNECT_TOOLTIP},
593+ backend.FILE_SYNC_ERROR:
594+ {'msg': WARNING_MARKUP % FILE_SYNC_ERROR, 'action': FILE_SYNC_RESTART,
595+ 'backend_method': 'restart_files',
596+ 'tooltip': FILE_SYNC_RESTART_TOOLTIP},
597+ backend.FILE_SYNC_IDLE:
598+ {'msg': FILE_SYNC_IDLE, 'action': FILE_SYNC_DISCONNECT,
599+ 'backend_method': 'disconnect_files',
600+ 'icon': IDLE_ICON, 'color': SUCCESS_COLOR,
601+ 'tooltip': FILE_SYNC_DISCONNECT_TOOLTIP},
602+ backend.FILE_SYNC_STARTING:
603+ {'msg': FILE_SYNC_STARTING, 'action': FILE_SYNC_STOP,
604+ 'backend_method': 'stop_files',
605+ 'icon': SYNCING_ICON, 'color': ORANGE,
606+ 'tooltip': FILE_SYNC_STOP_TOOLTIP},
607+ backend.FILE_SYNC_STOPPED:
608+ {'msg': FILE_SYNC_STOPPED, 'action': FILE_SYNC_START,
609+ 'backend_method': 'start_files',
610+ 'icon': ERROR_ICON, 'color': ERROR_COLOR,
611+ 'tooltip': FILE_SYNC_START_TOOLTIP},
612+ backend.FILE_SYNC_SYNCING:
613+ {'msg': FILE_SYNC_SYNCING, 'action': FILE_SYNC_DISCONNECT,
614+ 'backend_method': 'disconnect_files',
615+ 'icon': SYNCING_ICON, 'color': ORANGE,
616+ 'tooltip': FILE_SYNC_DISCONNECT_TOOLTIP},
617+ backend.FILE_SYNC_UNKNOWN:
618+ {'msg': WARNING_MARKUP % FILE_SYNC_ERROR, 'action': FILE_SYNC_RESTART,
619+ 'backend_method': 'restart_files',
620+ 'tooltip': FILE_SYNC_RESTART_TOOLTIP},
621+}
622+
623+
624+class FileSyncStatus(QtGui.QWidget):
625+ """The FileSyncStatus widget"""
626+
627+ def __init__(self, parent=None):
628+ """Initialize the UI of the widget."""
629+ QtGui.QWidget.__init__(self, parent)
630+ self.ui = filesyncstatus_ui.Ui_Form()
631+ self.ui.setupUi(self)
632+ self.show()
633+
634+ self._backend_method = None
635+
636+ self.backend = backend.ControlBackend()
637+ self.backend.status_changed_handler = self.process_status
638+
639+ logger.debug('%s: started.', self.__class__.__name__)
640+
641+ def load(self):
642+ """Load info."""
643+ d = defer.maybeDeferred(self.backend.file_sync_status)
644+ d.addCallback(self.process_status)
645+
646+ @log_call(logger.debug)
647+ def process_status(self, status):
648+ """Match status with signals."""
649+ logger.debug('process_status: new status received %r', status)
650+
651+ try:
652+ status_key = status[backend.STATUS_KEY]
653+ data = FILE_SYNC_STATUS[status_key]
654+ except (KeyError, TypeError):
655+ logger.exception('process_status: received unknown status dict %r',
656+ status)
657+ return
658+
659+ msg = data['msg']
660+ if status_key in (backend.FILE_SYNC_ERROR, backend.FILE_SYNC_UNKNOWN):
661+ reason = status[backend.MSG_KEY]
662+ if reason:
663+ msg += ' (' + reason + ')'
664+
665+ icon = data.get('icon')
666+ color = data.get('color')
667+ if icon is not None:
668+ foreground = '' if color is None else 'color="%s"' % color
669+ msg = '<font %s>%s</font> %s' % (foreground, icon, msg)
670+
671+ self.ui.sync_status_label.setText(msg)
672+
673+ action = data.get('action')
674+ if action is not None:
675+ self.ui.sync_status_button.setText(action)
676+ self.ui.sync_status_button.setEnabled(True)
677+ self.ui.sync_status_button.show()
678+ else:
679+ self.ui.sync_status_button.hide()
680+
681+ tooltip = data.get('tooltip')
682+ if tooltip is not None:
683+ self.ui.sync_status_button.setToolTip(tooltip)
684+
685+ self._backend_method = getattr(self.backend, data['backend_method'])
686+
687+ @QtCore.pyqtSlot()
688+ def on_sync_status_button_clicked(self, *a, **kw):
689+ """Button was clicked, act accordingly to the label."""
690+ self.ui.sync_status_button.setEnabled(False)
691+ if self._backend_method is not None:
692+ self._backend_method()
693+ else:
694+ logger.error('on_sync_status_button_clicked: backend method is '
695+ 'None!')
696
697=== modified file 'ubuntuone/controlpanel/gui/qt/gui.py'
698--- ubuntuone/controlpanel/gui/qt/gui.py 2011-05-24 14:20:18 +0000
699+++ ubuntuone/controlpanel/gui/qt/gui.py 2011-05-31 18:28:26 +0000
700@@ -50,6 +50,10 @@
701 self.close_callback()
702 event.accept()
703
704+ def load(self):
705+ """Load info."""
706+ self.ui.control_panel.load()
707+
708
709 def main(switch_to='', alert=False):
710 """Start the Qt reactor and open the main window."""
711@@ -68,6 +72,36 @@
712 qt4reactor.install()
713 from twisted.internet import reactor
714
715+ # Due to the crash with MemoryError as per
716+ # http://pastebin.ubuntu.com/612270/
717+ # we need patch SyncDaemonTool so there is no Dbus interaction
718+ from ubuntuone.controlpanel.integrationtests.test_sd_client.test_linux \
719+ import FakedSyncDaemonTool
720+
721+ import subprocess
722+
723+ class SDTool(FakedSyncDaemonTool):
724+ """A custom SDTool."""
725+
726+ def quit(self):
727+ """Quit the syncdaemon."""
728+ subprocess.Popen(('u1sdtool', '-q'))
729+
730+ def connect(self):
731+ """Connect syncdaemon."""
732+ subprocess.Popen(('u1sdtool', '-c'))
733+
734+ def disconnect(self):
735+ """Disconnect syncdaemon."""
736+ subprocess.Popen(('u1sdtool', '-d'))
737+
738+ def start(self):
739+ """Start syncdaemon if it's not running."""
740+ subprocess.Popen(('u1sdtool', '-s'))
741+
742+ from ubuntuone.platform.linux import tools
743+ tools.SyncDaemonTool = SDTool
744+
745 window = MainWindow(close_callback=reactor.stop)
746 window.show()
747
748
749=== modified file 'ubuntuone/controlpanel/gui/qt/tests/__init__.py'
750--- ubuntuone/controlpanel/gui/qt/tests/__init__.py 2011-05-24 18:56:16 +0000
751+++ ubuntuone/controlpanel/gui/qt/tests/__init__.py 2011-05-31 18:28:26 +0000
752@@ -18,7 +18,12 @@
753
754 """The test suite for the Qt UI for the control panel for Ubuntu One."""
755
756+from ubuntuone.controlpanel import backend
757 from ubuntuone.controlpanel.tests import TestCase
758+from ubuntuone.controlpanel.gui.tests import FakedObject
759+
760+# Attribute 'yyy' defined outside __init__, access to a protected member
761+# pylint: disable=W0201, W0212
762
763
764 def skip_if_abstract_class(test):
765@@ -38,6 +43,26 @@
766 return inner
767
768
769+class FakeUi(FakedObject):
770+ """A fake Ui object."""
771+
772+ exposed_methods = ['setupUi']
773+
774+
775+class FakedControlPanelBackend(FakedObject):
776+ """Fake a Control Panel Backend."""
777+
778+ exposed_methods = [
779+ 'account_info', # account
780+ 'devices_info', 'change_device_settings', 'remove_device', # devices
781+ 'volumes_info', 'change_volume_settings', # volumes
782+ 'replications_info', 'change_replication_settings', # replications
783+ 'file_sync_status', 'enable_files', 'disable_files', # files
784+ 'connect_files', 'disconnect_files',
785+ 'restart_files', 'start_files', 'stop_files', 'shutdown',
786+ ]
787+
788+
789 class BaseTestCase(TestCase):
790 """Base Test Case."""
791
792@@ -49,10 +74,21 @@
793 @skip_if_abstract_class
794 def setUp(self):
795 super(BaseTestCase, self).setUp()
796+ self.patch(backend, 'ControlBackend', FakedControlPanelBackend)
797 # self.class_ui is not callable
798 # pylint: disable=E1102
799 self.ui = self.class_ui(**self.kwargs)
800
801+ def assert_backend_called(self, method_name, args=None, kwargs=None):
802+ """Check that the control panel backend 'method_name' was called."""
803+ if args is None:
804+ args = ()
805+ if kwargs is None:
806+ kwargs = {}
807+
808+ self.assertIn(method_name, self.ui.backend._called)
809+ self.assertEqual(self.ui.backend._called[method_name], (args, kwargs))
810+
811 @skip_if_abstract_class
812 def test_init_loads_ui(self):
813 """The __init__ method loads the ui."""
814@@ -60,18 +96,5 @@
815 # pylint: disable=E1102
816 self.ui = self.class_ui(**self.kwargs)
817 # pylint: disable=E1101
818- self.assertEqual(self.ui.ui.called,
819- ((self.ui,), {}), 'setupUi called.')
820-
821-
822-class FakeUi(object):
823- """A fake Ui object."""
824-
825- def __init__(self):
826- """Initialize this fake instance."""
827- self.called = None
828-
829- # pylint: disable=C0103
830- def setupUi(self, *args, **kwargs):
831- """Fake setupUi."""
832- self.called = (args, kwargs)
833+ self.assertEqual(self.ui.ui._called,
834+ {'setupUi': ((self.ui,), {})})
835
836=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py'
837--- ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py 2011-05-24 20:00:59 +0000
838+++ ubuntuone/controlpanel/gui/qt/tests/test_controlpanel.py 2011-05-31 18:28:26 +0000
839@@ -33,45 +33,3 @@
840 """Backend is created."""
841 self.assertIsInstance(self.ui.backend,
842 gui.backend.ControlBackend)
843-
844- def test_process_status_changed(self):
845- """Backend's file sync status changed callback is connected."""
846- self.assertEqual(self.ui.backend.status_changed_handler,
847- self.ui.process_status)
848-
849- def _test_process_status(self, status_key):
850- """File sync status changes update the label."""
851- status = {gui.backend.STATUS_KEY: status_key,
852- gui.backend.MSG_KEY: 'something not important'}
853- self.ui.process_status(status)
854-
855- self.assertEqual(self.ui.ui.sync_status_label.text(),
856- gui.STATUS_MARKUP % gui.FILE_SYNC_STATUS[status_key])
857-
858- def test_process_status_disconnected(self):
859- """File sync status disconnected update the label."""
860- self._test_process_status(gui.backend.FILE_SYNC_DISCONNECTED)
861-
862- def test_process_status_disabled(self):
863- """File sync status disabled update the label."""
864- self._test_process_status(gui.backend.FILE_SYNC_DISABLED)
865-
866- def test_process_status_error(self):
867- """File sync status error update the label."""
868- self._test_process_status(gui.backend.FILE_SYNC_ERROR)
869-
870- def test_process_status_idle(self):
871- """File sync status idle update the label."""
872- self._test_process_status(gui.backend.FILE_SYNC_IDLE)
873-
874- def test_process_status_starting(self):
875- """File sync status starting update the label."""
876- self._test_process_status(gui.backend.FILE_SYNC_STARTING)
877-
878- def test_process_status_stopped(self):
879- """File sync status stopped update the label."""
880- self._test_process_status(gui.backend.FILE_SYNC_STOPPED)
881-
882- def test_process_status_syncing(self):
883- """File sync status syncing update the label."""
884- self._test_process_status(gui.backend.FILE_SYNC_SYNCING)
885
886=== added file 'ubuntuone/controlpanel/gui/qt/tests/test_filesyncstatus.py'
887--- ubuntuone/controlpanel/gui/qt/tests/test_filesyncstatus.py 1970-01-01 00:00:00 +0000
888+++ ubuntuone/controlpanel/gui/qt/tests/test_filesyncstatus.py 2011-05-31 18:28:26 +0000
889@@ -0,0 +1,159 @@
890+# -*- coding: utf-8 -*-
891+
892+# Authors: Natalia B Bidart <natalia.bidart@canonical.com>
893+#
894+# Copyright 2011 Canonical Ltd.
895+#
896+# This program is free software: you can redistribute it and/or modify it
897+# under the terms of the GNU General Public License version 3, as published
898+# by the Free Software Foundation.
899+#
900+# This program is distributed in the hope that it will be useful, but
901+# WITHOUT ANY WARRANTY; without even the implied warranties of
902+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
903+# PURPOSE. See the GNU General Public License for more details.
904+#
905+# You should have received a copy of the GNU General Public License along
906+# with this program. If not, see <http://www.gnu.org/licenses/>.
907+
908+"""Tests for the Control Panel."""
909+
910+from ubuntuone.controlpanel.gui.qt import filesyncstatus as gui
911+from ubuntuone.controlpanel.gui.qt.tests import BaseTestCase
912+
913+backend = gui.backend # pylint: disable=C0103
914+
915+
916+class FileSyncStatusTestCase(BaseTestCase):
917+ """Test the qt control panel."""
918+
919+ innerclass_ui = gui.filesyncstatus_ui
920+ innerclass_name = "Ui_Form"
921+ class_ui = gui.FileSyncStatus
922+
923+ def assert_status_correct(self, status_bd, status_ui, msg_bd=None,
924+ action=None, callback=None, tooltip=None):
925+ """The shown status is correct.
926+
927+ * The ui's label shows 'status_ui'.
928+ * If action is not None, the ui's button shows that 'action' as label
929+ and when clicking it, 'callback' gets executed.
930+ * If action is None, the ui's button should be hidden.
931+ * If a tooltip is given, then it exists with correct text.
932+
933+ """
934+ if msg_bd is None:
935+ msg_bd = None
936+ expected_text = status_ui
937+ else:
938+ expected_text = status_ui + ' (' + msg_bd + ')'
939+
940+ status = {backend.STATUS_KEY: status_bd, backend.MSG_KEY: msg_bd}
941+ self.ui.process_status(status)
942+
943+ self.assertTrue(self.ui.ui.sync_status_label.isVisible())
944+
945+ actual_text = self.ui.ui.sync_status_label.text()
946+ msg = '%r does not end with %r' % (actual_text, expected_text)
947+ self.assertTrue(actual_text.endsWith(expected_text), msg)
948+
949+ self.assertTrue(self.ui.ui.sync_status_button.isEnabled())
950+
951+ if action is not None:
952+ self.assertTrue(self.ui.ui.sync_status_button.isVisible())
953+ self.assertTrue(self.ui.ui.sync_status_button.isEnabled())
954+ self.assertEqual(self.ui.ui.sync_status_button.text(), action)
955+
956+ self.ui.ui.sync_status_button.click()
957+ self.assertFalse(self.ui.ui.sync_status_button.isEnabled())
958+ self.assert_backend_called(callback)
959+ else:
960+ self.assertFalse(self.ui.ui.sync_status_button.isVisible())
961+
962+ if tooltip is not None:
963+ self.assertEqual(self.ui.ui.sync_status_button.toolTip(), tooltip)
964+ else:
965+ self.assertEqual(self.ui.ui.sync_status_button.toolTip(), '')
966+
967+ def test_backend(self):
968+ """Backend is created."""
969+ self.assertIsInstance(self.ui.backend,
970+ backend.ControlBackend)
971+
972+ def test_process_status_changed(self):
973+ """Backend's file sync status changed callback is connected."""
974+ self.assertEqual(self.ui.backend.status_changed_handler,
975+ self.ui.process_status)
976+
977+ def test_file_sync_status_is_requested_on_load(self):
978+ """The file sync status is requested to the backend."""
979+ self.ui.load()
980+ self.assert_backend_called('file_sync_status', ())
981+
982+ def test_process_status_disabled(self):
983+ """File sync status disabled update the label."""
984+ self.assert_status_correct(status_bd=backend.FILE_SYNC_DISABLED,
985+ status_ui=gui.FILE_SYNC_DISABLED,
986+ action=gui.FILE_SYNC_ENABLE,
987+ callback='enable_files',
988+ tooltip=gui.FILE_SYNC_ENABLE_TOOLTIP)
989+
990+ def test_process_status_disconnected(self):
991+ """File sync status disconnected update the label."""
992+ self.assert_status_correct(status_bd=backend.FILE_SYNC_DISCONNECTED,
993+ status_ui=gui.FILE_SYNC_DISCONNECTED,
994+ action=gui.FILE_SYNC_CONNECT,
995+ callback='connect_files',
996+ tooltip=gui.FILE_SYNC_CONNECT_TOOLTIP)
997+
998+ def test_process_status_error(self):
999+ """File sync status error update the label."""
1000+ msg = gui.WARNING_MARKUP % gui.FILE_SYNC_ERROR
1001+ self.assert_status_correct(status_bd=backend.FILE_SYNC_ERROR,
1002+ status_ui=msg,
1003+ msg_bd='what an error!',
1004+ action=gui.FILE_SYNC_RESTART,
1005+ callback='restart_files',
1006+ tooltip=gui.FILE_SYNC_RESTART_TOOLTIP)
1007+
1008+ def test_process_status_idle(self):
1009+ """File sync status idle update the label."""
1010+ self.assert_status_correct(status_bd=backend.FILE_SYNC_IDLE,
1011+ status_ui=gui.FILE_SYNC_IDLE,
1012+ action=gui.FILE_SYNC_DISCONNECT,
1013+ callback='disconnect_files',
1014+ tooltip=gui.FILE_SYNC_DISCONNECT_TOOLTIP)
1015+
1016+ def test_process_status_starting(self):
1017+ """File sync status starting update the label."""
1018+ self.assert_status_correct(status_bd=backend.FILE_SYNC_STARTING,
1019+ status_ui=gui.FILE_SYNC_STARTING,
1020+ action=gui.FILE_SYNC_STOP,
1021+ callback='stop_files',
1022+ tooltip=gui.FILE_SYNC_STOP_TOOLTIP)
1023+
1024+ def test_process_status_stopped(self):
1025+ """File sync status stopped update the label."""
1026+ self.assert_status_correct(status_bd=backend.FILE_SYNC_STOPPED,
1027+ status_ui=gui.FILE_SYNC_STOPPED,
1028+ action=gui.FILE_SYNC_START,
1029+ callback='start_files',
1030+ tooltip=gui.FILE_SYNC_START_TOOLTIP)
1031+
1032+ def test_process_status_syncing(self):
1033+ """File sync status syncing update the label."""
1034+ self.assert_status_correct(status_bd=backend.FILE_SYNC_SYNCING,
1035+ status_ui=gui.FILE_SYNC_SYNCING,
1036+ action=gui.FILE_SYNC_DISCONNECT,
1037+ callback='disconnect_files',
1038+ tooltip=gui.FILE_SYNC_DISCONNECT_TOOLTIP)
1039+
1040+ def test_process_status_unknown(self):
1041+ """File sync status unknown update the label."""
1042+ msg = gui.WARNING_MARKUP % gui.FILE_SYNC_ERROR
1043+ self.assert_status_correct(status_bd=backend.FILE_SYNC_UNKNOWN,
1044+ status_ui=msg,
1045+ msg_bd='yadda oops',
1046+ action=gui.FILE_SYNC_RESTART,
1047+ callback='restart_files',
1048+ tooltip=gui.FILE_SYNC_RESTART_TOOLTIP)
1049
1050=== added directory 'ubuntuone/controlpanel/gui/tests'
1051=== added file 'ubuntuone/controlpanel/gui/tests/__init__.py'
1052--- ubuntuone/controlpanel/gui/tests/__init__.py 1970-01-01 00:00:00 +0000
1053+++ ubuntuone/controlpanel/gui/tests/__init__.py 2011-05-31 18:28:26 +0000
1054@@ -0,0 +1,133 @@
1055+# -*- coding: utf-8 -*-
1056+
1057+# Authors: Natalia B Bidart <natalia.bidart@canonical.com>
1058+#
1059+# Copyright 2011 Canonical Ltd.
1060+#
1061+# This program is free software: you can redistribute it and/or modify it
1062+# under the terms of the GNU General Public License version 3, as published
1063+# by the Free Software Foundation.
1064+#
1065+# This program is distributed in the hope that it will be useful, but
1066+# WITHOUT ANY WARRANTY; without even the implied warranties of
1067+# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1068+# PURPOSE. See the GNU General Public License for more details.
1069+#
1070+# You should have received a copy of the GNU General Public License along
1071+# with this program. If not, see <http://www.gnu.org/licenses/>.
1072+
1073+"""The test suite for the GTK UI for the control panel for Ubuntu One."""
1074+
1075+from ubuntuone.controlpanel import gui
1076+from ubuntuone.controlpanel.backend import ControlBackend
1077+
1078+# Attribute 'yyy' defined outside __init__, access to a protected member
1079+# pylint: disable=W0201, W0212
1080+
1081+
1082+FAKE_ACCOUNT_INFO = {'type': 'Payed', 'name': 'Test me',
1083+ 'email': 'test.com', 'quota_total': '12345', 'quota_used': '9999'}
1084+
1085+USER_HOME = '/home/tester'
1086+
1087+ROOT = {
1088+ u'volume_id': '', u'path': '/home/tester/My Ubuntu',
1089+ u'subscribed': 'True', u'type': ControlBackend.ROOT_TYPE,
1090+ u'display_name': u'My Ubuntu',
1091+}
1092+
1093+MUSIC_FOLDER = {
1094+ u'volume_id': u'58236', u'subscribed': u'True',
1095+ u'type': ControlBackend.FOLDER_TYPE,
1096+ u'path': u'/home/tester/.ubuntuone/Purchased from Ubuntu One',
1097+ u'suggested_path': u'~/.ubuntuone/Purchased from Ubuntu One',
1098+ u'display_name': u'.ubuntuone/Purchased from Ubuntu One',
1099+}
1100+
1101+FAKE_FOLDERS_INFO = [
1102+ # backend send this ordered by path
1103+ {u'volume_id': u'1', u'path': u'/home/tester/bar',
1104+ u'suggested_path': u'~/bar', u'subscribed': u'True',
1105+ u'type': ControlBackend.FOLDER_TYPE, u'display_name': u'bar'},
1106+ {u'volume_id': u'2', u'path': u'/home/tester/baz',
1107+ u'suggested_path': u'~/baz', u'subscribed': u'True',
1108+ u'type': ControlBackend.FOLDER_TYPE, u'display_name': u'baz'},
1109+ {u'volume_id': u'0', u'path': u'/home/tester/foo',
1110+ u'suggested_path': u'~/foo', u'subscribed': u'',
1111+ u'type': ControlBackend.FOLDER_TYPE, u'display_name': u'foo'},
1112+]
1113+
1114+FAKE_SHARES_INFO = [
1115+ # backend send this ordered by path
1116+ {u'volume_id': u'1234', u'name': u'do',
1117+ u'path': u'/home/tester/.local/share/ubuntuone/shares/do from Other User',
1118+ u'subscribed': u'', u'type': ControlBackend.SHARE_TYPE,
1119+ u'display_name': u'do'},
1120+ {u'volume_id': u'5678', u'name': u're',
1121+ u'path': u'/home/tester/.local/share/ubuntuone/shares/re from Other User',
1122+ u'subscribed': u'True', u'type': ControlBackend.SHARE_TYPE,
1123+ u'display_name': u're'},
1124+]
1125+
1126+FAKE_VOLUMES_INFO = [
1127+ (u'', u'147852369', [ROOT] + FAKE_FOLDERS_INFO),
1128+ (u'Other User', gui.SHARES_MIN_SIZE_FULL, FAKE_SHARES_INFO),
1129+]
1130+
1131+FAKE_VOLUMES_NO_FREE_SPACE_INFO = [
1132+ (u'', u'500', [ROOT]),
1133+ (u'No free space', u'0',
1134+ [{u'volume_id': u'0', u'name': u'full', u'path': u'full-share',
1135+ u'subscribed': u'', u'type': ControlBackend.SHARE_TYPE,
1136+ u'display_name': u'something',
1137+ }]),
1138+ (u'Almost no free space', gui.SHARES_MIN_SIZE_FULL - 1,
1139+ [{u'volume_id': u'1', u'name': u'almostfull', u'path': u'almost-full',
1140+ u'subscribed': u'', u'type': ControlBackend.SHARE_TYPE,
1141+ u'display_name': u'yadda',
1142+ }]),
1143+]
1144+
1145+FAKE_DEVICE_INFO = {
1146+ 'device_id': '1258-6854', 'device_name': 'Baz', 'device_type': 'Computer',
1147+ 'is_local': 'True', 'configurable': 'True', 'limit_bandwidth': 'True',
1148+ 'max_upload_speed': '1000', 'max_download_speed': '72548',
1149+ 'show_all_notifications': 'True',
1150+}
1151+
1152+FAKE_DEVICES_INFO = [
1153+ {'device_id': '0', 'name': 'Ubuntu One @ Foo', 'type': 'Computer',
1154+ 'is_local': '', 'configurable': ''},
1155+ {'device_id': '1', 'name': 'Ubuntu One @ Bar', 'type': 'Phone',
1156+ 'is_local': '', 'configurable': ''},
1157+ {'device_id': '2', 'name': 'Ubuntu One @ Z', 'type': 'Computer',
1158+ 'is_local': '', 'configurable': 'True', 'limit_bandwidth': '',
1159+ 'max_upload_speed': '0', 'max_download_speed': '0',
1160+ 'show_all_notifications': ''},
1161+ {'device_id': '1258-6854', 'name': 'Ubuntu One @ Baz', 'type': 'Computer',
1162+ 'is_local': 'True', 'configurable': 'True', 'limit_bandwidth': 'True',
1163+ 'max_upload_speed': '1000', 'max_download_speed': '72548',
1164+ 'show_all_notifications': 'True'}, # local
1165+]
1166+
1167+
1168+class FakedObject(object):
1169+ """Fake an object, record every call."""
1170+
1171+ exposed_methods = []
1172+
1173+ def __init__(self, *args, **kwargs):
1174+ self._args = args
1175+ self._kwargs = kwargs
1176+ self._called = {}
1177+ for i in self.exposed_methods:
1178+ setattr(self, i, self._record_call(i))
1179+
1180+ def _record_call(self, func_name):
1181+ """Store values when calling 'func_name'."""
1182+
1183+ def inner(*args, **kwargs):
1184+ """Fake 'func_name'."""
1185+ self._called[func_name] = (args, kwargs)
1186+
1187+ return inner

Subscribers

People subscribed via source and target branches