Merge lp:~dobey/ubuntuone-control-panel/update-4-0 into lp:ubuntuone-control-panel/stable-4-0
- update-4-0
- Merge into stable-4-0
| Status: | Merged |
|---|---|
| Approved by: | dobey on 2012-08-23 |
| Approved revision: | no longer in the source branch. |
| Merged at revision: | 335 |
| Proposed branch: | lp:~dobey/ubuntuone-control-panel/update-4-0 |
| Merge into: | lp:ubuntuone-control-panel/stable-4-0 |
| Diff against target: |
2345 lines (+1516/-167) 28 files modified
data/qt/controlpanel.ui (+13/-2) data/qt/share_links.ui (+133/-0) docs/ubuntuone-control-panel-qt.1 (+1/-1) run-tests (+4/-8) run-tests.bat (+1/-1) ubuntuone/controlpanel/backend.py (+14/-0) ubuntuone/controlpanel/gui/__init__.py (+21/-0) ubuntuone/controlpanel/gui/qt/__init__.py (+78/-0) ubuntuone/controlpanel/gui/qt/controlpanel.py (+4/-0) ubuntuone/controlpanel/gui/qt/filesyncstatus.py (+4/-77) ubuntuone/controlpanel/gui/qt/folders.py (+14/-4) ubuntuone/controlpanel/gui/qt/gui.py (+1/-1) ubuntuone/controlpanel/gui/qt/main/__init__.py (+16/-2) ubuntuone/controlpanel/gui/qt/main/tests/test_main.py (+35/-0) ubuntuone/controlpanel/gui/qt/share_links.py (+42/-0) ubuntuone/controlpanel/gui/qt/systray.py (+316/-23) ubuntuone/controlpanel/gui/qt/tests/__init__.py (+16/-1) ubuntuone/controlpanel/gui/qt/tests/test_filesyncstatus.py (+30/-29) ubuntuone/controlpanel/gui/qt/tests/test_folders.py (+44/-0) ubuntuone/controlpanel/gui/qt/tests/test_gui.py (+5/-0) ubuntuone/controlpanel/gui/qt/tests/test_share_links.py (+37/-0) ubuntuone/controlpanel/gui/qt/tests/test_systray.py (+481/-18) ubuntuone/controlpanel/sd_client/__init__.py (+4/-0) ubuntuone/controlpanel/tests/test_backend.py (+12/-0) ubuntuone/controlpanel/utils/__init__.py (+10/-0) ubuntuone/controlpanel/utils/darwin.py (+91/-0) ubuntuone/controlpanel/utils/tests/test_darwin.py (+78/-0) ubuntuone/controlpanel/utils/tests/test_utils.py (+11/-0) |
| To merge this branch: | bzr merge lp:~dobey/ubuntuone-control-panel/update-4-0 |
| Related bugs: |
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| Alejandro J. Cura (community) | trivial | 2012-08-22 | Approve on 2012-08-22 |
|
Review via email:
|
|||
Commit Message
[Diego Sarmentero]
- Adding Share Links Tab UI (LP: #1039142).
- Show the not accepted shares (LP: #1034542).
- Show current and recent transfers on the transfers menu (LP: #1034542).
- Adding open (folder, program, url) actions to the menu (LP: #1034542)
- Adding status actions to the system tray menu (LP: #1034542).
[Mike McCracken]
- Remove use of env vars for u1lint and u1trial from old buildout. (LP: #1037904)
- Add darwin-only function to perform first-run "install" steps (LP: #1024623)
[Roberto Alsina]
- Give an error if the user subscribes a UDF and the local path is not a folder (LP:1033488)
- Prevented loading of the scrollbar overlay (LP:1007421).
Description of the Change
| Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~dobey/ubuntuone-control-panel/update-4-0 into lp:ubuntuone-control-panel/stable-4-0 failed. Below is the output from the failed tests.
*** Running DBus test suite ***
ubuntuone.
BaseTestCase
runTest ... [OK]
DBusServiceMa
test_
test_
DBusServiceTe
test_
test_
test_
test_
test_
test_
test_
test_
FileSyncTestCase
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
OperationsAut
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
t...
| Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~dobey/ubuntuone-control-panel/update-4-0 into lp:ubuntuone-control-panel/stable-4-0 failed. Below is the output from the failed tests.
*** Running DBus test suite ***
ubuntuone.
BaseTestCase
runTest ... [OK]
DBusServiceMa
test_
test_
DBusServiceTe
test_
test_
test_
test_
test_
test_
test_
test_
FileSyncTestCase
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
OperationsAut
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
test_
t...
- 335. By Roberto Alsina on 2012-08-23
-
[Diego Sarmentero]
- Adding Share Links Tab UI (LP: #1039142).
- Show the not accepted shares (LP: #1034542).
- Show current and recent transfers on the transfers menu (LP: #1034542).
- Adding open (folder, program, url) actions to the menu (LP: #1034542)
- Adding status actions to the system tray menu (LP: #1034542).[Mike McCracken]
- Remove use of env vars for u1lint and u1trial from old buildout. (LP: #1037904)
- Add darwin-only function to perform first-run "install" steps (LP: #1024623)[Roberto Alsina]
- Give an error if the user subscribes a UDF and the local path is not a folder (LP:1033488)
- Prevented loading of the scrollbar overlay (LP:1007421).
Preview Diff
| 1 | === modified file 'data/qt/controlpanel.ui' |
| 2 | --- data/qt/controlpanel.ui 2012-03-20 13:05:20 +0000 |
| 3 | +++ data/qt/controlpanel.ui 2012-08-22 21:22:18 +0000 |
| 4 | @@ -26,7 +26,7 @@ |
| 5 | <item> |
| 6 | <widget class="QStackedWidget" name="switcher"> |
| 7 | <property name="currentIndex"> |
| 8 | - <number>1</number> |
| 9 | + <number>0</number> |
| 10 | </property> |
| 11 | <widget class="QWidget" name="management"> |
| 12 | <property name="sizePolicy"> |
| 13 | @@ -229,13 +229,18 @@ |
| 14 | </sizepolicy> |
| 15 | </property> |
| 16 | <property name="currentIndex"> |
| 17 | - <number>0</number> |
| 18 | + <number>1</number> |
| 19 | </property> |
| 20 | <widget class="FoldersPanel" name="folders_tab"> |
| 21 | <attribute name="title"> |
| 22 | <string notr="true">Folders</string> |
| 23 | </attribute> |
| 24 | </widget> |
| 25 | + <widget class="ShareLinksPanel" name="share_links_tab"> |
| 26 | + <attribute name="title"> |
| 27 | + <string>Share Links</string> |
| 28 | + </attribute> |
| 29 | + </widget> |
| 30 | <widget class="DevicesPanel" name="devices_tab"> |
| 31 | <attribute name="title"> |
| 32 | <string notr="true">Devices</string> |
| 33 | @@ -416,6 +421,12 @@ |
| 34 | <header>ubuntuone.controlpanel.gui.qt.wizard</header> |
| 35 | <container>1</container> |
| 36 | </customwidget> |
| 37 | + <customwidget> |
| 38 | + <class>ShareLinksPanel</class> |
| 39 | + <extends>QWidget</extends> |
| 40 | + <header>ubuntuone.controlpanel.gui.qt.share_links</header> |
| 41 | + <container>1</container> |
| 42 | + </customwidget> |
| 43 | </customwidgets> |
| 44 | <tabstops> |
| 45 | <tabstop>help_button</tabstop> |
| 46 | |
| 47 | === added file 'data/qt/share_links.ui' |
| 48 | --- data/qt/share_links.ui 1970-01-01 00:00:00 +0000 |
| 49 | +++ data/qt/share_links.ui 2012-08-22 21:22:18 +0000 |
| 50 | @@ -0,0 +1,133 @@ |
| 51 | +<?xml version="1.0" encoding="UTF-8"?> |
| 52 | +<ui version="4.0"> |
| 53 | + <class>Form</class> |
| 54 | + <widget class="QWidget" name="Form"> |
| 55 | + <property name="geometry"> |
| 56 | + <rect> |
| 57 | + <x>0</x> |
| 58 | + <y>0</y> |
| 59 | + <width>532</width> |
| 60 | + <height>335</height> |
| 61 | + </rect> |
| 62 | + </property> |
| 63 | + <property name="windowTitle"> |
| 64 | + <string>Form</string> |
| 65 | + </property> |
| 66 | + <layout class="QVBoxLayout" name="verticalLayout"> |
| 67 | + <property name="spacing"> |
| 68 | + <number>15</number> |
| 69 | + </property> |
| 70 | + <property name="leftMargin"> |
| 71 | + <number>0</number> |
| 72 | + </property> |
| 73 | + <property name="topMargin"> |
| 74 | + <number>10</number> |
| 75 | + </property> |
| 76 | + <property name="rightMargin"> |
| 77 | + <number>0</number> |
| 78 | + </property> |
| 79 | + <property name="bottomMargin"> |
| 80 | + <number>0</number> |
| 81 | + </property> |
| 82 | + <item> |
| 83 | + <layout class="QVBoxLayout" name="verticalLayout_2"> |
| 84 | + <property name="spacing"> |
| 85 | + <number>0</number> |
| 86 | + </property> |
| 87 | + <property name="sizeConstraint"> |
| 88 | + <enum>QLayout::SetDefaultConstraint</enum> |
| 89 | + </property> |
| 90 | + <property name="leftMargin"> |
| 91 | + <number>10</number> |
| 92 | + </property> |
| 93 | + <item> |
| 94 | + <widget class="QLabel" name="search_files_lbl"> |
| 95 | + <property name="sizePolicy"> |
| 96 | + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> |
| 97 | + <horstretch>0</horstretch> |
| 98 | + <verstretch>0</verstretch> |
| 99 | + </sizepolicy> |
| 100 | + </property> |
| 101 | + <property name="font"> |
| 102 | + <font> |
| 103 | + <pointsize>10</pointsize> |
| 104 | + <weight>75</weight> |
| 105 | + <bold>true</bold> |
| 106 | + </font> |
| 107 | + </property> |
| 108 | + <property name="text"> |
| 109 | + <string>Search files</string> |
| 110 | + </property> |
| 111 | + </widget> |
| 112 | + </item> |
| 113 | + <item> |
| 114 | + <widget class="QLineEdit" name="lineEdit"> |
| 115 | + <property name="sizePolicy"> |
| 116 | + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> |
| 117 | + <horstretch>0</horstretch> |
| 118 | + <verstretch>0</verstretch> |
| 119 | + </sizepolicy> |
| 120 | + </property> |
| 121 | + <property name="maximumSize"> |
| 122 | + <size> |
| 123 | + <width>400</width> |
| 124 | + <height>16777215</height> |
| 125 | + </size> |
| 126 | + </property> |
| 127 | + <property name="frame"> |
| 128 | + <bool>true</bool> |
| 129 | + </property> |
| 130 | + </widget> |
| 131 | + </item> |
| 132 | + </layout> |
| 133 | + </item> |
| 134 | + <item> |
| 135 | + <widget class="QStackedWidget" name="stackedWidget"> |
| 136 | + <widget class="QWidget" name="page"> |
| 137 | + <layout class="QVBoxLayout" name="verticalLayout_4"> |
| 138 | + <property name="spacing"> |
| 139 | + <number>0</number> |
| 140 | + </property> |
| 141 | + <property name="margin"> |
| 142 | + <number>0</number> |
| 143 | + </property> |
| 144 | + <item> |
| 145 | + <widget class="QGroupBox" name="shared_group"> |
| 146 | + <property name="title"> |
| 147 | + <string> Shared files</string> |
| 148 | + </property> |
| 149 | + <property name="flat"> |
| 150 | + <bool>false</bool> |
| 151 | + </property> |
| 152 | + <layout class="QVBoxLayout" name="verticalLayout_3"> |
| 153 | + <property name="spacing"> |
| 154 | + <number>0</number> |
| 155 | + </property> |
| 156 | + <property name="margin"> |
| 157 | + <number>0</number> |
| 158 | + </property> |
| 159 | + <item> |
| 160 | + <widget class="QTreeWidget" name="treeWidget"> |
| 161 | + <attribute name="headerVisible"> |
| 162 | + <bool>false</bool> |
| 163 | + </attribute> |
| 164 | + <column> |
| 165 | + <property name="text"> |
| 166 | + <string notr="true">1</string> |
| 167 | + </property> |
| 168 | + </column> |
| 169 | + </widget> |
| 170 | + </item> |
| 171 | + </layout> |
| 172 | + </widget> |
| 173 | + </item> |
| 174 | + </layout> |
| 175 | + </widget> |
| 176 | + <widget class="QWidget" name="page_2"/> |
| 177 | + </widget> |
| 178 | + </item> |
| 179 | + </layout> |
| 180 | + </widget> |
| 181 | + <resources/> |
| 182 | + <connections/> |
| 183 | +</ui> |
| 184 | |
| 185 | === modified file 'docs/ubuntuone-control-panel-qt.1' |
| 186 | --- docs/ubuntuone-control-panel-qt.1 2012-03-08 16:40:50 +0000 |
| 187 | +++ docs/ubuntuone-control-panel-qt.1 2012-08-22 21:22:18 +0000 |
| 188 | @@ -12,7 +12,7 @@ |
| 189 | .TP |
| 190 | \fB\-\-switch\-to\fR PANEL_NAME |
| 191 | Start Ubuntu One in the PANEL_NAME tab. Possible |
| 192 | -values are: folders, devices, settings, account |
| 193 | +values are: folders, share_links, devices, settings, account |
| 194 | .TP |
| 195 | \fB\-\-minimized\fR |
| 196 | Start Ubuntu One only in the notification area, with |
| 197 | |
| 198 | === modified file 'run-tests' |
| 199 | --- run-tests 2012-06-07 23:00:22 +0000 |
| 200 | +++ run-tests 2012-08-22 21:22:18 +0000 |
| 201 | @@ -32,7 +32,7 @@ |
| 202 | fi |
| 203 | |
| 204 | style_check() { |
| 205 | - $u1lint --ignore ubuntuone/controlpanel/gui/qt/ui |
| 206 | + u1lint --ignore ubuntuone/controlpanel/gui/qt/ui |
| 207 | if [ -x `which pep8` ]; then |
| 208 | pep8 --exclude '.svn,CVS,.bzr,.hg,.git,*_ui.py,*_rc.py' --repeat . bin/* |
| 209 | else |
| 210 | @@ -52,8 +52,6 @@ |
| 211 | SYSNAME=`uname -s` |
| 212 | |
| 213 | if [ "$SYSNAME" == "Darwin" ]; then |
| 214 | - u1trial="python $u1trial" |
| 215 | - u1lint="python $u1lint" |
| 216 | IGNORE_FILES="$WINDOWS_TESTS, $LINUX_TESTS" |
| 217 | IGNORE_DBUS="-p $DBUS_TESTS_PATH" |
| 218 | echo "*** Skipping DBus test suite ***" |
| 219 | @@ -64,21 +62,19 @@ |
| 220 | if [ "x$XVFB_CMDLINE" == "x" ]; then |
| 221 | echo "WARNING: Could not find xvfb-run, prepare for visual spam." |
| 222 | fi |
| 223 | - u1trial=u1trial |
| 224 | - u1lint=u1lint |
| 225 | IGNORE_FILES="$WINDOWS_TESTS, $DARWIN_TESTS" |
| 226 | IGNORE_DBUS="" |
| 227 | echo "*** Running DBus test suite ***" |
| 228 | - $u1trial --reactor=glib "$DBUS_TESTS_PATH" |
| 229 | + u1trial --reactor=glib "$DBUS_TESTS_PATH" |
| 230 | REACTOR=gi |
| 231 | fi |
| 232 | |
| 233 | echo "*** Running test suite for ""$MODULE"" ***" |
| 234 | -$u1trial --reactor=$REACTOR -p "$DBUS_TESTS_PATH, $QT_TESTS_PATH" -i "$IGNORE_FILES" "$MODULE" |
| 235 | +u1trial --reactor=$REACTOR -p "$DBUS_TESTS_PATH, $QT_TESTS_PATH" -i "$IGNORE_FILES" "$MODULE" |
| 236 | |
| 237 | echo "*** Running QT test suite for ""$MODULE"" ***" |
| 238 | ./setup.py build |
| 239 | -$XVFB_CMDLINE $u1trial $IGNORE_DBUS -i "$IGNORE_FILES" --reactor=qt4 --gui "$MODULE" |
| 240 | +$XVFB_CMDLINE u1trial $IGNORE_DBUS -i "$IGNORE_FILES" --reactor=qt4 --gui "$MODULE" |
| 241 | rm -rf _trial_temp |
| 242 | rm -rf build |
| 243 | |
| 244 | |
| 245 | === modified file 'run-tests.bat' |
| 246 | --- run-tests.bat 2012-04-20 14:45:56 +0000 |
| 247 | +++ run-tests.bat 2012-08-22 21:22:18 +0000 |
| 248 | @@ -19,7 +19,7 @@ |
| 249 | SET MODULE="ubuntuone" |
| 250 | SET PYTHONEXEPATH="" |
| 251 | SET IGNORE_PATHS="ubuntuone\controlpanel\dbustests" |
| 252 | -SET IGNORE_MODULES="test_linux.py, test_libsoup.py" |
| 253 | +SET IGNORE_MODULES="test_linux.py, test_libsoup.py, test_darwin.py" |
| 254 | SET PYTHONPATH=%PYTHONPATH%;..\ubuntu-sso-client;..\ubuntuone-client;. |
| 255 | |
| 256 | ECHO Checking for Python on the path |
| 257 | |
| 258 | === modified file 'ubuntuone/controlpanel/backend.py' |
| 259 | --- ubuntuone/controlpanel/backend.py 2012-06-22 14:42:29 +0000 |
| 260 | +++ ubuntuone/controlpanel/backend.py 2012-08-22 21:22:18 +0000 |
| 261 | @@ -821,6 +821,20 @@ |
| 262 | yield self.change_file_sync_settings(self.DEFAULT_FILE_SYNC_SETTINGS) |
| 263 | |
| 264 | @log_call(logger.info) |
| 265 | + @inlineCallbacks |
| 266 | + def sync_menu(self): |
| 267 | + """Obtain the data to create the sync menu.""" |
| 268 | + result = yield self.sd_client.sync_menu() |
| 269 | + returnValue(result) |
| 270 | + |
| 271 | + @log_call(logger.info) |
| 272 | + @inlineCallbacks |
| 273 | + def get_shares(self): |
| 274 | + """Obtain the data to create the sync menu.""" |
| 275 | + result = yield self.sd_client.get_shares() |
| 276 | + returnValue(result) |
| 277 | + |
| 278 | + @log_call(logger.info) |
| 279 | def shutdown(self): |
| 280 | """Stop this service.""" |
| 281 | # do any other needed cleanup |
| 282 | |
| 283 | === modified file 'ubuntuone/controlpanel/gui/__init__.py' |
| 284 | --- ubuntuone/controlpanel/gui/__init__.py 2012-05-29 14:36:17 +0000 |
| 285 | +++ ubuntuone/controlpanel/gui/__init__.py 2012-08-22 21:22:18 +0000 |
| 286 | @@ -42,6 +42,10 @@ |
| 287 | SHARES_MIN_SIZE_FULL = 1048576 |
| 288 | SUCCESS_COLOR = u'green' |
| 289 | |
| 290 | +# Sync Menu: |
| 291 | +RECENTTRANSFERS = 'recent-transfers' |
| 292 | +UPLOADING = 'uploading' |
| 293 | + |
| 294 | ERROR_ICON = u'✘' |
| 295 | SYNCING_ICON = u'⇅' |
| 296 | IDLE_ICON = u'✔' |
| 297 | @@ -65,7 +69,9 @@ |
| 298 | |
| 299 | FILE_URI_PREFIX = u'file://' |
| 300 | |
| 301 | +ACCEPT_SHARES = 'https://one.ubuntu.com/files/shareoffer/%s/' |
| 302 | CONTACTS_LINK = UBUNTUONE_LINK |
| 303 | +DASHBOARD = UBUNTUONE_LINK + u'dashboard/' |
| 304 | EDIT_ACCOUNT_LINK = UBUNTUONE_LINK + u'account/' |
| 305 | EDIT_DEVICES_LINK = EDIT_ACCOUNT_LINK + u'machines/' |
| 306 | EDIT_PROFILE_LINK = u'https://login.ubuntu.com/' |
| 307 | @@ -140,6 +146,9 @@ |
| 308 | 'with your local folder "%(folder_path)s" when ' |
| 309 | 'subscribing.\nDo you want to subscribe to this ' |
| 310 | 'cloud folder?') |
| 311 | +FOLDER_EXISTS_AS_FILE = _('Unable to subscribe because ' |
| 312 | + '"%(folder_path)s" already exists on your ' |
| 313 | + 'device and is not a folder.') |
| 314 | FOLDERS_MANAGE_LABEL = _('Go to the web for public and private sharing ' |
| 315 | 'options') |
| 316 | FOLDERS_TITLE = _('Select which folders from your cloud you want to sync with ' |
| 317 | @@ -173,6 +182,9 @@ |
| 318 | GET_HELP_ONLINE = _('Get help online') |
| 319 | GET_MORE_STORAGE = _('Get more storage') |
| 320 | GREETING = _('Hi %(user_display_name)s') |
| 321 | +GO_TO_WEB = _('Go to the Website') |
| 322 | +IN_PROGRESS = _('In Progress') |
| 323 | +IN_PROGRESS_FILE = u'%s – %d%%' |
| 324 | INSTALL = _('Install') |
| 325 | INSTALL_PACKAGE = _('You need to install the package <i>%(package_name)s' |
| 326 | '</i> in order to enable more sync services.') |
| 327 | @@ -202,6 +214,7 @@ |
| 328 | MAIN_DEVICES_TAB = _('Devices') |
| 329 | MAIN_FOLDERS_TAB = _('Folders') |
| 330 | MAIN_PREFERENCES_TAB = _('Settings') |
| 331 | +MAIN_SHARE_LINKS_TAB = _('Share links') |
| 332 | MAIN_WINDOW_TITLE = _('%(app_name)s Control Panel') |
| 333 | MUSIC_DISPLAY_NAME = _('Purchased Music') |
| 334 | MUSIC_REAL_PATH = u'.ubuntuone/Purchased from Ubuntu One' |
| 335 | @@ -209,15 +222,20 @@ |
| 336 | NAME_NOT_SET = _('[unknown user name]') |
| 337 | NETWORK_OFFLINE = _('An internet connection is required to join or sign ' |
| 338 | 'in to %(app_name)s.') |
| 339 | +NEW_SHARE_BY = _('New Share by %s') |
| 340 | NO_DEVICES = _('No devices to show.') |
| 341 | NO_FOLDERS = _('No folders to show.') |
| 342 | NO_PAIRING_RECORD = _('There is no Ubuntu One pairing record.') |
| 343 | +OPEN_UBUNTU_ONE = _('Open Ubuntu One') |
| 344 | +OPEN_UBUNTU_ONE_FOLDER = _('Open the Ubuntu One Folder') |
| 345 | PERCENTAGE_LABEL = _('%(percentage)s used') |
| 346 | PLEASE_WAIT = _('Please wait') |
| 347 | PROFILE_LABEL = _('Personal details') |
| 348 | QUOTA_LABEL = _('Using %(used)s of %(total)s (%(percentage).0f%%)') |
| 349 | REMOVE_BUTTON = _('Remove') |
| 350 | +RECENT_TRANSFERS = _('Recent Transfers') |
| 351 | RESTORE_LABEL = _('Restore') |
| 352 | +SEARCH_FILES = _('Search files') |
| 353 | SELECT_FOLDERS = _('Select sync folders') |
| 354 | SERVICES_BUTTON_TOOLTIP = _('Manage the sync services') |
| 355 | SERVICES_TITLE = _('Enable the sync services for this computer.') |
| 356 | @@ -236,12 +254,15 @@ |
| 357 | 'to this computer') |
| 358 | SETTINGS_SYNC_ALL_SHARES = _('Automatically sync all folders shared with me ' |
| 359 | 'to this computer') |
| 360 | +SHARE_A_FILE = _('Share a File') |
| 361 | +SHARED_FILES = _('Shared files') |
| 362 | SHARES_BUTTON_TOOLTIP = _('Manage the shares offered to others') |
| 363 | SHARES_TITLE = _('Manage permissions for shares made to other users.') |
| 364 | SIGN_IN = _('Sign in') |
| 365 | SUCCESS_INSTALL = _('<i>%(package_name)s</i> was successfully installed') |
| 366 | SYNC_STREAM_SHARE = _('Sync, stream, share') |
| 367 | TALK_TO_US = _('Talk to us') |
| 368 | +TRANSFERS = _('Current and Recent Transfers') |
| 369 | VALUE_ERROR = _('Value could not be retrieved.') |
| 370 | UNKNOWN_ERROR = _('Unknown error') |
| 371 | USAGE_LABEL = _('%(used)s of %(total)s') |
| 372 | |
| 373 | === modified file 'ubuntuone/controlpanel/gui/qt/__init__.py' |
| 374 | --- ubuntuone/controlpanel/gui/qt/__init__.py 2012-04-17 18:08:45 +0000 |
| 375 | +++ ubuntuone/controlpanel/gui/qt/__init__.py 2012-08-22 21:22:18 +0000 |
| 376 | @@ -24,13 +24,91 @@ |
| 377 | from PyQt4 import QtGui, QtCore |
| 378 | from twisted.internet import defer |
| 379 | |
| 380 | +from ubuntuone.controlpanel import backend |
| 381 | from ubuntuone.controlpanel.gui import ( |
| 382 | + ERROR_COLOR, |
| 383 | + FILE_SYNC_CONNECT, |
| 384 | + FILE_SYNC_CONNECT_TOOLTIP, |
| 385 | + FILE_SYNC_DISABLED, |
| 386 | + FILE_SYNC_DISCONNECT, |
| 387 | + FILE_SYNC_DISCONNECT_TOOLTIP, |
| 388 | + FILE_SYNC_DISCONNECTED, |
| 389 | + FILE_SYNC_ENABLE, |
| 390 | + FILE_SYNC_ENABLE_TOOLTIP, |
| 391 | + FILE_SYNC_ERROR, |
| 392 | + FILE_SYNC_IDLE, |
| 393 | + FILE_SYNC_RESTART, |
| 394 | + FILE_SYNC_RESTART_TOOLTIP, |
| 395 | + FILE_SYNC_START, |
| 396 | + FILE_SYNC_START_TOOLTIP, |
| 397 | + FILE_SYNC_STARTING, |
| 398 | + FILE_SYNC_STOP, |
| 399 | + FILE_SYNC_STOP_TOOLTIP, |
| 400 | + FILE_SYNC_STOPPED, |
| 401 | + FILE_SYNC_SYNCING, |
| 402 | FILE_URI_PREFIX, |
| 403 | GENERAL_ERROR_TITLE, |
| 404 | GENERAL_ERROR_MSG, |
| 405 | ) |
| 406 | |
| 407 | |
| 408 | +WARNING_MARKUP = '<font color="%s"><b>%%s</b></font>' % ERROR_COLOR |
| 409 | +DISCONNECTED_ICON = 'sync_status_disconnected' |
| 410 | +ERROR_ICON = 'sync_status_alert' |
| 411 | +IDLE_ICON = 'sync_status_sync_done' |
| 412 | +SYNCING_ICON = 'sync_status_syncing' |
| 413 | + |
| 414 | + |
| 415 | +FILE_SYNC_STATUS = { |
| 416 | + backend.FILE_SYNC_DISABLED: |
| 417 | + {'msg': FILE_SYNC_DISABLED, 'action': FILE_SYNC_ENABLE, |
| 418 | + 'backend_method': 'enable_files', |
| 419 | + 'icon': ERROR_ICON, |
| 420 | + 'tooltip': FILE_SYNC_ENABLE_TOOLTIP}, |
| 421 | + backend.FILE_SYNC_DISCONNECTED: |
| 422 | + {'msg': FILE_SYNC_DISCONNECTED, 'action': FILE_SYNC_CONNECT, |
| 423 | + 'backend_method': 'connect_files', |
| 424 | + 'icon': DISCONNECTED_ICON, |
| 425 | + 'tooltip': FILE_SYNC_CONNECT_TOOLTIP}, |
| 426 | + backend.FILE_SYNC_ERROR: |
| 427 | + {'msg': WARNING_MARKUP % FILE_SYNC_ERROR, 'action': FILE_SYNC_RESTART, |
| 428 | + 'backend_method': 'restart_files', |
| 429 | + 'icon': ERROR_ICON, |
| 430 | + 'tooltip': FILE_SYNC_RESTART_TOOLTIP}, |
| 431 | + backend.FILE_SYNC_IDLE: |
| 432 | + {'msg': FILE_SYNC_IDLE, 'action': FILE_SYNC_DISCONNECT, |
| 433 | + 'backend_method': 'disconnect_files', |
| 434 | + 'icon': IDLE_ICON, |
| 435 | + 'tooltip': FILE_SYNC_DISCONNECT_TOOLTIP}, |
| 436 | + backend.FILE_SYNC_STARTING: |
| 437 | + {'msg': FILE_SYNC_STARTING, 'action': FILE_SYNC_STOP, |
| 438 | + 'backend_method': 'stop_files', |
| 439 | + 'icon': SYNCING_ICON, |
| 440 | + 'tooltip': FILE_SYNC_STOP_TOOLTIP}, |
| 441 | + backend.FILE_SYNC_STOPPED: |
| 442 | + {'msg': FILE_SYNC_STOPPED, 'action': FILE_SYNC_START, |
| 443 | + 'backend_method': 'start_files', |
| 444 | + 'icon': ERROR_ICON, |
| 445 | + 'tooltip': FILE_SYNC_START_TOOLTIP}, |
| 446 | + backend.FILE_SYNC_SYNCING: |
| 447 | + {'msg': FILE_SYNC_SYNCING, 'action': FILE_SYNC_DISCONNECT, |
| 448 | + 'backend_method': 'disconnect_files', |
| 449 | + 'icon': SYNCING_ICON, |
| 450 | + 'tooltip': FILE_SYNC_DISCONNECT_TOOLTIP}, |
| 451 | + backend.FILE_SYNC_UNKNOWN: |
| 452 | + {'msg': WARNING_MARKUP % FILE_SYNC_ERROR, 'action': FILE_SYNC_RESTART, |
| 453 | + 'backend_method': 'restart_files', |
| 454 | + 'icon': ERROR_ICON, |
| 455 | + 'tooltip': FILE_SYNC_RESTART_TOOLTIP}, |
| 456 | +} |
| 457 | + |
| 458 | + |
| 459 | +def icon_name_from_status(status_key): |
| 460 | + """Get the icon name for the status.""" |
| 461 | + icon_name = FILE_SYNC_STATUS[status_key]['icon'] |
| 462 | + return icon_name |
| 463 | + |
| 464 | + |
| 465 | def force_wordwrap(widget, size, text): |
| 466 | """Set the text to the widget after word wrapping it.""" |
| 467 | font_metrics = QtGui.QFontMetrics(widget.font()) |
| 468 | |
| 469 | === modified file 'ubuntuone/controlpanel/gui/qt/controlpanel.py' |
| 470 | --- ubuntuone/controlpanel/gui/qt/controlpanel.py 2012-04-11 11:47:19 +0000 |
| 471 | +++ ubuntuone/controlpanel/gui/qt/controlpanel.py 2012-08-22 21:22:18 +0000 |
| 472 | @@ -35,6 +35,7 @@ |
| 473 | MAIN_DEVICES_TAB, |
| 474 | MAIN_FOLDERS_TAB, |
| 475 | MAIN_PREFERENCES_TAB, |
| 476 | + MAIN_SHARE_LINKS_TAB, |
| 477 | PERCENTAGE_LABEL, |
| 478 | show_quota_warning, |
| 479 | TALK_TO_US, |
| 480 | @@ -76,6 +77,9 @@ |
| 481 | self.ui.tab_widget.setTabText( |
| 482 | self.ui.tab_widget.indexOf(self.ui.folders_tab), MAIN_FOLDERS_TAB) |
| 483 | self.ui.tab_widget.setTabText( |
| 484 | + self.ui.tab_widget.indexOf(self.ui.share_links_tab), |
| 485 | + MAIN_SHARE_LINKS_TAB) |
| 486 | + self.ui.tab_widget.setTabText( |
| 487 | self.ui.tab_widget.indexOf(self.ui.devices_tab), MAIN_DEVICES_TAB) |
| 488 | self.ui.tab_widget.setTabText( |
| 489 | self.ui.tab_widget.indexOf(self.ui.preferences_tab), |
| 490 | |
| 491 | === modified file 'ubuntuone/controlpanel/gui/qt/filesyncstatus.py' |
| 492 | --- ubuntuone/controlpanel/gui/qt/filesyncstatus.py 2012-05-10 21:01:30 +0000 |
| 493 | +++ ubuntuone/controlpanel/gui/qt/filesyncstatus.py 2012-08-22 21:22:18 +0000 |
| 494 | @@ -22,92 +22,19 @@ |
| 495 | from ubuntuone.controlpanel import backend, cache |
| 496 | from ubuntuone.controlpanel.logger import setup_logging, log_call |
| 497 | from ubuntuone.controlpanel.gui import ( |
| 498 | - ERROR_COLOR, |
| 499 | - FILE_SYNC_CONNECT, |
| 500 | - FILE_SYNC_CONNECT_TOOLTIP, |
| 501 | - FILE_SYNC_DISABLED, |
| 502 | - FILE_SYNC_DISCONNECT, |
| 503 | - FILE_SYNC_DISCONNECT_TOOLTIP, |
| 504 | - FILE_SYNC_DISCONNECTED, |
| 505 | - FILE_SYNC_ENABLE, |
| 506 | - FILE_SYNC_ENABLE_TOOLTIP, |
| 507 | - FILE_SYNC_ERROR, |
| 508 | - FILE_SYNC_IDLE, |
| 509 | - FILE_SYNC_RESTART, |
| 510 | - FILE_SYNC_RESTART_TOOLTIP, |
| 511 | - FILE_SYNC_START, |
| 512 | - FILE_SYNC_START_TOOLTIP, |
| 513 | - FILE_SYNC_STARTING, |
| 514 | - FILE_SYNC_STOP, |
| 515 | - FILE_SYNC_STOP_TOOLTIP, |
| 516 | - FILE_SYNC_STOPPED, |
| 517 | - FILE_SYNC_SYNCING, |
| 518 | LOADING, |
| 519 | PLEASE_WAIT, |
| 520 | ) |
| 521 | -from ubuntuone.controlpanel.gui.qt import pixmap_from_name |
| 522 | +from ubuntuone.controlpanel.gui.qt import ( |
| 523 | + FILE_SYNC_STATUS, |
| 524 | + pixmap_from_name, |
| 525 | +) |
| 526 | from ubuntuone.controlpanel.gui.qt.ui import filesyncstatus_ui |
| 527 | |
| 528 | |
| 529 | logger = setup_logging('qt.filesyncstatus') |
| 530 | |
| 531 | |
| 532 | -WARNING_MARKUP = '<font color="%s"><b>%%s</b></font>' % ERROR_COLOR |
| 533 | -DISCONNECTED_ICON = 'sync_status_disconnected' |
| 534 | -ERROR_ICON = 'sync_status_alert' |
| 535 | -IDLE_ICON = 'sync_status_sync_done' |
| 536 | -SYNCING_ICON = 'sync_status_syncing' |
| 537 | - |
| 538 | -FILE_SYNC_STATUS = { |
| 539 | - backend.FILE_SYNC_DISABLED: |
| 540 | - {'msg': FILE_SYNC_DISABLED, 'action': FILE_SYNC_ENABLE, |
| 541 | - 'backend_method': 'enable_files', |
| 542 | - 'icon': ERROR_ICON, |
| 543 | - 'tooltip': FILE_SYNC_ENABLE_TOOLTIP}, |
| 544 | - backend.FILE_SYNC_DISCONNECTED: |
| 545 | - {'msg': FILE_SYNC_DISCONNECTED, 'action': FILE_SYNC_CONNECT, |
| 546 | - 'backend_method': 'connect_files', |
| 547 | - 'icon': DISCONNECTED_ICON, |
| 548 | - 'tooltip': FILE_SYNC_CONNECT_TOOLTIP}, |
| 549 | - backend.FILE_SYNC_ERROR: |
| 550 | - {'msg': WARNING_MARKUP % FILE_SYNC_ERROR, 'action': FILE_SYNC_RESTART, |
| 551 | - 'backend_method': 'restart_files', |
| 552 | - 'icon': ERROR_ICON, |
| 553 | - 'tooltip': FILE_SYNC_RESTART_TOOLTIP}, |
| 554 | - backend.FILE_SYNC_IDLE: |
| 555 | - {'msg': FILE_SYNC_IDLE, 'action': FILE_SYNC_DISCONNECT, |
| 556 | - 'backend_method': 'disconnect_files', |
| 557 | - 'icon': IDLE_ICON, |
| 558 | - 'tooltip': FILE_SYNC_DISCONNECT_TOOLTIP}, |
| 559 | - backend.FILE_SYNC_STARTING: |
| 560 | - {'msg': FILE_SYNC_STARTING, 'action': FILE_SYNC_STOP, |
| 561 | - 'backend_method': 'stop_files', |
| 562 | - 'icon': SYNCING_ICON, |
| 563 | - 'tooltip': FILE_SYNC_STOP_TOOLTIP}, |
| 564 | - backend.FILE_SYNC_STOPPED: |
| 565 | - {'msg': FILE_SYNC_STOPPED, 'action': FILE_SYNC_START, |
| 566 | - 'backend_method': 'start_files', |
| 567 | - 'icon': ERROR_ICON, |
| 568 | - 'tooltip': FILE_SYNC_START_TOOLTIP}, |
| 569 | - backend.FILE_SYNC_SYNCING: |
| 570 | - {'msg': FILE_SYNC_SYNCING, 'action': FILE_SYNC_DISCONNECT, |
| 571 | - 'backend_method': 'disconnect_files', |
| 572 | - 'icon': SYNCING_ICON, |
| 573 | - 'tooltip': FILE_SYNC_DISCONNECT_TOOLTIP}, |
| 574 | - backend.FILE_SYNC_UNKNOWN: |
| 575 | - {'msg': WARNING_MARKUP % FILE_SYNC_ERROR, 'action': FILE_SYNC_RESTART, |
| 576 | - 'backend_method': 'restart_files', |
| 577 | - 'icon': ERROR_ICON, |
| 578 | - 'tooltip': FILE_SYNC_RESTART_TOOLTIP}, |
| 579 | -} |
| 580 | - |
| 581 | - |
| 582 | -def icon_name_from_status(status_key): |
| 583 | - """Get the icon name for the status.""" |
| 584 | - icon_name = FILE_SYNC_STATUS[status_key]['icon'] |
| 585 | - return icon_name |
| 586 | - |
| 587 | - |
| 588 | class FileSyncStatus(cache.Cache, QtGui.QWidget): |
| 589 | """The FileSyncStatus widget""" |
| 590 | |
| 591 | |
| 592 | === modified file 'ubuntuone/controlpanel/gui/qt/folders.py' |
| 593 | --- ubuntuone/controlpanel/gui/qt/folders.py 2012-06-04 16:25:39 +0000 |
| 594 | +++ ubuntuone/controlpanel/gui/qt/folders.py 2012-08-22 21:22:18 +0000 |
| 595 | @@ -26,6 +26,8 @@ |
| 596 | from PyQt4 import QtGui, QtCore |
| 597 | from twisted.internet import defer |
| 598 | |
| 599 | +from ubuntuone.platform import is_link |
| 600 | + |
| 601 | from ubuntuone.controlpanel.utils import default_folders |
| 602 | from ubuntuone.controlpanel.logger import setup_logging, log_call |
| 603 | from ubuntuone.controlpanel.gui import ( |
| 604 | @@ -38,6 +40,7 @@ |
| 605 | FOLDERS_COLUMN_NAME, |
| 606 | FOLDERS_COLUMN_SYNC_LOCALLY, |
| 607 | FOLDERS_CONFIRM_MERGE, |
| 608 | + FOLDER_EXISTS_AS_FILE, |
| 609 | FOLDERS_MANAGE_LABEL, |
| 610 | GET_MORE_STORAGE, |
| 611 | humanize, |
| 612 | @@ -328,16 +331,23 @@ |
| 613 | os.path.exists(volume_path)) |
| 614 | response = YES |
| 615 | if subscribed and os.path.exists(volume_path): |
| 616 | - text = FOLDERS_CONFIRM_MERGE % {'folder_path': volume_path} |
| 617 | - buttons = YES | NO | CANCEL |
| 618 | - response = QtGui.QMessageBox.warning(self, '', text, buttons, YES) |
| 619 | + if os.path.isdir(volume_path) and not is_link(volume_path): |
| 620 | + text = FOLDERS_CONFIRM_MERGE % {'folder_path': volume_path} |
| 621 | + buttons = YES | NO | CANCEL |
| 622 | + response = QtGui.QMessageBox.warning(self, '', text, buttons, |
| 623 | + YES) |
| 624 | + else: |
| 625 | + text = FOLDER_EXISTS_AS_FILE % {'folder_path': volume_path} |
| 626 | + buttons = CLOSE |
| 627 | + QtGui.QMessageBox.warning(self, '', text, buttons) |
| 628 | + response = NO |
| 629 | |
| 630 | self.is_processing = True |
| 631 | |
| 632 | if response == YES: |
| 633 | # user accepted, merge the folder content |
| 634 | yield self.backend.change_volume_settings(volume_id, |
| 635 | - {'subscribed': subscribed}) |
| 636 | + {'subscribed': subscribed}) |
| 637 | self.load() |
| 638 | else: |
| 639 | # restore old value |
| 640 | |
| 641 | === modified file 'ubuntuone/controlpanel/gui/qt/gui.py' |
| 642 | --- ubuntuone/controlpanel/gui/qt/gui.py 2012-07-05 16:43:00 +0000 |
| 643 | +++ ubuntuone/controlpanel/gui/qt/gui.py 2012-08-22 21:22:18 +0000 |
| 644 | @@ -67,7 +67,7 @@ |
| 645 | |
| 646 | def switch_to(self, tabname="folders"): |
| 647 | """Switch control panel to the required tab.""" |
| 648 | - tabnames = ["folders", "devices", "settings", "account"] |
| 649 | + tabnames = ["folders", "share_links", "devices", "settings", "account"] |
| 650 | if tabname in tabnames: |
| 651 | self.ui.control_panel.ui.tab_widget.setCurrentIndex( |
| 652 | tabnames.index(tabname)) |
| 653 | |
| 654 | === modified file 'ubuntuone/controlpanel/gui/qt/main/__init__.py' |
| 655 | --- ubuntuone/controlpanel/gui/qt/main/__init__.py 2012-06-28 04:25:16 +0000 |
| 656 | +++ ubuntuone/controlpanel/gui/qt/main/__init__.py 2012-08-22 21:22:18 +0000 |
| 657 | @@ -17,6 +17,7 @@ |
| 658 | """Provide the correct ui main module.""" |
| 659 | |
| 660 | import argparse |
| 661 | +import os |
| 662 | import sys |
| 663 | |
| 664 | from PyQt4 import QtCore |
| 665 | @@ -37,6 +38,8 @@ |
| 666 | source = dbus_main |
| 667 | # pylint: enable=C0103 |
| 668 | |
| 669 | +from ubuntuone.controlpanel.utils import install_config_and_daemons |
| 670 | + |
| 671 | |
| 672 | def parser_options(): |
| 673 | """Parse command line parameters.""" |
| 674 | @@ -44,8 +47,8 @@ |
| 675 | result.add_argument("--switch-to", dest="switch_to", |
| 676 | metavar="PANEL_NAME", default="", |
| 677 | help="Start Ubuntu One in the " |
| 678 | - "PANEL_NAME tab. Possible values are: " |
| 679 | - "folders, devices, settings, account") |
| 680 | + "PANEL_NAME tab. Possible values are: " |
| 681 | + "folders, share_links, devices, settings, account") |
| 682 | result.add_argument("--minimized", dest="minimized", action="store_true", |
| 683 | default=False, |
| 684 | help="Start Ubuntu One " |
| 685 | @@ -69,6 +72,15 @@ |
| 686 | |
| 687 | def main(args, install_reactor_darwin=False): |
| 688 | """Start the Qt mainloop and open the main window.""" |
| 689 | + |
| 690 | + # Disable the overlay-scrollbar GTK module that was |
| 691 | + # added in Ubuntu 12.10 because it breaks Qt (LP:1007421) |
| 692 | + gtk_mod = os.getenv('GTK_MODULES') |
| 693 | + if gtk_mod is not None: |
| 694 | + gtk_mod = ':'.join([mod for mod in |
| 695 | + gtk_mod.split(':') if mod != 'overlay-scrollbar']) |
| 696 | + os.environ['GTK_MODULES'] = gtk_mod |
| 697 | + |
| 698 | # The following cannot be imported outside this function |
| 699 | # because u1trial already provides a reactor. |
| 700 | |
| 701 | @@ -109,6 +121,8 @@ |
| 702 | data.append(unicode(qss.data())) |
| 703 | app.setStyleSheet('\n'.join(data)) |
| 704 | |
| 705 | + install_config_and_daemons() |
| 706 | + |
| 707 | # Unused variable 'window', 'icon', pylint: disable=W0612 |
| 708 | icon, window = start(lambda: source.main_quit(app), |
| 709 | minimized=minimized, with_icon=with_icon, |
| 710 | |
| 711 | === modified file 'ubuntuone/controlpanel/gui/qt/main/tests/test_main.py' |
| 712 | --- ubuntuone/controlpanel/gui/qt/main/tests/test_main.py 2012-06-28 04:14:08 +0000 |
| 713 | +++ ubuntuone/controlpanel/gui/qt/main/tests/test_main.py 2012-08-22 21:22:18 +0000 |
| 714 | @@ -16,6 +16,7 @@ |
| 715 | |
| 716 | """Tests for the control panel's Qt main.""" |
| 717 | |
| 718 | +import os |
| 719 | import sys |
| 720 | |
| 721 | from PyQt4 import QtCore |
| 722 | @@ -146,6 +147,32 @@ |
| 723 | (['ubuntuone-installer', sys.argv[0]], |
| 724 | ('ubuntuone-control-panel',), {})) |
| 725 | |
| 726 | + def _test_gtk_module_cleanup(self, modules, expected): |
| 727 | + """Check if the module cleanup works.""" |
| 728 | + old_modules = os.environ.get('GTK_MODULES', '') |
| 729 | + |
| 730 | + def clean_env(): |
| 731 | + """Reset the environment variable.""" |
| 732 | + os.environ['GTK_MODULES'] = old_modules |
| 733 | + |
| 734 | + self.addCleanup(clean_env) |
| 735 | + os.environ['GTK_MODULES'] = modules |
| 736 | + main.main([sys.argv[0]]) |
| 737 | + self.assertEqual(os.environ['GTK_MODULES'], expected) |
| 738 | + |
| 739 | + def test_gtk_module_cleanup_1(self): |
| 740 | + """Test that we deactivate the overlay scrollbar GTK module.""" |
| 741 | + self._test_gtk_module_cleanup('aaa:overlay-scrollbar:bbb', 'aaa:bbb') |
| 742 | + |
| 743 | + def test_gtk_module_cleanup_2(self): |
| 744 | + """Test that we deactivate the overlay scrollbar GTK module.""" |
| 745 | + self._test_gtk_module_cleanup('overlay-scrollbar', '') |
| 746 | + |
| 747 | + def test_gtk_module_cleanup_3(self): |
| 748 | + """Test that we deactivate the overlay scrollbar GTK module.""" |
| 749 | + self._test_gtk_module_cleanup('overlay-scrollbar:overlay-scrollbars', |
| 750 | + 'overlay-scrollbars') |
| 751 | + |
| 752 | def test_title_not_fail(self): |
| 753 | """Ensure -title is removed before it gets to argparse.""" |
| 754 | main.main([sys.argv[0], "-title"]) |
| 755 | @@ -233,3 +260,11 @@ |
| 756 | self.patch(sys, 'platform', 'not-darwin') |
| 757 | main.main([sys.argv[0]], install_reactor_darwin=False) |
| 758 | self.assertEqual(self.qt4reactor_installed, False) |
| 759 | + |
| 760 | + def test_install_config(self): |
| 761 | + """Test that install_config_and_daemons is called.""" |
| 762 | + self.patch(main, 'install_config_and_daemons', |
| 763 | + self._set_called) |
| 764 | + |
| 765 | + main.main([sys.argv[0]], install_reactor_darwin=False) |
| 766 | + self.assertEqual(self._called, ((), {})) |
| 767 | |
| 768 | === added file 'ubuntuone/controlpanel/gui/qt/share_links.py' |
| 769 | --- ubuntuone/controlpanel/gui/qt/share_links.py 1970-01-01 00:00:00 +0000 |
| 770 | +++ ubuntuone/controlpanel/gui/qt/share_links.py 2012-08-22 21:22:18 +0000 |
| 771 | @@ -0,0 +1,42 @@ |
| 772 | +# -*- coding: utf-8 *-* |
| 773 | + |
| 774 | +# Copyright 2012 Canonical Ltd. |
| 775 | +# |
| 776 | +# This program is free software: you can redistribute it and/or modify it |
| 777 | +# under the terms of the GNU General Public License version 3, as published |
| 778 | +# by the Free Software Foundation. |
| 779 | +# |
| 780 | +# This program is distributed in the hope that it will be useful, but |
| 781 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
| 782 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
| 783 | +# PURPOSE. See the GNU General Public License for more details. |
| 784 | +# |
| 785 | +# You should have received a copy of the GNU General Public License along |
| 786 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
| 787 | + |
| 788 | +"""The user interface for the control panel for Ubuntu One.""" |
| 789 | + |
| 790 | +from ubuntuone.controlpanel.logger import setup_logging |
| 791 | +from ubuntuone.controlpanel.gui import ( |
| 792 | + SEARCH_FILES, |
| 793 | + SHARED_FILES, |
| 794 | +) |
| 795 | + |
| 796 | +from ubuntuone.controlpanel.gui.qt.ui import share_links_ui |
| 797 | +from ubuntuone.controlpanel.gui.qt.ubuntuonebin import UbuntuOneBin |
| 798 | + |
| 799 | + |
| 800 | +logger = setup_logging('qt.share_links') |
| 801 | + |
| 802 | + |
| 803 | +class ShareLinksPanel(UbuntuOneBin): |
| 804 | + """The Share Links Tab Panel widget""" |
| 805 | + |
| 806 | + ui_class = share_links_ui |
| 807 | + logger = logger |
| 808 | + |
| 809 | + def _setup(self): |
| 810 | + """Do some extra setupping for the UI.""" |
| 811 | + super(ShareLinksPanel, self)._setup() |
| 812 | + self.ui.search_files_lbl.setText(SEARCH_FILES) |
| 813 | + self.ui.shared_group.setTitle(SHARED_FILES) |
| 814 | |
| 815 | === modified file 'ubuntuone/controlpanel/gui/qt/systray.py' |
| 816 | --- ubuntuone/controlpanel/gui/qt/systray.py 2012-05-14 20:16:02 +0000 |
| 817 | +++ ubuntuone/controlpanel/gui/qt/systray.py 2012-08-22 21:22:18 +0000 |
| 818 | @@ -15,17 +15,94 @@ |
| 819 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
| 820 | """System notification area icon.""" |
| 821 | |
| 822 | -from PyQt4 import QtGui |
| 823 | +import os |
| 824 | +import sys |
| 825 | +from operator import itemgetter |
| 826 | + |
| 827 | +from PyQt4 import QtGui, QtCore |
| 828 | from twisted.internet.defer import inlineCallbacks |
| 829 | from ubuntuone.platform.tools import SyncDaemonTool |
| 830 | |
| 831 | +from ubuntuone.controlpanel import backend, cache |
| 832 | +from ubuntuone.controlpanel.logger import setup_logging |
| 833 | from ubuntuone.controlpanel.gui import ( |
| 834 | - RESTORE_LABEL, |
| 835 | - QUIT_LABEL, |
| 836 | -) |
| 837 | - |
| 838 | - |
| 839 | -class TrayIcon(QtGui.QSystemTrayIcon): |
| 840 | + ACCEPT_SHARES, |
| 841 | + DASHBOARD, |
| 842 | + GET_HELP_ONLINE, |
| 843 | + GET_MORE_STORAGE, |
| 844 | + GET_STORAGE_LINK, |
| 845 | + GET_SUPPORT_LINK, |
| 846 | + GO_TO_WEB, |
| 847 | + IN_PROGRESS, |
| 848 | + IN_PROGRESS_FILE, |
| 849 | + LOADING, |
| 850 | + NEW_SHARE_BY, |
| 851 | + OPEN_UBUNTU_ONE, |
| 852 | + OPEN_UBUNTU_ONE_FOLDER, |
| 853 | + PLEASE_WAIT, |
| 854 | + RECENT_TRANSFERS, |
| 855 | + RECENTTRANSFERS, |
| 856 | + TRANSFERS, |
| 857 | + UPLOADING, |
| 858 | +) |
| 859 | +from ubuntuone.controlpanel.gui.qt import ( |
| 860 | + FILE_SYNC_STATUS, |
| 861 | + icon_from_name, |
| 862 | +) |
| 863 | + |
| 864 | + |
| 865 | +logger = setup_logging('qt.systray') |
| 866 | + |
| 867 | + |
| 868 | +class ProgressBarAction(QtGui.QWidgetAction): |
| 869 | + |
| 870 | + """Menu action that display a progress bar for the uploads.""" |
| 871 | + |
| 872 | + def __init__(self, filename, percentage, parent=None): |
| 873 | + super(ProgressBarAction, self).__init__(parent) |
| 874 | + self._filename = os.path.basename(filename) |
| 875 | + self.widget = QtGui.QWidget(None) |
| 876 | + text = IN_PROGRESS_FILE % (self._filename, percentage) |
| 877 | + self.setText(text) |
| 878 | + self.label = QtGui.QLabel(text) |
| 879 | + self.progress = QtGui.QProgressBar() |
| 880 | + self.progress.setTextVisible(False) |
| 881 | + self.progress.setValue(percentage) |
| 882 | + self.progress.setFixedHeight(10) |
| 883 | + vbox = QtGui.QVBoxLayout() |
| 884 | + vbox.setContentsMargins(10, 5, 10, 5) |
| 885 | + vbox.addWidget(self.label) |
| 886 | + vbox.addWidget(self.progress) |
| 887 | + self.widget.setLayout(vbox) |
| 888 | + |
| 889 | + self.setDefaultWidget(self.widget) |
| 890 | + |
| 891 | + def set_value(self, progress): |
| 892 | + """Set the value of the progress bar.""" |
| 893 | + text = IN_PROGRESS_FILE % (self._filename, progress) |
| 894 | + self.setText(text) |
| 895 | + self.label.setText(text) |
| 896 | + self.setText(text) |
| 897 | + self.progress.setValue(progress) |
| 898 | + |
| 899 | + |
| 900 | +class SharesAction(QtGui.QAction): |
| 901 | + |
| 902 | + """New Shares action.""" |
| 903 | + |
| 904 | + def __init__(self, text, volume_id, parent=None): |
| 905 | + super(SharesAction, self).__init__(text, parent) |
| 906 | + self._volume_id = volume_id |
| 907 | + |
| 908 | + self.triggered.connect(self.accept_share) |
| 909 | + |
| 910 | + def accept_share(self): |
| 911 | + """Open the accept shares page.""" |
| 912 | + url = ACCEPT_SHARES % self._volume_id |
| 913 | + QtGui.QDesktopServices.openUrl(QtCore.QUrl(url)) |
| 914 | + |
| 915 | + |
| 916 | +class TrayIcon(QtGui.QSystemTrayIcon, cache.Cache): |
| 917 | |
| 918 | """System notification icon.""" |
| 919 | |
| 920 | @@ -34,23 +111,162 @@ |
| 921 | self.setIcon(QtGui.QIcon(":/icon.png")) |
| 922 | self.setVisible(True) |
| 923 | self.window = window |
| 924 | - self.activated.connect(self.on_activated) |
| 925 | + self.root_path = '' |
| 926 | + self.recent_transfers = {} |
| 927 | + self.uploading = {} |
| 928 | + self._backend_method = None |
| 929 | + self._previous_shares = None |
| 930 | + self.close_callback = close_callback |
| 931 | self.context_menu = QtGui.QMenu() |
| 932 | - self.restore = QtGui.QAction(RESTORE_LABEL, self, |
| 933 | - triggered=self.restore_window) |
| 934 | - self.quit = QtGui.QAction(QUIT_LABEL, self, |
| 935 | - triggered=self.stop) |
| 936 | - self.context_menu.addAction(self.restore) |
| 937 | - self.context_menu.addSeparator() |
| 938 | - self.context_menu.addAction(self.quit) |
| 939 | - self.setContextMenu(self.context_menu) |
| 940 | - |
| 941 | - self.close_callback = close_callback |
| 942 | - |
| 943 | - def on_activated(self, reason): |
| 944 | - """The user activated the icon.""" |
| 945 | - if reason == self.Trigger: # Left-click |
| 946 | - self.restore_window() |
| 947 | + self.status = None |
| 948 | + self.quit = None |
| 949 | + self.get_help_online = None |
| 950 | + self.open_u1 = None |
| 951 | + self.status_action = None |
| 952 | + self.go_to_web = None |
| 953 | + self.get_more_storage = None |
| 954 | + self.transfers = None |
| 955 | + self.open_u1_folder = None |
| 956 | + |
| 957 | + self.load_menu() |
| 958 | + # Refresh the Shares every five minutes if needed. |
| 959 | + self._timer_id = self.startTimer(300000) |
| 960 | + |
| 961 | + # pylint: disable=C0103 |
| 962 | + |
| 963 | + def timerEvent(self, event): |
| 964 | + """Update the menu on each iteration.""" |
| 965 | + self.load_menu() |
| 966 | + |
| 967 | + # pylint: enable=C0103 |
| 968 | + |
| 969 | + @inlineCallbacks |
| 970 | + def load_menu(self): |
| 971 | + """Load the content of the menu.""" |
| 972 | + shares_info = yield self.backend.get_shares() |
| 973 | + shares_info = [share for share in shares_info if not share['accepted']] |
| 974 | + if shares_info != self._previous_shares: |
| 975 | + # Items |
| 976 | + self.context_menu.clear() |
| 977 | + |
| 978 | + self.status = self.context_menu.addAction(LOADING) |
| 979 | + self.status.setEnabled(False) |
| 980 | + self.status_action = self.context_menu.addAction(PLEASE_WAIT) |
| 981 | + self.refresh_status() |
| 982 | + self.context_menu.addSeparator() |
| 983 | + |
| 984 | + self.open_u1 = self.context_menu.addAction(OPEN_UBUNTU_ONE) |
| 985 | + # TODO: Share a file action when the Shares tab is ready in U1-CP |
| 986 | + self.open_u1_folder = self.context_menu.addAction( |
| 987 | + OPEN_UBUNTU_ONE_FOLDER) |
| 988 | + self.go_to_web = self.context_menu.addAction(GO_TO_WEB) |
| 989 | + self.context_menu.addSeparator() |
| 990 | + |
| 991 | + # Shares |
| 992 | + self._previous_shares = shares_info |
| 993 | + max_shares = 0 |
| 994 | + for share in self._previous_shares: |
| 995 | + if max_shares == 3: |
| 996 | + break |
| 997 | + max_shares += 1 |
| 998 | + text = NEW_SHARE_BY % share['other_visible_name'] |
| 999 | + share_action = SharesAction(text, share['volume_id'], |
| 1000 | + self.context_menu) |
| 1001 | + self.context_menu.addAction(share_action) |
| 1002 | + if self._previous_shares: |
| 1003 | + self.context_menu.addSeparator() |
| 1004 | + |
| 1005 | + # Transfers |
| 1006 | + self.transfers = TransfersMenu(self) |
| 1007 | + self.context_menu.addMenu(self.transfers) |
| 1008 | + |
| 1009 | + self.get_more_storage = self.context_menu.addAction( |
| 1010 | + GET_MORE_STORAGE) |
| 1011 | + self.get_help_online = self.context_menu.addAction(GET_HELP_ONLINE) |
| 1012 | + self.quit = self.context_menu.addAction("Quit") |
| 1013 | + |
| 1014 | + self.setContextMenu(self.context_menu) |
| 1015 | + |
| 1016 | + # Connect Signals |
| 1017 | + self.status_action.triggered.connect(self.change_status) |
| 1018 | + self.open_u1.triggered.connect(self.restore_window) |
| 1019 | + self.open_u1_folder.triggered.connect(self.open_u1_folder_action) |
| 1020 | + self.get_more_storage.triggered.connect( |
| 1021 | + self.get_more_storage_action) |
| 1022 | + self.go_to_web.triggered.connect(self.go_to_web_action) |
| 1023 | + self.get_help_online.triggered.connect(self.get_help_action) |
| 1024 | + self.quit.triggered.connect(self.stop) |
| 1025 | + |
| 1026 | + self._get_volumes_info() |
| 1027 | + |
| 1028 | + @inlineCallbacks |
| 1029 | + def refresh_status(self): |
| 1030 | + """Update Ubuntu One status.""" |
| 1031 | + info = yield self.backend.file_sync_status() |
| 1032 | + self._process_status(info) |
| 1033 | + self.backend.status_changed_handler = self._process_status |
| 1034 | + |
| 1035 | + def _process_status(self, status): |
| 1036 | + """Match status with signals.""" |
| 1037 | + if status is None: |
| 1038 | + return |
| 1039 | + try: |
| 1040 | + status_key = status[backend.STATUS_KEY] |
| 1041 | + data = FILE_SYNC_STATUS[status_key] |
| 1042 | + except (KeyError, TypeError): |
| 1043 | + logger.exception( |
| 1044 | + '_process_status: received unknown status dict %r', status) |
| 1045 | + return |
| 1046 | + |
| 1047 | + self.status_action.setEnabled(True) |
| 1048 | + icon_name = data.get('icon') |
| 1049 | + icon = QtGui.QIcon() |
| 1050 | + if icon_name is not None: |
| 1051 | + icon = icon_from_name(icon_name) |
| 1052 | + text = data.get('msg') |
| 1053 | + self.status.setIcon(icon) |
| 1054 | + self.status.setText(text) |
| 1055 | + |
| 1056 | + action = data.get('action') |
| 1057 | + self._backend_method = getattr(self.backend, |
| 1058 | + data['backend_method'], None) |
| 1059 | + self.status_action.setText(action) |
| 1060 | + |
| 1061 | + def change_status(self): |
| 1062 | + """Change the Syncing status of syncdaemon.""" |
| 1063 | + if self._backend_method is not None: |
| 1064 | + self.status_action.setEnabled(False) |
| 1065 | + self._backend_method() |
| 1066 | + else: |
| 1067 | + logger.error('on_sync_status_button_clicked: backend method is ' |
| 1068 | + 'None!') |
| 1069 | + |
| 1070 | + @inlineCallbacks |
| 1071 | + def _get_volumes_info(self): |
| 1072 | + """Get the volumes info.""" |
| 1073 | + info = yield self.backend.volumes_info() |
| 1074 | + self._process_volumes_info(info) |
| 1075 | + |
| 1076 | + def _process_volumes_info(self, info): |
| 1077 | + """Process the volumes info.""" |
| 1078 | + _, _, volumes = info[0] |
| 1079 | + self.root_path = u'file://%s' % unicode(volumes[0]['path']) |
| 1080 | + |
| 1081 | + def open_u1_folder_action(self): |
| 1082 | + """Open the web to get more storage.""" |
| 1083 | + QtGui.QDesktopServices.openUrl(QtCore.QUrl(self.root_path)) |
| 1084 | + |
| 1085 | + def get_more_storage_action(self): |
| 1086 | + """Open the web to get more storage.""" |
| 1087 | + QtGui.QDesktopServices.openUrl(QtCore.QUrl(GET_STORAGE_LINK)) |
| 1088 | + |
| 1089 | + def go_to_web_action(self): |
| 1090 | + """Open the web in the dashboard.""" |
| 1091 | + QtGui.QDesktopServices.openUrl(QtCore.QUrl(DASHBOARD)) |
| 1092 | + |
| 1093 | + def get_help_action(self): |
| 1094 | + """Open the web in the dashboard.""" |
| 1095 | + QtGui.QDesktopServices.openUrl(QtCore.QUrl(GET_SUPPORT_LINK)) |
| 1096 | |
| 1097 | def restore_window(self): |
| 1098 | """Show the main window.""" |
| 1099 | @@ -81,3 +297,80 @@ |
| 1100 | # Maybe it was not running? |
| 1101 | pass |
| 1102 | self.close_callback() |
| 1103 | + |
| 1104 | + |
| 1105 | +class TransfersMenu(QtGui.QMenu): |
| 1106 | + |
| 1107 | + """Menu that shows recent and current transfers.""" |
| 1108 | + |
| 1109 | + def __init__(self, parent): |
| 1110 | + super(TransfersMenu, self).__init__(TRANSFERS) |
| 1111 | + self._parent = parent |
| 1112 | + self.uploading = {} |
| 1113 | + self.previous_data = {} |
| 1114 | + |
| 1115 | + if sys.platform not in ('win32', 'darwin'): |
| 1116 | + self._timer_id = self.startTimer(1000) |
| 1117 | + |
| 1118 | + # pylint: disable=C0103 |
| 1119 | + |
| 1120 | + def showEvent(self, event): |
| 1121 | + """Executed when the transfers menu is shown.""" |
| 1122 | + logger.info('This is never executed on Ubuntu') |
| 1123 | + super(TransfersMenu, self).showEvent(event) |
| 1124 | + self.get_transfers_data() |
| 1125 | + self._timer_id = self.startTimer(1000) |
| 1126 | + |
| 1127 | + def hideEvent(self, event): |
| 1128 | + """Executed when the transfers menu is hidden.""" |
| 1129 | + logger.info('This is never executed on Ubuntu') |
| 1130 | + super(TransfersMenu, self).hideEvent(event) |
| 1131 | + self.killTimer(self._timer_id) |
| 1132 | + |
| 1133 | + def timerEvent(self, event): |
| 1134 | + """Update the menu on each iteration.""" |
| 1135 | + self.get_transfers_data() |
| 1136 | + |
| 1137 | + # pylint: enable=C0103 |
| 1138 | + |
| 1139 | + @inlineCallbacks |
| 1140 | + def get_transfers_data(self): |
| 1141 | + """Get the transfers data to create the submenu.""" |
| 1142 | + data = yield self._parent.backend.sync_menu() |
| 1143 | + self._update_transfers(data) |
| 1144 | + |
| 1145 | + def _update_transfers(self, data): |
| 1146 | + """Generate the transfers menu.""" |
| 1147 | + current_transfers = data[RECENTTRANSFERS] |
| 1148 | + current_upload = [ |
| 1149 | + filename for filename, _, _ in data[UPLOADING]] |
| 1150 | + uploading_data = data[UPLOADING] |
| 1151 | + uploading_data.sort(key=itemgetter(2)) |
| 1152 | + uploading_data.reverse() |
| 1153 | + if current_transfers != self.previous_data.get('transfers') or \ |
| 1154 | + current_upload != self.previous_data.get('uploading'): |
| 1155 | + self.clear() |
| 1156 | + self.previous_data['transfers'] = current_transfers |
| 1157 | + self.previous_data['uploading'] = current_upload |
| 1158 | + self.uploading.clear() |
| 1159 | + recent = self.addAction(RECENT_TRANSFERS) |
| 1160 | + recent.setEnabled(False) |
| 1161 | + for filename in data[RECENTTRANSFERS]: |
| 1162 | + self.addAction(os.path.basename(filename)) |
| 1163 | + self.addSeparator() |
| 1164 | + in_progress = self.addAction(IN_PROGRESS) |
| 1165 | + in_progress.setEnabled(False) |
| 1166 | + show_max = 0 |
| 1167 | + for filename, size, written in uploading_data: |
| 1168 | + if show_max == 5: |
| 1169 | + break |
| 1170 | + percentage = written * 100 / size |
| 1171 | + self.uploading[filename] = ProgressBarAction( |
| 1172 | + filename, percentage) |
| 1173 | + self.addAction(self.uploading[filename]) |
| 1174 | + show_max += 1 |
| 1175 | + else: |
| 1176 | + for filename, size, written in uploading_data: |
| 1177 | + percentage = written * 100 / size |
| 1178 | + if filename in self.uploading: |
| 1179 | + self.uploading[filename].set_value(percentage) |
| 1180 | |
| 1181 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/__init__.py' |
| 1182 | --- ubuntuone/controlpanel/gui/qt/tests/__init__.py 2012-04-02 11:10:49 +0000 |
| 1183 | +++ ubuntuone/controlpanel/gui/qt/tests/__init__.py 2012-08-22 21:22:18 +0000 |
| 1184 | @@ -25,7 +25,11 @@ |
| 1185 | |
| 1186 | from ubuntuone.controlpanel import backend, cache |
| 1187 | from ubuntuone.controlpanel.tests import TestCase, EXPECTED_ACCOUNT_INFO, TOKEN |
| 1188 | -from ubuntuone.controlpanel.gui import qt |
| 1189 | +from ubuntuone.controlpanel.gui import ( |
| 1190 | + qt, |
| 1191 | + RECENTTRANSFERS, |
| 1192 | + UPLOADING, |
| 1193 | +) |
| 1194 | from ubuntuone.controlpanel.gui.tests import ( |
| 1195 | FakedObject, |
| 1196 | FAKE_VOLUMES_INFO, |
| 1197 | @@ -164,6 +168,17 @@ |
| 1198 | def build_signed_iri(self, iri): |
| 1199 | """Fake iri signing.""" |
| 1200 | |
| 1201 | + def sync_menu(self): |
| 1202 | + """Fake sync_menu.""" |
| 1203 | + data = {} |
| 1204 | + data[RECENTTRANSFERS] = [] |
| 1205 | + data[UPLOADING] = [] |
| 1206 | + return data |
| 1207 | + |
| 1208 | + def get_shares(self): |
| 1209 | + """Fake get_shares.""" |
| 1210 | + return [] |
| 1211 | + |
| 1212 | |
| 1213 | class CrashyBackendException(Exception): |
| 1214 | """A faked backend crash.""" |
| 1215 | |
| 1216 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_filesyncstatus.py' |
| 1217 | --- ubuntuone/controlpanel/gui/qt/tests/test_filesyncstatus.py 2012-05-10 21:01:30 +0000 |
| 1218 | +++ ubuntuone/controlpanel/gui/qt/tests/test_filesyncstatus.py 2012-08-22 21:22:18 +0000 |
| 1219 | @@ -22,6 +22,7 @@ |
| 1220 | LOADING, |
| 1221 | PLEASE_WAIT, |
| 1222 | ) |
| 1223 | +from ubuntuone.controlpanel.gui import qt |
| 1224 | from ubuntuone.controlpanel.gui.qt import filesyncstatus as gui |
| 1225 | from ubuntuone.controlpanel.gui.qt.tests import BaseTestCase |
| 1226 | |
| 1227 | @@ -64,7 +65,7 @@ |
| 1228 | self.assertEqual(expected_text, actual_text) |
| 1229 | |
| 1230 | actual_icon = self.ui.ui.sync_status_icon.pixmap() |
| 1231 | - pixmap_name = gui.icon_name_from_status(status_bd) |
| 1232 | + pixmap_name = qt.icon_name_from_status(status_bd) |
| 1233 | expected_icon = gui.pixmap_from_name(pixmap_name) |
| 1234 | self.assertEqualPixmaps(expected_icon, actual_icon) |
| 1235 | |
| 1236 | @@ -73,7 +74,7 @@ |
| 1237 | action) |
| 1238 | |
| 1239 | is_default = self.ui.ui.sync_status_button.isDefault() |
| 1240 | - expected_default = (action == gui.FILE_SYNC_CONNECT) |
| 1241 | + expected_default = (action == qt.FILE_SYNC_CONNECT) |
| 1242 | self.assertEqual(expected_default, is_default) |
| 1243 | |
| 1244 | self.ui.ui.sync_status_button.click() |
| 1245 | @@ -96,17 +97,17 @@ |
| 1246 | def test_process_info_invalid_status(self): |
| 1247 | """File sync status invalid, ignore event.""" |
| 1248 | status = {backend.STATUS_KEY: backend.FILE_SYNC_STARTING, |
| 1249 | - backend.MSG_KEY: gui.FILE_SYNC_STARTING, |
| 1250 | + backend.MSG_KEY: qt.FILE_SYNC_STARTING, |
| 1251 | 'icon': backend.FILE_SYNC_STARTING} |
| 1252 | self.ui.process_info(status) |
| 1253 | self.ui.process_info(3) |
| 1254 | |
| 1255 | actual_icon = self.ui.ui.sync_status_icon.pixmap() |
| 1256 | - pixmap_name = gui.icon_name_from_status(backend.FILE_SYNC_STARTING) |
| 1257 | + pixmap_name = qt.icon_name_from_status(backend.FILE_SYNC_STARTING) |
| 1258 | expected_icon = gui.pixmap_from_name(pixmap_name) |
| 1259 | self.assertEqualPixmaps(expected_icon, actual_icon) |
| 1260 | actual_text = unicode(self.ui.ui.sync_status_label.text()) |
| 1261 | - self.assertEqual(gui.FILE_SYNC_STARTING, actual_text) |
| 1262 | + self.assertEqual(qt.FILE_SYNC_STARTING, actual_text) |
| 1263 | |
| 1264 | def test_process_info_changed(self): |
| 1265 | """Backend's file sync status changed callback is connected.""" |
| 1266 | @@ -120,70 +121,70 @@ |
| 1267 | def test_process_info_disabled(self): |
| 1268 | """File sync status disabled update the label.""" |
| 1269 | self.assert_status_correct(status_bd=backend.FILE_SYNC_DISABLED, |
| 1270 | - status_ui=gui.FILE_SYNC_DISABLED, |
| 1271 | - action=gui.FILE_SYNC_ENABLE, |
| 1272 | + status_ui=qt.FILE_SYNC_DISABLED, |
| 1273 | + action=qt.FILE_SYNC_ENABLE, |
| 1274 | callback='enable_files', |
| 1275 | - tooltip=gui.FILE_SYNC_ENABLE_TOOLTIP) |
| 1276 | + tooltip=qt.FILE_SYNC_ENABLE_TOOLTIP) |
| 1277 | |
| 1278 | def test_process_info_disconnected(self): |
| 1279 | """File sync status disconnected update the label.""" |
| 1280 | self.assert_status_correct(status_bd=backend.FILE_SYNC_DISCONNECTED, |
| 1281 | - status_ui=gui.FILE_SYNC_DISCONNECTED, |
| 1282 | - action=gui.FILE_SYNC_CONNECT, |
| 1283 | + status_ui=qt.FILE_SYNC_DISCONNECTED, |
| 1284 | + action=qt.FILE_SYNC_CONNECT, |
| 1285 | callback='connect_files', |
| 1286 | - tooltip=gui.FILE_SYNC_CONNECT_TOOLTIP) |
| 1287 | + tooltip=qt.FILE_SYNC_CONNECT_TOOLTIP) |
| 1288 | |
| 1289 | def test_process_info_error(self): |
| 1290 | """File sync status error update the label.""" |
| 1291 | - msg = gui.WARNING_MARKUP % gui.FILE_SYNC_ERROR |
| 1292 | + msg = qt.WARNING_MARKUP % qt.FILE_SYNC_ERROR |
| 1293 | self.assert_status_correct(status_bd=backend.FILE_SYNC_ERROR, |
| 1294 | status_ui=msg, |
| 1295 | msg_bd='what an error!', |
| 1296 | - action=gui.FILE_SYNC_RESTART, |
| 1297 | + action=qt.FILE_SYNC_RESTART, |
| 1298 | callback='restart_files', |
| 1299 | - tooltip=gui.FILE_SYNC_RESTART_TOOLTIP) |
| 1300 | + tooltip=qt.FILE_SYNC_RESTART_TOOLTIP) |
| 1301 | |
| 1302 | def test_process_info_idle(self): |
| 1303 | """File sync status idle update the label.""" |
| 1304 | self.assert_status_correct(status_bd=backend.FILE_SYNC_IDLE, |
| 1305 | - status_ui=gui.FILE_SYNC_IDLE, |
| 1306 | - action=gui.FILE_SYNC_DISCONNECT, |
| 1307 | + status_ui=qt.FILE_SYNC_IDLE, |
| 1308 | + action=qt.FILE_SYNC_DISCONNECT, |
| 1309 | callback='disconnect_files', |
| 1310 | - tooltip=gui.FILE_SYNC_DISCONNECT_TOOLTIP) |
| 1311 | + tooltip=qt.FILE_SYNC_DISCONNECT_TOOLTIP) |
| 1312 | |
| 1313 | def test_process_info_starting(self): |
| 1314 | """File sync status starting update the label.""" |
| 1315 | self.assert_status_correct(status_bd=backend.FILE_SYNC_STARTING, |
| 1316 | - status_ui=gui.FILE_SYNC_STARTING, |
| 1317 | - action=gui.FILE_SYNC_STOP, |
| 1318 | + status_ui=qt.FILE_SYNC_STARTING, |
| 1319 | + action=qt.FILE_SYNC_STOP, |
| 1320 | callback='stop_files', |
| 1321 | - tooltip=gui.FILE_SYNC_STOP_TOOLTIP) |
| 1322 | + tooltip=qt.FILE_SYNC_STOP_TOOLTIP) |
| 1323 | |
| 1324 | def test_process_info_stopped(self): |
| 1325 | """File sync status stopped update the label.""" |
| 1326 | self.assert_status_correct(status_bd=backend.FILE_SYNC_STOPPED, |
| 1327 | - status_ui=gui.FILE_SYNC_STOPPED, |
| 1328 | - action=gui.FILE_SYNC_START, |
| 1329 | + status_ui=qt.FILE_SYNC_STOPPED, |
| 1330 | + action=qt.FILE_SYNC_START, |
| 1331 | callback='start_files', |
| 1332 | - tooltip=gui.FILE_SYNC_START_TOOLTIP) |
| 1333 | + tooltip=qt.FILE_SYNC_START_TOOLTIP) |
| 1334 | |
| 1335 | def test_process_info_syncing(self): |
| 1336 | """File sync status syncing update the label.""" |
| 1337 | self.assert_status_correct(status_bd=backend.FILE_SYNC_SYNCING, |
| 1338 | - status_ui=gui.FILE_SYNC_SYNCING, |
| 1339 | - action=gui.FILE_SYNC_DISCONNECT, |
| 1340 | + status_ui=qt.FILE_SYNC_SYNCING, |
| 1341 | + action=qt.FILE_SYNC_DISCONNECT, |
| 1342 | callback='disconnect_files', |
| 1343 | - tooltip=gui.FILE_SYNC_DISCONNECT_TOOLTIP) |
| 1344 | + tooltip=qt.FILE_SYNC_DISCONNECT_TOOLTIP) |
| 1345 | |
| 1346 | def test_process_info_unknown(self): |
| 1347 | """File sync status unknown update the label.""" |
| 1348 | - msg = gui.WARNING_MARKUP % gui.FILE_SYNC_ERROR |
| 1349 | + msg = qt.WARNING_MARKUP % qt.FILE_SYNC_ERROR |
| 1350 | self.assert_status_correct(status_bd=backend.FILE_SYNC_UNKNOWN, |
| 1351 | status_ui=msg, |
| 1352 | msg_bd='yadda oops', |
| 1353 | - action=gui.FILE_SYNC_RESTART, |
| 1354 | + action=qt.FILE_SYNC_RESTART, |
| 1355 | callback='restart_files', |
| 1356 | - tooltip=gui.FILE_SYNC_RESTART_TOOLTIP) |
| 1357 | + tooltip=qt.FILE_SYNC_RESTART_TOOLTIP) |
| 1358 | |
| 1359 | def test_on_sync_status_button_clicked(self): |
| 1360 | """Check the labels and icon when the button is pressed.""" |
| 1361 | |
| 1362 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_folders.py' |
| 1363 | --- ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-06-22 14:42:29 +0000 |
| 1364 | +++ ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-08-22 21:22:18 +0000 |
| 1365 | @@ -591,6 +591,8 @@ |
| 1366 | def test_confirm_dialog_if_path_exists(self): |
| 1367 | """The confirmation dialog is correct.""" |
| 1368 | self.patch(gui.os.path, 'exists', lambda path: True) |
| 1369 | + self.patch(gui.os.path, 'isdir', lambda path: True) |
| 1370 | + self.patch(gui, 'is_link', lambda path: False) |
| 1371 | |
| 1372 | # make sure the item is subscribed |
| 1373 | self.ui.is_processing = True |
| 1374 | @@ -607,6 +609,48 @@ |
| 1375 | self.assertEqual(FakedDialog.kwargs, {}) |
| 1376 | |
| 1377 | @defer.inlineCallbacks |
| 1378 | + def test_error_if_path_exists_as_file(self): |
| 1379 | + """The error dialog is correct.""" |
| 1380 | + self.patch(gui.os.path, 'exists', lambda path: True) |
| 1381 | + self.patch(gui.os.path, 'isdir', lambda path: False) |
| 1382 | + self.patch(gui, 'is_link', lambda path: False) |
| 1383 | + |
| 1384 | + # make sure the item is subscribed |
| 1385 | + self.ui.is_processing = True |
| 1386 | + self.set_item_checked() |
| 1387 | + self.ui.is_processing = False |
| 1388 | + |
| 1389 | + yield self.ui.on_folders_itemChanged(self.item) |
| 1390 | + |
| 1391 | + volume_path = self.item.volume_path |
| 1392 | + msg = gui.FOLDER_EXISTS_AS_FILE % {'folder_path': volume_path} |
| 1393 | + buttons = gui.CLOSE |
| 1394 | + self.assertEqual(FakedDialog.args, |
| 1395 | + (self.ui, '', msg, buttons)) |
| 1396 | + self.assertEqual(FakedDialog.kwargs, {}) |
| 1397 | + |
| 1398 | + @defer.inlineCallbacks |
| 1399 | + def test_error_if_path_exists_as_valid_link(self): |
| 1400 | + """The error dialog is correct.""" |
| 1401 | + self.patch(gui.os.path, 'exists', lambda path: True) |
| 1402 | + self.patch(gui.os.path, 'isdir', lambda path: True) |
| 1403 | + self.patch(gui, 'is_link', lambda path: True) |
| 1404 | + |
| 1405 | + # make sure the item is subscribed |
| 1406 | + self.ui.is_processing = True |
| 1407 | + self.set_item_checked() |
| 1408 | + self.ui.is_processing = False |
| 1409 | + |
| 1410 | + yield self.ui.on_folders_itemChanged(self.item) |
| 1411 | + |
| 1412 | + volume_path = self.item.volume_path |
| 1413 | + msg = gui.FOLDER_EXISTS_AS_FILE % {'folder_path': volume_path} |
| 1414 | + buttons = gui.CLOSE |
| 1415 | + self.assertEqual(FakedDialog.args, |
| 1416 | + (self.ui, '', msg, buttons)) |
| 1417 | + self.assertEqual(FakedDialog.kwargs, {}) |
| 1418 | + |
| 1419 | + @defer.inlineCallbacks |
| 1420 | def test_confirm_dialog_if_path_does_not_exist(self): |
| 1421 | """The confirmation dialog is not created if not needed.""" |
| 1422 | self.patch(gui.os.path, 'exists', lambda path: False) |
| 1423 | |
| 1424 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_gui.py' |
| 1425 | --- ubuntuone/controlpanel/gui/qt/tests/test_gui.py 2012-04-05 14:52:55 +0000 |
| 1426 | +++ ubuntuone/controlpanel/gui/qt/tests/test_gui.py 2012-08-22 21:22:18 +0000 |
| 1427 | @@ -81,6 +81,11 @@ |
| 1428 | self.ui.ui.control_panel.ui.tab_widget.currentIndex(), |
| 1429 | self.ui.ui.control_panel.ui.tab_widget.indexOf( |
| 1430 | self.ui.ui.control_panel.ui.folders_tab)) |
| 1431 | + self.ui.switch_to("share_links") |
| 1432 | + self.assertEqual( |
| 1433 | + self.ui.ui.control_panel.ui.tab_widget.currentIndex(), |
| 1434 | + self.ui.ui.control_panel.ui.tab_widget.indexOf( |
| 1435 | + self.ui.ui.control_panel.ui.share_links_tab)) |
| 1436 | self.ui.switch_to("devices") |
| 1437 | self.assertEqual( |
| 1438 | self.ui.ui.control_panel.ui.tab_widget.currentIndex(), |
| 1439 | |
| 1440 | === added file 'ubuntuone/controlpanel/gui/qt/tests/test_share_links.py' |
| 1441 | --- ubuntuone/controlpanel/gui/qt/tests/test_share_links.py 1970-01-01 00:00:00 +0000 |
| 1442 | +++ ubuntuone/controlpanel/gui/qt/tests/test_share_links.py 2012-08-22 21:22:18 +0000 |
| 1443 | @@ -0,0 +1,37 @@ |
| 1444 | +# -*- coding: utf-8 -*- |
| 1445 | + |
| 1446 | +# Copyright 2012 Canonical Ltd. |
| 1447 | +# |
| 1448 | +# This program is free software: you can redistribute it and/or modify it |
| 1449 | +# under the terms of the GNU General Public License version 3, as published |
| 1450 | +# by the Free Software Foundation. |
| 1451 | +# |
| 1452 | +# This program is distributed in the hope that it will be useful, but |
| 1453 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
| 1454 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
| 1455 | +# PURPOSE. See the GNU General Public License for more details. |
| 1456 | +# |
| 1457 | +# You should have received a copy of the GNU General Public License along |
| 1458 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
| 1459 | + |
| 1460 | +"""Tests for the account tab.""" |
| 1461 | + |
| 1462 | +from ubuntuone.controlpanel.gui import ( |
| 1463 | + SEARCH_FILES, |
| 1464 | + SHARED_FILES, |
| 1465 | +) |
| 1466 | +from ubuntuone.controlpanel.gui.qt import share_links as gui |
| 1467 | +from ubuntuone.controlpanel.gui.qt.tests import BaseTestCase |
| 1468 | + |
| 1469 | + |
| 1470 | +class ShareLinksTestCase(BaseTestCase): |
| 1471 | + """Test the qt control panel.""" |
| 1472 | + |
| 1473 | + innerclass_ui = gui.share_links_ui |
| 1474 | + innerclass_name = "Ui_Form" |
| 1475 | + class_ui = gui.ShareLinksPanel |
| 1476 | + |
| 1477 | + def test_setup(self): |
| 1478 | + """Check that the strings are properly setted.""" |
| 1479 | + self.assertEqual(self.ui.ui.search_files_lbl.text(), SEARCH_FILES) |
| 1480 | + self.assertEqual(self.ui.ui.shared_group.title(), SHARED_FILES) |
| 1481 | |
| 1482 | === modified file 'ubuntuone/controlpanel/gui/qt/tests/test_systray.py' |
| 1483 | --- ubuntuone/controlpanel/gui/qt/tests/test_systray.py 2012-06-22 14:42:29 +0000 |
| 1484 | +++ ubuntuone/controlpanel/gui/qt/tests/test_systray.py 2012-08-22 21:22:18 +0000 |
| 1485 | @@ -18,12 +18,25 @@ |
| 1486 | |
| 1487 | """Tests for the notification area icon.""" |
| 1488 | |
| 1489 | -from PyQt4 import QtGui |
| 1490 | +from PyQt4 import QtGui, QtCore |
| 1491 | from twisted.internet.defer import inlineCallbacks |
| 1492 | |
| 1493 | +import ubuntuone.controlpanel.gui.qt.gui |
| 1494 | +from ubuntuone.controlpanel.gui import ( |
| 1495 | + qt, |
| 1496 | + IN_PROGRESS, |
| 1497 | + IN_PROGRESS_FILE, |
| 1498 | + RECENT_TRANSFERS, |
| 1499 | + RECENTTRANSFERS, |
| 1500 | + UPLOADING, |
| 1501 | +) |
| 1502 | from ubuntuone.controlpanel.gui.qt import systray |
| 1503 | -from ubuntuone.controlpanel.tests import TestCase |
| 1504 | -import ubuntuone.controlpanel.gui.qt.gui |
| 1505 | +from ubuntuone.controlpanel.gui.qt.tests import BaseTestCase |
| 1506 | +from ubuntuone.controlpanel.tests import ROOT_PATH |
| 1507 | + |
| 1508 | + |
| 1509 | +# pylint: disable=C0103, W0212 |
| 1510 | +backend = systray.backend |
| 1511 | |
| 1512 | |
| 1513 | class FakeSDTool(object): |
| 1514 | @@ -46,14 +59,153 @@ |
| 1515 | super(FakeMainWindow, self).__init__() |
| 1516 | |
| 1517 | |
| 1518 | -class SystrayTestCase(TestCase): |
| 1519 | +class FakeDesktopService(object): |
| 1520 | + |
| 1521 | + """Fake QDesktopService.""" |
| 1522 | + |
| 1523 | + data = {} |
| 1524 | + |
| 1525 | + @classmethod |
| 1526 | + def openUrl(cls, url): |
| 1527 | + """Fake openUrl.""" |
| 1528 | + FakeDesktopService.data['cls'] = cls |
| 1529 | + FakeDesktopService.data['url'] = url |
| 1530 | + |
| 1531 | + |
| 1532 | +class SystrayTestCase(BaseTestCase): |
| 1533 | |
| 1534 | """Test the notification area icon.""" |
| 1535 | |
| 1536 | + class_ui = systray.TrayIcon |
| 1537 | + |
| 1538 | + @inlineCallbacks |
| 1539 | + def setUp(self): |
| 1540 | + # We need to patch the startTimer first, to avoid the timer |
| 1541 | + # to get started on initialization. |
| 1542 | + self.patch(systray.TrayIcon, "startTimer", lambda s, x: None) |
| 1543 | + self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
| 1544 | + yield super(SystrayTestCase, self).setUp() |
| 1545 | + self.patch(QtGui, "QDesktopServices", FakeDesktopService) |
| 1546 | + |
| 1547 | + def assert_status_correct(self, status_bd, status_ui, action, |
| 1548 | + callback=None): |
| 1549 | + """The shown status is correct.""" |
| 1550 | + expected_text = status_ui |
| 1551 | + |
| 1552 | + status = {backend.STATUS_KEY: status_bd} |
| 1553 | + self.ui._process_status(status) |
| 1554 | + |
| 1555 | + actual_text = unicode(self.ui.status.text()) |
| 1556 | + self.assertEqual(expected_text, actual_text) |
| 1557 | + |
| 1558 | + self.assertFalse(self.ui.status.isEnabled()) |
| 1559 | + self.assertEqual(unicode(self.ui.status_action.text()), action) |
| 1560 | + |
| 1561 | + self.ui.status_action.trigger() |
| 1562 | + self.assertFalse(self.ui.status_action.isEnabled()) |
| 1563 | + self.assert_backend_called(callback) |
| 1564 | + |
| 1565 | + def test_process_info_invalid_status(self): |
| 1566 | + """File sync status invalid, ignore event.""" |
| 1567 | + status = {backend.STATUS_KEY: backend.FILE_SYNC_STARTING, |
| 1568 | + backend.MSG_KEY: qt.FILE_SYNC_STARTING, |
| 1569 | + 'icon': backend.FILE_SYNC_STARTING} |
| 1570 | + self.ui._process_status(status) |
| 1571 | + self.ui._process_status(3) |
| 1572 | + |
| 1573 | + actual_text = unicode(self.ui.status.text()) |
| 1574 | + self.assertEqual(qt.FILE_SYNC_STARTING, actual_text) |
| 1575 | + |
| 1576 | + def test_process_info_changed(self): |
| 1577 | + """Backend's file sync status changed callback is connected.""" |
| 1578 | + self.ui.refresh_status() |
| 1579 | + self.assertEqual(self.ui.backend.status_changed_handler, |
| 1580 | + self.ui._process_status) |
| 1581 | + |
| 1582 | + def test_process_info_disabled(self): |
| 1583 | + """File sync status disabled update the label.""" |
| 1584 | + self.ui.refresh_status() |
| 1585 | + self.assert_status_correct(status_bd=backend.FILE_SYNC_DISABLED, |
| 1586 | + status_ui=qt.FILE_SYNC_DISABLED, |
| 1587 | + action=qt.FILE_SYNC_ENABLE, |
| 1588 | + callback='enable_files') |
| 1589 | + |
| 1590 | + def test_process_info_disconnected(self): |
| 1591 | + """File sync status disconnected update the label.""" |
| 1592 | + self.assert_status_correct(status_bd=backend.FILE_SYNC_DISCONNECTED, |
| 1593 | + status_ui=qt.FILE_SYNC_DISCONNECTED, |
| 1594 | + action=qt.FILE_SYNC_CONNECT, |
| 1595 | + callback='connect_files') |
| 1596 | + |
| 1597 | + def test_process_info_error(self): |
| 1598 | + """File sync status error update the label.""" |
| 1599 | + msg = qt.WARNING_MARKUP % qt.FILE_SYNC_ERROR |
| 1600 | + self.assert_status_correct(status_bd=backend.FILE_SYNC_ERROR, |
| 1601 | + status_ui=msg, |
| 1602 | + action=qt.FILE_SYNC_RESTART, |
| 1603 | + callback='restart_files') |
| 1604 | + |
| 1605 | + def test_process_info_idle(self): |
| 1606 | + """File sync status idle update the label.""" |
| 1607 | + self.assert_status_correct(status_bd=backend.FILE_SYNC_IDLE, |
| 1608 | + status_ui=qt.FILE_SYNC_IDLE, |
| 1609 | + action=qt.FILE_SYNC_DISCONNECT, |
| 1610 | + callback='disconnect_files') |
| 1611 | + |
| 1612 | + def test_process_info_starting(self): |
| 1613 | + """File sync status starting update the label.""" |
| 1614 | + self.assert_status_correct(status_bd=backend.FILE_SYNC_STARTING, |
| 1615 | + status_ui=qt.FILE_SYNC_STARTING, |
| 1616 | + action=qt.FILE_SYNC_STOP, |
| 1617 | + callback='stop_files') |
| 1618 | + |
| 1619 | + def test_process_info_stopped(self): |
| 1620 | + """File sync status stopped update the label.""" |
| 1621 | + self.assert_status_correct(status_bd=backend.FILE_SYNC_STOPPED, |
| 1622 | + status_ui=qt.FILE_SYNC_STOPPED, |
| 1623 | + action=qt.FILE_SYNC_START, |
| 1624 | + callback='start_files') |
| 1625 | + |
| 1626 | + def test_process_info_syncing(self): |
| 1627 | + """File sync status syncing update the label.""" |
| 1628 | + self.assert_status_correct(status_bd=backend.FILE_SYNC_SYNCING, |
| 1629 | + status_ui=qt.FILE_SYNC_SYNCING, |
| 1630 | + action=qt.FILE_SYNC_DISCONNECT, |
| 1631 | + callback='disconnect_files') |
| 1632 | + |
| 1633 | + def test_process_info_unknown(self): |
| 1634 | + """File sync status unknown update the label.""" |
| 1635 | + msg = qt.WARNING_MARKUP % qt.FILE_SYNC_ERROR |
| 1636 | + self.assert_status_correct(status_bd=backend.FILE_SYNC_UNKNOWN, |
| 1637 | + status_ui=msg, |
| 1638 | + action=qt.FILE_SYNC_RESTART, |
| 1639 | + callback='restart_files') |
| 1640 | + |
| 1641 | + def test_backend(self): |
| 1642 | + """Backend is created.""" |
| 1643 | + self.assertIsInstance(self.ui.backend, |
| 1644 | + backend.ControlBackend) |
| 1645 | + |
| 1646 | + def test_refresh_status_requested(self): |
| 1647 | + """test refresh_status was requested on initialized.""" |
| 1648 | + data = {} |
| 1649 | + |
| 1650 | + def callback(status): |
| 1651 | + """Fake _process_status callback.""" |
| 1652 | + data['called'] = True |
| 1653 | + data['status'] = status |
| 1654 | + |
| 1655 | + self.patch(self.ui, "_process_status", callback) |
| 1656 | + self.ui.refresh_status() |
| 1657 | + self.assert_backend_called('file_sync_status') |
| 1658 | + self.assertTrue(data['called']) |
| 1659 | + self.assertEqual(data['status'], []) |
| 1660 | + |
| 1661 | def test_quit(self): |
| 1662 | """Test the quit option in the menu.""" |
| 1663 | # Not done on setup, because if I patch stop |
| 1664 | # after instantiation, it doesn't get called. |
| 1665 | + self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
| 1666 | self.patch(systray.TrayIcon, "stop", self._set_called) |
| 1667 | tray = systray.TrayIcon() |
| 1668 | tray.quit.trigger() |
| 1669 | @@ -63,6 +215,7 @@ |
| 1670 | def test_stop_sd(self): |
| 1671 | """Quit should call SyncDaemonTool.quit().""" |
| 1672 | st = FakeSDTool() |
| 1673 | + self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
| 1674 | self.patch(systray, "SyncDaemonTool", lambda: st) |
| 1675 | tray = systray.TrayIcon() |
| 1676 | yield tray.stop() |
| 1677 | @@ -70,38 +223,31 @@ |
| 1678 | |
| 1679 | def test_restore_no_window(self): |
| 1680 | """Test the restore window option in the menu, with no window.""" |
| 1681 | + self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
| 1682 | self.patch(ubuntuone.controlpanel.gui.qt.gui, |
| 1683 | "MainWindow", FakeMainWindow) |
| 1684 | tray = systray.TrayIcon() |
| 1685 | self.assertEqual(tray.window, None) |
| 1686 | - tray.restore.trigger() |
| 1687 | + tray.open_u1.trigger() |
| 1688 | self.assertIsInstance(tray.window, FakeMainWindow) |
| 1689 | self.assertTrue(tray.window.isVisible()) |
| 1690 | self.assertEqual(tray.window.args, ((), |
| 1691 | {'close_callback': tray.delete_window})) |
| 1692 | |
| 1693 | - def test_activate(self): |
| 1694 | - """Test the icon activation.""" |
| 1695 | - tray = systray.TrayIcon() |
| 1696 | - window = FakeMainWindow() |
| 1697 | - tray.window = window |
| 1698 | - self.assertFalse(tray.window.isVisible()) |
| 1699 | - tray.activated.emit(tray.Trigger) |
| 1700 | - self.assertEqual(tray.window, window) |
| 1701 | - self.assertTrue(tray.window.isVisible()) |
| 1702 | - |
| 1703 | def test_restore_window(self): |
| 1704 | """Test the restore window option in the menu, with a window.""" |
| 1705 | + self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
| 1706 | tray = systray.TrayIcon() |
| 1707 | window = FakeMainWindow() |
| 1708 | tray.window = window |
| 1709 | self.assertFalse(tray.window.isVisible()) |
| 1710 | - tray.restore.trigger() |
| 1711 | + tray.open_u1.trigger() |
| 1712 | self.assertEqual(tray.window, window) |
| 1713 | self.assertTrue(tray.window.isVisible()) |
| 1714 | |
| 1715 | def test_restore_window_minimized(self): |
| 1716 | """Test the restore window option in the menu, with a min. window.""" |
| 1717 | + self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
| 1718 | tray = systray.TrayIcon() |
| 1719 | window = FakeMainWindow() |
| 1720 | # This cannot be tested with the real activateWindow |
| 1721 | @@ -109,11 +255,12 @@ |
| 1722 | # it has a small delay, and it fails. |
| 1723 | self.patch(window, "activateWindow", self._set_called) |
| 1724 | tray.window = window |
| 1725 | - tray.restore.trigger() |
| 1726 | + tray.open_u1.trigger() |
| 1727 | self.assertEqual(self._called, ((), {})) |
| 1728 | |
| 1729 | def test_delete_window(self): |
| 1730 | """Test deleting an existing window.""" |
| 1731 | + self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
| 1732 | tray = systray.TrayIcon() |
| 1733 | window = FakeMainWindow() |
| 1734 | tray.window = window |
| 1735 | @@ -123,14 +270,330 @@ |
| 1736 | |
| 1737 | def test_delete_no_window(self): |
| 1738 | """Test deleting without an existing window.""" |
| 1739 | + self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
| 1740 | tray = systray.TrayIcon() |
| 1741 | tray.delete_window() |
| 1742 | self.assertEqual(tray.window, None) |
| 1743 | |
| 1744 | + def test_open_u1_folder_action(self): |
| 1745 | + """Test open_u1_folder_action.""" |
| 1746 | + self.ui.open_u1_folder.trigger() |
| 1747 | + self.assertEqual(FakeDesktopService.data['cls'], FakeDesktopService) |
| 1748 | + expected_url = QtCore.QUrl(u'file://%s' % ROOT_PATH) |
| 1749 | + self.assertEqual(FakeDesktopService.data['url'], expected_url) |
| 1750 | + |
| 1751 | + def test_get_more_storage_action(self): |
| 1752 | + """Test get_more_storage.""" |
| 1753 | + self.ui.get_more_storage.trigger() |
| 1754 | + self.assertEqual(FakeDesktopService.data['cls'], FakeDesktopService) |
| 1755 | + expected_url = QtCore.QUrl(systray.GET_STORAGE_LINK) |
| 1756 | + self.assertEqual(FakeDesktopService.data['url'], expected_url) |
| 1757 | + |
| 1758 | + def test_go_to_web_action(self): |
| 1759 | + """Test go_to_web.""" |
| 1760 | + self.ui.go_to_web.trigger() |
| 1761 | + self.assertEqual(FakeDesktopService.data['cls'], FakeDesktopService) |
| 1762 | + expected_url = QtCore.QUrl(systray.DASHBOARD) |
| 1763 | + self.assertEqual(FakeDesktopService.data['url'], expected_url) |
| 1764 | + |
| 1765 | + def test_get_help_action(self): |
| 1766 | + """Test get_help_online.""" |
| 1767 | + self.ui.get_help_online.trigger() |
| 1768 | + self.assertEqual(FakeDesktopService.data['cls'], FakeDesktopService) |
| 1769 | + expected_url = QtCore.QUrl(systray.GET_SUPPORT_LINK) |
| 1770 | + self.assertEqual(FakeDesktopService.data['url'], expected_url) |
| 1771 | + |
| 1772 | def test_initialization(self): |
| 1773 | """Test that everything initializes correctly.""" |
| 1774 | + self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
| 1775 | tray = systray.TrayIcon() |
| 1776 | self.assertTrue(tray.isVisible()) |
| 1777 | self.assertEqual(tray.window, None) |
| 1778 | + self.assertNotEqual(tray.icon(), None) |
| 1779 | + self.assertEqual(tray.uploading, {}) |
| 1780 | + self.assertEqual(tray.recent_transfers, {}) |
| 1781 | self.assertIsInstance(tray.context_menu, QtGui.QMenu) |
| 1782 | - self.assertNotEqual(tray.icon(), None) |
| 1783 | + self.assertIsInstance(tray.status, QtGui.QAction) |
| 1784 | + self.assertIsInstance(tray.status_action, QtGui.QAction) |
| 1785 | + self.assertIsInstance(tray.open_u1, QtGui.QAction) |
| 1786 | + self.assertIsInstance(tray.open_u1_folder, QtGui.QAction) |
| 1787 | + self.assertIsInstance(tray.go_to_web, QtGui.QAction) |
| 1788 | + self.assertIsInstance(tray.get_more_storage, QtGui.QAction) |
| 1789 | + self.assertIsInstance(tray.get_help_online, QtGui.QAction) |
| 1790 | + self.assertIsInstance(tray.transfers, QtGui.QMenu) |
| 1791 | + # This checks that _get_volumes_info and _process_volumes_info |
| 1792 | + # is being called correctly on initialization. |
| 1793 | + expected_home = u'file://%s' % ROOT_PATH |
| 1794 | + self.assertEqual(tray.root_path, expected_home) |
| 1795 | + |
| 1796 | + def test_get_transfers_data(self): |
| 1797 | + """Check that _get_transfers_data return the proper data.""" |
| 1798 | + |
| 1799 | + result = [] |
| 1800 | + |
| 1801 | + def fake_callback(data): |
| 1802 | + """Fake callback to process data.""" |
| 1803 | + result.append(data) |
| 1804 | + |
| 1805 | + self.patch(self.ui.transfers, "_update_transfers", fake_callback) |
| 1806 | + self.ui.transfers.get_transfers_data() |
| 1807 | + self.assertEqual(result, [{RECENTTRANSFERS: [], UPLOADING: []}]) |
| 1808 | + |
| 1809 | + |
| 1810 | +class TransfersMenuTestCase(BaseTestCase): |
| 1811 | + |
| 1812 | + """Test the info to be display in the transfers menu.""" |
| 1813 | + |
| 1814 | + class_ui = systray.TrayIcon |
| 1815 | + |
| 1816 | + @inlineCallbacks |
| 1817 | + def setUp(self): |
| 1818 | + # We need to patch the startTimer first, to avoid the timer |
| 1819 | + # to get started on initialization. |
| 1820 | + self.patch(systray.TrayIcon, "startTimer", lambda s, x: None) |
| 1821 | + self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
| 1822 | + yield super(TransfersMenuTestCase, self).setUp() |
| 1823 | + self.patch(QtGui, "QDesktopServices", FakeDesktopService) |
| 1824 | + self.patch(self.ui.transfers._parent.backend, "sync_menu", |
| 1825 | + self.fake_sync_menu) |
| 1826 | + self.recent_transfers = [] |
| 1827 | + self.uploading = [] |
| 1828 | + |
| 1829 | + def fake_sync_menu(self): |
| 1830 | + """Fake backend sync_menu.""" |
| 1831 | + return {RECENTTRANSFERS: self.recent_transfers, |
| 1832 | + UPLOADING: self.uploading} |
| 1833 | + |
| 1834 | + def test_load_menu(self): |
| 1835 | + """Show the menu with just the labels.""" |
| 1836 | + self.ui.transfers.get_transfers_data() |
| 1837 | + actions = self.ui.transfers.actions() |
| 1838 | + self.assertEqual(actions[0].text(), RECENT_TRANSFERS) |
| 1839 | + self.assertTrue(actions[1].isSeparator()) |
| 1840 | + self.assertEqual(actions[2].text(), IN_PROGRESS) |
| 1841 | + |
| 1842 | + def test_load_recent_transfers(self): |
| 1843 | + """Show the menu with the recent transfers.""" |
| 1844 | + self.recent_transfers = ['file1', 'file2'] |
| 1845 | + self.ui.transfers.get_transfers_data() |
| 1846 | + actions = self.ui.transfers.actions() |
| 1847 | + self.assertEqual(actions[0].text(), RECENT_TRANSFERS) |
| 1848 | + self.assertEqual(actions[1].text(), 'file1') |
| 1849 | + self.assertEqual(actions[2].text(), 'file2') |
| 1850 | + self.assertTrue(actions[3].isSeparator()) |
| 1851 | + self.assertEqual(actions[4].text(), IN_PROGRESS) |
| 1852 | + |
| 1853 | + def test_load_in_progress(self): |
| 1854 | + """Show the menu with the current progress.""" |
| 1855 | + in_progress = [ |
| 1856 | + ('file1', 200, 150), |
| 1857 | + ('file2', 400, 100), |
| 1858 | + ('file3', 300, 200), |
| 1859 | + ] |
| 1860 | + |
| 1861 | + # Return copy of in_progress |
| 1862 | + self.uploading = in_progress[:] |
| 1863 | + self.ui.transfers.get_transfers_data() |
| 1864 | + actions = self.ui.transfers.actions() |
| 1865 | + self.assertEqual(actions[0].text(), RECENT_TRANSFERS) |
| 1866 | + self.assertTrue(actions[1].isSeparator()) |
| 1867 | + self.assertEqual(actions[2].text(), IN_PROGRESS) |
| 1868 | + |
| 1869 | + # This also check that the files are ordered based on written |
| 1870 | + percentage = in_progress[2][2] * 100 / in_progress[2][1] |
| 1871 | + text = IN_PROGRESS_FILE % (in_progress[2][0], percentage) |
| 1872 | + self.assertEqual(actions[3].label.text(), text) |
| 1873 | + self.assertEqual(actions[3].progress.value(), percentage) |
| 1874 | + |
| 1875 | + percentage = in_progress[0][2] * 100 / in_progress[0][1] |
| 1876 | + text = IN_PROGRESS_FILE % (in_progress[0][0], percentage) |
| 1877 | + self.assertEqual(actions[4].label.text(), text) |
| 1878 | + self.assertEqual(actions[4].progress.value(), percentage) |
| 1879 | + |
| 1880 | + percentage = in_progress[1][2] * 100 / in_progress[1][1] |
| 1881 | + text = IN_PROGRESS_FILE % (in_progress[1][0], percentage) |
| 1882 | + self.assertEqual(actions[5].label.text(), text) |
| 1883 | + self.assertEqual(actions[5].progress.value(), percentage) |
| 1884 | + |
| 1885 | + def test_load_in_progress_refresh(self): |
| 1886 | + """Show the menu with the current progress and refresh it.""" |
| 1887 | + in_progress = [ |
| 1888 | + ('file1', 200, 150), |
| 1889 | + ('file2', 400, 100), |
| 1890 | + ('file3', 300, 200), |
| 1891 | + ] |
| 1892 | + # Return copy of in_progress |
| 1893 | + self.uploading = in_progress[:] |
| 1894 | + self.ui.transfers.get_transfers_data() |
| 1895 | + actions = self.ui.transfers.actions() |
| 1896 | + self.assertEqual(actions[0].text(), RECENT_TRANSFERS) |
| 1897 | + self.assertTrue(actions[1].isSeparator()) |
| 1898 | + self.assertEqual(actions[2].text(), IN_PROGRESS) |
| 1899 | + |
| 1900 | + # This also check that the files are ordered based on written |
| 1901 | + previous_actions = [] |
| 1902 | + percentage = in_progress[2][2] * 100 / in_progress[2][1] |
| 1903 | + text = IN_PROGRESS_FILE % (in_progress[2][0], percentage) |
| 1904 | + self.assertEqual(actions[3].text(), text) |
| 1905 | + previous_actions.append(actions[3]) |
| 1906 | + |
| 1907 | + percentage = in_progress[0][2] * 100 / in_progress[0][1] |
| 1908 | + text = IN_PROGRESS_FILE % (in_progress[0][0], percentage) |
| 1909 | + self.assertEqual(actions[4].text(), text) |
| 1910 | + previous_actions.append(actions[4]) |
| 1911 | + |
| 1912 | + percentage = in_progress[1][2] * 100 / in_progress[1][1] |
| 1913 | + text = IN_PROGRESS_FILE % (in_progress[1][0], percentage) |
| 1914 | + self.assertEqual(actions[5].text(), text) |
| 1915 | + previous_actions.append(actions[5]) |
| 1916 | + |
| 1917 | + in_progress = [ |
| 1918 | + ('file1', 200, 170), |
| 1919 | + ('file2', 400, 300), |
| 1920 | + ('file3', 300, 210), |
| 1921 | + ] |
| 1922 | + self.uploading = in_progress[:] |
| 1923 | + |
| 1924 | + self.ui.transfers.get_transfers_data() |
| 1925 | + actions = self.ui.transfers.actions() |
| 1926 | + current_actions = [] |
| 1927 | + self.assertEqual(actions[0].text(), RECENT_TRANSFERS) |
| 1928 | + self.assertTrue(actions[1].isSeparator()) |
| 1929 | + self.assertEqual(actions[2].text(), IN_PROGRESS) |
| 1930 | + |
| 1931 | + # This also check that the files are ordered based on written |
| 1932 | + percentage = in_progress[2][2] * 100 / in_progress[2][1] |
| 1933 | + text = IN_PROGRESS_FILE % (in_progress[2][0], percentage) |
| 1934 | + self.assertEqual(actions[3].text(), text) |
| 1935 | + current_actions.append(actions[3]) |
| 1936 | + |
| 1937 | + percentage = in_progress[0][2] * 100 / in_progress[0][1] |
| 1938 | + text = IN_PROGRESS_FILE % (in_progress[0][0], percentage) |
| 1939 | + self.assertEqual(actions[4].text(), text) |
| 1940 | + current_actions.append(actions[4]) |
| 1941 | + |
| 1942 | + percentage = in_progress[1][2] * 100 / in_progress[1][1] |
| 1943 | + text = IN_PROGRESS_FILE % (in_progress[1][0], percentage) |
| 1944 | + self.assertEqual(actions[5].text(), text) |
| 1945 | + current_actions.append(actions[5]) |
| 1946 | + |
| 1947 | + self.assertEqual(previous_actions, current_actions) |
| 1948 | + |
| 1949 | + def test_menu_not_reload(self): |
| 1950 | + """Show the menu and test that is not reload it on refresh.""" |
| 1951 | + self.recent_transfers = ['file1', 'file2'] |
| 1952 | + self.ui.transfers.get_transfers_data() |
| 1953 | + previous_actions = self.ui.transfers.actions() |
| 1954 | + self.ui.transfers.get_transfers_data() |
| 1955 | + current_actions = self.ui.transfers.actions() |
| 1956 | + self.assertEqual(previous_actions, current_actions) |
| 1957 | + |
| 1958 | + def test_menu_reload(self): |
| 1959 | + """Show the menu and test that is reload it on refresh.""" |
| 1960 | + self.recent_transfers = ['file1', 'file2'] |
| 1961 | + self.ui.transfers.get_transfers_data() |
| 1962 | + previous_actions = self.ui.transfers.actions() |
| 1963 | + self.recent_transfers = ['file1', 'file2', 'file3'] |
| 1964 | + |
| 1965 | + self.ui.transfers.get_transfers_data() |
| 1966 | + current_actions = self.ui.transfers.actions() |
| 1967 | + self.assertNotEqual(previous_actions, current_actions) |
| 1968 | + |
| 1969 | + def test_progress_not_reload(self): |
| 1970 | + """Show the menu and test that is not reload it on refresh.""" |
| 1971 | + in_progress = [ |
| 1972 | + ('file1', 200, 150), |
| 1973 | + ('file2', 400, 100), |
| 1974 | + ('file3', 300, 200), |
| 1975 | + ] |
| 1976 | + # Return copy of in_progress |
| 1977 | + self.uploading = in_progress[:] |
| 1978 | + self.ui.transfers.get_transfers_data() |
| 1979 | + previous_actions = self.ui.transfers.actions() |
| 1980 | + in_progress = [ |
| 1981 | + ('file1', 200, 170), |
| 1982 | + ('file2', 400, 300), |
| 1983 | + ('file3', 300, 210), |
| 1984 | + ] |
| 1985 | + self.uploading = in_progress[:] |
| 1986 | + self.ui.transfers.get_transfers_data() |
| 1987 | + current_actions = self.ui.transfers.actions() |
| 1988 | + |
| 1989 | + self.assertEqual(previous_actions, current_actions) |
| 1990 | + |
| 1991 | + def test_progress_reload(self): |
| 1992 | + """Show the menu and test that is reload it on refresh.""" |
| 1993 | + in_progress = [ |
| 1994 | + ('file1', 200, 150), |
| 1995 | + ('file2', 400, 100), |
| 1996 | + ('file3', 300, 200), |
| 1997 | + ] |
| 1998 | + # Return copy of in_progress |
| 1999 | + self.uploading = in_progress[:] |
| 2000 | + self.ui.transfers.get_transfers_data() |
| 2001 | + previous_actions = self.ui.transfers.actions() |
| 2002 | + in_progress = [ |
| 2003 | + ('file1', 200, 170), |
| 2004 | + ('file2', 400, 300), |
| 2005 | + ('file3', 300, 210), |
| 2006 | + ('file4', 1000, 410), |
| 2007 | + ] |
| 2008 | + self.uploading = in_progress[:] |
| 2009 | + self.ui.transfers.get_transfers_data() |
| 2010 | + current_actions = self.ui.transfers.actions() |
| 2011 | + |
| 2012 | + self.assertNotEqual(previous_actions, current_actions) |
| 2013 | + |
| 2014 | + |
| 2015 | +class SharesTestCase(BaseTestCase): |
| 2016 | + |
| 2017 | + """Test the info to be displayed in the shares section.""" |
| 2018 | + |
| 2019 | + class_ui = systray.TrayIcon |
| 2020 | + |
| 2021 | + @inlineCallbacks |
| 2022 | + def setUp(self): |
| 2023 | + # We need to patch the startTimer first, to avoid the timer |
| 2024 | + # to get started on initialization. |
| 2025 | + self.patch(systray.TrayIcon, "startTimer", lambda s, x: None) |
| 2026 | + self.patch(systray.TransfersMenu, "startTimer", lambda s, x: None) |
| 2027 | + yield super(SharesTestCase, self).setUp() |
| 2028 | + self.patch(QtGui, "QDesktopServices", FakeDesktopService) |
| 2029 | + self.patch(self.ui.backend, "get_shares", self.fake_get_backend) |
| 2030 | + self._shares_info = [] |
| 2031 | + |
| 2032 | + def fake_get_backend(self): |
| 2033 | + """Fake get_backend.""" |
| 2034 | + return self._shares_info |
| 2035 | + |
| 2036 | + def test_shares_refresh_not_reload(self): |
| 2037 | + """Check that we get the new info of shares but not reload the menu.""" |
| 2038 | + actions = self.ui.context_menu.actions() |
| 2039 | + self.ui.load_menu() |
| 2040 | + post_actions = self.ui.context_menu.actions() |
| 2041 | + self.assertEqual(actions, post_actions) |
| 2042 | + |
| 2043 | + def test_shares_refresh_reload(self): |
| 2044 | + """Check that we get the new info of shares but not reload the menu.""" |
| 2045 | + actions = self.ui.context_menu.actions() |
| 2046 | + self._shares_info = [ |
| 2047 | + {'accepted': False, 'other_visible_name': 'name', |
| 2048 | + 'volume_id': 'asd123-asd123-asd123-asd123'} |
| 2049 | + ] |
| 2050 | + self.ui.load_menu() |
| 2051 | + post_actions = self.ui.context_menu.actions() |
| 2052 | + self.assertNotEqual(actions, post_actions) |
| 2053 | + |
| 2054 | + def test_share_triggered(self): |
| 2055 | + """Check that we get the new info of shares but not reload the menu.""" |
| 2056 | + volume_id = 'asd123-asd123-asd123-asd123' |
| 2057 | + self._shares_info = [ |
| 2058 | + {'accepted': False, 'other_visible_name': 'name', |
| 2059 | + 'volume_id': volume_id} |
| 2060 | + ] |
| 2061 | + self.ui.load_menu() |
| 2062 | + actions = self.ui.context_menu.actions() |
| 2063 | + share_action = actions[7] |
| 2064 | + share_action.trigger() |
| 2065 | + expected = QtCore.QUrl(systray.ACCEPT_SHARES % volume_id) |
| 2066 | + self.assertEqual(FakeDesktopService.data['url'], expected) |
| 2067 | |
| 2068 | === modified file 'ubuntuone/controlpanel/sd_client/__init__.py' |
| 2069 | --- ubuntuone/controlpanel/sd_client/__init__.py 2012-03-29 21:37:22 +0000 |
| 2070 | +++ ubuntuone/controlpanel/sd_client/__init__.py 2012-08-22 21:22:18 +0000 |
| 2071 | @@ -205,3 +205,7 @@ |
| 2072 | def refresh_volumes(self): |
| 2073 | """Refresh the volumes information from syncdaemon.""" |
| 2074 | return self.proxy.refresh_volumes() |
| 2075 | + |
| 2076 | + def sync_menu(self): |
| 2077 | + """Get the sync menu data from syncdaemon.""" |
| 2078 | + return self.proxy.sync_menu() |
| 2079 | |
| 2080 | === modified file 'ubuntuone/controlpanel/tests/test_backend.py' |
| 2081 | --- ubuntuone/controlpanel/tests/test_backend.py 2012-05-11 13:30:34 +0000 |
| 2082 | +++ ubuntuone/controlpanel/tests/test_backend.py 2012-08-22 21:22:18 +0000 |
| 2083 | @@ -167,6 +167,7 @@ |
| 2084 | self.shares = [] |
| 2085 | self.folders = [] |
| 2086 | self.volumes_refreshed = False |
| 2087 | + self.menu_data = {'recent-transfers': (), 'uploading': ()} |
| 2088 | |
| 2089 | def get_throttling_limits(self): |
| 2090 | """Return the sample speed limits.""" |
| 2091 | @@ -326,6 +327,10 @@ |
| 2092 | self.volumes_refreshed = True |
| 2093 | return defer.succeed(None) |
| 2094 | |
| 2095 | + def sync_menu(self): |
| 2096 | + """Return the sync menu data.""" |
| 2097 | + return self.menu_data |
| 2098 | + |
| 2099 | |
| 2100 | class MockReplicationClient(CallRecorder): |
| 2101 | """A mock replication_client module.""" |
| 2102 | @@ -1697,3 +1702,10 @@ |
| 2103 | |
| 2104 | result = yield self.be.file_sync_settings_info() |
| 2105 | self.assertEqual(self.default_settings, result) |
| 2106 | + |
| 2107 | + @inlineCallbacks |
| 2108 | + def test_sync_menu(self): |
| 2109 | + """Check that we get the right data to create the menu.""" |
| 2110 | + result = yield self.be.sync_menu() |
| 2111 | + expected = {'recent-transfers': (), 'uploading': ()} |
| 2112 | + self.assertEqual(result, expected) |
| 2113 | |
| 2114 | === modified file 'ubuntuone/controlpanel/utils/__init__.py' |
| 2115 | --- ubuntuone/controlpanel/utils/__init__.py 2012-04-05 14:52:55 +0000 |
| 2116 | +++ ubuntuone/controlpanel/utils/__init__.py 2012-08-22 21:22:18 +0000 |
| 2117 | @@ -40,13 +40,23 @@ |
| 2118 | add_to_autostart = windows.add_to_autostart |
| 2119 | are_updates_present = windows.are_updates_present |
| 2120 | default_folders = windows.default_folders |
| 2121 | + install_config_and_daemons = no_op |
| 2122 | perform_update = windows.perform_update |
| 2123 | uninstall_application = windows.uninstall_application |
| 2124 | +elif sys.platform == 'darwin': |
| 2125 | + from ubuntuone.controlpanel.utils import darwin |
| 2126 | + add_to_autostart = no_op |
| 2127 | + are_updates_present = no_op |
| 2128 | + default_folders = darwin.default_folders |
| 2129 | + install_config_and_daemons = darwin.install_config_and_daemons |
| 2130 | + perform_update = no_op |
| 2131 | + uninstall_application = no_op |
| 2132 | else: |
| 2133 | from ubuntuone.controlpanel.utils import linux |
| 2134 | add_to_autostart = no_op |
| 2135 | are_updates_present = no_op |
| 2136 | default_folders = linux.default_folders |
| 2137 | + install_config_and_daemons = no_op |
| 2138 | perform_update = no_op |
| 2139 | uninstall_application = no_op |
| 2140 | |
| 2141 | |
| 2142 | === added file 'ubuntuone/controlpanel/utils/darwin.py' |
| 2143 | --- ubuntuone/controlpanel/utils/darwin.py 1970-01-01 00:00:00 +0000 |
| 2144 | +++ ubuntuone/controlpanel/utils/darwin.py 2012-08-22 21:22:18 +0000 |
| 2145 | @@ -0,0 +1,91 @@ |
| 2146 | +# -*- coding: utf-8 -*- |
| 2147 | +# |
| 2148 | +# Copyright 2012 Canonical Ltd. |
| 2149 | +# |
| 2150 | +# This program is free software: you can redistribute it and/or modify it |
| 2151 | +# under the terms of the GNU General Public License version 3, as published |
| 2152 | +# by the Free Software Foundation. |
| 2153 | +# |
| 2154 | +# This program is distributed in the hope that it will be useful, but |
| 2155 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
| 2156 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
| 2157 | +# PURPOSE. See the GNU General Public License for more details. |
| 2158 | +# |
| 2159 | +# You should have received a copy of the GNU General Public License along |
| 2160 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
| 2161 | + |
| 2162 | +"""Miscelaneous functions and constants for darwin.""" |
| 2163 | + |
| 2164 | +import os |
| 2165 | +import shutil |
| 2166 | +import sys |
| 2167 | + |
| 2168 | +from twisted.internet import defer |
| 2169 | + |
| 2170 | +from dirspec.basedir import save_config_path |
| 2171 | +from ubuntuone.controlpanel.logger import setup_logging |
| 2172 | + |
| 2173 | +logger = setup_logging('utils.darwin') |
| 2174 | +AUTOUPDATE_BIN_NAME = 'autoupdate-darwin' |
| 2175 | +UNINSTALL_BIN_NAME = 'uninstall-darwin' |
| 2176 | + |
| 2177 | + |
| 2178 | +def add_to_autostart(): |
| 2179 | + """Add syncdaemon to the session's autostart.""" |
| 2180 | + # TODO |
| 2181 | + |
| 2182 | + |
| 2183 | +@defer.inlineCallbacks |
| 2184 | +def are_updates_present(): |
| 2185 | + """Return if there are updates for Ubuntu One.""" |
| 2186 | + result = False |
| 2187 | + # TODO |
| 2188 | + defer.returnValue(result) |
| 2189 | + |
| 2190 | + |
| 2191 | +def default_folders(user_home=None): |
| 2192 | + """Return a list of the folders to add by default.""" |
| 2193 | + folders = [] |
| 2194 | + # TODO |
| 2195 | + return folders |
| 2196 | + |
| 2197 | + |
| 2198 | +def install_config_and_daemons(): |
| 2199 | + """Install required data files and fsevents daemon. |
| 2200 | + |
| 2201 | + This function is a replacement for an installer. As such it is |
| 2202 | + required on first-run, but it's also called every time we start |
| 2203 | + up, in case anything has moved or been deleted. |
| 2204 | + """ |
| 2205 | + |
| 2206 | + # Do nothing if we are running from source: |
| 2207 | + if getattr(sys, 'frozen', None) is None: |
| 2208 | + return |
| 2209 | + |
| 2210 | + main_app_dir = ''.join(__file__.partition('.app')[:-1]) |
| 2211 | + main_app_resources_dir = os.path.join(main_app_dir, |
| 2212 | + 'Contents', |
| 2213 | + 'Resources') |
| 2214 | + |
| 2215 | + config_path = save_config_path('ubuntuone') |
| 2216 | + |
| 2217 | + conf_filenames = ['syncdaemon.conf', |
| 2218 | + 'logging.conf'] |
| 2219 | + for conf_filename in conf_filenames: |
| 2220 | + src_path = os.path.join(main_app_resources_dir, |
| 2221 | + conf_filename) |
| 2222 | + dest_path = os.path.join(config_path, |
| 2223 | + conf_filename) |
| 2224 | + |
| 2225 | + if not os.path.exists(dest_path): |
| 2226 | + shutil.copyfile(src_path, dest_path) |
| 2227 | + |
| 2228 | + |
| 2229 | +def perform_update(): |
| 2230 | + """Spawn the autoupdate process and call the stop function.""" |
| 2231 | + # TODO |
| 2232 | + |
| 2233 | + |
| 2234 | +def uninstall_application(): |
| 2235 | + """Uninstall Ubuntu One.""" |
| 2236 | + # TODO |
| 2237 | |
| 2238 | === added file 'ubuntuone/controlpanel/utils/tests/test_darwin.py' |
| 2239 | --- ubuntuone/controlpanel/utils/tests/test_darwin.py 1970-01-01 00:00:00 +0000 |
| 2240 | +++ ubuntuone/controlpanel/utils/tests/test_darwin.py 2012-08-22 21:22:18 +0000 |
| 2241 | @@ -0,0 +1,78 @@ |
| 2242 | +# -*- coding: utf-8 -*- |
| 2243 | +# |
| 2244 | +# Copyright 2012 Canonical Ltd. |
| 2245 | +# |
| 2246 | +# This program is free software: you can redistribute it and/or modify it |
| 2247 | +# under the terms of the GNU General Public License version 3, as published |
| 2248 | +# by the Free Software Foundation. |
| 2249 | +# |
| 2250 | +# This program is distributed in the hope that it will be useful, but |
| 2251 | +# WITHOUT ANY WARRANTY; without even the implied warranties of |
| 2252 | +# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR |
| 2253 | +# PURPOSE. See the GNU General Public License for more details. |
| 2254 | +# |
| 2255 | +# You should have received a copy of the GNU General Public License along |
| 2256 | +# with this program. If not, see <http://www.gnu.org/licenses/>. |
| 2257 | + |
| 2258 | +"""Test the darwin utils functions.""" |
| 2259 | + |
| 2260 | +import os |
| 2261 | +import sys |
| 2262 | + |
| 2263 | +from twisted.internet import defer |
| 2264 | + |
| 2265 | +from ubuntuone.controlpanel import utils |
| 2266 | +from ubuntuone.devtools.testcases import TestCase |
| 2267 | + |
| 2268 | +# let me use protected methods |
| 2269 | +# pylint: disable=W0212 |
| 2270 | + |
| 2271 | + |
| 2272 | +class InstallConfigTestCase(TestCase): |
| 2273 | + """Test install_config_and_daemons.""" |
| 2274 | + |
| 2275 | + @defer.inlineCallbacks |
| 2276 | + def setUp(self): |
| 2277 | + """Set up multi-call checker.""" |
| 2278 | + yield super(InstallConfigTestCase, self).setUp() |
| 2279 | + self._called = [] |
| 2280 | + |
| 2281 | + def _set_called(self, *args, **kwargs): |
| 2282 | + """Store 'args' and 'kwargs for test assertions.""" |
| 2283 | + self._called.append((args, kwargs)) |
| 2284 | + |
| 2285 | + def test_do_nothing_unfrozen(self): |
| 2286 | + """Test that install_config_and_daemons does nothing when unfrozen.""" |
| 2287 | + |
| 2288 | + self.patch(utils.darwin, 'save_config_path', |
| 2289 | + self._set_called) |
| 2290 | + utils.install_config_and_daemons() |
| 2291 | + self.assertEqual(self._called, []) |
| 2292 | + |
| 2293 | + def _test_copying_conf_files(self, exists): |
| 2294 | + """Call install_config_and_daemons, parameterize os.path.exists.""" |
| 2295 | + sys.frozen = 'macosx_app' |
| 2296 | + self.addCleanup(delattr, sys, 'frozen') |
| 2297 | + self.patch(utils.darwin, 'save_config_path', lambda x: "TARGET_PATH") |
| 2298 | + self.patch(utils.darwin, '__file__', "/path/to/Main.app/ignore") |
| 2299 | + self.patch(os.path, "exists", lambda x: exists) |
| 2300 | + self.patch(utils.darwin.shutil, 'copyfile', |
| 2301 | + self._set_called) |
| 2302 | + utils.install_config_and_daemons() |
| 2303 | + |
| 2304 | + def test_copies_conf_files(self): |
| 2305 | + """When frozen, we copy the conf files if they don't exist.""" |
| 2306 | + self._test_copying_conf_files(False) |
| 2307 | + self.assertEqual(self._called, |
| 2308 | + [(('/path/to/Main.app/Contents/' |
| 2309 | + 'Resources/syncdaemon.conf', |
| 2310 | + 'TARGET_PATH/syncdaemon.conf'), {}), |
| 2311 | + (('/path/to/Main.app/Contents/' |
| 2312 | + 'Resources/logging.conf', |
| 2313 | + 'TARGET_PATH/logging.conf'), |
| 2314 | + {})]) |
| 2315 | + |
| 2316 | + def test_does_not_copy_conf_files(self): |
| 2317 | + """When frozen, we do not copy the conf files if they do exist.""" |
| 2318 | + self._test_copying_conf_files(True) |
| 2319 | + self.assertEqual(self._called, []) |
| 2320 | |
| 2321 | === modified file 'ubuntuone/controlpanel/utils/tests/test_utils.py' |
| 2322 | --- ubuntuone/controlpanel/utils/tests/test_utils.py 2011-11-14 11:33:31 +0000 |
| 2323 | +++ ubuntuone/controlpanel/utils/tests/test_utils.py 2012-08-22 21:22:18 +0000 |
| 2324 | @@ -24,6 +24,7 @@ |
| 2325 | from twisted.internet import defer |
| 2326 | |
| 2327 | from ubuntuone.devtools.handlers import MementoHandler |
| 2328 | +from ubuntuone.devtools.testcases import skipIfOS |
| 2329 | |
| 2330 | from ubuntuone.controlpanel import utils |
| 2331 | from ubuntuone.controlpanel.tests import TestCase |
| 2332 | @@ -149,3 +150,13 @@ |
| 2333 | utils.ERROR_MESSAGE: unicode(exc)} |
| 2334 | |
| 2335 | self.assertEqual(expected, result) |
| 2336 | + |
| 2337 | + |
| 2338 | +class FirstRunInstallTestCase(TestCase): |
| 2339 | + """Test cases for darwin-only first-run installations.""" |
| 2340 | + |
| 2341 | + @skipIfOS("darwin", "Darwin-only code is tested in test_darwin.") |
| 2342 | + def test_no_op_on_win_or_linux(self): |
| 2343 | + """Test that install_config_and_daemons does nothing on win/linux.""" |
| 2344 | + self.assertEqual(utils.install_config_and_daemons, |
| 2345 | + utils.no_op) |


Looks just like trunk. +1.