Merge lp:~dobey/ubuntuone-control-panel/update-4-0 into lp:ubuntuone-control-panel/stable-4-0

Proposed by dobey
Status: Merged
Approved by: dobey
Approved revision: no longer in the source branch.
Merged at revision: 328
Proposed branch: lp:~dobey/ubuntuone-control-panel/update-4-0
Merge into: lp:ubuntuone-control-panel/stable-4-0
Diff against target: 463 lines (+118/-97)
7 files modified
bin/ubuntuone-control-panel-qt (+1/-1)
data/qt/images.qrc (+1/-0)
run-tests (+32/-7)
ubuntuone/controlpanel/gui/qt/folders.py (+28/-12)
ubuntuone/controlpanel/gui/qt/main/__init__.py (+5/-5)
ubuntuone/controlpanel/gui/qt/main/twisted_main.py (+1/-1)
ubuntuone/controlpanel/gui/qt/tests/test_folders.py (+50/-71)
To merge this branch: bzr merge lp:~dobey/ubuntuone-control-panel/update-4-0
Reviewer Review Type Date Requested Status
Mike McCracken (community) Approve
Review via email: mp+111291@code.launchpad.net

Commit message

[Mike McCracken]

    - Use qt4reactor on Darwin. (LP: #1013820)
    - Make run-tests script work on Darwin. (LP: #1010211)

[Roberto Alsina]

    - Enable folder list sorting (Fixes LP:1006385).

[Diego Sarmentero]

    - Using suggested_path to server path comparisons (LP: #1004603).

To post a comment you must log in.
Revision history for this message
Mike McCracken (mikemc) wrote :

Looks OK to me.

review: Approve
328. By Diego Sarmentero

[Mike McCracken]

    - Use qt4reactor on Darwin. (LP: #1013820)
    - Make run-tests script work on Darwin. (LP: #1010211)

[Roberto Alsina]

    - Enable folder list sorting (Fixes LP:1006385).

[Diego Sarmentero]

    - Using suggested_path to server path comparisons (LP: #1004603).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bin/ubuntuone-control-panel-qt'
2--- bin/ubuntuone-control-panel-qt 2012-03-01 17:47:54 +0000
3+++ bin/ubuntuone-control-panel-qt 2012-06-20 19:10:48 +0000
4@@ -22,7 +22,7 @@
5 import signal
6 import sys
7
8-if sys.platform == 'win32':
9+if sys.platform in ('win32', 'darwin'):
10 import qt4reactor
11 qt4reactor.install()
12
13
14=== added file 'data/audio-x-generic.png'
15Binary files data/audio-x-generic.png 1970-01-01 00:00:00 +0000 and data/audio-x-generic.png 2012-06-20 19:10:48 +0000 differ
16=== modified file 'data/qt/images.qrc'
17--- data/qt/images.qrc 2012-05-14 21:05:08 +0000
18+++ data/qt/images.qrc 2012-06-20 19:10:48 +0000
19@@ -1,5 +1,6 @@
20 <RCC>
21 <qresource prefix="/">
22+ <file>../audio-x-generic.png</file>
23 <file>../banner.png</file>
24 <file>../computer.png</file>
25 <file>../external_icon_dark_grey.png</file>
26
27=== modified file 'run-tests'
28--- run-tests 2012-04-25 13:35:35 +0000
29+++ run-tests 2012-06-20 19:10:48 +0000
30@@ -19,8 +19,9 @@
31 QT_TESTS_PATH="ubuntuone/controlpanel/gui/qt/tests, ubuntuone/controlpanel/gui/qt/main/tests, ubuntuone/controlpanel/gui/qt/uniqueapp/tests"
32 DBUS_TESTS_PATH=ubuntuone/controlpanel/dbustests
33 WINDOWS_TESTS=test_windows.py
34+LINUX_TESTS=test_linux.py
35+DARWIN_TESTS=test_darwin.py
36
37-set -e
38
39 if [ $# -ne 0 ]; then
40 # run specific module given by the caller
41@@ -31,7 +32,7 @@
42 fi
43
44 style_check() {
45- u1lint --ignore ubuntuone/controlpanel/gui/qt/ui
46+ $u1lint --ignore ubuntuone/controlpanel/gui/qt/ui
47 if [ -x `which pep8` ]; then
48 pep8 --exclude '.svn,CVS,.bzr,.hg,.git,*_ui.py,*_rc.py' --repeat . bin/*
49 else
50@@ -45,15 +46,39 @@
51 XVFB_CMDLINE="$XVFB -a"
52 fi
53
54+# now fail on errors, after we tried to find xvfb:
55+set -e
56+
57+SYSNAME=`uname -s`
58+
59+if [ "$SYSNAME" == "Darwin" ]; then
60+ u1trial="python $u1trial"
61+ u1lint="python $u1lint"
62+ IGNORE_FILES="$WINDOWS_TESTS, $LINUX_TESTS"
63+ IGNORE_DBUS="-p $DBUS_TESTS_PATH"
64+ echo "*** Skipping DBus test suite ***"
65+ REACTOR=qt4
66+ PYTHONPATH=$PYTHONPATH:../ubuntu-sso-client:../ubuntuone-client:../ubuntuone-storage-protocol:.
67+else
68+ # Linux:
69+ if [ "x$XVFB_CMDLINE" == "x" ]; then
70+ echo "WARNING: Could not find xvfb-run, prepare for visual spam."
71+ fi
72+ u1trial=u1trial
73+ u1lint=u1lint
74+ IGNORE_FILES="$WINDOWS_TESTS, $DARWIN_TESTS"
75+ IGNORE_DBUS=""
76+ echo "*** Running DBus test suite ***"
77+ $u1trial --reactor=glib "$DBUS_TESTS_PATH"
78+ REACTOR=gi
79+fi
80+
81 echo "*** Running test suite for ""$MODULE"" ***"
82-u1trial --reactor=gi -p "$DBUS_TESTS_PATH, $QT_TESTS_PATH" -i "$WINDOWS_TESTS" "$MODULE"
83-
84-echo "*** Running DBus test suite ***"
85-u1trial --reactor=glib "$DBUS_TESTS_PATH"
86+$u1trial --reactor=$REACTOR -p "$DBUS_TESTS_PATH, $QT_TESTS_PATH" -i "$IGNORE_FILES" "$MODULE"
87
88 echo "*** Running QT test suite for ""$MODULE"" ***"
89 ./setup.py build
90-$XVFB_CMDLINE u1trial -i "$WINDOWS_TESTS" --reactor=qt4 --gui "$MODULE"
91+$XVFB_CMDLINE $u1trial $IGNORE_DBUS -i "$IGNORE_FILES" --reactor=qt4 --gui "$MODULE"
92 rm -rf _trial_temp
93 rm -rf build
94
95
96=== modified file 'ubuntuone/controlpanel/gui/qt/folders.py'
97--- ubuntuone/controlpanel/gui/qt/folders.py 2012-05-14 18:50:12 +0000
98+++ ubuntuone/controlpanel/gui/qt/folders.py 2012-06-20 19:10:48 +0000
99@@ -80,10 +80,15 @@
100
101 def _process_name(name):
102 """Tweak 'name' with a translatable music folder name."""
103- if name == MUSIC_REAL_PATH:
104+ if name.startswith('~/'):
105+ suggested = name[2:]
106+ else:
107+ suggested = name
108+
109+ if suggested == MUSIC_REAL_PATH:
110 result = MUSIC_DISPLAY_NAME
111 else:
112- result = name
113+ result = suggested
114 return result
115
116
117@@ -132,6 +137,9 @@
118 headers.setResizeMode(SUBSCRIPTION_COL, headers.ResizeToContents)
119 headers.setResizeMode(EXPLORE_COL, headers.ResizeToContents)
120
121+ self.ui.folders.setSortingEnabled(True)
122+ self.ui.folders.sortByColumn(FOLDER_NAME_COL, QtCore.Qt.AscendingOrder)
123+
124 self.ui.share_publish_button.setText(FOLDERS_MANAGE_LABEL)
125 self.ui.share_publish_button.uri = MANAGE_FILES_LINK
126 QtGui.QApplication.instance().focusChanged.connect(self.focus_changed)
127@@ -203,7 +211,8 @@
128 child.volume_path = volume['path']
129 child.volume_id = volume['volume_id']
130
131- name = _process_name(volume[u'display_name'])
132+ name = _process_name(volume.get(u'suggested_path',
133+ volume[u'display_name']))
134 child.setText(FOLDER_NAME_COL, name)
135 child.setToolTip(FOLDER_NAME_COL, name)
136 child.setToolTip(EXPLORE_COL, FOLDERS_COLUMN_EXPLORE)
137@@ -505,21 +514,25 @@
138 for volume in volumes:
139 if (volume[u'type'] == self.backend.FOLDER_TYPE and
140 bool(volume['subscribed'])):
141- folders.append((volume['path'], volume['volume_id']))
142+ suggested = volume.get(u'suggested_path',
143+ volume[u'display_name'])
144+ folders.append((volume['path'], volume['volume_id'],
145+ suggested))
146
147 # add local folders only if they are valid
148 for folder in default_folders(user_home=self.user_home):
149 is_valid = yield self.backend.validate_path_for_folder(folder)
150 if is_valid:
151- folders.append((folder, None))
152+ folders.append((folder, None, None))
153
154 # always clear the items dict first, since clearing the folders
155 # list will trigger "underlying C/C++ object has been deleted"
156 self.items.clear()
157 self.ui.folders.clear()
158
159- for path, volume_id in folders:
160- self.add_folder(folder_path=path, volume_id=volume_id)
161+ for path, volume_id, suggested in folders:
162+ self.add_folder(folder_path=path, volume_id=volume_id,
163+ suggested=suggested)
164
165 if folders and not self.timer.isActive():
166 self.timer.start(2000)
167@@ -552,20 +565,23 @@
168 self.changesApplied.emit()
169
170 @handle_errors(logger=logger)
171- def add_folder(self, folder_path, volume_id=None):
172+ def add_folder(self, folder_path, volume_id=None, suggested=None):
173 """Add a folder to the list."""
174 if folder_path in self.items:
175 logger.warning('LocalFoldersPanel: already have an item for %r.',
176 folder_path)
177 return
178
179- if self.user_home is None:
180+ if suggested is None:
181 logger.warning('LocalFoldersPanel: user home is None! '
182 'paths will not be pretty.')
183- display_name = folder_path
184- else:
185- user_home = self.user_home + os.path.sep
186+ if self.user_home is None:
187+ user_home = ''
188+ else:
189+ user_home = self.user_home + os.path.sep
190 display_name = _process_name(folder_path.replace(user_home, ''))
191+ else:
192+ display_name = _process_name(suggested)
193
194 item = FolderItem([display_name, ""], path=folder_path,
195 queue=self.queue, volume_id=volume_id)
196
197=== modified file 'ubuntuone/controlpanel/gui/qt/main/__init__.py'
198--- ubuntuone/controlpanel/gui/qt/main/__init__.py 2012-05-14 22:27:07 +0000
199+++ ubuntuone/controlpanel/gui/qt/main/__init__.py 2012-06-20 19:10:48 +0000
200@@ -29,12 +29,12 @@
201 from ubuntuone.controlpanel.gui.qt.uniqueapp import UniqueApplication
202
203 # Invalid name "source", pylint: disable=C0103
204-if sys.platform == 'win32':
205- from ubuntuone.controlpanel.gui.qt.main import windows
206- source = windows
207+if sys.platform in ('win32', 'darwin'):
208+ from ubuntuone.controlpanel.gui.qt.main import twisted_main
209+ source = twisted_main
210 else:
211- from ubuntuone.controlpanel.gui.qt.main import linux
212- source = linux
213+ from ubuntuone.controlpanel.gui.qt.main import dbus_main
214+ source = dbus_main
215 # pylint: enable=C0103
216
217
218
219=== renamed file 'ubuntuone/controlpanel/gui/qt/main/linux.py' => 'ubuntuone/controlpanel/gui/qt/main/dbus_main.py'
220=== renamed file 'ubuntuone/controlpanel/gui/qt/main/windows.py' => 'ubuntuone/controlpanel/gui/qt/main/twisted_main.py'
221--- ubuntuone/controlpanel/gui/qt/main/windows.py 2012-04-05 14:52:55 +0000
222+++ ubuntuone/controlpanel/gui/qt/main/twisted_main.py 2012-06-20 19:10:48 +0000
223@@ -14,7 +14,7 @@
224 # You should have received a copy of the GNU General Public License along
225 # with this program. If not, see <http://www.gnu.org/licenses/>.
226
227-"""Main method to be used on windows."""
228+"""Main method to be used on platforms without dbus."""
229
230 from PyQt4 import QtGui
231
232
233=== modified file 'ubuntuone/controlpanel/gui/qt/tests/test_folders.py'
234--- ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-05-14 18:50:12 +0000
235+++ ubuntuone/controlpanel/gui/qt/tests/test_folders.py 2012-06-20 19:10:48 +0000
236@@ -24,7 +24,7 @@
237 import shutil
238 import sys
239
240-from PyQt4 import QtGui
241+from PyQt4 import QtCore, QtGui
242 from twisted.internet import defer
243 from ubuntuone.devtools.handlers import MementoHandler
244 from ubuntuone.devtools.testcases import skipIfOS
245@@ -37,6 +37,7 @@
246 FAKE_VOLUMES_ONLY_ROOT_INFO,
247 MUSIC_FOLDER, MUSIC_PATH, ROOT, USER_HOME,
248 )
249+from ubuntuone.controlpanel.gui import MUSIC_REAL_PATH
250 from ubuntuone.controlpanel.gui.qt import folders as gui
251 from ubuntuone.controlpanel.gui.qt.tests import (
252 BaseTestCase,
253@@ -243,28 +244,21 @@
254
255 def test_process_info(self):
256 """The volumes info is processed when ready."""
257+ folders = self.ui.ui.folders
258 self.ui.process_info(FAKE_VOLUMES_INFO)
259- folders = self.ui.ui.folders
260-
261 root = folders.invisibleRootItem()
262 self.assertEqual(len(FAKE_VOLUMES_INFO), root.childCount())
263
264- treeiter = gui.QtGui.QTreeWidgetItemIterator(folders)
265 for name, _, volumes in FAKE_VOLUMES_INFO:
266 name = _build_name(name)
267-
268- # get first child, matching "My Folders"
269- item = treeiter.value()
270- self.assertFalse(item.is_empty)
271-
272+ items = folders.findItems(name, QtCore.Qt.MatchFixedString
273+ | QtCore.Qt.MatchCaseSensitive | QtCore.Qt.MatchRecursive,
274+ gui.FOLDER_NAME_COL)
275+ self.assertEqual(1, len(items))
276+ item = items[0]
277 self.assert_folder_group_header_correct(item, name)
278-
279 self.assertEqual(len(volumes), item.childCount())
280- sorted_vols = sorted(volumes, key=operator.itemgetter('path'))
281- for volume in sorted_vols:
282- treeiter += 1
283- item = treeiter.value() # get child folder
284- self.assertTrue(item is not None)
285+ for volume in volumes:
286
287 name = volume['path'].replace(USER_HOME + os.path.sep, '')
288 expected_path = volume['path']
289@@ -277,12 +271,14 @@
290 else:
291 icon_name = gui.SHARE_ICON_NAME
292
293+ items = folders.findItems(name, QtCore.Qt.MatchFixedString |
294+ QtCore.Qt.MatchCaseSensitive | QtCore.Qt.MatchRecursive,
295+ gui.FOLDER_NAME_COL)
296+ self.assertEqual(1, len(items))
297+ item = items[0]
298 self.assert_folder_row_correct(item, name, icon_name, volume,
299 tweaked_path=expected_path)
300
301- treeiter += 1
302- item = treeiter.value()
303-
304 def test_process_info_clears_the_list(self):
305 """The old volumes info is cleared before updated."""
306 self.ui.process_info(FAKE_VOLUMES_INFO)
307@@ -304,9 +300,10 @@
308
309 child_index = 0
310 root = self.ui.ui.folders.invisibleRootItem()
311- for name, _, _ in FAKE_VOLUMES_NO_FREE_SPACE_INFO:
312- name = _build_name(name)
313-
314+ names = [_build_name(name) for name, _, _ in
315+ FAKE_VOLUMES_NO_FREE_SPACE_INFO]
316+ names.sort(key=lambda x: x.lower())
317+ for name in names:
318 item = root.child(child_index)
319 self.assertFalse(item.is_empty)
320 self.assert_folder_group_header_correct(item, name)
321@@ -413,34 +410,23 @@
322 """Ensure that the inner widgets are in the correct tab order."""
323 self.ui.process_info(FAKE_VOLUMES_INFO)
324 folders = self.ui.ui.folders
325-
326 widget = self.ui.ui.folders.nextInFocusChain()
327 treeiter = gui.QtGui.QTreeWidgetItemIterator(folders)
328- for name, _, volumes in FAKE_VOLUMES_INFO:
329- item = treeiter.value()
330- sorted_vols = sorted(volumes, key=operator.itemgetter('path'))
331- for volume in sorted_vols:
332+ while treeiter.value():
333+ if treeiter.value().parent() is None: # Top-levels
334 treeiter += 1
335- item = treeiter.value() # get child folder
336-
337- name = volume['path'].replace(USER_HOME + os.path.sep, '')
338- if volume['type'] == self.ui.backend.SHARE_TYPE:
339- name = volume['name']
340- self.assertEqual(unicode(item.text(gui.FOLDER_NAME_COL)), name)
341-
342- actual_name = unicode(self.ui.widget_items[widget].text(0))
343- if volume['type'] != self.ui.backend.ROOT_TYPE:
344- self.assertIsInstance(widget, QtGui.QCheckBox)
345- self.assertEqual(actual_name, name)
346- widget = widget.nextInFocusChain()
347-
348- if not self.ui.remote_folders:
349- self.assertIsInstance(widget, QtGui.QPushButton)
350- self.assertEqual(actual_name, name)
351- widget = widget.nextInFocusChain()
352-
353+ continue
354+ if self.ui.remote_folders:
355+ self.assertIsInstance(widget, QtGui.QCheckBox)
356+ # ROOT volume has no checkbox
357+ elif treeiter.value().text(1) or self.ui.remote_folders:
358+ self.assertIsInstance(widget, QtGui.QPushButton)
359+ else:
360+ self.assertIsInstance(widget, QtGui.QCheckBox)
361+ widget = widget.nextInFocusChain()
362+ self.assertIsInstance(widget, QtGui.QPushButton)
363+ widget = widget.nextInFocusChain()
364 treeiter += 1
365- item = treeiter.value()
366
367 def test_widget_dict(self):
368 """Ensure the widget_items dictionary is full."""
369@@ -994,13 +980,17 @@
370 def assert_folder_added(self, path, volume_id=None, item=None):
371 """A entry for 'path' was added to the folder list."""
372 folders = self.ui.ui.folders
373- if item is None: # grab the last one added
374- item = folders.topLevelItem(folders.topLevelItemCount() - 1)
375
376 if path == MUSIC_PATH:
377 display_name = gui.MUSIC_DISPLAY_NAME
378 else:
379 display_name = path.replace(USER_HOME + os.path.sep, '')
380+ if item is None: # search for it
381+ items = folders.findItems(display_name,
382+ QtCore.Qt.MatchFixedString | QtCore.Qt.MatchCaseSensitive,
383+ gui.FOLDER_NAME_COL)
384+ self.assertEqual(len(items), 1)
385+ item = items[0]
386 self.assertEqual(display_name,
387 unicode(item.text(gui.LOCAL_SUBSCRIPTION_COL)))
388
389@@ -1093,10 +1083,6 @@
390 # the following will execute update_sizes once
391 # will fill in all the path sizes for the already added folders
392 self.timer.timeout.emit()
393-
394- folders = self.ui.ui.folders
395- treeiter = gui.QtGui.QTreeWidgetItemIterator(folders)
396-
397 volumes = []
398 for _, _, vols in volumes_info:
399 volumes.extend(vols)
400@@ -1107,28 +1093,14 @@
401 not bool(volume['subscribed'])):
402 # non-folders or unsubscribed should not appear in the tree
403 continue
404-
405- item = treeiter.value()
406- self.assertTrue(item is not None)
407 self.assert_folder_added(volume['path'],
408- volume_id=volume['volume_id'], item=item)
409-
410- treeiter += 1
411- item = treeiter.value()
412-
413+ volume_id=volume['volume_id'])
414 for folder in gui.default_folders(USER_HOME):
415 valid = yield self.validate_path(folder)
416 if not valid:
417 continue # invalid folders should not appear in the tree
418-
419- item = treeiter.value()
420- self.assertTrue(item is not None)
421- self.assert_folder_added(folder, volume_id=None, item=item)
422-
423- treeiter += 1
424- item = treeiter.value()
425-
426- self.assertTrue(item is None)
427+ self.assert_folder_added(folder, volume_id=None, item=None)
428+
429 self.assert_space_header_correct(ACCOUNT_INFO['quota_used'])
430
431 @defer.inlineCallbacks
432@@ -1219,13 +1191,13 @@
433 class LocalFoldersPanelAddFolderTestCase(BaseLocalFoldersPanelTestCase):
434 """Test suite for the add_folder method from LocalFoldersPanel."""
435
436- def test_folder_is_added(self, folder_path=None):
437+ def test_folder_is_added(self, folder_path=None, suggested=None):
438 """Adding a folder adds it to the folder list."""
439 former_count = self.ui.ui.folders.topLevelItemCount()
440
441 if folder_path is None:
442 folder_path = self.path
443- self.ui.add_folder(folder_path)
444+ self.ui.add_folder(folder_path, suggested=suggested)
445
446 new_count = self.ui.ui.folders.topLevelItemCount()
447 self.assertEqual(new_count, former_count + 1)
448@@ -1233,7 +1205,14 @@
449
450 def test_add_music_folder_adds_the_folder(self):
451 """Adding the purchased music folder adds it to the folder list."""
452- self.test_folder_is_added(folder_path=MUSIC_PATH)
453+ self.test_folder_is_added(folder_path=MUSIC_PATH,
454+ suggested=MUSIC_REAL_PATH)
455+
456+ def test_add_music_folder_adds_the_folder_with_suggested_path(self):
457+ """Adding the purchased music folder adds it to the folder list."""
458+ suggested_path = '~/' + MUSIC_REAL_PATH
459+ self.test_folder_is_added(folder_path=MUSIC_PATH,
460+ suggested=suggested_path)
461
462 def test_do_not_add_twice(self):
463 """The same folder will not be added twice."""

Subscribers

People subscribed via source and target branches

to all changes: